From 3b941befae745e3f47708c3c8681454acf12070b Mon Sep 17 00:00:00 2001 From: stoffu Date: Tue, 20 Mar 2018 20:30:09 +0900 Subject: [PATCH 0001/1404] core: add get_earliest_ideal_height_for_version() --- src/cryptonote_core/blockchain.h | 7 +++++++ src/cryptonote_core/cryptonote_core.cpp | 5 +++++ src/cryptonote_core/cryptonote_core.h | 7 +++++++ tests/core_proxy/core_proxy.h | 1 + tests/unit_tests/ban.cpp | 1 + 5 files changed, 21 insertions(+) diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index 4423199dea3..6fb990a7722 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -791,6 +791,13 @@ namespace cryptonote */ uint8_t get_hard_fork_version(uint64_t height) const { return m_hardfork->get(height); } + /** + * @brief returns the earliest block a given version may activate + * + * @return the height + */ + uint64_t get_earliest_ideal_height_for_version(uint8_t version) const { return m_hardfork->get_earliest_ideal_height_for_version(version); } + /** * @brief get information about hardfork voting for a version * diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index adccd0469ea..7460cf8c8f4 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -1427,6 +1427,11 @@ namespace cryptonote return get_blockchain_storage().get_hard_fork_version(height); } //----------------------------------------------------------------------------------------------- + uint64_t core::get_earliest_ideal_height_for_version(uint8_t version) const + { + return get_blockchain_storage().get_earliest_ideal_height_for_version(version); + } + //----------------------------------------------------------------------------------------------- bool core::check_updates() { static const char software[] = "monero"; diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index abf79be1d2c..9a43c2e9fd7 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -655,6 +655,13 @@ namespace cryptonote */ uint8_t get_hard_fork_version(uint64_t height) const; + /** + * @brief return the earliest block a given version may activate + * + * @return what it says above + */ + uint64_t get_earliest_ideal_height_for_version(uint8_t version) const; + /** * @brief gets start_time * diff --git a/tests/core_proxy/core_proxy.h b/tests/core_proxy/core_proxy.h index 59f8d5239dc..75561dbf82d 100644 --- a/tests/core_proxy/core_proxy.h +++ b/tests/core_proxy/core_proxy.h @@ -99,6 +99,7 @@ namespace tests bool get_block_by_hash(const crypto::hash &h, cryptonote::block &blk, bool *orphan = NULL) const { return false; } uint8_t get_ideal_hard_fork_version(uint64_t height) const { return 0; } uint8_t get_hard_fork_version(uint64_t height) const { return 0; } + uint64_t get_earliest_ideal_height_for_version(uint8_t version) const { return 0; } cryptonote::difficulty_type get_block_cumulative_difficulty(uint64_t height) const { return 0; } bool fluffy_blocks_enabled() const { return false; } uint64_t prevalidate_block_hashes(uint64_t height, const std::list &hashes) { return 0; } diff --git a/tests/unit_tests/ban.cpp b/tests/unit_tests/ban.cpp index 688656cbcbe..313ff21d6e8 100644 --- a/tests/unit_tests/ban.cpp +++ b/tests/unit_tests/ban.cpp @@ -78,6 +78,7 @@ class test_core bool get_block_by_hash(const crypto::hash &h, cryptonote::block &blk, bool *orphan = NULL) const { return false; } uint8_t get_ideal_hard_fork_version(uint64_t height) const { return 0; } uint8_t get_hard_fork_version(uint64_t height) const { return 0; } + uint64_t get_earliest_ideal_height_for_version(uint8_t version) const { return 0; } cryptonote::difficulty_type get_block_cumulative_difficulty(uint64_t height) const { return 0; } bool fluffy_blocks_enabled() const { return false; } uint64_t prevalidate_block_hashes(uint64_t height, const std::list &hashes) { return 0; } From 54d9fffa10a3bcc2641567b4cc9774edacd1d6d5 Mon Sep 17 00:00:00 2001 From: stoffu Date: Wed, 11 Apr 2018 11:35:45 +0900 Subject: [PATCH 0002/1404] gtest: Fix compilation on MinGW with pthread /googletest#621 --- tests/gtest/cmake/internal_utils.cmake | 2 +- tests/gtest/include/gtest/internal/gtest-port.h | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/tests/gtest/cmake/internal_utils.cmake b/tests/gtest/cmake/internal_utils.cmake index 364db597081..567edaa300a 100644 --- a/tests/gtest/cmake/internal_utils.cmake +++ b/tests/gtest/cmake/internal_utils.cmake @@ -46,7 +46,7 @@ endmacro() # Google Mock. You can tweak these definitions to suit your need. A # variable's value is empty before it's explicitly assigned to. macro(config_compiler_and_linker) - if (NOT gtest_disable_pthreads) + if (NOT gtest_disable_pthreads AND NOT MINGW) # Defines CMAKE_USE_PTHREADS_INIT and CMAKE_THREAD_LIBS_INIT. find_package(Threads) endif() diff --git a/tests/gtest/include/gtest/internal/gtest-port.h b/tests/gtest/include/gtest/internal/gtest-port.h index 7d6e4658194..cb0639b4b03 100644 --- a/tests/gtest/include/gtest/internal/gtest-port.h +++ b/tests/gtest/include/gtest/internal/gtest-port.h @@ -1553,10 +1553,7 @@ class GTEST_API_ Notification { }; # endif // GTEST_HAS_NOTIFICATION_ -// On MinGW, we can have both GTEST_OS_WINDOWS and GTEST_HAS_PTHREAD -// defined, but we don't want to use MinGW's pthreads implementation, which -// has conformance problems with some versions of the POSIX standard. -# if GTEST_HAS_PTHREAD && !GTEST_OS_WINDOWS_MINGW +# if GTEST_HAS_PTHREAD // As a C-function, ThreadFuncWithCLinkage cannot be templated itself. // Consequently, it cannot select a correct instantiation of ThreadWithParam From 4c00a4d31eb797a284b7e3558b9b88dd6c0033a2 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 12 Apr 2018 13:49:05 +0100 Subject: [PATCH 0003/1404] unit_tests: add device unit tests Annoyingly, its locking semantics are borked since it does not do any locking --- tests/unit_tests/CMakeLists.txt | 1 + tests/unit_tests/device.cpp | 131 ++++++++++++++++++++++++++++++++ 2 files changed, 132 insertions(+) create mode 100644 tests/unit_tests/device.cpp diff --git a/tests/unit_tests/CMakeLists.txt b/tests/unit_tests/CMakeLists.txt index 9c58536c947..df98237ed4c 100644 --- a/tests/unit_tests/CMakeLists.txt +++ b/tests/unit_tests/CMakeLists.txt @@ -41,6 +41,7 @@ set(unit_tests_sources command_line.cpp crypto.cpp decompose_amount_into_digits.cpp + device.cpp dns_resolver.cpp epee_boosted_tcp_server.cpp epee_levin_protocol_handler_async.cpp diff --git a/tests/unit_tests/device.cpp b/tests/unit_tests/device.cpp new file mode 100644 index 00000000000..50ccec9fae0 --- /dev/null +++ b/tests/unit_tests/device.cpp @@ -0,0 +1,131 @@ +// Copyright (c) 2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "gtest/gtest.h" +#include "ringct/rctOps.h" +#include "device/device_default.hpp" + +TEST(device, name) +{ + hw::core::device_default dev; + ASSERT_TRUE(dev.set_name("test")); + ASSERT_EQ(dev.get_name(), "test"); +} + +/* +TEST(device, locking) +{ + hw::core::device_default dev; + ASSERT_TRUE(dev.try_lock()); + ASSERT_FALSE(dev.try_lock()); + dev.unlock(); + ASSERT_TRUE(dev.try_lock()); + dev.unlock(); + dev.lock(); + ASSERT_FALSE(dev.try_lock()); + dev.unlock(); + ASSERT_TRUE(dev.try_lock()); + dev.unlock(); +} +*/ + +TEST(device, open_close) +{ + hw::core::device_default dev; + crypto::secret_key key; + ASSERT_TRUE(dev.open_tx(key)); + ASSERT_TRUE(dev.close_tx()); +} + +TEST(device, ops) +{ + hw::core::device_default dev; + rct::key resd, res; + crypto::key_derivation derd, der; + rct::key sk, pk; + crypto::secret_key sk0, sk1; + crypto::public_key pk0, pk1; + crypto::ec_scalar ressc0, ressc1; + crypto::key_image ki0, ki1; + + rct::skpkGen(sk, pk); + rct::scalarmultBase((rct::key&)pk0, (rct::key&)sk0); + rct::scalarmultBase((rct::key&)pk1, (rct::key&)sk1); + + dev.scalarmultKey(resd, pk, sk); + rct::scalarmultKey(res, pk, sk); + ASSERT_EQ(resd, res); + + dev.scalarmultBase(resd, sk); + rct::scalarmultBase(res, sk); + ASSERT_EQ(resd, res); + + dev.sc_secret_add((crypto::secret_key&)resd, sk0, sk1); + sc_add((unsigned char*)&res, (unsigned char*)&sk0, (unsigned char*)&sk1); + ASSERT_EQ(resd, res); + + dev.generate_key_derivation(pk0, sk0, derd); + crypto::generate_key_derivation(pk0, sk0, der); + ASSERT_FALSE(memcmp(&derd, &der, sizeof(der))); + + dev.derivation_to_scalar(der, 0, ressc0); + crypto::derivation_to_scalar(der, 0, ressc1); + ASSERT_FALSE(memcmp(&ressc0, &ressc1, sizeof(ressc1))); + + dev.derive_secret_key(der, 0, rct::rct2sk(sk), sk0); + crypto::derive_secret_key(der, 0, rct::rct2sk(sk), sk1); + ASSERT_EQ(sk0, sk1); + + dev.derive_public_key(der, 0, rct::rct2pk(pk), pk0); + crypto::derive_public_key(der, 0, rct::rct2pk(pk), pk1); + ASSERT_EQ(pk0, pk1); + + dev.secret_key_to_public_key(rct::rct2sk(sk), pk0); + crypto::secret_key_to_public_key(rct::rct2sk(sk), pk1); + ASSERT_EQ(pk0, pk1); + + dev.generate_key_image(pk0, sk0, ki0); + crypto::generate_key_image(pk0, sk0, ki1); + ASSERT_EQ(ki0, ki1); +} + +TEST(device, ecdh) +{ + hw::core::device_default dev; + rct::ecdhTuple tuple, tuple2; + rct::key key = rct::skGen(); + tuple.mask = rct::skGen(); + tuple.amount = rct::skGen(); + tuple.senderPk = rct::pkGen(); + tuple2 = tuple; + dev.ecdhEncode(tuple, key); + dev.ecdhDecode(tuple, key); + ASSERT_EQ(tuple2.mask, tuple.mask); + ASSERT_EQ(tuple2.amount, tuple.amount); + ASSERT_EQ(tuple2.senderPk, tuple.senderPk); +} From 8787fd899b4e1f60d1a987dcfc77cb99ca81798d Mon Sep 17 00:00:00 2001 From: naughtyfox Date: Tue, 10 Apr 2018 19:21:25 +0300 Subject: [PATCH 0004/1404] WalletApi: publicMultisigSignerKey method --- src/wallet/api/wallet.cpp | 10 ++++++++++ src/wallet/api/wallet.h | 1 + src/wallet/api/wallet2_api.h | 6 ++++++ 3 files changed, 17 insertions(+) diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index b02884f6745..f1dd25d214f 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -753,6 +753,16 @@ std::string WalletImpl::publicSpendKey() const return epee::string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_account_address.m_spend_public_key); } +std::string WalletImpl::publicMultisigSignerKey() const +{ + try { + crypto::public_key signer = m_wallet->get_multisig_signer_public_key(); + return epee::string_tools::pod_to_hex(signer); + } catch (const std::exception&) { + return ""; + } +} + std::string WalletImpl::path() const { return m_wallet->path(); diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h index 4929c96739f..64c992a5f33 100644 --- a/src/wallet/api/wallet.h +++ b/src/wallet/api/wallet.h @@ -90,6 +90,7 @@ class WalletImpl : public Wallet std::string publicViewKey() const; std::string secretSpendKey() const; std::string publicSpendKey() const; + std::string publicMultisigSignerKey() const; std::string path() const; bool store(const std::string &path); std::string filename() const; diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h index d4e41c5aa38..7d76c954369 100644 --- a/src/wallet/api/wallet2_api.h +++ b/src/wallet/api/wallet2_api.h @@ -408,6 +408,12 @@ struct Wallet */ virtual std::string publicSpendKey() const = 0; + /*! + * \brief publicMultisigSignerKey - returns public signer key + * \return - public multisignature signer key or empty string if wallet is not multisig + */ + virtual std::string publicMultisigSignerKey() const = 0; + /*! * \brief store - stores wallet to file. * \param path - main filename to store wallet to. additionally stores address file and keys file. From e2b75586fb1ac9e9d37f1b25594d13a77b1b0e56 Mon Sep 17 00:00:00 2001 From: cryptochangements34 Date: Sun, 15 Apr 2018 11:33:22 -0400 Subject: [PATCH 0005/1404] handle improper log levels --- src/simplewallet/simplewallet.cpp | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 33615ffe273..b7dc83a855f 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -2439,8 +2439,24 @@ bool simple_wallet::set_log(const std::vector &args) fail_msg_writer() << tr("usage: set_log | "); return true; } - if (!args.empty()) - mlog_set_log(args[0].c_str()); + if(!args.empty()) + { + uint16_t level = 0; + if(epee::string_tools::get_xtype_from_string(level, args[0])) + { + if(4 < level) + { + fail_msg_writer() << tr("wrong number range, use: set_log | "); + return true; + } + mlog_set_log_level(level); + } + else + { + mlog_set_log(args[0].c_str()); + } + } + success_msg_writer() << "New log categories: " << mlog_get_categories(); return true; } From 6f36cf02e7a289342580e32d65f2af7b77ee0325 Mon Sep 17 00:00:00 2001 From: Ryan Ronnander <61520+ryan-ronnander@users.noreply.github.com> Date: Sun, 15 Apr 2018 19:23:15 -0700 Subject: [PATCH 0006/1404] Improve 'show_transfers' date formatting Change the wallet's 'show_transfers' command to always output the transaction date with timestamp (24 hour UTC). --- src/simplewallet/simplewallet.cpp | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 33615ffe273..27b76a7b209 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -5987,10 +5987,7 @@ static std::string get_human_readable_timestamp(uint64_t ts) #endif uint64_t now = time(NULL); uint64_t diff = ts > now ? ts - now : now - ts; - if (diff > 24*3600) - strftime(buffer, sizeof(buffer), "%Y-%m-%d", &tm); - else - strftime(buffer, sizeof(buffer), "%I:%M:%S %p", &tm); + strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", &tm); return std::string(buffer); } //---------------------------------------------------------------------------------------------------- @@ -6102,7 +6099,7 @@ bool simple_wallet::show_transfers(const std::vector &args_) if (payment_id.substr(16).find_first_not_of('0') == std::string::npos) payment_id = payment_id.substr(0,16); std::string note = m_wallet->get_tx_note(pd.m_tx_hash); - output.insert(std::make_pair(pd.m_block_height, std::make_pair(true, (boost::format("%16.16s %20.20s %s %s %d %s %s") % get_human_readable_timestamp(pd.m_timestamp) % print_money(pd.m_amount) % string_tools::pod_to_hex(pd.m_tx_hash) % payment_id % pd.m_subaddr_index.minor % "-" % note).str()))); + output.insert(std::make_pair(pd.m_block_height, std::make_pair(true, (boost::format("%25.25s %20.20s %s %s %d %s %s") % get_human_readable_timestamp(pd.m_timestamp) % print_money(pd.m_amount) % string_tools::pod_to_hex(pd.m_tx_hash) % payment_id % pd.m_subaddr_index.minor % "-" % note).str()))); } } @@ -6135,7 +6132,7 @@ bool simple_wallet::show_transfers(const std::vector &args_) if (payment_id.substr(16).find_first_not_of('0') == std::string::npos) payment_id = payment_id.substr(0,16); std::string note = m_wallet->get_tx_note(i->first); - output.insert(std::make_pair(pd.m_block_height, std::make_pair(false, (boost::format("%16.16s %20.20s %s %s %14.14s %s %s - %s") % get_human_readable_timestamp(pd.m_timestamp) % print_money(pd.m_amount_in - change - fee) % string_tools::pod_to_hex(i->first) % payment_id % print_money(fee) % dests % print_subaddr_indices(pd.m_subaddr_indices) % note).str()))); + output.insert(std::make_pair(pd.m_block_height, std::make_pair(false, (boost::format("%25.25s %20.20s %s %s %14.14s %s %s - %s") % get_human_readable_timestamp(pd.m_timestamp) % print_money(pd.m_amount_in - change - fee) % string_tools::pod_to_hex(i->first) % payment_id % print_money(fee) % dests % print_subaddr_indices(pd.m_subaddr_indices) % note).str()))); } } @@ -6161,7 +6158,7 @@ bool simple_wallet::show_transfers(const std::vector &args_) std::string double_spend_note; if (i->second.m_double_spend_seen) double_spend_note = tr("[Double spend seen on the network: this transaction may or may not end up being mined] "); - message_writer() << (boost::format("%8.8s %6.6s %16.16s %20.20s %s %s %d %s %s%s") % "pool" % "in" % get_human_readable_timestamp(pd.m_timestamp) % print_money(pd.m_amount) % string_tools::pod_to_hex(pd.m_tx_hash) % payment_id % pd.m_subaddr_index.minor % "-" % note % double_spend_note).str(); + message_writer() << (boost::format("%8.8s %6.6s %25.25s %20.20s %s %s %d %s %s%s") % "pool" % "in" % get_human_readable_timestamp(pd.m_timestamp) % print_money(pd.m_amount) % string_tools::pod_to_hex(pd.m_tx_hash) % payment_id % pd.m_subaddr_index.minor % "-" % note % double_spend_note).str(); } } catch (const std::exception& e) @@ -6184,7 +6181,7 @@ bool simple_wallet::show_transfers(const std::vector &args_) std::string note = m_wallet->get_tx_note(i->first); bool is_failed = pd.m_state == tools::wallet2::unconfirmed_transfer_details::failed; if ((failed && is_failed) || (!is_failed && pending)) { - message_writer() << (boost::format("%8.8s %6.6s %16.16s %20.20s %s %s %14.14s %s - %s") % (is_failed ? tr("failed") : tr("pending")) % tr("out") % get_human_readable_timestamp(pd.m_timestamp) % print_money(amount - pd.m_change - fee) % string_tools::pod_to_hex(i->first) % payment_id % print_money(fee) % print_subaddr_indices(pd.m_subaddr_indices) % note).str(); + message_writer() << (boost::format("%8.8s %6.6s %25.25s %20.20s %s %s %14.14s %s - %s") % (is_failed ? tr("failed") : tr("pending")) % tr("out") % get_human_readable_timestamp(pd.m_timestamp) % print_money(amount - pd.m_change - fee) % string_tools::pod_to_hex(i->first) % payment_id % print_money(fee) % print_subaddr_indices(pd.m_subaddr_indices) % note).str(); } } } From 6b13976330b6a7d3ebbea160080146c8eb46e1d3 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 23 Apr 2018 11:43:12 +0100 Subject: [PATCH 0007/1404] blockchain: log in DEBUG when a block is found, and where Eases up debugging --- src/cryptonote_core/blockchain.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index f02a1f8d6b3..0ad1c8c9922 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -2217,19 +2217,19 @@ bool Blockchain::have_block(const crypto::hash& id) const if(m_db->block_exists(id)) { - LOG_PRINT_L3("block exists in main chain"); + LOG_PRINT_L2("block " << id << " found in main chain"); return true; } if(m_alternative_chains.count(id)) { - LOG_PRINT_L3("block found in m_alternative_chains"); + LOG_PRINT_L2("block " << id << " found in m_alternative_chains"); return true; } if(m_invalid_blocks.count(id)) { - LOG_PRINT_L3("block found in m_invalid_blocks"); + LOG_PRINT_L2("block " << id << " found in m_invalid_blocks"); return true; } From efcecb42f3b7380a239c09aac5d57d4f93671463 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 24 Apr 2018 17:06:25 +0100 Subject: [PATCH 0008/1404] mnemonics: add some logs to help debug failures --- src/mnemonics/electrum-words.cpp | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/src/mnemonics/electrum-words.cpp b/src/mnemonics/electrum-words.cpp index 6a2a3e0c46a..7dd09ecb96d 100644 --- a/src/mnemonics/electrum-words.cpp +++ b/src/mnemonics/electrum-words.cpp @@ -67,6 +67,9 @@ #include "language_base.h" #include "singleton.h" +#undef MONERO_DEFAULT_LOG_CATEGORY +#define MONERO_DEFAULT_LOG_CATEGORY "mnemonic" + namespace { uint32_t create_checksum_index(const std::vector &word_list, @@ -152,6 +155,7 @@ namespace if (full_match) { *language = *it1; + MINFO("Full match for language " << (*language)->get_english_language_name()); return true; } // Some didn't match. Clear the index array. @@ -164,9 +168,11 @@ namespace if (fallback) { *language = fallback; + MINFO("Fallback match for language " << (*language)->get_english_language_name()); return true; } + MINFO("No match found"); return false; } @@ -217,7 +223,9 @@ namespace checksum; std::string trimmed_last_word = last_word.length() > unique_prefix_length ? Language::utf8prefix(last_word, unique_prefix_length) : last_word; - return trimmed_checksum == trimmed_last_word; + bool ret = trimmed_checksum == trimmed_last_word; + MINFO("Checksum is %s" << (ret ? "valid" : "invalid")); + return ret; } } @@ -253,7 +261,10 @@ namespace crypto boost::split(seed, words, boost::is_any_of(" "), boost::token_compress_on); if (len % 4) + { + MERROR("Invalid seed: not a multiple of 4"); return false; + } bool has_checksum = true; if (len) @@ -263,6 +274,7 @@ namespace crypto if (seed.size() != expected/2 && seed.size() != expected && seed.size() != expected + 1) { + MERROR("Invalid seed: unexpected number of words"); return false; } @@ -274,6 +286,7 @@ namespace crypto Language::Base *language; if (!find_seed_language(seed, has_checksum, matched_indices, &language)) { + MERROR("Invalid seed: language not found"); return false; } language_name = language->get_language_name(); @@ -284,6 +297,7 @@ namespace crypto if (!checksum_test(seed, language->get_unique_prefix_length())) { // Checksum fail + MERROR("Invalid seed: invalid checksum"); return false; } seed.pop_back(); @@ -300,7 +314,11 @@ namespace crypto val = w1 + word_list_length * (((word_list_length - w1) + w2) % word_list_length) + word_list_length * word_list_length * (((word_list_length - w2) + w3) % word_list_length); - if (!(val % word_list_length == w1)) return false; + if (!(val % word_list_length == w1)) + { + MERROR("Invalid seed: mumble mumble"); + return false; + } dst.append((const char*)&val, 4); // copy 4 bytes to position } @@ -332,9 +350,15 @@ namespace crypto { std::string s; if (!words_to_bytes(words, s, sizeof(dst), true, language_name)) + { + MERROR("Invalid seed: failed to convert words to bytes"); return false; + } if (s.size() != sizeof(dst)) + { + MERROR("Invalid seed: wrong output size"); return false; + } dst = *(const crypto::secret_key*)s.data(); return true; } From 3ca267b54697e2388bf0900dc083d6d4bf873b83 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 24 Apr 2018 20:56:41 +0100 Subject: [PATCH 0009/1404] cn_deserialize: dump additional tx pubkeys --- src/debug_utilities/cn_deserialize.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/debug_utilities/cn_deserialize.cpp b/src/debug_utilities/cn_deserialize.cpp index dfbd3b8645e..6c09b0f1835 100644 --- a/src/debug_utilities/cn_deserialize.cpp +++ b/src/debug_utilities/cn_deserialize.cpp @@ -27,6 +27,8 @@ // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include +#include +#include #include "cryptonote_basic/cryptonote_basic.h" #include "cryptonote_basic/tx_extra.h" #include "cryptonote_core/blockchain.h" @@ -51,6 +53,7 @@ static void print_extra_fields(const std::vector &fi else if (typeid(cryptonote::tx_extra_pub_key) == fields[n].type()) std::cout << "extra pub key: " << boost::get(fields[n]).pub_key; else if (typeid(cryptonote::tx_extra_nonce) == fields[n].type()) std::cout << "extra nonce: " << epee::string_tools::buff_to_hex_nodelimer(boost::get(fields[n]).nonce); else if (typeid(cryptonote::tx_extra_merge_mining_tag) == fields[n].type()) std::cout << "extra merge mining tag: depth " << boost::get(fields[n]).depth << ", merkle root " << boost::get(fields[n]).merkle_root; + else if (typeid(cryptonote::tx_extra_additional_pub_keys) == fields[n].type()) std::cout << "additional tx pubkeys: " << boost::join(boost::get(fields[n]).data | boost::adaptors::transformed([](const crypto::public_key &key){ return epee::string_tools::pod_to_hex(key); }), ", " ); else if (typeid(cryptonote::tx_extra_mysterious_minergate) == fields[n].type()) std::cout << "extra minergate custom: " << epee::string_tools::buff_to_hex_nodelimer(boost::get(fields[n]).data); else std::cout << "unknown"; std::cout << std::endl; From b21bc007049ed73a6c32cd49eb3178c6c43ad5aa Mon Sep 17 00:00:00 2001 From: naughtyfox Date: Tue, 10 Apr 2018 18:38:54 +0300 Subject: [PATCH 0010/1404] Wallet: added methods to sign and verify arbitrary message with multisig public signer's key (libwallet & wallet api) --- src/wallet/api/wallet.cpp | 44 ++++++++++++++++++++++++++++++++++++ src/wallet/api/wallet.h | 2 ++ src/wallet/api/wallet2_api.h | 15 ++++++++++++ src/wallet/wallet2.cpp | 36 +++++++++++++++++++++++++++++ src/wallet/wallet2.h | 16 +++++++++++++ 5 files changed, 113 insertions(+) diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index b02884f6745..63211fab0bd 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -1694,6 +1694,50 @@ bool WalletImpl::verifySignedMessage(const std::string &message, const std::stri return m_wallet->verify(message, info.address, signature); } +std::string WalletImpl::signMultisigParticipant(const std::string &message) const +{ + clearStatus(); + + bool ready = false; + if (!m_wallet->multisig(&ready) || !ready) { + m_status = Status_Error; + m_errorString = tr("The wallet must be in multisig ready state"); + return {}; + } + + try { + return m_wallet->sign_multisig_participant(message); + } catch (const std::exception& e) { + m_status = Status_Error; + m_errorString = e.what(); + } + + return {}; +} + +bool WalletImpl::verifyMessageWithPublicKey(const std::string &message, const std::string &publicKey, const std::string &signature) const +{ + clearStatus(); + + cryptonote::blobdata pkeyData; + if(!epee::string_tools::parse_hexstr_to_binbuff(publicKey, pkeyData) || pkeyData.size() != sizeof(crypto::public_key)) + { + m_status = Status_Error; + m_errorString = tr("Given string is not a key"); + return false; + } + + try { + crypto::public_key pkey = *reinterpret_cast(pkeyData.data()); + return m_wallet->verify_with_public_key(message, pkey, signature); + } catch (const std::exception& e) { + m_status = Status_Error; + m_errorString = e.what(); + } + + return false; +} + bool WalletImpl::connectToDaemon() { bool result = m_wallet->check_connection(NULL, DEFAULT_CONNECTION_TIMEOUT_MILLIS); diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h index 4929c96739f..fed1e75f2d0 100644 --- a/src/wallet/api/wallet.h +++ b/src/wallet/api/wallet.h @@ -157,6 +157,8 @@ class WalletImpl : public Wallet virtual bool checkReserveProof(const std::string &address, const std::string &message, const std::string &signature, bool &good, uint64_t &total, uint64_t &spent) const; virtual std::string signMessage(const std::string &message); virtual bool verifySignedMessage(const std::string &message, const std::string &address, const std::string &signature) const; + virtual std::string signMultisigParticipant(const std::string &message) const; + virtual bool verifyMessageWithPublicKey(const std::string &message, const std::string &publicKey, const std::string &signature) const; virtual void startRefresh(); virtual void pauseRefresh(); virtual bool parse_uri(const std::string &uri, std::string &address, std::string &payment_id, uint64_t &amount, std::string &tx_description, std::string &recipient_name, std::vector &unknown_parameters, std::string &error); diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h index d4e41c5aa38..4f9d2595777 100644 --- a/src/wallet/api/wallet2_api.h +++ b/src/wallet/api/wallet2_api.h @@ -747,6 +747,21 @@ struct Wallet */ virtual bool verifySignedMessage(const std::string &message, const std::string &addres, const std::string &signature) const = 0; + /*! + * \brief signMultisigParticipant signs given message with the multisig public signer key + * \param message message to sign + * \return signature in case of success. Sets status to Error and return empty string in case of error + */ + virtual std::string signMultisigParticipant(const std::string &message) const = 0; + /*! + * \brief verifyMessageWithPublicKey verifies that message was signed with the given public key + * \param message message + * \param publicKey hex encoded public key + * \param signature signature of the message + * \return true if the signature is correct. false and sets error state in case of error + */ + virtual bool verifyMessageWithPublicKey(const std::string &message, const std::string &publicKey, const std::string &signature) const = 0; + virtual bool parse_uri(const std::string &uri, std::string &address, std::string &payment_id, uint64_t &amount, std::string &tx_description, std::string &recipient_name, std::vector &unknown_parameters, std::string &error) = 0; virtual std::string getDefaultDataDir() const = 0; diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 4b7e6dd93af..ac83319706e 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -115,6 +115,8 @@ using namespace cryptonote; #define STAGENET_SEGREGATION_FORK_HEIGHT 1000000 #define SEGREGATION_FORK_VICINITY 1500 /* blocks */ +static const std::string MULTISIG_SIGNATURE_MAGIC = "SigMultisigPkV1"; + namespace { @@ -9149,6 +9151,40 @@ bool wallet2::verify(const std::string &data, const cryptonote::account_public_a memcpy(&s, decoded.data(), sizeof(s)); return crypto::check_signature(hash, address.m_spend_public_key, s); } + +std::string wallet2::sign_multisig_participant(const std::string& data) const +{ + CHECK_AND_ASSERT_THROW_MES(m_multisig, "Wallet is not multisig"); + + crypto::hash hash; + crypto::cn_fast_hash(data.data(), data.size(), hash); + const cryptonote::account_keys &keys = m_account.get_keys(); + crypto::signature signature; + crypto::generate_signature(hash, get_multisig_signer_public_key(), keys.m_spend_secret_key, signature); + return MULTISIG_SIGNATURE_MAGIC + tools::base58::encode(std::string((const char *)&signature, sizeof(signature))); +} + +bool wallet2::verify_with_public_key(const std::string &data, const crypto::public_key &public_key, const std::string &signature) const +{ + if (signature.size() < MULTISIG_SIGNATURE_MAGIC.size() || signature.substr(0, MULTISIG_SIGNATURE_MAGIC.size()) != MULTISIG_SIGNATURE_MAGIC) { + MERROR("Signature header check error"); + return false; + } + crypto::hash hash; + crypto::cn_fast_hash(data.data(), data.size(), hash); + std::string decoded; + if (!tools::base58::decode(signature.substr(MULTISIG_SIGNATURE_MAGIC.size()), decoded)) { + MERROR("Signature decoding error"); + return false; + } + crypto::signature s; + if (sizeof(s) != decoded.size()) { + MERROR("Signature decoding error"); + return false; + } + memcpy(&s, decoded.data(), sizeof(s)); + return crypto::check_signature(hash, public_key, s); +} //---------------------------------------------------------------------------------------------------- crypto::public_key wallet2::get_tx_pub_key_from_received_outs(const tools::wallet2::transfer_details &td) const { diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index abc7bb538cd..4989875d4b3 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -964,6 +964,22 @@ namespace tools std::string sign(const std::string &data) const; bool verify(const std::string &data, const cryptonote::account_public_address &address, const std::string &signature) const; + /*! + * \brief sign_multisig_participant signs given message with the multisig public signer key + * \param data message to sign + * \throws if wallet is not multisig + * \return signature + */ + std::string sign_multisig_participant(const std::string& data) const; + /*! + * \brief verify_with_public_key verifies message was signed with given public key + * \param data message + * \param public_key public key to check signature + * \param signature signature of the message + * \return true if the signature is correct + */ + bool verify_with_public_key(const std::string &data, const crypto::public_key &public_key, const std::string &signature) const; + // Import/Export wallet data std::vector export_outputs() const; size_t import_outputs(const std::vector &outputs); From 099bb830cf79c07821be18bf33f92d222f2cf3a4 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 15 Apr 2018 19:15:18 +0100 Subject: [PATCH 0011/1404] easylogging++: cached allowed categories It turns out this can be fairly heavy when logging a lot --- external/easylogging++/easylogging++.cc | 11 ++++++++++- external/easylogging++/easylogging++.h | 2 ++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/external/easylogging++/easylogging++.cc b/external/easylogging++/easylogging++.cc index 212a1822dd1..8a5dc91e956 100644 --- a/external/easylogging++/easylogging++.cc +++ b/external/easylogging++/easylogging++.cc @@ -1967,10 +1967,12 @@ void VRegistry::setCategories(const char* categories, bool clear) { base::threading::ScopedLock scopedLock(lock()); auto insert = [&](std::stringstream& ss, Level level) { m_categories.push_back(std::make_pair(ss.str(), level)); + m_cached_allowed_categories.clear(); }; if (clear) { m_categories.clear(); + m_cached_allowed_categories.clear(); m_categoriesString.clear(); } if (!m_categoriesString.empty()) @@ -2033,15 +2035,22 @@ static int priority(Level level) { bool VRegistry::allowed(Level level, const char* category) { base::threading::ScopedLock scopedLock(lock()); + const std::string scategory = category; + const std::map::const_iterator it = m_cached_allowed_categories.find(scategory); + if (it != m_cached_allowed_categories.end()) + return priority(level) <= it->second; if (m_categories.empty() || category == nullptr) { return false; } else { std::deque>::const_reverse_iterator it = m_categories.rbegin(); for (; it != m_categories.rend(); ++it) { if (base::utils::Str::wildCardMatch(category, it->first.c_str())) { - return priority(level) <= priority(it->second); + const int p = priority(it->second); + m_cached_allowed_categories.insert(std::make_pair(std::move(scategory), p)); + return priority(level) <= p; } } + m_cached_allowed_categories.insert(std::make_pair(std::move(scategory), -1)); return false; } } diff --git a/external/easylogging++/easylogging++.h b/external/easylogging++/easylogging++.h index 3270bd6074a..6b8b4fc35f3 100644 --- a/external/easylogging++/easylogging++.h +++ b/external/easylogging++/easylogging++.h @@ -2485,6 +2485,7 @@ class VRegistry : base::NoCopy, public base::threading::ThreadSafe { inline void clearCategories(void) { base::threading::ScopedLock scopedLock(lock()); m_categories.clear(); + m_cached_allowed_categories.clear(); } inline void clearModules(void) { @@ -2526,6 +2527,7 @@ class VRegistry : base::NoCopy, public base::threading::ThreadSafe { base::type::EnumType* m_pFlags; std::map m_modules; std::deque> m_categories; + std::map m_cached_allowed_categories; std::string m_categoriesString; std::string m_filenameCommonPrefix; }; From 9cc0d4220fd7b13c3b2f15e7cb8b06f6f1b0e2fb Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 28 Apr 2018 19:55:23 +0100 Subject: [PATCH 0012/1404] connection_context: remove "state_" prefix from state names It's redundant and makes it easier to print them in columns --- src/cryptonote_basic/connection_context.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/cryptonote_basic/connection_context.h b/src/cryptonote_basic/connection_context.h index 5cd1709ab35..3f4651565da 100644 --- a/src/cryptonote_basic/connection_context.h +++ b/src/cryptonote_basic/connection_context.h @@ -67,15 +67,15 @@ namespace cryptonote switch (s) { case cryptonote_connection_context::state_before_handshake: - return "state_before_handshake"; + return "before_handshake"; case cryptonote_connection_context::state_synchronizing: - return "state_synchronizing"; + return "synchronizing"; case cryptonote_connection_context::state_standby: - return "state_standby"; + return "standby"; case cryptonote_connection_context::state_idle: - return "state_idle"; + return "idle"; case cryptonote_connection_context::state_normal: - return "state_normal"; + return "normal"; default: return "unknown"; } From d9d002c3c1ed43a26f8d1227518d2af17625580b Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 28 Apr 2018 19:56:39 +0100 Subject: [PATCH 0013/1404] daemon: print peer state in sync_info It's often relevant --- src/daemon/rpc_command_executor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp index 64ce3a4067b..97571505d02 100644 --- a/src/daemon/rpc_command_executor.cpp +++ b/src/daemon/rpc_command_executor.cpp @@ -1891,7 +1891,7 @@ bool t_rpc_command_executor::sync_info() for (const auto &s: res.spans) if (s.rate > 0.0f && s.connection_id == p.info.connection_id) nblocks += s.nblocks, size += s.size; - tools::success_msg_writer() << address << " " << epee::string_tools::pad_string(p.info.peer_id, 16, '0', true) << " " << p.info.height << " " << p.info.current_download << " kB/s, " << nblocks << " blocks / " << size/1e6 << " MB queued"; + tools::success_msg_writer() << address << " " << epee::string_tools::pad_string(p.info.peer_id, 16, '0', true) << " " << epee::string_tools::pad_string(p.info.state, 16) << " " << p.info.height << " " << p.info.current_download << " kB/s, " << nblocks << " blocks / " << size/1e6 << " MB queued"; } uint64_t total_size = 0; From 159018396556968416d932591877e8aaf4c34354 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 29 Apr 2018 14:57:08 +0100 Subject: [PATCH 0014/1404] p2p: fix fallback seed node usage Those were added to the seed nodes list even when they had already been added. Moreover, the current index was not reset after they were added, typically causing previous seeds to be used, and some of those fallback seeds to not be tried. --- src/p2p/net_node.h | 1 + src/p2p/net_node.inl | 13 ++++++++++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h index 4606f66ee98..d5346afb536 100644 --- a/src/p2p/net_node.h +++ b/src/p2p/net_node.h @@ -316,6 +316,7 @@ namespace nodetool std::list m_priority_peers; std::vector m_exclusive_peers; std::vector m_seed_nodes; + bool m_fallback_seed_nodes_added; std::list m_command_line_peers; uint64_t m_peer_livetime; //keep connections to initiate some interactions diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index 9b21705ec15..07f369d4054 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -405,6 +405,7 @@ namespace nodetool bool res = handle_command_line(vm); CHECK_AND_ASSERT_MES(res, false, "Failed to handle command line"); + m_fallback_seed_nodes_added = false; if (m_nettype == cryptonote::TESTNET) { memcpy(&m_network_id, &::config::testnet::NETWORK_ID, 16); @@ -498,6 +499,7 @@ namespace nodetool for (const auto &peer: get_seed_nodes(cryptonote::MAINNET)) full_addrs.insert(peer); + m_fallback_seed_nodes_added = true; } } } @@ -1134,7 +1136,6 @@ namespace nodetool size_t try_count = 0; size_t current_index = crypto::rand()%m_seed_nodes.size(); - bool fallback_nodes_added = false; while(true) { if(m_net_server.is_stop_signal_sent()) @@ -1144,15 +1145,21 @@ namespace nodetool break; if(++try_count > m_seed_nodes.size()) { - if (!fallback_nodes_added) + if (!m_fallback_seed_nodes_added) { MWARNING("Failed to connect to any of seed peers, trying fallback seeds"); + current_index = m_seed_nodes.size(); for (const auto &peer: get_seed_nodes(m_nettype)) { MDEBUG("Fallback seed node: " << peer); append_net_address(m_seed_nodes, peer); } - fallback_nodes_added = true; + m_fallback_seed_nodes_added = true; + if (current_index == m_seed_nodes.size()) + { + MWARNING("No fallback seeds, continuing without seeds"); + break; + } // continue for another few cycles } else From fa0839f2f58ec84bbfe619aec25a6393277681c8 Mon Sep 17 00:00:00 2001 From: Thaer Khawaja Date: Sun, 29 Apr 2018 19:31:40 -0700 Subject: [PATCH 0015/1404] Ensure m_timestamps has the correct number for computing difficulty. --- src/cryptonote_core/blockchain.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 25e97f71fc3..a059e4d6e51 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -760,7 +760,7 @@ difficulty_type Blockchain::get_difficulty_for_next_block() // then when the next block difficulty is queried, push the latest height data and // pop the oldest one from the list. This only requires 1x read per height instead // of doing 735 (DIFFICULTY_BLOCKS_COUNT). - if (m_timestamps_and_difficulties_height != 0 && ((height - m_timestamps_and_difficulties_height) == 1)) + if (m_timestamps_and_difficulties_height != 0 && ((height - m_timestamps_and_difficulties_height) == 1) && m_timestamps.size() >= DIFFICULTY_BLOCKS_COUNT) { uint64_t index = height - 1; m_timestamps.push_back(m_db->get_block_timestamp(index)); From 4812c062c5c09876a62e47df0621a68cfcdf739d Mon Sep 17 00:00:00 2001 From: Teutone Date: Mon, 30 Apr 2018 19:01:58 +0200 Subject: [PATCH 0016/1404] add .load() to make Boost 1.67 happy with its new is_integral check --- src/wallet/api/wallet.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index b78a471b9da..7d638990486 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -1752,7 +1752,7 @@ void WalletImpl::refreshThreadFunc() // if auto refresh enabled, we wait for the "m_refreshIntervalSeconds" interval. // if not - we wait forever if (m_refreshIntervalMillis > 0) { - boost::posix_time::milliseconds wait_for_ms(m_refreshIntervalMillis); + boost::posix_time::milliseconds wait_for_ms(m_refreshIntervalMillis.load()); m_refreshCV.timed_wait(lock, wait_for_ms); } else { m_refreshCV.wait(lock); From 6f9260e38e15b7e78fb93977877000aec268aebe Mon Sep 17 00:00:00 2001 From: cryptochangements34 Date: Sun, 6 May 2018 12:28:57 -0400 Subject: [PATCH 0017/1404] handle optional miner params better --- src/cryptonote_basic/miner.cpp | 5 +++++ src/daemon/command_parser_executor.cpp | 19 +++++++++++++++++-- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/cryptonote_basic/miner.cpp b/src/cryptonote_basic/miner.cpp index 2c777f5a26d..3a3222f9b87 100644 --- a/src/cryptonote_basic/miner.cpp +++ b/src/cryptonote_basic/miner.cpp @@ -328,6 +328,11 @@ namespace cryptonote LOG_PRINT_L0("Background mining controller thread started" ); } + if(get_ignore_battery()) + { + MINFO("Ignoring battery"); + } + return true; } //----------------------------------------------------------------------------------------------------- diff --git a/src/daemon/command_parser_executor.cpp b/src/daemon/command_parser_executor.cpp index 8b51b9b857f..db7041e71e9 100644 --- a/src/daemon/command_parser_executor.cpp +++ b/src/daemon/command_parser_executor.cpp @@ -27,6 +27,7 @@ // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "common/dns_utils.h" +#include "common/command_line.h" #include "version.h" #include "daemon/command_parser_executor.h" @@ -326,12 +327,26 @@ bool t_command_parser_executor::start_mining(const std::vector& arg if(args.size() == 4) { - ignore_battery = args[3] == "true"; + if(args[3] == "true" || command_line::is_yes(args[3]) || args[3] == "1") + { + ignore_battery = true; + } + else if(args[3] != "false" && !command_line::is_no(args[3]) && args[3] != "0") + { + return false; + } } if(args.size() >= 3) { - do_background_mining = args[2] == "true"; + if(args[2] == "true" || command_line::is_yes(args[2]) || args[2] == "1") + { + do_background_mining = true; + } + else if(args[2] != "false" && !command_line::is_no(args[2]) && args[2] != "0") + { + return false; + } } if(args.size() >= 2) From eb9f3a3294c1c57009ec1b8f6428932caa4ccacf Mon Sep 17 00:00:00 2001 From: itssteven Date: Sun, 6 May 2018 17:42:27 +0100 Subject: [PATCH 0018/1404] check_spend_proof My intention is to mitigate #3761 by returning "bad signature", rather than throwing an error, as the error is triggered inappropriately in the case of checking a different txid than the one used to create the signature, which causes issues for monerophp: https://github.com/monero-integrations/monerophp/issues/72 & my temp fix: https://github.com/monero-integrations/monerophp/pull/74 --- src/wallet/wallet2.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index d53ed82a979..b14b88a9aa1 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -8414,8 +8414,9 @@ bool wallet2::check_spend_proof(const crypto::hash &txid, const std::string &mes } std::vector> signatures = { std::vector(1) }; const size_t sig_len = tools::base58::encode(std::string((const char *)&signatures[0][0], sizeof(crypto::signature))).size(); - THROW_WALLET_EXCEPTION_IF(sig_str.size() != header_len + num_sigs * sig_len, - error::wallet_internal_error, "incorrect signature size"); + if( sig_str.size() != header_len + num_sigs * sig_len ) { + return false; + } // decode base58 signatures.clear(); From a7ce392eeffe54d86fa54ee9ce15f53a614c5511 Mon Sep 17 00:00:00 2001 From: Gene Peters Date: Mon, 7 May 2018 14:44:38 -0700 Subject: [PATCH 0019/1404] Updating the FindReadline CMake hint to search in homebrew's default directory --- cmake/FindReadline.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/FindReadline.cmake b/cmake/FindReadline.cmake index 87f8ccace0a..de9ddc46dce 100644 --- a/cmake/FindReadline.cmake +++ b/cmake/FindReadline.cmake @@ -23,7 +23,7 @@ find_path(Readline_ROOT_DIR NAMES include/readline/readline.h - PATHS /opt/local/ /usr/local/ /usr/ + PATHS /usr/local/opt/readline/ /opt/local/ /usr/local/ /usr/ NO_DEFAULT_PATH ) From 261ee5628b08149728b023bdb9ad032211e07bd0 Mon Sep 17 00:00:00 2001 From: cryptochangements34 Date: Tue, 8 May 2018 19:09:14 -0400 Subject: [PATCH 0020/1404] don't limit miner threads in wallet --- src/simplewallet/simplewallet.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 639b394f38d..14bc693081d 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -3667,7 +3667,6 @@ bool simple_wallet::start_mining(const std::vector& args) req.miner_address = m_wallet->get_account().get_public_address_str(m_wallet->nettype()); bool ok = true; - size_t max_mining_threads_count = (std::max)(tools::get_max_concurrency(), static_cast(2)); size_t arg_size = args.size(); if(arg_size >= 3) { @@ -3683,7 +3682,7 @@ bool simple_wallet::start_mining(const std::vector& args) { uint16_t num = 1; ok = string_tools::get_xtype_from_string(num, args[0]); - ok = ok && (1 <= num && num <= max_mining_threads_count); + ok = ok && 1 <= num; req.threads_count = num; } else @@ -3693,8 +3692,7 @@ bool simple_wallet::start_mining(const std::vector& args) if (!ok) { - fail_msg_writer() << tr("invalid arguments. Please use start_mining [] [do_bg_mining] [ignore_battery], " - " should be from 1 to ") << max_mining_threads_count; + fail_msg_writer() << tr("invalid arguments. Please use start_mining [] [do_bg_mining] [ignore_battery]"); return true; } From b5cb1bc4034d10e254ddd3d1f6093853e1ba9517 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 29 Apr 2018 11:41:54 +0100 Subject: [PATCH 0021/1404] blockchain: avoid exception if asked for a block we do not have This can happen if a peer tries to obtain the next span from other peers if that span is needed for not downloaded yet. Also if the peer maliciously requests a non existent block hash. --- src/cryptonote_core/blockchain.cpp | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 25e97f71fc3..65bee4d8636 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -2032,16 +2032,19 @@ bool Blockchain::get_blocks(const t_ids_container& block_ids, t_blocks_container { try { - blocks.push_back(std::make_pair(m_db->get_block_blob(block_hash), block())); - if (!parse_and_validate_block_from_blob(blocks.back().first, blocks.back().second)) + uint64_t height = 0; + if (m_db->block_exists(block_hash, &height)) { - LOG_ERROR("Invalid block"); - return false; + blocks.push_back(std::make_pair(m_db->get_block_blob_from_height(height), block())); + if (!parse_and_validate_block_from_blob(blocks.back().first, blocks.back().second)) + { + LOG_ERROR("Invalid block: " << block_hash); + blocks.pop_back(); + missed_bs.push_back(block_hash); + } } - } - catch (const BLOCK_DNE& e) - { - missed_bs.push_back(block_hash); + else + missed_bs.push_back(block_hash); } catch (const std::exception& e) { From f357210c00ef0c3f2a96f814c0eba0cc1a20cf3c Mon Sep 17 00:00:00 2001 From: Rafficer Date: Sat, 12 May 2018 20:18:21 +0200 Subject: [PATCH 0022/1404] Fix Typo --- src/simplewallet/simplewallet.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 639b394f38d..0c2cca9dc90 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -507,7 +507,7 @@ namespace } if (warn_of_possible_attack) - fail_msg_writer() << tr("There was an error, which could mean the node may be trying to get you to retry creating a transaction, and zero in on which outputs you own. Or it could be a bona fide error. It may be prudent to disconnect from this node, and not try to send a tranasction immediately. Alternatively, connect to another node so the original node cannot correlate information."); + fail_msg_writer() << tr("There was an error, which could mean the node may be trying to get you to retry creating a transaction, and zero in on which outputs you own. Or it could be a bona fide error. It may be prudent to disconnect from this node, and not try to send a transaction immediately. Alternatively, connect to another node so the original node cannot correlate information."); } bool check_file_overwrite(const std::string &filename) From 89e51ecd38e6958f9de8431c2c95dc76e9b246cd Mon Sep 17 00:00:00 2001 From: jcktm Date: Thu, 19 Apr 2018 12:17:45 +1000 Subject: [PATCH 0023/1404] simple-wallet-cli: Add warnings about inaccurate balances to to watch-only wallet --- src/simplewallet/simplewallet.cpp | 2 ++ src/wallet/wallet2.cpp | 8 ++++++++ src/wallet/wallet2.h | 1 + 3 files changed, 11 insertions(+) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 639b394f38d..3a13181baba 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -3923,6 +3923,8 @@ bool simple_wallet::show_balance_unlocked(bool detailed) std::string extra; if (m_wallet->has_multisig_partial_key_images()) extra = tr(" (Some owned outputs have partial key images - import_multisig_info needed)"); + else if (m_wallet->has_unknown_key_images()) + extra += tr(" (Some owned outputs have missing key images - import_key_images needed)"); success_msg_writer() << tr("Currently selected account: [") << m_current_subaddress_account << tr("] ") << m_wallet->get_subaddress_label({m_current_subaddress_account, 0}); const std::string tag = m_wallet->get_account_tags().second[m_current_subaddress_account]; success_msg_writer() << tr("Tag: ") << (tag.empty() ? std::string{tr("(No tag assigned)")} : tag); diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 6a37b59eb9a..6a77584f4fa 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -3611,6 +3611,14 @@ bool wallet2::has_multisig_partial_key_images() const return false; } +bool wallet2::has_unknown_key_images() const +{ + for (const auto &td: m_transfers) + if (!td.m_key_image_known) + return true; + return false; +} + /*! * \brief Rewrites to the wallet file for wallet upgrade (doesn't generate key, assumes it's already there) * \param wallet_name Name of wallet file (should exist) diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 69b63876a35..763f8a9e22c 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -653,6 +653,7 @@ namespace tools bool watch_only() const { return m_watch_only; } bool multisig(bool *ready = NULL, uint32_t *threshold = NULL, uint32_t *total = NULL) const; bool has_multisig_partial_key_images() const; + bool has_unknown_key_images() const; bool get_multisig_seed(std::string& seed, const epee::wipeable_string &passphrase = std::string(), bool raw = true) const; bool key_on_device() const { return m_key_on_device; } From 558d05b47e6f6c2ff8362cc508dc68428ade889c Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 17 May 2018 10:38:00 +0100 Subject: [PATCH 0024/1404] epee: log type name when a container size check fails --- .../include/serialization/keyvalue_serialization_overloads.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/epee/include/serialization/keyvalue_serialization_overloads.h b/contrib/epee/include/serialization/keyvalue_serialization_overloads.h index 7087136ccfb..09087f78534 100644 --- a/contrib/epee/include/serialization/keyvalue_serialization_overloads.h +++ b/contrib/epee/include/serialization/keyvalue_serialization_overloads.h @@ -156,7 +156,7 @@ namespace epee typename stl_container::value_type* pelem = (typename stl_container::value_type*)buff.data(); CHECK_AND_ASSERT_MES(!(loaded_size%sizeof(typename stl_container::value_type)), false, - "size in blob " << loaded_size << " not have not zero modulo for sizeof(value_type) = " << sizeof(typename stl_container::value_type)); + "size in blob " << loaded_size << " not have not zero modulo for sizeof(value_type) = " << sizeof(typename stl_container::value_type) << ", type " << typeid(typename stl_container::value_type).name()); size_t count = (loaded_size/sizeof(typename stl_container::value_type)); for(size_t i = 0; i < count; i++) container.insert(container.end(), *(pelem++)); From a2566db09e45311f7af60a962c3bfd4cd4fef537 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 17 May 2018 10:38:26 +0100 Subject: [PATCH 0025/1404] object_sizes: add a few useful types --- src/debug_utilities/object_sizes.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/debug_utilities/object_sizes.cpp b/src/debug_utilities/object_sizes.cpp index a3d037a59df..ab883963688 100644 --- a/src/debug_utilities/object_sizes.cpp +++ b/src/debug_utilities/object_sizes.cpp @@ -94,6 +94,11 @@ int main(int argc, char* argv[]) SL(nodetool::node_server>); SL(nodetool::p2p_connection_context_t::connection_context>); SL(nodetool::network_address_old); + SL(nodetool::peerlist_entry_base); + + SL(nodetool::network_config); + SL(nodetool::basic_node_data); + SL(cryptonote::CORE_SYNC_DATA); SL(tools::wallet2::transfer_details); SL(tools::wallet2::payment_details); From 16e209e08dcb0e8ee49115071809e6540dc57ec1 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 18 May 2018 09:38:38 +0100 Subject: [PATCH 0026/1404] core: lock incoming tx lock when checking the txpool and chain This gets rid of an innocuous race trying to add the same tx twice to the txpool --- src/cryptonote_core/cryptonote_core.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index ce75f74d8cb..d723d35676f 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -675,6 +675,7 @@ namespace cryptonote bool core::handle_incoming_txs(const std::list& tx_blobs, std::vector& tvc, bool keeped_by_block, bool relayed, bool do_not_relay) { TRY_ENTRY(); + CRITICAL_REGION_LOCAL(m_incoming_tx_lock); struct result { bool res; cryptonote::transaction tx; crypto::hash hash; crypto::hash prefix_hash; bool in_txpool; bool in_blockchain; }; std::vector results(tx_blobs.size()); From d44d19ca79e1a6ae9e9dfd0ebecdb017b7cbf7f9 Mon Sep 17 00:00:00 2001 From: Judemir Ribeiro Date: Sat, 19 May 2018 18:03:59 -0400 Subject: [PATCH 0027/1404] Fixed for compilation in big endian PPC. "Everything is broken thought due to little endian logic." --- CMakeLists.txt | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b1297e716e8..189348ea0e1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -137,9 +137,24 @@ endif() if(ARCH_ID STREQUAL "ppc64le") set(PPC64LE 1) + set(PPC64 0) + set(PPC 0) + +endif() + +if(ARCH_ID STREQUAL "powerpc64" OR ARCH_ID STREQUAL "ppc64") + set(PPC64LE 0) + set(PPC64 1) + set(PPC 0) endif() -if(WIN32 OR ARM) +if(ARCH_ID STREQUAL "powerpc") + set(PPC64LE 0) + set(PPC64 0) + set(PPC 1) +endif() + +if(WIN32 OR ARM OR PPC64LE OR PPC64 OR PPC) set(OPT_FLAGS_RELEASE "-O2") else() set(OPT_FLAGS_RELEASE "-Ofast") @@ -474,7 +489,11 @@ else() if(ARCH STREQUAL "default") set(ARCH_FLAG "") elseif(PPC64LE) - set(ARCH_FLAG "-mcpu=${ARCH}") + set(ARCH_FLAG "-mcpu=power8") + elseif(PPC64) + set(ARCH_FLAG "-mcpu=970") + elseif(PPC) + set(ARCH_FLAG "-mcpu=7400") elseif(IOS AND ARCH STREQUAL "arm64") message(STATUS "IOS: Changing arch from arm64 to armv8") set(ARCH_FLAG "-march=armv8") @@ -584,12 +603,12 @@ else() message(STATUS "AES support explicitly disabled") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DNO_AES") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DNO_AES") - elseif(NOT ARM AND NOT PPC64LE) + elseif(NOT ARM AND NOT PPC64LE AND NOT PPC64 AND NOT PPC) message(STATUS "AES support enabled") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -maes") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -maes") - elseif(PPC64LE) - message(STATUS "AES support not available on ppc64le") + elseif(PPC64LE OR PPC64 OR PPC) + message(STATUS "AES support not available on POWER") elseif(ARM6) message(STATUS "AES support not available on ARMv6") elseif(ARM7) From db55263b4c25a5014ce72aa1b601cfdebfe5d176 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 23 Apr 2018 18:53:38 +0100 Subject: [PATCH 0028/1404] threadpool: allow constructing an object, and misc tweaks use unsigned int to avoid having to range check negative numbers, use const where possible, don't needlessly create empty objects, use std::move where possible --- src/common/threadpool.cpp | 8 ++++---- src/common/threadpool.h | 14 +++++++++----- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/common/threadpool.cpp b/src/common/threadpool.cpp index 51e0715772d..fb238dca7a1 100644 --- a/src/common/threadpool.cpp +++ b/src/common/threadpool.cpp @@ -39,11 +39,11 @@ static __thread int depth = 0; namespace tools { -threadpool::threadpool() : running(true), active(0) { +threadpool::threadpool(unsigned int max_threads) : running(true), active(0) { boost::thread::attributes attrs; attrs.set_stack_size(THREAD_STACK_SIZE); - max = tools::get_max_concurrency(); - size_t i = max; + max = max_threads ? max_threads : tools::get_max_concurrency(); + unsigned int i = max; while(i--) { threads.push_back(boost::thread(attrs, boost::bind(&threadpool::run, this))); } @@ -78,7 +78,7 @@ void threadpool::submit(waiter *obj, std::function f) { } } -int threadpool::get_max_concurrency() { +unsigned int threadpool::get_max_concurrency() const { return max; } diff --git a/src/common/threadpool.h b/src/common/threadpool.h index 34152541cb5..bf80a87f6c6 100644 --- a/src/common/threadpool.h +++ b/src/common/threadpool.h @@ -46,6 +46,9 @@ class threadpool static threadpool instance; return instance; } + static threadpool *getNewForUnitTests(unsigned max_threads = 0) { + return new threadpool(max_threads); + } // The waiter lets the caller know when all of its // tasks are completed. @@ -66,11 +69,12 @@ class threadpool // task to finish. void submit(waiter *waiter, std::function f); - int get_max_concurrency(); + unsigned int get_max_concurrency() const; + + ~threadpool(); private: - threadpool(); - ~threadpool(); + threadpool(unsigned int max_threads = 0); typedef struct entry { waiter *wo; std::function f; @@ -79,8 +83,8 @@ class threadpool boost::condition_variable has_work; boost::mutex mutex; std::vector threads; - int active; - int max; + unsigned int active; + unsigned int max; bool running; void run(); }; From ce173cbb5b119f8a34bbdfebcce817932422b71b Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 23 Apr 2018 18:55:58 +0100 Subject: [PATCH 0029/1404] core: remove threadpool dependency from header --- src/cryptonote_core/cryptonote_core.cpp | 6 +++--- src/cryptonote_core/cryptonote_core.h | 3 --- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 17400ab687c..61a9dd394e6 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -170,7 +170,6 @@ namespace cryptonote m_last_dns_checkpoints_update(0), m_last_json_checkpoints_update(0), m_disable_dns_checkpoints(false), - m_threadpool(tools::threadpool::getInstance()), m_update_download(0), m_nettype(UNDEFINED) { @@ -713,10 +712,11 @@ namespace cryptonote std::vector results(tx_blobs.size()); tvc.resize(tx_blobs.size()); + tools::threadpool& tpool = tools::threadpool::getInstance(); tools::threadpool::waiter waiter; std::list::const_iterator it = tx_blobs.begin(); for (size_t i = 0; i < tx_blobs.size(); i++, ++it) { - m_threadpool.submit(&waiter, [&, i, it] { + tpool.submit(&waiter, [&, i, it] { try { results[i].res = handle_incoming_tx_pre(*it, tvc[i], results[i].tx, results[i].hash, results[i].prefix_hash, keeped_by_block, relayed, do_not_relay); @@ -743,7 +743,7 @@ namespace cryptonote } else { - m_threadpool.submit(&waiter, [&, i, it] { + tpool.submit(&waiter, [&, i, it] { try { results[i].res = handle_incoming_tx_post(*it, tvc[i], results[i].tx, results[i].hash, results[i].prefix_hash, keeped_by_block, relayed, do_not_relay); diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index ff056c2a264..049c20a7290 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -39,7 +39,6 @@ #include "cryptonote_protocol/cryptonote_protocol_handler_common.h" #include "storages/portable_storage_template_helper.h" #include "common/download.h" -#include "common/threadpool.h" #include "common/command_line.h" #include "tx_pool.h" #include "blockchain.h" @@ -984,8 +983,6 @@ namespace cryptonote std::unordered_set bad_semantics_txes[2]; boost::mutex bad_semantics_txes_lock; - tools::threadpool& m_threadpool; - enum { UPDATES_DISABLED, UPDATES_NOTIFY, From 3147468d353a35bf4a6d67bcd594f30fcc717f41 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 23 Apr 2018 18:56:23 +0100 Subject: [PATCH 0030/1404] unit_tests: add threadpool unit test --- tests/unit_tests/CMakeLists.txt | 1 + tests/unit_tests/threadpool.cpp | 101 ++++++++++++++++++++++++++++++++ 2 files changed, 102 insertions(+) create mode 100644 tests/unit_tests/threadpool.cpp diff --git a/tests/unit_tests/CMakeLists.txt b/tests/unit_tests/CMakeLists.txt index 9c58536c947..46fd3f886e7 100644 --- a/tests/unit_tests/CMakeLists.txt +++ b/tests/unit_tests/CMakeLists.txt @@ -62,6 +62,7 @@ set(unit_tests_sources test_tx_utils.cpp test_peerlist.cpp test_protocol_pack.cpp + threadpool.cpp hardfork.cpp unbound.cpp uri.cpp diff --git a/tests/unit_tests/threadpool.cpp b/tests/unit_tests/threadpool.cpp new file mode 100644 index 00000000000..34be1417a12 --- /dev/null +++ b/tests/unit_tests/threadpool.cpp @@ -0,0 +1,101 @@ +// Copyright (c) 2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include +#include "gtest/gtest.h" +#include "misc_language.h" +#include "common/threadpool.h" + +TEST(threadpool, wait_nothing) +{ + std::shared_ptr tpool(tools::threadpool::getNewForUnitTests()); + tools::threadpool::waiter waiter; + waiter.wait(); +} + +TEST(threadpool, wait_waits) +{ + std::shared_ptr tpool(tools::threadpool::getNewForUnitTests()); + tools::threadpool::waiter waiter; + std::atomic b(false); + tpool->submit(&waiter, [&b](){ epee::misc_utils::sleep_no_w(1000); b = true; }); + ASSERT_FALSE(b); + waiter.wait(); + ASSERT_TRUE(b); +} + +TEST(threadpool, one_thread) +{ + std::shared_ptr tpool(tools::threadpool::getNewForUnitTests(1)); + tools::threadpool::waiter waiter; + + std::atomic counter(0); + for (size_t n = 0; n < 4096; ++n) + { + tpool->submit(&waiter, [&counter](){++counter;}); + } + waiter.wait(); + ASSERT_EQ(counter, 4096); +} + +TEST(threadpool, many_threads) +{ + std::shared_ptr tpool(tools::threadpool::getNewForUnitTests(256)); + tools::threadpool::waiter waiter; + + std::atomic counter(0); + for (size_t n = 0; n < 4096; ++n) + { + tpool->submit(&waiter, [&counter](){++counter;}); + } + waiter.wait(); + ASSERT_EQ(counter, 4096); +} + +static uint64_t fibonacci(std::shared_ptr tpool, uint64_t n) +{ + if (n <= 1) + return n; + uint64_t f1, f2; + tools::threadpool::waiter waiter; + tpool->submit(&waiter, [&tpool, &f1, n](){ f1 = fibonacci(tpool, n-1); }); + tpool->submit(&waiter, [&tpool, &f2, n](){ f2 = fibonacci(tpool, n-2); }); + waiter.wait(); + return f1 + f2; +} + +TEST(threadpool, reentrency) +{ + std::shared_ptr tpool(tools::threadpool::getNewForUnitTests(4)); + tools::threadpool::waiter waiter; + + uint64_t f = fibonacci(tpool, 13); + waiter.wait(); + ASSERT_EQ(f, 233); +} + From bdf5a3ad3fb9e626a0c1cc2264934f26d07d06fc Mon Sep 17 00:00:00 2001 From: cslashm Date: Wed, 23 May 2018 10:22:55 +0200 Subject: [PATCH 0031/1404] Fix PCSC compilation under windows PR3843 based on release-v0.12 => rebased on master --- CMakeLists.txt | 3 +++ cmake/FindPCSC.cmake | 6 ++++++ src/device/device_ledger.cpp | 9 +++++++++ src/device/device_ledger.hpp | 5 +++++ 4 files changed, 23 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index b1297e716e8..287f93bd1ec 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -452,9 +452,12 @@ link_directories(${LIBUNWIND_LIBRARY_DIRS}) # Final setup for libpcsc if (PCSC_FOUND) + message(STATUS "Using PCSC include dir at ${PCSC_INCLUDE_DIR}") add_definitions(-DHAVE_PCSC) include_directories(${PCSC_INCLUDE_DIR}) link_directories(${LIBPCSC_LIBRARY_DIRS}) +else (PCSC_FOUND) + message(STATUS "Could not find PCSC") endif() if(MSVC) diff --git a/cmake/FindPCSC.cmake b/cmake/FindPCSC.cmake index 8dd9d0e7640..8332abc4927 100644 --- a/cmake/FindPCSC.cmake +++ b/cmake/FindPCSC.cmake @@ -18,6 +18,9 @@ ENDIF (NOT WIN32) FIND_PATH(PCSC_INCLUDE_DIR winscard.h HINTS + IF (WIN32) + ${MSYS2_FOLDER}/mingw64/x86_64-w64-mingw32/include + ENDIF (WIN32) /usr/include/PCSC ${PC_PCSC_INCLUDEDIR} ${PC_PCSC_INCLUDE_DIRS} @@ -26,6 +29,9 @@ FIND_PATH(PCSC_INCLUDE_DIR winscard.h FIND_LIBRARY(PCSC_LIBRARY NAMES pcsclite libpcsclite WinSCard PCSC HINTS + IF (WIN32) + ${MSYS2_FOLDER}/mingw64/x86_64-w64-mingw32/lib + ENDIF (WIN32) ${PC_PCSC_LIBDIR} ${PC_PCSC_LIBRARY_DIRS} ) diff --git a/src/device/device_ledger.cpp b/src/device/device_ledger.cpp index 3b9ab674400..aedaf83829e 100644 --- a/src/device/device_ledger.cpp +++ b/src/device/device_ledger.cpp @@ -48,6 +48,15 @@ namespace hw { /* ===================================================================== */ /* === Debug ==== */ /* ===================================================================== */ + #ifdef WIN32 + static char *pcsc_stringify_error(LONG rv) { + static __thread char out[20]; + sprintf_s(out, sizeof(out), "0x%08lX", rv); + + return out; + } + #endif + void set_apdu_verbose(bool verbose) { apdu_verbose = verbose; } diff --git a/src/device/device_ledger.hpp b/src/device/device_ledger.hpp index f1fcaab871d..b62bdf959e1 100644 --- a/src/device/device_ledger.hpp +++ b/src/device/device_ledger.hpp @@ -33,8 +33,13 @@ #include #include #include "device.hpp" +#ifdef WIN32 +#include +#define MAX_ATR_SIZE 33 +#else #include #include +#endif #include #include From de1ffe07b12073c3845b660c98052a9c1c31c594 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 25 May 2018 12:34:52 +0100 Subject: [PATCH 0032/1404] p2p: warn when no incoming connections are seen for a while --- src/p2p/net_node.h | 2 ++ src/p2p/net_node.inl | 14 ++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h index 4606f66ee98..077296acd18 100644 --- a/src/p2p/net_node.h +++ b/src/p2p/net_node.h @@ -244,6 +244,7 @@ namespace nodetool bool check_connection_and_handshake_with_peer(const epee::net_utils::network_address& na, uint64_t last_seen_stamp); bool gray_peerlist_housekeeping(); + bool check_incoming_connections(); void kill() { ///< will be called e.g. from deinit() _info("Killing the net_node"); @@ -307,6 +308,7 @@ namespace nodetool epee::math_helper::once_a_time_seconds<1> m_connections_maker_interval; epee::math_helper::once_a_time_seconds<60*30, false> m_peerlist_store_interval; epee::math_helper::once_a_time_seconds<60> m_gray_peerlist_housekeeping_interval; + epee::math_helper::once_a_time_seconds<900, false> m_incoming_connections_interval; std::string m_bind_ip; std::string m_port; diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index 9b21705ec15..5f8699ac4bc 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -1293,6 +1293,20 @@ namespace nodetool m_connections_maker_interval.do_call(boost::bind(&node_server::connections_maker, this)); m_gray_peerlist_housekeeping_interval.do_call(boost::bind(&node_server::gray_peerlist_housekeeping, this)); m_peerlist_store_interval.do_call(boost::bind(&node_server::store_config, this)); + m_incoming_connections_interval.do_call(boost::bind(&node_server::check_incoming_connections, this)); + return true; + } + //----------------------------------------------------------------------------------- + template + bool node_server::check_incoming_connections() + { + if (m_offline || m_hide_my_port) + return true; + if (get_incoming_connections_count() == 0) + { + const el::Level level = el::Level::Warning; + MCLOG_RED(level, "global", "No incoming connections - check firewalls/routers allow port " << get_this_peer_port()); + } return true; } //----------------------------------------------------------------------------------- From a87980f6c2085a47e732d580947344649668adbe Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 24 May 2018 09:12:16 +0100 Subject: [PATCH 0033/1404] fix build with GCC 8.1.0 --- src/cryptonote_basic/account.cpp | 2 +- src/daemon/rpc_command_executor.cpp | 2 +- src/rpc/core_rpc_server_commands_defs.h | 2 ++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/cryptonote_basic/account.cpp b/src/cryptonote_basic/account.cpp index bab991d19bf..aac6ec22b6e 100644 --- a/src/cryptonote_basic/account.cpp +++ b/src/cryptonote_basic/account.cpp @@ -157,7 +157,7 @@ DISABLE_VS_WARNINGS(4244 4345) void account_base::create_from_viewkey(const cryptonote::account_public_address& address, const crypto::secret_key& viewkey) { crypto::secret_key fake; - memset(&fake, 0, sizeof(fake)); + memset(&unwrap(fake), 0, sizeof(fake)); create_from_keys(address, fake, viewkey); } //----------------------------------------------------------------- diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp index 2efb501ea4e..c6dd9f538b0 100644 --- a/src/daemon/rpc_command_executor.cpp +++ b/src/daemon/rpc_command_executor.cpp @@ -973,7 +973,7 @@ bool t_rpc_command_executor::print_transaction_pool_stats() { } else { - memset(&res.pool_stats, 0, sizeof(res.pool_stats)); + res.pool_stats = {}; if (!m_rpc_server->on_get_transaction_pool_stats(req, res, false) || res.status != CORE_RPC_STATUS_OK) { tools::fail_msg_writer() << make_error(fail_message, res.status); diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index 250c88e90bf..1227afef5e6 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -1554,6 +1554,8 @@ namespace cryptonote std::vector histo; uint32_t num_double_spends; + txpool_stats(): bytes_total(0), bytes_min(0), bytes_max(0), bytes_med(0), fee_total(0), oldest(0), txs_total(0), num_failing(0), num_10m(0), num_not_relayed(0), histo_98pc(0), num_double_spends(0) {} + BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(bytes_total) KV_SERIALIZE(bytes_min) From 5a412b7a3f5b7c53d9777d58fe1e6bd11a205596 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 28 May 2018 14:55:09 +0100 Subject: [PATCH 0034/1404] disable file size sanity check when loading the wallet cache --- contrib/epee/include/file_io_utils.h | 6 +++--- src/wallet/wallet2.cpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/contrib/epee/include/file_io_utils.h b/contrib/epee/include/file_io_utils.h index 1966106743f..0afff800f7b 100644 --- a/contrib/epee/include/file_io_utils.h +++ b/contrib/epee/include/file_io_utils.h @@ -128,7 +128,7 @@ namespace file_io_utils inline - bool load_file_to_string(const std::string& path_to_file, std::string& target_str) + bool load_file_to_string(const std::string& path_to_file, std::string& target_str, size_t max_size = 1000000000) { #ifdef WIN32 WCHAR wide_path[1000]; @@ -139,7 +139,7 @@ namespace file_io_utils if (file_handle == INVALID_HANDLE_VALUE) return false; DWORD file_size = GetFileSize(file_handle, NULL); - if ((file_size == INVALID_FILE_SIZE) || (file_size > 1000000000)) { + if ((file_size == INVALID_FILE_SIZE) || (uint64_t)file_size > (uint64_t)max_size) { CloseHandle(file_handle); return false; } @@ -159,7 +159,7 @@ namespace file_io_utils std::ifstream::pos_type file_size = fstream.tellg(); - if(file_size > 1000000000) + if((uint64_t)file_size > (uint64_t)max_size) // ensure a large domain for comparison, and negative -> too large return false;//don't go crazy size_t file_size_t = static_cast(file_size); diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index cd3ca324526..18136ec27dd 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -3772,7 +3772,7 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass { wallet2::cache_file_data cache_file_data; std::string buf; - bool r = epee::file_io_utils::load_file_to_string(m_wallet_file, buf); + bool r = epee::file_io_utils::load_file_to_string(m_wallet_file, buf, std::numeric_limits::max()); THROW_WALLET_EXCEPTION_IF(!r, error::file_read_error, m_wallet_file); // try to read it as an encrypted cache From 91663fcc006c773ce65408a0954388501decccc6 Mon Sep 17 00:00:00 2001 From: k1nghat <16073360+k1nghat@users.noreply.github.com> Date: Tue, 29 May 2018 12:20:35 -0500 Subject: [PATCH 0035/1404] adding win building details. cloning and extra build details included. --- README.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/README.md b/README.md index 4c3e9799a5f..cd1349f1fd8 100644 --- a/README.md +++ b/README.md @@ -353,8 +353,22 @@ application. or `MinGW-w64-Win64 Shell` shortcut on 32-bit Windows. Note that if you are running 64-bit Windows, you will have both 64-bit and 32-bit MinGW shells. +**Cloning** + +* To git clone, run: + + git clone --recursive https://github.com/monero-project/monero.git + **Building** +* Change to the cloned directory, run: + + cd monero + +* If you would like a specific [version/tag](https://github.com/monero-project/monero/tags), do a git checkout for that version. eg. 'v0.12.1.0'. If you dont care about the version and just want binaries from master, skip this step: + + git checkout v0.12.1.0 + * If you are on a 64-bit system, run: make release-static-win64 From a79fc219b75173d9257ecebb604d1ec24c9f1c2d Mon Sep 17 00:00:00 2001 From: stoffu Date: Tue, 20 Mar 2018 09:52:16 +0900 Subject: [PATCH 0036/1404] hardfork: fix get_earliest_ideal_height_for_version() to support non-existent versions --- src/cryptonote_basic/hardfork.cpp | 12 ++++++++---- tests/unit_tests/hardfork.cpp | 25 +++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/src/cryptonote_basic/hardfork.cpp b/src/cryptonote_basic/hardfork.cpp index 95f1ecab9d9..aa67463043e 100644 --- a/src/cryptonote_basic/hardfork.cpp +++ b/src/cryptonote_basic/hardfork.cpp @@ -379,11 +379,15 @@ uint8_t HardFork::get_ideal_version(uint64_t height) const uint64_t HardFork::get_earliest_ideal_height_for_version(uint8_t version) const { - for (unsigned int n = heights.size() - 1; n > 0; --n) { - if (heights[n].version <= version) - return heights[n].height; + uint64_t height = std::numeric_limits::max(); + for (auto i = heights.rbegin(); i != heights.rend(); ++i) { + if (i->version >= version) { + height = i->height; + } else { + break; + } } - return 0; + return height; } uint8_t HardFork::get_next_version() const diff --git a/tests/unit_tests/hardfork.cpp b/tests/unit_tests/hardfork.cpp index 7c27b9c5d35..372cee5136e 100644 --- a/tests/unit_tests/hardfork.cpp +++ b/tests/unit_tests/hardfork.cpp @@ -554,3 +554,28 @@ TEST(get, higher) ASSERT_EQ(hf.get_ideal_version(7), 3); } +TEST(get, earliest_ideal_height) +{ + TestDB db; + HardFork hf(db, 1, 0, 1, 1, 4, 50); + + // v h t + ASSERT_TRUE(hf.add_fork(1, 0, 0)); + ASSERT_TRUE(hf.add_fork(2, 2, 1)); + ASSERT_TRUE(hf.add_fork(5, 5, 2)); + ASSERT_TRUE(hf.add_fork(6, 10, 3)); + ASSERT_TRUE(hf.add_fork(9, 15, 4)); + hf.init(); + + ASSERT_EQ(hf.get_earliest_ideal_height_for_version(1), 0); + ASSERT_EQ(hf.get_earliest_ideal_height_for_version(2), 2); + ASSERT_EQ(hf.get_earliest_ideal_height_for_version(3), 5); + ASSERT_EQ(hf.get_earliest_ideal_height_for_version(4), 5); + ASSERT_EQ(hf.get_earliest_ideal_height_for_version(5), 5); + ASSERT_EQ(hf.get_earliest_ideal_height_for_version(6), 10); + ASSERT_EQ(hf.get_earliest_ideal_height_for_version(7), 15); + ASSERT_EQ(hf.get_earliest_ideal_height_for_version(8), 15); + ASSERT_EQ(hf.get_earliest_ideal_height_for_version(9), 15); + ASSERT_EQ(hf.get_earliest_ideal_height_for_version(10), std::numeric_limits::max()); +} + From 7e30eadb3298ed176b2ac209dae8824f947cb415 Mon Sep 17 00:00:00 2001 From: stoffu Date: Tue, 20 Mar 2018 11:02:41 +0900 Subject: [PATCH 0037/1404] unit_tests/hardfork: add tests for check_for_height() --- tests/unit_tests/hardfork.cpp | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/unit_tests/hardfork.cpp b/tests/unit_tests/hardfork.cpp index 372cee5136e..ba3e95e7d88 100644 --- a/tests/unit_tests/hardfork.cpp +++ b/tests/unit_tests/hardfork.cpp @@ -232,6 +232,30 @@ TEST(ordering, Success) ASSERT_FALSE(hf.add_fork(5, 5, 4)); } +TEST(check_for_height, Success) +{ + TestDB db; + HardFork hf(db, 1, 0, 0, 0, 1, 0); // no voting + + ASSERT_TRUE(hf.add_fork(1, 0, 0)); + ASSERT_TRUE(hf.add_fork(2, 5, 1)); + hf.init(); + + for (uint64_t h = 0; h <= 4; ++h) { + ASSERT_TRUE(hf.check_for_height(mkblock(1, 1), h)); + ASSERT_FALSE(hf.check_for_height(mkblock(2, 2), h)); // block version is too high + db.add_block(mkblock(hf, h, 1), 0, 0, 0, crypto::hash()); + ASSERT_TRUE(hf.add(db.get_block_from_height(h), h)); + } + + for (uint64_t h = 5; h <= 10; ++h) { + ASSERT_FALSE(hf.check_for_height(mkblock(1, 1), h)); // block version is too low + ASSERT_TRUE(hf.check_for_height(mkblock(2, 2), h)); + db.add_block(mkblock(hf, h, 2), 0, 0, 0, crypto::hash()); + ASSERT_TRUE(hf.add(db.get_block_from_height(h), h)); + } +} + TEST(states, Success) { TestDB db; From 98cf62cc45d6edd8d35f326ba0736728e624d5fd Mon Sep 17 00:00:00 2001 From: stoffu Date: Tue, 20 Mar 2018 11:09:58 +0900 Subject: [PATCH 0038/1404] hardfork: fix get_next_version() --- src/cryptonote_basic/hardfork.cpp | 6 +++--- tests/unit_tests/hardfork.cpp | 29 +++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/src/cryptonote_basic/hardfork.cpp b/src/cryptonote_basic/hardfork.cpp index aa67463043e..f05b25901e8 100644 --- a/src/cryptonote_basic/hardfork.cpp +++ b/src/cryptonote_basic/hardfork.cpp @@ -394,9 +394,9 @@ uint8_t HardFork::get_next_version() const { CRITICAL_REGION_LOCAL(lock); uint64_t height = db.height(); - for (unsigned int n = heights.size() - 1; n > 0; --n) { - if (height >= heights[n].height) { - return heights[n < heights.size() - 1 ? n + 1 : n].version; + for (auto i = heights.rbegin(); i != heights.rend(); ++i) { + if (height >= i->height) { + return (i == heights.rbegin() ? i : (i - 1))->version; } } return original_version; diff --git a/tests/unit_tests/hardfork.cpp b/tests/unit_tests/hardfork.cpp index ba3e95e7d88..d33daca4c2a 100644 --- a/tests/unit_tests/hardfork.cpp +++ b/tests/unit_tests/hardfork.cpp @@ -256,6 +256,35 @@ TEST(check_for_height, Success) } } +TEST(get, next_version) +{ + TestDB db; + HardFork hf(db); + + ASSERT_TRUE(hf.add_fork(1, 0, 0)); + ASSERT_TRUE(hf.add_fork(2, 5, 1)); + ASSERT_TRUE(hf.add_fork(4, 10, 2)); + hf.init(); + + for (uint64_t h = 0; h <= 4; ++h) { + ASSERT_EQ(2, hf.get_next_version()); + db.add_block(mkblock(hf, h, 1), 0, 0, 0, crypto::hash()); + ASSERT_TRUE(hf.add(db.get_block_from_height(h), h)); + } + + for (uint64_t h = 5; h <= 9; ++h) { + ASSERT_EQ(4, hf.get_next_version()); + db.add_block(mkblock(hf, h, 2), 0, 0, 0, crypto::hash()); + ASSERT_TRUE(hf.add(db.get_block_from_height(h), h)); + } + + for (uint64_t h = 10; h <= 15; ++h) { + ASSERT_EQ(4, hf.get_next_version()); + db.add_block(mkblock(hf, h, 4), 0, 0, 0, crypto::hash()); + ASSERT_TRUE(hf.add(db.get_block_from_height(h), h)); + } +} + TEST(states, Success) { TestDB db; From 0321d1ac794b52233225a6a03001528ce6eea21c Mon Sep 17 00:00:00 2001 From: stoffu Date: Tue, 20 Mar 2018 12:01:09 +0900 Subject: [PATCH 0039/1404] unit_tests/hardfork: add tests for get_voting_info() --- tests/unit_tests/hardfork.cpp | 49 +++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/tests/unit_tests/hardfork.cpp b/tests/unit_tests/hardfork.cpp index d33daca4c2a..913ebe84ab8 100644 --- a/tests/unit_tests/hardfork.cpp +++ b/tests/unit_tests/hardfork.cpp @@ -492,6 +492,55 @@ TEST(voting, different_thresholds) } } +TEST(voting, info) +{ + TestDB db; + HardFork hf(db, 1, 0, 1, 1, 4, 50); // window size 4, default threshold 50% + + // v h ts + ASSERT_TRUE(hf.add_fork(1, 0, 0)); + // v h thr ts + ASSERT_TRUE(hf.add_fork(2, 5, 0, 1)); // asap + ASSERT_TRUE(hf.add_fork(3, 10, 100, 2)); // all votes + // v h ts + ASSERT_TRUE(hf.add_fork(4, 15, 3)); // default 50% votes + hf.init(); + + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 + static const uint8_t block_versions[] = { 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4 }; + static const uint8_t expected_versions[] = { 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4 }; + static const uint8_t expected_thresholds[] = { 0, 1, 1, 2, 2, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 2, 2, 2, 2 }; + + for (uint64_t h = 0; h < sizeof(block_versions) / sizeof(block_versions[0]); ++h) { + uint32_t window, votes, threshold; + uint64_t earliest_height; + uint8_t voting; + + ASSERT_TRUE(hf.get_voting_info(1, window, votes, threshold, earliest_height, voting)); + ASSERT_EQ(std::min(h, 4), votes); + ASSERT_EQ(0, earliest_height); + + ASSERT_EQ(hf.get_current_version() >= 2, hf.get_voting_info(2, window, votes, threshold, earliest_height, voting)); + ASSERT_EQ(std::min(h <= 3 ? 0 : h - 3, 4), votes); + ASSERT_EQ(5, earliest_height); + + ASSERT_EQ(hf.get_current_version() >= 3, hf.get_voting_info(3, window, votes, threshold, earliest_height, voting)); + ASSERT_EQ(std::min(h <= 8 ? 0 : h - 8, 4), votes); + ASSERT_EQ(10, earliest_height); + + ASSERT_EQ(hf.get_current_version() == 4, hf.get_voting_info(4, window, votes, threshold, earliest_height, voting)); + ASSERT_EQ(std::min(h <= 14 ? 0 : h - 14, 4), votes); + ASSERT_EQ(15, earliest_height); + + ASSERT_EQ(std::min(h, 4), window); + ASSERT_EQ(expected_thresholds[h], threshold); + ASSERT_EQ(4, voting); + + db.add_block(mkblock(hf, h, block_versions[h]), 0, 0, 0, crypto::hash()); + ASSERT_TRUE(hf.add(db.get_block_from_height(h), h)); + } +} + TEST(new_blocks, denied) { TestDB db; From b26cd263336b44de7df24c2dc7f12c696ac959e8 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 18 Mar 2018 10:21:45 +0000 Subject: [PATCH 0040/1404] blockchain_utilities: do not link against unneeded p2p lib --- src/blockchain_utilities/CMakeLists.txt | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/blockchain_utilities/CMakeLists.txt b/src/blockchain_utilities/CMakeLists.txt index a5dd6955674..338ec3e4b12 100644 --- a/src/blockchain_utilities/CMakeLists.txt +++ b/src/blockchain_utilities/CMakeLists.txt @@ -100,7 +100,6 @@ target_link_libraries(blockchain_import PRIVATE cryptonote_core blockchain_db - p2p version epee ${Boost_FILESYSTEM_LIBRARY} @@ -127,7 +126,6 @@ target_link_libraries(blockchain_export PRIVATE cryptonote_core blockchain_db - p2p version epee ${Boost_FILESYSTEM_LIBRARY} @@ -150,7 +148,6 @@ target_link_libraries(blockchain_blackball wallet cryptonote_core blockchain_db - p2p version epee ${Boost_FILESYSTEM_LIBRARY} @@ -173,7 +170,6 @@ target_link_libraries(blockchain_usage PRIVATE cryptonote_core blockchain_db - p2p version epee ${Boost_FILESYSTEM_LIBRARY} From 66f4700f571312a9307694b89fa7c118cbffa619 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 18 Mar 2018 10:55:21 +0000 Subject: [PATCH 0041/1404] blockchain_blackball: add incremental mode --- .../blockchain_blackball.cpp | 121 +++++++++++++++--- 1 file changed, 102 insertions(+), 19 deletions(-) diff --git a/src/blockchain_utilities/blockchain_blackball.cpp b/src/blockchain_utilities/blockchain_blackball.cpp index 1243822bb2a..1700890ef7a 100644 --- a/src/blockchain_utilities/blockchain_blackball.cpp +++ b/src/blockchain_utilities/blockchain_blackball.cpp @@ -28,8 +28,13 @@ #include #include +#include +#include +#include "common/unordered_containers_boost_serialization.h" #include "common/command_line.h" #include "common/varint.h" +#include "serialization/crypto.h" +#include "cryptonote_basic/cryptonote_boost_serialization.h" #include "cryptonote_core/tx_pool.h" #include "cryptonote_core/cryptonote_core.h" #include "cryptonote_core/blockchain.h" @@ -49,9 +54,17 @@ struct output_data { uint64_t amount; uint64_t index; + output_data(): amount(0), index(0) {} output_data(uint64_t a, uint64_t i): amount(a), index(i) {} bool operator==(const output_data &other) const { return other.amount == amount && other.index == index; } + template void serialize(t_archive &a, const unsigned int ver) + { + a & amount; + a & index; + } }; +BOOST_CLASS_VERSION(output_data, 0) + namespace std { template<> struct hash @@ -66,6 +79,23 @@ namespace std }; } +struct blackball_state_t +{ + std::unordered_map> relative_rings; + std::unordered_map> outputs; + std::unordered_map processed_heights; + std::unordered_set spent; + + template void serialize(t_archive &a, const unsigned int ver) + { + a & relative_rings; + a & outputs; + a & processed_heights; + a & spent; + } +}; +BOOST_CLASS_VERSION(blackball_state_t, 0) + static std::string get_default_db_path() { boost::filesystem::path dir = tools::get_default_data_dir(); @@ -75,7 +105,7 @@ static std::string get_default_db_path() return dir.string(); } -static bool for_all_transactions(const std::string &filename, const std::function &f) +static bool for_all_transactions(const std::string &filename, uint64_t &start_idx, const std::function &f) { MDB_env *env; MDB_dbi dbi; @@ -109,7 +139,9 @@ static bool for_all_transactions(const std::string &filename, const std::functio MDB_val v; bool fret = true; - MDB_cursor_op op = MDB_FIRST; + k.mv_size = sizeof(uint64_t); + k.mv_data = &start_idx; + MDB_cursor_op op = MDB_SET; while (1) { int ret = mdb_cursor_get(cur, &k, &v, op); @@ -119,6 +151,12 @@ static bool for_all_transactions(const std::string &filename, const std::functio if (ret) throw std::runtime_error("Failed to enumerate transactions: " + std::string(mdb_strerror(ret))); + if (k.mv_size != sizeof(uint64_t)) + throw std::runtime_error("Bad key size"); + const uint64_t idx = *(uint64_t*)k.mv_data; + if (idx < start_idx) + continue; + cryptonote::transaction_prefix tx; blobdata bd; bd.assign(reinterpret_cast(v.mv_data), v.mv_size); @@ -128,6 +166,7 @@ static bool for_all_transactions(const std::string &filename, const std::functio bool r = do_serialize(ba, tx); CHECK_AND_ASSERT_MES(r, false, "Failed to parse transaction from blob"); + start_idx = *(uint64_t*)k.mv_data; if (!f(tx)) { fret = false; break; @@ -308,17 +347,41 @@ int main(int argc, char* argv[]) LOG_PRINT_L0("Scanning for blackballable outputs..."); size_t done = 0; - std::unordered_map> relative_rings; - std::unordered_map> outputs; - std::unordered_set spent, newly_spent; + blackball_state_t state; + std::unordered_set newly_spent; + const std::string state_file_path = (boost::filesystem::path(output_file_path) / "blackball-state.bin").string(); + + LOG_PRINT_L0("Loading state data from " << state_file_path); + std::ifstream state_data_in; + state_data_in.open(state_file_path, std::ios_base::binary | std::ios_base::in); + if (!state_data_in.fail()) + { + try + { + boost::archive::portable_binary_iarchive a(state_data_in); + a >> state; + } + catch (const std::exception &e) + { + MERROR("Failed to load state data from " << state_file_path << ", restarting from scratch"); + state = blackball_state_t(); + } + state_data_in.close(); + } + uint64_t start_blackballed_outputs = state.spent.size(); cryptonote::block b = core_storage[0]->get_db().get_block_from_height(0); tools::ringdb ringdb(output_file_path.string(), epee::string_tools::pod_to_hex(get_block_hash(b))); for (size_t n = 0; n < inputs.size(); ++n) { - LOG_PRINT_L0("Reading blockchain from " << inputs[n]); - for_all_transactions(inputs[n], [&](const cryptonote::transaction_prefix &tx)->bool + const std::string canonical = boost::filesystem::canonical(inputs[n]).string(); + uint64_t start_idx = 0; + auto it = state.processed_heights.find(canonical); + if (it != state.processed_heights.end()) + start_idx = it->second; + LOG_PRINT_L0("Reading blockchain from " << inputs[n] << " from " << start_idx); + for_all_transactions(inputs[n], start_idx, [&](const cryptonote::transaction_prefix &tx)->bool { for (const auto &in: tx.vin) { @@ -331,7 +394,7 @@ int main(int argc, char* argv[]) const std::vector absolute = cryptonote::relative_output_offsets_to_absolute(txin.key_offsets); if (n == 0) for (uint64_t out: absolute) - outputs[output_data(txin.amount, out)].insert(txin.k_image); + state.outputs[output_data(txin.amount, out)].insert(txin.k_image); std::vector new_ring = txin.key_offsets; const uint32_t ring_size = txin.key_offsets.size(); @@ -341,17 +404,17 @@ int main(int argc, char* argv[]) MINFO("Blackballing output " << pkey << ", due to being used in a 1-ring"); ringdb.blackball(pkey); newly_spent.insert(output_data(txin.amount, txin.key_offsets[0])); - spent.insert(output_data(txin.amount, txin.key_offsets[0])); + state.spent.insert(output_data(txin.amount, txin.key_offsets[0])); } - else if (relative_rings.find(txin.k_image) != relative_rings.end()) + else if (state.relative_rings.find(txin.k_image) != state.relative_rings.end()) { MINFO("Key image " << txin.k_image << " already seen: rings " << - boost::join(relative_rings[txin.k_image] | boost::adaptors::transformed([](uint64_t out){return std::to_string(out);}), " ") << + boost::join(state.relative_rings[txin.k_image] | boost::adaptors::transformed([](uint64_t out){return std::to_string(out);}), " ") << ", " << boost::join(txin.key_offsets | boost::adaptors::transformed([](uint64_t out){return std::to_string(out);}), " ")); - if (relative_rings[txin.k_image] != txin.key_offsets) + if (state.relative_rings[txin.k_image] != txin.key_offsets) { MINFO("Rings are different"); - const std::vector r0 = cryptonote::relative_output_offsets_to_absolute(relative_rings[txin.k_image]); + const std::vector r0 = cryptonote::relative_output_offsets_to_absolute(state.relative_rings[txin.k_image]); const std::vector r1 = cryptonote::relative_output_offsets_to_absolute(txin.key_offsets); std::vector common; for (uint64_t out: r0) @@ -369,7 +432,7 @@ int main(int argc, char* argv[]) MINFO("Blackballing output " << pkey << ", due to being used in rings with a single common element"); ringdb.blackball(pkey); newly_spent.insert(output_data(txin.amount, common[0])); - spent.insert(output_data(txin.amount, common[0])); + state.spent.insert(output_data(txin.amount, common[0])); } else { @@ -381,10 +444,11 @@ int main(int argc, char* argv[]) } } } - relative_rings[txin.k_image] = new_ring; + state.relative_rings[txin.k_image] = new_ring; } return true; }); + state.processed_heights[canonical] = start_idx; } while (!newly_spent.empty()) @@ -395,15 +459,15 @@ int main(int argc, char* argv[]) for (const output_data &od: work_spent) { - for (const crypto::key_image &ki: outputs[od]) + for (const crypto::key_image &ki: state.outputs[od]) { - std::vector absolute = cryptonote::relative_output_offsets_to_absolute(relative_rings[ki]); + std::vector absolute = cryptonote::relative_output_offsets_to_absolute(state.relative_rings[ki]); size_t known = 0; uint64_t last_unknown = 0; for (uint64_t out: absolute) { output_data new_od(od.amount, out); - if (spent.find(new_od) != spent.end()) + if (state.spent.find(new_od) != state.spent.end()) ++known; else last_unknown = out; @@ -415,12 +479,31 @@ int main(int argc, char* argv[]) absolute.size() << "-ring where all other outputs are known to be spent"); ringdb.blackball(pkey); newly_spent.insert(output_data(od.amount, last_unknown)); - spent.insert(output_data(od.amount, last_unknown)); + state.spent.insert(output_data(od.amount, last_unknown)); } } } } + LOG_PRINT_L0("Saving state data to " << state_file_path); + std::ofstream state_data_out; + state_data_out.open(state_file_path, std::ios_base::binary | std::ios_base::out | std::ios::trunc); + if (!state_data_out.fail()) + { + try + { + boost::archive::portable_binary_oarchive a(state_data_out); + a << state; + } + catch (const std::exception &e) + { + MERROR("Failed to save state data to " << state_file_path); + } + state_data_out.close(); + } + + uint64_t diff = state.spent.size() - start_blackballed_outputs; + LOG_PRINT_L0(std::to_string(diff) << " new outputs blackballed, " << state.spent.size() << " total outputs blackballed"); LOG_PRINT_L0("Blockchain blackball data exported OK"); return 0; From e09710f76edc463ede2d3711338dc0912e76c78b Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 19 Apr 2018 09:36:31 +0100 Subject: [PATCH 0042/1404] blockchain_blackball: also blackball N N-sized duplicate rings These are unlikely to happen at random, but Wijaya et al made a paper about it, so people might try it on purpose now (and it turns out it's easy to add anyway) --- .../blockchain_blackball.cpp | 53 +++++++++++++++++-- 1 file changed, 48 insertions(+), 5 deletions(-) diff --git a/src/blockchain_utilities/blockchain_blackball.cpp b/src/blockchain_utilities/blockchain_blackball.cpp index 1700890ef7a..95eb2f73d28 100644 --- a/src/blockchain_utilities/blockchain_blackball.cpp +++ b/src/blockchain_utilities/blockchain_blackball.cpp @@ -77,6 +77,15 @@ namespace std return reinterpret_cast(h); } }; + template<> struct hash> + { + size_t operator()(const std::vector &v) const + { + crypto::hash h; + crypto::cn_fast_hash(v.data(), v.size() * sizeof(uint64_t), h); + return reinterpret_cast(h); + } + }; } struct blackball_state_t @@ -85,6 +94,7 @@ struct blackball_state_t std::unordered_map> outputs; std::unordered_map processed_heights; std::unordered_set spent; + std::unordered_map, size_t> ring_instances; template void serialize(t_archive &a, const unsigned int ver) { @@ -92,9 +102,12 @@ struct blackball_state_t a & outputs; a & processed_heights; a & spent; + if (ver < 1) + return; + a & ring_instances; } }; -BOOST_CLASS_VERSION(blackball_state_t, 0) +BOOST_CLASS_VERSION(blackball_state_t, 1) static std::string get_default_db_path() { @@ -181,6 +194,24 @@ static bool for_all_transactions(const std::string &filename, uint64_t &start_id return fret; } +static std::vector canonicalize(const std::vector &v) +{ + std::vector c; + c.reserve(v.size()); + c.push_back(v[0]); + for (size_t n = 1; n < v.size(); ++n) + { + if (v[n] != 0) + c.push_back(v[n]); + } + if (c.size() < v.size()) + { + MINFO("Ring has duplicate member(s): " << + boost::join(v | boost::adaptors::transformed([](uint64_t out){return std::to_string(out);}), " ")); + } + return c; +} + int main(int argc, char* argv[]) { TRY_ENTRY(); @@ -396,15 +427,27 @@ int main(int argc, char* argv[]) for (uint64_t out: absolute) state.outputs[output_data(txin.amount, out)].insert(txin.k_image); - std::vector new_ring = txin.key_offsets; + std::vector new_ring = canonicalize(txin.key_offsets); const uint32_t ring_size = txin.key_offsets.size(); + state.ring_instances[new_ring] += 1; if (ring_size == 1) { - const crypto::public_key pkey = core_storage[n]->get_output_key(txin.amount, txin.key_offsets[0]); + const crypto::public_key pkey = core_storage[n]->get_output_key(txin.amount, absolute[0]); MINFO("Blackballing output " << pkey << ", due to being used in a 1-ring"); ringdb.blackball(pkey); - newly_spent.insert(output_data(txin.amount, txin.key_offsets[0])); - state.spent.insert(output_data(txin.amount, txin.key_offsets[0])); + newly_spent.insert(output_data(txin.amount, absolute[0])); + state.spent.insert(output_data(txin.amount, absolute[0])); + } + else if (state.ring_instances[new_ring] == new_ring.size()) + { + for (size_t o = 0; o < new_ring.size(); ++o) + { + const crypto::public_key pkey = core_storage[n]->get_output_key(txin.amount, absolute[o]); + MINFO("Blackballing output " << pkey << ", due to being used in " << new_ring.size() << " identical " << new_ring.size() << "-rings"); + ringdb.blackball(pkey); + newly_spent.insert(output_data(txin.amount, absolute[o])); + state.spent.insert(output_data(txin.amount, absolute[o])); + } } else if (state.relative_rings.find(txin.k_image) != state.relative_rings.end()) { From 14f0d38cd6b5ea7329180c73da5f497bee6eb5f0 Mon Sep 17 00:00:00 2001 From: philkode Date: Thu, 31 May 2018 10:40:00 +0100 Subject: [PATCH 0043/1404] Incorporate Window debug build targets --- CMakeLists.txt | 1 + Makefile | 8 ++++++++ README.md | 10 ++++++++++ 3 files changed, 19 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index b1297e716e8..e6ced6ec467 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -791,6 +791,7 @@ endif() include_directories(SYSTEM ${Boost_INCLUDE_DIRS}) if(MINGW) + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wa,-mbig-obj") set(EXTRA_LIBRARIES mswsock;ws2_32;iphlpapi) set(ICU_LIBRARIES ${Boost_LOCALE_LIBRARY} icuio icuin icuuc icudt icutu iconv) elseif(APPLE OR OPENBSD OR ANDROID) diff --git a/Makefile b/Makefile index d0be9bcd679..41e8805d4cc 100644 --- a/Makefile +++ b/Makefile @@ -49,6 +49,14 @@ debug-static-all: mkdir -p build/debug cd build/debug && cmake -D BUILD_TESTS=ON -D STATIC=ON -D CMAKE_BUILD_TYPE=Debug ../.. && $(MAKE) +debug-static-win64: + mkdir -p build/debug + cd build/debug && cmake -G "MSYS Makefiles" -D STATIC=ON -D ARCH="x86-64" -D BUILD_64=ON -D CMAKE_BUILD_TYPE=Debug -D BUILD_TAG="win-x64" -D CMAKE_TOOLCHAIN_FILE=../../cmake/64-bit-toolchain.cmake -D MSYS2_FOLDER=c:/msys64 ../.. && $(MAKE) + +debug-static-win32: + mkdir -p build/debug + cd build/debug && cmake -G "MSYS Makefiles" -D STATIC=ON -D ARCH="i686" -D BUILD_64=OFF -D CMAKE_BUILD_TYPE=Debug -D BUILD_TAG="win-x32" -D CMAKE_TOOLCHAIN_FILE=../../cmake/32-bit-toolchain.cmake -D MSYS2_FOLDER=c:/msys32 ../.. && $(MAKE) + cmake-release: mkdir -p build/release cd build/release && cmake -D CMAKE_BUILD_TYPE=Release ../.. diff --git a/README.md b/README.md index 4c3e9799a5f..9e28192b997 100644 --- a/README.md +++ b/README.md @@ -365,6 +365,16 @@ application. * The resulting executables can be found in `build/release/bin` +* **Optional**: to build Windows binaries suitable for debugging on a 64-bit system, run: + + make debug-static-win64 + +* **Optional**: to build Windows binaries suitable for debugging on a 32-bit system, run: + + make debug-static-win32 + +* The resulting executables can be found in `build/debug/bin` + ### On FreeBSD: The project can be built from scratch by following instructions for Linux above. If you are running monero in a jail you need to add the flag: `allow.sysvipc=1` to your jail configuration, otherwise lmdb will throw the error message: `Failed to open lmdb environment: Function not implemented`. From 1aae39dff27f6e7e48ae0a2d8ef2fd612e127824 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 31 May 2018 12:13:03 +0100 Subject: [PATCH 0044/1404] daemon: fix readline interfering with std::cerr usage Once readline is initialized, std::cerr's operator<< will output a 0xff byte for unknown reasons. --- src/daemon/main.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/daemon/main.cpp b/src/daemon/main.cpp index 49494e8899c..fd70c367c77 100644 --- a/src/daemon/main.cpp +++ b/src/daemon/main.cpp @@ -262,6 +262,9 @@ int main(int argc, char const * argv[]) } else { +#ifdef HAVE_READLINE + rdln::suspend_readline pause_readline; +#endif std::cerr << "Unknown command: " << command.front() << std::endl; return 1; } From f17383afc2cd3854cc2071bcc801dd912efc1212 Mon Sep 17 00:00:00 2001 From: cslashm Date: Thu, 31 May 2018 15:30:16 +0200 Subject: [PATCH 0045/1404] Fix CMAKE config for PCSC in order to avoid msys DLL dependencies merge of PR #3886 --- cmake/FindPCSC.cmake | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/cmake/FindPCSC.cmake b/cmake/FindPCSC.cmake index 8332abc4927..b5e8420e664 100644 --- a/cmake/FindPCSC.cmake +++ b/cmake/FindPCSC.cmake @@ -14,28 +14,31 @@ ENDIF (PCSC_INCLUDE_DIR AND PCSC_LIBRARIES) IF (NOT WIN32) FIND_PACKAGE(PkgConfig) PKG_CHECK_MODULES(PC_PCSC libpcsclite) -ENDIF (NOT WIN32) -FIND_PATH(PCSC_INCLUDE_DIR winscard.h - HINTS - IF (WIN32) - ${MSYS2_FOLDER}/mingw64/x86_64-w64-mingw32/include - ENDIF (WIN32) - /usr/include/PCSC - ${PC_PCSC_INCLUDEDIR} - ${PC_PCSC_INCLUDE_DIRS} - PATH_SUFFIXES PCSC + FIND_PATH(PCSC_INCLUDE_DIR winscard.h + HINTS + /usr/include/PCSC + ${PC_PCSC_INCLUDEDIR} + ${PC_PCSC_INCLUDE_DIRS} + PATH_SUFFIXES PCSC ) -FIND_LIBRARY(PCSC_LIBRARY NAMES pcsclite libpcsclite WinSCard PCSC - HINTS - IF (WIN32) - ${MSYS2_FOLDER}/mingw64/x86_64-w64-mingw32/lib - ENDIF (WIN32) - ${PC_PCSC_LIBDIR} - ${PC_PCSC_LIBRARY_DIRS} + FIND_LIBRARY(PCSC_LIBRARY NAMES pcsclite libpcsclite PCSC + HINTS + ${PC_PCSC_LIBDIR} + ${PC_PCSC_LIBRARY_DIRS} ) +ELSE (NOT WIN32) + IF(BUILD_64 STREQUAL "ON") + set(PCSC_INCLUDE_DIR /mingw64/x86_64-w64-mingw32/include) + set(PCSC_LIBRARY /mingw64/x86_64-w64-mingw32/lib/libwinscard.a) + ELSE(BUILD_64 STREQUAL "ON") + set(PCSC_INCLUDE_DIR /mingw32/i686-w64-mingw32/include) + set(PCSC_LIBRARY /mingw32/i686-w64-mingw32/lib/libwinscard.a) + ENDIF(BUILD_64 STREQUAL "ON") +ENDIF (NOT WIN32) + # handle the QUIETLY and REQUIRED arguments and set PCSC_FOUND to TRUE if # all listed variables are TRUE INCLUDE(FindPackageHandleStandardArgs) From 6b8568e9a13895257cdf05af31a0196a32620752 Mon Sep 17 00:00:00 2001 From: stoffu Date: Fri, 1 Jun 2018 08:45:20 +0900 Subject: [PATCH 0046/1404] simplewallet.sign_transfer: rename export to export_raw, add help message --- src/simplewallet/simplewallet.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 397614328aa..6cf9bfad767 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -2082,8 +2082,8 @@ simple_wallet::simple_wallet() tr("Donate to the development team (donate.getmonero.org).")); m_cmd_binder.set_handler("sign_transfer", boost::bind(&simple_wallet::sign_transfer, this, _1), - tr("sign_transfer [export]"), - tr("Sign a transaction from a file.")); + tr("sign_transfer [export_raw]"), + tr("Sign a transaction from a file. If the parameter \"export_raw\" is specified, transaction raw hex data suitable for the daemon RPC /sendrawtransaction is exported.")); m_cmd_binder.set_handler("submit_transfer", boost::bind(&simple_wallet::submit_transfer, this, _1), tr("Submit a signed transaction from a file.")); @@ -5469,9 +5469,9 @@ bool simple_wallet::sign_transfer(const std::vector &args_) fail_msg_writer() << tr("This is a watch only wallet"); return true; } - if (args_.size() > 1 || (args_.size() == 1 && args_[0] != "export")) + if (args_.size() > 1 || (args_.size() == 1 && args_[0] != "export_raw")) { - fail_msg_writer() << tr("usage: sign_transfer [export]"); + fail_msg_writer() << tr("usage: sign_transfer [export_raw]"); return true; } if (m_wallet->ask_password() && !get_and_verify_password()) { return true; } From f8dd43311078b92f4601565ae45441cfd01c82ad Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 1 Jun 2018 21:09:46 +0100 Subject: [PATCH 0047/1404] epee: fix detection of 172.16.0.0/172.31.255.255 local IP range --- contrib/epee/include/net/local_ip.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/epee/include/net/local_ip.h b/contrib/epee/include/net/local_ip.h index 0d458963c62..52c5855b9bf 100644 --- a/contrib/epee/include/net/local_ip.h +++ b/contrib/epee/include/net/local_ip.h @@ -48,7 +48,7 @@ namespace epee if( (ip | 0xffffff00) == 0xffffffac) { - uint32_t second_num = (ip << 8) & 0xff000000; + uint32_t second_num = (ip >> 8) & 0xff; if(second_num >= 16 && second_num <= 31 ) return true; } From 5db9e3c28e6aa96b673dbde6cf0e4eb5de8771de Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 1 Jun 2018 21:10:20 +0100 Subject: [PATCH 0048/1404] unit_tests: add tests for local IP range detection --- tests/unit_tests/epee_utils.cpp | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/tests/unit_tests/epee_utils.cpp b/tests/unit_tests/epee_utils.cpp index 3969f50f6e3..3474000d85d 100644 --- a/tests/unit_tests/epee_utils.cpp +++ b/tests/unit_tests/epee_utils.cpp @@ -45,6 +45,7 @@ #include "boost/archive/portable_binary_oarchive.hpp" #include "hex.h" #include "net/net_utils_base.h" +#include "net/local_ip.h" #include "p2p/net_peerlist_boost_serialization.h" #include "span.h" #include "string_tools.h" @@ -648,3 +649,34 @@ TEST(NetUtils, NetworkAddress) EXPECT_THROW(address1.as(), std::bad_cast); EXPECT_NO_THROW(address1.as()); } + +static bool is_local(const char *s) +{ + uint32_t ip; + CHECK_AND_ASSERT_THROW_MES(epee::string_tools::get_ip_int32_from_string(ip, s), std::string("Invalid IP address: ") + s); + return epee::net_utils::is_ip_local(ip); +} + +TEST(NetUtils, PrivateRanges) +{ + ASSERT_EQ(is_local("10.0.0.0"), true); + ASSERT_EQ(is_local("10.255.0.0"), true); + ASSERT_EQ(is_local("127.0.0.0"), false); // loopback is not considered local + ASSERT_EQ(is_local("192.167.255.255"), false); + ASSERT_EQ(is_local("192.168.0.0"), true); + ASSERT_EQ(is_local("192.168.255.255"), true); + ASSERT_EQ(is_local("192.169.0.0"), false); + ASSERT_EQ(is_local("172.0.0.0"), false); + ASSERT_EQ(is_local("172.15.255.255"), false); + ASSERT_EQ(is_local("172.16.0.0"), true); + ASSERT_EQ(is_local("172.16.255.255"), true); + ASSERT_EQ(is_local("172.31.255.255"), true); + ASSERT_EQ(is_local("172.32.0.0"), false); + ASSERT_EQ(is_local("0.0.0.0"), false); + ASSERT_EQ(is_local("255.255.255.254"), false); + ASSERT_EQ(is_local("11.255.255.255"), false); + ASSERT_EQ(is_local("0.0.0.10"), false); + ASSERT_EQ(is_local("0.0.168.192"), false); + ASSERT_EQ(is_local("0.0.30.172"), false); + ASSERT_EQ(is_local("0.0.30.127"), false); +} From 4f3a4fb7018db81a79446cda742e5c3aa799127f Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 1 Jun 2018 21:52:04 +0100 Subject: [PATCH 0049/1404] blockchain: return error when requesting non existent output avoids RPC thread dying, causing the wallet to timeout --- src/cryptonote_core/blockchain.cpp | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 54d8fac3172..83b9da89575 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -1956,14 +1956,21 @@ bool Blockchain::get_outs(const COMMAND_RPC_GET_OUTPUTS_BIN::request& req, COMMA res.outs.clear(); res.outs.reserve(req.outputs.size()); - for (const auto &i: req.outputs) + try { - // get tx_hash, tx_out_index from DB - const output_data_t od = m_db->get_output_key(i.amount, i.index); - tx_out_index toi = m_db->get_output_tx_and_index(i.amount, i.index); - bool unlocked = is_tx_spendtime_unlocked(m_db->get_tx_unlock_time(toi.first)); + for (const auto &i: req.outputs) + { + // get tx_hash, tx_out_index from DB + const output_data_t od = m_db->get_output_key(i.amount, i.index); + tx_out_index toi = m_db->get_output_tx_and_index(i.amount, i.index); + bool unlocked = is_tx_spendtime_unlocked(m_db->get_tx_unlock_time(toi.first)); - res.outs.push_back({od.pubkey, od.commitment, unlocked, od.height, toi.first}); + res.outs.push_back({od.pubkey, od.commitment, unlocked, od.height, toi.first}); + } + } + catch (const std::exception &e) + { + return false; } return true; } From 2b0c632f329572b6a0d2094ba0fda6c8642b5633 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 1 Jun 2018 21:50:46 +0100 Subject: [PATCH 0050/1404] tx_pool: hold off parsing a tx blob till we actually need it --- src/cryptonote_core/tx_pool.cpp | 43 ++++++++++++++++++++++++--------- src/cryptonote_core/tx_pool.h | 4 ++- 2 files changed, 35 insertions(+), 12 deletions(-) diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index bf1fe476e8c..164530b3eb8 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -927,8 +927,26 @@ namespace cryptonote m_transactions_lock.unlock(); } //--------------------------------------------------------------------------------- - bool tx_memory_pool::is_transaction_ready_to_go(txpool_tx_meta_t& txd, transaction &tx) const + bool tx_memory_pool::is_transaction_ready_to_go(txpool_tx_meta_t& txd, const cryptonote::blobdata &txblob, transaction &tx) const { + struct transction_parser + { + transction_parser(const cryptonote::blobdata &txblob, transaction &tx): txblob(txblob), tx(tx), parsed(false) {} + cryptonote::transaction &operator()() + { + if (!parsed) + { + if (!parse_and_validate_tx_from_blob(txblob, tx)) + throw std::runtime_error("failed to parse transaction blob"); + parsed = true; + } + return tx; + } + const cryptonote::blobdata &txblob; + transaction &tx; + bool parsed; + } lazy_tx(txblob, tx); + //not the best implementation at this time, sorry :( //check is ring_signature already checked ? if(txd.max_used_block_id == null_hash) @@ -938,7 +956,7 @@ namespace cryptonote return false;//we already sure that this tx is broken for this height tx_verification_context tvc; - if(!m_blockchain.check_tx_inputs(tx, txd.max_used_block_height, txd.max_used_block_id, tvc)) + if(!m_blockchain.check_tx_inputs(lazy_tx(), txd.max_used_block_height, txd.max_used_block_id, tvc)) { txd.last_failed_height = m_blockchain.get_current_blockchain_height()-1; txd.last_failed_id = m_blockchain.get_block_id_by_height(txd.last_failed_height); @@ -955,7 +973,7 @@ namespace cryptonote return false; //check ring signature again, it is possible (with very small chance) that this transaction become again valid tx_verification_context tvc; - if(!m_blockchain.check_tx_inputs(tx, txd.max_used_block_height, txd.max_used_block_id, tvc)) + if(!m_blockchain.check_tx_inputs(lazy_tx(), txd.max_used_block_height, txd.max_used_block_id, tvc)) { txd.last_failed_height = m_blockchain.get_current_blockchain_height()-1; txd.last_failed_id = m_blockchain.get_block_id_by_height(txd.last_failed_height); @@ -964,7 +982,7 @@ namespace cryptonote } } //if we here, transaction seems valid, but, anyway, check for key_images collisions with blockchain, just to be sure - if(m_blockchain.have_tx_keyimges_as_spent(tx)) + if(m_blockchain.have_tx_keyimges_as_spent(lazy_tx())) { txd.double_spend_seen = true; return false; @@ -1140,18 +1158,21 @@ namespace cryptonote cryptonote::blobdata txblob = m_blockchain.get_txpool_tx_blob(sorted_it->second); cryptonote::transaction tx; - if (!parse_and_validate_tx_from_blob(txblob, tx)) - { - MERROR("Failed to parse tx from txpool"); - sorted_it++; - continue; - } // Skip transactions that are not ready to be // included into the blockchain or that are // missing key images const cryptonote::txpool_tx_meta_t original_meta = meta; - bool ready = is_transaction_ready_to_go(meta, tx); + bool ready = false; + try + { + ready = is_transaction_ready_to_go(meta, txblob, tx); + } + catch (const std::exception &e) + { + MERROR("Failed to check transaction readiness: " << e.what()); + // continue, not fatal + } if (memcmp(&original_meta, &meta, sizeof(meta))) { try diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h index 19cd83ed9cb..4ce2f085d8d 100644 --- a/src/cryptonote_core/tx_pool.h +++ b/src/cryptonote_core/tx_pool.h @@ -499,10 +499,12 @@ namespace cryptonote * @brief check if a transaction is a valid candidate for inclusion in a block * * @param txd the transaction to check (and info about it) + * @param txblob the transaction blob to check + * @param tx the parsed transaction, if successful * * @return true if the transaction is good to go, otherwise false */ - bool is_transaction_ready_to_go(txpool_tx_meta_t& txd, transaction &tx) const; + bool is_transaction_ready_to_go(txpool_tx_meta_t& txd, const cryptonote::blobdata &txblob, transaction &tx) const; /** * @brief mark all transactions double spending the one passed From 827ca3fd3b092a5190a81d4f688edb55d9d741f0 Mon Sep 17 00:00:00 2001 From: Riccardo Spagni Date: Sat, 2 Jun 2018 14:27:48 +0200 Subject: [PATCH 0051/1404] bump version for 0.12.2 point release --- src/version.cpp.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/version.cpp.in b/src/version.cpp.in index 9fed91d9999..a03da7889c3 100644 --- a/src/version.cpp.in +++ b/src/version.cpp.in @@ -1,5 +1,5 @@ #define DEF_MONERO_VERSION_TAG "@VERSIONTAG@" -#define DEF_MONERO_VERSION "0.12.1.0-master" +#define DEF_MONERO_VERSION "0.12.2.0-master" #define DEF_MONERO_RELEASE_NAME "Lithium Luna" #define DEF_MONERO_VERSION_FULL DEF_MONERO_VERSION "-" DEF_MONERO_VERSION_TAG From 95ccf508cd6b84b7e62c003cabe24891037765e3 Mon Sep 17 00:00:00 2001 From: Riccardo Spagni Date: Sat, 2 Jun 2018 14:31:05 +0200 Subject: [PATCH 0052/1404] update checkpoints.dat for point release --- src/blocks/checkpoints.dat | Bin 197348 -> 198276 bytes src/cryptonote_core/blockchain.cpp | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/blocks/checkpoints.dat b/src/blocks/checkpoints.dat index 501a55673eb30bee550da9b3fcc9093d470890e3..08555850414691beb9b4b841911b7a347e8cb90e 100644 GIT binary patch delta 957 zcmV;u148`dg$#s^3;{HO0fhkpg#iM!0R!U#f4SW%5an`!v@Q@Ls{ArY=pla#*{OSu zjc{vTqS4O*%{jn&9QHCb5LL7bO0tB30o`u0(j~(PwgB#VAJ;24*HL$4e=~S&WsbzH zhKwX`@0p&1Q>Na-U~4{9=pOO_5)$)Dr2?O1H#^A$R%=~pEkK;y5kg^#iGUOacqk@| zf0rb)k%0p^7&XWN63E(T{5^mra?f7%x_wvmHxCjHsAe{rh+Rfk&zHCdKYPhI3vaL7 zVaa#;Q}AgcfFBdp$r7g+=;aIC3aU9-hdD+wlL4}SqcZaWE*b#rGs7Z<7{3q(Y<|p* z<34;1%;M>Wx|b(@apX?yw8zi5v+{q#fBs!BdW%vfso*)isdlR3RVJ>sdw&^SEu(7= z?M@%PpcLPJf=c_ct9NM<-ne(TktOw?QSOW=_F(>wqbzapqRCH{rZ4HE2w=Bz__s2F ztoRffS98up0r=9i@e_&G{YiCHNA{kXHukzemWi3cxZJ!5kxt>ub5Ua48D7*;} z#&>Pj`Yl`YozabeL0?LY9>x>oe-IN-&)f5rPz;oQqIDWb~NET`s z_GHZ&T<+jgHP8V~`)5-FOqUx^4sV)iD@yt7Ve*N*Qncjq3mo?dh6nlSPJoq^|0 zg*P>qHYQvwB5JDE%C~oD@0~njeF`Exhx{VZuB*YeNBjJ_b)eOcpLIxqC+Zf_uCC*x zd!>8FVrT*eaG2Q}8=N Date: Mon, 7 May 2018 23:23:47 +0200 Subject: [PATCH 0053/1404] wallet-rpc: watch-only and cold wallet features added - unsigned_txset, signed_txset in transfer / submit_transfer / sign_transfer - export_outputs, import_outputs Squashed commits: [f4d9f3d4] wallet-rpc: do_not_relay removed from submit_transfer [5b16a86f] wallet-rpc: review-fix - method signature changes, renaming [b7fbb10a] wallet-rpc: naming fixes (unsigned vs signed), consts renamed [8c7d2727] wallet-rpc: sign_transfer added [481d024a] wallet2: sign_tx splitted to work with strings and structs, more granular [2a474db9] wallet-rpc: wallet2::load_unsigned_tx split to load from str, file [b1e3a018] wallet-rpc: review fix, load_tx_from_str variable rename [1f6373be] wallet-rpc: review fix: save_tx_to_{str,file} [2a08eafc] wallet-rpc: review comments fixes - redundant this removed from wallet2.cpp - load_tx_from_str, load_tx_from_file [43498052] wallet-rpc: submit_transfer added [9c45d1ad] wallet-rpc: watch_only check, return unsigned_txset [62831396] wallet2: added string variants to load_tx, save_tx - analogously to save_multisig_tx - required for monero-wallet-rpc to support watch-only wallet --- src/simplewallet/simplewallet.cpp | 68 +----- src/wallet/wallet2.cpp | 183 ++++++++++++--- src/wallet/wallet2.h | 7 + src/wallet/wallet_rpc_server.cpp | 224 ++++++++++++++++++- src/wallet/wallet_rpc_server.h | 10 +- src/wallet/wallet_rpc_server_commands_defs.h | 97 ++++++++ src/wallet/wallet_rpc_server_error_codes.h | 4 + 7 files changed, 495 insertions(+), 98 deletions(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index a45d444f1a0..0d27f8acc56 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -90,8 +90,6 @@ typedef cryptonote::simple_wallet sw; #define MIN_RING_SIZE 7 // Used to inform user about min ring size -- does not track actual protocol -#define OUTPUT_EXPORT_FILE_MAGIC "Monero output export\003" - #define LOCK_IDLE_SCOPE() \ bool auto_refresh_enabled = m_auto_refresh_enabled.load(std::memory_order_relaxed); \ m_auto_refresh_enabled.store(false, std::memory_order_relaxed); \ @@ -7182,19 +7180,8 @@ bool simple_wallet::export_outputs(const std::vector &args) LOCK_IDLE_SCOPE(); try { - std::vector outs = m_wallet->export_outputs(); - - std::stringstream oss; - boost::archive::portable_binary_oarchive ar(oss); - ar << outs; - - std::string magic(OUTPUT_EXPORT_FILE_MAGIC, strlen(OUTPUT_EXPORT_FILE_MAGIC)); - const cryptonote::account_public_address &keys = m_wallet->get_account().get_keys().m_account_address; - std::string header; - header += std::string((const char *)&keys.m_spend_public_key, sizeof(crypto::public_key)); - header += std::string((const char *)&keys.m_view_public_key, sizeof(crypto::public_key)); - std::string ciphertext = m_wallet->encrypt_with_view_secret_key(header + oss.str()); - bool r = epee::file_io_utils::save_string_to_file(filename, magic + ciphertext); + std::string data = m_wallet->export_outputs_to_str(); + bool r = epee::file_io_utils::save_string_to_file(filename, data); if (!r) { fail_msg_writer() << tr("failed to save file ") << filename; @@ -7233,63 +7220,16 @@ bool simple_wallet::import_outputs(const std::vector &args) fail_msg_writer() << tr("failed to read file ") << filename; return true; } - const size_t magiclen = strlen(OUTPUT_EXPORT_FILE_MAGIC); - if (data.size() < magiclen || memcmp(data.data(), OUTPUT_EXPORT_FILE_MAGIC, magiclen)) - { - fail_msg_writer() << "Bad output export file magic in " << filename; - return true; - } - - try - { - data = m_wallet->decrypt_with_view_secret_key(std::string(data, magiclen)); - } - catch (const std::exception &e) - { - fail_msg_writer() << "Failed to decrypt " << filename << ": " << e.what(); - return true; - } - - const size_t headerlen = 2 * sizeof(crypto::public_key); - if (data.size() < headerlen) - { - fail_msg_writer() << "Bad data size from file " << filename; - return true; - } - const crypto::public_key &public_spend_key = *(const crypto::public_key*)&data[0]; - const crypto::public_key &public_view_key = *(const crypto::public_key*)&data[sizeof(crypto::public_key)]; - const cryptonote::account_public_address &keys = m_wallet->get_account().get_keys().m_account_address; - if (public_spend_key != keys.m_spend_public_key || public_view_key != keys.m_view_public_key) - { - fail_msg_writer() << "Outputs from " << filename << " are for a different account"; - return true; - } try { - std::string body(data, headerlen); - std::stringstream iss; - iss << body; - std::vector outputs; - try - { - boost::archive::portable_binary_iarchive ar(iss); - ar >> outputs; - } - catch (...) - { - iss.str(""); - iss << body; - boost::archive::binary_iarchive ar(iss); - ar >> outputs; - } LOCK_IDLE_SCOPE(); - size_t n_outputs = m_wallet->import_outputs(outputs); + size_t n_outputs = m_wallet->import_outputs_from_str(data); success_msg_writer() << boost::lexical_cast(n_outputs) << " outputs imported"; } catch (const std::exception &e) { - fail_msg_writer() << "Failed to import outputs: " << e.what(); + fail_msg_writer() << "Failed to import outputs " << filename << ": " << e.what(); return true; } diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index d11c9937893..128d9780a72 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -110,6 +110,8 @@ using namespace cryptonote; #define MULTISIG_EXPORT_FILE_MAGIC "Monero multisig export\001" +#define OUTPUT_EXPORT_FILE_MAGIC "Monero output export\003" + #define SEGREGATION_FORK_HEIGHT 1546000 #define TESTNET_SEGREGATION_FORK_HEIGHT 1000000 #define STAGENET_SEGREGATION_FORK_HEIGHT 1000000 @@ -4659,6 +4661,15 @@ void wallet2::commit_tx(std::vector& ptx_vector) } //---------------------------------------------------------------------------------------------------- bool wallet2::save_tx(const std::vector& ptx_vector, const std::string &filename) const +{ + LOG_PRINT_L0("saving " << ptx_vector.size() << " transactions"); + std::string ciphertext = dump_tx_to_str(ptx_vector); + if (ciphertext.empty()) + return false; + return epee::file_io_utils::save_string_to_file(filename, ciphertext); +} +//---------------------------------------------------------------------------------------------------- +std::string wallet2::dump_tx_to_str(const std::vector &ptx_vector) const { LOG_PRINT_L0("saving " << ptx_vector.size() << " transactions"); unsigned_tx_set txs; @@ -4680,11 +4691,11 @@ bool wallet2::save_tx(const std::vector& ptx_vector, const std::stri } catch (...) { - return false; + return std::string(); } LOG_PRINT_L2("Saving unsigned tx data: " << oss.str()); std::string ciphertext = encrypt_with_view_secret_key(oss.str()); - return epee::file_io_utils::save_string_to_file(filename, std::string(UNSIGNED_TX_PREFIX) + ciphertext); + return std::string(UNSIGNED_TX_PREFIX) + ciphertext; } //---------------------------------------------------------------------------------------------------- bool wallet2::load_unsigned_tx(const std::string &unsigned_filename, unsigned_tx_set &exported_txs) const @@ -4702,10 +4713,17 @@ bool wallet2::load_unsigned_tx(const std::string &unsigned_filename, unsigned_tx LOG_PRINT_L0("Failed to load from " << unsigned_filename); return false; } + + return parse_unsigned_tx_from_str(s, exported_txs); +} +//---------------------------------------------------------------------------------------------------- +bool wallet2::parse_unsigned_tx_from_str(const std::string &unsigned_tx_st, unsigned_tx_set &exported_txs) const +{ + std::string s = unsigned_tx_st; const size_t magiclen = strlen(UNSIGNED_TX_PREFIX) - 1; if (strncmp(s.c_str(), UNSIGNED_TX_PREFIX, magiclen)) { - LOG_PRINT_L0("Bad magic from " << unsigned_filename); + LOG_PRINT_L0("Bad magic from unsigned tx"); return false; } s = s.substr(magiclen); @@ -4721,7 +4739,7 @@ bool wallet2::load_unsigned_tx(const std::string &unsigned_filename, unsigned_tx } catch (...) { - LOG_PRINT_L0("Failed to parse data from " << unsigned_filename); + LOG_PRINT_L0("Failed to parse data from unsigned tx"); return false; } } @@ -4738,19 +4756,19 @@ bool wallet2::load_unsigned_tx(const std::string &unsigned_filename, unsigned_tx } catch (...) { - LOG_PRINT_L0("Failed to parse data from " << unsigned_filename); + LOG_PRINT_L0("Failed to parse data from unsigned tx"); return false; } } catch (const std::exception &e) { - LOG_PRINT_L0("Failed to decrypt " << unsigned_filename << ": " << e.what()); + LOG_PRINT_L0("Failed to decrypt unsigned tx: " << e.what()); return false; } } else { - LOG_PRINT_L0("Unsupported version in " << unsigned_filename); + LOG_PRINT_L0("Unsupported version in unsigned tx"); return false; } LOG_PRINT_L1("Loaded tx unsigned data from binary: " << exported_txs.txes.size() << " transactions"); @@ -4771,14 +4789,12 @@ bool wallet2::sign_tx(const std::string &unsigned_filename, const std::string &s } return sign_tx(exported_txs, signed_filename, txs, export_raw); } - //---------------------------------------------------------------------------------------------------- -bool wallet2::sign_tx(unsigned_tx_set &exported_txs, const std::string &signed_filename, std::vector &txs, bool export_raw) +bool wallet2::sign_tx(unsigned_tx_set &exported_txs, std::vector &txs, signed_tx_set &signed_txes) { import_outputs(exported_txs.transfers); // sign the transactions - signed_tx_set signed_txes; for (size_t n = 0; n < exported_txs.txes.size(); ++n) { tools::wallet2::tx_construction_data &sd = exported_txs.txes[n]; @@ -4840,19 +4856,20 @@ bool wallet2::sign_tx(unsigned_tx_set &exported_txs, const std::string &signed_f signed_txes.key_images[i] = m_transfers[i].m_key_image; } - // save as binary - std::ostringstream oss; - boost::archive::portable_binary_oarchive ar(oss); - try - { - ar << signed_txes; - } - catch(...) + return true; +} +//---------------------------------------------------------------------------------------------------- +bool wallet2::sign_tx(unsigned_tx_set &exported_txs, const std::string &signed_filename, std::vector &txs, bool export_raw) +{ + // sign the transactions + signed_tx_set signed_txes; + std::string ciphertext = sign_tx_dump_to_str(exported_txs, txs, signed_txes); + if (ciphertext.empty()) { + LOG_PRINT_L0("Failed to sign unsigned_tx_set"); return false; } - LOG_PRINT_L3("Saving signed tx data (with encryption): " << oss.str()); - std::string ciphertext = encrypt_with_view_secret_key(oss.str()); + if (!epee::file_io_utils::save_string_to_file(signed_filename, std::string(SIGNED_TX_PREFIX) + ciphertext)) { LOG_PRINT_L0("Failed to save file to " << signed_filename); @@ -4875,6 +4892,32 @@ bool wallet2::sign_tx(unsigned_tx_set &exported_txs, const std::string &signed_f return true; } //---------------------------------------------------------------------------------------------------- +std::string wallet2::sign_tx_dump_to_str(unsigned_tx_set &exported_txs, std::vector &ptx, signed_tx_set &signed_txes) +{ + // sign the transactions + bool r = sign_tx(exported_txs, ptx, signed_txes); + if (!r) + { + LOG_PRINT_L0("Failed to sign unsigned_tx_set"); + return std::string(); + } + + // save as binary + std::ostringstream oss; + boost::archive::portable_binary_oarchive ar(oss); + try + { + ar << signed_txes; + } + catch(...) + { + return std::string(); + } + LOG_PRINT_L3("Saving signed tx data (with encryption): " << oss.str()); + std::string ciphertext = encrypt_with_view_secret_key(oss.str()); + return std::string(SIGNED_TX_PREFIX) + ciphertext; +} +//---------------------------------------------------------------------------------------------------- bool wallet2::load_tx(const std::string &signed_filename, std::vector &ptx, std::function accept_func) { std::string s; @@ -4892,10 +4935,20 @@ bool wallet2::load_tx(const std::string &signed_filename, std::vector &ptx, std::function accept_func) +{ + std::string s = signed_tx_st; + boost::system::error_code errcode; + signed_tx_set signed_txs; + const size_t magiclen = strlen(SIGNED_TX_PREFIX) - 1; if (strncmp(s.c_str(), SIGNED_TX_PREFIX, magiclen)) { - LOG_PRINT_L0("Bad magic from " << signed_filename); + LOG_PRINT_L0("Bad magic from signed transaction"); return false; } s = s.substr(magiclen); @@ -4911,7 +4964,7 @@ bool wallet2::load_tx(const std::string &signed_filename, std::vector wallet2::export_outputs() const return outs; } //---------------------------------------------------------------------------------------------------- +std::string wallet2::export_outputs_to_str() const +{ + std::vector outs = export_outputs(); + + std::stringstream oss; + boost::archive::portable_binary_oarchive ar(oss); + ar << outs; + + std::string magic(OUTPUT_EXPORT_FILE_MAGIC, strlen(OUTPUT_EXPORT_FILE_MAGIC)); + const cryptonote::account_public_address &keys = get_account().get_keys().m_account_address; + std::string header; + header += std::string((const char *)&keys.m_spend_public_key, sizeof(crypto::public_key)); + header += std::string((const char *)&keys.m_view_public_key, sizeof(crypto::public_key)); + std::string ciphertext = encrypt_with_view_secret_key(header + oss.str()); + return magic + ciphertext; +} +//---------------------------------------------------------------------------------------------------- size_t wallet2::import_outputs(const std::vector &outputs) { m_transfers.clear(); @@ -9804,6 +9874,67 @@ size_t wallet2::import_outputs(const std::vector outputs; + try + { + boost::archive::portable_binary_iarchive ar(iss); + ar >> outputs; + } + catch (...) + { + iss.str(""); + iss << body; + boost::archive::binary_iarchive ar(iss); + ar >> outputs; + } + + imported_outputs = import_outputs(outputs); + } + catch (const std::exception &e) + { + THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string("Failed to import outputs") + e.what()); + } + + return imported_outputs; +} +//---------------------------------------------------------------------------------------------------- crypto::public_key wallet2::get_multisig_signer_public_key(const crypto::secret_key &spend_skey) const { crypto::public_key pkey; diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 61e6927bca4..b5718627e19 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -684,6 +684,7 @@ namespace tools void commit_tx(pending_tx& ptx_vector); void commit_tx(std::vector& ptx_vector); bool save_tx(const std::vector& ptx_vector, const std::string &filename) const; + std::string dump_tx_to_str(const std::vector &ptx_vector) const; std::string save_multisig_tx(multisig_tx_set txs); bool save_multisig_tx(const multisig_tx_set &txs, const std::string &filename); std::string save_multisig_tx(const std::vector& ptx_vector); @@ -692,9 +693,13 @@ namespace tools bool sign_tx(const std::string &unsigned_filename, const std::string &signed_filename, std::vector &ptx, std::function accept_func = NULL, bool export_raw = false); // sign unsigned tx. Takes unsigned_tx_set as argument. Used by GUI bool sign_tx(unsigned_tx_set &exported_txs, const std::string &signed_filename, std::vector &ptx, bool export_raw = false); + bool sign_tx(unsigned_tx_set &exported_txs, std::vector &ptx, signed_tx_set &signed_txs); + std::string sign_tx_dump_to_str(unsigned_tx_set &exported_txs, std::vector &ptx, signed_tx_set &signed_txes); // load unsigned_tx_set from file. bool load_unsigned_tx(const std::string &unsigned_filename, unsigned_tx_set &exported_txs) const; + bool parse_unsigned_tx_from_str(const std::string &unsigned_tx_st, unsigned_tx_set &exported_txs) const; bool load_tx(const std::string &signed_filename, std::vector &ptx, std::function accept_func = NULL); + bool parse_tx_from_str(const std::string &signed_tx_st, std::vector &ptx, std::function accept_func); std::vector create_transactions(std::vector dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector& extra, bool trusted_daemon); std::vector create_transactions_2(std::vector dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector& extra, uint32_t subaddr_account, std::set subaddr_indices, bool trusted_daemon); // pass subaddr_indices by value on purpose std::vector create_transactions_all(uint64_t below, const cryptonote::account_public_address &address, bool is_subaddress, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector& extra, uint32_t subaddr_account, std::set subaddr_indices, bool trusted_daemon); @@ -970,7 +975,9 @@ namespace tools // Import/Export wallet data std::vector export_outputs() const; + std::string export_outputs_to_str() const; size_t import_outputs(const std::vector &outputs); + size_t import_outputs_from_str(const std::string &outputs_st); payment_container export_payments() const; void import_payments(const payment_container &payments); void import_payments_out(const std::list> &confirmed_payments); diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index a9d211532f8..99087865e55 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -712,7 +712,7 @@ namespace tools //------------------------------------------------------------------------------------------------------------------------------ template bool wallet_rpc_server::fill_response(std::vector &ptx_vector, - bool get_tx_key, Ts& tx_key, Tu &amount, Tu &fee, std::string &multisig_txset, bool do_not_relay, + bool get_tx_key, Ts& tx_key, Tu &amount, Tu &fee, std::string &multisig_txset, std::string &unsigned_txset, bool do_not_relay, Ts &tx_hash, bool get_tx_hex, Ts &tx_blob, bool get_tx_metadata, Ts &tx_metadata, epee::json_rpc::error &er) { for (const auto & ptx : ptx_vector) @@ -741,7 +741,16 @@ namespace tools } else { - if (!do_not_relay) + if (m_wallet->watch_only()){ + unsigned_txset = epee::string_tools::buff_to_hex_nodelimer(m_wallet->dump_tx_to_str(ptx_vector)); + if (unsigned_txset.empty()) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "Failed to save unsigned tx set after creation"; + return false; + } + } + else if (!do_not_relay) m_wallet->commit_tx(ptx_vector); // populate response with tx hashes @@ -811,7 +820,7 @@ namespace tools return false; } - return fill_response(ptx_vector, req.get_tx_key, res.tx_key, res.amount, res.fee, res.multisig_txset, req.do_not_relay, + return fill_response(ptx_vector, req.get_tx_key, res.tx_key, res.amount, res.fee, res.multisig_txset, res.unsigned_txset, req.do_not_relay, res.tx_hash, req.get_tx_hex, res.tx_blob, req.get_tx_metadata, res.tx_metadata, er); } catch (const std::exception& e) @@ -858,7 +867,7 @@ namespace tools std::vector ptx_vector = m_wallet->create_transactions_2(dsts, mixin, req.unlock_time, priority, extra, req.account_index, req.subaddr_indices, m_trusted_daemon); LOG_PRINT_L2("on_transfer_split called create_transactions_2"); - return fill_response(ptx_vector, req.get_tx_keys, res.tx_key_list, res.amount_list, res.fee_list, res.multisig_txset, req.do_not_relay, + return fill_response(ptx_vector, req.get_tx_keys, res.tx_key_list, res.amount_list, res.fee_list, res.multisig_txset, res.unsigned_txset, req.do_not_relay, res.tx_hash_list, req.get_tx_hex, res.tx_blob_list, req.get_tx_metadata, res.tx_metadata_list, er); } catch (const std::exception& e) @@ -869,6 +878,141 @@ namespace tools return true; } //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::on_sign_transfer(const wallet_rpc::COMMAND_RPC_SIGN_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_SIGN_TRANSFER::response& res, epee::json_rpc::error& er) + { + if (!m_wallet) return not_open(er); + if (m_wallet->restricted()) + { + er.code = WALLET_RPC_ERROR_CODE_DENIED; + er.message = "Command unavailable in restricted mode."; + return false; + } + if (m_wallet->key_on_device()) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "command not supported by HW wallet"; + return false; + } + if(m_wallet->watch_only()) + { + er.code = WALLET_RPC_ERROR_CODE_WATCH_ONLY; + er.message = "command not supported by watch-only wallet"; + return false; + } + + cryptonote::blobdata blob; + if (!epee::string_tools::parse_hexstr_to_binbuff(req.unsigned_txset, blob)) + { + er.code = WALLET_RPC_ERROR_CODE_BAD_HEX; + er.message = "Failed to parse hex."; + return false; + } + + tools::wallet2::unsigned_tx_set exported_txs; + if(!m_wallet->parse_unsigned_tx_from_str(blob, exported_txs)) + { + er.code = WALLET_RPC_ERROR_CODE_BAD_UNSIGNED_TX_DATA; + er.message = "cannot load unsigned_txset"; + return false; + } + + std::vector ptxs; + try + { + tools::wallet2::signed_tx_set signed_txs; + std::string ciphertext = m_wallet->sign_tx_dump_to_str(exported_txs, ptxs, signed_txs); + if (ciphertext.empty()) + { + er.code = WALLET_RPC_ERROR_CODE_SIGN_UNSIGNED; + er.message = "Failed to sign unsigned tx"; + return false; + } + + res.signed_txset = epee::string_tools::buff_to_hex_nodelimer(ciphertext); + } + catch (const std::exception &e) + { + er.code = WALLET_RPC_ERROR_CODE_SIGN_UNSIGNED; + er.message = std::string("Failed to sign unsigned tx: ") + e.what(); + return false; + } + + for (auto &ptx: ptxs) + { + res.tx_hash_list.push_back(epee::string_tools::pod_to_hex(cryptonote::get_transaction_hash(ptx.tx))); + } + + if (req.export_raw) + { + for (auto &ptx: ptxs) + { + res.tx_raw_list.push_back(epee::string_tools::buff_to_hex_nodelimer(cryptonote::tx_to_blob(ptx.tx))); + } + } + + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::on_submit_transfer(const wallet_rpc::COMMAND_RPC_SUBMIT_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_SUBMIT_TRANSFER::response& res, epee::json_rpc::error& er) + { + if (!m_wallet) return not_open(er); + if (m_wallet->restricted()) + { + er.code = WALLET_RPC_ERROR_CODE_DENIED; + er.message = "Command unavailable in restricted mode."; + return false; + } + if (m_wallet->key_on_device()) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "command not supported by HW wallet"; + return false; + } + + cryptonote::blobdata blob; + if (!epee::string_tools::parse_hexstr_to_binbuff(req.tx_data_hex, blob)) + { + er.code = WALLET_RPC_ERROR_CODE_BAD_HEX; + er.message = "Failed to parse hex."; + return false; + } + + std::vector ptx_vector; + try + { + bool r = m_wallet->parse_tx_from_str(blob, ptx_vector, NULL); + if (!r) + { + er.code = WALLET_RPC_ERROR_CODE_BAD_SIGNED_TX_DATA; + er.message = "Failed to parse signed tx data."; + return false; + } + } + catch (const std::exception &e) + { + er.code = WALLET_RPC_ERROR_CODE_BAD_SIGNED_TX_DATA; + er.message = std::string("Failed to parse signed tx: ") + e.what(); + return false; + } + + try + { + for (auto &ptx: ptx_vector) + { + m_wallet->commit_tx(ptx); + res.tx_hash_list.push_back(epee::string_tools::pod_to_hex(cryptonote::get_transaction_hash(ptx.tx))); + } + } + catch (const std::exception &e) + { + er.code = WALLET_RPC_ERROR_CODE_SIGNED_SUBMISSION; + er.message = std::string("Failed to submit signed tx: ") + e.what(); + return false; + } + + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_sweep_dust(const wallet_rpc::COMMAND_RPC_SWEEP_DUST::request& req, wallet_rpc::COMMAND_RPC_SWEEP_DUST::response& res, epee::json_rpc::error& er) { if (!m_wallet) return not_open(er); @@ -883,7 +1027,7 @@ namespace tools { std::vector ptx_vector = m_wallet->create_unmixable_sweep_transactions(m_trusted_daemon); - return fill_response(ptx_vector, req.get_tx_keys, res.tx_key_list, res.amount_list, res.fee_list, res.multisig_txset, req.do_not_relay, + return fill_response(ptx_vector, req.get_tx_keys, res.tx_key_list, res.amount_list, res.fee_list, res.multisig_txset, res.unsigned_txset, req.do_not_relay, res.tx_hash_list, req.get_tx_hex, res.tx_blob_list, req.get_tx_metadata, res.tx_metadata_list, er); } catch (const std::exception& e) @@ -931,7 +1075,7 @@ namespace tools uint32_t priority = m_wallet->adjust_priority(req.priority); std::vector ptx_vector = m_wallet->create_transactions_all(req.below_amount, dsts[0].addr, dsts[0].is_subaddress, mixin, req.unlock_time, priority, extra, req.account_index, req.subaddr_indices, m_trusted_daemon); - return fill_response(ptx_vector, req.get_tx_keys, res.tx_key_list, res.amount_list, res.fee_list, res.multisig_txset, req.do_not_relay, + return fill_response(ptx_vector, req.get_tx_keys, res.tx_key_list, res.amount_list, res.fee_list, res.multisig_txset, res.unsigned_txset, req.do_not_relay, res.tx_hash_list, req.get_tx_hex, res.tx_blob_list, req.get_tx_metadata, res.tx_metadata_list, er); } catch (const std::exception& e) @@ -1007,7 +1151,7 @@ namespace tools return false; } - return fill_response(ptx_vector, req.get_tx_key, res.tx_key, res.amount, res.fee, res.multisig_txset, req.do_not_relay, + return fill_response(ptx_vector, req.get_tx_key, res.tx_key, res.amount, res.fee, res.multisig_txset, res.unsigned_txset, req.do_not_relay, res.tx_hash, req.get_tx_hex, res.tx_blob, req.get_tx_metadata, res.tx_metadata, er); } catch (const std::exception& e) @@ -1974,6 +2118,72 @@ namespace tools return false; } //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::on_export_outputs(const wallet_rpc::COMMAND_RPC_EXPORT_OUTPUTS::request& req, wallet_rpc::COMMAND_RPC_EXPORT_OUTPUTS::response& res, epee::json_rpc::error& er) + { + if (!m_wallet) return not_open(er); + if (m_wallet->restricted()) + { + er.code = WALLET_RPC_ERROR_CODE_DENIED; + er.message = "Command unavailable in restricted mode."; + return false; + } + if (m_wallet->key_on_device()) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "command not supported by HW wallet"; + return false; + } + + try + { + res.outputs_data_hex = epee::string_tools::buff_to_hex_nodelimer(m_wallet->export_outputs_to_str()); + } + catch (const std::exception &e) + { + handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR); + return false; + } + + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::on_import_outputs(const wallet_rpc::COMMAND_RPC_IMPORT_OUTPUTS::request& req, wallet_rpc::COMMAND_RPC_IMPORT_OUTPUTS::response& res, epee::json_rpc::error& er) + { + if (!m_wallet) return not_open(er); + if (m_wallet->restricted()) + { + er.code = WALLET_RPC_ERROR_CODE_DENIED; + er.message = "Command unavailable in restricted mode."; + return false; + } + if (m_wallet->key_on_device()) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "command not supported by HW wallet"; + return false; + } + + cryptonote::blobdata blob; + if (!epee::string_tools::parse_hexstr_to_binbuff(req.outputs_data_hex, blob)) + { + er.code = WALLET_RPC_ERROR_CODE_BAD_HEX; + er.message = "Failed to parse hex."; + return false; + } + + try + { + res.num_imported = m_wallet->import_outputs_from_str(blob); + } + catch (const std::exception &e) + { + handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR); + return false; + } + + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_export_key_images(const wallet_rpc::COMMAND_RPC_EXPORT_KEY_IMAGES::request& req, wallet_rpc::COMMAND_RPC_EXPORT_KEY_IMAGES::response& res, epee::json_rpc::error& er) { if (!m_wallet) return not_open(er); diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h index 2ec53cc8031..b00ca048e98 100644 --- a/src/wallet/wallet_rpc_server.h +++ b/src/wallet/wallet_rpc_server.h @@ -84,6 +84,8 @@ namespace tools MAP_JON_RPC_WE("getheight", on_getheight, wallet_rpc::COMMAND_RPC_GET_HEIGHT) MAP_JON_RPC_WE("transfer", on_transfer, wallet_rpc::COMMAND_RPC_TRANSFER) MAP_JON_RPC_WE("transfer_split", on_transfer_split, wallet_rpc::COMMAND_RPC_TRANSFER_SPLIT) + MAP_JON_RPC_WE("sign_transfer", on_sign_transfer, wallet_rpc::COMMAND_RPC_SIGN_TRANSFER) + MAP_JON_RPC_WE("submit_transfer", on_submit_transfer, wallet_rpc::COMMAND_RPC_SUBMIT_TRANSFER) MAP_JON_RPC_WE("sweep_dust", on_sweep_dust, wallet_rpc::COMMAND_RPC_SWEEP_DUST) MAP_JON_RPC_WE("sweep_unmixable", on_sweep_dust, wallet_rpc::COMMAND_RPC_SWEEP_DUST) MAP_JON_RPC_WE("sweep_all", on_sweep_all, wallet_rpc::COMMAND_RPC_SWEEP_ALL) @@ -114,6 +116,8 @@ namespace tools MAP_JON_RPC_WE("get_transfer_by_txid", on_get_transfer_by_txid, wallet_rpc::COMMAND_RPC_GET_TRANSFER_BY_TXID) MAP_JON_RPC_WE("sign", on_sign, wallet_rpc::COMMAND_RPC_SIGN) MAP_JON_RPC_WE("verify", on_verify, wallet_rpc::COMMAND_RPC_VERIFY) + MAP_JON_RPC_WE("export_outputs", on_export_outputs, wallet_rpc::COMMAND_RPC_EXPORT_OUTPUTS) + MAP_JON_RPC_WE("import_outputs", on_import_outputs, wallet_rpc::COMMAND_RPC_IMPORT_OUTPUTS) MAP_JON_RPC_WE("export_key_images", on_export_key_images, wallet_rpc::COMMAND_RPC_EXPORT_KEY_IMAGES) MAP_JON_RPC_WE("import_key_images", on_import_key_images, wallet_rpc::COMMAND_RPC_IMPORT_KEY_IMAGES) MAP_JON_RPC_WE("make_uri", on_make_uri, wallet_rpc::COMMAND_RPC_MAKE_URI) @@ -154,6 +158,8 @@ namespace tools bool validate_transfer(const std::list& destinations, const std::string& payment_id, std::vector& dsts, std::vector& extra, bool at_least_one_destination, epee::json_rpc::error& er); bool on_transfer(const wallet_rpc::COMMAND_RPC_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_TRANSFER::response& res, epee::json_rpc::error& er); bool on_transfer_split(const wallet_rpc::COMMAND_RPC_TRANSFER_SPLIT::request& req, wallet_rpc::COMMAND_RPC_TRANSFER_SPLIT::response& res, epee::json_rpc::error& er); + bool on_sign_transfer(const wallet_rpc::COMMAND_RPC_SIGN_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_SIGN_TRANSFER::response& res, epee::json_rpc::error& er); + bool on_submit_transfer(const wallet_rpc::COMMAND_RPC_SUBMIT_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_SUBMIT_TRANSFER::response& res, epee::json_rpc::error& er); bool on_sweep_dust(const wallet_rpc::COMMAND_RPC_SWEEP_DUST::request& req, wallet_rpc::COMMAND_RPC_SWEEP_DUST::response& res, epee::json_rpc::error& er); bool on_sweep_all(const wallet_rpc::COMMAND_RPC_SWEEP_ALL::request& req, wallet_rpc::COMMAND_RPC_SWEEP_ALL::response& res, epee::json_rpc::error& er); bool on_sweep_single(const wallet_rpc::COMMAND_RPC_SWEEP_SINGLE::request& req, wallet_rpc::COMMAND_RPC_SWEEP_SINGLE::response& res, epee::json_rpc::error& er); @@ -182,6 +188,8 @@ namespace tools bool on_get_transfer_by_txid(const wallet_rpc::COMMAND_RPC_GET_TRANSFER_BY_TXID::request& req, wallet_rpc::COMMAND_RPC_GET_TRANSFER_BY_TXID::response& res, epee::json_rpc::error& er); bool on_sign(const wallet_rpc::COMMAND_RPC_SIGN::request& req, wallet_rpc::COMMAND_RPC_SIGN::response& res, epee::json_rpc::error& er); bool on_verify(const wallet_rpc::COMMAND_RPC_VERIFY::request& req, wallet_rpc::COMMAND_RPC_VERIFY::response& res, epee::json_rpc::error& er); + bool on_export_outputs(const wallet_rpc::COMMAND_RPC_EXPORT_OUTPUTS::request& req, wallet_rpc::COMMAND_RPC_EXPORT_OUTPUTS::response& res, epee::json_rpc::error& er); + bool on_import_outputs(const wallet_rpc::COMMAND_RPC_IMPORT_OUTPUTS::request& req, wallet_rpc::COMMAND_RPC_IMPORT_OUTPUTS::response& res, epee::json_rpc::error& er); bool on_export_key_images(const wallet_rpc::COMMAND_RPC_EXPORT_KEY_IMAGES::request& req, wallet_rpc::COMMAND_RPC_EXPORT_KEY_IMAGES::response& res, epee::json_rpc::error& er); bool on_import_key_images(const wallet_rpc::COMMAND_RPC_IMPORT_KEY_IMAGES::request& req, wallet_rpc::COMMAND_RPC_IMPORT_KEY_IMAGES::response& res, epee::json_rpc::error& er); bool on_make_uri(const wallet_rpc::COMMAND_RPC_MAKE_URI::request& req, wallet_rpc::COMMAND_RPC_MAKE_URI::response& res, epee::json_rpc::error& er); @@ -217,7 +225,7 @@ namespace tools template bool fill_response(std::vector &ptx_vector, - bool get_tx_key, Ts& tx_key, Tu &amount, Tu &fee, std::string &multisig_txset, bool do_not_relay, + bool get_tx_key, Ts& tx_key, Tu &amount, Tu &fee, std::string &multisig_txset, std::string &unsigned_txset, bool do_not_relay, Ts &tx_hash, bool get_tx_hex, Ts &tx_blob, bool get_tx_metadata, Ts &tx_metadata, epee::json_rpc::error &er); wallet2 *m_wallet; diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h index a0f43c9b9eb..11f852d6f56 100644 --- a/src/wallet/wallet_rpc_server_commands_defs.h +++ b/src/wallet/wallet_rpc_server_commands_defs.h @@ -419,6 +419,7 @@ namespace wallet_rpc std::string tx_blob; std::string tx_metadata; std::string multisig_txset; + std::string unsigned_txset; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(tx_hash) @@ -429,6 +430,7 @@ namespace wallet_rpc KV_SERIALIZE(tx_blob) KV_SERIALIZE(tx_metadata) KV_SERIALIZE(multisig_txset) + KV_SERIALIZE(unsigned_txset) END_KV_SERIALIZE_MAP() }; }; @@ -484,6 +486,7 @@ namespace wallet_rpc std::list tx_blob_list; std::list tx_metadata_list; std::string multisig_txset; + std::string unsigned_txset; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(tx_hash_list) @@ -493,6 +496,55 @@ namespace wallet_rpc KV_SERIALIZE(tx_blob_list) KV_SERIALIZE(tx_metadata_list) KV_SERIALIZE(multisig_txset) + KV_SERIALIZE(unsigned_txset) + END_KV_SERIALIZE_MAP() + }; + }; + + struct COMMAND_RPC_SIGN_TRANSFER + { + struct request + { + std::string unsigned_txset; + bool export_raw; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(unsigned_txset) + KV_SERIALIZE_OPT(export_raw, false) + END_KV_SERIALIZE_MAP() + }; + + struct response + { + std::string signed_txset; + std::list tx_hash_list; + std::list tx_raw_list; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(signed_txset) + KV_SERIALIZE(tx_hash_list) + KV_SERIALIZE(tx_raw_list) + END_KV_SERIALIZE_MAP() + }; + }; + + struct COMMAND_RPC_SUBMIT_TRANSFER + { + struct request + { + std::string tx_data_hex; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(tx_data_hex) + END_KV_SERIALIZE_MAP() + }; + + struct response + { + std::list tx_hash_list; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(tx_hash_list) END_KV_SERIALIZE_MAP() }; }; @@ -532,6 +584,7 @@ namespace wallet_rpc std::list tx_blob_list; std::list tx_metadata_list; std::string multisig_txset; + std::string unsigned_txset; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(tx_hash_list) @@ -541,6 +594,7 @@ namespace wallet_rpc KV_SERIALIZE(tx_blob_list) KV_SERIALIZE(tx_metadata_list) KV_SERIALIZE(multisig_txset) + KV_SERIALIZE(unsigned_txset) END_KV_SERIALIZE_MAP() }; }; @@ -598,6 +652,7 @@ namespace wallet_rpc std::list tx_blob_list; std::list tx_metadata_list; std::string multisig_txset; + std::string unsigned_txset; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(tx_hash_list) @@ -607,6 +662,7 @@ namespace wallet_rpc KV_SERIALIZE(tx_blob_list) KV_SERIALIZE(tx_metadata_list) KV_SERIALIZE(multisig_txset) + KV_SERIALIZE(unsigned_txset) END_KV_SERIALIZE_MAP() }; }; @@ -651,6 +707,7 @@ namespace wallet_rpc std::string tx_blob; std::string tx_metadata; std::string multisig_txset; + std::string unsigned_txset; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(tx_hash) @@ -660,6 +717,7 @@ namespace wallet_rpc KV_SERIALIZE(tx_blob) KV_SERIALIZE(tx_metadata) KV_SERIALIZE(multisig_txset) + KV_SERIALIZE(unsigned_txset) END_KV_SERIALIZE_MAP() }; }; @@ -1364,6 +1422,45 @@ namespace wallet_rpc }; }; + struct COMMAND_RPC_EXPORT_OUTPUTS + { + struct request + { + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; + + struct response + { + std::string outputs_data_hex; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(outputs_data_hex); + END_KV_SERIALIZE_MAP() + }; + }; + + struct COMMAND_RPC_IMPORT_OUTPUTS + { + struct request + { + std::string outputs_data_hex; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(outputs_data_hex); + END_KV_SERIALIZE_MAP() + }; + + struct response + { + uint64_t num_imported; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(num_imported); + END_KV_SERIALIZE_MAP() + }; + }; + struct COMMAND_RPC_EXPORT_KEY_IMAGES { struct request diff --git a/src/wallet/wallet_rpc_server_error_codes.h b/src/wallet/wallet_rpc_server_error_codes.h index d4746794083..f127ae24033 100644 --- a/src/wallet/wallet_rpc_server_error_codes.h +++ b/src/wallet/wallet_rpc_server_error_codes.h @@ -69,3 +69,7 @@ #define WALLET_RPC_ERROR_CODE_MULTISIG_SUBMISSION -36 #define WALLET_RPC_ERROR_CODE_NOT_ENOUGH_UNLOCKED_MONEY -37 #define WALLET_RPC_ERROR_CODE_NO_DAEMON_CONNECTION -38 +#define WALLET_RPC_ERROR_CODE_BAD_UNSIGNED_TX_DATA -39 +#define WALLET_RPC_ERROR_CODE_BAD_SIGNED_TX_DATA -40 +#define WALLET_RPC_ERROR_CODE_SIGNED_SUBMISSION -41 +#define WALLET_RPC_ERROR_CODE_SIGN_UNSIGNED -42 From afed9787cd11515a725bacf907c4f0b82ad6b35f Mon Sep 17 00:00:00 2001 From: S Date: Mon, 4 Jun 2018 12:02:06 +0200 Subject: [PATCH 0054/1404] Fixes #3645: error on freebsd lambda return values forced to std::string --- src/blockchain_utilities/blockchain_blackball.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/blockchain_utilities/blockchain_blackball.cpp b/src/blockchain_utilities/blockchain_blackball.cpp index 1243822bb2a..a613d73ada4 100644 --- a/src/blockchain_utilities/blockchain_blackball.cpp +++ b/src/blockchain_utilities/blockchain_blackball.cpp @@ -165,7 +165,7 @@ int main(int argc, char* argv[]) "blackball-db-dir", "Specify blackball database directory", get_default_db_path(), {{ &arg_testnet_on, &arg_stagenet_on }}, - [](std::array testnet_stagenet, bool defaulted, std::string val) { + [](std::array testnet_stagenet, bool defaulted, std::string val)->std::string { if (testnet_stagenet[0]) return (boost::filesystem::path(val) / "testnet").string(); else if (testnet_stagenet[1]) From ace2edacb6e229bd6467f76662a4faad3cc76666 Mon Sep 17 00:00:00 2001 From: stoffu Date: Mon, 4 Jun 2018 20:46:24 +0900 Subject: [PATCH 0055/1404] blockchain: pop forked blocks only when DB is not read-only --- src/cryptonote_core/blockchain.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 54d8fac3172..fb2d5f6be96 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -444,7 +444,7 @@ bool Blockchain::init(BlockchainDB* db, const network_type nettype, bool offline m_db->block_txn_stop(); uint64_t num_popped_blocks = 0; - while (true) + while (!m_db->is_read_only()) { const uint64_t top_height = m_db->height() - 1; const crypto::hash top_id = m_db->top_block_hash(); From 5f00df3cf9afb220f472e3af90616a9bcb208bac Mon Sep 17 00:00:00 2001 From: Gingeropolous Date: Mon, 4 Jun 2018 22:32:43 -0400 Subject: [PATCH 0056/1404] Update Readme to indicate recommended version ... is v0.12.2.0 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4c3e9799a5f..64202ad2473 100644 --- a/README.md +++ b/README.md @@ -103,7 +103,7 @@ Dates are provided in the format YYYY-MM-DD. | 1220516 | 2017-01-05 | v4 | v0.10.1 | v0.10.2.1 | Allow normal and RingCT transactions | | 1288616 | 2017-04-15 | v5 | v0.10.3.0 | v0.10.3.1 | Adjusted minimum blocksize and fee algorithm | | 1400000 | 2017-09-16 | v6 | v0.11.0.0 | v0.11.0.0 | Allow only RingCT transactions, allow only >= ringsize 5 | -| 1546000 | 2018-04-06 | v7 | v0.12.0.0 | v0.12.0.0 | Cryptonight variant 1, ringsize >= 7, sorted inputs +| 1546000 | 2018-04-06 | v7 | v0.12.0.0 | v0.12.2.0 | Cryptonight variant 1, ringsize >= 7, sorted inputs | XXXXXXX | 2018-10-XX | XX | XXXXXXXXX | XXXXXXXXX | X X's indicate that these details have not been determined as of commit date. From 2861289efd8c298dbdbd405d652f7d0cd53ce8a4 Mon Sep 17 00:00:00 2001 From: Gingeropolous Date: Tue, 5 Jun 2018 08:31:55 -0400 Subject: [PATCH 0057/1404] readme - explain building master vs. release Update readme to explain and warn about building master, and encourage (via example) to build release branches. Also updated instance where example build instructions had 0.11.0 as the most recent release. --- README.md | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4c3e9799a5f..2fcf7851bdd 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,10 @@ Portions Copyright (c) 2012-2013 The Cryptonote developers. ## Build +### IMPORTANT + +These builds are of the master branch, which is used for active development and can be either unstable or incompatible with release software. Please compile release branches. + | Operating System | Processor | Status | | --------------------- | -------- |--------| | Ubuntu 16.04 | i686 | [![Ubuntu 16.04 i686](https://build.getmonero.org/png?builder=monero-static-ubuntu-i686)](https://build.getmonero.org/builders/monero-static-ubuntu-i686) @@ -210,9 +214,10 @@ invokes cmake commands as needed. #### On Linux and OS X * Install the dependencies -* Change to the root of the source code directory and build: +* Change to the root of the source code directory, change to the most recent release branch, and build: cd monero + git checkout v0.12.2.0 make *Optional*: If your machine has several cores and enough memory, enable @@ -222,6 +227,12 @@ invokes cmake commands as needed. *Note*: If cmake can not find zmq.hpp file on OS X, installing `zmq.hpp` from https://github.com/zeromq/cppzmq to `/usr/local/include` should fix that error. + + *Note*: The instructions above will compile the most stable release of the + Monero software. If you would like to use and test the most recent software, + use ```git checkout master```. The master branch may contain updates that are + both unstable and incompatible with release software, though testing is always + encouraged. * The resulting executables can be found in `build/release/bin` @@ -268,7 +279,7 @@ Tested on a Raspberry Pi Zero with a clean install of minimal Raspbian Stretch ( ``` git clone https://github.com/monero-project/monero.git cd monero - git checkout tags/v0.11.1.0 + git checkout tags/v0.12.2.0 ``` * Build: ``` From 8962f0032fa526582094307a4844d274c1c9f512 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 5 Jun 2018 10:45:01 +0100 Subject: [PATCH 0058/1404] simplewallet: add optional trusted/untrusted argument to set_daemon Otherwise the previous daemon's trustedness would carry over. If not specified, the local address check is performed again. --- src/simplewallet/simplewallet.cpp | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 397614328aa..4acf30b2722 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -2028,7 +2028,7 @@ simple_wallet::simple_wallet() tr("Stop mining in the daemon.")); m_cmd_binder.set_handler("set_daemon", boost::bind(&simple_wallet::set_daemon, this, _1), - tr("set_daemon [:]"), + tr("set_daemon [:] [trusted|untrusted]"), tr("Set another daemon to connect to.")); m_cmd_binder.set_handler("save_bc", boost::bind(&simple_wallet::save_bc, this, _1), @@ -3778,6 +3778,33 @@ bool simple_wallet::set_daemon(const std::vector& args) } LOCK_IDLE_SCOPE(); m_wallet->init(daemon_url); + + if (args.size() == 2) + { + if (args[1] == "trusted") + m_trusted_daemon = true; + else if (args[1] == "untrusted") + m_trusted_daemon = false; + else + { + fail_msg_writer() << tr("Expected trusted or untrusted, got ") << args[1] << ": assuming untrusted"; + m_trusted_daemon = false; + } + } + else + { + m_trusted_daemon = false; + try + { + if (tools::is_local_address(m_wallet->get_daemon_address())) + { + MINFO(tr("Daemon is local, assuming trusted")); + m_trusted_daemon = true; + } + } + catch (const std::exception &e) { } + } + success_msg_writer() << boost::format("Daemon set to %s, %s") % daemon_url % (*m_trusted_daemon ? tr("trusted") : tr("untrusted")); } else { fail_msg_writer() << tr("This does not seem to be a valid daemon URL."); } From 941a608d52ce74eaad2c4c7dc49a8571111081bb Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 5 Jun 2018 11:32:35 +0100 Subject: [PATCH 0059/1404] util: consider Tor/I2P addresses to be non local --- src/common/util.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/common/util.cpp b/src/common/util.cpp index 7e77e19b167..d17d2492868 100644 --- a/src/common/util.cpp +++ b/src/common/util.cpp @@ -657,6 +657,13 @@ std::string get_nix_version_display_string() bool is_local_address(const std::string &address) { + // always assume Tor/I2P addresses to be untrusted by default + if (boost::ends_with(address, ".onion") || boost::ends_with(address, ".i2p")) + { + MDEBUG("Address '" << address << "' is Tor/I2P, non local"); + return false; + } + // extract host epee::net_utils::http::url_content u_c; if (!epee::net_utils::parse_url(address, u_c)) From 2b3357ee98a36bc2252e12d1e336eb9c5c7474f6 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 5 Jun 2018 16:54:46 +0100 Subject: [PATCH 0060/1404] README: mention --untrusted-daemon when connecting the wallet to a daemon on a tor hidden service --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 4c3e9799a5f..aedf8efb1ac 100644 --- a/README.md +++ b/README.md @@ -546,6 +546,8 @@ setting the following configuration parameters and environment variables: as well. * Do NOT pass `--detach` when running through torsocks with systemd, (see [utils/systemd/monerod.service](utils/systemd/monerod.service) for details). +* If you use the wallet with a Tor daemon via the loopback IP (eg, 127.0.0.1:9050), + then use `--untrusted-daemon` unless it is your own hidden service. Example command line to start monerod through Tor: From d81e042306e3ce31abd2749a737055b4c6c8eba8 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 5 Jun 2018 19:59:45 +0100 Subject: [PATCH 0061/1404] tx_pool: initialize bitflags padding since it gets written to storage Avoids valgrind reporting uninitialized data usage --- src/blockchain_db/blockchain_db.h | 1 + src/cryptonote_core/tx_pool.cpp | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/blockchain_db/blockchain_db.h b/src/blockchain_db/blockchain_db.h index 19ba3234085..564016fc9a0 100644 --- a/src/blockchain_db/blockchain_db.h +++ b/src/blockchain_db/blockchain_db.h @@ -148,6 +148,7 @@ struct txpool_tx_meta_t uint8_t relayed; uint8_t do_not_relay; uint8_t double_spend_seen: 1; + uint8_t bf_padding: 7; uint8_t padding[76]; // till 192 bytes }; diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index bf1fe476e8c..9770fa82d06 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -239,6 +239,7 @@ namespace cryptonote meta.relayed = relayed; meta.do_not_relay = do_not_relay; meta.double_spend_seen = have_tx_keyimges_as_spent(tx); + meta.bf_padding = 0; memset(meta.padding, 0, sizeof(meta.padding)); try { @@ -278,6 +279,7 @@ namespace cryptonote meta.relayed = relayed; meta.do_not_relay = do_not_relay; meta.double_spend_seen = false; + meta.bf_padding = 0; memset(meta.padding, 0, sizeof(meta.padding)); try From 4764929ecb40ae8c58984d001318b7c47aff744d Mon Sep 17 00:00:00 2001 From: cryptochangements34 Date: Tue, 5 Jun 2018 19:32:03 -0400 Subject: [PATCH 0062/1404] use deterministic viewkey if not supplied --- src/wallet/api/wallet.cpp | 54 ++++++++++++++++++++++++--------------- 1 file changed, 34 insertions(+), 20 deletions(-) diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index fdecacd8ffc..2cda5a6db68 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -554,18 +554,26 @@ bool WalletImpl::recoverFromKeysWithPassword(const std::string &path, } // parse view secret key + bool has_viewkey = true; + crypto::secret_key viewkey; if (viewkey_string.empty()) { - setStatusError(tr("No view key supplied, cancelled")); - return false; + if(has_spendkey) { + has_viewkey = false; + } + else { + setStatusError(tr("Neither view key nor spend key supplied, cancelled")); + return false; + } } - cryptonote::blobdata viewkey_data; - if(!epee::string_tools::parse_hexstr_to_binbuff(viewkey_string, viewkey_data) || viewkey_data.size() != sizeof(crypto::secret_key)) - { - setStatusError(tr("failed to parse secret view key")); - return false; + if(has_viewkey) { + cryptonote::blobdata viewkey_data; + if(!epee::string_tools::parse_hexstr_to_binbuff(viewkey_string, viewkey_data) || viewkey_data.size() != sizeof(crypto::secret_key)) + { + setStatusError(tr("failed to parse secret view key")); + return false; + } + viewkey = *reinterpret_cast(viewkey_data.data()); } - crypto::secret_key viewkey = *reinterpret_cast(viewkey_data.data()); - // check the spend and view keys match the given address crypto::public_key pkey; if(has_spendkey) { @@ -578,26 +586,32 @@ bool WalletImpl::recoverFromKeysWithPassword(const std::string &path, return false; } } - if (!crypto::secret_key_to_public_key(viewkey, pkey)) { - setStatusError(tr("failed to verify secret view key")); - return false; - } - if (info.address.m_view_public_key != pkey) { - setStatusError(tr("view key does not match address")); - return false; + if(has_viewkey) { + if (!crypto::secret_key_to_public_key(viewkey, pkey)) { + setStatusError(tr("failed to verify secret view key")); + return false; + } + if (info.address.m_view_public_key != pkey) { + setStatusError(tr("view key does not match address")); + return false; + } } try { - if (has_spendkey) { + if (has_spendkey && has_viewkey) { m_wallet->generate(path, password, info.address, spendkey, viewkey); - setSeedLanguage(language); - LOG_PRINT_L1("Generated new wallet from keys with seed language: " + language); + LOG_PRINT_L1("Generated new wallet from spend key and view key"); } - else { + if(!has_spendkey && has_viewkey) { m_wallet->generate(path, password, info.address, viewkey); LOG_PRINT_L1("Generated new view only wallet from keys"); } + if(has_spendkey && !has_viewkey) { + m_wallet->generate(path, password, spendkey, true, false, false); + setSeedLanguage(language); + LOG_PRINT_L1("Generated deterministic wallet from spend key with seed language: " + language); + } } catch (const std::exception& e) { From 01cc9787225fb3ac810f25228eb0547516caedc8 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 6 Jun 2018 10:05:14 +0100 Subject: [PATCH 0063/1404] ringct: remove an unnecessary scalarmultBase in zeroCommit --- src/ringct/rctOps.cpp | 5 +---- src/ringct/rctOps.h | 1 + tests/unit_tests/ringct.cpp | 10 ++++++++++ 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/ringct/rctOps.cpp b/src/ringct/rctOps.cpp index cc46d0aa78a..68cc4312803 100644 --- a/src/ringct/rctOps.cpp +++ b/src/ringct/rctOps.cpp @@ -134,12 +134,9 @@ namespace rct { } key zeroCommit(xmr_amount amount) { - key mask = identity(); - mask = scalarmultBase(mask); key am = d2h(amount); key bH = scalarmultH(am); - addKeys(mask, mask, bH); - return mask; + return addKeys(G, bH); } key commit(xmr_amount amount, const key &mask) { diff --git a/src/ringct/rctOps.h b/src/ringct/rctOps.h index 3f8f6955c62..f8889af5c7d 100644 --- a/src/ringct/rctOps.h +++ b/src/ringct/rctOps.h @@ -62,6 +62,7 @@ namespace rct { static const key Z = { {0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 } }; static const key I = { {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 } }; static const key L = { {0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10 } }; + static const key G = { {0x58, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66 } }; //Creates a zero scalar inline key zero() { return Z; } diff --git a/tests/unit_tests/ringct.cpp b/tests/unit_tests/ringct.cpp index b7fcbbcab13..0f4bd3edf5b 100644 --- a/tests/unit_tests/ringct.cpp +++ b/tests/unit_tests/ringct.cpp @@ -1061,3 +1061,13 @@ TEST(ringct, key_ostream) out.str() ); } + +TEST(ringct, zeroCommmit) +{ + static const uint64_t amount = crypto::rand(); + const rct::key z = rct::zeroCommit(amount); + const rct::key a = rct::scalarmultBase(rct::identity()); + const rct::key b = rct::scalarmultH(rct::d2h(amount)); + const rct::key manual = rct::addKeys(a, b); + ASSERT_EQ(z, manual); +} From 62c8c07c47b2e30a466864bc483ff8a5e3665b81 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 6 Jun 2018 10:12:45 +0100 Subject: [PATCH 0064/1404] wallet: do not log by default if we're not asked to log to console This means monero-wallet-rpc still does, but the user level program does not. --- src/wallet/wallet_args.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/wallet/wallet_args.cpp b/src/wallet/wallet_args.cpp index 6311e770085..50a5540d41e 100644 --- a/src/wallet/wallet_args.cpp +++ b/src/wallet/wallet_args.cpp @@ -185,6 +185,10 @@ namespace wallet_args { mlog_set_log(command_line::get_arg(vm, arg_log_level).c_str()); } + else if (!log_to_console) + { + mlog_set_categories(""); + } if (notice) Print(print) << notice << ENDL; From bf26920f276555a30840a45c51e4aca33c874fbd Mon Sep 17 00:00:00 2001 From: stoffu Date: Wed, 6 Jun 2018 20:33:01 +0900 Subject: [PATCH 0065/1404] wallet2: fix get_approximate_blockchain_height for stagenet --- src/wallet/wallet2.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 3af4494550b..73044c45e91 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -9159,9 +9159,9 @@ uint64_t wallet2::get_daemon_blockchain_target_height(string &err) uint64_t wallet2::get_approximate_blockchain_height() const { // time of v2 fork - const time_t fork_time = m_nettype == TESTNET ? 1448285909 : m_nettype == STAGENET ? (time_t)-1/*TODO*/ : 1458748658; + const time_t fork_time = m_nettype == TESTNET ? 1448285909 : m_nettype == STAGENET ? 1520937818 : 1458748658; // v2 fork block - const uint64_t fork_block = m_nettype == TESTNET ? 624634 : m_nettype == STAGENET ? (uint64_t)-1/*TODO*/ : 1009827; + const uint64_t fork_block = m_nettype == TESTNET ? 624634 : m_nettype == STAGENET ? 32000 : 1009827; // avg seconds per block const int seconds_per_block = DIFFICULTY_TARGET_V2; // Calculated blockchain height From 8cc46069ac7b43e70c5f03aa32c1fb383e1d4f44 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 6 Jun 2018 15:29:18 +0100 Subject: [PATCH 0066/1404] snap: strip metadata from icon.png Reported to "contain[s] an embedded color calibration (ICC) profile which is copyright Hewlett Packard but lack license" --- contrib/snap/setup/gui/icon.png | Bin 19132 -> 1531 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/contrib/snap/setup/gui/icon.png b/contrib/snap/setup/gui/icon.png index 17b8bd47b2fe0d58cb6b133dda5819da41674727..b7e8212704942b8be4e919ad51ff3375fe31c428 100644 GIT binary patch delta 12 UcmdlpmGL+C1m(?7TyC%e04B->!~g&Q literal 19132 zcmeI4cT`i^x5qCoh0OT!B&+r;S8wq$1!4v;hIShR4mqKYi1fK8%o ze!qxQy`Z@{tK@^cM3xgLqK}I>sqq};>SG_5J$EAReB#kZA6`VB_wA`&dii*@ve#(x z(tJ)uJ9`}mPnwmclXf)EMU;%-^SI`FO)thZOk%j?0|5b!3Y=`niX0QLe89j!6h=7TT0fasj%%t$6+`)OGbro3bTB!o{i1AO#>OYK{angi}oK$u{A4*~S4 z15j%>f)#M}22j@~z+Db-@BmP!#CTV6hV41O5CK?UYq+(OFK3lZ^>N$lstUkNA^Y$(sBb4)- zR04TZ&)nt%*V&loCsp_NPmGWE+1x{I$3Ak4S|v8@u5(-Qdw0O#{uJEQV3@SQQ1 ztjo_ES{LjJq}oq$9`YJ|dD8OZ4eqs%(p}(t)?U@_D#Bw<8-q@mWgVY~7l4nTPAT74 z=v>2%j;MWLF&fbZuI@2nicD57-s#Qqm+fW&cS_yp5&+EAkn5i+aj-=D#Jy+_Tbngr zvCLBdq6yZgf&gHbnY;$(QH60c2LPDm#i(30m0YM7SF2-_s^2hJ&;8L;=MYN%UIR)1 z#f6KO3iedK6pKL|UuP$tNKtkWZt*^(LQTApd(TM{d1RZ44)?=>rZS ztn9NyCa#bNf9+L+>OcZtk+a_yU!JLm`K(1EObB4jph{HQ9ZYAqK z0N}A76KXACr(?>U44?8;qN|H_HyyG#$NdCHfn}W(&C7lge0`JQah?3hC#638&rTYl zo1t~D#Mw3P`9U?B8d2Bu)HqYTTdsjG@su`gy1MleOU;o*@Ahk9tQ%v@>o|2-!%!fs zjH#8oox4MyRWV3PUQ6&9r!FVUA*Y7T;FMz9>x#Do-`x}Ug2N8GTdB$QY&LTiEzp8+>@J)h+~RGg0y)IPZlbveJv`HIuw zVj-=)S%}ARqSm$rm(QhkZSoQGSt4C|MY$z7lq%zp{=}`bWy3s+5-v5TBf(@I@Sa<;s7k#r$m)Gt0Y#OpC%Zyk7WccpP@K#PAV0p&mTBCEIg|`sw`QkWmgTu@9-^2OHXqzwJLQgwS0YB zr);Jur|`CUjmL)=KGSBK%{w+*5>K{m$eDEMcNr?;DiXCfL@t(Tl=v1%=GT~o-3=&u zlaYI8S9Go&xoCLb?aB92QIHkPivRnfMN1xcwhA73wgH|tafAEXL1Y7INb|s3C-lxn z6LbMK6`TIzP;zyL!TD~B-!1r$lbUvn$&dLr`8St_wUA!T2b)t`LpsdQjDhf;V5+mNYU)K00 z<#tNct*GBu4B2@P_w$uXZ<9U{ay?iWxfMB_)pq8Man${Vv?&PDyeQ=z^imkv0h9b<@~7VqyYb*gi(?}>cX#{<`k>qzZY^T111O4CU?Q>{i7}1Y z&ir9HV!!O&6UN8^{9on$Gp5J$s!c^qPsU!3ov6Po>+|4`q|35PlNoKvZ6v<(B_LtwRXIAL)pk&(X3FAKim*`8y0seIVlKLB5R*gMVuzSB!sqH z!pE1d-VZo-10s{k{?NA~>Cy%db*?H@;VaBGrCt^Gaw6W1aQE?%M~Jl2T-C=jtrd?3 zl623^o?V;S*s$@%sj7kmTQ_xS_m{UX?Lm|q1nnE(8WY)|ny{7alEKZRRVx17j-@kh z>179QD@NG+#_Xzp6kA`(h)mn%uYvlh{s4J_I*N*q>;#xqh-PaV(B6W z`;-R5CnOJ(AKlFyJbnFQ>Ah-PJE4;>^>SD}cROENN&3rlz5w3-XV-a0wqBoE{L6d* zlKxm}peC*gU48zp!OdEH(ZjaK`o@?Gm<7yxjLw_Rp-OD;azMu$$;jHjwEpNG*j~G| zV)XFNfGd-$eK(;S|H1l0#gcK^k&>z9v=9C}FFW`L%`~%gvaGPn@m2L!Kkb(8G{iWc z>`{Gf$=Cebozysyn(Z5(TH+j8lHo-@3q2lr=>>b&Z=upeX~-T_ z)?AiFwoBHB?Oxk&Ikp;g^h^&&e6$?+jsJ|6fyX|ZdCWu7v#Hpw6yjX_@toA0r~?}B z20aJPRYgvEw~g%KC(qszpuFfbUpqFbU#s7?W9}wpu6$rZ$nBpE zV{^@!nyj+w9lq6XB5qek5|*YUb9LK>7H;o*E4tRxor^FWx;-C3Ub?@uI3hZ9Ab9gi zTT4xU*ycBvYaMHMFPO}j_M?XmcN=eC9qgMOJh-PWEN{MJ&~k|_KCXQ=dGRDadc(=g zlPw9n39obYj1Y)X$bseV*S&2W86ElQbLlB}{r4BF zOxL}ZxH8bx&?Ix`VYnu2*TU8hGxcGEVbZhbNBAAb_k#{9t@*sWw&?Pt=tGJ`O3E55 zOUC?1Z%y?906qKZ_4dV6KwfwvDZmgs zTXPo-BH;|dZrb)x`#?0_hh!BIjCYA}z<5XadLwXPVi6|#2pIIK3&qzEY`Q)m$lZPm2u%*ggS0iY)xDuGEs!ok1Evkr(u1ji;82(* z1gZ&vX{y8ENGJjc(*}KcfsMGR-wc9r1f;W>#h2lzmLb@OLJ34dAYox)8ey6m+V?3V+EVD<&#A?tI|_r>`scHQ%jVfb*;KZLD&K8Jluh0iNwK+QiSIv9_okb^N~vY*lCEc<@{M7@1d zvd4Op0@m-Rlno%X{l72vcPYFXmV!5;CZm=*R7V}AgMmSjFa%OtPZg?%ghGE1`P`F6 z!=9S>I4lMGZ&ZHjORGc1kqG<#mCE;?KWo_ABdr5MDA)jRytSDTb?zD@5)SD_-ELS- zEJ9rmtBX_Df>I~x1@j`P6A;?EdfspX3$)CgjCT&Lc-n*VfU!-wVX>D&Eybcb5 zRfl>(z16jF-Y|89j<&WsR1Xh_X~Q)MSiIgRepKln!v7}i7)+v;A*|mIeb$fue>%i3 zWd1gk6)A+8Jo|n~Dr#zdDl{(mpr2d6I{ZkV3PvC{I0V1G4-LUzH{!Pn^R0la$9&q) zNUZmIsWI|i&sIFn0P<7o_rv-Y`6Z0>_XYfSlt1hL5dP0&g!$kD=q3DTsZYKBStNu^ zpoC$A@uoy-PW-nU@w4R5F|^tSkoCIkNBTclg%E7$|85Qb-6s0~zXsp)*V_jhK*ZyW zAYW7WYs0@=w$F?C^Pc)z3jV$a4RGE_0y)?pOEDt(V~Kc3U;xno^1bo)Tz-n(e~a#3dj4on~|vJg^=}Xr%j#Hrx;qtF9SXPDewo! zm%z{I^usmWry}vWm3oCpy*Pz@yE^?_{rFa6|C3+7&*(p?N~6UXga!zGlYxsq9~v(M z7Yz{lCIc6JJ~UniE*c>8O$ILdd}zE3Tr@!Fn+#m^`OtV7xM+aTHyOC-^P%xFaM1vv zZ!&Pv=R@OV;GzLS-(=vT&xgj#z(oUuzRAEvpAU_ffr|zReUpKUJ|7w{0~ZYt`X&Px zeLgf^1}+*P^i2jX`g~}-3|uro=$i~&^!dHy9(dR?s zW#FO#Lf>TIqR)rM%fLkggucnZMV}9imw}502z`@*i#{J3F9R115c(zq7kxf7UIs22 zAoNWJF8X|EybN44K*JM*?!j9P~ktGvyEHE+{~D1ix6p;(9pKXV8BXhR{)X4Ffjb*SfC^EQfa zxcPJm`y3v~r^hSB)&?AWIrdP&dW}5?W#1&$FNJqxMr!N_1siN|0tQcNA;w zhBH?q^9dDEOn)Y9lWCt2XU6yMjt+~Nt7pFG;u|3ycZC>Z9L*lWTQ1YbkAYHDn5Mg( zTR0!+JlCEp5{nt?kKJFQL%CDQs&`tM+9;TCa!-t5sA&(skgfdmF3-cRNm&N7M<@}@ z;eAKZ3;l+T^B_)PrXd!O@yGH4kukjcq!uFU+RRvruPRP;ZsM&yeL+s_PxzmqGN5T! zRzaJ&Aui7dc#<3YE5GaSYef5yhXi=*Y!+7wnV$JMuqrdNasAd9G<#M8oUya6{APPH z(=&Cpru%UQxG}{z-Y!7xM#pM=cv80xS;rm9%b9ldTq-OHRyQLtsMQPoUBUzyOBHRpBOlMosfr7sbUzI5$BQ1zc6%Z$gZ<0bdmpLJO#dA&Y6-XP{I1YYd%S&1 zgIR`pl};#615@^DDIw+Vrrl3lPMlDw*{a+w@`Cy ztTXy4-;*Ngot@F-5MkAw?DP9=FC(-z`}yUh@PQ?zLxJWlEtQIn7s4Af!?IdN-tF}? zkcW%ClcEWR`cI@73S8$u}Ir(`smuswb=U~OPT<%I}$3oBS;mql|u%0kSu$s2Y z&KZsi3LGpkmS&?Qjgsp+{mU9av)7#pJ|%}!lKRom23CZmN|ORlX&M47IR9vIxUIB+ zKj_J$;9$-;mwe}~iSvbVQ1=btr;fLCQkvK{%_~V;qkXWs3L}*#Cn|D}9PegHTjD9i zt8`Ds)$QD>A$6X;PpsH8=5D-s{95!e(5cskxYN6i_Z`=7t!HaQez>KwtUG;Q72JEX zwJWONk0}YqYclubz~Vjp>B`g4YA+GyJQFi$V0n{U%eh>_9*MIwSs8;6wMR)`AmEA+7IH%mkOuZJ2iV=-Gqx%J*zHlv4f_cT01)r24F5D#KpMIsN2kBYR$ zhCTz@9o}M9p(4v(8~~r^YQw8V(~IW-`#KhtfKA75&$GNtDMmhjnwJ_kstK{$ON0M_OXW@RYP*#82qu-lLT From c36760944729ed7617ced0108772566cbe6c2b5f Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 6 Jun 2018 17:56:01 +0100 Subject: [PATCH 0067/1404] blockchain_import: warn+delay about using --dangerous-unverified-import This is the new name for --guard-against-pwnage 0 --- .../blockchain_import.cpp | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/blockchain_utilities/blockchain_import.cpp b/src/blockchain_utilities/blockchain_import.cpp index caa549c1327..a58257719bb 100644 --- a/src/blockchain_utilities/blockchain_import.cpp +++ b/src/blockchain_utilities/blockchain_import.cpp @@ -593,8 +593,8 @@ int main(int argc, char* argv[]) const command_line::arg_descriptor arg_database = { "database", available_dbs.c_str(), default_db_type }; - const command_line::arg_descriptor arg_verify = {"guard-against-pwnage", - "Verify blocks and transactions during import (only disable if you exported the file yourself)", true}; + const command_line::arg_descriptor arg_noverify = {"dangerous-unverified-import", + "Blindly trust the import file and use potentially malicious blocks and transactions during import (only enable if you exported the file yourself)", false}; const command_line::arg_descriptor arg_batch = {"batch", "Batch transactions for faster import", true}; const command_line::arg_descriptor arg_resume = {"resume", @@ -614,7 +614,7 @@ int main(int argc, char* argv[]) // call add_options() directly for these arguments since // command_line helpers support only boolean switch, not boolean argument desc_cmd_sett.add_options() - (arg_verify.name, make_semantic(arg_verify), arg_verify.description) + (arg_noverify.name, make_semantic(arg_noverify), arg_noverify.description) (arg_batch.name, make_semantic(arg_batch), arg_batch.description) (arg_resume.name, make_semantic(arg_resume), arg_resume.description) ; @@ -633,7 +633,7 @@ int main(int argc, char* argv[]) if (! r) return 1; - opt_verify = command_line::get_arg(vm, arg_verify); + opt_verify = !command_line::get_arg(vm, arg_noverify); opt_batch = command_line::get_arg(vm, arg_batch); opt_resume = command_line::get_arg(vm, arg_resume); block_stop = command_line::get_arg(vm, arg_block_stop); @@ -738,6 +738,18 @@ int main(int argc, char* argv[]) MINFO("bootstrap file path: " << import_file_path); MINFO("database path: " << m_config_folder); + if (!opt_verify) + { + MCLOG_RED(el::Level::Warning, "global", "\n" + "Import is set to proceed WITHOUT VERIFICATION.\n" + "This is a DANGEROUS operation: if the file was tampered with in transit, or obtained from a malicious source,\n" + "you could end up with a compromised database. It is recommended to NOT use " << arg_noverify.name << ".\n" + "*****************************************************************************************\n" + "You have 90 seconds to press ^C or terminate this program before unverified import starts\n" + "*****************************************************************************************"); + sleep(90); + } + cryptonote::cryptonote_protocol_stub pr; //TODO: stub only for this kind of test, make real validation of relayed objects cryptonote::core core(&pr); From d7a6b72c15f898c45a71831cca04cf0fc968ad91 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 7 Jun 2018 12:28:23 +0100 Subject: [PATCH 0068/1404] wallet2: fix bulletproof cold signing Cold signing was always using Borromean range proofs, causing a larger tx, and an incorrect fee --- src/wallet/wallet2.cpp | 8 ++++---- src/wallet/wallet2.h | 8 +++++++- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 3af4494550b..7750d1c3ae1 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -4788,11 +4788,10 @@ bool wallet2::sign_tx(unsigned_tx_set &exported_txs, const std::string &signed_f LOG_PRINT_L1(" " << (n+1) << ": " << sd.sources.size() << " inputs, ring size " << sd.sources[0].outputs.size()); signed_txes.ptx.push_back(pending_tx()); tools::wallet2::pending_tx &ptx = signed_txes.ptx.back(); - bool bulletproof = sd.use_rct && !ptx.tx.rct_signatures.p.bulletproofs.empty(); crypto::secret_key tx_key; std::vector additional_tx_keys; rct::multisig_out msout; - bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sd.sources, sd.splitted_dsts, sd.change_dts.addr, sd.extra, ptx.tx, sd.unlock_time, tx_key, additional_tx_keys, sd.use_rct, bulletproof, m_multisig ? &msout : NULL); + bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sd.sources, sd.splitted_dsts, sd.change_dts.addr, sd.extra, ptx.tx, sd.unlock_time, tx_key, additional_tx_keys, sd.use_rct, sd.use_bulletproofs, m_multisig ? &msout : NULL); THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sd.sources, sd.splitted_dsts, sd.unlock_time, m_nettype); // we don't test tx size, because we don't know the current limit, due to not having a blockchain, // and it's a bit pointless to fail there anyway, since it'd be a (good) guess only. We sign anyway, @@ -5168,8 +5167,7 @@ bool wallet2::sign_multisig_tx(multisig_tx_set &exported_txs, std::vector extra; uint64_t unlock_time; bool use_rct; + bool use_bulletproofs; std::vector dests; // original setup, does not include change uint32_t subaddr_account; // subaddress account of your wallet to be used in this transfer std::set subaddr_indices; // set of address indices used as inputs in this transfer @@ -332,6 +333,7 @@ namespace tools FIELD(extra) FIELD(unlock_time) FIELD(use_rct) + FIELD(use_bulletproofs) FIELD(dests) FIELD(subaddr_account) FIELD(subaddr_indices) @@ -1248,7 +1250,7 @@ BOOST_CLASS_VERSION(tools::wallet2::address_book_row, 17) BOOST_CLASS_VERSION(tools::wallet2::reserve_proof_entry, 0) BOOST_CLASS_VERSION(tools::wallet2::unsigned_tx_set, 0) BOOST_CLASS_VERSION(tools::wallet2::signed_tx_set, 0) -BOOST_CLASS_VERSION(tools::wallet2::tx_construction_data, 2) +BOOST_CLASS_VERSION(tools::wallet2::tx_construction_data, 3) BOOST_CLASS_VERSION(tools::wallet2::pending_tx, 3) BOOST_CLASS_VERSION(tools::wallet2::multisig_sig, 0) @@ -1595,6 +1597,9 @@ namespace boost if (ver < 2) return; a & x.selected_transfers; + if (ver < 3) + return; + a & x.use_bulletproofs; } template @@ -1880,6 +1885,7 @@ namespace tools ptx.construction_data.extra = tx.extra; ptx.construction_data.unlock_time = unlock_time; ptx.construction_data.use_rct = false; + ptx.construction_data.use_bulletproofs = false; ptx.construction_data.dests = dsts; // record which subaddress indices are being used as inputs ptx.construction_data.subaddr_account = subaddr_account; From 2bb2c1b9d74445c8386d9f0329d41877786687cb Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 7 Jun 2018 13:48:30 +0100 Subject: [PATCH 0069/1404] password: flush cout when prompting for password without newline --- src/common/password.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/password.cpp b/src/common/password.cpp index 9336a14fc83..3ce2ba42a34 100644 --- a/src/common/password.cpp +++ b/src/common/password.cpp @@ -164,7 +164,7 @@ namespace while (true) { if (message) - std::cout << message <<": "; + std::cout << message <<": " << std::flush; if (!read_from_tty(pass1)) return false; if (verify) From 284fe6438d5444ab11fecce959b32106f5d2562f Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 3 Mar 2018 14:01:52 +0000 Subject: [PATCH 0070/1404] db_lmdb: warn about slowness when running off a spinning disk --- src/blockchain_db/lmdb/db_lmdb.cpp | 3 ++ src/common/util.cpp | 70 ++++++++++++++++++++++++++++++ src/common/util.h | 2 + 3 files changed, 75 insertions(+) diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp index 300fb6d2f6c..fe31321f3be 100644 --- a/src/blockchain_db/lmdb/db_lmdb.cpp +++ b/src/blockchain_db/lmdb/db_lmdb.cpp @@ -1178,6 +1178,9 @@ void BlockchainLMDB::open(const std::string& filename, const int db_flags) throw DB_ERROR("Database could not be opened"); } + if (tools::is_hdd(filename.c_str())) + MCLOG_RED(el::Level::Warning, "global", "The blockchain is on a rotating drive: this will be very slow, use a SSD if possible"); + m_folder = filename; #ifdef __OpenBSD__ diff --git a/src/common/util.cpp b/src/common/util.cpp index 7e77e19b167..00861011772 100644 --- a/src/common/util.cpp +++ b/src/common/util.cpp @@ -34,6 +34,17 @@ #include #endif +#ifdef __GLIBC__ +#include +#include +#include +#include +#include +#include +#include +#include +#endif + #include "unbound.h" #include "include_base_utils.h" @@ -632,6 +643,65 @@ std::string get_nix_version_display_string() #endif } + bool is_hdd(const char *path) + { +#ifdef __GLIBC__ + std::string device = ""; + struct stat st, dst; + if (stat(path, &st) < 0) + return 0; + + DIR *dir = opendir("/dev/block"); + if (!dir) + return 0; + struct dirent *de; + while ((de = readdir(dir))) + { + if (strcmp(de->d_name, ".") && strcmp(de->d_name, "..")) + { + std::string dev_path = std::string("/dev/block/") + de->d_name; + char resolved[PATH_MAX]; + if (realpath(dev_path.c_str(), resolved) && !strncmp(resolved, "/dev/", 5)) + { + if (stat(resolved, &dst) == 0) + { + if (dst.st_rdev == st.st_dev) + { + // take out trailing digits (eg, sda1 -> sda) + char *ptr = resolved; + while (*ptr) + ++ptr; + while (ptr > resolved && isdigit(*--ptr)) + *ptr = 0; + device = resolved + 5; + break; + } + } + } + } + } + closedir(dir); + + if (device.empty()) + return 0; + + std::string sys_path = "/sys/block/" + device + "/queue/rotational"; + FILE *f = fopen(sys_path.c_str(), "r"); + if (!f) + return false; + char s[8]; + char *ptr = fgets(s, sizeof(s), f); + fclose(f); + if (!ptr) + return 0; + s[sizeof(s) - 1] = 0; + int n = atoi(s); // returns 0 on parse error + return n == 1; +#else + return 0; +#endif + } + namespace { boost::mutex max_concurrency_lock; diff --git a/src/common/util.h b/src/common/util.h index d3ba47a4fc5..7caf0e3c596 100644 --- a/src/common/util.h +++ b/src/common/util.h @@ -212,4 +212,6 @@ namespace tools bool sha256sum(const uint8_t *data, size_t len, crypto::hash &hash); bool sha256sum(const std::string &filename, crypto::hash &hash); + + bool is_hdd(const char *path); } From f61fe2ef2806a988d3273323a4d1be87a7da8ca7 Mon Sep 17 00:00:00 2001 From: Gingeropolous Date: Fri, 8 Jun 2018 16:05:50 -0400 Subject: [PATCH 0071/1404] Update readme to include pcslite dependency Update readme to include pcslite dependency --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 9e28192b997..56374863116 100644 --- a/README.md +++ b/README.md @@ -187,6 +187,7 @@ library archives (`.a`). | GTest | 1.5 | YES | `libgtest-dev`^ | `gtest` | `gtest-devel` | YES | Test suite | | Doxygen | any | NO | `doxygen` | `doxygen` | `doxygen` | YES | Documentation | | Graphviz | any | NO | `graphviz` | `graphviz` | `graphviz` | YES | Documentation | +| pcsclite | ? | NO | `libpcsclite-dev` | ? | `pcsc-lite pcsc-lite-devel` | NO | Ledger | [^] On Debian/Ubuntu `libgtest-dev` only includes sources and headers. You must From 9ddb07fcc4ba97542c208fd60098e4d739a65bfe Mon Sep 17 00:00:00 2001 From: Gingeropolous Date: Fri, 8 Jun 2018 16:13:51 -0400 Subject: [PATCH 0072/1404] update readme to include debian one liner for deps update readme to include debian one liner for deps , because who wants to select all of those individually --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 9e28192b997..943cfa0cc99 100644 --- a/README.md +++ b/README.md @@ -192,6 +192,9 @@ library archives (`.a`). [^] On Debian/Ubuntu `libgtest-dev` only includes sources and headers. You must build the library binary manually. This can be done with the following command ```sudo apt-get install libgtest-dev && cd /usr/src/gtest && sudo cmake . && sudo make && sudo mv libg* /usr/lib/ ``` +Debian / Ubuntu one liner for all dependencies +``` sudo apt update && sudo apt install build-essential cmake pkg-config libboost-all-dev libssl-dev libzmq3-dev libunbound-dev libsodium-dev libminiupnpc-dev libunwind8-dev liblzma-dev libreadline6-dev libldns-dev libexpat1-dev doxygen graphviz libpcsclite-dev ``` + ### Cloning the repository Clone recursively to pull-in needed submodule(s): From 948c48271a9bb92712f156e25003920c9667d45d Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 8 Jun 2018 21:28:11 +0100 Subject: [PATCH 0073/1404] simplewallet: grammar fix in warning message --- src/simplewallet/simplewallet.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 397614328aa..49c06f80453 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -7529,7 +7529,7 @@ int main(int argc, char* argv[]) std::tie(vm, should_terminate) = wallet_args::main( argc, argv, "monero-wallet-cli [--wallet-file=|--generate-new-wallet=] []", - sw::tr("This is the command line monero wallet. It needs to connect to a monero\ndaemon to work correctly.\nWARNING: Do not reuse your Monero keys on an another fork, UNLESS this fork has key reuse mitigations built in. Doing so will harm your privacy."), + sw::tr("This is the command line monero wallet. It needs to connect to a monero\ndaemon to work correctly.\nWARNING: Do not reuse your Monero keys on another fork, UNLESS this fork has key reuse mitigations built in. Doing so will harm your privacy."), desc_params, positional_options, [](const std::string &s, bool emphasis){ tools::scoped_message_writer(emphasis ? epee::console_color_white : epee::console_color_default, true) << s; }, From f4e329583e6b0adf2b8b467c9578585a15e5d972 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 1 Apr 2018 09:18:27 +0100 Subject: [PATCH 0074/1404] simplewallet: set_ring can now take a file with rings --- src/simplewallet/simplewallet.cpp | 112 +++++++++++++++++++++++++++++- 1 file changed, 110 insertions(+), 2 deletions(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 397614328aa..429a3871747 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -1371,9 +1371,117 @@ bool simple_wallet::print_ring(const std::vector &args) bool simple_wallet::set_ring(const std::vector &args) { crypto::key_image key_image; + + // try filename first + if (args.size() == 1) + { + if (!epee::file_io_utils::is_file_exist(args[0])) + { + fail_msg_writer() << tr("File doesn't exist"); + return true; + } + + char str[4096]; + std::unique_ptr f(fopen(args[0].c_str(), "r")); + if (f) + { + while (!feof(f.get())) + { + if (!fgets(str, sizeof(str), f.get())) + break; + const size_t len = strlen(str); + if (len > 0 && str[len - 1] == '\n') + str[len - 1] = 0; + if (!str[0]) + continue; + char key_image_str[65], type_str[9]; + int read_after_key_image = 0, read = 0; + int fields = sscanf(str, "%64[abcdefABCDEF0123456789] %n%8s %n", key_image_str, &read_after_key_image, type_str, &read); + if (fields != 2) + { + fail_msg_writer() << tr("Invalid ring specification: ") << str; + continue; + } + key_image_str[64] = 0; + type_str[8] = 0; + crypto::key_image key_image; + if (read_after_key_image == 0 || !epee::string_tools::hex_to_pod(key_image_str, key_image)) + { + fail_msg_writer() << tr("Invalid key image: ") << str; + continue; + } + if (read == read_after_key_image+8 || (strcmp(type_str, "absolute") && strcmp(type_str, "relative"))) + { + fail_msg_writer() << tr("Invalid ring type, expected relative or abosolute: ") << str; + continue; + } + bool relative = !strcmp(type_str, "relative"); + if (read < 0 || (size_t)read > strlen(str)) + { + fail_msg_writer() << tr("Error reading line: ") << str; + continue; + } + bool valid = true; + std::vector ring; + const char *ptr = str + read; + while (*ptr) + { + unsigned long offset; + int elements = sscanf(ptr, "%lu %n", &offset, &read); + if (elements == 0 || read <= 0 || (size_t)read > strlen(str)) + { + fail_msg_writer() << tr("Error reading line: ") << str; + valid = false; + break; + } + ring.push_back(offset); + ptr += read; + MGINFO("read offset: " << offset); + } + if (!valid) + continue; + if (ring.empty()) + { + fail_msg_writer() << tr("Invalid ring: ") << str; + continue; + } + if (relative) + { + for (size_t n = 1; n < ring.size(); ++n) + { + if (ring[n] <= 0) + { + fail_msg_writer() << tr("Invalid relative ring: ") << str; + valid = false; + break; + } + } + } + else + { + for (size_t n = 1; n < ring.size(); ++n) + { + if (ring[n] <= ring[n-1]) + { + fail_msg_writer() << tr("Invalid absolute ring: ") << str; + valid = false; + break; + } + } + } + if (!valid) + continue; + if (!m_wallet->set_ring(key_image, ring, relative)) + fail_msg_writer() << tr("Failed to set ring for key image: ") << key_image << ". " << tr("Continuing."); + } + f.reset(); + } + return true; + } + if (args.size() < 3) { - fail_msg_writer() << tr("usage: set_ring absolute|relative [...]"); + fail_msg_writer() << tr("usage: set_ring | ( absolute|relative [...] )"); return true; } @@ -2325,7 +2433,7 @@ simple_wallet::simple_wallet() tr("Print the ring(s) used to spend a given key image or transaction (if the ring size is > 1)")); m_cmd_binder.set_handler("set_ring", boost::bind(&simple_wallet::set_ring, this, _1), - tr("set_ring absolute|relative [...]"), + tr("set_ring | ( absolute|relative [...] )"), tr("Set the ring used for a given key image, so it can be reused in a fork")); m_cmd_binder.set_handler("save_known_rings", boost::bind(&simple_wallet::save_known_rings, this, _1), From 3381b6517e9f8138170b71038b6478afd9555d05 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 9 Jun 2018 12:37:15 +0100 Subject: [PATCH 0075/1404] abstract_tcp_server2: fix busy calling of idle IO service This would make monerod use 100% CPU when running with torsocks without Tor running --- contrib/epee/include/net/abstract_tcp_server2.inl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/contrib/epee/include/net/abstract_tcp_server2.inl b/contrib/epee/include/net/abstract_tcp_server2.inl index 91a94c21ebf..bc54acae44c 100644 --- a/contrib/epee/include/net/abstract_tcp_server2.inl +++ b/contrib/epee/include/net/abstract_tcp_server2.inl @@ -383,7 +383,7 @@ PRAGMA_WARNING_DISABLE_VS(4355) //ask it inside(!) critical region if we still able to go in event wait... size_t cnt = socket_.get_io_service().poll_one(); if(!cnt) - misc_utils::sleep_no_w(0); + misc_utils::sleep_no_w(1); } return true; @@ -836,7 +836,9 @@ POP_WARNINGS { try { - io_service_.run(); + size_t cnt = io_service_.run(); + if (cnt == 0) + misc_utils::sleep_no_w(1); } catch(const std::exception& ex) { From 4ecf71405f19cb5f09e68739b7f725d9ab53077e Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 30 May 2018 15:55:22 +0100 Subject: [PATCH 0076/1404] epee: fix include for ofstream --- contrib/epee/include/file_io_utils.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/epee/include/file_io_utils.h b/contrib/epee/include/file_io_utils.h index 1966106743f..68b9f82de2f 100644 --- a/contrib/epee/include/file_io_utils.h +++ b/contrib/epee/include/file_io_utils.h @@ -28,7 +28,7 @@ #ifndef _FILE_IO_UTILS_H_ #define _FILE_IO_UTILS_H_ -#include +#include #include #include #ifdef WIN32 From 87665281ea6044d313c9b863879758dfea6b9299 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 26 May 2018 23:38:08 +0100 Subject: [PATCH 0077/1404] epee: fallback to a counter if gmtime fails when rotating logs --- contrib/epee/src/mlog.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/contrib/epee/src/mlog.cpp b/contrib/epee/src/mlog.cpp index fb0b4ac2bb9..0c810729d02 100644 --- a/contrib/epee/src/mlog.cpp +++ b/contrib/epee/src/mlog.cpp @@ -47,6 +47,7 @@ using namespace epee; static std::string generate_log_filename(const char *base) { std::string filename(base); + static unsigned int fallback_counter = 0; char tmp[200]; struct tm tm; time_t now = time(NULL); @@ -56,7 +57,7 @@ static std::string generate_log_filename(const char *base) #else (!gmtime_r(&now, &tm)) #endif - strcpy(tmp, "unknown"); + snprintf(tmp, sizeof(tmp), "part-%u", ++fallback_counter); else strftime(tmp, sizeof(tmp), "%Y-%m-%d-%H-%M-%S", &tm); tmp[sizeof(tmp) - 1] = 0; From 1789b76b5d257a8c3e316b758222edf6745af225 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 1 May 2018 16:21:43 +0100 Subject: [PATCH 0078/1404] cryptonote_protocol_handler: log when dropping a peer --- src/cryptonote_protocol/cryptonote_protocol_handler.inl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl index 2e1df8078a3..dd34e461efb 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl @@ -1712,6 +1712,9 @@ skip: template void t_cryptonote_protocol_handler::drop_connection(cryptonote_connection_context &context, bool add_fail, bool flush_all_spans) { + LOG_DEBUG_CC(context, "dropping connection id " << context.m_connection_id << + ", add_fail " << add_fail << ", flush_all_spans " << flush_all_spans); + if (add_fail) m_p2p->add_host_fail(context.m_remote_address); From 2d5921eefa150c502f3e1ff5917e3e439458d6de Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 9 Jun 2018 19:49:58 +0100 Subject: [PATCH 0079/1404] blockchain: avoid duplicate db query for height --- src/cryptonote_core/blockchain.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index fbb2a222340..76a26a8f4a5 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -2205,7 +2205,7 @@ bool Blockchain::find_blockchain_supplement(const std::list& qbloc CRITICAL_REGION_LOCAL(m_blockchain_lock); bool result = find_blockchain_supplement(qblock_ids, resp.m_block_ids, resp.start_height, resp.total_height); - resp.cumulative_difficulty = m_db->get_block_cumulative_difficulty(m_db->height() - 1); + resp.cumulative_difficulty = m_db->get_block_cumulative_difficulty(resp.total_height - 1); return result; } From 03096150c626066bedf451846e6dbedd995df1aa Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 9 Jun 2018 20:49:05 +0100 Subject: [PATCH 0080/1404] device_ledger: fix bad memory access on connect error --- src/device/device_ledger.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/device/device_ledger.cpp b/src/device/device_ledger.cpp index 3b9ab674400..641fe74c399 100644 --- a/src/device/device_ledger.cpp +++ b/src/device/device_ledger.cpp @@ -396,7 +396,7 @@ namespace hw { } } - if (mszReaders) { + if (rv == SCARD_S_SUCCESS && mszReaders) { #ifdef SCARD_AUTOALLOCATE SCardFreeMemory(this->hContext, mszReaders); #else From 362f5e6eb8d4603cdc36db20bcf94987b0ff955c Mon Sep 17 00:00:00 2001 From: stoffu Date: Sun, 10 Jun 2018 20:00:59 +0900 Subject: [PATCH 0081/1404] simplewallet: don't confirm missing payment ID when sending to only subaddresses --- src/simplewallet/simplewallet.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 397614328aa..790ddb061aa 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -4437,6 +4437,7 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector dsts; + size_t num_subaddresses = 0; for (size_t i = 0; i < local_args.size(); i += 2) { cryptonote::address_parse_info info; @@ -4448,6 +4449,7 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vectorconfirm_missing_payment_id()) + if (!payment_id_seen && m_wallet->confirm_missing_payment_id() && dsts.size() > num_subaddresses) { std::string accepted = input_line(tr("No payment id is included with this transaction. Is this okay? (Y/Yes/N/No): ")); if (std::cin.eof()) @@ -4933,7 +4935,7 @@ bool simple_wallet::sweep_main(uint64_t below, const std::vector &a } // prompt is there is no payment id and confirmation is required - if (!payment_id_seen && m_wallet->confirm_missing_payment_id()) + if (!payment_id_seen && m_wallet->confirm_missing_payment_id() && !info.is_subaddress) { std::string accepted = input_line(tr("No payment id is included with this transaction. Is this okay? (Y/Yes/N/No): ")); if (std::cin.eof()) @@ -5146,7 +5148,7 @@ bool simple_wallet::sweep_single(const std::vector &args_) } // prompt if there is no payment id and confirmation is required - if (!payment_id_seen && m_wallet->confirm_missing_payment_id()) + if (!payment_id_seen && m_wallet->confirm_missing_payment_id() && !info.is_subaddress) { std::string accepted = input_line(tr("No payment id is included with this transaction. Is this okay? (Y/Yes/N/No): ")); if (std::cin.eof()) From 55c7fb87a99cbd89fe2d98de7bde9854ee12b161 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 7 Jun 2018 12:43:10 +0100 Subject: [PATCH 0082/1404] epee: adaptive connection timeout system a connection's timeout is halved for every extra connection from the same host. Also keep track of when we don't need to use a connection anymore, so we can close it and free the resource for another connection. Also use the longer timeout for non routable local addresses. --- .../epee/include/net/abstract_tcp_server2.h | 10 ++- .../epee/include/net/abstract_tcp_server2.inl | 80 +++++++++++++++---- .../include/net/http_protocol_handler.inl | 3 +- contrib/epee/include/net/net_utils_base.h | 1 + tests/fuzz/levin.cpp | 1 + .../epee_levin_protocol_handler_async.cpp | 1 + 6 files changed, 77 insertions(+), 19 deletions(-) diff --git a/contrib/epee/include/net/abstract_tcp_server2.h b/contrib/epee/include/net/abstract_tcp_server2.h index 2f7325be549..7ca6ac872f9 100644 --- a/contrib/epee/include/net/abstract_tcp_server2.h +++ b/contrib/epee/include/net/abstract_tcp_server2.h @@ -119,6 +119,7 @@ namespace net_utils //----------------- i_service_endpoint --------------------- virtual bool do_send(const void* ptr, size_t cb); ///< (see do_send from i_service_endpoint) virtual bool do_send_chunk(const void* ptr, size_t cb); ///< will send (or queue) a part of data + virtual bool send_done(); virtual bool close(); virtual bool call_run_once_service_io(); virtual bool request_callback(); @@ -137,8 +138,11 @@ namespace net_utils /// reset connection timeout timer and callback void reset_timer(boost::posix_time::milliseconds ms, bool add); - boost::posix_time::milliseconds get_default_time() const; - boost::posix_time::milliseconds get_timeout_from_bytes_read(size_t bytes) const; + boost::posix_time::milliseconds get_default_timeout(); + boost::posix_time::milliseconds get_timeout_from_bytes_read(size_t bytes); + + /// host connection count tracking + unsigned int host_count(const std::string &host, int delta = 0); /// Buffer for incoming data. boost::array buffer_; @@ -165,6 +169,8 @@ namespace net_utils boost::asio::deadline_timer m_timer; bool m_local; + bool m_ready_to_close; + std::string m_host; public: void setRpcStation(); diff --git a/contrib/epee/include/net/abstract_tcp_server2.inl b/contrib/epee/include/net/abstract_tcp_server2.inl index 91a94c21ebf..134bb4199ba 100644 --- a/contrib/epee/include/net/abstract_tcp_server2.inl +++ b/contrib/epee/include/net/abstract_tcp_server2.inl @@ -56,8 +56,8 @@ #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "net" -#define DEFAULT_TIMEOUT_MS_LOCAL boost::posix_time::milliseconds(120000) // 2 minutes -#define DEFAULT_TIMEOUT_MS_REMOTE boost::posix_time::milliseconds(10000) // 10 seconds +#define DEFAULT_TIMEOUT_MS_LOCAL 1800000 // 30 minutes +#define DEFAULT_TIMEOUT_MS_REMOTE 300000 // 5 minutes #define TIMEOUT_EXTRA_MS_PER_BYTE 0.2 PRAGMA_WARNING_PUSH @@ -86,7 +86,8 @@ PRAGMA_WARNING_DISABLE_VS(4355) m_throttle_speed_in("speed_in", "throttle_speed_in"), m_throttle_speed_out("speed_out", "throttle_speed_out"), m_timer(io_service), - m_local(false) + m_local(false), + m_ready_to_close(false) { MDEBUG("test, connection constructor set m_connection_type="<(); const unsigned long ip_{boost::asio::detail::socket_ops::host_to_network_long(remote_ep.address().to_v4().to_ulong())}; - m_local = epee::net_utils::is_ip_loopback(ip_); + m_local = epee::net_utils::is_ip_loopback(ip_) || epee::net_utils::is_ip_local(ip_); // create a random uuid boost::uuids::uuid random_uuid; @@ -165,9 +166,12 @@ PRAGMA_WARNING_DISABLE_VS(4355) return false; } + m_host = context.m_remote_address.host_str(); + try { host_count(m_host, 1); } catch(...) { /* ignore */ } + m_protocol_handler.after_init_connection(); - reset_timer(get_default_time(), false); + reset_timer(get_default_timeout(), false); socket_.async_read_some(boost::asio::buffer(buffer_), strand_.wrap( @@ -324,6 +328,7 @@ PRAGMA_WARNING_DISABLE_VS(4355) logger_handle_net_read(bytes_transferred); context.m_last_recv = time(NULL); context.m_recv_cnt += bytes_transferred; + m_ready_to_close = false; bool recv_res = m_protocol_handler.handle_recv(buffer_.data(), bytes_transferred); if(!recv_res) { @@ -356,6 +361,13 @@ PRAGMA_WARNING_DISABLE_VS(4355) _dbg3("[sock " << socket_.native_handle() << "] Some problems at read: " << e.message() << ':' << e.value()); shutdown(); } + else + { + _dbg3("[sock " << socket_.native_handle() << "] peer closed connection"); + if (m_ready_to_close) + shutdown(); + } + m_ready_to_close = true; } // If an error occurs then no new asynchronous operations are started. This // means that all shared_ptr references to the connection object will @@ -531,7 +543,7 @@ PRAGMA_WARNING_DISABLE_VS(4355) if(m_send_que.size() > 1) { // active operation should be in progress, nothing to do, just wait last operation callback auto size_now = cb; - MDEBUG("do_send() NOW just queues: packet="<::handle_write, self, _1, _2) @@ -566,29 +578,51 @@ PRAGMA_WARNING_DISABLE_VS(4355) return true; - CATCH_ENTRY_L0("connection::do_send", false); + CATCH_ENTRY_L0("connection::do_send_chunk", false); } // do_send_chunk //--------------------------------------------------------------------------------- template - boost::posix_time::milliseconds connection::get_default_time() const + boost::posix_time::milliseconds connection::get_default_timeout() { + unsigned count; + try { count = host_count(m_host); } catch (...) { count = 0; } + const unsigned shift = std::min(std::max(count, 1u) - 1, 8u); + boost::posix_time::milliseconds timeout(0); if (m_local) - return DEFAULT_TIMEOUT_MS_LOCAL; + timeout = boost::posix_time::milliseconds(DEFAULT_TIMEOUT_MS_LOCAL >> shift); else - return DEFAULT_TIMEOUT_MS_REMOTE; + timeout = boost::posix_time::milliseconds(DEFAULT_TIMEOUT_MS_REMOTE >> shift); + return timeout; } //--------------------------------------------------------------------------------- template - boost::posix_time::milliseconds connection::get_timeout_from_bytes_read(size_t bytes) const + boost::posix_time::milliseconds connection::get_timeout_from_bytes_read(size_t bytes) { boost::posix_time::milliseconds ms = (boost::posix_time::milliseconds)(unsigned)(bytes * TIMEOUT_EXTRA_MS_PER_BYTE); ms += m_timer.expires_from_now(); - if (ms > get_default_time()) - ms = get_default_time(); + if (ms > get_default_timeout()) + ms = get_default_timeout(); return ms; } //--------------------------------------------------------------------------------- template + unsigned int connection::host_count(const std::string &host, int delta) + { + static boost::mutex hosts_mutex; + CRITICAL_REGION_LOCAL(hosts_mutex); + static std::map hosts; + unsigned int &val = hosts[host]; + if (delta > 0) + MTRACE("New connection from host " << host << ": " << val); + else if (delta < 0) + MTRACE("Closed connection from host " << host << ": " << val); + CHECK_AND_ASSERT_THROW_MES(delta >= 0 || val >= (unsigned)-delta, "Count would go negative"); + CHECK_AND_ASSERT_THROW_MES(delta <= 0 || val <= std::numeric_limits::max() - (unsigned)delta, "Count would wrap"); + val += delta; + return val; + } + //--------------------------------------------------------------------------------- + template void connection::reset_timer(boost::posix_time::milliseconds ms, bool add) { if (m_connection_type != e_connection_type_RPC) @@ -621,6 +655,11 @@ PRAGMA_WARNING_DISABLE_VS(4355) socket_.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignored_ec); m_was_shutdown = true; m_protocol_handler.release_protocol(); + if (!m_host.empty()) + { + try { host_count(m_host, -1); } catch (...) { /* ignore */ } + m_host = ""; + } return true; } //--------------------------------------------------------------------------------- @@ -645,6 +684,15 @@ PRAGMA_WARNING_DISABLE_VS(4355) } //--------------------------------------------------------------------------------- template + bool connection::send_done() + { + if (m_ready_to_close) + return close(); + m_ready_to_close = true; + return true; + } + //--------------------------------------------------------------------------------- + template bool connection::cancel() { return close(); @@ -687,7 +735,7 @@ PRAGMA_WARNING_DISABLE_VS(4355) }else { //have more data to send - reset_timer(get_default_time(), false); + reset_timer(get_default_timeout(), false); auto size_now = m_send_que.front().size(); MDEBUG("handle_write() NOW SENDS: packet="<do_send((void*)response_data.data(), response_data.size()); if ((response.m_body.size() && (query_info.m_http_method != http::http_method_head)) || (query_info.m_http_method == http::http_method_options)) m_psnd_hndlr->do_send((void*)response.m_body.data(), response.m_body.size()); + m_psnd_hndlr->send_done(); return res; } //----------------------------------------------------------------------------------- diff --git a/contrib/epee/include/net/net_utils_base.h b/contrib/epee/include/net/net_utils_base.h index 7615786bea6..a133942fb03 100644 --- a/contrib/epee/include/net/net_utils_base.h +++ b/contrib/epee/include/net/net_utils_base.h @@ -281,6 +281,7 @@ namespace net_utils { virtual bool do_send(const void* ptr, size_t cb)=0; virtual bool close()=0; + virtual bool send_done()=0; virtual bool call_run_once_service_io()=0; virtual bool request_callback()=0; virtual boost::asio::io_service& get_io_service()=0; diff --git a/tests/fuzz/levin.cpp b/tests/fuzz/levin.cpp index 6a164dda9dc..4ced1837f78 100644 --- a/tests/fuzz/levin.cpp +++ b/tests/fuzz/levin.cpp @@ -158,6 +158,7 @@ namespace } virtual bool close() { return true; } + virtual bool send_done() { return true; } virtual bool call_run_once_service_io() { return true; } virtual bool request_callback() { return true; } virtual boost::asio::io_service& get_io_service() { return m_io_service; } diff --git a/tests/unit_tests/epee_levin_protocol_handler_async.cpp b/tests/unit_tests/epee_levin_protocol_handler_async.cpp index 38a8360d7b0..72d8f3205ea 100644 --- a/tests/unit_tests/epee_levin_protocol_handler_async.cpp +++ b/tests/unit_tests/epee_levin_protocol_handler_async.cpp @@ -150,6 +150,7 @@ namespace } virtual bool close() { /*std::cout << "test_connection::close()" << std::endl; */return true; } + virtual bool send_done() { /*std::cout << "test_connection::send_done()" << std::endl; */return true; } virtual bool call_run_once_service_io() { std::cout << "test_connection::call_run_once_service_io()" << std::endl; return true; } virtual bool request_callback() { std::cout << "test_connection::request_callback()" << std::endl; return true; } virtual boost::asio::io_service& get_io_service() { std::cout << "test_connection::get_io_service()" << std::endl; return m_io_service; } From 08b85a8e00c0b3e5bb7c4d05861f8a42593cecf1 Mon Sep 17 00:00:00 2001 From: stoffu Date: Mon, 11 Jun 2018 12:16:29 +0900 Subject: [PATCH 0083/1404] cryptonote_config: add get_config to refactor x = testnet ? config::testnet::X : stagenet ? config::stagenet::X : config::X --- .../cryptonote_basic_impl.cpp | 19 ++----- src/cryptonote_config.h | 56 +++++++++++++++++++ src/cryptonote_core/blockchain.cpp | 13 +---- src/p2p/net_node.inl | 6 +- src/simplewallet/simplewallet.cpp | 2 +- src/wallet/wallet2.cpp | 18 ++---- 6 files changed, 70 insertions(+), 44 deletions(-) diff --git a/src/cryptonote_basic/cryptonote_basic_impl.cpp b/src/cryptonote_basic/cryptonote_basic_impl.cpp index 08a95d2e9f4..cff23695f3b 100644 --- a/src/cryptonote_basic/cryptonote_basic_impl.cpp +++ b/src/cryptonote_basic/cryptonote_basic_impl.cpp @@ -162,10 +162,7 @@ namespace cryptonote { , account_public_address const & adr ) { - uint64_t address_prefix = nettype == TESTNET ? - (subaddress ? config::testnet::CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX : config::testnet::CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX) : nettype == STAGENET ? - (subaddress ? config::stagenet::CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX : config::stagenet::CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX) : - (subaddress ? config::CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX : config::CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX); + uint64_t address_prefix = subaddress ? get_config(nettype).CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX : get_config(nettype).CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX; return tools::base58::encode_addr(address_prefix, t_serializable_object_to_blob(adr)); } @@ -176,7 +173,7 @@ namespace cryptonote { , crypto::hash8 const & payment_id ) { - uint64_t integrated_address_prefix = nettype == TESTNET ? config::testnet::CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX : nettype == STAGENET ? config::stagenet::CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX : config::CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX; + uint64_t integrated_address_prefix = get_config(nettype).CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX; integrated_address iadr = { adr, payment_id @@ -201,15 +198,9 @@ namespace cryptonote { , std::string const & str ) { - uint64_t address_prefix = nettype == TESTNET ? - config::testnet::CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX : nettype == STAGENET ? - config::stagenet::CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX : config::CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX; - uint64_t integrated_address_prefix = nettype == TESTNET ? - config::testnet::CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX : nettype == STAGENET ? - config::stagenet::CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX : config::CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX; - uint64_t subaddress_prefix = nettype == TESTNET ? - config::testnet::CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX : nettype == STAGENET ? - config::stagenet::CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX : config::CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX; + uint64_t address_prefix = get_config(nettype).CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX; + uint64_t integrated_address_prefix = get_config(nettype).CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX; + uint64_t subaddress_prefix = get_config(nettype).CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX; if (2 * sizeof(public_address_outer_blob) != str.size()) { diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index 3e64073dd46..a0dcf2df18a 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -203,4 +203,60 @@ namespace cryptonote FAKECHAIN, UNDEFINED = 255 }; + struct config_t + { + uint64_t const CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX; + uint64_t const CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX; + uint64_t const CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX; + uint16_t const P2P_DEFAULT_PORT; + uint16_t const RPC_DEFAULT_PORT; + uint16_t const ZMQ_RPC_DEFAULT_PORT; + boost::uuids::uuid const NETWORK_ID; + std::string const GENESIS_TX; + uint32_t const GENESIS_NONCE; + }; + inline const config_t& get_config(network_type nettype) + { + static const config_t mainnet = { + ::config::CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX, + ::config::CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX, + ::config::CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX, + ::config::P2P_DEFAULT_PORT, + ::config::RPC_DEFAULT_PORT, + ::config::ZMQ_RPC_DEFAULT_PORT, + ::config::NETWORK_ID, + ::config::GENESIS_TX, + ::config::GENESIS_NONCE + }; + static const config_t testnet = { + ::config::testnet::CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX, + ::config::testnet::CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX, + ::config::testnet::CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX, + ::config::testnet::P2P_DEFAULT_PORT, + ::config::testnet::RPC_DEFAULT_PORT, + ::config::testnet::ZMQ_RPC_DEFAULT_PORT, + ::config::testnet::NETWORK_ID, + ::config::testnet::GENESIS_TX, + ::config::testnet::GENESIS_NONCE + }; + static const config_t stagenet = { + ::config::stagenet::CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX, + ::config::stagenet::CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX, + ::config::stagenet::CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX, + ::config::stagenet::P2P_DEFAULT_PORT, + ::config::stagenet::RPC_DEFAULT_PORT, + ::config::stagenet::ZMQ_RPC_DEFAULT_PORT, + ::config::stagenet::NETWORK_ID, + ::config::stagenet::GENESIS_TX, + ::config::stagenet::GENESIS_NONCE + }; + switch (nettype) + { + case MAINNET: return mainnet; + case TESTNET: return testnet; + case STAGENET: return stagenet; + case FAKECHAIN: return mainnet; + default: throw std::runtime_error("Invalid network type"); + } + }; } diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index fbb2a222340..d4509e4a31c 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -393,18 +393,7 @@ bool Blockchain::init(BlockchainDB* db, const network_type nettype, bool offline MINFO("Blockchain not loaded, generating genesis block."); block bl = boost::value_initialized(); block_verification_context bvc = boost::value_initialized(); - if (m_nettype == TESTNET) - { - generate_genesis_block(bl, config::testnet::GENESIS_TX, config::testnet::GENESIS_NONCE); - } - else if (m_nettype == STAGENET) - { - generate_genesis_block(bl, config::stagenet::GENESIS_TX, config::stagenet::GENESIS_NONCE); - } - else - { - generate_genesis_block(bl, config::GENESIS_TX, config::GENESIS_NONCE); - } + generate_genesis_block(bl, get_config(m_nettype).GENESIS_TX, get_config(m_nettype).GENESIS_NONCE); add_new_block(bl, bvc); CHECK_AND_ASSERT_MES(!bvc.m_verifivation_failed, false, "Failed to add genesis block to blockchain"); } diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index 9b21705ec15..58034f191da 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -273,7 +273,7 @@ namespace nodetool { nodetool::peerlist_entry pe = AUTO_VAL_INIT(pe); pe.id = crypto::rand(); - const uint16_t default_port = testnet ? ::config::testnet::P2P_DEFAULT_PORT : stagenet ? ::config::stagenet::P2P_DEFAULT_PORT : ::config::P2P_DEFAULT_PORT; + const uint16_t default_port = cryptonote::get_config(m_nettype).P2P_DEFAULT_PORT; bool r = parse_peer_from_string(pe.adr, pr_str, default_port); CHECK_AND_ASSERT_MES(r, false, "Failed to parse address from string: " << pr_str); m_command_line_peers.push_back(pe); @@ -483,7 +483,7 @@ namespace nodetool if (result.size()) { for (const auto& addr_string : result) - full_addrs.insert(addr_string + ":" + std::to_string(m_nettype == cryptonote::TESTNET ? ::config::testnet::P2P_DEFAULT_PORT : m_nettype == cryptonote::STAGENET ? ::config::stagenet::P2P_DEFAULT_PORT : ::config::P2P_DEFAULT_PORT)); + full_addrs.insert(addr_string + ":" + std::to_string(cryptonote::get_config(m_nettype).P2P_DEFAULT_PORT)); } ++i; } @@ -1807,7 +1807,7 @@ namespace nodetool for(const std::string& pr_str: perrs) { epee::net_utils::network_address na = AUTO_VAL_INIT(na); - const uint16_t default_port = m_nettype == cryptonote::TESTNET ? ::config::testnet::P2P_DEFAULT_PORT : m_nettype == cryptonote::STAGENET ? ::config::stagenet::P2P_DEFAULT_PORT : ::config::P2P_DEFAULT_PORT; + const uint16_t default_port = cryptonote::get_config(m_nettype).P2P_DEFAULT_PORT; bool r = parse_peer_from_string(na, pr_str, default_port); CHECK_AND_ASSERT_MES(r, false, "Failed to parse address from string: " << pr_str); container.push_back(na); diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 397614328aa..b530082d730 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -3771,7 +3771,7 @@ bool simple_wallet::set_daemon(const std::vector& args) // If no port has been provided, use the default from config if (!match[3].length()) { - int daemon_port = m_wallet->nettype() == cryptonote::TESTNET ? config::testnet::RPC_DEFAULT_PORT : m_wallet->nettype() == cryptonote::STAGENET ? config::stagenet::RPC_DEFAULT_PORT : config::RPC_DEFAULT_PORT; + int daemon_port = get_config(m_wallet->nettype()).RPC_DEFAULT_PORT; daemon_url = match[1] + match[2] + std::string(":") + std::to_string(daemon_port); } else { daemon_url = args[0]; diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index d2db45f12c8..aa0702716f6 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -196,6 +196,7 @@ std::unique_ptr make_basic(const boost::program_options::variabl { const bool testnet = command_line::get_arg(vm, opts.testnet); const bool stagenet = command_line::get_arg(vm, opts.stagenet); + const network_type nettype = testnet ? TESTNET : stagenet ? STAGENET : MAINNET; const bool restricted = command_line::get_arg(vm, opts.restricted); auto daemon_address = command_line::get_arg(vm, opts.daemon_address); @@ -224,13 +225,13 @@ std::unique_ptr make_basic(const boost::program_options::variabl if (!daemon_port) { - daemon_port = testnet ? config::testnet::RPC_DEFAULT_PORT : stagenet ? config::stagenet::RPC_DEFAULT_PORT : config::RPC_DEFAULT_PORT; + daemon_port = get_config(nettype).RPC_DEFAULT_PORT; } if (daemon_address.empty()) daemon_address = std::string("http://") + daemon_host + ":" + std::to_string(daemon_port); - std::unique_ptr wallet(new tools::wallet2(testnet ? TESTNET : stagenet ? STAGENET : MAINNET, restricted)); + std::unique_ptr wallet(new tools::wallet2(nettype, restricted)); wallet->init(std::move(daemon_address), std::move(login)); boost::filesystem::path ringdb_path = command_line::get_arg(vm, opts.shared_ringdb_dir); wallet->set_ring_database(ringdb_path.string()); @@ -10545,17 +10546,6 @@ uint64_t wallet2::get_segregation_fork_height() const } //---------------------------------------------------------------------------------------------------- void wallet2::generate_genesis(cryptonote::block& b) const { - if (m_nettype == TESTNET) - { - cryptonote::generate_genesis_block(b, config::testnet::GENESIS_TX, config::testnet::GENESIS_NONCE); - } - else if (m_nettype == STAGENET) - { - cryptonote::generate_genesis_block(b, config::stagenet::GENESIS_TX, config::stagenet::GENESIS_NONCE); - } - else - { - cryptonote::generate_genesis_block(b, config::GENESIS_TX, config::GENESIS_NONCE); - } + cryptonote::generate_genesis_block(b, get_config(m_nettype).GENESIS_TX, get_config(m_nettype).GENESIS_NONCE); } } From 0cf80baea4c3aafe4480a7546c47041bd3584512 Mon Sep 17 00:00:00 2001 From: stoffu Date: Mon, 11 Jun 2018 12:43:18 +0900 Subject: [PATCH 0084/1404] net_node: resolve host for node addresses given via command line flags --- src/p2p/net_node.inl | 56 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 43 insertions(+), 13 deletions(-) diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index 58034f191da..501be38944e 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -62,6 +62,7 @@ namespace nodetool { + inline bool append_net_address(std::vector & seed_nodes, std::string const & addr, uint16_t default_port); //----------------------------------------------------------------------------------- template void node_server::init_options(boost::program_options::options_description& desc) @@ -275,8 +276,20 @@ namespace nodetool pe.id = crypto::rand(); const uint16_t default_port = cryptonote::get_config(m_nettype).P2P_DEFAULT_PORT; bool r = parse_peer_from_string(pe.adr, pr_str, default_port); - CHECK_AND_ASSERT_MES(r, false, "Failed to parse address from string: " << pr_str); - m_command_line_peers.push_back(pe); + if (r) + { + m_command_line_peers.push_back(pe); + continue; + } + std::vector resolved_addrs; + r = append_net_address(resolved_addrs, pr_str, default_port); + CHECK_AND_ASSERT_MES(r, false, "Failed to parse or resolve address from string: " << pr_str); + for (const epee::net_utils::network_address& addr : resolved_addrs) + { + pe.id = crypto::rand(); + pe.adr = addr; + m_command_line_peers.push_back(pe); + } } } @@ -327,24 +340,31 @@ namespace nodetool return true; } //----------------------------------------------------------------------------------- - inline void append_net_address( + inline bool append_net_address( std::vector & seed_nodes , std::string const & addr + , uint16_t default_port ) { using namespace boost::asio; + std::string host = addr; + std::string port = std::to_string(default_port); size_t pos = addr.find_last_of(':'); - CHECK_AND_ASSERT_MES_NO_RET(std::string::npos != pos && addr.length() - 1 != pos && 0 != pos, "Failed to parse seed address from string: '" << addr << '\''); - std::string host = addr.substr(0, pos); - std::string port = addr.substr(pos + 1); + if (std::string::npos != pos) + { + CHECK_AND_ASSERT_MES(addr.length() - 1 != pos && 0 != pos, false, "Failed to parse seed address from string: '" << addr << '\''); + host = addr.substr(0, pos); + port = addr.substr(pos + 1); + } + MINFO("Resolving node address: host=" << host << ", port=" << port); io_service io_srv; ip::tcp::resolver resolver(io_srv); ip::tcp::resolver::query query(host, port, boost::asio::ip::tcp::resolver::query::canonical_name); boost::system::error_code ec; ip::tcp::resolver::iterator i = resolver.resolve(query, ec); - CHECK_AND_ASSERT_MES_NO_RET(!ec, "Failed to resolve host name '" << host << "': " << ec.message() << ':' << ec.value()); + CHECK_AND_ASSERT_MES(!ec, false, "Failed to resolve host name '" << host << "': " << ec.message() << ':' << ec.value()); ip::tcp::resolver::iterator iend; for (; i != iend; ++i) @@ -354,14 +374,14 @@ namespace nodetool { epee::net_utils::network_address na{epee::net_utils::ipv4_network_address{boost::asio::detail::socket_ops::host_to_network_long(endpoint.address().to_v4().to_ulong()), endpoint.port()}}; seed_nodes.push_back(na); - MINFO("Added seed node: " << na.str()); + MINFO("Added node: " << na.str()); } else { MWARNING("IPv6 unsupported, skip '" << host << "' -> " << endpoint.address().to_v6().to_string(ec)); - throw std::runtime_error("IPv6 unsupported"); } } + return true; } //----------------------------------------------------------------------------------- @@ -505,7 +525,7 @@ namespace nodetool for (const auto& full_addr : full_addrs) { MDEBUG("Seed node: " << full_addr); - append_net_address(m_seed_nodes, full_addr); + append_net_address(m_seed_nodes, full_addr, cryptonote::get_config(m_nettype).P2P_DEFAULT_PORT); } MDEBUG("Number of seed nodes: " << m_seed_nodes.size()); @@ -1150,7 +1170,7 @@ namespace nodetool for (const auto &peer: get_seed_nodes(m_nettype)) { MDEBUG("Fallback seed node: " << peer); - append_net_address(m_seed_nodes, peer); + append_net_address(m_seed_nodes, peer, cryptonote::get_config(m_nettype).P2P_DEFAULT_PORT); } fallback_nodes_added = true; // continue for another few cycles @@ -1809,8 +1829,18 @@ namespace nodetool epee::net_utils::network_address na = AUTO_VAL_INIT(na); const uint16_t default_port = cryptonote::get_config(m_nettype).P2P_DEFAULT_PORT; bool r = parse_peer_from_string(na, pr_str, default_port); - CHECK_AND_ASSERT_MES(r, false, "Failed to parse address from string: " << pr_str); - container.push_back(na); + if (r) + { + container.push_back(na); + continue; + } + std::vector resolved_addrs; + r = append_net_address(resolved_addrs, pr_str, default_port); + CHECK_AND_ASSERT_MES(r, false, "Failed to parse or resolve address from string: " << pr_str); + for (const epee::net_utils::network_address& addr : resolved_addrs) + { + container.push_back(addr); + } } return true; From b628503bad459dc0812458f8e7904f34c8968705 Mon Sep 17 00:00:00 2001 From: Jean Pierre Dudey Date: Mon, 11 Jun 2018 09:36:05 -0400 Subject: [PATCH 0085/1404] Remove old logic saved in comments. Signed-off-by: Jean Pierre Dudey --- src/cryptonote_core/blockchain.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index fbb2a222340..aa901dcb6b0 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -2015,9 +2015,9 @@ bool Blockchain::find_blockchain_supplement(const std::list& qbloc // make sure the request includes at least the genesis block, otherwise // how can we expect to sync from the client that the block list came from? - if(!qblock_ids.size() /*|| !req.m_total_height*/) + if(!qblock_ids.size()) { - MCERROR("net.p2p", "Client sent wrong NOTIFY_REQUEST_CHAIN: m_block_ids.size()=" << qblock_ids.size() << /*", m_height=" << req.m_total_height <<*/ ", dropping connection"); + MCERROR("net.p2p", "Client sent wrong NOTIFY_REQUEST_CHAIN: m_block_ids.size()=" << qblock_ids.size() << ", dropping connection"); return false; } From 2d10830cfb125267abf2c2b3898d7bfa7cd19563 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 11 Jun 2018 17:54:58 +0100 Subject: [PATCH 0086/1404] db_ldmb: print db version when complaining about incompatibility --- src/blockchain_db/lmdb/db_lmdb.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp index fe31321f3be..367bfa49e79 100644 --- a/src/blockchain_db/lmdb/db_lmdb.cpp +++ b/src/blockchain_db/lmdb/db_lmdb.cpp @@ -1306,20 +1306,21 @@ void BlockchainLMDB::open(const std::string& filename, const int db_flags) auto get_result = mdb_get(txn, m_properties, &k, &v); if(get_result == MDB_SUCCESS) { - if (*(const uint32_t*)v.mv_data > VERSION) + const uint32_t db_version = *(const uint32_t*)v.mv_data; + if (db_version > VERSION) { - MWARNING("Existing lmdb database was made by a later version. We don't know how it will change yet."); + MWARNING("Existing lmdb database was made by a later version (" << db_version << "). We don't know how it will change yet."); compatible = false; } #if VERSION > 0 - else if (*(const uint32_t*)v.mv_data < VERSION) + else if (db_version < VERSION) { // Note that there was a schema change within version 0 as well. // See commit e5d2680094ee15889934fe28901e4e133cda56f2 2015/07/10 // We don't handle the old format previous to that commit. txn.commit(); m_open = true; - migrate(*(const uint32_t *)v.mv_data); + migrate(db_version); return; } #endif From 25c15dca5c0ea58f5033309372eb154c48c81a3c Mon Sep 17 00:00:00 2001 From: stoffu Date: Tue, 12 Jun 2018 12:06:51 +0900 Subject: [PATCH 0087/1404] wallet2: use correct fee for split txes --- src/wallet/wallet2.cpp | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index d2db45f12c8..e4458fe26f8 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -7356,8 +7356,11 @@ std::vector wallet2::create_transactions_2(std::vector> outs; + TX() : bytes(0), needed_fee(0) {} + void add(const account_public_address &addr, bool is_subaddress, uint64_t amount, unsigned int original_output_index, bool merge_destinations) { if (merge_destinations) { @@ -7750,6 +7753,7 @@ std::vector wallet2::create_transactions_2(std::vector wallet2::create_transactions_2(std::vector> &outs, */ unlock_time, /* CONST uint64_t unlock_time, */ - needed_fee, /* CONST uint64_t fee, */ + tx.needed_fee, /* CONST uint64_t fee, */ extra, /* const std::vector& extra, */ test_tx, /* OUT cryptonote::transaction& tx, */ test_ptx, /* OUT cryptonote::transaction& tx, */ @@ -7812,7 +7816,7 @@ std::vector wallet2::create_transactions_2(std::vector wallet2::create_transactions_2(std::vector wallet2::create_transactions_from(const crypton cryptonote::transaction tx; pending_tx ptx; size_t bytes; + uint64_t needed_fee; std::vector> outs; + + TX() : bytes(0), needed_fee(0) {} }; std::vector txes; uint64_t needed_fee, available_for_fee = 0; @@ -8030,6 +8037,7 @@ std::vector wallet2::create_transactions_from(const crypton tx.ptx = test_ptx; tx.bytes = txBlob.size(); tx.outs = outs; + tx.needed_fee = needed_fee; accumulated_fee += test_ptx.fee; accumulated_change += test_ptx.change_dts.amount; if (!unused_transfers_indices.empty() || !unused_dust_indices.empty()) @@ -8050,10 +8058,10 @@ std::vector wallet2::create_transactions_from(const crypton cryptonote::transaction test_tx; pending_tx test_ptx; if (use_rct) { - transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, tx.outs, unlock_time, needed_fee, extra, + transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, tx.outs, unlock_time, tx.needed_fee, extra, test_tx, test_ptx, bulletproof); } else { - transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, tx.outs, unlock_time, needed_fee, extra, + transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, tx.outs, unlock_time, tx.needed_fee, extra, detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx); } auto txBlob = t_serializable_object_to_blob(test_ptx.tx); @@ -8070,7 +8078,7 @@ std::vector wallet2::create_transactions_from(const crypton for (size_t idx: tx.selected_transfers) tx_money += m_transfers[idx].amount(); LOG_PRINT_L1(" Transaction " << (1+std::distance(txes.begin(), i)) << "/" << txes.size() << - ": " << get_size_string(tx.bytes) << ", sending " << print_money(tx_money) << " in " << tx.selected_transfers.size() << + " " << get_transaction_hash(tx.ptx.tx) << ": " << get_size_string(tx.bytes) << ", sending " << print_money(tx_money) << " in " << tx.selected_transfers.size() << " outputs to " << tx.dsts.size() << " destination(s), including " << print_money(tx.ptx.fee) << " fee, " << print_money(tx.ptx.change_dts.amount) << " change"); ptx_vector.push_back(tx.ptx); From 0a5292c3394a4bb4368b59c27815a8fa90c5fe18 Mon Sep 17 00:00:00 2001 From: stoffu Date: Wed, 13 Jun 2018 01:06:44 +0900 Subject: [PATCH 0088/1404] wallet2: use decoded amount when reporting repeated output key --- src/wallet/wallet2.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index d2db45f12c8..3b5b2e2ca07 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -1303,20 +1303,20 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote m_callback->on_money_received(height, txid, tx, td.m_amount, td.m_subaddr_index); } } - else if (m_transfers[kit->second].m_spent || m_transfers[kit->second].amount() >= tx.vout[o].amount) + else if (m_transfers[kit->second].m_spent || m_transfers[kit->second].amount() >= tx_scan_info[o].amount) { LOG_ERROR("Public key " << epee::string_tools::pod_to_hex(kit->first) - << " from received " << print_money(tx.vout[o].amount) << " output already exists with " + << " from received " << print_money(tx_scan_info[o].amount) << " output already exists with " << (m_transfers[kit->second].m_spent ? "spent" : "unspent") << " " - << print_money(m_transfers[kit->second].amount()) << ", received output ignored"); + << print_money(m_transfers[kit->second].amount()) << " in tx " << m_transfers[kit->second].m_txid << ", received output ignored"); } else { LOG_ERROR("Public key " << epee::string_tools::pod_to_hex(kit->first) - << " from received " << print_money(tx.vout[o].amount) << " output already exists with " + << " from received " << print_money(tx_scan_info[o].amount) << " output already exists with " << print_money(m_transfers[kit->second].amount()) << ", replacing with new output"); // The new larger output replaced a previous smaller one - tx_money_got_in_outs[tx_scan_info[o].received->index] -= tx.vout[o].amount; + tx_money_got_in_outs[tx_scan_info[o].received->index] -= tx_scan_info[o].amount; if (!pool) { From 798dfcfe7993a3e114d9917cdaa4215feaa0125a Mon Sep 17 00:00:00 2001 From: stoffu Date: Tue, 12 Jun 2018 12:44:40 +0900 Subject: [PATCH 0089/1404] wallet: allow unspendable unmixable outputs to be discarded --- src/simplewallet/simplewallet.cpp | 14 ++++++++++++++ src/wallet/wallet2.cpp | 10 ++++++++++ src/wallet/wallet2.h | 1 + 3 files changed, 25 insertions(+) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 4020793e96d..747bfe3b1c1 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -4816,6 +4816,20 @@ bool simple_wallet::sweep_unmixable(const std::vector &args_) commit_or_save(ptx_vector, m_do_not_relay); } } + catch (const tools::error::not_enough_unlocked_money& e) + { + fail_msg_writer() << tr("Not enough money in unlocked balance"); + std::string accepted = input_line((boost::format(tr("Discarding %s of unmixable outputs that cannot be spent, which can be undone by \"rescan_spent\". Is this okay? (Y/Yes/N/No): ")) % print_money(e.available())).str()); + if (std::cin.eof()) + return true; + if (command_line::is_yes(accepted)) + { + try + { + m_wallet->discard_unmixable_outputs(is_daemon_trusted()); + } catch (...) {} + } + } catch (const std::exception &e) { handle_transfer_exception(std::current_exception(), is_daemon_trusted()); diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index d2db45f12c8..894049238cd 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -8257,6 +8257,16 @@ std::vector wallet2::create_unmixable_sweep_transactions(bo return create_transactions_from(m_account_public_address, false, unmixable_transfer_outputs, unmixable_dust_outputs, 0 /*fake_outs_count */, 0 /* unlock_time */, 1 /*priority */, std::vector(), trusted_daemon); } +//---------------------------------------------------------------------------------------------------- +void wallet2::discard_unmixable_outputs(bool trusted_daemon) +{ + // may throw + std::vector unmixable_outputs = select_available_unmixable_outputs(trusted_daemon); + for (size_t idx : unmixable_outputs) + { + m_transfers[idx].m_spent = true; + } +} bool wallet2::get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key, std::vector &additional_tx_keys) const { diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 40f6e08d97b..2f8556dda82 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -707,6 +707,7 @@ namespace tools bool sign_multisig_tx(multisig_tx_set &exported_txs, std::vector &txids); bool sign_multisig_tx_to_file(multisig_tx_set &exported_txs, const std::string &filename, std::vector &txids); std::vector create_unmixable_sweep_transactions(bool trusted_daemon); + void discard_unmixable_outputs(bool trusted_daemon); bool check_connection(uint32_t *version = NULL, uint32_t timeout = 200000); void get_transfers(wallet2::transfer_container& incoming_transfers) const; void get_payments(const crypto::hash& payment_id, std::list& payments, uint64_t min_height = 0, const boost::optional& subaddr_account = boost::none, const std::set& subaddr_indices = {}) const; From 63d0ab09b5445ce5d1dfa84d6600e61deef70601 Mon Sep 17 00:00:00 2001 From: stoffu Date: Fri, 1 Jun 2018 12:29:26 +0900 Subject: [PATCH 0090/1404] mlog: --max-log-files to set the max number of rotated log files --- contrib/epee/include/misc_log_ex.h | 3 +- contrib/epee/src/mlog.cpp | 53 ++++++++++++++++++++++++++++-- src/daemon/command_line_args.h | 5 +++ src/daemon/main.cpp | 3 +- src/wallet/wallet_args.cpp | 4 ++- 5 files changed, 63 insertions(+), 5 deletions(-) diff --git a/contrib/epee/include/misc_log_ex.h b/contrib/epee/include/misc_log_ex.h index 35ec0950bc8..530f8e63681 100644 --- a/contrib/epee/include/misc_log_ex.h +++ b/contrib/epee/include/misc_log_ex.h @@ -34,6 +34,7 @@ #define MONERO_DEFAULT_LOG_CATEGORY "default" #define MAX_LOG_FILE_SIZE 104850000 // 100 MB - 7600 bytes +#define MAX_LOG_FILES 50 #define MCFATAL(cat,x) CLOG(FATAL,cat) << x #define MCERROR(cat,x) CLOG(ERROR,cat) << x @@ -105,7 +106,7 @@ #endif std::string mlog_get_default_log_path(const char *default_filename); -void mlog_configure(const std::string &filename_base, bool console, const std::size_t max_log_file_size = MAX_LOG_FILE_SIZE); +void mlog_configure(const std::string &filename_base, bool console, const std::size_t max_log_file_size = MAX_LOG_FILE_SIZE, const std::size_t max_log_files = MAX_LOG_FILES); void mlog_set_categories(const char *categories); std::string mlog_get_categories(); void mlog_set_log_level(int level); diff --git a/contrib/epee/src/mlog.cpp b/contrib/epee/src/mlog.cpp index fb0b4ac2bb9..0759f5d349e 100644 --- a/contrib/epee/src/mlog.cpp +++ b/contrib/epee/src/mlog.cpp @@ -116,7 +116,7 @@ static const char *get_default_categories(int level) return categories; } -void mlog_configure(const std::string &filename_base, bool console, const std::size_t max_log_file_size) +void mlog_configure(const std::string &filename_base, bool console, const std::size_t max_log_file_size, const std::size_t max_log_files) { el::Configurations c; c.setGlobally(el::ConfigurationType::Filename, filename_base); @@ -134,9 +134,58 @@ void mlog_configure(const std::string &filename_base, bool console, const std::s el::Loggers::addFlag(el::LoggingFlag::DisableApplicationAbortOnFatalLog); el::Loggers::addFlag(el::LoggingFlag::ColoredTerminalOutput); el::Loggers::addFlag(el::LoggingFlag::StrictLogFileSizeCheck); - el::Helpers::installPreRollOutCallback([filename_base](const char *name, size_t){ + el::Helpers::installPreRollOutCallback([filename_base, max_log_files](const char *name, size_t){ std::string rname = generate_log_filename(filename_base.c_str()); rename(name, rname.c_str()); + if (max_log_files != 0) + { + std::vector found_files; + const boost::filesystem::directory_iterator end_itr; + for (boost::filesystem::directory_iterator iter(boost::filesystem::path(filename_base).parent_path()); iter != end_itr; ++iter) + { + const std::string filename = iter->path().string(); + if (filename.size() >= filename_base.size() && std::memcmp(filename.data(), filename_base.data(), filename_base.size()) == 0) + { + found_files.push_back(iter->path()); + } + } + if (found_files.size() >= max_log_files) + { + std::sort(found_files.begin(), found_files.end(), [](const boost::filesystem::path &a, const boost::filesystem::path &b) { + boost::system::error_code ec; + std::time_t ta = boost::filesystem::last_write_time(boost::filesystem::path(a), ec); + if (ec) + { + MERROR("Failed to get timestamp from " << a << ": " << ec); + ta = std::time(nullptr); + } + std::time_t tb = boost::filesystem::last_write_time(boost::filesystem::path(b), ec); + if (ec) + { + MERROR("Failed to get timestamp from " << b << ": " << ec); + tb = std::time(nullptr); + } + static_assert(std::is_integral(), "bad time_t"); + return ta < tb; + }); + for (size_t i = 0; i <= found_files.size() - max_log_files; ++i) + { + try + { + boost::system::error_code ec; + boost::filesystem::remove(found_files[i], ec); + if (ec) + { + MERROR("Failed to remove " << found_files[i] << ": " << ec); + } + } + catch (const std::exception &e) + { + MERROR("Failed to remove " << found_files[i] << ": " << e.what()); + } + } + } + } }); mlog_set_common_prefix(); const char *monero_log = getenv("MONERO_LOGS"); diff --git a/src/daemon/command_line_args.h b/src/daemon/command_line_args.h index 4673590aa63..cba71bf3b69 100644 --- a/src/daemon/command_line_args.h +++ b/src/daemon/command_line_args.h @@ -72,6 +72,11 @@ namespace daemon_args , "Specify maximum log file size [B]" , MAX_LOG_FILE_SIZE }; + const command_line::arg_descriptor arg_max_log_files = { + "max-log-files" + , "Specify maximum number of rotated log files to be saved (no limit by setting to 0)" + , MAX_LOG_FILES + }; const command_line::arg_descriptor arg_log_level = { "log-level" , "" diff --git a/src/daemon/main.cpp b/src/daemon/main.cpp index 49494e8899c..822be0d70c3 100644 --- a/src/daemon/main.cpp +++ b/src/daemon/main.cpp @@ -84,6 +84,7 @@ int main(int argc, char const * argv[]) command_line::add_arg(core_settings, daemon_args::arg_log_file); command_line::add_arg(core_settings, daemon_args::arg_log_level); command_line::add_arg(core_settings, daemon_args::arg_max_log_file_size); + command_line::add_arg(core_settings, daemon_args::arg_max_log_files); command_line::add_arg(core_settings, daemon_args::arg_max_concurrency); command_line::add_arg(core_settings, daemon_args::arg_zmq_rpc_bind_ip); command_line::add_arg(core_settings, daemon_args::arg_zmq_rpc_bind_port); @@ -203,7 +204,7 @@ int main(int argc, char const * argv[]) if (!command_line::is_arg_defaulted(vm, daemon_args::arg_log_file)) log_file_path = command_line::get_arg(vm, daemon_args::arg_log_file); log_file_path = bf::absolute(log_file_path, relative_path_base); - mlog_configure(log_file_path.string(), true, command_line::get_arg(vm, daemon_args::arg_max_log_file_size)); + mlog_configure(log_file_path.string(), true, command_line::get_arg(vm, daemon_args::arg_max_log_file_size), command_line::get_arg(vm, daemon_args::arg_max_log_files)); // Set log level if (!command_line::is_arg_defaulted(vm, daemon_args::arg_log_level)) diff --git a/src/wallet/wallet_args.cpp b/src/wallet/wallet_args.cpp index 6311e770085..91aad9c6e24 100644 --- a/src/wallet/wallet_args.cpp +++ b/src/wallet/wallet_args.cpp @@ -101,6 +101,7 @@ namespace wallet_args const command_line::arg_descriptor arg_log_level = {"log-level", "0-4 or categories", ""}; const command_line::arg_descriptor arg_max_log_file_size = {"max-log-file-size", "Specify maximum log file size [B]", MAX_LOG_FILE_SIZE}; + const command_line::arg_descriptor arg_max_log_files = {"max-log-files", "Specify maximum number of rotated log files to be saved (no limit by setting to 0)", MAX_LOG_FILES}; const command_line::arg_descriptor arg_max_concurrency = {"max-concurrency", wallet_args::tr("Max number of threads to use for a parallel job"), DEFAULT_MAX_CONCURRENCY}; const command_line::arg_descriptor arg_log_file = {"log-file", wallet_args::tr("Specify log file"), ""}; const command_line::arg_descriptor arg_config_file = {"config-file", wallet_args::tr("Config file"), "", true}; @@ -119,6 +120,7 @@ namespace wallet_args command_line::add_arg(desc_params, arg_log_file); command_line::add_arg(desc_params, arg_log_level); command_line::add_arg(desc_params, arg_max_log_file_size); + command_line::add_arg(desc_params, arg_max_log_files); command_line::add_arg(desc_params, arg_max_concurrency); command_line::add_arg(desc_params, arg_config_file); @@ -180,7 +182,7 @@ namespace wallet_args log_path = command_line::get_arg(vm, arg_log_file); else log_path = mlog_get_default_log_path(default_log_name); - mlog_configure(log_path, log_to_console, command_line::get_arg(vm, arg_max_log_file_size)); + mlog_configure(log_path, log_to_console, command_line::get_arg(vm, arg_max_log_file_size), command_line::get_arg(vm, arg_max_log_files)); if (!command_line::is_arg_defaulted(vm, arg_log_level)) { mlog_set_log(command_line::get_arg(vm, arg_log_level).c_str()); From b43b9a1304f1fb45509caf0ca0c35b60f8fd4682 Mon Sep 17 00:00:00 2001 From: jcktm Date: Tue, 17 Apr 2018 19:30:18 +1000 Subject: [PATCH 0091/1404] wallet-rpc: added IPs to error logging in simple_http_connection_handler --- .../epee/include/net/http_protocol_handler.h | 9 +++---- .../include/net/http_protocol_handler.inl | 27 ++++++++++--------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/contrib/epee/include/net/http_protocol_handler.h b/contrib/epee/include/net/http_protocol_handler.h index e602fac2b6b..1780f23935c 100644 --- a/contrib/epee/include/net/http_protocol_handler.h +++ b/contrib/epee/include/net/http_protocol_handler.h @@ -69,7 +69,7 @@ namespace net_utils typedef t_connection_context connection_context;//t_connection_context net_utils::connection_context_base connection_context; typedef http_server_config config_type; - simple_http_connection_handler(i_service_endpoint* psnd_hndlr, config_type& config); + simple_http_connection_handler(i_service_endpoint* psnd_hndlr, config_type& config, t_connection_context& conn_context); virtual ~simple_http_connection_handler(){} bool release_protocol() @@ -144,6 +144,7 @@ namespace net_utils size_t m_newlines; protected: i_service_endpoint* m_psnd_hndlr; + t_connection_context& m_conn_context; }; template @@ -175,9 +176,8 @@ namespace net_utils typedef custum_handler_config config_type; http_custom_handler(i_service_endpoint* psnd_hndlr, config_type& config, t_connection_context& conn_context) - : simple_http_connection_handler(psnd_hndlr, config), + : simple_http_connection_handler(psnd_hndlr, config, conn_context), m_config(config), - m_conn_context(conn_context), m_auth(m_config.m_user ? http_server_auth{*m_config.m_user, config.rng} : http_server_auth{}) {} inline bool handle_request(const http_request_info& query_info, http_response_info& response) @@ -197,7 +197,7 @@ namespace net_utils response.m_response_comment = "OK"; response.m_body.clear(); - return m_config.m_phandler->handle_http_request(query_info, response, m_conn_context); + return m_config.m_phandler->handle_http_request(query_info, response, this->m_conn_context); } virtual bool thread_init() @@ -219,7 +219,6 @@ namespace net_utils private: //simple_http_connection_handler::config_type m_stub_config; config_type& m_config; - t_connection_context& m_conn_context; http_server_auth m_auth; }; } diff --git a/contrib/epee/include/net/http_protocol_handler.inl b/contrib/epee/include/net/http_protocol_handler.inl index f1da5067a9e..b35bcb67028 100644 --- a/contrib/epee/include/net/http_protocol_handler.inl +++ b/contrib/epee/include/net/http_protocol_handler.inl @@ -196,16 +196,17 @@ namespace net_utils //-------------------------------------------------------------------------------------------- template - simple_http_connection_handler::simple_http_connection_handler(i_service_endpoint* psnd_hndlr, config_type& config): + simple_http_connection_handler::simple_http_connection_handler(i_service_endpoint* psnd_hndlr, config_type& config, t_connection_context& conn_context): m_state(http_state_retriving_comand_line), m_body_transfer_type(http_body_transfer_undefined), - m_is_stop_handling(false), + m_is_stop_handling(false), m_len_summary(0), m_len_remain(0), - m_config(config), + m_config(config), m_want_close(false), m_newlines(0), - m_psnd_hndlr(psnd_hndlr) + m_psnd_hndlr(psnd_hndlr), + m_conn_context(conn_context) { } @@ -281,7 +282,7 @@ namespace net_utils m_is_stop_handling = true; if(m_cache.size() > HTTP_MAX_URI_LEN) { - LOG_ERROR("simple_http_connection_handler::handle_buff_out: Too long URI line"); + LOG_ERROR_CC(m_conn_context, "simple_http_connection_handler::handle_buff_out: Too long URI line"); m_state = http_state_error; return false; } @@ -295,7 +296,7 @@ namespace net_utils m_is_stop_handling = true; if(m_cache.size() > HTTP_MAX_HEADER_LEN) { - LOG_ERROR("simple_http_connection_handler::handle_buff_in: Too long header area"); + LOG_ERROR_CC(m_conn_context, "simple_http_connection_handler::handle_buff_in: Too long header area"); m_state = http_state_error; return false; } @@ -310,10 +311,10 @@ namespace net_utils case http_state_connection_close: return false; default: - LOG_ERROR("simple_http_connection_handler::handle_char_out: Wrong state: " << m_state); + LOG_ERROR_CC(m_conn_context, "simple_http_connection_handler::handle_char_out: Wrong state: " << m_state); return false; case http_state_error: - LOG_ERROR("simple_http_connection_handler::handle_char_out: Error state!!!"); + LOG_ERROR_CC(m_conn_context, "simple_http_connection_handler::handle_char_out: Error state!!!"); return false; } @@ -375,7 +376,7 @@ namespace net_utils }else { m_state = http_state_error; - LOG_ERROR("simple_http_connection_handler::handle_invoke_query_line(): Failed to match first line: " << m_cache); + LOG_ERROR_CC(m_conn_context, "simple_http_connection_handler::handle_invoke_query_line(): Failed to match first line: " << m_cache); return false; } @@ -406,7 +407,7 @@ namespace net_utils if(!parse_cached_header(m_query_info.m_header_info, m_cache, pos)) { - LOG_ERROR("simple_http_connection_handler::analize_cached_request_header_and_invoke_state(): failed to anilize request header: " << m_cache); + LOG_ERROR_CC(m_conn_context, "simple_http_connection_handler::analize_cached_request_header_and_invoke_state(): failed to anilize request header: " << m_cache); m_state = http_state_error; return false; } @@ -422,7 +423,7 @@ namespace net_utils m_body_transfer_type = http_body_transfer_measure; if(!get_len_from_content_lenght(m_query_info.m_header_info.m_content_length, m_len_summary)) { - LOG_ERROR("simple_http_connection_handler::analize_cached_request_header_and_invoke_state(): Failed to get_len_from_content_lenght();, m_query_info.m_content_length="<::analize_cached_request_header_and_invoke_state(): Failed to get_len_from_content_lenght();, m_query_info.m_content_length="<::handle_retriving_query_body(): Unexpected m_body_query_type state:" << m_body_transfer_type); + LOG_ERROR_CC(m_conn_context, "simple_http_connection_handler::handle_retriving_query_body(): Unexpected m_body_query_type state:" << m_body_transfer_type); m_state = http_state_error; return false; } @@ -536,7 +537,7 @@ namespace net_utils body_info.m_etc_fields.push_back(std::pair(result[field_etc_name], result[field_val])); else { - LOG_ERROR("simple_http_connection_handler::parse_cached_header() not matched last entry in:"<::parse_cached_header() not matched last entry in:" << m_cache_to_process); } it_current_bound = result[(int)result.size()-1]. first; From 8db23df5810627661818fa378fa51f37208e0792 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 13 Jun 2018 12:16:07 +0100 Subject: [PATCH 0092/1404] wallet: on first refresh, start off with a quantized height for privacy reasons, so an untrusted node can't easily track wallets from IP address to IP address, etc. The granularity is 1024 blocks, which is about a day and a half. --- src/simplewallet/simplewallet.cpp | 4 +-- src/wallet/wallet2.cpp | 32 +++++++++++-------- src/wallet/wallet2.h | 11 ++++--- src/wallet/wallet_rpc_server.cpp | 4 +-- .../transactions_flow_test.cpp | 14 ++++---- 5 files changed, 36 insertions(+), 29 deletions(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 4020793e96d..af2afd12e44 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -3885,7 +3885,7 @@ bool simple_wallet::refresh_main(uint64_t start_height, bool reset, bool is_init { m_in_manual_refresh.store(true, std::memory_order_relaxed); epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){m_in_manual_refresh.store(false, std::memory_order_relaxed);}); - m_wallet->refresh(start_height, fetched_blocks); + m_wallet->refresh(is_daemon_trusted(), start_height, fetched_blocks); ok = true; // Clear line "Height xxx of xxx" std::cout << "\r \r"; @@ -6393,7 +6393,7 @@ void simple_wallet::wallet_idle_thread() { uint64_t fetched_blocks; if (try_connect_to_daemon(true)) - m_wallet->refresh(0, fetched_blocks); + m_wallet->refresh(is_daemon_trusted(), 0, fetched_blocks); } catch(...) {} m_auto_refresh_refreshing = false; diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index d2db45f12c8..4840137af65 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -115,6 +115,8 @@ using namespace cryptonote; #define STAGENET_SEGREGATION_FORK_HEIGHT 1000000 #define SEGREGATION_FORK_VICINITY 1500 /* blocks */ +#define FIRST_REFRESH_GRANULARITY 1024 + static const std::string MULTISIG_SIGNATURE_MAGIC = "SigMultisigPkV1"; @@ -654,6 +656,7 @@ wallet2::wallet2(network_type nettype, bool restricted): m_default_priority(0), m_refresh_type(RefreshOptimizeCoinbase), m_auto_refresh(true), + m_first_refresh_done(false), m_refresh_from_block_height(0), m_explicit_refresh_from_block_height(true), m_confirm_missing_payment_id(true), @@ -1608,11 +1611,12 @@ void wallet2::process_new_blockchain_entry(const cryptonote::block& b, const cry m_callback->on_new_block(height, b); } //---------------------------------------------------------------------------------------------------- -void wallet2::get_short_chain_history(std::list& ids) const +void wallet2::get_short_chain_history(std::list& ids, uint64_t granularity) const { size_t i = 0; size_t current_multiplier = 1; - size_t sz = m_blockchain.size() - m_blockchain.offset(); + size_t blockchain_size = std::max(m_blockchain.size() / granularity * granularity, m_blockchain.offset()); + size_t sz = blockchain_size - m_blockchain.offset(); if(!sz) { ids.push_back(m_blockchain.genesis()); @@ -1818,16 +1822,16 @@ void wallet2::process_blocks(uint64_t start_height, const std::list &short_chain_history, const std::list &prev_blocks, std::list &blocks, std::vector &o_indices, bool &error) @@ -2196,7 +2200,7 @@ bool wallet2::delete_address_book_row(std::size_t row_id) { } //---------------------------------------------------------------------------------------------------- -void wallet2::refresh(uint64_t start_height, uint64_t & blocks_fetched, bool& received_money) +void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blocks_fetched, bool& received_money) { if(m_light_wallet) { @@ -2245,7 +2249,7 @@ void wallet2::refresh(uint64_t start_height, uint64_t & blocks_fetched, bool& re bool refreshed = false; // pull the first set of blocks - get_short_chain_history(short_chain_history); + get_short_chain_history(short_chain_history, (m_first_refresh_done || trusted_daemon) ? 1 : FIRST_REFRESH_GRANULARITY); m_run.store(true, std::memory_order_relaxed); if (start_height > m_blockchain.size() || m_refresh_from_block_height > m_blockchain.size()) { if (!start_height) @@ -2254,7 +2258,7 @@ void wallet2::refresh(uint64_t start_height, uint64_t & blocks_fetched, bool& re fast_refresh(start_height, blocks_start_height, short_chain_history); // regenerate the history now that we've got a full set of hashes short_chain_history.clear(); - get_short_chain_history(short_chain_history); + get_short_chain_history(short_chain_history, (m_first_refresh_done || trusted_daemon) ? 1 : FIRST_REFRESH_GRANULARITY); start_height = 0; // and then fall through to regular refresh processing } @@ -2334,14 +2338,16 @@ void wallet2::refresh(uint64_t start_height, uint64_t & blocks_fetched, bool& re LOG_PRINT_L1("Failed to check pending transactions"); } + m_first_refresh_done = true; + LOG_PRINT_L1("Refresh done, blocks received: " << blocks_fetched << ", balance (all accounts): " << print_money(balance_all()) << ", unlocked: " << print_money(unlocked_balance_all())); } //---------------------------------------------------------------------------------------------------- -bool wallet2::refresh(uint64_t & blocks_fetched, bool& received_money, bool& ok) +bool wallet2::refresh(bool trusted_daemon, uint64_t & blocks_fetched, bool& received_money, bool& ok) { try { - refresh(0, blocks_fetched, received_money); + refresh(trusted_daemon, 0, blocks_fetched, received_money); ok = true; } catch (...) @@ -4256,7 +4262,7 @@ void wallet2::rescan_blockchain(bool refresh) m_local_bc_height = 1; if (refresh) - this->refresh(); + this->refresh(false); } //---------------------------------------------------------------------------------------------------- bool wallet2::is_transfer_unlocked(const transfer_details& td) const @@ -10118,7 +10124,7 @@ size_t wallet2::import_multisig(std::vector blobs) m_multisig_rescan_info = &info; try { - refresh(); + refresh(false); } catch (...) {} m_multisig_rescan_info = NULL; diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 40f6e08d97b..8ac6db8c1ce 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -642,10 +642,10 @@ namespace tools * \brief Tells if the wallet file is deprecated. */ bool is_deprecated() const; - void refresh(); - void refresh(uint64_t start_height, uint64_t & blocks_fetched); - void refresh(uint64_t start_height, uint64_t & blocks_fetched, bool& received_money); - bool refresh(uint64_t & blocks_fetched, bool& received_money, bool& ok); + void refresh(bool trusted_daemon); + void refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blocks_fetched); + void refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blocks_fetched, bool& received_money); + bool refresh(bool trusted_daemon, uint64_t & blocks_fetched, bool& received_money, bool& ok); void set_refresh_type(RefreshType refresh_type) { m_refresh_type = refresh_type; } RefreshType get_refresh_type() const { return m_refresh_type; } @@ -1112,7 +1112,7 @@ namespace tools void process_new_transaction(const crypto::hash &txid, const cryptonote::transaction& tx, const std::vector &o_indices, uint64_t height, uint64_t ts, bool miner_tx, bool pool, bool double_spend_seen); void process_new_blockchain_entry(const cryptonote::block& b, const cryptonote::block_complete_entry& bche, const crypto::hash& bl_id, uint64_t height, const cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices &o_indices); void detach_blockchain(uint64_t height); - void get_short_chain_history(std::list& ids) const; + void get_short_chain_history(std::list& ids, uint64_t granularity = 1) const; bool is_tx_spendtime_unlocked(uint64_t unlock_time, uint64_t block_height) const; bool clear(); void pull_blocks(uint64_t start_height, uint64_t& blocks_start_height, const std::list &short_chain_history, std::list &blocks, std::vector &o_indices); @@ -1210,6 +1210,7 @@ namespace tools uint32_t m_default_priority; RefreshType m_refresh_type; bool m_auto_refresh; + bool m_first_refresh_done; uint64_t m_refresh_from_block_height; // If m_refresh_from_block_height is explicitly set to zero we need this to differentiate it from the case that // m_refresh_from_block_height was defaulted to zero.*/ diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index dc1beef7b91..8369bec0717 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -104,7 +104,7 @@ namespace tools m_stop = false; m_net_server.add_idle_handler([this](){ try { - if (m_wallet) m_wallet->refresh(); + if (m_wallet) m_wallet->refresh(m_trusted_daemon); } catch (const std::exception& ex) { LOG_ERROR("Exception at while refreshing, what=" << ex.what()); } @@ -2986,7 +2986,7 @@ int main(int argc, char** argv) { wal->stop(); }); - wal->refresh(); + wal->refresh(command_line::get_arg(*vm, arg_trusted_daemon)); // if we ^C during potentially length load/refresh, there's no server loop yet if (quit) { diff --git a/tests/functional_tests/transactions_flow_test.cpp b/tests/functional_tests/transactions_flow_test.cpp index 55c18283ed1..c36c53b89ae 100644 --- a/tests/functional_tests/transactions_flow_test.cpp +++ b/tests/functional_tests/transactions_flow_test.cpp @@ -143,7 +143,7 @@ bool transactions_flow_test(std::string& working_folder, uint64_t blocks_fetched = 0; bool received_money; bool ok; - if(!w1.refresh(blocks_fetched, received_money, ok)) + if(!w1.refresh(true, blocks_fetched, received_money, ok)) { LOG_ERROR( "failed to refresh source wallet from " << daemon_addr_a ); return false; @@ -171,11 +171,11 @@ bool transactions_flow_test(std::string& working_folder, CHECK_AND_ASSERT_MES(daemon_rsp.status == CORE_RPC_STATUS_OK, false, "failed to getrandom_outs.bin"); //wait for money, until balance will have enough money - w1.refresh(blocks_fetched, received_money, ok); + w1.refresh(true, blocks_fetched, received_money, ok); while(w1.unlocked_balance(0) < amount_to_transfer) { misc_utils::sleep_no_w(1000); - w1.refresh(blocks_fetched, received_money, ok); + w1.refresh(true, blocks_fetched, received_money, ok); } //lets make a lot of small outs to ourselves @@ -202,7 +202,7 @@ bool transactions_flow_test(std::string& working_folder, }else { misc_utils::sleep_no_w(1000); - w1.refresh(blocks_fetched, received_money, ok); + w1.refresh(true, blocks_fetched, received_money, ok); } } //do actual transfer @@ -224,7 +224,7 @@ bool transactions_flow_test(std::string& working_folder, { misc_utils::sleep_no_w(1000); LOG_PRINT_L0("not enough money, waiting for cashback or mining"); - w1.refresh(blocks_fetched, received_money, ok); + w1.refresh(true, blocks_fetched, received_money, ok); } transaction tx; @@ -239,7 +239,7 @@ bool transactions_flow_test(std::string& working_folder, if(!do_send_money(w1, w2, mix_in_factor, amount_to_tx, tx)) { LOG_PRINT_L0("failed to transfer money, tx: " << get_transaction_hash(tx) << ", refresh and try again" ); - w1.refresh(blocks_fetched, received_money, ok); + w1.refresh(true, blocks_fetched, received_money, ok); if(!do_send_money(w1, w2, mix_in_factor, amount_to_tx, tx)) { LOG_PRINT_L0( "failed to transfer money, second chance. tx: " << get_transaction_hash(tx) << ", exit" ); @@ -264,7 +264,7 @@ bool transactions_flow_test(std::string& working_folder, misc_utils::sleep_no_w(DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN*20*1000);//wait two blocks before sync on another wallet on another daemon LOG_PRINT_L0( "refreshing..."); bool recvd_money = false; - while(w2.refresh(blocks_fetched, recvd_money, ok) && ( (blocks_fetched && recvd_money) || !blocks_fetched ) ) + while(w2.refresh(true, blocks_fetched, recvd_money, ok) && ( (blocks_fetched && recvd_money) || !blocks_fetched ) ) { misc_utils::sleep_no_w(DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN*1000);//wait two blocks before sync on another wallet on another daemon } From 149da420e9f6ca83efd471d7a52cd665e6f1cf2e Mon Sep 17 00:00:00 2001 From: stoffu Date: Thu, 14 Jun 2018 11:15:53 +0900 Subject: [PATCH 0093/1404] db_lmdb: enable batch transactions by default --- src/blockchain_db/lmdb/db_lmdb.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/blockchain_db/lmdb/db_lmdb.h b/src/blockchain_db/lmdb/db_lmdb.h index cc1b06ca02c..4580573ebca 100644 --- a/src/blockchain_db/lmdb/db_lmdb.h +++ b/src/blockchain_db/lmdb/db_lmdb.h @@ -166,7 +166,7 @@ struct mdb_txn_safe class BlockchainLMDB : public BlockchainDB { public: - BlockchainLMDB(bool batch_transactions=false); + BlockchainLMDB(bool batch_transactions=true); ~BlockchainLMDB(); virtual void open(const std::string& filename, const int mdb_flags=0); From bc443494f3265275945a04ee18a628f0e0afd1d8 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 15 Jun 2018 20:17:25 +0100 Subject: [PATCH 0094/1404] wallet2: fix out of sync account tag cache This would cause crashes when trying to tag an account that was just created --- src/wallet/wallet2.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index d2db45f12c8..2ee485ffdf1 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -944,6 +944,7 @@ void wallet2::expand_subaddresses(const cryptonote::subaddress_index& index) } m_subaddress_labels.resize(index.major + 1, {"Untitled account"}); m_subaddress_labels[index.major].resize(index.minor + 1); + get_account_tags(); } else if (m_subaddress_labels[index.major].size() <= index.minor) { From b85acb4cb87cfe35e5966d618bb890066aa4f65b Mon Sep 17 00:00:00 2001 From: Howard Chu Date: Fri, 15 Jun 2018 20:13:19 +0100 Subject: [PATCH 0095/1404] Fix RPC crashes that didn't check for an open wallet --- src/wallet/wallet_rpc_server.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index dc1beef7b91..ca171fc2d08 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -500,6 +500,7 @@ namespace tools //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_get_account_tags(const wallet_rpc::COMMAND_RPC_GET_ACCOUNT_TAGS::request& req, wallet_rpc::COMMAND_RPC_GET_ACCOUNT_TAGS::response& res, epee::json_rpc::error& er) { + if (!m_wallet) return not_open(er); const std::pair, std::vector> account_tags = m_wallet->get_account_tags(); for (const std::pair& p : account_tags.first) { @@ -518,6 +519,7 @@ namespace tools //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_tag_accounts(const wallet_rpc::COMMAND_RPC_TAG_ACCOUNTS::request& req, wallet_rpc::COMMAND_RPC_TAG_ACCOUNTS::response& res, epee::json_rpc::error& er) { + if (!m_wallet) return not_open(er); try { m_wallet->set_account_tag(req.accounts, req.tag); @@ -532,6 +534,7 @@ namespace tools //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_untag_accounts(const wallet_rpc::COMMAND_RPC_UNTAG_ACCOUNTS::request& req, wallet_rpc::COMMAND_RPC_UNTAG_ACCOUNTS::response& res, epee::json_rpc::error& er) { + if (!m_wallet) return not_open(er); try { m_wallet->set_account_tag(req.accounts, ""); @@ -546,6 +549,7 @@ namespace tools //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_set_account_tag_description(const wallet_rpc::COMMAND_RPC_SET_ACCOUNT_TAG_DESCRIPTION::request& req, wallet_rpc::COMMAND_RPC_SET_ACCOUNT_TAG_DESCRIPTION::response& res, epee::json_rpc::error& er) { + if (!m_wallet) return not_open(er); try { m_wallet->set_account_tag_description(req.tag, req.description); @@ -2054,6 +2058,7 @@ namespace tools //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_make_uri(const wallet_rpc::COMMAND_RPC_MAKE_URI::request& req, wallet_rpc::COMMAND_RPC_MAKE_URI::response& res, epee::json_rpc::error& er) { + if (!m_wallet) return not_open(er); std::string error; std::string uri = m_wallet->make_uri(req.address, req.payment_id, req.amount, req.tx_description, req.recipient_name, error); if (uri.empty()) @@ -2267,6 +2272,7 @@ namespace tools //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_stop_mining(const wallet_rpc::COMMAND_RPC_STOP_MINING::request& req, wallet_rpc::COMMAND_RPC_STOP_MINING::response& res, epee::json_rpc::error& er) { + if (!m_wallet) return not_open(er); cryptonote::COMMAND_RPC_STOP_MINING::request daemon_req; cryptonote::COMMAND_RPC_STOP_MINING::response daemon_res; bool r = m_wallet->invoke_http_json("/stop_mining", daemon_req, daemon_res); From 26c569d6f62950a69b29b2debb517cf0e6686471 Mon Sep 17 00:00:00 2001 From: Jethro Grassie Date: Wed, 9 May 2018 21:48:19 -0400 Subject: [PATCH 0096/1404] add disclaimer about 3rd party packages Demoted the 3rd party maintained package installs to after compiling from repository source and removed the broken Homebrew package instructions. --- README.md | 83 +++++++++++++++++++++++++++---------------------------- 1 file changed, 40 insertions(+), 43 deletions(-) diff --git a/README.md b/README.md index 9e28192b997..e7bac8ab9e0 100644 --- a/README.md +++ b/README.md @@ -112,49 +112,6 @@ X's indicate that these details have not been determined as of commit date. Approximately three months prior to a scheduled software upgrade, a branch from Master will be created with the new release version tag. Pull requests that address bugs should then be made to both Master and the new release branch. Pull requests that require extensive review and testing (generally, optimizations and new features) should *not* be made to the release branch. -## Installing Monero from a package - -Packages are available for - -* Ubuntu and [snap supported](https://snapcraft.io/docs/core/install) systems, via a community contributed build. - - snap install monero --beta - -Installing a snap is very quick. Snaps are secure. They are isolated with all of their dependencies. Snaps also auto update when a new version is released. - -* Arch Linux (via [AUR](https://aur.archlinux.org/)): - - Stable release: [`monero`](https://aur.archlinux.org/packages/monero) - - Bleeding edge: [`monero-git`](https://aur.archlinux.org/packages/monero-git) - -* Void Linux: - - xbps-install -S monero - -* GuixSD - - guix package -i monero - -* OS X via [Homebrew](http://brew.sh) - - brew tap sammy007/cryptonight - brew install monero --build-from-source - -* Docker - - # Build using all available cores - docker build -t monero . - - # or build using a specific number of cores (reduce RAM requirement) - docker build --build-arg NPROC=1 -t monero . - - # either run in foreground - docker run -it -v /monero/chain:/root/.bitmonero -v /monero/wallet:/wallet -p 18080:18080 monero - - # or in background - docker run -it -d -v /monero/chain:/root/.bitmonero -v /monero/wallet:/wallet -p 18080:18080 monero - -Packaging for your favorite distribution would be a welcome contribution! - ## Compiling Monero from source ### Dependencies @@ -503,6 +460,46 @@ By default, in either dynamically or statically linked builds, binaries target t * ```make release-static-win64``` builds binaries on 64-bit Windows portable across 64-bit Windows systems * ```make release-static-win32``` builds binaries on 64-bit or 32-bit Windows portable across 32-bit Windows systems +## Installing Monero from a package + +**DISCLAIMER: These packages are not part of this repository or maintained by this project's contributors, and as such, do not go through the same review process to ensure their trustworthiness and security.** + +Packages are available for + +* Ubuntu and [snap supported](https://snapcraft.io/docs/core/install) systems, via a community contributed build. + + snap install monero --beta + +Installing a snap is very quick. Snaps are secure. They are isolated with all of their dependencies. Snaps also auto update when a new version is released. + +* Arch Linux (via [AUR](https://aur.archlinux.org/)): + - Stable release: [`monero`](https://aur.archlinux.org/packages/monero) + - Bleeding edge: [`monero-git`](https://aur.archlinux.org/packages/monero-git) + +* Void Linux: + + xbps-install -S monero + +* GuixSD + + guix package -i monero + +* Docker + + # Build using all available cores + docker build -t monero . + + # or build using a specific number of cores (reduce RAM requirement) + docker build --build-arg NPROC=1 -t monero . + + # either run in foreground + docker run -it -v /monero/chain:/root/.bitmonero -v /monero/wallet:/wallet -p 18080:18080 monero + + # or in background + docker run -it -d -v /monero/chain:/root/.bitmonero -v /monero/wallet:/wallet -p 18080:18080 monero + +Packaging for your favorite distribution would be a welcome contribution! + ## Running monerod The build places the binary in `bin/` sub-directory within the build directory From 5b5828fcec3e4fe9c437e7e524c1d38bdbe454cb Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 17 Jun 2018 13:48:12 +0100 Subject: [PATCH 0097/1404] zmq_server: fix bind call when address and/or port are empty --- src/rpc/zmq_server.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/rpc/zmq_server.cpp b/src/rpc/zmq_server.cpp index 3aee8c4c7cb..edd3e666968 100644 --- a/src/rpc/zmq_server.cpp +++ b/src/rpc/zmq_server.cpp @@ -104,6 +104,10 @@ bool ZmqServer::addTCPSocket(std::string address, std::string port) rep_socket->setsockopt(ZMQ_RCVTIMEO, &DEFAULT_RPC_RECV_TIMEOUT_MS, sizeof(DEFAULT_RPC_RECV_TIMEOUT_MS)); + if (address.empty()) + address = "*"; + if (port.empty()) + port = "*"; std::string bind_address = addr_prefix + address + std::string(":") + port; rep_socket->bind(bind_address.c_str()); } From 6cb97076ce9ce4547985be3dbbc740d00888d6e2 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 18 Jun 2018 21:02:58 +0100 Subject: [PATCH 0098/1404] simplewallet: remove leftover global debug trace --- src/simplewallet/simplewallet.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 196a2eb7416..d41d63aa1d2 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -1434,7 +1434,6 @@ bool simple_wallet::set_ring(const std::vector &args) } ring.push_back(offset); ptr += read; - MGINFO("read offset: " << offset); } if (!valid) continue; From 5a2864c1fb676e5fbd97450622c639015a4e22d7 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 18 Jun 2018 21:03:51 +0100 Subject: [PATCH 0099/1404] simplewallet: fix delay when setting auto-refresh off --- src/simplewallet/simplewallet.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 196a2eb7416..8dc6724da24 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -1837,6 +1837,7 @@ bool simple_wallet::set_auto_refresh(const std::vector &args/* = st if (pwd_container) { parse_bool_and_use(args[1], [&](bool auto_refresh) { + m_auto_refresh_enabled.store(false, std::memory_order_relaxed); m_wallet->auto_refresh(auto_refresh); m_idle_mutex.lock(); m_auto_refresh_enabled.store(auto_refresh, std::memory_order_relaxed); From 09f43b9a6b98975c299fdbd90190d346d38c0418 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 18 Jun 2018 13:53:52 +0100 Subject: [PATCH 0100/1404] crypto: add a README pointing to the SUPERCOP licence --- src/crypto/crypto_ops_builder/ref10/README.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 src/crypto/crypto_ops_builder/ref10/README.md diff --git a/src/crypto/crypto_ops_builder/ref10/README.md b/src/crypto/crypto_ops_builder/ref10/README.md new file mode 100644 index 00000000000..59193305b95 --- /dev/null +++ b/src/crypto/crypto_ops_builder/ref10/README.md @@ -0,0 +1,4 @@ +This code comes from Daniel J. Bernstein's SUPERCOP source, +released in the public domain. + +[http://ed25519.cr.yp.to/software.html](http://ed25519.cr.yp.to/software.html) From 1a526ed57181a837b20481d1b5d96f3713579d45 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 12 Jun 2018 07:25:01 +0100 Subject: [PATCH 0101/1404] abstract_tcp_server2: restart async accept on error --- .../epee/include/net/abstract_tcp_server2.inl | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/contrib/epee/include/net/abstract_tcp_server2.inl b/contrib/epee/include/net/abstract_tcp_server2.inl index 91a94c21ebf..0694d7baf80 100644 --- a/contrib/epee/include/net/abstract_tcp_server2.inl +++ b/contrib/epee/include/net/abstract_tcp_server2.inl @@ -982,7 +982,8 @@ POP_WARNINGS void boosted_tcp_server::handle_accept(const boost::system::error_code& e) { MDEBUG("handle_accept"); - TRY_ENTRY(); + try + { if (!e) { if (m_connection_type == e_connection_type_RPC) { @@ -1000,11 +1001,25 @@ POP_WARNINGS conn->start(true, 1 < m_threads_count); conn->save_dbg_log(); - }else + return; + } + else + { + MERROR("Error in boosted_tcp_server::handle_accept: " << e); + } + } + catch (const std::exception &e) { - _erro("Some problems at accept: " << e.message() << ", connections_count = " << m_sock_count); + MERROR("Exception in boosted_tcp_server::handle_accept: " << e.what()); } - CATCH_ENTRY_L0("boosted_tcp_server::handle_accept", void()); + + // error path, if e or exception + _erro("Some problems at accept: " << e.message() << ", connections_count = " << m_sock_count); + misc_utils::sleep_no_w(100); + new_connection_.reset(new connection(io_service_, m_config, m_sock_count, m_sock_number, m_pfilter, m_connection_type)); + acceptor_.async_accept(new_connection_->socket(), + boost::bind(&boosted_tcp_server::handle_accept, this, + boost::asio::placeholders::error)); } //--------------------------------------------------------------------------------- template From 2a19697bd4500c80802bd35c6a6cb1d3d43c38a3 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 19 Jun 2018 22:07:30 +0100 Subject: [PATCH 0102/1404] wallet2: fix double header in unsigned transfer file --- src/wallet/wallet2.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 617fb9a87ee..67181844b8a 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -4882,7 +4882,7 @@ bool wallet2::sign_tx(unsigned_tx_set &exported_txs, const std::string &signed_f return false; } - if (!epee::file_io_utils::save_string_to_file(signed_filename, std::string(SIGNED_TX_PREFIX) + ciphertext)) + if (!epee::file_io_utils::save_string_to_file(signed_filename, ciphertext)) { LOG_PRINT_L0("Failed to save file to " << signed_filename); return false; From bd9e4e3149ab309471dee795a2c26537b5e4c631 Mon Sep 17 00:00:00 2001 From: stoffu Date: Wed, 20 Jun 2018 12:04:03 +0900 Subject: [PATCH 0103/1404] daemon: show a bit more info with print_block --- src/daemon/rpc_command_executor.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp index 956c84a0184..707f03ebdcf 100644 --- a/src/daemon/rpc_command_executor.cpp +++ b/src/daemon/rpc_command_executor.cpp @@ -75,7 +75,9 @@ namespace { << "hash: " << header.hash << std::endl << "difficulty: " << boost::lexical_cast(header.difficulty) << std::endl << "POW hash: " << header.pow_hash << std::endl - << "reward: " << boost::lexical_cast(header.reward); + << "block size: " << header.block_size << std::endl + << "num txes: " << header.num_txes << std::endl + << "reward: " << cryptonote::print_money(header.reward); } std::string get_human_time_ago(time_t t, time_t now) From 0c4148845bebcd5639a79197bb6b033266793f48 Mon Sep 17 00:00:00 2001 From: stoffu Date: Wed, 20 Jun 2018 12:08:40 +0900 Subject: [PATCH 0104/1404] miner: show id and height when a block is found --- src/cryptonote_basic/miner.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cryptonote_basic/miner.cpp b/src/cryptonote_basic/miner.cpp index 2c777f5a26d..62cdaaa539c 100644 --- a/src/cryptonote_basic/miner.cpp +++ b/src/cryptonote_basic/miner.cpp @@ -485,7 +485,7 @@ namespace cryptonote { //we lucky! ++m_config.current_extra_message_index; - MGINFO_GREEN("Found block for difficulty: " << local_diff); + MGINFO_GREEN("Found block " << get_block_hash(b) << " at height " << height << " for difficulty: " << local_diff); if(!m_phandler->handle_block_found(b)) { --m_config.current_extra_message_index; From 7bf99a237651fa3796682ac25558c369697492ae Mon Sep 17 00:00:00 2001 From: cryptochangements34 Date: Wed, 20 Jun 2018 15:57:23 -0400 Subject: [PATCH 0105/1404] fix string handling for donation message --- src/simplewallet/simplewallet.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 3668df7b956..dd0f7455ee5 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -4781,7 +4781,7 @@ bool simple_wallet::donate(const std::vector &args_) local_args.push_back(amount_str); if (!payment_id_str.empty()) local_args.push_back(payment_id_str); - message_writer() << tr("Donating ") << amount_str << " to The Monero Project (donate.getmonero.org or "<< MONERO_DONATION_ADDR <<")."; + message_writer() << (boost::format(tr("Donating %s %s to The Monero Project (donate.getmonero.org or %s).")) % amount_str % cryptonote::get_unit(cryptonote::get_default_decimal_point()) % MONERO_DONATION_ADDR).str(); transfer_new(local_args); return true; } From e5592c4bab058e154f68858dcb41eed6c2f0f18a Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 17 Jun 2018 22:07:15 +0100 Subject: [PATCH 0106/1404] rpc: add blockchain disk size to getinfo This should help new nodes predict how much disk space will be needed for a full sync --- src/blockchain_db/blockchain_db.h | 7 +++++++ src/blockchain_db/lmdb/db_lmdb.cpp | 11 +++++++++++ src/blockchain_db/lmdb/db_lmdb.h | 2 ++ src/rpc/core_rpc_server.cpp | 2 ++ src/rpc/core_rpc_server_commands_defs.h | 4 +++- tests/unit_tests/hardfork.cpp | 1 + 6 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/blockchain_db/blockchain_db.h b/src/blockchain_db/blockchain_db.h index 19ba3234085..e8e2bfdee4a 100644 --- a/src/blockchain_db/blockchain_db.h +++ b/src/blockchain_db/blockchain_db.h @@ -1535,6 +1535,13 @@ class BlockchainDB */ virtual bool is_read_only() const = 0; + /** + * @brief get disk space requirements + * + * @return the size required + */ + virtual uint64_t get_database_size() const = 0; + // TODO: this should perhaps be (or call) a series of functions which // progressively update through version updates /** diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp index fe31321f3be..be450dec06f 100644 --- a/src/blockchain_db/lmdb/db_lmdb.cpp +++ b/src/blockchain_db/lmdb/db_lmdb.cpp @@ -35,6 +35,7 @@ #include #include "string_tools.h" +#include "file_io_utils.h" #include "common/util.h" #include "cryptonote_basic/cryptonote_format_utils.h" #include "crypto/crypto.h" @@ -3422,6 +3423,16 @@ bool BlockchainLMDB::is_read_only() const return false; } +uint64_t BlockchainLMDB::get_database_size() const +{ + uint64_t size = 0; + boost::filesystem::path datafile(m_folder); + datafile /= CRYPTONOTE_BLOCKCHAINDATA_FILENAME; + if (!epee::file_io_utils::get_file_size(datafile.string(), size)) + size = 0; + return size; +} + void BlockchainLMDB::fixup() { LOG_PRINT_L3("BlockchainLMDB::" << __func__); diff --git a/src/blockchain_db/lmdb/db_lmdb.h b/src/blockchain_db/lmdb/db_lmdb.h index cc1b06ca02c..d1fb534a2f3 100644 --- a/src/blockchain_db/lmdb/db_lmdb.h +++ b/src/blockchain_db/lmdb/db_lmdb.h @@ -375,6 +375,8 @@ class BlockchainLMDB : public BlockchainDB virtual bool is_read_only() const; + virtual uint64_t get_database_size() const; + // fix up anything that may be wrong due to past bugs virtual void fixup(); diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index dc7b6b30fa2..357ef72be13 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -207,6 +207,7 @@ namespace cryptonote boost::shared_lock lock(m_bootstrap_daemon_mutex); res.was_bootstrap_ever_used = m_was_bootstrap_ever_used; } + res.database_size = m_core.get_blockchain_storage().get_db().get_database_size(); return true; } //------------------------------------------------------------------------------------------------------------------------------ @@ -1577,6 +1578,7 @@ namespace cryptonote boost::shared_lock lock(m_bootstrap_daemon_mutex); res.was_bootstrap_ever_used = m_was_bootstrap_ever_used; } + res.database_size = m_core.get_blockchain_storage().get_db().get_database_size(); return true; } //------------------------------------------------------------------------------------------------------------------------------ diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index 1e624da1b83..c722126f69d 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -49,7 +49,7 @@ namespace cryptonote // advance which version they will stop working with // Don't go over 32767 for any of these #define CORE_RPC_VERSION_MAJOR 1 -#define CORE_RPC_VERSION_MINOR 20 +#define CORE_RPC_VERSION_MINOR 21 #define MAKE_CORE_RPC_VERSION(major,minor) (((major)<<16)|(minor)) #define CORE_RPC_VERSION MAKE_CORE_RPC_VERSION(CORE_RPC_VERSION_MAJOR, CORE_RPC_VERSION_MINOR) @@ -964,6 +964,7 @@ namespace cryptonote std::string bootstrap_daemon_address; uint64_t height_without_bootstrap; bool was_bootstrap_ever_used; + uint64_t database_size; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(status) @@ -993,6 +994,7 @@ namespace cryptonote KV_SERIALIZE(bootstrap_daemon_address) KV_SERIALIZE(height_without_bootstrap) KV_SERIALIZE(was_bootstrap_ever_used) + KV_SERIALIZE(database_size) END_KV_SERIALIZE_MAP() }; }; diff --git a/tests/unit_tests/hardfork.cpp b/tests/unit_tests/hardfork.cpp index 913ebe84ab8..f206dd30e5d 100644 --- a/tests/unit_tests/hardfork.cpp +++ b/tests/unit_tests/hardfork.cpp @@ -124,6 +124,7 @@ class TestDB: public BlockchainDB { virtual void remove_txpool_tx(const crypto::hash& txid) {} virtual bool get_txpool_tx_meta(const crypto::hash& txid, txpool_tx_meta_t &meta) const { return false; } virtual bool get_txpool_tx_blob(const crypto::hash& txid, cryptonote::blobdata &bd) const { return false; } + virtual uint64_t get_database_size() const { return 0; } virtual cryptonote::blobdata get_txpool_tx_blob(const crypto::hash& txid) const { return ""; } virtual bool for_all_txpool_txes(std::function, bool include_blob = false, bool include_unrelayed_txes = false) const { return false; } From 66df13a58b6ede4b38aebede4b9a2ea08e161efe Mon Sep 17 00:00:00 2001 From: stoffu Date: Thu, 21 Jun 2018 10:49:03 +0900 Subject: [PATCH 0107/1404] wallet-rpc.getaddress: throw if index is out of bound --- src/wallet/wallet_rpc_server.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index 7f7d3364247..9b2dda3ebdd 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -362,6 +362,7 @@ namespace tools if (!m_wallet) return not_open(er); try { + THROW_WALLET_EXCEPTION_IF(req.account_index >= m_wallet->get_num_subaddress_accounts(), error::account_index_outofbound); res.addresses.clear(); std::vector req_address_index; if (req.address_index.empty()) @@ -377,6 +378,7 @@ namespace tools m_wallet->get_transfers(transfers); for (uint32_t i : req_address_index) { + THROW_WALLET_EXCEPTION_IF(i >= m_wallet->get_num_subaddresses(req.account_index), error::address_index_outofbound); res.addresses.resize(res.addresses.size() + 1); auto& info = res.addresses.back(); const cryptonote::subaddress_index index = {req.account_index, i}; From 8fc0cdb96f136fbce59503789d17c6be30fe219a Mon Sep 17 00:00:00 2001 From: stoffu Date: Mon, 4 Jun 2018 19:18:18 +0900 Subject: [PATCH 0108/1404] wallet2: lower default for subaddress lookahead when restoring with hardware --- src/wallet/wallet2.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index bb0953689d9..16ca04e3fda 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -3273,6 +3273,12 @@ void wallet2::restore(const std::string& wallet_, const epee::wipeable_string& p cryptonote::block b; generate_genesis(b); m_blockchain.push_back(get_block_hash(b)); + if (m_subaddress_lookahead_major == SUBADDRESS_LOOKAHEAD_MAJOR && m_subaddress_lookahead_minor == SUBADDRESS_LOOKAHEAD_MINOR) + { + // the default lookahead setting (50:200) is clearly too much for hardware wallet + m_subaddress_lookahead_major = 5; + m_subaddress_lookahead_minor = 20; + } add_subaddress_account(tr("Primary account")); if (!wallet_.empty()) { store(); From 248310de0666d9c12e6e861043386419da63f3b1 Mon Sep 17 00:00:00 2001 From: stoffu Date: Mon, 4 Jun 2018 16:54:35 +0900 Subject: [PATCH 0109/1404] Move parse_subaddress_lookahead() from simplewallet.cpp to util.cpp --- src/common/util.cpp | 18 ++++++++++++++++++ src/common/util.h | 3 +++ src/simplewallet/simplewallet.cpp | 17 +++-------------- 3 files changed, 24 insertions(+), 14 deletions(-) diff --git a/src/common/util.cpp b/src/common/util.cpp index 3f330fa13d0..329352e9477 100644 --- a/src/common/util.cpp +++ b/src/common/util.cpp @@ -827,4 +827,22 @@ std::string get_nix_version_display_string() return false; return true; } + + boost::optional> parse_subaddress_lookahead(const std::string& str) + { + auto pos = str.find(":"); + bool r = pos != std::string::npos; + uint32_t major; + r = r && epee::string_tools::get_xtype_from_string(major, str.substr(0, pos)); + uint32_t minor; + r = r && epee::string_tools::get_xtype_from_string(minor, str.substr(pos + 1)); + if (r) + { + return std::make_pair(major, minor); + } + else + { + return {}; + } + } } diff --git a/src/common/util.h b/src/common/util.h index 7caf0e3c596..dc426830b90 100644 --- a/src/common/util.h +++ b/src/common/util.h @@ -32,6 +32,7 @@ #include #include +#include #include #include #include @@ -214,4 +215,6 @@ namespace tools bool sha256sum(const std::string &filename, crypto::hash &hash); bool is_hdd(const char *path); + + boost::optional> parse_subaddress_lookahead(const std::string& str); } diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 90c3da6cad3..62f220a1ee9 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -379,21 +379,10 @@ namespace boost::optional> parse_subaddress_lookahead(const std::string& str) { - auto pos = str.find(":"); - bool r = pos != std::string::npos; - uint32_t major; - r = r && epee::string_tools::get_xtype_from_string(major, str.substr(0, pos)); - uint32_t minor; - r = r && epee::string_tools::get_xtype_from_string(minor, str.substr(pos + 1)); - if (r) - { - return std::make_pair(major, minor); - } - else - { + auto r = tools::parse_subaddress_lookahead(str); + if (!r) fail_msg_writer() << tr("invalid format for subaddress lookahead; must be :"); - return {}; - } + return r; } void handle_transfer_exception(const std::exception_ptr &e, bool trusted_daemon) From 46e90b7780459007b6bb74423c99f92791de77b4 Mon Sep 17 00:00:00 2001 From: stoffu Date: Mon, 4 Jun 2018 16:58:13 +0900 Subject: [PATCH 0110/1404] Wallet API: add support for wallet creation from hardware device --- src/wallet/api/wallet.cpp | 32 ++++++++++++++++++++++++++++++- src/wallet/api/wallet.h | 6 ++++++ src/wallet/api/wallet2_api.h | 32 +++++++++++++++++++++++++++++++ src/wallet/api/wallet_manager.cpp | 20 +++++++++++++++++++ src/wallet/api/wallet_manager.h | 6 ++++++ 5 files changed, 95 insertions(+), 1 deletion(-) diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index 7ade42e1164..c7dbd29e41c 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -372,6 +372,7 @@ WalletImpl::WalletImpl(NetworkType nettype) , m_trustedDaemon(false) , m_wallet2Callback(nullptr) , m_recoveringFromSeed(false) + , m_recoveringFromDevice(false) , m_synchronized(false) , m_rebuildWalletCache(false) , m_is_connected(false) @@ -419,6 +420,7 @@ bool WalletImpl::create(const std::string &path, const std::string &password, co clearStatus(); m_recoveringFromSeed = false; + m_recoveringFromDevice = false; bool keys_file_exists; bool wallet_file_exists; tools::wallet2::wallet_exists(path, keys_file_exists, wallet_file_exists); @@ -621,11 +623,28 @@ bool WalletImpl::recoverFromKeysWithPassword(const std::string &path, return true; } +bool WalletImpl::recoverFromDevice(const std::string &path, const std::string &password, const std::string &device_name) +{ + clearStatus(); + m_recoveringFromSeed = false; + m_recoveringFromDevice = true; + try + { + m_wallet->restore(path, password, device_name); + LOG_PRINT_L1("Generated new wallet from device: " + device_name); + } + catch (const std::exception& e) { + setStatusError(string(tr("failed to generate new wallet: ")) + e.what()); + return false; + } + return true; +} bool WalletImpl::open(const std::string &path, const std::string &password) { clearStatus(); m_recoveringFromSeed = false; + m_recoveringFromDevice = false; try { // TODO: handle "deprecated" // Check if wallet cache exists @@ -663,6 +682,7 @@ bool WalletImpl::recover(const std::string &path, const std::string &password, c } m_recoveringFromSeed = true; + m_recoveringFromDevice = false; crypto::secret_key recovery_key; std::string old_language; if (!crypto::ElectrumWords::words_to_bytes(seed, recovery_key, old_language)) { @@ -884,6 +904,16 @@ void WalletImpl::setRecoveringFromSeed(bool recoveringFromSeed) m_recoveringFromSeed = recoveringFromSeed; } +void WalletImpl::setRecoveringFromDevice(bool recoveringFromDevice) +{ + m_recoveringFromDevice = recoveringFromDevice; +} + +void WalletImpl::setSubaddressLookahead(uint32_t major, uint32_t minor) +{ + m_wallet->set_subaddress_lookahead(major, minor); +} + uint64_t WalletImpl::balance(uint32_t accountIndex) const { return m_wallet->balance(accountIndex); @@ -1996,7 +2026,7 @@ bool WalletImpl::isNewWallet() const // with the daemon (pull hashes instead of pull blocks). // If wallet cache is rebuilt, creation height stored in .keys is used. // Watch only wallet is a copy of an existing wallet. - return !(blockChainHeight() > 1 || m_recoveringFromSeed || m_rebuildWalletCache) && !watchOnly(); + return !(blockChainHeight() > 1 || m_recoveringFromSeed || m_recoveringFromDevice || m_rebuildWalletCache) && !watchOnly(); } bool WalletImpl::doInit(const string &daemon_address, uint64_t upper_transaction_size_limit, bool ssl) diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h index 813ca4b3092..08232cafdc4 100644 --- a/src/wallet/api/wallet.h +++ b/src/wallet/api/wallet.h @@ -76,6 +76,9 @@ class WalletImpl : public Wallet const std::string &address_string, const std::string &viewkey_string, const std::string &spendkey_string = ""); + bool recoverFromDevice(const std::string &path, + const std::string &password, + const std::string &device_name); bool close(bool store = true); std::string seed() const; std::string getSeedLanguage() const; @@ -115,6 +118,8 @@ class WalletImpl : public Wallet void setRefreshFromBlockHeight(uint64_t refresh_from_block_height); uint64_t getRefreshFromBlockHeight() const { return m_wallet->get_refresh_from_block_height(); }; void setRecoveringFromSeed(bool recoveringFromSeed); + void setRecoveringFromDevice(bool recoveringFromDevice) override; + void setSubaddressLookahead(uint32_t major, uint32_t minor) override; bool watchOnly() const; bool rescanSpent(); NetworkType nettype() const {return static_cast(m_wallet->nettype());} @@ -232,6 +237,7 @@ class WalletImpl : public Wallet // so it shouldn't be considered as new and pull blocks (slow-refresh) // instead of pulling hashes (fast-refresh) std::atomic m_recoveringFromSeed; + std::atomic m_recoveringFromDevice; std::atomic m_synchronized; std::atomic m_rebuildWalletCache; // cache connection status to avoid unnecessary RPC calls diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h index 5b99bd975bb..f54255e91c2 100644 --- a/src/wallet/api/wallet2_api.h +++ b/src/wallet/api/wallet2_api.h @@ -509,6 +509,21 @@ struct Wallet */ virtual void setRecoveringFromSeed(bool recoveringFromSeed) = 0; + /*! + * \brief setRecoveringFromDevice - set state to recovering from device + * + * \param recoveringFromDevice - true/false + */ + virtual void setRecoveringFromDevice(bool recoveringFromDevice) = 0; + + /*! + * \brief setSubaddressLookahead - set size of subaddress lookahead + * + * \param major - size fot the major index + * \param minor - size fot the minor index + */ + virtual void setSubaddressLookahead(uint32_t major, uint32_t minor) = 0; + /** * @brief connectToDaemon - connects to the daemon. TODO: check if it can be removed * @return @@ -1014,6 +1029,23 @@ struct WalletManager return createWalletFromKeys(path, language, testnet ? TESTNET : MAINNET, restoreHeight, addressString, viewKeyString, spendKeyString); } + /*! + * \brief creates wallet using hardware device. + * \param path Name of wallet file to be created + * \param password Password of wallet file + * \param nettype Network type + * \param deviceName Device name + * \param restoreHeight restore from start height (0 sets to current height) + * \param subaddressLookahead Size of subaddress lookahead (empty sets to some default low value) + * \return Wallet instance (Wallet::status() needs to be called to check if recovered successfully) + */ + virtual Wallet * createWalletFromDevice(const std::string &path, + const std::string &password, + NetworkType nettype, + const std::string &deviceName, + uint64_t restoreHeight = 0, + const std::string &subaddressLookahead = "") = 0; + /*! * \brief Closes wallet. In case operation succeeded, wallet object deleted. in case operation failed, wallet object not deleted * \param wallet previously opened / created wallet instance diff --git a/src/wallet/api/wallet_manager.cpp b/src/wallet/api/wallet_manager.cpp index a63716576ae..99eadc82fc5 100644 --- a/src/wallet/api/wallet_manager.cpp +++ b/src/wallet/api/wallet_manager.cpp @@ -114,6 +114,26 @@ Wallet *WalletManagerImpl::createWalletFromKeys(const std::string &path, return wallet; } +Wallet *WalletManagerImpl::createWalletFromDevice(const std::string &path, + const std::string &password, + NetworkType nettype, + const std::string &deviceName, + uint64_t restoreHeight, + const std::string &subaddressLookahead) +{ + WalletImpl * wallet = new WalletImpl(nettype); + if(restoreHeight > 0){ + wallet->setRefreshFromBlockHeight(restoreHeight); + } + auto lookahead = tools::parse_subaddress_lookahead(subaddressLookahead); + if (lookahead) + { + wallet->setSubaddressLookahead(lookahead->first, lookahead->second); + } + wallet->recoverFromDevice(path, password, deviceName); + return wallet; +} + bool WalletManagerImpl::closeWallet(Wallet *wallet, bool store) { WalletImpl * wallet_ = dynamic_cast(wallet); diff --git a/src/wallet/api/wallet_manager.h b/src/wallet/api/wallet_manager.h index 26238b658c2..19aad9ee38e 100644 --- a/src/wallet/api/wallet_manager.h +++ b/src/wallet/api/wallet_manager.h @@ -64,6 +64,12 @@ class WalletManagerImpl : public WalletManager const std::string &addressString, const std::string &viewKeyString, const std::string &spendKeyString = ""); + virtual Wallet * createWalletFromDevice(const std::string &path, + const std::string &password, + NetworkType nettype, + const std::string &deviceName, + uint64_t restoreHeight = 0, + const std::string &subaddressLookahead = "") override; virtual bool closeWallet(Wallet *wallet, bool store = true); bool walletExists(const std::string &path); bool verifyWalletPassword(const std::string &keys_file_name, const std::string &password, bool no_spend_key) const; From 3e46db97e556d9353741beb154b55e073caff8b9 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 20 Jun 2018 12:48:10 +0100 Subject: [PATCH 0111/1404] rpc: add a non binary get_transaction_pool_hashes RPC --- src/rpc/core_rpc_server.cpp | 20 +++++++++++++++++-- src/rpc/core_rpc_server.h | 4 +++- src/rpc/core_rpc_server_commands_defs.h | 26 +++++++++++++++++++++++-- src/wallet/wallet2.cpp | 4 ++-- 4 files changed, 47 insertions(+), 7 deletions(-) diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index dc7b6b30fa2..d9ce8699d07 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -981,11 +981,11 @@ namespace cryptonote return true; } //------------------------------------------------------------------------------------------------------------------------------ - bool core_rpc_server::on_get_transaction_pool_hashes(const COMMAND_RPC_GET_TRANSACTION_POOL_HASHES::request& req, COMMAND_RPC_GET_TRANSACTION_POOL_HASHES::response& res, bool request_has_rpc_origin) + bool core_rpc_server::on_get_transaction_pool_hashes_bin(const COMMAND_RPC_GET_TRANSACTION_POOL_HASHES_BIN::request& req, COMMAND_RPC_GET_TRANSACTION_POOL_HASHES_BIN::response& res, bool request_has_rpc_origin) { PERF_TIMER(on_get_transaction_pool_hashes); bool r; - if (use_bootstrap_daemon_if_necessary(invoke_http_mode::JON, "/get_transaction_pool_hashes.bin", req, res, r)) + if (use_bootstrap_daemon_if_necessary(invoke_http_mode::JON, "/get_transaction_pool_hashes.bin", req, res, r)) return r; m_core.get_pool_transaction_hashes(res.tx_hashes, !request_has_rpc_origin || !m_restricted); @@ -993,6 +993,22 @@ namespace cryptonote return true; } //------------------------------------------------------------------------------------------------------------------------------ + bool core_rpc_server::on_get_transaction_pool_hashes(const COMMAND_RPC_GET_TRANSACTION_POOL_HASHES::request& req, COMMAND_RPC_GET_TRANSACTION_POOL_HASHES::response& res, bool request_has_rpc_origin) + { + PERF_TIMER(on_get_transaction_pool_hashes); + bool r; + if (use_bootstrap_daemon_if_necessary(invoke_http_mode::JON, "/get_transaction_pool_hashes", req, res, r)) + return r; + + std::vector tx_hashes; + m_core.get_pool_transaction_hashes(tx_hashes, !request_has_rpc_origin || !m_restricted); + res.tx_hashes.reserve(tx_hashes.size()); + for (const crypto::hash &tx_hash: tx_hashes) + res.tx_hashes.push_back(epee::string_tools::pod_to_hex(tx_hash)); + res.status = CORE_RPC_STATUS_OK; + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_get_transaction_pool_stats(const COMMAND_RPC_GET_TRANSACTION_POOL_STATS::request& req, COMMAND_RPC_GET_TRANSACTION_POOL_STATS::response& res, bool request_has_rpc_origin) { PERF_TIMER(on_get_transaction_pool_stats); diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h index 324f219f8da..51e5cd8490f 100644 --- a/src/rpc/core_rpc_server.h +++ b/src/rpc/core_rpc_server.h @@ -107,7 +107,8 @@ namespace cryptonote MAP_URI_AUTO_JON2_IF("/set_log_level", on_set_log_level, COMMAND_RPC_SET_LOG_LEVEL, !m_restricted) MAP_URI_AUTO_JON2_IF("/set_log_categories", on_set_log_categories, COMMAND_RPC_SET_LOG_CATEGORIES, !m_restricted) MAP_URI_AUTO_JON2("/get_transaction_pool", on_get_transaction_pool, COMMAND_RPC_GET_TRANSACTION_POOL) - MAP_URI_AUTO_JON2("/get_transaction_pool_hashes.bin", on_get_transaction_pool_hashes, COMMAND_RPC_GET_TRANSACTION_POOL_HASHES) + MAP_URI_AUTO_JON2("/get_transaction_pool_hashes.bin", on_get_transaction_pool_hashes_bin, COMMAND_RPC_GET_TRANSACTION_POOL_HASHES_BIN) + MAP_URI_AUTO_JON2("/get_transaction_pool_hashes", on_get_transaction_pool_hashes, COMMAND_RPC_GET_TRANSACTION_POOL_HASHES) MAP_URI_AUTO_JON2("/get_transaction_pool_stats", on_get_transaction_pool_stats, COMMAND_RPC_GET_TRANSACTION_POOL_STATS) MAP_URI_AUTO_JON2_IF("/stop_daemon", on_stop_daemon, COMMAND_RPC_STOP_DAEMON, !m_restricted) MAP_URI_AUTO_JON2("/get_info", on_get_info, COMMAND_RPC_GET_INFO) @@ -180,6 +181,7 @@ namespace cryptonote bool on_set_log_level(const COMMAND_RPC_SET_LOG_LEVEL::request& req, COMMAND_RPC_SET_LOG_LEVEL::response& res); bool on_set_log_categories(const COMMAND_RPC_SET_LOG_CATEGORIES::request& req, COMMAND_RPC_SET_LOG_CATEGORIES::response& res); bool on_get_transaction_pool(const COMMAND_RPC_GET_TRANSACTION_POOL::request& req, COMMAND_RPC_GET_TRANSACTION_POOL::response& res, bool request_has_rpc_origin = true); + bool on_get_transaction_pool_hashes_bin(const COMMAND_RPC_GET_TRANSACTION_POOL_HASHES_BIN::request& req, COMMAND_RPC_GET_TRANSACTION_POOL_HASHES_BIN::response& res, bool request_has_rpc_origin = true); bool on_get_transaction_pool_hashes(const COMMAND_RPC_GET_TRANSACTION_POOL_HASHES::request& req, COMMAND_RPC_GET_TRANSACTION_POOL_HASHES::response& res, bool request_has_rpc_origin = true); bool on_get_transaction_pool_stats(const COMMAND_RPC_GET_TRANSACTION_POOL_STATS::request& req, COMMAND_RPC_GET_TRANSACTION_POOL_STATS::response& res, bool request_has_rpc_origin = true); bool on_stop_daemon(const COMMAND_RPC_STOP_DAEMON::request& req, COMMAND_RPC_STOP_DAEMON::response& res); diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index 1e624da1b83..f9b57b0c994 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -49,7 +49,7 @@ namespace cryptonote // advance which version they will stop working with // Don't go over 32767 for any of these #define CORE_RPC_VERSION_MAJOR 1 -#define CORE_RPC_VERSION_MINOR 20 +#define CORE_RPC_VERSION_MINOR 21 #define MAKE_CORE_RPC_VERSION(major,minor) (((major)<<16)|(minor)) #define CORE_RPC_VERSION MAKE_CORE_RPC_VERSION(CORE_RPC_VERSION_MAJOR, CORE_RPC_VERSION_MINOR) @@ -1487,7 +1487,7 @@ namespace cryptonote }; }; - struct COMMAND_RPC_GET_TRANSACTION_POOL_HASHES + struct COMMAND_RPC_GET_TRANSACTION_POOL_HASHES_BIN { struct request { @@ -1509,6 +1509,28 @@ namespace cryptonote }; }; + struct COMMAND_RPC_GET_TRANSACTION_POOL_HASHES + { + struct request + { + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; + + struct response + { + std::string status; + std::vector tx_hashes; + bool untrusted; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(status) + KV_SERIALIZE(tx_hashes) + KV_SERIALIZE(untrusted) + END_KV_SERIALIZE_MAP() + }; + }; + struct tx_backlog_entry { uint64_t blob_size; diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index bb0953689d9..25775b2ab60 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -1893,8 +1893,8 @@ void wallet2::update_pool_state(bool refreshed) MDEBUG("update_pool_state start"); // get the pool state - cryptonote::COMMAND_RPC_GET_TRANSACTION_POOL_HASHES::request req; - cryptonote::COMMAND_RPC_GET_TRANSACTION_POOL_HASHES::response res; + cryptonote::COMMAND_RPC_GET_TRANSACTION_POOL_HASHES_BIN::request req; + cryptonote::COMMAND_RPC_GET_TRANSACTION_POOL_HASHES_BIN::response res; m_daemon_rpc_mutex.lock(); bool r = epee::net_utils::invoke_http_json("/get_transaction_pool_hashes.bin", req, res, m_http_client, rpc_timeout); m_daemon_rpc_mutex.unlock(); From d45cd37c531b174ceefd4f1c2f66958430aebf05 Mon Sep 17 00:00:00 2001 From: ordtrogen Date: Wed, 13 Jun 2018 12:49:49 +0200 Subject: [PATCH 0112/1404] fix typos in language files and rpc_args.cpp --- src/rpc/rpc_args.cpp | 2 +- src/simplewallet/simplewallet.cpp | 2 +- translations/monero.ts | 4 ++-- translations/monero_fr.ts | 4 ++-- translations/monero_it.ts | 6 +++--- translations/monero_sv.ts | 4 ++-- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/rpc/rpc_args.cpp b/src/rpc/rpc_args.cpp index d4a6138ba14..d4044d11b7e 100644 --- a/src/rpc/rpc_args.cpp +++ b/src/rpc/rpc_args.cpp @@ -102,7 +102,7 @@ namespace cryptonote { if (!config.login) { - LOG_ERROR(arg.rpc_access_control_origins.name << tr(" requires RFC server password --") << arg.rpc_login.name << tr(" cannot be empty")); + LOG_ERROR(arg.rpc_access_control_origins.name << tr(" requires RPC server password --") << arg.rpc_login.name << tr(" cannot be empty")); return boost::none; } diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 90c3da6cad3..19c60c0fa2c 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -2259,7 +2259,7 @@ simple_wallet::simple_wallet() "refresh-type \n " " Set the wallet's refresh behaviour.\n " "priority [0|1|2|3|4]\n " - " Set the fee too default/unimportant/normal/elevated/priority.\n " + " Set the fee to default/unimportant/normal/elevated/priority.\n " "confirm-missing-payment-id <1|0>\n " "ask-password <1|0>\n " "unit \n " diff --git a/translations/monero.ts b/translations/monero.ts index 1c27236080a..5f0ba0c7c21 100644 --- a/translations/monero.ts +++ b/translations/monero.ts @@ -461,7 +461,7 @@ - requires RFC server password -- + requires RPC server password -- @@ -1678,7 +1678,7 @@ If the "tag_description" argument is specified, the tag <tag_name&g refresh-type <full|optimize-coinbase|no-coinbase|default> Set the wallet's refresh behaviour. priority [0|1|2|3|4] - Set the fee too default/unimportant/normal/elevated/priority. + Set the fee to default/unimportant/normal/elevated/priority. confirm-missing-payment-id <1|0> ask-password <1|0> unit <monero|millinero|micronero|nanonero|piconero> diff --git a/translations/monero_fr.ts b/translations/monero_fr.ts index a52d1a11f91..957d2a38250 100644 --- a/translations/monero_fr.ts +++ b/translations/monero_fr.ts @@ -461,7 +461,7 @@ - requires RFC server password -- + requires RPC server password -- nécessite le mot de passe du serveur RPC -- @@ -1699,7 +1699,7 @@ Si l'argument "tag_description" est spécifié, le texte arbitrai refresh-type <full|optimize-coinbase|no-coinbase|default> Set the wallet's refresh behaviour. priority [0|1|2|3|4] - Set the fee too default/unimportant/normal/elevated/priority. + Set the fee to default/unimportant/normal/elevated/priority. confirm-missing-payment-id <1|0> ask-password <1|0> unit <monero|millinero|micronero|nanonero|piconero> diff --git a/translations/monero_it.ts b/translations/monero_it.ts index d14a33d6012..d4b1695f30b 100644 --- a/translations/monero_it.ts +++ b/translations/monero_it.ts @@ -461,8 +461,8 @@ - requires RFC server password -- - richiede la password del server RFC -- + requires RPC server password -- + @@ -1582,7 +1582,7 @@ If the "tag_description" argument is specified, the tag <tag_name&g refresh-type <full|optimize-coinbase|no-coinbase|default> Set the wallet's refresh behaviour. priority [0|1|2|3|4] - Set the fee too default/unimportant/normal/elevated/priority. + Set the fee to default/unimportant/normal/elevated/priority. confirm-missing-payment-id <1|0> ask-password <1|0> unit <monero|millinero|micronero|nanonero|piconero> diff --git a/translations/monero_sv.ts b/translations/monero_sv.ts index e99076960a4..3c5ddf9afc8 100644 --- a/translations/monero_sv.ts +++ b/translations/monero_sv.ts @@ -461,7 +461,7 @@ - requires RFC server password -- + requires RPC server password -- kräver lösenord till RPC-server -- @@ -1699,7 +1699,7 @@ Om argumentet "tag_description" anges, så tilldelas taggen <taggna refresh-type <full|optimize-coinbase|no-coinbase|default> Set the wallet's refresh behaviour. priority [0|1|2|3|4] - Set the fee too default/unimportant/normal/elevated/priority. + Set the fee to default/unimportant/normal/elevated/priority. confirm-missing-payment-id <1|0> ask-password <1|0> unit <monero|millinero|micronero|nanonero|piconero> From df9d50a8a4e4d154f1f168815fd63532cfe1414b Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 23 Jun 2018 09:20:34 +0100 Subject: [PATCH 0113/1404] wallet2: fix read buffer overflow in import_key_images --- src/wallet/wallet2.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index bb0953689d9..5cffd8396de 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -9635,7 +9635,7 @@ uint64_t wallet2::import_key_images(const std::vector spent_txids; // For each spent key image, search for a tx in m_transfers that uses it as input. std::vector swept_transfers; // If such a spending tx wasn't found in m_transfers, this means the spending tx // was created by sweep_all, so we can't know the spent height and other detailed info. - for(size_t i = 0; i < m_transfers.size(); ++i) + for(size_t i = 0; i < signed_key_images.size(); ++i) { transfer_details &td = m_transfers[i]; uint64_t amount = td.amount(); From 9d7f6e60899bf0099b0a468a687420e715518e3f Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 23 Jun 2018 09:53:09 +0100 Subject: [PATCH 0114/1404] simplewallet: init trusted daemon flag to false when autodetecting --- src/simplewallet/simplewallet.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 5576a024f20..c4281318625 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -3245,6 +3245,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) { try { + m_trusted_daemon = false; if (tools::is_local_address(m_wallet->get_daemon_address())) { MINFO(tr("Daemon is local, assuming trusted")); From 3b4dec2d12f571a845c56ec02ba3d03bcaea5569 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 23 Jun 2018 15:26:22 +0100 Subject: [PATCH 0115/1404] device_ledger: fix potential buffer overflow from bad size calc --- src/device/device_ledger.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/device/device_ledger.cpp b/src/device/device_ledger.cpp index aedaf83829e..94cb3db89f2 100644 --- a/src/device/device_ledger.cpp +++ b/src/device/device_ledger.cpp @@ -194,7 +194,8 @@ namespace hw { this->buffer_send[3], this->buffer_send[4] ); - buffer_to_str(strbuffer+strlen(strbuffer), sizeof(strbuffer), (char*)(this->buffer_send+5), this->length_send-5); + const size_t len = strlen(strbuffer); + buffer_to_str(strbuffer+len, sizeof(strbuffer)-len, (char*)(this->buffer_send+5), this->length_send-5); MDEBUG( "CMD :" << strbuffer); } } @@ -206,7 +207,8 @@ namespace hw { this->buffer_recv[this->length_recv-2], this->buffer_recv[this->length_recv-1] ); - buffer_to_str(strbuffer+strlen(strbuffer), sizeof(strbuffer), (char*)(this->buffer_recv), this->length_recv-2); + const size_t len = strlen(strbuffer); + buffer_to_str(strbuffer+len, sizeof(strbuffer)-len, (char*)(this->buffer_recv), this->length_recv-2); MDEBUG( "RESP :" << strbuffer); } From 41e9cab4e1c9d6df26db1cecd30fe6aafacc80a7 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 23 Jun 2018 15:26:55 +0100 Subject: [PATCH 0116/1404] device: misc cleanup use snprintf "just in case" where appropriate consistently use unsigned for temp values pass std::string by const ref rather than by value add length check (which can't happen in practice) for memcpy --- src/device/device_ledger.cpp | 6 +++--- src/device/log.cpp | 12 +++++++----- src/device/log.hpp | 8 ++++---- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/device/device_ledger.cpp b/src/device/device_ledger.cpp index 94cb3db89f2..8735baeb6f6 100644 --- a/src/device/device_ledger.cpp +++ b/src/device/device_ledger.cpp @@ -187,7 +187,7 @@ namespace hw { void device_ledger::logCMD() { if (apdu_verbose) { char strbuffer[1024]; - sprintf(strbuffer, "%.02x %.02x %.02x %.02x %.02x ", + snprintf(strbuffer, sizeof(strbuffer), "%.02x %.02x %.02x %.02x %.02x ", this->buffer_send[0], this->buffer_send[1], this->buffer_send[2], @@ -203,7 +203,7 @@ namespace hw { void device_ledger::logRESP() { if (apdu_verbose) { char strbuffer[1024]; - sprintf(strbuffer, "%.02x%.02x ", + snprintf(strbuffer, sizeof(strbuffer), "%.02x%.02x ", this->buffer_recv[this->length_recv-2], this->buffer_recv[this->length_recv-1] ); @@ -295,7 +295,7 @@ namespace hw { unsigned int device_ledger::exchange(unsigned int ok, unsigned int mask) { LONG rv; - int sw; + unsigned int sw; ASSERT_T0(this->length_send <= BUFFER_SEND_SIZE); logCMD(); diff --git a/src/device/log.cpp b/src/device/log.cpp index cbbcfc95306..1707524fb76 100644 --- a/src/device/log.cpp +++ b/src/device/log.cpp @@ -45,13 +45,13 @@ namespace hw { } } - void log_hexbuffer(std::string msg, const char* buff, size_t len) { + void log_hexbuffer(const std::string &msg, const char* buff, size_t len) { char logstr[1025]; buffer_to_str(logstr, sizeof(logstr), buff, len); MDEBUG(msg<< ": " << logstr); } - void log_message(std::string msg, std::string info ) { + void log_message(const std::string &msg, const std::string &info ) { MDEBUG(msg << ": " << info); } @@ -122,16 +122,18 @@ namespace hw { rct::keyV decrypt(const rct::keyV &keys) { rct::keyV x ; + x.reserve(keys.size()); for (unsigned int j = 0; j Date: Sat, 23 Jun 2018 15:43:31 +0100 Subject: [PATCH 0117/1404] device_ledger: fix buffer underflow on bad data from device --- src/device/device_ledger.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/device/device_ledger.cpp b/src/device/device_ledger.cpp index 8735baeb6f6..bc7f3778945 100644 --- a/src/device/device_ledger.cpp +++ b/src/device/device_ledger.cpp @@ -304,6 +304,7 @@ namespace hw { SCARD_PCI_T0, this->buffer_send, this->length_send, NULL, this->buffer_recv, &this->length_recv); ASSERT_RV(rv); + ASSERT_T0(this->length_recv >= 2); ASSERT_T0(this->length_recv <= BUFFER_RECV_SIZE); logRESP(); From 7cdd147da5b06a692b0b825992de3bb07f142156 Mon Sep 17 00:00:00 2001 From: einsteinsfool Date: Sat, 23 Jun 2018 21:15:29 +0200 Subject: [PATCH 0118/1404] Changed URLs to HTTPS --- CONTRIBUTING.md | 2 +- Doxyfile | 50 +++++++++---------- README.md | 18 +++---- cmake/Doxyfile.in | 22 ++++---- src/common/scoped_message_writer.h | 2 +- src/common/stack_trace.cpp | 2 +- src/crypto/blake256.c | 2 +- .../crypto_ops_builder/ref10/description | 2 +- .../ref10CommentedCombined/MakeCryptoOps.py | 4 +- .../ref10CommentedCombined/description | 2 +- src/crypto/initializer.h | 4 +- src/crypto/slow-hash.c | 4 +- src/crypto/tree-hash.c | 2 +- src/mnemonics/esperanto.h | 2 +- src/mnemonics/lojban.h | 2 +- src/ringct/rctSigs.cpp | 18 +++---- src/ringct/rctSigs.h | 8 +-- src/ringct/rctTypes.h | 2 +- tests/data/fuzz/parse-url/URL2 | 2 +- utils/build_scripts/android32.Dockerfile | 4 +- utils/build_scripts/android64.Dockerfile | 4 +- 21 files changed, 79 insertions(+), 79 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f794c0d5e58..3de9cd5ceeb 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,7 +1,7 @@ # Contributing to Monero A good way to help is to test, and report bugs. See -[How to Report Bugs Effectively (by Simon Tatham)](http://www.chiark.greenend.org.uk/~sgtatham/bugs.html) +[How to Report Bugs Effectively (by Simon Tatham)](https://www.chiark.greenend.org.uk/~sgtatham/bugs.html) if you want to help that way. Testing is invaluable in making a piece of software solid and usable. diff --git a/Doxyfile b/Doxyfile index f622c618d2c..8e08ad3ec79 100644 --- a/Doxyfile +++ b/Doxyfile @@ -20,7 +20,7 @@ # 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 +# built into libc) for the transcoding. See https://www.gnu.org/software/libiconv # for the list of possible encodings. # The default value is: UTF-8. @@ -285,7 +285,7 @@ EXTENSION_MAPPING = # If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments # according to the Markdown format, which allows for more readable -# documentation. See http://daringfireball.net/projects/markdown/ for details. +# documentation. See https://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. @@ -318,7 +318,7 @@ BUILTIN_STL_SUPPORT = NO CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip (see: -# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen +# https://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen # will parse them like normal C++ but will assume all classes use public instead # of private inheritance when no explicit protection keyword is present. # The default value is: NO. @@ -677,7 +677,7 @@ LAYOUT_FILE = # The CITE_BIB_FILES tag can be used to specify one or more bib files containing # the reference definitions. This must be a list of .bib files. The .bib # extension is automatically appended if omitted. This requires the bibtex tool -# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. +# to be installed. See also https://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. Do not use file names with spaces, bibtex cannot handle them. See @@ -759,7 +759,7 @@ INPUT = src # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses # libiconv (or the iconv built into libc) for the transcoding. See the libiconv -# documentation (see: http://www.gnu.org/software/libiconv) for the list of +# documentation (see: https://www.gnu.org/software/libiconv) for the list of # possible encodings. # The default value is: UTF-8. @@ -951,7 +951,7 @@ SOURCE_TOOLTIPS = YES # If the USE_HTAGS tag is set to YES then the references to source code will # point to the HTML generated by the htags(1) tool instead of doxygen built-in # source browser. The htags tool is part of GNU's global source tagging system -# (see http://www.gnu.org/software/global/global.html). You will need version +# (see https://www.gnu.org/software/global/global.html). You will need version # 4.8.6 or higher. # # To use it do the following: @@ -1094,7 +1094,7 @@ HTML_EXTRA_FILES = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen # will adjust the colors in the stylesheet and background images according to # this color. Hue is specified as an angle on a colorwheel, see -# http://en.wikipedia.org/wiki/Hue for more information. For instance the value +# https://en.wikipedia.org/wiki/Hue for more information. For instance the value # 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 # purple, and 360 is red again. # Minimum value: 0, maximum value: 359, default value: 220. @@ -1152,12 +1152,12 @@ HTML_INDEX_NUM_ENTRIES = 100 # If the GENERATE_DOCSET tag is set to YES, additional index files will be # generated that can be used as input for Apple's Xcode 3 integrated development -# environment (see: http://developer.apple.com/tools/xcode/), introduced with +# environment (see: https://developer.apple.com/tools/xcode/), introduced with # OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a # Makefile in the HTML output directory. Running make will produce the docset in # that directory and running make install will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at -# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# startup. See https://developer.apple.com/tools/creatingdocsetswithdoxygen.html # for more information. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. @@ -1197,7 +1197,7 @@ DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three # additional HTML index files: index.hhp, index.hhc, and index.hhk. The # index.hhp is a project file that can be read by Microsoft's HTML Help Workshop -# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on +# (see: https://www.microsoft.com/en-us/download/details.aspx?id=21138) on # Windows. # # The HTML Help Workshop contains a compiler that can convert all HTML output @@ -1273,7 +1273,7 @@ QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help # Project output. For more information please see Qt Help Project / Namespace -# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). +# (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_QHP is set to YES. @@ -1281,7 +1281,7 @@ QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt # Help Project output. For more information please see Qt Help Project / Virtual -# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- +# Folders (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual- # folders). # The default value is: doc. # This tag requires that the tag GENERATE_QHP is set to YES. @@ -1290,7 +1290,7 @@ QHP_VIRTUAL_FOLDER = doc # If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom # filter to add. For more information please see Qt Help Project / Custom -# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom- # filters). # This tag requires that the tag GENERATE_QHP is set to YES. @@ -1298,7 +1298,7 @@ QHP_CUST_FILTER_NAME = # The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see Qt Help Project / Custom -# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom- # filters). # This tag requires that the tag GENERATE_QHP is set to YES. @@ -1306,7 +1306,7 @@ QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's filter section matches. Qt Help Project / Filter Attributes (see: -# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_SECT_FILTER_ATTRS = @@ -1411,7 +1411,7 @@ FORMULA_FONTSIZE = 10 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 +# https://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 @@ -1423,7 +1423,7 @@ USE_MATHJAX = NO # When MathJax is enabled you can set the default output format to be used for # the MathJax output. See the MathJax site (see: -# http://docs.mathjax.org/en/latest/output.html) for more details. +# https://docs.mathjax.org/en/latest/output.html) for more details. # Possible values are: HTML-CSS (which is slower, but has the best # compatibility), NativeMML (i.e. MathML) and SVG. # The default value is: HTML-CSS. @@ -1438,11 +1438,11 @@ MATHJAX_FORMAT = HTML-CSS # MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax # Content Delivery Network so you can quickly see the result without installing # MathJax. However, it is strongly recommended to install a local copy of -# MathJax from http://www.mathjax.org before deployment. -# The default value is: http://cdn.mathjax.org/mathjax/latest. +# MathJax from https://www.mathjax.org before deployment. +# The default value is: https://cdn.mathjax.org/mathjax/latest. # This tag requires that the tag USE_MATHJAX is set to YES. -MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest +MATHJAX_RELPATH = https://cdn.mathjax.org/mathjax/latest # The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax # extension names that should be enabled during MathJax rendering. For example @@ -1453,7 +1453,7 @@ MATHJAX_EXTENSIONS = # The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces # of code that will be used on startup of the MathJax code. See the MathJax site -# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an +# (see: https://docs.mathjax.org/en/latest/output.html) for more details. For an # example see the documentation. # This tag requires that the tag USE_MATHJAX is set to YES. @@ -1500,7 +1500,7 @@ SERVER_BASED_SEARCH = NO # # Doxygen ships with an example indexer ( doxyindexer) and search engine # (doxysearch.cgi) which are based on the open source search engine library -# Xapian (see: http://xapian.org/). +# Xapian (see: https://xapian.org/). # # See the section "External Indexing and Searching" for details. # The default value is: NO. @@ -1513,7 +1513,7 @@ EXTERNAL_SEARCH = NO # # Doxygen ships with an example indexer ( doxyindexer) and search engine # (doxysearch.cgi) which are based on the open source search engine library -# Xapian (see: http://xapian.org/). See the section "External Indexing and +# Xapian (see: https://xapian.org/). See the section "External Indexing and # Searching" for details. # This tag requires that the tag SEARCHENGINE is set to YES. @@ -1684,7 +1684,7 @@ 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. See -# http://en.wikipedia.org/wiki/BibTeX and \cite for more info. +# https://en.wikipedia.org/wiki/BibTeX and \cite for more info. # The default value is: plain. # This tag requires that the tag GENERATE_LATEX is set to YES. @@ -2051,7 +2051,7 @@ 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 (see: -# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent +# https://www.graphviz.org/), 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 value is: NO. diff --git a/README.md b/README.md index a8014e1f3d0..3b03ee471ba 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ Portions Copyright (c) 2012-2013 The Cryptonote developers. - Forum: [forum.getmonero.org](https://forum.getmonero.org) - Mail: [dev@getmonero.org](mailto:dev@getmonero.org) - GitHub: [https://github.com/monero-project/monero](https://github.com/monero-project/monero) -- IRC: [#monero-dev on Freenode](http://webchat.freenode.net/?randomnick=1&channels=%23monero-dev&prompt=1&uio=d4) +- IRC: [#monero-dev on Freenode](https://webchat.freenode.net/?randomnick=1&channels=%23monero-dev&prompt=1&uio=d4) ## Vulnerability response @@ -77,12 +77,12 @@ The Bitcoin donation address is: `1KTexdemPdxSBcG55heUuTjDRYqbC5ZL8H` Core development funding and/or some supporting services are also graciously provided by sponsors: [](https://mymonero.com) -[](http://kitware.com) -[](http://dome9.com) -[](http://araxis.com) -[](http://www.jetbrains.com/) -[](http://www.navicat.com/) -[](http://www.symas.com/) +[](https://kitware.com) +[](https://dome9.com) +[](https://araxis.com) +[](https://www.jetbrains.com/) +[](https://www.navicat.com/) +[](https://www.symas.com/) There are also several mining pools that kindly donate a portion of their fees, [a list of them can be found on our Bitcointalk post](https://bitcointalk.org/index.php?topic=583449.0). @@ -287,14 +287,14 @@ If you are using the older Raspbian Jessie image, compiling Monero is a bit more #### On Windows: Binaries for Windows are built on Windows using the MinGW toolchain within -[MSYS2 environment](http://msys2.github.io). The MSYS2 environment emulates a +[MSYS2 environment](https://www.msys2.org). The MSYS2 environment emulates a POSIX system. The toolchain runs within the environment and *cross-compiles* binaries that can run outside of the environment as a regular Windows application. **Preparing the build environment** -* Download and install the [MSYS2 installer](http://msys2.github.io), either the 64-bit or the 32-bit package, depending on your system. +* Download and install the [MSYS2 installer](https://www.msys2.org), either the 64-bit or the 32-bit package, depending on your system. * Open the MSYS shell via the `MSYS2 Shell` shortcut * Update packages using pacman: diff --git a/cmake/Doxyfile.in b/cmake/Doxyfile.in index 35a4911f8d8..81199d5e448 100644 --- a/cmake/Doxyfile.in +++ b/cmake/Doxyfile.in @@ -18,7 +18,7 @@ # 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. +# https://www.gnu.org/software/libiconv for the list of possible encodings. DOXYFILE_ENCODING = UTF-8 @@ -242,7 +242,7 @@ EXTENSION_MAPPING = # 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. +# documentation. See https://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. @@ -589,7 +589,7 @@ LAYOUT_FILE = # 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 +# https://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. @@ -665,7 +665,7 @@ INPUT = ../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 +# into libc) for the transcoding. See https://www.gnu.org/software/libiconv for # the list of possible encodings. INPUT_ENCODING = UTF-8 @@ -833,7 +833,7 @@ 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 +# tagging system (see https://www.gnu.org/software/global/global.html). You # will need version 4.8.6 or higher. USE_HTAGS = NO @@ -928,7 +928,7 @@ 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. +# see https://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. @@ -981,7 +981,7 @@ HTML_INDEX_NUM_ENTRIES = 100 # 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 +# See https://developer.apple.com/tools/creatingdocsetswithdoxygen.html # for more information. GENERATE_DOCSET = NO @@ -1179,7 +1179,7 @@ FORMULA_FONTSIZE = 10 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 +# (see https://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 @@ -1194,9 +1194,9 @@ USE_MATHJAX = NO # 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. +# copy of MathJax from https://www.mathjax.org before deployment. -MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest +MATHJAX_RELPATH = https://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. @@ -1318,7 +1318,7 @@ 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. +# https://en.wikipedia.org/wiki/BibTeX for more info. LATEX_BIB_STYLE = plain diff --git a/src/common/scoped_message_writer.h b/src/common/scoped_message_writer.h index d7517babb48..d887a13c980 100644 --- a/src/common/scoped_message_writer.h +++ b/src/common/scoped_message_writer.h @@ -73,7 +73,7 @@ class scoped_message_writer #if defined(_MSC_VER) , m_oss(std::move(rhs.m_oss)) #else - // GCC bug: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=54316 + // GCC bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=54316 , m_oss(rhs.m_oss.str(), std::ios_base::out | std::ios_base::ate) #endif , m_color(std::move(rhs.m_color)) diff --git a/src/common/stack_trace.cpp b/src/common/stack_trace.cpp index 9c2bf4b5379..d6dc4d7ccfa 100644 --- a/src/common/stack_trace.cpp +++ b/src/common/stack_trace.cpp @@ -51,7 +51,7 @@ #define ST_LOG(x) CINFO(el::base::Writer,el::base::DispatchAction::FileOnlyLog,MONERO_DEFAULT_LOG_CATEGORY) << x -// from http://stackoverflow.com/questions/11665829/how-can-i-print-stack-trace-for-caught-exceptions-in-c-code-injection-in-c +// from https://stackoverflow.com/questions/11665829/how-can-i-print-stack-trace-for-caught-exceptions-in-c-code-injection-in-c // The decl of __cxa_throw in /usr/include/.../cxxabi.h uses // 'std::type_info *', but GCC's built-in protype uses 'void *'. diff --git a/src/crypto/blake256.c b/src/crypto/blake256.c index d503c47e0bf..6ef7d4207ce 100644 --- a/src/crypto/blake256.c +++ b/src/crypto/blake256.c @@ -31,7 +31,7 @@ * The blake256_* and blake224_* functions are largely copied from * blake256_light.c and blake224_light.c from the BLAKE website: * - * http://131002.net/blake/ + * https://131002.net/blake/ * * The hmac_* functions implement HMAC-BLAKE-256 and HMAC-BLAKE-224. * HMAC is specified by RFC 2104. diff --git a/src/crypto/crypto_ops_builder/ref10/description b/src/crypto/crypto_ops_builder/ref10/description index cbfcb2cbafd..99f74774705 100644 --- a/src/crypto/crypto_ops_builder/ref10/description +++ b/src/crypto/crypto_ops_builder/ref10/description @@ -1,2 +1,2 @@ EdDSA signatures using Curve25519 -from http://hyperelliptic.org/ebats/supercop-20141124.tar.bz2 +from https://hyperelliptic.org/ebats/supercop-20141124.tar.bz2 diff --git a/src/crypto/crypto_ops_builder/ref10CommentedCombined/MakeCryptoOps.py b/src/crypto/crypto_ops_builder/ref10CommentedCombined/MakeCryptoOps.py index 9b55d260d02..0ed97d5f48b 100644 --- a/src/crypto/crypto_ops_builder/ref10CommentedCombined/MakeCryptoOps.py +++ b/src/crypto/crypto_ops_builder/ref10CommentedCombined/MakeCryptoOps.py @@ -1,5 +1,5 @@ #assumes you have gnu sed, osx sed might need slight syntax changeo -#c.f. http://unix.stackexchange.com/questions/112023/how-can-i-replace-a-string-in-a-files +#c.f. https://unix.stackexchange.com/questions/112023/how-can-i-replace-a-string-in-a-files #written by shen-noether monero research labs @@ -8,7 +8,7 @@ import textwrap #for comments etc print("make sure you have cat and grep installed") -print("also assumes gnu sed syntax, c.f. :http://unix.stackexchange.com/questions/112023/how-can-i-replace-a-string-in-a-files") +print("also assumes gnu sed syntax, c.f. :https://unix.stackexchange.com/questions/112023/how-can-i-replace-a-string-in-a-files") print("I believe osx may have slightly different version of sed") print("maybe someone smart can replace the sed with perl..") diff --git a/src/crypto/crypto_ops_builder/ref10CommentedCombined/description b/src/crypto/crypto_ops_builder/ref10CommentedCombined/description index fadc9f9afba..9407b400aa1 100644 --- a/src/crypto/crypto_ops_builder/ref10CommentedCombined/description +++ b/src/crypto/crypto_ops_builder/ref10CommentedCombined/description @@ -2,6 +2,6 @@ shen_ed25519_ref10 MakeCryptoOps.py makes crypto-ops.c in the Monero source from the ref10 implementation EdDSA signatures using Curve25519 -from http://hyperelliptic.org/ebats/supercop-20141124.tar.bz2 +from https://hyperelliptic.org/ebats/supercop-20141124.tar.bz2 Commented / combined by Shen Noether, Monero Research Lab diff --git a/src/crypto/initializer.h b/src/crypto/initializer.h index afbace726b6..107988d2b96 100644 --- a/src/crypto/initializer.h +++ b/src/crypto/initializer.h @@ -43,8 +43,8 @@ #elif defined(_MSC_VER) #include #include -// http://stackoverflow.com/questions/1113409/attribute-constructor-equivalent-in-vc -// http://msdn.microsoft.com/en-us/library/bb918180.aspx +// https://stackoverflow.com/questions/1113409/attribute-constructor-equivalent-in-vc +// https://msdn.microsoft.com/en-us/library/bb918180.aspx #pragma section(".CRT$XCT", read) #define INITIALIZER(name) \ static void __cdecl name(void); \ diff --git a/src/crypto/slow-hash.c b/src/crypto/slow-hash.c index 35e98f2f580..9d4fc0dfa12 100644 --- a/src/crypto/slow-hash.c +++ b/src/crypto/slow-hash.c @@ -309,7 +309,7 @@ STATIC INLINE void aes_256_assist2(__m128i* t1, __m128i * t3) * CPU AES support. * For more information about these functions, see page 19 of Intel's AES instructions * white paper: - * http://www.intel.com/content/dam/www/public/us/en/documents/white-papers/aes-instructions-set-white-paper.pdf + * https://www.intel.com/content/dam/doc/white-paper/advanced-encryption-standard-new-instructions-set-paper.pdf * * @param key the input 128 bit key * @param expandedKey An output buffer to hold the generated key schedule @@ -558,7 +558,7 @@ void slow_hash_free_state(void) * AES support on x86 CPUs. * * A diagram of the inner loop of this function can be found at - * http://www.cs.cmu.edu/~dga/crypto/xmr/cryptonight.png + * https://www.cs.cmu.edu/~dga/crypto/xmr/cryptonight.png * * @param data the data to hash * @param length the length in bytes of the data diff --git a/src/crypto/tree-hash.c b/src/crypto/tree-hash.c index e6d6a267c0c..57c38b86bbd 100644 --- a/src/crypto/tree-hash.c +++ b/src/crypto/tree-hash.c @@ -67,7 +67,7 @@ size_t tree_hash_cnt(size_t count) { } void tree_hash(const char (*hashes)[HASH_SIZE], size_t count, char *root_hash) { -// The blockchain block at height 202612 http://monerochain.info/block/bbd604d2ba11ba27935e006ed39c9bfdd99b76bf4a50654bc1e1e61217962698 +// The blockchain block at height 202612 https://moneroblocks.info/block/202612 // contained 514 transactions, that triggered bad calculation of variable "cnt" in the original version of this function // as from CryptoNote code. // diff --git a/src/mnemonics/esperanto.h b/src/mnemonics/esperanto.h index cb377a58ea3..a1d1a3f30ee 100644 --- a/src/mnemonics/esperanto.h +++ b/src/mnemonics/esperanto.h @@ -37,7 +37,7 @@ * Sources: * Baza Radikaro Oficiala * Reta Vortaro (http://www.reta-vortaro.de/revo/) - * Esperanto Panorama - Esperanto-English Dictionary (http://www.esperanto-panorama.net/vortaro/eoen.htm) + * Esperanto Panorama - Esperanto-English Dictionary (https://www.esperanto-panorama.net/vortaro/eoen.htm) * ESPDIC - Paul Denisowski (http://www.denisowski.org/Esperanto/ESPDIC/espdic.txt) */ diff --git a/src/mnemonics/lojban.h b/src/mnemonics/lojban.h index 412531a2307..0966a1169b9 100644 --- a/src/mnemonics/lojban.h +++ b/src/mnemonics/lojban.h @@ -35,7 +35,7 @@ /* * Word list authored by: sorpaas * Sources: - * lo gimste jo'u lo ma'oste (http://guskant.github.io/lojbo/gismu-cmavo.html) + * lo gimste jo'u lo ma'oste (https://guskant.github.io/lojbo/gismu-cmavo.html) * N-grams of Lojban corpus (https://mw.lojban.org/papri/N-grams_of_Lojban_corpus) */ diff --git a/src/ringct/rctSigs.cpp b/src/ringct/rctSigs.cpp index 777b4d13aa3..9a8ebb80047 100644 --- a/src/ringct/rctSigs.cpp +++ b/src/ringct/rctSigs.cpp @@ -147,7 +147,7 @@ namespace rct { //This is a just slghtly more efficient version than the ones described below //(will be explained in more detail in Ring Multisig paper //These are aka MG signatutes in earlier drafts of the ring ct paper - // c.f. http://eprint.iacr.org/2015/1098 section 2. + // c.f. https://eprint.iacr.org/2015/1098 section 2. // Gen creates a signature which proves that for some column in the keymatrix "pk" // the signer knows a secret key for each row in that column // Ver verifies that the MG sig was created correctly @@ -244,7 +244,7 @@ namespace rct { //This is a just slghtly more efficient version than the ones described below //(will be explained in more detail in Ring Multisig paper //These are aka MG signatutes in earlier drafts of the ring ct paper - // c.f. http://eprint.iacr.org/2015/1098 section 2. + // c.f. https://eprint.iacr.org/2015/1098 section 2. // Gen creates a signature which proves that for some column in the keymatrix "pk" // the signer knows a secret key for each row in that column // Ver verifies that the MG sig was created correctly @@ -307,7 +307,7 @@ namespace rct { //proveRange and verRange //proveRange gives C, and mask such that \sumCi = C - // c.f. http://eprint.iacr.org/2015/1098 section 5.1 + // c.f. https://eprint.iacr.org/2015/1098 section 5.1 // and Ci is a commitment to either 0 or 2^i, i=0,...,63 // thus this proves that "amount" is in [0, 2^64] // mask is a such that C = aG + bH, and b = amount @@ -339,7 +339,7 @@ namespace rct { //proveRange and verRange //proveRange gives C, and mask such that \sumCi = C - // c.f. http://eprint.iacr.org/2015/1098 section 5.1 + // c.f. https://eprint.iacr.org/2015/1098 section 5.1 // and Ci is a commitment to either 0 or 2^i, i=0,...,63 // thus this proves that "amount" is in [0, 2^64] // mask is a such that C = aG + bH, and b = amount @@ -441,7 +441,7 @@ namespace rct { //Ring-ct MG sigs //Prove: - // c.f. http://eprint.iacr.org/2015/1098 section 4. definition 10. + // c.f. https://eprint.iacr.org/2015/1098 section 4. definition 10. // This does the MG sig on the "dest" part of the given key matrix, and // the last row is the sum of input commitments from that column - sum output commitments // this shows that sum inputs = sum outputs @@ -527,7 +527,7 @@ namespace rct { //Ring-ct MG sigs //Prove: - // c.f. http://eprint.iacr.org/2015/1098 section 4. definition 10. + // c.f. https://eprint.iacr.org/2015/1098 section 4. definition 10. // This does the MG sig on the "dest" part of the given key matrix, and // the last row is the sum of input commitments from that column - sum output commitments // this shows that sum inputs = sum outputs @@ -650,7 +650,7 @@ namespace rct { // Also contains masked "amount" and "mask" so the receiver can see how much they received //verRct: // verifies that all signatures (rangeProogs, MG sig, sum inputs = outputs) are correct - //decodeRct: (c.f. http://eprint.iacr.org/2015/1098 section 5.1.1) + //decodeRct: (c.f. https://eprint.iacr.org/2015/1098 section 5.1.1) // uses the attached ecdh info to find the amounts represented by each output commitment // must know the destination private key to find the correct amount, else will return a random number // Note: For txn fees, the last index in the amounts vector should contain that @@ -828,7 +828,7 @@ namespace rct { // Also contains masked "amount" and "mask" so the receiver can see how much they received //verRct: // verifies that all signatures (rangeProogs, MG sig, sum inputs = outputs) are correct - //decodeRct: (c.f. http://eprint.iacr.org/2015/1098 section 5.1.1) + //decodeRct: (c.f. https://eprint.iacr.org/2015/1098 section 5.1.1) // uses the attached ecdh info to find the amounts represented by each output commitment // must know the destination private key to find the correct amount, else will return a random number bool verRct(const rctSig & rv, bool semantics) { @@ -1023,7 +1023,7 @@ namespace rct { // Also contains masked "amount" and "mask" so the receiver can see how much they received //verRct: // verifies that all signatures (rangeProogs, MG sig, sum inputs = outputs) are correct - //decodeRct: (c.f. http://eprint.iacr.org/2015/1098 section 5.1.1) + //decodeRct: (c.f. https://eprint.iacr.org/2015/1098 section 5.1.1) // uses the attached ecdh info to find the amounts represented by each output commitment // must know the destination private key to find the correct amount, else will return a random number xmr_amount decodeRct(const rctSig & rv, const key & sk, unsigned int i, key & mask, hw::device &hwdev) { diff --git a/src/ringct/rctSigs.h b/src/ringct/rctSigs.h index b8aab0f11a5..5a9b2dd44fe 100644 --- a/src/ringct/rctSigs.h +++ b/src/ringct/rctSigs.h @@ -70,7 +70,7 @@ namespace rct { //Multilayered Spontaneous Anonymous Group Signatures (MLSAG signatures) //These are aka MG signatutes in earlier drafts of the ring ct paper - // c.f. http://eprint.iacr.org/2015/1098 section 2. + // c.f. https://eprint.iacr.org/2015/1098 section 2. // Gen creates a signature which proves that for some column in the keymatrix "pk" // the signer knows a secret key for each row in that column // Ver verifies that the MG sig was created correctly @@ -80,7 +80,7 @@ namespace rct { //proveRange and verRange //proveRange gives C, and mask such that \sumCi = C - // c.f. http://eprint.iacr.org/2015/1098 section 5.1 + // c.f. https://eprint.iacr.org/2015/1098 section 5.1 // and Ci is a commitment to either 0 or 2^i, i=0,...,63 // thus this proves that "amount" is in [0, 2^64] // mask is a such that C = aG + bH, and b = amount @@ -90,7 +90,7 @@ namespace rct { //Ring-ct MG sigs //Prove: - // c.f. http://eprint.iacr.org/2015/1098 section 4. definition 10. + // c.f. https://eprint.iacr.org/2015/1098 section 4. definition 10. // This does the MG sig on the "dest" part of the given key matrix, and // the last row is the sum of input commitments from that column - sum output commitments // this shows that sum inputs = sum outputs @@ -116,7 +116,7 @@ namespace rct { // Also contains masked "amount" and "mask" so the receiver can see how much they received //verRct: // verifies that all signatures (rangeProogs, MG sig, sum inputs = outputs) are correct - //decodeRct: (c.f. http://eprint.iacr.org/2015/1098 section 5.1.1) + //decodeRct: (c.f. https://eprint.iacr.org/2015/1098 section 5.1.1) // uses the attached ecdh info to find the amounts represented by each output commitment // must know the destination private key to find the correct amount, else will return a random number rctSig genRct(const key &message, const ctkeyV & inSk, const keyV & destinations, const std::vector & amounts, const ctkeyM &mixRing, const keyV &amount_keys, const multisig_kLRki *kLRki, multisig_out *msout, unsigned int index, ctkeyV &outSk, bool bulletproof, hw::device &hwdev); diff --git a/src/ringct/rctTypes.h b/src/ringct/rctTypes.h index eba1e3d93fc..844291d0cc9 100644 --- a/src/ringct/rctTypes.h +++ b/src/ringct/rctTypes.h @@ -150,7 +150,7 @@ namespace rct { }; //just contains the necessary keys to represent MLSAG sigs - //c.f. http://eprint.iacr.org/2015/1098 + //c.f. https://eprint.iacr.org/2015/1098 struct mgSig { keyM ss; key cc; diff --git a/tests/data/fuzz/parse-url/URL2 b/tests/data/fuzz/parse-url/URL2 index b66e7de9aff..6eeadc01ab1 100644 --- a/tests/data/fuzz/parse-url/URL2 +++ b/tests/data/fuzz/parse-url/URL2 @@ -1 +1 @@ -iframe_test.html?api_url=http://api.vk.com/api.php&api_id=3289090&api_settings=1&viewer_id=562964060&viewer_type=0&sid=0aad8d1c5713130f9ca0076f2b7b47e532877424961367d81e7fa92455f069be7e21bc3193cbd0be11895&secret=368ebbc0ef&access_token=668bc03f43981d883f73876ffff4aa8564254b359cc745dfa1b3cde7bdab2e94105d8f6d8250717569c0a7&user_id=0&group_id=0&is_app_user=1&auth_key=d2f7a895ca5ff3fdb2a2a8ae23fe679a&language=0&parent_language=0&ad_info=ElsdCQBaQlxiAQRdFUVUXiN2AVBzBx5pU1BXIgZUJlIEAWcgAUoLQg==&referrer=unknown&lc_name=9834b6a3&hash= \ No newline at end of file +iframe_test.html?api_url=https://api.vk.com/api.php&api_id=3289090&api_settings=1&viewer_id=562964060&viewer_type=0&sid=0aad8d1c5713130f9ca0076f2b7b47e532877424961367d81e7fa92455f069be7e21bc3193cbd0be11895&secret=368ebbc0ef&access_token=668bc03f43981d883f73876ffff4aa8564254b359cc745dfa1b3cde7bdab2e94105d8f6d8250717569c0a7&user_id=0&group_id=0&is_app_user=1&auth_key=d2f7a895ca5ff3fdb2a2a8ae23fe679a&language=0&parent_language=0&ad_info=ElsdCQBaQlxiAQRdFUVUXiN2AVBzBx5pU1BXIgZUJlIEAWcgAUoLQg==&referrer=unknown&lc_name=9834b6a3&hash= \ No newline at end of file diff --git a/utils/build_scripts/android32.Dockerfile b/utils/build_scripts/android32.Dockerfile index 20b846aa1c3..d0d25aa3d01 100644 --- a/utils/build_scripts/android32.Dockerfile +++ b/utils/build_scripts/android32.Dockerfile @@ -4,7 +4,7 @@ RUN apt-get update && apt-get install -y unzip automake build-essential curl fil WORKDIR /opt/android ## INSTALL ANDROID SDK -RUN curl -s -O http://dl.google.com/android/android-sdk_r24.4.1-linux.tgz \ +RUN curl -s -O https://dl.google.com/android/android-sdk_r24.4.1-linux.tgz \ && tar --no-same-owner -xzf android-sdk_r24.4.1-linux.tgz \ && rm -f android-sdk_r24.4.1-linux.tgz @@ -51,7 +51,7 @@ ENV PATH /usr/cmake-${CMAKE_VERSION}-Linux-x86_64/bin:$PATH # download, configure and make Zlib ENV ZLIB_VERSION 1.2.11 -RUN curl -s -O http://zlib.net/zlib-${ZLIB_VERSION}.tar.gz \ +RUN curl -s -O https://zlib.net/zlib-${ZLIB_VERSION}.tar.gz \ && tar -xzf zlib-${ZLIB_VERSION}.tar.gz \ && rm zlib-${ZLIB_VERSION}.tar.gz \ && mv zlib-${ZLIB_VERSION} zlib \ diff --git a/utils/build_scripts/android64.Dockerfile b/utils/build_scripts/android64.Dockerfile index 83bcbad8935..a9504fd2c4d 100644 --- a/utils/build_scripts/android64.Dockerfile +++ b/utils/build_scripts/android64.Dockerfile @@ -4,7 +4,7 @@ RUN apt-get update && apt-get install -y unzip automake build-essential curl fil WORKDIR /opt/android ## INSTALL ANDROID SDK -RUN curl -s -O http://dl.google.com/android/android-sdk_r24.4.1-linux.tgz \ +RUN curl -s -O https://dl.google.com/android/android-sdk_r24.4.1-linux.tgz \ && tar --no-same-owner -xzf android-sdk_r24.4.1-linux.tgz \ && rm -f android-sdk_r24.4.1-linux.tgz @@ -51,7 +51,7 @@ ENV PATH /usr/cmake-${CMAKE_VERSION}-Linux-x86_64/bin:$PATH # download, configure and make Zlib ENV ZLIB_VERSION 1.2.11 -RUN curl -s -O http://zlib.net/zlib-${ZLIB_VERSION}.tar.gz \ +RUN curl -s -O https://zlib.net/zlib-${ZLIB_VERSION}.tar.gz \ && tar -xzf zlib-${ZLIB_VERSION}.tar.gz \ && rm zlib-${ZLIB_VERSION}.tar.gz \ && mv zlib-${ZLIB_VERSION} zlib \ From 41b4bf9d6df4bfd59210133416f1a90362794789 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 24 Jun 2018 10:46:57 +0100 Subject: [PATCH 0119/1404] tx_pool: cache check_tx_inputs results This is called a lot when creating a block template, and does not change until the blockchain changes. This also avoids tx parsing when cached. --- src/cryptonote_core/tx_pool.cpp | 33 ++++++++++++++++++++++++++++----- src/cryptonote_core/tx_pool.h | 8 +++++++- 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index 164530b3eb8..b6f1a5d40d8 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -220,7 +220,7 @@ namespace cryptonote crypto::hash max_used_block_id = null_hash; uint64_t max_used_block_height = 0; cryptonote::txpool_tx_meta_t meta; - bool ch_inp_res = m_blockchain.check_tx_inputs(tx, max_used_block_height, max_used_block_id, tvc, kept_by_block); + bool ch_inp_res = check_tx_inputs([&tx]()->cryptonote::transaction&{ return tx; }, id, max_used_block_height, max_used_block_id, tvc, kept_by_block); if(!ch_inp_res) { // if the transaction was valid before (kept_by_block), then it @@ -883,11 +883,15 @@ namespace cryptonote //--------------------------------------------------------------------------------- bool tx_memory_pool::on_blockchain_inc(uint64_t new_block_height, const crypto::hash& top_block_id) { + CRITICAL_REGION_LOCAL(m_transactions_lock); + m_input_cache.clear(); return true; } //--------------------------------------------------------------------------------- bool tx_memory_pool::on_blockchain_dec(uint64_t new_block_height, const crypto::hash& top_block_id) { + CRITICAL_REGION_LOCAL(m_transactions_lock); + m_input_cache.clear(); return true; } //--------------------------------------------------------------------------------- @@ -927,7 +931,26 @@ namespace cryptonote m_transactions_lock.unlock(); } //--------------------------------------------------------------------------------- - bool tx_memory_pool::is_transaction_ready_to_go(txpool_tx_meta_t& txd, const cryptonote::blobdata &txblob, transaction &tx) const + bool tx_memory_pool::check_tx_inputs(const std::function &get_tx, const crypto::hash &txid, uint64_t &max_used_block_height, crypto::hash &max_used_block_id, tx_verification_context &tvc, bool kept_by_block) const + { + if (!kept_by_block) + { + const std::unordered_map>::const_iterator i = m_input_cache.find(txid); + if (i != m_input_cache.end()) + { + max_used_block_height = std::get<2>(i->second); + max_used_block_id = std::get<3>(i->second); + tvc = std::get<1>(i->second); + return std::get<0>(i->second); + } + } + bool ret = m_blockchain.check_tx_inputs(get_tx(), max_used_block_height, max_used_block_id, tvc, kept_by_block); + if (!kept_by_block) + m_input_cache.insert(std::make_pair(txid, std::make_tuple(ret, tvc, max_used_block_height, max_used_block_id))); + return ret; + } + //--------------------------------------------------------------------------------- + bool tx_memory_pool::is_transaction_ready_to_go(txpool_tx_meta_t& txd, const crypto::hash &txid, const cryptonote::blobdata &txblob, transaction &tx) const { struct transction_parser { @@ -956,7 +979,7 @@ namespace cryptonote return false;//we already sure that this tx is broken for this height tx_verification_context tvc; - if(!m_blockchain.check_tx_inputs(lazy_tx(), txd.max_used_block_height, txd.max_used_block_id, tvc)) + if(!check_tx_inputs([&lazy_tx]()->cryptonote::transaction&{ return lazy_tx(); }, txid, txd.max_used_block_height, txd.max_used_block_id, tvc)) { txd.last_failed_height = m_blockchain.get_current_blockchain_height()-1; txd.last_failed_id = m_blockchain.get_block_id_by_height(txd.last_failed_height); @@ -973,7 +996,7 @@ namespace cryptonote return false; //check ring signature again, it is possible (with very small chance) that this transaction become again valid tx_verification_context tvc; - if(!m_blockchain.check_tx_inputs(lazy_tx(), txd.max_used_block_height, txd.max_used_block_id, tvc)) + if(!check_tx_inputs([&lazy_tx]()->cryptonote::transaction&{ return lazy_tx(); }, txid, txd.max_used_block_height, txd.max_used_block_id, tvc)) { txd.last_failed_height = m_blockchain.get_current_blockchain_height()-1; txd.last_failed_id = m_blockchain.get_block_id_by_height(txd.last_failed_height); @@ -1166,7 +1189,7 @@ namespace cryptonote bool ready = false; try { - ready = is_transaction_ready_to_go(meta, txblob, tx); + ready = is_transaction_ready_to_go(meta, sorted_it->second, txblob, tx); } catch (const std::exception &e) { diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h index 4ce2f085d8d..b87a59e0584 100644 --- a/src/cryptonote_core/tx_pool.h +++ b/src/cryptonote_core/tx_pool.h @@ -499,12 +499,13 @@ namespace cryptonote * @brief check if a transaction is a valid candidate for inclusion in a block * * @param txd the transaction to check (and info about it) + * @param txid the txid of the transaction to check * @param txblob the transaction blob to check * @param tx the parsed transaction, if successful * * @return true if the transaction is good to go, otherwise false */ - bool is_transaction_ready_to_go(txpool_tx_meta_t& txd, const cryptonote::blobdata &txblob, transaction &tx) const; + bool is_transaction_ready_to_go(txpool_tx_meta_t& txd, const crypto::hash &txid, const cryptonote::blobdata &txblob, transaction &tx) const; /** * @brief mark all transactions double spending the one passed @@ -557,6 +558,9 @@ namespace cryptonote */ sorted_tx_container::iterator find_tx_in_sorted_container(const crypto::hash& id) const; + //! cache/call Blockchain::check_tx_inputs results + bool check_tx_inputs(const std::function &get_tx, const crypto::hash &txid, uint64_t &max_used_block_height, crypto::hash &max_used_block_id, tx_verification_context &tvc, bool kept_by_block = false) const; + //! transactions which are unlikely to be included in blocks /*! These transactions are kept in RAM in case they *are* included * in a block eventually, but this container is not saved to disk. @@ -567,6 +571,8 @@ namespace cryptonote size_t m_txpool_max_size; size_t m_txpool_size; + + mutable std::unordered_map> m_input_cache; }; } From 87e158b5b9282fca932b9eff0488dd933409a3b3 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 20 Jun 2018 10:40:09 +0100 Subject: [PATCH 0120/1404] device_ledger: factor the prologue code --- src/device/device_ledger.cpp | 470 +++++------------------------------ src/device/device_ledger.hpp | 3 + 2 files changed, 61 insertions(+), 412 deletions(-) diff --git a/src/device/device_ledger.cpp b/src/device/device_ledger.cpp index aedaf83829e..6f2bcf37f74 100644 --- a/src/device/device_ledger.cpp +++ b/src/device/device_ledger.cpp @@ -272,22 +272,32 @@ namespace hw { /* ======================================================================= */ /* MISC */ /* ======================================================================= */ - bool device_ledger::reset() { - int offset; - reset_buffer(); - - this->buffer_send[0] = 0x00; - this->buffer_send[1] = INS_RESET; - this->buffer_send[2] = 0x00; - this->buffer_send[3] = 0x00; - this->buffer_send[4] = 0x00; - offset = 5; - //options - this->buffer_send[offset] = 0x00; + int device_ledger::set_command_header(BYTE ins, BYTE p1, BYTE p2) { + reset_buffer(); + int offset = 0; + this->buffer_send[0] = 0x00; + this->buffer_send[1] = ins; + this->buffer_send[2] = p1; + this->buffer_send[3] = p2; + this->buffer_send[4] = 0x00; + return 5; + } - this->buffer_send[4] = offset-5; - this->length_send = offset; - this->exchange(); + int device_ledger::set_command_header_noopt(BYTE ins, BYTE p1, BYTE p2) { + int offset = set_command_header(ins, p1, p2); + //options + this->buffer_send[offset++] = 0; + this->buffer_send[4] = offset - 5; + return offset; + } + + void device_ledger::send_simple(BYTE ins, BYTE p1) { + this->length_send = set_command_header_noopt(ins, p1); + this->exchange(); + } + + bool device_ledger::reset() { + send_simple(INS_RESET); return true; } @@ -448,20 +458,10 @@ namespace hw { int offset; - reset_buffer(); - switch(mode) { case TRANSACTION_CREATE_REAL: case TRANSACTION_CREATE_FAKE: - this->buffer_send[0] = 0x00; - this->buffer_send[1] = INS_SET_SIGNATURE_MODE; - this->buffer_send[2] = 0x01; - this->buffer_send[3] = 0x00; - this->buffer_send[4] = 0x00; - offset = 5; - //options - this->buffer_send[offset] = 0x00; - offset += 1; + offset = set_command_header_noopt(INS_SET_SIGNATURE_MODE, 1); //account this->buffer_send[offset] = mode; offset += 1; @@ -492,22 +492,7 @@ namespace hw { bool device_ledger::get_public_address(cryptonote::account_public_address &pubkey){ AUTO_LOCK_CMD(); - int offset; - reset_buffer(); - - this->buffer_send[0] = 0x00; - this->buffer_send[1] = INS_GET_KEY; - this->buffer_send[2] = 0x01; - this->buffer_send[3] = 0x00; - this->buffer_send[4] = 0x00; - offset = 5; - //options - this->buffer_send[offset] = 0x00; - offset += 1; - - this->buffer_send[4] = offset-5; - this->length_send = offset; - this->exchange(); + send_simple(INS_GET_KEY, 1); memmove(pubkey.m_view_public_key.data, this->buffer_recv, 32); memmove(pubkey.m_spend_public_key.data, this->buffer_recv+32, 32); @@ -523,22 +508,7 @@ namespace hw { memset(skey.data, 0xFF, 32); //spcialkey, normal conf handled in decrypt - int offset; - reset_buffer(); - - this->buffer_send[0] = 0x00; - this->buffer_send[1] = INS_GET_KEY; - this->buffer_send[2] = 0x02; - this->buffer_send[3] = 0x00; - this->buffer_send[4] = 0x00; - offset = 5; - //options - this->buffer_send[offset] = 0x00; - offset += 1; - - this->buffer_send[4] = offset-5; - this->length_send = offset; - this->exchange(); + send_simple(INS_GET_KEY, 0x02); //View key is retrievied, if allowed, to speed up blockchain parsing memmove(this->viewkey.data, this->buffer_recv+0, 32); @@ -560,7 +530,6 @@ namespace hw { bool device_ledger::generate_chacha_key(const cryptonote::account_keys &keys, crypto::chacha_key &key) { AUTO_LOCK_CMD(); - int offset; #ifdef DEBUG_HWDEVICE crypto::chacha_key key_x; @@ -568,20 +537,7 @@ namespace hw { this->controle_device->generate_chacha_key(keys_x, key_x); #endif - reset_buffer(); - this->buffer_send[0] = 0x00; - this->buffer_send[1] = INS_GET_CHACHA8_PREKEY; - this->buffer_send[2] = 0x00; - this->buffer_send[3] = 0x00; - this->buffer_send[4] = 0x00; - offset = 5; - //options - this->buffer_send[offset] = 0x00; - offset += 1; - - this->buffer_send[4] = offset-5; - this->length_send = offset; - this->exchange(); + send_simple(INS_GET_CHACHA8_PREKEY); char prekey[200]; memmove(prekey, &this->buffer_recv[0], 200); @@ -624,19 +580,7 @@ namespace hw { crypto::derive_subaddress_public_key(pub, derivation, output_index,derived_pub); } else { - int offset =0; - - reset_buffer(); - - this->buffer_send[0] = 0x00; - this->buffer_send[1] = INS_DERIVE_SUBADDRESS_PUBLIC_KEY; - this->buffer_send[2] = 0x00; - this->buffer_send[3] = 0x00; - this->buffer_send[4] = 0x00; - offset = 5; - //options - this->buffer_send[offset] = 0; - offset += 1; + int offset = set_command_header_noopt(INS_DERIVE_SUBADDRESS_PUBLIC_KEY); //pub memmove(this->buffer_send+offset, pub.data, 32); offset += 32; @@ -667,7 +611,6 @@ namespace hw { crypto::public_key device_ledger::get_subaddress_spend_public_key(const cryptonote::account_keys& keys, const cryptonote::subaddress_index &index) { AUTO_LOCK_CMD(); crypto::public_key D; - int offset; #ifdef DEBUG_HWDEVICE const cryptonote::account_keys keys_x = hw::ledger::decrypt(keys); @@ -684,17 +627,7 @@ namespace hw { D = keys.m_account_address.m_spend_public_key; } else { - reset_buffer(); - - this->buffer_send[0] = 0x00; - this->buffer_send[1] = INS_GET_SUBADDRESS_SPEND_PUBLIC_KEY; - this->buffer_send[2] = 0x00; - this->buffer_send[3] = 0x00; - this->buffer_send[4] = 0x00; - offset = 5; - //options - this->buffer_send[offset] = 0x00; - offset += 1; + int offset = set_command_header_noopt(INS_GET_SUBADDRESS_SPEND_PUBLIC_KEY); //index static_assert(sizeof(cryptonote::subaddress_index) == 8, "cryptonote::subaddress_index shall be 8 bytes length"); memmove(this->buffer_send+offset, &index, sizeof(cryptonote::subaddress_index)); @@ -729,7 +662,6 @@ namespace hw { cryptonote::account_public_address device_ledger::get_subaddress(const cryptonote::account_keys& keys, const cryptonote::subaddress_index &index) { AUTO_LOCK_CMD(); cryptonote::account_public_address address; - int offset; #ifdef DEBUG_HWDEVICE const cryptonote::account_keys keys_x = hw::ledger::decrypt(keys); @@ -748,17 +680,7 @@ namespace hw { if (index.is_zero()) { address = keys.m_account_address; } else { - reset_buffer(); - - this->buffer_send[0] = 0x00; - this->buffer_send[1] = INS_GET_SUBADDRESS; - this->buffer_send[2] = 0x00; - this->buffer_send[3] = 0x00; - this->buffer_send[4] = 0x00; - offset = 5; - //options - this->buffer_send[offset] = 0x00; - offset += 1; + int offset = set_command_header_noopt(INS_GET_SUBADDRESS); //index static_assert(sizeof(cryptonote::subaddress_index) == 8, "cryptonote::subaddress_index shall be 8 bytes length"); memmove(this->buffer_send+offset, &index, sizeof(cryptonote::subaddress_index)); @@ -783,7 +705,6 @@ namespace hw { crypto::secret_key device_ledger::get_subaddress_secret_key(const crypto::secret_key &sec, const cryptonote::subaddress_index &index) { AUTO_LOCK_CMD(); crypto::secret_key sub_sec; - int offset; #ifdef DEBUG_HWDEVICE const crypto::secret_key sec_x = hw::ledger::decrypt(sec); @@ -795,17 +716,7 @@ namespace hw { hw::ledger::log_hexbuffer("get_subaddress_secret_key: [[OUT]] sub_sec", sub_sec_x.data, 32); #endif - reset_buffer(); - - this->buffer_send[0] = 0x00; - this->buffer_send[1] = INS_GET_SUBADDRESS_SECRET_KEY; - this->buffer_send[2] = 0x00; - this->buffer_send[3] = 0x00; - this->buffer_send[4] = 0x00; - offset = 5; - //options - this->buffer_send[offset] = 0; - offset += 1; + int offset = set_command_header_noopt(INS_GET_SUBADDRESS_SECRET_KEY); //sec memmove(this->buffer_send+offset, sec.data, 32); offset += 32; @@ -836,16 +747,7 @@ namespace hw { AUTO_LOCK_CMD(); int offset, sw; - reset_buffer(); - this->buffer_send[0] = 0x00; - this->buffer_send[1] = INS_VERIFY_KEY; - this->buffer_send[2] = 0x00; - this->buffer_send[3] = 0x00; - this->buffer_send[4] = 0x00; - offset = 5; - //options - this->buffer_send[offset] = 0x00; - offset += 1; + offset = set_command_header_noopt(INS_VERIFY_KEY); //sec memmove(this->buffer_send+offset, secret_key.data, 32); offset += 32; @@ -868,7 +770,6 @@ namespace hw { bool device_ledger::scalarmultKey(rct::key & aP, const rct::key &P, const rct::key &a) { AUTO_LOCK_CMD(); - int offset; #ifdef DEBUG_HWDEVICE const rct::key P_x = P; @@ -880,17 +781,7 @@ namespace hw { hw::ledger::log_hexbuffer("scalarmultKey: [[OUT]] aP", (char*)aP_x.bytes, 32); #endif - reset_buffer(); - - this->buffer_send[0] = 0x00; - this->buffer_send[1] = INS_SECRET_SCAL_MUL_KEY; - this->buffer_send[2] = 0x00; - this->buffer_send[3] = 0x00; - this->buffer_send[4] = 0x00; - offset = 5; - //options - this->buffer_send[offset] = 0; - offset += 1; + int offset = set_command_header_noopt(INS_SECRET_SCAL_MUL_KEY); //pub memmove(this->buffer_send+offset, P.bytes, 32); offset += 32; @@ -915,7 +806,6 @@ namespace hw { bool device_ledger::scalarmultBase(rct::key &aG, const rct::key &a) { AUTO_LOCK_CMD(); - int offset; #ifdef DEBUG_HWDEVICE const rct::key a_x = hw::ledger::decrypt(a); @@ -925,17 +815,7 @@ namespace hw { hw::ledger::log_hexbuffer("scalarmultKey: [[OUT]] aG", (char*)aG_x.bytes, 32); #endif - reset_buffer(); - - this->buffer_send[0] = 0x00; - this->buffer_send[1] = INS_SECRET_SCAL_MUL_BASE; - this->buffer_send[2] = 0x00; - this->buffer_send[3] = 0x00; - this->buffer_send[4] = 0x00; - offset = 5; - //options - this->buffer_send[offset] = 0; - offset += 1; + int offset = set_command_header_noopt(INS_SECRET_SCAL_MUL_BASE); //sec memmove(this->buffer_send+offset, a.bytes, 32); offset += 32; @@ -956,7 +836,6 @@ namespace hw { bool device_ledger::sc_secret_add( crypto::secret_key &r, const crypto::secret_key &a, const crypto::secret_key &b) { AUTO_LOCK_CMD(); - int offset; #ifdef DEBUG_HWDEVICE const crypto::secret_key a_x = hw::ledger::decrypt(a); @@ -965,17 +844,7 @@ namespace hw { this->controle_device->sc_secret_add(r_x, a_x, b_x); #endif - reset_buffer(); - - this->buffer_send[0] = 0x00; - this->buffer_send[1] = INS_SECRET_KEY_ADD; - this->buffer_send[2] = 0x00; - this->buffer_send[3] = 0x00; - this->buffer_send[4] = 0x00; - offset = 5; - //options - this->buffer_send[offset] = 0; - offset += 1; + int offset = set_command_header_noopt(INS_SECRET_KEY_ADD); //sec key memmove(this->buffer_send+offset, a.data, 32); offset += 32; @@ -1004,8 +873,6 @@ namespace hw { throw std::runtime_error("device generate key does not support recover"); } - int offset; - #ifdef DEBUG_HWDEVICE bool recover_x = recover; const crypto::secret_key recovery_key_x = recovery_key; @@ -1013,21 +880,7 @@ namespace hw { crypto::secret_key sec_x; #endif - reset_buffer(); - - this->buffer_send[0] = 0x00; - this->buffer_send[1] = INS_GENERATE_KEYPAIR; - this->buffer_send[2] = 0x00; - this->buffer_send[3] = 0x00; - this->buffer_send[4] = 0x00; - offset = 5; - //options - this->buffer_send[offset] = 0; - offset += 1; - - this->buffer_send[4] = offset-5; - this->length_send = offset; - this->exchange(); + send_simple(INS_GENERATE_KEYPAIR); //pub key memmove(pub.data, &this->buffer_recv[0], 32); @@ -1067,19 +920,7 @@ namespace hw { r = crypto::generate_key_derivation(pub, this->viewkey, derivation); } else { - int offset; - - reset_buffer(); - - this->buffer_send[0] = 0x00; - this->buffer_send[1] = INS_GEN_KEY_DERIVATION; - this->buffer_send[2] = 0x00; - this->buffer_send[3] = 0x00; - this->buffer_send[4] = 0x00; - offset = 5; - //options - this->buffer_send[offset] = 0; - offset += 1; + int offset = set_command_header_noopt(INS_GEN_KEY_DERIVATION); //pub memmove(this->buffer_send+offset, pub.data, 32); offset += 32; @@ -1128,7 +969,6 @@ namespace hw { bool device_ledger::derivation_to_scalar(const crypto::key_derivation &derivation, const size_t output_index, crypto::ec_scalar &res) { AUTO_LOCK_CMD(); - int offset; #ifdef DEBUG_HWDEVICE const crypto::key_derivation derivation_x = hw::ledger::decrypt(derivation); @@ -1140,17 +980,7 @@ namespace hw { hw::ledger::log_hexbuffer("derivation_to_scalar: [[OUT]] res ", res_x.data, 32); #endif - reset_buffer(); - - this->buffer_send[0] = 0x00; - this->buffer_send[1] = INS_DERIVATION_TO_SCALAR; - this->buffer_send[2] = 0x00; - this->buffer_send[3] = 0x00; - this->buffer_send[4] = 0x00; - offset = 5; - //options - this->buffer_send[offset] = 0; - offset += 1; + int offset = set_command_header_noopt(INS_DERIVATION_TO_SCALAR); //derivattion memmove(this->buffer_send+offset, derivation.data, 32); offset += 32; @@ -1178,7 +1008,6 @@ namespace hw { bool device_ledger::derive_secret_key(const crypto::key_derivation &derivation, const std::size_t output_index, const crypto::secret_key &sec, crypto::secret_key &derived_sec) { AUTO_LOCK_CMD(); - int offset; #ifdef DEBUG_HWDEVICE const crypto::key_derivation derivation_x = hw::ledger::decrypt(derivation); @@ -1192,17 +1021,7 @@ namespace hw { hw::ledger::log_hexbuffer("derive_secret_key: [[OUT]] derived_sec", derived_sec_x.data, 32); #endif - reset_buffer(); - - this->buffer_send[0] = 0x00; - this->buffer_send[1] = INS_DERIVE_SECRET_KEY; - this->buffer_send[2] = 0x00; - this->buffer_send[3] = 0x00; - this->buffer_send[4] = 0x00; - offset = 5; - //options - this->buffer_send[offset] = 0; - offset += 1; + int offset = set_command_header_noopt(INS_DERIVE_SECRET_KEY); //derivation memmove(this->buffer_send+offset, derivation.data, 32); offset += 32; @@ -1233,7 +1052,6 @@ namespace hw { bool device_ledger::derive_public_key(const crypto::key_derivation &derivation, const std::size_t output_index, const crypto::public_key &pub, crypto::public_key &derived_pub){ AUTO_LOCK_CMD(); - int offset; #ifdef DEBUG_HWDEVICE const crypto::key_derivation derivation_x = hw::ledger::decrypt(derivation); @@ -1247,17 +1065,7 @@ namespace hw { hw::ledger::log_hexbuffer("derive_public_key: [[OUT]] derived_pub ", derived_pub_x.data, 32); #endif - reset_buffer(); - - this->buffer_send[0] = 0x00; - this->buffer_send[1] = INS_DERIVE_PUBLIC_KEY; - this->buffer_send[2] = 0x00; - this->buffer_send[3] = 0x00; - this->buffer_send[4] = 0x00; - offset = 5; - //options - this->buffer_send[offset] = 0; - offset += 1; + int offset = set_command_header_noopt(INS_DERIVE_PUBLIC_KEY); //derivation memmove(this->buffer_send+offset, derivation.data, 32); offset += 32; @@ -1287,7 +1095,6 @@ namespace hw { bool device_ledger::secret_key_to_public_key(const crypto::secret_key &sec, crypto::public_key &pub) { AUTO_LOCK_CMD(); - int offset; #ifdef DEBUG_HWDEVICE const crypto::secret_key sec_x = hw::ledger::decrypt(sec); @@ -1300,17 +1107,7 @@ namespace hw { } #endif - reset_buffer(); - - this->buffer_send[0] = 0x00; - this->buffer_send[1] = INS_SECRET_KEY_TO_PUBLIC_KEY; - this->buffer_send[2] = 0x00; - this->buffer_send[3] = 0x00; - this->buffer_send[4] = 0x00; - offset = 5; - //options - this->buffer_send[offset] = 0; - offset += 1; + int offset = set_command_header_noopt(INS_SECRET_KEY_TO_PUBLIC_KEY); //sec key memmove(this->buffer_send+offset, sec.data, 32); offset += 32; @@ -1331,7 +1128,6 @@ namespace hw { bool device_ledger::generate_key_image(const crypto::public_key &pub, const crypto::secret_key &sec, crypto::key_image &image){ AUTO_LOCK_CMD(); - int offset; #ifdef DEBUG_HWDEVICE const crypto::public_key pub_x = pub; @@ -1343,17 +1139,7 @@ namespace hw { hw::ledger::log_hexbuffer("generate_key_image: [[OUT]] image ", image_x.data, 32); #endif - reset_buffer(); - - this->buffer_send[0] = 0x00; - this->buffer_send[1] = INS_GEN_KEY_IMAGE; - this->buffer_send[2] = 0x00; - this->buffer_send[3] = 0x00; - this->buffer_send[4] = 0x00; - offset = 5; - //options - this->buffer_send[offset] = 0; - offset += 1; + int offset = set_command_header_noopt(INS_GEN_KEY_IMAGE); //pub memmove(this->buffer_send+offset, pub.data, 32); offset += 32; @@ -1381,20 +1167,10 @@ namespace hw { bool device_ledger::open_tx(crypto::secret_key &tx_key) { AUTO_LOCK_CMD(); - int offset; - reset_buffer(); key_map.clear(); + int offset = set_command_header_noopt(INS_OPEN_TX, 0x01); - this->buffer_send[0] = 0x00; - this->buffer_send[1] = INS_OPEN_TX; - this->buffer_send[2] = 0x01; - this->buffer_send[3] = 0x00; - this->buffer_send[4] = 0x00; - offset = 5; - //options - this->buffer_send[offset] = 0x00; - offset += 1; //account this->buffer_send[offset+0] = 0x00; this->buffer_send[offset+1] = 0x00; @@ -1413,7 +1189,6 @@ namespace hw { bool device_ledger::encrypt_payment_id(crypto::hash8 &payment_id, const crypto::public_key &public_key, const crypto::secret_key &secret_key) { AUTO_LOCK_CMD(); - int offset; #ifdef DEBUG_HWDEVICE const crypto::public_key public_key_x = public_key; @@ -1422,17 +1197,7 @@ namespace hw { this->controle_device->encrypt_payment_id(payment_id_x, public_key_x, secret_key_x); #endif - reset_buffer(); - - this->buffer_send[0] = 0x00; - this->buffer_send[1] = INS_STEALTH; - this->buffer_send[2] = 0x00; - this->buffer_send[3] = 0x00; - this->buffer_send[4] = 0x00; - offset = 5; - //options - this->buffer_send[offset] = 0x00; - offset += 1; + int offset = set_command_header_noopt(INS_STEALTH); //pub memmove(&this->buffer_send[offset], public_key.data, 32); offset += 32; @@ -1464,7 +1229,6 @@ namespace hw { bool device_ledger::ecdhEncode(rct::ecdhTuple & unmasked, const rct::key & AKout) { AUTO_LOCK_CMD(); - int offset; #ifdef DEBUG_HWDEVICE const rct::key AKout_x = hw::ledger::decrypt(AKout); @@ -1472,17 +1236,7 @@ namespace hw { this->controle_device->ecdhEncode(unmasked_x, AKout_x); #endif - reset_buffer(); - - this->buffer_send[0] = 0x00; - this->buffer_send[1] = INS_BLIND; - this->buffer_send[2] = 0x00; - this->buffer_send[3] = 0x00; - this->buffer_send[4] = 0x00; - offset = 5; - //options - this->buffer_send[offset] = 0x00; - offset += 1; + int offset = set_command_header_noopt(INS_BLIND); // AKout memmove(this->buffer_send+offset, AKout.bytes, 32); offset += 32; @@ -1512,7 +1266,6 @@ namespace hw { bool device_ledger::ecdhDecode(rct::ecdhTuple & masked, const rct::key & AKout) { AUTO_LOCK_CMD(); - int offset; #ifdef DEBUG_HWDEVICE const rct::key AKout_x = hw::ledger::decrypt(AKout); @@ -1520,17 +1273,8 @@ namespace hw { this->controle_device->ecdhDecode(masked_x, AKout_x); #endif - reset_buffer(); + int offset = set_command_header_noopt(INS_UNBLIND); - this->buffer_send[0] = 0x00; - this->buffer_send[1] = INS_UNBLIND; - this->buffer_send[2] = 0x00; - this->buffer_send[3] = 0x00; - this->buffer_send[4] = 0x00; - offset = 5; - //options - this->buffer_send[offset] = 0x00; - offset += 1; // AKout memmove(this->buffer_send+offset, AKout.bytes, 32); offset += 32; @@ -1580,16 +1324,7 @@ namespace hw { data = blob.data(); // ====== u8 type, varint txnfee ====== - int offset; - - reset_buffer(); - - this->buffer_send[0] = 0x00; - this->buffer_send[1] = INS_VALIDATE; - this->buffer_send[2] = 0x01; - this->buffer_send[3] = 0x01; - this->buffer_send[4] = 0x00; - offset = 5; + int offset = set_command_header(INS_VALIDATE, 0x01, 0x01); //options this->buffer_send[offset] = (inputs_size == 0)?0x00:0x80; offset += 1; @@ -1617,13 +1352,7 @@ namespace hw { //pseudoOuts if ((type == rct::RCTTypeSimple) || (type == rct::RCTTypeSimpleBulletproof)) { for ( i = 0; i < inputs_size; i++) { - reset_buffer(); - this->buffer_send[0] = 0x00; - this->buffer_send[1] = INS_VALIDATE; - this->buffer_send[2] = 0x01; - this->buffer_send[3] = i+2; - this->buffer_send[4] = 0x00; - offset = 5; + offset = set_command_header(INS_VALIDATE, 0x01, i+2); //options this->buffer_send[offset] = (i==inputs_size-1)? 0x00:0x80; offset += 1; @@ -1650,14 +1379,7 @@ namespace hw { log_hexbuffer("Pout not found", (char*)outPk[i].dest.bytes, 32); CHECK_AND_ASSERT_THROW_MES(found, "Pout not found"); } - reset_buffer(); - - this->buffer_send[0] = 0x00; - this->buffer_send[1] = INS_VALIDATE; - this->buffer_send[2] = 0x02; - this->buffer_send[3] = i+1; - this->buffer_send[4] = 0x00; - offset = 5; + offset = set_command_header(INS_VALIDATE, 0x02, i+1); //options this->buffer_send[offset] = (i==outputs_size-1)? 0x00:0x80 ; offset += 1; @@ -1702,14 +1424,7 @@ namespace hw { // ====== C[], message, proof====== C_offset = kv_offset; for (i = 0; i < outputs_size; i++) { - reset_buffer(); - - this->buffer_send[0] = 0x00; - this->buffer_send[1] = INS_VALIDATE; - this->buffer_send[2] = 0x03; - this->buffer_send[3] = i+1; - this->buffer_send[4] = 0x00; - offset = 5; + offset = set_command_header(INS_VALIDATE, 0x03, i+1); //options this->buffer_send[offset] = 0x80 ; offset += 1; @@ -1724,17 +1439,7 @@ namespace hw { } - reset_buffer(); - - this->buffer_send[0] = 0x00; - this->buffer_send[1] = INS_VALIDATE; - this->buffer_send[2] = 0x03; - this->buffer_send[3] = i+1; - this->buffer_send[4] = 0x00; - offset = 5; - //options - this->buffer_send[offset] = 0x00; - offset += 1; + offset = set_command_header_noopt(INS_VALIDATE, 0x03, i+1); //message memmove(this->buffer_send+offset, hashes[0].bytes,32); offset += 32; @@ -1759,7 +1464,6 @@ namespace hw { bool device_ledger::mlsag_prepare(const rct::key &H, const rct::key &xx, rct::key &a, rct::key &aG, rct::key &aHP, rct::key &II) { AUTO_LOCK_CMD(); - int offset; unsigned char options; #ifdef DEBUG_HWDEVICE @@ -1771,17 +1475,7 @@ namespace hw { rct::key II_x; #endif - reset_buffer(); - - this->buffer_send[0] = 0x00; - this->buffer_send[1] = INS_MLSAG; - this->buffer_send[2] = 0x01; - this->buffer_send[3] = 0x00; - this->buffer_send[4] = 0x00; - offset = 5; - //options - this->buffer_send[offset] = 0x00; - offset += 1; + int offset = set_command_header_noopt(INS_MLSAG, 0x01); //value H memmove(this->buffer_send+offset, H.bytes, 32); offset += 32; @@ -1814,7 +1508,6 @@ namespace hw { bool device_ledger::mlsag_prepare(rct::key &a, rct::key &aG) { AUTO_LOCK_CMD(); - int offset; unsigned char options; #ifdef DEBUG_HWDEVICE @@ -1822,21 +1515,7 @@ namespace hw { rct::key aG_x; #endif - reset_buffer(); - - this->buffer_send[0] = 0x00; - this->buffer_send[1] = INS_MLSAG; - this->buffer_send[2] = 0x01; - this->buffer_send[3] = 0x00; - this->buffer_send[4] = 0x00; - offset = 5; - //options - this->buffer_send[offset] = 0x00; - offset += 1; - - this->buffer_send[4] = offset-5; - this->length_send = offset; - this->exchange(); + send_simple(INS_MLSAG, 0x01); memmove(a.bytes, &this->buffer_recv[32*0], 32); memmove(aG.bytes, &this->buffer_recv[32*1], 32); @@ -1852,7 +1531,6 @@ namespace hw { bool device_ledger::mlsag_hash(const rct::keyV &long_message, rct::key &c) { AUTO_LOCK_CMD(); - int offset; unsigned char options; size_t cnt; @@ -1864,14 +1542,7 @@ namespace hw { cnt = long_message.size(); for (size_t i = 0; ibuffer_send[0] = 0x00; - this->buffer_send[1] = INS_MLSAG; - this->buffer_send[2] = 0x02; - this->buffer_send[3] = i+1; - this->buffer_send[4] = 0x00; - offset = 5; + int offset = set_command_header(INS_MLSAG, 0x02, i+1); //options this->buffer_send[offset] = (i==(cnt-1))?0x00:0x80; //last @@ -1896,7 +1567,6 @@ namespace hw { bool device_ledger::mlsag_sign(const rct::key &c, const rct::keyV &xx, const rct::keyV &alpha, const size_t rows, const size_t dsRows, rct::keyV &ss) { AUTO_LOCK_CMD(); - int offset; CHECK_AND_ASSERT_THROW_MES(dsRows<=rows, "dsRows greater than rows"); CHECK_AND_ASSERT_THROW_MES(xx.size() == rows, "xx size does not match rows"); @@ -1914,14 +1584,7 @@ namespace hw { #endif for (size_t j = 0; j < dsRows; j++) { - reset_buffer(); - - this->buffer_send[0] = 0x00; - this->buffer_send[1] = INS_MLSAG; - this->buffer_send[2] = 0x03; - this->buffer_send[3] = j+1; - this->buffer_send[4] = 0x00; - offset = 5; + int offset = set_command_header(INS_MLSAG, 0x03, j+1); //options this->buffer_send[offset] = 0x00; if (j==(dsRows-1)) { @@ -1958,24 +1621,7 @@ namespace hw { bool device_ledger::close_tx() { AUTO_LOCK_CMD(); - int offset; - - reset_buffer(); - - this->buffer_send[0] = 0x00; - this->buffer_send[1] = INS_CLOSE_TX; - this->buffer_send[2] = 0x00; - this->buffer_send[3] = 0x00; - this->buffer_send[4] = 0x00; - offset = 5; - //options - this->buffer_send[offset] = 0x00; - offset += 1; - - this->buffer_send[4] = offset-5; - this->length_send = offset; - this->exchange(); - + send_simple(INS_CLOSE_TX); return true; } diff --git a/src/device/device_ledger.hpp b/src/device/device_ledger.hpp index b62bdf959e1..c30a38aca23 100644 --- a/src/device/device_ledger.hpp +++ b/src/device/device_ledger.hpp @@ -102,6 +102,9 @@ namespace hw { void logRESP(void); unsigned int exchange(unsigned int ok=0x9000, unsigned int mask=0xFFFF); void reset_buffer(void); + int set_command_header(BYTE ins, BYTE p1 = 0x00, BYTE p2 = 0x00); + int set_command_header_noopt(BYTE ins, BYTE p1 = 0x00, BYTE p2 = 0x00); + void send_simple(BYTE ins, BYTE p1 = 0x00); // hw running mode device_mode mode; From 50af3579ee735ad6a9d95014f2d9b5141d05769c Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 19 May 2018 23:53:05 +0100 Subject: [PATCH 0121/1404] alt_chain_info can now give more info about a particular alt chain --- src/cryptonote_core/blockchain.cpp | 11 +++++---- src/cryptonote_core/blockchain.h | 2 +- src/daemon/command_parser_executor.cpp | 6 ++--- src/daemon/command_server.cpp | 1 + src/daemon/rpc_command_executor.cpp | 32 ++++++++++++++++++++----- src/daemon/rpc_command_executor.h | 2 +- src/rpc/core_rpc_server.cpp | 16 +++++++++++-- src/rpc/core_rpc_server_commands_defs.h | 6 ++++- 8 files changed, 57 insertions(+), 19 deletions(-) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index fbb2a222340..5d7022917df 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -4393,9 +4393,9 @@ std::map> Blockchain:: get_ou return m_db->get_output_histogram(amounts, unlocked, recent_cutoff, min_count); } -std::list> Blockchain::get_alternative_chains() const +std::list>> Blockchain::get_alternative_chains() const { - std::list> chains; + std::list>> chains; for (const auto &i: m_alternative_chains) { @@ -4411,15 +4411,16 @@ std::list> Blockchain::get_a } if (!found) { - uint64_t length = 1; + std::vector chain; auto h = i.second.bl.prev_id; + chain.push_back(top); blocks_ext_by_hash::const_iterator prev; while ((prev = m_alternative_chains.find(h)) != m_alternative_chains.end()) { + chain.push_back(h); h = prev->second.bl.prev_id; - ++length; } - chains.push_back(std::make_pair(i.second, length)); + chains.push_back(std::make_pair(i.second, chain)); } } return chains; diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index ef736d1e7cd..d5abba99839 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -939,7 +939,7 @@ namespace cryptonote * * @return a list of chains */ - std::list> get_alternative_chains() const; + std::list>> get_alternative_chains() const; void add_txpool_tx(transaction &tx, const txpool_tx_meta_t &meta); void update_txpool_tx(const crypto::hash &txid, const txpool_tx_meta_t &meta); diff --git a/src/daemon/command_parser_executor.cpp b/src/daemon/command_parser_executor.cpp index 34d9fb4c8b1..36e040ab55e 100644 --- a/src/daemon/command_parser_executor.cpp +++ b/src/daemon/command_parser_executor.cpp @@ -599,13 +599,13 @@ bool t_command_parser_executor::print_coinbase_tx_sum(const std::vector& args) { - if(args.size()) + if(args.size() > 1) { - std::cout << "No parameters allowed" << std::endl; + std::cout << "usage: alt_chain_info [block_hash]" << std::endl; return false; } - return m_executor.alt_chain_info(); + return m_executor.alt_chain_info(args.size() == 1 ? args[0] : ""); } bool t_command_parser_executor::print_blockchain_dynamic_stats(const std::vector& args) diff --git a/src/daemon/command_server.cpp b/src/daemon/command_server.cpp index 14460359725..35504f2c903 100644 --- a/src/daemon/command_server.cpp +++ b/src/daemon/command_server.cpp @@ -255,6 +255,7 @@ t_command_server::t_command_server( m_command_lookup.set_handler( "alt_chain_info" , std::bind(&t_command_parser_executor::alt_chain_info, &m_parser, p::_1) + , "alt_chain_info [blockhash]" , "Print the information about alternative chains." ); m_command_lookup.set_handler( diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp index 48785344172..0b5b05d3d4c 100644 --- a/src/daemon/rpc_command_executor.cpp +++ b/src/daemon/rpc_command_executor.cpp @@ -1628,7 +1628,7 @@ bool t_rpc_command_executor::print_coinbase_tx_sum(uint64_t height, uint64_t cou return true; } -bool t_rpc_command_executor::alt_chain_info() +bool t_rpc_command_executor::alt_chain_info(const std::string &tip) { cryptonote::COMMAND_RPC_GET_INFO::request ireq; cryptonote::COMMAND_RPC_GET_INFO::response ires; @@ -1663,12 +1663,32 @@ bool t_rpc_command_executor::alt_chain_info() } } - tools::msg_writer() << boost::lexical_cast(res.chains.size()) << " alternate chains found:"; - for (const auto &chain: res.chains) + if (tip.empty()) { - uint64_t start_height = (chain.height - chain.length + 1); - tools::msg_writer() << chain.length << " blocks long, from height " << start_height << " (" << (ires.height - start_height - 1) - << " deep), diff " << chain.difficulty << ": " << chain.block_hash; + tools::msg_writer() << boost::lexical_cast(res.chains.size()) << " alternate chains found:"; + for (const auto &chain: res.chains) + { + uint64_t start_height = (chain.height - chain.length + 1); + tools::msg_writer() << chain.length << " blocks long, from height " << start_height << " (" << (ires.height - start_height - 1) + << " deep), diff " << chain.difficulty << ": " << chain.block_hash; + } + } + else + { + const auto i = std::find_if(res.chains.begin(), res.chains.end(), [&tip](cryptonote::COMMAND_RPC_GET_ALTERNATE_CHAINS::chain_info &info){ return info.block_hash == tip; }); + if (i != res.chains.end()) + { + const auto &chain = *i; + tools::success_msg_writer() << "Found alternate chain with tip " << tip; + uint64_t start_height = (chain.height - chain.length + 1); + tools::msg_writer() << chain.length << " blocks long, from height " << start_height << " (" << (ires.height - start_height - 1) + << " deep), diff " << chain.difficulty << ":"; + for (const std::string &block_id: chain.block_hashes) + tools::msg_writer() << " " << block_id; + tools::msg_writer() << "Chain parent on main chain: " << chain.main_chain_parent_block; + } + else + tools::fail_msg_writer() << "Block hash " << tip << " is not the tip of any known alternate chain"; } return true; } diff --git a/src/daemon/rpc_command_executor.h b/src/daemon/rpc_command_executor.h index 46168c93b9e..9e6010c5b38 100644 --- a/src/daemon/rpc_command_executor.h +++ b/src/daemon/rpc_command_executor.h @@ -143,7 +143,7 @@ class t_rpc_command_executor final { bool print_coinbase_tx_sum(uint64_t height, uint64_t count); - bool alt_chain_info(); + bool alt_chain_info(const std::string &tip); bool print_blockchain_dynamic_stats(uint64_t nblocks); diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index dc7b6b30fa2..e87078398ec 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -1774,10 +1774,22 @@ namespace cryptonote PERF_TIMER(on_get_alternate_chains); try { - std::list> chains = m_core.get_blockchain_storage().get_alternative_chains(); + std::list>> chains = m_core.get_blockchain_storage().get_alternative_chains(); for (const auto &i: chains) { - res.chains.push_back(COMMAND_RPC_GET_ALTERNATE_CHAINS::chain_info{epee::string_tools::pod_to_hex(get_block_hash(i.first.bl)), i.first.height, i.second, i.first.cumulative_difficulty}); + res.chains.push_back(COMMAND_RPC_GET_ALTERNATE_CHAINS::chain_info{epee::string_tools::pod_to_hex(get_block_hash(i.first.bl)), i.first.height, i.second.size(), i.first.cumulative_difficulty, {}, std::string()}); + res.chains.back().block_hashes.reserve(i.second.size()); + for (const crypto::hash &block_id: i.second) + res.chains.back().block_hashes.push_back(epee::string_tools::pod_to_hex(block_id)); + if (i.first.height < i.second.size()) + { + res.status = "Error finding alternate chain attachment point"; + return true; + } + cryptonote::block main_chain_parent_block; + try { main_chain_parent_block = m_core.get_blockchain_storage().get_db().get_block_from_height(i.first.height - i.second.size()); } + catch (const std::exception &e) { res.status = "Error finding alternate chain attachment point"; return true; } + res.chains.back().main_chain_parent_block = epee::string_tools::pod_to_hex(get_block_hash(main_chain_parent_block)); } res.status = CORE_RPC_STATUS_OK; } diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index 70e18684871..2fb7e9ea335 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -49,7 +49,7 @@ namespace cryptonote // advance which version they will stop working with // Don't go over 32767 for any of these #define CORE_RPC_VERSION_MAJOR 1 -#define CORE_RPC_VERSION_MINOR 20 +#define CORE_RPC_VERSION_MINOR 21 #define MAKE_CORE_RPC_VERSION(major,minor) (((major)<<16)|(minor)) #define CORE_RPC_VERSION MAKE_CORE_RPC_VERSION(CORE_RPC_VERSION_MAJOR, CORE_RPC_VERSION_MINOR) @@ -2086,12 +2086,16 @@ namespace cryptonote uint64_t height; uint64_t length; uint64_t difficulty; + std::vector block_hashes; + std::string main_chain_parent_block; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(block_hash) KV_SERIALIZE(height) KV_SERIALIZE(length) KV_SERIALIZE(difficulty) + KV_SERIALIZE(block_hashes) + KV_SERIALIZE(main_chain_parent_block) END_KV_SERIALIZE_MAP() }; From a2b557fe5c3853be03f33bdabd88eb2c0ffd8136 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 15 Apr 2018 19:16:13 +0100 Subject: [PATCH 0122/1404] wallet2: use std::move on containers where appropriate --- src/wallet/wallet2.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index bb0953689d9..4d0f31fde40 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -1695,8 +1695,8 @@ void wallet2::pull_blocks(uint64_t start_height, uint64_t &blocks_start_height, boost::lexical_cast(res.output_indices.size()) + ") sizes from daemon"); blocks_start_height = res.start_height; - blocks = res.blocks; - o_indices = res.output_indices; + blocks = std::move(res.blocks); + o_indices = std::move(res.output_indices); } //---------------------------------------------------------------------------------------------------- void wallet2::pull_hashes(uint64_t start_height, uint64_t &blocks_start_height, const std::list &short_chain_history, std::list &hashes) @@ -1714,7 +1714,7 @@ void wallet2::pull_hashes(uint64_t start_height, uint64_t &blocks_start_height, THROW_WALLET_EXCEPTION_IF(res.status != CORE_RPC_STATUS_OK, error::get_hashes_error, res.status); blocks_start_height = res.start_height; - hashes = res.m_block_ids; + hashes = std::move(res.m_block_ids); } //---------------------------------------------------------------------------------------------------- void wallet2::process_blocks(uint64_t start_height, const std::list &blocks, const std::vector &o_indices, uint64_t& blocks_added) @@ -2297,7 +2297,7 @@ void wallet2::refresh(uint64_t start_height, uint64_t & blocks_fetched, bool& re // switch to the new blocks from the daemon blocks_start_height = next_blocks_start_height; - blocks = next_blocks; + blocks = std::move(next_blocks); o_indices = next_o_indices; // handle error from async fetching thread From 6795bd0d4eb680f378ea779a130cefad618e11d2 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 15 Apr 2018 21:58:09 +0100 Subject: [PATCH 0123/1404] rpc: add option to skip coinbase info in get_blocks.bin --- src/rpc/core_rpc_server.cpp | 11 +++++++---- src/rpc/core_rpc_server_commands_defs.h | 2 ++ src/wallet/wallet2.cpp | 1 + 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index dc7b6b30fa2..4c14e5c64fe 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -249,11 +249,14 @@ namespace cryptonote res.status = "Invalid block"; return false; } - bool r = m_core.get_tx_outputs_gindexs(get_transaction_hash(b.miner_tx), res.output_indices.back().indices.back().indices); - if (!r) + if (!req.no_miner_tx) { - res.status = "Failed"; - return false; + bool r = m_core.get_tx_outputs_gindexs(get_transaction_hash(b.miner_tx), res.output_indices.back().indices.back().indices); + if (!r) + { + res.status = "Failed"; + return false; + } } size_t txidx = 0; ntxes += bd.second.size(); diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index 1e624da1b83..abebad27baf 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -83,10 +83,12 @@ namespace cryptonote std::list block_ids; //*first 10 blocks id goes sequential, next goes in pow(2,n) offset, like 2, 4, 8, 16, 32, 64 and so on, and the last one is always genesis block */ uint64_t start_height; bool prune; + bool no_miner_tx; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE_CONTAINER_POD_AS_BLOB(block_ids) KV_SERIALIZE(start_height) KV_SERIALIZE(prune) + KV_SERIALIZE_OPT(no_miner_tx, false) END_KV_SERIALIZE_MAP() }; diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 4d0f31fde40..98bd6c31883 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -1684,6 +1684,7 @@ void wallet2::pull_blocks(uint64_t start_height, uint64_t &blocks_start_height, } req.start_height = start_height; + req.no_miner_tx = m_refresh_type == RefreshNoCoinbase; m_daemon_rpc_mutex.lock(); bool r = net_utils::invoke_http_bin("/getblocks.bin", req, res, m_http_client, rpc_timeout); m_daemon_rpc_mutex.unlock(); From 209ec963b5ed0a3ad98f159b9f500c28bb3e7d78 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 15 Apr 2018 22:17:58 +0100 Subject: [PATCH 0124/1404] rpc: sanity check on number of txes in a block --- src/rpc/core_rpc_server.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index 4c14e5c64fe..83cf5ba3c70 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -258,6 +258,12 @@ namespace cryptonote return false; } } + if(b.tx_hashes.size() != bd.second.size()) + { + MERROR("block " << get_block_hash(b) << ": tx_hashes.size() " << b.tx_hashes.size() << ", bd.second.size() " << bd.second.size()); + res.status = "Failed"; + return false; + } size_t txidx = 0; ntxes += bd.second.size(); for (std::list::iterator i = bd.second.begin(); i != bd.second.end(); ++i) From ed2c81ed95be71bace897e62a0c241068cfde7be Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 16 Apr 2018 00:16:02 +0100 Subject: [PATCH 0125/1404] replace std::list with std::vector on some hot paths also use reserve where appropriate --- .../blockchain_import.cpp | 8 +-- src/cryptonote_basic/connection_context.h | 2 +- src/cryptonote_core/blockchain.cpp | 66 ++++++++++++------ src/cryptonote_core/blockchain.h | 16 ++--- src/cryptonote_core/cryptonote_core.cpp | 42 ++++++------ src/cryptonote_core/cryptonote_core.h | 40 +++++------ src/cryptonote_core/tx_pool.cpp | 14 +++- src/cryptonote_core/tx_pool.h | 6 +- src/cryptonote_protocol/block_queue.cpp | 18 ++--- src/cryptonote_protocol/block_queue.h | 20 +++--- .../cryptonote_protocol_defs.h | 16 ++--- .../cryptonote_protocol_handler.inl | 67 ++++++++++--------- src/rpc/core_rpc_server.cpp | 37 +++++----- src/rpc/core_rpc_server_commands_defs.h | 18 ++--- src/rpc/daemon_handler.cpp | 30 ++------- src/rpc/daemon_messages.h | 2 +- src/wallet/wallet2.cpp | 22 +++--- src/wallet/wallet2.h | 8 +-- tests/core_proxy/core_proxy.cpp | 2 +- tests/core_proxy/core_proxy.h | 10 +-- tests/core_tests/chain_switch_1.cpp | 10 +-- tests/core_tests/chain_switch_1.h | 4 +- tests/core_tests/chaingen.h | 2 +- tests/core_tests/chaingen001.cpp | 2 +- tests/core_tests/double_spend.cpp | 2 +- tests/core_tests/double_spend.inl | 4 +- tests/core_tests/ring_signature_1.cpp | 12 ++-- tests/unit_tests/ban.cpp | 10 +-- 28 files changed, 257 insertions(+), 233 deletions(-) diff --git a/src/blockchain_utilities/blockchain_import.cpp b/src/blockchain_utilities/blockchain_import.cpp index caa549c1327..3078ec31b2c 100644 --- a/src/blockchain_utilities/blockchain_import.cpp +++ b/src/blockchain_utilities/blockchain_import.cpp @@ -164,7 +164,7 @@ int pop_blocks(cryptonote::core& core, int num_blocks) return num_blocks; } -int check_flush(cryptonote::core &core, std::list &blocks, bool force) +int check_flush(cryptonote::core &core, std::vector &blocks, bool force) { if (blocks.empty()) return 0; @@ -176,7 +176,7 @@ int check_flush(cryptonote::core &core, std::list &blocks, if (!force && new_height % HASH_OF_HASHES_STEP) return 0; - std::list hashes; + std::vector hashes; for (const auto &b: blocks) { cryptonote::block block; @@ -312,7 +312,7 @@ int import_from_file(cryptonote::core& core, const std::string& import_file_path MINFO("Reading blockchain from bootstrap file..."); std::cout << ENDL; - std::list blocks; + std::vector blocks; // Skip to start_height before we start adding. { @@ -437,7 +437,7 @@ int import_from_file(cryptonote::core& core, const std::string& import_file_path { cryptonote::blobdata block; cryptonote::block_to_blob(bp.block, block); - std::list txs; + std::vector txs; for (const auto &tx: bp.txs) { txs.push_back(cryptonote::blobdata()); diff --git a/src/cryptonote_basic/connection_context.h b/src/cryptonote_basic/connection_context.h index 3f4651565da..eb73ab0eaa9 100644 --- a/src/cryptonote_basic/connection_context.h +++ b/src/cryptonote_basic/connection_context.h @@ -52,7 +52,7 @@ namespace cryptonote }; state m_state; - std::list m_needed_objects; + std::vector m_needed_objects; std::unordered_set m_requested_objects; uint64_t m_remote_blockchain_height; uint64_t m_last_response_height; diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index ad604deef84..7518cc4e1a6 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -242,6 +242,7 @@ bool Blockchain::scan_outputkeys_for_indexes(size_t tx_version, const txin_to_ke MDEBUG("Additional outputs needed: " << absolute_offsets.size() - outputs.size()); std::vector < uint64_t > add_offsets; std::vector add_outputs; + add_outputs.reserve(absolute_offsets.size() - outputs.size()); for (size_t i = outputs.size(); i < absolute_offsets.size(); i++) add_offsets.push_back(absolute_offsets[i]); try @@ -850,6 +851,11 @@ difficulty_type Blockchain::get_difficulty_for_next_block() timestamps.clear(); difficulties.clear(); + if (height > offset) + { + timestamps.reserve(height - offset); + difficulties.reserve(height - offset); + } for (; offset < height; offset++) { timestamps.push_back(m_db->get_block_timestamp(offset)); @@ -1170,6 +1176,7 @@ void Blockchain::get_last_n_blocks_sizes(std::vector& sz, size_t count) m_db->block_txn_start(true); // add size of last blocks to vector (or less, if blockchain size < count) size_t start_offset = h - std::min(h, count); + sz.reserve(sz.size() + h - start_offset); for(size_t i = start_offset; i < h; i++) { sz.push_back(m_db->get_block_size(i)); @@ -1367,6 +1374,7 @@ bool Blockchain::complete_timestamps_vector(uint64_t start_top_height, std::vect size_t need_elements = BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW - timestamps.size(); CHECK_AND_ASSERT_MES(start_top_height < m_db->height(), false, "internal error: passed start_height not < " << " m_db->height() -- " << start_top_height << " >= " << m_db->height()); size_t stop_offset = start_top_height > need_elements ? start_top_height - need_elements : 0; + timestamps.reserve(timestamps.size() + start_top_height - stop_offset); while (start_top_height != stop_offset) { timestamps.push_back(m_db->get_block_timestamp(start_top_height)); @@ -1566,7 +1574,7 @@ bool Blockchain::handle_alternative_block(const block& b, const crypto::hash& id return true; } //------------------------------------------------------------------ -bool Blockchain::get_blocks(uint64_t start_offset, size_t count, std::list>& blocks, std::list& txs) const +bool Blockchain::get_blocks(uint64_t start_offset, size_t count, std::vector>& blocks, std::vector& txs) const { LOG_PRINT_L3("Blockchain::" << __func__); CRITICAL_REGION_LOCAL(m_blockchain_lock); @@ -1580,7 +1588,7 @@ bool Blockchain::get_blocks(uint64_t start_offset, size_t count, std::list missed_ids; + std::vector missed_ids; get_transactions_blobs(blk.second.tx_hashes, txs, missed_ids); CHECK_AND_ASSERT_MES(!missed_ids.size(), false, "has missed transactions in own block in main blockchain"); } @@ -1588,14 +1596,16 @@ bool Blockchain::get_blocks(uint64_t start_offset, size_t count, std::list>& blocks) const +bool Blockchain::get_blocks(uint64_t start_offset, size_t count, std::vector>& blocks) const { LOG_PRINT_L3("Blockchain::" << __func__); CRITICAL_REGION_LOCAL(m_blockchain_lock); - if(start_offset >= m_db->height()) + const uint64_t height = m_db->height(); + if(start_offset >= height) return false; - for(size_t i = start_offset; i < start_offset + count && i < m_db->height();i++) + blocks.reserve(blocks.size() + height - start_offset); + for(size_t i = start_offset; i < start_offset + count && i < height;i++) { blocks.push_back(std::make_pair(m_db->get_block_blob_from_height(i), block())); if (!parse_and_validate_block_from_blob(blocks.back().first, blocks.back().second)) @@ -1620,13 +1630,13 @@ bool Blockchain::handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NO CRITICAL_REGION_LOCAL(m_blockchain_lock); m_db->block_txn_start(true); rsp.current_blockchain_height = get_current_blockchain_height(); - std::list> blocks; + std::vector> blocks; get_blocks(arg.blocks, blocks, rsp.missed_ids); for (const auto& bl: blocks) { - std::list missed_tx_ids; - std::list txs; + std::vector missed_tx_ids; + std::vector txs; // FIXME: s/rsp.missed_ids/missed_tx_id/ ? Seems like rsp.missed_ids // is for missed blocks, not missed transactions as well. @@ -1642,8 +1652,8 @@ bool Blockchain::handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NO // append missed transaction hashes to response missed_ids field, // as done below if any standalone transactions were requested // and missed. - rsp.missed_ids.splice(rsp.missed_ids.end(), missed_tx_ids); - m_db->block_txn_stop(); + rsp.missed_ids.insert(rsp.missed_ids.end(), missed_tx_ids.begin(), missed_tx_ids.end()); + m_db->block_txn_stop(); return false; } @@ -1656,7 +1666,7 @@ bool Blockchain::handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NO e.txs.push_back(tx); } //get another transactions, if need - std::list txs; + std::vector txs; get_transactions_blobs(arg.txs, txs, rsp.missed_ids); //pack aside transactions for (const auto& tx: txs) @@ -1666,11 +1676,12 @@ bool Blockchain::handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NO return true; } //------------------------------------------------------------------ -bool Blockchain::get_alternative_blocks(std::list& blocks) const +bool Blockchain::get_alternative_blocks(std::vector& blocks) const { LOG_PRINT_L3("Blockchain::" << __func__); CRITICAL_REGION_LOCAL(m_blockchain_lock); + blocks.reserve(m_alternative_chains.size()); for (const auto& alt_bl: m_alternative_chains) { blocks.push_back(alt_bl.second.bl); @@ -2090,6 +2101,9 @@ uint64_t Blockchain::block_difficulty(uint64_t i) const return 0; } //------------------------------------------------------------------ +template void reserve_container(std::vector &v, size_t N) { v.reserve(N); } +template void reserve_container(std::list &v, size_t N) { } +//------------------------------------------------------------------ //TODO: return type should be void, throw on exception // alternatively, return true only if no blocks missed template @@ -2098,6 +2112,7 @@ bool Blockchain::get_blocks(const t_ids_container& block_ids, t_blocks_container LOG_PRINT_L3("Blockchain::" << __func__); CRITICAL_REGION_LOCAL(m_blockchain_lock); + reserve_container(blocks, block_ids.size()); for (const auto& block_hash : block_ids) { try @@ -2132,6 +2147,7 @@ bool Blockchain::get_transactions_blobs(const t_ids_container& txs_ids, t_tx_con LOG_PRINT_L3("Blockchain::" << __func__); CRITICAL_REGION_LOCAL(m_blockchain_lock); + reserve_container(txs, txs_ids.size()); for (const auto& tx_hash : txs_ids) { try @@ -2158,6 +2174,7 @@ bool Blockchain::get_transactions(const t_ids_container& txs_ids, t_tx_container LOG_PRINT_L3("Blockchain::" << __func__); CRITICAL_REGION_LOCAL(m_blockchain_lock); + reserve_container(txs, txs_ids.size()); for (const auto& tx_hash : txs_ids) { try @@ -2186,7 +2203,7 @@ bool Blockchain::get_transactions(const t_ids_container& txs_ids, t_tx_container // Find the split point between us and foreign blockchain and return // (by reference) the most recent common block hash along with up to // BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT additional (more recent) hashes. -bool Blockchain::find_blockchain_supplement(const std::list& qblock_ids, std::list& hashes, uint64_t& start_height, uint64_t& current_height) const +bool Blockchain::find_blockchain_supplement(const std::list& qblock_ids, std::vector& hashes, uint64_t& start_height, uint64_t& current_height) const { LOG_PRINT_L3("Blockchain::" << __func__); CRITICAL_REGION_LOCAL(m_blockchain_lock); @@ -2200,6 +2217,7 @@ bool Blockchain::find_blockchain_supplement(const std::list& qbloc m_db->block_txn_start(true); current_height = get_current_blockchain_height(); size_t count = 0; + hashes.reserve(std::max((size_t)(current_height - start_height), (size_t)BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT)); for(size_t i = start_height; i < current_height && count < BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT; i++, count++) { hashes.push_back(m_db->get_block_hash_from_height(i)); @@ -2224,7 +2242,7 @@ bool Blockchain::find_blockchain_supplement(const std::list& qbloc // find split point between ours and foreign blockchain (or start at // blockchain height ), and return up to max_count FULL // blocks by reference. -bool Blockchain::find_blockchain_supplement(const uint64_t req_start_block, const std::list& qblock_ids, std::list > >& blocks, uint64_t& total_height, uint64_t& start_height, bool pruned, size_t max_count) const +bool Blockchain::find_blockchain_supplement(const uint64_t req_start_block, const std::list& qblock_ids, std::vector > >& blocks, uint64_t& total_height, uint64_t& start_height, bool pruned, size_t max_count) const { LOG_PRINT_L3("Blockchain::" << __func__); CRITICAL_REGION_LOCAL(m_blockchain_lock); @@ -2250,13 +2268,14 @@ bool Blockchain::find_blockchain_supplement(const uint64_t req_start_block, cons m_db->block_txn_start(true); total_height = get_current_blockchain_height(); size_t count = 0, size = 0; + blocks.reserve(std::min(std::min(max_count, (size_t)10000), (size_t)(total_height - start_height))); for(size_t i = start_height; i < total_height && count < max_count && (size < FIND_BLOCKCHAIN_SUPPLEMENT_MAX_SIZE || count < 3); i++, count++) { blocks.resize(blocks.size()+1); blocks.back().first = m_db->get_block_blob_from_height(i); block b; CHECK_AND_ASSERT_MES(parse_and_validate_block_from_blob(blocks.back().first, b), false, "internal error, invalid block"); - std::list mis; + std::vector mis; get_transactions_blobs(b.tx_hashes, blocks.back().second, mis, pruned); CHECK_AND_ASSERT_MES(!mis.size(), false, "internal error, transaction from block not found"); size += blocks.back().first.size(); @@ -2991,6 +3010,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, void Blockchain::check_ring_signature(const crypto::hash &tx_prefix_hash, const crypto::key_image &key_image, const std::vector &pubkeys, const std::vector& sig, uint64_t &result) { std::vector p_output_keys; + p_output_keys.reserve(pubkeys.size()); for (auto &key : pubkeys) { // rct::key and crypto::public_key have the same structure, avoid object ctor/memcpy @@ -3087,6 +3107,7 @@ uint64_t Blockchain::get_dynamic_per_kb_fee_estimate(uint64_t grace_blocks) cons const uint64_t min_block_size = get_min_block_size(version); std::vector sz; get_last_n_blocks_sizes(sz, CRYPTONOTE_REWARD_BLOCKS_WINDOW - grace_blocks); + sz.reserve(grace_blocks); for (size_t i = 0; i < grace_blocks; ++i) sz.push_back(min_block_size); @@ -3244,6 +3265,7 @@ bool Blockchain::check_block_timestamp(const block& b, uint64_t& median_ts) cons // need most recent 60 blocks, get index of first of those size_t offset = h - BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW; + timestamps.reserve(h - offset); for(;offset < h; ++offset) { timestamps.push_back(m_db->get_block_timestamp(offset)); @@ -3270,7 +3292,7 @@ void Blockchain::return_tx_to_pool(std::vector &txs) } } //------------------------------------------------------------------ -bool Blockchain::flush_txes_from_pool(const std::list &txids) +bool Blockchain::flush_txes_from_pool(const std::vector &txids) { CRITICAL_REGION_LOCAL(m_tx_pool); @@ -3460,6 +3482,7 @@ bool Blockchain::handle_block_to_main_chain(const block& bl, const crypto::hash& // Iterate over the block's transaction hashes, grabbing each // from the tx_pool and validating them. Each is then added // to txs. Keys spent in each are added to by the double spend check. + txs.reserve(bl.tx_hashes.size()); for (const crypto::hash& tx_id : bl.tx_hashes) { transaction tx; @@ -3873,7 +3896,7 @@ void Blockchain::output_scan_worker(const uint64_t amount, const std::vector &hashes) +uint64_t Blockchain::prevalidate_block_hashes(uint64_t height, const std::vector &hashes) { // new: . . . . . X X X X X . . . . . . // pre: A A A A B B B B C C C C D D D D @@ -3976,7 +3999,7 @@ uint64_t Blockchain::prevalidate_block_hashes(uint64_t height, const std::list &blocks_entry) +bool Blockchain::prepare_handle_incoming_blocks(const std::vector &blocks_entry) { MTRACE("Blockchain::" << __func__); TIME_MEASURE_START(prepare); @@ -4042,6 +4065,7 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::list txs; + std::vector txs; m_tx_pool.get_transactions(txs); size_t blob_size; @@ -4568,6 +4592,6 @@ bool Blockchain::for_all_outputs(uint64_t amount, std::function&, std::list&, std::list&) const; -template bool Blockchain::get_transactions_blobs(const std::vector&, std::list&, std::list&, bool) const; +template bool Blockchain::get_transactions(const std::vector&, std::vector&, std::vector&) const; +template bool Blockchain::get_transactions_blobs(const std::vector&, std::vector&, std::vector&, bool) const; } diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index ef736d1e7cd..bd46be0af5f 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -157,7 +157,7 @@ namespace cryptonote * * @return false if start_offset > blockchain height, else true */ - bool get_blocks(uint64_t start_offset, size_t count, std::list>& blocks, std::list& txs) const; + bool get_blocks(uint64_t start_offset, size_t count, std::vector>& blocks, std::vector& txs) const; /** * @brief get blocks from blocks based on start height and count @@ -168,7 +168,7 @@ namespace cryptonote * * @return false if start_offset > blockchain height, else true */ - bool get_blocks(uint64_t start_offset, size_t count, std::list>& blocks) const; + bool get_blocks(uint64_t start_offset, size_t count, std::vector>& blocks) const; /** * @brief compiles a list of all blocks stored as alternative chains @@ -177,7 +177,7 @@ namespace cryptonote * * @return true */ - bool get_alternative_blocks(std::list& blocks) const; + bool get_alternative_blocks(std::vector& blocks) const; /** * @brief returns the number of alternative blocks stored @@ -213,7 +213,7 @@ namespace cryptonote * * @return false on erroneous blocks, else true */ - bool prepare_handle_incoming_blocks(const std::list &blocks); + bool prepare_handle_incoming_blocks(const std::vector &blocks); /** * @brief incoming blocks post-processing, cleanup, and disk sync @@ -373,7 +373,7 @@ namespace cryptonote * * @return true if a block found in common, else false */ - bool find_blockchain_supplement(const std::list& qblock_ids, std::list& hashes, uint64_t& start_height, uint64_t& current_height) const; + bool find_blockchain_supplement(const std::list& qblock_ids, std::vector& hashes, uint64_t& start_height, uint64_t& current_height) const; /** * @brief get recent block hashes for a foreign chain @@ -420,7 +420,7 @@ namespace cryptonote * * @return true if a block found in common or req_start_block specified, else false */ - bool find_blockchain_supplement(const uint64_t req_start_block, const std::list& qblock_ids, std::list > >& blocks, uint64_t& total_height, uint64_t& start_height, bool pruned, size_t max_count) const; + bool find_blockchain_supplement(const uint64_t req_start_block, const std::list& qblock_ids, std::vector > >& blocks, uint64_t& total_height, uint64_t& start_height, bool pruned, size_t max_count) const; /** * @brief retrieves a set of blocks and their transactions, and possibly other transactions @@ -829,7 +829,7 @@ namespace cryptonote * * @return false if any removals fail, otherwise true */ - bool flush_txes_from_pool(const std::list &txids); + bool flush_txes_from_pool(const std::vector &txids); /** * @brief return a histogram of outputs on the blockchain @@ -952,7 +952,7 @@ namespace cryptonote bool is_within_compiled_block_hash_area(uint64_t height) const; bool is_within_compiled_block_hash_area() const { return is_within_compiled_block_hash_area(m_db->height()); } - uint64_t prevalidate_block_hashes(uint64_t height, const std::list &hashes); + uint64_t prevalidate_block_hashes(uint64_t height, const std::vector &hashes); void lock(); void unlock(); diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 7fc81a87dbf..8b24af43cd4 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -323,19 +323,19 @@ namespace cryptonote top_id = m_blockchain_storage.get_tail_id(height); } //----------------------------------------------------------------------------------------------- - bool core::get_blocks(uint64_t start_offset, size_t count, std::list>& blocks, std::list& txs) const + bool core::get_blocks(uint64_t start_offset, size_t count, std::vector>& blocks, std::vector& txs) const { return m_blockchain_storage.get_blocks(start_offset, count, blocks, txs); } //----------------------------------------------------------------------------------------------- - bool core::get_blocks(uint64_t start_offset, size_t count, std::list>& blocks) const + bool core::get_blocks(uint64_t start_offset, size_t count, std::vector>& blocks) const { return m_blockchain_storage.get_blocks(start_offset, count, blocks); } //----------------------------------------------------------------------------------------------- - bool core::get_blocks(uint64_t start_offset, size_t count, std::list& blocks) const + bool core::get_blocks(uint64_t start_offset, size_t count, std::vector& blocks) const { - std::list> bs; + std::vector> bs; if (!m_blockchain_storage.get_blocks(start_offset, count, bs)) return false; for (const auto &b: bs) @@ -343,7 +343,7 @@ namespace cryptonote return true; } //----------------------------------------------------------------------------------------------- - bool core::get_transactions(const std::vector& txs_ids, std::list& txs, std::list& missed_txs) const + bool core::get_transactions(const std::vector& txs_ids, std::vector& txs, std::vector& missed_txs) const { return m_blockchain_storage.get_transactions_blobs(txs_ids, txs, missed_txs); } @@ -354,12 +354,12 @@ namespace cryptonote return true; } //----------------------------------------------------------------------------------------------- - bool core::get_transactions(const std::vector& txs_ids, std::list& txs, std::list& missed_txs) const + bool core::get_transactions(const std::vector& txs_ids, std::vector& txs, std::vector& missed_txs) const { return m_blockchain_storage.get_transactions(txs_ids, txs, missed_txs); } //----------------------------------------------------------------------------------------------- - bool core::get_alternative_blocks(std::list& blocks) const + bool core::get_alternative_blocks(std::vector& blocks) const { return m_blockchain_storage.get_alternative_blocks(blocks); } @@ -672,7 +672,7 @@ namespace cryptonote return true; } //----------------------------------------------------------------------------------------------- - bool core::handle_incoming_txs(const std::list& tx_blobs, std::vector& tvc, bool keeped_by_block, bool relayed, bool do_not_relay) + bool core::handle_incoming_txs(const std::vector& tx_blobs, std::vector& tvc, bool keeped_by_block, bool relayed, bool do_not_relay) { TRY_ENTRY(); CRITICAL_REGION_LOCAL(m_incoming_tx_lock); @@ -683,7 +683,7 @@ namespace cryptonote tvc.resize(tx_blobs.size()); tools::threadpool& tpool = tools::threadpool::getInstance(); tools::threadpool::waiter waiter; - std::list::const_iterator it = tx_blobs.begin(); + std::vector::const_iterator it = tx_blobs.begin(); for (size_t i = 0; i < tx_blobs.size(); i++, ++it) { tpool.submit(&waiter, [&, i, it] { try @@ -752,7 +752,7 @@ namespace cryptonote //----------------------------------------------------------------------------------------------- bool core::handle_incoming_tx(const blobdata& tx_blob, tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay) { - std::list tx_blobs; + std::vector tx_blobs; tx_blobs.push_back(tx_blob); std::vector tvcv(1); bool r = handle_incoming_txs(tx_blobs, tvcv, keeped_by_block, relayed, do_not_relay); @@ -918,8 +918,8 @@ namespace cryptonote const uint64_t end = start_offset + count - 1; m_blockchain_storage.for_blocks_range(start_offset, end, [this, &emission_amount, &total_fee_amount](uint64_t, const crypto::hash& hash, const block& b){ - std::list txs; - std::list missed_txs; + std::vector txs; + std::vector missed_txs; uint64_t coinbase_amount = get_outs_money_amount(b.miner_tx); this->get_transactions(b.tx_hashes, txs, missed_txs); uint64_t tx_fee_amount = 0; @@ -1015,7 +1015,7 @@ namespace cryptonote bool core::relay_txpool_transactions() { // we attempt to relay txes that should be relayed, but were not - std::list> txs; + std::vector> txs; if (m_mempool.get_relayable_transactions(txs) && !txs.empty()) { cryptonote_connection_context fake_context = AUTO_VAL_INIT(fake_context); @@ -1033,7 +1033,7 @@ namespace cryptonote //----------------------------------------------------------------------------------------------- void core::on_transaction_relayed(const cryptonote::blobdata& tx_blob) { - std::list> txs; + std::vector> txs; cryptonote::transaction tx; crypto::hash tx_hash, tx_prefix_hash; if (!parse_and_validate_tx_from_blob(tx_blob, tx, tx_hash, tx_prefix_hash)) @@ -1055,7 +1055,7 @@ namespace cryptonote return m_blockchain_storage.find_blockchain_supplement(qblock_ids, resp); } //----------------------------------------------------------------------------------------------- - bool core::find_blockchain_supplement(const uint64_t req_start_block, const std::list& qblock_ids, std::list > >& blocks, uint64_t& total_height, uint64_t& start_height, bool pruned, size_t max_count) const + bool core::find_blockchain_supplement(const uint64_t req_start_block, const std::list& qblock_ids, std::vector > >& blocks, uint64_t& total_height, uint64_t& start_height, bool pruned, size_t max_count) const { return m_blockchain_storage.find_blockchain_supplement(req_start_block, qblock_ids, blocks, total_height, start_height, pruned, max_count); } @@ -1112,7 +1112,7 @@ namespace cryptonote { block_verification_context bvc = boost::value_initialized(); m_miner.pause(); - std::list blocks; + std::vector blocks; try { blocks.push_back(get_block_complete_entry(b, m_mempool)); @@ -1136,8 +1136,8 @@ namespace cryptonote cryptonote_connection_context exclude_context = boost::value_initialized(); NOTIFY_NEW_BLOCK::request arg = AUTO_VAL_INIT(arg); arg.current_blockchain_height = m_blockchain_storage.get_current_blockchain_height(); - std::list missed_txs; - std::list txs; + std::vector missed_txs; + std::vector txs; m_blockchain_storage.get_transactions_blobs(b.tx_hashes, txs, missed_txs); if(missed_txs.size() && m_blockchain_storage.get_block_id_by_height(get_block_height(b)) != get_block_hash(b)) { @@ -1173,7 +1173,7 @@ namespace cryptonote } //----------------------------------------------------------------------------------------------- - bool core::prepare_handle_incoming_blocks(const std::list &blocks) + bool core::prepare_handle_incoming_blocks(const std::vector &blocks) { m_incoming_tx_lock.lock(); m_blockchain_storage.prepare_handle_incoming_blocks(blocks); @@ -1266,7 +1266,7 @@ namespace cryptonote return true; } //----------------------------------------------------------------------------------------------- - bool core::get_pool_transactions(std::list& txs, bool include_sensitive_data) const + bool core::get_pool_transactions(std::vector& txs, bool include_sensitive_data) const { m_mempool.get_transactions(txs, include_sensitive_data); return true; @@ -1554,7 +1554,7 @@ namespace cryptonote return m_target_blockchain_height; } //----------------------------------------------------------------------------------------------- - uint64_t core::prevalidate_block_hashes(uint64_t height, const std::list &hashes) + uint64_t core::prevalidate_block_hashes(uint64_t height, const std::vector &hashes) { return get_blockchain_storage().prevalidate_block_hashes(height, hashes); } diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index 91bd50729bf..443290c8254 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -133,7 +133,7 @@ namespace cryptonote * * @return true if the transactions made it to the transaction pool, otherwise false */ - bool handle_incoming_txs(const std::list& tx_blobs, std::vector& tvc, bool keeped_by_block, bool relayed, bool do_not_relay); + bool handle_incoming_txs(const std::vector& tx_blobs, std::vector& tvc, bool keeped_by_block, bool relayed, bool do_not_relay); /** * @brief handles an incoming block @@ -156,7 +156,7 @@ namespace cryptonote * * @note see Blockchain::prepare_handle_incoming_blocks */ - bool prepare_handle_incoming_blocks(const std::list &blocks); + bool prepare_handle_incoming_blocks(const std::vector &blocks); /** * @copydoc Blockchain::cleanup_handle_incoming_blocks @@ -308,25 +308,25 @@ namespace cryptonote void get_blockchain_top(uint64_t& height, crypto::hash& top_id) const; /** - * @copydoc Blockchain::get_blocks(uint64_t, size_t, std::list>&, std::list&) const + * @copydoc Blockchain::get_blocks(uint64_t, size_t, std::vector>&, std::vector&) const * - * @note see Blockchain::get_blocks(uint64_t, size_t, std::list>&, std::list&) const + * @note see Blockchain::get_blocks(uint64_t, size_t, std::vector>&, std::vector&) const */ - bool get_blocks(uint64_t start_offset, size_t count, std::list>& blocks, std::list& txs) const; + bool get_blocks(uint64_t start_offset, size_t count, std::vector>& blocks, std::vector& txs) const; /** - * @copydoc Blockchain::get_blocks(uint64_t, size_t, std::list>&) const + * @copydoc Blockchain::get_blocks(uint64_t, size_t, std::vector>&) const * - * @note see Blockchain::get_blocks(uint64_t, size_t, std::list>&) const + * @note see Blockchain::get_blocks(uint64_t, size_t, std::vector>&) const */ - bool get_blocks(uint64_t start_offset, size_t count, std::list>& blocks) const; + bool get_blocks(uint64_t start_offset, size_t count, std::vector>& blocks) const; /** - * @copydoc Blockchain::get_blocks(uint64_t, size_t, std::list>&) const + * @copydoc Blockchain::get_blocks(uint64_t, size_t, std::vector>&) const * - * @note see Blockchain::get_blocks(uint64_t, size_t, std::list>&) const + * @note see Blockchain::get_blocks(uint64_t, size_t, std::vector>&) const */ - bool get_blocks(uint64_t start_offset, size_t count, std::list& blocks) const; + bool get_blocks(uint64_t start_offset, size_t count, std::vector& blocks) const; /** * @copydoc Blockchain::get_blocks(const t_ids_container&, t_blocks_container&, t_missed_container&) const @@ -351,14 +351,14 @@ namespace cryptonote * * @note see Blockchain::get_transactions */ - bool get_transactions(const std::vector& txs_ids, std::list& txs, std::list& missed_txs) const; + bool get_transactions(const std::vector& txs_ids, std::vector& txs, std::vector& missed_txs) const; /** * @copydoc Blockchain::get_transactions * * @note see Blockchain::get_transactions */ - bool get_transactions(const std::vector& txs_ids, std::list& txs, std::list& missed_txs) const; + bool get_transactions(const std::vector& txs_ids, std::vector& txs, std::vector& missed_txs) const; /** * @copydoc Blockchain::get_block_by_hash @@ -370,9 +370,9 @@ namespace cryptonote /** * @copydoc Blockchain::get_alternative_blocks * - * @note see Blockchain::get_alternative_blocks(std::list&) const + * @note see Blockchain::get_alternative_blocks(std::vector&) const */ - bool get_alternative_blocks(std::list& blocks) const; + bool get_alternative_blocks(std::vector& blocks) const; /** * @copydoc Blockchain::get_alternative_blocks_count @@ -429,7 +429,7 @@ namespace cryptonote * * @note see tx_memory_pool::get_transactions */ - bool get_pool_transactions(std::list& txs, bool include_unrelayed_txes = true) const; + bool get_pool_transactions(std::vector& txs, bool include_unrelayed_txes = true) const; /** * @copydoc tx_memory_pool::get_txpool_backlog @@ -512,11 +512,11 @@ namespace cryptonote bool find_blockchain_supplement(const std::list& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp) const; /** - * @copydoc Blockchain::find_blockchain_supplement(const uint64_t, const std::list&, std::list > >&, uint64_t&, uint64_t&, size_t) const + * @copydoc Blockchain::find_blockchain_supplement(const uint64_t, const std::list&, std::vector > >&, uint64_t&, uint64_t&, size_t) const * - * @note see Blockchain::find_blockchain_supplement(const uint64_t, const std::list&, std::list > >&, uint64_t&, uint64_t&, size_t) const + * @note see Blockchain::find_blockchain_supplement(const uint64_t, const std::list&, std::vector > >&, uint64_t&, uint64_t&, size_t) const */ - bool find_blockchain_supplement(const uint64_t req_start_block, const std::list& qblock_ids, std::list > >& blocks, uint64_t& total_height, uint64_t& start_height, bool pruned, size_t max_count) const; + bool find_blockchain_supplement(const uint64_t req_start_block, const std::list& qblock_ids, std::vector > >& blocks, uint64_t& total_height, uint64_t& start_height, bool pruned, size_t max_count) const; /** * @brief gets some stats about the daemon @@ -763,7 +763,7 @@ namespace cryptonote * * @return number of usable blocks */ - uint64_t prevalidate_block_hashes(uint64_t height, const std::list &hashes); + uint64_t prevalidate_block_hashes(uint64_t height, const std::vector &hashes); /** * @brief get free disk space on the blockchain partition diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index 164530b3eb8..2e8dd197d1c 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -556,11 +556,12 @@ namespace cryptonote } //--------------------------------------------------------------------------------- //TODO: investigate whether boolean return is appropriate - bool tx_memory_pool::get_relayable_transactions(std::list> &txs) const + bool tx_memory_pool::get_relayable_transactions(std::vector> &txs) const { CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL1(m_blockchain); const uint64_t now = time(NULL); + txs.reserve(m_blockchain.get_txpool_tx_count()); m_blockchain.for_all_txpool_txes([this, now, &txs](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *){ // 0 fee transactions are never relayed if(meta.fee > 0 && !meta.do_not_relay && now - meta.last_relayed_time > get_relay_delay(now, meta.receive_time)) @@ -588,7 +589,7 @@ namespace cryptonote return true; } //--------------------------------------------------------------------------------- - void tx_memory_pool::set_relayed(const std::list> &txs) + void tx_memory_pool::set_relayed(const std::vector> &txs) { CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL1(m_blockchain); @@ -621,10 +622,11 @@ namespace cryptonote return m_blockchain.get_txpool_tx_count(include_unrelayed_txes); } //--------------------------------------------------------------------------------- - void tx_memory_pool::get_transactions(std::list& txs, bool include_unrelayed_txes) const + void tx_memory_pool::get_transactions(std::vector& txs, bool include_unrelayed_txes) const { CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL1(m_blockchain); + txs.reserve(m_blockchain.get_txpool_tx_count(include_unrelayed_txes)); m_blockchain.for_all_txpool_txes([&txs](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd){ transaction tx; if (!parse_and_validate_tx_from_blob(*bd, tx)) @@ -642,6 +644,7 @@ namespace cryptonote { CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL1(m_blockchain); + txs.reserve(m_blockchain.get_txpool_tx_count(include_unrelayed_txes)); m_blockchain.for_all_txpool_txes([&txs](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd){ txs.push_back(txid); return true; @@ -653,6 +656,7 @@ namespace cryptonote CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL1(m_blockchain); const uint64_t now = time(NULL); + backlog.reserve(m_blockchain.get_txpool_tx_count(include_unrelayed_txes)); m_blockchain.for_all_txpool_txes([&backlog, now](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd){ backlog.push_back({meta.blob_size, meta.fee, meta.receive_time - now}); return true; @@ -741,6 +745,8 @@ namespace cryptonote { CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL1(m_blockchain); + tx_infos.reserve(m_blockchain.get_txpool_tx_count()); + key_image_infos.reserve(m_blockchain.get_txpool_tx_count()); m_blockchain.for_all_txpool_txes([&tx_infos, key_image_infos, include_sensitive_data](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd){ tx_info txi; txi.id_hash = epee::string_tools::pod_to_hex(txid); @@ -811,6 +817,8 @@ namespace cryptonote { CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL1(m_blockchain); + tx_infos.reserve(m_blockchain.get_txpool_tx_count()); + key_image_infos.reserve(m_blockchain.get_txpool_tx_count()); m_blockchain.for_all_txpool_txes([&tx_infos, key_image_infos](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd){ cryptonote::rpc::tx_in_pool txi; txi.tx_hash = txid; diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h index 4ce2f085d8d..5ccb71196ae 100644 --- a/src/cryptonote_core/tx_pool.h +++ b/src/cryptonote_core/tx_pool.h @@ -237,7 +237,7 @@ namespace cryptonote * @param include_unrelayed_txes include unrelayed txes in the result * */ - void get_transactions(std::list& txs, bool include_unrelayed_txes = true) const; + void get_transactions(std::vector& txs, bool include_unrelayed_txes = true) const; /** * @brief get a list of all transaction hashes in the pool @@ -324,14 +324,14 @@ namespace cryptonote * * @return true */ - bool get_relayable_transactions(std::list>& txs) const; + bool get_relayable_transactions(std::vector>& txs) const; /** * @brief tell the pool that certain transactions were just relayed * * @param txs the list of transactions (and their hashes) */ - void set_relayed(const std::list>& txs); + void set_relayed(const std::vector>& txs); /** * @brief get the total number of transactions in the pool diff --git a/src/cryptonote_protocol/block_queue.cpp b/src/cryptonote_protocol/block_queue.cpp index 9ae33d5401c..c39d67cebf4 100644 --- a/src/cryptonote_protocol/block_queue.cpp +++ b/src/cryptonote_protocol/block_queue.cpp @@ -50,10 +50,10 @@ namespace std { namespace cryptonote { -void block_queue::add_blocks(uint64_t height, std::list bcel, const boost::uuids::uuid &connection_id, float rate, size_t size) +void block_queue::add_blocks(uint64_t height, std::vector bcel, const boost::uuids::uuid &connection_id, float rate, size_t size) { boost::unique_lock lock(mutex); - std::list hashes; + std::vector hashes; bool has_hashes = remove_span(height, &hashes); blocks.insert(span(height, std::move(bcel), connection_id, rate, size)); if (has_hashes) @@ -97,7 +97,7 @@ void block_queue::flush_stale_spans(const std::set &live_con } } -bool block_queue::remove_span(uint64_t start_block_height, std::list *hashes) +bool block_queue::remove_span(uint64_t start_block_height, std::vector *hashes) { boost::unique_lock lock(mutex); for (block_map::iterator i = blocks.begin(); i != blocks.end(); ++i) @@ -172,7 +172,7 @@ bool block_queue::requested(const crypto::hash &hash) const return false; } -std::pair block_queue::reserve_span(uint64_t first_block_height, uint64_t last_block_height, uint64_t max_blocks, const boost::uuids::uuid &connection_id, const std::list &block_hashes, boost::posix_time::ptime time) +std::pair block_queue::reserve_span(uint64_t first_block_height, uint64_t last_block_height, uint64_t max_blocks, const boost::uuids::uuid &connection_id, const std::vector &block_hashes, boost::posix_time::ptime time) { boost::unique_lock lock(mutex); @@ -183,14 +183,14 @@ std::pair block_queue::reserve_span(uint64_t first_block_hei } uint64_t span_start_height = last_block_height - block_hashes.size() + 1; - std::list::const_iterator i = block_hashes.begin(); + std::vector::const_iterator i = block_hashes.begin(); while (i != block_hashes.end() && requested(*i)) { ++i; ++span_start_height; } uint64_t span_length = 0; - std::list hashes; + std::vector hashes; while (i != block_hashes.end() && span_length < max_blocks) { hashes.push_back(*i); @@ -230,7 +230,7 @@ std::pair block_queue::get_start_gap_span() const return std::make_pair(current_height + 1, first_span_height - current_height - 1); } -std::pair block_queue::get_next_span_if_scheduled(std::list &hashes, boost::uuids::uuid &connection_id, boost::posix_time::ptime &time) const +std::pair block_queue::get_next_span_if_scheduled(std::vector &hashes, boost::uuids::uuid &connection_id, boost::posix_time::ptime &time) const { boost::unique_lock lock(mutex); if (blocks.empty()) @@ -248,7 +248,7 @@ std::pair block_queue::get_next_span_if_scheduled(std::list< return std::make_pair(i->start_block_height, i->nblocks); } -void block_queue::set_span_hashes(uint64_t start_height, const boost::uuids::uuid &connection_id, std::list hashes) +void block_queue::set_span_hashes(uint64_t start_height, const boost::uuids::uuid &connection_id, std::vector hashes) { boost::unique_lock lock(mutex); for (block_map::iterator i = blocks.begin(); i != blocks.end(); ++i) @@ -264,7 +264,7 @@ void block_queue::set_span_hashes(uint64_t start_height, const boost::uuids::uui } } -bool block_queue::get_next_span(uint64_t &height, std::list &bcel, boost::uuids::uuid &connection_id, bool filled) const +bool block_queue::get_next_span(uint64_t &height, std::vector &bcel, boost::uuids::uuid &connection_id, bool filled) const { boost::unique_lock lock(mutex); if (blocks.empty()) diff --git a/src/cryptonote_protocol/block_queue.h b/src/cryptonote_protocol/block_queue.h index 69ddaa4358f..9059e89acd6 100644 --- a/src/cryptonote_protocol/block_queue.h +++ b/src/cryptonote_protocol/block_queue.h @@ -31,7 +31,7 @@ #pragma once #include -#include +#include #include #include #include @@ -49,15 +49,15 @@ namespace cryptonote struct span { uint64_t start_block_height; - std::list hashes; - std::list blocks; + std::vector hashes; + std::vector blocks; boost::uuids::uuid connection_id; uint64_t nblocks; float rate; size_t size; boost::posix_time::ptime time; - span(uint64_t start_block_height, std::list blocks, const boost::uuids::uuid &connection_id, float rate, size_t size): + span(uint64_t start_block_height, std::vector blocks, const boost::uuids::uuid &connection_id, float rate, size_t size): start_block_height(start_block_height), blocks(std::move(blocks)), connection_id(connection_id), nblocks(this->blocks.size()), rate(rate), size(size), time() {} span(uint64_t start_block_height, uint64_t nblocks, const boost::uuids::uuid &connection_id, boost::posix_time::ptime time): start_block_height(start_block_height), connection_id(connection_id), nblocks(nblocks), rate(0.0f), size(0), time(time) {} @@ -67,21 +67,21 @@ namespace cryptonote typedef std::set block_map; public: - void add_blocks(uint64_t height, std::list bcel, const boost::uuids::uuid &connection_id, float rate, size_t size); + void add_blocks(uint64_t height, std::vector bcel, const boost::uuids::uuid &connection_id, float rate, size_t size); void add_blocks(uint64_t height, uint64_t nblocks, const boost::uuids::uuid &connection_id, boost::posix_time::ptime time = boost::date_time::min_date_time); void flush_spans(const boost::uuids::uuid &connection_id, bool all = false); void flush_stale_spans(const std::set &live_connections); - bool remove_span(uint64_t start_block_height, std::list *hashes = NULL); + bool remove_span(uint64_t start_block_height, std::vector *hashes = NULL); void remove_spans(const boost::uuids::uuid &connection_id, uint64_t start_block_height); uint64_t get_max_block_height() const; void print() const; std::string get_overview() const; - std::pair reserve_span(uint64_t first_block_height, uint64_t last_block_height, uint64_t max_blocks, const boost::uuids::uuid &connection_id, const std::list &block_hashes, boost::posix_time::ptime time = boost::posix_time::microsec_clock::universal_time()); + std::pair reserve_span(uint64_t first_block_height, uint64_t last_block_height, uint64_t max_blocks, const boost::uuids::uuid &connection_id, const std::vector &block_hashes, boost::posix_time::ptime time = boost::posix_time::microsec_clock::universal_time()); bool is_blockchain_placeholder(const span &span) const; std::pair get_start_gap_span() const; - std::pair get_next_span_if_scheduled(std::list &hashes, boost::uuids::uuid &connection_id, boost::posix_time::ptime &time) const; - void set_span_hashes(uint64_t start_height, const boost::uuids::uuid &connection_id, std::list hashes); - bool get_next_span(uint64_t &height, std::list &bcel, boost::uuids::uuid &connection_id, bool filled = true) const; + std::pair get_next_span_if_scheduled(std::vector &hashes, boost::uuids::uuid &connection_id, boost::posix_time::ptime &time) const; + void set_span_hashes(uint64_t start_height, const boost::uuids::uuid &connection_id, std::vector hashes); + bool get_next_span(uint64_t &height, std::vector &bcel, boost::uuids::uuid &connection_id, bool filled = true) const; bool has_next_span(const boost::uuids::uuid &connection_id, bool &filled) const; size_t get_data_size() const; size_t get_num_filled_spans_prefix() const; diff --git a/src/cryptonote_protocol/cryptonote_protocol_defs.h b/src/cryptonote_protocol/cryptonote_protocol_defs.h index cf0043287c0..db159f0f464 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_defs.h +++ b/src/cryptonote_protocol/cryptonote_protocol_defs.h @@ -109,7 +109,7 @@ namespace cryptonote struct block_complete_entry { blobdata block; - std::list txs; + std::vector txs; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(block) KV_SERIALIZE(txs) @@ -145,7 +145,7 @@ namespace cryptonote struct request { - std::list txs; + std::vector txs; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(txs) @@ -161,8 +161,8 @@ namespace cryptonote struct request { - std::list txs; - std::list blocks; + std::vector txs; + std::vector blocks; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE_CONTAINER_POD_AS_BLOB(txs) @@ -177,9 +177,9 @@ namespace cryptonote struct request { - std::list txs; - std::list blocks; - std::list missed_ids; + std::vector txs; + std::vector blocks; + std::vector missed_ids; uint64_t current_blockchain_height; BEGIN_KV_SERIALIZE_MAP() @@ -230,7 +230,7 @@ namespace cryptonote uint64_t start_height; uint64_t total_height; uint64_t cumulative_difficulty; - std::list m_block_ids; + std::vector m_block_ids; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(start_height) diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl index 2e1df8078a3..b4786b90372 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl @@ -351,7 +351,7 @@ namespace cryptonote return 1; } m_core.pause_mine(); - std::list blocks; + std::vector blocks; blocks.push_back(arg.b); m_core.prepare_handle_incoming_blocks(blocks); for(auto tx_blob_it = arg.b.txs.begin(); tx_blob_it!=arg.b.txs.end();tx_blob_it++) @@ -438,7 +438,7 @@ namespace cryptonote } } - std::list have_tx; + std::vector have_tx; // Instead of requesting missing transactions by hash like BTC, // we do it by index (thanks to a suggestion from moneromooo) because @@ -578,8 +578,8 @@ namespace cryptonote else { std::vector tx_ids; - std::list txes; - std::list missing; + std::vector txes; + std::vector missing; tx_ids.push_back(tx_hash); if (m_core.get_transactions(tx_ids, txes, missing) && missing.empty()) { @@ -626,7 +626,7 @@ namespace cryptonote b.block = arg.b.block; b.txs = have_tx; - std::list blocks; + std::vector blocks; blocks.push_back(b); m_core.prepare_handle_incoming_blocks(blocks); @@ -687,8 +687,8 @@ namespace cryptonote { MLOG_P2P_MESSAGE("Received NOTIFY_REQUEST_FLUFFY_MISSING_TX (" << arg.missing_tx_indices.size() << " txes), block hash " << arg.block_hash); - std::list> local_blocks; - std::list local_txs; + std::vector> local_blocks; + std::vector local_txs; block b; if (!m_core.get_block_by_hash(arg.block_hash, b)) @@ -725,8 +725,8 @@ namespace cryptonote } } - std::list txs; - std::list missed; + std::vector txs; + std::vector missed; if (!m_core.get_transactions(txids, txs, missed)) { LOG_ERROR_CCONTEXT("Failed to handle request NOTIFY_REQUEST_FLUFFY_MISSING_TX, " @@ -774,10 +774,12 @@ namespace cryptonote return 1; } - for(auto tx_blob_it = arg.txs.begin(); tx_blob_it!=arg.txs.end();) + std::vector newtxs; + newtxs.reserve(arg.txs.size()); + for (size_t i = 0; i < arg.txs.size(); ++i) { cryptonote::tx_verification_context tvc = AUTO_VAL_INIT(tvc); - m_core.handle_incoming_tx(*tx_blob_it, tvc, false, true, false); + m_core.handle_incoming_tx(arg.txs[i], tvc, false, true, false); if(tvc.m_verifivation_failed) { LOG_PRINT_CCONTEXT_L1("Tx verification failed, dropping connection"); @@ -785,10 +787,9 @@ namespace cryptonote return 1; } if(tvc.m_should_be_relayed) - ++tx_blob_it; - else - arg.txs.erase(tx_blob_it++); + newtxs.push_back(std::move(arg.txs[i])); } + arg.txs = std::move(newtxs); if(arg.txs.size()) { @@ -996,7 +997,7 @@ skip: { const uint64_t previous_height = m_core.get_current_blockchain_height(); uint64_t start_height; - std::list blocks; + std::vector blocks; boost::uuids::uuid span_connection_id; if (!m_block_queue.get_next_span(start_height, blocks, span_connection_id)) { @@ -1070,7 +1071,7 @@ skip: LOG_ERROR_CCONTEXT("Internal error: tvc.size() != block_entry.txs.size()"); return 1; } - std::list::const_iterator it = block_entry.txs.begin(); + std::vector::const_iterator it = block_entry.txs.begin(); for (size_t i = 0; i < tvc.size(); ++i, ++it) { if(tvc[i].m_verifivation_failed) @@ -1248,7 +1249,7 @@ skip: template bool t_cryptonote_protocol_handler::should_download_next_span(cryptonote_connection_context& context) const { - std::list hashes; + std::vector hashes; boost::uuids::uuid span_connection_id; boost::posix_time::ptime request_time; std::pair span; @@ -1267,7 +1268,7 @@ skip: // we might be in a weird case where there is a filled next span, // but it starts higher than the current height uint64_t height; - std::list bcel; + std::vector bcel; if (!m_block_queue.get_next_span(height, bcel, span_connection_id, true)) return false; if (height > m_core.get_current_blockchain_height()) @@ -1415,7 +1416,7 @@ skip: { if (span.second == 0) { - std::list hashes; + std::vector hashes; boost::uuids::uuid span_connection_id; boost::posix_time::ptime time; span = m_block_queue.get_next_span_if_scheduled(hashes, span_connection_id, time); @@ -1441,14 +1442,18 @@ skip: goto skip; } // take out blocks we already have - while (!context.m_needed_objects.empty() && m_core.have_block(context.m_needed_objects.front())) + size_t skip = 0; + while (skip < context.m_needed_objects.size() && m_core.have_block(context.m_needed_objects[skip])) { // if we're popping the last hash, record it so we can ask again from that hash, // this prevents never being able to progress on peers we get old hash lists from - if (context.m_needed_objects.size() == 1) - context.m_last_known_hash = context.m_needed_objects.front(); - context.m_needed_objects.pop_front(); + if (skip + 1 == context.m_needed_objects.size()) + context.m_last_known_hash = context.m_needed_objects[skip]; + ++skip; } + if (skip > 0) + context.m_needed_objects = std::vector(context.m_needed_objects.begin() + skip, context.m_needed_objects.end()); + const uint64_t first_block_height = context.m_last_response_height - context.m_needed_objects.size() + 1; span = m_block_queue.reserve_span(first_block_height, context.m_last_response_height, count_limit, context.m_connection_id, context.m_needed_objects); MDEBUG(context << " span from " << first_block_height << ": " << span.first << "/" << span.second); @@ -1456,7 +1461,7 @@ skip: if (span.second == 0 && !force_next_span) { MDEBUG(context << " still no span reserved, we may be in the corner case of next span scheduled and everything else scheduled/filled"); - std::list hashes; + std::vector hashes; boost::uuids::uuid span_connection_id; boost::posix_time::ptime time; span = m_block_queue.get_next_span_if_scheduled(hashes, span_connection_id, time); @@ -1487,23 +1492,21 @@ skip: MERROR("ERROR: skip " << skip << ", m_needed_objects " << context.m_needed_objects.size() << ", first_context_block_height" << first_context_block_height); return false; } - while (skip--) - context.m_needed_objects.pop_front(); + if (skip > 0) + context.m_needed_objects = std::vector(context.m_needed_objects.begin() + skip, context.m_needed_objects.end()); if (context.m_needed_objects.size() < span.second) { MERROR("ERROR: span " << span.first << "/" << span.second << ", m_needed_objects " << context.m_needed_objects.size()); return false; } - auto it = context.m_needed_objects.begin(); for (size_t n = 0; n < span.second; ++n) { - req.blocks.push_back(*it); + req.blocks.push_back(context.m_needed_objects[n]); ++count; - context.m_requested_objects.insert(*it); - auto j = it++; - context.m_needed_objects.erase(j); + context.m_requested_objects.insert(context.m_needed_objects[n]); } + context.m_needed_objects = std::vector(context.m_needed_objects.begin() + span.second, context.m_needed_objects.end()); } context.m_last_request_time = boost::posix_time::microsec_clock::universal_time(); @@ -1664,7 +1667,7 @@ skip: { NOTIFY_NEW_FLUFFY_BLOCK::request fluffy_arg = AUTO_VAL_INIT(fluffy_arg); fluffy_arg.current_blockchain_height = arg.current_blockchain_height; - std::list fluffy_txs; + std::vector fluffy_txs; fluffy_arg.b = arg.b; fluffy_arg.b.txs = fluffy_txs; diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index 83cf5ba3c70..b921e143896 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -226,7 +226,7 @@ namespace cryptonote if (use_bootstrap_daemon_if_necessary(invoke_http_mode::BIN, "/getblocks.bin", req, res, r)) return r; - std::list > > bs; + std::vector > > bs; if(!m_core.find_blockchain_supplement(req.start_height, req.block_ids, bs, res.current_height, res.start_height, req.prune, COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT)) { @@ -235,6 +235,8 @@ namespace cryptonote } size_t pruned_size = 0, unpruned_size = 0, ntxes = 0; + res.blocks.reserve(bs.size()); + res.output_indices.reserve(bs.size()); for(auto& bd: bs) { res.blocks.resize(res.blocks.size()+1); @@ -266,7 +268,9 @@ namespace cryptonote } size_t txidx = 0; ntxes += bd.second.size(); - for (std::list::iterator i = bd.second.begin(); i != bd.second.end(); ++i) + res.blocks.back().txs.reserve(bd.second.size()); + res.output_indices.back().indices.reserve(bd.second.size()); + for (std::vector::iterator i = bd.second.begin(); i != bd.second.end(); ++i) { unpruned_size += i->size(); res.blocks.back().txs.push_back(std::move(*i)); @@ -295,7 +299,7 @@ namespace cryptonote if (use_bootstrap_daemon_if_necessary(invoke_http_mode::JON, "/get_alt_blocks_hashes", req, res, r)) return r; - std::list blks; + std::vector blks; if(!m_core.get_alternative_blocks(blks)) { @@ -337,8 +341,8 @@ namespace cryptonote res.status = "Error retrieving block at height " + std::to_string(height); return true; } - std::list txs; - std::list missed_txs; + std::vector txs; + std::vector missed_txs; m_core.get_transactions(blk.tx_hashes, txs, missed_txs); res.blocks.resize(res.blocks.size() + 1); res.blocks.back().block = block_to_blob(blk); @@ -553,8 +557,8 @@ namespace cryptonote } vh.push_back(*reinterpret_cast(b.data())); } - std::list missed_txs; - std::list txs; + std::vector missed_txs; + std::vector txs; bool r = m_core.get_transactions(vh, txs, missed_txs); if(!r) { @@ -575,25 +579,26 @@ namespace cryptonote if(r) { // sort to match original request - std::list sorted_txs; + std::vector sorted_txs; std::vector::const_iterator i; + unsigned txs_processed = 0; for (const crypto::hash &h: vh) { if (std::find(missed_txs.begin(), missed_txs.end(), h) == missed_txs.end()) { - if (txs.empty()) + if (txs.size() == txs_processed) { res.status = "Failed: internal error - txs is empty"; return true; } // core returns the ones it finds in the right order - if (get_transaction_hash(txs.front()) != h) + if (get_transaction_hash(txs[txs_processed]) != h) { res.status = "Failed: tx hash mismatch"; return true; } - sorted_txs.push_back(std::move(txs.front())); - txs.pop_front(); + sorted_txs.push_back(std::move(txs[txs_processed])); + ++txs_processed; } else if ((i = std::find_if(pool_tx_info.begin(), pool_tx_info.end(), [h](const tx_info &txi) { return epee::string_tools::pod_to_hex(h) == txi.id_hash; })) != pool_tx_info.end()) { @@ -604,7 +609,7 @@ namespace cryptonote return true; } sorted_txs.push_back(tx); - missed_txs.remove(h); + missed_txs.erase(std::find(missed_txs.begin(), missed_txs.end(), h)); pool_tx_hashes.insert(h); const std::string hash_string = epee::string_tools::pod_to_hex(h); for (const auto &ti: pool_tx_info) @@ -623,7 +628,7 @@ namespace cryptonote LOG_PRINT_L2("Found " << found_in_pool << "/" << vh.size() << " transactions in the pool"); } - std::list::const_iterator txhi = req.txs_hashes.begin(); + std::vector::const_iterator txhi = req.txs_hashes.begin(); std::vector::const_iterator vhi = vh.begin(); for(auto& tx: txs) { @@ -1664,10 +1669,10 @@ namespace cryptonote PERF_TIMER(on_flush_txpool); bool failed = false; - std::list txids; + std::vector txids; if (req.txids.empty()) { - std::list pool_txs; + std::vector pool_txs; bool r = m_core.get_pool_transactions(pool_txs); if (!r) { diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index abebad27baf..49b73014994 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -112,7 +112,7 @@ namespace cryptonote struct response { - std::list blocks; + std::vector blocks; uint64_t start_height; uint64_t current_height; std::string status; @@ -190,7 +190,7 @@ namespace cryptonote struct response { - std::list m_block_ids; + std::vector m_block_ids; uint64_t start_height; uint64_t current_height; std::string status; @@ -275,7 +275,7 @@ namespace cryptonote uint64_t total_received; uint64_t total_received_unlocked = 0; // OpenMonero only uint64_t scanned_height; - std::list transactions; + std::vector transactions; uint64_t blockchain_height; uint64_t scanned_block_height; std::string status; @@ -563,7 +563,7 @@ namespace cryptonote { struct request { - std::list txs_hashes; + std::vector txs_hashes; bool decode_as_json; bool prune; @@ -600,11 +600,11 @@ namespace cryptonote struct response { // older compatibility stuff - std::list txs_as_hex; //transactions blobs as hex (old compat) - std::list txs_as_json; //transactions decoded as json (old compat) + std::vector txs_as_hex; //transactions blobs as hex (old compat) + std::vector txs_as_json; //transactions decoded as json (old compat) // in both old and new - std::list missed_tx; //not found transactions + std::vector missed_tx; //not found transactions // new style std::vector txs; @@ -1933,7 +1933,7 @@ namespace cryptonote { struct request { - std::list txids; + std::vector txids; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(txids) @@ -2150,7 +2150,7 @@ namespace cryptonote { struct request { - std::list txids; + std::vector txids; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(txids) diff --git a/src/rpc/daemon_handler.cpp b/src/rpc/daemon_handler.cpp index 39f169cdf9f..582a3440bb6 100644 --- a/src/rpc/daemon_handler.cpp +++ b/src/rpc/daemon_handler.cpp @@ -50,7 +50,7 @@ namespace rpc void DaemonHandler::handle(const GetBlocksFast::Request& req, GetBlocksFast::Response& res) { - std::list > > blocks; + std::vector > > blocks; if(!m_core.find_blockchain_supplement(req.start_height, req.block_ids, blocks, res.current_height, res.start_height, req.prune, COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT)) { @@ -62,9 +62,6 @@ namespace rpc res.blocks.resize(blocks.size()); res.output_indices.resize(blocks.size()); - //TODO: really need to switch uses of std::list to std::vector unless - // it's a huge performance concern - auto it = blocks.begin(); uint64_t block_count = 0; @@ -89,7 +86,7 @@ namespace rpc res.error_details = "incorrect number of transactions retrieved for block"; return; } - std::list txs; + std::vector txs; for (const auto& blob : it->second) { txs.resize(txs.size() + 1); @@ -163,10 +160,10 @@ namespace rpc void DaemonHandler::handle(const GetTransactions::Request& req, GetTransactions::Response& res) { - std::list found_txs; - std::list missed_hashes; + std::vector found_txs_vec; + std::vector missed_vec; - bool r = m_core.get_transactions(req.tx_hashes, found_txs, missed_hashes); + bool r = m_core.get_transactions(req.tx_hashes, found_txs_vec, missed_vec); // TODO: consider fixing core::get_transactions to not hide exceptions if (!r) @@ -176,20 +173,7 @@ namespace rpc return; } - size_t num_found = found_txs.size(); - - // std::list is annoying - std::vector found_txs_vec - { - std::make_move_iterator(std::begin(found_txs)), - std::make_move_iterator(std::end(found_txs)) - }; - - std::vector missed_vec - { - std::make_move_iterator(std::begin(missed_hashes)), - std::make_move_iterator(std::end(missed_hashes)) - }; + size_t num_found = found_txs_vec.size(); std::vector heights(num_found); std::vector in_pool(num_found, false); @@ -204,7 +188,7 @@ namespace rpc // if any missing from blockchain, check in tx pool if (!missed_vec.empty()) { - std::list pool_txs; + std::vector pool_txs; m_core.get_pool_transactions(pool_txs); diff --git a/src/rpc/daemon_messages.h b/src/rpc/daemon_messages.h index 1495c845fc7..8fff369df4f 100644 --- a/src/rpc/daemon_messages.h +++ b/src/rpc/daemon_messages.h @@ -106,7 +106,7 @@ BEGIN_RPC_MESSAGE_CLASS(GetHashesFast); RPC_MESSAGE_MEMBER(uint64_t, start_height); END_RPC_MESSAGE_REQUEST; BEGIN_RPC_MESSAGE_RESPONSE; - RPC_MESSAGE_MEMBER(std::list, hashes); + RPC_MESSAGE_MEMBER(std::vector, hashes); RPC_MESSAGE_MEMBER(uint64_t, start_height); RPC_MESSAGE_MEMBER(uint64_t, current_height); END_RPC_MESSAGE_RESPONSE; diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 98bd6c31883..56a2a2adfca 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -1649,7 +1649,7 @@ void wallet2::parse_block_round(const cryptonote::blobdata &blob, cryptonote::bl bl_id = get_block_hash(bl); } //---------------------------------------------------------------------------------------------------- -void wallet2::pull_blocks(uint64_t start_height, uint64_t &blocks_start_height, const std::list &short_chain_history, std::list &blocks, std::vector &o_indices) +void wallet2::pull_blocks(uint64_t start_height, uint64_t &blocks_start_height, const std::list &short_chain_history, std::vector &blocks, std::vector &o_indices) { cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::request req = AUTO_VAL_INIT(req); cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::response res = AUTO_VAL_INIT(res); @@ -1700,7 +1700,7 @@ void wallet2::pull_blocks(uint64_t start_height, uint64_t &blocks_start_height, o_indices = std::move(res.output_indices); } //---------------------------------------------------------------------------------------------------- -void wallet2::pull_hashes(uint64_t start_height, uint64_t &blocks_start_height, const std::list &short_chain_history, std::list &hashes) +void wallet2::pull_hashes(uint64_t start_height, uint64_t &blocks_start_height, const std::list &short_chain_history, std::vector &hashes) { cryptonote::COMMAND_RPC_GET_HASHES_FAST::request req = AUTO_VAL_INIT(req); cryptonote::COMMAND_RPC_GET_HASHES_FAST::response res = AUTO_VAL_INIT(res); @@ -1718,7 +1718,7 @@ void wallet2::pull_hashes(uint64_t start_height, uint64_t &blocks_start_height, hashes = std::move(res.m_block_ids); } //---------------------------------------------------------------------------------------------------- -void wallet2::process_blocks(uint64_t start_height, const std::list &blocks, const std::vector &o_indices, uint64_t& blocks_added) +void wallet2::process_blocks(uint64_t start_height, const std::vector &blocks, const std::vector &o_indices, uint64_t& blocks_added) { size_t current_index = start_height; blocks_added = 0; @@ -1735,13 +1735,13 @@ void wallet2::process_blocks(uint64_t start_height, const std::list round_blocks(threads); std::deque error(threads); size_t blocks_size = blocks.size(); - std::list::const_iterator blocki = blocks.begin(); + std::vector::const_iterator blocki = blocks.begin(); for (size_t b = 0; b < blocks_size; b += threads) { size_t round_size = std::min((size_t)threads, blocks_size - b); tools::threadpool::waiter waiter; - std::list::const_iterator tmpblocki = blocki; + std::vector::const_iterator tmpblocki = blocki; for (size_t i = 0; i < round_size; ++i) { tpool.submit(&waiter, boost::bind(&wallet2::parse_block_round, this, std::cref(tmpblocki->block), @@ -1833,7 +1833,7 @@ void wallet2::refresh(uint64_t start_height, uint64_t & blocks_fetched) refresh(start_height, blocks_fetched, received_money); } //---------------------------------------------------------------------------------------------------- -void wallet2::pull_next_blocks(uint64_t start_height, uint64_t &blocks_start_height, std::list &short_chain_history, const std::list &prev_blocks, std::list &blocks, std::vector &o_indices, bool &error) +void wallet2::pull_next_blocks(uint64_t start_height, uint64_t &blocks_start_height, std::list &short_chain_history, const std::vector &prev_blocks, std::vector &blocks, std::vector &o_indices, bool &error) { error = false; @@ -1843,7 +1843,7 @@ void wallet2::pull_next_blocks(uint64_t start_height, uint64_t &blocks_start_hei // prepend the last 3 blocks, should be enough to guard against a block or two's reorg cryptonote::block bl; - std::list::const_reverse_iterator i = prev_blocks.rbegin(); + std::vector::const_reverse_iterator i = prev_blocks.rbegin(); for (size_t n = 0; n < std::min((size_t)3, prev_blocks.size()); ++n) { bool ok = cryptonote::parse_and_validate_block_from_blob(i->block, bl); @@ -2110,7 +2110,7 @@ void wallet2::update_pool_state(bool refreshed) //---------------------------------------------------------------------------------------------------- void wallet2::fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height, std::list &short_chain_history) { - std::list hashes; + std::vector hashes; const uint64_t checkpoint_height = m_checkpoints.get_max_height(); if (stop_height > checkpoint_height && m_blockchain.size()-1 < checkpoint_height) @@ -2138,7 +2138,7 @@ void wallet2::fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height, } if (hashes.size() + current_index < stop_height) { drop_from_short_history(short_chain_history, 3); - std::list::iterator right = hashes.end(); + std::vector::iterator right = hashes.end(); // prepend 3 more for (int i = 0; i<3; i++) { right--; @@ -2243,7 +2243,7 @@ void wallet2::refresh(uint64_t start_height, uint64_t & blocks_fetched, bool& re tools::threadpool& tpool = tools::threadpool::getInstance(); tools::threadpool::waiter waiter; uint64_t blocks_start_height; - std::list blocks; + std::vector blocks; std::vector o_indices; bool refreshed = false; @@ -2276,7 +2276,7 @@ void wallet2::refresh(uint64_t start_height, uint64_t & blocks_fetched, bool& re { // pull the next set of blocks while we're processing the current one uint64_t next_blocks_start_height; - std::list next_blocks; + std::vector next_blocks; std::vector next_o_indices; bool error = false; if (blocks.empty()) diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index a10cef56151..3a1f555d7bd 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -1123,11 +1123,11 @@ namespace tools void get_short_chain_history(std::list& ids) const; bool is_tx_spendtime_unlocked(uint64_t unlock_time, uint64_t block_height) const; bool clear(); - void pull_blocks(uint64_t start_height, uint64_t& blocks_start_height, const std::list &short_chain_history, std::list &blocks, std::vector &o_indices); - void pull_hashes(uint64_t start_height, uint64_t& blocks_start_height, const std::list &short_chain_history, std::list &hashes); + void pull_blocks(uint64_t start_height, uint64_t& blocks_start_height, const std::list &short_chain_history, std::vector &blocks, std::vector &o_indices); + void pull_hashes(uint64_t start_height, uint64_t& blocks_start_height, const std::list &short_chain_history, std::vector &hashes); void fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height, std::list &short_chain_history); - void pull_next_blocks(uint64_t start_height, uint64_t &blocks_start_height, std::list &short_chain_history, const std::list &prev_blocks, std::list &blocks, std::vector &o_indices, bool &error); - void process_blocks(uint64_t start_height, const std::list &blocks, const std::vector &o_indices, uint64_t& blocks_added); + void pull_next_blocks(uint64_t start_height, uint64_t &blocks_start_height, std::list &short_chain_history, const std::vector &prev_blocks, std::vector &blocks, std::vector &o_indices, bool &error); + void process_blocks(uint64_t start_height, const std::vector &blocks, const std::vector &o_indices, uint64_t& blocks_added); uint64_t select_transfers(uint64_t needed_money, std::vector unused_transfers_indices, std::vector& selected_transfers, bool trusted_daemon) const; bool prepare_file_names(const std::string& file_path); void process_unconfirmed(const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t height); diff --git a/tests/core_proxy/core_proxy.cpp b/tests/core_proxy/core_proxy.cpp index f0a1eb5cee0..17e5527149c 100644 --- a/tests/core_proxy/core_proxy.cpp +++ b/tests/core_proxy/core_proxy.cpp @@ -184,7 +184,7 @@ bool tests::proxy_core::handle_incoming_tx(const cryptonote::blobdata& tx_blob, return true; } -bool tests::proxy_core::handle_incoming_txs(const std::list& tx_blobs, std::vector& tvc, bool keeped_by_block, bool relayed, bool do_not_relay) +bool tests::proxy_core::handle_incoming_txs(const std::vector& tx_blobs, std::vector& tvc, bool keeped_by_block, bool relayed, bool do_not_relay) { tvc.resize(tx_blobs.size()); size_t i = 0; diff --git a/tests/core_proxy/core_proxy.h b/tests/core_proxy/core_proxy.h index 8b7ac429170..7d36a0f689f 100644 --- a/tests/core_proxy/core_proxy.h +++ b/tests/core_proxy/core_proxy.h @@ -76,7 +76,7 @@ namespace tests bool have_block(const crypto::hash& id); void get_blockchain_top(uint64_t& height, crypto::hash& top_id); bool handle_incoming_tx(const cryptonote::blobdata& tx_blob, cryptonote::tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay); - bool handle_incoming_txs(const std::list& tx_blobs, std::vector& tvc, bool keeped_by_block, bool relayed, bool do_not_relay); + bool handle_incoming_txs(const std::vector& tx_blobs, std::vector& tvc, bool keeped_by_block, bool relayed, bool do_not_relay); bool handle_incoming_block(const cryptonote::blobdata& block_blob, cryptonote::block_verification_context& bvc, bool update_miner_blocktemplate = true); void pause_mine(){} void resume_mine(){} @@ -86,7 +86,7 @@ namespace tests cryptonote::Blockchain &get_blockchain_storage() { throw std::runtime_error("Called invalid member function: please never call get_blockchain_storage on the TESTING class proxy_core."); } bool get_test_drop_download() {return true;} bool get_test_drop_download_height() {return true;} - bool prepare_handle_incoming_blocks(const std::list &blocks) { return true; } + bool prepare_handle_incoming_blocks(const std::vector &blocks) { return true; } bool cleanup_handle_incoming_blocks(bool force_sync = false) { return true; } uint64_t get_target_blockchain_height() const { return 1; } size_t get_block_sync_size(uint64_t height) const { return BLOCKS_SYNCHRONIZING_DEFAULT_COUNT; } @@ -94,8 +94,8 @@ namespace tests cryptonote::network_type get_nettype() const { return cryptonote::MAINNET; } bool get_pool_transaction(const crypto::hash& id, cryptonote::blobdata& tx_blob) const { return false; } bool pool_has_tx(const crypto::hash &txid) const { return false; } - bool get_blocks(uint64_t start_offset, size_t count, std::list>& blocks, std::list& txs) const { return false; } - bool get_transactions(const std::vector& txs_ids, std::list& txs, std::list& missed_txs) const { return false; } + bool get_blocks(uint64_t start_offset, size_t count, std::vector>& blocks, std::vector& txs) const { return false; } + bool get_transactions(const std::vector& txs_ids, std::vector& txs, std::vector& missed_txs) const { return false; } bool get_block_by_hash(const crypto::hash &h, cryptonote::block &blk, bool *orphan = NULL) const { return false; } uint8_t get_ideal_hard_fork_version() const { return 0; } uint8_t get_ideal_hard_fork_version(uint64_t height) const { return 0; } @@ -103,6 +103,6 @@ namespace tests uint64_t get_earliest_ideal_height_for_version(uint8_t version) const { return 0; } cryptonote::difficulty_type get_block_cumulative_difficulty(uint64_t height) const { return 0; } bool fluffy_blocks_enabled() const { return false; } - uint64_t prevalidate_block_hashes(uint64_t height, const std::list &hashes) { return 0; } + uint64_t prevalidate_block_hashes(uint64_t height, const std::vector &hashes) { return 0; } }; } diff --git a/tests/core_tests/chain_switch_1.cpp b/tests/core_tests/chain_switch_1.cpp index c5017a0df19..18a813b19ea 100644 --- a/tests/core_tests/chain_switch_1.cpp +++ b/tests/core_tests/chain_switch_1.cpp @@ -128,7 +128,7 @@ bool gen_chain_switch_1::check_split_not_switched(cryptonote::core& c, size_t ev m_recipient_account_3 = boost::get(events[3]); m_recipient_account_4 = boost::get(events[4]); - std::list blocks; + std::vector blocks; bool r = c.get_blocks(0, 10000, blocks); CHECK_TEST_CONDITION(r); CHECK_EQ(5 + 2 * CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, blocks.size()); @@ -145,7 +145,7 @@ bool gen_chain_switch_1::check_split_not_switched(cryptonote::core& c, size_t ev CHECK_EQ(MK_COINS(14), get_balance(m_recipient_account_3, chain, mtx)); CHECK_EQ(MK_COINS(3), get_balance(m_recipient_account_4, chain, mtx)); - std::list tx_pool; + std::vector tx_pool; r = c.get_pool_transactions(tx_pool); CHECK_TEST_CONDITION(r); CHECK_EQ(1, tx_pool.size()); @@ -166,7 +166,7 @@ bool gen_chain_switch_1::check_split_switched(cryptonote::core& c, size_t ev_ind { DEFINE_TESTS_ERROR_CONTEXT("gen_chain_switch_1::check_split_switched"); - std::list blocks; + std::vector blocks; bool r = c.get_blocks(0, 10000, blocks); CHECK_TEST_CONDITION(r); CHECK_EQ(6 + 2 * CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, blocks.size()); @@ -175,7 +175,7 @@ bool gen_chain_switch_1::check_split_switched(cryptonote::core& c, size_t ev_ind CHECK_TEST_CONDITION(std::equal(blocks.begin(), it, m_chain_1.begin())); CHECK_TEST_CONDITION(blocks.back() == boost::get(events[24 + 2 * CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW])); // blk_7 - std::list alt_blocks; + std::vector alt_blocks; r = c.get_alternative_blocks(alt_blocks); CHECK_TEST_CONDITION(r); CHECK_EQ(2, c.get_alternative_blocks_count()); @@ -195,7 +195,7 @@ bool gen_chain_switch_1::check_split_switched(cryptonote::core& c, size_t ev_ind CHECK_EQ(MK_COINS(14), get_balance(m_recipient_account_3, chain, mtx)); CHECK_EQ(MK_COINS(16), get_balance(m_recipient_account_4, chain, mtx)); - std::list tx_pool; + std::vector tx_pool; r = c.get_pool_transactions(tx_pool); CHECK_TEST_CONDITION(r); CHECK_EQ(1, tx_pool.size()); diff --git a/tests/core_tests/chain_switch_1.h b/tests/core_tests/chain_switch_1.h index 5a035bf06f5..989b6df11c9 100644 --- a/tests/core_tests/chain_switch_1.h +++ b/tests/core_tests/chain_switch_1.h @@ -45,12 +45,12 @@ class gen_chain_switch_1 : public test_chain_unit_base bool check_split_switched(cryptonote::core& c, size_t ev_index, const std::vector& events); private: - std::list m_chain_1; + std::vector m_chain_1; cryptonote::account_base m_recipient_account_1; cryptonote::account_base m_recipient_account_2; cryptonote::account_base m_recipient_account_3; cryptonote::account_base m_recipient_account_4; - std::list m_tx_pool; + std::vector m_tx_pool; }; diff --git a/tests/core_tests/chaingen.h b/tests/core_tests/chaingen.h index 6a723d56f94..201da4fa01a 100644 --- a/tests/core_tests/chaingen.h +++ b/tests/core_tests/chaingen.h @@ -481,7 +481,7 @@ inline bool do_replay_events(std::vector& events) MERROR("Failed to flush txpool"); return false; } - c.get_blockchain_storage().flush_txes_from_pool(std::list(pool_txs.begin(), pool_txs.end())); + c.get_blockchain_storage().flush_txes_from_pool(pool_txs); t_test_class validator; bool ret = replay_events_through_core(c, events, validator); diff --git a/tests/core_tests/chaingen001.cpp b/tests/core_tests/chaingen001.cpp index e84bfb92426..a76cf1592f3 100644 --- a/tests/core_tests/chaingen001.cpp +++ b/tests/core_tests/chaingen001.cpp @@ -78,7 +78,7 @@ bool one_block::verify_1(cryptonote::core& c, size_t ev_index, const std::vector //CHECK_TEST_CONDITION(get_block_reward(0) == get_balance(alice, events, chain, mtx)); // check height - std::list blocks; + std::vector blocks; std::list outs; bool r = c.get_blocks(0, 100, blocks); //c.get_outs(100, outs); diff --git a/tests/core_tests/double_spend.cpp b/tests/core_tests/double_spend.cpp index 7ed62cf6db0..c60ea885e99 100644 --- a/tests/core_tests/double_spend.cpp +++ b/tests/core_tests/double_spend.cpp @@ -73,7 +73,7 @@ bool gen_double_spend_in_different_chains::check_double_spend(cryptonote::core& { DEFINE_TESTS_ERROR_CONTEXT("gen_double_spend_in_different_chains::check_double_spend"); - std::list block_list; + std::vector block_list; bool r = c.get_blocks(0, 100 + 2 * CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, block_list); CHECK_TEST_CONDITION(r); diff --git a/tests/core_tests/double_spend.inl b/tests/core_tests/double_spend.inl index 0c58fb01813..d021470657b 100644 --- a/tests/core_tests/double_spend.inl +++ b/tests/core_tests/double_spend.inl @@ -64,7 +64,7 @@ bool gen_double_spend_base::check_block_verification_context(cons template bool gen_double_spend_base::mark_last_valid_block(cryptonote::core& c, size_t /*ev_index*/, const std::vector& /*events*/) { - std::list block_list; + std::vector block_list; bool r = c.get_blocks(c.get_current_blockchain_height() - 1, 1, block_list); CHECK_AND_ASSERT_MES(r, false, "core::get_blocks failed"); m_last_valid_block = block_list.back(); @@ -96,7 +96,7 @@ bool gen_double_spend_base::check_double_spend(cryptonote::core& } CHECK_NOT_EQ(invalid_index_value, m_invalid_block_index); - std::list block_list; + std::vector block_list; bool r = c.get_blocks(0, 100 + 2 * CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, block_list); CHECK_TEST_CONDITION(r); CHECK_TEST_CONDITION(m_last_valid_block == block_list.back()); diff --git a/tests/core_tests/ring_signature_1.cpp b/tests/core_tests/ring_signature_1.cpp index 38eb1cf9bcd..8b2b943cc88 100644 --- a/tests/core_tests/ring_signature_1.cpp +++ b/tests/core_tests/ring_signature_1.cpp @@ -101,7 +101,7 @@ bool gen_ring_signature_1::check_balances_1(cryptonote::core& c, size_t ev_index m_bob_account = boost::get(events[3]); m_alice_account = boost::get(events[4]); - std::list blocks; + std::vector blocks; bool r = c.get_blocks(0, 100 + 2 * CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, blocks); CHECK_TEST_CONDITION(r); @@ -119,7 +119,7 @@ bool gen_ring_signature_1::check_balances_2(cryptonote::core& c, size_t ev_index { DEFINE_TESTS_ERROR_CONTEXT("gen_ring_signature_1::check_balances_2"); - std::list blocks; + std::vector blocks; bool r = c.get_blocks(0, 100 + 2 * CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, blocks); CHECK_TEST_CONDITION(r); @@ -182,7 +182,7 @@ bool gen_ring_signature_2::check_balances_1(cryptonote::core& c, size_t ev_index m_bob_account = boost::get(events[1]); m_alice_account = boost::get(events[2]); - std::list blocks; + std::vector blocks; bool r = c.get_blocks(0, 100 + 2 * CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, blocks); CHECK_TEST_CONDITION(r); @@ -200,7 +200,7 @@ bool gen_ring_signature_2::check_balances_2(cryptonote::core& c, size_t ev_index { DEFINE_TESTS_ERROR_CONTEXT("gen_ring_signature_2::check_balances_2"); - std::list blocks; + std::vector blocks; bool r = c.get_blocks(0, 100 + 2 * CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, blocks); CHECK_TEST_CONDITION(r); @@ -292,7 +292,7 @@ bool gen_ring_signature_big::check_balances_1(cryptonote::core& c, size_t ev_ind m_bob_account = boost::get(events[1]); m_alice_account = boost::get(events[1 + m_test_size]); - std::list blocks; + std::vector blocks; bool r = c.get_blocks(0, 2 * m_test_size + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, blocks); CHECK_TEST_CONDITION(r); @@ -317,7 +317,7 @@ bool gen_ring_signature_big::check_balances_2(cryptonote::core& c, size_t ev_ind { DEFINE_TESTS_ERROR_CONTEXT("gen_ring_signature_big::check_balances_2"); - std::list blocks; + std::vector blocks; bool r = c.get_blocks(0, 2 * m_test_size + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, blocks); CHECK_TEST_CONDITION(r); diff --git a/tests/unit_tests/ban.cpp b/tests/unit_tests/ban.cpp index 15bc0bce380..e3dbdaef154 100644 --- a/tests/unit_tests/ban.cpp +++ b/tests/unit_tests/ban.cpp @@ -55,7 +55,7 @@ class test_core bool have_block(const crypto::hash& id) const {return true;} void get_blockchain_top(uint64_t& height, crypto::hash& top_id)const{height=0;top_id=crypto::null_hash;} bool handle_incoming_tx(const cryptonote::blobdata& tx_blob, cryptonote::tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay) { return true; } - bool handle_incoming_txs(const std::list& tx_blob, std::vector& tvc, bool keeped_by_block, bool relayed, bool do_not_relay) { return true; } + bool handle_incoming_txs(const std::vector& tx_blob, std::vector& tvc, bool keeped_by_block, bool relayed, bool do_not_relay) { return true; } bool handle_incoming_block(const cryptonote::blobdata& block_blob, cryptonote::block_verification_context& bvc, bool update_miner_blocktemplate = true) { return true; } void pause_mine(){} void resume_mine(){} @@ -65,7 +65,7 @@ class test_core cryptonote::blockchain_storage &get_blockchain_storage() { throw std::runtime_error("Called invalid member function: please never call get_blockchain_storage on the TESTING class test_core."); } bool get_test_drop_download() const {return true;} bool get_test_drop_download_height() const {return true;} - bool prepare_handle_incoming_blocks(const std::list &blocks) { return true; } + bool prepare_handle_incoming_blocks(const std::vector &blocks) { return true; } bool cleanup_handle_incoming_blocks(bool force_sync = false) { return true; } uint64_t get_target_blockchain_height() const { return 1; } size_t get_block_sync_size(uint64_t height) const { return BLOCKS_SYNCHRONIZING_DEFAULT_COUNT; } @@ -73,8 +73,8 @@ class test_core cryptonote::network_type get_nettype() const { return cryptonote::MAINNET; } bool get_pool_transaction(const crypto::hash& id, cryptonote::blobdata& tx_blob) const { return false; } bool pool_has_tx(const crypto::hash &txid) const { return false; } - bool get_blocks(uint64_t start_offset, size_t count, std::list>& blocks, std::list& txs) const { return false; } - bool get_transactions(const std::vector& txs_ids, std::list& txs, std::list& missed_txs) const { return false; } + bool get_blocks(uint64_t start_offset, size_t count, std::vector>& blocks, std::vector& txs) const { return false; } + bool get_transactions(const std::vector& txs_ids, std::vector& txs, std::vector& missed_txs) const { return false; } bool get_block_by_hash(const crypto::hash &h, cryptonote::block &blk, bool *orphan = NULL) const { return false; } uint8_t get_ideal_hard_fork_version() const { return 0; } uint8_t get_ideal_hard_fork_version(uint64_t height) const { return 0; } @@ -82,7 +82,7 @@ class test_core uint64_t get_earliest_ideal_height_for_version(uint8_t version) const { return 0; } cryptonote::difficulty_type get_block_cumulative_difficulty(uint64_t height) const { return 0; } bool fluffy_blocks_enabled() const { return false; } - uint64_t prevalidate_block_hashes(uint64_t height, const std::list &hashes) { return 0; } + uint64_t prevalidate_block_hashes(uint64_t height, const std::vector &hashes) { return 0; } void stop() {} }; From a830db25775f7b708210e736154caf147ddf0ef3 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 16 Apr 2018 20:16:00 +0100 Subject: [PATCH 0126/1404] rpc: rework to avoid repeated calculations in get_blocks.bin --- src/cryptonote_core/blockchain.cpp | 21 ++++++++++---- src/cryptonote_core/blockchain.h | 2 +- src/cryptonote_core/cryptonote_core.cpp | 4 +-- src/cryptonote_core/cryptonote_core.h | 2 +- src/rpc/core_rpc_server.cpp | 37 ++++++++----------------- src/rpc/daemon_handler.cpp | 10 +++---- 6 files changed, 36 insertions(+), 40 deletions(-) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 7518cc4e1a6..59563380de9 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -2242,7 +2242,7 @@ bool Blockchain::find_blockchain_supplement(const std::list& qbloc // find split point between ours and foreign blockchain (or start at // blockchain height ), and return up to max_count FULL // blocks by reference. -bool Blockchain::find_blockchain_supplement(const uint64_t req_start_block, const std::list& qblock_ids, std::vector > >& blocks, uint64_t& total_height, uint64_t& start_height, bool pruned, size_t max_count) const +bool Blockchain::find_blockchain_supplement(const uint64_t req_start_block, const std::list& qblock_ids, std::vector, std::vector > > >& blocks, uint64_t& total_height, uint64_t& start_height, bool pruned, bool get_miner_tx_hash, size_t max_count) const { LOG_PRINT_L3("Blockchain::" << __func__); CRITICAL_REGION_LOCAL(m_blockchain_lock); @@ -2272,15 +2272,24 @@ bool Blockchain::find_blockchain_supplement(const uint64_t req_start_block, cons for(size_t i = start_height; i < total_height && count < max_count && (size < FIND_BLOCKCHAIN_SUPPLEMENT_MAX_SIZE || count < 3); i++, count++) { blocks.resize(blocks.size()+1); - blocks.back().first = m_db->get_block_blob_from_height(i); + blocks.back().first.first = m_db->get_block_blob_from_height(i); block b; - CHECK_AND_ASSERT_MES(parse_and_validate_block_from_blob(blocks.back().first, b), false, "internal error, invalid block"); + CHECK_AND_ASSERT_MES(parse_and_validate_block_from_blob(blocks.back().first.first, b), false, "internal error, invalid block"); + blocks.back().first.second = get_miner_tx_hash ? cryptonote::get_transaction_hash(b.miner_tx) : crypto::null_hash; std::vector mis; - get_transactions_blobs(b.tx_hashes, blocks.back().second, mis, pruned); + std::vector txs; + get_transactions_blobs(b.tx_hashes, txs, mis, pruned); CHECK_AND_ASSERT_MES(!mis.size(), false, "internal error, transaction from block not found"); - size += blocks.back().first.size(); - for (const auto &t: blocks.back().second) + size += blocks.back().first.first.size(); + for (const auto &t: txs) size += t.size(); + + CHECK_AND_ASSERT_MES(txs.size() == b.tx_hashes.size(), false, "mismatched sizes of b.tx_hashes and txs"); + blocks.back().second.reserve(txs.size()); + for (size_t i = 0; i < txs.size(); ++i) + { + blocks.back().second.push_back(std::make_pair(b.tx_hashes[i], std::move(txs[i]))); + } } m_db->block_txn_stop(); return true; diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index bd46be0af5f..36d6b8609da 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -420,7 +420,7 @@ namespace cryptonote * * @return true if a block found in common or req_start_block specified, else false */ - bool find_blockchain_supplement(const uint64_t req_start_block, const std::list& qblock_ids, std::vector > >& blocks, uint64_t& total_height, uint64_t& start_height, bool pruned, size_t max_count) const; + bool find_blockchain_supplement(const uint64_t req_start_block, const std::list& qblock_ids, std::vector, std::vector > > >& blocks, uint64_t& total_height, uint64_t& start_height, bool pruned, bool get_miner_tx_hash, size_t max_count) const; /** * @brief retrieves a set of blocks and their transactions, and possibly other transactions diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 8b24af43cd4..dc0b0d29790 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -1055,9 +1055,9 @@ namespace cryptonote return m_blockchain_storage.find_blockchain_supplement(qblock_ids, resp); } //----------------------------------------------------------------------------------------------- - bool core::find_blockchain_supplement(const uint64_t req_start_block, const std::list& qblock_ids, std::vector > >& blocks, uint64_t& total_height, uint64_t& start_height, bool pruned, size_t max_count) const + bool core::find_blockchain_supplement(const uint64_t req_start_block, const std::list& qblock_ids, std::vector, std::vector > > >& blocks, uint64_t& total_height, uint64_t& start_height, bool pruned, bool get_miner_tx_hash, size_t max_count) const { - return m_blockchain_storage.find_blockchain_supplement(req_start_block, qblock_ids, blocks, total_height, start_height, pruned, max_count); + return m_blockchain_storage.find_blockchain_supplement(req_start_block, qblock_ids, blocks, total_height, start_height, pruned, get_miner_tx_hash, max_count); } //----------------------------------------------------------------------------------------------- bool core::get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res) const diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index 443290c8254..03000383ed6 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -516,7 +516,7 @@ namespace cryptonote * * @note see Blockchain::find_blockchain_supplement(const uint64_t, const std::list&, std::vector > >&, uint64_t&, uint64_t&, size_t) const */ - bool find_blockchain_supplement(const uint64_t req_start_block, const std::list& qblock_ids, std::vector > >& blocks, uint64_t& total_height, uint64_t& start_height, bool pruned, size_t max_count) const; + bool find_blockchain_supplement(const uint64_t req_start_block, const std::list& qblock_ids, std::vector, std::vector > > >& blocks, uint64_t& total_height, uint64_t& start_height, bool pruned, bool get_miner_tx_hash, size_t max_count) const; /** * @brief gets some stats about the daemon diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index b921e143896..b55b1994b14 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -226,9 +226,9 @@ namespace cryptonote if (use_bootstrap_daemon_if_necessary(invoke_http_mode::BIN, "/getblocks.bin", req, res, r)) return r; - std::vector > > bs; + std::vector, std::vector > > > bs; - if(!m_core.find_blockchain_supplement(req.start_height, req.block_ids, bs, res.current_height, res.start_height, req.prune, COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT)) + if(!m_core.find_blockchain_supplement(req.start_height, req.block_ids, bs, res.current_height, res.start_height, req.prune, !req.no_miner_tx, COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT)) { res.status = "Failed"; return false; @@ -240,46 +240,33 @@ namespace cryptonote for(auto& bd: bs) { res.blocks.resize(res.blocks.size()+1); - res.blocks.back().block = bd.first; - pruned_size += bd.first.size(); - unpruned_size += bd.first.size(); + res.blocks.back().block = bd.first.first; + pruned_size += bd.first.first.size(); + unpruned_size += bd.first.first.size(); res.output_indices.push_back(COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices()); res.output_indices.back().indices.push_back(COMMAND_RPC_GET_BLOCKS_FAST::tx_output_indices()); - block b; - if (!parse_and_validate_block_from_blob(bd.first, b)) - { - res.status = "Invalid block"; - return false; - } if (!req.no_miner_tx) { - bool r = m_core.get_tx_outputs_gindexs(get_transaction_hash(b.miner_tx), res.output_indices.back().indices.back().indices); + bool r = m_core.get_tx_outputs_gindexs(bd.first.second, res.output_indices.back().indices.back().indices); if (!r) { res.status = "Failed"; return false; } } - if(b.tx_hashes.size() != bd.second.size()) - { - MERROR("block " << get_block_hash(b) << ": tx_hashes.size() " << b.tx_hashes.size() << ", bd.second.size() " << bd.second.size()); - res.status = "Failed"; - return false; - } - size_t txidx = 0; ntxes += bd.second.size(); res.blocks.back().txs.reserve(bd.second.size()); res.output_indices.back().indices.reserve(bd.second.size()); - for (std::vector::iterator i = bd.second.begin(); i != bd.second.end(); ++i) + for (std::vector>::iterator i = bd.second.begin(); i != bd.second.end(); ++i) { - unpruned_size += i->size(); - res.blocks.back().txs.push_back(std::move(*i)); - i->clear(); - i->shrink_to_fit(); + unpruned_size += i->second.size(); + res.blocks.back().txs.push_back(std::move(i->second)); + i->second.clear(); + i->second.shrink_to_fit(); pruned_size += res.blocks.back().txs.back().size(); res.output_indices.back().indices.push_back(COMMAND_RPC_GET_BLOCKS_FAST::tx_output_indices()); - bool r = m_core.get_tx_outputs_gindexs(b.tx_hashes[txidx++], res.output_indices.back().indices.back().indices); + bool r = m_core.get_tx_outputs_gindexs(i->first, res.output_indices.back().indices.back().indices); if (!r) { res.status = "Failed"; diff --git a/src/rpc/daemon_handler.cpp b/src/rpc/daemon_handler.cpp index 582a3440bb6..55858cc2a13 100644 --- a/src/rpc/daemon_handler.cpp +++ b/src/rpc/daemon_handler.cpp @@ -50,9 +50,9 @@ namespace rpc void DaemonHandler::handle(const GetBlocksFast::Request& req, GetBlocksFast::Response& res) { - std::vector > > blocks; + std::vector, std::vector > > > blocks; - if(!m_core.find_blockchain_supplement(req.start_height, req.block_ids, blocks, res.current_height, res.start_height, req.prune, COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT)) + if(!m_core.find_blockchain_supplement(req.start_height, req.block_ids, blocks, res.current_height, res.start_height, req.prune, true, COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT)) { res.status = Message::STATUS_FAILED; res.error_details = "core::find_blockchain_supplement() returned false"; @@ -69,7 +69,7 @@ namespace rpc { cryptonote::rpc::block_with_transactions& bwt = res.blocks[block_count]; - if (!parse_and_validate_block_from_blob(it->first, bwt.block)) + if (!parse_and_validate_block_from_blob(it->first.first, bwt.block)) { res.blocks.clear(); res.output_indices.clear(); @@ -87,10 +87,10 @@ namespace rpc return; } std::vector txs; - for (const auto& blob : it->second) + for (const auto& p : it->second) { txs.resize(txs.size() + 1); - if (!parse_and_validate_tx_from_blob(blob, txs.back())) + if (!parse_and_validate_tx_from_blob(p.second, txs.back())) { res.blocks.clear(); res.output_indices.clear(); From 57ea90285cb403416b9b59d0b62393fb2b6998a0 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 17 Apr 2018 11:44:17 +0100 Subject: [PATCH 0127/1404] wallet: don't process miner tx if we're refreshing with no-coinbase --- src/wallet/wallet2.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 56a2a2adfca..61b488624e1 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -1582,7 +1582,9 @@ void wallet2::process_new_blockchain_entry(const cryptonote::block& b, const cry if(b.timestamp + 60*60*24 > m_account.get_createtime() && height >= m_refresh_from_block_height) { TIME_MEASURE_START(miner_tx_handle_time); - process_new_transaction(get_transaction_hash(b.miner_tx), b.miner_tx, o_indices.indices[txidx++].indices, height, b.timestamp, true, false, false); + if (m_refresh_type != RefreshNoCoinbase) + process_new_transaction(get_transaction_hash(b.miner_tx), b.miner_tx, o_indices.indices[txidx].indices, height, b.timestamp, true, false, false); + ++txidx; TIME_MEASURE_FINISH(miner_tx_handle_time); TIME_MEASURE_START(txs_handle_time); From 31a895e876bca91be52247f79b0e34b42d0d0ddc Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 17 Apr 2018 11:45:54 +0100 Subject: [PATCH 0128/1404] wallet2: simplify/speedup process_blocks --- src/wallet/wallet2.cpp | 83 +++++++++--------------------------------- 1 file changed, 17 insertions(+), 66 deletions(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 61b488624e1..9fdcde93f67 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -1729,76 +1729,30 @@ void wallet2::process_blocks(uint64_t start_height, const std::vector round_block_hashes(blocks.size()); + std::vector round_blocks(blocks.size()); + std::deque error(blocks.size()); + tools::threadpool& tpool = tools::threadpool::getInstance(); - int threads = tpool.get_max_concurrency(); - if (threads > 1) + tools::threadpool::waiter waiter; + for (size_t i = 0; i < blocks.size(); ++i) { - std::vector round_block_hashes(threads); - std::vector round_blocks(threads); - std::deque error(threads); - size_t blocks_size = blocks.size(); - std::vector::const_iterator blocki = blocks.begin(); - for (size_t b = 0; b < blocks_size; b += threads) - { - size_t round_size = std::min((size_t)threads, blocks_size - b); - tools::threadpool::waiter waiter; - - std::vector::const_iterator tmpblocki = blocki; - for (size_t i = 0; i < round_size; ++i) - { - tpool.submit(&waiter, boost::bind(&wallet2::parse_block_round, this, std::cref(tmpblocki->block), - std::ref(round_blocks[i]), std::ref(round_block_hashes[i]), std::ref(error[i]))); - ++tmpblocki; - } - waiter.wait(); - tmpblocki = blocki; - for (size_t i = 0; i < round_size; ++i) - { - THROW_WALLET_EXCEPTION_IF(error[i], error::block_parse_error, tmpblocki->block); - ++tmpblocki; - } - for (size_t i = 0; i < round_size; ++i) - { - const crypto::hash &bl_id = round_block_hashes[i]; - cryptonote::block &bl = round_blocks[i]; - - if(current_index >= m_blockchain.size()) - { - process_new_blockchain_entry(bl, *blocki, bl_id, current_index, o_indices[b+i]); - ++blocks_added; - } - else if(bl_id != m_blockchain[current_index]) - { - //split detected here !!! - THROW_WALLET_EXCEPTION_IF(current_index == start_height, error::wallet_internal_error, - "wrong daemon response: split starts from the first block in response " + string_tools::pod_to_hex(bl_id) + - " (height " + std::to_string(start_height) + "), local block id at this height: " + - string_tools::pod_to_hex(m_blockchain[current_index])); - - detach_blockchain(current_index); - process_new_blockchain_entry(bl, *blocki, bl_id, current_index, o_indices[b+i]); - } - else - { - LOG_PRINT_L2("Block is already in blockchain: " << string_tools::pod_to_hex(bl_id)); - } - ++current_index; - ++blocki; - } - } + tpool.submit(&waiter, boost::bind(&wallet2::parse_block_round, this, std::cref(blocks[i].block), + std::ref(round_blocks[i]), std::ref(round_block_hashes[i]), std::ref(error[i]))); } - else + waiter.wait(); + for (size_t i = 0; i < blocks.size(); ++i) { - for(auto& bl_entry: blocks) + THROW_WALLET_EXCEPTION_IF(error[i], error::block_parse_error, blocks[i].block); + } + for (size_t i = 0; i < blocks.size(); ++i) { - cryptonote::block bl; - bool r = cryptonote::parse_and_validate_block_from_blob(bl_entry.block, bl); - THROW_WALLET_EXCEPTION_IF(!r, error::block_parse_error, bl_entry.block); + const crypto::hash &bl_id = round_block_hashes[i]; + cryptonote::block &bl = round_blocks[i]; - crypto::hash bl_id = get_block_hash(bl); if(current_index >= m_blockchain.size()) { - process_new_blockchain_entry(bl, bl_entry, bl_id, current_index, o_indices[tx_o_indices_idx]); + process_new_blockchain_entry(bl, blocks[i], bl_id, current_index, o_indices[i]); ++blocks_added; } else if(bl_id != m_blockchain[current_index]) @@ -1810,16 +1764,13 @@ void wallet2::process_blocks(uint64_t start_height, const std::vector Date: Tue, 17 Apr 2018 18:16:19 +0100 Subject: [PATCH 0129/1404] wallet2: parse blocks in the RPC thread, not the processing thread Processing typically is the bottleneck --- src/wallet/wallet2.cpp | 73 ++++++++++++++++++++++++------------------ src/wallet/wallet2.h | 12 +++++-- 2 files changed, 51 insertions(+), 34 deletions(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 9fdcde93f67..57f75e3ec8c 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -1720,39 +1720,23 @@ void wallet2::pull_hashes(uint64_t start_height, uint64_t &blocks_start_height, hashes = std::move(res.m_block_ids); } //---------------------------------------------------------------------------------------------------- -void wallet2::process_blocks(uint64_t start_height, const std::vector &blocks, const std::vector &o_indices, uint64_t& blocks_added) +void wallet2::process_parsed_blocks(uint64_t start_height, const std::vector &blocks, const std::vector &parsed_blocks, uint64_t& blocks_added) { size_t current_index = start_height; blocks_added = 0; size_t tx_o_indices_idx = 0; - THROW_WALLET_EXCEPTION_IF(blocks.size() != o_indices.size(), error::wallet_internal_error, "size mismatch"); + THROW_WALLET_EXCEPTION_IF(blocks.size() != parsed_blocks.size(), error::wallet_internal_error, "size mismatch"); THROW_WALLET_EXCEPTION_IF(!m_blockchain.is_in_bounds(current_index), error::wallet_internal_error, "Index out of bounds of hashchain"); - std::vector round_block_hashes(blocks.size()); - std::vector round_blocks(blocks.size()); - std::deque error(blocks.size()); - - tools::threadpool& tpool = tools::threadpool::getInstance(); - tools::threadpool::waiter waiter; - for (size_t i = 0; i < blocks.size(); ++i) - { - tpool.submit(&waiter, boost::bind(&wallet2::parse_block_round, this, std::cref(blocks[i].block), - std::ref(round_blocks[i]), std::ref(round_block_hashes[i]), std::ref(error[i]))); - } - waiter.wait(); for (size_t i = 0; i < blocks.size(); ++i) { - THROW_WALLET_EXCEPTION_IF(error[i], error::block_parse_error, blocks[i].block); - } - for (size_t i = 0; i < blocks.size(); ++i) - { - const crypto::hash &bl_id = round_block_hashes[i]; - cryptonote::block &bl = round_blocks[i]; + const crypto::hash &bl_id = parsed_blocks[i].hash; + const cryptonote::block &bl = parsed_blocks[i].block; if(current_index >= m_blockchain.size()) { - process_new_blockchain_entry(bl, blocks[i], bl_id, current_index, o_indices[i]); + process_new_blockchain_entry(bl, blocks[i], bl_id, current_index, parsed_blocks[i].o_indices); ++blocks_added; } else if(bl_id != m_blockchain[current_index]) @@ -1764,7 +1748,7 @@ void wallet2::process_blocks(uint64_t start_height, const std::vector &short_chain_history, const std::vector &prev_blocks, std::vector &blocks, std::vector &o_indices, bool &error) +void wallet2::pull_and_parse_next_blocks(uint64_t start_height, uint64_t &blocks_start_height, std::list &short_chain_history, const std::vector &prev_blocks, std::vector &blocks, std::vector &parsed_blocks, bool &error) { error = false; @@ -1806,7 +1790,28 @@ void wallet2::pull_next_blocks(uint64_t start_height, uint64_t &blocks_start_hei } // pull the new blocks + std::vector o_indices; pull_blocks(start_height, blocks_start_height, short_chain_history, blocks, o_indices); + THROW_WALLET_EXCEPTION_IF(blocks.size() != o_indices.size(), error::wallet_internal_error, "Mismatched sizes of blocks and o_indices"); + + tools::threadpool& tpool = tools::threadpool::getInstance(); + tools::threadpool::waiter waiter; + parsed_blocks.resize(blocks.size()); + for (size_t i = 0; i < blocks.size(); ++i) + { + tpool.submit(&waiter, boost::bind(&wallet2::parse_block_round, this, std::cref(blocks[i].block), + std::ref(parsed_blocks[i].block), std::ref(parsed_blocks[i].hash), std::ref(parsed_blocks[i].error))); + } + waiter.wait(); + for (size_t i = 0; i < blocks.size(); ++i) + { + if (parsed_blocks[i].error) + { + error = true; + break; + } + parsed_blocks[i].o_indices = std::move(o_indices[i]); + } } catch(...) { @@ -2197,7 +2202,7 @@ void wallet2::refresh(uint64_t start_height, uint64_t & blocks_fetched, bool& re tools::threadpool::waiter waiter; uint64_t blocks_start_height; std::vector blocks; - std::vector o_indices; + std::vector parsed_blocks; bool refreshed = false; // pull the first set of blocks @@ -2218,11 +2223,11 @@ void wallet2::refresh(uint64_t start_height, uint64_t & blocks_fetched, bool& re // If stop() is called during fast refresh we don't need to continue if(!m_run.load(std::memory_order_relaxed)) return; - pull_blocks(start_height, blocks_start_height, short_chain_history, blocks, o_indices); // always reset start_height to 0 to force short_chain_ history to be used on // subsequent pulls in this refresh. start_height = 0; + bool first = true; while(m_run.load(std::memory_order_relaxed)) { try @@ -2230,19 +2235,22 @@ void wallet2::refresh(uint64_t start_height, uint64_t & blocks_fetched, bool& re // pull the next set of blocks while we're processing the current one uint64_t next_blocks_start_height; std::vector next_blocks; - std::vector next_o_indices; + std::vector next_parsed_blocks; bool error = false; - if (blocks.empty()) + if (!first && blocks.empty()) { refreshed = false; break; } - tpool.submit(&waiter, [&]{pull_next_blocks(start_height, next_blocks_start_height, short_chain_history, blocks, next_blocks, next_o_indices, error);}); + tpool.submit(&waiter, [&]{pull_and_parse_next_blocks(start_height, next_blocks_start_height, short_chain_history, blocks, next_blocks, next_parsed_blocks, error);}); - process_blocks(blocks_start_height, blocks, o_indices, added_blocks); - blocks_fetched += added_blocks; + if (!first) + { + process_parsed_blocks(blocks_start_height, blocks, parsed_blocks, added_blocks); + blocks_fetched += added_blocks; + } waiter.wait(); - if(blocks_start_height == next_blocks_start_height) + if(!first && blocks_start_height == next_blocks_start_height) { m_node_rpc_proxy.set_height(m_blockchain.size()); refreshed = true; @@ -2252,7 +2260,8 @@ void wallet2::refresh(uint64_t start_height, uint64_t & blocks_fetched, bool& re // switch to the new blocks from the daemon blocks_start_height = next_blocks_start_height; blocks = std::move(next_blocks); - o_indices = next_o_indices; + parsed_blocks = std::move(next_parsed_blocks); + first = false; // handle error from async fetching thread if (error) diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 3a1f555d7bd..8f036a707fc 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -452,6 +452,14 @@ namespace tools typedef std::tuple get_outs_entry; + struct parsed_block + { + crypto::hash hash; + cryptonote::block block; + cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices o_indices; + bool error; + }; + /*! * \brief Generates a wallet or restores one. * \param wallet_ Name of wallet file @@ -1126,8 +1134,8 @@ namespace tools void pull_blocks(uint64_t start_height, uint64_t& blocks_start_height, const std::list &short_chain_history, std::vector &blocks, std::vector &o_indices); void pull_hashes(uint64_t start_height, uint64_t& blocks_start_height, const std::list &short_chain_history, std::vector &hashes); void fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height, std::list &short_chain_history); - void pull_next_blocks(uint64_t start_height, uint64_t &blocks_start_height, std::list &short_chain_history, const std::vector &prev_blocks, std::vector &blocks, std::vector &o_indices, bool &error); - void process_blocks(uint64_t start_height, const std::vector &blocks, const std::vector &o_indices, uint64_t& blocks_added); + void pull_and_parse_next_blocks(uint64_t start_height, uint64_t &blocks_start_height, std::list &short_chain_history, const std::vector &prev_blocks, std::vector &blocks, std::vector &parsed_blocks, bool &error); + void process_parsed_blocks(uint64_t start_height, const std::vector &blocks, const std::vector &parsed_blocks, uint64_t& blocks_added); uint64_t select_transfers(uint64_t needed_money, std::vector unused_transfers_indices, std::vector& selected_transfers, bool trusted_daemon) const; bool prepare_file_names(const std::string& file_path); void process_unconfirmed(const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t height); From f7f1917ed4708a73e54d9f983e6919df764beb06 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 18 Apr 2018 09:31:11 +0100 Subject: [PATCH 0130/1404] wallet2: remove useless device locking --- src/wallet/wallet2.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 57f75e3ec8c..e220cc05821 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -1093,9 +1093,8 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote //ensure device is let in NONE mode in any case hw::device &hwdev = m_account.get_device(); - boost::unique_lock hwdev_lock (hwdev); + boost::unique_lock hwdev_lock (hwdev, boost::defer_lock); hw::reset_mode rst(hwdev); - hwdev_lock.unlock(); // In this function, tx (probably) only contains the base information // (that is, the prunable stuff may or may not be included) From 41be339655018d17c3169d3add8d5ca81f960c6a Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 18 Apr 2018 09:33:46 +0100 Subject: [PATCH 0131/1404] wallet2: avoid duplicate parsing of tx extra --- src/wallet/wallet2.cpp | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index e220cc05821..7ebc648bfcd 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -1146,15 +1146,18 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote } // additional tx pubkeys and derivations for multi-destination transfers involving one or more subaddresses - std::vector additional_tx_pub_keys = get_additional_tx_pub_keys_from_extra(tx); + tx_extra_additional_pub_keys additional_tx_pub_keys; std::vector additional_derivations; - for (size_t i = 0; i < additional_tx_pub_keys.size(); ++i) + if (find_tx_extra_field_by_type(tx_extra_fields, additional_tx_pub_keys)) { - additional_derivations.push_back({}); - if (!hwdev.generate_key_derivation(additional_tx_pub_keys[i], keys.m_view_secret_key, additional_derivations.back())) + for (size_t i = 0; i < additional_tx_pub_keys.data.size(); ++i) { - MWARNING("Failed to generate key derivation from tx pubkey, skipping"); - additional_derivations.pop_back(); + additional_derivations.push_back({}); + if (!hwdev.generate_key_derivation(additional_tx_pub_keys.data[i], keys.m_view_secret_key, additional_derivations.back())) + { + MWARNING("Failed to generate key derivation from tx pubkey, skipping"); + additional_derivations.pop_back(); + } } } hwdev_lock.unlock(); @@ -1187,7 +1190,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote THROW_WALLET_EXCEPTION_IF(tx_scan_info[i].error, error::acc_outs_lookup_error, tx, tx_pub_key, m_account.get_keys()); if (tx_scan_info[i].received) { - hwdev.conceal_derivation(tx_scan_info[i].received->derivation, tx_pub_key, additional_tx_pub_keys, derivation, additional_derivations); + hwdev.conceal_derivation(tx_scan_info[i].received->derivation, tx_pub_key, additional_tx_pub_keys.data, derivation, additional_derivations); scan_output(tx, tx_pub_key, i, tx_scan_info[i], num_vouts_received, tx_money_got_in_outs, outs); } } @@ -1210,7 +1213,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote THROW_WALLET_EXCEPTION_IF(tx_scan_info[i].error, error::acc_outs_lookup_error, tx, tx_pub_key, m_account.get_keys()); if (tx_scan_info[i].received) { - hwdev.conceal_derivation(tx_scan_info[i].received->derivation, tx_pub_key, additional_tx_pub_keys, derivation, additional_derivations); + hwdev.conceal_derivation(tx_scan_info[i].received->derivation, tx_pub_key, additional_tx_pub_keys.data, derivation, additional_derivations); scan_output(tx, tx_pub_key, i, tx_scan_info[i], num_vouts_received, tx_money_got_in_outs, outs); } } @@ -1226,7 +1229,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote { hwdev_lock.lock(); hwdev.set_mode(hw::device::NONE); - hwdev.conceal_derivation(tx_scan_info[i].received->derivation, tx_pub_key, additional_tx_pub_keys, derivation, additional_derivations); + hwdev.conceal_derivation(tx_scan_info[i].received->derivation, tx_pub_key, additional_tx_pub_keys.data, derivation, additional_derivations); scan_output(tx, tx_pub_key, i, tx_scan_info[i], num_vouts_received, tx_money_got_in_outs, outs); hwdev_lock.unlock(); } From f025ae9760c4cc5c9438a6884b07975e9e3014b1 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 18 Apr 2018 10:28:35 +0100 Subject: [PATCH 0132/1404] wallet2: avoid re-parsing blocks when refreshing in a loop --- src/wallet/wallet2.cpp | 11 ++++------- src/wallet/wallet2.h | 2 +- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 7ebc648bfcd..e61d9ca2a96 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -1772,7 +1772,7 @@ void wallet2::refresh(uint64_t start_height, uint64_t & blocks_fetched) refresh(start_height, blocks_fetched, received_money); } //---------------------------------------------------------------------------------------------------- -void wallet2::pull_and_parse_next_blocks(uint64_t start_height, uint64_t &blocks_start_height, std::list &short_chain_history, const std::vector &prev_blocks, std::vector &blocks, std::vector &parsed_blocks, bool &error) +void wallet2::pull_and_parse_next_blocks(uint64_t start_height, uint64_t &blocks_start_height, std::list &short_chain_history, const std::vector &prev_blocks, const std::vector &prev_parsed_blocks, std::vector &blocks, std::vector &parsed_blocks, bool &error) { error = false; @@ -1781,13 +1781,10 @@ void wallet2::pull_and_parse_next_blocks(uint64_t start_height, uint64_t &blocks drop_from_short_history(short_chain_history, 3); // prepend the last 3 blocks, should be enough to guard against a block or two's reorg - cryptonote::block bl; - std::vector::const_reverse_iterator i = prev_blocks.rbegin(); + std::vector::const_reverse_iterator i = prev_parsed_blocks.rbegin(); for (size_t n = 0; n < std::min((size_t)3, prev_blocks.size()); ++n) { - bool ok = cryptonote::parse_and_validate_block_from_blob(i->block, bl); - THROW_WALLET_EXCEPTION_IF(!ok, error::block_parse_error, i->block); - short_chain_history.push_front(cryptonote::get_block_hash(bl)); + short_chain_history.push_front(i->hash); ++i; } @@ -2244,7 +2241,7 @@ void wallet2::refresh(uint64_t start_height, uint64_t & blocks_fetched, bool& re refreshed = false; break; } - tpool.submit(&waiter, [&]{pull_and_parse_next_blocks(start_height, next_blocks_start_height, short_chain_history, blocks, next_blocks, next_parsed_blocks, error);}); + tpool.submit(&waiter, [&]{pull_and_parse_next_blocks(start_height, next_blocks_start_height, short_chain_history, blocks, parsed_blocks, next_blocks, next_parsed_blocks, error);}); if (!first) { diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 8f036a707fc..ee70492317d 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -1134,7 +1134,7 @@ namespace tools void pull_blocks(uint64_t start_height, uint64_t& blocks_start_height, const std::list &short_chain_history, std::vector &blocks, std::vector &o_indices); void pull_hashes(uint64_t start_height, uint64_t& blocks_start_height, const std::list &short_chain_history, std::vector &hashes); void fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height, std::list &short_chain_history); - void pull_and_parse_next_blocks(uint64_t start_height, uint64_t &blocks_start_height, std::list &short_chain_history, const std::vector &prev_blocks, std::vector &blocks, std::vector &parsed_blocks, bool &error); + void pull_and_parse_next_blocks(uint64_t start_height, uint64_t &blocks_start_height, std::list &short_chain_history, const std::vector &prev_blocks, const std::vector &prev_parsed_blocks, std::vector &blocks, std::vector &parsed_blocks, bool &error); void process_parsed_blocks(uint64_t start_height, const std::vector &blocks, const std::vector &parsed_blocks, uint64_t& blocks_added); uint64_t select_transfers(uint64_t needed_money, std::vector unused_transfers_indices, std::vector& selected_transfers, bool trusted_daemon) const; bool prepare_file_names(const std::string& file_path); From ef2cb6328788e14e8d593593ea9b2b716d72d6bd Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 18 Apr 2018 11:41:52 +0100 Subject: [PATCH 0133/1404] blockchain: simplify/speedup handle_get_objects --- src/cryptonote_core/blockchain.cpp | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 59563380de9..208ea1efae0 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -1633,14 +1633,17 @@ bool Blockchain::handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NO std::vector> blocks; get_blocks(arg.blocks, blocks, rsp.missed_ids); - for (const auto& bl: blocks) + for (auto& bl: blocks) { std::vector missed_tx_ids; std::vector txs; + rsp.blocks.push_back(block_complete_entry()); + block_complete_entry& e = rsp.blocks.back(); + // FIXME: s/rsp.missed_ids/missed_tx_id/ ? Seems like rsp.missed_ids // is for missed blocks, not missed transactions as well. - get_transactions_blobs(bl.second.tx_hashes, txs, missed_tx_ids); + get_transactions_blobs(bl.second.tx_hashes, e.txs, missed_tx_ids); if (missed_tx_ids.size() != 0) { @@ -1657,20 +1660,12 @@ bool Blockchain::handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NO return false; } - rsp.blocks.push_back(block_complete_entry()); - block_complete_entry& e = rsp.blocks.back(); //pack block - e.block = bl.first; - //pack transactions - for (const cryptonote::blobdata& tx: txs) - e.txs.push_back(tx); + e.block = std::move(bl.first); } - //get another transactions, if need + //get and pack other transactions, if needed std::vector txs; - get_transactions_blobs(arg.txs, txs, rsp.missed_ids); - //pack aside transactions - for (const auto& tx: txs) - rsp.txs.push_back(tx); + get_transactions_blobs(arg.txs, rsp.txs, rsp.missed_ids); m_db->block_txn_stop(); return true; From dcfd299239cae50caad7a2f115489f86014b5114 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 21 Apr 2018 12:33:02 +0100 Subject: [PATCH 0134/1404] wallet2: speedup refresh key derivation and checking for incoming outputs are threaded in batch before adding blocks to the local blockchain. Other minor bits and bobs are also cached. --- src/wallet/wallet2.cpp | 295 +++++++++++++++++++++++++++++++++-------- src/wallet/wallet2.h | 22 ++- 2 files changed, 258 insertions(+), 59 deletions(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index e61d9ca2a96..ff35ebd9eca 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -1035,6 +1035,23 @@ void wallet2::check_acc_out_precomp(const tx_out &o, const crypto::key_derivatio tx_scan_info.error = false; } //---------------------------------------------------------------------------------------------------- +void wallet2::check_acc_out_precomp(const tx_out &o, const crypto::key_derivation &derivation, const std::vector &additional_derivations, size_t i, const is_out_data *is_out_data, tx_scan_info_t &tx_scan_info) const +{ + if (!is_out_data || i >= is_out_data->received.size()) + return check_acc_out_precomp(o, derivation, additional_derivations, i, tx_scan_info); + + tx_scan_info.received = is_out_data->received[i]; + if(tx_scan_info.received) + { + tx_scan_info.money_transfered = o.amount; // may be 0 for ringct outputs + } + else + { + tx_scan_info.money_transfered = 0; + } + tx_scan_info.error = false; +} +//---------------------------------------------------------------------------------------------------- static uint64_t decodeRct(const rct::rctSig & rv, const crypto::key_derivation &derivation, unsigned int i, rct::key & mask, hw::device &hwdev) { crypto::secret_key scalar1; @@ -1088,15 +1105,48 @@ void wallet2::scan_output(const cryptonote::transaction &tx, const crypto::publi ++num_vouts_received; } //---------------------------------------------------------------------------------------------------- -void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote::transaction& tx, const std::vector &o_indices, uint64_t height, uint64_t ts, bool miner_tx, bool pool, bool double_spend_seen) +void wallet2::cache_tx_data(const cryptonote::transaction& tx, const crypto::hash &txid, tx_cache_data &tx_cache_data) const { - //ensure device is let in NONE mode in any case - hw::device &hwdev = m_account.get_device(); - - boost::unique_lock hwdev_lock (hwdev, boost::defer_lock); - hw::reset_mode rst(hwdev); + const cryptonote::account_keys& keys = m_account.get_keys(); + + if(!parse_tx_extra(tx.extra, tx_cache_data.tx_extra_fields)) + { + // Extra may only be partially parsed, it's OK if tx_extra_fields contains public key + LOG_PRINT_L0("Transaction extra has unsupported format: " << txid); + tx_cache_data.tx_extra_fields.clear(); + return; + } - // In this function, tx (probably) only contains the base information + // Don't try to extract tx public key if tx has no ouputs + const bool is_miner = tx.vin.size() == 1 && tx.vin[0].type() == typeid(cryptonote::txin_gen); + if (!is_miner || m_refresh_type != RefreshType::RefreshNoCoinbase) + { + const size_t rec_size = is_miner && m_refresh_type == RefreshType::RefreshOptimizeCoinbase ? 1 : tx.vout.size(); + if (!tx.vout.empty()) + { + // if tx.vout is not empty, we loop through all tx pubkeys + const std::vector> rec(rec_size, boost::none); + + tx_extra_pub_key pub_key_field; + size_t pk_index = 0; + while (find_tx_extra_field_by_type(tx_cache_data.tx_extra_fields, pub_key_field, pk_index++)) + tx_cache_data.primary.push_back({pub_key_field.pub_key, {}, rec}); + + // additional tx pubkeys and derivations for multi-destination transfers involving one or more subaddresses + tx_extra_additional_pub_keys additional_tx_pub_keys; + std::vector additional_derivations; + if (find_tx_extra_field_by_type(tx_cache_data.tx_extra_fields, additional_tx_pub_keys)) + { + for (size_t i = 0; i < additional_tx_pub_keys.data.size(); ++i) + tx_cache_data.additional.push_back({additional_tx_pub_keys.data[i], {}, {}}); + } + } + } +} +//---------------------------------------------------------------------------------------------------- +void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote::transaction& tx, const std::vector &o_indices, uint64_t height, uint64_t ts, bool miner_tx, bool pool, bool double_spend_seen, const tx_cache_data &tx_cache_data) +{ + // In this function, tx (probably) only contains the base information // (that is, the prunable stuff may or may not be included) if (!miner_tx && !pool) process_unconfirmed(txid, tx, height); @@ -1104,12 +1154,16 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote std::unordered_map tx_money_got_in_outs; // per receiving subaddress index crypto::public_key tx_pub_key = null_pkey; - std::vector tx_extra_fields; - if(!parse_tx_extra(tx.extra, tx_extra_fields)) + std::vector local_tx_extra_fields; + if (tx_cache_data.tx_extra_fields.empty()) { - // Extra may only be partially parsed, it's OK if tx_extra_fields contains public key - LOG_PRINT_L0("Transaction extra has unsupported format: " << txid); + if(!parse_tx_extra(tx.extra, local_tx_extra_fields)) + { + // Extra may only be partially parsed, it's OK if tx_extra_fields contains public key + LOG_PRINT_L0("Transaction extra has unsupported format: " << txid); + } } + const std::vector &tx_extra_fields = tx_cache_data.tx_extra_fields.empty() ? local_tx_extra_fields : tx_cache_data.tx_extra_fields; // Don't try to extract tx public key if tx has no ouputs size_t pk_index = 0; @@ -1128,6 +1182,11 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote m_callback->on_skip_transaction(height, txid, tx); break; } + if (!tx_cache_data.primary.empty()) + { + THROW_WALLET_EXCEPTION_IF(tx_cache_data.primary.size() < pk_index || pub_key_field.pub_key != tx_cache_data.primary[pk_index - 1].pkey, + error::wallet_internal_error, "tx_cache_data is out of sync"); + } int num_vouts_received = 0; tx_pub_key = pub_key_field.pub_key; @@ -1136,31 +1195,49 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote const cryptonote::account_keys& keys = m_account.get_keys(); crypto::key_derivation derivation; - hwdev_lock.lock(); - hwdev.set_mode(hw::device::TRANSACTION_PARSE); - if (!hwdev.generate_key_derivation(tx_pub_key, keys.m_view_secret_key, derivation)) - { - MWARNING("Failed to generate key derivation from tx pubkey, skipping"); - static_assert(sizeof(derivation) == sizeof(rct::key), "Mismatched sizes of key_derivation and rct::key"); - memcpy(&derivation, rct::identity().bytes, sizeof(derivation)); - } - - // additional tx pubkeys and derivations for multi-destination transfers involving one or more subaddresses - tx_extra_additional_pub_keys additional_tx_pub_keys; std::vector additional_derivations; - if (find_tx_extra_field_by_type(tx_extra_fields, additional_tx_pub_keys)) + tx_extra_additional_pub_keys additional_tx_pub_keys; + const wallet2::is_out_data *is_out_data_ptr = NULL; + if (tx_cache_data.primary.empty()) { - for (size_t i = 0; i < additional_tx_pub_keys.data.size(); ++i) + hw::device &hwdev = m_account.get_device(); + boost::unique_lock hwdev_lock (hwdev); + hw::reset_mode rst(hwdev); + + hwdev.set_mode(hw::device::TRANSACTION_PARSE); + if (!hwdev.generate_key_derivation(tx_pub_key, keys.m_view_secret_key, derivation)) { - additional_derivations.push_back({}); - if (!hwdev.generate_key_derivation(additional_tx_pub_keys.data[i], keys.m_view_secret_key, additional_derivations.back())) + MWARNING("Failed to generate key derivation from tx pubkey in " << txid << ", skipping"); + static_assert(sizeof(derivation) == sizeof(rct::key), "Mismatched sizes of key_derivation and rct::key"); + memcpy(&derivation, rct::identity().bytes, sizeof(derivation)); + } + + // additional tx pubkeys and derivations for multi-destination transfers involving one or more subaddresses + if (find_tx_extra_field_by_type(tx_extra_fields, additional_tx_pub_keys)) + { + for (size_t i = 0; i < additional_tx_pub_keys.data.size(); ++i) { - MWARNING("Failed to generate key derivation from tx pubkey, skipping"); - additional_derivations.pop_back(); + additional_derivations.push_back({}); + if (!hwdev.generate_key_derivation(additional_tx_pub_keys.data[i], keys.m_view_secret_key, additional_derivations.back())) + { + MWARNING("Failed to generate key derivation from additional tx pubkey in " << txid << ", skipping"); + memcpy(&additional_derivations.back(), rct::identity().bytes, sizeof(crypto::key_derivation)); + } } } } - hwdev_lock.unlock(); + else + { + THROW_WALLET_EXCEPTION_IF(pk_index - 1 >= tx_cache_data.primary.size(), + error::wallet_internal_error, "pk_index out of range of tx_cache_data"); + is_out_data_ptr = &tx_cache_data.primary[pk_index - 1]; + derivation = tx_cache_data.primary[pk_index - 1].derivation; + for (size_t n = 0; n < tx_cache_data.additional.size(); ++n) + { + additional_tx_pub_keys.data.push_back(tx_cache_data.additional[n].pkey); + additional_derivations.push_back(tx_cache_data.additional[n].derivation); + } + } if (miner_tx && m_refresh_type == RefreshNoCoinbase) { @@ -1168,7 +1245,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote } else if (miner_tx && m_refresh_type == RefreshOptimizeCoinbase) { - check_acc_out_precomp(tx.vout[0], derivation, additional_derivations, 0, tx_scan_info[0]); + check_acc_out_precomp(tx.vout[0], derivation, additional_derivations, 0, is_out_data_ptr, tx_scan_info[0]); THROW_WALLET_EXCEPTION_IF(tx_scan_info[0].error, error::acc_outs_lookup_error, tx, tx_pub_key, m_account.get_keys()); // this assumes that the miner tx pays a single address @@ -1179,11 +1256,12 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote for (size_t i = 1; i < tx.vout.size(); ++i) { tpool.submit(&waiter, boost::bind(&wallet2::check_acc_out_precomp, this, std::cref(tx.vout[i]), std::cref(derivation), std::cref(additional_derivations), i, - std::ref(tx_scan_info[i]))); + std::cref(is_out_data_ptr), std::ref(tx_scan_info[i]))); } waiter.wait(); // then scan all outputs from 0 - hwdev_lock.lock(); + hw::device &hwdev = m_account.get_device(); + boost::unique_lock hwdev_lock (hwdev); hwdev.set_mode(hw::device::NONE); for (size_t i = 0; i < tx.vout.size(); ++i) { @@ -1194,19 +1272,19 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote scan_output(tx, tx_pub_key, i, tx_scan_info[i], num_vouts_received, tx_money_got_in_outs, outs); } } - hwdev_lock.unlock(); } } - else if (tx.vout.size() > 1 && tools::threadpool::getInstance().get_max_concurrency() > 1) + else if (tx.vout.size() > 1 && tools::threadpool::getInstance().get_max_concurrency() > 1 && !is_out_data_ptr) { for (size_t i = 0; i < tx.vout.size(); ++i) { tpool.submit(&waiter, boost::bind(&wallet2::check_acc_out_precomp, this, std::cref(tx.vout[i]), std::cref(derivation), std::cref(additional_derivations), i, - std::ref(tx_scan_info[i]))); + std::cref(is_out_data_ptr), std::ref(tx_scan_info[i]))); } waiter.wait(); - hwdev_lock.lock(); + hw::device &hwdev = m_account.get_device(); + boost::unique_lock hwdev_lock (hwdev); hwdev.set_mode(hw::device::NONE); for (size_t i = 0; i < tx.vout.size(); ++i) { @@ -1217,21 +1295,20 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote scan_output(tx, tx_pub_key, i, tx_scan_info[i], num_vouts_received, tx_money_got_in_outs, outs); } } - hwdev_lock.unlock(); } else { for (size_t i = 0; i < tx.vout.size(); ++i) { - check_acc_out_precomp(tx.vout[i], derivation, additional_derivations, i, tx_scan_info[i]); + check_acc_out_precomp(tx.vout[i], derivation, additional_derivations, i, is_out_data_ptr, tx_scan_info[i]); THROW_WALLET_EXCEPTION_IF(tx_scan_info[i].error, error::acc_outs_lookup_error, tx, tx_pub_key, m_account.get_keys()); if (tx_scan_info[i].received) { - hwdev_lock.lock(); + hw::device &hwdev = m_account.get_device(); + boost::unique_lock hwdev_lock (hwdev); hwdev.set_mode(hw::device::NONE); hwdev.conceal_derivation(tx_scan_info[i].received->derivation, tx_pub_key, additional_tx_pub_keys.data, derivation, additional_derivations); scan_output(tx, tx_pub_key, i, tx_scan_info[i], num_vouts_received, tx_money_got_in_outs, outs); - hwdev_lock.unlock(); } } } @@ -1571,12 +1648,11 @@ void wallet2::process_outgoing(const crypto::hash &txid, const cryptonote::trans add_rings(tx); } //---------------------------------------------------------------------------------------------------- -void wallet2::process_new_blockchain_entry(const cryptonote::block& b, const cryptonote::block_complete_entry& bche, const crypto::hash& bl_id, uint64_t height, const cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices &o_indices) +void wallet2::process_new_blockchain_entry(const cryptonote::block& b, const cryptonote::block_complete_entry& bche, const parsed_block &parsed_block, const crypto::hash& bl_id, uint64_t height, const std::vector &tx_cache_data, size_t tx_cache_data_offset) { - size_t txidx = 0; - THROW_WALLET_EXCEPTION_IF(bche.txs.size() + 1 != o_indices.indices.size(), error::wallet_internal_error, + THROW_WALLET_EXCEPTION_IF(bche.txs.size() + 1 != parsed_block.o_indices.indices.size(), error::wallet_internal_error, "block transactions=" + std::to_string(bche.txs.size()) + - " not match with daemon response size=" + std::to_string(o_indices.indices.size())); + " not match with daemon response size=" + std::to_string(parsed_block.o_indices.indices.size())); //handle transactions from new block @@ -1585,20 +1661,16 @@ void wallet2::process_new_blockchain_entry(const cryptonote::block& b, const cry { TIME_MEASURE_START(miner_tx_handle_time); if (m_refresh_type != RefreshNoCoinbase) - process_new_transaction(get_transaction_hash(b.miner_tx), b.miner_tx, o_indices.indices[txidx].indices, height, b.timestamp, true, false, false); - ++txidx; + process_new_transaction(get_transaction_hash(b.miner_tx), b.miner_tx, parsed_block.o_indices.indices[0].indices, height, b.timestamp, true, false, false, tx_cache_data[tx_cache_data_offset]); + ++tx_cache_data_offset; TIME_MEASURE_FINISH(miner_tx_handle_time); TIME_MEASURE_START(txs_handle_time); THROW_WALLET_EXCEPTION_IF(bche.txs.size() != b.tx_hashes.size(), error::wallet_internal_error, "Wrong amount of transactions for block"); - size_t idx = 0; - for (const auto& txblob: bche.txs) + THROW_WALLET_EXCEPTION_IF(bche.txs.size() != parsed_block.txes.size(), error::wallet_internal_error, "Wrong amount of transactions for block"); + for (size_t idx = 0; idx < b.tx_hashes.size(); ++idx) { - cryptonote::transaction tx; - bool r = parse_and_validate_tx_base_from_blob(txblob, tx); - THROW_WALLET_EXCEPTION_IF(!r, error::tx_parse_error, txblob); - process_new_transaction(b.tx_hashes[idx], tx, o_indices.indices[txidx++].indices, height, b.timestamp, false, false, false); - ++idx; + process_new_transaction(b.tx_hashes[idx], parsed_block.txes[idx], parsed_block.o_indices.indices[idx+1].indices, height, b.timestamp, false, false, false, tx_cache_data[tx_cache_data_offset++]); } TIME_MEASURE_FINISH(txs_handle_time); LOG_PRINT_L2("Processed block: " << bl_id << ", height " << height << ", " << miner_tx_handle_time + txs_handle_time << "(" << miner_tx_handle_time << "/" << txs_handle_time <<")ms"); @@ -1726,11 +1798,102 @@ void wallet2::process_parsed_blocks(uint64_t start_height, const std::vector tx_cache_data; + for (size_t i = 0; i < blocks.size(); ++i) + num_txes += 1 + parsed_blocks[i].txes.size(); + tx_cache_data.resize(num_txes); + size_t txidx = 0; + for (size_t i = 0; i < blocks.size(); ++i) + { + THROW_WALLET_EXCEPTION_IF(parsed_blocks[i].txes.size() != parsed_blocks[i].block.tx_hashes.size(), + error::wallet_internal_error, "Mismatched parsed_blocks[i].txes.size() and parsed_blocks[i].block.tx_hashes.size()"); + if (m_refresh_type != RefreshNoCoinbase) + tpool.submit(&waiter, [&, i, txidx](){ cache_tx_data(parsed_blocks[i].block.miner_tx, get_transaction_hash(parsed_blocks[i].block.miner_tx), tx_cache_data[txidx]); }); + ++txidx; + for (size_t idx = 0; idx < parsed_blocks[i].txes.size(); ++idx) + { + tpool.submit(&waiter, [&, i, idx, txidx](){ cache_tx_data(parsed_blocks[i].txes[idx], parsed_blocks[i].block.tx_hashes[idx], tx_cache_data[txidx]); }); + ++txidx; + } + } + THROW_WALLET_EXCEPTION_IF(txidx != num_txes, error::wallet_internal_error, "txidx does not match tx_cache_data size"); + waiter.wait(); + + hw::device &hwdev = m_account.get_device(); + hw::reset_mode rst(hwdev); + hwdev.set_mode(hw::device::TRANSACTION_PARSE); + const cryptonote::account_keys &keys = m_account.get_keys(); + + auto gender = [&](wallet2::is_out_data &iod) { + boost::unique_lock hwdev_lock(hwdev); + if (!hwdev.generate_key_derivation(iod.pkey, keys.m_view_secret_key, iod.derivation)) + { + MWARNING("Failed to generate key derivation from tx pubkey, skipping"); + static_assert(sizeof(iod.derivation) == sizeof(rct::key), "Mismatched sizes of key_derivation and rct::key"); + memcpy(&iod.derivation, rct::identity().bytes, sizeof(iod.derivation)); + } + }; + + for (auto &slot: tx_cache_data) + { + for (auto &iod: slot.primary) + tpool.submit(&waiter, [&gender, &iod]() { gender(iod); }); + for (auto &iod: slot.additional) + tpool.submit(&waiter, [&gender, &iod]() { gender(iod); }); + } + waiter.wait(); + + auto geniod = [&](const cryptonote::transaction &tx, size_t n_vouts, size_t txidx) { + for (size_t k = 0; k < n_vouts; ++k) + { + const auto &o = tx.vout[k]; + if (o.target.type() == typeid(cryptonote::txout_to_key)) + { + std::vector additional_derivations; + for (const auto &iod: tx_cache_data[txidx].additional) + additional_derivations.push_back(iod.derivation); + const auto &key = boost::get(o.target).key; + for (size_t l = 0; l < tx_cache_data[txidx].primary.size(); ++l) + { + THROW_WALLET_EXCEPTION_IF(tx_cache_data[txidx].primary[l].received.size() != n_vouts, + error::wallet_internal_error, "Unexpected received array size"); + tx_cache_data[txidx].primary[l].received[k] = is_out_to_acc_precomp(m_subaddresses, key, tx_cache_data[txidx].primary[l].derivation, additional_derivations, k, hwdev); + additional_derivations.clear(); + } + } + } + }; + + txidx = 0; + for (size_t i = 0; i < blocks.size(); ++i) + { + if (m_refresh_type != RefreshType::RefreshNoCoinbase) + { + THROW_WALLET_EXCEPTION_IF(txidx >= tx_cache_data.size(), error::wallet_internal_error, "txidx out of range"); + const size_t n_vouts = m_refresh_type == RefreshType::RefreshOptimizeCoinbase ? 1 : parsed_blocks[i].block.miner_tx.vout.size(); + tpool.submit(&waiter, [&, i, txidx](){ geniod(parsed_blocks[i].block.miner_tx, n_vouts, txidx); }); + } + ++txidx; + for (size_t j = 0; j < parsed_blocks[i].txes.size(); ++j) + { + THROW_WALLET_EXCEPTION_IF(txidx >= tx_cache_data.size(), error::wallet_internal_error, "txidx out of range"); + tpool.submit(&waiter, [&, i, j, txidx](){ geniod(parsed_blocks[i].txes[j], parsed_blocks[i].txes[j].vout.size(), txidx); }); + ++txidx; + } + } + THROW_WALLET_EXCEPTION_IF(txidx != tx_cache_data.size(), error::wallet_internal_error, "txidx did not reach expected value"); + waiter.wait(); + hwdev.set_mode(hw::device::NONE); + + size_t tx_cache_data_offset = 0; for (size_t i = 0; i < blocks.size(); ++i) { const crypto::hash &bl_id = parsed_blocks[i].hash; @@ -1738,7 +1901,7 @@ void wallet2::process_parsed_blocks(uint64_t start_height, const std::vector= m_blockchain.size()) { - process_new_blockchain_entry(bl, blocks[i], bl_id, current_index, parsed_blocks[i].o_indices); + process_new_blockchain_entry(bl, blocks[i], parsed_blocks[i], bl_id, current_index, tx_cache_data, tx_cache_data_offset); ++blocks_added; } else if(bl_id != m_blockchain[current_index]) @@ -1750,13 +1913,14 @@ void wallet2::process_parsed_blocks(uint64_t start_height, const std::vector lock(error_lock); + error = true; + } + }); + } + } + waiter.wait(); } catch(...) { @@ -2023,7 +2204,7 @@ void wallet2::update_pool_state(bool refreshed) [tx_hash](const std::pair &e) { return e.first == tx_hash; }); if (i != txids.end()) { - process_new_transaction(tx_hash, tx, std::vector(), 0, time(NULL), false, true, tx_entry.double_spend_seen); + process_new_transaction(tx_hash, tx, std::vector(), 0, time(NULL), false, true, tx_entry.double_spend_seen, {}); m_scanned_pool_txs[0].insert(tx_hash); if (m_scanned_pool_txs[0].size() > 5000) { diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index ee70492317d..522a2404a58 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -456,10 +456,25 @@ namespace tools { crypto::hash hash; cryptonote::block block; + std::vector txes; cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices o_indices; bool error; }; + struct is_out_data + { + crypto::public_key pkey; + crypto::key_derivation derivation; + std::vector> received; + }; + + struct tx_cache_data + { + std::vector tx_extra_fields; + std::vector primary; + std::vector additional; + }; + /*! * \brief Generates a wallet or restores one. * \param wallet_ Name of wallet file @@ -1125,8 +1140,8 @@ namespace tools * \param password Password of wallet file */ bool load_keys(const std::string& keys_file_name, const epee::wipeable_string& password); - void process_new_transaction(const crypto::hash &txid, const cryptonote::transaction& tx, const std::vector &o_indices, uint64_t height, uint64_t ts, bool miner_tx, bool pool, bool double_spend_seen); - void process_new_blockchain_entry(const cryptonote::block& b, const cryptonote::block_complete_entry& bche, const crypto::hash& bl_id, uint64_t height, const cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices &o_indices); + void process_new_transaction(const crypto::hash &txid, const cryptonote::transaction& tx, const std::vector &o_indices, uint64_t height, uint64_t ts, bool miner_tx, bool pool, bool double_spend_seen, const tx_cache_data &tx_cache_data); + void process_new_blockchain_entry(const cryptonote::block& b, const cryptonote::block_complete_entry& bche, const parsed_block &parsed_block, const crypto::hash& bl_id, uint64_t height, const std::vector &tx_cache_data, size_t tx_cache_data_offset); void detach_blockchain(uint64_t height); void get_short_chain_history(std::list& ids) const; bool is_tx_spendtime_unlocked(uint64_t unlock_time, uint64_t block_height) const; @@ -1146,6 +1161,7 @@ namespace tools bool generate_chacha_key_from_secret_keys(crypto::chacha_key &key) const; crypto::hash get_payment_id(const pending_tx &ptx) const; void check_acc_out_precomp(const cryptonote::tx_out &o, const crypto::key_derivation &derivation, const std::vector &additional_derivations, size_t i, tx_scan_info_t &tx_scan_info) const; + void check_acc_out_precomp(const cryptonote::tx_out &o, const crypto::key_derivation &derivation, const std::vector &additional_derivations, size_t i, const is_out_data *is_out_data, tx_scan_info_t &tx_scan_info) const; void parse_block_round(const cryptonote::blobdata &blob, cryptonote::block &bl, crypto::hash &bl_id, bool &error) const; uint64_t get_upper_transaction_size_limit() const; std::vector get_unspent_amounts_vector() const; @@ -1175,6 +1191,8 @@ namespace tools uint64_t get_segregation_fork_height() const; + void cache_tx_data(const cryptonote::transaction& tx, const crypto::hash &txid, tx_cache_data &tx_cache_data) const; + cryptonote::account_base m_account; boost::optional m_daemon_login; std::string m_daemon_address; From 5d3e70229e0f12891817b81705652ecf5d49919a Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 24 Apr 2018 01:03:34 +0100 Subject: [PATCH 0135/1404] wallet2: remove old blockchain size cache hack --- src/wallet/wallet2.cpp | 16 ++-------------- src/wallet/wallet2.h | 3 +-- 2 files changed, 3 insertions(+), 16 deletions(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index ff35ebd9eca..f911b3a97e7 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -769,8 +769,6 @@ bool wallet2::init(std::string daemon_address, boost::optionalon_new_block(height, b); @@ -2258,7 +2255,6 @@ void wallet2::fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height, while (missing_blocks-- > 0) m_blockchain.push_back(crypto::null_hash); // maybe a bit suboptimal, but deque won't do huge reallocs like vector m_blockchain.push_back(m_checkpoints.get_points().at(checkpoint_height)); - m_local_bc_height = m_blockchain.size(); short_chain_history.clear(); get_short_chain_history(short_chain_history); } @@ -2291,7 +2287,6 @@ void wallet2::fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height, if (!(current_index % 1000)) LOG_PRINT_L2( "Skipped block by height: " << current_index); m_blockchain.push_back(bl_id); - ++m_local_bc_height; if (0 != m_callback) { // FIXME: this isn't right, but simplewallet just logs that we got a block. @@ -2352,7 +2347,6 @@ void wallet2::refresh(uint64_t start_height, uint64_t & blocks_fetched, bool& re // Update lw heights m_light_wallet_scanned_block_height = res.scanned_block_height; m_light_wallet_blockchain_height = res.blockchain_height; - m_local_bc_height = res.blockchain_height; // If new height - call new_block callback if(m_light_wallet_blockchain_height != prev_height) { @@ -2607,7 +2601,6 @@ void wallet2::detach_blockchain(uint64_t height) size_t blocks_detached = m_blockchain.size() - height; m_blockchain.crop(height); - m_local_bc_height -= blocks_detached; for (auto it = m_payments.begin(); it != m_payments.end(); ) { @@ -2649,7 +2642,6 @@ bool wallet2::clear() m_scanned_pool_txs[0].clear(); m_scanned_pool_txs[1].clear(); m_address_book.clear(); - m_local_bc_height = 1; m_subaddresses.clear(); m_subaddress_labels.clear(); return true; @@ -4025,8 +4017,6 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass if (get_num_subaddress_accounts() == 0) add_subaddress_account(tr("Primary account")); - m_local_bc_height = m_blockchain.size(); - try { find_and_save_rings(false); @@ -4406,7 +4396,6 @@ void wallet2::rescan_blockchain(bool refresh) crypto::hash genesis_hash = get_block_hash(genesis); m_blockchain.push_back(genesis_hash); add_subaddress_account(tr("Primary account")); - m_local_bc_height = 1; if (refresh) this->refresh(); @@ -4422,7 +4411,7 @@ bool wallet2::is_transfer_unlocked(uint64_t unlock_time, uint64_t block_height) if(!is_tx_spendtime_unlocked(unlock_time, block_height)) return false; - if(block_height + CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE > m_local_bc_height) + if(block_height + CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE > get_blockchain_current_height()) return false; return true; @@ -4433,7 +4422,7 @@ bool wallet2::is_tx_spendtime_unlocked(uint64_t unlock_time, uint64_t block_heig if(unlock_time < CRYPTONOTE_MAX_BLOCK_NUMBER) { //interpret as block index - if(m_local_bc_height-1 + CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_BLOCKS >= unlock_time) + if(get_blockchain_current_height()-1 + CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_BLOCKS >= unlock_time) return true; else return false; @@ -10001,7 +9990,6 @@ void wallet2::import_blockchain(const std::tuple wallet2::export_outputs() const diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 522a2404a58..b3c84228818 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -745,7 +745,7 @@ namespace tools void get_unconfirmed_payments_out(std::list>& unconfirmed_payments, const boost::optional& subaddr_account = boost::none, const std::set& subaddr_indices = {}) const; void get_unconfirmed_payments(std::list>& unconfirmed_payments, const boost::optional& subaddr_account = boost::none, const std::set& subaddr_indices = {}) const; - uint64_t get_blockchain_current_height() const { return m_local_bc_height; } + uint64_t get_blockchain_current_height() const { return m_light_wallet_blockchain_height ? m_light_wallet_blockchain_height : m_blockchain.size(); } void rescan_spent(); void rescan_blockchain(bool refresh = true); bool is_transfer_unlocked(const transfer_details& td) const; @@ -1200,7 +1200,6 @@ namespace tools std::string m_keys_file; epee::net_utils::http::http_simple_client m_http_client; hashchain m_blockchain; - std::atomic m_local_bc_height; //temporary workaround std::unordered_map m_unconfirmed_txs; std::unordered_map m_confirmed_txs; std::unordered_multimap m_unconfirmed_payments; From 2704624eae34016642c940811e9bcfc18aa58a82 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 24 Apr 2018 01:04:35 +0100 Subject: [PATCH 0136/1404] wallet2: remove unneeded divisions --- src/wallet/wallet2.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index f911b3a97e7..4c0223eab32 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -1674,7 +1674,7 @@ void wallet2::process_new_blockchain_entry(const cryptonote::block& b, const cry LOG_PRINT_L2("Processed block: " << bl_id << ", height " << height << ", " << miner_tx_handle_time + txs_handle_time << "(" << miner_tx_handle_time << "/" << txs_handle_time <<")ms"); }else { - if (!(height % 100)) + if (!(height % 128)) LOG_PRINT_L2( "Skipped block by timestamp, height: " << height << ", block time " << b.timestamp << ", account time " << m_account.get_createtime()); } m_blockchain.push_back(bl_id); @@ -2284,7 +2284,7 @@ void wallet2::fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height, { if(current_index >= m_blockchain.size()) { - if (!(current_index % 1000)) + if (!(current_index % 1024)) LOG_PRINT_L2( "Skipped block by height: " << current_index); m_blockchain.push_back(bl_id); From 2771a18e85bd91dd881f89f6c14d26cba35a1844 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 26 Apr 2018 11:44:47 +0100 Subject: [PATCH 0137/1404] threadpool: allow leaf functions to run concurrently Decrease the number of worker threads by one to account for the fact the calling thread acts as a worker thread now --- src/common/threadpool.cpp | 37 +++++++++++----- src/common/threadpool.h | 7 +-- src/cryptonote_core/blockchain.cpp | 12 +++--- src/cryptonote_core/cryptonote_core.cpp | 4 +- src/ringct/rctSigs.cpp | 12 +++--- src/wallet/wallet2.cpp | 34 +++++++-------- tests/unit_tests/threadpool.cpp | 57 ++++++++++++++++++++++--- 7 files changed, 112 insertions(+), 51 deletions(-) diff --git a/src/common/threadpool.cpp b/src/common/threadpool.cpp index fb238dca7a1..6b69e2a12c9 100644 --- a/src/common/threadpool.cpp +++ b/src/common/threadpool.cpp @@ -36,6 +36,7 @@ #include "common/util.h" static __thread int depth = 0; +static __thread bool is_leaf = false; namespace tools { @@ -43,9 +44,9 @@ threadpool::threadpool(unsigned int max_threads) : running(true), active(0) { boost::thread::attributes attrs; attrs.set_stack_size(THREAD_STACK_SIZE); max = max_threads ? max_threads : tools::get_max_concurrency(); - unsigned int i = max; + size_t i = max ? max - 1 : 0; while(i--) { - threads.push_back(boost::thread(attrs, boost::bind(&threadpool::run, this))); + threads.push_back(boost::thread(attrs, boost::bind(&threadpool::run, this, false))); } } @@ -60,20 +61,25 @@ threadpool::~threadpool() { } } -void threadpool::submit(waiter *obj, std::function f) { - entry e = {obj, f}; +void threadpool::submit(waiter *obj, std::function f, bool leaf) { + CHECK_AND_ASSERT_THROW_MES(!is_leaf, "A leaf routine is using a thread pool"); boost::unique_lock lock(mutex); - if ((active == max && !queue.empty()) || depth > 0) { + if (!leaf && ((active == max && !queue.empty()) || depth > 0)) { // if all available threads are already running // and there's work waiting, just run in current thread lock.unlock(); ++depth; + is_leaf = leaf; f(); --depth; + is_leaf = false; } else { if (obj) obj->inc(); - queue.push_back(e); + if (leaf) + queue.push_front({obj, f, leaf}); + else + queue.push_back({obj, f, leaf}); has_work.notify_one(); } } @@ -91,7 +97,7 @@ threadpool::waiter::~waiter() } try { - wait(); + wait(NULL); } catch (const std::exception &e) { @@ -99,9 +105,12 @@ threadpool::waiter::~waiter() } } -void threadpool::waiter::wait() { +void threadpool::waiter::wait(threadpool *tpool) { + if (tpool) + tpool->run(true); boost::unique_lock lock(mt); - while(num) cv.wait(lock); + while(num) + cv.wait(lock); } void threadpool::waiter::inc() { @@ -113,15 +122,19 @@ void threadpool::waiter::dec() { const boost::unique_lock lock(mt); num--; if (!num) - cv.notify_one(); + cv.notify_all(); } -void threadpool::run() { +void threadpool::run(bool flush) { boost::unique_lock lock(mutex); while (running) { entry e; while(queue.empty() && running) + { + if (flush) + return; has_work.wait(lock); + } if (!running) break; active++; @@ -129,8 +142,10 @@ void threadpool::run() { queue.pop_front(); lock.unlock(); ++depth; + is_leaf = e.leaf; e.f(); --depth; + is_leaf = false; if (e.wo) e.wo->dec(); diff --git a/src/common/threadpool.h b/src/common/threadpool.h index bf80a87f6c6..a43e38a767d 100644 --- a/src/common/threadpool.h +++ b/src/common/threadpool.h @@ -59,7 +59,7 @@ class threadpool public: void inc(); void dec(); - void wait(); //! Wait for a set of tasks to finish. + void wait(threadpool *tpool); //! Wait for a set of tasks to finish. waiter() : num(0){} ~waiter(); }; @@ -67,7 +67,7 @@ class threadpool // Submit a task to the pool. The waiter pointer may be // NULL if the caller doesn't care to wait for the // task to finish. - void submit(waiter *waiter, std::function f); + void submit(waiter *waiter, std::function f, bool leaf = false); unsigned int get_max_concurrency() const; @@ -78,6 +78,7 @@ class threadpool typedef struct entry { waiter *wo; std::function f; + bool leaf; } entry; std::deque queue; boost::condition_variable has_work; @@ -86,7 +87,7 @@ class threadpool unsigned int active; unsigned int max; bool running; - void run(); + void run(bool flush = false); }; } diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 208ea1efae0..854570eb5b8 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -2818,7 +2818,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, { // ND: Speedup // 1. Thread ring signature verification if possible. - tpool.submit(&waiter, boost::bind(&Blockchain::check_ring_signature, this, std::cref(tx_prefix_hash), std::cref(in_to_key.k_image), std::cref(pubkeys[sig_index]), std::cref(tx.signatures[sig_index]), std::ref(results[sig_index]))); + tpool.submit(&waiter, boost::bind(&Blockchain::check_ring_signature, this, std::cref(tx_prefix_hash), std::cref(in_to_key.k_image), std::cref(pubkeys[sig_index]), std::cref(tx.signatures[sig_index]), std::ref(results[sig_index])), true); } else { @@ -2842,7 +2842,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, sig_index++; } if (tx.version == 1 && threads > 1) - waiter.wait(); + waiter.wait(&tpool); if (tx.version == 1) { @@ -4128,11 +4128,11 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector hwdev_lock (hwdev); @@ -1277,9 +1277,9 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote for (size_t i = 0; i < tx.vout.size(); ++i) { tpool.submit(&waiter, boost::bind(&wallet2::check_acc_out_precomp, this, std::cref(tx.vout[i]), std::cref(derivation), std::cref(additional_derivations), i, - std::cref(is_out_data_ptr), std::ref(tx_scan_info[i]))); + std::cref(is_out_data_ptr), std::ref(tx_scan_info[i])), true); } - waiter.wait(); + waiter.wait(&tpool); hw::device &hwdev = m_account.get_device(); boost::unique_lock hwdev_lock (hwdev); @@ -1822,7 +1822,7 @@ void wallet2::process_parsed_blocks(uint64_t start_height, const std::vector= tx_cache_data.size(), error::wallet_internal_error, "txidx out of range"); const size_t n_vouts = m_refresh_type == RefreshType::RefreshOptimizeCoinbase ? 1 : parsed_blocks[i].block.miner_tx.vout.size(); - tpool.submit(&waiter, [&, i, txidx](){ geniod(parsed_blocks[i].block.miner_tx, n_vouts, txidx); }); + tpool.submit(&waiter, [&, i, txidx](){ geniod(parsed_blocks[i].block.miner_tx, n_vouts, txidx); }, true); } ++txidx; for (size_t j = 0; j < parsed_blocks[i].txes.size(); ++j) { THROW_WALLET_EXCEPTION_IF(txidx >= tx_cache_data.size(), error::wallet_internal_error, "txidx out of range"); - tpool.submit(&waiter, [&, i, j, txidx](){ geniod(parsed_blocks[i].txes[j], parsed_blocks[i].txes[j].vout.size(), txidx); }); + tpool.submit(&waiter, [&, i, j, txidx](){ geniod(parsed_blocks[i].txes[j], parsed_blocks[i].txes[j].vout.size(), txidx); }, true); ++txidx; } } THROW_WALLET_EXCEPTION_IF(txidx != tx_cache_data.size(), error::wallet_internal_error, "txidx did not reach expected value"); - waiter.wait(); + waiter.wait(&tpool); hwdev.set_mode(hw::device::NONE); size_t tx_cache_data_offset = 0; @@ -1960,9 +1960,9 @@ void wallet2::pull_and_parse_next_blocks(uint64_t start_height, uint64_t &blocks for (size_t i = 0; i < blocks.size(); ++i) { tpool.submit(&waiter, boost::bind(&wallet2::parse_block_round, this, std::cref(blocks[i].block), - std::ref(parsed_blocks[i].block), std::ref(parsed_blocks[i].hash), std::ref(parsed_blocks[i].error))); + std::ref(parsed_blocks[i].block), std::ref(parsed_blocks[i].hash), std::ref(parsed_blocks[i].error)), true); } - waiter.wait(); + waiter.wait(&tpool); for (size_t i = 0; i < blocks.size(); ++i) { if (parsed_blocks[i].error) @@ -1985,10 +1985,10 @@ void wallet2::pull_and_parse_next_blocks(uint64_t start_height, uint64_t &blocks boost::unique_lock lock(error_lock); error = true; } - }); + }, true); } } - waiter.wait(); + waiter.wait(&tpool); } catch(...) { @@ -2423,7 +2423,7 @@ void wallet2::refresh(uint64_t start_height, uint64_t & blocks_fetched, bool& re process_parsed_blocks(blocks_start_height, blocks, parsed_blocks, added_blocks); blocks_fetched += added_blocks; } - waiter.wait(); + waiter.wait(&tpool); if(!first && blocks_start_height == next_blocks_start_height) { m_node_rpc_proxy.set_height(m_blockchain.size()); @@ -2446,7 +2446,7 @@ void wallet2::refresh(uint64_t start_height, uint64_t & blocks_fetched, bool& re catch (const std::exception&) { blocks_fetched += added_blocks; - waiter.wait(); + waiter.wait(&tpool); if(try_count < 3) { LOG_PRINT_L1("Another try pull_blocks (try_count=" << try_count << ")..."); diff --git a/tests/unit_tests/threadpool.cpp b/tests/unit_tests/threadpool.cpp index 34be1417a12..1307cd7380b 100644 --- a/tests/unit_tests/threadpool.cpp +++ b/tests/unit_tests/threadpool.cpp @@ -35,7 +35,7 @@ TEST(threadpool, wait_nothing) { std::shared_ptr tpool(tools::threadpool::getNewForUnitTests()); tools::threadpool::waiter waiter; - waiter.wait(); + waiter.wait(tpool.get()); } TEST(threadpool, wait_waits) @@ -45,7 +45,7 @@ TEST(threadpool, wait_waits) std::atomic b(false); tpool->submit(&waiter, [&b](){ epee::misc_utils::sleep_no_w(1000); b = true; }); ASSERT_FALSE(b); - waiter.wait(); + waiter.wait(tpool.get()); ASSERT_TRUE(b); } @@ -59,7 +59,7 @@ TEST(threadpool, one_thread) { tpool->submit(&waiter, [&counter](){++counter;}); } - waiter.wait(); + waiter.wait(tpool.get()); ASSERT_EQ(counter, 4096); } @@ -73,7 +73,7 @@ TEST(threadpool, many_threads) { tpool->submit(&waiter, [&counter](){++counter;}); } - waiter.wait(); + waiter.wait(tpool.get()); ASSERT_EQ(counter, 4096); } @@ -85,7 +85,7 @@ static uint64_t fibonacci(std::shared_ptr tpool, uint64_t n) tools::threadpool::waiter waiter; tpool->submit(&waiter, [&tpool, &f1, n](){ f1 = fibonacci(tpool, n-1); }); tpool->submit(&waiter, [&tpool, &f2, n](){ f2 = fibonacci(tpool, n-2); }); - waiter.wait(); + waiter.wait(tpool.get()); return f1 + f2; } @@ -95,7 +95,52 @@ TEST(threadpool, reentrency) tools::threadpool::waiter waiter; uint64_t f = fibonacci(tpool, 13); - waiter.wait(); + waiter.wait(tpool.get()); ASSERT_EQ(f, 233); } +TEST(threadpool, reentrancy) +{ + std::shared_ptr tpool(tools::threadpool::getNewForUnitTests(4)); + tools::threadpool::waiter waiter; + + uint64_t f = fibonacci(tpool, 13); + waiter.wait(tpool.get()); + ASSERT_EQ(f, 233); +} + +TEST(threadpool, leaf_throws) +{ + std::shared_ptr tpool(tools::threadpool::getNewForUnitTests()); + tools::threadpool::waiter waiter; + + bool thrown = false, executed = false; + tpool->submit(&waiter, [&](){ + try { tpool->submit(&waiter, [&](){ executed = true; }); } + catch(const std::exception &e) { thrown = true; } + }, true); + waiter.wait(tpool.get()); + ASSERT_TRUE(thrown); + ASSERT_FALSE(executed); +} + +TEST(threadpool, leaf_reentrancy) +{ + std::shared_ptr tpool(tools::threadpool::getNewForUnitTests(4)); + tools::threadpool::waiter waiter; + + std::atomic counter(0); + for (int i = 0; i < 1000; ++i) + { + tpool->submit(&waiter, [&](){ + tools::threadpool::waiter waiter; + for (int j = 0; j < 500; ++j) + { + tpool->submit(&waiter, [&](){ ++counter; }, true); + } + waiter.wait(tpool.get()); + }); + } + waiter.wait(tpool.get()); + ASSERT_EQ(counter, 500000); +} From 0e4c7d0fae398a46cdc223fc6186821b56e5a8e9 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 27 Apr 2018 11:08:24 +0100 Subject: [PATCH 0138/1404] wallet2: cache ringdb key while refreshing Speeds up syncing with a lot of outgoing outputs as key generation runs Cryptonight. --- src/wallet/wallet2.cpp | 56 +++++++++++++++++++++++++----------------- src/wallet/wallet2.h | 14 +++++++++++ 2 files changed, 47 insertions(+), 23 deletions(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 476bd4a137b..a2798c20344 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -119,6 +119,7 @@ using namespace cryptonote; static const std::string MULTISIG_SIGNATURE_MAGIC = "SigMultisigPkV1"; +std::atomic tools::wallet2::key_ref::refs(0); namespace { @@ -2334,6 +2335,8 @@ bool wallet2::delete_address_book_row(std::size_t row_id) { //---------------------------------------------------------------------------------------------------- void wallet2::refresh(uint64_t start_height, uint64_t & blocks_fetched, bool& received_money) { + key_ref kref(*this); + if(m_light_wallet) { // MyMonero get_address_info needs to be called occasionally to trigger wallet sync. @@ -5757,6 +5760,24 @@ bool wallet2::set_ring_database(const std::string &filename) return true; } +crypto::chacha_key wallet2::get_ringdb_key() +{ + if (!m_ringdb_key) + { + MINFO("caching ringdb key"); + crypto::chacha_key key; + generate_chacha_key_from_secret_keys(key); + m_ringdb_key = key; + } + return *m_ringdb_key; +} + +void wallet2::clear_ringdb_key() +{ + MINFO("clearing ringdb key"); + m_ringdb_key = boost::none; +} + bool wallet2::add_rings(const crypto::chacha_key &key, const cryptonote::transaction_prefix &tx) { if (!m_ringdb) @@ -5767,9 +5788,8 @@ bool wallet2::add_rings(const crypto::chacha_key &key, const cryptonote::transac bool wallet2::add_rings(const cryptonote::transaction_prefix &tx) { - crypto::chacha_key key; - generate_chacha_key_from_secret_keys(key); - try { return add_rings(key, tx); } + key_ref kref(*this); + try { return add_rings(get_ringdb_key(), tx); } catch (const std::exception &e) { return false; } } @@ -5777,9 +5797,8 @@ bool wallet2::remove_rings(const cryptonote::transaction_prefix &tx) { if (!m_ringdb) return false; - crypto::chacha_key key; - generate_chacha_key_from_secret_keys(key); - try { return m_ringdb->remove_rings(key, tx); } + key_ref kref(*this); + try { return m_ringdb->remove_rings(get_ringdb_key(), tx); } catch (const std::exception &e) { return false; } } @@ -5816,10 +5835,8 @@ bool wallet2::get_rings(const crypto::hash &txid, std::vector &outs) { - crypto::chacha_key key; - generate_chacha_key_from_secret_keys(key); - - try { return get_ring(key, key_image, outs); } + key_ref kref(*this); + try { return get_ring(get_ringdb_key(), key_image, outs); } catch (const std::exception &e) { return false; } } @@ -5828,10 +5845,8 @@ bool wallet2::set_ring(const crypto::key_image &key_image, const std::vectorset_ring(key, key_image, outs, relative); } + key_ref kref(*this); + try { return m_ringdb->set_ring(get_ringdb_key(), key_image, outs, relative); } catch (const std::exception &e) { return false; } } @@ -5842,6 +5857,7 @@ bool wallet2::find_and_save_rings(bool force) if (!m_ringdb) return false; + key_ref kref(*this); COMMAND_RPC_GET_TRANSACTIONS::request req = AUTO_VAL_INIT(req); COMMAND_RPC_GET_TRANSACTIONS::response res = AUTO_VAL_INIT(res); @@ -5859,9 +5875,6 @@ bool wallet2::find_and_save_rings(bool force) MDEBUG("Found " << std::to_string(txs_hashes.size()) << " transactions"); - crypto::chacha_key key; - generate_chacha_key_from_secret_keys(key); - // get those transactions from the daemon static const size_t SLICE_SIZE = 200; for (size_t slice = 0; slice < txs_hashes.size(); slice += SLICE_SIZE) @@ -5898,7 +5911,7 @@ bool wallet2::find_and_save_rings(bool force) crypto::hash tx_hash, tx_prefix_hash; THROW_WALLET_EXCEPTION_IF(!cryptonote::parse_and_validate_tx_from_blob(bd, tx, tx_hash, tx_prefix_hash), error::wallet_internal_error, "failed to parse tx from blob"); THROW_WALLET_EXCEPTION_IF(epee::string_tools::pod_to_hex(tx_hash) != tx_info.tx_hash, error::wallet_internal_error, "txid mismatch"); - THROW_WALLET_EXCEPTION_IF(!add_rings(key, tx), error::wallet_internal_error, "Failed to save ring"); + THROW_WALLET_EXCEPTION_IF(!add_rings(get_ringdb_key(), tx), error::wallet_internal_error, "Failed to save ring"); } } @@ -6077,9 +6090,6 @@ void wallet2::get_outs(std::vector> return; } - crypto::chacha_key key; - generate_chacha_key_from_secret_keys(key); - if (fake_outputs_count > 0) { uint64_t segregation_fork_height = get_segregation_fork_height(); @@ -6257,7 +6267,7 @@ void wallet2::get_outs(std::vector> if (td.m_key_image_known && !td.m_key_image_partial) { std::vector ring; - if (get_ring(key, td.m_key_image, ring)) + if (get_ring(get_ringdb_key(), td.m_key_image, ring)) { MINFO("This output has a known ring, reusing (size " << ring.size() << ")"); THROW_WALLET_EXCEPTION_IF(ring.size() > fake_outputs_count + 1, error::wallet_internal_error, @@ -6449,7 +6459,7 @@ void wallet2::get_outs(std::vector> if (td.m_key_image_known && !td.m_key_image_partial) { std::vector ring; - if (get_ring(key, td.m_key_image, ring)) + if (get_ring(get_ringdb_key(), td.m_key_image, ring)) { for (uint64_t out: ring) { diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index b3c84228818..09d99efd892 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -475,6 +475,16 @@ namespace tools std::vector additional; }; + struct key_ref + { + key_ref(tools::wallet2 &w): wallet(w) { ++refs; } + ~key_ref() { if (!--refs) wallet.clear_ringdb_key(); } + + private: + tools::wallet2 &wallet; + static std::atomic refs; + }; + /*! * \brief Generates a wallet or restores one. * \param wallet_ Name of wallet file @@ -1186,6 +1196,9 @@ namespace tools bool add_rings(const cryptonote::transaction_prefix &tx); bool remove_rings(const cryptonote::transaction_prefix &tx); bool get_ring(const crypto::chacha_key &key, const crypto::key_image &key_image, std::vector &outs); + crypto::chacha_key get_ringdb_key(); + void cache_ringdb_key(); + void clear_ringdb_key(); bool get_output_distribution(uint64_t &start_height, std::vector &distribution); @@ -1282,6 +1295,7 @@ namespace tools std::string m_ring_database; bool m_ring_history_saved; std::unique_ptr m_ringdb; + boost::optional m_ringdb_key; }; } BOOST_CLASS_VERSION(tools::wallet2, 24) From e6f4d4acf0190f4b4133ec5b830f2def4c269d98 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 27 Jun 2018 11:09:33 +0100 Subject: [PATCH 0139/1404] unit_tests: do not recreate the same base rct sig all the time Helps a bit when running with valgrind --- tests/unit_tests/ringct.cpp | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/tests/unit_tests/ringct.cpp b/tests/unit_tests/ringct.cpp index 0f4bd3edf5b..6e3958f8a1d 100644 --- a/tests/unit_tests/ringct.cpp +++ b/tests/unit_tests/ringct.cpp @@ -957,12 +957,20 @@ TEST(ringct, fee_burn_valid_zero_out_simple) EXPECT_TRUE(range_proof_test(true, NELTS(inputs), inputs, NELTS(outputs), outputs, true, true)); } +static rctSig make_sig() +{ + static const uint64_t inputs[] = {1000, 1000}; + static const uint64_t outputs[] = {1000, 1000}; + static rct::rctSig sig = make_sample_rct_sig(NELTS(inputs), inputs, NELTS(outputs), outputs, true); + return sig; +} + #define TEST_rctSig_elements(name, op) \ TEST(ringct, rctSig_##name) \ { \ const uint64_t inputs[] = {1000, 1000}; \ const uint64_t outputs[] = {1000, 1000}; \ - rct::rctSig sig = make_sample_rct_sig(NELTS(inputs), inputs, NELTS(outputs), outputs, true); \ + rct::rctSig sig = make_sig(); \ ASSERT_TRUE(rct::verRct(sig)); \ op; \ ASSERT_FALSE(rct::verRct(sig)); \ @@ -994,12 +1002,18 @@ TEST_rctSig_elements(outPk_empty, sig.outPk.resize(0)); TEST_rctSig_elements(outPk_too_many, sig.outPk.push_back(sig.outPk.back())); TEST_rctSig_elements(outPk_too_few, sig.outPk.pop_back()); +static rct::rctSig make_sig_simple() +{ + static const uint64_t inputs[] = {1000, 1000}; + static const uint64_t outputs[] = {1000}; + static rct::rctSig sig = make_sample_simple_rct_sig(NELTS(inputs), inputs, NELTS(outputs), outputs, 1000); + return sig; +} + #define TEST_rctSig_elements_simple(name, op) \ TEST(ringct, rctSig_##name##_simple) \ { \ - const uint64_t inputs[] = {1000, 1000}; \ - const uint64_t outputs[] = {1000}; \ - rct::rctSig sig = make_sample_simple_rct_sig(NELTS(inputs), inputs, NELTS(outputs), outputs, 1000); \ + rct::rctSig sig = make_sig_simple(); \ ASSERT_TRUE(rct::verRctSimple(sig)); \ op; \ ASSERT_FALSE(rct::verRctSimple(sig)); \ From 80d7c6c7305e01c6be1c1cde72e18bc04371914d Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 27 Jun 2018 14:11:35 +0100 Subject: [PATCH 0140/1404] blockchain_utilities: report file offset where a read error occurs --- src/blockchain_utilities/bootstrap_file.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/blockchain_utilities/bootstrap_file.cpp b/src/blockchain_utilities/bootstrap_file.cpp index aa85e5e5312..ba2697226d4 100644 --- a/src/blockchain_utilities/bootstrap_file.cpp +++ b/src/blockchain_utilities/bootstrap_file.cpp @@ -400,18 +400,18 @@ uint64_t BootstrapFile::count_bytes(std::ifstream& import_file, uint64_t blocks, { std::cout << refresh_string; MWARNING("WARNING: chunk_size " << chunk_size << " > BUFFER_SIZE " << BUFFER_SIZE - << " height: " << h-1); + << " height: " << h-1 << ", offset " << bytes_read); throw std::runtime_error("Aborting: chunk size exceeds buffer size"); } if (chunk_size > CHUNK_SIZE_WARNING_THRESHOLD) { std::cout << refresh_string; MDEBUG("NOTE: chunk_size " << chunk_size << " > " << CHUNK_SIZE_WARNING_THRESHOLD << " << height: " - << h-1); + << h-1 << ", offset " << bytes_read); } else if (chunk_size <= 0) { std::cout << refresh_string; - MDEBUG("ERROR: chunk_size " << chunk_size << " <= 0" << " height: " << h-1); + MDEBUG("ERROR: chunk_size " << chunk_size << " <= 0" << " height: " << h-1 << ", offset " << bytes_read); throw std::runtime_error("Aborting"); } // skip to next expected block size value From 13851b28c72f7b0399be2558424ded721b1e714d Mon Sep 17 00:00:00 2001 From: stoffu Date: Wed, 27 Jun 2018 20:33:26 +0900 Subject: [PATCH 0141/1404] simplewallet.sweep_all: show usage when parsing fails --- src/simplewallet/simplewallet.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 90c3da6cad3..2918313d541 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -4964,10 +4964,14 @@ bool simple_wallet::sweep_unmixable(const std::vector &args_) //---------------------------------------------------------------------------------------------------- bool simple_wallet::sweep_main(uint64_t below, const std::vector &args_) { - // sweep_all [index=[,,...]] []
[] + auto print_usage = [below]() + { + fail_msg_writer() << boost::format(tr("usage: %s [index=[,,...]] [] []
[]")) % (below ? "sweep_below" : "sweep_all"); + }; if (args_.size() == 0) { fail_msg_writer() << tr("No address given"); + print_usage(); return true; } @@ -4981,7 +4985,10 @@ bool simple_wallet::sweep_main(uint64_t below, const std::vector &a if (local_args.size() > 0 && local_args[0].substr(0, 6) == "index=") { if (!parse_subaddress_indices(local_args[0], subaddr_indices)) + { + print_usage(); return true; + } local_args.erase(local_args.begin()); } @@ -5049,6 +5056,7 @@ bool simple_wallet::sweep_main(uint64_t below, const std::vector &a if(!r && local_args.size() == 3) { fail_msg_writer() << tr("payment id has invalid format, expected 16 or 64 character hex string: ") << payment_id_str; + print_usage(); return true; } if (payment_id_seen) @@ -5059,6 +5067,7 @@ bool simple_wallet::sweep_main(uint64_t below, const std::vector &a if (!cryptonote::get_account_address_from_str_or_url(info, m_wallet->nettype(), local_args[0], oa_prompter)) { fail_msg_writer() << tr("failed to parse address"); + print_usage(); return true; } From dcbc17e97e782d168d1653e216d415bb4d34a892 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 2 Jun 2018 13:06:41 +0100 Subject: [PATCH 0142/1404] wallet: include a suggested number of confirmations based on amount This is based on how much an attacking miner stands to lose in block rewardy by mining a private chain which double spends a payment. This is not foolproof, since mining is based on luck, and breaks down as the attacking miner nears 50% of the network hash rate, and the estimation is based on a constant block reward. --- src/simplewallet/simplewallet.cpp | 4 ++++ src/wallet/wallet2.cpp | 13 ++++++++++++- src/wallet/wallet2.h | 10 +++++++++- src/wallet/wallet_rpc_server.cpp | 19 +++++++++++++++++++ src/wallet/wallet_rpc_server_commands_defs.h | 6 +++++- 5 files changed, 49 insertions(+), 3 deletions(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 24c42a5972b..a64936e4fe6 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -7408,8 +7408,12 @@ bool simple_wallet::show_transfer(const std::vector &args) if (pd.m_unlock_time < CRYPTONOTE_MAX_BLOCK_NUMBER) { uint64_t bh = std::max(pd.m_unlock_time, pd.m_block_height + CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE); + uint64_t last_block_reward = m_wallet->get_last_block_reward(); + uint64_t suggested_threshold = last_block_reward ? (pd.m_amount + last_block_reward - 1) / last_block_reward : 0; if (bh >= last_block_height) success_msg_writer() << "Locked: " << (bh - last_block_height) << " blocks to unlock"; + else if (suggested_threshold > 0) + success_msg_writer() << std::to_string(last_block_height - bh) << " confirmations (" << suggested_threshold << " suggested threshold)"; else success_msg_writer() << std::to_string(last_block_height - bh) << " confirmations"; } diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index aa6874d170a..5b8f1738b57 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -686,7 +686,8 @@ wallet2::wallet2(network_type nettype, bool restricted): m_light_wallet_unlocked_balance(0), m_key_on_device(false), m_ring_history_saved(false), - m_ringdb() + m_ringdb(), + m_last_block_reward(0) { } @@ -1672,6 +1673,7 @@ void wallet2::process_new_blockchain_entry(const cryptonote::block& b, const cry process_new_transaction(b.tx_hashes[idx], parsed_block.txes[idx], parsed_block.o_indices.indices[idx+1].indices, height, b.timestamp, false, false, false, tx_cache_data[tx_cache_data_offset++]); } TIME_MEASURE_FINISH(txs_handle_time); + m_last_block_reward = cryptonote::get_outs_money_amount(b.miner_tx); LOG_PRINT_L2("Processed block: " << bl_id << ", height " << height << ", " << miner_tx_handle_time + txs_handle_time << "(" << miner_tx_handle_time << "/" << txs_handle_time <<")ms"); }else { @@ -3166,6 +3168,7 @@ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string& cryptonote::block b; generate_genesis(b); m_blockchain.push_back(get_block_hash(b)); + m_last_block_reward = cryptonote::get_outs_money_amount(b.miner_tx); add_subaddress_account(tr("Primary account")); if (!wallet_.empty()) @@ -3224,6 +3227,7 @@ crypto::secret_key wallet2::generate(const std::string& wallet_, const epee::wip cryptonote::block b; generate_genesis(b); m_blockchain.push_back(get_block_hash(b)); + m_last_block_reward = cryptonote::get_outs_money_amount(b.miner_tx); add_subaddress_account(tr("Primary account")); if (!wallet_.empty()) @@ -3319,6 +3323,7 @@ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string& cryptonote::block b; generate_genesis(b); m_blockchain.push_back(get_block_hash(b)); + m_last_block_reward = cryptonote::get_outs_money_amount(b.miner_tx); add_subaddress_account(tr("Primary account")); if (!wallet_.empty()) @@ -3371,6 +3376,7 @@ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string& cryptonote::block b; generate_genesis(b); m_blockchain.push_back(get_block_hash(b)); + m_last_block_reward = cryptonote::get_outs_money_amount(b.miner_tx); add_subaddress_account(tr("Primary account")); if (!wallet_.empty()) @@ -3417,6 +3423,7 @@ void wallet2::restore(const std::string& wallet_, const epee::wipeable_string& p m_subaddress_lookahead_major = 5; m_subaddress_lookahead_minor = 20; } + m_last_block_reward = cryptonote::get_outs_money_amount(b.miner_tx); add_subaddress_account(tr("Primary account")); if (!wallet_.empty()) { store(); @@ -3513,6 +3520,7 @@ std::string wallet2::make_multisig(const epee::wipeable_string &password, cryptonote::block b; generate_genesis(b); m_blockchain.push_back(get_block_hash(b)); + m_last_block_reward = cryptonote::get_outs_money_amount(b.miner_tx); add_subaddress_account(tr("Primary account")); if (!m_wallet_file.empty()) @@ -4015,6 +4023,7 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass if (m_blockchain.empty()) { m_blockchain.push_back(genesis_hash); + m_last_block_reward = cryptonote::get_outs_money_amount(genesis.miner_tx); } else { @@ -4404,6 +4413,7 @@ void wallet2::rescan_blockchain(bool refresh) generate_genesis(genesis); crypto::hash genesis_hash = get_block_hash(genesis); m_blockchain.push_back(genesis_hash); + m_last_block_reward = cryptonote::get_outs_money_amount(genesis.miner_tx); add_subaddress_account(tr("Primary account")); if (refresh) @@ -10006,6 +10016,7 @@ void wallet2::import_blockchain(const std::tuple wallet2::export_outputs() const diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 2da6dd21af1..ba5df982869 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -762,6 +762,9 @@ namespace tools void rescan_blockchain(bool refresh = true); bool is_transfer_unlocked(const transfer_details& td) const; bool is_transfer_unlocked(uint64_t unlock_time, uint64_t block_height) const; + + uint64_t get_last_block_reward() const { return m_last_block_reward; } + template inline void serialize(t_archive &a, const unsigned int ver) { @@ -863,6 +866,9 @@ namespace tools if(ver < 24) return; a & m_ring_history_saved; + if(ver < 25) + return; + a & m_last_block_reward; } /*! @@ -1298,9 +1304,11 @@ namespace tools bool m_ring_history_saved; std::unique_ptr m_ringdb; boost::optional m_ringdb_key; + + uint64_t m_last_block_reward; }; } -BOOST_CLASS_VERSION(tools::wallet2, 24) +BOOST_CLASS_VERSION(tools::wallet2, 25) BOOST_CLASS_VERSION(tools::wallet2::transfer_details, 9) BOOST_CLASS_VERSION(tools::wallet2::multisig_info, 1) BOOST_CLASS_VERSION(tools::wallet2::multisig_info::LR, 0) diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index 7f7d3364247..2970968e4b9 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -74,6 +74,21 @@ namespace } return pwd_container; } + //------------------------------------------------------------------------------------------------------------------------------ + void set_confirmations(tools::wallet_rpc::transfer_entry &entry, uint64_t blockchain_height, uint64_t block_reward) + { + if (entry.height >= blockchain_height) + { + entry.confirmations = 0; + entry.suggested_confirmations_threshold = 0; + return; + } + entry.confirmations = blockchain_height - entry.height; + if (block_reward == 0) + entry.suggested_confirmations_threshold = 0; + else + entry.suggested_confirmations_threshold = (entry.amount + block_reward - 1) / block_reward; + } } namespace tools @@ -258,6 +273,7 @@ namespace tools entry.type = "in"; entry.subaddr_index = pd.m_subaddr_index; entry.address = m_wallet->get_subaddress_as_str(pd.m_subaddr_index); + set_confirmations(entry, m_wallet->get_blockchain_current_height(), m_wallet->get_last_block_reward()); } //------------------------------------------------------------------------------------------------------------------------------ void wallet_rpc_server::fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &txid, const tools::wallet2::confirmed_transfer_details &pd) @@ -284,6 +300,7 @@ namespace tools entry.type = "out"; entry.subaddr_index = { pd.m_subaddr_account, 0 }; entry.address = m_wallet->get_subaddress_as_str({pd.m_subaddr_account, 0}); + set_confirmations(entry, m_wallet->get_blockchain_current_height(), m_wallet->get_last_block_reward()); } //------------------------------------------------------------------------------------------------------------------------------ void wallet_rpc_server::fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &txid, const tools::wallet2::unconfirmed_transfer_details &pd) @@ -303,6 +320,7 @@ namespace tools entry.type = is_failed ? "failed" : "pending"; entry.subaddr_index = { pd.m_subaddr_account, 0 }; entry.address = m_wallet->get_subaddress_as_str({pd.m_subaddr_account, 0}); + set_confirmations(entry, m_wallet->get_blockchain_current_height(), m_wallet->get_last_block_reward()); } //------------------------------------------------------------------------------------------------------------------------------ void wallet_rpc_server::fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &payment_id, const tools::wallet2::pool_payment_details &ppd) @@ -322,6 +340,7 @@ namespace tools entry.type = "pool"; entry.subaddr_index = pd.m_subaddr_index; entry.address = m_wallet->get_subaddress_as_str(pd.m_subaddr_index); + set_confirmations(entry, m_wallet->get_blockchain_current_height(), m_wallet->get_last_block_reward()); } //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_getbalance(const wallet_rpc::COMMAND_RPC_GET_BALANCE::request& req, wallet_rpc::COMMAND_RPC_GET_BALANCE::response& res, epee::json_rpc::error& er) diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h index 96e135f01ab..aa61640a34f 100644 --- a/src/wallet/wallet_rpc_server_commands_defs.h +++ b/src/wallet/wallet_rpc_server_commands_defs.h @@ -47,7 +47,7 @@ // advance which version they will stop working with // Don't go over 32767 for any of these #define WALLET_RPC_VERSION_MAJOR 1 -#define WALLET_RPC_VERSION_MINOR 0 +#define WALLET_RPC_VERSION_MINOR 1 #define MAKE_WALLET_RPC_VERSION(major,minor) (((major)<<16)|(minor)) #define WALLET_RPC_VERSION MAKE_WALLET_RPC_VERSION(WALLET_RPC_VERSION_MAJOR, WALLET_RPC_VERSION_MINOR) namespace tools @@ -1194,6 +1194,8 @@ namespace wallet_rpc cryptonote::subaddress_index subaddr_index; std::string address; bool double_spend_seen; + uint64_t confirmations; + uint64_t suggested_confirmation_threshold; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(txid); @@ -1209,6 +1211,8 @@ namespace wallet_rpc KV_SERIALIZE(subaddr_index); KV_SERIALIZE(address); KV_SERIALIZE(double_spend_seen) + KV_SERIALIZE_OPT(confirmations, 0) + KV_SERIALIZE_OPT(suggested_confirmation_threshold, 0) END_KV_SERIALIZE_MAP() }; From 9b38551c3cb696c410790c278df3910eb1786fb1 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 27 Jun 2018 23:23:45 +0100 Subject: [PATCH 0143/1404] wallet_rpc_server: fix build - forgot to build rpc version --- src/wallet/wallet_rpc_server_commands_defs.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h index aa61640a34f..1bd572adda8 100644 --- a/src/wallet/wallet_rpc_server_commands_defs.h +++ b/src/wallet/wallet_rpc_server_commands_defs.h @@ -1195,7 +1195,7 @@ namespace wallet_rpc std::string address; bool double_spend_seen; uint64_t confirmations; - uint64_t suggested_confirmation_threshold; + uint64_t suggested_confirmations_threshold; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(txid); @@ -1211,8 +1211,8 @@ namespace wallet_rpc KV_SERIALIZE(subaddr_index); KV_SERIALIZE(address); KV_SERIALIZE(double_spend_seen) - KV_SERIALIZE_OPT(confirmations, 0) - KV_SERIALIZE_OPT(suggested_confirmation_threshold, 0) + KV_SERIALIZE_OPT(confirmations, (uint64_t)0) + KV_SERIALIZE_OPT(suggested_confirmations_threshold, (uint64_t)0) END_KV_SERIALIZE_MAP() }; From 84d9e7faec030b02406ddce09b4899d5887595e2 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 28 Jun 2018 00:24:39 +0100 Subject: [PATCH 0144/1404] blockchain: fix build after waiter::wait prototype change --- src/cryptonote_core/blockchain.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index fb88a13cb2d..73ce98366f5 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -2745,7 +2745,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, tools::threadpool& tpool = tools::threadpool::getInstance(); tools::threadpool::waiter waiter; - const auto waiter_guard = epee::misc_utils::create_scope_leave_handler([&]() { waiter.wait(); }); + const auto waiter_guard = epee::misc_utils::create_scope_leave_handler([&]() { waiter.wait(&tpool); }); int threads = tpool.get_max_concurrency(); for (const auto& txin : tx.vin) From 9a39b7dd7ff032620278f5636b7fd84c477d7695 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 28 Jun 2018 00:46:38 +0100 Subject: [PATCH 0145/1404] wallet2: fix build for windows (std::max again) --- src/wallet/wallet2.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 28049fe9204..e5e331797d4 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -1694,7 +1694,7 @@ void wallet2::get_short_chain_history(std::list& ids, uint64_t gra { size_t i = 0; size_t current_multiplier = 1; - size_t blockchain_size = std::max(m_blockchain.size() / granularity * granularity, m_blockchain.offset()); + size_t blockchain_size = std::max((size_t)(m_blockchain.size() / granularity * granularity), m_blockchain.offset()); size_t sz = blockchain_size - m_blockchain.offset(); if(!sz) { From 1d176473e90d0e75588ac97f32feb354f4b5da37 Mon Sep 17 00:00:00 2001 From: stoffu Date: Mon, 25 Jun 2018 09:44:07 +0900 Subject: [PATCH 0146/1404] epee.string_tools: add conversion between UTF-8 and UTF-16 --- contrib/epee/include/file_io_utils.h | 25 ++++++++------------ contrib/epee/include/string_tools.h | 35 ++++++++++++++++++++++++++++ src/common/util.cpp | 31 ++++++++++++++---------- 3 files changed, 64 insertions(+), 27 deletions(-) diff --git a/contrib/epee/include/file_io_utils.h b/contrib/epee/include/file_io_utils.h index 4434f7383cd..25f8c648b69 100644 --- a/contrib/epee/include/file_io_utils.h +++ b/contrib/epee/include/file_io_utils.h @@ -33,6 +33,7 @@ #include #ifdef WIN32 #include +#include "string_tools.h" #endif // On Windows there is a problem with non-ASCII characters in path and file names @@ -72,11 +73,9 @@ namespace file_io_utils bool save_string_to_file(const std::string& path_to_file, const std::string& str) { #ifdef WIN32 - WCHAR wide_path[1000]; - int chars = MultiByteToWideChar(CP_UTF8, 0, path_to_file.c_str(), path_to_file.size() + 1, wide_path, 1000); - if (chars == 0) - return false; - HANDLE file_handle = CreateFileW(wide_path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + std::wstring wide_path; + try { wide_path = string_tools::utf8_to_utf16(path_to_file); } catch (...) { return false; } + HANDLE file_handle = CreateFileW(wide_path.c_str(), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (file_handle == INVALID_HANDLE_VALUE) return false; DWORD bytes_written; @@ -131,11 +130,9 @@ namespace file_io_utils bool load_file_to_string(const std::string& path_to_file, std::string& target_str, size_t max_size = 1000000000) { #ifdef WIN32 - WCHAR wide_path[1000]; - int chars = MultiByteToWideChar(CP_UTF8, 0, path_to_file.c_str(), path_to_file.size() + 1, wide_path, 1000); - if (chars == 0) - return false; - HANDLE file_handle = CreateFileW(wide_path, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + std::wstring wide_path; + try { wide_path = string_tools::utf8_to_utf16(path_to_file); } catch (...) { return false; } + HANDLE file_handle = CreateFileW(wide_path.c_str(), GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (file_handle == INVALID_HANDLE_VALUE) return false; DWORD file_size = GetFileSize(file_handle, NULL); @@ -202,11 +199,9 @@ namespace file_io_utils bool get_file_size(const std::string& path_to_file, uint64_t &size) { #ifdef WIN32 - WCHAR wide_path[1000]; - int chars = MultiByteToWideChar(CP_UTF8, 0, path_to_file.c_str(), path_to_file.size() + 1, wide_path, 1000); - if (chars == 0) - return false; - HANDLE file_handle = CreateFileW(wide_path, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + std::wstring wide_path; + try { wide_path = string_tools::utf8_to_utf16(path_to_file); } catch (...) { return false; } + HANDLE file_handle = CreateFileW(wide_path.c_str(), GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (file_handle == INVALID_HANDLE_VALUE) return false; LARGE_INTEGER file_size; diff --git a/contrib/epee/include/string_tools.h b/contrib/epee/include/string_tools.h index 63705e401b4..8d8603076dd 100644 --- a/contrib/epee/include/string_tools.h +++ b/contrib/epee/include/string_tools.h @@ -381,6 +381,41 @@ POP_WARNINGS res = str.substr(0, pos); return res; } + //---------------------------------------------------------------------------- +#ifdef _WIN32 + inline std::wstring utf8_to_utf16(const std::string& str) + { + if (str.empty()) + return {}; + int wstr_size = MultiByteToWideChar(CP_UTF8, 0, &str[0], str.size(), NULL, 0); + if (wstr_size == 0) + { + throw std::runtime_error(std::error_code(GetLastError(), std::system_category()).message()); + } + std::wstring wstr(wstr_size, wchar_t{}); + if (!MultiByteToWideChar(CP_UTF8, 0, &str[0], str.size(), &wstr[0], wstr_size)) + { + throw std::runtime_error(std::error_code(GetLastError(), std::system_category()).message()); + } + return wstr; + } + inline std::string utf16_to_utf8(const std::wstring& wstr) + { + if (wstr.empty()) + return {}; + int str_size = WideCharToMultiByte(CP_UTF8, 0, &wstr[0], wstr.size(), NULL, 0, NULL, NULL); + if (str_size == 0) + { + throw std::runtime_error(std::error_code(GetLastError(), std::system_category()).message()); + } + std::string str(str_size, char{}); + if (!WideCharToMultiByte(CP_UTF8, 0, &wstr[0], wstr.size(), &str[0], str_size, NULL, NULL)) + { + throw std::runtime_error(std::error_code(GetLastError(), std::system_category()).message()); + } + return str; + } +#endif } } #endif //_STRING_TOOLS_H_ diff --git a/src/common/util.cpp b/src/common/util.cpp index 329352e9477..97c61a17425 100644 --- a/src/common/util.cpp +++ b/src/common/util.cpp @@ -451,10 +451,15 @@ std::string get_nix_version_display_string() if (SHGetSpecialFolderPathW(NULL, psz_path, nfolder, iscreate)) { - int size_needed = WideCharToMultiByte(CP_UTF8, 0, psz_path, wcslen(psz_path), NULL, 0, NULL, NULL); - std::string folder_name(size_needed, 0); - WideCharToMultiByte(CP_UTF8, 0, psz_path, wcslen(psz_path), &folder_name[0], size_needed, NULL, NULL); - return folder_name; + try + { + return string_tools::utf16_to_utf8(psz_path); + } + catch (const std::exception &e) + { + MERROR("utf16_to_utf8 failed: " << e.what()); + return ""; + } } LOG_ERROR("SHGetSpecialFolderPathW() failed, could not obtain requested path."); @@ -515,18 +520,20 @@ std::string get_nix_version_display_string() int code; #if defined(WIN32) // Maximizing chances for success - WCHAR wide_replacement_name[1000]; - MultiByteToWideChar(CP_UTF8, 0, replacement_name.c_str(), replacement_name.size() + 1, wide_replacement_name, 1000); - WCHAR wide_replaced_name[1000]; - MultiByteToWideChar(CP_UTF8, 0, replaced_name.c_str(), replaced_name.size() + 1, wide_replaced_name, 1000); - - DWORD attributes = ::GetFileAttributesW(wide_replaced_name); + std::wstring wide_replacement_name; + try { wide_replacement_name = string_tools::utf8_to_utf16(replacement_name); } + catch (...) { return std::error_code(GetLastError(), std::system_category()); } + std::wstring wide_replaced_name; + try { wide_replaced_name = string_tools::utf8_to_utf16(replaced_name); } + catch (...) { return std::error_code(GetLastError(), std::system_category()); } + + DWORD attributes = ::GetFileAttributesW(wide_replaced_name.c_str()); if (INVALID_FILE_ATTRIBUTES != attributes) { - ::SetFileAttributesW(wide_replaced_name, attributes & (~FILE_ATTRIBUTE_READONLY)); + ::SetFileAttributesW(wide_replaced_name.c_str(), attributes & (~FILE_ATTRIBUTE_READONLY)); } - bool ok = 0 != ::MoveFileExW(wide_replacement_name, wide_replaced_name, MOVEFILE_REPLACE_EXISTING); + bool ok = 0 != ::MoveFileExW(wide_replacement_name.c_str(), wide_replaced_name.c_str(), MOVEFILE_REPLACE_EXISTING); code = ok ? 0 : static_cast(::GetLastError()); #else bool ok = 0 == std::rename(replacement_name.c_str(), replaced_name.c_str()); From 59de6f8d99f07888c199b466dff9ff020e60dab6 Mon Sep 17 00:00:00 2001 From: stoffu Date: Thu, 21 Jun 2018 17:19:16 +0900 Subject: [PATCH 0147/1404] util: add file_locker class --- src/common/util.cpp | 67 +++++++++++++++++++++++++++++++++++++++++++++ src/common/util.h | 14 ++++++++++ 2 files changed, 81 insertions(+) diff --git a/src/common/util.cpp b/src/common/util.cpp index 97c61a17425..eed6fd8d20b 100644 --- a/src/common/util.cpp +++ b/src/common/util.cpp @@ -195,6 +195,73 @@ namespace tools catch (...) {} } + file_locker::file_locker(const std::string &filename) + { +#ifdef WIN32 + m_fd = INVALID_HANDLE_VALUE; + std::wstring filename_wide; + try + { + filename_wide = string_tools::utf8_to_utf16(filename); + } + catch (const std::exception &e) + { + MERROR("Failed to convert path \"" << filename << "\" to UTF-16: " << e.what()); + return; + } + m_fd = CreateFileW(filename_wide.c_str(), GENERIC_READ, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (m_fd != INVALID_HANDLE_VALUE) + { + OVERLAPPED ov; + memset(&ov, 0, sizeof(ov)); + if (!LockFileEx(m_fd, LOCKFILE_FAIL_IMMEDIATELY | LOCKFILE_EXCLUSIVE_LOCK, 0, 1, 0, &ov)) + { + MERROR("Failed to lock " << filename << ": " << std::error_code(GetLastError(), std::system_category())); + CloseHandle(m_fd); + m_fd = INVALID_HANDLE_VALUE; + } + } + else + { + MERROR("Failed to open " << filename << ": " << std::error_code(GetLastError(), std::system_category())); + } +#else + m_fd = open(filename, O_RDONLY | O_CREAT, 0666); + if (m_fd != -1) + { + if (flock(m_fd, LOCK_EX | LOCK_NB) == -1) + { + MERROR("Failed to lock " << filename << ": " << std::strerr(errno)); + close(m_fd); + m_fd = -1; + } + } + else + { + MERROR("Failed to open " << filename << ": " << std::strerr(errno)); + } +#endif + } + file_locker::~file_locker() + { + if (locked()) + { +#ifdef WIN32 + CloseHandle(m_fd); +#else + close(m_fd); +#endif + } + } + bool file_locker::locked() const + { +#ifdef WIN32 + return m_fd != INVALID_HANDLE_VALUE; +#else + return m_fd != -1; +#endif + } + #ifdef WIN32 std::string get_windows_version_display_string() { diff --git a/src/common/util.h b/src/common/util.h index dc426830b90..a57a85fee65 100644 --- a/src/common/util.h +++ b/src/common/util.h @@ -91,6 +91,20 @@ namespace tools const std::string& filename() const noexcept { return m_filename; } }; + class file_locker + { + public: + file_locker(const std::string &filename); + ~file_locker(); + bool locked() const; + private: +#ifdef WIN32 + HANDLE m_fd; +#else + int m_fd; +#endif + }; + /*! \brief Returns the default data directory. * * \details Windows < Vista: C:\\Documents and Settings\\Username\\Application Data\\CRYPTONOTE_NAME From 3d623a86d17d4f9884b766fc8e07fc1799ae0a9f Mon Sep 17 00:00:00 2001 From: stoffu Date: Tue, 12 Jun 2018 15:29:33 +0900 Subject: [PATCH 0148/1404] wallet: prevent the same wallet file from being opened by multiple processes --- src/wallet/api/wallet.cpp | 1 + src/wallet/wallet2.cpp | 8 ++++++++ src/wallet/wallet2.h | 1 + 3 files changed, 10 insertions(+) diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index c7dbd29e41c..e2c4fe7afa7 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -721,6 +721,7 @@ bool WalletImpl::close(bool store) LOG_PRINT_L1("Calling wallet::stop..."); m_wallet->stop(); LOG_PRINT_L1("wallet::stop done"); + m_wallet->deinit(); result = true; clearStatus(); } catch (const std::exception &e) { diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 28049fe9204..f07736ebda0 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -2636,6 +2636,7 @@ void wallet2::detach_blockchain(uint64_t height) bool wallet2::deinit() { m_is_initialized=false; + m_keys_file_locker.reset(); return true; } //---------------------------------------------------------------------------------------------------- @@ -2802,10 +2803,12 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable crypto::chacha20(account_data.data(), account_data.size(), key, keys_file_data.iv, &cipher[0]); keys_file_data.account_data = cipher; + m_keys_file_locker.reset(); std::string buf; r = ::serialization::dump_binary(keys_file_data, buf); r = r && epee::file_io_utils::save_string_to_file(keys_file_name, buf); //and never touch wallet_keys_file again, only read CHECK_AND_ASSERT_MES(r, false, "failed to generate wallet keys file " << keys_file_name); + m_keys_file_locker.reset(new tools::file_locker(m_keys_file)); return true; } @@ -3935,12 +3938,17 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass boost::system::error_code e; bool exists = boost::filesystem::exists(m_keys_file, e); THROW_WALLET_EXCEPTION_IF(e || !exists, error::file_not_found, m_keys_file); + m_keys_file_locker.reset(new tools::file_locker(m_keys_file)); + THROW_WALLET_EXCEPTION_IF(!m_keys_file_locker->locked(), error::wallet_internal_error, "internal error: \"" + m_keys_file + "\" is opened by another wallet program"); + // this temporary unlocking is necessary for Windows (otherwise the file couldn't be loaded). + m_keys_file_locker.reset(); if (!load_keys(m_keys_file, password)) { THROW_WALLET_EXCEPTION_IF(true, error::file_read_error, m_keys_file); } LOG_PRINT_L0("Loaded wallet keys file, with public address: " << m_account.get_public_address_str(m_nettype)); + m_keys_file_locker.reset(new tools::file_locker(m_keys_file)); //keys loaded ok! //try to load wallet file. but even if we failed, it is not big problem diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 7059339797f..d33d8258bbd 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -1308,6 +1308,7 @@ namespace tools boost::optional m_ringdb_key; uint64_t m_last_block_reward; + std::unique_ptr m_keys_file_locker; }; } BOOST_CLASS_VERSION(tools::wallet2, 25) From 4510f417f9726f0043859055408a99ad62f91383 Mon Sep 17 00:00:00 2001 From: stoffu Date: Thu, 14 Jun 2018 08:22:30 +0900 Subject: [PATCH 0149/1404] Wallet API: add some missing override keyword Also remove dust() from UnsignedTransactionImpl (already in PendingTransactionImpl) --- src/wallet/api/address_book.h | 14 +- src/wallet/api/pending_transaction.h | 26 ++-- src/wallet/api/subaddress.h | 8 +- src/wallet/api/transaction_info.h | 30 ++--- src/wallet/api/unsigned_transaction.h | 23 ++-- src/wallet/api/wallet.h | 184 +++++++++++++------------- src/wallet/api/wallet_manager.h | 44 +++--- 7 files changed, 164 insertions(+), 165 deletions(-) diff --git a/src/wallet/api/address_book.h b/src/wallet/api/address_book.h index 7d9200550e8..f4ca68efda6 100644 --- a/src/wallet/api/address_book.h +++ b/src/wallet/api/address_book.h @@ -42,16 +42,16 @@ class AddressBookImpl : public AddressBook ~AddressBookImpl(); // Fetches addresses from Wallet2 - void refresh(); - std::vector getAll() const; - bool addRow(const std::string &dst_addr , const std::string &payment_id, const std::string &description); - bool deleteRow(std::size_t rowId); + void refresh() override; + std::vector getAll() const override; + bool addRow(const std::string &dst_addr , const std::string &payment_id, const std::string &description) override; + bool deleteRow(std::size_t rowId) override; // Error codes. See AddressBook:ErrorCode enum in wallet2_api.h - std::string errorString() const {return m_errorString;} - int errorCode() const {return m_errorCode;} + std::string errorString() const override {return m_errorString;} + int errorCode() const override {return m_errorCode;} - int lookupPaymentID(const std::string &payment_id) const; + int lookupPaymentID(const std::string &payment_id) const override; private: void clearRows(); diff --git a/src/wallet/api/pending_transaction.h b/src/wallet/api/pending_transaction.h index 4f963c13407..50b9f07ef7f 100644 --- a/src/wallet/api/pending_transaction.h +++ b/src/wallet/api/pending_transaction.h @@ -43,21 +43,21 @@ class PendingTransactionImpl : public PendingTransaction public: PendingTransactionImpl(WalletImpl &wallet); ~PendingTransactionImpl(); - int status() const; - std::string errorString() const; - bool commit(const std::string &filename = "", bool overwrite = false); - uint64_t amount() const; - uint64_t dust() const; - uint64_t fee() const; - std::vector txid() const; - uint64_t txCount() const; - std::vector subaddrAccount() const; - std::vector> subaddrIndices() const; + int status() const override; + std::string errorString() const override; + bool commit(const std::string &filename = "", bool overwrite = false) override; + uint64_t amount() const override; + uint64_t dust() const override; + uint64_t fee() const override; + std::vector txid() const override; + uint64_t txCount() const override; + std::vector subaddrAccount() const override; + std::vector> subaddrIndices() const override; // TODO: continue with interface; - std::string multisigSignData(); - void signMultisigTx(); - std::vector signersKeys() const; + std::string multisigSignData() override; + void signMultisigTx() override; + std::vector signersKeys() const override; private: friend class WalletImpl; diff --git a/src/wallet/api/subaddress.h b/src/wallet/api/subaddress.h index 3f9e37ac827..f3db7d97bcf 100644 --- a/src/wallet/api/subaddress.h +++ b/src/wallet/api/subaddress.h @@ -40,10 +40,10 @@ class SubaddressImpl : public Subaddress ~SubaddressImpl(); // Fetches addresses from Wallet2 - void refresh(uint32_t accountIndex); - std::vector getAll() const; - void addRow(uint32_t accountIndex, const std::string &label); - void setLabel(uint32_t accountIndex, uint32_t addressIndex, const std::string &label); + void refresh(uint32_t accountIndex) override; + std::vector getAll() const override; + void addRow(uint32_t accountIndex, const std::string &label) override; + void setLabel(uint32_t accountIndex, uint32_t addressIndex, const std::string &label) override; private: void clearRows(); diff --git a/src/wallet/api/transaction_info.h b/src/wallet/api/transaction_info.h index 5df9a44ef17..37e0445d970 100644 --- a/src/wallet/api/transaction_info.h +++ b/src/wallet/api/transaction_info.h @@ -42,24 +42,24 @@ class TransactionInfoImpl : public TransactionInfo TransactionInfoImpl(); ~TransactionInfoImpl(); //! in/out - virtual int direction() const; + virtual int direction() const override; //! true if hold - virtual bool isPending() const; - virtual bool isFailed() const; - virtual uint64_t amount() const; + virtual bool isPending() const override; + virtual bool isFailed() const override; + virtual uint64_t amount() const override; //! always 0 for incoming txes - virtual uint64_t fee() const; - virtual uint64_t blockHeight() const; - virtual std::set subaddrIndex() const; - virtual uint32_t subaddrAccount() const; - virtual std::string label() const; + virtual uint64_t fee() const override; + virtual uint64_t blockHeight() const override; + virtual std::set subaddrIndex() const override; + virtual uint32_t subaddrAccount() const override; + virtual std::string label() const override; - virtual std::string hash() const; - virtual std::time_t timestamp() const; - virtual std::string paymentId() const; - virtual const std::vector &transfers() const; - virtual uint64_t confirmations() const; - virtual uint64_t unlockTime() const; + virtual std::string hash() const override; + virtual std::time_t timestamp() const override; + virtual std::string paymentId() const override; + virtual const std::vector &transfers() const override; + virtual uint64_t confirmations() const override; + virtual uint64_t unlockTime() const override; private: int m_direction; diff --git a/src/wallet/api/unsigned_transaction.h b/src/wallet/api/unsigned_transaction.h index a35630535c9..8a33300145b 100644 --- a/src/wallet/api/unsigned_transaction.h +++ b/src/wallet/api/unsigned_transaction.h @@ -43,19 +43,18 @@ class UnsignedTransactionImpl : public UnsignedTransaction public: UnsignedTransactionImpl(WalletImpl &wallet); ~UnsignedTransactionImpl(); - int status() const; - std::string errorString() const; - std::vector amount() const; - std::vector dust() const; - std::vector fee() const; - std::vector mixin() const; - std::vector paymentId() const; - std::vector recipientAddress() const; - uint64_t txCount() const; + int status() const override; + std::string errorString() const override; + std::vector amount() const override; + std::vector fee() const override; + std::vector mixin() const override; + std::vector paymentId() const override; + std::vector recipientAddress() const override; + uint64_t txCount() const override; // sign txs and save to file - bool sign(const std::string &signedFileName); - std::string confirmationMessage() const {return m_confirmationMessage;} - uint64_t minMixinCount() const; + bool sign(const std::string &signedFileName) override; + std::string confirmationMessage() const override {return m_confirmationMessage;} + uint64_t minMixinCount() const override; private: // Callback function to check all loaded tx's and generate confirmationMessage diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h index 08232cafdc4..eefb2fe94a9 100644 --- a/src/wallet/api/wallet.h +++ b/src/wallet/api/wallet.h @@ -57,7 +57,7 @@ class WalletImpl : public Wallet bool create(const std::string &path, const std::string &password, const std::string &language); bool createWatchOnly(const std::string &path, const std::string &password, - const std::string &language) const; + const std::string &language) const override; bool open(const std::string &path, const std::string &password); bool recover(const std::string &path,const std::string &password, const std::string &seed); @@ -80,58 +80,58 @@ class WalletImpl : public Wallet const std::string &password, const std::string &device_name); bool close(bool store = true); - std::string seed() const; - std::string getSeedLanguage() const; - void setSeedLanguage(const std::string &arg); + std::string seed() const override; + std::string getSeedLanguage() const override; + void setSeedLanguage(const std::string &arg) override; // void setListener(Listener *) {} - int status() const; - std::string errorString() const; + int status() const override; + std::string errorString() const override; void statusWithErrorString(int& status, std::string& errorString) const override; - bool setPassword(const std::string &password); - std::string address(uint32_t accountIndex = 0, uint32_t addressIndex = 0) const; - std::string integratedAddress(const std::string &payment_id) const; - std::string secretViewKey() const; - std::string publicViewKey() const; - std::string secretSpendKey() const; - std::string publicSpendKey() const; - std::string publicMultisigSignerKey() const; - std::string path() const; - bool store(const std::string &path); - std::string filename() const; - std::string keysFilename() const; - bool init(const std::string &daemon_address, uint64_t upper_transaction_size_limit = 0, const std::string &daemon_username = "", const std::string &daemon_password = "", bool use_ssl = false, bool lightWallet = false); - bool connectToDaemon(); - ConnectionStatus connected() const; - void setTrustedDaemon(bool arg); - bool trustedDaemon() const; - uint64_t balance(uint32_t accountIndex = 0) const; - uint64_t unlockedBalance(uint32_t accountIndex = 0) const; - uint64_t blockChainHeight() const; - uint64_t approximateBlockChainHeight() const; - uint64_t daemonBlockChainHeight() const; - uint64_t daemonBlockChainTargetHeight() const; - bool synchronized() const; - bool refresh(); - void refreshAsync(); - void setAutoRefreshInterval(int millis); - int autoRefreshInterval() const; - void setRefreshFromBlockHeight(uint64_t refresh_from_block_height); - uint64_t getRefreshFromBlockHeight() const { return m_wallet->get_refresh_from_block_height(); }; - void setRecoveringFromSeed(bool recoveringFromSeed); + bool setPassword(const std::string &password) override; + std::string address(uint32_t accountIndex = 0, uint32_t addressIndex = 0) const override; + std::string integratedAddress(const std::string &payment_id) const override; + std::string secretViewKey() const override; + std::string publicViewKey() const override; + std::string secretSpendKey() const override; + std::string publicSpendKey() const override; + std::string publicMultisigSignerKey() const override; + std::string path() const override; + bool store(const std::string &path) override; + std::string filename() const override; + std::string keysFilename() const override; + bool init(const std::string &daemon_address, uint64_t upper_transaction_size_limit = 0, const std::string &daemon_username = "", const std::string &daemon_password = "", bool use_ssl = false, bool lightWallet = false) override; + bool connectToDaemon() override; + ConnectionStatus connected() const override; + void setTrustedDaemon(bool arg) override; + bool trustedDaemon() const override; + uint64_t balance(uint32_t accountIndex = 0) const override; + uint64_t unlockedBalance(uint32_t accountIndex = 0) const override; + uint64_t blockChainHeight() const override; + uint64_t approximateBlockChainHeight() const override; + uint64_t daemonBlockChainHeight() const override; + uint64_t daemonBlockChainTargetHeight() const override; + bool synchronized() const override; + bool refresh() override; + void refreshAsync() override; + void setAutoRefreshInterval(int millis) override; + int autoRefreshInterval() const override; + void setRefreshFromBlockHeight(uint64_t refresh_from_block_height) override; + uint64_t getRefreshFromBlockHeight() const override { return m_wallet->get_refresh_from_block_height(); }; + void setRecoveringFromSeed(bool recoveringFromSeed) override; void setRecoveringFromDevice(bool recoveringFromDevice) override; void setSubaddressLookahead(uint32_t major, uint32_t minor) override; - bool watchOnly() const; - bool rescanSpent(); - NetworkType nettype() const {return static_cast(m_wallet->nettype());} - void hardForkInfo(uint8_t &version, uint64_t &earliest_height) const; - bool useForkRules(uint8_t version, int64_t early_blocks) const; - - void addSubaddressAccount(const std::string& label); - size_t numSubaddressAccounts() const; - size_t numSubaddresses(uint32_t accountIndex) const; - void addSubaddress(uint32_t accountIndex, const std::string& label); - std::string getSubaddressLabel(uint32_t accountIndex, uint32_t addressIndex) const; - void setSubaddressLabel(uint32_t accountIndex, uint32_t addressIndex, const std::string &label); + bool watchOnly() const override; + bool rescanSpent() override; + NetworkType nettype() const override {return static_cast(m_wallet->nettype());} + void hardForkInfo(uint8_t &version, uint64_t &earliest_height) const override; + bool useForkRules(uint8_t version, int64_t early_blocks) const override; + + void addSubaddressAccount(const std::string& label) override; + size_t numSubaddressAccounts() const override; + size_t numSubaddresses(uint32_t accountIndex) const override; + void addSubaddress(uint32_t accountIndex, const std::string& label) override; + std::string getSubaddressLabel(uint32_t accountIndex, uint32_t addressIndex) const override; + void setSubaddressLabel(uint32_t accountIndex, uint32_t addressIndex, const std::string &label) override; MultisigState multisig() const override; std::string getMultisigInfo() const override; @@ -145,49 +145,49 @@ class WalletImpl : public Wallet optional amount, uint32_t mixin_count, PendingTransaction::Priority priority = PendingTransaction::Priority_Low, uint32_t subaddr_account = 0, - std::set subaddr_indices = {}); - virtual PendingTransaction * createSweepUnmixableTransaction(); - bool submitTransaction(const std::string &fileName); - virtual UnsignedTransaction * loadUnsignedTx(const std::string &unsigned_filename); - bool exportKeyImages(const std::string &filename); - bool importKeyImages(const std::string &filename); - - virtual void disposeTransaction(PendingTransaction * t); - virtual TransactionHistory * history(); - virtual AddressBook * addressBook(); - virtual Subaddress * subaddress(); - virtual SubaddressAccount * subaddressAccount(); - virtual void setListener(WalletListener * l); - virtual uint32_t defaultMixin() const; - virtual void setDefaultMixin(uint32_t arg); - virtual bool setUserNote(const std::string &txid, const std::string ¬e); - virtual std::string getUserNote(const std::string &txid) const; - virtual std::string getTxKey(const std::string &txid) const; - virtual bool checkTxKey(const std::string &txid, std::string tx_key, const std::string &address, uint64_t &received, bool &in_pool, uint64_t &confirmations); - virtual std::string getTxProof(const std::string &txid, const std::string &address, const std::string &message) const; - virtual bool checkTxProof(const std::string &txid, const std::string &address, const std::string &message, const std::string &signature, bool &good, uint64_t &received, bool &in_pool, uint64_t &confirmations); - virtual std::string getSpendProof(const std::string &txid, const std::string &message) const; - virtual bool checkSpendProof(const std::string &txid, const std::string &message, const std::string &signature, bool &good) const; - virtual std::string getReserveProof(bool all, uint32_t account_index, uint64_t amount, const std::string &message) const; - virtual bool checkReserveProof(const std::string &address, const std::string &message, const std::string &signature, bool &good, uint64_t &total, uint64_t &spent) const; - virtual std::string signMessage(const std::string &message); - virtual bool verifySignedMessage(const std::string &message, const std::string &address, const std::string &signature) const; - virtual std::string signMultisigParticipant(const std::string &message) const; - virtual bool verifyMessageWithPublicKey(const std::string &message, const std::string &publicKey, const std::string &signature) const; - virtual void startRefresh(); - virtual void pauseRefresh(); - virtual bool parse_uri(const std::string &uri, std::string &address, std::string &payment_id, uint64_t &amount, std::string &tx_description, std::string &recipient_name, std::vector &unknown_parameters, std::string &error); - virtual std::string getDefaultDataDir() const; - virtual bool lightWalletLogin(bool &isNewWallet) const; - virtual bool lightWalletImportWalletRequest(std::string &payment_id, uint64_t &fee, bool &new_request, bool &request_fulfilled, std::string &payment_address, std::string &status); - virtual bool blackballOutputs(const std::vector &pubkeys, bool add); - virtual bool unblackballOutput(const std::string &pubkey); - virtual bool getRing(const std::string &key_image, std::vector &ring) const; - virtual bool getRings(const std::string &txid, std::vector>> &rings) const; - virtual bool setRing(const std::string &key_image, const std::vector &ring, bool relative); - virtual void segregatePreForkOutputs(bool segregate); - virtual void segregationHeight(uint64_t height); - virtual void keyReuseMitigation2(bool mitigation); + std::set subaddr_indices = {}) override; + virtual PendingTransaction * createSweepUnmixableTransaction() override; + bool submitTransaction(const std::string &fileName) override; + virtual UnsignedTransaction * loadUnsignedTx(const std::string &unsigned_filename) override; + bool exportKeyImages(const std::string &filename) override; + bool importKeyImages(const std::string &filename) override; + + virtual void disposeTransaction(PendingTransaction * t) override; + virtual TransactionHistory * history() override; + virtual AddressBook * addressBook() override; + virtual Subaddress * subaddress() override; + virtual SubaddressAccount * subaddressAccount() override; + virtual void setListener(WalletListener * l) override; + virtual uint32_t defaultMixin() const override; + virtual void setDefaultMixin(uint32_t arg) override; + virtual bool setUserNote(const std::string &txid, const std::string ¬e) override; + virtual std::string getUserNote(const std::string &txid) const override; + virtual std::string getTxKey(const std::string &txid) const override; + virtual bool checkTxKey(const std::string &txid, std::string tx_key, const std::string &address, uint64_t &received, bool &in_pool, uint64_t &confirmations) override; + virtual std::string getTxProof(const std::string &txid, const std::string &address, const std::string &message) const override; + virtual bool checkTxProof(const std::string &txid, const std::string &address, const std::string &message, const std::string &signature, bool &good, uint64_t &received, bool &in_pool, uint64_t &confirmations) override; + virtual std::string getSpendProof(const std::string &txid, const std::string &message) const override; + virtual bool checkSpendProof(const std::string &txid, const std::string &message, const std::string &signature, bool &good) const override; + virtual std::string getReserveProof(bool all, uint32_t account_index, uint64_t amount, const std::string &message) const override; + virtual bool checkReserveProof(const std::string &address, const std::string &message, const std::string &signature, bool &good, uint64_t &total, uint64_t &spent) const override; + virtual std::string signMessage(const std::string &message) override; + virtual bool verifySignedMessage(const std::string &message, const std::string &address, const std::string &signature) const override; + virtual std::string signMultisigParticipant(const std::string &message) const override; + virtual bool verifyMessageWithPublicKey(const std::string &message, const std::string &publicKey, const std::string &signature) const override; + virtual void startRefresh() override; + virtual void pauseRefresh() override; + virtual bool parse_uri(const std::string &uri, std::string &address, std::string &payment_id, uint64_t &amount, std::string &tx_description, std::string &recipient_name, std::vector &unknown_parameters, std::string &error) override; + virtual std::string getDefaultDataDir() const override; + virtual bool lightWalletLogin(bool &isNewWallet) const override; + virtual bool lightWalletImportWalletRequest(std::string &payment_id, uint64_t &fee, bool &new_request, bool &request_fulfilled, std::string &payment_address, std::string &status) override; + virtual bool blackballOutputs(const std::vector &pubkeys, bool add) override; + virtual bool unblackballOutput(const std::string &pubkey) override; + virtual bool getRing(const std::string &key_image, std::vector &ring) const override; + virtual bool getRings(const std::string &txid, std::vector>> &rings) const override; + virtual bool setRing(const std::string &key_image, const std::vector &ring, bool relative) override; + virtual void segregatePreForkOutputs(bool segregate) override; + virtual void segregationHeight(uint64_t height) override; + virtual void keyReuseMitigation2(bool mitigation) override; private: void clearStatus() const; diff --git a/src/wallet/api/wallet_manager.h b/src/wallet/api/wallet_manager.h index 19aad9ee38e..656a7142cec 100644 --- a/src/wallet/api/wallet_manager.h +++ b/src/wallet/api/wallet_manager.h @@ -39,13 +39,13 @@ class WalletManagerImpl : public WalletManager { public: Wallet * createWallet(const std::string &path, const std::string &password, - const std::string &language, NetworkType nettype); - Wallet * openWallet(const std::string &path, const std::string &password, NetworkType nettype); + const std::string &language, NetworkType nettype) override; + Wallet * openWallet(const std::string &path, const std::string &password, NetworkType nettype) override; virtual Wallet * recoveryWallet(const std::string &path, const std::string &password, const std::string &mnemonic, NetworkType nettype, - uint64_t restoreHeight); + uint64_t restoreHeight) override; virtual Wallet * createWalletFromKeys(const std::string &path, const std::string &password, const std::string &language, @@ -53,9 +53,9 @@ class WalletManagerImpl : public WalletManager uint64_t restoreHeight, const std::string &addressString, const std::string &viewKeyString, - const std::string &spendKeyString = ""); + const std::string &spendKeyString = "") override; // next two methods are deprecated - use the above version which allow setting of a password - virtual Wallet * recoveryWallet(const std::string &path, const std::string &mnemonic, NetworkType nettype, uint64_t restoreHeight); + virtual Wallet * recoveryWallet(const std::string &path, const std::string &mnemonic, NetworkType nettype, uint64_t restoreHeight) override; // deprecated: use createWalletFromKeys(..., password, ...) instead virtual Wallet * createWalletFromKeys(const std::string &path, const std::string &language, @@ -63,29 +63,29 @@ class WalletManagerImpl : public WalletManager uint64_t restoreHeight, const std::string &addressString, const std::string &viewKeyString, - const std::string &spendKeyString = ""); + const std::string &spendKeyString = "") override; virtual Wallet * createWalletFromDevice(const std::string &path, const std::string &password, NetworkType nettype, const std::string &deviceName, uint64_t restoreHeight = 0, const std::string &subaddressLookahead = "") override; - virtual bool closeWallet(Wallet *wallet, bool store = true); - bool walletExists(const std::string &path); - bool verifyWalletPassword(const std::string &keys_file_name, const std::string &password, bool no_spend_key) const; - std::vector findWallets(const std::string &path); - std::string errorString() const; - void setDaemonAddress(const std::string &address); - bool connected(uint32_t *version = NULL); - uint64_t blockchainHeight(); - uint64_t blockchainTargetHeight(); - uint64_t networkDifficulty(); - double miningHashRate(); - uint64_t blockTarget(); - bool isMining(); - bool startMining(const std::string &address, uint32_t threads = 1, bool background_mining = false, bool ignore_battery = true); - bool stopMining(); - std::string resolveOpenAlias(const std::string &address, bool &dnssec_valid) const; + virtual bool closeWallet(Wallet *wallet, bool store = true) override; + bool walletExists(const std::string &path) override; + bool verifyWalletPassword(const std::string &keys_file_name, const std::string &password, bool no_spend_key) const override; + std::vector findWallets(const std::string &path) override; + std::string errorString() const override; + void setDaemonAddress(const std::string &address) override; + bool connected(uint32_t *version = NULL) override; + uint64_t blockchainHeight() override; + uint64_t blockchainTargetHeight() override; + uint64_t networkDifficulty() override; + double miningHashRate() override; + uint64_t blockTarget() override; + bool isMining() override; + bool startMining(const std::string &address, uint32_t threads = 1, bool background_mining = false, bool ignore_battery = true) override; + bool stopMining() override; + std::string resolveOpenAlias(const std::string &address, bool &dnssec_valid) const override; private: WalletManagerImpl() {} From 29254fc06eca6a1696aca2bd02bc5cb69803f01f Mon Sep 17 00:00:00 2001 From: stoffu Date: Thu, 28 Jun 2018 10:20:01 +0900 Subject: [PATCH 0150/1404] api/wallet: add missing arg to wallet2::refresh() --- src/wallet/api/wallet.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index e2c4fe7afa7..3f6bfec9e88 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -1966,7 +1966,7 @@ void WalletImpl::doRefresh() // Syncing daemon and refreshing wallet simultaneously is very resource intensive. // Disable refresh if wallet is disconnected or daemon isn't synced. if (m_wallet->light_wallet() || daemonSynced()) { - m_wallet->refresh(); + m_wallet->refresh(trustedDaemon()); if (!m_synchronized) { m_synchronized = true; } From 49dc78d99b35c80784e772542485431ba3ceb655 Mon Sep 17 00:00:00 2001 From: stoffu Date: Thu, 28 Jun 2018 10:39:04 +0900 Subject: [PATCH 0151/1404] util: fix mistakes made in #3994 --- src/common/util.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/common/util.cpp b/src/common/util.cpp index eed6fd8d20b..7d9d7b408ea 100644 --- a/src/common/util.cpp +++ b/src/common/util.cpp @@ -226,19 +226,19 @@ namespace tools MERROR("Failed to open " << filename << ": " << std::error_code(GetLastError(), std::system_category())); } #else - m_fd = open(filename, O_RDONLY | O_CREAT, 0666); + m_fd = open(filename.c_str(), O_RDONLY | O_CREAT, 0666); if (m_fd != -1) { if (flock(m_fd, LOCK_EX | LOCK_NB) == -1) { - MERROR("Failed to lock " << filename << ": " << std::strerr(errno)); + MERROR("Failed to lock " << filename << ": " << std::strerror(errno)); close(m_fd); m_fd = -1; } } else { - MERROR("Failed to open " << filename << ": " << std::strerr(errno)); + MERROR("Failed to open " << filename << ": " << std::strerror(errno)); } #endif } From 3474154b75cb6cf22af10454a0fe5e18dc57acb3 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 28 Jun 2018 16:33:22 +0100 Subject: [PATCH 0152/1404] gtest: use -fPIC with CLANG too --- tests/gtest/cmake/internal_utils.cmake | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/gtest/cmake/internal_utils.cmake b/tests/gtest/cmake/internal_utils.cmake index 567edaa300a..ae45d7d7af5 100644 --- a/tests/gtest/cmake/internal_utils.cmake +++ b/tests/gtest/cmake/internal_utils.cmake @@ -96,6 +96,10 @@ macro(config_compiler_and_linker) set(cxx_no_rtti_flags "-fno-rtti -DGTEST_HAS_RTTI=0") set(cxx_strict_flags "-Wextra -Wno-unused-parameter -Wno-missing-field-initializers") + elseif (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + set(cxx_base_flags "-Wall -Wshadow -fPIC") + set(cxx_exception_flags "-fexceptions") + set(cxx_no_exception_flags "-fno-exceptions") elseif (CMAKE_CXX_COMPILER_ID STREQUAL "SunPro") set(cxx_exception_flags "-features=except") # Sun Pro doesn't provide macros to indicate whether exceptions and From ef649f998dd45b2e42985188cb9a2cbdcf9fa3c1 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 28 Jun 2018 16:35:01 +0100 Subject: [PATCH 0153/1404] crypto: remove unused unsafe random scalar generator --- src/crypto/crypto.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/crypto/crypto.cpp b/src/crypto/crypto.cpp index f4ef751d388..0fd6d936393 100644 --- a/src/crypto/crypto.cpp +++ b/src/crypto/crypto.cpp @@ -94,12 +94,6 @@ namespace crypto { } /* generate a random 32-byte (256-bit) integer and copy it to res */ - static inline void random_scalar_not_thread_safe(ec_scalar &res) { - unsigned char tmp[64]; - generate_random_bytes_not_thread_safe(64, tmp); - sc_reduce(tmp); - memcpy(&res, tmp, 32); - } static inline void random_scalar(ec_scalar &res) { unsigned char tmp[64]; generate_random_bytes_thread_safe(64, tmp); From 438d52deaf2657fb7c196d505f9133dabdb0a258 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 28 Jun 2018 16:37:10 +0100 Subject: [PATCH 0154/1404] remove epee from link lines where it's redundant For some reason, this confuses and kills ASAN on startup as it thinks const uint8_t ipv4_network_address::ID is defined multiple times. --- src/common/CMakeLists.txt | 1 - src/daemon/CMakeLists.txt | 1 - src/p2p/CMakeLists.txt | 1 - src/rpc/CMakeLists.txt | 1 - src/simplewallet/CMakeLists.txt | 1 - src/wallet/CMakeLists.txt | 1 - tests/unit_tests/CMakeLists.txt | 1 - 7 files changed, 7 deletions(-) diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 808ef76306c..f0df05b0d27 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -78,7 +78,6 @@ monero_add_library(common DEPENDS generate_translations_header) target_link_libraries(common PUBLIC - epee cncrypto ${UNBOUND_LIBRARY} ${LIBUNWIND_LIBRARIES} diff --git a/src/daemon/CMakeLists.txt b/src/daemon/CMakeLists.txt index 3b1d0d8262b..84004c3c619 100644 --- a/src/daemon/CMakeLists.txt +++ b/src/daemon/CMakeLists.txt @@ -92,7 +92,6 @@ target_link_libraries(daemon daemonizer serialization daemon_rpc_server - epee ${EPEE_READLINE} version ${Boost_CHRONO_LIBRARY} diff --git a/src/p2p/CMakeLists.txt b/src/p2p/CMakeLists.txt index 83bdffab5f7..9b924907e0c 100644 --- a/src/p2p/CMakeLists.txt +++ b/src/p2p/CMakeLists.txt @@ -38,7 +38,6 @@ source_group(p2p FILES ${P2P}) monero_add_library(p2p ${P2P}) target_link_libraries(p2p PUBLIC - epee version cryptonote_core ${UPNP_LIBRARIES} diff --git a/src/rpc/CMakeLists.txt b/src/rpc/CMakeLists.txt index 7162317ed6d..4f8f96524fd 100644 --- a/src/rpc/CMakeLists.txt +++ b/src/rpc/CMakeLists.txt @@ -111,7 +111,6 @@ target_link_libraries(rpc common cryptonote_core cryptonote_protocol - epee ${Boost_REGEX_LIBRARY} ${Boost_THREAD_LIBRARY} PRIVATE diff --git a/src/simplewallet/CMakeLists.txt b/src/simplewallet/CMakeLists.txt index 8bb2959314f..c31cdebde10 100644 --- a/src/simplewallet/CMakeLists.txt +++ b/src/simplewallet/CMakeLists.txt @@ -48,7 +48,6 @@ target_link_libraries(simplewallet cncrypto common mnemonics - epee ${EPEE_READLINE} version ${Boost_CHRONO_LIBRARY} diff --git a/src/wallet/CMakeLists.txt b/src/wallet/CMakeLists.txt index a5a4c7f5622..a16f4fe1900 100644 --- a/src/wallet/CMakeLists.txt +++ b/src/wallet/CMakeLists.txt @@ -85,7 +85,6 @@ monero_add_executable(wallet_rpc_server target_link_libraries(wallet_rpc_server PRIVATE wallet - epee rpc_base cryptonote_core cncrypto diff --git a/tests/unit_tests/CMakeLists.txt b/tests/unit_tests/CMakeLists.txt index 6d79ba74b37..ee496d1cbda 100644 --- a/tests/unit_tests/CMakeLists.txt +++ b/tests/unit_tests/CMakeLists.txt @@ -88,7 +88,6 @@ target_link_libraries(unit_tests wallet p2p version - epee ${Boost_CHRONO_LIBRARY} ${Boost_THREAD_LIBRARY} ${GTEST_LIBRARIES} From aa0ea0aafcee110e57686ce5ccff9b38b3370d10 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 27 Jun 2018 18:34:15 +0100 Subject: [PATCH 0155/1404] blockchain: set the m_verifivation_failed flag in a couple more places when a block being added to the main chain is invalid. This ensures the peer is banned after a number of these. --- src/cryptonote_core/blockchain.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 73ce98366f5..0c94d5dd81d 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -3323,6 +3323,7 @@ bool Blockchain::handle_block_to_main_chain(const block& bl, const crypto::hash& if(bl.prev_id != get_tail_id()) { MERROR_VER("Block with id: " << id << std::endl << "has wrong prev_id: " << bl.prev_id << std::endl << "expected: " << get_tail_id()); + bvc.m_verifivation_failed = true; leave: m_db->block_txn_stop(); return false; @@ -3624,6 +3625,7 @@ bool Blockchain::handle_block_to_main_chain(const block& bl, const crypto::hash& { //TODO: figure out the best way to deal with this failure LOG_ERROR("Error adding block with hash: " << id << " to blockchain, what = " << e.what()); + bvc.m_verifivation_failed = true; return_tx_to_pool(txs); return false; } From 34cb6b4b703f30be89388f4cdcc5d7b6780ee988 Mon Sep 17 00:00:00 2001 From: victorsintnicolaas Date: Thu, 14 Jun 2018 21:11:49 +0200 Subject: [PATCH 0156/1404] add --regtest and --fixed-difficulty for regression testing on_generateblocks RPC call combines functionality from the on_getblocktemplate and on_submitblock RPC calls to allow rapid block creation. Difficulty is set permanently to 1 for regtest. Makes use of FAKECHAIN network type, but takes hard fork heights from mainchain Default reserve_size in generate_blocks RPC call is now 1. If it is 0, the following error occurs 'Failed to calculate offset for'. Queries hard fork heights info of other network types --- src/blockchain_db/berkeleydb/db_bdb.cpp | 5 ++ src/blockchain_db/berkeleydb/db_bdb.h | 2 + src/blockchain_db/blockchain_db.h | 14 ++++++ src/blockchain_db/lmdb/db_lmdb.cpp | 15 ++++++ src/blockchain_db/lmdb/db_lmdb.h | 2 + src/cryptonote_basic/hardfork.h | 15 +++--- src/cryptonote_core/blockchain.cpp | 46 +++++++++++++++++- src/cryptonote_core/blockchain.h | 11 ++++- src/cryptonote_core/cryptonote_core.cpp | 32 ++++++++++++- src/cryptonote_core/cryptonote_core.h | 2 + src/daemon/daemon.cpp | 3 +- src/daemon/main.cpp | 5 +- src/p2p/net_node.inl | 3 ++ src/rpc/core_rpc_server.cpp | 62 +++++++++++++++++++++++++ src/rpc/core_rpc_server.h | 2 + src/rpc/core_rpc_server_commands_defs.h | 25 ++++++++++ src/rpc/core_rpc_server_error_codes.h | 1 + tests/unit_tests/hardfork.cpp | 1 + 18 files changed, 232 insertions(+), 14 deletions(-) diff --git a/src/blockchain_db/berkeleydb/db_bdb.cpp b/src/blockchain_db/berkeleydb/db_bdb.cpp index e1b76ec1ef7..f827ab7c32a 100644 --- a/src/blockchain_db/berkeleydb/db_bdb.cpp +++ b/src/blockchain_db/berkeleydb/db_bdb.cpp @@ -1213,6 +1213,11 @@ std::vector BlockchainBDB::get_filenames() const return full_paths; } +bool BlockchainBDB::remove_data_file(const std::string& folder) +{ + return true; +} + std::string BlockchainBDB::get_db_name() const { LOG_PRINT_L3("BlockchainBDB::" << __func__); diff --git a/src/blockchain_db/berkeleydb/db_bdb.h b/src/blockchain_db/berkeleydb/db_bdb.h index cecbba28f80..c90d030a26e 100644 --- a/src/blockchain_db/berkeleydb/db_bdb.h +++ b/src/blockchain_db/berkeleydb/db_bdb.h @@ -244,6 +244,8 @@ class BlockchainBDB : public BlockchainDB virtual std::vector get_filenames() const; + virtual bool remove_data_file(const std::string& folder); + virtual std::string get_db_name() const; virtual bool lock(); diff --git a/src/blockchain_db/blockchain_db.h b/src/blockchain_db/blockchain_db.h index 19ba3234085..02077ac84c1 100644 --- a/src/blockchain_db/blockchain_db.h +++ b/src/blockchain_db/blockchain_db.h @@ -654,6 +654,20 @@ class BlockchainDB */ virtual std::vector get_filenames() const = 0; + /** + * @brief remove file(s) storing the database + * + * This function is for resetting the database (for core tests, functional tests, etc). + * The function reset() is not usable because it needs to open the database file first + * which can fail if the existing database file is in an incompatible format. + * As such, this function needs to be called before calling open(). + * + * @param folder The path of the folder containing the database file(s) which must not end with slash '/'. + * + * @return true if the operation is succesfull + */ + virtual bool remove_data_file(const std::string& folder) const = 0; + // return the name of the folder the db's file(s) should reside in /** * @brief gets the name of the folder the BlockchainDB's file(s) should be in diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp index 300fb6d2f6c..05af0f5aa3a 100644 --- a/src/blockchain_db/lmdb/db_lmdb.cpp +++ b/src/blockchain_db/lmdb/db_lmdb.cpp @@ -1465,6 +1465,21 @@ std::vector BlockchainLMDB::get_filenames() const return filenames; } +bool BlockchainLMDB::remove_data_file(const std::string& folder) const +{ + const std::string filename = folder + "/data.mdb"; + try + { + boost::filesystem::remove(filename); + } + catch (const std::exception &e) + { + MERROR("Failed to remove " << filename << ": " << e.what()); + return false; + } + return true; +} + std::string BlockchainLMDB::get_db_name() const { LOG_PRINT_L3("BlockchainLMDB::" << __func__); diff --git a/src/blockchain_db/lmdb/db_lmdb.h b/src/blockchain_db/lmdb/db_lmdb.h index 4580573ebca..54aa864a6f8 100644 --- a/src/blockchain_db/lmdb/db_lmdb.h +++ b/src/blockchain_db/lmdb/db_lmdb.h @@ -181,6 +181,8 @@ class BlockchainLMDB : public BlockchainDB virtual std::vector get_filenames() const; + virtual bool remove_data_file(const std::string& folder) const; + virtual std::string get_db_name() const; virtual bool lock(); diff --git a/src/cryptonote_basic/hardfork.h b/src/cryptonote_basic/hardfork.h index ee5ec0596bc..a63a669761e 100644 --- a/src/cryptonote_basic/hardfork.h +++ b/src/cryptonote_basic/hardfork.h @@ -220,6 +220,14 @@ namespace cryptonote */ uint64_t get_window_size() const { return window_size; } + struct Params { + uint8_t version; + uint8_t threshold; + uint64_t height; + time_t time; + Params(uint8_t version, uint64_t height, uint8_t threshold, time_t time): version(version), threshold(threshold), height(height), time(time) {} + }; + private: uint8_t get_block_version(uint64_t height) const; @@ -244,13 +252,6 @@ namespace cryptonote uint8_t original_version; uint64_t original_version_till_height; - struct Params { - uint8_t version; - uint8_t threshold; - uint64_t height; - time_t time; - Params(uint8_t version, uint64_t height, uint8_t threshold, time_t time): version(version), threshold(threshold), height(height), time(time) {} - }; std::vector heights; std::deque versions; /* rolling window of the last N blocks' versions */ diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 54d8fac3172..1911309530a 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -329,7 +329,7 @@ uint64_t Blockchain::get_current_blockchain_height() const //------------------------------------------------------------------ //FIXME: possibly move this into the constructor, to avoid accidentally // dereferencing a null BlockchainDB pointer -bool Blockchain::init(BlockchainDB* db, const network_type nettype, bool offline, const cryptonote::test_options *test_options) +bool Blockchain::init(BlockchainDB* db, const network_type nettype, bool offline, const cryptonote::test_options *test_options, difficulty_type fixed_difficulty) { LOG_PRINT_L3("Blockchain::" << __func__); CRITICAL_REGION_LOCAL(m_tx_pool); @@ -351,6 +351,7 @@ bool Blockchain::init(BlockchainDB* db, const network_type nettype, bool offline m_nettype = test_options != NULL ? FAKECHAIN : nettype; m_offline = offline; + m_fixed_difficulty = fixed_difficulty; if (m_hardfork == nullptr) { if (m_nettype == FAKECHAIN || m_nettype == STAGENET) @@ -805,6 +806,11 @@ bool Blockchain::get_block_by_hash(const crypto::hash &h, block &blk, bool *orph // less blocks than desired if there aren't enough. difficulty_type Blockchain::get_difficulty_for_next_block() { + if (m_fixed_difficulty) + { + return m_db->height() ? m_fixed_difficulty : 1; + } + LOG_PRINT_L3("Blockchain::" << __func__); CRITICAL_REGION_LOCAL(m_difficulty_lock); @@ -1007,6 +1013,11 @@ bool Blockchain::switch_to_alternative_blockchain(std::list& alt_chain, block_extended_info& bei) const { + if (m_fixed_difficulty) + { + return m_db->height() ? m_fixed_difficulty : 1; + } + LOG_PRINT_L3("Blockchain::" << __func__); std::vector timestamps; std::vector cumulative_difficulties; @@ -4374,6 +4385,39 @@ HardFork::State Blockchain::get_hard_fork_state() const return m_hardfork->get_state(); } +const std::vector& Blockchain::get_hard_fork_heights(network_type nettype) +{ + static const std::vector mainnet_heights = []() + { + std::vector heights; + for (const auto& i : mainnet_hard_forks) + heights.emplace_back(i.version, i.height, i.threshold, i.time); + return heights; + }(); + static const std::vector testnet_heights = []() + { + std::vector heights; + for (const auto& i : testnet_hard_forks) + heights.emplace_back(i.version, i.height, i.threshold, i.time); + return heights; + }(); + static const std::vector stagenet_heights = []() + { + std::vector heights; + for (const auto& i : stagenet_hard_forks) + heights.emplace_back(i.version, i.height, i.threshold, i.time); + return heights; + }(); + static const std::vector dummy; + switch (nettype) + { + case MAINNET: return mainnet_heights; + case TESTNET: return testnet_heights; + case STAGENET: return stagenet_heights; + default: return dummy; + } +} + bool Blockchain::get_hard_fork_voting_info(uint8_t version, uint32_t &window, uint32_t &votes, uint32_t &threshold, uint64_t &earliest_height, uint8_t &voting) const { return m_hardfork->get_voting_info(version, window, votes, threshold, earliest_height, voting); diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index 769e608caa4..a3cdecbb869 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -114,10 +114,11 @@ namespace cryptonote * @param nettype network type * @param offline true if running offline, else false * @param test_options test parameters + * @param fixed_difficulty fixed difficulty for testing purposes; 0 means disabled * * @return true on success, false if any initialization steps fail */ - bool init(BlockchainDB* db, const network_type nettype = MAINNET, bool offline = false, const cryptonote::test_options *test_options = NULL); + bool init(BlockchainDB* db, const network_type nettype = MAINNET, bool offline = false, const cryptonote::test_options *test_options = NULL, difficulty_type fixed_difficulty = 0); /** * @brief Initialize the Blockchain state @@ -754,6 +755,13 @@ namespace cryptonote */ HardFork::State get_hard_fork_state() const; + /** + * @brief gets the hardfork heights of given network + * + * @return the HardFork object + */ + static const std::vector& get_hard_fork_heights(network_type nettype); + /** * @brief gets the current hardfork version in use/voted for * @@ -1033,6 +1041,7 @@ namespace cryptonote network_type m_nettype; bool m_offline; + difficulty_type m_fixed_difficulty; std::atomic m_cancel; diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index d2796deeb0f..54c88acbd14 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -76,6 +76,16 @@ namespace cryptonote , "Run on stagenet. The wallet must be launched with --stagenet flag." , false }; + const command_line::arg_descriptor arg_regtest_on = { + "regtest" + , "Run in a regression testing mode." + , false + }; + const command_line::arg_descriptor arg_fixed_difficulty = { + "fixed-difficulty" + , "Fixed difficulty used for testing." + , 0 + }; const command_line::arg_descriptor arg_data_dir = { "data-dir" , "Specify data directory" @@ -252,6 +262,8 @@ namespace cryptonote command_line::add_arg(desc, arg_testnet_on); command_line::add_arg(desc, arg_stagenet_on); + command_line::add_arg(desc, arg_regtest_on); + command_line::add_arg(desc, arg_fixed_difficulty); command_line::add_arg(desc, arg_dns_checkpoints); command_line::add_arg(desc, arg_prep_blocks_threads); command_line::add_arg(desc, arg_fast_block_sync); @@ -374,7 +386,8 @@ namespace cryptonote { start_time = std::time(nullptr); - if (test_options != NULL) + const bool regtest = command_line::get_arg(vm, arg_regtest_on); + if (test_options != NULL || regtest) { m_nettype = FAKECHAIN; } @@ -431,6 +444,16 @@ namespace cryptonote blockchain_db_sync_mode sync_mode = db_defaultsync; uint64_t blocks_per_sync = 1; + if (m_nettype == FAKECHAIN) + { + // reset the db by removing the database file before opening it + if (!db->remove_data_file(filename)) + { + MERROR("Failed to remove data file in " << filename); + return false; + } + } + try { uint64_t db_flags = 0; @@ -508,7 +531,12 @@ namespace cryptonote m_blockchain_storage.set_user_options(blocks_threads, blocks_per_sync, sync_mode, fast_sync); - r = m_blockchain_storage.init(db.release(), m_nettype, m_offline, test_options); + const std::pair regtest_hard_forks[3] = {std::make_pair(1, 0), std::make_pair(Blockchain::get_hard_fork_heights(MAINNET).back().version, 1), std::make_pair(0, 0)}; + const cryptonote::test_options regtest_test_options = { + regtest_hard_forks + }; + const difficulty_type fixed_difficulty = command_line::get_arg(vm, arg_fixed_difficulty); + r = m_blockchain_storage.init(db.release(), m_nettype, m_offline, regtest ? ®test_test_options : test_options, fixed_difficulty); r = m_mempool.init(max_txpool_size); CHECK_AND_ASSERT_MES(r, false, "Failed to initialize memory pool"); diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index 17b5680e5e5..2d8ae79309c 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -61,6 +61,8 @@ namespace cryptonote extern const command_line::arg_descriptor arg_data_dir; extern const command_line::arg_descriptor arg_testnet_on; extern const command_line::arg_descriptor arg_stagenet_on; + extern const command_line::arg_descriptor arg_regtest_on; + extern const command_line::arg_descriptor arg_fixed_difficulty; extern const command_line::arg_descriptor arg_offline; /************************************************************************/ diff --git a/src/daemon/daemon.cpp b/src/daemon/daemon.cpp index 48671f190c5..ea24e32eb7d 100644 --- a/src/daemon/daemon.cpp +++ b/src/daemon/daemon.cpp @@ -77,9 +77,10 @@ struct t_internals { const auto testnet = command_line::get_arg(vm, cryptonote::arg_testnet_on); const auto stagenet = command_line::get_arg(vm, cryptonote::arg_stagenet_on); + const auto regtest = command_line::get_arg(vm, cryptonote::arg_regtest_on); const auto restricted = command_line::get_arg(vm, cryptonote::core_rpc_server::arg_restricted_rpc); const auto main_rpc_port = command_line::get_arg(vm, cryptonote::core_rpc_server::arg_rpc_bind_port); - rpcs.emplace_back(new t_rpc{vm, core, p2p, restricted, testnet ? cryptonote::TESTNET : stagenet ? cryptonote::STAGENET : cryptonote::MAINNET, main_rpc_port, "core"}); + rpcs.emplace_back(new t_rpc{vm, core, p2p, restricted, testnet ? cryptonote::TESTNET : stagenet ? cryptonote::STAGENET : regtest ? cryptonote::FAKECHAIN : cryptonote::MAINNET, main_rpc_port, "core"}); auto restricted_rpc_port_arg = cryptonote::core_rpc_server::arg_rpc_restricted_bind_port; if(!command_line::is_arg_defaulted(vm, restricted_rpc_port_arg)) diff --git a/src/daemon/main.cpp b/src/daemon/main.cpp index 49494e8899c..711de073602 100644 --- a/src/daemon/main.cpp +++ b/src/daemon/main.cpp @@ -162,9 +162,10 @@ int main(int argc, char const * argv[]) const bool testnet = command_line::get_arg(vm, cryptonote::arg_testnet_on); const bool stagenet = command_line::get_arg(vm, cryptonote::arg_stagenet_on); - if (testnet && stagenet) + const bool regtest = command_line::get_arg(vm, cryptonote::arg_regtest_on); + if (testnet + stagenet + regtest > 1) { - std::cerr << "Can't specify more than one of --tesnet and --stagenet" << ENDL; + std::cerr << "Can't specify more than one of --tesnet and --stagenet and --regtest" << ENDL; return 1; } diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index 9b21705ec15..de0aa169b52 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -382,6 +382,9 @@ namespace nodetool full_addrs.insert("162.210.173.150:38080"); full_addrs.insert("162.210.173.151:38080"); } + else if (nettype == cryptonote::FAKECHAIN) + { + } else { full_addrs.insert("107.152.130.98:18080"); diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index aa105567c81..c0b1d8891f6 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -1209,6 +1209,68 @@ namespace cryptonote return true; } //------------------------------------------------------------------------------------------------------------------------------ + bool core_rpc_server::on_generateblocks(const COMMAND_RPC_GENERATEBLOCKS::request& req, COMMAND_RPC_GENERATEBLOCKS::response& res, epee::json_rpc::error& error_resp) + { + PERF_TIMER(on_generateblocks); + + CHECK_CORE_READY(); + + res.status = CORE_RPC_STATUS_OK; + + if(m_core.get_nettype() != FAKECHAIN) + { + error_resp.code = CORE_RPC_ERROR_CODE_REGTEST_REQUIRED; + error_resp.message = "Regtest required when generating blocks"; + return false; + } + + COMMAND_RPC_GETBLOCKTEMPLATE::request template_req; + COMMAND_RPC_GETBLOCKTEMPLATE::response template_res; + COMMAND_RPC_SUBMITBLOCK::request submit_req; + COMMAND_RPC_SUBMITBLOCK::response submit_res; + + template_req.reserve_size = 1; + template_req.wallet_address = req.wallet_address; + submit_req.push_back(boost::value_initialized()); + res.height = m_core.get_blockchain_storage().get_current_blockchain_height(); + + bool r; + + for(size_t i = 0; i < req.amount_of_blocks; i++) + { + r = on_getblocktemplate(template_req, template_res, error_resp); + res.status = template_res.status; + + if (!r) return false; + + blobdata blockblob; + if(!string_tools::parse_hexstr_to_binbuff(template_res.blocktemplate_blob, blockblob)) + { + error_resp.code = CORE_RPC_ERROR_CODE_WRONG_BLOCKBLOB; + error_resp.message = "Wrong block blob"; + return false; + } + block b = AUTO_VAL_INIT(b); + if(!parse_and_validate_block_from_blob(blockblob, b)) + { + error_resp.code = CORE_RPC_ERROR_CODE_WRONG_BLOCKBLOB; + error_resp.message = "Wrong block blob"; + return false; + } + miner::find_nonce_for_given_block(b, template_res.difficulty, template_res.height); + + submit_req.front() = string_tools::buff_to_hex_nodelimer(block_to_blob(b)); + r = on_submitblock(submit_req, submit_res, error_resp); + res.status = submit_res.status; + + if (!r) return false; + + res.height = template_res.height; + } + + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ uint64_t core_rpc_server::get_block_reward(const block& blk) { uint64_t reward = 0; diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h index 324f219f8da..5e62bc4a826 100644 --- a/src/rpc/core_rpc_server.h +++ b/src/rpc/core_rpc_server.h @@ -129,6 +129,7 @@ namespace cryptonote MAP_JON_RPC_WE("getblocktemplate", on_getblocktemplate, COMMAND_RPC_GETBLOCKTEMPLATE) MAP_JON_RPC_WE("submit_block", on_submitblock, COMMAND_RPC_SUBMITBLOCK) MAP_JON_RPC_WE("submitblock", on_submitblock, COMMAND_RPC_SUBMITBLOCK) + MAP_JON_RPC_WE_IF("generateblocks", on_generateblocks, COMMAND_RPC_GENERATEBLOCKS, !m_restricted) MAP_JON_RPC_WE("get_last_block_header", on_get_last_block_header, COMMAND_RPC_GET_LAST_BLOCK_HEADER) MAP_JON_RPC_WE("getlastblockheader", on_get_last_block_header, COMMAND_RPC_GET_LAST_BLOCK_HEADER) MAP_JON_RPC_WE("get_block_header_by_hash", on_get_block_header_by_hash, COMMAND_RPC_GET_BLOCK_HEADER_BY_HASH) @@ -196,6 +197,7 @@ namespace cryptonote bool on_getblockhash(const COMMAND_RPC_GETBLOCKHASH::request& req, COMMAND_RPC_GETBLOCKHASH::response& res, epee::json_rpc::error& error_resp); bool on_getblocktemplate(const COMMAND_RPC_GETBLOCKTEMPLATE::request& req, COMMAND_RPC_GETBLOCKTEMPLATE::response& res, epee::json_rpc::error& error_resp); bool on_submitblock(const COMMAND_RPC_SUBMITBLOCK::request& req, COMMAND_RPC_SUBMITBLOCK::response& res, epee::json_rpc::error& error_resp); + bool on_generateblocks(const COMMAND_RPC_GENERATEBLOCKS::request& req, COMMAND_RPC_GENERATEBLOCKS::response& res, epee::json_rpc::error& error_resp); bool on_get_last_block_header(const COMMAND_RPC_GET_LAST_BLOCK_HEADER::request& req, COMMAND_RPC_GET_LAST_BLOCK_HEADER::response& res, epee::json_rpc::error& error_resp); bool on_get_block_header_by_hash(const COMMAND_RPC_GET_BLOCK_HEADER_BY_HASH::request& req, COMMAND_RPC_GET_BLOCK_HEADER_BY_HASH::response& res, epee::json_rpc::error& error_resp); bool on_get_block_header_by_height(const COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT::request& req, COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT::response& res, epee::json_rpc::error& error_resp); diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index 70e18684871..c0b0577c7b1 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -1149,6 +1149,31 @@ namespace cryptonote END_KV_SERIALIZE_MAP() }; }; + + struct COMMAND_RPC_GENERATEBLOCKS + { + struct request + { + uint64_t amount_of_blocks; + std::string wallet_address; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(amount_of_blocks) + KV_SERIALIZE(wallet_address) + END_KV_SERIALIZE_MAP() + }; + + struct response + { + uint64_t height; + std::string status; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(height) + KV_SERIALIZE(status) + END_KV_SERIALIZE_MAP() + }; + }; struct block_header_response { diff --git a/src/rpc/core_rpc_server_error_codes.h b/src/rpc/core_rpc_server_error_codes.h index 69caaa6a63d..5a754749f68 100644 --- a/src/rpc/core_rpc_server_error_codes.h +++ b/src/rpc/core_rpc_server_error_codes.h @@ -42,5 +42,6 @@ #define CORE_RPC_ERROR_CODE_WRONG_BLOCKBLOB_SIZE -10 #define CORE_RPC_ERROR_CODE_UNSUPPORTED_RPC -11 #define CORE_RPC_ERROR_CODE_MINING_TO_SUBADDRESS -12 +#define CORE_RPC_ERROR_CODE_REGTEST_REQUIRED -13 diff --git a/tests/unit_tests/hardfork.cpp b/tests/unit_tests/hardfork.cpp index 7c27b9c5d35..083a69b3f2e 100644 --- a/tests/unit_tests/hardfork.cpp +++ b/tests/unit_tests/hardfork.cpp @@ -50,6 +50,7 @@ class TestDB: public BlockchainDB { virtual void safesyncmode(const bool onoff) {} virtual void reset() {} virtual std::vector get_filenames() const { return std::vector(); } + virtual bool remove_data_file(const std::string& folder) const { return true; } virtual std::string get_db_name() const { return std::string(); } virtual bool lock() { return true; } virtual void unlock() { } From 9e1403e1556c58065bf415a507af06f19f3cfeda Mon Sep 17 00:00:00 2001 From: victorsintnicolaas Date: Wed, 13 Jun 2018 22:38:26 +0200 Subject: [PATCH 0157/1404] update get_info RPC and bump RPC version --- src/rpc/core_rpc_server.cpp | 2 ++ src/rpc/core_rpc_server_commands_defs.h | 4 +++- src/rpc/message_data_structs.h | 1 + src/serialization/json_object.cpp | 2 ++ 4 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index c0b1d8891f6..d06636ebf48 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -194,6 +194,7 @@ namespace cryptonote res.mainnet = m_nettype == MAINNET; res.testnet = m_nettype == TESTNET; res.stagenet = m_nettype == STAGENET; + res.nettype = m_nettype == MAINNET ? "mainnet" : m_nettype == TESTNET ? "testnet" : m_nettype == STAGENET ? "stagenet" : "fakechain"; res.cumulative_difficulty = m_core.get_blockchain_storage().get_db().get_block_cumulative_difficulty(res.height - 1); res.block_size_limit = m_core.get_blockchain_storage().get_current_cumulative_blocksize_limit(); res.block_size_median = m_core.get_blockchain_storage().get_current_cumulative_blocksize_median(); @@ -1626,6 +1627,7 @@ namespace cryptonote res.mainnet = m_nettype == MAINNET; res.testnet = m_nettype == TESTNET; res.stagenet = m_nettype == STAGENET; + res.nettype = m_nettype == MAINNET ? "mainnet" : m_nettype == TESTNET ? "testnet" : m_nettype == STAGENET ? "stagenet" : "fakechain"; res.cumulative_difficulty = m_core.get_blockchain_storage().get_db().get_block_cumulative_difficulty(res.height - 1); res.block_size_limit = m_core.get_blockchain_storage().get_current_cumulative_blocksize_limit(); res.block_size_median = m_core.get_blockchain_storage().get_current_cumulative_blocksize_median(); diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index c0b0577c7b1..19f88988748 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -49,7 +49,7 @@ namespace cryptonote // advance which version they will stop working with // Don't go over 32767 for any of these #define CORE_RPC_VERSION_MAJOR 1 -#define CORE_RPC_VERSION_MINOR 20 +#define CORE_RPC_VERSION_MINOR 21 #define MAKE_CORE_RPC_VERSION(major,minor) (((major)<<16)|(minor)) #define CORE_RPC_VERSION MAKE_CORE_RPC_VERSION(CORE_RPC_VERSION_MAJOR, CORE_RPC_VERSION_MINOR) @@ -953,6 +953,7 @@ namespace cryptonote bool mainnet; bool testnet; bool stagenet; + std::string nettype; std::string top_block_hash; uint64_t cumulative_difficulty; uint64_t block_size_limit; @@ -982,6 +983,7 @@ namespace cryptonote KV_SERIALIZE(mainnet) KV_SERIALIZE(testnet) KV_SERIALIZE(stagenet) + KV_SERIALIZE(nettype) KV_SERIALIZE(top_block_hash) KV_SERIALIZE(cumulative_difficulty) KV_SERIALIZE(block_size_limit) diff --git a/src/rpc/message_data_structs.h b/src/rpc/message_data_structs.h index 17ae9629fba..fc1b2329d13 100644 --- a/src/rpc/message_data_structs.h +++ b/src/rpc/message_data_structs.h @@ -181,6 +181,7 @@ namespace rpc bool mainnet; bool testnet; bool stagenet; + std::string nettype; crypto::hash top_block_hash; uint64_t cumulative_difficulty; uint64_t block_size_limit; diff --git a/src/serialization/json_object.cpp b/src/serialization/json_object.cpp index a7fb58ee4ff..c2467b863e6 100644 --- a/src/serialization/json_object.cpp +++ b/src/serialization/json_object.cpp @@ -1175,6 +1175,7 @@ void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::DaemonInfo& in INSERT_INTO_JSON_OBJECT(val, doc, white_peerlist_size, info.white_peerlist_size); INSERT_INTO_JSON_OBJECT(val, doc, grey_peerlist_size, info.grey_peerlist_size); INSERT_INTO_JSON_OBJECT(val, doc, testnet, info.testnet); + INSERT_INTO_JSON_OBJECT(val, doc, nettype, info.nettype); INSERT_INTO_JSON_OBJECT(val, doc, top_block_hash, info.top_block_hash); INSERT_INTO_JSON_OBJECT(val, doc, cumulative_difficulty, info.cumulative_difficulty); INSERT_INTO_JSON_OBJECT(val, doc, block_size_limit, info.block_size_limit); @@ -1200,6 +1201,7 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::DaemonInfo& inf GET_FROM_JSON_OBJECT(val, info.white_peerlist_size, white_peerlist_size); GET_FROM_JSON_OBJECT(val, info.grey_peerlist_size, grey_peerlist_size); GET_FROM_JSON_OBJECT(val, info.testnet, testnet); + GET_FROM_JSON_OBJECT(val, info.nettype, nettype); GET_FROM_JSON_OBJECT(val, info.top_block_hash, top_block_hash); GET_FROM_JSON_OBJECT(val, info.cumulative_difficulty, cumulative_difficulty); GET_FROM_JSON_OBJECT(val, info.block_size_limit, block_size_limit); From 207b66ecc28833f1cb211d3489df96fc42ebab28 Mon Sep 17 00:00:00 2001 From: victorsintnicolaas Date: Fri, 15 Jun 2018 11:57:22 +0200 Subject: [PATCH 0158/1404] first new functional tests --- tests/README.md | 14 ++ tests/functional_tests/blockchain.py | 103 +++++++++++++++ tests/functional_tests/speed.py | 87 +++++++++++++ .../test_framework/__init__.py | 0 .../functional_tests/test_framework/daemon.py | 105 +++++++++++++++ tests/functional_tests/test_framework/rpc.py | 49 +++++++ .../functional_tests/test_framework/wallet.py | 120 ++++++++++++++++++ 7 files changed, 478 insertions(+) create mode 100755 tests/functional_tests/blockchain.py create mode 100755 tests/functional_tests/speed.py create mode 100644 tests/functional_tests/test_framework/__init__.py create mode 100644 tests/functional_tests/test_framework/daemon.py create mode 100644 tests/functional_tests/test_framework/rpc.py create mode 100644 tests/functional_tests/test_framework/wallet.py diff --git a/tests/README.md b/tests/README.md index 48a6c41a7ce..0bf097254c2 100644 --- a/tests/README.md +++ b/tests/README.md @@ -50,6 +50,20 @@ To run the same tests on a release build, replace `debug` with `release`. # Functional tests [TODO] +Functional tests are located under the `tests/functional` directory. + +First, run a regtest daemon in the offline mode and with a fixed difficulty: +``` +monerod --regtest --offline --fixed-difficulty 1 +``` +Alternatively, you can run multiple daemons and let them connect with each other by using `--add-exclusive-node`. In this case, make sure that the same fixed difficulty is given to all the daemons. + +Next, restore a mainnet wallet with the following seed and restore height 0 (the file path doesn't matter): +``` +velvet lymph giddy number token physics poetry unquoted nibs useful sabotage limits benches lifestyle eden nitrogen anvil fewest avoid batch vials washing fences goat unquoted +``` + +Open the wallet file with `monero-wallet-rpc` with RPC port 18083. Finally, start tests by invoking ./blockchain.py or ./speed.py # Fuzz tests diff --git a/tests/functional_tests/blockchain.py b/tests/functional_tests/blockchain.py new file mode 100755 index 00000000000..983658a7c98 --- /dev/null +++ b/tests/functional_tests/blockchain.py @@ -0,0 +1,103 @@ +#!/usr/bin/env python3 + +# Copyright (c) 2018 The Monero Project +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, are +# permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of +# conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list +# of conditions and the following disclaimer in the documentation and/or other +# materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be +# used to endorse or promote products derived from this software without specific +# prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +# THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Test blockchain RPC calls + +Test the following RPCs: + - get_info + - generateblocks + - [TODO: many tests still need to be written] + +""" + +from test_framework.daemon import Daemon +from test_framework.wallet import Wallet + +class BlockchainTest(): + def run_test(self): + self._test_get_info() + self._test_hardfork_info() + self._test_generateblocks(5) + + def _test_get_info(self): + print('Test get_info') + + daemon = Daemon() + res = daemon.get_info() + + # difficulty should be set to 1 for this test + assert 'difficulty' in res.keys() + assert res['difficulty'] == 1; + + # nettype should not be TESTNET + assert 'testnet' in res.keys() + assert res['testnet'] == False; + + # nettype should not be STAGENET + assert 'stagenet' in res.keys() + assert res['stagenet'] == False; + + # nettype should be FAKECHAIN + assert 'nettype' in res.keys() + assert res['nettype'] == "fakechain"; + + # free_space should be > 0 + assert 'free_space' in res.keys() + assert res['free_space'] > 0 + + # height should be greater or equal to 1 + assert 'height' in res.keys() + assert res['height'] >= 1 + + + def _test_hardfork_info(self): + print('Test hard_fork_info') + + daemon = Daemon() + res = daemon.hard_fork_info() + + # hard_fork version should be set at height 1 + assert 'earliest_height' in res.keys() + assert res['earliest_height'] == 1; + + + def _test_generateblocks(self, blocks): + print("Test generating", blocks, 'blocks') + + daemon = Daemon() + res = daemon.get_info() + height = res['height'] + res = daemon.generateblocks('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', blocks) + + assert res['height'] == height + blocks - 1 + + +if __name__ == '__main__': + BlockchainTest().run_test() diff --git a/tests/functional_tests/speed.py b/tests/functional_tests/speed.py new file mode 100755 index 00000000000..3d2af9a10bf --- /dev/null +++ b/tests/functional_tests/speed.py @@ -0,0 +1,87 @@ +#!/usr/bin/env python3 + +# Copyright (c) 2018 The Monero Project +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, are +# permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of +# conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list +# of conditions and the following disclaimer in the documentation and/or other +# materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be +# used to endorse or promote products derived from this software without specific +# prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +# THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Test speed of various procedures + +Test the following RPCs: + - generateblocks + - transfer + - [TODO: many tests still need to be written] + +""" + + +import time +from time import sleep +from decimal import Decimal + +from test_framework.daemon import Daemon +from test_framework.wallet import Wallet + + +class SpeedTest(): + def set_test_params(self): + self.num_nodes = 1 + + def run_test(self): + daemon = Daemon() + wallet = Wallet() + + destinations = wallet.make_uniform_destinations('44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A',1,3) + + self._test_speed_generateblocks(daemon=daemon, blocks=70) + for i in range(1, 10): + while wallet.get_balance()['unlocked_balance'] == 0: + print('Waiting for wallet to refresh...') + sleep(1) + self._test_speed_transfer_split(wallet=wallet) + self._test_speed_generateblocks(daemon=daemon, blocks=10) + + def _test_speed_generateblocks(self, daemon, blocks): + print('Test speed of block generation') + start = time.time() + + res = daemon.generateblocks('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', blocks) + # wallet seed: velvet lymph giddy number token physics poetry unquoted nibs useful sabotage limits benches lifestyle eden nitrogen anvil fewest avoid batch vials washing fences goat unquoted + + print('generating ', blocks, 'blocks took: ', time.time() - start, 'seconds') + + def _test_speed_transfer_split(self, wallet): + print('Test speed of transfer') + start = time.time() + + destinations = wallet.make_uniform_destinations('44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A',1) + res = wallet.transfer_split(destinations) + + print('generating tx took: ', time.time() - start, 'seconds') + + +if __name__ == '__main__': + SpeedTest().run_test() diff --git a/tests/functional_tests/test_framework/__init__.py b/tests/functional_tests/test_framework/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/functional_tests/test_framework/daemon.py b/tests/functional_tests/test_framework/daemon.py new file mode 100644 index 00000000000..f3490b2328a --- /dev/null +++ b/tests/functional_tests/test_framework/daemon.py @@ -0,0 +1,105 @@ +# Copyright (c) 2018 The Monero Project +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, are +# permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of +# conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list +# of conditions and the following disclaimer in the documentation and/or other +# materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be +# used to endorse or promote products derived from this software without specific +# prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +# THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Daemon class to make rpc calls and store state.""" + +from .rpc import JSONRPC + +class Daemon(object): + + def __init__(self, protocol='http', host='127.0.0.1', port=18081, path='/json_rpc'): + self.rpc = JSONRPC('{protocol}://{host}:{port}{path}'.format(protocol=protocol, host=host, port=port, path=path)) + + def getblocktemplate(self, address): + getblocktemplate = { + 'method': 'getblocktemplate', + 'params': { + 'wallet_address': address, + 'reserve_size' : 1 + }, + 'jsonrpc': '2.0', + 'id': '0' + } + return self.rpc.send_request(getblocktemplate) + + def submitblock(self, block): + submitblock = { + 'method': 'submitblock', + 'params': [ block ], + 'jsonrpc': '2.0', + 'id': '0' + } + return self.rpc.send_request(submitblock) + + def getblock(self, height=0): + getblock = { + 'method': 'getblock', + 'params': { + 'height': height + }, + 'jsonrpc': '2.0', + 'id': '0' + } + return self.rpc.send_request(getblock) + + def get_connections(self): + get_connections = { + 'method': 'get_connections', + 'jsonrpc': '2.0', + 'id': '0' + } + return self.rpc.send_request(get_connections) + + def get_info(self): + get_info = { + 'method': 'get_info', + 'jsonrpc': '2.0', + 'id': '0' + } + return self.rpc.send_request(get_info) + + def hard_fork_info(self): + hard_fork_info = { + 'method': 'hard_fork_info', + 'jsonrpc': '2.0', + 'id': '0' + } + return self.rpc.send_request(hard_fork_info) + + def generateblocks(self, address, blocks=1): + generateblocks = { + 'method': 'generateblocks', + 'params': { + 'amount_of_blocks' : blocks, + 'reserve_size' : 20, + 'wallet_address': address + }, + 'jsonrpc': '2.0', + 'id': '0' + } + return self.rpc.send_request(generateblocks) diff --git a/tests/functional_tests/test_framework/rpc.py b/tests/functional_tests/test_framework/rpc.py new file mode 100644 index 00000000000..b21df7b93c0 --- /dev/null +++ b/tests/functional_tests/test_framework/rpc.py @@ -0,0 +1,49 @@ +# Copyright (c) 2018 The Monero Project +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, are +# permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of +# conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list +# of conditions and the following disclaimer in the documentation and/or other +# materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be +# used to endorse or promote products derived from this software without specific +# prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +# THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import requests +import json + +class JSONRPC(object): + def __init__(self, url): + self.url = url + + def send_request(self, inputs): + res = requests.post( + self.url, + data=json.dumps(inputs), + headers={'content-type': 'application/json'}) + res = res.json() + + assert 'error' not in res, res + + return res['result'] + + + + diff --git a/tests/functional_tests/test_framework/wallet.py b/tests/functional_tests/test_framework/wallet.py new file mode 100644 index 00000000000..357eab5b266 --- /dev/null +++ b/tests/functional_tests/test_framework/wallet.py @@ -0,0 +1,120 @@ +# Copyright (c) 2018 The Monero Project +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, are +# permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of +# conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list +# of conditions and the following disclaimer in the documentation and/or other +# materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be +# used to endorse or promote products derived from this software without specific +# prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +# THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Daemon class to make rpc calls and store state.""" + +from .rpc import JSONRPC + +class Wallet(object): + + def __init__(self, protocol='http', host='127.0.0.1', port=18083, path='/json_rpc'): + self.rpc = JSONRPC('{protocol}://{host}:{port}{path}'.format(protocol=protocol, host=host, port=port, path=path)) + + def make_uniform_destinations(self, address, transfer_amount, transfer_number_of_destinations=1): + destinations = [] + for i in range(transfer_number_of_destinations): + destinations.append({"amount":transfer_amount,"address":address}) + return destinations + + def make_destinations(self, addresses, transfer_amounts): + destinations = [] + for i in range(len(addresses)): + destinations.append({'amount':transfer_amounts[i],'address':addresses[i]}) + return destinations + + def transfer(self, destinations, ringsize=7, payment_id=''): + transfer = { + 'method': 'transfer', + 'params': { + 'destinations': destinations, + 'mixin' : ringsize - 1, + 'get_tx_key' : True + }, + 'jsonrpc': '2.0', + 'id': '0' + } + if(len(payment_id) > 0): + transfer['params'].update({'payment_id' : payment_id}) + return self.rpc.send_request(transfer) + + def transfer_split(self, destinations, ringsize=7, payment_id=''): + print(destinations) + transfer = { + "method": "transfer_split", + "params": { + "destinations": destinations, + "mixin" : ringsize - 1, + "get_tx_key" : True, + "new_algorithm" : True + }, + "jsonrpc": "2.0", + "id": "0" + } + if(len(payment_id) > 0): + transfer['params'].update({'payment_id' : payment_id}) + return self.rpc.send_request(transfer) + + def create_wallet(self, index=''): + create_wallet = { + 'method': 'create_wallet', + 'params': { + 'filename': 'testWallet' + index, + 'password' : '', + 'language' : 'English' + }, + 'jsonrpc': '2.0', + 'id': '0' + } + return self.rpc.send_request(create_wallet) + + def get_balance(self): + get_balance = { + 'method': 'get_balance', + 'jsonrpc': '2.0', + 'id': '0' + } + return self.rpc.send_request(get_balance) + + def sweep_dust(self): + sweep_dust = { + 'method': 'sweep_dust', + 'jsonrpc': '2.0', + 'id': '0' + } + return self.rpc.send_request(sweep_dust) + + def sweep_all(self, address): + sweep_all = { + 'method': 'sweep_all', + 'params' : { + 'address' : '' + }, + 'jsonrpc': '2.0', + 'id': '0' + } + return self.rpc.send_request(sweep_all) From dead780f8a3f384a57be29747b570b94ff6eabb5 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 29 Jun 2018 15:40:19 +0100 Subject: [PATCH 0159/1404] abstract_tcp_server2: fix use after free --- contrib/epee/include/net/abstract_tcp_server2.inl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contrib/epee/include/net/abstract_tcp_server2.inl b/contrib/epee/include/net/abstract_tcp_server2.inl index 134bb4199ba..5f1f3528016 100644 --- a/contrib/epee/include/net/abstract_tcp_server2.inl +++ b/contrib/epee/include/net/abstract_tcp_server2.inl @@ -653,13 +653,13 @@ PRAGMA_WARNING_DISABLE_VS(4355) m_timer.cancel(); boost::system::error_code ignored_ec; socket_.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignored_ec); - m_was_shutdown = true; - m_protocol_handler.release_protocol(); if (!m_host.empty()) { try { host_count(m_host, -1); } catch (...) { /* ignore */ } m_host = ""; } + m_was_shutdown = true; + m_protocol_handler.release_protocol(); return true; } //--------------------------------------------------------------------------------- From d95bc44c6ba2a1096ca95d1565851e5827f48e4f Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 29 Jun 2018 15:49:49 +0100 Subject: [PATCH 0160/1404] blockchain: fix getting invalid block data on failure --- src/cryptonote_core/blockchain.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 73ce98366f5..87f5c165de7 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -2217,7 +2217,8 @@ bool Blockchain::find_blockchain_supplement(const std::list& qbloc CRITICAL_REGION_LOCAL(m_blockchain_lock); bool result = find_blockchain_supplement(qblock_ids, resp.m_block_ids, resp.start_height, resp.total_height); - resp.cumulative_difficulty = m_db->get_block_cumulative_difficulty(resp.total_height - 1); + if (result) + resp.cumulative_difficulty = m_db->get_block_cumulative_difficulty(resp.total_height - 1); return result; } From 8c4db68ff71537759aac5dca50d5d8d1c6032d37 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 30 Jun 2018 22:06:09 +0100 Subject: [PATCH 0161/1404] node_rpc_proxy: factor a few RPC calls using get_info Takes advantage of caching --- src/simplewallet/simplewallet.cpp | 7 +-- src/wallet/node_rpc_proxy.cpp | 72 +++++++++++++++---------------- src/wallet/node_rpc_proxy.h | 7 ++- src/wallet/wallet2.cpp | 58 +++++++------------------ 4 files changed, 58 insertions(+), 86 deletions(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 0b2d072d6e7..08357ec88b4 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -4277,12 +4277,7 @@ uint64_t simple_wallet::get_daemon_blockchain_height(std::string& err) { throw std::runtime_error("simple_wallet null wallet"); } - - COMMAND_RPC_GET_HEIGHT::request req; - COMMAND_RPC_GET_HEIGHT::response res = boost::value_initialized(); - bool r = m_wallet->invoke_http_json("/getheight", req, res); - err = interpret_rpc_response(r, res.status); - return res.height; + return m_wallet->get_daemon_blockchain_height(err); } //---------------------------------------------------------------------------------------------------- bool simple_wallet::show_blockchain_height(const std::vector& args) diff --git a/src/wallet/node_rpc_proxy.cpp b/src/wallet/node_rpc_proxy.cpp index c5d869354ab..401ada61b34 100644 --- a/src/wallet/node_rpc_proxy.cpp +++ b/src/wallet/node_rpc_proxy.cpp @@ -41,21 +41,13 @@ static const std::chrono::seconds rpc_timeout = std::chrono::minutes(3) + std::c NodeRPCProxy::NodeRPCProxy(epee::net_utils::http::http_simple_client &http_client, boost::mutex &mutex) : m_http_client(http_client) , m_daemon_rpc_mutex(mutex) - , m_height(0) - , m_height_time(0) - , m_earliest_height() - , m_dynamic_per_kb_fee_estimate(0) - , m_dynamic_per_kb_fee_estimate_cached_height(0) - , m_dynamic_per_kb_fee_estimate_grace_blocks(0) - , m_rpc_version(0) - , m_target_height(0) - , m_target_height_time(0) -{} +{ + invalidate(); +} void NodeRPCProxy::invalidate() { m_height = 0; - m_height_time = 0; for (size_t n = 0; n < 256; ++n) m_earliest_height[n] = 0; m_dynamic_per_kb_fee_estimate = 0; @@ -63,7 +55,8 @@ void NodeRPCProxy::invalidate() m_dynamic_per_kb_fee_estimate_grace_blocks = 0; m_rpc_version = 0; m_target_height = 0; - m_target_height_time = 0; + m_block_size_limit = 0; + m_get_info_time = 0; } boost::optional NodeRPCProxy::get_rpc_version(uint32_t &rpc_version) const @@ -84,36 +77,15 @@ boost::optional NodeRPCProxy::get_rpc_version(uint32_t &rpc_version return boost::optional(); } -boost::optional NodeRPCProxy::get_height(uint64_t &height) const -{ - const time_t now = time(NULL); - if (m_height == 0 || now >= m_height_time + 30) // re-cache every 30 seconds - { - cryptonote::COMMAND_RPC_GET_HEIGHT::request req = AUTO_VAL_INIT(req); - cryptonote::COMMAND_RPC_GET_HEIGHT::response res = AUTO_VAL_INIT(res); - - m_daemon_rpc_mutex.lock(); - bool r = net_utils::invoke_http_json("/getheight", req, res, m_http_client, rpc_timeout); - m_daemon_rpc_mutex.unlock(); - CHECK_AND_ASSERT_MES(r, std::string(), "Failed to connect to daemon"); - CHECK_AND_ASSERT_MES(res.status != CORE_RPC_STATUS_BUSY, res.status, "Failed to connect to daemon"); - CHECK_AND_ASSERT_MES(res.status == CORE_RPC_STATUS_OK, res.status, "Failed to get current blockchain height"); - m_height = res.height; - m_height_time = now; - } - height = m_height; - return boost::optional(); -} - void NodeRPCProxy::set_height(uint64_t h) { m_height = h; } -boost::optional NodeRPCProxy::get_target_height(uint64_t &height) const +boost::optional NodeRPCProxy::get_info() const { const time_t now = time(NULL); - if (m_target_height == 0 || now >= m_target_height_time + 30) // re-cache every 30 seconds + if (now >= m_get_info_time + 30) // re-cache every 30 seconds { cryptonote::COMMAND_RPC_GET_INFO::request req_t = AUTO_VAL_INIT(req_t); cryptonote::COMMAND_RPC_GET_INFO::response resp_t = AUTO_VAL_INIT(resp_t); @@ -125,13 +97,41 @@ boost::optional NodeRPCProxy::get_target_height(uint64_t &height) c CHECK_AND_ASSERT_MES(r, std::string(), "Failed to connect to daemon"); CHECK_AND_ASSERT_MES(resp_t.status != CORE_RPC_STATUS_BUSY, resp_t.status, "Failed to connect to daemon"); CHECK_AND_ASSERT_MES(resp_t.status == CORE_RPC_STATUS_OK, resp_t.status, "Failed to get target blockchain height"); + m_height = resp_t.height; m_target_height = resp_t.target_height; - m_target_height_time = now; + m_block_size_limit = resp_t.block_size_limit; + m_get_info_time = now; } + return boost::optional(); +} + +boost::optional NodeRPCProxy::get_height(uint64_t &height) const +{ + auto res = get_info(); + if (res) + return res; + height = m_height; + return boost::optional(); +} + +boost::optional NodeRPCProxy::get_target_height(uint64_t &height) const +{ + auto res = get_info(); + if (res) + return res; height = m_target_height; return boost::optional(); } +boost::optional NodeRPCProxy::get_block_size_limit(uint64_t &block_size_limit) const +{ + auto res = get_info(); + if (res) + return res; + block_size_limit = m_block_size_limit; + return boost::optional(); +} + boost::optional NodeRPCProxy::get_earliest_height(uint8_t version, uint64_t &earliest_height) const { if (m_earliest_height[version] == 0) diff --git a/src/wallet/node_rpc_proxy.h b/src/wallet/node_rpc_proxy.h index 1b183212ddb..8a65884f71d 100644 --- a/src/wallet/node_rpc_proxy.h +++ b/src/wallet/node_rpc_proxy.h @@ -47,22 +47,25 @@ class NodeRPCProxy boost::optional get_height(uint64_t &height) const; void set_height(uint64_t h); boost::optional get_target_height(uint64_t &height) const; + boost::optional get_block_size_limit(uint64_t &block_size_limit) const; boost::optional get_earliest_height(uint8_t version, uint64_t &earliest_height) const; boost::optional get_dynamic_per_kb_fee_estimate(uint64_t grace_blocks, uint64_t &fee) const; private: + boost::optional get_info() const; + epee::net_utils::http::http_simple_client &m_http_client; boost::mutex &m_daemon_rpc_mutex; mutable uint64_t m_height; - mutable time_t m_height_time; mutable uint64_t m_earliest_height[256]; mutable uint64_t m_dynamic_per_kb_fee_estimate; mutable uint64_t m_dynamic_per_kb_fee_estimate_cached_height; mutable uint64_t m_dynamic_per_kb_fee_estimate_grace_blocks; mutable uint32_t m_rpc_version; mutable uint64_t m_target_height; - mutable time_t m_target_height_time; + mutable uint64_t m_block_size_limit; + mutable time_t m_get_info_time; }; } diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 1a1537e621b..2e1923fd6a4 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -5600,15 +5600,10 @@ uint32_t wallet2::adjust_priority(uint32_t priority) } // get the current full reward zone - cryptonote::COMMAND_RPC_GET_INFO::request getinfo_req = AUTO_VAL_INIT(getinfo_req); - cryptonote::COMMAND_RPC_GET_INFO::response getinfo_res = AUTO_VAL_INIT(getinfo_res); - m_daemon_rpc_mutex.lock(); - bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_info", getinfo_req, getinfo_res, m_http_client); - m_daemon_rpc_mutex.unlock(); - THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_info"); - THROW_WALLET_EXCEPTION_IF(getinfo_res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_info"); - THROW_WALLET_EXCEPTION_IF(getinfo_res.status != CORE_RPC_STATUS_OK, error::get_tx_pool_error); - const uint64_t full_reward_zone = getinfo_res.block_size_limit / 2; + uint64_t block_size_limit = 0; + const auto result = m_node_rpc_proxy.get_block_size_limit(block_size_limit); + throw_on_rpc_response_error(result, "get_info"); + const uint64_t full_reward_zone = block_size_limit / 2; // get the last N block headers and sum the block sizes const size_t N = 10; @@ -5622,7 +5617,7 @@ uint32_t wallet2::adjust_priority(uint32_t priority) m_daemon_rpc_mutex.lock(); getbh_req.start_height = m_blockchain.size() - N; getbh_req.end_height = m_blockchain.size() - 1; - r = net_utils::invoke_http_json_rpc("/json_rpc", "getblockheadersrange", getbh_req, getbh_res, m_http_client, rpc_timeout); + bool r = net_utils::invoke_http_json_rpc("/json_rpc", "getblockheadersrange", getbh_req, getbh_res, m_http_client, rpc_timeout); m_daemon_rpc_mutex.unlock(); THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "getblockheadersrange"); THROW_WALLET_EXCEPTION_IF(getbh_res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "getblockheadersrange"); @@ -9384,31 +9379,15 @@ uint64_t wallet2::get_daemon_blockchain_height(string &err) const uint64_t wallet2::get_daemon_blockchain_target_height(string &err) { - cryptonote::COMMAND_RPC_GET_INFO::request req_t = AUTO_VAL_INIT(req_t); - cryptonote::COMMAND_RPC_GET_INFO::response resp_t = AUTO_VAL_INIT(resp_t); - m_daemon_rpc_mutex.lock(); - bool ok = net_utils::invoke_http_json_rpc("/json_rpc", "get_info", req_t, resp_t, m_http_client); - m_daemon_rpc_mutex.unlock(); - if (ok) - { - if (resp_t.status == CORE_RPC_STATUS_BUSY) - { - err = "daemon is busy. Please try again later."; - } - else if (resp_t.status != CORE_RPC_STATUS_OK) - { - err = resp_t.status; - } - else // success, cleaning up error message - { - err = ""; - } - } - else + err = ""; + uint64_t target_height = 0; + const auto result = m_node_rpc_proxy.get_target_height(target_height); + if (result && *result != CORE_RPC_STATUS_OK) { - err = "possibly lost connection to daemon"; + err= *result; + return 0; } - return resp_t.target_height; + return target_height; } uint64_t wallet2::get_approximate_blockchain_height() const @@ -10766,15 +10745,10 @@ std::vector> wallet2::estimate_backlog(const std:: THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_txpool_backlog"); THROW_WALLET_EXCEPTION_IF(res.status != CORE_RPC_STATUS_OK, error::get_tx_pool_error); - cryptonote::COMMAND_RPC_GET_INFO::request req_t = AUTO_VAL_INIT(req_t); - cryptonote::COMMAND_RPC_GET_INFO::response resp_t = AUTO_VAL_INIT(resp_t); - m_daemon_rpc_mutex.lock(); - r = net_utils::invoke_http_json_rpc("/json_rpc", "get_info", req_t, resp_t, m_http_client); - m_daemon_rpc_mutex.unlock(); - THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_info"); - THROW_WALLET_EXCEPTION_IF(resp_t.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_info"); - THROW_WALLET_EXCEPTION_IF(resp_t.status != CORE_RPC_STATUS_OK, error::get_tx_pool_error); - uint64_t full_reward_zone = resp_t.block_size_limit / 2; + uint64_t block_size_limit = 0; + const auto result = m_node_rpc_proxy.get_block_size_limit(block_size_limit); + throw_on_rpc_response_error(result, "get_info"); + uint64_t full_reward_zone = block_size_limit / 2; std::vector> blocks; for (const auto &fee_level: fee_levels) From 909398efc79cb1fa92e330e9a50a316ca5858953 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 2 Jul 2018 18:04:44 +0100 Subject: [PATCH 0162/1404] p2p: connect via the bound ip, if any --- src/p2p/net_node.inl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index 85470f79962..014c3e9c19b 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -934,7 +934,7 @@ namespace nodetool bool res = m_net_server.connect(epee::string_tools::get_ip_string_from_int32(ipv4.ip()), epee::string_tools::num_to_string_fast(ipv4.port()), m_config.m_net_config.connection_timeout, - con); + con, m_bind_ip.empty() ? "0.0.0.0" : m_bind_ip); if(!res) { @@ -999,7 +999,7 @@ namespace nodetool bool res = m_net_server.connect(epee::string_tools::get_ip_string_from_int32(ipv4.ip()), epee::string_tools::num_to_string_fast(ipv4.port()), m_config.m_net_config.connection_timeout, - con); + con, m_bind_ip.empty() ? "0.0.0.0" : m_bind_ip); if (!res) { bool is_priority = is_priority_node(na); @@ -1614,7 +1614,7 @@ namespace nodetool return false; } return true; - }); + }, m_bind_ip.empty() ? "0.0.0.0" : m_bind_ip); if(!r) { LOG_WARNING_CC(context, "Failed to call connect_async, network error."); From d6d78f157b9cb21fc9e4877d06d61155897f4864 Mon Sep 17 00:00:00 2001 From: stoffu Date: Thu, 28 Jun 2018 11:31:50 +0900 Subject: [PATCH 0163/1404] Allow fractional outputs to be ignored --- src/simplewallet/simplewallet.cpp | 15 +++++++++++++++ src/simplewallet/simplewallet.h | 1 + src/wallet/wallet2.cpp | 19 +++++++++++++++++++ src/wallet/wallet2.h | 3 +++ 4 files changed, 38 insertions(+) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 0b2d072d6e7..b0489644dd5 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -2092,6 +2092,19 @@ bool simple_wallet::set_segregation_height(const std::vector &args/ return true; } +bool simple_wallet::set_ignore_fractional_outputs(const std::vector &args/* = std::vector()*/) +{ + const auto pwd_container = get_and_verify_password(); + if (pwd_container) + { + parse_bool_and_use(args[1], [&](bool r) { + m_wallet->ignore_fractional_outputs(r); + m_wallet->rewrite(m_wallet_file, pwd_container->password()); + }); + } + return true; +} + bool simple_wallet::help(const std::vector &args/* = std::vector()*/) { if(args.empty()) @@ -2479,6 +2492,7 @@ bool simple_wallet::set_variable(const std::vector &args) const std::pair lookahead = m_wallet->get_subaddress_lookahead(); success_msg_writer() << "subaddress-lookahead = " << lookahead.first << ":" << lookahead.second; success_msg_writer() << "segregation-height = " << m_wallet->segregation_height(); + success_msg_writer() << "ignore-fractional-outputs = " << m_wallet->ignore_fractional_outputs(); return true; } else @@ -2533,6 +2547,7 @@ bool simple_wallet::set_variable(const std::vector &args) CHECK_SIMPLE_VARIABLE("key-reuse-mitigation2", set_key_reuse_mitigation2, tr("0 or 1")); CHECK_SIMPLE_VARIABLE("subaddress-lookahead", set_subaddress_lookahead, tr(":")); CHECK_SIMPLE_VARIABLE("segregation-height", set_segregation_height, tr("unsigned integer")); + CHECK_SIMPLE_VARIABLE("ignore-fractional-outputs", set_ignore_fractional_outputs, tr("0 or 1")); } fail_msg_writer() << tr("set: unrecognized argument(s)"); return true; diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index 7a788d43270..1f27650553a 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -137,6 +137,7 @@ namespace cryptonote bool set_key_reuse_mitigation2(const std::vector &args = std::vector()); bool set_subaddress_lookahead(const std::vector &args = std::vector()); bool set_segregation_height(const std::vector &args = std::vector()); + bool set_ignore_fractional_outputs(const std::vector &args = std::vector()); bool help(const std::vector &args = std::vector()); bool start_mining(const std::vector &args); bool stop_mining(const std::vector &args); diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 1a1537e621b..ebbf9b1e714 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -676,6 +676,7 @@ wallet2::wallet2(network_type nettype, bool restricted): m_segregate_pre_fork_outputs(true), m_key_reuse_mitigation2(true), m_segregation_height(0), + m_ignore_fractional_outputs(true), m_is_initialized(false), m_restricted(restricted), is_old_file_format(false), @@ -2782,6 +2783,9 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable value2.SetUint(m_segregation_height); json.AddMember("segregation_height", value2, json.GetAllocator()); + value2.SetInt(m_ignore_fractional_outputs ? 1 : 0); + json.AddMember("ignore_fractional_outputs", value2, json.GetAllocator()); + value2.SetUint(m_subaddress_lookahead_major); json.AddMember("subaddress_lookahead_major", value2, json.GetAllocator()); @@ -2864,6 +2868,7 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_ m_segregate_pre_fork_outputs = true; m_key_reuse_mitigation2 = true; m_segregation_height = 0; + m_ignore_fractional_outputs = true; m_subaddress_lookahead_major = SUBADDRESS_LOOKAHEAD_MAJOR; m_subaddress_lookahead_minor = SUBADDRESS_LOOKAHEAD_MINOR; m_key_on_device = false; @@ -2990,6 +2995,8 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_ m_key_reuse_mitigation2 = field_key_reuse_mitigation2; GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, segregation_height, int, Uint, false, 0); m_segregation_height = field_segregation_height; + GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, ignore_fractional_outputs, int, Int, false, true); + m_ignore_fractional_outputs = field_ignore_fractional_outputs; GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, subaddress_lookahead_major, uint32_t, Uint, false, SUBADDRESS_LOOKAHEAD_MAJOR); m_subaddress_lookahead_major = field_subaddress_lookahead_major; GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, subaddress_lookahead_minor, uint32_t, Uint, false, SUBADDRESS_LOOKAHEAD_MINOR); @@ -7673,12 +7680,24 @@ std::vector wallet2::create_transactions_2(std::vector tx_size_two_rings, error::wallet_internal_error, "Estimated tx size with 1 input is larger than with 2 inputs!"); + const size_t tx_size_per_ring = tx_size_two_rings - tx_size_one_ring; + const uint64_t fractional_threshold = (fee_multiplier * fee_per_kb * tx_size_per_ring) / 1024; + // gather all dust and non-dust outputs belonging to specified subaddresses size_t num_nondust_outputs = 0; size_t num_dust_outputs = 0; for (size_t i = 0; i < m_transfers.size(); ++i) { const transfer_details& td = m_transfers[i]; + if (m_ignore_fractional_outputs && td.amount() < fractional_threshold) + { + MDEBUG("Ignoring output " << i << " of amount " << print_money(td.amount()) << " which is below threshold " << print_money(fractional_threshold)); + continue; + } if (!td.m_spent && !td.m_key_image_partial && (use_rct ? true : !td.is_rct()) && is_transfer_unlocked(td) && td.m_subaddr_index.major == subaddr_account && subaddr_indices.count(td.m_subaddr_index.minor) == 1) { const uint32_t index_minor = td.m_subaddr_index.minor; diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index d33d8258bbd..1b8014daaec 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -925,6 +925,8 @@ namespace tools void key_reuse_mitigation2(bool value) { m_key_reuse_mitigation2 = value; } uint64_t segregation_height() const { return m_segregation_height; } void segregation_height(uint64_t height) { m_segregation_height = height; } + bool ignore_fractional_outputs() const { return m_ignore_fractional_outputs; } + void ignore_fractional_outputs(bool value) { m_ignore_fractional_outputs = value; } bool confirm_non_default_ring_size() const { return m_confirm_non_default_ring_size; } void confirm_non_default_ring_size(bool always) { m_confirm_non_default_ring_size = always; } @@ -1283,6 +1285,7 @@ namespace tools bool m_segregate_pre_fork_outputs; bool m_key_reuse_mitigation2; uint64_t m_segregation_height; + bool m_ignore_fractional_outputs; bool m_is_initialized; NodeRPCProxy m_node_rpc_proxy; std::unordered_set m_scanned_pool_txs[2]; From a4272de79746c2349a0da8371f45390989cc6b90 Mon Sep 17 00:00:00 2001 From: stoffu Date: Tue, 3 Jul 2018 11:33:11 +0900 Subject: [PATCH 0164/1404] wallet2: unlock keys file before calling verify_password (needed for Windows) Also added notes to WalletManager::verifyWalletPassword (which afaik seems unused by anyone at the moment) regarding the need to unlock the keys file beforehand. --- src/wallet/api/wallet.cpp | 14 +++++++++++ src/wallet/api/wallet.h | 3 +++ src/wallet/api/wallet2_api.h | 10 ++++++++ src/wallet/wallet2.cpp | 49 +++++++++++++++++++++++++++++------- src/wallet/wallet2.h | 5 +++- 5 files changed, 71 insertions(+), 10 deletions(-) diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index 3f6bfec9e88..680da26ce5d 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -2205,6 +2205,20 @@ void WalletImpl::keyReuseMitigation2(bool mitigation) m_wallet->key_reuse_mitigation2(mitigation); } +bool WalletImpl::lockKeysFile() +{ + return m_wallet->lock_keys_file(); +} + +bool WalletImpl::unlockKeysFile() +{ + return m_wallet->unlock_keys_file(); +} + +bool WalletImpl::isKeysFileLocked() +{ + return m_wallet->is_keys_file_locked(); +} } // namespace namespace Bitmonero = Monero; diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h index eefb2fe94a9..58be686fcd2 100644 --- a/src/wallet/api/wallet.h +++ b/src/wallet/api/wallet.h @@ -188,6 +188,9 @@ class WalletImpl : public Wallet virtual void segregatePreForkOutputs(bool segregate) override; virtual void segregationHeight(uint64_t height) override; virtual void keyReuseMitigation2(bool mitigation) override; + virtual bool lockKeysFile() override; + virtual bool unlockKeysFile() override; + virtual bool isKeysFileLocked() override; private: void clearStatus() const; diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h index f54255e91c2..0cd0ff5cf1a 100644 --- a/src/wallet/api/wallet2_api.h +++ b/src/wallet/api/wallet2_api.h @@ -900,6 +900,12 @@ struct Wallet //! Initiates a light wallet import wallet request virtual bool lightWalletImportWalletRequest(std::string &payment_id, uint64_t &fee, bool &new_request, bool &request_fulfilled, std::string &payment_address, std::string &status) = 0; + + //! locks/unlocks the keys file; returns true on success + virtual bool lockKeysFile() = 0; + virtual bool unlockKeysFile() = 0; + //! returns true if the keys file is locked + virtual bool isKeysFileLocked() = 0; }; /** @@ -1070,6 +1076,10 @@ struct WalletManager * @param password - password to verify * @param no_spend_key - verify only view keys? * @return - true if password is correct + * + * @note + * This function will fail when the wallet keys file is opened because the wallet program locks the keys file. + * In this case, Wallet::unlockKeysFile() and Wallet::lockKeysFile() need to be called before and after the call to this function, respectively. */ virtual bool verifyWalletPassword(const std::string &keys_file_name, const std::string &password, bool no_spend_key) const = 0; diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 1a1537e621b..7433cb9a5c7 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -2636,7 +2636,7 @@ void wallet2::detach_blockchain(uint64_t height) bool wallet2::deinit() { m_is_initialized=false; - m_keys_file_locker.reset(); + unlock_keys_file(); return true; } //---------------------------------------------------------------------------------------------------- @@ -2803,12 +2803,12 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable crypto::chacha20(account_data.data(), account_data.size(), key, keys_file_data.iv, &cipher[0]); keys_file_data.account_data = cipher; - m_keys_file_locker.reset(); + unlock_keys_file(); std::string buf; r = ::serialization::dump_binary(keys_file_data, buf); r = r && epee::file_io_utils::save_string_to_file(keys_file_name, buf); //and never touch wallet_keys_file again, only read CHECK_AND_ASSERT_MES(r, false, "failed to generate wallet keys file " << keys_file_name); - m_keys_file_locker.reset(new tools::file_locker(m_keys_file)); + lock_keys_file(); return true; } @@ -3029,9 +3029,13 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_ * can be used prior to rewriting wallet keys file, to ensure user has entered the correct password * */ -bool wallet2::verify_password(const epee::wipeable_string& password) const +bool wallet2::verify_password(const epee::wipeable_string& password) { - return verify_password(m_keys_file, password, m_watch_only || m_multisig, m_account.get_device()); + // this temporary unlocking is necessary for Windows (otherwise the file couldn't be loaded). + unlock_keys_file(); + bool r = verify_password(m_keys_file, password, m_watch_only || m_multisig, m_account.get_device()); + lock_keys_file(); + return r; } /*! @@ -3938,17 +3942,17 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass boost::system::error_code e; bool exists = boost::filesystem::exists(m_keys_file, e); THROW_WALLET_EXCEPTION_IF(e || !exists, error::file_not_found, m_keys_file); - m_keys_file_locker.reset(new tools::file_locker(m_keys_file)); - THROW_WALLET_EXCEPTION_IF(!m_keys_file_locker->locked(), error::wallet_internal_error, "internal error: \"" + m_keys_file + "\" is opened by another wallet program"); + lock_keys_file(); + THROW_WALLET_EXCEPTION_IF(!is_keys_file_locked(), error::wallet_internal_error, "internal error: \"" + m_keys_file + "\" is opened by another wallet program"); // this temporary unlocking is necessary for Windows (otherwise the file couldn't be loaded). - m_keys_file_locker.reset(); + unlock_keys_file(); if (!load_keys(m_keys_file, password)) { THROW_WALLET_EXCEPTION_IF(true, error::file_read_error, m_keys_file); } LOG_PRINT_L0("Loaded wallet keys file, with public address: " << m_account.get_public_address_str(m_nettype)); - m_keys_file_locker.reset(new tools::file_locker(m_keys_file)); + lock_keys_file(); //keys loaded ok! //try to load wallet file. but even if we failed, it is not big problem @@ -5989,6 +5993,33 @@ bool wallet2::is_output_blackballed(const crypto::public_key &output) const catch (const std::exception &e) { return false; } } +bool wallet2::lock_keys_file() +{ + if (m_keys_file_locker) + { + MDEBUG(m_keys_file << " is already locked."); + return false; + } + m_keys_file_locker.reset(new tools::file_locker(m_keys_file)); + return true; +} + +bool wallet2::unlock_keys_file() +{ + if (!m_keys_file_locker) + { + MDEBUG(m_keys_file << " is already unlocked."); + return false; + } + m_keys_file_locker.reset(); + return true; +} + +bool wallet2::is_keys_file_locked() const +{ + return m_keys_file_locker->locked(); +} + bool wallet2::tx_add_fake_output(std::vector> &outs, uint64_t global_index, const crypto::public_key& output_public_key, const rct::key& mask, uint64_t real_index, bool unlocked) const { if (!unlocked) // don't add locked outs diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index d33d8258bbd..c545876939d 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -609,7 +609,7 @@ namespace tools /*! * \brief verifies given password is correct for default wallet keys file */ - bool verify_password(const epee::wipeable_string& password) const; + bool verify_password(const epee::wipeable_string& password); cryptonote::account_base& get_account(){return m_account;} const cryptonote::account_base& get_account()const{return m_account;} @@ -1144,6 +1144,9 @@ namespace tools bool unblackball_output(const crypto::public_key &output); bool is_output_blackballed(const crypto::public_key &output) const; + bool lock_keys_file(); + bool unlock_keys_file(); + bool is_keys_file_locked() const; private: /*! * \brief Stores wallet information to wallet file. From 58cceaad7102c8e1c7130c47fa22cdb50eab97e8 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 11 Jun 2018 12:01:41 +0100 Subject: [PATCH 0165/1404] wallet2: fix double counting outs if the tx pubkey is duplicated --- src/wallet/wallet2.cpp | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 1a1537e621b..b080172e0c4 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -1172,6 +1172,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote // Don't try to extract tx public key if tx has no ouputs size_t pk_index = 0; std::vector tx_scan_info(tx.vout.size()); + std::unordered_set public_keys_seen; while (!tx.vout.empty()) { // if tx.vout is not empty, we loop through all tx pubkeys @@ -1192,6 +1193,13 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote error::wallet_internal_error, "tx_cache_data is out of sync"); } + if (public_keys_seen.find(pub_key_field.pub_key) != public_keys_seen.end()) + { + MWARNING("The same transaction pubkey is present more than once, ignoring extra instance"); + continue; + } + public_keys_seen.insert(pub_key_field.pub_key); + int num_vouts_received = 0; tx_pub_key = pub_key_field.pub_key; tools::threadpool& tpool = tools::threadpool::getInstance(); @@ -1216,16 +1224,19 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote memcpy(&derivation, rct::identity().bytes, sizeof(derivation)); } - // additional tx pubkeys and derivations for multi-destination transfers involving one or more subaddresses - if (find_tx_extra_field_by_type(tx_extra_fields, additional_tx_pub_keys)) + if (pk_index == 1) { - for (size_t i = 0; i < additional_tx_pub_keys.data.size(); ++i) + // additional tx pubkeys and derivations for multi-destination transfers involving one or more subaddresses + if (find_tx_extra_field_by_type(tx_extra_fields, additional_tx_pub_keys)) { - additional_derivations.push_back({}); - if (!hwdev.generate_key_derivation(additional_tx_pub_keys.data[i], keys.m_view_secret_key, additional_derivations.back())) + for (size_t i = 0; i < additional_tx_pub_keys.data.size(); ++i) { - MWARNING("Failed to generate key derivation from additional tx pubkey in " << txid << ", skipping"); - memcpy(&additional_derivations.back(), rct::identity().bytes, sizeof(crypto::key_derivation)); + additional_derivations.push_back({}); + if (!hwdev.generate_key_derivation(additional_tx_pub_keys.data[i], keys.m_view_secret_key, additional_derivations.back())) + { + MWARNING("Failed to generate key derivation from additional tx pubkey in " << txid << ", skipping"); + memcpy(&additional_derivations.back(), rct::identity().bytes, sizeof(crypto::key_derivation)); + } } } } @@ -1236,10 +1247,13 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote error::wallet_internal_error, "pk_index out of range of tx_cache_data"); is_out_data_ptr = &tx_cache_data.primary[pk_index - 1]; derivation = tx_cache_data.primary[pk_index - 1].derivation; - for (size_t n = 0; n < tx_cache_data.additional.size(); ++n) + if (pk_index == 1) { - additional_tx_pub_keys.data.push_back(tx_cache_data.additional[n].pkey); - additional_derivations.push_back(tx_cache_data.additional[n].derivation); + for (size_t n = 0; n < tx_cache_data.additional.size(); ++n) + { + additional_tx_pub_keys.data.push_back(tx_cache_data.additional[n].pkey); + additional_derivations.push_back(tx_cache_data.additional[n].derivation); + } } } From 34d4b798d44250f64fdcac61439a86afa8607c3b Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 23 Mar 2018 15:43:38 +0000 Subject: [PATCH 0166/1404] wallet2: use a gamma distribution to pick fake outs as per "An Empirical Analysis of Linkability in the Monero Blockchain", by Miller et al. --- src/wallet/wallet2.cpp | 143 ++++++++++++++++++++++++++++++++--------- src/wallet/wallet2.h | 2 +- 2 files changed, 115 insertions(+), 30 deletions(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 1a1537e621b..5b21cd2249a 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -2502,7 +2502,7 @@ bool wallet2::refresh(bool trusted_daemon, uint64_t & blocks_fetched, bool& rece return ok; } //---------------------------------------------------------------------------------------------------- -bool wallet2::get_output_distribution(uint64_t &start_height, std::vector &distribution) +bool wallet2::get_rct_distribution(uint64_t &start_height, std::vector &distribution) { uint32_t rpc_version; boost::optional result = m_node_rpc_proxy.get_rpc_version(rpc_version); @@ -6129,22 +6129,42 @@ void wallet2::get_outs(std::vector> bool is_shortly_after_segregation_fork = height >= segregation_fork_height && height < segregation_fork_height + SEGREGATION_FORK_VICINITY; bool is_after_segregation_fork = height >= segregation_fork_height; + // if we have at least one rct out, get the distribution, or fall back to the previous system + uint64_t rct_start_height; + std::vector rct_offsets; + bool has_rct = false; + for (size_t idx: selected_transfers) + if (m_transfers[idx].is_rct()) + { has_rct = true; break; } + const bool has_rct_distribution = has_rct && get_rct_distribution(rct_start_height, rct_offsets); + if (has_rct_distribution) + { + // check we're clear enough of rct start, to avoid corner cases below + THROW_WALLET_EXCEPTION_IF(rct_offsets.size() <= CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE, + error::get_output_distribution, "Not enough rct outputs"); + } + // get histogram for the amounts we need cryptonote::COMMAND_RPC_GET_OUTPUT_HISTOGRAM::request req_t = AUTO_VAL_INIT(req_t); cryptonote::COMMAND_RPC_GET_OUTPUT_HISTOGRAM::response resp_t = AUTO_VAL_INIT(resp_t); - m_daemon_rpc_mutex.lock(); + // request histogram for all outputs, except 0 if we have the rct distribution for(size_t idx: selected_transfers) - req_t.amounts.push_back(m_transfers[idx].is_rct() ? 0 : m_transfers[idx].amount()); - std::sort(req_t.amounts.begin(), req_t.amounts.end()); - auto end = std::unique(req_t.amounts.begin(), req_t.amounts.end()); - req_t.amounts.resize(std::distance(req_t.amounts.begin(), end)); - req_t.unlocked = true; - req_t.recent_cutoff = time(NULL) - RECENT_OUTPUT_ZONE; - bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_output_histogram", req_t, resp_t, m_http_client, rpc_timeout); - m_daemon_rpc_mutex.unlock(); - THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "transfer_selected"); - THROW_WALLET_EXCEPTION_IF(resp_t.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_output_histogram"); - THROW_WALLET_EXCEPTION_IF(resp_t.status != CORE_RPC_STATUS_OK, error::get_histogram_error, resp_t.status); + if (!m_transfers[idx].is_rct() || !has_rct_distribution) + req_t.amounts.push_back(m_transfers[idx].is_rct() ? 0 : m_transfers[idx].amount()); + if (!req_t.amounts.empty()) + { + std::sort(req_t.amounts.begin(), req_t.amounts.end()); + auto end = std::unique(req_t.amounts.begin(), req_t.amounts.end()); + req_t.amounts.resize(std::distance(req_t.amounts.begin(), end)); + req_t.unlocked = true; + req_t.recent_cutoff = time(NULL) - RECENT_OUTPUT_ZONE; + m_daemon_rpc_mutex.lock(); + bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_output_histogram", req_t, resp_t, m_http_client, rpc_timeout); + m_daemon_rpc_mutex.unlock(); + THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "transfer_selected"); + THROW_WALLET_EXCEPTION_IF(resp_t.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_output_histogram"); + THROW_WALLET_EXCEPTION_IF(resp_t.status != CORE_RPC_STATUS_OK, error::get_histogram_error, resp_t.status); + } // if we want to segregate fake outs pre or post fork, get distribution std::unordered_map> segregation_limit; @@ -6200,6 +6220,36 @@ void wallet2::get_outs(std::vector> COMMAND_RPC_GET_OUTPUTS_BIN::request req = AUTO_VAL_INIT(req); COMMAND_RPC_GET_OUTPUTS_BIN::response daemon_resp = AUTO_VAL_INIT(daemon_resp); + struct gamma_engine + { + typedef uint64_t result_type; + static constexpr result_type min() { return 0; } + static constexpr result_type max() { return std::numeric_limits::max(); } + result_type operator()() { return crypto::rand(); } + } engine; + static const double shape = 19.28/*16.94*/; + //static const double shape = m_testnet ? 17.02 : 17.28; + static const double scale = 1/1.61; + std::gamma_distribution gamma(shape, scale); + auto pick_gamma = [&]() + { + double x = gamma(engine); + x = exp(x); + uint64_t block_offset = x / DIFFICULTY_TARGET_V2; // this assumes constant target over the whole rct range + if (block_offset >= rct_offsets.size() - 1) + return std::numeric_limits::max(); // bad pick + block_offset = rct_offsets.size() - 2 - block_offset; + THROW_WALLET_EXCEPTION_IF(block_offset >= rct_offsets.size() - 1, error::wallet_internal_error, "Bad offset calculation"); + THROW_WALLET_EXCEPTION_IF(rct_offsets[block_offset + 1] < rct_offsets[block_offset], + error::get_output_distribution, "Decreasing offsets in rct distribution: " + + std::to_string(block_offset) + ": " + std::to_string(rct_offsets[block_offset]) + ", " + + std::to_string(block_offset + 1) + ": " + std::to_string(rct_offsets[block_offset + 1])); + uint64_t n_rct = rct_offsets[block_offset + 1] - rct_offsets[block_offset]; + if (n_rct == 0) + return rct_offsets[block_offset] ? rct_offsets[block_offset] - 1 : 0; + return rct_offsets[block_offset] + crypto::rand() % n_rct; + }; + size_t num_selected_transfers = 0; for(size_t idx: selected_transfers) { @@ -6210,6 +6260,7 @@ void wallet2::get_outs(std::vector> // request more for rct in base recent (locked) coinbases are picked, since they're locked for longer size_t requested_outputs_count = base_requested_outputs_count + (td.is_rct() ? CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW - CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE : 0); size_t start = req.outputs.size(); + bool use_histogram = amount != 0 || !has_rct_distribution; const bool output_is_pre_fork = td.m_block_height < segregation_fork_height; uint64_t num_outs = 0, num_recent_outs = 0; @@ -6265,26 +6316,41 @@ void wallet2::get_outs(std::vector> num_post_fork_outs = num_outs - segregation_limit[amount].first; } - LOG_PRINT_L1("" << num_outs << " unlocked outputs of size " << print_money(amount)); - THROW_WALLET_EXCEPTION_IF(num_outs == 0, error::wallet_internal_error, - "histogram reports no unlocked outputs for " + boost::lexical_cast(amount) + ", not even ours"); - THROW_WALLET_EXCEPTION_IF(num_recent_outs > num_outs, error::wallet_internal_error, - "histogram reports more recent outs than outs for " + boost::lexical_cast(amount)); + if (use_histogram) + { + LOG_PRINT_L1("" << num_outs << " unlocked outputs of size " << print_money(amount)); + THROW_WALLET_EXCEPTION_IF(num_outs == 0, error::wallet_internal_error, + "histogram reports no unlocked outputs for " + boost::lexical_cast(amount) + ", not even ours"); + THROW_WALLET_EXCEPTION_IF(num_recent_outs > num_outs, error::wallet_internal_error, + "histogram reports more recent outs than outs for " + boost::lexical_cast(amount)); + } + else + { + // the base offset of the first rct output in the first unlocked block (or the one to be if there's none) + num_outs = rct_offsets[rct_offsets.size() - CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE]; + LOG_PRINT_L1("" << num_outs << " unlocked rct outputs"); + THROW_WALLET_EXCEPTION_IF(num_outs == 0, error::wallet_internal_error, + "histogram reports no unlocked rct outputs, not even ours"); + } - // how many fake outs to draw on a pre-fork triangular distribution + // how many fake outs to draw on a pre-fork distribution size_t pre_fork_outputs_count = requested_outputs_count * pre_fork_num_out_ratio; size_t post_fork_outputs_count = requested_outputs_count * post_fork_num_out_ratio; // how many fake outs to draw otherwise size_t normal_output_count = requested_outputs_count - pre_fork_outputs_count - post_fork_outputs_count; - // X% of those outs are to be taken from recent outputs - size_t recent_outputs_count = normal_output_count * RECENT_OUTPUT_RATIO; - if (recent_outputs_count == 0) - recent_outputs_count = 1; // ensure we have at least one, if possible - if (recent_outputs_count > num_recent_outs) - recent_outputs_count = num_recent_outs; - if (td.m_global_output_index >= num_outs - num_recent_outs && recent_outputs_count > 0) - --recent_outputs_count; // if the real out is recent, pick one less recent fake out + size_t recent_outputs_count = 0; + if (use_histogram) + { + // X% of those outs are to be taken from recent outputs + recent_outputs_count = normal_output_count * RECENT_OUTPUT_RATIO; + if (recent_outputs_count == 0) + recent_outputs_count = 1; // ensure we have at least one, if possible + if (recent_outputs_count > num_recent_outs) + recent_outputs_count = num_recent_outs; + if (td.m_global_output_index >= num_outs - num_recent_outs && recent_outputs_count > 0) + --recent_outputs_count; // if the real out is recent, pick one less recent fake out + } LOG_PRINT_L1("Fake output makeup: " << requested_outputs_count << " requested: " << recent_outputs_count << " recent, " << pre_fork_outputs_count << " pre-fork, " << post_fork_outputs_count << " post-fork, " << (requested_outputs_count - recent_outputs_count - pre_fork_outputs_count - post_fork_outputs_count) << " full-chain"); @@ -6364,7 +6430,26 @@ void wallet2::get_outs(std::vector> uint64_t i; const char *type = ""; - if (num_found - 1 < recent_outputs_count) // -1 to account for the real one we seeded with + if (amount == 0 && has_rct_distribution) + { + // gamma distribution + if (num_found -1 < recent_outputs_count + pre_fork_outputs_count) + { + do i = pick_gamma(); while (i >= segregation_limit[amount].first); + type = "pre-fork gamma"; + } + else if (num_found -1 < recent_outputs_count + pre_fork_outputs_count + post_fork_outputs_count) + { + do i = pick_gamma(); while (i < segregation_limit[amount].first || i >= num_outs); + type = "post-fork gamma"; + } + else + { + do i = pick_gamma(); while (i >= num_outs); + type = "gamma"; + } + } + else if (num_found - 1 < recent_outputs_count) // -1 to account for the real one we seeded with { // triangular distribution over [a,b) with a=0, mode c=b=up_index_limit uint64_t r = crypto::rand() % ((uint64_t)1 << 53); @@ -6429,7 +6514,7 @@ void wallet2::get_outs(std::vector> // get the keys for those m_daemon_rpc_mutex.lock(); - r = epee::net_utils::invoke_http_bin("/get_outs.bin", req, daemon_resp, m_http_client, rpc_timeout); + bool r = epee::net_utils::invoke_http_bin("/get_outs.bin", req, daemon_resp, m_http_client, rpc_timeout); m_daemon_rpc_mutex.unlock(); THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_outs.bin"); THROW_WALLET_EXCEPTION_IF(daemon_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_outs.bin"); diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index d33d8258bbd..fb61aa6ea5d 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -1209,7 +1209,7 @@ namespace tools void cache_ringdb_key(); void clear_ringdb_key(); - bool get_output_distribution(uint64_t &start_height, std::vector &distribution); + bool get_rct_distribution(uint64_t &start_height, std::vector &distribution); uint64_t get_segregation_fork_height() const; From 22411149655cf12a636588b1b01c2c901637b238 Mon Sep 17 00:00:00 2001 From: iDunk5400 Date: Tue, 26 Jun 2018 23:46:17 +0200 Subject: [PATCH 0167/1404] blockchain_import: make sleep compile on Windows --- src/blockchain_utilities/blockchain_import.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/blockchain_utilities/blockchain_import.cpp b/src/blockchain_utilities/blockchain_import.cpp index a58257719bb..9e2c97bd0ce 100644 --- a/src/blockchain_utilities/blockchain_import.cpp +++ b/src/blockchain_utilities/blockchain_import.cpp @@ -33,6 +33,7 @@ #include #include +#include #include "misc_log_ex.h" #include "bootstrap_file.h" #include "bootstrap_serialization.h" From 9c211b50ded6be87631d14523996e8539a684ba8 Mon Sep 17 00:00:00 2001 From: HomDx Date: Tue, 3 Jul 2018 19:44:03 +0300 Subject: [PATCH 0168/1404] docker: update to new versions of dependencies --- Dockerfile | 37 +++++++++++++++++++++++++++---------- README.md | 3 +++ 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/Dockerfile b/Dockerfile index e36e2082652..0decb4fdeb8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -17,14 +17,28 @@ RUN set -ex && \ curl \ libtool-bin \ autoconf \ - automake + automake \ + bzip2 WORKDIR /usr/local +#Cmake +ARG CMAKE_VERSION=3.11.4 +ARG CMAKE_VERSION_DOT=v3.11 +ARG CMAKE_HASH=8f864e9f78917de3e1483e256270daabc4a321741592c5b36af028e72bff87f5 +RUN set -ex \ + && curl -s -O https://cmake.org/files/${CMAKE_VERSION_DOT}/cmake-${CMAKE_VERSION}.tar.gz \ + && echo "${CMAKE_HASH} cmake-${CMAKE_VERSION}.tar.gz" | sha256sum -c \ + && tar -xzf cmake-${CMAKE_VERSION}.tar.gz \ + && cd cmake-${CMAKE_VERSION} \ + && ./configure \ + && make \ + && make install + ## Boost -ARG BOOST_VERSION=1_66_0 -ARG BOOST_VERSION_DOT=1.66.0 -ARG BOOST_HASH=5721818253e6a0989583192f96782c4a98eb6204965316df9f5ad75819225ca9 +ARG BOOST_VERSION=1_67_0 +ARG BOOST_VERSION_DOT=1.67.0 +ARG BOOST_HASH=2684c972994ee57fc5632e03bf044746f6eb45d4920c343937a465fd67a5adba RUN set -ex \ && curl -s -L -o boost_${BOOST_VERSION}.tar.bz2 https://dl.bintray.com/boostorg/release/${BOOST_VERSION_DOT}/source/boost_${BOOST_VERSION}.tar.bz2 \ && echo "${BOOST_HASH} boost_${BOOST_VERSION}.tar.bz2" | sha256sum -c \ @@ -35,21 +49,22 @@ RUN set -ex \ ENV BOOST_ROOT /usr/local/boost_${BOOST_VERSION} # OpenSSL -ARG OPENSSL_VERSION=1.0.2n -ARG OPENSSL_HASH=370babb75f278c39e0c50e8c4e7493bc0f18db6867478341a832a982fd15a8fe +ARG OPENSSL_VERSION=1.1.0h +ARG OPENSSL_HASH=5835626cde9e99656585fc7aaa2302a73a7e1340bf8c14fd635a62c66802a517 RUN set -ex \ && curl -s -O https://www.openssl.org/source/openssl-${OPENSSL_VERSION}.tar.gz \ && echo "${OPENSSL_HASH} openssl-${OPENSSL_VERSION}.tar.gz" | sha256sum -c \ && tar -xzf openssl-${OPENSSL_VERSION}.tar.gz \ && cd openssl-${OPENSSL_VERSION} \ && ./Configure linux-x86_64 no-shared --static -fPIC \ - && make build_crypto build_ssl \ + && make build_generated \ + && make libcrypto.a \ && make install ENV OPENSSL_ROOT_DIR=/usr/local/openssl-${OPENSSL_VERSION} # ZMQ -ARG ZMQ_VERSION=v4.2.3 -ARG ZMQ_HASH=3226b8ebddd9c6c738ba42986822c26418a49afb +ARG ZMQ_VERSION=v4.2.5 +ARG ZMQ_HASH=d062edd8c142384792955796329baf1e5a3377cd RUN set -ex \ && git clone https://github.com/zeromq/libzmq.git -b ${ZMQ_VERSION} \ && cd libzmq \ @@ -61,9 +76,10 @@ RUN set -ex \ && ldconfig # zmq.hpp +ARG CPPZMQ_VERSION=v4.2.3 ARG CPPZMQ_HASH=6aa3ab686e916cb0e62df7fa7d12e0b13ae9fae6 RUN set -ex \ - && git clone https://github.com/zeromq/cppzmq.git -b ${ZMQ_VERSION} \ + && git clone https://github.com/zeromq/cppzmq.git -b ${CPPZMQ_VERSION} \ && cd cppzmq \ && test `git rev-parse HEAD` = ${CPPZMQ_HASH} || exit 1 \ && mv *.hpp /usr/local/include @@ -127,3 +143,4 @@ EXPOSE 18080 EXPOSE 18081 ENTRYPOINT ["monerod", "--p2p-bind-ip=0.0.0.0", "--p2p-bind-port=18080", "--rpc-bind-ip=0.0.0.0", "--rpc-bind-port=18081", "--non-interactive", "--confirm-external-bind"] + diff --git a/README.md b/README.md index cc715d399ec..50a884202e4 100644 --- a/README.md +++ b/README.md @@ -527,6 +527,9 @@ Installing a snap is very quick. Snaps are secure. They are isolated with all of # or in background docker run -it -d -v /monero/chain:/root/.bitmonero -v /monero/wallet:/wallet -p 18080:18080 monero +* The build needs 3 GB space. +* Wait one hour or more + Packaging for your favorite distribution would be a welcome contribution! ## Running monerod From 42f3b7cbcab156e89720e466043bfc89f9ed2e96 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 3 Jul 2018 00:26:13 +0100 Subject: [PATCH 0169/1404] http_protocol_handler: catch invalid numbers when parsing --- .../epee/include/net/http_protocol_handler.inl | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/contrib/epee/include/net/http_protocol_handler.inl b/contrib/epee/include/net/http_protocol_handler.inl index 0bdba0bfe0b..dc2c06972a0 100644 --- a/contrib/epee/include/net/http_protocol_handler.inl +++ b/contrib/epee/include/net/http_protocol_handler.inl @@ -328,8 +328,10 @@ namespace net_utils inline bool analize_http_method(const boost::smatch& result, http::http_method& method, int& http_ver_major, int& http_ver_minor) { CHECK_AND_ASSERT_MES(result[0].matched, false, "simple_http_connection_handler::analize_http_method() assert failed..."); - http_ver_major = boost::lexical_cast(result[11]); - http_ver_minor = boost::lexical_cast(result[12]); + if (!boost::conversion::try_lexical_convert(result[11], http_ver_major)) + return false; + if (!boost::conversion::try_lexical_convert(result[12], http_ver_minor)) + return false; if(result[3].matched) method = http::http_method_options; @@ -357,7 +359,12 @@ namespace net_utils boost::smatch result; if(boost::regex_search(m_cache, result, rexp_match_command_line, boost::match_default) && result[0].matched) { - analize_http_method(result, m_query_info.m_http_method, m_query_info.m_http_ver_hi, m_query_info.m_http_ver_hi); + if (!analize_http_method(result, m_query_info.m_http_method, m_query_info.m_http_ver_hi, m_query_info.m_http_ver_hi)) + { + m_state = http_state_error; + MERROR("Failed to analyze method"); + return false; + } m_query_info.m_URI = result[10]; if (!parse_uri(m_query_info.m_URI, m_query_info.m_uri_content)) { @@ -554,7 +561,8 @@ namespace net_utils if(!(boost::regex_search( str, result, rexp_mach_field, boost::match_default) && result[0].matched)) return false; - len = boost::lexical_cast(result[0]); + try { len = boost::lexical_cast(result[0]); } + catch(...) { return false; } return true; } //----------------------------------------------------------------------------------- From 0a4a7da35cd043411b5544440f996f588be1e892 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 3 Jul 2018 00:27:08 +0100 Subject: [PATCH 0170/1404] http_protocol_handler: fix HTTP/x.y parsing It was accepting any character for the dot (yeah, massive big I know) --- contrib/epee/include/net/http_protocol_handler.inl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/epee/include/net/http_protocol_handler.inl b/contrib/epee/include/net/http_protocol_handler.inl index dc2c06972a0..76db5346f2d 100644 --- a/contrib/epee/include/net/http_protocol_handler.inl +++ b/contrib/epee/include/net/http_protocol_handler.inl @@ -353,7 +353,7 @@ namespace net_utils template bool simple_http_connection_handler::handle_invoke_query_line() { - STATIC_REGEXP_EXPR_1(rexp_match_command_line, "^(((OPTIONS)|(GET)|(HEAD)|(POST)|(PUT)|(DELETE)|(TRACE)) (\\S+) HTTP/(\\d+).(\\d+))\r?\n", boost::regex::icase | boost::regex::normal); + STATIC_REGEXP_EXPR_1(rexp_match_command_line, "^(((OPTIONS)|(GET)|(HEAD)|(POST)|(PUT)|(DELETE)|(TRACE)) (\\S+) HTTP/(\\d+)\\.(\\d+))\r?\n", boost::regex::icase | boost::regex::normal); // 123 4 5 6 7 8 9 10 11 12 //size_t match_len = 0; boost::smatch result; From 96f8c62dc43ae6c8f574dd004064ad56b38a3214 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 4 Jul 2018 23:59:06 +0100 Subject: [PATCH 0171/1404] README: remove mentions of external miniupnpc dependency --- README.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 50a884202e4..706ff8e4073 100644 --- a/README.md +++ b/README.md @@ -139,7 +139,6 @@ library archives (`.a`). | libzmq | 3.0.0 | NO | `libzmq3-dev` | `zeromq` | `cppzmq-devel` | NO | ZeroMQ library | | libunbound | 1.4.16 | YES | `libunbound-dev` | `unbound` | `unbound-devel` | NO | DNS resolver | | libsodium | ? | NO | `libsodium-dev` | ? | `libsodium-devel` | NO | libsodium | -| libminiupnpc | 2.0 | YES | `libminiupnpc-dev` | `miniupnpc` | `miniupnpc-devel` | YES | NAT punching | | libunwind | any | NO | `libunwind8-dev` | `libunwind` | `libunwind-devel` | YES | Stack traces | | liblzma | any | NO | `liblzma-dev` | `xz` | `xz-devel` | YES | For libunwind | | libreadline | 6.3.0 | NO | `libreadline6-dev` | `readline` | `readline-devel` | YES | Input editing | @@ -155,7 +154,7 @@ library archives (`.a`). build the library binary manually. This can be done with the following command ```sudo apt-get install libgtest-dev && cd /usr/src/gtest && sudo cmake . && sudo make && sudo mv libg* /usr/lib/ ``` Debian / Ubuntu one liner for all dependencies -``` sudo apt update && sudo apt install build-essential cmake pkg-config libboost-all-dev libssl-dev libzmq3-dev libunbound-dev libsodium-dev libminiupnpc-dev libunwind8-dev liblzma-dev libreadline6-dev libldns-dev libexpat1-dev doxygen graphviz libpcsclite-dev ``` +``` sudo apt update && sudo apt install build-essential cmake pkg-config libboost-all-dev libssl-dev libzmq3-dev libunbound-dev libsodium-dev libunwind8-dev liblzma-dev libreadline6-dev libldns-dev libexpat1-dev doxygen graphviz libpcsclite-dev ``` ### Cloning the repository @@ -373,7 +372,7 @@ We expect to add Monero into the ports tree in the near future, which will aid i This has been tested on OpenBSD 5.8. -You will need to add a few packages to your system. `pkg_add db cmake gcc gcc-libs g++ miniupnpc gtest`. +You will need to add a few packages to your system. `pkg_add db cmake gcc gcc-libs g++ gtest`. The doxygen and graphviz packages are optional and require the xbase set. @@ -386,7 +385,7 @@ To build: `env CC=egcc CXX=eg++ CPP=ecpp DEVELOPER_LOCAL_TOOLS=1 BOOST_ROOT=/pat #### OpenBSD >= 6.2 -You will need to add a few packages to your system. `pkg_add cmake miniupnpc zeromq libiconv`. +You will need to add a few packages to your system. `pkg_add cmake zeromq libiconv`. The doxygen and graphviz packages are optional and require the xbase set. From 61caab8a8c8a4e4239216c861c4d1eba5bcfd76e Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 4 Jul 2018 22:17:20 +0100 Subject: [PATCH 0172/1404] crypto: remove slight bias in key generation due to modulo --- src/crypto/crypto.cpp | 28 +++++++++++++++++--- src/crypto/crypto.h | 1 + src/ringct/rctOps.cpp | 10 +++---- tests/unit_tests/CMakeLists.txt | 1 + tests/unit_tests/random.cpp | 47 +++++++++++++++++++++++++++++++++ 5 files changed, 77 insertions(+), 10 deletions(-) create mode 100644 tests/unit_tests/random.cpp diff --git a/src/crypto/crypto.cpp b/src/crypto/crypto.cpp index f4ef751d388..9eafa0e2878 100644 --- a/src/crypto/crypto.cpp +++ b/src/crypto/crypto.cpp @@ -93,6 +93,29 @@ namespace crypto { generate_random_bytes_not_thread_safe(N, bytes); } + static inline bool less32(const unsigned char *k0, const unsigned char *k1) + { + for (int n = 31; n >= 0; --n) + { + if (k0[n] < k1[n]) + return true; + if (k0[n] > k1[n]) + return false; + } + return false; + } + + void random32_unbiased(unsigned char *bytes) + { + // l = 2^252 + 27742317777372353535851937790883648493. + // it fits 15 in 32 bytes + static const unsigned char limit[32] = { 0xe3, 0x6a, 0x67, 0x72, 0x8b, 0xce, 0x13, 0x29, 0x8f, 0x30, 0x82, 0x8c, 0x0b, 0xa4, 0x10, 0x39, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0 }; + do + { + generate_random_bytes_thread_safe(32, bytes); + } while (!less32(bytes, limit)); // should be good about 15/16 of the time + sc_reduce32(bytes); + } /* generate a random 32-byte (256-bit) integer and copy it to res */ static inline void random_scalar_not_thread_safe(ec_scalar &res) { unsigned char tmp[64]; @@ -101,10 +124,7 @@ namespace crypto { memcpy(&res, tmp, 32); } static inline void random_scalar(ec_scalar &res) { - unsigned char tmp[64]; - generate_random_bytes_thread_safe(64, tmp); - sc_reduce(tmp); - memcpy(&res, tmp, 32); + random32_unbiased((unsigned char*)res.data); } void hash_to_scalar(const void *data, size_t length, ec_scalar &res) { diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h index 9ea0f2ec0cf..449af8f6d6e 100644 --- a/src/crypto/crypto.h +++ b/src/crypto/crypto.h @@ -99,6 +99,7 @@ namespace crypto { #pragma pack(pop) void hash_to_scalar(const void *data, size_t length, ec_scalar &res); + void random32_unbiased(unsigned char *bytes); static_assert(sizeof(ec_point) == 32 && sizeof(ec_scalar) == 32 && sizeof(public_key) == 32 && sizeof(secret_key) == 32 && diff --git a/src/ringct/rctOps.cpp b/src/ringct/rctOps.cpp index 68cc4312803..50693bad743 100644 --- a/src/ringct/rctOps.cpp +++ b/src/ringct/rctOps.cpp @@ -62,14 +62,13 @@ namespace rct { //generates a random scalar which can be used as a secret key or mask void skGen(key &sk) { - sk = crypto::rand(); - sc_reduce32(sk.bytes); + random32_unbiased(sk.bytes); } //generates a random scalar which can be used as a secret key or mask key skGen() { - key sk = crypto::rand(); - sc_reduce32(sk.bytes); + key sk; + skGen(sk); return sk; } @@ -79,9 +78,8 @@ namespace rct { CHECK_AND_ASSERT_THROW_MES(rows > 0, "0 keys requested"); keyV rv(rows); size_t i = 0; - crypto::rand(rows * sizeof(key), (uint8_t*)&rv[0]); for (i = 0 ; i < rows ; i++) { - sc_reduce32(rv[i].bytes); + skGen(rv[i]); } return rv; } diff --git a/tests/unit_tests/CMakeLists.txt b/tests/unit_tests/CMakeLists.txt index 6d79ba74b37..06112d3ee86 100644 --- a/tests/unit_tests/CMakeLists.txt +++ b/tests/unit_tests/CMakeLists.txt @@ -55,6 +55,7 @@ set(unit_tests_sources mul_div.cpp multisig.cpp parse_amount.cpp + random.cpp serialization.cpp sha256.cpp slow_memmem.cpp diff --git a/tests/unit_tests/random.cpp b/tests/unit_tests/random.cpp new file mode 100644 index 00000000000..7653453cd8d --- /dev/null +++ b/tests/unit_tests/random.cpp @@ -0,0 +1,47 @@ +// Copyright (c) 2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "gtest/gtest.h" + +#include "crypto/crypto.h" + +extern "C" { +#include "crypto/crypto-ops.h" +} + +TEST(random32_unbiased, less_than_order) +{ + unsigned char tmp[32], tmp2[32]; + for (int i = 0; i < 1000; ++i) + { + crypto::random32_unbiased(tmp); + memcpy(tmp2, tmp, 32); + sc_reduce32(tmp2); + ASSERT_EQ(memcmp(tmp, tmp2, 32), 0); + } +} From e64792208057c5f37cbc8c44c9bcdbe13ec4ab41 Mon Sep 17 00:00:00 2001 From: Riccardo Spagni Date: Fri, 6 Jul 2018 07:57:00 +0200 Subject: [PATCH 0173/1404] bump version to 12.3 --- src/version.cpp.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/version.cpp.in b/src/version.cpp.in index a03da7889c3..55ba51f5013 100644 --- a/src/version.cpp.in +++ b/src/version.cpp.in @@ -1,5 +1,5 @@ #define DEF_MONERO_VERSION_TAG "@VERSIONTAG@" -#define DEF_MONERO_VERSION "0.12.2.0-master" +#define DEF_MONERO_VERSION "0.12.3.0-master" #define DEF_MONERO_RELEASE_NAME "Lithium Luna" #define DEF_MONERO_VERSION_FULL DEF_MONERO_VERSION "-" DEF_MONERO_VERSION_TAG From e2311db717afe271261c45c75749970bee5eab38 Mon Sep 17 00:00:00 2001 From: anonimal Date: Fri, 6 Jul 2018 21:35:07 +0000 Subject: [PATCH 0174/1404] Build: bump rapidjson to 129d19b Includes various rapidjson fixes. --- external/rapidjson | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/external/rapidjson b/external/rapidjson index af223d44f4e..129d19ba7f4 160000 --- a/external/rapidjson +++ b/external/rapidjson @@ -1 +1 @@ -Subproject commit af223d44f4e8d3772cb1ac0ce8bc2a132b51717f +Subproject commit 129d19ba7f496df5e33658527a7158c79b99c21c From 78a6690a6fa844fbd087262da8895f68e6ccf1ea Mon Sep 17 00:00:00 2001 From: anonimal Date: Fri, 6 Jul 2018 21:37:37 +0000 Subject: [PATCH 0175/1404] Build: bump unbound to 1.7.3 --- external/unbound | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/external/unbound b/external/unbound index 193bdc4ee3f..d3724dfa553 160000 --- a/external/unbound +++ b/external/unbound @@ -1 +1 @@ -Subproject commit 193bdc4ee3fe2b0d17e547e86512528c2614483a +Subproject commit d3724dfa553429d368c27aef160f02f5e8b8075f From 4e409be8878e9341b1d1471b6b27515430189a73 Mon Sep 17 00:00:00 2001 From: anonimal Date: Fri, 6 Jul 2018 21:38:52 +0000 Subject: [PATCH 0176/1404] Build: bump miniupnp to 2.1 --- external/miniupnp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/external/miniupnp b/external/miniupnp index 6a63f995495..6b9b73a567e 160000 --- a/external/miniupnp +++ b/external/miniupnp @@ -1 +1 @@ -Subproject commit 6a63f9954959119568fbc4af57d7b491b9428d87 +Subproject commit 6b9b73a567e351b844f96c077f7b752ea92e298a From 20eb1921623e31266649903e24b1e10fdda4153f Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 7 Jul 2018 11:09:59 +0100 Subject: [PATCH 0177/1404] simplewallet: reword "seed encryption" to "seed offset" avoids people thinking it's somehow a generic AE system --- src/simplewallet/simplewallet.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 16866a80d2c..08f0a20f428 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -628,7 +628,7 @@ bool simple_wallet::print_seed(bool encrypted) epee::wipeable_string seed_pass; if (encrypted) { - auto pwd_container = password_prompter(tr("Enter optional seed encryption passphrase, empty to see raw seed"), true); + auto pwd_container = password_prompter(tr("Enter optional seed offset passphrase, empty to see raw seed"), true); if (std::cin.eof() || !pwd_container) return true; seed_pass = pwd_container->password(); @@ -2775,7 +2775,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) } } - auto pwd_container = password_prompter(tr("Enter seed encryption passphrase, empty if none"), false); + auto pwd_container = password_prompter(tr("Enter seed offset passphrase, empty if none"), false); if (std::cin.eof() || !pwd_container) return false; epee::wipeable_string seed_pass = pwd_container->password(); From 639ca3b1fa9c153a99f1d4e9bb488bd02d282e46 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 6 Jul 2018 19:10:39 +0100 Subject: [PATCH 0178/1404] core_tests: add --filter to select which tests to run --- src/common/util.cpp | 19 +++++++++++++++++++ src/common/util.h | 2 ++ tests/core_tests/chaingen.h | 1 + tests/core_tests/chaingen_main.cpp | 6 ++++++ tests/performance_tests/main.cpp | 21 +-------------------- 5 files changed, 29 insertions(+), 20 deletions(-) diff --git a/src/common/util.cpp b/src/common/util.cpp index 7d9d7b408ea..f644c573c6d 100644 --- a/src/common/util.cpp +++ b/src/common/util.cpp @@ -919,4 +919,23 @@ std::string get_nix_version_display_string() return {}; } } + + std::string glob_to_regex(const std::string &val) + { + std::string newval; + + bool escape = false; + for (char c: val) + { + if (c == '*') + newval += escape ? "*" : ".*"; + else if (c == '?') + newval += escape ? "?" : "."; + else if (c == '\\') + newval += '\\', escape = !escape; + else + newval += c; + } + return newval; + } } diff --git a/src/common/util.h b/src/common/util.h index a57a85fee65..6ec901e7f5f 100644 --- a/src/common/util.h +++ b/src/common/util.h @@ -231,4 +231,6 @@ namespace tools bool is_hdd(const char *path); boost::optional> parse_subaddress_lookahead(const std::string& str); + + std::string glob_to_regex(const std::string &val); } diff --git a/tests/core_tests/chaingen.h b/tests/core_tests/chaingen.h index 201da4fa01a..bbc9edd1987 100644 --- a/tests/core_tests/chaingen.h +++ b/tests/core_tests/chaingen.h @@ -664,6 +664,7 @@ inline bool do_replay_file(const std::string& filename) } #define GENERATE_AND_PLAY(genclass) \ + if (filter.empty() || boost::regex_match(std::string(#genclass), match, boost::regex(filter))) \ { \ std::vector events; \ ++tests_count; \ diff --git a/tests/core_tests/chaingen_main.cpp b/tests/core_tests/chaingen_main.cpp index 6a1992cd12e..c3165507076 100644 --- a/tests/core_tests/chaingen_main.cpp +++ b/tests/core_tests/chaingen_main.cpp @@ -30,6 +30,7 @@ #include "chaingen.h" #include "chaingen_tests_list.h" +#include "common/util.h" #include "common/command_line.h" #include "transaction_tests.h" @@ -42,6 +43,7 @@ namespace const command_line::arg_descriptor arg_play_test_data = {"play_test_data", ""}; const command_line::arg_descriptor arg_generate_and_play_test_data = {"generate_and_play_test_data", ""}; const command_line::arg_descriptor arg_test_transactions = {"test_transactions", ""}; + const command_line::arg_descriptor arg_filter = { "filter", "Regular expression filter for which tests to run" }; } int main(int argc, char* argv[]) @@ -61,6 +63,7 @@ int main(int argc, char* argv[]) command_line::add_arg(desc_options, arg_play_test_data); command_line::add_arg(desc_options, arg_generate_and_play_test_data); command_line::add_arg(desc_options, arg_test_transactions); + command_line::add_arg(desc_options, arg_filter); po::variables_map vm; bool r = command_line::handle_error_helper(desc_options, [&]() @@ -78,6 +81,9 @@ int main(int argc, char* argv[]) return 0; } + const std::string filter = tools::glob_to_regex(command_line::get_arg(vm, arg_filter)); + boost::smatch match; + size_t tests_count = 0; std::vector failed_tests; std::string tests_folder = command_line::get_arg(vm, arg_test_data_path); diff --git a/tests/performance_tests/main.cpp b/tests/performance_tests/main.cpp index 902bd89c060..bc3622ea8d5 100644 --- a/tests/performance_tests/main.cpp +++ b/tests/performance_tests/main.cpp @@ -54,25 +54,6 @@ namespace po = boost::program_options; -std::string glob_to_regex(const std::string &val) -{ - std::string newval; - - bool escape = false; - for (char c: val) - { - if (c == '*') - newval += escape ? "*" : ".*"; - else if (c == '?') - newval += escape ? "?" : "."; - else if (c == '\\') - newval += '\\', escape = !escape; - else - newval += c; - } - return newval; -} - int main(int argc, char** argv) { TRY_ENTRY(); @@ -97,7 +78,7 @@ int main(int argc, char** argv) if (!r) return 1; - const std::string filter = glob_to_regex(command_line::get_arg(vm, arg_filter)); + const std::string filter = tools::glob_to_regex(command_line::get_arg(vm, arg_filter)); performance_timer timer; timer.start(); From 8c0523771feba590b4bcee2616db5685b1ccb2a1 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 6 Jul 2018 19:11:01 +0100 Subject: [PATCH 0179/1404] blockchain: cache next block difficulty after adding a block It's not 100% certain it'll be needed, but it avoids getinfo needing the blockchain lock and potentially blocking --- src/cryptonote_core/blockchain.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 73ce98366f5..fc16138d6c7 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -3653,6 +3653,7 @@ bool Blockchain::handle_block_to_main_chain(const block& bl, const crypto::hash& // appears to be a NOP *and* is called elsewhere. wat? m_tx_pool.on_blockchain_inc(new_height, id); + get_difficulty_for_next_block(); // just to cache it return true; } From fc39d3b23ce6762834b58b52b204f080b6b3288f Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 8 Jul 2018 11:01:13 +0100 Subject: [PATCH 0180/1404] wallet2: ensure outputs are processed only once This should be proof against any way one might get to multiple processing, such as generating the same derivation from the same pubkey, etc --- src/wallet/wallet2.cpp | 31 +++++++++++++++++-------------- src/wallet/wallet2.h | 1 + 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index d993716730a..40a43b8e76a 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -1057,6 +1057,16 @@ void wallet2::check_acc_out_precomp(const tx_out &o, const crypto::key_derivatio tx_scan_info.error = false; } //---------------------------------------------------------------------------------------------------- +void wallet2::check_acc_out_precomp_once(const tx_out &o, const crypto::key_derivation &derivation, const std::vector &additional_derivations, size_t i, const is_out_data *is_out_data, tx_scan_info_t &tx_scan_info, bool &already_seen) const +{ + tx_scan_info.received = boost::none; + if (already_seen) + return; + check_acc_out_precomp(o, derivation, additional_derivations, i, is_out_data, tx_scan_info); + if (tx_scan_info.received) + already_seen = true; +} +//---------------------------------------------------------------------------------------------------- static uint64_t decodeRct(const rct::rctSig & rv, const crypto::key_derivation &derivation, unsigned int i, rct::key & mask, hw::device &hwdev) { crypto::secret_key scalar1; @@ -1173,7 +1183,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote // Don't try to extract tx public key if tx has no ouputs size_t pk_index = 0; std::vector tx_scan_info(tx.vout.size()); - std::unordered_set public_keys_seen; + std::deque output_found(tx.vout.size(), false); while (!tx.vout.empty()) { // if tx.vout is not empty, we loop through all tx pubkeys @@ -1194,13 +1204,6 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote error::wallet_internal_error, "tx_cache_data is out of sync"); } - if (public_keys_seen.find(pub_key_field.pub_key) != public_keys_seen.end()) - { - MWARNING("The same transaction pubkey is present more than once, ignoring extra instance"); - continue; - } - public_keys_seen.insert(pub_key_field.pub_key); - int num_vouts_received = 0; tx_pub_key = pub_key_field.pub_key; tools::threadpool& tpool = tools::threadpool::getInstance(); @@ -1264,7 +1267,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote } else if (miner_tx && m_refresh_type == RefreshOptimizeCoinbase) { - check_acc_out_precomp(tx.vout[0], derivation, additional_derivations, 0, is_out_data_ptr, tx_scan_info[0]); + check_acc_out_precomp_once(tx.vout[0], derivation, additional_derivations, 0, is_out_data_ptr, tx_scan_info[0], output_found[0]); THROW_WALLET_EXCEPTION_IF(tx_scan_info[0].error, error::acc_outs_lookup_error, tx, tx_pub_key, m_account.get_keys()); // this assumes that the miner tx pays a single address @@ -1274,8 +1277,8 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote // the first one was already checked for (size_t i = 1; i < tx.vout.size(); ++i) { - tpool.submit(&waiter, boost::bind(&wallet2::check_acc_out_precomp, this, std::cref(tx.vout[i]), std::cref(derivation), std::cref(additional_derivations), i, - std::cref(is_out_data_ptr), std::ref(tx_scan_info[i])), true); + tpool.submit(&waiter, boost::bind(&wallet2::check_acc_out_precomp_once, this, std::cref(tx.vout[i]), std::cref(derivation), std::cref(additional_derivations), i, + std::cref(is_out_data_ptr), std::ref(tx_scan_info[i]), std::ref(output_found[i])), true); } waiter.wait(&tpool); // then scan all outputs from 0 @@ -1297,8 +1300,8 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote { for (size_t i = 0; i < tx.vout.size(); ++i) { - tpool.submit(&waiter, boost::bind(&wallet2::check_acc_out_precomp, this, std::cref(tx.vout[i]), std::cref(derivation), std::cref(additional_derivations), i, - std::cref(is_out_data_ptr), std::ref(tx_scan_info[i])), true); + tpool.submit(&waiter, boost::bind(&wallet2::check_acc_out_precomp_once, this, std::cref(tx.vout[i]), std::cref(derivation), std::cref(additional_derivations), i, + std::cref(is_out_data_ptr), std::ref(tx_scan_info[i]), std::ref(output_found[i])), true); } waiter.wait(&tpool); @@ -1319,7 +1322,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote { for (size_t i = 0; i < tx.vout.size(); ++i) { - check_acc_out_precomp(tx.vout[i], derivation, additional_derivations, i, is_out_data_ptr, tx_scan_info[i]); + check_acc_out_precomp_once(tx.vout[i], derivation, additional_derivations, i, is_out_data_ptr, tx_scan_info[i], output_found[i]); THROW_WALLET_EXCEPTION_IF(tx_scan_info[i].error, error::acc_outs_lookup_error, tx, tx_pub_key, m_account.get_keys()); if (tx_scan_info[i].received) { diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index d33d8258bbd..4d22b69152c 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -1181,6 +1181,7 @@ namespace tools crypto::hash get_payment_id(const pending_tx &ptx) const; void check_acc_out_precomp(const cryptonote::tx_out &o, const crypto::key_derivation &derivation, const std::vector &additional_derivations, size_t i, tx_scan_info_t &tx_scan_info) const; void check_acc_out_precomp(const cryptonote::tx_out &o, const crypto::key_derivation &derivation, const std::vector &additional_derivations, size_t i, const is_out_data *is_out_data, tx_scan_info_t &tx_scan_info) const; + void check_acc_out_precomp_once(const cryptonote::tx_out &o, const crypto::key_derivation &derivation, const std::vector &additional_derivations, size_t i, const is_out_data *is_out_data, tx_scan_info_t &tx_scan_info, bool &already_seen) const; void parse_block_round(const cryptonote::blobdata &blob, cryptonote::block &bl, crypto::hash &bl_id, bool &error) const; uint64_t get_upper_transaction_size_limit() const; std::vector get_unspent_amounts_vector() const; From 8d578f1f2df872be93d6fd2f3c4a4aef40d9faa3 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 10 Jul 2018 14:37:56 +0100 Subject: [PATCH 0181/1404] memwipe: don't call the workhorse for 0 bytes Some of them don't like it --- contrib/epee/src/memwipe.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/contrib/epee/src/memwipe.c b/contrib/epee/src/memwipe.c index e3a2f76c8fd..c2a26c392d7 100644 --- a/contrib/epee/src/memwipe.c +++ b/contrib/epee/src/memwipe.c @@ -50,7 +50,7 @@ void *memwipe(void *ptr, size_t n) { - if (memset_s(ptr, n, 0, n)) + if (n > 0 && memset_s(ptr, n, 0, n)) { #ifdef NDEBUG fprintf(stderr, "Error: memset_s failed\n"); @@ -67,7 +67,8 @@ void *memwipe(void *ptr, size_t n) void *memwipe(void *ptr, size_t n) { - explicit_bzero(ptr, n); + if (n > 0) + explicit_bzero(ptr, n); SCARECROW return ptr; } @@ -105,7 +106,8 @@ static void memory_cleanse(void *ptr, size_t len) void *memwipe(void *ptr, size_t n) { - memory_cleanse(ptr, n); + if (n > 0) + memory_cleanse(ptr, n); SCARECROW return ptr; } From ee31383a5209883cb6f22fd248ea5956718c66b2 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 11 Jul 2018 18:14:22 +0100 Subject: [PATCH 0182/1404] db_lmdb: don't sync a read only DB This would only throw --- src/blockchain_db/lmdb/db_lmdb.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp index 367bfa49e79..034574349d8 100644 --- a/src/blockchain_db/lmdb/db_lmdb.cpp +++ b/src/blockchain_db/lmdb/db_lmdb.cpp @@ -1391,6 +1391,9 @@ void BlockchainLMDB::sync() LOG_PRINT_L3("BlockchainLMDB::" << __func__); check_open(); + if (is_read_only()) + return; + // Does nothing unless LMDB environment was opened with MDB_NOSYNC or in part // MDB_NOMETASYNC. Force flush to be synchronous. if (auto result = mdb_env_sync(m_env, true)) From 2951436704e931e25537ca99e994186aa02c7ba6 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 6 Jul 2018 18:14:08 +0100 Subject: [PATCH 0183/1404] wallet: warn when payment IDs are used Subaddresses are better for privacy --- src/simplewallet/simplewallet.cpp | 19 +++++++++++++++++++ src/wallet/wallet2.cpp | 6 ++---- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 16866a80d2c..eb30dd3f1f2 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -3955,6 +3955,24 @@ void simple_wallet::on_money_received(uint64_t height, const crypto::hash &txid, tr("txid ") << txid << ", " << print_money(amount) << ", " << tr("idx ") << subaddr_index; + + const uint64_t warn_height = m_wallet->nettype() == TESTNET ? 1000000 : m_wallet->nettype() == STAGENET ? 50000 : 1650000; + if (height >= warn_height) + { + std::vector tx_extra_fields; + parse_tx_extra(tx.extra, tx_extra_fields); // failure ok + tx_extra_nonce extra_nonce; + if (find_tx_extra_field_by_type(tx_extra_fields, extra_nonce)) + { + crypto::hash8 payment_id8 = crypto::null_hash8; + if (get_encrypted_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id8)) + message_writer() << + tr("NOTE: this transaction uses an encrypted payment ID: consider using subaddresses instead"); + else + message_writer(console_color_red, false) << + tr("WARNING: this transaction uses an unencrypted payment ID: consider using subaddresses instead"); + } + } if (m_auto_refresh_refreshing) m_cmd_binder.print_prompt(); else @@ -4553,6 +4571,7 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector Date: Tue, 22 May 2018 14:46:30 +0100 Subject: [PATCH 0184/1404] db: store cumulative rct output distribution in the db for speed This gets rid of the temporary precalc cache. Also make the RPC able to send data back in binary or JSON, since there can be a lot of data This bumps the LMDB database format to v3, with migration. --- src/blockchain_db/blockchain_db.cpp | 11 +- src/blockchain_db/blockchain_db.h | 15 ++ src/blockchain_db/lmdb/db_lmdb.cpp | 211 +++++++++++++++++++++++- src/blockchain_db/lmdb/db_lmdb.h | 6 + src/cryptonote_core/blockchain.cpp | 39 ++++- src/rpc/core_rpc_server.cpp | 46 ++---- src/rpc/core_rpc_server_commands_defs.h | 13 +- src/rpc/get_output_distribution_cache.h | 113 ------------- tests/unit_tests/hardfork.cpp | 38 +++-- 9 files changed, 315 insertions(+), 177 deletions(-) delete mode 100644 src/rpc/get_output_distribution_cache.h diff --git a/src/blockchain_db/blockchain_db.cpp b/src/blockchain_db/blockchain_db.cpp index 88ac342557b..8544cc3f0db 100644 --- a/src/blockchain_db/blockchain_db.cpp +++ b/src/blockchain_db/blockchain_db.cpp @@ -218,13 +218,22 @@ uint64_t BlockchainDB::add_block( const block& blk // call out to add the transactions time1 = epee::misc_utils::get_tick_count(); + + uint64_t num_rct_outs = 0; add_transaction(blk_hash, blk.miner_tx); + if (blk.miner_tx.version == 2) + num_rct_outs += blk.miner_tx.vout.size(); int tx_i = 0; crypto::hash tx_hash = crypto::null_hash; for (const transaction& tx : txs) { tx_hash = blk.tx_hashes[tx_i]; add_transaction(blk_hash, tx, &tx_hash); + for (const auto &vout: tx.vout) + { + if (vout.amount == 0) + ++num_rct_outs; + } ++tx_i; } TIME_MEASURE_FINISH(time1); @@ -232,7 +241,7 @@ uint64_t BlockchainDB::add_block( const block& blk // call out to subclass implementation to add the block & metadata time1 = epee::misc_utils::get_tick_count(); - add_block(blk, block_size, cumulative_difficulty, coins_generated, blk_hash); + add_block(blk, block_size, cumulative_difficulty, coins_generated, num_rct_outs, blk_hash); TIME_MEASURE_FINISH(time1); time_add_block1 += time1; diff --git a/src/blockchain_db/blockchain_db.h b/src/blockchain_db/blockchain_db.h index 564016fc9a0..a52782b3c57 100644 --- a/src/blockchain_db/blockchain_db.h +++ b/src/blockchain_db/blockchain_db.h @@ -367,6 +367,7 @@ class BlockchainDB , const size_t& block_size , const difficulty_type& cumulative_difficulty , const uint64_t& coins_generated + , uint64_t num_rct_outs , const crypto::hash& blk_hash ) = 0; @@ -891,6 +892,20 @@ class BlockchainDB */ virtual uint64_t get_block_timestamp(const uint64_t& height) const = 0; + /** + * @brief fetch a block's cumulative number of rct outputs + * + * The subclass should return the numer of rct outputs in the blockchain + * up to the block with the given height (inclusive). + * + * If the block does not exist, the subclass should throw BLOCK_DNE + * + * @param height the height requested + * + * @return the cumulative number of rct outputs + */ + virtual std::vector get_block_cumulative_rct_outputs(const std::vector &heights) const = 0; + /** * @brief fetch the top block's timestamp * diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp index 367bfa49e79..3aaade3b4f5 100644 --- a/src/blockchain_db/lmdb/db_lmdb.cpp +++ b/src/blockchain_db/lmdb/db_lmdb.cpp @@ -53,7 +53,7 @@ using epee::string_tools::pod_to_hex; using namespace crypto; // Increase when the DB structure changes -#define VERSION 2 +#define VERSION 3 namespace { @@ -250,6 +250,16 @@ inline void lmdb_db_open(MDB_txn* txn, const char* name, int flags, MDB_dbi& dbi namespace cryptonote { +typedef struct mdb_block_info_old +{ + uint64_t bi_height; + uint64_t bi_timestamp; + uint64_t bi_coins; + uint64_t bi_size; // a size_t really but we need 32-bit compat + difficulty_type bi_diff; + crypto::hash bi_hash; +} mdb_block_info_old; + typedef struct mdb_block_info { uint64_t bi_height; @@ -258,6 +268,7 @@ typedef struct mdb_block_info uint64_t bi_size; // a size_t really but we need 32-bit compat difficulty_type bi_diff; crypto::hash bi_hash; + uint64_t bi_cum_rct; } mdb_block_info; typedef struct blk_height { @@ -667,7 +678,7 @@ uint64_t BlockchainLMDB::get_estimated_batch_size(uint64_t batch_num_blocks, uin } void BlockchainLMDB::add_block(const block& blk, const size_t& block_size, const difficulty_type& cumulative_difficulty, const uint64_t& coins_generated, - const crypto::hash& blk_hash) + uint64_t num_rct_outs, const crypto::hash& blk_hash) { LOG_PRINT_L3("BlockchainLMDB::" << __func__); check_open(); @@ -715,6 +726,16 @@ void BlockchainLMDB::add_block(const block& blk, const size_t& block_size, const bi.bi_size = block_size; bi.bi_diff = cumulative_difficulty; bi.bi_hash = blk_hash; + bi.bi_cum_rct = num_rct_outs; + if (blk.major_version >= 4) + { + uint64_t last_height = m_height-1; + MDB_val_set(h, last_height); + if ((result = mdb_cursor_get(m_cur_block_info, (MDB_val *)&zerokval, &h, MDB_GET_BOTH))) + throw1(BLOCK_DNE(lmdb_error("Failed to get block info: ", result).c_str())); + const mdb_block_info *bi_prev = (const mdb_block_info*)h.mv_data; + bi.bi_cum_rct += bi_prev->bi_cum_rct; + } MDB_val_set(val, bi); result = mdb_cursor_put(m_cur_block_info, (MDB_val *)&zerokval, &val, MDB_APPENDDUP); @@ -759,8 +780,6 @@ void BlockchainLMDB::remove_block() if ((result = mdb_cursor_del(m_cur_block_heights, 0))) throw1(DB_ERROR(lmdb_error("Failed to add removal of block height by hash to db transaction: ", result).c_str())); - if ((result = mdb_cursor_get(m_cur_blocks, &k, NULL, MDB_SET))) - throw1(DB_ERROR(lmdb_error("Failed to locate block for removal: ", result).c_str())); if ((result = mdb_cursor_del(m_cur_blocks, 0))) throw1(DB_ERROR(lmdb_error("Failed to add removal of block to db transaction: ", result).c_str())); @@ -1143,6 +1162,8 @@ BlockchainLMDB::BlockchainLMDB(bool batch_transactions): BlockchainDB() m_cum_size = 0; m_cum_count = 0; + // reset may also need changing when initialize things here + m_hardfork = nullptr; } @@ -1908,6 +1929,43 @@ uint64_t BlockchainLMDB::get_block_timestamp(const uint64_t& height) const return ret; } +std::vector BlockchainLMDB::get_block_cumulative_rct_outputs(const std::vector &heights) const +{ + LOG_PRINT_L3("BlockchainLMDB::" << __func__); + check_open(); + std::vector res; + int result; + + if (heights.empty()) + return {}; + res.reserve(heights.size()); + + TXN_PREFIX_RDONLY(); + RCURSOR(block_info); + + MDB_stat db_stats; + if ((result = mdb_stat(m_txn, m_blocks, &db_stats))) + throw0(DB_ERROR(lmdb_error("Failed to query m_blocks: ", result).c_str())); + for (size_t i = 0; i < heights.size(); ++i) + if (heights[i] >= db_stats.ms_entries) + throw0(BLOCK_DNE(std::string("Attempt to get rct distribution from height " + std::to_string(heights[i]) + " failed -- block size not in db").c_str())); + + MDB_val v; + + for (uint64_t height: heights) + { + MDB_val_set(v, height); + result = mdb_cursor_get(m_cur_block_info, (MDB_val *)&zerokval, &v, MDB_GET_BOTH); + if (result) + throw0(DB_ERROR(lmdb_error("Error attempting to retrieve rct distribution from the db: ", result).c_str())); + const mdb_block_info *bi = (const mdb_block_info *)v.mv_data; + res.push_back(bi->bi_cum_rct); + } + + TXN_POSTFIX_RDONLY(); + return res; +} + uint64_t BlockchainLMDB::get_top_block_timestamp() const { LOG_PRINT_L3("BlockchainLMDB::" << __func__); @@ -3328,6 +3386,7 @@ bool BlockchainLMDB::get_output_distribution(uint64_t amount, uint64_t from_heig MDB_val_set(k, amount); MDB_val v; MDB_cursor_op op = MDB_SET; + base = 0; while (1) { int ret = mdb_cursor_get(m_cur_output_amounts, &k, &v, op); @@ -3346,6 +3405,9 @@ bool BlockchainLMDB::get_output_distribution(uint64_t amount, uint64_t from_heig break; } + for (size_t n = 1; n < distribution.size(); ++n) + distribution[n] += distribution[n - 1]; + TXN_POSTFIX_RDONLY(); return true; @@ -3440,7 +3502,7 @@ void BlockchainLMDB::fixup() if (result) \ throw0(DB_ERROR(lmdb_error("Failed to get DB record for " name ": ", result).c_str())); \ ptr = (char *)k.mv_data; \ - ptr[sizeof(name)-2] = 's' + ptr[sizeof(name)-2]++ #define LOGIF(y) if (ELPP->vRegistry()->allowed(y, "global")) @@ -3580,7 +3642,7 @@ void BlockchainLMDB::migrate_0_1() break; } MDB_dbi diffs, hashes, sizes, timestamps; - mdb_block_info bi; + mdb_block_info_old bi; MDB_val_set(nv, bi); lmdb_db_open(txn, "block_diffs", 0, diffs, "Failed to open db handle for block_diffs"); @@ -4120,6 +4182,141 @@ void BlockchainLMDB::migrate_1_2() txn.commit(); } +void BlockchainLMDB::migrate_2_3() +{ + LOG_PRINT_L3("BlockchainLMDB::" << __func__); + uint64_t i; + int result; + mdb_txn_safe txn(false); + MDB_val k, v; + char *ptr; + + MGINFO_YELLOW("Migrating blockchain from DB version 2 to 3 - this may take a while:"); + + do { + LOG_PRINT_L1("migrating block info:"); + + result = mdb_txn_begin(m_env, NULL, 0, txn); + if (result) + throw0(DB_ERROR(lmdb_error("Failed to create a transaction for the db: ", result).c_str())); + + MDB_stat db_stats; + if ((result = mdb_stat(txn, m_blocks, &db_stats))) + throw0(DB_ERROR(lmdb_error("Failed to query m_blocks: ", result).c_str())); + const uint64_t blockchain_height = db_stats.ms_entries; + + MDEBUG("enumerating rct outputs..."); + std::vector distribution(blockchain_height, 0); + bool r = for_all_outputs(0, [&](uint64_t height) { + if (height >= blockchain_height) + { + MERROR("Output found claiming height >= blockchain height"); + return false; + } + distribution[height]++; + return true; + }); + if (!r) + throw0(DB_ERROR("Failed to build rct output distribution")); + for (size_t i = 1; i < distribution.size(); ++i) + distribution[i] += distribution[i - 1]; + + /* the block_info table name is the same but the old version and new version + * have incompatible data. Create a new table. We want the name to be similar + * to the old name so that it will occupy the same location in the DB. + */ + MDB_dbi o_block_info = m_block_info; + lmdb_db_open(txn, "block_infn", MDB_INTEGERKEY | MDB_CREATE | MDB_DUPSORT | MDB_DUPFIXED, m_block_info, "Failed to open db handle for block_infn"); + mdb_set_dupsort(txn, m_block_info, compare_uint64); + + MDB_cursor *c_old, *c_cur; + i = 0; + while(1) { + if (!(i % 1000)) { + if (i) { + LOGIF(el::Level::Info) { + std::cout << i << " / " << blockchain_height << " \r" << std::flush; + } + txn.commit(); + result = mdb_txn_begin(m_env, NULL, 0, txn); + if (result) + throw0(DB_ERROR(lmdb_error("Failed to create a transaction for the db: ", result).c_str())); + } + result = mdb_cursor_open(txn, m_block_info, &c_cur); + if (result) + throw0(DB_ERROR(lmdb_error("Failed to open a cursor for block_infn: ", result).c_str())); + result = mdb_cursor_open(txn, o_block_info, &c_old); + if (result) + throw0(DB_ERROR(lmdb_error("Failed to open a cursor for block_info: ", result).c_str())); + if (!i) { + MDB_stat db_stat; + result = mdb_stat(txn, m_block_info, &db_stats); + if (result) + throw0(DB_ERROR(lmdb_error("Failed to query m_block_info: ", result).c_str())); + i = db_stats.ms_entries; + } + } + result = mdb_cursor_get(c_old, &k, &v, MDB_NEXT); + if (result == MDB_NOTFOUND) { + txn.commit(); + break; + } + else if (result) + throw0(DB_ERROR(lmdb_error("Failed to get a record from block_info: ", result).c_str())); + const mdb_block_info_old *bi_old = (const mdb_block_info_old*)v.mv_data; + mdb_block_info bi; + bi.bi_height = bi_old->bi_height; + bi.bi_timestamp = bi_old->bi_timestamp; + bi.bi_coins = bi_old->bi_coins; + bi.bi_size = bi_old->bi_size; + bi.bi_diff = bi_old->bi_diff; + bi.bi_hash = bi_old->bi_hash; + if (bi_old->bi_height >= distribution.size()) + throw0(DB_ERROR("Bad height in block_info record")); + bi.bi_cum_rct = distribution[bi_old->bi_height]; + MDB_val_set(nv, bi); + result = mdb_cursor_put(c_cur, (MDB_val *)&zerokval, &nv, MDB_APPENDDUP); + if (result) + throw0(DB_ERROR(lmdb_error("Failed to put a record into block_infn: ", result).c_str())); + /* we delete the old records immediately, so the overall DB and mapsize should not grow. + * This is a little slower than just letting mdb_drop() delete it all at the end, but + * it saves a significant amount of disk space. + */ + result = mdb_cursor_del(c_old, 0); + if (result) + throw0(DB_ERROR(lmdb_error("Failed to delete a record from block_info: ", result).c_str())); + i++; + } + + result = mdb_txn_begin(m_env, NULL, 0, txn); + if (result) + throw0(DB_ERROR(lmdb_error("Failed to create a transaction for the db: ", result).c_str())); + /* Delete the old table */ + result = mdb_drop(txn, o_block_info, 1); + if (result) + throw0(DB_ERROR(lmdb_error("Failed to delete old block_info table: ", result).c_str())); + + RENAME_DB("block_infn"); + + lmdb_db_open(txn, "block_info", MDB_INTEGERKEY | MDB_CREATE | MDB_DUPSORT | MDB_DUPFIXED, m_block_info, "Failed to open db handle for block_infn"); + mdb_set_dupsort(txn, m_block_info, compare_uint64); + + txn.commit(); + } while(0); + + uint32_t version = 3; + v.mv_data = (void *)&version; + v.mv_size = sizeof(version); + MDB_val_copy vk("version"); + result = mdb_txn_begin(m_env, NULL, 0, txn); + if (result) + throw0(DB_ERROR(lmdb_error("Failed to create a transaction for the db: ", result).c_str())); + result = mdb_put(txn, m_properties, &vk, &v, 0); + if (result) + throw0(DB_ERROR(lmdb_error("Failed to update version for the db: ", result).c_str())); + txn.commit(); +} + void BlockchainLMDB::migrate(const uint32_t oldversion) { switch(oldversion) { @@ -4127,6 +4324,8 @@ void BlockchainLMDB::migrate(const uint32_t oldversion) migrate_0_1(); /* FALLTHRU */ case 1: migrate_1_2(); /* FALLTHRU */ + case 2: + migrate_2_3(); /* FALLTHRU */ default: ; } diff --git a/src/blockchain_db/lmdb/db_lmdb.h b/src/blockchain_db/lmdb/db_lmdb.h index cc1b06ca02c..b7f6262ae4f 100644 --- a/src/blockchain_db/lmdb/db_lmdb.h +++ b/src/blockchain_db/lmdb/db_lmdb.h @@ -197,6 +197,8 @@ class BlockchainLMDB : public BlockchainDB virtual cryptonote::blobdata get_block_blob_from_height(const uint64_t& height) const; + virtual std::vector get_block_cumulative_rct_outputs(const std::vector &heights) const; + virtual uint64_t get_block_timestamp(const uint64_t& height) const; virtual uint64_t get_top_block_timestamp() const; @@ -317,6 +319,7 @@ class BlockchainLMDB : public BlockchainDB , const size_t& block_size , const difficulty_type& cumulative_difficulty , const uint64_t& coins_generated + , uint64_t num_rct_outs , const crypto::hash& block_hash ); @@ -387,6 +390,9 @@ class BlockchainLMDB : public BlockchainDB // migrate from DB version 1 to 2 void migrate_1_2(); + // migrate from DB version 2 to 3 + void migrate_2_3(); + void cleanup_batch(); private: diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 73ce98366f5..a7cd0947aca 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -1985,14 +1985,14 @@ void Blockchain::get_output_key_mask_unlocked(const uint64_t& amount, const uint //------------------------------------------------------------------ bool Blockchain::get_output_distribution(uint64_t amount, uint64_t from_height, uint64_t to_height, uint64_t &start_height, std::vector &distribution, uint64_t &base) const { - // rct outputs don't exist before v3 + // rct outputs don't exist before v4 if (amount == 0) { switch (m_nettype) { - case STAGENET: start_height = stagenet_hard_forks[2].height; break; - case TESTNET: start_height = testnet_hard_forks[2].height; break; - case MAINNET: start_height = mainnet_hard_forks[2].height; break; + case STAGENET: start_height = stagenet_hard_forks[3].height; break; + case TESTNET: start_height = testnet_hard_forks[3].height; break; + case MAINNET: start_height = mainnet_hard_forks[3].height; break; default: return false; } } @@ -2000,11 +2000,40 @@ bool Blockchain::get_output_distribution(uint64_t amount, uint64_t from_height, start_height = 0; base = 0; + if (to_height > 0 && to_height < from_height) + return false; + const uint64_t real_start_height = start_height; if (from_height > start_height) start_height = from_height; - return m_db->get_output_distribution(amount, start_height, to_height, distribution, base); + distribution.clear(); + uint64_t db_height = m_db->height(); + if (db_height == 0) + return false; + if (to_height == 0) + to_height = db_height - 1; + if (start_height >= db_height || to_height >= db_height) + return false; + if (amount == 0) + { + std::vector heights; + heights.reserve(to_height + 1 - start_height); + uint64_t real_start_height = start_height > 0 ? start_height-1 : start_height; + for (uint64_t h = real_start_height; h <= to_height; ++h) + heights.push_back(h); + distribution = m_db->get_block_cumulative_rct_outputs(heights); + if (start_height > 0) + { + base = distribution[0]; + distribution.erase(distribution.begin()); + } + return true; + } + else + { + return m_db->get_output_distribution(amount, start_height, to_height, distribution, base); + } } //------------------------------------------------------------------ // This function takes a list of block hashes from another node diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index b55b1994b14..857370dbbaf 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -47,7 +47,6 @@ using namespace epee; #include "rpc/rpc_args.h" #include "core_rpc_server_error_codes.h" #include "p2p/net_node.h" -#include "get_output_distribution_cache.h" #include "version.h" #undef MONERO_DEFAULT_LOG_CATEGORY @@ -2071,6 +2070,10 @@ namespace cryptonote bool core_rpc_server::on_get_output_distribution(const COMMAND_RPC_GET_OUTPUT_DISTRIBUTION::request& req, COMMAND_RPC_GET_OUTPUT_DISTRIBUTION::response& res, epee::json_rpc::error& error_resp) { PERF_TIMER(on_get_output_distribution); + bool r; + if (use_bootstrap_daemon_if_necessary(invoke_http_mode::JON_RPC, "get_output_distribution", req, res, r)) + return r; + try { for (uint64_t amount: req.amounts) @@ -2087,38 +2090,17 @@ namespace cryptonote if (d.cached && amount == 0 && d.cached_from == req.from_height && d.cached_to == req.to_height) { - res.distributions.push_back({amount, d.cached_start_height, d.cached_distribution, d.cached_base}); - if (req.cumulative) + res.distributions.push_back({amount, d.cached_start_height, req.binary, d.cached_distribution, d.cached_base}); + if (!req.cumulative) { auto &distribution = res.distributions.back().distribution; - distribution[0] += d.cached_base; - for (size_t n = 1; n < distribution.size(); ++n) - distribution[n] += distribution[n-1]; + for (size_t n = distribution.size() - 1; n > 0; --n) + distribution[n] -= distribution[n-1]; + distribution[0] -= d.cached_base; } continue; } - // this is a slow operation, so we have precomputed caches of common cases - bool found = false; - for (const auto &slot: get_output_distribution_cache) - { - if (slot.amount == amount && slot.from_height == req.from_height && slot.to_height == req.to_height) - { - res.distributions.push_back({amount, slot.start_height, slot.distribution, slot.base}); - found = true; - if (req.cumulative) - { - auto &distribution = res.distributions.back().distribution; - distribution[0] += slot.base; - for (size_t n = 1; n < distribution.size(); ++n) - distribution[n] += distribution[n-1]; - } - break; - } - } - if (found) - continue; - std::vector distribution; uint64_t start_height, base; if (!m_core.get_output_distribution(amount, req.from_height, req.to_height, start_height, distribution, base)) @@ -2144,14 +2126,14 @@ namespace cryptonote d.cached = true; } - if (req.cumulative) + if (!req.cumulative) { - distribution[0] += base; - for (size_t n = 1; n < distribution.size(); ++n) - distribution[n] += distribution[n-1]; + for (size_t n = distribution.size() - 1; n > 0; --n) + distribution[n] -= distribution[n-1]; + distribution[0] -= base; } - res.distributions.push_back({amount, start_height, std::move(distribution), base}); + res.distributions.push_back({amount, start_height, req.binary, std::move(distribution), base}); } } catch (const std::exception &e) diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index 49b73014994..05690390d8e 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -49,7 +49,7 @@ namespace cryptonote // advance which version they will stop working with // Don't go over 32767 for any of these #define CORE_RPC_VERSION_MAJOR 1 -#define CORE_RPC_VERSION_MINOR 20 +#define CORE_RPC_VERSION_MINOR 21 #define MAKE_CORE_RPC_VERSION(major,minor) (((major)<<16)|(minor)) #define CORE_RPC_VERSION MAKE_CORE_RPC_VERSION(CORE_RPC_VERSION_MAJOR, CORE_RPC_VERSION_MINOR) @@ -2231,12 +2231,14 @@ namespace cryptonote uint64_t from_height; uint64_t to_height; bool cumulative; + bool binary; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(amounts) KV_SERIALIZE_OPT(from_height, (uint64_t)0) KV_SERIALIZE_OPT(to_height, (uint64_t)0) KV_SERIALIZE_OPT(cumulative, false) + KV_SERIALIZE_OPT(binary, true) END_KV_SERIALIZE_MAP() }; @@ -2244,13 +2246,18 @@ namespace cryptonote { uint64_t amount; uint64_t start_height; + bool binary; std::vector distribution; uint64_t base; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(amount) KV_SERIALIZE(start_height) - KV_SERIALIZE_CONTAINER_POD_AS_BLOB(distribution) + KV_SERIALIZE(binary) + if (this_ref.binary) + KV_SERIALIZE_CONTAINER_POD_AS_BLOB(distribution) + else + KV_SERIALIZE(distribution) KV_SERIALIZE(base) END_KV_SERIALIZE_MAP() }; @@ -2259,10 +2266,12 @@ namespace cryptonote { std::string status; std::vector distributions; + bool untrusted; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(status) KV_SERIALIZE(distributions) + KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; }; diff --git a/src/rpc/get_output_distribution_cache.h b/src/rpc/get_output_distribution_cache.h deleted file mode 100644 index 6495e7d4cea..00000000000 --- a/src/rpc/get_output_distribution_cache.h +++ /dev/null @@ -1,113 +0,0 @@ -static const struct -{ - uint64_t amount; - uint64_t from_height; - uint64_t to_height; - uint64_t start_height; - uint64_t base; - std::vector distribution; -} -get_output_distribution_cache[] = -{ - { - 0, - 1544704, - 1546001, - 1544704, - 5143500, - { - 5, 38, 37, 33, 39, 7, 1, 1, 5, 9, 7, 5, 17, 5, 3, 9, 3, 17, 5, 17, 1, 1, 15, 13, 3, 10, 5, 3, 34, 1, 45, 7, - 5, 17, 5, 22, 3, 1, 17, 16, 5, 1, 3, 43, 5, 13, 3, 23, 9, 7, 9, 13, 1, 11, 1, 17, 1, 3, 16, 11, 5, 11, 7, 7, - 33, 11, 7, 1, 5, 1, 21, 19, 1, 17, 1, 49, 17, 3, 3, 9, 35, 46, 46, 39, 26, 33, 21, 3, 23, 3, 9, 37, 1, 33, 11, 32, - 1, 13, 16, 12, 3, 21, 1, 18, 3, 19, 1, 25, 5, 3, 18, 7, 17, 5, 9, 15, 7, 7, 11, 9, 9, 17, 5, 16, 1, 3, 13, 3, - 5, 5, 5, 13, 5, 9, 5, 13, 3, 17, 15, 36, 13, 3, 20, 12, 6, 23, 17, 10, 22, 23, 1, 7, 21, 6, 23, 1, 3, 19, 13, 1, - 3, 43, 35, 13, 1, 31, 7, 3, 17, 1, 15, 5, 11, 15, 24, 1, 18, 13, 5, 15, 1, 29, 3, 3, 13, 3, 15, 7, 17, 3, 1, 1, - 17, 1, 1, 45, 39, 27, 45, 46, 34, 7, 3, 3, 9, 3, 3, 11, 7, 5, 9, 25, 19, 3, 33, 1, 5, 17, 1, 45, 4, 1, 45, 11, - 44, 32, 3, 1, 3, 7, 17, 15, 5, 45, 35, 41, 1, 35, 3, 3, 19, 1, 9, 17, 29, 29, 3, 1, 13, 1, 3, 47, 21, 13, 7, 1, - 7, 5, 1, 11, 1, 40, 9, 7, 3, 3, 13, 25, 1, 47, 5, 7, 3, 7, 31, 40, 34, 6, 3, 15, 3, 31, 5, 13, 27, 9, 12, 21, - 3, 1, 19, 1, 19, 5, 47, 49, 47, 42, 50, 34, 29, 23, 1, 5, 9, 16, 11, 7, 1, 19, 7, 5, 1, 15, 1, 1, 9, 13, 9, 5, - 27, 3, 3, 29, 1, 33, 3, 9, 5, 35, 5, 1, 17, 7, 3, 39, 3, 28, 19, 1, 1, 9, 1, 3, 27, 1, 37, 3, 1, 1, 16, 3, - 25, 11, 5, 3, 33, 45, 17, 11, 7, 22, 9, 1, 5, 5, 5, 15, 1, 15, 9, 7, 11, 13, 37, 49, 46, 38, 11, 1, 25, 1, 13, 18, - 3, 7, 39, 3, 37, 19, 35, 3, 1, 3, 19, 1, 3, 15, 21, 3, 27, 1, 45, 48, 1, 13, 29, 9, 1, 1, 46, 43, 5, 15, 3, 7, - 29, 26, 5, 5, 21, 37, 17, 21, 3, 13, 1, 5, 1, 17, 5, 31, 13, 1, 11, 3, 46, 9, 3, 7, 1, 1, 41, 1, 21, 1, 5, 12, - 7, 13, 9, 25, 1, 47, 47, 48, 48, 48, 48, 48, 47, 48, 45, 45, 33, 52, 50, 46, 45, 47, 35, 41, 38, 35, 42, 38, 34, 41, 39, 35, - 51, 51, 45, 43, 49, 52, 53, 45, 42, 46, 37, 53, 49, 41, 46, 49, 46, 47, 48, 37, 41, 33, 43, 38, 15, 3, 3, 27, 11, 5, 23, 13, - 1, 1, 37, 3, 15, 3, 30, 13, 3, 45, 12, 3, 5, 11, 1, 1, 21, 9, 11, 19, 1, 1, 1, 25, 5, 21, 3, 1, 32, 44, 3, 33, - 11, 7, 5, 23, 1, 37, 47, 48, 48, 48, 48, 48, 48, 46, 47, 47, 50, 45, 49, 50, 46, 47, 49, 45, 51, 49, 50, 49, 49, 46, 47, 48, - 46, 48, 46, 50, 46, 43, 46, 46, 48, 47, 46, 47, 45, 49, 46, 43, 50, 45, 45, 49, 45, 48, 45, 49, 48, 45, 45, 51, 45, 51, 45, 46, - 52, 45, 45, 51, 51, 52, 44, 45, 52, 50, 50, 46, 47, 51, 51, 46, 47, 47, 47, 50, 47, 51, 48, 49, 51, 50, 48, 48, 48, 50, 49, 49, - 52, 52, 49, 50, 49, 49, 49, 51, 52, 49, 52, 50, 49, 47, 29, 15, 39, 17, 31, 5, 40, 5, 18, 23, 25, 7, 35, 26, 5, 31, 49, 22, - 3, 17, 7, 49, 7, 49, 47, 12, 44, 46, 36, 15, 3, 1, 47, 13, 35, 40, 5, 21, 19, 39, 21, 33, 31, 29, 1, 1, 37, 1, 15, 47, - 7, 7, 47, 41, 13, 3, 47, 31, 9, 33, 13, 43, 29, 5, 1, 9, 33, 7, 27, 15, 15, 25, 5, 43, 22, 31, 7, 1, 47, 1, 15, 27, - 3, 27, 45, 15, 1, 36, 17, 1, 23, 39, 38, 45, 7, 7, 19, 7, 11, 47, 33, 16, 3, 45, 45, 45, 9, 27, 3, 3, 21, 3, 7, 21, - 7, 3, 43, 1, 17, 1, 45, 37, 46, 5, 5, 13, 46, 40, 48, 48, 45, 34, 1, 46, 19, 25, 9, 7, 47, 23, 37, 31, 3, 25, 13, 46, - 31, 25, 5, 46, 35, 52, 11, 23, 27, 4, 15, 11, 11, 11, 9, 34, 7, 9, 15, 34, 9, 27, 37, 28, 25, 45, 13, 30, 5, 25, 15, 7, - 3, 19, 27, 1, 7, 11, 1, 32, 3, 45, 11, 9, 21, 25, 9, 13, 13, 1, 7, 1, 33, 11, 5, 3, 3, 27, 27, 5, 3, 37, 17, 17, - 3, 7, 5, 13, 1, 3, 44, 45, 26, 25, 1, 13, 3, 13, 3, 11, 1, 11, 7, 45, 3, 3, 1, 43, 1, 19, 3, 1, 15, 5, 39, 7, - 7, 1, 9, 1, 11, 19, 3, 35, 29, 7, 15, 11, 40, 7, 44, 38, 34, 7, 9, 7, 1, 27, 1, 9, 5, 45, 1, 21, 3, 1, 5, 9, - 3, 21, 23, 33, 3, 1, 7, 3, 3, 7, 41, 9, 7, 1, 5, 31, 9, 7, 1, 1, 11, 41, 51, 20, 9, 47, 39, 17, 9, 35, 1, 41, - 1, 19, 1, 19, 15, 1, 13, 5, 23, 15, 9, 15, 17, 1, 15, 27, 33, 31, 29, 7, 13, 1, 5, 45, 5, 1, 1, 11, 1, 13, 3, 7, - 9, 1, 13, 39, 3, 33, 5, 3, 7, 7, 5, 29, 11, 1, 7, 1, 15, 3, 13, 3, 15, 3, 3, 1, 5, 1, 9, 1, 44, 49, 24, 25, - 1, 1, 34, 22, 7, 5, 5, 5, 10, 9, 13, 3, 9, 1, 9, 19, 7, 43, 48, 7, 11, 7, 3, 3, 7, 21, 1, 1, 3, 3, 11, 31, - 1, 1, 13, 22, 23, 7, 27, 9, 3, 3, 21, 1, 35, 21, 9, 11, 13, 39, 1, 3, 7, 23, 3, 28, 3, 45, 47, 38, 32, 37, 34, 1, - 23, 3, 3, 1, 19, 19, 1, 5, 13, 1, 5, 11, 38, 3, 1, 36, 13, 1, 1, 23, 5, 17, 11, 1, 13, 1, 3, 7, 11, 3, 33, 7, - 19, 5, 5, 1, 1, 3, 5, 41, 1, 3, 25, 1, 7, 7, 9, 3, 11, 3, 13, 5, 7, 1, 3, 9, 1, 1, 43, 47, 47, 47, 17, 7, - 17, 3, 19, 1, 9, 9, 33, 22, 1, 25, 1, 3, 3, 32, 5, 1, 23, 9, 5, 1, 31, 5, 9, 1, 3, 7, 19, 1, 12, 11, 5, 1, - 1, 9, 25, 15, 15, 13, 5, 3, 15, 1, 17, 1, 1, 5, 5, 41, 11, 15, 7, 3, 21, 21, 35, 22, 46, 35, 3, 27, 5, 3, 45, 22, - 27, 1, 19, 9, 1, 25, 1, 29, 3, 5, 25, 17, 27, 5, 19, 5, 25, 7, 19, 1, 9, 21, 3, 7, 29, 27, 17, 3, 3, 15, 7, 19, - 5, 25, 1, 23, 45, 4, 31, 1, 37, 14, 29, 3, 29, 1, 23, 29, 19, 11, 1, 13, 13, 9, 1, 25, 1, 33, 1, 37, 37, 23, 7, 21, - 7, 3, 13, 7, 3, 7, 21, 11, 9, 1, 31, 3, 1, 7, 39, 46, 3, 30, - }, - }, - { - 0, - 1562704, - 1564001, - 1562704, - 5521986, - { - 35, 45, 23, 3, 44, 47, 44, 3, 17, 35, 7, 11, 7, 29, 43, 27, 11, 7, 5, 31, 13, 9, 45, 45, 7, 42, 17, 15, 19, 11, 45, 19, - 45, 46, 45, 46, 32, 34, 43, 34, 46, 47, 45, 30, 17, 45, 46, 36, 35, 38, 19, 9, 23, 17, 3, 19, 31, 41, 35, 24, 15, 45, 15, 5, - 11, 5, 19, 11, 11, 7, 15, 19, 45, 34, 7, 7, 29, 1, 9, 36, 7, 44, 45, 33, 25, 8, 17, 7, 44, 43, 48, 45, 42, 46, 40, 44, - 1, 43, 45, 46, 46, 35, 19, 19, 23, 5, 13, 19, 7, 16, 9, 3, 25, 34, 3, 27, 9, 39, 3, 43, 21, 1, 45, 45, 39, 25, 23, 13, - 39, 39, 3, 45, 43, 46, 44, 40, 39, 33, 45, 47, 38, 45, 45, 39, 47, 47, 45, 46, 35, 45, 43, 47, 45, 40, 34, 42, 42, 48, 49, 47, - 47, 48, 47, 45, 43, 48, 37, 48, 41, 45, 48, 34, 42, 44, 9, 19, 27, 1, 47, 47, 43, 25, 29, 5, 5, 21, 39, 35, 43, 37, 13, 45, - 25, 31, 26, 47, 45, 23, 23, 39, 32, 25, 44, 47, 35, 47, 15, 17, 7, 9, 5, 35, 31, 3, 45, 47, 46, 13, 17, 48, 45, 9, 13, 45, - 45, 31, 1, 53, 44, 46, 39, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 47, 47, 47, 47, 47, 47, 47, 47, 53, 49, 45, 45, 50, - 50, 27, 43, 46, 47, 46, 45, 48, 36, 42, 42, 46, 45, 47, 41, 45, 43, 47, 38, 48, 47, 33, 11, 45, 46, 34, 42, 32, 3, 45, 37, 45, - 15, 3, 45, 29, 31, 9, 5, 3, 27, 5, 21, 25, 7, 15, 46, 34, 5, 3, 17, 3, 9, 13, 7, 11, 3, 1, 34, 13, 7, 45, 33, 26, - 9, 5, 9, 41, 43, 34, 3, 35, 3, 17, 37, 11, 17, 3, 15, 27, 15, 45, 46, 13, 26, 16, 11, 7, 5, 45, 38, 45, 45, 22, 44, 44, - 43, 6, 11, 35, 15, 44, 17, 27, 13, 3, 40, 5, 9, 7, 35, 19, 5, 5, 1, 28, 33, 15, 45, 5, 29, 3, 31, 12, 5, 32, 24, 3, - 23, 13, 47, 45, 42, 46, 39, 21, 21, 1, 44, 44, 47, 41, 5, 1, 11, 36, 20, 5, 45, 39, 45, 45, 44, 45, 32, 22, 40, 11, 38, 1, - 45, 46, 37, 9, 23, 9, 15, 44, 7, 16, 38, 46, 11, 14, 24, 19, 19, 7, 26, 25, 45, 37, 17, 1, 35, 1, 3, 28, 3, 11, 22, 13, - 3, 1, 7, 38, 5, 3, 1, 26, 1, 3, 43, 44, 46, 45, 21, 11, 1, 44, 27, 1, 11, 26, 10, 44, 45, 45, 45, 47, 47, 45, 48, 45, - 38, 9, 5, 44, 46, 27, 3, 12, 1, 11, 3, 44, 43, 1, 5, 2, 46, 17, 13, 19, 1, 12, 7, 23, 1, 17, 6, 13, 3, 5, 27, 7, - 46, 36, 19, 25, 1, 1, 3, 8, 15, 3, 45, 45, 45, 37, 6, 15, 3, 5, 38, 14, 41, 1, 13, 45, 45, 39, 44, 29, 43, 48, 51, 50, - 37, 5, 17, 46, 47, 31, 5, 42, 49, 38, 39, 24, 7, 11, 44, 35, 21, 6, 15, 5, 47, 13, 28, 45, 34, 27, 24, 15, 35, 13, 7, 25, - 43, 13, 14, 5, 3, 5, 46, 45, 45, 35, 5, 21, 28, 3, 13, 4, 30, 19, 45, 45, 40, 37, 5, 40, 17, 9, 3, 9, 13, 4, 17, 33, - 44, 39, 17, 45, 28, 5, 11, 45, 19, 45, 21, 44, 31, 43, 49, 48, 15, 3, 1, 44, 45, 43, 11, 1, 1, 27, 43, 45, 39, 3, 1, 3, - 5, 31, 1, 43, 23, 19, 7, 5, 45, 31, 11, 7, 3, 9, 5, 7, 13, 43, 47, 45, 46, 47, 14, 3, 25, 45, 7, 17, 32, 21, 3, 17, - 5, 11, 31, 40, 45, 20, 45, 45, 32, 38, 47, 38, 45, 41, 49, 30, 45, 5, 36, 31, 22, 3, 46, 45, 13, 21, 23, 5, 46, 45, 33, 19, - 25, 1, 7, 13, 5, 44, 23, 29, 23, 24, 7, 5, 37, 13, 29, 13, 7, 17, 7, 43, 3, 21, 7, 44, 1, 19, 15, 9, 34, 43, 26, 3, - 17, 5, 6, 5, 7, 3, 33, 35, 43, 41, 48, 47, 30, 45, 19, 7, 5, 33, 11, 34, 25, 1, 21, 11, 29, 7, 1, 4, 5, 10, 14, 3, - 44, 11, 47, 45, 33, 3, 9, 7, 40, 23, 9, 1, 3, 1, 7, 5, 9, 9, 6, 11, 45, 41, 45, 19, 5, 11, 10, 39, 9, 19, 3, 11, - 43, 42, 1, 13, 35, 5, 32, 7, 5, 5, 43, 37, 3, 32, 17, 3, 23, 1, 13, 45, 17, 1, 21, 45, 43, 46, 49, 47, 45, 30, 9, 31, - 19, 42, 19, 44, 17, 14, 19, 25, 1, 7, 5, 45, 19, 13, 7, 3, 1, 1, 9, 21, 37, 9, 11, 1, 3, 37, 27, 13, 5, 21, 33, 3, - 27, 9, 27, 1, 39, 1, 46, 21, 10, 13, 21, 40, 22, 35, 41, 9, 33, 3, 17, 8, 45, 46, 42, 46, 47, 47, 47, 48, 48, 47, 43, 48, - 37, 39, 50, 35, 3, 40, 45, 40, 46, 36, 34, 28, 9, 9, 37, 9, 5, 7, 13, 31, 1, 7, 3, 3, 44, 45, 25, 15, 1, 21, 43, 25, - 1, 38, 34, 42, 31, 23, 33, 35, 37, 20, 7, 15, 3, 7, 7, 27, 45, 45, 48, 47, 45, 44, 47, 23, 25, 36, 11, 3, 18, 24, 27, 13, - 41, 13, 5, 5, 7, 19, 15, 7, 5, 14, 45, 45, 37, 1, 5, 17, 21, 41, 17, 37, 53, 41, 45, 45, 45, 45, 45, 45, 45, 45, 43, 47, - 47, 48, 53, 47, 47, 47, 49, 27, 45, 47, 47, 47, 47, 45, 45, 45, 47, 43, 48, 34, 34, 43, 46, 15, 37, 21, 5, 27, 11, 1, 9, 7, - 19, 15, 1, 1, 19, 3, 36, 27, 29, 13, 21, 9, 17, 5, 16, 45, 23, 34, 3, 1, 7, 25, 28, 13, 29, 15, 11, 19, 17, 1, 27, 23, - 31, 19, 41, 41, 40, 47, 28, 31, 26, 26, 36, 17, 5, 1, 23, 1, 45, 34, 49, 51, 34, 43, 37, 5, 41, 15, 5, 21, 1, 7, 9, 19, - 5, 11, 39, 19, 45, 45, 38, 17, 9, 1, 15, 11, 5, 13, 47, 46, 48, 45, 19, 32, 7, 19, 5, 7, 23, 29, 5, 45, 41, 37, 1, 5, - 27, 5, 5, 7, 19, 9, 1, 35, 48, 38, 38, 39, 42, 43, 21, 23, 43, 41, 7, 3, 7, 13, 1, 46, 47, 46, 23, 46, 45, 25, 7, 9, - 21, 7, 41, 13, 20, 1, 21, 15, 37, 5, 40, 45, 45, 5, 45, 46, 15, 33, 46, 12, 13, 7, 24, 7, 5, 30, 7, 46, 13, 8, 44, 35, - 45, 33, 40, 36, 47, 47, 29, 43, 36, 43, 45, 42, 36, 19, 7, 7, 43, 3, 44, 25, 48, 29, 11, 45, 30, 1, 17, 13, 25, 1, 48, 45, - 45, 45, 44, 49, 37, 9, 21, 17, 15, 7, 15, 25, 1, 1, 9, 43, 33, 11, 3, 29, 45, 45, 9, 7, 7, 27, 47, 45, 47, 48, 45, 47, - 26, 1, 43, 15, 45, 17, 1, 5, 35, 31, 9, 3, 9, 19, 9, 21, 43, 5, 27, 1, 5, 9, 4, 34, 19, 3, 7, 11, 45, 46, 45, 45, - 46, 47, 47, 44, 45, 43, 27, 9, 17, 15, 19, 44, 45, 46, 47, 47, 45, 45, - } - } -}; - diff --git a/tests/unit_tests/hardfork.cpp b/tests/unit_tests/hardfork.cpp index 913ebe84ab8..110b6492a4d 100644 --- a/tests/unit_tests/hardfork.cpp +++ b/tests/unit_tests/hardfork.cpp @@ -69,6 +69,7 @@ class TestDB: public BlockchainDB { virtual uint64_t get_block_height(const crypto::hash& h) const { return 0; } virtual block_header get_block_header(const crypto::hash& h) const { return block_header(); } virtual uint64_t get_block_timestamp(const uint64_t& height) const { return 0; } + virtual std::vector get_block_cumulative_rct_outputs(const std::vector &heights) const { return {}; } virtual uint64_t get_top_block_timestamp() const { return 0; } virtual size_t get_block_size(const uint64_t& height) const { return 128; } virtual difficulty_type get_block_cumulative_difficulty(const uint64_t& height) const { return 10; } @@ -131,6 +132,7 @@ class TestDB: public BlockchainDB { , const size_t& block_size , const difficulty_type& cumulative_difficulty , const uint64_t& coins_generated + , uint64_t num_rct_outs , const crypto::hash& blk_hash ) { blocks.push_back(blk); @@ -183,20 +185,20 @@ TEST(major, Only) ASSERT_FALSE(hf.add(mkblock(0, 2), 0)); ASSERT_FALSE(hf.add(mkblock(2, 2), 0)); ASSERT_TRUE(hf.add(mkblock(1, 2), 0)); - db.add_block(mkblock(1, 1), 0, 0, 0, crypto::hash()); + db.add_block(mkblock(1, 1), 0, 0, 0, 0, crypto::hash()); // block height 1, only version 1 is accepted ASSERT_FALSE(hf.add(mkblock(0, 2), 1)); ASSERT_FALSE(hf.add(mkblock(2, 2), 1)); ASSERT_TRUE(hf.add(mkblock(1, 2), 1)); - db.add_block(mkblock(1, 1), 0, 0, 0, crypto::hash()); + db.add_block(mkblock(1, 1), 0, 0, 0, 0, crypto::hash()); // block height 2, only version 2 is accepted ASSERT_FALSE(hf.add(mkblock(0, 2), 2)); ASSERT_FALSE(hf.add(mkblock(1, 2), 2)); ASSERT_FALSE(hf.add(mkblock(3, 2), 2)); ASSERT_TRUE(hf.add(mkblock(2, 2), 2)); - db.add_block(mkblock(2, 1), 0, 0, 0, crypto::hash()); + db.add_block(mkblock(2, 1), 0, 0, 0, 0, crypto::hash()); } TEST(empty_hardforks, Success) @@ -210,7 +212,7 @@ TEST(empty_hardforks, Success) ASSERT_TRUE(hf.get_state(time(NULL) + 3600*24*400) == HardFork::Ready); for (uint64_t h = 0; h <= 10; ++h) { - db.add_block(mkblock(hf, h, 1), 0, 0, 0, crypto::hash()); + db.add_block(mkblock(hf, h, 1), 0, 0, 0, 0, crypto::hash()); ASSERT_TRUE(hf.add(db.get_block_from_height(h), h)); } ASSERT_EQ(hf.get(0), 1); @@ -244,14 +246,14 @@ TEST(check_for_height, Success) for (uint64_t h = 0; h <= 4; ++h) { ASSERT_TRUE(hf.check_for_height(mkblock(1, 1), h)); ASSERT_FALSE(hf.check_for_height(mkblock(2, 2), h)); // block version is too high - db.add_block(mkblock(hf, h, 1), 0, 0, 0, crypto::hash()); + db.add_block(mkblock(hf, h, 1), 0, 0, 0, 0, crypto::hash()); ASSERT_TRUE(hf.add(db.get_block_from_height(h), h)); } for (uint64_t h = 5; h <= 10; ++h) { ASSERT_FALSE(hf.check_for_height(mkblock(1, 1), h)); // block version is too low ASSERT_TRUE(hf.check_for_height(mkblock(2, 2), h)); - db.add_block(mkblock(hf, h, 2), 0, 0, 0, crypto::hash()); + db.add_block(mkblock(hf, h, 2), 0, 0, 0, 0, crypto::hash()); ASSERT_TRUE(hf.add(db.get_block_from_height(h), h)); } } @@ -268,19 +270,19 @@ TEST(get, next_version) for (uint64_t h = 0; h <= 4; ++h) { ASSERT_EQ(2, hf.get_next_version()); - db.add_block(mkblock(hf, h, 1), 0, 0, 0, crypto::hash()); + db.add_block(mkblock(hf, h, 1), 0, 0, 0, 0, crypto::hash()); ASSERT_TRUE(hf.add(db.get_block_from_height(h), h)); } for (uint64_t h = 5; h <= 9; ++h) { ASSERT_EQ(4, hf.get_next_version()); - db.add_block(mkblock(hf, h, 2), 0, 0, 0, crypto::hash()); + db.add_block(mkblock(hf, h, 2), 0, 0, 0, 0, crypto::hash()); ASSERT_TRUE(hf.add(db.get_block_from_height(h), h)); } for (uint64_t h = 10; h <= 15; ++h) { ASSERT_EQ(4, hf.get_next_version()); - db.add_block(mkblock(hf, h, 4), 0, 0, 0, crypto::hash()); + db.add_block(mkblock(hf, h, 4), 0, 0, 0, 0, crypto::hash()); ASSERT_TRUE(hf.add(db.get_block_from_height(h), h)); } } @@ -321,7 +323,7 @@ TEST(steps_asap, Success) hf.init(); for (uint64_t h = 0; h < 10; ++h) { - db.add_block(mkblock(hf, h, 9), 0, 0, 0, crypto::hash()); + db.add_block(mkblock(hf, h, 9), 0, 0, 0, 0, crypto::hash()); ASSERT_TRUE(hf.add(db.get_block_from_height(h), h)); } @@ -348,7 +350,7 @@ TEST(steps_1, Success) hf.init(); for (uint64_t h = 0 ; h < 10; ++h) { - db.add_block(mkblock(hf, h, h+1), 0, 0, 0, crypto::hash()); + db.add_block(mkblock(hf, h, h+1), 0, 0, 0, 0, crypto::hash()); ASSERT_TRUE(hf.add(db.get_block_from_height(h), h)); } @@ -373,7 +375,7 @@ TEST(reorganize, Same) // index 0 1 2 3 4 5 6 7 8 9 static const uint8_t block_versions[] = { 1, 1, 4, 4, 7, 7, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 }; for (uint64_t h = 0; h < 20; ++h) { - db.add_block(mkblock(hf, h, block_versions[h]), 0, 0, 0, crypto::hash()); + db.add_block(mkblock(hf, h, block_versions[h]), 0, 0, 0, 0, crypto::hash()); ASSERT_TRUE(hf.add(db.get_block_from_height(h), h)); } @@ -404,7 +406,7 @@ TEST(reorganize, Changed) static const uint8_t block_versions[] = { 1, 1, 4, 4, 7, 7, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 }; static const uint8_t expected_versions[] = { 1, 1, 1, 1, 1, 1, 4, 4, 7, 7, 9, 9, 9, 9, 9, 9 }; for (uint64_t h = 0; h < 16; ++h) { - db.add_block(mkblock(hf, h, block_versions[h]), 0, 0, 0, crypto::hash()); + db.add_block(mkblock(hf, h, block_versions[h]), 0, 0, 0, 0, crypto::hash()); ASSERT_TRUE (hf.add(db.get_block_from_height(h), h)); } @@ -424,7 +426,7 @@ TEST(reorganize, Changed) ASSERT_EQ(db.height(), 3); hf.reorganize_from_block_height(2); for (uint64_t h = 3; h < 16; ++h) { - db.add_block(mkblock(hf, h, block_versions_new[h]), 0, 0, 0, crypto::hash()); + db.add_block(mkblock(hf, h, block_versions_new[h]), 0, 0, 0, 0, crypto::hash()); bool ret = hf.add(db.get_block_from_height(h), h); ASSERT_EQ (ret, h < 15); } @@ -448,7 +450,7 @@ TEST(voting, threshold) for (uint64_t h = 0; h <= 8; ++h) { uint8_t v = 1 + !!(h % 8); - db.add_block(mkblock(hf, h, v), 0, 0, 0, crypto::hash()); + db.add_block(mkblock(hf, h, v), 0, 0, 0, 0, crypto::hash()); bool ret = hf.add(db.get_block_from_height(h), h); if (h >= 8 && threshold == 87) { // for threshold 87, we reach the treshold at height 7, so from height 8, hard fork to version 2, but 8 tries to add 1 @@ -482,7 +484,7 @@ TEST(voting, different_thresholds) static const uint8_t expected_versions[] = { 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4 }; for (uint64_t h = 0; h < sizeof(block_versions) / sizeof(block_versions[0]); ++h) { - db.add_block(mkblock(hf, h, block_versions[h]), 0, 0, 0, crypto::hash()); + db.add_block(mkblock(hf, h, block_versions[h]), 0, 0, 0, 0, crypto::hash()); bool ret = hf.add(db.get_block_from_height(h), h); ASSERT_EQ(ret, true); } @@ -536,7 +538,7 @@ TEST(voting, info) ASSERT_EQ(expected_thresholds[h], threshold); ASSERT_EQ(4, voting); - db.add_block(mkblock(hf, h, block_versions[h]), 0, 0, 0, crypto::hash()); + db.add_block(mkblock(hf, h, block_versions[h]), 0, 0, 0, 0, crypto::hash()); ASSERT_TRUE(hf.add(db.get_block_from_height(h), h)); } } @@ -599,7 +601,7 @@ TEST(reorganize, changed) #define ADD(v, h, a) \ do { \ cryptonote::block b = mkblock(hf, h, v); \ - db.add_block(b, 0, 0, 0, crypto::hash()); \ + db.add_block(b, 0, 0, 0, 0, crypto::hash()); \ ASSERT_##a(hf.add(b, h)); \ } while(0) #define ADD_TRUE(v, h) ADD(v, h, TRUE) From 979105b2989746f1a426588862127d28c418f124 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 11 Jul 2018 22:02:28 +0100 Subject: [PATCH 0185/1404] abstract_tcp_server2: fix race on shutdown --- contrib/epee/include/net/abstract_tcp_server2.h | 1 + contrib/epee/include/net/abstract_tcp_server2.inl | 9 ++++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/contrib/epee/include/net/abstract_tcp_server2.h b/contrib/epee/include/net/abstract_tcp_server2.h index 7ca6ac872f9..b2c05ebb0b0 100644 --- a/contrib/epee/include/net/abstract_tcp_server2.h +++ b/contrib/epee/include/net/abstract_tcp_server2.h @@ -158,6 +158,7 @@ namespace net_utils std::list > > m_self_refs; // add_ref/release support critical_section m_self_refs_lock; critical_section m_chunking_lock; // held while we add small chunks of the big do_send() to small do_send_chunk() + critical_section m_shutdown_lock; // held while shutting down t_connection_type m_connection_type; diff --git a/contrib/epee/include/net/abstract_tcp_server2.inl b/contrib/epee/include/net/abstract_tcp_server2.inl index 5b3550005f9..59a1261638c 100644 --- a/contrib/epee/include/net/abstract_tcp_server2.inl +++ b/contrib/epee/include/net/abstract_tcp_server2.inl @@ -649,6 +649,10 @@ PRAGMA_WARNING_DISABLE_VS(4355) template bool connection::shutdown() { + CRITICAL_REGION_BEGIN(m_shutdown_lock); + if (m_was_shutdown) + return true; + m_was_shutdown = true; // Initiate graceful connection closure. m_timer.cancel(); boost::system::error_code ignored_ec; @@ -658,7 +662,7 @@ PRAGMA_WARNING_DISABLE_VS(4355) try { host_count(m_host, -1); } catch (...) { /* ignore */ } m_host = ""; } - m_was_shutdown = true; + CRITICAL_REGION_END(); m_protocol_handler.release_protocol(); return true; } @@ -667,6 +671,9 @@ PRAGMA_WARNING_DISABLE_VS(4355) bool connection::close() { TRY_ENTRY(); + auto self = safe_shared_from_this(); + if(!self) + return false; //_info("[sock " << socket_.native_handle() << "] Que Shutdown called."); m_timer.cancel(); size_t send_que_size = 0; From 63e342be8452510abb67bfe24df90a0972a4edfd Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 15 Jul 2018 12:59:58 +0100 Subject: [PATCH 0186/1404] crypto: move null_pkey/null_skey to the cpp file --- src/crypto/crypto.cpp | 3 +++ src/crypto/crypto.h | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/crypto/crypto.cpp b/src/crypto/crypto.cpp index f4ef751d388..36b510a5b00 100644 --- a/src/crypto/crypto.cpp +++ b/src/crypto/crypto.cpp @@ -70,6 +70,9 @@ namespace crypto { #include "random.h" } + const crypto::public_key null_pkey = boost::value_initialized(); + const crypto::secret_key null_skey = boost::value_initialized(); + static inline unsigned char *operator &(ec_point &point) { return &reinterpret_cast(point); } diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h index 9ea0f2ec0cf..492bf966e12 100644 --- a/src/crypto/crypto.h +++ b/src/crypto/crypto.h @@ -277,8 +277,8 @@ namespace crypto { epee::to_hex::formatted(o, epee::as_byte_span(v)); return o; } - const static crypto::public_key null_pkey = boost::value_initialized(); - const static crypto::secret_key null_skey = boost::value_initialized(); + const extern crypto::public_key null_pkey; + const extern crypto::secret_key null_skey; } CRYPTO_MAKE_HASHABLE(public_key) From 7f8bdeb35c73c70b2b65e30aa2a1cb93696355b3 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 15 Jul 2018 12:57:45 +0100 Subject: [PATCH 0187/1404] easylogging++: make the logger handle early/late logging --- external/easylogging++/easylogging++.cc | 12 ++++++++++++ external/easylogging++/easylogging++.h | 9 +++++---- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/external/easylogging++/easylogging++.cc b/external/easylogging++/easylogging++.cc index 8a5dc91e956..b438fa543a7 100644 --- a/external/easylogging++/easylogging++.cc +++ b/external/easylogging++/easylogging++.cc @@ -2092,6 +2092,17 @@ void VRegistry::setFromArgs(const base::utils::CommandLineArgs* commandLineArgs) # define ELPP_DEFAULT_LOGGING_FLAGS 0x0 #endif // !defined(ELPP_DEFAULT_LOGGING_FLAGS) // Storage +el::base::type::StoragePointer getresetELPP(bool reset) +{ + static el::base::type::StoragePointer p(new el::base::Storage(el::LogBuilderPtr(new el::base::DefaultLogBuilder()))); + if (reset) + p = NULL; + return p; +} +el::base::type::StoragePointer el::base::Storage::getELPP() +{ + return getresetELPP(false); +} #if ELPP_ASYNC_LOGGING Storage::Storage(const LogBuilderPtr& defaultLogBuilder, base::IWorker* asyncDispatchWorker) : #else @@ -2140,6 +2151,7 @@ Storage::Storage(const LogBuilderPtr& defaultLogBuilder) : Storage::~Storage(void) { ELPP_INTERNAL_INFO(4, "Destroying storage"); + getresetELPP(true); #if ELPP_ASYNC_LOGGING ELPP_INTERNAL_INFO(5, "Replacing log dispatch callback to synchronous"); uninstallLogDispatchCallback(std::string("AsyncLogDispatchCallback")); diff --git a/external/easylogging++/easylogging++.h b/external/easylogging++/easylogging++.h index 6b8b4fc35f3..193f835b029 100644 --- a/external/easylogging++/easylogging++.h +++ b/external/easylogging++/easylogging++.h @@ -2766,6 +2766,8 @@ class Storage : base::NoCopy, public base::threading::ThreadSafe { return it->second; } + static el::base::type::StoragePointer getELPP(); + private: base::RegisteredHitCounters* m_registeredHitCounters; base::RegisteredLoggers* m_registeredLoggers; @@ -2798,7 +2800,7 @@ class Storage : base::NoCopy, public base::threading::ThreadSafe { } }; extern ELPP_EXPORT base::type::StoragePointer elStorage; -#define ELPP el::base::elStorage +#define ELPP el::base::Storage::getELPP() class DefaultLogDispatchCallback : public LogDispatchCallback { protected: void handle(const LogDispatchData* data); @@ -4628,10 +4630,9 @@ el::base::debug::CrashHandler elCrashHandler(ELPP_USE_DEF_CRASH_HANDLER); \ } #if ELPP_ASYNC_LOGGING -# define INITIALIZE_EASYLOGGINGPP ELPP_INIT_EASYLOGGINGPP(new el::base::Storage(el::LogBuilderPtr(new el::base::DefaultLogBuilder()),\ -new el::base::AsyncDispatchWorker())) +# define INITIALIZE_EASYLOGGINGPP ELPP_INIT_EASYLOGGINGPP(NULL) #else -# define INITIALIZE_EASYLOGGINGPP ELPP_INIT_EASYLOGGINGPP(new el::base::Storage(el::LogBuilderPtr(new el::base::DefaultLogBuilder()))) +# define INITIALIZE_EASYLOGGINGPP ELPP_INIT_EASYLOGGINGPP(NULL) #endif // ELPP_ASYNC_LOGGING #define INITIALIZE_NULL_EASYLOGGINGPP \ namespace el {\ From d6440ab319ea6adf21485550d6a08ae452d49818 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 28 Apr 2018 12:11:42 +0100 Subject: [PATCH 0188/1404] wallet2: recover from index out of hashchain bounds error This can happen when there's a very large reorg on the daemon (ie, on testnet) --- src/wallet/wallet2.cpp | 36 ++++++++++++++++++++++++++++++++---- src/wallet/wallet2.h | 2 +- src/wallet/wallet_errors.h | 11 +++++++++++ 3 files changed, 44 insertions(+), 5 deletions(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 40a43b8e76a..66188b8239c 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -1823,7 +1823,7 @@ void wallet2::process_parsed_blocks(uint64_t start_height, const std::vector &short_chain_history) +void wallet2::fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height, std::list &short_chain_history, bool force) { std::vector hashes; const uint64_t checkpoint_height = m_checkpoints.get_max_height(); - if (stop_height > checkpoint_height && m_blockchain.size()-1 < checkpoint_height) + if ((stop_height > checkpoint_height && m_blockchain.size()-1 < checkpoint_height) && !force) { // we will drop all these, so don't bother getting them uint64_t missing_blocks = m_checkpoints.get_max_height() - m_blockchain.size(); @@ -2439,6 +2439,7 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo std::vector next_blocks; std::vector next_parsed_blocks; bool error = false; + added_blocks = 0; if (!first && blocks.empty()) { refreshed = false; @@ -2448,7 +2449,33 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo if (!first) { - process_parsed_blocks(blocks_start_height, blocks, parsed_blocks, added_blocks); + try + { + process_parsed_blocks(blocks_start_height, blocks, parsed_blocks, added_blocks); + } + catch (const tools::error::out_of_hashchain_bounds_error&) + { + MINFO("Daemon claims next refresh block is out of hash chain bounds, resetting hash chain"); + uint64_t stop_height = m_blockchain.offset(); + std::vector tip(m_blockchain.size() - m_blockchain.offset()); + for (size_t i = m_blockchain.offset(); i < m_blockchain.size(); ++i) + tip[i - m_blockchain.offset()] = m_blockchain[i]; + cryptonote::block b; + generate_genesis(b); + m_blockchain.clear(); + m_blockchain.push_back(get_block_hash(b)); + short_chain_history.clear(); + get_short_chain_history(short_chain_history); + fast_refresh(stop_height, blocks_start_height, short_chain_history, true); + THROW_WALLET_EXCEPTION_IF(m_blockchain.size() != stop_height, error::wallet_internal_error, "Unexpected hashchain size"); + THROW_WALLET_EXCEPTION_IF(m_blockchain.offset() != 0, error::wallet_internal_error, "Unexpected hashchain offset"); + for (const auto &h: tip) + m_blockchain.push_back(h); + short_chain_history.clear(); + get_short_chain_history(short_chain_history); + start_height = stop_height; + throw std::runtime_error(""); // loop again + } blocks_fetched += added_blocks; } waiter.wait(&tpool); @@ -2478,6 +2505,7 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo if(try_count < 3) { LOG_PRINT_L1("Another try pull_blocks (try_count=" << try_count << ")..."); + first = true; ++try_count; } else diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 4d22b69152c..e2a547ec3ef 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -1167,7 +1167,7 @@ namespace tools bool clear(); void pull_blocks(uint64_t start_height, uint64_t& blocks_start_height, const std::list &short_chain_history, std::vector &blocks, std::vector &o_indices); void pull_hashes(uint64_t start_height, uint64_t& blocks_start_height, const std::list &short_chain_history, std::vector &hashes); - void fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height, std::list &short_chain_history); + void fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height, std::list &short_chain_history, bool force = false); void pull_and_parse_next_blocks(uint64_t start_height, uint64_t &blocks_start_height, std::list &short_chain_history, const std::vector &prev_blocks, const std::vector &prev_parsed_blocks, std::vector &blocks, std::vector &parsed_blocks, bool &error); void process_parsed_blocks(uint64_t start_height, const std::vector &blocks, const std::vector &parsed_blocks, uint64_t& blocks_added); uint64_t select_transfers(uint64_t needed_money, std::vector unused_transfers_indices, std::vector& selected_transfers, bool trusted_daemon) const; diff --git a/src/wallet/wallet_errors.h b/src/wallet/wallet_errors.h index 214d51cde16..e80652750d4 100644 --- a/src/wallet/wallet_errors.h +++ b/src/wallet/wallet_errors.h @@ -70,6 +70,7 @@ namespace tools // get_out_indexes_error // tx_parse_error // get_tx_pool_error + // out_of_hashchain_bounds_error // transfer_error * // get_random_outs_general_error // not_enough_unlocked_money @@ -398,6 +399,16 @@ namespace tools std::string to_string() const { return refresh_error::to_string(); } }; //---------------------------------------------------------------------------------------------------- + struct out_of_hashchain_bounds_error : public refresh_error + { + explicit out_of_hashchain_bounds_error(std::string&& loc) + : refresh_error(std::move(loc), "Index out of bounds of of hashchain") + { + } + + std::string to_string() const { return refresh_error::to_string(); } + }; + //---------------------------------------------------------------------------------------------------- struct transfer_error : public wallet_logic_error { protected: From fead7ebab016c089e51f60d472740949d500d746 Mon Sep 17 00:00:00 2001 From: Jonathan Cross Date: Wed, 18 Jul 2018 20:07:05 +0200 Subject: [PATCH 0189/1404] Clarification of boolean options in config file --- contrib/snap/monerod.conf | 3 ++- utils/conf/monerod.conf | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/contrib/snap/monerod.conf b/contrib/snap/monerod.conf index 9b3d308ede0..1078e73b2da 100644 --- a/contrib/snap/monerod.conf +++ b/contrib/snap/monerod.conf @@ -1,8 +1,9 @@ # Configuration for monerod # Syntax: any command line option may be specified as 'clioptionname=value'. +# Boolean options such as 'no-igd' are specified as 'no-igd=1'. # See 'monerod --help' for all available options. -# Overrided by snap: +# Overridden by snap: # data-dir=/var/lib/monero # log-file=/var/log/monero/monero.log diff --git a/utils/conf/monerod.conf b/utils/conf/monerod.conf index 9b391dfa1c1..d019576c60b 100644 --- a/utils/conf/monerod.conf +++ b/utils/conf/monerod.conf @@ -1,5 +1,6 @@ # Configuration for monerod # Syntax: any command line option may be specified as 'clioptionname=value'. +# Boolean options such as 'no-igd' are specified as 'no-igd=1'. # See 'monerod --help' for all available options. data-dir=/var/lib/monero From f2e65c6e5092862094c3674cf5a692b9c31456b4 Mon Sep 17 00:00:00 2001 From: stoffu Date: Thu, 19 Jul 2018 20:26:19 +0900 Subject: [PATCH 0190/1404] wallet2: consider minimum fee when testing if balance is sufficient --- src/wallet/wallet2.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 40a43b8e76a..4597d442617 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -7675,6 +7675,7 @@ std::vector wallet2::create_transactions_2(std::vector wallet2::create_transactions_2(std::vector balance_subtotal, error::not_enough_money, + THROW_WALLET_EXCEPTION_IF(needed_money + min_fee > balance_subtotal, error::not_enough_money, balance_subtotal, needed_money, 0); // first check overall balance is enough, then unlocked one, so we throw distinct exceptions - THROW_WALLET_EXCEPTION_IF(needed_money > unlocked_balance_subtotal, error::not_enough_unlocked_money, + THROW_WALLET_EXCEPTION_IF(needed_money + min_fee > unlocked_balance_subtotal, error::not_enough_unlocked_money, unlocked_balance_subtotal, needed_money, 0); for (uint32_t i : subaddr_indices) From 7db7ec8591a3f1e870e158a0b1cf71765e8a835a Mon Sep 17 00:00:00 2001 From: stoffu Date: Fri, 20 Jul 2018 21:33:36 +0900 Subject: [PATCH 0191/1404] wallet rpc: support making integrated address of given standard address --- src/wallet/wallet_rpc_server.cpp | 34 +++++++++++++++++++- src/wallet/wallet_rpc_server_commands_defs.h | 2 ++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index b9cf996359d..60206a4a781 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -1255,7 +1255,39 @@ namespace tools } } - res.integrated_address = m_wallet->get_integrated_address_as_str(payment_id); + if (req.standard_address.empty()) + { + res.integrated_address = m_wallet->get_integrated_address_as_str(payment_id); + } + else + { + cryptonote::address_parse_info info; + if(!get_account_address_from_str(info, m_wallet->nettype(), req.standard_address)) + { + er.code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS; + er.message = "Invalid address"; + return false; + } + if (info.is_subaddress) + { + er.code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS; + er.message = "Subaddress shouldn't be used"; + return false; + } + if (info.has_payment_id) + { + er.code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS; + er.message = "Already integrated address"; + return false; + } + if (req.payment_id.empty()) + { + er.code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID; + er.message = "Payment ID shouldn't be left unspecified"; + return false; + } + res.integrated_address = get_account_integrated_address_as_str(m_wallet->nettype(), info.address, payment_id); + } res.payment_id = epee::string_tools::pod_to_hex(payment_id); return true; } diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h index 1bd572adda8..026c5d8ad67 100644 --- a/src/wallet/wallet_rpc_server_commands_defs.h +++ b/src/wallet/wallet_rpc_server_commands_defs.h @@ -914,9 +914,11 @@ namespace wallet_rpc { struct request { + std::string standard_address; std::string payment_id; BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(standard_address) KV_SERIALIZE(payment_id) END_KV_SERIALIZE_MAP() }; From 704b60caf046e98ad6289043ab5f713d2ec3d5a1 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 19 Jul 2018 15:36:07 +0100 Subject: [PATCH 0192/1404] block_queue: faster check whether a block was requested --- src/cryptonote_protocol/block_queue.cpp | 39 ++++++++++++++++--------- src/cryptonote_protocol/block_queue.h | 6 ++++ 2 files changed, 32 insertions(+), 13 deletions(-) diff --git a/src/cryptonote_protocol/block_queue.cpp b/src/cryptonote_protocol/block_queue.cpp index c39d67cebf4..05f4189fb60 100644 --- a/src/cryptonote_protocol/block_queue.cpp +++ b/src/cryptonote_protocol/block_queue.cpp @@ -57,7 +57,11 @@ void block_queue::add_blocks(uint64_t height, std::vectorconnection_id == connection_id && (all || j->blocks.size() == 0)) { - blocks.erase(j); + erase_block(j); } } } +void block_queue::erase_block(block_map::iterator j) +{ + CHECK_AND_ASSERT_THROW_MES(j != blocks.end(), "Invalid iterator"); + for (const crypto::hash &h: j->hashes) + requested_hashes.erase(h); + blocks.erase(j); +} + void block_queue::flush_stale_spans(const std::set &live_connections) { boost::unique_lock lock(mutex); @@ -92,7 +104,7 @@ void block_queue::flush_stale_spans(const std::set &live_con block_map::iterator j = i++; if (live_connections.find(j->connection_id) == live_connections.end() && j->blocks.size() == 0) { - blocks.erase(j); + erase_block(j); } } } @@ -106,7 +118,7 @@ bool block_queue::remove_span(uint64_t start_block_height, std::vectorhashes); - blocks.erase(i); + erase_block(i); return true; } } @@ -121,7 +133,7 @@ void block_queue::remove_spans(const boost::uuids::uuid &connection_id, uint64_t block_map::iterator j = i++; if (j->connection_id == connection_id && j->start_block_height <= start_block_height) { - blocks.erase(j); + erase_block(j); } } } @@ -160,16 +172,15 @@ std::string block_queue::get_overview() const return s; } +inline bool block_queue::requested_internal(const crypto::hash &hash) const +{ + return requested_hashes.find(hash) != requested_hashes.end(); +} + bool block_queue::requested(const crypto::hash &hash) const { boost::unique_lock lock(mutex); - for (const auto &span: blocks) - { - for (const auto &h: span.hashes) - if (h == hash) - return true; - } - return false; + return requested_internal(hash); } std::pair block_queue::reserve_span(uint64_t first_block_height, uint64_t last_block_height, uint64_t max_blocks, const boost::uuids::uuid &connection_id, const std::vector &block_hashes, boost::posix_time::ptime time) @@ -184,7 +195,7 @@ std::pair block_queue::reserve_span(uint64_t first_block_hei uint64_t span_start_height = last_block_height - block_hashes.size() + 1; std::vector::const_iterator i = block_hashes.begin(); - while (i != block_hashes.end() && requested(*i)) + while (i != block_hashes.end() && requested_internal(*i)) { ++i; ++span_start_height; @@ -256,8 +267,10 @@ void block_queue::set_span_hashes(uint64_t start_height, const boost::uuids::uui if (i->start_block_height == start_height && i->connection_id == connection_id) { span s = *i; - blocks.erase(i); + erase_block(i); s.hashes = std::move(hashes); + for (const crypto::hash &h: s.hashes) + requested_hashes.insert(h); blocks.insert(s); return; } diff --git a/src/cryptonote_protocol/block_queue.h b/src/cryptonote_protocol/block_queue.h index 9059e89acd6..9cce9507561 100644 --- a/src/cryptonote_protocol/block_queue.h +++ b/src/cryptonote_protocol/block_queue.h @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -92,8 +93,13 @@ namespace cryptonote bool foreach(std::function f, bool include_blockchain_placeholder = false) const; bool requested(const crypto::hash &hash) const; + private: + void erase_block(block_map::iterator j); + inline bool requested_internal(const crypto::hash &hash) const; + private: block_map blocks; mutable boost::recursive_mutex mutex; + std::unordered_set requested_hashes; }; } From 0496c7c91abbf8ec20cc70f383868957c6f9adc9 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 20 Jul 2018 22:59:18 +0100 Subject: [PATCH 0193/1404] crypto: do not use boost::value_initialized to init null skey/pkey --- src/crypto/crypto.cpp | 4 ++-- src/crypto/crypto.h | 1 - tests/unit_tests/crypto.cpp | 8 ++++++++ 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/crypto/crypto.cpp b/src/crypto/crypto.cpp index 36b510a5b00..a2db14e0855 100644 --- a/src/crypto/crypto.cpp +++ b/src/crypto/crypto.cpp @@ -70,8 +70,8 @@ namespace crypto { #include "random.h" } - const crypto::public_key null_pkey = boost::value_initialized(); - const crypto::secret_key null_skey = boost::value_initialized(); + const crypto::public_key null_pkey = crypto::public_key{}; + const crypto::secret_key null_skey = crypto::secret_key{}; static inline unsigned char *operator &(ec_point &point) { return &reinterpret_cast(point); diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h index 492bf966e12..073707876ea 100644 --- a/src/crypto/crypto.h +++ b/src/crypto/crypto.h @@ -34,7 +34,6 @@ #include #include #include -#include #include #include #include diff --git a/tests/unit_tests/crypto.cpp b/tests/unit_tests/crypto.cpp index 4bed061731f..9e168056841 100644 --- a/tests/unit_tests/crypto.cpp +++ b/tests/unit_tests/crypto.cpp @@ -73,3 +73,11 @@ TEST(Crypto, Ostream) EXPECT_TRUE(is_formatted()); EXPECT_TRUE(is_formatted()); } + +TEST(Crypto, null_keys) +{ + char zero[32]; + memset(zero, 0, 32); + ASSERT_EQ(memcmp(crypto::null_skey.data, zero, 32), 0); + ASSERT_EQ(memcmp(crypto::null_pkey.data, zero, 32), 0); +} From be02eb93899ff8db908b379d22a01450500e5214 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 20 Jul 2018 23:40:51 +0100 Subject: [PATCH 0194/1404] db_lmdb: demote the "batch already enabled" log, it's harmless --- src/blockchain_db/lmdb/db_lmdb.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp index 5d1679c0c8f..3576d08c474 100644 --- a/src/blockchain_db/lmdb/db_lmdb.cpp +++ b/src/blockchain_db/lmdb/db_lmdb.cpp @@ -2982,10 +2982,10 @@ void BlockchainLMDB::set_batch_transactions(bool batch_transactions) LOG_PRINT_L3("BlockchainLMDB::" << __func__); if ((batch_transactions) && (m_batch_transactions)) { - LOG_PRINT_L0("WARNING: batch transaction mode already enabled, but asked to enable batch mode"); + MINFO("batch transaction mode already enabled, but asked to enable batch mode"); } m_batch_transactions = batch_transactions; - LOG_PRINT_L3("batch transactions " << (m_batch_transactions ? "enabled" : "disabled")); + MINFO("batch transactions " << (m_batch_transactions ? "enabled" : "disabled")); } // return true if we started the txn, false if already started From 8e24533a7fbc9b4ace6fd5a31fe8d033918a64d6 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 21 Jul 2018 11:54:48 +0100 Subject: [PATCH 0195/1404] blockchain: some batch tx scanning speedup --- src/cryptonote_core/blockchain.cpp | 34 +++++++++++++++++------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 6e35744e6eb..959fb8cb606 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -4041,6 +4041,7 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vectorbatch_start(blocks_entry.size(), bytes))) { m_blockchain_lock.unlock(); @@ -4128,7 +4130,7 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector> offset_map; // [output] stores all output_data_t for each absolute_offset std::map> tx_map; + std::vector> txes(total_txs); #define SCAN_TABLE_QUIT(m) \ do { \ @@ -4214,6 +4217,7 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector= txes.size()) + SCAN_TABLE_QUIT("tx_index is out of sync"); + transaction &tx = txes[tx_index].first; + crypto::hash &tx_prefix_hash = txes[tx_index].second; + ++tx_index; - if (!parse_and_validate_tx_from_blob(tx_blob, tx, tx_hash, tx_prefix_hash)) + if (!parse_and_validate_tx_base_from_blob(tx_blob, tx)) SCAN_TABLE_QUIT("Could not parse tx from incoming blocks."); + cryptonote::get_transaction_prefix_hash(tx, tx_prefix_hash); auto its = m_scan_table.find(tx_prefix_hash); if (its != m_scan_table.end()) @@ -4312,9 +4319,8 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector= txes.size()) + SCAN_TABLE_QUIT("tx_index is out of sync"); + const transaction &tx = txes[tx_index].first; + const crypto::hash &tx_prefix_hash = txes[tx_index].second; + ++tx_index; - ++total_txs; auto its = m_scan_table.find(tx_prefix_hash); if (its == m_scan_table.end()) SCAN_TABLE_QUIT("Tx not found on scan table from incoming blocks."); From 5e675de7c28018cacbb432884f52867845823ab8 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 22 Jul 2018 11:28:11 +0100 Subject: [PATCH 0196/1404] simplewallet: fix language detection when using --use-english-language-names --- src/mnemonics/electrum-words.cpp | 62 +++++++------------------------ src/simplewallet/simplewallet.cpp | 10 +++-- 2 files changed, 20 insertions(+), 52 deletions(-) diff --git a/src/mnemonics/electrum-words.cpp b/src/mnemonics/electrum-words.cpp index 7dd09ecb96d..19a9c26bbca 100644 --- a/src/mnemonics/electrum-words.cpp +++ b/src/mnemonics/electrum-words.cpp @@ -70,6 +70,14 @@ #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "mnemonic" +namespace crypto +{ + namespace ElectrumWords + { + std::vector get_language_list(); + } +} + namespace { uint32_t create_checksum_index(const std::vector &word_list, @@ -376,56 +384,14 @@ namespace crypto if (len % 4 != 0 || len == 0) return false; - Language::Base *language; - if (language_name == "English") - { - language = Language::Singleton::instance(); - } - else if (language_name == "Nederlands") - { - language = Language::Singleton::instance(); - } - else if (language_name == "Français") - { - language = Language::Singleton::instance(); - } - else if (language_name == "Español") - { - language = Language::Singleton::instance(); - } - else if (language_name == "Português") + const Language::Base *language = NULL; + const std::vector language_list = crypto::ElectrumWords::get_language_list(); + for (const Language::Base *l: language_list) { - language = Language::Singleton::instance(); + if (language_name == l->get_language_name() || language_name == l->get_english_language_name()) + language = l; } - else if (language_name == "日本語") - { - language = Language::Singleton::instance(); - } - else if (language_name == "Italiano") - { - language = Language::Singleton::instance(); - } - else if (language_name == "Deutsch") - { - language = Language::Singleton::instance(); - } - else if (language_name == "русский язык") - { - language = Language::Singleton::instance(); - } - else if (language_name == "简体中文 (中国)") - { - language = Language::Singleton::instance(); - } - else if (language_name == "Esperanto") - { - language = Language::Singleton::instance(); - } - else if (language_name == "Lojban") - { - language = Language::Singleton::instance(); - } - else + if (!language) { return false; } diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 193b62571b6..751b8854528 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -3344,14 +3344,16 @@ bool simple_wallet::try_connect_to_daemon(bool silent, uint32_t* version) */ std::string simple_wallet::get_mnemonic_language() { - std::vector language_list; + std::vector language_list_self, language_list_english; + const std::vector &language_list = m_use_english_language_names ? language_list_english : language_list_self; std::string language_choice; int language_number = -1; - crypto::ElectrumWords::get_language_list(language_list, m_use_english_language_names); + crypto::ElectrumWords::get_language_list(language_list_self, false); + crypto::ElectrumWords::get_language_list(language_list_english, true); std::cout << tr("List of available languages for your wallet's seed:") << std::endl; std::cout << tr("If your display freezes, exit blind with ^C, then run again with --use-english-language-names") << std::endl; int ii; - std::vector::iterator it; + std::vector::const_iterator it; for (it = language_list.begin(), ii = 0; it != language_list.end(); it++, ii++) { std::cout << ii << " : " << *it << std::endl; @@ -3375,7 +3377,7 @@ std::string simple_wallet::get_mnemonic_language() fail_msg_writer() << tr("invalid language choice entered. Please try again.\n"); } } - return language_list[language_number]; + return language_list_self[language_number]; } //---------------------------------------------------------------------------------------------------- boost::optional simple_wallet::get_and_verify_password() const From 68b1197f5db8b4410c25c5188d666edb0c4d938c Mon Sep 17 00:00:00 2001 From: redfish Date: Sat, 21 Jul 2018 21:30:39 -0400 Subject: [PATCH 0197/1404] tests: ringdb: use system's temporary dir path Fixes failing test during Arch package build (due to attempt to write to ~/.bitmonero/...). Prefix temp dir path with "monero-" because we are not putting it on the system, so good to identify ourselves in case the dir gets left over due to crash, etc. --- tests/unit_tests/ringdb.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/unit_tests/ringdb.cpp b/tests/unit_tests/ringdb.cpp index d50d61b0fee..ef954fda2a8 100644 --- a/tests/unit_tests/ringdb.cpp +++ b/tests/unit_tests/ringdb.cpp @@ -76,13 +76,13 @@ class RingDB: public tools::ringdb private: std::string make_filename() { - boost::filesystem::path path = tools::get_default_data_dir(); - path /= "fake"; + boost::filesystem::path path = + boost::filesystem::temp_directory_path(); #if defined(__MINGW32__) || defined(__MINGW__) - filename = tempnam(path.string().c_str(), "ringdb-test-"); + filename = tempnam(path.string().c_str(), "monero-ringdb-test-"); EXPECT_TRUE(filename != NULL); #else - path /= "ringdb-test-XXXXXX"; + path /= "monero-ringdb-test-XXXXXX"; filename = strdup(path.string().c_str()); EXPECT_TRUE(mkdtemp(filename) != NULL); #endif From ed7825dc0c67d7c410a5effc1eca14bc5c77e257 Mon Sep 17 00:00:00 2001 From: jcktm Date: Fri, 13 Apr 2018 11:13:24 +1000 Subject: [PATCH 0198/1404] monero-wallet-cli: added locked_sweep_all command --- src/simplewallet/simplewallet.cpp | 54 ++++++++++++++++++++++++++++--- src/simplewallet/simplewallet.h | 3 +- 2 files changed, 51 insertions(+), 6 deletions(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 193b62571b6..6a6f98a20f3 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -2169,6 +2169,10 @@ simple_wallet::simple_wallet() boost::bind(&simple_wallet::locked_transfer, this, _1), tr("locked_transfer [index=[,,...]] [] [] []"), tr("Transfer to
and lock it for (max. 1000000). If the parameter \"index=[,,...]\" is specified, the wallet uses outputs received by addresses of those indices. If omitted, the wallet randomly chooses address indices to be used. In any case, it tries its best not to combine outputs across multiple addresses. is the priority of the transaction. The higher the priority, the higher the transaction fee. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command \"set priority\") is used. is the number of inputs to include for untraceability. Multiple payments can be made at once by adding etcetera (before the payment ID, if it's included)")); + m_cmd_binder.set_handler("locked_sweep_all", + boost::bind(&simple_wallet::locked_sweep_all, this, _1), + tr("locked_sweep_all [index=[,,...]] [] []
[]"), + tr("Send all unlocked balance to an address and lock it for (max. 1000000). If the parameter \"index[,,...]\" is specified, the wallet sweeps outputs received by those address indices. If omitted, the wallet randomly chooses an address index to be used. is the priority of the sweep. The higher the priority, the higher the transaction fee. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command \"set priority\") is used. is the number of inputs to include for untraceability.")); m_cmd_binder.set_handler("sweep_unmixable", boost::bind(&simple_wallet::sweep_unmixable, this, _1), tr("Send all unmixable outputs to yourself with ring_size 1")); @@ -4870,6 +4874,11 @@ bool simple_wallet::locked_transfer(const std::vector &args_) return transfer_main(TransferLocked, args_); } //---------------------------------------------------------------------------------------------------- +bool simple_wallet::locked_sweep_all(const std::vector &args_) +{ + return sweep_main(0, true, args_); +} +//---------------------------------------------------------------------------------------------------- bool simple_wallet::sweep_unmixable(const std::vector &args_) { @@ -4977,7 +4986,7 @@ bool simple_wallet::sweep_unmixable(const std::vector &args_) return true; } //---------------------------------------------------------------------------------------------------- -bool simple_wallet::sweep_main(uint64_t below, const std::vector &args_) +bool simple_wallet::sweep_main(uint64_t below, bool locked, const std::vector &args_) { auto print_usage = [below]() { @@ -5040,9 +5049,44 @@ bool simple_wallet::sweep_main(uint64_t below, const std::vector &a return true; } + uint64_t unlock_block = 0; + if (locked) { + uint64_t locked_blocks = 0; + + if (local_args.size() < 2) { + fail_msg_writer() << tr("missing lockedblocks parameter"); + return true; + } + + try + { + locked_blocks = boost::lexical_cast(local_args[1]); + } + catch (const std::exception &e) + { + fail_msg_writer() << tr("bad locked_blocks parameter"); + return true; + } + if (locked_blocks > 1000000) + { + fail_msg_writer() << tr("Locked blocks too high, max 1000000 (˜4 yrs)"); + return true; + } + std::string err; + uint64_t bc_height = get_daemon_blockchain_height(err); + if (!err.empty()) + { + fail_msg_writer() << tr("failed to get blockchain height: ") << err; + return true; + } + unlock_block = bc_height + locked_blocks; + + local_args.erase(local_args.begin() + 1); + } + std::vector extra; bool payment_id_seen = false; - if (2 >= local_args.size()) + if (local_args.size() >= 2) { std::string payment_id_str = local_args.back(); @@ -5124,7 +5168,7 @@ bool simple_wallet::sweep_main(uint64_t below, const std::vector &a try { // figure out what tx will be necessary - auto ptx_vector = m_wallet->create_transactions_all(below, info.address, info.is_subaddress, fake_outs_count, 0 /* unlock_time */, priority, extra, m_current_subaddress_account, subaddr_indices, is_daemon_trusted()); + auto ptx_vector = m_wallet->create_transactions_all(below, info.address, info.is_subaddress, fake_outs_count, unlock_block /* unlock_time */, priority, extra, m_current_subaddress_account, subaddr_indices, is_daemon_trusted()); if (ptx_vector.empty()) { @@ -5420,7 +5464,7 @@ bool simple_wallet::sweep_single(const std::vector &args_) //---------------------------------------------------------------------------------------------------- bool simple_wallet::sweep_all(const std::vector &args_) { - return sweep_main(0, args_); + return sweep_main(0, false, args_); } //---------------------------------------------------------------------------------------------------- bool simple_wallet::sweep_below(const std::vector &args_) @@ -5436,7 +5480,7 @@ bool simple_wallet::sweep_below(const std::vector &args_) fail_msg_writer() << tr("invalid amount threshold"); return true; } - return sweep_main(below, std::vector(++args_.begin(), args_.end())); + return sweep_main(below, false, std::vector(++args_.begin(), args_.end())); } //---------------------------------------------------------------------------------------------------- bool simple_wallet::donate(const std::vector &args_) diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index 1f27650553a..b8f7bb84b67 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -153,7 +153,8 @@ namespace cryptonote bool transfer(const std::vector &args); bool transfer_new(const std::vector &args); bool locked_transfer(const std::vector &args); - bool sweep_main(uint64_t below, const std::vector &args); + bool locked_sweep_all(const std::vector &args); + bool sweep_main(uint64_t below, bool locked, const std::vector &args); bool sweep_all(const std::vector &args); bool sweep_below(const std::vector &args); bool sweep_single(const std::vector &args); From 3a4c3ac89104323cb72df4e2102c4563c92be2df Mon Sep 17 00:00:00 2001 From: stoffu Date: Tue, 24 Jul 2018 13:32:58 +0900 Subject: [PATCH 0199/1404] simplewallet.unspent_outputs: fix wrong logic for parsing --- src/simplewallet/simplewallet.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 193b62571b6..cda5f4ddc46 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -6401,7 +6401,7 @@ bool simple_wallet::unspent_outputs(const std::vector &args_) auto local_args = args_; std::set subaddr_indices; - if (local_args.size() > 0 && local_args[0].substr(0, 6) != "index=") + if (local_args.size() > 0 && local_args[0].substr(0, 6) == "index=") { if (!parse_subaddress_indices(local_args[0], subaddr_indices)) return true; From 3940e12933c4ca223dace66a17ffda84a68a9475 Mon Sep 17 00:00:00 2001 From: stoffu Date: Tue, 24 Jul 2018 16:17:49 +0900 Subject: [PATCH 0200/1404] daemon.print_bc: don't print difficulty twice --- src/daemon/rpc_command_executor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp index e2b42c806da..45ba81e1687 100644 --- a/src/daemon/rpc_command_executor.cpp +++ b/src/daemon/rpc_command_executor.cpp @@ -557,7 +557,7 @@ bool t_rpc_command_executor::print_blockchain_info(uint64_t start_block_index, u if (!first) std::cout << std::endl; std::cout - << "height: " << header.height << ", timestamp: " << header.timestamp << ", difficulty: " << header.difficulty + << "height: " << header.height << ", timestamp: " << header.timestamp << ", size: " << header.block_size << ", transactions: " << header.num_txes << std::endl << "major version: " << (unsigned)header.major_version << ", minor version: " << (unsigned)header.minor_version << std::endl << "block id: " << header.hash << ", previous block id: " << header.prev_hash << std::endl From ff37bd00bccb895f3ea2315929deca5c176ab0b6 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 24 Jul 2018 14:33:33 +0100 Subject: [PATCH 0201/1404] wallet2: fix O(n^2) behaviour in import_key_images That takes a lot of time for even not so large wallets --- src/wallet/wallet2.cpp | 36 +++++++++++++++--------------------- 1 file changed, 15 insertions(+), 21 deletions(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index c2c02dd673c..2985e8d2f49 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -9870,6 +9870,17 @@ uint64_t wallet2::import_key_images(const std::vector spent_txids; // For each spent key image, search for a tx in m_transfers that uses it as input. std::vector swept_transfers; // If such a spending tx wasn't found in m_transfers, this means the spending tx // was created by sweep_all, so we can't know the spent height and other detailed info. + std::unordered_map spent_key_images; + + for (const transfer_details &td: m_transfers) + { + for (const cryptonote::txin_v& in : td.m_tx.vin) + { + if (in.type() == typeid(cryptonote::txin_to_key)) + spent_key_images.insert(std::make_pair(boost::get(in).k_image, td.m_txid)); + } + } + for(size_t i = 0; i < signed_key_images.size(); ++i) { transfer_details &td = m_transfers[i]; @@ -9883,28 +9894,11 @@ uint64_t wallet2::import_key_images(const std::vectorm_tx.vin) - { - if(in.type() == typeid(cryptonote::txin_to_key) && td.m_key_image == boost::get(in).k_image) - { - is_spent_tx = true; - break; - } - } - if (is_spent_tx) - { - is_spent_tx_found = true; - spent_txids.insert(it->m_txid); - break; - } - } - - if (!is_spent_tx_found) + const std::unordered_map::const_iterator skii = spent_key_images.find(td.m_key_image); + if (skii == spent_key_images.end()) swept_transfers.push_back(i); + else + spent_txids.insert(skii->second); } } MDEBUG("Total: " << print_money(spent) << " spent, " << print_money(unspent) << " unspent"); From c24a0af9f1a3c809ba6c11a703984fedabaa6e9f Mon Sep 17 00:00:00 2001 From: Jorropo Date: Wed, 25 Jul 2018 10:16:01 +0200 Subject: [PATCH 0202/1404] [monerod] Added blocks remaining count during syncronisation. And percent if usefull (% < 99) --- .../cryptonote_protocol_handler.inl | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl index 2e1df8078a3..2d4bc129942 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl @@ -1167,8 +1167,20 @@ skip: + " blocks/sec), " + std::to_string(m_block_queue.get_data_size() / 1048576.f) + " MB queued"; if (ELPP->vRegistry()->allowed(el::Level::Debug, "sync-info")) timing_message += std::string(": ") + m_block_queue.get_overview(); - MGINFO_YELLOW(context << " Synced " << m_core.get_current_blockchain_height() << "/" << m_core.get_target_blockchain_height() + if(m_core.get_target_blockchain_height() == 0){ + MGINFO_YELLOW(context << " Synced " << m_core.get_current_blockchain_height() << "/" << m_core.get_target_blockchain_height() << timing_message); + } else { + const int completition_percent = (m_core.get_current_blockchain_height() * 100 / m_core.get_target_blockchain_height()); + if(completition_percent < 99) {//printing completion percent only if % is < of 99 cause for 99 >= this is useless + MGINFO_YELLOW(context << " Synced " << m_core.get_current_blockchain_height() << "/" << m_core.get_target_blockchain_height() + << " (" << completition_percent << "% " << (m_core.get_target_blockchain_height() - m_core.get_current_blockchain_height()) + << " blocks remaining)" << timing_message); + } else { + MGINFO_YELLOW(context << " Synced " << m_core.get_current_blockchain_height() << "/" << m_core.get_target_blockchain_height() + << timing_message); + } + } } } } @@ -1747,3 +1759,4 @@ skip: m_core.stop(); } } // namespace + From e7c0fcd8f3f80f651c09935d05f2ff36a1870646 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 25 Jul 2018 23:00:19 +0100 Subject: [PATCH 0203/1404] epee: set jsonrpc to "2.0" in parse error return data --- contrib/epee/include/net/http_server_handlers_map2.h | 1 + 1 file changed, 1 insertion(+) diff --git a/contrib/epee/include/net/http_server_handlers_map2.h b/contrib/epee/include/net/http_server_handlers_map2.h index 429e3e1afdc..00a867d3e9c 100644 --- a/contrib/epee/include/net/http_server_handlers_map2.h +++ b/contrib/epee/include/net/http_server_handlers_map2.h @@ -122,6 +122,7 @@ if(!ps.load_from_json(query_info.m_body)) \ { \ boost::value_initialized rsp; \ + static_cast(rsp).jsonrpc = "2.0"; \ static_cast(rsp).error.code = -32700; \ static_cast(rsp).error.message = "Parse error"; \ epee::serialization::store_t_to_json(static_cast(rsp), response_info.m_body); \ From 262e391fb168ce2ac5f41869125208526a811891 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 26 Jul 2018 14:50:23 +0100 Subject: [PATCH 0204/1404] mlog: handle filenames without parent directories eg, --log-file=foo.log This would otherwise throw and crash with a stack overflow --- contrib/epee/src/mlog.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/contrib/epee/src/mlog.cpp b/contrib/epee/src/mlog.cpp index e8248c958cb..818fc0a69aa 100644 --- a/contrib/epee/src/mlog.cpp +++ b/contrib/epee/src/mlog.cpp @@ -142,7 +142,9 @@ void mlog_configure(const std::string &filename_base, bool console, const std::s { std::vector found_files; const boost::filesystem::directory_iterator end_itr; - for (boost::filesystem::directory_iterator iter(boost::filesystem::path(filename_base).parent_path()); iter != end_itr; ++iter) + const boost::filesystem::path filename_base_path(filename_base); + const boost::filesystem::path parent_path = filename_base_path.has_parent_path() ? filename_base_path.parent_path() : "."; + for (boost::filesystem::directory_iterator iter(parent_path); iter != end_itr; ++iter) { const std::string filename = iter->path().string(); if (filename.size() >= filename_base.size() && std::memcmp(filename.data(), filename_base.data(), filename_base.size()) == 0) From c8226ad1f73376edd51716bf29ace5c914a5a751 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 28 Jul 2018 19:39:05 +0100 Subject: [PATCH 0205/1404] unit_tests: use valid key images, pub keys, etc --- tests/unit_tests/ringdb.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/tests/unit_tests/ringdb.cpp b/tests/unit_tests/ringdb.cpp index d50d61b0fee..10e6c46c9fe 100644 --- a/tests/unit_tests/ringdb.cpp +++ b/tests/unit_tests/ringdb.cpp @@ -39,25 +39,29 @@ #include "crypto/crypto.h" #include "crypto/random.h" #include "crypto/chacha.h" +#include "ringct/rctOps.h" +#include "cryptonote_basic/cryptonote_basic.h" #include "wallet/ringdb.h" static crypto::chacha_key generate_chacha_key() { - uint8_t key[CHACHA_KEY_SIZE]; - crypto::rand(CHACHA_KEY_SIZE, key); crypto::chacha_key chacha_key; - memcpy(&chacha_key, key, CHACHA_KEY_SIZE); + uint64_t password = crypto::rand(); + crypto::generate_chacha_key(std::string((const char*)&password, sizeof(password)), chacha_key); return chacha_key; } static crypto::key_image generate_key_image() { - return crypto::rand(); + crypto::key_image key_image; + cryptonote::keypair keypair = cryptonote::keypair::generate(hw::get_device("default")); + crypto::generate_key_image(keypair.pub, keypair.sec, key_image); + return key_image; } static crypto::public_key generate_output() { - return crypto::rand(); + return rct::rct2pk(rct::scalarmultBase(rct::skGen())); } From 5860611afa888d4644537e3c888eecc93932cae1 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 30 Jul 2018 11:14:02 +0100 Subject: [PATCH 0206/1404] blockchain_blackball: allow resumable interrupt with ^C --- src/blockchain_utilities/blockchain_blackball.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/blockchain_utilities/blockchain_blackball.cpp b/src/blockchain_utilities/blockchain_blackball.cpp index 52d2938cd85..65b6384dc55 100644 --- a/src/blockchain_utilities/blockchain_blackball.cpp +++ b/src/blockchain_utilities/blockchain_blackball.cpp @@ -404,6 +404,11 @@ int main(int argc, char* argv[]) cryptonote::block b = core_storage[0]->get_db().get_block_from_height(0); tools::ringdb ringdb(output_file_path.string(), epee::string_tools::pod_to_hex(get_block_hash(b))); + bool stop_requested = false; + tools::signal_handler::install([&stop_requested](int type) { + stop_requested = true; + }); + for (size_t n = 0; n < inputs.size(); ++n) { const std::string canonical = boost::filesystem::canonical(inputs[n]).string(); @@ -489,9 +494,17 @@ int main(int argc, char* argv[]) } state.relative_rings[txin.k_image] = new_ring; } + if (stop_requested) + { + MINFO("Stopping scan, secondary passes will still happen..."); + return false; + } return true; }); + LOG_PRINT_L0("blockchain from " << inputs[n] << " processed still height " << start_idx); state.processed_heights[canonical] = start_idx; + if (stop_requested) + break; } while (!newly_spent.empty()) From f13c7a8263139afe127f3c8cf34277334e24700a Mon Sep 17 00:00:00 2001 From: stoffu Date: Mon, 30 Jul 2018 20:39:05 +0900 Subject: [PATCH 0207/1404] simplewallet: make sure wallet config is stored right after creation --- src/simplewallet/simplewallet.cpp | 60 ++++++++++++++++++------------- src/simplewallet/simplewallet.h | 8 ++--- 2 files changed, 40 insertions(+), 28 deletions(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 193b62571b6..4b720b0290a 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -2806,6 +2806,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) m_recovery_key = cryptonote::decrypt_key(m_recovery_key, seed_pass); } } + epee::wipeable_string password; if (!m_generate_from_view_key.empty()) { m_wallet_file = m_generate_from_view_key; @@ -2858,8 +2859,9 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) return false; } - bool r = new_wallet(vm, info.address, boost::none, viewkey); + auto r = new_wallet(vm, info.address, boost::none, viewkey); CHECK_AND_ASSERT_MES(r, false, tr("account creation failed")); + password = *r; } else if (!m_generate_from_spend_key.empty()) { @@ -2877,8 +2879,9 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) fail_msg_writer() << tr("failed to parse spend key secret key"); return false; } - bool r = new_wallet(vm, m_recovery_key, true, false, ""); + auto r = new_wallet(vm, m_recovery_key, true, false, ""); CHECK_AND_ASSERT_MES(r, false, tr("account creation failed")); + password = *r; } else if (!m_generate_from_keys.empty()) { @@ -2955,8 +2958,9 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) fail_msg_writer() << tr("view key does not match standard address"); return false; } - bool r = new_wallet(vm, info.address, spendkey, viewkey); + auto r = new_wallet(vm, info.address, spendkey, viewkey); CHECK_AND_ASSERT_MES(r, false, tr("account creation failed")); + password = *r; } // Asks user for all the data required to merge secret keys from multisig wallets into one master wallet, which then gets full control of the multisig wallet. The resulting wallet will be the same as any other regular wallet. @@ -3089,8 +3093,9 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) } // create wallet - bool r = new_wallet(vm, info.address, spendkey, viewkey); + auto r = new_wallet(vm, info.address, spendkey, viewkey); CHECK_AND_ASSERT_MES(r, false, tr("account creation failed")); + password = *r; } else if (!m_generate_from_json.empty()) @@ -3112,8 +3117,9 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) { m_wallet_file = m_generate_from_device; // create wallet - bool r = new_wallet(vm, "Ledger"); + auto r = new_wallet(vm, "Ledger"); CHECK_AND_ASSERT_MES(r, false, tr("account creation failed")); + password = *r; // if no block_height is specified, assume its a new account and start it "now" if(m_wallet->get_refresh_from_block_height() == 0) { { @@ -3138,12 +3144,13 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) return false; } m_wallet_file = m_generate_new; - bool r; + boost::optional r; if (m_restore_multisig_wallet) r = new_wallet(vm, multisig_keys, old_language); else r = new_wallet(vm, m_recovery_key, m_restore_deterministic_wallet, m_non_deterministic, old_language); CHECK_AND_ASSERT_MES(r, false, tr("account creation failed")); + password = *r; } if (m_restoring && m_generate_from_json.empty() && m_generate_from_device.empty()) @@ -3225,6 +3232,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) } m_wallet->set_refresh_from_block_height(m_restore_height); } + m_wallet->rewrite(m_wallet_file, password); } else { @@ -3392,15 +3400,16 @@ boost::optional simple_wallet::get_and_verify_passwor return pwd_container; } //---------------------------------------------------------------------------------------------------- -bool simple_wallet::new_wallet(const boost::program_options::variables_map& vm, +boost::optional simple_wallet::new_wallet(const boost::program_options::variables_map& vm, const crypto::secret_key& recovery_key, bool recover, bool two_random, const std::string &old_language) { auto rc = tools::wallet2::make_new(vm, password_prompter); m_wallet = std::move(rc.first); if (!m_wallet) { - return false; + return {}; } + epee::wipeable_string password = rc.second.password(); if (!m_subaddress_lookahead.empty()) { @@ -3436,7 +3445,7 @@ bool simple_wallet::new_wallet(const boost::program_options::variables_map& vm, } mnemonic_language = get_mnemonic_language(); if (mnemonic_language.empty()) - return false; + return {}; } m_wallet->set_seed_language(mnemonic_language); @@ -3454,7 +3463,7 @@ bool simple_wallet::new_wallet(const boost::program_options::variables_map& vm, catch (const std::exception& e) { fail_msg_writer() << tr("failed to generate new wallet: ") << e.what(); - return false; + return {}; } // convert rng value to electrum-style word list @@ -3479,10 +3488,10 @@ bool simple_wallet::new_wallet(const boost::program_options::variables_map& vm, } success_msg_writer() << "**********************************************************************"; - return true; + return std::move(password); } //---------------------------------------------------------------------------------------------------- -bool simple_wallet::new_wallet(const boost::program_options::variables_map& vm, +boost::optional simple_wallet::new_wallet(const boost::program_options::variables_map& vm, const cryptonote::account_public_address& address, const boost::optional& spendkey, const crypto::secret_key& viewkey) { @@ -3490,8 +3499,9 @@ bool simple_wallet::new_wallet(const boost::program_options::variables_map& vm, m_wallet = std::move(rc.first); if (!m_wallet) { - return false; + return {}; } + epee::wipeable_string password = rc.second.password(); if (!m_subaddress_lookahead.empty()) { @@ -3521,22 +3531,23 @@ bool simple_wallet::new_wallet(const boost::program_options::variables_map& vm, catch (const std::exception& e) { fail_msg_writer() << tr("failed to generate new wallet: ") << e.what(); - return false; + return {}; } - return true; + return std::move(password); } //---------------------------------------------------------------------------------------------------- -bool simple_wallet::new_wallet(const boost::program_options::variables_map& vm, +boost::optional simple_wallet::new_wallet(const boost::program_options::variables_map& vm, const std::string &device_name) { auto rc = tools::wallet2::make_new(vm, password_prompter); m_wallet = std::move(rc.first); if (!m_wallet) { - return false; + return {}; } + epee::wipeable_string password = rc.second.password(); if (!m_subaddress_lookahead.empty()) { @@ -3557,21 +3568,22 @@ bool simple_wallet::new_wallet(const boost::program_options::variables_map& vm, catch (const std::exception& e) { fail_msg_writer() << tr("failed to generate new wallet: ") << e.what(); - return false; + return {}; } - return true; + return std::move(password); } //---------------------------------------------------------------------------------------------------- -bool simple_wallet::new_wallet(const boost::program_options::variables_map& vm, +boost::optional simple_wallet::new_wallet(const boost::program_options::variables_map& vm, const std::string &multisig_keys, const std::string &old_language) { auto rc = tools::wallet2::make_new(vm, password_prompter); m_wallet = std::move(rc.first); if (!m_wallet) { - return false; + return {}; } + epee::wipeable_string password = rc.second.password(); if (!m_subaddress_lookahead.empty()) { @@ -3601,7 +3613,7 @@ bool simple_wallet::new_wallet(const boost::program_options::variables_map& vm, if (!m_wallet->multisig(&ready, &threshold, &total) || !ready) { fail_msg_writer() << tr("failed to generate new mutlisig wallet"); - return false; + return {}; } message_writer(console_color_white, true) << boost::format(tr("Generated new %u/%u multisig wallet: ")) % threshold % total << m_wallet->get_account().get_public_address_str(m_wallet->nettype()); @@ -3609,10 +3621,10 @@ bool simple_wallet::new_wallet(const boost::program_options::variables_map& vm, catch (const std::exception& e) { fail_msg_writer() << tr("failed to generate new wallet: ") << e.what(); - return false; + return {}; } - return true; + return std::move(password); } //---------------------------------------------------------------------------------------------------- bool simple_wallet::open_wallet(const boost::program_options::variables_map& vm) diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index 1f27650553a..4de8c58a5da 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -91,13 +91,13 @@ namespace cryptonote //! \return Prompts user for password and verifies against local file. Logs on error and returns `none` boost::optional get_and_verify_password() const; - bool new_wallet(const boost::program_options::variables_map& vm, const crypto::secret_key& recovery_key, + boost::optional new_wallet(const boost::program_options::variables_map& vm, const crypto::secret_key& recovery_key, bool recover, bool two_random, const std::string &old_language); - bool new_wallet(const boost::program_options::variables_map& vm, const cryptonote::account_public_address& address, + boost::optional new_wallet(const boost::program_options::variables_map& vm, const cryptonote::account_public_address& address, const boost::optional& spendkey, const crypto::secret_key& viewkey); - bool new_wallet(const boost::program_options::variables_map& vm, + boost::optional new_wallet(const boost::program_options::variables_map& vm, const std::string &multisig_keys, const std::string &old_language); - bool new_wallet(const boost::program_options::variables_map& vm, const std::string& device_name); + boost::optional new_wallet(const boost::program_options::variables_map& vm, const std::string& device_name); bool open_wallet(const boost::program_options::variables_map& vm); bool close_wallet(); From a3fe1c56ee782caa54e3159738f19589855d504e Mon Sep 17 00:00:00 2001 From: stoffu Date: Sun, 29 Jul 2018 21:29:56 +0900 Subject: [PATCH 0208/1404] simplewallet: add set_tx_key for importing tx keys from 3rd party wallets --- src/simplewallet/simplewallet.cpp | 62 +++++++++++++++++++++++++++++++ src/simplewallet/simplewallet.h | 1 + src/wallet/wallet2.cpp | 48 ++++++++++++++++++++++++ src/wallet/wallet2.h | 1 + 4 files changed, 112 insertions(+) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 193b62571b6..43e8cb72f13 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -2298,6 +2298,10 @@ simple_wallet::simple_wallet() boost::bind(&simple_wallet::get_tx_key, this, _1), tr("get_tx_key "), tr("Get the transaction key (r) for a given .")); + m_cmd_binder.set_handler("set_tx_key", + boost::bind(&simple_wallet::set_tx_key, this, _1), + tr("set_tx_key "), + tr("Set the transaction key (r) for a given in case the tx was made by some other device or 3rd party wallet.")); m_cmd_binder.set_handler("check_tx_key", boost::bind(&simple_wallet::check_tx_key, this, _1), tr("check_tx_key
"), @@ -5765,6 +5769,64 @@ bool simple_wallet::get_tx_key(const std::vector &args_) } } //---------------------------------------------------------------------------------------------------- +bool simple_wallet::set_tx_key(const std::vector &args_) +{ + std::vector local_args = args_; + + if(local_args.size() != 2) { + fail_msg_writer() << tr("usage: set_tx_key "); + return true; + } + + crypto::hash txid; + if (!epee::string_tools::hex_to_pod(local_args[0], txid)) + { + fail_msg_writer() << tr("failed to parse txid"); + return true; + } + + crypto::secret_key tx_key; + std::vector additional_tx_keys; + try + { + if (!epee::string_tools::hex_to_pod(local_args[1].substr(0, 64), tx_key)) + { + fail_msg_writer() << tr("failed to parse tx_key"); + return true; + } + while(true) + { + local_args[1] = local_args[1].substr(64); + if (local_args[1].empty()) + break; + additional_tx_keys.resize(additional_tx_keys.size() + 1); + if (!epee::string_tools::hex_to_pod(local_args[1].substr(0, 64), additional_tx_keys.back())) + { + fail_msg_writer() << tr("failed to parse tx_key"); + return true; + } + } + } + catch (const std::out_of_range &e) + { + fail_msg_writer() << tr("failed to parse tx_key"); + return true; + } + + LOCK_IDLE_SCOPE(); + + try + { + m_wallet->set_tx_key(txid, tx_key, additional_tx_keys); + success_msg_writer() << tr("Tx key successfully stored."); + } + catch (const std::exception &e) + { + fail_msg_writer() << tr("Failed to store tx key: ") << e.what(); + } + return true; +} +//---------------------------------------------------------------------------------------------------- bool simple_wallet::get_tx_proof(const std::vector &args) { if (m_wallet->key_on_device()) diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index 1f27650553a..e78cdcc316c 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -176,6 +176,7 @@ namespace cryptonote bool rescan_spent(const std::vector &args); bool set_log(const std::vector &args); bool get_tx_key(const std::vector &args); + bool set_tx_key(const std::vector &args); bool check_tx_key(const std::vector &args); bool get_tx_proof(const std::vector &args); bool check_tx_proof(const std::vector &args); diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index c2c02dd673c..22826115097 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -8583,6 +8583,54 @@ bool wallet2::get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key, s return true; } //---------------------------------------------------------------------------------------------------- +void wallet2::set_tx_key(const crypto::hash &txid, const crypto::secret_key &tx_key, const std::vector &additional_tx_keys) +{ + // fetch tx from daemon and check if secret keys agree with corresponding public keys + COMMAND_RPC_GET_TRANSACTIONS::request req = AUTO_VAL_INIT(req); + req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txid)); + req.decode_as_json = false; + req.prune = false; + COMMAND_RPC_GET_TRANSACTIONS::response res = AUTO_VAL_INIT(res); + bool r; + { + const boost::lock_guard lock{m_daemon_rpc_mutex}; + r = epee::net_utils::invoke_http_json("/gettransactions", req, res, m_http_client, rpc_timeout); + } + THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "gettransactions"); + THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "gettransactions"); + THROW_WALLET_EXCEPTION_IF(res.status != CORE_RPC_STATUS_OK, error::wallet_internal_error, "gettransactions"); + THROW_WALLET_EXCEPTION_IF(res.txs.size() != 1, error::wallet_internal_error, + "daemon returned wrong response for gettransactions, wrong txs count = " + + std::to_string(res.txs.size()) + ", expected 1"); + cryptonote::blobdata bd; + THROW_WALLET_EXCEPTION_IF(!epee::string_tools::parse_hexstr_to_binbuff(res.txs[0].as_hex, bd), error::wallet_internal_error, "failed to parse tx from hexstr"); + cryptonote::transaction tx; + crypto::hash tx_hash, tx_prefix_hash; + THROW_WALLET_EXCEPTION_IF(!cryptonote::parse_and_validate_tx_from_blob(bd, tx, tx_hash, tx_prefix_hash), error::wallet_internal_error, "failed to parse tx from blob"); + THROW_WALLET_EXCEPTION_IF(tx_hash != txid, error::wallet_internal_error, "txid mismatch"); + std::vector tx_extra_fields; + THROW_WALLET_EXCEPTION_IF(!parse_tx_extra(tx.extra, tx_extra_fields), error::wallet_internal_error, "Transaction extra has unsupported format"); + tx_extra_pub_key pub_key_field; + bool found = false; + size_t index = 0; + while (find_tx_extra_field_by_type(tx_extra_fields, pub_key_field, index++)) + { + crypto::public_key calculated_pub_key; + crypto::secret_key_to_public_key(tx_key, calculated_pub_key); + if (calculated_pub_key == pub_key_field.pub_key) + { + found = true; + break; + } + } + THROW_WALLET_EXCEPTION_IF(!found, error::wallet_internal_error, "Given tx secret key doesn't agree with the tx public key in the blockchain"); + tx_extra_additional_pub_keys additional_tx_pub_keys; + find_tx_extra_field_by_type(tx_extra_fields, additional_tx_pub_keys); + THROW_WALLET_EXCEPTION_IF(additional_tx_keys.size() != additional_tx_pub_keys.data.size(), error::wallet_internal_error, "The number of additional tx secret keys doesn't agree with the number of additional tx public keys in the blockchain" ); + m_tx_keys.insert(std::make_pair(txid, tx_key)); + m_additional_tx_keys.insert(std::make_pair(txid, additional_tx_keys)); +} +//---------------------------------------------------------------------------------------------------- std::string wallet2::get_spend_proof(const crypto::hash &txid, const std::string &message) { THROW_WALLET_EXCEPTION_IF(m_watch_only, error::wallet_internal_error, diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index d4d76e66c67..7c74c580373 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -931,6 +931,7 @@ namespace tools void confirm_non_default_ring_size(bool always) { m_confirm_non_default_ring_size = always; } bool get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key, std::vector &additional_tx_keys) const; + void set_tx_key(const crypto::hash &txid, const crypto::secret_key &tx_key, const std::vector &additional_tx_keys); void check_tx_key(const crypto::hash &txid, const crypto::secret_key &tx_key, const std::vector &additional_tx_keys, const cryptonote::account_public_address &address, uint64_t &received, bool &in_pool, uint64_t &confirmations); void check_tx_key_helper(const crypto::hash &txid, const crypto::key_derivation &derivation, const std::vector &additional_derivations, const cryptonote::account_public_address &address, uint64_t &received, bool &in_pool, uint64_t &confirmations); std::string get_tx_proof(const crypto::hash &txid, const cryptonote::account_public_address &address, bool is_subaddress, const std::string &message); From 4520cfd9783bb2ea6989c618606afc9cee986452 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 30 Jul 2018 13:15:41 +0100 Subject: [PATCH 0209/1404] wallet2: guard against bad outputs in import_outputs also some minor speedup --- src/wallet/wallet2.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 90807803af2..b4b86057f89 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -10172,18 +10172,20 @@ size_t wallet2::import_outputs(const std::vector additional_tx_pub_keys = get_additional_tx_pub_keys_from_extra(td.m_tx); + THROW_WALLET_EXCEPTION_IF(td.m_tx.vout[td.m_internal_output_index].target.type() != typeid(cryptonote::txout_to_key), + error::wallet_internal_error, "Unsupported output type"); const crypto::public_key& out_key = boost::get(td.m_tx.vout[td.m_internal_output_index].target).key; bool r = cryptonote::generate_key_image_helper(m_account.get_keys(), m_subaddresses, out_key, tx_pub_key, additional_tx_pub_keys, td.m_internal_output_index, in_ephemeral, td.m_key_image, m_account.get_device()); THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key image"); expand_subaddresses(td.m_subaddr_index); td.m_key_image_known = true; td.m_key_image_partial = false; - THROW_WALLET_EXCEPTION_IF(in_ephemeral.pub != boost::get(td.m_tx.vout[td.m_internal_output_index].target).key, + THROW_WALLET_EXCEPTION_IF(in_ephemeral.pub != out_key, error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key at index " + boost::lexical_cast(i)); m_key_images[td.m_key_image] = m_transfers.size(); m_pub_keys[td.get_public_key()] = m_transfers.size(); - m_transfers.push_back(td); + m_transfers.push_back(std::move(td)); } return m_transfers.size(); From 0f757177fe9b8128d2507a8d57911fb51d667696 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 30 Jul 2018 13:33:02 +0100 Subject: [PATCH 0210/1404] wallet2: avoid using arbitrary random values when unknown --- src/wallet/wallet2.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 90807803af2..ed09640045d 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -10056,11 +10056,10 @@ uint64_t wallet2::import_key_images(const std::vector(); // spent txid is unknown, so hypothetically set to random + pd.m_block_height = 0; // spent block height is unknown + const crypto::hash &spent_txid = crypto::null_hash; // spent txid is unknown m_confirmed_txs.insert(std::make_pair(spent_txid, pd)); } } From 888324fa57dea0fba1138c4347c31a33b4241090 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 16 Jul 2018 16:27:52 +0100 Subject: [PATCH 0211/1404] blockchain_ancestry: finds all ancestors of a tx, block, or chain --- src/blockchain_utilities/CMakeLists.txt | 32 + .../blockchain_ancestry.cpp | 634 ++++++++++++++++++ 2 files changed, 666 insertions(+) create mode 100644 src/blockchain_utilities/blockchain_ancestry.cpp diff --git a/src/blockchain_utilities/CMakeLists.txt b/src/blockchain_utilities/CMakeLists.txt index 338ec3e4b12..0ff768143f6 100644 --- a/src/blockchain_utilities/CMakeLists.txt +++ b/src/blockchain_utilities/CMakeLists.txt @@ -91,6 +91,17 @@ monero_private_headers(blockchain_usage +set(blockchain_ancestry_sources + blockchain_ancestry.cpp + ) + +set(blockchain_ancestry_private_headers) + +monero_private_headers(blockchain_ancestry + ${blockchain_ancestry_private_headers}) + + + monero_add_executable(blockchain_import ${blockchain_import_sources} ${blockchain_import_private_headers} @@ -183,3 +194,24 @@ set_property(TARGET blockchain_usage OUTPUT_NAME "monero-blockchain-usage") install(TARGETS blockchain_usage DESTINATION bin) +monero_add_executable(blockchain_ancestry + ${blockchain_ancestry_sources} + ${blockchain_ancestry_private_headers}) + +target_link_libraries(blockchain_ancestry + PRIVATE + cryptonote_core + blockchain_db + version + epee + ${Boost_FILESYSTEM_LIBRARY} + ${Boost_SYSTEM_LIBRARY} + ${Boost_THREAD_LIBRARY} + ${CMAKE_THREAD_LIBS_INIT} + ${EXTRA_LIBRARIES}) + +set_property(TARGET blockchain_ancestry + PROPERTY + OUTPUT_NAME "monero-blockchain-ancestry") +install(TARGETS blockchain_ancestry DESTINATION bin) + diff --git a/src/blockchain_utilities/blockchain_ancestry.cpp b/src/blockchain_utilities/blockchain_ancestry.cpp new file mode 100644 index 00000000000..0de1c69df2a --- /dev/null +++ b/src/blockchain_utilities/blockchain_ancestry.cpp @@ -0,0 +1,634 @@ +// Copyright (c) 2014-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include +#include +#include +#include +#include "common/command_line.h" +#include "common/varint.h" +#include "cryptonote_core/tx_pool.h" +#include "cryptonote_core/cryptonote_core.h" +#include "cryptonote_core/blockchain.h" +#include "blockchain_db/blockchain_db.h" +#include "blockchain_db/db_types.h" +#include "version.h" + +#undef MONERO_DEFAULT_LOG_CATEGORY +#define MONERO_DEFAULT_LOG_CATEGORY "bcutil" + +namespace po = boost::program_options; +using namespace epee; +using namespace cryptonote; + +struct ancestor +{ + uint64_t amount; + uint64_t offset; + + bool operator==(const ancestor &other) const { return amount == other.amount && offset == other.offset; } +}; + +namespace std +{ + template<> struct hash + { + size_t operator()(const ancestor &a) const + { + crypto::hash h; + crypto::cn_fast_hash(&a, sizeof(a), h); + return reinterpret_cast(h); + } + }; +} + +static void add_ancestor(std::unordered_map &ancestry, uint64_t amount, uint64_t offset) +{ + std::pair::iterator, bool> p = ancestry.insert(std::make_pair(ancestor{amount, offset}, 1)); + if (!p.second) + { + ++p.first->second; + } +} + +static size_t get_full_ancestry(const std::unordered_map &ancestry) +{ + size_t count = 0; + for (const auto &i: ancestry) + count += i.second; + return count; +} + +static size_t get_deduplicated_ancestry(const std::unordered_map &ancestry) +{ + return ancestry.size(); +} + +static void add_ancestry(std::unordered_map> &ancestry, const crypto::hash &txid, const std::unordered_set &ancestors) +{ + std::pair>::iterator, bool> p = ancestry.insert(std::make_pair(txid, ancestors)); + if (!p.second) + { + for (const auto &e: ancestors) + p.first->second.insert(e); + } +} + +static void add_ancestry(std::unordered_map> &ancestry, const crypto::hash &txid, const ancestor &new_ancestor) +{ + std::pair>::iterator, bool> p = ancestry.insert(std::make_pair(txid, std::unordered_set())); + p.first->second.insert(new_ancestor); +} + +static std::unordered_set get_ancestry(const std::unordered_map> &ancestry, const crypto::hash &txid) +{ + std::unordered_map>::const_iterator i = ancestry.find(txid); + if (i == ancestry.end()) + { + //MERROR("txid ancestry not found: " << txid); + //throw std::runtime_error("txid ancestry not found"); + return std::unordered_set(); + } + return i->second; +} + +int main(int argc, char* argv[]) +{ + TRY_ENTRY(); + + epee::string_tools::set_module_name_and_folder(argv[0]); + + std::string default_db_type = "lmdb"; + + std::string available_dbs = cryptonote::blockchain_db_types(", "); + available_dbs = "available: " + available_dbs; + + uint32_t log_level = 0; + + tools::on_startup(); + + boost::filesystem::path output_file_path; + + po::options_description desc_cmd_only("Command line options"); + po::options_description desc_cmd_sett("Command line options and settings options"); + const command_line::arg_descriptor arg_log_level = {"log-level", "0-4 or categories", ""}; + const command_line::arg_descriptor arg_database = { + "database", available_dbs.c_str(), default_db_type + }; + const command_line::arg_descriptor arg_txid = {"txid", "Get ancestry for this txid", ""}; + const command_line::arg_descriptor arg_height = {"height", "Get ancestry for all txes at this height", 0}; + const command_line::arg_descriptor arg_all = {"all", "Include the whole chain", false}; + const command_line::arg_descriptor arg_cache_outputs = {"cache-outputs", "Cache outputs (memory hungry)", false}; + const command_line::arg_descriptor arg_cache_txes = {"cache-txes", "Cache txes (memory hungry)", false}; + const command_line::arg_descriptor arg_cache_blocks = {"cache-blocks", "Cache blocks (memory hungry)", false}; + const command_line::arg_descriptor arg_include_coinbase = {"include-coinbase", "Including coinbase tx", false}; + const command_line::arg_descriptor arg_show_cache_stats = {"show-cache-stats", "Show cache statistics", false}; + + command_line::add_arg(desc_cmd_sett, cryptonote::arg_data_dir); + command_line::add_arg(desc_cmd_sett, cryptonote::arg_testnet_on); + command_line::add_arg(desc_cmd_sett, cryptonote::arg_stagenet_on); + command_line::add_arg(desc_cmd_sett, arg_log_level); + command_line::add_arg(desc_cmd_sett, arg_database); + command_line::add_arg(desc_cmd_sett, arg_txid); + command_line::add_arg(desc_cmd_sett, arg_height); + command_line::add_arg(desc_cmd_sett, arg_all); + command_line::add_arg(desc_cmd_sett, arg_cache_outputs); + command_line::add_arg(desc_cmd_sett, arg_cache_txes); + command_line::add_arg(desc_cmd_sett, arg_cache_blocks); + command_line::add_arg(desc_cmd_sett, arg_include_coinbase); + command_line::add_arg(desc_cmd_sett, arg_show_cache_stats); + command_line::add_arg(desc_cmd_only, command_line::arg_help); + + po::options_description desc_options("Allowed options"); + desc_options.add(desc_cmd_only).add(desc_cmd_sett); + + po::variables_map vm; + bool r = command_line::handle_error_helper(desc_options, [&]() + { + auto parser = po::command_line_parser(argc, argv).options(desc_options); + po::store(parser.run(), vm); + po::notify(vm); + return true; + }); + if (! r) + return 1; + + if (command_line::get_arg(vm, command_line::arg_help)) + { + std::cout << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")" << ENDL << ENDL; + std::cout << desc_options << std::endl; + return 1; + } + + mlog_configure(mlog_get_default_log_path("monero-blockchain-ancestry.log"), true); + if (!command_line::is_arg_defaulted(vm, arg_log_level)) + mlog_set_log(command_line::get_arg(vm, arg_log_level).c_str()); + else + mlog_set_log(std::string(std::to_string(log_level) + ",bcutil:INFO").c_str()); + + LOG_PRINT_L0("Starting..."); + + std::string opt_data_dir = command_line::get_arg(vm, cryptonote::arg_data_dir); + bool opt_testnet = command_line::get_arg(vm, cryptonote::arg_testnet_on); + bool opt_stagenet = command_line::get_arg(vm, cryptonote::arg_stagenet_on); + network_type net_type = opt_testnet ? TESTNET : opt_stagenet ? STAGENET : MAINNET; + std::string opt_txid_string = command_line::get_arg(vm, arg_txid); + uint64_t opt_height = command_line::get_arg(vm, arg_height); + bool opt_all = command_line::get_arg(vm, arg_all); + bool opt_cache_outputs = command_line::get_arg(vm, arg_cache_outputs); + bool opt_cache_txes = command_line::get_arg(vm, arg_cache_txes); + bool opt_cache_blocks = command_line::get_arg(vm, arg_cache_blocks); + bool opt_include_coinbase = command_line::get_arg(vm, arg_include_coinbase); + bool opt_show_cache_stats = command_line::get_arg(vm, arg_show_cache_stats); + + if ((!opt_txid_string.empty()) + !!opt_height + !!opt_all > 1) + { + std::cerr << "Only one of --txid, --height and --all can be given" << std::endl; + return 1; + } + crypto::hash opt_txid = crypto::null_hash; + if (!opt_txid_string.empty()) + { + if (!epee::string_tools::hex_to_pod(opt_txid_string, opt_txid)) + { + std::cerr << "Invalid txid" << std::endl; + return 1; + } + } + + std::string db_type = command_line::get_arg(vm, arg_database); + if (!cryptonote::blockchain_valid_db_type(db_type)) + { + std::cerr << "Invalid database type: " << db_type << std::endl; + return 1; + } + + // If we wanted to use the memory pool, we would set up a fake_core. + + // Use Blockchain instead of lower-level BlockchainDB for two reasons: + // 1. Blockchain has the init() method for easy setup + // 2. exporter needs to use get_current_blockchain_height(), get_block_id_by_height(), get_block_by_hash() + // + // cannot match blockchain_storage setup above with just one line, + // e.g. + // Blockchain* core_storage = new Blockchain(NULL); + // because unlike blockchain_storage constructor, which takes a pointer to + // tx_memory_pool, Blockchain's constructor takes tx_memory_pool object. + LOG_PRINT_L0("Initializing source blockchain (BlockchainDB)"); + std::unique_ptr core_storage; + tx_memory_pool m_mempool(*core_storage); + core_storage.reset(new Blockchain(m_mempool)); + BlockchainDB *db = new_db(db_type); + if (db == NULL) + { + LOG_ERROR("Attempted to use non-existent database type: " << db_type); + throw std::runtime_error("Attempting to use non-existent database type"); + } + LOG_PRINT_L0("database: " << db_type); + + const std::string filename = (boost::filesystem::path(opt_data_dir) / db->get_db_name()).string(); + LOG_PRINT_L0("Loading blockchain from folder " << filename << " ..."); + + try + { + db->open(filename, DBF_RDONLY); + } + catch (const std::exception& e) + { + LOG_PRINT_L0("Error opening database: " << e.what()); + return 1; + } + r = core_storage->init(db, net_type); + + CHECK_AND_ASSERT_MES(r, 1, "Failed to initialize source blockchain storage"); + LOG_PRINT_L0("Source blockchain storage initialized OK"); + + std::vector start_txids; + + // forward method + if (opt_all) + { + uint64_t cached_txes = 0, cached_blocks = 0, cached_outputs = 0, total_txes = 0, total_blocks = 0, total_outputs = 0; + std::unordered_map> ancestry; + std::unordered_map output_cache; + std::unordered_map tx_cache; + std::map block_cache; + + const uint64_t db_height = db->height(); + for (uint64_t h = 0; h < db_height; ++h) + { + size_t block_ancestry_size = 0; + const crypto::hash block_hash = db->get_block_hash_from_height(h); + const cryptonote::blobdata bd = db->get_block_blob(block_hash); + ++total_blocks; + cryptonote::block b; + if (!cryptonote::parse_and_validate_block_from_blob(bd, b)) + { + LOG_PRINT_L0("Bad block from db"); + return 1; + } + if (opt_cache_blocks) + block_cache.insert(std::make_pair(h, b)); + std::vector txids; + txids.reserve(1 + b.tx_hashes.size()); + if (opt_include_coinbase) + txids.push_back(cryptonote::get_transaction_hash(b.miner_tx)); + for (const auto &h: b.tx_hashes) + txids.push_back(h); + for (const crypto::hash &txid: txids) + { + printf("%lu/%lu \r", (unsigned long)h, (unsigned long)db_height); + fflush(stdout); + cryptonote::transaction tx; + std::unordered_map::const_iterator i = tx_cache.find(txid); + ++total_txes; + if (i != tx_cache.end()) + { + ++cached_txes; + tx = i->second; + } + else + { + cryptonote::blobdata bd; + if (!db->get_pruned_tx_blob(txid, bd)) + { + LOG_PRINT_L0("Failed to get txid " << txid << " from db"); + return 1; + } + if (!cryptonote::parse_and_validate_tx_base_from_blob(bd, tx)) + { + LOG_PRINT_L0("Bad tx: " << txid); + return 1; + } + if (opt_cache_txes) + tx_cache.insert(std::make_pair(txid, tx)); + } + const bool coinbase = tx.vin.size() == 1 && tx.vin[0].type() == typeid(cryptonote::txin_gen); + if (coinbase) + { + add_ancestry(ancestry, txid, std::unordered_set()); + } + else + { + for (size_t ring = 0; ring < tx.vin.size(); ++ring) + { + if (tx.vin[ring].type() == typeid(cryptonote::txin_to_key)) + { + const cryptonote::txin_to_key &txin = boost::get(tx.vin[ring]); + const uint64_t amount = txin.amount; + auto absolute_offsets = cryptonote::relative_output_offsets_to_absolute(txin.key_offsets); + for (uint64_t offset: absolute_offsets) + { + const output_data_t od = db->get_output_key(amount, offset); + add_ancestry(ancestry, txid, ancestor{amount, offset}); + cryptonote::block b; + auto iblock = block_cache.find(od.height); + ++total_blocks; + if (iblock != block_cache.end()) + { + ++cached_blocks; + b = iblock->second; + } + else + { + const crypto::hash block_hash = db->get_block_hash_from_height(od.height); + cryptonote::blobdata bd = db->get_block_blob(block_hash); + if (!cryptonote::parse_and_validate_block_from_blob(bd, b)) + { + LOG_PRINT_L0("Bad block from db"); + return 1; + } + if (opt_cache_blocks) + block_cache.insert(std::make_pair(od.height, b)); + } + // find the tx which created this output + bool found = false; + std::unordered_map::const_iterator i = output_cache.find({amount, offset}); + ++total_outputs; + if (i != output_cache.end()) + { + ++cached_outputs; + add_ancestry(ancestry, txid, get_ancestry(ancestry, i->second)); + found = true; + } + else for (size_t out = 0; out < b.miner_tx.vout.size(); ++out) + { + if (b.miner_tx.vout[out].target.type() == typeid(cryptonote::txout_to_key)) + { + const auto &txout = boost::get(b.miner_tx.vout[out].target); + if (txout.key == od.pubkey) + { + found = true; + add_ancestry(ancestry, txid, get_ancestry(ancestry, cryptonote::get_transaction_hash(b.miner_tx))); + if (opt_cache_outputs) + output_cache.insert(std::make_pair(ancestor{amount, offset}, cryptonote::get_transaction_hash(b.miner_tx))); + break; + } + } + else + { + LOG_PRINT_L0("Bad vout type in txid " << cryptonote::get_transaction_hash(b.miner_tx)); + return 1; + } + } + for (const crypto::hash &block_txid: b.tx_hashes) + { + if (found) + break; + cryptonote::transaction tx2; + std::unordered_map::const_iterator i = tx_cache.find(block_txid); + ++total_txes; + if (i != tx_cache.end()) + { + ++cached_txes; + tx2 = i->second; + } + else + { + cryptonote::blobdata bd; + if (!db->get_pruned_tx_blob(block_txid, bd)) + { + LOG_PRINT_L0("Failed to get txid " << block_txid << " from db"); + return 1; + } + if (!cryptonote::parse_and_validate_tx_base_from_blob(bd, tx2)) + { + LOG_PRINT_L0("Bad tx: " << block_txid); + return 1; + } + if (opt_cache_txes) + tx_cache.insert(std::make_pair(block_txid, tx2)); + } + for (size_t out = 0; out < tx2.vout.size(); ++out) + { + if (tx2.vout[out].target.type() == typeid(cryptonote::txout_to_key)) + { + const auto &txout = boost::get(tx2.vout[out].target); + if (txout.key == od.pubkey) + { + found = true; + add_ancestry(ancestry, txid, get_ancestry(ancestry, block_txid)); + if (opt_cache_outputs) + output_cache.insert(std::make_pair(ancestor{amount, offset}, block_txid)); + break; + } + } + else + { + LOG_PRINT_L0("Bad vout type in txid " << block_txid); + return 1; + } + } + } + if (!found) + { + LOG_PRINT_L0("Output originating transaction not found"); + return 1; + } + } + } + else + { + LOG_PRINT_L0("Bad vin type in txid " << txid); + return 1; + } + } + } + const size_t ancestry_size = get_ancestry(ancestry, txid).size(); + block_ancestry_size += ancestry_size; + MINFO(txid << ": " << ancestry_size); + } + if (!txids.empty()) + { + std::string stats_msg; + if (opt_show_cache_stats) + stats_msg = std::string(", cache: txes ") + std::to_string(cached_txes*100./total_txes) + + ", blocks " + std::to_string(cached_blocks*100./total_blocks) + ", outputs " + + std::to_string(cached_outputs*100./total_outputs); + MINFO("Height " << h << ": " << (block_ancestry_size / txids.size()) << " average over " << txids.size() << stats_msg); + } + } + goto done; + } + + if (!opt_txid_string.empty()) + { + start_txids.push_back(opt_txid); + } + else + { + const crypto::hash block_hash = db->get_block_hash_from_height(opt_height); + const cryptonote::blobdata bd = db->get_block_blob(block_hash); + cryptonote::block b; + if (!cryptonote::parse_and_validate_block_from_blob(bd, b)) + { + LOG_PRINT_L0("Bad block from db"); + return 1; + } + for (const crypto::hash &txid: b.tx_hashes) + start_txids.push_back(txid); + } + + if (start_txids.empty()) + { + LOG_PRINT_L0("No transaction(s) to check"); + return 1; + } + + for (const crypto::hash &start_txid: start_txids) + { + LOG_PRINT_L0("Checking ancestry for txid " << start_txid); + + std::unordered_map ancestry; + + std::list txids; + txids.push_back(start_txid); + while (!txids.empty()) + { + const crypto::hash txid = txids.front(); + txids.pop_front(); + + cryptonote::blobdata bd; + if (!db->get_pruned_tx_blob(txid, bd)) + { + LOG_PRINT_L0("Failed to get txid " << txid << " from db"); + return 1; + } + cryptonote::transaction tx; + if (!cryptonote::parse_and_validate_tx_base_from_blob(bd, tx)) + { + LOG_PRINT_L0("Bad tx: " << txid); + return 1; + } + const bool coinbase = tx.vin.size() == 1 && tx.vin[0].type() == typeid(cryptonote::txin_gen); + if (coinbase) + continue; + + for (size_t ring = 0; ring < tx.vin.size(); ++ring) + { + if (tx.vin[ring].type() == typeid(cryptonote::txin_to_key)) + { + const cryptonote::txin_to_key &txin = boost::get(tx.vin[ring]); + const uint64_t amount = txin.amount; + auto absolute_offsets = cryptonote::relative_output_offsets_to_absolute(txin.key_offsets); + for (uint64_t offset: absolute_offsets) + { + add_ancestor(ancestry, amount, offset); + const output_data_t od = db->get_output_key(amount, offset); + const crypto::hash block_hash = db->get_block_hash_from_height(od.height); + bd = db->get_block_blob(block_hash); + cryptonote::block b; + if (!cryptonote::parse_and_validate_block_from_blob(bd, b)) + { + LOG_PRINT_L0("Bad block from db"); + return 1; + } + // find the tx which created this output + bool found = false; + for (size_t out = 0; out < b.miner_tx.vout.size(); ++out) + { + if (b.miner_tx.vout[out].target.type() == typeid(cryptonote::txout_to_key)) + { + const auto &txout = boost::get(b.miner_tx.vout[out].target); + if (txout.key == od.pubkey) + { + found = true; + txids.push_back(cryptonote::get_transaction_hash(b.miner_tx)); + MDEBUG("adding txid: " << cryptonote::get_transaction_hash(b.miner_tx)); + break; + } + } + else + { + LOG_PRINT_L0("Bad vout type in txid " << cryptonote::get_transaction_hash(b.miner_tx)); + return 1; + } + } + for (const crypto::hash &block_txid: b.tx_hashes) + { + if (found) + break; + if (!db->get_pruned_tx_blob(block_txid, bd)) + { + LOG_PRINT_L0("Failed to get txid " << block_txid << " from db"); + return 1; + } + cryptonote::transaction tx2; + if (!cryptonote::parse_and_validate_tx_base_from_blob(bd, tx2)) + { + LOG_PRINT_L0("Bad tx: " << block_txid); + return 1; + } + for (size_t out = 0; out < tx2.vout.size(); ++out) + { + if (tx2.vout[out].target.type() == typeid(cryptonote::txout_to_key)) + { + const auto &txout = boost::get(tx2.vout[out].target); + if (txout.key == od.pubkey) + { + found = true; + txids.push_back(block_txid); + MDEBUG("adding txid: " << block_txid); + break; + } + } + else + { + LOG_PRINT_L0("Bad vout type in txid " << block_txid); + return 1; + } + } + } + if (!found) + { + LOG_PRINT_L0("Output originating transaction not found"); + return 1; + } + } + } + else + { + LOG_PRINT_L0("Bad vin type in txid " << txid); + return 1; + } + } + } + + MINFO("Ancestry for " << start_txid << ": " << get_deduplicated_ancestry(ancestry) << " / " << get_full_ancestry(ancestry)); + for (const auto &i: ancestry) + { + MINFO(cryptonote::print_money(i.first.amount) << "/" << i.first.offset << ": " << i.second); + } + } + +done: + core_storage->deinit(); + return 0; + + CATCH_ENTRY("Depth query error", 1); +} From 2382484dcddbfeb9eac34e2dcb6808cd50a77a7c Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 28 Jul 2018 00:56:37 +0100 Subject: [PATCH 0212/1404] blockchain_ancestry: add an incremental mode --- .../blockchain_ancestry.cpp | 124 ++++++++++++++---- 1 file changed, 99 insertions(+), 25 deletions(-) diff --git a/src/blockchain_utilities/blockchain_ancestry.cpp b/src/blockchain_utilities/blockchain_ancestry.cpp index 0de1c69df2a..c988b725036 100644 --- a/src/blockchain_utilities/blockchain_ancestry.cpp +++ b/src/blockchain_utilities/blockchain_ancestry.cpp @@ -30,8 +30,12 @@ #include #include #include +#include +#include +#include "common/unordered_containers_boost_serialization.h" #include "common/command_line.h" #include "common/varint.h" +#include "cryptonote_basic/cryptonote_boost_serialization.h" #include "cryptonote_core/tx_pool.h" #include "cryptonote_core/cryptonote_core.h" #include "cryptonote_core/blockchain.h" @@ -46,13 +50,22 @@ namespace po = boost::program_options; using namespace epee; using namespace cryptonote; +static bool stop_requested = false; + struct ancestor { uint64_t amount; uint64_t offset; bool operator==(const ancestor &other) const { return amount == other.amount && offset == other.offset; } + + template void serialize(t_archive &a, const unsigned int ver) + { + a & amount; + a & offset; + } }; +BOOST_CLASS_VERSION(ancestor, 0) namespace std { @@ -67,6 +80,25 @@ namespace std }; } +struct ancestry_state_t +{ + uint64_t height; + std::unordered_map> ancestry; + std::unordered_map output_cache; + std::unordered_map tx_cache; + std::unordered_map block_cache; + + template void serialize(t_archive &a, const unsigned int ver) + { + a & height; + a & ancestry; + a & output_cache; + a & tx_cache; + a & block_cache; + } +}; +BOOST_CLASS_VERSION(ancestry_state_t, 0) + static void add_ancestor(std::unordered_map &ancestry, uint64_t amount, uint64_t offset) { std::pair::iterator, bool> p = ancestry.insert(std::make_pair(ancestor{amount, offset}, 1)); @@ -274,13 +306,34 @@ int main(int argc, char* argv[]) if (opt_all) { uint64_t cached_txes = 0, cached_blocks = 0, cached_outputs = 0, total_txes = 0, total_blocks = 0, total_outputs = 0; - std::unordered_map> ancestry; - std::unordered_map output_cache; - std::unordered_map tx_cache; - std::map block_cache; + ancestry_state_t state; + + const std::string state_file_path = (boost::filesystem::path(opt_data_dir) / "ancestry-state.bin").string(); + LOG_PRINT_L0("Loading state data from " << state_file_path); + std::ifstream state_data_in; + state_data_in.open(state_file_path, std::ios_base::binary | std::ios_base::in); + if (!state_data_in.fail()) + { + try + { + boost::archive::portable_binary_iarchive a(state_data_in); + a >> state; + } + catch (const std::exception &e) + { + MERROR("Failed to load state data from " << state_file_path << ", restarting from scratch"); + state = ancestry_state_t(); + } + state_data_in.close(); + } + tools::signal_handler::install([](int type) { + stop_requested = true; + }); + + MINFO("Starting from height " << state.height); const uint64_t db_height = db->height(); - for (uint64_t h = 0; h < db_height; ++h) + for (uint64_t h = state.height; h < db_height; ++h) { size_t block_ancestry_size = 0; const crypto::hash block_hash = db->get_block_hash_from_height(h); @@ -293,7 +346,7 @@ int main(int argc, char* argv[]) return 1; } if (opt_cache_blocks) - block_cache.insert(std::make_pair(h, b)); + state.block_cache.insert(std::make_pair(h, b)); std::vector txids; txids.reserve(1 + b.tx_hashes.size()); if (opt_include_coinbase) @@ -305,9 +358,9 @@ int main(int argc, char* argv[]) printf("%lu/%lu \r", (unsigned long)h, (unsigned long)db_height); fflush(stdout); cryptonote::transaction tx; - std::unordered_map::const_iterator i = tx_cache.find(txid); + std::unordered_map::const_iterator i = state.tx_cache.find(txid); ++total_txes; - if (i != tx_cache.end()) + if (i != state.tx_cache.end()) { ++cached_txes; tx = i->second; @@ -326,12 +379,12 @@ int main(int argc, char* argv[]) return 1; } if (opt_cache_txes) - tx_cache.insert(std::make_pair(txid, tx)); + state.tx_cache.insert(std::make_pair(txid, tx)); } const bool coinbase = tx.vin.size() == 1 && tx.vin[0].type() == typeid(cryptonote::txin_gen); if (coinbase) { - add_ancestry(ancestry, txid, std::unordered_set()); + add_ancestry(state.ancestry, txid, std::unordered_set()); } else { @@ -345,11 +398,11 @@ int main(int argc, char* argv[]) for (uint64_t offset: absolute_offsets) { const output_data_t od = db->get_output_key(amount, offset); - add_ancestry(ancestry, txid, ancestor{amount, offset}); + add_ancestry(state.ancestry, txid, ancestor{amount, offset}); cryptonote::block b; - auto iblock = block_cache.find(od.height); + auto iblock = state.block_cache.find(od.height); ++total_blocks; - if (iblock != block_cache.end()) + if (iblock != state.block_cache.end()) { ++cached_blocks; b = iblock->second; @@ -364,16 +417,16 @@ int main(int argc, char* argv[]) return 1; } if (opt_cache_blocks) - block_cache.insert(std::make_pair(od.height, b)); + state.block_cache.insert(std::make_pair(od.height, b)); } // find the tx which created this output bool found = false; - std::unordered_map::const_iterator i = output_cache.find({amount, offset}); + std::unordered_map::const_iterator i = state.output_cache.find({amount, offset}); ++total_outputs; - if (i != output_cache.end()) + if (i != state.output_cache.end()) { ++cached_outputs; - add_ancestry(ancestry, txid, get_ancestry(ancestry, i->second)); + add_ancestry(state.ancestry, txid, get_ancestry(state.ancestry, i->second)); found = true; } else for (size_t out = 0; out < b.miner_tx.vout.size(); ++out) @@ -384,9 +437,9 @@ int main(int argc, char* argv[]) if (txout.key == od.pubkey) { found = true; - add_ancestry(ancestry, txid, get_ancestry(ancestry, cryptonote::get_transaction_hash(b.miner_tx))); + add_ancestry(state.ancestry, txid, get_ancestry(state.ancestry, cryptonote::get_transaction_hash(b.miner_tx))); if (opt_cache_outputs) - output_cache.insert(std::make_pair(ancestor{amount, offset}, cryptonote::get_transaction_hash(b.miner_tx))); + state.output_cache.insert(std::make_pair(ancestor{amount, offset}, cryptonote::get_transaction_hash(b.miner_tx))); break; } } @@ -401,9 +454,9 @@ int main(int argc, char* argv[]) if (found) break; cryptonote::transaction tx2; - std::unordered_map::const_iterator i = tx_cache.find(block_txid); + std::unordered_map::const_iterator i = state.tx_cache.find(block_txid); ++total_txes; - if (i != tx_cache.end()) + if (i != state.tx_cache.end()) { ++cached_txes; tx2 = i->second; @@ -422,7 +475,7 @@ int main(int argc, char* argv[]) return 1; } if (opt_cache_txes) - tx_cache.insert(std::make_pair(block_txid, tx2)); + state.tx_cache.insert(std::make_pair(block_txid, tx2)); } for (size_t out = 0; out < tx2.vout.size(); ++out) { @@ -432,9 +485,9 @@ int main(int argc, char* argv[]) if (txout.key == od.pubkey) { found = true; - add_ancestry(ancestry, txid, get_ancestry(ancestry, block_txid)); + add_ancestry(state.ancestry, txid, get_ancestry(state.ancestry, block_txid)); if (opt_cache_outputs) - output_cache.insert(std::make_pair(ancestor{amount, offset}, block_txid)); + state.output_cache.insert(std::make_pair(ancestor{amount, offset}, block_txid)); break; } } @@ -459,7 +512,7 @@ int main(int argc, char* argv[]) } } } - const size_t ancestry_size = get_ancestry(ancestry, txid).size(); + const size_t ancestry_size = get_ancestry(state.ancestry, txid).size(); block_ancestry_size += ancestry_size; MINFO(txid << ": " << ancestry_size); } @@ -472,7 +525,28 @@ int main(int argc, char* argv[]) + std::to_string(cached_outputs*100./total_outputs); MINFO("Height " << h << ": " << (block_ancestry_size / txids.size()) << " average over " << txids.size() << stats_msg); } + state.height = h; + if (stop_requested) + break; } + + LOG_PRINT_L0("Saving state data to " << state_file_path); + std::ofstream state_data_out; + state_data_out.open(state_file_path, std::ios_base::binary | std::ios_base::out | std::ios::trunc); + if (!state_data_out.fail()) + { + try + { + boost::archive::portable_binary_oarchive a(state_data_out); + a << state; + } + catch (const std::exception &e) + { + MERROR("Failed to save state data to " << state_file_path); + } + state_data_out.close(); + } + goto done; } From 628428a0df589b03b0020572e45d888c4ba17471 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 30 Jul 2018 21:36:38 +0100 Subject: [PATCH 0213/1404] blockchain_ancestry: faster and uses less memory --- .../blockchain_ancestry.cpp | 164 ++++++++++++------ 1 file changed, 114 insertions(+), 50 deletions(-) diff --git a/src/blockchain_utilities/blockchain_ancestry.cpp b/src/blockchain_utilities/blockchain_ancestry.cpp index c988b725036..2f0bbffd64e 100644 --- a/src/blockchain_utilities/blockchain_ancestry.cpp +++ b/src/blockchain_utilities/blockchain_ancestry.cpp @@ -73,31 +73,101 @@ namespace std { size_t operator()(const ancestor &a) const { - crypto::hash h; - crypto::cn_fast_hash(&a, sizeof(a), h); - return reinterpret_cast(h); + return a.amount ^ a.offset; // not that bad, since amount almost always have a high bit set, and offset doesn't } }; } +struct tx_data_t +{ + std::vector>> vin; + std::vector vout; + bool coinbase; + + tx_data_t(): coinbase(false) {} + tx_data_t(const cryptonote::transaction &tx) + { + coinbase = tx.vin.size() == 1 && tx.vin[0].type() == typeid(cryptonote::txin_gen); + if (!coinbase) + { + vin.reserve(tx.vin.size()); + for (size_t ring = 0; ring < tx.vin.size(); ++ring) + { + if (tx.vin[ring].type() == typeid(cryptonote::txin_to_key)) + { + const cryptonote::txin_to_key &txin = boost::get(tx.vin[ring]); + vin.push_back(std::make_pair(txin.amount, cryptonote::relative_output_offsets_to_absolute(txin.key_offsets))); + } + else + { + LOG_PRINT_L0("Bad vin type in txid " << get_transaction_hash(tx)); + throw std::runtime_error("Bad vin type"); + } + } + } + vout.reserve(tx.vout.size()); + for (size_t out = 0; out < tx.vout.size(); ++out) + { + if (tx.vout[out].target.type() == typeid(cryptonote::txout_to_key)) + { + const auto &txout = boost::get(tx.vout[out].target); + vout.push_back(txout.key); + } + else + { + LOG_PRINT_L0("Bad vout type in txid " << get_transaction_hash(tx)); + throw std::runtime_error("Bad vout type"); + } + } + } + + template void serialize(t_archive &a, const unsigned int ver) + { + a & coinbase; + a & vin; + a & vout; + } +}; + struct ancestry_state_t { uint64_t height; std::unordered_map> ancestry; std::unordered_map output_cache; - std::unordered_map tx_cache; - std::unordered_map block_cache; + std::unordered_map tx_cache; + std::vector block_cache; template void serialize(t_archive &a, const unsigned int ver) { a & height; a & ancestry; a & output_cache; - a & tx_cache; - a & block_cache; + if (ver < 1) + { + std::unordered_map old_tx_cache; + a & old_tx_cache; + for (const auto i: old_tx_cache) + tx_cache.insert(std::make_pair(i.first, ::tx_data_t(i.second))); + } + else + { + a & tx_cache; + } + if (ver < 2) + { + std::unordered_map old_block_cache; + a & old_block_cache; + block_cache.resize(old_block_cache.size()); + for (const auto i: old_block_cache) + block_cache[i.first] = i.second; + } + else + { + a & block_cache; + } } }; -BOOST_CLASS_VERSION(ancestry_state_t, 0) +BOOST_CLASS_VERSION(ancestry_state_t, 2) static void add_ancestor(std::unordered_map &ancestry, uint64_t amount, uint64_t offset) { @@ -333,6 +403,7 @@ int main(int argc, char* argv[]) MINFO("Starting from height " << state.height); const uint64_t db_height = db->height(); + state.block_cache.reserve(db_height); for (uint64_t h = state.height; h < db_height; ++h) { size_t block_ancestry_size = 0; @@ -346,7 +417,10 @@ int main(int argc, char* argv[]) return 1; } if (opt_cache_blocks) - state.block_cache.insert(std::make_pair(h, b)); + { + state.block_cache.resize(h + 1); + state.block_cache[h] = b; + } std::vector txids; txids.reserve(1 + b.tx_hashes.size()); if (opt_include_coinbase) @@ -357,13 +431,13 @@ int main(int argc, char* argv[]) { printf("%lu/%lu \r", (unsigned long)h, (unsigned long)db_height); fflush(stdout); - cryptonote::transaction tx; - std::unordered_map::const_iterator i = state.tx_cache.find(txid); + ::tx_data_t tx_data; + std::unordered_map::const_iterator i = state.tx_cache.find(txid); ++total_txes; if (i != state.tx_cache.end()) { ++cached_txes; - tx = i->second; + tx_data = i->second; } else { @@ -373,39 +447,38 @@ int main(int argc, char* argv[]) LOG_PRINT_L0("Failed to get txid " << txid << " from db"); return 1; } + cryptonote::transaction tx; if (!cryptonote::parse_and_validate_tx_base_from_blob(bd, tx)) { LOG_PRINT_L0("Bad tx: " << txid); return 1; } + tx_data = ::tx_data_t(tx); if (opt_cache_txes) - state.tx_cache.insert(std::make_pair(txid, tx)); + state.tx_cache.insert(std::make_pair(txid, tx_data)); } - const bool coinbase = tx.vin.size() == 1 && tx.vin[0].type() == typeid(cryptonote::txin_gen); - if (coinbase) + if (tx_data.coinbase) { add_ancestry(state.ancestry, txid, std::unordered_set()); } else { - for (size_t ring = 0; ring < tx.vin.size(); ++ring) + for (size_t ring = 0; ring < tx_data.vin.size(); ++ring) { - if (tx.vin[ring].type() == typeid(cryptonote::txin_to_key)) + if (1) { - const cryptonote::txin_to_key &txin = boost::get(tx.vin[ring]); - const uint64_t amount = txin.amount; - auto absolute_offsets = cryptonote::relative_output_offsets_to_absolute(txin.key_offsets); + const uint64_t amount = tx_data.vin[ring].first; + const std::vector &absolute_offsets = tx_data.vin[ring].second; for (uint64_t offset: absolute_offsets) { const output_data_t od = db->get_output_key(amount, offset); add_ancestry(state.ancestry, txid, ancestor{amount, offset}); cryptonote::block b; - auto iblock = state.block_cache.find(od.height); ++total_blocks; - if (iblock != state.block_cache.end()) + if (state.block_cache.size() > od.height && !state.block_cache[od.height].miner_tx.vin.empty()) { ++cached_blocks; - b = iblock->second; + b = state.block_cache[od.height]; } else { @@ -417,7 +490,10 @@ int main(int argc, char* argv[]) return 1; } if (opt_cache_blocks) - state.block_cache.insert(std::make_pair(od.height, b)); + { + state.block_cache.resize(od.height + 1); + state.block_cache[od.height] = b; + } } // find the tx which created this output bool found = false; @@ -453,13 +529,13 @@ int main(int argc, char* argv[]) { if (found) break; - cryptonote::transaction tx2; - std::unordered_map::const_iterator i = state.tx_cache.find(block_txid); + ::tx_data_t tx_data2; + std::unordered_map::const_iterator i = state.tx_cache.find(block_txid); ++total_txes; if (i != state.tx_cache.end()) { ++cached_txes; - tx2 = i->second; + tx_data2 = i->second; } else { @@ -469,32 +545,25 @@ int main(int argc, char* argv[]) LOG_PRINT_L0("Failed to get txid " << block_txid << " from db"); return 1; } - if (!cryptonote::parse_and_validate_tx_base_from_blob(bd, tx2)) + cryptonote::transaction tx; + if (!cryptonote::parse_and_validate_tx_base_from_blob(bd, tx)) { LOG_PRINT_L0("Bad tx: " << block_txid); return 1; } + tx_data2 = ::tx_data_t(tx); if (opt_cache_txes) - state.tx_cache.insert(std::make_pair(block_txid, tx2)); + state.tx_cache.insert(std::make_pair(block_txid, tx_data2)); } - for (size_t out = 0; out < tx2.vout.size(); ++out) + for (size_t out = 0; out < tx_data2.vout.size(); ++out) { - if (tx2.vout[out].target.type() == typeid(cryptonote::txout_to_key)) + if (tx_data2.vout[out] == od.pubkey) { - const auto &txout = boost::get(tx2.vout[out].target); - if (txout.key == od.pubkey) - { - found = true; - add_ancestry(state.ancestry, txid, get_ancestry(state.ancestry, block_txid)); - if (opt_cache_outputs) - state.output_cache.insert(std::make_pair(ancestor{amount, offset}, block_txid)); - break; - } - } - else - { - LOG_PRINT_L0("Bad vout type in txid " << block_txid); - return 1; + found = true; + add_ancestry(state.ancestry, txid, get_ancestry(state.ancestry, block_txid)); + if (opt_cache_outputs) + state.output_cache.insert(std::make_pair(ancestor{amount, offset}, block_txid)); + break; } } } @@ -505,11 +574,6 @@ int main(int argc, char* argv[]) } } } - else - { - LOG_PRINT_L0("Bad vin type in txid " << txid); - return 1; - } } } const size_t ancestry_size = get_ancestry(state.ancestry, txid).size(); From 43f71100bc3e52d06061ef24ba13aa238c01a1f3 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 30 Jul 2018 21:27:33 +0000 Subject: [PATCH 0214/1404] blockchain_db: remove unused get_output_key variant It was actually incorrect, as it would not return commitment --- src/blockchain_db/berkeleydb/db_bdb.h | 2 +- src/blockchain_db/blockchain_db.h | 17 ---------- src/blockchain_db/lmdb/db_lmdb.cpp | 49 --------------------------- src/blockchain_db/lmdb/db_lmdb.h | 1 - tests/unit_tests/hardfork.cpp | 1 - 5 files changed, 1 insertion(+), 69 deletions(-) diff --git a/src/blockchain_db/berkeleydb/db_bdb.h b/src/blockchain_db/berkeleydb/db_bdb.h index c90d030a26e..8c99ab25582 100644 --- a/src/blockchain_db/berkeleydb/db_bdb.h +++ b/src/blockchain_db/berkeleydb/db_bdb.h @@ -303,7 +303,6 @@ class BlockchainBDB : public BlockchainDB virtual uint64_t get_indexing_base() const { return 1; } virtual output_data_t get_output_key(const uint64_t& amount, const uint64_t& index); - virtual output_data_t get_output_key(const uint64_t& global_index) const; virtual void get_output_key(const uint64_t &amount, const std::vector &offsets, std::vector &outputs); virtual tx_out_index get_output_tx_and_index_from_global(const uint64_t& index) const; @@ -419,6 +418,7 @@ class BlockchainBDB : public BlockchainDB * @return the global index of the desired output */ uint64_t get_output_global_index(const uint64_t& amount, const uint64_t& index); + output_data_t get_output_key(const uint64_t& global_index) const; void checkpoint_worker() const; void check_open() const; diff --git a/src/blockchain_db/blockchain_db.h b/src/blockchain_db/blockchain_db.h index 6851e240456..4431ca44cc0 100644 --- a/src/blockchain_db/blockchain_db.h +++ b/src/blockchain_db/blockchain_db.h @@ -1261,23 +1261,6 @@ class BlockchainDB */ virtual output_data_t get_output_key(const uint64_t& amount, const uint64_t& index) = 0; - /** - * @brief get some of an output's data - * - * The subclass should return the public key, unlock time, and block height - * for the output with the given global index, collected in a struct. - * - * If the output cannot be found, the subclass should throw OUTPUT_DNE. - * - * If any of these parts cannot be found, but some are, the subclass - * should throw DB_ERROR with a message stating as much. - * - * @param global_index the output's index (global) - * - * @return the requested output data - */ - virtual output_data_t get_output_key(const uint64_t& global_index) const = 0; - /** * @brief gets an output's tx hash and index * diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp index a6eb3d880f7..7eecd1881aa 100644 --- a/src/blockchain_db/lmdb/db_lmdb.cpp +++ b/src/blockchain_db/lmdb/db_lmdb.cpp @@ -2454,55 +2454,6 @@ uint64_t BlockchainLMDB::get_num_outputs(const uint64_t& amount) const return num_elems; } -// This is a lot harder now that we've removed the output_keys index -output_data_t BlockchainLMDB::get_output_key(const uint64_t &global_index) const -{ - LOG_PRINT_L3("BlockchainLMDB::" << __func__ << " (unused version - does nothing)"); - check_open(); - TXN_PREFIX_RDONLY(); - RCURSOR(output_txs); - RCURSOR(tx_indices); - - output_data_t od; - MDB_val_set(v, global_index); - auto get_result = mdb_cursor_get(m_cur_output_txs, (MDB_val *)&zerokval, &v, MDB_GET_BOTH); - if (get_result == MDB_NOTFOUND) - throw1(OUTPUT_DNE("output with given index not in db")); - else if (get_result) - throw0(DB_ERROR("DB error attempting to fetch output tx hash")); - - outtx *ot = (outtx *)v.mv_data; - - MDB_val_set(val_h, ot->tx_hash); - get_result = mdb_cursor_get(m_cur_tx_indices, (MDB_val *)&zerokval, &val_h, MDB_GET_BOTH); - if (get_result) - throw0(DB_ERROR(lmdb_error(std::string("DB error attempting to fetch transaction index from hash ") + epee::string_tools::pod_to_hex(ot->tx_hash) + ": ", get_result).c_str())); - - txindex *tip = (txindex *)val_h.mv_data; - MDB_val_set(val_tx_id, tip->data.tx_id); - MDB_val result; - get_result = mdb_cursor_get(m_cur_txs_pruned, &val_tx_id, &result, MDB_SET); - if (get_result == MDB_NOTFOUND) - throw1(TX_DNE(std::string("tx with hash ").append(epee::string_tools::pod_to_hex(ot->tx_hash)).append(" not found in db").c_str())); - else if (get_result) - throw0(DB_ERROR(lmdb_error("DB error attempting to fetch tx from hash", get_result).c_str())); - - blobdata bd; - bd.assign(reinterpret_cast(result.mv_data), result.mv_size); - - transaction tx; - if (!parse_and_validate_tx_base_from_blob(bd, tx)) - throw0(DB_ERROR("Failed to parse tx from blob retrieved from the db")); - - const tx_out tx_output = tx.vout[ot->local_index]; - od.unlock_time = tip->data.unlock_time; - od.height = tip->data.block_id; - od.pubkey = boost::get(tx_output.target).key; - - TXN_POSTFIX_RDONLY(); - return od; -} - output_data_t BlockchainLMDB::get_output_key(const uint64_t& amount, const uint64_t& index) { LOG_PRINT_L3("BlockchainLMDB::" << __func__); diff --git a/src/blockchain_db/lmdb/db_lmdb.h b/src/blockchain_db/lmdb/db_lmdb.h index 8b214d2dfb6..8ff2073da42 100644 --- a/src/blockchain_db/lmdb/db_lmdb.h +++ b/src/blockchain_db/lmdb/db_lmdb.h @@ -243,7 +243,6 @@ class BlockchainLMDB : public BlockchainDB virtual uint64_t get_num_outputs(const uint64_t& amount) const; virtual output_data_t get_output_key(const uint64_t& amount, const uint64_t& index); - virtual output_data_t get_output_key(const uint64_t& global_index) const; virtual void get_output_key(const uint64_t &amount, const std::vector &offsets, std::vector &outputs, bool allow_partial = false); virtual tx_out_index get_output_tx_and_index_from_global(const uint64_t& index) const; diff --git a/tests/unit_tests/hardfork.cpp b/tests/unit_tests/hardfork.cpp index 930aeb782a1..8155c65f940 100644 --- a/tests/unit_tests/hardfork.cpp +++ b/tests/unit_tests/hardfork.cpp @@ -93,7 +93,6 @@ class TestDB: public BlockchainDB { virtual uint64_t get_num_outputs(const uint64_t& amount) const { return 1; } virtual uint64_t get_indexing_base() const { return 0; } virtual output_data_t get_output_key(const uint64_t& amount, const uint64_t& index) { return output_data_t(); } - virtual output_data_t get_output_key(const uint64_t& global_index) const { return output_data_t(); } virtual tx_out_index get_output_tx_and_index_from_global(const uint64_t& index) const { return tx_out_index(); } virtual tx_out_index get_output_tx_and_index(const uint64_t& amount, const uint64_t& index) const { return tx_out_index(); } virtual void get_output_tx_and_index(const uint64_t& amount, const std::vector &offsets, std::vector &indices) const {} From 9127a8b79f673c1a939fc2398ad2f76a3e5c356c Mon Sep 17 00:00:00 2001 From: stoffu Date: Mon, 23 Jul 2018 23:58:31 +0900 Subject: [PATCH 0215/1404] wallet-rpc: filter getbalance response by address index --- src/wallet/wallet_rpc_server.cpp | 14 ++++++++++---- src/wallet/wallet_rpc_server_commands_defs.h | 4 +++- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index b9cf996359d..e37f122e622 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -355,14 +355,20 @@ namespace tools std::map unlocked_balance_per_subaddress = m_wallet->unlocked_balance_per_subaddress(req.account_index); std::vector transfers; m_wallet->get_transfers(transfers); - for (const auto& i : balance_per_subaddress) + std::set address_indices = req.address_indices; + if (address_indices.empty()) + { + for (const auto& i : balance_per_subaddress) + address_indices.insert(i.first); + } + for (uint32_t i : address_indices) { wallet_rpc::COMMAND_RPC_GET_BALANCE::per_subaddress_info info; - info.address_index = i.first; + info.address_index = i; cryptonote::subaddress_index index = {req.account_index, info.address_index}; info.address = m_wallet->get_subaddress_as_str(index); - info.balance = i.second; - info.unlocked_balance = unlocked_balance_per_subaddress[i.first]; + info.balance = balance_per_subaddress[i]; + info.unlocked_balance = unlocked_balance_per_subaddress[i]; info.label = m_wallet->get_subaddress_label(index); info.num_unspent_outputs = std::count_if(transfers.begin(), transfers.end(), [&](const tools::wallet2::transfer_details& td) { return !td.m_spent && td.m_subaddr_index == index; }); res.per_subaddress.push_back(info); diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h index 1bd572adda8..33821a4ea5c 100644 --- a/src/wallet/wallet_rpc_server_commands_defs.h +++ b/src/wallet/wallet_rpc_server_commands_defs.h @@ -47,7 +47,7 @@ // advance which version they will stop working with // Don't go over 32767 for any of these #define WALLET_RPC_VERSION_MAJOR 1 -#define WALLET_RPC_VERSION_MINOR 1 +#define WALLET_RPC_VERSION_MINOR 2 #define MAKE_WALLET_RPC_VERSION(major,minor) (((major)<<16)|(minor)) #define WALLET_RPC_VERSION MAKE_WALLET_RPC_VERSION(WALLET_RPC_VERSION_MAJOR, WALLET_RPC_VERSION_MINOR) namespace tools @@ -62,8 +62,10 @@ namespace wallet_rpc struct request { uint32_t account_index; + std::set address_indices; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(account_index) + KV_SERIALIZE(address_indices) END_KV_SERIALIZE_MAP() }; From 1c6cfd34f4ad5939ebeb29a9776faf2f29c8b04f Mon Sep 17 00:00:00 2001 From: stoffu Date: Mon, 23 Jul 2018 23:45:11 +0900 Subject: [PATCH 0216/1404] wallet-rpc: add get_address_index command --- src/wallet/wallet2.cpp | 8 ++++++++ src/wallet/wallet2.h | 1 + src/wallet/wallet_rpc_server.cpp | 21 ++++++++++++++++++++ src/wallet/wallet_rpc_server.h | 2 ++ src/wallet/wallet_rpc_server_commands_defs.h | 21 +++++++++++++++++++- 5 files changed, 52 insertions(+), 1 deletion(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index c2c02dd673c..39e5cce8094 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -899,6 +899,14 @@ cryptonote::account_public_address wallet2::get_subaddress(const cryptonote::sub return hwdev.get_subaddress(m_account.get_keys(), index); } //---------------------------------------------------------------------------------------------------- +boost::optional wallet2::get_subaddress_index(const cryptonote::account_public_address& address) const +{ + auto index = m_subaddresses.find(address.m_spend_public_key); + if (index == m_subaddresses.end()) + return boost::none; + return index->second; +} +//---------------------------------------------------------------------------------------------------- crypto::public_key wallet2::get_subaddress_spend_public_key(const cryptonote::subaddress_index& index) const { hw::device &hwdev = m_account.get_device(); diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index d4d76e66c67..dc2725a677c 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -659,6 +659,7 @@ namespace tools // Subaddress scheme cryptonote::account_public_address get_subaddress(const cryptonote::subaddress_index& index) const; cryptonote::account_public_address get_address() const { return get_subaddress({0,0}); } + boost::optional get_subaddress_index(const cryptonote::account_public_address& address) const; crypto::public_key get_subaddress_spend_public_key(const cryptonote::subaddress_index& index) const; std::vector get_subaddress_spend_public_keys(uint32_t account, uint32_t begin, uint32_t end) const; std::string get_subaddress_as_str(const cryptonote::subaddress_index& index) const; diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index b9cf996359d..f34534198f3 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -416,6 +416,27 @@ namespace tools return true; } //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::on_getaddress_index(const wallet_rpc::COMMAND_RPC_GET_ADDRESS_INDEX::request& req, wallet_rpc::COMMAND_RPC_GET_ADDRESS_INDEX::response& res, epee::json_rpc::error& er) + { + if (!m_wallet) return not_open(er); + cryptonote::address_parse_info info; + if(!get_account_address_from_str(info, m_wallet->nettype(), req.address)) + { + er.code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS; + er.message = "Invalid address"; + return false; + } + auto index = m_wallet->get_subaddress_index(info.address); + if (!index) + { + er.code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS; + er.message = "Address doesn't belong to the wallet"; + return false; + } + res.index = *index; + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_create_address(const wallet_rpc::COMMAND_RPC_CREATE_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_CREATE_ADDRESS::response& res, epee::json_rpc::error& er) { if (!m_wallet) return not_open(er); diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h index 9cb67c593c1..b7e545c53f3 100644 --- a/src/wallet/wallet_rpc_server.h +++ b/src/wallet/wallet_rpc_server.h @@ -69,6 +69,7 @@ namespace tools BEGIN_JSON_RPC_MAP("/json_rpc") MAP_JON_RPC_WE("get_balance", on_getbalance, wallet_rpc::COMMAND_RPC_GET_BALANCE) MAP_JON_RPC_WE("get_address", on_getaddress, wallet_rpc::COMMAND_RPC_GET_ADDRESS) + MAP_JON_RPC_WE("get_address_index", on_getaddress_index, wallet_rpc::COMMAND_RPC_GET_ADDRESS_INDEX) MAP_JON_RPC_WE("getbalance", on_getbalance, wallet_rpc::COMMAND_RPC_GET_BALANCE) MAP_JON_RPC_WE("getaddress", on_getaddress, wallet_rpc::COMMAND_RPC_GET_ADDRESS) MAP_JON_RPC_WE("create_address", on_create_address, wallet_rpc::COMMAND_RPC_CREATE_ADDRESS) @@ -146,6 +147,7 @@ namespace tools //json_rpc bool on_getbalance(const wallet_rpc::COMMAND_RPC_GET_BALANCE::request& req, wallet_rpc::COMMAND_RPC_GET_BALANCE::response& res, epee::json_rpc::error& er); bool on_getaddress(const wallet_rpc::COMMAND_RPC_GET_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_GET_ADDRESS::response& res, epee::json_rpc::error& er); + bool on_getaddress_index(const wallet_rpc::COMMAND_RPC_GET_ADDRESS_INDEX::request& req, wallet_rpc::COMMAND_RPC_GET_ADDRESS_INDEX::response& res, epee::json_rpc::error& er); bool on_create_address(const wallet_rpc::COMMAND_RPC_CREATE_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_CREATE_ADDRESS::response& res, epee::json_rpc::error& er); bool on_label_address(const wallet_rpc::COMMAND_RPC_LABEL_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_LABEL_ADDRESS::response& res, epee::json_rpc::error& er); bool on_get_accounts(const wallet_rpc::COMMAND_RPC_GET_ACCOUNTS::request& req, wallet_rpc::COMMAND_RPC_GET_ACCOUNTS::response& res, epee::json_rpc::error& er); diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h index 1bd572adda8..de92b22cf85 100644 --- a/src/wallet/wallet_rpc_server_commands_defs.h +++ b/src/wallet/wallet_rpc_server_commands_defs.h @@ -47,7 +47,7 @@ // advance which version they will stop working with // Don't go over 32767 for any of these #define WALLET_RPC_VERSION_MAJOR 1 -#define WALLET_RPC_VERSION_MINOR 1 +#define WALLET_RPC_VERSION_MINOR 2 #define MAKE_WALLET_RPC_VERSION(major,minor) (((major)<<16)|(minor)) #define WALLET_RPC_VERSION MAKE_WALLET_RPC_VERSION(WALLET_RPC_VERSION_MAJOR, WALLET_RPC_VERSION_MINOR) namespace tools @@ -141,6 +141,25 @@ namespace wallet_rpc }; }; + struct COMMAND_RPC_GET_ADDRESS_INDEX + { + struct request + { + std::string address; + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(address) + END_KV_SERIALIZE_MAP() + }; + + struct response + { + cryptonote::subaddress_index index; + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(index) + END_KV_SERIALIZE_MAP() + }; + }; + struct COMMAND_RPC_CREATE_ADDRESS { struct request From 84393062120751921b54bea47e4b9b5b7b91910e Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 31 Jul 2018 14:25:29 +0000 Subject: [PATCH 0217/1404] wallet2: do not divide by 0 on invalid daemon response --- src/wallet/wallet2.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 90807803af2..663bcb21523 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -10843,6 +10843,7 @@ std::vector> wallet2::estimate_backlog(const std:: const auto result = m_node_rpc_proxy.get_block_size_limit(block_size_limit); throw_on_rpc_response_error(result, "get_info"); uint64_t full_reward_zone = block_size_limit / 2; + THROW_WALLET_EXCEPTION_IF(full_reward_zone == 0, error::wallet_internal_error, "Invalid block size limit from daemon"); std::vector> blocks; for (const auto &fee_level: fee_levels) From 4616cf2641c69e48ae19303099e701088ffe9045 Mon Sep 17 00:00:00 2001 From: Lee Clagett Date: Wed, 1 Aug 2018 22:10:09 -0400 Subject: [PATCH 0218/1404] Fixed ZMQ-RPC for transactions and GET_BLOCKS_FAST --- src/ringct/rctTypes.h | 10 + src/rpc/daemon_handler.cpp | 42 ++- src/rpc/message_data_structs.h | 2 +- src/serialization/json_object.cpp | 328 +++++++++++++----------- src/serialization/json_object.h | 6 - tests/unit_tests/CMakeLists.txt | 2 + tests/unit_tests/json_serialization.cpp | 217 ++++++++++++++++ 7 files changed, 419 insertions(+), 188 deletions(-) create mode 100644 tests/unit_tests/json_serialization.cpp diff --git a/src/ringct/rctTypes.h b/src/ringct/rctTypes.h index 844291d0cc9..a3ccf2e8594 100644 --- a/src/ringct/rctTypes.h +++ b/src/ringct/rctTypes.h @@ -403,6 +403,16 @@ namespace rct { }; struct rctSig: public rctSigBase { rctSigPrunable p; + + keyV& get_pseudo_outs() + { + return type == RCTTypeSimpleBulletproof ? p.pseudoOuts : pseudoOuts; + } + + keyV const& get_pseudo_outs() const + { + return type == RCTTypeSimpleBulletproof ? p.pseudoOuts : pseudoOuts; + } }; //other basepoint H = toPoint(cn_fast_hash(G)), G the basepoint diff --git a/src/rpc/daemon_handler.cpp b/src/rpc/daemon_handler.cpp index 55858cc2a13..25abe48250e 100644 --- a/src/rpc/daemon_handler.cpp +++ b/src/rpc/daemon_handler.cpp @@ -86,53 +86,45 @@ namespace rpc res.error_details = "incorrect number of transactions retrieved for block"; return; } - std::vector txs; - for (const auto& p : it->second) - { - txs.resize(txs.size() + 1); - if (!parse_and_validate_tx_from_blob(p.second, txs.back())) - { - res.blocks.clear(); - res.output_indices.clear(); - res.status = Message::STATUS_FAILED; - res.error_details = "failed retrieving a requested transaction"; - return; - } - } cryptonote::rpc::block_output_indices& indices = res.output_indices[block_count]; // miner tx output indices { cryptonote::rpc::tx_output_indices tx_indices; - bool r = m_core.get_tx_outputs_gindexs(get_transaction_hash(bwt.block.miner_tx), tx_indices); - if (!r) + if (!m_core.get_tx_outputs_gindexs(get_transaction_hash(bwt.block.miner_tx), tx_indices)) { res.status = Message::STATUS_FAILED; res.error_details = "core::get_tx_outputs_gindexs() returned false"; return; } - indices.push_back(tx_indices); + indices.push_back(std::move(tx_indices)); } - // assume each block returned is returned with all its transactions - // in the correct order. - auto tx_it = txs.begin(); - for (const crypto::hash& h : bwt.block.tx_hashes) + auto hash_it = bwt.block.tx_hashes.begin(); + bwt.transactions.reserve(it->second.size()); + for (const auto& blob : it->second) { - bwt.transactions.emplace(h, *tx_it); - tx_it++; + bwt.transactions.emplace_back(); + if (!parse_and_validate_tx_from_blob(blob.second, bwt.transactions.back())) + { + res.blocks.clear(); + res.output_indices.clear(); + res.status = Message::STATUS_FAILED; + res.error_details = "failed retrieving a requested transaction"; + return; + } cryptonote::rpc::tx_output_indices tx_indices; - bool r = m_core.get_tx_outputs_gindexs(h, tx_indices); - if (!r) + if (!m_core.get_tx_outputs_gindexs(*hash_it, tx_indices)) { res.status = Message::STATUS_FAILED; res.error_details = "core::get_tx_outputs_gindexs() returned false"; return; } - indices.push_back(tx_indices); + indices.push_back(std::move(tx_indices)); + ++hash_it; } it++; diff --git a/src/rpc/message_data_structs.h b/src/rpc/message_data_structs.h index fc1b2329d13..20390aee84e 100644 --- a/src/rpc/message_data_structs.h +++ b/src/rpc/message_data_structs.h @@ -44,7 +44,7 @@ namespace rpc struct block_with_transactions { cryptonote::block block; - std::unordered_map transactions; + std::vector transactions; }; typedef std::vector tx_output_indices; diff --git a/src/serialization/json_object.cpp b/src/serialization/json_object.cpp index c2467b863e6..67fe709dcef 100644 --- a/src/serialization/json_object.cpp +++ b/src/serialization/json_object.cpp @@ -28,6 +28,8 @@ #include "json_object.h" +#include +#include #include #include #include "string_tools.h" @@ -219,11 +221,11 @@ void toJsonValue(rapidjson::Document& doc, const cryptonote::transaction& tx, ra INSERT_INTO_JSON_OBJECT(val, doc, version, tx.version); INSERT_INTO_JSON_OBJECT(val, doc, unlock_time, tx.unlock_time); - INSERT_INTO_JSON_OBJECT(val, doc, vin, tx.vin); - INSERT_INTO_JSON_OBJECT(val, doc, vout, tx.vout); + INSERT_INTO_JSON_OBJECT(val, doc, inputs, tx.vin); + INSERT_INTO_JSON_OBJECT(val, doc, outputs, tx.vout); INSERT_INTO_JSON_OBJECT(val, doc, extra, tx.extra); INSERT_INTO_JSON_OBJECT(val, doc, signatures, tx.signatures); - INSERT_INTO_JSON_OBJECT(val, doc, rct_signatures, tx.rct_signatures); + INSERT_INTO_JSON_OBJECT(val, doc, ringct, tx.rct_signatures); } @@ -236,11 +238,11 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::transaction& tx) GET_FROM_JSON_OBJECT(val, tx.version, version); GET_FROM_JSON_OBJECT(val, tx.unlock_time, unlock_time); - GET_FROM_JSON_OBJECT(val, tx.vin, vin); - GET_FROM_JSON_OBJECT(val, tx.vout, vout); + GET_FROM_JSON_OBJECT(val, tx.vin, inputs); + GET_FROM_JSON_OBJECT(val, tx.vout, outputs); GET_FROM_JSON_OBJECT(val, tx.extra, extra); GET_FROM_JSON_OBJECT(val, tx.signatures, signatures); - GET_FROM_JSON_OBJECT(val, tx.rct_signatures, rct_signatures); + GET_FROM_JSON_OBJECT(val, tx.rct_signatures, ringct); } void toJsonValue(rapidjson::Document& doc, const cryptonote::block& b, rapidjson::Value& val) @@ -277,26 +279,31 @@ void toJsonValue(rapidjson::Document& doc, const cryptonote::txin_v& txin, rapid { val.SetObject(); - if (txin.type() == typeid(cryptonote::txin_gen)) + struct add_input { - val.AddMember("type", "txin_gen", doc.GetAllocator()); - INSERT_INTO_JSON_OBJECT(val, doc, value, boost::get(txin)); - } - else if (txin.type() == typeid(cryptonote::txin_to_script)) - { - val.AddMember("type", "txin_to_script", doc.GetAllocator()); - INSERT_INTO_JSON_OBJECT(val, doc, value, boost::get(txin)); - } - else if (txin.type() == typeid(cryptonote::txin_to_scripthash)) - { - val.AddMember("type", "txin_to_scripthash", doc.GetAllocator()); - INSERT_INTO_JSON_OBJECT(val, doc, value, boost::get(txin)); - } - else if (txin.type() == typeid(cryptonote::txin_to_key)) - { - val.AddMember("type", "txin_to_key", doc.GetAllocator()); - INSERT_INTO_JSON_OBJECT(val, doc, value, boost::get(txin)); - } + using result_type = void; + + rapidjson::Document& doc; + rapidjson::Value& val; + + void operator()(cryptonote::txin_to_key const& input) const + { + INSERT_INTO_JSON_OBJECT(val, doc, to_key, input); + } + void operator()(cryptonote::txin_gen const& input) const + { + INSERT_INTO_JSON_OBJECT(val, doc, gen, input); + } + void operator()(cryptonote::txin_to_script const& input) const + { + INSERT_INTO_JSON_OBJECT(val, doc, to_script, input); + } + void operator()(cryptonote::txin_to_scripthash const& input) const + { + INSERT_INTO_JSON_OBJECT(val, doc, to_scripthash, input); + } + }; + boost::apply_visitor(add_input{doc, val}, txin); } @@ -307,31 +314,37 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::txin_v& txin) throw WRONG_TYPE("json object"); } - OBJECT_HAS_MEMBER_OR_THROW(val, "type") - OBJECT_HAS_MEMBER_OR_THROW(val, "value") - if (val["type"]== "txin_gen") - { - cryptonote::txin_gen tmpVal; - fromJsonValue(val["value"], tmpVal); - txin = tmpVal; - } - else if (val["type"]== "txin_to_script") + if (val.MemberCount() != 1) { - cryptonote::txin_to_script tmpVal; - fromJsonValue(val["value"], tmpVal); - txin = tmpVal; + throw MISSING_KEY("Invalid input object"); } - else if (val["type"] == "txin_to_scripthash") - { - cryptonote::txin_to_scripthash tmpVal; - fromJsonValue(val["value"], tmpVal); - txin = tmpVal; - } - else if (val["type"] == "txin_to_key") + + for (auto const& elem : val.GetObject()) { - cryptonote::txin_to_key tmpVal; - fromJsonValue(val["value"], tmpVal); - txin = tmpVal; + if (elem.name == "to_key") + { + cryptonote::txin_to_key tmpVal; + fromJsonValue(elem.value, tmpVal); + txin = std::move(tmpVal); + } + else if (elem.name == "gen") + { + cryptonote::txin_gen tmpVal; + fromJsonValue(elem.value, tmpVal); + txin = std::move(tmpVal); + } + else if (elem.name == "to_script") + { + cryptonote::txin_to_script tmpVal; + fromJsonValue(elem.value, tmpVal); + txin = std::move(tmpVal); + } + else if (elem.name == "to_scripthash") + { + cryptonote::txin_to_scripthash tmpVal; + fromJsonValue(elem.value, tmpVal); + txin = std::move(tmpVal); + } } } @@ -405,7 +418,7 @@ void toJsonValue(rapidjson::Document& doc, const cryptonote::txin_to_key& txin, INSERT_INTO_JSON_OBJECT(val, doc, amount, txin.amount); INSERT_INTO_JSON_OBJECT(val, doc, key_offsets, txin.key_offsets); - INSERT_INTO_JSON_OBJECT(val, doc, k_image, txin.k_image); + INSERT_INTO_JSON_OBJECT(val, doc, key_image, txin.k_image); } @@ -418,58 +431,7 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::txin_to_key& txin) GET_FROM_JSON_OBJECT(val, txin.amount, amount); GET_FROM_JSON_OBJECT(val, txin.key_offsets, key_offsets); - GET_FROM_JSON_OBJECT(val, txin.k_image, k_image); -} - -void toJsonValue(rapidjson::Document& doc, const cryptonote::txout_target_v& txout, rapidjson::Value& val) -{ - val.SetObject(); - - if (txout.type() == typeid(cryptonote::txout_to_script)) - { - val.AddMember("type", "txout_to_script", doc.GetAllocator()); - INSERT_INTO_JSON_OBJECT(val, doc, value, boost::get(txout)); - } - else if (txout.type() == typeid(cryptonote::txout_to_scripthash)) - { - val.AddMember("type", "txout_to_scripthash", doc.GetAllocator()); - INSERT_INTO_JSON_OBJECT(val, doc, value, boost::get(txout)); - } - else if (txout.type() == typeid(cryptonote::txout_to_key)) - { - val.AddMember("type", "txout_to_key", doc.GetAllocator()); - INSERT_INTO_JSON_OBJECT(val, doc, value, boost::get(txout)); - } -} - - -void fromJsonValue(const rapidjson::Value& val, cryptonote::txout_target_v& txout) -{ - if (!val.IsObject()) - { - throw WRONG_TYPE("json object"); - } - - OBJECT_HAS_MEMBER_OR_THROW(val, "type") - OBJECT_HAS_MEMBER_OR_THROW(val, "value") - if (val["type"]== "txout_to_script") - { - cryptonote::txout_to_script tmpVal; - fromJsonValue(val["value"], tmpVal); - txout = tmpVal; - } - else if (val["type"] == "txout_to_scripthash") - { - cryptonote::txout_to_scripthash tmpVal; - fromJsonValue(val["value"], tmpVal); - txout = tmpVal; - } - else if (val["type"] == "txout_to_key") - { - cryptonote::txout_to_key tmpVal; - fromJsonValue(val["value"], tmpVal); - txout = tmpVal; - } + GET_FROM_JSON_OBJECT(val, txin.k_image, key_image); } void toJsonValue(rapidjson::Document& doc, const cryptonote::txout_to_script& txout, rapidjson::Value& val) @@ -533,7 +495,28 @@ void toJsonValue(rapidjson::Document& doc, const cryptonote::tx_out& txout, rapi val.SetObject(); INSERT_INTO_JSON_OBJECT(val, doc, amount, txout.amount); - INSERT_INTO_JSON_OBJECT(val, doc, target, txout.target); + + struct add_output + { + using result_type = void; + + rapidjson::Document& doc; + rapidjson::Value& val; + + void operator()(cryptonote::txout_to_key const& output) const + { + INSERT_INTO_JSON_OBJECT(val, doc, to_key, output); + } + void operator()(cryptonote::txout_to_script const& output) const + { + INSERT_INTO_JSON_OBJECT(val, doc, to_script, output); + } + void operator()(cryptonote::txout_to_scripthash const& output) const + { + INSERT_INTO_JSON_OBJECT(val, doc, to_scripthash, output); + } + }; + boost::apply_visitor(add_output{doc, val}, txout.target); } void fromJsonValue(const rapidjson::Value& val, cryptonote::tx_out& txout) @@ -543,8 +526,37 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::tx_out& txout) throw WRONG_TYPE("json object"); } - GET_FROM_JSON_OBJECT(val, txout.amount, amount); - GET_FROM_JSON_OBJECT(val, txout.target, target); + if (val.MemberCount() != 2) + { + throw MISSING_KEY("Invalid input object"); + } + + for (auto const& elem : val.GetObject()) + { + if (elem.name == "amount") + { + fromJsonValue(elem.value, txout.amount); + } + + if (elem.name == "to_key") + { + cryptonote::txout_to_key tmpVal; + fromJsonValue(elem.value, tmpVal); + txout.target = std::move(tmpVal); + } + else if (elem.name == "to_script") + { + cryptonote::txout_to_script tmpVal; + fromJsonValue(elem.value, tmpVal); + txout.target = std::move(tmpVal); + } + else if (elem.name == "to_scripthash") + { + cryptonote::txout_to_scripthash tmpVal; + fromJsonValue(elem.value, tmpVal); + txout.target = std::move(tmpVal); + } + } } void toJsonValue(rapidjson::Document& doc, const cryptonote::connection_info& info, rapidjson::Value& val) @@ -617,7 +629,7 @@ void toJsonValue(rapidjson::Document& doc, const cryptonote::block_complete_entr val.SetObject(); INSERT_INTO_JSON_OBJECT(val, doc, block, blk.block); - INSERT_INTO_JSON_OBJECT(val, doc, txs, blk.txs); + INSERT_INTO_JSON_OBJECT(val, doc, transactions, blk.txs); } @@ -629,7 +641,7 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::block_complete_entry } GET_FROM_JSON_OBJECT(val, blk.block, block); - GET_FROM_JSON_OBJECT(val, blk.txs, txs); + GET_FROM_JSON_OBJECT(val, blk.txs, transactions); } void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::block_with_transactions& blk, rapidjson::Value& val) @@ -936,51 +948,70 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::BlockHeaderResp void toJsonValue(rapidjson::Document& doc, const rct::rctSig& sig, rapidjson::Value& val) { + using boost::adaptors::transform; + val.SetObject(); + const auto just_mask = [] (rct::ctkey const& key) -> rct::key const& + { + return key.mask; + }; + INSERT_INTO_JSON_OBJECT(val, doc, type, sig.type); - INSERT_INTO_JSON_OBJECT(val, doc, message, sig.message); - INSERT_INTO_JSON_OBJECT(val, doc, mixRing, sig.mixRing); - INSERT_INTO_JSON_OBJECT(val, doc, pseudoOuts, sig.pseudoOuts); - INSERT_INTO_JSON_OBJECT(val, doc, ecdhInfo, sig.ecdhInfo); - INSERT_INTO_JSON_OBJECT(val, doc, outPk, sig.outPk); - INSERT_INTO_JSON_OBJECT(val, doc, txnFee, sig.txnFee); - INSERT_INTO_JSON_OBJECT(val, doc, p, sig.p); + INSERT_INTO_JSON_OBJECT(val, doc, encrypted, sig.ecdhInfo); + INSERT_INTO_JSON_OBJECT(val, doc, commitments, transform(sig.outPk, just_mask)); + INSERT_INTO_JSON_OBJECT(val, doc, fee, sig.txnFee); + + // prunable + { + rapidjson::Value prunable; + prunable.SetObject(); + + INSERT_INTO_JSON_OBJECT(prunable, doc, range_proofs, sig.p.rangeSigs); + INSERT_INTO_JSON_OBJECT(prunable, doc, bulletproofs, sig.p.bulletproofs); + INSERT_INTO_JSON_OBJECT(prunable, doc, mlsags, sig.p.MGs); + INSERT_INTO_JSON_OBJECT(prunable, doc, pseudo_outs, sig.get_pseudo_outs()); + + val.AddMember("prunable", prunable, doc.GetAllocator()); + } } void fromJsonValue(const rapidjson::Value& val, rct::rctSig& sig) { + using boost::adaptors::transform; + if (!val.IsObject()) { throw WRONG_TYPE("json object"); } + std::vector commitments; + GET_FROM_JSON_OBJECT(val, sig.type, type); - GET_FROM_JSON_OBJECT(val, sig.message, message); - GET_FROM_JSON_OBJECT(val, sig.mixRing, mixRing); - GET_FROM_JSON_OBJECT(val, sig.pseudoOuts, pseudoOuts); - GET_FROM_JSON_OBJECT(val, sig.ecdhInfo, ecdhInfo); - GET_FROM_JSON_OBJECT(val, sig.outPk, outPk); - GET_FROM_JSON_OBJECT(val, sig.txnFee, txnFee); - GET_FROM_JSON_OBJECT(val, sig.p, p); -} + GET_FROM_JSON_OBJECT(val, sig.ecdhInfo, encrypted); + GET_FROM_JSON_OBJECT(val, commitments, commitments); + GET_FROM_JSON_OBJECT(val, sig.txnFee, fee); -void toJsonValue(rapidjson::Document& doc, const rct::ctkey& key, rapidjson::Value& val) -{ - val.SetObject(); + // prunable + { + OBJECT_HAS_MEMBER_OR_THROW(val, "prunable"); + const auto& prunable = val["prunable"]; - INSERT_INTO_JSON_OBJECT(val, doc, dest, key.dest); - INSERT_INTO_JSON_OBJECT(val, doc, mask, key.mask); -} + rct::keyV pseudo_outs; -void fromJsonValue(const rapidjson::Value& val, rct::ctkey& key) -{ - if (!val.IsObject()) + GET_FROM_JSON_OBJECT(prunable, sig.p.rangeSigs, range_proofs); + GET_FROM_JSON_OBJECT(prunable, sig.p.bulletproofs, bulletproofs); + GET_FROM_JSON_OBJECT(prunable, sig.p.MGs, mlsags); + GET_FROM_JSON_OBJECT(prunable, pseudo_outs, pseudo_outs); + + sig.get_pseudo_outs() = std::move(pseudo_outs); + } + + sig.outPk.reserve(commitments.size()); + for (rct::key const& commitment : commitments) { - throw WRONG_TYPE("json object"); + sig.outPk.push_back({{}, commitment}); } - GET_FROM_JSON_OBJECT(val, key.dest, dest); - GET_FROM_JSON_OBJECT(val, key.mask, mask); } void toJsonValue(rapidjson::Document& doc, const rct::ecdhTuple& tuple, rapidjson::Value& val) @@ -1002,27 +1033,6 @@ void fromJsonValue(const rapidjson::Value& val, rct::ecdhTuple& tuple) GET_FROM_JSON_OBJECT(val, tuple.amount, amount); } -void toJsonValue(rapidjson::Document& doc, const rct::rctSigPrunable& sig, rapidjson::Value& val) -{ - val.SetObject(); - - INSERT_INTO_JSON_OBJECT(val, doc, rangeSigs, sig.rangeSigs); - INSERT_INTO_JSON_OBJECT(val, doc, bulletproofs, sig.bulletproofs); - INSERT_INTO_JSON_OBJECT(val, doc, MGs, sig.MGs); -} - -void fromJsonValue(const rapidjson::Value& val, rct::rctSigPrunable& sig) -{ - if (!val.IsObject()) - { - throw WRONG_TYPE("json object"); - } - - GET_FROM_JSON_OBJECT(val, sig.rangeSigs, rangeSigs); - GET_FROM_JSON_OBJECT(val, sig.bulletproofs, bulletproofs); - GET_FROM_JSON_OBJECT(val, sig.MGs, MGs); -} - void toJsonValue(rapidjson::Document& doc, const rct::rangeSig& sig, rapidjson::Value& val) { val.SetObject(); @@ -1040,10 +1050,16 @@ void fromJsonValue(const rapidjson::Value& val, rct::rangeSig& sig) throw WRONG_TYPE("json object"); } + const auto ci = val.FindMember("Ci"); + if (ci == val.MemberEnd()) + { + throw MISSING_KEY("Ci"); + } + GET_FROM_JSON_OBJECT(val, sig.asig, asig); std::vector keyVector; - cryptonote::json::fromJsonValue(val["Ci"], keyVector); + cryptonote::json::fromJsonValue(ci->value, keyVector); if (!(keyVector.size() == 64)) { throw WRONG_TYPE("key64 (rct::key[64])"); @@ -1098,10 +1114,10 @@ void toJsonValue(rapidjson::Document& doc, const rct::boroSig& sig, rapidjson::V val.SetObject(); std::vector keyVector(sig.s0, std::end(sig.s0)); - INSERT_INTO_JSON_OBJECT(val, doc, s0, sig.s0); + INSERT_INTO_JSON_OBJECT(val, doc, s0, keyVector); keyVector.assign(sig.s1, std::end(sig.s1)); - INSERT_INTO_JSON_OBJECT(val, doc, s1, sig.s1); + INSERT_INTO_JSON_OBJECT(val, doc, s1, keyVector); INSERT_INTO_JSON_OBJECT(val, doc, ee, sig.ee); } diff --git a/src/serialization/json_object.h b/src/serialization/json_object.h index de21ace6611..da3351fe3ae 100644 --- a/src/serialization/json_object.h +++ b/src/serialization/json_object.h @@ -263,15 +263,9 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::BlockHeaderResp void toJsonValue(rapidjson::Document& doc, const rct::rctSig& i, rapidjson::Value& val); void fromJsonValue(const rapidjson::Value& i, rct::rctSig& sig); -void toJsonValue(rapidjson::Document& doc, const rct::ctkey& key, rapidjson::Value& val); -void fromJsonValue(const rapidjson::Value& val, rct::ctkey& key); - void toJsonValue(rapidjson::Document& doc, const rct::ecdhTuple& tuple, rapidjson::Value& val); void fromJsonValue(const rapidjson::Value& val, rct::ecdhTuple& tuple); -void toJsonValue(rapidjson::Document& doc, const rct::rctSigPrunable& sig, rapidjson::Value& val); -void fromJsonValue(const rapidjson::Value& val, rct::rctSigPrunable& sig); - void toJsonValue(rapidjson::Document& doc, const rct::rangeSig& sig, rapidjson::Value& val); void fromJsonValue(const rapidjson::Value& val, rct::rangeSig& sig); diff --git a/tests/unit_tests/CMakeLists.txt b/tests/unit_tests/CMakeLists.txt index ffa8065ab82..0821751cb57 100644 --- a/tests/unit_tests/CMakeLists.txt +++ b/tests/unit_tests/CMakeLists.txt @@ -47,6 +47,7 @@ set(unit_tests_sources epee_levin_protocol_handler_async.cpp epee_utils.cpp fee.cpp + json_serialization.cpp get_xtype_from_string.cpp hashchain.cpp http.cpp @@ -86,6 +87,7 @@ target_link_libraries(unit_tests cryptonote_core blockchain_db rpc + serialization wallet p2p version diff --git a/tests/unit_tests/json_serialization.cpp b/tests/unit_tests/json_serialization.cpp new file mode 100644 index 00000000000..ac6b60846cf --- /dev/null +++ b/tests/unit_tests/json_serialization.cpp @@ -0,0 +1,217 @@ + +#include +#include +#include +#include +#include + +#include "crypto/hash.h" +#include "cryptonote_basic/account.h" +#include "cryptonote_basic/cryptonote_basic.h" +#include "cryptonote_basic/cryptonote_format_utils.h" +#include "cryptonote_core/cryptonote_tx_utils.h" +#include "serialization/json_object.h" + + +namespace +{ + cryptonote::transaction + make_miner_transaction(cryptonote::account_public_address const& to) + { + cryptonote::transaction tx{}; + if (!cryptonote::construct_miner_tx(0, 0, 5000, 500, 500, to, tx)) + throw std::runtime_error{"transaction construction error"}; + + crypto::hash id{0}; + if (!cryptonote::get_transaction_hash(tx, id)) + throw std::runtime_error{"could not get transaction hash"}; + + return tx; + } + + cryptonote::transaction + make_transaction( + cryptonote::account_keys const& from, + std::vector const& sources, + std::vector const& destinations, + bool rct, + bool bulletproof) + { + std::uint64_t source_amount = 0; + std::vector actual_sources; + for (auto const& source : sources) + { + std::vector extra_fields; + if (!cryptonote::parse_tx_extra(source.extra, extra_fields)) + throw std::runtime_error{"invalid transaction"}; + + cryptonote::tx_extra_pub_key key_field{}; + if (!cryptonote::find_tx_extra_field_by_type(extra_fields, key_field)) + throw std::runtime_error{"invalid transaction"}; + + for (auto const& input : boost::adaptors::index(source.vout)) + { + source_amount += input.value().amount; + auto const& key = boost::get(input.value().target); + + actual_sources.push_back( + {{}, 0, key_field.pub_key, {}, std::size_t(input.index()), input.value().amount, rct, rct::identity()} + ); + + for (unsigned ring = 0; ring < 10; ++ring) + actual_sources.back().push_output(input.index(), key.key, input.value().amount); + } + } + + std::vector to; + for (auto const& destination : destinations) + to.push_back({(source_amount / destinations.size()), destination, false}); + + cryptonote::transaction tx{}; + + crypto::secret_key tx_key{}; + std::vector extra_keys{}; + + std::unordered_map subaddresses; + subaddresses[from.m_account_address.m_spend_public_key] = {0,0}; + + if (!cryptonote::construct_tx_and_get_tx_key(from, subaddresses, actual_sources, to, boost::none, {}, tx, 0, tx_key, extra_keys, rct, bulletproof)) + throw std::runtime_error{"transaction construction error"}; + + return tx; + } +} // anonymous + +TEST(JsonSerialization, MinerTransaction) +{ + cryptonote::account_base acct; + acct.generate(); + const auto miner_tx = make_miner_transaction(acct.get_keys().m_account_address); + + crypto::hash tx_hash{}; + ASSERT_TRUE(cryptonote::get_transaction_hash(miner_tx, tx_hash)); + + rapidjson::Document doc; + cryptonote::json::toJsonValue(doc, miner_tx, doc); + + cryptonote::transaction miner_tx_copy; + cryptonote::json::fromJsonValue(doc, miner_tx_copy); + + crypto::hash tx_copy_hash{}; + ASSERT_TRUE(cryptonote::get_transaction_hash(miner_tx_copy, tx_copy_hash)); + EXPECT_EQ(tx_hash, tx_copy_hash); + + cryptonote::blobdata tx_bytes{}; + cryptonote::blobdata tx_copy_bytes{}; + + ASSERT_TRUE(cryptonote::t_serializable_object_to_blob(miner_tx, tx_bytes)); + ASSERT_TRUE(cryptonote::t_serializable_object_to_blob(miner_tx_copy, tx_copy_bytes)); + + EXPECT_EQ(tx_bytes, tx_copy_bytes); +} + +TEST(JsonSerialization, RegularTransaction) +{ + cryptonote::account_base acct1; + acct1.generate(); + + cryptonote::account_base acct2; + acct2.generate(); + + const auto miner_tx = make_miner_transaction(acct1.get_keys().m_account_address); + const auto tx = make_transaction( + acct1.get_keys(), {miner_tx}, {acct2.get_keys().m_account_address}, false, false + ); + + crypto::hash tx_hash{}; + ASSERT_TRUE(cryptonote::get_transaction_hash(tx, tx_hash)); + + rapidjson::Document doc; + cryptonote::json::toJsonValue(doc, tx, doc); + + cryptonote::transaction tx_copy; + cryptonote::json::fromJsonValue(doc, tx_copy); + + crypto::hash tx_copy_hash{}; + ASSERT_TRUE(cryptonote::get_transaction_hash(tx_copy, tx_copy_hash)); + EXPECT_EQ(tx_hash, tx_copy_hash); + + cryptonote::blobdata tx_bytes{}; + cryptonote::blobdata tx_copy_bytes{}; + + ASSERT_TRUE(cryptonote::t_serializable_object_to_blob(tx, tx_bytes)); + ASSERT_TRUE(cryptonote::t_serializable_object_to_blob(tx_copy, tx_copy_bytes)); + + EXPECT_EQ(tx_bytes, tx_copy_bytes); +} + +TEST(JsonSerialization, RingctTransaction) +{ + cryptonote::account_base acct1; + acct1.generate(); + + cryptonote::account_base acct2; + acct2.generate(); + + const auto miner_tx = make_miner_transaction(acct1.get_keys().m_account_address); + const auto tx = make_transaction( + acct1.get_keys(), {miner_tx}, {acct2.get_keys().m_account_address}, true, false + ); + + crypto::hash tx_hash{}; + ASSERT_TRUE(cryptonote::get_transaction_hash(tx, tx_hash)); + + rapidjson::Document doc; + cryptonote::json::toJsonValue(doc, tx, doc); + + cryptonote::transaction tx_copy; + cryptonote::json::fromJsonValue(doc, tx_copy); + + crypto::hash tx_copy_hash{}; + ASSERT_TRUE(cryptonote::get_transaction_hash(tx_copy, tx_copy_hash)); + EXPECT_EQ(tx_hash, tx_copy_hash); + + cryptonote::blobdata tx_bytes{}; + cryptonote::blobdata tx_copy_bytes{}; + + ASSERT_TRUE(cryptonote::t_serializable_object_to_blob(tx, tx_bytes)); + ASSERT_TRUE(cryptonote::t_serializable_object_to_blob(tx_copy, tx_copy_bytes)); + + EXPECT_EQ(tx_bytes, tx_copy_bytes); +} + +TEST(JsonSerialization, BulletproofTransaction) +{ + cryptonote::account_base acct1; + acct1.generate(); + + cryptonote::account_base acct2; + acct2.generate(); + + const auto miner_tx = make_miner_transaction(acct1.get_keys().m_account_address); + const auto tx = make_transaction( + acct1.get_keys(), {miner_tx}, {acct2.get_keys().m_account_address}, true, true + ); + + crypto::hash tx_hash{}; + ASSERT_TRUE(cryptonote::get_transaction_hash(tx, tx_hash)); + + rapidjson::Document doc; + cryptonote::json::toJsonValue(doc, tx, doc); + + cryptonote::transaction tx_copy; + cryptonote::json::fromJsonValue(doc, tx_copy); + + crypto::hash tx_copy_hash{}; + ASSERT_TRUE(cryptonote::get_transaction_hash(tx_copy, tx_copy_hash)); + EXPECT_EQ(tx_hash, tx_copy_hash); + + cryptonote::blobdata tx_bytes{}; + cryptonote::blobdata tx_copy_bytes{}; + + ASSERT_TRUE(cryptonote::t_serializable_object_to_blob(tx, tx_bytes)); + ASSERT_TRUE(cryptonote::t_serializable_object_to_blob(tx_copy, tx_copy_bytes)); + + EXPECT_EQ(tx_bytes, tx_copy_bytes); +} + From 3411326a04bc2b50a7663bcc735648b905f6e025 Mon Sep 17 00:00:00 2001 From: HomDx Date: Thu, 2 Aug 2018 21:42:58 +0300 Subject: [PATCH 0219/1404] Docker: updated cmake version --- Dockerfile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index 0decb4fdeb8..5fde5a477c4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -23,9 +23,9 @@ RUN set -ex && \ WORKDIR /usr/local #Cmake -ARG CMAKE_VERSION=3.11.4 -ARG CMAKE_VERSION_DOT=v3.11 -ARG CMAKE_HASH=8f864e9f78917de3e1483e256270daabc4a321741592c5b36af028e72bff87f5 +ARG CMAKE_VERSION=3.12.0 +ARG CMAKE_VERSION_DOT=v3.12 +ARG CMAKE_HASH=d0781a90f6cdb9049d104ac16a150f9350b693498b9dea8a0331e799db6b9d69 RUN set -ex \ && curl -s -O https://cmake.org/files/${CMAKE_VERSION_DOT}/cmake-${CMAKE_VERSION}.tar.gz \ && echo "${CMAKE_HASH} cmake-${CMAKE_VERSION}.tar.gz" | sha256sum -c \ From b0b6959c1dce1a38626722ebfc0756305933cb21 Mon Sep 17 00:00:00 2001 From: Gingeropolous Date: Thu, 2 Aug 2018 23:25:03 -0400 Subject: [PATCH 0220/1404] update README to change all 0.12.2 to 0.12.3 I didn't change the minimum version in the table though, because 0.12.0 will work for this block version --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 98c300a9d24..f08d762066f 100644 --- a/README.md +++ b/README.md @@ -107,7 +107,7 @@ Dates are provided in the format YYYY-MM-DD. | 1220516 | 2017-01-05 | v4 | v0.10.1 | v0.10.2.1 | Allow normal and RingCT transactions | | 1288616 | 2017-04-15 | v5 | v0.10.3.0 | v0.10.3.1 | Adjusted minimum blocksize and fee algorithm | | 1400000 | 2017-09-16 | v6 | v0.11.0.0 | v0.11.0.0 | Allow only RingCT transactions, allow only >= ringsize 5 | -| 1546000 | 2018-04-06 | v7 | v0.12.0.0 | v0.12.2.0 | Cryptonight variant 1, ringsize >= 7, sorted inputs +| 1546000 | 2018-04-06 | v7 | v0.12.0.0 | v0.12.3.0 | Cryptonight variant 1, ringsize >= 7, sorted inputs | XXXXXXX | 2018-10-XX | XX | XXXXXXXXX | XXXXXXXXX | X X's indicate that these details have not been determined as of commit date. @@ -177,7 +177,7 @@ invokes cmake commands as needed. * Change to the root of the source code directory, change to the most recent release branch, and build: cd monero - git checkout v0.12.2.0 + git checkout v0.12.3.0 make *Optional*: If your machine has several cores and enough memory, enable @@ -239,7 +239,7 @@ Tested on a Raspberry Pi Zero with a clean install of minimal Raspbian Stretch ( ``` git clone https://github.com/monero-project/monero.git cd monero - git checkout tags/v0.12.2.0 + git checkout tags/v0.12.3.0 ``` * Build: ``` @@ -336,9 +336,9 @@ application. cd monero -* If you would like a specific [version/tag](https://github.com/monero-project/monero/tags), do a git checkout for that version. eg. 'v0.12.1.0'. If you dont care about the version and just want binaries from master, skip this step: +* If you would like a specific [version/tag](https://github.com/monero-project/monero/tags), do a git checkout for that version. eg. 'v0.12.3.0'. If you dont care about the version and just want binaries from master, skip this step: - git checkout v0.12.1.0 + git checkout v0.12.3.0 * If you are on a 64-bit system, run: From 37f07992846ce0b07b872e5f2c61fc65930de06d Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 19 Jul 2018 17:49:15 +0100 Subject: [PATCH 0221/1404] wallet: distinguish coinbase from other txes in show_transfers --- src/simplewallet/simplewallet.cpp | 35 +++++++++++++++++++------------ src/wallet/wallet2.cpp | 2 ++ src/wallet/wallet2.h | 12 +++++++++-- src/wallet/wallet_rpc_server.cpp | 2 +- 4 files changed, 35 insertions(+), 16 deletions(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 16866a80d2c..b883cd62728 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -2318,7 +2318,7 @@ simple_wallet::simple_wallet() tr("Check a signature proving that the owner of
holds at least this much, optionally with a challenge string .")); m_cmd_binder.set_handler("show_transfers", boost::bind(&simple_wallet::show_transfers, this, _1), - tr("show_transfers [in|out|pending|failed|pool] [index=[,,...]] [ []]"), + tr("show_transfers [in|out|pending|failed|pool|coinbase] [index=[,,...]] [ []]"), tr("Show the incoming/outgoing transfers within an optional height range.")); m_cmd_binder.set_handler("unspent_outputs", boost::bind(&simple_wallet::unspent_outputs, this, _1), @@ -6199,12 +6199,13 @@ bool simple_wallet::show_transfers(const std::vector &args_) bool pending = true; bool failed = true; bool pool = true; + bool coinbase = true; uint64_t min_height = 0; uint64_t max_height = (uint64_t)-1; boost::optional subaddr_index; if(local_args.size() > 4) { - fail_msg_writer() << tr("usage: show_transfers [in|out|all|pending|failed] [index=[,,...]] [ []]"); + fail_msg_writer() << tr("usage: show_transfers [in|out|all|pending|failed|coinbase] [index=[,,...]] [ []]"); return true; } @@ -6217,19 +6218,24 @@ bool simple_wallet::show_transfers(const std::vector &args_) local_args.erase(local_args.begin()); } else if (local_args[0] == "out" || local_args[0] == "outgoing") { - in = pool = false; + in = pool = coinbase = false; local_args.erase(local_args.begin()); } else if (local_args[0] == "pending") { - in = out = failed = false; + in = out = failed = coinbase = false; local_args.erase(local_args.begin()); } else if (local_args[0] == "failed") { - in = out = pending = pool = false; + in = out = pending = pool = coinbase = false; local_args.erase(local_args.begin()); } else if (local_args[0] == "pool") { - in = out = pending = failed = false; + in = out = pending = failed = coinbase = false; + local_args.erase(local_args.begin()); + } + else if (local_args[0] == "coinbase") { + in = out = pending = failed = pool = false; + coinbase = true; local_args.erase(local_args.begin()); } else if (local_args[0] == "all" || local_args[0] == "both") { @@ -6270,20 +6276,23 @@ bool simple_wallet::show_transfers(const std::vector &args_) local_args.erase(local_args.begin()); } - std::multimap> output; + std::multimap> output; PAUSE_READLINE(); - if (in) { + if (in || coinbase) { std::list> payments; m_wallet->get_payments(payments, min_height, max_height, m_current_subaddress_account, subaddr_indices); for (std::list>::const_iterator i = payments.begin(); i != payments.end(); ++i) { const tools::wallet2::payment_details &pd = i->second; + if (!pd.m_coinbase && !in) + continue; std::string payment_id = string_tools::pod_to_hex(i->first); if (payment_id.substr(16).find_first_not_of('0') == std::string::npos) payment_id = payment_id.substr(0,16); std::string note = m_wallet->get_tx_note(pd.m_tx_hash); - output.insert(std::make_pair(pd.m_block_height, std::make_pair(true, (boost::format("%25.25s %20.20s %s %s %d %s %s") % get_human_readable_timestamp(pd.m_timestamp) % print_money(pd.m_amount) % string_tools::pod_to_hex(pd.m_tx_hash) % payment_id % pd.m_subaddr_index.minor % "-" % note).str()))); + const std::string type = pd.m_coinbase ? tr("block") : tr("in"); + output.insert(std::make_pair(pd.m_block_height, std::make_tuple(epee::console_color_green, type, (boost::format("%25.25s %20.20s %s %s %d %s %s") % get_human_readable_timestamp(pd.m_timestamp) % print_money(pd.m_amount) % string_tools::pod_to_hex(pd.m_tx_hash) % payment_id % pd.m_subaddr_index.minor % "-" % note).str()))); } } @@ -6316,15 +6325,15 @@ bool simple_wallet::show_transfers(const std::vector &args_) if (payment_id.substr(16).find_first_not_of('0') == std::string::npos) payment_id = payment_id.substr(0,16); std::string note = m_wallet->get_tx_note(i->first); - output.insert(std::make_pair(pd.m_block_height, std::make_pair(false, (boost::format("%25.25s %20.20s %s %s %14.14s %s %s - %s") % get_human_readable_timestamp(pd.m_timestamp) % print_money(pd.m_amount_in - change - fee) % string_tools::pod_to_hex(i->first) % payment_id % print_money(fee) % dests % print_subaddr_indices(pd.m_subaddr_indices) % note).str()))); + output.insert(std::make_pair(pd.m_block_height, std::make_tuple(epee::console_color_magenta, tr("out"), (boost::format("%25.25s %20.20s %s %s %14.14s %s %s - %s") % get_human_readable_timestamp(pd.m_timestamp) % print_money(pd.m_amount_in - change - fee) % string_tools::pod_to_hex(i->first) % payment_id % print_money(fee) % dests % print_subaddr_indices(pd.m_subaddr_indices) % note).str()))); } } // print in and out sorted by height - for (std::map>::const_iterator i = output.begin(); i != output.end(); ++i) { - message_writer(i->second.first ? console_color_green : console_color_magenta, false) << + for (std::multimap>::const_iterator i = output.begin(); i != output.end(); ++i) { + message_writer(std::get<0>(i->second), false) << boost::format("%8.8llu %6.6s %s") % - ((unsigned long long)i->first) % (i->second.first ? tr("in") : tr("out")) % i->second.second; + ((unsigned long long)i->first) % std::get<1>(i->second) % std::get<2>(i->second); } if (pool) { diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 40a43b8e76a..c3055f2074a 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -1595,6 +1595,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote payment.m_block_height = height; payment.m_unlock_time = tx.unlock_time; payment.m_timestamp = ts; + payment.m_coinbase = miner_tx; payment.m_subaddr_index = i.first; if (pool) { emplace_or_replace(m_unconfirmed_payments, payment_id, pool_payment_details{payment, double_spend_seen}); @@ -7407,6 +7408,7 @@ void wallet2::light_wallet_get_address_txs() payment.m_block_height = t.height; payment.m_unlock_time = t.unlock_time; payment.m_timestamp = t.timestamp; + payment.m_coinbase = t.coinbase; if (t.mempool) { if (std::find(unconfirmed_payments_txs.begin(), unconfirmed_payments_txs.end(), tx_hash) == unconfirmed_payments_txs.end()) { diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 4d22b69152c..1f46413a5a4 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -260,12 +260,12 @@ namespace tools uint64_t m_block_height; uint64_t m_unlock_time; uint64_t m_timestamp; + bool m_coinbase; cryptonote::subaddress_index m_subaddr_index; }; struct address_tx : payment_details { - bool m_coinbase; bool m_mempool; bool m_incoming; }; @@ -1317,7 +1317,7 @@ BOOST_CLASS_VERSION(tools::wallet2::transfer_details, 9) BOOST_CLASS_VERSION(tools::wallet2::multisig_info, 1) BOOST_CLASS_VERSION(tools::wallet2::multisig_info::LR, 0) BOOST_CLASS_VERSION(tools::wallet2::multisig_tx_set, 1) -BOOST_CLASS_VERSION(tools::wallet2::payment_details, 3) +BOOST_CLASS_VERSION(tools::wallet2::payment_details, 4) BOOST_CLASS_VERSION(tools::wallet2::pool_payment_details, 1) BOOST_CLASS_VERSION(tools::wallet2::unconfirmed_transfer_details, 8) BOOST_CLASS_VERSION(tools::wallet2::confirmed_transfer_details, 6) @@ -1584,16 +1584,24 @@ namespace boost a & x.m_timestamp; if (ver < 2) { + x.m_coinbase = false; x.m_subaddr_index = {}; return; } a & x.m_subaddr_index; if (ver < 3) { + x.m_coinbase = false; x.m_fee = 0; return; } a & x.m_fee; + if (ver < 4) + { + x.m_coinbase = false; + return; + } + a & x.m_coinbase; } template diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index b9cf996359d..e49032dad27 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -270,7 +270,7 @@ namespace tools entry.unlock_time = pd.m_unlock_time; entry.fee = pd.m_fee; entry.note = m_wallet->get_tx_note(pd.m_tx_hash); - entry.type = "in"; + entry.type = pd.m_coinbase ? "block" : "in"; entry.subaddr_index = pd.m_subaddr_index; entry.address = m_wallet->get_subaddress_as_str(pd.m_subaddr_index); set_confirmations(entry, m_wallet->get_blockchain_current_height(), m_wallet->get_last_block_reward()); From 26a42fe54ae0a7e6f348c205cfc7d7b4bf3c012b Mon Sep 17 00:00:00 2001 From: Lee Clagett Date: Wed, 1 Aug 2018 23:08:59 -0400 Subject: [PATCH 0222/1404] Added features to epee::span : - Support for classes - Added `remove_prefix` function - Added `to_mut_span` and `as_mut_byte_span` --- contrib/epee/include/span.h | 46 ++++++++++++++++++++++---- tests/unit_tests/epee_utils.cpp | 57 ++++++++++++++++++++++++++++++++- 2 files changed, 96 insertions(+), 7 deletions(-) diff --git a/contrib/epee/include/span.h b/contrib/epee/include/span.h index 452cc088fae..174915ecfb7 100644 --- a/contrib/epee/include/span.h +++ b/contrib/epee/include/span.h @@ -28,6 +28,7 @@ #pragma once +#include #include #include #include @@ -52,11 +53,15 @@ namespace epee template class span { - /* Supporting class types is tricky - the {ptr,len} constructor will allow - derived-to-base conversions. This is NOT desireable because an array of - derived types is not an array of base types. It is possible to handle - this case, implement when/if needed. */ - static_assert(!std::is_class(), "no class types are currently allowed"); + template + static constexpr bool safe_conversion() noexcept + { + // Allow exact matches or `T*` -> `const T*`. + using with_const = typename std::add_const::type; + return std::is_same() || + (std::is_const() && std::is_same()); + } + public: using value_type = T; using size_type = std::size_t; @@ -71,7 +76,9 @@ namespace epee constexpr span() noexcept : ptr(nullptr), len(0) {} constexpr span(std::nullptr_t) noexcept : span() {} - constexpr span(T* const src_ptr, const std::size_t count) noexcept + //! Prevent derived-to-base conversions; invalid in this context. + template()>::type> + constexpr span(U* const src_ptr, const std::size_t count) noexcept : ptr(src_ptr), len(count) {} //! Conversion from C-array. Prevents common bugs with sizeof + arrays. @@ -81,6 +88,16 @@ namespace epee constexpr span(const span&) noexcept = default; span& operator=(const span&) noexcept = default; + /*! Try to remove `amount` elements from beginning of span. + \return Number of elements removed. */ + std::size_t remove_prefix(std::size_t amount) noexcept + { + amount = std::min(len, amount); + ptr += amount; + len -= amount; + return amount; + } + constexpr iterator begin() const noexcept { return ptr; } constexpr const_iterator cbegin() const noexcept { return ptr; } @@ -105,6 +122,14 @@ namespace epee return {src.data(), src.size()}; } + //! \return `span` from a STL compatible `src`. + template + constexpr span to_mut_span(T& src) + { + // compiler provides diagnostic if size() is not size_t. + return {src.data(), src.size()}; + } + template constexpr bool has_padding() noexcept { @@ -127,4 +152,13 @@ namespace epee static_assert(!has_padding(), "source type may have padding"); return {reinterpret_cast(std::addressof(src)), sizeof(T)}; } + + //! \return `span` which represents the bytes at `&src`. + template + span as_mut_byte_span(T& src) noexcept + { + static_assert(!std::is_empty(), "empty types will not work -> sizeof == 1"); + static_assert(!has_padding(), "source type may have padding"); + return {reinterpret_cast(std::addressof(src)), sizeof(T)}; + } } diff --git a/tests/unit_tests/epee_utils.cpp b/tests/unit_tests/epee_utils.cpp index 3474000d85d..c2b0b764707 100644 --- a/tests/unit_tests/epee_utils.cpp +++ b/tests/unit_tests/epee_utils.cpp @@ -166,12 +166,17 @@ TEST(Span, Traits) TEST(Span, MutableConstruction) { struct no_conversion{}; + struct inherited : no_conversion {}; EXPECT_TRUE(std::is_constructible>()); EXPECT_TRUE((std::is_constructible, char*, std::size_t>())); EXPECT_FALSE((std::is_constructible, const char*, std::size_t>())); EXPECT_FALSE((std::is_constructible, unsigned char*, std::size_t>())); + EXPECT_TRUE(std::is_constructible>()); + EXPECT_TRUE((std::is_constructible, no_conversion*, std::size_t>())); + EXPECT_FALSE((std::is_constructible, inherited*, std::size_t>())); + EXPECT_TRUE((can_construct, std::nullptr_t>())); EXPECT_TRUE((can_construct, char(&)[1]>())); @@ -193,12 +198,19 @@ TEST(Span, MutableConstruction) TEST(Span, ImmutableConstruction) { struct no_conversion{}; + struct inherited : no_conversion {}; EXPECT_TRUE(std::is_constructible>()); EXPECT_TRUE((std::is_constructible, char*, std::size_t>())); EXPECT_TRUE((std::is_constructible, const char*, std::size_t>())); EXPECT_FALSE((std::is_constructible, unsigned char*, std::size_t>())); + EXPECT_TRUE(std::is_constructible>()); + EXPECT_TRUE((std::is_constructible, const no_conversion*, std::size_t>())); + EXPECT_TRUE((std::is_constructible, no_conversion*, std::size_t>())); + EXPECT_FALSE((std::is_constructible, const inherited*, std::size_t>())); + EXPECT_FALSE((std::is_constructible, inherited*, std::size_t>())); + EXPECT_FALSE((can_construct, std::string>())); EXPECT_FALSE((can_construct, std::vector>())); EXPECT_FALSE((can_construct, const std::vector>())); @@ -231,7 +243,6 @@ TEST(Span, NoExcept) const epee::span clvalue(data); EXPECT_TRUE(noexcept(epee::span())); EXPECT_TRUE(noexcept(epee::span(nullptr))); - EXPECT_TRUE(noexcept(epee::span(nullptr, 0))); EXPECT_TRUE(noexcept(epee::span(data))); EXPECT_TRUE(noexcept(epee::span(lvalue))); EXPECT_TRUE(noexcept(epee::span(clvalue))); @@ -284,6 +295,25 @@ TEST(Span, Writing) EXPECT_TRUE(boost::range::equal(expected, span)); } +TEST(Span, RemovePrefix) +{ + const std::array expected{0, 1, 2, 3}; + auto span = epee::to_span(expected); + + EXPECT_EQ(expected.begin(), span.begin()); + EXPECT_EQ(expected.end(), span.end()); + + EXPECT_EQ(2u, span.remove_prefix(2)); + EXPECT_EQ(expected.begin() + 2, span.begin()); + EXPECT_EQ(expected.end(), span.end()); + + EXPECT_EQ(2u, span.remove_prefix(3)); + EXPECT_EQ(span.begin(), span.end()); + EXPECT_EQ(expected.end(), span.begin()); + + EXPECT_EQ(0u, span.remove_prefix(100)); +} + TEST(Span, ToByteSpan) { const char expected[] = {56, 44, 11, 5}; @@ -318,6 +348,30 @@ TEST(Span, AsByteSpan) ); } +TEST(Span, AsMutByteSpan) +{ + struct some_pod { char value[4]; }; + some_pod actual {}; + + auto span = epee::as_mut_byte_span(actual); + boost::range::iota(span, 1); + EXPECT_TRUE( + boost::range::equal( + std::array{{1, 2, 3, 4}}, actual.value + ) + ); +} + +TEST(Span, ToMutSpan) +{ + std::vector mut; + mut.resize(4); + + auto span = epee::to_mut_span(mut); + boost::range::iota(span, 1); + EXPECT_EQ((std::vector{1, 2, 3, 4}), mut); +} + TEST(ToHex, String) { EXPECT_TRUE(epee::to_hex::string(nullptr).empty()); @@ -330,6 +384,7 @@ TEST(ToHex, String) EXPECT_EQ( std_to_hex(all_bytes), epee::to_hex::string(epee::to_span(all_bytes)) ); + } TEST(ToHex, Array) From c5e2aee961bbf2cc8aa6dc395888ef25727a5723 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 5 Aug 2018 00:19:22 +0000 Subject: [PATCH 0223/1404] updates: fix hash sanity checking --- src/common/updates.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/common/updates.cpp b/src/common/updates.cpp index 9eb402e0b5c..9f12f8dbce8 100644 --- a/src/common/updates.cpp +++ b/src/common/updates.cpp @@ -69,12 +69,12 @@ namespace tools continue; bool alnum = true; - for (auto c: hash) + for (auto c: fields[3]) if (!isalnum(c)) alnum = false; - if (hash.size() != 64 && !alnum) + if (fields[3].size() != 64 && !alnum) { - MWARNING("Invalid hash: " << hash); + MWARNING("Invalid hash: " << fields[3]); continue; } From 2fd9be1646e331b68e4ce4ffc221bd81e85f4eaa Mon Sep 17 00:00:00 2001 From: rbrunner7 Date: Sun, 5 Aug 2018 09:58:33 +0200 Subject: [PATCH 0224/1404] simplewallet: correct number of human-readable months --- src/simplewallet/simplewallet.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 775b7c3598e..0d48dba34b5 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -6268,7 +6268,7 @@ static std::string get_human_readable_timespan(std::chrono::seconds seconds) if (ts < 3600 * 24 * 30.5) return std::to_string((uint64_t)(ts / (3600 * 24))) + tr(" days"); if (ts < 3600 * 24 * 365.25) - return std::to_string((uint64_t)(ts / (3600 * 24 * 365.25))) + tr(" months"); + return std::to_string((uint64_t)(ts / (3600 * 24 * 30.5))) + tr(" months"); return tr("a long time"); } //---------------------------------------------------------------------------------------------------- From 96c1de979a30b6ed8452a80d51d370e169f56706 Mon Sep 17 00:00:00 2001 From: Maxim Shishmarev Date: Tue, 7 Aug 2018 15:58:40 +1000 Subject: [PATCH 0225/1404] fixed cncrypto tests not passing after the change in random_scalar --- tests/crypto/tests.txt | 2048 ++++++++++++++++++++-------------------- 1 file changed, 1024 insertions(+), 1024 deletions(-) diff --git a/tests/crypto/tests.txt b/tests/crypto/tests.txt index a02e71ee926..0ae6a05b75b 100644 --- a/tests/crypto/tests.txt +++ b/tests/crypto/tests.txt @@ -335,262 +335,262 @@ check_scalar fcffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff fa check_scalar fdffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff false check_scalar feffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff false check_scalar ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff false -random_scalar 53e7f81c3aa8722a7797c451118d6b8727152a9db79e48022b34efc9ed44550e -random_scalar b19e8604fa44d940045a39f30dcba467eabb989c6588b7750a85d9d57d195a06 -random_scalar 85597fc8d0327049037d764772a3947caea5dbe8f7d6a02932888459f55b5c0d -random_scalar 36fd20f901420c661fbc3993fd90577aa7441bd870640f8bc72f72e512a11606 -random_scalar d41d51943fe73f66ce3a67ec25ee45fb332c9b2def9e9c2f89f4db9273f5470b -random_scalar 6f5047eea7f75022b96c32e380249734a612f8b4fba59db93076b15d81e3b002 -random_scalar fe2530f8564a8f679518aee57961a77c62b277ee95ab088835af24250c094906 -random_scalar b5f7954c7ab1ece1223b343f3f19b30000b8e798a3189809cf8aaf0364dbc80a -random_scalar 7a72a2adc6fd9f63758f6c9f79241d1d40d58bd2dc634557847b683ccc955003 -random_scalar f673d2a60e3a7f567c1302ef95456874cbddf40b9387638871b643183c498f02 -random_scalar 12b175038d6a9b1945e5710353366cc3b17de7b97a0e5ce388e4a22a47b42503 -random_scalar 183fc26ff30c1c3c167158e1f14b7609a39731f361e049ef08144f82b8149107 -random_scalar d797c95f9c47a4cccbacaa73d7a66f4be631e1e6244cf9fb00fa62254f38350b -random_scalar ce9558ee115567099068c94c8b58ff3a43011b3190a107824d782f5acead810f -random_scalar 834898dfcbc3146d92e2d0fdff371d88a46fdcb000f65231f68308cb4e92ea09 -random_scalar f52ec5c489cc936c90c005f78299bd840d5e4974c30e9775c898660ecb0f2909 -random_scalar c07ba88b2164a8ddc59116d860f24c43f8787decb9c39c5b7b5c2a4ecf92fb05 -random_scalar e6d16381607d61ec7d310419662f736aa840e46f254d26d7b49704d3e3c0640f -random_scalar 84fbcf68b71581b359aa239c2860e477be526fb5b752adb915c2eb57f7911c0a -random_scalar 1fbee4cb33a7f500b7fb68878d1106af22a7563a240c75b9b9884fc91ac60e01 -random_scalar d0203f3960b2dede483ac33cb9c4595369a146ad4480222f594e145fef7e590a -random_scalar cf0526b232fe4fabf3712a9a3da1a463971ebbdde3a183b4138d699d69006709 -random_scalar c2c4fa4db53d6345d478df65635f892150c366a7013a4b3bb2e39db513ce7700 -random_scalar 91473568dbb80ae41251fcf69f35cd9dbcd2cc849ee4f3d707e8e02dc5f00702 -random_scalar a130f8a3c42bf8e8439ad010896ef1b557ff9ff8a658c5056648aef59a31ea05 -random_scalar 50e7402b731478f02bb1ff1351f6117477da20464fc59a923df3b582e3674a0b -random_scalar 496cc9e98868e0cd74c07ddde4ad364912ad3a913f19bf748d42c03628e85d09 -random_scalar 1c2c48347e7f6e1b0598592913e31bcf57d4db81682bd42e957adb87bd599a03 -random_scalar b5ae0d9dc69140f9194f9fd9a910d885a576f9f1af31f039a03186042dbdcb00 -random_scalar 2bd954b752e9eae8010e2068bc8dbc0ab5206a5389f7cdf87595c2a15f63e403 -random_scalar eb3c08152437f64e68b11ae1d7958ebd9a7000000a446e3cf2eb7c0bb7c03207 -random_scalar 351fa38b5652e6f84389f92dfedb6f306502a1410042d7052b9921c0b7bd6b09 -random_scalar 4d13ba0257403c948fbdcc8c9b20323eb58c9c579db7eb5a3294bcf3f7432307 -random_scalar 5f4b72a843bfeb72a94161e3cfed21ee78e20cf2a6ac7d4b471a88951240df0a -random_scalar a1636db16b71e8a9b2b8736f0d45e56dbf2e1dfc27a46421e8bea862ebebe100 -random_scalar c8e6312eab290a4d8b0485812e1f9535a5e47932e185c32033a9e7e4599aa308 -random_scalar e3645a06c88e048c83500f8a002057d6cb4bee84ad7d2d2b19f31f74bb5acc0b -random_scalar aeff085bd972a0ddfaec586a753a68e57fcf8e22f1156387574695591a51f507 -random_scalar 061f87e59c8f7a4ff1ab14e0d6a4bc1c4406c2342ac1710bbea891dbd59fab05 -random_scalar 260e790703252ace6db5149b17ae7409ad14bc5a55c4065b3fcb6dfc7ffd510a -random_scalar 242911a8a34638010fe81cba7b402cf23312fc9831b7b2af53c719d7b27b8209 -random_scalar d54aa4b1d086dd8c005c1179ee50f51558fe24bb1a986eca75d9ae3a2a45df08 -random_scalar d1963ea06b83bd807173cb3c25ce12ef2d3b2181aea83a82b20d710231e84305 -random_scalar dc64142813b42a54a53872ccc26cd4614e8946763ae55868c36808fa74b6630d -random_scalar 5e3f7c7a61011fa6d628138cc20293cce857b16cc18dfffc2a7d2a27e6435c05 -random_scalar 15ca79a2b2cc2d9b158a4d86c774221c82a5fa0231a34c20529f5dfdb29bf80b -random_scalar 16356566cdc1c2bd74b772bfe0be24c2b70e41161e932b7b0d1193f7e094020c -random_scalar a38d90739c9a3a7f87e1dca293684d663764df26e234e7240384c060001ca60f -random_scalar 5dd3443d574f0a8a71dad9913640858e8ea5fec7b4d220a8fb0ca0ba91665d00 -random_scalar 5817dea9510990a19914e1cdf675ce057a78ed2e437250b3e786788c2033e906 -random_scalar d4c8d3ab20d5a39f50319f7f98529e39b77cce813d77fbe9dab3d1aec8b54f0b -random_scalar 8cfcf0e0de3fb497e1983aa2af6423cf57c32742fbfb9ff0d85f8a4193c46b08 -random_scalar 3f90e807165ebbcd25949d9bb57c1d0fd3a885b1f0f519426d22c879618f4a00 -random_scalar 041bbc0997401ea529ae52b946db2be0ce21052b1fce1254bb05bd41169d190f -random_scalar 01dc1a19be80418ebcd23c671ad9b2656eb56df46c918b74857159a0e33d0901 -random_scalar 65707704f384762c8266afdef1189d87f7725ac84747ddb1c34f7a154bd2ed0a -random_scalar cbc2abe6011cbfc0192594bbce8914c60e02fb19bd402ff5a8c4b0ae74427201 -random_scalar 02843acfe10fb0776cf2a47baa3b21d84c947847876af7c7e77f78f5b4eb2c01 -random_scalar 0fbe5a2e031b7f64efe56548d9117c7899cb998a337028fb5cd5daf65fc91809 -random_scalar 724aae3318e10d21c4393c25626c3950283b7d0d97c31288a43fd21f63045809 -random_scalar 4f630225ba3863ed1d6773b032e3ff0a1f48ccf120b4f08bdaeca8191ca6960c -random_scalar 456aba919a70f3cee5c97a923fdabce81aa4ad6e5ce346523e7dbcf51523430a -random_scalar 60943fa051b1b26d76342d7ea190eb04ef3dbb1acd7b091eee834d0fc6c78308 -random_scalar b098cd4403466873e32904236731473298c5f172b69345367dbe42c7f4bdce08 -random_scalar 8b49112d62c2a299122052812c76381a13c1eadafa4a4c645f438c9d7d83a003 -random_scalar e27de729ca8ae4fce2d1bced1461db520f80568b573137cbeb864d14f86ac30e -random_scalar 509410d3dea8f1d2caafa3c8f9d4f355fb8d792d917eb441b377dff7cf619c0b -random_scalar 55cc39624c61327c48c548eba012aea76ac5d22afedb74306686ea936654bd02 -random_scalar e2afca33f6111806abfa1af0fced797885848cbf71061d9b8a4851227b7ab00a -random_scalar 186c9684dd9d44644a0daad304399ed2d3039341740a810474d127788ba8460d -random_scalar 8e00fea0dee1928e76876cbafb20128126af12f7dff3a2a11ebf19627d71e609 -random_scalar 13b7588c2aae2c44c7d99146ef68d7e9a354c00bf765e9a18cdf19489041de08 -random_scalar 9c72d99148c9de2af7f384687fc5c020f0c0e90cb5ae04f1c5a7a20b53dc7401 -random_scalar aee5d59e6ec2a057d25f102e0e925e9a6efa10ebca4094f884d5b4df96581b01 -random_scalar 4ab61e2c277f8f0125138c9635227d17ab7afbe0190438cefe27fa7b0ac92f08 -random_scalar cfcb36274564229211b395da894d62d788d472f5397ad23d5ca685206bbd6405 -random_scalar 5d21c8700e5a8b12fa6d146f0186ea10af4c3586d5f1eac21d0a1b134611cd03 -random_scalar 7dee1c2e6ec4a6382d31c0a4a3430717a25bb8b8804a79962d3385fcc74feb04 -random_scalar 60265c97c5f855557d19d995d9c1e09bd930467f2c35ca2d8ced83bdc84ff10e -random_scalar b304094bd96788e4a3ce031f099282dcaf8476a8fd0f2ceed06fd28e5b606908 -random_scalar 1ffcd82a15372db2140902190e26fc213700c293a65ff622e8706d4ce5275105 -random_scalar bbae0d113135006580c704b8e83f6eb6f55e57c24a048a60913526ad21f00d03 -random_scalar a362273c0e4abb0fa5d67ba557d30aa3cf27a180856da576be1019d79f013b0c -random_scalar 73ea84eacf95e0819e178dd0aced9bc7f844b1ffeff27a01b5f2ba988dbb7d01 -random_scalar f05bfa17b6e13ef18a3874dfd8b1e1c244674c1f372ea04cb0292c173008b103 -random_scalar 54b648f07ad446e009e573ea48d8acf1b9a75c2b4d0364c495e23f087aaabe0b -random_scalar 202e08aab92b49465f96fe10ac191e93b8ab49cd96579aa8774d8271b8867b07 -random_scalar 584f6405526557aacb37b1bacd94fbe595e19421c48c9eb57ff90c20d5ad390c -random_scalar b3621e07b73c9ee32ffe407e636620614d7e4f68707499f11bc6369ef6b07b00 -random_scalar a394fff87a726d82256cfc4058b9ec84117e03f7d7e25ef212317460ad5f930f -random_scalar c32fd38cd0a59083bdca06027a3208221553727f3e2e2daae1d5572646514708 -random_scalar cf9ba6583c2eca4fad19d1667a390a2ef572fe9ecef30bda28c21074111c0a08 -random_scalar 69ada07f80910c972e0a3fdc12145f68aafcb686b369d06b101266d8c4ad9b05 -random_scalar e1afdd0fe6ed47a25760c9d76bbf446e688d44ce2d482edb1f1bbb1d8b723a02 -random_scalar 360ce0cc2955ee6c1372587b3f33f7142fc175b13abaa1504f08061ff5577d0f -random_scalar 6917b030d9ffa1eb043b0e8e35d0514d3b3039eebbfee1e8bb66ff121377b20b -random_scalar 5adea893762a29521673d6e72ab665797248ce5a6e89c252c3fb152caeaa7c06 -random_scalar a11ed0df4410df6daff30489a7d4900a241e68cffcc0f4fccdd2008c6d6b970b -random_scalar 119ef792c0fafa57ecfe2277c8ef3a0582c7ab31bf312f1219db1b7a9711d10c -random_scalar 76e4913a56714022236190dc7044c323eae06529767f89e21fd869e846b7cc08 -random_scalar 5030adecd3febfb5346af386e7a6acf0aef1d6a8608c3e1b651314bdd8359d0d -random_scalar cc9ae2356c0ef441ea587aa6b57feea532ff1a51a60fe9cba851b7d1baae7100 -random_scalar 22dd1dca7fb0ef41a0bbf02a36a217200f83043d07b68b6bf100a7ae2054c402 -random_scalar 02d4580d62243bccb0df8a7b136d281bc53f444ed8edb0e7e45b50b645c34102 -random_scalar 6c7febbcd9b42e419e2675567c36825b56f2a839f368b2594e49b36569b74107 -random_scalar 5d04ea12cdf0f747c865a2e806b8c1ceb2b229f40eadbb1a87b6c5f405cfb60e -random_scalar c7697bfb02951d6016c9455cc1fb242b1a8ce69b5fa7e04abe5cb52bcc63be03 -random_scalar 1d3d9eef9439050c1d62905fb93a5235e328aec178084cbe61d7b1390d515202 -random_scalar 89205b48cf29a3ce4124314ffc5537912e815bc1cb87b481918e78af0aeb1506 -random_scalar a33a8909eed10ed148a80fc976e6d478de912d461f9341bd8e930426903ef905 -random_scalar 51780f62883f8d4e8d6f8202860f11e29499b3ea888a3783730d27f90ff6d407 -random_scalar af6f16fa7c015f94a571343c08c0239460df338a666d87f4cc59f83b588a340b -random_scalar c60d1e70c511e42a33ff49fa61d3c0df3d0ce76e32cc602dcd86d5e55e131005 -random_scalar e2969637e86fcf1c02bfba9bb0b15b8dc934e7619152fc32cd3161f7336bc305 -random_scalar e4302426c4be390c61ef4e27ce8c3f0afb30efc9629dc0b0ceeac0c744664409 -random_scalar a9710d635b6d92d96edb2793185eae270b289861403d181b906877ccb7e9750c -random_scalar f76a101a4624553374f93ed40fd4732908ed4512737c111b8ede59227e208309 -random_scalar c443d188f44ecb75bdef8f36700d6c239dab1c85588fb0652c38cbb377c9fc0b -random_scalar 246653b379591d0bd8e22792993b88fe13b8b819e104fde650d9fa254d44b10b -random_scalar c245161d0f9a2805b8629ae7615b81aa39b6f7206b2ed1b3fce44d6fd978dc0f -random_scalar a2b7ee14b35ec69172cb0163c4151eb025a4f44b5b18d192af3c596ed024a506 -random_scalar 09fbf4f5da8af3044ac6baf30abcd395b60759abda6399cf39f7d9b569f6dc08 -random_scalar 45562292a1402222434d581167abfd7bf66eea7b586380d01ef92fad63f42c08 -random_scalar d145d2aadd4cafed6a5c7e6bee92622d04216197730d8d2065a4e0318077710c -random_scalar 8d703fc50a478dd91c71d7a078508ba813edbb9e66131756f4602b4c9951230d -random_scalar e87a7cfd9d0df825002c695ee25d7ea5ace17331da48d27c702ebd9e6ab0f309 -random_scalar eb8eab1398026436a704dbe19d1652247ed49f671b504ad944fbefaacc09dc0d -random_scalar 59f80f56cf9295d683ef7d232ae4aab6655335e2238e833010f06d0dc9345e0a -random_scalar fcefa6366cd53a1d9438fd90d84e4ce9d77a329d7e7f2ee31d66c0bb8c0b7200 -random_scalar 50f83302a432115978b2543b1555df44fdeb62dc496c03cbe7b38339d1f22b0c -random_scalar 875e33664db7d37e4fc6e9a7fa215b1958a71267d277c0e5f5cabe87c58a7401 -random_scalar 2b3d75b23f42d2d2993a5791204aec41c9a4fce1503458323baa6c44c1adab0a -random_scalar 1fbcff1080374e3270c44a9df75bad337eb10d0f9c05327db19a73e7dc6c8600 -random_scalar 980d99dad41f8350915a626e215fd5093d789f4475c2184ee2483ea88b7c1b03 -random_scalar 974b60039fd58ad185e76be82e94df52396ff343650cab6297d332bc44354407 -random_scalar 47f13e7457e6d3b7f338d235237847a5e7191754388d4252572ab3ce7cfd0b05 -random_scalar 4bfb443aba7ac38dca404bec886d02d9ca94fce92d2b13ef86909e88281b8100 -random_scalar ef856e327aa92cb4aa04299d48d49fa7e45656bff2534f24a119b38ed5cf9b09 -random_scalar aed90763fbe1d83a158990135232ba43d4056f84a3b5381b459612730c05f80d -random_scalar d39f9a3830a0787c15bb81c75eb1e225705fdc3d32a89c8dbe58b1eec438510d -random_scalar 073573648aeed18276b6ca04b5eada88c6ae933feaae882aa19319847ae3bd0c -random_scalar 86d0fc7743705f3018a15f9ccb2b4d25964104a85f7db1a8be3b459e463b160d -random_scalar f70d8fa395ddc366880306e1bd3f3a45bd81125a1e2acaba3ffef5535fa4660f -random_scalar 0a4a310e48f4ee1f968118c1fa9e04d9810a19f7344f2eb5e2f3a54a74046a01 -random_scalar a1be2ab7ea07bb1df1abdb8587db537947f439090fc8644cedfefc72e51ff306 -random_scalar 72375f5af0aa22b7480688b9b276fd3f482b8d5e561baec6bc1e16345470e504 -random_scalar 8ecd53e6f28d0b7bc4afb0fd82af084d570b28416419a635269e2b95e45c2d0d -random_scalar 4517b82e59d1585c5d66f2badbd1e8b225de8794c2a830b7612c2a089b9ba70f -random_scalar 1ccc5122efe52d0cbc37b57d6454bfefc429227c3109bae79e063e7d14f1500f -random_scalar edb27f193200a2b2d55e39a7b7b947a28dd4c196d5ed4cf7e239c9765bb2a002 -random_scalar 0f630ccc55e0b8cd228f0bb959b665bb77230e2583585d86012411fe0fa17409 -random_scalar 915b03284eb43b1927da93ad429cf2e2d6304f54bf144cea37c51596286f3502 -random_scalar 819f95699799ea6a55c8623e3969db63c8d0a32a1509e53752a4db36b980240c -random_scalar 245c532550f12c33a417d9ce620305d7c07584bc823a43b553ebeb700579a207 -random_scalar 790ddae8df2b81ee6128ecc131652309ee91361e3738c8a2f5245d5793995901 -random_scalar dba8117b8ebe25b86cad7436cf42b85f7802df93b101aaa61b2f300bcab90a01 -random_scalar 46dc861117e982b434e954f3896b57a18b31cc0b808b0bc5735725740e1d9306 -random_scalar aedfff16b3e5cf89c77d2a49a87bc205c4fc145723d03d3b748ad02fce51f505 -random_scalar 5c6a324798e056bb443cd1785d3dac3b83d553e6a05ed2bf48ffbbbfb49a4f0b -random_scalar d7794ea28786abb0cd05cfc344a912470abe35476f331063bac28f3aa98c9802 -random_scalar 543da265ebe7357ea618e783fef22a429128cc7771ce7fc129adfdbca12d360e -random_scalar 29b5e43025c3aa6901179717a2fa0ccfc50871d7d956e8a36022b3aeaad9ce03 -random_scalar befddc81aaca2843d598e2182cf7f91f512a6f8fb0424fbb012898bae52b6406 -random_scalar 0d9a3b8e4b8cf72a92508a7a4684bc47cf5be55610de660a1889e533debcfa01 -random_scalar 9791f6e80732b1caa0d0b58ad5e1525d36bf902f57a79fe80ea235271bd65409 -random_scalar e28f7d2d70ddb4d47d63661b2b632749c19f779335b6668a82884ba09c0cf006 -random_scalar f176052f4b52d50c923b78ef8c48bec7912f50011124b8c221428fbb1ee1780b -random_scalar f5f650106db441aa5fcacbebc823b01be9124e6ec84b11517b055a46e7c72f04 -random_scalar 16bd2af776481bc0938ffa810a1d04f62e4ba7d3ecf120de3a31469ce327a705 -random_scalar 8e50c4409e1146a7bd437eda7acce2bafea6d43cfbca018a2955a7a259aaaf0b -random_scalar d7f8862aecdabde52173b225bb0be1205b4719df1b7ef9173e8c1d8c6e9e0b05 -random_scalar fdafc76c9f7c8281277064cf754d06b6f555bd8dba0c6d92fbcea914958c6604 -random_scalar c5471b14c230baab32f83c3ea887b766f6eb23929bb2f3bef5c519061661db07 -random_scalar 27bed1183e9a8ad3e5c9b973e5b8c3ef54a5056684a6529743f5a62b54640e0f -random_scalar 5961cbdcac12c8b78034e18d076487f47d18fcb1cf41ff2ef4c1420d7888f101 -random_scalar 8f05f3f5b67cfabfa77559bf87e01d84e788162a6f1402d11cb5c8629ba27d08 -random_scalar 4f4b44c5e1e451708ea8e752b873fb40fd4c3aa4c24d6f1ae7168b055b25dd0f -random_scalar 1539fd568b2ec07503ba37dc38fe3611cdc6ebe0cf4af7cab8eca5a82112960c -random_scalar c67a1b6ff1b96102e92e7f414a76323c7e310a99feaf6c5bc7b827b711e41c05 -random_scalar 0c79d16ece73a6ee269e5725ed75313e8f6eb908090ac1e4d84601ea60f87007 -random_scalar 107877d5b17fc2e1b0165e61beecaf470b1e431dc0a817afdfd4a0ca1f61450d -random_scalar 360dbf5a622e5a8ecd3c0f9c97152e2ff59556bb17f1d0aedb4123ef4df1e906 -random_scalar a9677fa7eab1be0fbe40d315768a29c99479e3df0f50d81dd1c8a059c83af30c -random_scalar 130d7b30e81134f63ee56ffa33c24fb08e0b4bd547d2f0a161e83f78e8c22a0f -random_scalar 9c4cd5a33283a1001eb0f30a514a9e9736239281e4963558c2ea23b651267c06 -random_scalar 02f664e21c0478b51c84e3c2eed8fe485a1e750f115d18966a972e5de97ed700 -random_scalar 2f04e383afb8b93faac5680c21551ab94a529e85365eaa3a2b5b35fedd3bf903 -random_scalar 82dc19a0b923003d17c07e7541107dfeed98fd179c9caeb55a7731c9e3e53b04 -random_scalar 05ac3237591930a76a2fa67af180b0831258cbafb671f6283ef4ecc83ed3dc07 -random_scalar 0181e017770f075add64629936eca1f81f067762ee7a09a93852e2df91618f05 -random_scalar 4ca38c24060c7deb483f05fb5ccdc6ce5ae4b0e68f9a3c71acb0afa1a05dfd03 -random_scalar cf5e003d0f661948fc33b40c24f959314a115da2065f260a4fd6c0952746600c -random_scalar 495f83f263a2f14748e11e788b00b3aa7d8f095966fda40fdc8b45a07f32a506 -random_scalar df358ad6f286cbf10441a331e740c697a10729d7deb91d38293371b010118002 -random_scalar 262ac9388ecc8459971beea5212dc0cfa0450cca53008886c0d460d43c93fe04 -random_scalar 40e987f2213fb1e8dfa36e5209dacd8478155a463388fb05c3bb2e050cabb302 -random_scalar ecd53e90727eed6e7d661cadcafdd0726d2d81b611565069d09df9ef76debb05 -random_scalar d09aa7364d6c78d9d00db71bf7bca796bcceca239ace6bc7519e5b10a4a16107 -random_scalar ca11e12dc7ca088d294001a77465436095b0fe3a83ab97ddd9f0d645198bc702 -random_scalar bda47658303724418db4176a7dfe6db46f2114a14f166dd7630079d7531ab50c -random_scalar 1720fb3bfbf92c82d352215288fc48793d2ac50b96881a6ef8a6efbb221df10e -random_scalar 418276ee2044143e52d0be9a9df3c42eaf56af78f65179014a792edbbfcb4000 -random_scalar 4cbdc8564016ad41f2f70702162b6f185463b9c2f72e99f60194d04bfd31cf0a -random_scalar 66d3b4122090d0fbbe612ac54a04d99a054fc02df651d9b0651fe53101dd8a0f -random_scalar 48ca7152d9b52a19420b49a4fa804405456e0c53912bc6ed8ddb1f883e88c70f -random_scalar 5e070dfc15fe900925141eb30b52b6864d5d301a3e5665d5795bd0d2af176d06 -random_scalar 246feed9747a9b29488e8b958da8c15d0c97c0c292cb4361b5c1d88a1a1f6b0c -random_scalar e20d788ae8d75d9e08891b6fe4cd893fbe6d33840680c24bed69c4cc1e815808 -random_scalar 39154da67b82521c514ca17fb4a46a288fa2016e24017706d00380eeabbb9906 -random_scalar 8cd0adc36d8ad3c7f99b53b2199c251466a8c0edcafe4762d43aad83f883c602 -random_scalar 6c0c917eadd787e7b3ed826b38d28afe7e5ab782c4e2f4689ad5ce2b0100eb05 -random_scalar 513c8eebd9b646ec42cfa14d5e7d1e0b91a7c78741534291704ad79419775e0f -random_scalar b23dae629a9946f0f1855b08ed8df2ae5c9116e531f8a7dc773697e61cec550d -random_scalar 028e63ead18c1801748097f33e7d23339d3d68fb7a1d53c54257ded8a925140d -random_scalar 57c20a585c3023d0291ce1a0aac2634bc0ad341b43f7439a2b6387ec44afae08 -random_scalar e76a6fe667882a23c798789f2d0435f7422026f87db8d28279e31e622a1ff707 -random_scalar 4dc2c6feebb1cc417f4bf29482d2b70128cb0b366543e9074a95bc3031ffd901 -random_scalar 35d75f01dc1455e886e58776897ac3460017a165571bbf3e8abf2c2e401a6b0f -random_scalar 977336852b0f8b94ee80f021786fcb154f038fb62aac149c8ffc6200c5163709 -random_scalar b7f367b678a94d2a811d4c6e6b06b57055fabb36c0fc22599436df65a7a46109 -random_scalar 924b3ad7b7a58bd9cf675c4ed19b5b5b890cef5ca8e9f7f2d60b607317360d0f -random_scalar d2ebb58ad144eb508f832955739fc468a70505b3187329329f69c3ffae21f50c -random_scalar 3442f00a098497bca9fae5dc9fed4fff561b53007edf69904d8627c7b41e0900 -random_scalar a1cde4619213edbedc3528476a3e84e390fd3e7833e86e59512c65f59381be06 -random_scalar 02f97c5d607b03b7822392a0dbc96c160bcdc1d1e8f5c0333c5f0d5d430f4504 -random_scalar 7093044afac1ad315b6fe8ba49eb9311df49f56e4db3308d5ccb4892d1e5d503 -random_scalar fd646f9b2100009c441b524b82726a233d00fedfaea9fe73b0930695e916e60d -random_scalar a00eb2c9b6e7384c7da45898f7407585fcbc6c56430766745207b6ac4b7ff304 -random_scalar c956f7dc4e533e82ff48428092aa89251e269f86f12868f956f4e432027bff0a -random_scalar c3d4519bb3c62de749ac4cf9b71983c904648899dd7db3d468b4f3a7427f3f04 -random_scalar 8548fe94f37c98d58c164d6425ac1e6c58469516bcff9dc60cf84fe840622e04 -random_scalar 069a4cbb2284d71d91a32f0170a05dc34899c5878ec5b1a730676bc7b781a106 -random_scalar 9f64acab1c5c4697537b187894c702a5348ba9adc1b04c507291eb4eafebf502 -random_scalar 372d5200682f645734a4be4e28f26eb2783b29cdb91f4ba9ee5909de5ab58b07 -random_scalar f57ca646fc6d8226d9918fe0063c2aed0f57dba8f5e5ee474240125ccf5ecd00 -random_scalar 8f891f844943edf758d57f59fd750e6adfc6607541fcbec665bcb7a3b64c6306 -random_scalar 5bfca5b7c97784c86e125ae74c723501e07127639210a794e9eeece03b409a06 -random_scalar 85e5eaff8c53ff2dd88f888fa01b554d34245290ed25065ea540c8a0752c6709 -random_scalar 4230dc9af32f677d00d9e828b05d3094e5b0d1b166e8df0571ac7fc2ddc51d0f -random_scalar d79944b5965fb3cfd700bbf60fa3ee4f7ddd693816547f3dd3adb8e6e0a46c0f -random_scalar 26167d57ca1a456b82c5da2a3b06cd7c51bb9f2ddf2372b052c26b7a00f60605 -random_scalar 861e6f68c150e23c0a30bc59a7110875a38793a805de187c8d56a128d114ba05 -random_scalar 964cc4aaf44d5000ed33b884b13b4d7250bd5dda232ebafd7e6e12b54d78aa0c -random_scalar 0c4abd57b7145fc01e6765ac717e1237a6b158c81fcdb08ddc4512330bc97703 -random_scalar 872b9cef495ab4a776cc335159675521540b389a43c309a125d7ae91c77a3701 -random_scalar e351851f75d2ea767c6f5fb3f44c8b7ae8e585be9bcc67d5c799729979ef7c02 -random_scalar 63b9d7914096ce2c9904a5cffdee37076c28f3c94844507df5da2839f148f705 -random_scalar a72feedd729bf1453b90034eb33e0258866ebfacc340a211caddce72186b2b0b -random_scalar 1d4ce86a8c64eb50fe72c43306d0297e88fa2939d5c9d40afa7451403f640f07 -random_scalar 1eafb7bcaa48417b45e0f16fb4187f6aa7520d79e2a0000907f0d04769725302 -random_scalar ebfa52176c8b0af780d37817cac2a4d30e974b80180e6d827744ed83cdea080d -random_scalar c69d6ef54744f3294795e8c0a38bc7e2405f924cb69fe744dced6d8b0d404002 -random_scalar 6347961f950c3af015bb4cf727ab80ea438f16943ccbd70cb27ec7cdb32a370c -random_scalar 38730eb626ac9a5e813c6df53dbbb1eeda44bc10b23f5f762331efac05e65d09 -random_scalar 8ad8d1d414ab01be4c3df1b98120ccc14b8729f0f81b015f2a3242b021a8a007 -random_scalar 8eee6d7779ad3d7494dbcf31febb49a70a4544e19647313370db30ab99b28307 +random_scalar 6cf2e94a33a801b7f0822fb93a0bbeda3b1ae2b869bc48d1a4fbee82e1d85b09 +random_scalar b380e50df296578856654800f5b9d67d49aa9558546a670420732287b4c75e00 +random_scalar 921c21906cac3499d38f8ee5eb5bdc943a0f19358524d97ca9ad694ba3780707 +random_scalar c6164059b6e29b4f34101c3116625c245a56b8353cf276aacb54f08318ff5e0d +random_scalar 0d54c3c5d8046ad415e0bb9e99d359f60ddbaa07cf12cdb069ba80fa35021102 +random_scalar 23b888c3387955b2950af690c5b9bab6ca3af8ed9a9ffb951789277d50332e0b +random_scalar 42e4b4d13ed6f9302b0a050f7fac7fb7022d79976b9795c93946c433e2665d06 +random_scalar 347065b13ad735ff9e1cd8b471aca4f97d7690b326173fecbbd78528a6d8990e +random_scalar a843cbb2a416c655669cbf1a5f452beddba17c8f5c17711c9af6d2580b4b890a +random_scalar 93dba1d862d13f16c84fb050117483e48d5821979a4bf468901cf954f6b86d0c +random_scalar 307e5c1c1643cba6aec9b1f7c67fde082ff1686c6a53376b2b4a8a84af2c3f0f +random_scalar b92d6b60e266e5ffddd530548d77dfb7a3b251748acd16f6ffefab5ecdf2f902 +random_scalar 9e6d32f8ac881f5821a146969b33b89dd16e4dfcaa1dcae522aa080021caee0f +random_scalar 17072c56bfe20546f15c8bd14e91c861d7795bedd3bedf9120233c4e289c7906 +random_scalar a4c2fa4cbfda552b15ce8d764a594eef75660bf09613a94eb4d8e384aaa8d707 +random_scalar b9af0375cb59ce2ec40ae3c400878babbb26c2c8dc5cffc09918761813348605 +random_scalar 861465e05ae9e9015b9dc7b33973c7225b645fe72329d208a5b7f8089a027f07 +random_scalar 861adc8b323347fb0bc74db2ba2fff1641d731c5a9086d9e7938674ef3b41700 +random_scalar 421dc7ec33939a0716e9d4db7dfe35ce3f156014db5ef02de37f36a2f70f1b03 +random_scalar 09c0e3f497ba7c0361f2e372585a79bd91bf75b1dba43f85d3cb741b7f515900 +random_scalar 3a41820695587f9e9fea986516e7e41298189ef94582130eb767ccf69f97c506 +random_scalar 22b09c7e64d7c4e643fa7518f44952142286ef384ea4be9197a99ecac848b308 +random_scalar e75e4b57803e417212091f0eb12037bc8b615f4e50bd970b083b33a961ff4206 +random_scalar 147d1c20aeb10b2aa287356a5db580f109725e027bb2e7231ea1e4c030059a0d +random_scalar db7b4bc2e5be2a0a684003d1ad04e41df7f5596af02d48e81f8fc2623a7a170f +random_scalar aad7a3d1d4d39a64c556fe9c0fcdef45032750cab3985891d17b011d36533509 +random_scalar dc03587dc4792750ab1933544da05cd5dc9535481cd8c0eaa63ca998fd5eba0c +random_scalar 82b1687cf37d707c95b3e8ae92e83f2e77f870a20fa4961859d5e9550066ba09 +random_scalar 5fdf8d72853de3cabedcb9e29866dd80c0bffa5bdb23470a2fe8d07668d3670f +random_scalar a1afa6f3d04f23997142975c2839363e41951ec7123426aa863498e09d7eaf01 +random_scalar 116e122a7767f7c459d3751c2e42a322b627033c542c30269d55554878893406 +random_scalar 2c2a2a466c4f05b6af62c079f5dbf4b04072b34091391257b01670a80d959d00 +random_scalar 7fb3dd21094d153908ad1f3961c9bd67c95ce535ce02d2860da5d1ad66986d0c +random_scalar 8897b355e40352edd1eca4bdc456da6f3a82af638a6cb4758210fa0989f8880a +random_scalar d1018f5985ce7919f6b75f4f34e0b1298b320be736c4f8c2b112c750f4592c0f +random_scalar e31d163e7e89d784e5bde9c52c81331920dc53b491c970120907ea9778927b03 +random_scalar 52c86a476fa7eef5344371862dac4469d7f92893a0f5508ff1e605ccb099cc04 +random_scalar 3ca7f7c38e1ecd2642f2d0bb2ed18ef41450cbcf3a80792ed26504692800bb07 +random_scalar 7e1c02b1f254b7fe2c9f85aa16c9113bc66b9f635238df20ef49d845462dc508 +random_scalar 4c7f24c5324b124ac31489e8584fef342832de1c01801b91fea1d2c497f83c00 +random_scalar 8b8bab789852d49df9d4d6de669cf8f38a56edee67be1fbb0c4521ab4ffe0c0a +random_scalar e7a26fd1197911dcce065b766f07471c762193908b8902d7ab1382d2af80fb0f +random_scalar 799c72b7e571808f749b1b342ae30a145d5fefb3af6b1e1233e128dec8359f0d +random_scalar 63b1db6f0a4f98a26e783eb8fb73fb16ea0da816e47eb9231dd00a60e7517c03 +random_scalar ffe2b3d84d08d7bcc36695a1d57c5c0d835d8e76fcc9b961e017ed111ee4850f +random_scalar f881be752da6bdb31e29f1228760870668550fb793a091fc67d580d236a5c108 +random_scalar 013171a3c5ed17b2701874a1f15ec2ef281ec5f5304a497696c611755e39170e +random_scalar 69ca89c140209055502cadbb190b818c5305a297786a198c647061e7a7dd5f0e +random_scalar 56d497bf314bf6d27cbeed9c7834ec67836b6f9f007e1992164c106b705e8408 +random_scalar f0d3539042a82a700d7b8797ef0d7658f5b1b4b8e9689b0c34dd9f2a4262ea0a +random_scalar dab99505e85a1023b13e81adbe2e6bcd04246080939fa3df1d04aad2c48ed50a +random_scalar eef7506df7c961067d69556c7c29e7c7cc0250e0ef48c49a662f0cef06f5c603 +random_scalar f7a1fb65c3309ec3c128770bb05c933a7674fe839d9795bd95f0081f363cf408 +random_scalar 56bab8b86d061537cf3316284bf03a37026c767a8879329445ee09c702f9b600 +random_scalar c532228afc6624728ef743e6a603e3f22913a90f658ff889547d7c3a454c3707 +random_scalar ad79513e95b1682410fb64062137402a9ed6be978b5ad94617a19c1cd07d1804 +random_scalar 95b604b16d7e1c30de6064d0fa06b12468c3f943c1b7bf4a6af33eca4edf020f +random_scalar 0b63a6743c42b413d64c35d24887802f8c3958800f87bc03152274eebb32010f +random_scalar b3cb783e1d2eec53193cb55315ba57e1b8c2068e4237d95c4dd7cc9684b3780e +random_scalar 7a81d4d0e42d2860c5aaec0905eb21b7e7e30eee4c3ed250e897b948c9948b08 +random_scalar 32d2ba6f0aedd287d057b70e800f139b38fedd4e9ba086e8ea0bdce0edb2d60b +random_scalar feb926e119a69a7c20d685134959c950af88f240ab684c46ccb64a9bef3f5205 +random_scalar 01a2140b7d88300515e55027177df316ff001fd90c4f11707db6c9e20978fc05 +random_scalar e840ffd8973be1954603e57dbacf7f37e203e46ba2a6c2392edc486d90e4a705 +random_scalar 834363fdbc08edc274d32192ffbf96b8517c4670d4130ea010e243105a5b790b +random_scalar 83fc076430df5bc84823ee2a797a6233c42c212ddd10845f307b5b743f4aa50b +random_scalar 52aaf2984a859ec22ea2a1b4d3992a19ec4dcea4260192f043ed49cfe5ddd705 +random_scalar 0a560a580866b38724402d5d912b827f1fb2964af948dbe0496afa145793bd0d +random_scalar bafb7d180deec27520223d6fba4b9abfcff9c141c1fed61a595b788dc6645b01 +random_scalar 929e298412d697287b61cdcb0d0afa266ff954aed449e15f49f6f55121e99006 +random_scalar 29b159c4069d33395f3eedb93dd4122f9287dc1f55114250b3dc8061bb09970c +random_scalar ef5b9b18e232056b91a0b73da1fa364e7192b9276df3c428d5e3556b4fe9f402 +random_scalar 7d58f02afa1c64da5e2e05e7b703c53553e914e66c25a5dfb88063c05bcb7500 +random_scalar a7c8d18c3f879578f1bf331323358e7c7feecdb58b508444a6466504b6ead009 +random_scalar 0ae41c5656bb3d3a899df86a4b93db9eb7776fe76430025e759387d4df5e320a +random_scalar 43ed981404a1cf024c24484477941a7a92285723c0f47d6d57f4e1c66a28ce06 +random_scalar 939019df6ff9b3e37034a70527468f51a91099b29917772b12e475cec11eab07 +random_scalar cbcb6c3f15934e47741922a5593ae52e4bac95553f3a409742463a1350641800 +random_scalar 4ea2293558b34a5276e2ce4155f56073a3c6ec68d61695b62ae1459d7996020c +random_scalar 1477be630f529286ba32d34e15bb95e910146b61cb7b9758964233d17add960f +random_scalar 05595b226f810185b71c4bf9b93700f060dd40ea5a4dadfae05906688ff0820c +random_scalar 8d4b632a555e02e8537afa2084f545e74fb75cf2955129316c4f2540b667c009 +random_scalar ad03930a17cf94beb8c45f9300acf44c0f7792fba5a550aa7ef51326f44a2f06 +random_scalar c03cf7cf0b79beeaf20bd3209386fde99da3ef787e2be1749fe4cf2d0d197200 +random_scalar 379d16d753cb0de17d958056b596c4d0cf7b54a05e5f62218bb2285f2dc55501 +random_scalar 9d79792bf4708c06fa59f14af6141658a4b778a738ca2bcd1088f45df23f3e0d +random_scalar a056651374d7df2ed89c3e37ef9072063281aecd4ff95e48767da04c1fd71206 +random_scalar 005253f190dd9d70b900859b3057ef8f5cb01a99af2874b3abcb3a629d51a805 +random_scalar 9376111937238f6c53309773217cc89364f8697ea5f3d05ae47adb9174cef201 +random_scalar 1cb13c17e9005e6b38f21ae7013ae9507ade4590b24e5f2b54f842c62993040d +random_scalar c2bd6ec6c68431943f0b4d24b97aa83a4f6b465fb5dd9d9c1f73c26286c3500c +random_scalar 699eef9c8339ed18ae8129866549a7b6145cd05ea46c7e072491acf0dabc1206 +random_scalar 4eab216fcf6a99336807269418930e2c02544c3029a281b0ade0b34b71ac6c04 +random_scalar cee08c6dc0cee0f301a27733019fcb15888af5c0674d87867ca6629f13622f03 +random_scalar 4eb667a0a18c1e79586a74ec59a13331eba48da3f45d83617ac817955e25b00c +random_scalar 2848f8ce7ea8d18530abcc092a3d053133d1389da3a495df960e64f1a4545305 +random_scalar e5f42de9fbc98360b42823cf6f4531e089e5b2a5d2917a1d89e943c6c7ba480f +random_scalar 6e78afa29732cd6fd9a39b5887d407544b0e138ede3eabf21c7bd2fd4db48401 +random_scalar 42e07e460fcbcccdd9a214c5ebca0dcd6911caf42d1864d09082469ffe931a0e +random_scalar d0a42bfe1c237acf638fa6516f3c71da4fc9c2b85d69c3e85cfe31c501901103 +random_scalar 4b3027d51b259c08482eef0dcc73e80cb726211191db9181c5f22233adf7ab0f +random_scalar 750e74f5fb391bb8a196de61a1166e7a284068a9c4c2c484cb335e00bf438004 +random_scalar 348b577e0ec071701d71d4b3ba8f2062fa917a290b78ce983e85ce688405320c +random_scalar b075c73a504da7ac92749782444e4dc0de805f88337fcc34c707d1201c50510c +random_scalar d27cde45cdb31c0bcca9de3d4ec84fa812b21bbb4aa8e8cdc954e7f27b44e50b +random_scalar cba31cf6991a5985a9da11dfcb9d69a6024cd604045b1bb83c1efb54470d850a +random_scalar bd283786550ebb7681046aa685533c0638aecc8ea62f4bbe13f657fc7dfdac0e +random_scalar 2bd95267788121b1796525e28153f199f744d0f79b0f2ce294169454aae34605 +random_scalar 257dce0771e9afe289fa3cef1b405b4a715aa043da055932f85cc076c4838702 +random_scalar 5b515323f0a7d7ebafd8d628ef4f32925a6225ea92ec1410bc47d87c33044d06 +random_scalar 9816f6ccd6703afcc53026ae742dba2881cdd9fe4e60f16c46f3eed92dde1906 +random_scalar c6cf65615f4eeb1b7c883663a3ead84a3e631dae7a2e5c12bb33e0f340e8ac0a +random_scalar d915348a86948774d7ea9ecb5dd9dc7ef822d7ef31446e9f0a05a7009ffcdf04 +random_scalar f9918366b633b8fbe442783869f88e5a231fac34fc9a34782d2cd09b6254e60f +random_scalar a443ed5b8e5d178184c940534da5d6139bb2e0dce6a6ecde3f9fc040cea0640c +random_scalar aa2bdf5311d205af876f8f0d65a603097a618f58fa9b436fad0856d768fc6b03 +random_scalar 6e139003fe94890dc04c79a49229b4832922050ddad8ef676a65a48fa8a55706 +random_scalar c94b9e606eff6664b39b0926648ec87730ee77ed07130b8d1ece2ccad7100804 +random_scalar 86b64d3333d929a312b847a99956a6e343518f7eed681b21cc81e7e9b690d600 +random_scalar 6cf210cbba801072afa883a4db5dea1121b5a0fab727a046f6d08d1b76addb0f +random_scalar 1244e035744c4ebb6388543e6938850afd96a7e2b08035a03943d54eb4883206 +random_scalar 0254b1c7310f6597914ae0a40ae832ba1c7796011e93df17ba760420ca0a2d0d +random_scalar 7fa680a145f0220c50f677b75d4e39280f74da8e583c7e66518ba12a2862fe0b +random_scalar d5e6ae512163e2a522bc94a163e03b1e558e287496c8132cacdb4350513fa50c +random_scalar d7a99dfbed9dcd878c9d42d2c205e23fb9bf0b0036be1a60cf023f6a5b9d6909 +random_scalar 220ee699f2672c3bc0708aab2706ad88fb67512c729f461702937e6a238c4504 +random_scalar acc7a9f8c4aeb0ac278bc3fca64236fa687b82646f8c5d9cd9cceaf419ac0204 +random_scalar 5be20180845c56535a91452c6244a162a848dd84690b35e9961a8474ddfcf805 +random_scalar d458ba370df30fcb8f7c645f8506dffdb979eb03a21ac9d819736a97076aab0b +random_scalar 3d6635a5affbe91e9bccc662352eb8a1ae894530be8a37917f6940b6d351e707 +random_scalar 3a8e656a14a4a3d1bbb0df8f672b96e4325ade7d80592bd18ee3f2be3a67ad0f +random_scalar ad59917316b03769f7a71fd9e2258d5c4ac228a3dd93c7a6304aef89b9e15a04 +random_scalar a8c663a51fd373b2d0e307beefc1b50f2f5af6b5c5c1fd5b300f622732aae70d +random_scalar ad50787538fc7044f81405567ed64ae478491f76af12a77c567dd0cfff4b920a +random_scalar 25a814949866440b4420bf7e3336fa55f407ae856a76cc46d1a7ff440b469809 +random_scalar 3c888129b53eaf240041fecbb27369bca1b3f735233906a31550a9fe26521a05 +random_scalar aeceea1ca91bcf2520901c1c41c791c21f20dd3a8b82f2d098f650d124830d0f +random_scalar 21ce977442fa5ff47b8fc632785f782a9114af3ead1297923871e4655580a305 +random_scalar 86498832262769780b05960cd8f062f8534db98061672027009d7bb0f3d1db0b +random_scalar c5377213e42a52375754ec2d2d98ad283ba1864c931a1e59b61f7869092fce09 +random_scalar 5939e63a0bc9d02303e7bd6d91831d80b73e5ff7da73a693d602f00ab9cee706 +random_scalar f672736a70385eb3fa91a46f4ba82b0ee48400950d878470192522d9c6b75a0e +random_scalar 3b259cf2ae7eb8d3c7f58b8bda4ad825f34754f9c5307ebdb810441ca25edd0b +random_scalar ea8253023f95a292375e5aed563cd1adef66892a76357880c6f38dc76896da06 +random_scalar 22e128590dec1aed5cbd5e374cee868d0b3bf2ec06921ba98312e9bf1486880d +random_scalar cce38a425d652f4f0ac8e5ebd99eb05a111b51602e0f4527533325b015e70a05 +random_scalar 3798a47b4cdb21595d3fb5cf314347284771427f90dadaef72016f1904681206 +random_scalar 789fac4d4d7c47b88f8ca9a3cccc633aabadd8ff51337236b5d3c79cd8912c0a +random_scalar b9ea6cb523026f955cb221bb6cc0ad69dc4124b788045a024c941cc447b15401 +random_scalar a5cbf16377db957b2fb6eb28d188ebdf0823bb93c7341c904959952d16d7da0a +random_scalar 1d72637b2f247a0e22fe6cbf180a1ed2a4997697b798d793794b073ab11efe0e +random_scalar 6262411ffff10f36232a43afef4ae397b5c65d90fd4bd1ebc0ea177c37f7cf0b +random_scalar 4b43cc16ecb7dae45cdf9b09e79538bc96371dbd08253a1605d44d525732de0b +random_scalar a7e65b1eac2b19da0c5c91717a5487d1f9871b869010790d73fc3553c0953302 +random_scalar 2042fa43e1deacd9e831f479d4d2ccc166aae9d0b14445adcc9e9604509f140e +random_scalar d674c2e0650d9e1d4e73075486fb9181944348901da9afb0567da6e75c59f308 +random_scalar 10fd2439b976d8161281913e291f3f1e54bf8794f2aed81185dc747310ea2009 +random_scalar 307fddd3f3653ff27f7a3652e7f1095501f5c51f88cb61ec47562e2dfecab20c +random_scalar 5c03601ad1bf7f7f890f1a32ae424e89806356f30bf74f6476e62e0ddd65410b +random_scalar 5db471e746738434811ae12a90418a2f1b0050665150df02729b45b9a0f90b08 +random_scalar 6f05346735a766751fc79c64fb549151dd46204685f6d0c5f678e8170b202002 +random_scalar b13fe3e0b8bad83f67993092168d0c1339cc66d1884e6001c473ccf3137c7a08 +random_scalar c36c959956715d431b7cf5af690b5db62ad3229ad8c5f4eeaee453aff592d405 +random_scalar f1f9a2fb58f15e3fd05eda337a4209c30ee6b32d40dcbd6eff6dce6f1ba83709 +random_scalar d6314d8df0870b0619179476dd191f808c4e96d47137b6b89e8a9381d837160f +random_scalar f46d8cede2033e132575ab9c6519433567a2db44fb7c053956ad41ea5e249b03 +random_scalar 98be7216d6fbfb3d6fb37fe1a1e38cca1d801ab15b3c251aca64e82f5ae61209 +random_scalar 7b35e65078727ab59240e0134d9baa89bfea3d43725418276bad1df26cada30f +random_scalar 6609940a8220310253f8638e06908199442ef218f120fbec68bb9530a2c56208 +random_scalar 32a097ca954fd17c8d9b8106060c67e5ba791be5485da4550923a67f68826104 +random_scalar 667f9336db630f40f0ec10e2ffed4cf8dd7dba4903e8c3ed712ddb0ae35f4607 +random_scalar 6abbeab20ed718d14bfd7bf58e577eab3eda7bb40ca0aa80796cff472fa4960c +random_scalar e7a0d9d4d47840d844725ec063ca9fd1442e92ec8fde9240d15c3a837d1e080f +random_scalar bbe7ceee833f824b63fa02b6a49a3a96527fabe6d97b3edff98555498b991b04 +random_scalar a319115555a4114e4d68d0427c7e80cce22f3ec01b988949be420b2ca446a40f +random_scalar 6e61bcaeb19eac83173799404df79cbf3dd1d6a811b6ac6f1a6b9548a5b12500 +random_scalar 13aba81a8841745c078302b4464e26df22cfc420b0c7592a917564df71abd00e +random_scalar b17a5eefdbdd01be4c7ee398f0cac2d71863f8a76c1937f2bc206947ff941c0e +random_scalar e9f4aed4bebb6a0696eaa9e325ca919a80730bcf23e5f4e711558a59c819dd07 +random_scalar 9b39c9258a4af69da0a90b5afbf007eb8223448b2d24092100f61351302acd08 +random_scalar 0be7ff706ce5149aba5dd1f2882999128758122e716c525e906b47dadbe32e0f +random_scalar 9fdf56b57e89c03f3e5b3afbcdff3ffafe6ec0b113732438693a817d3199fd04 +random_scalar c2c78a9db04074d6ab99d0a6825ebf5d570f23361123348fb633920dd7f5760a +random_scalar 115c7ecbd2a8532b4b3eb6af88fe3f5add6ca6b47024fbcc7c22ac8c9ecc2208 +random_scalar ab0320c37a50b8f294ce6175dcba03681159180bec3de8fb41833ce9a26df10a +random_scalar 3e73c59503c2e914a69e9993d55939dd32d3bb9cda83f89ac117d69ff90e780c +random_scalar 214aa45e537027404d80bf212a50957ea74463db30d39d51059f379db3dc0b0a +random_scalar 4e9788e27021a286b2c3e0ee9d24afb5b4d325f991483b5a747f8fb1120b470d +random_scalar 0d502d51078467beedfeec12cdb6cda4bfdcee9e9f31d7e1c7fb771026899607 +random_scalar 1d946d3d2d58c2d7c0970058ebd8178a914a35867eae2478995037021570e105 +random_scalar 826364542061fdad216d339333d7f6c571e746db746a155fa280cc819da0380e +random_scalar 6cce49754611f83531bb6fa1b5dcc2895c20a3744b476c43653cb006831b7b05 +random_scalar 0697537587dfbdb885060d3baaf7fcbe721b1676ad61bcf7f2e5524dbe403f0b +random_scalar 94ba20fef767ebae55b5accf1d41767b2e5cf3c5182ce3a02bad7b6772955406 +random_scalar 226d1f82fb68da5da182a8ec88a4f317cc51c280fd50d2048c7f5991edf03a01 +random_scalar 42aaf88cf69e4558541ea582afffd26d90652d062dda640a14412d751ef37e0c +random_scalar c87a1f9b1aa80f71d1c78d57c21fe47d7f56aa2d4f397f72fb2af8ef2a401b04 +random_scalar 23344bcfa22e85cbd315f370075e3af849f361f7a2e960626564855071b1f004 +random_scalar a1dfa3cc72b59b1e4bd70e25b10107f1a0e4270f09a07122845379cf56dac104 +random_scalar 46b1f9a4a2d23ef6f65190d0d763d0484d5a43e075840ba522586e956b824906 +random_scalar 0df69f6758ce363fe6090e9ea60b8e4e62506cb032a16c4d4cff29e8ceb7a10c +random_scalar 41322acad808cc6b332d7ce9ac45b3ec216596186e4d2e1524414b1e56174f0c +random_scalar 555b681ba25e17cf7a493db2360887ec410df0d1531a21fdb3a70080313c170d +random_scalar 493d080d01bcf337fa7e81cb9467ee528e245413fb3d100323dbda0c1429310e +random_scalar 0d6bd8be3a66d83039fe5abb78c2258bbd028ca211416f3c011a49f358ae0e0e +random_scalar 3a872ce933eaaf2f9c845fa8c73651300f5bcfcd59b6faaef12a2f368cd1c30f +random_scalar e63184fb512ed19db445caeae0adfee4e373ba93116c661bc60dae380133a104 +random_scalar efab70caabf4a2a5a67f12ea27171df5ec4e3394c8f3b352effb47a558c35604 +random_scalar 7de9e6ee6a1ba4019077114e83e29078f89ebbebe688248ca3acc3c8539eb80f +random_scalar 64825ec5a071749732ce7354e234a0293828de98a98c3a563662e509b7072a05 +random_scalar dba5ec762e38f676ca8a85876c0bb3c11ab57838893793ff81a47c767261380a +random_scalar 22f506889b19f0f699dcce8e3d9bdbe16d91a6105a26e8b3f95e8cb17be20001 +random_scalar 09c435aaecac79899d508796a85d0d55059fb8ea344602e206120fff6e0c6d05 +random_scalar c061af8c54ab90c35b816115925942a66115f76719330346ba55523d0b4e5906 +random_scalar a8004b7d6c33dce271c9dab93db10a4c5809673a143e9991a8bf688575207902 +random_scalar a55c1c11ab918f42b3e45995534e3076d697f9329cdc86ce419763b752609e08 +random_scalar dc5afdbda56cdd0c354ce7048ca6615c5cf10ad6d3c31c8c416810fab6af4e05 +random_scalar 947637dd4cf8fd8a43222c520ccc872a3f91eb5dedf83ee15f6d7cd254d4150c +random_scalar b94df95cbf6f3b1f376de77761919abae14bcf01d99c9acb3e0d461d1f1bf10a +random_scalar c5a108186032e712dd7aeece86651f1f94dbb7b8c02ee5fcf082f8f1e2f8b80a +random_scalar 0e6fee198b15b13801687b7fcc04622035186f71d6010c4e880f28ab75607f00 +random_scalar f352cd628d86bd73fa5f3c947b9f838d00966bd2915d988ed8bd2067cf8c840e +random_scalar c5ab4129d42396221bbb4274a20b231f7507cb0b65911f765a943751800fb90a +random_scalar dc8c5d0e642ee21c437fff0f77a08b9a4795c467f3c50354083148b796fbfe08 +random_scalar 329b4c0b05d1ce5621612491328cac7f1509001fb5f373a66e8e52090b91fa0d +random_scalar 152e4e3f2801194a38b7213fcd2e53859ed22d3e176700921b044db74acaa903 +random_scalar 17cd1a31b9b315713c2a6e8295419645918bf8ffa1670441d4dcbac6fcda9c00 +random_scalar a8e83c4ddf185d7b048ca69a96753bd055071f43ba4bbd1ce9527b172a796e0c +random_scalar ac881d61a4fc26880ff82fe4fce254c5db672b6500ce05658104a39c9792d30d +random_scalar 44bd5a9744c3ca92f9429e31a26c1e8206fa88d6b0109767df3dd6b5e081d701 +random_scalar df585700d24b16794d4e931a26fd3ebd666604cf0be40acc032e9e704153f905 +random_scalar 444ec7f3eba365d9e5fa91b4fd19df66b4f982ebb76d6a18a2c6f220ede2b001 +random_scalar 3f2569c0d441efbb3a15aa01ec773eb67d52bf2563fcb6828497c24d0b64c60e +random_scalar e162205c97f598794353239f02214f25895705b864f472a59d229301a7c41d00 +random_scalar 8dcd442061dcd332fe0ac86c5f97d00cc59c8029f18acc3b5e9de5e8fb910401 +random_scalar 40a6e2b2469b93dcd95aec551033180583f1509c3dbc484b54ca2e5a3fd8a702 +random_scalar 6d87007ff22b03e9b11a97ef2b7d16171d2f6e3b79970f261e1d3236a179d307 +random_scalar e4de14222ccd095e68bef9a3405b778dcdfe6ef5aa178142cfd2819d623ba60f +random_scalar 42bf10f06625682a429c62ee0856ae168d4214aa254732ecfe67fc73cd2be606 +random_scalar bfb5ff7e7b27b0d32d822ea3abce41d30078b9de1e9dba88944ce558d3991c00 +random_scalar 1ce399ce3590215c271c7872d0e7b3b958e108d40b1319ff31a42d5b83ade90d +random_scalar c50084e02284eddb75cf7f0655b63eef571177042bc9e5dc29df3ed4f54b0408 +random_scalar 578d31434a982bb5ec6ce9a931199c7a42176f04264a35ad71847fe06781d80c +random_scalar ca3ae8837892a88874f8c675fd4ebfc59fca8d98e056093f25d88bb07908600e +random_scalar 259ea7deeacd6a38f0d9619cdab2774dbbb93b1d17cb32b3051fc5906c9b1e0d +random_scalar f59d75c0834df84b4aae8c73523388c4eb57c125fdfd0cdc52389751673d3902 +random_scalar 04b222b3c67335aad76448daa244472f40f6764d851cd047e3206d8ac32c0c07 +random_scalar 63ef886fcd7cc534d1e141d64f5af699633c6ce9eb6e4c104ef03300f150a901 +random_scalar e4fac60ab7fb0c2c2c32039cfba4760c6c22a5c2f3c437c50a43b198d817a601 +random_scalar 8b57cb35f1a0714a904896ce7e9a515d7083259f1d325355910c5ef6c79fd90c +random_scalar dc84b8cfce597f499ffbd52829f4dbfb3b110a3482b168c223bbe24cd5aa4800 +random_scalar 3d7d972adc1b1279f71a6eaa4deb8c05a73be74345c7e862ced54822be0f7309 +random_scalar b4129d530f956b59b0bfdfb2036f4ae189cc96c9086bdca921cd348bde327403 +random_scalar 14144a16940145ae1c8ffc09cf5cd15f640d774bb78b95b756de5d7f9af47606 +random_scalar c9e4e8fd6916595ec1b92fcefd2313df304b05856716c8c7ed6b5589f0f72c07 +random_scalar 7b12882ce5338bad3fb9245e65254a8140552f6c89e95a799daeb817d602e109 hash_to_scalar 59d28aeade98016722948bf596af0b7deb5dd641f1aa2a906bd4e1 7d0b25809fc4032a81dd5b0f721a2b21f7f68157c834374f580876f5d91f7409 hash_to_scalar 60d9a4b96951481ab458 b0955682b297dbcae4a5c1b6f21addb211d6180632b538472045b5d592c38109 hash_to_scalar 7d535b4896ddc350a5fdff 7bb1a59783be93ada537801f31ef52b0d2ea135a084c47cbad9a7c6b0d2c990f @@ -847,262 +847,262 @@ hash_to_scalar ea753ba82b4573d428d526de89daccdfa7a33079aa9c9ac3 d9bf5688fed9c3f4 hash_to_scalar 2a5d772c76aae0040915ffd7 8227a0904298e726cedb75746b0a3b41662002b4ed8fa05c2110e4a15bfd310c hash_to_scalar 585900d92342cb8448c944f97d1642 acfa0d43b9c6a91810b0fa1bd42f0d9e077f7f5a62f9d9aaff8418e1130ea508 hash_to_scalar 2ace 427f5090283713a2a8448285f2a22cc8cf5374845766b6370425e2319e40f50d -generate_keys 5b1bff17a9c26feff8ba3fabc9e14d9c729bc182a4e5e6c0ea390510d0b69f4d 548b044902b6da1edf7abbba8de293cc2c8b3db901864027d35d1587f003e50f -generate_keys 50bedfc7485d87610cbb55f052585bee67e9955e3eb4cf48216a3c370f0a67f8 3b290c6a5465047525d4b639763d778ee55b1e9683c5fbcf809c8867d579d809 -generate_keys 1660aadd3abd4b8c4ed1c618969b15d68de63df06fd5c2a570b12daa644c245c bc3d92fed4453a9a359b64236b8de6c3f31ce1369c2e745c46fd0441bb28a708 -generate_keys bbb0a1b8f6f66ed9e2cd0c1a84bb7b6823480ea2dc4830d155681e69b62ed762 30ee49af7ce72b883aba42adc06bd4d04a77c92849b27755a878be4137ad8304 -generate_keys 4de96ef7e6052e3362fa1b95da33ccff4b49f182a9b1894d03d6ca7c74f70296 0c827ac906c29c3bd85791d530ecb6e22eb1437536a9cf4d67494daed44af402 -generate_keys 3b0bb4bff19c10709f40ab280296f46a12bbcfad5c76c85be2d1edb6647276e5 3c36eba7519901a99750a50d0f57be30b8b7b40106a410f92bb1e977a89cac0c -generate_keys a4bf1f9e58c1b739616ac76505740991196f7a8fbe6b3707100498a958b6570a f934d64699453da097dd21ddfa04eb5862dd25115e1cf4e590f49fa017902a03 -generate_keys 8ac96afdc404c837c3668e412096d667453e598ae8d1c061e7a85853168e5fa9 bbed60a47a517ee49a0763e8daf2aff02461690faec1d0575b7d5d8b9c57bf0d -generate_keys fea2995a1a360eba2569ea99c59d4e516e4835b96d9a4e50d3e3c2953b8bc68d 4e9c7f7a92c7a90f84a3a2a27b8d0a8fd5139d35e76bde9748ebb7279c3b6b02 -generate_keys b475418a659dbf6eac90d553f70b90ef4792d5a9008f6fd3d6fe9206b51e5f74 02d6a8fc49eb02ccc2bc5e8945b9cfdbf613ea925474cd6bc09c502cc203a808 -generate_keys 94d245388554d0649bcc946cec9d75712004a0b2afa9a9bc7cabb2a2bbf0a307 2e03b177cfad669246e48dcafb04c68cb0909c4e172b1c31247ff557406b6609 -generate_keys ef30ef6d032fbe80675ab89c4ff82f16efe346957ef618cc7a6d57a3682727ad 90cc1294e09a386629d14b85392cde3e40c11d3ab9ff3b7962977825d0d22c0b -generate_keys 79aa700d1080e221df74db8284a39a253585b806bf308b8b0b2ae9d8d16797e5 d16fffaac46f8ecb6c1889f5ede0dd4f2fe6d97cf276de2e9d8b7985cc0de208 -generate_keys 1f8bc117bba6d6203629ad1bb7c7a685f5380b158e3ce62f12a566f45b5723b9 117bee308c1a471adff1f208b36b647a156095d94af3afa0e5526377fd169806 -generate_keys cf7d56347ea427d761ab587fb4eec7aec14c0f2b8e7ddbf3c08481a7a7fd5c86 a7d3618ecd9f0fc023abdf658efe47aacb91025831fe324d5d67c5d073b8b506 -generate_keys 4cc97e74f5775eb20e25fc62b5a6f89b2f0c8c4e25a781b4d803edcbf38f163e 6c1af6a11d377c5d4a52d679cad6b099a7b9dbd73ad996feb2383c09e1f5ce0c -generate_keys c76662b6c0cbfe654431494db620282c549b1118ab609c101f6d3477a4fdf021 af13fb9fcf24ba1e4af5f389e660c38749906f7520e0a31369d4e6208b67d00a -generate_keys 7bd25381eb1de9be0cddb5201d9412c5180b0591954507821439e9b8a54448f9 5dbab52b0f2fd0da15a5873b86ed5798c25fb89b458da66f5050751a7bf12d03 -generate_keys d031a3b2fc50d238bc6784506a2067076bd98b4705db0065022c4d8553acf5d7 d163039b46085c2685b818ece1b0242151f8da82ce0c84f52b79ebf66486bd0f -generate_keys 17b080a8ebd878f101861cf23d20537a6dc3d88b8e831ba3b05b719993eb4f30 ff804532c84e4a0422c8ae62201d65d1c1d39748ab235ae90d1f2a200b807909 -generate_keys af5862785966c79585df522ca6c4e0f4f4249d46e19daca1a9f5dea6c6d9618e c46e7d74486182e5bc9adc051f3312f289603ec63f6c94f435e5a1ab3277cc06 -generate_keys c235c61d248da5b1d8d8e39885063f56b6bbefda3218f4225d709c9df0ef7ada d7d6ded3cd6151753d8a6685dbebf9feb804187895df8abaae42b30f66a9f503 -generate_keys 8fd66befdc1e9e9d46a2d2b68ec2c58f8ca77295079f277e3446fccf748e6bf0 8f7201d0cb6b8c7f035089e6b69ca23f118f46cd4a4869b9d79a63114be28707 -generate_keys d88dbb2c218b73935d980be99468b6c4f8fdb65bfe3d9db2e3327b3bbe9a6751 c193575c0c7eb2da96e4309f259d24a722a7cdc040c6e139ab4c03cd23c4130c -generate_keys a33ae9b0052c4377ec5162a3860acfa271d1d89eaebc7ee5b2d78c9bdaf65b98 241ad9128abba5c0535baaa0f8ee0aa9d53badf4cfba3a209a2e2a6f71314108 -generate_keys 9206e2f4021e1dab6986a7f8cbd3c8c5f713c155dcda7c722a5ed1ee160b3fd2 de576852c9f130a3865d9795a4f5f5615a43fbd49c09d5e9ed485c95e6a6800f -generate_keys fb210efcaabb5f7a5f65d78c459b9d5c1cb0cbe972c15e29194fe5b8af4d9229 fe2bcc33b7faaca55c5f2c5d18212e7ad8d29782b7088202046c011269d1a20a -generate_keys 58ee355886041dbe8cbbb7738014abd69d1f75b6cdaf32bb2a56126a02be61be 05c9b44c3a12373f9683266a7fe8950d7909a29f0497de77bb5b94ee92d78a02 -generate_keys 8dc35a32b21d5a841532e275d10ea73d56d3f37b6aea701ccd6a5bc1ea5bb5bc 0afbdd7bf6cf3b6eed97a7b231f517c29c4731433a7744502ce16d23d01ac603 -generate_keys 904d35fdb4060a7fc9848f90bf268541b76e4cf5417dcfb9413af248d194d6ac a111f0ff77042009a7a376b159eb068f7a2f1c4bcea12d23df78e5067ee87d08 -generate_keys 1d82d6b307e5475fae2ad9b83032158eb4cd4333e301c93e973bfe0ade007345 3684110de307ee5167b1daa06f57496f9ecbf6957f59adda0569d7580f90c507 -generate_keys 585b8ac5c39f152e81c125f5a4eb05c5aefb2406bf435c771ca13ae337604808 bdaa87631bce2d4f1b17f489387accafb946655c0f885f06ad9c91c89fc9b000 -generate_keys cf80081246896ed7b5e2a2028457d0fb49db747bd06d797a7ff751b542094ca6 1ba926a3eb6b5a9921ea3428bb5267c1bc7e9c85203244e86d2c8d364a02d207 -generate_keys 294258f27ca6632b11e2d3df89ff50a323aec9e4ca110e407022e17468562819 67669913687c7462e3df45e9818921f69c6155994e8e8963e91d1c92817e8101 -generate_keys f79c754e9b2449064fceddc59c464a65bafa1032f8e426eb8d6aff03385328d1 3edfcebdc2e427686da5228f7f05e14a1ef242535af45c66d38f1be17c852906 -generate_keys e31ddb15d87d8a959d4956b25f8dfc45f1d54fb567db2603a13e36f1ce450f1e 1553e2b92de9efac7588423a60156d451bfdf4591856a49dc5a037c141e14b0e -generate_keys 5e42b38d735259a11dbc738456a63359806dad3120b38dde9a5233f37909f680 40504b7117797a1fdc632d7d9cc148bf0db55210a155c0f7ee44b60ba3844104 -generate_keys ce6b8ed77b3c51d78004532c74d6eb051dcbfd73975140218018e674686f23fe f3ced0e2971c60b44a5e84c5810ddf1310f81ba5ff8a12b24d0cd264dae71607 -generate_keys 00498e98c66511a6079ae1102c35b0e722c88fcca77b4ea5b665ce318ca86f96 ffd5f03495784f5b8a79a6e1f77d21894a2a37406a09ae84c32a9b6266d58e0a -generate_keys d1efddb196a689f10dd9819e4a8166b62839b438e4e1dd46c805dcf8b570a31f c3c9323d3e446d220c7059fd98fa0c9ab9b026212835a970378c143c938a3108 -generate_keys 30eda42e7fd79d90e653fa605172eba8f3677a6c6dead0acb93b6e1b04c4c2de 6fe06ec6c1accc7e1565f472c5a7ef867a3b1d23f559d0810ac124616aa1830b -generate_keys b3ab042bca24c4a39759190898c25c50482cb73abcfb29667a7fd9efcde04e6b cb9eed98bf42c8cbf1cdc198262c71cd061d997603f2adeb775507be60c6af0b -generate_keys 2dd2227e2c0389d859b2d9750fb11a8c9091317256c2cdd225ee92ab07f896f6 d6598b1a68f7336b82c8aa371ab7e00c4051177223bbf9983ce33f918e16a704 -generate_keys e19ecf5adb04349a5044c197304d00024a76137cca9cc5ec4b517d0ffab6cc07 798f93cc0628d50ea67efede34650bb8b1596cd5e1b5a1c6f89b8e9389310203 -generate_keys 45434718ef414575caaf5c1f9c6e5e6646ca908eba755c309f7894b939e883d2 0f809e03709ed6f0e9daced253b315d91ff7f9550981b3fa6450a05bb92fa40c -generate_keys ed0ca85d956ea041613206545696d3864080268226dad32fdd41cd222c42c045 6b9518aab7daa6cff16778f448d4e2b36c478a4fd9381b4e6c33370c2f3af40a -generate_keys 69a3628d2fca516ccd9dd28340a950d462691c8873325ab062c26257907bed95 a6e7dd03681bdd1695aa2022c7d378cbe78300a6fc58e5fa8e0ac61bd3af8a0e -generate_keys 3db683cde4d0c0ae2efce188268f5c1ca87a3ad21546ac71cb9e975401426986 c95e5da7115f792fd4fab589e45855f31de19d341b2917d31db8f86ace60c10b -generate_keys 831eaf30631ac76f73107d6d828f8369b4aeca7aa7ed469388c6685f1d8af419 bc1f1efa933caab6c5d608fa202284da37e53815429ef84982671da669785606 -generate_keys 632c0aa6df19aa1f404e39f1ff49538120be12bb9f22cd35ecce9a2982a8a593 bae04146f2ce06f20686664d800a9c6dadfeb6fc8cf377af01dedd137150060f -generate_keys 26b2e8dc65f82ba76741b47d7a37ecfc89e40a4f47e32b4dfd5ae6eb73039180 fceda7c8835b0b559ddf17d41a12e3a14f2f52add7cb73feb0b2cdb40c7a4a0f -generate_keys 9664a312a2fc2ec3e9f99e5ab5941000d08de91db78ecaacb136b0546bb8b3d2 236b68574f16ef4dbfc3702e16498049443d7bb821ee47d146a45e46beac8a0c -generate_keys d8e60e86158b4ad40545ffbc1d5c4e5304e32e60719568ee9a6438a04c301e24 83c0ccc0495659b4ee28532f204eedaa1ffdcbd0bb20e4df7b336ed641c4410a -generate_keys 093613f65e539c735398e2d623099e2a2b4efb0378fa5151760dffc6aa9fee1e 9798015bb8fe277c836b17b7770da6620c3c0bf40fe837f30f69d233b6e7eb0d -generate_keys ff3e3883b3f1c120419816ea29b21c5579c3430b0d1cbef86ebf27cf5fe73c4f 4c62326d13d3a1667fd9d14ed592d03044097a60f940d9942d43e0afc080f102 -generate_keys b4152f960aef5d4c332b75385f9fbfa22abbac2d52f2ada962187fb522cc9916 7dd6cecbf9a3ae112a4c8857cd922d9e986f4125d415a75b8bd657ef6a6ce90f -generate_keys b2f9cb612b6cc8f2dcf6402a0d101a85e490d976e26afa77168e030bd2ecb32e f1c97787f585ec27df9292df3e3d0d1d915a564dd80640feb437490c58c43303 -generate_keys a64b351b5d6feb45817ddd31696ead6acc0bcfe4b38439761121232f79ac5089 0aa3a82fdebd778527502e1c644dd2c344c2c8c493c62059cd1c33c213a0a705 -generate_keys 3bf47d762cc2c6432a047ef071d7dd45577aa7e5de22540f13f92a06857dc7d8 12a0d11516b36c5edf46d0d095cc3ffdaf3d792b34ee4ea07d1e76424a70f508 -generate_keys 1612a1c727a36e5e6e04c941905783f29ed79555633f063ac110821bfaa318d6 dffee88cd40a8ea4cec5f8be7b8161cac9d9c3891f4cecc290e001b0adae4808 -generate_keys 725820f73a8305fad708facb32ddc98f4c98a104d366b1c314e4850daa3a4b41 c3503c975c72817e604429bbd976f2be9f4527ecf0ffeeb89f17cabd5f405304 -generate_keys b5adf16d1e703bd6ad3f08184cbafbe03fba3926076f6d6b843f4d9efdc934e0 8337f17836182f718a14369415556e5c7efdf2b7a8a0e6f181db9f68a1370a08 -generate_keys 278c8ca7605fe252699e0ca5897b8b87b68da1e86602505c1688f86c57a684db 7d36240639e26a7738e54281d33e3f9f359b8b9ac79f7133e23c9cb28737490c -generate_keys 77212eb4ec3bb2239f409fc4e33b1626da4d1183c7a4c88784a6ddb7a4f32495 7b8c06a2a9f9119eb22c02c0299d27db2d75c58fa6f104c922be6e24f59eed05 -generate_keys fd470afac9eb2ef7ee1802746b4cb46a5c247dca54a14a12fc32af81d2d6bc1f 5a706c562f3971e2b51edd836dd81947c6f263c8620e0c4a14182316d3d39b0a -generate_keys 58a4ba97f03eb8d05855c7df06c863baa5bf0203538c70ce868154d8be8b6e03 702046003079df66b8316d46722b2e3732966c99d6443141237113f52f006205 -generate_keys e7a8abaf96e61cc7be03011376d3f204169c0b6e7f8a087856c11eb503f485d4 18967c04a7183a2174850e1389a74d005cd4730235e68803b2cadfd3fdae2d04 -generate_keys 8c19e0356b3a78b18a492643c1cffccec3c92117219103cb634db028e8141788 c72699bb88889d06f6b74e27ec66d5e004b8892d5f17afbd7540b056a7439702 -generate_keys 73fd4b38e1fa460396fdd206580ffd682b732fe0c5b76d4e7124457073b0c9c2 83ac6807cecb9858d19a33b7154110b1e5ab9c53739dbb86ec61ea60db526e01 -generate_keys 05a72d54166657932d78442c263ba7089bc081d15deb4720ea01ee10d89eb68c 7e0887b87721af58854753387b4868812dfad99f81936a7f18da8a30c844c50b -generate_keys 53c083bf0d9695f0e7f0c639a394cc15571c80148646fcf87748365fc702a809 e84473be8e06e04b88062bb2728a5f1cf2a6a61d199af677eb771979ec503a0f -generate_keys 475974688153f2e244c9ba25549fa5dabd4bb3a457a011d95ce1ac8a93643079 d026ae72a7b4eae888d1b00464b6a4f524f58b68f3e28d1b87fa852aa76b6807 -generate_keys d0446bbafd1d81e47441a0d0f662d35d8e8e8c7a46d340298880ae79f6b8d83e baf8cfd4040391d3c52cbabaa5f0236838fe235df2745b02235bb82a90da380e -generate_keys 229d8c9229ba7aaadcd575cc825ac2bd0301fff46cc05bd01110535ce43a15d1 217b89eb99e66c31eea36de52cd05fbdbbd612935f666838bf2949a757998807 -generate_keys 280b1c899bb52cb8a597cede3730a1383dc0a48c63fc133dde87d89dd513cb07 3335c7acab6860d5f1ca823a9dcaecd92270622f5f8f5270a75f379df1200e03 -generate_keys 88e6db236fc9f01c11b6c3f26d4cdf2763704756442ae8501214416d8e1a7082 01e7bb013452a447324b1e8bac74108b82750ab5b612a6f8c162005a67be1d02 -generate_keys fdadcb4433fa04a2b6900bfcca8bf4bfd39ab6df876d788e309028b116b17a33 cb1e5891dacc9428113c786971305f30c7761f6aaf6f325f2bac6ebf18c89802 -generate_keys 6430989596c8225d82fc4b21cee6518def534620961a4176cdf989b0b9cdba17 4da2ae7366a6069e46042cbafc63283d5d672c4c994ec038c0686f2599fb3b08 -generate_keys 80cc6c6ef5b49f607787e32a47ee2ac8a41f9e0fbacd1c4be329861aa1e54f90 a943c700fa1b28bdb407ecdf5b30ed3d086dd977c7fd1ae9e2cfbb398605d200 -generate_keys 1a8c936e9fe8d6088763bc602401db3e41bd5e41d1c1639d9b73c7ab5da9256b 150710ed8fdab598e150f6db8503a5caec8490ad369ee5e23f3ea664ce8aa703 -generate_keys 17bc58ecf7ee52da6bbae74ad453d5f7f87de51ce540733f2cdb1ac0973ff244 2902db0cba12872ef8421311bf7dc9e22aff133d582fa67ec0d8f6a61b148700 -generate_keys ad5111aa930a4a1b9d41fddb573f302884ceeecc57d8b8a52c288130c856e13c 2de137448d0613c2da977be5561977b1ca2bf04db559bd06dce8e49946657504 -generate_keys 14168064cfd8d2e746e7bedb3d184b9e1bc6d4544512fde8f0231492c30b2067 7b0622d19cf94e1f7bc5b551d561464e425f1634279f64d701cab25261714901 -generate_keys 6b507f25c8b74ea00962b5d2fc3d0f60d5b8a9acc07b7168ed7abc8e243aa17c b2a05c536f87e96ec5461a74d5397bc61e176330e3550b190ee3f2119fe20d07 -generate_keys 7ead086dd0aa68bb70af16667e0687a51da9691c569e716e8ecc8292a5c60ae4 fa181562cafc0f62f14b014bf650263a0e758245077592c5048d6bae9ce7b904 -generate_keys ae7faf51af45cae65d9500800ffa4e23c42fa6591a373850ad7824812afabb20 21b7257d3a2cc7cc42f8b9e1ccfe30b1552fdf2179f3af298481c322cde5c40b -generate_keys 291d9d0ee240b44b92e7e0a06d592585bdc5eb2a8e830286e3db67ba7cef1d2b 23d8064d7fc3bb0a5f79e669adc88a12b85c479ac544e415cf92dad46bc8520d -generate_keys 314fb5acb1aa0443744588951de4ff42b46e9c84ac3fc2f8f7950849972d8ef2 5b8535c4e2c163643414446408a02cc121418ae291eaf94be302db00fb908a09 -generate_keys 09270e301d35ffe97a31c8ba8d05db2f7cc4ce39757f2169d68f7771345974b5 fdb02e5ee98ed6960e6ce10181076ea8303cb314611384f4248dd4e01257ea00 -generate_keys 26b91657d0625dbdf78201f61913e2fa6fae9d3dceb19d0861faf616dfba1466 4e929bf2e36a19a583beb53029ab0e51bdd6a9489e4ad272024033323feff80c -generate_keys 3a989ed61cfa729599a34361621d69ffa0cd5d69470f8b49787b1dfb7d26d302 0ddb22893d5500c4220c130b6824c2605fe41e250fb7d1a9bc446ea80020ed0f -generate_keys 6585ef6b4f6992bc611d1eb1e506f09f977ae10d402244f60c315a2d0cdd03db 66a87aa4006e807d4eb26134bd13719e0438b5fdd7d9ae81f22ac0d7ddbaa402 -generate_keys 3a869f822f6e2bf8398addb052e6ff449426d18c72f1efc1162ea53406cec119 2be030a5fa7b68696e4619d84716544bfdef9c202d7f11dbab4f67b7d0dbbb07 -generate_keys 7d000a90146cf13716e32bc6223a433074ac823d54219c62787f1644ca7ce01a 09920478e8185daad53f89b26f889c47cc953a6cf179767a865236550e51f109 -generate_keys 534483c22be59d10891a6aaac4aa60cbc368f20e5865a617d201c1219fca65c0 3a89c221c3e8bb29a1bac504b0f1304eb945d0ca651e203f86944d01590b500d -generate_keys 2dd8415ac4c83c6de33d03abb01fca432c2b179501081e5840a6508a43701030 3efef35f253f8e93463c6dde4076ddbed0b155e8d509dd2258f0579d9a3d5508 -generate_keys 890beb49d277495977b0b4b0928f2b41d5c8a5424a48fd7df2db1bae665a392f 3cf1a35f01377bd70a190b3673e3248d2b1543863ef0e0d9fa0e3b4ee99eac0d -generate_keys 59ec4b30bf26d68c96617759bc6ffc3bdd3fb5bb1afd8796dd2a5b15efd2f9b1 2a3be73ba5a3ac03c066ba7f5927c73f604633e53b3f3a6bffea5f33de5b200f -generate_keys 4712cf846db2dc79aa8eead0ebbd8852869503f9f2bf4cf94a769febaf387df3 2ba8d586d863153b613b7511e73b262f33d6d39e2a359871d8e4d57a7a85dd05 -generate_keys 20d59ce77abd46b04b2adbf65c4932b88e357e95e47251df0e3711be03d885fb c55b91ebcd79c92ec29b31b9962d23e5d8cbdaae51416824eac092702eadb20e -generate_keys c2daaa84bfe48f2df408288535822058f73680c3284e4c6a05680c2f6c2ce450 916aeacc03422c207402e485c9820299cdef0065b3a573d1621ace920639a50a -generate_keys 648cbd6469186de180d808421697025ac45540b06058f30318a324f228545cb9 1291cce65423b162db3c9fd66da9c5c0ebc5423058d76f8958bea45ce726850b -generate_keys a26572b9975e2b8fb0c02743c0b2b8c32f4f16c0546ebd4be2f74d88742ce996 6955b7d248f12a838afbd106ea6e43127478a41e584ea6090ea581af2a28a206 -generate_keys dbc87b3bbd00cbef20ba1fdd51a928f4f24211a89af3730d1dbd9c90a9efedf4 93d3a24f5f38172b70f46cfaaf7eb115b88474b55c4f122cd3eb06abfcaf7701 -generate_keys c2e40aa89e135078c8c4b0db948d03a751fa28eb45a1a37f22031d0147b98bdc 8b68edede1fd6aae455f40f97d4b4155ae2b32e01a530803d9515bd514ab5508 -generate_keys 7fdf4a2497e2bcd1adc644b5fc1acd7eb0fe53f8f5c519b2444a2826e8463cc4 696b161cea1a0234e1fa4d50763a8eb6361043a0a2954bd325d9c71f1336ad0c -generate_keys 0c0de80107edfabc84bac72cbca0d1038fcb0a32dc25bb5017eb52c072021c6f bc2f2e26acc1f460cca178cc38fff83c90ea3451392ad788503b3cd82eb6a003 -generate_keys 04d641fdfe440f3e01a3596e4200c857d24e2918d3764080c5a2f7c44e38ade5 68fa96ac8e5b428e170b45fb0ac3151bdcd3a1a7022abd8e3c4584adfca4d200 -generate_keys 807688153b50640b7abaa3d9d1825818dced5bad08874c645f9c55d1defc8a8c ac3a5e34247bfc11ab12de6bee20e4be8b86be1e83c907591eba04c9ab562e0b -generate_keys d344358bbd78c559951fbc39973be66c7a6d40791a4d95cb3460c0451d32fe1b 055e32944b5b68efdaefb007031f1f6f635e104b0061ad06713e72e553f01f09 -generate_keys a97a6c4ca9ae0608a47a5a802c3ff3bd2fda7684fb466418053ce151b3d2f965 3be573497ca3f7ff6a9e74b2b071ca47a92875ea2e8684bef53ffee469e13e0c -generate_keys 1644a5a274893dfa8cb5b002ae81a0184bf8dbce27153e2a5b61c039d11fe4b8 ec5e13b096f906a39ae8191139521451220c55135b4e7137c541021bfb8d4f0d -generate_keys cda191117ee28798a4595101f36677f3012fb8a8d77c15dfcde5c5370e65f74a c5707d3b7c46ee96aea13572ce0098c147ed76561f8eaa785db3c751a38e5702 -generate_keys 8badb0f3c682bab82a98030e501858e99c4cd8882e9ba57b4ca4d6dd632f072b 8b72648cf1c935b59cbea1f88ccf9d2db4f51e9d3dd171ca584cdf5417f7ad0f -generate_keys f14528cc98f49f22f72215dc049201098cfb3fe8a10ec54f48e1e23abf5a406f 0a41beea9c5c52248ea46a167082e2a237a25a23dc839d98b0617f30a0cb7c04 -generate_keys bd5e2bf398062052a7b8fc499f5a80474ea70a8de306db3ce043db1032c7ee07 a146746c51e8299e8aff371967fcd33e6ac3b96cb5fb8ae02d42530cbec97d0f -generate_keys 7a9a52124939ac956aea137e859b63c2a66a94ae3ed3b9fb5eba69eb48b3e816 b2000748fbea1f6df428bb671ff12c9a2d6697b31c56ce27b680a6aa86da750b -generate_keys 69b79c451023546b4c20f49ec3d50cbdf98cab6423f4c106d2415a10fd0dd013 ed8d702fd96184aa4b5c3b6a2d683b50ece8c866564cc861ec37b1321565c806 -generate_keys 36faa952a937b438050d542521e4e4e1d6734504ba12f433930af83ff3acdf07 e90291292669866a5394a0a34cc64cd3d561b8355937b98e5021b2d96693fe0f -generate_keys eb7d36454b781100f69fe70297ef7ab1d8fc349b376ea545d0a430db1f5d839a 63e52602fa15ea6931454e621e757ec3395e13fd57d860cbafb34b90f9ba7201 -generate_keys c1a3027bb6ac32c304ad9a70290f681e63e80cb1578d15e619cfea843e5eb34e c6f8299781d6524d17499e5fa04b09c435576e9be69e47fae036c2c4b84dc703 -generate_keys 4b3ef10e56e4f5d55ee12458b3b67066027599cb48e63dea8abd27c550fc86c1 8de4f875aa66f27d5e53887e0053ea7558b918e16964a12d9c682799283b190a -generate_keys 21025c4b6e55fe86855db0046c50e651028d89a6b8f8347e13a42c5967818bc9 9fc82cb609e23e2308228c2b86d3ff277463e3a2009947ee3ddbf94e56ed2808 -generate_keys f7056db091ee3826916468d2f576eebbe465989a830abd1ff16493073522cfd1 ac107058df4686cf9edc7f76133505f0ca66365f71033cef509b5f7821c18f0d -generate_keys 995d3fd1eee8e212ee7c04c37a17a2b2e95c2853eab81c37dbcf5e74d789f0e1 b8fdf3463d0913f2446b1ec0b11ae8c7b91cc8495d73c286ec683151b1508305 -generate_keys 14b59433a23359931f2814013b1fff0006a969af807b036e53a946b7cc92f3de 04d6678e56290bd21092c10c5523a8c9b81b2b9352b8f4091b3867e3c4b3ff03 -generate_keys c938fcf664e7b6e93b22d8a3dad5ae412d4eaa6d019c3d713f1182ba8b3493a8 57fdb61f87b284029e6729976185fec161a29b42f5fc1c34bb0e39080de8f20b -generate_keys 4a206c012e437d2f4c13afd6aa1d360c4686d14016db0b0f0db4f8cfad4fad32 4b16f64756d5ea6820347e633d87b23ea39988d3e7cdf8236eab2afc969f7706 -generate_keys 36e9c71c91c9cf0d6afc0a1c20971f4838f5ea15448301a63654ce17d76fb094 2246bbe1f2ea6695c5e7016a581755993233d9e517638b0be739e60e84f49f09 -generate_keys 552e069a6c480746e90b3736cc77cd9d58bcabda73de2dcbc33d8586c08f4499 c9ec5681253da937faf512c9dae1c4f5d778903562afec6b2b6582fa4380d80c -generate_keys ea1117459d4d102ca8b2df1eba6d0033be916eb884e6966c150936d2b610fb28 4548a565e8971fc09572ea0001d0321ac5437884a91be5865b95dc9d3c5d4d08 -generate_keys ed83434a71e5967fc36526b046ce6fc38293f2a9cc85317e719b77843bc0fef4 14aafe8443a6d0416e3ab4ce89e6474cec8a9f6020ee80550f7c413ad144de00 -generate_keys 20be914a2473423da71c8ccdc265b529411fea9d85f4a3e3ef899ae0f929f227 441ad4c295361c7cbde452b8ffb1cee4122dcd97e68df0c1be2b38a5f324ff08 -generate_keys f6f529f85665c82c3d2dbce1282de6f7fd1a89a4f264e80584ed0ace499e3e26 8e82db8d8445dac40f2014e3975e75c0ac53d1235a77a0661d7c4e1cad79e70e -generate_keys ee3257492f5d109237123ed10f08f667854a1ed3de0b315bf48b635a96b9744a 04854edafd7ca4426ea961c9f32c616b2c61ab92ac694e816db38a4168c68a03 -generate_keys eefd4f221f76de3564732d9ff6ebf5ae9279e6242b6cce545d918bb56d564bc1 48c23c8277ca7a29ee0acf4fea00511dbf04243394f4d81f109c9b2c8c67050d -generate_keys fddc7e7f9ab851a026cbe382f51fd9aada78cf56691a7dd5401cce8c9a70450a 23fdef9a57c0624ab27d321783489905d15df666f8b64d75e0ba8f5baa3eaa04 -generate_keys e7b5cd9ac0af057ce8e2490942898c4502944b9beef616ec536ae19718c1f11a c952168c46a2619a533cac11c8218c303c8d0452cdb9bf4e159d4e9dc3f7b501 -generate_keys 4931c45856bead621f2116219db91309a55e662eebec0f34ed2a5db999b0f7a9 dff0ef8ae5eedd0356a3a8b31e75e68bccbb6ac87adb58194a967f05909e430b -generate_keys 4e50722ea5c4215126fb364deff4a2190503a08c9ca065a57724931e1ca60e2d 2c177c44e58b323202616c6fea98d8d5d87b31f374ff1dbc3c9a3eb002dbb10e -generate_keys 18152285674f8ede11892e0908430efed268e444fbb358a9b836b22ca82bd852 5fdb23624f8f746c56d887a8760e37815c8f92f09d303dcceff79956c2816b0c -generate_keys 69909d1c78df30ad0f318e734ca3121ff7dd54bdf70cf3ab2f900813d55317b5 748ae40ee3fd197973cc95b499e3f916ea557a811f62368349bfb43686a4ed05 -generate_keys 34e7f46a1c2fcb589525275098d1fe93b007bf9d81ff4c8eea2d17080d69b748 d7d490f2c1634d95baebf639be908c110831e29b4614a5d1e3479fca3d4b0a03 -generate_keys f3ad6130ab195d85ef12cf8aecd4c06a942a33887fa9b986cb9ec2d7adf0017b d866ca716f6bbd40a0a82dda2a4168e8c7d00291b70e135b4af801d676994708 -generate_keys 959dbc0e9828fe3ccfef2118f5b3d941e8eb0232190bcd4bd9700b19d5af6517 bd793a83bd13ad03d413e346079ac9e12ba4f1a00b1b1ca90d58844424e58e01 -generate_keys d294bb7f4bf95350a55562a34b6b1128dad2fc140c6cff0b68e6a172579051b9 0981e7b85633c526f93e7e7ee3c086d9f1d12ba0fac23f304e043d2975af2c09 -generate_keys 8a583f2bfe7d394f16a2010777aaf59df96117a79ddbfca87e0fb8c66eeda76d 2bbb038b6e986cb7d4c168da9ca96fc3569ab626fce670cf8a9ea204b251110f -generate_keys 5178f2b1d1e948d2ceb01494e6df580598eb3a2042d953e3220b82ec99619bda a91b76c31d69524466196390663730abd2265d4d3a73ed6052fe9ff799d5eb07 -generate_keys 2c32615363ac581fb6979d7cc4f8c26ec366258c1dcfa7510e5553687f6dada3 594ec5b03f573c5b2893e878c539c30bf0933b241e47897841f9d5ba4e015603 -generate_keys d5af6e427f29b00d8c995cc26889e9f6970d2345b8498b4b7202c97488fa33ca 10903196ed1e2e0fcf8719f1e73aca2e140e11d08c24d0925f87b7cfd9d22d00 -generate_keys 743f96e1be451423745ae71bd6d9105741fd456132ce2998835affab90439cf0 cfafa7247879fbaabc0da3866cba788928f3f2e51176f77184fb462ab9ac0504 -generate_keys 9eb9452708c448f661a050757c083a5726f89828daa95a59c92f07e1123d6658 3a52fd4b79b95b0f045022b49d0f941eaaa2ab6792e5fa6c44a4f4bf13310a00 -generate_keys ee2999e4347a3c9a5f08a6345f8c9623cf4bfeec6cf648d1dd47df5a8639b4ab 28c61b0c43e67df4233701598026230b1c9948b3c0ffb9d4a1686a49c51bf507 -generate_keys c49fa30f4b663f0bb096ad1a6b86d4a7817c7e2545818a738f5df440b1b31e93 8044d7c2036266903b9d3f3dc2787862feda94d53795b6b6029de9a5d769150e -generate_keys 55635b25520a88fadd6887d89d756a35660fcd7ba10e8a57b324308b8a68b7ab 6b0bfcabb5c8bce361fd34ad428c18d0986f7b5a6317633b2975c2fb48644607 -generate_keys f64c4595e9b90873efbb8a6bf2833ffbd06177953c7ee91ed826415cb42b49a5 4daf418c6d1232ffeab4e4f2e2e9940d29f867fd0d237a37f8d576eac4feff0b -generate_keys cb09dd31dbda881e86249c6b23481380d7261435893e774009f35f854c8582b7 1af00fef5c54a30331f980ffaa5d1081ef17bfa00cae3da03fee66b6d7784108 -generate_keys 2fc3faf2348bc0be89b0b4ca5ef1b8d9508ea00f74ef11d9fbf0a4346e543565 9e5aea34d3014e8515660e43f30ff62380fa61c57c098b032629a648019ccf0e -generate_keys ca0da87c8f8f9042646bbcd90515bca8ff4bf8e7ff8e7551e627f341674b9b7f d08dff2882bfba1e6efcdca9714dc2bc46e7eb74d314d2910b0e36b0e1e0fe0c -generate_keys e2a484c6e868a92b10660d9bc10fe0183a9b2969a96bea2ef41f7630c8c2504c ad6d2cddf36c5d18606b143c7d31fc30cf84244b2d2ae7733b2a136f1e6d5104 -generate_keys e64120db95f30c49cad33ca62a90b5c6d44e1e45d8d0c8b248682a3bb630e39b e559fc0fde0dd5d8292f52193e30ef421d990ec636b1232c0a347fcd8acfbe09 -generate_keys 239d0fbdde4339cdd8ddd4690cf190b6c3e3364c09b06c4517f354b0f38d03ba 99846e2ef2eb4bba7cd7b9a089c2eebe150bf0bee5c65b2133ed4a40031b4402 -generate_keys d02f998271d8546e77c848b47da7830ce9370ae38724008c6fb2fee35db0e23e 5e2e2883d0c6f0f2f58d7cbe761bc0db98b3afc42e33aaa1cf428e2020532f00 -generate_keys 91cba576e408beb078729fe927a5175a52364db21879b06faed82eb1771ecc8c b964a7f5a8c62e9582aba6795837636481edbb35cde26ab4c609c75f8ef2530d -generate_keys 07e97bac3444bc9ecedcf91323ab5246828e60bacc86e982c3048f7c992fb0e9 51a8f73daec11ac6c7db362965b74f84d009b581744f54bb317f3c76df496f0f -generate_keys 93e74a33b8a682473f6c8e115f4ad9f211c60ac9a0b393f1f82db970b16334b0 46e6955821a6eef75dcf16c527f6251abad801f42a12966bb55f802dbe45f40d -generate_keys d6172b356b65d072086cecec8a49bf65f847006b7fc4b9e704e4784893f21df2 cf01d30e3fd0094e70fb12c5e4176bd7338d5e95995105c7fcfa9293e923f900 -generate_keys 4067f06ec739d01268eddb3446f30a7661084080882583fb7f8bd7055314e216 8fa3699c7d2ed3cc563123b120c17d4085842a9dec2c92f5df37593cc096890d -generate_keys cb8a7d80530db110e62ddf14b28036ece54bd0f8fd159f1f5c78488d141c1426 e6cf08de874d7feae11091c2bbd09d22652ec18222bf253d0aece533c84f5904 -generate_keys 9d41d5a930b74663132acca08662919040c4279537f830d603a3fce00caf379c abb013d1a540ec0c9aae8cf71cacbbf171914f9a521812b6aee68e2c646f7f05 -generate_keys b7a01ced9294248dae433a60d31371675b69c728b524cbe21e1f5174dc8c0827 d1180c69e745d82c2fb355b896b16c89471a3654373a3191a404c09b56d27d01 -generate_keys 00893171807a440bf3d88c000ac334ab6aadcfbd15b96bdb714755d839babc65 1833b15521d048fdf0dcbb3189a5d71939415b60568c989e6d29d6ca74110b02 -generate_keys df4baaafb91e33b5a1b711894b2ab57ab1a9f7a43dae1bdff07e6b422a2ead77 d02efbb7de745dde153a2013c84c50031a353bb433fb58504aff1df12d589a01 -generate_keys 237b4cff929d0605a478e6e77eb0d0bcd3e629b1ad0baf970242aa470d0b8622 18f969585c571069734b260a844a0db3fd2040f9ff035bc8517d99dce0c0850d -generate_keys 1c1d37627db49c8ad06688e4022b6190131cc364541b106e73b5a092b2e48b21 1c99372c91fbc0878bae676ba67aa91a3d6d01f65b8d7e580f08191131b50007 -generate_keys 58f89fbf8ac0167c150ef07975907a75246cf7849916b44f188b77ce45224672 00441321e8597a75ca29aec7d167494819a5cb664752b20b078103aa2c5ddf04 -generate_keys 461d68a7ed05cb355c0eeb0caa6b08eb704442e9867f4a43f52478882822bcb2 07f82a58fd05e51233619e61eee4fd22605dad2d4d11321f897dc8d4a47ad205 -generate_keys a84c805abf8e8d409babf557fa5736a85e6fcdba7e4b0c096e229900d6601ca2 c4b53824e7b766b8c8cee6306378b5f197299519fd90a83dd33a44f08c9e6f0f -generate_keys d8c016db39ac6dad0cee4df023a17b33c9dcc3b3711ed03b2b800bf24d9ed69c 3d54f8b4aa7ca169a79d17bcbe2717747437759493dac98629bbe5959ea2d10d -generate_keys c145737eaafbfa8b4b6b355efcfc6cf4d8ffc1b517a20ca1612c8d66d19cd2c8 2d59259149fd406e250e6d874f1d58a6dcf216775e655043d6e867410ea7a406 -generate_keys 3038c320501792bcc625a27f425560b992883707e87166a238ed9d264b860b42 11bc4415a2df6755d9bea880fc5e6216a8f149cc21721fec82445f5acab8f90c -generate_keys 9b70a23c83df0ea2e795364b1c191e6b78a8542289f70cafc9d90c9a6bf5a870 9c2949bdedbf8173256b4376cfca362d6da9fc7fb643b1eb878fd6a13e618a01 -generate_keys d14b2c7ca8f14ade1359200078b9baf172e05f3a4efa6daf57efda40f96528bf a0b1abf1fb731cc939068e7dbf270527f87b96e75ea2277eba520104df699207 -generate_keys 3b94c8ca9a2104c2f6cb4daef2a5e98d26050860dd44c979aa75636f82784570 649c12de60a88a128797aad418e5ca1ba7bdc3f52bf8ed1a46901123fd7d9501 -generate_keys aa355cb61b468dbfbcd553e429083b88f4cc310ddafc8298dec2e05b3521a776 2e2899ddf55b53d6d169827444eec801aa15a8834287946f6e8fc8b81ccb940d -generate_keys e3c5306515831b0a33376814ae211875a9adda6bfa7e5b28271ae86bd45e03a0 c623c8d4cd58e0e8b19851eb8bb31496572390d06f6ad61919e3c03026450707 -generate_keys a041190dc37b9804ad425950972c349b65bda86d0fd0d0d5caa67e084e89c401 6b687313e709231973dbb9e44d963161f5680700de26f40a9283d3e9643e8a06 -generate_keys 213864143a9ec2b7ed0ef6869904fd49cfc4a9ed2bfeb6a45a3e939d73b43322 e62f4d052c86f8df37e86b9db3bbe8c7a1858bf3802fa2a414e3d56edda8d407 -generate_keys a3caf76927a272f0b763341e290eed2ca8db2fe6fc77c7e768bada164ae9a4b9 815829f66c3759e8bb5d860ac991fece60e7b96edbd9994e45be5b8d7e3a4902 -generate_keys d4aec2d112199b5f5db16dc8797d05264d9c3a48d47513886f70eaee7e7feffe 32fc3f2e90cd0f913e7f4e31f0ca3fd4571f1c4e1ea8fc77165a457ae8c97308 -generate_keys 4926826efbc05bcc68ab322b48170edc07583e00ab79ab03466cda4f6ec02046 779a54ccb0ef31dd476653e7e5437da031739587d9fdf9fcb2af7dd48024da0d -generate_keys d89973be10e8aecc06a286798116b01263025d0e9ad4f573b3a5507529a19657 144cc1a5e4f387c494d3540804b49b91b860abf2ecf57c8fb830aecd5299ad09 -generate_keys 62464f9d548025262b40d9df10215266fe3b1d0490afcc1926d3063ff843b10e 66518a2868c7f627f70ef134b019ab615b06df44068297754baebaf59afa6800 -generate_keys 858df7b352e95f1b0db5ccd024569558aec1eb6f5f12d4e3cc973195016f65ad 4746a90bfd6344b6882a02d7e49689a9b3a0d90208f71d77be9aa1fe5cb6cf02 -generate_keys bef7333c565fdc9f13e8548d4f4e9cb85966f7de206c7814596c69110d6cfc3c cf26f1e423565bb985a41ebce25310d1f7f3e4ed84cd09865614e3f51a7ee105 -generate_keys 34129d06a9d1aa0ec30509303ef9e6d5030dfcd06ec430c98ecb8c995ca38d7f 7e61c1aba8854e40b2ecba4f2094f3945191d86678bf7578f26d3b8329e4a70c -generate_keys 9861410e7c4412b11587f24b72b70b3a1c70895c3d334ad3589751ed7151c60a 204e866babe65d4be720429f8a3160dffa0147f1956d4ea51e9fc128fa88c309 -generate_keys 9457ad32f3c8d1751059a7f0bf8e3991eb90878278d0b2588270f73ca90fd40a 7ba56e436e23fd2cd8698b2b8f30285a65edf671cef1d681afcf068e0c02ff0e -generate_keys db27fdf381eff45085dfef00289f74c1593e48ae8a4b4bb3e4249d62717800cc ba5dcf2e7acf0e0ab4eb98799aa250a56678a0de3a2c43102b0b3915bf39d300 -generate_keys c803ad8fa94d52c7e1b80d1b70265d1749a7d01f092c9205a206cd6b22c16a0c 9f72fdf649d8e11ac24cead862c8fc4ccd4c7cac81372cc6a8d31d6d6bbdb000 -generate_keys fd9382f23aa8eb9a45443abeab9166c5cc5c82551e48c7de1896a24800481385 2ac53b477112edbd83c29e9ca44be41ff1961032b69764a49e544db1a9d52e03 -generate_keys bb6812e294abdaefb4a7c495f431ed70ce54f4be17a03bd23c3f96c7feeb8166 1f2b9064de1cea114e432b3f7d3006c0d8f152e6da806fe60bf07a3d1425320e -generate_keys 58f44247c5211adcc9aa1e39f30e824f86277fb3a7a3dd4579eb69a7604a8b2b 9c9bc189f58dd2c310bbfec3db09100e66e789ee1c9c1a135138eb84f8b1b004 -generate_keys aa3972dd13304c0667eaabad9226f1600801c86d6dd4bc866ec7b6bb02355d84 2001b668402bb0018b5ee68cc1409bac6ac81e11edf327593199b2721085520a -generate_keys fa8893419d85032ee942567007b6399b3ce84b66084d87ce7cf1928416a54ad3 c10d20ff85d6c1e488a55269524743efd8ef208b0c8a66dbbed68f00d9fa0304 -generate_keys 834d06a425eabb71f7a07a19b27cc1288a439d2b2ae80c331ab8901f73d699f9 07e8ac54911af50cc8d79159e7314d5e5a60198b624bbff3ed0a9ab81dc7340f -generate_keys 42fea3047a014b71b1db1cf90de03d0550edc5087ecd0f321173e6d3c0b9789b 275d6bf22051dfdd90adf4e5978822b21bf45f2bfaeb707df7f8b29590cd7a01 -generate_keys d7c3d2b4b000a5896db5b80f791b677ca7e013b774a091847f3d25b3604a595c cddc7efe7ba386a25b4c730ce271eaaf03807901a3eeb475bfb84811901da100 -generate_keys c21fc8887dd3800f66fa0662c8511f11d912b2ef377cd1e6a400a5dcf325c14e 2266944d63b2f17811df85cc86d5c7fae4d14741f603ebd51f2d11361c173707 -generate_keys 94ca1f7b82bb63d908aa16a4e53db8ef8285525d5e274770a3dd4ce4c03b4765 2333c008c69d5a4af2f1774ef3108a499139787c83cf00d300ba127d84f08c03 -generate_keys f9eb32f61c53a7b39563fd7a858cfbb297f3b0308a5fa239356d774cd9e82bc5 4faa9fa31886b73ece9835693d9f9c375aa7e85575e2a5a07e6666dce711d908 -generate_keys 14e52bea2fa51dc37701b79a35f22c23131b1a774f7a6e767ac8195cff4530d6 38a9d141eb34200d07b5214764ed3a60fae1227f4ee875dc219ad54f8942bc00 -generate_keys 65d8f19befde7d697573c0c5eff6aabb1ce7cb004914cfdf8590c309a35620be 73410fcaa0cad528e1a94ef0d99d8d40236600f17017d2d76ec5a96935616d0f -generate_keys b49e34f9e87fa71c56bd73b06bc09665bd134d72d232721a2c46028083933cd7 329a2159965a402dd1f6dc511888c7600852cdfddc4b6695c12a90eb1c4fa90d -generate_keys 97887034d804a94667381ccdcc77047fd0cb29aa03436c60474dc041ba3a36b2 8561307bf2316a7625c3f637474fb6db49334cad2ed5883eacbb276e74499604 -generate_keys 3660d40bb4c51aea6a99caa7d3c05a7f66c450f7916ea663871da92ea87de22a d10464a7703fbc848d0c000458688d3894ca34ebdf16442bab1fc7d728f78901 -generate_keys d23bc0b3f73c519a474db364fc6df409c95b8e84ae0f4af837c4a8b0459cafea aeccbff36ba75c638173d8ea6d0940960d247e7009655258ef1090fde0a58b09 -generate_keys a1558f7ea407d2607e08d2aab0ddb2a534c98f8427afe162a81de0f3a043a9c1 d6144c3da2c10417a8a4a6e0cb366401cc724164f0f4c0213a00cdad385b1201 -generate_keys 34cea74b7afb81070047c0c8105109d9029a9327f8bbbe5f0d47a297640a333e 8616285505cd2412e34d11cb4f5402ae941fc45f373328b80a1a513079d67709 -generate_keys 775db4dd4119cc34ccb138db6e31898e807a97ad5a326bdb19d8d0ca0d2351aa 6bdf77d16905c391fd6701ce2e9cbc7652cf06d12dd0cd4e53470d761f7cdd0e -generate_keys dccffd2450b593691498e7e3e6c9b91cd5c911fbb34c3f1ce47b8538f4b01fbf 5fa65fecd565a8254a79f282db46a8540cd08d1352deef9df1c8f02c3bf64607 -generate_keys cb9929280a93e5d75d9d762a8dace4bdd792092f9e308bc427f69948ad9db80e bd51d5fd98643be89ef5ade769abea280b54772053ceb130835c6c4e4fdb0506 -generate_keys 602d92e7bb6516f5bed53ddb7ff4c2a21458547d3caba2a661b2efee48c12ff5 1c89008502d9d12718c18a5e06d3f02b3592a07ee6a23b8b9ba4ce43c4b8340c -generate_keys f3682299996e71075e0e3765c6467fbfa49e15f5bdc5cd763814498210c6b692 1244fa03249becec2660893f47e4042d4ba5c82a1863697b7e92c1f308cf8300 -generate_keys 07666586b7bb1d64b722becaf5e04030fb4d810101d9d6576a607b813a394bb9 2e7d57cc5c5096fa26b19d7dff2724ce35c06b4567d9e90fec65b7ce9c9dcb02 -generate_keys 24f76b93880f2f9bfb8fb5fe02d1a8b556256a588b2961b89b46dcb234fa56d5 b2f7e888bb515cc70d121e4db3ae11899beedfb83441f942fe2277d478eada08 -generate_keys e66744afaede774de5cdaddf7da49597634047f14082a8e7ba31e6d1d598e5ec 3eb23129065cbf015a74157bdbf5e621b1b055d68e3d89be772a2c65080bf803 -generate_keys df4d49cd4229e758188152568192c8d125e9c4c3450dbc3c4942d7df9608a80d bd1353aa7f23e52b050218a54a81f1acb99fa9ca1a4bd129d453e00f93ee1c03 -generate_keys 980002d21a7b5e816b685b95bf4d0b1c0b6875e232c07ebf1ad5d9e50291e33b 400b6149223b950c0f9671a0f6e15f03b87f61a39469cb5e64d45b16be23670b -generate_keys 9deddbd4f93aec6dadd565140cc33b47b430c56af25d37d7e2268f3cdb5789d0 7ea6884b41b391c3b2d550276ed187a60564f660536c2935013ce84253f33c09 -generate_keys eeacde3b65c20923e76f498ec3d2f2d120de67917faeed1e12c160c9eabd4f29 a417f386c7cc9a5dbfabaa27ed475540aa2b5b90daeb2b1ec59a4394a96ac10c -generate_keys 9bef0d0ec65e02ab9e53031337a8c5c21cd1f47cf4bc36a21a55d4f13a70fd36 0d51f52d62745359c55467a78ac330802f4f99aff36d0c01d4682c7f1f21a605 -generate_keys b24d55f4f7fc55901ffdd602d62c9e3cbfb0b7929c86f143e5079c218aa384a1 c1e289f4f5adb5a28f8f551ee65b1e7f1627b82400354ed9d0a866d7485c4306 -generate_keys 6461a2eb23b011d6ae906337a9239c50d4bf5cc1fe84ce931db775b4822d0171 b0930d2bd1916646deb4c7bb65c525a29f265f019aa5a5806ec2be5f2c486b04 -generate_keys 59e814a9cfbfa6784809c00a0528ecc69601b2d3bb345dcf115d6857b9f16ba1 d06b14237267ffa9e2f55507a5f2289f4eb3fe89f398673bdfbf051003567e0e -generate_keys 846917dc56e941bc2fd8944c777aa4d81a584d9f8a16976d15177b5715921146 a0dcd8d70e6d6c0e1c6c2da146319effa1e00123f57c54189d98c13f4d8caf0f -generate_keys 03a6ac9ebc217587438a3d8f9e349f82c3ff5456e01b12943f3f801abc42a643 30d121ba4d0b499eeb559cecbca3c7dc459f591e102e16fe7ba14260c72cd301 -generate_keys 41c4520da5f22d7643ba42dd985678cecc6d488af1fb01ccfe628b63f64cdcd5 ad9b947f62cf725da1573202d1044f575f1d0a7c74a0b9ec2498e64caa942d0f -generate_keys 03544579ac7936a4cdb72d85ed84c74935d00bef1dac605ffab84965e1b15e43 1468a286066bec0763c36d956b250e683467ed947f4e29e13f44a8e226a3de0c -generate_keys 17c3cb24792e29407dbca3d97860a50107353a83daa5877db933e2acf8f44cac a1211c88f231dfee6d5e135e02bf96b35918fc06f997a4acc3e645fc443e4100 -generate_keys 16f7c5f8694f44efc66ca707eb44e1810914028b6443d49bb63c255ef1929456 e73a072feeebdbef77489fabab3cacb64381a1af51df1fc48a45075fb2054806 -generate_keys ca6800e3cafd9596d1beded9ac8632843c25c0c144a197ee2d83de9504980c2d 994a1b04fb5accf36cbb3afefffaac60e4af977b4142c6ad92763baa5259110e -generate_keys 88c6d5eda326cc429ace335e2dd2f8ccea7fb92b442fd3d3b9c8a43cf673b681 5b335c0a81252d2928af843c3d8f2755fe291860a88aa5ebf2d41a10e374f60a -generate_keys 50abd9aa3dd60b11cc138dd5b495b05168c1b456fc0db5703654d55a9560e650 e3a4edd54cac10a104c89be41ff498266c4ec5ca8a120e88b4c90e21af8db402 -generate_keys 999bcc50c873d67b80fbe108c34628e73e92a3f95c622b68b11efefcd2d05b5f 7498fb247a265cf0169e460654b695f27cb7f40fbf4e3f5fdd422b7943969302 -generate_keys c30b4b74672dfe1f6d8e8805b49577b7d31777774a419445e698cf14fd087392 cf5d27968f521f45b77b9c374e6ead4552a7c4a1aa2a957fde794ee27f01970d -generate_keys cc5b519994590d413d2c62175e15aec85c290e51ae7b5f87ac173ed9c0977706 1bb0b61e5d3b4a5194084505827ab4aad305e242e0763757f14fe63198d7bf0e -generate_keys 584cf994fdc23ee3f352f9317de13349d0401ed86a3184ef87b3b20ba9276c1c 2ef083e2e5223a8842f61e0e508da37749ed796047553c0968da27dc31b0a70a -generate_keys 50ad0cdc695f832b543eaa2a21b7a23d5987e0cd4829a0af58083877fc930e7c aff1407286adfc773864652bb2218c8081f6ef6e6fabfafaeba17f5bf98c5503 -generate_keys 36f23af9ea19f3d36371ddea14f439e5950cf1d429ed2be1cdbd1581852c9e34 e3ec8aa5320c82b51e1d254f1e08dc826fbe72a1afd32a8395548967f10daf03 -generate_keys af4bdddb4652c034a63afe38bfd9787346dfc617f47588b586955f8d00046380 c5d8ef2b11e4c55952ff5d9ffc95726baaa2be3b1d5034dcd0453f7adedaaa0d -generate_keys 93dcbd4eb0494f34222d2ae4263a5a64c1217ffb37705efbb673a3c73c61bc30 d098457de9bcdc0381d7c51770127b6fbed2081917aedec762498626df461c06 -generate_keys 1395c23089ebb52f18c1824523ccb05cee69d68684fc28b2a2f2d2179276ed27 5aaadf56381f650a5b8b3f9f793c9cbea913b52a1cc075d308dffc45ac8ccb0c -generate_keys 5e54333317feb3750a389f6f29f4ff13f61982595042f281fcb0d60300816b0f 676281ff816421ace7e8db6329a4b291fe296e347716a85707ed319b66d12501 -generate_keys 97562c45c747675d240103a8999bceb8426420035d881af55fa3278cdab5d7c4 4d4fd7e57286aa2d3cb66df5de8c1a61ef69179d0cb3f6b4dddc93ad8e460100 -generate_keys 6db001d7224d662d41435ea04c756e31428bf3472226b4f0f55228c13ee66773 9e39e281dc59750f252e51b68dadfaaad7c29b601e0a8c2579bbd66f88368f04 +generate_keys 14565db661b4d3c24df2607fa517e0176d15d1f6be4c469f8b1a6fee0fd46f73 6f407d2247d035a94dce1dc64077dee8767960630a94822c5ee44002c610e70e +generate_keys c2b496447b7c5a05499760171e36eca65959c6bddd8f51d5f165ad8451274700 cdb7cd68d4213a845c5a6cf909c055a889c558b18839c21a891705b6bfbd5d05 +generate_keys 31e48168da03e468e7794fbb3e1c026b53e2f62d9034e52ed7e1435ac8d7ef57 703f93f1f47b7491569f96367e1f18a321c849d7e083122caf903d28b3dc7904 +generate_keys 2461c0c453a1fe65dbd402b3596c709252df9724c880a70c17bc8e52dab1b033 80903c45294623587216584c54572a8ca42c2bc0c517bd5b642ec16751e13a0f +generate_keys 9d04bbb298ba555b7bda63543b090efea8b8f8e8340b626c0ee827d24db30ca5 4c81b43db42fc0c2899c5d0c1526a80d8b2a9cd71f44ee54a8f164a4bf415602 +generate_keys e6edc63dab6642cebcff549ae431dfd77f3ffc30895f1e9b8528faa2913d68e6 38f06cb2487ef54e7a385ea9d181b197f845df74d6e04dbb84921d3c76171d03 +generate_keys 0274b5a94858cf112f4040c18801f5c76e4155b65cca42536a1a3405d214143e eefc0e563f871a40af0d0ea65b9ed0bf9165c5b5d0160aa24c392a726033330b +generate_keys b8e1f158c343ccecc5d69fccf08ccf253c0c7853da7301244158e623b1b9f772 ba87a828f091942d5dddcfa55d7304949c38f88d7946eee9f3ca0baa4134c900 +generate_keys be8d33e8f719b82ada1008eb06e1892430837d42109d8f6a844298f5d902f0bc 3162aa68f6b00a240c83db0e31836700f114ce1a708b27ba7ab0294b86067d08 +generate_keys f1afe8a8ee33c6c625409845994d4abca92a084f5d194bf72263befd8aa97934 85d65dd72ec221c4a84ab1279aed20851ad67ba56fd6549beb7b76a2510dcf0f +generate_keys 69ed1dba489a2f89b9a22a39f8bad4c754f3068060d57d4989f1158d13bfbb61 a00291e0088073f8c38dfb2c3b0391f8eadd11f6350a7c34a5e5874337018803 +generate_keys 3f0cc9e68a6e8d28c84c9c76e96b6d6904b39936c4e8ff186c5749e3515401da 3708635a8382e486f261e205c92289884607fd48e4052cb770709e934bc1e70c +generate_keys 555b6d073a01a8a8f535b5e26e09a1ad4eb2d067ac4339e9edf6bd4de4a9c86c ea06ab7d1c48e08956b329db410d12e5e4270d93605ba7a6e788ce4da6e8e008 +generate_keys de79233ec2014041d23d35abe417a40ae14d0370aea8723a4251aee880d903b3 d02a6abe341f0e938e99c327f4453c7f48eec91a137c1f3017aaf031c39a110c +generate_keys 0781873199b6705f1df6e3097d0d0f8245a57b7eaf9dda194646d35ae6048abc d956009fc68a13df4ddc73e677e21d7e2e9e9e94d1bed67e5497dc1ccc509d03 +generate_keys 87dda05d85a76f76814ab7c069875892dfafb68649e16a5706ba57a4eae30efb 7aab409a2e659021e7f6add89dbbfac1306581c9af489d1955af9ca9e6dca004 +generate_keys f78d0ba130522ee4b2e53c0ee547a4923fc2666dbb2567fef4cdcfc5c427deed ecd3d3bf8c5078362014aebfabddf2287f961a59e2c30404542c383a1f9dbf05 +generate_keys b8626776e5277e182f432f9bb1735ecbed074bea3e842ffbe736c7fec5a9cc8a 0d74c9368c5342e4fd27d17b1048bc8b31bc45864699871fc54884a1da442604 +generate_keys 9189676a8a29486b7b910cb5114c6fbfc2c3693fec52bb3fa07acbc8bada4aea 247061ef4faaffcbfc89fe16f4f4285177ac4a0adcd26b46e10ff87a1f6b5b01 +generate_keys 1837653add3f972d755fc9f78274e7f77d6c4ca59fdf468ab58c34697971405c 686716d5aa4feda8ca873a53311ea48e2b4a378dce95ed72ce050f0545d93305 +generate_keys 78437f4e3619da94f50d541f3fdc24087fd4a9bea64101f4fa00c242234b43ec 4ee58dcaf14a4ace28c69e5ed88c67c69330a0d0b7a15337003dc44fc2f4db0c +generate_keys 6176bbd85b15f6d2cef74376d248250b482f4649553a01f9db323c222a41580b 5536ed1a93335a0dba2aed5315c7b51207f0b78994d2bc5d17d974e9e7904c01 +generate_keys bca1355ca382dc99c1c3dc0cf412e8f2efbcc23e0cc110afc764dc4b5e36e9c2 9c8072a9583c9778cc7dd9e9d3ce1a2c22c18af5e87d14801279568a1acf0902 +generate_keys c974c6be8d21d7760719f33b3f3d5c34dff1dc1551f96196978e82575dffe926 fbcf6d11000fab789e43e5c454f286dadb35fa652481e845516a157285b9790b +generate_keys d5eb21313f44e18633c09408837b3c62bae2611b871f174cb12b52e67176d67e c70e1ca6166c79f10752fa9fa63e2579cc5793aa404e0ddb0d3df1eab6f9620f +generate_keys 0c56ba7c0dfc8ed5756264abe3be304e74a4a82b730e022a2c1a2d629b09b899 9b6274bc30cc831f2a1e18a052cc76740f649fe4e62773d74b2fa6dd60b19908 +generate_keys 00b60090340132a9d7221b1411132cfc6b422162222adc483ce5c258a79a5ddf e18bd122aca283f61e918dfc8ed29ff060e9960b82c582a34af5a48c3b3ab00b +generate_keys 6fbb96abd1cb2f3b6bf260976e5eb2d7209333e2ea318b70fe8ef56f2cca563b 2f74e7d39af793285828ccc19ce2bf338703c333fbe5b1d49e8122c3a62ff005 +generate_keys a5b247cd1fb124b8f1f77e4d4807c1f6723f4da7c67812d18dabfaf02c017308 0820bec2d83b67d2332abb0e533db77b082113cea2eae2d260060f5fe5837e09 +generate_keys e1cf8f19634593162945959cefe550a4fc91e1602a53c071d2b55de955640c4d b0a368943f1a865fc540e031ea3763dc1d99a4a21efe5661f3edc03ab077b40e +generate_keys 884f9e311366b6b2d81a5c9be2cda1ddc18b6bfcc9cb5cfdd677ef7445f07eba d5795faf9005f0964e9296d3ed858b29f59766cbae3664490740d70e7ac23400 +generate_keys c87c66c3f8e4a57fa40544e5c9332f07610e63ed3c80238eea393e4924bbe025 192ff4e0907fccf6235ec5b25c8e60f2a9153aef9bb63df537f9f54362e8a709 +generate_keys d0fe4378a788e81ac55fd96a6fa774c297d79a0279afa2f8674c6da49ec5303e 9b8b7b1367406cc77bf50945db65e907bd462910cf815e935de5b53893312100 +generate_keys a1fabfcbebac1ec4a594f8c9993ea3a1385c666e7247da31109d2d66ebebe0d1 ba34cbb315dacae5fac84c1d2df21f368ec6677185b8d8930b4c60eb2b1ac20b +generate_keys d24928eac169ceca0cf161e1ac1f36864597480097eb52e5f82e045af4ec7408 fd3f538b82bd71fae047364a1dba8b5e0a88a59face7a4121288e7114dc09204 +generate_keys eab64f3a10bec084a573beb9e2b9912a094965aa3e577a4214cc6d25f3928873 a4f2957efc92e892e15a942bc921227db3b3e16d9db7d21f26fef2ac5d3f5b06 +generate_keys 9ff9ed77cbd7de1569f9f4ddd496acd9105bd459a9bf6f1008d685bcdde4e057 93e80be543335845d0c17b3ee5c02a2fb138f05bbf14c7f33d57b9a3d6c93905 +generate_keys c95e2086d5100e0ac0ed9465ccc2e0f21a396a13face22dc8a97f98624104698 983032881eb2933e6bb34d954cca1aed7271877873a0948b5f6d5312d33a600d +generate_keys f95d49d5de56ed1b7d347bfc60e3f5e56604eabf44ec8ac30fde2f1768060b24 647815255ca57db807e5d1678b16c6da7fc9b4c0ad2bd6bb18322fb63cd5c203 +generate_keys 50c1097ed21242fe649173a23af2994a9cbf34785494d9e64c46f7cec6ce125b 2d0df8272708d5c02a19f16371c2e3c10945cd2c1ab568307664a2b5ba80850a +generate_keys a842686decb6e90d2b8b29540836aa5f16036c15fca345da34dfc84c6058ca86 240c886a112328271b94dd9c81d1ee718360db2e03aa850a720c5a31cdfac70b +generate_keys 0bfe7208e259b6db60974adaab6e61e4b3cc6a681a5993284dd09765eb4c2598 f978891279bf903a9ebc738f6697f539e39e3183f0b5404c5d933d686c7d4d02 +generate_keys fb0fdc654666291326d463921010c1861965a18c4d565a0936967886f2e8805c 2c8d81b83aca13c7b99e360f33547a69945415410c8dd81f4c416378f1c22f0d +generate_keys f768a0c2c45d97d7b5bf1b3ad5320e9f812e919477bf7bee0fa206da94f216de 88e17fcc36a2db7a775327cbcc6493ceaf577965af4fb140c3443b8d34d56808 +generate_keys 5180d771320c248d74f88d593e3ef0c1f969cef65647c8b263272e7a5a3c274e 8e5c15a98096332cbc386bfeb5500cb3091c5fb718a961d45c464d25a840d309 +generate_keys ceaa9e8d71186e185cd78ba3d08fb0c725158559d905424085e89c497551c3a7 b2772caec28abb7c1765122406aed37c5af2770b3beacd0659e85fba4a7c3808 +generate_keys 83143a0b0644795b87cbf80639157820a2ec16255ebb194a4de18e5654620496 b0d0e616f56ddec6b2c6fe81cdffec192e4d28095b15532a91e9c4fee0041309 +generate_keys f2a1d60c8803249578bd412679ecc18fc1ac0806a5bcde3cb604fd91e325141f b26491a12a0f78595008c66212a7197667d7eb3175cabf3b2f75c99ff9dc1008 +generate_keys 3c5a0f04067b957c1ba576b24c48b687c9e623fcf28b2a4360d355df7844824b 72e12112fd84802b87befba233738b9e823105a766cdcc1883ca752fd5d8540b +generate_keys 49d4df98236840725cbb69cb5995d13d4ac23ae6e1ca5150ac53b738d726353f e10f812048d6b0289302c1ab7056d8678b2e6dd54fd24382ed725810daa1880c +generate_keys ebc5aa6b904c74d8ac649223123a7dca7aebd8d57e6e0afa2d1c13cee66adccc c4db7787a03ff44705f807f3ec38dd135eb3e58dd81f3793ded0e896d6d8d507 +generate_keys 2f5ec477148a3e488bb5bdcbcf9137b499f1062ec8870edfd24b2329b505651a fa532736e12238f85c1579d6c5bf9493550f22d8b51378efdb387020404b600c +generate_keys 29b9d350f5ad27a832d4967f3e64e9542428472245432acea7eba11fa8142f39 301389e749c76076f9aaabfe01ad5e4f163b94847504b014ca93a8e1a8363c02 +generate_keys b4c4b6106edf4c3cb20ad33bb0447b3d6393c4e0f44b74d3fefbe5bc6f46999b 2bc2fe18c6ed60c90048827f3f453796474d9ae66e674d0c7fca683d1ad7790e +generate_keys fc4c1d2c1e8ebe78b301cb545a0d2099466b5f5d6793e2fc9bea6b94b2952a38 68c95084d9d2d209c6357cc7d75ab295db8abec5959476be8b3e510f70abfd06 +generate_keys 84e8a4694e36749e1f95b169ab0c1873b81161ddc9bcf6f3db1df45a60973c88 da963600cb3925f146650425dde10cd0cedb8d5fb31b4125dbb780a151bd750c +generate_keys 4c0255d6d504c882ba9c70bfa008fce37b64bf6c3f0e28766516a4420ae81e26 6657308e8186f157d0cf3a904204a5f45571bcf2ec9cd447918edd483b57fa0c +generate_keys 73cff6accb2477274d9c7be7ef9acd6af4601c451273846dcdc18009e0ad2438 9c0f09a10b8a612e567d1b7535a9c01264550d36b08a0e3f23f540de4e94a607 +generate_keys 50a99c17fd6ec8c9a7e2adda758e472f3bf333ee38e8679fb650dc61823a8cb3 06def25052d92ded8abfb5797daba02b61eb3d30df2542e1147906f170fc8e00 +generate_keys 7fb9833ef15b807d0a8ed849ac64ac3d57fd0622608e4726c9da0dc1427e1b9e 3526b4818e2efe4dacaa9cdbc6aaaa2f6f4ca8856e1f24565fa7cf20570f240f +generate_keys 7003d681d1bdae4e9c0463f97f04d103e013546190453af9e55a1ed5dda89be0 d3f12639669bd1443c08b44afe51a31778ad669daad197713e74cc8b2ca6f80a +generate_keys 73e51f73eeb26db5c40964a43b3d5734dfac4088a0a67372d45e1413ee496128 6749a688d7b1d226273aa386614087968d89196b96530450779cc730074bef07 +generate_keys c33c50bf96c51822d3283b681c5da8555734bb7034238119b7312d8cff92df72 03204fd35245b0c31ffd961380d3451d4108d311c23d2f565662530474eec30e +generate_keys 28ca6b41bad4cbda2d9a888be4b92cd7d2e34185b916a697543dd00aa507909a d5c05f1ad0ac49b7a8deed811f88e890235d7c839921d47a8b87cc966aa7410c +generate_keys 1383c5d7280f819ad09212e5eb6b1864691ba452bfac32fb86fc77897b242102 8e9ac6bed0dec5d219fbd7180c6ae5fdbca0c135be75c98ba735bf97021e5602 +generate_keys 0c90ad3ba3987640f1eb2614b6705646a494bcb190252e8c1f44ee0275fad9ca b66884cc0897e8571947ad17de9b4d2ddf9dc0c7d9d16748e190066cd0cc1104 +generate_keys d86b5c3bdd8d78db41929d6a4f419cc7d846f3bf237229933d427a1f1f4c4cf6 d83a4ee80a265b9524389ac158a6b808a085fe6e5d9261565cf049733006b507 +generate_keys 18ba28345eac1e82c525cd69258c187dd3a5a399c18fbf6a621187bcb087925f adc65d48849be14f3c8163c366a502bd36e903b5f1a6d7e2727a99afa453bd06 +generate_keys 722a62e3d08b6c0a2c474117a16e4b98d83a096fa9843e872252098fb803ef15 ba131d307b38017615eef7c8a08ae10bc9c0a72195672a25ad1b159077adb403 +generate_keys 760829dbd876ec7473a436cee0fe4662c1fc00dc20544e4ab330e36c17edee78 dd880775c7d6a3620e4275f732b4890cb89bb1137153244c8b257888618c9a0e +generate_keys 74f7047c90f4d0ce0d08905d65c420b273a7ba9c90863afad305084527f06636 be86e0a949f1fa725e641a5270609f7708fe2dbf74452ba0de603829bc195208 +generate_keys 8b740a5ce546809bf0c0d652eef203f66b5bb57bca0381f61cd517718f63cc2f 9a5d8700d889a924b859e6d39d2aa098ffd3a715a656c4224d3d7e5d7d530c09 +generate_keys 28824bb2320d1e713a04020b92328efdb7c1714e04d1660afdcd3f2214da73e3 49e3f3f777049653a6164439463dfce50d636dd67ba3322579bdb7668e13a40f +generate_keys 4d445f85e4c68be38ca88b7c4291de269d6c5d3d15e72cb8ddfeecb0650903f9 aef7f632c273a32955f3de834af596f4e8ea72e1bb8228ee7868c96a4b5f200b +generate_keys 87972505181dcc38cd00d2c8111b85fd2ebb6cb42bd57f8ee09640b8f86e471b 3fef041d29057cb02f8fccaefdde93a56b363011c1ca940efdc1c61f963b8205 +generate_keys 3e6145a87eb29986b0dbbe2fff6eb574ded2e350a2509335e50eee1c84ea5196 7c40a9e69249bd7c63927e60e9df41dabd01ac33783ab09f55c5465af1f88d07 +generate_keys 5e87c0a69dba008520ea5b1c207578ab2c008a967f384e0656a9e5c9e36a198d 816fc4d269e6ece1f186249e52cc95a32d5637cda6e3ac7cb9d15e174f66970f +generate_keys 5d797e41fdea56fc5e08943550e50755efa01a539571acdbbeae49b0013df73b cb0bb4d009f445565555da32b92bf1b620db6071325366e96a8b8a227fe18f07 +generate_keys 19114616854e1eed43c727820ace27d9703953ed918de4b79db80ab9e15d0de0 d3d4c5a9b3054fc1efa5746abb1931421214c59f63b1e1cde3da0584e9df3002 +generate_keys 81d11a53a95c037f3f4c9e188a51452a44514adcfe619deaba3ffe8e0220296a 040c1d675ad053475544d5be5bd5eff25102d44030a8c89fe0770e7a094fcb0d +generate_keys bed6d6c4b90e9eeabdd9b3f7e618b5ce5da61d21a47002d30fee79546e0b5003 f12c881deb67033337e91724089ef4b5bb4084353004bc3d7ec9e8dba1200203 +generate_keys 3312d7943f133afb0cb9c4576f7cc144753de1b24ade4d1b08f47fa0d9a788ac a560f2435ecebfae53b14082635c5fc4ff77b6e12a9bd19d7c1cf698bc2af306 +generate_keys dba9e17971f179be3839ba791fa532490a6672a6d725068ac9663078851667bb 110f4273f19ffa0892250fc31cd88cbed36355d2dc56689c4ae6fe1b7a13990a +generate_keys d7b6ee0095beca7a80673c3406d6c6669369b1fd72ca4706590494546746c701 95905caff73e2b3c790a6d0fd85a2794e3a9ab009c760dea7f62d189cb314101 +generate_keys daffc8a26773421dc7626d91d3fea6f7cb49821819a143420fbc924f3d069078 46302578dda3edc66fdc7afd3351ea663d1a060ec1e7070c8d43d19b282b9c04 +generate_keys 909bce428ae8cddd6bab77fbe07bbc9e82f5aa76eadca6d293f5852cfbb58a8e 567382e2c6f7c44cb69541d6a62ef86c12bf39e8a24894d4ed07cb8a48f8a408 +generate_keys c6cefbf2fa12a80939aefac41b3b76eec352aede0e556204aa98e9c346ee4513 e4d792af177fb656c0b136bb2d95013ee4f10a18b4123051ce60ba27f7513b01 +generate_keys 35127595fdd75057220c8cdcd8736fb5f78dd49ca053696b448c1156b3b0715f f81e403202da7f1e7c38c17eeaa9d064daff09d6335689be23f17d8f63c03400 +generate_keys 010838eab570d42412e68da8937f916549864f68dda71e238fd5cf9e8c779118 eb5d47a10d726f4081229defaee8d17da9a0a0acabc888b3d15b25900f620c0a +generate_keys cc980ea05adeb70bf1f8f80fd2aca0aab2d20787c272dcfe8768ba02d209a1f5 e16f838c39680b085508e2f4484afffcc4501cf17771de1f4e8f3d776f456309 +generate_keys 47bdc6f2dc630955cf7b4ddbbc13498b704aa1a55124c5632ae993b5d2d608a4 d68517c279418039aa65e7f0e9725186bf68aa237995477dd9bbff8198e9c00f +generate_keys 76d6d68b922a87c0db2734f2143ba25556056a66a7e7353ac734554660584b92 c675b54d023d6b7db0b0eecaa575d20473118d26fb2a22eafdb34a7523dba50b +generate_keys 39ba5391b0ff5320950406ce07d88e63eed0fc5e457566eafd735438aa0c4763 8937cd4762abedcaff44fd1a9f4d76dfebdff979c4587b2f4634e559e034d70c +generate_keys 02171a639079d57ecdf6d744bf089d3c7465e69f57603a3935a58345918c3d3a 92fa05e212d1b7057eb90d65bd2ceeb0c7fbc038d391ce1137604d9ec908630b +generate_keys 4760d333319da5f9b83d76aad3fd934bfe7066d8dadab26bc752775a5567628f 4cbfff9bdd253172badb89ac8a8430e0d1f8e6869f05a1a7dd73a1e4352a1708 +generate_keys 7ce83069c7ecacf944dc818a3679bb8580f2fb0016150636e1a7b51848056e00 736495ac6065995dbf1c9574f477431b912d412e6d54d8bdd3e9becd826a0e0d +generate_keys 729f7bb3f7d4e9d626929655fc0616e53abdd9d8131137c2582473b749e1e0c6 125d58bfed4f4b39cf9ffe3827e837babcaef948409cb9b499deed35bfab8a00 +generate_keys c8d4adb5355a141bb00600579fa12523397ea283cda27dff8800d34285612859 b4cadf8f6607c0c467dd7795008fbf4311658b3fbcb2b3c26d970d454174df06 +generate_keys 078c902693d54e9c6fbc14aacfa9f324415402f4533fb07345b698755dd3b8e4 248f95d8f173aadc378c161f70d324efdd3439907acbedecedc5d025e2d91e09 +generate_keys b0ea07474c878e538f7cfe261099db55cba704bcbc5a0fa59ec3a2eba068bf04 a8d7990e8189631f82692180369e2893bc02a9a4dbf15fbaf616636db068c80a +generate_keys b56953cd97fab52e8a7b5715776f26a25722dc05d474801f5f51bb309d7729b2 b8db3b576fc280c644de282d1afe3dff6a1c792064bd95ed6b65678dc0877d06 +generate_keys e0e791293fcefc5c6276347bf8cc3231a52119e66f36f2e4d750158a1f4ce7c8 d3a5f8cab604960fca614fa407a0b3d15354bb9770939d399acb96353365ac08 +generate_keys 0d5774f9fb3346f4719d4c3b38760d1f3f700e3d74cac45f96d8b5cfcf014c13 74c691b430c940b73e655dac205f21da6d386c62daf0b1886343935205aecc06 +generate_keys 9266040e981db66adf394e8511c340d8829b1479bd2a962746c77c7e33386e09 b507358db19bd1376e5f008f054da0446dc1680023b9d0ccfeb25fd6216d7a0e +generate_keys ec3a92b30853c6d4df45820c1e6ea89b9bb80d28190d3ad48ce78ff76daf5276 12468d27d8c08ae34648db65b4eb96dd650f537a274153a878142df5e8edb505 +generate_keys 45cb27ca1ea5e652f629c3d5ce3b8aaf483a06f19e955c2f014d7eeb42968382 fb33a050510a30be8470d263e1c33b2452a7ce4986dff576909da274bf26b10e +generate_keys 9142ed5195c9fedd5cea3cd64ae0371ddc38c58ffe9bc875c9616e8534025ef5 003ce4d4cf7d1e2e298f8ca4f1c60eccc4c1596574036f2db6593c9d9969f108 +generate_keys 4aa5f795f73e02e3a1cbdefb215d05bb983bb47b85e6a88e4eee72b39a94ca72 b9ea6a8e154fe071e317767ce2f0f02b3eff74adaae8bc47e02499f85f3c090e +generate_keys 9e2fdb1628e14391db99550ef8b5277c5b55ff8fff60a1b5faaa47992634533e 0ce4cc391099be0d39a8be29fb10a279a6b65dc4c923a070a4eb8cd530430c0f +generate_keys 0c52370043fa3a4d3f40e0f95f9b7ad0da5f5ad4104f49736847e6971b09082f ad9454272692de734432ee021133676c434d12ba5dbe87c55a0994fd8781a90d +generate_keys ec87c62cd426718595d1ebbc43370b0409121e8c9d0ba5ceda8119a505c3ffe6 0993d873d5e694427bad3108b48a9182f111301a7daa8af2860588fbf4e6a802 +generate_keys 1dd166362a3bde3c56b961ed3fa7694db8325f81414893c903e435a2a44bfa4e fe83a5b475519f6d0ec35fde10e70f753b76e1070a7538196e7ebd6a4d5d0506 +generate_keys 0d6bfbe8e454c19a693312f55a87dd720a94e0bc00f09aad6292ebd6b5ee371b 286313c582243c4cf636c02a8cda434ebd86eb3eb8d532ddbdcd498a6850ff07 +generate_keys ad6d7326d9cdc0337b953f00151c6d4533c026fa0a6eaef57f4065b6beb3eb11 38d2f407ffa6c9c158f6f8342aabe612cab54e276c7e66e00bbad6fd6a5b2f05 +generate_keys 530824d34e0da18eba2230acaad119317f213091c1a428bb1c8308dedd00068f 7a7eb057281f3fdc69f5c80da07ddf15e6dfcdf60e4f6eb7af7bd4bb2b2f8509 +generate_keys e67712f62552111211414e0a080b51fde0d521507d1cac94a472d766beb08192 78a91e5961b5d7ba0bd334abee6f670fef5683fe0b3ddb3b4871f2c8bdd5d30c +generate_keys e8de3c189f345c2616c48bd8969ea74d3f6471e2a30c33ba90b943c52527466c 2aced09170f0086118094c99bfb45f36e464e4916c4d64b03026f8e7e6b4a80b +generate_keys 1dbb4cc7afea96bfe782b747a6d86a9d944fe2542bf12bf9de7f5bd7e8913547 e98890f85285b4bbf02434eab047dcabb7547b9a9e395b13fdf5a01b7f9ba808 +generate_keys 172f3d7f2c2aaf1cc43679981cd1bade2af95d8aa0cfd56a36ba9b6e778ceae8 93c72a1f7ca32272bb41120f1ef5dac3ff656a85b3e5b2563cf5a3749008f50b +generate_keys 627dbf760b3159778aa35e1ff1a7b580aca57c77d3b1374dd8773f3b99f34cd0 7c6d58e466f728b78df5bb53eaa8d1c360ca486e6c5d20e869a1703e1c4b2704 +generate_keys 00218cbcbe7e647bc23c95c5e1db95adf0b796d749896da45635124377345b7b 51f6fe7bb3a93c1805cacf222271fb59e7abe48ea122d1e7db39c22d0e693f08 +generate_keys 4737fe0445416507aa37539c160ae2ed536c520c523a19369bb4f4fee5b85b85 11532fa7590844d1a9d7a49c29d351a42c14bf7c85c0def65637de146cecc000 +generate_keys b44afbc96003f1776485ee8e094bc1d9715efee50f54fad4e4fa701c2c7f61b0 1e43234502a6569ea8b119704eefbcd8196aad4fd2323bc7a1d0574f6cd1ea0c +generate_keys e0ec2b77f62521847552cda4af0e39fd035c29353a9e10154633db3586d8ab12 be0ab5769976d3098ed038d76aa711affc45a13f084261d4a004fbbbce4cf601 +generate_keys 5d1604f328a710529c4bbd1d5b6cd5c48c35f58931626b2e2949160ad8ba0292 81ffbeb67640c41b8951fb100b0142ae527f590960ac73d2f87be37786580601 +generate_keys b2ee25f645ec6d90f87d0e95e0d773a4e4f8d4a76cd738e56a755959c77c15ea 42a197c92924529a6bd931dc0beb70825849fdbed4686c42dc8dea2a6bccca07 +generate_keys 3824658833e4745d6346896cdaa4c30dc34085d2d061fd628793c88ba426e0fd 15ffb928c14ea08179945b58960ab4175247abbbedf2a773331fbc4f648b6f0b +generate_keys 9b3bc70e19a5cc6f080ef0a1656bd047962d32f747fb1555d668a166d037abd8 9b00aa7e6bcabdf9e9231daab532a39424581e3cdbba27cbb74f19fa21e11407 +generate_keys 8e5f4f6acddbb9a7757a6d4b563e4f4bc34b15f1d21e35908c8409e2d83e90a5 21a9b6ed697ac4fe8fdbe4d3c3e45932af5c3f74dd821e5c4ead1ca1a87d5d04 +generate_keys 23b6bace2a23ceaaec200de9ecaa2b8d6fa6a3a4cb498f0623f9c1aa9db2ffcd d28ea86cda2b11ac2f7736c4828638f95670f2da9347f1ba38abcf09b54e8605 +generate_keys 63ac8b1add50e14ee6dddac6cc58ae7c9ae861aa5a40153a0fd4be149a30d767 7a0125ed6b232e5573d23249b29d2965759b59f4590460630a2e34aecbfacc0f +generate_keys ec02bb8e14c221ba8e18ff3b4fc86cdc5de3e69d95b4abf7ad7b1eed466c47a2 9565bd3d1f082c4dcd3a0120020a45083fe78b1951fe7b770d13b5059e9a2f08 +generate_keys 691b7eaf29f2237545ca1793c8f83bfbdd7963371940270ad2e4817f30705cc4 9cd5332353599f15c4863c77a2e6032ebb5ef90203c1c8e8f413f0fe6b3a1e0b +generate_keys 14031d81a5ef0c0c5ad3fffa06fd8fb7e233f27f08369792976b64bcb757f3d6 d8645305ea4c40b18de06701dc81e0a2058819b23b8b9d7634cad88160d98b09 +generate_keys 0b10c6e149a511cc168a439e371c58b771586b52f940e18d6b17bbe3164278f2 710135ad3d4a2fcb9267dca707cd84aef1fde7f93188051bc150db64e6f13106 +generate_keys 243b043b12434a1a2554e727bbc144304b5774db2ec9e533a58be37fc100bece 1ee9b74e3ace50669606ca3e0a7f7dc61631c40330f41cd565ed9e3c1d38630d +generate_keys 8b72d867a5aec3f4c7ed8d04e79f5828ec106408097b4e1e7a732cbf5265385b df3492cb84a732219f3190e9f75423a5fdcac96d41405d03513ef63027e1df0d +generate_keys 453f186f6935f12d9649f8f5bb7c19d2e9c847dec698953e7a9431b4be4963cf 111778b1daec754ac318bb939de303fbca562f1a3fabd5cd5ad7fbc8fcd7860c +generate_keys 791e1018cee481be7b3cf1c836d28429ebdd3016a72c42ab3ab5d9ad624a6f01 b54264fe2eaed1a4a32fcbfd708c55a029f4effebd7ef1c1590c5639be31b60e +generate_keys 1a9065d4d497df66f213fea5f4f62e211b11bcd84594dd7ce000581f6ecc0323 99cc3ee865f38d74a21ad63617bb8fbc0f319f23f3fa6593b1cc95d0cc026e06 +generate_keys fa23e398cbe38e6f6ef2b4cb1ba5a42b9255aa4976239144dd1ce180de9cf082 cbc8123ce649304a6bf87f4f464d7028aa33f23a84a4841d791d7a641ff2640a +generate_keys c18dc595d0981c71a0f168e47b49b7152ee778352ec0ee94e2451ee713b2f1bf 46fce9e5609e3807610c23e6c6e95614a82d16b02afb204d7130824bdf8e830c +generate_keys 6709af179adb2a6c359479a877da5a6e3f5bc94e3b755100c1569b5a8eea6e9a 137d33dbb185b548398080d48b8afbdc622e1b0d47ec3be21ab8a20adf7f7d04 +generate_keys bea22555f10aa5cebdec8b92f8c3ed331608cf73a125b298e84a194986addcdd 335c0200503d0ffc005f619e486f305e90d3e40e9e1ef0e43517e68c68fd1108 +generate_keys 58b93de2026d2c5b5e9a0f50e7617a93a1d10c7f49a8858e7a4ad98f9d7fd2d4 eeefd0f3dfbb242c8562d0b0199a80a8d0137ad8f7a7fb716fb2214ea0f0240c +generate_keys d0934b797e9aab5feb6bd564bcf54c166b71c8d474c9c48d21370ad59eadfa6d 2441e5c4da2645a16f17e278cb8fe76b45750bb1936c3c3459ca225b4b800c0f +generate_keys 497c1259c6f4b963ee6d66beaadd17ec250f2b3b0e6ef10839d6a5fa473f14ee accbd43a23e87f077a602923927da71a94ff1a2dcb61d67eb6c1dc03832e360e +generate_keys 685dd1514e5b3201083086bb3a6f3232add6975af2d613c11035a4e2cfb0012c 6ab0feb875dd4a8b61c12070057e388aee3809ee8df299e20a042edb7860b109 +generate_keys a9d3c7f8c3d47d49ec014460c304326ece7809c979cac0538107ced8e6b538b1 9e2227f40cbe11c4882cd5d5d52f0fd3ce17440d79d4bd6170809113017d0c06 +generate_keys e3d46eaa9e91834c3e93ac494f222f45afae96d742636234426544b3226cd109 beb5442cb4b56ebc80a6ad8dfb4f59e9036b465db4eee84293f0107a2308920b +generate_keys 667e432b6832985a6cc77c3977e1c408f32d12f1cf137a0eac9ea921be57d9c6 9e693fc3ff80004544c3f498709698318afc408fe548bf40286092ec260e9b0d +generate_keys 09b3b22d4b1d5461d8b1912a6abe1f61dcdfeca07ee68b82a1aebabf5a9fda58 094fdb8c5278753544877e0c63cac4c6268b89029e6e855141845a2ddfa5bd04 +generate_keys b8db2d557d26fc54a4f06fee7b27578504721aad3ddf12e7e087c2472095f5a9 032989e40d1c51c132d04e69d497097dd463cf97a54509d06038333ed015280e +generate_keys 1e0d777e3e35f8c2577a36e7f259745999897803806d747264fb0dfb745b9a2d 48e53bc47e44463a47d807b968297374a82701e053969e323c99734b2210e507 +generate_keys 70c8740757549c8bad55c6c4297d52f02ecdcedf867580eb5069306df18b1606 fa0b21cb212522255a98829af7dbd18efd7f6fcd049f70dd590d982c1609850c +generate_keys 511cf9f41e05bf81b34f5ca978efce4f484444f9ad6b901c7b9a2301a8bbc8c7 e7f4fcff0cdbbef5ddf59d1992aafdfa70e00ae405bd322c2d258a392b345c08 +generate_keys 8e0757f3ab2147542dacf8aba416894dd6dd514f04bb57adb4ff0be8e954a4d3 4e68d261da8ff63b55f3abff9e10dfa5d974e79d9fbcd2673b9e997a8e770f0e +generate_keys df151b966a8ac6fee74d85baebfdb9867fd913283bea5e199c908fbfad97241c 05b32b61deee7c6743a7c62cbfe40d8820aabe22883f264021d924dd50598303 +generate_keys 57ec524745ad184a059b40c77d64b73d4b6368ec13c7777f983151b50d638ae4 19aff48224747bbf8d34603e7fbcbdf2ac5988a32bdd87a01fa2ab112b010104 +generate_keys d081dd8388f51ea462669d14930a19e115c8b8bce8948c362b210d765bc27be6 d637b6977bde5fb34d03f2d1dc4c7123963e769ea69c70a3a2a1a55048d54109 +generate_keys 627dd8e8d657fe3d18bb39a9eb8df7d31e728fe3edab4c63ecfab9549f88927b 0a2231f1a14f84bc251c824486ab4b1f741fb2b446d69d9f5220ddabce6a9e07 +generate_keys d8bac2759d5866e9c35734418b0148a10cce5fdb230d22de26a13a72f25bec56 8f29710f70527900bb12c8d3e14839a421d95b6f77b0b1297a5018bb17005f0d +generate_keys 77e1ce97f9db1a0809e28567c419f63453784b3b75060a4cc161b455e8b8c651 151eb1f11abb1fdf9bd89f3150dcdaed8e3bf81613478f124187b3e44a8b440e +generate_keys eb580d781a9b9517a1cda006abfaeaa61160f5319d12d555d28dd1d6f27a42f4 733ff2e03f23d4ca2740707c0cad9737f1da7640250ea64c94297627f92db70c +generate_keys 9d22c13f0c4b95f49fb6cb33f18b1a302bf997c9e5c20ea24ea4f7476f4b2f84 e8faa71052d3efcf22458a27a3fad394376f394bbc7862fb693d555a477ec006 +generate_keys 6b50a355ba28748ddf6f842903c27af7f1f3c7072f5bf15a19582c934730213e 6f2a3499ad95326a3f82a7f1e973cc0b17a0723ef6bce602efbe7f96b4846e0c +generate_keys 565dacdf20aee3635a8e0b1a7f4ed33e2745dbc39437d1d34fdecf2955b313e4 f0931469f66db20c2e19734b69858f2ae22443fc32f473f7a92db7b251a39b07 +generate_keys d55ad462e91eb2d82b6cd97adc3ad525feafd56f33217690dc5998e8eacc6486 d44e4e7c825932fab0a1cd4f1709f3043e64fe3cade041dfa4d0703896d15500 +generate_keys eae925bd0bdf731452ca2a7dbdf1cb59888f205d57e79c9c4efe2820be5484c0 acc8a8a01334249fd105719540604ef411d6e15c2fc369d84da5f2e456174608 +generate_keys 01de68dd01325478cc0c2dc446a06b680bd82d78ec78ae2a55933b785a9818da 192a6ea92f2a4da50206abccf5f6738ded478251b78012f738791e72ec4e1109 +generate_keys 3e61a2e1c24bd4eb7eec90a73ba4e776e4aaaabd53e81bcf6acb8abbf0c0a70a b903aa16ed5b1bb827ca708f01d3f17327c83496f98d529b955c24b4e239790b +generate_keys ee059145b85d7c2d8edd788889232d1a98dd51eeb1cfa17124c0ddd63192d3b9 7b1c2e277bd11b27fc8f944fc223fe67e312865be75db065bd13f1fcbdc73109 +generate_keys 17d84dc3ac40aa6aa46ec8d135dfa15f3c653881d404db9d1cb8072028177231 e0b3904fe701046d68d6098b4d0382f896acc0f3c2ab7192d188a086a401fe0d +generate_keys 6775c39b75ef1116538321278f91190e14c85fa3278280d30dd2e72b230caa5d 033b3e43691801d3c791c4018371faebb6ce8f27e9685b03efd6b96c1a5fb209 +generate_keys 19c6980f38dab192922bde825893dfa97d035773466f4628f752ae16589ab81a 96f4ecd7781f3fbc17516cd68be2c2795f7acbdc4924021b691e15ac870ac00a +generate_keys 9635fbfd280948f00e3a8f08437afcf9795d25bd56c6c1d932e78d74b06de8b2 25a5c3e30e84b9103509e798c99560a90e9b487c91f02687255a172d7fb34301 +generate_keys acd801fbd2ddf6a7458dcdcee9475c91cf99fb0cb240c29a316e1ca2fa37a0d1 914eb553f9ddb51fbdec59c7cbb9f87c2509739947db32cee1c30d5f6154670b +generate_keys eb843677a1586ca2c60bc5681f4e1898f8f74bd5f8dd503871707ddb275166b4 c679b63a4217e000255d6d7107fdef6d976db30334e1e7059eafc15c5fca3b09 +generate_keys 33c9f09bad0b13d00ef984335a1bef40ab5377de6f221c9663603a95b066eac8 c44e6d257fddfbda3af3c8834511973cb661b3e7002908992cb9992e2d97b802 +generate_keys 3bf9e0af1a4cfcb76410cf3088f67671e86082840a9207c9607b892618855428 47f5b062568e836719924bd1e4fe2c9aba9c355d4e303c8a59b7abc4eb309c0f +generate_keys 43fc36dd5710c6862d20ba045730a32fcac8f263ca5b6896f83a1adb63574a2d 808c71423f6287fc1a79d3ce70f0925e03d6dfebf8ec0238483efd6f6e6a8801 +generate_keys 7095c419f290911e6506600d91f1c4cdcdbab57a3b8cbf7f8f2c2c28ed1ecff6 e3f060245dfe2b1e04422743e4d12083369cf45b6e0cae916b7b0371508e0b0f +generate_keys eea390fff487e9d309ee6972968d2ae982cb9e2cf49495a4b020c3cd63cea19d 9d202d3de7b791cb38968cebe011226b8dd6f6eb5901cb11e446f4d97fa0a10b +generate_keys b48f170c66520d8226d0ec64742edaf6e366a4fd9897703f08aedca2935ad8a2 0be7fb304015140adcadfbfda84acff85ce29377844108391e8a2b51a9ba4d00 +generate_keys b992226eddbca653b8c49f82dd9637788ce8afdc4b2a6aba180e1b714cb5aabf a0bbee28408ba84fd04afa053cf03da06136a06fc93f0a130e06188eaead6e07 +generate_keys 669a57b7146dae91840f394a25e6bca109cfa17f21e42c442d5faa8a823152a1 7a5d9f5c8a9eb22887bfde6ebac1dcfc772598046b124052e2eda71429287700 +generate_keys 4ce60531b8cb519cf2a78ac06daaa17830b58bb535903c28111f4c00be925764 a55eb1ab79b8c75ce902e9064ca0ad3778d21337dec97bc92a574856b65eb30d +generate_keys 50f71761a3eeae6b534e984983bc0549442ac92335794c7b07a7c073c0b7d38c a9f879e65598776e7d91558abc53ee124952355557c767cd3a4dd80cb44d2401 +generate_keys 389955b3d095ec81c36208dd641b40cf203f7241c665139715d902fd002ed26e 60f3072e7f6f7d759d5fb403e362091ecfc42298df047bc781b073ab4cad540f +generate_keys a565e6f6d965b9d52e4e7b5c5fae99f9180f8156c9414892228752bc9f77e797 c6b7f89c0317aa07ce75aec0ae9fd4d3368406ef75ae9ee05bcb93272df87a01 +generate_keys 8edfe959281a6c723c0526442f2dafeef42ebd37082e7ba6255f6c0a55613e60 f512c30785bbb51bf8375798fef078588f75fbe83720dc9a08594496c48c780b +generate_keys ceaf8ba28373243b75bf687d11c7c2c98ad7d5d7ada3b43a9baa3ed9feff20b6 71eb92bbcb7148a2d95eecfc8261f3f6ff11e9390100c60282a60ea14805c209 +generate_keys 6f982d9714320ec0b160b42ff72f7f840def903418501032aef1132adb90c5c5 bff38a916864ab8e29ac60a38e38b93d8770112eb77549312cd7680901374109 +generate_keys bbb1d491847a47c46573933fbf10c780fc52524686e3797e28ffe8cdb5b3ea63 738b53f92315fd3fe2138819f01a943b07aa8ddb60e33bbe117bbbda6b664b0f +generate_keys c88de2b035527ae3d95b370ca426255dfeb29bd53c8f899f83d96147dc0e5c15 aaa83a0ec8d79ceb058b8dd078cc0e89ea59ec3c65dac3222036fc64c0643109 +generate_keys e29e95c281f94afc48349dce7a029642b3953f0aec69d76a2393602065b09ef8 00b481d8382cb04a0eae2c41da6c85aa735551c6cf1c4f503f0daf76a05aa605 +generate_keys e713137b719310ca1aee1ec8eef3438f9e178a62d7402d975bf851e03b089153 2371f11ff3e5fef35fab86290bc32c08784a8f67600e9657e8f2954aa3b98a0e +generate_keys bb8fd1eb43a19a7d38b167c23af1b5f3a6a76e3f49948acf0cb32ee8c566d731 ee15cae77f0737605bf2277f2f0bfce83d1575b09a5a53060c8a625bc5cd1b0c +generate_keys 5dff5f1e8433a62132667c971b02e6a850c5a4b61846f4c3a62389dc06a060e5 87238d060d7911fd308f1a46779e30a287d1471e220832cbc7dd985b516c8d00 +generate_keys 44b25512597d0eb9b70793081d5d2fcff7ad55ad8339acd40e12dc6bbe960aee 63dccf9358f271ebcb1fedb4710839758c18302be3223f5c70fea3f45b0c2900 +generate_keys 846c1afba63efe903416edb4becbf9f575316b2afaa741cd246d9c9e27f636ee 7c0561092cdad4ad18473a5934a1d4777e3b9bdc82f3f4d59adb26362fd7e200 +generate_keys 0e732171a94eca8998aa207f15c6e0be4ea697b7bf750d32288957aa18eb77a4 9f48474173570d90cefbf45b5686347e6e80748e1c09c0383890020d26b37d03 +generate_keys ffa5f021eef6d51e4b615df8a6ce1d9e7dac6f1bedf561155845dc0b886f2837 edb934f562b868d21720e8c9d4a26190b1ebbc0d572958dad22a5ac04195a209 +generate_keys e52b939f200a287c5a157df41d839751aa2669e5ac4f7d4ad42c852dcd8f6604 af2f823dab021f70d510da3e7446ec8c2ffd4f6190b2e62fc3740ba16052b504 +generate_keys 3b863f0480c773984f897e00ac9e43a656e4574d85f61d4ea1f7e7936c3f00a6 26c75a25a2cb49ed3af9ca15c1a1ecf7f2987f2f6c6ec8f02d5ebd4b61e79b0a +generate_keys a45400ec8e294aa40dbd5ef101592aa8dfbe42c4a9c7a1446a09cad84a38e8e9 57f4fc9d164282659ecc8b61c34fb8b558a529136ac5fb77f15837d7b7dc5808 +generate_keys d2b7719750b5ee4445ef9b4b4188e4d9a1154c422ff6b60d28c0c9853fb8e87c f3d724255a867d2e2b37aa3fd9579b96ff92dd1db3f4bd330051a6a8be8a5e04 +generate_keys ee6046e0555bda474858cf7bd5374d76b7ddad9caec61b7ea5c46df6972a2a1d 10b96fa3391804bf8cd5a99dda177cb71b462e26e8299c45c752f47e0f12f00e +generate_keys 0b8ed0983507075c27e5eebe3bdb3be4d6d89dd8c65c0fedb758f24648ffb923 1f616af2aef77127dc19abdd6ae92abc1da33fa2fe15debdab64a15f001ef20b +generate_keys 2bbef2ef84df5a5808f580f0d0b8ff302f7e754efae6cae91c5ba569adcc0620 88c897106f8d0b16aba770a3c730a6ee11007cf436a5bdbc699bfd64d80f6b04 +generate_keys 68763c4a1585cf46e2b6c622541d2d8a0475448b1996537cba1439247c409981 f4f86d500b969fe6b5516aa7fca97919aeab9583183f1adee5052d2bea9f6d05 +generate_keys bb0f0b1a7bee84770a17528a03dfc7334a8c7aa1f5862175665d492f65ed3f01 3874d24ae4aae0f01e363f5c77dff6ffac121fc702ff21c984280248c3d92101 +generate_keys f33a5149134dba68608aaee392dc437f262db29cb80792b6202d4dcee2d11864 f9cd3e41e3d8d3f021cb9a9ec6392dd8867fca94cd8cc9ce4e568041a6448907 +generate_keys a4a1ee3609db194930a9a2c3bc14b3f9250c4177155eac580660836f8accf64b 1de51d996ce5e5608598a6903dabc797ddaa2312c945e02f255f4057f5196c0e +generate_keys 0d4eef1ca84195aba1862d6782bdf0f6dad976203dd1ce76f69a60e759e2a674 ad096f3ad2993fe7dfe3bd9db5dd15494f91b6645bed57b614376fe8bcbcf60e +generate_keys 28ad3d1b3ead2cab958a8705a983150bbaa948da0d706786f6a26f71e3119627 42aac52a33b61cb9bdf11788969b1eb2b9629f4be8befc7cd373b386d303d006 +generate_keys b693e73199b2fb5e139ad9fe630466d38b5974683bd4d1efed95009bcab9c3f4 a27609a2e50e4eca82d390b3f0754748d60fc544edfbb7596a043c2dcd34ed02 +generate_keys 2bf13e487402b267a6f703a29ae92b7acc688662fe6b28e2c263afa770fac411 1f71ed476cb4e51ae514b6ec4528418b7d6696e9e51cbfb96c0d0b1402a2d20a +generate_keys 4fd4b885efbf598b0ed3f88ebaa177113c0822c2ff0064be25699e16678e2873 9fadd2b824fec3bbfc8a1a7e3353658b243371d782f3e85e03a1b5f22607640a +generate_keys f952f19f8ec4afa98d2849d24d9a5ccadf72e19c452098e85a0faa76aeb3436e 3ba125ca3b0ba135360b9622a3f8d64ab2d990e44d303531e69ae55370b71a0a +generate_keys d81d76ba2c93b2adb371649378bd7753c4eb8142d747a7594b3a8dae69b0e375 a102c139e6a3966c66024da689fe2eae150fad9f8116de548580513fdd565403 +generate_keys 562bea1c058b5678aaa558776122a626ff0181167f57f78f25160e27b7a3e20b 6e9aaff46815c66d4c34e8cb3c9d4cd39c2601936c248ba5efec1cb3a7619108 +generate_keys abc4b53bb43840770afb0643ddc423890364863511927950b1cf8ad39c242a64 ed1c7cabfab7eb0511dd82d5bfbe93eef17fa5e324d107901db280c46fa8b105 +generate_keys b3197deb5a1d42a1b8e60d6c305618b7768c4c1fdf40fe9d4d89525eadb6ad6d 56d5d0122cc5246094338d9ee1c39fbdeb2b7e541dc46b79b7cb26e11e9bdb0d +generate_keys 338a5e980d24a9425cbd090355725975285d7c4c4bf869a8008e04fb55657ba9 6cfbe833b5621ac5085c60b27056f982ec1aecbf9421ee53fc84ab186738eb08 +generate_keys 2099d8091ee581abef48157f6de3e12ed8bcd4b609aaad357e1b228029f5177b ade0e6103101077cc4fc930d84796d91d79d53a126aaadb24263ee1fb17cc501 +generate_keys b2c5fdd235b30135ccc5be70c3c22cc2f93c7570550f79e1f7a2172782b8ed8b 2c45c6b5ff95a6787197a5b10c71311e773d57fa25697516c8cd6af765907f0f +generate_keys ad3eb2cda5c708a3f3374c1b8b143b2cf98afa5de266a552641251455b4cf0a9 c4483d14855aafc5d5c228b4a98da0656bc6b6a5b29ad1da3e34b8210c248c08 +generate_keys 6ff21081a27ff16b9d8ea2ca2b9027f98016a47ba29db437d8f497888a875e5b f78f6da277de036296d5ac65102b8dc519b80c47e2884805f0cd880883918e06 +generate_keys ab310aa5deb6f3e06c4b7fafe5074576675c290fca8342537d309e69edc4ac10 1235768ea6594cb5382af6e7ea8b3f2f3a35ba7f6410bb4d812a4dab9793b302 +generate_keys b94d4bb952473740d23195d4059794dbba28f371da9882f357b8c97f6e508c9a 0c77805834933bf0d7b592fec876ec5faadd05fc6630cabad7145322f5974f05 +generate_keys 5456e980a80276df07ef25181b7241132f3d81d036b3804f876e468a9243fc8e ad5a20b594a1e93d54948d7402e88182ad056424d5193b404b494a74ae29770b +generate_keys 2d288c9f5b551bf525ac7a3658fca13de29da34c6dbf845c98fc7823d1c45687 d78dddff8fe4b48943f6e0ebc7db1228b2af22498f19a88e5d3249b35831280f +generate_keys 6820655049885d86f6cbfe99599ea1202b21b7154749f030a875e23789fa868b a94ed2d9f358c0b3cda2113a28825708ecbf7e51e12f5eb3fd1f5e9fa8792a08 +generate_keys dd1dcc14a48f40f9ccb8eb21187147ec69d26b863e43633fbc4a4bfc103c9050 ce4b8b954babb07072db3dd628c2c56ae5b3bbb8e49efe0ec9f290d55fb5c404 +generate_keys ba413857badb50fb5d061080ab8d98c61609d44251630ac17bc372832ee91936 ead0d08793a756854671c1d526b30a2a37a31e62ff4e1d6168ea0d3b804f770c +generate_keys 922073a150f80d0ba376ab552131a72c73b95a9c81f9dffad03112c5801a7d30 63e122396b196c4af18b15c724123b0801d0dc920d0071988d4c3ffe6828d007 +generate_keys 02321dcd8751ed49d82db9e72bb347f393434d057a4279bd89045ab68aaaf7ac 0feab9cc7ecf85d27f947cfe53c390182fb077b4d52887fcb4d3e86257311b07 +generate_keys 55521b8de00b9ca73e83b015e26dd2656b31c608c992391bd556c9433eaad627 5a042d48700db6b4bc508975d771002eabe11ad36cf634a0c3eebc963d6dae09 +generate_keys 0a57cd9a3c5831f4c291860f81e0a85ca70d18292e4ecf87823349f9cf6a4d36 28fe6d3b4f96ff0763b7a1c27bcab75ac0cb4121203ebe1044285254b0f1c705 +generate_keys 72d9e8a001677654dade5639673b0ab22fd74f4a7b7249049af54991084499e8 47331f977b73c8d3f504f6267c4fb548aff45ab474624337df08627875689e0e +generate_keys f223f63cd65695034890fdf26f68ed0d4e0fa6388974993fc46ef4193f3e4a04 ad6d6b470eba69dcc558856cb1cfec5d7968024372f073d1fbd5b70ef99f7b0d +generate_keys 3efd50e50252c3f97d58a5b59a3692bf07f5107f3c78627c23918dae0b134d0d 7939b1c0b51b4ef008f4b0f408e2e4bc12d25d16a5015ce3351206e263c0ec0e +generate_keys 0d86f9c06a59f65d58797b0da53a6649f4dae67775b49d7ab33a840c015486ee 300f0728eec27699be487cf1213776550c5c57f49ca05e7cca6c4be0eb3e7803 +generate_keys 0a2b1189e1382f89ef7ee33026df16f5e73316d6c530b55327327cf7e438dc93 2ed988eda95ee210e2ce5e717771410a659a2b68e199d2bcc3fe26426c52f406 +generate_keys 84ed9a78a1a9540c56598b2a53e1702567f0ef1adec43080f9453b9537739be4 5408f0126dcc80f0701bc882832d1eac6f5babb4fa976ce87e60a32b437eec08 +generate_keys d65669aa48c36c7decf86f120fd3078848d4568d9a337a9e8af78de4fba59039 7955a64af6e4e4f7fdf24668088666f8df8b793ea77d3e1105f7fe53a4f36909 +generate_keys 23ad296961cbcd310e41eecf365a95fa1183dc4b1d892bc4ecc29327bd273fa4 e7bc1af4b4703bdfad76174e4329af1607872983eb4249477d9323646e8bba09 +generate_keys ae6b3edd13d95cd970dffe4d997299258b9d77da7883701c5e7baec6a2b60787 068ca8e94dec061db5539c1d8ec8e2d53f6b249303777e3e45fc1fb39153ab0a +generate_keys ce7a204cfefdad743e2dd1d06eebd6333a150e0ce7f9102973a279f650e54b5b f10a00f97468e5cba6a79f66bf2a3dbc0c8500b56ed7c98077c9f73f2a751d09 +generate_keys c6224f99251225aaa138d756a5715ec3b6e64844cb09451d85340b88cfaaa812 5d14a4482e4d15db92f848b5fdc8076e97b74cb52d775d3e9f6f1e8fe42a1c0a +generate_keys 4f4182d8ec8c7e0ac7d1c3ab41d79212713a0f657b82d7054cec14ca71c96315 9442c2aa1710d99383d4cf48cb2f89f986699d426c5432bbc8bf0575ff56f500 +generate_keys 22a110dab4e71cfcf9a2d4067c74e37de177201e28ff5411f81eabe34ddace51 83160ff6147970103181024292dbd1fa96c070311c5c98a0f12f324809fc020f +generate_keys f188155d50a8f83e99115c1725e5cdc9466c47ad17f7a8ac2fa38eac9cd46013 26d0f8bd1eab7a3dc9a701457bf9f107cbdec8f38bac901af8d188c103968904 +generate_keys 506bd71758bce525b6e4215e8f25c9da13a32222262e70aa2e59464487133c61 83cf4c6b69e53cbcf20ab0ff28a7cdc74a1ae6477d26c062af12fb096f81d809 +generate_keys 9436667a054df080fdf7aa01d54bfe3ce366433579e2451f79fd17c91b2a01ef 40f3d7f8068ee7b451e02305f8f192bf86bbe6b3ad7c0138a77a55542440430d check_key c2cb3cf3840aa9893e00ec77093d3d44dba7da840b51c48462072d58d8efd183 false check_key bd85a61bae0c101d826cbed54b1290f941d26e70607a07fc6f0ad611eb8f70a6 true check_key 328f81cad4eba24ab2bad7c0e56b1e2e7346e625bcb06ae649aef3ffa0b8bef3 false @@ -2547,262 +2547,262 @@ derive_secret_key d76fed84dce0fc00392419d6453cbe540a5750ab795c74b32309fbe000de16 derive_secret_key 2a300cc0c66bc939134a64d0c7647667ad2bda2c9617215a082ac6312aa3ef49 517850 bd18c8e907248bc5cf0a342af0acca3c22a8595aaf37280c61f0e4d772a9c102 4c07dab384e9f7a69789974fcc1296a64c1093c770413d9cc1b0e73a0e55aa0d derive_secret_key cb99d2c0c11ae7c9274de77f8883bcc78d5ef2ed7d77cc024982ceac07cfa2e5 900712766 3dfd746c1318742d7f374508d2131e5da8117f79d7aa32dbc14788933781620f 3103a53e415e8044c484c8e38a756601d18c092b0e60cf20f833aad242feee0e derive_secret_key a2eb32b0e18438b8fccebd078c7a8c419d76edd7a163aadd80d9cc1097da4845 683919645 8fc1e8972e4a8e4d429d2116c2993c3a9f0405e195d9db1ad6ee5c130d661b06 8287a1a390b3c726746829c8e6cecc47d76203b50eb8d4ac58cc43954558550a -generate_signature f63c961bb5086f07773645716d9013a5169590fd7033a3bc9be571c7442c4c98 b8970905fbeaa1d0fd89659bab506c2f503e60670b7afd1cb56a4dfe8383f38f 7bb35441e077be8bb8d77d849c926bf1dd0e696c1c83017e648c20513d2d6907 41adfba66b31972636e89f946a639c84e8408569492a5119d7a235fbd7a96804ebc6b4827d10f0a69b0b43ddd25bdc185d20e41c9291180a3a54332fd1bf290f -generate_signature 2ade1389a860c9249a42e45d32a9fdc29286c8dc0c8ea1216ba786c74517eefc aa2521256174ef6566618a6468c7b8a71ce2dca398be2290148b813710d12f7e 344a6ad0374b6ae8278e3f226d58e8bb2796e89141eb0ac37cff8552b158260e 97ac22e2085532253e277effd07dba071ed8fd3eb2cdec34c81b9ebd6404a900ff03b52d5d51a286fae6a2a43811412dd7681c19999722602842843b62e49b08 -generate_signature 5e91901e800a1959b4ec07a2eeaa3a9b28893029a26e8ef5d13adde490e5df91 486dfb4904d81b1bdaf865dc07ff71145d1bf8a9e0c160b9c817315f6cb30398 6a05fa0a97e172c9a8f5d2e24851ce87bb649a46c34b33330ae71d0d24a4e70a 4dbdd3aa67d152372c830abead3de5c0b42b31856cd8f19f54c7c581dce6b602881b701d291f7098b8f4a33e444c39b66ae50b57182aac8bf92b97164ecbfc0f -generate_signature 03e29bd7435541f8b12830d68fa31a8976a7a2750e4942f36cf834506f3f1b17 3a9768ff7593e63e615433567e9f2d7167a934a9afe7fbe587f0502942ed4ff6 b1b9f0cdb59910598538708eea87ad5114ee9eaae0bc5c62907688d6adc60004 e368cec71ccf503e67340520a2fbbf76d5a064e525565e2ae3a86dedb233e70e53ec3a40f5e0dc0f7ac026f731faa001f0038852306f1e173baf16af19f04b02 -generate_signature 5eeeeea12358c1ca323e2bb7f7c4f4d4f64082739f196e5505a116760af245bd 238cb505dcad22cca64e100b9b0a3133b44dec7a6a0abe198f6563a1df53a3cd ba2c8fa6cba86ddc91f810519281abeb43554e43ade7bdb3c238bec0bdd12e01 7f9598f10dcb8bcd2bea225a86bcb0791976d6f45824ad6c06a41c856985ca0ab486fe24061873e699e7371f007a1a7d165583ca18f032586ff6984a310ba608 -generate_signature f3f9ac0a5f5323f4af5441d5700f9d520e6cf41ea441ba045716b591e743cad8 73c0232f4a68d26578542b4d1e02287b995aef419200f78b3294f1c1dbf979e8 9758db88417ec357419b8e7ef2a75d2c65bdb800d27f6203aa54b5609955780f 58368b40d61dd72e811d3925bda152a9332a1cab3587d1dfd437b5ac057010035bc178a5643b423fecaf3fab32785976436a89129d6b4a42a131f959e4a9d903 -generate_signature 0c85328313e78623d436a1e3cf04ebadfd0178d4b230004469ff8e995fde6d11 c1429c3c7676607688349d6727135bb442466932035cda069ab2463b97387d59 1e58bd527b1ba1014ad772c523700434791a4d4c005f2ffb81fddfccd03bd304 c9de33459bdfc20e2d0d2238ea692f8cec8b9cad491d997405fa60fefabe0f055d5ddbfa39e704b1d2592a48d74ab7b19cdcc5de0f13a72eea636e22a27b5c0e -generate_signature 9d2ea8622e9d46d17a568571332f14d6be90a40df1aebeec20f8418a5f94ab55 f8b705820107149e4845f2a3f3bd045a55944aebf35d1104632450bba4141db3 d699d458752018ca3ca82433e6fac7547030990c923e4192978ef179ad4aeb06 45fd031c990492f3f31f8712885b2758fd0612fcaa59c6a6a178f854eec381086930790b63842590a04a60ea47aff6f94fc772da7256136e0f2edac424756c0a -generate_signature de9240454aec3ea2ae6e13ca9457e43003991b9d0f75a4b25117e1ae66617ea2 ef02c34f64ca98543ae5b871197e413a7ccbc008f2366a2552f2cb5f852d8ef0 05cfc28bb9cbe0c4d02d8692f40fcc864665b118064f1bd31a347baeed24240e ebcdb986cb11bb19861348c764e26e085bafb36c0aa9c4f16c077fa6bc926d0cd98102f4fc0223a482c8f66430e2ec4595c739ec291a3031d86d2094ee1c5a0d -generate_signature 806c083034f5bfc469e68a490f512a3c6cc3d21d043dc3f9d4941f5a9fac609a 65aa36a44250dc1e5985676f193b23119f7d5f9e0291e6643fa55ac70b1ebdb4 5f5cbfd6c2d5e22b703db1c64163cc0bb674a1bad00cdc77ee51001af5f8af04 46388f8a24890347dfb85d0877a78fb53049ca7c615525df66730387d9cc180378a2eea2ba71b470f18310abd0b6b5e9fa895b0da5ea9216ff48ed12f025da03 -generate_signature 413c368f9b4327907fb6bcccbbdc0540acab58dcf1a860c98a4340448ca4ef4b e37df5cfb5f8ff60b822d3550bdc498a7557e1c11d82a035782931854b996b27 4dfcc2bbad477e1cfc6076408fc85ff9f3381dbc2ca5f6b61b4423a29f372d06 d151fa8538046dd8c858dcaf45a9b1cc6fbce607b97f6e52ffe30845e31f220b7166a115a492342539a34ed08a1c04a77bb57d96f1f65b567e90767ccbb5d20a -generate_signature 3932123f3c95fdb21bc6fc74042f39eec45e588d707fbb7036983f3597382bb4 8b0138170662561e8b4e32f0821d21d187c62e7939903ee747aed3b1bb277d2c 649ae266093e93d0968f31ccf4d0b9307abf1e50b7d2b811f74c9b83f58ab00a c9651ee70677d92edd2589102f539274933030d00f6f05a2c0cb73235a08bb06f9077c2f6c8278db213db8417da6985f8aae08e6f93b44710d86a0244797a304 -generate_signature d17ab44dd70f88e19f0e8ba88a413fc18b2a1ce0ed712d6d110a191c9a284236 38bba4ea01a82f0c16f36f4f3387d6015f2c703cf211f6b73936db53645ef2a4 9d7af7de69861c61d3c8c6f2022e360a6cc8a647a4f3cfbeae6c14f46265da03 ac9b40c5e13fcc7d81378558d9c9e5e15e197741404e4c2a263807715d7fb100c4e4c71b75cb41329e3cabe6b18191a3de9d30f64cbd2977d83889b58721e50f -generate_signature 7dd81bb4dcd4d74f4c034a4b6a51a49662ad34a2687401ca00e8a628a298ef44 5edda4da92d8227684c8e30c940a9d4f9ac51c9f1e1c9216e826e6de3212f47b cd386423bdae74357991ad01bae929b6ba9d65803d00f06fc9f0ee34a764bb0c 339060668eb3ce21c16bf2942c4142a93923a30435fe7ce8e39359409c129b05afb68a73471070e9e975dae045cf16b7d923a9d99dad56ced01e5c5c3b47a908 -generate_signature 2e8abcd827b3c90a24a65d29e229b5bf59ded9b13ca77f51a98fccb37cf9775c 10df138936f814628662c2cbdb25dd7820c310d7974bc74314131f8423880dba 0d8d845fd386f1f47c4e92d6861026d697215a396f3eb7eb76cd478dd6c1220a 942d0ed063b91ffc7898a212438f326091d2288aaa44d7e2606b68282aaa73009e1f864171601bfbaee92c178a4697b8783ef34dc445a7cf5958040a892d1005 -generate_signature 83e42343a638dba6fb5d78e1b9c11e236425b432c77deac3ad098b93ad73f14e 3e855c996c5341e02e85737a2b03e98491037d91721cecaa17a83c2c4d5741f8 87be0823f5741f09e2f0c1a3337afddcf1533dd10e0b28efa8b1147c9ef3a703 cead7ef79b1f6b72b59cc12f5074e9cb36fc63bbbade09a4ec78aa894903af0f735a3b552c8df2edaaec3fed47e7a9b739c766036bf43a1b4f14b4660f4e990a -generate_signature 206fef6045a5e44f87bc7acd4bdcd271e1c55cc8e42f5943f1f0b1b3dc0fcc54 9d32cb01bec30e0d7dc921f1ce44b5a829fd84c37e4304e4f910cb9df501af4b 8adec55623bd61bbf39d72e3f891419b5264af1bfaca43278dd86a944ec04c0d 5e2d5355efe9aa2e04ef836cd92fcfeb269930f3dd194533a4d29768bae62e03183b74c64ca5bdb76f2763473ee10ce9e7227d630e4e2ac6617f1cdc7ec60f0d -generate_signature 0f1d0617a849e1266d6741e17e8fd8a4af392577ba27783ed203377cce7514af bee8f6fdc7360d9acd4a4076ddb2f572d9e9306940b39e5bdae031db28f201e4 95f785c3db1b8ba26e660e9ae8a60af20a00289d984a83c852482c505d1a940a 08d017ab97322008141dc15db28abaa92bbb0fbaaae0f31e3b700eb7fd136c096fb004b703177cd007e5c9d6e963c8b64247791ad0889c7a8b8c748858490b02 -generate_signature fc47da4563a05a5334fc291b1730ccad835e9469f417987ed3d95aa041ca2aaf 0c1ad3f7c9e60987a84a3525a4dfbec5ad69f276ecb32e3c0880ce69befa4810 50b84ef4f4178228d07435d3105b0fe52ac7d2bfd38da43875579fe65a7efb0a 876f0589f7d1272022999b3545fb8dc1cfcf62c9a2b04aa196fb8099afd30f0270dc58c1735cb0b1cbc29e53b10e9dd9975be9ca7b656e95e0483c97dbce230d -generate_signature 02cb1e9244aa1f6736d8aa1d5428b30658756bd318bc938d5fd704d97e4a5d9f d52a28a31f59208e4e41f6473e6bd9229ab6a809b228fae3d8f0a6bc3a34eba0 c6fa9ba77466112e1d268891c5cee9f88d091a93c06cd4ba23bd034cb3d0840c bc7b429861d39a6383b830df49564da9856a1a5b7df9a6895fef0d8df75cec050083d7505d2e19f89dc91cc598d16e64e494c544f1ad7453dfc835b70247fe04 -generate_signature 62eb41777076bd1a90450e80e77cb1aca6a5f08e2320128b704b663402759d01 8e83ce60900fabed38fb560505d26420775f10ad6729b209992816101c1c4cae 14fcd95cd127e1218c11fa267c46f8c2d5c1c736e53f8bd2bf084693d1114404 d689be63dbd3b31093605400348e1d6a7325e7530ce650c7adbbfd11f5885106f9372cecb3305de28621124e25fad69942a304c16338df0467b3707869f20d07 -generate_signature b862759c98652ca2b1522a556729103de96cfe831b3ce615547ba70c5f622c0f b7827c8b2edc9b4014cf4421f1c9792c012ac3264b15204596652d5854bc7673 1e62f5b7eb5d2bf88032c98edc74a9c138969cfee15f4cb69990feabc0f8840c 21f9b422fb0575bc0416549d3695b106f2fae834196764800df0ef6e1e5e34074abbc54b65582daa19859f5c029d51ec361d55cf7f3d2f336f63bdde26fabb0b -generate_signature cd35a8dc333ac5f33f3e12831a0a2f9ee03c7f421dffd7900b7661e76b056f4a 31ea2dbe369fc7a4514f3f826cd259eb8baa6bfbf22fbc2ef520d98be6e821dd ae4ef972bdf190ee205caaee156166e180e054993709bed5ac96bfd80e8b7401 c743c58c7f4c27a3a240d9d9d2107fdb176db92f9e95e627fc6fa47f170cf70e011865a8720913fc77efa1f0a74415c8e187ba42a093cd0ccc5ffa0b20410307 -generate_signature 5dee08dd0f483aa67e0b2539c362eca5c4529961935d56f661ba641e5efa7e30 be75e0ce86b68a72f7487fec4857a7a6a188176ebcb9192123ff98c49bd2b4b1 b13ba6bfe9c23671e7bbc4a6380c1ab6c61b09db58220d28781d8ecb1b37e609 943f654c9747d760055a4a7f98186a374eea7257e638e567f0497b4777e0d3097c8fc6cb49d29672dca8a9536afcc5921f716b9d0755b62f169ac7b243c5cb03 -generate_signature e88cf519e819375fc59bc8de8ba403278999311c7490290f2f5438306bdaf339 ce99f73115a02e03f42a1b583dc3cfd5fd1747c1400d1b6df73e1a3ddf616932 0a33216348544ad80a652433253b6921c0599d3a80e4b2d4a0477cb121e08104 3f0929d82c19d7d9ec2a495960315124877bcb5398ce6c27e4088e1c495fb70bdf696deac9edf202eaa4ebbead7a6f12517794cc33e4b9dca2c8236b1fc3b40e -generate_signature 82a2377ac1c6a103cc0b955ea1ed3fcc41bbfbf6416b31984f28aaacaf4e2a94 535ce02ef72e89bd86e0fbd9f8f0197a99b11f81128f5179044e4e6d0f5f1a93 dcf1736d7a4306330ffd64315b2122c5b55bdf115dd52680d5ce798893b62a02 a9a71249372e01b2a126cde62b97e956044cbb60c4995f440eb7ae4d1f81da0f655ab67f279fa8f7390d377b79dbeb575577e685b89c5446700ef3394ab22d0d -generate_signature b207af5749179b6f2b30f48f2e0c19d90b6655cdeeb3fc37f656321f50ab1508 b2b43995a5ad7f51c6c71d29e1991f431444b5e73019b3d36fca05bc566bcf41 b1e043b1b9f4c280f798aa59c2f62e46c4abbc65ceb17349f4d11b867937ad0d 4818ceb75362196ae48d16da6069b7d603330b010dc7b471b898635c4638e30d2e4d5c22cb98cf66b94e5c924531d22388ed40b69ba64ddc1d8664df56da900e -generate_signature 63e53137888836ef30cd6a93d34a0fa749fcd9459bafcf143da29956a6f35060 4762dfaa39af96a85ae746326693ca87d82a008b96c84792ed1c570abaaa81b5 723eaeaf84c9dae7476aabdc5fd409a0faa7c61ba8b424ff78d299b9480d5a02 b051fc13c69406d5da0141e0087398273bc45fd732bdb90e38acec452b237c051fa4cecdc3677362f64071366e5b244e991c20e406cd2536e60990b5a6e71a04 -generate_signature 4cd5f4b8cbbe735336d469aa8f2f7c7e5195d60f897b9a81b599b2e045850705 4a6dea121c196f56360e8c17273f1b94827efe1f24cbcdb9d3ed61b9a207b73a a8e62911309f4d4fe0bb6284fc4e3cf4fc5b08e3812e27328f68c2a6c278440e f771ebbe054fffaa12d29573f25c624d88b343dbea5ad4992753b730254a2403199bf703fc4e3629a5c14c675893bda4584c509ebc62c8e2e453609f3333d506 -generate_signature 7687ef751d6533aca6c911d70bbc4a8c5252fbd3112969a9a40b9e1dd041756d d17a5002e137731bdad6600b5ea02b520efcd1005008c6f1244deb839ea041cc c848b5aec4dd76bd8e33c37ac65e884699fdd7b5c7a905801f2f46c56f783005 ba7099ba5a21f70406981a9b0b5ced6895ce4ea43d8da9fa8d4b26fb5af4430d5d36d7ef9a3cc36f0627978b546531cb6e28c9468dc25850650fea9fec092f02 -generate_signature 7f75ac1c1d1832d78136ae9459b070982eb03e91fb4b686f17e60be644127d2f bf2d56a4e6d6f7eb153f31bd9f39d23a9e8d06ab8f906c716dde473fb2d16554 c901408e3a9a52cd7b95db0a8a5550aad0af38190c082f5d8228e5587367f500 e5bf769d9ec203f32035249c10da7236da8908fef78aeb6afd7bf1229b4d9206e2fc3729692af87293d3653637847975357844d4627a88d3fb30da8c5c1fb502 -generate_signature 1772e5a77a57fd69d6ce9a68c81bd9c4f81579741103a177bf584819a656e1b2 2ed6932a62fa064c9c2e39c7ab0ddbb6786dc849286af8e35655a58cc9be79da b9446a654072103d6e331cea1425472e0782b532ba9e235d61ced65ad6ff3705 cab455a551ca5771af30c75c9972aae9fde4a90fb7b24e5a8b78ab4bd30be60a129838221fd7ed56e614c710538b27dc39c1004ca8e271af0c2c1ac9bc5bdb0b -generate_signature 8bd02d71480ae9f17e999ca813b837402c999bedc4374d416e6aa9e233818f7f e0380564647a85532bf68ecb662fd8299921c8c51982b6310b4a6806bc056dca 9df00a62dfe8ffe27a34b7bc6a24a10d7f147d620f3ed78953930fb845598a09 1f014065e83ae2a42edee5a7c4183fc5102efab8f3d2a3ca65cd1c3e7776ee08232ed12cca6b5309890659261b9dce4c009f4c3c9b77d8dab30597a294701002 -generate_signature fda1233d780d70dbc9580c2b80ef1806a8ea2df30c317f7891462d96fa4d66af 5f769cca4be9aeb809e9803160b179b1a7bb7cab0e2b0f6311c9e29a8db20de8 076d27b6f85be7acbfbdb38d1b22e1d75c139072f7ae9d33bfb7590b474abf08 4600dfed5e02f3115f2450c0a19ce5c755bfbd57b11bef8756f6d7d04be3760419b973b1580625524dc3ffb8559e7e3051642da3ab69a051e886b1feb2a89a0a -generate_signature 80a147a42fa1e4f83d3915888f5c22ad71f92c0b2e1dbef9657330912baac097 add3133bbd5b10f3b34304864277b2a335b3625afe0d188ea6f60ba7302cacf3 911a315f1f7c21c97b7dd0385da95b6bb06de39796a538d909d51bfc07439700 19d51a6825df10eaef009e53e9efe3265c6016947345e148b25579dc45c8fc006bafac3f33c5b210860da6e2193be3f32f91cb26a2576695b2d6003cd0edbc05 -generate_signature 870ef0ebf5756b28ffbdd4cb65c2abfe6bbc366408ba42940376e91c750473fa 7c91e023da55901252f69ef58bf56329bb7ab688f444df00c1dfa80916a1851d 1752295c9937a10004ca9884b676bf4cd5608e4fb882f733ba1d91093257320a 1ac31cf0bb8870192c82d9bfef1caf1e8eff59f805696dfe7ac54fd062d9a903c1688320e27eab378c69cd017c965c9b73ad97bdb655c50e383128d26d68e703 -generate_signature 711f84e938e19a461e28c770573ba9220b84b233ef6494e1600b432333bed7e8 2fa1edf5af46532c7d290322f145d44487bdc39de5b60d365f9d2937de5bc4d8 86b9d8c1124989a8d5704873ee831f1620e580799fd46e507e55c9dbb2bde40c 55d08531dd7495946b4fc89b28ec041eaf54e703b2f38faea6c662b58f3f48024af646ea9c781e925e9b032709d40713debcf917db7ce66ae98c7ef59e772a0f -generate_signature 182159820cf794a0a367e94e6181cd2bce0aa7e8628c89f2aca4315136d58bd9 6252042e2d1f1694b73799d9f4bb777e7019ca1655c73dd79715e42f473f6c29 f34fe47fd0f4e59854e9b1740c91336978ef9b72bd07b2cbc601319adb261a05 947ea276f779aa2a0a56026b5ac48968cbf7eb42e5f369b35b0d6139a23e5204a75a70f9ab869c7b4a20e2c916c92c9ff2e2ed6bc9e359d05b0c3161b82bdb08 -generate_signature ac1490c8b703addaf061bda16c13c0ddd4ab06da053317ebba240be4e794b7d6 14d9ac957f7b65d0a8e55488d62a60325caa227bd28aec1d6a4f7fdb83a2b6d5 1d9d9a3c2200e2a127b68b06b432714b585dfca9fc8bc46c44e1a7fe7462fe07 551a5d005e3ef55273c16701435d8e3847c7c1c20df3978900308d37da64a50677f004c9d90cc1f457f9f50176f6af997b102d65af194b57a1a7f683ceea3c0c -generate_signature 6712b724736993684f7380ac03ead9b3df8c8d0f131d4ea99ade9d153671a3ab 69f90161bfbeae0720a5e0463f15814ff8267d8b742c2eb612f387f08cf32c30 74d48447a77283726f32f5e726074f1e3173d05a55b9e14796d2ea5436f0ce0d fc0854c64a24d2ab5441da978409d1812e3570ffcdfc7b635d208a797941380b21577620eab0effc14b78eaec5f8da471bba02dac2695d5576a338f72f8dfc05 -generate_signature 2796b04ccbd3ecb7ca3182d12bce035edcc049e6eb7226977e914c22b406939c e02e549729c483263547dfb5e4280d5e4ebe90a7140c79c0eae07ddcd91f2266 a81f6fdda8b3c5efda411f6ae5830c151c1e09ddee44fcae52128e2b4fba540b 5f520bcb0072fad6191264f77d70de5720cc8fff1a0c3b7c2f45512874f58f06df0bd649ef3fb22da23e018e2b120c170e1aa03f107b88434fcd15c3d7827407 -generate_signature 6b07f68fe4e8de6a194532606d0f64bb2561abd14d66cf4dac2526f8a5f40763 e21eb4c04c5abd7c4b12e434560c636a9c0fb4df380a207a93321e1865a7aece c194dbf0de2af2b96b1af7fa24b4b29f1291088413c9f26ed98954856c411d0e 3db5294a7b4c3497c7aa2e994244d155c3c8fa6784482095367b4c0000e5810b22867ae36011124356da26203873bc00a32dae2d9933f81692aae5961740d001 -generate_signature 17bf7d4f22e2e16297c3105c5ea01f9d7210b77a02d2ddc8d9263b536c1c51e4 94fc695c28959e0d68fe1dfaea0bde1a2b11be6eede96a4ab65b68ef0a98c082 bc021c4b80b42671b43227544c185fbbd90d29b5d2f110d5030aca995d0e2905 fd3ee4bced7fefbbdd768317c50b2d0b697cf4f5481ccf21e3b9677c0eff660b92488d51f2b2c6de91fa0a409df8933a420af9c649d241d6f26991a5699b1207 -generate_signature 0833ccd1e71d03cf19ab214685f348fa88e711bdbc6e7106047b64a151fb6c57 f6064b733fbce4e1200382adaf77d93c542a673573fa492cd4260fe28b14ee16 01f9a1785bfd24fa38af0a5d0c8324f8946b9d0583222c53772e69367597490c 555e6c2c3179553ef41683cf98050e78d580eec4feb0483e6724a79bf091550bc18e69035297d9b622b0b079d66c33472a3eab7f29fff3084fd3a377e95aa708 -generate_signature 2185872fb1e5cfada7668e6914ffc05f1c5fde38a7319e79010a8c1fc1c2bf28 23bc7fc2dcc89856410f5f2a006f4030d7880226ec512f8ce22926c569b557e1 e8ff76b5f7d00a8b8e6e2fa40b7385dd86692bb197cbf1220f3b59f00e149604 0773eb40a6b4cd588a746cf7b1d98ef2b012e89204d7d67dee14cfcfb0b7f00a9e5fad263f40b4dd80f5bed4f238125f9cd8895bf0ee804465cd9d6b8bd7ba09 -generate_signature 690194220a436c23dd6863d2b7ab4ac41cfe4ec6a6bc6843f1efd4388f37ee46 950f222148c84dbf4d586e7f7a2b53fdc43e45e034d0363883f50059302e5074 46d06e7cd27acea2c4094cbc3ead11d40b800fdde1a0fc1f687048e2b7ac8602 7c1844d1b9f5b79dcab032ce04f41c98bba449bbeefc16518433d0c9abc9ba0c3bb4c52991292a33e41d547de839451356988a36be1b47edfd5c9341171cc005 -generate_signature f2f674fe63b53dbeddd1966c2b3a3fc7d647f63190bd0a183908f782ee65b60b b75b075e9ce254f9efe7baf09897c5f58263397a0cedd458ed8c123a552e08c8 5f2ef89fa9956057bbd7654f09ce75f82188214ad1c75d8a1edb1f954689310b 7b01e326c3253e9c417ee74763584a12e1f6810f567c33fb4ccdd9c5af22b60b60be5d7c62d53afdd3525abd04aadebd8655a345f7ed65382a2e3ffa628ed909 -generate_signature 98e1472b88f2580a68a4b10b0634bfdd859590805b67e9b2cf8b76be25c57942 1edb8eededb14db7343fc79bba30422af90e92c739492ba1abca24a33808668f a8e7bac4b9bedd6cf89f709fc97a5d9c7180cf8cfe36fea26f3931192366d200 0222e379d7f51d6c4fea45f8d4191c586e983bc5f428e6665a62f93cf160ef00460115207f0c3650c942bb7d66846df8f2b520e644fd47098cc182a1e557e200 -generate_signature 1ad499a52576270197f365c0268892d44b73e9130f70c5fced7746498ef531c8 2394d9a89125d99e13502169301dedda64cb38509465e972b6f20c7f098d851b fe04a7d2a3e4e8c2a870c774483ef0b122b8c01813914e59042fbb76a59a3a07 770f748be4ff75c8bd2ede1677d97e69dfa451942e1cb023c912034322d6d507a53a3c3c794b3fbdbc60a727f4498220e1fb88d4124c81b63561c0ca6c22db0c -generate_signature 43038508a053508376f5d0595bd8cba556610e673b8291a322a2b9c72dd4b214 4934f2132f6ca4a2aaebe35ee547f935fcb28588e8061d7da267ae0954bcc987 041a55efcb7dd92f6fd6c2c2ae8c93af8d6248a27cf51c3d3770a0712783ff02 bcd9b147bcd926b86b86457a5a621b159a0ca9fadf3417a02fbec9c3f22471022de2c6ce6f3a2c06a2214dc053503a0b533ddadf3431932cccec0953b426c50f -generate_signature 4f9141ce176f61503a42ad71e3713b8a52dc56505809c8d6d98baabafc4e9910 516aba422fc25e0533ff24157c263ec3a284e1dcea2a8a04972b00c29df0dea0 23a237bff637b3008b429465d3223cf820ca4db0d75084831ab81808d4878006 d35b73ff85fe969f151102483096463d246972b33ce5869471fda9a25b456b06d3fc4c2d17f4d10acc4f09d0d53d0ae1acb3aa614c394b12c06c716d6b4bce0d -generate_signature 13dd60b4ae30f8146fdc6a54eb8018493742413dfd267f18bf0d21b380dcba18 8b85dd2808f4a567bbe3bc67bd11209c98fdd6d177678afe6cbc6fff19fa2d76 396068b93693fcc4afac343f8955a705d3fdcec131495c1b131e0ee9fc7ff80c a421b80de01776d82ea32a82de2fc03d5447f54bd2e26f3236b0a363d08be10def72f057b4452b4cfb365d32ae394f2f3b6bc3ef158ba8a19890077e7393b205 -generate_signature 72b57bd7a1dc353f62cad2299a225484014343a40ce052f66ef2bdd581f3d5c5 57427bb8fc70228d84e191047948c9bcd9582c8f1b3118f56064eda938857a33 daf6de1663b64ca4fb7660058f16926e1fa33175f298249183c6c8d862712d08 fedf7c31efbdc6ec1203e73fb32df97dbc9847a5ee8fce0d5b72d240f038d00ee7500d00c3fd230eb6a35c77fc1212c4ee95cada8f900f6f1f27254f82cec70d -generate_signature 0e5824738082572ccd6c9ac2b97e37412b432a6bb4a1101234bf77d2a2a1109b 9ddbc2b910f572818af16baf273026b8cac3e6b7f2fa540a9bec2b38d2020dcd 3f4b0832a84cebc9377eadef2fc78a62f6f32576258c8d2424d7dc7669213d08 9094cf5be4120f21fc861d49fca07ccc76039c1c3487bfc55680a228a597000aab5e7612ad2ad5bf626f4476514bcdd510b4faa5f501ea84d1964adce4cf8a01 -generate_signature 459f98423e876494700752609232d068596b762ef006c299750cb3ff573b0104 c2fc73baa1d5897f75668116cab853bf359f156e24b19a745966512f6835bae8 9ae7c3212e6695e0f5668d062b2291b27bc45affa1f77b9bd65951914a842d05 3fe60cfa8e3910025e9ab62a44b4966f37b2b23994155f36380522c5d15e92008d18a15846d2ba682f6cd903bd0408544e6f5235436399c7aefa18b70b599206 -generate_signature 6d176d259af7ee75ad769376ea2a5ab9accd9a9f4d878192759f5dadec194f35 2317f20756c1462f4d69d36275fc14958419769167eb526fe6a4f3007036d474 1bb97762a71f9aa32b2f7733f1fe1e0bfe8e654ee6f8361f83a61a22f32d5101 a762be3847061986f5255fc0c704f9adda6de7257609010d0a75729ea5df3508def8eefd24d4f87b7914f792769034d5f0c21863b969aee91a116f4190d5710f -generate_signature 88989b9365a33a6a6870f4978d8a68c2b310f7f6f6bcc3c7342fee9a2985400b 09ff1ab9622a87b66f39804ce7b84c1620456e83bd2ac9fce8cf3c8564170543 f3cf70e5732cf0b4fbf81f78e95bddd0166aa597c2a932ca16d9e90a2742c80c b21fd534079cf7886f5d4ce8da689c1d1a7b65a69bd4c35b4b2757590b01570798cf76438e3042093cd0407a7e8408c43c123500663570a7ef462564e92d3e02 -generate_signature 72f08a3499045d8e2a14b4872a89a7b53756fd3dc0e538ece7093168c6f88b06 26ebd8fc7bf1a0c6c1c8f55f701d66411ce6d2d2a70f87c2a5ca42e0f8c8b359 75c532399d4cd4475ab2714f0da1f6b1f131ce4f18144b69c99259c58b5ac203 b845886d1518eddeb80b5a3c9e5feecb75a39f6281eac2080df8a7daca625605c54bf951eb03ad383ebf5b14eaf7497aea70946859ec2fc5b259c610fbff080e -generate_signature 9dcccae9215809a2a124f8a3bf62b5bbdd0ebe6d7710586c5355c7a7283b0e43 cfe96abf54cf0ab6895ee0a7c9fb90ec9ccd654fd526c36d2370398cfb7e8f6c 911e17b1af1be9f9446dda5d6d03de66c64d0a785b60a03ab7b26e2068c77101 ae0dcd705b5780a4bcd9de74ac43597bf21cfba8af806e309515b59fc7723a0634e1025fda1daea9e6e128f99accc3b2ad45e5ab19c9949f7752ec9352435205 -generate_signature 841ac5bce833dbb72069a88c959cc59561e7f05baaf63bb7fdc98a4d6e1dbeaa f3e8bb769efab6217972ced819551c79f29bc32a131a2ba05ab26c33d62c6ebd 858dde600179ebea9ab1806ab8e41ed2abb320ba593faea28eafbb5a7f989206 8425e70e5e6c1ab947c4a485aaee02a348eea7bb29ce0562e59d3866c57115009bbed9542da4b6900bf99c289ee951600ed658dabbaa13a498d8d66546f9310c -generate_signature c570dde787ce9fcc3d48c5ada9054346455ab9a11a1d65b0eb223547bd39d7d6 426ccc3e1c17db41bb5c975d08279f1451c00976216271c9bf221910ad1eb8c6 4979107437a699a931e67dc36312a1c0b5b5edd3a60c1b3dee3c12596e500107 c1b3396f25461e454361132520393f44d95c6dd73960b63dba2ade304eb9a80df26fcb5debf7e6ffdf1105a9e32b0958e6f5e3ba5fd1ffc439a07469e76a260f -generate_signature 62e8187f216da0c992208f3f7afde4e5ee42e0c09ed9897b010430170bb87350 91eb9815ec0a5d2d2b98bde3a097db217a922089e11c4b15d9e4109b6c534b56 4d2234323011d6837889e21b7482c69925eb483ba5b367ca48f5360cd92ce708 63aa181566c4f76e7b4111c53cf681993a0769527e31378ee5542d2b367532038098ff87c797476d9fa189b87e62c0477a23f40b08cf492b8e07de5131334505 -generate_signature 49d7e10bd4a42988f7b202cce474c577619f9bea53a7914de4d295b8529aec80 2daf7743268523b94442ce3f0be3910f40771a4f18a22b52f3132b6b5ab2b3cd 5e6e1c05e2e836a936e17a1765b30165f3851ab56ca1e38ac459c8b7a81a2004 c4be0fcc508b840ab2a16b08817731d3c525844ef4ac6696455e983fc5bbd40492322dd10070df82a5ed703046a49762753d353a5a1dea0553ab93d354a6b40e -generate_signature 51c27e54ec56e597dedeb33c6f2fe379383a8df301688f95e37fb695ce64b1e0 01487d5868e86da7cbfbf00ae1bdf8faf8d51a40c5be011a1ef11a2734616a38 e844859d1b9a5328533fb41383c1ed108a110e6413c044cfec538975bddc8405 9a3e8b8a7b675ac6d819e4306ddb84f4255839a8290c7e422502d49429deb9083b86ea785e1869a838615fe1b2db2760300ddeaa7ea9c0d16bb1c63256caa60c -generate_signature a26bbb54e14b7f7587fdf8b0c308bb80e3bd626c20a6980cc08b9f25317aa00a f248b297c284ef1114629819876d311bc01926a75899ab339891b4270ffaadf0 7c7af96309f390370a9a5031b9bd524936233f5f806b9c45f3821b91da8b8706 6c729754f7623395fdcd8c9191be74389c2dc1da74d635d865e259fba95e6e0f75280fd8baae9ed059684d9027c014b4afbbd3b63a4ddf2c23c5890c5aabe70d -generate_signature 18937794c31d063933d48dde3fe7b5c6709f931e139fdd3cbae74e68abfc5cfa 4abf6a6c47126af111ab9761d8136bbbefef57cc6eb6ae4693803ac915351fc6 4acac844a196b1754f42f9af15c6aa9893bd00b5f466fb691ef396149171ca00 891700e4091deba80791d3185c753fa6d73582b63c9a387aa394496fd218610c35bdd31b077158a17fc0ca4d8ae17c13a65ad2dd9c7ba2a0719f19be3e5a3208 -generate_signature 0b32c806869d8b25e120317582013a95c995f9347814e4e37f6b948b4ba1e2f2 47397e6dbce5916e87dff2a55c63e49d1bdc826d4ead31401d47cc0a37d99352 c852e5ba498eb8b9686557309591b1e9b0533a07c97bc6a8186c036bee2f420f 178f1d4db9021615d689f5d236c0166a52d92b7cde2e06eefb722f171b315b0a168eccf7cae21e3255598aab5745d2c96d2a14e5b69ca3cb5171a376ca94c500 -generate_signature bb5e240d1bc7e986e6e929dea3095830fa11b51fefae6cb1bdc8e060775905e3 7ae97231c99afe3dcd855aa587f1acf5f62ea0c61cba1e0c14b0fe4fd26a7b1c 25cb0101d7914aa49cd52abc533a7cdc00f174ba107cdd6788a1f64ff0ae390c 4338d69b5a05ac3f1ab190d86bfafb8bb5afbd4dad0f01eda6647b7d00937f050443aafd80335e53d593fcd0efb35c247e35e5e22dfa9c8c5098da1e4a5e6b0b -generate_signature 30d230c4ebda0c60892e470dc95ec1152b00c8176c6a93ca73b3057cc7c1a494 20200fa0969e8ecccde73758c864cd4079a9efc8476776755390b9a56800be07 4cce1e28f0c62eb772138560e232b27c44ec7ecb7081bfbe07de7d7281c3da04 7cd4b400f8e8e586fd136a9ec2d0a38de45ad2b4df62c0c75d110ccdfd6d330e1d9c2d6707abf2a95a960b5b18f61f950e12e06ac96591e21a82ff2ab4a5470c -generate_signature f1dabb3ce43a519e5899d465abc054635e9fb30f3d49dbacf2dbb54725041d61 2be7da84b3e47473568338ddf79a8c87b22f29ef4295ae6c6ca463df3102278e 8831f066a87841b2beaed0f20b93da8cc730e303bd135ca540511c6f697f7f04 b0ebe3b9ae5b0f38e9aee0f38ddc73380a09e3464d065628679c1d8ae31d650e52cbc634317ad21c20831e41a66ddb9354d6d4678ad245d584a670ee2c921f0e -generate_signature 5d60c456bf82803cb3830fa2de437c377f5f0ce42b762a0d2a5847b95d6d4400 38aa6f868aba852ee5f2c2444212088ca48268451ad0bdf5b42d020db2eab246 208ca6972527da0e4333b6d756e5c3f336d70f1af86ee8088b050e73dd51810c 0f4cf7ac3ebd37d543bd4280959e4bdcd10237ca5655001462e7e1af7077c80be081a8480114caf1ad748c757e1fd87b45a4e76ca84bb8f910dbf6c7d4a7e303 -generate_signature 58bf45b4eb8cdd324173894cbb55511bc69ab649e1dc79311bbfa58c6c0f9275 a429e66f32ec3d35fa825eb5b6357f4daaffcac3a11c47ceb80ef1114a06a217 c3330d61f993dd6652cba521461f4280d83330119a6ae92681b708ab32418904 16004cfe01f44d8d5ba6f286de0fbdf1bee75008bfa19c0b6cefdd50d9f5f201f01d80ac93210791e99927dc64c400ebecc3dcff83bea30c66a5b3149ffd260d -generate_signature 2229a90c79f0e3dc230cc3919066d3e4b385ec53b0559f7ca8b611ee2f29e12d 83d93b52f0df0fcfb3d6e2b13ad01ef38c494090c18014296eed91a5d988b2ac 3e286618640c874daa6e36dfb01b5e2d1f5d77b8d9ca6f5ae5b7ebe659b46709 52f15398b14700c6a20fa88aa69e73970bd5a7e6dffe41edac06a0cd5cef620fb02a8aef130066026f91f07be9db30ea16585d40870ea2bba0294da3b045f409 -generate_signature 9e47f640aaee7ab2e09c8fc12a32a531768258c5135eb8e521965d8708eff2e8 a8411a8de998e4b6be239be96460ec6d4f4a8b616b94f77347838212ce4b8613 cc4598110b1aff2282997a410e7cbd837e12a98461a6c6ffc307b77a323f0b06 a1849d95dd1a93ef3c1657c0795af350e02868456018a681674c9607317f3f0bb6c546151451815b555712a6a5b4ac0f919c130a97bd2ffd5c98d0e26bb61904 -generate_signature 1fbd47cacc32270ecfe0bda8b1b1bd9ae4e03a794016a2696eab9cb0ce2c40dc 41d8ee40da7c75784b464f1b2fe89409f898fc57f395b46ed0038e98c184aba2 ce061c022ad0ad3125b6cc34b0d27291874d29575b10e1d44528ded224b1500d 4dbb38e46e6cbb8af09d4adbd3705675e0914a1ada643809ff5b8612606dde0514787ab50dcfbd27f5e27786831508a8ae5217dca41de53b87208a092c258302 -generate_signature 0756833a04e57930ad436ed6e50995529036c5b55db3f9ef070a000ea7b3b668 ef541d76406b4b71299d45937ffac3a4d284b61faa8644775e346ec6f6eda51f efe31b98bdb433b3535e792aafb0373d66952196bf564dee8c02ca0dabeee40b 8ea8ddc00b50317b34ab166c50065378112220576f75977528b4875466df23089f467ef93f5dd171797c4c726dee90f36255b8413a068d581b9bad542e1d2b08 -generate_signature c5d9d4483c4b9b23e21c259fa19136f0c69cf2cee896945510e95141ffbb195d 39e841d4ac6335babb18e8d9e47e1c75111639709335a1310db0ce2edf686871 6a2b88bfe8a66655b29e20294d715cccec610efa3503cafcbcb5323d8e618400 0a7b4105826aa2d590d4ecaf30e81cbbd4ec3ad5e39136f3b914c826cfa21702e31071c8ec5e57cf02c769088f1ff0cf26784354a17bc5090261365e7c1a0a07 -generate_signature 8bc9f9c21660ee20ffa666e7be110d8accd2d1be0550aa5bb3b4ae905418b060 14b57372138f3995b35fbb964ed0481969684f0338470a637e183b9780191372 85465783ede0e697db9aedcaba7f2aad3f4fda534db37b9654a3325dd4361302 24e9a54b859358b6491c6938623adb62cdbde74e413083cfcaa54d0dea46aa0b5a7c2b5058fba87928f76c6e754956d3c871a2c0f6f0c31116a6cb8021d22206 -generate_signature 7ed3d902f3ee03f81674c4452bb1cf33c0601fd27e25d5aed0b618b55aaf7167 458fa36ccf56bcf966910083e625c7efd4114daee53c45f288ec2cddeb444aae 3a0e8fec3301b4906383419798381d28a2a26e9249a58097734dfde9b9be370d 0939222f3b92ab882cd3baef26407f95a4f97884ea09dc9ab76658ffb4d16208256bdc8fe3ff36ad6d6ca0ec2d3f384d28aa993b2f2b007003c24880ecb8050c -generate_signature d95b625b3397e7726956f869e61df3634ee46b08a914a6a0b10b9e427a9179da f2cc630788dd65572d325ce0895de974fa3d56e07717ce2776ea51a32a542644 921c6a6d736c246dc6bef590f359f7093b32d56b05cd8639c7e94541caa2a504 5280282f8d8bb3e1a5588de1c9b9203bce905ed4efd52694a59864068ec897099a0e1e3dfa614450ce5953f7059777602381b5f9b673d380f4b3b8c63fcae80c -generate_signature d17c7a1189954c38343e20e2b8e8a290e294753072895948d184cf0d1c3193c1 2cfe22dd27f8b7041cddd1b8111a29a1c56b35989aabe9edafab342a484f372c 9317e035d8bc4a451e52ff94e5594885823ad2cdc7eedf66c5231a4f81bddd0b 54922b34b23a86c30f75ba43485179b3ceeb50794df52e5bcf6030fd5557b505781b9669848792282ec6642c93566f4afc9a6673a47c825bcc62a785b9b9cc06 -generate_signature 9387ebacbf7d29655f65513d886d006af10a8c75a11cbcfef8f530a72fbfc972 006e6a957483b2f082fae65ffd18fd525bdff0d774dc20ca0b48b9cd8f592b2d 811da01862356cb02275a0c7878185670732b10c7de15ce1b2dc9ace1dbd8e09 ec0e4197e71038dbe2ab542cc23436af93d6a10a0f78652f00aa3a9be3ef560cbbcd8d1edc513788b57264880e6d8b07f626655ea0f34aa4149a3ca3f1ce2d02 -generate_signature 307a4932ef8f15539d70e8721dfa1a4142ba4ed7b2a734f8fb37ffd31f72accc e48b91027d713f4e5ec7d0fe315589b36d6bc6ec338e218f31eeea8862209922 0fc991a39d51dbfb559047a95078e8cee3210760eb9c0ca860e5fa48e267be01 7a95d2186d09b5fc3812e03edaad6f94a274ac48e98734a9e5886f343ac4990efeb465e6ef476371d2219147de64c656a3abf3e5c1aa3cf7af752f61625d0709 -generate_signature 4366484aed2052c6c28d935b2300b5a4e5172bd79d96a37393df3584c0020b2c 39ed6f8df7c2d04ca843e2e76bac2c6a4de490f9c8d1c4ff16b70df13588a4f0 de879609d955fb6ddb4b0609fabb4e49fbc7ec017952681f911552eac8ed2d0a f9964381f05d3f1cb4410307d353b4594ab4b5a067b3a83ec025513191ff48098831fc7fda4abb112932fb58325c2d03b9aff51d5232cde9562c2b49cdec7801 -generate_signature aeac4a1e1552df635de365394f86621ee2f21f7561a71a63c07fffd53fdbdc0f a6a9623f5791d0194199f52ea1e4c77be18a94541e9fc8d86b591ac8f1c0c3a1 c4ffa6e43fad2e9e558118f7b5a1cd2196453f91e1aa3691013209c7f7bfd40b 36ab079f39687356743588b86a89ae7895ad7f9096d0f3e087defa925a16bb0afd1c2e9d9b57571049f4e36840e03424629c256de047cded04355797fc6d3301 -generate_signature ed905056a502fe94edd1c28b3f65491330b58a1e2563dbb8d8369651a2b30cb1 fe89f71bf6d52874eb89f234baff8c878ccd22c5c614ad18eac64f1d49ee00db 9826bb63674eb73980eab3d333d90b1ecdd18a227a34e729ac4aa4c679570708 d93edc001d061a215ffeebc9323af12ebc5dfcd0b8ebfc102255974a5d142703fe494c5ff9495d6b2a0f194898295e50ffcf737fb73111c081d278ce08478d09 -generate_signature 327024bcbf89837e82f4af135ab5bbbae710d4fdbfd771423dba6d089d62ecbf 089a1ffc8c9ee8b28e3181694127146615db3ca57d4f808e375b0844d68e4ba7 9c7c12aa4310db31790ed6c9041b711f0c742fe3c903e3093e2589de9279130c 54ea465f4a54dc5233e39ea5e7af2539faeaaecd522dba9270d0c1f1b4cb54025938666791141451fa3e69690b3275cf792e6dd38329768b308d5c59f6bffd0f -generate_signature 5fca639815dc14114fa452346ca15db70f4f3c13a1bd1dd2e5cc6633bd884a97 0acae623f287aa3b9758a36ebef615ce9aaa16f619d3cf56b7ceb515e2ea5c27 11569cbb68a9b17a1ac43c5b6b87a9c94e269ac507541b87accb5681fda30a04 03ab406ff16709a103b4f9afb962881c9433710bac9b5ebd9b1b456127ad86073e4849debc4460ef38991f48769e98904b4f11f9b87cd380ba5406c14214250b -generate_signature 0f217303b1076cd895641ff1751d2d469def5e140e0d61dc1a4239c3decdb382 d0cdd348b338fd12dd2602b2d5a872e820e1d9cd6f0b2df9c5c4a04e49559a62 fad9edd7ad17647a02d8f2077b9ff3c0fc755e5eb071165bf902654e7ff1050c c03672ba03f28a92adf35c74412b704b6b4a4674110b2984bd8d869fcf14450b6dc3f7efafafefb1177b80831e9f4621638b4721c199a2db6d14cf04344e1900 -generate_signature 254a879d64f8532e5f281149b52a68bcd96c2148f33e3bf7cabb4dfc3450043d db123979ed900409c0090c74705fc059ff72c4e1ed05656b117327e7d3f68d4a e39e1a551efa30ce0b2461796147ebd86394c8c0abcd8fa8118fbd7cb5f7920f ce3f68b5a6d7b779fce51f68003b90bb4fbb6b1e204c269f425ba62a00686a0bbb2200e3e711592e8239ebba26e17d2a2a0adf21220a84209bc24cedeaa9b80e -generate_signature e65ec30c477ec561fd8dfcdd1a3b89c099ee7c415de5e780618433c3209b9824 e45c9e7b6341e47a07e794556c9cf91869cb97291fecabe4867e036060c81ed5 860e36d4b74b80737704a4db0ac58e4181d807b6a0ccb4064dee200de67ae603 44a375d193dedacba4b04a25987c3120cff9cec824481d9a79954766166a6905a147c163a6eeb674278dd3768f55a57b29be2f784baa82c71706876442270104 -generate_signature e701f76a8a06647e5e10c677a9fc17ac37c628580bccc5500f34381536b88b9f 1bfe84084ebb54bfe97e3701eb27cd77e585f510270dff9b22aec8a688d0512c c7d74b95fddc2dcfbfa0ef72aa1078f62b786f5c0d0650782a2913b5a22ca609 47ba4aa05b4ef3642cc45cb89ebf9aa98adae0e223feb56f2bd7d889c1a6f30850fd39e724d3d96ede7218f35f862ad276bf021ea61fd716711f7df0fb597305 -generate_signature f220bdb877fbf2bad58a7e98cdaffc0047f8d4512cf884e45c484d5b2a19275c c37d89b27a7449d9015b93669810f35faf07908d5fbf4d93ebceed9c1021770d e4692ca12eccebdd039f9d234a083b49a184b7d241025e4b27d022fb9f46c802 d93a98d75a69da5ed543c128ca0a6f0336ed5ab2e928b97efe4631a1232caa024822d96c71feba3483d289845ead4b1f4ffa8123cdc9ee8e1568208003f7e707 -generate_signature 2de9420d201089ef5c3c1fdc2ecb69446de59b44b84813d4f2c60c4762cba262 ea272022926e4b6059728e63ea0530c064280312e22e82419a5463b34b5d223a eef8d695f5b62c90c77bfd4a4e257de3defea7eb93cd546db2f0c448deb08d0f e49801c901643f91af6c0f0e5b77b3f4b8b706a9e5e7cf6259accc6d86b2bb0efb08c8bd57d99cc5277b6913d92782a35be89c6753e4a9425cc99c9c65ce6b0c -generate_signature d22270489687f1aefc08d45cb567859a5a7f0fc6bb4346ff56be386f3c64ba68 b231b42bafbff5286ccf0828fa423487e3edfafdb3cf84b4333b92f7c5f8e005 3d6b4f25367114c0fd1922209e6d6ddb062c959f0643d6b5f5a54aa7a8dd850a 475b0d6305af0ef4e0cc3f45e71b2d0792b4aafc6c0c007d03b48e8abcf36308a3ca8844b111cab85f49fe0812fc10522a1db62cb0f126c955508ec88ab5ed00 -generate_signature 0f80ee01c68b1c0fa0f72a6890a523e2f941a934d5aea5a37756fc9308e3d149 40485d2af8f529aad1cdce430971bf593c40ea5f650c65d24ef2f3a1d6e09844 ba73077ab95f6b2185b68884e757c95ff288af975d70f54e6ddf75446b59ef0b 261d88446b54f13867b0ff5424fd5a74aed5ca239b9d53ab6c68a1c1c772f30a5cc3ab0c02e6a05c226f33d6c8f5653fba3499ac27818ef70ec2ff339b1ece0d -generate_signature fb9279828f9075e43491950ee0a6b55f2740a03e2803e20e9baf42a71845db16 a60c8215f4c3144d397db32359b1638a94bd48efb4921488be9bb2dbac2d3621 24d0feecf4a09080ce8cfd390619e0f0c9185278b712b9a22f23fe6d1b028105 6bf9a38e81fddeb3049ba90e263afd4ad6566d78f161614eb5f37efcd713570fba72e52514fdf7519c63b912cd6ac9c613de097b25df9b0dbf59c081a0cb0a0e -generate_signature 1853a3886d3421c884701015146416e2372be5dff4804a87c367d69a14ce1674 a469f283112cb9e1247cb12358e1d1f0c2c1efeeb0311b4921a4ec3c2fa6149b ec7f0053fb49ae322026d8dc884d02e4784fef17469b85415f4bb269f833410c 3102d52f2bad13c1296c3c1271c2a40a3f327534d5a94ff6340b8e1eec35b40bb54724c6b92ae4d43ccb2a7f91711653563e3f4cd29431f8d575fb246551970c -generate_signature 87a4d1813017c757f2fc7adbb6346bc711619edaa8531378e25686bdde9b54fc 8e1cf8a065ebb111aeaee0458198b02e9df18e357e60604aeaa62f0e7e81fb47 d75a74c80860cb58614643bde56d7d0793c57750af954f4fe949e08d73e14402 0acf181298846ec59a8452c54882763ffc6adc5cce0c165af559b77d4523e5049fd9fc62c6cae47e24a1a400e745df419b4435e5ffdfc3c8732e8011b2950201 -generate_signature 36332e7b2431dd072a158803b24744d01b69f096aee41407d70ccebda99984db f37969dd37feb2205fa2d7c87d62bba58a675b9ee08caabd2496886059a5aee4 fce10979cba19bc179dadec71cdbf640bb3fcdd3afd07dc0e61124d293ae7209 f65e8762a0dd238f771c37cf41e896cb86ceec9c80636bdbbd9099854e468a06cf9a078f78dbf835b67a224a97b61ec8bfd248a5505e90b1dc0d3550bc50f602 -generate_signature 2c7db34f2f249ab08724d1a679fe855bfab2ed68228668c7e712b4760a839f63 ac948ab799b000d6125355c96a4c7c2f7918831e2cdb86c66f75a84ab3b588d8 711ee793591c685bde7f3b136751e5a39d55f0cdcdac03631ece1622a5982904 088816bb72067d7a3cd24e654713dbad7c48cce15d484f27833ed29133bcf10f5ed1af0894411ce92b38c443475e7be2a3f3e409faa563419e9b4c8d369fcc00 -generate_signature 9f92e54ff6d3c2033a5ce9135c8f079a93f0a7ff4c94467d856fc63c0e96fbc4 c6407e167719e2dc8e9e9d719b1ba7be2797eed2e43dd27b02e8b7affc6ba801 9a3b191d1806472ff03203d5602b9cb73d3f0262cdf56ad70d20119941f83e00 7ef02f0590bd3e4bd2d67e32a4782c19a77a48319580743480012c21a285c90c4562c7d24a4c71b6c73fa10a5331f9037fca5a35915998f7ea693d873756610e -generate_signature a70f0611b8337641f0d45cdffc56bfaec855fabfeb6fcd3da1292206b4a03309 0baef43d5b9d89ad7572c413b531381745af1dcfd1ae90d4b20f0f78080f2ccb 9e5527c3b3da09edcebf39183c5d912e28f47befb833177726833e281748c909 fb2867fdf85a7fc9461cee077da26e3829ff31e792ca6a7a64e45eaeaf68ff03844d3493d76c14a791b8896a2536c9cbc16f3339118029543829138cf4800205 -generate_signature 878f172b5f34e0b18a55df0ca544d7dab961eed79e07fc4689a0a3be8b3ecc72 03971f351d3e118213c1762bb564a2b56e3df37d3217665f16f5193fb82f8a5d 213d506a9731bcdf5bf8c3968d89e7385ee573aba69d1572c3604cdc29e9a200 65c7143d6bc0ba476efac208e4769adb5e8f8a8aa8c89c02c00f66fc6c67dc0afe8ab0d4ab745c3158d9ab400fd5fe9da1b1b2992f0042007efc5730a3ae1c0b -generate_signature d5c32a4a81da9af917c63f72b91c14e0a8fa93413572b702cbb3e9594481deb1 b558e0724567f7b4b08318ffe6b0dd0b53cc4a876ad1530692f286b068d19f9f d5a64d1b5c47ae79629ecb770ccdf0bffb1148869360958202ddffb2b5a6340d 0671b73ca86650771e95ce5db94afd94f749a315ea513d0760fda3e436cf380ad654fb229db4be617f59e3b26d2ee2b037244a703f33415037062a95986cb700 -generate_signature b3f0024e9682ccfeb0d69b0c6d2f0911526899190e7f0fb4eb1e600da16aa63f 019ce545f3b46419d987730c551e1787ccfa158bb42a8f203631fc211e492c6c f03757ea2d5b66ba968b93e5109593e8ec7f4c6809f15c6f6d881b1f76435909 32b47e92fea38ada7278ef7ab83ec74757eb453ffa1a39add472c1a609baf707fec8964a51f6d6830afb3013d0df44f0ea70f2167f3d5de47d1641a0bd39d006 -generate_signature 0b18efa336170b3a6683dc486d1f36b6c4cbe86eb310388f71cdf70e143d0aec 987eb3e73980b221b2524179565720f451786f8de2b3407c8b5716ef683c15b4 d3db1e882a144f312d1dc307d8bdd2eaab5816804329bc36430803d23744530c fd39527b88ad639bd08f80467ac9ff846125ede2f7a422ac1d844891a7dffe0df8c3d50ddf503802d75698c4e35730f0e0c30fe0068f1d5cc9a05485419bd909 -generate_signature 69ae4cedd012737619b29be8065ede9f93b324b50252024d928ef04c543a79b3 35a734465352db4e813f76b446da1c00dc703317f1ce52c8a35a0cd571074568 ebc39ce202ec647da79c7af3ce6ddf307fc44de800a68a255c2e7dc7ff997204 ee0fb3670e6144f3d957268e011948e5d4588076d0f76d304da9825d1557200d3a69763c391e7fb53031582b5ab2b237cbc566b4b23ae067d702de4f147ebf00 -generate_signature 065fbb38e8325511ceb9aa041f32aac98a5dc8cfb06a85c98853b25cdfc47057 281b3c8844fa282b064d56acc83e92c8e6568c3a628645f63e40a07aee80aeb5 bca17611de2f9642df95a938f64894a4079ad06f842cfaa9bac087fbaca3e70c 64a81d6fbdd3736760ec9e52d6b5718b615b5ca2ce97a963597fe32ba1704c0a7a92e1f2a1ed943343f4de48f6b2cebac61652d973e74b559723831929487402 -generate_signature c13ed90a542fd070e07610a0fee13cd42f8753f3448591ef9c9f57ad721fe9d7 02cb435d82524b0d03853e29c7ec0920556b85fa12886b063cf64345dba55483 e90a0b4646cdded432d27a6204c67a76b8a3db2dfe0bb2ac6957d68e6aa72304 6e347979ae11e64140741cb9579ce9f664db155f0033169d08193c517c75b501855404a91130ea0e4dd4f718d15326b3fc390c20da9ed6c530c09eced6a7d20a -generate_signature 7badd2f8d2dd8198990738617145e90c87880b95d5c875be0cd7be0dc25e7839 240b3d620b61537d7fcbde252e9de6da442bdce4e24cf4bcb9d13b50206129d4 9d9c7c91e5d71920ff7db53015814d4360eb7e398cd6ce8233c2a1dcc54ae40a be1f20b3442f54fbd16a05f46800ea387acbd8d518642e95d75d55d585c8c30416356a60be645881df162bdfc0a0cf1ea454ed5492c467e0fd53c12b4db35d0a -generate_signature c537ab8551726c4d7ade51835585bf7850fbbc1d57f9245fe1b29618e2aa5c30 1ef9913d379ce22e9f97757c68fc5414e73a27e69a55ca223cb5eba854888d90 c3f5baf38db5121dfb177a0cac1d6bc423ee0c8f7c1323d9b691b07489006b07 0c558d33ac98a07ae757a3147040c39854db2f51bad3f3ad756ad210e53536032d3b78fcc3039c43248bf40d9db900d551f10fc434d10e83cd4b8cd04f296f0e -generate_signature 696aed47faaac314742d0e6386548c68617dee798a83105d3746747fd06d0cbf 279e1bd9754759a921c232f37d744d7b57531a302245dbb54f95259aaf923c12 28bec44d3dde67ee976fa0761ff650fb0d9ac60e74725e4c5ba3d7a0575eb90d 6000828f187aaa4b1571d40586e84b178e8fef605fe679ef34fb14a76d5d880b0b66c93ac8a30e2c39f32ae4099e69488a4f50a74cc6caddb179c0a5da822b0e -generate_signature 0215e850ee377ac137b6f9ac06809bd6cfc31918bd0a372155f28d45c65eac25 45621dd9f0fd09053bb23b178886d7f258045154ce90a9576d708f0d92ac656e 0da2f7254d9796a93c9ebb3fafe40d398726941e469c6f8deffc9c32c2b5d00f 9eaf7b47487448c11058ce0e56c9f3972652b72f92fc36659fa5a3650fc05e055f72acdb571d7894974d41f66ed1e319c44ed5cb8aa7e7bb74e4e7dd7f987602 -generate_signature 877a11a34fea5898e4643861721def338135c5cf6dfe1a6fc6777e77fc68d245 9e47630b2a88e9f86555a6a8462a435c0ac2c45bbb85115add2c4ba3dd578031 a3322e75481f1ae1eb9828f57a2b4cc1c786167aeb54547c36c936600747ac0c 05488f5a23ad7cc70be490d82bb9d476b0c841b02f9188630f765de655326b00982c27bb60142a6372b532af009c116bae1fe0105639597adfa7e1410934ef0b -generate_signature 191a17e4ab9fe35c2b1ff3190967be43353c62dba427e461cc6a2dca6b12228c c7312755cd3546fc800b0af704752069cbd24d4d132f4af435c149d1024da97c faeadb6cc780147a42bd6d1968169e088a0b5b5025171ec01fe7ce17155b7d00 cdeffb195302958760ed2d2bef9cdddc7f9c9651a9dc7f63972c75420d2c9f0b0088fbb2553481eec49d7e17ac9a94368735daf207c1e0483df31a188beabb02 -generate_signature 37bcb62ac859e2e85caa766836436533bb47f58b8748f921d3b1bbb81f9b51e4 bc46c4cd6ca4c15d76864b66439d46e8f8ccd846e899f18ad782d6a3608e0c49 28fff080636d9950ecd5dc362736734a75a72851823c7e01e7489b28c462ce06 eec9ee4812d750381e3e152c5f0c4669ba2ce39e3d6c731b3ca52e09379cca01ef63232170a386d21dea336147926f974d6493440638bc6cd6b2ac1b0f880b05 -generate_signature 42a48c4a50b87313860ea656a1df5698aec2a2f8d03e69ab24c7f9c7f7f66056 e4e4c0e5cd007712ed5120892e21b0615217230b1370f9a531192e09f5463f1f 0f98ce51f4aae0b3c5c549fac5d94fe0587e38f298354f36e6a259843322740a 0458b2f6d0e2b1bdbdbaa3b4653d0c97fdb3a821929a072905854ed994274d070305e686f66173b8d4f2f6a915de163cfcba68fff4f8d78eb4324ded1e09880f -generate_signature 2280cec561410c47170e37a38805c146ac1a8b73c1691537fdd9d10a9c430579 21ffbe4f3e0ef3bd589689c0f2c35bb702f121d83d8d1ca62554220c1da72e56 a2cb2795994ed3ff548fe707d64f59369cc6e893ee2ce9830ffa526b2f41f402 a4584714c13588b1eac0dcf9c8d73295783595b5c1c10a0255ef1e6d5dc31a07c562b89314ff7507d6fa41ac07f7e5ef5e5a7e263a655f2fe7800a950f1b700e -generate_signature 7df35cef6dfe5607c06a64a38510e5c42093c703a88d5d76dfa97b14a0f6add8 9211c6183c199f81e344d3650c92b83c96bbbacd6ae654d8644b76a21611ae07 16b3d5bc1949ecae9e4db756c129c71889214a5af2ff78aa8a479a0c5d0ff90c d7d54ada1500341157c35bed3eff082960df1fd03ca9af942753111ea2d7c3012360268ade0609c08a666b06bc7fbae65b0b9c9df232f73bc173e62590de6a09 -generate_signature 4991a192c5c493c47372771fde392802036834e9fa14ebf5c4696e40420a4c39 61c0eedb78215458d3c724551d7220f92b8f70aa656514de7fb209b26728a083 ff600a4ce0fabd6602b51e9636ff9b8b9e102f1da91f00376aa2e50411deaf08 538e575f413148ce714f379f1387616893157e9db2b07dd778dc3b1b19d8580d0f422c504192e9a001143239ffa6d279805082abdcef22a1fad40d9a66456f0f -generate_signature e9d7fa57a984dd1d9c7a93125fe69f4d408c821d7c17d199ca494d16405c0daf c536bcc2db715ae003881137da965ff0e30dbbee0f4e29d5a439547b8cd48175 1148d655f1e23fec319f04dcf5c5ddf131fc182b50e39334ec0f312e25429700 7f1a8d29ebd0c1f06abb2545a32a1caa4b99a447970938a8091dbf719a986508f2806d08397fec9c73f56268c205c28f413ffee92e6d40ebaa2030c4c4cf5005 -generate_signature 9c619840f7234b099d0995b905a15ddce999775aa1720f0bc9477bbf0c5525ab 4d8720590ddcf790a1baac573dda9ddf0efc21223335b15a23189fa67f515ea7 dee02eb9d27969ffdbe6f7c22642936e5965f3124fdbb0c51b1c44856c075a04 275fe14ee523a70ef474980cf253f07d7ed9a176a18121462d03354c219b2f09ce78ba68576e2178a77e9b910f136d3ab6f89659d6b3111fb4bceed0efc5dd0c -generate_signature 4e5d10eaeddd0acead90bb49b03e73b84e966bb05d63055031cacc8ea5e49aa3 e2c8684aab1e9860370e94d295bac00283208ebdd29910d54c9a28477daf6551 4487e8f01f0ebfd7b1870e7a1cdcd86470fe644de11161309114f120683b3509 dee91e24591faebfc4358de07267eb3260a2f39c7c82d941ddafe7e64373a202e2be0abbafe8ae48f797832ffbcd2231daf81ef1c2420ae49701067b9862940f -generate_signature 8db465446cb5848044acf5826a3c8baea057859846b551238a0536dae39ef712 f3a88b4793016d12031ffa76a5da01dfb771e9255e8de871769717199b83ccdb 3ddbbf3cf5f282afd370de5d33dbd8464b805bf4d7c74be2410eefcb5072da01 4c5f5674f6f8b597fa00bc83d44138c6249b9ae1bf6c147cba3785b9b1a576097e00e4a12811e3ff669e970cf0badf06a2b8567a4137aee88b98ee4bc998860f -generate_signature 24979bf7adb7f8b840a3524c3d3bbcb73490e50e71400632d824f420427df802 78057cddcf685984606870f37a2f0d76e4ba59d169722729695edf4934528856 fb691338b3fe0cd1a272b7ff7e363727c66689c1c7cfca7c007a5543b3abd401 cbbc150182612960da3c274d32af2f3e8bfd7e5c22fa19a2c1429e66089e43097254cb39ade82fc5842228cfeda6c89fcf46513a35c0d8e60fae55491850b103 -generate_signature 8866f2c3e0566f73f173a7e0f9e33cf8b1795fde4cb09deeb1efb17dd82ece33 28c508eeeda24bbaa55dbb312f534292bb1d8ca09cc34ceba36a3db1ae5aca9a bed8a7e6bc28ff86ed87c5f54d91f8fd381a2fa9057b6d38b095245dca1b3708 d223739d8c5d9ab33d0fb7994683407c8860a6f09ff1dcab0880a29a4f952402040f91abcd345e0412da0ad7db731bd64eb5231c84a099daf595c382797a1e0a -generate_signature 421c152f337614c5a007b4eeba8d4299f7fab313c1e4fa935048288d4b2531d3 862b1dfbfbe3934696a9ad41adf2022714feb99e5a1936ba2dd4cc08edd7166d fcc52b66e50a4761eabbbdba3fa82f2e391db31ed1be585ba5a936c31dc36400 7ac59d4a3823da486906fedb7b89a97ee188542b4ee6139335b5e4261b1a8304ed3e5c57523a6c27c3915699e0411c83e98b55e41892776b7243058812007c01 -generate_signature cd4eaba1978fc73de4114aca7dc6708308e72a8a861c96c2eaa13dd06f2e82cc 88855b172e2e512fc6ae2bb6e1252670210b17fe4c021f97ce7245ef5e0766b7 fb333a06054d22771b8422f547838f14f4b868e6c0ffc7abaf52643b36e25a0d 96f77fbad2246d441e11d77a74f68e371a5e8f4487fc2403c8ce9642d32d0d0d16a1ad913b564f2ec35789d10dab51546cad04482fc14cd144d49b38da4a480e -generate_signature 38d2cc1cc489bb0af029d12f1ed624bec71d68c0713f1fd7f5d367efe71d72d3 3b3df37e4fbb3a730b5f9c5a1f7adc45dd2fb2c05d1e141d939536b6f55065d6 f10b5d6fc5830989df088c2a1a9bdf95c37781bc8990b95beeaff246b858770b 0e74576563e2797f03483aa7264b93cb5fc9fa20c9b8fb7c8e84dbb2eec77900a29802ec12c00ce4bf681b7443807cb763289a379ff76b0840282f27ff4bb708 -generate_signature 017a5495b9f777b45cd256eb87cd298ae9d346caef147cbbb916d95a6b61c677 e8d23a7fa523a7d1d59d23f5929cdf9872ad01eb77347b076121b2a8d24289a7 d77e90cfc8bdced4c2957514ad1dd3ee2b4e841f843dc2cd711df92d31225801 ec2e112f2a9509e04290278361b2182c636b5779d038baaa4c28786bb518a60373555493b606fef1a19147ed825c46bdec673740bef177ae2eeaefe6afa58e06 -generate_signature 5cdee6559376c9ce870c83b06a6492b019ec54bd0058c2051813cf600bdf25d9 a2def558fd26b3e1b9f1d13b131a324ebffe1412ac2d043f7395f060e35f4780 29e13e332419405883c0103dbabdd28035a4be98f8216230d4a31072ffdbad0f 61fdf6abc507b614d99dc516a00b568b1fdeb9b1c11e38494e6d4dfb17ebf70a2e2ef100ea9b82856fe3cb3c440eda34c2a55e31d646568bac6a8aa15eb07903 -generate_signature e5f7e794c78196616415dad78d3d07439809df7c7443e44b82e54c6faa5bacc8 6fb3a5c5d7b694b63f29ebaafa2cb101d455d95d1b30c51d2027714c6048d81b ce2794dac0a5fab2074829050ff0f631ed916adb97c110d1d9c01f11fc572005 89c614040b6f140e0e5a245ca46e1037cd52d2bde37966cb6157d6a9a6acab06b051f746f8a75b6a5f4baab29264f4483ca81ca49153a02956640c20518f540f -generate_signature 5283cc05c1a813496774fe331d90989baca3a8932ed5d6ca1633a96e9881deb0 c776290262741f162cc11cd2c6ac606330a0c53fa543cadf061d844ef306920b 7012ea2ec897d4db791c283cb2f878103fb6e29c831549f2c11fb306a3f33301 9b468682a0c83686e4e73beb7538d7a0df6a50f651e6e579eae18b707ad65000e99962217bfb21b0351e08b09eb13056bc172c9ee8887658fb06d738b06bb10d -generate_signature c7769cf2307480649f60e6b97930ec24d3ad128375546675b9da306de670ab0c 3cedc8256aa3d7844139e180826e005f8619adfa8a493da9e824181e4cc007ec 4514bdaf0b277950569d81b5195c63f14fdaadf1156dbb3ee16f3aebe2cccb09 3a862ba6ed9f70b1e1dcaa0b2cca4688c36f6724afe2ae3389e8ed8b94d6ce0acada69f4f6684699906456c157eab26d4970c9bdf06d9083ea3016d955a7de02 -generate_signature 0020398fa58108e0b0e5356b87113093b1e657fc5aaa229a2cbed766a1343b13 dbda7c364135fe9237e54e87c742a1174d17d3e70a08051780ebe274e399a4cb 12160063e4e47b248ec37339a3e62e920b41da6ed6b9ef1dfcc1f48a17c0490c 15189616f9824f7156e65f6fba5a919152017d62a8764234094bc10fc0e3890354e04208aabfb98c06aae907ff53c72733665f5b94d036b3b98a57f272da9a04 -generate_signature b1e43d4e2cadb0e60c8791fe13909d0226ddc2fbc5bd25f73466910ed0ab3f2e a1926c9a221e847a653ec6136e4e23768ee329db20a7a5986b51c0b35eb73d15 5b0d93ac7c30cb138102c03ca6b27291e5adf76d82bc88862bcb3af8df86d308 bf17905747bb2f99f89708413dbb1caeecd29cd73f0f46d24b79c13fa09a9c0a2e4638481623cbec52f1986318c640017f22045bd00c2b2d27c66963bc34ac00 -generate_signature c5e0e0a1d97e19628730750589a954c02102545c18f7bb05ef3e9eb8cbe36523 e4a4af78733fe9a3b297bf135e8b57181d8c0e39ecf9a5a7ce1898db57b18b17 7d29486ba52c61fc5ba24e42ae6f1861f161a0063f102ae3e86c480b52d5a40a f95c29cbd0ca78b63e09a77cc7b9801b775e347bcbb4f1e93b3d2f9d39b3fa004f2be67ee13077527fbb8fc0f4c2a8c9ff8db421917b7ce13f7d5ca43c4feb09 -generate_signature 81bb454381f1614e7e5764e88664dc47f825d18ed5710899d1e5968169d7bf58 1bce52512bbb39ddb4dbe9a10cd12314ce08fb7ca6b20d8a0855b334b5416a17 8fcbfbb6f711655ccec1b456597b0ce4a5c81adfe9523721503d36f5d5be5708 e14bfdebd4d7ea7bafc2467872e8a6a39c65f473598d1baf468dc14d6f56330940153f68e296f8579248c1c91371f466cedc69e48a69b324e038252a082d6103 -generate_signature 8c9949bde07909984cec9d462c7e1de28f0d47d143790a686bee51d4983556dd fb6fb90c091135388023524466045bfca011113099973ceaf2d6dc4fa0aafbbe 8dd9d958ef607e46a14f742e8581c903661935e2257f24e852ced3f7edc5ca0e 2bfdc1a161b5277c7b18ead751e5e3db524857247642ff0c4927218395c8fa0b60cfe294e004eaf429a3cc1e6990cc90722bedbcbb47c72ade76d6811e68f20a -generate_signature ab5556f1489abbd7a0c8c877a18313617887be7ba186a8e0a2d50e9d94a578c8 e7f84cf6411ca6bba208b07ab26e48a09b779a1e7bc907ed587a1d7ebc39e273 601f584a65a16dd59e6a1b86310a66a11192cd5ee68a4943ec38bbbb1bb9370f 937c72108a1f2c680b5a1bf88696ba4d36d94c3f929e645c177ed9370b6eee03e4fd662ccced32ee38f1e6a9ccdabd282e7f6146aac75a09c09b31f4a69acc04 -generate_signature 47fb94c340262e3cab3ffb03e06695bfbdc2439807cf26620c8cfe4395391309 a5f429dd825be9eb1b76f7300754a454844d07d1627312b14c70a4ea4e4566ab 7747b7704dbf86b1d338f3d49649e43009e7525edb11e684294fdf1d6fbfb708 1ca22676df041fab4c2471184ac3e10903c26c03340517b54fb2014d45aa970e4d38f193f768d15d38b1516c56f713ab80617660ab14218c41f252d950e9740c -generate_signature 6ba24acafdbcf7ca8125c560f585d33fc166ae363adc8f0422c2242f6a2adad0 b27456386a01d84300fe0695bcb594296fedc0b2ea31e1a176cfa49eafa0e5a5 237401f9b747baaabeb8d8df6f526f00a6331288118c5aceb343908fea8ff708 7afd863804c60d1bde37837a7435eed6e888268aa6fb4bd8e2672c9278f3b208e883ed4f080a35e9bd21bc808b7148ca63c00038cb3140160aa32e2cbb530407 -generate_signature c8dc5ec96cda8c740358be0754fdb8644a33bbdb9352ba63ba8c8ebb19f7a240 d9041763f43939ba1b6015c369bf8596a53584891e45b20f4031ecbeff20b36d 849e6afd48fbab23161a1dfb7d3b60151dbc4b288596726ac07b2fa4fab68109 0335fe7518873c6261f5e903fdaaad29663089cb9576f9adc3581d2c9ddbae099c75901b24c6c6c9f9d4540a815040ceaa9d237545a59b6fc84154cd8c0d260b -generate_signature b6d466f81e80d56104e09dcbc4bcb94e4f095ad48c635aa14f74c106ae57b9d0 005a126a81f80baa89c897c921f562aa44afb0a7f317e2cc2988197f980fad94 08042ce99fa8ff0a4c082fc1ba66bb11f8c94ba9ae38b4f37aa050a3345b2808 ce904f0bf1eaffeb0198072bd43130dd5995d52ffa4244b2ac5a1319bf19c70db5d3690936907bd2a20a1f3d9685ccffc70885bc448e1fb9f42b70bc5721e80e -generate_signature 8c442d20262492d6075a482a853037319738a944e8ced7a26c236008348cd06a 1543c07acd90bdcc1ed42560b2129f52db09965732e1ad75da5df80f851e0a30 5abba5c682b23a2e44b9b426abcea8faecc8da15f0d124fa428cd21ab523490a 646db7f9198a8a96a69e9313ab1301f6b8e2ba82e6a4cf5b6686119bbcc8060c859a09d1e9b6ff8151cb43d00c5ff1f82a96d4e86a80d05fc38fac566131c406 -generate_signature 6f7951c7ca70cbe7abe0d6befe5a5a10559b7d05a56fba3280266361a919b07b 2a08f949c037c51b3bc036e3779b6dac628a9fa2561087d15c029de399596209 77f07519b561abd91c4bf4a10be08deb641537d7503102d30b8403bae936910b 9a1b29e8e4cb584a1cadf576bde1d39d45c9c4af99f3ed48ca0381fd05712a0a5a27fcfddfd1fed67b42b4fae7dff51fccc79868a7b52714713dfe50dc4e4e06 -generate_signature 22b1dcde0d75d009c1f37df37ed0a79e162049ac2ee48681e312244d540b046a e340d157c047d55d82817a49b51f3c1d39a82d38876a05050dc5296c12a2b6df 8fbe0766b15d4fa7542756b1cc33a62bfe2da4f7f00ca30dfcccd7d63d6a350e c4071a80eddb13d17341e19223ab4b731301d0ee69b58e98ae613bca450e3c09c5b0dbc233ddfa2cca333109cf74f909662d58a5db790c7029568fb58357350c -generate_signature e1552258578c4238b55b9c096f1da86bc399ebe8c55df9dd6d0468ce939a7695 98557f788cbd01ac0b77f5e4bd2c0aa65c58cc61b49f5842a53f898461d22ce6 7d052de95e7e055d117709ca3ffbd6294edb2b0698b549f623327a7856d18b0d 86ff6dfac2b9372d5530633568234ee713760ab511cc7c31615acf536e2c710eac1c79c1737e978f6e2335be68ddb328cf5136a9e2fd5b7e9c261470b109850d -generate_signature 9ef760bfb089f081576ef619c98fa69e80bff76db56853759a98359944af31f7 1e2022577b355dee237be685fc8704c73ec09fb448ce3ac9224c7cb26ed7cac3 e9d5ed5d17eb81635b3f44bbde735d506a1b1002fc963369db98c2da1fb9990f 4d691872669063c2a108efe35bc269e58db0a3fa80d0d791fb323d97e1519203004116a760873df160d6c6fb4a16bf0a7f22dc594d5917f9bf59532bd5573804 -generate_signature d48118e8850b94a1d6536e8ad26424db5ff6a8856837b5f2b759991cecc0ea79 76acc258e905aa60091cd19a0453c086bc63fbe6a9b9f9713c5a59e4a1f65735 e7eb8679919eec79d95612ea8d4f42d3dba03c92997a8db4ea7ee670bbda580a 66b105887f3460afc15e5bfabcab4fb30ffad9dcfe1de27021cfc4749fb8a70fea6f6ca92c9b2399ec8fb3f5d5843b9a7d13b69e2bcbff2303c7d31da93d2505 -generate_signature 95b23e89020f166b62f1a0ee717a478c07b3c96d697a3a5e1eda873c407511a8 8d7c53fca9f208f436d9b1a921daa731f79e537a6aa1f54b809aa4ad9b33b860 b702f52f1dfe951d4cbafe535089128cfdf8e3c62e95238614d230bc783fed09 4711ce87b656e3ba158dd2c7b54f4c90c3cf42e2f8d8db5317428c7a32552b0780d2f879e10b26baf38416335ced8a2c137fe802cfae343576a11c80fea0e207 -generate_signature 73017e127a3cd7c8a9f97f80ccfe6804c5bb683b22b6242a307aa84f2b2e5663 f17f920f113d54ccf8790951b2963d35165c852ed0cf7c6f5da3c1af2874f7b4 7dd05f2bed1ebd7b1bba8060fcad9368c9c114e49ff553273cdca2adbe5b7c02 b4e3f18ffb19b5ea02521aef8eecf414c5f6d399db8b8c567f749f766816b101831e6908c5ecc535682a1cff9855acf5fb082b28c866e719494c1181c8da0b02 -generate_signature 5df7c3d6ef2ed1b6140e0c52d836594ae75adfe243cddc0cc11e253674ea1d3c 5d45e0470b6d249abde6354c3db9e980c56705786c11bef17f94a31cf7d05d95 60e30994ebb7c53ad955faef54e0abf7d7aff8932af097db250fb56099712b06 dcd01dbbc6e6c768ff4eb9092012091fefe7f2dd65212ade4be19cecc7e31e0ca74b09b01ed1e267fce42128201cc71467e261fe69f5932b19c8f1ab7f14280d -generate_signature 5e1dbdf1b13c9e0be80b4f4e10a2bf29144fa2e66553e81fb50f2f7b55633f1e 4cb10093b5c9cda140ec668a576def490cf7453635bb4921cb9ef8c8e909ee52 067e27cc60b9d9da528aaa8a48ee6cb30418189c8f6bb0d5710dc1074dfbd605 41115aaf71624ee2144d7d0f0d0ddface6cae12d620fb3aaea3b6a58abd86e0a9c1bec91e4421c3b4a889bca090a118405b67693f7122f7894371d5b7a18f200 -generate_signature a08e5b2e08d5f1c70edac02d97b7174e348c6d4bbc201485245e8578efdb2f5b 4a10b74ec6c5ba4cca27a473ff6022c3618b2e3c24625708add88767da62e3f6 50262cbb867b9feef014c6d0f301838bb92b66a8f2109e67d036129096168900 2047cecb87c08ecd61efe138d887422fff6eab7aba3fd3dc5ffa34fc116c8a0aac15f8423b3f79b636539ff45550abf2b8ab49f56593fc47352ef57427c0fd01 -generate_signature 07edba63a2c0fdb2e02bb8b8e6b531d2a542b0674e24f5ed1f3ef2583d2d9eb6 8c1059bbe4a373948ede638667da04f8caf05aaafecbad08de797a804f2e65e3 abdbf74be8cba1d22bf91057a94b1f12a063acd976e4410152a07de48b290b00 e33c1c1f387cd06a311d3f8829fc0700a71008e98c9a692401267d604b00a20173191f1c8ad6aad05432cd76ac66c891cd1e6855c77d2b2420afa027a159310a -generate_signature 5dfe35eb9c70c55e285e3e9121651110e8daf329a1d3110bb342341f0b13e285 898082df3ceb5d2e96fd5400b22bc169e04d3ad581a63de9ea638eed30a1f63d 1e83a05b9ccc0daeb952e2d6b5cfdd414fca43664ed0ecdd30c5eadf7a8ad20c 793d027fbccb1243cd72667351e943e576775bce2ad9d158455983bdeb866108647b3e72499a2829b262d2f840399e4016ef6911cb36e17c598b741b55961402 -generate_signature bcface283643b5f6fa0140dafff732ff07f4345abf3770523489ab4ff2731c58 85dc41f9264298c7ad08099dd2b20682dfa4c9798e69695f9626f644ef7c7980 b23d6a04cf867f63aa2e6a26784d5c7a00afe68a37af3069248177b77b50c506 4395dbd98754e623f364ed63da090581c325a259116e7b9b40266456814e450c727e087486a7135f24d85f1d47f30959aca0a254faf472088ba6a579346e0c05 -generate_signature 79444422974fda912b29426d4fb4e8900953d14b8a04b2b48a3370fbf6e0d34d 55c8b685946d2e1720c290ff6c2bab2643e35f6edaf04476dbed56472ccfa242 eabb6c32344af64e15d351011ece8fb6cb2309ecaff2d43d819f59389bf57607 b736b2d0323f3517365e93050888145c9b62e2fbb1bc62d4216a363f2caf660257c419e6bbff0bdf4cec461717e414cd0dc66acf9c7d5a0940b9c09064591106 -generate_signature d2c081f5d9a76df2448bf6aaa021331d6e0fc87e31b6fee0bcdcfd499d2dd94e df1afbf7c09126a407fc34b118985ccf41c4e82427e6dcbfb0d3897cd3705d8a 56db60488923ad7e0575b143e71ce05d93e6d273a5bd445cf74ac9d8336c230d 6a720099dbe026e42a9ddff8334cae0b471c24f78a589fb6aed16c04da89250df3d023610cbd8368a4c7310e6508c9e3085935e77c17dd237e26c5d024502b0d -generate_signature 8153ce2a925a6437aa445072075b5cae447affcc86b452290e5aadd91e446e9d a504a848e87137e24df67b8330d7dce41efb6a3ffe99ca69d7a2775bb4ff66a3 58a649099831b7343c6800ced12204ed25408aa9b1525d2f8aef6b8a40cfa908 443f43e6ba71a442e3475b75e5b8057d2263224e5cfb04325512b78a5d6c5d02fb7555a350eacf05389a03e4402f24279a86f677908f2db58477fc3284ec3c0f -generate_signature f85e575bcd9427a761197581a05d22e3784e602cc471d88ddef5ed4e6ef26ac1 e4582873da79806344aeabdac709ce261fbeeee546d0068c3d17a64527aa1cbc 1090799dccb3d243647defbe3ae894d316d68d4539a134d8aa86f3a4116be203 ae3f5bd2451f1d0427fa3ddfd43e29952830737637eca92bc11b702e725fe204cd80aafc0a304ccc4465965304ee385032a32fdb0cb8031667f4494e25b09004 -generate_signature ac157e1a76bd72924e77dbf97e15469a4da1be6d154a8974aca1d77da7592e93 f4005a9e2341967cae9307f262d87c064163d0997d9f039a2488fb044d449604 7f8c5592166b6445aa14fa0b765d3908f8ac9af1c6de0f01544ea95c487e9c04 12cd81c5b4a26a419ebeaa4c9359921076a6f4f4ba58fb8672c3274c2eb77d0acc9b43dd9e20143b7e7b0ac7174ebe0749b0c961fa427ba34fca7daedd094b04 -generate_signature f135a8ea135688e2cf612a1517c305ba88b556d3d0f5c3cbda706bd485993c1a 5ad2fec0008abc302e993a7e03a99358e7113ad6cce435c81d440463e39fef88 5a0e0b8724a44adf7eeb27ed8cdbd6a11d9f1a00d0729f6325910ca6783dd500 18185dc79fe4c41b9ce8e5a81f8d8459c641b4bc2333c1d26da4e3d07badc4088fd440494360ae965555e6a4b802e9b455a3a618ac3bf9d787b1fde0e3606907 -generate_signature 2f37a11507b767b81357f9d03bce2d6f92cbd79af252ec737a2126e1f1a59fa0 2030a8eea7a816364dd23b81655dbd00e10edc8f40b5139cc49506db9fe12c44 907f88dde3d47b78945ba4180c53fab25024bc009aa678b34c6958c3af14a00d 813f32c4700ae51d736026ee5932ecf36ba0603c8e076fe76b51cf39c339b00cd15fc123ef72733f9a29733f303cf7192970aa192d80441f16a063cebe25ed06 -generate_signature 40f923c6bbde2b5aa8ae568a53594738edcc102ae77d2eb2ba13250bb3701aff 3d0d965e35f87612a3d53ac8b62ae4e7d4aa2f1cd3a6fe3a258ebe91457a810f f88b02b56a6d583e5ee52898fd7124270a1de3aae61ddb45e3fd4d272ca49e01 347b25ee0d085e03306e6ff10264e23311d2e5916edd915d809af46f0b4ac802e7594b45618ffd115bc9b08c28c060559bf619feef33842192a3683c04122e07 -generate_signature 3c4a9e9af77301f53f078bb5780d71d3b14e55457fb26308647706d88e7e8ec9 41a6ad8332efccc56dbcfb936c84a1e608467d9f58aad447e7cedc9c091b8f33 6ae28d2f4d72100a1638132f6c363d2d65c73f2f6e9e6ef883775ce3eee94401 8284789ac7c0c1632e78fb9fd3eb8cab8443374d6c7a99a51f59458face66609a409e5e03dc375f14760086de96d1f183ba6cfdcff5aba35519854d4b62f1a0a -generate_signature 6e361cb98046db1c58738f54bfa5e24003ad2843625089084112d3e956781da1 a40a310dbf5128eb712aa9ee5d339f8c0e78c90e8e44977ec83a6138b6191627 ce3e7483fae52df03b7c08d8f30fa43fa505f0a7948889282f5e46b0882db500 f67f9c536d25e02d55a34455ec80be6995391acc2f12f83b0e88bfb41fcb4c0236490ee6618f747414be9b17488a78594a0f2a3d7b4112798adef6be68492f08 -generate_signature ba7df37a2d75e962d42d31a91525075ac836100ed1a78ae4e978afa432a1235a b4ccbeb6c17e719c79942b74b81a3cfb5ccd63e0c06b12fa88f9e90cd9e8a1e0 a35bbaaf8d8fcf421d4307e3a52f63d382b8ff35c7f6e5f7a6eb0a7adfd02f04 039d5886e1ec0ec7d0eed7365fe0fd8cf61d24e62bd8e86cdae81130b1a4be0812ed100f2a208298a0ae742f5bf7dc52c52f54cc083a7955c5f73855d840b107 -generate_signature 81599211da2f1378330d2bf7f86e0686947fa402578cee048c6fd648a909f2b6 865b484252a23a1697c736ddf81d44674d9b51fcbaaf5bddf88061402ac65189 8509f1f20c25d821fe6057863277222b54399eba3712721c82c174d81650080f f1efa9cb98ad24b4360127092c67db3e2202d0fd4ee28c7ed2a996d674e3a4073e2b8c52b0f84e7a300e1184cd96e9299367d623c0397410b356daf6db9e3b01 -generate_signature 9655414428a87d11c0bbed9af0bb32010c4847cfdf75ebf4a8c73c1d5500f44f 53bc48c7306182d958147ed4510dc3c1e5b3ccfc16938f1a7d757ce7ca2fdeac dc1506e5b95e1d6e6e06ecceb4b8cfaa00daf3d6fa26fb672374ffeceb0cb505 55e0d362cb4370b5b6ea862a28322fe9ea8c4e5bc18bc078cfca149ba923480b476bdb7053951597784e0efec4e7f088a4e805b1ad8b1b258c10e9bc2ad8910d -generate_signature 3e1ad25eb7a1dc33c84bfb09d73d946aee497638ea639157e1eed1739bd0a84e b507cad326ce68772dd3cb43c31bdd70cd71d722bb551d209d53449c232f5055 c06773a6b15d1409ccf3bb4182509cd418fee29a93d966c511809b19eaae4d06 1b387007ff1d75ef49f4756996b38487f3d21dd5d639f5c62bb9ef1ab1e9d70bd7de58dc2d198910d3196f5554725c44befc6f43c5e89bbde88ddfb579fb2d0d -generate_signature 36bd8cd274466d762f8bd459e5f32dbd7662fed9bcb44b76276e793701c7f5dc df9013de104e242aec33250ee64a3019fb7ad37bc06a421c60ac21626c9ddd6f 6342d56599a678fca61f584facc41e316f0bf152165767f50e3346b7ec74a208 dc68d3cfac0ff48e0e056e37a27c8ef096135f17b115a5a2e94359e60b32740cfdab3e035e9ee0d922203917b312bb9ef0a8e2bbfeb9d34c3c89ee75a81cbb03 -generate_signature 829f725bd1ec12e3c244c4b0006499fbff693b33d0797c043334ec591a8d64d7 fc559b71cc887e98088cc873b9d7c69d104276e257cb28197644f49e8854e69f 36228dd0b09d05703d7f8b5235b94958439b8a95b43c8e09b577a0fe6b26a901 3dd769e6d79ed52a62a137d1af70867756fd3e140f0ae674c7441b7976fe48001cf568f0c922066b4daffd4d58e2da2897fe8df0096f3366943a36a3a28cd502 -generate_signature b2fbde52043e979fae4cbfaa1fc218bb87b87b21410c498be62c5b288f0a59d9 fa437eb7611028c6141280c056fdcdd20912575cb57d652f78034f7afaa5e0a1 df1288522dc2cbca4473941856c0f24952f69b5c9efbe6df24396ae25ea91105 35e46c633de3a5281fd4df5a84e4b55bbdb893a87518726cd3950927d056f80f3c094c3391b5cb66a253a9405038f4dc7c6d1a2dc7a104b859512ee15a30310b -generate_signature d21de662f0d58c9f746f0984fc3691a3d0bb0776e539a2282207b8ebd8e5ca85 679397579f31b061479734bc5fb2218c577503f35827c815f22bf53761c272d9 b04aca24dcc7f0c6d04c3c676a6efe8062fd5618d16155ba2a0e4d0485332c0c e38a5bb9e2ebe003172cabf2a3f6a67a2c4c1b536390f989b26f9f0a3147690f55942d4db5748ecdba45d9bad81c5c325f143575ebd3df478d0ea7ea0bc86d0d -generate_signature 44c6eecba5511adb489dfb05815e764f933553baf1acfb6e9cebe010337b24c0 a08e4416e02f056c71ea72e2eb1f8a89f669fbc225a7254e4c8d0f8d39ebd1d3 40f60e9ef735a23ffe2ac767726ae743f2844fd7e8981a96cef85096359c230b 332c0730801c2d15308856ea23883a28938284fc68f1be702370714468f03701305c5470d6b4e0f663e95362062f9d96f39099f9227f29dc3552d92829359200 -generate_signature c36bf112fe3f11d4a33a28c8f7e96e18c0a561eda487ccc7e441439405c5b339 5d764a790fb3442a21aef48510e95dac552d0f34c2b03289ab11faf21623c451 ec45f5dcff2cae290875866dcb122645d1b88cece607be63783c3e088cba410e 6018c6815387e952d1f8c42222e166f7b360d16ba78a35a27ce3bd4233ad7c012ca0cc118dd6323794252a31015a131058851a7572b5fbdabf1164aff2940504 -generate_signature e642c5bd2b451b20aab856686bab22f6987a7b5330c2491ad9d43059cb5b17a7 e5c73f7ca70afc4019ba044c177ab7ba3b57070effd788ec950e23f766617ae2 1430d7db432dbcf48c88f489017aadb6bc4a4dd30dd68245e281359e7bc6610c 71a256a377534509f1716542678d8635c0c4446fd7138c4a2d153780434d210d3d583d49e518456623198dea829e2f5803e14293a3b70c5eac349fb6f8d05409 -generate_signature 0b427120304ff23f1906c33972a5ddfabc833455e1c00b4ad522dde15f401237 45df3d7b2ff3ea6a638a360019b6998dff96ba9c11ec454fa763b75ed9e21891 07290dd0ccfcf302d33d38e5b943f338a40e63fce395f47984e7edfb617e3509 1c076bcdcdae3426a4f91bf88437b01a7ee474bd7ba086dc1f0f8c0c9a032c04bc9e326bd4471f744ef043d50aac23a526e1499e4f5b21bd1dcdba3d77c4b50e -generate_signature 5ada550217e164eb59ea6506858ae1ab537c9cc15d362f94570255e558911490 891681f48e13a2f0f2a7e189995a87f9af3f3b4aad553deae50f38455eae34ae 9f12a804897eef85dc4f89885958acdab399cea0cbb6c7bd2e2b40185bf53201 4e5464d9c965b145bbfa4ee2143105bb98f79c457953368916978d64e4d7d20bd741f24ea3e6eeacf4060272911f8116c500a4da816da186df61cbc073e8050d -generate_signature dc200cfb46b99ed1789b022df4462e679fffde969ef75181470fe77d2f659224 a8987ff64be78dc7120fa7c7caf8dad90e4b8dc44597769eefc67df636c26062 4f5a3321915e3c9922eb7704a158f64d27d32192687412d28748d2ed4636900b 621b4a89aa668bb9bc4ad9bf786bec99efb8572d27771708aac1580f18fdac054d31ce1e13c56a9cc393a269a5307fb20a389c14d7f4ee495767ee841ed7ba0c -generate_signature 7462d6b416e428b5c0ee3f90ca39575495955b720ad0e7e4c2955c1ccaae1888 36260bf759635f1628c16ab8a85e22d20d9554f6811bf2d7d2b0e724088c8567 2f37c7a26aabe14e904c61e256d5e2857adb8e94c3d28a57950bd68b5677f302 c45607b9f23ac3cbae4566df28dc0fd8e46ee8a524daba56c785cdedf446ed01754f26a9bb72b8b42421b463cbbcc8c6087b5390496ff1afd7dae9d78be1da04 -generate_signature 51e9571c4b6814a631509d7b43dda8a19fbd9c5536c3355c3b9ec28fc7d4aea8 cf237efdc47a5fbb1063f9851704177751b2bb6dd40f35a35bc49acd95e11370 a37484d7875c479c3df673457b1d82b1d8fc80df5274aae7d1aeb39fe2aa310f 08d9be6a729a27d13eedba8ff518720bde6f1867eaa23ecc2389259ddee62d08d24a6ad2e1f969161b56b1bbe9768288cd1a960f1553e72449f8508a7da7de07 -generate_signature 512f462d431403b24c18924eaeddf9d911d5c614d2a5e274c21933a136cd8e10 20f1bb98259c891bfcb5e11793ad38c2ca863d739f7a2c53bf5f74c9472b8fd6 8f92a6e203eb6d8ea988a11177aa370bf142928fb93c466bf18ab55b50492e08 73bf95a5d2587b1569df3621d582da84a5522fedb5a5794f3757847c83cf280e85f87c22a184fe5b66c4cc8f7cf9f19a1488e11e2e6698c38d8fac7e0e19cc03 -generate_signature c67ceead9a67e5bb1211538a0c96cb3605da36f1fe2a8664de0f134781a85889 25adad7475f476c9c02b9fcd3c7865d261e5da7261031256dea250c71f305bf0 8a451763e81d056b3622ff5db2432b5ac52a4565735d1f399d7fe44f409b6109 c5be7bf89c1848aeafba6946ae1019dede83ff740554e4ceb69b7859fb47b6053b5653e3bd00421fe086aa291c210f3aec1ac29a0c11724d0320b0f13dd1a008 -generate_signature 8744e27a87056f551083b1240834edfdf4576a4b8c4db7e93c03bec8f714b842 ee8c02e0eb978f310eeca705aa7a4bfe38cc10a9a92e8458f5656dbcb22a604f ebba71caa77c769b5c0920033be75dcd8e1af9a19c97c4cfad52bd52efb3cd02 2e7581480b18c06d344166ee5a79ecb5fa3039a5c9957e8068ff09a5aa0fcf0cada3be3cf01ad9113924caca3b88d610e7738b5962e0e0fa21ae62ebf124120f -generate_signature 01b5d99e68a458de21c085b50147ba1f67b0930144fa77ee5d4e1d37e91da1aa a928416ec7f0a87dbded403c87fcb073528cd4f90651ab9e3bbdd14ca61b6e46 370f367e7ea29b50ead04f5195c6b0486923c4e7109c8fb79b1059059378b302 210044bed17a014c53a89aa9635481e3ed1c2b20f219504f13ad6909fd79f00f5b098bf0e49fdd36aae37b620d12c2682fb110521ab0331392107d495859f40d -generate_signature fe682bde8ff2a30f81d616c05b9b356e32b3b6f11fc4f58c133d7b9082f3fe84 dd191591dd57a04d33124ce1af1ada602b3f5304b15d4a90aa0732d72742f501 c2b494b0c63900df29f46d4ff2e56a644aee5cad4105aa0123bd9809d675df04 44d5c029aabc08a8ec7360fced674523b6ffa5f4361badcc5dd5b7ccd57af5000a82acea519b8ad6b6db219f930d65945667d4c87677ac59361be12c92f46a06 -generate_signature 96a71cd74467e3b95e4dd8874535ab8fe7710ec4956aff40dae1e7849bcd4c24 c55e53a61395c80d8adddafc2bb1224f1e64ca8014fd13d809c86d7b5e299e0a 84356cbf4af292e87e3b7fc328cfe45d5bab67b784405de76533fa4fe049d702 b4eb1a5c7d454613235c5a1fa22e412a8a06b8d1da1404cb80b59f84f036c001094e748720c5fea4326be5ca34caf8267557a6c224b7b3f6b0e9ee19aee02d00 -generate_signature 7332333e4bedbc6291e64c3acb32779b6bb94493e83e676da1ea8e7ac593cf7b fc9dd303f58dbb87fd82695b463e1197a265279031cf9ae5a6a7e6dafe2f0fb4 9fabb96d4292617086a3a9c0dc084bb6118f73eeb602a6fb3082a37fe6d7a40f 29829cd4e965decffdfc0d866cfcc76f021cd5816280c5f9ed14b4c43f46b80e81f5f10762f002686bfa98ba238d562e493b8c65a9addb4512398afc8f8d6c0a -generate_signature b15d619a2e307fc4e1848688c3388deb8f2e0eea38328cae0886adeb42474204 5d2eedf3536c0d1857d75190f3da8f9e214dd170790722c0ec90b5876f46fa1b f107b7b3f7e17ed25504d36574d01318cea43a84b0c275d097ba0cb83e872601 da0bbb4309f077b22353ed0fe47203dccff8a301c1857833c41d72d7955a0906bfcc99e30f45503fcd2e032723c159b34d10d0d4a90755da7210710599ee0502 -generate_signature bfd9fa1f474799d11ab3ef3abae59c17b300b8cb899b2fcbfacec678cc77bdb2 362bbd5e5c95c7e1adb900a3c6c5a67b5f050e58ceb6917bfd037abc61457f23 4bb72c886f3d978943afdd5c1e53882caa6b323e0c0836a1485156c569c2b604 25b34f04046a44efea3c442f6853b0bc39d2a2970a5530b3f6345eedd27fc60f3d5674de169ddc6334d0a1e4b4f2304abc434d4b4b62e9cb07a437849a2b0502 -generate_signature 3b8254fb834017e439bf4c9db7a54b8d72cfdfe734e64e3db94bc42761e52a64 3f1a3561b18915b34bf1b451fa01ce6b9e2124719b4f3843da1caa106f5e724b a9439b2e768ea849b35fe3e8f31fafd2460343374600031aadc7a047d8f4650d 8151bef3f67b91e1b8db1d6d8872e0ab27e70817861787e99286ab9ab28c6e06491904015409f151431d34c17e501e264cccbcca23e18eacc3b93144fc18ef06 -generate_signature eee51b7ed448e86eb34c493bb7cde32b654a2c3b94734b54e313b44705736194 9cf9b0c581562977e791eaab88f9574ac7e7b173077b3e67283fa00ae448d19c 7e0763abac9d638732d9cd91c7662b13b14067fb458dd8c13329ba76d9685a01 9eb25ef0b5a92471dc77ef399c705af676dee3b61418aeee3df2c57c35a5680aa662d877334846532df869225b174590ed234055d0139425e1dd94871c9cde01 -generate_signature 4d0fe60a2d913071d88b67d806b451e5217b66279c8a366ea50df32dc7f019f0 30be371fcd62d0aa789bec1511074649e012bafde098d1997867cfd1a1c00020 14ae35724fef1e5136b485fd319f2ddfa31858c2f6e8718016ac61c7bc09a00c 34c16aa2d76361ae999faf61bac6dcb21d8f67912cbe56cc92535012da0a2f04396d161191380d670a0b5366a5a83f95111e916de574614a44fbedd1591dac0d -generate_signature 52558464c45be054502fd7a95873356906175f45d96a123a679c61293ea82c59 f2df74450892e64b630c1bd242ad6d6eeb74eaaa37817359f0ca6ceedadac351 fe2330bb3f97ebcb29060bad76f122164f4f376f1dfece45ec8e1b74a9d73304 92963c1f4e21a692cff1fffd36356da35282861844610d0a58e19ce85438c8016f2064d303a8abd19f7de365869ec1d26d5cb02cfa5c864b69f0ddc902ed920e -generate_signature 110cb3d607cf3cdeaedf72bb5e757beaaf33735c4e0a6c9d36e15c3bc44f46a9 299e119e03f8698bb3f91c0383fc2fa3566893b430c4181ae75da3b6db52a14c 5f8e1334f8edd3031ae48b6167b69ee954b2dc825d96c1a66115f3e0dd19bc0d c7bbb514cb6333cd47d4e4c99ec6aaab793a3e8ecfc5754c48aae09631c5040889f6a1551d2eada3c3f3b9e735c5183e9b0d4198ed10dd6b31706d521631a40d -generate_signature b9c72e905e48aa099d4618aa4be67625490d93c298ae9a3ad50bd3051825ba5e a2d1ca5b1280a41694c3378e2346b3f9ec16d27905a7d9f1ade0aead412fdc5c 2fd3f1486114d6f267cce879108ab62839c678eb46a846383c25904f47719605 29d47564c241ac9f6e80e65caea00e4bfe36a2e082fd09930a21eadc6f74b401fffc06d15df962209468aefd1a36435c0ecce62ca2dea41e16e5c24b5fd1d70e -generate_signature a61d5acf702d80aad1e60667f5186d692d03c291015af38ff48b3f627dfb6c93 794e3180afac94b50792ae3b11ab01c8ef4e02973b3c2efa5b3110d2cc5bc5fd 89a145b6365dbcd67ad36878174eab5d3278b2da8a0724fc6b9fff3dadfdf103 f17b84326f52bbc547b1ccb32c1280c0a6325797ebf34d0828ab717fa72127012c17f67ed4aeb4644511b20879b7dd7362d5ea5997010e77dc93c8bf25fd4f08 -generate_signature d0666d9f44a461d56c2c6c35bd098282042d7ab5b4138f80b22b98b940df2543 c97f167d5fdccb9158ab96824c60752f6a1034e163b96da179cc42a8d6f8cea9 443d72bd61cb9ce36ac11839333cab61bc85506b91dec9ae8d0071fe9805ea0c d5e4edee9dd069933451ded4de5cab375d61a17407c73266ef59541997f4b10249025c47e5df85d637d7cd9c8942aa7bc226b48e762be242a318662543119c0a -generate_signature dd4e6582d6f65498b6e6dbf35f5dcdfab52f90a8eb7ef7391111bcbe7066929e 1200b5eac5cf70e9c52da437ab1d7da3ad5f951f65baee0ba8aa02ffe69a3675 b72904a3d7180d9c2c16e090c52051ebbebe0ba8376e2defd850bdd698fb6f09 317e3873b1711eb2d1b6683b891c3f9fd1d9b640d798b399eaff7de9ae138600e1c535aa0fdbb737c45b2635ac1857b29a3d7e0768237ccf8630f7bdd7e39809 -generate_signature 6a1c4227f59b75fd001623aef3687fafa8eff103526023aa6676e8f08fe3c15b 1613c592fccc2e0031487aed9aefc3eb0b86ffefea0f014173bdb28acf4e8bb3 bb93bcb48a4efa8ae0b0fc9393653f2696c77603e9fc3bc1956473f04f64e402 9afe337be855a0aad8e2f2219fe5f38efff0779665bf106d8d3d75307df8700f21add70316824ebee74d2cc197d3a8bac772adb9df31e8b5ca72cd4981b3d503 -generate_signature 2677f450f708876541bbae6ec7030aa87d2f7599bb4f669c190b8df2b62daf66 7a85581bd21fdad5609a84478a9e61a084ab78021c7bb0fdcd6c9efad8cfcbb9 26971f12134601802ef62ff5dacb99e5e0e08ea2455861e63af3cfcbb8cc050b 758896ff7ab6393727c45a53748c1c0db57eb132bbf97624130f5edb37a88001b83beeea0a4fc27abaa153d0584f00d8168c615e15181d8a4ebd4a96513c3703 -generate_signature 15bbccb57984f152d62b0c82f12d732b75068118fc3062805c157c33b9f35741 cae0a132e7e32e55906039a7f510d940cbf4dc16543beb1004b636866635a5d5 6d16a1ed565e148f653ae3c20a978dea4f2bdf009dfc8d9e66a201c9d184e906 92adb060e60bc4b3904366dafff0e3bc700e29683605059fff7ea92b615a270b409e65981d579c3327fdee258683c2055e688aa45ba9fe62cbdbed6520f8c305 -generate_signature dba1e5cb897f3a931ca00c61296c26aa6edaaaa95d844e8cb29a8818e5d3cf40 8011fbc326c27242f794f1c93e7e616ae847badaae42e30ce345ee2054752490 9b02000c18cbce2c5bd97d9e572ef1124b710bc4bf820b5e740c6f52185fdc04 68e45f5355fe0521e0f10297ad4e59592be05f13976d12dbda5e1bc7efc4290a77f4fa3a1b4fc46fb8c547ea6c1208b8d2e63b837f2dad11df5faedd6c38de0c -generate_signature e9257b43c57e17027c955dc21454cdb41cc59ad29307468ecc595466470519ea 5c841fc7705e9f0691444e3833e84b946f0978500e66dd187b6b4011e2bc5722 ad7c20800b766bfc8d97bd71fdd21b3390f9b5dea4a763ec94209e832add0c0e a83bbb8d76c32675b4d7b7a24dfcc21619964cbb84b7f4ebe92cc79676b55d06089ad673ded1660a4c61cce4fecb9dcd862291bed7955f0b046f42536453e402 -generate_signature 49a91500dd7fd49dedfc127ad940124684ceb5d0d298fd708906728551ce059b d19aa3856ff99afd3a621c339cd5688ad3d108e313cc6a12e76b857a56c8b254 8120cda41048d6102dd2ad998972b345886573e7caa4955df7c3b6632f96a50f 38f2df81caedab8a5665a076b9c7eb1d7e05477f10ee293dee4f54f668de2b0bea8a4975a22ea588d8fb3e0731d54cc76728230533e9456648b853536fce8601 -generate_signature ee3d3c574231cab4d8b8de5b127423e88ce63d30ffba94e44b7cb52d933c4551 c7c4c1ebb9ac8a9ab8c71bd032c95bf4d76ed79fe8ac53265f8d2e0ff0913e5e 685985d5d94c9702bc73c58eecd4f2c69aed4b5ec1136af0636ee81d94c2530f 9366cd5036e5ddca159204be01b19b236122ad17c3aa0c3f102921b388b91e053691cb727a06e90b126a4fa082fed31efc75835d6e5134999260099c97e90c01 -generate_signature 1473d8159b8f7ea02a7227be5ddd874e9c6e3c114708f7c46de729a81f0379bb 7d995c9eed1ff652f954617817f772dfeaad1ebb968c3b4260c22bcc6aa2e764 302201443d6fcdfd98585792085a7b7e804ea4a79bca8fd6b1d24d9ec30eb10a 165e8c1f5c0c47e2e1ddaad96c9dbea0b94313bd6c947bf2b49efe8020ce190d3ed64e455eed6fad4ef0fd748a93226bc5cab3033f1bb05e087a0d1e98f66b07 -generate_signature e0b62a10aa27778f06ce587a439179f6be9a444588be2fc6c61fec24e4cbfae9 b31d7350dc561bdd04634b1c65d4d07721b21be061f4ea6779a1a9f0268830fe 033f706a513a3102241553f06fcdbaa4f1899f9776f51584495e0eea1195c906 e4b3f64a7fb38b66679f5dfec62eddd9a46ade8a6cdb9f771d9eb89aa563350e11d925f130ac20f0c42432cab63fedad810de8e3a72d6dc0557e01717aaf2101 -generate_signature ce6fe431c9d841f2f7494d0c214ed3f4d470b1de679e30d0980137dc8cca5369 7c4883ca0408e5279b4333cd5970366e7d2cae46e88bfa9efd5b9554b8677438 bf876c480666daf828d4b5aed26b853372ea58355c370ece915600f127dc2a04 b8e22ec11439598354682b80d81c6654e937702fc2ca6de42376601aa6954a0928cf8497b5d945bb4d25a9ba6ad88c3e39b770a53d92dd665970c8b771f3220f -generate_signature ea984e9cc588ddcbd884642c0c1eb40dd85cb049f22688a80f42b0835b3d2721 4e200ad1863ddbec552c937ea0f28577d93959185269c21b5b0a2faf11738582 144de89fdc0a762c28f54d7cfe1d9c7c1dce3d1b520bc8e6b1df39405708b505 a81c8574819b8bae35bc0f513affa3c1492467c4b22e268bcb7db200083a9d086f3144aeb3f995667730eb266229263300707f937e2ee850d642c38bdf59cd0b -generate_signature 298dbd2126d0276c1d95e2e5220bd529b25c16ca670094a667fd3397c7b7b22b afb3b9bb1769caea17f4102c3e59eecabdc9108195d33f0129307af251f8f330 f17b90b7c4bd2169f7f1f5816f8f72a2bbfc03c4617b957db3ffc04b6e42a303 4bba71c3d06121ea7e003ebed49852a0fa77673fe01c228f40960fdd74126805a329f480a96a671b6699bc0b3f0c51d31442518db3a533493219b8ab2da1590e -generate_signature 9c5c87cc348b763b29665e7e9fc83c2ee425c57bc610856179879b5fc644dcc6 67894b54c0628bb6c56ee847b712253782a9b02a506dab960a5b8eb9e613215f 082fff76e7d834c3034b37eba3aaa231b1b190ea3514791beaadd4b3daaf5508 328f5737f98b455d82bfdb8655e7387d8a4370a54b8f91ed71342863318fc401b5ca3584db8fd104db823bad2b033414361c61eaa331675da17c6fc9e1512909 -generate_signature ab263c0d9626de5860ea22e2f999676b455641c23e9edd517f420c01e84cd2ce 1ecb565cdd6da4bb6f6cfa57e75940630360f663cf56cc716f1b949419f9b6e9 d22ef716713fc60b11e619292c44ec432fe7bd4cf582544fcf2ebce23d566009 226e143d5e2d033474ede0e4114f4da01baead5e9bbae6a9f342305ddbd8e601cd927d3c3ee7b2e460d2baee59211761370f6f33deeceebaf8c1fada626efe05 -generate_signature 2acd9cb6a9418f90b507b15bd258878fab69afebd0cdc57ade4719ab89be32d3 814c8efa712bed141ffa2e72b2317dcba9e917330523946c2a97e8af3f992fe3 199e9c09c85ff00f9431ef41d7d9a56aa05cb9d09f548d2fad7a783eb2faa400 1ef47316b07f7cad4076e70863c08071a5be323e858f0a7baefc5113b24455089410a2c18fdffa9c5e3ace8d4332200a156d601ec0c724c7fdd55eb667bd7008 -generate_signature b8aeb16bf5d31ae4fe7e321da12d8defa7923946e197390f0a1696526099cb03 eefaf0226cbb5685e2770e2b72a0de26f30c9ef5673053fd64e762d9efea370f 4e524c57a12abdf684c05bd99eab65efb947ff2a50b1f27ceef7f4cd268afe05 dff7951487b8b37103d5922520ace922aa582362a590117d07d9414a92619d0f33fd2b1a2460cac288375e9376d746f6fbea5b068e736220052af14320163801 -generate_signature 4fbbf6dcbbf50f92d5f60b467c9bcd4921b756691660b2ac73b256e6b614acc5 0453e634c6c63a0dd1a999307b2c9da357b54fc1e89eedd1240f6501475d35b6 ee840259b97e841cdd17946b4b4acbcb90c7bc05996fea802825c62f18c00902 9fa0357df98c0199ff33613b08ab56c50d361aee1373aa9994f1410e0df30a0c45620b9194b8ec490168c1fafa9e00803679ec292b80d2c9a87b256fd5b45f07 -generate_signature 7038946351d9332520e91640e4ea8aad9642e66a9661c1e62a519a3e03d24f57 9bbd178c6a6bc802cc56f0565ba7ef52eec02c1f0098a3fb1a958aa1885e4d63 f68267ea33101df63bb15fcff1bc528b032d770fe69a0b6f0341c614e7ee5d01 07ba12b7840ff1b735594118f7f0668a475cebee5c4c810cc30875d2d4d70d08ab4e0aea65460b2674585fc4190a6e888cda887ca5846355cf21903f636cc507 -generate_signature e8e5ae2e4f4f51cff909a6a73d0a24be6e049cdf32af893b6156a9f3bd7a0613 54e78d50e27c98418a28d0972af5ff4f7e6a5f2e212d792871f4395a4e0842fd 873ea1693f04187e4b39eeb4395d38e2b12494f3a426dc3ef1bb58a2c0a55608 ee8b2afdb47c4272ab2d8156da188012f36a2afdc93500a9d038aa9627981b063b9cf755c397d64a03b02e7bf29637de8610c25dc2846c99768d5c9f3497ad09 -generate_signature abdb0a9f87e46a82240bcc77f5aa5f8c0981398731f7bc392b1e7f2d569c491e e8551e4b00aa9a74084250f33285a955efc56a310bd287d97ce3f213eb51f97d d28b26262099d372ce35d41e3a289d793b425a62205f0f22fe348510120fbe00 dd41dc6165ad1104e020dbabf94731bc7d630e0131cc7d2a72fbc8ea2ce68b0144f207cf47715c7a64cd01db4f346166a38548bcadd315f11deac6ca7f913e08 -generate_signature b6e7c69d55fd322626930ec53729cc2975b911706721a48e41ca406146cb63c4 58a63d82e3245e9771435211f2b415c5739c99370d08011e0baa1b502302b16f e1e4f2327b82c6fd7fbd35905d91056df0c8df6702eb282487ac55b3801adc06 8409e7f33570971ed3913f916a8c61984eb39b68dbc3b370cd5a744cdd183600fe53c8934c5163892e7a6ba80f31e735f4ff8129e96b396cb6ddb6929eb8140f -generate_signature 98d1fa8e6b508971d25a7147eedd2347f076b72729a35f9b1a81203ff85c810d d1e1c86421a34483152b3cc8bb124d140fdd638210ee1bf0df30c80b54e61343 5acdd92d2c8eb54a0ea6d466f5c2718d703a5108ffd352b6f8ff70f2baa9010b 4167df54c0ccae4237e42a60566db15bc83f7469f5ef2c858c67f9b82b9070046e2e6096d66b57e36566d675191c0ee371c752b2d8032997356f5b1b17b66209 -generate_signature 91dcff5d1d7ec3854de213306e4e744ca4c04176d4b20830d1a2012e6cb026ce 15b09f757df19ae41ef4e9050d0be8e76c151b20a7f4f792748f83aa14b7e0bc a9e865fee7d1469c369dd683d7808d5ec65cd0f035dd5e09c1eb034a7396c305 3674d44d7f649e67b3172deeb645b1f55ecbb38cca3fa0bebe01e774a26bbe07d64678779607e6a789f97b01aa9d8c58a1ccedc5d585bd5936c6a0a34f44be00 -generate_signature 9291ce37db25aa4c373266a7aa25d585206492949c8ca4671eb7e470c6a85778 9d35b6133fee1dca194f7a64e8c1dd4a03a76e3805cfbcf80c9c42f9a45e91a0 6716b59ebdead7684c13ead0a4aef3fea77cda742bed36b3dee1d66a475d0309 3352a3119309b753ad845bea25d891e3b5f3aef5d1797dcc795f07b6326c8b03f9e5be6d20cc886f7c65cf4645a224258b590f645b08070644693a780a6a0a0e -generate_signature d2dfb2e50813260e6bda0a4150ad893b32f99b2dec47ca8402f0ab8ae30603cd 7b8bd1f571d5083a8316fd99efd6a3a3dc54d87d78425ce1f46185a2cdb18a95 5d76e12ac8050665387e1c8af4e18308d9f227b0decf22355fefab31ce1df804 d31a05360afeff90e060eea669d0cff745f1d778c87fdcdad515d3b32cd1c00b7b32723ea45461db6d8414901f2c65ce8ef1d834845913a9de2d8788d170b606 -generate_signature 7ba6dedecefb1d24c087c994a2ce612e568fe90b47eabd04a9f44a9196cc7d06 ee5a5d892f36ba3ec29b259ef936c1a14085caf56e5419ce2c85dc356750231b 648229308a95c52f19b3011f816050496d77dc42afff59adfbfa851ff4da470f b6f60be2927902228d168c4b8d94037764adec3117f2f79b712ab31f0403b6022a4bb0ee90061d3da34be0281e743e0b9505095dd01fabc97230229846085104 -generate_signature e53c575148d6502987c90b32ebc374a2ee76fdb167b4bcc2fd963aa3293a50d3 db099a6c2dab7de4916d3b2e21c2bf1f89f24280c29a4802ee27899089353573 9a34773656675cdf07d64bde08eb98585b9719cb9d52d919d54d70047639b508 461d3f532687ffc861956a8d6241e5655b668e2c8f9e56aff004b26d8d654800975a6382b6d8dfa457f75fc179ef8894baa2978a92683c3b41686188988d5404 -generate_signature aa909b74b14671e3c56b0aa0a39e956897151694e42d90f502f5426606a4bd3d a2481756e3204a51d40c8088e69851185a29c962f97c800a88102b812606ca0a 47d0d3279058134729f860e7e0dec0943115dfe22f052ef7756b5bc263aa030c 24316e8c132996a46fc1c0b3e2caca9e2c77e73e9edfc3ea5855a0d6828f87072efdc277478ead9a7c188cdfc2cec79390f975cf96afc38e82f059f3fdec9608 -generate_signature 09719be164df8ab8ffd5d4b6820d1520992afbf6fa717cec48964f3af489af51 3798b31c7c0f929e70268010839042b76df746c4f46b0e219521eb73e89a0d4c 516de341e7a3e73b96f2a8e3c0427bb73eb04fdc967f6e0d2b87135c0ad72907 71fe0a2b2adaa3e1e5cf44d19ffd1a9a1c1198570d7d650a01443190efe84d0ad6a9fe4a91c4ec8303de7eaaa35827ab513a1899c1a5ede9d4f8cd030225b303 -generate_signature 95bc343c1fbc1ac7fc31c617f0944c8a5caa4b91977bad385ddb7b72133b9918 3b510a05791831d9d86d268a3c56f48b40bb3b2854236f4a1457aa573f1e3616 83ed82fb8ea18afb5ad9664ad412d2a9ad5d36119aa08eae5ae48d3b6e979c06 f75d061edec30fdbe29df7c7b922746bec64358f9a06ec1b0771e4763def7b073f60a399eb149f6c97989060a5fe72d6b99e8518e55acef43edd2e092ccb7f0f -generate_signature aaa59d9c7b9745cc62775f1a565de3e704ea082feeaabf91897c163e9c453057 f1d8a8a9d1ca40c3c1985276b8dbad02f9b05b308633f8d53ddd23fde382046c 38b8d2d9729218db5231f4e46c2641bc43983320d493eba916c9d99aaac88208 8dc9436c1922cb261e5a022d06e4c29c6f75eed7fd33e3f52394cea623bdd402f66fece7c5589962c1c57edf86958397ed7f75fcb36207088ba5e8133c95f007 -generate_signature f4433ab65909fad4afe06cdab382d2fe7b1f2d12a47ddf3c4c634bc9cddbd52f 320c05d188d2ecaecc06c15352dedd207739ebde86133f7152b3b83074764778 d34800b40f3d39319a844b8a85c65bc8e7aee303a7b8e423d2efc1c4c48a6704 f7778febd060ff54eb5018bac99f24330a68c6aa8e0245515d777979c1edf10cf757e148982923c8ec721931aa31b6802685ccdcff9d04989386e5f388ba060a -generate_signature 121cda7dd82e45d586ec16c5ea52fb38026dc84d3acf8b53d9b14c752bbac650 5af2a1a8a88c0e0b53e555a7077a737c2cea050b7dbc0ba3d8ca2ad037c908e7 007af32d54c623c7eeda1e02984238b6799b08db17e3a4231daff62ab3d1640b 5a17bcff814143b151c30fb7faaef0e0dd8280fc05708ff002e2b6d91602ba046f4239de8623e83bbff48ad728f0f81f67386c425979c4c9f72ec1b86888730b -generate_signature 4adacb5f6d707bc06145c6c445ef70db86145b78af456ee5d687530713f153dc 2d96e3dd0c90587368b586dfc7954d5d6ecf04523d0797a0fedfba91f65649b1 1f8093909e4e249adbd396d9296980bf6b65164dc6ea679d955b98502622de03 c8b24021d492912631f3d934bb7ad355cce16d81deb4e49599c8a39e41f72a09898602c9b29e7c20c7fff5b57691cb822bb7a7476f9c791881a3a570bcc51309 -generate_signature 86034412d429483e52eaa4f8e3426f920886f5a7302f9733b60c3368f8feda5d f47f3d62360154548b01b27b8e20bb653569dd2e0d8f3e2e4926563b19d2905e 383927c522c2201332dac111a750e15bfdda86b1d47483a11624290569a4f808 a996312afaf7f1c1f329a676b3c5279725946332d0e9d0d508105a57294419049b89e8fa741732e6310a678478d96548551185843ba71822154e9145b47c7e08 -generate_signature 891ce05ba0c25b7d2409dd83487ffb0328f515d0dd77bf1c2503e33370f8cb99 0103c4ef2f18527e1630510bc27ff5966b018199c0b82a4a0d62815c74f1d0b7 fb1bf90759993320f4e16f3237c2d63dfddf6594f21c74e20267d61a8bf7520c 1096ecdbb36e14d420244ed5b97207865387d4156c98d1a97882608bbc7aa907afc6518205690d1cecea28d8796f5e610f9e06a79ee3d04bbd97eb0d3706f509 -generate_signature 268378aaf9b6403f036e24e356e90d67c3b82edb01180fb7d135e895283237b0 32b9fbd425dc1a9e2d6c693699594bc310737d3954b8dcd5f3bec5a056a4e63d fa028fb4484c90b355655014f1f20eaa711d1d61ed8e200b718b46d475603506 9e6bdda01719f742eb5b58c41d6ed1eb83b88e8081225fdb21ab61c53bfdd10efc9075f7d8596453bc7ea0ea304cac95ca04ddf4509bc9e66d1b28d680b2b208 -generate_signature 4c1124a23917f12efbf34c9c90b1a079667aa900b78b8a77297f55e50c3d6e21 ea3392bb6cb736c3053fe90bc64917412190b673f2d1db93338414fafbd15414 54cff91e78a84670f568167ee279e72cb0a32ecf75544b5e4d49ea5a77a92003 f47c858497a8ffaaac900c58fcba9459733bc13df2d1f105d56611ddd7d56a030f3c3ff48416dd500daa03d95e3bb6a96b7d9b44086fb1db12edd29058490d01 -generate_signature 112cd2bc459cc678065ba583a09c83d962e385cb546523901ec4d5b47425d0e3 0693ef1cfdc58c35a00dee5477fa4d8c73422294c88b8f9d826dbc414dec2306 a3d6655a3bcdb2baf44248a541141a67018c9080001457e165ec29d2c3248600 56441c40867c340d3846c6a0f3d1d33225389d4ed31f3b3bc7c6d3e5f6a83700d604336909499e393749af785fcba11800742cd5fbfb2277cc87d32022a99c09 -generate_signature 2de379049e28fe930809c36f1062769665d4133d4d668a718b84f9183332a2a4 29aa902ba683847b1b59f09ee480e1814454bc72c48fb3884540d369f1a9f2b9 de444332bd0fcd47f6c1a5b7fc7ae1bb544a2298672288f04effaa04aa7d940c 50a2049a64533aad969971a943246b4252700f8b4866c098cbe0b6169449c00c15bf4cdfe7256e9d1a06ed80b9e5cef867f2faadd59876cf96e5211acf8bbf05 -generate_signature 0d2ff1cf8e58881dbe688be73ce844772c6696636469eb8ca9086a3ee2ce07a3 a743a9ed747abc154394fa822224212c3f7cb9fdfcb40a24a142740d1644de9b 08281ab64eca078a437c7f10208d0f5692ae85f6603fcd1d9e8ec2bcf9f64d00 989e4c821a4179a4a57143659ecbfedb147ecd720182b7570fb6b1b47f770f02dc1ef1468bc59a40f6a21c472c1e0a1f94f8b6731b7044845870e705bbacd20c -generate_signature 7104100649edb970e649881d4059faee3e51e221a3ae07fc33d944eb6c192322 c10ad6f4972dc849e2bac69e549929822102071d43f404abe8049a529b9c930b 0e060a4fef0a46f9c3d8702b38f82270e76124aac1f6a8e1ec402aa9e7812b02 fca2c2db423b200286542664e4b7bf55e323800f26c987bf28d6278168e11204564c7e8732009691fb4ab19de5a90e44ba3ca053c8b683a3682eaba29a2f940b -generate_signature 995be2688f3f7354dc17d7fbd48e010611de9d84247e1e738be9faae777d20f9 89d352fce80cc5dba45a618468866cbb9414f5fdf608ed5dde3fb446fd665c20 791941512b24ea359aafcf96ae0d9a7f7c24d34e2177eb1822dcf12776580506 41472fd0cc207313f5bd0212c1de4d971510d1e7e7a2850f47e67dab4992730960e043b67d87cf031c3b09f0608650f14e553272de34ad331919521194ee510b -generate_signature 77262429448c69ae1fc4c99b652c282af2082b046a227b57dcd724f56ba0a696 9b605f01f72e9fa6faffa84481b9e609f7b1e20b8a33b46b16cceb7f5c0cf411 b912862268d63a977c75fbfe9aa7008190758367379862024232c0cd2504000c c3f93f091f6fa336cb9a09c29ab0d96ffc4d326c5d12c9d8026cd3512854fa0bcd13a8c0d97424510ab0249ae4894d44942857be68ca037693ab614570c4b908 -generate_signature 382537401e042257c1a0c6062ea8ed101da3193a6cac540d675354f12f7cab82 1c209e65076e9d4dbfb5762f3d1f59896da89efddec6d696a4498303f67d667f fdc2f3443bb4f28cda3ee7cda5255d36a89a983b91361700b63b87db477d1108 e9d7fbaf32f4bc541b776ccd19052bd26e01155bd86de53b03ac985ca2722c03ed47e10dd152a83ca0b2690230ca48b733c2f3ab66e548c30fbd5961998dcf09 -generate_signature 55b89623ad6f7b1d2ed599d2a04d8236f2d3fb8b337ba9704439193ac7956889 eceb2e5ac867b1603e9c61890aafe7871b3583d38c0a9f4084573497bed466a8 cb1a0a38f92fcf57064b0b1e666693cd1abe9a2b42cd4c7c667c933e3ae1340f 22773ff165a1aa41527743ca240d1a956139654d4798be7e068491e31624350529f5b2ee0766db4eb429e7e14822bd4d485f2f9c762c6d352912d7bef564fa04 -generate_signature 22c3ac4d3466742eef52c67e784050c30b5d5ba7b9235e4fed8190ec18cc50ff 628a568ca8cc07afb1ce46b3f78af000a37dc9bca3fb8f73d7d5e697b267fd3a 0e180e4841d5de78630e900c364dec44daf2f74e72066cc609eb917995a8cb01 a3cbdf00fb6a2180d3409a44086a54dd57c5969c2342bf7cf96c9d759317e10a487bcb17e5dfedad7dd978b19cce4f3f4891e3c8cea103597dcccf3311d6550b -generate_signature 8bcdbe48e967d9e68775be61493e60f6c5488427caa85c0c42b3ca3aff756b75 5de2c451223d0cc7293ca1826c02215c6b519bf3ee0e5b91483990c6250d808e e8b26635c9a1fa4b7f9165b745abbd45cba7258b6420187ee6e4325a89b0e802 9296e3925e31746ac7cbb076c5c51dbae362f60f470de8d70a10d1e8fc01fb016c083693a8d0004073b85b2e1fb9bb20d26691d42a2245354d63a065394b7a06 -generate_signature 37b33cb52f44b8c43a0d619ba8270eeb2ee657c0e70734a05c2de878f77ab540 7f5ce9f0aff75716477a94c13dbcf878a8a2513920dc40fd2a29814555ce1629 f8afe1e242f39bf2b08c35d6d18aca0900a1e5ae729efe56892919484b5e8c03 31f977b07bd089f253b8bccc5062b8bd5e8aee1c0f844e9a1d8dadfa1026ff01b3a9cdabd534f522c10658688a070e286d2a852ca513ad47d9fa7ab9062b9d0b -generate_signature 069bfa548d79462cd27ad3720842430f2c4947532513a36f0e012097b77e6a7b e6ebb1f4eae6ec2d813b9213050ac47c0054723d754685452f112551e9e6619a 6cb63dc9d194448e748210d8bae8bbee8e0cb494bcec2b51ae06c9f1478b740f f6fc3efb52ba1961692bc9abba13b7e0de9a3f1cfa521a56834596b3032f0c0fd84a999ffc423dc42512f170256c8cdf5c7899336cbd58ebb186ca89ef0cba07 -generate_signature 3fbde8fc46fb4f1001ac7fd712760e0d8ada351c2d2c28b6da74bbf9e2795c80 79ae18c7007b1a790ddf2e6080615398ca77b4d06c1b2f7e536cd6b8ddd5a21f 70f8219f8b4d2567a4771051f2988b84f137a4f84cb26f081843908c7d959809 c2f662bdf5f076e5dd6b1eacb974743caf2dcd09cec07a78bb44590533eb8102b348187f2bbf65b5cf058544ce670f679c797b028a3cc367d150df5e71cf980e -generate_signature 7b752d6c0cc3750012b98ff0a4fb325eab31e8bbc0dcd995d9932215386e4582 9519b641893b754f8224bec54da6b5c06730c3f4c7d4704f2999a21b46a30032 0e931c71c9a3789e25f79e722cd6858d0fff3f5198597b056247b9cacbffa20b 378ddc8ca56332d6e831304ae6757727ca8e8c61eec5c5e55833c74f8d89f500bc318c852dfe94947e69236505ad92536001c34f10f86d634a5a9ec02584f005 -generate_signature e64074ccd0cfb5b56a89cb07faa1386061fc4853b0b2211d87a7af02bc3fc0fb 7072faef529d5daf4fb8663574a4ed86a7fa17e5cf10f09190e280500a216738 d6d77164dd8a1f6859e8a814d2a17367b8a8fcb5e6bdf345d74ad6d70658520a 13047ed243beb6543cc82002457c04f54e07b4300815aba2c7e9028e2ec1620911ee285a16267af44fd78a375369d72f598d3e2184b541361e2cc1eb205b170f +generate_signature f63c961bb5086f07773645716d9013a5169590fd7033a3bc9be571c7442c4c98 b8970905fbeaa1d0fd89659bab506c2f503e60670b7afd1cb56a4dfe8383f38f 7bb35441e077be8bb8d77d849c926bf1dd0e696c1c83017e648c20513d2d6907 82dc395b7fea160c4d3a4825da723bb91679b63781cf32d1cbb9d6e9bec92d05be00246f1162b8127419f2e9b02692a1911ddbff68d2e5cd0e0bee60f12f270b +generate_signature 2ade1389a860c9249a42e45d32a9fdc29286c8dc0c8ea1216ba786c74517eefc aa2521256174ef6566618a6468c7b8a71ce2dca398be2290148b813710d12f7e 344a6ad0374b6ae8278e3f226d58e8bb2796e89141eb0ac37cff8552b158260e 94d0f3c942de0edbe8e20478fd7e82c6d90a34c0951f55ba120df66b1ef36805afd4c172b88b9344a5a19c76ad2737803700280a7fee8c304a0174168bc56508 +generate_signature 5e91901e800a1959b4ec07a2eeaa3a9b28893029a26e8ef5d13adde490e5df91 486dfb4904d81b1bdaf865dc07ff71145d1bf8a9e0c160b9c817315f6cb30398 6a05fa0a97e172c9a8f5d2e24851ce87bb649a46c34b33330ae71d0d24a4e70a d7ba7ce64bfaaeb85c8a1ccf038069010a0271f903d5991e331864be958e0908e9e2cbff97f41c74fad5a692262fa89c9608208eccd7864d88deef7062827c0c +generate_signature 03e29bd7435541f8b12830d68fa31a8976a7a2750e4942f36cf834506f3f1b17 3a9768ff7593e63e615433567e9f2d7167a934a9afe7fbe587f0502942ed4ff6 b1b9f0cdb59910598538708eea87ad5114ee9eaae0bc5c62907688d6adc60004 9fcd306d5514b6b598880fa1d529a4fdf6fb7dc72261db0e40665ecec8ecf702f638fed709d71c1746974053644e12dc80bc32ce1afa17f1c8277eea3ebf480b +generate_signature 5eeeeea12358c1ca323e2bb7f7c4f4d4f64082739f196e5505a116760af245bd 238cb505dcad22cca64e100b9b0a3133b44dec7a6a0abe198f6563a1df53a3cd ba2c8fa6cba86ddc91f810519281abeb43554e43ade7bdb3c238bec0bdd12e01 241223aeb98514ad177e864ef93b19b3746d2c75fd3f75317f7c2822ee3e43031ff189159e7dcad21e1fc9c7af603dd960190644566b41d249f014ef0c367909 +generate_signature f3f9ac0a5f5323f4af5441d5700f9d520e6cf41ea441ba045716b591e743cad8 73c0232f4a68d26578542b4d1e02287b995aef419200f78b3294f1c1dbf979e8 9758db88417ec357419b8e7ef2a75d2c65bdb800d27f6203aa54b5609955780f 4cd6430d0e115a554533d9c34856bb96481108cbb8f4d51ee696a97ac9668d0c033fa070873c8d51b8b89347f739fafb93368e6be41c8504a388dfb4dfebe70a +generate_signature 0c85328313e78623d436a1e3cf04ebadfd0178d4b230004469ff8e995fde6d11 c1429c3c7676607688349d6727135bb442466932035cda069ab2463b97387d59 1e58bd527b1ba1014ad772c523700434791a4d4c005f2ffb81fddfccd03bd304 5d0bdc0ce2ecd4f56a70ee6d4d3d794d2d75663be235345a08eeaed41795eb0747ce02e226dd4ae18b10d384dd2b20ae2d331f0a74e22670baca1b777adf8c02 +generate_signature 9d2ea8622e9d46d17a568571332f14d6be90a40df1aebeec20f8418a5f94ab55 f8b705820107149e4845f2a3f3bd045a55944aebf35d1104632450bba4141db3 d699d458752018ca3ca82433e6fac7547030990c923e4192978ef179ad4aeb06 f6ac03c0aeb13106e3c7d892a8f8cae495958e0ab45c3b73e15d3eee0c97f008cbe20124ff7c1047281e709c420eb68af616f41c7edb54945ea7a03b0cf4c700 +generate_signature de9240454aec3ea2ae6e13ca9457e43003991b9d0f75a4b25117e1ae66617ea2 ef02c34f64ca98543ae5b871197e413a7ccbc008f2366a2552f2cb5f852d8ef0 05cfc28bb9cbe0c4d02d8692f40fcc864665b118064f1bd31a347baeed24240e d9b53a764068be0a0ae22ede9f6aa109e5cb5c6a01628b6d87046c259cca1906c60db6d383b94811f45445da4fa33ef1077f9d59f37d96057f211e773022c005 +generate_signature 806c083034f5bfc469e68a490f512a3c6cc3d21d043dc3f9d4941f5a9fac609a 65aa36a44250dc1e5985676f193b23119f7d5f9e0291e6643fa55ac70b1ebdb4 5f5cbfd6c2d5e22b703db1c64163cc0bb674a1bad00cdc77ee51001af5f8af04 3449eebc2f98c2ae81ea1dee4b860693f2af5e9806a935d9d6f40c42beb5ab0d40b73ba8e77db1eacaea02bf1d342b5f9ebea4186bed8a961f56cd4b7029860c +generate_signature 413c368f9b4327907fb6bcccbbdc0540acab58dcf1a860c98a4340448ca4ef4b e37df5cfb5f8ff60b822d3550bdc498a7557e1c11d82a035782931854b996b27 4dfcc2bbad477e1cfc6076408fc85ff9f3381dbc2ca5f6b61b4423a29f372d06 a3b95073fa4b6349e116b8abb70b31afb53de79472e0075601c43df0f6a3f70116fd4a6ab88604e4d122638f4d0883bf928adf466bf296a38fe72a5e0f8d5207 +generate_signature 3932123f3c95fdb21bc6fc74042f39eec45e588d707fbb7036983f3597382bb4 8b0138170662561e8b4e32f0821d21d187c62e7939903ee747aed3b1bb277d2c 649ae266093e93d0968f31ccf4d0b9307abf1e50b7d2b811f74c9b83f58ab00a 2906551a86e553adce26514dc6f935d02639962760c4e040f0a14e5d15d1900220f02fd7e6ec00a9cfd62cc62253508526c17d80a58464949cb167d6caba950b +generate_signature d17ab44dd70f88e19f0e8ba88a413fc18b2a1ce0ed712d6d110a191c9a284236 38bba4ea01a82f0c16f36f4f3387d6015f2c703cf211f6b73936db53645ef2a4 9d7af7de69861c61d3c8c6f2022e360a6cc8a647a4f3cfbeae6c14f46265da03 132ffe4f3b2bd28b82aae9c202c556f3963f0416457c6721874a6c212ca818091b7286db323f26bd56fc0462edad2a5b4dda0c8fb0a4934e30c21153f46a1d09 +generate_signature 7dd81bb4dcd4d74f4c034a4b6a51a49662ad34a2687401ca00e8a628a298ef44 5edda4da92d8227684c8e30c940a9d4f9ac51c9f1e1c9216e826e6de3212f47b cd386423bdae74357991ad01bae929b6ba9d65803d00f06fc9f0ee34a764bb0c 1fbd451cf374e11e6826fee71c4733e398986bfa21b1d956a915d99ca34a44088961bc45fc3c47d484ae517fc4381e3874800b16ee86039c3043072123507102 +generate_signature 2e8abcd827b3c90a24a65d29e229b5bf59ded9b13ca77f51a98fccb37cf9775c 10df138936f814628662c2cbdb25dd7820c310d7974bc74314131f8423880dba 0d8d845fd386f1f47c4e92d6861026d697215a396f3eb7eb76cd478dd6c1220a 15a2b65a8a9540b1ecbc079d72af458911434db49200bc61aba7cc037ffc6e0df211472b53884922224c8a6e09f9e8801b59dd554ac856356a69fc14a389fa02 +generate_signature 83e42343a638dba6fb5d78e1b9c11e236425b432c77deac3ad098b93ad73f14e 3e855c996c5341e02e85737a2b03e98491037d91721cecaa17a83c2c4d5741f8 87be0823f5741f09e2f0c1a3337afddcf1533dd10e0b28efa8b1147c9ef3a703 26d8904ca8d3266cc821d2b47f0e9706b8421ddff2d8ad45e128d27686471e01d030bbd9f0afa09aa28b331a1ed8b770b157100f7c85f4cb5e4ca69c2231da00 +generate_signature 206fef6045a5e44f87bc7acd4bdcd271e1c55cc8e42f5943f1f0b1b3dc0fcc54 9d32cb01bec30e0d7dc921f1ce44b5a829fd84c37e4304e4f910cb9df501af4b 8adec55623bd61bbf39d72e3f891419b5264af1bfaca43278dd86a944ec04c0d 855cce36bf8774d2aeb4025a20bee96ac9f447f98907387213ccee5b66763008980b2b19b8aac1984d202ec6d030916bf84cabd1536360e2601e3636bba2fa04 +generate_signature 0f1d0617a849e1266d6741e17e8fd8a4af392577ba27783ed203377cce7514af bee8f6fdc7360d9acd4a4076ddb2f572d9e9306940b39e5bdae031db28f201e4 95f785c3db1b8ba26e660e9ae8a60af20a00289d984a83c852482c505d1a940a 84cfae0ce5ac0f6de3cf01a41e321d771bef0e120c69269c5d17f3364e59cb025e3f6195270469d16b1654e1da59559e184d382cb631e22f916a813c3a0cd30d +generate_signature fc47da4563a05a5334fc291b1730ccad835e9469f417987ed3d95aa041ca2aaf 0c1ad3f7c9e60987a84a3525a4dfbec5ad69f276ecb32e3c0880ce69befa4810 50b84ef4f4178228d07435d3105b0fe52ac7d2bfd38da43875579fe65a7efb0a 7124e58514e1959b3042cea4d6483b77f47c31ec61c741f51e85e64c0a557a06057e1321cc689cf27e455061e17228b7c6b90dcace53032eb36eb761e881d506 +generate_signature 02cb1e9244aa1f6736d8aa1d5428b30658756bd318bc938d5fd704d97e4a5d9f d52a28a31f59208e4e41f6473e6bd9229ab6a809b228fae3d8f0a6bc3a34eba0 c6fa9ba77466112e1d268891c5cee9f88d091a93c06cd4ba23bd034cb3d0840c e6ab7b36f234da2d16d6fd72c8efa27518dc21eab5318af59bbdcf30413c110990e878c8fb8092d11c724b828d8e098523ad79eb7f88223f69f8d29343845208 +generate_signature 62eb41777076bd1a90450e80e77cb1aca6a5f08e2320128b704b663402759d01 8e83ce60900fabed38fb560505d26420775f10ad6729b209992816101c1c4cae 14fcd95cd127e1218c11fa267c46f8c2d5c1c736e53f8bd2bf084693d1114404 9f53cefc1a5da70b6d8ad309db890f3755ad994dd0d5ee5ba778d050c09c410221f525d06864e6c48c13232aa1827db5e48d9b0212a4c166f367daa59946c208 +generate_signature b862759c98652ca2b1522a556729103de96cfe831b3ce615547ba70c5f622c0f b7827c8b2edc9b4014cf4421f1c9792c012ac3264b15204596652d5854bc7673 1e62f5b7eb5d2bf88032c98edc74a9c138969cfee15f4cb69990feabc0f8840c 7991860be6e8a268f69d5de5292f5dd566000389bade3053790a52e39c84540e4d5b3a42d3960649adc93143f08f947c03842c01cfa98a3b3351e0f7d075e407 +generate_signature cd35a8dc333ac5f33f3e12831a0a2f9ee03c7f421dffd7900b7661e76b056f4a 31ea2dbe369fc7a4514f3f826cd259eb8baa6bfbf22fbc2ef520d98be6e821dd ae4ef972bdf190ee205caaee156166e180e054993709bed5ac96bfd80e8b7401 9984acbd99cfdc3c0ae03a8b2fc4db337318ab1d734502fa22a55dba9418220a923a3b25f1be8df1440c66d1f5ae2b3fe53d7700a935080866250c3756211e0f +generate_signature 5dee08dd0f483aa67e0b2539c362eca5c4529961935d56f661ba641e5efa7e30 be75e0ce86b68a72f7487fec4857a7a6a188176ebcb9192123ff98c49bd2b4b1 b13ba6bfe9c23671e7bbc4a6380c1ab6c61b09db58220d28781d8ecb1b37e609 1be9e2597fc39bd0363857f3b6b65e86734a9a699fa017a7fe63b70ddfd4ad019feefd8d8cae3426490a2b34d7654a8da016e1f0585216ee5558b5fcae821a07 +generate_signature e88cf519e819375fc59bc8de8ba403278999311c7490290f2f5438306bdaf339 ce99f73115a02e03f42a1b583dc3cfd5fd1747c1400d1b6df73e1a3ddf616932 0a33216348544ad80a652433253b6921c0599d3a80e4b2d4a0477cb121e08104 9e59ee9fddd64321f4ee22b73d27c43d4da50ecdaf5a41e57a732c4c5718ea0d53b0cc6ea733b311eb68e7376a15c2c2f804ba2a18fa2e3ad6adbc1a0fe30905 +generate_signature 82a2377ac1c6a103cc0b955ea1ed3fcc41bbfbf6416b31984f28aaacaf4e2a94 535ce02ef72e89bd86e0fbd9f8f0197a99b11f81128f5179044e4e6d0f5f1a93 dcf1736d7a4306330ffd64315b2122c5b55bdf115dd52680d5ce798893b62a02 15936525f24ad06264e8a75c014cdba6fb11f11d5ef1bb9e1dc4cb1c34a6cc081dcf1068fbf519f387a028a68c05dbdc10024e78b7d4717f709b0f3b58aa1501 +generate_signature b207af5749179b6f2b30f48f2e0c19d90b6655cdeeb3fc37f656321f50ab1508 b2b43995a5ad7f51c6c71d29e1991f431444b5e73019b3d36fca05bc566bcf41 b1e043b1b9f4c280f798aa59c2f62e46c4abbc65ceb17349f4d11b867937ad0d e82bd2ee59da43eee8137763be0c87d94db840743dca70e9d2f6a8dd85f9110ca63c7b62ef1f5737bdcf54109889a28f719364bc68b0278e31b4425f8e6ec20d +generate_signature 63e53137888836ef30cd6a93d34a0fa749fcd9459bafcf143da29956a6f35060 4762dfaa39af96a85ae746326693ca87d82a008b96c84792ed1c570abaaa81b5 723eaeaf84c9dae7476aabdc5fd409a0faa7c61ba8b424ff78d299b9480d5a02 700574c73278532462d73ce2bd90bd049815abba8436580cd7c0e5834033d705d984d2466d02231b70559d84ddef9e5756938790cbe93fa1e3731900543e4500 +generate_signature 4cd5f4b8cbbe735336d469aa8f2f7c7e5195d60f897b9a81b599b2e045850705 4a6dea121c196f56360e8c17273f1b94827efe1f24cbcdb9d3ed61b9a207b73a a8e62911309f4d4fe0bb6284fc4e3cf4fc5b08e3812e27328f68c2a6c278440e b7f46c071cfc09be24152dac2e6803d042f6802259f5da4c672243d90887b2007e89de8647749d9a89111bc793b3872bd4ba9023d67fb1b2eeb642a3ed477d02 +generate_signature 7687ef751d6533aca6c911d70bbc4a8c5252fbd3112969a9a40b9e1dd041756d d17a5002e137731bdad6600b5ea02b520efcd1005008c6f1244deb839ea041cc c848b5aec4dd76bd8e33c37ac65e884699fdd7b5c7a905801f2f46c56f783005 1f1a36b19a97bc4860a97a7a4dabdb70f75257d0cd69c5ffa7670ad5940dae01921a6218ebb957dba2d5c13c06aa2185116b1bb146d168436b376a1ce8523505 +generate_signature 7f75ac1c1d1832d78136ae9459b070982eb03e91fb4b686f17e60be644127d2f bf2d56a4e6d6f7eb153f31bd9f39d23a9e8d06ab8f906c716dde473fb2d16554 c901408e3a9a52cd7b95db0a8a5550aad0af38190c082f5d8228e5587367f500 8d578c33ef47b681ddd5407a631c72a78efadd74515697e2fe6be0afb1b31b0dd5064c6aaf236eafe2d1b06433dbc2cd9637e02685c3915ae3b88cb768ddaa06 +generate_signature 1772e5a77a57fd69d6ce9a68c81bd9c4f81579741103a177bf584819a656e1b2 2ed6932a62fa064c9c2e39c7ab0ddbb6786dc849286af8e35655a58cc9be79da b9446a654072103d6e331cea1425472e0782b532ba9e235d61ced65ad6ff3705 e6aa025a2ab35bb9f141ef2e7cf8b4d18aa5e83649273c2f19199b90901969064d97ae17c96b207fea4fcf4e139bc0c516a747fe927e119c2b9f3a2d1f615108 +generate_signature 8bd02d71480ae9f17e999ca813b837402c999bedc4374d416e6aa9e233818f7f e0380564647a85532bf68ecb662fd8299921c8c51982b6310b4a6806bc056dca 9df00a62dfe8ffe27a34b7bc6a24a10d7f147d620f3ed78953930fb845598a09 6c8dd0c7a57927317469a5b6351666095cbf9c234da786cc7126033885103c04c0a83dce35c4f1a43820defa9cb713c707573b7ec62a4ef36b123bb87aa85700 +generate_signature fda1233d780d70dbc9580c2b80ef1806a8ea2df30c317f7891462d96fa4d66af 5f769cca4be9aeb809e9803160b179b1a7bb7cab0e2b0f6311c9e29a8db20de8 076d27b6f85be7acbfbdb38d1b22e1d75c139072f7ae9d33bfb7590b474abf08 56d25ca5d6b9d69b90938f898340f8b8a4e575e0a247193ce8a3307178ae7504c6405de28ee8f385e7a58059f45e884a97325c27d9d79f9c649972fcd8c5f806 +generate_signature 80a147a42fa1e4f83d3915888f5c22ad71f92c0b2e1dbef9657330912baac097 add3133bbd5b10f3b34304864277b2a335b3625afe0d188ea6f60ba7302cacf3 911a315f1f7c21c97b7dd0385da95b6bb06de39796a538d909d51bfc07439700 1093f3472fa816e92a742c899f06e3b8b22ba9e9ac9212ad652ac91df7feb60f16cdf4f7ca6369b9bc6959acd7ce2271b8d1486c98ba2059342f59ebf33dba06 +generate_signature 870ef0ebf5756b28ffbdd4cb65c2abfe6bbc366408ba42940376e91c750473fa 7c91e023da55901252f69ef58bf56329bb7ab688f444df00c1dfa80916a1851d 1752295c9937a10004ca9884b676bf4cd5608e4fb882f733ba1d91093257320a b52257abefcf92febfdd84a62c6df43938ac0cec9d64d6ea6fc6739f3c6eb0060a06eeaaf5feda7b63e7868e3afd068fc669703bc4524d6de74106e7f7512c0d +generate_signature 711f84e938e19a461e28c770573ba9220b84b233ef6494e1600b432333bed7e8 2fa1edf5af46532c7d290322f145d44487bdc39de5b60d365f9d2937de5bc4d8 86b9d8c1124989a8d5704873ee831f1620e580799fd46e507e55c9dbb2bde40c 6b3e238d3fa1428dec940f3f10f7cbf75c6024cb518e72700c5feff1a8cf5e0f366eaf202c6fbc2afcc987f4e76d56f09649d250bc98e9fe0fc1d122ea70db0d +generate_signature 182159820cf794a0a367e94e6181cd2bce0aa7e8628c89f2aca4315136d58bd9 6252042e2d1f1694b73799d9f4bb777e7019ca1655c73dd79715e42f473f6c29 f34fe47fd0f4e59854e9b1740c91336978ef9b72bd07b2cbc601319adb261a05 c079d67bcde1dd9bafe531e6abdab409792a7e41de64dde26205829fa99e33056108d71a6e48e7d48cdb5a581a3d457bd3daee313893fdbfbf6181d3ed4ce104 +generate_signature ac1490c8b703addaf061bda16c13c0ddd4ab06da053317ebba240be4e794b7d6 14d9ac957f7b65d0a8e55488d62a60325caa227bd28aec1d6a4f7fdb83a2b6d5 1d9d9a3c2200e2a127b68b06b432714b585dfca9fc8bc46c44e1a7fe7462fe07 d51fb46dc7233fb29d8c183e1bea7c2c015cc52e76403c05fc0b213c4f9e5702b4784fc70ab883bb857df5f3b64cbad205fbf28946eb6b86f7c66f2ad6fbfc03 +generate_signature 6712b724736993684f7380ac03ead9b3df8c8d0f131d4ea99ade9d153671a3ab 69f90161bfbeae0720a5e0463f15814ff8267d8b742c2eb612f387f08cf32c30 74d48447a77283726f32f5e726074f1e3173d05a55b9e14796d2ea5436f0ce0d f533fea5941d67585bc65d61e8c661460b1fb5a354dc581e271dc4ea10e1a008163acbf86a5c9f9e6515ba7ddd5ff0301b690c04777f9eecab05ed6a8f2c5505 +generate_signature 2796b04ccbd3ecb7ca3182d12bce035edcc049e6eb7226977e914c22b406939c e02e549729c483263547dfb5e4280d5e4ebe90a7140c79c0eae07ddcd91f2266 a81f6fdda8b3c5efda411f6ae5830c151c1e09ddee44fcae52128e2b4fba540b fbc28bf52e6a2f2c8117c68d3533e71f4538163905ec09e7ef97263b3935b1048abc8959313ac3517ad54b3b973c6cee9854138a8f59679d27f2ef22c2989600 +generate_signature 6b07f68fe4e8de6a194532606d0f64bb2561abd14d66cf4dac2526f8a5f40763 e21eb4c04c5abd7c4b12e434560c636a9c0fb4df380a207a93321e1865a7aece c194dbf0de2af2b96b1af7fa24b4b29f1291088413c9f26ed98954856c411d0e 2ae3c1b1128a4fbeaa74ca1fc3ef5d647e3863952a7a0ee6c001ae7f19a2fc0a4a7abbbc4a846f36099f45bc398f9b29047b48d7a97cdd8d2e1d7dc4ac24ca0b +generate_signature 17bf7d4f22e2e16297c3105c5ea01f9d7210b77a02d2ddc8d9263b536c1c51e4 94fc695c28959e0d68fe1dfaea0bde1a2b11be6eede96a4ab65b68ef0a98c082 bc021c4b80b42671b43227544c185fbbd90d29b5d2f110d5030aca995d0e2905 8df0f6c7b2233d0ea35e48c8670e9eb6911a691dcc0116c87a265426e3b3e1092ec5edef5010453722f5f8b115f15e9601681e82ddea327cd188c5f0dc8b7c0e +generate_signature 0833ccd1e71d03cf19ab214685f348fa88e711bdbc6e7106047b64a151fb6c57 f6064b733fbce4e1200382adaf77d93c542a673573fa492cd4260fe28b14ee16 01f9a1785bfd24fa38af0a5d0c8324f8946b9d0583222c53772e69367597490c 4a4a9e8994c948660dc08a7742f1a11da0e0ad992ff1438d3bfab12af3dbc8093d9e9228a539e9c0281791b7b33de73c4fd67af4c04705e4aba4c1df70b25308 +generate_signature 2185872fb1e5cfada7668e6914ffc05f1c5fde38a7319e79010a8c1fc1c2bf28 23bc7fc2dcc89856410f5f2a006f4030d7880226ec512f8ce22926c569b557e1 e8ff76b5f7d00a8b8e6e2fa40b7385dd86692bb197cbf1220f3b59f00e149604 a3d8d403e81bbf654d0d3914e8e3f1c7cd76d332c7f3f2e8ff12952e5f7208056f0dfccb8cbbc22e822fdbfb5d75b1328544f1542f9df165b78a4e238b936406 +generate_signature 690194220a436c23dd6863d2b7ab4ac41cfe4ec6a6bc6843f1efd4388f37ee46 950f222148c84dbf4d586e7f7a2b53fdc43e45e034d0363883f50059302e5074 46d06e7cd27acea2c4094cbc3ead11d40b800fdde1a0fc1f687048e2b7ac8602 905a28806225128eb7d1ef47c6a27431170f5374390591f69ea98772cbff6608296be2412af3610ec8fc3e9d492d11cddff156e90763cc62c427603795225d01 +generate_signature f2f674fe63b53dbeddd1966c2b3a3fc7d647f63190bd0a183908f782ee65b60b b75b075e9ce254f9efe7baf09897c5f58263397a0cedd458ed8c123a552e08c8 5f2ef89fa9956057bbd7654f09ce75f82188214ad1c75d8a1edb1f954689310b f3d64d9b473ed27e34187713fe55193925c6c53143c1c740913160d6214f6a00bf412c34081a30e1863b4d5a4e1f67a50be806fe81003b611ef42dad9a97330b +generate_signature 98e1472b88f2580a68a4b10b0634bfdd859590805b67e9b2cf8b76be25c57942 1edb8eededb14db7343fc79bba30422af90e92c739492ba1abca24a33808668f a8e7bac4b9bedd6cf89f709fc97a5d9c7180cf8cfe36fea26f3931192366d200 151ea077a6afbd59a303864bfd2f0a0882f87724fd72f03c20597a92cec8800cb71afbb4dbc536e3661572cf637e8691029e84782ce38d3a2010215ceb48af0a +generate_signature 1ad499a52576270197f365c0268892d44b73e9130f70c5fced7746498ef531c8 2394d9a89125d99e13502169301dedda64cb38509465e972b6f20c7f098d851b fe04a7d2a3e4e8c2a870c774483ef0b122b8c01813914e59042fbb76a59a3a07 07933a6c63a4dd7838ae7757cb42d0ee3c5e807a136d0a8d3d50e9e2f4ac2205485f670eb484ca18a4297ee3d5adfe7766a86f6ece6d4e87403d65aae92c6509 +generate_signature 43038508a053508376f5d0595bd8cba556610e673b8291a322a2b9c72dd4b214 4934f2132f6ca4a2aaebe35ee547f935fcb28588e8061d7da267ae0954bcc987 041a55efcb7dd92f6fd6c2c2ae8c93af8d6248a27cf51c3d3770a0712783ff02 4c05071b1ffce1a1735437f06dd8227870a8a1e6afeea04f313cb1dc28afed06213f52a3d24b0511d19052014a56bd3fb4d3eadb73276aa2e5a010bf4b8e1800 +generate_signature 4f9141ce176f61503a42ad71e3713b8a52dc56505809c8d6d98baabafc4e9910 516aba422fc25e0533ff24157c263ec3a284e1dcea2a8a04972b00c29df0dea0 23a237bff637b3008b429465d3223cf820ca4db0d75084831ab81808d4878006 02a732f3701b59a4420ffe5c5fb238ab6d0f03032d2a855d2cfc1bc668ba630ed4273345ae0279871c1d8195a529666bd0913a346234101b568e2db483ae8704 +generate_signature 13dd60b4ae30f8146fdc6a54eb8018493742413dfd267f18bf0d21b380dcba18 8b85dd2808f4a567bbe3bc67bd11209c98fdd6d177678afe6cbc6fff19fa2d76 396068b93693fcc4afac343f8955a705d3fdcec131495c1b131e0ee9fc7ff80c 7f1208925f3c8014a5dc3360660865d6b8e57520c715868e540965e8e41ded0812d602d58d26a4a411bce1b6113bfbffb2bce4ced22fa844cb4694d4b0ae1404 +generate_signature 72b57bd7a1dc353f62cad2299a225484014343a40ce052f66ef2bdd581f3d5c5 57427bb8fc70228d84e191047948c9bcd9582c8f1b3118f56064eda938857a33 daf6de1663b64ca4fb7660058f16926e1fa33175f298249183c6c8d862712d08 96979747c606aa3dbb54d2dce4ebb8e9369216eae26c04635ca8963e4024b8054f42b97aac8bcaaed63917e53576e4921e451a9e7c9fa9daf11ac36888718507 +generate_signature 0e5824738082572ccd6c9ac2b97e37412b432a6bb4a1101234bf77d2a2a1109b 9ddbc2b910f572818af16baf273026b8cac3e6b7f2fa540a9bec2b38d2020dcd 3f4b0832a84cebc9377eadef2fc78a62f6f32576258c8d2424d7dc7669213d08 dad4f5c4eeae91ba65dc3793bbaddf37e18c1616adbb8863d306d78de10f2b0782e8cabaa9b7224f461c2dd7796ad43f0ced94f24a96caee506a85ae66641000 +generate_signature 459f98423e876494700752609232d068596b762ef006c299750cb3ff573b0104 c2fc73baa1d5897f75668116cab853bf359f156e24b19a745966512f6835bae8 9ae7c3212e6695e0f5668d062b2291b27bc45affa1f77b9bd65951914a842d05 7ee993ea51f836088a5ee78c56cf11e49d1c92f65d3b20930486fd3fda87040fd6af90fbe5a9e38efba148ab5c28795b043642ae76688629647e068c630a880d +generate_signature 6d176d259af7ee75ad769376ea2a5ab9accd9a9f4d878192759f5dadec194f35 2317f20756c1462f4d69d36275fc14958419769167eb526fe6a4f3007036d474 1bb97762a71f9aa32b2f7733f1fe1e0bfe8e654ee6f8361f83a61a22f32d5101 93a10eb03ca166d256fb193f4b57aca1ec0a570375e55d77bf5f97be15a2e800c5815b3b727c9c8be61355018ff3bb0c0abaf20332edf92fc4d80b674c7b5401 +generate_signature 88989b9365a33a6a6870f4978d8a68c2b310f7f6f6bcc3c7342fee9a2985400b 09ff1ab9622a87b66f39804ce7b84c1620456e83bd2ac9fce8cf3c8564170543 f3cf70e5732cf0b4fbf81f78e95bddd0166aa597c2a932ca16d9e90a2742c80c 777f41771e4af5d397ff4ec6bca78510e06a8d13eed74a53679d40bf23798d0d8a1c247ebc86bfe877badac16ae5c589bf9e1b729f427ec944e3ba70f8458d02 +generate_signature 72f08a3499045d8e2a14b4872a89a7b53756fd3dc0e538ece7093168c6f88b06 26ebd8fc7bf1a0c6c1c8f55f701d66411ce6d2d2a70f87c2a5ca42e0f8c8b359 75c532399d4cd4475ab2714f0da1f6b1f131ce4f18144b69c99259c58b5ac203 ab59dda6eaba38b990c9010cee0970928bcb976e5db234aa8baaef67bd38b10cd40c6ca3bba133b5aa281b95dda4a5b705b88b0520e07386336444ca4c11db09 +generate_signature 9dcccae9215809a2a124f8a3bf62b5bbdd0ebe6d7710586c5355c7a7283b0e43 cfe96abf54cf0ab6895ee0a7c9fb90ec9ccd654fd526c36d2370398cfb7e8f6c 911e17b1af1be9f9446dda5d6d03de66c64d0a785b60a03ab7b26e2068c77101 6e7f046ef3a3175511576089e60cd9db1f415b1eb0ce81b334b55b52b7ed040e2d8f6f103b71c1c19e7ffe1415433dbe1838fd88e7acb219c19cab1186c1cc04 +generate_signature 841ac5bce833dbb72069a88c959cc59561e7f05baaf63bb7fdc98a4d6e1dbeaa f3e8bb769efab6217972ced819551c79f29bc32a131a2ba05ab26c33d62c6ebd 858dde600179ebea9ab1806ab8e41ed2abb320ba593faea28eafbb5a7f989206 6679adc8583aa23d5b167d4a8653bfa7638c1b8c383d58b0fb827e4c92991c01e813d60d8221ddd25d638797cbbd38a59f7fa5e582afa4445220eb4a63983805 +generate_signature c570dde787ce9fcc3d48c5ada9054346455ab9a11a1d65b0eb223547bd39d7d6 426ccc3e1c17db41bb5c975d08279f1451c00976216271c9bf221910ad1eb8c6 4979107437a699a931e67dc36312a1c0b5b5edd3a60c1b3dee3c12596e500107 b1ddeb09a0ce43f0ab3817e5619da7168e784d8086e60b30214392839aba2d0a7eaae3b6c4f206d7a4b8640d6068b447bd9d129156fb2db30fe1896695eef80c +generate_signature 62e8187f216da0c992208f3f7afde4e5ee42e0c09ed9897b010430170bb87350 91eb9815ec0a5d2d2b98bde3a097db217a922089e11c4b15d9e4109b6c534b56 4d2234323011d6837889e21b7482c69925eb483ba5b367ca48f5360cd92ce708 5bffb00d0dfd3abdd2d2d6253b2f3276eef954107cf6a64a020de2afeeac4307916569625c8703fbe5e5cb95919da8eb23fbdbaf42f734f22428469dcd1bd908 +generate_signature 49d7e10bd4a42988f7b202cce474c577619f9bea53a7914de4d295b8529aec80 2daf7743268523b94442ce3f0be3910f40771a4f18a22b52f3132b6b5ab2b3cd 5e6e1c05e2e836a936e17a1765b30165f3851ab56ca1e38ac459c8b7a81a2004 aa8f7f10e3da2efd297659e0ac753c6fa3faddf72a4860c5f554a6671d1cd901a9a63c340360fb81dd6bf9f0c6a525682ac8f232ee5c17f76e07bafc5a97580a +generate_signature 51c27e54ec56e597dedeb33c6f2fe379383a8df301688f95e37fb695ce64b1e0 01487d5868e86da7cbfbf00ae1bdf8faf8d51a40c5be011a1ef11a2734616a38 e844859d1b9a5328533fb41383c1ed108a110e6413c044cfec538975bddc8405 9ab80828e02be66b482b0ad956aff1fe9cb947f314fd5d799556c8c6689a7901a35f49c69c05b4990a75405d39d88dd25cfa2cb05e9316b05e97240f81d88007 +generate_signature a26bbb54e14b7f7587fdf8b0c308bb80e3bd626c20a6980cc08b9f25317aa00a f248b297c284ef1114629819876d311bc01926a75899ab339891b4270ffaadf0 7c7af96309f390370a9a5031b9bd524936233f5f806b9c45f3821b91da8b8706 6f3a02fc2e6c79648a9238d75b9f71c2604364d2f81f9b8b4374ecff91e15000a2319cc8bbc046322ccfb91566a4d1c93b67c499b61eaff0f96fb3b650bc7002 +generate_signature 18937794c31d063933d48dde3fe7b5c6709f931e139fdd3cbae74e68abfc5cfa 4abf6a6c47126af111ab9761d8136bbbefef57cc6eb6ae4693803ac915351fc6 4acac844a196b1754f42f9af15c6aa9893bd00b5f466fb691ef396149171ca00 a7961fd5ed608061cafc6f83eb2ce21a0399dfe8733c899eaeaf3d5b6b26ab0a700b2599bf08f70e20acfc449b7fe889d2b7895097e936bcd5a97f5fdb45fe0d +generate_signature 0b32c806869d8b25e120317582013a95c995f9347814e4e37f6b948b4ba1e2f2 47397e6dbce5916e87dff2a55c63e49d1bdc826d4ead31401d47cc0a37d99352 c852e5ba498eb8b9686557309591b1e9b0533a07c97bc6a8186c036bee2f420f 5f2b0f0f0d745b96fc05f9cf0c57549452a32b1f36f5b6e34af8ecbf962bde07a9c60870865456ab0c0ed6a3babbdce3c2ac89a28b851e37b1b151a635fa2e0a +generate_signature bb5e240d1bc7e986e6e929dea3095830fa11b51fefae6cb1bdc8e060775905e3 7ae97231c99afe3dcd855aa587f1acf5f62ea0c61cba1e0c14b0fe4fd26a7b1c 25cb0101d7914aa49cd52abc533a7cdc00f174ba107cdd6788a1f64ff0ae390c 0fee95662712429ca9cae0921418f8dbf15accf71ac28157ee57256dc074ac04863125ab2850db5948f51cb7e9a00f521f7b8369d2f3485891250d7101945d0b +generate_signature 30d230c4ebda0c60892e470dc95ec1152b00c8176c6a93ca73b3057cc7c1a494 20200fa0969e8ecccde73758c864cd4079a9efc8476776755390b9a56800be07 4cce1e28f0c62eb772138560e232b27c44ec7ecb7081bfbe07de7d7281c3da04 88478670f74a44813a8b95aed7b939c730a7c302f3d473af5da9b0ea27db8800b7a584a51b98200176de2a0ced978802bb0e0f6063042f6bab0e901eaceaf001 +generate_signature f1dabb3ce43a519e5899d465abc054635e9fb30f3d49dbacf2dbb54725041d61 2be7da84b3e47473568338ddf79a8c87b22f29ef4295ae6c6ca463df3102278e 8831f066a87841b2beaed0f20b93da8cc730e303bd135ca540511c6f697f7f04 e742a94c268b69f404d8d0b9d025f99e0b3a297c79cda1c90d8f482e900969013c5a4a59cbb4ac94b0dfc7a0ff3f99d04f950da5e94cd66b8034b481d8f3550e +generate_signature 5d60c456bf82803cb3830fa2de437c377f5f0ce42b762a0d2a5847b95d6d4400 38aa6f868aba852ee5f2c2444212088ca48268451ad0bdf5b42d020db2eab246 208ca6972527da0e4333b6d756e5c3f336d70f1af86ee8088b050e73dd51810c 4dd34aebfaa17a9db10270137e87cde2d7795df757078a51bfd63e4a0b7b1a04c93f48bd6ffb2e21d31b1c2675e22e1e60869fce534fa61f84cf3ef4dbb7510e +generate_signature 58bf45b4eb8cdd324173894cbb55511bc69ab649e1dc79311bbfa58c6c0f9275 a429e66f32ec3d35fa825eb5b6357f4daaffcac3a11c47ceb80ef1114a06a217 c3330d61f993dd6652cba521461f4280d83330119a6ae92681b708ab32418904 8666e962966cd44f2f1bbaaa102b192e4f86f9518895522c8437d5cd070b8503de647dc701fca446ca46bc2bdc838021b551c13d5113e362879a430da425d80c +generate_signature 2229a90c79f0e3dc230cc3919066d3e4b385ec53b0559f7ca8b611ee2f29e12d 83d93b52f0df0fcfb3d6e2b13ad01ef38c494090c18014296eed91a5d988b2ac 3e286618640c874daa6e36dfb01b5e2d1f5d77b8d9ca6f5ae5b7ebe659b46709 7738834b40e26f543dec2c8f94289ba12db18a023274390e8911bd2faf2239055063d397e3a939e9fed08c4b88dd2d18bf97ccfcc66483764f3eb494594d7c0f +generate_signature 9e47f640aaee7ab2e09c8fc12a32a531768258c5135eb8e521965d8708eff2e8 a8411a8de998e4b6be239be96460ec6d4f4a8b616b94f77347838212ce4b8613 cc4598110b1aff2282997a410e7cbd837e12a98461a6c6ffc307b77a323f0b06 0d40ba0f2254f4631bf4749320781b75da6c877a536ee6592ce03983cf4e7a0a1bb35353383b77864bf849dd496cbab8094935ef331052c976fc2086c4c04204 +generate_signature 1fbd47cacc32270ecfe0bda8b1b1bd9ae4e03a794016a2696eab9cb0ce2c40dc 41d8ee40da7c75784b464f1b2fe89409f898fc57f395b46ed0038e98c184aba2 ce061c022ad0ad3125b6cc34b0d27291874d29575b10e1d44528ded224b1500d df75e84e803edc61ebb345d6cd96e28f0a30e0e455839d841c18c236671f8a05e9410f779ce84447376c3b0312aa52595bbee530966426713f5c68e353ae3109 +generate_signature 0756833a04e57930ad436ed6e50995529036c5b55db3f9ef070a000ea7b3b668 ef541d76406b4b71299d45937ffac3a4d284b61faa8644775e346ec6f6eda51f efe31b98bdb433b3535e792aafb0373d66952196bf564dee8c02ca0dabeee40b 3ab06c0ef27583ee933857bf6f8c195736b52dcdc23098853616cc1d7a07220f63ce77582cea5bd9c8daff96609ab2a57575927986047a54c5833ffdb67ce309 +generate_signature c5d9d4483c4b9b23e21c259fa19136f0c69cf2cee896945510e95141ffbb195d 39e841d4ac6335babb18e8d9e47e1c75111639709335a1310db0ce2edf686871 6a2b88bfe8a66655b29e20294d715cccec610efa3503cafcbcb5323d8e618400 185a330e61a207ace347f4ade91bf02edf89b6f8433c80564cd03461ed9360052528e631badc4e3c8c18bfb8aba9c493646cfe098e84da1a542b3fd8ec7db601 +generate_signature 8bc9f9c21660ee20ffa666e7be110d8accd2d1be0550aa5bb3b4ae905418b060 14b57372138f3995b35fbb964ed0481969684f0338470a637e183b9780191372 85465783ede0e697db9aedcaba7f2aad3f4fda534db37b9654a3325dd4361302 498697da4ac1809e3eb302b77255da7d3c837ff28475c5f8aa573d6b19a5770bc0ba352d8969758646cf2f228944f29d8d58b04b026f45a0bf55d1c67eb0e109 +generate_signature 7ed3d902f3ee03f81674c4452bb1cf33c0601fd27e25d5aed0b618b55aaf7167 458fa36ccf56bcf966910083e625c7efd4114daee53c45f288ec2cddeb444aae 3a0e8fec3301b4906383419798381d28a2a26e9249a58097734dfde9b9be370d 1c6c6f2efc425c9a479214dff275a1af5f878a5ce4544746875c4b40adc057098424b3f108aa13aa47190e3360837a5630b0d5d487911ac366c0d49426b4340a +generate_signature d95b625b3397e7726956f869e61df3634ee46b08a914a6a0b10b9e427a9179da f2cc630788dd65572d325ce0895de974fa3d56e07717ce2776ea51a32a542644 921c6a6d736c246dc6bef590f359f7093b32d56b05cd8639c7e94541caa2a504 eba26d1aea4ff26bd2c2bfb8e1273598158fb06beca35d4bf5438346d17f2f0e94fc260310c6772989965f6a4d435c7091ffc774769336d0f24de616caa45203 +generate_signature d17c7a1189954c38343e20e2b8e8a290e294753072895948d184cf0d1c3193c1 2cfe22dd27f8b7041cddd1b8111a29a1c56b35989aabe9edafab342a484f372c 9317e035d8bc4a451e52ff94e5594885823ad2cdc7eedf66c5231a4f81bddd0b 90744f8faec7ad521de63578935e614f5c017a11fc22d215638377377ba6c80ccdcf147278879b9e501f67d027a1aa230a7f7133addcb4486484a1ebe3e51a03 +generate_signature 9387ebacbf7d29655f65513d886d006af10a8c75a11cbcfef8f530a72fbfc972 006e6a957483b2f082fae65ffd18fd525bdff0d774dc20ca0b48b9cd8f592b2d 811da01862356cb02275a0c7878185670732b10c7de15ce1b2dc9ace1dbd8e09 27b0b5f87521c91b211454442d72d9a3723aba68369f9c8364fc6117a28c2f0249848764fbaf65399c46c025194aa6f5c86cbac127c26f99deed8d553d555408 +generate_signature 307a4932ef8f15539d70e8721dfa1a4142ba4ed7b2a734f8fb37ffd31f72accc e48b91027d713f4e5ec7d0fe315589b36d6bc6ec338e218f31eeea8862209922 0fc991a39d51dbfb559047a95078e8cee3210760eb9c0ca860e5fa48e267be01 297decad20d429f93736693898f434f0efd936f8e271056184693248fc60c70bf6a126e76e14a535722ac8732373c6a8e593540d9ad695c64ecc9b2c24a24204 +generate_signature 4366484aed2052c6c28d935b2300b5a4e5172bd79d96a37393df3584c0020b2c 39ed6f8df7c2d04ca843e2e76bac2c6a4de490f9c8d1c4ff16b70df13588a4f0 de879609d955fb6ddb4b0609fabb4e49fbc7ec017952681f911552eac8ed2d0a 651f4cf9e28b6ec1c5ff34f3e8a2b0a8ff29ae0e8c2bf22f030186a77764d20c8a72f89f358e6c727db157042730c3cdc029da1186aaccbcaafbe17ac6e9ae0f +generate_signature aeac4a1e1552df635de365394f86621ee2f21f7561a71a63c07fffd53fdbdc0f a6a9623f5791d0194199f52ea1e4c77be18a94541e9fc8d86b591ac8f1c0c3a1 c4ffa6e43fad2e9e558118f7b5a1cd2196453f91e1aa3691013209c7f7bfd40b b37b64b57d2a2227c7f47591dc189ca368e39b92c8308c064af7fac34c578d01e13e37bc10269f2e974fc1814172d9f4ccd983cc7d74c48fb57b95abd67cbc04 +generate_signature ed905056a502fe94edd1c28b3f65491330b58a1e2563dbb8d8369651a2b30cb1 fe89f71bf6d52874eb89f234baff8c878ccd22c5c614ad18eac64f1d49ee00db 9826bb63674eb73980eab3d333d90b1ecdd18a227a34e729ac4aa4c679570708 ac774fce9ca2a9bce0b22d8c3a0144e938766fd6b33a8f6f573d61a7d00d140d548b820edd507aa7665a0676206f14e97a97497c09a6ce8718dcca0e8dd56608 +generate_signature 327024bcbf89837e82f4af135ab5bbbae710d4fdbfd771423dba6d089d62ecbf 089a1ffc8c9ee8b28e3181694127146615db3ca57d4f808e375b0844d68e4ba7 9c7c12aa4310db31790ed6c9041b711f0c742fe3c903e3093e2589de9279130c 9484e5fdeb9bb78ae4cab30b1bca3a9616959c19d7e8b284a3f10e5da8406708c2590dfe2e5fadc55bbd97ee7ade1a36f160c3ae7416c1f030ba568301742e07 +generate_signature 5fca639815dc14114fa452346ca15db70f4f3c13a1bd1dd2e5cc6633bd884a97 0acae623f287aa3b9758a36ebef615ce9aaa16f619d3cf56b7ceb515e2ea5c27 11569cbb68a9b17a1ac43c5b6b87a9c94e269ac507541b87accb5681fda30a04 bed44ccd0bd1cf2ca84310dc64deb9dbc606548bef95b98256799c6b728b2b0a33073f8b3fe2c7218a0b8d3ae3b489ef247c7680d8804feca0302ecc39ac7b05 +generate_signature 0f217303b1076cd895641ff1751d2d469def5e140e0d61dc1a4239c3decdb382 d0cdd348b338fd12dd2602b2d5a872e820e1d9cd6f0b2df9c5c4a04e49559a62 fad9edd7ad17647a02d8f2077b9ff3c0fc755e5eb071165bf902654e7ff1050c 903699943a3dad0bd4fd72b80380767beaf83b352fbebab074ba926776b5b105503cc3ce924fb520c580ef21641d75c1dc1c9392dc8d706e9da9501c47152c09 +generate_signature 254a879d64f8532e5f281149b52a68bcd96c2148f33e3bf7cabb4dfc3450043d db123979ed900409c0090c74705fc059ff72c4e1ed05656b117327e7d3f68d4a e39e1a551efa30ce0b2461796147ebd86394c8c0abcd8fa8118fbd7cb5f7920f d44cfd92bfc3e6822b638dd0234d287c099741fa4939645ed4144453d4f5830740c55ca4d9847bb38454cc11e44a46908cc26a76ff256104acef9ca11986030c +generate_signature e65ec30c477ec561fd8dfcdd1a3b89c099ee7c415de5e780618433c3209b9824 e45c9e7b6341e47a07e794556c9cf91869cb97291fecabe4867e036060c81ed5 860e36d4b74b80737704a4db0ac58e4181d807b6a0ccb4064dee200de67ae603 7953e307c61b9b0097afc72b588f53237ff3ab1d85b3c5cb773c6ffbe695fd0affddddc9e293702cc9b312b377b91cedf50085bb7fb914e1bfe83c4598995404 +generate_signature e701f76a8a06647e5e10c677a9fc17ac37c628580bccc5500f34381536b88b9f 1bfe84084ebb54bfe97e3701eb27cd77e585f510270dff9b22aec8a688d0512c c7d74b95fddc2dcfbfa0ef72aa1078f62b786f5c0d0650782a2913b5a22ca609 13617b56d84c4e269981e2eec20b8924b4af22bd686d2504c46859ea49811303ff3f01a20c9816e81746a80e88d5df1a5238232ab3699d4c34565f6bf412b30c +generate_signature f220bdb877fbf2bad58a7e98cdaffc0047f8d4512cf884e45c484d5b2a19275c c37d89b27a7449d9015b93669810f35faf07908d5fbf4d93ebceed9c1021770d e4692ca12eccebdd039f9d234a083b49a184b7d241025e4b27d022fb9f46c802 649f3ced8a90210f95324e8ccb7fe3048302743a133c7e6992b79751dc855a062083fc8b932967fac79d0a497164b20b0fd234fc6c7bb3da9f389ecc03215202 +generate_signature 2de9420d201089ef5c3c1fdc2ecb69446de59b44b84813d4f2c60c4762cba262 ea272022926e4b6059728e63ea0530c064280312e22e82419a5463b34b5d223a eef8d695f5b62c90c77bfd4a4e257de3defea7eb93cd546db2f0c448deb08d0f 689a32023340b961e528ff696ebcb0763f3fbdac8c1ff23e72ec3c542ea1f1014af7ce1810fcda6a4b71daebfa4369040b3e54d40a40934f08294ce064f3de00 +generate_signature d22270489687f1aefc08d45cb567859a5a7f0fc6bb4346ff56be386f3c64ba68 b231b42bafbff5286ccf0828fa423487e3edfafdb3cf84b4333b92f7c5f8e005 3d6b4f25367114c0fd1922209e6d6ddb062c959f0643d6b5f5a54aa7a8dd850a 39d8bf2d2134e3ac428ea055350b17470a0a31a59e4ccacf49859c2cd123010313f312176309f64fe2c06ace20d3a027765d1cf0dd73a595e3f1bf0582417501 +generate_signature 0f80ee01c68b1c0fa0f72a6890a523e2f941a934d5aea5a37756fc9308e3d149 40485d2af8f529aad1cdce430971bf593c40ea5f650c65d24ef2f3a1d6e09844 ba73077ab95f6b2185b68884e757c95ff288af975d70f54e6ddf75446b59ef0b 6284f664f1eaa327e9614ae44ce702e465a3f3eeb58cdadf97cf715dbb2acc02db17d32bc9fb0f9336156fa512283930c3ed1dda52e7b1d1f3f1c82d15b2b50d +generate_signature fb9279828f9075e43491950ee0a6b55f2740a03e2803e20e9baf42a71845db16 a60c8215f4c3144d397db32359b1638a94bd48efb4921488be9bb2dbac2d3621 24d0feecf4a09080ce8cfd390619e0f0c9185278b712b9a22f23fe6d1b028105 4b919c7b5a5387a1d791cd9067f08c77ac3b05306825bcaffeaa6303f06c8d08f32a80e2f40f3cf3f073a4af3718e10edd74db85c0a2b4be60adb5bc4abd8606 +generate_signature 1853a3886d3421c884701015146416e2372be5dff4804a87c367d69a14ce1674 a469f283112cb9e1247cb12358e1d1f0c2c1efeeb0311b4921a4ec3c2fa6149b ec7f0053fb49ae322026d8dc884d02e4784fef17469b85415f4bb269f833410c 349af7c50f673ec3b90f2a3e7bde23ce1d592730064d2724703a94349440b205418c43ad614ad88b6975c230411878ea43d76962e0d370246943875126e42f01 +generate_signature 87a4d1813017c757f2fc7adbb6346bc711619edaa8531378e25686bdde9b54fc 8e1cf8a065ebb111aeaee0458198b02e9df18e357e60604aeaa62f0e7e81fb47 d75a74c80860cb58614643bde56d7d0793c57750af954f4fe949e08d73e14402 0cf654c8d5dd5e5e48a61e3eba21e68b6b862fd26ea66a409ebe60f6b0d67d0c87dbb4859132f7c7f87639dfa107b3a198c3f912b4d9a22937e2d9d83f630a0a +generate_signature 36332e7b2431dd072a158803b24744d01b69f096aee41407d70ccebda99984db f37969dd37feb2205fa2d7c87d62bba58a675b9ee08caabd2496886059a5aee4 fce10979cba19bc179dadec71cdbf640bb3fcdd3afd07dc0e61124d293ae7209 2adc6d4481f5ba5c18bf7393944f7c8879ec4c8055241f7c1835b514261d5606acce13d49a141285ab9cf45d33de4da1ab86acc47f49cf4df4081634db7f9b0a +generate_signature 2c7db34f2f249ab08724d1a679fe855bfab2ed68228668c7e712b4760a839f63 ac948ab799b000d6125355c96a4c7c2f7918831e2cdb86c66f75a84ab3b588d8 711ee793591c685bde7f3b136751e5a39d55f0cdcdac03631ece1622a5982904 9891c04238700e30e40643aa0d7446d218dd0f0a976192743e52464428e33f0e5c94cae2fa5e8197846d8f4fcc0198f26dda091e1e692ef61a22de077d7a7900 +generate_signature 9f92e54ff6d3c2033a5ce9135c8f079a93f0a7ff4c94467d856fc63c0e96fbc4 c6407e167719e2dc8e9e9d719b1ba7be2797eed2e43dd27b02e8b7affc6ba801 9a3b191d1806472ff03203d5602b9cb73d3f0262cdf56ad70d20119941f83e00 b254c6dab28e8caa175c8efd122106baa69e785af9228445c324ca3306059d0378186b1bddb287b4351ec38b05131da322bd8d1809c6a4a76dd3ec7658ed5201 +generate_signature a70f0611b8337641f0d45cdffc56bfaec855fabfeb6fcd3da1292206b4a03309 0baef43d5b9d89ad7572c413b531381745af1dcfd1ae90d4b20f0f78080f2ccb 9e5527c3b3da09edcebf39183c5d912e28f47befb833177726833e281748c909 91674393a0af7360c4d8db85d49c8c8f373e347d3d38e8f1e6f74aaffe46610da791ffd69ebd3181a705f041701a423d1b9f6bab51b6ab6725794812b93ba205 +generate_signature 878f172b5f34e0b18a55df0ca544d7dab961eed79e07fc4689a0a3be8b3ecc72 03971f351d3e118213c1762bb564a2b56e3df37d3217665f16f5193fb82f8a5d 213d506a9731bcdf5bf8c3968d89e7385ee573aba69d1572c3604cdc29e9a200 3938dbbdc1f3d9301eb69faef8997bf406f7dd811a047c721e33986d159f3b070214cd952883adda3bbe9906b818b4375670e1f5dcffe14104d91b395defe101 +generate_signature d5c32a4a81da9af917c63f72b91c14e0a8fa93413572b702cbb3e9594481deb1 b558e0724567f7b4b08318ffe6b0dd0b53cc4a876ad1530692f286b068d19f9f d5a64d1b5c47ae79629ecb770ccdf0bffb1148869360958202ddffb2b5a6340d bc781f0fac469bf1cf36ece152f554b5f325e64625f1b23ba28ced2c47253c0a45acec1edbffa92ee5b2053a119124aac075a0a684fc8c192e4281042500e70e +generate_signature b3f0024e9682ccfeb0d69b0c6d2f0911526899190e7f0fb4eb1e600da16aa63f 019ce545f3b46419d987730c551e1787ccfa158bb42a8f203631fc211e492c6c f03757ea2d5b66ba968b93e5109593e8ec7f4c6809f15c6f6d881b1f76435909 b0662b8d31c13e202584a8f3f524c3a32f3c5156399cb2c941768ca348c5500faf22d81f40bf6be393615f47a063abe63664090bce0c1e86d43d6887cd76aa03 +generate_signature 0b18efa336170b3a6683dc486d1f36b6c4cbe86eb310388f71cdf70e143d0aec 987eb3e73980b221b2524179565720f451786f8de2b3407c8b5716ef683c15b4 d3db1e882a144f312d1dc307d8bdd2eaab5816804329bc36430803d23744530c f4591ad8762bac85c540c6d4bc56233bfafa00217d914604bc2a70bb3466c00b64f65d79d07884123667d1f11ba04143adec52f56e34e8f62c2e9f54e08d7204 +generate_signature 69ae4cedd012737619b29be8065ede9f93b324b50252024d928ef04c543a79b3 35a734465352db4e813f76b446da1c00dc703317f1ce52c8a35a0cd571074568 ebc39ce202ec647da79c7af3ce6ddf307fc44de800a68a255c2e7dc7ff997204 548dc54f0ef555150fe479752a77bcfd9f68bbdc4d80a4d39277e789d6f0eb0447e21e023dc2d08ea5dabf2ed8c73deccc57ed7b38da5f141db2f7bd25c60a0c +generate_signature 065fbb38e8325511ceb9aa041f32aac98a5dc8cfb06a85c98853b25cdfc47057 281b3c8844fa282b064d56acc83e92c8e6568c3a628645f63e40a07aee80aeb5 bca17611de2f9642df95a938f64894a4079ad06f842cfaa9bac087fbaca3e70c a1cd7e79b21d9d740a112ed49167b9e56fa4b618680a6ae3089d06ab95f8e20f97b694a587a2e43640fff2278f488e3a7f8aafd6395cbd98ef5c216761be9904 +generate_signature c13ed90a542fd070e07610a0fee13cd42f8753f3448591ef9c9f57ad721fe9d7 02cb435d82524b0d03853e29c7ec0920556b85fa12886b063cf64345dba55483 e90a0b4646cdded432d27a6204c67a76b8a3db2dfe0bb2ac6957d68e6aa72304 26469332b2791f8178c8998d738b04d405522643014666a81ade8f9ffccf29024b339f8dbb46216e3d077ff7417ec561ab7912143248ab4dab6f7355efed6a04 +generate_signature 7badd2f8d2dd8198990738617145e90c87880b95d5c875be0cd7be0dc25e7839 240b3d620b61537d7fcbde252e9de6da442bdce4e24cf4bcb9d13b50206129d4 9d9c7c91e5d71920ff7db53015814d4360eb7e398cd6ce8233c2a1dcc54ae40a 39937b610f93007ad70b22d0acb3b52ab42e2442607870e18c6f4ab31b22940366825f88437798285b03cd220b0a3300258b0f0ea996add9892e427c3fceea00 +generate_signature c537ab8551726c4d7ade51835585bf7850fbbc1d57f9245fe1b29618e2aa5c30 1ef9913d379ce22e9f97757c68fc5414e73a27e69a55ca223cb5eba854888d90 c3f5baf38db5121dfb177a0cac1d6bc423ee0c8f7c1323d9b691b07489006b07 75991f162cf2991b4b9d8b9fb1c04821a8edd8efc19f76410655b64e235c450fd2e6003a33f13a58dabd88cc5731dee3b9bb17cc56f83a5ba52618977524cc01 +generate_signature 696aed47faaac314742d0e6386548c68617dee798a83105d3746747fd06d0cbf 279e1bd9754759a921c232f37d744d7b57531a302245dbb54f95259aaf923c12 28bec44d3dde67ee976fa0761ff650fb0d9ac60e74725e4c5ba3d7a0575eb90d 12feb731bf04c38293aa30b77c2f00868646d26c08a7fc9eb1c341ece5695d0c9f9d80580972a86a3d327a938fe95115ae113d8595abad0cf4ee1aab460b650c +generate_signature 0215e850ee377ac137b6f9ac06809bd6cfc31918bd0a372155f28d45c65eac25 45621dd9f0fd09053bb23b178886d7f258045154ce90a9576d708f0d92ac656e 0da2f7254d9796a93c9ebb3fafe40d398726941e469c6f8deffc9c32c2b5d00f 12ac07a817e0f19add9ade660c64c8f31661070859379581169753a30cc92404326a55421cfa2c864f9b7555f0f5e4beaa03c64ba16d2d2756fa32efd8c33302 +generate_signature 877a11a34fea5898e4643861721def338135c5cf6dfe1a6fc6777e77fc68d245 9e47630b2a88e9f86555a6a8462a435c0ac2c45bbb85115add2c4ba3dd578031 a3322e75481f1ae1eb9828f57a2b4cc1c786167aeb54547c36c936600747ac0c 705522c688f93fd8baba824ab73ce15588fb6e160a71abb9667639eeca0d5a02dfbccf5ad9c34d80fb194739d945fe328e436607de135244a43f70aa459ab10e +generate_signature 191a17e4ab9fe35c2b1ff3190967be43353c62dba427e461cc6a2dca6b12228c c7312755cd3546fc800b0af704752069cbd24d4d132f4af435c149d1024da97c faeadb6cc780147a42bd6d1968169e088a0b5b5025171ec01fe7ce17155b7d00 6502829ab5c8f077d473ec70b50c7145cd61518e6efe4b2840009557e361f804928acda8f80037f48cc7da3f93056653a73c2dff82d269442895d79d8882c70e +generate_signature 37bcb62ac859e2e85caa766836436533bb47f58b8748f921d3b1bbb81f9b51e4 bc46c4cd6ca4c15d76864b66439d46e8f8ccd846e899f18ad782d6a3608e0c49 28fff080636d9950ecd5dc362736734a75a72851823c7e01e7489b28c462ce06 bba8d06542aa7b119265acfa49aafebf229ffa8c63c6a95c09e0e2d01f461e07936d7f0da1f92cf5b32294d1f5c8d62f455d114cce8f53539d84b6877a3fdb0d +generate_signature 42a48c4a50b87313860ea656a1df5698aec2a2f8d03e69ab24c7f9c7f7f66056 e4e4c0e5cd007712ed5120892e21b0615217230b1370f9a531192e09f5463f1f 0f98ce51f4aae0b3c5c549fac5d94fe0587e38f298354f36e6a259843322740a 4cf47185f64306d939efb8e368a61be874fd320758c1513045a4c667bb10510f3e454b2b83b451782a63da46b24be69a83226ef0dd5ee5f4b757eae763b53801 +generate_signature 2280cec561410c47170e37a38805c146ac1a8b73c1691537fdd9d10a9c430579 21ffbe4f3e0ef3bd589689c0f2c35bb702f121d83d8d1ca62554220c1da72e56 a2cb2795994ed3ff548fe707d64f59369cc6e893ee2ce9830ffa526b2f41f402 e19cd4211041838a1ddfe3cab324e14c537282b3bd9b0344ab1b9dbaf22dca05235b6d085399858d883c977140ea3bfc9b6dffa2d3c1b12f43b3946ea5acc70e +generate_signature 7df35cef6dfe5607c06a64a38510e5c42093c703a88d5d76dfa97b14a0f6add8 9211c6183c199f81e344d3650c92b83c96bbbacd6ae654d8644b76a21611ae07 16b3d5bc1949ecae9e4db756c129c71889214a5af2ff78aa8a479a0c5d0ff90c a3c92bd6865aef52b3628faa710bdeecc44fb1f0da779d9d9c02c3a5ae7cd00d4d9229cb46fc73c4c2c3327e5c0f5418e09b1d707e93a92efcf40a7039295a0b +generate_signature 4991a192c5c493c47372771fde392802036834e9fa14ebf5c4696e40420a4c39 61c0eedb78215458d3c724551d7220f92b8f70aa656514de7fb209b26728a083 ff600a4ce0fabd6602b51e9636ff9b8b9e102f1da91f00376aa2e50411deaf08 1922bcd73daab892e6a580495396892833fd190e8eb074a26121a62b49a7dc01613318d3b418a5fc243d4d50156cb7fb5e2b5839116b0f4808ca280c38738204 +generate_signature e9d7fa57a984dd1d9c7a93125fe69f4d408c821d7c17d199ca494d16405c0daf c536bcc2db715ae003881137da965ff0e30dbbee0f4e29d5a439547b8cd48175 1148d655f1e23fec319f04dcf5c5ddf131fc182b50e39334ec0f312e25429700 61c507d190a114f2a3ab92463ce038563314c164c327c1c37e7b1530f33ad505376b070639bc51423d12aa326492894c577c3c511589599ad1d293c0d7ffdf04 +generate_signature 9c619840f7234b099d0995b905a15ddce999775aa1720f0bc9477bbf0c5525ab 4d8720590ddcf790a1baac573dda9ddf0efc21223335b15a23189fa67f515ea7 dee02eb9d27969ffdbe6f7c22642936e5965f3124fdbb0c51b1c44856c075a04 645d6f8120f7a12bf694408541a72e5e1c08085f4e2f63815d19c8bade6299057093aa996838bc753823d01bd3fb9543cb94d289c6a4befe6597c319c7993e02 +generate_signature 4e5d10eaeddd0acead90bb49b03e73b84e966bb05d63055031cacc8ea5e49aa3 e2c8684aab1e9860370e94d295bac00283208ebdd29910d54c9a28477daf6551 4487e8f01f0ebfd7b1870e7a1cdcd86470fe644de11161309114f120683b3509 b2b1fb0378592a0a1e8d4df646e96c34bd2647a3f5fab66318a9b686699d340273b6c8d482c55a3dcb559bcea7a59ce37abc3f71d9ec403a3f519ff5789f9f0f +generate_signature 8db465446cb5848044acf5826a3c8baea057859846b551238a0536dae39ef712 f3a88b4793016d12031ffa76a5da01dfb771e9255e8de871769717199b83ccdb 3ddbbf3cf5f282afd370de5d33dbd8464b805bf4d7c74be2410eefcb5072da01 0ddcc237fcc5c3a08eb2b5fabfd440b7e89cd94835bd491131a2c71143962b02765ade7dc00ab791af75f610c6124e15d11c796e29af8210014655a08feae904 +generate_signature 24979bf7adb7f8b840a3524c3d3bbcb73490e50e71400632d824f420427df802 78057cddcf685984606870f37a2f0d76e4ba59d169722729695edf4934528856 fb691338b3fe0cd1a272b7ff7e363727c66689c1c7cfca7c007a5543b3abd401 8ae1c701c68851de8a8af85d420c0d1953ab9d05465979b9ea009d54c96cc303b1bb1d12e471ec77109dab9c17a6806caa1c8c2c9e0702f3bec8ce16f786a60f +generate_signature 8866f2c3e0566f73f173a7e0f9e33cf8b1795fde4cb09deeb1efb17dd82ece33 28c508eeeda24bbaa55dbb312f534292bb1d8ca09cc34ceba36a3db1ae5aca9a bed8a7e6bc28ff86ed87c5f54d91f8fd381a2fa9057b6d38b095245dca1b3708 8f718f3e412d251bc6b2339f9e66632bdf4331236e8c4107d73879ac26bbe807a50e5bdfc96b80e20c6f5547d1cb4bd07821c7cc62b2969f53ad1cd37d3de20e +generate_signature 421c152f337614c5a007b4eeba8d4299f7fab313c1e4fa935048288d4b2531d3 862b1dfbfbe3934696a9ad41adf2022714feb99e5a1936ba2dd4cc08edd7166d fcc52b66e50a4761eabbbdba3fa82f2e391db31ed1be585ba5a936c31dc36400 90abd70c347048d47bfd4c7a8a99226f339706cbb60f6c0014b28ad2674be00e5b8acdfc5d3d5c47cb3fd7c4430ce61035e3f227ac4e73bedbcc024d0269e10c +generate_signature cd4eaba1978fc73de4114aca7dc6708308e72a8a861c96c2eaa13dd06f2e82cc 88855b172e2e512fc6ae2bb6e1252670210b17fe4c021f97ce7245ef5e0766b7 fb333a06054d22771b8422f547838f14f4b868e6c0ffc7abaf52643b36e25a0d 4e9779439de8cf8f1f5f52cb204affb110829c4b813c758d095705f01076ce0303eed8e0dc54164f580e7dc30c4b954cfad72dc474a4ee7d5906df93899eef09 +generate_signature 38d2cc1cc489bb0af029d12f1ed624bec71d68c0713f1fd7f5d367efe71d72d3 3b3df37e4fbb3a730b5f9c5a1f7adc45dd2fb2c05d1e141d939536b6f55065d6 f10b5d6fc5830989df088c2a1a9bdf95c37781bc8990b95beeaff246b858770b 8ab91d8d59c11c7fee692e1e0d89efc7aca705d0a37cc3fbf7189f3f7638350e5aff672891f3b387975e55699a7994b76a1e65729923cb404d75ab83e5421402 +generate_signature 017a5495b9f777b45cd256eb87cd298ae9d346caef147cbbb916d95a6b61c677 e8d23a7fa523a7d1d59d23f5929cdf9872ad01eb77347b076121b2a8d24289a7 d77e90cfc8bdced4c2957514ad1dd3ee2b4e841f843dc2cd711df92d31225801 a4d9d842ee79f9fd09637d8b1cbe3464257285962ef8f063065ef57576a79c05156b616c5ca01c54bd394bb03195ad1a3fcd16c06f35ddd253d356f91746f806 +generate_signature 5cdee6559376c9ce870c83b06a6492b019ec54bd0058c2051813cf600bdf25d9 a2def558fd26b3e1b9f1d13b131a324ebffe1412ac2d043f7395f060e35f4780 29e13e332419405883c0103dbabdd28035a4be98f8216230d4a31072ffdbad0f 8c7dbf5351690e1cf84235b8ba57b8227ca87d62349a882de275c2e83f44d8087e1b087fb19330d9b2fb2b144cd01b8801b3ad4d9377344953da1e9fccf79103 +generate_signature e5f7e794c78196616415dad78d3d07439809df7c7443e44b82e54c6faa5bacc8 6fb3a5c5d7b694b63f29ebaafa2cb101d455d95d1b30c51d2027714c6048d81b ce2794dac0a5fab2074829050ff0f631ed916adb97c110d1d9c01f11fc572005 4de61dcac6c2964dba30ca6c8d0bb2dde316f7dfbd1da8e18e1c200663400706499938f22cac48d0a03a7206e9bc48bc62cd4fc898fe3c9f16e65b059d36d705 +generate_signature 5283cc05c1a813496774fe331d90989baca3a8932ed5d6ca1633a96e9881deb0 c776290262741f162cc11cd2c6ac606330a0c53fa543cadf061d844ef306920b 7012ea2ec897d4db791c283cb2f878103fb6e29c831549f2c11fb306a3f33301 727afe80ee09679925d76b89d6bcbbf274ef85c67ff689fbaa1478eeec71bb0d142d8f759199ae5fa11e9d588c77ebe7c16b85e7a969c34fe570ce372c792400 +generate_signature c7769cf2307480649f60e6b97930ec24d3ad128375546675b9da306de670ab0c 3cedc8256aa3d7844139e180826e005f8619adfa8a493da9e824181e4cc007ec 4514bdaf0b277950569d81b5195c63f14fdaadf1156dbb3ee16f3aebe2cccb09 d21dde1a7f581d9582459e573c621a989de819e6b778b528c024ea1be7137b0ad3752080d9eb66364f9ef322ffaa013146cbaabf8876159af8d80b8c211bee0d +generate_signature 0020398fa58108e0b0e5356b87113093b1e657fc5aaa229a2cbed766a1343b13 dbda7c364135fe9237e54e87c742a1174d17d3e70a08051780ebe274e399a4cb 12160063e4e47b248ec37339a3e62e920b41da6ed6b9ef1dfcc1f48a17c0490c bb8c5825747475467cda1d925b752f89d51292ba8b2d08d2abf5ee8b993f3b0dcb63d6cb31358a0c651ea96d7ee85084291de45ec36ae8588a2797a147afff04 +generate_signature b1e43d4e2cadb0e60c8791fe13909d0226ddc2fbc5bd25f73466910ed0ab3f2e a1926c9a221e847a653ec6136e4e23768ee329db20a7a5986b51c0b35eb73d15 5b0d93ac7c30cb138102c03ca6b27291e5adf76d82bc88862bcb3af8df86d308 c4e83d8bb69c4826754c500f1bbbc42e146188ee476abb992d14d9f524d5430e254d80c1337b57acb14b45b0c2ada760e7d3e341e6abc92de12c1aa2e2e4c207 +generate_signature c5e0e0a1d97e19628730750589a954c02102545c18f7bb05ef3e9eb8cbe36523 e4a4af78733fe9a3b297bf135e8b57181d8c0e39ecf9a5a7ce1898db57b18b17 7d29486ba52c61fc5ba24e42ae6f1861f161a0063f102ae3e86c480b52d5a40a 97c123af28998c087fb7d0085b1979754d9cf8851c9e6be628fbd35869b91b04a03d59f8f274897b69dd06ed24514d6780b4d5510e9f4b86eb548e86d10c0205 +generate_signature 81bb454381f1614e7e5764e88664dc47f825d18ed5710899d1e5968169d7bf58 1bce52512bbb39ddb4dbe9a10cd12314ce08fb7ca6b20d8a0855b334b5416a17 8fcbfbb6f711655ccec1b456597b0ce4a5c81adfe9523721503d36f5d5be5708 6d7fe9b31473594ecdaaf69fc4f4fa21857bcc94882978af178bc4abbba44d0f11aa2838ad1dbbf27bc4bf7469e262817a36449d522827f750489699d514c903 +generate_signature 8c9949bde07909984cec9d462c7e1de28f0d47d143790a686bee51d4983556dd fb6fb90c091135388023524466045bfca011113099973ceaf2d6dc4fa0aafbbe 8dd9d958ef607e46a14f742e8581c903661935e2257f24e852ced3f7edc5ca0e 5ff8a67a870444f90c7d34984e07026c171a31cad991dd45f3b50d28069e7e08c3f13e5a2a238c82913be7d8182dd52ce335dd5b19980f8d063fede845a2bd01 +generate_signature ab5556f1489abbd7a0c8c877a18313617887be7ba186a8e0a2d50e9d94a578c8 e7f84cf6411ca6bba208b07ab26e48a09b779a1e7bc907ed587a1d7ebc39e273 601f584a65a16dd59e6a1b86310a66a11192cd5ee68a4943ec38bbbb1bb9370f 2a25addea0c69a5c989407a493d14afec4f81511ea82ec51e73917fc6fea950c8b30997a3b5e72de722d3930c66ec245b732c91d7cd1519463307cd613d41204 +generate_signature 47fb94c340262e3cab3ffb03e06695bfbdc2439807cf26620c8cfe4395391309 a5f429dd825be9eb1b76f7300754a454844d07d1627312b14c70a4ea4e4566ab 7747b7704dbf86b1d338f3d49649e43009e7525edb11e684294fdf1d6fbfb708 6a474e70bb7d0ff1855058a03a42139f553b6eae29d4822b901ca2ce149e140fccd8f98e14c7f8cc9182fe2b88d9864644b2dfc0fc26d2477e0baf9ceb3cb40c +generate_signature 6ba24acafdbcf7ca8125c560f585d33fc166ae363adc8f0422c2242f6a2adad0 b27456386a01d84300fe0695bcb594296fedc0b2ea31e1a176cfa49eafa0e5a5 237401f9b747baaabeb8d8df6f526f00a6331288118c5aceb343908fea8ff708 a139daebc42068703c73c9c8c2b5c0534d7f9a80d282be54a2522dd40ff0cb05f1023cf88611610f45abd326f17851fdf9f8d1a078e966c89fc65b8368b1090b +generate_signature c8dc5ec96cda8c740358be0754fdb8644a33bbdb9352ba63ba8c8ebb19f7a240 d9041763f43939ba1b6015c369bf8596a53584891e45b20f4031ecbeff20b36d 849e6afd48fbab23161a1dfb7d3b60151dbc4b288596726ac07b2fa4fab68109 e2613136e1e553778259c66401fef46552d217a95e57d1dc2c0cfb4921e4ed0ccc13ab2422bbd3bef6731863085c2bcfcfe78af97025e5e8306d791a0fa48807 +generate_signature b6d466f81e80d56104e09dcbc4bcb94e4f095ad48c635aa14f74c106ae57b9d0 005a126a81f80baa89c897c921f562aa44afb0a7f317e2cc2988197f980fad94 08042ce99fa8ff0a4c082fc1ba66bb11f8c94ba9ae38b4f37aa050a3345b2808 195e69f5b202fe7e5b096a0a75935a7f8555ea4803f36ef4ef9cd0c2ddb7170827ab7ece6c669cf144794f4acc6cfbe301995a2fc177265c3f258c958845160b +generate_signature 8c442d20262492d6075a482a853037319738a944e8ced7a26c236008348cd06a 1543c07acd90bdcc1ed42560b2129f52db09965732e1ad75da5df80f851e0a30 5abba5c682b23a2e44b9b426abcea8faecc8da15f0d124fa428cd21ab523490a 8cc7857d265d81243eff39e9921aab33ec737487c692237c3e34267368aabd0c291a0889df4f806e79e91256f67178a763afe05460438301a31ae483165a8b0a +generate_signature 6f7951c7ca70cbe7abe0d6befe5a5a10559b7d05a56fba3280266361a919b07b 2a08f949c037c51b3bc036e3779b6dac628a9fa2561087d15c029de399596209 77f07519b561abd91c4bf4a10be08deb641537d7503102d30b8403bae936910b c366fdd82651f709018875c712e5a98c9bb5faf23ef1fc38e1b31cf074d9b00e72c18d2b2eff556ca99db887d6ad230622010fcf18f773a5ed6016c1d19f190f +generate_signature 22b1dcde0d75d009c1f37df37ed0a79e162049ac2ee48681e312244d540b046a e340d157c047d55d82817a49b51f3c1d39a82d38876a05050dc5296c12a2b6df 8fbe0766b15d4fa7542756b1cc33a62bfe2da4f7f00ca30dfcccd7d63d6a350e 0d99f9fa0c628c56a78bc9204b7b1a8f8bc412c18e45f6036b7bb2b65012c207bd5825ba2a48641a1918672c134fedc6a9c3d47181a03b3508a2b5861bab8e0d +generate_signature e1552258578c4238b55b9c096f1da86bc399ebe8c55df9dd6d0468ce939a7695 98557f788cbd01ac0b77f5e4bd2c0aa65c58cc61b49f5842a53f898461d22ce6 7d052de95e7e055d117709ca3ffbd6294edb2b0698b549f623327a7856d18b0d d3c98fae5f8da3441f45e5ae03ff4f1271938ddcc94e2f29353e6b591e74710b476f6e89a706ddfeb1b67e315f58ca516adde708c250393498eef78f07c37404 +generate_signature 9ef760bfb089f081576ef619c98fa69e80bff76db56853759a98359944af31f7 1e2022577b355dee237be685fc8704c73ec09fb448ce3ac9224c7cb26ed7cac3 e9d5ed5d17eb81635b3f44bbde735d506a1b1002fc963369db98c2da1fb9990f 90a58b604d5b1b720b4a797ad74d7da0993ee84b40d88f031d176da5d4d4f1056407367e4902cb87c789855c93bb39c4a74bbc6e08dc20c4b659dead53bfd002 +generate_signature d48118e8850b94a1d6536e8ad26424db5ff6a8856837b5f2b759991cecc0ea79 76acc258e905aa60091cd19a0453c086bc63fbe6a9b9f9713c5a59e4a1f65735 e7eb8679919eec79d95612ea8d4f42d3dba03c92997a8db4ea7ee670bbda580a 8b0ddd2d0e9e9b97505aa443be20bd7bc77038c15100fee0fa6123caf3bbaa0ccc14e332aa3a7be9052719c3e200596943bbd27cf64696d0a437f6a137fab70c +generate_signature 95b23e89020f166b62f1a0ee717a478c07b3c96d697a3a5e1eda873c407511a8 8d7c53fca9f208f436d9b1a921daa731f79e537a6aa1f54b809aa4ad9b33b860 b702f52f1dfe951d4cbafe535089128cfdf8e3c62e95238614d230bc783fed09 5572be8bb8a2ab96f18d54837dcb8be02bc2c65091f3a8f30dd00c0907a46d0721a0b578492273c43dd256a7260690831ddb1ac3c7924b4f214140b76aaf4107 +generate_signature 73017e127a3cd7c8a9f97f80ccfe6804c5bb683b22b6242a307aa84f2b2e5663 f17f920f113d54ccf8790951b2963d35165c852ed0cf7c6f5da3c1af2874f7b4 7dd05f2bed1ebd7b1bba8060fcad9368c9c114e49ff553273cdca2adbe5b7c02 65a02f3ca050e4130426df233322c51046c0347da04bb4527a18848aded1060db8b12909341828014027287bda5e8bfb10e9e0501c8d077ab1139c84085fed05 +generate_signature 5df7c3d6ef2ed1b6140e0c52d836594ae75adfe243cddc0cc11e253674ea1d3c 5d45e0470b6d249abde6354c3db9e980c56705786c11bef17f94a31cf7d05d95 60e30994ebb7c53ad955faef54e0abf7d7aff8932af097db250fb56099712b06 8297fac84932b47a5ea32d0f030aec465b863a89d8cc8f67d39594bcb887e2011b70b6f270c80731b612193ce974cdec769183dcf107bd8b944f550a523c3d09 +generate_signature 5e1dbdf1b13c9e0be80b4f4e10a2bf29144fa2e66553e81fb50f2f7b55633f1e 4cb10093b5c9cda140ec668a576def490cf7453635bb4921cb9ef8c8e909ee52 067e27cc60b9d9da528aaa8a48ee6cb30418189c8f6bb0d5710dc1074dfbd605 dc5bef213742edc6e1790f5d755b66a09631abca0b31f433806eb7ffa038b707f8b430770c72ce06f5bada556a2d305e56119a2e96577636a108deb5c2ac8407 +generate_signature a08e5b2e08d5f1c70edac02d97b7174e348c6d4bbc201485245e8578efdb2f5b 4a10b74ec6c5ba4cca27a473ff6022c3618b2e3c24625708add88767da62e3f6 50262cbb867b9feef014c6d0f301838bb92b66a8f2109e67d036129096168900 98f69d3e1949a6074e0591f826d9c781e697e7bd4b61a8aba9e4f6954092ab03c1154d32ffd177de61da82df4b051a1d764025f5dd6b2698372d44e04a087c05 +generate_signature 07edba63a2c0fdb2e02bb8b8e6b531d2a542b0674e24f5ed1f3ef2583d2d9eb6 8c1059bbe4a373948ede638667da04f8caf05aaafecbad08de797a804f2e65e3 abdbf74be8cba1d22bf91057a94b1f12a063acd976e4410152a07de48b290b00 b7a94f9645f804354366ee550d8ec34d0cfb9379728b6ba10c80271752d76406cd9101aacdce3beb15f8600ff2e98f7035a1ca5c1c42c98b5700e02ff354d804 +generate_signature 5dfe35eb9c70c55e285e3e9121651110e8daf329a1d3110bb342341f0b13e285 898082df3ceb5d2e96fd5400b22bc169e04d3ad581a63de9ea638eed30a1f63d 1e83a05b9ccc0daeb952e2d6b5cfdd414fca43664ed0ecdd30c5eadf7a8ad20c 67a177c1f3a4a3d4781086fe5a1c452a5ed8df5f3ac32a89c32c54f23b441f09aa749c2539584cbf6927839dc33d08c5b93f95ac7760ca8e0e25d015c7d6e101 +generate_signature bcface283643b5f6fa0140dafff732ff07f4345abf3770523489ab4ff2731c58 85dc41f9264298c7ad08099dd2b20682dfa4c9798e69695f9626f644ef7c7980 b23d6a04cf867f63aa2e6a26784d5c7a00afe68a37af3069248177b77b50c506 537d655129d447345c89dc76ae04ddb1004286825de2deb3283779d188e7ea0230df1c620660d0f4df07c572ed6e6c99b8d072e6f8fa096fab174b2349d21a07 +generate_signature 79444422974fda912b29426d4fb4e8900953d14b8a04b2b48a3370fbf6e0d34d 55c8b685946d2e1720c290ff6c2bab2643e35f6edaf04476dbed56472ccfa242 eabb6c32344af64e15d351011ece8fb6cb2309ecaff2d43d819f59389bf57607 32033a5fb3bc9eca2153666445a6a2b8c699f8204d28771f52612de606ba9500f0c194fcfdd61fff79878871752448d032161d1e9bfd5d1393203cc0dc08dd0c +generate_signature d2c081f5d9a76df2448bf6aaa021331d6e0fc87e31b6fee0bcdcfd499d2dd94e df1afbf7c09126a407fc34b118985ccf41c4e82427e6dcbfb0d3897cd3705d8a 56db60488923ad7e0575b143e71ce05d93e6d273a5bd445cf74ac9d8336c230d c736fd99b281712a1212086ac772cc29b0f4cfec900467802b7e12e5211e7802fcb0541900d9a3c9ce8383ed1e8f01d8417821503576503c2e8030e245030401 +generate_signature 8153ce2a925a6437aa445072075b5cae447affcc86b452290e5aadd91e446e9d a504a848e87137e24df67b8330d7dce41efb6a3ffe99ca69d7a2775bb4ff66a3 58a649099831b7343c6800ced12204ed25408aa9b1525d2f8aef6b8a40cfa908 985b0fd42c384ba1376b0fdbffc9a2023cb5201c74465e963c1ac2e526378a020171ef1b0c55db424db2b49a358b9b081141a60ba98f7086b0c02a084dcdb20e +generate_signature f85e575bcd9427a761197581a05d22e3784e602cc471d88ddef5ed4e6ef26ac1 e4582873da79806344aeabdac709ce261fbeeee546d0068c3d17a64527aa1cbc 1090799dccb3d243647defbe3ae894d316d68d4539a134d8aa86f3a4116be203 8ef01495537f359c3ba907a589b2573ec7815ddf3a036fee401348c6396401037af7d6845ad871f268e837a3f5f787d3aff09996a1671de340a522db90e2e30a +generate_signature ac157e1a76bd72924e77dbf97e15469a4da1be6d154a8974aca1d77da7592e93 f4005a9e2341967cae9307f262d87c064163d0997d9f039a2488fb044d449604 7f8c5592166b6445aa14fa0b765d3908f8ac9af1c6de0f01544ea95c487e9c04 b0f7b82f81ce75f0efc950dbd91e5441cc5ebaeded0e219f1e27c284ae0b29021ab13a375ddcc82188e714809ced5c71cc9421155cd8a55d5c327ec5a3c8340c +generate_signature f135a8ea135688e2cf612a1517c305ba88b556d3d0f5c3cbda706bd485993c1a 5ad2fec0008abc302e993a7e03a99358e7113ad6cce435c81d440463e39fef88 5a0e0b8724a44adf7eeb27ed8cdbd6a11d9f1a00d0729f6325910ca6783dd500 ea713e35ca9040e21cbfb9cecbcf940f6ed77929297c655165f874a8b75b2c082ab4d576b1f2628acb419b520f9d3e47705842a69c80665b4562492a3f270803 +generate_signature 2f37a11507b767b81357f9d03bce2d6f92cbd79af252ec737a2126e1f1a59fa0 2030a8eea7a816364dd23b81655dbd00e10edc8f40b5139cc49506db9fe12c44 907f88dde3d47b78945ba4180c53fab25024bc009aa678b34c6958c3af14a00d 85edbd0eedecd157d416be391fbcd59c075df166a9ee4c6f5c196de9da006309e0aacb072e5279259b7c39357b4db00bb74f60f9b543481aa6e4047eba52cf00 +generate_signature 40f923c6bbde2b5aa8ae568a53594738edcc102ae77d2eb2ba13250bb3701aff 3d0d965e35f87612a3d53ac8b62ae4e7d4aa2f1cd3a6fe3a258ebe91457a810f f88b02b56a6d583e5ee52898fd7124270a1de3aae61ddb45e3fd4d272ca49e01 cc37543dea1809a63cec611b8014559e7ec8b809be43b555fd251d095165a2032510695f9867aefa456c6c97c2548e3f931783a033d756a90b984004998dea00 +generate_signature 3c4a9e9af77301f53f078bb5780d71d3b14e55457fb26308647706d88e7e8ec9 41a6ad8332efccc56dbcfb936c84a1e608467d9f58aad447e7cedc9c091b8f33 6ae28d2f4d72100a1638132f6c363d2d65c73f2f6e9e6ef883775ce3eee94401 8ed9b2162d0ae8f284eaccdf5054062251e8683a4f092b1eeb14ab47ecc5bf0d38ca546b4a2b37a58763e7ee5c97bc5185e0baae593a85c5e9a7e26d0c491b0c +generate_signature 6e361cb98046db1c58738f54bfa5e24003ad2843625089084112d3e956781da1 a40a310dbf5128eb712aa9ee5d339f8c0e78c90e8e44977ec83a6138b6191627 ce3e7483fae52df03b7c08d8f30fa43fa505f0a7948889282f5e46b0882db500 28ff04002010f15563ea46bc3bffc3be7624f1f2c6356216af436807fa5a7d01f6d5f737e8d24fd52cf00da44c2af6333b3b1db2c356f9b6604e5171906a280e +generate_signature ba7df37a2d75e962d42d31a91525075ac836100ed1a78ae4e978afa432a1235a b4ccbeb6c17e719c79942b74b81a3cfb5ccd63e0c06b12fa88f9e90cd9e8a1e0 a35bbaaf8d8fcf421d4307e3a52f63d382b8ff35c7f6e5f7a6eb0a7adfd02f04 8cbb0917f41e745c78d3531b8f7e0ba05bb0ace0ed6c89d257943a156fa90604a9b1abf636c591c2f355718dbc203a22ce93301e85027a99c4d6630a84afca0b +generate_signature 81599211da2f1378330d2bf7f86e0686947fa402578cee048c6fd648a909f2b6 865b484252a23a1697c736ddf81d44674d9b51fcbaaf5bddf88061402ac65189 8509f1f20c25d821fe6057863277222b54399eba3712721c82c174d81650080f 598064b49145d11cd8d1ab8d2d0e713422a63cbf8e518017001a17701cf947096649918903eb3b025cb489bf9005c395c91ce69c9e02a88cab281d11b2a4ae09 +generate_signature 9655414428a87d11c0bbed9af0bb32010c4847cfdf75ebf4a8c73c1d5500f44f 53bc48c7306182d958147ed4510dc3c1e5b3ccfc16938f1a7d757ce7ca2fdeac dc1506e5b95e1d6e6e06ecceb4b8cfaa00daf3d6fa26fb672374ffeceb0cb505 5d34f44b0fe9495d3dc8ca88b622c59d425ad572ac3323b66de955018fc47b046074b2bcf0a4b9e1210704f289669d66fe3c249ec18b81f94b440d23e43db50f +generate_signature 3e1ad25eb7a1dc33c84bfb09d73d946aee497638ea639157e1eed1739bd0a84e b507cad326ce68772dd3cb43c31bdd70cd71d722bb551d209d53449c232f5055 c06773a6b15d1409ccf3bb4182509cd418fee29a93d966c511809b19eaae4d06 ed34e9763f481ac9f530a2300a2b2408f17d2ba824238b5b001548911a8ae605e3d0f703fc48090f670131215ae93226aa201a3c2b2c57436e29437ec5176407 +generate_signature 36bd8cd274466d762f8bd459e5f32dbd7662fed9bcb44b76276e793701c7f5dc df9013de104e242aec33250ee64a3019fb7ad37bc06a421c60ac21626c9ddd6f 6342d56599a678fca61f584facc41e316f0bf152165767f50e3346b7ec74a208 098e0c722c4e5b695884e0e9d8db72025f782f300ec65d50e28d673f07f5e30ae6748341058b0710230d6437ff7c03f11ce89e0ae2c699880d008e1372d7e80c +generate_signature 829f725bd1ec12e3c244c4b0006499fbff693b33d0797c043334ec591a8d64d7 fc559b71cc887e98088cc873b9d7c69d104276e257cb28197644f49e8854e69f 36228dd0b09d05703d7f8b5235b94958439b8a95b43c8e09b577a0fe6b26a901 b26c169aa7c95e62a447bc55d495cd624a36446066a5fae340f58c3c0af69108747c9322f2dd55deb910ae261d458da4e8977129c1648c9ec14af92477c45605 +generate_signature b2fbde52043e979fae4cbfaa1fc218bb87b87b21410c498be62c5b288f0a59d9 fa437eb7611028c6141280c056fdcdd20912575cb57d652f78034f7afaa5e0a1 df1288522dc2cbca4473941856c0f24952f69b5c9efbe6df24396ae25ea91105 220618c019f4e7789d59419056198a5c96385545dee117b50715276a9def240caa09e94601ccf2e5febdd31ef445b0536dc64835c320da8f5d557b05e0ec4902 +generate_signature d21de662f0d58c9f746f0984fc3691a3d0bb0776e539a2282207b8ebd8e5ca85 679397579f31b061479734bc5fb2218c577503f35827c815f22bf53761c272d9 b04aca24dcc7f0c6d04c3c676a6efe8062fd5618d16155ba2a0e4d0485332c0c f83ff67418174b6d532b1e19ee545980a04bf777db1135c4464f8908288a6500f134aa66add8a9d075f40eda38a673d00d3df3cc1c8b77781aa79d64e7aaa808 +generate_signature 44c6eecba5511adb489dfb05815e764f933553baf1acfb6e9cebe010337b24c0 a08e4416e02f056c71ea72e2eb1f8a89f669fbc225a7254e4c8d0f8d39ebd1d3 40f60e9ef735a23ffe2ac767726ae743f2844fd7e8981a96cef85096359c230b b57c11aa25f65165904d994ddc4c067b55ec61420982b193f8e81394958aa20865cb62037440212a43c99d169b1ee478195f9430f887b1e6bd3f8b1a84618c02 +generate_signature c36bf112fe3f11d4a33a28c8f7e96e18c0a561eda487ccc7e441439405c5b339 5d764a790fb3442a21aef48510e95dac552d0f34c2b03289ab11faf21623c451 ec45f5dcff2cae290875866dcb122645d1b88cece607be63783c3e088cba410e 518393c79d1a3a0b5ae250a27c0b7ee8b82a914612b272d9fb45797546fa0a06b185cf414ca709da8b821ec3059ced87592ea4e7342884e9c668e7ce94ee470c +generate_signature e642c5bd2b451b20aab856686bab22f6987a7b5330c2491ad9d43059cb5b17a7 e5c73f7ca70afc4019ba044c177ab7ba3b57070effd788ec950e23f766617ae2 1430d7db432dbcf48c88f489017aadb6bc4a4dd30dd68245e281359e7bc6610c 46052008fcab7b25e4359e1345f8be3bcd3a1b95c195dc4fcfde127089ddbc05a4aa6fe16e0b9ac5be528cbe4e0f9fe1879d056ccdf0752db1938127178e1202 +generate_signature 0b427120304ff23f1906c33972a5ddfabc833455e1c00b4ad522dde15f401237 45df3d7b2ff3ea6a638a360019b6998dff96ba9c11ec454fa763b75ed9e21891 07290dd0ccfcf302d33d38e5b943f338a40e63fce395f47984e7edfb617e3509 ca2c0e4daaff01c720910cc7948ba52167f1fc98c1950eb80a125f626615200e477a125c37a8f71afd9ae9ef3a949f9a07c9b252ee26f7feb32fb30ceb62330e +generate_signature 5ada550217e164eb59ea6506858ae1ab537c9cc15d362f94570255e558911490 891681f48e13a2f0f2a7e189995a87f9af3f3b4aad553deae50f38455eae34ae 9f12a804897eef85dc4f89885958acdab399cea0cbb6c7bd2e2b40185bf53201 33ed93954590ebd70631c2d87c5360bb086ccc0d54c861d3256e697f83ece3006b87cf05601e8c37c8f6489a7f9b44c6ea6519aa901d30012068dab793feef01 +generate_signature dc200cfb46b99ed1789b022df4462e679fffde969ef75181470fe77d2f659224 a8987ff64be78dc7120fa7c7caf8dad90e4b8dc44597769eefc67df636c26062 4f5a3321915e3c9922eb7704a158f64d27d32192687412d28748d2ed4636900b e67fb58146ddb2ab786b9b6eeec4d27f940f05fae39abf908d3c40bfccc53103399e987219901700c5682eaa9490b025047bbc1177c69c7e9db460c7d20c2006 +generate_signature 7462d6b416e428b5c0ee3f90ca39575495955b720ad0e7e4c2955c1ccaae1888 36260bf759635f1628c16ab8a85e22d20d9554f6811bf2d7d2b0e724088c8567 2f37c7a26aabe14e904c61e256d5e2857adb8e94c3d28a57950bd68b5677f302 359e004fac509564560233cc6220dc6feb544e1f001b750870a9d05a5b70410aacb04b1c95236c62230c6dbdb7b3411e10c7f24248e9993ee7bce144f6657306 +generate_signature 51e9571c4b6814a631509d7b43dda8a19fbd9c5536c3355c3b9ec28fc7d4aea8 cf237efdc47a5fbb1063f9851704177751b2bb6dd40f35a35bc49acd95e11370 a37484d7875c479c3df673457b1d82b1d8fc80df5274aae7d1aeb39fe2aa310f 5157c7399de7cb42cc16040646d8f81fc98ae57b3247855858dab76b29ad9906b18bb08515693f3ef3079e76d540f9e36b172a39adbb0940f71a4cc15cf5bc03 +generate_signature 512f462d431403b24c18924eaeddf9d911d5c614d2a5e274c21933a136cd8e10 20f1bb98259c891bfcb5e11793ad38c2ca863d739f7a2c53bf5f74c9472b8fd6 8f92a6e203eb6d8ea988a11177aa370bf142928fb93c466bf18ab55b50492e08 60de330b3a97e9609bfe15d805202ab8f57a41b569b7538562586ec2d30aee0933bcef65a5dab7377410eec4834243e1997668ac9a2a86710549996a93306d0d +generate_signature c67ceead9a67e5bb1211538a0c96cb3605da36f1fe2a8664de0f134781a85889 25adad7475f476c9c02b9fcd3c7865d261e5da7261031256dea250c71f305bf0 8a451763e81d056b3622ff5db2432b5ac52a4565735d1f399d7fe44f409b6109 fac0c7fd890eaaf93e177812032578a0caae37b310555566847f97305bb9d509737ac895d56cddb77b09ffba78a3c1778ffcde410115b732b11d2f713224330c +generate_signature 8744e27a87056f551083b1240834edfdf4576a4b8c4db7e93c03bec8f714b842 ee8c02e0eb978f310eeca705aa7a4bfe38cc10a9a92e8458f5656dbcb22a604f ebba71caa77c769b5c0920033be75dcd8e1af9a19c97c4cfad52bd52efb3cd02 ea958d0b7abeb37e780241004e2fb8db25f6fcd886121d22e9bae96ac5ebc40902ed767cb77f9bafc9125a8a2eb2930bce6bfd43584165f93fbfaa4639f2ab01 +generate_signature 01b5d99e68a458de21c085b50147ba1f67b0930144fa77ee5d4e1d37e91da1aa a928416ec7f0a87dbded403c87fcb073528cd4f90651ab9e3bbdd14ca61b6e46 370f367e7ea29b50ead04f5195c6b0486923c4e7109c8fb79b1059059378b302 3af8752964ef33888081ed30f3d4120aa6d7b1df1db8b2ca246ef21e68aa650452341252e93e7d5e906a7ddc06a56b18f5f99f26212f31e1639e00c267846503 +generate_signature fe682bde8ff2a30f81d616c05b9b356e32b3b6f11fc4f58c133d7b9082f3fe84 dd191591dd57a04d33124ce1af1ada602b3f5304b15d4a90aa0732d72742f501 c2b494b0c63900df29f46d4ff2e56a644aee5cad4105aa0123bd9809d675df04 f62b33ef2864c9d3535938d4852a388563ea188624c7352fba3d557dd9f568028e00056bc1a022d0df98dbe43c09ff3cb012a4b9d628acec222c2b6ba749760e +generate_signature 96a71cd74467e3b95e4dd8874535ab8fe7710ec4956aff40dae1e7849bcd4c24 c55e53a61395c80d8adddafc2bb1224f1e64ca8014fd13d809c86d7b5e299e0a 84356cbf4af292e87e3b7fc328cfe45d5bab67b784405de76533fa4fe049d702 b9fc15218a12c8c4e3011dcef1c0e9a223f0e1c0aac84727216185769e887a0a6585ea87b4ab6163f897d47fa2c245d1826fc1fb4fcfd301c8cd36eff1321a08 +generate_signature 7332333e4bedbc6291e64c3acb32779b6bb94493e83e676da1ea8e7ac593cf7b fc9dd303f58dbb87fd82695b463e1197a265279031cf9ae5a6a7e6dafe2f0fb4 9fabb96d4292617086a3a9c0dc084bb6118f73eeb602a6fb3082a37fe6d7a40f 820c0611ae2d1ed5b63426b9a927de46ea98aac9fde0d5ab4e1528da95fd9b070bf88d17158fb76c96fb0ea53873db6d982256e3c7dbfb2ee32b47ea795ac80c +generate_signature b15d619a2e307fc4e1848688c3388deb8f2e0eea38328cae0886adeb42474204 5d2eedf3536c0d1857d75190f3da8f9e214dd170790722c0ec90b5876f46fa1b f107b7b3f7e17ed25504d36574d01318cea43a84b0c275d097ba0cb83e872601 a80d36596e68768a67a49f82eca4cf829305da2881cc5f06c4cad88a6702470cdc184b82d209b4568f7f22c944862bbac4f336a004b561baa78a91f74872da02 +generate_signature bfd9fa1f474799d11ab3ef3abae59c17b300b8cb899b2fcbfacec678cc77bdb2 362bbd5e5c95c7e1adb900a3c6c5a67b5f050e58ceb6917bfd037abc61457f23 4bb72c886f3d978943afdd5c1e53882caa6b323e0c0836a1485156c569c2b604 887dc3e64c5fad7f37794818ebf4fd319560b637e5387907dbbfe8c569e8de0be34e3f2728226f28fb66dd8f6be93da4223d289f55c50b66fa23f30c0cb7d504 +generate_signature 3b8254fb834017e439bf4c9db7a54b8d72cfdfe734e64e3db94bc42761e52a64 3f1a3561b18915b34bf1b451fa01ce6b9e2124719b4f3843da1caa106f5e724b a9439b2e768ea849b35fe3e8f31fafd2460343374600031aadc7a047d8f4650d 570b9687c0cdfa89cc409b28bdd896e71d13a35169ab67eff7a9cc0fba83420e3dcb2ec7b42c81166d1c4880f4462ff84473c1c9e3889c24c4caa8aff322b50e +generate_signature eee51b7ed448e86eb34c493bb7cde32b654a2c3b94734b54e313b44705736194 9cf9b0c581562977e791eaab88f9574ac7e7b173077b3e67283fa00ae448d19c 7e0763abac9d638732d9cd91c7662b13b14067fb458dd8c13329ba76d9685a01 c3a02a98e1337aaf74fcc83c83ca9db4016b5bcf2db29ad1a1281f267571b101449e9afb50bbdfc780251ec5fce344118c65730a54c99f5c30dd4a9c26c94b05 +generate_signature 4d0fe60a2d913071d88b67d806b451e5217b66279c8a366ea50df32dc7f019f0 30be371fcd62d0aa789bec1511074649e012bafde098d1997867cfd1a1c00020 14ae35724fef1e5136b485fd319f2ddfa31858c2f6e8718016ac61c7bc09a00c c30b2d22bbdad1d644beeda9621902da4336a0a03dbafbf707b681e983ea920e071c59a3c7a01f6f2c2c1dd4d0c1bcd089b02e509402579e6b96511d02f4f204 +generate_signature 52558464c45be054502fd7a95873356906175f45d96a123a679c61293ea82c59 f2df74450892e64b630c1bd242ad6d6eeb74eaaa37817359f0ca6ceedadac351 fe2330bb3f97ebcb29060bad76f122164f4f376f1dfece45ec8e1b74a9d73304 96ce07548ab12899a7ef013774b816759664c77b66340d9fc47754071d0bec06ce4356b4fd402974d447522307f0a29e350997d4cba56c7fe09f85a56b16cb05 +generate_signature 110cb3d607cf3cdeaedf72bb5e757beaaf33735c4e0a6c9d36e15c3bc44f46a9 299e119e03f8698bb3f91c0383fc2fa3566893b430c4181ae75da3b6db52a14c 5f8e1334f8edd3031ae48b6167b69ee954b2dc825d96c1a66115f3e0dd19bc0d 3519a213bd6d92898c750739c323f9b3dcc5beed7d87b374bb0e8efa5b3fe40bbe27cf0794652468c3dc55aaf4069c38b174043aab8db2553579e6468701b609 +generate_signature b9c72e905e48aa099d4618aa4be67625490d93c298ae9a3ad50bd3051825ba5e a2d1ca5b1280a41694c3378e2346b3f9ec16d27905a7d9f1ade0aead412fdc5c 2fd3f1486114d6f267cce879108ab62839c678eb46a846383c25904f47719605 38869854fbe5866df44cba649eec3ba9ac48b74a87fd445b89238b9953cf5d05d6a8b9f592e02745fd11dbca46b4026f8f77a4f52c37e3ce702f88514e54360a +generate_signature a61d5acf702d80aad1e60667f5186d692d03c291015af38ff48b3f627dfb6c93 794e3180afac94b50792ae3b11ab01c8ef4e02973b3c2efa5b3110d2cc5bc5fd 89a145b6365dbcd67ad36878174eab5d3278b2da8a0724fc6b9fff3dadfdf103 c1ff8f8badfb9df0e5618d7e5615184536733acf02c4b7f551fd1ca846c7a20c1ac285c6dddb3b067d30c57e693306dc4333efa84ac82b63d7d546a67fe58902 +generate_signature d0666d9f44a461d56c2c6c35bd098282042d7ab5b4138f80b22b98b940df2543 c97f167d5fdccb9158ab96824c60752f6a1034e163b96da179cc42a8d6f8cea9 443d72bd61cb9ce36ac11839333cab61bc85506b91dec9ae8d0071fe9805ea0c 69625e0ee4bff373f0c0fec580f93fb195c69c56f78cee8544429845c25b1505a9655db1ad290cf2a3b7f4b7b0ddac7da963fa0716ad5d04867dfeb44f6e8e05 +generate_signature dd4e6582d6f65498b6e6dbf35f5dcdfab52f90a8eb7ef7391111bcbe7066929e 1200b5eac5cf70e9c52da437ab1d7da3ad5f951f65baee0ba8aa02ffe69a3675 b72904a3d7180d9c2c16e090c52051ebbebe0ba8376e2defd850bdd698fb6f09 52d652dcae18e38ae04487e6dd838bb836baf5008a00e43293a972e10b68bb0c307c1272212b6f43ba86dd609aa80024ef2def3774c67cc7375d3ea820937d0b +generate_signature 6a1c4227f59b75fd001623aef3687fafa8eff103526023aa6676e8f08fe3c15b 1613c592fccc2e0031487aed9aefc3eb0b86ffefea0f014173bdb28acf4e8bb3 bb93bcb48a4efa8ae0b0fc9393653f2696c77603e9fc3bc1956473f04f64e402 aebf47ce25241355f9716c17a4caaf93b0c30b47dba5e76481ee1353c45a010b7f06061edbce4f9cf24033f0d55beee957c8b79fe534f3c519337ad7cb7cbf02 +generate_signature 2677f450f708876541bbae6ec7030aa87d2f7599bb4f669c190b8df2b62daf66 7a85581bd21fdad5609a84478a9e61a084ab78021c7bb0fdcd6c9efad8cfcbb9 26971f12134601802ef62ff5dacb99e5e0e08ea2455861e63af3cfcbb8cc050b 1d1fb428beee08dcdd0e056e7dff12298596e63139e2261521e1653c14a70b0795c6ecfc6a663c821aaf09f81d9453cfb57a056e7206eae08149aef628abb809 +generate_signature 15bbccb57984f152d62b0c82f12d732b75068118fc3062805c157c33b9f35741 cae0a132e7e32e55906039a7f510d940cbf4dc16543beb1004b636866635a5d5 6d16a1ed565e148f653ae3c20a978dea4f2bdf009dfc8d9e66a201c9d184e906 ca9c6492373de8523df5acc2995d5e64a041b14f0260916e0413971f0741520c9d2e834fc706c8f1c3d34ef90199090d28f5f18b08020e905da1c2c1b463c005 +generate_signature dba1e5cb897f3a931ca00c61296c26aa6edaaaa95d844e8cb29a8818e5d3cf40 8011fbc326c27242f794f1c93e7e616ae847badaae42e30ce345ee2054752490 9b02000c18cbce2c5bd97d9e572ef1124b710bc4bf820b5e740c6f52185fdc04 6bf32ef5a08b67abc7a6785e3d9ac927f7350aebc28fe816558cff2b643dea0a45c4e555b9c4d118ed8ac0a241b9a7ad1467b50ef63659e92acbe306d7fbe307 +generate_signature e9257b43c57e17027c955dc21454cdb41cc59ad29307468ecc595466470519ea 5c841fc7705e9f0691444e3833e84b946f0978500e66dd187b6b4011e2bc5722 ad7c20800b766bfc8d97bd71fdd21b3390f9b5dea4a763ec94209e832add0c0e 2762069c8d0c14985163c29a3c826787f2b7ce36cf7bd338955e0f421fcd070cfda5f2246426e0ae016d0bd14a39bde575d1824e262ce79da377856b583a6b04 +generate_signature 49a91500dd7fd49dedfc127ad940124684ceb5d0d298fd708906728551ce059b d19aa3856ff99afd3a621c339cd5688ad3d108e313cc6a12e76b857a56c8b254 8120cda41048d6102dd2ad998972b345886573e7caa4955df7c3b6632f96a50f d929b5ecf6206fd6106c82e73c0617760c6304d91cb0666a1f13659f5eb9d90658dba6067bc1a45030f7ac837e919d7493cee33c242bc3607d7393fe08b2f20b +generate_signature ee3d3c574231cab4d8b8de5b127423e88ce63d30ffba94e44b7cb52d933c4551 c7c4c1ebb9ac8a9ab8c71bd032c95bf4d76ed79fe8ac53265f8d2e0ff0913e5e 685985d5d94c9702bc73c58eecd4f2c69aed4b5ec1136af0636ee81d94c2530f ed0fb86ed300cbcbb6e08756005255b7c830f4fef04866543d852f70a18d9f0fdfbfec42faa466e4ecf82d4a4f17dcf827dc7fc77cd2f42690f6f5d06f9c2d01 +generate_signature 1473d8159b8f7ea02a7227be5ddd874e9c6e3c114708f7c46de729a81f0379bb 7d995c9eed1ff652f954617817f772dfeaad1ebb968c3b4260c22bcc6aa2e764 302201443d6fcdfd98585792085a7b7e804ea4a79bca8fd6b1d24d9ec30eb10a c2a4b12dc28da79accc33ea50f4a3a89cc2a34b114c84741c2a412dd6aa60206176084d12503129ddfb39667911e7ee96b71de75915bb51a2db6d6c1c885db0a +generate_signature e0b62a10aa27778f06ce587a439179f6be9a444588be2fc6c61fec24e4cbfae9 b31d7350dc561bdd04634b1c65d4d07721b21be061f4ea6779a1a9f0268830fe 033f706a513a3102241553f06fcdbaa4f1899f9776f51584495e0eea1195c906 3256c679796cc12208f70c7a3a897c90222f9aa0c8f5c7bf5baab592b2ed7e0abf0b5ecd176ba9aae4b89cbe6937242d395d68de45eee745e7d1a1990b1f1a08 +generate_signature ce6fe431c9d841f2f7494d0c214ed3f4d470b1de679e30d0980137dc8cca5369 7c4883ca0408e5279b4333cd5970366e7d2cae46e88bfa9efd5b9554b8677438 bf876c480666daf828d4b5aed26b853372ea58355c370ece915600f127dc2a04 7b028a068f221b9e086bcf0b190563c6b70ee19f805f28d1360229d239060a0f3e263e09505a28a2675a3d3f4981d296d1be63b13048bff9042f3428477cc109 +generate_signature ea984e9cc588ddcbd884642c0c1eb40dd85cb049f22688a80f42b0835b3d2721 4e200ad1863ddbec552c937ea0f28577d93959185269c21b5b0a2faf11738582 144de89fdc0a762c28f54d7cfe1d9c7c1dce3d1b520bc8e6b1df39405708b505 8accfb3a1e3f3474503b36612b54ea4f5d25ea35f23e07f571482db5b3a0150a46c0ef2a4b74e2a84d3ce9bda75053453aca35d360064b14c216ba18b115bd08 +generate_signature 298dbd2126d0276c1d95e2e5220bd529b25c16ca670094a667fd3397c7b7b22b afb3b9bb1769caea17f4102c3e59eecabdc9108195d33f0129307af251f8f330 f17b90b7c4bd2169f7f1f5816f8f72a2bbfc03c4617b957db3ffc04b6e42a303 fef52b0852b12a2f9b9029939b23ee6be96171025a4c9c01f39710183456330442a8a3471e0acbc221ea06188ccd73dd76c6c2be87412d81dded3d9eed3c4c0f +generate_signature 9c5c87cc348b763b29665e7e9fc83c2ee425c57bc610856179879b5fc644dcc6 67894b54c0628bb6c56ee847b712253782a9b02a506dab960a5b8eb9e613215f 082fff76e7d834c3034b37eba3aaa231b1b190ea3514791beaadd4b3daaf5508 6c154567386602e698b0e97befc089b8600d1d87248554a9ecdf1c56f3e83c071041058835c497c5fe26d2103648f816fa5c08560a7edc725f2c643bb3052704 +generate_signature ab263c0d9626de5860ea22e2f999676b455641c23e9edd517f420c01e84cd2ce 1ecb565cdd6da4bb6f6cfa57e75940630360f663cf56cc716f1b949419f9b6e9 d22ef716713fc60b11e619292c44ec432fe7bd4cf582544fcf2ebce23d566009 3ca0890d5c72635b0060612f0d6a78383dab96cd050164b5fe2698618f57810844f4e5b6d71252566fe40345351a049b90fb33b360f75adc6aa2d04a3cd30108 +generate_signature 2acd9cb6a9418f90b507b15bd258878fab69afebd0cdc57ade4719ab89be32d3 814c8efa712bed141ffa2e72b2317dcba9e917330523946c2a97e8af3f992fe3 199e9c09c85ff00f9431ef41d7d9a56aa05cb9d09f548d2fad7a783eb2faa400 043e7c233d8d74cc9448ba023767c896b4c24b6222ee85e7089edf7684431c05eed29ea1d580c2bc32bacf54b566c070dc441639636f670372b18235ee450a0c +generate_signature b8aeb16bf5d31ae4fe7e321da12d8defa7923946e197390f0a1696526099cb03 eefaf0226cbb5685e2770e2b72a0de26f30c9ef5673053fd64e762d9efea370f 4e524c57a12abdf684c05bd99eab65efb947ff2a50b1f27ceef7f4cd268afe05 7ea54ff6e9e18258a4eaf50aecb8b8631e5585d0c5fc0455a294210dbb4b81031c3fc6b4c3b46875cb4673a76479363b0b69651f44f46626601967486febc007 +generate_signature 4fbbf6dcbbf50f92d5f60b467c9bcd4921b756691660b2ac73b256e6b614acc5 0453e634c6c63a0dd1a999307b2c9da357b54fc1e89eedd1240f6501475d35b6 ee840259b97e841cdd17946b4b4acbcb90c7bc05996fea802825c62f18c00902 612d2ebe0aa2091ad0830f5bd38d8aa853a575447fa209b23a92d5433ab86c08e00ae2d043720912c08ecf3fc9ba8b632ee79dcd7625f1ddcadc4ef96bf5930f +generate_signature 7038946351d9332520e91640e4ea8aad9642e66a9661c1e62a519a3e03d24f57 9bbd178c6a6bc802cc56f0565ba7ef52eec02c1f0098a3fb1a958aa1885e4d63 f68267ea33101df63bb15fcff1bc528b032d770fe69a0b6f0341c614e7ee5d01 ea8870dcef734dde892415bf0cdf3da8b692cde8f53aaa87e5a04bb29b31a00bed141061199d3bcf302d22cb987ea6cf116aa41e06e2ffe87d6dbf65e868710e +generate_signature e8e5ae2e4f4f51cff909a6a73d0a24be6e049cdf32af893b6156a9f3bd7a0613 54e78d50e27c98418a28d0972af5ff4f7e6a5f2e212d792871f4395a4e0842fd 873ea1693f04187e4b39eeb4395d38e2b12494f3a426dc3ef1bb58a2c0a55608 074767a64b02256244ad61512d18e388b01ecca6f1cc59ce543844c2d5175b046b54b1df249f9a76ca88cc5978549955d434e90d8c9fd7bbd90a5207fb34410d +generate_signature abdb0a9f87e46a82240bcc77f5aa5f8c0981398731f7bc392b1e7f2d569c491e e8551e4b00aa9a74084250f33285a955efc56a310bd287d97ce3f213eb51f97d d28b26262099d372ce35d41e3a289d793b425a62205f0f22fe348510120fbe00 372168122486de89b42e7212bffca1d12f8b823548e546118527171aad5f0a00342160275980d62b506db43eaf8e4aaed7f860652e642640640da626d7f0db00 +generate_signature b6e7c69d55fd322626930ec53729cc2975b911706721a48e41ca406146cb63c4 58a63d82e3245e9771435211f2b415c5739c99370d08011e0baa1b502302b16f e1e4f2327b82c6fd7fbd35905d91056df0c8df6702eb282487ac55b3801adc06 92e1da2b471004dd692acb02de5c81e9894026b5110faea46a507ade972686014d1356c54064ed8be0767e1fb384117adf2de59486249b7eec8cde356a72bf09 +generate_signature 98d1fa8e6b508971d25a7147eedd2347f076b72729a35f9b1a81203ff85c810d d1e1c86421a34483152b3cc8bb124d140fdd638210ee1bf0df30c80b54e61343 5acdd92d2c8eb54a0ea6d466f5c2718d703a5108ffd352b6f8ff70f2baa9010b cd14e80fae2e57e8763ea261ee8047934e99e56ed1714358c5f467aa4c6762084a886cb3e8d308cbe647b6f03550a0232cf9cc7792fe74e068248968665faf02 +generate_signature 91dcff5d1d7ec3854de213306e4e744ca4c04176d4b20830d1a2012e6cb026ce 15b09f757df19ae41ef4e9050d0be8e76c151b20a7f4f792748f83aa14b7e0bc a9e865fee7d1469c369dd683d7808d5ec65cd0f035dd5e09c1eb034a7396c305 ad16e6770c959eaedc99847d78717a0114d4ea2ff2a5aebcc9ffab231937ac0f97e3a1aeb7a08db20eee6cb56cea3652bc124357f1bce24d23c8b59490e3fd09 +generate_signature 9291ce37db25aa4c373266a7aa25d585206492949c8ca4671eb7e470c6a85778 9d35b6133fee1dca194f7a64e8c1dd4a03a76e3805cfbcf80c9c42f9a45e91a0 6716b59ebdead7684c13ead0a4aef3fea77cda742bed36b3dee1d66a475d0309 adcf1f74fc81b559c8f283bf102539961b601efae456613e178daa6071135402aeb0b8995923906a7478380d5856bb1c685d09d73e77726ad3140e392f42020d +generate_signature d2dfb2e50813260e6bda0a4150ad893b32f99b2dec47ca8402f0ab8ae30603cd 7b8bd1f571d5083a8316fd99efd6a3a3dc54d87d78425ce1f46185a2cdb18a95 5d76e12ac8050665387e1c8af4e18308d9f227b0decf22355fefab31ce1df804 50ae30997a05830cecea0569708e71a09bbd80433808b9d603b83dfebbab6e0da3ede4c988d6cc5fc88deeb84849a8684f41ff62b4a9a377a78c8d1101bf6b0b +generate_signature 7ba6dedecefb1d24c087c994a2ce612e568fe90b47eabd04a9f44a9196cc7d06 ee5a5d892f36ba3ec29b259ef936c1a14085caf56e5419ce2c85dc356750231b 648229308a95c52f19b3011f816050496d77dc42afff59adfbfa851ff4da470f 8bd877bf496b8fcbb981a040e3f3562fc4049213e7b4b99a625da77c3557290c8b00a9c7eca02f73af59bcd5f517f57cdfb0fe2ebfeaced50857bb7c1d12d305 +generate_signature e53c575148d6502987c90b32ebc374a2ee76fdb167b4bcc2fd963aa3293a50d3 db099a6c2dab7de4916d3b2e21c2bf1f89f24280c29a4802ee27899089353573 9a34773656675cdf07d64bde08eb98585b9719cb9d52d919d54d70047639b508 01b2fcdd617661bc28e35e737fcd1a9d658f919f605bcfb27445f5a6284b1b046ef2b70f47126243e7ba20ef28de050b2681692297d38ae7931c40a056719d0d +generate_signature aa909b74b14671e3c56b0aa0a39e956897151694e42d90f502f5426606a4bd3d a2481756e3204a51d40c8088e69851185a29c962f97c800a88102b812606ca0a 47d0d3279058134729f860e7e0dec0943115dfe22f052ef7756b5bc263aa030c 01fa19ae7795d56841a2acb8d8e0ee9922e10832ee3c9ece45516afa1ee4470061c5a64d8d5d5a853f2665640559b1e6866f3c8de5472ff69b5816f53ec7960a +generate_signature 09719be164df8ab8ffd5d4b6820d1520992afbf6fa717cec48964f3af489af51 3798b31c7c0f929e70268010839042b76df746c4f46b0e219521eb73e89a0d4c 516de341e7a3e73b96f2a8e3c0427bb73eb04fdc967f6e0d2b87135c0ad72907 458e445ff7bba518d30d20d3eae97975574c3207e0e8b49e58c39bb72abe8d0549673bc9f0101dbbd8bfefc403872f834690c11906d40563a8fac2ea94deae05 +generate_signature 95bc343c1fbc1ac7fc31c617f0944c8a5caa4b91977bad385ddb7b72133b9918 3b510a05791831d9d86d268a3c56f48b40bb3b2854236f4a1457aa573f1e3616 83ed82fb8ea18afb5ad9664ad412d2a9ad5d36119aa08eae5ae48d3b6e979c06 57b902b0934f8f8716502758edc678fc1ce9d145b236b715ae91706e78f8fc05a09c96d05a176e349e966c21336aa2bfa3a69773aa8c5f65cde3d0ce786f1f0f +generate_signature aaa59d9c7b9745cc62775f1a565de3e704ea082feeaabf91897c163e9c453057 f1d8a8a9d1ca40c3c1985276b8dbad02f9b05b308633f8d53ddd23fde382046c 38b8d2d9729218db5231f4e46c2641bc43983320d493eba916c9d99aaac88208 a26c43b0acc8a3372f401bbcd727231da599c0129e05a7be0c5d35fa0b407d007cd0a5334a8484bdc1c060547da745560bbbe325b13a77d8043c1c34aafb2a08 +generate_signature f4433ab65909fad4afe06cdab382d2fe7b1f2d12a47ddf3c4c634bc9cddbd52f 320c05d188d2ecaecc06c15352dedd207739ebde86133f7152b3b83074764778 d34800b40f3d39319a844b8a85c65bc8e7aee303a7b8e423d2efc1c4c48a6704 91f495d13061dc36c89a6b593bf09d8b69adc30b37e5c0a15d6fff53184d1f012f94bff895c797f33afb05e2ddfe982de47ae8189217ac655c05734ef1d9b401 +generate_signature 121cda7dd82e45d586ec16c5ea52fb38026dc84d3acf8b53d9b14c752bbac650 5af2a1a8a88c0e0b53e555a7077a737c2cea050b7dbc0ba3d8ca2ad037c908e7 007af32d54c623c7eeda1e02984238b6799b08db17e3a4231daff62ab3d1640b fac776b68d0563207fd2bc29e074fb0fa0c417c1637b0a376951ca1a0bd41a0f097c31c28f04010c8d310c3a5c13ed73ecf04ef7da7f794fa5b2c872facd580d +generate_signature 4adacb5f6d707bc06145c6c445ef70db86145b78af456ee5d687530713f153dc 2d96e3dd0c90587368b586dfc7954d5d6ecf04523d0797a0fedfba91f65649b1 1f8093909e4e249adbd396d9296980bf6b65164dc6ea679d955b98502622de03 b97963c1c9161edde2149181d3bf17f18d3cb3333771b377ea768bbeb3f69e0cf6771d0a1ff8553dd669165c88db009c1e21446127da78fbd5bd524c46e09704 +generate_signature 86034412d429483e52eaa4f8e3426f920886f5a7302f9733b60c3368f8feda5d f47f3d62360154548b01b27b8e20bb653569dd2e0d8f3e2e4926563b19d2905e 383927c522c2201332dac111a750e15bfdda86b1d47483a11624290569a4f808 e4a23b97db60f54bf1f294e37135294b06e7c6fbef11a931f14722498aedb70b9c1729839c09fec4aeacd074c9feb3d973f3176a1f104ef8c2ec49214b008c02 +generate_signature 891ce05ba0c25b7d2409dd83487ffb0328f515d0dd77bf1c2503e33370f8cb99 0103c4ef2f18527e1630510bc27ff5966b018199c0b82a4a0d62815c74f1d0b7 fb1bf90759993320f4e16f3237c2d63dfddf6594f21c74e20267d61a8bf7520c fdfe25319020489bb7b7620fbe8eff7aa797aa8d6f454934f2151bef4088390e1557c87b3205a07f8e6d14cfc77bd99911e4416adbe8622e5302c1b63c4e990b +generate_signature 268378aaf9b6403f036e24e356e90d67c3b82edb01180fb7d135e895283237b0 32b9fbd425dc1a9e2d6c693699594bc310737d3954b8dcd5f3bec5a056a4e63d fa028fb4484c90b355655014f1f20eaa711d1d61ed8e200b718b46d475603506 548cd3a0d3232bad8329d0e95d97227100736766eef062dd19dd0fcbce2f7d00a8eda6e24c9dcd7c9dd93e364d12be694b874954ae66fe34aa6c197a4d003109 +generate_signature 4c1124a23917f12efbf34c9c90b1a079667aa900b78b8a77297f55e50c3d6e21 ea3392bb6cb736c3053fe90bc64917412190b673f2d1db93338414fafbd15414 54cff91e78a84670f568167ee279e72cb0a32ecf75544b5e4d49ea5a77a92003 04505da749d2eee35cf9c644216dba9941a5b342e23be04062bab1b636548601d88a241af1e02fa14a4ec666b29643bc9c07ca640bcfd001f93075d90ce94a04 +generate_signature 112cd2bc459cc678065ba583a09c83d962e385cb546523901ec4d5b47425d0e3 0693ef1cfdc58c35a00dee5477fa4d8c73422294c88b8f9d826dbc414dec2306 a3d6655a3bcdb2baf44248a541141a67018c9080001457e165ec29d2c3248600 b290e9849810e5835e1d0a0b08049f7940f831a121d098a2d2ea899879cc880398b0467b8c71188455b6532a59500cf5e644d15b408befb2bde9cd2080360c08 +generate_signature 2de379049e28fe930809c36f1062769665d4133d4d668a718b84f9183332a2a4 29aa902ba683847b1b59f09ee480e1814454bc72c48fb3884540d369f1a9f2b9 de444332bd0fcd47f6c1a5b7fc7ae1bb544a2298672288f04effaa04aa7d940c 33aadb26e603ccf402cc1a7f441c063b10c72b064fc9bf9574efcf107d6c5109cdfd79fcf8ce7525d96141cb4e92464eecf51c00af4f29e8d77ad5a73410dc00 +generate_signature 0d2ff1cf8e58881dbe688be73ce844772c6696636469eb8ca9086a3ee2ce07a3 a743a9ed747abc154394fa822224212c3f7cb9fdfcb40a24a142740d1644de9b 08281ab64eca078a437c7f10208d0f5692ae85f6603fcd1d9e8ec2bcf9f64d00 0136e3f553f90dfcd9bfa978da2b9528ef4c55a70c5a9ca71af3e6cb3c9e7f064b2fe3f5350e63577dac7660966cc2b36b73ce2bf2764fed129cb56f4ed1850e +generate_signature 7104100649edb970e649881d4059faee3e51e221a3ae07fc33d944eb6c192322 c10ad6f4972dc849e2bac69e549929822102071d43f404abe8049a529b9c930b 0e060a4fef0a46f9c3d8702b38f82270e76124aac1f6a8e1ec402aa9e7812b02 2d9ece3e8a1470022fd9741ffb1469d458a786e876d3a994bb285366c912f5032cb116d577a457ec4609bcb672f68ff87a5036f81a41ce2b61efd00df6dd270b +generate_signature 995be2688f3f7354dc17d7fbd48e010611de9d84247e1e738be9faae777d20f9 89d352fce80cc5dba45a618468866cbb9414f5fdf608ed5dde3fb446fd665c20 791941512b24ea359aafcf96ae0d9a7f7c24d34e2177eb1822dcf12776580506 811e28243cd55c3f185efa2b6cf544411cb30cafcf688244471cf6111bb8840cb3b9c7b0e3ce1a15d9ff0a70029457a86c2919a68b43c15970ae804e5fc1b500 +generate_signature 77262429448c69ae1fc4c99b652c282af2082b046a227b57dcd724f56ba0a696 9b605f01f72e9fa6faffa84481b9e609f7b1e20b8a33b46b16cceb7f5c0cf411 b912862268d63a977c75fbfe9aa7008190758367379862024232c0cd2504000c 03c825f7d6220da4c28556383578e182da4c266bae998a083f67ef45d8a86a0867ff545d797ed8b7d86952f086cbe8fbc7d4f724dadc39dd5488b067c88f2e09 +generate_signature 382537401e042257c1a0c6062ea8ed101da3193a6cac540d675354f12f7cab82 1c209e65076e9d4dbfb5762f3d1f59896da89efddec6d696a4498303f67d667f fdc2f3443bb4f28cda3ee7cda5255d36a89a983b91361700b63b87db477d1108 74776b5babd9fa36478a8f1139a793df63451c8cb4a4c01caf846b31d9c3560b4b395609f436a68433dc043526a54ac5cbfb6a73febc4f76c6076bd477cfd706 +generate_signature 55b89623ad6f7b1d2ed599d2a04d8236f2d3fb8b337ba9704439193ac7956889 eceb2e5ac867b1603e9c61890aafe7871b3583d38c0a9f4084573497bed466a8 cb1a0a38f92fcf57064b0b1e666693cd1abe9a2b42cd4c7c667c933e3ae1340f a7f8b754cab3e57c1de0343bad94d46cd46cc787871ea186d8093c3feea1580e5980a17520269cd32b183bc15e6068ddc6efdfe203b8303b61214ceb9dead40f +generate_signature 22c3ac4d3466742eef52c67e784050c30b5d5ba7b9235e4fed8190ec18cc50ff 628a568ca8cc07afb1ce46b3f78af000a37dc9bca3fb8f73d7d5e697b267fd3a 0e180e4841d5de78630e900c364dec44daf2f74e72066cc609eb917995a8cb01 2e1dbdd3f768386ce44085d32e74358acc219f00e28bfdc355293bdfbed17c0d4b6bc3da9474caacda128a9d1b8b402e7a4ff1f1d2dd312608e203e2445e2103 +generate_signature 8bcdbe48e967d9e68775be61493e60f6c5488427caa85c0c42b3ca3aff756b75 5de2c451223d0cc7293ca1826c02215c6b519bf3ee0e5b91483990c6250d808e e8b26635c9a1fa4b7f9165b745abbd45cba7258b6420187ee6e4325a89b0e802 66020a81a792108ae070b7878fe7764d6f8e062a323c66b4bca765bef739fc08e60683cff028e280280e5febdedee2265a022b4d9cbbdccc03bee47143a6fe04 +generate_signature 37b33cb52f44b8c43a0d619ba8270eeb2ee657c0e70734a05c2de878f77ab540 7f5ce9f0aff75716477a94c13dbcf878a8a2513920dc40fd2a29814555ce1629 f8afe1e242f39bf2b08c35d6d18aca0900a1e5ae729efe56892919484b5e8c03 e690a20785106553dee2b3d789599dfe94d2c3eab0131aaeb3f408afc0a90a02cb37c6ab2f9f0025c4732477c7413b0d86e69d16dbb4ccb8f003166242372704 +generate_signature 069bfa548d79462cd27ad3720842430f2c4947532513a36f0e012097b77e6a7b e6ebb1f4eae6ec2d813b9213050ac47c0054723d754685452f112551e9e6619a 6cb63dc9d194448e748210d8bae8bbee8e0cb494bcec2b51ae06c9f1478b740f 35819d13ae03277e24bf54f6c1b6bdaf8f67797818da95db1c75a7652f22640a14b45a160d4f5d333e15ab11403d04f457a1bc23c6cf9e1c0c7b81660315a80e +generate_signature 3fbde8fc46fb4f1001ac7fd712760e0d8ada351c2d2c28b6da74bbf9e2795c80 79ae18c7007b1a790ddf2e6080615398ca77b4d06c1b2f7e536cd6b8ddd5a21f 70f8219f8b4d2567a4771051f2988b84f137a4f84cb26f081843908c7d959809 d9e9d7a9ccbc3f77374632d807c2c2d6986c30c0075ee21f634e3f21ddc2910bb1b4a879cfcde156ac9d3bcdd524eb9505647190c07f2b45e9291a8c1ae7200b +generate_signature 7b752d6c0cc3750012b98ff0a4fb325eab31e8bbc0dcd995d9932215386e4582 9519b641893b754f8224bec54da6b5c06730c3f4c7d4704f2999a21b46a30032 0e931c71c9a3789e25f79e722cd6858d0fff3f5198597b056247b9cacbffa20b e89357d5b3c4606102f77e417a4abe0b13d10134ded514bd2da9a305f611a801eae9be74a4db727fa2323172612bc1f1ab4fb074c9ee963998fd1cef2b67a609 +generate_signature e64074ccd0cfb5b56a89cb07faa1386061fc4853b0b2211d87a7af02bc3fc0fb 7072faef529d5daf4fb8663574a4ed86a7fa17e5cf10f09190e280500a216738 d6d77164dd8a1f6859e8a814d2a17367b8a8fcb5e6bdf345d74ad6d70658520a 939e1ba922c1b0ea5ee22a63712187e49d58388310fb12bed9788987ed731005290feb84c4c144bec363cde9039ddd80b7a73799a00a3134952742d7c90a0907 check_signature 57fd3427123988a99aae02ce20312b61a88a39692f3462769947467c6e4c3961 a5e61831eb296ad2b18e4b4b00ec0ff160e30b2834f8d1eda4f28d9656a2ec75 cd89c4cbb1697ebc641e77fdcd843ff9b2feaf37cfeee078045ef1bb8f0efe0bb5fd0131fbc314121d9c19e046aea55140165441941906a757e574b8b775c008 true check_signature 92c1259cddde43602eeac1ab825dc12ffc915c9cfe57abcca04c8405df338359 9fa6c7fd338517c7d45b3693fbc91d4a28cd8cc226c4217f3e2694ae89a6f3dc b027582f0d05bacb3ebe4e5f12a8a9d65e987cc1e99b759dca3fee84289efa5124ad37550b985ed4f2db0ab6f44d2ebbc195a7123fd39441d3a57e0f70ecf608 false check_signature f8628174b471912e7b51aceecd9373d22824065cee93ff899968819213d338c3 8a7d608934a96ae5f1f141f8aa45a2f0ba5819ad668b22d6a12ad6e366bbc467 d7e827fbc168a81b401be58c919b7bcf2d7934fe10da6082970a1eb9d98ca609c660855ae5617aeed466c5fd832daa405ee83aef69f0c2661bfa7edf91ca6201 true @@ -4198,262 +4198,262 @@ generate_key_image b9adc45192d3096748f58c6aa4180c1084ee1c78a116c3480ec522b542021 generate_key_image d8e79793544bab0ad58fdfc3c1bed3759f34dd272a16d487172013ee7a0eb516 d033ad38553f7da7820e2d808f89e13ece56cabbf599e8f89346998bc2607609 0d65336c2af006c30b9b3726e6785765d35bfca27272c7f6b886ed39c9f138da generate_key_image 1b70e47678324ca44632276550876be0e3507884e8ad9e268788d5e03f7119f1 0fc5090c2d4d32178cd217509da32aa88966e3ec90fc0e1ff5ee6ad42f038107 f06ecd7a0b5ae6a98c1d711dd7bee77f6262f84df6ad013a355b5cf823863ed0 generate_key_image 7427c053014583ecd07e81e1c917906c381af54a7ecaba8498225518a156bae9 d9e07405029a8bd4ba75dbda93756228e18674e3138d2f7b2e7796ee7430ee01 1253d396a6e7a2bff50c1fdf54bb90157727fa20466fac74e9138355b17092e9 -generate_ring_signature f23065c28a04de5d38606f37b7cf29c680c06c2a9bc00b2bb3bac696f9df5c61 7046e48f17ddaaebb928ae39dbe62c23441f01ed40d23fbf5f07e5a352d06197 16 9cf7b3138ddeb90d60b31bbad6458bd58bcad71192d37e69444b28f9450ab37b fe25432b3e417e7123b3b55661f9d8e01b9238c801feec00d01b0a144439126d 1af5776b1bddd8ea5e775e035ae7a3fbfa2e08401f7bb01162ca7d2c9ce21aa6 8138759a1d698a79ba0ee523980c7899f5833ccd15f9c224a02c44accc46ed71 00cc8407088c92ac28aaeebfa3de0f699de189923fb7fc18daf1361a7356c203 6493d68aec79197563b65b4d202665da993a27318538b46678aba6df7475e6ef 2193c3b3e8c038d3ee84d30a3e6b8ccbdf29b25f9b576953907b28a3abe41689 3f4692db3e0e194005497b99f5097938659fb00906b308e4413ab07d20d21ca2 5adf105a86dd1d4579bfb3bbd362119122bbf9517c089273570189f368919f60 8e2b0684c678a5e9500332e375fe28f9ce127e1d847a56aa198f81d02afecc3d 75cd3c47a8d0616d4f7374f3b652e2b4dcbedc826b8b0f22c8f2b12d8449171b b26567ad1bc3e39a4d59795b347cf852c35944498d5ab3537ed76f63111bc196 a78edb131426ca91e0a25be0197578a4d4d2876d18313a940814d8c8e65f493e 943aa48aa6dec62a8f30eb16e796196e3a35569203e58067228c8b496a739d4e f9d1ef180af3b08d9ae7ee9dbb687591bdd3f8fd2b1912de622cce6a9e0d25ba dc6c4e0b458a8f3b47d5841dda4b34bd01c845031421c8fc34d90f2356564fd7 f7738841783117a5206b00b4054df1949312437643b353459e02552d2c69f10c 6 affedcb553dd79b07e24cdc8dcaeff9eb6bfbdc7e69bed31e1a947d52d82c20de0d21492ce5f5f624c401f8109471ca96a98f99e30005c619ca940d90222af0685dc25c4e748514f933d6eb4711bbcfb7edbf7e01218bab600b59f24a62a610200ca954abdf4e26ad4e1d65510fb955856b6922acb037369238ee9e17b9bd50ae2d2449cf18c587de72a07be09dc2b47f1fac913d32002b12df7db66c8b85c06c07c46cb7f7c102801a21cd4b606991a1e5465c9bd9d91eff876f3e22364d70ad7b7da047d45e44b49468e430d1d371808c82bc990fc9eda298526aa867a030650e07e9d41a22f313c957d6235609a340f93dc833c56fe54c83b6d87a7480200542f37093a3a948c87dc86767eca8c2a1f235d45d9f923bfc276e46467dc50072f2522498b02c4f92fcc02109bfb8f808941d585917b281524d701da984e6202a16263d459cb9fc5a5ba1c3f771c9f887e364b2d15e5d460bcdcb57bdbc8b30b7d32ee6f19d75dac8608e9c18567007862fe13a1376f306136e0a82354d7bb02501214e2088488a4b152f3f57a87cbbd4179331a2d454482e81a7c56cbbfb0031a13dc85a24b55e74e809910a428328a8e50f4bd6c874abf77df07f427d1c501f2977337b5908177d2da3861db29d7c7d8afc6892ca9957b29a189c8ff6cea078917039ef19aa9aef4bd5afa4c462fd3f2f44c1dc4b6009867259b61c6eff80163d876d1c2a4406d83bab0fda4a6d1685a4eb6fe185eb15cab654c5b32c6370c8229070b7cb528a5e7d5e69572d376baad2163f9d19d08c5552f7897b2f85802382928f03988b8f34e43b9d47d695872b3f6522ff27c9a68ca507257f3756c06bcf4ec50a40c79d0a15c973be4c197da89fb72a4cce942fa57eb517c33ce120199b0fa3a7ed9ebe7e55fb7b20f65ed1d357cd837ece71c587ba89bf862aba30e16b004a0ce65d06ad7d4941d5546b00de2135433d1392397035336627aea4002ff7752b457b4202dc73c4603d2ebaa7164d3c140cdc2a65ed8cdb97543d9a10df2eb1dde44dd0c91db6ed27e71cfe30c6533bbfe0def81a0fd854871e8313e0d77067507be6bbc75e97b616ae568b44faa7dc5ece426da412336b34ce8042d049ac06e7d934cf9401d10edc29df614dc01081d719ea28cc79078290366604f0d99ef213d7eb8266c8fa24e4a393f5fb4a6aff00446a24809ecf3fd448b66b50ef06493484fd822eccb715b1846a2875ad18b51ab8e326ad2fc93ffa24ba080082a90f4a0b4c0af4dd40162c0b890931924b41418437433446bbc5c3e1d3d7707ce09679004ce98bc2c43b7c464f248a7645d5048c62634aa54bcfa193ec6420ce869d7e9613a459c93fd7641cde7e72760a13a8fd02aba9dde33f38b5118260b152501f7af3e0d56635f304447204fd4b6c26dccdf4a9f24c63cd53c48e3b201 -generate_ring_signature 89226689e486049662075f55d46361d821c5ede1fc172581458207aeb3d7374b 4048d63774cf0e3d73059b76c1160f5b36fae2add758c0b5d0a76eccf459081b 2 68943d3665e40eaa5d8ce9a3279e70e9d00afa0cea15d6671e024efcdad2900c fb89cf7108eb3b68243e732e820d716e11a0baa0def8d2d837ab998a9bd642c0 f75b48b628a7f3fd1dce1055f7c0b81c36454e012dc7ead8d5528c11cd52990d 1 b75835eb2db9033ea11f871e0e161a1e30492a62f3eb7a82c8c121831dd38e05eb354cae51a82957662bb379e4f64fbdddf3cbde4ef12f78110a2c5ae9d9d60632da8d7b460b7bb875b5a60d61260db92d107b3406506bf8bbe5de83380370028bba901d4ce74b00dc1e2b6e95d6a5f8e9a0704d107992ee8d3cfb773318050d -generate_ring_signature b5d8229ce74db9826924b3c6e5caef7e7e5bf671ed19fc11be2989dd3c47931b cce45b0c2c85b4210b5b11644afc9395fcb69ba98603adddb5553445e9ec7b6c 144 0be075ebeaf99a172e8e39e394767c3a3f987767dd4980c63801034626594ae6 73fccd68f4dddcedf7206f4928e50477501c1efd04ac95c240bcdb1311abe7d7 82d86f2cee19fa0019fdd5680e9f5bc9a0a19fd20fe801693646eb80029cd721 5028c84c1470faabaa794544bdc8cd2c711465def027442c68cbb51d8b26a173 cd8f47f244cc2c434906ff8a3b05cb0cc6237dfca4df80f54f737ed73a8de6c9 b5820b71fcb114db2922d51ff6ce2243fee94e0b2b325539c7ca31701a291b9b f8935af82ec3e9e25665a07e742a0db3ab87cac8f9aa8765bf50edde6b039370 3ccbd63146f9d7b10b80d92ee369e723579b31d62d93c6792de1a4eb498cfb3f cd2d042ef2fe9c3cabe3092cae589b3ac93bc006c44191ec547645ccf67afdff e2d93bf17b1a2fea61e957bfc3e8a9f354ec7716fca36d9d3147608eb3af97c2 8a48888c11261fb5a99f7d446d679b545f9053b2daf6c82afafe43f8ddc2b6e0 1d5a580055cadbb839eee43d38dddb20d970ab5c1a9e2f05c553d299cf495774 f502b134474771d28c63829de5e2cbb68f561ac4424979ad81d9561c77c9a8e5 1d0c147249493a9cc4c6a08a5e1b787bcafe06363736cfa165b1a01347160495 5ac2b45e81b6098c0039a2af48d87b98e39923f3140768b797ca3d33929ac7ab 3859ceeb597d189dc18be59daa68d39bfc2361b820524631bc05e22b78ff775e 96dcad77dc296cfb3929634e642229bb0f748cc674266991ed53d64956d6677d 3035e9feb91d2bdc3917a8c58b76600141a4333dc94499e36b9a75509ec9278a 78b57f73d0d54237460f950433f02b23a8b73fd686db4872dbbaccfd93e4962d bbc589f174313a7862b5cdbe0bdce6ddac58d6167fdd906bf255e3e98ec79160 cf34f7674a32ea2273517d25472e5bcc686f3f461c734e4e93a302b4c9439d0b f880e06bdefa123e7b6a09ba0e0de33fde7cf7ec3b792abb07ad06107e192f73 988521245df44f9840aab96fd90af3c22455058e24eda4f2e33e1269dff4430b ebf20602e0afa497d66b8e34e5269ed831e970f912310e178c1414d25ca71c35 5efd0c9703fc2e020379534b469c526171091147c59d2552a871823c11821e6c 943dc0fc80c35556a653903f09cee980d4f66d9e7179dc26ace15aed32cfb9c0 274eec520fb3250395785dc104e5b914ff88a4d7f9da7e07da6c0830b2f3a1c2 560f145a25c13e0a1f6d9d79edd399d5afc6b09e3103c09f1d77b11a975e3b2c 3b13f3fe0e4428737929f2e13aa57f675e7e3688d8b7b0475d0a950a730d354f c0c4ddf2f2fbf1c0c8ec3873285ffd498b594cb5714c851c27e1397d386e12bb 81ae0f6596c54518ccad5d592123c5960e17b589686cadf3b530b185d93048f7 4ca1879a6b680cbc2024fb37a2a17853767e884cd8e2ad20c2499fbdb53d57bf 56d56a1d537d1f9bdc2505bec8cd238d765e1bed5c245c1054422520d7204ab3 b3c4e1763c0bdb20cd468a13560fd0ec0576d167a3d3d62523067e8e23a2ece4 90805b8b9ad99246003275c1dab6d1bc2dd6bb12454ac381c662048c5bc54738 dfea8fcc3267c854025ec6c917b696c1905e23282ccc042d81ab3e37782380c7 d86baafc5adb032b78d5a7e2047b619544d0d97236d79002d3a091897fa0c48d d1f6ee6693c03674a865ff5f24316e48f844784161afffa57f0bec4d098f01de 9ac006713f84fa6e3bcef13946203f85dfdddac7a471ccc417a65fb929c52d3b 9cbfc2677c60a2cf155dbdcb8065778c7428f6ce8524f483d67eb45e7e9ce463 ab7bd7860210e59ad3231d0309b591d1032b6af933d59506641e55b659efcda3 7d5e773ac67fb198cd86d94f52d8949d3a44bf213980e2e9355451e65d5d3888 3f34184c4cc8f2d7fff24871d02347f522a690866c695f2c7592cd37ce48f9bc 6674fa097442f932e1e25123d663672e2de17b2385b8c41a6a509c46f89e324a 234a35e2f0da5e2182c2450e90101cf1690235894fe8ff5cf7036e03d4423865 f2c8967a786fe891000838ae56ee84891df8dad799971bd87ad9e3aa0dc1d03a 0484249715649583e496f21fec59eb4c35553761ab82b3bf85ce18e67c184ade f6104748641de70509aaaf7330d390f45ed534ef38bb11ebf52ce8e6b1f9cc42 beb938142360b3b1f0933990a5c0e65180d77c2923b2aa2a3f6250c153c8dd13 518d918bf968bdcd664b6bf24e865348f7093ed723e0293ee86a2402b87e5df4 e86e665a415da43c81bb03d895151814ec4c045147a4bcfe37a014a846f26436 1a11a329f7530ab7b31198f03d8d29754ffe53af384fe5d4db3fe90ffa3360a1 cbf7a677e205f9ad968df137584d4c90438505a629dd6b5f3c4f822564b41c8f 903ea1173296e05bc70d6ecaab59f1a6ae55ba8b428d5ea9a3a5e4217ab5dd49 aba326ca541d77ebc99dda5c1ecc21a07010c2945ce9d5d18a2bc753fcc4156c cc007e712cac3f9d64f16ed07dadddf934b1b9373247e0e8d350e5a2551ee0b7 14200d798b42df0eccfa9f36ad0e18f1aef0021e9d3ae7912e71bf4efa947f29 658b34f8f8f575805b075dd65b5493b737b1d8b6ba122a55a25f7053f23da8da 146f311fe86024f1a81034d6c9bda14c3d4728fdbc030901a1ad577bef3ca5a0 9eb53c235a0298a92643833c1705b6bbbbf99e254a74e93a63b1443934ee7aa2 ca1831e48ac62ded5bba90fb71d61fcc7704151495bd72e05c62f780cf9b519c 4d1d68714278a5333173e703409b4303912cf3b44133102fe9eee8842306fe08 4ec22758002566d15055dc51f1386907f7104dd011c8337f1b36fb29e881bdd6 0c90d65ca0d89c7835d5cc8f185c6abefcc68c0fde058a516ddc19aae133f6af 548f850b0f17d7b21e8c1a0d77785253b05737045b944364c157b942d094e5b2 d0607ab0ba606a0765283abdc2db799ab04c5c63edde5554396e28a87c5bcdd8 51d421c5fb161cb1c883e2e81930d9f848c5eb587aa2092431afe368fef3e0ae 5ece9e7ca0757f3342408b957d8a0848fdee5938251626670255ebb0a75ae101 5579c81801b58b99d1238f2253651b143d8e3ec4cc69f0ef5fd5483d216e1319 7bcaa05b83bb272070bb8987bd618c24e45311b57085b5b6a33f3e1ab804c4bb 89d7c2d2338e0b5fe50d17efd1a567460b484d1dccf9d528efa6757d5233c85e cef2cf9153cb0ba4f39713613ba40ff9cad9cbb7e518fda13e721c329e6e38b0 8a831f7a9a03d40371d569427e1c00b4c6b59a804135c90849fb0abeeb203bbc a71b0481ffb4425af168570f2b0b415ed1fc1c181204e21a27fa5862d8a63c6b 99fedf8d43745d034e111b8cc7d99e54d18b53c40a428d2628365b2b720a67e7 d2575e2b349a45027aea08f5b423ae42950e39fd365891533196026f6cd2d496 e081393f25372bf4f1aee37b8bb04921fb8f6aa0fedc1143ad9fce6de55243e1 ac4168afb6887345c8491a395b777fc2ce312372c6655295faed65742ac69bfe 5472335979b474db10d62e368bd14c7fa3681e2aa3310ab5175d47b9b23d4861 3e70485ea10a5d0763c84c5dd9e09a25adf926965c59c0d7d1345a60d0ce6024 ea6173b067f377d97a79537f532b8d2343567e92cb9dc977e772cdc6129447e6 da151c45da028df365edf5ffb819db00f38ceb10fa687d629f958d653c7f7753 4d94eb70a30b0c63513a4f09907f972fa82a2f941be6bd9ca914641ebe8553bc 3907706663fca37513a9a30facd133017aa772f69731be6d4deb4f40047ccc48 32d52817b46b90f7cd04c2aad2ba6f8d44c8082ba397c60b0d982c5c7aa9a8f7 8abe3186bbb742c573ac355421962942ab2c75facc6c898ee4a19a03b2526ddc 9bcbd8770aeeeeda57a9a2dda3b526e5abd65acbfe8b68ab4fb346da066c476d 010c68c46034de748d583b050e2715d98ba852735272e8d11d6e527bf1a40cf9 3407b8a4d8c5b985a20d33d3896cde2cb823320fa4f7a8f2385dc320d83d5ddb 53a7e5fb8541fa6abcbe75a39cd9b0352119fcc84e724a515f3442d4a558c55d 73becebdf4b4ddea87969a7d672b4778586a5711226e7ea72b6d615bd6eaca29 96cc838b65be8f2cc82682aae4948d766751bdcdb162b399f73b2d1b28a47492 2a0780dc42a9aa0ff3cda976a46510ea79ade528d57c66131da66f29046669ba c0e71216cc5e4dbd872b783181fc19b8c838881b8c7feeb7b679bfa700c5f00f f7b1094d3f05868b1d8f82468e3e63d6ee98a2556190e8ebdeeddbe739662e70 7cd1ae917e9fe9b2250d118b320a611c42907668a2a916430086e79fdc31624a 6c02f8d8d410727f698344007fde07f7bb260eff705a031fcca93387c0538136 d0f96ccb71432fa05978ec91f83d522bc6237dbc29a28ef02c9019f7674df4fd 0754988d535e9b8e840d17088c97eb0484b9086fde51eb499f9079c8694b9092 b45a7849a1b66581d71acd062609a406e78566c94863ae128f4dd89db838b09b f2c392a9b334780a36990057be799cd2d96d9544b3388563fac448e2a8e61ba5 ba1ac60d28e8fe4266f7e99ba5dcfdf2c3f03688836b6e39295b4f08356e5e39 3cb51ec951027e73f76394226b502c12549883591c82f40998d70a24433c584b 6349964aa8486223d4b70c8b8fe69145525bd91ef6628e1c6cdd2ce0ba04c266 290f25e23204297c88d4e709a0ca24e3cbc54b657393ed8569e1d9f5110cc2ef 0ff7d6198640d9e511867fc6f6eb733728d4087cbf803cd6fda69743415dfa0a b11f6e8f2fb83fd784b1281372023354d58d84ff26c24f6fd7701537406c00be 0b5a3a445240b4151eaf074d6be3c50bc54fbb69db6f1eae09b6c146e81f87d2 a70b418a5ea1f707725dd25fdb646fc10411acf4ee3495c160e56057d469be7f ba5d677d820274a39f795af23e660a8fa619f6213d0c99051f93006a472105a1 da947bc68bb177db418974a50388357ba6c0c7f7d1c269c6c7e7ac12013aa388 b8fface27affd9eabd46e85658c00b2d1a187938e70aaa0401ce2645e1faadc9 01950f516b23727d50e930d6a7f982701200cc9b4f91e5fdc24fae0621df0648 d1525f7d9ef2244122bc256f431b58a53f348af292547b970214c78677d96748 928e5176379bd48f34ef5acbf25898ddde6fc2de80783d71d01dda12f38a85e5 c4e9fb2607e4e77d6be6651e8f3252ee3b0cb4612adbf38432d8cc40a25b6423 e8c78da2f6c26bec3eb9d0c8e54d7169a780b4eb3a67f60a7be45b3b347950c5 5f198b0d848e36526f7159ede1b985ef4ac6981ec31d337efe6fe896236b48b5 d49ad6a6b2bb67adb3503179d1c79163385eee4ee6169fc060058b8ddb9fd401 80bef6ec30689b27bfee132087ec9308d11746263f7dd4313c400cf98a45724a ccc998e5dc245cedcc864285e40a6453fd2e443a687e0695e1f36887168dc5a2 c6b95bf4bf9d0514ef8f69abba93cf993c4731598e9bef7ac7d1df9b5cd868b0 571028b516c0d6f51d1b7809fd5066494fde172fb962f2a95d9091cc231bb462 f039561da33396d6cf7f3d5234c3b624981a193128904f296a2d49539c5a5354 c254db3225ffbe528992738a1c2a2d659a2b69b7823d5dca1f074768b2d0fa1d 26e8fb8d19c19f48c8a4a6fc8c7ae07d81a20e338b26ebf71eb6fe9cbedf82e6 20c5dc8182e869ee3b7824eb8f3aa8bbe41cbe85fef2416a116a781cc761c459 627f6fa349be0f4fcc820ebab0c373d346081727c41b3e41f6908401c55ca8d9 dd81b44f36eacc86416c791de3b6bab04d52f8a001cb805f71a09572a8dee99a 621a8ccbf534ee9b37d39f83a6f02768bc0bbdd56a9ffad301c905f43f95df1f c0662a10f78456cc0b271e278563c6b269dbf1cf6ebe5cdda87c07b735aa2e64 49f3a3193e87521d12becc9ef14c623e844f1f1a6f55fd6c2704390268f45d90 6623968079cf82cc14b56fc0eb73faf93b4cb5a4f6e1c56f12e2978aca03f6f4 c9f349184647ab482890e8f118a92abb469f62c2756b599706898a7ba2709a8e ab17adae0cb94f56924e7aa5e4dbb18b640fecbeb23f3728694a071af7efdeb1 d3b700aecd64ee52833e70c957c2d6d3f3325321786e4ec6fcd95a4645e016a8 a6e161b23862fcf9f036fa3627f2887089f79bb4e9df6587a23cf40079e07a02 44354eb4d4a96bd9dee8a8e74f2596f5cf117eb2137057758b012ef761ebb94b c66e82e556e827481441c89dbb03bc57e7fbeed3b684ded48c276056bec9fac7 1376ca9c2e462f8d22aac18d284d5ea20b8614a86b30fbe073baeb41074cc51e e098ba48f7f5f85336e47ff10a3edca5eeda1bf9717de3f4262a80e77ab93674 2779e3d23b90fb744d69d5812b0c06bd17ef83d509fd6d5a86cdfd534dceaab8 d66a3585928fcb67eb352804e9905ac7bca6d6011fe5f7e3c15df19a6f9d55c0 9892556bf022ceda7b1f04aaf0328ec0220e468010a235b760069df477789167 d089b93676963402c9ca2305200908ef13f6e5e5539cbd865b44cf6b32499b02 106 3413c4ac094c8ac9afef6b468ffbcdd9780b8bd5662235fc146222bb8d3f6b0877f987adcfe9fed34dd6f83754eb1918965f353f260424780f7b99b960b1e9054a514bdcbc51fcd96c30a3758eb36726b4f03556166fa0407f571e2bd9a5690f5d30d97f2659918ca6f0833a57ba69f97523faf2fb9970c0a86e3577b090710fa70ec8bf83577ab31306d08eef1472dbc63c6b6e7c7686c3c09895292ce60e0ba631c2ba62745ccc576a9b78d10ea99c1c98291dde7313848a7af94e7b56260424e0580e81e121c9d607d96efd7d7b71a9814ab13cce2d82c2575563d01bb502cd1ff804bb109d6e8656265e334c07b101d65e853307478ab706825b210af7058c2915b290bdcb275ea3b64a80e36cad7dff6c9ac32a4155175ffe8670f5a20b33f4ede23397ba01ff0a878fee93e2d5053499f6ab6ad6fcbecd096c193395070cc161d27454221b968df1d610c503c311e21d2fe42dc1870a82bbb72540da0a7abb497d9038807b250a33b2dbd72ba7fd29bf3e301dbe556dd9d3227cfff70893d2ea2c326b4e33375a70bda07a49a43e2c2a6fd07c9070791f40257ec9c6037f1d66593213614c925b31a095b0cee5cc845b59540e2027b5e093c832172202794ac7433fa1fb142f10d5979e0f45b52acb38f03b6109e1b6447fa86ead550c7c24ad96f4571a7f0ffa253604e78a43f85a1b401aa59b8482c5c14b114ef6075147d57d14feb9e2daab74934fb1da4ae05875d346531d6c52fafadfdce8e4014c24e5a07f3c3114d8910fe9317b24dd32a2b128511f3e2a8299511e1de581014a64e7e8bb4025839c7dd32cb19e267cd32ae2628ef20686e5f9eb78772c2700d0c39ee8d4d8957285f9bf4c28ac6cc7e3c387bdde84d022d8c6b485addebc0c1683732097fa0b2c8259a1ed98f932f4359f5795af3fd39250580a9f6f9a9f0f968767eaf031cb63bcc56335d515cd08b49ee8e1523ea143f7c245853703e80a723cdb1cac44cb4bdba56212042fd5ba77f2ed5a703bf9a9c6ae14961ac59f08b3076d7ce9370978cb40bb9f3f8f701a49c0ad9e8d27937572f003e3af3a5d0d3bf9201e2dc6172c9e4cc2eb6fe267716386f6d94d7802d2abae49c2a819670d412aa017abe97cb209625f197dae2b264afa234605e4fade8682cf9d78e67c0bf43b88b960f0c3150a81bcb1ce6b66f63577b6062cf9514ade291742317a700a1da07fc71fa9e142da0370a71e6284108b1ed117a33067cf87d99cfb2d4e5b028abc7868dd7de8653814312c1d3c73875f6eca711c5a3ff1e5504126806d6d0bf2d4064438838dfc65286b4c52377cd527c8b2a31d6088083c28f9f932b26002956dafa3bdb0b6a82f5a39f9030d4f161635e8ddc3aa1ca94c7c53ae5d5af806282ab02bb5068d2e80a819961383deb114bff14c7ad98b77beb839975b0fd20b34b75afe65ce8869ba88569b752060a6f1e38064d118cdc8ce8be501e7271d03459bf0f1efeddc534bb72cbfa0a41b594abdbb66ca76539e0147c23f6285f20050ff6be7e7944fc1b35bb5fffd3c3a2ccb773cdd7de1670f66d8be3bb3567c09358e7327cb7a69dd35657195d6c915a3eb3b7656dd4f8e2f5e2d8cfa30eddd059582885a9efb7209dfc5642d730f143b5f6feca4e3f5cc77553f6e5b46687a00477277e74d04ae0f478ee5027d63f85ed04491b142b24751656d64d3420748023f4ee9c3184fe767e644d51d013178c7265c8d2bbce03eddb1d02b451d2b130cc5e4350647679e1673fa093fd1a292b4bf9b18609e4ecca1c497d8ba9117d309e9b5fef34b4b514b107f32a146b1049d585cb642fa5bcda07eeee748573a550e5bc4e7bcad958ab167edcc341976c893be5fa4c570a25bb5bde4d2d1cde1cb0071bf313349eb39a85d5eaab995086b06fda7f4f5db6115a231a9865f4e3eee01e08a2709806ca016fc9efb5c434cd8624b024c8f55a1c29f2395188044018b02e34e62ec8c2aded6219cb289e3012ef396cc7d8096c92f937375381fed0c730ac5e13ed0794d54347bf27cc803d26b0b436b35f929d9acf6e4df72d2fd9f78072bc79b1686bbbbc2111fca40ba9c24895069a4dacf2c467e68fc1c087c45bc0cf3fc82ffa8fa4230f9fe5f8693f7c51c5f1bd22dbe45bfa56c2961f41ed7ca07f16482cc2487d436d0c1697bd18927f63e56f3f458a2e357dc4ad8119312490fb7433a82bd282c16bfdf9b518d7844e3c32af6dcda1226c741e84582a7c5d402a0562e188ea1fe8804e97e9262f069ea220dab019ee6dbbde992e6dccfdaa60789496ec7ca525caccf96289aeaeccb33944429a3443a637aa3b39c34c1ff6d0c9b51c45a5d6140111b70fd4d1c7c40cfe1ffab26147a0efc989415bbdf80a307a5d2b253e55eb446a10f287473e2cae26b21632481809b1aad2adc773d89c70639e152ac709897e29913e0d60b81c3e50cb19d936dcdee93ce1bffb67a8a060a3a1f9d1e454aa3d1805e2d5968d3649639926c895c3dce199ebb89fe1bd4770c4085268a84b4eedffa7162b84b095a8a4f08774ffe0bd5edb7dc2df0c3c44608ff53e4d23f1033967ffda0d018b82049caa9de74f75866fd86aa63abde4e7b01a340c4c2fdc3cee177edf854de928542278c50c372b42fd32c5a6bf891bc0401c12b3433584290676c4d25b80d6bd27c4a7d161d6a3ef2e0741d63979acdba03b038d248a929132e21f0fca03439e1b512b9c4c97eaa53a29626170d5132e2022c9efc0bc7d6959755cc6411d82528aa38e00e460be8be9c6ff0bb57d33d3a01f7999b2f63d5421e8d49e372cd540481733fe63b7ecb8a3613746fb6a6dd5005847f37155633ed2847aa50bebbb091df9e902350ebb08b788d8392c846ee74049f78e675ee31bb9c8ea6ea60dbecd36ca5ce7458a3bca8b6ae5e48444343730eae630650c0ea897dc441415801f07574c0b4155ad141da47ab0e7602d7325e05f3f162a545d3739166c40d8acd436e8424fedcf0e130587dbcb51b3b806dfa0563094444c2ea3bd0088ac4ea75b938f09280ad02fca5016b1c75ce19acf83604be8900c5467c3e9a9d9db6c6ebd0e7110809c84c39ec186d813f232353de110dd64d2fb5a342abc7ca87572eec1d1039af55f2d86ef0999b706b1c1903ee8b0fcd4a5f571efad26a4c4e9f3a91f18b45325ea5cc200ce7a9c78a83d168954e027e733b855f789f39e8659280d907ee6cc118b3bffec532c841b1da793163640d373e54ceefe55b09833b975b5a8ea0e1747c6f0c79544b15871891554b22bb0a6057bd214421b901624c04f12e2e9ede4707c799c1bb787fd68a520aababc30dd5dfe441ea5d6754003d0fdb636dc302eca3b466978dbc4c4cd1b7a23d8f9f075f99679b5a5f3021b61b46f3745ce66db2a916b528c24229c251d014ef3ef60c7f537696f5c6e0405115ee3eae433fc9242ff5750a9c208767f9f4d58616ab070fbdf3c53072db3aed8274e9f5319e6bcbe633058c69b4b836e642938251ef0ab0a3514877496960e99a75acf845c2fedcb29edbba60f7a94e7441ffad020202aeee6507a7169d1d5bc187acdb8ceeafcc2142cbe1575d6858262e610e20780e662d6bcf9fcba7afff58f86f373f32801e8be69356e8bd7d175384481db43a067e0dfa9a3059f478ed2d5c0747d7ffa130960a667fefd1af3a9a07aa11481903948d850619b10839151f5a178d704ee84993e1ef350b70000481e5e9cd6d5603f2d55e2b3d1c0de5a879ecce15b0a98c9497a864fd4dc4520cdec736c6d83a09cfdaea2aae116bac1d815f8d5fad54d0d7cab0d2ec9d7149b43bf0385e6ba808c887d4a44f0dcb7d9e39458165e21011cb1c9f74f449d80a9ab34a268e285e021dcc45921c9404351b73a44e97aea1e3d2852fcc787aa959614d2119bcb8d908da03d90c615516a83ded9bea7b894854723ec9a38c4f6f2709c78596e982a305e3e262ccdffc374889c359862049b15731e00d963761c43eca5a68e7f931100a6b174534f5f2bc8ab8734a00c8c25f851d720a6b0048983da99425f43b84950c7e8ec25bccc59c2cbf56293b29a37f24e51a79d24120d0437893384dd7e1310295fab57e1fbc60e1a08adddefeaa7f45ad341008960ed711db7494e00c444a09642b9f1fd1353f84f782a6e8953e036aa7ec9a0c867885b2ceaed90516f55e07ef21dc777d2e9c9558dee7ab71084de7340af774848eece1f3b7c5f58897940ea7b479dc695b44e51e1c39d12f2928b9aff1e574a15260cfe8350315061d8804640b1c9e57986287b13c37ea88fa02369d1b870466207b44e5753aabb977ab0a4205628bc86492bdbadfe2da4eb94d38da383c2fca9e58780cf66ae67fe0540d168b000f1e91cd2015f9f78c8532baa59e62da2b75628e292bb18003e4f367018f9ca4c7fa3088a3145a9c7e433186267468ee5303d7f74684206a208833a50d809e9f0d9ed388351eb3ef0d04235ef195b61207a6bf219c8b90178dd28ad40229cc64aa06c35debd86e4f8ad75fec204421cdddea1d09245f2733bb91a0c902bddbf2bf2852213a17c06f22a9e5d28c16f1f240d3b74f9bacb1578e74d7f00df2f7eb4b0111e46bf06d5140df84bf0b162560ccce4caf24f7bfa7f30df46904867128ed1956365720c94fdb396e8d065f73e27bb95e9efe814edb3e38efef0dbc71aa99bce22aabfd9fce37eaf98f49adac76e59eebb8007f3482f49eec2703e4c99a37f2c2a2af3bead46c8fee0acbe809a68ed8986af2c18be60ef1bd5107c8fc9a28a52054832ca9e3ffb666fc51d4918dcc3ae2ae296915cd312ad322028c4d1b566bc22b47eab3f72a8c3345fca5e002e67019b312c6b1815a0876e7059616464aab7a7b4e8cf8611b18fa4337e98a3a4abd68caa4884b23540d0d9905d6e2dc05485ac27eb6b0dc2564ab8f1b2daa7b458c49ccdf7e6dba0d911be20da035144fb13d7546bc5876120a89af15f29fd07ef7aa3a621564547945fcd600b1eeb81010955afa2c28a95f262afecc7a6c462c8f46772cb1f4bccf98a7cc03dc10e41f0eb6ba3d08aab2c3e124ca859e21ecab7326edc131d696af77086f070d561f43d9e93974df09b14f8ae09b09141486ab7d665b7cb98be3b7592a180ebcc99edeb9a7e28e8db7aceba868fffa33ac8cdcb63d09aabd78432afda26409d2fec20b8fc44b2eede0f1688a7e6d82f2b2608c83e551a3619f1af2cf16940495dffd471eae478976995c4578ca02e0034a55e9240fcb2499cc6c13c8191e0a5e2d7f70bc4efba9703ae1b74bafae9d2520710e59f68d19e175fd712fe0db029e5bb52547651112f116f9af54a177d688c3ffb8d6fd841226353ecdae51b902575ab58cf75bb598628cafb147d192bab37a1fcff54d530f05ee9fb35a66090476e1b33761aaea9662b6cce09ad73effadbec8fcb712536f456333740bf1d905bf08b21325bf4123d18bbeae96d9acd4e86bbbb0d9466754c8d2c6a4af73f2084b8f8c34ba2382bbc7c8c2d042a85d6309842c6e24f8b688641620b0739b3b012612ab1e152d95c1ac94c7cbae447d1a4d512910283a7bcf467719fe0e83ed0edcf4339027d02d5d8bf7ceadd0567ca29d9198a7d5473f591b5b75867d99950552ea04711188e46fefcf18806ffd47e52ef15e0510abb269d0fbaa3a62a8d0021e8342396fe3998876c85a1c8c48a4885f7be38609980bb5db5481f6c47b9106836b95044d7270d788add03203b4737a27017532de34a75b783ea431babf8c09148ef641a25721fa5ac8419cb6767867326d1deb850228fb581cf708d9b5a30ddd6304700c4b5c19a215e964659dd51dba3d2d80a47521f3a0c7d17573548e0d7c5ae696de512db9222ce2713443a6b45209181efed64ae7aaad6fcd76f8e907b1f432c7e63e08d77dd0a17d79d33c7785003fdce41ea8aa2f519ba379c7c90829c600f706c9560a4a1ef984637c8166bbf3cf6fd6ea9dbc9852f2e714a7a603c97462cc5e3dcd17106cbacd2211d19279f25cf93dc434994da2f6024e640e0d215e241d3a767e21cc60841adc8d35bc498d75732f66c53bbedd805602c8a20d55aa21ff60c44aea59902a359525c76aa87bf370c7a72a3c44d70b13239eba085802771a96a9f2bab36c378183bddb6f594e3e64d6d895406e60bbc6a9ef91005a36d5b0174fc482a942bf08b0a2503c238b3c8d949525673e932815a2fb84026faa3ebb256fbf2bbbb316ebc2a34d5f9d60837b01d2b14930d1f680bd65e707ee2740309bbee058b3e37f97afda465325c6748bfa7c5fd70fc2974bf597aa0b75872153f50818b1fe9a51f8574423df10f04b4240f78b6a6cacba350e786a08ebf219565729de3690e6b558c28db7f63fd71eb32287028c4856a7854426e500ba6d68e0533171b226c20d178382714335f4a7680ce5073bfe20c5a9ebdd3d087f11ab74ae5aad90c4f40463c94baeb07e68e119f1f3bc87198f517bfdb3ce0f0b09fdc435f87e12ea0cb724df7337c684bc25d181e3fe8f46753ff69b39ca06d66570d3715615893bd802dce16400bdf3657d840b5af3124695d5b4482b7e0d04a0bbc426e0b877fcf16214702049bc6829a048dd294b1a549b60bec97eff098d8711a161dd3f361acfe466ead7d158a0adadeb435f37bc5ee308957931c10584b99b38d3e7a0cd1fcf0319d1f9b8d202e9f6b48ad0ea8ebc2b383aeb77010e603c83cb8daf66cfbba3f92bc4ca3a22e760e0b351ff87e6818887a4dcab9308278ab0e6b03c2c8644466f8756af3db27b51e8d4c8eeb87720602216ce9e4f0dbcf83cbf3800b40b1d5fa799f092c5c6e733b34aed2ef44192095f78a24c6609beea13b5c5e9ac399da69fc2f9efd80d81cd683ffb40c7376d476c5acbf6e206ab73f12c27687f464c867ebd11e26f64ed7f15ff5a4e9e95ae42b495eb7e970f6914e2a122c0bc5b01581094994029810ac3e1be5de80931604a381ea637a00e9f9ccbc1bfd313328323e8e689b0610f48e4ded3a2a229e5591f6ceba6020d06159ec04f6255568540d20e1c4a6e07f6146adf1d5f0c8110810ffcefa152ce00d7d8dd42cb9e4283b7ef014d9c0291dfac686c406bf75bf2cb2771fc65c1fb070f034317b287ea557a923c90f2d9976540f3883b43cc274389b55cf89e54ee072c35559b30ebe318f996e71317de3f911ae857da27f84e013aae439087b6db07e412f062b34a95cef4a0b2749721147b878e66c8d0d37e5a7b136b4a3effae06c5550a3a06fdf4f2a341a0aea974e394bb8e0489d8f29b9f0106f31861e2c20ce5aa8b55c36defe6678b6621e9b90b4f095760c271f2c3da6150e98527272f024bbc856b6671ddc0e661fe708e516397053652ef2cfde8c96549eca5fa24a400c592347e08f9b375034fc4f0f3cc9e533ca769024c222ef0f4cb11a256dd180e69e2f49cd09525f529554aa59cc5382aaa68933d886b4b9f9f4a3423f931b802aed7fa873d4facdb9e975ec9b70ccc13bd9608513d4862e6527d1e0b8163e20840bb08e88ceee25c5e65a494b06e14a9b8a3a921fb87c2ec354ef4763ec1c7085bea1b689798e8dd2809f20e29be3cc8af181ecc68c1353f01c5bebd7289060b95aeffb47aa892614c98b7e46a7ca0d15c352daba0c2440cef76c6a52278f8091f1c799ff172f4fe35ce2ea03c396b6cc576efe029c1dcbf66d88fefb676840b81ffa401c2f7e225ec135a75542debc86bc92385c2d028b8e6ebe11a3ff0930043ba76e56a7c83241b1ec7918f4c7e0026d3ecb1b91472f5786945d59889d50f39104b68e1f812b43e28981f8e231a3a5e900dba520aa04491d251f7838705048f5cc9f90aa88daf908aa1348e7b1faf16b7c3af1d7a99dc628d1256ce98de052357f8a9694a0fb9a520a6e0ccc8dcf696397a7a68ebd2faa873da7d1a5d570accb370f559f8dd4505fb6d03fe4d6b795cf9d31080a9564bfa098af7813f320908f4ec36fd423712f645df0735f78744b786e379d3cbcbe2761de75976f39f04a36dcd7a40dcdfc7a8b3f46ca8f1a0134e64427bd6b5d46381da2ad0afe84b09ce392c13cf4a7574dcb9c068cb0ab43eb359091279e951e94abcc2db31be910d0557b55ac8d0512501bde8057b81123ca26d2ebe398b605dc3ec4ef5df63b7026f9867d352e140773fe8cc491c48b2ed08afc5fbd52917c742e854e8ca94100083976f42de00fdbac1457b8c1f85c77274776a1587755383fc8bce2a4de56a0d51513a59b7bfdf5dc9dbc3b389f3671687f1d4e27dd6fd951118447f88bee901911dd4085a3a151598738f6df3f244d871d4943f309aa033920caede024fe80962287fad76013da5afdee2d1a484ac2711a5f3d2264e16e76c79ddb82f68a50e18f9f118c29211395cbec8a56dfad2ab6c236f8b47379ebd3d3fbc74a4035403638d9fcce69d5e628a9f376aa7fdd3ff167e127df1b8ae6d79f46bc18d8e6e0d745e3d0cab5c3c68c4b7e05a504a9aeb8d76723afb644510856412c589336708844f66747904f7e331a380dbd11aecbd25d9f772f01f8d20219d07342b2de80fa85d6ad496f6fd0919d54a01e98a487d4af76827babcf0cf049fab42a82cfb082e9202f8a606532e22d0ef5dabbe734ee930cdaa92b7a6cee218b9ee7347420cb66cf3b95810eba9392c1b34c543afda1db2d29e07493b8eb48bb3f3713a4604d19a7fa2fd127419327888b5f045c0e17b18c0c48c0b33fbdf3c9dc15cec240ad47bda4032e139c2d64fe54c8df648e6c703302e8cd9a30ff53ae771079c6d07bd44caa1f14e7f548d18a914e7bccdac055ba6e9ea84786641e743b7e2e1d50f1941ef714ce9985f796904926467a1838939147b9202624e5be5082fac7459078b95dfcf28f90bd96410ca29f2512ffe30f3fe5a87f957f088948988609f870e36d199338cd18fef7eba8d5736417cede41b67efbe8e0cf0cf3ba2af5b7d1603ceaf9d884cf0aa666ebbd63eae33775e78a0f43b14969a9682af483247e3fe0c7f9f3804cdc32fb38190883fd3e429130afe57c429b80d6f0f3bda8ec18f680b997529a6e3b9a5f7a9aa6b11d1834656300061a600f9b5bab5f186dbcf4e39043bfcd3e9104ed1f4dca438063c28c5951b96e78aa496830bad6dd42fcfba6f06e568071c1ee14d3eea8fdeb15947d7f1ee1b0e86dd52c43b00dad8682312a40b88d2e052aa921e3608111e235e166fea05588fbc0f29d2d2e16e2cc5e1b6030d3cb01044eb67570bb8bc04c51a3bda5cf8fe1c4ffb48ba3b1ad2c15bbc863608c83befb1e7be2fc268e92b565c5f20fc4f0d34f48685f9c175132074c627da0b4afca6d90ec1126837c6a55004eec0cc65b55ae316789ff06bad7bb03173ae0690db563b457f113fef4584dac3c11d887623d0df8e117b66f4d407935795af00b52680bfafb9c789543d51dd34f5751673f394cff752b4c5d293125688b4cd0efe5c2537222f2ef3060f1d4962e55587d543b6b78fe77b4b921f89302cf72404ab1ea9d18b3fac81a3d8936cb8ac3a413bdfa44eefec634fdd8c809cefb5c40b032ecf6745f4d491634443957f14e9dfb62ae258c3f3faa18517f9fab39f1a08e3d89a50c5f56e4776d3d59d21ff8bfe5418cce676ba363557788eecf9a6d20d4f3d44b15986143a8e52095e9c78268ec72f9277cfee4df4176833a79f9436027e49100c0c1111c60763d87bbc95306a924dcdef3afd46e094048b818753c8005438cb7e29fb9a8e30f53418417eab185db2b510125889d85de249b36980fe0c906e879a37171b5de871d85fe0a7b4f798dab27879b65ce8b5d960585f3f6509cb42d4bced4d03b90221cea8c672cf438a1064248a450c38d6d55edd94c86607d0863835088063579baa629fd6718d2f6209b872ad85836152bb4439d190ac0ffff90f6f52bed77ea0374002f2882402261c4fcf9d379eabbb904b9e5aea1706675560e844ff8b691ec546d021cc49da04a44df231b3b0ba0d0c1b37fd50bb07661415b05eaaae48a8bf3c9d9ff3b52bd3663e242c86bdfba6df41b0b90ab30c345ce4f19e3819707855dc371bec54e903b16a77a404bb3d547c109a20ae0607b990464af8cb4ddf8179f5ed475ced799c9dda56fa053f761ae213d69599b0018704c8e13cf7ecea102986d7ef3f5b9ce0e5fad5d4514f4e6adbcc2c1a8f6e005eef1d6cc9a25c142c612aad90634a8ebc9b96eef4ddb9ed80b3d2fb19da6d02d6abd831ed6e7d3dd80c740caa3707d4a899f97f72b411675b658ccdcde3d70ae5f4746de7fe17167505f30cd77b18c96d16a2b747bb8d417980392c4e43240ffc1884f82bd527355a8329744a71d416bdd663fe4acb3de23cd9aef63b3add0e97028d4b6b343f61d3e7e112669af4ae2c12ceff995f4fbc0a8ef2d8dd967b0b6e581d169ca97729a9d0330a3842e4bd7895ec882c4b5e6438c6d6556758380f29d7c70d4257bf352c7eab60990d4ec04edc64bc403014bd329e9d061865d40baf3d87cfa306d2f164c1da124c018fefd04741c0bdb60682f60dade2385c770965777c40a1e92cc0e8bb5abee655abf708c0e6ffbf7e07cb68265e059cc488053ea02c5c2d505bef9beaaf06c2681183ca7a4f0d99adda5365e5b7132995f7013e9e6925c547de1a31147307083c58f8c3657c6b07d238e61a34e472d3caab022b5d07ce468dc433b1fe38c447ae6890cb3c4efdf943a390671be44b4712b00d5fa927f0987a441b9c739ed2137f0503e6b8ffe47e328fdde893ca2985457705a86923187028b4e3d39aaa38e2eea3601d2eb55e8f534eab5dfc707c03fe6a08cc0638807ff6660dadccb15c3a34eea613e03ca745c68d63c02dbfd253da5e03166159c0eefbc36e3890c65debd77b8617a2d718be4a9a0be97fa1860d6997010f525ca0a095363beefde5cc2b8e6e33b66b3a0f0b272d62f8da8e9a891dc40cd3718994f63739c02fd47d39c457987b91ea3a46213d281fcce809cd13f24808d8277859ad4382da3bd5067fa847e19a0c8194e117c2b0d9fe1713332a2f7505d7e993ab04668936c3a14920257b3c99e8a168194f534802311ed95ad185890e07f6fb450fac842f35e10922094e5fac0c793b91ddb77dd9ff3b73d3b7af2609ba4a9054415d64fb37ce73bbdc3fcaa9748db7e6d4ede907d6f49ea4a8fcd50190fac0f858a2bbc9ed4b80f1a4e53dca6eafad61e2d1daed311e627c9d9e6808c52051e248150cffab2c0928277b545f905553f81d21ac5df9411097638a940bdaa3da1a6c4ada362e676b8d0d278e2624cfe3a61afe9f45112fb470e8fe7f0cab63492a0b36867b59ff5e05fa7596ca0b9d568501f653576a4603b871ea7403940552ff3a1cf24ede2c4fb63d109f7e84725312e0ca59cd812db1efef6dec07a5bcd21c3f74542350102586dcee394d38aeb657ecc1f58f2bddeb067f796e05bb4320e52d67d10a854822c8ec4ca7ac4233f17215b61e01e997169547938705465017535f54c1158932f685d4b1ed5c61ce4580705c0b6405dc1c3c87c2ac056201394258574fdaf796452c081ffa8fb3f85b68a21b0aaddb53aeadc764c70e63c5357e729173f8e1bf9dfecb7cc1e6efa85290a2d2c9f1a2d2e0d02018180f14fed9384b3d83197a3e661fc5f8d2932f75c2cd7e1aed049ef0f96044393c0eea32250ee9aaabd5b21523411060dcd4d361f00c0aa895f0d14eaaa8d494fc0548a60bd8e75b18f10be28b66f44690c934859520a5b850cee7ce45f72c281303cf5b47b5babeec770652792a3d011dc09b233cc0c3ee7e4abdc2d200d68add078c57516ece2fa372b7e4326a176771a6899e9d435b0df7efcb041a939fcbd507e632aae3816746fe56b27ad383a7566235884c7179fa3a1ba4e6c932bd5e080f914d5f7cc587a283971b3e99acb9e001da9337e0a386aee95ccd32d86caf800d393ce7881002d2cb1040f3b41c73770a0ac0d65eaf5a9df599373a08806ea905e0c840977bc76e1e2d5e13cef5e9e82448f4b34f60f4fb57ce8b9dcf7ee3640f07c375f1f1581804ee80dc6ae8fc20355b2090b464a36af77cab52e98bb07d0dff57c9383e848853a814c60e6bbe5f366b7741de1d79f4010d1b51ac61ec1507e68a2f0ce7e8ebb5ab30c2d0f41d7a3ba426d4249b0e19d14cec08951638d8019a8f75e2c2a1fdc8c912815fc043d9b31959d739d71aba09af9254c00ddc120720cebba9edbbfda84c14e493cc2c887f8f7686406041451622e6c6e3f21d1906cc46080bce8cc05d16602ef6f585ec8504f17be5a33be345eb47fef024a1d50e4c4ad67119cc2412dc504de07c153178a4c85ff155be2d47f2d553e6ffc0250507695406b3feeb513bb45077f229fbc36e6ab3f9a644b0a500ef52ad29b86705af22431ed16313f9e97f5eec4c73166812ef8c0a33e6332432569aa5cf73e8089880c1171c18d2ca6e29c63cf730d87c47725577f3a4e9a99c6a17c58d13e60b2dfe1d521b35ea1f6d678510b35906e1dd71ab47087aff4a98fa8b3266a2f20dcfc2f564cec124d2f1f3cdf609b180a0c5b99389136c73f27c056b53ec762d0245e26ca3c2a988688a8bc9e1f87488dd0ee25839c8809456f52ba54269b8220f86acb816b1771c4eed565c99a3101f6d9bd61db6a4e24c19a9b6958d7164b2026e3172a4bb4f64136c186aeeda3e42a75fe1b7cb2bb2efec5c3c84670bc56300d44ba424f5521985da79fcb35c90d9555684607af47500b14ac288bbcfda4a0ed772ada082e1ebcf9d921fd8aa704755b45d7acbe1368cbf0d16f44a2f83f10b44099eeab7f5cfec2e548136fe0a95fdedabf2f04866f03c47e0d0a7b0118c081956819be483eb72351956a4711251033ea7e873862effe5c9499b63c6990003a37398f20dd6dcbaa2267e4bd7b8940235f7f11d4b4d69065699087736a64f0e662e06c141f7a833876a91e67bccc27ded50f0992d3cc220e77a214ecd787001 -generate_ring_signature e8020add6aebab8ca5f8b49f2072f631745887fa205d8db1a3aa1b5a4e232d81 dd963b8d2145bd369feed2501aeac1de2fa16ecb1b7cb93564535a6dbccbb3c0 2 9f1c652d42539ae6c7a43a5118d5890a4e1b386b3a400ebd8f2dd5687626dd55 90a86ceb7951f7b5668205c22e8d2c9953d0e44773f7d0159618a8a0238a53b6 5e6dd5608134fa4c64007aecaa340a0f379d1a5c6d4adbf017a239eba303c808 0 54f64f10696886893b4f082a6eb9d359fad11a3e37ea1ddb98a107e8981d3d0e12172753fe43fc0ed4619c4377ac771769da9b4fbfc953e6edbff7cf5f718e0ca3de0c75ab5ffb3eb1cdaa427da5130428a7c070eb5e76b8004e36e1b00ca208419b1e6dcd67d33f5669bbe6806f5268c5f4bca38c2cd3f880b55158d1e0040a -generate_ring_signature 3e592f32788ec54e3f10560b1380761329df534096ec12251382a96f8cab7eba 0b72892038d4248c430d1ac93f4985a8bb9b43fdf457a83618b7045980b8e6a2 1 526c98024c9a4fa8c6ccad5101f4afc972fdeb228620d774669c13ec4d029cb7 0741f944319e0c4678f4a414a12c6956cf9d4b3ad04a40cd6030e47ab7e9700e 0 2aa7cf859727b38bc828220f8e498f6c9a2474da3462cf802afcd9e69b8db204574fd64224cc7f6b8baffc7e26fe091d2bdbd14bf7ef4bfa32302db080c21a0f -generate_ring_signature 701268c079222197470218f73587bdeb19c3620f25d1511042edabd0efcae448 c2b7340137942802b68be0a9cd0deb826a43f7645807c8f4179c64b11021c36a 1 515cc1cbddabcae11062f52ba676265114a44372719b9b5dcff7865cfbbf1568 20966d94502e95dffb45bf07173991a5c0ebca48c0941c8c6705f7370bb2340b 0 09f1d8a4a5fbdf99caf3b6a5a8c876ca82ddf45e0975171e72a60625eadda80c5bf666ef8d1764db408de5a6f5decb2a985cc87a0db4958243a36eff86021701 -generate_ring_signature 847a7cdd3eda4d087626d16b975bd8dea19bd914d35051759ccd76080649a606 a05d0004f793a217255613e5b533efd58bf1d00e9ffe7d1062aa6eaff00bf614 29 60fa9da64e6873d5585d7c6b445aa994181e460fcf9c4edf9c2562432034c418 319cab68723c9c65b13df37ec1080812e182838e83f12a49d72aa8b82f8a906d e0d78f852a658ba8e2812978927a42caaad783594e463b73d25bad7ebf602a5d e5d6e07c783c40cf86479b179ae2c3064df13826f6ef66d1b5bb458788f628ad 97d175ad6b1daeb14df659240d2e567b269604bcbe62615efc1acbb3ebdad4de 5915b9e450d09664efa12f6477a526084e9a1c16c814f366e4175cf03f0bdeaf b08b9168207c344ec1f1addf104b06f4d396fe0a703c6fa22676549e221a9819 e28dd982bb083b405c5888a05b7ff932026a2d163febf55f7f8425df7a2381e4 b6cc0bc087d836d4e29e5ead0bfe51998943e147c259a851e138e931d6ee2cea 6bc6641ed933fd309190317c1b43bf34a7fa205b2cfb041c9f8d51da1775c09b 2438d50597d45379e848462033a037b7281eb6b3acd259a5190d9279e79a3d15 eec728d14ce0721d82e552a13d446866f3e43c5fd45d8459f6517aca1f703e38 6fbf0ca4da28ff0d2457d63f48864662748274eb304d6c22e3bfc83296ae0deb 51aed9ec8aaf554276b3397a30f18f76eef129585a5ac056ad9db42d6b062ab0 6d3b31ca3c9fcfc03be971f1107ec82cc5f5be67a0cf280c899ab566ee3c3cf9 acbf10cf496ccbba0ce691cfcfb240d1392c583288e0dca2b562710f0acabf5c b227447474e2fdbd382e6dc62061f4313c040bd951dd46d380b1fc4b8dfdb341 93ae8143cbcd9604e8862ee5d5942e90114e5133fd55052cd2d9c266a858dc85 2d90f8a4ed2e98285e9ab6938ef20da123f365b84f3b269b3ed03dc885e2c86d f7a9bc8f7901886e7f5b6a62c0f09022281d213e043df961c9bc61554ef4c8b7 58cdbcbb72356615330903011bf7267c8f2d7d61abfa88789d43567e51c7cfeb 742e0b7bfc9382859b7531e43e11e99c1ec1089ee901d502f116742194f78534 bed2b49b3170073561c62fbbcc1397c1e82ec0a252299234709a02b5badbe17f 6c7802b9d92c53b2b8ec8f761a260e680f92b4577485b5f516fc594984381047 3f32e6f5e41eb10431e1113916f94976f4fcc9db76edfdcde37cbd20a22756e3 9031e2a54db5f2d4358cc9486e5b25626c40afc5c43b20e260b0bc301d85bc41 02dca47cae77e6ec54842ae1a80f341adda1e0f5f78d016a06d1bc5595b7574b 65399dff20e45ceeb08f30f42a1e4f45c7c086e2a5f5c30f0645acea610cf4d8 894d72f759785abdc31f519d816040dedbb4df5af332187e9265eb1d4051da06 6f6da025d6473c5c0f30402add3b401e177f144bae5f907af5febe3ce32aac0d 7 ab26be747998735e8ef3dc2b914f6058f4ae4158a3d3527a81a4a5ad8a2364011b9d45fe2d7d6bae7fe6241c7bee219d393050ef73294c3b6b4eaba248ff70051590a1319597f7c5518200dd97ecfe3960febd98e8505f23355e6df9d469960d6d898c2c31d41dca0eefdf02ed11b370fd003928bb19e937305d00453984ba0d8caedef7ca3b6577d10441aaf4d0740edcce44f9bb7b5f0769b5b51ff1217f0b2ff5bb09cbbe0d1690e8cf70979155cd84d77c2274dc7f8cf0c296af3349b302cf3199814f79944523988a42235281a66a9850224cdb049900b9b3a9dd62680abd3fcb20a5f6f89d2a9b5e4157e086a1b4f0d5b7e4ee599d3075b8916d5ddc0f79ef1fc8ff67a3b257861de6d9a306beb1af563107f1540ee2b069f960061802cb411ebca70da445d421086a98d797e8a446d462c216f79cfb3a979e6cb0f403b0ea86679812e08d5f64db0104eaefc5f36d1199543c52d75c1e59b50c9e1b0ea6c8c670fc2d7e427351bffbe4376f9f88901203bd8b386a3e0e0140aa85810fc00aa5cf9a14e3610e4b3a9a81d4b7baab69ffb47f94387da5f2efea469e4f05ff972efb43303b56243fac2564d3782b679782af3f9d23b2a30fa17b349f0b0db6c2a039ad43993f774b59d290b3a124aaf198f7a18e635d0cbbc0af1a1b3801a8f47484ffcfeefcec7302de005fc96db0086b3e8ed4e21a5c608fa33a8a2f07108cf50c59a33d552fc5d11f30a8dea5e7be66e50ce8ed4a97db5aca6a9d630a6eacd36bf97538b473a87cd286a48560ba3df99711eeffb71ee53f3541012e0b7951d7aab2aa0832907c0583f826d44a02447757b4e0213882d77b1abd8c400e2095c214b39b384f75a6a73d7473ae91e74b38852c9a5210bf0eedc90ed32c08511453c2c67161f1bded8b65fe14ccae2b4ae2c25b6ba64c9f455ff301c98e0b24b9881dfb952ab2b85602776169a015992377f160181bfe46cd3b48f65be903604cdd7f03061ccf781ed98eba685e86d7703e81cf1216d2bddcc27a9e0265090be0297560a297d03adcfecf7c296261983066c789aa5c14e9342d9ce24b73073b3596d1aec5623a7814b63a6766941f66ac706001de2c91b10ce87962e6c10a9162f2818fd3a147da03b70d0b33e5889f73643f8ed5d8352f6d410583d7880b1cc4a805a1f367668c8914d368456e292a93a380aa80651976930d3c76547c0621a4f698b4dead900445c98226d579dc1552f9577d5acb20c89dc763ce97a209257fe1011bca9c185bb04a0a9da135aec7ff9a23f586fc3092cd6581bdbe220c776ed4d2a8a1068856df596eaf870435828edf48fd1df34b6a607583ccf594033009cff299795d2389e65cdaf0ec05dcbd503ba9997c1059e2147894398f430301fe5e754496d82c1f1141a7a98a047e4a1c085ba7fb069055d662e002c7650f3b0cc7d756d78cecc0243d276b222b418d46033c981a381a4d35bf333accfa0b329bfeee45bda0f15671da2e64a15192e8a9ea31b848266d699c1ea40e8d19065ca7b23b7f9d35080b1dde223d95a5f30323044cce11a96cd9cd8528cf8c1c0e722fc4cd7f7ee88bf1c4cadfe3f42fbde9a5b2ca49d9a2caaaea548fcd1fbf02ab66589356e86f53f6a410f0cbfe041a56a3bdcc7f0f8ae2116eeef37290440a186b10d28c1acad1003b379a655560ab01f41acd2b9f12b7c68f10c0279f5204e33b3b0462d9a7d096fcf17f90eec48762e2b8f4bcb9022f1e47c7fa6b5373017406ff82bcb98e2e0abdc188df21a8f5c064b7d7669dc7ec466731b8211a1c077ae29c1600aad3c3801051d93ba9bff5f37dc1ce8d691d733d6b4d3fca1bfa0bfa3f3c751d4dc1c470666f55e8a25d5366efe2d8c6232795f85e8c4e0bd4e4099422b64d813d46f2927065ed5fe05f916f47be19acc373feddb0123c8a05220239e517f8f070ec312685674bc90311dd5afa718446ae16e053a1afeb2449bf0fa4a94dca3415a654fdda7671dc774ef638000588af01561942b05d3ccfd61704dc5055c27d9bad21db376821e598911c73412479ba21e897ce672f74dd1e7805e07993658bbc699d575071c54bdbe291e0213adf35bc04876510ab0ce873020d198045e346929648738adfccd1135838a5a1621f9f355c7b53ea7e815478c30e675760e85275877d1b18e39468eb039d1902e12f22ef2b6c34f6616ad31f00009aa0f2a6593fa577337ba257dfbbdd578ae1dc88b26026dfa1c9c7b327387c002fc296c6bbfd740c883a7b93572be85e5e130912a3efa5088c11a1eb3bd008081c87c20f3032ec1ec7f9542f650b059db5e53ec8f7bce79419665d79255df30a0c471aa6eb5e9acfccaf29a5aedd0b997741adb50d7ba2b82a6b96b693d1d00e3fd83fb16aba5398334ac101c56ea37ca42eb8a50fb1bf193b9503ec030f5c0b3563434c1473a9ed8bf0e9b0b6ac5c2ae251a511207fb9a153e39b6563cdcb01fc58aea75c78379b388b931e8d48e287c89988f2f404a010e3f1a05109dfcf088c75bade5c6dccd74846f389c9ced08656d37bd72772b61528251088a77a8c03ac60327ecb58208ae622ac2161b54d411f052701844bfd818901771659420e0b -generate_ring_signature 21b7918f07df71bb61eea02d6eaf44427254d6929fed711b69702a07f2581ee1 2170c653c773adf0ca992a320751335723a37f418473b51e05f39f4c56ca0158 198 a7310ff48dae857eff6a14d2feaa21e9d2002f36130b16460b6c39a4a7f9fcfa 638c6ab8292f8350eba3e282099ff5bf2157e249d6d8f4817c1710f650107663 15a1047de62574f925b50199dab771affb8f588651f52d19fd6412c40510ec84 fb40ef2ea3d9c88142c8014b364e0e0c7a3c588e5f45c8588e54450d51b426aa ca504e824c28187e327b5d7ae7199a246f73858407bcade0f3ec9b4d51678d6b acfc1fa1a602a4e8d7b40f5db21b12cd4fee3db4ad9dc873f6b2f835573ff911 a0c976e070ae6c6ac4950b098f25fd6152cce78b02f9c781500fc12714ca63a0 4bcd175516e2d4f9b189dc916c29ea08625a10439ea0a81ba3ff909ecf188edf 8c68075275c103c3581e9e0c3b70687278dc986cdf603376d8257e4d5d95808a a27f6a62d4aa751c9240553e0a3997eb518033004ffad5ef037ee51358bc6d2c 738354933226891098ecc542f68555f2d16326e5dbb8f8c2fc91db0d93db6c11 37b94158c61cbbd44dca406bf67c8d4c13e7d0c8c7e9d6794d5d29f576c477c7 dc71ccc63377eaa777e6f591ac890132d59e63dfd9fc26d1ac5d90e995fe2bd8 a9c3655c5e63cfaff27b9b7553d34b8dcb632d8d7eb60685904c589665e02996 35b199d68a146841cc6b62ef32f7fcd470c6ba3ac20bf4bde67ab988875ca553 7e00d706b974eb2cdec2dc4de1e71773dc7c7dea234749b3dcd9cb6ceb3d68f8 515e89a78026305838e4e059b0ee9573c61017b25e92bba60f61ecfd06118880 9fdbcc0a4a7b68abb0cc94571b407ebb3300c1a37cd1d629dcfcfa8ec982afce fe328fd284ef8f8ef08babe0357c39c1afc76657b3fb3e76948c546dadb5ae55 473046fbfc71a93af79fe516e88dd2cd2e4a129346fdafeb8458a887434020be 76025b240fd55cd3f0d7618cb987353c22ee6ff017f68aadf246af80d8f53a1a a3e4ef02949475560168d74e757d636ce95390013a9f7598e5751c785a0a3558 ca46eeb8fbe1b6f71eb46039987aa3a0333b2423000e28c8ef91bbf96574b5a3 756fc67e7addf14dd110af278b6025f47b721604970da8af89988004a74f30c8 430ee30d84f2bf7dd3d73f6cc7d25970b0da0e44b346203eaaba6b87c8d1bc49 f0bf365b8ef1c4af3c1489f969def37f165f60bf90c4b1e7d1e64caac02e0d00 90d8d60b866aff84be78562d69efb7c75ef8ad43a6dbf8180c7f3bfa55385f42 81fae7a46b3206690ba6592ec7ea31908b6b7426eafcdf4b72a7d867db80c290 47dd69f8405178eaaeeb253b81d9854ca3a5cf4dee853c9f09e5c018a888a990 3dbd9c96886f5ef5ea553af02f558c12699a62aef89f0959d36c11b9613edb5a b6e92ea527b2d00198bf9d08c2349346bb9d2e52f9a58f6f21886762a8bee77c 5137c2a7ef939c6dd71c516956c0bad82ecce452fbd92d80ad2f893d5db044bb 83b98b57a098b4606091163dd68af52900d995955054bfbf8dc5be2e285e67da a34e7a7bd0709f83d3e8edf3f4703e8e78f5a8328b8b537001ccd78a1fea0099 04bb7b526f22df2a7fdd7dd6d96a996dfc840d3c52e51407e6e02fb48653c17b f85f3534c07f3ede1fe71fa8ae9c41a7a6a8ca0114807f27c3a648e67d010c0d 30772180f96ce761ddb6bba60e078ad2697edb3b6368fd6b5532cb556a14a927 246288f7cfa451ceed0911a552bb87eec77673a9ea63868c95fa1a5cd2595d28 5ed823adf2b16c339daf9a63307143086bbd5d9bd48ad7884e12342177b93873 98d730c7b3ecf9a20d36270912edfb78e51f7d57783caa5f0fb73aca4a98faac c9b1c8b87c927d1ff20e8e11be50ac120ea712d56c79bbd2b0b4a7cdc380f0e6 e016f3e3bca138e242edf9b041cc9fe41ebbd76758fd4648b617c8566227d19d b4785c29d4cf5ec8136a91d82b37f00d32cd9f5c0cc4ed9f98d89e9e12321501 d7530c1336052f7fbad4f88f42f7b93a70d824ffedfc0c677b021ba884f54a2b 8a2b6f62da42c1c2345040a426a328224b6e1dde8046031bc38aa198fc946495 8ab5343f36337e7292f19f4f0cf0ed20f7129cb685d46960181ef8ddca615f6c eb77146a3a92895571947e4624b52f76349eea5415a1f0b64cafe44a1d86d427 144403c29b2301fd621b2192d294770c43271c4b1a65aa8bed9820928939f9e4 6fb15a7b933b936a2b00a1c574f114c8f1fb55109c0db8fbb1f6aa0cedd24411 5c098c6ddcc9775715a8712cc941dc760043b1be592cc1e794ae2df2674bd12e 3d3aef339088b60d7de300b385eb5740bd7c45dab071724717e85d10dfac35d0 1f965c8bf7cf38446d5cbbca496b9f4e1316598c9714ba751abd6279ba119fbf b50c8380cee5ab2db21d19d3790fb04dab6abc77ff660d9249ba511823270917 50f3f18353606ecf073b404fa9fd25dd89081fb2558fa739fac549332d81f426 b317387602d238b8f8e60dd59cc63418f3c911bf08e27e743802a6056d7dbe6b a67b3f52133c365193b3d0b07a770ac1a9ae2f968357f405ec0c5dc764d9e89d 51fb034fc77c16548c523577fb83026394dffcda641352e07f261046aedc29f4 cda39cdbcdee442b798ac1c3eca2c9fd9029387c5b9d6e4931ea007c69c1438b 5eabf6be3369f04af85cc3d9ea07eb7d36d5e8c917bb4ac82f65f783087fbef3 b93813588e0e1c30066efbaeaa1305afd7f3bd335fce47e11eba8613c2332277 2ab4bef429a6b5516c179036065c2f051e75cd0d39f5be376db50fbe7d7b524c 4ba8c45569fb7f9633d85db9c3e614fee637bc57f7d8366cdf43384b1c07d5ce 1c6be33f9df30233ee408d93647939aec0a74010acdcb1aadcc828378bc2ba7a 3a5440df19aa23f49469bb5a49f03e94dc8e1518f9acfc64d2992a424c3ecaae 9e251605352bc394ae6cbc7075e8d83fd2c7b8de90c327a3c3682e1b146fe87a 806ad4707c67355f8cef4711357215018aaa16178497d0b7870c27d8319772d8 f6ef4a8771330a3b101dcda998edd72efb655dc5a061dffa72f20fccce62a05f 458caee8a3eb73594db714221608f3ec2e8307b7bfe4ae6076a695ecb329c105 430bf3feb7d8c15d59db56c928803fcc586b1388a9f55864c9509af0263b1583 3c481cab8ee77c6c9182c23a0813c673b5a79f9e39ba968911926127b90fecde 354c79b857a63799189c3fd2ed192c9c5ad45649389191442c4d5d3d653430dc 06f2fd30509dcdbeb9f1b1dadf07a2c1a0f07f0c09ed89eed7aea5d177ec4293 185158f86c6ae379c94c02a972d6a2b7b509611c01fd464e42d1f4b6108312cf dc9c395819a1a1a4d441c34f826100fa164d746dec8e4ec67e2957cdb66bf496 3b2180d54f65060b21d0e3908eb9e61d51d401c3dc6ba7f9a6b6601ae9fc25da 1dfd4f86dab80474c115ced9f14e9654b8c0bfe960d4783ea749a04af99b329d 2836b7ec30ceff00da114f97fb42827adae4f851c960a82f3afc7649b15f9af0 e2f4901c3f23a57de9bb273e7c475a6e95cd151b0f124913ff8793a54e86b00d 3dd28896b87d391d35d51a898a534d20f08d7551e7b6e94bf1620e35016a8d56 29dc089a27b31456ac0015c122b2f01fd3c2df6a7198068ade70e56790b5bfe2 5f9f159b1955da1e199020a276d8775f5ddd405986545bfaa6d760be0b4f43d2 ddf00a1730b611dcf862d320af05aa81bb4f514d36c7debb5ba42e098a1afcc9 68e9c66c54501f39a8a15ae6ae4264acbab0b5d80de355e451d5290baae5c5a3 7b408eb753d2fcaff19e4660d42d3bdd8ef59e2064b85432b324090d59131996 6b0a02f9766c59032201c2c5b04453741921cd65d5150740d99155d2dca6e647 5c06dd72b563394a34e43ac353e54551486d2be717327ec3907e4eecf84f389d 416ac45ccdbcd8b039df9243151e7b150d9c8d5ceb663049bddf4fdc5b13d35c eea9d96242a44976de1526661b40a94932999919479b04d5c542b354f7fca5b5 2d3473d34a84358e2088f6aa6d9fc1035e3bc513ea613fd6604589f0befc56cf 33f7f9c42a7a1f400f64885a3b8f754f7a37cd240568ed89eb4b3bcfcf627adf b10bf05f99f3580191561caf04194fcdeb7b2354aba272159f16dde7688a458d a191c822cd757e136bf17d7b3255d2fabd55b92fa07c3fd2e7769290ccf759d1 6dccd76d0adc94bdc5a3ef4530edb326e205dacf527efb8f46c7be4e21ed9a0d 6f6c9c24f0a9dfc75ba05a829af930df9c90641e35647ac8d9dfe3bf162e345f 4bf557d5904b29c5ebfd6e8bd11eaa8c1ab125a5617c0e26d999afd1da4c69bf c5e1cb8996e6e9c445f5272b25f89ef9f89be61bdf71688f066a8156701b3c14 70e3172770643e04ec34ac004259f8d2219259abd222f89650f41861d47b1fe9 e6be521972ea378c4cf5c41d0776217078f891f04f630497c98e9e4cdcf0eee9 5bf7784a3925c89933199fea56841277e029bdb8e4dfe7ac78b8467d8f84c97f 70f5ad88720513a888301c9bd51a0d5dbe12de43df5d52293153547de0d4f8fc a62e3849fe1b727cf71319308e67d346bb3fc735e27152893b63ee238d0af39a 241c0ba2326a2cb22561110bfb366b7e6a6eadafea230a61dcfd4a9f6b4dcba6 568c41fd4e61caac83dd975631219405341201cc7afa1abc9853ae67a0a80713 b50eadeb0d534b9d1d089862655f4fb15c60763edc542dbe8b55bcded4e16027 f96b13a63ba656428ec73c7d5f6b22a5cbaaf54426b49252428af975333aa980 087e4e0d721080d22ae9b7163c846daa8f5b589a6691e8963c5da8025f9624a2 e24e5da5e60393b9553fc0a0b7a519fce15c3909ed4d79b1204a67dcde2e4569 8892499bdfe206daa5cd2355d77651010f3d4bd3dba9d9f031bff0754c270479 decfbffe3e059ea424c13a18e0825f19128bf74d3bb8bf0ea9f16c77e2ad2e83 cdd209bf4caeeb585551e50944ec07a319f3dffd5b09a012d18f5f96468b52d4 a85025abaeb2656c57b586cc1b592d49b01b6041f84b7c76d74de7ff226361f3 4698f35cd4064410373adcf01d1a5cc9975401fdd593451d29c8fa8685284791 776ad642d6cd0f9c3272ab6cc24064c5ce287d0c4c0c970bf796249515244965 815a7d461b39d605d0cf09624a01741b100d7d8fe7f5a43be593ac631e5f1ad6 3e06f180ec6b34f02d9fb05ad6b1e18f61c86b30ff481ddc5998f454d1e66557 053342a00d7f7e7d63094f10f2db6aeea8bc2a4604bcf0780b06f670780f5fc9 0e0b9d2d83b917845a3f5e73a04137a499d9e6fa8b867ab98bfa82b5fd76ddd7 8a7478a0b93dcc275f706f458a0f3f7022ac8812b1a0b3fb59a17ff33887a926 2aa41062284cb512431c3da7a24fd4dfca3b3b9d4746e9c281f0a6530c77bdb6 d2aa71095261507fdf7ff02a66ec2f810fb4cf25a1cc061f2fd9e6cd991d29d8 749d7d4fb454e0441786b0b83405adc7521b9133be1e9069e98b0b4a0a019742 9402fb817cb1bdf0ec3b3ea7f0e6de5af7ba40e5dc449a4604ffbddeca3d4bc6 7fb5ad8c1c39dc05b6b9f5683b0bfe3453a36ccffab3d7dfe8b0b24c9affd6eb 1d5beb87c516b2f164cf9b133359af4170f80919ec18b0c32130ac1b72384813 d7a29f1eb4fbf3d5e59c92fc82de7b21ae3dd5592fe5bf14ca947cb07f91f294 7c4414a285d08785d7ea218d851629f3ea64cde0db5f122a7d1234f2846679c1 da461281fb75f952c2d820e634628c9b7ad8bc85b2652066514dbd5081a8f001 24ed659f872fe2ab6ee3ad8e503aebee8871399ef6e8173a9a93f3c927c9a006 302b8bb71116213bb79816efe7111d60102a43e2553085ac726b3e9de9ebca63 ebf8a047c74fce459c1cfb3a267c973f22be621e82ddbdbf5c0fb8e94d8dedd0 82ff33c38beeae7ebc65ea166ca06e0838209719d9f68574bb0403e7a07951c9 856688e811d72ab66cf00280841d83d21d9a5982f971ca1b1406e5c205ccbeff 9314feb63caa7c71cc0c2e1957cd334732875c8da47356dae4982c32eabc902c ec90e690723155d6a5ba149f7070c142f367020035f3c9f7fe394f4c67ff5bcc 222caed12c17c95bacec54ca61a7eb3e37710521dd268815f025c082fa8249df 532fe8d1a928b1715dfc99a1e2fbc51fe9dc599badb35c31bbcbaeb53377253d d41e747309374c7b1d87c86affee4d0747b7a1d9fd1d127b54891d896008c6c1 89170b10570cdb10d9e191a8a73cd46f8b9698db908daf7fb1dc9c7ea34e2c92 4c7c8409f431e936708106858f52c7c54b59bbf7485f66533ca079bc93128903 e2e9f74f6d1ab74bcb1c276143e6bacc3656fa372ae0c55d86365d4bf87f387a 71370d72ac3428b928fb2eb392b7f9b08b1e332c5571b720b1062e60600d4566 3fd75f265802ca0b4e7dc1b45f2248cda782bcc3b60571b72ef62b3980fe4801 094c60456ae08e383c95c2dcf3bc2e3c067762548d57b89bb7bfff28ea5de068 af19c103bb8c17788e90adeeab90d5744cf8f7272b83c51dd1c5076e9da6568f c51e9883759ff945e2072a319879dd1940f8298b2752fdc99c3eab5162a9b9be bea441eae3c9b9554bbdd4f797920a478705209d2c837dfe6a43f76df9102375 68ca55b4f18a7ce36e32f68f31d6e55e0f6056eafe2066d1f4f10a1d36e4aa61 746d492d44cdee213b7b1ba63f380d3582cc84b187c115f13489cda5fa7e56ef 89407326a649e5a0904d2f02dd4e488f88a020c40a9f91671fd3f7c2212f00c8 a28169cb023c0f991bb0035546a78d954d0cee7235bd52bcf88a5df99b557f56 19c17f8474d5daf47659a9864cf1af09a594663270eca53da879650400469a92 10ffea22bad2190a153341c2b0d21cb3a4f907b8a7e926ab8d0c41382e4b8058 5e0f853d144bd9e6f460d06a4a3a39f150448b88835b4532c41b2587fd4c0af7 31754c5684b404654bca97ea38f2c00ad3e5f4ffdb97fbc8668ec58694724d82 b32e6d9773921282689cc369c0312f5a0451e66ea941e6448c98f8e981d48acf b147c1c3510eeb037c72cb71dc37425e963685f84f32a50cc01fd408d0bf4d20 57d2c9657c3e9a7296bb505297e55796b77f5d3db2f4fb1f09d23771ac62f398 b25cf9566236c3997d4d997f3d5670f676c13965d4a4a66fdf6900fc4883618f b7b93b8daffc9d5a4e47f4757a70e21a66df2f452bef667140299482f3173478 7098f7acbb510ee3f9c0499b2395d96f852d21376186f275f02cd7b2d87faff3 8c6ece212cade6da775e5f059b0efec5bb6136903d3e5ce07edb1ae45854b6bc 94887bf7a0fd4b7fd0d2da3bd78c13923ba3416a1e4e84c90aec94b860541a56 176b865ea64c0020c7e8371ffee45df70ee0f2bea422735c30201ee2aa31771b 45b58bb64bac42231f7f98469abbb0ef075568d99de4d3ebe2a480fb52cc6c95 10fdf36454f77b8909acf5478166555bcb2474be93a5cfe6d4c882c05e08d3de 4b0d03f74f8a5dadc9ac19dd2b0811f5b73b4dc60972dc052a656803510b8e86 c275c94455f1a680592271bbc61593c4dfbb38fbcad68743c1500cacfb542d03 bbde1fe13a989e8ae0ccb8260d4c1f3330a337dec7cd5c20dee2ad781610329f 50c2ae0be35cad3b9f25102f68b7e69310e1829d36ff52d23a7a000b31caef02 35bb8d2683e7e25d64547774b2c9e8553c295c367129a53cd2ed7447393358da f8e1f760cc5b69685e8d68d6d023fd5760790bed603f69b3148fb4d245d07e29 ea4174e4e5a6844c93f511af95d507c45c939ceaee14b0a033f2c93fe8f5a040 4f117a7382d9d5c50861b62a8ec5e5905ffa6633ec41b00bd61d0ffee78dd05a a88c98a8ef1d64f1395a7029525d0de4b131db3cda25b1a401c723c64cb5e357 617280cb8f8e0e0f42c62c53c732e87ca4fa192b578c1120e40905df9e8d8194 c409b94d1cab6ccadb8460b3d131cc18becb84159897e15edd646ec4dd4fdb9b 2e42617d8e1bea79f0f399443fbd5f15947ae5e52abe17726ddb03ed0d256d86 6a73186410df4cd8670032cbfb4b62b0b7f95ba4d2c3bad16d5a053f71515852 7205bdadd2305aea4b0082ec7bc8a2a8856edee2560e02741a775f7a0ed2e1b2 cc7ba0caffb42807788c1eb7944ab97f8fe62b73c41300131853c968347864f2 78d4acabd2f716af0d52789f27bf35ac8bfe3b95ebdf90437010ef89324fe1e2 98198945989839786c18edf339f538c065e83985d97dd7eabec1baddef389457 603d891e289f2b8ca96b43ad5c54d284beea789a4ae319517833723feb4ea9d5 8eec45e1c95eb6a99c6223b40f6b9e740709941c848df5ee5c4a1b23f100e68c 948afeeaf126f4fe582998b6cf014b9ce21b3a7e0cd439f40be5384f5c7ec905 2702a8ef3481001ff530a56006930329e095a1f73543b515159a0412d53303cd 1d0c05dcbc95d9a736871d7c12f9bf6b0455de0c6d19a17f90680a39e974b96c c92cbe487bb69a8bfd16a8fd9b4f2c272f564208f1f6a39694ef3a72b7f1c236 5d8db733373e9343d771cc78b6f7fa5b5fac0e5b7b55ef6115c7611fca121150 ff9701d74d4540e1b6494044cb26d0d1edc2c6df60c5f646753b256ef187fcf8 c414793da4b592458d92c8116978ae7a5d3bfaf0adf1a0799427907987098949 83184b7d1d49adaccd9662c4004306d458a42d88d16088ee50ec8d82c9d23256 35905e97de54db871c52e900b7b28c9524050c8508f547ca355122781dfb8dda a177e03fee74823cd5498a30bb30defbf7fcb04a77c7a03ea9a53f166196138a c589143a5a14b61399ac8303294231661f6871f01fb323b645415f9a6f854e54 223d256da69a41c4391235895d0afc14513ad9b8bbfa6c19475a07287bd591dc 6da1397c2980f41a9802a535ec35e3c605cc0af558a2c08a44435c086491c85d 2570acf82514e472c79a88e3baaf0f070386b8e68f783292237d920b3155de1c 59d4fd0553f2577e6817728152a7c6dc652fe69f78bdb2a95feaaeb37e8dda0d 65 678eae8de139c182bde3b5d0a860b58b19501143b7d86e998c4afe013aa1140d9eb9c6c50743da686a72ad3d660be2d250874c3d4df077871694f9c8d262e704f2815784dd9d18730b34e781d9c9064a46b76290a66d5807765c18208eaade0c4281bc3d3a46785424f30b9f8db77df811576c6c57a456df3aa997947aabac0c9e4a1b74a033a8030c12d5ad5a322a10224fdf1636fd966e6fb1641c4668db0bb650237ac1236e42ad06763af29eb93f7598e2b30f476006876d8d36371c24076be79594ac94a2d78cb4bde2bae735e7e8e3e3062e7b20d1fb791f1a560a870ceda6ed84ed7dd8bec813becbfca631be3364766ed49ac144ad74649e043279054627dfa8f5c47f41eda9ff14e242ef8bb3c0ae7cad354261fe106c434229990ccb89ba7d99adc20a14c503ae49ce5644a07d9dccd066e9bf034a4df755ddff0cc84fe104ac67e0a9e2b446fc3eff80e6ceffc917a51c5bd7deb2ecdec9ca0d04cd51cd3952baf89ca71e9090f06896226e4b9eb27cf3d3108466db0b9848fe06a8b73a11055069a8a0507fff8e0bda43e14f98364ef89dfec2b2e60d48074407949e493776fd478e0e718973b75abc1cb43def9447d1552bb76abfc80247690a455763cc7001faa65f002e1afb18e3d594fb64aa3e94e5f03e54ad17c83b210f80fe0bd73b2e4ddbba70dd2b98fbb3fd74c8a2951487cc4cfa3d2f2dd036e7049adb530148d39f87857e7c1de27c47e95a00254689be705ea93ebf70622dc10b6d97df1233af52571e9b45ba178640c4cb37992bc1ef7804ae453ec0e576160ffaca3b809d74e59651a368c088abfbeb7a69be9178832acdd935b3c0f2171d07e35a6fb4decbcc2820b0cdcb980ea7448a28e2592f0f07eae160ee2569d07405b33e19d105647f16a5c9ec39b5d220f11b678f8845f3c4bb93b90afeed944e0860f9ccc0ef52b3599e951be761aff6b0a715c78e447cc649fc535813c1046402e7b8e4e12c7a4d7b1114f4117792de9c64ee113babba10db7f22e771ab99620a1ed8df6297c39134b42863ff63c1b241fe652155d7c960bc9203424db29dcb0b97755bea4e9c7e65426410bd56ba78d63fad168bcbb0130ba9851384b4a8ad06891a7e91789b5e9c168d8f8669165cb427de0f9a770160f8e3817523566ef400faed35f630621bbb7448fcd55619b6b73e6d86a5b87f20fc318608d1ea1acb0669d2c9e30f67adc65b4e028fcdfc1828d37c7c80f9f116742fd53ded8c64ff0df2eef69d68d1cb28eb5bd8a2b9738e1b3f6dfd2e1e47aa1340b2306142383b0a3cf1f5347e9e614be8cc4a8a097870910fa6703ae177c108274397b36679530e79c288087c1b754ce87e9078375c447ab33616c54d11e784e08f6fb57d084807af9daaa78e3e68e3e017a6a9ee9eb3f4e27b5d26b959eee150c44ef58ac5870e0d0bc39ea0a0bb1b0487f4d8d923afe74ff85d1e6060c1ec634d0184bc53ed0b4326faa5d9de64bbd311c6b0f116f07fce479b9ae2ac4224a2acf53a22f077053347362de8a431c6c754cc567fe338e361d3e6d3cb91e5cf1a4ff58c63e10307336e3d03597ab7df3f62470953ba2b5a19ec4f5ad7ee60a4baac399556807e0b6a2469c2da924cad6e6e96302040f67549166f41e666aa379b6b8644e983ce00c7ed5089c8876973a28cdfcfb3fb762892ba05e5352cc270b19ed26919b51d0687bc152ff601789580adfb0d47ec88ccee6a7a23d1762178ae618b56d321ab037caf3bc810835e9ef4a11301954825501a017aefb8f7c7340d435708d4957003cc30d93e102172078625d88332ff499c55f3172c19e2a6ff3378060808a0510f460557e634148037267376e4afb5ca277ca9895bdd8512fd19d3c2135444f508250d91819e41f33532b972cf5f4096906b1dfe1070cd43c72773f531c770a705f009192e6e6f88486a079de3122582ef6717c64c8b83df7b4adf4d37ce842107378acfecd299fe4ce11a4484ebb4487a6be6dafa7e6751c2b981e5f48941a10028652fae7ad60648717647762447819c09d16a94001dea7d51393497296aa1062ecbf2a921ab3a53b7bfbd8ca7e938fee0540d0ce299cf36ee2810dd12c0de0af1aca421b78d986ee148c444a61b4f47454b5984321f47a0b7db9cf639571704883db7252edc2a4d6690c5c5d8e901b388148af9c9f9e4dd17e0661829e535028ffa5a1938d62499358b0eefb568379231c3a6b9281b9ca8a4c9ce31aad3750671918d37828e05f4240e06f4f3c34f005e8da54786c5708ea3cca41169c603061db06dfa14ecab857c39c1d922ff2a0e18fc4553e6dec474e4d7571db54e030488f7c7982519c2bcbff4e599f8b931d2375b77ab364eff362237fa2d4f1fca0aa7df88287e2862c7b2c4ae8ff68468538ac83b3092c65f05e45aba016158a50338f8de53f24d1c04bd02ef0ee01248fceb462993512a5dde379f9f47c624940d1981904ef782295d815ea00979d4f7bb922d881f8b5a0bdfee7f8077125c08014399ea0cb67afc9da4de2a64c0ee51486b30a1b346e47490e62a252db166f60b66deb9dd6f6b6eab6fdd321a916e373451bedce7f774edbb45bffbe1edc9a807602acd39a13ae7331e5f89509403ccb5fb14319c0e4291b99deb5e18ad01bb02f7d8777e183cc1dbf63f7467541ca3a56b2e8ccf4f2a0fbb34586e5afdf9e7023db11b276404c876e5c87c210d08b02621158362bcfe839e5184710f185c5f013df2a3be8961e5cf80423ef000e8a9822ea0016c6ed89f91e75fdd56d9abc60d2eaa41dd80c51c7f704e28ef6d7a0a6438d7e5f276191317317390954b88180b297bb66054f0b77c746bc6352d49e35bf9dfa0d5ffb468f186d1bb91613c3d05aa6ffddae8e1dc943b5e316994b9b8c0542a29be857105ae973a0e101f11b80add24f608f642e445c08bf1317de2fa2bf7e1ab714b5d06f53eac26a72cbe5108f423e698435311052e9c1a3b8a813a8f9acb341dc7761e11693d728efb50f50cfad9f7a607be8faab179dda2308f6cc7764ae92d9182cb961090f9b93ee04a0b9f5f720b93144d8a87c33802934cac5c552b593aa345609122f2b0121716310da48e9081a453e6c8feb7cf95eacaff94c80df844aefde081eaeca626bf27630cfe438d8b387eb1dafbf32ca80e8ab7f4927744f936407ddbe6c791e91ab1e90518b4d492a97260a9ec607119356b20b623b9eea46f9e80e24a52f4905e3c6e0809bb28901fb11ef8d4b4867a3e17c3b098fa56a51d9a22286fde67293fed6e04b0e5e90d3023c964f0ad72285df784efa9629db04985c012a19881c37deb4f0341580ba2524410e45c08f2411b0bfb209e9a43b317c648ff507032fbd7cce2000048fcb60f3b77ea6b1b4c6498823fdd6e3b851b22dc180c3e870a8b90d9f003b01a13da91caf0d9351e21063b7f7c563136ff9979c1f684408675ddb032b60442c2a92d8e3915b4c24dcbb6c6c038544f88946cc6989262c62a954369fdd20e1b3684d440849d27b83895724ce9588bead187a8163a4e84dedaa75e8c55720234dd026a9fcd5c55a42f9d197eadf48d3e94bb9ec9785354f02652dcc66a8c0cce98b1bd5dcabd1a4bede01a20055f9ebb18a43bde6b9a4f896d60f7d203e2069de7312a97c8d72c1b474b2c1e6cbca04a9b52f4a0a368844dea6396a2a64f06aa5373ae0e78f8170394287177077b7d1ed532b662626dd79a95eba3b3eae30274fc12bbc2d21df776763402b12d9d4a5001761d066d9ae0ca38e3395709be0aec3811a953bfdc8708d005b8ea1aecbf052298c93d13147a5a34434e551ab504a4ba17dd4dbbe8d3318277409e6c3a62a43d0ae0b929aa8bbd27a716ff7a6d077afb249369383584297bb3991d989d9e6c4405f694af4de1aa5a47357ff8e30344ef8caef73777df3171f4ae23f2740ed4a6deb46e9da8cb514020bbb685cc0d0f366e21b3443c0f4b113d02a78c77b995f85d821be6f6b1e0da439b512b8600740a67cd5ce22e4d13bbf502c44119f1b31685ebd198c585bb72ad0e33e5d90d9906e9ecc3216dadc68c4362e72084170dc1db57e0e713785ea8fb5676ee720df5b8acbf11734bc5bfff1176a321dbe70ef27990a5cca4e53356affc9a79300aba9e32c84ba408256dca1be92d67a8add78c98e86b6d6921b0e8461047049503043943a7a7b6775d6a525471e377dcaabbd7fc51d45501eedb22f9ba62b21f022d2ac7841d509615f6b803f257d5a7a89b7ba5407c1513ad0b3da1203dcbf207fe4123984458cdf767c5130e97a42c61be4a9e111dddb90f2dbdf7cdb3e5c00a6df4177099170a7b6dadea1445491809790d6a0a3fcc0e11c13db86496696509196a005901e71eba6b52e45c0a091949df2413f72509bf1431410078a4bfc50dcc44a54d04bca020caf1e376034e54f2667272cc45dc65fe1e7ec175fa2e400f4a3618475eaaad6fe46008f85efca36930a9db4ee733470280ae462cdd11ce0cfeb7cf23aaab7bc10bc9967d49e2dc61e326b6208cd057b4464a089c39be570d44e1800b9b15a4e4b39899045223a2d1c9cf5764580ec0b1142de0a5b03be20bc15ddafa91f9b2737eea1e4e90a7bf791b8537fbba45a93d3350ded03513b1021b14e711cb08291f9df0f051c5a24c875551e6e6d735a30d17cbe6e13ce9b40721c44b880f5613024cd91862839610fee3447720e8ded350955b7d069df7cf042bb16c47455c729b2631270c9781d6a3eae40206f3cc8b1ed924ca258034810a7e19f54de6a21ac2b82e9cfc8df42fa138db06550fc3bc78d69c2c4677ba37016594489990a2ceb778fc984113f789c2844c47c2e65b748b4a390bec4ee7d308dddd39d5d0591ac0381880bd6ba0eab2243345f922466ba4bf3bc910c2ac2e092ae0775e45c6cfb707f96babe5bf44e9ebd458250b63f99df848b9503744740ab032e58fd6587831185daef5185004237463e7c82580e975e5c13b76fd562e0cecb110b781c47fc8c51e5c1e3094883c384a2af0d810947c85040db0ed353c01b1bd4d39763fef8e6c1db3735cb0d7cfc10a6442c5df12157d28a534a801320d877f27244c3d4e8fbc0f20c88b0661bd1b3b7adafe15540c26e0f1824570cb0728962e26c903819be14243d51b253e8f22f029664bb8df535f615819740aca078812107793df667408ccda6938d6cb4f2ecc5e257384c1f911147fa5799753098e15912016367c6f70c10ac8b6548ba5dabadf952734b008f7e9e44ff4fa6e076a7fe2243e4ad48beb7f36f47cea5984cb3ea801bd20471d089dbeb3c21745018b6c1affce81843cfce5dfc741bed10af6db606a7d9becddc5d5f2bf7097a00bcfa682bf089220d12ce471ee0ee94c520754c48458bcc6b3976724a7a2efc6032c63fde3856c63d333563f2570e5b9f6df8ca848b939bd3fcda9213168c40e06f7f2ffcc8aad216b107c5f77b38d3e563c200dd2286e09dd31238de68dd2080b131937e25a9feb207f48a36a1e27f28c222e5acb1148c525f106d0b28f3aea039a02bd85878b849176947d2baf39b1db58b745a70216576d1c087b0ac5cf3a0278ead0f1fafd1b4a5cf3bf711d0384fd8f8320fd6fa4a7b82017667d16438f0a1d0046a35356849982cbf07c8f44ad3bb9e2f199e2dc694729daaecd7de9c80325e0c7cfe150224d662e2e7f4de9ef4c41b4609e2d5b0ee528b7126144811c01e6a85bf6d017def3063ce2cc76ed8acb043c78521dbbb9d0394c2af8189e5f095cf2dc216ec58d34a4dd46bae6e5727acea6a4ce117b6c26d2b64efe444f6c00b396d3fdfe199b98dff5eb513a38dcfc2255c8d65dea605d8f984e7068992203aef0364d4bd8a97f653ad7b0559d826fdb5e199e189a9e980753ef3fab8a2e08b1bf1b985a5ebab67010f973620d69ca558db09c906ba01a2ddee1fc43944b07a84722b92a2a46c5b1ed26e07e206b3e1c908e8d64397926966f2a4aa397b3023c32f38d4b01b3d8cb97f53a16f7380fbfde9ade35a71dc6c53e17143ca53309a23f19c5e765e4a3d41fc47ac471665a2147dd9e819264819b10f7a4a4e1c0040442aa038216de6dec12d8024f2b6c555c3d7eb778b32901e07afe5d97eb8b004a153c768b121f1d9220e22d590b6bba02e82d360d3e3ae224c19daa68aa080e7bc977ad9e9b09d2c648123ed96d8276578b50f8d1a079b37772ead24ecf210aefe0a5374e3a257ed756ac58abb27ba75f0c50b6b79822d4db3eae761dc74307a1e9c074c7326a57f956fd6492c6a1e101e0766a122692270d019f65c81b8e0bf0c13cf5c68b194b396bc139cbe3f50509b7e65c4e8f47a8a1be88f1acd162068a7f1e62a350a5867c86f4786cea6410bfb7780e51a017fb86fd6bd620d042029410513d199d102dbcabd9653f5ae2d30e61703840f11ac0665051ad480ede012a10e41188231ec887679040e5e0886101ffef9a6b9cbc876dbbb52ee19b880d38f7787e1604f67d3e4cbbd58e642ba98e005de3b2a008175d9841c5e66e650a6d5bc6168b05cf6b9e478f9df14949dba25009185ca17b413160be80c1620c0bfbef130f96ca7b4530e9cd74ab162adfbd0c85190e649197a7dcf2856a6ecb079098757a1419a6f69559b396a692df7908ee79beacf599d3ccb0df6f68df6a09eef4aacee38f57d5ee837c49fbfc55ab3a4f4209b9d6e74fc79c8c4d5b11db078a6da069082638a9885f5678733dcd7cdfa3cc097cbc425e6fbfd9b74128fb0eb9e109d219f4f0a8e602da357a848b10179767403fcde486aa5c4afbb28bfb0c5661493b9d5cf7f60444464c0195969b47d9f0b01545baa940e8296da7be9d01ffa6605f6997cdf28b40288f7c697f5bcc150285a03377f0254c8c8f379e6d044c9bb7717edc2aec6d8f69e83be85bf9b2719c6b68060daf13eb8ca5f8ee8d02f11dbfd8afa4da5c07a74f39c0e86dc6fdba091dee398530f4434659a236cd0a8e83ec1e9dd9ada1c63dabe878ad926b40dbd4902470b98b964e0bf98bbb090a28ab3788310ad36eb4aca8dd9aaafc857de4934e31d0e34d508e835de92e1609692ea047d6183e3606aa8a1b2957a834e1bd392e6309fd046184d258c624e202336a5d6d2fa5cab6dc9cbbcc28122b0d38bfd0115c062e586f1cb57c50889e0c47134e34d3c0725dd05aa47ed0745e26296bc91e353731c3d1b859fc3005e304a3c9d9cc31cc0a7e55742acc6d5d4f8b73d25f86131cb2d166640db6be857c018a77a107e849f6ea6593a0fb13096b9c8d41e60bd4b909bf6639c301d21246087303d32d450c23f9bf9b4a5d77055945abc2c701003e24ff23d82f2b1d16110f41553aced880f5863a73cb6c2d159a823ba43f2d66c73b51788109571956300cb2f88055763c463f5be4a9038d32f39f50ebfd60ef1d942dbef1b12f6764d90330a6be554f7e9b10027c9179037dd6dabff94c39abf4903224c7cc5e9cffa903738e76bbabd90e25cb144893225412ac99cd09e4d6506c627c5226ed64265808dc2c29446e4a0c5c72bd517f42b12c474a9cb49d95ac77797ea5ae0c0afa8f00751c2b6099670551a1b3fa98e454495451cfea31b571c8f30e366dd765cf080c0072c348b16986bd21f3ed367d679423d9fb2d1c3ca58f6f4b1453b0329c9d03b2eed81cb9b89365db906491f091463d3dc5cf149e3c204e075b3febb410da0f56907dab42fa94aa033e6df957171b8569f57aca116c06aba9b6d3b8c24c1d0847188b8ae40d6c2651482dc88b0ccef6092c39b64943b9bd9f6f61a753658b0b428b31a65920da33604b7ee04b5a6ea512036488afc7827f88bd94cf62ef150b51980e963a6ace6b93d716c929b6740e314d37ac15c0fe9e394a399bd8d5eb069fae1bdc066e1db83aa723d78118986dfa6f661a8686f21b3bcc9e6b232f2c0469bec005a366dd47c243cb9254ecadfa500e64bfeb293b48d0d6656569b89a07451fb71513ddc2beeb59a6d66ed6f12a2cc5aea24c0ba95870a5a0c6a29eb50aa9316064132bf5d170216ea8fafde49b531602339e68f1671376ecb50815af0b694ffe3ce6d242c25af4975e6ed6a24175c6f99e34b1849ca4b1565ccd5a0a02d9cb39c0262dd7e2fbb88d3b62f3e9b5f6cffab227d4d3f1f84164a5e9e34c00bec9794977fa05032249fd4f1de5dae5e132fd96ee8fde292b78511cd8e1220c6062c0279f1bfdab5f8860c00182140c277f5df8b0a851b70ab491896ebc460923efeedb57eb2a31d42b982da27bc46badf52f754ea83b59aa8a29ee6d536101a6e7288f9bd18bbaa632a1c2916fa565d842af56b5fdf4e1c6c4121582b58a045e2b6096c0d122f9a0a9167504a69284dc6c944c3ee6b6a5fafc85cf81c8ed0c78f5e701140055c745851840e7014d50c1e091e506e1700cdc2d3cbeb8f307080ae06eaa141187fba043d4ffc00bb5368cfd4d5c3ff2e40e7fdbd5bce31ea606529668096df2abb4f346de2bf9ec9d7767f311f722bc40f0637590ecde63c00f20a68cbd8745db16018eeeb26c5fad6b52777bde926867ad4a92a3afe59a4f0c4655f0035f63687cef766e390f59358a967ae931bad0fa511e86d5ecc0f4b20625ca6cd9691498b47007be56e5be857857f20a51bfa441a94a8c74e869f3420586df0a9e3b1f20071700c5e81d07a89e333478d0ef724199188ff3ab9337780300a7f50d52a6e682cb6185b689ba07d36616449a42a1cf6a50165b7b06492b0a1d7d37512c0e9391b5bd7da39ed336f3aa93290d32de2335ca2d1b3108c34d076b9258ae6de2690c1a6e9dc308e38d261f68aeedca5342740cd2c2739ac6ea0f0605b0a9ed5643698e22722b99f4e2c8e827f0e3cb63351700abe35d8733ba0bd3cec9d1a73086b83e469fba89c32abe99cdc472a13a6265161a98b704fe1504dbef318b31348f5830750ba6567749471498717e6f6aaa14791ce3cd47d9030741ea9802ccf55587ce46535beb744b70af680638fbbb7566062f450286505f02cafc16d4c8cd98e832f3e46ad7cb6cb3da45d81354408850211c11a942002d0b9fd7ddb07b9db67e4ed3ebb7446cbcdbc1cb2dcdc45d74e2f8c48b5654b0e709d6af4d2a44bdf1d0a6f1761af7f9372cb8c5312a8dbdca72eb2f0215f8041d003101ffe0701c20d4a5145ccb49c461199196428b517bb9f92f7d39661d02710da20ed1dedeabe16bc7daadb3e8496ad9bdbf07ea78a0ec3e60324d71d944b00c237c75622ee016abb732956511ebbfd9e0218c62754c0316f99e198f446c2d0a99d37685254b64ac1588eabf8bace118967e6293d0d8c52c514b71b6be540e0d658ba5e21baa8a28fccf6a444dd719d5823069a4b8f556886308e9dc6ddf140831f5d759e176dea560fbdba607e00d2b52ceec030e2b4f8b0cca131ed6afb202f498f7e99aadf966607058a1e43ec6a4de776041dea9bc196ae4199d2a6fd80aaae16903f20ced8550da409302270426506b06f35a8dd51c9ac8112f939c2c0ca22dcb7f20a96c788f334d3054bf63ad713315ddb0225eba607e808ce615eb05c282db743389120a09339d2831366a324455ee2c0df9e0704bd98219764db602bbbddd96858c7ecdd31d05bbe37bbff69e9703ec7c3111ae8d4a923558f1230b885bedaeae58aaffb7c50774861f37beb840ae6ceffa4067e4ff91b03f41980e7c07eaa8afefb57b1267fe166f1ea931de0cabe668cd8714fa913a525293ea0adf07d56a404c1b6264c87109d5945bd23c4313d9641e96bfc123180bdae3ac0dc02c2a52510a8591f5435e6a003e7ca089a312a26e0a188435f40bf4abf4db0239ec20a91f353af812389d537890f73c13734376805779129e28fb5e49eb090b41a741b230809f4f07aafd32b7968e4fc70aad4c48e279e2ff36934741e2fc0f7db8a3113c765049ddb10831b0fcd7c43f1c322451d6707a9490ad429412f00f55f788e6a57618c0a24badbcc7c8cb17f9fc04c59519f212181edb1e59aa6b0b1d81eb2a07d3bcd5527d2a3fda83be228c5d5a69e643cabfee840c36a15e7e0bcc4e6f17b66237a7ad4762362a2f367be28b27b01657bc96c7e01899cea8250656e6b6cd92e056c79d93d33b12d6e7d5d43b4008708d8ebaf0710668c0887e01b742e1a3d5f79a9212e36a432a659d37b9a89670e1571ece3784a3dd39f15902af762954c4c7c0bfbe383bff410d97de9f9f7408f612fc9cdbb2fca93a11f40d088d90f0669fc0b7427edf1796e5806d4665e53b1de0a774efc5864ccf3d8903809f03bf8ebc9f0f4a774a19b3cd84e9fa50dbdbf5da1ace4472c8e3a5c84a03d652daeb1a3960d9338e89a1d350ff24699d0567c540787058f847fa237c9407f09ae6250ab959988ea4760e5c8ba12870c392428a1b56d40ba56ee1c9df030dc7f93c4113ddd313b72711c6d05405ab5afcd51db32fc18285356dd27e3685087817d41b5be3470889aee80a89276acccc57ffbb8236fb6c94c7a91a08e22d05205e4afc8524a1d2d676c20042b25e455a76b2232d45f4bb11b4d590e0d97703a3dfe4a662947d77fde5232e1ce6479cabc731a38972b7e697f69c74798e2d07b89cca2fea581517e3f54c6959e502af62cddea4f3142cb195566263f224400fc2341be8a6fed59c44d79afc01adf16d70c20f6d5c8bfcd7c7646bbad486b107921da2e08f2101dee74dbce109af34fae3a1649d631803017836053b564c1f0b5844266d8ef7b165352b0eccc8c70edaa2cf55a0a2ddf8a813cf02101e8f0e0992874df4fb0ad12d487487a29b1ff8be88c90ef533e69967d2f7140d747a180ac83b0f23e311db182e6328187d5b0941780790a8b3e3f19f776b482dabaf11095cdb5194451bded33240215291ccefc7347eb50c2643c4a7a686481fb94b7d0c876337ba13e9f6787a99ba7994cbb27e22fe7ad06d72e399db711a01c657760b91cae550246e2e33499c745934bf8f164d5953fe138a999cdfe179f0919aa9008116b18ade72b39899ebe3be7826efd0deb7a1b93754ae9d328c75d0ae558107f67f73c3527eeccb7cfb063571d81480a8c9537eb476e0a803e602d57fb0b90cff36120816f1200c5dca1fe307aec761d553484956e58deb0bb6f99038313107af30c66d5dea9aedcfab6c4ff33adaf35d5d2bd30776051dc6e91868d0c23d06af7370f9c97fe7a8799794db0926fd427e9c04929a11167c8fb0a43f47c40505ad6bcb207741e965c6183dcf60fa8711a1959fdae2f3f6bcd006ffdc857be1003f5c35620b804dc791155957af884c38a0b0bf8d1f5a95cb6deea176a25ae00b70f5063d3eca1fb08741d48d6dd2bbde3aa5be24d2af75758014f7eb37b1ff05258bd97dbf082a3e2938841d5af30e4feec2395c9059f6943371f3ffbba7d104cc92f0358d4dfbe8bb8424c37f2ab65080219160f49519ea722e0a5b9041fd01e342d834febbc4665eb109c88c522c4275a96b0e51b64d718d494db4ca76c105b5191c83523aa10bef19a634d35327769faac822d98540e129436b03144fef050e40f03641c0dac4232d2da6a45ffe82f41bf4740ba6bf30992bb0e4b78c850c43430a03fe1704f1cce943771bc9f6602bb2eb4f2cf4bf0735e0f2f4150e9b0c74494c7054fb5311e3284a5d1af9d01c308301546329c95e101d52ee1b102505b92dbadedd0d885a981da3b9ada143cb500716284ec21e017c8dc39ed717680894df38eae595aa2286d6dd2b14a8da0f26cb6d0ff5d783183c284330ea7c9c0fcd2bdd1527d23474847bd66a43bd1309f114f1519efa73e2653848ab53fe4a01c67b7b26928bd52b9b8a0d432fab24c7a36715da4836e09f4c7de7845bbf11017c2a1db545162e57c961fed4ef70eb1e15b62da0ee389d6e68c7ef9b6201b604167ae60f2f9c61a695735bb0b848a1d6123a5b95e5fc3f2f66884545a5de0607a9e0c7df4a2e61dd721260d220b664518794575dc42c073aabb73f02dc94d0089994042233f947623c71e8bdc795c44f817fa34f201ff25894147f2ebceb250a8465e2c88933f35063a72b990fee8d504d8ee6dd68e666c0963b9388f272a60ee0eaa967b5b35d5c70e37e6f1deac5f6dbf7139cace61df92d83860ce6290a0e3119a24028001cef921c67cae2d3e1580db747f052837e64d9fe76b8a96b590b5cbc578f927cecbe3a6c88eab1a0190b77b9ac3c96e9a36fb2d27dd107931e025fa6a12bc6d5e6598e457c4e284c63bba709e73ae80b1976e720f8884ae60b05022c1fc6a4eb84061db4343232dfb05d5e5036292bfd28ae7ebbd0de1823640f738d0b50416ac10f080bd0346c0dd66cc11140eaa831c18f1eddbfd6a2d33507cf0a8a7f671c7e18c444aaf949d9aa6feb48e8fdf1416dd049186cd8a293a70edf8b9bff38ff5cca6fc89f376928e05b913a60e00bd4f73aed589f50ba20940b98774bce4213c25585f2f5e145d5a60e5ffb11306fd06f0fdc6fc7f948046a0637523c29a73286da1f23e8e22c40175de2cf53cd871a106d1d4fae99def6290e6fa8419efbf8d7a2a646ae21bf1742333c32962bb546709da470e89fb8681d0a6187db08f2382def7c37f853a7520b96cf7afeaa4ee3b2839b5cab53e5f9490cef42ecc1438d525b97c3226407057217ab2711cbc40e18c60dfcb1537816090fbb4e218c0569b91f480e3e123c1b3f4ee9efcade4a09368dddf96e8ad06ccb0e8236c7043b9e8a86388c65a6684e0e8509ca555fd6b0107d1b6a9f5a6ff43c044760eef66c9d48b0216c97036ffbe2785a860850ef403b065dcc71589ffef400ab490453b5a76765caeaeae0c801de6627096f712ca6b558cbe98123fa56540c81383ab0993691308e37516bce49002e0ad969d4d3b50415bb46b36735ab9d0a43e1c96b89b3ced88b8c16c561026bdc482cbf4b4db50225f0f397eab9b5290b80b1de208f454bb91862e6056c015cd3cf0c5b300e03c30cba99f2c077fbca0cb699454771303d87ddbb1471ee73698a54003dee6495818f106cc2145dc36403327f74d694c2f94dfb2daaee027e5260c23fe7d588a9a0ce8952d2a6e4d5a507e0abe84de92c34056182e67fe8d2536059f55be02318510b0dbdfeb59e213b07686164ff79d806b3354acf8e2c1343b4c894a56ad9911759256c9dba3975550683fdbdbdd56e44d181c675cc192901fe09135dccaadb4ed8df6354658a023c0595fe83364b993a246179620aad19fc50f5de39d1907031143c48706b75cf3e082f2294558d89001c15f618a76c72947717ce8bf433485dcb298bd0dcdb3f0b0b546f312a5e3cc4c5d220b4006e0043338e9f660ecb59a5642e0b03461706510b0f6f127fe2e1c1bba2a544f955bb3fe4046133e12c8906a007b177e282e20f0b5a5e1a63fa03110c823284370a36b817875911f38a29d5b4d457a37c80136b08e60fc9c934e31f06fcfd5d543234ebdd6e322d5190fe0222a868128b54787c0b15e366f56fbeaee2d3cfd732c155a7bc62edc0dfd8e55709ae6906fc979c7e01f58c2df7a2599da9334e4a7af08f01702250a6fedb94e00a24503ba9035068074400bd4fd50b929ec6e82b0cfc0059d3fcff52120518bb1b303e0b7d02cb6b0391c5ac2b6424bcca2d675de6593afcf0626d3bfa5b32910cc7747d642a6808015c2151a4f55073be936d174b4707e3c9432ba8167bba182d6e212dc3ae958103012995cb62823e7cd16f3ddc69d47c50acc91145a92c97cbf85f1743bf5ff20f8d0b4afcf5535714f646aa72d9df3ad68949c45a2c3d5376ecd994af943fea09b9c2e53bcad6d593ffcb2af88bcc506430990432f5678a5a492e99f24c2f0901a240022e3effb15abae8819b4c43abe7a4e0297a59de84de994a1e9353072b0fda39948dcd2caae2606f7918d2ca2b54e0822eadc43ae6207ee2220a908972088611ab66e0cc228f1fd9214fc08cec4f9c30acb43e7ad1efe0d977603ac0490e2366104b58da04b21118b9b7b8d749a87ce09ebcc08702b2f6938a8d982ec208797c2f25b443860f2a8d7478f0af6686227170d6867a7ab9409f7fc6d6f5d00eed9662f71295d3fc7d1fe9cf1898409c0680745b9ce2ea86296d39f542b98104125bb32062bef3ae85734d336bbff98394a8986bbe58412d829bfc19e3ea3d0a5bedf8bd98e1b5e393974fa97ef86e4789c17445595784badbb7d2436e23190cf886ac79884931a6fa211fe0403a59b392246992247a0dd01bf9883c5b05ce0e745fbc04950e5956f383f256f02b2355017450651b90449efb12b54ccf01be0facf9b6ae1bfe41a645c8f03594a2a7bf00eab17ab97083a6ba9196ee1cf0050c98fdb7ec8cdb66d0e998eeee561a92538e2f7212897c2660b7b4ad0b7d625c0428c6d3a9e206b6b5879a64bb250535a3e3095b8a963cff9323420e275c8fc5036499633f91b5ecd9b733705f582e00062b00ae73f7e779f0740d599228ebf407e6a62e55627df0940f5b5877d3dda149a6414a4d115a35519b0eda04d9b20c03224d90b26eb014c2e121997695c5cbb77b2a53502396e62fb1450e0430bea400c8e336845a7101d853f41e9766e2d9eb5d9b3270bcf659d59f194946baa49602d1f252c76939438f8ddf434b04089d6fd2053131266b2ce8893b101a8c01be019d179f19d3d682ffdb8c635d10c717dad9801b3d8b574ac7e215544310e91e008a9b844d49d9177f8fff32cbfd1c440476fdbcf479f91c787743bbb985dc9508ae6096fe6c256448b3a726d97f2afcddc8dcfd677761a9a9e1c09b10c54713037cdfd38b4f85c84580153f3357c07d7a78c19951e63167bce99c692ac6c96701cc82214a64da261b7bbce4fd553f9476aef47ca97f70127fa49d49ed3138040416223e6a72427e1f8d03644bfd12753d0aa76c5656b0c169698acdbab6cfee01aaef743ef255c4c54bb200679f2706c1aacaff2c38a3a9169bcd986af5823d02ccb76748fd5499b7ed8cd2df93b3498ab3ffea98c46a457b06c562b4acbf0508cea81b899cf7bc41d4003799d0a48bd6b55b5d327c1523ed4d289a87cc4f20045eb8cb6549c30498584a979c4097ada465eb9e4ca1d66c445af6e693fcc078072852b4c883d4ab9de26baa30b00d388a1b4533d336b6c9c466d8022cdca0140988abad4b0980fa93010ff27616d3dea56a5f540d71bfd81504a8986f825cfb06ee66bff3adfaa5453b8cfaa6072c4bedf853fb77616103e3d0cb47385aa7ed04bce61fc68857f8237ba958ffc768a09e01a8e9592704a282db7d632e944b1b06e51d9fd7609885d6fb1c2b7abda827a165b48b024ef02c174f58cc29fed34b09b6f96307350c9ab4ca33429c510521f9cb5e51b36bab079d5b4447179bfd6101c92046688a236bf1e7798dd2952ee18837412c7bef93491d5c672534f3e02f0944d3aba49394e0efa523f4d8b2a3c3107eaf4af501ac873cae321530c105f404e41450a60c473297b87ee2be12a51dd91ff9cc170b94869e58c6cea79d003603e7d762a5ab0b3703d4b383eff0d64a49460c51241ab61446df1c5c3ba26fb302062b9496b092aa6ff60c8db23d5922f83fdf540a5b905374ef59da3d84bf8f0f18f1668596e7797deecdebd8426e74f6fd002dcc8b5223cf44d6bbde992acd05978ebd0cd48039c07c5f1de96b9977b37dde313e309eaa6cfad70eb664709d011080af89d33eebed29ec9e4b603bed724ffd658e23c6c9a97bfbbc8597091e03fb1c815a41671df4169663d64c838622c26dae3b9270eb2b6e7845867678de0dd43add0ab5aada4c636548732f95318b37d25f1fd73569d4b9359951c781fe097931899dc46f83942e2d59f7dee33d72cd07686c7278332a6701b102c0957104f687b7a531e785fd691095dd0c37a67643c2c31e8a8c36daee9082a02accd5033d0f5e44fa72867aaaad34f7d8c90f0fcae6f0f3489c798db614414506d0f4029d26b94383546ca456ed2afebe6a646c9102e1439bb2fc62e8c1881bb7f9ab0ffd8dc727dc640ff2e289de780dba298dc99ff0ab3c7eb2c16421beb67015940e99fc0ccedfaa196e5f7ee378110979e124b8050d07826fdc723feae458a62706fda2c2dd41a83b02f5c0110b9ec99175a89687bb291efb51df102c9e9df5640e13bc50aa097467f28c6de314075bed69c68e5693b55817cbf902ec6fab68c506fa34f2faff9aec2c57877d7706431ab1ce8a24fb517bc2c38f2c204db8c8130aa744fc3b103ad2eab62e6c7a9f7509d38d50a8f1ec165a8f4e34deba1d6d4f0f51ad35eec8a37b695fe7afca3ee5ca0518cb2b00e0223416584d0b2ec50da50926bac0c81949324f1416aa571cc18170f1f6f29dfd1d4409a6d6e609250ee60940248619d8e0beaac465b6b7c0009bf1ac54220ab2aa05afdd8e9fac57774206d8b3e2daaea7bb89f18d7d73054b3e127edc50fb787c186a7d0389808fe76b0c188b6d842ecd206581f27c4d5916adf4ea54662bcca0feb7ec32b3d5ee189d099ddba3b3968d212f33a7be7072d9d31b151cc20b455702c515f88783bb2ad60e4b1e6378cdc2ccf43a1f93fb5a159d7487ccf5a92c8840f8868984a2ae0b4b0fea2e5f38050239d2d30f5fbc94bc1cda52eddb139ae146accf604d15d8ff9d07ae1f45a766a02a7599810e1598240d46561543581fc919fb8826bfe528a63302a28c3f9d7202522bf2ff7fe3fff1916b000582ecb04a51e9a243a68cac62090c405a02573ed3f799b1715b967b057f8eaf9cd8754cddf0b6a5998920f87df50675fd77fb62a5b62499959f5eaffea9f788c8e16479cb36944597ce5050ce750fda2699d86395baf9b13ca5cbfd9b30db278153166c133a30f87b35db1bba65030e59ddfd85506226ea5198eca52eb6bafdd371b0221129fb757086304978ab0fcf9b6fc4ee8e18c931347e73946d62de1ad16c0da9ba964e9a8068a0066fa201485c706291b0b29dde1afd06a1b29c3107854e6e81639d25d6a03d61fb12b90776d8d5a58abdf8428db6a4fe279d0de69afe1e9b1832c2bffffd76f61182d30ea7c55e313b206c6f31bfa843edf515b2e822d48af55ac5060e711ad08028030c32b517b73ddfd3f639ee3b4559f3f154228eb11e6fb908550e2d30318fa3dc02175bd7bd2143826d695b9b226d926f2e424ea9dc210a740f107198cc2b6e920d299af685c2eb899a6c41dc1b24be3faa07119f312a27bdbfcb90f649b6f2490e3f81f3787402cd53dbe6f79be00d0485b2ee94bade037a53af6e664fc4e1dc06fb1f6d14de94fbc9e49632e790c047926a1e80cd507437fee60f998aff452409eeeb22359a519526dcef52b970a7e3ba7c9c9fd9423e6234da49e79d1d5f510041de22287070118b9d7d4b5bf4285c9af2fdd7bfa3fad891c8b176c861ae2b0ff3de96d550c373c8f6d06d80be3b9d59bb67fc0701390da0f346257e32c40309a217de45860e1e4ca76d575ebdc33066d372d991bb25428def2ed03cdc08f70bf350884cf037e5c2f3b7e6aa69e490ea53512485584b6e1ae386499772708d0ed4be823e0713c8b6fb9147c4eb0b15db32638838c47716dfd1ea98f7f35c660731806ce6f0e7acc629f7a8865c32d9f24a89dbd4f29c911ce88b390deeb3b3050ec69ebd137fad32282515bc74e8337c0fe1d0b0c692fa2a69dcb222bd0f5f001331112f82f12ef708b81fb5529f1ba49cb5783100ca67418a4444be02c2710c869d6535917e8f9235b14bf50e63221356ccb9315fb500b2ed695479ab60ab016e88d9d7e6d2e073497268e2fb04c5bbba99ce351d3ec871db18dd13fdd4f0017f48ee6f0037387e9951173ad58cf76dce65a155f0732c85a1510132e87d750c -generate_ring_signature 25dd893cae76d6ac1932f11696af4d23ced5c7f8c9b26c443513e7460ec16ecd 5d9938750d6d01d3cae39ec888e440f33fec671feccd0a5bcb83a0bdfda37022 6 c29716ea74a2eee8270fcfaab52d6eb58c02084e6c0ee1efece22d8ff5ac9ea1 85179c988425643a848188ae78fbb0c39eb912d731fd789ba04016020e904dd7 5dbf0ab5804b5052e98871ef45eb1d79fc94177d6cfde3574f118c0418a84aca 3053056e01b44a45d9d4137e3fc8cf10b86865dfce0f9773fdbb459b0b305c41 e35ae435c1147ab31281c1a79c2d3a31678be7e87050d041c61aab1dca9b74e4 e451cf5197c665cf0ceb5a9d7b93d139051f56e11bc31ef258f56c9023b94d80 c53355e6c89359655240817ebebc88e2a7c039a53e718bd076dadbe8b166fa0b 4 1dbb453e5b453701c3338eef3ef68db6ee035086bfc8410913627dd0c7d3720a1df862e5c6f6a8f6d12b125f95c0012c088b040dcac9cb7407a105fcac15e003d7d642a147325243afaa4c2bcebfcf6fd1ca10e87a0462b5d6db60fc76025e01760602341861b338a0ef7cb7af886b34c7b9d4182663afdc031d38851ec783058045c1e0ab105ddac7f3ad790b91df1d409b53f098db27402cafa868f9f4a302071196df3342bb5fd7c0021b880d1990f688cb6ee09a118c20b06970dfc8920c0bc04c1a4555272699e5ce5555b5794a6ef0faaccee5f0108fb98231a754f4000995148ab1927c15b1f43a4d0a4cf8191a6bfce7611ba96ef87453bc025fd40edd0d0aad42d9e7391bd225e5d7b2b69f77650d2e33dcf188545feb85fe9bf90b48f34188ee9d40d85ecaa125e82a959d77e301758786267f2fd133f0d6b88d0188f16c7204fc6670552455198feb84383067b8fc201d66e494d8732c9572a40b5d9028b2638c0eb3104747b6ab09a65e174b1ef8c7f8d83e465dd2fc0c53be05 -generate_ring_signature 2f1a0d247c8827dba4d4ce33f142f157e2f1767529fc7c19c789f00cf6ffd688 3374f89c3f99dad856ba5ce07e7c38dfeec5b312427bb33c8e1b8c372c6f61e8 15 41a94a5d8abcebc4c87b58bd560596b715bd33edb75aa248955b5e4602960ffe d30add0397722f8cd6238bbc36ef02a1351b0bfd5a462151a57cb156e477a909 8ab0a8764b7ce7c3c10a7e6d201732c6ff4fa5ccc9e2c6e25256db8f88c19467 c12136781dc3aa8a1dfe8fa588c768f3f5b73acda82626cb380ebc0e42a3edce 571d28f9270ca75dabb42b076a8f7febfdecdc966b0c106c4541cb6820380f2c c9e639cb8b24ec739970c8f7d87bea123690fd4f1edd394cac1fe570109db396 84321e4fe650871a21b17061c6581701f5acddb389d49e4cfb8a65bf37fadf7c cbbc7e4105af837cc47494fc5b5e78735a33190f1dce55e2657d2d538ae6fe95 d8d75ba6364a1ef06d0dcda3ab45bb2c245d7abd7dca3574a5f48c3f5c10f3cd 74effe798cf28847c01bf6276a0c24f5be7e6c52a3968b7df9b8545a692bd9d8 72740510584e7b51328029f4bc32a40c715d892cb00f70fe60f7912cd7b7ccfb 518813c1abe65ebdf8bfa96b139e171b30da00b3619fc258770bb316410d8488 bfe01ebd9d90edab131dffccbdb58cacd599d5f0a3fab24690c7e932f2f06835 9d8fa00ff4874b2f6cd130a224a2d2df214f27977224132d1d84a5ea13a85493 d780603fca86efa77fe4d7a9d35617b56e6b1f1cbae974bc5ca9ab4b37f94388 783590cb03f14936ac520b8b90b9a59ee656aa1a35a60774c23c0b120e0ad40a 12 9158ccc2d472e28a8d2f1e6ed3b62b39a91524d2f6520accefcc6cec8aca100cdc94b436ed91c91834beb2a6ee4a53e9713ff9921a09779a4f45785101684509e487c496f8c1e22a514a9465c401dbc71489cace0b22da0adbd67e51547c7303535d60b04e6ba6ae10dba5f8aff7abfd7609862b0f0d2bb80cc0a5ee7f15340658f3c13fde8dcbdf4eeca60ffd70ef863b1b6d58a886a6e38619b211943bf00fd45592662ba19ccd03a07816c46e81ce065bdcb3e3fe812f6800d285f6fc5f00100afd0dfdf73ac374ce5e947b3554166fb4cb29b109e9ae9a11c7cf2588180273309c202317a6be74cf8a7fa81222889828df87e6e925f5a072e033225f410532e10dbc765cdf29629228ee600b1651dc2feae0689182015be8b9031c3e6902cbf363a740f5642f15b45af5bbfb6f0678ae2ef1bdf9e2dbdb9fa9346382fa03ccca7828983bcbcff1304b5f18614fe5ab4d4b2f19f54fcf3750692b64045c092e0b11e9a4eeea6e81fc1e1c5ccb1d092bcee755ec785d9620e4f9fd05432a0128d9faeb2620045995d1dd25d4c20ca3ee5a47a80a5b2478e8863f26a3f4d303b9473b92b277d99ee47366593632b30fa7e9540ba2fc4006b9156ff826d89609238e76ac26af11f00bd2ffe82fba9bd21449cc859e7bb526074459638a4d81013a879b56024fc37b68e86d9971aef4eb21d7d58c31e0ff1008005c01853e1104852255cd2ce65b29b1970e1d7c111cc6ad347931f75951c7751c5a89f43ed5040bf8d430488768766be16f689d306d4ebbb432c2bb69c99a067d34038d43520a9276cbfe1019c9e467cca3c690919d223bcd236ef486ff913cd0b1a4c9f0f60922f1977788912619cdc1dbd4a87a1023103a4db40b4c59c2fa3f28582457fb062d79407e3249dd62667d8168b16c9639acbde74d28e35b3e2ebb834776c4be00c3c78ddcf13afb883a6423595702700557b0fe2586bff9114e5fb5401eaecf0b3270c59b8ba28ce9f2974c052371041086564a4938bec60051695327069df502cf9f127d98fd41febb2277740b83b006602a412a80a8e3503d88b696911ff102bae8b249d3fcbd6eea881e70a30ffe99b9986404e62a0279bc64a0f8c078de0ce559db4a0d6ade50912ff586546fadc349c5bee6e1e656508601bde2cb84c80c79854968d21818c1eee82a6fffa31a4b76d294e7c0673200aeb87d3becce6f03a289ed0c99c378151119e7d66b9b4f268a73a39fad0f56f06c8be76d3333f5011ead9d8963a0d0bf96156bc09288e710c3d592981937a61eff462bed4cbdf802943de30091af3c556b6c74c0c64ec6aaf7b99c38fb3f4300ef3283ccff7a4106 -generate_ring_signature da547499dc76464e031e5ff7ee251b23e86a65e7ef4a702f49430dbc1a3224cb 33d203426218d7f5ba88cfe70ee0255b22f15cd23d1e7fe94e02fb4d7a835de4 1 86b01d9ccfb40a5020a4d522c02855924147e974f4909a1f15afed38ffdea8c5 55adae1b399bf6a36b775e369894601a26a5395e5092e93f1717e75dbf74f60c 0 c7beb3e7a8955c8e95061f6f4797d35dc859774c02a788b58f39cd67bed09d0712860b14824dacaeebaffe7fd9acedeceee60eb353d0a327deefa4598663010e -generate_ring_signature e7e69c7adb279f38f2e2f05051fd992a11149a8998d1369be08f3dc277fae761 85bbeb775e94cc97da84eb667dbc70a26e60a5276454ef41a97e0aabfb8c2e2f 56 18d09a4f9592883fcd32ff6593cec77b9f973837f03d16996ca2cc33651992c6 974af2b4ef03f64b3051a9a18ee09ebe5ee860ade199631490bbdf6d8de269af 0f9b58c9eb9bb3211deb53ef5b997c8aa68ebb9357ae4b827e6042248e07525d 582bcbfbd1c46085fc27008c081b8c3190d8295ba3ee29a28a811c8dc7ddf87b 46e691e35acd711297c564b251cb5b279d11936e67afe44f321a83e7b2362157 254a0add41690908429a4349aa13813cb4b18b08659017515816181971d80324 97f30a5f6836526a6cff058d570b292595e8b056f740b325334712e9c197b8e0 5848a77ebae6530b5cbb1d1f479109604dd9491182c4f4537f3d857b160f63fe fecb6d36c025378dd87880402a8ea775376954bad2bdd78ba5614c793c0f9c8c 172a5304befb1ceefbcc22bf34479c924f29afd264a3ac3b426cbef3df1e1903 7bdbe1fa6447623cc54d957d4870e2be682aabc434db57e31db09b1472774c85 394ef3d1104938454158def95ef99ce4614a373235912fad5dc6bea68e9d8b41 23e1141711a551670210262fa8a59271dce5a05939d906a5d3cd7ec5277c1da5 969dde51d1fac502a370b8bf4f40d13824fc919670d4671b402a294b6a89bb08 0509b587aa1566227473c0867249948630965f4f94a6b024dcc64faab4cd0e66 7e0462bb2cb462c7af329b6bcc6b9664a2643cd6e360cfd14bf7eb53544c999e 738ec5605320e83f6fd439853de054cf99d9b320916ca3c26c53b914e6aa490d c1613e126399c42bae1c4750d814dd533c406db959abdd275b3d9c3e72984c24 d588bbe6c2c2744068758a5d6e259d1cf02473af17a1368e62932688eb7764c1 26d840d38fa305151e8873b14a441f42c864d873ffd7aa2c7c278109a446bdb8 66ff84c63ae19639ccacc7b493892f8f26ceeb3dd6d8f063859449da9d5b13ff 828787793865161872024f6ba77dba847f0873be2562afd85a64bfa0f453adb7 1874b21b0a18b6b43318736c3b53d871bc491695f5d86c8811ad8b2aa8464648 8f1df6bc7f2a2ca7a7d00f475fa487409a0f4155d2bcb2db1e9b007cd9368b83 40b39d643696c1517209c946e7e797b4c1cee0b339464c9e8b13ae28d9434afe b3621ef2364d2a8a9f38aabb9b624b932300046569e9fb026d36c7e73a3e8fc4 3115f7a4b63d229c5182da0a3f624de6c7e2b50e13a7da14a322f0de69595418 7146996c40e7e9570671539d471235c7baef7387ba12f1fc06602fc973a3ffa8 ddae2cda3fa2d25bb4a412f4aedcdd206274bcdbae18a3d6b4c25d978f8f5dfa 124c68d03ba3fd815102f4ec1e16016e6335ed47d9637771c426c49dfd87b1b7 19a1d656e101999c3b1457e995d8a0977c18bed779deb21ef832e7795d4d9044 b69f73ce720530c3fa94f7f6f0b1c6c8e5466a89b97be80739187695970f6871 ffebe2f897378da204d5e8338caf629744fe910569166768481b76d0934e5e0d cfcdf78c7fe82f7fd14a6766d4cf608d6eb92af30bf11ce79348bb2422c1ae8e 7af0a5c4137717bee0db0faf10813515b700be3b04d639490226b047a32b660c 8e824733a807d4fdb63c7621b6838f70054b9a069c3e8b8e36c7b7a2fde41de4 b51e86c2ca33303c307c9d1080f5066ffd47b67dd1a1a368d4bfe5def1fdcad4 f271285616306d959f3ee158276b7c35364191836ceabae3d5f532d1c2f6cb0b 0634672a39f8f196a86e8afd3028b1ee852026e53b57ab5dbfb36024bb3801bb 84b77938a69db65b4f705507f9a052aea980fb5827062570470bf1674772cbf4 b3f1ae595027df180e6d1ec987e7d4faaa5832d72c759f6c4b33fbec5c197248 b3aa197ab9bc1411a0088271908462c745ad08ba0b6bc52b82300760d1edf3e2 764478333c006629e5be2ede6050b6a9e19f84bfcc9af0cb606bca2a7fd6ff57 42ede624cf6b0648b2219d84e2800f86295b45b2d62c400911936b00d23e8eec c6d188b2593f276c9d8af38786e8dc83fc3f4888357576e0ce1f817c6f4ed0e7 e54f8c15e52301951fae217b400db2f8a42aa824f182bd97e979d38cc6beac71 815eb621be2111a52233f936689d47fa18695b52431435cf969ea1521abfca97 c39db1d00d3dfb57a9e61d8921569686b0cbe20e69915cd65c9530c8bc7840c2 88841ef6d698114e8a73b56a437acaf9dff3fae7edbf85394ceea8d0a0abac7d a10006d29887038ae2678d8ccf83cc66f6e30cea14f3776dbf3b1064b572680f 867185b15807549d9248eec40382c0c2efdddd0b6c18a884ab811bf0ba074dcb e2408a5abedc5ca9c397b10df9eb1d4f327fd875932544769b9b6ba124131669 390f5c7be19c6f50c0645efd3d416b1c740d38d7f95fc6983f5297921026518c 5368d661c76b57e15f8e4cf676aa0c8179bad2cf815856a97ad48db1deca7db0 87494de9d4062f589bd263b6fc826dd85c6b81b66583d6abb6fb6cd5954dd296 a353830ac2b086e36537b57320da789652df3df9b6bbe6f963e9cd6941758aef a0d9ff166385807b7eb58ccbd283e08e6b2194e0dc82ef190b3dcb15bcb3160d 15 764f5fcbf629811d9ed1e4ceaa5f4f129ff85accf72b8bda8a585af5a2c1ff0b6b054111ba27a1f19ba4138dd8169457469af0cdde9391e645ca3b735894f005edfeeda1a89e2c4c800abcd66b66681cdf91fd76ab39ee14a92a8c7f0548f30ff0c48e0bfe296559331c74630e563e53775fc9e52c7393b98e122f88665cfb0879db3561bd35ab60355ea5564fa01f4d589243d88b7e84e77e4849117f535f0c27823cba793eda431937a41e267446d804001d14ce7c69038900baa8cab6df0058b392282e1fd7edd2eab20d02c925cf56a9138336cb94665e28515cc22df80a5c5ec49ddef579e850de1020c62b4bfc1515a7b0ada8e4aef523f28f13570f0feda6729cf321e81f0ed00e4aad6f114b3fb3b9ac1e05af44e00955b5f4838c0cae79ad0b788a8f283442f945e6abd9f4d6ec9c55aef1d98eecc58caa1f52c00a332552899fdb8f762008bdd8d592cb07847e2af86f3abe351410534c64e96d01e2194630439fd460cd32cc3f36f65e86ece252590323261c38e771a0f5df71073d353893311c86f57b8dc7140aff5cec30fa1a154b8fbddd1412838bc127140d9b63853ccd43609d3e1a403c3bb84f4ce4a878b5ccaa981c860d3c3c9d47a3086830224470e96cc046c29716e7a65f47687c9ab7a357a8491c8d4e4c2fb65a0cbadce0d901ae03a007a4a4cf682a82a044194d5c5c0337828e4a7fde5e9b6b0bb98c155e2f39a3e3137126fa8ce9333083377ec73a510b02f8ead78c8f6c7f0ce3190ff2c3622f770edc4ead8ac109f038d44cb9311cab30095a33e893333406b8a304e0ae4f29be19a320b7d796e147f052b381f0579a6bbcf2a4698e08290013b8dc95cc284975dd341c3f319104228625438dd2a9153af41df0228de00c0751babc5add668d813882829fb81dea9ab078a265f4f59bc421ccc9983e66d6016285d5a21ef8c3856a65d2fde1225cf4e9149911fa10cc95805c94c5b7bf1006d9697854d8ee4ae18245be716527e327fe53a23938d33466d4ba2e4bddd401067907bb3dc006181c4218c1ebf15b74acc30dbd8f800d4c3dfb70b32591426d09df03f44f9a4381f70793b23b558c24e029f61d176a25c49657af21c8bc2f450ebd76778d65a482c912a829f786a6fd013c2c61fce6821f37d231154db25d540c14d5405aa7c44070336cb77b759868cc41deb4a10f0289c774d819030c2710023045ddfcbe3a8e85465fce2be8ae4411356e178d2ed0d1e1d2635374bb59cd02cad60751f623708e7be3ba8efdc0f98b4efb514a26815de1962a218ceba902003421686cdf056e64f90bad2ec4a177b148237fb2aaff04cbe38d9588b1abd9048f345a738d41ddd94022d61ff43e9a0c338a596ead2405b4267ed017f174da0baf8668aadf6e3d9c46f7701397795ce9f11f61acc8b1e740e3fd468e8ed22e0d0bb1052c48fbdb9deb37dd2dc8fa3c2ea0ffd41cfec958e66d6eb20d49a95a0f75b00635e7f96b1b4e1639469acca8af01b924549fd91d7d63efb7693beb14010cab2b472511c90c1cf3e9f028e63a4f9abb14ac117e0646adf8a99db782820cc3a5330a39979cd7d37fec6e8ce55570f862a4dec38d0fa538310a1c94cc56085ef7fa5e6bf6c8855db7147ffa29118a9513548f91c773350aef576e035d5506cd1c158709012390a9dd277f346732de4c2fd6e07f00e9d694d1cb3feb864f07c62a65cbc983b2767a16dd1c9750073faff9d89fedc00e1a81ab55931398ff0ccb8f9b664872706aafc2daa31f7de513aa1cca67b7df1108a027a41c7e80d302a8c229b2adb12e8441ed7b08b55301c34a9b7b8b87fd5ea2aa9759ffa372170c8e20039b8bb541b48cc34dfda93eb75211be2d7f8a0e3feab0ea9eeb1901930a3fa974bb0f22ca70df617a1fcb779e74b346ac08e18342fcfa7a46e78be043052efd3d4b33669dc59819f67978b51a68b5951d3d10a3a60d71e600bab92bfa06921f565c86f38c7e3d3c359c8d7a8709a320948d0b56568509c56fc0d6c841037c67b6011b7fc69a1e8cd90abbe04fed04e1e12e975baadf3fd822e869b95f0d98fb9214f10915985bb805fa4ea72e0e19992c16f928aa3bbb0fe31a59af080224ae8f451642d5ec00fa7923dcd5b834f28d6ad1faaaa934899da3ea8577c309d1c17b50a81b37cc36ed2578950ae9ef395d87263a1c0814a91008ab7ee2ea0910022af65c5d48942efcf5dbffd00aba132290d2488e45dba7044cfb490f3d0898dc16e4e66abfcb3b2134ed597c038664b058e975da030e51c5a0ceb0f04e01d5a39c68644f7add4a3d7848761bb19cdf0f15710ff473eaab93842c7eacf90c73d30274f820d76463daca841e5fa10b290ada07ee23f8e1296398d5fb44bf0a33990c969708a0441f33ef0a2a6b7d1a03f84fb922006f7df8f13e7ab284c50302b7ff4cce84662b038f824d97dff93f9035ab8e5820fc4c350d66d340d3670599cc54b25024ecbd19196095b1fb5ff173c51c95f9e0208e9f5725d07bfe3e0f3f5469a7c3cac1918b8479ac0a4d9dbd19bb35f54a71e4c6bd3f6dde52d71b0cb26c56d1e3fac1e2fbe60f7f462e5e462b4acbe9b0325b07a8dfcf6fa1c2f80bd5698aa3d4f75431da2d2d2b734a32db55e5c14abe4dc91a7157e2864c39d70d88b45da3e129ba961e37fbab534d3e66f1c98d307453dee55b6a70718bec020364cc645111834de950943063a26978891a6ef015a56945aed6101bfb9e6e920f8869dd677f3a56239b34e61ac27adc82c46e24898824bb7cfb8c5a7e57fb2d0130f9f19f1c2ee09e62895359e87288c2997c6c6c40a934b1658b3b288fea9a08abbb3020a289d4457962d21d82f8e9f1d6d463eacd1137d6c8c654db124fd30a91afe1be9c5c9e568b320d21fa6480df835af06375aa1f4f6715c499d7d55009e349049fd500fe0786aa1e1657f16cdfce0bfad5525f9d355d4f790a3f36d9095216b9571c5240b571eb16b0ff9f40aaa49dd7683190070ec8e87589d3b2820aa13fc7beaa89b59d35ea92527947698d20279f8c653540cebf1f1ae3b581c701ff9963fed44b640d17151a9b80b15d778f5f77687ca16545c15f17a964698d06b0052e6e9d45cf3868233b7891dd8f011e50567a3cfc8db0ff0ff7f0941f3c07f571f28e53c19f580336fdd8cb6bcd4604d8dda25a95e4b5645b7a9772caf6024bd563f1fc7add6fe79cf861dba8e9825290ca5088083f32f3bc1c1cfb0e6f0898452301b847f4c823611f411702f759e127d49ebe27cc8deabab318e6e913076476d255a53682510e0e9a5ec2958d9fddf913e0696a22c26cdaf0aa06aebe0786dd8a412fa44f71d4c4f68e8bdecb17cad9dec5466d488625d6ecb49628dd076420e531e0cfa87d8b3f58d59f15878e61906216ffb4f9998edd44f86ff492064773893049c777a6417c7ca3a527e54f1b3e182fb8448d967786a57543082f046edc950791b7d9a861dc5a277c82d05809c9e426d7c1ba703403e2d2706fb204d0bc7a1fb89225bad1d781868235e2a476aec7068634aa8bb67eb5bbda66fe0895436232b3ace24c5342cf7892293dce55a990c8727b17e897bead7d1ff0a60c1f3591f9be92c1ffbc2723e1fb64d043c9414409ac8920d7068e6781245bb306a9a8abe0f8d61a8f6bcc9ae7ea8fabee336c9bce02a85b3d0aeb20688a2606092e60e8ea42e2549a09ce10e35afaeb2a895deb135fb33869e08d33c4fd52300ab03934befd5ec88564e85daf1d2fcb56380235f5e51ec3d87eb0f2ea310a4b083465e36d61f7ca0857cad896bf9c8f7af34498bc5e4ab60d3ce0bf0c8ddd2e0baf7eac95f6cdc8f777839e49765beedddafccae6ceafdd896b4d9e89ab2ee4067291d4a11ee48e0f58f194676141496a096744de5f49a1b3d737f94b71c6d102899d17ca8b17b3a0db2759f57537ffa04fda039bf327f78f9daa8de1343a6405a5d59a336002fa749f681644e90328cbb4db39d97fda61f173ed3cbc9c0410057174b81f73b27320cca71d90334229887739685a437369654cbf41173b1d610eb58eddbd25b42a08dbf00863f2677230989035163411f97c70e91723a6b6900feb80e4eb0a63fc662b24f8ddf4d33e388b183f791db8d81e8e27973905d61e0aa2c58fb5b47c297b98fc613d6713875dabf1894258e3da42cdc65137f6633f04608da79df99633d5c392083f2aed113a3b88d8533402a60b8476af8c925df4064a8fa0da44c5c2804425fcd570738456114407175eb712fe6218b4401bb3c9074ce193daf711cd794de612e1a62dea386cc12ef45a7cf15bdf91785012610300eb1032d77db0a4640267e62e6ab433cd43baf521dbc0812912eebd20b4f715061b56e8841bdf1eeb3ff44cc2cfaf5449ab94dac5d27995415974ab21ceec3206e4bf6d191ca610fbb0add46a302b245a07b58fbb5a329847d5050f38ba2bcd056d0510d438d6157506bba26177d853765f57d7407824eea2bb5f03d2acceab0a06cf9b5fd5ccadef14896e12d74bb4225e8b943344a44097b02f2529de6abc0e24145aeaea0d4274c1987663eeb3ef746aa0ee1887919acd175b1f26f876e90bf28e5a647318394508d41a13808f4f9c0cad50c739dd633d5e52852d3c2b710298de5aeaba96e903c64df1b40e50cf13ccab459e821add173a4c84fc9b325d0504c57eefc06fea70696eafead4c964ffa616cfdf673ee8202a8e606530fbf60da9625a431102ce91934de93985de674d45480a7172f3263e596e1389765845036e83ae3f65958d4db1464bf2da3d1b410deaa61d3cbcd4c7ad70034656a7ed00f0ec09fac7ad07283e88935b55a5b230802e3dc13ca6c8b543aeafbe6af62d0a8b49ae2d1946638ec96293943ce7152a5c18a3e4c675f01cb1e8e10247c1d70d4ee992e8bd8890371ff53f424f49786a3bb82201c25f2b6dcc33c574686f2d0d6f826f8fc59133b56a3529b1f1cfab95c31d02190047e97e3d566dbe122a180510c5203984a916d97f49e18b2767435cdda5b822dabfb72d5d23f1f5c7b09c01 -generate_ring_signature 8d502bf01a32e567707adbea7c5b07946c8a91ed1231dabbb363bb6c475debe8 797d78be1bd8f1fa9f586e9710621bd3d480427b45588fdfe8b845b12414974e 1 bf227dd9be0cb17b4cafdeae98527a2bd5f22d56f4a4449af561a5c7e9979411 c1d63edbfe8e5ccc53d5c54f571718b81448166bc241c68f09aa1fc0f337d60e 0 3431aaf62aebf00ff389f3f2dec9f3e61edc48de2c8a9e70e78399deb7d4910ed463c56c0c03a6a8609a3f6c6819c45645da18bdfb0e1b54a930bb3e44d6660d -generate_ring_signature a360d6c579fda6e77d9d8f7c5670c5a3e56dd67f9bbeb9a2a51ef4fc846c9e4c efe52ab1e05aa48518f70282135dbffc35e61066c0ff77f69dca8f4f77cf26a7 2 5f4e6fe57d228860581b1597387794778018d9508ae293cacef275f3f380f949 7f855f04ad22ae8e65f0dd48b9140295e23165c1bdc44c2e9caac8f7a4d47690 742392bd543557e97a5c79270150105e0e05099b7db61d2f07f2a4ae1b0bf301 1 43a07da3b3c8ba581efac13b4a9f6f7b291c98e19c0173f60b50c57c7b88ba0e575c41be6b3d499ecf03ba6acac4b2ffbba6f8ef2f16c5be9cb8a3d0ebbd3b0f27fa9399fc0a8204d9615116a9eba8b93e2058e07165c03e51072f6ce5bdd3067aa8852c87bd4e4c0e868b37b9d3370b79c2563e99828e6792cfd8c40bf40b04 -generate_ring_signature f06ada276b4cb15bfe0baf1973b4159c4ae03306b3d1cb6624410d84ff75ed8c 33f1bb96ac7545b8d506cf2332fb5c1221d7833e8f1a58c7e72e7ea0c3a85f44 1 59ebef8302f5943bdd11879b6b4e3bcf098eb70a56e44871a3e6097b2a38f682 11e1c3f3d5d9187ead09f8679cb2172afc020766ce0de7ae29998b7af9895b01 0 0277772991037345e5a55766893e55b96deebc313f09ae546bd4ce693f71ff032ab8fb6f2e858f23fa66daff911dc8add3d672a5ae14930736c693079c5ab10a -generate_ring_signature 4bf0339d9f8d053c5508af5620f121860191aefa336297ae61c07ab16e830e95 4b74cb953354a6d552dd2093fe6547018f2ee804b950c00ae2cd959d9edffa02 249 b273d48608383a643e8f4fc7a3218d79135807e1f01bafdfd131a6ebb066f53f 13a87abbe6ebc9c0a6a758e2bee754d629c0f0e3c9a8c922f2d6b84ed1d09563 41368a4bca9b547d015b4c88063bc4c61bcda812a31c5fa1380cb9917d91bfe2 7adc855900644fe40f6fcad939d2ef3d2ad64c78c67081eafd47aa014fbcf569 327c96fa149e38b3e54f28f3462c9e66df4c74a6ba7957eb9fc52a91fdaf7dad 421af8feeb282c03a8fb30a7837837641c153fbd43a8331acc28b1c4a9e86bf0 61281e80c633170d98119dfb0fdb35405f8ad76b0f0d867185ff43116abe96f4 2e25524e65bfc6c97f57a92766d15c394e46386ec49d2749df2d45bf52daf1b8 47ee6c2a1c29f513e09d89f74fa7b86a39fc4468cb648f74e0fbc1e48d8f6d98 09e04617608530d57ace72efad388ae1ae3947c0466c5b430b9dbf351f392867 ddbf7d3d8bf539aee1727a3eda3737f18ab63ffdf80651c5597e10fe6676ec56 135947fdaa00c8fef0bee2b1a8e843746094317d6feaca3c12b15d10154979e3 203b1c508ae96f42485d43de27fdb3075f3be201748e51ff0facd328399ff2e0 c336b25aa242b63ae73f67e1bf193f50fa8f40e59110b69e76db94ed57886d6f 8b285837b4f1690849e2b7766d9f530218aebb3e2ff9110a7b149378dba033d3 563db591fe4810c041d6fee4423947f244f35e819f0638b691b2b9fc77cad430 03e7898c213d50755f382dc62e36599c1d3dd87b6c9e0159472ed2f9f334c0c0 22e97507ef3b946b063e25d97f5212b6494d2696f9971ff5d54475ca9dcc1afa 229b32794f12ebb14c367f5f92290fd8190e14a65aeff3fc78aab3f8637b2f5c d6a8b2d32e3fd2fb5d1c71bf9fc51c0095033217feda93d973e49308f5694ee1 babc6af36f56c37f9d1cfe50d88c6e7a1403d9ca9cda788a01669a8ab6f050ca f07adec8757f7740eb2403d1a4b1c29bc591f5f90aa3c8cfa17035b07f0e6937 040390e90391a7eddab4f52925efa58dfaf935ae202267ec0f2aa535266dedff 84a82b340c5ba88f1359f113fbf717a945ccf2baa3b1561f347a70a83eb3727a ed06ddbbcfe612935bb53add5ef26d3e763e820f62d549af2713be22c6c6ee48 422ce5b6fb4e39f0846ce3a429a03fd2dc53df54f806b5ecf1dde1f2b004b074 0de9902ce29035edeec28692133417787524ed249b1cc1e2a1c000c2f35d563c 51c3352d40a920458a2e73a32198bfc93a72732d434b1138050ab8574cbab540 777b6aeae382f6d39fc2e613dd90a4228c2e8d69b3c56a84f250996a3ccc401a 2a324732d151575134c39e80275ea6aefaeb038346fe08314bcc90cda45b75fe 99a2da7d0817ebd800f5c2dc62649fc202068cae2692760a4b5bc17950249a05 3b8676f56b6358890bd11968a18e40f0fa56155aad8f89967a9aa37d8297b177 c0c464a4bd5d573e1bdffb58dad5697660e675a9e9e63858449b89b267271a7d e649468d35445f60819fe76f8780023b5a1ea23f116398ffeb4b4620c68e344a 05e7d38bebe36c388c980a9df464f018f48681ba297ba940cd46853b52300819 56f4fe13aead9eb250187d3f55bad323522841e0b5fed2257cf419692be899a4 acbfda9c0407409f6363b3718432409a2c56ead586ea6f8625aece0d48fbb29b 0bfa3d79778d063ad3675a485bbb8f8baca3296368409d81aa25ed3da324199a 800a29bcac3c088906ddba33ed1cd9e8ad0335d5075ddcfd28ded381179fd5a0 7dffeda14c5e22bd9cb1269ac3326a6a617b1bc277a77f5066aa2676dd6b9d40 8b786c7df9fb0e695678e9cfc79771bc84badd695e7285ac75bff9d6a07c7b8a 11bab52ae903cf6d7eea8a8f14da338ab52829ae7abbd6826e0c96cb1d8ce098 6e0219a5b11dfdc9ac7e6a8ebce4bac2b775a98a12697f8ce2afa792dce8553e a45f1f5479673fcea8ac0680f92089b115fc939bbf97094a430ab849ffea4dd9 e5aaf69d15c087793936a53d679544d2634ff9db1b81cd2266629c5b5d6048e7 bcfd31af3666aea09e3255e835fd931019b944af779af8e22428b713d942694a d320bc2b9ee9a777749dcb6c40e59a9eff375737cdc0c9e63539e89264e922b5 00910267ec94f2cef9ab012f1884518fac66acc140e82bf9684319d438792eb8 b13f3a4fe4429f8c34ca50fd9649193a1d9d42939280df3d40baf029c08fea85 79ddc3b230713949fac670ca6ee8442ab77d3987ad1855a856b20ff8c184fbd0 b9cf848cf9d1aae5f0d066e281b3eb0d447851c688123e4823dbf3bc5ff84929 3ff30f29ca0f5d56e3114205f032febe2c7ce3dcb778ed20d0fde85e42d44a69 49f1c860b9653e21a699f11dc10a11cde10d311512294622594c7fdce67fb8b8 8b18920f526da2a285481fd308428fc2a125c257155fe8b9c439e100c1f4299e c3b822f191fac8d362d2262efde806e5bb0a468290708fd31d6fa83890363a8e 114fc1213163e40d2f246906455a7edbd3070157b2089bbf0f23ebcd4576e7bb f39aaa33d3a77cfc664fab4560c47c230af7809cbff25f6806c59918296e95c4 c5a3eb0d203d502d830ffa7ac764c029b2890cd5c7c48aeffb793dda0e35f206 ecefa373bd1cba2ad4b04d5f2921218abf99f0825cf482c3540f94432cf01c45 3d4313281bc933e2bedbd7cad99fd60f1748256df79c08936d2162bde71fbeea 0ae2cc5b6d3b36b676cf08f4a9b4d754339dfa0d0ea5ce5abf5470b4726d09b5 6990d8de84993016435ca8f23aa18a7e72dae4d8d7e232994f7fa5657d34fcad 3cffa4c6b3aec0efe4e7e977e5f8a7294ee5c99dd5c0ddaaa3fe23351c941d85 a2d0b812fd1c7d191eeb61567298981e36b340c134c78da944f8a2203352e8cd f455055f5002146f5bbded6001d7ce6cd58e7e274caee50cdefba3613388a0da 477131a096bacef938be8fb54088a9004b29aba49f4579c33ca52deac5a8e5ab 67080c465a248f515e86b58bdbe91ff272cd23b08b2a7efe4d69f47e0ac85fdf bec5ac0c0b01a028ea0b29369e79e1c81eff46e9e69031ac3cac91aaf4db462d 2ccacdc26da82afaeeefb377043620a661bac86ff30ac720a847275c3b22ef41 6b20b96ecbbaaff46371c8c5094ee3af5250e9171c3207804bd0ce83c486b5c7 77d1c2e2a5b9048ad9c2200cd77495505c851fe1d0e4872f46de66b7af209ef1 fcd862e7cc429f954f422dd13ae8bb48dc029bbcf9b914244b7ef0a20102a155 3ede83a52d68d2fe4f8d6e721aeb809bfdcc0b38882d41e41dc2087c2b197f26 e38fa088d56b933da20b16848fe1310e4243513dd3c729f4032f74d47891ac8b ff44c21ca0af4dbc2e0dc012ee8e122de372f0d530db0d68cecac9f2a7c33b73 3b9903664c6ef51dc655f45f82de37b954dd70a37af1a45eccff40c492f797a3 1f70a93f7b6b459fa011b5e7bf421d5108430c443b1849f09e23aadbafdf2b22 b957c51631fe2c2dae71100ece8911fa410088a255dfc3521c0b1299d1ec89b2 b4c2e3669df39ada556b367b6cc9ad80b49d3ee765b54f288f31eaedf947aae9 bec54b546520ce1edf6def1e99bdd92fe35fc090f3d7ec249ea4ad7ddd998d5d c0105b38734b5a317dbeeca88fd34ea91633d4eaa90171724a420512a1769f48 34d20eccf3554bc5cfeab0839a5da3af1026be554b8006c91e495014f47a98a2 89b6ae04aa15388f5286393aec911d4ae16fa9e1c6cf991a8edc93ca27511c02 dd8ed17a2d4ed78da78446870808e79b46bd9302b686b72600575324c9a18e72 9127b0709d801ac54b8d550285a6a4e30a77f84a532eaab6c337a1bef97dd1cb aa0c38fd5fad2f1302e5983a9a09ae83edc730a32610a7dd102475624720588f 1b614f9407aa350b5cc5c52ccbc788e943a3f3d6e6a75e332103c7cb52503e2b 7bb824ad656dd32ad9c9ca1deddc41a3589f3fb9673ecb1621da283659139d26 8bb69321746fb816425ac5848e3d941decc629180993180572a2d673766d217f 17a6eb6175bd025118254836943f75898ff81a9cf26cd18202665bfd15c98e16 efa75e34a18284a2434c5b59d0c28891b998b2120a274cabc2d6757225c1b746 4866748e5ce204fef81c801a58339108a51e6d3fea7e5e2c879b22b358aee2e5 eadb7e4fa4a23a04f7eaff1faf8cd72b98215fe213ea9404398c3d942551249f bf44973880920ae02da88d1293672bddb4c5ca8d2c7d128cb244e480618cbef8 466f9c575f9eecb3f26664d593dff14960f4c68b3545a704b99594cec3f63060 b57feeda8456b15539b5d24c1aab860b97cdb498b629f81d57ec233e4111007e 4915b38ba83896e3f34bd08451300e26d5ef5d8d8af7518ac0592d19845b4464 d0524fd7c02630ab477ab113cbd21b5347063c933529fa01813d43a0d7d80d5b 27e89f490f81f8b21f9a08539970ea149b3a18196d54edae47105e2c21262498 a159d840849c5682a7fcc45335e329c3f4f65a0370bd835c70b2ebdc08d2eb83 b6876311d19355a2bcb0990f7b3fb98639eb5306af6624767cbde5013246cf8e c9351e40f566cd7e30de47c39154f598902d0d2a4f78eee4b37cc5412b2a6aab 1e30ceae50d00e8a0e87c28e46713011b4d911da0642468a47b95b98c33f4cdf 0c0f7add9e8f5c60117f50951765739424cf52757f48455162770e2642b44c90 e72c4aad3a76f21c7f40a8f7726b4f25c887fa11de547e5174e57d146462e764 49ae88514ddd003ef502e13b4ac56270888953a5802a5859961459a4c702cb31 acba3157ed52715ab3c0ce192c822fc4d317e84e360103c3cee3115c70a601d8 b05947d43cd215f923d48dc386c3f992351c5034c9112763bc2f89d9b4c323bc 0995423fc0a5997c95e07d3c4e16bade98d1885670ff48e59929c59c6c91fdfb 722c55efaea965818c6588b5b72f63a5b5036964ce66c662b0d01e85417e03fc bf2a2562576c9108ff535b04e023ade892ffe832c81deb4ab1cf69df767ac1fb bd6bfeb30c62acdf53af007c5fffe06ac6479fdc1582dfea2e5100dcda23950b 0c597284b35c159e09adee3c5f2b4383ea00488302714851b99f0fc6a774b7f1 86af1fe8506d70599b9f8f277f4a9467bff5f11c7838feeade171b6d62c109da 3ba725fc3c37b7c284b280ecbd3030d15b99008537d072da46609f1118322170 0185bccbc5d27113551e4eaf0dac7a724fc0c5bd8e03ab58891a5af79d0c4540 a03be24b919fc712ccef8e92f8402b32a5ac2e6ed435f13a01ac0adbe4cfff04 13a37f625c969172b64012f9c07ea8f87606f9a44bca086f77b72126dbae08b4 c370773fc793e240c04a6c6b44ce8c6bc06bbbf85cad228c299a0fe0a30ce63a d1c0b2fdcc1f83f9ff3808ed8246a6bcec7818eb8aa2f985a1cffb4bc71240bc 8df94b166ba97d7b5d3cbc0efac70565d5fd8f1bb7676765433122d3241476eb baed16c73411d8331650189ebe4b53fe9bf2237abe48579f14fd362fa1eca68d 968e2c6632c17127cae014ebf9c72942da77624a3c7e850b821a3b35cf3fc089 042d32521b49938a514044f1d65e69d2e74e3382f9044dc58d33d762f6a3483c 5f6ca5b45a1bac1ce6f04d42b6eef09ccdad558e1b3d5737fdd4c1947d57580c b8f960e7d188503d9c23b47eaac1ebb6ecf00558059a2062d3ff4916dabeb00e ac773dc9ce7620b6372078649fdb96ab0036c6524d2c24af9cb2054372306681 dcc1a2b1cdd61951167bcf580126f4d91854144f410ed394af5a29732c430935 399c3cff7cadc89baed9290842c4d689768b41dbd3402aee4751e4e6dc83cd83 cba3fddbbbde39fb6f01534b8049452ffe7e1837d28e466c0929c7726b5bb3d2 a525c1b382c198a32c710f6d1bbba6ed75ed85bad57ade17efe1deef3cbebfbe b10695a3567eb36d140a8c81b364477e724812c5d5cdd77631f756a678965fd6 100f2e875351cfa296de66052c4a83ae00c2f40bb97eadd1e7ea0068e524c568 65373bf6d75c43fd82031c030df1cbb1f2e54fe11dd9152d06f67784437a8c6b 10900f24d787db19021c7f6ffd05ccd2c1485bc9ad50bea77b5f7e2dd4d168b5 9206d8d7772a8042bd9b3ccae4d1e897dee041227269589a85225df73d51d8e9 e2196925ba83980352a4583ecfc2609194bea93e43702e1efab71c76eb99a77e 5555834ada3f35ac16555078158b1e129d347b2815d0cee6e17bb1db04fab190 9e411227937e25b05de329d88b29a3281e1471c26ee51636edd7b2bdb54532b5 cd812857f0c9592e899749df1d1ca06a4ec898e2d459fd463b87644ca1a80952 c336f88981b39cd2835bb54ff9d40dbaace66bb32005c266e4697d072fcc59fe 2f96e2125d594ad89999559a2d49f196ae3a075651d34351aa729147fbae4c21 d7f34bf026e0f3eb8f989d50f7fe2ca31e3e84f322bab7d956379b6ca67eb8c5 4ee15b8fecc0cd747a0813b82a98727e6557d35636210fa25046ba2ec63f6778 5c024d33681e72ce084e049ac978cdf05f2f690f9eb9c3c0a2f667e9feae4114 eca951b655f40be30b5139801f0eb195e6c1236dc1de5eb982a64ddc5f3e0ee0 b1ec1fe38059d22a2361a3698b04b3633f8c5e90f94e1b2e67c7b0a83d0ba135 249dc9cd75683ea99a1a6d78b941d25f54240f025218ca113c1d10e83e1e0844 395735c24e6403b5f9b3bf6cfec617ac4afaaa68e3861320c6ddc0c3dbba3e3b b88c05ad20d89d50304c08620f6b7f4ef80c50a95cc2877fecd2b929658de37c f4d13f28d1c3d0b5424b8ac845becb99c0c63fdf0d50c36a72a2b49bd64c4486 5eb59459759a26c15ede5971790bcf73fca4ef0bca2b2d2c712ce5d7551c7bbf f1eb0ea50f8c27d0914231446e3fadb7f0f4076b236bd76d15a2178043f15b1f 042c13be06da921788250ae0fe2169ccfd7b8f58258f6a03f0eeeb9ee4b056cb 3e8805215ac6b972a2007adbec893c526522a43ef8817e16dd09637946cdfe01 82f05de9344173995217794bc4d6a239f157038e41858fb49d73eefcc4ce979f 779060ea9dcf24e80010259572f453b743c818fe22d78f73c178f4f8e15f8340 8578dabb1c0d7d98f5e8cdcf2aead724a8fef51d24850eac7acc4ec67de107eb c8959add000fef8da12a32535810a4629c9f3005990500c472ca0c115a0f57aa bdbef5249d2f3fff40c7bbbf5293987ef976bdb15d410a62dbbc80f5066cfab3 82cf3f9a8c5131e29b1b1cbc9f3d48594fe30a5a0faf9f7b4d3518ff44905567 cb5a1a999801bf308a00933438df9adc4ec64b82a91cc819f46fdfa4dc63cd10 a2b5ff7c5043db55d1ba34b94dfbbda40c407393b2b5cdade2a01296d6a2d6cc cd9f89e414c961caf06f311ec8dd62f3941f116b42ee2220f6cf8730af87f225 94f778566f9ef6a3eb371752f76cf66523306c2ded5612d187210140dbe8ffca 1fee562d0e19adb7274d1bef2616122f37fcab620c31b259095758c361749193 2753541527a5f28886b6079e94031216be897533dc3aea2ecfad2d3b34c14a0d 74f9cf2e7284f89e175f7da2ef898edfb392ff850bd0469cec5b2999ea27e413 0822f78b4c9670073a76bbb206aeaa9f56d2d4c37115cbfef360375fc2bc498a e3bbbc6efaace48edb5dcce9a82bdf8b1a01ffef4e0ba272695ed17f31ee0ba6 9c249607b3ec14755909b1a4b96576864490c7c6cf077222f40e604089c2ab78 845830b099308bb31cf8d202681d2f42a709ef7a7ff29f25fcb340b8f6f1af21 f18514f95167651c539492ddcc5a154da18999f6fb5ca844c6661f113f36505d 1095e5095d519c92794caecc036b98052f8f8ab7dbac113cc144734cf6e732c0 4fc7a8f505c45e9cea40249551027ea08f7dbfc56f2cfc7dba28a31f8ed09695 faf4bd877cac477edefae6746b50518756026be3f58c3e283325f8b3c68546c9 ec3ad5ebc4dacec3ed641ec9aff310f11f6528b186175affc0dc3ac494280e90 6762ea15a154777659755f3e952190a37166ece974ab87c0f0ae41fd7e919646 6218aa86f87c50c1d29d74530fc73096cc5770e41c34bed1094ddc66835326cd 6c6e8e95cecfdbca5a6a3f81efcbbec3f6822c94abd6824b0b2ff04d8e005a28 42529960daf5e9fcf69f0378c7fab63dc04a440b590129d09190b43adb33877e 34da8e22d7ac3501057278a7976adcdfceb2f0946f9295163c42b9969f349de8 3567172889fa533c80d6345fc3bc669824b16091528a9ac6363a49555b3b1839 83d3c5a2dc852c1de0fdfddfe67bdabd232e7e786aeabc536e469cdc5a96100a fba85c323676f6f3d712a7fe174a74291ac8a7093279ca4770e2fb8b53292245 9f745cf4495ccdc23902bec531bfe44ee6608599bf528860fe615b5e64cf22cc ec2408378662a405bda9cacafda3bc482bf0ca4a5c3ecda11822ed66dd23ccf4 e6e2a769bb7bc694d5334699e807b312b31dcb84f12c098e9ebe730aa0aba197 86d5f8f8b1ceb931231e7955ac295f22b7dd5cbd5474c6f17ae9c67bde08b7a6 872a5aa1c00ab676ff697bc965082db4b84c9c5912b13cd73e238205b968f6c9 98e88fb15b4e7149d36a4d96369605702421a82536601f0d92a8a22a00196a74 b90582eb1cbf5d567bedb375902a66f452aedcc9fa4e610361b1994ce9756c9c 46f7eac37dadeb7d586f1f2154fb93224ffef25dbdcf5a50fbe2058fe4af4fa6 1f7515f2e3b6b0adc19d28b1733b596f408b3e01efd7a1c32d8a730687c83303 d9095d067e48593259474e88b1bc9d7c1b8f7c9fd6dad38541738577faf674a2 13b7e9dad168b21952ab494ae468f7eb3622eef3c93754dc85ea5076395b8d6f c53c69379620465b2ca2c0b55e292b41771066169b131d8ede8d958de335a124 f0f67b6cba0a5e26f0fb871f3102afe16c95dcfb3984d36f77158c55c2116055 83d6462bb79bd75ed33ac87b5a7792c49f6a7c6ad2fd9462846a7dab0bd5ccd4 e105fda7964432c084b06d5a4ec3ae8bfab30b85fad2ac5e12bc91f570f06c1f 7281cf80a74a1ec7881cb6173e8c82a9878a07ff0642d24408eeda7de48bf6c0 8841e7f99d72d4523a6434bf2f7654354f5d262a9f25d06dc5387cfb53201fa0 7221681239e07f9604d5e68a46965faf97af8c651600abf993177a340f6e88d3 113159560276fff26c42d020fea67f3d02536350cbd7b4ea36f76c70c300624f 14152448ea110c2a7f6efe4bf7173821cc7436646378634944164141e862c083 3f85f4b21ba09576235a74bf59ee99fc7690159934ac709639927a12598de4db 0ffb0791b151164266c3f61fee62371ad4b847681e39f11545fd7e79c8049a87 deab05a92357a34e87684d48570d216eb3a600a40ce746851bd80e33ec1c68e1 704537fe1c46b6175756a847e8a6c8a4eac68c3834e18f1aea4159e867b4afe8 9432b7a32bf0e82599f4c89b837400ad18bd09a29678d52f9ef0fe672e5db9e7 72915931e01acf984fb517b671b1c2a48bde0650bcc8dde11ecdc957deb2ee0b f0abb70c156aa44e30f7ecbe678f9ee9c0036c8912b737f31356714014a28026 93af6cfb7d315152248a3f5832795c53ccfef807bf889c9e08d7159ba5c7bbae 5cdcfbd85daddd4f1342a440b80663d03545d138fa31afadf45f8f97d6215145 bc67d0bbc9c1e729334eb1f625be3f76271f67075e080ff2101f608cfa6c9243 647f856b3e4a7f5b9f0edbc3c2086a14d97c4cfd940804e93e7fa38d4657fb8b b04035320466e030fe02365fc9835b0a13c75e171dfc1fcbcd058af05fe9a193 f804d2682dca7dcdf909155f817115f585dceb66b4cbcc513460e48dfe2e0ccb ca00d1eae1ad641fd240af58d71dd5d1b360773df738f76475f9ddce0743c053 99de32f96953e27f7fc7d6ace8082625adad8f875af5ccf98271211f6ffbf168 cb72c37155f98e712331a709f85b2b4cc137c3065c93b9a8750612e0bbc55797 ac942c8126db13a0a40d133df902ded357717d0cb8791d9c2b1b62586ec0e7c7 09e99ca05570543f80cbcf9c6381e19e8d5f21c00d96c9d04620219b842cb88b 6c754834e8f10b4f7c63b15b62bcd51232d514d1cb6542d44b84a84ce6d35c14 9cb2a9c1e1b3d50718d06faed604122745d6a0c4b7218428d2a40da6694b0b20 37fa13a595721d0ffeda06edcf2a9192aca945e8f845f39c850361060779bf8a f6a1fb231730587cff8331eafaffe84c036062f58c23313d0366d227f936dc0f c4352ee007fa39a2d837e554c96e3ea9c5f30bc131ee4bfee4691af41093e8f8 7888f2ce93dd67b62dbd401a649f2bb53030cdaf63958062a3afc269c65aa1ae d4beedaaab33b1ca7a62d8e3922a9066cc57cda6f5f53a1821c4b93663acf2a0 86c01b16025f966d766445addbe03d51be961d39f02611b1be6d52ce2e65ae03 61f2347d273c611883aeb9d63b502cfbb03935e4dc57815f89b2d960ca5f852d 88de1640f8120b16fe3cccfc6cbafdc3b2cad4aad54cbbddce71dca7e5a93d56 234e16921e47d84dca0ecde65723ffac02962de45664eaeb48f886f502f127f9 a7cc083d50070f1f751506a25c920ced9af70758dcbc609018ea29d2bbd92378 2114d7509873f58a86c60bfc37cfdcfd9a9b3d6bcd9adb7ea2f872ebf105362a cd8022df16cfa6aaa2e4ba5a2a387ea45695489724e472b48af3002557a76ab6 8deef1f124f6471ba33742a17098f6bb74c0d78a02ef47d303d616e393eb4519 48b56089969f9016e90b1a0aa3bab624a76ffd2d5bf7f8938269b114591141b2 9f18576b44e23d3c53213b7b7e4ac62a106282fa4561b3909c2e8576dedf414e 6839dda8b939921fce2234d2de861429ef0e896e8bf53734c49a7d9f9987f2b2 b52f6b08edcdfe3b6c7913e9da807d82e25a549e36202b8a8950f0a14fbc58d3 558d9906fe2fe216003200d96b368d4277f950cba6ec1949be6e42f0bbf522fa f369d7d155899f1fb4351c2a580c78ba591f7389ade0a8be48104a149dba7f13 06ff79895fbdc3db1d298456ae015ed3b7d61ebe3c69df56878fa9d6bd4522de 6e90d3ac10d5efe134def9d541f0d840c09ad7cdbc80e7ae326c132abdc919f5 b51705d06714d270bc2acfce33937dd15cb0329952c899d6f4304c7476adacca eea364ab4655adc1d2af1dac26b24fd8a6554abd57925dd4bbed807d6c40dc3a f815a1d1b99c386ede5a04765bfddcd83bdcf4d3c43cbe2eb768e9064bca8bfe 542eec4353c7aee91d9415ba2fd07311a3936640f91f1b061f1a1fb62d0fdd0c 127 41c17a189502b1bead4572e6e8cd744de12b10d68969c49af7f0eb3e87a37b0e8cc7a68596cacb7aee855c839d70aa17fab935e23862f299ac4e0e48671c150292724bb14a883beaf82ea8fc8a4ab684452f230ccc5194d60bafd9ec459ded0e9a76ca2d819698779d7e109d8941f0076b286a7f09542dcf018093e35414df0bd93b1bf95f6c123082adc4c845b115a3e27dbbeb87c7b65f6424bde7e9e2b903d4b9ebfc707a29cd27325ea9d6e1862e85ae84c8ace3da5e093a9c83d61b570926adfc04d6feb68bf1d2eebbc9fdb61aa949d206b79f38e7ba47dbdee6c06b008ecf279b4eea3103297e63623d74538a05643d7807430b482087286bf9a3220992cfa8c8b2cc2051ae8da2304acddfb3e27f4223e65b22c7709f5fc9559763096b1bfeb6a10bceed77e4cc74772f34d29fab7cf52aee9c35648a99e2f6d4fc07b1f7c9597835ef53c14ed4bb8cbf95b8b7cfdca7513ed88ce79cf3c20927f40677972391cf0064d2a7c2bd32f7f61e36cfe6df47207c0321761fbb78811ce20bb1b4606d04ae423b8eb4139fc299ad57131d47d5fda5e1394f6859b103d4410f7277dacd7dc7f8bd5cc5e187d48e2425942fc4449693d71655c17eec0e3b1d03e37ac701c7a2c7e14e00871bf5bb6e2146074778fbb1df6304c31496ef2b5805cd3bee107913ac6a385cf0b1a111a20afad8c1284fc474c22368d1dcc1ec280c22a18b3a192d6ca4ff9310130dc040096356e47f5f450e89f94cdd49f562690c4c17bd83fd87181a0b395f1ec131dd40380d0bdc2e059acb40d7a1e92cd8540c7dd6b7c8321622a8af07be39aa407c380713c3269aa008ccc9129d504c8f5a0fac070ebf334ae2581933cc7f0146bc5cb2042efc526afa694bbdbe0f8518280ae562cb65473724a3d86da4c7f6127271455435c30720d751eb1323ae2390b8083556bf4d247e3dd8fdc831ae8c94d62450fb3ddba477f5c8f0dbb1c6762af1050fac0050f0aab353dd8d6d2c42fe22d40f03f9bc04a3dd3b13aa20cc3f80e901737cf3f0225b33d50c596886d569086409ee5c7e737dfd94fa93d9f73c0d94048b4a7f31f920c49d8b6df883c561a605829df89c8334e5bf468bf2e48190b807657f408497aac770338cc5766e61017fd290fb91eab9c78ae1b0831f2e614e0e55d69cfddb5a386212d8c3daeaa2ca1de208a0f67077e6e31dd89b7dfaea940451201d0a679111beac286bf041fac6e5c17f0b51fca4b65073eb1ec4debcf7062a9b2361077fc15990c41c05d364158ee37e6399a2e3a78194223e09b9ad7c07eabde4d91eaff9d22b761d34bda9b72129ee966882bb27fbf7d711ee37ca450fb92ca806cbab085243d59d96959079d772371af4ae5b98b8444929ac4e133f05c78d87d4a0f5e6f7c1e15d458ab35fc83ce3ec81f12d41590faa86c0b034620cbfe9863b4f621c6071041962413f88ca5c4f507cc272dcd3a9addd3f5a19bc042559289b6e626be50b6783a5cdebd689a5f6ea4f199b2bde17f5e03979bebe080ffb155f1ba74f69fe0a5a84484816e5de5a4dc83bb97faaa942facd63ac760ea865dd813fcdb366199949668db87b9affeb05f01bbdc704632ab117fc2dc60abd5895dd445cf05df41c3fd781d05b12fd61c86f0b4a28d596693acc0d157f03bf8ec13e9c3f8fcefc83b5888fc530b1cca18e65f385629208edd28899a7370963f280a9d6309b7006ab401e3dd1d7fddab92ae664147667fefb81dcf8d0600bd41e74cf83c882d58821c990322f66be642e16d8e698bef9e021f7f88719780856ff5ab83ad6f95da7104a69d3695bb7efe629e03a7ac46955450ae6506edf0fb3df0aa725fffd0030fee58cf7922e9cc73fe03f8f6fb45c986421602d3e71078341944963149897c98f21814d263bd8508a84971b7e3320771293532c4a810fa353a2178a481b9e17dd90f2bb4b6fd927bc6eeb05f478c6a686dc8d025e280b5323f7395226bb3ad24d662217f4f211b2ef8cda3f6ef853fcfe45a51cd952021ea7a513aaf49733e36fd63ebcc3c99ad3a5060ebb3a7be2ede8bfbf32370b01c9030a51553904016ed685bdda8add6689cee72e4863045d78f6d9e67948c30ccfa8a084aa85073d50a7c19866f4e2f11677ac50e5abd287fc2ed28ecb388205aa6b1cf05ab05992fa70b5820722098e65e1c648955e2640c91e379e1312870966c0a8623b1b2a3b31a5464cf77113346eedb18e1cd6b44a8496f6dc66a76f088289a105cd2845907d5435557db9b2b1ff0bc4368b9b658643f9a6b87d8fe20a71411871d5a64115e371d8a41478542d1e831ca466f17ecfbe7f5da03499fd07fe70dad3a328cfc7715769dc44637eab186d71ad70df0211acf22f294713b2081aab4881659f712d9ae16fde6db666112ce6dedd41d09de3734d7d263fa8920afc075e4d678d6b4da24a8014b027ca1f282507a5b2818edee2bb0cc8e13bca0218ba7882c8e582ab2ad660e8f6ecd6736002c4906d3b8e813751aaca79a5450cf619aa4f11b319552653a09e7a709604c4a6cf8783087a4e9649b05d07b5090e71648937f3ec8a3025d5f4915e8fd99b04884baee319f3e0b023eda1a1f8770b69a7ee115017970591d38a0fa4c7ecd442fb961a2f87ca072962c44574215d09da1f4834408a7c69fcd60a2f9a37075303a082f98b7f61498ff9e0535fa1c90b2b1c80e763570538991c6224c7c3192de11ac8204f51cb6ee78e508431baaa03291041dfc06445cd470d48d6b779cfb3c0c021a62fffd8c725c918909dfdcb0a22aec472d2d6e30c854ddb7577add0f308d63c830164c99825af1533808a2a093085ff3cc47a3ca0233019832f9f267d71497483f1d8a53658d9bfeb879d33096731327db9c28c58b58e78db9070afbae87a73de2bb1bf5119bfa8c60c165d07f76e181a05700e2d1224117a9ba5dbe5d88232f725772652ef8a0f03b42f370665e560e6328855b0d37c5e78c0edba7b353c4487c20baf0b7d05d56e14d8c5067eed1ff5c63e2863a3e98159317b0c45950a51efbf4784797ed8e38dd59ffd0bd72f1b6a6e277df635326f9a074c45855919d57fe71c85ac7b2a3e2d0d143d09488dc486fab3e80b611403a91fd8e244f224486d809def44b1d336b3c295db0d6d877bc4f876985931721ecc7339006fa33feb7fd2be4d1d71317f40d8e95c0e2f04f2b0ce5de9408d2f8a68d49425c7ab2671a56b45a316348eb26290af400fcd8442afbc65031ef3baf2f36ea69f665a0ce5abe9da16f39460fd44b698820e11b8e5b34e39c96f8babde5d332b325038e436139da121b919de3ca705d0fa0bf3cfbec21eeadd8d93cde64eaed00613eec25065526013110a9beb87b54399097d016da0805e3e13806487dcb8ff58e889decab6e9bfc0fbb7a26ac47ac2440db66a2d5805b22fed09cedcb53f45d2fdc22755d139f276c2af8980c8e154350c535f35bc8c032cafb2e88b6bef6ad683ada2f451480fa06f268706f271bc220954c13ccc397e76d83b2cde28aa541b3093301bc990e120ad1f9bd04e43d8080c8172335ad937f7877b63648d39780cd556e2dfef45b4bd663b8b4c1e59e39f08e9f3d2ada848dcd9e281ad5ee2b2b2726abe327db073726ec0384cfa6de60f03359ae2cf7b6cd6de262dcc776e76dd7a89fcf2b4737c10362553bc600675630bb749182f2f9cbd1b4940654b31a683c43c18ff4fc769f898179b7740b27f7906a924e2a3ffd7ceb8819550c67def1fd37e80e4281a3913ac6068eaaae810a00b333cf91fe35d4804a91ec9cb86330495e49082718b872e4fc091e5ae92299705736b566b3c41f37d5c0a1020eb1fcfa6e17d873a5835fb92cf196807c61042089c754f9604372e933e301551013a7f489eac9e1518918073be3ee6118d21fc070e01cb942e132cbb73b03d52bb7cf1e82f6cc70fd79415348daa5cf40d5b4104a8516a3701f5c5221f10a23b7a021aa0ba254ea048d06354636b6bf66eae1e06a61835f1657801342db1afc27d4485a15d5d3e97002a7b3bc23b9ba55dbaaa0eb87c2870cee5208baddc484864878c5da9e3b41fdd996c7fddc524801fc1b50e192cf472ccd9c5a99a5ef347dfef549fe0d9192049b9a8e51658a8b88633d907681b717ae3cd93cb0c31d98ffecda97d11bf4226b44870671920e8f0c7a905012df60e1443b9f801b4de66b7f1f6d6fd9503fd7f00f50b282ef87fecf7b73a06d004cce7e27b1dbb1ef7b16d145324c50e589ff4dfe3a94d122843c26ac6b70a5befe1c84818938a0fbcf22c9a8f99d02ee094cd22937427c0652d1ab3107d009911ed93ce7b4f939ae87304b2d127dba37dd04ee725f68d4596ce566846830eed57aba8bc9c8a622a602a00ec1ade7023d3d909740d43d62ba98a9cafadd207892361069d8ef820d53a05d08ebddf4d0b7ac5e63d38b10cc9fa21fb306f9606e7c34220c1ce011543d2bc2dcdacdb7b9f7d304fb12b247934dfda688e721405004c7249f9592a5c200cf79af508b8943b1197b968d5e1380129f27e29b6f70be06a000d631e6aef836c2d7057ab2539f01751b4ade57178098c1ee122b18703bcde5097dbdd644631587660572b52176da1abe6d9c86fbac27808761ad424077520164f0dc7c659bee7f07b80fe9dc409ea81e605af0d35f48c8681d6b95601291cedd9ae8ea32bd2e6eb4faf6de1e80c345ed46c54df065e510be45a27c90d39f47b8ca6868db6633dde1974839825e8b0e33bb2390bf7ea5fb7f9dc51af0f848a6883ec5c3ef3ac83a85b95b14d065428d0adcb8cb05e5310c9b85fc06600c3cc66f16d67d1356f8322e3f880621f0074185a1bb82fd55707765befe5ae0634acb390f51fd3c7a2cee5a46ee4f630682161cfd26fcdc540ce1a3ed957c20746e0bfff648a5f3c0e980885ad5341b25d666e07969be153284be6ff0ce3f20d8056f9981f892f8acda992938c7f165b9f7717cfccc3f03b3f50a39ef3402a09aba912639a04dd908c0ecdbe551c46d62ed4eb66d0f5ccbd0be7d181e7667100aa16b2510be007be044c753e27fb6c5af275f50079093deb004db272852d460b43a1dd75c2d62f09db0c4216b0f9006fdfe6289943806d2e0f5819037f555505c2e87d593f1eb17ec3d18e46bff12b9662b201d0eda9a22007857efe426cd70b7f4eaeab6d4374e159ddaadbc869d8e11e8884a09b3952af49c2ae830073780c506018aee759c27cdec91f0424032272bd8ac179c31f29d3cea185b358faa9060d7910b0685a404a03387acfbcd43f9fb97412a392da33f16304a25e7c0e310838154d8fdca427cb3fe3f9fb5e10e3cd976c16c13bd47ac85aa19fb7e0f3dd045d41b060684997bd42e90904881f3f57d37c5ba5ca7c272a3b0f5585a7397903e1f24c8c3bdd3effe0ae378188d9b562598764dc7154f46b04676d2a24d52d0716470229f6c8ef0548d5cf9c4bdf4462a4000693b76901d25deea266060de406089cdd7565b4c6d4884292251e565c9fb219a6a9afdd51451e9c01d49a90d50e7de6d0094b84f48b86ad46ec3f831cff23a483079446725c475f9f5365988709c4f049c1f77207e30c11538800bcebd3d013e4968cf7db99d5427a211eafb400e29f5d026a61e88f6f279cf3aa1f187a7fd996ad0a839c9c7c459f6dbd75e10c0a5c9486ca13e46e768443ed4d1b92b4e43636c94ac367e6d017660ef93cce00444971cf997866f0140ed74704efce4ca0fd1e870373170115cf9e3d377fe50eff5dbe101abf556aaa5be5c69eb30d11ef06ce36dc66fde75890cace0905d6010ca378d874b938eaee0373ff6e163576deb932c3c46ae3945fb7c4ded54a83096e0c5cdf43aac5d83671a0c0e6ea33feefa3e3962c126afb44b9c9248525c50d8eba6e4faf6e543b17fe5804ad4bb47586935c8dbd3d206271d59f269328960dfa962f7f659a01c5882f4c15590cce808ebb8f718b3eb76723b5c2d971ab1e0e3297bb026de2506406e5b6c872be490c876d3ba21a29b30fd22b6f1f09821804dcd09357b61432a8d13f810dd22993be17249b4c18e46f461555dd1a3d57e906c6a7157d8c98cb1391373844ecbe6a20f185f8d89b35b7fa9de11456a91a2403e6f6ccc28f4aa6f92210e31c49923679d3880ce5a30207379cb49ca3796d1c0defc9d86f634dfbe99d38c7ede679cf9aabcf8923b087fd63255cf022bb76820f55ae91e6c502969183a69e8e9565019a0a426ab5f4f8a76631d8e8d11a1b6f023b65da15f2d32cd800b2300189b4ed61aaa4ddd966bade4b292ec244dbc5c803b61f3759a40b0de19a82ef0a39693382b5ae3450c8ed3b74d49bf21c7dfc680c6828f6ffa6a87499cea94864f4fe638ad816fb9ff14bcc6f2cf9afb2a3c4e30a6f90b6b952a59309823a8bad9136130f5cb910132cab24ef2d9f6ff500c26c0387f03d7f562ee284551adf92ce3e41e1ae649c71e44733d85ead9dafea71e603ad6d7eca59f5df521e135eb312d309c0f3cdf9452ead4fb4ff6f818e86ff0d04e21424803899cefa5206fb71880cb7e427e0cfb19997658de993ed033cdf460ab65d6462a3c3b993db9e53e089fe92db22782c510ea87a98b8454f10c185f70dcc2f5f89c4c866b2c8a3acbdb83ddc88517e6ea8f5cf5a126459f8a5b9cbf10460dd1588d679fd3ff3813be68fcbe917e7a3a47647202b9c3e8e2df879cfcb0ee08ec8404d1e2526c583d3c148a4e214670f431b8fd9133470bf2584e141120add97d12a4258c3ecd851cae914eeaead6bb0e5b904e5e06c424e699f6e94140fee23bb4603a5ad3bc75b60895892fe85db3faa3da6fb6fd4fea8b9702b2a8006346ea3aa03832b1027b3843a494a4213c902be374fc8619c46d6723620d74c0af51a78319b295c352647b3e3388b9df28dc9822dd90afc7aaf05dfed909c7e05862e90edcbb97664f821a96a80a614389de135576be7d4678b9ce5e745febb034350c55706487155f0dbfcaded3d867a0483a34a84fba01e8e707f498635b80d15adc1ce74a71b62febac5d48a4ae9ea593ae426f3f3e52b38a681ce5e34cf083880563fd3daab48999affcf5f0b52c8deeebbed576123ea92c91eb6d6b2b708807301bf8a0960b64414fd05d79e7d0a7f5cc197d00961acd603a50f87073d08b9de47691f2802d41c8fcde1f04a43081b9494cba64b382c2c30f663ca41a1004a338440b7592ac8ed7cdc164123036b5e216a06cd3ed724455edb1acb76f10dd30ede86a50639f425f2ceda88f63a79ee99ff28127e34eab324b698587d6900d674fe4655a3d6d61092435e3dd857cb1f13b9cf86e73a3be86e1c036154020b75f32ac2e846b8c7a2a2e2aa6238dfce42f2d2f4645217da69c5130d708a17048e010d4bf4f49085fb9d4a490f89c4aca0e376c8449e8180747d4fb5189c1a0ae07cb1390237150e46c9ef3a9ee9030f3cc5b780e8a664b924f234af7194390351b5d3d6a17dcf010a85b2700bb0d2864bc8dbc2aa0813d14e7fd6d5eb798f07ec4ae038132d693a3c196010ff7f397637292eaa4db18608b5fb236f9123950eecab4d4da3cede799b5b09437fdcf4b69c0736e92e0847ca29446b5c09c9cc0b479943c47d49a2ca12f27a5fd3de0fdebacbaeeef0544a7e3d675ec12bf59c0b582eb159bccbecc8332528b4520af84d71fe712959f2974beeccd0d9d8c202024644b43108dc97100d8a9e0d47a2c616a99a7e26206bd576fa6904821b306603691457810b69699b4ccd3e84d30c3920f498322dea1785a95f92b971de5b2a0573c1cb98c4c044986f8bc9d54dd616421a166da6b9cefea591968d8822419a0058ca640876ad6ebd8d897fba0f145468901609a640be20dfb0c9b3d52d5c59026436a360d0651bcfc68d14d42c527fd9a06b6a52ae3220ea79392bf7500f1d07e0afa5d731ff1e30490c0e0c432ed6fb7985fdd9fa9fff3610ab22ce63596e04246c8e00774ff08b625e51a894304f0f0ac55c1b35d8cdf63d05756542c5ac0e83013e1e811729d4b231eea79e8bb2f8b669952670e2d617950b3785f8dbc60bad56e8c30e471e6244f24d1320769a2914522368347a06286f7c0c5636438101b6307657fd65e1690b082192a0413e99443cde808c5ad6152e8d13724e224a0b7dc2a8fadb407a1e53843298252a47c31247370656fbadc9850d51306637ce0030c28e0c51fe8189888084d991426cd20ea2fe57f92a703f0118e8a8eabd3f07d7c78fb1822f2c0d538f0614c9102d7a1323f381c21cb871457377aea0eb6b0a495db2cb351311f23a2839ad436214208a21b56895e54389d8d90ecc1a2ca40778dd5d97203a89d0362e001bac8d7f374c2fbb3b5fcbd7c3cd0a9dbe77f0740d6ba5973d81861c8866b7fdd244460eff2122d8931fbb2e9ad8c95de04a64b5095eb799f48b4c1a95498c26822cd4c690574569d9e2821238e8c7c5c9e73746045b01e81db6aafe563f7228285623bbe190c3e12fd7273598663ca300e915ff04d1fdb39f160a60b5e8f06dcbd787e4e375bd1dd92cf9667cf244316df81e27025dd1f6f711509e61e9cdae0d27b791f0b7123ffc6b0f5f888506e60f19245a07eda27c94ae62a332a5bb5212b0fb40d252b60a9b562b63183533c29679d5890cf3d1cfa01bfbf251fa8e0316a3d9ae70e0b1fd970097ddacffdd307e978fac02abf37720598cd5bc0dfbbbab98e0b8260d25e2d8d389a32923d2a023d424440ea2d0feb573a2f600f28e6ac47ce0dd05f9db5e303913bd52e8fa95839baed2037597d778f0939a9b7a5d92f2361aa7eafe870493e059f4d405498889d3335006379dd3e0069f6411e87f4bce1f867789efb501a321a93dd89931074c4e9ac1079282e4df4a2cfec276390dac297efa5a9ebab5486bd20daef7136f3d24eaea04c19f710645bb1a9fed6eff3a36b5508dc90f5e1df6b181b021bbbbd71db6f404b5340bf4f2aa714c0c563217b9245e605dab20470d0faa473cdca9b85107c10ea0119ecbff310da10b3c166f077b568b5678111e59cc4f346a503906212111014419d27881673994835d131c06a63cd422dcd0c39fec821946e2e050fac920016f4df082b33c586fb38da1ca2a42a7367b0379f05ad12544be3d80f13ea020076eeb6736cc0296ac915aa04622ca0723e9a14fc5bcbd0fab8c872754e9e5de0fc3766b5161ae29015041290c848cc6d05dd0ce0c1a6f9c9497d2e64777ca3c0dee4033d093b2b9a302264cb9f3b5d2d0d33698e92f83ec687bf4a60800914801f37c838a9c0f16375bff1c0b42593b291350a5e08d9cfbf50b151efcc650fc0dd6b43a3cb00f0968e38f2d60cabc0548f22888c4582c08b0bd684065cc0f61023c11cc8013af8b87c976973ccbed9fcd4144526adb4687c54ce47cd339a7ee01adaf0764ffbfd568653918465db8d1374d7e58ecb1b3884f00443eb116fb3907b1c58f2f56f11875d38ac248c425fd4434067bf521e98804402a58fe463ad308f9e707d4ec0845ea27fecd59c2f7915debb858ea5bf0b03058eb401e400e8d03ddefd70356602204cb81b31ed6183d086d6a7033c52d890c026b51f31a30f00a132f2dd73434a877a18836e9108be75043a322e439f7c6e8304b36242a7d6101029553327772cc10d5e500a6f958f97a9eaa71ac07b031bdbfa58c779d2cd70f0013da017e61ef7d217eb407d386f6944b6343846199467dec9653d5504b110cce737b0243f6a78b2eb07b34d1c04d274897929f0b03f4102298c1ff2555bd0f0d48581ff16b617c70d25492b36384de77ce64dafde048eac49dae8cd692e4014a8fe743c6b194b30fadb4a9f86c2e423212f481facb3f954ca993d24d449a09fa2512bf1af06b2127b8dce0d4f4f8c9a0352483c7bc7f4c43f16b7cc234c8005fa60227ea2f3a6fa17fcaf6ac912c284a982dd2b1c10b8812d68886f70f1b0eb0917c3b879a7741dc3b0bdfab218e774e59856e85c899db51e3e5da3fc2310533a46937e7347d10e0072fc1675bf81dd0054057415bc024fde909571b90c4087a7edb9ca7b4ec122a3f4c514555b4486845cfe4aee79215a7d2700ba18beb03b76c7803e3ecea3238770419f97d80b8bd7113fe845f6194291f682e37a1dc056f604315fd9ea558d58c2037ac99c308ad4a3bbcb43d5eb357eabbe2468df90f7855803d50255c7722421ffef6214f4dab1f29d487e9ce584715fe2be34eb40a20a488ddb093bd3ebaab1890863d2214d5de911a2edf242690d298a060a2bc04bbf83cb61a541598df81c177f59490bf14efd25abcec89701bc32098e4cfa0025bcdc8e8fa2d59004eda679225544c2865346cb623c5da50e42fffdde0f2580d1701ac4ab76b4d1392af4f54b55c2ba585b7b35b92c22055a05f20a2d4e7e103a9864593cdad36898db101168c5bbb10a6c865217eb5b3e2363753098f9d4b0cbb1dbacae892679ee38582da093baf1b0a2238f2bc59d4899380c322a14dac00e240513dfa273e97f215c7ca4dedffb3b526496148672a70498d973e22c6aa014976cfc87bfd136dffd4f22135c3095bc07f54a6b192e27b8fd4f37e2b227102db03e27d035784b8f678a5eca6fbdf526ea2d1b547ca26a39c8dff51949bef0b1f690ae18a652318beb584ad09d573e8b96ed1df04a0be3ab7de3a4fba31440afeb8a3582689fbb5397faa8dd8470deb0c950630ea6c1cd4f37fb51c80c2bf06d31acb716d6943ffc12c8240baa47b00305ff00bdc42fa95ecbf9758ee08d00f006443e69517f9f1f63e1732ac8965ca9858a4b7d1f1fa375c435f9e59f745018b41f23665600a89cce7c54d2f0c491cdf9e2fa07dd65d376be3a917ab5d5509e176d94a11cda96b751ce2f89f28924846ac0adf2d0db9d4875ce2ba19b76a0693c4f037fa65e50b8ad4f1274bdc6866307e075bb423dff1f334f070fc23b5020456aa93c6e5474921fe4756a527cad37b3d860276a1e6d4eba66258b6616403f6991d6d211ca56cd79b21f4d1df68462835305e24339f8f34eb5096a7e7b101c6ae8d864f9c363d2f1096cb974ff57bd79058687346e563016d20ac24c4730c90761a375961e925fc129405fb6dab75092d93494697dd2537a61dd3d1f88704a4177bef7378c0694c816997d23f23a74905031eb1ec35260618539e63a4db01cb48b5429b098b2955ab5efb37296f1471170f9eb7d9a591ba9727f8cad1cf0070f54b5b7f36f38d95acef30859f7ccccfee3ff91fd6eba3dca73bbded16a50cac49a4851e13f7c666c6ba514619eb351ab6722b5723897174adbe201ff5350641d07619fed76e5689a0d953fcdf325396980ea911d1e2025b7454d07fa0d90f9ee3e1bc184167ee87adaf809b9b1be3c1d1dfee5f056391150a687bded3000598c01f4ab92d99275feedd27da3ad8a306fa23f98b67e36bd5ac20ed2e72170e19836d84e4e50c6ee3e283f8cccccf731bf53e68d75a5df42986bfe3a554430e305c427bdd5863107a87b4fce4626b2da71f108098c92606580b8e47b1175c0c05702f288b317a475d8fac21fe6e36be3fd56bc8ad579a31c48391edcf67770f466cd500fe8068ac365e20d5673b465efa2a2b293b11b7f169f96e646a83610a7bdf1cf0f29d5bb681f55bef1dd5f2c7ec22baada3b75f537fcc6655f09a2e04bd989de22f20e55d7ba591ced714899dd1fe17438cf5491ddcfc3dbb4c426d048a30313269fc5956ac678c61b6609d4558c20a4307e2b420fb22e888d6631a0353d60f51e6092e33ffeb29e1c5526f93715ad1fad20918d68b05dc492cd1af0df75d81ceca40dd0b0566f4197711203401a4313a9cbf8ba0875b75719844260d28e6f8a0e4c1a5419f1440783d79fcacff23506fd46766a3a6aa77d72426e00872aea4ff611fe945f1be3a9d9264c12bd5640062fcff09ff437fa4f86db9190c084aa24575951ad45ac1f632a3551a95c220b57e68ecd89469314bee4c89a405251559aa47cc3cbc9719e514d2cc8f0cb3495215ffabaef93353b5e55fc4dd07d67e89dc185df51c24635929762da359e29a41204d70bef25effd127adda9607878c6329826ca6557953497f52596803a81f1d299068c9310612db21401e450b1ae5c9826370584e72569a29f26ff75e6f1c69078b650087f416b1fded37270a5fceb89bc84a17dd15ceff23825c97c48cddbe23e95619f9ad218ae37a5ad201f1e0f1aea3c0561b90a43debf848b76dea3093d9ba746f218d16c7aa7a37040e4e6e3e6a43ae54b2b939f00fa9a692d486341ca88f5864737499f394b431f50de73884629692f2ce962c095c935d010fd181304672aab80bdf423fe762a3f50a58603d98524f65241381ab612fc3cab3e5461b866474d0eebd22fbf3f7ceaa05741f55fcd81713d78ffd866783fce5866a55b794c60a0e5d19ff1b738c17a70655b94b75f5150398787379e6d5acc07ac8085d1d9f637ee1f9617e738bc8540d0e9959856d412127d00133066c188f12f617ddca67498378845157613d1ee60ca1e9e82d5302ba940084b44abe06843ba2f472637b6233b97ddadf79cc0dab0517f1197dcec84f7b169e32a743a1c40475005504ca5b658a74a37228bdb3f2026a241b4e740d1534dcddce4c2f73cb4d0f961241afe21de28e7a60ac905cce00b835da70e4898c90d5e6689326971bb8ade1a0109bab818662ee2bedfda9590f937a58a8ecccb933e144001644585f0d775f82fde69e3a8ca54abd21fa661f0bc5df74ad14f002d7f00b9d2fa65889d5e8e96d3d7b1704ddbe6492c66dc0370e0603ef2fae7dfb6ff80b47ac64bf001d49570dbd5de5e72d46286ee995745d0458d7dfae2c5066f695682706c44fb98ee2a1107ba66b6b9471947a9eefb1e6028f83c7f9329270510c9e1b36722d444e1926bfec7760de3c4896f30fa0bb1102f7cc5cc9920303539d3f308717624fa2334201e854cdb07514a593a1d1769e0e5a732094e6313b364ca12a0eb4bc871f27a8f528c5ec316368979d9a4149140363c3d5e3151469910c19a55b9c543a80dad4988b8809bea298aead8973d9660d0c5de3f620e7a8c7c7e6639429627d4bbdc9be3a1edeb6ba3060d85b8308aa01140b0bd3d0262aa0821c63f9f99050d7834283d3f5641ccdf631df8008fd7d0875dcc5de34b011bbe227429f3e0694e6046974eaed923ebd289bc6b96646f10841b93e1a0b6eb569ca0922516053473ccd1c9db8ce0b12829f459da8f705a608c3dfc4cb87db34b422f20696a9b68b5428231ddf19ca4256a7e12c648ea8f708c002c0a3402089f1eb9c54f7fe7524d8e2c92cfa2729d981cbfd3c94e1f827067189a3200c2184db8074738317e0553ee0a18086cf9f57ecac5c7df236564a0072555b76297e4fe6e3f3dc3b20187728297fb3eff2a7d2d90d56cf906ac4460b6500993c4dc6eb8d89391993b17a19f267a47bb4e3bb3cd60aba6385c40a7000da60b0fc4c5ecec1e357a7308c9c351a610c1d4baf0d287321749ca676cbf106591d858eb047a829e24265a4bdd91d7117c1cbcad84a703080a1b98e16fee007afeeb721e056a6c6b915dc6269f87ef8a4c43bb5fe648ca2d80c6280ebe6660d2363ba6681d8c8769fc95eceeaa0835da81cfbf488130133e9f65238f462af0bfacc61a645bb74fb5c455b169132a32417a4ae719ab5cd77303619a232dd5a0ed8f97f1d6fa1b89da37b869025817f9eee1bb3e698f45d52df64725643b2dc0f2184a5b13eb3c14efcbd12af38d5af228f66f2c68a40d373e06b8cfba9e3690e7e3573e06740fe02ea780b2a79f131991522f7aed41c05c54e50d2d7a39b57042277e31adb604edfe3b86ad78a4ae6e918d4edc8cee2c460e064668339b21a0a54bb94a9d550447fcbeffbb6bac3e32bb72adcdf57708819b62321e8ede5a4000d8f89bc34181d4e10cdb9279eb0d6731690099f794d6c449cdba7a3f84ee400bcc7500f256243cb7656ec25e22eb82bf6387b502737034d624d261450341202d5f8a6a4fb679023e0ac6edfd8ba6fb223ca0aefe16de051be25b3667885d60af573a2144f091fc51efac03de9031f54a6ddec4bca27cedc0a4b9614578b690f5cb7cd2c83cfa2cff2dc17423ad782d9ed68ec068ed2a3ebe7a3ec7fa84c6f09113ca3d47bd643a5f4dde16c7e47f7ad50fbfd55d331865a50819da741733e0c6b967b57bae10dc257610b9c9975bab2c3e2336a9698c0d5d9a7a2b50311740181094f6a8f0e4f1bb99f36c911de1628be8d0a24394808d78ccaa5d7e83ab1047f05c0f4b2e93ac267f08e4dabe904a9f62a0073fab59dc25c9b27a66e7f6f07e9c4e27a820367180757b7eac6a90c2ce30f593654cb1a37ad13964f24a80701f02050a8d067d94d55bc1af5516b6b3b40614006460be36a1895bf5e5a53610be95f17ec71513e40d5541b9585fc548d89697e5b17de79463cc34f8b60c0850e61f3410173706636db48eb65890a78ea1567576a8b15254566d297bdc6784800e6d28b9d92b5a74948b9f7ea88cf1d66f78a7cd017289c7eb5c1c75a5dc88500a209e38625dd7c48e0516658d6b9970584e19ed650ff5dc83aee264297a3e804fc1f9db536407e2889f1daf8218793da01f5c62ee481616d1bf8cab525950d09c1cfff9b56683e74082f3406581d4c6c193da38fac64a3acb23b17566221cc0fc0cde603e88f487f55ef526a7a0fb7e3ef154e0988f2dbc4327565e74d0d020b8acc8efe8f24770c908f3bb6c1977ad14e810b7f5094812b6c6ddc7eed4c2e03cdd676e0d9efedf55287bf40d9a5dac323d6e06309dfbfdda7a487c98cdd800d1805c9f9cc26da6891eba7cd6ff3989deedd3e7187128b3149b127bc3cf95b0eb111eb46648daf491b676ad92cbecfdeb9cad76abd56f37c2afc1fc3e6a12b02f559f7cb37d0d7e62b67ee4e25b411643a6178f21e8f327eed2040e013bdcc09111a7939c2a5f381b35f0ce0d96b935f84dc39c8a401cf7c0551dba2ce7acd0968f4e111d876972a27ce6896d67defb9a5c027e225c50cd1637c6e90b4898001d1c29034bf2bb4c428d65348847f60e06353575449e9b4697f38d0ac8d03330bb474ba1458fd22e8f4c966c25dbc58394eb68bae656cf26aff1d970030df230b268081ffb1ec8ba260b8f7496c3679bc601c8de608f1728c228f15f34501360616e4c871747de971585afb5920e08cd10a07f01502b026fcbce51e47864b110fb94341b248c59cb5a4de095e92f9fa96ae05c7ce56125850e5afae342be7b208d835834df82cde708a30ef760ea07f9b936adf9bdbe029ca00b58601b1c9de08a907bd208ba839bad5a6a0a7bfb2fbdb7ea762b2dbffcb5f3c612232e47a8107e45989d6a65aa8c357abc4e4df0a5b336099be6428befc8c7bf66f0f66a63203641f4c919b9646fd710c00ca449ddec471955790730ba748f5431aae5d28b5080d9ac17a3bcae5681dc46f9bf04e825a75094d55165c84abe24bfae42a9de60d4b71980d71f4810fa0920c21acd37369183967ec611e196bb04038c3477cbf00c503ce3a3f6bccd83bd7aa3d3f497ce455871169b48a8a3955e046d281ecd10d3a4e623927c06d7317148f36af9479a8cf5ef506752f9468c2e9fe5db71507075d78e84166866e5bbc6a6c27003f3e480accb63a4274dca790092f4e11837d08519f1e5c7b77b31f1c956eccfe3ba7b9cf8755639fc497f8395bbddd75614807b0fab88f7f14488a8bcb5133b37d3416ea9a3b998c4576506102d5c3d1515602578495f7681be9273f470ffc8b10c95ca78abae338140dc258572c04d17e8c0b5d89458db5f279e9bcbb31b0d9a294a030dda25890ead7f0e20f634a8c0fb90a6dc15a9b7e68b9f64a5637d78b302b9a0fed8afea88acffd854b6452872e37093b5a5708cfbf5a93ca42284510358f764d5077da5b924bb062433a405bffb009e2560781b90f309ccdabd4ffd6df44af462432e2749ce37856dc27e28b938d097e836c35cd54d74bef256912b2a0fba2a4390011d482a56050a133089d45f10f949dd49871df5ec056e8e08f31c59e86789ddf407951f0df4db3f22deb4b950bc163305c5a071b670fe8b19779207fe49fa9fe79e695b6b3c262b8dec1b1cc06a3b33cffb23b4995047590388ebd11f4fead7d6a81bd2465b2a7621e4b3e290858c509386d6104b3eade816464f7b665c8b0a2848c18c4b1110fbb09fe8f620b6ccf170bd373d7f295c9be831cf6938b3d552f09e2cfe7d2dd7289b9d584930c7dacba8fb3f686e340e16093db144118e951bc04fda6bad869dc670d7bed670ad1c3556015eb7a56a2eaf745ead766851544fe54200c067613f485db78fd5104d069785183759f9f68dc1e93adb3a83831bfa829758d0b51c1aaaaf421e6c8008026591b1292ac1afaf32fd29d60f028652c456d4e30477440523e8fce5efa0ac30f79fbce798fea1dc540b6fe4b0a2dc3d38a157ef03dda79d28ec0a20b3c0f8ec9c9a5d4c7c4dbd83de23ad18d752b2bcd12f824efd92d54bce17e6390c20089879304fbb10861937e5c6731abfcab7cdbae714bb48a041162f2b73ab67b02c51f8028cfc25ebebf43abe03bde06a457dbe07ac61e581f1148287e1bef4b02ddf3fb60a6df8c58ee3bc0ac0d985a6bf2ef5f04ee0b1e489fd3cab3c1ed0a07f8a1a31286e0b30759c1867d727efa6f40c85f536d5323ae8b2b871376f1130b6d6d77fdc8c6fa7c7824058a7e02d1194df850b59754c1a36c3b55716b6a9b03130297fbd851477853b33c1c70b94e15a4b93384e7407d7db8e781885b1fe3059ad3e9ac891316a685b1e63f8c27142df8754c58bef4b21726d770b1dbf6e005b0fa8a034e4fe780c1fde4374d6ea8b3d24f2c3d0c1d7ebbcd0bed9f4079dd06090be40496e6d6c78f77cace756ecd533fdced5957da5c80155366eb1c08c201ac06a24a8910d6c21bea0cc8997056958e85a8d6d1b0b6b711cf1ca00a83440598d346828c19974907d627f84870997aaba379a3ca5befa7e3729699f2e1730ad2e3b9810e3a412eb0433e9a3ce27ad27f8cb103478e711edd4277d010efa802f7e79c0bc67464de0bd04c2ac3b9257b0674565b1ff7ae61ebc891438543880d53a5bd635b0b921a5ec28870137a2b8b903a152f9729486949816ffe91cc8b04161730411c071c07f168c170a99b311a67ce4ba4764e126b8594452036c9ff0c3cd12325e10d2cc6af1a6b143f1a383ec5c96c61c5b45498058af21e0195bc00d416a2998b57fd49daf20c0c8960938f8b578de34c9ea869309260d6ac356a03ef953057330502317a96bd6f68e763d71478501ee3cc5b2452ce513a1ab2cf00c96aa69bf8a6741496eefc544ae129281e76632ef2d811cbea506a57cf050d042407f995fd8e6bd38ff7fc3273ef542aafe98186d96706adaf7d086eefced30b96d413ebafcfef4759a736b761a0834a1a2faacf81d2e30c0c124c087fc22f0988a80e63baa58ce10ea08f51863145ae011aac911df16ac4ffcf440555fb5e032339abc3ea0f3bbea5e209ce8de582500006cce1c5cb252597dba11b83f3e90ca640fdd18b67c1922cdbd38e51702de91bcab2f5daf1d6fd0beccae4a179f00a5035e905ea176cdb717e311d974e7a9788352d6403fb052ed7da614c145f150675c6b22bdb5a272daa3732d65b74acfe444339750bd2f04e80a35fea994d840c6230473e03115a4a4a2bb36f1e5ea33f459d5db1ad14ff2aa0bf4d26c788c905dc48b14d14c10714081b1d66a57b3a597353b9bb8c66e1d43b65b60ab7be5201767dd16f12a018fc04ff1ddae3859aa9f25c0acb2e18b0a3dfbcee3c17fdf60e87b8382cef601bff5fb4589c1010d609c218a4331bb38896ca1795600b37db0a0f8a6d36edac3712b2f7370d0726cf6bab6164cc3be4ecdf56e99d4a46123e0b734c25d033681e89925651bfed2269b84e68c08efde540a8a01ce652f838d00c96394079feff44bccb18ad801cf78948bf0afe946c76d2ebdef3bd7251341d0ba07d6171db1ed62742cadbac95ea9eb126fc3a588d17d61b5b069d1ce04af205026ac43b610aeaee77f217a7bdd161bb5a1a48d01ad769d83b11370b4f06580c991d60d7688b955b06df0da3ea5cc612bfb0d088b235cc8c0686919cac6c5c069ce3105233e0a671ddc498c84ddd62190a0f8daec38c2dee0c617b86d8097b06554e746217f329b39de45109cf5c2e110db741de3a742e3b14f0ea9138217f0d86a7649773e3a27191a6c6a2e8a8f8c9c647789cd94c4ea3b9321a8e4b255c0a398da8c66acca7aca94819693d97eac740b8655fbc5414f85fbc579d67b8af0ab569234a9d4b58d2c912c78d51d131b9c0e09086511c498f510f6bf1a2bc8c09503729a869e801c4605ddf2afd7581de641fdc0e796022f19c09330835bf83013138bd3cdca68eaa2bc4ac9f8dc1054196c751d1cd5ead2b22811103099b28032640e1cc9c20c96e12aaeadcaaac7555c435aee9bd5bf9cb29adbda470e1df0f9a929be0844c90218ec6ab537b16c7fd9b81f909a1ed338aac7fcaefbeba00046c2095ddcdc0fedbe1e387fcdd71bdb9e3bb742e4ce8e2328cf0465377a371036b2ec4be16bb2e1c7e8efd2059c36b5309d33c693a5ed543aeeb027db62f2b07c2d4dbc068f16af3bebe0fdfa2d730a13a91ec13d7d8041bfbce12ae21f6ed0d28de28c73c117eb5464f14acd14504a1d4474aa3392ecf3a18a57220c159e107efd2739361f2418069caf7b6ca49ab2b32dc12095b5e11d3a62542451989ef0de9d9e0903df6cfd0b3076692d480977a35fbf4df0c25bf9ab6371c7f2aa0630c095f07a156b3acc9f6bea01741305c67eb8114ac86a4f64e406150a9c5b3590f4fd90bfff3f4d96241cb1ea4768ac402c4a6d4b51f3875439e661407b2709b031ae77b48b6abf42ecab94e589ab1fbe88b7df09f6123585af32439b2c8480a0def1056d2148cf4ced9ca4b1e1b51d8691f356b5313aef85a3cd3aec7fcfefd02459363f1de4fb7eccec2d90681172531118bb3e08987b37b824bc209076ed502cc935f131c10a1da6a25994e9788033a63d5dcab0ebe74d8928bdb30e0518a02e9f3a9030e435d96b03cad0d5cafbc552bc15cace574841c97d3164123cb85026b4f0aab073722621dcf7aba0324c5c179429de6b62bfce8bac827868875030e6e9ac195b2d4e56653ba2e4f84d2544a0d715120541f6b273d62ae5964612301a005ae8a3f8fdcb27071543872424853ec23f82607a269a1bcc5f663994c2b028a0259738cd520368a7847c26643beba3da24f268867cf585008c35952256000476ec977ea3603ad0f85544333df1ead2dcc8631939e72df48ca0c447cf1dc0ad57897bef03034232eeea944a00a277629e89eb4a95221e21e4cf6b47a42c8031cfaea1c8680f0b423b35d725fba54b743ab2247d8e70ae2f10ca16e424eca076b7e30ec40596db135c3bcf5858f0e4989c154e5ca4beb5dcb3908688ad9e80463175f17f036cc30a6e31f2672ab4156816584041446ff0eaeaf88142028930e557c81f62adbb493f765fe78b887c6568dcab3ba63da440f28dd79808ea0a50b11a04d8e55dc87f7a4b9afbb0a7cb3ba8abf294b1ded2f52cf7854811cc68102e289fa6e4912bda41d5ec0a2e29dc9d99e297111a3854091c267f18e9f7bcb048357d12463579bea22fa534d024f4c514fbbc249db0f38d78083335bbdfcdd02976d1ebbcdf44ca2ecd5ea2210589d2a0939dab885f879252f58fea5017e1a0c4ba0259c35123c276c079ab36a7bc22af60706a5a269a9fbd7ef8fa9474f95011f746c8b0839edd645c067bae80ab05647612c0af5b37f9b5eecbee5ff0d5a077ea3e00474ee358e0ef7ce6c5b60bc895fbf341c549e34c9acac955510000606727da8cc7bfef228d87ffaedd2af1c97a2f787c595cd0d8e4f9626bf2ae5060252537d9e883a8430576b9ddf791ca41e435280ee90de896fb06aa1a30de3b30867485f431518b4b36afb0550590864ad6d2b4af6e74a4093c6e29076b5fb620f5e6e2ddc04c0382911a05158472f23fd39be251116a867c340ffc8f48044940c8aa7696454b255064bb18dcbd513bbbefa87d4dada25d85d2d6518ad3346ec00be979d397b6351352b3818a54b1f9803ba307b0047939b9ca126337a6f385903072a25e1d50ad4f90f23fd6d0174490784b1f02f272a94f195849107c16f9d087120765d6af44f740aa67461b2764b5a989e038b4b8fe99d409159035c6d1c06165d4cec602da1faf328004ff59ac8dfab4cb7004e83610018a6b4c5b9938109fc9e8823b21ff341b7f8664300bd524b337243d5891e323a4798d03b4e97c2013ad0ddb0e572cc1e49e844acfaa7d94dde230d26fb80aa3350a1dd41642ee80d647aac84680dcc4670de9f273982219b8024a492a468a62105d3c65064f5d608cd4c40c47178e89699e75261727832f37f4c7055a8a067e61ce24daa2f4da4093ecbcd28345805944e72eb4bcbf6922292e9d52c39b765f496284988f15a0202adf64125fa2ae5fd0787d291f4cbd2602972c9f3a5537811d654d21b708a36029ff4d90e972906c744a9913be4e4c7dcfff24ebb804629cb44a01c6da1c06602f51f85d4530165aeadd4d8832bc1e16a77b073ae2fbea7ed40c571b363e8b601399b7122f3c36d3458f56e6a1d34f91678dc0375b59075ed64088adf520f080ad94a58f9e37b970d873b98042d703569d8ab862e951738bee82299e0a6ff2b0e55e43b6ba05fd7a5685a999c269d53a083bf33a72ceb7bfcc3aadd2a315b8f094525778aa84ba792d23a02c17530d880bb79485219dfb93b14819f820f4f210c9e1bda66df68cfde148f16411246af7af21263179dbecafbc6f231b3a77fa8082942495b3679b6cec3caf3f37fbde7b7e05f6e6eb32cd3aae6d49e84d500b200f4d9a25be645719d63800b2994f03228f5e4b6e76af3376bc621f02234de410d4bf8447c969424a025e2e7c98116d858e3e4e8a94d1ba1c22513dd0d5ac1b6088bbe3f482016a4b39ee8ef345c979d7ebdfec33400e2bc6480826ad0d7c11e0d2581fde37b6b16d429145726e19c18d5d80817ae08a48eb6ce0ad7de4c64c3062c7a12ec2951d89c4209d7d969c0c27abc16fe86597881ddc5eab2e89defcf02c791cb3c8910e78b7105faebf2565a55dfdcdfc81538e99defd20aadb21efc0e1980390a08ba30bdef37aa965be8e733284ea5edffb290547ae3498aadddea0a2ef3e22d58ffbd48ed6386436f15c6e533b73d7e92cba4972c9738aeb64bca01f739b840eb04ff4a5a8362ba5135181694ef611d2a1edf7285714d3c0352cf0ad848720362471cd18c0722e3c4cc8eceecef593dd837142722f0eb18391a5a0eeae082e22bf6904acc0e3ff256b8c76a2bcfe4665f4bf5014191af680bb43a085fea7bc5166f8da0543e43f8c9276ca25d31087027c37391ed39737bd52313071974d1dc26496fe65d0a99d0f1780e9563955ed44bca504bc3790da34b4dc50dc5e5b449463f7e20194b88fe8073ad5dddf9dbd8c59e3bb9fd26dbef4c498b0e1e958ac8b0dedd67534c4b5abfb60f60bec260646f222842b96579a654a3150003f2b987da589a47da296ef69f50ef5c6fa81a4daedff30e04aa9650d2cf3008dea525fa94d5e97edb71d788e6007377e5d392a4caf52576617303497120cc0dad55de73682c586f8c7324a63fb0d9261b7b69d794f947384f4d9936bad2a9011f525bb410350b443374cd2a8869cf8f18a5ae7fbb8b39ac260e2ca14c76280291fc096770d3b37fb1540a97f5e6d2ce5f6e291ca3ddb8bf37121f7f5983050f0a4ccd633146cd1fd1860a92f93f7b4a362ce90b028e1fdf0b6a33ee90c6fd0da9eb1d3458ec385e5a7ffcfd3000dbed2f216d1d57c74928a85fffe3e12be8028640d0dc26b47c12edb00ce534620b204ca3dba48687c1e2136007b0849228099f588d711be7d5d85834ce5b9e00da4a9fc0d7e69c58c41d459421a5e23211000fac8d58064ecbacef948e83f63783f6ff85f859df46cf3ee797d515d0d892000fb3e896069c069b55c0c85845adfba5fb50151f2b5423af76891711f0843209d47f2e8e3155881fa7861ac7e65f672c5e650d19c36d9db39d3d90be266f290c17e751f5613ddb20c251304679df44ee6d6735645f696a30eedce3bc4d750c09255468151748c3fc3d018e4f15f644f0bc36508bc092f5d90e8235b5dfab8e02b67c66ca632ef6da626a506d44a8be4b7e98c48c32c658e078e75c1f3ffd2508ade8faf3e7f5a44c9d78b4e764e91b0c6bb276bfb36eba819adf602eda32d90e56c51d4feac2d667c8ef54531309f1ad12b8778aad38f8bff9d9393cabcd73001c9daeaa52e8a034005bc256cfd10a75874817ce8f4e9e681f1e018326b6470d -generate_ring_signature 1013e6d2970665fae054a3bd9fe5e1ed9e80e081cdb6ca240c0f517f1b415cab 0af5247217901d271584c54159546c10a1d682cd243e66a945af30b5eb427cd2 2 8eb838cd7d2596dde1caac7f8c29e081333098096355a3888f6cef9ad54b4c43 ecb9686dedf61fc294b3c96c56ea43d6e57500ca683d8ce93e4212bce07e71a3 f36383ca3516a2e053540ebabe435e3b57cebf3890478f221c4aeb0b8bd4e30d 1 7d8cd6e679d88936bf287a6962f480c13b01a1d64907097a5796fc0294364900679be5616fa52df84d4b681b89693f12267445bda43c1a137b14b07115ffbe08ce0713fa2e177cfc9cd7871c6f5e45e824b4b3cbf55a4df5aec174e4aaa3ad0775f0023282c33e861124e2b796ef2d67eb3f1900514732f639d448c827ec8301 -generate_ring_signature 0b5ec78693ba8b68d4c52d569772840dda48ff5fb748ffa833e5392392c55dd4 be1f92e110606c4de9745e00fcc25187beeb7caef967abd9c355cc032245326c 2 3957312d5af6b690f318ccacc696d12ae70a51a5e59169a823de76c16418908f 4c4a89e48bcc7beb356d78e9d9455a67c53f4624c53254135d0f665562c388f3 6360f4e3a558da8015355552ef2b95fe491529953bb454449caf53a3169f6f0c 0 bb3f8230867bad4df30226744dd0edf927ab22776965da611846dd02007d480fcf9aec9c337cb31b7fa4cc0af7c875ebcdf2ab74571edbae5b17d02e4991900b7c9646b6ca7580ed361eb066e5d1042e626cef8440a71885342838bc7fb3e601394a865d0068bc334da16fe993744513b1341bd9f96d02791df43a59dce89109 -generate_ring_signature a8ae168867259e89dfddb607d8121f78d9bd1afe3b56bdf7f8d44f328c407626 29c15d05cf7408ed0252e1d25c47b8fa0c34878017937a285dc037fe48c3d9dd 4 38aafb4066b703586c33d7c055353712cea2a8fc42bb1af683af4d52194ae229 e258caa68a2c25529b6d845e6477ddd241646d89135d3e860f9138582b0e2156 fe25ca5baae2ae89f703b0d17d0e9ab8c314eb69ee1b5ad3ccc2f178f7a13a89 ff91b9cb17606444470257f1b2520fd7c7eee2e9f62fa284d96c423fbb32c32e ff3f205bd350797a39e072c715c24902a909dab05fa8b7e3338d2ef69a85fa00 0 7321e749d710b62d6e86d55228fbbdd0b83d46e4f76a8d78d8f0aec1fdbbbf0726308b06b760238bbeaaf24c39eb9e2af4c91834830ae752ecc2bada677b9d04965f63a9e8a14d1ebf50ccebafa4ed28d078949d0d03b57849c8f8237c9eca033f0b1f0b40fe3e800fb48cf2964037aec19704a5f63f490d5c191f84bdc81b0b177a9804c76dc1693a66271dded98480c352d87e5476a77641a0d82c3ce57704546d2fafed736708d7352a70581d7907d53fd0eb41da6f8f864325737fafa3062815e8bfd095d79737c66c7c1b710aeaebafee6415250a96e64be58f64aea104ddfc33418f15847ba685fafdfb002cfff772889490a9445ec715937f45dd910a -generate_ring_signature 6f7c9d02e53d619372700dcfbde7105963e017372fbe2918ff0331191453ff60 11bbdfd15b4d972900e7d088fe7971daa6acc8e8d97e2414e8f44fa0acce27f7 217 a9d2c0e8a6674d829f4d758c0dfdc0adaa073d9ffa6456c2d06cb7ed9c7a7359 ef1c7c48c75f1ba091bff82de93d8ff68f7bc31da2ce87c8569e7443d359c5f4 044e3b84c6acf627a821ab990f7085c93abd290e5930767d6a177982d6ca67e9 781b4c9fb9e32d73d2d1b963ce415c90cb1844416f7dd13280e1146ba1fc4e5e 05d8c1d8841d0e5eff67f665ef58e5aedd799e1b9949443cc414932ef536fd46 7cf07260320c63fdaca80f7b8ff9eb61bb4f9a9d3a27cbf0c93834cf93fc259d cbb0202ac483c7c3b838c1fd4ca2f1bb47e91b0fbe6bd10b1e45da586d6ef64c 25ddec4e59d2ee1da3a0736e7aaba1969c8c4baeb25b45f7414ecc3a39f94e98 0dcc6715cb35ec3f1d7808ff671b2433657e4384ad77c6e24f3fb40ce407ff9b 207324c43b3cb524a6cf244595d44d9a9fb6bc5dee499308b95ae8a226feaae9 32e51b3211ffb05469bfaf5366e9818fad3265923f4fb734acfdebee7c183342 f3745f3cc4453c17488dec37617c06e24c7d05bcfb9449c21e93c310aefa4a44 55cca614e77dcc4ba78cec6c46f38d3d0172ad2da45c4693733b845449b7211a d01bdb20dda9148e2ae0b71cce5ea5914848f79e3072f0b248ec7950fb0cb32d a7b51154c60f197d723af16e4e0f324a901b4b26393a71669d0e5a621b6a4c26 038d389b297f53b1d35e64b92e579422c93528a82caa3742747cf55212a7d9f8 9255e412cd98623b34f764d2a55c443d3675ddc1d67e70af7d3903cfa80ea18b 0dfe3c5dcedd604a467aa13cc1ae316c1f077eabb02803dc06de657126484f3b 5ed0e9819d6e2d0d113f1edd978e543056ef6e4bc431f95f2076d0d2fb475d2b 18ea161052684b291829d5246f410d2a10844b60733a978571e2aac29b8498f2 7e2e83d2284a89f30052c7d3607e88bf658f9fbb1bfedebc419fe3aafcc484ac d7d0b95c8140094761ed4460afb84bac314b952bae09bd0031961209a56791e6 76492fbe42a3e2e00f6b98c207bd06dc8a2b9211bd27154f2938fa0e81d54589 dd2d108646f82c50e2e1a84de94cb2bf50d660d604182dd87ddf620ed3751c7e 43b77c3b8b7b4f57657194ea5c26858d5d60849b5c2fde7b9e2db9b2593959af 68d833ef2995aa4b3ecd512361d4f99c912d20e152976be8471b18e5f82d7a5a 80ea13958354db6fb72d24ca091ccc979e37e8f5e866097a199f726a15ed0df7 568bbc6de62162897808b97c17cdcc6bc1986534dd5f3e48a8c39a0c2a5a345a d052a818e54496d2db412bf4ee41ed0152510bc19a0863a59def191336be7393 e9202d3178b1ba5cd5de88ae45610a543418e844e9b99b443292218dc1abf21c cdb6149924a134413884a183d5d5e4f88d37a7c7a85897f5d7349ffc66571910 64066a9b8f720038d5094cea5c59a4d87f09a121bbded687e2913561b7462d3f fb6d221ac1fcf65a9d188165549d595bff987464ff5e99ad035deb282d42dc93 a4ee1970e64f9e52e882efe800594e98f29e8aecf4ea3b662f95fab5b9e94447 ff21f495bd4634f38c0e1e162f3946cc0a593f7c738833cc83737a3cbbe2b808 5160ac5ea648b08a8219878f0fe2a2dead686c3ffa490bdaf76f5aa70ddc8474 7d432689aabb6ad473dffe72c3fd10f332fc667806187a1d0523272d69671da9 17e4a72c95f54d277c7ba2bad16f634ee82bf403c3b2af475acda08042017fc0 7c8d25f7cb1a3c7c76eb0f1a0afcff5e0acd6bc497c99c2aef04284e58cda7b6 a2f0a4e90c503240cfed6db5ebc3730fbe73dcd04e083c5c74e4e992b4bfcd19 61731b0a82a84272a3484abb794d41f8aa35f7ad7c15eda62119994fa82e1be8 9d0cb6dcb296c7a1d9f315d9f3cd1ee319a32cdb26f6ff29eb60864d1653435b c57d7d5187cf78f06a2b4192f4b2af9e6e3b5323f1923d520d675ad716b67e7b 66865357df4a203d0365078d80e8393f91157836e16592ce129aff41de009be4 6fa3380a808f73acf4636741ddf415cf2968b5c66822a3d9db9231b981319118 4705319d29971da7992ed65e5edf5a5d4ab65312ee5a555f92230dff85a8f6a8 84e4c913f2f98cdf68913c688ab2660ac1f77034c2007fa33bce2832450129b5 a2d9ef7d02cea31f517947a914a9731b731ed66c50eeb27c152ab8f3cdbe7f78 a4ebe351b09a08e8c9f508938034395f032d7992a67671a6feca145caa354cf5 584b6866fa954938d5a134b840a801dd0eac1a17c24e45e13f4566bde3e9a140 5abd93a726c61acdc9505a30eebd91df1453e1d7cff7d2ec334d986b08f71672 a6931ec1d2b21862f1cb935fb0d1b52791d8f916c2afb9937b51fa2317f493c5 8209bdfb0f4967a4855efb9c48b019d21ef2d17d2e768ec86de7bc57f36c916b 301e708d9fc079acf118ce1623a4d2eefd5ae3765883eb7ae06f0df59f0a9225 7da0a633b9c97d588d0c4f011db498f7fe262f15de189c77d0052e522e624f3d 69680b2e40a4ec561e8a753bf932feed7e401ab505e55305226121dc14084cd8 1cc70e12f5b2cc86a4c5fece7524a4bb842a4ea57944518d035f3f2f68b44b47 69a0efbd545b2662c8a6b04aeeb37fa58011570dd1133d95b47d544cafa3c81a 91ebc540692be156388b17ec9936efeaeabfa760bf334286cfce9ee9832a299f ffd00880cf9aff6051b7a16388171b8a2fcbe54862015476c77f7b3fc46d7086 c95389fa96f525e20aa521de82876391681fc1e62c92c80253f609775e0ad584 a72805faa42fac4965162a569cfa86887b8e6df3f9155d6a87d9f3c8c03c8cc9 e4f035aa88e869257fb5938c550db6d3400f4a1ad9338d412d04c2f60ba7c11b 9284397d95cdba29d6ea6df1609a48b96117cdbee19e841be496b79337d63281 012c09af03e3d3d7f48221672c5bf4e05dff3716d744647807627d9d7ed75e52 b9c9d1f834c86d1113d8536412829692e6794731e2cb85934f3add8ca5296295 70f4428a1f36c543107dee9931b378e738af47b4c960e2360521281ed01db82b 2245991898701a04d0702da058f30eac9f2eb869566bf5b0832f41449834994d debb66dadd879e7875cdd1fcc2e0c73a7164e295829ccd0af5ca6a44897e4ddd 3a094f9d7b3d011c97897ca7cb55c1e4aee5d2833ef5cb56a9d7c1aaf043e359 20072e748ddbda7f5a4f6a9f30c4e7a640997e5ecf97f15bf0cdb068c14d823c e5dffd9502a42b8638da63500706cde697dc42cf941636a3775d4ecf5f11563b 2decf150e5e635c47b67ff2c4c2f1c3cb74d15724c050b985a0d37e01e067646 8ab05fc816919f8017dfad54062e9f93d2f8cee3c011cc077e442df75c3ba0f1 5929f00cbc961bc693eb17131b6f89de73cd2137a6112d482346fdce4da3072f cd245415fca9190b3f5fa2b229f8ebca3aab2e084f01d0638afa99edd2c817f4 a71c52bd92541494e4a389f667ca59f34923911e17ef4ba923a04d238f89401d 8ae0b7bba43ec4b225e4829ffa00d0fa6f995f4ff93ce9237c645a7d7708b078 3a55518cedb46f2b1edeecdf56914a8f2de3605df30995fb2c331100dd73063d b249212b901d73152d5d6575a1ef5f27079ee8a6660c6dba3f0d70ab18079d5d 09cece2cec55abe14f6c5d3681e6a3da9e3bd75442369ace34915c8a725ec880 1710aa52ba925feafc4477ca73dec7dc11805e393642e5a444442989be1def0d b993ca971ea66dad353f6f5c338b6a2c8aecef15bd79ba03838a82c845172440 c9d8a3bcf7523dcc38d1a5ab4ca71c999310589e9712f54d8bdc13075d9602ef 12e8d6c4b4ccbab7ad98baecd77ccc8a1666474a1d9717e227750f22d6f46016 04a3246f91e86c0cea76b123bc08903dfdce13e5bc1710c656cca5d859148cf2 92494c362575eb4a19462b1b14067534a465d331ac233bd770adc155a0e9f805 6baf277853495cf5482fe08582d79a957f70e0a878e86bceaa9af967a3a1afdb 2b5fa12f48497ae1fb670b936287f03f1c473281dd42b594dbdcde4079ddfbaa fd9f84887a617768d79b3492844be5b8da294e3ea016c1e51bce26a36e72577d e7da58d7aaa13fea9936eb28114f9b7f0ef4997414b569aa895500c0afab20b0 5e0faad0e8d55ecad29d0ac6316e17bcc9eade3bda7aa3006d03a2e9fdef7e08 50c555404f87b93e52de103c405c9fb1cb3c19586a0b90422061c4dd513cb58b e6e9e2aed640f9c58d0e2645f8ff308af33d8c8e8124c0a61c8ecc9ca28dbda5 959d3c613b4151b28c1fb35569fd3d89d1e6d276af4030b77cfe5ee22ea7c11a 404dc708e03dfe8f28ba1ba88f2e5db8757a3a67edf5c430ef6f889711b1971b 12256a5cc36afdb2cde8d58f6b6c996cb4dbb3eabc9ef530f9b93466d53d99d6 1e423eb530434bf15bae5f8a7c2bdbaf7b1c10501a6609dd2446636d90a332ea ad60a07f8e4093ee17b949b1deb91f62e1ab72595402d45062978df4c3266819 1379f3a747a040ef4e5073b3ad4e33e8c31902c87dd3d61e6c08c94c979f78f0 33efe37673f13b2ad5a982ba809af30d7480efb4892f14364b1275f8cf4fbed9 d4271609be30b70ce95cf10b2c25bfae6eafe50af6dea2d8306a760db35eac85 c7d1569541855b69bf0546034f9b48afa641bbd532d38388e96cf1768b9af4e8 2aad5b98da163fb561c7595a5949383a6b9a0805f8b0e2a8844e045e11cfbc22 1358ce01434faf7c8e9b2dade15bd514c7e92c6dec4855c2d10683f45bc6095e c3e37be5881f20a1ab5ce33605713ace69020f4d9969b735dd84e6aed56c86ed 846c51c4b6eeaf711c7aab321c05262e21d1327d367bd5fe08c0e92be077c490 ece46633e4e417898ed86f20cec69668d6e04f5215f80fd8d9b7fd1f310c337d f104dab4913ff4d456ef784b0ee10a7bd79f6693e37c670bde2fff363f2a090c da677636d4db6c721915564b13e6b53d12ef075590f76dae676e4c53d67410dc 04a4fb6c35c38360ce5fdc268739e2ef6a2beed8ff7bc95460d62b5adf68f8dc 5e7559290137fd3d83706e5e7c1121883c359fedbbb94c8ca4b2680733d8b0c6 479b7e2475c7de034efd81859b61288b803c456684be12537de7aa99b5d127be 402fd78886359830d6839113804b636a85d473ead1920c9932cef4d9cea5c6f5 5cb4b5011651d2c69e636f81ca0d841807209d841a739bf2961d1480c4edfc55 d07f7d8beae6750468330d553fe1978cb404f4a1e2488c753a5d0749ae524636 7295ff8a66d12b4818b5f35d91f2b0ad70ccc7443130792f4e484a782db7f8e5 fee7468ba7194735b76a6416aa03e803afb3bc66077f65a4fc666a8670813a73 b1b2854a6dbc4cd826dfe66b8c34253af6c3860502bb629ebd8d4fe071fcd1a6 7df1fe7319b51ad76d1d6e9115456c2100b8fa74e46c3059ae65b32c756464ed a07ae5f7e8c60a6ee0b6bc0cd2b4cac1e60a964e98b4da967722bd3d03409a54 2c89e51f3e4a9ca6c0d283ebfc97ed96edbf16fb0f327548617e86b89e5f8dfc 8e6b6ae609a6940736bfc9a50a7b299b0274d9ccf03351d2a8f7694e398abb2e 2ea8b51ffa4de90f3cb2bb07ff59893aa13de7c32b7726ce1006dc88317161fc 904a28f7389923ee6374cb5d32e08197e899cfc066c96682cf3b0fbc3b72ca1b 729ed8fb818480801cb35ce3253ee767255cac0031cb70b47ce4388e1c28a3ac aff0ba9017e8179fff3df916b714c215b85d094907124b4ff5899450665b8da8 27f59c289add2c9470af149c956ff3b04f5c6388926b2984c24e5b6756c847c3 fec3cb7c1fd7fa60da89c019ead2d966e06fc052096442d3142226ff0fdb82fa 42d091f65225d2e064d979404ab5754f7ca4bad7c51406376fba94c4fd0b88d0 2bfb9b80fdadd222bb08bb790e6d188fc54419aa279c9b21c0951548674341bf 2916e5d0c9a9138cc0166320366ff1a1f7c1f491a64159f903e7a2025441ce63 405628a15b6d835366899c6daa9030494dd33216415a88e6d9645aed8c89b0e7 3b2656ab204b8472eaefa1dff67a0f1302d9aa9a1ad8f23b5481a685c6a856de c11e154bd9f80cc376fa95f3fc8fa3af606256855b354b639b4df67b8e8640dd 8bd2ebe7318b454747d500f3833246e8a3edda5913577d9871e7501f4c4c65db a720d5a5a7242952dfe68637045142b76dff814195f426dcc20d317213720e33 ecadd044840a168f7e83a8f2b3f67f5d49131f75fe3322cfacaaff9ef68a0e86 a23c3b773dc8374a0b50173d6dd2a5b29ce66b0d1d62dfef1560462e5a41cdfc d0a8fe4d361bff92a2c8d77aee367a097c301b1a0dd518cf136f8dbe5af9fdc8 f604bcb4374ee29b3c8e4041d27a08ef094ebbcb4ed19559f7515401dca0744a fbbddb30feff43c8798c2c6ddfe3e8b19ec2d3ce405e1f0ea1d6c148e9491dff 54b2dbdc5399e72856119bf552be5cb3974ddf1a7de015d7e5da1ee02b0dfaa2 4eca7debb202e342051c049226f8cb4767442f13e712d3a1742857e3188fe879 829dd2aae457f9465995c79a10cc1c03488a95de1b8dc12ee74ca66325a7be88 ad6d9c33f993edb38182239144b58430aced85e701780364ae9faa243fa7a217 e78c8e16cc112e8b526ae3496bc19f9fade6fa6778173a88dd0bc18f7262e2c5 31ea3fb778c29313a09ce9665a52949dd6ef52d9fd514ace880300a6f2dd1f2f 8615a6a3d70967d960dc0bc48e8ea0ebf59d8b39a5cd8a99266288f4e8316284 65f76789b071a4e9354db6f980cd2c35f1eaea62e32f34fa1a7e4b341606ab3e d5f40047e69e7b5f819b276bc2d277a11c9937813d5924e467b8c9bc81d215f9 355b0bf3c48b6f3b738c8562887e0ddf6f7fc04d1456d202e6ba02014a4c90c2 d137ab92a2881eced4293511a819679cbbc89008d21075ee21c6bb5b046215d6 86332fbeb6b2f50aa906a49247b09f207c425f17f0d2e7a8a2deb585bbf0eaed 58bcdce587e6eed7609d388a55b05e4240a61b568e9e5141591ce8629c238fe0 d6097f96caa48e380a7ad94df021698e525aa113791a2b8be91a7974d29de2f0 a9cae6409dab5099d26ec34a41f9d646663cdc941ef0862ecd384d27cf2e9a34 2751d3a661a51328185df4ed66658f02393f90c3350eda059a4c71b9d1ed9f67 a989804de21383e46dd2155ec0dbe2f5c127f7cd97a127ad4b7c76f017493f87 7c337941ed522a8f61149434e903942c595e0bf68a19eee809e500395ecffe04 d531d3a090f7b9feeaf254438852eee405bf412d41c053023716198d98f8d0f5 625cecc397c1b67441730cd1701986484c2b24b5c32ca61321ece2c4283c6a05 9f0fa0d4e8999b7adf27f9b31824a7c309b23d2be2ee83e851df8c2b660360f2 407b0bfe39971b9e0037dd79db93d164d079f8e00dcb8d4085dc8b41f91772a9 5455a1f8e7e7cc1374f610eaa089086fa7446610e74aa8bb4afda37715091f27 cbcb2b6fdca0f6e72f741e8ca88271c52d913d93784d69e70f16d6da7477ad98 db1e3d9df06599d6485e2ff371d4a600b43ccc249bbc6309c3f9a36de1f3af1b 406725971985265d7a2888e91b955ed40f4e3d1cedeacb710f8051c53cbaf49f 8b28e249da7f5c9280c01249572cba2e6eecc970a93bac53bcb4b9c8e6e640bf ae00617c7b499f288834e52a64504210be9fa398d113ef39525d6a85ce757c40 0677a6fd22fc12cf3dcdca106e05ee8fd18759b78598eedb7e176ac44d65e2d4 8e60bf4afee39109eea9edac28875c31f30e6e280d898a00f796d85de041eb6e cc7249762d8dc1bc2cbfe83e5a3a28842a48e6bf29c4e78c9f115cfc27bf8bf7 a6810203173aa88357dfed1f35beee6d73e38da78a7583b329810a06c46dac5d 431f2e1fe54a13aa3aad164e339cf6463b8190db5f5632d4c096b08e100246f0 e03c6d93874e0ec2c8ee0d238382040d455a55dc0923fd0b2afc1bd1f7d399a6 73b62b31b6ebcc9937dbbe4ac6907609a68d6eb4feb51f7e70afcc912d22623f afde6010128e5d4fd46a0c2afc6d12b4216b51e092dc050097ad9cee3b6148c2 cf660b8abca78a389af858c41ca235fa127cb86098964a5dda309ace3ff34c32 f74fbd3f2d3da14032200763a61edfb019f6a34492184702cb800ea889bf37d6 e74dfa1a4004ddfac92d41d388572a5f255982d40e207189e2c1892dc5af1130 6bf7dc128af4e7c039f52093b80cabbf0342f923cb372712fb012edc469607e8 ea3f3eed317815434b64a8c02647bb49933a341363351fb463198d6bfdfe1ab0 bf607ade73d9fbdf67d6f66f4757bd85f8084a1de1a5411217720e2875b94722 91d0180661702425c83970e04fdf5bb86511516c400f6618c9f66e60fa79b70f a4175d3185abd7a03ad7d139907322418577644a7394c77cda9092e937a1b401 31f2b49a2f3df0b3966ea20984843e4d40df5d3a115cc71923142d2cc9a5a339 12cbd557508932dad07402d9fe599cdf290d8e13404a7f0bbc30cb0e8f6fb051 e87e1757f7257a30b7fa51fda581127a5ae9df6f3a63c79ea0592b63dd431d51 624357831afe4f7f4acac70c457280b1e5e3d3ceb7d6642a7c5062f71bb73df5 beb1b43f58b59c7926aba3b5a873c614d008ae6220a12623680f2e7d571f2931 61ee7bc796acbfa157a882721938f4c6a38ea7c5780bca69f09d11bdbf4c6bcb 6be6cce2d2daa1c1dc937f44c7b290880f72e849a178f0fde7c451bbb16cc960 c6701e6389ea37f45b9faa73b44dc412c43e15f63827f73253251048d0d1aaf1 3bd90e4ae4ec59c20955a8786436fe323545d7d76dd86e977fae235710c2770d 86e7e8fae2623b5072bee6585705ad8c6a35467053011a29a2902bddf28f2e97 f975164d0117adc42dba23154292e0d9907eb1300524f9f146430763f27fe1a4 b1f1177dcdb89c3330fe6137eecd4e3f9f613456e32db51fa3f334882640166f 507565d88391e24f45e3219a723a2201a02c40c5cd88b3e16e7ab8d36d3f0442 682acb51bb734a6c5a8184f716a0b2bc890dd14d80a268d9ca697cbad6d2aef5 d6b05b529283e658771e12cb8db03e805a6f377515d2ee11ce71bee3f22b3f58 a3b057a1933ed0381d0cd89166e7b2cc8c3613f9cdd92cd109d86b4ffb842490 bd337f0b6ac1634f12c067493bcc741b21581dbcd5eecfd1386b4b50803a5beb d46971f3d8b4f9fbb71a71cc887646ffdf1ac95e40c8374881aed95f4e60d6e6 8a3249e79c08af225720d75c7736a07fa6cf863eea4eaaad647788c43b8f4ba6 f25ff9ae361b090d43b63274a411a1b5d4536307bf4c2aa857c3d515ef26335c c306224eb6323a16ab68eac94a38649236d1124f55b7bdec67c8961e411f1fc5 fbdf0163bdbb52808c79b0670c091b39c906583b379eab71153db54af0fa64db 01834dbe726bb20cfd6e8304bd14ca1f2ad2a92c6e0676640ba2306661d886d8 163b7b5864dea3f7fa0a7205004588c2f58326f0a93ec6f4ed42db249b46552c d4c93ebeb66d6eafebd24ff97dd405b61dabb318122177de4b20916eed1ddf99 68800a101d30377b7cc7ec21e5e5ba8f31098a1426f09c5713c59838faa86a1e 697c8ff36a824ab70fdedf3b4890e0e4d767f4894c7a4fe02753715e4e5504b6 169089c3de0a304c16958b81bc597b22424e08e4b44effcbfdc53cfb7ca32592 f7f4aac8edd8081a5d88e1bf2a1d8af7fc495f279183c1cc8ae9c2a2559acd6e 102abb0de31c22bd64ca47ae288a2ec0945640c9d8736361c8e7e5c70193bd6a 44ad04132ef42640626b75cdb2e4aef4bd6f8cbf52b5097db6ed3d89cf980c5c d97cb6a5a0a76ded4893fe31663fb9e463d6734281adbff52fb386a34c14f60e 109 53410f24a369f6c7747893bf4fc4787728a6345d3d12f5ecf296d275178fe50d1a0758473f78210c36427a44d8b18902bdc0a215f13be707d575fdd1a6226e0ee93b69e7dfcd31ae3657193c28a56ad6ecc6ac131294ce9be39645a5c5c5d10f3463a094664c5fda344a70388399ef736d223b058423e77cd58a535e39aee7020f636498200e4a8d5f3332b0f91f2e37c7e7ead728fa28ebb282344db07dfb0c416f4506fe5e81f3965b77acc0fbc01b30f5a0e96b22f26ffcdf2ddc7cc3830bb8997597dd2955ec04ae8991616e8bd61edcb47268800dbc5cd0091df682e200f3682baa7f226ddf5a294c5c9fc515c792a0ec51545ad1292e5933a115dc920d984d5d414fe8951d049a2dbd3e15b631eb5dd25203ae87d734e23fd6e02d4405b91148e04cecf42766318b9e05f8855f1f0a63bb6905fba6f0e0b02c3c0fe0065c570bd696f2a7fa71d3c3636172ef8a1dc7af872192c5d38ccc2e6aa5a85a02163b34dddbcd21eb8cfcd07c26880010cf8bf13d65583531351c5984fc809303885a8feb8db7a3ca5b8a0fc3346df6e4ce4e385d347bd2c7fa66c52fd5da790d9ab1b348b110110975b83048e05c79d5fd5e2a18b3857041f5174353292c5a0702b460abbf19e7b7e41931867d410f961fff64821d082330abb66596dd01b6035bfbb08bcae268caf1d1b77282d3cbcf1ca91f0d1fa7ecc73aecba1b4728fd0e16004994b587a72636d5a7937bbce4d1a716e7a78ba6e174bf13646b516aaa0f9905920057dce84779aa3c69e82f701d0a0eb48a8264388ec3da81a18279b90829a08a4a73557f3daefb6d0dd3f5d21540a87b237060983a893326ab3e042c064e01b442534a590b3a25b05664869f4010dd6a543625915f5cc8574fca314402773fe5c3a1c547c0d6b01a1e563f9d0b9b6a281a5b75c2abd8bae9ef2847a20612d5878efb2f351e68cdba8c417959a05974bbcb21f185b6cc9d5a8fd308f806b34e677089efcd76688dacf6aed9d269aeafcf235fea011cec2768532621e10d7b1dbe5e39937eb2bd6a1d0be9f8ccd9569539d39efcc0a6391ec4ccffda570ae723b2c924394f42fd1520cfa48b3b39390e51782ee16f91ac3d7c71345acd080e5b0830f548544f140822d1ea63405f883b9cbff2fdd272df6e139af254ba0482d7aa3787f6b390b031d37041c8cf54822b8ff80a592443a9c84848825a9e0dc3c0408444bcf0265faf5069125fef3b42ec90237780d385f8ae92f7d4bb7b0a2aeee2ae4f20be3a9427e23b21f73885c3a6d4cc7601dbf768ef339f3f6a9e05008ceee0e177937ec3fdca7250ba3a1b624ccd74fd7a77358e542a5881ec5707c44ea95fdc4fe392f0d085aa855b647a4b05a1feae6003120d86c0ae92effc0bf137fabb93cf1381c5b9a656a11d6614e1ed549c3ff645696b70d1ab36813c0be8a0a8c926fff0e0e004e8bfb6b31deae1ed097bbeca807c5cc8707650764406acb4fa685679407edcd6050c7cda54f219f88d109c858e529ff9b5e008c26009eeebc7260edb651fe03e0908bb8793b56405b305f539eed577f070a44aba9b04c89cd94ee1a82f5c164fbdb1ac2258423bb7c8deeb6c92235bf24d16fcb69a0fa0300aa9fc528bdeb32b775e6f3138cc6bf28b2719e42acfe7691a422a496d0db2a475533bce0ed1446bdc38bce6f559de90460674038d498b89abbe601be1093a78b74d12f28782e7560aa64137a0e2123130b9317eaef0054576915177cf0d4062a21f18ceba224d2072d4617eebf89d707eafe7621226db1fe8d4e9319b0aa73f8ab6df8e8453f06a4700991058da7dee970e3c8f25d4e16f746e89a66b0601a59002c0ab449f86fc24552828211c25ec091f6cef0e516099ad88eac24004623992cb59558dcec47652437e2b41fcaf8e3a8c3b04fafc114ebd8c1caecc085e3cb08d4fa394267ab8c51621636a6fe87ebe0aa3de851d3ac8d38608da3d0844aaefe53c7653db62bbf6653b4eeb5f83f4f16c8f5e4148c71442958b85e30fe7c48156eb14a5d25a68e10a369a7f1e07015b9c6150312b64c2085e46ccd40e3d21d8422d895dceaa16a583f74ef7c6b0e8d6461d0dcc9fe7fdd17319318b06dab88642aedc55e6b54c2389bf8b4560ed2e297ca3e4e19cfaacaa89f492e40f53b4eb96ed19d54ca826c534de2df03e55314097c4f8ca856729bc8d3706820646915a580689401f806f105d340cb87cf010baa1aa63ccd569acbb41666bb00d680bc6ef8b7b9a3fb1ba586126eb86d807df5fa3b920d44443cfd8d9f86f340aca8e0316ec1173b984a9280bf5f8780f05293fec0093d84d60109fdd82d6990b5a3864fe52d3b83718561f1a9b79347e2d2dfa8e7ea8fdb9bd8c730fe4d6910b22efa9fb7b527b98b09be10fe5d129212f9e672aa2bc2e43b58053eb1f826c0951a429ac1cf5b15dc4503be9de385a28841ef927ce132aaaca1630e30615ba0226aac3896d6259266b90114399774ad60d6e2dfd50c231ef3f6fc8434d0f2500b1074945ff58c1d331dd52d1b503f76e1718351617f7a043295c643262489b08658653cf709c279dc5bfd37c21b166d90dbb68fb1ca8112f2f5426ec2695d20bdd93fa9dd37161fe05cac19a10c649949c5be41c98a6b03fb07f3e84f035730b6b30720734e2185c7b49a5082ba6e76131997a100a5c57f8394457d60a594707ff835fb3305be29deb762383e8732525439e2590c5eb3681438eeb5b1fbb390203876f72805fabc9e4f7627106d2950cc2cfdcac7d13ae6cdf4c69fa8a43d107c3fe20d633e4fd35d3406f14a31cd24e717f782b7e4b1a2c42dc3129ca4ab7020dd02a222ba6917780e67728ad4da7ae38ff2340b705bceeab3f6ccea0f9700202eba70e47d16d7262e1c01fd8dbec17f9651165e36cb3a826646d3d722f050a0ea89c55335424763fbe8e5e53c9945473d4b23ae8248174b5ca1c4a23c2ac0f91abaae8d736d2fab66bba605492b6954cfd3d7bc3f24fdc59ad598c4dc5920b7a291b79d255472c77459b8be1b8297f33735e788b2e3adf4838ae35b54d200a64034a2d8248240ef917d1bc42a7ba26804bdbbdc7ee48621ad724bc4012eb03a3a48ddac10a5f046452aa7050776e75c3bde3ba90801204fdef6fa42ce4b90dc2fbff8dacf6729066741476c65db1495db28ac64c69c46dc042be50ef048c05d5508c04d22113f3f4318470ffe27518e4dc263b5b81c8c6c7e303fb577fe9050da8aeaaa4da8c641db5e93cca808c12e8e0e05a32c8db0ea5d06db4cdf0d102b9e9055a95c7cee35029e4f019de814f1601842b0c8e4d7f0a8c5b29e875690f6bf88591b93aabb65194685c384f0cd2a1894b08eaa93d6425ba8de06a90c701e1e6e0191c913d9605e051c9ad517fa109587442c89a45b3eb7bb3ed5bb727055df35e3395befe9435187f8e9c51ffb224ccd38dbce45f66163da6c5e01e7d0701bf211519a782da0206c7a87607dcbbff872479465e336afd6537950c774009aee5e4c0dc342c8ccdaccd9913237c1e857b4db28291321cfc4e79d14041e209875d8b2656035fc2f237b3e8860d87ed26c37922e3d678fa5b20e03bbd129908b14aeb8ba0625874f69f158353cd2c2218f10d1e388d8dccbbc541cdd8b4980da32111ddcfced7f3add704053ee5650cee5a602e573af4ef142ae15da5a96a00c31889c35106cca6d623236d41c606109e202ad2196d10ee083cd150496d2d0aa69a035b4d57b41bdb0f20b47444d94c98edf3f303829fbfec3208241428a80eaa8a07bca7892e316eb04a0bb7be534f8ca6c51e226f61643f070ebaff77dc0a3079fedca87e625d198d3b862d1503015867074edd11e6b84604b1a7c3400a0b9aded1562761792ae5068d40159a675bb9944f8c07753e48415d2d34d436860627e69592358c7cb3704c8fcad6c5b1861539f8f6f4ef28c6014de7c1ff7ae10919398fce8e5e21cce558e5a8635755b69cb2034b546d92ce0e6e480585866307ef7557edead8160bafdad20cd5a4f6a9321ec4641d4ea798b23574906ff7a2048118afe07c18fcfbbbebd3011fb44331227bc277fbc6f65a5e63af8d509a850ddb4cde013af73de775eea45bdc3d254347bd4fd5ff51b6ebe7d965360820b101fe358fdf25c26f74d337c28ea60ce4a9315439409775f9b01c8553743994250480e0d2e77b7e6953bbca8a34d8fd39a2a316c1d1609af2151ae8c956dc0d650f5aba4c5428b9c73628f98ff90a31c897e8b6893273dfdaf989502816fae87b047075ddcb986b010afa1f869a97a9e6a049466db88dc0ab3337307ac01800d00e11e3e7d01dd7f0253eb4637d9034f2053bc25f7c26b06a2c50f428516768a20ec03e0e11eec71f1a6dabb159eae876309c8dacbe828c99d58fc3591c6fc20906bc8477c2959c205dde6594b5d981b7ac0a82d181668bc4a539ae76858a55bb0017ca2612a2146bdc28e93e8f80836c561f6e34333a84a79c21897569ea36cb0e0dd6bb714f0e37c79e2039c20462141c690f536a385959a9fd945e691f73240c3b56d2d136684dcec3d84ac9e4c23a31378f6fc72c3eb1d2980c86428131080ba36dc7518b6f02b154927984dc3cc6199bddb040fdb6e9fc2edc4e62bd253c01ec2463bc23cdfd42e1d5d8e8188d3c7eca939c9bd90d98d79c9b5e7f04c9fa00c8efc07f884c6a3d206410b062e7a495e29effb3736ddfb5233142352dcbc60a0751d4630fa9e990a6d929da4a88f524a97b64f20e517f4a9be0b8790acf5c0b5b789199b72b3cc813002e3a371886e951341a2da298e6c7742ffbca9810bf0679e3abe693bfa3a83c2dbecbaba65ab6c5b95e33c99acbeb854bdd271a55210168c91ccde3e0f3f35843c7e4a807380e2eec169bd9c0557f478fcf19552ec3001fa06f9d69718e37dd70c8fe5ac81880adc894c5431f12be29f1b455f871de0a0e9f8e3fdc751b5f1404637c7604795b6abfc577e5e0a7fa1989f0b8f0f40b0b841a2a5be363e18395009e989a47c4a56e7fa7e2108bb6dba81c9a56ead22e09186ebcf03b634f85a0287ed6c10883803f2ab4319dedcae76f4c39df5882b709c2be9a80a628a3868d7040a59e608eaf7249f88ca72b4811d38febd510081a055b701c049d485dbc0f5e671e558a799f60388e3a0add6da97c6a1ef46b4828089ecc39cc1766b48b558ba49432424a117301d5981e89343bea8655f8dea3fe0b4e8bc3bef6159aa223934ff4789033c5c0e3555387bd2a134889e3f3d481f00d30e4dd97d163b5d1434e0e0b84ef6c81818feb68f3bcf40e6254eb8d0d6ba10f7462d4437ec6254a23e86495673f5d06cd36410cc07f48f2fba9460ead997203c2c7b6141a58eab80dd621471454d1798b6fd6d11bd039e2966b50237a39f1082628ec8d554e48c5c37ffae71b5f617e12d4b159f5ecb856c0ae648d0d75870b5a8b34cdb3dadc24cf4d52c02a5f9ef4e1445fff7fcb09900e2b885019af020cb840cd8a4ce0c8fcfd4104d6280e45a1d59b53734979471b58e2d4c3523604074dd09946c36bf7679fcb3ceb27e558cdb162a1b7f3cac00a5e0952cabaedb506487f5b388441dc0ca081c40ec153e3d0020248681023af75ae66173ff9e94e0531a1fa140a7f7a98f359859b9b336a014e13fab8db98ebf42d346e2a6efcbe04862400e4ce79b4877a61d9b8501efc1e281b79463f14b030beea07fd3dfa8f07b169aebca7837f6e14b9fa80f8945e577b6fa06af16afcbc5e1504c263204905ea7ce0455820f39ff356fd7d2ba9f58584d3767adee978515350ba4c6eb7950dfd14347879f76c09b73e88f50b586acd2cc0aa60a3ec9777b4e2b8685bcfba087bb7d5b5cec41a47ade5f9a0628570ace3ccf51ca0508561f37dd7207d4e16031be3e9de5639961f7935a093ce8022d04e68ddb994b662c21f0698c1540f6709901b28b2c6e7ff775b51dd5d33fc0008097287b28b21c4675f423e34d2779d0d4dc6ffc5924039257259bc7bbf596b028b05fe9826cb3f81ba364fc56687c50c716a74cf9e5dcb5ae24079c5fd6eb1082d17e9903ade9ac72eea1e39db699900b71609deeb84f042faed447f11a10b25a93d21c6926542d386bf3dda78df3402d73ea977ef78c888d9e96dbc3ad614ea13f5db00aa08222b2bba89a4e45f6a03dedff0c2fbe40157b4df5cbf681638cc998591e3ec4afefa84a7b2b098519205555b5ab8967260a7bf1df7467d34068c42f2a2602d0bbe618304faf8c19c7f0ea5844bed0fe02b86ee3929251e06b5075edfc0cfb536e7190feb90e1f77bdb012fa4f44b015bf8b7a33cf76f6dd593a50cd37295f40228cda58405a8d73dab0f73857fa8adfa2abaeb6ae8bf29bafd9f92170c249968c795fa08d0075d4f9207a8101c44ec1a807da50203d6df396a16be078798a910aaec65dc575638cc9101c4a7d8ae1f6f68b9f3e56afadbd146a82a4c61eb5ec335a37491cd7162de1c0b38a13e121e451ddbc8310d7d3da059282ffc15a719dc8bfac931c377ada4aa019bb564f6bcb615c80fd8960a694fca9d11c2148b8fb629f43182625b19697b0b37b3ab77578cf0559967178f4fe2bc6d6b5c28993d7dac6d87320bbbe099130064cd0505def5baa591811fd209cf7eee18226146fa55d01b9557f3b2a4e84d0c40f0465c56841e1add1afb908e81fc75db52e47ffa91e87c239b786a191b6a08ca7bbfc312c2836cd50dd41e574031ac48c98a1ed9d9ac0eccd914484f4cd00fa511684847bcf0b354b11d89f1cf61eddb19c939a1021c652733a43bae6a9f0a5dc047fdd41dc086a69671eb0666766ab201023bd6da6dba70d3b44d0e5fbe01966a31997746eab742044610dbce2c41484e255324ee51ba020e3a7436aab207567d4b645e1b230c8402f2e9c5e63442dcfe8f0fcc9afad4b420a5510cf2200dd0ffaf0419b18e7a4198b1ceadcea1af34a4071a7b62867bc1bfc181e0bfc30919e9eb87a66832d90359e61ee5ec7ee9ef7479fe955ae14a892eed85812ff2032fd58bd59f2ccf783d634bc51f8d0a5fec1f85d4936aa1f9d0cc412f08a0500ca8d3f68fb1c9368ee51992a780d9ac67fb14480a661d495fbe7a465ec32d0c04e8d6d2a0504f6584037235c8a5a18fe969fcac983df58227cacfa82c6740950c212ba7aa3ccfe6b06daedb896f26e275ac0ba091e80e93c74d7d639188a6d902d8232226e39ba99a8a1c2ac3ede8779fb748667414053ee15baf75f347aef0040f6ecd3073bd079936b8b1913bc021c1640cff6442a82a79b7da3bca9f50810ab8c0869f15e09080123f49e9017dc6da7d2b01ef473430d925931d9a39561d0e039754e50ef473c48b13ef8e2f2475bea7f038caaee16dfc85ecac79536fe60c5a719ddfa4caf74da69c4f72c1ca488888ec4e9633e95146c2128f4991fc7400c0fa5041620ee44ea7e36a5daf7aa0f8b074a5753146e35d5de5243915705e00c44efb32df27014c6f9df5ef5f9c14b4be0986b21eeaa3a92039b94bfe9eb9078ec7201355aad554c6ea2c765885ef9cb34c371d69b739e78493faf68e211a06c38a6504d831b4e26804621e6b3779729a1e9cfa604eca18a344892e8ce1db0967348aa1c07b4afa0f14f85c84f5e9fd663ec5e35240eafac851e9b9e557ee0e273d153637a8009812a00baa53af4ebdabeeb084642cf0acdbf143c32569c40f604cc3c3456d782b532a2c082760eb12d4e31e02606e6c705c8a4b5185ff560ec183d56a55a199d1907a67d9e3094c28d8427e71410d1b2f95459603aa0f090c99b239cded44cde5cd4b51fc4433bdaa0e5e701027d8be8d764f4754e8a4290f852dbbfb43e1f952a3358991d10ace168d60b0c2e46020584f3fd4ad8ca90004f2886e906bc6e3de3aaa8f94dacdb74a867902c814f72d01f8ec026e3fad4608cbafedce4208a86a8bcd38d410f56a197b41206fb6cb6b3dcfc73d17e5583b0131c14c3ad2d5c6564a6466fe96413b6b5e002abcc59253d71b436266eea74000af5c53126a68d9ee1413f89a0a13ec98f63119a613151a76959755f2b5e5a80cdfffefce0b94835d0d4713af1eeb2c4f04af2e3bd8fd086c6cfc13471f8a990e0ccc98f12a908931b11cf5d74fbf92fadda07a30aa2d4177f553678ee39b810bf17744c2350864f9caa882ac1446ebba9f9d470669aecdf467758c5b702df201ff8dbb6bc8aa260a009dcdaa6697f30f314e472462dfede72f783ab1cb047c098eb8ad8265a0dad188ea696206f088d48a3412ec098d6b7543eb80b9bd094908513c3b301d7fe0d66bcdce58d94f93d5d022681106d395ffa0bb5a266cf2ed0d1c5ba14e770fd17fc31eee392069aeeff733a2e470aa501d77e7ef8182d5e101a00c6441bb70cf7a68de4891240136244a2afe8d728786e78a2071622eed0a0cbe93923931d352b8df2ef1731e470ba3cf329d3ee99b1b72fb5e45d4cfd21e0232fa5e8d585daf6496faa4afc4f720aa7ab12f076f615dd96c3b6596a88afd004cc98fe38f465126ea9673c73fa1c12c954773618031fe177799332803890100fc0e4bb029f66ad60433a4c5140663b8d1d596c6034364201ad801f5ac664e042de6a2f390d6fc6a8f53d7e5f44cedf0218a4a2874d4c9ed6d16e590924da30e0462d4ed35be9212310af067a7c188e8db2287153e3fd44adcf28342d028180423c190314582a74e8a7bc05ee2e1599e49ff6857a310d79bf3f7fe7ee480fa043bcca9bd917fae9baa3b2e2add44e78fe4c5bfa5132bf7db40ec86144af75a06497a15e6c975cb9693a57180f2680429f077aebad164cb404ee36a341503fb0cf623ed6a959f5d4d5b608ed8a32be6a8d52afa22cdb25f0f0637b64830bcd3037c6006d933db96a3d6265f38ee1fde20afc3d981ea0ac2e9de93066a86240c0b77b8ff28b27af1e48f23f3bc374610f9fda06e5a9d2f730664b00dd8ac828f07f602e5c8d898387fb6dd9b739a4297fde466d450843dced0fb1b2d46158700012503430fb402402e0667e5d2608c1521032afbf0ea957a13ee552fe1dd90d8070f3442854306d03ace55af2faec10305d5be2ecab26d27fa9f295243ee5a4b09bec8e6974c35f3bcb09d1b67d9d30e3745f5afa249055bea222672e26fe2d6060101058a673e934b1b559a8070a799ee43f530541fd2a0eadf0abd84bb21460bd04a795573abb964b21b754d91025aaac3e6f9a6ddbc35bd93cbef374e9d520846c038edcc3dde7b3f93737b60289b93ae067fa9e80fe7e032431ab6069f0607edb61f375a3be3d603e8de96cd6775417e906a9aa82759bf73badada9bdf740eaf3220aefd4f88d269136c5e51602a5c91b5079747dfefe442e5ddb379ff380ad6c120f389cdb33b495e77597507f90c96b30b93de1d12d70a8392bf8d18d20307a7ad2a79a16e490aff59ce98da535fc7fe683fe365a7bad6b5abac2cea120c9f966fc43b6d7e3c991317b85c0232c9d8c40ed629c0b7f64cb391edd5f971094776faaa068ba6c08b9f59b46b3aaa9a1a27525e55b3ba85a7d5d0711e4f510ecfcc9edf24a4efeaf7b5c5832e42173c1966c91993216759f8a4b62f94a6990cb6c21ab2f1965f0d4d3086e90cc6556fd4d3c8706af022bbc8714fdd4bf3460bc3cf0d27a00dbac0754eb360fab37c446dee38aa7c67c44325621f8b517dbc0e8a3997dc29ea7ae6c30bf925766c44e1bf96c0e5067a4cc159d16832b975f0087672e7c4adcb9d5f4f2777f9ece3159ef4048a6ded070041f783b13f07c7e5081658b2e4f4dfcfeea2d7490fb0b91a10298da1405ec9ce20d13f849bf5ed300b3eba7fc50d5a17f961aaed0796b20f7abbce2b44608fb5b18640bf5b6b002d04207195a8c5a01ab6ece5bcd428097173bc0a227782b9dca9beb84b61bf596d0aa37ff85391d84b1ae545cfaa98a486c3a3b5910a56eb8e11bce0811594c71300b224cc3f78cf39977c4b1cb6146d51359705511a374782e77a68180e33f37902de00a29e87502acc0db8b58e0d9321861a8d9455cc4af25b8a72387e9779220fe966163343334096ee51a5abd6b396821d6622147fee1846da190ac0bab97803c99db0b5f8f78dadfdb7548cd06e08139860a6405404069ab752250838bae109a565c77501f312b26c8b2fabc31c02e859b0898db3a6dbd20ae5ff5ec770790756c1c737cfc0a1c9d902d6af504db7dc3ea039e64590907475059e2e01d99102b47956d47d2a0da3874ca33ab9a39574ce6b6eac156988a26dd6fe49a78f15051e9ecb979fa687512ef06f53234627c927cffea67d79016886ddf6a80b43940334d6ed4dc25927e013b849ac0d45414fe81a1cff7ced33be67c100be0165170243bd47f6862aaffd2abb5c26c4eb040704efaeca75453407cef2cc7fd7789a0dc31f09f83692ed1a4092a5067b5934c4fb4dcb960128bfb4d721a2bfbd8b1e0e89e14b79ba1b1932da801d4fc4b1eb8703a49e2edbc77ca58e4a858238eaba042c4fbca329745e8f9269a51063586312da6439fca33f8e6c9d2e1e376fd7e10aacd5382983970404352babe24cf40be697371f0039223126753bcd9c8a6ae005fb1c8e8ae5f241c07d2e51acb4138af494f24ef86b2f312bc87b1fb36d5f6902483ddfac009bac4e5c03c438528d88f11fc36120f1596c73436ba618ecbb6202585846fa4002005e9d66c66ea9db47817d3ef5bd771e31b3113f3d22625474024f8f2c2c82970b1a2c79dabe49d9129a323920a3d9634728554aafc165bb930ec17666099ffa57db3fae9f0c6ad620cac61a47e4389ce6dd1e092396259cc707f2f8e37aa370536e704058a0a29c7517e51980133aed029d53aa38920f622b04a0ff621552df7672a38a073e5205d543fd82e0b8e6d300d11da5de3cad3d8c0433c8053c5febc08ab070ef93dc062c0e1829d6ab740432b938876220060c860290e8527499969f4213fb6808c9634af5a86298129e5ab9fa4d7bf92af2b8410165612bffd7e619a596f55e3b63886e616690ea346b0b9b7ceec52cb4345d5906cbb151d8fd344ddb54e21ecfb54c7aca05acc8618d87758b947b8c7e02d89303ffad4cf00ba7ee4d509907a1aa79aaaa6bba4758a13ad9a9841ef91e6a41be090effdf19b799d05f2f94161718be6bd3bad443ebe47344fafb2bd64e1665250f9126b5db8bca97770b8521e799627b0fc9d6a81dca727bdd2597f9c5c2de5c0718f550661a072fc28243d05f6fdd386a16efd397b58240f2da77e9821313f600fd86ac2813853bec78fc9a8808c49ba144cbe0230cb4a57af6571ef09f0c450d399b40cb3b785d0eeff6ed3a57d66676f8bd7773cea9806b0b365f781dcc400233816634f01c7bac0220735a7779e8a806bed47fbb3811084b31ec0ccbeaa70677ac11cf54a67e790e576e22408ef6debdccc62a160793664870054df2303c07fd5f1c805317323efbe178660b3c9a35c64d5307865df6b9a2f180622f7854086b3a60dee05a783be85cacae8df2f22d5ebd9738f2c2bf1bf6a02c1be929b30f6a46ea2d518baac63286fababa5da9e84d4fc40a9c8b989988cb1a5437f52302a022bd5f341b4b6ff369259bcc49e1ba4a5da776279eb8f49a6d3e6e7e305a0b4287574af6c2271c55b9fd45fc988177b18d9edf46ba1ac56e7aa4b20b1dbf0c1880bf3dd2578c72308069d26cffdd808900958b6fc0f8fa0ef608fe64fa47064252b303476da33d7c766af70e229c206744c9bcf511e1d0f2e247be266bca0f524273eb349cf02279fc5f28f120828e45c991d75154669d749ffded94c2420077182679302704a53a8e79a637ff41f46eb8ee000e1773acec0f9304730661043950794967feb9c99f2006f271a8d20a6b4ae4709263f698b91d91f00e386303afcb0c51e055463ce406b64cff3b10671a5b29d109e22f34b86347d06d0c1704e41a15709b153ea2e0d050e23db3e70575ad2f28c39456260cb00057d5915f0df383611944f7c9bc467d571a18875cc9594952caf58d8dd035394839c3e260058322b10bb6910c9827e314323339fd85d988ee88ec16de4357384a801c8213058ba942e1207f3c9ab034f1c02ef5c8fc59c9f8342e8615473d27182225274e0b665959cb04fafa61cb42d5e1bcc12a65186b3e30df13995871f550fb216d240a719294798be3de5d61876cf6338019575101b76f491cbb0a63a46cccf6b69909e0123b192e73ab6e6457e7f1015c7cafa0a43f36c7d8ead15ee597bcfa700a00d8cf084980e3cf93b192599eeb8f455666cbb441ab825656109e8e16ff129e0e72291746ce845557d1dea16b24f8a0407082fa2982f1396c93c7a76636bd5903b38d34154a1ba099c478193cf6455370bad57da7221d7fbab616d9b2676d2a0707501206d8a31f269400e184b765d49d8b0174fcea48e40a0ef187466425b603d1d55cd76abdc15f1414229f7c8f0c0c851f0046fb6d2b6e1fc551da532eb609d3946a3b71602b8bf89bcd6955e5b2646f34237b15641b4109853b8300b1410d73b389bb9fcf6b2df02633a110573f0556f78493e6ed5e7da83ef70d693fe50356a0ab6a2e5e0aa2117b39ff87b58a24103aa908c6885858c7428e4351a9fb06757c9703694fe2d93d4d2fd6bef95ffc4bc3fdde768d23fee5413834ff50130999b0e9335a027fd20d37e0618b9c46b8b59ecd2160b7dc616802109f7268ce03cd529ba52f107673e532ee30f3d57793382735152d6d21a9b2abb0995d09a80e3bc993cd077871b5afbed21937b1d786a7346ec531c7f2fec8b3fe889c4c190c069cabc47e9ae78cafa9b894e0436d0386342b15f2027a014885356f356971052f020985819766364140c72856271cbd437931d2e276662a718ad088ba323b0cd1d653fc7250306bed636376b677157aeb2c13127d68fe79a5f54c9dea05a7086d1b73b3524f72154b55225cea2192e04ec563594868815301494832aaeb1c072c0a142bc01ae74bec4b2de2615b5ecf466a714690a10acdee1cb8580697550e8bae3cf6a004f699497573ba8c5ef819da8745152cdf65ab997027e7135fa7061b6d4a364ca78297a4cfdd1e5f816dc74bc7f3c7529b546f10bad2b0c528ee0cccd96b6415fcd5f57cc6a9eea37162ebfe109b1fa54aebd3af2c0dee7590b60deb88c98c7eebc2bccc74ef6799d9b670cf2b6a62ce0e066021465799a4b2d302de2fe978a40e7e50c33bb2c03aa314022709b5732c1d8d284c7a6421eb3fd10bc841c90ded74e97cbf5aa08900bfc94d8c87b589d678b8dab86088b656992a0b7e15b91054e285c9dec8b10cdf24975b58ef7a657205c83db892c59f1f1fae0bc6d86a6688c35a05f4c8691f56bfcf3b3346f24a72e1254d3a5a8a6dd4fc3b0ded6962722284519cb7ef72bf2b417f230e8f586b30397b589b2c88e51bc1850946ec5e6ce6ad3d87df682069e3626f016b1aaf0a8c46e7e06d6c6d41bbe32c06f0f0ae852ab7c03529f711b8cce877d3c0be94a055c0e995fff011bb9364140f81894c5a771b2dedda261b863423e6b643552f35bf86db1a1e8615419416a803950e5fb07a2f09d9e4e628d19c837b5686c2f63b0014857939a783a18d2fb107711064b5af448d33014c345b1659bd59e80a664eb7c47cca7aa65464cf027609fa386a9ef78f688534e556eed1cdf437661362a45b3f256b6d97f856ea632b06cd1c067026b694dbedae42ada52a4ca2271ee9a13f64b036f62b311511e69902ceff96386bea3d33fe3e5f1b3eecc5f18d9f598911d7d0524a65bb9ad40a1101f7432051eab465c09fa993679468d89b4118cf2c7a3c2f395023abdea9c0db057191a452c0ba1e21871b524462b754350c702443272ff5b44270f765918d6c018a1e7d1e70452a98cca9abf669d4843fde9694c73228608f173ff19434eb71027576574daa92a6ef92161b201ee00d66a07183efbc393686534fd8d0321de708d5864233ecc6c7d4855c9f42b95f12f350e4c1059d6cc0039b179c2f70323b01fc6aeea90a37f97624e86ae8a83fcd1ccd2a86e04b7f9e1cd1804673ec293a0b8f26f126baf8ae299dd4cc9ff846ed9f608ab348f4561095857f455af045ea089151b3ca0677aa835f26d29f950687aa6dc61eb36d7cf06d0831bc0f0c0ca60bfce4139938c21f7f975828eed1cda866b488e8ec8fb2d81090e07e1882228c08b3967bcfa894340af51e4f93a71e9cc09e513e31b745aed619139b7dcec8a70a7891e7ae3c229da2f8f554d0cc7c1dcd7c62705f9a02e88c9588d3ae653f2e0c3d15d3925992d3fe956fc8fefb06823248b85a56ea5d080554867cfc3c8c84044bd6ac7b50abdd7148f2d4e794fa7f8b7641b92e6e57f971beb46c6cc0208908793934d5bbb8ef85d49fb36555e339d7608d7e97347d2fe2e34af46a9624960d351cc194b50d8158d5115db8e55940ee6057d8b775b99995a9c8b7c06a501d01319ef243a610cd21e4b02ba19be7dcf92788a12e2047aa555b2543d48c0098008eb212c6f008f3d6163b576c0661597b9b351a330cbd6c8d325aa5a52531fb0394442f9dcd2c3ae5c2102648a03f01550548bfa03645edc40b05ab262dbb94057c3324a7e4ddd15bef4c89ac1454a3ec427dd058406606d0c38526554914840d096b2bfcf0f64ad0299a0f9f4fa17ae2793d1d8b33319619e8ecb375677c9e0044d8b7bf9a658c50b5da91788f051f218f2543027b6f951f492c6807b49ac20660d582097108b657be33e4ebc52a8ce2060df2ccd71b1a67b5a5bf918c9cfa07839fd668baabf0465ec8594b16a9a4723d8a598c26fc5ab60168a54a6cd7bb0c31a626d3c2c440c19fcf0c88539fbcaa51bdc8afac1fc57ecf3546d0eae9e909a30a5e82f48fbe4063ddf83b039f3c38a3ce914a1970fd0634b620636ed78b0236129418057697cf036dfa1d9ea053f8c568964faea33d2b6f204df00e222e063058eb1b0c6ab01020e30b06d712d5b2c40a92194c7abf8a0891dcc2bad4ff067a66b01c5ec1e4eb5db290e5965943d7ca0d46cb8e22bb0e9985c53501e98a08ff82cfde936dd3e8054566ae92562db999e239e04a40ca7b52bdbb37d37cbc0e6460a731a98303adc3511b4aea49f1080aa59a751ddebd42b4f858b30c758809093a74b640f8104eaa6e79eb541cb9f45492a2393f65740ed3eb6c2967016d0b626c0336f4016bd4857c915dfeec00871923127a1aa064fff264bf1b9abf270b0a2cf5e40b2fe110760f90399209b40d2c83766fd86fdabdac09a3fac038e601ff30e323eb035220b9bc5d66a99d2a345d813cd80d3f6a72107656160003b8032c20316ed4efa87138b6d9ce22d010d4d00d9a12119ccaf2cdbf0c936c08c8056ad7d87b28941d149ae2be68885691f191e5945c23936749448a97858e44eb0b46d518fc76622285fdd9cd789b66c63eedc2b0daa9642e6483e6fcc5ed491d005d2b75fa2c1a135c7c84588607160dded0ccacb91409377e5d05fcc796371407dc3e2154fce721cd3d2b51ea35a0b30de6cc50fb670fdf599e5cabc560ad2d0cd580ca20618090f7221de59d425de6620233e1e8a95a4b6962a05fc53d06510db576ba9bd936f67d3bab76297d1677c11a1d01110fbbbbf8baa0437b1b381f0ffea95d6659965981dfed023bc2a7dcd71ba1b432454871cc8a2da21a7640eb006531f8be34f81a2bc956ce67f7d33b5b684ae2c79b317f051eac8b388a767901f36ca1012b6071d1211fd905b3197cf6293112df0873f5fe185980b142b41204edece66653794841fcecbf29b7d79b864b21d6e537c6f0d6c370fd249d06d40c6e8366d807107d40f2ff17ff1f5bf7807cc1b6fe4578fc567e2a385d92cb5309fc5632ca4f0d25bdadb3621e5f94e3dd454fe34dbd964b53d7b149b9fa51660fffe6459eaedcc89c17b3d9943f7bd5b71271590b1115f8fd67b628d22e97fc0189c81b838819172bb543e84616382be9a68331ea844c8dedc13dd0165c8d1e0915e9f9912e2b6174a9d43be981686c785986a964a8a53aca702a6ac56573dc00a0785eebcaae85bc9cd9f125fdbe843d07f2359975e741e86b65daa260ea0d086910e8dde3426dfdbc161e3ddf6ed3471a1cd507e45b4e22bb922620693319054adb3cfc361257a9502b82be4af3e3db9f3b7d85a7f7456b49141f71c370a00f6d4eab54cf4120403a5d5c07f24d472b6f56993f70a3cddeb51020380dace7082916d70c3e16373bf3339f352e608509e0796c9ac9c76e43c0d603356abe1d0ae73b5bd99b231961d8a358e35d73538cfcde8dd266f4636be72bdf39766a200c5e78b1ca8d75b154fb928b30944c092a7fd87fdd6c3cc813e5aef78df177f00a4ba16a4887b50dca6b7074574749087939ab59a6299e79313f2dc217b3a8c30cefcef325d5422572debc291fd9b18a30cb43cd8bc50b929a669c7cfadec0a2073ad3049a2060e03347b838e6e125d19efc69628fec6072f0422e591788effa04f0caf66b1f9f65c6fc4c014c0badc5caebccb2f79af082c916673911c0e0d20a7855e06d52ad615e24c7cb66fe3c3e685051fc200da0919adefba55ac0916f00579c7002e2c1a580304e74113723d2f3364c8babf0e84a41ef1e890e10069205063de553dba0b9c20b47a6d6e74624a9daee518848b372261c6c014319fd4c03452fe4333b06c47dda6a4a571625d895f8aad091ecc99ea2056332a3786286048d931f5037ab1603b507fabe6d170d6d5a7249309744368911297278bb94ac077d489e8934600e46b96a352fff1b4f5fb81ea29bf7c2f594b406a8c7b59b4e0bc5e3f06d2c7cb9f82606c437a58471bfdb20ad52c773d3f2f1ea5f4eda175b0563018c9634b4d4f8ed40468cfa1026f3fc05cad4a86fe6554c0189ef0ca84b0fb476d074ed8a6ca26badc68db55f35f7be1843d6337113641baa2e841faf9105acc8b6edd38c99f547e43b1511aaebfdade6f43592d6cf70c08792c8e824b5063be003754f3d597a5edc750115e445d4c7ab4233ea1aeafd45c2915db13f9e0b0a329db869f0e8faebf35d32d1314d00711ccd356ffc19c26ad1f10d2e3426006482eb9b7769b953200b9718414618c18d96e1e3f7b0483c6c331d58678a850446127436f89b89e9bcda058877249e2adaade2f181e33755969abc87530b3900104e0a32530a48ebfceac329d0ce5e9a45fc83c087b959997c2aee80ee91560ef6fd6f5c02c78d7eb8564bd432310660131f4602fc0889499ea39a4dedca670a5520d71cde1432143e5ffc9c89154274f1c4f6ff1c787fed593bf03f05190c0db67f08f47ef1f959b52bc0f83baa8d354b7d0485a52e075ce0e6dedc7da7260a477562ecf640eb3bac919dded41c5032e0b25f5184c7233e3f8f9207387c020610d546052462bb052fa6cf93900d293578d6ac1f5ad8b6f9d8f213b7863eeb0ae4f22dd815984f0fbeed68d7b95a8141416b6259119ff4b22e9b0a77d983020d8a9e1f9d7caa97bbf9094655bd7d7b5851a1ead42a6392af94627e97dd119509465a66f93484f0c25b0c329a856d09363b86214d6623e1a4793c216f2752fb01d57d442478092ef0ffc7b2bdc9222d4ea9c3789c73f46799832ccaf8574c4d06e4b9b4e5328ef4f472098889dc5608135fec6a80a11178dcae22b6c0b763e9018b0f432ea4170fcaf0e6b5c7bd5b668865fa03481ede94a209213f7bdc56ef0a4a21e54ff2f2dfd4aadedc7cfbc1a53dacbdc14eb0001911eaee3938b04c680b48694787b094b014b2f2c3af6fd0259158b950b0893bccb449b3895cd6ed0500290b0ac2002c461ea95ed4d698ae459519737bae46f38798ffa68b8baf2cba0fa3055aca841af09d945c805eb2016b02a15a38818bad54a54706b1efc0347d0b9229d80e142275216ac496e8ef469945fafbc3e24a101e9e46fc2e68751a26036dbabad7824ed11daf560e076913991db7f707f50bcb3b0799d02591334332002e4dd7699ec0ac612e3a88108a52c6b0e08db7312ceafafe22a054c63be63a014dadabf570dc612ceb7e8ff6d2a6cf7ce4adcd7c24e917daf4d4265ea196530d3e76ae2ab232911ba22e1016140b7cc4ff8887890f5a1f6606276da4c777970afa95876ab2c312266c6ee3301d810cd627833966c77cf69acb4b5e9aba14a30baf069bde349953da537f921f41b239c41a99d7b54fa3943d15d2ec2f3fc97c05fcefa5bfc3d1e6134f1d5e847bf0f23fe5fc4b75e77f131e6ce4ada4a35b9c0d33449ca5f2416b8674a16ec47617c2b79fd3bb860d7d62258542aa34e511ea0bb8b5b02751db07eda23b6f6804e324d9e04fdee5ec9abed6078a2ac01e8e390c54d3f9ee5c2ccf3ffa452b1350536d5a1d199bc8fe7937e34948e21594dfe80760f9855e792fa8a9d5baf9a0b87d50e18302c92dc7666be7785e94f550532c02ab88537a2fbaf1597e077d7f3a5e89c3270ce91c50e1f15acfcd8dbcf04fe80f390fe67019851a53c9285e67b99e84e6e37168d0cffd541a51d3f22d273f6f00710331c406f4578887d2cf5ae1ddb05adf5bbbfdc65fe3d42e04a44c1aab890808077a1606098572094fdba66f604e8c95248acfe9065ee087427536f888810250589c73f454905c1f061c9cd8758ec66e1a3da228d200e675eeec4d6badd504ef19d255c9cebf93e139a82d6d04b057da3b1488b6d2a0d15486b2048619750719046016cc0714126bf129ca1831634f5af54d1900939df6c45fffafdd441f0439d44dbb03eca880fd44424aebed1a2f6bded1865562dc9ccd2c329b49b96a01eb1d60e38a96379ae2b575f5cb9961d39e6918dc7ba27e583cf4166947a68300e414369292001666803dfc06724123c0b616521055e5652e4daea256ff9c1a02dd75f8f847cd242f39d0c006c0e37297abb2a04779aec222bbd1d915c56427040b099099c282cf70e1af22e2e48401e7c079317a36b0b9f1834c5016e99e8d0f165369a87da5e53efdec5f25694a93396ae3eb3fb39dd3d3710ac1d758041a09c15cd8a8bac7fc09e3b2838be4efc1782885b90b11cfb9f22d0888877d8ffe0a9fd655486760486e1f04dfd68916d743aa3c8d53b6a9a63986fd5262ae387e0cf4268cc984759bc8552c3c6f98df1bb0dab45938c7cba08ee21df1d560b3920d24a8474804837ba94b3684b71e768fb58d489c5c0ef410584572590323c1dc077f4fa6185746770432374a0f08b7bad50ec7cafe269852a12adc51753945ab0f54c72296c48e728ff5f707991c9884b31528adcb8073e9046c86405942b025022a8244e0efc8171af7165ceb4e3d20e07f6e19158e48fe176c155371f8e5200266711ddb231ac55bcb009c0c22a2fcae9fe5c5c42a6054155f5ee61549a26d085d678f9e2f3932ed67b245e86912a3c43ef72ea33ae83b561c0bfa7c23febf02e0d331e8a4a800fa9cd663229d26b8101036fd173bc5eeafee092176c4fa15072579a538d7f423d005ebc63e41dbc78cc06e624c603621379f2d7d723619c90ed77404fb066fe3f20329bcd47360ac60dcd8665ea3b241ed4fbf10453785b707 -generate_ring_signature 7d40505d96cc9812cda2ecf10be26e35068ef21403fc13a3fe0374f1752bdc3b 58399ed2f6ab0ef8e44f8baabd4b7668e5d9e0ce163b55fde697dc1f57b65176 2 a0f1b1b883584dd18faeaee8affa2076a50d7783086353a9b7d4877f0c23bd55 9c840d84c4e8d918410d5efcfaad94d5be97b5a16738f9ba27c1301e30adc367 bc309edac23260a587d0ead081229e22a1a46029e24bfc99b8d264f0cb550e02 1 8b78c9925e52578dc859beceb2992ea8ae839ef081d08aac63d5d81cc2fe7c06551346edc43bac05062d05d52c8f8537d36b05243a5aab03147fda99ef25040d9eb5219ed48cc03d36c9290eb3847c3ff32a8d282d1c43ea826134f8be3218079bc6e7ff41cb03b61bfb391aab4c75b9bddbed1df874820bc64ea4665331aa09 -generate_ring_signature d2b1be9c36ccec50df11eb89ccada0a0d065fafc1a4b7296d85180598d0208fb 790464d130c590b15283bdb524cdc611c1dfe90ed958cfdd1c61f4a8c610e971 3 c760502af5bd01024b414aa8e6ec3291d24a119c069e5394023daa44cf502a35 304177d3c97d2ea31afd6600fab36a15763c2a7d6fd2b179aaf9adf4a6d93288 5c01ec55a849da5d9c141a1da2cadd189c7cc4302866056619bcf541c0ff4c0b 67567e52f4f0d0b41c1b633d3ff41eb5a3af5abbb4b71ea61b57dc39e433bd01 2 0d06a24ad4f89b490bf6c882391af0e88b6184c41240f81aee87adcdf8914b0783936088a8925db3fed6c41e7e257d37a82068691ae19448b28208816e5eac0d89ba84f7fa5e8fef12bf8b0c2b1b69ae072a00a056dfee71a1a80a3f7d536a05c1b2f435e4bb895d8e61f69eb665050291dd318cc5b40f0c7187797c0800d30f6f3a308ab2b5ecb91468e9fb0ac42f11c71b1af288b07e145d478b8b08c52700d082cd970ef5fb58bd7b4465c399a64434c8d0c47b1e7675b01f953d6ee9640c -generate_ring_signature 6a7157bd5b7abb0dbf254d14383ec158061c39beb909e371c736bc053821894e 319ca2b6bf52f500ee46fa4e19e07878690597cea71cfd1294d689377803b4bc 2 f579fbadb5fe3ef087c03567db8a70b8b40670bd8e77c9cc58a519e55fa91265 75e979d40c24323138df595858e567668aa8085c2adae4cd9a72ce08499f99f9 0e8eda5e20345e1e85b44af5a172c57f1b7ab0a80d4323bb560058b4bca9aa07 0 79c41dc101e9454b91bf39f319e679b534c6633a8fd7e92a9bcbae159a9a9207908de4dd620a6aac8f1839f3f7404aabd662eb14800a2dc0048d93eae2e8510c652f2a71c334f5d22988b882b4d1e98e44d51968c2dbc332cfb059f156bfe9096b857e437fefd8163185a032797cd1ccb50549b76d956c355dd910b9056c8e06 -generate_ring_signature 2ff3bc41c8184d849397465ce855f0331b2fb8089acd03896ecc40f13c26c6cb 3c0d534bad069aea7a7a99f476537d5fc7d744827fb3c5b1747bd5e628c0c060 1 6a2dd8917bb9f1afe10a52fdc06a7f1a8be194adfd27720f52dc6b68e41dac13 9a8ae3cce36c6a7ce062ca5008b7fbcec509d750a7af1755506ca42987e71f03 0 0ff2c80243d7614217c7aa10a79b02c034bb610d9a708c3b0b6ebe95fed30b0c8cd1abe749b9147c636fe55c141859977be4090ea812eac5b0b8406a9defbc0b -generate_ring_signature 4de455ffd336973df436d8cc5400f445edaadc720ff6edeff859b49238aa2fce 0d974c6f5c5852046fbb400ecb528e463a9ac8ddb7e4f87290675000e4c29634 6 e119b88581dfc38a94fe46a82590cd9b062d321097017947ab2a039d77723a41 7ed1ae0e3e982d596cc6b1c33913cab5e0cac7b70a22d957ce5b51fb86ce1535 d74735989a7d20983a2adc2f7913caebf4fc12e1d33946c96d893ed9fcc41b12 af83956b543c7ab0878eda74010bcce3c086b748e6ab788628c78eef075c90d6 357399bcc7afcb24242b9a1eb62432c35ae6cedaefd732cc858d7eae7cb92e79 dae830a72a324ab3113ae4913a820523184bbdabacfe07bcbd687ade7d8659aa b5558d1400cf33fa98203b0af326975ef9038e19f64b41299a32a0f135bc2804 3 45b8d2059cbe52e1f310218c416ce77abfcc3e9edeb2103cccc9a0a7bbb4fa01f4a10360b9270b733eefa8a29e7a8fa7566f89c13b6d107415a433f6c181bb012d295044d63b0ef0087c60f6f336cd916984ededa488a5b78addeb47db6bb609e7d6a95be81b4dd14d501be253e0eac658688ce3984d1378710d67155b41ec086a6f76b410250721ee7d0d86c8cd3ec6bbdf6ca1bfb0ea4b9a85b86e1758ed0b6cec5cfe7938dd7ba6c2f4130deeaf489fc2f8e5f49b17e09b305dbce5f0530f4fc82e0cd6e987dfb50aa7eceab74d5e3ec21c00d7d19e62e83ca1aa5d1b7705041406c68a375a5733aa0f3a00c7c2da766e7a16e657f15f7bcefdd57d5f3c0f8c1bf927976487ee7a1bc82c0a8f15aa0e9d95ee8b1d91711c4cdd3502eea2091367c7a531c80840bbce75ab44d4c54e2f06b65fade1c9967cb4482babee370c037c81f7a910c7dcf54a317dbd47e5c0baac251b89b3d243c332db4947de2f0cef43f64843428e3b80dd229ef5b8d632cdb4ea914011886442b8552fc3356201 -generate_ring_signature c5a69d4c24d9ccccb2b7f24569071ae98acf6e03b925e5a9cc09752d801c62bb 3eabbfe3695bc2dbd5901d017eb2e54b9caba0945720164f342fbe3e6ce66b78 26 f43a0cea4cdc1617fdf4dc9aabd74562d8a295c3f85ed4acfe4042119367ea4c f4b250e120df0108ef321a0ada6f8d56a290ddc4164803d62d92e06524f4487a 60c9eaf854e68882287d9f28241a6ee63496342162b018d85c7e547bd65ff258 d3bbab657cf5184747c0ea16043192f6b16743a2aec28a35fb3e03c78114d01c 8b00e6cde3c562f5d9318949c9eac03c685090b8a3d57ba4ae5704ff3b5d1f02 4e16fef6f635620955f7ca6ff8f3077e146acc5591573501874e0f7ec8a6d84d 1ab363c7267a5f871fa39b58b3f77f132c832a148255fa43d488e145b12f24e6 93d47d397bd986ae573e04cfd6502baa9f86572d8fbe2fcfc29d35a6c55b5453 a61daf279f18faf9f001831bc06539fabea3cf733b10abef58a1523414ba2496 063029c65fe66102a1c47575d374692a459a416b9fac9450b3c102494b85b5bf 095fc13758f256b4550452a379a65f8d1c23e670cb5904394d153e4f9ef6f0e9 8be1d43efa5494cd054ba8f7a2ab134fa778dde275d453564e2794af637c26e8 146719dffba2869567d19999455814cedc3384b8a104dfb6512dd434041e8f04 8c21f868f215881a4d8f8ecef15a45ea9c5577c96edc5f2db93aaa97627267d3 c6cd4788daba63903038377a7748208b58351ec9384c96fb1c85558063ca8128 3e5881dcdd2d29e42ce5229d59c8427d266e9c0bfc48bd849a22f9c563ff572b f2ac153edf0d83e0b04bf20f7fb6c70881a8079af523710983e8aa89373700f6 0bd89db8a50f485379de7737d43b3f7899c254190c41f8ed38c1b82eeada7554 50313db4b4816e421de0dbb01a534131546b64539e2ff471e63e8b745b825c5f bed722400190efb4c21cdb4d76679ac6ddd56ef850ea89f5ab43f89d4f2c02e1 c81d8bb33ba874db043e3d8bf303b5790192fea6459adb800a76d48614937fcb 01563d30080870956b47ac0514635364e1c5f8d45e62af0895c405e96be1516e 909d70e1e8813e0ac85788146a836b2cf2d3ade2d2c5b18c9b08cc41b688990a 2da5ed39c9928eaf2ba35e5195ba789f8a01e99bd9bcc55c81c5b1c7bf6caa48 4668235408de2079f6d3111e96e502117b10d42a5c0e7790e9eebb7105a792bc e6574c97ebfd1ffb756f4b27c71b787e0c332b830086081ba57cfa1a699ea613 66dfd1881a176aaaf91af0a512636d9c0049460df4ccce64a4f7527e2d0e0702 16 2bce27fa8a4e1c1eb6ab6cd17682f673d2c59999747e209ddefb5aba7d2ee40e52044a6200a880c0db8a19659caf3e6bd4e38867e4dc0b9eebd26d477b626f0480e91e55d78235095cb7bdec423a96330e7e2c23bdbc3e37dc8ca17d9166610a46ab3741c1b84157320c07b9fdfe45004c622afc245b7aaccb17f5c0abe9d708e60db3c0c4eaa1c04f93088462af20e83ffdbe62c55f4764efa24b10baa3900151c41410397ef07fab9daa067212a2c5490488c99f60252313f363060eb5cb0d81201bc96bd19b9e6ae97fb559a076f5318b7c5cce758ac5bb9ec4ed346a740f9f04f35496cfe991915d75bc149a5752bdfa543c5c3c29a61975bba959855b0384b53fe53c1418df87d3ffbabe76d6836f84824f6a1c1f4636f8139e48dd10081e070d5c6924f05c5c39b82b73e3c1ec92850a2052998be524f21705933c8508d57b5060db5bb4cfb9b5b1f7b869984083cceba8ffb1b2c2221edc53a4458103236ce531e65fa351a7ed287f568d092d3e0313f283ecae83bd09a71b46af5d0265d0ff1f8155301cbb3fb989bb086e246ca5727539521ce60f639a75ebc52601e1adb871a61b856cbc96742167863c5e6cd37b157c0997d02170679a2989700d51407fc644ebfd5cc12d85259467cb38db26742035883d3c47f48e8dcc14b1081d0df7c8671f1887e6c483478242e598462032e2d3ad90a10debb832159de20d5e94721d54f30c57ec7d273f6078704ce458f63c5e002580bf9cacd06061590ee8198925d952aed86b2ae6381afb32b13f5f1f8569126309d79f8b178f9d620b85743beed1fd9b09c791915288289132043f4a441c8ac6c7aa19f66be1f9a900977754ad2d1420fa44a59a0c1c6f97653b4c18fce5477f7295663a22dc86b30943ec69f635d934d87517161c5089095896af99686c1e2ce79938d52fc1d8cd014d44f2f9f1f1aacf3077f505832dcb055f5d20f5dd97ccf3fbc2cc569892910dd01d035286f111ed30da3188eec887dd45c990514c51cf7e72066d936a7f4e09f1418fb9104102a3b85c26b55837133965735f2d68f4d9c13ad734c460eff20ece44c9f7e6f302952d68b73989bd8d12e355f34be6670f22f4e6362801110706629f024bc57f309ac518040c3ce57d95789b0da319aeb167872b56210275640be4e0abd44ea7fbd9da7c78f3e9017262a1151cd475443a1b55b27959c8cbf801ed4d789434f522223ccce824a0b618105f86007a01ab806f76d02690d6c54c02e7b7e3efd4a506f3cfda767e69d94f366d1f937ad24aeb6946f9cd31aef2710be61aac76d05fc86086c1ee8fc579275de365fcb0b822ca360fd5c1272f9ac200a1cba46a2267ecee31c2ac40282a171273f30ae5e87d1b2c7c8e85618ddb75063577b187067bccc457db3f56923f60ebf76f6a247bfa10d72a8876cc5a503b0d9a1dfd7313f627a9a45542d00c283f9a867c24ce96ce7adc0b223cb366e0b20a67815e6ea044261da1274b9b6df9d977e07676c10ee938e23b0d66904572ae0efded190d9ba1cb42a9916379097d71968663583af28e18f61c70ab85cef8460006830f32e150027c64edff64d2331962c1704723b48c8be99cfc12d7fae88507061348f4ba7fb873bdb4980ba92ffe25f5b485910eff8f3afb3e4b38bc2fac0a668236bb1b223459cd428abd8fc449a25ddbcd4f836a0fa06d57aadcd1447e04db2bbca48ef83f5aef0027082a18aae1008a37ba1a66d1f145213610ba1d0c04839b5de943cfdd22c3a8170bae8b4ffa5a633ece9ff19fa0e37d053b1bf3b60a46dff49b2db28e1c098aacee299b63949cadd4fbc2e927f52e279dfee058160be0466335354a37415d38581cf7b96ef19f10b7a11a445998cff67421025b2c06ce79ebe09d5388ca909f0be3821d713ad65db8c1ac42fe560baa3af4d8e6520afc81bb45333a42af172bdc5c012dc7535be49ab5fe9970936dd1f7cb1af7a60ab6e1d84cfcea747cc21b3550219cc9b3c1b40ca2d91ee8e270c6db30f681ce025b3d36984e0407e501348c312f018d885e421ddeaa134677a9267b8252c50400ed100e574c2458411204421de73ca99fc395ee9c255238fd8f3316154bbfc606caa33919f67c5a4e0dce1264a50f41f435e1ef115bbda423903587a7d517b009f7a2945af9142efe486778f90b660ae4a2174555cee863a84a57208983df5500a914ad4fde328008626e6595695352324fe8f4d9d84e145511c2859a2bfe1f0f74f8db4ed11cb85572a4467b8de8aab016f2796f3b3a99841e36157a304b05030671c77920a4227bfa583cc152393809214d7dea429272bc8b6df58177e56b0e -generate_ring_signature 1a25025edeed2967079043c06388cd736a6305609ead156e76233de39443e6db cdd7e9ef74c4f826659baa13cc7027e9d3c0b4379472adae05f36e99ff600541 32 18cbd5ae5ba735e8ff1df944cca9ae3bc07187947d3a0377811c7e0769cc46b5 2eee82ab6674d77e15df8e3fef6d52f2159d96f510aef9f03a7080cc1df8a551 8c891c48ea8e5578faad92012e8519be5415a6397f92493cf368598927562aaf bbeacd239001a036995137abcd6f6df6af0d42517532c177cee3d774a14bd50d 6a7439d1ffd71e17552bf2d14763bad542aef8690dec9a35e7033d941df846cf 99d0c3c86698a62548085e50acff48c7142218ab6c51308429e5d516d27c1e2f b14165aeef86209407ffbfdb583485fd28f6edfc09bfb30000a69af629660478 a1b08ad9ac3f2fab9ae1c44b64c1f8c0d89c7ccfe524220a63c8fd4b80355937 c6f39774cabc63d8a858d76d2b67968c57754f6cf539a0516ebdc2681bfba6c8 e1420e8805e8267cf5cf14ea411f178feae61416ea6097c2936cf255d6da75af 68f36553bc4e176554dcdb4d40911c9d2fc1ad535ed5de68e61214740973721f 36995a5cc2d48acf783e851bc82a7d33b39e8d5fa98a28d053496431f62f4e8e 6e5a2e1e833e72907908da061752783d2bae0fc8ab5a5269693ad104fa23a541 e4c81b9504412585a70f804cda57e30835428f9e7b33db9e303cd8280e1eb133 b21e9d751bc67faa423ebbec6bdeaae824d66f1fd287229d15202ecebf972ada d85dfd2d4c2d681e4da972cb7ac70b9472b5a9cf847e51c7257beaf9a71b6c7d cc68be54b9a80b51446d0588e46f314f0ccc495c7d51e3d271737e76a465c33c 34305ec1efbf3dfad30fcd4e67ec2b03687d889bc2cdc77ab48e0b5ff9cb56b8 c1e4997cb9f2baa62de3da1f39b14b0f81dbd2b5b1458d4017e9c3d78a24d406 bec351ba97f26bcb0b2e4c2dc0c42db166d281a7907971bf5087eda6fd4c7092 a61009cd579acb5a183f65cb11309fbfc2c83f15a8e9a3733dbcce4bcc9b68d0 4652edce62b97c125f033318f97c9d0b6af10e054d424e0054da0892856f8a5d 089d6c08e5cda9afc0e25c942156cba8e1154aa6e108f7157b21beb0b1dd9c71 7e67864cce0de6e0b36e85a0ffa8839316dd5257f1d43a4bdacb63723937940e b7924cc3f1eb11c19f0f92c4328e485ff5c087f7cd65d56266882437cf36b3f9 b90b30b4dfe9bb2e74a319d3fefdfe16dd466c5f304cbc7002c0b406c75f6cf0 f0e309314c00e1052b94b8b61b8215385eb018d80f7d416c7ffd3e303c8355f1 532eb3f1f94d0c51c3c2084e1459abfb07ef8735edfd8f0549904a66bea4a6ed 00c8322013b025dabc91953fc9d2c7558e2d482f7d3befba5ade16bcaf12e2ab e073df0ab60437b296be99f91cb20f4e2739ed54d3f387f1bef16935a75c1712 9e3706f7488756a2813420361f139e2e8478d77a52a07e0e3e0f2f85c873eab3 021fe8333e571ffc9e67b99ba619a42450b5c1fffd3139127f63412b9464a57a 1bbbc450a6b3450d412637420a225f94f6a76e0de606ccff43a749e255e7a803 28 0997ea963783fa79ea7fa6de843ac513daa8320f93eb0ebbaada034dbd313d02e405021036375581f2469068373dcbbfb2b724194cc67e574b20f41f23b742004b190ea16898144477898a9b150d73f25660b8e1c47379ff3821c5c38057cf08d2fd35fd4b512e4ff7010b42a8b75e91131d9552ff90e97e2d9e2bed6ac9ff08e73f3e2b3aa173aedb0610c6a926b729e6586ae67325bd5fa0aa6902bf280a0bc0e9ba344ad04daa3fb41b8cab1ca2f0c7156f760577feeafc0946ddd91a830bf9e2a301077753d93407f0269ed59df668acd88750035e1570fd5966082b7305a0ebefe90554c331f7698d4132ce73bf42f56050fb9c84a9f422d839eb26000ee78714cb9d6ea13c19d1423da1b14ebc05d995b6acd430c411d31e9b3b6f60020138ad97948eea3f4bf6112ff271dc1de77b78b049a0ed65ccc5356f12473c029b4f2cd3f68e25317e0fe895f0fa7ba5c2a30f92a6b1cae75a7537dbb42a970a400b904348e9a6c0f445f475626121f98e18a65d008de9a26f2ba24f770167091781d538b2a1add63165aef0df93c90d3eb26f2313ef5290aecc0d522dc234023c447499814c5d9a0e9f8b00faf42fb7e0014c2ef0edeb2ab4824806d7a8050085cdbbaa6bcd5effeaf554dafde0e1f7a2d4e2f182020d7f7fcc2e803136930f8ed06714d324e3c74b62e34a7b779d2a87ede3d62ea9f41b06994d6a7c4a500fd6d3de7f6f59772fb14fd9d0ca5de377f03a2e7526e24de7389ae54bacd6cb0a35ee84830c6fbcdbd519548eed27a34ce41a0bccf9abf74778ce80c7746d2a02508a83988f9d5736d5cbf6e455e024baf50133f603fc02e64d2c38421bdf42067b777a74ff784dd77caa243c4f0e177a8343c1cc44e9e012d1e0e04989bf88098587a2c7bfd5e7c2cd10c8ef4390fa23efe9105434657d018e1bf4f311124b03bfa9e7ab9a733e65e3dcca633941b2b5edbf5d581c1c20e14ec44379141acf0a8c435f1928e0a9e7a7c0da5297002e3515391d2cb8fc239d44b8c256d715a605d866ff99d32cf80c0d2ff333ef4b9b8df4b271935874501625ddf3534b800f0e7ccb623f2776d0f017b428d1a0cea0e89fc0f4eafd13d0ad8cbb91cc54b1310f46efc15d32beeb5a07d2aed7d7ae1b55d070994c4a08478f93837b46c039de06704c64484bee0835663e9831f2491b2ad5a33d1ffb0554244181efbab40f170f535c61a948bae54f7c20d0e2e51cec09466b12b0f5a62e500de5cb53bb69370381fa1c414e8cf0d312c2eb8d2c730e7cf886570e25581f14da84d3818d451e04140e6b8cea05a91971828a45df48c7ea4bc2fd1b19e4c188e5fed4274538220511ffba6bbaad1b0ddf7c9bb069f1de025d790601264400477d1523d615f37308e36b144a0c3b2a8e5fc0b2b912ec350c81303e36c77d7764a6ec650493106003d5043cd8eafd61388d0def0da1466b1be37cbcd6df2020528ec62dfc7a20600f79ba6483ee1a79a4a14174470ed366034e2d4447704eef0e028c557401d409004b825e7f65f67e6e504be68d3aae5b4fba7ce5f0fd4a8d7700adebd5f33caf036b6e6551c2c811b9dea48d0052877c84bfcbf81acdc80dbdb577664111efe30c5180a4b30ef53c9f600d631f5779f5c31caf32a98769a8b98d780867e55abc06e3d29462816cae05891c6a2814e62105cae8e3df040e5381edc69412bf4b6603bb30e2bc53ea79fe2a61ce2e1cd78774c57934498f60c3dce28f3792d6e8cc0dea0ce47159f9631c8aa8492830882183d0a01973f53950cae85366f2de5d5906a8a9351637df0fcfc31c5fe4dcfa7f8cb2c1eadc1de05673041ece354f7ff50158799916705e25fcc5deb2f2e8000890d1eb38dfd034f50981ff3c8dbd1c000d6ee51d89ba327721f7ae71bc1f3f477eb00db44260f330c85e131696f7f680073b8c465b8768987e85afa69540431fb0ad0f929ca40bd0484d12033f85b48008a5911a19c2d85c3cb8ed2d77550e594a5c7f85b3ec95981aaae10c554210960cb3c9203e9f86384d3dd718f7b6d9977c45d623235b29d3d4646e8bf9abcb6a0bfc83a7eb38309766f959a872f7dbde6e5a6c14ef42fe769e5ebdd55f7ca3190cdc043e83872260c7afcf6575209f81dff10e8f62e84527ad92d37382dce0d602a4e20a916cd8cd18b483f4a24ea20c755390ecdbcc257292324bd2d6bfb8f90c1fb76e7f7f13abee1b61c9e4080ab89f7898e5bf0944ec7982188a2dbbfd2f058316f477a7a5a9954e605a879c243e6289b7f58fae8ce78f2ee9d3382f9c69071e0a1cd287ef72118014829ffd827aebfb60126b848f937dcebb193c8699320b5efb7f429c1953f7786ffddfbbbb33b191e587ee47c4dfc9b7e9c20671528c0a65c08adf777c11e202cdc81916c859f379c585e82fcd7b5c7867f2280fb7c3005efff98bcb27d7cf0b4defcb74c17d2e211f98f5b71b422ccb25d83b98cc1202ae6576da559a5ee1949cb5821ccdf8a604ae31948e840d9c8e7b9132bc965d05b1542aeb055d842c28d4d8f54d8ff2687353f9149719433d669594e793bff6099639e7c1f47c55ee4f208706d54e14575814ab9e378372b811fd56f9feea6909661f63f3032266e0b0f66fc5216146fb43497ffad889d66de327821c93da8109a7c936140b388341379964b467c02856b3fe6d1b7426c0f7da60cd66b427a6091901d258a948e9360f0714093c1ce4ea0debd1304d326d614fc880df3800060e16744051764f1eed306fec5d6f931c2dd6d467b1bd258809b026ee0aa93c5a048890331996006fd92da0fc6fd12b851a4e57aca0d403b73fec5aa4ca258adb0f73b296ee48f9c0ef6603eceb929dcaa4d19e202f3bb529abc53591e93873280d -generate_ring_signature 06849cd0519c6f1009ff2038da647ab6c1db4158bfee7644db31219a32de2265 5ab33b23374071466d658f4931aa7ce87e2800bee54a77b3228c07a8d1f9e4f8 50 f4dca6918c240e30306a9f94dba59fc51a8ce538d71996d1b1ec029c9b32acc1 3cecaf5af6facb8438a4feef5999cb9fdc28fd4e4ea8ecd67ee057c70679a00c aac24c3bf580a2047cb608e7414c28ba41923980929864f3a59c5ddabe1813b1 178bc14f54597a5d3216a28b763597cd9593deea8e59a5e4ffab0d3af17dad67 cb648c43bb1ea79463c31058908db6360ae89bafede111ff9119f619ad40be49 645417d726d5f09e7c40ba1b1b1e17de4b445cc6b443f106ef8cdf20709de226 42fe478f50153993cb0de879050356c81055b30b04c773d07215ce8a05e86b9b 853818c89f9822f8b5bbd1116cb8fb192b49db52881a63fe91be8828843bf49f 43996d323e85feda01a303ec77bcad68cfbd45fa93ec6f52994fe002f3f63320 e80f906975529b458402d90e871d4fe391325a3506880ce3590640c1721663e3 249d745a3bed28cf3ab726767b78266a4f0df17ee72e4c228dd4ad9363198416 127e4eb44765ebb96929285776f2f28b8b506fd2f802709b960ba5122386e4da 47925a49af8a140f389594c6b0f58c0cc7d1e9b2ddf8b4b19caddcc3c191e66e f2c5333dd0c1fa1b3bbeb8f76e0c3ad4d90ae352e5491733577f53653a512a47 41ba603e6990ab4a6b38194ee651f33b1bd811ce9b415f738a999791e9ecc08c f0436f4175cc6fd250f1df326cacd940d9554ad61ea96fc21f7338b5314c0f0e 165eee723e8a32ed375e2454dd569b0a94ded444d743180b81883e7faa4a4f13 a85e2a7516551b743a2485984fb579351503bbf3ffe4a74a04877f9dcbb53690 e94ccff70bc813f73e14eae7093ce6610bd111710c284aec75a04b0f4e931fcc 03671c6ea4b79883673021463f66222b4aec7c91f5bc03ad7efc562fb6fe2db5 26a39e79ccb667024f0b7c90c1dd724da2e3754d810cd5491eac2b3cde14a1be a250f4bf3ac3e0b8aec3983660251dc7d6540d59ef7d05ee4b2c7412c166c98f 914bdb7c9c9b0735dd1be3f60171458b8e312e53ddbbb23e541660bb31fd1a24 a67f611b7bf550f4b54065c0c77352d3c49a3dbb71dde20777788eafcea84513 a9d5fd6f07e027beea34762f024615ff38a74cde6b009cb1795346607e5c1ec8 7c894a201bb20226d380c8b58342ab7b946782bed4532f9f25c40f639f1b1312 c360d3b1274db4fba04b8c6db76de6d52fd4326f7aa8ec955be3c62aed7188db 2be5959d53e19c49a6960aab64e2488996addfffb2419e3c8cef3db1f3c33a35 ba81236a84fbd39181eea3b60b1a85d6947007cf42c93ac15ef7a2cef6a4d3fb 8de16d964a3df51e199b8df767470f4508ccfccd27b7f6d78b62cb9398bc55ea ef9b3fbcbc282afb025ca8db586de74a30264588f5f1060d243c7865e4c18c6f 4dc1a34353b429f5e1811290d12d6e69f3485e6af9b389843aa1b5a067ced872 c8295f4fcc4620e37171d648e229e07c0ad3cf81fbdd49e895c08ca2dbb1a73e 39ed7eb8e5da66c5b688e7447508a283c96bba5c55ed8ec000e42ed4872ff145 4f7f18ec1e2eeee07658a986008ba1086b3a1f030a0dffb339099f94d4c8f3d1 c05959888131b964bb63476d63904d04bf1e62538e8c227fa6d6a84b1b2c5755 9af1683e1561c8c82aed8dc257a5f301eeeb76d538cb62c80d85825c3b58840d 58b9d74270c39051b00080c63d5d306b6c7e8ce773b912ae34cb528723b31409 75c8f3e9399c3e6a233b1f511138af008476a4fef6749b0d039ad92edfa6cfa3 8f2d018ba38454006905612074115d7f52f021de849afb63834f621b0cf86813 edb4523261f4218904cd2316470a97269c10b7dd6278345fad3c78508947624c 89ec4715a6d64497ee9825ec916c66ea04f4f6005f2c09737d094667e0e2c134 60d872a3dd8bc3dcd1e620a448fb2ab021b5dbd0e42bc54fd1a22173b7af796d 7aa0e6c13aa26d3cf23d32b3aac2f4b9f07dd8975532d326fbdce20cd978b271 f20616d23430a3a6df1fec7eb6ed20ac2f5f4d17cc4af342a92928939bc220c4 30ee3a8103f3b6869b385f54aa458acb4fbb8225b8cb1ba1ce0b313b9ab32830 d2c9241543810e19e2d25fb2b82f1bba7a7da6d72093f8aa35ccb1a7eb9d1271 e716caf713eb8e1c3f8c0c02dfcd682faec217cd7fb0a872f9a6e498be2d8f1c 8040cbcc375bb11d6c4da2de3035a95273695f28b3f766a966966a217af215df 118137a60cac4033aadd331dd3c1d1511d4c6b3ae41b929c05a31252f43d097c f28183b869e0e416647dfbdef14a6e7b1276ae55c760120cfb92d5333cbb870a 39 83ceec6c7a4d0a5ad1520bf80f8789fc78fc3cc8b8012445e93432ac861a590473325f96b6cc4059aac708961149740c802c6971e8aa0ea78e5acd2ef57f2e0ef7a3ad8c47d4b2b55f421cef57184e760f0c42f1fc9a41e7a6a6332ad881bc0cec790f60f5035366844f44a16d0e761e092c2110b5c9f8f758f0ff498b8bb601544de8ce5fb3aad15502d35b615ae5296fb67965eb23c855ed4886a4dda95806d822c013e83315bd3e505343e61e1af752c4e1e8994d08c732c0504105462e0ce6a93ba83e716e73b6c94fdf20359c0053ae2d16267ae20e2655e83416f556041d0de374fdaad4de679ddee6882121cc820f8026cb670d8b50a0935b3477480fa613a9b42e2a6e71b7e1d7577c812a0b317b33eb0ec354d18ce51637b077ae01c98bb8969300cfa9dca719d747fa1e3be21cbd9801ac7d04a81ee8413fc1f4019d5245451cc4aace041a69edc012235e6c0bb82641ee8235e0acb03ebb3b700a021acf48474c94747a5a13296b17efbba4df668fd2c7a9a31d94780c43c0ec067781c5c728361eaba84516101155fa115113bfa251d99f365bfb9005ecbced0d6089f2b49a53b03b153f98ad7e506d433b61e4b13086baf34f3b2775ecb0a00e30b6b6aeb606fc2a92547fb584cfab7fa6a4639e4de9f30397548b5d90f267071a32f1b3060e13926cd7f3edd3a1770cb6af37680475b6c1a435503519d90e038403844f83397fb841f634f618bc1051b8fe1366366251b10c2c2f3e82d91d0029e9cf2f0f294b8ff0d1bf72090f720f8b94334f479486f7e5ef859f62ae510314c0e9a428ff1b2cf9173382d330ba47529767cd3c772aeaa6087841b94fbb07bbe309fd14412bcc5b32edeab572f51e5a00311eee3f473734cc15fa7ba674038a27bf1f0df73cb5e2d4b743d73164a5b3cb8200d9a65b4efa4cdb26422b430957e93b87bc6254c1e209cc1a27c15cde689b80de640a74e094e55111d3fcc10a1bcffea31f1828e948ba49fa80e9d04be923b09d8e86e2a30ca405e7a506aa090242dd7e3318be3ff67f8d5647539583ac23a3cca1ce500e9027d1cbe01b930028905d74c15951524b5500cf4e66a829578d1a85258f41bd82c39b57656c590e62ca792dfd788975f1679b67a1289db4db1659e576bec566ade726986ff8e10a2a9db7425cb3b1861fe2716ce74ae0a4256812d6492a3b34e5f28f46715089044b627975b662eb165c0e996eeec6c58e72f530c3bd1a81362ec07899b815c506e42700ccec4019c3b27b85bf7a12a0cac41a26c47be162156937d21328fe1a0d18b93249a51fad9c6a26237286293e28aed7793e44a34a3b60feeab09346f104e31f5278689c4dac9f579da739cf97c5a2449df5b4404468da344d458ed21f075986b0ffa673bd9674264c343ff20716bb74638395917dd11fb87a6209e87203f28c2f55e4d613dac94ca265707b48130662d7de4a7d6ad66c928a445ed4fa0152284c2ae8e1622514b47e8ff23a883806640f0d3bb2c3ef3b006ae16d7b2106ad8e2d7e2a33594d13bedd026a049e9627d91adce15a92c05cd3cd1f2806550064a1c9c97179d4f402873ab46225ec4c2b1473437cf0fceb85578b8c8330c8040c923be0a01323ffbec6809a85bed478427d30162d1f4c6f9ce2f3b8b637aa0e41027a1a76a2882c08deb4eef8c400797970ffee96b00fca66b895569aa4f7073bb2bf422162fb756a0835186d1da68a1a5f2a63e8c992fa822c22dbee40d8083576fb60345cf2462ae2a09cd7feb55f922672dfdaa74bd708e63f1a3c26bf065027721a8eca31473aa7f8e5fd68550c1e6fedb0abbaa140def967b68c173004a3a78fbf67ed4c435513282355bea9d6854fac15dcf0741294a41307d0e5830f938d8838923ea212b145d48b6dc356d5bd11370fbb5d030fc6b6616df3fe3809962817ad20b43bbb871ca48de9aa90042e94fd9d7864d64dc01753af5b02190665f38f8e6c533116bdb9af4fd248b362bc17b77cec3c73f79c2ac5ee460d790e78875fe7bbf2213638309afeb49bbfa66acd8fadc4badbe601ba107c97a90b0f0700fc754f01855a2405d83970e83d78293d81e8573f228f411cd52002258b00d1eb28bec88c71c9213fdc7375ce9a8d7b198ede9ec4614c663199b536c40404c0e810b2a1af8957a8809f6c6551bd0d7dfaab933f5adcfacd44511b40acb50c41f8d30b1a7a0ccf90abf02b41edea1188367945c2138ab56db9a515228cf802316903d4624c8949864d8ed3d145ac3e5ffb526446345b606f37d7b001486d045ae4dd4db6904e02ab24dc4ad6a0deabed09ad74f36469510ce00bc351d928010e9a04c3f9f90d01a42599867f6a035ffe0d1c7962d889b1ead85fbb1df8b2051f582fab0a3e4a317a1050ba95a8f8d38144eac19a87f4278f2cbbd47eaee7010ddc2728ac62c7735bc5d1107d8ac99d287899cffab765011fc65fbe11b6f207535ea960ef2e21fcd98603cc7d0c432314d18128f9473472f3b11b69f62ec10067892d38494cb322f2a5be36b43745e4cd73cca1959eff3f589b4afe2a411f066babe89ae15641d6b4bfa51b0128c44d1615c21fc50e103a88281d3c2301a00fa0eaec90851485be76b35fe48ded2c740c7076e6baddc2531c446e6cbedb7506b0a916ae48370541f31a777d7186cf8cea08507648dd31e8e55c671e1bb9d100d274d568f001cd5915c8dc3f507f09e203ba88c878e6a4e5659a6b3932dabc05be46cc70dab045696eb0fc18a0e2390b929ebd3ed1bafa6c2976b1081ba7d208a616b53b407f659cc4a76db4ab4284307209598e7aa66c956db300d22db92909de155f8619d20ebc3ae3e58e42b81d06c990f212bf9fe091208ac73164ec6d00a6b8595a5cfe7e095afdd703d05f7a84223e925f921befcd74d82208c0031c06bbfc5d713991dad458f106eaafedb00007f7bcd660d3785ce9d17f4e0acbe9026eb5280a3be5e7be37395fd38fe92dce05a78fcd3ae73d3741a4b023fdbdd9035d8cb1ae47dca5c5bff08496d3b531d040d65eb2cd66b007ab5496759b33500b2f0e339036e5b9163d983aca2c91af9417ce1367892df24315bd530f2dcb180c60ae0b0c7b8b121ceb6a1bd66d004ba4d2d9eb00f017f41924fface09e4f7101430af8649cbbd7d0b4f9a180cd421e7616697b272b8246e1af07b7af85b30b0ad7b9abf1f82c5e3efd752cefc89f3a9d21e457c14cbbd35b7b90b15c94bffd0ec3b28b90cddfdbd4bafc5f073ba5a5235be9881d497855ff5946720a9011c004fde8683bf3100975e279d77c609558427a2eb48b3f39ec0ce8f705307126cf002161ff4114b2d7159f4790b936d2882c6198483cd523593a302cd1d0e712180f9a4a9151a2b051d8daa3002f5ddbd3c32076702ead26a28b2f07c6dd69e3aa022c059ab46801c963563226e34801c0d413c780b794441724524ae732d5a8d60e6b1fd14eccca696dc2442148225267aca26896b7721c9ecb9b3501019e12660ddb3ca7a6009ff56f0e46d0747d34a7206857041d99d9cf8552683424ca570201d62b98b182d1f3179c5ebba28d3505811ae1c818c4fdba44e2e7fdce3c888e0c4e3b0a78c8b30ec0e46f4750d58e57ae4c44ed5ba1998d7ab25dc51569ee7105c15b9a2e326fd9a177729a02948db53aec68265fb7f831ab157d97dfaf953b074a285523d65eb8372c7dfc42c6089127c0ce3119b12d744eb180c95fdc9f1a0ddc3c41fca77587e0923d6d447dd5f6209c004d6a8a5ed0cd544df46d9adff702dfa407c30f508bc92e551d498de5d5525ea7c10c51ea17c39a7ce0aa8983850a329280991d81df6ad975087720522261f24a1558921d90ccbc4949194b5fe00101e331a803b03eddb5231b3fe6b453135674e997718af2d92df38d984a6d6d04805c538048d62a6898f9466fd1006be8aff74817d15c8cd15035577c745e2202ec4781581c4cdecee96ac40c48d6ed2bc156643b083b0a76aab5e93cd5000a0af136f620ef54ab23f473f7ca09a6e47e4b11f3fbf5e4f852f12d84366d4904054184d2a6add272670d33f4d869b2b56c347123756b38f0662c28f96a7875d903b138d677738c58c1f649ac13f2f09b50ac27c46f3cddfd8418ff695d81879009ba226621a2baf68fa5e58b168ea763e0c5ca82357a6b6062e75681288483520e41f8520f5b5279df04cbd8f94416640ca021f45492c64a0b946484026f3e780fed1d5880ab92193cc0fd6c9e6a18f5537afc37ecd6c5c079f732a9ffcb7c7809818cd9cc6b9b0db7f56cbe0079d9d1b109270aed7de4806d8e2546b5fa94700d32094fe0ab36cb9b52a87aba4c089a6fc538f68bc0c4e9927eb7d2b37a6e6507c163c06804b7dae93e01b1f022d9d5d68c63538ad9863d6555da910fd1866f07d8e339a3fd727c89ff5e44f766e6f08f50f02b20763729ce3dfc64d312fac20e9a873e46c0e5ab4ea4608956845b136838bb6d6d5a694b89e9a24e35c0d96f06 -generate_ring_signature c3ed92bde9e8446efb5b9367d42ec2d1422a2cfa450a71141a7097a49688df52 f0dab29e41e047f2c8183acee8e0708501ef4ef0ddfa6e7f17a9024b6ccfe877 63 da39f87cd0110e4eeb4c3569349e603a31a6fb69dba927f28d18ccf5cd877d42 cd374e7929e2a64d6b9be6dbd9cd599d84d20f7612ac652f64372b73f1877408 fe80449785af9cf3ee7a75a83a43659c6f5ba4c18db3270b87fa75164c859f0b 1be6eb1f5707cc1869a2e6ebdaff53cf48ae08eae0a8b1e531b888542cf293d1 8ee5d44e7badf8d5c406edcdd8c530b16e4e0b3b9b9bb9355befaf2efc077aea 3d438a0fd481a0eed145afda32e3476b76ac92a006fb4fd7810ad329a29b858e 6b76c127a388232f2df579062c1bf1a28528be805489d4f2705d66123812b2c7 680039121a8a1928e28f3153cb1176401d5caeb5dba958c24fcfb0474cd4dea5 a19c49f7d41672de74e063361060426d2a8b882349b92bfc808ce1e61ef12f11 8303590b8103b58ae2c77bad446c90df388ab2ecf06baac717f0afc80695caf6 562b47c8f013f3b19c61ebacd5526acfeb1cb804935d358f7c91ae04c5837419 ef3a80c7141778f0a022a868bc60312f647d43431992d300194ef4ba3c35d438 79b1823612b9bf0aadaffb6084fd2227e72cd3d7e4e9c01ed1b49a6676ebaa48 0dc8aa6f08a8927375135075e0bb400446e6444e0e8417e6124520eddf59716c cf05ad9b936775c640987b5c24e97747a556d79438e8c971026419d7d570ed80 c2a77c27771f2b48d872ca607557e68b5209646fcd51d33bc2e7354ad20c64c6 bfb6f453161cf18160d66e15b35b0cec92e8ff6f280c7f8a3f7f6c8bd7c43ac6 f51fa0445e7aea779340cf8be0bbf08ebd182f58075bac9d6444c6ac43fc2f5f d007b1eda8d4fcf518760ac888c98c55e95d31a80f60dd2b4ac977b16ec218a6 621bad8defeb944bb8d45d8e639188f7126c575c99181c490020483bade02400 4be49d6eca74a476c53ea0b10339efd3f1c7b3b7ec94ed1231ed9d98d0c06535 e290e3eb2f3610c491129c37300cd2e06bf327604430c38e4ae74c2378521c01 404bac8f26b8258897596efeb6a2038769e5fdb5bd9f025879e62de6099b79c2 d831751f5e3f66c566623b38c3f42772de731327660ad06a3b074007ba95a0a1 1a82cd19f29451407682902139f2c23830fc162b921ae2ae5aa91829559610c2 15d0830fc658ae55e4dd3911034f674a1a401f09b7cc11c3cc6f4d2245dcaab9 f48765e2972e935d57885af53bca175a9f7900569120d1fcb7986c2f42b3a305 e3e2b896e6bc92b4079ddd3a3490f9c9fd0e759fd4fc2709e1d742a6196e49fc 02c733dd69cdf8e3890d006ea71c1555a8b6407fa2f3ac6d967f6e43db4df100 42d84b667f691ba1f585b49d1d5fa029da5520b0686d8798c592ea844137b3f6 5e6605e0f7abe6131b382e6a8759c4ca5fae77ec8291fa878935962ee02e7023 43db8314dbb6db17309b12392187087f92934bf63d7e052ff7df1dab66251e46 c1de18964d5b564728cda7674a983d85b9e61af2a5012489473cb9bd048de0a7 80972ee2f028d2f4b2a15800a16152d42bcc81ca3c2437145bfca495514cb69d 83ef50ea7e02045bbf4f158d32f4b942284e63e5f2b780280a87703e4f7036f6 87604dfb8212ad16d491f0d56d7b311b4b24888b360c36b5c1a1129db3f3a8aa a3a8d82b787407f866a125572c62efbc1da5db0ef23fc74562d4390c68aaff56 01fa56294a12caea1c202ced527940cc24c4a89dc8270f49ae2d18e842400b28 9f931197ff4f05f68b25ae4a86de55987756793579d84b5d62de550150164c44 b61458c7d49003834597f78d4a2592d1fb486482e8e7bc3c3a57f49ef2b8ac42 d554e6418a72f0331d05757d200c3692558f334802719b53927648acddcc3351 cbed989db9983b608bd23cf0f5dbb80015add6f0b67b1b6774dfa96e70d506d1 795ad4295025bd980139455246590abadab9203a035c50846e4e6bf5faefc729 75b50ce254809aae0d537d54c0156197e20dd20c84bbd9a62e2e301590ee0e3f dd8006d8f8db4216f3a0fdc27a872b9f2d16c511d450ef8ef9204f10860d8453 9903457d3aab9decd7c2e07202cbeeae6191259e348d0e05fc8439f06404ac53 40ef0206e8abb47158988a82b39fda91d6f52d592e49ddf90181bf959be81da7 baf85bdc56b8033c1e262e08e123e5353cb7f7ddb2b7ffd74ccfa529227853c1 a4d2ee759a828c54e976a8bbc55294e0f971cae0f30c4ff2fdb04f07de39c7c1 a0d47358f5ffe1d742a0eec679ec72cd1481f7190cb0aff2baaa141b68899de4 9cf118c576775697f38f65e1605978c2af376fc5e87e18c7438b07b48cb4ee82 9f022677e170ee00cb689b13de994c7ae9f5bac789ee8c8ae1e4c5e7e179e880 9064ec6c92d15bbed8c3ffa9c54dd4a71c3e940373033de467234d6ba7e863ce ed04f38c3d6180703052c3cb1150e685ce390ab1491c85cb317533f10fa252fb 5b71fdcbff0fd2e0d68d25f03b750e7ec3adf0c4455b0e06bfeae8437cf746af 7bd75fd7e511a36f6462dd1242f4c53db2b6fee823cb5bedc26bcc9c3bb52372 3b403b21997c01407a6af4215fd3c8e21b4badfba425c032623b182bb2e22ce7 2d15770f93f004a76c5e4ccc25dfabedf39e57420ec152fdfeecd75eea79d139 f1d12a64020bd216a36776fa960a50453b3a3cf398ac5cd64ede354e8b882868 f1ee519a49f43b4d6eeab6922191742c70adc22d3eb88f68f285114600740f06 8b48ebb921c0dcea1e01d7600fcb33fe4c50a1646de0f5feb775cb9bd87b8988 3485e0a10bfed2eba8dd4dd81e2ea42a7daba5c4ec4a035521004ef90274565c b6a73a9b1b67ebc907bfd0429107f58b7867a029b9e48bde23b821102f2b0ffc d5daaf0ec5ee5a4ae2a10fead09449b44b165bc5e657dbdb9aa0ae2a16824e02 58 e3b87b3056e8d5ec464e2d1f3ad34e3b535fbd354470b3b6477d72f04d87d50bbaea78132b9fb9b9bbadb027457800641849156ab7f756f41df06ef0197f230b091653ad06addccd2ce3dbee76a9055f23f0d13f330af4e59e22ddae93709b0b724e5cc8fe82a6206a764d46f36068f2b59d73ba750b9dcb0874ff615813c702d9ab9c1e0872811e9ad9da881c7274e3ae855882be8089138b2999f9b7c8f60350d82a0d9843960ff24c1338de8092677bc56c89b8dd192215865dccdbc6140472687a972c283b649e84c18e47c32fd830cd48e3933e8f08ef56a1f08e5fdf0338ebc30f7db86f4db86cfdda1b8cf304842582dd1449cfe9ec065a685d0ddf09a49dcc7ec3a311f0dea71a2c7428a9b54774ea16848eca0fb9e5037ce5f6660bd12dde4adbfb96cc167580965f35718cedeae022129bc4cdfe54dc5880052908037769137b6f870fec12fa3ed797440a62f1218072fbaf6c7edd9d9228146d0aa2c87995d7f629413902af2b5a9e333c55db3b902c377c4ba1d97a77205ac200dbed76ac8a8fae53453eec2bd13e440925a3744260baaf24bb5cfefda6567102bdf76195eab4991612e5d9932d95ed0fc064e4c0a100f2cf9ae4ee1ee071e80a3da9bb3a6df41005d841bfbf62d829f83d6d7d917e9456bf430288500c1bf505e8109cbdb3f937b6054e14b8658b3d3b227c21e57b4f521d44c1e6ab6ab6a60137345891d2ae84e620b9a3a0a9cc14006db20b2ef222c449894a41fb93ce4b09f1dd5ebf355897e35b56d87a131df647f7db76e3c36a71572d288d95134c4a03ba74e02107582da9e4c89ad539562f49e65c93e950ef5ad208f32e070c3c4f08c62d6bb907108b39081223416cf2d010e20a4db10237b6fe8c6768f986518a07129010a952671fbd5f99dcd5422d44cf947ec5e381c13ea1075ef79fd368680406941eef448775f34e0cabb1abc82737cafe18b7ad941043ecc4e4cb715e5d0d309169cc93dfc671b046ea9e4d283bc646870e1962219ec90429f3b919486400c3d4200722cf64a3dd17b6d1328a2b9f630f3218ee79064799726f9665d7b801ab7a7da18920372379d049e7d54c35db0cde634fd5d8bcffa8fc22c453b5d5082506ab1ed6fa16a9287316b731fdb80432e6bf86519275f40f37b202b7f9f20fe601b902a0aa8d910116d8e16ed0a74a3bfa426b2b901eeb9e8c372edb07640988fa05a67a83d70d15ed1dd34ee3231d1ee29c390394242b6033761b2ee7a1052ca5785475fc734bf8b2ce9d18e797f578e6affac7518be363e657fd4ca7290686e68267a79a07a867dca4e9759989f8367be88ad43b6f2f5b4e72ffc0f9950a139aebd0823ad0ada46548ea2c89a1fc0ce420cd74de6a5cd785b819836d0f09f12b71f67747c833fb81a41f2c273e0100f97730d200b99d6e5fe24c18e49c0f5c5fc8a27efdb301dc287e0ab0d7f896c77a859324ab47afffb2e34e75051a0cc2bd1393eb19a78ea83db16d298fd40242d7c639f8c650588c6afc640913ca0f6e27f9a09277d516b06cda2f1a0aab61f6704f626f171dfe08f926351292dd0515fbdb0f8d85ab0690294172bb170f0077cb792440da0d29d9de18b8bbde50021d86e13bb9488ea273fc4c0c687413317b63b2b9089114fd13bcf08e00aabb00f64befc2e158455f02c9d6f73265e3ced5ce4a64cb3640b159bc12dab0c55f0456c4aefdf7e0c2ffe6e47b12f0fb334c0e94249897e6517a9e788d78e1ea9003672229244cc5c6ed0462d9146ac424260286097725de9f5df9c13caa4e1662088399b9bb344117311fa6c080403d58126054a54daa6ebf62e82b0d39a1ba9a0ae3aad114feab804336b73da9eacf15970aeb378c5d05a658447a73732c1ce00c7a622f023d310a00a277ee9deefa7f388686fe6b51eb4dcdc74af0397fed60073a6bf2b9130342360e91d9bfecdc09e6714be01b5ff36dff8867afcb24d570092ec295789235eae689af35638a3d9efc618ecd88dcd0d44d341463049b0d5b0401a4b919ec8cd3f2d79fa4d4f1dc0ec8085532e19892f15c37120c83d176dc0e33e15eb63ff37516a694b9f5510e9ea9c8474fcea2a45b0f130d5eea2fe0b70a86d05f17d99185b41febf1d16df22aaa9e755f49a9f30d896b500d4eddcd900e784173bb369b08618e67ac50062e97a164dfeec8ac8210dc09e8e9a0ee3efe086575e9a9cc8a05056a65cd98f25bffa9e702e849fcc322ec903a99a5bbfdd40799dd2c864d7944cce27f6528a6e4cd541adae59d9aa107f6b1c9f6602cb80d0c420e6eda6b1d23a1839b6ba41f63d24db06b63d2b6fdf459ccbcd41f754c2804daef6328b6e24aeb1c3007dbd2440ec490dabb082a626dc6b0e48a6aa56bc1073160121c530035b696b3942a63262a09e6204ee85e91f19af6ecc52c68be9103f1b8c33a94dfe4fd332b61fcc274d465000a806d0e5f92f43e83c9d62d46de08db4ffa975087a990cdfebabc3167ca238a15fd693712553087ae3fe0d3a13b09998b28e993bd59960daa6934895c773dd62f3da637924965cf1f55fdd0f5920ad2435a942c1a52ff66af5495c33ae776c61ba1bbee26d8886629cdfa649f6c08e03a42d051346a1a8de61429293bfc619ca87c326b8600b59a845ab8cffb7c06da8418e324bb5922a96c2faabe64f40bb8c34c2f4650863b9cced75ea8ccc2041ac4c690cbc711e670b58235856883acce6f9dacc0ec3e5c70e5e5d5e84e5e0612aa06819d17b253de1d1e65254dfdc94429197ead2c7da584092744a8a689009582e94d5603c13f2b32beb79547d053a59f020de992a1bec3f0c44bf85a1c0e84cb6cf0f74542096600ab63d0ae0b9ded2ee08ca1858ed03264531e0bff8101fa328b54e088c3f974f407c5dbb2f6b17dd88d6c0ed20e0eaa98a02cbe7ad50353c7d241a51dc89aa62493bfe9e36cd4a1decaef486b00e3928f03cc47f0a80db8ed73c9fe552ce3f9448a8b195edeacb1f6165fd02e9fb7e92998d679a7a0025b2b45cd759d567db7c7dc7f271f9bbead1b76332b5350c9366e79103e020e05b3b3c536a22ff7c58ad8442183500296bc3d7a501a54df81d37ae89c53d53d00c3e79f19a7b68bc32c8599275e8e2fbeef776ad3bd1233f89448c14aef20ba03eb5260561ba2cc96e8554965d239dcad9e3bb7992bb21a3418c18e23f699c702f22e4dfb5a97df50455265b00e28190579e730b14757b32384e4e01a19f3480f1d025852a8fe42ea6d83aec259d25bc2d7b44d4c1f893de320827ed21b96a40b3d4f8ab663808f655caa935b7f51738d0c758a21bfa6175ddfce7e27dcfb4b0a1d02161e78ac17ec36b83926d83eec437a9a427bdfb31cce375a5e536994ff035c69ae446ffb68c5bfcc9fe773e829acd8e28c45e27e12da39134fbd89fc540dbae1b298dd5f3fea8a41cccbe770fa63d6037a945bc1aa040426e0f63fbcd60f4b59b7f0ed60424885781e78fabf5d714185e396b6cfd0c02322e8152bbef504624f3d98cd9d0436922b260a422b63a65f6a84159c6ec7194979b0f53a04d404cfcfa81716ef04b583319e56398fa3f6efaf23b3d75d81aa4f4cea6017c242063c18e8d2b5aac7feccd78ab025b29f2945b1dccf0e6ac2a2db1cdc8799a1bd0486737b7f30d4388c1adfae8e1b9aa17913cfef1d4ea96c629e95396caa1b8a0bcca1bbc540fb602918524cfca69a94c977d25220a66eda3da589d4137252e6026be6b816f6d5242f9b0313ab2a51e287f67c4ac68d1a81278085518ad1a17505d999cd4be65bd9eee63e3f7cf51b1bde9c68d64f147370a78a49ea5986ed340efea3e645ee510cd1b71d276427c22ea41d9a1da6a3ce24464886f3cee0a1550a675122c20a5c889ab3d0085097d6e1d67d0f6a6be6c84a4007fad968645a6a029c4f1c5047e55226605f005317014c6fae5605d91515b91a50c482306129b709fa5ea891297eca5254a1a3576c83557ebc6f6fcf0e8e6dd806a26078cf1884020c13c84893f37e659492657f7aff1337125401fac939f3887eafe995731cb70cb5e3de6b41c823eeb22c9815155fdca9fefa31b50321329171a34b72a716140b693d79a7c051112c631e1ababf9db4812c0c82f08cb684783bf34ff0c82ead007b335a583d8e945e458957a0c490984d3bb21b6e03e75933d4600083fd55bd046150a20f0e80d7d136771366425b3286fc7f303acb07ab4b272c2ddc4bceaa03a3695b175e3f875f1ca1db89ea78075bb5265246d3d3aa1f005d8fba724d1f03b49558d30556f62ce56b6ce430313c31e1a8a5aef22d5696b66fa192dc8742098603f9cbaf0836ac464844347720996aab3fce4cbb7304188bfd2183fb5b7d05960b2b86e037b178757397da3df2a2d22d326b3c06905eab5233f349c17a1507f05f0f2f538fee1a36d63c1cc898528c66240acde0af5d65b8f4f24c80dc2105def448c774d2ee1a929671c5f204d738bb1114bdb511d94780cdc109e5f62c0b88b414d47a9170f12768a8efee754e0a41de48020cc8204890b8c6fde8d6ec0596d36e26d2a504d3c7d0e62a3c25b19eb9c2b475311471e34d98402f2a1a89023ab42257f0f17724f65c555467f5b8200c02cbd577aae544488ad3c06171f00afbbbd35fd3758db69107288f0a8bf2b20d53597eb00491091910bd4b0711c90324881a947369bb7c32fbe0fd1c5f2e4e99261c167d535a1fbeea0523c7d8f9061fe4ca26567c8e513682be7f5421b67935a4de003d1cc73a2893abc6510f4c05cfd2ffcf9deb8d9a99af7a5762a6f654fe6c05cb78051061da13470a642f9a007bb79642f3161439ec74875e661e3cf3ca255874c53284f213a72f24c4848b08fde251d94d8ab069d25e1dc74c84cb60ba35131af0dc93559c41f5bd2027730f63d29b5282119011d2fa551df84de48ef6c9416aea59e63f77058a5ccdaac706ecc6396b098a599568f9db2dde6f082ed4de9e9108f3f6df59f589cc85a0900d6ea76d5c8e411de77a406504171ed43a0a8b431b0825df3ad3165fc2017eb207e954154a2fb2761493ad1305002de113483ca521101289747e9cf26e515b740d1a9f8128babce5e6d71dc371975fbe33c0cf80a392cbac04527cf1ab57f3410f1ed1efc87793a29c363eca914ce42cf9fed09dedb87abda96aea15c19165b30d564d639ea310b04e5d6025055297869a4b6ab30f6bdd05c94dad48b9fe5cdc0144e12aa9aa51a72f44a1142545077fd1fa10a5fc136703e7f63e6def7476900349b5b33413445d46430799cbaf67407c81f5ac9b048a3d3c70b15a46d29dbe06cfc33484f65ecd0c5a307605b75720874fd61861cfa6fd4378c09276e4e94d0ad6f29406bdd5bf2d35cdd8611b5c659b1288815c5890d6c8b9b0dbdb52a87f044565f8f556ebd59ffe09c5aeb28ef4e46dad07ead92c842788d76ca1b58642057a0002d33fb13590c7ba094f874430008e4b34a00194fb4a499b16f06da40206a25bd4327ceb8761703b54728b9323f52cfa6c268a2d20d2755373c2fa8dcc06fb5bae009f8e63a37568377e43fb2b2a7bd8b69e6bf7de12bf772ff1de8b6b031cd9d18a36b4f4d3ab4d983fe5ca9fdab035f843a90ac2d4f59cde8a6fdf70093efdb5dc922325dac958a9e9ec994567eff9375d956ca0177fbfd84e5887430e -generate_ring_signature 33bcbd4c5fa68af0c24eec449ea51bb72ef7190c0ece9dffef244eb90d6765da 732fdd3197aa6855d284d6bc4749adbb4c265336b62300984ebcf74d03d31ca1 1 878686a68ba5dd5aa696fd1078dd94d627486bd9c11d6dd328ff0a9b6c091f0e 658be715fa02ba1b7f4eafc02f985fe14c9f7d25f6bb4663ff87ebdc2d63eb00 0 cec5feb9a7f2c6522bb28e0d3287a0f6fa646237b29094dbf2670b246e7f670d22bc14e5b6674931f4702e824da00be5d922edb570b4d9cd99590063318c460c -generate_ring_signature 61c98e5693c58afb5c7311d89c4589e4669b52d8ff4571f293ff1f98af9b6224 68535c66978cc85a04be5b88c000167008d36429547ee67230abc78d01b3a07e 2 244b5e8cfbc8ef701d79624bb3499515be5273e45b73fbcfbde5b238121567b6 04cf46394b267c0bc9a5c9e2e5f88b07d59cd5a9bb056f94b46e816db4789be3 f0c934fded787d254b29eac8eea8e249d6217a82e8e9d5bd7565611394277404 0 a050dc70373e2d18df942e8efb47813dd670e0308ab4cf85543b299ce2ecd50d72976fe1f3ae0091edf5420f8119c44019a96db05a583ed4a7fdc6a8f02e390447ff270eac3ed4c42ced54d3bc9e00c86bbf3942e14e6f4570fda2c4bd97a804b35d131590839f91938e8e6e24da8c39875fc1d1fb4d1d7aabde2ea12884380a -generate_ring_signature ccc5dbfe3a1cdf9f9c21948ae48b7cdf305a2054183d05654f6a5b8f1d93b6d0 406a3a1d1b23710ed1555c857e26bb6185a783eef109fbd102728811279bf26f 111 2921b78ba30bd3557e4b772b3d61ea6a16138631c4ba2d8613419171e99e66e3 ad1f075026848ef6bc1680d01e676f8fc292a83df780b49224e9b62c215f6eae f0ce3cecd97138d3819ee595d9c3edebaca5e340afe2f07183373d0dffa0f4be 26eb1a7d2b2c5bb31b7fb377b2523ca36648014dfd16c7f695bed93b718e8733 92523fa28a9a5267ed1801b4d606e0fa5c1b70d54c4634ad0365c6373de04a81 19c3b3475cdfe8c9e801d0be4def7e2d002cd570fb9d9234fca3a0011a8bc5ee 0e9593813368b47bb51b706641310e983b927386a90a00398108e8bb790ce5e1 5d46bdc035314dd0de17318cd46d886dea88c67150375407f34c0039989ecbfb 0e8d745fe8777c695390abcbdc771c9a7038cc327c417f3b754118c65b6d6f99 8b05713d11a953c083ede9778e7e7bbbf4410e35fea2af5b4320b0c7cc4ab789 13761d8cf0742d4b60bbfa4d205c49d4231eabf718ec4234f4036ca168d9d1ec cd2db44254c778fc88a7737e400abcce5d893219d8460c6ab3511b42512657fe b4534bc0527d545be7a0192467837a675c47e94db7ab43ea0773bb968a5740f8 ae5ce2355ada6f7780d89d1f33a249d4dfe7e079b5e102a82314bac3c04c3f6a 3838cd930a369c5458933289ba73002c71134a5e9dbe16c9dca9471987882c26 ff437f8c5906e641d47e53514ff94977417cab286d5f9998c0a7b8177173616d 9abfca55d6be1bc9d37e412d7c35202301060b89e1e700d5514d02d9011aab29 a3232deeb8e763d369ae5c94553aa0e264705dfe24f1fada06a1be1ebe93dfb5 8f51c13bfcf2486557450ea30970171fb0d428c02a823c7f9c704da2402b6594 1791024939f618fe68b0d73e853103b1fcbded7abdb65fa15aef63f55d8b7f44 ebebcd04a35a3eb5c9af95cd67d75289e616092523b8674c659d55209b9b2d61 4702673f7fa85237ae5f20e68b966f4fa9b68c8b6c900316e9f3864a2720b509 bba906384ec35445dd4fdfcd246f38654e8b9d1e3b24911e8c892687d1bff04f 1242ea27621579afd6aae5311988fda8be14600d5fc64352af2ebc4dd383f7d9 7dbe2bf95b8051ea0adaa52744aada889d89e6ad189ea26d8e8c355eb469571a 4cdf70da3f89ee7210375564f8d46aa5fc3a3c47e13866abac7c0d618346a042 dcd45fefa6e3de7ddcd3e105f8888bb6749cb565b2d1fe67bb1b56fef790fc5d 9507267fc1696e5c35cd3718490dc2a853cd2f6a249827f0235d6cb95ecea192 fdd370f6de86d821dd2ea27158eb2ba1a9f6bbe1626485fadd01a80a92a3ded1 575e8265225649995b947201fd76719e51e56f15eee0d5e6462331f69650bc7e c9c8a863860d12415cb601e9d8362990ee855a1e5d1d70cd75e15cc53c07e7e8 dcfe0f0f4119063c29577224bd9a7a1a3800d645e0db7547ea05389f45e73c95 76b4cabbccb35f51dd5f61dc72c717ea336795fc71d7510feba7a84e5beb5fab 0f12bd1eeaa102f5531d9fcb6b79423393fc47bf0e7f753c814f5c273a97ff75 86292224747d0e179c14b7a3e65e4561281b6a827aef6afa2dcb8f8b16571b92 1f9378d5cd626274da52a2b71027b5ee6335871550ffde522ff0a6f49986eaf2 6d5daac0ba917f50cbc0c8502489320d6130cc4a2b332b34556086c5084a5324 9ceb51482de662fe631eb9009212b401fe7138b667b7bd8cf94d9181f955f3ac 958663234c70ff625e10a10779504e267f643aede28861c4a0759eb01fadb1e3 d906f333761bfd3b113903255c1589cafdd20378d85665af168d0aad0367f83a 0635c64549ffedc46dab199ebf89fcc8ac599c8d89970f7133ab5e235e19f832 decccd21d763536ae6d62e9c56019358437f7176b0c6620b51bf59c5f2b0a518 62bfeccab34d00e92ccf718f671b735d07fc475bc5e4fd783e487666b0fff721 adaa55bfbbc3f50b640ccaec02646f4b152fef46d323065efda9bdadaef91ba8 82bfa52412799a07d83541bf6a3de59bc0af127e4310f09fea4d1971af8c6aa5 4c796345e19c94182721175a198a8ad535ec0f547558c7f07cbc3127a1ba0d87 ac9bc2d8ef7f43f80ac9d87142545ec4d14e54f64d4af3aae305bb5407a819fd 934000e2895878f33474fb25b205d5064c181277719336e36e688133a93bfda2 1b3ca7399c0b5508f80a645e083627435056ace47a15f8fe9f9fda75a0b17baa 9e360d75e54b81e3434caf08a8341bfd70abec216f1760388e7d68aebd87e6db ba19f181e552de8ee972884cf82d87abf397949851f9bf867a7bd57656fb8d80 a3d028b2702f532fb6dd99154d310afe882a709f8d0f194a3352f800b855c91d 6c477cc3e4587c389469d20bd69c196613bd50208f91324404ad58201f7549b2 a296d83f65b17869870485296b1df3bb2245aa4245abfad85e826dbd93ca4e21 23bea6d1f3a996575696f69c7b4f4e4b276707eed13cd00cbcc97bcd807b9bf9 5273d520380cd20c951e725147ee7aa942b743b8873b0ad756bdf30851dc2d98 85aca67f1e96a7e3fcf7ea9a64c5eb63dfddf35b692f631f89d179d5d3ecfcbe 84240ec649ba87138abdd7d1524c2b580c95d601395ecfa75ddcefb52fba547f d11fb311986843cef697a42c1db914979eccde0dae94d1fe7ebee7d4974be857 0af06bab640a620fbf4cc5c3a777e209c5421641b8ab610b3c5a2a1a47ae2a0e ba30f319070ffb436f5a5853d00f8013241fb6a6bc472d142f303623d33ad465 34132768b3b2dfb6d97c8eccffa9e4009230eedb6e84ddfed5dafcac3763adc3 c5903c815b8bd030162bbdae429032faca5d73777ce3a8248dd717578f0b8541 adc10a53868cc31d0d81395ff25be93e04212b1f45c00ab5f6eebc4c9b28aaad 1b841fd24b0e7929dabc30e700c6d312a8e3d976c95155a3bb3102fd9e0227b0 56f8165751e6767f607821a630ab9d2132661b4ac81e2f693e5c4f60114ffec2 6865e341bb94717a4de51578360b59cbb07a1660f70b0048d9475d28d7adbe3c 3dca83f1651d518dc458d331c9b9e7c2791ee1019cdf392cd722214e169ff2d3 72099a12ac2a7852c982c2345a5b13d7ee8fd6cfcecdf989430e365dbcd758cc 400f30e5179063981ae05f55129898e12a61dfd6379670fba47f4af1f1abd042 771d8631769dd24075741231cfda6e1bf41d075c4c60af3521546b2f6f2f7da9 1a4ac92b9c6b0ceca0a0914c21934774314b7610eb65d81639971606e2109a13 c6555b566f15c912b0fe560cfa4b5df5dea4e44624b49aa8a6209f9131109e25 944e66c093a3b22b7b64434839dffdfda5b8e0ddb6e93b3fb4e6c0d64ec80ccf 5682de8256dc5f421323d2e18e4fe8e86810eaa87c6d58535dd2c8d29d27d6f2 ac52d43a2c0fe2b23e73c67f6b0db159498140621b13b4bf2fa6311de6cfee8c 24db498c9238003c869f46647b4ec96fab351438cee5fe815ce700f5a83aba83 75ba2f7f38f6bbd6f58b59357fb99a74b46cb4252758fdd4e669d327a28fc693 74d45357edbaedf1f26950c22d83506ab3477bc671e685583418c81aa4c11f4f 61c814f9c7d4066bf95ecfc56a6b60d95eb63b0cdd4c0a45ff4d4f83367aff38 b58e16527d8d0435434e83e64c6ef570cf3717f79f8bb142a3510dce185286cc fb771fda4468b3ea8ee77c23ffa586486cbccd347fcac9fb97960262fc4ea76a a5147a14b7ef157b8288f781275b06f0b785c0888fab930c6a78d94ac818ff89 34557754636e143800091dd6e2a233b47f140010f36eb4ce27b48855b74076ba f95443b3d18f130e37fd03856ec8c6e38ac04dc9d528220481cbb49ed27a9f03 3a6d4cd695f507a0a8a404c02d1e0bd2ac93f4ef2e2056da9ef7a73e70d82f7e 5420f0d5dc5f296057a98a879fbc0d64605806aaad6b574da9a6e2ce9cab60b7 71b23bafb900f95b9d8d0deb139f0cb966ccc1078542b32dceef6849de962e8c 2255481001bf79311d76a4989dd98ff8c7f2660ab70f273b52024560d7d9fd66 2d291106b2157d69bc98c89e4034cfb9988d54b69a413a543074613cb1cf7923 3e38943714f3d3db34f5c12f375e73e99e6947fa661e86065c4041d15408eff8 7c79bb5943468383d769a23b260a0179587e9fbf3224349369bce1e5a49e4869 fe4fbdee077a24b3ac3fabed5c8708f4a5d9f844a8741cd6b2f539932d1a34ff b3fbe8f600c1f5a192bea732df2068edf5ffd6d766b2eba55ab7f6e8a8e4de8e 242b451f824ac319d890214a53a0c84ac7b2555ae80a02cb758343a607bfae93 2dcee51f5cf0cdcae0fca85c195a0f1ff1b3d4e468c9bf8d5dd6979b46bfac09 e35f7bf7fbeb114a5d73bc5450a2e24d171d947268a41c4da4ef322f387a20e2 5247a3383b66dbecec8e74af6fb621f5ae3fd77554c3c3f34f0de68a5802706f d727e6a804a5321ceca5df85777121c501e00cfc1beea4455cf94c68ca3beb8b 64a875b077447051c23ede119427c11392bc495a401d0631da8eb3ddc6af5c79 a1f1595cedc5db42062066166a58c45cf7065af7e52d54c26e16fd98c6defa16 91410d274f804c5ad21393f0f1758a43d0003d604e34fcc9ffd545a3d39b35b7 61ae9820c5041ff102e5232cd43fba22d68bdb5bfee2d997ed97f59edfefee53 d32a713c0ae6079b57526da8b355edd35cedd2ea55b9192e6e2bb97f2fe10119 9057bacde18dd4271192956ae16433cffdb67580478f52fb9e4145658f1fca55 8bae0275e1a2f5b71e2c2beb809e5a98d02b22abb8ade1c5356aeaf8587926bb cbb581f34ad405175e6b591d7f0d68272140a303a4044ad386bcd12489cead56 477283eb5796ec76bbdff973d760fb3a25f5a776d2cbb9f9d3a6e2391064dfb2 2096412af720af30473b8db41ee7c88ae13c2954d7078d28d284aa5bc76ae1f5 2086b5fff76bbc94f0df920c81d101e50708d68f0b18f3b2214c84b137126a8d 68b883a806d347d1ab2efb8d975c12725a68dbd162756d50454408fa12390cbc 427a77fa841f3dea30b971fdd3e26d79a6b1378674651569ce263b68e3654d05 27 b497d734f7af5dc2f5ff32a694eb8a4eb7da50d699817c9e60032c3eacd9bc03c089deb294e43a29f3ff30139f694e08910d799999027366a12a4cfadf9a65067c3f295c725bf95705cce9c373437f76ed153894105e019d7f51823de2e87309f9ffa7551627fbd74fe9b44b990c6a4a0917255802397e9aef0d6babbfc6280940c305bbb954d218488bec0556f1dec9cb3db01920428b9f88c2d31a4833e60b7230778f064f5124c4d5f0bf239105476872f7ccc1cf4e1ae31b4c46b5e02f058cf7b52c6065d8a2f5bfe3897340a0b95929ce18ac437ec7f3ff406e7ae9430525bd08e34dc42bf2faef1069092d712cbb6a0b2867ca4489523c9a4454bc54084dcdb576bef2c203c0d69e7b4ea7733e11e66ec47ac98dbbb5e1f2a413c44109d66e4f8680670fb9f3826bdf58cebe94ec79b28c8c629fbfdc3eb95d32abb10203347fd3343319e873e74f65f10c83cba16a48e01f41c4e01103d0c0996090010be030d518a28ec6f13a8e143f8b0fe2c153ed526e3cd17d65554fc06333b00a1472c6f18c6457eeea8835361f4aace6e06550bb080c38009b30ccd351c925028b2abe5793a6fab12a81e72983c405a69eaafd87d6fd70a40b8957ee1b83370283fb95965ff3f7655f476ba2b1aea34f84e5a1c6a7a2217a89094b0afc42ed0c051793005a7780286ce1e8cf2656b717d97893f92f1c48be16e7dee4277784035d79261930be8d0953108f77c98c497e77ff4de0df3a4c1734fe206777a6350c9519ba9a2d2415d2c71d2792bdb32269869a2a6496a47eaa78fc3ac5ef0ee1072591aa253d1f6a94f65f0b6ab13321cd23b80139daa11533108f8d1e7e177204d644adf51db847f7bf1b133b3b70f5f00ceab6486c24aa0322ffb41cbbcc2209a77bd88c9bf9ca35b142d2c4ed277ea9e14f072b38d8ce5bf3c62b03b69b1c0ed8d5365182e12054bf419942f5e820f92a832f0f738da6accb5a3914eb98d00d839b8527cdb674cfaf568688283e653a6703e7e7c086963854bb422f88f93004ffdf6e3044c58438b20e3c581c8f2bc8200784889e8e45786a061c5802e66c090e7ee13f42ba367584395adfb963e3035968f30874b38ccd2130e44c4257190ab2b1e65e1a76d99ebf9e6a3d6d1ef428926754d594f86b0aae1fada3169d3b0547c8aa4fcfff9f4f9232e647ad1b841aed82bb7d6dae968dcaedaca4bf6c740e88c6f84996cd924ef183fd232ad867b264f4fc0d1fefb2f92c4e3097a58f510f415533a2fba44fe7e4a7690c0a4364ec8a29377fc9fccb12ab06a22619804505c310598cbef1c4160b7f8c12715d38dfee01e277cea0d19caebf46bb3ec89001c0e23e234a0b32e44f67cb8a9aa666895a24ef30deec9c37a691685c10f4c80cbf59475fa9fd8ea68fceb648d800904e9b2a130d6826c783db64b774c54935035d92772ad24a1d390cc9018cc3766dffd512f20fa7fd5da68876d9470a38d903c9f98ac8fdc96728d6a58bcdabdb55ae3fae5a444b644c697de7b65ffa7a8c0cdc5921f5a0e393991986da0c972d1036b90c7761f484d5c256a999b886b8d7008fbaa611000d94675a0f3632a194989ee2d96aa2c3a9fc87add0ad18a20db303197f81e4eabb1c30bbd60b452dd1e9470d6205e7ae87ba1655fa4d7b0b506f09abd71222a07223400c8891a817b97ee90c31b8b75817e844bd9b8dded408ce0b73f18e32a0aac86a70c28c1957017548a286ffff395dd990e83b51f5b06bfb064a2b6b5447c0ed4c7c9f78c821b34a816533113d8c50823c2ba7493d8b45a9028f5728952f838fc658c88cdb7f70f3c1711c08f784ff3a8d2f12457572e65c07012f6ab3fde43e05b232cb1ff3f07ea3aa4c729772b31a786a732895310d7b0ba4140a542a0e170bf1b66655f524485822d300010930f474973e4f72da06e80e6c0ef0940c63c64cdb28f7d718ac4f4d08c5e750bfc2093f18e455ba12283e0286dd152ddfceb509087b7c7cb363610d6245c346849dca061324914b82aee5037c8fb8987ff6a64239ea2bf4f4c7c20850f2d50a6a30b57a96ea0d948a5d7107b50ad73b0ec51b63ecb848917ba9c98e88a56eb62b4889841e8f26d44be08600d53b93593edf53b85130a0a2d3c4ab582953889a4dba291614b4fccadaad850f335043177f684c2b0f9343e8d189e618266a554ca48bf1dbeabfbf8f006a67005c35a0fa188b14ee0f29b96471de60246c67fc8c01c839834df41985946c17054278b8e5fa0b7d0ab81d75c8bf39966aaa9da353b82956a0d8ab4834b2542408201bc38880e05af2474b51e099c6148764d78bb5f48004490dc6221ae2a6d207e55c85952edcd0e6df4f24c39021bc91550995c7bb563c2b65018cd38de24f0718b71375d637e7edcb053514d3f887761d02e61e2c7d8bebb2117ee4abcdb4029af25e7b6077499c5a16e57a0fef60f1c4a1b43530ca2cac96bb18ebcf71e10356014859711c1f036bd43567d9792bd4eebcd0115544ce43ab07bf00b547fa06e93369da0e895566b33d595c7b9c03e6a811bdc58a7a9a7ccd3675c7b183ec099ae90ee710d015a5cabb137774d5b570656eb754840015cbe8bdb558b53f450e700d69a1817282ca1e6d479d58f3c92e545454b7cb0f29f22af59277793817018508b29b34bf398681d550099450fe97f61b3e1b36914757b2828a54f305ff083094411520e6cce06fc2253fb803970ed5837e602cd7398f6fe5d7ccb11089096a1c66e919c7c8b030d6ede6344fc1c4b686bb41bc3153b2bf652e925fc171078c37b939bbdfbb6ce9fb6a58d79585db6c8a4d14364f913faa6d68e969e2cb0338af4c102f6bb422c4fee4c9714f1f97c0c156ce1c60300917370ac0e1e677022705af53d2c651e2458e32934f6723151b223ebd39f0b3e518fed0af90057709f472ca0ac2c9f2ae294ead1981c8f9f2b9d4cfea5d029b1215506058c95b2501fa0f56914df186a298d34599c4d6396bc321fee4d97fa3fabf7b41202560e2015eb7caf7976411c08f9d4e37ef3f82bffea3d408fff2da799852dae71e2c0a05399d3c122b44b7fe3bf7a44379e9eaf98cdffb77b2b706365630c6f469aed20e620f4c3ce593bd4d65947e67e111a82e90b9818666b1a67a1697704e02a5c90e3033d37c159074777aeb2bfc8811f2d2125f2e42e537e4e5f743cf25379fb605bcd020c9eed3bf429ac0f199ee13bea6d7328c3c1a39973fe53927d4c437d50aaba207f2206627585773eac7a32282b72070f111e187fab30d790f4d6f0fc40ae74c690e9defc4be95556c78b16d0f9b548627657f727f4866dd5e8052efeb0b7ec3902cb93a3bbfd40f1d9e5d718e052164d04cfde9b252024fb0f55a655609c76fa80c94d667c2ad760d89b6be75d267d8f9287d0e48f5722315f162e5ff018a416546eebe3dc4f2f47e8f69eda9ad1d4e181aef2266130593d456af8a220b4d02f05191e1faaa1243df685f1a97f89a0101ec53378e21dd0c59584a886f037f58bb8c82228bed8ce31072ba88ca03f56b6151fe8ed95e876222032fdb5e024eb6bf117271df247607ac86c7167f8873f7731cdc0ed47140ce9afb9d895f025a70cd86d1f33a1e2646c6885158989aef4cc75de7ce1aa5a8226bae92b7cb074fa459c3bf7dc1938011cc17287bb4f61ebc9ec7f84be311a0114bb8f476440847c9a57acf1d5aaf3ba0f7fc4f294acb9e9e25631d3a36be9ef65193782db609d95a673d1619d30d6e6ba382d2807f1691de9cdffeec2fa931151a3bd52c170db25e56471db52abf6df7c1ae6d19078a2bd0c068891822a865bf6934579da60e301645e238786d2885cef96f6bd304ba75ac810b6e0d6923386b9ba7e3c1df0d5f69426153e3fd07e75d4991b1285e0108c739ad3eba73615eefaa8077db710216481919d402d0841509b5063463352b2d1f13139ea043aebf57d747845ba708147f29f3f30977449bc6a16e1a69d01f2e77d9bb5cbeb7a4dd0e0e53fb74600cac706707688cbeda01227bdf523e0b6f82a53acafee69d0ed319d1b8caeb070f32f75fc8bb28a312fe164d3db529ba3687e183316b71a5a287ad22a226bc4c0bee922ddd44e44f19d61e7b676b2309fcf08f16117de4e1725f7cb1ee6c86970905e56153f4646ae302f3d3a0cb573324f8779ce81e544ff21515374080b00d0c5ebfc41fbeb6886ae697cbc63481ee2dd15718ec8b9fa5739ae35a3695bdf303e713d34a237499b5a359318c2e636739145dbab1150881c142221adf3f445f03a3154d2c1c4dfd4835dde74bf6dfe6cd0cd98778d55ad829abdc89b11c40e2014bc60204e2a48cdfef521f444816ecd026da2028702728ea691e1ef5532d9301a70d40db26fcfcd1a91d632118fce7b2b997be61b1a1d28ab0466b7eaf30f60ea15f8c527876ebe48a26adf390881c42496c8d955e181c9335f1def53f48870c59a26589ffd758a59e6337d6bbb55c14ad4a4b3f4817e33bfc9114c0243af00b217a16a394eca1c0d88d6142077e924f4f61b88ed460544f020b9a2f74fe1a0f31fb15266bc32f0fff802f6fdfd8568550c55af2e6b4fd0b40e8a1f3c3d6bf0adaf4eeaec18d496534b2502379d92117681138d874be826c9fccf9c77deded0fefcf2ce2abd33446a32336081326fa0cb80ab0ea78996e3f1467384a79b50b0bcf6ca3b371ee663b123699d22f454640853b8726fe922b23b89346998a2e1109554afd193913792351b8d55d37ab399008f7d8453f749bed137152e43d66fb0e5d5c0f0366845edb99c8f4494a224e5fb01c896c6cc4cdb82763cba631904a01e13cad259be3af480d0c905f79e2c03bcaeb4e142e7fc631cc27ddceaa8f9f03e5278fa7bf7bf12ca4da0f8291c04f532b44fee200d428a17902a6451d85ea007886a4f37a31458180a45b07c8870f2a437099666975c8279b04efbabccd990b3ee4f34ec0fe84b4842cb561071122147c0c0d53e9948c86da63b44b39c3b909f0e8bbd5684895adb237d31cd405b6c613c1c31f92bc76498019c5acc91ebc0ef54365dc38a4acfe21a932a53c10e754b253dab5a80d10b6b411bc1de9ab4d0436469c105ade1292082e86ce2e807525684ee792f5f3429768ecc101e1f95e0259bd25c470ce1d6f76c401e40a5567abe34da52b150ba3b9127129df0cbd3408ce271e8235131ce07da80b33aaec9f4203f18201b2fa3cd95d8a60286b440302aedef5e7ca8af6eee8a3f1d62f06a4ea34fc251aaca3a37a202e9f24dd46c606ba7337837f234e22d1ca0cb2fb9623a4c60e573f6ca9bf00de0e4f7032589e0046c7d22e0c8aafa73a76165aa3d65f66939ed957c0bdc41ddc96963a8cd6be06c17568a2e068494638bdee02b7e70cf8e859c34829cbb4b89477a286d41f140276f5f7d261dffb7679f6965dcd3281ac6b60cbf5418c71a42d43ea674260f403c5f50849d245e34f363af0f2893ae92e08c31e664aa4ea45fec53c2fef2b1d014ab3e291404d1713fd813dbbc964d234d37512d6845dc63476b095a95b52120a0df384e9b8226ac400cf075fb5535e418771a14b9c0ef60b5ca3504dcb2fef066377cd51973aa814c51692a96a0e42ea8b7b40626e27e6c8132b5e9070765208a639f54f0c8feebe544944a1d7c896f4b85ea62d3cccd333d31407704c68f50c3634ad6a2dece210008e8b7285973b9035a5856059fbc01e9e43c4d39a2eb00ae3d846ed6ced000606df358230ef101522cafe33a7799efdecd0f04713a7d30aa7216052a7c11077c94a4735d667873d58a8d0d9aeb2783521e1ac396f1ffa0ccccd9fa2211e348341b422125e07ea581faa7c8b0bc902c77287339956cf2f0c5c35bc17a3a7ef46df1a6ba920a762a638196d7429c90d1c6c20c0843ec8380bc10dec115d6d41645841cc9384fad07e7bde4bdf1ea5fa5be13c5f72ce3ae003da98676622c697722fbc33d4fc148998b1eabab882ad83831c17d51dd3635f0fe3a279ba287d4dbfcd216dceef7cb04a7c9af16ca60de353cc0f78425559dd0a41aab5bc92126a38ebe42e5744c1250c416997acfa3a6429f82ad12d0004f90ed6ed0b8162b9dea972ad3fc6c2ca253dd2eee6c7571fefa52ad1f4a62b082909dde6de61256824a56f1919488cf8a21eae5d207ad19cb046ccdb8339ebb70104e394dbb5b49cf41f01c7f145b0d7a3e40420ccd78b11b43385d3a9525c48d103a391020034d60f1479e6b04ecfff58163a9703e6d430db2a5e05c2e8cc776803313e097215bb574e8d387f0e6e958dc1bf32c4d1527341b20a5406b34586b10f7a502b44309aec3aa8b4658384a2c29e6b1cf040744f40484006ba5517e0af08e6de13956c35012a2212d0abd5c38e6702b796ab77a586e30d5fa79b8701100a33c3778c77e90ed4d68152d328f1e007d70b656521ce77f50f9f8fa0fb79ad0ba3d80c1182a9f27ccec964123070b5c10ed1c4e844278887f5bcb1229d5a34087b815451eaa9afcd821fa59f3965a9e2d8ac2a91dc95691f94d8807f65f3c40aeae8f55ff61316b5ab3977d81f72fa162b4f1eb60d2bef4a115f282a61467c0d376b771a12b794b350ad727a78b86b4048e75a85fcc01e2cccd9ee05deb78f0fd707a4737c037eb8902318bc91e2df361f3be5c9f09f5844107b14a3f20afa094308313da5696203ef0b9883b0fe2fea23bd0b6d7a035ea4e7c6da8a96d0900c2211ebc2301cd886e0e8a98319eedf153b61ba0962814878314267c41c83a00f3bf85aa4d105e1e1bfc7a4f4b09a8cef0e17ad332bbeacb29a6af5c5d90e6c0cc41c87151774c5af0f1a0c55a81411ab5dad5310d3ae3a0797c705fed75eaa0741138e57ee09161cc1287f0eb79cafa65cef01777105cb73292b27bafa5e53012e6982fadb8d7345d6efcf4f95a4459397ae222ec8d5a8b36503105ea7248008ab8d2a156ed644a589300212b00a3ec09e97a420c149f059b44db932e62ea0043315c39c9f2768aa0ef4decc479fe1d18e1fcedc3e5279229fc4cafc8d0e31095d4544b4bce3a990be0e1ec74ca1a3c269a0328b766efb3e1f353d92acb27b0d9765a4ef3b2b5045454eaa0005b4f19b2de4ece01f84b59f373ae58789ac520b79926f3abc7a002ba534ea493fd10867c0b834d3f9f45f2d83d1e931f331bd0641aaf483e0de39c9ba8186fb1f07da63cc37e7f1d3af39626dd6424cbcff680c77a5ddaa4ccb6a6d00fd41d13a9e47276982dea3d1164fd721961bb928cb570c955b4cd05de3a4194bb531e6bde1172207625a6fca15829370d9f45929c15c072d3bf21fa99ef40e49a19e3ff692cf4afb38aff3d3abe1a66c84ed7d27973504b44aba87a4182ddce8f0dfaf349e3ccf5cfce254eeb27351fd215fe423825d02ce7cba29ad86cf2c400e1d1ebafce24f70aade8d7a98839bb111963b43947d0793281b2d4fa5f54be75f793a195d76fc9b0c7beb78bbabc3b89c84ff66c4cb0615f871535e6a7617dc389bfcbda1e7b9db77ea87d0c6233840f4010e2070f0042df8ba862c50a204e1eb1caecf86688cc66e5ba71e8f4f4b77312f55382b8c01c4854815af233dfeb17b27a2a081c1f170e0f95e7cef001f6f65a9b83b2a7e0a018f2a048c6d8bcb57a7c532ff58cfb6ce935b32db7e3e10a4acc20f971df90b323bf868d320bb75fdea0a8f970adf9b66ad842e714a3d6f5b90d27d449e710179fbcbc65592648d3ba8b67f10e3d6a9e71b0dd15d9a73ce119d4093468c900727596287bcc669ebf12d8c6d8fd0e8cd6d3570a68783adfa4abbfa859b777903bb283dd299e41d0ef3e5ff015a9699d2564d3e8843bf1de7d2c8c0253f15f206ca25fb2ec9743a029da1eb33154ade747b063ea81b9e53fcc619ab3f15e6e60127db839bf7c0f1d42f29caa3e8642996b6205ea68ca2fb30da2f40a4b46325055a32e041fdfc039e5b29b2644a3c7626d9cb6e6095da01db8d6e44d86b332d0135ab0771b69f404b834c2b723a76f510707fbf1221a3e18f16c5935234995f0fd2f3b62ef930b96acb8335a3aa40f14c8bdf20b8a8d1f234e2a032f285aeb0044f4891b806f34b1f02443aabc60dd05f1192602ac547b0155d12cf8fa905920018b26ff27253b45390775eddade641ef7c66a2756fa6e7c160306ecc4bba8505753f3f48c304202abcf70925fe9af9397007afc075123c4f4d42c369b1180f0ace7cbf781cc0f0372d23042262378fff0b1058ab98daba8b17e7cb5630ea2506f430b613cea48dfca2a9671a708b6e5214281fe5ac26965b3aeb0f461e46690e6ccd21782369d1cccd4834e8a184fc37b45b09884a0ccb1fcff9383bdaa5890dc5f2fffd8bd6f3aeefc07bbac6ab0d20f65b291653d17db1a1d779d09db52c0def5de303ae0d97b5090885f99546d31ae69bd47ee409f26097c8f4a5969bf009cfc94a95d18ddb01e39beaab0c94c7e995506818abebb6b1e091400a0ca34200c5c350e7550b9f71e38270b248a45cb84fefbc51de9de37404771ac40d1ec20967a662845594dd9e003d5dad9b701920a621abfe6b4b4fccb7707c54c691780b4cf51e7f5c9cd298ac981e6de09810f410901efe04c55a90b94c944cae18cd0bf8ce9f7c9d277c6c575e13d118a47b54c3b5b2b52152a2d3ab80c149f5aa6d06972525680731c577bb1549103ba6c0672b049e6e1b5d486edcbb6fe934ec260cfea700f7a61bab79d36dcabd18e2ffb5b8829637314dd05ff101542982e047080d822b6eadef2ee0bf4760d3e93b72a06dc186321db63223bbba93077828990f1facd2c3065c18db8d5469298a6646a71553065cce7912d06e2ec8af972fd504f7a0d74a3b7899554c602e22a052c96baaf09bcb6297687411f43276a6688d0c93d355256715e444f6df4100325d7969ceb714c30aa8a7a5246d56177552cc0e1112afb1f63b4301145372e15b2d9d027cb34c6c8b56333cb26bfcb12d70720c41dbe6e2fb0bd45d75edc16e1228d9636b7289fb66ae78a7fbdc27c9a4e89d0231a97afbfded3dc16be2d8970ba75d49630263c32d1eb890673fa507d534520dd88d53dbee52cad3e9ec03eb64bc07a59325fecb5b26d1b8614bd9aba638aa03ed6a26404afce8185e09c9bf8da55df9447c040e5260b1e2d07af339372c820a91f87aff238eb5e05ae3562d9b61c9758a03ed514f9d78c4ddba946e664f73087e65b73514237e934d639a3ec96fda115c2025837624c9f481e4303f56984d0b3cc918c7115872060c0a822046aa16e91e371fc049f20cbd58833d02e16df3069795c314a4086a1318a454f803f946758f17b3459e93d95950f208757c57bb0e560460344619f9dc7fc65a61b2c76b8cd3acb63514e2c27e52f89df48933fb010777557525ca36c6e09d5cff3a20d48a4f06312fe7428df3f65e1fed2f7aa40c0bca7fb56bd1592d5fc61130cd585a450188ccad780a1c4278234d1cff7b860c383203bd61c4975b143bc9491ed6f8cb9ac60b50559ae994127f77222c66db016f5a8f175bf183e064ba6d3d1e67786b80209cd13425923fdaabab418291ce004f12cb0392c804843784a9d7ce2ecc6dd98fbf8ca283c8a1dc6c68877d5d3b077ed2e764ba1065ea606d005d3f45f3f479b239044b4aaeb820dff7786b11a801615d25a8d4c3f8ed2d5b0beeb8e96a1afa8bb08ec76a323034e2f1eac54c170e652af71529a757c174fb8d908176478e26379048b366eab7d90369eaa0a4f7021784b43c708d2be8090d9891c465f4f14929c4e112bf1ea3ea498bc3ef8b060b837c14b2849e513363e10e3456b0bdf7c7d1207910583d5a46cabffc7796a20431a1348b8110982911ed3d76141d6393b38196547e292c3e1173f31e9053880f588f6c406392301f0559531e6cb3e2be8d612c9a7d85eadc117e84c9614a81098fd8c15d57171df31055927bc0678b8ebb963efe55a84dd6daae0208caa6780f5dbddd4cc1d8c53fa7d80185659e9285759aad1477cb6b31d4134368244ea301 -generate_ring_signature 15e1e8785c6672c8c9bd42e8b0b9dfa73ed16b7312e067bb23380a5662884e13 df79782b5900dfe6d529fffee15fbbc1768ed0a58b535232124ffcc89073fb56 1 5f5eedb18339d1ea19675e00cf95acdcfbfb71c56a86c51c473752b1a6eddf22 d2494b222ddf6a368339062b29abe06a0fffd651ca408844a13e8e9c1483c501 0 b79ffacdbd5cd1a2f0a6c604ca3fbd8843b4923813e0736dbecc35568cdfdc0e1fe24ac519c32b3b72a7b5e4a2fbe2255b06494d868c2db57b6f1cdf4d01660c -generate_ring_signature ca505427f45e3f3327ca3206a0b5da1292e0cd7361f6a625dd9345ea0d03da47 9fd40e7e4bc7f5c892c8000666b514a73ee6603def5babca6b51285c40f27309 2 839e8769b9dd788743f455e74f7253e687cca012d0ddad7cca0e4767e4bc79a5 72e824806950403597e8dd402e2913d458aa49d39982318165e32e5de46044ae 76e8413823935c97bcb8c388d61482247026874a53b8d31daa616b6f244d3c0b 1 d34645ee57a5637c4f9d7bb4bff466e37941b61a29908f59ba8f933215cf5509a8abbb61dbd6a2059e0ae785ecab78e22be66f54503e7f92cb835cd49a24e70a7232f6008cedb6583fd61ee85eb299642d5a7915991d339775a979cbe576aa0942987b0120950b3424981c6db0807881a397b97763f7391629766bcd6204e00a -generate_ring_signature 640933f68d149a459fd5d65fa800ed1bc733ccbecf94b17a8a6678dc6e90e3d0 7b3e6f1907ad536d7bb54b41827c3d9483da8e87b5041faf6cc7fd235237f3a7 199 e1fd6f2f9381f01f62f9027760c178dd9f5716898395fa150bd04f9c1bcd7257 0c32219292a9a10f7902af43cb0b0ac820d2618dd2efd266e36d6f7bf375b546 44bb2452da68ccbad071ceddc6bfaa5a7a0471e67235b5db25ae0c401c848ba7 ef310ce28bfbb3868d079d856042448a1890589fd919796efcf0958a6fcdd49a 6138d4ccf0a22b094bf5f6f4bb25431c73194d4cd455225e9f90e890ecc258f8 6cd95891a2d29e19eb1f2368ca8c5bb0e17765269ebe249c2c9d6e4cfec2e701 518bad14481f59ca818908ca5290b892474cb9da9ae78dcb4684df6758a12fe6 486ef59a6090636d737f22e6f3c6786d600d7dd50084fd5a6802f4d1c14aa8fd 7b7ba8bfca6105c550f82244e8dc88a3d14f2573721cbca36bd2abeb5dda3361 6a8ab23a0f9a41e4f83b815e83295d23f704b0a46bd7787e017fb003111952c6 6e629c6afebae95df966e02ff8ca6589152dc2f1a8d79ea4ec806578a861bfe7 bc0df98eab5b4834f520506a48092bccb07099a8a57ab0d05d73a1b644ce5a9c 1bf49181176e9be00603b8697bd817908ffcaa7f5b6d3483e4a20f86a14ab144 2ee400bd275960a00316ecc4f4d62b0c07c77b5b39c9ba35aa21183e01ce7576 643c7057cf4f13239a05fb4daa429764b6c4812e6b31ff31990182c486537fa8 72c592d097ea1d533270ab38045068d9f8d3de716633a16cedbfa5de6ef1d94e 397c51ac125fa63b5872e6ab21da9a04edb4769975f0eb50fec6022b4163ad79 0ef1c332ec45f12caf7434918b5df896d7ff57dd0c5bfd517a99d1e45279dbd7 b85a252d88a0f3c910836ebbc1917a5fff542a8b6e193fb17de53aa7806a9df2 faa62a1b7452db23c98f0e36f6a9978640636d2a9a0f16713e5f10c09fe3e508 328f24953c88403511a3544cda71c6011fe7dfb9d56d3f24f0ff21368eac61b6 df1d055e26e6a5ef462e906fc0deb64d8b29598bc9bd85b5458082089bc96ddd 6c212c22396b7d5581bd5fb81bbb666ce3a184aec90d58d5e44ba1de5cde5bdb 4ce80f8e5c3097fd18e3bdaf8ba2c9319d2c038fe2133cb567699bf8ba797e32 da98eb0d9f2c7b7f966f5160a318169c9ac34176486af194cfbcb3d72860fde7 4660b2e8c795df61adb43ae7c80fd13d4f3291a55af278dbd315450a0c729656 b2847cd4e5196c29d705c1117de6dd6e517140346ec2331f88fff66555b453f8 74ae53ec473006db85d690d8241a46b390c62b66038ab2cc19fbb9557ac410f7 60b875aa3122e4bf0b8404ea36c488f0dda20ff1b4a4f3a23d13b03e1224456c 781f2e0c5693d7ffe37ea904f72274a969f3ca5362dff6587bd7731e3c8af2a1 2eedc4786cced370cc96010b5be504e587357a9fec120022c6f417874e885aab 57f3046490762d405a2cf33b0e42cc077f962b4762cad12d73acfc7812e53752 95d804b52fdee15815250d2de834ef304b6097fc5492370c2e2377290094ec84 91e241d7a90145bf0320818a46cee8638b8000afcf8a79c4573b9d63f1aa75be e5c1a8968de7e4e022a29419db34f969f0a32cad3262f609633f6fb7078f3836 da909ec5404bc643cdf16d774275fecf796a3f114c333dfe55b5b5f3cb605571 3977d5e358dffea1fb052a1cc7aea970ca6f1d5caba77633c8a019c099fd5c5a f4fc33716b9d5a9aaec8707fdc0ce658cd2f45b389c8d0e5cfe68bd986999389 816a38588fb04069185b53c8e7a152992387822555e6a42678a3aa09ed256b2f 73c235791b37af8bc5d20251695df714e2001301c2c42e6f4f0f6933b68b0b3c 37a1f5ffb8877b39e7f07c497b079918e528943284a86ce6ae4bbf92111c63ab 81127e3f9e51799349ca316fbbfc66b9615b81e34c1da957a2523cbede998e5f d022b7db7aab2fa830ca163e9db126143615a14d00784d029c1ee583b95d4ffa 518e4d4eb6c823e6ab52675ec57090510eb1cc5171e21ad531e97c1ebd9c84f4 bf18ff3b5f363af34722d8703b4ab8f6c20a5a5c00e70b29b4a0e2011248d830 09dd17995fba7f2bfcaa29dfefc5670acb38139f95c015604150699ede968e11 a10518e114803f176d8cac931c689a5b9aed0719fed4f3cf724121f2396e2bf2 840cb0975d20cf39161e599dffafd50389930787e3c9f9d5422818074490d30c e079333d9d7a6e99f86881d2383aa810fd16b12bea9762f98a3420f14fdddcc2 2d966bd97f8f3942e1943ece7b23d8125c4ba8899ce0f9f46784a2d1b38e4d7c d65bafd2b7fa8030414db8d167121da88d4e623e43aaf7aafed24ee837069785 672b6a10e067b982811c24a5e83cc2d70e92d45c2de5b499f906a8a9d695bb6d 508d519efbc059adc7eefe047c8eda930330c27d83fad61c2a41b502f07d4e64 239b57469cf421c31e765b9891a5d1a42251bc9e62c5dd55be426c500f5d7e41 bdf54bdc32858ebd44760b9afb27ec803fc6ccce43f5cb72290456c31a120312 4b42e3bf898e45da9dc4c59408e1f5aaf40bf11b8a4d09ccc66b4d626784fa04 0ac47f6b42bda3688fb69a48aa126d607e26ac91a443b2d6fc7669bc52a8dc2f 940f30149014968e9cc01fec132785bb6990f01ac0b7bc1ac97b3b5eb6256183 f81895b0ba67d9850828ccba479bec869c188f2c2d254b5612d627ef719a88a7 854708c1da30f9f98cb05ca5b8b8573321d4f729a7344921a9f376bef75c9e51 8e34b500038eae8d855ed7b80b3a81c05d6664958cda88e2518bc9e7eddce863 48fb26a2d6987b3b5dab2991b90e8a149dac0dc5021b198d2dac0a5900adef51 ff8ffee9e4ce4056d5bcefd56d6b06b481b72f96430ae41fe3320e165d122d7a 0f35e5b2d3736b6a9be73a6403c6ba0f98a938cb6e06be3f700bacf83e2540b3 07334dca089b23c1d1cd601e69b5d64c1af0347861f3754274d9b556ac9aacd0 e02f32c3a031698d5b6eb871798306b9f1f083947ef081e37e7f58226b6d09b8 6a0ce596efab1e09e24f83c816a2910c0f18d054a914fa059c41b1ea4c78f6b2 57f619f188c0a869d043f4c41cf43236c8e1ce80f12b6168233a6f452b667392 417484f00663dcaffd323823225e1cd0cf76ad7df724db16cce584492173e15e ea96e9c65d7759c4a0a9be30e0ad102f4dd0f47ab4dc75af9a8fcac816528d5c c01db326db562a48369f862a6057bf152e12154b9b71233f0b21b80462f56ef1 32050cf1fe635c0df1b584a947d370f1274e290fffc192dcd6126203518e47ce e6f2f9c2565ed7a334c0ca91efe58820010aa1ec1241e1b8827b23935f6895cb bc851af7e8ee6501beb8b4d03a7f0062442d6033ba37d5ac2f25144f7d01aa45 59cf88f92e93c57c35d98a382be18f17c24df3d41d8c8db2a91e6d5ebbb42e12 31f592ae1cae0de2e4f399339798f72f287afe968e9bc56aabc323a9ca0dd278 8ee9c8c5b1d3f378199fc47099e415fcd802651e208afece1284d94b5f45ce3d 508b5b43b6344dcd99d4c13df8d80f02055f094ff3e9d7f6db3447e01bc441a4 74872c9591414526444e50e95e0006a77b98c41b70c56a647772160c77603d7e 21302e5c387885ce1359e32b0988d0c3c0b645002d361994ee2031a1c9c06834 9bdf64b862161a2556140305b68339c517f6f54a247f96f238d45e1834596414 1693c3cd7e03df5737e6f206435972fa8c100d68e5d55b1efc6f26ae85e16942 1aa6e0e8cc7840effa32f20a21c92b48cf8a676f69e3465cd1594dbe800e798d 97598a6b0042768ffcd605725132c323250013d9ffdd4cbc46c71995803fd6a9 be9c2ff2e3b3e788f89547a687a2d8990d51c1cc70be9715840030715f14bb6c ad23094b33c609916ca52ccb48bfc68cfceb423ed2c230b4230d34409b1701ee 98a4bed71cc89baf62675cf7a8617177d6ce4f7c62014274ca9a574e974e7a26 c71c798ea3ec9ebf1f59b949e539a67aee11b8def522f6ed695f433b71b159f1 b298b7f06fe863b3ae360a12e6cca2d5ad1d105ae139fc01325be21f78d1aa70 57932261b773012ce60c5e26bd7a2a87fd66d47f0cc3b94437fea1cbe0b4145f ff35cb6db2c5ebdd155010503ff566ed6d1f5b1c9a36626ade48d76254250d3e dce0046a37851c5026c7a4ed303d870e9766d84a9e6d4e5976dcdcd60f34d4bc 6d37a87c1f685b27af20795e89487f790697b5c99b97dd0a19ef0711a1ac3733 f7266a5cf5b3f7ef28fb4e2428c5547c670a056b110bcb9188ac0a4452e39c25 a2b19116391e6ec0526b4af1f0fb04508fcd4306c91c7124196162424f6191cf cb1cb1c1dca53fa0e7b104127ab69699cc55aa750a9474b9517fb375faee3862 797b0ca02fed4c418405d88223eb001882e2665c0e631dea72205db3249a9903 104956bad09c091477f9906ed41f63490a62fe6e81898b8bc7d2044c02e7a620 74773d104bc903adb07c4e8bad78bbcdb44b5eea9bb052aff7ff96daa8f97365 8787c10d273fc7c43d5b055cbb631cd6fa24068ae2ba576049e029a174527222 6b13581a144e59149e402649568811cfe67d363c2466a272c2503861219156d2 61eb9b1780fc73fab757095aa7731b0a5da2c813cc36d1e607363a37fef27d0d 7672f3451e80b4fdbb01dc2efc655ac562defd71fe7e9c785f8e2d43b7ca09a8 2402e629ec86218046a59177c682f07d298a2327e203b9ba29aa3a7cda2a905f 2862572827a76baea9b113cd3057dae5021169ffaed7353a8e8a150d99914941 95eeb5f30313df74b524624cb8ca9397c97814698fd503eb80a915325d4bfd65 02d6feb3e2e024540b00f37b5b2198d2ff05d1398e1d1c1f3b73399befa20a15 5b52a566cbf14cd15d44df91d26109ffd242010a5bba7692d8b4e180e79aacf3 a713f0c2e91fb5e99e2974efa91de6fc0609c64a0e0cd2bf849cc0ee7fa3fd36 73ba4abac7c1325dd63f832e34dee41d4189d90e64f775f904bcce4bb3db793c 0a7857cd3c77a4c8449abea91b3d424fc8f7b1a39417efdf5f1b85827e65621c 0bf93c473af049f8496ccef2672503d3c75092f1dafcb7da8a60b7c6bbb1386a 5487792ffa3566c4c3c551abda71b6d5b461add6fd81c2db459017af4d3686c1 38bda5d44463d0841416199cd4c7b5a924b1965a77ece07a069763893a9cb75d cdd3fdaa97260218c247273eb293d4e90e4b0a0a2bfa1f72302fb98e1249fbf4 cddd8cb104fed1e429b08285ae0b471d5d0bf74fd1606887171f7176bc9e1253 eb84cafe4857855499e373ef983a2143e1b1d6155e94a3407e2e85f164ba421c 46f8579c8618a783fa34d0d6768d7dce4c4460b1ed21a24d4b48117ab148ec3c f8443fb9597919df3d9caba2a9836b1a3076b78675facdb890a574090c94cced d297d85af7014563f80c4f1a0d1886f17af1a43de53b34c449d660e35d08eaf6 1855741e4cb18127641e4d80db0e585c6f3d357eaa5ab9cf134e52f3023bc2cd b3eb8bb92cbbb2248a347b7d439d6e7deba3e11c0f491dc8dda1234d33ff7e59 48eac61ceea14de3a054bd98e687cdb860ac9dad6f3aa5732c1e2b8c2deb8c43 05a7638c5fe710f66a02e55b4cebb837de550a5ce2ebaf2f036b371f366baf47 6781eff113890f0dbe3421870aaf39fff6f64a9773208450e3c6cc5180fc0446 c52ef31914ef680e9940a5d04d6faa9d5a3bde6f30e4392a9614b4586ea11fca de390cf19cc66beb0772ceb5713fc9d06233071045499e7baa08f0aa398ce9ca 50a4b637e93b15704cb72105fc9e0a8c08e02d4bd386421aa42df597cf32cbb0 5e5cf1bede2ca83f83b04c7a3ceb54283e769734431060946b4092a6e3f1043a 90107a3e5bd79cf0c9d14f7c41afad86e1564a2bc87b96b78f64448278dc83d9 5c12fd730cfd26bc35831b953ef6c88ba99c02f2ed682fcdc5a7b45eb2995e4b 6a5534816c314480e918bdc6ace3924ec8b3ab9f7aabac1e46931df705d0d6b3 d33821f1f4480e39146c5d22c5f514d12de2acac4ffa1add24e94d0aef01e3b5 0ccfdf97d8f4d1c3bbc405a6c0a05cb10c84e68a9e828e91bc95613184285887 d8a28eda02b6054603560444fb09c586c851f6d7dc9cdbd62b627f04ffb1d125 91d7a87a8f597ccdb99c8a1fe05cc9c025aa267c63a8512f4ad9ede0f598817f 095efc60133065f2367438149bb8fe64be7a8e2ee6f0b6d66e7c1d44546863d3 096b3016663a9ba25eae4b5420d5dfd2752753526b5e300e6632a52f581246ff ce0f9ef751a001b0ea6c95c8f93f6065bf242b4ae69d8f0300243e223023235d ce21a3b1d7cc54cbee8c3b2042e2251d2476cd0e2f73119ecbee806492eab3dd 93976f4fe773f35761b0a3d56b5f260358c7b04960d6b01342c13319e11c1ffa 09c8b32547300288d6e9c3ecaeaba6dd15c7fa037a565b201c961f7aee9ef1c1 decb1e86aa6a956e687aeccc633c688d76fa9d76ca8d7b99961b1591b4c3acec 530080918050c0f671d21b63e4cce47b639790caf570a32c88700998b61f77d0 1cfe24c75253c93e025d78117ce0c2a1571e2e89e1687a28220c222b4582718c c4ff26336a238471bef959ffa39222ca67bb4eceaff89a104dbf99671b92d7d2 5a306e5353c2f33d596fe151c616ed716d9bfd68237038714b53dbeee2185f0d 7c0699c7e93eef0f2cbdec06332b23fd9804512d7b9a709790cad1291ee62329 07b843fbe8bb4882fd4bab8c042dc86a597985fe6c031bbaaf14a1ea7b1d2a8f ddcf47a01055394119fbfc49ba1dbe50eaa73f3cf19cf8b60a96326333bd13b9 6884f9ea3a3c4684547f6f8613d1087ce35d5b14dac078dcb27d63a5c3a7c626 c965f6b81f78472b1730b1444d9ec15c9fb6b54918b7b3f4993ea21c9610bd34 bc587636da4ec349e715ef7cedf3ede92d7a3cb0905b2dd35046f01dd12ffa6e 901f65b107e0217ce7569b32cf50ad1cfb72834ab06803f5b53db3464c6f05dc 51d7b3ee2af84871f009a8caddb325823200ae4462e88f3bc9c94b9bff00459d d85bf76e475ef81747d3eda905cce4fa6e876605e102b9b2e8f72cda9153c8b1 9d9a63453c0525302a0d2e7737449abd52163c08f24c1e37f15a527d6be68c95 b6e86edd5fb169911f22137f1111a253fe2bf493cfc989dda1f9a880f3d5ef9f 7d072839af8950bbaf6313a4cadc4a04f152f16ba3e84ca932d1445f9a34afc2 177f7508e693dd881969727b67446fa7eb9a47c26e826d43bd3a6e0ab5834bf2 901823191b933476f034f77d166cc5bda91b0c41daa76c781bf098e13983885b 922f40a9eab5b333199265429c4fcf25f459e09ebdc63d5e077bf733dc7412f2 319fa49be35d561499b802e63d8e2c61849b7639c623785266bf3d6dad7e3b18 07cc2c6e39549e7f76fdd89aa4a9becd09d31822781a94839007720107462159 dee6471f5a489e8ebe6ccc9b1e2b16a2f6d7df7919372b2115498781ff90efb6 29ec1cedd8d6ef03d011b35c17debfb9d4b202d964164928daafc80c66c0de08 9fa044d016d3ae76f4479d8831f4005efcc6daafcc78b0bb8cc8b65f63c8e9a5 768c5cc4a18fea67f416195fe7e1af269ad1829cb8861e19afa6384239e6894e 914752c736372d835b803db11dcd48156a4123895095f124b39279da0dd5236f cbeee34f7cef744beee1fda22bff75f4423bcf170e5abb20fc7bea38cea001e2 9023bf6f93a11c44043ce93f694351453c492f86feb6b44928d253473652f5a4 124a47bc0b26b26c43f6f23a65fc74d995bbd282e1044f56298b54b599c2cbb5 5c2774eaadbdb04a732dbcf9c23c77c335bdbcf686369e6e8223cb0274253508 f46bdd8fb828d1177c62dba969af55e292901774759d2c79cf8a5f1a57acbb9d f2af226a6a889ddc46eace4b559a7ce081395056ea030e1c0f4547b539c4b0a3 faf7b038e862ac015e8190049ff5cbde4f6b437adff070df511866e804ba4959 827f334cb6e5fbb142890c745e63905e0f0778d2a170369adb64658014ecb0ed 7b43a29455e7bf9bb18c406d8220c9dd42e737967e159697f38fd77174949c27 cb633ae5fd91d1769d88557b5a428183ef703293f5065b578e9fe3ff3b1bdf15 fc811147b7c40d424c287edbebaf6d8e7242d285103d1b6f60487287afb4d848 32ef999686ffbb7655c4aed56d167d2d7292f711448e8847a1a05e55e6424735 86389e37f7bc902081321af80c748af26665076e553bad8bdffae84b5cac4466 39033a1d13f762335650c32db88aa2c19a128014946330e09a0a03ebfb06f604 f47a687b06ac083c8511527bb3dcc57b8483e295ef2f24b3e84280e739f7baac f8451f5f9935a8713f1eccb30d1800915f83e0736b8c71c62cd34720c75dc7b5 227ac01feefc3da4c2e7b452e1cc37b20e7b5f4effe6061ecde47332385355aa ced1f5d61e7b24c1f65297ed6a611d92f233473862ad54d1a7134007ffc35e03 cda1925c58a914d8b11ebc4d2668340bd55565aff5c118f8a707141a4313af09 29932f04e23bf8c4e235571f2ec07cb384da86c855d2e3b45fec4051a6bda5b0 3fe044639d70fc0e5e6757db4e45df0af07385c3a25aa4dc8294a477bf39be08 bd398f48990e8183d93117a6162841d3df3eb78811f8a84e101e5931cbca754a 52b8ec2637c8327591ce0f41a81f77603ca5d508353486a5b380f3c495ac2b2b 2b6fac9a5788ac550314d5ef4a85d855ca12f866dcc8e55f63b7898ee77c1c17 880a74bd63ec12ffbadc16c57915d3098476cdf5b25c4e8cff1de4b35009b7f8 96bb3115b7d40842c7ea3631bfe35184e2d16af4261bd3fba60c2913e4911c9e b3d442f2d897e782bcab66f0a1de170fa5171f588761a3ed7f80a04f59644abf 4da03bce5db4b41ef54d3a5dfb5140fb1794332c20c2cc80e4184549ec38ff68 cd6abefacad45eaa7c9e295c08857910943d9b0e6945ba77199ad93b5668eda3 6eb4e11f2e62cfa6164dc0ae1ada61ebaa6949a07cd88303c633835fa98c1e53 e4705bda82e65f57781a596d1e54871dc587e1a614b77eab59911c1721d0090d 34 89c0f563f897b51c8dc815ea1e618a548ccdef6ab13fd410a78c97bf19806d09bdde5c1baadb233325479760d2e9a2cc2b41349a1837c88183c698b97ab0e60708f38d5d482e121c674f328dd7bed325631cf182ac9b7daa21b892c605236107fb72ea761e3965616150c53820094ff47c9ce85063541223ccefc8436a7ef002edcd7ff8b9c3b94746b475ad2c9ec6ed06f1ea16a34388a33a1b8752229f6a0cd2200abd6bc0a1e0c66b120044cca723f050bb86a0113b8cb33d545bf2c76c0b49e7e59d7a5d96aca6430a2fc8d6f7313fb0803a8fe275f717d64bf47222e90488e63838f78ccf02e73cdbf6bf93b183e2612e2ef52d33f19901e700a901ae01ed4b6b7beecbb393232086b286a971d4f64141895a687978115557303526b800b0994e024ac2a5ef0a305edf4b8244885b6bf66c99bd5988534fac1a05c2bb03f255fb32636af263a3a950efdee0256d2dc80ab3fa01b7a5efbdac157f17980225776604b093c6a566bc84d6833e6e1c34108993a319f7d9f20654de1bb27f0312c1e10b5788fae5623bcb4a8a1f5f44a6d4bbdec3f89917dc7808286cb13706f1629bc98313d9078b5e7bbd8147cd3be8b25c19f8c881d3680c178cecb7710404e59da61333b4f67ce320d5349a3e34eaf1f76254683d02afc3c39fa8be0b0f9ae372d277529d0e834b226e44b8a73a122ba6780ca5aab1e19f92a07a3ce30e109f0858af0b240a8088fffd8df3e7c897a500479ea4766c892520a703f33d0ab7e489d8be94afc1d41002c06719615b301f337aa91d5bf305bf06d16ff6ec0aa693cf10dad21dc4a0074d35bdb3927acb41091202d912889c40d5e1371f03039a11b6bbf6332a2d25b00c3f9f05e19c576d4634ed637677c10e5c33c82d840ad1bd1c9724d5d7a78ab61ca91f9e9d60847869abd14282c31ebabd92e815100af699242b66c0cfc01e2450265adfcb720eb8c2dc60f3f176b360f2897f895e0d2fb14d4554507f975566507f306f6a7570dd70c8c4bb275aeb3028216a152006f586fd1b5b897c486c979592a8305defe8877f8c5bc2b05c90a31f58792ea8063704b4898b21b8d79f72eb6af274c2affccaa29ac151eb8620ac240b23c3380a46f4a73070fbae23562b5057cc725652ec25e39464b37074b8ad803d523ccd0180c2cca183ab2142dd0cff84a07d12214f0520b3922b21d97ffff6965db01a063b16bfb6ccc16997745a6bc8064db6268b52c7281b866252130e7f4b15ac2b0c8ae7f62dcc97dca951d830f0ef98c2db95673325bdb2b2251dd73e63e6ce8207cbb4b7f2ebf8a21a7b8e7712e35335a29332fcaac8a5bc92fb29fc0eae15690f02593c0e8d7c05a21628956d831a3f080308436fca23387c0577d92d5e1f3e01e9abc7f674c52b38418a36d0cbd2dfa891c7e729aa77a20c3b3532210e7d1204153241075a897d4b4514a5be57be16276f928ea30cac29d874dd4d25919f8f067d91270bf5dbfaa7b8cb623841dd0403ec8c6e15317dd508d017e6197bd96105aaa4035abe226d16ac93e7a69c5750cc0d0b279f514595844ddee04b80089408d22e2efc5097ab41cd8a26e867714eed25276ae358b2cf6e2b52556f6b20e80f245fcd1f04070fac7479b0cd202c12956232a359e9223eaa173abef8922f4e08fa4051b6dfab81ce2fa0a53fedf1e7aaa89a957b0b9d19ed946e2527b54e63090f969bbb87d8adc6d8b3fc6ad60badefd9f494b91a223d43b131a6c5bc98540e75ad7c1809e87859f69cd662a40fce9f01010f530f604a06cda6e613ce2b5e0e4c4063b547686fbf836434a5890a52817f503a3b1b38dc40e657215a98a1e20fe157c282b8876dc8f10fcfdab83368bb6dca7d355b24c9028bc3d0642dd6d50491f24b1ceea7d4626e081166f62a8e2bf309adf32913049b92bee57d8d85270e3d938e9aed155fc14a2756e9cde4a0dace6c58c81b0377bdba88b395eb4f900eea0d982732ebd16b46d8b61f2d0048330458ff239b3b76cfb241c7030c918107fe764b8b68faa39227665999d1809a8ca946cf430332a3a2f052741ad682f00d6c65fd53ade2467d9d52df3cd9a3890c52daad6b00be240bcdfc9842e83e6105c574764b1ac442c2fb02513a708e079d83445709a6256d6168e90e996117d305be654d5f438d6c2e2e8a3ba44d27c5fabb12ea5d381894bb1837ea1830454e07e2320c14d488bdd66545a65f9e49d674b240540ca879e9e0885cd376fed5590e4736b5019667cb70d04c77f8cab2a5b5ad740cfb0fb149a558b57fa3f4232704900fea6fa000d9f1434c82e37048279171a645ef38a1d2602638694c1bd5770773693f0e29c5e9e4ec2b5a093dd81d73c81eee0a49f2cb66132e13fb1f993f0b8b3def3ba00077f32cb191edf55a7c36bf6eeeccfc398a7e7abb6734cf85e20551be594def29a266bd85198c1f488f03767f634625753a66d6c38f06e9fb360c920ad50dc7c3ff306ddf1a22d88a86650c867e6f79be9d95bb9f0356a103870718f5e3b9a2c08b676929eb0192d66eadcc6a615070863f0b78a964bdd1e4e70a70f14929a53325ae7eff7108650c0af753b155f10dc48a6279afba189ee59b0907d980c848fcc103960fc3afa82a6c4091690fd98b1d4eb40282e06f0b18040dad556ddd1d918a529bbb2130060f1d5d864e0f11f716f9f008e11637f0957d08ee37214b6c3776b9dfa1b54f8b8ce80770db176e843cdcdf4ae97a5af517b20f31a6bfd2f7a36702f97db2a0ce279585aacdec463873eb3294f7a81fd0da8204c01a7b01f92694fe1174fd6d79cc5fee99161d4e6ebcfffe58b597693aed6c02d2e30fb07abc58b44ca5093fc3160ef11bdc4684fd16804b63043e0db8421c0c4b03997a735a1301b09a99c6d71b5b98bb5cecbeeae0896b899c38f88867200e502221433e9788f1bdf3f862434372e541118a5801edf86b1f8f065e281d99010b22b0da4af99b8190d366d640690060e18d56b1bd19c7b853cf5fb377aace084aa63d3c577e61ef2f0c5ad326e47c46b28c9582f448664ca9110b8e53d1b80ea9124faa211f90b4c12fe5266d07772430f30d2e95405b69b38fab9c69dafa0a2629d2142a5ce84bdc049ed1860516255a8c953c11980dadecd0452afbbd82050a6adc3eb66ca223b5d5b0fe1e39ea17939c7bd19b903ac1df30547c9977c604c4ef81a8deff5240fb358f49ea093aed9f7cf381547c1cf13cbb96027f9d8e0e4ef286ccc70ae72cf13a86ceaa56437072a1c3ae028a4dc0556633b8d4e21a047fed73118b27514f97f3b1dc869e8a481b79977c5f22506367b9bbb59cc8eb083467c179c7098ef7a6817a7bfb06f43b167283707a2f16a76479327f54cb4c0e170c73684fdad71bc3f95aa1d5543445023db3a29b2cba3736156cea48d09b0f1769e82bf9efa3a8b5cf8ed16aa98b99b4bfd53a32ffa27defdd4039ede9400235c4d44ad9a7a298f3ce0da249d78f6bf773855622c1f33a853565055d8f460b6728329445051cf71626a5f626cad6a9ce23a8ed072f42d778fffbeb99f8fd01e474f99c64667fbd8fc8fcc74920d667a70a45cac3e8550cd6100b90a5a5a40273bd4fb0d07df040cf7bdfd3701328be2e2a2ca77edc15581057b0d9ce322b0d6f642f1eda83843cc16c9cb9fcfbe77e53d0ebaeeb5c77eae6752452ec180c0917cea985254f27fa6893546834c24fb171afc7bbee642c7ff2dcf93eec39cb0b4ed064e969320f9f1b93051d9a503986d3151e3d8c365610008bac67a631760b9c103b2c3fb13d93fec57a3fc7ca46b0e4d11e3efdc58856a51e48d9cade9b0f368cd33c4684b58adf6fe3549b51e749aa0b28c5bfcd57f9bdf835bdf0a7d30a7bc5bdd643c76cb83bd8f48799e460d3e8cc99945ae5809c75003a0befae6702055c45ff5e657254135545e33f5406f27eb7d6f4de525c6da0496f34450d7b019f0dd26eec3f2e25f243623986d1d42685014e1844bfd3ee4a484bfa47887d0d2880c64269ea5443b817fe88db13241eba3b4e141acfc5df1f1c3e389906330cb1afba6763877441bc083667b8eda12faeb149ee6d17bf0dc832b03133b96004797f9d0894cad651c60548056b00a6f052ee00a46509b5698d72382c310a1b0295f0480fc5914c091daaf244397adc1f2153d3d23aa9480d067ea64de0a73a0d0b804c2e9469cbd2bd8d28604b127b5eb0f6997c7b3375616ba71e830ea03008297631a5fe7818bdade45478f139f31ddba0f5574de9f80953219529ffae3806a6ee1c5159a0a450a9c3e432dcb557225db6ca4d70bbc9219e6caf56e42db00cb93fa6d5a4ce94035978d2625cfcd2971643f7ed19a53ba5331bbe72ad150600d01c208ac07bb28da9ba526d482d283fd02622ec5fa62f55ae57f8973b13ab0add5c42c3d34090c2f3520784444fd2fcc3780394ef19caced459600fec88a90672e7396772aaa42df9dfb7f9c7f79fba772fe73a40ea316d03807a5955fabb08f01b6875c0953d49199597bb7fe243feb2fe45c3c8dfda9dea0c2ae09adf36003737a78928b24ab4f3cf348d8a1a964864c414fa0b7e51c274ed7e80b3a31e0b51e7abc6196e1ac9634b625dafcb6764a3c8f8f5342ae8e4efcfcbfadab14d0b585645a2349b2d87cee7136e897540739944011f54a5f45b6d2a65d8e4423c0acba0bb117b4273ffd66dcef50b94ca63cfa9cbd4153d6920b84d32888b3aff0d6de5b9a2cc0e188c3bacabe65095a4a1c20694cc8ca37834e91ed1d1acac090a2fd83e7b659f94bd5611069b3e69fa59eae9d0be700b69f908350d1aab8a2d0cd8821b88f7a28303d8d3003961a55cdbbcb489045186effcad057a35823c600bcc10e3f998ac95132e86fa908d024c0418cd38faccb099aaef5208a510be36026722c7ba1646dac90d406e344560faa6cbc1ebec435de647e26c054b44c438094200f119ce52eb00b08e38c09cf7f7b76a57ef77a0e682a3008b1b54999165065e4fd83abd97cf779d3844ccd32c5e729dd0353ab26425e99f9141d5d103ac0e725cf6f73bd6e70842be46531dfba48653f6b4fec7bf4eaf723ed1f86afb9a075fbaf6f2df4386f69cf3c4078e164e8bf911759bca58824b0cb5738742cf2e0d5403817f45d671cc9b12513f55243a095f28a063ebbc6f110b7cc69ff9f9980dfdf0a7cf1d26ae99d60db3ec4fd3973a2cf7cafe276bb7f86dbd5a0d8c98600724fd1e701bb6c05f53247e22593eec3f57dfd2a85b6afbd70597656e70923e0ebe23a14b05b7582c50177673856ff4e43b647aed4db5f7f9a7c4287ff516b201898ae83a7e9774ee35c30a74cb6d23ec15a01cab2a80af13bcef44def8d1180f061a53e59fe416031ac5456b837e2d8232d9534a592bc9ee26c6005f83c20a0a80447941ee2350dff48fd23a6bd5239c1d54339c3bb34802fe1dfefe5c0abb0f25a38c22c39ceceadae5c883ed8b2d86cf8e34fc7f2de5db2b8f3b87f52c250c21de325c56e0faa60d9af69cbf29433d13b21d0f1e07feee7d9a02a27849cb00fd2cb46f808d3b96f02bfea5b23407f205d3cf18b47d95e50134f4f579940b044900d49e682db258d02195906f4e428baa1f545311a778d4635f9d60936a73010b7b95d5e9335bf32d016eac31790f512b5c152c2715b20eb923e5a04f62f1061880f0901c252c012d7440ea7c4a7394095f69096fdd5a16c37402e1bb187d0ad9b567d4a57f5a2c745033c8a96eb420a53d4214447d50798a846436ad13d00dc3eff33f270d8b350fb9d0f203835b2a5c07e411c1dd5b4251bd2e5b2cee360326251e5c106aaf91591cbfb9c9b0ce5151cc1a631e8497478d25c3afb98f0908b859bda6d23ead47de16b5fe35ba9462469bf819747ec2d71e0f34a62a8b0d0fb1115c6d76cd60ebc91fd44ac50636a12f65e1920449f571265bca0637bbbf00c0a8c4c6a94e1d277cee28d340484a1e73c28613c9f5369faf6bb9b869f4930bfce6152676c18b96f0737e210205c79fe63df78b6297749271e650bd1bb4b5009b2e2b569081626663261c459633df8bac5188e84c2e13383e1bd5b6eb01da00ce3e59c8a8a92891d3c2fd1653369f58be952e41e4965326a921c16ff429ef070d48696ebbc80c6463f334bf5629aa92d802a3d2921a7e0c89281def6be1d90301443c1036755e31b63a433958f24120aa320a861236c0e9889a945bc45593038c4bb1e679c5d09e18c90872d30c1a43195922be9d6a831bec7410cb0117630ec108d95919215ea3fb238b4f33a157d2c20e71b21e00b7dd9ce3ea86a5c50e0e485d557eb140902c40cbe3e3df236ae76aa095b283ddf29eae4e413be68c6306e05f282b36ed0b8c9a424cef88807ab5764be2377f640f25851c03b48ed8c70368ac0e57558a05ba4b6be225685c629d36c26061618c2d54672623e00ca0d600c95a9ffc2b317bc89d5bc61f1d38d74c12107b075a0cfaed1ea8f9c3ca578c05b512772195824f75e003101537b523e2cebff486fa6fa071fe5cafc02abdda03b013148caa42bc5d31a373884e5d278423b9018bea9b2dd9d183a9bb83c7f90f207970bfbb161ac1d5f3849c7a005127d83a067c4d4dc5fb4e838042bcded6023d8f80a555ffb01bf2f10ace24ecc2f08a9e2fc9cc7162f51c5473942cc98d0801c4ee092804804f86ccde546c11e74816e0659d7a4869c8940370f78efb2f0ae9795b69feb0326c67c2e190b7556dccd83c2e740e379693433f7e9d2a77ff0720a2bd5e8be9907f94f51f9ab1fa8ebace45e8c0b667405bc66f25fab4cbb10a4e58612552dff8e6c5aa77a4a0c033de6705e56dace8b4fddcc87e863a2be7089ffcc4e732451a2360feb674b550150d4cca5960d75d4d35faf0665d3ff0380e532798977c1920d9db81eee53e913a7e641adf42b70f7bc4c69f083c6ead55051ddb55e81f5d5ae49c9b143f68bfaf482ad755f9ed63261467ff0549aad0a70416055678ad26d00356e6ab225dffaf46a645e05b18115be9dde8d72a448e2209d6c33e3e0e511c128e66ad4023410c35819b96e8b2148eeb7cbcdae45acfd70d87607c1256aa13de66aa90a83e670928869cc4bd10adc1a3dee4d61dea088904a47e97756d0d3b67b8a0f45cf4b8bb869828b3c118692fe94febfc164a218e04972ad5fe433a89071d89707ec3b05c2199c7b73d71dc2b9394e8d8c41878ef03c5872d617332d3b46af0a2b858b1cd0772af9b37e9224838248798c7e11f680a1cc18367202d85a51bc3c0efba359bcdfc4fd011626bbe7ad32e501d07f7c200e6e5f4d92dc9be4f51dc6d29fddd84e47cf26fe27802fa5235b3a6e1e84b64065002bbf81562b999e34f1626b602cd33add58c51a52501cbb81dd49fc91989089471c3aff461e54b7e4485acc221a957a486ef60ddd35501659b67cca135c500d36ac2e4f3fbed84267822884515748e73bdb0f7c7c955381485b291a6b51506bd899c75c37237e24b5e4a2953bbb0e0525f2f1e6fc192596267e58a5a8e910555d29eaacbb107cd853379f475f5509f6682c383c1cd1777011f38517e89d304ec0d17d55f7ab98bbc92e302e5fa261b81903c50419239e4e97a59c5bb40db0c187a7aa04edc59edfcb87a383ba40b4481dc84701acc2a9e2805af25d24e120b3d2f97f93cd8834993088588dfcc116c113da5fb4d2962f7a0d0c2ad2d27af0fb14148b5fa5e102c562df2f607aaf96e6ed970b7e01b5143ecb7db5ff41fd30e0ca400665352606884732dacf6d91c9f803a9ccd537de804ee0d61d9a17fab023648aad39e0185a5b06373e9b69b98297d9f5fa9c5763d420e4398d3dafef903727e3d3d2090da5a91262b2552cf0ec62ae854492bdb2ff6de8dd1d179a2e10c1b0860ded73d78f06a594a756926a5b5305a4708a981f9f90e2372861b36aa0bdb97043f093d33df88ee38047621ae4a0b2f424302a88d718475e69152ab7e02f56bda3b6baba9b986cbe1964c5408c04f6db62c909e01bb33d9ca7f5fa173096f7f8fcce17eecb9bf1f969447e62a054e948d52830ad2ac2f2a0fad156dfe00e136307218f8e5dfdfc29bca922504862dbe567a6ea5b0de98543c81c0bc7f083d4cdbbd0bf41abf051b529b9ddc6c1350c7ca3618559e4370c2d87a2bfe150c83df9661e0c708216516a52fec17fb0134819cd9e3815ec590585b29858dc9070eed0d4f55997870ace39cea6c2350e96f3ff5263de7eca20bb735f3ee9498013305efad1390cc4e2b1d0bca0018c1eeb4c251497902c741fd36e3ae289f4103900b1483f5ddbd1b4aa899f64a90e1b237b4362927c6a7d7baba96e65d65290a80dd5b43dc7e84bb74b9a5683d2344077d96b8a382f4ed414f1a67cb1f087001e11ee74c9e80fd982075f406921e3c1372c2a25a447593d8d08693a2cb7e060f6fde18c09153e2a56a0120ecd160b7f844d51690c8b2d0c222b1fddc577a40003fe42c9aba8dd4cdf4372ca52e8481d8e983bb82b6562ef95aa2e94ec36bc40b608cd2bb05c92877fe8cfe8abe377394c606229333a0d07b7601dca5b44513036312bc1e8ce0891984f2c2bdf1f60dce763ac4d9c9406ef2541bf7244c062b05fc33b3de86eecb2ed14e81701567b539565551de5fec0fa00fbb20aa4b699b0af4db4bbd4859a3126bd6094a46237cbdbd16cfe2279e176fdf06a54dedb61a072d9fcbdcedaeee9d85b75b0d2e358b219b160678be8a8e0168d58e59fd9d2a04dd95adfb9bcdc27b4d629f439a19295cc6bf2601860d75331ca3462f9df6540b23b3904140b752efc44d01b35b967d56c78164489c6543c4e203db0d095181002791338cc722ea50f1263975fe949f0eeb7b6fa2444d19b95559e708d49ce00986d96c4fe7b48b5f26ca990bca2fcb1a77731a06ab8db0c1e9a68bf05cd1d408ce924f42143ce576b74f627be13faf05b8dacb97d49351aea9e194a39b13d50c568c2fe8ecd4656f49454bbc2748754be0fcd2cf01c7829586f87b24780bfe0b82f9dc6047f7899c80c3b2493f8c35f1b11ecb55e90f3aa75f0e767be98be60c1adbb961eb09d32324f6e9e16853b3739b5b39efad609c8c99a98bb78a4ffc03b6e5bfaf6473d1968337d34d7ff2630b73b5520222bec2c9cdd6872a5398360678291b523a983a574e2f1628c287ce7eb7e630e20f4667e844d9ffc6648cb500e8d043e46319c242a63506dc36c5270f5d1489752ed12824f9d1069f88ecf607cc9148e6d69748944803a5cd717626e9d8608bb48ae5240e86cc178b4afcc50e7536cc3fc86d7edb8e0acb3069b0503114f2b8e451bd151451605aa2bc1eb407349a64d89a71bd64c6bb8380b46b00cf926fbce6e4e5913c37907dab9b00210979e1d1f31a6b628c340677b6e0ff0087ca42b294793e1003c44cad5c48c553073913f0cdb63b6f67a323df3e514f747c489051e4cbed27210abc67f13d0aca060ae5253518d3bf481866dfdc6fa470b987580c28ddffa431336160e2a8ec6e03e6f166bcc119279de76a786ae58751cff2bfc5b759108a3797fc6056ae2eb50c72a593c355065c48f37ca4d7e97a9717c78bef8e5ab449f7763e317a1f29fb029c1c7d1d7e137fb8766fd52fc7fdde0eda2dd4cf8db2ca68b13a324bf2a2b1040ebc7e0f690a50bde81fa9c69efe4d9f1d538d23a7ebd512be7b19b858870e006767b28501e24bdf6717d6acfff42ccebc4aa34d1a20270f20775035db17c7090ab0a9f8f9b0d395ad9af51047063d5425a6841c24b5d0a4b3ba58437b8bc304d921ae33cb1bc6573ddc9195e1aab39e1164de51694045944a560109b2978f034160fb55334b0a081c544f2a5249ed53e1407b7f8903ecb9775ac18efb60e4060714a8fe098c6fced73f53dd56caa8dac3d1062f060b15340d75fd1cc981d206f9284520e8e309795c1ecc93772db86ae63ec786bedd591b4ddf43c836b27103c48e0e7e0b5c58ea3b2efb159bf6d9468001fb8a24a8d7058a35dcc68251a005df1eb06efb243c926c520a9d718ed5d2d3b732b1f4cd8c48e22729bc53c15b09f896f9dd918c2ef075e89601d2dd254cc7b8f92c4de4f722811b7af1c482bb05a82c7e979ded65639dbfe1b2546cb3764edfcef181824012bcfdbfc7c66cf00829ae5799ddae6f5c4e8376e71653e9426bad1ad8228957ce22488e15cce64f0c9d71a1c43b57c64f989883dec8f4c70cc2541fda1ea1b5f8497d34a1fe47300c98a6ad36e1e3b9cf2cff466d61211fe49510db74f8dd881341f25252e79be10898a3dc1747ba3c8c136c313af5b9fc49db7f9b49c28a7233626cf418935c0e0d8d2c2b445f68cbc87e8c42b672f997735949e937dc3d4a99bfc6f921f706340b10190ae8bfbe2c45c50aadd6664ed3376c158546cfc1c9b44db3ee14a6de4308bba9f098ab6cc5715848f3d5928a03f507881d737af6e46a3f9974144a3f7d013e6625e77c1d0e0e25d47602ae0740e2844ebb5f09491e32a20068236764f0010443cae26474f4d37c8a1d0b12aff5dfd8df218c5abc9c06f6f97a157737010435af54ed2928d542b402db88cb0889bdc148597fac8846fb88ac2e7e93f991061b357f04adb9ecb19451e663eb80db64f3bf09bfa0ab64639ff13f3ac7a00301ba411acbbfbadf0bf2b89329a5cab42d0d285c33f9060861110e56c76dd2960c701efd08d2e57b2bb9773cb6f830471d1622251bfba63ff9868f7f6e8e9e7c002c7887d63002a47af8f5370fb49da79ab4315741fdc025193152721dd906180c95bf86a900c023db7317becdc880bff892f9e573e996df6db284af629e9b620e9e40c44ec933bdc69a020e8d7dd85b71a5f612b0ad05e52b079c07ce7012fe088bb65ec402602ab2f6b7a2b0186a1dba4ed68f633d2ccce9e63b069d755c5900b38eea414ea71f4d18bdea5646d26a37214775bfd20815235d3ca4f9b451d408de564161effbd9510b641cefb3a8361a07bc7bc7fb084f0d7ebbfe39c6c1d202c9987fa1932de448df85b4b8c20a107f3420752298c43220c6d2b67582049b03658889c9e94de08bd75dd10ac579c999a5a702ffd2572f4931766e4c88132a03309703c19c20b2b5dcc33d106b27bcc60dcd945226a1d974ed7bffb449852900561c30ff4e4aa4d1670b7ec4e8b5a8a3aa81a1fd8a97d85483329189c25cb706799c1a07112dafc55c89ac403bb1c81e4b7d5ccd32828afc85d25cffaee153061a27077d7bda86eb2234b5ca2ad4dd5d0c247f58c4567dd65e042bde6489be06804ed0d64f9fb7e461d32427b7719940244858106b853292a158683d62c43f0bedb8ad87045af72871ff6ba3ea9f6f79e75539e1e815fa20df6089deed0b2804c5f1b5e2e6bf2948e65e7352a484bb27c1ea0c6d4979d2ba3e91ec2cfb63420ab5809e005dc97b709f2a1942b950ee5ba740a5778cc6900c05476e1c21ab8d0dbec2575112afbb29e3316bb92210f626ec6883a1e7d482e7b3a7392656236204ff299b844d9fca5b03da7a09fdaa0c3aca0493edf5c658dc7d5f45e6265db4098de33e08995c3834aefa3087bb4a5961715fa91c16ba61efee73aaedb0d2f70bb576bbafd0853110299be63e6f6136dd3a847e4d0292f994a78ef2faee9be60d50b0fe1803500ac7c0b3408587b67bd79b9381df88699b3318a35b4391b363001ac2aef58f276480ebd3bed1992419541a6d4633687c9efe4fa83fff72b8b1097efd28c03df0be6b5a6ae45109bff15e13b81f2e0eaa86009af17cdfdf90a60551d60d77daed5d131cd4c4dc9aa6d3bb4c7b80c277c8d99241e55b0a82b9890e44b27a9624e1b3a55c07d384920f5ca9ddb1d1733a9b3d97620d6129caa7df0d4a1bd24a8b292f7327d7cf25943168bb4270b99318e7a0798e22df47fdb7ed0c52f2c65d2fb87c0fc7ec0005d3889e5b21b4043c01afe4a8b9d0f2411fc859004b8e7c2037b618b5a7c7290f07bc85d5ca63f9541cc10ead0fbf3e500559230070341d2c3cb5203f600b7aed512d9a6cea639fdbfe4d70426dc7faf971b8940e80b18d009f31bfa34834b5f967b6a88483607d52c9b17c2362957c615c87aa02cfe675033a94b37421c493978bef3f8e20af97a9c8a653f7790a848b75f14507e19ac61b41b26acd6f40b62d2ee1b01bd176be91a6c951cf0b7aa9fca35a5c00dbc45812d49a7c98dc3f55ba86b419a07d2bf71e8abad3ac6d9177d95b44370160b13451c729c71523d5e78ebf140c80a83ef6b2f2dea100f741d6ca90eaba0373cdf1ebbe325ac0628d993ab25cfb17bacee0590577cfd00c807f3d90f5d10d531b87a94ce09b97b8ddfe5968ff5b90fa0321585cc62b6d6e7d915f09a7720e5574c5b197abeb04a687b8a202e89a7b331d78e6d610d9e70a3e83b4fac1de0253332e132c7ab7a71b8488acac51438bb544a62ebd7da69a6452bdc879453d038e85d774763f64c29bb7e7187a54cf06252f7a04ceaf0d1efddd2acd7332f406fd6458ed766d9f056a16e53a2af6b8320b812be9b8ad18dd695d08eae1a9df0206c1117def74372c68d01acec24709adb59f8e0696cf1bc46bd62fc23c4632026e1afd5df47047033eb71ed880c0ff2c894d0dab604fa6431a6098ac82d24c08c50df3920912eb814b91561d1a05671fd4e61458a2d38c0008f2d220b52af00196fbbd0f8e4f8d424ba042bb160f56b1ea670c0c001648ed495370e96b89e307439ca4b8d420855ca34c3a09e596e7d1620f650418f56d9b2fa3b2adc3955c054294f03644841ee31a97e86c67bd9c3406a2cf24789cdec77df50ad81c20b50cf6d117a2f1285d0613abd682a1661d3ede349a11fd994c0971f9f9f019811705df419650aff50b8be9d3d613c256aaa79757e4e9470623d0ff9d62ee19fe0701afe239cf4f18664a54f329383811c2bee93384add78ad8a223f7439b575e660f486c85d33c931a76d0dcf4ff9b83f170d1fbdb21733950191a6edc7a9261260f07e0754c542ec570652be18598c0383fb9aa6b04c0a0d90d280d2da8028a2809a99ac08f3cd71804229ef47fceecc8d189351e7651c106d123c1a6ecd4caa90e02f61afad58c709dfc987436c3212ecdefc368b8bc303d9e8a32efeb81b4920a46fdf1b546a7f7906f64ca2f8aab120861f7e1b1f0333a4470be299b31defe09d50b6546739b47acb9046f06ec64562829d02bcce0df8639cca70e0ee1fa04022b3ad6ae26bab32867036c6fe87c97e5e60db8f6b83e588e38af7b47d413520ba737c8b794fd297a7062e30f3829e2d892739ccd1352837da5afb417892f780b7d96985c6f12ab9b19f773a4dd0b0007bb238d0b95f481a8fddf20a241974e01a78684cf1cb9bf60b6c7cfb717b0b06f21c1ecde9b487ad64d13ae2009ab1b0ddacbecaf5ba187542220c9faa6a9bc4c1cef8e888fe9f7134075c8cceba3ff0e275383fdbc61de7f38bce44d20eeced1d730236ffc08cac7cde99a5f52e6eb00a136a8b40fd5f5e3d9619e97c1ac0c91ff76fea39cfad742c1bd780eb96cff01c4a79475f9515ce0e2aeafa3ae3db279d0cd5160b1c9a4032a447c87bf0ce70f471e5e9f8a2fb5ca895eccf820b9a7b254063b668f1467443670559f3166c60413890b62f6416ccb0ddf70f10a97441f97d554d97a300206135a2b1620b0550872014c7ccffea8b65d4d170799157ef4afeb25a57005507775b9f5a4278a20038ebcfe4666fb94944cd943d4a138ed8915af1b9adc369b0ecc106669c3b4cb01cc5f3c29c812949cea14d92cc6495813ca099c19e22aa92b8286e190cb214a04dd4aa5b1faf08ed842e377fda44fbb8e7533054496ff60f3e70c396084467305e4226e17e6e46c02f4c00568b7a521950b0d81b032a1be359c4e0507b9d64f0a07fab99065425eb552b19f7f873f52c9b6ef831c06d12ea464a70312ba135b0578f2e8fc388666f61b70c85bad867e16b20e297815772dc195153e058eee7906b0dd073775efc20543be33ff0e0d541cfb2c88850e58308f9a62e7af84f58c07ead743f1487f392661e036f89291bfe3a3fd5f08f0b97df4227fb3be491ae508e31cba3d76b6e0ce01c72321c899840260f5e94a2413b1542a8fa279d41be60c9fb020a00acbefe5bcdd757b1f8daad7d178601efea3484cbb18b8bfe65d2503d2a055ba1928da4c52a499cd25468477f4a890c3dc760ceaf0da8aea8c210d0f7b880cd61bee5dd005423a9f5e748b9ce0fac0e4ac5c0a4503339ed2f0a4ad08fc067f40e46ce980d0581d4eeb312a5cffb6c159ecd6ebd55dbf6ca6ab763b05a9f6afc3a5f7113a7b74205814464cbd7fa5f6755682ac9f706d9b730398e103a9339e2f5389930a6e6a95caab2fb72837b308790b2399319c1f2d7deb0ecf0e688b38a3b854e13e7e3987461b7478a8929c9075d3b03c26cfb3b808fa73c205613c7ab1896084675996affa04f040e147ce4f236a1f52ddb985c1f3d778a401da7a37f03e88f58913d5b2ba06175bc1e49ff091b2f612fb10409a10f710fe061aa92b45b0c6f296480722021989dcb54c09ca5ee65f2b3dcb5d233a3bb08e01f99abd247198edee9df7b8755abeba330da012ae513dd719f915ea96364eae019738c20de55dcace1365907103fb5667d49e61052eca3771a3298f8bd27a920135b76b6ef4b2ee7c644d2682435bf797e5b340cf4d69ec7cc09029c98b52570a559f4f737cde8a61ccfd44ece9895607e731e3e6a88900b8a802129e2a38a205627aad237b5b4c16586098112771a17441a945279d6939e2a725e9616f7b1609746ef446257e5459c31ee4433b92c9b361d111ec5c78c109865f5b0199f8ff07ef2fbbb220d731248ef4636cc561207ccc7fc796c5035046964cb46126398f0b3b63ce970d308894997012b94d82db709669ff788db32e5e71c000cc5bc4bc0da4e7a079dc3536080fa7cfccd40d209924b52dd11b7c708f835da98f9bb51403ce61b21c96d6842c74243aad66f7399477528e8c32d410fcc95cba21221e2c0f01af329432c91363eb86f64b3e90fb2a6f24f9430129c77b2e54ad4718cdf703c1b7cab1f6a311e6d9d7dc148373df9aca92077da33bab02bfec8a7084119f01f621dadeca19cd40246f2ddef9674ee9976a007ce0581759b5382c48da14f30513c1b5aaf4f6148d56f3b03ff66083810a878ff8f574084692748c022dcc8804fd9425a45e8a6923df4409cc25b8786259b1e72fc7687e434db1c3af1933640fae4e9c36118481ebd3d2398a95bfc641428124af20ec1531ef5be3285fd90a0da419e9f64ee33a07324b3f6bb05894d51b0168fb2e8b41ebb3e7bae6863f5d0331c31a4f3415194167dc3c7383cb3329dc3831da9c6e68348f3075b4ab24d3085df70474b4e554201a07e9fe75a897a587435c53212c15e0507e7901ca7edb0d1342ad31623ec77da723cba6a8dd568568cfbd5afd96ec75209a0db53c81d600e316957b8617875cee18b115c21d1c179611a370331a72d06a0fa57980943d0ea631369104dab7cac63810eb7ebf7fb98c7518236a045c35177e4103b85f6a0011a5399ab854493410af3194be049d801911411bd82cecdbad761e629211900fa387f22fddda49b4a9f0c5cb4b23796d63408b77830f4130063a5cc928ea680182e95adaa73ef0529b11f1fe089683bcb10cc09be80c205b8766836dd24907065f94f014e0cebb7a4dac5ef2bc191f83690920eea4bab4b5f2c486b144ff410d0f4e0d80259a0df28361947e8a20d2fbe34655403843d2ac2160235b5d6f240dce97ad24ba52a07d0fd5dcdf363a9f4515997fb5278a6ecbb46c260be7d1ce0d9d569dd720fbe8611ccb0a56a063b8a8c96ea98a115fb6f60a3bd0af64a69900a3d4d9b54bbf45b2810501799f5fe2e887d2c1e1f43760208f2e202f949e090d0d9453f452c63aca1149be49380ced2cd1847b8a1a83fb85cc9db16734273b0a55ee2a856ef7199adcca9178f92b6244dd39237d6496090168646462501f1f0a33615a051e0390b45ae81463216bf99a15a373876094318f51d4cc041f42dc05fb84510ac1a90bfc5e62cf36b7972e82006add623fabf079b8c267446f3a700725e487de3fb6e8e54d7da6b2c54ccccfcb9ed6fc3498b39498409af872697b00da8417a7f7c5cd84ec5e46f90c1a33b1ee577621cdee3e2e8f5680a738a10a03eb6a7f5e6b1dbd7eff8ca6a3ce85c59ad1d62ca89faad1bf96f50093397e880eb450c50173c93bfc2e7393206606901208afec94fac28fff4b1290a3b76dd609a40dc3e70b69729dd15f14aa677e1df13d602973b04eb6a7babb1394730ed502e3ba9c61f9df9e6f1d9d2eecc2b6a0e5a8c4f99dd10f1b5feba0bd3176cb7f0743a159ce9e2993a6b9b3f7abd7292d9f5710b387b06772e3ef5bed43c040f80caf7d804e1f5305fcc87de831fe446872d94a727e3dad6b740c41711f493d3f007fb2722174d5c9530078f7aecbe4633ec3dc86393e6b3b8843cfdd8e86e1d70238881522ef4867cd9600532166d73f22cd3a6ab7144da1abe0330e84494a480c91c86e563c9d7bf275c1b5b9e3bc5f983cdda8d4691ff7539d3604f2e39dae0db6e2d0dbc5778d90bb261543706924a0e7f0c2968749001996e8be3c3221640b7ca780d0f7fba28ccecc9071bbb557d8fc22dd5962feef64659d5627bce4ea006f28eb162c47b88b239f1fcc60d4bf5029dd6f4c520c1d91e51aab4fd4ffe4018b6447c9fe051451e13f7d3b2d0abd4645bbb661e7d9e6a2a740dc7b84fa210b1ca775854901d740802186bb099face49b713fd21016acbee94defadcd4fa30535d0e85719358d53478881bf98e9579eb9f518d0825aa50ab5f179dc621f4106179448c5188f930e1522a7d4d59dfaebda8fbaa82a246908758e27aeb44ec303dc5488c5006fe1121fe463a2b5b39458b0bc6dbdba899dd04cf7c7d931a1080e273388d2db163382f87f6f78f1db9adbd720617dd240c72e9167bcf7c9d1b90eb866992db8533888b4a049de1600f594dbdd649891bc0522062c7681d8f06c09dcaa7cbe59934fb60deaaa357f5f1a28f9799e8fb3cf4bc4a81c2c462fdcb5029467003af696e7ad73026b12df546e989b2ce29307361504a01134a8f26d5c0c686e08613aeb4b72b9b5eb4d5e7c2a9dc1c147ae4f3ce1b6e7bdadfeb7c13705daebe01c96c35d4bdae282bbfbc6c05016ba3e28675f4b75ab6a88f65f645a09880045b089c71503f39812a2822598c446156614e7d0c572f3ad16a1e4449f071d6fb4fc552ec45369e3d8386c4cfe2019ca621aa4602a9f9b8670fd7073fc065b3f3c1a956f028f8cb3407ef3e754891364a8d4c7ac140a4dface205dff97094852a1d1b7e408b538db5d0fa10c9448911e26e3ffdd56513fd01f64d8436a0eefbe17a8e575b9a1910e5a2eb0add1f974b5b19cab3859d507204208dc1c9500a86248d50523df3d3fd8d29fa0e2641a32f6abbbd0c81524eba71f3d263dc903e555b659f9747337b26c9616ea7792da0e5ca56808a249c0276692793bfc6a0118d6aabc46984fd1945715494e7b8d18099d77d7fb2111f27d1ff4ebe7df0c0f8d41de09e10b862dc1a4ab33db71b2323debde3a3e31ecae0edf8ae9ac179a0660bcbeddb2d215e54dc5257dffcc0ea3b4d115441547cee0485d89611f550a0e08785e6e39f88144792db66286980c748e8a1de61b937f33df8fbdd28558b50c70a39e70cd36213103c05f44949a8d4b7554036a0d03a4d012718ba569b6a409e791dfe323005fbd710fac0639b45dbb1203f4bc1d2965f3795797d8df5c5b000c571f608b780fc66770781603222f684235b916d6d58ed00614568fe9abfb0b714ac20fe3671ad569abe5424c1a0f16baee8cd8e332f6dd5f8f253705f7dc05b5f6b3fa1b467cfecf0b03061365764c6a2b295d0cf855570089418ef294d80b -generate_ring_signature 7864676e28d3c4970b0debb0e6f97ea408580a98d226dafee3dd81f5a3487cc4 40d7c9c8b56934e5a5d9a07621a15fb22374327fec4fe966bb846f06dfb62cc6 1 494838f14fcf86139012741ad7732de1d344dec1773830005b3602c49e363c25 39dff26442e1ba3eda6724c73174a46d8fed8eca7523ced7781fe134b2f4400d 0 289cbfce67060bbe8b0a69073250ae29a3b0d5479edc896874a080274378d302ee48d0258546a7cd64ba74f013410c99082177555a4a48be9961661dd99fda0a -generate_ring_signature 2e56246c092a45724cacd88af27591f7b36558138dc9f84551e343f3e5f9795d 79b775c12d319addb422da0e67ba0569d1d04cfd608980caecf219b65feb465d 1 fddbe3aec2ab6a7c85a14e5a631db26aeb656f75311c48450ee02dfa54a7a16f 2de3c5a26614cbfa5cebdcf016c9a87afb79822485174be1ae85544aa2a35003 0 4b64404c3fac63c251150b8efd7507970568c1688b8fad48b107622c934f17080d109d4290855748f31bbab239eb09948d5674b72c8ba235cc22b04609d54309 -generate_ring_signature 7815f1d4b85b2fa3d55770abd44e7d7111f47c5571935d9e5abaee6bee223095 bf1e67b2b20d957c19b829881f3654396a78adbcb44e985f7aafc4f5efe39231 61 d3399fe1944fc161ed48c41e5199c0d1543e1983de4cfd5d2c410f45311b9f00 e2166c3a7f15b793bd2403656e289d1689feb65f8e6fa9b7d46a34005b261107 b800cca2346900c2e4a7fffce98677cfc8847b83a5c69915a3ec51862f302555 8f8b9048a0d2f4d0e0de1d8aaf9760766ab3b9e7cc5fa3254ce88f951029b04f dd638dac03e070da9c635e93ae6250be11a35cfe9bcfdbe7df3fc5041b11e63e dd85beb9d51168206681cb9e2d07315dc15c19594df7e2119f073566d80d8d97 31e92e147b4549910056a05d60cb44d95c258ec13b82e57d1348265626c938d1 f6d071a1f002aa6b543a8f3c5749255d8c9e659e1ed46e4800976e953b82bb1f a6b081eb3489da635b97f6cb6878848c085a60d8b567f08b001ae48ee1591649 372ce8f0c084f59732aa7312dd360760dd3019a073c8cae656dfe2fe499004d3 01c7206287219eb32f90879dee2d05f3ea36557f828e4b91ba14d30c6c36273f e8220e44b09d67dd1ff189de878989324d964281b678e238dd5ef329332c1b19 226dc13531650b0de4c419171f4265fb88bf9b645396c39835a627424d3162ec 1eb7fdedfa46a4eb222797815aebb70e1f3c0a1bea9bfa3a5bf999d2c2588684 53f0fe809deee1f45adade3acf4b4514a1d437639c98cc9562951b7e95d28e3a aea6a6fc2cb0b865ebeb9047309270eaa1442c10796f8b2fced7458cfcd0e57e 214b0b9af85917ceac889de8f197bb26c5226a60aae4dcb812395e9830b7b185 59ab7c5277761232bb784b937b97290407dc2e22e0712270c18561322dd25297 5da1f62493bde6c3f398172047c7de1217bd463249a3279193df9b6068d2af55 dad11f986df301dfac98557bdcf96e36efb105e458cc3157494319b9eeb90cbc 6ed23b5f3108a39439aed73fc45b313eff7b198585a0de020ebc72543d4c735c 90936fc627704467602d21b46ac283168dc6a8081d0ba0330e0e08f7d8e9a255 0fd25a20ea3a833592279b5342ba758ed433a3997677e84f36a96bb0ed48c985 48b4d1123003cd14d0a9e7c323d1220824f536deec12a4b887a31a35fef9707d ea8c6a93e31262c5ba1be8c81129da5c5b38f4b60290a43bea72ba8a64bc498b 17b8f47c4b2ad10e21427ce62990cba355a82827add46abd01b0a9c6c2bfdea0 de19153bf9d58038d4f0c6cb613e037b2572c641f4bd71b1447aa4c93ac75438 26b47b7e2b2996a659afd62b05add0068672d724a4dc18a33ccb083bfe30d760 fc8adaf826a43673e332af17c4dcc381d69c5f05f82241202f799ab0c75bb58a c25fb0b991261f8b531bc5ad19e62b1b8055db902c2b1a435823c3e9856ed4e0 c46d960e1541d03db8b945297fb8cfb6febf632a151515ab0579595ab9977695 0411911de3280f44aaee23820e05effb2b3947eaba456a246a29d3c41814f5c7 1531a1532463934f0bc7ef7b30eb947a65f7b6770c55689506176dbf387553f4 f620c8e1ca6b327fd3646bf64dd2c94e9acfa3b4d52a008ccc2d0ad6db5ef896 82a462271505cb720f9f9234ac367fa6608edb83d33c1de3d92bbb3afba19b16 d443003d8fca22d127eb6acf6b26c0e8c4feaee60bbd276e19cdebfa2fe20acf 2b3faa1881c5ddd2d4f4b2d8a15872e4c74c1c6d3bbd54c5d1c2e334857369b6 8fec354ba1f19ded18abd2a28812c09019bad7f4ad07a12fe4c3a0f7e5be1521 ccd228e8336c7edbf92a2faf42c221b890a6c2f5d48ce59cb6858551f93429b5 c36dd157fad2ee13582891fb642397b4ab159de4b55fdc5fdaa6a01dd6763249 6d750ef9905133a6073dd0043fdcd7ef1f0d0e7b68aab2e79321b5f904a9da19 800055a80889361483f078c8be0f37c455919ee300b6fab6e637d31e00433b12 0ad3cb33206d1f80995b7ba6518a7626739c1b4057d5169db2f39e78de3e23a5 8e077ff44226cbee60be2f4d75befbcc46e691cfb60c664c1209e1bbb031f868 1d4fbec878de15c747915bc2942331ae6353e2ea2b1a562fc93b264730dd6c24 5681010b12e8f47019e0308f2952ffbe4daec078a63c6e8452bb95cd2ef6ef09 53f5417119d15c1df61673eb903a9509011c0ed83fb8a6863f8d972bd0f1c6c0 a5479f031a8eb4eae48c0881a6fee4fb3a3cc4e2a2d372594d5ff3277044fb02 c07652126a9845578a02a1512c77ac191d20a8318f1f1756d59d14e9f32f2830 8c8bafe3ce09e7721da3d52ee6d63013650d18c8482e57d501c3fd7ee5b0e9e4 f865858cdbb9fb50898b084136dae15caf9574aac8b712679d4318e9edf7b04d e318fb363e54aef5e7ddc0aa2340c2afdeab63e262688c95dd6efbc7f5a2aa1f 4b01b80161b7bcab306ccff59258d11e994258e9163bf55cc8e652416d817be8 5975d6d8d944547346f31768e953db73941c6bc1a189836012f77b200ed8bdfb f9e4bf1d84793e3702091f8ae0d833c7480e474666b169a0884cb84d6e5f55a1 3b161766200c121ce33a1ace049b0b65e29fdd307e1dcde2afa95a4dcac3750b 9e7861e59e8d3dc063aea6f094eefa2914851e0a96f12332a4dfed98f81e1f7b 363cc8d62506cd8dde52bdd490a3d3de6c3314303166ca2a8baf8e753fb3db0d a1e2e309a4dbecf825cc4dc6cb4a6675c1c4c66bc77cf63c5e19e9554de3e70f 282113e165a5ef3f6327066b407b464d2f23abdcc0a6366bfdbd10b1f65ecd07 7522f8708006f4930053063d182a25bfbeac061d4a499d1aa7687f910a2b6274 879fcaecfc2dd21a62fb8478768194e0efe3a920592237ecfad527bdd042880b 3 54d6c162a429df0e1c14c2881b75d760a8ad5b3a0421a18160879f7b50b6e20209316f430ec267121b3d40d75d63990469709bb85e7fa2ece42a2504b251710fc32cc0f6211725981c805b6e7c590f1c3f0e2ce16509f7938a1d3ef07ca51207741c46ec59a4940954b3fc759a796c66d5b999bcbbc46d024c283c60995255009e052ffdb2e28e72551642f1a026552143eff0d1751db575b0ca11dedb7394061a3e70dfe95a02970401dc029ed1709bee70d89139bc63dac0ff5d7ccb92d702296f49314fa55be775605149b187e5fe8c472a247fd78628e2607e9e8bcf7b01ad6d1b5d6c48c86a57025385039524b6eff41f11e85e6e5c7902b1fba6e8c50bc0b8b989374bfd6bc2ed6a96a051a2867f39eabd60b9363db993b573d07f7d0dd3ea75d1c5a854cb55a8c5c3eb65cb6a663e96171aa9bc118629ff9f19ad0b0c893f00a540b883f361448fdc820e98b0b6c85cda45284422d62fdba548ca4a00ba56c5694657961cb5dad8cda9ac46a3945960dcf97e840fc19eaa54dcc4e30fa8474a0f5ace08804c6e5264c2d35c18a715d3c3280da3a83e2fe85fec803b02f7e7f410aaceedd41a920b3d8b0993077be0991579990977e93a0c7539c6de09a65e764f1bf04747843adfea8c7f855e333db4fa5f4d5ce36b90bfca9c90480d591b37b8a7c7e281b7420b9280e47733ec22dcd4dae0f1f1a7d9c33e50df400df015d32207753d1d326d0a7bc9149a79a24329c952c0568264438cfa7e3fbb0c1545679505e64eece9d55cb319f9bdcc253d32f15bb410beca2d5eb128edaa06fab4e1e4427864c6cb8683ecbf4ed8021c191fb32f01e5fafd99d8269a3f280d59022974edef9c290c945e81ab811559b0f7a4fa3f6c1fe322293d8de152ef0335005a7c28dbfc82dd78a8ac81a52a3e7c059c02dcc6d3539ef28a578a1b080b819544d7f1ac63d11c3711cb6b060495fdf6370e3844020cf5671ea901863c0863680aff79f4bb44dd36d705ed93a45ec7864bf432be6fb9fdb4990d3bbbcd0befe4a34e41efaf993555d1a076d72a3edbdfccbd7b9e8337ba5a849baece670e810245573fea730f7d2ebf09a84bcfd8beb1772fe09791083b9ec83343b9940ab1b79dc9b51ba2121bec73cf54861c184cb46f839499eda21933da5c451fcf08cb56e9cf4b9896dd6b5333b712051a905e1b4183b882b92c4277ad7f670c76079c07374b0e27e357001cb9b1277b4e00749fcf45c23063801e24efcd707e6304470d103ad10c4a0ee9b6fd81f6fc58ee5e86bd0ee46056e8d0297f31bab4220958ecba3a108330d1202025aef0ac37e31716dd566af882b3bc016f8c61ef57086ddcc2c7951876a9793fd5ad32ff02e2d2976f7edcef72c3b65d8bc903b9f307870b65eeac2b6633f78b59000a7604887ef0b88402401841bfc8f60b677502079df9e0b603be3aadddcd889752e48d36c433a80ec177819fcfc5888b987dc50dda1b21d85815b9539b96062603dbc5f81fa03dbba29756548e52a4828965c602b8a8e5bf4c20003830157331ddb3804be9d390c84fd4ae268ca6210e2592b00fc109ef52b27c9d05210b0f86032b9a97ae7d791581ea7822b389a6939dd7fd00e2f7492f6648885c1c442f0ca49ccc6c5b4ce6e1608e7c178411fdb836df0d063e7f567762b6af26ac0625bd7105f298c99751e027fb97395f68acf3d1f70a00c6450b220cccf3f4ac8e9c31fcf99493d0768d8953d0df333e6125c08dbb6c0172a3440fbfc54679c4bba0541045b867505fbc4d60351233e761f1ce85a340087bc0347cdf994db9088a09d4a6f2d0244b8345b163d6d7398ff3f986aa3e490a3746cf0a1714ecc115abb67fa29edb4635dec1e25744edf1759712901cd48a00bbb5a25e6e9c6307aca4972655f4d208be23f5130578ac990616cff249ec7f03bd8fed17c5fc8bda8e4772752678b4996dfb8e088ccb4ff10e942c216d9b1105115b6b7af2173b792786ed9c9734ec303da94dac04561484a6db209a8d7b5d0b889d5d483b3132f76a40c0fac4870210fa8c290f34a86805cfda3530a8ba960bd486ab3832976f49ec0418ee9b8a07442bd7fdb8dc5b3949e9fe354fd80a930cb453bd8cb0cc8624a163b409c9609956dc8776cf5b5bac478d48e1cbf70f6f00f27df8ad58b507ff04b586b1fbe9f02b88dca622a0b8fffa5c088c4e5d9638077a6b9b8dd63dcc798778f0e6bc8d7ccb6f849e525ea176c4fdc29450a44da4030052b3dbb078f8f828cd249822b5cf37b38782622e652121f8b8730aa2aa050204558402774aad5b664697bfe4962c9734db84c60357b8096e8d784e6f0f7d0bc155246c6019b8ebd236a7b36fd77530fc370ae6c86c1d0d8b6299063d53d30aca18661ffd090bb6b10ea1908a83ae2a1ec0a859b8c7feb126291ca7faf5820a3ae46a5980c0214a18329632d5d6823e42a3b80f72bcb70aaac93d0c78b1d50957d1f7cd45481ac22ab4c7c3825877f0999f7c6f6285eadd3a5252fc652ebd0b08de7d58b4a8635d285c1fb699bc69a774b1ba5224b8d69ba6622024b15c840df55c1287f6b1a4949c73936ad846b3547e5eb1501acbae503364696c1626a201c23c7b84354c80a3b0da7a56c165dd5131ff2446fa16356d349343a25db63e0dc1d4869ac2b88a2ff8aa753dfc2f8db086e186b2003825c47ef85bbb1209f6019efe3c0d2548874b797a0b04528223ef6faf191288667e047231bdf8e920fc0d9f6212c61a619cf31b72d2d7c5cf1edd8c26de0847d821676658027522e4eb0e06e8b1647e7e1ef9666865f299c572ea0478a69f5928de4caed4c96c4fcf2d08059590a7c5bd542f226c4b55f9e8cf6784eeabbeaf72c90695fafa58fdd6c7003d43d11ec3e01988cd4049a94d174d3f585289675ae543660843810ed1f853046147195fe15ca7570a6c8c2b7226829e8efc85836f629e4f9eff45ad3d1a7807e4938b4439daf19f0abeb3e2571962a224271aa3d416ba35b62f9b937020180d9ded43ab8a25e5773940e7b794744bb350176411618db5ee630b1be0f3350d0efce3d9504507e49f840185770a866e1f30ef8a9e66bf49e3e688aa310c78c20890b863db94ec61be119785195dde5a63c530f26c1dd62af2b33dc2ecdd9e1c04790908d0b1eb062fa9d4ff1e62760f986ef60060c8133aa74aade8489f6fc608ec0aa4aaac401e2120742e0802f8b2d5f0f2f2a805680841c7c0464657a3d301969446c860b582e639a3867879b6e4b103657e02ba6171c3041ec4fc43493409a53b12c44f96b413cdba2980d908269d97b20c8143978188c2007fd742991700234346331e93b53ffedc65ed9222346ae01a61a7d27a3fa86ee722015d134808bbb4d43f34a363d0962655d719e35baeb6b84e0aee24679e8cb778975f7690076858cbfe682fb239ee0e4c6ceccc9e11f76b788cd19c9b75aa2ee7a124bfb0052877cf7703c29fc893bcb99e842344e4b51f24a0aa2e549006f852419dcd140730c48e4a204161056efcb8040d25b496ec29bf495b62e912f4e121457b744f06c35fee6f3784f91da6a25946acf26f0d2944d7bcc5421ce92b00a50ca4ddd502c6115939ea7d574b054659e8386c7775e8852073a8dba7615a1155aeca6947054f756dc48e3873486e77467fec7ed7b60c8c582c0bd5317b4c4ff13692d4b9098eabd8b3aa417d8612288185ef74a0859dd116b3c165a1b0cd8c9d0b2aabeb0d428ffa02ef977309f45a790d6744c4ab4010abc0e2a7ae0d75ae4481ea4a5a0d62fc8e813ec145433a8e2068fa747b5b8307fc0c085cb09a100b05884b01d60d543ea3d3fc33ca958acef7b9b4075037d425fbd36f649c413f38f88dee739f02e023f2f5d0cc18633ce81707207955c906c1855c56adcf275742bf8b1f947c0dabc587f95194f6930993103fefbfe948b005835be9fc0a3be4b03e98d0be360b480f53bc4ea2a319cee317cc85be7fa5170d295d367e66689bae68e6fe438700de5ca8d5e253fed069bdaec1d5afe5025f62371760508f8658d5ffcfdf4bae0ce0cec28204b870a4f36940d43971c4e56aa5ecbce58cd71890773643cd76ba07e340c319238969e2c350884abb350d8086ab5f843f3fd1e65c0e6ebac6629a0db1f373525ccc262d33e5a91b299b2bbb1744e75f9b709cebc02f06d8c045cb094c6e7b967bc3a25f40cc5ef904ace8d1775bf6171140fd38ca5de554917cde04fb3de3c965001bb906cad41f08d7bd84158df173cf56516fd4742698dddd810feb1182dd8d36b9717e4de51ac7d0e92293b623d58b58be8edbadc8a0e6033008a8f6b4fdfa4ea0b1c137a6dedb5ae26855f7b78b9ac2c0565139dc48cc68b30fe6032346e70566e581f42b974c6f305af5e32e21e15e905e3dea96e12adba70d4f6e7fa1c63f7670cb572075591ca70b29335dcbee9bb573c674c92b03088c02566ad644ead71ef56e3e43763ac130e40b381509a1ada96d0cfcae2a92b3ca0a888557a073b787098e8369dcdae262cee7456e7185281f8eec00b2196eaa3d06ac21a87b3431166cba06d60b8b9080355ebd1df12a6662edccc8df7c64c0ec08164086f2f52cb8ab65ca5b89d00d931fb3887d37f1d493fc38d1d563a7757b0dd3fed0392a1e7c55bbb5c71c931f68d7b756871578bbfa67a99604961b94bf0841479b423327be2068a524f3d8591b4363ab3a24377351e4fc8a1098086d3b0e7a80ea72b20f633296ea01ccaa076542a3b117520afe20d1d1627c402f3834032ebcbe1d6e028f09b32cc907e4be52fc9843a9b28ade127a215642a26c0c0806779718a58f7d5e90c4d003b4fe845e4e210247cc89ad63588718c9be6fdadf00c02c98366fc0a30fdac62648ce944fb85d6360050782969dfe0fbf712b79ca0ee5e535cdac4882e9548e5cf98f0558e9d56e135050ad8144cee5ea3f4433e1005baa29013618860d2393118e36fbc4e9486ccbda7c92dbad45e69cc7650f6e0e3aa29b6fe12a84536864db0ecc2b53019d1e65d82fde60824ebda153d4a3a80c15fb8555497046f60d19c1d41a2b3d469ab7364cd89a6b672acd10802b83c508a9ce395055a8c64d0ac61f5cdf114b4ed9d221bb2ac6e0ba096fa413f2c63103a15b3f989449ef732cab5a60cfa0ce82bd9da41d61a97b676d17146e1e9b3a01a0cb05124113ce8f11dafb5bbb2592ad3218d5ec5f115857a539165cfeb45707fb23caa64c82d1a0010f33352e905416814976413ba21963ad3feb4e051d1b0c25dbd86ceb9b03bd921172657ad2d7a559cd237402e5f0cacfa7797f70ac9f023badb2580324203750172019ee824a544a1a5236e03e3acb1c9342ec02b30d066d4a9d307b612c6dec409854546817dcf07a847752b0be8a109375ff0e239a0fe2d4fdea9ce51519a70f2a3dc8109ef6544f36721986e3833411f061984abb07fda15f33616a7e5e0b0114d4690d41830a32d8f2ba4864cf0dbc5e1481405e0f -generate_ring_signature 4c4e0ad8f2598b7d77a053302d5a6795c381364241b30287731d6c7d063e5253 765d90a45d3bedd3c6ee22b5783e15ae9d418c64dabe2683135027a4e0f0d47f 1 f0aa7e8a8283be703ec37480d9a1e445ce42c58dad7d275752bd8ffaae075f9b ba659efc83361288e75dace789db89084f3970e73518e0e8d03f939eabb5fc0c 0 650cb1656a6956be2c4c344830b48aca0ddad5b9cf76ce77bdd3336b5567e50feb692a01f802285617656b59c622c6102b650bf6a42b230a5a146f2d39a7c108 -generate_ring_signature fa75d79801a1ec122a36862796c333cf133a930d25ba2faa3cfd8401b1ce65ec 64c40ebcf87adc0ce335ff113ed46236769df40a1bfa9dad5ef83d8babae3ffe 1 fa560cabefb92c708a2018c978743b79a348ec2c3908c74c44f619d4a437c2e5 c6f86b60576c5b4f0f03bc9fa8a52482cbdb490b0eb9d99b79e319609fd10c02 0 16d8fc3977456528256772969744b0bcbe92fccf8323a1b7a1d197df85ff3a0f5f7e26380aa76f25f40d53204f847f7939f02144c7fdd30ef807502d7c154205 -generate_ring_signature 7c3fb9bb3ee4c42954cafd7b2b7e8cdc0bc46262e1c01911002435bdbbeaa392 bda43de0bab94f0f16ba11829801217e25b467af70657b746544a48667d928a7 196 3d7deb9d2aeb577cf0188c2f8e3226415e5c4f641e83da62745d8f20120a2c46 4f74fe1cb3af7a94a70f3c562eb98142765e1dfef4baf9f38bd368b922515c10 ba66623e0a2b563d871badd3c5dc6a1492d92839cd3aa5d2c0eb5f70c5f79581 71b8b0d917a73fe39ee5f2f3113a90b9b26abd07f56239ea5b326544359b19d3 eefaa73d07640c1019bd2f642cd443e98832f82d7890c49a2ea7d01abeb71455 f9d84b3c3389860cfd7499393b13ddb2d1d47ec3c782fccaebdf892d7341fa19 1b0bdd06f4dab389ea44e9d7d1005d501af618687d00940c2443b84e40e72136 3e1f0672ef333d52cab1cccdc601f87d010c82611598eea47c8b445823f5d489 a8aaf0ef9003a56517acab915650d7dbbdd51e702c2ee3d84f3e62fe0d8c86a8 eedd4498a0d2c6810091b0dcba02ab3e34e8dd4a4efcf2dff9ab5d91056181a9 d020f2842103fb4d38bc130cd14439b4a4957fcc666dfb3f8e9cea602d0648ba b2dd26957db1ee390466b7ff7b84cb95d8989a0aeaa0ddce683b254390d6a6cc 4b72466a6f2eb480af8715b9a37cde092b5beaa597f4e6e00746ad6d2d05b398 f921877891f56881d08de5ac9c2981c54e6c1d0ae2b16c7cd1a677b198864ccc 41f35918409892706539e055bb30aa7f2fa282f432e13fe769af5b0f636cd69b a2b4d259f5bcc195075eca1ff07997b0377cf25d771716bd4560238839867e8e bb4fc78175626e852e68fef83df8968ca5f5c4f81e1e6112ed6451b9d5f5bd7c 4edfcc81529f6e8888764bc5e41a1fede7049dd960f5a5d197f229a5bd1e707d 58e5461abca133964190a634685bd0e149628bb202ca0d5445c7a852765ae82d 0fed57463cca6186faf243735eb3ee19296afe91474c492f5489f8ca8a9028c3 7274df448dc3ec321473fd02de58ae70c0e27cb95f0720826d7853fdbdee48a9 add935fdef42a91396a34e19659a13fff9ee76b437d4084594e7575a3de21b4e 993ebeda5d18e86f53235876c9af7e6d10f3fb65068da9bf3afbb1abb464f52f 1099ba8fd7f7cc772abe480fa5f216bfe48f46e6340736f452f5e59b10182349 1c5a734cab5a13536221c7f83bebccdcf2660425b358a30a13d49b8a2c832cf3 c0d64b092dc9db3810f59e3fb8feec10188f38d31e227adf80496f775becd04f 755a97fd034508b8bc2702ba1250f5d71ce48e86c7e351e86790d2ef1ff825d9 e47db52ba46c257338824e3adf22706fa2445fe44805d4bb94b5e0ad8c47df7d 6b2ac3928ca6dc8ef22afaa034f66fff380bafa2674a4cf490960fc8b560dd9f 9c1aeea7d25156d1a58b0252704b71b3db0228f17c4cf95ea71b2b076a8b210d 74215b03d16e45d3b251a4c6367b54f8ae438f6965167fe5ec3bb645a5885bc2 013aea76e7a4f7c15338d048a54626c8fed0bd291661963e1f3567923b1eb93f 97c048ef8421b828ac7c75253c51ccd77e427a6ca6390d554abe773a947e24f1 a28841ac91711236b0e4e6f12fd88bbfdc09e269a6e26d7aaed06c3702efcfa0 e0b8ac3c9851b8aeba1edf45b2fcacc18d7834757c4df91b1e14b102b215982f cfa5948f6bb9b6e5678f5fe5c45d266c7c9fee864289ca3dfa79ce248f0c0b79 db95bdfd0f2429c78c806dd772fb934c3acad4fb5f7393e25b521afeb6a35c38 6200b0a613b936303102722aabeb1212b78f6af51735a3f0d96bd76058ce0267 79e29d799a6afaf916f0b5ce30feeb0eae2c9952c1ae4d7708a24067169b4ad5 20ff221440a6bb518535f42abdb4d5df26b3297a92b74eabe8d660510d103919 5fd147c22101ad4fbe7fbcd32931d55b13c55cbad3db68a1ab3f6996ea25d03e 01f4dac1ecd9e01bf1320587ec8674b88d34205ca51ba8483ecdd27b07f7ce02 73227647ce4ebde477e0cd99631d977fa049e3f0d016df930d38794d45ce7c32 829d500cb72ccce0680b7445877d1b1ad89f9d643f7429aa103a655670501f43 3f3f0ada3115f1dcf6079c0867c0c9e3c9ecd63c5d02920b12581cd67bf4251c 9c0a5f030055125abf6ef7631709f06c386af32ba9bc659647f6f6b349588245 56ef91cc69961d6b417ac27d82ab9a7c1b56fe8ed7935e6802487153eb8bba15 6943def5c2ada8d0d78fd8da01a138c16737992bae209444f4b02d5b96f3ac2c 3517f0a7f5203714e1b2da2723af527b99acddc6b48733b6cf23d0cfb6a4afdf fbec53b91419e5f4c1e9ea47c499808c27055492101ce15c85592dfc9f87fbbb ab7a6bc753aad0d5485f332d260f351b75115bcdeb32d1b0a1fd0d8421ee9ebb 6d24d7851f0cf26a3ee3d3c29f8e69f4546c90d9cbd3402fae623e07d69b022d 1ec91aa4a3898b66a1ca297e32940ef75bf5695d62408b11e46b831b25617f09 78b9628ae4e16b74d36ededb48245ffc70fb823ea0e2b7effffc34d4dfb8d308 e9980856b66d6e29bacc3272d8d94ebd638117ccbddce2f8357dd45ca32934c4 3263b10bce76a36c53c3ff5cb9d439c549c7de8d9e3e28cc29ed094ea304ba73 7d15f57f8ba68ad9c179b94a752888ca27480d439b13bf7b22e0f09c30890c29 76806ce6f9f66d86d585658d049c90b7a55d55035e7b9f426f2afa2c295fb6d3 da9630706b7b980a5e17e988ae2ff5dcdd0928d3baa3b736ff8fbcbf5e6acb4b f8da823972eefbcc84a68d34ec0cb69538491d96d8b9a1ad25ccd89bca2eb227 2649b2831326133828b22a7ae2dd1c2d794bc80fc92fb219bd038e20b4087aa1 41dbeab376c0cb3badd9ef47886d8e91a16bd176babd5c93d4222eee59583951 507d4bf45b583eecd177a2d7de1d303e908a9e7b326ee66c384074a19d0e8986 c4327912595750e1b4c774dab82ff6251392fd3f2901ed495552ab943896afb5 de7c4677a46a08361d3748d31c1df86d6a677a46b2fc52fb18cc8c17e631f9d8 bd154b97db57d73e1489e4aece2e891cc90689461a32ece0adc0252f2ea8cd0d f85947ac2d27b169e3f8668cb5d309c93474e2b4e39d303ef00cd22bfb516488 ce68cca1c16786b5f84d2d1b6be185c50bb7e06b6587fd12e88aa0ad55aef1c8 fbc677f0efe1e33b870182a6c970805dbd587f7341577ffaa2ee0c20e73e2b19 062ffbb81d55a0599d07c2e7cbafe69b85f1562731b7b6ff29aef4910c83f8e2 516bbc709109e873a712e6ee502612fd247531d24de032f46d104c6968887c99 147992743e4b8c1e638e6c84d6b426544cc49051f098366837217ec6839b2832 283508f305a819032db1b7d8e5ce5f6e9c7591fe555558d5e18c1d8308acc36f cf7ef848184c876862e4414cd556f787f880025486a4932ed1428ea22ea83e5b 688407f0f2c3c3e1d46c2481b4507245be9e897b6e6bf2f71ec42ff60cac5233 eb82880ae90623b8601b37b8f7f1cec0cf463aaa4c7041de35da6bb1e8dc3383 be18599f4111c5d903acb9dfb4c435768a3003f1ad1428472263292dfede4e14 036c74f81ef0d9812c60b77d4d996fa358d1059b806a17686115cb91fcb891a7 56c13bdcb883d9ba092af5ffab17ace1bfbb6a020c562e12cb02c8ac4aa56d78 c0e519110a5cff1bd58e27124f7c2ada7c4e337bb8c6df93c71ae1e01c862bf9 58596e856040f9da1b9996da7ecc4eb82201b92d45c7fd70f7a365b95bb58b1f 58c0e7620d52079f74559b40e84bd5206ff697e8a96a236017d646daff386596 7d9971daa6d9b033d5fb18153c941f468874eed0db6c31597e7339d0a6abb824 1266519036a3cb221de554081a56e9b6ab0da26dca30dd1cda0a08b8f3ae2e53 7ba8112ca27bf3efe3fe5d6f839dd7c60c904013e0c299fca13447cdf4046a17 23ccdadf7fb569338fe0880f84c81c843d32fc8e51d53d0e6cc06288441c12f9 89ca3b7d4bcbc5d983a9210c8e25a5e9ba55ccb51399dc4f87bf766e87a803db 8e1df71b815b60d01596821f77f20e8ba714e13680e4bb57c3650110994106f5 56b73066872d8a4b86f780e8d9833dfa56419e278da45ba5e2db20bffeb66577 2a47099d3dcf25018cfce34ceafe9d87438ee4186c41490fd403e6e42b5c2d3f c7a3399f0fa1dbbd9976ecc6b4f939c860b4af7115a14bbda763632b1427ffd4 9e95ec90e3f86a6ba34930bbba2f93439766b2c1aa20d6b372f53aa785ac6503 77790eee24121080bd6cd771633b58fea9554710b771021f74d7438a99bd783a 53a887bb398735352c8b991478de995167951f2a985275f2c38102937bfbae4c 1bca9a852dc22ba46db7b0bb5793346a905a6e45bf2a7373dbf8960b5b42594e f03ce64633d857f3d0f4b267c6ed14e3a82664338b26159ff25dee6ca784ebc6 7d8524ed9d52118efce4fadef93e0a0bc66011bbc9877e66c9649ecb16aedcb3 cf65cab677b16ce2e76b2e675de6f2b324c6cc4ea82fd3d9ef646876ffc59185 d84478e2ff8128c73b3afcef77cc2f1d9f08a49ff3f0587a3a6d473f55e0eb53 f59c7d8fa9eb320ed90a86c92a02ebdc016390dca8c9722170dd5198cd1e9579 f0b0ede5391b6c9c584a61db76a0cec84cb1640472365fd278f6cd5da4946921 07cd121ae416d38443e3f9ec281774098c4779d712b0b7a85d31721c41a7044b 6f33a40ba6f6c8fe17c1f387b2a15a440afc0b262628c8c189ec936c7ccce868 93d3c1f4187815f7afb9bfb316594bbb7bd194dabec5fb906b83fc2153b4e02a c0514cde62fbd830f585c13491d6567b0e287b3b4a7ee7b7816b1e91a34637de 175954ea8c7a717813d62813e3af15ccd10935e95aebcc4ac60473f3d24de202 7f3f6c6efbc30d15995a2e1ad3d5baf1685f65463d19b98d14d7c22edcd33cee 8da94cd37f45d0993b6f37761dfdbe699fb00446e37766f1a8d2fbed8919aa26 ca869ef84d9196b289c84b0a0f90281d787a4505157eaa91f4f1f6b9e7443a5e 2eda08be6e1d26b9d6bad8360c5f30c2f47af50e07b647d69e0cb6991fa37898 d3ddc6aaa0883e6c155f2118c5e2501ef8d182593518889792e50b1403a04246 944b3af2047b28c5bf51427b1c29242cf2a2a3f6882c6fc7060086bb72ce26e3 44579a1b2fb42179b9e10e232c9d11e1cba5e6268e3e27e22fb292c75b550732 6713824a19a140834e3181023eeea4d9090d4c0ef7938ca5ac760798807149e3 d99e86bba4142a398a2dc7852aeaea64d710e086b241fba4fb70ffbbfe4ebab0 241c2ea4ecd5936b02fadb15c2d763aabad2e7531892a1448c5cb1b2e540a928 6cf74d18fb775f01ceb9222574d0d704f7efe797b096dae146c2d7f860d6ae60 2df994259a9e9279a8c08f7f37d1333cfeff7d4c9b00e273ba0bffe67ee91d58 e1f86f218491f4e1297187b600e17dcae2d71362b0aa051bd241981b4b694902 a85a6b762a221ae263dc0f8568b23388879e94454b9b588752e1a16105e4efeb 213b0ee105d7df0ca3e893dde9f50a57d0ff2e91031cacf2cdfc6547c07d189e 9c5879307b9d6957f504d1b2cf3160fc787f747122abdc423526640327ac59a1 ec4fd6c81d070ee743061c36568f2e02cd1e1b75caef5aff94a6143005a49994 2002f3a945a9d21bb9949e8747df9e40c1bca2d793eaf979ddae2c712ee6f85c 3f0eb01b2613ed363e129a4dc8d820fe325c25911d8a480a62159c7c53ec845a a8164cd59ffdbe57cb56c7c049b1e95f3693b0536831c840febbce5a723fc5da 154861f7eb6cc3253cffbfc3bf8e5dbd79171aff9244a205454df5a827bed731 224f573206dbc13fa4621c6b73b036a241ea25619c14628b0d9767743b7c967f 6cdf85e92acd2dfd4399aa1bd45d803ea3d0794c76945e2d80274f53cdca5f88 0d3b885c422d389a29a0928d6b09883f4a309bde6573b495c6f6a268eec2d519 428e5249c0abb96946314d6ae67a91339e8920d2d6df6bd2ab01618a81d353fa d699f8d209ef4bbfbde089705716e162ef404983be581dfe016e0c49a9302029 9eda3826a1477e2c37e40e687959331664ededc5fc4937d03e81da5c0b34ed68 f8fe870c71cb553166802db6027fca173c85ebf0f9537430371b2ed7c3a3764a 622bbdaf0a8dbc4430d89147f9092de7a2637d0c5ca77d1a2269081d85c3dba0 5c372dc15cf0c42e3f359fd4c1a5c2e1a019414a400e0ebd18394b96a40c98dd 91bd292ce4c3c68a5f2579f90b4a8b09b8030e31f7e773ee844c720b079d1e2d b2c171f58346747d6ed7b02fc65873f33b7e08ac8183144fa59ef23f2bfc959e 5cf6a61116fe8d0b9f4804e6fbc07af6ae15207b392c8783e404d5fd293c543e c6646612e88da493c7944943e2e7bc2ec432410e0f43bf7dbe63545a306975b5 ac6f8b9b72dad35d5cc636b93a3b4c3dc77b0e2ddbfe90978be8a66e98e3957d 83171e32bb5e628424811c615deecd0f89377cb0279186374c36d874b5ce2a2d 474871f34e82e8aae1f236fd7ece6b9bb8d9627d12878e64fba94ee0ff917e90 697ac8f1dcbf582bef5162d945b2dc2d1a1969bf59161c5aa4e6ecdddbcc6b75 d0ac304d9ab860d7ad0ca2ba9b3a2c2b47d937051992b547426502dd8cb74fb7 775252729d52b7fbcb724b63ef65d2c14607fcd3f57cd9277a10c95aefcf682b 8e3523b50d6ced51c5a82b2aa13f2d498b504ecbaf220d23665f5c55645faaa9 40460ae2f74bde032b31812d53a373111a7f3551e0360aee6a0a5468324b29bb 2a971e6b6aea6dd849df590900fc1f2f527ef624b7f578ce23a43b7da1766ad8 64228053b65371ae1d0a5b3823c3bdde6ba4e9ab15cf84525efc24ab7810d7f7 673629e04c5f248c138f987e346d6f62aa8b70142768ed8faf516280f1ea6b56 aceea696d75ecf12f55924214699453432418a6ed52b2954dfdf1d6eb66028d4 124b855dc5d1164885ea22ebea5849b513932aa42feea0df88bc146b26062102 2043c7629e30d8554739ea06709d746206c08cd7998b6fe7278022855ec96f8a 114a9e312df49f4650807d8d568da9c868508d970437d843c14f23a5b5c745e0 c6150dab7d30c4a57bbf9f74bddc9775d86d26bb9c51496b5fb0887faf32ad8a 24335733fea5102fb726536a5f8ba5cdf20ff37b4f7640b116dc72952bd617c1 bbe2bb771c7bd5fd1c79eb68e58bf4d49e0c45c00e274bd5876df1049242a729 a3ecdcdf9b4af0bef4676d65a25188742351af5648c9233a76e1c75a24eb7a8d fa75c3f4785cb295c8e4b7cf4f95e9cdd80cbc14a9dd67a1ca7d544afd7ffc17 b02c22ee4e0d7d797a6d7212759fc99de5594b4075454495f786aca12e919678 c81e4d5e4af72bb134385df0131616470a16e312f2e660610045f5d3031884ca de87536fde596ff29d65922de1d67d596238e5a03ac0d4e5251dafda619a838c ad6c7c271779001b676bc498d945e90fbf6ba0db589c5b2d5a2e4a74bd6e8d01 4aec1dbdb3e2c9c272606ee77ecb170e6e64b0c857ef9790d612aec230a8475b 82ff743e813f4ef077ebfd66ec61c32a27afb8978bcdbfa69c8b8988998acec3 5a0af108c17d6deead0149accb66158f8246d4939930b8683bab88c17d30d30b 0f46615ccbf367984b3a89a7648d557ac69848376fc56e77ee796dd124ee47a2 5bf94829a2e3afb01140433a9d57e8a59899db28f19e64aa46ff28f5ef64cec1 3e01bf0f93e546cad6eb526f1249c1899a784808ae940f4d15e0a896191d4e42 86e649034e59402ab71e9e7e577d427d917a4257d65a283f1d7ac023ae559fef 29a914d0f4951aa24e2f0aa63b1f537c2081b11da7799a70258d7651a35c5747 3f0107399325655f8b6b33023796e8e55d75525a31a9e5c256f24cb23fdcadb1 6b052938fcf90cea2e5caea0d9cd4e183990f1efba477314b8ed59faa89bd34a 1c6a32ac2c6d3cc6541da9afedd51357bd81172dea1eb2d03a1264a988f761dc 889927d51a0256466f031595d94b95fdf732708efd504b9d22c6ff54aa3d4c59 c612ac8c54634bb1d7cc614a177c03d4b0bcd024ba4a74a43827af518ee607fb 6bd3a9348b3e2cf91c5e9431dce292fc2c2322d07ca626767479639c654365cd 9deebf112490cc58fe510d7aa8c804657bec78642f236df79317165cf2f206f0 dced935b8c9df181c26a925613e96193d52dd051f2001e74f9b7e6348d9148f0 3f6277b2f63bfbe985ece514161452767ade8331f20c40f3f0d0a1035419d533 4d95e5dcefe000349998e1a3130b7a7357453129503423a0f02683aa941cc0c0 84bf7e3dfac85642c1004cbf84ca5f9efead8b8fb4ca98b779e9e5f5445fbcd3 c3aad5fc72c27162dd18f9fa38b371cfd98b70b885ce7e0c428527ca7d8b7569 c716e52b55b748463b4aa1af2f5e178536b0ffb6bb62807d0a51960ad5cff40d f1bcbedc26b50cc7de6f7ef24a1f98d58564336922df6e6f4a70337e6670c6bd 65323b25016172bd154dd7336dd6beba5ef6f45a8713797a6ee39d0979c746f3 4204c9aae42fb89fedb2270e6e29eea1df504c91a9a50f33bb3cabb506412d56 6113d4806a2e2f16880724d125ec7fffe7c1292ff38cb6c6eb8a8f5af171d781 0f022e7bae4d0615d72527758f20be97b6ad2a574830b1d6f6c754ec4b478a1b 25178ef51a568a7d23ce35b29ab02a618185fc258aabf3ddd01fde845938aa64 f06519e655fa5a6ed6cc80efaad8db30ea29324c7dc983cbfe0f3e931bd5b558 46b4b06ccdb3670d7027499daddb0bf9a1269378856cec1700cee91ecda64497 2399b6d1babe50a8c854a3bec66c0e7cdcc7959b03d8ab9f7963063e3ee7cd25 a7a4f6c54713d2f6305c2800a582092aaca32c34b20aa07d0a968cc1ba45481d ed82f9e92b53a19571b5366ae203f7dcf90f19fa2edc87335726fc22dc09d81e d9787436fda6cd8e5791574b3aaa0283ca7c4a5ede9b2433376ff45160b58a0b 82 2acb76323c11259873cdbfa5f4108a461bfe611364ac05014e7f41008b94e20c1610408ce05ad2b6582802689a2e82845539631be030a986f8ffee43fd65a6081e939a480b0e685a90f8d69ceb9486a812a5bd77558d974ce53a24e0f72b4d01a0d291e421de0dc2092cd61316593d0a137dd6c1c0ccdd86e6b93e367f3c0a0dd5d2d34f29f932f2ac3f8d4bcdcee405755e201a8b5c18b73946a17ec0b1010c37b77c3b2940b1160fa985bfe554846be7776843955fe474eccf13f1b13df50ca2dd2737a86af73bebe5f9f7bdcd488d1d8958c859fc64c62d378e87e6166d019337e606813c052cf20883a2e4ae1e1a3b8b37afb1227d7c6876b18106569c0b06069445a13fad842c772a0f397d8974e5b3f99bbb0f2dd8354ce8486275270a8a2a7ba12b82b5c026ef0904d1de28ac1b1288f29abaf6b5f6baedaca4c6060f2d729b0fb48d3bb9a25534e302c5c7b14c39f70b722f4dd22c053602bb273f08d01f70d7e746c0bc476d7039a2ac706d040ef31491d6e732007d6a08b9557d06db6cb81ff12868de57a2e50cb6362bd46f37773c01a05428d838253a38a3560cbbc4f04231c8f309f9fed63cfa5bc40c66e9b2d07d8d74059ff34e2ee834c500e3a566d38b49713e49278cde77eb200f7d5c79b0b22525a7405d0a0c677a520c9335a0a3882cba2eea157d082c1b28fa31e06dac49dfeda28578205847805005881acbe1c95988a2e8f81aa37b0a7d2f60a2b06a6617e2d7309eb11f09968f031e211c743259746552ae79019020b1bc4f5caddd2220f614ff6671dc58e02d0d355fbfc71bcf625fb992bc55d00434d3487cb9246fec876ec985caf18d30a8018f3c930f41f46a9fda494178195aaaf0102fafcd53506dffd628cfb130115b0ad9b85a807d3134b0b8f894624ed869607ac139309d27fbb9a12dc34d09037406bd70f7910e9529f175f4b4d346b61557986ffa732b3a3195b90588a738eb7c0bfc4d47b8788f1f62131755f323ad9ea83583d3347ee7f61f1c1aca4bccda4002318f84c2d9a910c5801ed23fb4a033c8f7d6ef340a3297706461db429ce39808196fa7e99a0a5b60586c2d02b29e2fbb359af37ba7257def7c8c111bae464005c4ed1c2a39c6338a55705405018042440303ed124e31d7b12a729719b2f0a3042aa329f94dd478b2f5d6d1dab630ab89f3d16d5cfe845c523306e5a08c3d880e7f452301376797ac3dcaa319293863dcb5cd395a62b4ef7555f01ff8e437db0370c0d322198a600eb28e5dceccfb75adebafff408f2bc93fdb1d85b9b5aeea02ffd8d4c3c1ec354c80929c1fcf9afae7fa4059a7f5d43265eed11d61afbd36010e8f01bf932a41092a6b8ff649e14b24918c43525be237da5bfc4f8d93b9940a8baf3f9492e704f252ada20c2c79a908c7395618a1f1aed4c04771c34608020c6da1f30c130b70bd49a752c1c3f7f26d6291858fc851e2eee1607cc004259e02db206173c8cadc16eb6dada5d537e08cdef1f328486278cf06a1b1d17eddf90873b238ee416380e30c0114a06f1580451a95baeeda3de31bcf5463d58211640595e15ff81653ecf6dfe4c78015d80092a29c6ebbe0e7475a592c7518431873074717f178e469cc1eb9ca828e3ab2b84755db56ecda84f4ec7bcb53b8756ed400a2cd3247322d67c818e3a28a7bc4ff0f0d199ed5335ad503ac7e1566a848f008b6d43edb13f9585519f310f2092f8d0ee914aad4298a7ef058bd389025d03d0dfa22f33201a48bf56cedf47c1b4591bd3095dde6facc0d5fe3a76443fbfb1e0a1f372488ac3a04d14119ead3f34b8c10a3a4958e60ec2d973bad29eca24e450a8cb0262493cf0e8a053e8acd1a422dbc0b71484e10c1ead3d7422e41bfa19604abbfde5f243c8438220f92caaf351d43bd1db0fd00dbf47eaf8f68051389ce02935855264e4c805fd9369a55d702af2b9f8c9426faa16418e93830bd0aaf760658d86101b208affd12ecb4e3335a93dbb907bb8577d3d09a51f4a51c2c385c06b9b3a131c98101e22ddb4491cc774ccf4c1ae3f939c42cc48636e2e87fdbe40ae8857021d1f49d11fb78b180143a62b22a946bf474cb7311f959b3ac60e9a0020ba298497103ea048efef539e74c2e121fff6347e7ec4e7daa8ec5a6e1e0380d1d9522fb014415928cb4fbf1ddd3a9928a3375c551d14572ca40be9bd09ea107a6e4f67a4a1661ef385c8e16476fc35b6565110b01823ac0dc1a94d18c549406730bb246b8da40dbd136a59b39d93dab22b49504fc35e52b778a10c4c63a120687877f0a99227153a66b23cf487be93e949686b1284e53cf0a80fa77a011f80ab83464d011fe72f7b23c8f52efe69ddd61a8149e9b2d8d983553fc68aeb82e04647e48a7f5286763388d7c86b5ce280564751012be90750a9d0644670550860051dffd84c1c3bb8271d6824c7967b9e4f5346dccf2ea0764c3eef9bdb34b7c04cb2e77de53f4786534c76581619f5db369364ddfcda1ddb426650539177b060495b6134281e7a7c5353fa3bdce166801f0a780f78fd13d3ffa867a6b0a016c0fe56411ccbe6a146e84d9b6547917bf14d7282733b66c31f26a23e27c28882e078798464af548ad7128ca4ca84b1a418dc22651aac4ff0945d1082f1bd9765204733b10d7afd451b60221cf276536d75644b68e60304df090db729b7ce80d30009b48e2ef95897e4166e49b7b0536367ff89021aaa350f7805675fa8cc214cf007ff4880f43c82f0e21f7dd57b73a413215919e223e3a2541f3386af196828e0bad64d8b4f9097a2739dda12fbeaeedc05bc0f69a6562657baebecdd11ec15106b312cbd389ac26137fc6dbce5d64f5eaf20ebcb51abfb4ee572444def17e91087ae57574888ab851720400b7157d740618ccc5257ba7ea3b1736d2f8dae5000bd699a331d435cbed5a14a318fa0c849a61deb278d702d7f3f559942187b8220f9939a72cddca41095efcf51e48f92c4da20a83c63cf4758878333f1cb089790c2ea7f398764fe1fa914f0fe7078145f6040009a861dcd27615f5c58f5a17320ca436a4fee5ba69c60b2af50fc7f7aca7ed9727fd40a208d966c791d3996c120aee118d6c3d00b064844d23e24ff16c79bb271b1aa6d92553e331dc58d6eeaa065ec3297213fdc8dc4abd692da3908e46c021503cefea501be674f3601f08a70316a49229c8499e9087d925cfe3e719f2a739761b4f7768f7efccda887f5e0c0f1dc170ac9beead502f758dd1a28be927271746dd2f665aba003097065fef530318e78bb0e6031e38544f00d5ff77421790ede53e9d33b4bf28848f0b1297150256f2d75d335bd07635f505b62e2b9fa4eacfbe26694c2f0aa3e3a6cc4315a90fabdb2d22ceab0d531819a05507563b3e9664cfc3d1076d257fc4aeef9c8de703f25dd26086dc42ed93934a960ba47c956c35753f8ded7ae256cadb11758928086a93ae382448475adef44408b9eba3859a772b491ff82eb62ccad5434a556a09935cb3dce71f56649f786b7d2dcbb8dcf5fde40da01551f310499e63880194094279494c319f26b2be006c69cd93c315e87a9e25216cbd13d61cd714c5644b08376de24e6c661de22dc2d1bdc6130acddcf94f99fac6e29ec95e9f14fd942d0da6ab70463bc5de3f010830be54dc2f3d59a1ecc521e8c059fa00f23ec72b6602f9a82fa9f852f7f078262077fbf2149abc283d6bb7b2595c7f1569aacfd2d508a4558105c5d73450d578aab5efbd4321cc0911bb6a4d8c99f99cb690a675f506b7ec531cf8b5de08d0182a27effdee6fbd7d04c65b9c54e94d660ff1865a280401486fcd8f26ca7e91cc24fa6319583383810fc909fdf88cb5c1bbaeee11ef0e58aed9376a0e78912444da4fa20e1dfeae0e57b9434db6fe8c0ef0fc8a3451095560770362029225a3600982d295779b36d0ff05ff432b4c450c5e34dd97750bb4d98479a8566a025d32c4ddab00c28210212fdebe4bfbed92a7e46b29cbca064ad2d75f14b40f364407bc885e38cda0ac32729983246ef1657124e541e4a40c40f6e84bdb5c433db202b59db49e953bc7700a1c144050be8646a6e5dc0ff70992485ac3ee81a7e773d638f9ae7450ac61a682f805b18200fc0c76b53542ce00a7f64eaac91e7de6ff53a92a2efe04d8d325ed0cd150c148c22b603dbfbf8102e41c7431fdb9ff2ed8e3640d83eec7df5380914666a8583974810d43a1ce620c89085b6aaaf3ab53d5d8ecee15e6184800569531a697f0a8f3a827bca202af0828675190524d1eb14392ace2f2c801927546ce80f83892f3d24ddaa020ba23008bfba031ee6ae2e3d73c9caabe6c34a34e667e9e91bb7201feb0f5e13010c60d361f9ec50d0584f8b2a3152897a50c2dd84990567254a7fb5255b1a82d11850d566802b1181d090df0c4aa627a15b1c7aafc332cf0ae52af1f81ce93495cd906c376a96092372fb7ea24a489b7ef64b323a83969d48cb366e9d7730931ab35095d1de8db865389814acd1036c58bed52495c1594aa0c0efce69cf911c6508a06d6a187090721ab4552241a01c94ed576286b957ccea0f2f8f694e095dc485603a59b6ce46216ca52c8db74a8ec2e445bcb3205d7fb0322098ff9142b18819b046829b8d4da2981567026fa73a27e719923c42b885d1f2f88f0eebc17cc6b3e0060bec75f0cf99ea85d18400fc2f426814123a612011b71ca6fe129c6ba9a4c09e38c6f8ea7f33a6ef49778ff9a8c59014e7ed68e66b16a4bd838c9387ff7330e06771c4f8279d83d78d4dbd1a37fb376c8b8a72cb5c64b0d21de66b470903e0ed55bb67640e6e67979e62d8ae917f3bc1b71679374d04d7d62d56dc3f8244e03c686a5c24c6769a2eb1f39b73bcd1a16c6f68f7dcfcf5661635c0a5d3691e90f575c9a0792e7c700bdbb06aee5cc497887f38b5f3724143e8f64b74c9fd7a109b07fd4e2352f6a37cd3a41c892dc477699cabaddb6dea3e6a98dab4799e6d5010baf2dad4878c1017b4801ae7927b113e47f1ae3fcf555dadcfe53e4f1cdd10a4202d30e627ab79d4f4fdedf4004dd383cee46ba0d12ed424ac5e4e634931706b30554a84e13cdd72c3b3aa3cf04f50cec37afb5d738dbe29851f41a5decbd007e4e6a697db06f55e8daef9b1398c43b49ba2ebf2597cc2fa8ac28f7caf26504809719b2f88229df23e00581ecec6f0d9129929fd8feacb5ccae30a96cd74d00301f4ac99f669a5788c09ae25ad2343f2e52b5a8ab2b162b21af3c60810b1a0682b3f38d9305234522c267332e0fa54ae9e4a8bd830bc92268757c752a96f204be039914b4f32dcceaf81e8ac382fef26e01261117f662a52955b6877838c10e03bf6582223f9ba4acc7c9840a56de283c384da8c5124eaa7036de43465e090407bf627fd2da48aad6276a8dd6ee8dd1836ebaed402dfc3af99cd08f35208504dfffbe0e392c636e3d33b34a2680964549e90acdf36f53c4c9df1d2dd305d20767429558a6357ef8c88e65b1b29deac7459d2bac2bc581ba363ce366869bc70c4e8aad373ae0ce2052de4aab89581d58125e4f8585e4a58906c687ba9163860706c7324c7ff516ef59154147c83cb5f6e4f7841268ea1b8e26a7bed239a74f0330b62aca65b5bdc2c51d0d785bec963b88330b21be98ab8822c87b406f96190b5f81c5c7e1de42c85782c86fdcbd939b8087693cbf95bec1c76d9acd0cbab80687bec3096793de0cc7a02c1c0cd4dc39b4b4d0285d5d13303315ef29b06be4035b233496d8da3bb1774171d5fabbf0f97d7069475b068d5c25d39d63e77dd50edcc58221e77f3b2fd56cdbe65bfa02cbc8b338c08816763a09d03e334a4eb20de766317b4e10c5a42ed8874160d6e7d079e0888de19dbc4a7b181e313398be099812c01a6c84e8dacc7bd6f7d3da937397a032157ad21baa95e89983dbf0660915b63952b23087bb962af5dd8fd81b0ea95c38dac12cef62d4778ec2f0428d014729989ee3352471dfe09d5944402ee21e5038c3f210ed5f0357966f1c569d0053c419bdfac45599bcec64084d3db6e040d47bbcd6e22dad5664e71c79b5010649b598e71edc39ea0d5bf64dffe06d1da396ce131753c59776c61a13cce984018c5694e566596ef915ebbd4ad5e95cf078096e88cd3c7771693b3476da4ca7031eeb24b85364ee5f1f127d4cfb175983026e1a9f05c7e5a433bde8605415b4094226a5bf5055d242665313739bc15bf75f27342434248e6d49e3c984bc93c300a6aef818cc5213954ee403ff3e8ff12b9f11e167b7dfd88c4274f58ad50f540dd8123618be71414b970316c99e022a1ba33b5f8b08ae927931920b1a1a866809ef11678dfd7bb985906d3eb829a8e4f87e21b922d0107cf184ba18db0e05cc017654f3851a29ffbe66eac0cce0c36856de90bc19580f6001f1a4080c455cf503160cb4a772deb29d37a58944ffe95f4d918582bdd638b611e8d1e5e0b02a5d031539579e8e8e13c42bec3b501c1ef2911312f287d34e51f0e7df570a9065310bbdbb87c1a99a2248c20bb70c7978c5d686b9843fcf66e5d3d070e416c0cf7f04dd070e79eef4c3b17a9f254bc8e48c17a9ac9091bb9139c5faa23f18bae1bc0d270d7b8cb7f8c870e457a1285dd73fe752136205cc26c2bc7c0d4c7b22adba08e8873b08d19c6b8af5bdab286b4174fab65f75e3585ecf7f8010390765d4660cf9400be07712e3bd7bf7bf917c499edfa156fc6a6b49202752dd6cea91d26a035b4d8bf2cbb3c436c920615e1d286a8048836c7e80f931c8573aee92c3bbfa023196a63bc5d27e926cd163498b871b755f25153e66aa0287a9b176ca80edc10ad0c51c37259ebcc49ab5bb554a412887646d892cef791f50b3fa9e90d426e108217f76d143becf9a5674d1917b45df7b8081f8863c192bc689b07cce4d83b20f8480e71c3e37448d247c9eae4d3391f26b95d84b49e64226eec0759cb4e5a507a445b687f4cbf4c52cd6d894eae69a698780f7355a9db22f14664215a226120b410a0c928f006951f15a288bd5fe0155671106ebad7fda2fa9702e66a8a7cf0cb4d3afd52eb2e07cac7b28e9608530aefba8ffe5f1c0b77df205e61e476b0505d47f5bcda43f1b088ef4c7fe7d570421f1a0655a91b384726de2d71c0c822e0ba255a71319118206742b66d482a648c6143241645eace3cc1595a4fff5a5160f6bb0fcc53b16ca5686be15f692bb87166ba741f3682a45430cadb54b1078b209015d2e3619cda55de54d89d538d72389e3eba4199df218966797299ecbec3c09d35c26024e9d66c0457893cb0e7e0827186ee638f3af2bd6c01ef585264219050c9d8f247d85f6afc4a18939dacd8f3207cf6b8ebf32094130df7146acdf0a08e93a38340300a5a6cf722e50c3527a21821ba41c24411433671da11b5e62a80e1dc1aff01b9a8abefe3054542eb55d09714667a149161be56352b716617fdf03f6bf65e77129ae8cc7a7a06fc35a90d4f0f4069cca0affa2faa1e7e547a201032d5358760a57ed17c7d7e6ce356c416f76036e45a67ba9e6cf3bd24ebd01ed01d755812efc8924f580aa3dc6f97c9c801b193df77ce9d50bee9dd5a8dc21eb050d7aa8fb42b91bbc8fa7a21adb428d6d2378578da3871a02151366d8bdee8b0d0b73d191439003a39559130d080fb15462a9cce312da4abcf3fae679de8ae90bce88fa4f7b835159c8f974cac4dd7d0b5be28739f85b67bde1fdc0a8c5bfad07eb34bdbfd7cc8a97be5047243cc66051f6032cfcf3570cf8f2a7a174dc157b000d567d67c8d0de75a8fa6691bd89e37ee4e1f3849eb336cb5cec711fc0c6d50efe3c6ad66b93307651370f342f3eb09a3013b2871e3bc76be372516969fbf30b71f4499ae26cfebea5fa8f75b6b1ab641f8be58d47b03a9c35a66db1bc1fde02e01e593c0de5f2050d1aa428b1a71e4213ae63c6edd96a75202a0476162c370cbccbda0b90fc12a7f9d7814e8fded3cda58f1abbc53df5f69b6628b07c556b034687cfa34737fe1ddf1fa5a44263c7bd57c808f8c8f5740442f2d1a0f60b0f00c966db1acc68bdd03763022c2d2cd90ea044c42ed3499740c8a47564f57f2d07f72f097abdb0693f8a4a42a86ab1628ab8cfee0af976e6df84cdbcec5b14b00f0ce5774c3d34146e21d51a8caae9fb9fcf7560e3ae74402ec9087fe07a94f70d2eb52081a2b0a1946e05cf90afe7e6488fd640a7616456300062c99164870009fae52c90d219c3e1d27871d1f161b800dbc988c18624c93c78a81fab62d4770abce8433a59687211d58effc6d3f0a7bd74d2f85fb93518093fa28d43beccc20c9aa24436c5f384a9368a7c239250fcd25d8342057dd25d4a47b3b37f1d54750e3f7ff641bc0ea308a32e6f8a2be3a624fbdbc1970d5dfdbb7d5b57a20a182904836d39e88c24c6ef5504ec0819b5a7ea30ff833baab0756eaefb9097d419bf0bab5d9124d0d8037af1420b350e5acfb63411c861c983b766d6bd6bf16550b50e43919b39911351d31bf9aee35a4d1161504c75551406d941f34c785408ac5d06c6ad567043ae7fc414b377e6ac0b7c143a4eb183d3798647a949d52eb2e6f10012e03619125f41d6635ad65c86a866608ba0607035815bd718ee7d611a9a8e0796def74bcf92707dbe8c41820dcfeefb02cea57f7705af9bea1990eb3494bb0d0c8a629fdefa6c1120728dc7e6190ea79a8515916d8a34406eed8ce982974a0554195fa8de29acde9f54e7ac31fe4c9b0dab168864603b50726e005a5ed114024eb35426bf9eeac0226ab9bfd00acc103902ee534e7926c4ebac4f579862f6037d6915c8cd0f845514ccad6ada6c583e42488bbd4dbb2bd5ab8982e1daf76805a85ae1c2c087b367babdbecaf45b23a78399c9c96580a9020aa57fb4e66fa9016ecf15be775062ace07257036a8bfa0f389a48bed92c2d1cfa15731e69192c02e032222d39707290850888c4c43a60bc8e5e3839950bb2bd33f519611152da09418f9ee7e459f8c0fb3c310c13fa15db255b8bff94e12ca5f6a29527fe6dd700cadc5c1ac75dcecb760207920b9b86648fcdbeab2dd03353ba74fdfeed5af103be1defd50190a641ab546bc8ec32cdb73877d97c6f4e41e5f151657ee0ea2d0bc7a4249983f38a5833535c6d62684f08d5859aa2b0faf8e33aedfc13cb4a91013f549d6a3acb4108cfc84bd5a9453d5bc55bafcfb01153062ef3c52f94a7bd05fdce0373114844bdf2eea82cba82df7e82346ae4b8b951c2fced404896868c0f5456bae6df029abe4533cab647e6f85234ae8e197c41c51ce13942339b09c801fedc365180a90ef64aeee66f89a5873a3ef76b1456ed76dc6f7f041134756c01ad0ab380eeec6fdf38df6e4ba2e9bc6fd33ded3fb3cf047b8e0b25053db4da0e061763451c19ffcf7c0935ef7731d0503e346a9e9402bec37f933fde152d2c0821087e1ce84bb0360ee236a916be89a3c228190da2582a18f7fa033f8065b40867f17a4525eef932dec5668994da6f522c549700c0209e5464dd46eb33848a0e36963fb727f56613dcf2ec2597d9a682dfe82fa422495a32a6273308ffb768007f0a44e8d997db416f0d7fc77900d315a018315e33fa854cbc86c927682644044a988446dc779536ba198e254861cb7ee18dada358611917ae3e350044d1410cc7420e20f64519d701f21b3fbb317b8b44d0ec69008808dce18fb2a901e8c907dd988a91c430407072c17af8d4350fc0c36cd451d57c613a60cde97c6c24e90476ced43c9b84ae6079651ea2ca62c4c83c0f9841157f8917c48ab5a36884040ecd27a89383947d9eedcbee66979397f997c4f9a3eb93127aca56919dd0e6f202d8d282fe1cfd2d30094415adeca541365e64909ab92ebcfd052a124572221f075c0450e01c465db5581630e1509abacc72b22f98339d4605124053cdfbb3ae00d3a42cf75adeb4f49829187b5b824c76ac6eeb3873284ad40e625d986f243806a0ad08507062d41ff646ac0165286bc2a7968d80f128d7098245febf6f2f17091bb8ce616d176454ea8c97d54f8d87a1b8ab22eff707adfb4ba5120953551e0dcdd253f56d2e60524c9baddc2ba19198a9a0c2372be17a1706084ef6c95a2f0a74a89fe9e866a985e673c915df4a9af2887b803ca14f175b984309e8989876044e83cce4e48a812baf11220507555d07337576155d82469131a004488b90140c23caaa6d33889c2b8e0d06f9fb1afd6e8d35a40119450cd2336503418879090637f5c7ea6907f04cc6ad11d1bafdf95a693e703815bc71ffeed805b2ef4609097318ea537e07c2376fc268ec5911243e451b569b03a757bd6459c42dabe46e057bf1a4a04b1262061718629c35486156df3365cf2284e7d6049723ca48cf0a000b164814223cfc8a52b7f0637b05e6d27f03140d3eb8bd124839309ac4918a0db704d85b989ee9d7fbf8aeaaca115df92cf4955428c25d5e13dba56bdacd3a05df07cbf0932d321a5835b9a250f7e94a64b88fa1a22a393ef14ea49823f01c04381df894bb15ce00664d01564bc41867159e92b99c851684e62b387f861f4f03577c37f11900736fc2d85b421f61b9147000f5f57e7d67a0f060b38b561a74039e9520dbd41cc423af03e2e27efb71b1ea4ea583b2656f26ac07156456420d0c947dbceac0d99c8fa9bde00188e0e27281384192966fe69492ce7dfe0559df016edae3b27325db47b2b20b35fa3fd4c85e2004c8dd1690235cc49cb259e0890cd529e6876a6fb48ac20297437513ed006a068b2a902e1777798dfa38829ba50d3ddb85b3e199f4ba6baf2cd4b5314aa8538518a0de5dc4d8a40dd89758e81a02cf9d805191e424ebcff9dc66326232b2971f3880e61b2fe23d96b5b4e8f78d03a12b9dcbd0675a962b748b51199bf2c14ce2ac67303f2bc5891b6458360ed4047e3696d8d2e8e1863390481cc5df440f0a4fe6c420a108a40fcaf6142574ff046bfa6e689e746b1ced5eb1f329e4975d609507e999eb85c204b1cbb578ddd70f6e87278cdd3d237d7fcb1f9c027393b3b77e88ebb43861fc9e9181912c31690cf80ff6e13455b54bb6f47ba51d5c032c6b1a29fdb84186cd04b66c5d3184df074e75b9d860d7a19223f9c2653c0724d9566c4b2da7c29d3c9cab22ca92582c0e1c2c7a9605fb9e58a011bde170412dc082b264dba6d7690b4bde99f166ca810a6dde4b3e265b9ee3202998062f463469fe2f1737e8ef2e4cf3a8d0af59d72208d5d15a0dbac55eaff3ce0a1e63f43a3fb25d7714631fbf851c5d5bccc71d15043e2415f0bbe8bcdf3337df35826e0d55ca14b971c879cb28a4f9f2d18afdc00a7164870761cee1cbb2d6f38c4fdaf9f204e11cf316cda15556f4f69a4595fc0d6a587792b037ec5eb40f7c06620a790036851deb5a9e805fd09e05763c8ba50bf7cb7e5f8f1b7c83bd9b24903b2bdc5c48f8304d817fdebd048392e32c393b000b6f797ad8d1002529ebeeaf10d6dfe46240c26e77982fa6ee0dcafe8d4909039e9ca9e234f1fa5556b456c98e107f31810d413d07014d7510aff625fd747b0175bcd76c72b280679faa5c80858846b670a1af06bd20f2c51c7fd86d50543e00b3d93e041e89ba3eaeae7192deb9fd0295d3bac30d57ddecb7d00ae4b095300083ed90f366c993d8fe3b0bdc61a740bb283883e0781b2208cdc78aba225a800c63bf097307a95aa1a9cc3b57a3e569622047189e62c758ecefef1f0475f27b04cd04eeedcc0b54e3ba5769f8186f30fd7e137abb664d437058f23e1c87b8e6069f0584e7a25ddbfdf9a076de3bbd3f43a105ca101217aadb75fee6ae19ea0a01a932163c9e18bb255446ff24f6b3dc364ec07fcae69373938f723bc07eb017011fe6805c5c05178eba5f668d01ecaa795d8f622eb775135d7e97f63e0692220cb4e1160f4a4f115225ca20605df5b299cd1d746d583cc8fc5245936102c22d06861cdac802a10ee07f4464881c4900c327e64df6ab533a43b70ec96147bc9b0c671a4cab887f949b44b52af63b04cceff89deebe9f788178d8472715e346d503837298e7e4256ea3c5dfa3270f1c67260c4a0ffefaf802b220591511f06e430fbbcc3a9cdd9c714c9285085a57b4ba5fc59135cd274d89214b88639eb76d8f0827c1ac74947f3486f0414a70cf60cb6ca693bcd5b34a73486cbad0fe2ec636096c4c62fcada2289e13c34ffb514fa646806fc4131dd2d968e54f3bad2a0e060db26e60ca99d95b42b7c3c4a33db22bd2bcc1c111e08d7c46e1fe04ae3e8d1e0f228993cc158b447af4c60280493037db98c7a95f1ae566a215044d816aeff50a2101dc4cd13637d410e598d5452fb100090fa01efa04c01f288b25ad180d7609acc3f67b42a9481003c491207a1a889e014ed040d7fd77eea45974469e074b03064f2098fc7b7f7304470630086f8ec3ac94c1ab9bd9a82a7f72d65c37b01e0b1ecc10f76c3f348ad2d3143235f0a2174d14ad8241982e951e94e80c11ab530874c8cf78b87b38c1c9f6fb0f7527de24c9cf72c6a4fdde7bb6bb01caaefb8207f43e25001f414e5b4ac96f8ec15c69b83a2a4c145e3268c2103e80131c825b007c18738aa7c31290a345f80175f0570060f2ef9b8464cb32289dd42f0911d10a534cbefc9adc5d9b383b87130e7f0ff8a78899aaf2e52382ed4eaba6e132890cbecf0c76f04d6bb7b674a3953656b228e0ffe07c09f0ea3c3ba2c1790334f0051fc98897ca6ce47d3ea9334a12b7d4afe1e2e74c343bfc71321e141383cbe10acba357c5b3372a7482a80695b5987ff985017ecd343b33a546365a53e09577005bd1e4d0e29da17ccdd2cc87733344d8a89a897b104e63755c12448d8323e4071f6992936f61ff9a351b5f0fe4da593114477307232a685de382289ebcf18f0aba2563e826275831dbdb81e4448abc96e6efd56dc43f67bc38bce297a3cf4507dc75b8166c872ffbec1b73c70cee93d9e48d059c431a74aacbd41d224919270d496048e0c9cade705315910b0be09c0f06d2c406812c55ee1f013d5dbb659108ab4fd5086da22f12c87d1b216c087b7515dfda0ef0893150f501528c284fe60713659b4c238ee8798314f8cb46ce5a83c0682d0ad85b9de396f339f6888aa805e3c3101c1f6cc982c53854418aa03117e8feb6f8e62ce509bc57c561443a4109507c0b65ef425021c4983d26eb9b7c1df651dcbac09cc7b7222d38d4c3f38a04771ab7d41b2b0ea9ca2a25b0bd5f630c83709ec8f65169e0f4234bb5bcd9050a51e89d943b8a94facb0ef47e13b89e28213d09e1fc82ef8d634749f8ce60a5026b55db7d9ae29ba1e573be501e9c6ba113e6f777217502c4205e35fc4c36ed0b64cf2d0cde522f400510ee33730c362b893227a27307272348ff01f12059b2066c46adce7178a2b5b501e47f35405cb4f7b3352a73c90c29972dcb0eac82fd06cc3aa8ceb261186ebc8df619ccdf72d48f7fbd57e49faf6ca9b37d9b71efb305478e1bf1a2bc853a90be93a7ed66eb71583106cf0223d44e814d85f23178e3039b664c5b2077c1b2a92b09edc153b5159b632b895f177b3933f907e903c1810732db925c1356a7bb99287b305d273aba5bd9931d8b32c48b62d4cf482bdf3b0d1b8252e49154c4817938e73567ab5099f658cfc4c84e8e4bc42496ddd7c9490c1536b4938c3e4a84302301a1d79941466d4b800114f0464dfb4cb309f4705003ace3033b9bc247c92a062eab746e4d439e20f1b668ce568400fd06ddd6f38e07799bebb719cb7e277276c1e9d1ea63e5f1ecc4c2e245922fe8e8415e78c16f081c060e4b718ad35dd0778d03f2fc067a30c0cdc73cb250508897be83d7590d063ea36e32b366f2832579eec83803f0bcf2c59d63d872c5d295152d500f14390d5148197ebff9aaac3e6203516e5eef74e5b02ac41b7630042fac87a90b18ff0d64eebeec9cb38221d72acf81e23460c1da849d321e8d749b45cf242f8100780c89087d1a3f41ae6b3a7c5f43ff51d9a10f3be80906b868db51792822df44ca0e274602df1a74a93606df1094c6bb9664ccc637badafd420b2b4a6508fd1a55030f27fb5a5c3dde04f2714b7be28e33850ceaf6b556570f69ab52f119bd344e0832b6958f0c14e0308574c4e0f8d0bf18dceee90615b21e43cadf7169b04d1505eecca306fde36dc309247bfbe67c44702eecfddca83dae335f7cf6672437940a7126eb05f1408dbc23c80b8262ec38453970bc64160a859156ec73fd8ec0a004d2010370a2fbb3e87f54de0198f5b897f83d74124f419555d4385dbd07dab70f606d6d0f7b65e8afba2c943aed1b1eb7dc4664ee16b84191a07c0b1322cade064e9dd86172e21bcd89d72d8fcdafcd9a10708f0ab735eccd883c237918cd890c6d34741571987d61e01ad17438257927592607260ca991436cfde1d970025a05673db25e6d6dd96be7d5c05db86c3fb9ffc0dc4ef972d56dea72435a2f76a003cace49e1b66c67095d42b3e9aa174dc27ee474b0da9f343650c9130d84813f0bbbd1a6d33210df7b6d39e7e50daab08b6236adb7cf7afed9ae024a3453c21001c938b07e0b4f22c966ee9204b3363e8f59a19986317a411ab7608b825dfe6606f72d21269013c4500a92e15390df3c7a25b8c2b9c21c86aa9cb3c29b58563d0c16ee1c3e57be466d2b2cadecb41cab6825300dda2bc21a790a4a5a95e4d49b01cc64b01070ffa3d921e93ebf7e9d36693fe90a6919b0c97bc6ce158e06c2b9041b6c8f3b76dfa0782b9d3eb6c046dd8b7c98549e232dd0183ce69f328d614905f24bdb33ff33af3976b19cc38125a721f9d9891118dfcce04bb6ea50d10dc40898f045614d1378996448ead574df21d737f5ebbd6c4994e915383e96d4a106044cf57f5973b27203fb138c525d94b389d96e5ae041acd6b4a6126667c531ba06b2dfdaf8da9fc9060ae76b79936027f0bb533be0bd3a769c87d8f97fac70540327f99471ab01adc44331e3423bc58a3e7efb5fb831b32217dd81c8b5f1b3a70c3be7ac0779e38ab9e69f9735121a5488b510f36169ef57dbee611bac7c91c700b5eed4d6c9ddf4c86c86c78d9d11ee537b5e762f03f8674c6665e4e261016309684a9fb50f64704f530f4c7d1c56fffc8dad135812f931f8c8812a0da101e704cae2ae42add0fe3954ce15534cc4d4a4a41bf3ba678e562648be2042bc752a01c97fb68f76c311539a61277cab126f11393cffa9cdea89c86faabbe7f7a7330831879175a12eef2945ffeacdb33bd9e2cdb6b03acba942881e8e00fa9c46fe0bc6d37a82442d1938ad57a2c889d0f1b4cd8147ac9b4c22bdb49a0eb56ec1a40cd608726c9ba99f5e9c96c89c42f5e617ab5c95e0dbcbfa66d83bdcc23e3416029b0e4fbb70aeb54e8b0ff14221073d25c70b6c3b9ed6649cb60fd8cac4f3e50888e724fa38b852cc3fbbca03733e69dc170e0aaa349264024cbafd1b40766302a36a36521a5c21fcc69a729b11d066dd729b85362882a77ccb901cf5dfa3c501de7642b02e13c7ee75b593e3256ea079e41d441f1b980ec3ce44764cb737c001e63bf2e4d0565e5afa488abeb7514184659dd201297299088be6b98fcd4f840f82d91f82d4665ea26754d13ed1e1b9d68e324f1b1d5e9c7bb0557207fef72c06d9f2458c110fa90364d818730967cb47b7fce754137751b99705cb8be1de8a05b1dab0a1f5cd6e98c7e105ad9d184fff34b17abd1cc7211515f90248ad4add0831dfde2779b135d636c52de5ddc47d237a21a391659e56509a4af0ad97bd880dbc284fc3b5d6864d38d500b659f743644121cd311e02fcb7d1cab547d9acd40d5ea960e3fb1090f157ae5dd728b569e5c7a4791d929a612a91eea80b09b85f05a7a0ceb3a7f84ee6d613d12956f26b392ee8f5483dd63dafbd340d39b29bad09296ca46787d11735d6d55702adef45fc9f1849500c9c00375413c549b3401400dd527e7deaf95bf9723a79699ed949b5188cf13127fa5a7304c70d5645ac8d02192a12f7bc261a4b339d1aaa648332a6943ee5b7213b4f436a534fa1fbf5c109bee686e58ed508542b29753aaff6ffdd1f657dfcb06b2114d2c3ad890ca6010ac12417457a49d12f10f847401250f2a9ce10505e30b2cf4a69c996bc2399de0b3abc7273b9b30cfab4cafb867072dd9138c8a5eb739b4ef47c2f831db224ad0f55ff7e9df4ee6553bc6eae1b5a4b05a26dac75f8c93f4b02a8d5e5b50b6a5f069cf691a9b0f7a038238b9c65e883927d4c1f7e6d724285f64d2e2e6c6f0a730d10e4411a5d148dcae1347fef3954ca5cd574c47fe4feb88bd9c89ecf15e07903627cb8bdfb1232f660beb0855f4f2e24bfc21826a3dd437bd68e14414a1b5100e23eb9b1df67de709988d74455381827beca9fe704f7f4efc9b4f627eeb01902564d6de66dea14c8a153d992b63d3ac47b58f00502124326c978f23a95afc00f383fd7a5310f810de2574e0b504f800fba375f0272320d99e949ece3dba456006ad82049eeed08caa969f5f3bd9996f31d70a437cb3dc61b4b024afc88ca640b8c14c29cc2bd455d9593eb5bfafdfb8b64e0b958aacd420c64b07cc022d8d00057b9d7320c35f191154277370db342b1b2e64b0b183193f39ea534e5a355320e7cd21682474e5531956662e7c590cfb06fa83282e110ccd688d9f4fad12a920778d19dffd022318b1ad4685570ca7e7db4f10d0149e73c3b733b8443634c2e04622428288983b52d587d4edf85fd1ac22eabfd25c376375ca32d21e00c32bf059501eb8a063e12bd1a2639adbc68ef1ab98243d1c616ecf514751453f234de0117a1f0dd65d4c3cf00f37161729b3c14aea2f9b8db7bcdf39fece1db5ff27a098ac239f59f4ed6334b676dc9255e73708b5e0a61a54e01f6e5bcbda9df99d109f534942b6c6f43b1c859b9750f7018730da2d2281ead01f0047fe3a0f1df5802613bebf4a6a3de145361460a8611fc821ba897b30604a700fb1703bf8238b90600aeef55e6c397bf49d50457462583ab0b18aa25cc79dad37ab73bc564f94003869a4573c26aa22f79b2293189089a0a2f5ddcd4bf3def17aa9badfbe28de10f33e4fd1ab609cbbe27282ceb040dae507decf82f2f6743eb2542bbf3486f07036c3a8441c96c76b2a1226ed5763a9c84a1f0dc3dec3bf6183c619cbcf49c930b4b367787d1bd90f471c2616bb5fe17b82883b72981f86d8da65ce383e22cfc0a5f14189fda308eb8df2095c1da9375830bc8e7542ff299ac43a27ae251727e0ebf5e1e35301dd5c93648fb3b28e623fb78b093671dbbb227527bc0c03fc74904527d8abd50fdf88669cdedbe0a407ae36d5ca25f2763b1fcfd4c4202683d230226ff737379c9c4e8de09b57bcaa4e9e4a456d66a91b7ffb12fe251c73df52e02d581443c4a298b64d0e7e7d0956c874caaab628c7a06269e22c37ee23b21e904584bf78b5ab5596d9878bc164e1b820f61d524624b61ddc7eae9cc03509d37089331f5de4320169a14c366671ce4b3c8f8c60426b12f0d2eda6871f749cab50615ff8b71bed09e9168bb4faef4901da92314aaa71887ab1dc76229c26f8d5f0fdd6885d397f5abf88e778070596fefab1f264d2f5d9acc666c8f06e6aa54090b -generate_ring_signature 6a7c21315ea9a0794f13bcb92ebfe303e1b0713cbd6bfacb529134a6ed01585d c86de54a568c92e9d6a2be843389eea6397c740fb17949b70fb10a9a58842e2f 53 a4eab03193f38a2e0e982a87120a234f90b0bc7944f51cac35a25176fd58b777 d23b91b0325fe014f6e53b9fe832d97042b4aaa25cc5f1247658419c48a8bd49 1bbb20d6fae0893cf5485c19db5e675d999a0e9dba0e0718801dd3f2707793f3 2b515426826b80e919c7b7acf9616b6dd5572e0d5dba19bf9e4da51c00335487 7c9eefaa09435f31121d5e355e4fa554668f4837a61973d30dab74d464cde65b 37a99f9bd561f0192816187d019987c0ce5f147d4c418bc66f1a5cdfa0e883bb 8638bfc48383464f756c3664e864d326bca7855cd4f8741e29c74dba9cf4847a d67f42eaf5b97140183b520d329b27d245e46cd7bd00c2a171ffcf3cd8a0c9e0 4e7fb336b76b0304d312f2d75b8bf75834b7398db6526008a74874e4e56e6aac da1afcb4fc99dbd35c26712046956d72c6ae8500c3f916b34d670e6c9daf89c3 3d982fabf70524a3fbad979afbbcfb2d0bbf8c2aff2305bdec06e5c79d98b47a a5f8ac98cba7822b50a7073a987e71de73ab843465b744a3435b4bd005522a94 08ac7bba764eecbd335e48c24dd1c04c69e0f2b24165b12a408901dc5da87043 6a9232cec3fa118d16cd669e6dab9a8ec07a0141b3d3b5c97619289400168432 cdec045aa9b95db4758e28ef5d3375c539019188ed4994d59d932c5147e7d84c 76b7e24b615b9c211c78a8b212655d2abd2ba6da10ced987e827163c9f8d80c3 48da2559b00cab37fbb05f10ebcacf51650976a020f045d7dc83024afe1b2f6d 4f16a0f0e1da96f7b885a88f5d203502d5bd717ec8b5836707b2da85accb05c1 80d6ff504ab0531ce476105fc6c6ab4cd39e75f9ba0bd781b7f6e9ff483f71dd a063b2575fc0ed77466d817dadeb051e1264af664b3baf2fab0b3bf3c7b5cbec 69d8a67beb3f99a7844a980df676962f97b9971a1b084a5220bd830cf279e181 33acd6c839c0c7f438189e1f67a8313be209413fbc7b0b39d1265a6d757f879f 97d4d0006c8c7deb27813cb1235e7c914a20da278801c2f3697b462071114276 f006a40b43795409e5c5254e5486c9522ee2fd1a338ed26e9b40ec4e1c29cd52 eb6c0ccb68a8bcf2c2f322652f6e0635d46e214f0bc0961e6ee431d9a6e70e6d 10a6835fabd12b737505f67c3194489ad1f67e2f75982d50c5eccfac7661c050 2743f8484a17f4b8ab423d9a06e648638f50037a20b453f91da821bb8dd98737 ef6cbc0d2dfe502227ce198c737f0bf0f7a4931444bb8ddbbd82cc317449dade 574b33bf68724f9217faccbc97a60e2e6a7497582224aabd262dadf54a73453b 1996d428c975b3ebc9504f329e76d724f2716fd2d6b09436f0241d55525dfcf4 4aa00e19bbb3aa17adead7dc76b7c018a9afc20b424fd11b53514193ebb48568 346d12690e9ec83837d4f402b0fccf731d999b38cc66fa1bb8be1e46398b04d5 a819b6df989fdff76ab04fdd6cdcadfa2751c54e14c359b6dc63f285bb545ae6 8cae8ebd0995b16248b6f1e3c7c61d9f0de44b582ba27f155e1f315b81c0af60 0e6ab1fadc1ce2f6473481649a3a500a86e4aa00a042d0257506022a7abd0965 e2adb81732a12b14a36d92d5ac877c7eb23b4d365a31f6340ee0e85e3044cd83 557afb592dc585c70ad082f601b97ea5d53acc1e8c1cf2acbf73cb2e4ec8ed20 6e2edee22b8a26501186cf35808472bd9c5343c3763d921322398abd5b524e4b 321503c0c52728139ef0c745f510897978d1805ec1366245f14c12472c8f7926 1e94842bf9529a263ac929ac90835961315297f2d01d83fe9212cd0c5d86f662 f278b17bbe1236d3066be0bd4befb0e914a524f85bc146c117e05db1abc54126 d62acf5d9572433e91cfff5d9d319723e0d49dab4a3ff4a7a2c27597ef4171f9 1cb00476f95508a5c4b27c7de7ae82386bfed926cb335dc6bc934b21460abd7f 32855938be63eaf809c44abd425f69aa8699cbf7f8ddb2a94817692574f85b31 6d16cd460eef86fdd3a4ca2642866ce01e7f09d9c7e3158ab61fb4fe7b1f1866 1382892cc72eaad23a9d62e3dcddc3c8e093177fd99076db600ecfed64b60977 d7757e0cabd39e25dbe09943b20145c8ebe5710004c53ef8355b9e72027f6cca dbcccfcb3708dc00901fda1044bba7d6528a966dbbd4c7fdaad104817a37428d 42296a58204e10349dc6b9e58625ed27b7a0fc30d56f275e5c492c78faf3005c f40a1bd0bf96be276a9a65ce584aa7be335b762ac9adef807c31630587ec85f6 4e4e6efaa2112f4a9f3e2baadd787b09546ae6090f39bb2c2c701ea2bdd8a36c f74d02271c39444a3d2742e7e4015fe3f3a7f3abd96e4c04d719ab0324352638 1f89028e20b6bbd28eb46ce5a94886c0fd79ed9142147991750b7ea512c03be5 fdfa920e8a966735aa26527da416a60a777c4e9977d85417cadbf32400d27204 12 c01d6cb418427af7afbb792ca644ce9336ce7ac9c4dea4ad525fd431f6ac1a0e0113d2842ecd445c64732e197f34fe02cf5d0ac0cee9d6daf61afebf9e9f9507310e99cce3b30e0794615d60650e38327559e0f0139605638b763e3af85e1e05414bbed64c828745396aed05e5b93dd677f28533d3c1cc6d6c8be3e65fd1c3070a3fba978c2d59228db763f6fac16d48d3595f558240504d0388a45b9246260e8a8aba6a4f1947bc8182d80de663c963d8327eb994d2f7624ed3822d5be02b0a430e2ad697f48adfde90d8d5479bdabb5c90fa822ff795dbbb3ba622c6824c002c2204522c94f051c86c6cbb7da443e17e6d04e4337119536df847431c036d0ce7a2f9a1589fdea991f81aef7775593ca0add81c742b86459ec10eac3b745a05a23c6459c51f742c9da61b1d749fca0e1a9b470a9ce2b935afb1ef6eddf1ec062ae7fa3ef5ea5d8764e71351832cb2dff6b0309a0ff2891f02bc679b5db4d00d9188a02d131b2e254290668ad6675773b6d871664cbcb00d3384b3c62addc80c40f6f12116e3b3e84fb7ab4734bc5b9e09b64fb0c32b4a14315916c4e8b12600fb762eb6f2f19a068b2671a2cc968a4b24e161057f587bf24ae8ab1f2a4f39072c615bed124c101791679d5612186fddd858086ad9fafda3debdd230910fdc06f89cf810d85c410b6324c8a629740204925451ee69cc81f786584d3c79bede03c0790bb97e8073aa201162b0de9cc455b95c5c0cca7172331008ae60020ea50c0349b999d930e78cbbcafd0657f57cef724b7de63e70459e4921ffe76678040d92ea7e0d1817eee31d2de0b67d3c204fe36bc9dd61cc0a1071fff0ce798d77077e464e4d035e8fa5882738514f6c87d70f6ff23da373c648b5bee37d773fd30cef270ad09ccaa28be0b59d26d582771cbb5f882b6984ea43694e326157642f09e7fdf0fadd20b1d61036b2f107fe9422cfc8eae53a3570c44f8b330671002903cf6865ce3a8f024dfe7f54a1899cda525102b1de333df7aa9b35884a922e0a01dd7954c0bd55ddafc88cff982caeddb497c4f2d3dd5e366a6dad8c0d5984ff0eda628268fa150b00bff73a7fdea43ebf1f538ea7f0440aab77e2713ca8497e092f179ad3269f34b64f09c5ef8254dffc52a7aa74b2542c17c55dea4cc6f8e90e722151ba3a01d87aa07965dc2001e4f295f157e789d419c4886b1edb4c5ce50ef3f945adfbe7f3fd79d8f74211e54ad81bd454234b22ea6897f42d8a71a40108b2374a6efc028e50205df6fff4da404325c405634db665c4929b9e0f494a1306f2f715e84b97930a122d9efddfeab90b1fc40bfce7a0780d275c518a05dc0902ef96462e4b42c077b4aaadcd9355046db9a6c167d498f83712eeefed6319520e0f58e4d294e0e5622f2976c0f6d5e16d2ec459aa79dd471ae50cb27cd0901f0880ece376a18904364a24e81434987de9bc6dc1fabf9d1036fc8a2bebf59aff02cfb6aa21723405672174a1f79b4d92f1b38d603ab693e9224e25aec51bf26803c2468916dfb2e2c1a0dd5f8a9aec9ac6d0aa489f75f5963fa4506f914fc8800b36028bcc74303c8c11b349dc226ec2d744c4463daaa4d584320e15cfae83210147cf23fcb2f60280cc39a925d6b6bd26c8fd35be3c67a74761e183fac274aa0a39390ffef2a28ba4f9c2f27fc8eff5a1eb54f190b5aac51e94f58a82673901036f0ab01d187e4ddbabd9f2c52aa5739cecc097441a31e554debfc76da0d68400e2b130db60720102893afedf7213399e3d7286819b35f0bf962dea2c68794100bb4ec91f5b8d97bfecb2e2069d97ef6e0298889f14f128ae4ea90df45c2c060ea136b8def05336f47b436833e8b7332dd265f8a28bbd3c668720e6776e61340fe992c3440dc79da73362ec6fff8ec929db6bac08aab2555d36a30b2143496506787ab770bbcafea940f1c5c983bfb2a1fddfdc6a145fd24af10bb15f0f99fa0342f35db2a1847f49c2312cb4281c6e053f437e17b542b5e9839267693a5975033fe38b7779f1d34ac6e963f85d4d168319c47873d317751ec3c597744104c907e762d5ebe2be694416e1c76272c4fc720eb6b5247ffc509f65ed8830cfcf840cfed01f47c32d789da505af45da3bb64d9961aec551073e2e1c9b796c4adc8f04ab77acc1b730352b082334dfe8056e3b8386d0ea417733b747fa43f1f20aa80b5d94b10c83c3d10a82c574f4501cd2d31b95bebac61f9ee98d7a16c5a0706f089210f001c9a830529a44d8af62d394aa7ead10b9674e0a0f9ea7eebd3591ca019b71e0af91a81b2077e4d32bdbf907c39b13aff0196632eca310a20c4dace403dd4a1ad80d97386a5ee8c2b5ce1976ea38e121b1581ff27d7b6a4ef2bff2da0e1416de8a1daa6641d3f7b940751c2b0bc93c3367a3794a5a118d74eec9ab560c466d45b3f5b51f024703d94982c4e425a12d0d811fa171ef5ed37438867fe600d247fc08def60ca1d4e0a096b2908dd99347b4e58c495ac64dbaaa02167c620ba677880772c02da8ee257bf79c683a7101049d5dd894952a98ea0550e34f9a0532972c484f7a9f0379e6461de3719fdddaba2e3892afebf1f755c2d0a077110be04be1cb0d6823d848a140455c142b7eb98649d35fcaa10a764175377b96b102717959814ac071cfe1549100651543e39f4c72855b21f41bcb37916521e8b50d9727cba4330223b16585fc0138db67a2277ef8c182cee0b4afda3fa4af67950ebcf5d7584d45d546c13d07455b9a16dc347504888312aa67d55218c9b8f8a30a18548390de92893fb7d89b3ef0ab95cbd0bedb5fc8a9da03b9785d1578b1d0048ed384c18e7d1e3163d3702ae21f81041ca25c83b000313d557e0e8f57c1bb09a5e9a499056638f46a87edcd2b97131fbad44e39406f83e4964e996a0cef630a94cce99b8f0bd7063712ab23f40d019303485bf63b4f6d8adc54fec4614de2029ecca1fef6dec10256e65d99b9c2d30c84bd16925ac65e8199182c135c757508a8a0025b03a81cf3cfd2575d55e5eb7de974f59d1b87d66434a27661663f3d0009f70b97ae71e9f0b41023e2da6ef1de1d38d7294fe004e3198d8f63057ea40f096d37395154c8494018163039a23b060cb25afc2c385dcd309a82382653a50a42f05add0c18743f412de3a50182dd97cab071deaa2db3cd2f1796c9ab9d1b0bc70c620c7d2f1bab4052c50086d75afd753969155c66ec867463fbb7c12a5106c9e9d36074434e8cff3748712963119648314b33bec178e0494ebfc8152bff0855d1c7f377b6f0f827b4249f56a775fe5ec1cfe898b39ca6e75eb0cba7b52002d97e937fed205e2311e2983c35a97250a5cb0fdff19f261d44b7ebdfabfe600b5fab6abc7f93b79940172048046f20d09a8362168cddc062ab1c57d700afb20dff95aae285c494fd3f7fbdd0787743c9802c59996ba27320d78cbb3517ecb00f8754b3668a456847fdec90f137fce970c76ea2df4cef926f9eed02ac70c6880ed8e67cadd561897d53a0058576d6564e9c8085be6a9172e1f911bfcca39238024ffa59fbaea72840c0784add45bf9c2a8eb4038612552a2447e8e5fecf0cd108b3b62705c5a8be80fa4444589be4faf2e878b1abdc996061990db1a89b485c0c4f73ee2a4ce3c91c1595e423f5c55d6233e0f0d6a1f88f7efc04076fdf97fe0929d649a893004739be54bf5617617eae6808ea1a5a8af0b136a3e8e8771b2200d567067307dbc28e0c71d5e96b7c82a1288ab7c91edbd96eb753dc60082e2d076da509bdc935ad5ba97c4e553fa6c2bfa1e3561663080d482a74a6039290350c098d2b874d1b5366c980627cbbe6a3dc07bcc630363336dec6dfd65f4a9a5c0d559bc99afa62d8522ea2aebda7863a98d6107287c7ea0cf87a50d5cee441f30a1a07aeabb6bfb197fd169cd93e0e8e147cd01a31c5a6eb74ce425e38dd6a2f063112d18d5b8fabcc3fb101230903a52e7ae40de2b3cf4d9eb0c17d3e3098680ce81cd33c356509b8b9e93cfde1c3aac7d24f6f6415ab56f822eb0da7e4bc9f0f1ef32149f5b664fb4a16aeff554d3a15e5f2084720abd5e0b562a1cba24c3a0ea36d8bdd0174f354970c2a53154b8ad404c770c9e8595a4cf6dc87f869c5eb0d56ce145dac923b980336ebfd808361eeec862cd119b85c4702345c4c78e3210ee67e58cb1eeb0a2a9bea6080687d72f2a546c576ee392682bd2d6c236876c30d3645a160c7a8c7416bb3229aaff0b79cfae866e84b70660a42e38d6af85f47046a3a4268a5d7063d1cb14301596ee8d56b4fb73ab5a1174597f2c483dd1d81073ab0a4b26c681fa20886475454e769ad06fa8bae0751d1a45ece79268bac2b0a1cb54bfb1f38cadd93f55eb5e5abe7a4d49fe33f1989618376e81a70c54c3e026eb7948bf45b8e3faf7e086017413d024a8ddc448181579be75f4cc89a87aa05f14c93c79e9b3172f444b47d6020f0b13a68afce3245bf138e336a074a130509bbbcc64b51f0ad467f8e16c10b2966de1ea54442291f044ac68e2a8355f7440ddded1ba83b1816a571129b36d3faef75348c36fdebacddf9799502da63d8410f2dc5e4456f82fb5aab1b11f2756b4593002caba548be71ff41ec73ce9d754f0aefbc1a4753efa967876e854f38dee2c29c84e42ada384898bc13065c4ac98200daecd16e8cc1d2388fbfb5ae0143af42906d9edcda4df0e1e6cac95b29726902ed97d94694a47a56725c5fb188fe93fd8fe3e2884333822df5d34e7a8d040002 -generate_ring_signature a4f99b4aebecdbfa9f6c5fd9d62aaf72bf17ba6b85b2b86f6857d4eb5aa4e2a4 903b964eaedb5d9df0b0b34f0b20d58cabf1279e7254300089735c55779f6d06 2 6312750888bfa7ded17884f70275bf7e07062a1df46e580cac091363ce7030b2 155806e2d34207010d5b4149cff7eb191ccd3d38dd5a1cfd9dcaf19891878dfa 04ffca61416f5b7b5d918e456a81e4f50a9d4bbae583602f8544be5a74c35006 1 bfbaa60c508de15b95f4cdde7ce7c26458daee388d5945558b86a7ca867c680a272ed6496af299cb4b93f6b9c82b3c7beca3795ae311cabea4df69dbc7dde40c74baaf2c10c7c20fa0c15439a7ea90c3abd8c01cea7e93a8afdee69d5f29af07a11665c96886833359fc8f94591e6d34e30024dc3fcf2d339d80743380a5190b -generate_ring_signature bdc62a6efca6c735edea0f7c692ebf61244271d37dcfbd89d538eec558460cef 615390da874162142d31fe5efe83e353bb9a35a06e861b6fa96012afb6606e53 3 f0927d23a105b3550d3aecfa2c3115c5730636fb068080dab5f9e2a91455fad5 c7a262ec1e963a0c885a967fea2e3da4374e2400ba9645db84ea1c785d13d07c 38a3d5ff005353ffd24da4db56e22636e1bd2c0182cf4e08a84cb50af7752351 a31a2f30ef28f312d39d35032878250a53ebf5c77325eba642299bf98b567d01 2 6ee9d0b249b5b94f7ba5386935680ad8e728239d5a9ca1613383aeaf3dd70b0e0dd3c689ca8e739389f34368c86b2c4ad67f3f7dc6ef53bbcb22a9896cff720ac933b923c5d470746620f00cbd9e6178e99e64336211d0595a0a38d9a3c04a0763237916002bd2963bd50ffb5fb46dd2fb04b4032026ea967f0edb11fa77760eba5a42d1bc2cb2a8d13c09e409286f48e10adc629d7811bd76fd2e3fe5c7a60d9f50db398d27d4fad2106c63a5c00693c91c397057815969d04f8c18ab362201 -generate_ring_signature eab1439501745d1a9b34fcaddb7a42e8d09aac0a4070e49c193b7eabe02f3e72 6ce8208563bcd24b2582f5c8686df7670c4046c6639d9948bd844d83c79e6813 1 df7698d71575f97f8c3699da593b61ed18f032140799e2e62c7a14e191ac49f3 004c41b8110c26fb97c3ceee44a7423e7ff0fdb3e7911af60bc4a03f2efd5f04 0 830b019484f33343ea13032817f2d514cc55977ae1125cb5cbeb7abccc7426029d3d734fd40ae517597ec923e5d3d0ff2c7a6710dd64c280950d9f14eb7b0d0a -generate_ring_signature 50d6c84602d35a33a30fd22db0d5cafff513d3947624c0185dbea80caee90c5b f686926e40d3a434315e5f4ec24c8282275b89773f52a4ed817025943ff7f022 25 9a30a185b0624525d23c91ea4f28bb3721a797a790c5cec3a7028bbb8e191027 8f4f792489a3796f4c9ae40b0e7fc91a7de29072db0dc09d3c5890a294d01a8c e12506e001bc9c5bafe5301f3df40bd27a974ae162d1446fcdb268a2406193e9 e250ded73cc3dbf89e920217cfca4c614d4eafb5a40c6e36bddc349a4c686597 b8b0831d43d7e2af8aa0d607b3ac194c33dfdf7d3efe78edf3cf6546011810a0 76423bd48cf42c31fc156e3091f20982c6bdc3db8e792893528279ecf06384c1 b7cc082b2637338f2530bb5340ae7c4fee3a2143bd2453970ebaaef83c997592 f85494d2c98b7f25681f18bc32565d0039015aa5266f65dd232dd731b09b4cbf 3ee9148bb2f4dbceb9c0869f3c4e7a5245e6e549f214bcfe4d91f78fcb15d6ea 1b250b0f1e2114b6cf2d6c7fdd4c7b9da1f7bf721172be560e2fcefdfd4813db 9d1b3233a32073ce52df7d1a307de6b836582afbf0474351e7f0a2b2a7a9213c 83c98a063202a587794fa6d5b0fa0d3c18a21b81a43cbb366d26a91735117f86 537697e77585c2097b51341c2811cda8fa3550cfbc44e529f0a63d63ed9290e7 5da85646a0b264efb575a635de1ef2c955a765dd336351e1b2ec3994d2c04e5e 832efcb83d53c171206dc4cfac510358bba39225f385a551897c4139e5eab6cf c6c37d8fa87c45ddd9c6d9f1b770c6faf56f903b59afaa8803ee7fc6ba5a3e14 6389ab8261b9771d991039e4aae4bcf58fa807749905593d1479a23dcf6d2e3d b6fc1558d3db2bd362d2aca176659835a3d7f34d5ee2c6da1209bb2355b4e954 91c3a0980db59fccc7a90a69af24680af4bdc0d17e0a72fa7fa5da59bfa997c0 e6f8b0e3e397895d57639d5aadc9881aaa3d8ee04d31fc68d2f5b9e411683075 0a4503f9c48b731d1dd8b2280a575c15e5fef2ecaca934969cffc5cd115dbf26 1327c1f73ffb2ac17233d79624a17b1aac6b645f16f2407de8a92077c19a3c7b 6dccf4059e35ab49f66e199315e763efe2815a503eab8bcf44f4d25534f9d200 f947e8966541a52c0835c2a064c2cb71bbc652dbcdb1dbd3314fcc8e75a01f3b ae46f670527d4c0d9f04e0a4ab67d23a4e4089107c3250ef62c9a43a27c1c4db ab8a1a9091fa8089477cb325d13df16b35fceccaea164250309204415733760c 21 5fdea718228d75b5e66698253fefbe06a21cf77a8d999febe1ec272a39f50604cb5e8bb8b3dc1d0cacd3b0b078af9ba2c9fd9fac216595ba95e81724c751e2054bbe08dac2124d0d25861bbe1c65f4d100a3e8663fe9f208a0adbda3838c3b0dd2cdf5550ebb34d3a7806555b27ac2e0bed1c36811c57cd89b7a9d0553dcda08a2962631c8414787640ead8e5006b6bb83866a5e73246010821e88fc74619d0c901e2ae34feda9fcbbb260338262417a1d7b001c430150cdd8f6c015a7cfb2053fb70bdda784a0d7fd7040db53da9cd36bc96d9a94d863527c07fc7eeab47e01518b798c5f8bb78367db7c4d2908966927c3aecfaa1660d67af9327e1c41b1095251c81f68fa2fe0e61f08f467b2ed8344b9ae5d16859bd6e615ea5878fe7b0d77cf395da8e29e19ba66eb25c023d52a9dc9b78e61d514448bb3785b911e9a0c2e3ee7ac7b67213b5eff5a37d8e9b63dac61cab79f20e1389dd4718bd0401c0f0cec97c762aefaa2d49d9caaf5e4b7ef83a4aed937999a2ad03528fe09d7d40f269718c3a6694b09924c625d5b9e3d147a015354501cc2f45e6f06d7184f8e00e4acf989a487bb03dbbb16d42fe0136d83487dfc0d0c621b089065c4e90d9f0d4108d39a0bf1e47758f66794e082513659bf1900017a57b86bcde486aaef8908dffacde7e2ad32da5522d890d24af73891807384a86b4db998e88b9b5d6bc905360cc28925822ad4ef2334197906b7d193c5a2fb4b69d7346928549b9b2258042cbc55210aff59348f27085a662820b9b7980c5e8b3ed4d40e612365e10aee0fc4f9142ce3cd28539fa9e2335b51acd48e0ae3127d98c1cebba86904574e61014d5d532c5b3cf2bfc7ea62a0edf8acd8b0aec02b0b6e0f2db2970c1be8e6ad0f39b6e148eaff96b1856e07f21c34f0b24dd5d180d8db45ede2de379fc5f5f20153d1922b8160ce7e3fcc712ab29fec2b1d16d52d49e4f0087b269ed6fe2200052921ac391ea3c4473d013c0ec1530f8b58cd8ca2319867eb8e60b735d597100c4096aff355ff4734e706a6c1622de70d8bccf2e7e6b7ebc73d7ae1af8d4ee403dbc1b9cee5aa61a34a0d170c7b633a008f8699c724388262712e7ad4a68b930e480d180cbd155692259381018692f44e9e3bb3edf9fcce8e459c062335f0af0edad13ae2094d5fa21b0aa5cc45726d7baf324516835f67e463da6c6fddb5e2082e142acbc0356b05acf8bb0975c29d892b5be24f565025dc1bf689ef370c950f405342d53659741ca31c774ecacdd04c793f53134a1bda96bc49983de80164009445987f642bc3ca29a570538688aac1fa07842879fd639873af315bfcab22084d874c81df9ec42a8aa35795be4f56143a48b1991e4e410897c75571cbd42b06e1d1d36d04c9b2b9f816b45aa15e5891b86317c91fd78241fdbe4e775fa30b03bc7515fef9ebb893d078856a40e49453a896e6b55eb9481ff4ed581f5add6306530a57e55b626e003943cf5db795d86d5b6bff74255c288bca82672a2ea67505069b9c0b0ff3d8a5e007085bb684e61108598cdfa0f40875d5d80fb07a3eff032a0becba0f4a0badc2c444482a32d2859d990a891042ec236f6167cc2ccef10c0b6f4712b7321f743466ade2e993743fbeb7ff583e375d1017525c1240fdb10ba4812a32303bf0b3a20e4ec1edf625425b781a06da60ece291dec74b207a5806b920e5d86501737f1857d127e315c212c41363250356ce500d7e1cc0480b83033a5831cbb07c30cfffb3b927944f1f2e394b802e9b11684a0dd0e285d1ad3b0c07c72da2c4b365099bde76280fa7e62d6b4d57f655494ea38ca23e0d827b950b85637f95675da658a0df5c61f34544294d111ce4f4c933a4eed851f8eedd2c033397dc8a879611b8deb11e7908182079190ad05af11cf20916d312bc59d7190b76b0e6239b4b498f7e1c97445b0b9833be3042545fb96cd6eaa6b1113d635c0c68ec01f28b49fa837482abeceee98583112f94fea14379ccd269534168717c07c6ae408248f70139aa9b9c987be8068e790a899590e7fa444f4b0f8295e436018ecc0ea0305b90169dac5d29a23207ea78e7ebeb590cb537245f304ac15d4f00d93261cf7b8d57334a5f12a44c18abfe1ce8467b6360393dcab8ec29444b940b417861d0198f5e5e05475d629057ffc0341527ca766c9e120e83cfe5a8468a025a82399c7f77c6d76471dda84c4ae53644c05e5f72ebc3290da10f0388ceae05 -generate_ring_signature 24b6afb3f6d03575e6b84afb6fd3787b6554e649223fcc94f0c3f8aefabf9457 c34607c491db77c2a81d12dbf15e28e3fc50190e43560765d84b1d88a04d763a 5 37604db2c71da54aee4ffa062a3ce264f6f968bb22e195de69667a1f6d8346d7 92ff0f511601d6610f31f3a53c956265053e53a94a9b08e841da63cef7480fff bef1ec8aa444f5d1702357ccf31de57d9d08297471c8071ca27d3e9f30302f6b a0dc011ae5cdcdc8ea827b893a02feaa74556f6a50801ab1d5994f13ffffd133 77ce2aea1d830beb85788d5b9b9ea39a1aa0383807299c479547e16e7c633acd 1c5e52449305df865f8156ae662da8e69d334ee7d0c9ac835a359ce5ecb0eb05 4 e8dfe8292757ffd5bddb66f751489759c0ff150a629d4b8eda0aba4868e918034df2cb0969a882479c463e15b522eae4be19356236f870a91cfecd2c54109e09471635282024b84cb3feffad8d59b8781e63034e3c1928de4a75923640217f015fed4beff5303710480f50e905e1e371a99309b76b52dc581545c9744e54ce0daa63b476e52187faced15e357b711f1b5bac1dd6430c4304da3507506f926e00e8912d151a48e404191db06e10b112d43babd1585c57494f2a7e49087bd3400449ba829a3a443ec383241fdd9adcd6a83c78be44d3c55819bed2c5059be0460b2051a5eead3a5e0c7088d294c4f387dd57c42c20670bf2dbe4c47797dddbe205e7197be0c4300e7f229b030fff670de1db1106e870fc7a992d6d10584e506c0a0deb718e6d0dea6ac857c4f2ac6fca93d6bddf0cfaeaf8a7eeab77505b0b190a -generate_ring_signature c5629b2c40a6c6c737f5dce8411acc9c276e6f0a56e707f8e57d60a7071557d2 743c872c39ccccc663ca95a73a624559e470c89275f9451e99b943a12c917508 4 7675eb1b2c42c881247435df509b540e8af4b715b863f696cf8f1d7def9492bc 2225eb2d6559955c01988be31a3dcedf4cfd6eab26c1b894f0c89b6625bc207e 287edf6cc3280b1a0e57115d79b5b762b3dd404ed1749aee4cdd0f3850bec996 601df0bfe882a553240b1bec04f63aa6592171d4f1da9f9bfd6509f1f76b463e fa9e98f42c3d58380216f5c6aa0ddeadf5a8f7f7e94b0f5e8d68215838d3bf02 1 528ce4323c607b5aabdaf6c3ab2f51b4abbb6ce136863f278e219e93a760db0db49122c7d8c4d9ec7d26605c282f827c421f9324bcc94602e913eebc819cfa00523ca90adbce632efa800cffd47d9beec3162b7a1bc396c29b78f9ac38c3be06425b5afcab4c2c629e38175f4bd010dd20e9c77b0de8afe03af853591381fa081c81b20ef5dd06a943d37d9235ab291446cd2f6beeabb75d6fc2d817dceccc02488ca210e44e4b3c345a3988de2d053729d6723223aebe9f8fb2f48ff2589c04476f10a21fc6842535bb4f36a138b37f65424d9f3bb517180e4f16b2f60c85086590124dbb590c8f13f4af8b3a4e0f3391a9025c6b901423d32638f3f1cfce03 -generate_ring_signature 1a4197dee423f190acd42720cc120eacd6fdb3bf10b79234630b8858b6ca9767 b4267203b4ae75d442d1ff8149369c96273da7aa9eb82e40b905e1dd1637e96c 2 8dceed51a14035531d0438a6e2c16815792a3e4d76f0f7b34b5491e4da661fdd ae627eb1310b295996cba5c7958b1d0f0fe5bda5ebe6bfb1b4b75e8f82a46b10 0db7b3861e845b76870c76b143d4f559cf6449fdba4bc46075817caed034420e 1 f3bc0f0dc7cc65987c38cde66a6d9969cbe48f9a0e2c3559fcf2844656860f017eff136e9f00663aa190dd4dd055e093987a705850723f91c35c1d0d16505c09de23032e8cf9af9f70c05bc7c496a1f8d21313c0cce4f1c5c87ba01ffadc530f04884862db47a58c155cd790fcc1dae9344be6e88075a49a4c5ff4bee34c6e0a -generate_ring_signature aac5d207c0aa7557360adc8f06738e1881fe94b73f57368d845f5682f8096f4e 552caa3ef09555e0a216cf8a78b2e18271373231c4758eda7a270b2ddd71b92b 53 6fb3972559f40e71e36a3f1106a7866ee3ac12c0782692e78c10bca229640457 e883af1eeabbbb707cfe9f543956f5bacf9bbc169183279e901b91a1d2481e45 f1ffd5bf8f58e9e1474d1305a55939efef176dd58763423eb3dffa481e103c71 18e614da426c877dc72f4229ac2b4811fa69f56feeb194b73b1df699b14e79f7 a1ec96815246af7cd69ea904d218f724d15dbc0eb271235a4ff1be126925d4fa 344278387d740c1965901597ba56b536d71aa9354b209819bb4a0feeab3deca4 9ae49f5ae55a66e5b3a4b78e1c05756c13c7f7955805e984e8955bc79a7c8640 1c0fbea44d7ffe1a9f1145a760f6f42f7470e8e4037ba9cf4da94228b91d5380 57eb35ad73b6a99178453a88a91be2ce87e886f38b50167b7d121906883bb4d7 2a7b54e7aaad50e8c817096733116f69a22a72aeee7d23b9ea99a8a381015bf3 bedf676e37a4e4dd53b92a8a469184ddac922854327d98a02e5fc648232bac01 e46ca855f80c8e707cffbda4f0cb6ddc8875548aa8d30e1699facb2b1912ef43 89d6e8228d7a16faa1b37309835249ee56870557877394a039fac2d2dfaa27ef 2315f8280152ce6b1e5634e2419b22dd94aea973193b61d9296dd8bfd93b9ab2 12cc102744cfb58b2c388e52fa9c581c2f4ac84e34073a2a96376c6030880a62 f6974ceea92b921fd60f6cb02e275bfa7f500eaa682c915aec114068d6db88f5 138b0b7687520bc45d8bc3e6d9bfa314dcd1d8ad51c48c449afaaf7e7f056d71 e3cf7715e05664a5cd498f79055b41a5bc60b4b8827fc1fca083ee55822eebd5 4bfb5cd9dbfc3a1c41e59f9e1c00237a46819643fb967b5b92a80e7e386430c5 04e91cdeebb6eee9643337c58c719e44d401eec9b044969798f92fc2208d6ea4 f4be010794473e1db3049ecd4d84d05627dd33be6858ada87513d89bebd93778 b422e821b07ec1531698e2b89d50997475f78f8c59ddcf7452fa7094f8994412 8ff811aaa240794bd5e8cf5d21ead05fd755678656e8f0e7ae030a896ad0e3a1 b2b31ee946f51c58dc3d1e831acdacb308da4c894ada26d92cf885b466da12b5 2e333a1e3ee3b906b058bdb67db9884f2286d53bdc14c5143054b1e6348ecf63 cdbdc45b81cfcaa2bdde6d4319b0234ca2b8c464e6aac3ada947a181463a27c4 846eff34251f5c0076e4e2c854983c4044b6fdb836b1d6958084404c842541d0 31b2b6d776b38f559c1e8f6fbd7701a1f9cf067bedc98b91d5992579a6fb20a6 753c3a97d7a7ac59c3e32b3245b4c1e6a8c65fd79e2645dfec9e08196bf5c5c1 facda9cbfff0b7f8bdf25dd6af2534f64f3edf18109def802733de31abe06579 288da7b095382a572bf2b269f3d2f0dd91468d458a2c50be1139dbc014572af1 7edee65275e7cb061af3b8538a02dd1cb54c3a0049d722b3fc295af17c95814a 90f1b088ff7cef4436e5cad92502d21075b84748c2b4758dd0c1fdf0396c45a9 e1e9df368aacda8282da3cb81ea1758599413bff17aa9b3ed4d4c58a26c27857 7d59b675fcd945c0cafe8c2ba2623064e066fcca4127b04e1750baea465528f0 74e8a9e84b57bb4b07e46a3ed620a85ff609dfaee364bcb6c1dbb28de7f1c812 14fd789d3530800344d64a8b0173693bc41a2a909b18cfa88a5ca700acdc726a a6b766d16ee81a8570f6e7e3fefa39954260442b42c4f51a3b83ab6100292baf 6c8a23d96bd60ceb6b63e8353c2fc37efdd85b4af8ef9d6bbb6cbc56b78f3ce6 2fa3b181f1d202dd58d0b064348bdf31c1dad990dbbc8e059a1b414cd126e918 d2621e0e30d4c067701617544e8c86a0d4e2bcee498a419103b500868d9606ae e654d8b768ecbf0b688d3e513858f9c9515de599c70e4403236436768d1a1660 6e05f7f1c282324b171d197a03ab799a0bfdc371ec291a5dc8ab1407bc0d7e71 793bfc7db631e1edbd61fbec38831f9b5e324d2342ba6fb39e1f066e2d8a812a d869a67a0a327f783d72c395cc8b3319fc55edf427f2233b40200a7a76b3e4f1 823c7157e173f079713580b594edb97a0cc24feaa2356b5c493d927b25e576f9 1f52f97a0f224dae75244213223daead4262a21324f336affcb1186301dd851b dc4d8714ba6fb00db603e63b71095e0c0f48375647d8328bd1f1a98ddbd758b1 52b458e8782e3afb49cf0b4142fdb82de0b9a9b02bf66e396291581a96ac660e cc7a1833b75ca30eb566ec98a16e444022cd0537e9eeca574e5279c72cd402ba 492dc7df7860ada5e7fd7c076a7ac5515934d345ce74fc7f4461b19ee0c95dd1 a393560e00ef2187f5baf6338066076d72d170d8f3f76d53058804ff7ff76be0 77a6d6591d159cc018704b75d4ed14a0040fb8ab7924a6504e31263404ad7c20 d68bc46912d5f41a65b74c8cf7b0f387363e2d02b1cac744cd4bbf48c072e006 13 de28f8b4829e5db6d35865fe2b7bdb16fd2b83c5a3bc98690cc265d37b9d180041f745d6f9c0f13150076cc342d13f3539c86937c8f8bdc03cae0660a2f95b06a097351f7c25f54b1c6c646d81e82c0394ba822f8fea35174ad144f8227d0c042f222281c66d68e4098544036fd79a7bb404813ac4fa0bb29d7c13359d61e10bfdfb1fd9f8f579a16795d4be4ae314fd48c9b26c27c08c1256c28c17a775c703a35207704892fabfdb69ece4b6a3caa7776cfb31b49ef9959ac3a3545b9ec30be4839d7573160191b8db60584fcad0de9a3fd91156e545f5f55f0729b61eaa07b246fe0aeccedeec5dbbb94737bd68474a8ed1495a899fe4db7ff500782a69072d2c790d2e0bbbfcccf05615a396c8990bbfc33a7c66c131382d7a0cd62e2c039572a7767208d64f7e6e11d7226497f21d1919fb70966aa3e8802b9e1579b809d6ed96e0b122560812ba549bfc764429c43124ddf35d9a121ab1e0c83b00e50e2d764e1b669628c21a7328082dd01383edfd7bcb47f61484f45b22abea3c6203e2e74bf09450ee7f18ac8052d8665d0bf6237774b6a3242d90e78dc633d279043d61d8b59d1f11f31b3d0c26c7bbe0ff00b6c32580776f20dd529f477744a905ca0af9f161f00950c928ba41eba3bddc0eaeac9e9b43b664e8d639a5ef45e10dc0fc96fdeddd394592a02e839e6a58670ab56247af41fd05d0a41f6c758bb602cdfb9c42175fc564402420af47c3202c535cf10d6e301dc43dd126a380b0a20ff7b9687345b3725ddb46638bda04259703a93b06e293f7dfa3ac7ae791b2de0d8ac5e6c7bbe757db505157ca03c41812c79398d08b758bbe5537b93442c83508af4a68c03aa076f31d8639bf51a1d484e8c59c39ff2c51d222e29d9fd846810e7d808cfff412b420d9a37cccc5a90711bccbb63354be9770c4a79a0cdfd2960b7d0ec834aaa6a01d090772628e1b379066b4bd90b96d64a8f328e5a93dd7410a0950f841966a6dd17be8987302760d5dd4f7bb73fab3e336fcd48ee9ee1e2d0574ded55459c2e274e286e9fbb4380df504ecffdfbe824c27f1be258347c829099b29b5cfe857b76e254c1706ba06a7c6a3f3503abdfbd5f1c2c2640858e44109c0c92d81e3bf8d5801ec1ad0751d3b6f4d3c74f60fdec6219aef00282e604d02f66ca6ae7044be57e4aaf08350cfb04a9a349731582271837d1892ccbdcb470990c03f0fcd2801ba103db4d5c1901f1795e9b0aa108aaff7c9910a8bedecc7050e7710ac4a854d9da1378b844fcc03cbb6fa2f0b0737f772bfedfb78f3d5a009e5fada20ff489d0d0e1618158959e5da020d4942270dfc86ecaf8264fb878c0e76f7a7bd3363c804e1aa689453d4f2eb3049be20a4f828610bc4608660b1390bfa16cb4bb011fda6a992675f9ba2c62885a5cc06351e1e7743d026d8248c100cb0871a33e507a35bef755b4b890eca47301119f15aa58df1be80e2d0a18f6804603267ae831d1cb28a44b812431ab122a0f86d7870543ca33f4d07f7c6f2c60d570603b507ff81f8750fdcdde5db1b27a0bf21ee9bf229db879febbbe1694e01e9a4aee526f17d4a43a93d8ec3773dd102836de550dfdc74a090546d1e068e09ebdfcfab8ce3d770bf5a557c5901a2c3eba28caba30d3991deb30bfd67b765006353c29a5b5286d5fbccd8d53b028de175f753f7e60902d11560568ba34f7d0a074223a0fbfcb18ec94ae252d4ee0bb25801bb86e746fc04105910011dacd50a9877927ac91fb1bd9472662f62743c6879ddcf92253d8bd60a8dbcafb48833074a32f36c178b9607af3a988fe3bd1dd2d4d656d8a9292e999e3c347bb8483b03843dc776e2856098d9c8d8515fb1ec5ab86812da5554ffb93e76b0610d70dd02d76243cf532b14d66ce4b94410ac8e2a3a2c3704e89c37b68ef4025b1ebf6402e0a17d437733b8528a8d5078e0ac0b8b29b30ade19ca5a7a404919c140224008b403752879129c2caddca59e5b5e574df024915471c10d4399cf28a228bae2019a86e60e6c5bce5ed629ed9495ac7740ef2233fff8ca1cda9e73b8751fd210097a4a5766d842ec2842c3d967984ef45285420b7124e2e2f2c3ab9a39231e1b0f53cf4211559a1a1b953447ccd822c7acb64ddeae8e5c5eec630caed69499dc00542f53fb9e24553067024fdc13ae367ae337ffdcc6a6207d48b3999882442d0bc32462f0f7b02084a7e1493465f4718114d98182bc1074bb755686fceadf9f0fcce117bbbfb2c267e820535a29494bc0a49edffbb8b0a24f6d1f294bff900702e947e7764c499e0d329a6b813cf75b65d834ae24e2d9213bc38bb04951495604284f44c125bb329569b86672d62c98575eb2299cfba87144b77dab1d651d9a0aa612e66e8ea97d16736a8b7d0198ee01e0b44cfa21cdb7098790c4aac31afd07cd1829f403b85d4dfd7e25552524394eaa026fe75840d22e0f60b131edbdfd0983f0539127812d4604ca561c4c40854ed95ca07d4249f39820cb988703fbc202e45470e8062e6020cde12c214a762980734ee01f2c4cb8992b98940727d42501528db9ac111a75e4f0053602b9367222166d0ed1298bc00d82a8a1d65f9e0a0dd12e60919f8f896b48d594fd737ceebf88183e3036498ee71156f6b454d79908000f303abda2bfd724c646526421f4d6beaf73b0907c5a97234b8594a5e7b20345bdf97acfe1ef9af430a9bdddd4176c1dd8bfe1402d5598c1d40517206f83070cc7f8ec6b41ae1ca776b7b8f0b005771750f6ba450167c41f0df0cacdaff502c3b5b0e62bae62609af2c6b23cd36f13d50b1e43336e9e9526c7783a1d7deb0c2bf2e67ff9bc78924185bd94593d2fa61872b49e1f5a3426d02bf41902a6ce0ae5bf26ff85c3abeb00da799aa11ace904aa9c9085eae5c8415676f8a0b65a30772cf9a98f9a327243c9412772462a9573770c91c1cb11a5bb34d35926f2e780272efffe0e028562c4aa817a1314a7a2e3bbc46acc66267085f0ddc5d7ca2a40350b5be5c81d6aa1338b62a1fbed3ad984e863e93d5bc52c10eadbcc3c162df011bbf660181cbd6c0c5e595bfa5c2c8dc5159630d1be336d9a3bc4ea918a18a0264ac6f429a0206c8dfbb462debcab5c0efaa1ff17a704ffdaae865434a086e0f61e989c4e588a74b601ff0af770827a4505cd357e8fc376ed1db6384c5cb0e06b824161121462f534487ee0a83aff30a34c34c9dda9d9dfff87dbb4cbfb47b0f74ba77f14469e30d217e7212f7a343781dabcfe87db3009688185af3585e500287b96ea69baa667f788606d4be1c07ce70610fa0415d801765c5d611ba50f00935ad3bafb69e1d6c42f4d07d49b430496e7d2c0453396f2a605e294912b33d02e6cd306caf4661156bd454595ac71e8adad0a1ffb2c1519ec83e294ad3982a0acbc30528698b894ac31cc8a43f807dc5d47492a88d5a8887887e7dc22640e5059d4e0c016dbfc48bb4a852e000a213a22a17fc425ae4575a1f772206645c830a3dc7279187e551a4fce0978ccb78a4d776cf6b6211929db195c62c7c08ad66030e714ed7c30be6b0d6c3493e384d7db33a9460029e878a5a7e363f3fdcd580010617f61e28c03cc48984686f79096e56c542ffabcf069ec75da37a06367e860332c1e6acad9f1d59ecbc7a4320f0a0a3cff846b11c37c80a9c32d64e68b64e0f0b08e607879326eea7a9ea5062de4ec198de95305e6434b4674429fb035a4d0593823ceee3cfec8d79acc7be18933bdb5c0a3f8312d81a02cec24a979d638304713051ea078e821feec1c35da9ac2276b57a2da0380eb8f909890540083f550093e0e7a8529ecbc628a323c4f6130819ac1ee7b0ade97929fea0b39d9042ba0fe505ef08e8cc05001afd0b8ddd6a72e805735dadb6c875221766d82b789fa601d9e335761016f77eac57396f7050c0807b588d8b08334b8da1457a9bffd05708d113e4814c5a99dd251c4098b51ed303198f02815682494b380650f769b7a70b45c302e100a71bf83f7ada0cd8cf842f9eb1253ff63b4fd0f48c1caab2f7b30030d62217c8962bd4c859918c21ccab2747eec14b0f3940c84975c2c7eadf07033d8814e1b273c480910a0b6e4b42cd304073392638ee6d497bd58b7e3bc1e30706c274df61c35a421f7ce966381b7061aa658cfd1469a0c363d63296d062f102f647373279fd203bcba32048d5556224d0a63c1b42d430f38e5e572b19a7e40f6e766bc223d8d9b2b3fa3da538ec04793b065e0460a2bbd5ca5663eb254a940a98fb8449e717cacfd5a3cc58a10ebdea84d57a0dbe7406ef38f37ae6e03b5a0010ded2efe01ae3e18cc81f98db29b610ae3d87432045f1f9e22c1c3cbac4000489d17df3f88390ab4cd0799317a9445f89564a6d298d3612789009564c870e094c4825e1f0ebf0deca6692e296ba4cf2a71b732b926c40c6ff19273699afa70c330102467be27decc1824a8e5b52b78a0c112eb8b1c9afcfac05de0ccab1300914205745eafb442b805256d12ef1558d8f504d1f63d2e19465c42e7246030708982db5a3d665f852fff2f52fbdf250b675ba38706f6b5b5052afe6cf3aa8910fb2f8a660fa04b1e938e6d794a3621295295557bf946e8991e2cf2dbf42c301006a6198e06156d079bb7ee2b34c58e3312a873ac99178464232a5cde168323e0a90d446566a8a28f56652526121d3bd7c8b65dfdb38528def61a964ecccd66d04af16b51481105e745ad3999f4d1019656aa7a84471e56656418936a96b5b0f07 -generate_ring_signature 1b9c52310cd90de6486e3c8b73ec646b0da895b54b118acff6ecfb69afa6e346 fee5ae2310a6b91390b1f30f2499faff560fb74ebe4b32f965af0edbb140a740 1 8084c2112b5d153f5086f416d6d1c059843bdee8f391f547ba9460225a69cadf 56320a68733e538cca2d2bc41b01634fc3b9e2ae3f9f83635b2574a4245ae00e 0 c5189cc07ebda73d06b022cdaaaa2441adcfc5af69d199d3d38213f9b74d4705c29a243ff2e9772b4d41501672090d77cf46771b317dcc7f178c6d743af6da0c -generate_ring_signature 65e7321376ed6c6696528042a65a9a4f6516d65fcef80214808c0c3230a52087 fcd664a50e9adb47d9ae24dc83f707e5178d3235b84c17e9a6f586c0bbc52a47 1 42fb2300246f28cb82112974815f4c14cdca8750a20b25ef861621effd08c45c 8e82a603216e1835a583482e580e635a34b431012284d709815768785245f800 0 eeb2c18bcc4f1f5cb758fc0e7a23a20f5c4ee01c3c1cff2a68093cbbd8a6d301359bf9c5aeab314222cd92b02889c2d95dd3e833f10a3dd5810a515f0b90260c -generate_ring_signature 5fc776eaeb6aa0303db0d4397d093d225c8cc0e00caa55d5e3110e9d7aba1aa7 5745637caf7435d578fb680de1416357dc6c6ef0ef7f00c0d98952cc3eca7d74 13 31b7451326d0a7a224f17225a19e946750d81bda5a2f56ef09fb60bb2172a44e 9665dbe2e581c02838c7406b1c2632ea5898f527c27aee64001f13fb69d4e46b 43522177f2705f00ce314e061859663485afe95dc1103d184dd8445b51f8d943 d087ba1e5c49e3d7f627e402554bf365e115eb049d1f7101bfcd02bbffc09d5a a76ea4f37eacc9214defe54f2d59017a3d410499c3a6c7784c538d480764e2ec 881632bf12f572675667d5b546c761b3448453c771872793058d7e74975d616e af5bd86056c8494bc987c9c38ca25b9479c98749dc1db4a352ddfa119c436afb bc6d378f748e912b350e8716c0965c91fd00798534a25148a9cc8308b0ef1922 bb0e87d6c20206a4237b34b1deeab57891c6750e4d152e28a6477400e4d5b3a8 3556aee9033cd19cac0d3aba95b99694deb7dec82a18509529836cb013f7e656 1b0e39b149d8425fb452857732ef05daab09881647d5e5e648f57650a5fcc5e6 c2a218592130b95abb79673da2204d97970894818e0cd4ed17569c973403aabb 3a3a3c9771727a8adb3029e1022d766357396ac444aa17f75ecd13b7dfc4f25a 20fd014c0ce804e8bc23ced4b22b168bc3c94b8f76b7b0f2366c91e4196e070e 7 09342a79f92ed180608094155386526f92f408bde4691d8afd347c8b22897b08beae950df84aac57afbe98522c35dfb3c022fcd7629f5665afdee45d0180b60003650f34687a86caa06add5d3d5479537d2a1db672fc7c1d5b41384280d2b80e86393d7afd9991e7df8674ddb14000fba0eee44ea5b1295fcf60f943e289dc046c4b4b8b6de66125c068c32ba71d27d69a6df1eca96f6cdde2ef51c48b572304238efa7d43bdda3f49453e9e89780c849dbb7ea2dfa6ce0476133adef529c40d2aafdb0644e45133be798d96e777a5be88a4e12c7e0d19dadb854cfceaf84c0aa13544cc330e92c331ccbfce0710f94f1b353bdcf0e31bce049dc688d5473f0436083c1f01f5150ababc127d832f4e5d6ca6cc28b7a1ea9cc40f99f5bdaafe01b0c8619dc62d01f1ee11b17438694d5fd7060d701ca3f9ee596353c4bae05a0fa20a2d6a2b943d6b3629506f914b1b4f3c518185fb1cfeecf2d8e915995e670fca45c4dd442894520840dc701fd4749f40b1d045ccf86d2e0f6134cf9b0014025d8e45a672fc1b8fb92a47aa54eb60dc745e8b2365a36813c3b7b10723fa2a09b616a8a7dee986e5965b71bef0891833b8cac2db7ec6bcf331043def964a30025389a8fb642925dbdb34f4caf4cf43d9656cb2ed82d474e2a325d2c4599bb5042a8632693500fc9b2888197e7c2e9d4d021793c1f5eff6e1701900447f8a4d01db5e01ade4a84a0de213fc061b49602247c9a757216e006996fe9d34665c4d060d41e49584a10e25c6285f2157290035ed8f2b0f5e6259509531616c7e013c0047ca85a84a03b27687b85f301e7509159d376e46876ce8304c33db5ce934b109c04cf9eac528e6550b82811ac472cafd4b9acaf0aadc61d660a52397e1718b01f330681b1a4e4ee43b7af1387ce1b90bbfb7fb7be53fa75efe15b2412f13070bc56be891946eade5595aae00375d6aa3998b517f38db9223333d4f0fb7d5860a8941c58240bfd8a920a0112a3276a740bb9d7334680faf2d01b60b6049fe660943789821109482838d0ae202a87c768190f61de98d9979863cbbe11339126a0a7f3af2c01d8c850c6a130ce1227fc0b9362caedd23afc47d2f5fda265d26fc06baa48dde88cefb588891b98013993260594fa6eaa2f267330d185ecfb2ae7f0a -generate_ring_signature 1b670fa7fab988e6f8fdc8fb107b3d075faac101675f315f48f069d363482811 311bd02641a786377befcf7f879163d21693f0eb1bcb162e016f16961adf8cc1 6 81b31e3da8c61e445c7f342caa50aa1f14e957cbbca3d541c909dc5040436732 bd9772f8dcb21506b557824bb497ef50079ea93d74b936ae6ee40fa1ebc5e261 7a31c4a1f85d63b66d38c7656d27611f99b933c8b97c1181376acb149dbc2446 763dab71cdc26c00204f5d24ca3dc0bbf122401d0bab86246774d04adea8d2ef 19d20452c993dc154c00d764ea28ca9c18f92df93067a40b90cd98cf6055f2a2 0765b3b2cb35ad43f4b64d7da4ea43c89d080c201bdb45d5b546f1ede6eafe4b 8ac480f5d3827bccb2bcf68e45cb0ca7cf3e9c876d80405a7c455b22bf68b70a 0 e9f94626a35bef08510b5c952b610e15a12981fd6d293c9d40e820664fab6f087b0d3f240fe5698b8418017b2634b851a2a9d8e426284e0a124e08895e09f303c7f778190b997a4aa697a09e0f99d674194f9b7e1d748a485540363f747ce40136668f861b42d86283d41e90fddf83515df339fd186d36031d8339d4dd11d801226df23a16381527adee612245b3ce8145b1b001d4097417ddfc1335d344670a3344364026b202b25b42b67a5666eddf17a3ad0daba28426fed51688edf42009d7845447824566b6ee580851b3c51056490d3d788689e1928a366e1b1518f90bf9062d5cfcb2fc1e84d05021d6c06ea32bf5059df8a8963e5985eea1f91c900c0335377491752000dce817bec9f53a948d81aa07caa2450deba10dca4afa900a717c0c86f42ec95b4e6efcfb3034725c22267fc7f7157a082b606ffcee1f1506d584418bc9dfcd31868afb31c4bf1f00644b37d8b58c7c0f3bffda7a008ad10438a38a5781772a7bdc07fabca076fcd7ae93ca7feb3da46cd056139dd47e5304 -generate_ring_signature 89102300bdccd553ce352ef36bd8cfbdf13f099c02bd18596e3d42e6239bd3d2 66b67ac092566c2a368b9a8d9ca4c4cb6a98f299e5f410c662017950512d6031 116 aacf971f4cb8083e4592930c84c2f7e1e814359c56cf8595064261e3a57062d0 4286968d242a26dfb7144780c2399aa1b000e5c64a7fa136663d9e9129974657 914d0875d0b0ebb1a309ba776dfe0a0e0c3ccb37d57da02a0adff77911fa6cb1 25960a259e5be8f63b901767d00eaff9e3b5b920f6080b957913c065036357b9 f42f1612df591adbbd3075dd4d7e13f632262a2aa4692fb66a9b23f929520ecb 2cc95b8fccad32778dc53e534511749bb98138621eba4b49df2ee579c12970f8 b613d3379b8bc47e960b43003a77dee82913090ef169ec2c24bc1e2e849c6ac8 a2fdff8411483e8012060880b6b2ec460ab8c4092dc88b48bd2111593221a425 3991843e89714483490b27ae31f275df2dbe7e29b0adefd7cd2a985edc5e69e1 b04e1160aef718f8095f6d74b592429a04998db7e3235af0805e2393ab257f47 f56c8839ad45f551557e2f0207ef083f0c5ab8afd19a093106f079304bf1fd8c babf3b67c8a305bb3fb26c0a0cbdbf11bb96df24e36903e5d7931d8acd8a4def e08078fa1b5592ea810f70aa9ec2b5af155d158cfb72a4339b06953d203a98f6 24de47d9b5fb0f83502c076360a5f84ff10fae7c178dcb6899f0e54d04c6f744 bf656a168108aac05e7489c23b7d3aba492ddad0a7305b7a235cda9b5f28d62c 20df1335f52c54cd9a273021327eb3cde3927a2a1c589c0b8fd2bf5305542255 6932a7468388e9361a00b61444b2fb0eaf68d975c554ffe92d301fa54277a9e3 c6878450d4032a0a632133777ba8904cbb2e6034d05d5ccfe183d9189403d913 27780f528549913bcb7d6bcb96e14c6cfacdd4a229135dc06d7c5102219f0110 92a1e11e5d0a1e83ef1aa41bd739c136fe4d3b2585c0d8ad15061d93de77d704 7ca5bca6af948f20929a81e95d75faedc991ba2faccdc3f2d942265d0e60beec 7d0299200ec4b03843d3c9c78d33d9cb871902c2da6e871f5328e1330038991c 95250393ccfb12b976d783e295fe5a0986370b05c48e564c2aa5d5c3e3d49a05 81ca61171b3f4f312a2460fe2f3a9a3d70a4c648a1549ff47ac4f621ec00e8d8 8eb43535fe2ed47605beb83118cde781c3699ec00473a56ed1ebb72bf177b356 03e96f6ec4eea800c6f3ef8b2c43f31160570d383285ecb1738a481cd3d5c335 209433cb2ab66891b1f9dd9fb74a75dcdac2639611f72890bc4c2a90ee0ce990 1c64633169c011513bc0a3c23dbab9ebde0d84ec021815b5efd749122a316d39 aeb62e7eeca2e3d46381c5411a47372bd9ed18db465d99a055a3b47c8be3aa1b 8253bd9c2ed9610658e5e339f2531f19fd147d0782ef9f60917e9d06babb7e68 2728a4782111a52f676bee5c3baa8bfb5c346b560510f38921cad69f9de907ba 12d57f3b4d36edf9656e67cff6dbe2d6bae85ba9a4365d04c066cb9b85eb4c11 e458bfe87a0561429dab19d989c37b09726656c8a399d609559db03680c946d8 e273d0effd007e930b1e9e130b4f87497188c4ed48fedd3bc34d169f83735576 31b09e291aa05bcfd743b3da58eaa02c772b817aff414d469a1f668f41ca908a 2309b29e52ccaec650074476470410a53fe1091621881ed61e30240b7d8e5ad6 f26e0a3ae0c0b5d5c53b4fde3a017c0dfb1cc1e67c7e9201c484256c02fbf584 b2369e479b65aa0e85c6d61e312733724eb93cb2c5b34b088adfca7f61c939c2 bb0e268150f215a3fd664363b2ffda9c7f8cea4c47c1efc43a9118f09bb66cbb 71b08cb319616d98b8ecb176a8d109ca5adfc90e7d88838cbf6a92de3e8c7ecf b2e5781e43a4b2c32d0acd5c373cab979b290928728e9ba476791c4dbdd9be32 fa305e51d4f7c53dda4689c34856a691ae3d6a9d01ee043f2106b4bc3787dda4 fe970cb89108b4e704e492c8db64cacaf564956bb1c47610ee25245a842fb210 336aec71dbefc4e4e44fb1e19239080446d9299b0149e1fa171c48d601a41c8e 554b235718cac2e9355720d4acc8838de199b59ace20cd1ffd575f0f4631841d d3ff0d68b3ff9683e5bfecdb8e8377de4ec37ecb155cf39c4e63dcf94b1ee979 f2521d0a0e9836e48760f0dd9910f229175740e2c81ecdd62e3859a0f4e0c7ff 66b07816d226538b578bb1df23e408d754a8a07f3ca5b6374ec386c5134d2697 ca59cba4793221d8430c41eb4ef32de270d0c89eca0b858b420a57696eb30be5 c3c213d1c7effc9e43a51d247f0fda635055491dc26a23201ea60758a37feac7 cb0f2314b2ad60b4230957199a9de58698da7811f47847ca973102a66f0d9498 c928cf24971c0890606fdb0b56f17b9409595764e7e398a2e729ee7838b0de64 b189e86e14c4ca5ae8b4cb715feaac84b921f3bed879deac20c9b27799176d88 65e86d5133b1d43edb8213bd3bfa7babc6e23628e52e36cdec478d39759cd6a7 3407636c7c04d71490e9edb5839531aa4724402b3c663ba22857af6daf6c9353 3445879109faedda39c091848b3b49cdbf61ca6e95ffdde56bcfa28da6d9d27d de4c9c94c1a01d5d177b47355118f70a68e7cea90d162a4ab67d7f8b8be582c4 cd847f96764801152575a86b30e6ed3a75acedbfdb16a5576d3a094342f599bf 6890ecec3024edd3be7e67817ce51b8de27b7a4feb288d8c4aaf6c91e5e909ad 66e74b36d7939f347f0acd6699214d4a78a016d9c2ca512a6f6112fc4be6bf91 a582a1c2948b238fc1de90feb779b9d11e57b06410ef58e74f565b6e3dec4fd2 1d58c0007cbd13072dc8aca24abb0b6cccf238b47c897a1010a300cae267c021 a309f20016a70430700d55c29521f51dcbb9491ec7a7be3666a0183f7b5cc032 23c15ac582b1d2443eb5b4e1aab30a44f108491057a87064a1784eb64023e96b 49796b6bfbbcc45aee369a4cd875e73b43fc6e108ee97d813dbb82f4227cd821 008de8724f40b50fb90cc68e804d395de075bf3f40888741c98fcf0df2cff0a5 c974385a5536e2e7018885cf4efe028760084ba063279d848293ebf7cfb5cd90 827792ebb3a8a1283d94fb063877561a4e92c709e9541b7c87817b28ed185696 2fe3aa80686718b9739256c1a433a37109c903f0be3abb74819cabf35e0c1bb7 e93816be20d1dcdf6ee39748a1f08e06f8f39490991d3058abbda941329c3be8 ee1b391beb2319d8f67e7376e68c069c0011ccf712f5351a82c13552cc890047 eda56aa82c4e114c8aca9636db3b356e7ad9c76c63acfd1ee135d3a2ec3393ac 0bf5483e4bdcf85336f82b77f70b1623d201ff601877bbec4b7b4963268ab62e cd4567b648634eb4ac39958e55ef352570122929639894f31abc61adc2609fdd 0ad0e927718cf2b4eca2e50e0d27d99c75cb7cfd33fd63b7fa7ae38387d92e12 6735f7d46c51519a4a2d475899fcae2f47e08c91a08d28faf81ae21611548f64 210e26da394508be17607c6d23d08e6d93ad5009f0677228c754d212a8e084ec 70bf02ec4ffebbce2020b5db776c6eb463b24989f1f3f8aa2e1bca91f2a4e315 498b3093b6563f383502e489fde1cc1201acdfd6fe776bfd68f674c4e4a2e8a6 8e3a7e23a5cb28de1bd8e28ba5d87a1f1f234fa405dfb5f9206dcc1df31378e7 9dc0a522c312cc2a6c95d954ded0023fa95e049848cbd06cfa85798b91d9b96a e84f3f5612a23c4399389dfd2ed6972e6744b860a8f110ea83ebd77a3a7f632a 670b5b7c96794bf0300575edadeb40dc40f30c52ab560f55a8f7219c2c7be945 b97fdaf538c771ef870edac92c52b266eddf2f41a662c3b812483423f1172f32 34fcb11256ef3dc85e4100bdcecd84e89b64d9c39da22b7f7383ff1b172f0327 0d735c44c9c7c5502d4b4b280fba770b284e2eae7f9446d0964bb6ff7b4e2f92 08f03cdfdeb880a685504b775045779c86135aa6dcfc8e06899bda0391141946 865c08b9ca9748b72018189051fb1282dcd1662aef708c62593dce09981a45fe 647f241c0da6d5e897f96f709e4c5e0ee7db1a90026e297b2b98739662599656 b424e2d17bad69bee7e51c8c8c017a3689b4cdf52cd016af5e65e7084b3c380c 671455db63d8773c5e95e71d37c3bf7d3e6316a39cb2fe7d234899011458c83e 28119b556f4048b9b31e7946073be2b62f74b74a56cbd31fee1cfbc2f5f512a5 a8e89c237d06199bcd94c030f5c58ad1a3caa2ff5bda6d45de213262d038cd03 4d6ae40c4e28276d908f10fac7db8fb0954d0b09f3d516cb61bd9bf9b5df9fe1 7fccc1bfba50e840563c220d2520c9f77afef38d9dfab6e01892e6345ea100e2 28e363ec69911e6cd79ef7ec1c6899a531ca6aa1b00d00f68d71cfc038043847 21dc113dbc94fad84468f32b6b654976e9e2b7ba9caa011fc46394cc0efffab4 513c7e81377e13bd283918a5a44a5ea8bb253f54d554fa760e04523c27978471 f121a815f969fc0597b1b5c518cd49ba4589394fc511633cfeaf0554a33e5bf6 19b4883add93f293a640f138d0599d8abd50027f605f68e78e398568ea444647 ff453b1975ed16f8c0e1411dfde72046542032a9e753b4cf9bdce4cff141b734 4d39889a38efe6d22a0d3569d79b2d5d27a2caa9d40f8d4232db0ceae78c4980 471dd89be76b7673c0948e9dee567ba70acf98d02ef82b8ebcb73dba23a25aed 21a96e9e7fed5e8301c71774469297fa1d0e095ba317b916501070d18be35eff 1529c4c48b69e33c78314005df30b4e24f25341538b4e4784187f8492a95ca4c dd5756c71342bc91146a6d89dd9a1755b2e67297dd08ee2ad47da6f88712cfde e223d3cf9f77cb85b9443da601d925305edea5d1f86b69db533edc032dec4132 9df21b3f335c553a66f4c6b1dd7ac870dc885de1b3323e86dffb116d3012a430 c545e90cde0ea37de654dc7f6cab3042b9c53ebb923580740bad667891f34556 cf75bf6c505a45d0bb8b966e4fd8fcdb60caf6998310f13ef23d48e9f8da5f2c daa6ca75877e1e7eb9c9a1aa990f850d3748ed84dc65be4ef4509ac2dc15a50a 93d1f3b4a03ece48c32a6e8f2ca589953ea4eed5c53798a388e51f9a6a5b9e57 0e41584b69979e002262d1cca78e9ee773e22a4458ebaa53de795a9885051fae 4aba7f5eb551ee6c25d9290c10ec73cc2d1fbb0fb3646f2c3d2959e620d6543f 6e0fea29e401d70f40f8be4d865f7db6add2fab1ab82090f3b85e53bdf9a6cb9 743899b36c46807eae625a8fcb717ae77e7119dd436a6cfc11a7876a1201309e f8fc487e376e13bba04de3825648ddfe435e78b4bfebb43e5b0d40a33859a500 6 43cec6a0c0304856dd0d519c262739943f87f9d52160140d1a762b239bf2f10c74962b0da0d0ac703bca600278e8fb27b7757df1374b505b21153900412f6c001328221a49f99e723060f61015840c26e857ee0d1b423d1e990eadd28892520cb1931e29dee406c7f95eff38f40d19a6492fa87c8db751979ceffd57cf442c027896bf79cf6e5b639b9b042bb0f280e1c25987b3c9999afade9705539dd78e03ee0e84b95d5034ec83d82175a3bd2d260ca39a4918a97a2814bd6dd27e9bd40ea44079f963e1f518eb29c311deb2e567500971633bfb97526f52915898a60d0402da77715d3458f28d050688937fb33054b3716cd7802adc8a25d89e087ee40d6a48ef4c8534d7403c8d6ddc0400c1a0060883df6599e0b19d0d3fd5bd6895056f46f8b72a0dd5ea91b0f8ae30dad617e5c4a7ab3b8cf6a78e6115cccea974063c49dded25026d3b003183b21cf4a10a86655d2080a20a58d17f9c37493f560462f06530cff94191d0730811d701f6c8115ae902725bb3819eef596c333f910a712266a6de00e65afbcb5e49c5427deca0f0b8095a6ebad049353bf410486e019c1e5f7f858369546bf6f50525dd89c640b8adc528adff6f83fd67f9a37a800f257f7b9214525a88922e876a2452a47b219bd53cd9111d197a79d16c43f92f0c48b88ad2d99729c1554fcbf242c26621601607464c9a5d29ec844e471bc11501f4ee12469f3a6545904aa0d44333acf94ad5ef0cd08c11ae2ea9c7c9ab2c1c0f696d4b0a612ad91dead27dad857a297d48ee78e27ce9678564d0212f24e8b9088433be9bf3ac522ab5e4ae243661e90e3f26014ee9df8d930ff6cde2f27a680c8eaa4bd24dc674103d40e87a029df25f616a955b2f398c545ef025f79129020a4cc2901b69c4813586e7a4f7c778481db86052e9f3bd2597207ccfa913c6c003e9c33139329b5eb70a0e4e9edbd1b42ad0c3cf7d190f89317a4d63c96782c5014ba2148165f1ff3f132317d92d9508a710c30ad41035a44f5afb1aceb599f102199dba4b4f47df06760649e0d561dde21488860eec810ff8a3a7a70f30f087007bf40017db4bf0b70c4554ae8aebea92436a7d35a1f866ffe54c3c21f926e70c868f074bf6fae7d4566ba0a24488f250ced49d91565980914e70916f0449ac03093cd48257af70d1749cf6dc0371081e62b65008a0a107bd83ffc5e032d347079c0c01c65946479c929fd318285fbd2c7c5701a98c14c9228e94d929644d2d0af11e91a1e74bf810ee2496db46796a9cea5d56e1ec84dc75353fbc0c010307022cafb33861adc944db70b837ab0b152603f5178cd29e87600fc37af3abb6640c7eebd1e7d941548cf161562aa74e31a638fa54a779dfd949659211cdca26240037dbdc98d7496b376261dc1d68f81588a1a133d2a35c598bec4d201e0696920fa6c6338ba271395a03a5aa4ad97a54a74c1e3488a4654ab0c1755550b95f090448c9692e6a22e12d322e4e4517b05a95149862eeb068a0ac394833dd390f7e05256ef72e80b9474164f615c5c6a20a6d2b6b750c1fc18d8e755ebbc6bb8ea8028b1d9bdf47e36a2fa9266af6a9bce367c0eeb74f46651c21cca31e5e61791607665b85c7ee9550c50856ffbf8e1e36cb2673feae3aeea1f8f47103d894a76f037879043d351318635418379ca5cc514886d53249a4e1fb7cbd24983668de5a071ce311188512730f8efcabb814b24c53a8c995d80773de9bc2f8fa4547a5240a5e461cc44a694884a049fb9b77044b2e062b606f75eaae944d138f31f3aaa50d5c151c86f3a0f0148178ad80ab7b7340b9d3009157fd355e20216bcfa7fb3c0ebfec81b3ba2cca9f395311f24e4041a0296a56b7d6e4a0e9fc30356678403b0b28fb124e913482572b5fa404674a597254d5c4d35158fb3be92c2bb71e3097038871a6bbca7ac73877fd3722afd3df63f11fa7fee793d68e963c4fa2c503c003bb5c93d541ec91a3f4174813fa19bcdf910595356bbe950a73b8be68166a18084faf963bd52c69cad8ad847acfaefd143ad79119dc49a1cd085757d88acaea0517f007697fc5b1460db409f307b8682f25e3bc95e71b6373e25cf8287ad15e0c6626a26c1c1bd6be0c983908363473afc7006e9fac3a61c7020e61ea0e7fe10a3b469988150079f588fa9b05fe4333844a47be436d937ff3e1b0d55ffb7213044622509582f7e92fe720348ac06225a6b05d4ea800e49cb3e18877a73f8e410c6d89ce8aa30a06b4caeb64fdf615575a791c825c9249bdf3b9fdfbbf204ed400c1c4b1109f440a994d97fb998c40deacb35547782c78cf5813d0754fa665eb0205c9dbfefb1e7e5ae69abea504eb7373ba4e6fd3e16b5c06c7aeb0190c58990a6c4bc3a9a8f95e57e042b4a301da7ed993040edf2e9c3ff55eae2cb6528fb70209bb44c7e0fc1f4876a9485009b290c1464684ecba388121c8dba54abbfde40400df037a099775f1446fa38340a851bbaf05bd5a57ec936c13e9db34af721304fc581f5348528f28b5ec197f65939a4c5faeb9db8aac88d41dba71fa8f698d010f3505edfaf1c4277148a8c2a5b586eb3450c954af36d60f723535b894908f07c9ddfa23548a4fd1543b209e302d36314e9db1efd8292e79029106c3f71ee00437338e2628e385ec813f48b1a6bbecdc2fd6afca0bfa8d3c1f18729204309b0e709d3fe89d155d9db6ab39366372b05530d73d8c0c1a0374e1badee71ac9430c076ca2b43fd9ddf23905be5ffd507a78458c072571a264c3d4eea9231d917d019636ac3f4343e268bd58916b5af69c7d611c6983f0e5a653fdb19f419f028d066731d827bd16e2cbdf4ea05d8de5ce22686bad1145c9a863b217d4d84afa0e0a37d7d0f5e34ecfb4e1b12b9af7c2c291c014e67912c2801db29e46936e033501ab2512888fccd3a6a8513c0b6f61f97c3d95695a1bbb1736ff17c85cca06590af0118ac97692f883042396fc92b2c500cf3b40fa91f54014f9ce026aa4736906c5b81cf8f5b3c7ed2e03f5d8f3d1f3121b56ed82dd3076f4ee330950ac42930e129ffed6e0460521f54d7b57a13662684b2d4541b155e5efd94927c1f333f80f78eae8e56f704f4ce3848ba782190f07c9aab223cdf22d73eb71fb507328290e71364c4759c5f21f60a4e5aa95834efce52081ea582880a3c6952b6ee31efb0c121b30e3045cf5f762b45a6b54da116f1ba327c891617940c8d7107bbd0388055decb5f240704a7e36ea19d1ebc1ccad2bd8a37e554e5a5b6158667d8d9dcd0c443ae07d6b1c09c7d943bce1117b082fcda2598e0796414ebd5a0126ab4a020b9236c3e5a9cadbc909164003718131b5641fee61de0590346d7f5d49adb6370cb78a07765bd59d89421465fe8ca6fd3c4d69df0525425232153f34e759d1f605d65447646143ce5b2863d1e9c5ca125ad72c1707e1213469d33958bae17fcd0b78264dc1d7e6d0e4e7bdf59a02d012c972ae2b5299cee370319f97949944a10add1bbf520315dc073a76956a96aa6022dcd36d0573e4e7659b0d271d52ebda024361cf502fe3e6915237a7e19686b2796f3de5967167e6922a4d59a2461b6a005fd1432a1df507d7a7399136923880eff6a1a3ff60a73a503b6fce7352042b034cec05c6a6ce01c13039a9b47fd37d1ac02b8fc325b245d4764bc8af0afbd705f68b4e95d739e6e6ce9bcdfe33c2e9a14c02c8aeb22d13d6797e90e084257903b287f41f52405e727d9eeebe9a0cd96cfa53ec2d5a9ac27c19890f26002a800268cf74576291341ce0fff7e3ccf6221b6db8a73a761a7e9317ace7bcce9da60ce59b656b2ac0207182180771634db22c1ef9137c8ca93ac0d6feff70c91beb0e562d4fe92e8dc148ad36085505597f87430aeabd75c7ddd5745136093993500f3f19560ad1e6e474b87319adb977aec73784d5774e986a4c673e39d12013e106c9e474c907c7c7125d6b8d48defa993a2c349b06cea3c57908e7f256eb6007086175f5912d9b59b644d91bc6fd41d4ac9042c294031a2cb3b14b07bc1c40880afb6c8dcb36446daa33d3345147d6e7924bef263cd949a34f3e24ef839ef21502ed639e086b907f3218fffbcbe05942563865e4f98032415d2a4bd4b0475d2e03709055b48494c1c4f271902148a6471aef0033aa6099a3a11aaadfe479fa6c0ce8c49c25a1d6f2148dbabb289dd9923f178d07cbe7106f09dd0f35079d925e004207cc5653b58a9b04bd568be9c1e37b0ac61da557082fa7743e312c1556e009e57b002fa611ccd017dee58ed9abec2b7785ebc73cd309fa9b22ef826e95360a8528a8e2897a786a51a4c101a3b5d589bd1e294563bafedaa2b1f975044eb20387fc3080f087406106d5b26ea33acc4510295bc6dd2c405f8992d3a52312950c5701e40929f14e69218f75e0cbdbed842d43180fef31bddc7c8e75bde5afec03c5b9ae77397b833242ff2321d2c83d78af41ca2277e687d0fb30d9147eb216005d2b8652222410a82841dec1a3cfe6a1a3075cc067b7f14c606f9094e7e385028b3691015c8864dbba35f8aa4b23a48d5f6acea0142cb4b0470bcab48a71190d19abf13cb7e67a9890e9caf33fc0d395cbee9cb9fc0bdd5800083c11f7df300dfb102633bdecbb6df7d70d24d556d195473b8f4adea46b2f2883cc43d89906097b609c37a793c01b7dbe7f419fadcb6e81153ce693ca4039a78145b0178d500a2c56aa83b3811edbb2b5fbad72ba1725cbb164c0c0e691a0f7d37fe129267400317ea0641496eebafe989c5fad8b74e796ab3e9460b5930be49425d1943fed00bc112656018c93937d7cbb910055e9756d4a980350e670baa7fa3c00340f610485143db298ec7fde33b57ffade27df98f4456be080c427c22752f3260b29b2071627796a59ad278731a4e8719f865a3386ae36e936c17ba94de84a34b300fb0b2add91439999f5e7161073ccdaa1aef2c2e96ebac1aea695c11f61ebc8e23205a901a8af7341a18bb37393f18c947a0e96728e17bb80500b1085d37b1e51a305cd0741f60fa8fd130e57195a393e2a43c577d837d95c592bcfa2b45b20fd6f0ab85099d6c3f43559284126ff578243bd8c338545d576e7a413aa2ec1349ef008df56d2f02fcef212ad0648896b89bb0537927f94d8a41699ab8a4f39246d920c3de10afe6b9fb8646157091bc98327091e9dbc2e6ae388994975b9668f76b60efc87fea94a89c1ec6fa0dc32bffbc84100b41af88758f2e8a2c2048f4f25f300f60e217c8e9ae063a227b62a43b99028a3b186264a6e20c9250ebd1f74bfcd022a4f51316da56fa9266143ebf224a121e062b5d699217189e98786057e7db702152b2948fa4d18dffc79d4cc8a04059a7396e8e62e5d1adfd2c65d8bb101a80416b251ba0683fd70c45179fef382891d49eb0b853fdd80b93d99fdefba53bd06056a746b10ee43a05dcaf0aa99bfd21717e40a4436bfbabb2de5e9f810ebe10dfc9d92eae6b6f11d4d9f1009cd2c8bdc9c2263e516881ceff9970fdc069d2609458bb52c0afb68aa5c0ff2fb79fb808e1d3f6bc5f892bb959dbd774bdc99a30a644e5496cc80b2947bd3a2eeb21e80d648b79a2e3d479006def344fd18e5830bc15142038d3d38128382a5e03b4d4cc3880ee39b380116a502ab15aa4aba16071c4db28b8008233886b4f4fbe1b81d8a875559b22b7822217ee51e8c4361470830c20085171e673e235da05e7fdabded185e32addcfcaf5f2530695cee3daa03bde3268c9ddbbb759670e43dc6c2b5abf2166010f7f56aa5b66be37828decd09f605e6a2b05f1a1d31901c541c1c24c52e892412fc7fe1d22446e871377c67006e6ce9d2fee9ca73907b94e4e9c6d0be94fc924b7fb8a38edaec08a797449508d0f3b0b10cfc867b679d76e22d41ed784dbcfbbef3105bd572befd6a5b38650ffa47adf42d5a94c16f15d51ca88623ba769d642a7ce153f8cee735b71094cb0d7bc7bcad3aa431a5efd90b2eabfd0442cb2b8d2913500a03bbf11792cd11c20e896070ac568d20117497ff8cfb4c3a0a7d6b0421010db7f383e8ab619486c3016cb7d610bc7627ff0636eaca6679769f850905d8a9c7b8ea2ebd79c188c85300c0d24f048d88037468a3ac88555466d713adbce5d85fdccdb90cd223f297f70e8abd8b8ac28ee9700ed200dd26e3201ff77e8f69a90b2c0a18089a68fb5f9e0eb0483cf689e139fc4906ca865a9f5b5554f2ea037b20f9db824f0eac5d8e1d02f018821dea3c227c9d8963ff9e2d1414dbee4f16f66e1185e0b11140ff81c1094680f8eb65629a3d7df9700fef33f1c2cd38130261d6b9a96ad2a2b29165fd01d11a8df1830ba1ec1ce410c2f51e204b1efb077174ed69f10c693b38971e140937c0f381ea0da97a0846c3c8c3b6360428f0f9235fbedd965031785c9ffa0b010176f95786e315da1b2465c73254bb949759f3fec43320eb38e52f57e63d720c3a45e2db1ba0a3f1f27ce82cb7904865a550b89e536a9ddb5ca310ce2fc31609ccb9d18a1cc2f5c991ce4a9f6286a31a184bfe482c5f315035c6a6ce5be54405853faa4938e5e12655ac4bd318fa29157a4ab6fe1ff896dae2f9e7cbd0a8d20430d3b6b00ffec329832c56816a57bc9107f6442ce0899c6bd6ef9c0ab958d00f33d12a6182c8be686c633cf53c52e28cbaeb2385795d6b6a5bb691e3b2824b08f00fae6faf7cef59b29b46bebdf69813d2d6c743e83868c29bb07219b3d7c60f2ba8caf2ae3d2929ee2ff6d56b45927fe933ddb28931f4d4f9379e6cd5d94505fae2f754a3aa6a241f04745c65f545b1a8f6fc82797be4c6a4b4cfe6a35fce018609033b35a5fbfaad3c29232ab71063f4a520762dd97cad978fe9d013e6ca0fe70f383470d3e2ae7366cbbeb5797f02369388d8f2575d59982ca00402301c0d1927522342f8828391b06fdc57140942f96957b2aa320bfcffcbb62f1841eb03c02d59669d08afa374c67b92ee4b3b248dc7699f245cc9971a461610c52d9604d4ae06eba5ca0790f06d7a06bd12a7eccc30fe10d1a04246c7d377267ccb440481e25cc60765dc1a8c3cb17ca49a1ccc1534a26fe0b95a1a56a19a5039a7c80a3689b58c9ad8020561d88cf13aa5063cd3a41efef28cecef934a54edee2b8a033988006748c53f3733c1223d39d177c0313a0914ded7709901979aa1030bcd046d8dbf377f707f26076fe313a7ddf251b5cd2c0593df247b97c9c7fa9814bc0fb8e4ec36f65680a48750e354b80bf257613eaa7c31c662563e5fb9960a454504ee6ae264186722aeb8bb4396d1e6aad3832e26887f4e06c295711af2efb4630b750369ba30dac687046ce95de409a3ef2eceafa8f26059d42371856c5e915a06c07fdc587f8a699f54d96577abaa420c5f25e172ec3f566792fed1d469ef170ab317e01673b31c350b8c3a4eae5f7d12ea3e231ab666f11c11f64ad2ce91da0d8fa95d502feaa2df26f778563eac77410d8b74df23974508fafbd1293672810b72ba8d92c54d238b03d601e51933c100a9f95605f62e97c6c5b5b994229958070d64998f5c38b2bd08d3e9e603912b6d1ff7a3814652e481a803e759767f5b0bf9fcc380b6cf58df50d350ea80877dddda5dc2dea919068f90d9c32915275e02cf78fde2a1ffd94cc2847f2664cb018de97aa2d30c00217e7f5ea409ddc93a03bb0be17850ec3ff4ad9a02fe869797222fa89f2d339100e32283035eabd4a3077183c2a472225a352a0e046899d89d42fae67b9df13a009332db8829e3f8e80cb8eccf1d977cce5e2e81d572af126541a3449012f2be71e7c5e8bf602cc1ef08b218a154c1744cd23f9715672c7359903216871606b78ef04ac3b3e717cf860d0bfdd810721c87b2e27be9988db741059972e4a0f90d3cca1e4b92e2d0d88f09069167d7a597df546559fc792c1b3060a3578958dc4bcde93cc6040bcb0ffc0a1a4eac1e26bd8bf091ded31b9b719cb5b958f0019b8eba702080e6def402230514fd0c128abdee3e1095b2f14576308a48bed1b0a6763d7771175043a7c3ec06812afa28bf443fd43bd3b4f984b385acdb2d726e0f6702b48e08b2abdf540f0889df6d5bacab9fb89307113cb0f931c912b5cec337f1baf6a684dc54b11f540e875d2a902282cad2c9388c78f88d2c711812e0bfdf0a013d82149b7c000d67086d68a7b79bb80d5c384883ed24041852f83de125c69fc0a15184700738c52903a786726f66841179e997c122e703b8af6a3fa8545f7a2a3c204f359a998907060bb02b957a4d1c2cc738d24bdedca11d12b90b79bdd8732c5d506bf5be01f40b009fcfca336e722d90e1effafafee636d80a5457810bc20e253ab0259c14e40f2fb79d82f7a0054653eb9d1ddea44f4f2c5751015f5e34ff48705a2e06caf7046268c412e1d8450f374aac2ddfcd730718fad50467ef341db2d4e1b92bea940ba480d937044eb6e403ff520b8c45967d5c09a622795c596410d729284258330791aabca0730c1e0ecaaa1d8f5f8827b88a7f4edc14f9fcc018d8b3c823c07403e80abf7815061701c1314dea4cf90834de80d572cb5dbc7aa904efad7581240bb0648a9496bd170fd81ef5c07634326987540f828085a3b0c8f7c379572caa0c77b6c1c0f7221c5a2179e53aa6a94c5cab5f05b26472cb8a1c45afa97991970458a18d6009f5321a3608fafce6dbd8c03ce3a74a24a425abe14fecec188ba3016e33d00c3fdca5e09ff2bc79cbb9a7cf4a4982edaca5172d428f08a061b06b0e11a10b8c971a4b55b83f50a8e905f2ca311506b6578b2f9593a63459312442034912b36fa7000365263c1ac6b1f520ca6d0a0d2ddadd7920befc898b80e42e01937526a65552cafba142d888cc9417e4b996429d623561b9c3b8ede4cf92b10e6a74f406bd634034c91051bd0dee18ab626489129762bc5be7767930df4b3e0adaa4f223d4442cad7838d58346370208f44e05103c723cffb857a86eb6cef40d290c38717c82bbd5dd2f5b1b110d6622ed3a85209123c61a624d6b6cadfc0e02a887148cd890c0987cff7991c86041542d149d30b073646b98da4a2625bed4001ea3a24ccad76bd949ca8aee98f1024c497df8567a9d8ed29129c8c6fe46af08c4941c6ab24fa82fb5a5fa123abcb7ce895b9e4d9c2af2e933438f7ecb79ca0ca783e0bf440c18abb5e6b5dba23afa1601faa74e46e8b638452351985996fa0207d184453f3f002ea71019de0b289318e90ac236d0a68bb25128b1b331b1a10c3717e5c88a344687a4015c366347b93d73446c80c0ce421bddf0bbdd7ed567067985abddf720f0a7871787e6e0d32ef3e3eb7acd0ffd23fcb66687890944f5014653ecfd67bc0b4042f8599796a0efd416a9dedd09f3ff678ee07460920fa900b9c48df26351c292358aebacd1889fe7682f507cbba7455635626d803c122f07240cfcdb2e7ed793e637a8e89c8416600736b14bb370e2d46c9076d04636ae07c5e5c2ab96ce1fb599803d07272482dfed6ac9d46180093586f7c4b20820b50ae6720ae6b3974ffd571876ee54ce46d7f8bf5229514adb020fd682b6b512cc0e2a14ea9a134dcdaf5da985dbfc11405f1876398d5b3178f96fc63c23e7cae40a988b3662afc9960bf2c59805006560abcfcaa6632d173e90cb4a9d89e579c808e0e63976ad7a65f41653cff56a95b9e2a47e2f82916e7ff202ea0abb7fe4950d8a962c0dcbf2c209289fa0d9bae16234a7a3468205bb0808b20ac27ab4b7da0c16db7d8f6c6736a21407c90d6fcd1af713b26bb163546d805509eaa83897c00aa443b974fc131870ec45845b5b2fbcea2ef2302f80d531fcdd7ac14e0b22600de97bad2e9ca349dc09eef5310e180aac10d3d738fba9d41929504fcb7125f906b578e86d9d245124d0d4875989ee02b19608d170116cbb6ae43121dd4ee847013f2729904631545736486d331c9c2488fb799549cd6a04484c79381bfa621706afe8cacdd602ca8bc4da2b3321aa1780c406e91718fb363e1b06a24f34898b0b8732d9daada6302c2b681ad80c8e1d5eddea76dc3ac594d6c61a6334c88eb207f2c2bbb15dbc8699faa4e1c9a841391469ad3fe049d2100fd630cd6add4bbc0b6955e09b45e5065b1d464e45bf8f39a9ee24d797e2df6bac813f8803242a07008dca4f56877f9e5a4ffbb7081523b05a6f72142201ad7d84f0f9d55c5052fe024f0b27939f9d39753e8a003c9ea5fc92c100fe6b6860431c1ae4116258ff020d21e2ecae70f01da130355ecd4b23b30efbc97e53af7cad22cd2cf45b73aaef038de8635b23b6313b1e6d45b57b0c00a541539e347958a7082a674b8ddcf8950f0b413817da446658078d62db1dfe91dacd1c13226797aec9bfc9cb7f9bb1a50d16372c18b18c55899b3e98a0cc9037499dbe0850040915eea2ec1c34bb673b02 -generate_ring_signature 39fef726944368533d166d84ca3f2ed35aca6b33e5a3ddcbde30f1a60465c61b ba1907948b591f4d5e7faf586b3f95d9d259ec089ba3ab71ef4b57beb3e5830e 4 cfc47ad6292db3248c65c78fd2db49eea2c2a6ff4c6f50771decac7631c4f866 4454dc103cf1ca4cfba718e2d34dd2489b08e0b350523dc9477dea5f16743e73 74d30aed08a71ab71277adfc83d8e0de28b2eba61edb0e94acce6309973b6d45 2aefe7ed1319282d5e1e01220850c933406384c6a931b0acdcf4957de3891eac fca4d6c4cc291b9db974d09df386a7742740068d0db8e96b1b4faede19f06e01 3 46779bb309eed60a3d4fa0c97e7c953b5c17d56b68fd640072e67a0ab2f70501f14c793290f7dc314ad6d7f7a515d400d6f2e6ca28267086e1cfd87b36c0ae08b207a9a0795bbba801eae04406953f67e1b1a8b112207be1f6a7577bf2bdce04f0ab8eec62ef50071cb931be6b5fcb18f12085940e6d82c012af5c9f72c7dc09d8a7fa0401ae53e0bcb11b997640d398fcf3232505b1c6acdd1b2de5c799f90427751ece69a45c76e2626e115c8b98b7c462db0ed970c3ef42b86e4de820890b7dac77de5ecc7dd03392583f9034d42d383321bf65ca3dd5918ef99a4db12e075bfcfabba3adb845ae04cb952286416ca5195902d7bd34942d1250bbc9de1e01 -generate_ring_signature 7f0c29493bb36b01a82a2f7077d547890e5bc7831ae6a35a2f31a0f902d4854a da08db64452bb864c36174d98a4e14781c78b7be3ff474071c0afc444fec84d6 1 bcf34b4ed58ed15b02861434750fb724d3101346d5e8303437f56bcd5eaa5c8c bfe6a1b8cb2a570d84f6df232db97b7a8e4fa3dd8e6af54d09fef6aa6f67d305 0 c7a0e607f15e84452d9dad48cffee7069b3e41c62bc5254bf7a88cb8f6b04100c60700decf672a6c52b3817fc5139a2cc6d362adcbf05a0fccda8ab8d9db1f00 -generate_ring_signature dbde6983e1560c26fba2a47c5e5d55dc182a12985b43766992a1e1f119947126 bd9bd3dba5bf6a2d3e986daf25c2cad584ae7c3afa7c27f5a98bc220ed0378ad 14 0f89f5a886f94ae576b8e5d8b947f0e1c7430d6187b9cf04c718ef3e99d945e4 d3e5b4d8755f61d6ef6943f8ba572fca1041346eeabaaaf4d53b6820cda40b3b ef305918a04cf633ab888ed026dafce004b79e96ec7bdbed5ed73e7daa14f5d7 bca88a261ca9d4ac8b51c8880ec327b1893d829605be97a299e9f29998a90d42 aa06e88145690e0e77549fac710a5c2279c1cb330203a88efc44dcc81465ed19 a2b43e260b1ad8a55ee794ed03fad1cd4a7a80371fa5799451593dbd4d4c7685 2fcedda6848e5af9ddbb3c94e181c8153a6229dbc896098e1dd4892f4d4db92a 117ea92dd03c8e8d8b54d680f30ae97884be678c4a868e672c5f78de1012f66d 20b0511d897bcf5c00b6d601dadd3bc7a879fda5fd41644dc07015fb95de9a13 38ea2cf665769f2661fa7e08165a9bf94cb807ef946d2cae10fb0cf314919620 d7d6c773b3294e5aa5436ed9c61ed5f03213fab5b30b83ddd0e7c12a412533d1 de39e3972890dd3e175e30880c96f497e180070bb64d726b968970d2e3be7ff4 fec305745ffcfb15d1e52cb29377b9a029b555b6a4470f0b8165e32d7bc8afcd 623eac57d7111b44d17da4b98c585b59eace5b904538060f2e4527b2f08d7d96 7817e9be56a20810243bf96eed3585470b75dfb717ccd52bed820321c9999e0e 13 c4cfcaa8e3f1b2bafedfdb1f7ccfa211a417d7066e32f76d6eeef50b94c1f20ac06a841829975c9adfd349aca2aaf13b4a932d655074654b6d356c0dc95dae016154234d4bbcc655da1b70044a42fee89d33dcea06c61c92c372fde61515d60be05d1a95dd4b876d0dc0755268a22b3425a5691ca0b3cd94c9c49fe1fa856b0db1c90d0f4ba2ee0536823a3ee56e243512b33047baed42e45c53a30f1b6ceb072c7f2179d7b4569142e327925cf6a2671470ab930fd435a01d181b74731dc2005f14908b74638aaed591134d0181fefda1231282c6db8ede6d932ac4cfdeea0b5e138f4c0f465145e4f3decbe0efd9c01398ca9f0aa3c1af653934af90b7120d3539e9f388abeb832c8b5d9ce73b0dc8474aeb061542869611e59f201b2fcb07cbe224066962c34acbbde66629dbe9d89707d385aa238c9184775d06d70d790dfd8ef14d4ba94cf504c1b6a9b311ec8f4c4037ad4fea31f83d7e48045b4402085c71e988c19932a207cb9f39fa112d9173742901ac4cbd37a22df6d810209106063a5e17aae1e545a72747a23b0c44130839b4d26db59c56b7772f91bbe26105f33a401546be475052bccda69a8a0cd8b29ccef8ae56370c37c9b8932ff185018e5feb6355099c79065e480d99cc57e5b03d1ffe7a4c2da43001fd1cbea37704b09bc6ee66685112568b3674058677f59075c98ecbf6d157ee18c5b6732a6c0a9a9fc9ecc4040cfe69d4ba083a460d2db338bde4d5828e7fbb40824e18e21508674806c738bde7f927e3df9bdacf39f94338e4daf331874a5aba23365bbe6606d92d8b475575ac7a37e7c94fc6d7bb75945b4877d1ec12b3466f4a363f9df2033136dc21bda7877c2e5730afb30295919b0c52578e8cc5ac235c99d34a43d2075bdfcdfeb1746bb5941ad0080483225fe8597128467f5e7b9249e06dd32ecd09e7be1878d78dd04af525c2e21f69232913986184594d6fae597d77d07241b50f340213c984fc70779a00c94dcd5352b2b7e96c9ffbc625d6007dea365ab98a0727ea14bed3c17f2a1300aa5ef6ba9b84f691e83b8c944a2857535d40c9a318059e526631bfbefb8ea6a4d93d874c0fdc88e6a2fab1c2f79c19ac08f7e354dd0f92e65f54d73ad0adff902e9dd21c08c05e30da1f12a4be48835401860874670a4b76ae4ff24846977a48f728cbd141f99b12bbe85458a8312c0024580ca0570395de76378cc73ac4e968a4a7ce248436f7562380fd2f45baca915d6f99a8930d -generate_ring_signature 4a049f0f8bf7379759bafc11f3e9326258df0ac45b612ad5088bef00d28ddf03 d4eced528b619b2c7e3478ca4325d630c3e79822e59327310e1e58f2a5bf46a1 1 8e1cd22c64de56e319bddfd26af3e8a6213dabdd844ced186eba1fead7172564 ec5b99eb1d24eaf612b507786695acdf1d15372e3acf311457d7197159059d0d 0 729e9adfd36508439325678207f24cffb868a0dc4b9dbe93e6eb2d21b4d0e10a83b8a5ea0923d84439b2fe3118d81c341d504dff11b9be90b6cf51b62b29cf0e -generate_ring_signature cc597b17e1ff8657ef92c92504a0a1638229a7960b286503597687714d90c0be 55ca58a01832cfe103ee846f2a11e237bd11e22a4baebed2548e75ceca9b2eec 11 02c74957d08764bb2dce5202466669f53b66570d75afaf9d6394bdf325be7c93 7aef7a7f9f34bd0716ea1483a0a4372b437ecdf14874b98455ebf4f6ea4a2314 affa26c8824fc326e2024d05c1286ad0797e4921c3c75009f80f965ad0b6061e e9946e75782aa240166ec2d39b2232303b0f79774d2a9a08310b9336eeec17d3 a7c3963b8e1784fd9004e58fc205b2de95f71d24ccb474cab1cdb15080931520 80a6a8dfde031883f53bf598dc1c47061487b20f393d0a630f0b15fbc71449d3 d9583c14e71f5d28c474e25fbd43336c5542ee1a4ce603f5299e0273f889f9d3 2ecca88312088b7e4e346b72d37ac2c9f312d4d82a842eb9393ebec65a0d7fe9 b59e25c663d8617339865199c0500feadbcc5e2b9d00819a3b91304266cc98ed 9acd42dc028bf3b39fe1fbd98d8eb7c7c3ad85acea86efecce7e41831e5a0e03 3304c25e953f30bdbfb6ed159e4a344c804dd52aea4d104bcab5a281d6a4a594 53cc0ff1070cb40bce44ecc8de8f6b189239790afb0545d951992845cdca0b05 6 679c8d32388a577be890855b18b1712c66e8faf1484b73a9add5c0221a61430ef7ed2c7322647720f4a688cb917927e7ce62752fecddc18ad4170739ae06560da1ad8b3b6eab2b661e508031f801f40ce4904c39c9c025105d51f6c18d1b3d011b1689ddd2be039d187ceca5fed24d994b3f646a4955e7eff719fda67f571f062552046859a3730d25b8386776baecde30baa9db2b195c3cf07ccc70d97e300e867865af98ca97b08769e046aef397d9857a3db45850c31d2b9fcc3fd5fb950cf55f36965ce02b8cf366578b83554ff571caa61126e247b38a6e62e40e66c90ee9495dcff99ab1950e24ce34397947200cfb1eec0f8db976044c529b10b3620e0a6d0545cda31ca43f8e934a531ab805a03ab401b5e3e90d93c34f1ae9e55d04f5316574eef4b860691cd6ad733f1872e2c9d2a5992b925ab64cb1e03b1ac50853abe0acb8137002d03bdd0da920ac686f2196f707a4b27ee78a454d5a126e0fc8b3c168b284bc38ba02b4beb37e5c1b25e9783b716134cf0c5008769e878b0755995231f5b85b1a6776915d9f1daa9c885fdb6c5aeee4b216e745d177a6e20be90b2af615e68ac4b653f8e20402f0e71a70a9a4f4e78a19ad8242bfea15670417d6be5b580fe1e751f6eb51993b4b77e276989d082b7fea4864d9a7324d7703272100e82b0231e7b00d00ebf2a3df540d714a6e99c09ceab28e52ea45514b0c99f3808cb0b6b8e2cb220b15ab4038b15cc9f3e5505fe6fc0f8dcf37c310440348c32c4deea855b208830653b88e2006ece357417cc769148ec683c0cb6f0e05b2d285980f87228ba801dd02b36e96ec4f6310c650deb2e9b8ef69d3166ca103d17f287373951d0dc70fe4ba015ae3c24584ace21b92e151746d6f0d5644ef0087b1b0017248267f03e80b316520823d15f2ba68807e0ddfd8392544f551dd0633943c4f7b169d7169e98c99ef9c4b11bd0e70790bd849ffeacb598000fac90d -generate_ring_signature a1a10532175cdd68eeb82f0f46b27f97cc2bd57ecbdf6e339a92c61d67056fe7 a7cf65fccb2a26a3470e9f90ef57f9c406df48f0dcf5e9819553605479d54cb8 1 516241f236a13881ee4aeb786cf686b340ea9f3dc8f142ab7939b63ea948f9d8 0666045383e7f9829c827c19f36ab1b57e58988af20ce494064ec1783c85fc09 0 f6238161808c785643c423949bd05be0d83d418c479adc8ad9a4b4120f09880a98f2d5a02a9b70491d2223e45e928e36f981f3014d323cfdf40f82d26ce2f502 -generate_ring_signature 33b853afc4a8fff7ac0b290b524bc23068dc3bd098dad3cf44bb96470ceaea6a 13b719bda608fee66c92ed1f005cd7993c144ce764fbfaf5349de4ccf5b6c319 1 29b8fb819c8be48673c0b35b8d3dc52b81fa528cfc91ac486b7751b1ba88ea73 bb666988157a61e08efec906b79f3e1321fd06d1f401712c0b4c47a98de66907 0 36f668c324361f15ec8246927d1b60434e5e2ea4adf6ccafa07c853fb3d5c7084c89cd6bba3da896045493baa04e3f54d2a7788b9a8ed056a4a280a4d7c35406 -generate_ring_signature 7267598c7cab03345e1d5e770ea5cb4fb41d934079557f538b0fc4115962e89c 72a4cb672b3233588ce82c8aa41613f0e1f087a370871ed79b72983d414d2dc6 20 1fbc0e3d207a90cfb27c1619a4db82fa106454b554910381521071121f7cb756 3abb94f2520ef80147aed005c178e6ee800ab93d91c284f924871ad3baa3d607 180edd37508ccb4ae708ea73f0e56b0bf4d4a0367a7fabb67803e1e2937aaf70 947f5548e9c8937ecb3b13d7f8c17889369ab8302ffb140a742659c2d1744043 54c8e1ffb3f50f9e364aef8d7164980cec2ceaf36edce662252f53ead9a784db 613208344207cd0665eb79c8e0ce0fab7f8b864bd49014c7d3fd20552194b947 338aca305d87c2f8792b82136cbb7396bdb218213d5e282bb4d29cd713389a33 ebb825b0cb8392e3dbd324e009fa3cf5f22662c70b35ed38ad32155942d2e7fc 2a6a57bc6115aecea112235dfcb6908a5305b54c3e1be6ad47f4286b4d94fcd1 1e9131251f0cbe92c17dbf1cf67836afd9dadefeedd0011f3d53292e889e750c f1d339e8dabea718797095a461fb2eb70e536fa2690a639d486c027cf5af6cf5 4f4f6582bb2e5eea2f1b799635680b34e5c7aea6069dd434a5b1df7ec53eed40 00ef708cc6a3843468e01fb87abc5a78fb3e44d1adde671ef9f977652d93f0ad 1b441c5ffb9d966ebe924088aa42718a58e5f21129fbb5ab4710b30a5eab0bc1 28ea41a63a7c05801ffa3f0775114b52a07540e024e4f437d15c6f596999a54c 5d1635b9996f202342826c0462a030f4a3926923201d97e2e97782dde4232e8b d63ef61485f58a8a8421c1030c63b13e7f9285b3d17a97c0ec617156f3d87e12 22e861e8e46894f227e0c5aef38e7144d61ca338a95c8ac00c222afc157a012a dd99fb211e2e4fcfef75cb569e9a5f751bed5f29c5f69e4afebd558e1a915b1b a79ebc1f781ad0bc716d9d20ba4a4be3a41babd8b940b57419d4090ebab97ba0 77458fd9ec7a1208f2ebed73ce83b8910072cd2e4658cffdded55675eacaa305 8 c62b339d53de40d56c3942f0550d25f1e416e705566a40ec90c88b72e4d5f001c5c8b309633dde663307faf01e9ff99f56a1cf1ee7768c3007ae3c21aaedc60dc5659d2d3718482d494ef6c0216a9ac3befedcf4aa9fd99224199721d58c9e0c0145c47b7ff0f081004c89256b9c55f59270ae5b2f02ab5326c2adc6d67d0b047815d520bc20b60a60c16d5b14f70d94ef6e2687c97eaf6da305335837970d0e3eb0c01cd0a9cdb21d2d7bfbd51698f7d214e3c94d58c5f196415ad087dafb02adc93f16421dbf3468a3851e331affd385a92e934c3a9632547b2508bad4ae0c0810f69bd2833321cb7e598838567c1e17a03ddac83a4b96ad6bf32dd6b92a02f766ce2ba0cf3c5f66935b804875d3d928002aa59cfd24b7f7be13996db8bb011a8e575edfcd2276b27aa06ec206ecc79bfdd2ddc949502f308c6af2c8eb7d08bf8835ffa831b02e181e7ebab853c31d0ea22eeaf99cae8c6bb4ed88861d47020eaed6d3b338d822eb7197d6f9ef53b954a1dafad6dd9189a25323516aeda1090f0aeeb8505ebc49eeee56174512b557904a592f3b6aae0a1983c3f653e4cd06b7ee078b486f33e358fc9de4d81a1d4d85675c148bc13b7cdba697836191380d1b20f67e8e3e441e2259ee13b98497cc0dc5a9d478f3302ad2df2f24036112009dd353f37f3e9c1ce5bc236b2113803ab02216b3e527c49389ba73a4b83b4c092d886bdfd32e278335545a610a70f97d7db49eece2212b238ba4a5999b08250f1a53a0721b23f38ea85d7aa009edbd7686d437badd541d42d3c07e3cf73cf008f83f89e2f4d2c2971d249fbf658704ee0c6ffb4b02c62e790092bc3d72dfcd0f9a5e9709b689e1e3d2cd0939452c34c9c785082d546dabb566dbffb4e472150cdd9aca954ad265ad45f9d62e965022a90f01d86da325af3e8081d65058c6c8023e0966f8796eb2b47c1163130b934de10620235fa343e999b81e8e44dcb653033ab400b2ca451bcdb6f5f233c3c20f66016b7f7942e31337f439222e7675da06332f7fd2c2bc0b3b19d28778cb8ccd686c0fb7c56d322220de3d1d2f7898880d3962986c56a88517948a227d6a8dbcdea78297ee9cdb9e9f68c85b07403a2a0b7ece3582800ca0fa77b4b1525789ac45c89049ed833a3f7fea632b8c8e1d0206c2a4bcf67eb3602d4ae503cc43c65b246285ff453ecfa85eb0d9e0e2b369d70f71c4fbd2d642cc0d3fd9252761e95b304a06e76f6cc765f6f6d5a4f6aa79af0a43b368cb2b88770f8a7643efa09411b58119bf64d75058dab500f6ea3c0af709f570f03e4e67a16e5fdb4c8b8034a268e5a88f21de686bd2949195f7dd09300265f5f32c95960fc5f9eaab17b707a466a9b530f52944f4c8d99cc4c23253d80ed493a8926f2365d316c6b78e69663fed248d0e68aecc75d3db030a9eb5e9af0021634000a349391ae44400541f00fbb6a05725cdff629e5291c952f59fb6fa09a820494ae06fc83cb5864cf5a80faf3e67a2c87a822ca2e30400a76f476a840e3eee6d6841295f16e586c6866245146e4e72a3b92e3c1b745e553de9d2d27d0e80330c68937bf4d9543cff69e99299ffb5240c866262e96804f1cfa7343c0e0a730d605834802f345ec6fb24f5bed48933ccddd25ae0d42ef4d37c960e485a0e674daf8323bc1e3f6043a2c37694f7aaac3916880fd361ad17027818506e0d07764677de579a55ac4dabf9cf1c740147daf29fae84475c076761fe6a228fe20be2312971e39355427bec8f62c83d88ef9c174af30ca9db0c5c317c1c07cf4607 -generate_ring_signature 43790ade135a2bf9ffbec0b7dbf243f6dcdd40eb7b420d849868ff7aaa0f67d5 f454c7087f82361c3912a9120b4226db15f0d752d96a5f445e19ab88d0bb32cd 2 5513355588936c635e602614de59f0dadd566c4e7a77fa151830e454838fb72c c00a25f291c66d07365ad72d80b0562e7a91e990305a0bf0e5b01a0db499c8e0 30c8fecaa0b7f217aacb273eeae2ae495f65dec9dd62c17965c5f46beeaaa705 1 7f7517aa6d8966e19941065b1130a98b9c32c1537d4caa1cc9ba7ebafcade40de7881fa0a15756e90b93b29c8932b1762d080f298a42e983d154f7f84cdda8001f4e6ef214ff4b563c331e6431d82cf6545250b7ebd0e870aa5f5f4cd8d3d00a7b36f232cc5ebc2f5c9dc8862db4ba9e980ae791ac257e872058cd3f86da0e0f -generate_ring_signature f745e1b2dc48174a463a7b510ac8e6bffdc6303c2816ca05195d9ae739db3ff5 eb4669ddcb40d449366daba63b22670bf36b64ea51970a796f07fbe78b6e1af2 6 fcfa341e9f83038e4f10b48ea65db8dcc728b2324140891a0566c0cf16017af2 3abe9c454ab3179b0fdd96e644d37285582e54423cf27904fff0cd060e3522ec 202812d70556c9afedbfbcb2b863e47bbfa43a0f0e366d50fb369cfa9ba40496 f3280be164b445d7605ed53195350d332b6c4a0425993782d6f5974520f433e0 8b368764599684ce8308969573d800ea8e4e9dca46c25429005d23b8c2ef0f67 dac4c7e0b261d9ce63a7afd3a1e7f1d3c04682287567525b1696ba305a26b42c 40e0ef7967fa1af5d40c8e74b5aa2ce841b04634e8eef869a3e25b4920c6f405 0 21e48e3036319fbb7b7e4a164381698c863607991e66354dc6ad1dfccdbac703ccf8e86fadfe87d8fe8d9aecb6e7429118c661c02afe97e2e3fd338883527d009a66c1cf0cd196aff556cb429417458f9989c8b6ac9a83fed4d4fd2b30c8150ec579a8f9e1bde499bc7e00f67f294cbdecd0956393305409e9eb38f74c5e4b0b4030fde83679c0c498da914709541465edfa43dd2f181f5f50a2261e00ba9805fb4d3e77ce3bea71698f0230d30a0df3462dbb04ff7495c3a6527acd34163e03a0e39bc63d4c8df652321cb1908361aae17d7f539ed366af353225ab2fb3e104ff969baae5083c4f8ce8a5b995552231c96b8fc3338987120c7462ac3282b80f9738aac0b380f80e58905ed86e7048c9c8f7d571c210cee03892bf2ef603a808c15cac0c70ddc3a9da893b34dc2732a74a0616ce7cbb9395491c380e47b8f50e0d54de75bce6d1fe7117480c106ed73287e27967ae78d603f4b1d41031221606deefef29209e04ff2fc95e260778152f895cf7b46dc59b98c37de30009555a0a -generate_ring_signature 0c1ca4f91f49d1955400c6f4f0ad43a05b4b8e5bdf8c10da751c98dd85c47d95 ed2b4cce5bd48fa761dc4b884062ebfcb4d51dfb6add0bcda77488c533ec29a2 1 c31c24cc6088cf25e09cab4318376a3202945099c5b2117ec04a0d03da2afea0 7297a530706b0a9826fee21337f341f3a7df6d4b53e78d21c0ce596ad2c4fb06 0 b10fface876a1daa614d6e318762e39cbe8b910647bedc5e865f5c128d848101f617ddee0dc15579a431a5381d1267947087b8ec3ae09d82a6c7290b94c46d0f -generate_ring_signature 31e7df067e668a7056c57de9230390b3b21832b0aaf26c1d136442f138615387 5a53279d3b67d60495984b9cb94036d0504971c6f5d582b8a9c868bfe5856de8 28 d8e11f5cb385732a6b28074afb6337a101560a0015e9772d3f30d3948adfa268 4da2764bb6febfd8c8e04b2cfa881095a1311336854fd54fc4b6b1d3b50b3087 03601a88b19d5bc27bab15b33d1ff472d91e7a928a3ac537c709dab71bd56e0b d18d584e85ebc1788a95a43f24242a33cc9fd281a25029934850752ba44d1cc0 a2696d71168201625a49011911d328a514d6ffc61600e807450349198bad3d8b b1c2f0574ff45ceeb330de965a3ccead9738da629a941436b22f709939a536bf 7d024af0ad9a8bc11b1870d4e4bb34a3aa7f615b47728c80e366899088627861 5e16ab4a356ca7682ef9b89c2ff32ab2233393c1f6f8ec39945fd8f68d29df5d a4b8a7093c7b2f11f4ef98f3e17f398c05daa7f6778c4ef6bfe8da1b6ad8638b 0f3c7ebdd53bfd02e2f96ef56487d96efcd6c6d791486816317d624ee90f31e4 744845074f17c4d32bf7b9c0d4580e94a1e86f2c5521c5f3a2c08a815a0978b9 31298b4531ac19393a103da8df5afdca6312fdc9bd467bd488e4aafe0336563c 9127fd630d5000a395971aa0d908cf98a5c7711e0f01d460a2a7684f52d059da ecae915500777293d1a23bdb2d70c17c964b9a63ff8dd4ce6dbcf45772c3ad3f b48d34c124988a550b37c4a1d823017ba38b39b790880379ab0d9cc0031f934c 2de61fe61cbcaf6ea1f10cb8ba8f4fb8df780cc00c0e517bcaf9284cef02bc46 040f04ae028b915b6d1c7d906be11eeb401f7e352def5f7613e211fb0fb558ce 551d509feb45aec8e434baea8603a2ff77b5e3a07cbb0d0b527c8558c4c64708 d073b898ab4ee3836c67d0e5d1dda2085f4c12aa416c81bc340b85c3b8b6ed52 435503b90a2d14f405096ebca6b42282534834fe9ea39a6bba5a4caa4f079766 2acd80251b3515fb625a9a18d2154b9ad1beb8d8b010d8be83ef0b749c5dd6e3 e5215f531238d2c273de542c6f4fcd444e82134080f087d0afc64f6fc8bd44af 22424ee51118bf80a8cef77b8e113cc0330e24a3844dfd84917fbead9b8c254e 2fd2a01313fc8b08b276862efe9a959c3b1a19c26615d90530bb81a9f7bb86d3 ed488f41eab4fe62d9ca4b7b84264cc8320704afcbaa174d0cce1799d577e741 4294f3ccfe624c32b9b2274e0c12e55daa840db023085c7e25ec32e00e889bc2 0f8c0b190eb31fd4afed7e449e6110176f33ccc4d986e948104607a326c879d6 3bdeb0e9895a3814004480cc6f992bb3d35b747f697bb88a04147e419c5c2c72 3e8890b916eea212d4e1e8c72b2775339eae8c42e4ae8b43ed09e24c694e7e0c 6 cd09d8fd335c9962cffc1b0beb4c4bb0d56909861a9e48fb36be1cdb85df6c0d04609e1c875a9abba5e3a364fb84220d1b6167ce9301a4f74c81b335adcbce007034d7d9b9c4c40d4f06fa557e17bcf677848897c3974d9673cb6f6e07aae60521df94c51bb361f9ebadda58a80bf78cce3067250e7c298c8e89616f0dce180c66ca4f4977fc42dbf0ebf5d254598426f1d8878692adce098642a25f894b340b2016c0ee62d78dde7f73ffe497d6d618d57cd5a92bba7c7309c46a85865491088b705a69396b65423457db60533456269a7e1e6bee02faac417aef9b23f41e00e4aa8b4f3565585364910f17e15fece3c0bbec52f54a35753f5cae7d30742307c31fcb9f395b90aafcc85d949ea3ea53b9ddffc765c8e517997c84166ea69e08e0a22acc560d1ffe46abea60048ff30ac981369d4bdc4560c98de3fcb9ac63028107a946e39b9d8a715cb84ad90927b29cd3a7d8cdb5d4ecd183aa1f9200e10b57743f6a86bd6be6a2e659565acc1c89c59bb26271b61a0406906203993aa40370f46606423347847168618013e0db0df7a38ab9e89bb993744b1f40e32b6606255f6e7aecabcfe65c2364f936150a0a4c1ad04cdcb2f9e5d533b5fefe813f09cd6a1e4a551cde1feb1f88285740d8044bbda0ca89e151d78d44ad1fe0e656098115f2556479175643095785a5b1b6bc5a230a63009416ca0ae63abce5ad1e0e019ad1bb16781f51ece664dce3fc463630c7cd68464651d18fa3d1ef2cbbd40769c0040b0c2b996f408d43114a1892656afb03cc161e93fc94367c63ea53780a037b7aaaee692176a6b943edcd90ff8eae3da2be7f8f924a9ad73fe1ebd07b056bce3137094edc5a4bed78f79d9d733657fcfd83eba2002d77cdc4d4668c1306dd19c30266ffc0427822d32b9a93cb86ff8c01340f7ae43d139ad6d4aee6a50fe80729a7fbfea64b1db77779a96153532af38f2b6fdef06d91a069c86d12850163e8b90ecdb5475716421ec85c2c2899da8d858bede4202dd300d96bc912db0e24796ecf1373154ab1fa8c28420c118696b89f3543b419fbf2adbd31b2dcd7069486e8b9f28c0a2b6a602aace74f367cdf561591f6ccbc4245d4b0d677a4390b8d7782fea9ea4b07d75ec3bd0c3144f2a81fc41ee08dd237dabaac539cb57202a21831f51d1bb2fd231d4cf326c8350d04dcccd233999bf5854e3243b7e7510ca66b8050bbd8e8adf9d88645d06ecfa0ba7a99177a9d6432e283f3742be9c5098b67146f2b9e3e4b25362c560350cdb4a75f83a727a91480266ffedc40f5b20939f080a390963c0fed20c1b4ca4b0453ecc22d6e5c8f6be043d4d9368d48bb08224a021da213a095d232bb3233ec4bff8ffb280a8d9cb6de8b4a1102ae3de301cba68e4b833566a6f7de3d95942fea5caec10eb43b3c302b41b8e3e11f1e380f831c3656f75eaa22ecada377c5c21bbed21de78f3590eb185db14f4d9b41a503c87dd1b81cd6176c1f48a1e294c8a1b2641a9338f1c9775a9e3491f9ed641f0e4cd179e87c93203f9eb00926540e566b84a372c2aa21eed66ae4bd15d147ca059da57b4f34d9f31f23f126ed3701ba1915aa422ccd83c378641d35aaefe2b6046b5b401dc6b2fbb5d48bcdc3056b7ddd02f6fc93980a02381f1157655263f80027e17ec95817eb06e88130d78ee96781ce4fb339cb175cf041f116cf985c3c011e837360c2677ee9fec439721d24ff595691ac853656f89d5919a993f838d30842219ec47c80190cd5ad6df4d843ff8c50068ad62a075a0c084abaf263631203be2d938847eea6f6e75e8977bd3f23e4731284b3e5adec876c9a2e255afeae023b6c837812811e0b3e2c525f48ef7d543b3c3b2defc01c3f541f2b830ce5bb0ca919831951de7710617dfd7961673d5c04e14351d4ead0d00978edb5b69f030a62fb4da56fdcba2450c4c78a47ff2dec63d7004a7c8fe0dd319d2d64208664059c1ccaac719b680c214f3078eed8a9d4ffd8a8603a3809c2d734e07db607680af14cea0d37d1d3489bea91fb84a173ecf26bf726643220593f2aace7696b8000e7e8a22f64d640f33c59446b6691eb70ccca458ae134652661b7ac7f9dca9b0d7cf687315baeb97ff9d1cacadf4653cbd69e4ea0862c52315989bc6c705c170220ac23896b41ea74b06e7cad0089de194f4ca13e1f78b5e88a0b2bd26519c40fe4248e0b3b8115ccccbfca658156635bb5cf79a1c0f52e820b5fe03869a4e10c161c3214c6a873fafbee4d592cc76411034b8ab57c2b8c2500e78eb8ce83120c86b721a52fe8d11e2e02edf87bf602491547500e027af2561c40337f4f0e0c0c84e4e130a6a2394084416cd765fea55bed250c3b59d70601a3ef8545802e6f00fc24aa01e07538ebe359f5fd7e4a58e2a80c3603c85ef7bedc4eb806401d490fd930eba08f5cd80730f2b8b38ed1b6a5027456fe428b3b249d4bf2c8e23cdc02aa4a4f79d18bf321f723007d3dab4447c3e893ade64110fb6bf1a1040c52b70e -generate_ring_signature 74cf9d48387f68da96ca0b0be54b436b877a40b8d7ce35afbae4930e435245d4 21d51622f92eecdf0071d205840bb1e1e1b8b26d69942dac91980a694df9641a 2 b06cabb47b0111da68931631552e977c5d0f7391abd7e835d4865ca6b6ddc4c1 c59a24c4128d29bfc1d676496b3b275e1623dd226ddb23f8b4852eaad13038b7 880eddb85bcd967112fc751b1512b490b11abe5d054e43126990afc8226dba0e 1 2117033f93aa6f9c14a268fbb10806b96d9dfd441652978914650f5cb6c11a0ce7347bfa9f95910526e4afe71f66b21ab9d8cec18c5a84a644d92ab0f444030a0e5803d238eba46fdd3d47672342df4f6626a52c467d7207a6de697d1765960166f94b05ea182da1c4e3dc60b2fee2f07dc87f3091f0390b3561eaa7348d1b06 -generate_ring_signature 9207d102e70ef77349166326193c037511f13940a761e9caa6276c186b3eb2dc b935299ab3141f7bcea9047224771b9eda89b7e65d5bbc85ca163f1d7defb31c 1 764d6791a583a488cdce815e5fa424f70de370e2669d6767ddd3d553c6199343 f6a1a2a2a3dbd61d40508ba1f192c0dc37fe3d297f3df14c9730a6f084779706 0 8fba13972d87b08b18a4e6d8e5d734cc3e997acb786efb14b6ac68edceaf430c85b093fbcbb8fd8aadb7a575d526a8e17d031e4277b6d0e0a16458fa550c490a -generate_ring_signature 8f1205e1c7bb2c98ace5d21dfc38b0e52715a4dd21f9b7de40d9dc516c0483fb d27c1b3252173f413b070231ffaa38185f412f2fc8af2dfa34c6923146cb8f8a 229 52c5784d3cda705f4866716a833c47355b5fa4898cdf0f719014b1e06da6e6a3 913c3f015930f8ef9ef6e7113a6d9dc22bd50d047517e2551fa3eaebaf108502 6492b0bc2eb958caa42949a7a154c2900bc56ed651135dbf4b1b170fe9cd7045 15511be2cebf723622ccd3683485b7643fc64514f97a882d9254f4dcba06aa96 30b0cebff8941b4295fceb60692aab5cc46f9080e27da45272545311f81f211d da45fd73c50d7fd41205025c634b92098ac9d86dc163fbc5cefb2bc8c50ffc5c a6bf60e5242b2fed9b365e5e30a7a400c1ed7a182291be8661340f03a63ee265 5bdeb86000a7c98f5c7c3570e807dc75355909ddf07ced6b1b8a49b8ab5b4b3b 7626e7ddfac740421e4ec2f375da22498175b569cb9ecb7cd0ff729f17c77cb9 33f89703bcc1f9888f44ac7af120c14ac800adf93302e7fc80b6953318683540 15ad941b050df08d5fbed5646703b809a4b2bf59fc806f68be975a1659a3d67c be0d234f31dee1eafdb19d6219a12ecfb0cba4b276dbcb9c7926588efe936bae d2c6f00ce060342eda234645e3302365ec1b1634b24a3b48bb83f7c2e38a4c58 68e9268bf9af8e28e84ca36852a5cdabf74996854218b84e62fe1821a6fd3be4 de95a3015e45e1e1a20ebe69066248bb6d9091c33fb5f0fd99c99e2c98b3d9f0 175f159b521035a0d4244455f49c292cb6a84c4820ddfe691d34c960ca04f7d7 8c2334a87ffbdda21fb11cb805d7a0ed8ce0bfe34974660956a8ea56395aa4d8 c7edbee25c17019b2202d4d0536bed6c36c0cfe9368be0a937587792be18a73c 42b6ab41fecf4dca2b1150f4d8952ff6d3e26c124d1abe310be551f2d5f5275a 3622119839daf53fd79c3d53a87e92b7174a4ca560cbad31f906b8def47149d9 1ae04ed9d6599aaa3208bb092eda99dfc9e9b48f08b73b45211d907038f38aeb 01cc92d5068bf3d9ccdcbd5622d574a02bfad10f4efd4b38d0f443da3ae00e5b e862f2cd0bcbd35d523302201f9bc7d15ba68b2c03076e475005de87bc5bac25 0a9d2fd156f8c3e893f28bab84b7b9384e31a15d1131386bd66fc345e5599653 ee3144f7734cb223aca913d15b02900aa7122f079f9a6247d37341be18c7493e 49630627a6644da9ee51fadd02c54dba4abd237db31d8e7f911f9b3669c71ac3 cc1323b8356b0134cee1836eb0e42282f78e46a54ec1406a7d28ea639fbdaa92 3cc0a3b7da1acc1ad6d518029595b1221e9b632ac78a707ed136b597d18ea8e5 1437804f33e10dcf092b2e2de5ff4b0e541b103b05b160e6416fe5d915b3d867 a28c48c79a7694db14669c83277b53961a86332fe3e488074b3b4b637e7b13a0 0f352d25647efb575677787de2e358d579be0aa9bea73dfbe3dc6217b12706c0 ad3a5789bf9a2500ca930d64d72f5166516c71f42534055b3a945447bb0a4fa2 0e338d303c59926ff532b127bfe7f5275b6ba6110631ad6932059d028fa49b7d b68a76cc12b0825d96739793cc5365d02bfe50ceff68afd0b9865f65cb60a3cd 018de5869e7859cb475b48aff57f07f0aa8c1e1ffba1e8e96e87fb32c6d684da 06888fe284c0789eb6e33303797f365dda3cc8f2a2f9ae7754ea1192ce14d435 7f41f8ccdc6f2b5194de7e0477302d1fdae0a74239d94d481d3147ebff4f635f 1ff844011bc0f9a1044b23ab46fc2acbe00d939a9fa90b32dafb8d52e25c14c3 1559cd22ca6b489236f6854add12fc7c2946c2f1f02d378539ecc9305700f87f a597e24bc6f4dba4354d97eec4ae6eaa44428e3f35ca2d060b85bdc2785b2dd2 861635da1e9955e43980a21e4885bb8998c8d86a047f382f94f93da569985c35 66f0dd261e45dedbe26d5e060b60cda4da5aee59cc0ae63387dbed7baff10649 447477689524d8c7a55799d1133c47352dbc5d1e71f1355822fc984ad6cdc2a3 1011c08840a7436f9366091864d940e93e6ccdf7b04189907ae27e4d7e00d96a 954b68b72e15fbb72d805b79c8137d01f8a83546848f8e84d99d7e049dfda8c5 160761e382b3dddeb20b285a60335140f03f3d75acf9005a8274769d3d2b3b60 3fb8f5f3539927e5c4d8d254d696ad4a2eee6d8850d9ceae857851a608dc11b8 5597e4539febf5cf5572df1ae65e58ceed3d7de6c7d8f48d4fb0f3b895951b5f 5c84d316987cb2949689b99adcff5ddb82e0c1996f9ff5e7ef510ad3b679ea1f 2721a20b915b3e8b3263055b1e819947c8b6acdea2fad1d5f2799ec3f52f5423 abbc7b0ba883501c0d07f898df3d11080eb4780ad610e1e8155b4b31458b93cc bd7254abfae24aa591fcc630b3fe13b2f6ecaffa09d1f220ec3d2cc166709ced fc3646f77bd6635c64a53e50517ce45fc7a60e84dd81e4ca06235693d8cfb44d 9fcfeb091e1bf9cefbf395dba14848ffba2714efeb80f49e9b00b00c9f05be86 8c6ef973374deb3c15fed4721bd7aab0e6cb36eb543146d69ba5b99cceeb2ee4 3313b1b042d283088915c9416960722098d20dc06e0805f9d20dc2eb7b31b46f 9e44b4c47f110e2940b199cf3ffceb69c711876cf5f44b3bcddc9424ddcc6218 52387c696e8b95cf5ab7f9fc26fe4a66bf8e1d84ab56b787591619fa2c3e44fd 47ecdc3e664495b46990625452266fc65083351cee005e2a769986e161788ad4 93b8097c3c625640736d2558ff7678691cde0fc00fc2a4e0c4e7bb1b54463447 d32b0abad912cbed3ea7bbeab6db254e521596bd6fd944df06144a2cd685a68b 6b2a2312bdf27d04cb2aacf4a26994dd4b307efe7dc2b1393e815fdea8ad6e9c ebced6a281ec97f6f7ce32d1e62da1ead1173bb873770ccb11f79f9d196b4307 16fdba74bd5741106023583564de63f5b77fd0ae433253ed67d554bd7bcb2826 f1ed0bf4e6f4ee1fd98a9a9b69645fdfbe81ee7908dd0c21cb1860b62e356ed7 681c3a1347ad98aaec4d7031be4969b3f26bc09b03e30497d58d30bbf95f036c 0b9211db6b2a938346aa0ff7df2800f81b4c45a58e5d7689503d584d0a0a5b0e 7dddd689afeea27ecb36fb61df6a0f088152a5694f4cf4155cce68fa2397f6a0 accaf8a5f922e97e1e740b5e8f60674563ef8efbd6daae9cecefa048705ca80a a79aec40aa552d5d2c8564d40860785bcd12aaa001bb3c1beb6846908eec519e 6a8c92275c25fbc29c8b9e9ea0a7eb2d50dfa48836a11890adb27053114e730d 604d081c0de894e8a44412d641baa0e067fc038347ac1302aa2a9b0baced9311 0ada2bc88e8648a5e0c01a90a72ef1ef334849a5cec553309769ab2ff98618b1 3e89073127cc5f0fb823d9d0c8b7ff60974413a12754775b9fba0efc19efbb3c 01e8a98e41bc850b040d84719c66b985e798a624749dcbf8b358ff7d64c233fc 6feb1c7182cf86bd80f8eab7f4681e6f4af6f2701a024d8dc6e581b4a4168ec7 f8e67fea54bffd71c8629a90d50c7f6134a0c2b0c4526795ca2a5e07f146f392 069158fb4cc625c32daf2608bf603d73b7cf3cd344670e218f3e8e54ee691ff8 4cd66ad442a4cfc55b4d0e913daaf33cdfe7dfd1da22b6c2e3a766d2e60d772d 1d0bf5a9a0c93b83efe4143b4cd759079984ee4b0b56512a3a0a5e2c0c9d287f 4df2310fb9561413b277873f38428cb814f221dcd694af87c2a9dfe676a64b2f 370a267d62385479b270e46f8578fd2f68325de6b7c71f7bd6ad678b061b71a5 7695614d59d1ee67af6684e4e9ba5e82cef5880b53b3070a9ed5520757fb2f2b 5c95cabe47f6be580ba29dfc59a01fe24985b9cca5ad1c8f11d94a3c231efed8 520519e1a10836f288b25c7c32c15469bbbe2618d5193df95381d1cc32860847 0d1f46f19b2ddf805247e8023194a97e1f25a23d7c9fbcd9ee75408b369f89ca 6691f76d727a9fcd6729798328728e9b4991ab4ea23a0e8fe35566985dddc1cd 1e9f2fafd07dbe67ce07bdc4b81cbe6bef69b5f69e90db19f37bc88680d67513 3a44c7187849ae084e96d6e6fc5607550a96d597a298c34804edef0c611718b1 f6d7535c498c428a792217943664569f4c301481b400c11a03c03fc156fdcd51 2dda0f1b716a5a96014d1ad7c0b0d6c3049be66082acc5760b70db40591b7059 97b342dff13f2ec104afa7ef42af22d053c0d4724d5c150a600309f679783c10 397b9cd9cf85af509978ee6324ea25ce2ad46f4b6fffd70fd31376c411c98b8f a38a93655b55c9ade39d4d1e6a81b5e3014066fde7e81e91ebf5b82f6dd139f8 1cf789d950a4748478226d2cdb521f75a63c6dc3edb4cb561c2e57b38750cba6 4644dbd47839e1e94946ace12b1dcf5c64920d0c0188649ef4a5fd11b5075599 765b04257c56f566bf8748ce698d77f0961dce48440c50673e838a9f24233a88 6b61314fef1282d0eae6088f5180abde895a022e6cca980cc193597a2854ac6f c2baadb820f257ca6f3d48de4c9768c4b07c7b8d4f7342acecfb12becc6879ff 0cb01561b6391e1493e1910575fed3fc3c314a417313256912de21c61610a077 1a1c943bfd4be26fc17ab352e30988407bc7de6de0899e3c8aac2a575b84705e 0b2dad0d3b05d15de7f035d82acac91d21c7772aab52cf2f35ee17cd1f7496b0 7e3b55ad8e3a235dac80156c46e290880d9568752fc2843851e6de584d7fce99 2cca8f02b11d269de7a1790ba4ab530b9f0d893d6a6cc8369d183170d940e860 3a7191d183ca61fab0c82a531ba5cf5b1da51ddeb1bccc8a3a5de1b3a08ed590 3ffaca59a42c9a06585132bc401078b1e69659c151876b562918cad9c66b080c 482a252d0b29f7fb876666d565f5f67aab6d39f29317d31236129632585b2ef4 4163654838c2ba6d17590d6710cfcd1bce67f76b7298bfcf92139d2502b899ea 94a1dc49c5222983b4d843765d51526f0f1cdb54bfc9a68f62bee58da9cd0270 4887f9ef5441a1f87896339506af632b7d8a9c2df05b002b42c5b6c32165fb66 c53bf4a93bdaf03f2e3c4561357ff9a247cf019dc38b621b916f2c4d3395f9b9 f94b686e0d068cae20b55014454f50fd950ac9f84ca705b629dfde4baecf1ffc 2a3f77e3c18ebe3ec45879fab23e3e3f1db348d06eb30666e3a185484a91b7a5 5a40ba01f1427edd310b9408c5ab2488e5d05ce48b8bc85f8c0081adf11cd9dc 75abf56aa4921954d4423e957f680abbf1e3f458a1717f21b69d26bc7132d484 08a037168f337c2f30ea5db11153eccf6f2cc847e052ef0f2dcca49e446f9c2e 9a88ed5ea6035728e6cfa1407aa9467df401bf31e5b9661160bdb48ff7e1fc36 5c86516712f28f11b0a96055acd38d7819d497ca2e4283fec8300da62612ece2 b8a0708e9b6d8586fc9bc3279a5f0a1bc99505f627bbba52d9cb4e313ebf4d4d ecc9011f2f931508fd5b199f76df7bb96ed8c1345a1787567d80b249d2b0eb55 18275f7f1472730153d052fe574b8e890a4a32b012934ea236b3663dfa28f14d eb3014649405efbf8bfc962ee7706e9bee6a4ef5625eedeafaae0051aa721917 59fee3ebc15513a0c63a4040bf31bb02b6c20c9246d006b4f026c1db2e7a49e1 8bbaf1b46137d97273c26d93918ac0baabba90084bcd209a0a172eb8e7ef9b65 8b2d7272c1ae09827cf434578ad48fe3ebba74ce2d3fa00a35c55523e3a9de89 e129b195950d493e3c9e498f8dbea5b3e1eb4dea65fc03ac2bdb26df885e4dff 3671edffe7f5fa8fe14ccd74930c7ef620eaec25534f1e0db04046db3a09d3ca b086b636c71efd2445cf6281fb3fe8c858c5a345a6ab38314e3d4cdbe962c34d 7ce5992f4a5b9ab21f3b00bd9d03eb3b508c4e8d472f6ce75a624bf69c489cdc f709e736c33fad590fc82f9a7147597d3ef86a3421c0cb90d4f7824f19ee0643 0a758da68465fe3fae79ad29012b6035395373d0959fb27bb440dc9468683f19 5de2ab6d01977528e26cb98e22ce5205b76ec3fe33dfc38beb8ed1ca8d65f41c 0718b9494418118f2da64066b22439acef7fdf1e0f8b14f0522d42d4ecff4722 52ef5deaf9620304b0dc480da5db8e3cf017484061de018008e2ca1c2a6f0675 cad10726ba8716d69051940c377ac5f6a82e2e9a23096597cebfcf06bf515e86 9fbbd93d71ad8ce532ab9b2024aa3cfc17b89137e91f4c3f93097e8a34a10e17 d7d5f11eacdc7cbb053d15d3f63bd0e33554b9dd2eb8c83800f8fb9d0b2dde42 07027433acea249fa21af4cc3df4123babd856022b1a6e7e972a80602a5292e4 4b1cdfb44d28cbe272563682d11e1489f4308bdb836b6ff4a61d0d38be1a959c 9f35f5c9b5b382dc4b6476d912351a322cf349d8b5861df181163a46cd393fab 210cec3467f2c3cc24ef57caf3f7fbf6b7a8b46edf272adbedd8cf67ee954aec 6d5fafbbdb69bd02b98a2b4bdf24065092c0fae33cf6c6279647675cf20f5fe8 6814c20e384dd2298476d630badd43d8834560a06e4a5d328268f03b9f65f714 d8bdec2059ee9a599affa6e3548a00524dc582cd194a66b5740332d8404e43a1 8e446db99e8e579e5059ac28440d2d2c6a6f1838bdfe074f2da61805b213c11e 50a06a07245c8319c6f64966086e439e8445382b79a31513ae10998537568235 5efecc1a91a9a9df0fffb1050cb1ac2905100ca92c0ef87074d50e17a54c4696 ddf9201bb6ae23f18cf6ef069d183d0ecd22b08e481d2f1c4760f3a029a17ba0 6c1f2b0a8ccce1c65d4f411584c4a4811894b96a7c74dfeb4ed9680e4edf0259 300e358000c2b357e48e532f4e02dd6901dea80a371cf4b4856cb5adf2f97c5d 49ca50fd86f5ee0445bf9292fd492e4140e550fc6a520024619501179732d503 026d202c9bcda09cef16ca1c5dcaaef6da40fe42e533562c530e1b4a69c17277 8e4d82f0a0384babe7fd0a4326952955832a37d1152d0fd189094fd0922cf106 c3ccae4f7e60f4fa82bdb101fbff68cd2071e2c7c5729b3e9d7a0a6a51809807 e2c970b2577d34c1a4849fd4ddcbf48bc88627d51c8640b3cb2ff73a0ca852c9 054bdbb5411a58a04ab389997a4c85e38f2c714137252db2e130b80947c505d4 491f4b4d406f6e89833f4255c2f53103a26e3fc9f2d3f04ab6f5e034f9d16a3e d7f7a5fe433888c71ba0567bb87dd28d719e92c71254a1f84238c7a78937ea2e bc03db54203b141f413912cb5a0cf4aca06609dedd432042f8775aa2524b0a58 5e59b2c9319d9e6b450020f4b19b4c30bc5b2be4d32f4f635005f3efdb40ddc6 4d3553533863be129705f3008ffb8cfc0477860675c0ab53a642a13445185e4f 10837c90037eee815d50df2304f1c5f35ccfe3bef086aac31ea953fdd5b1da1b 5783cfc3d851b2043684d08af46bcc28c218c4800aab3e4a25192990c3a3c143 a529b861e4f2a800c334f542c958072804073d33ef707a324ffb950a0d9a3bc7 533de95bee68271d28cf19a0cc1eb17310c12b00516d8cbdf11a0af3d90f6ae1 ec2a1aac02d4a55ff2436405b7e821d9612406a4e1aca17b3e05f54039d47fa1 cfbb9f0fe10486fb902b329603cef2d1ed5be4e50557e366dd2d9c75cf535129 b0fa5d3efcee11d9e75ae5e77cb0363f64278fc6958114c6896c77f776ea456b 92562a6e185be15eb3dba54eba90080b2b556ea4e3244195e97c289f2936c09c 1130fefded4d6afe6bd3c775c6f3d92e7c49dc0b5f4a64ab09dd94efe5bb27cd 03df5ab7e0e605c91b33709c625af020147fd3d14594ef3928524b3e41956276 fd4730ba5fb0ba5e26f9bbcb8e24be8e92768b874ba84bd850d3d6c6bf14fa36 ff340888ba7d0ec25503b411fba67bd3eab35e778299f4c7b8c4685ca0e4073c 7cc168482cc27ace90bc3f551975052082c33d3c74244cbf423910d00122e760 b67e965b6227c63f7b1f7c1cac3f4ffa044b499ef2126be0e1874db9fa24a6e8 af6ba3f29ce5b209c40565ca02b58dd4380715f6f10d872006488359e5a468b6 4267ae8273f14e15165df0be29aeb50a9a3bd312816a9be63bb554bd6b9f82df 4ea2aca4800d87b6df9995e3d83240df97e309fa4de089f8b12582cda536ca05 bbd135a87a41a425b196297e060306fc2ea93a1aa2b9f3e6ed2b66b52f4c1f89 d025ed814116ab93c9bac3f9a4272502bb77bf9f71562de1445129e041bd2831 12ab2d4d0972a1c1cb4edb0c36bb60f3c347a8a651b814325f85b326e48707e4 99f3623de0fedae77f920301cb2380861f9844fdbf0d71991b0942331449f9f0 a0f626fbf3ec494ab01485e7e9e49f97a28784231070d4ae3c6b7a9df94c37ed 580d0cb132d0204f173abefcf7caf3abb8bf62bf65bb4954b29f4451f642bbac 8c97a982ed265af9a10f3f8093be8fbf23bf583fbc5b85e8bceb0aabdef3217e 66c73b6297879ab3b614c47308a3e64501904c7bb61e2896121b3c97b12d3ba8 33c536581435b1ab7a93b47ad2e8ee25d3358873c7f13e07e38ea9d1fde9581d 358af96d40f33c96c703a54970c329d9cb71777c66a41b3706b161730e178e6c 973bd3b32a458e0b238a95feecc044bcc7640de370863919a517629d26a0c100 9d3ba5fac2f6aa8fc46a7558bc756ffed5a1a2ae57aebf71360681fed878424c 95898052706ffa6d889c76a090e8244d2e6e7d339610a429da38c3e454465e8f 6604e03547ecea591a6d36ed604582cd8abd72bdb2b49e0a917771da20bcbaaa 38d80837cb55b1ce698fdd5b8d44ce9bfccad9cdc19e0e3b7bb7de9f5eea7833 16bb6df962e6127a9b053dee33aa890210c2afb63058011a95d5d6217aad5d41 09d9cd42fbb4bcafa7fa122ee44a13b420e77c58add73a305eb3c2b89f81348c c5aa1333b34398e500bb2cf60376e5dd6126f4d3db3034259fdf55bdbd3ba7fb 3fae5690d4a5bc412b86d7618d36dc4ec01009aad3edb77ff776c673109c7548 b28456140a3e985c226ab99a6c80f87afa3519e955faf49227c7de36f93d6d5d fa133b36fbfadd578d68199bd44c7e2b902c2ace06dae2ec52034875ab963d0f 3a5c007a828fd572ff8e0e713c46fc41f5aee958e186c1b88198135b116e5ed8 de33d23fc56054e68f333fecfca5f4d576fec6c500a494b008591f99c2a6c126 8db348260e9bb621ed1ac68622e551e852db4e8bfefb048e31e606e71cd9ab0c d909650ed9a50ffcc7f26fafdcfe91064dd187eaea4dff027bfb66d734a94bc3 2275d3df45c2e67a1831735a9852351d1e40d7903380cf3b33ca7ad4700ad8e5 9bf506cd884abfccf74b7ac9899e5430ab9be9ce1dcf4339453cac9308ab4867 044e10b3a3f390f0e44eae198a8fc246162762c0303b9326cdf01ae02285cf27 3423e004c3572f016a2711b9b7d5cfa4be8327185f4782a83e719fbd880ec73f fd57d3173d91f4f5b7991b70a4a7c17993e783c9a250f909d8a452fa1d4a7d89 4fc2721e8e5cb0f0e49791437299a6969485895295be74f63d8253c3e0897622 c370fea6b41b19482dbffa79495759699404f5b8aec95c42d022e710feceb4ee d76fa502cf3ca64f27d37460208b45d8bdb4343aa09f60bc86b43b45b3bca456 63fa51cc0c431db9458fc137148bc7072aff9e34af67204b0646139b5efb43b9 09ea32768ee291622d18b4ab9b182a0158604757150dae3692016c830f19b508 f2b2230fb64c40711b943000fc9faee75bac64677635f21b141fd76aebb38dd2 5e39938c9c977fbf84df5ee0f5cddded6d55f17eee0a38dd5bf01798a9df1c01 855f49af12792ec85075cd22ce20f3bcdae31749371e906bd96504b1662c1466 4fd587a7358b0de45ad268c237bb9b299d6a9fb06414aa30300e237ba1a3f72c 3edf61a65c3ce5226dd877d6aa70d639d52ebea7fb4f3aa7259e0efc8a5ea1ac 5846a56eb003d2b04f8d8a5cd02391ce10bdc43ea6114b740f590070d28a7003 c842f7022d26e8f0c9b832102efa259ce5e77ac2453fce14a364c590d1ec5d50 f90822ac98bcb91651e1f71f9c0849f821e2810743ff1e8e7aeae5909908f451 45091874969d3c9792356d245536a8da824e9adff04698cebcb99ff8e9e20f3b 9542ecf82586fdcd124636a4ee4bf2c4bebfa0f98dd76b45adac28227b0f455e 38c69a23cfeb562bf698b66058d99e63b5ecd7b8ca0b1354a57e9d60140c35a1 f7f75d344ba6ecd4ee5072363a6eff47161aa6334c86e4f3ff832074fb064b16 f7815c6dd6946ccabff6b93effcd8cdb8dbc9630ec5274dd476b27c3344438f2 b21ed1f01a74f7cc86f2ebb376b203a82bdbcc17a6acc4700d314012d3ca7e6b 54655a6c6b8e909278c760828a75b2daf5bf809aa10d84fc68bf4e1d3636d5f6 6b69083974fab0a280295796253901ac813b657efea7348aae3e2eda762ff923 fbfdf4bf87b4bb696d14bc2b350acd77eae89f267666a092789e0af07a32740d 38 907237251310557deea1dfc721208198d09a94253a3a30776e20b66113406409fe6790d40e369d34eb1943b646a7ff21093d83f104bd02639b10c6e610377904ab0194078d927589f666ffd760f85b2731ed5938046c63b097abf669a46f0b0e5b12cb2ba21244972d9985f1963c855be9cabdc52ae9cb94e0de7456150e2f05d89a56fc92c92a6a429f7b6829c0d4b99477a286fe394803ef09e925ff941f0f615cf4f80cc3a184ec95bc1a9d98bb8d5a4028a323a328048ed91a15b77051056965f1af13038312d91b59adccbd95914fc4efa5a3f91b7d17703a3479ac500176115cb9953da72c304ef4ad7e2e373ba8e88311b7c8a3e9b56985455d3aba039ad79d8ec960016b738da33bc1c8cd94c4b48f7f1ae1b369ec567232cdbabb0b542baf32478c6163fbadfd533b9a621f097d500bdc9bbc8c4792b0013a94fa078f8f656aaeea3bda29922dfdf80e7edcc531ad18c65815313b9c4a0310d00c08f0dbc39a82bde74da859c8299c8cb92c3d768192e464c7f10a3179858a81a5063c66f30672f408f0123f4b1c68fa3cc28509f187cfc83aa9dd658ad1244b3e060fd096850166adff6ffaaf6e22d9e5561a2461eb08ae3409f7b73e18b864f20b560852f04fc801b3abc08b06b7a9913b65cdc169fb9c10f9cef1ecfbb94736093210765097d1e3fd10c4431178dce2177c2eafea60c9377032d54dfbef063e0dadfb1aac8b604f3c9a2776b86a20d531b34bccd2cb3c3126008d5b89e9299b03e841810ec7b0439cae25d07cc95dcb0c3ace1c8c14a0c5c9cfdaa1dc4f69b402674db19d605299cd1eb8c511e558be4e3ba86bdc8b4341e08743aec79f01b4087fe06b2115d3018f42cb82ae134d14a6f06ec740abe14b2bc42d050072e3900b919fe7f81efdae73cfcb7047f89d8574eb503058c92d848b466ee78412b8380a6b81b9831a81fbfaa765f7447af2edd31c75f5888b02afa39712af9e334f1002c5ee9505fa0f2e16e6fac68de00bba97df15d15dbbe6468d85843a617c9fc00d73564627094c6f6f4d10c4c5abae315850d723e10582cd0e3e3c9673c886a60be159f05b2f7a99786182228c47ca4505ba1fa65e1a61576629dcea95779cc708c0b95f430987b6c2277cf76ae38a0e88c56c3f1508ca8a73a37cb863c683490db5f002312845fc9a9fce24599cc27417b115cd30e339ec2ce649e827a7c76a01eafbd0e52c37e660a50cf9cf09228122965f1db7a426779ae0f2a771f5944f0521bab199d44662faec253a6aaa0a9a7cc610529bfffb62e63076920fc0050b031f30cc073f46f9f5bc7b40b6326fd520b1bbe1c75597d117330d9eb480375a02cda8792f7e37490b66b3633b8644700c5bffd2b361dc1897ea391478ba944e02c071ab66949dfed5a73496f1430a9e4925dd32d7dcde3f63b8a6dc22e975fe0f17978230c67138d0b8b86f48aed521b925401acf1230b18b07282157286815071b9e5280c71a7e3f359e6609df8022ee71835753ff0c031ca3ae98c12c482c0ab3de173b6f24a8c3653d1099f1463f49a69fae810314d2befd046371b6f39e0f2d662cfd2fce2adf703c1d49a5de9351f950eb44981e241667b09385b5f990026ffefcd2c86383ae666a5870cd05a286d030661acb8a599b1a761f83d549180841872c759465faccc97ffa7bdcfd4bc714ee42e369f9432fa5a1cddd505b8a0f15a90efed1f3672cb191f58bc93e577188a0369b8ffc3f1a712468806ef63b0cd2a8cab92c6e1656f6a4d5eaeffbd84d577f9e705c4080e88aa22d70d9a98f0bda448e326d6b3795ed5e0a75a6a6ffcc0aa17ed79066aaee77f9d3d42fbf64070bef78cf9f06a1a0d605500530bd18e035bdf23d3ee5a388378e2eeca72f350665a02aec8809e19d51e56f5bb17ea0723f544660c645338b358de338de9d8d001c746d241a7efa3c88d034330679731d9c5627ef6185b7fc93543a06d4816d04cc856e52e18d89f1ec7f77373eeb5cfd2648070565c885840b6b8e0f74799a04a82b2bc58c1885936139ba917e1ee0b62fd97b57c1063a25d057dc0620de910ae82c6d1a19298ab70055cea5961ee5157e735a0b031e5bbc92ae6b1a1eb857058e17833cc97ff3322dde1e85ad86acdd4f5f84ea76af07a8c9049a080c502b06af5c2b964e84fa5db70af00c4e0f8ea83cf24fb70ba5d351cc698e3589299a08debd5bd8a96de91973ca5a97a41f40f79647cb180dbc775d2f8e436674daef0bfe590a77978532974bb7ce54b21e165fffef1b846629a37de987834b342140053e0c8308c97006c3e7962f987d939c7cc023c4e460f7d11dfce3a0b9feeada0578fbb9c28a463ee07928e73c008f821e9977c053d037900eb2f423198fcc790715e698667bdb2816d70d6454f80c991cfb383733c4b31ba84500b4b59275b008e06c0b9b9f2b363440af3262c90e8b74194894ff0db159a5d84ec38b4e0b7c055d95d70cc1383ee778b7dfbd5e1b8d729a46aa08ad710cb48eee8a5a6a335c000bff7ba36cc08b838aa3d4573a8da3f29c1e32fafbdb1aedcd3696eb9389940c3786c24048e08779bd9e66f0563e576da8b67af39e473bc43807fe0ac9dc5900be765d8e6ba06bc53a5d0986aef5ddfe7ab1385e29d2f77ddafc09d2eb189207cb30cf37556412b160a41c556c70dfb471742e155938f850c90cc6c75b045a01810b00b85699ca0f4bec4086ac804734589e704e9818da08aa09eb03044f5b05427cb6ffbc9fb45b776250fd51ff7526cf65b148ac57c4c1b367706edd2fdc07dfe2a45d97fc05fb96128723d12b10710d548efa2e1585bf6b4852f76750d20cab29e0521a9ed776449bba11c3e3f5a6acb5c0de6e9a028ae70c7923e709370dd0f7aaa902ee6737c87bdd70edf4ac5a2ef1b1ad6d282ef5af3cb2fa07f45b06f13ca1fad4ee897b410f85a62bfdc029254ef1aa2383b7de13e078133e310a09a203e0ca75cdff2b40cfb7fdc8c2ebabb85889d28e8b1c89184a3e6ed984260d35a1e49b2ef6a13dc2759f3f79503ade9a8bffdd19ffe3d97e632d418661af0464341fd80c8577f9596e441b40d49e735e45240140fc6ae267552f79a8117d0ea33c1414c4193effd1102b2959952490ce9d70aa106061a040a31d0428fbdb04d0ab2309204a161e953e966c2e5e58de2a7e896280f51c9bc0c2701a68b98d00ba1e952d100074f39d1a36049eaa63e1337e738b20e2c9ea03545fa38998020e827eb4ffdd673f18279815d8e955a2d6020dac4f2e319bc216a497b58ff94206d874eeeaa2e1384e3985e41b06185b78f67b8a15bf6b8ed4ce8611533fee8d005b0d27910f5e85c0a0e82b091f3acc93f46d4c88b0c3da571ea1f896001b7504d5d97b73523c13371bff0656ea1cdcccf45d2bd4598cbe588b21e8c5bad72d068ebb0dbe41c7f23b6cce75a05a68d3a11cda5dde613fc00dcd26d541da641805062092dad7568d87772146c8852204c97a3c38429966e18547b98a2168169505f07e9b1e6a6f06b173d3a36c7bd31de882c43e1d0d20303d00d78c2abe4baa0d7c8862b65abe5001accda0b355cf0712cd56daeb18ce96974b3f871ea063b20fe7edc0674594b65437c32b7e2b964c55f3e6e9bc4e5f9c3e3aad8c8a182e3c0afeabb28cdbc8242b8f58ab85d3ac29a9e92abaf27d416d129cf5c44a3d29ab0176da04cd81a64cd8b5bc945a004d1d72dc0d10ba979687df04659f3c11ee6c050e77745c0a45fca5784177a394a830ceb3734c7c9a426521065567a10d784c06309ca760419a9c434ab7d29232d13c2610897cedf531d5313579511935a7000597f769d651f44c0890976b7d77902c8f478957ceae47fcf95ddec185fbf0fe03a08fb5592916a3d25057c7d32a4a0ca5acaa0a6d70c3a3e07d8f916292c3c70638a4e6602e2e9c1a092d921b26624e0ab2e5ef50783299fd0a0a8aa59f216a0b2cf5135a7a5968225e5bdca80181d588b0879c6d04a92a13971309d8f3df5202af831344eacb85a767882c4f1406f1c495a63adaccca747aca775ef2d803fe0a90eb1224eb7ed7ff19a6454a54e7ca734b7c129b5e67334f7aa29b1712490c02b9e859492595bde11a6e77d85e6bdb1d80bc35153aa9bb86d48da3c8b35ee308cdb8227f51699bd718b1ce428b915f712ce7f028ec21b5dd2642ae9bb8b63c00dae84c7cd63f0138df36c0654ac964981120bc5193fee418c532249f9e2e90058fb4ba54ed729d10c6c646917cb752b8dcf4943d3e25ed1cd057d07a1aaf5b035e0c0dc7a602de2d8820bbca88e0e0abfe4bdec342683e3f553a6e80845a0803ee35e6b3e7b7b02b8e31695f54c63015a9815c66d6f2d1261168647db6712808522f73e1b60eba8ccd4167b7d1deb6380f30aaa6c3ff5610dc22aca17efdbc0e61acd6e9bee4c4827449f6b8c2b7f5cca2c006a3de07d65cd3291eebc44a06087a806c85d9b5c1847801abc370f6d76f3cf30d9356a422c34b015f6d07d47a0582d76bb4f1c3d4f2c366f4bc3ae87811109dc104c304d780cd936697fe8e310a0d0a40d51303959ae1d058c2fc11a1c2b61425d281ff7e60e44fd596ed32460d574508c5f2314a90656032691c2b386b2e0278ce1a68ce933019bb5098db8c07534670ec131a7d1fa55843372c4a06a606fd281016ea426ad816785f4171b60b3a4edcdf86aeabef59ac7eec706d610dfe0525a5b8fc634b0e9c75e832741f01f0badc5cae428f6cfe40cf6e1e9a6ffdbcfc7311fd6b0685c07ea34064e51a00f0e0665c7375e277499557b7da9b4295602d568eb55a0cf99d55aedcd14b8305bc0aca1fbd28129f9daf2cfa1e4dd5e328264502b47b3bf090c39850ddd292034b67f16ca07facdbe92b1d40889a942543e0770e6c901b7f838b9b7201de7c0a7cf9f8edde5427b56ae7a6cd6573417a6d4f598bfbe235a786713454eea2d501ba1d5fdb1c837a830d4b1beb34d7133e7bd12b9869320a998c5dc0392faf9109350dc80cf93b23018efd01572818c4bbf4968c17e4574c4391796c919179d50adcccef2742fb3f142c9278326a4dc53015182bf2e09546379e5f3e919271e60c0a48bea9b115feb1a72b67ac36404e9f5788ddf72d50e03ff53ec9b733e0f70b1c45b3714e94c45a02cc5b14f4d73579f94394c2da606750ba51dc6d0dbf140067b139a5203ee50890fbe07c9938fdf282224c92eca69d946bea5e67b017fb0c69cbb2d0b8221d7c7d11981b26b3881f7c84bae2e3280a234789341733fb0e01798ec93fb457eebba17da4cd0cfc5fe0ff321b381d9c94e9169516af93b6cb08ab5c3101dfbaeaec87a01a330a4f90ddafa9f52960aed1bb424936f10ec71a0c2ed983755b73e7e019f7000876ca9f13bf43de86d753ddb5bd23b5c2a356df0ea5fc8799c09dfca1c5b0ad6fa164be96886d72c95dbdf3a14be09ea667c1010b8ccd4984bc397cf569f54dd58764f38a1b912412af1e8e3a27b4675395347b0eb27d7634ee10e89d06d738cb7f2c30bdffed118cd1498f6b699a402b6dbaf70c66fabee52ec335d63976833850b0eb6a806bc6c78a87c436a0a828165bc250037230c62e1e841fba35d78ab6e45516a4f0f9b5d9bb67aaa098bcdae0ed47c301e75b5443b63f308ef0462dde97cef8cec8949f32372457665d2a477593d5890a3aa5c521865f23538975e30b2926b69eb9bb12f930dc15ddc30b796bf201130b82cd2538b2640dc589cffbdc1a6036fc3aeb1be370fc27b74b99278928589103e1d1451031d8eb641c0171f4de353701c6d9a7216ddf582220bf9d966d3a8101a3fd8b659263b7b9238ab2fff62231c33ac2099e6c5f968196dfe3721f69da0e0411a93fe645ce639f66d07eb24e8d06b4a746d26b0c6e6a99bbd8e7bbab31019235bfb60e774e57fb8d69aceda6ff5dddb779234dab34e37adfda08b5fd6a08b3510af235465e38a58e8a8457151bf35b515e157c5d9bd8bb75c30abba12c0e78ca34c7214667f4d809cc46bf2a466867281665c47b03cdfc8f122dc05f2605852200b52b7eaf99a693e683dfdfd53664973257effb1b3a108c0aa6ac15620ceb9aa4186c07a58d4563f0ef2875d5d52ab1891d92961bd8222fde90afffec02d48533f7d77fbb4d5519c2ef47328f55ca0bbbb070a6cdd5f156c082dfc8550687e2317a879494646c7f06e1f3be996d76e29631ec39c6733e9604a8fd41110e4616ec90c2b7555c926fbc483081503debaab59c9f1d33bccdd9b067c136930f62d8c59e83a4c0980b8299b5cb1105774d9ddb25291abf1f885c041ad881e204596e7bf8202f4e48a1144a80b0a25e800774392ca266725adc298d6acfb3260a917419b49963a357a95ebc2d1c870502dca605f38174b4fa16209bdaa4a12d0cd861f8c0129ab8d1a195d653b24fc40abcb231c7963f77c7bad7472a46ac9305583be7dcb0ab392185c83f057e8f4031b9602849f77c1f52c550ad4b3bad85005d3ded21d1c88f6a00c008343f241fe07d65cf711ecbe40323c19782e84a4b0e52e69a8cad6a8a8e535186b504a9e671141eb81094f52d2b81186e3d9c3e160f3d530f8de43b562f6db2c50937bc0c75a9e97ec6a80c337b9d00bb8a889287018d7a663cec5b36b4e429f6da978d40788c33334085988d30947c4b66ad8b97063b5bb0e1c2dc740ada29670d63125b1e377f8a123e9408030245bb83de34170c0da65119b9837a7885a5a0e88f7f676e48d962a7f1365b2914ec5f4df67bf5070619aac8eb7835a8e17cd66152b7f9252770be7134316ca6ef0c73e2a4e1230ac3c3050eff2afeb95667ec390fe6fffa6cc1ce356a13aeec853fed4a53b4b6022578dadff6fdfc9d52838a2234d9f1333017b13f834fd30e5c2ff6be368d9909e965613a29a4a816124f53182dacb7d66be9e03a08cf03bcc3df52579f71d70b623d4e99f45c67f0adbefe9e6de351a1bcd58ff33dd96c89ccc2650359df080ed58c305fb6b4a33ed692a897abae06346df4e31b92e6b1508c2ebe5c97f9ac0c36abf5c82b8f21410be60f182a887bfa5627a914c5d791e22c956f8276a1a2078f26ffc3321a1140c32e760b36fb958cc0230a1ac00aa3fdcefa02d53f06ed07492f0f4241181aea6c97d5ec3d89661a62210c0df5a91d3d73d772bb98b5e20a62fd3f21b67c52922ec750a2cc50ece8e9a06081cfa30eba8bb1dc06c8c7d10ea523dbe7b224b045f87de031add2a2faed25d1ee6ca35de70cc4e802e5bff10e21c1ea30fe8acd9abf8ee7ac8b575824037326cefba58c88dde0bfa3fbd7a3055ebd4ba99e885acfbed3bbff88d04c9b9979e5afe202fbceb2fa530d8c775a0e982fe8847f62053f9dfa8b7ee0c876025c6fcddce413fd1cc2cc2b31a22acb06352b41cef1ee63034f2fa10956e5d8e8108f8d3e0d844e6bf57cd654042f680fa4d08ff558b677c601457d9e5413331f7d31c9fb6bbb357dceb8bb9c1dbf0b0450aa00cc0c8f9fc78d8e27f4300a288dd70507f02455f971c20930cc5968210064f593a71fbdd164a4002457543f61aa10b88af5e6750583d5d0e2ee64ec8b01bd4d36f54535fd5edac983101c767cd06edc2de36fc8adc678bdb2da14af910539ef5b67bd3e32b62994ac015c42dc3caac1a1cb0585aadc5de8dfaa56ffc903def54b707b489181144095bcd87c952ec86ed41e125486239fce8b0c569ec4095e485a9d1e8a12373e6f5aa32f2a72ee95b36ee9fa54ac9ee47e25db39c30e000ecefd28d757a905d33a756e9f7b4f124f8de53d51bbeca940e339718ee96f0956b913ef3d7d9dc597e3bdb81b908f0af7e2045ed51bbbbb7f61af30bf9be20f6d8035201eb427c949159ca3f84109077c8e12222273edc35a6858fd39f22b09bd7077e3a602726b073f80ab7fb436d625aadb6487829171ec44926285e6b3093fafe0e1515cb044f36f466bb8759a579ba5bb481b1bacb5bfe1f122057f04053552f9dffea80819d246fdc69361f5b35342abb783f3857234cd0276aba7a4003c24cbcc4f6f5854ac17fe929eb297cdf27da0e5ed5b0167f9a45571a867e40834d553607c9508e5861d90a513c20f7bbae08799198280ba539e68f0caaef90a046e4c36701fd85ebdeb05d1aa86502d7daad12bfca255a3e5349409a4c19806e555e87f482b77006cf416812feff7e79e9a8c670979cc71d734487b94f1ac0528d975654aac31609e63bae4fb7cb353db4bb3287311032181453dbb35af1f0d6d4179c20a1842c9877dcf63312098d272973eadb41465c9066fb8de96f43b0d57d0859d3335cba3e685904ea6500a69ae7cf70d29279a9606079c3aeefad50ca8efca8698bd65b55e4b898b3828920b803de9946f1022f745e9af09adf5a4060d0378bf4e71981475ca1115395f2094960280aba3c91985c491656412d9e8032e0953b6a8bf161c7bd0b7e9635af20315922eeda9eed0bf84d724ef67eab804741f1a38988d705a3d8fff31f531caf063ec40c9102bb21f8a46fc316019480194f02a79afb88a96b80ec597fee0c8186c8f0171dd36c0cccd4c445bab4bdc07ada6d24b4b363a7aab3cb7b2ef13efa39f6d315688bcdad2044d05540b9979076ed035973fe4352dc5c6a43a98a2a1ff4d19751c1006a9080ea2ac958ebfd70cc9e1da6be3db3fb22ae5a00970d5340c355fe467b4ad749ff11dd2607784ca0b3e047740fb1924e03c7c0cd4f25b4c668923bc3eac6de8cdec2b454ff05c680324089080b3a541449c18bfc9e83cb75be185a81b62fd1531c1ea3dd47e1901029a0cd54e99e28bfde72c6cae3f492a21eb9407c684cb198c5d289b7583b6360d5814063595c458175ba84551b2f59cfbfb60f4232dcfb3f7d73c390d5a157b0d7641997458f1d3d2e55c6eadcd498800e200e5a642f183de57fee2e21d1c4e0af700fe40cc85a5662f2031cb19b7d8cd5cb7561c3fa2b0da6b97992c0bfe8c02145c10907822b6b9f392b485166b9e6b3b82c490f6a077708bd13cfc4c4a9004daad8d1996df4706ea034a1219dc31db13ba443acd1c2f9ef1daa6d9298ece060511dca78d6e467cbce919d1ed40f4ffc0d9d5d4f71d1f052e77c3442297810cdfff0a0079913b3e7c157c741fab658694d12b3934b665bd214c51b7d80154072ffe38b6746220d544ed36ecb0775fd6fe1cca0e2992432bf035efd91672440174ac65ca8843267d4b4cda494955d1d2c2177b4b3785a8fccdd1c5cd5346c501eeb89663b9fb607c2709c9c813ec65e2287c583a1461dc9eeeca96de9d415e001d40cb3b6aec4fbebdd06afe5ede787bef6b842766e971407852bc2c754bd80e102ad20020d568a303fd4034e65abcaac8eb50757bc1bead9e95bba8b763f900aacccbd17fa7d687ccad926a250ad0483718a1bda827069dcb180c3e2e9b80046f6d525f4fac675572b9e48dc46858bcb826911cb74c936757a7dce2d5551d0ab49e6876e3f76569dd5de8d29cc0e0467524e2df86e113d60174bf81daaeee0fab0d1cf05e7a974a07abcbd16b02c90c909c5de1e87c13bd4c810856bdf6ce0cc1533be366964959e3a5f8b24d4aee9d5946373ae5d47c0ecde2a379071c9f04768a0d51e2b3d0823952ca8da0a63f3d0f3105664479518aa20bc93fae1d720cddec2243873ccffdf2d7db761396b0a21568e12745b47dc5ab3f5c6ccf65830d4cb5b7da7ca82898182016245d7e6a09bf2867ac1e24ece8e469bd89e0bf4304041c888569b61400c2193f507b2977f1fca7b3da447ac03d39b2ad45011df30160a4aa4af39653cd6712e1c0596dcc9240aaa2fa7ecb77190c753652b666e20b14e6f0d3dfed54c5d69ce4400c98b70456105f15bb8ce1d36ae8b51a3f97c50eba8ab41c672f4191b7c0bc1dfd2c121a29dfb48704ed2ab16af56242f34e3109118305d5ec50e6add996a54b63a5a1f04baa5f441f8295b9942e26ff2a9122071de999ad584754e118b978baf1eb9ffe4cf6e5b2d20cc1872621465544f47801af7dcd651b31718a047cd5809aaffd514280bb14de4fd9c991fbb4ee413534080ac64cf417e28b1291417c9fea124101b03bf0969db4a3cd87e45e10c089fa0ce97493f503c9b296d8a5f4c302e12e576d03b6ab21a825f8d9040471bc268704cf1cb8c21dee73da433d70b05d91d3cade550874a58f799ab591871de7dbfc06dfe70990eaeacbfc2cfef27dca6976fb6930e3b5272b7067f94b1ef785484702f11ca30e6f26fab9071ba021397c0dd301b293ccc520d8de320c2a9a3cb7c00ea031f47fb98895ab3389711b1cc6280d06541631244d271f6217d7877800d600ffbe6c4436ac3b03ce4b3be8809c4bc3c90daa2107fb6e93c48494c5aea3ff0545db2af10d9b398fb3d787434e65e3bf8b7dc6283212fd515e31045b70f9e7044ddf0d6b11a0bf4c5e5998d16bd3d4bc86a780af0cb017cc70f69cf76c94e20111f6763a397ef9c7db98e0fb66d3346b3cf92dace3e1216c66d022b506b0f606be28435b8ad9d2d0e52c4a785488dba87b49414eee64382940a5f58f5820f904171becfed9ae20d0242d4e38a19040352e3a0341906a00f6a12ce9f70077fb040ea7c9465f50983acc6872faf5bc6771cda9ad07cf122c9bcf429905abdc720c3ab2adf6f852f2cd4efe973097836436f65b5a36e84f9f21828a3da8f1c8050950befe70c0cebfce0c2fa32099ce023fcd95d33ffc1155d1ec937df114f45604c02ae7112f8795dd406153dde63f39526ca0d81981364407d4f28444ae0d7b05068f7c70dfd439af0e33db1431a055f1992bd9f84ea0d94a32d7c71528638e051ba773fc1512ae991a4a44a433d5451b65475ab4b9ce21b4b1ca22d75cc0110e9658959d8f289fbd0473cbf29b5f6ef8d7af1d5c784bfeef414d0e4a9a9c5d09f7b87efe9f07a6093455f192c8468583546ee64f435826f002877fc86ca7d10944460320f688e1a5377c757a68ec2d53c1c4975227bf893d0e6eedcbbfa40b087f34f086f5cd188f2411ad68884324c7f286adbbb1c4705abbf222a12970c507c9ab578c9529a8a9133ded27c9e3a1f5ec4c2d1a3b93e3df7835b8ec8121e70f37beec7d3b0c1d1e772cb732b9a59155d2227e4260ab6a40b18a1cc4e8a4bc0c24920d2f86f9a19f5b7f1de1ab1440c2ad87a0a29c4a7f7ad02f67dcf4571d05512e585da945d60c2b75487e0658167222782782b9a77f099a383f3518247f0d3e01324cc932f0760501d369a3cfab1cedbcd5986ce0f639d7de84c395692d002d01b72c641ad4553f98640f5824716db74ce1b2a89f9447fe4fc7e276990a0250e69e4a64fda3382f97841db0212fc5ed24ef8644544c2d9aa2808a9e08db08ce3ab87d7ef14a2c5d234c826fcc021cc6d829bae75755fffc8e838d41fd36076caa2ceb30020103279aa225594e2843ccdde2208f7062552977bc627715b7057629e5b87180822cfa1d97c362848b20579443f484b10bb355ce2095b4252c0741b53ba45b5b3970724aa300ab2dda919ec7e9ec96bc55ff947ba9d3e2a441013f70ddf20fe7fcc5b25b01187eacbcfae52c9d394c816f4851ff707779790e03b035c8fdaf2e3e3d7aa6950c4ee28e82e853d97ebf507a58bcbd7fe51e7ef1006575c26f76befac0a2d9acddaa35607b1b53bf85a9a000a0f63e62ae6f4a4303c9403394492ee1f8c56cb15aaa9f718a73bdec10511de6830ae474ef6152ea099c2cca21238ab104fb8e3d95d8dd33b0261c9e3444fc0a05552171f1b04d6d0c5c0e4e5851d1cb9fdd6fac114b944cc45bfc3d248ab01d61190222c162b31107f7a5d14c7a0fca9b02c52f9a95881188f29ad3e81a1c1c5ca37563f0df68850a9fdcec6c31572707ae8ff3ae77b75f52315602296a68f6db6b398ae84a238c07bee1fd13d8995cab208f14def989d838976b798e4e8b7e8f8ac437f5fb1d750646ea8913c2dc03e8c2349d91e192a7dda2c3f10430fe60a214170dbfd3bc240d30233f51d4a5f3157a8fbcd29bffca3430efbb9e4e6593310fcc5054b25e6b0a8f7f81bbb61dc8cc4a08d86eda0dda2f9d7ff1336196e65acb88a0d5c20bb1065e6bb798ea68b4c63acc6fc79a2e403abde969bfc6fd4c94f3a6daa2cdf5de04613b121ecb98d0d6214eec49a570d9072a7f15288dc87e47e00d78aee877270eafb7f4c95692467b3460eb80a161e58497950e56b46a6cb1aaace912f0c6120f3ab15e40aec285d362ad1d62a4929107603a6fbd0b2cd9c5b0eeed6bc0734e099dc44a567e7542700d194d20935b9f0062fafc9942a72fa9aa1ce9c12eaf1303572aa7591d5ab3ed2ba707fa691a79b9059bae9bd52fe57a86cf263e27cddc0c7fa0f1354ecaebbcc8aca629faa1eaff8ff623b190f331367e951ba541a0330b8add60ad9b705bc1ba7cd5cfa2c179e6eaac20a102dd2f8b31d19964c473370b2e06559cbe9200370cc1c04213ec87b003526cc925242b3e09252156a283b0036af67f34013e4b9e32af141117441d1ffac97df5149e8c13c32c1b31c82dc2009e73174909d29085d027d3dd241407f3725c26e0c066899e2f0481af23ec990ea193474d0ca20fb6244793c84e47b6f677be584cb030e4762dbc982c5d4f7a044d4b061f6d6e9930e05fbe7aed980e97776fc0ccd12ceefc5ececa294ab20c008a156418be199b641e598be1d29193f96c3f38804f20ef3845339e6ce24b050342e8a29b5c79b1f86ca1185e71fafbf6054237197ad90927cfb9b5f9e69f3c01b699a3c7f23dd70e5fa0a1f76bc0262adf2475d00996c8d5e2c22dad74446f023c1f478b74df2fecb5ec683141c0a96c4023d6c4c2b13abbc6d5f9efc6bf9c01e6e95cd6d2d00eb3b8fb678499e4893041ba5946577519a3affae81ee51dfc0533314da905ebfe4753c240567e0e69d1e5da65ba45a7c39cb2d8c8f42c5dc503cef42d6c719088c8de4a1d2af347896a208de11cc3d66c21d767f211314e090a7f4a04420c8e897f192ff7d7851f17ed14d08db1f0b1d368611331a61412040251736c1e34cf5f97d6c1d20ac174967e0ab68786aa972039c421eae75feab40280130c39862508466dd0753ca12a44f6b857510c51fc9128aaa96b611125150206a1ea400a46f2603744eb7f0fefb22077f19c6b9f693b6944d46c5f2a57320f0a87d7050fe5d1f35c8cfa285fa46ef13623e80a3b62d622f039842b5708890bd69debcc6c927859e7fa5eb1fd01ff1967cff1eb0d149357ea126f7c995d2400c5c5d53368d68ba62cf63581270ac3336b391371cb897ab732bdbb1ec998df0828796ad2272a6427510c7a3d5d98808c39a4a10ec17f55a6bf1592c405ad730dc07c26f8bdaa47243c3f100df5f95c27b3d18ba2747aafcb450f3cee3abfbc0da6065d25bf30b3e2e0be42a8052934fbe6ed9c6c7cad930c3b032d20d613f10358c07e6050e2f79d23517cd7f5df7469e347b513e5441fc493d1327374585808750b8a9300bf560f0bbcb9c98062c403f704f01634bc605ba2edf4cacd09af0454f0be4b04e36ffcdae8be9b92e1fbb1d8b2c90176428adfc4062739afdb0d0784613c9b9c50a59543eeb1f352b301101f6e5d9bf114f79a9bde344212cb22046d7957c337da0edf56959c79862cf5c981e0c1547b2bfdb7e08a919b85c8fb07c5447a5e1db9e177c2090201790e494edb3d73c815e1307dd8e0fcf59254670aa21428772c370ed9ba7cde280cce82d125c5cd26979417f69305d538967ebb0e810bd4eca100ac0155d843fe6b8a326a27e50cd98fd224deb77030fe5205d90e12af378539c11538240fe1c94cd46b5bca6d7bf9efa361503bd508f203b7250a69ce8253f21e177ef6e18856d02fa1108df11992c2775959618caddcf093e7062afe2bd1efaca9f288398245555f0f3744f388a98df45f1c391e75716fe0650c5b02e80255392b26faa46f4675b55854e85ed54593032d59923d3c169895d804fd448d4c658c6e47cd2feb3c47239273ec587dc61b20d02a5fa18392c66e560c99cffa58f5b204013524f1d26ad0253dd405898ab485dc11495e2ac6a19e540e3e25f8aa4d0d8ce6303b8d91deb8bbf7fee93fb3183f65495337d575c73ed70f9788643fb4ce2e68e4934e214ea8a10b5b0cb874b66bbfe3fe54259e2bfcb803780522b2709336e1943497b1aaf240f7e4b8904df09d8d46167013a22537db0105a71fec02ae0f118ce263e6530abb9561eb912b928064851a8db4883e73af0ae82d70a3d8d7bb06eb0547cb1ace67928a28add43eab4c4eb1cd95a94c7da90901738a2f5b935ddfe6bd172698048c94eab2ac87647467424e377408ae19fa0daa940b68d451a6e12a9d19f9af285b48b542a7e3f2690d8701e3f643a8ece1021a913562d80e5b9fcc7c872a3b30584c2870d1843bad3cfebf849cf4bb4600064e595e13be3f580585ab98a10fbe7f80d9e196c9d43747e8f3a4303ba4d0b80794dffc188e4b2ffe59da63607bad208f1466eb80b369faf3c1288f724713fe0025c01ff28a0c61c0a5e6d7bf7c53c56510a042bec0f81ac38a1d0f3b827e13091f57738216759ea85356272b824deb0d15e0a9da9dd09a0859e2edab3681450b6f40bbf1683d1bbda655a209d4b90d40230d9853dd408dd09a7cfffcff3eff0e6ba3f34118d6a64eef4427a39902d5d664fa9232fe343daf9c17d98076302700705eaf89964d9a8a7deb2591aa9d80152383bbfbe4aa6dc37a8920567cfc42089f131836e84682878fd272778b04adcf155df836fafed5859490e0a620c7b50d4b278b1ec5dee22e63fecd2405948764e08c72e9087b073a39d4066bd3082b0b78cad0b68e88044ee54ea99544f0f56db92892b8dd12b9ad392624568b01df0f73c9b8358a9b5f227ad68822497a1705fb50375412a7be6cc40d9cabcd230503b8dfa88bf05dfaac20a9c364543e787f9c1821b731bff05f908fbdaa444492007755c25a905091f60931701d242cc163966b1f12617d380c2d1bea3b9a6f6b03b7c6a09732bdd7477bec7fd2693e0cf327dec2548cece0a64c0f17f04251630b4514ad091fd15bb2c75694057080d60131ec3501ca2eb50019efa1aa3d976b0c4fdb431829fa9dd2b1c59da9d01d801cf94a4410735af1ff6a28d41dc53a9e0397bc7c0220d3fb6742da87705a57c57689f54c5882bab5bbcf0c00707fd1fe07917214e97555b9223d8fb78f4b736f223e145f9a2d7af5c0c81e226f2de29d01893ac55f933098884478ed2919527013b783b8b6286fa0d1aacbbd8b326c0f04df6976109fe611896dd6b21273eef3bba8c391c3f49550e72fc1b37cfa15c7089657b2996a908b1769665dd52c08648c9f119b46743f02c3f0831a5e2607f9047ae6d56a46c4d07541e66d1e5cee1998a83611dac5de0835da9c5f4d24946508f53c3248eb0969d9131755aae55324640f013a291151c83aeb93f80a149cf2022bf421ee87d498b4730a4b2432ec274471def33111bc7935e48e21078083900f6c0c48f495838cbf35236fa94946dde4d8ac0d38cab68c65fad19f34cd33cb03789af7fadbdb09f92fb0c0a6cd7fb4c0fd792a0a3eed232dc532e149fa346b0190c0cf7e98db81c88c0dca8828f545d0e9574ebbe6972d564f33460a5e42290765eb25b6081d92d189d1c6b46d1a156cbb4f97d5309f22e1ba7e3c589aebf909c6aefc590559eb09a2fc74571d093a06b0afd467dfe8575f5a52544656410d04b48d907060e51dbd46225d06a633706f2c9f33c819e48ba939a3875862273e0d47a3dcf4e876f06c7256e8857dba04d4be0522c949a0c24ba0ea88efad53cd0e7b4f955fed79c8a7b11bc654e124e2436be9dcc2f6eeed5b4f41810bc4ffe309628534246cd2dae5e51a9ca2860d79c4d8907a57b50bb170b5f73b40e512770b36b45e1eebef6380c91a92cf3451e08f6ffefce787aab6e6348530af47b3c102280ff2587b89d62ba2b06db19404e425eedb32d7681a7f77cf4baefa5cf5480226559f62eea7ea67b7550b7753a8ef0046a995dc3d0391a33856d11073785c0be45f8139deeab023d70966d0823eb360e1616888499a56fd0d6bcc017c9a660e6a16ad2293c1079d573008556e3705f536ee3415167e407c38ef14b5e5f1280679b718f202de973868fb92fce4d378faeae46b3e6d827f4f60e334378fc91a098ee2feacb5bcc3a7543b7dbc26454799a01f3cb6ae0c35a73f0d7d8586376e043887d09afa80ec6238cc20c7ec564f7252b333ac6607a9689db433e7bdb37b0544ae91a2d1ff4a2902c9d69a71c8421b9aef939be1d70265f469b71affc0310b8675407872fa0a3a406f343bdcac64e96eaa1f6df0618f8c0c4e84ec93d978072bfafacac7ca2813d8d3b6ea442e1cf18030dcf506d96012d3661e9d399ae30cb28e5d0aa081050c8682c5e39aefc56d26c569ac8ae505b926dd9b6ff12c080295f208414a0d718aeaa25a8edaf9d9c92e9dfbf3fbfa43b8ad7084643ed9190acc75c2301d6b81dfda870fcd2d4c9fb7e4556d2ee05d811455ffc96d7a2edc07ba00aef3565bb11cfff2c6d59806bddc2098314b3409a285d4e93b6b6557520cf6d3c07e445612a4b2533bddbe20be90eade852bb65f987f9f332902e5674300035dbb07b9abe345e2723fd3fe072cb68e1fbabf602f441e56d2e6e6f857b001fa8fb5d29ecb57cfed95e1e5ba90f4f48884b0931bc21a50fb11a0aa291d3b0c9c34c3caccf46cd4371f835755c515172a7c2f519b297fbcfd82caed6b8f6200c00fcf1fc4477cba0ff48892438e3ea798b45c31b09e237226646af87720cd02c61d878b0678d3bc32bd62095d0af62803b1812b84e884cddb5ae443443dd60a0b37997bea1a915830a1d51c6a7711e10c41d5ee45d8a6884d92c7dc318522070f964f342c5dbb3abf8f9afc6efebe4b3408db8b6ed354ef770aa2fb12229d035558e965fd2d4bcbd7d94285c48cde29ff84e1687f20640470bb47051c23f1056cad1e929fac1f61b59a5bc6a99a08a9306318dc7468f9daa1410c144d09e00379b7502dd294edf747040ff4d4ea830cda329b5b18fc863436eccec86e38270df0b712069792b08626ef240f1df7ce752393fe219812299d4da04726c2d3a604271990dc88af0d865883eb3e2308ac8bc3f26e342767dc550793196c8bca1309b8f855e238b59676be07ad693d88c5ace477439b1c103b99f0ae51b5c287d309036fe5dfd9de9416331e29e36989d8f9e6c2f3dd4a5d1cd7030e099fff54f109d12357fe8f874f51bfad1f7e07e0f6ac41b540eb7076b224e3cbff1cc4a45700afabac7aeb969389b0fd1482eb904c58230688be90669d503e05d9ab91345704c39ad48fd1537085385883d3dee96c3ceaa0643a5a634c447a35b8df1f0d5d0641aa64903d162245cd62f96f68d2745441f98634b00761e44442b2a8a9dea10e6f98d8ebe20c96fee91688f678eec379433a207604eefb885a0f12d75dc7f50c3444137c43598e117c24ea2d9439b87aeff9b8edcfb72ddbc5f52b7c174b1b0174bc44453f23ea99ff0b4101afecd650314d7189b4734e1a8334989459708b0dcbf1de257bb1600c946ce86ea4fb8ff7cf48e678ffa788bf956ed15e04b74908defbb20004d81f66e0473e3c5831f50bf2a98070ae6d64f6bd30a18e0083ee048b34e3ce5f89d6699855118c353cbfd4747d1ca59b57ffbfbd2286aac392a60d97b4f47000487086408dbc2e92c3c2c69c2e264864bc74e14ca61314bd7f9705b216b7b0cfcc7d7aee5475d9601590db7929925ccb21958a51d632b70b12d808eca1e4127f1dc33787a668bc0b7f5dc7a3ce885aa23cdbc81e3658fa76d9a900449cd134e627501fb8742d15f4e118c7426d47c187cb6fa7a8980c58350ab806b7cfbb3e24949f2fb59a3f832d0ea91d3c89a308e2f86621b09acacfc511d302908f11756f2b415b08f0b68cc23637c505711997e449df7f2be6af0d4f882e0e2acee835f1de614cf2fdfd69cc2dcf6186f15449f126295ab3e001f196ac9301c263bdd40dbeb010c16e49c89f1c4b1be38b6dc4541d8a8182c665181819a70736d55223420db207dccf53ba599f2b52e479b94eb4b5fc3d7b703979e2acb40b08bde31399b1cd4e9de4402df15cecb281ea4cfc17f26396662f0cf6405408031a08ca9a53bd5699067a3dc68fc6b47687f4839a67b85b42985f062c76c4fc0bbbb5aa3d95dfad752c4da2e60a2ee06ec1e28278945fa65d587facfe9598b8043695fa9950edab521a197c024f6fda766153d9e3b08176ae5ca88a4865b25602f704898bba829c3b85f911ea03cb5c9fee67a7bf067e739c154d46fed943080288f8330808a03b032ab28d44bd422cb18574235fc33242ef818e04051ec0df0ee1da417adf9e5a6b1d5b8e78f20b9dff18e98dc9a96d7fe07cf44903ffc55702e3f001b7a775b5d665a812b28cca861342a2ce01c7a0a953661d82616e20a9002ab7bd8b0a5c4e6590d04a215b1ca885e3e72bd7a8bdf97b48d810f5dd55550b154c13e5c53f3a68661c54893b80bb7f1402610d7a76e3031fffda15722094024f4bd13f4ad7285d9ea43bf55b5d08c1a974d504efedc9f23b696ed982fdaa0ef38ba8004357ff573d53f5502c8e5ef235d5fa666e0b98141d49a0ee8e55b803aa10c9c6d32931eb72d04b650d4382b993dc591e1da22f07e5eb867846b9dc038ddfdd7acf3fb58d1da5c6da876b2c15ca9af402f1c053660c1f8e86f1487c0c9585aa3df01dc611e27895803d8ea115e89117dcfcfeed4b812e144a95b8d709d6ffa03ffd6767fd8fcda2e65652ef4916827cc566f7602e47ab776cb6df2f06fb65a5305f27027f39caddfd1ede0f33b0d9d3d02788001b2442139665e3ce01a703163aa80c4e9c551b706e01db895c7cc05afaa2b572963ebe50aa36644c087ccaa7fa60f3ac9a32675fabd45882f161ca678d246de4f6ac58483d92d63700355eb11017db69dadf8b30190f1531e92927c1d7bc5cb66e6a822c7f3b0ed507adafd3b59c3d7106cb0c9603e872adf47bb31f648e223feff20bd6fc9f20700cb14ab92dca062ee89053db624ef284389b834976a06f592e4c2d57c01b2d0f05729489e40de569d9de66e17d39eebacc8fc23b5d532fe1a2761c059c3a650d0130116c922cbb5ee1fd5a080b36df5f78a4294e88905a52b7717e4ab91484790c01c2230596849563be1d4bf1159241c893715e097e4fcaca3497e4713450490425eb545ff7886459e2286836c3484cded23f55f3728d3902b994eec90aa8dd059859a1a3d35d0b64bd73112de5e8b7142fb3b5042726a4a059449b4c7f71390cb4324072feee8e94f5df53213191eb14847a5806a734a5ecd2bb7a767911d605a4b37bc537a76bf4bffbaa8ce802cadcec691c4f9bab57deec90d94fc1bebc03aaa1d945fd983e46ce171efb77c411d76f5e4a77e254c946a3173a8a512f210bab86518f052f2dcd17baec0560ef4e3d17b458b2c7f695204f0fe34923559f0cbd38740fff68761c03e9d9103bd54d47bfe1d80a664a37d20d5063ce22ab6e0f42c8760f3a9d4949b93b8e25e7b5c82300ff1a32aa23f7c722e72e1a20b25d03e114a4f5179f0c777f43dfc5e8d59c70f0435c023bf6bd561457f54efb261a08183d7c583f5b418d79cc741a2db14634810961e4f49956d0f122e560d6445704c93aca385222d9cb5eebda455be07041bde23c58ea39b67dcbcfc75938f06801de9dcbb6371d764891aaff70578fef0fdc11b91f6d6b67915389645e6ca0e40a7481c3ffeb17ee126a758760da4a8777a35360dcb782d922c7bdd9c7481ce8093b85d43e467ef0306b5244a22fe7c18e3c401927a4f50a5492e77611eede9409d871ccec87c8867d0e15fb2edbd6c56723d796992a1482e3b7c5ef2bdd2440055f2085373bcd4061bfc209735e676058aefc07b004e2b61ae1aa28379d31ed0649d67519e7b8b030a2b77d339c4728174ae4cf1ee9f0e9f570d052d365853f05a59ed44808ba467f1d22904e6532119ce1768ec46e2849201caf14d1fcb6220fbc906cec720055f9d3895c8acd4e325d4bc1be65ffe657a4cb300dfe6f0e5405fc0667797b62e73e4bde6231e92ceee7f6bcea4d998a85406d90ac579c3b340f4417de3c8b26c78759ac26e976ba5d18aef0ca354120ec9a6430fb4a6d8054070cd0b299b063a6e97b040ca58037c65ca900c8e8b0a25f7d6b5d8462c5307107d9adc0dd41856e7f2f8a85416659adae7178ab4eea41f331263aec54ff774b0694deba9ef4d89a877733ed84b689c50ae5860d85133a22e28be6da345f834504a7d383037d642de007cc85e6b87cdae0fb2fdedbb03dc963409f96e4df5a030fb227aa324ba28d8e0f55c51c51bb42382eadba557103cda388dcea9cf9700d0fd8fa9d84e8d012c91e83e5e47efb0c21141c8eb63412e7a71dcc3003422fec0f700a6b5ec7b028e355e83b919e1dfcb690d5171c5f2878ce9f166663cd06ed0a1ca34401d03102cb3a80ceadbb5ff4a65500b001d9ad3026c3f363419d627b0f5575a03a04d558ae222bc760584865de690e66e979e5a790bfab6783f47c5d0717318d79eec74ab56ba33940c9975d0dec5dca2612a0d0940193f40610b8f20d -generate_ring_signature ff48702510ab57e5c8d1b6e81acafb78f7012e10e03ab15b0f1827ef34042dd0 82eddeb24599f7945a021098f99c7a1cb6edfeaa937e824e2f529708dc25ea77 1 c2e3e864568b78db05944261bb131881cd05c85c69033962d3c4f7e2a25a98f9 bcde8c2f9402b56df766a132e18cea4bd2d2c70cb537b599c2e829b7e3af4700 0 58e777057635a50541a903863ddb09177fd38f3c5af7dd93b87464e69f1fd70434c1839d66744f6aec5c5ef658302036c442067c36532aef55ebd901b7194608 -generate_ring_signature 417b4e63adf44a5a461ff2ff030a1757fd342e7f93ebd920c25b4c8e6d740187 bf738513972b08c9078a1b21cd261df99f52670326ec59dc94279e626c0ce9b2 2 f204e5533b350d6232e62308092afee62c4ec0f4fce45938a2bc17ec3bcea3a6 50ccf363fd401389de4f584b3597c3893faa78316cb8799ec6c20a37596677c4 a1bffbc16adc7bbed80ece0ae2b4978a0b3325257a96a7e3c3c097584d096e00 1 a751d8846efca33cf77a257a9245b2dd217a627683898d1c527007c4c9f2250b8fae55e0d37e9cfd97efbcaa48f7407dbd52e2257a1b501d1ee8a9088d45650e82f980b5cb0aac14d91ba6c235321cf6058d5957c59a2c4b3152fc07a9a39706f3c8e8c2e1e2acb7c6390247702b8bc23703fe766daae010dec7f50824b8bd0d -generate_ring_signature 030be7e561c24a2c9a75cf63dfe5bbb49addd7023362f7688d24ad9369967bea 158ff2e128941383275c97ac9c0206450dd2f16dbad1a0f30b045cbdbb76c2c9 5 205a0dc801db4e3d217a9a9e91bad16ab78ef983f799b8364ff9ab44add8ad6c 0a6f06fed33891ad9f778c3689968dfb1283af71b48c1690dc941ab115ef4d69 a19641d3c2804dd2450d313d1ab0ea95b16c4cc1cbdfebec643ba05604625dbe 93bbc7d65e865f094d1f7e7cb5b07f734016648b7d3e104db00b0d8899d19e8f 68e6e3257e7e974b4ceb1f7c4461950e60c1e5d1d8e935e02f4116bf89f5d842 0e0e85834add085696b849ea3ac806740cbbf7e2dba4002934fe022efe25f60b 1 6b1167722628d30b0b0c467ed16d98cecf39bdf78984115a5ad25f5df4a1f50d384d0595b7a48f6c713ebc4e128aa3f9034e470793f1618831a96a1a3e06570be13b96f42e73d236f5d842f4b311b90e804c0663ea21c6c71dc4a8040151100bfc41ec2cea4339963f616956d9b85fab18d9db84469ff7f0304336361f65840a3ad7e30af1291d2e63ee864c3a0948a1f28613dce51acdce88f3ada73719ca0e80686c8e9a05a40aac9b7940da2eef5b4afc3c2e22ce481982cc6b2d60574d067ee0b18f526ef3de2282659b46f57ef2662b4fe766125b098cd3b7e764f78a035c277520e637f4da2dd677a29b2dd82899b5c5cbed6659457b62261435ff9f093570672c750ac293ac48bf3d248b14f52366b8f3632d8dee37b5b78111747e09331bb6b3a930338ceed708e21a8066bbda232d68eb53e1c6747f2a9335cb550f -generate_ring_signature 676d9d3f03cde540717f27be188a1f3f089b9de4264a66906be09441f3910378 1b768edfb4e27ae223a513c913ca9122e2da6bd2f88efa1718149068e6a000b3 1 fdb9b839c1c33c111ee96c1fba14a6ba9aa851737e89a629ab9be2c215c1bc5a f8eec233f256d39bf9d6b86bc1858e47748c5686abed539c8c44457cada7b202 0 4c0f7e8cbb16e653b4d2de6b3a1016add36fd3e0524fa0e6c905c5ef86098b09f029298c240a8b2d26ce1667e105ec57a55b4a0b06f32e3fdd25a9625130ed06 -generate_ring_signature 6b1284d1a3858c8b7932de78cb05afbd1d97616929e31905ba975dba70d7507a 4ace77c3426f132a65348c8f2abc450307cfbb5aab6b83748cdd9da470b37162 32 8e02737d48fdf2acb36863a26c00930d9d7022156fea101760d6f178ce30e0a1 7dd3e2d772fb4626db9ceea8dd6e455d147d86a4aacfe0f8dccba90937dbc2d0 413747d1db30c5a9e8723aca5d8e2b105c8be221c1bb4337222689f184c4a6de 297bb55ca843b5b0b291ec07dc6af5746d7348920fb57138bc2a6ae208239223 eebe4b70a7669dfd49a6996775053955a4f03e98b74f1ac029c1d3ce1579fceb 62d36715fafc49f61c615704f85ef67bf6c82c876abeb029e875c3847e9e8d21 c003d25e8b139b9a40b1e169a8006e335b7cae4a3736026a4ce0ae55f1d1154c f89a5190779476851312b1297ff0d8bdb0d6032edcbae23b48f9a8241fb7b54a 22a64918ea6e8d63399c1837210716fea97d6022c3657c869d7c9255cae853eb beb2dbcc8a4ebb25afade190a4b396df94791d873e34bf4fdd04f0a5a5f86753 51779058b46699ed76f017f551b53beb6259dce7238fcb23f62553638c0a0721 077f6bc7e6eeaf2cb3beb60ac00eca6412980fa9eddd932b9a408edca4981ec5 82da8fbbdd764bd252885dc704945be6465fc45cc30dbe3eb6fff9692ed05210 1df2acf66b87bdcec1b2d8e454e73fce0e6edf521d9c2ce5d8a0a5d8e7c4bfc4 c4107473cd90199469995334065883df47143ef21704bad6fef7db88a11e4799 616adf3cdbe84f04cf90b894eaf7a3e420d8606b3f5638feb3dc57684b63f606 180972d031b5a05c43c1822aa2f391de533069c98bcda58962e8f124a5816f79 e7fadf78edcf92bba764a30e00c626eecda4fe7b732e588f1fa668dfffae6425 369a2989333a13a52af60cc2400536292333a78735b5e2cf12ae1405d8e29c70 0036cac6d8ce1a2a5e623db6f9dd8eaa231184c85c1b7661e4a391288d82e7c5 dfdb8d0684014a38ddc314d51c948bb1fa93d2f491a0cb71c0119e7c3c677023 dae655dc8a1f360aae2d2f97cccfc95244703e91841372ad4e19ff62bf7ef77e 79d75438920510587b7e8935b56e1182a6b1736ec651a624385215d8d461820f bc7705ff99d0becd021efeb88f032a9f1a8afb6e4215da04aae62d4ccadf4b2a aa92d90286a2e97e8deb50bd1f88b0c17af828c037f5d6ca756461ce5aa50947 8a93227c8c929fe5668877d141cbd701b9221505a0af068d7de66651c3633b1d d8c07205cd999930a681e3df4c93d0f0306d2f7af98f54b7b0f9cc6c856164f6 71027190c84d4ecabdf9c6e5e4e441532b31da01076940d9ec6f8212674954c5 16fa8ffdb9437d1ea18c12820a0bdedc301adfbd3c525408784a5af4fc6334d2 dcf6a3df67483bc91e2cfcfdffbd967f1c50724d15754c08f46cb3afdd16775b 6982f0182fb32a36fe007faff4f28c411a686f97a40b44c4c825e865f9487400 2b4cc6cd754512d8987ebd79a820b681787aed6cb7bbd7cb5ef792ccf181b228 0b9d7dfac5f2c2203dccee3bf001cda08ef2b12f8cb201e2883df36f61970405 14 d6e927a489e73f67789c4eb4f950552d56af8d2446fdc1663e06ace879ef850cd16e42429741a4c55e7f587c86fb2b7bb777ccaa0f22d738b36817c33fa15c06306eacdafe469f4487f23615aa4777951f81a2cb14fa682c3aa802c6ed8bdd0e1c8bd5b14d8c5f90376633c91c3fda7a541b88ad935ef3d5feff4be731041b0dbd9b899813415bd610ecaaca4c845f448d5871a986db1373beeb94d82de04f0670b064332d84963e00d584fd2002cb890fc5968178c4d5a87b7dd25fb9446b0ddca05c6a2aeb6e1a62cd12c4671372aed24a6fa2641a677ee244b704f9626e075f2df67d86b9b4561dcdcdafb2a411e9b02b6eb873a30fe0604ea6d5bdd327072b88a087bd258546a250f7d8105fb7b5dc7e804cde9f04b6a1ccfb2c0255b9029372ef1efed13ba34dd85655543c8cbd39e89de58f17ab4ebf1dc961cd58040b832ba1341dd643c0a1e5d9b0271daea3c7ae6be78f46ae62a2d6321e76752b0c883542b282770e62155e01a1048671c8a5db1068e03639628140981b5d9b7205519b73495ffe6d094d056f5806d42459bc62427b4096ea20b7b43412a2c9c70d6a0388c3e2bd6cbc569428c1ec0707fe5cd85b71bcc2baa4167b6b0ad8bb700d48be4eaac96bc555219fb68bd279337fcc804f24ecf3c443dd7d221fe8c7bd0274ae470e4104a5d2f8c4cbbabe68dbc9b875c27e2f8fd777e23c9e2a9fb59302945746b5525677424db8bb39130892ead62d9ed97e3a5366c7f913428fdc3802a6866cb2c8109e5a271ff4bdc92cb8fc2f6b69f5c520193b4e543279bde37c034efef28a1c40baf1a0787434388d9084816d5543c20ec7fba6ddae229f98180069cd7f40078c452d895b1e53620bf0db99663c4060b6c7f0823c020b8a49ec0e9f4355876aa2505f9f263a9aa45d90e5d0fdb982fdf980cf2bd7573446f9630b1c97a86f62230d4012db97db4354e134026638a719368dcdee25110766e85a0999af6513114770d8230e6e4b1de0faf17a2ec38ca3a80e731f1b0dd67df9620e84b3b955ab692b53b53be4b936b658cc1ddb99b34e563ae15d6eb671c7b42a068745c9456e56362a5c66873b2a5ca56b35aed8abe5d1502ea2c02125e22afa0142d05230e4f032b240aa9e30bb4136022d5cb93a233d1eaca2ff091be30c4c088a2df2e4fe40934f27ab8cab159bc409bfec20762ef11fb0b72f6a5454a00202f6f9a31b8609204d8d6624b935864cd510d764ff226fd0528c9f019d6c806c0f1f857daa6519fb82bee782c73363852a15b7f249d20c3e610edf950d78d7d90b5e95cffdb5bc07addbf591989570cda126463a1d5e4a768be860f4a0b0c148070c20b6a9c10e382e13ef57856c28cfe31c49000e10ff204d7055f61639cf1309d167beb78a3577575183ee9ec65effa40518ea01639faf45a0612c8db4944b016aa80cf52f87948ce4ff7a14530efaaecd555fc48a600c4f6896e5eb71fb87007f479e96d594efa26fea6968cdebdc4ead32b4e25dd1980cadc5f318756c5e02128c169773798e01dbe8dd00844da21bf1fc8a6a0312226232c44aa51feb8a0c940e425593f9f2e64aa5c985cc505625ac72405485e2b3c2a542e938a8c1b602ab21b31c0b8f5c8701615ae7d0d6e4f950c4aee097df360e740f69cb53b87c0c361f665985caac51095e28f9effaa89c17348c125d02ab9261ee852a1166bd079f613f72a71ec17d2b95f2f37ff4dddbdee7c9e1bc9f662378f7f7d66b1fdb0825e584636ec22b2689049033d1561658f625d492ae65099a662bc9e5e3eb8f0bb1d966bca19793a610c9c4b1025636e496e19fddf178b217fb3d4d71fa8839039874c9f91ccc92154cd08eb0f77b6479179768b223dfd1107bc95fcc16a59d0621d8041d574b3f74dc3e96954d25349cf4604c2c0dfaeec80c0280d04e4e01034bb55ed7f016c28650af56d854c714178acc1b82028b7e0479b6afb1c07e62063b2b8e4b87b899e05e1534e4fa3c96ce62c4d27ce76985731747b2b0e95015050b971967245d5a5e81ee76d7d446c424a33990c69255fec1b96fb1790336d10cb0e5110abf9eecbd6d0c86631c9a046c4cc54c2986ded357ac413b50886d4306e36c7e60227a90b6625afcc1935439db3de6eb93a469317ecd4ee6af318462074b3bdc950e7932a733acba6fad9bfe4c408e1d4be4c915793c993100d14735007961e41bc6f8b90e057b9e64a27ffa54e631d8e1675799c34d8c283206cb1906dc02084b8c13a9280966f2291c002d28ceea7120b35aa61e45981e88ac57480f3f5970c4ce4b13d01f8e403525b15d097b6ea35cdbaf407b674b190ea4b46d0d6b9633b6fc382fffc00a546a029708ccb43e119ba9160d7b151848c11349b50da8baabb3ba03a81bef87a8b8ccaf8993f3a6ad1eebd486b9e5f61bf614c98e0e08bd67342091485e691234cb91884dd0b135942fa1eaac0cc4170d83d819eb004dfea136dc62e8fa8b5c767e1197cf73c6f0f66ad8f770d6faab71464445900bf028aa6cc4423e133974f6760c9c5dbbc265b7ce0a0d6b1a1c6cf73914ad800b39f9c871ccdc3821dffef96de9709bd9e14d508286a4b8d854adbbae6a36380a76bcd3be5f3f8c19515ff42451f597e242ffbc8fc16ebe295e13e117f32ddf00968f05222384b3933c4a67dc9f8093b6bc6575150b8e708393ce2c055fd61a07490773e765a05c1c5d415faea79545f1f531343ed84dd421f12a66fd16c14e0e48ef5bfee5e0bfe83aaafce65bd168209da10601e0d2f45af64c1e0ae9309c0aa301d203c500458cf63ca5bd0a524828a68367e1727229ef462d5e876a2877065142b3a317763b29eed8563e3c8694775a2117efd39404bf506d2b77bfd9eb05 -generate_ring_signature 2877c2572723b6c1de6b392c1fd7e6b9b06be04a33b2c87ee27559a4f440d301 6be1605f102b685abebe480e495a6ee11b401fc4f8ed2c7a9d59372677c51133 1 43f61d5fa46d25cff1552dbd032d2072537865b23f7631b16355d6e04f57406c 5768afeb110865a5c863b330dde198fee80d775dbd33ce56e639cc750825e60c 0 e5715a14bb474108b60d0446c4dbdb7e3a3af608115854eaad5709e720563500d8bc8527ee0957ebce8a511b498a108a6fcab784597d7f8f01b50ebc1ff63d0e -generate_ring_signature e85672e6b328687d087e5e27a222d908708082526eab8d9d1ed9c741309ae642 1f47514331ac0da99d0bb8bd185aa97b127a5c428ead41a49a1df9795df80a21 9 5d81cfee1bb4c5d9f12691a0a7f099897a6d2b5db92a5ad6e4f16ddf67ed1546 b352738407c3d1b642ec14ce4e87783c3461995d4a759f213a302d2f6644be10 2561702e1179fe38a61701da52b2c6470b7ae222b7be59aac2a4850a00a0517a b8e838d471c530f3c0eb2b6ef7a5e91cba4fe4ae976f5b9dd34d388678ac2c7d 00e859925f2b79171a96df93368feca24d15d9160aa005d86343f69b2147319f 59a3b6c9ca5cbca2b1b27ee13a158565ecff5267bfe407da2bf94c863afd94ed 51eb2c685edfba3a962e435aae95b54f866eb1429ddc3ffc79573c076cf4a638 bccd0d7379c0efd79aa6f27515826d350b8cbbb6713802a834a1f34f49a09fb9 5b7b1cc1529d4eebc5fec0b0d478591c0bb0bdae91eee28219841262036a8a60 4442e0c6b2c4e6cf3f5a1a33cfd304e8fd6c0c1cd8047061a19d83525b50b30a 2 69bec01f0eb6405be28ddc493a68164e688839a0ace810423eea5bb9d3c9cd0317426830c099ce0e7e65823cddfdad0e9d3071d877be8fe2175088ab0bce1c0e93fa53ff7dfb3dbd441834b4fe5eaa122c61115d189d1f268b0cf97c38d25f04e1aa0c1c47835708597bd3c9a2ecc4963e58bd22f4df5ad9c477fc4620aff30df536e5824fd040a52b9b74dfebe2266c4d84cc6075adffa8cfcd93e3fb2d5e058ded7a1d6fd37a6ef64e65c9ec6513d1baa49b915e59129b2c44064d01c54d0db720508bd5df28f4e8e01a8e6d82c7417ba1944b30e68adb10bc03e3e325c80a7d0f373ceec9b6b659f973ec70f092a3ec3b1e078d6bf5804edeb98d4de4ec05676368cddfe9ef8d2cec608465e8551ae3b6d8e23ff3262182d36d2f65a822076b49ff4db7af9f56d34052483638d87b027903fd287711a44d8fb87bcb26c30e3d1c3e82f385e4d716fb0e1a7d329bdd06475766fd320ca4a6382bd3c2928d0c817a0f0be38503f7c4c73b21081a19b6f1acab268bb658f01c4bf470d7478f0a7a1821f6102f2c96e299b0b83387758f776fca8283edfaa0292bd5c5d8919308cce402cf866116bcc080e9c18f05247344f0e0695981f1f2adbd6dd4ffb54303957d6474f3cb968272b4c8cdc65f3c356a9b97eacbd9ef2a0752e07454bdc90ae69279030c2a36813f15bc23595349d2de0d8e2956325e0a4b69f04c7d38ef01ed3afcea841373f72bff8f334cad9aa2ca2ccf0fb268d1d0495e4bfcd1e30106ab251f9cb9533779ed47637606ee9f62946492d9c25a1768daf683e4c304eb06 -generate_ring_signature c960ac5d7f59051cc139e56674bcc5a3f0fd10f6d4525685133ad71da9505d03 d110909f0a37135c260f7198ab302d84f9980cb0f9fcc863b9865e0fd76e88ec 1 5cf0e409f6ce507bb1d619490f4501603aa0b5b11c2040681f5691c5e7af95b2 1f795ee966c8a672d15337e590b1d327632928970274de649f6d9a6d71cf8906 0 c7cc15d92ebefad8ba3bfd54acbe42875a691976b7e780cf10c54176f2953306455c13765534e933002c952bbe4d194f523ad80a90a9bd80a82d76560c7be60a -generate_ring_signature 00480a8cafb58dd4fac3d4e0b78615ed33fcea74c26ad9c23d6f610dd131879f 2b7fbd562eacf805c3bf9aad99e90ecbd22a521dddc5030c2777dc9f139219d8 2 66f670b720ad778de82b255292fce0291b5729b4dca211c9f0def3ecd0f8c143 58a740373134543502818f2326766e0188b935de010814e3e61fa44a2d1944bd 118bae54eddaa218b8e7b8c9079fa5e42666d38b08677a64606708e0d6574206 1 427325e6827a366d0f8b50a68689f5108ea332c20f7128e67cd1192e1631a00b99038d5da9ccc0c6be18a00f99057a03c32db69c01f8771f7a8ece1e54675f076ceda7c7a7a93b07ebba2f3b7ccfb4d10f0140eeacea4d86551945e7984c990f86c2bfc95cadebb594ac4bd517337fc74d5b629ebfedf5a55bdcfe5e2fcca300 -generate_ring_signature f92442a7a535e39ea68305835767d33b4a1527539ad2a184188fa473673e583b 9f256d40277f781e035f26613b45660acd3f41f24a8179b60a56999115a83d77 172 fd881da6976a55847f9590a313352a1a24a7c5b3c8c568bc37b920abf088ebb1 ce48a50c2e6ce8edf3a295368278eec1c9b73b56435783886ebada20591770af c1533314426eb66f59ad0f7469d65e8019f902b053502e03b0dac849b7262414 284bae0be6d2bd1ecdfb9ad7c6857ee9a9ff6ed1cf50233e87a997ee2bfdf909 060002eb3f3b22f173e4ba9a50b9140c3cd5c7f7c3c307369d3c87466ba9ed8d d7d32b301bf7bb28ead0c666282fd23a0cf445219423997f8dd30afdebaae9b1 56367289441de9f8938588385f9a3a9f4f9a63f871cd3b72c471702b8712cc2f ea5c570305cbb52ba6d889d8b7a340e9335972d520ae71c9c13832e491567464 071eac248b47a2c4653eaee65e55030239c5a4477254f12b2edf3b0be8367ac6 3f3b3221a7fe3ed062a4d5af91c3246cc7d2bc30e9d3f85947970944058d6be4 bf078413d79661eda1d8774f7003a5816a93632a8cfba1f3730d8a127e28a9c4 e889d95ae3eeee41f3debf69be540254c8e68347e8b2d19d96b7759dfc14e377 f5dc25d2ce32defa29030708ae1d6d36673f8b41146cf3d89c7aa2f0f2b6e313 5205a7793f0a4b0d6e1e9257dcad29614c9d0f886fdb16ef4dfb4fc36af632f1 cc33629b3226d7404851fef79927fbb90c1df81cd95b7703e38ffa526ddb206c a37b372b2c312ab90f771dff4b2053d95ef3099c76a6fba984bf916b29b56341 f4044c8f9a9801a5a04a157b7c9aafe40c019207ee66d29fbd3a356bf710fe84 dee92a17e7bf79f7ba3d6b6d01b213bdada853621087e1ae1247b115a98768ef 0d696f6a9682a9f2d4c0e582925281368f16efb8c3cdb700154db78114377dd8 57728b15184b062d286f942fd3a163ba68cfca54ae3304e739ad3076e1cffce3 82ab1f9afa7f7f77289c00080b1f98a487a14a2f073cf36bf96a30440f20ca52 139c5e0efbbf51c5af2b9117b7fa58d6412d46d67eab6997857701230a94825e 4e17a9d3cbfff868b70ba95e4243a2da2df968ae5b0b67fcde527eec0c084244 923577965437137b6a933bd61505ccfffd228aae996d25fc96ae9db5b98d07fb d841ba1679e8ace379aada4547333b839b808c514f6d19e38895ab48b1a9d16a 8175b06c4a1b58b90b367aec2e81dd9c41db930a96b3e08cf06ef9162a21415e 818abf49a345f93c1275d55321fa70cb50f458e9e67fe911aca8e9a4a253a34a 2f521138d0edd239f6e54d5e4d94efdd9749f6b520fea327e642a0fbee8a5c04 9f46910d6268a0dce884a538bec4a81c3bd550b739bf5d02035174d1b6707a17 64656d4ab3a6f1c92cd71ddf45659176f2f37789d0338dcf124b1e7ad1030f71 f8c3b190e7a9091781da8ace4b6019b2d68f05dc92e54960a1a8571702b9c77d 9ae039b7b615745cb4e51634a5b8a10484d73b81a9baf555e11efeebdb5fa24a 75157319ba0ed1b0208d3764bcfe60cddfac56aa2330c76439923d245bd7d81d f997a99abab99f25b18d72b7d5b73d4dbc1902dc3e1e01d1fd0d12b633772533 aad3267d5679ef6eae0d094ae5df02b5228d3257d9a1edc0af7aaeabdb9051b6 1eb4d7c23a4c40827eefad193fa0c778f1f84a07210edc51b42d3150048f90bf 70e8bc3d0136207ee441333ab2f463ce6a456198aa7d7acf7d1c966f89371bdd a6041cc33222e72a94710efa35235937f843d4372ac73668217849e4f908f4d0 265d61d8b2272325b2b69dae4f728d3fbb3073da4c4006d49867abd4c1575806 502d8c2e6f00025493227c87f12a1319199c3283503748b32b23af0bd20eedc6 41d762110cf7279095cd2116f10ec88f99520c860d3ced6ef72ae99c53468ec3 1270155b3fa3602160171da175a36e4ac52adf90a7f52ae84d8be8471863f681 f4bfee7e8374801d097ba1199f24c2750bee92f29f5f84174200dc8f25234023 ab8ece6cf4ff2b2bf545f8c57791685361182df280ff1bbd925e8fd3c16dc329 04f1c90fc0b45e41ed9cc34a012d623d0a71e726eb61a157f7e5fdd331cfea20 ee65cd19e5b17a0dca4706ebfaae7474929503051fb26bc70c7f78f986311e2e af5af1a0079b7b62f0b2eb03964ca314a21e0be6685c1c6e6dde44bd4346b943 96b9d0ba83bcd6f976a964068dafb5329d349380910d3eb67e550fbdc46ab884 1956cd083ace90b869f0fc7041362b47a5317164ff30d5d4597f205d53a71474 402f7334d4db7dba7b1ca8572cc893223cdd7fa520459080f70cefe98a093635 ba1053f093a6d06659433238b107558e26a28d2db942a84d9747fd05839f5056 89b8acb7bba7b8cfdb045e158837a4beb36c65fd064fdfb2715ac78a95dd76c7 7d16961a37a25cfd9020b44566facccb9ee31c8663c3917ad7ba0cfdee07ceef a731d84951d200dc2dd94d18c77712c558bc3704acda6085d6bb46d0021dc7b6 a14c510bab57377909416af4573bd3b8a551d2d35421a706758408880bc4871f 59f8144bd5690d28f29907c8a958a1a70f8bd305a36162e310f04f2413ac55c8 0d56e71a5e55aa4fb86756dad3e4edf87f3a01a81987d1e13f86099dfc3b9494 5510c0c357755864b4485fa43bf7dba01d33052918604ec41533610130dbdc35 9fb86f44b481cd504258ac3975fb5a8534673fd77765f5ed90cd310cdcb4006c 3298a93187801ec62be93c5aa5900916e6ef57a4e19331597b82dfb9c00b2d9c 55b2800d41d2641fd75a214efe80a565fd5f27410783232f2a44f7a774ef9f2d d6e2629ebc2413e4f294af37b2fd948a7d1d71728fb59794fce78ab173f32a5a 23617f3efb68af4f70ba8173f61b95356389c4cf788e3b8f72cadc0d681975ff b06347741f5fdb84b017a177fd904913a409c46e97cc00440ed449900e9ec2f7 e88ad1d059d2d9aed7150005189836ce56dec709b8a22f63ed2304ad6c42c87a 2fcb144f29caa8d4e19a07f977ae6917e8fa0c830c52b0d4748ebba4801edca9 a432e7e9cfa465440d9a0b1df528d974cc32d4c7f25ceb53440e5995e9b8feae 0b0428e6f8ed8c90dc54832b15a20fcbfc82cb5f908f36045b1dcf6b69cfbfca f4064a72fdc46314d3652c0bdfb6f08f1b248d5c747998fe412cb86e97c6d83f 45cba132b9b9a0ea8525b70a158d531e0f4bab123078906e1257558b9904a24e 9b41948b3d7e5d4636bb670ce3b66d16a939f34320f0317ce20ede8a9651b700 bbccf33c2d44e1e59f62fa8144edb353961bd522a1e380f95ed006172c88af0b c04093b6c7bcd3446060f9c18cb62503acd1b1d4d5c4e7818292065bb29bdb88 5cb1f7658f73d542bfe7b10a988f45db4bb9d777ed6b0f780049ae54e82b6ca5 3a8cdcff1cd09bf1ba1952514ffdb00b204322613221f6c6efb969a36c164adb 03f8c02d0dc951aac9f5b70c1c044c06c9c98b0f61c5b30d6045f9f1a12c1fe0 0e7c767d8a26d57dd35d5aea58aa9d3a6098dd8ef2732c66bdf0e183ba23f6b0 1d6d4190d0b0f4b8ce1c53f1ab48c601ac7f4c487834f2ed355255a62813e11f f609212468d6b96a6ed41dc61708c2521aa924711029801e8e994095bb5361da 87f496fc9296d7e4a2b3757e555083f89192935073915fc41bc8f4bf447bdecd 1741f6e096f8cf208f002cde14274303ddcb25c2533b9dd10f0728e41f28e501 b091eafa29e0509f605d0671e4c207a74acd926ca791824d0e8751f58124bfa3 8fef9d73d54f5984806448be3a3b469b1b9a50ddb6c7a69a53e404ef3940e32a 5da7b39dfc6181a2cf1ee4f9f2e695b5bed73909dfc32f575eb815f53c8c0484 5e95b7c9f65a53d07781d57093ab9cadc813bb153e669de752f5c97e05571507 505ed9f5b5ae645ef41900a40d7157b332290bdeae3d19f866af304989d5d8b1 1f8959af3cf6788fa599a3a00d84c323bcf29dee02bc65171681fe42c76d4b5c 443b4dbe3217e35f436a76531ee08d19024edc0453c2d341991a8390fddb1568 7f3f7aaa9da801b08f6cf47eb23b3cc7d098c5d21bef44ab449ac00fe6a19299 6ab9c1bcc391acb14e701e26b32b7a6f7fb09da69d51b289469f2a977bde80d4 8efffda82d82d52bb1cefad9ed90fd82956db431019d040f6100ac161eb7779d 59cf1ebb7bebd9e07338a515fa9882239bcc77f6e8709f4765b463388a7f99a8 efd4bb0ff013d411e7a7d220f792ec5ca72933777cf416c5d7f2a2945071beed d1daec9942d6cfe1c2a082609eefc2c9e831a0c07dd21570390e2cca7f2dff50 bd3adce1a245373d0073f2430729aa57954748d90e3718399028fdbdf68b05c3 b93a50fe5a835ce86b56209a1057babf323e6dd32fee9433db6ed3695de7d9ab 3a1f69a2825eb6d2c90b438e9d7fcb3fe8c1e38983aadb0126aefa31bcf6c645 ad8be22685e2be87180baf881ab3dc90cc636da658175b732adfdd968e6e0246 9d57a848b3079857b0bd7c7f961e61f3d6e3d52c2194e3c3bff28dd1bd5080db 1c1d0bcc04c7ce4cc76effd19c8469638abc1e044dc2a79c7eaa0613528c7c67 f562f27b6b50c3cad5d14e7c4f12ecae046dc008d5dc888a89d796aee7d92272 e292217b111114bcfb21cc886b628e57eafb81a6f15eb72478b3a58c9fae5ed7 0398e72c9373ab5a0e21048f4fa031532ba8fa7072d08fc12fdb9cadaf0f160a 77c364f1d84650d7cb383dbc06e3c4570dcc2a343ed4f0cac0bfd76e1a0d250a 067c1fd7d8f49457bfe82088af2622f1d164681e70865f2b317e8a39b5b1d5fb 32a06673ae1fafda5af9e2a4400b0b29397a71e148ab0a10588d97cd408ce759 5a9d9c4d860c661e86742c4b8a2f9f641a50abd42b85a702f0f9078e06189fa2 939d92a6c0bab1d47d8eca4c7a2637498e41d517bfe0af1578ca6ac2d43fac67 b95cee77da3467db2e20ef20a9b88a31f1160cd4c96b6f08cdd41f719471086f 36ec08a5ba086104f1e20aa1bee9d911e499d0a83ee3273a9afad03c64d86885 d322470a709b5aa95a584918ef8e80351984e58799fe061c8112dbb8ed166e00 3bf94b5d1bc3af26948c9e95f0efe70707b7567f6c868a26fe0b3736f4eb84c1 0dacec7f9473b709d9d08eb0ebc09f8d14153596afc5f2151e21ad5f4ffc552d 91f9062ae066dc0cdb72a9caa0dfa82230fa2db6df73e69d4ce042a8c060d813 689f47516ae100226f511b2491ce607d33565d036c270dd879f725fac4f85ae1 38a3a226fb70015ede495dcd52d5317c8f837ccf80ea444e0872742b959617d8 2698e10a18971e5533259238128acaae4db48bddde16b3671e0ca608ead5705d 41e5a86e6e2986ee7803d5302b70e50086b379232a81b8985de0c6d8072ba128 9904888e211357fc304f14a141953565774a8681ce718cd669ff3d0cf1b796d3 6f7470a432f921806f7704af9e2ae3db09e79a98691f5fbb355ee869c824a9a2 71ed2c4844a82c9bb02a0ed2ceaa1bfe5011364c90ee2bb86bf2eb9ea4656198 2872fb459f19143c897500e78f4e03ed59836d14c8a99abfe1c45c25fd9dc225 afc7929206da56bfecb5fcbd92f504b8840ba109f2bed578179316f2840b7493 740e6ac8f581a0d48d1c890ae1efa021348711b7668c2bf44ce4df9de3900d8e ab637889c7cf30038e81325a13de81d28096719222b2eab649ab4dc46e306abb dcbf3053b800c54b354f4ab29aa1470d2cb41f516deddfe7dcf37a32c6baa4b5 43f4f3389d1301a51d22acee3a2bc19d8ca3b4051279ffd1cdc43a81ef0f3120 d53a2897da965ed3ec8fe3a64c84e94148f5a44e26aa618f2d7d6ef6e1fd5f45 eb36446a8f29a00e560e3539910c595e84159b0097ca45e1251e211efd4457a6 fcfc38062f786c990627e08f21824cb2d7aaec22f7b375e5f9dd32d82fb9ce7e 8b639553c2a999201727a1b7402b65f75e048993ba308690ad9ba2b65361f507 e71aaa0bbc4e864ae50dc86b9ebb8644320d91352025fa0eb0f60d0a3d054334 3e0aaf77018f0588e9bbf0adb974a83cc2fa77f42cb4209c24766282cff72286 52d0a11678b725c2199ffbda6814f92e9033c2eb5c5ec249d4fd4fe94e23a726 8f8c15d5ed07695a7d4e815cdcf4dd3dda0926d0d752468948ed919e4729ccf0 aaedeb70ef549d4196dca9e3fbb7ae4bfb81e2908969840c384d2afd34af9f1d c4033ba752ae96463c8c6d0d2b84ff52be33f3215de8f78e89d817ea24f3dc76 f1207b72213b423c405c200a7a5fd7f8023e1f239c8e606ddf71031525f23b37 baf147ae7bdea07b53a5f5e75ba469179543dd0ca592095ff616ed04ef9beee6 2cce4a9d993261d22aacc8b5846160c0d1c4fbadca7a70479fcdeb550c959c83 c43edda5a8413902774b1f8b09b27182bec45f69e5889e202060dfceae6e9ce5 22832c1ec22d2ab3b19093ac102a7aa2b8b0bd38952b9e35e44c223de1269a0c ccaeb8254c1836cfe80e63c41f1b0b12cba744f6252a115ce428368eaa19f561 aa56531344d04ac78cde18fe6182151d31569d6989ee9d5dea5292b21da8a3d9 543c8af32bfbd127e6719470804887617c42fad95fcfcb653d49009ec615b5c2 9c54714b1fd93b07226cb356e7e29e1fe9c093a22f3832f3498b6fbac03f50af 0a1d9f709bac257082747850c6adca588a7497f5853cdf90843a4deefd0ebb61 1accb6cb2ad322986802e1cf695a77856dea76a0aca440e6f7ddf7c6995bf1d9 5a077b219652fe5072cd730e904db6069475fa9808c9cba94668189922e3fe14 90a84ec912c8cf108a484d9305d0435f6f8aeaf48bcc4e11f86f5bcfa27f1f94 0600ebf5b2f516607f12411699f6dd8c8817c88cb9f3382d854d4f943ad23f29 2559aba16f4afe15749840263fda8974a76f6ae4e5643e3f6b797a3c62cf58ca 7ab8110bfecb7a962ae0c9c503a44ca616d3fe93adbebb1daf376ea312adbf6d f43d50e52ee7398b0fa244e144ee76d30d39a41e7199424950d4f00e9796f9ec 78cf72929a258d65087e8568e2088a6ceb6cb807a9ed07f5af1af426a59c5eb7 1d840f3efef11524a202b6b4c55d15f152ebec1fbf543292464bb3de84a3c0ac 244cef52f6ecc47defa48791392fa2300201d8b10310aff5450038c14991948b 74779637e05d6c6634b4639a77837b33a5931db6fd4669514df6fa3a95db181e e2f711df4622b4ede67c2a90f1b54e90e8cd0a9e9233c30ca82ebab46c68d97f 935c0b5866b20fd6094efea23ec5a44d9f38dfc5f9e13aa13681f6c944903946 d7c11734281c2918cd59b872819af1fe6808b67aea22aebcbf9dae2289eeab50 b8496e518ba54a9047c61e1c88255305f43ddbb46ee1952e5261728f6f233950 a12f64dd6adcefacf9a68dda0307f97699f45069750fd2842dcf49d30d9ed03a 8f98e2e04a873d27b24032446f3802d96921a3d364c825e6b918f4e8f3e8ab85 03b646a54e208cd8e396a18b43b96fc1e742843437aa917e6cac0d706b0782d6 30322a0e721a026ef6ff39158e4315aa2a492cbf3763723e7bc78657d9573a3c 4060d5f0af5ce15d7a8d1479313e0fee2b20f8e968cebe186761d6c01317bd05 825b5e2c98d748a4749705d0ebd93e0baca50c01b072dba309b71e7c52be9e1b 57deecc6a18a1eb81dff5424b8da3f6a6e34ec7af818a361d583b905ff7296db d576304b302f68dc38dd385a2241268c204f6d0219615dc123b0753ba0cf35ef 13f693db38785ee165adf0cd566a7402f0d4181d2bd633c081e86c5509013fd9 258de71f92a436777203c25e57a22fc51750bfb959022cdbff5fc1d3eab8b41b ef87aead744d41649ade14650cf65f719a7a1083495fa3908acb756f84468a0b 99 1eb3f81836bb835eee82ac761f9b37820d6f4bc562d6491f3e078a6294b01c006133141f93f47fac1a91ea6be8942a96034675b9a0b7bee72a6462eb79a9540531f8a63261e1fc3907828a0418572f980b39c817df7a06f4db472154bb7035028ffb6e2c90c3f43482d30ec49cdfd1ddf0fcb3ba824cf43d6b9db2adbfd3db030ba6bf3c79d4e26e4c80d6b74ec623af820458344790d6966cc076b32cfa9c030f9b58f9b7037a792d87a82064147121e3fabe6ecd2f06f104db55c7d86d6a0f1ccca26a912adaa30e7d9ddeb6f88608683e6c110b84288c958f93451165bb01385588a48e343b6aa8054e2fc7ec8bd540f1da332515ddc092a948dcd6e6ae0ed97c3ea3a9b5c89499b780c4d49103561a57c4826e6c1a5ab4cbaea1c9cbdb0b85f9342f696886e3610eff3024ffe6b3b5984631aecf82b3ba6515a4cc8c7609db9470fb36688fce628423cfadf7c80e03e7928d3133726bd957309056907d0ed37f93fc6a8e71b02fbc920caf3426c72ba3044f7340e2de42edd6b0149d9d05bf31720fee26ddf8ecd626a24bd3f9fbc3680368f4827659505bb046c4d5230419f51c2eabfd2120aeba6d99efe4745276f48ad785a4d79d8c3f911757047102798aba9ca3ac98478537226bf817c24f451403301290f595b755e164be73bc0a9c74ffd79175082dc0b302c06841b804e2d7b40e6978426a4be0306e7ba1ab0199ddcff443d58d273f2cf5962de9e10918cf5177c82276c8efcf3c64bbef3e062aa10bce6495a8d5e00e3b6be909817e93c4bdea1ac6968b354425711a155802fa7a936fcf444398e23648222f88df83fde8426d10903515e0c5fad27ae31702bbafdb57963b607cfde7885394bd49b4e5f02936bb919deaa2a5ef506459e107af5244973c168e6c05b1380e0f996815c1cb69fe093cabaf1b724b2dab71120ba242df6a6fa35343168f2fd2cc720bd9f739b25a7a643b00ac4f3d20adb2be0a7d189f62bf643ca339daae59f3ff3097051252ed6fa83c2ebe177357ee6d8a01de3f2cfc2f74ae4f8d9529171dc4830f6da960e8422dd6e1dc381c1ce70a6408a3279e201e1aa3162301c3e00275a77cb5c9ea93b8eeecd126d33baa0e8621014615b1c5b6064a89dcc1a42246099942cc26ecdf932a267a0590117defb52b0536dcca2cae6f9000a7d5893d28b4214b064ef2a14a7427c839df3e78ea140e09bee5c61e6bf87401218dad6f09f2d422813307417774c2d336c96b92f30cbe0e5d326aa10e0cfadddd93cbae267c2e5230b88992fd2d3b0fb97a8ce5e1b68f06a9d61ac282344ee15806a221a4131a42f9e35f842e23500d149e890cf088170e5ee5b4eb451750e1ae8a842b0b7afcfd7ac7153f481adf1f4cd95324a55e0007d3743ba32240f0b5042d8cba0dc99ac63cd7831884b39886d6b8df89542ef103c096dc6189f5f1700b4b304cfa1978d8a63f9673d24fcd0533c35abb8138870a410af74486335435781b1f380927ff47b7fcc988c2346af3f66eec255b1c5606f6b4da52ad21278d374c958d006b088739815161fa68f3a83a9b6a11fdda3a06efa2fcee3501823e4fa0eb2982fa15aeeb05acab0c72d2b9a699ba7d7ef6c50c24f3ab570b841b00d0482927279e6e68034faf77b22643a30114eae93a1c6f09565a07856bfbd0599be8239c2516d70a2afcaf3c82686e56e0a141f5e78d290ec502cda77cd6c61f35d1f13cb53a189a2e4e430537d3217c912c8b34f18f930ecf04fb131cc60f0c8a88c31965a87bc338f63ad01fcdcc711c3da1f31c26d004262777a2261e1e36c8fb8252635731d5bd61ab4e2a3fb536dc5945ce2454050e933c1b781d5a2c47f631cdf04e60feb3d7f8d967d20b4e66156644f21b369b039da4d0b4793141a6ebf0b5d2751b3946ca75e69c08366f1ebce41f75fff91c0432b142f911801faa9d55e81b0b6db8b3282cbc3be3cafa026344181b6434420c2556a54d30ea0391bd43fa8703f778b5ef93f40902f32cdc7129346c17b50c0be82648ac81fffb01cf9e6da19208b3193a91954954ee158cbd002c70092d8e0ad6c93d747cad97d93f724caebad1033c711c33892b61c7e8af1c78f3b40d720bc566b282e9af16fe91a16ab9b21115ae81a33337a65ab92417aaada4f26e2902383395952c2355b49fb24a4412df68ec25ed43a340f60426aad3dedcb93a4707166ff52e60607e8235637ee58c69824255ccea20e63812eb3fa139a79ebb69097aa50758d7edccf565712438e85485af6f0ab65e5d49aba5e06aadea73c367002a9d71fe657a4c15fdb013ff457b41325e65156d1034ccd51261610b1e897a0debf1bef6aaf52b1d422ea6a3355754a5321958a624697c28ee098a27a5e9da07442724cdd25826dba4adda17c309075ff152a0a34ebfd47b56ea3c72a0484c06137713075542b62d34463245aa0f82bd9589fea69ec966442d097e48d865090e96bab84d33f96fdb23899d6711524adaadeb30f987050ff311c10d936bc1d30032708ce11ce8377644046990f1530a41d6627c35b5e09845771d200a51a4260cf63449d5c85dab260bd3676e8705bc46166508f25be0501a51eaa6feebd9d7039864f8ce539d94f6dcc7e4406b74ade564655a57cfaf44c9db7898efc5d377028f149c1c4473ea84f105fa0bbed3e2dae5042db7db30ce16814f913fdba58c0485c3cc1846582a39fda28ec0e8b489dc02e0ee0dfb55e2df4e81407ce13fc60ba7e4c749655675094143d113c442725fcae7bb99d7ae500edf1f45d0fd15770b4c5484438013777ccd1ec182480bc58d35e5367ec85d2a7e1b9824d815f817019ddf27eaec92bae174a330a9bb596066ae06a0c240f25b32831e7aeb24287a02563d2fc01804d63ca7f84654bc35801fdfa34b5f2a119ec07eedb1e057c0230af8f37fbbf91348e28f9857a3fdb586e506dcfa1f115c87488a77dc8066c6c60aa78e559d02674d05bca5cd255b9841019b301b5b9789a03d019144f2137ede0f61e17b65fbcd75f55ba237145f4ee4893a551b65268c34610e7b05b7e1407a0d6efe1963d9a3f28268e428d3d50b4f89c0c8998082e6ce5fdf9222f1ce74730d7fd49b07e6dda0cf60d2014e1ea92da1030869257f3021e23c3e8389eb27ca0d133b6cfcad729f3bfd3084164e4cd865430793f32ec6d1d1ef28e94bf069f40f3fbc65a071a846353f3d9d7ca065ff67db159bf8a91bc893d888f09aeb4d650c78a7300db7906c9112f9f6e56143bb6608f655dc94de1780714843548a1e1a0afba9588a40726f22ccca2194f6d73dd87b929672ff0c887e3fc8e9608d4bae036345480db48ff47122a4f0ed4863748cb0c3f339ae46cd83e6dd72c95907e1077c6d43b3ec163eb8c80bc9e9c6846e6af823d385e709a7e72514f6e98bbf53041e346abf05dedc57348577278f9df4965d915cfcc854eb547e0997c6fb20d101e09cfd860f7d1de5100a90f89fc50e7a5eecfdcac785c6a7ded1c4e93f70ea08661d2193e7e54c9a97c2115ed696f3ae0e1c6651545c47d5bb3fc0d9e3875d0cb239f4f21b1781180dcae58f31dcbd3fe81f66012b67ca379e3fb4784a712e00e25c0a99808928830217db56034fa814ba61029c5ee6fe86e5c4a862d687e709e519703b19c9274685477cee8f4acc53a6b03c38f8efddea59ad7c69354ff506053a39727b4be66c9fc262765395e94b0a8540f266f31dd884f3afcb33d0000cb1a48421399a4243c70a920d23ca1cd94d4b28a128ac9facd30ced3616a538072a4913e4c1ac1d8d5eb35a7d7a3b5c7421fc3e3c18d10967312fe89520d0f60a05a8e2917b6ebade72ba9b81cc5b53bd7dff63639c7f084f7b5590ab2eb5700739a7c716c7894e419e4bacc9e95fd25c5e52c35621c93cc3159276edc8c2880b6a42f43d2651d3f3999d231d387cab89aa3104afb1561d52bc4800f76bc5b90a4d75eda5e3d25f6691ec70842ea94a0d939c05f39f6a804b6a90838042db8d0f95ecf749044928903eec35528a94d9e62a5cf141b0ae85d7f4b9673439d99b0704a5c6943724154d7454f13e6b4edb77de82725c009fccac878f0e6bc22c8f0e0d0be117f78a726c360e94f4635da3991d5e6c0ec2fdd48bc691fd4c2006d1015c48fdd20f9aad88a100b227e9cac9e51962c7438cf105f7e93b732a6b244505ccb2301487e9d14fdd9e9100a7c10167e8a13098f7a8efcf07862b99fea1af007b1a6288a3e042cf515a169637da4c8bc46762c2135bd79a08c9a4a82d96870d946df5869fa72114a2b3513726d70006bfe7d877ae3f4623d445769ad49e450f7c7b84aef1f450f4eea232e4370a4af1ccd3fdb5143d046b31dddcc82177690ab4ba483f262e59b3312d1676bb4a1052cc9c708d873d0db5a0fe0008839be90a2bc1abaa6adee4f9578aad74ec5ce8ede57050629ee5035fd605013c2668c10d94858e5165b2eb1deca53f1f23d6b252eda9b3bf0a993bda229f684e2fe98e03fc92576518840888a3c078f7d78fb59b74e56203239634fe1c6e077c49e9f90d766e8e2dd693b45138c631123ce72de9d06ccc146aeec5b64c6ea08781964f0ec2a56ab7c5cd4523f23aa771c1ece525c132295a204263dd38614eed0c3d8d08675b92b17292315deb991e93b1b51ad10e87168b3baeb3291cab0959a3bdfa01180eeeaafc258459b94c0dc53db1b2cb39e92de190672d0d29c38a95ee6dfd06e73cd66121e26f63fb98ea08b7dcdbc539df98bdded17e0b4d0d02d669e7100d0a0006c94835cf3788e4f215329eaf390233fe45c0e03186d2dee0f9ffe45c0caa406144f54d848578c0977c94c3e4919eb97fee102122dfdd6eab55157c130ef8d6511ce29dc1bcea87b1dd040c9f52798d8a063847d1a6a958270b850f3905261f538b66088fa29dd5e5b6598425d77e03158e0a1721099683fdbd424cfe01977f8c97f8d1ef3b3911b40f842f01f9c5e17fb4adefd68b590a2d33ec694f0e703068159b1ffeaf52813fc3524a5271127c4387f0e5b25aa4ec04e4fc88f00d7cfe81367e2398642c8b32eb6cb3e816d62fa1915b48c6fe3ebb7070e5470f0ae47c58b810101bc366efb264b2db1e31a5735a7ad0b593ec615159332a48ae0968d69ebd226ababb07b60b5708906ac5476e393fc1ca6277556e1b7f55147401a8457ac995d80999b51b73a2ca97114da0f68645c3a22d7d30ef74186dec9f0471064ea0c7baaeb52f4c404825c609cec68c414fc8c51ccfbc8a01c5e95e4a0118d90f527ec4091450022d2a7637aff410afbcb52cd67fd9f221e60a779a6b0411662304535d1ead4dd919978119a07573cdd4cbde78de826062534c9f62c50b0ed6c7cb57eb9d3981a9d95790aab429a928be4ecf9cf17424036c7c1e3ed706549a9d89bb7463519122af6d479e51e2fd2c2a017351888149643c4c4a87e0019d75e66878288edcf96d6d261b0309f18f2229ac152ecb29121aa1c7b66b4e07a0bdfdf8c3a1b4a7b695d430b63e996e57a66f4160572e2552a51b6c316196045d27527b98b5c1a0f98da697b78763bbffab5e9aab27d856b664e4ae85f5f20d0eab8a08daa2022a60367cd5a10d649631bce699744734dcb071afbf9533b30c592c9cc7003dbba26a48cb9818a0a0b89fb29a7dac101eb1597d3bb013da090b14594bb1079ebef05b8e8bbc0b0174994ce36bfd2b6e5ba5f18cb773bf85e302965f681b158b14383eb2b2d4e1e1bb5470f901a97bf5529fd337141af6cce001f9bca908c567a5bf34036b90ede75f21e9fbaa459a6fb09e7a0a5e5594576105d401c488f6533dd068f4cbf73bb5ac45a1cacc5dca3347539c10ae483978180571eee0d580a6b7f5ff8207aaa9b4fe6415bd0ebfa64b68459e7c1e09d36e480da11aa31ecf968231db5c68cc697b5525a8209d950a48c44251c59c2369cf3606d9bd8a1de0acbe473a4219a634b9a5b673e931ee55430d85f3a6090d00d66101d8cc94ab4701e3258d01339ad1205e2266f5f03d2c7d68037800808059cd3c00846b44b2711b3666bf5d146f66a2e9d14b6b288e321979bd8465e92dfed32b0525a976b427693ff8cb9d1b305d44eea29105a7f57319521fac153283465e1f006025d8bd41ec8242b91148cca70c738223c7734cb55932b7b424862ed351b5015ded45da2cbf7106b6e8da062e758bd96963b40b109e4eff93bb7fe64598e7038ea8ef500e95622b3a899fec2e1913d67bd15a77bfbbe24b4dece0aeed9ed808221ceed7311ea139e358a0043d67ffbdd21635bd642577a2cda9d454c5048000f29b926b25597d12202db7d1731a6d0506b2d94c2125c4d192a1c2f2c7c41709a5a536676ddce3190abce8cbf7c6da0910fa108edac3684d43dc9207cdbbfd04b1466e0d126468a70deb53094c990ab470313178a66b21bcbd293ff7a231a80c197d7ad65f2411012e5fc021088a229e3f2ab695431593e92b9954b52fee1901af6579d6eef8fedac4f256ba801f56472972cf2aa0a3ca2f306786dc874d520ec9986f159ebcfb44cbb8bb27c9f6338fcbf39747d933bce287461bb2b7ee8603128f03c8a775ac66a7f5bc86adc659f928364fc3033523975d266a39d15f2c05b67ab089ec267cb11fb274c3649fb4ae39759e260483d61b7eee7f014a97c606f031374fe314b96480def9c2e01a9d0af5aea7acc35ae0425e62f7c7d54026072dd9dc5abc8eba0a0c8edc1783a2cf14d85736f428bcefc59dadd7f58970620461fc683ac5624225f282f6d4423dcfc9f4be8bdd9b31f0fa6fc061c99dbc460be1363854f3d0582b45d2732566bc842de962a4bb764f99eabc861cabbe95a20f17e947d486a1a29353ded9a175a450802c8b59defadd18968551906c3bfb5006ef550edbb79c8e53a6c393e87aa6944f7acc398e2e781580b00e032c772df10dc0b68440c23ea342cb532ade264fdaa08772fe7fe81f05fbe09989863022bd0653bb126c284aa5fc1d20c5f633b584fe1f9b72d0e6eb8982523183759dd99e0a5c937cf560e507adb09f1db1d8f204b0d2faf36044f2b8a95c259a80d3c3db03a84351b1e57590ea14d3101cad0440328beea0b5e68cd40ce95dbe7d44aa180cb81e26b51bbcf369830a333341e48f92b28c2645cb0339a08767a5569d21670da6cd651ea41b810ad858f79ec46a0541d26a2e34c9362611fa8e79f4193bb70e4771bc8ce22b902ab7731b2890a316a66a05e7604fbb6d8597f8066b11f655063e85c4537675c4af73a744027588cfce73b6c9951dbb3570521d465594f9da020cc05cb9793dadf5d4ea654e1816a7c5cd785b01ace4285f6b796de830d8ef02168f2b1b4836122fd60e5480b6f24c9bac781ba446cee0bac253428b114f3e07edd214e29bc2f8c96b0b34d217762660c9fed8fcf973c6903a38cec2c3fe7d090c583cbe8efb679bad56f4298d54d77d8703e4455416489d12a6f6b93994240c407f8d70a7260ccd4de5dbbbde763e9508d32a5bacf0000551a991659c0a840c0d1ae8a62b9fa9c31df3638e9444fe6ae78c818bf8f7352bb874902ac18cb60007c08b4dcffc4c771047169d0445893a036a3db09e6d7791f73c7a5b2744230279d8269f239a75da7b3688f35c8d6b5c43d615a3269459e8e7c897ae276d6502a600ccaf29ed74f5e6947c9eb4998ecb9ea93b8a0aae57f008d5a126462593079e65512f65fdd7b3fda4b599c2a6f66573ff2d04e281988bc743f37f46b5b706c2eba0deca5b648670be3f3f06756c6ee23f8873bcde2fb3811747afc05f610640bc942b0a9061192ece179546c6975adae6c2d2e8efa74471c782cd6237540fe6b50494725e1870363724fd66016ab77a5fc16073b3dad7f1c419397215c90a7c0108b7f29e0f37085d11babff911f290ab1f052cdef8e418d8392983e2d1007a3027101c6861feacc824650e200605662a7689b09a180b9cb08591ef085c0977a015f1395590c384ce7f3084df907dd29e366896310043d0113b11de64700fb7cc7cea935df33ad442d1218d5035fde74c7cc488cd083e4b027181b365960c659af46dd510765f356c5328c762086a4ffbee65a0e4788b837e146accd9b80a3016dd062f5934d8bfedb1d60b726b5fa7c30622dfd82ebe8de9696e5278b5076fb232dd3c26b6f9cb7b14141a319bcf8096ea8fc69d5aba4a85b7bc09fdd1057f211f6ae2895f49d44f5472279e9f3465614eb1845cddd854fbb95d1a65df0d87e0cc023620abbcb8a80196edb8f1752367c7c2dfb532ad1ebe78533ca8c7044c8e4e533d21d6c741150de185ee448116d6d73d2f35efe0172a89442b11bb0017537a8e1c6efaefd37c9e84d41edc69a40794a111f6cfacdc38c89531707400961c127fb439876703df4b63627366d4a605fd7d6003fad0cb1ac6067bf68d04b9d6bfd964094ab01301cbbee6f8babafb059b65d46f9abd0e8f720caef5ae0f46cd98252dbbbf8b6e51682e685d02a4d5593d44116e7c289b0481f906b1ff00062d858bc183d865f878a8c33e2b51dca9e550719f13c78d3e4973313664620adee531fd75347e90adb0ce3d0a584a78f366e8b9b78b2f8137f6f95a8bd919082ab195613c4bf1414f9038c399cb45c36f69025cac91164a7fd066a87531230597860013619f96db483a716438781c19a4b94cc40bfed64313d083dc7302530f087e1d869410d2440b423dbd696dfe4fd53177977184c570c72f07fbd12a8d0d3f328a5fc12192ac90e37f739b6da58d60cd1bbffe0da47ab2d96451ca18970e1f0a5603ee8ea345e1ec7a6654281abb9561ff0fc6bc640aea7a2820f046b90fe976630a38808f6db196bd56654cfe9bc241399461dbd3b99a0d9833e517b500553cfb1c04009b09ff283d4b9fe007585d6dc602963936bd42509b961a229403276b9c170b5ea944cd90fe993277b0c80975aaaa93e811e515bc8ab73a9eea05c6b321098f0fb7d61e4c83b32a921f0c728fe9eed33c9c04b1575d87bc3e4c011825687267b2a6e6168729eff2ef17fc876f745dd208a99df5baa1caaa52e80e3605a2af7e93b16ace09e80338fc05b4aeeccdbc225aff659ad73c22948ecc00c13fb08e766b8bc482d67814b90690db672d64f6fb6cb4c3a21bb92db8309e0263fa14891f1b803e73599637b0ef024c373a0123f0ff78c4ecc6042889687404a45c0a81abf301bd58f9aca4f4b74dab2c99a538159dc966cd7a98d2b8eb460cd6fb77ab8d02437bee21cbe41b1d4676dfc607a592e6965ba4aa33e521f7c9009023b97806bd32030b6eab34bda2b6a2e4dca33eafe3adce6c79d72795d4410bc6851f809cfb28ed82723895a4de9f4c4b1a706308f19e322eab58cbd644450f091461461ea4cd5ce23a74364cc3a35c33d46d60c9637b92406ebc2827ae230312e7a467c3b77f38be8ca818218a4e0b12733e059f86c172b6931829e62e090415a65c90dbf8995b84d0f604465608c90187d9115f533717c70726b72740e7013e51757b71886b27572ce43e28f05729517d6e16147776450fed0214cf28a40593e868de242b788da77642316b324f5f016d4e48f92c4655eee3005eb9d5ec094ab076eae6f1f7ff86663e9521c4f6518f50b9194fa1926cf0d90435faca9f0812bebc883187b293eb3525be956e3eeed3d466ea5b3e79041adfb975bf2df4046ce17dbae4e1f891c1d0f313eb4f2ad00d99f46ecfc36b63ec848dca2076530396216caef45edacbada5f8e5985cc8c53c6bc53a0a40bb917db3aa5f360984039e880f5d6c9c4b324e2666573415538413fe93280532a255d62dd1629d993304e30a78f1eeedbb58efc05f827c97e0503b605b3c735513da4d4b594c6e707d0faf9cdcc8eb19b4fcfd82cb8a805478212044cd983363d357d34daf084c27350e4f0f965abf301f651f276a8b69552539fee69b79afd13224e070871fdb457f0a73c70b4a577423adf361b47d4e201f1fe974dfb3900a96d5768978db23c0b704b329e6e746065360d0264a3b8798cc6a72d0cbf11054e6d7aed7d94f4616820e0cceee35153f79f3ca95ddf6e48498e7add6011553c19fe1f1f8ec9d1cf09f051b706c631b9b4fd11f7d44fdc842e683ab1a5ed4bb4aba3c7aa40fd56ed73e0b4cdaeec6145524f31a1149dc7431e841b93350c84cf4d77c609709ebea5c5a08bfc13fef03547646c945fc3c612441c1e95413751f58c4d77d8b9ae262cac101e3916fe0a48cdfbb0de25abb9fc3fa059f481309b99f8014abb5084ad2c9c20aa8785cff3c2e410176fdb2c81ca19351615b0902c5380171c8049001f5599907736dd9edbeba043ac2a7eed88199dc821fa7bb74e47a76b0ab83a8aa606cc30c3695559e09ea6bfabee08b457419ea355a465462a8fc4c74505b2212307179046d9f4d6355e28a9cd105f1ede8140d824a655ea79c2982bee3ae6b32a8d5320015f663acdaa32d0dee7b995d90d70eb54a613a93c9f4dda9e262b17488772a0f780eb54e2efeb6d0a6fbc44bc1eb843cbe3c759572bfd5e9f6604d978b982d09a34c469e31d19c79d3f8d79609812648fc5379d73349d58dcdc62fc003b23303ec4403201a5cc1299d79f3136490d62583d68e3bbb414421795363d7780112097e670cc8841da92f27d927b2c6d693dfd2b3b62704a04802e8ef669aeb66f90d7b3b7b0c60b79ddbc64f261b7e85237bf113aa592aac2433a67ebe65fa17820d2aa09fb99f6e3c404cb172e0c90a5436608ebe5a38935d5f602098f1327f3607651bea874a4a8fbd813730b3786f922dbe5a091dd0886eb7c76074233f487d0585db0ecbd38b54b248383a3db1b536335e33d126abbe55ba81f2e0a9deff9502b2ef842bcd3dc1032db5ed2970e480ab0f6211f233552703cd69fd609aa94006a6a5faca8b581b570d5d13a011a634a18742cc09aa88d3c4d1bea79d9c790e0628a7e525879248d280f5e5f5d6825bb01ee727c2753f63e1e517b6088fdb8e05c9ca1c2b303627cb130233f76d9fe7ebbb7219934017a706c47b89efe9b49e06a82e52604777df96a66b59def478068faf35bf423552ce0b580282652ad0c807112b41c4aee56d4df30f22226c29c75b765df035708adefa2f453057f4d2c802a0e0905c3907a2966eb6025a4d9515f944597b1f26294e34a5a090c170a4770a45971223a5255e7055b9edb7c6c4af857013a5e0a1b46e59f352c174d9be380462bbcdc366cd24c63ef4eaa1464b7df8b1ef02eccf7a71883b61d34be9b9ae0c62b33bf80e76bb4fb95dca04201ee43b11008296f803f8540a4a0a438fbc510c94c5e867bab5a306077b14772722484f76eebb5d15115b7c6758df524353b202818b203e5147988d86372047cc0c30a6decd88fb9a06bf920ca30acc7002140a899812c1145d36ca897d5700e1368cae3f20c945bed38b6f2010d31b44c7670fdf17197242ec3b79047d46cf49fb8c58138d438a2f06be42c1bd761497beaa02f539e2d8e8b78d2d0e698a0c7de56e908e26054d8d6eba43b0a42fbc71e8250042dad9ff3fd12831afd3295c3a3ea20dcb60de564cd7889f242349c1dc10a00e23c93a9922e986e1669e953bd32dfb1ca63c29f7069b798c2e901ef5b8be780a3628b94ac3a5a4715bedba03979972421531a9fe1552a8d82595cea24878ee0c461e05db4662ba14e5c4356d626012efa93e87f584cf05de8771f3d688e0340ad220d1412256e9eb656c321fb26bdca3bf301593b54b47fdf495b3e6561d5609724e451d0de1c487173e79c19b03f952f77037953f0cbfd6d044ce70f3ffa40e29b999cf619c690c5022a1fe596f5a26464740cbc993bd939a6884ace11b850859c6877d6494163d832cf71d1145b4b4bf1eda7ec8ba0b0ceb0962ff1604a50d3bc053b4351722a3133bc00d56cee2189e08b09a66302ca1a19fc553382b52034050a2fad373ede9886b047c815685ea8656143043999fdf79ae4730033b1d088a5b1a6b09b58fb8562ac18ba9b8a937013e771a06626a7efe9b9fb012094805b4d8a82068c4a776ed0589bd9f282e2f6cb2f8176d31b0cd53a6280b78ec760d0176898ca7fd169f1e759f1540e5d28a935b6bbb0d5f30c54cfedaa9a1fc3e09c43a609dd96af79aa2a1b9c5a0685498273f8de7ea11403d0a1a7c018ee1340a384bb5abd15ef1d9afb1dbca69697d4fd1673fd0829dfa66b7707025bbe8a804924155478782cbea4371931d75100b3c58bf5de77fc189c258697cb93024450cb9cbfda4314972d7eb0c105f8baf86c17ad9a64126fd885d0e7dc4cd595e1d0d000b3ef6eb50b2de9f9e54dce4d24d6a0d79a539b25dfd99128f1c88dc5c2d0973cee877916d4976459187c1011aad2af1fbcb74f13ce40627d2065090169802649879e56a933f6eb7717013de8fbbd42bbf3de7e3abef8e7aba53d202a8580169df826bbb4229f22383dafa7e0e3bf8ecae4f73a2ce700e67c94ffe16c75e02804482d5e78414606b13a2c6818ee3d368fe9f421a720a408af019aaa7bbf30521f3348cc88cdded65cd280e7b8288a674139aa949a06ea6cb2f29bc9c9bae03e28ae4235ab9c1445fcc0966e5301555fc08fe6b9f1789f9130e1c1da507580fd230607d6aef59aff2f03bc6080fac0aee503dc1c292b83819066e0f23756e060cb747e371e2984a6261c7f6b41ab014b484b861d6fc371b48cb7ad9ffb452040f04b620de9a345181fbf7c22bf09387ec0e3abbdb40ce3842e6dd26e2e64a004f5703d0e64d49b0e7e481f876f577d316691259cd55b99d7fa12ee14239980b8369bf23ea8cf9486fd007d8734149c3842d07ad27a87f08f89c4b06e3f88c0a9eb0dcdf06c8f3ef67ac125025139802d173f8b0e0ba385a92a1091b6d823307b7fc9829a697a98fcb3e70bd23acc504910517afaf72d2e305733a77b2f08e031b736f925b61c43365e78fef20f4ea4f12cda9818964c622d3b38d08c6716f02efc773e64aef7ee0990bb0ce687f165b9f1dc000e8a004dcaccc55972fc08602d6bcbeea5544a5f6d62bbcf5c373e6e3f567fd6fd4c48c4fb19b57e76a718f01d48a7b51e2dd16a8a6be5b1e5e6874749cd8fddbda2b45afd567001343eebb0e7769521972c024ff8e0810bc0c4414123caa393cfa5a762b06cdfa72a2c5bd01487b73a1c57ee5bb1eea1487407c56362a73420fd2172c287f548f3215fafd03846fd8f7eeac6186008f1db36f148452f981f3d7bb675cbe3f21630dc86fce09d93cad1cfbdb2ff4ce94e5bd62ec79b81885503eb2f2fb479f54a61fe512410d56cf8d30dcfee1a95e1acd5eb7c1360c92c9cf652930000f2e945054d09d4c01507293b541bfbd3a06e619a0469c30e46a60b353f300b2b0f0109e480c970707487f469feb70add6fd36a8f6dfc5867b8e3d010896ad0d9810ec4dff4d6b0d0e22e2ec998fcc3bb10eb08a8ce31b884272d25680ffc16af1f0a7b421953c55091e02ac32de29409c7c02d5a3682d301347d3e4c42a830569590895714166400538620f2935fa129f02e4d90683e05de5bdf083dbbee6ab43a5a809b621db940195fecfcede53e347a9a0dc342e4f73502a5fc051a1d99bec6a156af90507fe00491d7dc38edffc560a065444b986be082d51fd943d91a1674fb3535af1fcd9084a9b184b1f12ca67d3946b7b06896ee9132fa10efff4204901da3ad277efb8061fe3c92a1992c922a41bb58e200d103f1718fce12e8ea83b867dfb691ad8f306cf450e0a886d6402d093d26b2dc33007c6873e9f2f1036cb0a1cfbc5845ec6095dd5a63fd6247ca1b56ce29423f3a1f949a3ff2c61c158967ea938ebbeb943043f96db2d4d40e15a3d0a088fa376f05a5c6d6440aa97db6fcfa364f5d4b2f703661c9854693602d6d100e31a753f8d54e7d42f8ffd65632299a6564ff9cb1406e5bf875cc315c354955491ec75b37987c6ea65d6489d45905dab0de185fdfd06a68e3647bf13cd1ac7058eba97c1777ba74636f33f40b00a58a9b15883c7f5067c94b05886e164a91b10a56d8f79637a74648c7308d1b813bab620aed183fa0d34ea96f5f240436b133d5dd6039c6346581604cf41b677989bf31d4e1c7f7f03af68ff0274536f5ffe635599c2cf7aa1947368162ea19de83c925ed66eb4860940e6f5c98974aa0bdb0d02d449914d65f7d6d7225fd170a0b261de9680bbed0013180b2ee6a969e4f6b1b7a56bf9d2942964f30fe815f8781b0911df6cb83f0f2c91da0ea30a3ed180521e8c486531dcd855a76850cb1a6c39850b6d5d2ae20ff600cf3df53a8db17e0dc79df107fdb4650a55c1b37be8aa1fc39ffe8579940278974ef472919d92bf60389917065ee6c901b02ac3c4e480b629d1f1d983db0ad3efea30c2a50bf7ae08d6b2d5b361505997ca71c549f065d0d627eede53070875cff5e9cc20b6a8f8cc516b4473199a67109905e2776fbcd94908afc82d880424fad9e8108434287670822bf00f41793b5de0cb70a76123929440579c5cf0051b69d74efac6eda99de0450f282e077dcfb5006f637ea7e640174b74816ba8009fa704fb1bc0c6fe710fcc31aa3de2905992ce162e7b3c24920eec7e20913e0ddf1ca6c6a03efb4ed328a15241485cd01a283ce415acb9b0ad3d16351f4f320c687d7eaf698d0d137d2c9328fe77b2cbcd798c164361895d7081dc1508795f09e763365d82ac4ca6c1e5ccc5e5a61ec12d74f71edc1474cd96f15982bd31400943b2491ec8780c19c83e77fa322ffe80bc5762672bcd1b9cfdaae373e27a080ad395c55c9de36af5b142b3628d6e99b2528478e52108585fc5af5658c88ca9041c10835031b92554e0e8c9fcf8a32a05c4b35fc8c2eeba82c9cd1787e02c540a9de875b255937e384dbb099d04d9c7d7d91da858c10a6dd1ccd8782b48bdd90a3fc11175c13e457f5361b8b6499064c0ea7b48b3ee84cd55926c9182c9e5d40b79911a79fb14bba9855c56c0a2ab21898cf71d82fc8e6654d3df2e7c6965ef0be5acf0563d53361639a5fd4f6d2716c7930c5216f835f0cdf4cbfbaf77980009913277fe38f4bccb1a0423e2bbfe7d60ce5e8f84ce7764e5bd1769184c80190dc1b91dce9b586e7e86a7445a109fef836636cb9f53deb78f943adc622f20190ae9da923312c46936e0eba3a1409e516f7e1885a315d5f1eda019937c578b0b04ddef02638a00cc263db78a755f2b776d384f80c68a50822ffd12d4c50493e30869e17cb360b8c5a9cc8942683791350cb75374c4f9d987729f754a2596e5c20e7d7a7334ed8d964947942e47a08b92ca94b55580286d404c1ec10b109dd80603e2bd4e18cfad5799cc819c0fdc84c366ad651813d8b7d2868f716a989cba030d8b2d5d7554c6bd53cb5faee71469e4a6161939e664ac91ab69c169331eeee703f19f9f34f0a627e67c0eca040baf0ee0115148b7af0626589e0e423120835e049b04c2fc686113c34b5e9fae5aa1a178d2e84832fdce8f0334907e13e4a3980a -generate_ring_signature e46bda5f8d177a85b3d4f3f9b52ec30d3675e7239eb271e9756c1b9dc52d0c1a fb375e609a51a1a54d30f6554e5481a94158e49705233340357ad15f25c832b5 1 d9515348ed96ba26f155412e0b4b7fb190f48c1adff6d6f7bb0d68c4ebe432fe 70df55444de1551ddad38c3ba80c388032316cba280052447947c02151dbef0e 0 8002020b542f528ad5e342b14256981876923480c89931cf94f99468998b1304cc8bd25d12ea3cd021b45118f7faf09bf7e012456b50396108c8ad01a94fb40b -generate_ring_signature dd3a63be8c1d0e7c20341ff51de1ae69be6ad9a4af2494d969de4b5c5a3d1213 bed9efb67d0318e6e2fa22a32ab381332b38f0894f8a8ada7f8b4372ebfc967e 4 066b617b0fb79775aa1b548de8757a121e7d0e38459a6bc5dd0a587a88fa26b3 193a48039163ffb32e1ffa931abbee6e1aded29f388aa635a3ab148c8d5e2568 4732c8e3a686c813b657069e304e06df196259ac5f1634bfb7713438345f053f 4f1a2284c22c27aacd12d68d25ab10b1fcdef99a708fe2903c30e185fbc99c28 79be700bc846a57a8404540767e0c3f22a32f812668276c2d6b9a2938d286a06 1 ca16aa9926e99c53f24278a84c291b482cbf608f61fa34c5b2d593dcdbf6860c766bb673a021d23e7b3bf755c1063eeb70804680c1a9bcfe6e715af229fedf0a408e206819f3b8a83979fd3582a9a005f36af7a1e541064b4929de3011e34207422fd67e7a2c7ccd7880085c205de71b51360b9a82618697555e114821b6c500300179c2ea6631cd6aefd2cd92cc379ec10322d683637c9377bcfdec1bb03906eed9cc9a717890773940a697a60385463eabfd73f85e7524de8081772a845d046cf46461a7211049135a54de00f1e58cd9b974bc6eb775f6b05028ea9388310940db5c8f09e0666452aaa73b5ade37e20d908dbc2300771e48629bcc82411e08 -generate_ring_signature 2a3fecc5533445d1ff407029a75e714626b2eaee3f6479c67a27201ed06b2b9d 01c8dbe8167151ed13b296b133178b80e39a38b0c0c38051040ade484b69e4d2 221 8b859d51a272d7d7f42eecf840f7fa9149a4dbd6a7724f6afa17c4a1bf26d8ae 8d6f021c956ea9a626077dd81b5fb6e459a370b56deb4f4fface816bfaf00668 815a27ec056a040e436e299d9632876500d90f3dde3eaf1107d9503788cc0c95 a24932b33273a1ceda8f5b32f270fc6e0df415fff64ad6b39017af5359242b6f 9a0adcc60772f3f654b3b417943433b00b6bfc27e8b8afe5fbbf844147e8c5ab 90f1e5a5891e354d4fd02205a33f49657a5de15d7ff4ddc31f39d8643d62aac5 450f08d11598d355afdb687cce5632cbbf31e3c1e0766765ac352dcaf036a67f 3866d2f6934814b1095f0e0a1f147b93c50f15716e254c9e7d74645a4df78e0e e3491b5ebfe4fd7c5bd3f1dd3a40ab2825bec7c4d827e0599f1145fe51629f3e 62c9bd5660490a1d2e9ff39215eb040f736051a242963bf2f272eb9d73fe132f 71e01607b9b65d973be08bf5983d862bd42ef2c3f0fd6be7ba33a54a5cb909e3 d561622d76d33431f0a7881258e4906e5774696441e93194eace2bede8d5d095 9e1c44bf1c3513bcf7ad2c087c0eb6f0d19ac9288a21838cbe629312543a7d32 506331bba887f11ea4ad8c9044e332c082302838c750f0b0ba8936b8dfc70715 a1f5054ef80b2ec1f151f60451f4c982280f128b8ed831095ff4479f2adc8b19 19dd0493e1890f8e0ac05b9fcdaaa0bd9688e2ec9f47e2432f02ca60992e4bb7 8d2df708b2ce601d1bf7d762da0d31681643d74b8ef61e061c1f67b8e88f3975 b765132b1552f0c34491edcb6d601849aee7557ca805edc5296334860376838c e4e824505152dc5a1800451cfa73437c266b6a0ddc9a6b13f8a82f2a308195e3 33be8cd89800df491acc53ec6eff8132fc3875a454d2fccd019fc6a3d4aca291 f7289f047047bfe7fb28bd1466daba43a6b73318aaf082fe49a9703f2650c54f 85aab4738840c444824cadc97dac6f1b738102ab8d495c5760ed553ec8cdb8c9 eb87a343835083e45d815729559c3e1de67d69ce50f0f1bee874a3e90b8701d8 c58861edf4520042f37e873b69016220706fed7fbb5f4ebc1e21b9fb5018e20b 9febb8ed1c5a2cae82c73149c0fd7accdac0451b8fdc81121b954dcc500e5dd0 c5825602f9ec7f01f1d7905c3b3e7a4671178863672f3ef2c83e1abee6788522 fb3fa6e53af880f0cf31f95a65b5181b4be788265f9a1c0cdd5c3f1718b5bf83 f6b63d638100268978d89ed0f6005d24a1b143c112c73a918ad27b6eed6f0f1d b4589104a456ee51feadfc532a6f52e5bca5eb9070d0a77a02624d774d4d2eca 1fc0934d38877661c56143af7277eb837868e8465f0a3351efe115d1f61a6b20 bc4f9dbd0695c462a8b57fd6b0af3974104bde49c6062b5c4bcd09615d028b7c bbefa2f48cf69f7b1df407805e23764ac7266494449ab9fdef0641b4f13fefc5 1c9ce4f9a6a3931a3ad753ba874fae5acd3e85351b3456dad3c0a8ef85028633 b378c247b6855244af5246a0760ddd1a703629716b16b50f2f7afa5ebfd57e01 88adc9c2bec405b609d7190671cd84ca990e3f2794ab3550fcf5ceb569009fa9 d063eb0d4f7ab5f0b7bff340644c61590014570207189c73846322e0b77f8392 7ef5e21552383ce730b75abb6de94d8623ef8987e2d382d85dca46d2f5b94324 7aeaacdda090144ac43de8e04075421917f57f1044871738ad85c3933a7b92ce 73efd0833c7b48c215c0f92d2e72c9584bf1a5d6f15c9d99e19a6f6808a9ed8b 65b8c07bc87e20fb9ca0fb72787720d7a6653b04e11d4d31f6c699fed3ceb5fd 250511f67f3ec1d43778d1c712a6b72090c1812349262f5aadbd37e6f3cb69ef dd168f71e4b227d7417b3a6d0c6dba72b1fe04e47b3816569f149ff7e736c6d4 e1837515ea0184def7085f6455cbb2ea7d957942dc3a67d8c6c2ac5c3f588528 be818f8f702a06b5757bf5620b6ed5c110924a73252d5818b4903036b38e9ac2 9348070e09c4ea8e76401842a74161a94a84a46143a1242e60bcd7769f8c10a4 775b7d0938679d7a173f56742e08d0eb911378e65e137548d9ccf8ac6f61e00a b44cbf01cc2e599fdce193fc44b58c7e9f67532e6aa0cc18a771823b7709f379 2452d9e33927a56350678ca3975ed7b2c8e6d4d567ad7ac0e4126b1a55dde552 75b837f6b344b941183721fb45bed9ea0f5ad890e05f465b4f0078b8c860ea30 626ef8f4b4cd5a655548792ac7acb47f3bd0f22411a91d39a6460773b8b962d8 173a57cf7b632d55ba237914c471a75ebd5023b471406501db025318dd7c5afb 6e50ed5141d31c5c71a32b01dff6c0f9b4d1fafcdb59e7363d0b0de0f6f22049 353c625beb6b2e3ad123fbb9a5b2596f38021bbeef7b5e0c10ecc9234f6d553f cd24fa2bd9e36f5ff131b44852c0bcb99d82f055792ed8dff48e837d1b4212ca 2388231a621404db213c4060ff612cb04291828799919be7462d9b0ce8db9642 f05660f72b5d1b9c8713a957604cb32b1713a8cbe32a3ed4c30f9f581d1b10e3 a9604a2437780030a1ea1fc481a7d2d538570c08f355696156a8313c4dd6e00c b759e38e1dc01c64386765f177c8aa429cee8ead275d6ceb7eb13ba22780e430 aca51ddfbfb7e7d73b15a6a620bd5be30c5ea0fff58d2952f2764ea5378f8e15 7bebea82546589b5992f3990f088c948ffc03551d65275de4cf379fe08a231f5 637176cb1b76867a4a07c3f4a7e972ae621c128cef42937a4f367b5b52688ae1 444b6b8d2ad3f031dc9884f1c8d33bf92b0f8e51a9decd2d1853a613a09ef956 d2cabe5ea3d6b8e8539f6ecea33ec239ece6b1d362ec8ace8900c7f5d32b18d0 12ce5eb61291c983d787b75b4bd4b4fcec42870f7f1cfe8d7864d955705863e3 ac72fb9e0b6a14fe14a7e4313c3df10a5f6a4e424c8467547d02ee590df993b1 8e7757946777d73b44177c05b9beae59bc440eac16f548c95931ef4d608401cd 649919d443bfae5542ed3d35dda9d4a8f5027f8529eb735ed8cb27ee408783c6 59510fe2efccbc9e071932f0a1d6295e9f0c23375af49e4e1ac630b6b2ef7ef9 bf061ea258631a7bf55c1bb2e782d745f8fef6433688f4a254b5871f1a59cc8c 37542533284da65b04bb83ee315c0aa265552d9583d7805f02a4d683ebe87846 b32389352f301af4a588d53580b2af6d76947705d45414e18c4ba8865dff0f33 a48963364cf079b4fe0128013c3d937ec06ac6d3cc4adb28165c39a37a0319fb cd8c533905d1ff627b1e69130e3758472c86174adb5002f0220369d51133aaeb 61e6c4d613863a1bfa1f9124380cce995ac5dd90732f5979bcb29d42de22eea4 2d0723b5254153eb2a17896b7fc15b8059d29e5005020ef172aa50a9ab7ea7a8 8a743f0297f0efe2ddca293984ee8d3db4f5cc461c2aa9a487f13494b59fa25b e6a3d6ec91a5aa85079c7580779b393a04c54782b61e0bceb0d0d0275cacea69 0df4d37fb5c42f35624003a5cfca5c3d6eb240758569b5d26f16616551574ab3 c9a427a5d45f34e865751f516eb16c1645fc0d34f9d3f29d15defd41c90d7c11 d611c94f2cfee7f21c74272603f669eed93b9be74f5e0aab045bdeaf02218e11 4a414aa192df1dc95b7bccd5352c3dacee9da798a587be8d0dcfc2d932e53c23 d432cc97206bf2028e875b0c39e098029670e2c499b88ef451b95c526bba2f09 bc4594e40b1c06c2dce163483efb045e8378d73d819d4364f8fa4519c8704a16 37e6a276d50c83cb2ab5a9f7ced57f2b229edd17e2c91fdacc32d93925f10145 290c5152fffc9d95e8dc1d815fe633cd79b1427d50c8db67e8b68aa2c4b98470 8fc57c77048100572648ff75b9c5d82dc1c0d0fa5e06f05afbf9f201712adc4c 0bded12b4809fc9ac013e84373728305e8464ef4c098932625294a4011450e25 b8e535e32f58d625101ff5318e30492fb54507ed21adce526c366d5d3e6830e0 f8377198a04f2faaeddb95b3184eda59ab45d52583bc8e6277e326817f5e288a 8ebb388549bac9d0bc58a50efa290860bafb5c558ac5ff97014e5e91e2b5d6f9 80d45483e6d7e55efe1cf485514b7c9b15ad69550f8228adab97dc7dedb77f2b 860df3068a952468d7de7b480ee3c114e2af6a1bfd57718ea70a532156d316ae 1e7db7133690a920da094aaf299bce31255ebbd1ff89019334069563d97a428f 4f729843f70c02f0aae4848573fa65fc44fea293545643effc5a0414cbd02b72 f6ea093551a609547278602ff331bffcd788474b51a4ccf1576e9c8f4c351d0f 0d1425e162b53dad767159eefcc68e3481e7aa15cf5dde15c560e86e9cb0735a 0e8af77622167bf235723615c1c3d7a819ff60db6c5ae32e80f5a2856a68daaa 1e59be38167c56a186e49d712150083c5c1dd7588433b428e23062de4221fff7 b43c12e5bf504f04fdc66bda7621af9e8660bdfccb2816075159ac2713ca3f2a 9f7666358a33abab5fd147f4adcdf57bb32e12150c079be07d8fdeaa9ea8beed 521a52a0b3e192d19c0d35c561f27f6eeca58ff13e58d529beee488b93d2f3d7 7e7effff3b399f59166c50e33f5cfc0bb5260ced230184bf8992be8dcf887b10 ace89f0a5fde0334b8c4cd6372814c0e424e477a71838d4ee4be6b063ef83011 c6f19f9441c7443c9aad2a73076f466f5e4f905ea61c3f294bc20b588959ee2a 2a635eef90451e80ada03b004c75fab54e15e69f569f9c3e9129f58c713f2b49 4e822815ba216e4e657c7a4ef7937215b6d62196d294345f321589d5dce6e209 a68901cbeaa21bee99f17aeda62c6fe986a2938494df1fac1741511c6c11cf88 43d3eb9c030f4d9964d5e9c6ecf2b639525d633187e11f0da531bab6e5ee8a01 c0f196df45d950b38ec8d2629d12aab07856874494c29f71c6881c1d9854d2f8 43c9afda75cb063deb114f4b37dbfb0078f8cbee6b8cb740b97227cddffa6a18 d75e8c0dd232a4a241372836d8f12afa216dc4aba3cca2f7238d37cdaa54e1a1 edf772170c15f645d820e11095642e27a7fcb2a1850d1fd08f3e8f17655cb480 9c47f9140a0615c1532835e0f0bd405ce7ccc8b385febd11a1bdf68f3e921259 9d73a698ad37b84a11b02b49d6bfc96929194baba505645ef1761bf23fedcf61 2643526f0e6e1cf9f2af821278710b7065f0cae0ab880e9058c329ffdfdf3ec0 bb76790fa787e08a246e71857c921d4748b40ac6e8451baad291e7424edb40c8 35e56823de9d39318ad185de2a1d4568835b54a6abcf49eecba5f97ec49006c1 9c705590ea073318a4982ce47e67d422e751ad1043f7510e6a104fa8a7a81496 1bdfd29ec07987e54e8c928f680cc028dd1672ee6415727a8d66639cef804fb6 6b389fe787a31b450f1818ffaa0db20cdfa9b6880fa0ce0a7c541a90dca40242 dde10545b8e2fd54ffdfd483c18afccd262e204605538588848ea1c0c99efb03 4a10e9ff89c15561cb1157fc992e6133281504420f132623b9de3174658d28c2 c05d11b8468a9c285d195c40436785b680eff853135ad3202d85fd6b3b71efbc 709d67e136f6fbb15c6b209d56c894a0012d3a3010bf785ec6cf46565c7ecaa1 a551208cb6283ff8ddab393d47d2f930957a931a70212bd55fb83eb03497ffb8 d65abbf9e83cd429a72a2c0b11e878e5a4de06c256eb6ae8b7ece0ee7ec4e899 a03de6ac90e23e9470213a21b251e70e74ecb35077f2485ebb2f7d150c1f79a1 83ce4ba9a0d68fe3ce9421f42b8a010c550fb6100c2f8ab008be42902f3c036c eb4edd84bbf5d3299f3dae898586c55b4483e0afe6f4c253a779b59a928d21a6 10b0465dbbc0d6ccdd8fefadffcdc6cf636c7d6eda6ba735718e6e0bd4ea0ee7 f77556104aa4371f176d65aa01c0c3dc4eda40e10df76beb443b450ba666c16b fceac9a002728d8175618729f289d0e9a1dfe429ef2b9758aeaa63990daf74ec 96ac9c4c4680607dd545ce646e48ba793e146988aada0c9c0581a41c7d9de1d3 0f749c6dfa9b9d40996764cca834443751a654c1396161ff146f145158946816 873a8cfa29fadc9e3fa66fcdba54a99c0d5a645880fe162ca31dab20003988f1 a9138ec342b3cfd9fb544a5e5c148d2ca59bc62f54d27fb22c39e6f6915ccae3 e43bae09f5a5fa2596ea6f61dce8d81f0b9d51ae509cdaae1a24b963c274ef40 7a1b36d60806fbc951852e66ef531c403925e9bfe7ac324fefbb2fd81a43baf8 fa159a7bfc0fd40d2bc5a70a1bc9852bf10bcdef8d8e2e1ca9c62161b719e1e6 ca533537e8ea96c9b417a07b70a3ec07d0ec256f1bd14e8b34e4d1c89f72097c ae8e7c402fa13e037cc53e9fcabf06811b17afa21020b647b1dd686834a2d67a 6ae329a6be8103e2e4976e0950fc024639b41b8bd921ce630667216be4359db5 1a5b42a72f462555ea91f9e43c52f9154feac0f2b4ecdedb450143d090b93d06 f6e54cd2ebb518244dc737487da1d95e303b6ab78196a6c9c59212f18824b0a3 51ad7925ae3bec9cb057ca255ae54aa59698c225e5a0293e09d0e83fc4e5b7df 4984ed3a617023b33947a5644cd4bbe8ca79b562a091f7f61080c96de880a9fb d9fae11e8d2eca9d88188fa1b5dd6088ce1875eedb85335003b8aae6abef11d4 e20fba3947dc0003f5b6ebc49a40df8c029408546659375d5b22974c7d3ed1fb ed2616df75233d9c496a1f0d130ec9bb0a318c0b05047fd8bdcf1ebc2edf62f8 482f06cb80351780d5d49898014ed4f4582348c4ad33f283311027382516c54d a59efdaecb58328250fe5e506a5afe36662a5265cb4782eac0b3839761e87b5c 8870807b28a03b28321d06f6296ecace564cdb01d7f37592168b2d2ca075c7c5 62a59c15f0a3dfb0c1949dbe156c2294db9f840dc3bdd4b342b8405aa28391fb db2b5cf69db1da72690f3d0bcdb621e1ab6bf2e8e40e1d0202dcf35d49904067 31aa4a6adbc872d7d62189c83880b5ff945bed0da282b75dad30a60790c0f33a c93ac37aea14d028789d9db7fa08d8ca9856c4b1c101e5f212a2bbd71e1784d3 c8bcacc9a538112417d16664b694a837e6d397b1a4de3aab80db4d326750cee7 a784aa9d92f6e9379e576afd1175a0e5b0540f3145173fb30784a830e85487dd 0a564ec2c2d56e504bbc96330a884de705a47a25142e44e039b915264d74fd3a eb71a630f352135a48b03da1d0cf9d96832fa4ce940e6bc1ff73c47371b47754 36431d4d8a33fc41e52add24baf42d705860d5cfc83aadfccc6e4fbe7c6c972c d16de71a298b57cb77398f2f0226415bf28602c439bffe0334b0d68905d219b5 4ffdcfe47d26d0d30ef567366c6eb46275989a8f847b39a8f30d89a74ebde012 1c1832371c681f58df93c6c4fe33292285a801c2e21ded6e8191f80800b981ea d5fe4823f4b5c221a275060de1d1bdf72672dce31bd3f2e0a474fa8c38c41077 bab9b85dda37d0559958c8cb542609ac95a5963fd4631ccf14a6535d320cf9e9 15f589eed4f67eb0593ac0cba466f4b1be1bed4fca49aca54536a4a24a77f29b ed67d0fca43e7dc33c83efb68375c3a19fa8eb4536c49acc27621ff542e86ab6 ca9dbdcd0b47bf2adb0e773a0cede86e7dd2aabc5e6b95c2fc3c1b8c5210ae59 5e915792e97503b657a9338082f5ba9b24e2fee4d1a2938a3acc902e3dbf4997 d633420dafefbf863766da7c63606d22239d6548894e26f06d891bd406b21524 cf4f849796892dd5adccde35a74999c25c6ad3c803718becb4243ba6d8c7e1d5 13167c9c0793e87af248369823f245d99592a43c60a15349d44fe233c5bb9f2a e183538bc5ee8ed570eb66d393fd056793e269b109043eae5c01a0a090d18b3e 276b50ea1b15f2a45809d0b22cc7834e406c1c1054c0c7f7d8525c32e10719b3 191e9285d6d24e95fd7934a09d44920af2042291e4c9a8586e0515225c8413e6 de9d26522bf6f4ed113d33a1443c5f160de08691daf09272e7ea03232f4e6e84 d333341edd7b38a52be376f8287d13f89bf6b01dea8a50aceb421d9f602b05a5 b8a0364136c444f623e4dda641be4088ffb270a4c455c6e941d341681c6d6d6e a62fee4d42bc6db241ff2513f2540f6c7db4fee24b129717e3004f95960397af c259025eb09f04eb7431f508b3c6999a3418bf6bab8af9b55cb7d3fa4eaedb87 c27c59ea6664edb562515b3f4c71715483aac96657e26017b054f01777db8887 f91c66dd8483e994c9ebaac3688076e8797d4d67668909cb2192d279d42379c9 9f58c7886468a9c83ba1642052eef85ed2a45ce928fcf10d173f4ff4c89dbb44 4653a8407b6d1cc000eb05b1b840dd14bb6188ef1d37a8ac72e12217fe166472 93b14e264350b8c30798d2a39b9072feee77dda2b52e375196b3106022b4bedb 5273509157346436df1b7f67faeed0c0a43e999f398466c246bc2e9286b87459 10017a84966ba1304aa896dc9fc5708330f19054d064687b39376a9fa3241fb4 9d9063ccd0d2d540300637243750c6bc652c1f119651f81140e5322879d82f8a 940eb9dccd7636d1f891ade6e4306521419fe57030ad7175cb3c8da0fc72304d a820c82bb599fdf8d086d39acd194e39f132a85f18d8300f4cc56a8220d225c2 233233f3420506c6227ac4a72e37a4bc907d63b291a4f32d4123a7da47159514 7fdbf1d6f41f271391410a05f7a8d30e55e1777905673b8d4b3d5219a47d656c 2661848fb57f9b929c0982fa6fb2ed2e504d2d44448cf087952dde5ef2ccd758 07621d43208d862972ab719f654d757ceccec4f52dd70f013e2cddbb6119219d 53f0af85e186307dcff8b2ae1b87f9dbfc5250f5bfdb77f36ef3aee934853150 34c6dc33ff9b31f5cb61c6cd9cac5a57494ac89ff81f6eb768373507e2331f2c 9dd70bd2b9228331231be2a27d40d3d9a1bcef5c5065ea456f091d554b47e5ce c3f51b37f9ca58c36acc1832c7a0ae39d1a4cff8fd2f754d3d05d546d2c0c561 362b58b15e123a74435aea20c0c96ddb5ffc7413018c4ff6540b6c3b941d1b88 445a8726a0fe3b24c15b4d1df672059bace0614e07f61b3984a624b9eff63aaf a49862a6574460fc5e9b3432da844458d407ab51632f28b02aec0d5af356937f c9d3ce57149377557ebb03ffd48b85bfe30f5fe44819e2d589ebeefd05f4da1c 9184c6dcbadcc0425ca7f69ed9a5f120cb9fbd9126c761cfb10307c90f75af22 e5b3ae461948bb71e5d5c1c408cb68a6fc1773b89f2a16fb06bddea5bebfe84d 78717fce1d774e4857b31faee17551a3c70a161f9733b9e82cae3839de372e16 5259c3ef1249d01c1b4270759a51df4e9c04dda8a96eebf5031b9b49ad47a5fc 2c0a3e8f2ac581b434d56652e75893f86395d8e34e0852ddec0496e35e286b91 3b2af796e8f040399653cc212a709de714d5259e74869c8ca1879c4c35b16acd db2975a318a9c2e4e8fa5be0c62e49d88c4b6606dc1d9cf71538c4508d68d92f d5c78555d2a2a033d104c868c3d908855247a0a9b31047b29d169b1be2eb20f1 d1a7e0ab08e9236ee740f2c42c57212be40213c9a5507d1f383d1c8371b47669 f7b7d65be98a75555b03e382aa01c790f1952dc8f8b019a34f2d6418ff64466d 773bc5294e59c1a624f89f25b359df72dc603234815e17abc0afe586dd33b264 25806ec523cb87bd4c3f5425a78fd7f879978b9d0a5bc2efc4db3a80340b91c5 1a0d6042f55fc72643c05ef6bd3a0c3a05589780b9c37779dedfabfa12be6926 4474180e2cb3f2d3ee664c63cda703a82d49c0da75dfdca8aa10c173a85d04f7 d2f254cb36b039214a42b95fdefdb45ccff86bcab3f38bbf3f06f7bd6bd6c324 fff1eb7b3a6e85f22fe1cf95d3d7e28b2c750a3e711b75ecda1f21279301cd7b d82cdfe7dbb0d0d15a39d392e5529b61256cf6dac2ca0c04f10c81feddcb5502 1f7426796d532dbe452a65916e56583c418ddab1d655dbfe9e4047d5fefa95c6 acd10a2211b93dbb4ab82079d09b712f7060980023a09ce67440af525afadf04 190 f228960e15bb60c23b41955e814c020a6fa06cff386e93b4a292829c1d96f30421fd3f6207194596557c226329dbae1c9f376aa5822c7cbaa9e7eefb090b270db002081b2863e831c493104d389763229187599a3ad4afb2696eb4a7c7b872018f2494e4d8b3268a71ce41dba2155de9945add72b768b4f0941d5f81a1ee9204f00fd63c8d07335985ac2ffb6b8320e91ec4a43ce2d4988b5fa48e4ec87f340ff6993276f43c2e748ffb8e391065b312628ba8d2bfe55758c65f23bc23089d0d07b75fd379e489c0942ddbe63cddd9a3b762d7d78bab85561254edf5e5f106008391e736f2319f734666d81ed80ecb3ee90e126e6bb694d2be4e299f9249a40efc7b35954654a7bd7574ab2413a8dc5728dd8432ac4dec1bb3dffd3821226b01a017d43cc9663f1551913d8e4a15c85aa4597083b6257b9ada03750adedf6f056abd30613422c42b5a0067098dec03d9719eec06ae264d066880441a8c9af30d022e772b937970a0e5db1b1ffb20e66115980bd3c2f356d603f0ae50b09bf60b77b884982a556b94d94984e637bd26c6e8cced1cd064dadb8d72c950fbace00ed805d658d4bff0cc814a726298805fedfaa03d0b75573fc4a2f442c377e7d9077420a5e2a766b7e11de8ad617a1e61e83401dfa9df7c133f8abb3563cadabc07e9a885389f370065fb4d2cd7d611e48267ddbf206bf28700026b90d82268f701d17f183e6ed39b388daed5d29983f5a953ad77b7b981504c790ec20d66f19d0e0f58c77db5b73b08b251c7b5dfe06541cc487f3b9df0ec01d5e1d7db5032260644efff9b35a3c45cf294a31f084c119b8a6b1798b12c43c42a9dd1519c66100d3aa7ee972217c8bc8ba2264d7187b83042ccb89c0865ad05d2859052a450b104de8ebc02ba0c5753ca96384284e04586571fca1ed2476faf6e51d6e652c0d30ac79165dd8f3adae12d4ad8f8610b90133aee51012ceed9883bcebb09ede2980a53efed00f254d8b3a5796f76d87488bac81fc1a9fb345e15db9c215aa329b40583122d66f7f86db3a68daae471978f96cf7b213f13e68a9e10ee8ac9ac8210064adc8157a8964fbe324a0ce977a47f791af161b1024f8fc56e7124292420ea080d059d2362a0c1346348b6b48b8b37ec7cc73c27f6325b45a7119543278a8a07b758ec39fc8283bd04e642150211cb779754daa6913c0d19e2477aeb678d46085148e41d180d172b0a3e80be6f13a0c9c6c68e8bb5cc3fd20588bbe51344950e1aaab3a090bc506aebb40f6569def33f028961bd4e2ac98f28e783e32795c60f2c81f87176d8d43cbcee4a0eda32dfc2f7ef09e202485c103898679c592db20131e40cddd05737c2f20dc289cf12c0a76c1e16bf58614a2579effd08ff719a0f8207912087c83b4e5b79df2e86477f7f6a4c350ccf765467aa928618f3ad4b02068eb50f0647a4c799d233f364748e1cc8f5153eed29cbcb7408b07df2286208d02742f20b68bea9957263ee871a1e1e4c910adfc51409bbf01b3a32c99d220a908544d4a50dc08e215207f736d0df70ee9cd5a6adf5f383df040b1e7c59e00402385a9848ccff005bff9ea43384b68ecfc8d6164ffc6b93541005a85de7670b6662e82a2f4a33a7b6c8fb7eae9aba49b6eec6471a6375492e38e62fb4fd0408adf23948e04b9d013b82249faa5801500ac29bec7c014e151b04231e6657060daef331c316355dea2d651d665b1978b7f550450575ef5ca450f259e03a693d06cae1ece2f7b2568e8dc0ce280c3778356a4638523592e3dc051cfa9397ab0c0b03d69c71fd6fa66b28c7a9ab8456ed6f5e7b780510f8ad806ba91a96eedcaa04e4bad829cc6d993a4f853f4dc34ca613aaf9851e851b446ccfc2837cdf4f7d02bc97f46218e4a89b0926e8b17836b8f438e1efb9c60b1b636b346f08d491940644611d74c33a9ef49c529c02c10d27f82cce383ca153739ae6e13dadd00ba7097fb465eedb7456c5600de2dcf2ba1fff116899824e81098a140e3d554006240f6a0624f7b41c9d18dc010a8a4bcf989ffeee324c0daf00f88413ac747df4260f526e7b29034e7af06faa58cb6b8d109a1255dd231d4a718dc8c4385211141f08df8a034e83cb4f171ea95936711949f67f9f3b2a93d340ad908a1c153170420d440dd910dc441fa71d89dbad8c9e1e977760f85f0de6a6b5323b5abdf089ec093df5528354fe4737ecdd94dcc6e080703649c2a9aa8a1ee77b41922408b82e0d1811668dbfe260fb64bb31fcde65ad510283f056ef4921e227d8d62a9e95df024b9a052a6145bd416f171d48e04d09744302f5548b9324610109461e82aab303913899979ef0f30c03ce085f17612dada642045df0b830ddebfebc64b3153f0cbac6def5d5683ebf4bbe311c6a58d97b6b94c17a5f15ab7f456b0e3cbe5cd30566d6d4a32f7b9b9a1c47cfd99da5d0298ea81966511ea42057aa289e7df4f60a009e82b3bcc670020dfcb633a2d12c917c78b8af019faea9f2b43d225e86280afb1551bdb60b3cec9935e34e76edd02d870a89a157c8b6c9e275905cc9469b0c3403003cccb2ccecdab6741dd2734f55b82a93c3a7ba3b8faf2c0681c987d5061058820497685f99b8e6470aed1c650a98d6f6789e0c56318bb31e7d25933a08830a0911e194e9313619b872c3630020ca0e22ac41e06d56c98299fd78c8b1047bfaf589a6b608fdc5a26e38a0f822df9f3193c214cbc066d8e361df83e7f706ddc712ec8d2e1949a4910a363d10da5b351a10e75d00b4ec1ffbd97cd61ab3036a88cb4f5b353e05e773295c5d6bd4287eb00778b4527ea7183ad2230c7b880cdd8ea9f26aac325e3b79333411cdd68bf2d2ec202fc2bc2e84de76186e0bcb07a8bf7a5a3a2c599cfad00b024b59fcd4234e4b647b69615b14596c58d46df605f5da04cfbc5da714f6ca4ca4c51377a3c0b6d7cdf0ad6a7056f34d30acbb970eb45bd40880a600c90b6284dd6d32d7bdd4019dc6485559cbb2dca92c8bb6390a49276e227246c028e80690b08339f4309452d92c6d0f684d71a20875d2ccf405a9a21c4b697d970e444bd5942d4a62147b4876951e0e8875c4af860e922f4805d2428ec7ece89bc1ce5408b0822ff8fcda1726f4ac816d90352c27c8dbbe8e0111dbf042decdaaace9f30fb95b81d1eec43ceda21a8430864f5aaf39ba6433038cc50aa2c0e83b71d2b485c92c612e2ce0347cee908d1bf11802013d605f380d1d38b1e9231c11bef5da0f709a9e3bf14891c7aca60d01255a9bad6f64bcc70ae8820a2ae22882a76b6f74f93a4514d5e15a335c3c496a7a84ea53b2e50b0a00771569050c9aa1d563accf3ce61e11cc7dd724966961b403daf0396fa4480d0c558e6091a201ddda9fafc799d055687d8947e1e81c544788a3de06c7655f9f0ac78adaac6a5369d3e9d17b12c9608532179bee45ce895d6bdad751b2d1a87006f0a0d4c58775aa526478df22089f0badffeb8a9f12a55210af89eb74e97ded04b5aeb1d7bfbb87e3e6dc336f0b3235a1b267b54c627e4a806c5feddd58848d03963d2be4a295503b0573bcf8c4819128a3beee7571caa9414ab81bc130f8f4018bd62e6eccf8beeab00e8cdf141b0b55a8b7abd7931840dd80b8229bf76aad06b5fac330c9c9dc11b62d31df2337ae982104d0b70d2e0b2851c2716824878d048d63debe6a727e5816440ff99ae86a7c528fe52a2fab448c7944ed08833eab09e8b126d225b350bec5c172ad42222f4b97bbd72284ee721a6bee05c776e1740022a28846a687b9ccc1513e390a1200c223bd625dfda4158d03db0df60288850bc29fa65872819b8e4ebc74a1f1652263a05abc449c13eb1f4447ff84ae23a10d60226b65c03caf20d30b493573335cb6b8a4c1d0a5d71b0ea77edb88be14a20b3537c5eccd1675f861551704b2fa83c09bca45f5dc1399d90c4a3ec281b0dc0c3c63b0833202e776450139049c9c33ed3f1fb145781a41d3567e76933b89540c3711e230da5d04e453241802edd99a55994f52bf7ea1ad0c943de722993d7b0ae4427bf523ed08a9984f00cb76b0b426bea7704c8626457d7fad204dc039cb00245958457e8f6cbfe824b6aa7528c1460c5aa343bc24e3cdae3804745f5b0b0b01e30ca670396206450d24266a70d83253ab97ad9d86f613761f5e04f4283f037ebab01cb553e19b255e8cd855067d88133845352cd585e55d3f37089b528d0b6f925d43e1b7bef00b484266221750d84cf394bfa13e02bd4c29040099242d0a72e0808454d7bbcbe3b6825009c2fcb7ed7ad79c3055f4ac251296273548ee0f1ff3ac4454dc7ffc3f461815a6712bfb1291c76c8275a5d28dd6a9141fa69b0144cb57cdcdb2a2b985a2170dca16dd17d7d0f2977dd8368aaed32cf57b37d2018d0a19739a1792cc41acbf668930301d5352578ab5a659f15308f604be1f95026f74f3f4c953470ce93e3e5f8edb49fccf266b2a69717319aab4ef1cc9246d0861c7c6ba663240f2cec1ed603fffca92a9d8c18d804508963c2f80d9bde6db07f92ca3f05c0b4e261d9eb36195433687fd1f9e1e201b7adfbd8043f4a4d5200d413a8a48bc9862fb9af31a90505e5ab52374533e9ce982d34ccf34e3aef5b00ff5f27176e3a60cb72fdf9f62e1b7abe2785a7fda1644c67cfba352503796f10f497e710255bbd6b2cb8f5144ad3d0c9296382ca4c70773356fb2c0331d721300d0fd29fbcba38abe854ee30d68bdb764e30b85fd4c2c9de40d52dfee2bdad8022b85071be84435fe2df907b903db5acd3591bb7c81bdc790170cf3ba8b90350fed279cf95284b8bf128bc2a75f55581331d662cb14513c00e4639831c8e2230ba151b8708d307c93c05a54fe7605b5faaa7510fb92f9db720c35a2ccc17e320acc9e9ec43e60a02696f7f7987b97f102574e66198faa1a971b1f478cc9dd82005e97078b4ae9ecd607c4c2de6bd8b9997cc044de7b1c51150d2cc4f489d911014f467f288adf5c315c53a08b4d337b991693d438793bb98796294c9558a5520c81eb1c44da54175b254911ddd686d79c12cba7411f2f42e0bfd544d5da57bf081833700ba73c10a7ebe919032e23a7e44af7fd25f3b6ba4cf28d56b394eb9b01fbc70eb7a8ea75a6865f78146a6e8030318a63dd4142e005490ceb9a0c6d5a0f9155e3b13fc833d443ead9d9795074086dfb89bd59b417a8d3a72c970037f705ec99e5dbcc4bc7c5c64af3b4d63cf1608ab133871075e297fd6599f8cf3aff048f779fa1db27fe3a4f858467aad7e11f1667608686a85795b75528b65f09660f59edf320a7e30fab0ce849ed23eaf7091d4966bac895044ceabc6bbe4d58930c76e517db245f4dd329c4ea32c4f4f33e10cc15f2cdd898ecfac0ccef192b190dc007c290873a5561d996183218884c529f17bce90801f1848bc7bcd3b14b1303c1f10a8b1503b7103c2e8f4c72d7b4df21c3f8b18fbdfb7d8db2a47bbfbe2407c76469a0333c920a985e7c68c4f2487f4968ed88b74c3824a4f0072b6c6db90d60aac4863f4dd4fc36d05bb7c035aa9d436e0d5a005cc28166f96a3d73916e0dd2554a4dcc66b974668fa508ec7264ada08ceac5bafac59264757b7549bee20ed3b078eb05d40dd54e9c3ef28bf2553ead80bd59a8e97d4c0d799c4a60e91f091e20b258a036d9a986cfea9bc2f03e865df495bf267d6c43fe3e9c05a97576026f4158f9e12b37ae0c16045bc39f614b9a0bdd9cd1da39bec75417940753800e3736af83ed7776e297e66e40f60d1ae6f5574b48a28d2e01253ae37c84a1f306d1d73242b8f154106d2285fa54250d151a4234fc879ac724fb9c11a961e9540a40b2a2b2d482d41ecbeaf30a66b89351f9f678386bf31e6e6ab28c3021674004f736148e907e2429be17d51cb5c984550e883e41bfed6d418fa66b4fb924cc075d215a78cb3d5042551d1f0e1b1208a91ce4c700e75d6f9a2211d66f4b7a2c03de977606aa72f9ec33f93cf0d7c94788f9932978c2b9f62bffa4156163e229092c593ac38411a3234b8053e9ce90656faf7676feebacdf42ad84c52f67444e02b4294d8918e77a1fe5ee77d7aa351a7a573fc8b46dd0ed021d841d5768f0ff0cc6fef64707097767b966da327ce620231ef3d7c88c11e4c68585686645ee340076017bebc3be5444d91cb59ca265771e5e3dad7a98c64cbfde987bdefd3f0d076da2cf6878c4250208319d4b9b87439255272641b6fb2f5a63aed5a1ec41500cbb167a02e9c0ff5f517072d10aaf6d44060edd77a2af096938c864643b7c4101f39eb2adc2350a90443761ba64f01cbf80849b4705bad7916d1480b3d01bcb0ed65c4269d1f9d34167eb77389345f2f49f7f3b2d54e8921c1170fd0514b6d70f3dc0666c6753b1f268a240687341082dd63089a278d979c946b16488495ecf0586a54c0b9b5f85ec28419777f1d938579cd3ce97b17181d71f2ea30fd0e3890cd4c6737b6864c1edc2c00c46c9043a468287ac0d8e125641ae1d90e934ffb20e402cb4b2084c22847c6aefe9b148e1c70926b23b0c912bda7e2532b4965c5403c8bd4faf33e202be3f828f01bec54d1cdee9e8297b29eb8b275b3c6627bc1c0fa6516165d76f7a7e4196e0f0967788f35f67fd06741cf0f2efb42d4aa463e405fbd2dfe2c198e2bda12ad262c937267d511a6b4c871fcd47df7e9422d9a4040a5da8e30e6a45e1e0347e56b02557fe1cbd60a1c346c1335d74c1b188991a06007bcb8d618a473e25221584cc7d4bc00c0faf5affa4c681dbc9442fab1d3afa0937d8739e5c8f228f3845f9862c4d0b1a1bbacbf5ec41ad21276e693ea06d6b0505efc13fb3ac62fad0b3dfd9feef42f13ceac3c8f16080a6d1e085d43fee7b097f3e3eff9e2b36316f50cc736a5719bc375119526a0a8e484d218f7c2120cb0d70209ee86b22d3942f8e4001338369493907e99e3fdb700b2c6a8fdb451269007ea703f446fb28ef00e5ffc654aa6b5fc8090b22e1b0842ffe5f197592c83a028052faf10995419f3c7d726f44268759591fb1e65d7139fd69e40aa7dea8b20be489a21cde4a3548518ece1844b56e934f52c47680d1972e23b3dcd1ec98220976b2ac9d14d2c02da0ab0d1cf99c153411f46315b06e80dd29c107159ef9af024cfe9ec078b6ea84c6754f22af0807fbf004b9847d4852f77767da4a13c0dc0ca9f20e153707347a8e0a29b6ef217eb9c46d19fe7b59e227d8ecd449c8509c0fe39db25af1e0167815f08027514b7ee21d9ff2fad0367c30aeb1b626dc039d099f13d73b5b0950a5138abb7fefc7b569213c8c1067bcdee0ec4eff7e0d26e904279df6ed0e9ae086afccf3ea73b7e631db40dfbfa41671f4bd64aa398a26b507df5d2bfd94d36c5375899d42ba614bd52cba51daf99297766c2357e4829954075c5d7193348111f2e3af3af19030c16f1037ea7dde96005f53cf30fccd717101bacc1c2326fa94fc189e2421b1e730e65986deab07de1d95fe66ec0080291e0277c99b0f7267446f593a07921f1e398d27965be6a68c28ed48a28dfe7e680e07204ea029b13848b9617a6a8205d2b67823014e3b50789be6c09d1549ad5bd80df9182d6d2167ca63d8bd5537d1d859749ab2e46ae0fb5a623aa2ce49e17e8f0b4c62f8c0864709233d0248814b01125ec936312580269f843d64e81c5fa2b40530157b84f3859f8127cd93128246975688cfb81e9a62c67e1faf487141efaa017c73dd6797471d0b52668afab39c8d9842e72d6bedda13ccc7aa4a8b70ca8c0c6bd3f15a87b9e0f61f43291279221b239f7fff89269e99ed6c7c524e43bf3703bbc1ff7c77e9a3797a6eaf4c012f39c7c0419edfbeb0ea12d198715cf92ad60ceda407877898e6e9dd34eb3e53777b704be23a47a319041d99d23bf40bd110037ae4ee84d221d988477f029249bedfb3fc4d5fb036afe547c4d68f4ff95110013ec27485ce1b98b6e98f4c4eba2effe849c0de32e562351a4864a37a3054480de6bf08fd5872d6b06aa23e54bbb9b29c7bcedd709a07af929bf72a46504b9e0e83e9271d5c0bd2478ef4c083fbec56321fe6a4c6dcf92c6675ae340e93dc9304b93e64a70edb2523257c4ce49beb114eb16ba4d1967935f5a1d6de5a27075d0caf6c164b529564c2417d6632c3eaaf9bae07d65480c93f9b5ea612fb79d41c0e7277482e5e7741bcbc9ed6a1372cf51839f9f4358ef51be1d3170c70dbaea70549969544269f14bcbf73eff8d4d660def1bdccd9df0a7d255ff035bbfd22cc0dd91c8af9b8a88688cdc98e7357fdaa3c484a177f44dfc49aa90322a7bf205d0649067d896ef7695cc8b37143ae4be823d55388d1135f39e6b7573f828ea01207e218331d779272d36699e1566af1bed0ad576ec8acf1b40c5c176709307b800e94994a7ecdde98246c1196c0a48eb67f22586c7ca5d23ad5e15cf35f72b9230eb89749f076e4a5f46db1ab21feddf5293c6a171544a93c38c4bb0490ec040f09bc19af213c1f791ec0e098a4eaded8d407dce3454b453d2b0b522574c40e420208afc9b1cf21b78ced1904c04c9df93ab17301c9ffb958fbc64c0840e89ec70b44b02b865c2c5cd9965d57c79b2f01ae8fd541a5c3de0b4ad38b5f6e06d6e90312393dfe632a47f978013767fe418a7bd5c0856b1535977f8cddb63514cba00de03a5bea9b0991e5bb3a37706a6e943e6a342cd09b6f787d693a4335d1e0aa046461e282e60549e1ec3252f018dea4322be995ce725455b5ee5d9b35f6fb4f09bdd8919349169ff069daaa5672db73b85c626a59de0cb39d1017c969f54087054d93dc437e57744441f5887a0585a3dfc34d424ffa6cddb45421f73ea95f0c0462038e45ff5030177683eaedb6d5f31743e07d15c354e5abb74c0774dde99903344eb8db09af07c474151f220272b7fd1d14237595a20c7d74fcdbc89ff75102d26c25b457ba767e5bd1ea4fac78b916a64e216822af7c5681eb621dbf830309d5197fc3a9fda6823aba90799181a11739204e35df65ec542961caaa983b110022fc86debb66f35dd2ee2cd44ec51353b382c56914373ca78330007e9de1350c8d6c2be176d258f3da949cefdf2238092a8cc922ac7fb61ea0d076592efcec0a14e461bd96140a5bdd0ce0d61f8096d31f50a6e56e314eca938425905d1df50bddc8db3aae65e3b4bfbb62a0dfac856f596e3a19ba3cdda272702853c03e8a088a687af20411b71bd97f647a64df87eba6c969027daa910f60ea88bbc6ed250bf3df8f4769c6df92d5ff3fe87584eaccf5e968d71f38afeb3b17570caf02b40c89fb133d8e2f694a23567e1ac2acf2688bc1aab0979d972062e4edf29a77da0d3d67fd339dea6a4e2760e702d6dd9f118d9b865009b4c275625cf8cbb3c1b304f68770f7f65b5ab4360bd12b10e63d38dd6e2acdb48d598f6c487ec8772ad80d3c1aaa5d16529c85af55ee17362c942899325bda378de2a1fb43a36d54a622044d8e063cd0bdbcd331146064d6b9ce21b5ab2df81d4edc7f37253ac34fd596052c61071316500fe5ea29e7edc1d0e1f163bc98589042c8dca49e91c086ead00baafb8755c9aa88739b7eee8336be965326073e6142a6845cf0362cd0f7bea402e3da8bbead4ce0aee60f995a84344279adb415401b70131693772bc7380eff0b95539f60bc92066f60d2a05cad4ab4b49e76c7b715a1f3fabc24e6230e44ae0847e9c613bbbc78e24ff8d0798fc8bbcebc1a07eeb35672202c3490b300c39108576fef9c614b2c9ecefe6ea2193c5540fe6289c9e650347f933dc7db1f60c8069339fec6e015973bbff1a4b056280ee794035388e81353b09d0c984ab4ea3c09f5c32e9a39a42a9273bb3216b93099f20db6bba5a1f44f5e6cc97b82d7cb0c0579cfae162e8c501e9589cd6e0a9f336ba1446a61ffbe0754a2d799f4dd443c052ddf88b6cfa4d8a315e791442177376ed96e06d67bf8cc0c3556034b5893d30df98926e0dea9f4034a7f97752392dcd2fdad3b6cf7646e370289c53849fb26075d71ec314405cd0e34feb88a3ccbf670bd0d61c5a3e4859a79ea3f24ab405d0140eb4c805b1bf3ae4291e56eb4e12f5886c384c2c0666510d308d3c481f66e07d5662735ed0983b5a708e7406bc4fa3ef7cf3e09c9d5ba59309c42457571cc07c8bf2dbde7af6ef827e3cb9482906ceab6306bd7fed5491dafc1bce202a9d1022029416f0e410ba91c76adbf18d321ecd95f64baf1c2ef099edd2b9fb87e4a0cc934b1ecf50ae9e2e2ba7433a9591c07443d38066ab756ccbe5b00ceb12647006ad25d5ff41205219fbce2f22871219a5c2a9ac3c6a90708f58efbf53b03fd0f5383853d24698f0f9e904105717c1956c4de0d54087f0af8119db3a990ab390467625232af073c35ee17a7f6398b8a462dfdb83be7e7ed86fc60b834bfa51e0768e52671612c3f94e1fcd58392532edfb6d9819b54cec98398f9fd27ac2fd004b3e770afda511a3ee8bc0ba8af1acbdfa14046f0e98b59a26b9e874f8a35870d56b0c1b32551b74ebd5c5fac33ae19442c47bce515d2983cb8e6855d18c62907de0abe6bceb5aedc0152ba80ee06f58f2204f71fd30984070a5f3ac0260d2602f1a84cdb83bb3059aab236048b68bce97b89aa47ee9cd5d61657b82cf2a81e072732fd7716b9b4d4c57103697eba92a4a79d986c92d7ac56891f663b8a3f7101f169c9f14c40aa1d57e1a9b358befe65767ac3732fe956e0b06b7eadf618c20929157c6105c9f805c11995e792e617a368961d95ea41eac1aa274b670272d60e8d43a902666cb869795052bc58f6f13d0ab510791a9c7e73c8fcac3f4e56830af9b423a6517384e038348eeb2c6017a763f48619a44e7224615e73ba32cb3e08ad961c51aaec61ad7d9a4059cb8ebdf014a58ff64f3d65394dd4a08bc9e5fd04bd527ccc313319122604186eb25dbbe364fb511eab3c2992ba8223cc19ee4c0297ae02b6631c642ecb798584ea280b3db079f09384105b4bdb895ab44941680a87262172b1185ace47a267c2c3efdb9fad87cb3800008ac289dd806abff0b5070a246196ee6be8219218e57b0bda8fcc192392a8eb1cfce3db86706109f9d10e97c6222eeddedcf00564eeb46b7bb229126a3e3c6ee85c3e903d6430a779d204a1c720cc31251388deda60007dec3cc3e3c86e54f3731e1ad0741403d726af0220d8fd337c74bddbdfbbde87ccb4e64af42885ca644b29fe6e21cbae73d54403e1c006e827708cc83aa429a8fa3bc2dea052bef647d270ff31ca8ac1cff1db0d592fa77420535357d298d1a4ea4ecb0394f43e114866a483c70aa4c2ec6320071312232d6fccb53851fd1a50ad04ced1b3582553296cd68ca65a8f4a1bb1b80a7d3e528dfb33441022f6bb3ea76b2283e47b984f1da48dbf8303fe09472e7e02c7f33246533fc9bd2355a22ce2fcb2adaa0342bf436553888708c4d8e12a720e0bf67287bb967f2f72590f56e6129057531d8e0edfe32df810d13cef9e64d1030060e31970a97eecbce3b7a8e7025d635cd7f05cd974a6b55fa1a68ef9ca7702057efb8358568257a02be15bfda2a35265c80396fb5355ba0bce6da00f215a0d248d66918b648e70f8af7e5e46421d58cb1820ab921c1a19ee4a837ffaec2f0bd2988994d9e0218642ee99bdb1cd29dd14a215c6a4a58c7cdaa2c48b8bbd7f05c142259980a437442f856a1095717a8a301420465c662ff899723d6c970cac0f54bbea322ac7cfd614f9a4c63f190b5328b2588f8bfeb091a44d5e49f60326088a0e097f3a5402c4224355d3a60a397cc27935cbae0fea065e2672953156660e1f63ccee81065ecf1b2fb733b6487a178875c30302dc2ce64758bcdd68b97d06857ee791a78f87396bc8bfba1329e44d6ff6c7fd963bb4f89f83164bd6fbd50fb73168cd495a54849311479e904e4223e7a92a65ff5e5a08694450cc4701bb047d775a7a45b72e84e4c209fca24cfda3a1cdb265022b1ab4cb8e6d7a0fd3350326007e74131c43f3cb668750218dce2c7f714c21a12ea1cefc20ac57ff08ad0bbccaab7222efcb42c65a12754ca6319c6f1a31e398da61004c38ed15cf704c0da75463a32f5eee2b89772dcae5b924053758d73aa289388333eb61bf793bed0e0bd453e13f13b5b96a9be5ba21a09d883d3d19c3a4b1b45776ac5f3168a1b80cffd699aedfd2ca92c2920c9b5ec7bcf90afc111cbc80f9e7e0fabcb5ac978a0b59216fdf13f9804fdbe2b399b8164d848898ff51a5e354734061a318fc30550a9e949b8bc385628dae3c9748b9f02eaa0a2f91f077b349c1cb26329b4469b90b1b9db4dc96fc8ef6de06a3f89d2b2a0159b5d38808bac1702706bbb7b1ea4d08f1eea768ffa76c400346083d702f5fa5f2543e9512c15130e2fe1252b5a3270da85a7ac1a09b2ace45a91a090ff3b900accaf64b9368f695f1034fbf4ca2da0add9ff7d7665c2f8b225baf9d8388af3a739f48d5cadf5bc0563230421da2810315f3a974ee84ca66d5f133a98e3a6bde708adefd21eeb085b6f401cf8ee5190ffb7b393b068bf2b5d63a2dca84ec8658b4d1b52cd661d6a2aceabfc10665ec011f71ffedf1623f8061904e8a150094a6b65741bd41a1f8a74606d199fb70fe0cac45ef401cc10aa78b42eb10b568392630bd617b7687d984b6f6c14df0b86a0116eaffed58e3c334de638bad21553db86f3c98c2dbf99fb1193aa714e429210110a26e29cd15eb6bbf391f3bd5abf0cc18eaee664571011fa79292fdb0d63f0242a6438d2e62dda6c6a92f709095e8ab1d43841467b9d2830db3a73727514907a5cd2bdd9687a81c3b37e7285b66135ba51653c2b73db8927553cf5f924941004c4d5daa9b19eb66d090ecd2c153345af50ebc2c9f95a85afd2cb4a8bb5b9d055657a7d9f8cb800d790ef0e404f43f7d7bd4272b9c906e9731a9cdcd1de051081aa651d44aba92a78aeb95a6e4d37e7ae80a4248145bd6c37c02617b79026f07440239f58695dbb1c0ce0ceef8a705793601363517e3e9dd196248bd96e5e00754e7139d993c4c8346bb71b2083d1b3b65def068bbd69c32e539e7f4c592a2031698f9cb78ac753fb6298a204a28df74dbb0576e5355be307963b3a0c386090e117230af0c442dbeb45cd084fc9b2ae8da79dacebddfdd36d9747d47377c3f05381a17243019f7b46f799407092aa96ec021f6879a7e66d97c34e55204207c08f4c9ae5fa68c230943e7028c04d176dbbbe6cca1758296f84e0401b4bfe1130886be23c117abae4cd5c2998a85863a5e8fe3fb39061f86ee18db6c688488d80b45a5b2c7529196f859ea1ebef627fd8350956cc53f9666e7c58637fbd402930018bed276950ad9ecd64e4c8beb4e045bec1a0f41a6aaad3608666eb3bc5215069b7a4729be14144788601d4d87d9030c799ab961c9f432247b5f090c0381c705325f0eb5b27e36c1ecec49cfcd1086d7de471b1c416fe7367b81a62b6c24b4036d9833835e96b552d74fd2e53daadf17c2d823e0292f4b1462e73c789e6b050bcfe16f8964178a9f424e393c1a9d072d798460289bc6596f3491671a5705ea09ba03246b4ed5c19520f2e523087ece792d7a8627c2e68c9a4620370cdd1d05056af1bd6a1ac4a55ee8f397eadf004acb538407375075216e09c61254f48e7f0b766f357cf08e97822109712623a27d406bf97b8c7f84b832a5c79c69c48b29024eeea39f7d2764d463bd8edea0b0e7d8d5dbb2e20f0f19210cdebc7c4b6e820cb74a95b13d393d035b1c1926281e0fa88911d4b6d6ffebfa6e383d8295a3c203fe006a68b3d5dc498705ab1cb112b160ab0668026c28890c23ced30c43612c0d637a52ba784cc1e7d6cda95d48c39ce6d86e0cee1394fcfd4e61439abb3cad0d6b6c80fcf7122f06ceb8e387a4d63160ff394c3ceff157b01773bc895fb7d707445077870eb22b20ecee55875078e5f52314b8d309f8aa9632e71cf6c8e2c00dc5d8189b7871cf57abc94d943ba62cbf28b9db97e8ab0a1248e248bdc72d8309d57adfc290f92e858ea5a6ac9375fc836c345d7a4a6023c6020e154d7ece760bf17cddb02f3de51d9897cbfd77d7e93df8a4a794e361e5e62f880e70cb7a000ac8f76de521ee037814cb0537499db66462cfe02fe803dbfb59db0097b5e416070d515ca3f666ca9f554663b2ce12edb9a0b1cb562dd904519956128a17d8fb0c1b42e4b7796e4bbef863071ba12466c5b27d3ed0fc4234635b9d254d3b11460f06c93557ca31883bf351136f4fe02f2eed393ac936162bc26d8393aea1ab9e09a9fd71be0b9527c2c816d2fe8e7bf17042804aa39c84c62fc1e064c8740e1d0160e7da1ce595e2e6099b7cebc8548e26222c76ec117dd6139748b78aaa592001522aedb578e0a698ac69c740acc94c17349411985fffbc31dbeb6b9cc360bb0c1340aa97a5eaf5ab90720dfef92a2dc161cd4f13269565920c4888e6d40b180df7bbf36aceec2f395dffdeb7171f5a476ea31f3f536f3fa5065c5ad8fe1933084aaa60e5f17ca7c12433d54bbf9ed8a9311c0bffc6d5f3344f3c71ec6b9221023b3bc87fd1484f969b5d1d3eaaaffa11f4c7ea8e88d3c80b79dbcc8227c8e506293172104553e3b91128eddad43c4ce0b71e1c7789e86c204bcd989098f253068168df99d29b527c5eb7bfc0abdb0a5bbb93453f0e4b8e9aba50449ac7b6bf0d8fa8531e1592ebf78b1796afc53596fef3ea9b8a80208e69ada0420a9a947900a7f199afe66cdad4097a633d7aa706331961ec42e346de1de43019d5b4335e0fc3bb77133e4ad9ba87b29f79ab2b06a2761c2a77d768ec92d81c9f349edc4800b20f570793bcf03f39adc3546d7d6ecc20760bfa3d2f87f8d47515ae631c660cafec8ca2f8ecd392fb78f9d5bdb5de745e5de27d7feb1b01ae83bf9ac8ddb10293bcf776df833cbd5285c5cc929dd61fe601941831e1238e77353be38838210d0a3b91f958f61d7a3ed73ed9bf3fb90cdbb21345e806b81942906f09ed39a0082e7a3245a84f66f2a73afa08cffbf4ac6c3979b5c170f18f15a274f68452b70c012ffe2e133e7bd1d67b77510a4aeaf5888023765d2d431970f942991e69ba0ebd2665e1202472d3c429e6e27ed1eb514d128874ed29b1692ce71e70ddaccc017c5debba9fbcf6bbb2af2ead58dc27f4c0e5d3079df2d184ea0fda3e1478e800c7dc991909905443976c1be996a791fe0bf1b4ede9565e3ba5d88e1c82f13f02bc223352458daf31680d825eb61ea86cd321303f1d746a93818d765f6b621c00ca3eab5d5cbabff6faeccad2870bf44d085ff17489f2c7b67e0fe66386fcfc089a40c31b7b8e5ff17a07785529e48af0f6f2f6a6aface3609d9cce6aa335780d695a7edc8e11dc634dd51aac1f723fe00b99ff5bd86d4ddfe8c3512b5aa1260865294f71238bbc172e18d2716c05513901fd169a0b5d8c7f737670d8b5416c0487453eb66681d32ec48f6cf3839de0e9b30e12e834c55fc0955a6801862255048e5e858bb90d5f93a8bdb305ca978b7346b15c6e4a589d141f350c20f50ef60b8b6899041d4523ba8f4393c1a142724e4138121974418afdd62da98673830604c1cc06875109ead27f1b38d9aec4c211361e8fab8a3bd8c8d6fcd10af882df04f4fea10bacbe184e8749fb6d6875d484c12023619389dd17cf7a9280a669d20f7f15f9badc4bf7f934242683c1a920caa9dcb7471a6de7ce306a466196280601fe066fdffc96de9d446ed6dad9d846e413695a96a2e551e777190a2f6ef56e023ffab17d398627b794948bb3cd483213656a786c989884355eed526df1418607e1a92cfb168f87fe2c16db9adc095fc78b4c9fe3201f430a703bb2bd164c6d096e5e439f5718274d79f87892dea7f53fde28a865762208266eddf3799745db0444ebbd3bc42ba3f9715b1c6a45c89b8916042cc8da4ea373d5dcacfea0fed1059955c95a4d87572908fe1a7c1e21e428f9320d3b4caba66a9d19d75ab64dc7029d9eb7ac8408eb456bbd35fe1161230e8b9793bc220d63b9219ae13a24413a06a353e38d749b7a02dfe7591ef101a6db00a1a18bcce9e12c2db0687b7a5a970ee2d87a8fb1c035efce5721616ccba52b1b0b08ed8b96ee6782ff0430d4ebf90e83a56d7a18e7f69d3722b152faecd48b0bb0f9789535575ad4d4f54c49d6cd0d8600812c3f518e51fe38b71220285dc88f7e4a1fb6b297198c465a901242750178e565d34b5785048b226069d9b2b727f56ec3538d1d2391561ac6603714760ec0a554b6c6ecd858093c656d9a39b3a60dac7d213c3bcdeda1460afffd8e640d69a3c15c2449070d5d09a2289c5a883075775128a04a69baa3057bad14ad7a073b0fb14197f706eb464b8e82a042d07dcc2155f3b467465912f8c3174bcdb3068d678821597baea752392cd2f0d8ca8bcf6c9e5d2caf8bd2cc4f979eebdeeb0b4d1946a1b9c9e37f7417c61969cc65f1624574a8972e210bf3ebb4d408fa390069139428af72007e145bc24c5e0f7ea0a37a764cbf10467aaaa4593a7dd89c0edbd91cc8f80af8f96be8885413731e745555d4d0e88f9ba4462be87d2e94c3068124b6024a753add50015700fe481aca7ccbf578ad188820c2825a0f9073010b76571fd3739cf8c9d22d9fb3fb22389cf67f3879ed02cd8ed2a4018da5fdb5063b36d1d02fe7dfa7df935bde4799dda213f1f7922397aabb4f98381da6ddd70f02b277d303dcb42da594410afe17585fbdcbbd793463fdf1e83e85814177570805fee1c794b85b650344c75e2d96dabb085df83f67f43fc5bd8bae80c1325d0db809af6ccf8e9ac30992a7fb0fcfa4c5b3d0e46caff6caabcabe9ad0876dbf0921a0a647c406aa30171cf3db398d39875ec7591037f0f7023b0f550a02548b090cc69130d5c717d2190639f68493c76692c6ac4444e32c7e3d11e52e1df1860a26b275751b3688fd25bc57024095b19b561fac23a02104e6839d87836e15890c18e2497970ed1baebcc31cf9d9678e2b6302c379c159d6261c527a2efa136d0fc2caa75d57a5cbc6e1d556e16228c56253e5faf5c345dff9d733e93beb4d0b0d87901a039002844cba7cfa6edb7c7eafce5b1e31edcdb9fc15e766c1c0dc86030c269e985b9cbc4f14b58787ca0d1884fab38d216b7169ce0c46bc92ff8ef1023d24074b27070b9da64437ab8cb734e5eb258f9d66ae565fbdc9e1e11e20820f8569e5d364ebd0fc6822c9605434ea75ad4d08b9483bc307065365a80323190294592ad089f7ecf38d1b75868861f020b2d4f59d74e8ec4a98812fe9736ad30ad41cd3332858bb84d1e981690c9358311ec6aa2fa65e7825293d0fff87b1c90c6ef28ceba11b3c205462272146e9b0550f4925457c83cc73f10c7e7f4d1af10c70c1691c1f89fb3b538ba42596903ad71e19dfdd0bef51ebe88c44d4aece39076705070ebe886cee3038f2fd573c202508574914c090cc85cefc57999e2e1802442c2262650cccec99036a7ad71df7316ae084edf52297667b99a9d29f1bc608c0e9528e9ec68fe28a31fc1feeba16693e2e0ea1bb06cf9b983a4aa346bc7c0c80278e29fa9c4e9073eeacd3d7c98a71101e5b18529e0329bf8c8030dfb37e0f1a7ab73eb95afa118987936bee26320d24099593b572cafecc9224a243c7ed0ca53271dad1a9c5220bd7e3e16404dd343379db10bd8617568b9cb96c5a748d07150f26090a792819b616890f73138eab2c558dbbafcc44c7910410ceef4249046740a554f3f361c957936b652f5b52cf9144a3df835c2877d5ff98a3af84020514897cea22fcd39b5b89932fe025b9753624d407bef8a0aee64e940fcee95c0b8ca1d2387349150ef2c994626853023e7da2046938fe69aab2a2cb14de98040fe144fc511ea90d8bff4eab01ed06d33fe428f0f6221f45b878b26b8a0376b20a50b9ccf2a8104a8fe62f3df5a69fc76002eeff565f85b65891cd792930286f07aa48e110e0ec1c843c0c390d10812cc0369ab3f5c36c3884ebaaafaf733b55037ddec280bb058cd49e1d9dac2496087817dd467d420914afdfc09b2b563d460b6b3977bdf691dd849099cea6f1b9360f0c05a932566a18d7294ad5eed534210917d215d2034cd949987eff97ecbf8c45d01b89c8c2f8cb60e23fba06dfe281078df96a76b1b3f1288c7b7fb19956d48ed8c8a45d737b3f0f46d19d29a3bf51050e0815c3bec51b5768080a6f3f405cc4a2309f0b7cc366296f4b6c2c214b2802130b3c839a71dc0ce94bee3a637352427b90fe6907699dc6171d51ef3bf6d20eae5fd379af9dc0d7d92c3874fcdeab72278b79e02be65bc46892fd18d68ef8093ea61a0a6b984371a913b3d076c277107a458e7f9e8f1433e03c95e283911d00e920b7c7d0c394c2562e211bbd89437a549dda5f24c76d169a453efe5b6563012aaf9e54a56bca4943d2be60bb806ae945ca8f41dcf51e2de44b5d931fc7140410f556c5849661284784cd827c8150913ecc317dc5c427b995110b4361e03f04c7957ff8bb3d26be5d0e3563630e24d9d2a67ada5a35d03acaf89dbb6409df0e209a6690904937aa63ccce745ec8a5d00470ab92af69681b20d1c01f38d3bd0a82a1c2d172d1380d75b102d694195a50634cd6f17275e73847ed98b807667d0611cf19f2765ab9553ec008b9aee6c2c199919d128fdff4aefe0a58c89a584206851505efc90b7385364964794a69fe613b158d47806ede5b6b21439e36b6e009c28286b378c392692d3f0623dd8d601a7e4b47f7019ee506f1836d4db680430c2ef74f3c5c526603144f881fdc30c101ef169030f66a0ddf4ad80487dd2efa0305cc409be4efef6e5ca6b5a69bacf92be245f2b009527ab07d1b9e5f17a2f403531a644bddfdc887086159161eecfb681cba168d40064255463c22b2c543e009230f348b000394dd5e947cddacc92cd48a48d0ad1445c62007ae2863f2d2e608b7a65913758db2a460dacfda59b536924e048995975a7a1772226dcf92c8850b850c988079b7eaed2bd02fb2718f2ec4692c5bd06ed1d15828c66de272005400ceeb56e0bac61e357caf228f6ca25aa9467f9de1b80385ba4eef7636766c7e0a5e4ee1f2c213bde5e064b69839ccd920020285ebfe8525edb03145546eb6a70486a58b324c4d04be8c5cbbd65957b0f59b0bd46b6be136ff9b326a48ee32f70043e4786d1ce8fbe45cd58c75bd1311267967f522d290b51b4975c953f8738001a60d9dcb10f79bc89ee14c86db1782dad2afb6fdecd005f7191e2a67af565e03ba3db1653af23de6f52fc111841514ea3674f8b0047c0cfd42c99863b44ebb0ffd6dbd91277a360812edde152f79dcb83730ef554d43aacb7dae10227a225603028e3175b20560cc2a8aa4ed6103ec908a930ba1a2e712d335f31578628df4075a06d34a279baf1b6bc1cb5ff803ac217dcfa962f3d8a138a687df561e8b2e0ce0f02baf5b1145a2a6583deabcd8e65e8ea8c796de579964389306ba6bce8607e4ee8d11a055c69cfcdcdf6804042ab7f43ee7f9cef3a1fd730f27fa49a7fa05f9c0b7f104de296c8026440ad05b99e65c28a8d1862456e80189746cfaeb6202160d8c6cc2d721893ca7ef402f56e97b77255d40990374de0ef62f6d1df8260669d119b1434c3a11f6172722376b526412e045436b6797b9c7a130d496bcd10de8a5f2658c1065b802d01ef7ea1d802a9920edfb6abb060fba469cc453b4df07e1c8a7aac9c2a282d376cb6e21bebc8d4e20603166c877bd09cda0c24db99e08332e76ec33f324ff765f756baa51b2daf4c06f5a6351f6c5ab40aef806ed0e05ddd5aac190504654020092bf65360fe692abbb2390404063045626ac86e9de04 -generate_ring_signature 67011525aec41e62812368de7e56ad8ed15cd4845e00871483703bf9dcba31ab 819d1090586ddd1ec978a2f24412bab7d583208042f35c68d6ad5ec98752bba7 76 dd964f5695c3a008543c185f0a1b5fa461e9c22fea98d1af2a1e5d60cdaf6e01 d1b04957e8b634589c062eb1b743fa1cffb725a8e272703bc10f50d1f5a0642a 51dbe51747f44800638559a63971289e43845c9286aded8b96d55d982fa22637 81e0b148f3ce2bfb07971eb4acf845ea0b1cf1c08b27ace8b0714a78daab6097 9ba5b03511558e8edd7b3c0336d9086011c5adc81aaa03664841d8c62f1fd2f1 3ba3b659ae309970f3494eb0ec612a5cf70da1357bb5beecb2fe946a9913f6f1 882ea42f44c98b4e0d85af542d9de44ed889c3d65f9ed6d24c9eb751b640c12e ce38c8d666e28094548fdd3dd3a9e9706e0ab2c863d5e1c43fdbc0682bbc00e0 b0998d216a9ea0224668c114c8db2efa0a565838075f96126bfcaa22085b4745 c83836b02f86df9d9c8f753f52670cb80ec5b6b2720cb2b7fd7fbbfa2d838e5c a441967d23071f5a95a1f82f5e2ccecc2dd3d31bd1849d137c37e9415c01595f 0322b7bd2b2430e428c8f1273fcc36f45f97892b5572bc53163675abcd987753 b9e1f6ea22472fa3db4957c448172b1d58aa61f6048aaefb75d3c647743c87fb 348fea709d69b0752a6f382577e2149d83b39c5aeca6f0474d13b1d4627da825 50360c7ac3a214e93473db297a3e1211f08704061bc868bdf06976a95db2e6fd 8a5ed26f5ceb633b0e25b2a4410178c89ab38634fc36175aac9847fe666d6876 9b2690089def921d1d5cbf63ddd3f58b5d7fecb4ebb54b9b900032ef0d687084 4a22af02b3ee18ca29add712fb0890f5fe0bcd52848f1f8e38df6f38dd5c99b9 46b943bcc3d9183b473a4d522e4a1febbd713e23b7ec8959e5a4fccbaa9a4e58 ce8f7b05f9e53ca34b01d6a5bd63ee14991d7ba4e42a236a7248855832023dcd 851d02e432b19150a6f451c82eb384d730b0c3ce3c35a4505765785fc307ff36 7ede31e63912f8434bb525222bdc771cc1539e42cccf8953ed50b7bbd152f87a 989a4e49e2169c52b2e8ed72378944c7f4ca80cdfeede413096d6bc6bb481987 8d197160b9241e31906172e6e59aca990939999d13f247f5a5d63feb35218761 cf685e95fa014fbd3a7d23c9345b54c8918a82cf04519dcce9aba2064e703bf8 7b7bf2a667d37550e4e55a37e728ccc78cdb856082abaf80f30bf1b9c75805b7 b63b938e1ae69254a31620ed6db8a682a73755ed347b020e245209f4e5aa344c 8e1bc9db8e3ad41682859991867e2096c5e39067bc4157bdc5564c3391902df7 998290f1542e57fb84928d36aa67ceff21910f22bc8c3558a0ef10398fdfd3a0 f4aeaba0a6d239ea8a01d9cc7daf6ce0109d26aa92cb30f4456bc5eb2f3d6738 17833eee37eb03a9ae32973d56754808205ef421688f18fdce0caf0bc66e4f27 30b5341b038b23ae69a365a896790cd64aa19eb31864bb1638c08285909d764f de2d29581951dfc59b9c1d2a2a4ddf67919dbabf8a58651b2fa527c414a5cddd 0457828b3514870bd16433cba46f8738c173d41eb59ded65acc6c7dbaff5dff8 5c9fbb3ee625a4c846821074213e505f9c51d5358abaf3148a8d154cacc6d6f1 6c4485b140aaad6b9e994c7d2e5023d0940d85e19af437fd21f236ec3a4733af 88e354a64180b6869ca2192aff2b69fa306c43be47a4f110b85dcc5495f1c977 6a6a1de9b1c8cf8b9226fc675ddaf1a032205f897d0727829da84b8b89b22d51 78d1502e1cd1fe7c525ad2bb4f7c9f742d679f7908e1a2d4f7bde67ce95b8ecd b6ec7b175273d7aa77355401a3c36d43d003f6efbdc0a961564bad698e8264b0 b4cd90e8ea0018b56c069551b6c43f45d5e330633ef17161b85acd7418294d58 25281c2d7629d25ec3b0bfe45d621b5429c1588f2831611c642cc7dabcded2c6 6dfc0172238b46bc6b48fad9ffa9ca9e8dffd0667ef95611bdeaf212ecd6457f fd8836213387691d192f743f779da56e093a435d355426422afed7cd71d18cd3 d32a9bfe7024fd29b185a1308ac3fe06e21b181ac51ab2de211270a503ee93b5 d65eb25830cb331b5bf837c591ea4e4a350fe3cc01e795e0bed07d38b4f84991 c521b61b43eb63632c484c6955c669f84095d769f226524f2b324d4243398cef 3c282a717091456d676299112eee95a854a7a6a74e890c295071033c4086760e fdfa169265150e45aa61bb1450d7a1ff82a7f9f076bfd1ea5fcb3fc35b527299 139659524a57f89d2425bc842cb106d8c84c9dba73fcc03d61ef2b7e6ce2e002 dd28d9609d1d3e4f53c48cb86fb6af584275dc00ece5322836b89209bbbef2bb 139e90fd22032ea389767357866c7b2ee9a19d9637d5911e74556e89687e79ca f09a6690d27cd15743f95203885b89545c0d31d6f745a0c1830e9c2c613b94f4 b39a46aee128baa794634dfff8157c6d3971de45ea87b210a568b21d4cc2cd39 8f63b5d7703ce9dda77b5fe3a6569482d1a42a2a7e7aaa46e3f50a1bdd217e12 14f4e2881ee2111945d653a6c5ca978361671932c50890bb355f277be25d2a77 e29737b0d67fd4b843c907975e594978ece72d400eedeb0133e875cd65710c3b ea3cb93a2202ce93bce08902a70149ed46a56d01d646ba50c39f8fc122e19926 e60ded286f6ce5f73348f53cd99e4a3de0c5e60297be0d7f066fe19c08c13c16 77754e38b821d49702150ebf7f43fafb0889c05cdab297341578cfa18128133c 434012a532762ae1ebd6706d4740128e4f1c4b97488de8844115de50d7b9149f ce7d22e2a914b82c673b65b28172af3760540a84a45b62bde70bfd5833c33509 7af0361c2aed7982fef7782873dabd2d0ae6adc240dbaacbff15409d916a6a01 d3bebf533d2922ea2539d196acc3e160a8c776bd0305992eab11cee72b51576e 68bc2a53fe9f28b0fcac97ff57c0dd6469b3387741aab8bbe8779b72264f10bc 97741abb965b119d339269b289ad2b630e8152a2e8ab33bc9da9fd155a2453f2 a549ea88e7610bf3c7d92fb93bb175146bca920a8a566a6b8bbaca0c73fd31b3 fae4b40e6dfda8cd353c561aec3c9c8da0954d888b9c6adf883621b50c3c99b7 55e367e48d3ee954646ac39548bd79e9e56d216b1ffd07d7cefd5b99bf08ffa3 b5611452300c2159de3e38795fd01a3fc57dfaa516ade34e288e984ef3a0d604 3ac22c7a0b7ffc3611cbbb883cbb1fb6b4fecfbddf4d43c2c4fb484d4b103868 94d7a77a15bf45726606d1a5f4ebdb46609b7cf029cba1e51a629e1083f3d76d e7c85510fac98d7cdf17ff2c1103f65411fd196c37571a4d2002bedb00258cc3 023a807dfef8c1e036c0a55e6b1b09ba9aa0637caafee1fd1b283f48dcc7d88d 72638bdbc1acec76e018aa9671b04f82e1e0711f708f6424bd77f9c048060ab7 f19bab5af9575092387a5070f63e2c8bb72c14cc9f1ffd5944c2c24d82e880c5 b5a3a51d296fd23efced3457f883efcbdce7b2e7ac1514e5c51211be8c328604 24 56d36b8824c9b09f25c91342c082543c068a14ac8c7814acd8662adb89b7df00f0fbe742e10869c44855f3d943c8e4e7fe7f0a7d12ed898606520f7a00daf90b5f44ae0dbc6a38425dd8a5e14a07b09f282ae4a26913eb1329aef71262ec0202c6ba103aa37e4daf2e5875ce63c34a4b52c263117c79ea551bd4e6de877aca0a5db8386cc50954ecbe48581312cf0b59bdfb53cee0745ebf613c7a52552a5504bc4aad1194f4302458a1870da63d8af69d24e208856772bfdb367b6f23c80a08f9d1848a26e669e58ed6ed6ecbf2ad25e17af94b4dada0f95c46e5f37eb6bd0f1baffd1de518e5a7b58e569defbffe76a863c620f227483cf0ed2f8a99834b0075894353ac74da43a5a45e6f10b2ec06ee6133fd3f3bf3a9ac9b7fc29d1cb50009d5038ed033c069382f9b5109554e2cf85f7cb1e68d139f04c41ad0394d3a0de1cfd224dffbb7d0a75bd3f26d521402b75ffd3c3cf5cadfc9e6304809600606c53e31600a60d90bd8829166457bc493a585b17938163ddb21b62e89e5294d034e82eb1c4424a0545bc5d205e3554ad5a14a57fc6a80ef7acdcc66be4f53660c57baa5c5c8f47ceb551b8a217a88da4af94155a40c022e0e86e84adab1976b08036680342c682e89d6df4686f84f885fb433df387a8ce087b01bac9aeb24480328ac48a6a06ad29d7ba6d2bc89ed943e8d7ac2fdf9ca7b3ec60bea50d00a190dba8198d8266f057c1010ffdd6d6a28122fec807b990ccc209c286699d9501006d691cfdb3db5af4be9bb0638fe9f80ae8487786c25aa8d51ab5837802aefb107207462b6797ddfb969d40463f3d9ec66986af0bddcca7f8985c2f707a04cb404b8edcae84eb9a2dc8a137bf6f86828bddff24e503110b506b065ef477edfea004ca9707be13702886e5809150b463a1c44d36bb7de440ce51c3fdb1a0cdd0e0698074481f7d608f44e2dc2f7f5d2dab967e17f73e39af5620f2960717e8f8a061653ffa3ade70de75a8ab8cdd7179cf5c19f1ce01f37782aa33f583cce37090fd966ae0474fef3bf353acc9cc1054019e0a4dfef017e31999b6307537b939b02b650c25470ce6f289ae5ad7207bff952b9ddd06c04f5e14d13369257584d4804aacaf5fcccfaa25cf317919f9e97a0c7009c161d508e7668e5ddaa4f0bf8c50f5156caabac38b4a0d2730d0a9777a77029b9e2d89949403e9480c46749b715040a73ed23e0b1497f4cd461c410e2b29a3c0f4cf06c302176548531566042af0834d0cfad5bea8f64f0be8f8788fe6dde9b99042586a2bee321f754a37e8b2c0c384215ba15d685380d1f5352f5ac074f50f026b17a52e22a4f95502d97b9510f8b69e944d11bf8f9280d9c059487359757c51dede20f9020347a99bb35540c0fb66949662142bf64e62624094dda39b3bcc3ad0b1955de28dd3cb10754316206af641099060f30688e5cef2ba0dd8e977cc6197fb3a251fe7b4551cac497fd002624d5ca8778ed45c25d62c9a908f96700b6484a16bd707ebc42f9a28d9b9702981f8d738b052dfc8a414c76b0fec67c0843a07a5c9d9433e183b7b805923c064c67bf1298e33f0adbb84b32470b7a9fcfba58289226ce2e1d08520afefa450f2e4a9d40e2ef02eeeb363425c276ec4ebcab4c248d8f1ac039553eab951375033c13381a4eb50abc4e3d407729421fb6b8b5ab9486a10c9f4acda62ff055860d1f5eb6916498a09c1ac9eb319d69be5f1427f5add38e6697c0edf48604d1ff09c595d267bde5d75aa0796e550be62c5204e32c3d86e5b049286de00364aa6b01ee31f6eb101585353d0fba4933a796a9515ec51d162eada60713025735238403a021cc81835496f337eb1252ed8a28e2c2db0d7432e20413f6886ba0be5e3a034befa87d1ed53b95075bb865360d95189bf6a041c1ec6d31ae1fa1f1fe8eba070180532a55abc38f61ee82438c31909cc0b04f8ea12353bf57024af27cf2030c15b22666c8d86fe21d612ceeddda5feea2f24e6b8487d55fc7a48998ffd3740f352dfcc3174534c2043612dda174cb4108dd2c89ca880e6dcb82714258024504bd1b021e4d930d1fe4cece2a5c5312fb4a3414fed7762a7a29a4172f9ef0620e705784a00d72a4cd3d00aba62e080dd8776b02b7ad668bfe72e53915f6c3bb07d28c79cb39f63e0c05010839c0b26d8d1820445c011a52ae24213fdbc1f761040386f2ef49315732f6a4efc0703c094fe11640ce08d1abc90042627a947cde0d348ac45cf08ec79fced7c4b4415afeb2820cdb84a4016c00d3d6696ddd06fb0710ee7ba44410f0fa4b1eff607f9d1f227adffafb8e2897b099503c7fd0fca40b0e44ebfd6b122add600ad95134eb759246d4738bcc42e6e3d97f2882c7311c061232a6f83489eb09ed3a312eb7b910f23767427a0cc3a172af93c71188217d0b40d12a6883576b3a60eee7bad3a5f3e0307c5dad6efa8c112e7f583a413a020aa979aacdc49060c9c5199090d1e0fbfc19fd70df6199c5083bb5297411f7310eda1571e958ad438280009c9acbeab675fab4aa3264ba360de0e19a9adbe14b0cce60355d48427e30a59f4932be418d894005a47f56b4e123d2576e6c759fba0dfa26e582c5148ae3dce1f15c75fa6df6e766302e0f51d5abb473d4ffe660330578329d2610cb8c2c2546b4031be82c3eda11c46fa9d6b11b38061bebe8bb33058a5ce7d8f02c28767a91d79218ea6765fcaf57a32a53661de2397bff9534ca056f586cecd54a979e120326f63ce2830f48a1d7bb8347b882d30a3f6ce043df04bf9bbb11642dd2f36e191f719a7922c96f9f2f0c0ef6b129d29b9465f54e4d0af16346b901bd7496340a7c869b2e6e8c07ed5134150fbc43f9bbb7d021399801b613dadf5c15e4e2979ac5eb9d16267fb9467ca7928d4497d369180eee3e8a0a6b8145b18c0b74e313794591749c745f1aebcf0de765f006ec55b06516ba9f01dbf501152a2b4f3b9173a2baa7c040e85a17b55193212fb0b58e95162539aa0ffb064ff4116146bf2f67161f4e328515fe4f324c6c80ec2d225c1c1144f74c05c531ebc4ac5f299b7bf5257994b2c71c1464d6de29c7c416e99f4b6de852da0ddf4096331d0aa322535d71067ed6f9d7613706310540de09592de5ca67680d0f530a7dbddf00b7aaef4a58076ef2dea1fa23f2ac7b5fc214e76a199e901f2403d3143317cce83ed356e7a39be0d3c6b17735a9c741a398f91a3a7872a1fe190a9ffe802aec237d69a2055fde4dff116d7a9cce4c6e92900840cc933ec76daf0ced3c8f0799dc37db742115b829fa32049dbe067ddbe082c1809e631135999e071bc4e4ac375479274623f7636deac31823745052cd69bc748573e72d40d1e70d03ef3c85ff68d076d7bd2665f3ff851e515d09d3d0e4fb5c465fc2a2f2c9af02169308e91c3ba97bce0418739118ef393eb846ed25df75eb12f52b29c0d66f06a7965db5945212fc6ac52f18259ab701e2d8a3c8fb8f3e927dbc637425893804083f9991dc691380cbe227a12e9f4571bb9aacb42cfe18e392c97b79960b8b043868322e9a43e19faa02d36e1c02dd3a72e5699e7167bfa41c411bc1860cde00a539cabd8523584efb2b78178d79c4e0927dc811e7610cb73830f5199e590400b0f9db5b011afcb5eea715ab0484a44ad2e3c4c1632eb185f9c6ae997eb72601673840615c76579886150f70320aceb4e9bd65b0d95073cd1bbb8fd00ffb50023fb322e2fbf2bc0adfbd4dc18c7dab46e56f09f7ba98d0cc939192fc739ecb0056e013d2c15e22bc7be8ec19ed4ddfb86d54b32f3daff4d01b745ee3410dda0f98a22fdbac698b0230c421cab3350cbd8516d377166a01bd6165116e19835a0cbc67301db35ac95a159333b632a14fcd82d1ae98dc93c364b5fb6631d1d3ff0b761ed093b32ba83606b34646ba596c1b1235334ffbf5571b236fe56c9bdb9909b674e523b4f05c0801f660f28b0455db1d69117028b798de13ef2dfc1e6d9c0d5ac417bfcd821f577c7e5cd2c60ad6d65b804e8ddedc0348a9f94433fc0d730eefe059bbbaee1397e02ebb0533a2210f430ff2e4d5b2076342f2b7cd62d814067d04c322ef715b642451f7c515af26efa5839853745e8eba3e130f9cc14f5a0ee2d2e8c85256343d47691c919fd72b01f12cd2a1d724bb40b059ad3f559238039b4657de44f14a2f4f84eddb786fc787a126b96e07ea6410063a47beba6ed709ede08662f900720f4dc009092dec863457a0980862de0561138ac866beaf1803b89044e5edc0df8bc618f22720921b56c3c15126f9d03fc188cb21d5801fa4035bf3e522e8156f5ebeaf3b4b629ef78074c342af18c3bcacfb77694655b6300f1f6252e2dcc7ca1beeb9e628c376a9b1a44ecac142b6c9a6da96c8b2e3ebbb092ad6e885de4ecb37f73be6c4ae1d86724259d65db9fbbd6cd5ec0cbf9e697a0f3b1642749426d3231bb9001962b7fc22346aa5aaac3777444fcffa6b38b94b0297b286066b6df06f73f48e402cf6e52d28f3d34a877bb61a95682e5e69056b04f8188b9a98b98fa5a18e43ff47653590db26b6d17573bdbbf8b58e325338a00cea26b4f6061142600c0e6cfb6a3a0d07b57db58efaf195757451f163aa6f710ee7fa3f576f663960fae7d7ff2c85d9a1c1e3fa22f672b2a31491babe661e2d0cde441e4e19f604a329a1945d611da4612852303caadd1afc8b45837a38bc430c5a92fe234b91b760f0953ccf984de4f151a0c695c39eaae773c3bbca5c773c0fdf00613a18efa4fa1675fbaf25788743c7e1b87fd8c58e5c94fb93fce3a1cd00171648a51c4f88e2edb09f1b8fe32a4db69792031b588777084f1939342d30011ac4ddc826a1648db66e2d4a2385152d6a60e3753811787130f984c52212ec04ff38d1d12b46d4489374dc2ddc397895cf0e227e44cd3e9cf061b3b35c774b00b187f8029b4de9c4ed1e5f174be4eba6f5132da82a1226c8e57317df21fae30f419cbb618ccd18ce9bae2f8dcb25e3aedc3d6aadf18add4ced787cb0bb45910dd5f8c65e191ca08c0965cd29b36ad98f171ad10ea360ac67f768fe8fec52ab09661775851ef96682fbbeb94550b28d061ce2c6b5b1e5566be19493464086260b2bedd7fc8b3ff07596ead3726fe1c91302f08b88166af5a30b29933464aacf0e827ac4c5e4c64e18fa44b9cfe1265da34f1ca61f10c587b1b4831040125fbf0ec3dc9c29aed0709d0d64e7624ed71f154a1ae624dbddba55185b8b6f830f14084e944f9892089ec195b46706fd3a45e13eb343a32435318c279c2f522030d8021363948509ce5b52e2ecca023f3c5447addb2de817642d8823b15543d6dbb90c6c492578b75ef7a6fb6b68610eb46cbc04d0aa49d1e46b489accdddf727b2c0745abf366ac4dbb05a10eefa4c8b89ad9fd2a2fb99a6af47dea77f6050e304b08b140f76f496339e1508e4614eb8a5fde19e76a68a21f4260025e87e3c499d30b045d514c6396fec580adb4ec65ca487095d69209ae8172ff8bbed3391dc6a80ec33876b46ebf8b544771fedfd91c779e9ea7ff59bbc1a8b85942e8c8ba421204cf2afd357329a31f941eeef45052566c1975e1882e8abaea891097bbc628320b16a6c3e0a3be19ef97cd4ae81e9479392daf18df08a7c58ba9b0aeef68781c04dcfa2cfdf4e3f5526645787dd309a6c62b7dd6c21b0b93c73edd31323cc16803bca2ead0d6963151fb878f69cec112bf20f959fc5e98009de1209f2556ec7e02d60d82ce55721cf2c7bd6081ff2ba89fe3e9dc6a179c56e5cec6ce8baa85bd092b147a66136a5a70423661c36d6701b1132bb4b77b6a8afb4bf2c8d8d3395300cfde725f2e28bfa43e6b9e6a01d4fe88f53936e73fc7a43d45b59f42e8d4230ccf228d43120be8a8afcee8421c18a542a3c887098ebfd0fd9b3c44b71d4e8108ca2b945a828159560fcee8547049b604c198e4bbf0258956e0e7e645b527f507bd5189caffeb6e5bec1a900485c5f8541da28ad83530fef6b31bdc597a1f090c7ca7159f11d61bd877dfd5e8fc249d9b2078cc21beef6ecda49ae2b7ba3f7804780c4ba8cb54c2a2b69743fffa01d5f3e13e31de772110074563099e086bc9071722a2415ac02915553c332ae790e66fd76e83db63d7316b7c2587b3729cc6016be22a7f2438193b48495171d55a2463a639d8a0d86383f6f7869eef2d6db10088c1a19b4bc14e95b1e60f8f241acea5617d24cff9bfb782ecc81b2811e97e0edcc0d177bd2bfe934fc21e882faf65b3eacfee902e853c925b1031447f877c0655f55a69961d84c463f1a9bc87f540ecd38de2acf1bf7ce2aab818503d79640b63a7b80f31478f0af37b48161ec6a29210bf68c86f30be110b3a372149e4b30fd2a6f7e2f9e65652b0f24f42db4433a1e30264ad1ab34b6bf34f2a690034650ee53505efa2b6b2aac541c5cd55e18a362f48b0ca8248a3f8cde2cd13b70a0406f440dc0cfee480d0b1428635d671c1abebaed3f59bf7a8c4e033d6582d05770be91504faf569cb029a0913e832b9edca34bdb5d9e3f6eb1e948df12c901dd906d22fd64388e4016ad3f56f54ce28b2f5bd4d651989bf5b213ba5ae2ea83156028857dfcdc758b6e05221afa2705ac5eb4f46b1d8aaac34f2ce280799dd2ec707236c20df1f71951f589feaaf822ed81807064671fedce0123b7520a07bd5210b13c81b6bbe5261c51c52c19c23a0c598da9a42498a059c454bf921996cd7860cd542ee0d69b934a6ef5d9bf07f8679109b27cb3ce4157434a02d9e50e094840cf7660e70b5426e5aded63e22e38887c029bb2238bcce3ff6d6653eade51b1401 -generate_ring_signature a204b66b297db8196c5e43fa172a9cf72d610c1a3e5c14a7e64b6e77a902a7cc 360b1eeeadccee686eabb2475524e968331807e166ea52bcbf4b806e056ee332 6 3a819e2a86cbb7c93d82eefb25ffc9d557aaba2e76ea0cd35ef017a0f9db4a74 fe4bd986465cd8d4684788e3abfeed9a49365cf9a40225a9b5f7a8f0d27d4992 ddb1f912f0733db94098ad017feee9b550c2afdae5e0bc4bdaab634dde7485e3 ea08a323f76486e70dcdc071e43b7458b070d5e15148e57268b4801fc0e9ecdf 16e7e2e0736b76d75013cf19835f4b3130b7761fa2f1243e513b1940deac38ed 9330ae8956224cc4732f9c94d92a3470737d4943400c6b837d8a5e4769e4c2bb 5f961793fa7fab2364e40a420192a778e214079b163c235d48364ed07808b50b 5 d9d377395fc9db6387bd2e0deffc0dbea9c0039dae2729fed1a5ab9c40bc0f0c998fafc48bf22b7b2d2ef2c5fa8782a0917e536c7574c2e4cc1e5b6ce0164402479427d64aadcd801bcac35d1daadf573d239d8eb8ac155ab71e62b373fe900901d6191f67e8feadcdcfe7297bcfbeefad34ea1586de8d086a7d17dbc6c56107bd9cff11490bf9c9d679a1477862619ac421b1ee38fb7b686f27b37112c9010209e003cb9c7fabf610a2b355a4e7770e49cb91cfbee549321cca8fc26f561b067d1e4d5a246d347f9533fc87953c822c60f629d700ea43fdf31a580ddc4fc906ba4f7d1396473b1dde7d2511bd5ac53f17896a06183eb9b9c9c320dd542e52062e30bd7f06190fe1288f8d7140756bdc7395ce3d7cc32fc582d5d59dbe31bf09ab9f3d3761d54d3d47159e0a4546ddea1b7eb378808a29b5a5f458df4348dc081fab8e442fb0331e650f4138bbd423ef4809a2066614352ebdb268cfbd9ef50a0f24435d4b7f80daa08a2ed7976d38174d8d5452f7609cfc44ccfcee6c74000c -generate_ring_signature ef79e077a9e0e9424ccd7c150414f789a14759ca964a0e13fc01fa5466c81e78 f946890532edc31375a4fc9e0201f16121acea77989bc1f44f7cc9e27d618cb5 7 7833f5dbe94cb5307fa63720d352767c4b1eeae31307e89dc2bab1b483eb07f7 a7c0d7cedf45355d0d2981b5f47390c3e452becb05e015647e39fc4221dc242f 883664c5618118cc6d2e9982d3e3b6d28916edb1db5db6db7bd5120dc2ed1f7c 1227bf14d8642d1ec64199c7b955557194a4bfd8962aac9236e38d25a8fb47f9 eb41c810fb20bed74105bc9cff98bff7526462572cf709a8b85ae65462769d60 55e0ae0f1a539622d44ef3ec8fa1565017e9f763c45f27eb5cbf6b926a414bfd 7af96fcfd866484934851fda064375a00816e21466ffa8fff2425997adb7c190 0b0c659145f52a67e664d6c06aa96452c552c1884cdd0f9e5b7819adcf173003 2 9263fb5d94e7365fe3e494fc4500ad768f81a9b3b3666bd15936850aaad6b30ed45a99ea1b31e1e0bcc35913c82d516973199d58034da218ea6fd1bc1484c1093d0b07ea5069e50240f44397c705d0f0e318756f9c060f3c88a1a61d7d57a2071e2be256ea12f47005b426e4b27dbd6e0a7b9ad6901c95aef1633344cb82150a2c37e718a0a6435506ab74de4eda571986c1f98133378f35f09a3adafac6bc003d58f39b9c882a1635f383d85f1709eedd7a71c2600d00fb70f843141ce0fe0ad68a4d5f3bb987673bbbd8f7d0cd245d0e84be2fcb646576ec9da62619674c0cc86a5a02fd68aea5844f596ca3bd7171bb6c7ca1b9c858ecda2591fab0335f0b7dbe0bc5152dafb81af94de5c4df035b4b7dfbc4874b3454e8a32effd397010df5846a88d53656f5e2687f0c4e81ef68592a98b72f4691c78f2f43b4fdd2d30d45a9d807f713d047d76dbd423223eb20598052cdf6a49b8c9c43dbb9c2d1250b1ca180c32c14873665353c7e316855738eb0becae0e9771efc6d40b3ea4be9071525755640de265576899a314b1ef06c5f8fadc737fcf6c2301fbb8515349a0e988cf0d6d90363ad4ed0eb40e7e63e3b54367967455a16de7327d1bc08864f00 -generate_ring_signature 78e0172eb16bc072a3b80ca90d93b44df26ffbc7dce563174f5865f7c16e5f43 7479b29e23de8cff8858d8ce192a594e7c4c3eddd8b898300b3994efab06c5a6 4 ca91beaf2146e06879051b0b1605935a0df5b5eb1ec9ea4f7f8d0c698e7c5bf3 567e5e858d217b840f50e66af8547d3bfd23c76290ee017b1c5ea07abe4ca354 c24d4eea3e2e636a9a24934ab8174a05043ad5612cef38bda772063f291d090e 7c51495c749ae14fd8ba542a7fc76aa77d4160b73f84e67811c1087eeca84d05 ae5a08468ae07c2ae86e46255bad3bec88fc27a5697b88a0a426c8ee41372102 3 50f7e31413f75f5f4c5707a2e0b246b703e44ccfc7562767cdf7b85e44249e0f72fd6b79a1c4f4e1e47df0bf479bd68028d65093a1ccae83e7b078d2c3594000979687dab48863baad8279c266b1e9ee63356774f90defda947cc31a11e20d06dc4b685dbc1ae7bdc4298d082f97dcc5891b265f912648529f97ea8a09580a0999f9620e1faee899bc4da7acab72b2c0f1b97fd7613c86c68b89bf254eadca015c68c79aa819a5b96b35b265c92e7c485dc7e733119f33fede7ef4bb9cebb603345109857147b716e5bdb56a9197663ed0df89c30c0a1142cfde91459c42cd066b02d43ece97dc229b6badb3ef08ac66f51e84119b508131463d5296d6e5bf0d -generate_ring_signature b2b17f8eddfd010da161593db87e352a00d0f915b3095657ce31bebcf0531d77 6d9765acd0f77a17564da4a5f7f07a0b918bc309f136baf6b452eff47b5278d4 7 0dec43ab1cb7836dd07b41edf9013b8f8565502509bd853b73a3fd7c2d8332d7 d007bdb3584697e80ca791a489452dfaaf96690715738e9d0a9772761622910b 3bb2e27057e5cb52140c29f86ec9072947931c4496da0bf88eadea327b45e13c 85fb98fe49d5489a55a67860a4dccda34a81b00bf5796565a5bca4258eae531f 3d1c25052eaeb43c950506c73a61cdc464c3718a502d77dab4bd23eb34093bbb 386800a322ab462444d1bdb3cd943642b4532cb4e6304270d56606daec593bec 898e99644703e2ab485bf42197618479f68c7767f1a81a754254194df7bb5541 8ccf33c9539ce3697c1ec001f6374d950dc0f5888fb2459f0bf5f1c12dd3fa09 5 10e8243ddb88900ffff4949bc87de7e1f03d84b0dabf6e1ac710e5d94b1b360e28cd117df72e1150e598c9875b5f8578186555645a22a887f400093f90623f0d3f02603e35beae29f56833451a6ae410405fc0c51cba8cd32936ba65b9199b06b7ba48299454ef41d90171ee26ca81bfac2cff6413afd2daf15e7b5c1b826c05c6b80c63253a3fcbf6c6204298450f1534d2601188b59915c6deeeb4fada850703e07a4d150456c7451daaff2f2625a0c2b51e973eaa4a0bed7875ba2dfd3c01fded17cb2ac3117773b764f7bf88bc594a91857a9958d8fe0b4f5363286c95092d4e945fdaf51bde0fdca132d3cef76918ff07949666bccca17a766f1efbc80cddf79e9cab89b307a49f8cc6b8ea54a5817e51a1c7c8c2322622b83e9da0f8049300c71fa454027c0c9385dac052442d7dc9bbc26b98a0e67b680540cdea58017528414d5712eb5833a01c5f7dd41440d492ec35cefdf1280f0a0c3a42393e0e7917a14b6f0f25808cfc3a7d2d9b4e603dc881e50a7ff64f83d1ce0be0c4dc0587083b4b062ba5cc97a03f79a7239bb581a259170e714dae4461db10cf26950a7aa391ed33ad28665a50803ed4bc8ddc0e3a0c5deef15dd6171aef30b0af4c07 -generate_ring_signature dccd54de1c3418a71890e09bdaea34de43197a723146a148dfce50806f6351d6 beb57de477af6fff12b965fcaf9ff7229f670af5466c040902abbaf20ad26281 227 de6f898395f9d3acd113f747c1ec42b3c669642e954e078a84625e04f1203a86 50e0ec8c3ba80da72d39132072ab6db97355bb6d689df8d49323d6519f7afe76 33214ddb5c37f8ee1f71d77ef8f8389cc93ebe3e0750f0ca750067116ba6dfef 624036187cd190236a3af52cbd8ff1551af9ea618b7ad46c4039956372f2b37e 4d8a76e47fa3de3b4230220d19f147eb2ac1ea9cb0e815d5210310474bd722a2 9dbcb6dccb566b9b4ee3d9f4dd096ad64b86de505861a4b27007ed26bd7fa057 8afabd34bb7fd38de6ac26b47d135d6a0c05b890061de8e26e719e87d1140637 7d8f3290f75e81a26454b06a408ad4a5a025586eec86b8f1d4a20c006b0cfd5a a81cb1aa8dd7b2845b6f62cb21e37327eafab75c305977a487c7dd9cd9b72285 586bd7a57d35523981bb88c3a05c38f6ee6f7a4596a5c70d9bcbe72ff94d4230 75c17101e9fd6868e7881f5dede9b009aaf2e6945858c286ed121c3b912b9466 bcb29dbd41f103217282fa56c1c3e49a30bdc22c29f490a0c87f15979e90db94 10865f15bbda6df7774693537a26ffecf4f8ed4a9be5ea8256fc5128a5f5ab24 79c038c008b87d3425473433d22a6ee12f6a94912b2b473be3525df20304632a d2ea981b421db4622dac964447a5fa0f0b91253cd985fa651a0a94bb06a2e531 40360e8d38645b50466c06c3d2f366fd3b41ce9dfd854b4b6a6bbd91b9f2bbbb 85386e78ee0cfdb9c053759b698aee59487b080a3f9bb73636b6e9a36e80ff91 938980150ff4af1e402456d64d15e1a932f5b9ae0d03b7122c952a35456b6540 aa274c87ccf47e58a227424fbbee715aefb91a6a624217bd3ff62f4295200849 17ba30bae32dc6e3aff58c7b865f28ccfe8ad56d1779c3a40c9091dce280cd21 40182f7b319e2332eec3a96ae080935a8d06376a85d50e1dc1e54459b00538a4 18772a0589e44334bfdb0154f4ab5168e7cb33c2aa77a7603425da7c12d6479b efdce92d903112472849ff28ca2f7e312873d9325d28a378ed723d5fc2d0dde9 c874df6ef1ad049bc46597e1de0533c878070fda32bad8dd6139e98b04a44af6 09d67f7cc7632824c92065fea6024d80f1a5c9c2fc97d66150a5e77e9b069fb3 7bd68164f521e0c47994aa974693f8bd236564ad73add045a1f06d5d9a42da0a 7aab16af4335f06f3f24f38806b10c20ecd7f75e16905a97caae144393c880e2 096bfb98ae3213965acb6b92a60066b8f8ee495df93e5a2944de1646c369ac30 460ff31e4a5363b5aaca21aeedf944a713196ca387f438268f5817de68c22293 2ada78c71e899bf785980d8cd94f027e365684eae7c4292cc7da633e00edcb90 b84693047173cbc9166b0ad16a918d2dd5cd5b4dcb3175343e2a061f83623e23 7fcbf67da417193a649c5ca188c3c330d1615cf12e6854cb45a7899c24e08aca 1324e3c54e30e3276fad872fdcd7ba4f9ddc97cebe3f886658ac3767d60843d9 e555257d3f2fd95dc9555f8d89ef1dae813d0f6a9ab1e181f7d5ada73c8efd3a e52d868d66be401ee50f7eb5d74746beb94f677a4254eed91ab753f738e75da3 1d7355aa5837df46db3e8e8993e73c4a294d646d190a73ba40aa3ce841a83534 1aa2563077ff97e778155ceb8f8b4c98e45295e005fd9a5202e62ca462f995e2 f4e6929b34d3833940d5d30d2a7b0ccaa425360c42effb1940a10f49abb4b8d6 960da78fa1fdebf415e0baf03b63eff38b81ed99639186748188c348a3d5d8e5 ab52db15249b26d53920280b2b4686454ee211ef81bc330128e7c7365728d36d 72182c9dd9736c191efa97312318003e1550e18af5ae7b5f57b75a78d93462ae 8a822606f03874e68a1297636b7ddd645966b342ed11e826390a85d6fd13c3e8 77ccb0cb46f40123a379e014f46103cb21169b8f8dc376f34602a61ff3a164f6 c4a730fb9e281d183e142c9c6256183bba9f5871290071ead468d2b5479e2cf1 def7aa66dd688039565fa35a223d34c83aeefe5a6e702889eaee554028efd3e4 c42af9fb89579eb04d706df14cb7c47aeb844fa1c81cbec70512323ab608eecd 6f7331a1adfd3751d3edc5dfa02caed59d35813e646c474854241c7573beb1b6 91573e6964a2f69fba125d7897ee25fef6b0227a51a2deff1dc9205ed0778ee4 ad9bf2a571f586d8368024107021af31d5f5751999b25129f660f5b63cec9c97 0837392fd063e15910d2192b3c180228bfc7f12dd2428a81cfab8f91cdd44bb6 9c363f58e7440049d17164c4bb1f5a8a1ff444e1c26cc1ff97e43f4fd21f1b56 7e3e818d6a8d02896dcb8a7d53add542cf205c46108773495810bd8848d13e3f 4788b7a8e7b695a8ce58a3d61a7a3bd37d2c65cbcddf0f8402dfb8a5b1996394 fb352441f47b8f406cdb638ac64e7b6e8088eefdc9e757b9eacc7d3f72222a82 02ae8186d25a835ea25bdd979a85e2ed9681180b51fe5812ed67f431ae4c5f62 099abc67b8f2b1411f365db32e1973dcb2f6193e674f5ba9615cd3665a509a22 f140bb04214d43c84272acd28a16c49e2b6ac054ca2edb06542c2c2a764f6a80 cb2a56e6e72f1017eafb033c66e4899cc0da2b161c47265124ebaf0ce5c4fec2 838c1eb32105b74439cedb6bc89f68fd1e1359841994b58a4a7aaf5475c54323 e1c28c4e44817363335668ad1b2a38b2cf8ba1c0da151c1793d6cba07af2c5c7 3cf16340f818400832e2f4d3ac53242aa822b581835c701be359124fa74d51da bab07fc6e7065cfd2ef24b0d99bdd0a37b7c3a94890a0ba6e112ecefd9b59fcc 69ae4430e581881a5666177cfdfb2aa4d076e7c7fb1fc1df85f4a42631b652f8 5cb788dadfc2f0a8a71a53cdd95e973eb56ee8d8120caa38c9075b1e1e00620b d37ae42bacf5b181aff456091f7142680b056d866e1dc537b4c9c7c47d892068 5d8879fa016d9badd76b8e69f77bd6f02ad5b0ef79323306e8cdb3d3175fab59 b7e0d03bcc3356971ff4e5f2014a1ea9e2b8a3f87a170d25cd334ef440f1cca3 1cfcec5e2dfcb2243be40ae986f597f5e62efe2056fc0ae0f093a1ef8eac4c10 d45fa110c2c973d529ee68f8b00a7599c9e9821af0006cedc1ddd6ab3e37d9cb c003ff2afabb78406fdd999a1b48796082f4106fb616ba7afc27b475f2d78b79 6579d1ac0b18977a0b4a3184901abb13148a833b29445112fc0936e7710834e4 d2742bb8b5bd2fc348adb9121eb048b842ff428edb4cc0e628a51b235453c183 bf09a5bc58fa250176148cffd82244e614b0fadf94c23e53abf288facc92510e 6d37834e2372f26ad0ba8c510660ce2d74a495419065d78480332253bcb686e6 56d85f362ed1b9d5cf0cc9b22b58f704d7a1d18cd9799621c158e0cf6eac263f 15c8bd061f78d454c02a67973a18883014ea1a506740e3231a9b942b405099de 444188e9d62b697974066852422a091f9f9687895db8036c4baddf71bff17b63 652c85a355ae256e65bb47ff48c0f421633806c127bdbbc99b11131986a17476 d6d364039ad279968195fc81f2b2b1b8e315fdbf21618d05976eb434533ddbc5 082d940a66f319b4f2a26578dcb0277c9dd241d209f11059aeee396c767d6943 0f1ba3b82c1278d71c7ada842514e9e8a6b03d49925f279a55d84cecf4b8ea97 64369ba0557e73fdcb4fab986fdc58db1965fcb1b5fc2baaf7c86bea7e4cf0a8 c612977b21bde87630763327ba5fb9bda6312a03fcfed6aaf40b56e65c945789 8463e1860d65d4b64eac0d5367a5af5bcfa203c16dbfd083353917a61a74bc14 4ad00dd4cb6e7b62c74a36ce1ed7720b8c6453b570677012091f47ec7632759f d544467e93925c0fbe87a630d5a2471f3b67e79abfa1f8d3e89833e36d57c7cc 1127b1aa8e3d8bea0b4a9184720eae39e1879fa973d87966cd3fddc8901e0ed1 afae49672415a9cf1aa4f5d5a921e6ade299a517a5681c5888e1eafac35caefe 1bb1d53b9801aec3210277fb8af2e473a8d1026495c0900735d89f91adb54508 53c979d5d09084b0d729b5e740ef1ed51f99cc416b3096869eca4a287598cf60 42da4fc63c86c7a13bed979e30195d45e73d0b959865411465c2ccc30983a0e8 b0e4fa801af1c5c2834b31cf80831fb11d626020255f2b0bbc7c96465f523288 2af31e59dcb552b4eed7558885de0cffbb5cfa7217b423c590017588b488bac0 6acfb4632b22521dbe49fd353a239848f53e7265d43c89b59da8b0cc3f8303ff bd720e79385f6d2ca5f70fe141d02b50e21648cb3f959daa425de1e4392e5c75 0e3e941a4fb0c523195cc6a0dfdea6f52c566ab82c25483b9a10a9d11465589c 85fea41341b6d49d4823e309d4468d7cb655811177bb8a53e236c0747368b819 32593bb228b2f454aa091fe6b2e4716bfa29be3089306895310b1f2f57230c56 4ce8bbf39404f890dd296a2718624b8845eec23023437ad83dd3f90e3ebeb52d 434fc3946e64e1d07b97b77988cb48b8ae1133912dce2b9c1902bb9c83f63886 14354e897eba43297579a03878669d513e37ac42bee8e7c64e354ed39ba6ac3b 67c9bd298f98c083736bbd6511fed9fb238ba09634d630acf8c13fbf5d4139cc 3c77c3a7286acbf62237efbbae13eac56c4c0bd55f7fae2176550ae2f49b6b46 5149f6fe63a8042b6fe16c6e31b5b3da0245bb49ec249045152b1814e777efa0 e9c51ac176861a0c5e72275c34686f38e8fbcec99f81176a4f280e50c1d68de8 6d2de0ce0adf4eef46e9bc7a082ba9c45384c5e2766f7cd374f6e7ecf767afe4 7defec0b1eb6aad14ba9f886a3bf94df39f8f9beb4bf3dcfdd8e9f30c74b62fb f816bc04b50cb419e5342f8d9c1c45f61e8f7ee1021271cab7f60eb938619856 4e740f39bde4b3ca794f5218df1045ad330c992f198de22c7b1581e585f6e8bc 2a707dd3aa3ef5a0fb89987ef90884797e9b679941563b1b0f0fe482df2cb704 780363e8eaa858601ceb6eca2a95ff77caccf4a4ea15893dbfdf1d97f6196b5f 7954b47f519dd86d64fe572f5c83746dfc6cb7ffe388e257e5a27242705a615e cd730bc5c83ab9fd608d5e4d0ed2fb3a090e993da29c0341120f94940fb31a54 50eaa1323e56b6cb217c2728120f51cb7fa19c2dce41be26baef95c6f61cc276 811817719c3c0541dfeceff7756e80f7c868d56d6bd06feb1027a0c37b9957ef 1221701c18e4cb5e606260d2a85c99433c136549ffcbed18cf2e843ac4118695 972eada79c154e43a5ab8797202f266809d163f8a1496bf750667667d1f73659 812e63c9f4f8225eb94880915c2142e26163ef6d27d4a240b9bd3424ab0568ea 65e81550555313646f8ea5d2e1d998039c087825103a1cc9aec56489d6e7cb4e 4cadbcd7d0be79f6f32dfaa6a45a7479081bb824b5029c3efcfd51cd218791db 163486ea2d379505656ed6065a355a2b5a0bec2f1c56121047e2513708af0ba6 e312f142355698ac9e4705286e24ce259986cbf9c475be9908484ded0aba457d 5cf113e03b26cb54d5f672b6997a44b28af8421d258d1a51244abae07783dde4 7d19c56181925f1502281af675823ed9799978028ea3fde25e7cb7f2b2bf1af5 561035986edcd4fb791798407f9351b9d15ab7ddec1b1156211e4372f65c081f 9c2d63e65438f187250b110223fc1a41600f1516b75299d418698ebdb278972d 8db1c5443b19e83d124cfb93cb7659022e0ab39ca30fceb1cb7eebccc04c33e9 2a0beb68b0cc5fb35a2c8ad37619b7faec3a573380101bb1f3f8f930e22f17c5 95a8467c529e347700ead64b0f2b2520edba9317e1988a5e05b48d88de6b23c1 1f07b7c84d37af770b0d4dd3db8031fd8671db285db5fb292bb24b4cf03a66c6 03c26aa45c9344f2108ed8bceda99db51e2b3e476a9507305f4a264cfc22487a 7acc92bf21c416c9c2853cdbcb75912d17768c0558f45a4f9dd89f9950c04925 570dfcad88ccc890f989f6fa90445433718d1a33c2f6cb9d06f01e105641a0a0 0e703be79defbf776eacf28e3c02faafd451295528af4c3c5abb85af27a26c6c 8868637d4111edc6b9967b4817419116ff4e08342f94a1c919f88cfb66511db8 ad34c041687a0aac2d5b95645c3be9a092854f05ca306fc4df87b66fa20ac854 3c9c07cb12c72cdbd9c0a53a56dcf49286ccf1443bb46e389bc7f98e9c1e5a08 0a73a13db0b8a4cec8adb5f49b3ef1aff9116ea1b40907e7d7d53747914a6165 301e0e2771166d45a7db4739b33e67ded6f458140e03b77f7dc37dc10e5795c3 456ac73e71f674bd8436300971c616ba64522aad67f199c8600ddf5de5a5e6db cf529e39c63f83fdeaf309fe226248dd7c0d8ece4889249b1ee3f42802e84bb4 6c593de6e012579b5cc3f43dff7ac21e30433117f39e1dc5d17627dbcccfa8bb 2310887b528cdb93fbb62059afefb5dc8bb1ad99a6c26f470e8dafea9a21b75c c874e7cda400a4a7d347b9ccc3849b871083bbe16ba4a0961b5407d885d885e2 7d85f52aabe672cd6374d1f45ee2b8bb64bc9aa713c9d5b11164358dc0618043 ee63fc1d84e5ce7b6fea05733df25621d53e2f8b59d46cf6d3c206e0a1a4bbad 1945742ad66ed67a7e7be7619553eae5772137b123efc125cfa56771f73628c6 16578d8f6792c063ffefbb9fbda79cc2cf4a217a15d09fd6d9e8c6f5cb3edfb7 2fb93dbad3bdd8c52e66bdecb845ac12df250668987a46533e64ee1702586c11 0dc58bb319f98d536c5aba1b410dd591de2cfbbcb9626004e73f953989a09063 8146f9ce2ec99a5581481f5f9ccc32e31550925b16239c20adebe48c2a0dc8ed 6fbeeda5f57a6c3bec5266808f679ebe1786ad8556df24452d9465ba0163cbca 434b44876cfb72d61daf97f0bd8aa85d323af4320f52b880dc55a9c06c3c1463 c13ff3702adfcd8acc2a0b6e4e675b82f503549dcbf14a7c29f66393fad8093d fb76c1d8836c2a1f718106011e3dc5ad544b893726fa72b941d123ec6a20f785 2b7ecd72aea8643057327b542103b08e00b79ae6198592dde61cd62929089e83 e7cef581b5062606b00a2ae4310940b5f18d56d2742705a8bc3c7c54042c1694 d5e7cf1bfe62c0c6abb750e4b22fdc9b11355e4b34e884c94571163d21a81d6f ce69ce14cb9153cacea90c7fe3e8460653d27c61d239718a937f1fca35f99d17 b2aceb0c7bb9a7f865b3f01d06eac0cb1d3ef32a4e2145a49473135641276c0e e0dd5fac25d7e724aa18512adf237fc463676a5d1c9ce42c6a8b2c5cda013506 f7e58fe431e81b6fb8d5db68f69a7e4596c76227c0291f73f9754640085c7675 b63f41f757fe3f6eb32d2b7ecbcc86dd06d3ed3c68f22e41db42550913169a45 3a143a89e143e860c93e71c71f42a04ab75dac68baf92da4fefa6c6b173523c0 7417ca2eef7e735a3ab12e4a0e3b3c2439c71dfad03014200b8192206acc78f1 3e0136f18d38405b67567e2ea5165d8782a1ced39df60f06980d3d2ad9d24f3b 4a3e58971a4e4f94ce12c054249d1fdf9dc371bc7b5279f78197800c2a51bb46 269cfdf63c238721dba6bba1b839759bb0fe9e83d609a2901d141786090c3f94 95eb48427cb6c4f471c2ea568ed9df6633862be02cb2ca025c73b2d624b056af 421e453d565a46650ef8227bace09af7c068466da857357cd9b2873ad1ad09d6 822692d9465ea2005914901f30869c138f9a6351ee06b318ea9a26d443d97d39 fa23a03fc5b0b6e1150a54fe2bdfc78e925f4a66443f7e4a918a7c44cdcd05b4 c40394856093b0f40c439a94508d1fc3cdd5d6f1747d7a876c3250deb24261f8 0082526a179e1f85f2c456b87355d540144ae21c53cadf9783c5a00cc145fa73 91265fa8f7e3ccf55f695c17a0004ac429cfe485463c2dca00a116b84883ed3c 81dc3104800931b6a4c5cece8d3e2e8629722bdcc210bda4cbcbf9871a43edc8 aeeefded266eb59949423a42d3ebc1af07873e1e954696bf44daf3a75e7dd5cb 230cb94219fe81dec527b602ceb442d2fd047f5f69f94e5aa5229e18ce6eeb72 e495df1e8aa30144401c58dd35d99e206efef5800b42ae2c855ba2933ed1641f a3aea8193f840fd72d17b18a89b937a4892fd267a117d45db566c334763275f5 21df42a8e0ebc06d0be965488fa6bc6fd56e9a8071e7d7066e9c0be8a18715e9 199b10cff8216a7ee22ceb37cf3c03d219233e65a473f26fef00137d8c52be50 69375507f886578c829b830f5964a71243a21a88b9aea1555bb34b136776ae64 53d95e59a7cec2ea6ba975536d48a76fb560f61e988a7d5e27b2564c4e429450 9840ea72bdb5e374a4b515fdbaa737f50be2d4115e4192d0973a652c10d553d1 ad8d699290c6ea3864c6dbfe1fc2df282a7033a115ac050af16cadb47f1814fd b9b84b4655239182a7c635dfda8444a090d56ad9f36b39e49c9cc04c9b903d5b f5c7569fa43ac9d2639d582f90467c15ff8edac68da3368307be710019cc73af 52b6e9e81e004a4d350d87b19d86dcd759429a09954105b4b884508d0fd2b022 194d7708a9ae555cb73ddcd4b5e41ae54e297c19b03525eadf28030b16c47388 d158efb1f35f7d182a24ae62a543440c932d48b056d62a6bca8da8fa388fa251 6aecb71bb4a7ffcd6c4d15bccca66e1f2bc16d4a6c3d75990c3dbd30800d0282 5ecab698f65743d8bcd619885f36e574f28ec1f665847dd87ed66c7bf94bb8f9 5824367f4a299d2f99dba1bed610b421f6486ca776d3b1b3e8ac8e9c7567b4a1 9b1a0a3bd2e544a7cdec5b13861e77c8103386e57710ee6f31ee7ac777d3e089 eb852b565d9a9a4b1175a30bef0ed763c6d2d0369dee85bc28671c7d50dedc79 845b3fa7475922b76431644b6be2f0cd48c997ee497b0e3ad7ea8c3d1061e1da 1e9b85af0b4eaf78c85ff0089c6888b8cfcdd54f876d7111a3df6f96bcfcc678 d30f8e5b89b088b901c943e89f093b40838b706757139eff1b47c848edb286f9 aafba65a11d917cd3b38bc357cea2ea44cd018cffa6fcb9045e8b35501f93ed0 931de2cdda585f0ff0f7f81e747c1c5b20ae49a930a943871c8581dbfa5467e3 6f7ec2a05580bea3790eda02264a371e19ea10a05818b2d1f9df342945276a76 f5a08ffbb5b9317ba9281c7c77f3f6b91b02a17b8bcd51b7e0b2a84bfa0399de 4737d5fea3b9e3567f1c74a6527c2be489002195ec96c75fa7a412fa020c4b90 8693790f118dd039b89e51cfa8b7887c98bf8145dba4a981adabf3e354e45d77 b8a660ce9846d73f1ebb888faea6345ddc5c2124c9eecbab120d6ed427c59f2a 4b1ebf45036321b68ec8b0543203d21bf9a639027fe58261a98b44c0af19bfbe 24b6b654c8f53fbe508e669b3804cb9cd55056540a9b7df24ce403888b3999d3 fd9930b13d52c5d70440304d0e1e5b54a4b6b1ca2a435677c5f118f07e2b36ee 717eaf9e1090b2f1894e4678602a3cb5c0f374858af6b0d4fc723b396353ea1d de9c034a546b2c6cd88154fc9eb766f30b986dac38c3cfba22b16ddcdfe9dc7d 93c221d3be2d20f4ac343b9e67beb6dbe181acfa2e03e80b536b74bddd754f38 4e3424bb19b878f1845783d33117f315a972e4cb8a4f5f39218ad3eb081e07d8 4f254a776c0f3bd7779c82c22c33390aa41909c89ea9bdcad91dafc6e4003050 2050b427b2a2ec9dfba6c86014777901852742c5783c8e89d114938ab728ee18 9dbfb19eb52ca6e2ae0ee44e5df65bcc5e7439af40389244cb3b54fd9866aa26 d8b8df21ed6caea91e075382344d7e63de02addc86beb7d76be24ef2155e8353 22662c59ef7015896560440498100035ee0763c5a6de0b263dca397058523c34 0b8d591914e20c40bb5fa99de44ffbaa993b8fb08938e3f5033c07f9e4bfe881 55f9e2ad6e2699c81c525daa2008c323a0978a3b85dc99d591dda69f0aacba72 d228415156119c0157cccc74483b149d443a4ce32971461239627b7aa02a4b89 e8c7e96a2ffea407a803eadcf5ddc6254795c329a6f7332fe8220bd26cfc83f0 6e14ae550d01199ae9b978ace5b382fde0807d8671827a75f3951b18827527d3 259bd7e461198a3ca17d4336ed9f6214cfd10404d3a6f6a9a0bbd87c26bd9076 25e4b2650205fe5dd3c4b1ec5677389b00a4912b5810622b505d72125200a42c b38caff798124570938f7d2e92aa8b0a37229020355e0525069cb2c2ea16eebd f6d09aef0f9a2820f555475e44521817a2044a407da71731aac20fc4b6f2c26e 1fd4bd9f88327912a6c6ccf04009e9bd3cf0fe43fd654e1fe96382ff0ca4c509 35 cdeaa79e388588a228b213006bf04a63ac19db85eb2deb2dd98ae260d01f000f9cc58322d29ff9e9e24a212bf7305775eefea0d3174535107afc79b25df10a0665f7b42550b344a8a11331e9aa89e0d1995f6da5d76f614318d341ce5a2e250b4d2c3b439d41593c4e6eb467470f0769079d9de17690f137f223806432f3d20b17b1f446d5dfb1e48fa5559a39fe5921934bae756d4084555349a8830ca9f80cd4e8e12288864e05b296eff84edacdcff1653d71d4aa39149bac191be0649706242f00f28d4a52b6a60f85e127acb608671d89b6e497f30aa17df04ea04e1707cc6d2eac91f4f47dea55529385edb1d777ddc8d1c940e5bdfa9ad7526ef6e8050da5576365e5023b4e1157f8844a443c3a9a7b487fc2a5bf3b36f6fad2335a085002c9ea34f3ec4a619b0f445b7e0c0c1e76bdb24b4358d9a75976463d026f01a33830bb5eeef8510fbbdbca0e8a944e127c356738f59ae00a4ec870d8da670eb815f7e7ad0080794522089ab161326e9c85063a837bc95b88a3bb6d993331070724a2f4e4e0a22e4dfd5c698568d5d8ed30987b1a6640525a6a9f248895d80713e75c0ba48132858f7aa1f19d770859f9394e8632caa6f1e10c3190bded0600040a56daf1772d5680a2d12d4c775b63523eaa21ccd67c6a910b0bd130d80d04ae2f34cb4e61f4396100628a98d4d35cd166fdbf3a3775b14f9385c93f8b6e00769a9fd420af5952b1d08494e1ced6c4193fa2e11f59f687abd98e5663a87803fb283a62c8d860cd6d2f7364a07cedc728a7c2bd1eb1c88c69bf48b1674d870a484c50c6a5473982c74c05bd0fbf3f41dbc4d1dcf543500885ae2791f214b20bc5c9a12a38d264e19346f11a4d3a7bf7fc4b6b4918f4857780dcb908a4301a0de95db179f9e2cc806c99cca0c276d0a509becf743e414063b54d5027d0edc70404f49b7b95a8d69f254e230f1f4db248b17a4dd99810afe3bcdea1b930ac3e0fb27c2f2ab943433b0dec6c97367c193a6e547de3ef510372d6e8ddb9b05e990e958d2892cc1d784c002ea1d6b9e350cb05406e4a2d94a5e7055138260f0ea60822e6e42e1e854fbd83450fd6228cb32c301264e606fc806f01b2e1feb549ea0b24c423012e27ba58724599305a16d71427ca4e9d2a0024920dc26eb708c2b206ff2c1770ee0f743c0140b227119f01bf51548b27ad4650be0ce076ed0a7b460ea6c57e13713dc13cb8d5885034f96c51639d4e7b3817d01991cc8fa55af8b5083a182efd696d00c2f3f60dafcb3cdd448e6e12ccf16a8ee3b67d1a2c1f64dc0f70a5a9acd0ec1560c4078444c3caf356afbe8ac020699ec65f29abc97ee634013773bf96c7b23a0f28c446a6ade3411648abe7d590e0565aef83aaf49a3f7000311a1f515055bac75db885cb809df0fd90c9f6898810a40a8eb52934e214ab091e9321fa9fa1b3e70eb42881b89f744a423d3b841114c89d6d28932627aed50bc5fc9a086b3d5ec9174c7e7b1016b1845372aab68a3a74a3082b288480351701b4c911743a6f2e401faa58504fedcea048aae67604bee3cf64012c7622d33f00b48bdbda977881a47f7d87d1faec022e724faf7ba64c9232ed07258cda325c098466b1b26d4ea65b1b1ccffa3fac2a679d592b1e6c8bbb2ed54748213c7c530cf6f060cda105bb2b47dfa1baa5d6952af807caa9ffa6e67b907e1ee706f02c006c35a0f1dfa7eef88ba3a38f7a27fbfa9c8181a4de8c52b7598b5ebc7bdeb00f62b9d3a16bce2730d632845471d53c1f919d88d95eb05a1db942f840a24bd000f7d3e5b3a0f78f6b7033559b09a7abe01158325714109f37c92b2a6d99da4a0b090bd1da7225c767783f672b2c0283eb61ddda1a1e1d4301b114e6640b73570c1b929bc0f52d16824cf70653a44b2f956a7ed42a09e4bf0caf7c733a314b6304b88d9bb35da68a3333fea412bbe375cc76ad8f5c0d17c917d656afe196de9a09e225e4936fa2455071adad386fb6d1929b23dfb1f6781c33b09824f8f78e750e5ff103caf65b4bee44bf315cc849ad776a9e0d61ffa7a87f2f45bfc608a0070e4da498fc65da7baab53519fba0501f68b2909c88ef714be6f4e4f79f854e8707aad4b49e8788d681faa6b7e71ebf95c9257317faf67c3c12a8d02094392d0702a67765488d36df139041c051739016f400657ef4c835835988410cd1d3287f0863db49490cb9e24318d20f8d53d32c30967cf0538b48e2f1592ced8975c4f702a33531d9cdcdf3443fd78b1df226218bbf7e24a79ed31300530f225740b6700f7442a3ce993f323cbd826bd308b887d90cadaabaf002bd77a5df68743d68bf0a6bead99a307a4139bf414ab99ea7f8410b7bf60bd191bd84fe7760c98d4fc50f7242abe82d13b754cf3b2c477127481720e02490de10a328e37f2ec8f6752906ef9cc5597763c1fa7ae1fe1972fccc2a38f764bb4cdf53eb94221cd6a5a12d022c561c1224b6043cb817241be0119b7a4b095f40347054be19291a997053e3041a13dd85461a770d2c274c72d9b2e52cade0c3c5d7e5a096639f742e5a115b0d69894976ca8bbcb3c4fed370149fe3869b07ed334fd760eea68aa00104301900310ddbd5bd113f538178b32f2f102e4aa3082688acd35be132dee4a593e5bf0608304d9e228b6dd71f1e8506d24072cf23010912e709d485880e2fbc319859088ee3a7409561c9acd797f12f1dc1608de96d348cf18948d8fcd863dac5dee70b545f9c9c126ec0c3aa56f4f207d5c9998afe5d92a7a5e2a9686956d8cb9c270b08fca47302f4c712c3242e0ad6a74daf70e00a41a545f0555d0fc4b25c251a0fd5cf4025f8585a3701beb018e896001c24c520545096a3e2731af209d70301060f960ad732f1492b6a6973f9b1c55314eabbd87cfaee19adac008de884ff1c080978f5f4b8b90502645c52bd9c828b7680a1b9b2b6f759c2a0a040eb75f1090c2aec7a07f9a113a6e71a2b488aae087b0a0c4571fb7f53d05715a5c19533fb055a6e929a4bcfac0b68a8c1d37179cfbc74053dbab1e283da9821e004f710c60893554359be8790b8bdd7e913d0489643291a2d0cf21d14cb1835837d7ed52e0b8067308083e970a2aa04919072bb0ee6188ee434c61cc79a6f42929ef81ba50607281abbf44af6dae459015b1d8abd6adf20878ecc9cf2a34ac7807ef3fbd009bd87e1fe328f15c7c64039c2e333a46adf98d129a3ac3b046f7a4e0b1321ba0d6d04f710c01ca9cd8e6d8a98970161405ac874a6aaf4007a2c07b6451f950f04424704f15ba46f70526a0ce1a54a0454a255dd4dccf3a93374a13b1b3c507a0124dcbf2160298b35e2688c4e5b4e34d5c97135b464e51094f7d4aae00f31cc07f6918b4a2a09287ff24ba4a07a9dfa73a068231d1a2e0a25412ee9b843814c015ce5bf24852f005f0ca4d476262b37d8d0d99fdf04f4bef46dc51dd4aa90970ecf685723861b5aec4b9ac974a1ba68f3db11c064b5a1e8e2572f7c53e9aeb8091d18db6c2230410357a5b0964af1cf257eb20c5dea2951c7643c63621cddeb02fc07f0203d1966933963616ed88bbe6d1465682c60e9c595c174627245ae17005fcfa8704686cce92d22c5fdfee01784ace5c0b1317854421866da3815ae74059794972f1c2762c56f777aa1ead083ffd2c00c1efdf4d2767d8dd2057f0ee8090f85a93a86c39ef62deb890f3ce006d4c8115866a86d628427dad2862ef27001907034cbbf27f6d01c92ab75171bd0b103c776777024b2b84c2270c6e359d30d6aa565eadc33e4101591fda33cf2ecd7989211df4a1ca5ec2033494a699b130b3aae31d647f74c8baacb416125cf140e3a4ba99bebf3e7b3244feed98490c90da71b22820ef6148bf0917866c261a969b665cec2f637d78f9651bbf4baee2b0da24d96f299aea87b2a47b7f2f1161dc6e65dc65bf63d42c54eba79d65980c60d4c8829aea5bc47234126f97dbda1c1da59594ed3df1bce82eea5e5d290358e0eb2c3609da5d34d19d733c3266bd2b9a9072edbf36e5d5c2a9365ba77dc8741081f6c0109d12a38fe750d618e3002f91a4910cb1a405195346dbd74f663e48c0c71536a347434f6910076c4ce30225d0ea03b5d81d443e088d16f6d18cdb70608c9fb9c03c45e418cb149657b492d06ceb10af647debb04cde864b5c20c28430d475cd2604552a1fd4fef38a3c6ccbc30b98832b8b558674faf1c4080ad715206fb0934d13d159fdca4932d830f98b8b8bff590a7de5879da054f17f8db1127052a9e7fcc069c31db53a5f65497841c75f0802c0a81b9637798fd2a8e9ddf2902cf8fd6b9580c7f3493e454cf145c63698799594a65c29b3a31d1579e658e4b0357d2ee5b7da8cbbfea179a622302cf5c06104ce24fad40f9d41b6cbdf3264407fe833c33b6d1e7d75f5a3a7ff72ac5ac10587299c370ebca9de479a5a344f704047721e4592bbbdfb21e03fc694a2bff62deef1935863e4b7bc624582e854e08a89d66b54418e407a9155387bff7d89275b3762d909cbd7a8880f3c58295060d4fe1657b5591ae78f20d0cc8161b13c45ca8d7eec600e246d3dbacc4f2f7af0a62dcd41dbca4e253571420eac293c2c63b818294537561392e2f3b13ac33f20f5ff521a476dc8caef82c5cb51e39734f274c29a789100af7884daaeec02453096dc7352a3bbd6aff3fbc3356f801095d1d6cf7a5d813049841d94d471aa03108583ca7d9de1582e8e4dd45f9f88fe16713ccbe9f39a52ead09d9a395cac7930d6c611eeee5b6778451405e9ca09c4b9bba9f97bf237fb776427323d7d9b926045b2d5c95d3e433f6531fa01d34d7af45e6e2212fa712e2164db646795d83eb08e80d35ac30aa28ce8932ec0d73942302262d928782e8ace0486f986165a25c08bf88f10958e3e2a474618885b492a5d7eeffb7771e3e6b00bfe8da3efff57802e52aee6ffb4d7dbcf6dadd5acd4d0b2dc4a41982be90af45c4ab1bf9f62f030f8ff3cef2a36948d8316fc800a8743d2325dfd0e1e07e0b4dad3687529ab5ab0e384059fb6418ea8cf9e88379f8215a235187417d8a879cca2a685858e21c540bb359158de7e98740003b5a1b7f88a1cd2e9968dfd474795deb2f193e632b9202f596e5ebcf0795563be50ececd16af2cdd4e2eae8ec35b8d8c93c7a3fe6d7b0f1d8239a05b2b2cbb675cbcd268c933c03b0cad7447fb57670dacd54d32fbfe04b32e1da6315396f7a0183ec066097136a54577391ec4c549d06dd4097ffb490e78c4ce6dfd0d93c236c8f337d90e2a926dca75cc1819867ec70bcd44571f5402cc1250f2001782035b22519dc75e19104c8c0a83d6dba13904970c6a6908f4082f269fe6068e05aa6f42e1a7d48afe822619d2ac3c9aa4241d0165e9d53acb01c2ceb643e1f426379c847ec4f7af4479a1c4bc71c9d37df76055ef0ba8004806c6d7e9b07a43f58ffd53821697e50e28878f5b8fbb86fcb54ac221cec6cf69086d2da930fe636102fbf6eedc0ce72e6b5aae2d4aa5696166dfba67258e83d209b4d1eec2b4384d5261e4f1d4a954796a562cb2bc5517aecc2f775c08a77a230226ae18efcce4e136943aa37bc1c7221b1bb2b4310c3c08d16dc1e2f1cc789d0b39e043fcd39a011ebbe4a97099a9728a518c5959f1d3c1b245f012fde9dede0dbdb342707440cc6e9ce51a0f3e4785b657d86b89813f1a5ca6bef629ead3f30babc3b64ec83cc4e69ba1eb58944bf669d08aebd1e4cbb86db2384d48e5d9aa0ef7efce0d17d24ffc5401b4a2a42710ee66ec558524fae93413b552504cf485034d0ad2c36fb3c62c8a2b07f9d823aa1256efcc2f13688dd9ecb8973a48dbdd05b15080bceb1652df115f8aa9a31e771b10397fe4b3844b79293522fad42c780cdbfdcfb893a3de7fb3c38bca750bb0fd5941d7b2108633eb74018aa195f98600bcebe7e1204a85198c2fbddbfe10fda46b421c87f6f68a873720dc93953efe0370552f755d5b7a23fc3873d6df9c04d4712c6868cad526cc42f85f418c8daa053434841ada928bfeeaf99fad80357484a96facbd480e77a96addbfd46686c10f4e6066f858a5487ec5de40fefaeb6bad322d6d78c36fb49d00940704cbf61b083115b9deb5a512a7161ca8e858d5dc37ec96d3c8cffef899a9110b25dad12a0983b92c3bd43a85c463bb745cb724b10007f98bce4ed3e512dea1a29fa9bd0309ff7efda12c5791fbb40a9c1f3d401eee4e9dc551378086721248fa46ea4f440b2057091975a65d8544e94c410d1aa2399ddfcd1fd12fe77f11d314fad2c290081efa17caee8d44417cb5f3b83f87d44427062d9bf3013e5dd569d16888d4800b6f374bb53b1b67968ed89a2043eff4aeb1671cf01dc50fd202d05da1e7810208c32b7362e276a24fd5ffa9fe8553b526cd3805ac929803f4feb2d8110accbe0699a4be33049281d6bcc6016c2956f350e7058a6a941b250166778e7c2171300ea1eff23f135cc101aef79adf820453e6c22d2e2795ee93464429f6e3382205007a61308bcbd2b3010041b2ca33d039d3c9027da032b355bc4efe30933facfc01038f459739bb5a3703fbd7251e15b0f900572f2e31b121871be7fdd89c8fc30aeb3d18669ecf5e9a8470aa4fc768f10b59ae16763172c5b4428b9ddfa906fd00e022f69137efa5307b6ffeae57d21c0a1e8bccd6d618cef7e7ca823ac143040d96335e6c7684d708253686dcb13c23837c3a7410126cc6721274148b451bdb06ffe7c94adb867b157ece494f51abbe060a52803900517ae39524fa5b42f9b803d071828f7024257bfa66961e8ef9f0e68c7db1d1a9246775f02af5bd1c54d40fda731d7d80846739b163e1e231ac3fd8073b2dad678ec0f67035358aacd0370bb3feca62d2d2eaaeae524ed4f217e19bdb007499ad7248e3def590757b2a4005fdd16a81257f642f463b2a200d8bc6e7e8e6604cd9a0d59690cba2e631d63a0bb3cddce90f809ecb866ad923a5b255e10298b4c8194fca56bc6afd3fdc6d2302f5fc45ef486bdf714fddccc81987932fa6f691c47f613527bde4fdc5112581033a87cb8af0987c60d886cb7997c79437ca93432d5db6baa552561431a8fdfe02ed1e0fce8a54bc7d81d948ef70f2b47b8ec4c36f01d364da0ab4ff87ec32cb0714081360a8050b544cf47cc499a06d523ff10007ba031a26e71b705aea77540ec52001b24694b4d1950012738377709d341eeff435003e04b00c284c3cf3410e7387ab80828b6f399a03bd4730a066d859c9391009d50eaf421a6fab2094230acf0207fd8f690b404af3b284318429ad39208b6cf12c3d6a93d7a0e710a314074ab21e1ea1ced2043517db3395a996d2199226900f7f38b475fdcd07ba6d5f08f1ecad206677275cb49d6b2ae1ff9d47da5df61804d39fc802a0050b1d7fb50d360936f132e4e781eb2c9a4c3330e428151ee0a6b4f84840f791727cbe19f803fc35ff98d2c85550d700d83f918c2271b1dfc28c6765b9830a0eb00b4edc4a07bfc83831eebaf1068d164642c2ef7bfcef45971912bbf73b46d7fc99e01c3107e364cd274737a320736dfbc13a5501c70a124189f79f3f7d4d46d3c44325430cabd25615ae4fc6845b07291cb07e4d97de80fdce7e1cb9aaf1ebc9de062fd108f0ef40cf79977bcb66fabe3411f71ded21d673e647331f6014c36634cb77ac06d7518ace6eccfaeccc357a4adc183437f05e3bce67dd1b848a61e9591ae50905b74e0096d330a7f0c06fd86f617addfccf01634b3b30e26f67f26b2677e5a60eabdab5634255426dfe682df38ac090bd42e5139baee04ed6c78f8fef5cea4607406adcb5eac3b7a8f63cbd6a8c04bf427c0cf5ed90c811af8a51fa9d396ca700a5add4a40985cd9f959607a6a0cb50fd7e25ff6d89953d7e85982a5b1ef80d03392dd99b5a6f15ad3c674e67b0b645c3f714a8a06813b5eb0a766367d9be62031a9a788343bc4aeff833e8ef4547f5393a6d3903ce5935644020f7f7ca5db40a9e0d44716a42af0fbd8c8f1ca2682cd72b248d5c971f6456d440e61c8df88c05e38f4e075920a11020ea9f764bdedf777794bde24e6f47bce1e5e550b6afa60f119a1dd04248ec776105e7a3f945a3ce67ab9ae6871b5d466d70eee5f8bc8205d5924c9b0a57abb87acfba871c78002e5bb333a94bbda688a8f0c99cfa639a003ea94302ea59d2fbe9198ce230cb88ee6b55898c51f4d0c1ecc50380b1efe406662e0c7fb80688e0c9650c9e54b6437688b95de6860406a8ce2dfcfb6fdf0c0e3976cdfe3246bc26fc4fc82a97344a4f4ab4d8983636d3559471e609e81928034c19ca0b5407e0dd64713b698b3b8bde163299075adfafe2fcd99fd782893a0c82704eaf49797f7d3c20935131ad4fe1fa7e1ed94f3ca80b983eed640a89dd0c606e86ff30b1184aaf1de5e55d34479d930fa1ba2a5367abc03a8b92ee3c7f0ed9e4db76ff06c68acb2bb1b228064dd6fc9cb7df2333537856a77afa330f8f0b505b571f2a5395cb50ce6acb19ccedee7b1c5d2af0c33eaf9a408e8e487cd901b0424d3157ad145af80571cce3e06b2f2a02a37b7e4a22b582a84830337ec30e81b49c1cb0d6ff18db26d517e1c41a1c0acef119d7ff8bc0e9457613f5a9e30fad48551667ec55115674a1c5c71d04c20fdbc1a96d450bfe0c0a273824bd3f0f2b8994bf47a95e9d0ce255f3ccd4713ea93eed4d5eb840140c2558a24ca2210ec5d23b00da2816d2d3a3262742f59b1ca15a1f90ac944b826a31770dd8bce30aa106a8ad55c7370ccaf159f746baa62c41f0f56770240d8a41edc5803dc18102669f47f93dded14422b715b34262f84ba3947a793c3cc1ef1a84082f0d8b4b0b20966a3d38209d93d7003ac9c4c1eb1f3cbbfa766dbfc6e660abb1e87ce5dc0daa9b022d22e4eea6205569f7633e2276d84223f0c9beee40e70bc8438626600694d20006119debeb36c56f5a0702771902fa326ced613a16499395d5d80f310f8a81cdb84511b7f76b2f0e165a00bf501a230a5448b1e7d31bcda28ef7e4c004ed00ea976b7a8303fed1541e88c58e23d3b95d1ed48773b8616b139ae23f060794fd7cd566151e53740e7bac0cf765f60e6e798850f92f3f1473b9a704015f0dd4a87968de6f7b285875b160e18c3435be260fcf93025043f6962ae75300e50b4d478f941b36d1c9ca673e778ba26e930c6b890b8a8b41fa0e16d53fd4247a0c2832727e0bbedb29f84ea07d205b60c7eb71a161875271b3365d60f98456fd05cbb320f4bc2939107cd69466a78c4f121c2a4751dc68a9b551d621acc7afc8095bc808b6a877df5c49f803a15bfbc5f721895ae6f067d1dfb3875f1cc4af260c78c56e7316d996fb4fa08a367c70a23e60437f855c016293258fd1ca03e04c083b28316ad9f5c20178298af49eb36b004c2612335b502a0b05e50ca20ba0ce043e8f2187df5b62bbd51379cd7ffbe4eeb4ee480189963653cc967154b5dc060d6a881a727ac6b55b235170f7d28935ab74c376a8b5b4cce95544a99e7855480fdccf4e124eac6a5f72162cadf3ba258f159101cc9521e65231ab1d9c44e35f0001e493e7d42a9333b139b320d2074f9e024a8e2525d50726f334cf21625be4013d2d62669714ea3506b83cfbb818d7e9b5d779a68c47f5ba513c83539441cd04287d7b218d26907eb9a73b962c343dfc68e3375618e1109dbec9603b9dc2c2094b0a5f9c8086882e3cb80c2af5cf8fb528ddfb0cc85bdbf54fab39a002eb010cf3d925a51f34f695ee0f1cde9b3ffe47b382a76349ade45806eff53297473c0dbf8ea142e02c2046349d9e3cd23efd76929a7ada269246cd1c0b8aca21508c05f3d8556b65e67579c5cdb71330e050e653d97f86911c72ee7f24a53bdaa92a026e4ab01429e75e5ac0114022ef5933f3d485e6fee786c205481554587a586f0ee635fac45bd8382926d3032f3a3e078328f562c028d6c5f7ed056a96d1759e05efde2905f5558d94956588311d0217b7fc54b147e02892c6ec4b0ff2358eae00a1995f59edf3a325a4c65a221e9c1c7ac795e485a3ce2f178ace51fd2f61bf0500596181f632fe32ea242340a80a0791cd4159063f09f75f9e103fd89868a80177de9b4157ad515296277a8fba36f9e01899bb31eb61b10d6912eabeea9fc00cfb7ecd29a6da1110be6e3f3c832b4200231b5d599e63770253c49862b19e730bb7904b6d6f1ce84acca0e553a7d5826f46784b71c5601ec33766eef3600ff007239d61435757ceb1c36f111a5153bb2508d6cef09f0605fc8295a2fbf0ef120d637312573ec6fd3c128dd4090aee377378099a27e2da78577e25a4ea4c276d01d662752c32b1592420259cfdf65aa3b3d912fa7ed56c94dd957d2d27dc58fa0b6298a6359ee4dc492bfff33921156371a28537d3f87b1c2e6c10f5b7a460f90b3981a59c2276ef99ec8f372c971f10bdc250aad67d4fe6f0a887c50823229702ecf78f20c196392a6f42b00c924264f61d0031a0c17a37a30dc1e5f64fc32e08b7349ed535ee68e6faba1e482f721cd8fc133a2405bc3f1521def4479a9c4e077cdc142b5069bad91f4d9cf3237f18c2eb063df57c8cb05bb9f509b24a268506ba6a6c3ae2bcb4f97f50207c4a6768df3324f273f5084ae94d0c0aa8ce2b9c06a5fc82eea90896cedcc7daa59d96f5cee9723542266ed10bc72dc945be13f80d5474eaff22b6af3e2d7fab8ad84797f1474d01afd382ffb6e5ecfd1f94a1f4005e9b44e4b98316e4cb80e83d9cf8fabca10f6fce0b57df7faf68f249c5ba2e07fea131bc7ab27744230014964ff55b01acdef41d0f5858fc500a2ac1712ef10a269ea91e4741824cec9dbe27fa11b02160d46e9142425787e3d50de9e7d50306cf87ebd5f98fd7e1834ee229956e36f9e4c732d247e56dc66d65fc4be5ea400a31ba08e3c75f38c6faa5862664523ac5b855c40baba22d6345bb800facf75a026adda86144b550d15b2f3880c6fe5d862bb8acae22a1cc57c439dc05b91c7c0e11204f64529ba9d0c1467be24d221e151492b6bf90b54ec55ce067be80536b0154f7804f265de7b8ce9efcb0cfaab0ec148b64fea21e7e230d8dd511d78f7e0b53c9554e6475395b5883be2b15684e637dfcd33b1540ee1d42c434b335182106bc48800665e27254b338c19588b1620f768fea5458bf80f24ff667cc7be356059d6a80810bc2b499324806bb89d501a077520597eaefdebd14462b2912e215027c92cb33cff1dd1e995e6c2b6e7149d9957ecd483e7e35793947df313d01800a52bed35c16b50e20fbf799bbc956ff4da05fa1d9e9f16a629bf10e304f8434068169e04959cb2813a672f9c65ff0806ebc7d33428130e55d1ecb7d76b64e5c0b35df0c330ad2dfa2367a63c9cc8a40e33b07d03d26b59c8a1bb130c4090c1d005bf4159df29e8575a522c7d0c9d3ca6af712012f6a5cf0cd82eaeb4711d3c20cdaa848dff524ae9142fddcd78b1c50001e398162f7cfbd822c55ff5334905d0eb0b3f2f1cacd6756f906a1400f5564fb5764e75aa3ca57d827e16b48d3c03203eead6c3b9db23d9c9ba9555dfb3b7bf51417ac1afced0dcf291d83377cc13b002d7f39484c4ae398ad12d15fd85d73b05572522206b654891de330abb00da5056576aa43b1924989390883350716e8d33ca9fdc33ab250b3b60c5bface8a950e07b85593fd456311a728d73ac32a054482d960e7143c9680301a18464d310308f1af1d823b3c8db2b887567a7956cc795dba7a21b83362a012cfdcc762a86805ce7ee9ae88d32ae5d1b8e9b970633792788f3afca11eb64351e49b77160a250d4e18f05ab2a69037190b80808b8adbfbc603928c4a4be9fe236b0e608f873003399d35f117f277d7594946e2fcd06d6742da1c120e5794d1383e08e220550306c3c92e41f19aa2373255fe04f5798318da420fe88c9ef5e562def50563814a0bd461604574c6fd89ebc751cdba8e995652be02200dce75387277d70a5fedde07dc0c9e293a8167138f054657ffcd7bad8c67d4ce468365646f851ae30566010299b748b9d2d1978757f6104546c7facddd6e141a6342274788cb27258852fa054fe537bd5f70021f4ae1473df685e63bd239e7e671647d16c9b2e39326c2bd02d309ef9fc2845f9ad48dc05abac407a796a095c405c884bac4b4cc76e039420e1c89b4c6ce396093ddc13722068b4f93a719349407ad71bf7904bdc6da73d805e003750832e93e155cbda8f3877209c71cdd2a5d5660a4e5c38a41e287bbd30e592750005b36e3bac4e5658b5c60ee13dfb46a70f1fef1180c840e877bd8480409b48d9d5cfb789914a602b0fbff3a4cbf2be7fb8ad2503eba7745c939b2d908ab6b5e9dd813c58919be30c13c98edf94e28cb210eb1d1d6d913e19a92d8910e58e0d86212cccd5c6d845eabb3e5be459c1530e07116bd2c062325df095e370dd2ec8889544c4954b1712816241808139c1c39206c470ba153cffb74f03f8b0353f25f42d366b5c0a51e826271a4c2db6ee6ed77773dc797fcb869e1d69fc801e6c1e568195aa22865a5cf6b1a6f3f5323c86fcc2fddb689d72e886618d8cc0821b657e9ab47ff9b62db88f7a841ffd794de51b8b0796407442b305ab5950201788c08789960c13455bb1960f79435aef19e2b3def2f3dedd41322ec0253ab0322d2971ed2a5c54299db591c6ae94680a5326ce3a241fd31844f028cd0182a0edc8f285c59c5cb9b1a6bc455463d756d895db5c0995ef520db111d937ed12b0dc8a40a84359a8f9339aaae621c37c529bb08ebed1466968b8c4eff3946ddda05146a907fef84bffdf36f16ec44a6fecce9111fb552789a809f91c771b765510e5d0a350d614063d3247758e5835a5084a3db14853507d5277e2bb148e614c30751ef87886b9c1353b44a693b25160a8decc22c290e5e7e5fb629a4364dafbe0ae03834f7e407b7ca2130096cfda3ba7009164382d13c9ce278799022465424099628e07758bc13a26c7eedc17efa4dc545087f2c1afc8d7945ed4f0565eb6008ded8e5227be03b107a144becf05ee5be5e439053fc76f809a59306409d9eaf07544a4b615cb9ab5e6a844aa43efa137ae743f037926c46a147cc10bd7791ce047b21188f9be6d460dbf2e8b3854ea75772e1d86d6dce54d1c8741382fc42e60e69f278e290daf2e59548bcacd310b425153426ad9b6be2ef1590589a341d190631977493a747a2a10de0029634ad92063a0d8585175ee47617dbe238aeacb1059edc1e75517464d1034e8def8df4763fdc10780a3be6ccb107b53218cfa1580cadceb3a052f4774fe52c1dc638c818710cda72299da8c9ef319de2a1341c570b8593090a25d525d8170624e347ae0b2ffc4b225395aa0f50b6ac64fb3c8ff902e9f97b32a92ef8915443110c52f89f6a999ec77fd4b8e66f4198587cc0df15057956b1089ac5f4e62f7062a80de0bc86f4810e86be56e2150be0347138398f0db2e8a468de4f2e68ebc42a3d56e91505949e2f712de88e193eba0266ff64c30ac56f23b14ada083b1b7706103467c2770a2811725dfe3948b0921e1af1b1600ec3d29a9d93262df58092f441290f31056684afa93268c46248922befaf92f10864a1e59e53f6fea4eb0f0c79163bcd5cd6a7d89b72db0c74802776b47cd9d702d4ae4a08c81c0c45c854271c9bee9d3af2a51853f977610b8dda68d04d7a2109033ebd96bfe29ea1b8069beefa2c8d815683a84c776f9f16eb46c13cac89b5064cbd858a142bdf640afdfc0ace9af3a3c6fb4839a779d723261d7875d79d3a01d9becb60ff8c4fc5ac81946c6777ee3b1889058e20fdeb5fc386a22ea217c80c75a4cbcf11b915bd1adf528287a35b976aa56d0359560a3f52091ff515cd3807ec016b6ab98c1fb8af420473d063599105e0a1e054336f83a41930878b4a9208c9b800b629547275d8601e19ddf6398df907ff94e2d689b6cc4d7405125a8e06ccfd133043d9c1edc53e26287eed0752045d28f747fd2f799007708bfa2f27030551702664a2d3a9f4593a57b1201c9bf507b4a3d63d4e9eb1ab9779e05de8008d0e1647ccb9a916bd2b17ed7e5cdcf15ae19be966a229a9e4eef5e78d8ecb00ca077c7fe59a018a28a1b26856188659737d72eaf671792cac9a939caaeb58009ccdfa69ab08c2f99132bb2eb0da5cf89304d2af77ea7314ed86bafbdf6cc20d169adf66543aa350bba8eeb3a689a7fdd05096af8daecef63a3eecbafe517803980526d3629405733f30ec986330f70c234edec213422c6970e28150cbcde505ec446b86bf218dd4f0289f4627c2c29bb7cf80e0399ab7722b5ed8296503dd0f5857046b6abb20be7b65952ddedd3b2d82814208fa411ca6ca21bf4c85b70b078e14e292c3cc52fbc11283b7a61de388b400415633853ab8cb8cbcb0e4043301b976dce916c8e210aef3a80884f929075e1e65e71e93c004826c24761820840f7b0373159e0dfc73e0c37d6c56c06d3fae5860c0d741d98b233e05cf286ec002627aeadd015ab252e3f1fcac467251138696831390cdf85e6c9de1943d6b9d099d5a9af108a1dedbe471e36a255f2da864eb60ec6d768240ea1bd5bf95aedd0ba27cd49e9cb9863cd984f3f655d78a4dc9dee1613cc6d6c4ca2020d755ed8d017b2b846c061a85c3dffa3646ec0c68540b6da19a592830db3112d75ad39b11080d8f8a50c05e20a4613f816e2f8b7d8326b3d9d223c4ef1f949cd3c903c37603432d81c922444445339a85126166829e28dd8802b3e6d69ca1d2c71b7b4d8a0e39833caa57750c213adf54f53d2db2b80410bf8edd387a2e1e55148d843a260fa0e671dcef15d3a66e65012dcd5b987fcaee20981918da8b2c0bc3734faebe019ab54a84513b1df5f713d47eea719d1bd85067c8ad1767aac18032c57636a1007d2218ab7007237d5e87a3a78e2ab825e55e41ac9dce6380207fb635e34522029873055936a2ff46f1a67afb0ab845407363abbe7b5960266fdc4982c02f2506192a827c88470a2c1e07af2ecf807538ef95fdb3dbafe7ae571ca5d4524b040e887d9122fc6e06d77c6641f663146dfbbfbdec0261bf5116d1d51e3a39dfac09833f08d69d647376d2b326ebcb7952f70699054b80eff57be2497b1269fafb069bffc2c0f8233b16140585ee4d86c6e358b09cf98dae8962ef35c2e1f0bb5c06555d13460a74498574222574b7a3aaf47f12eabec8a75393bb6321595ed93b0848d11c88d486a15d7018c152bcb75c93ab91578b9aa04198377b7e7e32eaa8022de882cb8e02073abd880186368ee50a999faa9365346d4324d59a89e1561800610a3710ae036446c7608912307b19ba274bf00be8f6852343e9c2d581b1e201740a5c3a7843621f7e96cd0eb1b1e5f96c9003bb1593a68b9402873e43d2ec0c0cbe88114b9050b738685dcbe59432120a8d53c78e29a82215f95f7d147d5206ca9d67b3682dd9b840f9b23a860222b61e9bf1a05952d2d9827eddf6aaa4480e51f244e84532af9a52b40de3b2bf103e525434c6d13177247169c7aef54edb0de118e195d1b108bf86549c6eaf5c43492600ebdad3127ada26c80b5e6e8c450ff3553728b243f2af37b728461dc0cddee6b0103546ba11a85226598f30f5ee0c005d282258ae425dee435bedb0a7c868669b233b480bec36f17f85efe76c390ac275c38083d1c23cc0850f051000cbd07592438ecec9f6f8b34e07a0bca1bd097c154e208562eb43fda95d6390f07485b21b440cecd40df72250c9d4159717086e6dd46d51a70c0d9d1ece52359723f93a3372d1e23d2f0d1dc7c70a6cb1c102d297c961134c681672487ebfcffa6f7db1553cf6ef11323c76ea723e0405cd0a41c807ac271e44be9b7d4eed91f36317373ded2583562f2653ca0cbde53d300e7273530befd90d22b681eb60fb2885c1481afecdd9c6671890d2877473e3e00e5bd93ada7b79725ed2f374e15e613bde119a275b041922a4b9956c78ab7331014c537ca508574a9aaff59b8e7754d7dc08adbee1ff3b507d437f7f232533de0ed5d6439df2c4de22c2cf0ed2aba2b265ee6fc58b1bc26a73b0d4f8e72133100da92fbd6531f082f54bd706ee86a84a0c6e354c164064ef65fba5d3f3be052b02ee9bb1a3371c15d9a6c6e7bbc70728307b95ec17f08adcf7ce6c2a5d6e928c0eff6e2c60beea67ac7ec628da351e75c6a9f4c0df5c859a71c1c9fec978c8470ecd4499d29c4d007da67da1fb9fc856f95fe87de9bbd963e41bdc2f6bad211902c25f8b38631431f68338142da599d2d6e687ac0bf2a89c22378b3ac85c0ec107383324e8be63eea9fb70f111166ef97bdbd9e496f46e3b0976e3dc7a9438c60bbd6bf113dc3db276b08eefd93f9929c1ffce226996879cc85c0dc7ee5844a6067156e91fd5651782c99f13a981fd552132d4dc2f8e58f36631347e32f7c1cd0d058ad28eac50436c91d62ee2f1628b0e580df9bfb128fed2ca7d2b389c97020441e43eddca4e2d94f0199bc0ff239599b1862b4bfa079d4463087b414751500e4aff3109b0909b826aad6df455fc4f7d27d92d3e713e9a62b4aa095259382f09d29dc19a0810502879c54a47a463e4eed4461dfb9fea6fa3996f62ac9256fa098fdd5bd675ef2bf0a56662586bd2e8891af10a618873bb4dc392d71d105fd1089975f06004bc19c58a6ed5a3e770fb3debabb5617971a719a4120197b1219905489151a4bae3d3044c1a07481c5785ef9f639298face685a793555c3ec3e00089a06145960c1be57ddb8abaa99262ddff393b6b9e87c23a38a3538793d369609cbc0a317abbd422fc9398ebc43fa645b079674e918772fad0e28297540b5330e217914e23d8caff07c6ac19c62214e0e9a284bd5270853529a577b1d6d74230d8cb101cea1df3f85cf4b6b37423efae95def057633ebedb2dd15ff01164f400eec6f78482bc9b8d502bf987bd7e803e4bddcf56779473f3bafc87143d5c08504ebd79fbae8912a5d06dc8606b87dc834e7d008f65fd8517e4b2e29f9b62a25082200e986c917ef3bc6e51558afb0e1bfe30b317230f7db3f3683842dee6ccc06d0b6853075a9cf2809c1eba3dd4a7428fb9eb847e60e7403048daab2e40aaa02bb61a635e078ac13f2c35e6e0854a8d3a7006f87033047d6a701163fb5cd7d0f8670797c3e6745073ceac6dfd19f38c51c215e780a1fd24085d7632d4d658903f9aa319d9d119a1d98941abb5c756f88c3f4528825ad89254495c5ecc1f33e0f44b98fda383630f76175340e4275bc8616fefe81c2a1c23d1bbf28b2e47bf703c75b2432b46f03457d9e803213f9c74cbd08cd2deb445588457adc05e69d320af00feffd570f3362167e6b0eb43c3ae605349bd1b932e16f26db6d33cf8078051081daaeb75c5ce7de982c6d5088a6061ecb6febf156861c3d50d86c45e60203f256694d2efe896e03d32ed4dfc4051ae2faf0b6c2c3cbeed5213b5dfbfea509756628aa3fc5721696e3b3f12a41daed0c311a292547ad8d7d942b411e1fb5055e4119734a3a520964f0c40848a861817c602781d611aa1586a614e23c698405014d340acbc61734e334850b63b62a5b9b1d58fd9cb9001378708aec332a7e07ee7593d6b699b2e2442188371887d09146c6ff33c8888000e44c28ebf0fd890599a6c343d8c01521a930bfce4ddd3ee632179a0f82199ce0b13da80a30054008309c6dadd9aa1b53a161ef9648e01d3057b6e0f1ffc73ad33d283e0f649adf0877d836de77052cd9228d04829461723ce80fa4b7dc5127be0b1d54ceece7fa0e3aa80c24945a606a2cca53dfea3596318893404fb37c22de34352dec440de908ff22d4c4dc5f0b38be10203b19fc9917387f89de437d1dda8aca8954a1fb8206e6f96f7476c41d9cab00604831e78218cde1b60664ff4519c9eab6e3e3288700bbbc3bf7a3229558c81f43cfeea5f77961255ffd315c2611ac47054309ab56054e0e7aeda601b3883900fce06cd44a762c5f2d902fa344b0bb83cfb88ff95e03b3dd651794add1416723a6ab9e08bc516d887187a55b81c4ae730246cabe510cc8305b218a0b894353399eab8acbc27ab7b9b1d6bff456f9ba7d1b31734b05029529c6a4e2bacb205df7cb2470743e99ba38c80f81daa271715dfe3a16553b0805ec528c024fe30cb3a6c6c77503736bd6fd7289a3d4bf18b90cf63be49499046de776c6ec74d21cc38458822abd12eb2562837f80f44088f8de009ad6d6630b5a9ed56eefd1f0793b6fd7e74cf3d15498b112f024c07a49bcaafffd6386d60f26af5dc8755382aad3862f5d79fd91f907e0cbbfb7cdb964eb8f84d2794d3e07e978520ca1d50e5f48055aeffb745f89d270ae3c5cde1187524de7aa62260d06912978ab0a018ace84d0fd17a2f47133d1c8b17eca7e354d436f50bd93fc6b0e59d1ca1bcb0e6b575e8b70e28d6606aaaeb432ee6e32c8b98d122bb4c650d40cf6e63b51ee2dcdedc59f740127aa5c48e43262fc9bc640ae148d5dd0faca1c0163ec1f07a73ac7377914f980f275fe9d83c9ad2217bbb93d49281580a243d801c57f8ed0f471db63aee674ba687588399cf98fb5858d3c7a678eaf67cfa4f30f523e0ed0c8e1fd595278a5d06d50d6e8fca0e4abed1a712c542390ef4a4c6a02c5bd772acae7ba18d5377fcc52695059b980a30f22f2dbf91b20837ec24b2c0ec1f23393ef30e2d29a6dfec42a060c593b5114bdc4c6d8e06fc5893ab7fbcf087ffaff6ff89db8275716ea40847a17465044e896624e3236ab9143c64ae5d40c0ce0d86e7b91b458b8b3bb7bd5d6c84b8d071c47cb00455e25b9e2c81875ab00047990db02024e78bf8e0c90d06ae473235a39673df66e118a5e194cee56ff09672ef17ee81c54ab0148f6e5da0a19d35dc9dfeeb3b662ddfbd4a01af47065015ec144683f7cea209061c6dd4fb7273f845bdf75312b68af958d161a67afaa0579f053cbaac0286dafd05ef2a1beb3ca08d3915b53022f98f465eb327ee4f209f14a9a2ae9a6a59270be1b2a641ad30bcd81bb7e3e5249073ad4af4d2edc3b00cd7f0809705aaed39bd750c07a95a19d271904995ecdb5aabcea8da27065900efa58056b0bd1253cc47352261b3d8e81e891152be4476e6e15aa9e055d04fa0ad3fe56eab69f80b77d3398a1cac0295f7636214efb7208fa7cf7e797c422a50e9d775d4ee35110b3e18b7c445f211da2ff4bf60bb39f6504194b942e7aa268022b2af67ceb362876044101ff0cf3cdfe9c2749a5541f4c8ab4dd684ab5b843084f4ce3a3f1338be360b82c417e2e41872c9c3bda61e5e30770745f1892320605f12206fd71a77fa262c85c025a69e8ddbb35c66910166418be302f9c853563020d3aa9d87f9e7c6ea120db2efb88979671090bd760d320843fa79a9400e5ef097d66f47e24bd69e71bbfb70062e0ee8e8ceffcd83ecdc2fcd09f121259017603d0a9f3257bd54ac3acab08d3216a33ee84b9bf81ae6723aae4482d84def0980f5a5d4bc02dac35a2e40e14eb33512705e7aa6ccb1b37f2276ecfe138ff33f60c5cd39885ef4bfcd06515ef19fb1a299c00b954b91bbc101dd776233634523603d9cad2719bc6812242dffa91d36969bdb8b982eabd181c4be867b060dc5b1c03b9b308c4a7f90a185f697b22ed9acdae09fd6affb4d11181afa979c184029d0a045f3d42830775cd95cad1b47cbb6bd42b92b00879ee6b99d8aeaf8ed7b7be08ef324a1e2ec5ecdd2305e62ee99ca2b32eb272b68d0da5d639521c77b9fff60ba7e25be3ea7986491834431bcfa6d7d79c0c4be218f0b8c158991814434028055354cad9d1dd80827dc933c5da5f9a8035eb439cd8abeb4bb1d7eefcfeb47901fd613132d898ead285ccade9a4df1014d0d9a16de93deb56e06d07eb96af3d0c5e202757f37e20e67ac9b0a8fc9b79a1cf6020286587a07712e961b2baa7ed041ca848e66ac4f8c05207905cc66dcae725c64db8d5eac403c4fdf53ae8a81904b1fde3ec5d6a415c49ed86bb5642728daf96df2d587fc7ec72173a70806af5073accbfa2fa2763d10e7cda7e2f661f01f9cdcd95c1634ba59528255e7347660bf228dee268d59bca7a0e0034c5a4928f25103ceba362c16b624bbaeee4bb140894f84f9aaf259e956f77b819796bcf891029dfec5657a55d002e4d09215f37046eb168dd33d50bc48cfb5cd2302ba91a41a060f7d3adb3aa0a9bff2128a1690e62e28ebb95eeb0e94a7c9e34275851f126372e92b36da36028f839c92d389009ce59af264cf180a279f6164b2c4be3d304457fa7372c81a7f1ccccb5c258db0b5ca35685a924439b76a9a766df1ced549b731f70af4ed14e2d6903b7cffb7c0761db3c01dcdf7f72e05703ac62880002f91622609615b5150d85bf40d91bdf06 -generate_ring_signature 511e40c694c62adccd1cd78dc759ad6f7e6dfc6f65c233300edd092548e6837b 1f100b5863c7a8cecbca53896f81bfb37129724d1ac0482da5e3c041ff57c7dc 1 25fe770727b3555d648a78a3e962b1bf25f9733c0fffed8837ced7e2b3214a07 805b31538ebb4de57afbd5abba894511ef846fa6c94906865803092d7302fe0a 0 dc36936e89643be02e4859dace1105be15ffb8466014663166684a8d7412c30e114dbfa4a99635fec9ba0ff3a8596e5619ae787eb6c2d375b47379aa4f0e840b -generate_ring_signature fd1acd2eea26d5ea5f10a0b1e26b68e4e89fb9760cb610bcee9a0e6903a25904 984b195e9d1b1019e51a1a00ef3fead39de4a03464838690084b5b1f5b49eb4a 26 bed2706f07b60e6e4ffa986002009646f5ea5fabd1b164bab9644b690893d9e6 a4fb36914b96a15fd407598ec221d6d6a06bad7c21bfd0079e91db6b64048f91 ac5d1a9778bcb740b0a04211516231df150d714a929520183c6dd54153b07eb0 c82a344033f5709ae3398a87d909ae637b24c0b355d25b76a04fd436c0c01488 56bfdeae80cd043158fd30b06ebc4605b4dcafcdf90db2982f1cfeb7e5494ddb f6d0da924cabf77083bc07058088dd8c4074e33c1839d7137b59cd665ed18637 d4897cf986fc87998d75784803bf73a1e9854612b9cabb0d5a91754a9587c83a d0261ce00fb5edefe2cfc4b2f1152a5802e64bd8bed7ca4d5ce7ae8e6be671ad d5d7d51abe48bb01095b73590a322c95d0c2d8aac5a30cd9c071d42fd222bca9 f6fdb08161059d27e267b29730e633e405365951a79a9af34e64b2831510bc5a 5dc9b05fb96aff4ba4d6589ad93d086044a0e6483528706bd65550ed154657e6 a7766d3acd6e9abc9c2925c3d1060214894f6b084d42907bcd8ce486a100348c d681d22b74d9caab9d5f8a39c2072a899fe687e74c04beef4dc50c856eb18407 45151bebf7146e8115b238efdd4a62e9902d8ec3b2d17bee96e177e6d7aa19ed 9f0f1064920ea54f315688deb2138113e19a1ca6ce6401fe778a01749a2a8736 e862feb485334845db565de4af0b91d08cb2e4488553054918a4fcc5cdb99ea7 c07283ca458cd202e47d486a82345801fd8351b6d511a7bb5f4ada80d3c6109a 7ba6f60b39d016bf3ab7752a09d9a9bd72b34af8dfa098fdb1f75e565e5323d4 75a784cfc3245741caf9e5d074b516bdcecc8e27784230f92084850460c6986a 1b852eb3b1840382e80d8beb354567e7d6ac15e5abf7e450358b70f067388d68 9b06b82abcd972ffd5bb38d7c6bda7a4e50e79350d0d22e9ba34231db2c69d6c 0a65a8633f5152b3b3514e838be2c0a6cb63c686f3be5163044812ba73372779 e535860453422af9b12fad4a50fcb1c233b303c4c1cd254923b7594ec0e1e95f 5c70df9727b368bcdf48d910dd9bbe2d113d468e22eddd03bf268c6194f5e46e 934174a3b129c761d997d0df693377e44c5303aeb2036e6b26fb0b6f7c688146 a3a8c856c73b2c20025a9db3d15ed505a5a3cb453c7dd31f318a76fe5098f59f e7def0f0562dfcdf304d8ee8b7483c1292e92a39a2b6616e0763719aeeb4c10f 9 5a1105e836b9adb9a8167aa356fe2d461862d40a352cde54fa810d6638c6790125c2f5349c67f0079a17088d221e20c68b82ce4791ad7b28e20678af05df2a05d8dfb0be4f7e84d8b4e37ab74b4ab4483b3ac7f54a95eebee92507f0a88167083aec084496dd7711a87147498f4a5f44dcb7d51e94c5e8cf6fabde4c5d6c9a0a6b3e82fe56a9a693440aa8e4fb90e418a69f6ba4f4607036398776de633a350f848385fa584069a0ca7d3371f4dcdc8347c2340c0b5d46c2ce41cf40a2723c0bbd31d4b62e05070bb100af9619a8f7a6d7b558221eff679fce79bf00c424310d2fbfdfc09943bcfd63a3d3ea9000bc75212e0ad989f52798e678d254a76c1407b7d9bf3063284211d0e1a85d0b0c416aa35aed76b8bf3e6f7cbd3c45bc98450d588ae58e391014b882e9edc7c17d8ea5b05e4aac46a01e38c1bf2f0d58fbc80e1b9542727f97a6ebef40c67df53a186c5073f08c4e6152584bc6ada4604a4d0e72f791e179f9857c8025c1611b16c3df59c6d130dcd796ce696cbf1e4b93b701eac52257bbe36fc0e42c38bf5faabbaf1a71e1c32612a1d9d6a248f65bf34c0f91d6683d299962e393f91924699612f0b1b40a27e0b6531f887b1ed2c38f28080819f2e02b61ffe71ba47c9dc5719a8346939cba4ca16185e7534a98e6042e0483bc8ade91360f4d4b09b334634ee37903bc4be5fba6276940025ae7c3dda30545329c05e0e7e498a65b60f28b116225920239b8bbd14e4acff95b9fb29c4702b828d747bf415a926f50c07baebd0ccfd1a0486c4bcb14a22e2e78af6f3b4c08440ded45eafed59b2f34a0b670dff0204bb9e804fdfd7125087c160c2894ae08ee3988d48934de925cfd20ea872b1b8f41c386a7d209ab6f70253768ab4027089e9e3f1c6c41e096da56e9e69a11f6baa8a9c074dfcb2bd74ca4278d2f30a90e39a7860251e28d6809a0356f731d471ec64d5fecfe654c0f292f576c0abdba0d0501a8051e56392ebd0516713bf7f582e01053c069919b3b6a0551bb023d77012880f2bc7f2cd1348eca9797c2eb73caf0cd00df1cf93af54d4140ddcb94e406b034283ad10f0894bc3dfd2bc442037d6ca2d4adf4f1e5e60998c4ed44dd650d81b700bfc79bac595313b2db372ca90dc23aba56785d6548b3519323a584e00b606b43d4d188d4e9601869dba4d1a52968f91651f7fb860885245e7936ca770d57c213c4a368b2233d84134d5501535303f5b443b1f337c179b9ff076d57df052a807e2a0e209075dbf9aaab930300b3a31b6da4b5afd03a0388d7fe915113000639f4cd3babc9e1a20ac86c0219ccc0e436a2c1eaa1293e8f9d5ac47fd1a10bf893ec4cec19f53ec55edb7c59094c84303a8ee0ac7abce4704e647e12b46a0878f76fe8be5de656a9aad5415c562462aa81123cd2f506a6a74aa6867f2d610458c7b0e0e5087b22df3c1dacf4518c1aab0081261e95270bb324e55830b04f018faeb5c3aa92bf8dabb2473da76fe917fb0abb2ccf478a870942aa420004740173b7b289866bc91676b6fdbf85734c715a030434a5e67bc5d9eeff05390af80d450a936a4be06560030d2864f578d2ea1aeb74dc1146680eff78da13a135840d791cf014df4dd135127afc4787dcd8a0532df1f61870a51dee0b02817229b00db3d0254b2d99ffce4183ef25e7df5b4cad6ef6868f1d6a1ca1c85498e66cce0e78e133e828283cafb3f85613aa2ba85a3589e5c5397ed02bbb071258a28e0c0032fda768cd5a24b412bbf0adfdb765f033fa02fe59401e1b8d89151edb92790ffe3525253f906d4388966fd65384abfae2f2507cc36f939a3ba7400b718cbb09cb3acbd0a68d01af8296ebf26693bb66d08d5914ac063f2c8edb2813eebf8b0b2917f811e0935ce1dd710adcea39b5eb9f6fa9104b0b22325346a2111156540e37380c8b0dcade1c1403f3d206875a80dc5d999196233be1817ad5ebcedc450e1ac250e639987f79fa2c5867686efe54c22cbaaf4c12c6de5601cfaf9a57370024c00f51a10b616df07ef3efc50bf7bfa2b7d35ef570b5dc27ab37fe6788e008bde4a7ef3ed14d8484e55e01d0586b1e5f54c5ac5b95931efa33dc705c031e0fe5880feeb850efe440c29cb0268cdc84f5562b6f7c53cae1733bd06b21fa7709b06bab60b3950f477b0bfad682b7dbab9e57ee7c7ca2db57913a99f795708703cbb00d30dabfcc760ef7be01d819f08d2fd4cc6a67c1de7c1735bb6c1429820cc368f19c3890aacdbaebb5110739cd2e041b4c27ef2325bc08fd50ff3a7976043ec9c242a355801beb75e1bdaa25c16f0080b8f89ac68289823f1418c07bb208 -generate_ring_signature 5a353b51a4b8e73c0ea6abc2c657c04a77fdff66fc6725ef72f3ad627f1d475c c9900fb41f580b8dbfe112df57f0c06e1ce7fb0130751cd61c16c51120cf2805 31 2082c30f4fb72d624bb617a6a03dd887a65bc0f54d266caf486fbac1aae5442a 0eda2f4ef08a2c05146633a297314ad90dd695da99f4f765c18c22c0c808812c 749d789d269f4a8d46b4af5ddb19cf5ee7f86c54e0c08c6d93691d96a1fb348d edd996b1ac97ae752bb02bdd19976097ddcba82f6039f8115498fbe93cfc09fc b7123f37c63a6ba41ec9b4868113d6f66cbbbaa8d80427ee6964172265fc2e8b bf64bcfb1fd3669ac6664c3e9352e5f30ed3060fd80c97bf43105bb062b9beee 991ec7c190a8f7b9accadd7c2b7ed031fe9c579f343499814c4c4d898855a3f5 420ef41f473736a88f14d32ff6416ee906a4bd91c3a573c9299a4f1c054471f3 236a9c9deb53c00f8668443a9515510ab1e9b968f4be278a94c3bd17c1bf06e8 0d96fc77c34ffa4ef13b0de637055983224a12c1dcd206b283c797aab5e7127b f11026dc54d653154b6f4a9c29d188e9c8d4633605e689426cc3fea64d70b114 04b312d50cda555c47641f109b9f4c6778d9a7c52e2ebd57bbbbd32c459b5c96 b1ba5c22e1a25e62c099f43f3e62f3bd082fe17b2e0334d30b45bddb8290fda6 b69eb8187d101ca4ebb8d84c72592d77f57d04943f3ed6ce93f56bc370b915d8 a0c257d7c690866b100211a0555956726db6192ef9ddb6569b0a452e7764b0ee 99cd8de3617f2f157fcf318c43e96e37df9ed7303c27b039bad46a8ee337b684 e929eebe671159239cb71bd63468e94a2cb90b756400a389b49376f84aa269ce 8f56d7e009725f04ea1ac3521329a1572dbce6fa2098cbe4ed7132fa5ccea342 47e13fc70a64213fe9d3278cd08ca6aeb844bbaf556df1c57d927c30f6d2c75e e4632e8650a1bd477c11dfb50e3c48c77c0bb5ffbcc2de1c41ea85ef50214b7f c8d0e4cac23ffa3b13127de2c01f885d110620b899d6d1ed4fa014c809642284 33873c867e6a07e795638df98011dae680049a28824194c4c7d5924d0f7bc18a 7930cbe3c49a2067ca5a88383454ada7c1529ac75665be86acc0906a63d0b84a 803714d531449220c83f1582d3a0b96462e6a1c4fac0f73bf486fbecfd99a216 6ab2e19ea796cd40a1973def711a3c370edd3afbc452afec3a27375c468232a5 5f58473e62a5a53727b3556b0d537a5210c77effa6ef4c00a00e6ab328f29133 228455d4ae9f1cb40781b45e6bbb26562bc01fa91b81f16f46e018f299454b57 5ebd456bc28a81eb4399fbce59a8c8ce0d765543e7d1dc876ccf2401da38e483 edb57f628c6675f649fb2947c2b7abb9504e00aac31d3be494b258daffff3935 44d94d6c395ebbd88bd008d0ff9802d38e06bac61a71f3afd0c1d6b75f5517cc db1c12682ce582aecb3ae22f6040d3c804d0d000de0b051c8739587850534ecc fa0835dfecc3c5213938d2abc5b71467e1412c22cb0cd2c42011a03f15af9f0f 3 9dae49816cc08d7e5343debc7aadfcc43b9336fa77c3f50164fb0095914f320502f97373ecd95413b640a6daf8f645f4047a6f14bf7d55f71f43b5a2a34041010a1d48469c4c2db26dac14653a8c0dbf62e8de1faf2ddfb8472bdd93a4ad9e0196aef6da8e7b79c4dcd2db4469552691757404754960659c8196a2828f81f30edc8152f941ffd0f56ffc18eef2908a6691cd5c42dd84a0f498e9aa1af533d3027793b5719f4acdb6d051a657db94854ea8f6b25706ca8594c752089ab27a45001f2d77835a244aec4a4afb628555a05691b70844d5ecfaac06d821a4331b9e06c03a5763630c60a0fdef4d5ead75e8e2920345df5eeb6c951bf56118ea89290bcec3768315de6ec13cd8e22ead83c5dbd109e87acdb3968a58b196c60c39cd01d3fa802f993d5ccee0abbcf6eb28e7d2d26936278ed6b03df37c2c2f07a58a017c0482e27fe448fbfa935ae0b71437f4d9cd7184946717034ef6461b76f91d01ade47e7f72473b3e45a7d696836a40cfbf1a7dd0da9d8e892a8cb73d5777480410a9297e34f73966477d68a0feb7f4a81ffd26069d060283a741d2d781ef0b0f2327a8912cc3891ab699f425051afff5ad9ad3ba91ea8c1b87e36b12291ec40f01a0ea626c67a230d20eed54b1fe3abbb6f2bd2d35e6ff4c46d331b2f40852057a72f7b41ffbe695beb17e7a20f00e1dea0fe1a2afa07fccef970967196e3b04d464e4b783a2c0a25d19689ec5b5b139b2b6090e45c5edfec706889e92f1b50c3498ae07c85f1e1c7283814d883c46991e34c3151d59320e5914960795fcfa014260fcee1ff03afa2c602648c40dc789d28ef7af40400b45fcc16692a64e01097db094d04b8d15705ab874e797697919ba212126d7b9da7d53eddd7a527a760fa86492836e0d6c5e9bf48a08a130dfa5b46100ea9d8171b1596c99bc2593e90cef1eec5ecb13c08b306c14d266a1ac7ea7f47b905137a4f8843fb0ba3136770f132f3a700f6b06e5d85e4fd58547f237dbd53f54169334f50647c8a0fae411050c708972328e264468a684010e520954db6a862e0cd04c5bc39bb0f75963b7039576817fc3ffc5c778e17ae48a27df3912878eb226ae83ca043d8fa79c77c202419c7cff8c7deee4a8affd23fcadebca3cfb01200188508786ea7a02916cd507efc44bc6151b3a1a8cc54fee342d7fc418b8580a1a8d479d179165e7ccb88004bc9bb733cc80cdcdd4f663742f468f16f7a17c442f3f6f85f4e83771357dfa05894bd45befdbd9b655342b7702cb80d56335ca0f1bbfcef3322f2637409ceb09d707dbdd67b1a4c39876cd8f18dc863852631165407c0ae99c5a33f3471b5108cdc964c0ec40281c7bb1aee9e94378127ceb62f37c4dc6971443a0ef4d96a30e19d718b58850e6c804eb99a6da5c168f85d6c60dd4e26907a537608791fedd0091f74e8f2d1ec151fda049e9e5c577aae060b1474c2a381852921370d10de00c0f9e429382ee95a9ffe9d0420306e7beb98ed4656d4b1ad3d22fb5f843300203bc06a858e1aed773271e72f4a850bafcf21d363f2c9d24659387a0b51ea1de0a6ae1ae1e20e40a5cb071b62dba844ebdd1fc1416bbc56ba983b180e33165f20f70d73fe21b09df5716c2abdd0662e4a80f74034142e76c0dd3bb391a1b889d01f7fcb058e74a4718b032f3bcf7b4ee17c4c58655243d82a87e8363ecece3c107406cfdb365febaa4038aa1db6565cdacee7cbd0e3d880b6b70a5aeae20b81d09bb8905d9151ae4b90e19078591858d51c9632f892fbfd6b0d17617ab5530b600da6068939617397f6dbd6fa06ca4e874de661f9ec6f25a10cbb8f987a838dd0ca73feb4282f4cff2d2a570dbca198c22eaa965fcdff9a6e61e5474fb4071ab0258efe8bf867277e960f7d020ad1210d35e9a33460689f511ff720e537662ae0160cec6d0764aca454107c2726872716d1b1e2d126323801d9236de01f0051a04f34365920c3ed54390ea0f09e37824b908fc05bdf6d26b511297a9a226aec80fcca2a9487eefb1b5f2c380bd9ba211fb2fbdeda09a4368416dfdfbec0bc7090454b19ff3213000d60eab73cc7cabf7e1b3f9b0ae59c9b7f2989f0e25f264860064df366ed5ffcd3b965f8d15bea72ee3cebd276d2eabd69f8161de5af3405703718c00ecd17178b398602393b77c69eb9de038e0b331accb115912640460880606913eb9f0be2e016fcafb9d83d45ceed83426745813264b6cfb1ccc2b9f90066379ecd48713b7d027b855ec4b07fbe688ec324c9a4b4597e909a438a35d0e008494bdd1cebb90c6ff4e17b2f2e3abb0728e242b01465500db00fdd611ae3e0f4d6f7bd03b45d572a324f082f909c9b19b04558ef905bd8b7cbdbf6583b2a302afe8b946931f90b0c5ef27e2cfee844c5b61aebf4a34d845108bd3d44b7eb90ab7fffcdcb0e07b8514fd882fe60d078b5c0a55fc6408902c730308a344eb3c0caf85d7145a3156a83b8eead61b7684a6e23873a267fcc738250143a41bf91a0c59e0afdd2bc1bcfceb033f3e0bb4d8ced2ca9ada75b3f452ebb9a485d4d39104d8dda253e12c196aca2f14a4d75f324bcec775759760418fb621fdc2ac34a4070ba93e3fd9da8cd598e24e4dd508c016afdd9baa9be70da7f9cfe8534bea4b0d7b23b5e8f89fe15b2a1318e39e129538d8101f49f949ae868155150288835e0f96a901893bdf92f6b0844a57c1f174bf363bad42d805a7f675db48aafe41e4063c9f43f188e5910da3894c20df4966a8f3db7ab2eb97f416fbde38968eacb908 -generate_ring_signature 6b0442ce1572d31322392813b8f2796600e1bd3a7f311cfcd5d18db158df3f83 005e98bbb03be89de4e329438452ac2466eb114dea37227d0583067af043e361 26 d753796ecd8caefcdb6d6eaa0b9f198541cad7e68ac70b436a7c5fa4db496797 6ae845a62d710002eb816996c34cfc5534d1323ec9b664c20151c4571617185d faae0348d8ea4771b6855852bb5fcc60d992a0fd9ccbb4418f9f205fb3eae956 c51b9891e997455b4b220b2f1b57dd8cac0e35489e9f8156cbac6b7792a35f42 eacb4149226b18cf68ef4cf0bf21d09d3e094002482accc00b213d0c32010d25 da150b24184ad0285d59eade87dcb51e73222301f46a0b8cff4007576858e11b 731a7cfd4b0bfc6824e2b5cb6e3fd66a77981d707259a9d2dd332168310e0128 b8ed9d7e6d9db63cd5dd9f5effb80843648c18a5c63c32b67ef6232240707fcc e611578ee243322c9d2e3210f57abd215388bb1e79319fccea9944dc8640e294 410acc54a3f9762e069471c4686a7e9e3e92c61811900962d6ca2400188a6ddb 87d32d08f2338c3da64bfbdba28f6b75d46dc4ad7fa2f018f033d09b608871b1 bd0219ef9562064deaed5e8d494ab9a22c32f468c5573258d6f8963c5402c62d a56e1100ea7ada74f25f46b738a3101732b7fb2a6a24e860fc42a122cb295603 b5c1a4655e455a01e68e3f6002e6aeaf2ce8e84d610c5e4542fe2d3100a01df6 6d324750ad63ef0f6be7ba47418601b0a5b5c1317d151a9449c53d27425131fa 46deeb14d5108ae9456f2b8375dee122dd93f8c03fb326866e6409586b44c7a0 ff9057e3940c9016f2dc47f986425e3d4f7f751ef2f54ee3aa1aae85a2f32460 9598db5a457095e3d02ea5c1ea0c56f8d43a2febcc64a089907219992cc8e4e5 afba70859ebbf598a74ae65fcd63fce51abdd43b5b6485bc8dcf9a2eeff66d7f f0d872a622b64de9d6f5db16745b7b7dcbce78595217317b73637e9c253cced1 c244cc03d464699ce9f3c9fcebc80a8131104d03f2fff938dfa1f76dcaeb6ec2 ca824fa57901f10a1ad1fa6c51a444064870adb920046153246b60176229569c 2ca019b674ed3157c84475298abad0f6b62dc1cda2a11a92fab49f9743567a24 68903516c9127c4e405d1583738f2329755d88724995901269931006aed07600 0f8e7fb6d91889ad5a0aa80048a6a1d941f666e2f5c9ef5b322bd3729de419a9 6f87fe92f681328fca521963539878521e1549077f5851e04b200e3e5d9859fb e4887420585b1f8449d75da7de40507abcae71e73652b880ecfab2d43a9fad0f 20 48eeba9323ead91ffff98825d5afb48f06aeda7e3605a110cf7339870c40cb0dce84f142523dace04ae8e348ab6bc515368c75d98fac07b1e6a8453b8c095a0cffe2216fa5cb4094a6aa0a801685c2f8cfe72c76b5c66e58d0ee76f31bc6b9064406e50afbd47430d221ac8a29bfb4c14cf39b6c11910da53fab5b4323f85d0ff08680942e49fb440b9687a5fc263f062e5c621a9b32e4423dd1f4f36a06670cd00a19e153725eedc6907075eca47bd4517db1145cb83c8ed92997f660e93503522fb42024faafcbb31e6c0511b3dac93891ad279b5ba89f0a57a737605dc501879d7565254fe3597fc0656dff5d6324e096619cc115bf0b76c560f4d2d3f20770a7696433c5eedcd7005bab0c21cba403498fd292f1caaf7cf5c3de0b567803a9af176d2007a816d9813d7d9616139d1c09130c9beaa5e98fa848a12e29c80c0dad21d35a9598c222e69d10e0f393af7412172e51a9386de3295ee52e96fd0fc0226411098a45fde11bd0fc41b45a3cdd54dbd97c41d834ea6198631c6a210fb83676c3b4a95a9e83aa683ae5c25e218b4b5785be5f4d58122ec2816cd45e011d91f240222313522bb32254e551f7c625fe0df9db32c82aec85ba6305ba5607c4b77ff8a888cc2ddddb64b40ee79f1e1931e9f6426d94bae170c1f776173d067e265fb852862850d33c9d89b32a1682476b5bf162d9ee9c1ee7451dd9ed0805033cad7c8f3b1387f65584c39943836697f2301549c266ea89360644a63c0407c8f0ec0872e8c80987acf7f8eb9e78ecdefe8bbcd13ccd4b05f646c83e5f920775cb0ee4a89544d321152257593d7dbc7fb7097726515ee3f489311581e2fc03c5817e265f3c2cbc0a832e2fc28296774e05afcb6054e577f0c2b9252064f70050281dce0a292a7584b5ff7319c0a2302aed675e19d563e58f877701338daa0b25ce90fec48aac5209a7abc16863b2f5aa524765c0c533d8cf3130091c6ea80ad89c9a1853b6666761b47d3645ff484d7e6738a30a5aa447dbd9adcf133b3c049d692676cc4b31747a366384153b344923b86af65678d431466df7e99e05b30fc00bfbfae6c59384f6d66932c17e8dd08bcb4ccb40d1b5ef2bf15e0882a18f0358a500322cb5cc090285e04f7288e8c1ba7d1abb671844ee94473f0ac3cbdf04b591e857a0dde0838f644e5d484648950d9f0608209985e00c9cc54abad114061dc756f4cdcec5aa4cf6bbd2d5c61c5617576f7a150666ba72085d514452d904969f34b365f4530905230b56701451a0b59deb3b8dd6d7e729c7611bd0edd10bb79e2205e13ea452c628863e24a5379ab87290e38160b6e4de1c892ac90be60324ed366deb2c2daaf34acb06d559d2d7140d6a6fffeac747d515cbcdd80533085642485b6d5cf9699241d2508c0742d60417a72cb0df67073b9eb7c123b1cd05bbf19d2040cf53a1e3fee8a59bfece63cf4c785b878c91ba8d0631c3b208410fb00a0c2b98b17355124413ccdb9abf233fe59b500dbf33e0223993c09256650ea4c98001143a3ed673bddf6ff5d8c528b8d62308192feb6f245c621321b31d0a5514565a641b47eec3addc16dc0232d2303872dd32bf9b8ec816af485739b508f6b76554859d06c28cc3a59e1686200e35f6355a712267f50ed7cece68288a044a18900d5666e21c1a72da15de670d343840418816000d0d0eb4c12b669ea001ba6932e13a7d890958f2bea856a65a8459441bed717469877be524b491e6fc0f12b57113869c8549c916b42bcedcddbfc583303795a958314a5d907ba55ae104c41f62c7e7cf93bc4da7dd38a79ac37902b6cda0e99fdfab9eaaadd00ffa5c0924800de1b3d90d2d99ed11b8f2acebca0ca564bd75aba22bdbbfe24d953f3409700377acfe8b29b4d7ce47e7f08f24694aacb7e01ec06baafcb56b316300700b1a0a1a4fcab979f90b5359fa7cbea4bf256f9f0e1dc489d71a506e54fd45120acdddf09521a9ca4bc1969800b6d158c0ef6644222ea2f540b86f2de1ace5bb0dd9c919a0a5faa9b8668af8ee14adf4d1152098309476e786e97404a0f7374308c228688555f902f5c64fbf8625219ae30ac67629abe6951bbcd1947a780f6b0a7b5cafa921804d37b6b1bcf354d6101cdf9f293252c304c038da48d58604fe00622498728d3310e61bd4e9141e39645f27f69bee1392c1b8ebb4469b4a5d890ebdb3cd201fe6d8be899c07d4a3a51d1adcb749ce32977f1eabec0554d9487d0914fbad773fac6da9764c46e3294df66a85d49a5caf7a55c54da0b61df4c1a207f319c4971b6a399f3c28bb6a17554c611eed476ad070c0f06d38f86a8079c903 -generate_ring_signature fa878e98be7bf61dc3b35153e4ab01e1c444b109729d4428288fa17dd1277318 c711a0a27fef0f9531030c710cc93fcd675120c2b44fda635fc3fe58ce68d5ac 2 8705fbfb8783ab5987596b73b4d1ff0156795c951e0c58b8091724678d9d4174 aeccdc9ec7f198a2f4824e33ed96d40215f9b22fd69cd2f0f84546093fa44c67 128378ed23f8edc0dd5305fa3e39996706a4c77d63b5ffda8b294b057c3c580e 0 dd67c185b6380c5d6c92a7aa559a730dc1f370abefedcece38423a448c30da0a90733701e1b0d64d8be75f74f35b65a23dfd3aa9ed6345fb781c462ff556e601b473d16e2b2f10a5215bf12b67db1f1af8af9c6af25ccf62766dca726bf80706cdb829af6cddfe770d7220865a1c5d6c931ccc1ea698c577daaccfc36a46de02 -generate_ring_signature 4dc38ce91c25bd6cb03ed5c0900c0b3a3d171ecd3f441d8bf38218214082d784 4279f878215bf5455ef8c99444b6a8cf0484d54010b0d1e8fe7adfe97af85f10 114 bd939bb5f29b32bd0b9b884328cb04da64b7a77b9d0f1975a42d74c06222906b bd64c1eef3f01b60b4c60765795c12d9d1e67df40fee313bc7b0cd7d769bb669 85917e8366e4bb6557e7e3d6d0851f0bb72cdb2f5605cf80287dc503333cb4ff ae20b8d284cc742818c38e284160f244b157b868fbb088f6125b8d039623d282 67676a9bcf5d35194deed9e14a41c23c81f5e96a82ec4aa28d9c54574c27cae1 c57610fd10ed400b939d00134aede8e719c8e4a8abcd18ff1b32f7fc96b9c937 494be2c5d57bab3e0e2df1715d4ab09912124a8eb65d44745b6ed4bd50718f10 d89243a7661e6cf3d7ef223ad3eac26916c71f9337e02d56db8bb710a41dca38 bedd21b55f596d4fc3ca8577ab48af6d0e4caf878bdbc81c61368799da91b655 c18ae2c77b4f87e07ed8564e9eec44a7fa841b9deaf425ea1be3ea7c800cfb94 789dbe3cb82cb86630f62da491b353336731b43ecacd9d76841917db6e265b50 d983f5d7e4980efdc335a6eef84da9d194aea20eadaa2b1cb2a2dcf6e510cb24 3fe08f0aa6a0bd5ffacc337d8cfb3ffa5ed8b1ad2717d0d51b929d9a33806790 686623a9f75270dacfeb22784afb28c0fea864434b32fc76a7c7908c378f1e8a eb2af211b6ec6d3a7583c0a264a4de602ebef841008dca26e06a659e933994f5 1e02ac91f995e252099050a2f9bf0a107500f689fc785f1f9d221deff9354e50 526b8e6185f40078ccdf886172921d276549dfbdd3a0cd27fc65f172d3bdc2a0 8e3d39b85ed3a05da76c47c8f70b76020002823548f8d45fdc27df8e6852af9d dc0915a34b7411206b4694438d629c17f84c2c07aa745284b1e84133765b88b2 6f1070b084301889189f7db4aefba51bbf037bee8dbdb086ddba9e9fa316502d 8cc4b03df9b06722f19ee747f5e4adb7b6a5ba3f9473bb33e98f06afca0155ff 7913b5b2a3371c554ee75295492f120e4fcc1761a2e90c163800e0bc8566530f 62a6ae54a6ad56bf2cc867132bdc8ed65858367d7ad38ffba12b25bc2f44a923 afd37d8701510ff65f36a4089d28a116e923640d83cd013c9ce2bd232f689cf6 d2b980b075ad35c7080194272558a004747fa3d4f54d52e54d7eb15a55fe47b5 09dde581825bb3e27bf96127461b67b50bb5258077ab5773544f29fdaa4cc48a d5e555016be782538646c00925362d280850ecf85d9c1975254025187739a0b4 099ec3a3c0b2448c583d9279b8df8d6f9d95a524d7ce35e5ad796619559a3c6a b1785018039ed1f0e47624cb17cba05a29daa6af97847e985c77b5de9e2f90b6 4d1d4f2af470f9e5c21fdf9985312b217dc866cd3c99798322d4332a75ed3f83 61bac09466cc764cd2549229e63b21ec0e00125211c18d2e784c027edd279198 e401492fd7498a1281617bbb9750fa4dea2f7ac1c05d4601d7b4d6f93f05e05b 4b50fa467dd7bf98cfdfa98b87a924aa5122b6b09fa19e2c6b1aa82e4f109d71 e4c8c681e9f7f7f0607ba801fa44816a802e81e8fc42faa343d0c0ebfe8e3e78 8a0e62086feaa8a6259ce3a21c4063fd4cc807c1beb371372f45e7aef1493123 fa7a5f9e71d90eb5ba18bdb7a9337243ba0d8735c4721bb9b65be880ac4ec6e7 8c89e1ad8f42ee19016d782eb7489ccb70b72be6ed9bc3861647bd742fba528f 339678b140b4c32ab322905eec017d40d039ed7e02c9ae57c68d2fa7e8584626 6c2ceb1dfd2d66f045bd54bfd7e5908a57bdd5216b7b17ea808e479c0e4d7d22 f36e853ea751ae307fdbe0837663f823106aded803139620a9cbf8d65d85c1ca 1514c9820ad846074be5e94a1a460efdd832703bab36dad44b04f6723e6b983f 4734b3ad5971011990b5ef237b152c24a493f304b337f556afba97607746fd81 8cb350acbb7d6e8c4da9dd936645a8750b052a29414c450a9bc886f1ee58bc9b 90f095ac9e66ecd8c321797c8cac0b6ead2328db02968466cb2a813a14b97b63 b6ca5b532fb55b28dd5788905f16a4c017617e45bd9b59ddb916a910c004c906 2acfe7532da865b1eb9f8ac6d344f535c2dc9e6f38be192986a02ff8968af635 e656d87021f86642a2f147b58869913e1721e8c8320ab77acdf32775ae4b816d 45847b41ac23724e860ede8d7fabed5982702ce646484b7c008a603b48b08a4d 6d0c71b58f122a7c9e6f4a71c0a8ef6ab1d598d67118486fb704c4d1fa8adc99 abc2b523b9f0111245bfa413450546125e29293df013aaf0783cfda5db038302 8649564fc912d27de342dec94c8dee22da9d5953045463bcecd0cf111a2e19e4 7ebc13266c2d167736b1fe30b6447f51d915a09cedccaf68726bc2dfde668816 dfe2f28066a7b2489f85153ad3fe8c42301efc3792e55764241f0377b1df59ae 53337434326613e0a7e86c87effea7123084637fd9ec488d6a5cf79678418d71 2fd4edb314b9ed004f702e97673312cd247ca6cec9dfdcae83ad266cf70d70ef 63627d0e850e46d81bcc136ad2aa095ad1a451f2d40d0954b5b9a99009c9c723 22d3231a1d7e59481188324c8a300004065010feb4408ae9bb8c5a6028099005 00452956c834dc93e022257d4ccf84a981262c21e5b0e364d606e5b8b35ef10b 8e4624bafddf2d0659dd9568425153ce7249d66dd796ae9d9a3958c207ff621e 18e20d7be6b02026da91d6a20212fb83271ca5c0e41d31f7742f9e5d7c9f75c8 0321996b983d708e35a7062ec8de17bdb8eed2aa44a33da0d2d1fdad633641ce bd8512058818d365f2a894bbae3fb48a8d8f683cb5b7ea86c20876fbcfacfd4a aa8206c4c30cae28a9539f34a06071b321f3bfbb1192890beb6cfcea066224b0 208e753a3e2ad56bc88aaeda6673781d74c4adea302c24bb430bc9d2ad99ffeb bd60707aab510cfe9aea55674eab81065c4481d823653bfdf4ceed5aa4cbd2fd d73cf31ffb213b78260d22a47f44376c188231f066635944f43b5ae6c487fdfc 04acf87b977a025d7aed687524a1612b4ec58f042f570dd537e2b7bf1f374819 b67f9de08052c5fb3a0b62ccb6ba87ea75cd0ceaa251708b40844267f6b014e2 1ba734db12fcc7ec682fd0ba5e50f8e201f88bacb180be02ca298bc501a04334 36d7d9519faf0b07448f93704963f2d5be500feaa60567408b62c2feee1abf63 fb64b38df7b1c5c931251663722e1bbcf64aeb53da9d97989e62eef5b3d33185 9934ed813d8e4ff4b96ea8691f2a73701443643065025a46b8d0ffed57deedcf ab16da45b8ff0732acd013c1a3574d7a76d4f8c510dc7dddf54723f8bc9235d0 cb6174b0170ef65b191639424a1d18c67ecb6366d5e789d006718ff192b74e31 5e832d1026ceafa167e47f8e846d6f9fe37d337fcd63cfcb0327ae049c79c253 7495dff8ec7290d6672d5ee2f5bef600fe0b573dd51d4d356dc3093801b1db78 218cd5ae20837d084034d5de568343e729c8c38c4c1aa9a7129eadc0b155b2b7 9466ad947fce451cf7fadbe9aaa5c7b6084f72b3421abe042121d6947579543f d221ba32ba6279137c88c55d22a192535e263d14033e775ef45eea9c076d0dc1 c428cfb99f6446666b862996418db4a552bb045ed8d8fcfd2b08deae775398af 8769dc3e2d1ad107d4f0d96cb5823602daf842cb159238809a9088752d340ca7 4083c2735f5c674f4896893952c90d275c6d28ec96961b36e75164b42d63469f c1f161d41107d3e258a1f77985876fcf02f05d98183ad77c5c943944024c4ce4 7e5fc3484d81d4f393f6fafe0dd4357ce73d08b6db85aa6d4a5dfdf61586f9ca 5c39e84b62a9dbcbe60d5625491f3cd44621d990ee50b74b7b3a7862057c7c48 34383e8918f547536c4a41cc91b2f02f642905325140b52f53589bba81f16383 1cddf5fc7808b6466f75be06c1b19b90025a85e241a44512e65a94b275058179 141df5ff0158ea408546d801ba6f0c6af9bcdefdf23ac134cf932835091c2f22 d476b262891e7be025046acc191b5950f598d26b940d883be486e5032cb31445 522d85cc71685afa492a62c6f4508207849af1987a7116cfc90e4c19ebe400df 1d1f78f69b6744335233e84495b0511e32d3c720e2373bf669c27a4990668e74 905d0a24578ba9bd1bc7b3686d1b08848b470ab12f4956824cf3b0fcf056c0fb 8a3768805b5a09ebc505e5c0d4242b617adb9e0f3865199bbd2a7be7579024c2 e7c80d659dbdd822a62c5a2b5580932a55c9baa6dc0818e8ae5467869f87375b 1b32d833d664cf3338c60fa3205c119e1ddc8c9cbeb44702459a97512575c832 12abc274c96f4ad4062309fa3060d05df560ec075cb78a80251cecacc9000a8e a01aff1dc079be3335a3efec1cb3be3eaffb22080688a4fea7ef634f8b1e5bac 1f4f6d983fe92cbac643d9a027741439190bd7d911d90c50a86bde27d8f4dabd 5d82c7c2e907bc4f46744c8bf1d8e441c21e28d3e194ad9acabad465022496bc 8177edd83f79988face73a00ead13d6c0ecb1db4f365b2e3f9568ad58cd8c641 2ef4116bf37982032ec32ceb4e1415331f4fb988957d113425ae1268975157b7 58a2d5b81c539077063bb9d470cc5aaa3f5014295ea7790b6bb002ba368ec551 452b24765b3f19045dd8c1b37e16823a6ea3c91d9500709ea0aa83880ba159f1 2e967b6d4aff868718cb1a7b26f50384b1012eb02313e8947d8e9a7c52d28a7e f160277b45b9b94f4004260a2dd9b2682f13081a1490cb873207ff96874d296a 6b9995916945798f3a838a968e36a030e64a240737da517d6c29fcb56e2e033a 3d3a67122086e15daa676f97a1f704008dbc47084ddd75bff2d52de83ef44377 e2c2c8614448e6b57b2bff177ce01d466211ec6cc80e26e9c4a9238657b13932 240762e3135c801ffc2df0714bc22bac52f23089d3698fa14c4f9dfd1eb09377 1b120c957aea684c4babc04fd213fcf10b20f56f8873a808dd707982c73b349b 468c72e026931e245e55e916ac2d0235c52e4255826f113b35c9857e171b2901 4dbd62c32aa0b9fa0cbb9c7f8174387957f23d2b7cf1386f39400b48f40802c8 a3d0a864f3226f810e69cfa3c6fe0e2967af9d69f12d39f85ce0c2c43e6310d3 3e7bf7715c43237de8eee1de54dc56b73f8e7982b30c1f93bfecedd49be9adbe 00ee10408bd89c7576d8e154cac0c7f743aad43ec26fe575f3a005ffadf84606 44 773565da4a3859b090cf797f6b9f55ec17c70d9e83a65139c8ffde9c6bbc9d03cb4b71fdd7f479cfa769f6e3961ea4ca32cf4414604503c67b7be5d446fc830f114eafec2a04b10e3caf9396fcf3ea4a2f1d44873e722c06ca4a8ce81d141a0097f11d8d59dc5830649d88955c935e46adc91d708660a86bbbbe92336e7de00c31e87e2b3e1cc19bd4f2a33c3220aac13b2dff8d6a7edcf053e15b6406ca850885de73773418af682169ab5ce64e38b26f857e5bb40c78291b54c135a2109a0532f11fc7c2a2129adfc95ec29d51b3cbb467c07c2b305967dbc706fb5234c906064835c101b9dc8979781a726d5ae1ef4bac70baea83e4123607b62f211c02034100198be159e388106ff6422e4bf86d848372efe96edd2e908c04608a2b0e0c632541120e687a61e09a578e22a4d8b1fb0ac2580eab0abf9506f201664a660651fcbf78343a381e32542a9d3941d6b8031d54f199ce1e335a09270b64c49c0996fd57bb76125ed9485ea4329db44e14b6a40a681c8dfaec7f02eae7e8b1ef09dcbd3b989708262c9040da9028dfc155539ff4d2ffa04bf9607acd2b2fb90d0406a0428040b66200ccf24a592b336c826451112f38f02e38084c45e493d6ca0206c02b5b9717fd0978cb67d7d1c1552f77bb6d93856a34d9f73927c676d2e101b14f73c1545d9545c9b86df8319a43184f2c2e6a69816513daa48d5828d8c5075970eccbdcbe097eb283e8f8f9ac19b14858e0ac3b06fa93b527abdd112b710d3d02d2180c059eca017074f028a41b05fe524facf1be50f76086a3ada595080da234098bfe8dd2a12da4911640c6e60efe88c2220cd6492cfc171b0d2cad7308573787bb1202eb29147469327cacf7baebe2eceda04a04ca1a48720ff07baa0662fef238efbcff9a476bdc3a4ba1400372b0e810e5318a70742edd6e9f3c010377881f223361abac93f83fe81409bb34bad302a77e5795ff8e250b3dfffa930fe7efb5281a308311e451117da110122875baf57df125cc4cbb1fd3bb4a82ff0fb4d2f6dc29a6fe3bf1fa6605eca688ae2a84f6fa8890b7723538b3b0d7a9fb0dac982411ec36e0f4cf08029451e49a070c1011c2a389b62cb34d38dec6c8fa0ea42edc071ce287823f46715eba6593eeeae29a09187c06f4b05fb08b4243e30fceff503a64495657f4ee6e41e34cc709c23345ae15192d0c06d1da5d0796eb0f09e1d0af9748d228ad92dd40103196058379b4a74f0717f91b5f652efc27a10dbccb7f58c52db90ffd492c2167b313475ba77ee1843cac82a8444894caebf60fe50ecc08ff21469a59614c94e7969778838802118ee6ca04f41a0b705a36270427a144c90a6c2b940474cf36a9c5091d02f874896b4add80a30eb1b14b98d7021ec467175e4a46ec0eaf7e457f14e5b038d2dbf588889c6ff1b7f9c916b25907578eef80059061a7e76a963b8491e04a5efec82faeb6d1814718d6a4d0cee6054bcb9e38c56184d4f9e8706990c8bafdd629665f8ea07bb04d5c0da13b77ff09e6fcccca8302404c856ce8ff37c14538341417e2b01d37be4024b9391c028707780ab076e20724ca06a22736582e7af8732d664ca1956f13560e61d26f9b030f9525f63b1e5145e69d32f42cd319ba2f8321063c70e43382bec71ae32beeef061b49302745a6a760c06d7c56960731030176969d8a5afa4bf0e61305c7ec640b543b08eb2be0f4ec11e0cdc3cf5440919bd008f662491c23a800d5c7f975f8031a2c5d01965b8e9181711134acae0fc5a990b1442c12172b5cb6a8de5a7a2d045ed2b243ae5e29b2cbb7fee274908fe8eaa38f3ab7d1e13d6701a865a7b26c02ee00778c75133414cbcd145c94ccd18bee177db18e367797748ced49b714690ce5670c604f513523fb8c32226f0da3b1973ba10f65effe90c7cb7b32ad727c013e1f02fff9d1d78fdec655c54f90b9ac7c7d0e6db53c2520a7495a5e8fc2680f637db162b2ea027e30dfe9e7e1d81483c8a150d0cd45fb1d9d9d0d26a148df01913bff51b838a2bc16e8e0bf9bd2326bc46e04d7dadb735c569a0940e472800bc0cb5a5d38dc315a0c4ad9c700ac7c169e4d70f9c94ab042c7dacbb898802e0ba4099ffd38278d3b41c3db53861cb1c90761bd2f012673550458dcfc97718205237b5bade7dc123675f6050fc39f1e0bb4b160b28fdca716da422f6843ae57091588f20fe7e450be69ac4ed9058f7241b4af10db7993a09632c67e382b1d5c01634be89f7c0786fcdb7b9289f26653abd2b7e469e8e3a6f8062256e24364ba01d32210c0022bb698c80c1eb0ebd0c3de92b73a7a573cd7e80fd010267568c803154be7029146c88a8ec6b35f54e440ab070e23624d974a70bc1a5b2b9a644808393487a0be253ab66dcfd39465584459885831b861639fab641041a31d5f9c0a6a693e39c0297e0cc018121d2566b21c50c26b0a8b6f23abf0223c1e999bd008983a4b03d4d9102dc23f064d89c36036cf0cc088175578194dbdf6952f3eed0a6b815059c6bba320b6769995214db226e66a462ab6a79dc3b4db81358b39500df4de9abcedd5f87dd6f7abde0d76309332246c749fdb7197e6d6bf840ef80d08f8f328410b4ee3975a84afed09c870e005dd9dc1e94acb0831379cb499725902a9c122315e7d6b00196b41913062da087e348b2c26374884ba251e0e2ad60e0df94255ccef66b110a2aab17e3c98ff45f224c96907980d0d07471a6ec8f93a016031104c3baa75eab04541de26924526b0d25685c0d3f315f098c767781e8509d76a55a74e95cbf8bf29c8cb1d83ab4acad61d9bf1bc01b8b6bbe32498262f0f1dc16c88f572b141f6e102accb2e99de44cbcc209719efcbe4b158fefc144306254a2e2e64e35e46985ac344106d34443690308bb005a8cec5d53eab3319f005811b42759519c9eb05ba2ba925fda5af91453fc08be0cb357a09da2f54985608f6e0cde38d0d37a6792842310a171cc88593c80ea20351f94ea5a47cc5ea74038678e8c347f4489a21556072c065a2315948690a0dda014e7004445de46092024553aa6866e79a60cb62236387896e6ba7546ca4b3cf395d6bf5af57ada4fc06f18da3ed28e209d5206d41d598fb919f50487bf43df18d04bcc7a26cee111d0684ca68ddc9d6909b10a109f2aee4490e8f14ffe0ffd1cb0ca4f6352adf55300d6c3e859a0c8ea2ed9e02f80eba00470ceb2e06f755afd931e2aa85da7a667a0e65dac2a62be8d9e408bb93374984a7779a9a4fba74a50f61022e5777cdc75c0d7d92492cb03c3279a8298441f1b66349a932d6fb10988e1b45f368c6c8429800b05f226c00253fb1e89f98f91a5794f691b6be46ed61ef567cd5d46de6bdac069d6e4548a431d948c45e5f2ca7ec5892071155d863242a8665978e65d7dc450ff09907ce69d1fb35e69b95a79d65d547c5a10740410a435819e33ace8c446205434338b41ff680da6def62effba476569d6b1bea2c7c9eaab4c0f40cbfb5fe0aed99e352cb5e6e4ee528955e3bac1afbd289a248239b48e40e4a043d5023080a97a65caad2864c9f2668c4901b91490ec652f941e3d4e7301bb195bbabb37b0af0c9f1de155ae7027ee74d50f09e9b847986475118e7f38386d8f95485c319041571a96cdbd154635e07ee373d232622feee5c29505b6cf34ddc6ec0d07864093bc17c3c916d9bf3ece75c442dbc17f604163ad954855a2b7a52882e12bd660d8fb541c380f5701f2825b3b625a9267cb5d83fbbb7668cdb4f9a61bc38050c0b7bdb1d8af6eaf416d8e0e04b39166ed35725f6964e5cbc636b1a68b1f2292c0a3c28c9bf6def90ac0803ef9754ab51bb1626e9faea62bb45d744b3fd922a7c0dbe9919beb2af315f2a71257024b6e275106577a4f119fd992c8508277fd7ca063aa625771bd55eb3c0a0e372b66e9383b39d9aba38fb189760c67fbbcb5f040e88c8e157ac8b216a5234876184834a096c078fee276fe72fb25168b73dd3a40edcc7547c1f1af96513ad2d2dc7e063bf6c522af0c66f255fdf9ecceb39c11404393141dc34494eac4a57133d1774c5562156e16ef72597844f29245e3bc7c000b1de547530400713a419b488a858df36d891811996adf2bec1bae4478a3fbb03f56124b242d8151bfa2d3c6a7fafa1d080085abe970dab12b7b0b46041e7fb0ac61d5347e800d7f60170d87c52d4725dede237ac9a727ffedcda440582ee2d0e8db028b41d2826a7ab92ed94b38676a0c4dd5c5e5fa90ae7af147d51472aef03619a831f45222afbc2f6923654da58761a1391f3ed4340a52fdd27d888f56a04dc2826e97816250980a323a8daacbe3588b659b72300d2b3055cf8abe4a58b025685ad2242edbd8873fa943ddcae7893a0053a65194051608af469990d7d200bff7dabe5b21778c2fbeadfcf69d1e72ca0d72d206d8e900473dc7b87af01b1065d78729023b27b337a40cc9f9362752bc26cc5da0adb1c459c0909416f59410680c58cc5f9fd0191ba2123ab3a8c4babdfc51f583b8fb87d2b005e894bfec1064ba0fb53ccef06ba46bfdea53e91e6d15e7ca803f5a970af16c8a3bf89585c0d5776e7c067d781f0736ae450b2d3e165abcfb618925b70fa58d3351e71131a0b14f480aa3c731cf4af63ccde43a52b3f680035858dc92771eabfc68c5c65e303651c58c6ec832e8941624f70b20a64be8ef7dd817cf73cda62ac1bd39b0c0a07994ff2744903e6b3e5f397044f9a28407f00f500ee2f14d3fc243c7bc12bd30cad1eab536edd21631d4696f583e93eb9e2262c135e8b1b4f446fe54e1ef4fb0b232dbc0c9135ef0e8b0ef03107455fd8409687ee092008021a0093ab82c6670db200bc273f53e6acfe365950904aa5499e746ac9b88a652f7e9f28470d6764004909c2d67918daa144b3126ae67ffc63b6fa5146aecdaf3a5c407ad109d0ee05ee1ccf76821d57ee5ae0eb4770a453e6d1235e2a895f5b74078d0decb9f71a07adf04fd1254c0c8fbba5dd570370409294363fa7c0ad69fb0140fd4f7f1fa30bceb64372bc7e373a982d315e6a06bbd0c85d7eb2f760986d934e83090b21f60f05a9bbc0d6290e02c4f97e149c7d764775faee252b6ea57a122bbf6c1d7cfb0c665707647ff2db2321ad92193519e2dc670495a4b1bc13ebc997fb5ce93aff0140aa59bbb390378beeeaf056e2fdb84d03e8b6e11b0d320a4a6316de60c4780faaa00447a77e2f40b1d0596f564101f81ecd6163e4efb70ab7b219885da5f40b3bd2bced5bf7e5f49ccdb5744bbb9bb56b96190fd7d6031cdb7756bd43b9af0fd29152deeabbec005120ae2d730c3be20446ee54172553bfba9146e9ac47450ff7ca651966e6a579bfd89f419d317046db76ccd1479925b08d1f90e2189816000fc3e06afb0abc7583d508faf7a2d7c69cf45feb0c578add97f7db40777aea0c56654062567ba62e73d46b53c4edd269626f1d636ae9f23ab9d898719263a0084ee054551508c8c2de22f48c633b4a0e43f094c6a23660868ee36e47b7e98b04f0e887fdcf6ec56e91b48722b61cfc6fb59bb1eb84e6d13e78f4126c74bea10a3bb871031c90fd2cedeba1aef589aa615cbcef553e1a3cbd1c85b62e03d5f70dd263aed628a1b4886d105a0dc864af5a39fea392718db8656bedb924e40d10080fd7038d0c0f77682871fb1982509349fdb941ce9dcc9be3ce85b8ee42d6f206982b96875f2e0b3e14000b107767e45ca9eba1ab061eb2a4db61783cd8e9c00d939eb83a3ef1caaedcc26fa24fc0c7d4a02693781021b2bb8846295280cfb3036a3e2ad5698696b5d7d3fc6cf2d66941395b070e1c3de9d1f2e2c5b91495750be9be40078793283c3a3f4acdf6a3af636da5d84e0d0f9bbb2db59c18fdcac20fd6a3b5d333e1c60d29737fde0c60f1d9d441c7939bae48b46cd49762ad2f560581b1504365ae5f2ca3ce355c0238966a89913abb8dd8a924ddc9d56136d2e50c0b615493d03bb6dcf172169a7f05d2a6a207c827bd6663d861e0017ed121b30c482ca759daab504f1745f53f499c3d145d261d65cded788e4cd608504db424015b9a7181bdaf5feca7c7d7c3354b5057513df8761c7d25fc3b50bf29cae5ac0479b28460ac771ca391b1742aa7c4db34eb1f0a4de3c6893367235551ffa6d300c94d1b2ea5528be78ac5ee3d001f149dfa91c220bb535fef82d330bfac3a6907e3df8445189bc1593bf50e43a3ebc8e9ba74c4bd86ad8aba0ffb3b79d6691b04df5ed970a8c13987b132f40bfb19396b145606ac50bfa16ecb9a146b6ec6b903af07a93e893b90e4287f0354d0faa6b8dec3ffcdb9b61d028848da2177a0160075df7551d8da6b8dfe2e93ea718854a5fe116d9a554e260c90d76bddbab8e20f2d80264c5c24b685cf0c2e58d7af007dd49d6f52abd35167731303bfaab4600adccad9ecb2e6b74d45f4917c779443f0f62e75951a8b5813e57fb270a6910104978bf1ce768069d9419e7d96d363775709657ddb7d6bdf3e962d138c57be6900d78a84dbf41a6ce2b01343e20b13e6b14adfab0a8e462bdaa1d63b3f56fc24062d4ddb600e4eac8169ed97e7fa3b2d3735d648236bdcd7dfef4269791b1648093be81afcf60c8ed67512a80d1ecf2274575c6a7b4cec813223a61f1147950805dcc702e37ef783cbb74ce5ef3c759c2a30b636d20c26cc03338823ac9225bb09c97e2d7b5e26462e7677e88fe8a098161d7f28b5fc8651a081687f187ac94d0101af7a361f2f2f591c26f182517912988ddd47a8e78dad730fededfb3fe95e0ecc33a4a8842f76e87251b3d80a8b1ea52cfaf2c55c409a57f5c6e4577ee67e0f2d00387edef7312c4dd089cf8ffe8f4aa55afb79737bef19792674435d3cdd07019e8a51950091f349eb8348ebdaa81f5b2a31d7a6c208454ee38d9ba17947023c39ab7eecae26957aecd76dc1de07e448e42a561dfd3609e7cf410ab3903a062c410a22b08d0d23233b046ac20bd018cdb6dfbb6893b20517cac433396c660114932711304f43bd8f3b39fb85cacf7901a1712589d9b04def30865a066a6903691c30ee5850fa316792c879b35af3fa909fb3aa33d0cf8759a56a5c8a17e00ff39301d366eab6788c9eca765a7a49fe18bed8e3973b4a1197706e347e1d780053279373ef1c01e35a09b6afd9c3062d245cf66ce1c4f084e71edada6300ed0254040e8aeb31a330340215914adae1777fe7b3a235d727827329d582c8fafb0c95eddad1fcbfff275192837cd5c432af63513a67b663a8117a70f3baa2bbb003e8a9acb7b6f4d4b277fea49376ee806f929540b01fceab083d49d0288634c20ffe712c0de3e0d99ae4f718490c852840fb0ac1e4f69a7f8cd074fab6df477d017fee930b67cf08678bebc15292e0f81bbe701009b205ecaefca6058b87daf00eb584b7b2e6efc80a51968e741e4cc735fea3f47505831fc7012ada2f4e1b820d6603e1b38fb5d9081d0a55ac819f3ca82b83bb70cd2e4b8f9bd834806a7ce90e1e8f3a3d27e8f7d0c93cc14b2feb2a5c8087a6db88184b5fbd659ce968bf3c0b58acfa89bf851ffb7af16880706fe91ac64e2a016083ac55d673bdef0288e705980670519a65a55ec6b92dab94e0139c8daa5d257d067e91777c9801af1a160de8ddddd3b702be86b3ee74361806c0cd8cba509211d55fe157a4198abd2732013a158be5bd0f2c4b41cd1418f9724174f4c39d27a6b9f5390cc2eef40b7a7e012295d3a2921ce9102b62e4822b8b5e34df993d5eedf0bf3a011c001c7c7c9e09670206fd24b6a9eca9acda528f6f32befa273266a6ac5bb62b55274494aa7803ecb412d4c10455c8547207b4a915c3b853b736c0f9fc9caab0c3b53476a4780f6e7aa51435281a573eb7904ab08465c3ed4d25d306ec4c4ba014dd1a84d431079a2d10d5f6feecca812322f57c6e472ca32a48c7afdeaa0a488266bb8c21e7030c49dfcb7cc96be55be488173d3682093e5b3ddd69a303d848c75149a03f98001c211670d4e02244db7bfe827af43df889f21fb491329b744dcfdc1605971e0abb2f5762fb6c215d570569ae02b3fababe5bd9cb9c82eaa35159eac4d3fe5f0c20cc869f8cdb296b9cac5d696dd5a07143c03c2e358b093c026a7f48e2a20707bdfdfce0be251da4f0e8ce810d9130b99f030da65db78543f14ec53282f5330f9f884d8f841e30603f762629e76a11663cdde8adc06811c86d830969db6d03021325a91127493e67f04fd35b8d0b9af84faf1ccdff4eb839a1c40302ff0722090a597a26afcd478f054148eeffebc269f252a3deb1249aa6aad53451d5c17b0dfcc4abd30b7890ce2f5e967e6335180695f3ba4961ec95ec51f9e787351d4808792f4d98e8c542f66f41290cb0d4be6905271f6e7398eda1a3318e886d17440bfba5c340297330ddb62cc2e683cbbec53725b4e5b07bad47c6322f78a6d7860ecd635d3529458c2b1e402d0de02e070909d13d8ef46b7f2cfa8228479a5e7204af46fff145529ca4803c9eb835e99277cb835b5ca3c86d46d3a124de8b6ae103b2cc996e676fb9b1501ffdb6ed64ac16460ef82a5c916e1ecf815d94bd877c0aa7516a83ad1144dc068899bd66a865ac7e8e5255cfe4dd742d7dbada6900e4025f34c8b620948b4a9e3420c347f5640059e73701fdddfca962232b83a7bd56051a49f82ed7318d4b8fbc9960c2b723b06eebf0c7721c9a534d92d58c24120f008031b351413b7cf104562a830378e71fa420ae83db991d6ec9a8a2ec5f40140c7f3b1135880c159c3ff16f3ec956e325d43952baedf78aaddcfc5b5a842f0105cc1a79ee7db794693b887d643c5670363d6eb755a58272306a447bc9b534cb06e3ce26fd7a740c912c0433b5c5b4a7a8105ffe81880997cbd12df48fd230d800805fc2cb43b97b166925913ec5075a84f399bef4150203e7b068f272122b9e0b831d871232a7dcbd2c852ffecba003b231a849487e1c5eb1ff7ce3a038d66309dbb11264fd03cda170e5f17635b55df65c2b01b3572661b06121790ec67d470993333571772d7257ecd75d5e1f52228648a149fa03ece06c0caf0557be73bc07331912da8c1bb57baf0380f7bdbe0c88bcf992092c81190b33dcc49b5341c40fc08aa8032e74cabe15c2c89f847d26cd81f6314db7cdbb2f15242804f02e6806395224e02503420c5ab37fdee8000d2edef2da1fe49c6dbd533249f2e3323503e6a68fc10dacfcf01bca0d086aba7570ac5b7d1eaa8b6453dc4b3ea781bfc10a62cbf3123abae3447670a93a6b4e02841e0f472a00489678afd20094f3b6c3008c06ed6632f3cfbeaa4e4f8189df1c17bfefb13111976ef54adb5538cc5fe20e5c75cfad07808e785348ca786079b8a5f3c10bd0989c6f494476fd914ab2f10d247982ac1078184e97532f370e769da920c32bfa52376b02f5964737147efa047731af801208facbc9a0a561dbf9020c1e2a18c292143288edfa81d651ca4d065b65287050ccbef529f92674da14b2da0b925a094083585b89ff261dadfe720c32523c0778317389ff0ac0e973ccf20725f7301d1f185f39130b7744009bde0e0dd88ae0e1fb5bc63c71431a4845bb49fb46bf386c4810c66d2efc8d93e24607c668dacd6f124585fb7326d289524758f0b41a08a77af4c9736d86242d1ac70553742b43c08063449af8b59dc0c3a42d162a4a315bcc146c0071e9b3bb6708022d8f582ba11e62e68a5f5fbd1da6ad783e111477f3ab8e606fad29739d73ea00403baba0a64312faf8f5362101fde71c395c8894c203dc41b4aafee11b8fdf098198d911a67c6c5cd922f1e1418728dde6c73b09ca20f5473f5b15ffda96ea01e84c62df7d12f02363ee5397e219038e5114b717e6399e954efaa5a0a8f89802f9c2cb2597b941c19353ecdde0de28d4ac0e0333e1710138f45e0f69a2db3401ed793dfb3deeba8ca6017038280c6db571b440826929a485bd5096aa2e905c0bac7a007bdb4fe560b18a095d0ddc38f1bf4c4bc891cd22a9025439b45ca949089298fb240cd48b52539ed3fa710bfefc2a3feb2c2da211f8c8518e4297305d0876f214f0a9cab927509178dfacbba4b5c0869b3ae8e9000d71145d263d072f0cb80f41b77f3acf1132b809f6a18c3afcecb436f19c560e1c435d9fcac33542041d6d941750fcb886e399d4d94a11d3a71854ab38c87f42928ae496cda9d13e0058859a5fda8b4ab0f187b65ac4c4576e501283422d6de5e50f86a2c40ac6c807 -generate_ring_signature 52ab38afe83396f4a7abbbe478732d39260c8086f1b221f37018ad83a56295e8 52144c557bee42c63d3943a1da22bf53c53cf47e18e39ec7618060fdfa2a056f 113 2667878a43543a286946b627dba44ad682f51a0ec0dd5885c0c17706e1f27962 56070ef37ef631a894808bf2998c18079451cab0c36076b94ba7046928c7a1b7 09d61f61e982a1e500603b81bc56fc727b7ab3822a8af511a213619776bc559e 3d16d0811f8d8b1ff564124ac0735d77c2b62ed3339a2542deb20167ac8d78dc 8d1e73a58271f020e21201b77b3e1a0d5802960cef0da9c30f88331d1e53b7b8 b15c3a90c6d12c50c12ad1f1cd4f5cbca77abfbe26dd1003c5f92d256b7d9a2a fce4121de27006176bafef1742169e769f3a25a075b413ef0c3f7affd5150561 23e7a79af125e2cc0907abc7b61ebb2b6d712691a6c556474ca79c1cd2873c0e 1806559d6d2e9812dfa4d97efabbd8ecafb8331b2a836e8b94212ae21d9e935d 3d825ca551aeb017319626965977dac767f90994d4574ea52b0600086f745c96 9e5d692b5e82670f6284fef4efb6a3dce320ca9644126d52782f922e2f1cb62d b04d172573b99e1b8074d362c5f6b997b05ca74a5aa4d35c957853f9078ea90a 451d26bdd2e49368fca9c893cbf9a2551aa0bdb1df3d919a75f16da4615ac00f 39123ff66a0a8cf47643625b997f3febe9b843028a733213f920fcb0fb224321 2e344a31a1f4abb279b66741e22b00d72342c93ddd66332b9480f38ad010dbf4 174dad431a88a820abab2a8ddf30bacd6c0720da30f98ccab7c821f2ce13570a 8fd6651dd86f87c6e9baa019e3222459a3495c481b5983bc1f3bdd43600538d4 70bd22972b4e8c895d93c5313a2c4de3f096b2832401b34492d8b63e4c828b93 724ac189a333456efd5ea95bbd6c3174180204a0541f56c9d3c4e82c1b9a9430 ee0295b969e97cfbe6d47f879da1a789cb27f9bb2af514fd549b4c8d8db67ea8 6516c5de7a016a83efaeb52dc3810e147831cc6a7bf6d556aa711e3f26ae74f1 3f8aa4e1f31046011579a2efb2ff2e98c2e94868bf61f529b28e0c9dc922e65a 40f502a1fc081d3e0870baf688962eaeb053998be9e61627a99ea713bedb7d33 f5de2cbe86740e20007d273d240495720423986ad67d13022a69f67eb65c9db9 a72c9067245c4f1f7d3c6411578b2e02e18c373f78957b0c011d1ba817d5fcfa 2a6f3ed6fe0238355cf8a1bbab5f2c1f5674c2432cd8c964f4bde62d1f84089f a1be89b4e0cd03a7080617e61c08203f935e63429a34f8ae637c6290a5f855a9 dc25b39ea362c07b69225919ec631e1de3e539a0c99076421a03888c136040de c42663cb57da0d67ce5e40583deafdab17af7f37f4e9cacac7be29510b77ae60 57e85e3d5bb0bfb51da145a2345412b3d4f1bef0a9ff125c2533e3a76c2fb261 51d82c0f6c42340c6f37f6a4d24eeddde756700a2a674f0e5745b35b0493cf47 d6239694cf291b89f96ca1f448b250193f738547b70c7627a72d6d8c3edef2a8 3e3ef92d08a0f37954c6ada45ef29204a7dacc626c7181d7613f2fa613a7d515 479b439e8cc29f89d9dbbc3acd98fac89603d262b014a2f876ae4646ada1d401 ac9c823e44caa76f6d0d2ae5da6684a6fe0fc4c623cb28bb0ffedab0de052047 bd47406403e535790864051db0c1c2d09b0e7a0fa9df52d23036106cc5eb915e 19abfae288790ac7d77c0581167f78415897eacc19ae8d537d118c47fa2b1553 b5a5323c096b566ec9e91eb9f4c870869872499205d05cf3d7b58811d60b1f58 36d46886bca44dcd834fa408bc22275efd1264644143dc3fde9814b06d1ca44b 758aaa67d8781b59b605c687e993e1d43913e3693a351d9addaff6a21cf196f0 32c1e4af3a5597eef97f47511fd377a961dcf5e3ed4c1b8ffe981abe8ce31a84 ec3619a0a1928ce8907e344b0c7a8e87e15fd53ba8693341138a5714442e2c34 c6bd33f733cbaff02918e91d14855be7966fbe29a04b6149833010293b04920a 6b8a52bf35bf9bcdb80f5c22fa241b7342b4f6bbe085f78e5096f0647f0298ba 3222525c78358332d2571622030a2c91873a8912f815486ae7f1b38418cccb2b 5ffc8527b801db0fd1f9fa568e6fd8915afd4d2a12c9e760f8a0e3f65c13fee6 65cd0954290ad98ee8fc0ade4ca8931b1f6832199e69496a57d71b25e0c4da6d 519dd0ce81bfb0e2e2aed5c4404fd08cc5dfc32cdebd2c4eac8e8341a99593dd 028506245b4782ff02e531921ad41f33458316d2a04701949c9a1130170f667a 65479d0e3b8c5e054a42165784f71979e44c5ec86edbd9360d364bec827c38ad b056a8e8159cb51dd0ff6854f0b5cb7d73eb6af8a5056f7a157000fe950956a9 121bab1f2128a355510aff20a2348ca726015f44b14247ce79d17147a4c4060c 67b7dd5f52f743bf4421b83e735123e1c17173c562bebbc92f241f748c45c682 1e8a956e74016f5431b773efa5ecb58a388e94f91b9a5a4d928c90b987298361 928bca6f3ef639fb399620438c215df701e992d50c32a7d519c9c4029777d8a9 4b928f62407d82ea63fe4ff44757b0e2c99b3d809f473a2d9856a4e7915a4048 2638494a4220023d7abb69e0663e321321a39f10ed88170cad8c098adc2b5762 b4e4fb545e5daad738d95438f9caafa048eb5b7b6cda4984f0d98b218f8ecab9 4e1da4093412883d96f6080929c73604c9b918e4b4eb3263edd50d07cd370ac2 1825bd87bd4c8621c5d585e3a150b810734249c5f8b98826d4fca4fe17a479c0 afba4add454e41364dfb8f3f10e613f4379125837d809f9617c0c59557d8ba06 be6c78f9549f725e77166b209a3a62be24858c6fa6730b751b454d8db46513f8 d72dae253fef5793e2c4b21266aa5fd4bd4bb298ac12024c3136bacc698bb5b7 3cccdd128fb4279cdb8782fe4e891c92b9212ca7df38c1281a7c23bf0488c7bd 38d08fe57e0103171b5000d5f7d6f741eb51c405dbcc7ba351ced61d610b5e0d 7877f90fcf24f4cf3fca2f456e942af7cbb7c713aa9a26a437870bb0dff5adec ec4175d3e5369062603878430224568d1d9f6b01b48a0377dc82f66f9ca309c7 eb039d354117c3df90c53e9eb0492b4cf2e4f6b4b57bfb6cc809c22621edf016 6ceb1f244447e6c4fb2f4b7abebdd227271ae511383515241e146e3401b47afc 65cb32f44ecb9ce88d08bac89c7b36eeac2e52fbafae9c104b65851d0d3fb689 7ae2db115d82335fe2d49f12ef8248994b958fbedfde2f99dcae864daadadbfd e547d52a6eeb118a7cd1147895f5a75dd0dfca914d50706f6a7033235e3ba07f 6ab9a84a88f767a1cbc69b2740c05acaa9fc9d84ae58cdc77e01b0900cb19ed7 601efbbfc5cb40884d47fba0a5e1e5750685788e4225ad69319f0dedc8053cd8 151f98617ce7dca0ac7bdc7910fcf5afceea4b8acb144110377b458ee877eab7 7dc10dc41ad38ef8c879ac0c434ba7dfe0c4ee458285bfc0b07be3127c970bca 171ce0ef0830d4ed99f42e0c20d063ccc3c6ca50b747b36f24572535d28c77ae 3f55caf66c98c9d773a90472b2432e6bcdd84a286b518a3f3f95e2ff104847f0 98ac27c0ec172728c1284698b62bf64afcc2cd1535ab245b6ef9e39e1a1ee486 38a2e914fce5327a0510a04ebfc2843e5ca25b995ec616bc1e214df14cdbb2d8 6ede2050ccc3ce325d911fc26311bc74f9f09f51096acb61ffff4b751705e68f 527e46d547a1337da68feb3065d017f1fc3d37fea430714287ac3bf13de5e43f 8208fe6fa4af1af795df8b07f36dcb08d87f9b5fdbb06059b6ccebb6cea006ce 41cc08d2fa9ca82df528502af00d1d8ab83840d3961cce3a000909322c393b15 c04393bc4ae049524110432433f0290f23a88dc4b0bed266bd3c139180f4571b e41fde50f6a402f874e366619f17cf3af987c3641720207c0e48b30f3321d462 d680c870d7b43c6ae308b818dd6d14b29758aec61195bbd3e354221c897f5458 352582605e62e595ee051a22e47c7f10c86a0ea59896c907afd853c418893bcf 39b1b721c361d5cc7d3455cfeb782aec7f122348a62caa0d67f4fdbc98665503 66e0b54e8d0d0b0b61a6641685c1f3acbe72bc1e55254d88a2cbcf50399fad68 c3afda12547982d6afa86121d0eae5a549d22192469079100f272ab6d8831338 3d8021bf75cd1a3b64192e532e21744cca23a32a7a50b459b635d8cfdd6e28be 176079f943ba5100d89a6f665864407996e7ea1d554cfa79322d38300fbf3cb6 735b236716beb3d302ad5518901b9a523d51fac099752fa0c1714fa342ecc944 d6d69f6f715109ba6b81cc303efda0abd38a312f526b49bc058ccc44d076be32 1e2dd2d69008482d8e7a02b3d026e549d61455b1add013bc88e131dbae129b16 e02a717aae13cc6ec9a9ae8bfeb952c42d8fc709233cfd1ef7ccbc1bbf912bbf 009b16fd9effa67a4c4c8eda2b15fcc67adfb5aec379d592c6b80c63d3750a9f 40cba48b6fe177037c2d2f652e35278d5aa779ec0e37a01a3338154b0109c7d5 fa94d28721ee024c3920937cc597b6e440056d873da9183a4121740079e91c52 b87e5b2635dd6e4bbe8d22f83e55a9b088fc79bfc79b3866ce37aa3be95a4783 705a31ef4484a1d621a26ff0b0250ab8f4b4feed12227f709c41b03aef4739a3 5c424ca54db641680633f09638dccf23657a0fc34f09c2ed97b3ff0193c330c4 f00e3bff74143e2eb81b5aa10d4f8e110d6f82c12a6ecd870f786d120a863ce5 2b57a48a8c801c44fb5a193e27b51a3b63ba879a68a603f282ee29bb06dc7b20 b02e81daa731f503076e99d7e975ccd900b8a4f4a20768e03225a74ee7769c6b 2dbc9cba9c5b1c99149fff8f97be8faa42fc16d0be7bc5ac77dcf8886a290e32 0027178fea8c180bdedbfcb2bb7e394185b37a42977afbb70ef8cc3f5b2b1997 c417798c3869d3bef770f05c7eb4e89948bd4c76031dc429d6a52fb199e16b02 829c84deef8ab603af8fd2a06bdd0daeb12820c32345488d074fdf0c8f004879 4be25ba569f1b4aa9c55a3e6dcf232d3b3ce4cf9990a6f8698b24db41ca20eb8 37165ce3e5c4b593456f947b9813754b44e40e8407b81178db50716df2352374 b899ab2a46ba764730d7a822b6e24afd6d6d276c4a73ee66fc39eaaf2ed57b84 fae3ccc4f506532923a8f03d2d227f60d7870ca2c855098aedabd89abfebfb04 108 f18b83ce4297aab48ddf39ac521373491902582913dd636ed6c856372883040fbd5fedd42d00d56761bac266b7a3ea9716f3d761ecea5b7e8bcaa7477a51f30009af143d22c736e7e0dc422cf38867ce587406d55cf3f5c1f522df01b3ad7e037a6f109701c3755937c453f327de6bd2b20849b8de2277bc01499d8f5084ff04d4d766f63342be77e4342112aa0454d6a7b4ea1214d1c13ccc35a0d7afd31a0478f7229f236fe0426cb7546ec6963c34fac7e27710d904617aadb2ea17513b0dc026c8660d887bb75478abc7cbe6346d21bb4387bd92a9acc33e288884a7870c87d57e1447ff3f0ee664630101aa1a540fc8951577b2946b53112aaf2913850874a1d96d0ec2dc9c5f280886ea3d4ceded36142497a339610f03e48eaec8e905d169f7e9837cb86ee4f91c2290c843b055bd3d3605b40471c4148a8a37d5bf0cc4d12010023a6092b640343a0ed316d1c098c1dc3119a4f2f195519f048bcf052584163afec3d1f2ae77100b02a8dae3a8b77c45ccd5ca267dbc428c4b82d402a37c35c2593cc41e802c745db1c9e08faaeced24342af14be37a23e2460c01078df0802260305e5257e5fdc92fa343295f3d06cd5d674915c07e3a31f34380079ed7f34a297704579e4936995a897e9dabeed33f3af49b5b553510eeb231490339a988f9936612d67d120e428c222c8175b3688c8c4f068aea65f329292c94013741c6d3b90d97b172b283b4b43ea552e09159cfc860e30c0f377e785f7e580d4e7b17b87b2f55fc8111324741d46a243fff87b6f83c7a76171922ab657a9e053bfda200c5f92a62986bbf73c6b634a1e294641ddc24d0179d1c13e25439550ad9c8288b79b1a5cc94b36570654f7dc2380b0b393e82179df380b683a7eab106f6566727f93afeeb73af7cf0e0fe7175fd7d1b0b8827b65ed1d0696268c5a90baa93c0d2e970a8fe9c3cb782dd8abb1b2f7ed22918ba914981cec5b6812a5c078d6179ea4805bb94a92f8c8856519b710a2843a448c641a17a4decb0d7d4350ab7b24dca92410857dc514e0c9e3d9d33fbd4d2a6d97c485666f4dcc3131614058223bc35ebc1dec077a7784337c4d55da2c3fe6b8403acf365ce1baf38dd780b485fff94da07685a4c1deefcb42d76521775f4af953854915e3427d59cd7ed0eef61be7a3bbf9647a13c4999d2076af643cdf5136dd4002d67cdb9c40600af01a827ecb2bdff091163e4958c460f1f4194bf023b9d5862d74ed0483da92d020634cacfb2f04a843407d6efbf5924c512ea51c3adc263a6c458d6302a8ff6520f4bd945ed26c0fc088733e2b6a1febd4c7d1cba2003cd42c63379250a52189b0eb4c6e4d4a4926d6f2832e7261ebb07a8dff136322f065249006f474def5a680db5743dfa5d09ddaeaf873c3f532a1aa883bec2c0d578bfa3973cd9bea3594d0fff7d5334435b42fd7c47e47022ccadb997785c6a2c3e22129ac501d6dc2e28073b923a0fa9448633d28d402e4720cc430de502a9f778d71ec01620658fac9103a2e9aaa58fc5559abf9c3ba0ce4db069b501d07119e6ba97ec12fb1175359a0cbbec03cdb5445770e2599820e1ab3b1b4c4f5b75330067c5fcd3e60a48d2ed0a959b4775d8fb0e52f369dfc039bea3fe81c16917d6ef8889f7fe67ad0ec7e50044e15a0f24551c09a3f15b563053ec77dbf753df36f5e582a4d5872f2ccc1d0fb4647f37248bd5fe6b8f51a10eea8fa121296f8e87410c208c4e7faedacde1019e61e8205f6f3d6c4111213b1bfef5f175b869518ebc2b4d194b84a0e60c810f1f72043d74a1608d0c85260078db0a6445b6d784477ca95630b6093dafc5500c9a2611c54309c9101f421eeac9f0018428ce468176dd53c4dc5c8960f2169807f5d019e46a185e7077eb502e116982fcc5b962e5ba3233d6feff440d6bbaf2098a7b832fd1d59f48e9db83777abca177d1c5bd21f7be8beadd09b94fd981b702ae06b75643c31f08d45ee8106a6c1073f0da4348d4d9f1c5da9aeefd33dd0f0fae539d8466a846e38a65235f4ada9949362a49b9e8d5cdbaee714e0e9555970144a616bf9e67e09a57ace5512be6e734cc3dde62b6349b5281bc5e27fa8e5809fb5cbaa67629d6835ee11c0dc1aafada214ece29e36e7a5b84bd5aa7eb6e5a0e69c984f80cf7c5b44fcc5413ada2c00fdeba5b28bfc54ff05f676bd4e4ba3608a5f04be1e0f52f7846b24f411fe5fb16afa965cdb350ea3453cfeea210915d0c5d4cf2b6faf05f6b7f1cc5d948a053e355b513b4b05526f481be89a80d91e40a5156a76f4cc90f3aa19590d67492e0c6ac6283c4b82f766046079ef0cdc76809fbd2c1933de913a32ba576338a60adad157c18988dcac393eaee7045e5cb770e8773ea55d6756c410df59ff71088262d1ab62d92817563cf3d40b504636f4a0197b7c74be6cb9aa661f12f79473961a137ec95c52b23c28269e32ee53eac9206927cf0d65be5c0ec8dc984327e231a739432429ed3334c56172dbade5b3b0f09aecb4da0e696437724d1f66c2ac3c3b8fd786a4c2022d26ca3217a820e3a6b050ed7f08ff3e66adeb244b28020e15b23647671c5ce973fa2beec824f8a6c1e0cb0c953aebf97d1c6f4625e863d62825dc062ef212fb6ad964077ab25e4e80a0626091b64830c43fc5628b2e07da81c13595054209c93e813f99e45f2a6981104734973931b45a7edd7795b2d9e8e0384a04e995866d29f1cd7e0b77c3f8489088b7af35f2c92ef03cc21f5c727caa23166a1451d17fdfa8ad3a97f958e84910e8ccc8fac9f3e1430722789803ee8222371bb74ea5939a817dbc0f6ba74bb490849b8d446363a72436659e6b336b562ba486796448c2497d121e5f1de14ee4f059003b274f6421820c589dc8117145f166931097d865ae8339a79c9f7dc020a0029b3b25688e41c07009dfac52de261d17e3f9b464f6727dc8aec0936ee025301d702a9f4582bf3c1be14e697da2a58909f125f42b66be3dbc0cfce31c3e09d072898b2b1508ffacbb1821a3a917ad17823525e9142cc6bbd84236e6cb913910c74db8c51f66ee08c0daea8241fea7cbcb1ce6c18ca2bc26d415d39b3a877fe043a3885f3d653b487f841b54576fed5f697b48907dba46dbd29abc91f53de350d7698dd6a38ff93919be67d0f6c3b44e9b2e227ddd099f6c3d941cd8c69482f077926eb9a0fcb13548718655acb0d1e3eec65cb88c6d0ca5b69d251c50e1b3809e1fb4576edb72001766aa2c963f717d7d96de22f5ff7772f49053e216ef31b0d37fa905bfcdae88b0eb40f7a0e2724c212d6c328f765e5b8ca0c185c6bc799061c88463409ab22fd54ba8c65ae866395312fe0eabe28315140d1922a109255072e9aa99d4a1989e2a0d0e2e95853eb5df11378d98b4acc1a7b1e31bb9d5d640c4928cb4c6f464a8e34e9f0983037288503d51d0d7169ae66f661778574249b0cd76f9814b9a52fc328fad2b777facd18fea6d7501f5f44146fbb012193655a0a306564ec51763198398a602939fa9ca73a8d725e81d0b453d52673d489d2aa043c368fe7de2b61e0f23d024963cb4fbf15b4e16c30b6b7cabb7ca4259285800e8c20f5a215822af371422130945bee307eb00967c66179dbb5fccf4e0bf4bb00e6bacfcf74ba9cc4e849f346260f0b3ce81bf1126c715fc2633422851152a00b18830c8a9ae90180f87cde6e025bd4565768c77207dab6505f1bed03d8c28507bf77baa1fa806cb983ba83636366d5f1888071e755bdb954ab270a303c8dc007a6bbc64d54cbeab53a11151f926c1c10dc41022b0cc18c010b4bcd2af9bf2407d577bb40cef488994ad1659774a6f1f0588d0faf2e89ad5539d52172f4fb2f0e892c04cd142a74b2086d6dad64c8c11a522a84062a88301799147f0f5fc6df00017f18cbc07e9fa02e11d5e0ac27bade23e08883c656938cecb6da53f17f650a7ae8dc8020e749641f4e9632d614bc61cb88cbcb822e4f4bcaad8e82692ae60f64a357ea047d4cc3de9ff7f64ef19569e752db5f279f01aa313218f4f897df020c5f48c428454b4554a30e2caabef575053cfdc55e153924f6ee05e27ee2c6076a1caf806707bbf72e74853114ad36d71c2df1c94fd6180aac02e8d569005b07d0621e745ff61a80c60e643b3f002e69c712f1cf672310fa9f67cac36eb51701dd5122da5be81457269f077baef6fdc81b9b133423d1f8955c2f7abe8c60f10302d713dc10135b5a1bf0d0c9671492185770f71b6840e5076438b838d754d3000cbd02de354d2eb12b92f4766fb8bd157717f22ca165682fbcaa6dae301184056d4ead9f35c1555028fd437f880b0ed6de6152bee7777acc28e5a949c3ecab07f12c931e8dc5614456d877e67a2bcbbd0dc6db18f78b235565fd8b6158e8a502e8994e90f2755e10f56148b8fb70606220f847ba01d8297f369f378ca464e90a525ebd63cf795d625084cc7d9a119e6f30734f8655a3296b8b2f0b8d8806380a8aeba130394c6a271fffd4da4dd98e7a8377324c660239991163f6941b8192031bab8b0760da72bd00d6c4954543ddb76622f543657821075da91b85987d4a0036017ac76e852b8f7d469319dec551bb4c18bc79079ded43acd0c616633d44049369c5b3764b0a66fbb5d09adacea2090005aab6e7f1f328b3b7120655c73f07e510418e47cc63fa0d877ba453a9694d54976dced8b76a03538cedcc2de77d0d191c2edc16c653c8962a765d7197b205ac0f2a9241f23054d9fa58bc5b84e10b8e080b0dfd124481e16785e570969b6a9d8e46257a10a8c7d101b62c787ae40b4049c617b33d8db9650f4316c4800f572bd752f9e3b052831fb21a9129a3fa0ae80a7c40226de8b47b86726ea144d174175201bf4483d7570984f461177e160bb3c37e59865c9efd2596b76f2cc6f6da2f98ff4d888fb8271e474b7aff58d104b548de7f41bf13777bf5774e0d19e80ec5ce2ab1966ff61de2d1dd45ae1ddc019c8b7c5aaf58e96f7801d07d3d1eaeda30c86b3af2a2fe798b392cc7e7db5e064a7d0657819a2aa53aeb06c69e8c5b56bbae4d13f002d84e9f9f2d25cbd10900f34bf5b8e7da18ae420c327b7e88e4b63769c01c9e09f5bb2641ef599ed2b1019ba2559a71f20f65ab7e4bae52fa5446d2e40fbcb4508e297343ae609a227d0f0fb71c0dd4649892ff80762ee499a01a59287c51867b6715f331b361e0cd0b0cc9d20ede66de28a535a8b732ed0ed01aa66494299fc449a4e86182a1d899d10625461bc0dfef43437467844115a5e7797e7aaf052376ab779ced39735f66d007e86ec4434a1e996c338518266f882a92967f9a289423b5196c0e28bec4cabe03892ab37b49e81e8924294f38217cc132354ce40428dd7f7c0de1731b31fbec0cbd84f0e992ce6b9f1ed28bc102e42463a5b96a89286719f2c916d3991b5e5802bc52d501a2b0eb238546782ea3e6fe376a2c71b7308a03b48af53ecca039e00bdccf2512b31c96a9d0c79e4f549f32bf44526352f18db5b5f80c5371c5e6170d2971f970bf424c054058c97fc5f064cc3a06eec9ae656a3f1c1467e04ea4680544525f4bc515eb90b7bbda45389e545f4f18a0ec772465dfbbc6bfcc51a01d0e0597e74c36c5ff0005e42ad3ae9b2995385d10cd3753bba05692c8010659f10eb25025f108e06983dcb84e49b26f28c0d057da620adf628511882e142a129106016caa4f2d3d68e81010dec7db114ca085a75f91f1efc633dbe9ceca36e70a07e96bba3f625d6186ece692707dd42bddcd7b2541a2b1ac32287d95a86d430a07798a326c9cc8d5854a5f6081e2e52f9cd4531bc2ed19244b96ddefa69127da0d6661c253b9a30d95ccc9622f774c324bc18b26d47815fe0666bf8bfbb83899043196238f2df5dd42abe0476298f84b6ba0cf5982bfa255cf7f6d15df92a7a5086dc464bf2c0bfc35f064aaaead12af3242fbb99a2f45abc634a75759de25e404f6df74078dd2c23c166cb3b38d5249f88c48de544ee19ca2abeed53383388d04fab8031545d07e92de47a0b82ee714957ef13881021bcb36a83e3978a55cf50c201807b5dad13a07c69e3814d71458cf4b1eabd881b5de9a52bf21b61a37ff026c0246dc54a40bd309870486300e9c87aa6f70a5c94fb9fca9e4a6c139342602eaf940a50a1e9db9ec549aeade094aba23457d5a6cd2d733e6e52abff6672f0af206dc20708add56284964b0a25046e86f68fde58b2a446369bf6dba6255f809882f655b0c5f2774f5821816995934fcf5e6a967303eceee5f2f30b58814d6003a07fa598f8e7712be42cd59ccd5137b1bb47b1a8d4df04386fe514c456aad0a1cf4e7138a094b73f73e9b817babf80fca484b4f2aff85c46b6df14653cf390323a692562eb5afa478028b8bb19be2d75ccc15831c3be745f4ab5386db99ed045878b4cc5b739ea95f39c8223fb428ce77725d3de67e75846164b5e0e199030c230bf705bb098b74f430e9a328792468c8618feb20cc241e8ee1d40e8ab13b0bffcd83e135ca951aed021bda1ca109dfe720e9b3a23d353263500d1ba4551d0a949b272b3675e57b2af1d1fdacd5f3f11eaea07758161265d3a7be78794020076976daf471844ad08187e8dd34475e09d7d70a0dcc481b2a23be435ee8b4dc07eb09fc638bde9b6f58c38e1bdd760b128c37730d1bde9fe311e7976159dcdb0b7b549f81ec2bae7f71ea0268ab93a0da5e45f173db5ca2eeafae1f6439f48d017ee8f35c05b107fde4e65cbd59d5aa81a4a60393512dda2a81ecf7daa9926d0c5fb401f2b13156590258304ce62fd5cf4f5d81ff773223cc4a850da86673d7004c187b1f288109261c60462a1e6a6b01edb1b7491fb8cc45991790f5c14f8c0cd597591279ab00612f9393d6ad3d2266c2fb55485fad9f49925d0281b9167405ecab9f99fee7ae3ca2b97ea6f71edf8b01b42d73990fb74363d199b2f088a405f77078cade2794a9e7ae4b252325b2bc5058b08a444abb05afd9a7a51bbf5e0f2b0a2dbc26d78402d8287d99380f97f76a5e875332c45243ee8f11ffeb6a0a0090da5c1c979de1ce2c51d75ef60cb841feeedc806f6bd29ccee51ceaf857c104c62c6859aa2230bb54448c34bd0c064216e04f73915c2cb7f5cfdcfc183a2e02eec0aac6afcb5816838265663ab08d637f94f5107119fa8d944e8719e7c8020dbb2d0c2842e202ee39440816c217d064a1ab45864344575483fca3f18d9e6609fa5896af8aa49b7cbeef5fee5a41aa70c209d08c59c61c7301c3fd2a93b6730a970e1af1378370d5bcfe26134e98ca7965b52414639f83ee7c5bd493deeaaa04158ccf1e49aa314e0383d5a6e7dcc4bb976d42bc9c754d79fe334f0f5299530421fa5cedb1070bc1f837cd7803880f846349ce29f367c348b648e0ece22ff40f48471bebba700d9cd7039e7ae83715da52262bf24aa9c53208e4d16e2981200afd736aa0252f4229a7f21dd4b7a8711021970c9b762896893140456ce8b23200a1e59b619730b9f8d45ec76efadf3099ce6da55e792b6229fbe6847f7127ed012f39dd585597c13a22a81133406c90f23371d0bfb3c39ff9fa3cdefbd7dee500d7b68bb025b89d132f1a1e854dc73bf1de2bf41f6232cd714dd36abe5d2757023959c970c0116630785f51bb82c253f7a217ec2038cf765074d6797d7abd63012566181c0c3b1fd34ab359908ef61ff71894f75994a7537b824392c29b2d0403a473dc6440ae375cad7dad09dc8563fa57b042382bafede170631e20224f1f0d586939c2623fc7d0f4b3eafc92865507333fe331860990a4bd9a29a1139f150487900124ff3e68db5959df764d34f028b29d7ed5706b364a0a11107f8d7ca90720b9ff202d70ff8ec9553bed6ac8a9c72cdcd9036babec7124288b237eb524044ad90c8b4e1c1614976ed993659fcff8f738f4049cbcabf3fe5e5cb851213f02d5172dedbbc0575d0ec76a0c3651a6850eaa423e2c3bf2d556f476c236d4eb03591d9df6fe4c2f462a24ab867708893e504cf89bdd1b4a29f6c1ce8d3ed0e80638ab46a8da84fb5456716119384a9c036bd3f208325debd0eafa2aceb817900f2a55e84a68ffa2694cc4ba948be0e830e8290680de889fc7f469c3b63eafca0b35e1976f6fab0ec560baca010d81a48ed0162ed47050ab86a7c628033c448f0b49d7f0a8cfef60e6e7c3605eb18679da2c791f37b57d1fb65ce54207abc1340fa21435c434222e1676a5b453007e05e086607fe6a9c63dee9772f1b2c6aa1609a95bdf5bf18f78a27901b637c8e22dd4516244fd58aad7f27f397b1de1d18c0ce8f9e8b7e5e39ad5c3b2621c6b264b7b5b7bccafc28c102eb92e840c9def5f08e10a27d1a099c13e24892f2b886599eabd5786ff11f879bf36f019409c4af20dcf85593bc65ba368e1a97e28d2eefa2c0cc18da18f69eab981d163f7d56fe701a263e4ea95be8f362cc79d856ad915b3da62be1a9c4867dee558396884ff100c93eff27f93db80bf0c0128ec5e7794d657c2a49a5f62aedd1a07d4c8d34c3000bc90449ff1a0869de544a7453bd3ce8cecbe151e80bed86676292380b0b62a075a682ed4d1e5da6572e2824173639cce61ff1a1bc3bdb0275b910595a121640f12a1713a213da67f53b6ac973cb4ceaebc09da41fecfb171577ca500abb77e0bf07de2566f074f71a76036a6b9c11609280bc0f9fc45e66e28ffce19e9c1640abacb6dfe29730754f69b76aa0ed50549f561b19a42b931c648caed98887a010271ac4ca2a43261e7f1e1f9b19f0874bd7135d49342a8eebb638f04f3ada2ca0b9be1da9a008139cdfe45e93605cb03b1fd77a1149ace5a58526ef36589777308e2f72a05ce3759f812dfd83fba9a45588f0edcff9015fcfa7d1365e2d0629c0ba4819250ab77feea32d3553fc70445f00b5449fa3b457e984e7c19c60a5c3500d6b92c5a11930d13b6d84fc964b96fa20465a22dfa43ba41a9d2775b1d1ded0e53851d02d9eb319346336bbbe80d58c4bbf4282d9bd7b1e4d17d70e16335f8076e28603fd66d6cf8704e352fcaa5e339f82c5855e2ad4ac4cd29c3269930ad0db9ceee2d79cc1b6ed703a6426285bb6823be70d889d745827dc3bed4f5ddc80ce80340847633bc06db6cb4218189b0ac3edbd2bb1a8dbda2a7e74b71a3c5be05255aebac4fa7b075ea0d6f87563b8d9e5459bc5a9e5ece10aea346249cbd8e0666e3b41084af60ad61ed0c462e6ec3f051865e0bf2fef3c6c5796dd29e6ccf027dfe0d8c24f867d59a1a2614043154e2520dc6bb35f84bfc4b0536b775c5290d00e07fe6d367fc1bf6289cf98b75e5b3d14d6a12f4c291c65d62e69943fa270147a97954759fee1aab91abec68005fda859232870ab19f97e0091b995b3f100edef32955d0d677340616ca464d943e0996e0023308000a160254e3932db3c401b8eaa23491efb3330f4f5deab45aad76eff9a2eb98b04c3a3ce27fc9a90c52028c454cc0a3a2fcf558ce23b03d3dbb2afb5d6f94225b2920e99c5044fd8d6c0fe88edb0613e8cecf0c40bad256860c551af934b3fe612bc95f814cf17e4349077a34f9775b5b7a7735858bbebef5c1d6766a9dc3652f4aa92c7ffe5a923ecf0ee28024c4f408bd865d96a8e681bed01767d710bfd507af36f603a7d2699e1308b5264e81d8921781f7ced544dc05fe0c90d17b25a6f1fe7efce63389605b420f6cd8195e43db149300ee47606fc7d4b2ed05444c4c399d3eb28b0a318aae2c0eb9054a9ba4227ea6512a5437fd823c7834d02d2d273e8bc41db5589e621a9d08f6b7756e7b813fc87f3dcfb1c19abe028eb54cd5b1b23717a1818ccd82c86f0b0f0bcb9e5a3ad2c074e68f89fd92c2fea00c51c5e3e13fbb8e5d1a499ce51a01fe570e803a9a1da7344ede430fbb501dc63c0b23a926283779e059ba05817e0ae582f013bda193dd5fb57c8335a6f5a88d98377dd14964ca4e351787800a9606873e5b2ba66e57e21306e737e3a43727ecc292316ee6a4d94598589caddb43002a7ebe1556f3939de02a55f3860e7b1620822607c11c486c87405c4b751fa90d7089df5603bff3b513f3cf0d26c7bb29055b3a6c9c637b100173f37cdb9dc508c3685bc8ebb5357ee0fbe936e9c514f826944b8cdda55ad0c9eb56193f249604 -generate_ring_signature 869c7247a0b0a1fb90d96cfbb2e3d17682be83f64ea5718718768a7ff74bdd41 cd4c787f13645ac936e8e981b3c725d1bf042959eab770f7c28179f6f37f959a 50 64a0db89add71f4dacf4b369f61a58504533ede4d3740cecca23b82f3a75f4ef b37094f708fb5ffce04e866a09c3aa4ae4e9693abdde06a7b22dff21330d2865 e80522d9eaf7c40d7ebd82c81d893dae50ccc030c67eae697aa0f7df696b87d3 603bf0a35d784c9788ce83017240d87603463c6c41f61896f24ff3a54e5183bd 2879ca5cbcd35d10079dd6b0875cec28e8f177fd12a7e89a597d4041c60b28e6 8f16a24090b39a5295926e9f7e1e73b85fd8f12f96e58862d5a70b5b8067fa74 af4aed48e14d50dd553871225b4cb21dc488041ec203ae1f4140c3a46595ba69 91619b1d38238a1d1ce66c10b3c474893e0484a3af833c55a346d08df4520b2b 4fcd543fdd76e2a7d5a31e6d0d39ec96e4d70b14ecf3419d8f59f8a87e31a26d 84fb3a395fc0fe8ac6bb8fa697cb62d3d56cbfe568c24ed6f6c973cc674e63e6 2c9215c4af4e6ba4447a7ff5c92670eaca21bcc140aae3ad637a93ffa4e0c707 5384ef060ba220e21f947772eb03716fb79aee73e70298e74003d6150f4fe609 ef2500d30e8ece7ae0a2ca2026d4f176ace683edef6326a16f631e42029de6c2 4702a38b0795b6c71a0b8934c2b43b0420c29436fcdeb1ff04c5d44470e900a7 bcf1c1b26acbb158b8d4444fc9951fb62388c9e2e37cfaba70a676a0166bdb04 32d56ab2539721810ada48acaf38e0d28a2dd6e4eedc2dd9c82cac01786111e5 9c3321f4e62d07e104398e1e405823e9e1ed36032ffb78ead40900ec52779db9 8b732bf86cb9368873cbc81c0390eec33c88cefdc66d4063c54b09ba3aa2e78d 2dc91d83ec7a70f8fa663e49c70102671b43eff575c4ec9dd2ebf3abd7c03d61 f982af7048136c5075ba09d0d82da852eb1ff9879a49967cffdb181dbe08d576 a603ae7cd1acd05ddae21bb0803fd7d4af1a4fd56f995abc3343b3d86827093b 296b597866b55b7eea58f80d58ddaa00b7be7058e07b24563c4732a3de2c23d1 1a0a0ebf748500c4c58716300a78bc636d8616b3c6d4cd9d27224ffc6ffd21a6 2a2ccb830271f5438d1a91b5255ba79c629261eda2dc9237d63963c2650e7038 5644c2851b08f83e350bd96db37015bafa8100cdfaafe8d9576b7e7b5d52f76e a69c652a4d177a681277d5c1b4ad6b9ef22737b1d49789380dc890069aaa3046 3e7facbdea7a85a4b59ff60f20d441179fe72d3e07719597b11f544341fbc223 55358eef614502ef018661f3092b076e66612215a378f67e060fdd3a625bd41a 92db53a847ef3a1e0786d07ec6dff53daf37c2358d1b76174406a75bece6a192 6c248b764ac844f7f20317da6ee0ade57c3addf491a6309e19954df847fe0b57 c2b3c177d65920dc7e16352f4d4a1cfa82da74c45c502ffec6148240b048da0a eae88428fa3f81f6a565c6f71a34a1a282550b543d7af367ae568cde957a5adb 347737d568850ab93cdaba356e2f2c932df6a8b41fdec8b05b9104950766e803 40473c24f38722a5db64db65f713d670ca27db39726f32a728eb93774a89ad9d afb366a426ce9f1906a9d3673aba98e048aac4e4e668dbcb17054378cd42ab44 e9f3ab459f7194bc370c362fd7d8a6a75ca199b9ab4eecab528939d27dedc48a 0d4b4bca9ded630b3612d80d468b504d0c180d0cb434c46ca42c68ee98dc2cbc e1a6e0b00675b3b136853539a4e7f82877bee50b7ecc3fa26b8bef50ea81e5f9 7ffb6985097f8b43c0b1d8e645e7a7a92c44f72d6fea1ac90e429ad8cd6bc389 9cc6611070bee3ad7b9ee4cb7019a55e89981fb33689d4ff7a876d543d0e9112 e7bc5ed886bf0e3d7f6f5c226f8fcabf9d1935e7e2d609932ba263f451cd9d27 1c65db720c1282e65a0e49d0b06ef80fb5bdef56c74df1989eb020a3568bd4ab 7cc29b80758c5528e9534bf02225fe899ce2d91bcda453e046f275cbe0f1f9ae 51094251b02255d1ee35ef29afce78bcd0082a50dc86a164aababac34c46bf55 ff25ef58937eac01fffb1d4c73935ae9852a5612d26c13174f7199bf48a6c25f 69c882e768b9f5ab02710125377cd5f2e137d22b25ab1e8e3ea7421e262391b4 c4fda0318a3ddcfe2017ebf0bd023beac85fe29e49ea175b75846fe20a97d461 49eee66a75a60155e24d1337eac7f8110bcd122a9a25309383135d43b37a80ee df2a2fd55bdf1941f0a7d40293da98f050c253827d420f6e020d11e81613a0a3 d56f97969fd7002aa6a3b9448bcb83354ddc3cf148c4e9a5e36c050fbaa7df42 b61e797b24014d16d9cab92b84a7a057a1a05054a1f1de374cd61c040fcc7c00 2 a9883feeb3c7759bdb92b648f8892cd5ef81e8b33ee542ab9610787cd082670bbffda37e4c1d78206ecc65e34d62f5ee02efe5e1f2bd7ae727916073892bfb0cc34b9ee91ba8977b4a117e115025dcd72e9562f1fb66b0d66a297102d1b5780b2735a1b5dc5a056f4efe0d9f7ac574f29bbb36c0931f3048460010796cd1d20d38623c1432452591758e407f2c7425e0c45c6346aa3f2f15c3b9d8bf74916e0a5cb1ed92d9f2c4a0349c6d0f9363738d5435071efe30bbf5858cf8b437508804db2796ba7bee29ecac06cdc1448e9a05130ee5f64905724fafc67f167d68a7018568cdcc41ea23d40b5e47c51481dc0412765d67e8ee1f366314158f83ddf30ade63f8b965f1a4dfeab7a20a8c033951e0475383fb181cad85bbe318adcec806d82a7406bcf46ef1b8d378ed89c4441531a9db29b0cba6cf77abf5b0537c670af2edae68e2acde0315ade9e03fefd84b8dda69fa516a73ac3f7e5d2d366cba05fc0a5ab0ae8885b3a11680ad19fc6aef08535b31379e5006c4618f01d0686f0c68772f815274a5b28a192c5ec3f5854895780cdbc4a39368541451df2bde5509e08e5c89ef1108b52c709b40086b9618b89f47087ef71ff66e8126156badee0aa8df96cfdf6e3c22baf4be8739a297b479912dfc111ee0ac96f7472a8d0ab90c2a4506722e419f767af2b6dc21af9b880a594181a2212ce72c1115ff5e261103c018f17b4d9f71f147a7807895de86fa0db709e1a886bf709ab56f40d341a50c5ea66b5288df5938b98a852c58aa50f57fd00e5721f19438e8823b409c44560c4e44d2f2d6c918aba6e31e70f63020279be425ad9f9756336b4983ec8be200030185a7317a4a763a38aeba132f7118a7ea5a7ee7f1b47dd230d88b1afc11b5058bf9ec44cb661bb2efd16c88ae8dea66323d7e0357eb7db35afde44cfa82560db7154b3eeaebec8edf94db4ba0cf7bcb49c1ed551ad408bd76b9ea48c2dda901b17bed30edf0234f19bf489d81f6e8db6a8dfbcf8f7d2dc58babd8f7746a7607e51040deaf84a7764f9d886a2a008784d4115e5c3b82fa499bdd5eefd9696b08018fb608f5a962de97fedfd8f5a8228178ce43176f54fe30c635a3520a1b480b40ab115e0ce14a78fd126d401da8fc61a1d3d56ecee546778df63e6d8f2032018086f2e27a52ba8d4af390e5d229d84b6e5d70d40e3aa34629c8b768557e8105c251fea0ee7a1a7103c72cb7064c0277de737c9f56d8cc44488c6305ce2c9a024d033a4f0f4df8849fc346019ff739dae9da77d7fa6cbed6629776d58244bb0eef134d6c540a208b4d3ebbdc6b1ed1e89f9eb68390852c5077047c0ca0a3690874b6702a9b9d7867af37504376cc41dcfcbab70dd836208f2af27182018b8a074e9f0bb0a9d8d0ebf0fad11de4e283bbff52b9bc0ff006244dd857f079c5110fb80df2ad96a66fe907deb2493ebb690e12f1eed10bb63b8c31b3159c8910ec003e5b8eb8c68eb9eb79b2066ce28bcf12285c6d5d66c615769bfff99bd7298e03f88d75d86c03076c6fe927c6e965977c86acfe32ed4586d052d5c4cdbc2860049495b7b15a86c2717116b2b1a81de4decaabe5c7fb5f0273d023aee3c29fa20681932077d17b8661776a0e20ed6152a67612c77ef347e356e9ee6e60af37e90f2dbe217b74d58dd43c2a2e117d1f5cc3fcc7cb3cb73311055bde27cae1e17e0aefcebac8385232326c8dc8ce0c34fa8e2aad4646aba4503940190c47e575190514aa6002aea09e739351fcbedc0940e9c9ada306ceaf26791eca66531cfcc409bf0bcb53f9f48454503173730e1a76a70f01b38843e367dc99beba99cce80a00dd5cf8899201610c183c5f8c5f28e1a3f833988bfd21329a8e0ee3a1d453b40c8b17b3371c91466d6358bb452da8a661921941176e49d8daa293a67fc25df50f1a8fc712fbef5f00111b5a79b1f1cc8507c61a09b2f94ee38de4fdc7201d560fd12274a03d9f820ebbade217b66eba03ac1a01dae1600cdd406b8417cdd09c0448bee59878266716ab0cc09ea690377786286479fc4412c32601cfcb9ef73308e39d04189d723c46ae8c48347387d3a40283394089be5d29bfd7fc43846d0d06ba5ca58b3f7a051f106a5bb806ef0de363c09512c9f62043818aa4d158b74c074ea58126314e4fc8e5f53e3981bcdb2e5e1b95fa1e0d76873af19f77d58ca10a16233d02775fc775bb1ecb6538d90fa90011a156b60ec7cb2256a96faaa2a101949eb4a7543e6fc0d7b8577ce7dc009e10f817611c07ba4145dd7abcefa3280ec36850903f3ef1c4213321c4d05559df1c298f863ebaa68ed2e7107a44b83e0224bf71a205b41e82e16da7a9d808c8455c59c34ab45ab43afd1306be19870506df8ad0b23c62a481b5b750cfdf29b6df6f5b278c9e727abf8dc81c669706b705b77b2424fcb759bad4d730d423c76668a0ddc1ccc0f6e7ffdc218c7d10488408d4691c401e508b9116786bd6849f05ad5bcd29418d29861774f2f92a2c090d0ee2117abe4bc9785764049773fa67344406853e0914374fbaa538b2d5dc0e2600ef017225bd6c0398e9bd3e93d26c6e9dd01c7903c53155c07a8c069e14e3f10b89a1e23aa29a4d51421fe9009183782516bf3aa898937a10698ae076bbf7200281cf9b5974d14971f6197d38899078641dc846b996468546ba4eea21c5c23202cbe6be6b1bf39b7374dc210d21b4cb6fe43419ac175a9613bb17ac06c4fc8d0f9a8106b8f6246186cf0ab442f00a773d7ea7919f1318a1a09b674db74de12c07c5ef5fa53d1e57b24b4de90b074bc9de6bccc21182c59a689f395ca58bdd82014c7ab455518ebb04deaf5503fa4c099db04a926e0bb4871fac68cf4c46e6c60ab03923076463b77ce5c04d0f23b05447975b511a0bee07c20e9d8f5570cb36027b48f56c541863c0ffd6d92af6463b688b3ba05d018db8d257ca970e918a11056aa2d80e65616dfce7082db588909b33ef1c7a8baafe6a792f5925dda762fe04d2c635b6f3d242b7f85be4be8d694cea618305c6fdf40eb4a60bf36bd128a20ea963387912734d2c6bc8f3b849a5487d785327fd9ae42d331b77efbe78a1a90323e9bcef2465a427ba5f6057f99c042b3924cd21b18bbf797dd0dc72f5c22703bd9c6b793c37f52ea5340d87f783eafc072156bb1b958474f2e161bcd09d8b0603f371ac9a4c473691754cbee411826d1929508eccbdfd1f5ab0938786ee00072b12794ae1f09610e13ae244388107db12988a8d7b4b0c76d8653403c0af8f0843ac99c794fe42afaf463f7151126356256e74fe50e07d81806e6d3bcd66160c1b09fac1fa7432ed62c82bd0dc1d558ab52183ebdd19e004c8da11596a85130291c2d1388f21cfcb81384f8c3de43603c449b27ce788700ffdb0b80f956a6c0dae9d4417a6e4c5e12d2928cbda7c4e49f3a2b49a38d458d0c760b36d2e777a07eae695ab55ebd407709c23b7c4df7b207e8edcd828fe266ad14407925ae2c50e6fc9d57e59fae268dced5f887dccb71868a831ec5337e56686fd36d400d76a0d1a39b5f1f0c251f8201dc87ded81248c1f7f311ce992baf3080ceee1a90298011e45d66a5c123a2c75e8222abd69d85191ad26ea8022b28f8bce293eea5d0a02e7ede55ee7a104445429ade3c8a8dc1ffc38ba435777d4192f99f32a38c1e10539457e29a025f693e80b66200c3283342185adc000df699c07d6ac18b501b507e792359b3b4439ed360ba12779f90fea4548f2f748326203d7fa166498b0a90a86d73eb850e6279df24b2ea1030dbc42c5c53121478eeaaac9e603be6d3a2c06de4be622bd80db4cdc1d107afc6cea114d4fb2ace7b17fb79c642e700f6e1904c796c83866423156570c9d62c896ad331c984c2f435c1e41760d73f73a4abb06abc89fb6c36d92728211f4fc8dcaf7b27133b2379bcbb5b8b1f5d15df3c0cf05df7ca37f0a9f0443a0934c3cf3ac3273c574c00c710c2b8f042bf918f783300157e71737cec51e5cc5a6908fe4d1d79502b0d7d6e0c598603bd6c5800307bb0c1bae958619a8247d428e6ef028c395a45061583c4071e8f29f45f5d4a94e1c089e5695655789e32eb3d5d67206cc84399b26875b5d9afb78fe10c25724513d0b0ca29ec24e23eee1a49db1ad7f4111375ce4da6f9a38dfab0bc8d3572df0d40a5483ea1095e629a25f8685b500ee44e9170fbd7df92c1ec87457ec3e54279b08978b3b1c2c9cb2845cd67454b7c96e059ffe11307e22bdc1a50c22700a64e00b0063c3e37bf25dafe2acc40a327df0828cee3b0d18fc74bb98702e6d5dc7ec0f3f78e0520902cc2f64ac53bb214923ad5892578c3d4812cc115bd30310d62907bf60fcf32d3cebb9c5d49f332b4c120e4b5efe53884e600f1ab8369e226a260059bd58d14c525835e11a83a6b5afb8cef0335a7aa53034dd59e1c34f32065606939654b5d29b8f663a815e09048d7cbc728b11a90cea49fb29c80fa8e664bf0c -generate_ring_signature 1d0b119ca5e69bfccdec2771ff272dc2c8638c187733438939525ccb8005f4ff 8645d7285139bdfe550241936b2fc8a26757ffcaa7bac9084ff8a85cebbd1e89 3 8a075c4f74b5520b511ee1a487cb31c4cc93befa5af0887414efe0115fbac0cd 5407168cd81fe9cba6a34e7e30cbca17f448f2226131ea57acd74420675830fc 2ffe6b476ff37d8d6487cc1c7e5e89ba0295a5d0128e6ecf8ae64165d6d14d84 ffacc2cc992ccbdc7cb0cc1a695ca31a92f6d6a2563b03349688a63a86f8ed01 0 5bfe16c427da132edafa6533fabba48b3e8181aadfc22d1a3c351c0a4679b10f279973e209c7d8091199e7c089b2277a47e050c9ba36ceaecb05d58060a7da0b86b78f4403bc5d2293661a6f1950ca8a90b2589c5087e93db72c79d1eed89b0ec1e33ab507653bef38b9d5cc26cee03210c2523b4ee58886deaecce98033b506d2d97538d8f6f33c0b1cf66e978f4f7f8ede24dabc43b4cd7b4f3c31cbcd770f8f79d11b392b2c9befc039e0fc2ea7a3d42283b07160d741e4cc0411f357fa0b -generate_ring_signature dc0159dbf72ca06954ef706baaf14f7c84f6d6e96aa47cb0fc100a73f21506ca b0dce2128797d8cd8ea9c4dc13be5ffa76e3c68278d8721835f7bea75d42621e 155 55bdfd958e609cf23a67c353b313cbbfef67c4b9f888aa9ff96473fc9c841e61 5cc2a7c274ee46db7d80a45e09bcb4701ca0b6c651a3f71698df8681f565da1b e1ec3ba3b9a5c1f93867401ad6f4b8f45a81fc04b057bf48198f93507db0c45e d26059d3c9f334e298bc08d99e64060f1c68e8cb0db08904f3f2e989c8eb0da2 06ff2aa52649f6205f044d65d691168f3bf22da79ae46a294a362c23fd674cad 376f687a817012087ac04320874b9e169806b3a384ecbab35aa7caabe11af3af e6f0454df292cffbefc002e20637b78ae2c52993e2f1d7801fefe9894babaf97 965c5c80ee12cdc2b0ef724fea7b86aba5ffa1ef7a8d371f634177fe0c77ed69 5cf4164fd1d89da37159323130a12b0dc4d22b6743f7a14f37d85675cb7711df f43d9857a3c0ced069e7e66e99416b0f4969010ae1ec4f90568fd9bf1ab42d27 15a59c1dd5958f6b8b48471d7fd113b4a761808409dd5606b5eedbb3fce5749d 4e8c6e51b88c5df9d2f41a66e80a5b443be79333309de74e36d26867c5260f29 84d85995ab7fba0a34a50d10c6c9ba52922b7f5f1541e7740dfa7fceba592b3b 04409b6c2ee649e8836796a2d0413dae06f8f41cc352fb00bfb108a6f608ca32 9c207be7f2a862125bc38aba0e04e98d16d0d2ff2d36461c0480716a95ec0a36 39fce2d41d67bf1f182d1ccf23e140ceaba4322266b10ca3c950055b6be5b023 fef4c2cf18221ce0ff9810a1928d9168e1b88db9f42f668d74c2876286c848e3 d1ebeccee551e9c25aaf9c6f1f6334fb5471e5e94f46bbb1d80729919c5691e6 9e7ab6f141bd4ecaa6d7885514127168338ff935f1ffc56eaad972e19b807aff 4aa10541e1a9124fcac6896b065066ed0d73150c9e9455f914e4d96ec07adf5e 32ca3c20ab39e82821420d6e2e37cb9a72f19d1df5908fa1fedb306b16c3273e 8172aa6cda51efb16683412242fc3b90a3a37c9381e4109c1f00521546fced9d 2f9f8b8f71b8e1d5cb244c0f0a14caea1c5d77b13ceb6a2ebf2b600ca2d236e6 719b3d1256ab1d9f286f9cb4bac235da7131be9dd47388b91c6eac952c53312a 14a64d08f79f8b03c321fdc291fc633fbeac766125cd5bb1293028bb7e86e4cc cdb1c8bbcea2d12159ca80158b1d8944d839d82f94e1c0c3ec84c71d00a67659 aface084d21571606d8b913198308fc78bbfe4fef087b2cfdceb8c335d756d43 66502a8fcec2e95a45c10fd5288ecd64e1f880a6b7757a3921e14f432ece527a 63b5a9153f88dc28a9dd325a81edb0bb2fd73fa2ad2aca31839f9350412b357d 7c377465d285f2f6d31834ff61cb8a9cd5ab2fe99b4ff9c330e0d59d305b21a1 ced17c63ce3dba87598c1adc062f3a8af81d4937447ff8991f7c780bfd9d9631 d33c03c444dad20c86164d634bcc2689be5724091a95c006723756544a5f88b3 42c6f935724c8f7f3f5c53645123165daf862cb05366a895eca0a7656cebe76c 4950dc9ec99c46c3d605d97f96407e9de344b0bfb7b19f976c9c84a18b3d26ab c813aa66c11bb66586ad3af0cadcfd7f4678f9ce71478135b8cfaf3f7fccff8a 91f5b936445454387f607698a4b2ca38191ae36dc6fd0ab0580ca4b80b058d8f 9b8741d58259a1f279a397da968d1df565d8e133ee7c69e1ab72448660625adb 4a7c1752977c764cd5ac091751bff8dcd41500f38b7c3baf458963f2133defc3 2d2b4a25e1f4a864c3f45e0bb5808484bb51d2e35b9180e3565dd2945c13a9ed aaa64e83a28293a0f8759ed4cac37082552d3935c827293da30bbb5d3403613d 66452a289e79a5361719e68d911741f20163ef4896f0257585bb9c832069c5c3 a14e0b54b0e02a7e81fa314190c273653926c342c819d74e9aa461300f238855 1c1322db318ccf855d7a5ce5a6aed6bface24d4086cc7b26ac67733be55570ca e10ddca61c7790d94984c7ed066543eb76623923557c610ec2f8de6f79f635b2 7588b14ea4f0e4a215d9a982eda345bdbc6aa4cfd04511867c40f9011556434d 409d5ba7c80a7e1c29df9bf923ce011ef47e284c0bc462533245be82213b4b7a d5e6a2f9f50e6e03b514ed898ac28f24fbec78eefbb3dbe0a6a00627f72aaf4a bf34dbc0ff7eb6c8a74f111324fd8eeb2be93a93c8975635ee06b526ff1b3ad2 ebf00d242b0667a638b7c93c397e45f92d1e8acf73bb2abf73b03490b3b6c3ae f3688388ec40d6680cdee04c6f11d79bcf9eb4e5ac2ae253b4ba852826b76bf9 0ee543f8b280ba4381bca59e1fcfff6dd425851c4119e251a7732c99b8d2f168 d872db69074557c2102d81103935a90bdea94ed0b0200c3ab0847b526e29b4f7 d4db1b4947a3e86d998ff66601af2f2da8cc84e9d724441b464143b171d7c89e 746e4cd0a3813757876862505807b4b78270c0a170b888a3bb9e3e60c0101908 8d3606dc0218adf7c809f52dc2c19c9aea02db66e95b21a4dffe48680bad98c7 721174c2d937c62637e492c02253ee2a7defa7d4a3b6d5bd47bc6f0addf40ef6 97bc500b936ed73732b36010e062fbdf149dc2a5cce48c1a648828156c956c28 f3422baed25571091c63261b7f854a389392cfee89a8aafe67b0986c77f933f9 3224b4758162f6a3ad94d76f096029d96b8542e883a50f48d189fc9ddd927efa 8634ba86f40b66d5e0ae98089bec9c49bf7ed348c857e9a8ae6af9fc1a1f1f66 afdc97548124eefa649ccc49f8c4895e1503a0b86e4732dfe073b18a14c6e0bc 5290911f8661f9a415535158f2effbe89a9178c4194c922b660645825e234584 a79798f044fe2f369b259d2b59c949a0cff4c4eb54b5464a1c07862032180273 36f35784685306db02646934f6ee0734226dd836ee407bc83ca232539af2a10b 1a689224e864e3777accd246ff0ec5ed4f78b44c7af8f551bc9b411f8614693e 37d040f52915b13ecff79dd88fb88f109f091a5716ec289f72adbcd9d1877221 3f8d7c20a19fc64d9ca3b1eff52f6c4c062d6ad85e9b242b3d16618cc5c359b7 8fb07bf7455db11b402bf9548947b6af97803a02577e538e9ad452fe51960335 e457e7a04242381202e5ec2f466205ab1cc3fe0fa20a97eed9cb1afa6f04664c b8b4761d93cb707c2e0fc386991643d770ce3309203f1b0c8f182ef797fcbd3a 4a5c2e5aec7e342cb8e6cf16f17678553cfd06e5359be5994ee6e50bf572fb22 5c16c16457e9fee9a62c4ba6b6dd7e46aa1f3f85d68f08629ec7a96b85118869 b7299ed0fb01db3149fcf30dd1d4ba8c99b9384bed66d70c3580e042a6f7833c e6c61646a287ad0a53d582ffb300a86755164342b83f3a6e5fddd5471068658b 21e7442030bf8d818a79e5674f1b5798a576fbb1ef84472add4689990f62e8a4 7755f1fcb777b9182eebd528bf3f60f6a2da148514e90695acf897196bd897ac 8f50370b4a8db13017296ae820c4b728f46e8244824e590293dfd9db7888ba5c 2fa322e48e87ea8a9ce4f70ac45fec4bad47ed617817a39c6ebb38bf007f3802 add067ff6442a2d93b12b6de049b5fa78541e45dd0a0f74c2223ad7c61a7198e 46554f1886d0ba1aa9e264a5f29b08c957b5c2896974664b222e60b6e47e1b0d 4e0a19f0b65791c9b0b510e49c670cc06bbb324d67f657a4028ed9f03784d8d8 386291c7799ecd8cdcef7fe30f6021f42ea2918b06be3fe8df2f47a0fcf2d616 2f4a5d75e0868c2b8e523ae394049ed0ca390bc64f641ba203b9b4dbc8d41d2f 841f17f2584641774eb003c751d0ccae29152dbbadbba8eb066a2ded5dabfe98 d96e7157019eb21d9ca9ddad08a2c2cfdd752fa10cf6aaf11089c180f6dfb13e 7360d3476419e86cb99abd0a33264327b0b5171557d812bdcf6800ea0bab7131 463083e7e2a8fd2d956876dec974816bde097827365b94a1494d5f7169f69686 44e8016a6c0e2e21241c67bcc8b844793daea164b5e2a14baea66c3c7a3cc773 22764f41e82557790205bc987553010dfc5bda9d8b9cab62def4c881e4ffabd9 9a52e8f5b5cf1bcde87297728a79c4154c272fab3a31505e9de06cc22db12b20 45ecf071a3f952a3886e388374de59aaca3109b8a9424b7cc4e9844de93f3c38 04bfda9f253526a91d0a4eaae8e8449bdfa9d200346509ad9fdcc90c9bf72046 baec1c08bd22e1d1803758f86de1dab9d472cefa662c4db5da8bdc3efbc19b34 efc658768831e5dd37bc425c2d958f14c088f72d86651f63099b6a59fc956aad 8bb4c7e070bdbdcf0bc3ccb85a7f636c40f6855886fb9e5afe3663c78ad39122 a4a4c96cd0361853a0f6e464a2ef681a05d30501c71191f74883a7071b38ca74 1f0eaf33404e3d3acd817f9f8a49200f6610b730b03dcad2c75829a92b67d8f2 693c7c1e884e24acb6ea9f79df9a4d82343d2f00a36617e80b2341f4a7061e9c 57e1c0ebdb580b1ef2eefe50d96d81d9182b45cec86b8c2bd8bc70d8094c3a64 10b2ab4153737474de7bc454ad5f247fd398761bb29f665cba97dd2a446f7397 e80e6ca216d809401268bea920b105ae5bdd440577b7a9ae2981b3d75b39bb55 967fcc98c8830d8374f52e19229f50d39ae130caf369227941c97f432d6a381f 6276cd137870acf4b11d90c2aef851894fd6aa8d8ef3a2e6a7464e0daed09c87 43a43751bf077cba57e404a62eff1082d66777f1e304f0251dc884a472d65730 199e70fda56e43e548af5bff218590ec49467db20d932ba7b93490df9bb79ed2 75621e025d68155e2501289bf8caba1ee9b89e6389fd2ee595e72baf5ea3b34d 6b7e8ef6c4575bac539bc5fb12d6428cec7286e03ffe1f00d4996ac4214051df 3a28bc22d5954f34ab59bca16162feecad41b3a25ed1008b21729917e9e4dc4a 8996cb1d13337dcffb5f57547eb7c6be1cc2af29d9bce46c4dac048159a15920 7b0eb9a962b2673a4c3385f17fdf9059f202421a36ec76a2190eec8ac1680d1e 6da2909d4ba18f512cbc36a2221a2e16d99a78c6e526e802fd9fbd38b8608ddd d989d9c710f84bd67771a252197af590b63b424ad1c89d623181ee4dbcc2acb2 5f03667eafa7c0eee3c741d4aaea93246d9085248002cf92b4fbf664da891e5a 4e885b308279d51ff6ba3294f150cfa79de993e4b4fe240e0383926eb1b72a7e 48310fea7a0b977e646e440ccfd050663c1567bbcf716c18738631be861bd928 2d8e36d9c5d5bf1a121d9b86dfd22dd40df765a8640383aa3a98e86815f6b202 c2ba1b76ef0a6ffad1f816d5a195b4290ea5bc5a195c21b99a2205a0fb0fe19f 9264af1aff612cf6c5423fba55d78357d35f1507961a1e5777cf9324496ce5e2 ce48bc8a5d073ebbb81bf4e4a31d30b43869d47aead425642fd590c2c6b71ad9 415ba1f571226cc741585b624b408f244b226a3ef68112a787b7f4ab117b7070 02690c0871b65ed57af53dfe9ad9af13fffa80e17d559e3ad8bcebec4e3244ae 28a00ccffd9aa89903398ac9b36e8bf24e77b25000ec0cc05435f70caec00f5f d71cf74bd0d2adb4721b4c2a2601109e25b41290716c2dbd20cd71416560042d e497022d82a79c271eda123b3f2cb12d7e050eec55c7ba3ca5c18f90456df49e 5593d828a1c1ad5fffaff2fc0e4b82c488a4a4fcdee2bee513fa7f2b8ca21402 ba7deb31a63aa2c63a9b6de0d018061bfd5bd1305de6cf9be496f2d0b38f0f1d 2fdf34d6c3f7b14cbf3d6dc057f68f9238a01843bd79f65c343f8888c57e032f 0ec1bebdae697a17fd38bd3c2b1d466c6a2cd62b221663dc14450fa9645f8f0d 64b8735f9e0d05b013bd5649e8df55455cf90d0a32e01a5bd47ce4bae2e2663e d3210f6fb94b6157defd835fbf6e24f335a53470ccc876c8f24b9cabc4bdc689 854d5cc84664a763d4ba94fab9f6826e4b6795b2c4c4644d1272fafcfb38dfe2 ef8341e4a2cffca78f4f85382c1bb11231f98e313e8b4b158f1f3858e0f1bd89 721bcfd1800fb89d20114f080b739a7f577466853064735708dca02bce6c08bc 2b21a17310ff3da504aba575ec91cbeabfe7cc7ddba637140f2b7ea4a087e5d2 c9002d13dbd6d60da440acd4e2fa2a96a51cb18380b2304c45f8d55d9e3a23c1 9c5743359406a6ff0228502eea24354ea96edfeb39d4149612a9f84320c3752b 4fc972f7883ce8ea63a0579fc59ba3626876688dc96b3b2774202f8c2d514671 8262e5c2fdc9e2d7add1a28b1ab3acdd49dcbbd4ad6b733de427fdca8bbdd9bd bcb2d7f1ab48ae12ac447d9006b14953c7b75a45eba9d7c28f3b596ae67d90d9 26cc343804e2dca0d14ab0d2a2d7d03b794576bf270c1e79b737191a78dc02b8 9acf167693db32fe00a5d4ec090129992b39c269833c6666c1946d8523bb054a 507659e6454ec03bc6228a469717eb91adc0c14f2dafa266c56522e037bd543f 6dc86ba6eb6846cf225842fdfcdbce3b702041a03dede7e5003cc402df63335b 1677fad719b74408ed9e1b6e2f9e5e6f0d5d54f850f8e4a4307c3fd2a5db6d0c b27e2f7563d41739dcc27a208e3c1cc6d7925820e953b36906bd63456c23a4c7 46a4d38ba7ef139dd455ed55953279c6229eb792833d5360a449e84d31ec9d8e 97d72d2c3deeb90f2b7bbea277bb5409dc5e185443550599dee57628b152cbe1 06cbf2f8915bad92aa93e100fca048910bac864de2e67b786924a5dfa47ed2dd 5f01c55600492999cd80dca1fb95bdaccc1ccc4807e274f5e2d76780a62ab9d3 bf3525ee75abe62a109fb35e297a6ae6ab51d430756ca8a44c2aa0dc558623a3 3a78768168d7f695ea5c96485f80efadd5d0434c256732af421ca95921cbee87 1748b5a61482181b63b42252c7663b9c19d3386000b73e440d49a2602801a192 47fd23ed6490a2ca505a01a2a0216e518b5459c621df2fdf9f4090575ecc0819 22cfab5bcf95785b94f17529b1292b25b8ef368d6d3bad17438c9fc4c41709ee 3309c171ccd892d173085fce4bda373b15bbd84719baa5c86b00bcf2c04191f5 6e8a6e2009014f498970673b8c94765896e485f7f5fdc8d36c97e1daf93c6003 77 6ed124a282f8829187f057f3be05abff550c0d7c84d1487f3e97174db5858509624c81024efa32bb2b7025c489bcd475ba429ac4a5e6245441bec08d3489780c19a17935880042aeaf5fe860f5b1e2beded4d5c96a59b3f932480b4d2254e8036d33532254aa78b4df817a0ecc86450fbd4c5b96c749e1cd3ccee52d75d689080ab4409b915be5b9962dc6f434d94b077d094d8ba750f9cfeabd9eab88b3d10ccb65294b0c906ef1eb6415b348bed7b291b6e7e194bb504a812af0eca26d340facd69bdbc17c5131c5dabf2c6974546a663c5f1a149a368ad7534e5954275304982dde0205b44a2feb3dfd9dc5a347f5c08c32801d53ca5dd4f687880c527e07e89749cdef37d5fbb2b47cb1f105b7de373d3003b9d236b82bb15fcb95030d0f31397ee4378d0644a90d3727f37be1ef44cb4546a74e5451a145bcd5cbb52b0f90025a25fa0b4d2a7cc68cc4e3dfb058eac044a5e67ad6e4d3a9704fa34a8503044c9e694e24c6529e268c1c81a901bd2df74c9468f4eb505e5e2c2bc837b60d3e8021d8910cbad9d4658da634a98957c98b871e45f9ce4aebd0933f0c7a9e00b94ab50631ca6608315d822e32a5232c2c9096c3f8d5093b480ef93f91a4ec0210c38e63e3e89d5f917581c81208662184298b3361cda328f478f889bf298403beb66763678c517c3d66080e33b58751990e3d4f4926c467c07ebaf569d7a90be310dbc146e7589c2305682a2bbc70bf5e43854281de6bbc2532a7df03fcee0014b3a95cff515016394638f4a41266a7b9087893c5d2eda94d002d7f146fb90347dfae04fd63d448062897b66c5a096fd1520a4224efe564b6c43633d9316f06abb4cbc3824c939be70c539d39d8a0d80a3a8a057946ecfb8acd194e963324066ca3716af7f23a131b1a10fb021542c734b66695e7454e1c680ddced280e83054b954ebfedf68d9082c5e735f2c1c368e9a446e70a2fe37a07d9d70a7db7360e787d3c0b9b304c87932dccedb67c88ed1335293ff600c1b5ab704c0fd3937a0980b39982163bc020c9204cccb766d18090effe31cf660c64f56ef00411c309061dbb75f2845d6d04aa31b42b5b44914b46ae9d2e0c4b968f4e240a09e6daea0983ec6f3c3847d3e5491aa0ebd2500a273eedda4061de617ba9011f000ff618003efb3af7dd3e8c24a0cf62e1bcc6f93a1c6804be8fc9062a5b2924ebfc6c930d286830053b8231e162f2cfc866c49184d663e23fda8c0343930252c1ea968702fb9cf263a3528c81f9a1bac0913d3bb0941cea9d2e4e180476be255e2d39360c6a051e2a1d05e7e59b72c8700dacdcbb589f60778b6149918a939b450e6523072d04ba1bb2e562eab8eae2b81ee7c9b06e00b3f109cb33b5e291f52f8c1f6b0dac68eabb71ff42ccd42fb73e0d2b534ca7261ed6742187ad6e52c5a6986e1805fc222a8f3c647253180bddb56b5728d92bbc899375940ad7b084b4097d0cf8071e3a10b2e3d169a1f6ca75fb5934e482c9594c630ea149cfd5b99ef98c42eb09bf56c488f2f1a6fc474226d543c2357ddfa2a53620cbe64a6475bea68fe6d20c8673de4104773a56e060950c153603dd7a5f17eab792e8edfa5068de5474df042ce843906e41b6731cd52ad6c0ab289746ecf8cb4250ee8f32746a9445d22c0d63cce0c57e5ff499d15731892529e47320c8c595a96386eae0251febec43d2044e7651db2786746140d8aa50602b03ef2eef3179db4746eb7cb617ec42e36607f600c6b58e475974de0f0408e3fc1cafa8582f6146cc19f3a3bba30ae42b8b075d5adbc03117ffc01c3fe92d7700f7d9b633331eed8841111fb98a8ea834b306c591da878a1f5fe45b559e5f16e571a736b2662bea27969ed8e93b5cb2116504fdc2acaf844d4f0765d9119b5cb5ae360065b3f41fcb07766f54faef0633470413e14b5a21a588fb495447ed65f39ea31a010180cd7e7f5a6285ff20beec410a3668e374e63c903b775b2fe68a3cc29ce1fd2f733024b08831c64a187e58d90397663f07b25338a0338c2a190f46fed77ef80595d03892a3bef57a1d4c59d4079708532be33f747c6ab9cc9eb8fecacfba783829ef04633be95cead665720409e3c003c108a0d6442ce78abe5cdfcf1bda948c3fb589b7014cdb53798372370951141845cd3a79f98532e654c0379c754ac497080b246cf361d6157b7ff4920a591cba28747affb0acb86cb7d8ef4204cec998ccebc399069b8d516592c382098dd2b376c5cc4e9305aadbc741933397b7525cc325e6af5bc4d57848a961c909fbef9f7cc00743135347bd8c3a5253bf16f2184293166913cf2f46bdc8982d0eff4d0564c6e6c21e8ea64293fc8747c4009924fe10a073c805a67dedb37e7109c731a037d3ad828eb95f7f95857dab2f48cde42ceb4b670badfb57833cc6a304234e760982963fa85744f76fed720ebfcbb35dde9a4f7eb9fe4388fe8a148b08a49becb888fbf72fed3c930616bc9d97d6186afd8f8c9e7ed36b58b1391c7a092408d3c55f174928576fe5668980ba7fe6daac071090ee1e2c60edaaa09266075a71b56bddf9dd6ad7ac02eb9d34f1151b27ac127c455c81e09c29a0b2c02409501c8bc40c971a5617294399c16b90c402dd4612c1ae43d106d4306dfdcbcd0515872bb1895dd03b2a4c5e0d57c8ed9916e80d3c79b2f12da0257a7df91b160f1af96ae243612c92f68873f19d71166d22ca2dbab0a5f24201e736326850c40cb1084c462840234ddd94fa16e838a00535116a7ee7201840f86844955f3ad90738ab21db16ab0dd562dac3d7073fce2e055d51385bc150f44a8dd2c7befc7d0164448f0396320c82b47aa27a08729524998b7f3c998c78f552e3d296a3244a032a55d613317aa5c8372bbed101289b5fb5b14dfd58bb74c0f0d9892df696cb0264cb8f55ef439b7af0e536b5903a39caf99a6cec4aff6b96c0dbf26f1a43080eaa73456a0c2ed64462e6dd5352c4f34fd2eb2c9116ca781031294c920afa010a9da0962fbe102e929134409c7dc615522ace308e99d0f29805d4ffd36b5e800d34e7d08f02c8466d51c5df1b3fd0c4a391aa5e71b7380c530cbbdca4fc668703397788c4f82a4d1703ffb5188708ea8d3643f178689553fa225e6d07f84ff10f647e12f6d2727429974b7888dcaa6c71919fae95d95cd763f85b2c543bd3e509d79aba261d8527ff6b3f78a9d75a8691ff3f651236362b32e4e4551161818d0a8c3bd248ab505c98a1ea9e20fa5cee5912003eac7f1c760a8c8e3d66688e5f0d64f71d6d2258eae6c80d6a8a3f298ee54aec7802fae27976fb324dd81a08840d82f267729290b44ee61340d17a14731f1608c5468a6dbd23d7adeb5ef57bf20ab7e8ef543c144c40d70b658dcabb6ff940e61a003fdbd7b8ff02cd6405dbbf0c99eb7f79b11f702bc2104f207053afbbce73830b58de2a220fa3c91a18e8ca0386836a2daab06a83bf996ed07141f7be49f27ab30a29db973c682387a76da704f0bf9a7c60b3839e69958f2a7c4b47dc13e3c098bfe8a86068f81e6efa629603f3d464b7c2e924946b577d54167e87e5987be464245cd00f9ebb938537b9080d84a9000e95d765264013845ef8af1410f0096bc86a1962db82d791e7a2000c04281324852ab0505356ccd01cce2b7104537aae503113f807d624b8a90c96860b1ef5e05b5460a0c857109cb8f10f7995047a5fc04178ff30b891341614569c0bc186075b41f90a4c6ec76d399355236e35bc8eb213041ece7f69433eb92f030ce5e05a6613fdd610c169a0e7f6ea236a46c673f87c1864a219ee964e75c1b90d7560d3cbf7ae08c6bb4615d572387f57cf9ca1d0e37cc6b1961637f533d99c09905b714543ae93a057605b3559e827cd4b643ad5ac10e966ce96cdc057068407876fef9acd567a43e614c6c7014ed57acc0c5b63e14fd35b7942c28d9df56402f7a1fae9ff70a72ecfcf88343bafce9af0ecf993f545e580f3895219b816150d3a4e7b0687723461f4a0b86b1e92521c1487ae43f11de2f2413fe1c79d4e1b089e45be81b899334469396b4e1dd8f4d992a2ecb4d8ec0ffb92620a108269940cc61b135c6afa449bfe75fbf28f20cdf9673082ea35d787d1f2ce39c3a34c7100214164aa36dbbcce1e4ee80a3ee40248895969321d87bd50f528d2e73e015f0aefd601f242b6139c3fefc0a4246e1d75f813c70ee453e8837d1f205a03fc9a0837277e13bebe2c0cd77a15f857059bdee35f3274fb2bc9ae6efd76d75ed84d0b12d8ed411db1db230d962d896e6ac08d1f70f437132f25ef07e8f116ed81dc08c6bf7653bae9a14aa891fea441bcce9ce169f12f4ac51b0d2c67d8db1264b70af5840aaf61d3765e60ca456f303d7f2631b8460ac1466524aef8fb6a0d97e803919ba94dcb5f9e8450a7952151aa2cb5377bfcd70a0086e244afb4fa9b8abf0dcde3490bbd65ccc555ac521f00be5b7c389b700e39f04c96bec76c0d655b260ab1cd5ad8aafe3b584013e012c711d553bbfebf83a8bc447259e8906d79bf670ddf67e79defc4341e7ec5da7213070208bbf75cf71b3b95d01d5159b862046f0fc157e74ee11ea64d77483cb24f67f46054df9d88381110a6c0c7783daf0630017c0f64f80af4840134ddc41bee48433f0eafe115f2da0fe4da58f22d82758e0fa43f28a2319ef6a65e20982bfb7208ffc9448262386ebe03b71b29142b39eb0c9e4f4cfdb2d824c73b9eb56f4bd5140bebfa2c0f7728b70c76c400e85d9e6308ea0ed7f7ae996ce6cdbc3ddc1ca922effc4cbd72140273bb0f3cc0e847a96509f13ad61fbfdb37e133d87b2e158640fddfac2912628718a247ce001edcd8bc02e75d366448cb5ab854f91cf228a531610334bc60fc079a7ba481f834b698f80b5b147ca191fb46efb13c1e62ae2da93b4eff4c9e7172b367f7558365281f94019cc366ea25933f1f5743e08bb7927ec1f84a2c3f54f183da406674f40dc357055ba015429a174189a6bc1e0a0045049e745c8fc96f10ad64651c7e0adffdb702e3d735f3431759f550594aa423a850316e690f8a338e1b4d211af40f0c11f70a629703d806aacf268edf5743816f7524aab685777ec3709835cd4875e62e610e75dccf22630231deeef029f449ab60fe912b40c3d9f0739336022b874baaa606af0410252907213e8a6ca4e445c26cca8c75f45e345f9140f098a8c6d15b6b098a412962d17c0f749ba406fecd2ab385183e76a4576f596b96fc6d0aa03b2b0720f63ee344668d30ddd0552b9343c210af3e24d736fae800e4fe41c0d27e1f0836c356a427dab83bfbc3823e77ed20f2b0a0cdecb7dbed59cd98171c59f8d008f9f389db43977255b4bd549d45555877b6a5cc0c8fa8974e5d83e9d807e7ab04390c0b3e0680a4403fc2950443bf969dbf09570a59bfc69119b0bd6b67852d0d05bc63b573160a371581aeaf39206222b218e58ef12a3d061b6e47214f40f6086b20269d985f2c45b541f893400445382b0f5b4ef97b6eacfbedba87b21ee60466d64dadacd2a9d726e0c46858b76ffdee33c43c2310d54fb9466f62ddae1a06b732e7022a1aa70b73e9971f2e5864063d286d22f4e5b211942f95e746c06202bcdaaaf35895f67483c7588193963cbedf3cd41bebef90b6de9dcdc10716c508be91b369dd43d7f722eadd4a7c83600ff60ffa30818d4c54cc51b62426a03d07c27d02cbc33f39c5c3c42ddf6a29143a1941dfad91807d8bff30803ddd281b0ef13b55bfcab6b2b04231207077b98d1380580100d6b51be896c81816ef5f9e0ce8412fcfd31ffe7a208755cbf595ec539e8c2743f220be7be026f11015a3ab05eb3e96e753a17176af427b1b60ff652233afb4baab4b70d0ef7f46705f882e0423acfa4141d4bfeba5e007598b9a51c091f4a076fc33de0747ac509646c7e90ec99a8df4160d01f797d8e6906e662a75d1603f7256a626468020701997cb6d04dff67956f5c93d1553af5f968ca5d08a73f13831fe8c52560b69b102c788680bce257d4e54ecdc0479f0137b18a5f3bf4b82218467902150a627ff46c6964a0487ecd11e6c5f687fd2c9a61252e5ded1e1be8730b2701faee8ec742c21bb610eef894feb22fcea8e8b28b70c8ecf43395a11c378b5913eab2c2e14dd68b4740df56d7107e128e1bed3ec0c0ecde53a45bc7f01db0b6a95137ac93432d743500315a7023b973f5dfbb06084d36e8d19dd444384ae2048ea07a4dd5072e1c4d904a4ff4e2a5a8accdfa1241966ba860125fd526876310eb136b5fec6b5b7925a0580c341b459f527058d03d136f597e4928057b16b431d0398ac7a6580db726d0c18045cf1275e42409ce8a6e5f0fea2c44bdd9780e11a55e003ad00e51204f40c45d986b13148b8902e07245b4d31870e2f0fbae75939ce69a062c1116475150fd4422561c5806ac96c7a21eb63ea0c816a94ef522ab6a9c00a14cb17439967048316d6c0a9ea316b889a836a7072dfc4c27a1887f3f3880c41e7a3e4cb3421094b71f4546cec634d1767a305467b70139341076f8a1064ec13b487a823ff2004fdf01f5008d31bf3ec400b11957ed46ba980fac40553b6f99ac0c919ba0e010f913e91db1490716bee13bffb91efa33e9fdca799cca265ea702b19cbe7891c0f3e2a2724cf962a195edac9564c8ecc5afbc7dbe6b9e7f1ded7f64d6283274f0f92fa23fcbd5a5c9d906778688cc70917574dbfbece91c3ae1c5422ffafce64053db69675b59569fff338b3ecda90f4da91a8891cfc45e27301bdac2c0da24304b188b1d24f6c41949c71fa7aeeee14dc472f29d4d69c57bf797fd1374e690a06555345f54f5f776580b0421baad18d7c247fa5c798c61d4ab08c5dde4165fe074bd1e502a38e7f5611e1592038a3677bb98f70a21a1128b788f02d799c75c40937b73e1592cc542e4f172d4b45e9bd9e925ad54edbdf717e8759ede3a293130021ebec4c910a93c2ade95d4943c6fde6ca9066d4640f02956ba617703e92b10c31d6a6ebad909c8afa4092f033c0445d39a75d63199e4f5501cf019dd0053100b4d07e7e3c5b585a3793f4dafa0f1eb249187291629bbbe07b1120bb4c998c0e05672a0cfd6ae5ae5ab01327dddd050584355128ceaf3dbd8dc24250eb057f00ced13902eeb3923861a156b4c386c6bb9783b28d9544409969098035b5754c07ab0be326688b1ab498ea554c167cbc7c2a70b43d5ada6d9d6f82a77e9687c007691ed22cd2fa497d4d4d0aa9406cbfaad92c9ff9fa5d556ef3c8c9a6b86f9908a61cadb6d46ea11c13fc645d0895e836307bd3e0bfbc8605d8233609d10f65099f636e144fe7fad602726237f4483736295a2fcc7c2b1a391b0e43f9e8212d0aff611252392f60fc301883b2b79db01cb42e264a0a6aac5c55c4d2d86c9f200f558ed2ae46540eb898737ab0a9bd659a8935f59ea6e042af5af7645153feaf05cc4ddc4a4d87d56aee105a8e24baf2df9671d4558ffcf1108f50ca64734c9607e6b8b3f630e4f360ed255a8edd6ca3721c8dd5163ced4756f3879b02c465430cd2f4cef54cc8a87850f6369f663a872589fbda06f51f7b6e2c21ac5bc2647b08e117d25af1c81b3b0a5ce6e3926de6dbcfcf47e005f8ec51f4e70d17ffdefc05299e0170738f842ea1a2ecedd77bb906d34523775288f741dd7b550debd1150ae99009880bbcbe470dd540b2ae40bc229d1205132909b3ba92442a79f3e29f0a4167df7dbe522ed5b59fed69cd7a2b1283ad167023a08b4a0b43c8e2c4caea07b4ce8052dcfb3bd90629e56bd7dc1d7237bee1f929a45d465ed6060a76299d09a6d627703026447b75ed8ee9433eb9180b79dbfc5c8f2cd380889d551e73c0073992d58a166f775781692698e2f72fad4af930e4364ef521f8a6cfb62be18a07f06ea262ed54f5eb56ac08acaf5a1f56b7f30e6ac895e76f356654141dadc5009cbc5d1fdad54d8fdb42f999abd829a044dca9276cb617db9d40c32e36f6d40005982cdeaca835b5c0fa17dcaf071b5b9b16e861c87e722c6954b5cd07334709698327860298cdd2d24dcd6b14d4c8079247df13d85a7d8d3ba1f85e5bb61003bdf4b0c42173bfbb801b570af0b232c72ebb62ffdbac0459392630321cca4e0a72e7a850742aa0ce57293bd5f15b760733e689933d03c6b8c58ca5bde61872069e59aae1d0239aa733e32812b46d2ede15303b8105c0c7953af0fc968d8a850849c59070c18e261e6c519a1075e4ea54ae7f1de44f4c649134305918f526040e2c5b055f8f8658613ddbe6d2e772d60411939bee95c0880e0c14953e718e600d5f39d7d40e865891dc4fa8b96673db1e934b900721c81ee5613ccdeb8c73cd0282ebf8a3668f7f2682389118df891454988c6acb6eea620e57b1396d45a034093aa3daa979cf8b586d702b8903e48d206db35063e7f2eb5257f714fa26c71908e2925b96066ee9076a340d8e3d3bf236ff580a279d0a7a4b24bf12755e54c70b9ba85a969adcad23dacc76b52eb2049372eb64352bb2aba6bdad0f186a287a037b9438ad6472218ee5c87c083fb7f9591402b5c12b6cce6005b3639da11a4a06559bd97bca04a566a7ac37e9bb15a0e1513c54ca86889a34a253e67dd9c0f50e8b3fa9a19bb7b53e3510f39ab1195d38b4e3e8395d9db34b6220d23f8ebedf006cfed08e2f2d89f9e516ce9a37a5ab88598e5f210c4a0fcaf5291e1f0b6c0c07254e436607659332094de17d7c75ae10b0299e091f55000093e93f935886fc0df835c4292696e8abad848640923eaa650666b78236a49d444a781c169d766d0e51669d878da70b76881810f7d5d110bd174196981480824eeec772ac1c3e1d0882a827cfbdf5bfafa9d38436ba7ffa92fdee6fbc19516afa70099a7b3c062f03060ee322fcb5e816fd354aa890a010857104c73be7685d0a8264edf7cd76fd0b304e0ef7537acc33c0f6562e412a4baaa4d2a53e9816a2d44f5c0d80fa88f40cddb5e96f2e25e883be6b425e80448acb45b7eea48c8f55ffd7c7894e69201a0fc2953ab7786108e59cc8a7bc45c2d38b242f24dc812a5dbc66b68d6ec6447a046075b80527163d5280754260b5134eb766dceb50112efbdab83d5f13c23b80062adc2740b04492cdb81a5faa3993a943631900d4bc6470d71a8a0797f70fd70966669f5a71bf6fa3c7f38b76a3e2c5cadae94461c658ab4f6c8128fc78114b053497c3363ef9eb3739bc78934160d54eea90e7655e4ac2a75d0fb794e2d6ed090d56bd7cf49e4ef0cdea176761dad5eede354e493d5619bfe039735cf5d7b00f88c4a74cc44bd54760ff7ed463d212dc8982433cd29e3de3cc27c2502ca07c0a37be873713fa8c4af0fcf4522400f9c9a563c27c9d5c281345a757674c910b0c0e45e50bfd9892ad60090507dec28b60094536c70059d21c1165b69586d558053dfa071ba62e5217dfc016b8bf1b012b5efa7f7042f341d3ba65e897783f630eec83084967a0bf24f7f9510b0a2e052a880641ae7f425ad6584b43199181db0931b1b0d63066796a3542ca15440e87a9e4b08128af9e3ddb59f0a304ca2c800f8b7da72f0414dadb213e46d301ee73187a73da2352e976288209e71435484c0f1f0fb0a2e14f2e45597ed3202114ba2f8d24d4a57c14ebc68881fe43e19aab0eb5d6015818573a783e7eeecd388086b370915be847305dd6fc732f7dd7717b0823e5220f98b8aa424631d0cc208256861850ba9e3ee3ea03a1d075bbc6ccbf0d570c6a62f145a97747c46ae575c3a4a281beecdeec1fe05eb5dcb23148551809070dfbc10878afb2c8c6ca33292b67ebf8fa0af6c2e6b559373fe0ad26522f0657299145d492b3369a76b8f9633fad5284449d3f30b1b0c7588b2c5bfc139403aef3d35d33d5644ce645edd01545f07d11fb49ccb62c408fb8740e3659d4c90acdbae1c6b9d6a14878e241d9a8df447a16917db0b9b0a2fae08d487e9feab10b962d2e8db5bb524c298ecb7e085fa1b4e570600799565c68d33a11ebad0d0701eff07d0d4d036d8405a81c4baee1b1755987cf372dbf2731f798e749765f870926b9eee96ba35fcc0c2f443290469a48df004526f99c847dba49a1f1c673610ef5743a089704a1bd4bd8abfd8f8a5e539180adabccc3e8ee1e149f5ddda1d9001e9891038eb8a6325cf6df79877aa5d08935fa588f1618583579811628aaf30a3dd9b0d0e1dfc4b26c0fa890bb727d7631bae8103eca8c046515c6e41833960307cff5ee5f9b328d455a97b8ccf1c9f2c43f35369fa637982c9063930ec98504f3fbfafea3ee3092345437b64d747f8c96031a1ac2b35ffc7b10dd96162c570d617abea710b3178366c4eb06675fa7cb9d2d82f678b4c8d884942cf4ab09c50e6a672354e32c0f8dd76433299515927696dceb536e9ce1b5e8ea57c8038e280a2cf75626e3ac222a8f974c00ffa85e090d4ba8f077dd8151513332fea3b564082f0bae5f428ece1ebcc1c915b58e6a666b8ec090c683ea1181aca8a3e0088708173205a8faf4f2c12dbb6774345abc2c32387c44dfc6465190de6885920bb00e986b2042893c3d09440f944e0e60c33e2ad3d5ecfcbf52cf61b45e81bfe78f0b8ce980bfc044110f8c881b6f394a85de71588113a9c042d58901a90e1741b90ceaf46cfce352dc8c32b42b4494ca9b57024080ef556a91f8d996e50115046604ed6925c734c2a485beb1d538be64ce11158114b40b7efcf6e0d5af8d47fe0609eef6386f45e4ddf0a614fc30c0527bf05db82b3bb91db3e9798e92e456cba200489e94dc992f0eb270f945b884fca8c7d1d0c026a4b7da0484db231a1bb11a08b1b7b8630f9ac77b8e2f0d417969f1d0eb7061317bd7b5e970774d3fddce3000a356632977dedd25969d8531704c396c6d2397220abce9c075e62e73044dff0bb568a3663e097a54c50e458ba1fed1241dc26e107758007d18a320c961a4710f8ec342251269a6187deda0d23d4e2c26799e030228cfda437fe4d0edcf6c000ce96580de7d02a7bf779ac2ca8fb2ee9745f85f1d13200b31dde6acacdd2e1a0c50550275ff40d97f4f7a953ce335891467588a8c3c0ef2cb98fc5f07d3a11a0eea798e4892c1028b2d2b2ba9da22f59f1b3af0c6cb6e5cc281fdc146af2e440bd92bc680cd006b1cd702abdad16b243fc563ec18e626b377e13e34edca15f202383877e6a66bcd2242d986034e900017e067812c61347700f52089b4b4dac5022fe8f48dab038158497fc90e3d5561c9087164c7777698b025a1e55c88973f07b674f1a19d049df21cb4820e68d35363a168c3c8154cd6a124a8a6d9c28a300829c135b63ef4bec920e61945591767d81e2ff3e4b2cb7228ba70f8c4f3edc2095901103699ded0090d7ec52cce87bb79bafeeb7cee3dbea6fb27ebe6a7bbd504e3abfc5acd943cf68c1235dae8d03eff0d3ae2be13370411735e0fd619046e03b2069b60be4da51b89e6350187de6d72761769c2831d2f6f7b145e0dc9abf405e31e5efa6c7515dbe6413cc2a5b28a848208f51b9bcf1985bb6e43dbee52a20456343572e7e12e709c70c587f71b1b3328090e626c4363b47815bf347ae95502b1b8555c7865d87939839dee7b41043c37b9f5aab92d011fe2a2cb80c84b560024538bc62dbea125f7a820e7c7167ced3f1a7e98ff1a55ed8ae946b4a3f0d200e74f21d8a47b575c7b6128750edf853c444eb192760d99b63060b11d976d160c72c443c20e6c88b2d4e812ace55bec7641d12a6bb63414c349547bc900d0ba0f8b9687f9af3dfbc6c956990f55b931b043977871da080e0ba164b42192fcdb0b5e970f2216686927e835b295d16a0636847b4df644b3531483c18b14ee1fd003c744a72e1b69f8214c1e6febddadb5d611bdbe764ff96c43ef2b1d4b731f640f118dd99c5ce3f72bb62237498f769e0ba4dd4eeeedb20f83d94b28d22f6e390e45631913308957cec12a0a0138564df776b22dc484e5fff20955e29fd2c43207e978b3a0f5aa83ea66f3cb32cf11a55ba43392310916152da66890d3a0f7a0032b038dfa13c29c09f19653bd49cacb07f7bdba54105e3763ec852dc0044d980e9294a163c19b9d61e2dbe7f56544e98b54f9b3c883809a70114676de1180140c8fee2fc4a9d41024b465e42e846bdb65dce6e552796b8c8007689c1bd8d7760060148c11556a82ce548ebe1bf14b14cae0941fc4521851086c28ddfc028fa701c452dd3eaeb488fef2007d050fff01eb65fc35c150302cca0bb75ca0b46ee101d174ce5ab5cfe0d7fee6b930e4906a2a318dbed0302581d9247cd7ef2a2ada08bc869013d74ab09b8ce0054a28ec7881af2e76ef8219a1154f854ce4564bae057c4d669367c47be266c1c4db887c44560627d951da448b699ce8e7bed7552307f3e2bd33edaac957319d27742b277f40cdeb71b7dfd36d48b462b851acd3400601ea3a65ad898933762c074bb90a5c58e32f762258f82acc9108ac47a29c5408e0fb9091328e3ccf0a5473f23abe1a888322a6ce903631db2a31b6bb71880a02e8a0eb5676569d220efbf43e03f56d11e5e898a8efce7ac3a2389ee2acff4a019ff753f3762b84ffbdd5f4c52275f13c4b6e47cbd5fdc8a5f34c75dd7e63f20de9d348de218a60b44892195c0eb7db4f79a3740edbc712f9a548a4532e18bd07eca1387ad7a27c2234c4a1f403e9f419cb126bf6aefb1e4b27c22f9ed19bd208031a0494b20cc3080aa7831c5a88e1954015911d86c845f2c376d76354cdd0039bcbfed762e2c94901d2ad58180ad864480503e77b070a922c169469abbf050bec24f6ee0e1eaf7e59604ed06dd99409dd90361bf09fb810bae92d88922e0e02d41652bdf69122c685b09f507c65fb16b2d0a47d9a95fb61a4a9f017db70510f9cb0243cf1a77e4064e6ece8a7aaf4d0a2f6b771d4561be4ae3a15330dfe6f098450544f2dfaf9435b9da5f5b67da2884165287429f9c0f990511ffbf86c050eb5579d52ec9f0887558a0ad0ede1829c039d220420606bef22f7231befac7e0ff03b3a92ac4bd0518e4125f15bc0d1e90b67245743218a0c11572aaf7a54b60efa20fc180ee3434ddc16254a8ab18fa9088696f2eba873e95cc7a22289299e04e1f634cfe30cc628d4e8e6ad859c8423c820840334bf3453c8a90d58939e280fa8511bb9257c34f7e03c749352ed0c2688da08160b919a4957bc9e1b9d1476061671997e5cb5db3f882c796f5122758f486f54face99cf15fb07418012a3120f61f9f8c266fba8e7189eda9406fb77ca4bfa6da85a95675ad6010f2ab3f7c8062792648a723e7b07749c33dc5209d2b24ad20465d427005337f1d4091e4a9308e869d9ec6eeefdc28bb21e1a7d4feb7ded5e35e51c0a160b5dee8bd7137824003b5b7ca82b2662874c7b788576adafffc79af0fb3db74ee56f3dacc85cf3f30eab2faaf1582f770197488bab88864b1296f8aadedb919db91b53269b6d0b3e0fd46f00600a081234bc08b09b15a1d7a6719feef5c2d070546674679811f120038a17418e3a964e45b0e28c503628734d6b54e0b2f1e36ea056936dc95d0e3306debbc92a043bda6c5cd93509aab9678be2cebaae3cc098c9a2f378e4ec85d8065917bacc357d87b2673d21a15a1831bfd2a0c834fd2b093b0cfc128f76ddff0f48c86e3032e53cef7ea90a9a23a522b23a9ba03a26df8e61fde1a69666d43f04f633e20fd09202dc2946f66227ebcb8e6c898d5cc6f36e8b092d936017c517067352506c0c6f82d59854d6be9c73f2bdd225f5456db8c4ae8de96ab95b38ff05ecf8688a88df3f5a8ac8df44e02c16cf4f25d85c7c5aaba0c33d057de0cb0106f9b108e08a145148dbe4c55e4dd20eea720c54e29473042d2a529812ee2d5602ba370a542f4bea7e79f480f3e110689422eb1f356ad3968e6047ba28b065510f -generate_ring_signature 62d9fdf998799ad5f0044a6a7c5bcbb9deaaf78ac678f0ca3750aee518456f70 e85e6d34b7a44c497b42634e4e8be3a56e714cf5c44792c29204903769a21091 16 21b726d56ecad082666ffe11fa61408df99af9e47500483421b47a34bcc410c0 314d10d957af2e4da98a98de73d201ce595460aa9f842237ccb0347e9493d3cf 9cbf055445a0e7121e267b3a73e7449e9eb5915b70922f47437dad8f4ec9417b fefea43cf649abf557a369c3f7a11d4812394d1def349ab23e7c2e44d019288a 48360109d5b6da985b6701f47397ecbdc59d97383be7e9b067b318acdd109ecc a97375453701cf393e2a8e95e5b6c2ce365c461eaa62cf1449633e850b5a889d 7ca32230254a075dd5d166b06603ed21cfc4f5215c94bf84ce9ccf3721c44df8 14beb62093b4712b058f10f8232884896e4e85d2ed8ffc1fc6e03b6e1080e2b1 a453729d18b0d90e519e241710b05547b2569dc07ed744dbe3a5c97942e3ca75 5c9896d9305a062aa12eee2bb9b6c8e3be3641528c6d19847081172694d3594c 574a15a29cb4e3c1b244fbb9e2bcaf0cc6c964d19047d3dd44efe0d5bb5ea1ce 0980b5584b6a796bcd0d1e87ecf3be9f1615413e3f1ea237bcaa17108c11a5c8 653a3daca423c001f6f69ecc804ed50a82d0d95cdbdb35c7483fdaeca547d807 18f5a832f606874954b4316e40f69c9df460e505c0a7506f0d42af45c58b483c cee4f248d50d0302575d03de3e792cc5ca9a2cdf58abe5b47be7e1e4a801771a d63afdc5a0cfc9a531b4f099fe8b74b2f9c758c39c1cfa3fda7def01abb4e422 7c23e8fc27aeb5d238494829871baa490fabe516081fa922f3a4161ed0e7c502 10 0723c704a32bdc98cab7f831603f20d45b21e84c7b8073cfb43868b4d29c8609de5fe9ce2ae7a5b2af3fa3bcda9332e8f84df38c856c759b2dfb9f44ebac05018b9d5d8672c3baf2bf488473533e0e3265397150b872f16da4155d85bbb00901570bf1a24ea653a94c4878865f273f2dc8b5484cf49969e22b2d319613183c002b1075f8dfeccc7779adbab59f5cc2e7ebf7e032a0949d9c4bd034317188430cbe5974d327d81d06b37270d5547442dcbccc913424d87e8f01c1f423888ac60d6d13d00c68c5dd56a45e601631694b0f920b62fbce689ff5ac327e553036ba0b916afdc94fbec5b8bd4b2dc6f45f406eeb30d32ba02fd10d1400c6f4c8a2970684c230a5039cd0e8674d31c4768b2353b1190cf2b7ad98a7af4a5c218b7d080986f3d13b3bc6cbb8dc23d08024005aa47ba2b733041d105ed4005dc35f0e150c6a3e0a3e2dd6c67dc90e02001bcbe4b26ad29a5ab40179c738b9a368ce10cb0c665d0169625ee7b8cc46006d5e0d5cd7a6be68e83f10ee6ef60ec60baa587705fb91b3c7e9b95eefb719752e00ed16d4d2d03a81323b567d0c0ea4429e68e10019e0bb8305ec8b7e854c1a70974068e20078bbf3f531fd0b58c5da64e0b28d0f86fb2747f5148febbcff9e4f2a9ac93f1c87966d24f045cc079a0bc0410c4605cd4f03c388a9c684d90b99990de7e93ca885251ea0922e31fb51313c81e2680117ab6ed36ea919b09601b28379ac333a16cb814779000310c3ac2b536f055f0989426e2e76484f7f8cf781276cae6b4c1b35c9febb111fa9175831e35ec7fa0c438417c497602b4e608a3885fa4fba0ce1a8ec6c81376641a84e12d7c581a10e62eacbd2d4721ec10dde995b7ce070bbb69f9997e7ca9a0fe0f59efc6c0db004475ee0ea00d0a0be6d2d1666c43a81e83d505a2953bfcf56b260ed1e65fee70d7cef95b0475c6e0938ab51fa359687c7e207896e684ecb00222ca0aacdfb370640da3a3ec57bfa24f02a79a9576946cba417404a2408993b782463d3d032cc01a10cb908354989247dd38c19b1d0c293922b43f283298922074e91e7cbad8a0b4c5e21f2d447ab4917ca2f89dfd04d33d9569d624b232355942c337069ada10af7ab4a1467d14fe0d2db812df5a1cc048d68fb7c1ef17cd5c6e42ae8ecfe4e06fa25a4fcd289fc21497198f431b0aa9ad794fad15e0ffcd065a112226a048909dd89d803ac20d334b41c548d965781edc9c38fd6a65d68517018f84580175d035624febe101e2c34c54fda1e9754a6b87838b17389032ba9285c9388d0af530500e2e7198c908a88b3ac4b310bd226a43d52c4f2b6f13e1fe4b70cc67a884e000a5f9efa30e25644dd4fc012040c5c898ee5dfc63d820fefcb9d3a34b854a10b7a36b5376efe6481ccf867c44885b99f6ec335742f8bce1c0dd98de9c2e45c07 -generate_ring_signature 10f3730c2f6a2514d89bc556346e2a37fc87d7e83757248623edeb9fab8ac049 0add4f035c3579eea25efb66d55d73cc4df230ee3a749cb5ec8ed434d913e41a 1 8c906227edaf3c2236ca3f0e9b6aaf208bcf39ff871919427a7719ff092d26f5 1fd2d19d0945cd70b8a3104d3f0a7548a9acc311648967d48ba83203f213a50f 0 a6ffbcb777150abef7185e4b6eb16690c923503a5d76408338fcacae4146cd055e96a71d566dfd248c3acb6cdae136ad553004a8992fdcc682d89d5733e84205 -generate_ring_signature f4555c7a94cab7fa9604d4c106969a84ad66a469599db08ba49bc24b1e1488bb d97745abc00bb8228a40b47a0344c9556d8dd15964fcf20f0fd82341d6f18670 171 cfc4bef5eaf34348f327005dc7f809d94e495153532aa7bd337e1230de4974da 79b0a445dd281c6c167dbc468a4946bc46d7ea55b1393895ff1e8008f2f53a3e 411bad6789b69ae4a955d7d5bee956a5b7c73ccd3481b6e331fffa049a45d625 c5e8959c5e9f915e5af54c1f77a81fe3cff041c001bf163243bf59c206b9b78f 401660dc495dcb63633517cdfb4c83f7c1bbc39b510caa85d77861bc3f221618 6ca7e36b209d1e1c5b20b78e26ca678325502c504c43e51fe2c9e12d2ef289e4 b295c552bd1495b7b21d99ae5bb7b8025e507a3042b6fbd81cc78f2ab2878927 1a10dd6423205ce8dee6302ca9a40f2257aacc9e954bb6fce5cf4a14889b00fe 4def6357385d6989e58e06f38a50fa51503aaf3b13c2bffa146d2824f93f2fe0 8efd4d3019835bec1143624705a46b9f54dd4f410ad23a75e39f868728585a6a 448e81181967b7c2f211960907184ccc9dc01effeaf2bf0ff3246cf715fa7841 e12006e490cd7037403ccb0cd61a54d6016103d8228ff5bc5b178a4be5b3d5a2 24c6dd871e6f6d1e466e1c2be9779ccf2567abe7533a6d0868b3497346d6f08c 4dc82719c9af722b68c9c9b7a6eff94600c7e6a08240307ec343762f9d24b142 f72f1ebd2e930a6f94e2b3487e3bdc9f8f82c78e8a890635c76455bd12d2620c 508410277ea62390fb737108acb07efd9b4afa8cc3f0492f1735381a6bdd85a8 f98a6c409991d99882a77806afee0f5a4f479f4840279447945b2873c992d257 32e736d6c58b4a36c91cf1b8d9719430db9182e15184a30797eee75dcd966b22 f6fdd93cdc90c61980ea7069933b6dbbd53075b5a0dfea2079a461b4b4ea6658 38cdf0a99c31eda7fc48ffd68989add1a6224503e269a5536701b11e4028708b b1aa00ffa31d1d3d7e645dc9ab2c9d724b3a699702c877e7ea610808dd2ea9f5 1a8f990377f923a024effcf088cc532ea6049d533d59563e398140dbfef2790f 635710a81651e374f11c728151a6b81f8904f4036f70cb3be54c4a1f394780f1 d4c8b7a93f5aa7b5029651f44bfb87ed4e762e9a166e37db35eed7ceded15017 5abde930e31af431a396c120cfb04ae6377f2f653214c76278b5e93ee91c7989 596acbd0cee6be1584cd61634edd8ed33f0fadd3918e8b3398ddee785be0d2e7 197c6902281fba43aa82e272c5cbeec79ba8b28dc0bbb1f1ddecff2c91cf0444 88176dadd11bb0920f9dcb0b53cd9bc720919e663588317d6d48c38fb4c6a795 c623bab71256ec174614b343d79a6b1c7c29f89a94ae9758f60cd5f2c01fc40f bbc6e9dd8b262f08d09935723bd9ceca95667d6000b64d6566985d8ceb02f5f0 e8d10be27173f52da0711d82d8a2ae34925fdec44dc232eacfd24932b2e459c0 05d9f54e15501e036c89fc60c8fdf9ce39a7520d0585e02e852f944a76cc47b4 bb548d6df5b6e342173a857f8378450dfd1c1526399648a05f81b7e72cc5676c 037311c8983b23d7ed1450a24706026c1930d7ead062b4c5806127061c96fa18 41d01eb664f28f27bd51f1959a06120d7eddfeeed185fc4b5b9c67593ca3f750 ee1fa901b60359598a0985f6642fac8604aa654e78f1f3d18a1fc4ecc87536cd 7d69624959350420a65aec671b481fa6ca9f3406e9080acafc5b6dc251e50a56 5315e45e258217f50b9bc1d79bc411aa350d3427080b3cdf5c97ac703861d519 a4ab5d6d62a398b29a4bb0ff983060158e2ed7a81488fb1271e380839d5ab8b5 05985546d407861f5c113c25061eb90876cb7b1d043a74af25c62b406b821337 bad614857399ac6ec7ed7887bd1e9e0fc0320b595233fa0c6797f8dce599ebda eebe0e178a0e1fe78e6233eb6d86d3bd701173831d7f40da0d64cbc572826cea b329aeb55ae0de7a468892cdb57aecde5268829aeb84bb88245111fb438dffa6 fdac8651768a15a0f8e502319d3cf2dc41fd737e975c2b4a428b42db1dcede27 88d3032b6384a94593f5c782e3e7bdb027414c3fd99d8730b2252a7ca744a3a7 1771480e847547152d3aa315218a1c2cfd01cd45ba95182a1b6447103ccceb38 37b27bdf4740b2a100f54f9a289cf58c2b66a4e4f1b13e577095403c46ecdf86 427e01a2c9473a771c6fa5e2dbeccb62d351e429e2947b2cf8c228bb10da4649 15762146795864be7e50a35b2f3155b05eaef4d1b01c61347db5f11d5baff073 69e02c54680cc7205966f38e4a27a37a4a7357026fc38b1b40bce7e7edd5d1ee ef15e86c3ef859f2c85cdf828cd425d4f4ab7aefc11f5a0f4a16daa2b5fd22b0 618cdf2644069de2e8275619d923ead50f93789a4ae2ee8b68b625cfb206b39c 50d292bb61966a8d4beb864d9c120170f1570bd63cf07b41332797b6c29f5c9e 0a34b827392ba1883b610d1422c11696cc281c8b6ae75e961c42be3a3671acca 14730187723cc634969ee839ecf45452304a07bca280f7dae82ab0edc0877c29 699a24951c6ad1649350086f99a1854c89bda4228cb4a472179987daf854cc10 09aeb8ef6645a359661d04a03b7b7ca72c17a0a31b88997264cb49a38b8d7c13 85b0ab6229bbfa7a3b5b7b23811785805f63909500c94fb51d89e8f0554e70bb 3b665ca09ac6eb886900c99aec8e95270ff3ea73c1d8654cafe3acfd40019003 cdadbf8b0087a497967578999992fdb5f6dfbca21531dc5430e41151e051ae56 0ac2710ec9c037dfc4c3f93c5191a14737c0349a2c0ad7f6a44796b7bab64ea0 ceb2bda229231c4d5dc7c031cf687e6bee4d94c75313fc3d662dc8a6925dde77 a530dd5485fb70af577f782c9bb1b3694f832fea5555309dc992c65950137c14 2f55b2ee169c1115ff9eb53e24e1a240b7759af68bf305667fedce45e3f0d619 69aeafa707c68a9aa05a273a1b3fc158a5fdc20e143523cb6362f6ca37f99c25 2e03f9603c7abff4fe2766d73c4a39fdef14d144b0019896ec3685684443f1cb ca18d53a70086097a653b294e2eb531efee7c58909c0a35c80202224e4dee356 aa7b68adc147c53ae015e29d898a72b94ef6b6661f3a54c88a03075c73fbcc81 70cd2e3c6c589814e7a3191ec0f20839bbb2849436b9108110d689abb9e0027a 7dc0f441b3f1c7b35cb5ea301dffdb55e8bfdbf0acad34bd01786107d43a847a 3f08b78c2866d0779ef8a6751b7814b6a509a091c99e96fd3734886a6d001ac5 11db0478b0e9a19dc38723770b1798e3addbf704cf8f5d2e2764d139f0d22a51 f961c491a8525f841bac81919a905dfbd249a7a60ee6257584dfe276ae7950f5 28565b59bedbe41949595332aeb2711fc67733117120b1822c2e9a327e977125 022098f9fa98ecdfcba9ed3a8bf49c72ccd8117cc14d21dfbd22db54deda7ba0 68f94489f7a92f0a9222e201d2b4bba8ad6daa41b4de2c57c7ce9b393dc9365f 34b9147b28a3ac76dbb13ecaeb3f6d6e722a4a8c7d0bfd8efcbeeb187b096a79 b9a3ddbedc533698050e8c4dd60e05919b70fc5d487df6d01b62858b05f48aa4 c214edc1e0d6f77f18a3beb648b37a547ad6f929f3bab96586ec3034d6f9fd8d d04804e17f6bbd4fc3248c049895d3911d3953ac98790ed97f7d038928be2946 08ff0bf8daf6b3b57164169c2b48ca64ada70baa106e164513ff2a5e00a29ce0 bfc8cefb5c9e90ac9e5a1d92e9b7529d813f8041e519fa527b224151af806e26 a07f1068e09111fb6b58ad945f1c7705afe8ffd16cbe5488bc8fb94b8d53caf0 a451ae9ea7500d137403e431646e4ffdf6292489608606042f1a0e373e062f7d 6fe4867afa167c2f14ef976eb6403af05c632057e0d0c5884a3b83052a9cf32d 2e2e29ff51678630871baf5f4b1c97d6474647749de46d887ff31916194cd43f 662a6698e43f9c15cbeb38c3a8f31fb131e1d9b476f0f3a0bf40ee0e8e6a11a2 8421394abfa917e685bec5736e2492f825e7b259b587d6e568929d4a05ae2a0c 442f5c4e472fe9a9ae50496e3ee4281a7945d55a585aa296873e28e2cae5fea9 d09852ea077348503bd7995157301f844afc568e86d3cd18f0b6d415643e3e20 268d7e0eb3a46a11012dabeaba1f7e3574d1e32074bf4a2feded65c12b97453b c122fc5e2fc268e3b10f328e4ec77edc375399c1100dd10acc894903da97b9cd 9f39d95d583cbd4cb2779e0820d40954218f009a7cc5df92e030ecd9329564c3 98e17aee11922bdb6a06e8d5b639097fe9127dbf79d12aa9a8adcab208b36a2e 43250a86c378ab45ea8f34e71a451cdcc8e0709574d40f77357ac3693db3d4b2 f33bf249022052081d7b5da52abccd5fec3330fa176306958c7e3d6291f52984 020c044368cad6dd0e1d4b563ff0d334233c6a875a652e551d0bf8855c9d772b d32198328b301c208a8ffc272e512f909012a07cc99488ca9bf08c8fd8940f83 0832c436f450191a0e0ae66be14f07ed5081fc53f66a0df1315d7358cb299d52 fd2d84f1afa4fbe177b4da82bf8f6a5b46406e4dcd6f308fc54c6eccd018d707 c650a776d5e5f98ca43507f46d2d28048a3e21a5196dbaba699ba1904ff2571a c87c053930f772f7cb317a3191c3c114fc323220d74216c28b9d1354c35a866e 7dc1e344d66e726db6bd8d296dee9d3f04ef75862bf5380e445ecd43b0932618 af34f348b06c894e95c02c6a16fd7e52da6b45663df93c6a2533141959729f7b f99155d1317e0a8014da9f95ef688226097cf59080edfb3cb1a7d8c40c236517 bcffc6582fb43aa29d9df01cfd043166191ae64ea184af279d3c4cec05e46572 dd56ac7d1111f96f932fc79f3b9a33bbe5ab7c2dfac8d3c082c1d07c31147ba1 af799d2346c63aa6e1aff8bcd560b70bf99b3a84780a2186b018f87cdc7becb8 a613368b100cc5a85c4955ad5bc04e5f8d95fafbcd62908e699b984210e548a7 f6db5e3e8cf9f271b129038c706e1a335baed4cb85a23934722e22ed7aad4814 f94f6840a410db8d7809f6f9b6e4898bebe6ee6c792f046cf08a0e192f0af4f6 cb6060273be798eb8e209509a44301993613f4fd5b56b4535c515fa55b4d746a ed1c8d689f250b24e66c8153558ba88317c26cc02ddd3fcc6c9c59cca038dcf8 68d13f2e581dcaa275f2523c351febf927fb6c93e65ca9a293ad6e812b413663 46f723ca4398207c1531aab36297a3f437f1a26458eaa3b7cae6dede9fe8bf91 d74bd9c293e3022a7f7a2a5cb8c22f6da99c4dfac585bc91d4a54428af645a8f 30eb33b7a3cfcb4ce2bb552ff92711152b2b114b9a3dd027a7367b8e8426d5b5 f61968957b10bd9c877b50c5f7c8beb6791800f1f3bbe9b78648d3fa0faeccb8 04e2acbd42014dcb32cc1e7690cf83bc017378fcd3b391ebebc56efb6b515ac7 bbd85c1e26423b3c6c68739a82bb5bb1bb045b8e154ea69faec0c4e55400b79d 87e234d1e02dc9a3cd8167b7fc36b72b2cc9a510dfc160b87e4c3f08046e504c ef996edd8c19019436b04acd173b66a4a881ddffefad2180c07abb7534021af3 2b75cedb11ccebc4875dfbf2f4b18ef82ed352924567cf9b8bedba2e79ac9b3c d012005892ad3bceeddb59a34f98d1717cb85daf2e9fdff671d38d29919c3306 5934b1dac37b15070667de83bfcbfb87386f93dadd3d90abe02115ee1be25511 8c8367ac396509cd1bdc3baf341cb8659e85e597c321cd8b07d8e349cffb27fd db8e1c01cbe556892ea60baabf545c1944bcaca8b03c22d4a1f5e9167b5749c2 26929b9a191e52549d227b3798974397b97d8127911f6945dcd52c3a915df871 bdf06c632061e00b667dff7405eb578912508ce8b1cda679ba90637a6ec9d755 adec4d15f5b6858c8c7fba49da88d68d4bf31cd5b92b34ff29ac5d7cde88cc98 d70026fa9b5753d42069c8def719a909d34d6cab3b1949b5398564bf6f408d70 ace300fd54f1eb3b6428c46feee6824f1ad1b7519c099800f8c90c92f4c52833 0d6a0b164b9b02a3877f8326ba35cce82e4eced1d21af4d2e80f86ca1d8b72ad 3e65edc9aa0612700e957251c766be20acdad8ecbcbac2a645e286c3c42889c1 c797cc975661a163376b9d0ac24abd415490f5c0e5f70f87c40cdbf7f0c76c29 40f0a14168aa61ba89b9e5c40a8548858efaae2990b1a102a6810cd4e14a4200 422dc367b19a024000e1f102653a2db986d13c1be3b0fd9a6d9882f6456cac0b e69f2c3a2328b8546bc48b72f489d782e4f669f0a95e759e2c58101b10fbbbb0 d0b9df63e05f270697be34988384816f750d2cc5f1b058637cc4e494bcb247d1 cf39c2a55ff7c176830e14298088e6482b864bdc9935a880e54000ba2eb0ce9c 47b5d892a0a98abe0646e21bf3acc9414e08e74d9d7ebc46b8c5127e5aecb478 df6c351cef6ffc4738adf60d0f7f995a651e93dfa38d18b5e8be83d6cf7ef6e7 9f765118726950e8cf8f2f6f6db2cae8e49556b0432fe6960fb31f4262acac57 9cf2fdb6049dfecd5cca8e4a5fd17364a89ad0ef7524a28f48aaf7b32309b4cf 13cfdc82e184d2626825ccd9f10915fd95ec8adf1f11dfca92f5fb5771f48a0e a3aa71ccdf007f88880835328b070265dcbec5b8b537af52774d99cad5aeee68 3cc2fa6550177b8354d2f5e241f8b721d2a5366c34494df2cab5ba99864cc23d f2ce0c858dc7639de0154418fdc0f5f36ad7237830b43a200eb1e5b8ed713345 08ed90cab1be91c643d1d9c620c3bf85f3cfe1173c7cadc37ad2dead9b5ad176 00ad99b49db1adf38784bd79bf313794e45de8dfbeadb26dd8a2d475b49a6b71 37f121e481a9282144254a27b07b031e44bf34a47e2ef1bcf7b9b8524db52050 e00d06925df9f5236e69fa2a78b78de859780ca4e8345d50efa819179759e483 a4f6188c99012e9f26fa24abbb7dc87a3057d1bb4b99f4f384e5decf9c17c36c b5a5e73fb99c679b4357da96990a6d19a881e12922f1466958a4daf9af573e88 4a52f4bd038ea6c046ac554f842f88a654a055e5e9bc18bd6172f78dd2c97cb8 8efa9ea083dc3fa22da94cd582a3fee99d6a51db50444b1aea61c4bccc2b6140 ce192be6d8d50cc467a4b520368ab399e880b3eb4d93b8a96bbec74070e7c155 f22f7b08626b947b8d6017b846b258c75d55e1098d381bcfa7440d5ae51ff91d f11cea7db46c8bce9a69f5eef55f9a4131bcb465ea1153c9144fb698b1d936e5 b134a75ebcc9fa5b09567511151c8cc6cadb20af14e11c040f19519a6f613102 59b74ee6ed5cd8ac35f7dcd3b5b3497d391a3cfcbd21afa40a72713cd45b23f1 7f31beff2c2c80200ba74222781dc3b00d1cb9c2a92d0a0deae2032b8b6ebe43 acf94b13dfb0480f64d8f24874daba01d93f09d2d29be66a48e076dcfda39980 d31da219eb8bd9b7ebb4d6723adad876c59e1cb458f66a11b8b1e494940ec782 0f493b671148ad3cc994506460ee02bf973068429f9f1073d2cb09096918a5c4 e7464582dac622f032f7964927c94b2d801f92464c59ee34d533b474e0c4c568 faf9274a260a3f80554a0e9b6015ead2b3c7e39d9d5041cd63f834c5af85f398 424919660a91ecbdf5dc035759894703844ee28939b9eb004ced281901dce148 08df61a2481c760147c0a65a188ebbb4c0a3394bbc7850117317032ba5f7a31a cb51316d6b4d9797ef4ae30b6ea1314aff7d91e1e8b0cdc3d56649b9037cb193 fb517591cd9ed330972170eb93c3563848becb59e905f421afe8fd8f37e5a1e2 9cd6bf970dd2da5899dc28da8446cad1bc6881cea822a9a20debf919b23ab20a 103 fd7898a89656c611f2f499529a0d6c387e9385a1700523ede2c96dcc2f5ceb014fb2da36f821cf3d71f029bdd45588914d52af087a4c27b2e17c18d91e409e0493e32bb78c8e694bf96c416bfb4ee49d39178e8b46f8bc26b206df884dbf0d06426bdad6d958b40a2c5878af339b157942dbb4975ce1b26f37296d0727b7660687da1dacec8fb48a19d455e0ee429621b93f4b909a14e4de0b6d61b0faaa96048433a3e4aef400d3f3edb516db797f3a9dd4b1bc461814553709c33bf607d309fe04c5894a0b507d62891cccad61d2710795ddeb9b38b898e74675c86f0309039173b5e28d6e0cd822e3f101798c3192a7e5a520b92ecf4841e764885ad39d0e64bb5bba7262046dbe0389d98aaf607285659a29517971b251ede5cd78cddf0ed16b58def3f16ec97646249801a8b9c5cb4bbd152a275fa2d7b36e57a6996a0b476e0c2edf0cea6d71af435fce68dd3cdd1ee54fd444d8edaca934d8385ba00c657478d39a6afacf9ab874b9cf47dcbf93a9746b6fb2660381d598f99ad4460101c6ab5b8499763bab936078dfe28aa4a11f46f6e4d64a9f5a6491c20db3ab0bd927f632992338e84a9841c1af2e44d1dac79d0a075cd678633bdd2a68200b00dbd88aabb0fef8ac287f99364ecb57d3d66b2daee02346f0f7450d45bbd5a10485c007bd9aee9238db5e35299cd02d6ca2348552f0eb6c7858239df105cb580e58689a12ea7c3cc78cadd417a88b084c26ed25dd4022a68f615030cabbd00f0e9e9da0d046bd221a1717e75ff6ee418b18a6551fb9f138fee63422e8ba7d2f01bd0b69adc8b7d792d3f6002fe9643a4434b5786daf91eac59bc18599dc19410517a9c337b0ebc4a5640b53fc130644d27380934a62d7a0d62f17c5b7bfb0bf003876e463af980223938df24bd52be82758b1608d8b96abc03023236c0428b005ef05de1c37342fa9d82ba6638aa5ff0cb7d494de5855ffacd126ee705abafd0a2d97cc5a854a1af31b2cbc06e6aa384ce929b34cb25637d02961b233da32e10870461fe0c0e946bb2a99c56570dcd460cef90805d8f7e6544be6b929283d1c0eb9564e6c9d1d953212e7d4101a84ee82809cb3f24cd293e9eb17d9e605c3fa0420de064113c9cda6bdfd199a0b54b9d0f00d79f90c13e64f144dd5d31b48550a5ecbd03cc4fb43c5de092323b558903de8db2c91241c430838b02c4f1e6d120f7d4ff05e6ecae87e2b00042b175b411dc05ee55e56a0546e58f9eb915f77f60781d03ab3ec2b581594cbd7d88c057a11c452e79bc79775788aa73f946ba26003de80bf2448ebec3ebca500dd82630e386ad04ca70f705995efcb18b11b99f902e25884e41d7501829e0e9254da5595fa4b975ea4cea5359f9a70c458234eba09cbf76f8143fb31846242a73953337229ab122a1bbba9317bb321044a380cae0c1cff2f5a0b8a1513d4d45d2b352a648eff41c652a732b6323408260e4417d103c65ce6fad0244dc10b46e59764ffca1fba1804d433cfcfb6c5bba7da600cab0d2fb777440afc3be6fff9f48e9e01b2adbe75fcbf3aecf894a85a40cf0eadd808525622c60ee53c2d03b7f947439a3354b612faeb86e9a3c7fb3da5f5391d670b2ffb49ae20397ac25370c1f38b275f70e0cfcb84df4a20808b7160d84869450790549f28cd754f739be88e9dbe00585597c9bad5a5f5a916dbdb992a58777c00b07ff1592c54e6074b8f5a944fe48cf2c1fc133b6f97dfd91ffbd3c24c37a90eae9739590fcba3880ddbd9303a0ae0dde6c2737fb2fa0d6952cb7f2e36e3c70d2ef32c91d59e5956650ce34095b5ec5d6697b469f5a6a632887322c57c143b0b0a3a39cb0cf91590a9052938a09637f4c9eba6f2f28a9223a3603a3d3f711e069a596eb39fc3f233185d54a211f519990ed254c07f5b10744e4c84c160117d0510c88c76250cfc83bd6b215fa7c3661aac474e1cb0d5048188d4485b874fc305f90623f80fe372f2e53c4d9a8ece5277f993117769ba5f361cd504c7298acc0659eb1d840a155074a4fefacb8c4ef1a7d44b83a1e3a54645cd3528d0ec5c5d03c8e09d7fc70db1dd29798d53100a218e3e32288ede08b094f9ec05173bb6890f63b71536f28fb711a6287187ff7ae93a196cb7dcf6032554bb948b6e6d91cc0e9edb29cb1775bb10c99d6db293c9d9f04df59eb0f10d31b445973b3dac394d0190f9a866301041a18980ab0578dc8a7effb1286e42684c2338eef2bc924ffc0339dd95a5f8b75ebeb0da4be6e993f534c6841e027ea84fb228554b11c063470cbd973c92f2df2115aa3cc681ffe1de5d9ce1afc511d281003375bdaded35e30c4f57d71bdca770e48453abf472c41f45ca4f2998d8583dcac75e7896b7282a069fe79c9e1e6028de3e378ab3ddb26c51d058c9bb68a9825a6d005842aca2ff00ea555d3762365541a215cae9ca5c131bbedd2e98a7d18685e3d4e5c627483b0818e61f7a373810a9831978204b97f2eeec5482bdcfb3007bec4eb4a58e727b0ec83caa9771e8f64f4a645eec22a9b5aee62ac5da753c095a899a1622707c7c0cba5979ed28e53f9164061c7815f6a14fd712ddc3da929ad714960f4a3863df020ea22cf6f707c7fe11df75f75486e9f24538dc016898f4a36849aed7e4d4490b3ed07e045bb6b22407e4878d93bc498c173356e791c2f02621ac7791b7462b030d9ae8e2f3404c4ff08f88c2975dc3f769119468032e03ecbe69cbfafc9c150b272172536a6eb404f87f9cfcc63b5d8311f74522b7f10ec5d40299b20909360281b081deeedac5c816b63352f2a5e0056e9849d0b7e3d99cc857262971aeea0fb64ea8be0fc004a658d69ad12ebba206f20035e49c1e7a9e8d6c2dcafbb2960bdcba8574872a24dcdd05dd7358c5dbf32ee1367a36080306622b314aa27edb0b049a704519db313ebd11e8d890709cc89d19c77d5bf594e17eded6e89e89bb04b5b324d008e36449d25b9659d8892275e053bd47fb01e1bcf46aab7404777b047fbfae64b97eecee9d81014e15e65d4b91ea8c3fb46372d77e2804966cdf600e52dc91613b979ceeee05941c0c35bf23eb70da79d3460cd9f009b177124bc505a75ffc5a69ac1aa624aa414c4cca137dc1603281aa890ebeca18e824ff9b2a0f72894fd8f5574e2a4c464eb5cc3747512ddb309cc13ce3d5f4a40fccfe3ce20fc6fb2a6bfe18e939fb055b85dcc086297f48671fba27f108cd1426bdbc5e2d06ae28b0aca2c430d9ba3c4ef7ceca58ebb31b97293326fde67e49aedf351f90077e0a3c39db84f3f2358acc89d4db2e98ac890a557a32286479e6509d10723f0fd58cb029dee79195f49dda650197be60f55bd2da9f7fd39fc43c116ba3328c06d6b3ddb02f0f372881eb0c8007ca8d6c5ce48091d41e2836299368479a2ece028eee1598acf198b6adb921aef6f6e3d5ab106b2639414131051cb9896ce0fb0f5a5eafdce9ad70d104e1493ee55b990c0f6d5420a1b8ffe0643d20f76d6f64098f94e5221de667850be66cd6b66556074562b3009e2a4ff3233e7a52d1e6e1025e93723e5f7dff6a3bb87107a948cbd249896262de7287db1c48702ce15e6b09a48efb1939216548edfb6f5fbe5f0b1a0f5068ef0d0f16826fa5894512ab870b65d028d8cd2464770ad76b0cb4b7039bd87173deaf8b28fa99c33507dad56b0db8f8de89343910556681a697517166748c206ee68cc4a9ae5c283a4c92408c01436359e359d336ffa0f609828a0e6259eaac0106e576ff0fed29f47225c7330f9a3da1573bca268d12b8cf45ea61396d97cea8e0e675216b9c6e1361647b380a90162df0f0daa053097e2c6110a658b7bd9a4b38aaab017d71fd9439d81ae90ce5e4a84841a7e65a845cd16df9897a092a26a27f5b167dbe8f1b3093cc5bcd0b1c25ef1e0084f8d1faf87786a88695786f898d66de1eb0b254f0eba82730780163ab3a69a66052656ed7b41f5cfff6e1849d1ebbd68ea706efd58425e5364e0455a5053236e9a93a4471689d3de613585288f8b9bcca3ccb9b47550b7f809d0f2b1e0de3676e7db0d25f425b037b8f4206b4bd8af92538e8a21e18699c924d0565f785d25e5bb79e54cf051527ea7f6e9bcedde091bd81f45f099b6d886a5f0ead94a9a83c7dffe90b901c13f4581352e999d6b12664a9db8ea0e1aa9e5efa00c1d67e7ffae3ecf8eb67dc743ba27dd6fb93dc45a197c50753a2a5d83357ae0e015f3689859f4f43e480eb235d46654a69451895f8413e0df35452190fd5de0af70723abdcac0035de81ea575c63bbc6f90b392f1946d8bab1dad68cfc893e0d7468e03d4f0d4e1012eb6105bc762e507f34e4e88ace67ef34f11bc7a307250986c57b2bef510d06493621b930529a8a58f1a519d04e888114c7e0c4bb24a407b88dd15a93cd143906014a9e17eef73cec2425bb6054dc379b9f1cdeab53f1059eea7458df43e34423f047c474ba0d3778493abb3b139d8c752381104189d70532996aff1e9ecd301bdd21df25db06d7a1a8406a0f02e6c58db1a597355ef7041e4636a07f8a5d56079a6d123a7219274de25ed44b7a5425705eea52d3aaa20f10c8a37089cdf4fb3876b6c4b3f37d895132c9f3e5e04b49b7a6b2eba6faf1029710bf2db7da62e6ce7929c954457977af1e85e66f252672e1eb93c4f028a40c998beae5df1368123d69c1228d289de1e53d17e42f54d9b5995e360fb0c8690712d34699b4b67e5a018baef2b8f98e38e8a2db821e4a46bb36fc3b4824d50907e31823f245851c4f564a3797315b1c3dfa4768d9f2eff9d752994c1d4799fd09347228798bb9171a47ca5b69d3d72d50adeb133a8874d62e925df90d3cb75f00fc687eaf1dce521473497f7970339f2e56e933356f27858de9c7dc4338e2d00c5a44cabcd21b0afef1be19b5835bfcbb893a89281af8e42877c7309b85f14e06c05c24f9727d3b2579535c1e573e3aa86a5e38ef90cde5f1aea7c39754c9120711c827cd5c0bccd24a220c9a80d097fdc77feef21ab2aedcc0b1339249626b034be3cb6df1596c7c37a81b7e576c69116e5687c8ad1e34f2d11c7fcd1894df075c325c520d7bebec386a37b90473e74d7fbdee3cb6064612a61025de017dc70b565635c1165e3a91a17821a59510a63a6c19833e7f6c39bd3759bb508a40c80f79e6d2f66c81bd39a79af4d0a8f08657d8d46d3e42517bd0a54d3735100a7f0725373d218aa01947d04ab45c568c858532f26eb72ebe556e1675335b416c6801d4e6a0a97d2b6b74e420bdf748ca0e70a5428bc8c8515e8a239a849b3701b80d01ddd0022dff5c5b20d0f3a3ab547fbcfc674bca871bdcafc32cad29f783cb054f4fe706c8b29ddcb568e22c73557930c842a83e004b254ef40c4128622eab022c5b27b40d8d71bdc945a690e8b62a63163f1360e75f6dcf188ec1b6715713069736d9a13660ccf207606a3ae8d3826f0fdb8f0efd547c53a0c3b0522ffdd505e39ee85299faa4c0c9ae4042b45d65a6ccd9b38707f0d697e74ee900bec8e30803786cff93190d14253f6a1e1db243d9e4b6152d77d52c4eee0c6c6f1876a30fe00d99c1114bb2289276f0fea78dff9fe49f901b487f68b936b3e93e3f9d6c076a9dad40151cd3889623d18fa11c9e03423eb43de66b8c2fd6d7c49617a3b70460e9bab20b94a7a38c1910ddf1dbd79f80174c5d692e2681d04f146783b90e04c6687ed9403d377a13270348721492ee280506d5c15f8242fe5180b8bbfc850daa08cd7a97a0f6a17bf6dd8e8d909f539513587d7637cb9f2311456976fec4026823c1bca22e9ff315c6412145f29bf906dd417c3d33055ee6265045db914002322c4c081c429067e2f500fbd8ab2c3b38d2ff4cd24e2ab340ebf8cc6453eb0d8e0d8c2672ee63d94413c99f057f661b556c62a4e71ce3dd7811a68272957c01a8736ce753f297d97854d225fe49d2680117d281a34c0447520e1fd5ea80830dd610b8a566f63f8c593662b7d5079e62467ee53272827047bac87fe6eb0ca009a3d7844d616b68eb8ca0dfe2caf3c9c62192488d05d0c9515c57c58fa122690248a5215615c74a226a95d7eed4c67620541f023f1ce6d4d6767fde4f5cec410b428b631678640cd43caa475d26b26858657a723b8b784f85153094369ab9de0f0d9e5d463c252c151c339c73e482a13a633785d1ff818696ad5785e2447e240415815c281d355a17bfd041b26b666b4d25d44eedb28846b9af31476cb484880f62d1125c360ee7925afd2cca2990f92ab22feabc3fbd7cfe803d675e747a6e00581ef398cb6b88f13ba318488f8b5b6dcb74564231049f992fdbaf32e0b36d0c3c9d4e1e4e232be77138b93ebc6b6d151928a844a90f76c761af365cca82a90023703772faa55e9406ca9b3e348afdd3aa26a2dbc171aacc2bbb088d7c18b9006871a2f0316c0a970201e3470b7a60142efb59804773bf75b5a71c7e08e9f00798f3305e530b04c25b150aacff157974094d19df3ad5bbc397234324dd31d705b5e9326d60d82cbdf3f628ddde9e54c711cadde9ed08d10bb0940d53463c5809b54f2711e0d1ada2d9e7efb02b11b9e78f6229a65eec37c5fc8ea3ec3af6b30eb446a6ecee39a12fb59053462e1bccb53a735262497cfea7bd2f0442c9b9fd06b746df185ec12c52208a00d35b55cf71b1d5916e8778e3839f55da591d97830d06051e370a79ac3c0517f77f4287b46ddf18b7b92133b264d08d22476bc58208c57f835512687714994487f4840049038e950a2985e75690d7a24b66f9396c076cafec23ba1fff46fd40f5dbbc8a574f21049683d65e69f5778a787b7e6e8503458df1e816443722993924d1a6b5de7147350f06f59dfef3eaeb23ab80dd0500ac5e06fc6f404f7ecccb1cfc2b974cc020669ba53f3eeac2ad351555e464640624863fab3789e3013c2e96b46abf7aec0bae0bb4dc9ce056d253edc892e98e00d519ed8e9ceca094481cda831a370f64b2323145a428b963d0d28bc741e3b30c0f1211a2a86bea654f8a1ed5189bcaf84fec1efb4a0fae54b37ec0cd9e5ee60f89162b44d7dd2c0ae21e64d9ed89568a743fbed4ff5ccec92ab43f61b45b3f0d1de9be5863908957d3d8f128b75064d872bf1c5cfe1d304aff43104f38fc3f07cf201cc9b350b451ed3b2fd9647d6c8dafa171f05e005aeb8e54ea3478641a03c373e708bdc99601f8085f765ca07381e6780e09dafc17c736b875c188eb26010875233f3fc5bb6f6668e503d42d1d5c27538b4393dadc278bd3e0bc540295091ddc1f1f671e64104586d47e7ff4bce6355dbdd876dd9c3e33d6ccf1fbcd2501394437193b038d6a22a50e9db230a83589da24ee47315f30ba95b080c31f8b0685f8ff3e044521b841687ac9e197dc0bd5f83c83d5aa7075ac87118166d9250171d70df48fbca8c6aa20edb8a5e45a21133c1c507bc00cf0f4e9d8b1b7000c0fcc2fe3f39f40abce768d4d178c1d0ada9b5a5c344d75d599433f890eaad89b02d79b8224059e07ce3ae632e0f34be6a7c06dc73618208664c6d5f70445cca20b6609d1aeafa23dc6cb7a7eff9484fd02afde3dbdd6fff9a2860e94e70af99f0bc63f1c405203de9ff59d6f5846f81baf5e09d68cc4e7bbfb4243499187a4af04355430e176ee833f59219bd0689d59b4ba0eabd95166792e9507a8dab0e8200f84e18d493cdadcd375f14e9c72e4b642836e4c0ae46002cac647604e21f46d039a704253ec73069f5315e749b0a91459d6cb298daa0132e69c1c433c82702d0facd4b73604f5c747f03e2a81f195ee6ef75500d649cde736c0643cb53d65210a0bc68fcd11124c5e5dd446a0b9c1bff9fa0a2b19645c85c9f4734c2f3e9a350892a1b7deb812d0a53daf3d606c4b2b211e0234cd56004b9b19a4d8ab39812202709bd67e6e348e172ae16d19801770ee40fd829477ecd0ddf574ac34e17ca40c78bfad23135f027f39fc14154c6332ff609291519ce31fbe1577aee8d8d5f00d7cd5ca025e6191feeacbcf974eb17824b75aa1d4c5b6197ed6419086eb8d430b8edbd555acb5b1e25f3159d6121103e7bf7304951e546f9e51a14e169b4e63035269e48a586abbe1d3a20a34827dbe8b33638e7b4d905ec54400930351e78b0ae5e3640bb5f92e54522d6ac6b57c0c2fc5bd0151378e3c5d954153c0cbc5350fae9b01dddf2f7c8260d498a4af5401d5e2b03da16c299180960196c247cf7406a6aa168af6a77a08f90131562cc39d6106655370dd05135fe676f2e7926c590ac883eaad671415cf051621aac530a7cfd7f6b427a6e6f5ca3276746a73ed200355355ec058ce192b4e1135a77adcddd860df7e3a48b0e978b3bb527e6b929a0a5a23063b7a68efab8369489be72758f870a307f2f64892b3844e88bb2846f709f6507f6dee2a70cf305148e02d85f8786dbf7a58b3bded1c5436b95a3b9a4d0dd9557b46a7865faad86c52122cf777447d6dbceb339c8f094ce41e2cf6eaff0a756c6177c76440314760d1b7e63ea9f59950362ab84aba87c89aeb88dd1a250b90da701840d3ddbda7c433d507daa2f175773753c168e37ec7c4451505f23e0341c438f9291841a4926446b6a0706f4dc6fe32598cda75a4652bfe982f1b030400b9871a82a7c96fab8955f52e7926b7ca2027e638f7ada82054f5e9efd98e0b707d660bf9b54101b5fd9d869bf8532d7c882b6d89b64955267db9e16da06b0462e4033b484f017396bc456174a5ef8b8d2bd279ae1e538d500b09dfa72e8f0f42bba70482093914fb5fc5681aeb5b762f296cd3725348938a95bdf77be7460ab7a15165d3eca688985d955b54bb167ad6bbe3ae1dc3e28b6d77de55a180ea0256cdb74c2ce5571b36abac0358ffee5d46b03f98979d1b5f5e4f7a2646ab3a02d8cd90f586161d5c99ab9cd73deb555053c33b280fe8c347d89f5577675bc004d03418695caf809aeb3c9290537b2bbdf72454ad3352a8f0fce901092d54fa0b44fd56cdb23500331ab63f3ca0a466444506479afb52dac889f801fcab293403adc34b13928a90f3f2ec099838b42d8b5651381be5fcd195c07e56534581be0209302757405b70dd9cb0aa62b674c4e9395dc173d5eddbb987b8cf2b7638f10c9f884ed936b9986bfff8cb2db80df12198a3dec47d702b7b49bcb85b570d6509cca307c382fd6473818cb4d53b2bbc5379597f28197ab5b1cd93aa15dc52e9045850dc7acf005b41143a65187082f668993ce1748a7b482a24f3e0763ed3990746277af4354a99f1ed4dc9c62c8298367ecfebb57264dde18efb36bea2789b09b0365ae0c12e94eeedaced2b6e64ea55dba6eed27e201de5ea5386dc5921240b9badec9061c26843dd23db9918a7573cf75032096d890c29d29606b760c11b0ee0a0534a983923a0ec5b7cb63d7539e593729c0d747d9374a78dddb62d739d020211d82c7f647ca4cbe4196ad1e2137c33b1b863af6efffab810c2ee048bad08bcf5053535b46df0d9058ca2e48e24d856312d09e55800fadd9712ec10b4200af8216f15466272c56ddf0f35595842510f72679f7f9d2208474e14acb29836005de94e99c00fd76c72b250fb83861fad9a299bebba2d2bd8dbd16e143ad7290475c05ba60c6399ef970ac6f2aec9d6d2b6bc0fa2cb79e8df46a51d6ac40d5109cbc9e9cf59514c909fb6944696cbc35603b3f9a971a644063762f5ac7dc3d606b1c1bdd79a66d02935988dcba349dd61288f0f41c4a113ee5186cc1e06ab3e0eb7a3920077bef06dc0a8c7c8c58a7c0953d432181bec10c84338d475d29510055c5b10a4bdb9b8b8c8a8e0cb50a6f31fc7557c9b706569dc29bb773172971404ac252392c950b3e6cd92902f671ab76731ceb165be1360e4048a280b9853cf0688bd3a962764ca80d827d5979a12c7aa13fc38451643a3c175bcfe9a476ed90557a4398414f761a281a99852567eab7ea2f83daba8831e7ce73b4d5232a19c0eecb96b08f3b0b4ac9bcefc0270da0fc215ce76d0396824c5b7f9ff125c50080890fbc848bd95007a2270ded301ee24fea8569c9dd67da5fca4b5944976efaf067f670c8ab56b751f17edb943fee8ed800db5e58911db660576ba930d9d47d50ad0fd0340b2213ced28ec59e1a86ae0d3cbb5a32e62690c33b5595d0d51707a07b5ca4d165b0bab5ff8a8b79eb090d514b723548110469d5b227c61510780190c1ecb70658e8653b39c23b40f7049dd9a8099528c15fe969061a29944d9a1d9019c322d640c12eec0a4f9de426eca6c7b1a4a282c12428a5a86c8ad374cce4b09ae1f051215744bde9fcadb311a424b91d9dda4e6b04eccf81caf1dcc8b9a8406ddd14e8f12445e91a3538425c626bf5d031fd2ac75a7b3bb2f9fd5d269fab6087bb894148ff269653ef9c5afd09c2e147450d9c881d452399d556ad5c3e59f09b70306515679c796d16e159b52d42bee83ccbee122fc02f2e154c8ab02297f0b147406e0d8231b6b5f3bd8e4e9795ea389856b2d673a9b2bb2f4693e8802910b9c69180d04af7b3422cad824495b2ab54383f3aee9c6148b6ec0a6f10dc656037d8a44b6e67fc14101136aa988b22471308e091784cd386399ba676f632db401434218dc2b6194719ccd8c526e5bb77d701d8c37f3bfbe4e038c0c49fd2c880dc674cbb2220d4e2398e5d5a18c7563309acefdd9f4cb5e481ae1b44b34f159015d477f2183596eaa9fc2e32f344458f3c546f160aeeeba6f882a3b31d58f8e004ce3cb16078b4a4880e6961f458fe3a8c31bea0f778e3c86ad33c226ef07930d2266f8f2741fe6a45f1479cd4d58586f4cfb784f4968cc3192ce5329cc201e0545f9f8d4b0a3eb3499268c5d7f1e96117bce7301325d2da552572486d73494092008d42d6499befeb3463659a16d4670b0996922cc2bea44fbf4eb9f33398b05f15d544c29ce2a7af6400f4d6a928d0646cbd515f5787f40bbac711328cca905cbd0cbcb9955c0e831cab63c812d1bb943e38cb63ba9e147bcdf94fdcb4034039af4eb0c2649df980e496ee114a8556c42f3bc0847c5ab40cb68a1fecd1f360014eb467ae4581f69df9535878190a4e87215f511817a8c9f5aa3df07281bc0021e38fa6db15769a65d3198c4bd2ffbcf593b1073a766146e5603591b7ed7e50bc565106ad26a5b728caa2336683d326ba5086c1e51bc83c6fa11f37a66edac0f8d6d4cb279257f3c7fdbb689a1d1d8ebbb4dcc4e1000701462f8504577786e04591f99eb24e2a03035e602e297c463954907b51ec1d73df9c29e2ce4e9dbcb096b0b0b840faa6958181ddd168d87e078195c20447a417094b08f794b0999550319f3ed5dfcaaf826183f283b3c63e85126cfb2efa1601bc771649e0f7b58f10f9e63b73687fefc9aee5c09d0facf9906a7856cf5b27987e10908f4af35edf0086761b587caf8f4e88e071d704f43ca774c131bf6e194de06b3d472ccb7031e0df7234cda2efbf06603473e73d31115e96f9b405e17d76f71f5057899ca3ee208ddcd69e46e10111015bdd7ed5cc9f080c3806b44efb04fc470ec0d170a06000885a5704b7edab4146a4460830ca747528afba5e4c0faf9d4bd90d89db1aecd041b3ca97fdccb3defd5e700e1649818f6c9422e4b9ed5beee3c9cdc2ce4dcf102af35b7cba1d6e6d4f33b30bb618041f0cff3ea6ca96cd4cacd867fb48b98b30f23f59e97569329fc09b48e3111db3e97a6e04470cd306ef3f7956e81f11fab0efe01cf8bb6e79a1b7e95dd98d9a647d27c164251610c1327b0acb6ce7145d00b030ac818aa8ac55fa78307ea7ac38499c8daab06b8ad54fbe9977d12dad97e057f99dbfdd678b50322c97bfc33b1cd99398339b746dcb5be12873e24056a9709157aa18e86598dc0bdb99a50ff33d65726a28d82eda385426255e90efada4506766a5e2299346d708fed6f1bcbdf7af21b5ff5c0448ff3fd3674b4e5a674d8002af1bea83c8f9e5f7ab069a73d3d9447f12f95acb745ff708a5cb8182de49c0b41296fb7b39642d45fab88fb9c59ab551d7b64d6c533a3e7c3777228a08e0a0798b7ec0ad92d0e880c34c54116220e8c5e36ce0b8137b134bd2a350f9510840733d9da822ecb3d2184bc4c9b4f1a640a8eb4ff8e3b22638a8c1a1abaa7088d07ebe2c2b05cc79f37f4a8f714c4107d55e3d4d66ac165d6d801d80a333e7d730489e41386588dc8429808a33986583450f13e4f7a9fd4bb0d1f3e7c2bebe2950d8cb54f13dac1ae3558537ca62b90d119d062652d5ddcd679f7700aa9b18a1b033db69d27ed1c4e772891a0ee68d439537eed11f7dec43cc227d65049328e3e0cfc8bb96b40d664db2d961e94cdce93042881f1185b946244fe521ddc6e082e0dd93c1d3091c27331dd0b1a1f21b5bd85e28bcc335abdc0ecc725c8980195a50605bd2df8b2c182788969c235d95686672ebc2c6a8106946ac667d421b5804b042b006a65695c1a8c659a374602691ce1d5338bcddcef24f898a1dee90a77620560dc55cf708a21bd7f2755fad85a39c8181bd9451b8571a96efae1901e28350578980c1e91b1179b21db39cecd7e5f009e5da5720072f5938f60982a77bc5708f3b5a28a1907d0593d1e07d2d81f6a62ca7996d3a99d309acb4e2aa7be719b010a955937c1db0e1cc700428361aecf5c5058cfaa400fab9c761715516ec1ca0f2c6bdb6a7260c4bea2acab7bd4e893e2345d53955d1b3a2a4a9be9c94196ab05f8894241a130b42c0dabd6b01396014003bed48105a8247c6c682f04ec308108606db89ec6767720d33c42eefc60cb16ca943eb2240e7ceea89a8211b7a1ea0e107213d8e53929f4470e72ff74f534adb36d1c47a97b5386b1a5607dcc6ad20235c350b273805e133075a5aa5be56afcdf06f0eba11495d047dd601a1589d30ec68abb43265e0174808f682cfa48c0eb18deca57f316c204e3cc829f35345c0d98ae766ef8abbe676aa9b9a4900f5b8e0ac1428507d3d1c96863843516e2810168dca38957ccadca1889b3a80a0df9f0d0413035bbe9a9b0d98c3d0a6666c20340f7dd6670bf683cebcadc0e0e0430a01ba8589cc83b08d48b143c8df9c3620305845528af6121d587a9ba0ae90009be31cdb7600c52c686f1f8fc3d76c9520c5a0a1bb9aa6e90e076f4b742aeb4f5b45d8bbc31f45b55e93ba6aa1ed9c12a08c415f445af51c3a39c1f79b7b4cd805236b93ee1ade25e6c7e26a3ead19fe005995c4e1874d964880c97e657631983a455b26a946e711806da19a82bfe6ec703ac5e3bda47eae7fc238c8a8f3fa830588e490f573d578f0c1225e4deea251b0e387b6fdc2f73d3e9835ad508367acceb0826b4037189818864efa973cec71f0d8dd37430a2f64cb49570222a9899004f0f8e4982aa6ec1f7a3826f2d9cb9b1014d415d2eb7ef6b2532c65756dd23a9b72a99b255d45538ed9274d8c3eada6e0d7e31a4117e70a83698c001c2a371731e535caf279cab1286b916a4eedabcb9041dcc48eadb7b8b12569dcdf631e635e202b6ed3b4b9a88e605d8094ed3510a09d93a50ed358b16a42cb7a7a1438113865db26ec6b6136c7841bcb74c9cfe390f868524502501cfd06eba5df4bae62334ead7d7ddf3dc795f968f438dbf4255003676793d15c69603bc7ae94d78dd134b478909d293a10230dc1146dca5fe650b978ceabcd59ddf383421a78120498afe38c1c7478b9eaa58a9f7cb62483cee06cdf24811056f8e7381c0b4d5c60287716ab89ea3707b9ede814a57a34ef1630ca5829b859c8681e34ea8e9ba242f2838c805ae52e03cf7f3907ccf7fc56c570fd099c8382d3fc03269e9ff18a08a3bf5b17a14cd8ce26721b4e59bfb63019c07848708dd31ad4b25a69f36d8755e6622adee57e6bd793e195d07d51420550c0c21f463a3905e8a08070a21d0b5ecb7078ffec7e67746d4257a40799671d49f08664153de762a6b8cd9b2642f5f9ba99b52e0020ec46c460bf3ea51cf7c73e003a404c979319b571c00ea425755a2477b7a79cfb17de52f37019ecc16a7afd30ef89d5d75e6a536f8f4fc9b3cea872bad928ab5a3d86eefb2401cf79dbd72a6017c4771f3b7b39d3672f9c7d5dfeddec6f0253f36b74244a54f276b795418820bf8ea9d9222c9463614f6e2ff359c633d1141f70cd48eb3dec41d1b5327f0f8040c9e08981304797b0682d319948f8a8464230f5b2b5e6463cb952801c739c809469c868d6b142e9ea77f22afbffc2cf54609f6a7507bac6fc4320592b0684d0a1da4060497ce8ffeec964fb7c33ed8a70f85170e9aa32a53e6d222de5ec6c50c1498741649e9355919af33529dbccf6ae611e8890000f863dce6ce8f5f5a2000a097abb669f7746d4b3d22ad120bf4be7be07cf4ff1ee094d13bd6cc6398c203772179736ed63adb10ffe41acf5b1817856cbef577ef321bcaa8eafe4819fd0b26624aa5c4e60588fa18f51629e95b42b973129d70bd80f35f93215a71e7dd04a4e58d3ecd485ad34df9d52a0f8b0c8ecdd24f7f908feca82ae0acfdac2ebc088452d9fbb7dc1677cd8fbafe84e3ceac6daba485bed92c9f06ac8499758af20588984d374928e8a6e398da409cffdb93e456153d82e4e8b3f40bb42ecf00810d810bd39c4179359a0999faa458eab3c779defa34c301be7285acf25ce694740d4749287f7ece511a33bbd999d788b7326c0f65c2297c70f7680b2840917a89050bbb88b67c5b90496495874a552a7adb06808c3aae3ae55a0b3d0a85b87f4503b7ad4b1e8ee558dbc40d44ea1c71df9941b819edcf0fa2087a06b1b08c7de408a0d4b760e781d3e4b073b7c25bcd7f188263fd4372e09d421029dfb08114a30f43e3d1f58aacbfac1afbbf00731590f651194c329a6b3a6af040f4f5b0c75909a42309e486fbb735a70351f1ca9699abbab6d5e9217d01d8243d623a97bbf10789c158d38d02f335d336007d435f98707dd58f4ee04050662c2c3429ce467c03f1e9dbefc460e27f2be88960355674dcc421419fd35158d350652d79d8772e054b983cdc2bd991b9b8e33324f3ca4e90931813e000e8054fb8ab459b7bc83c0439158d788cedc7bfb41f2a1ff6713273229ee9aa2ceb4e6fdf8970234cc05e0b35767c1f62b16509233ed477fa8381ccdce927b13548269058588131a610790911b07c73f3406c6940ef751d29ec243e93e1d311e40411d0c8516032a735730a16d6820138aa3fda2623c5c76318471356c6cbbdc77209528b185bb5b3cae00309a27f3061098a217608dc1ec234434be1d76704f4d2784a2783886cc727210066f960ea421d2eb3d2eb6cdfe55218868257a8f34ee7abf167bf807280c16203a7dcbdc9704d9e22a45667ba10f8532f85cbff63d0715736a55392688b1c4907 -generate_ring_signature c4f2c0a39e10a7d2881c8753890aaac6ac136088a60bfdb67d9f4b17f5670464 8330fbc47f5ef2d5bcf3109e30396640363dab70a4760847e1ede29a99a660e2 112 e47318b68aa07faa35ef3ae5b8ac38acbb0fbaf3def0a4bcfff10395650c1eb3 0059e481fdff16f9b76cd2203d707be8ac31d48614585fccdc232a1e89841d2a b16f9868b930ad0f327f095f2f58eee17344ef219e5119c2a4d23078f3288af2 d375e7d98e7f286e38459c0a90a742c0e9db87f173609b3e088d6b0d1eb3a71b b8aeaad7d8293c44eba8932b8d7ebdc654d48ee3bb99a2ce9320b5356c363aca 016437d4ce1c2ec84d278ad1151e86005ba0757cdb110145110fa26b74038590 70bbd94b724d740a11d7d4a816c4c87f3eb7bcb2b17004c0ca11afd3671d6bf0 8346fdf809bce48d576e43e8005cbd6290633094eb508f7b1b9c2a8225a169b7 d66885f9ef8d956b5f7f65641651e31302075ce1b5865c5a241fe9eea9b29d22 8314224a7d53944a56d8ca1db88ce4f86a63b4663eefb779cf8153e2561fe761 bbaaa6e1e54ca7dbf72e37893f2154e027f4fa16175be97626efbbdc69586e6c 113bd03d75850ac055e90308da8b18f336ce2f7a33a88403dd26f5252506311a 652f8202dd69d0d86855a85f39f01eb961046f598588d8082ff4b4bea0501af0 24342da5fe22d17ca26d88f21357e5d375dea5e293ea81bd8ddcd5727213f196 392997ed25a6b43ff9f800a04131a81fdd28b839bbcf017337d407b145856907 abcae499ea45e168aa579ca1d7f5cde47450cf61bdb768b74f38684227be9a20 41793fa54c828bd0eb3a127ccbe27c4400055b2e7199ea5c918b88ee0d983314 c57bcceec6f61fb7de098dd8ee46f9d5cfc42cf864306601ab6dac9229efe5d4 2f9c53e941b12af7d6d1f49617aa8984be45080837d56c015ef28041d70bbb6e 16beac4e9d17d70a7eb517f110d8e7ab5cf60b4234f0064a884330e7af8f8264 fbdd6593adcbedb594273cdb4f71d628e6922f572ae7b94d560ad1ee543e17e8 3cc322f308c404283bc3a5476b262744a2d23402b4c8edd729b963a1417f08a6 1cd533cdf11e6c7078cce85de6290555fd75411a556c128a9962b7dd6f4d5c09 51ecac75cd208d0dd87edbafff96006fdcc82eaafc0a6a4084444802ad396758 1409b737766520c2d4789b6d59c26341c7aa1ed815f5674e794d9d0af5d83647 ff676950fa8d97c9c227cec21314b2970fd0e7fd688c64737a246eee1ce7167f ee4cef48d1ecb46fbceb2db3768f26d5d41424c5c7263e5df8dc206642445681 635b8c1a27a5ad357b46ed43f5911da74f20df7538ccab7afe574a4ec152b4c7 8f1ed5f84e8d913a43bf95b9f1566cfc3a067d30a28194f3fca347d2b937851e 1b97e52ae79c274e456980c2930f47174197392c8576111136cb6354d3f844a5 114069c991bde006821a4bf4d7981168046477d02fd97826d812d9f16f77907a b655863086ef74aa89ec9dbeb65bd766bc59e600f2fb91c06de972d813eb2ce0 a0f739a4e1b36c8cc807d771c84e081ea83a49ba98e194b0604d2d516665b778 58fb8b5f2a78e34edf2caec39b3f382d5c5af7e6efc6260e55d139a7371d25d3 821efd6c97ca6faa08c9b14624e4e143e52cde411d27c05cd1ad1c4b126f6e67 b20135c3919f7efc856e39bab403fb72d11970c31660a418b8aa67a427a0b303 fefdcd2c9f62d2788aed73c44a3632486974a7c146d5814229132b136e21e413 db6b3594fc8d6e4086aaac78f29e786d988d88818f175d2ffda41d5a472711c7 07b7567fb88ce0f9f467d8c42f1432224fff4629e4e46ab69026340c6b2a7e43 a1644980560aa39caf5deec4a899557be279f3d9503b973450f08c4dd6e82fae 74eb226428afdbcc7558ada5d79a5b6bfdcc9bb8338e75b750a27ccc083d919c b9239985dec6a42c08999efa4e7817fdab6b49301f04559daa1b80adf42b355e 10113e293205f5847c7c8d8f41083e82f3a525b5f56de04359706e5b473babe7 62156ff12c819c07a5b399c6a5694cf81b3cad3182d9c89fe9493399df4d7fcb 0d88e5842e2a1507642926ee6f947772799e27c4276e84a0a377f28e8787766d da17155ac68b93a53b3450cff5c3627b633b2b7464d3f0a74d9f47bc7a9b63fd 027b33143e2bc160670358bd4f206b7650ab3ca6120f163eaf7d52b1f6419ad0 38dcf4d9c7f711c8c3f333ecd93ec97e22abc07670a8c8658e6277d579923d7b e88323c9eaddcd13f3348bf85f5251f593f034ade1ac3b5309b0d542443a5e37 fa14c4f0c750ce4b52d4811caa95d3c9a7ad209966683cbf083e24d00e1765f8 838365eac90f707d698b76078f9ec1d896590f9adfeb5c0dc219d80b42929d63 e599e66bf44e7fccb2722f3a97df6fe782e80811e957a6d79108dd700d46deaf 63e1ac5f9583e1463db1c0dbea5f5780eea86c3863fb7f2f7144b094d52713c0 f30040af05b4f9d55c2cbe1de1c2be3d0a464a3c2a07590ca5248eaa76947ed8 f293f6c22c31534998d09fcc9f83c6711345ef9acb4799297af1a30ba50889e7 6adf66f4a7655d9e80dd691cbb6ba6b0c34ddf01e00bef4020f5fdd993bfa62e 7413a65c5acb2f77d01af852e1d17d05544d767f62761ec4bba98ebeab91db0a a604ccb842e25ad2739ada5513fa55c922e98a1cb47db75cd945fd1821a8a84b 760744f0d42cb0fbb23e3be8e04860eeff2c489c93e39cb7fd3e644313376de4 d3319b1bfc438b6c652aec300eaba43a8d1ecb0b10ce7b1456732042b0d6c219 f8a118056a6be318825229a5bbc9800b6002522e577ddef8382d272513672bad bc8b2c33bb713ed0d4cbd68d7c118e312fcad230965cb8086dcc60e0f725ce87 1d7bf114a3d4e60c584046097d308779be8ebfd81bc4cc2ed7434f700d0f877c ad197648d3c14b3a127c04a02b1bea5b8dc8e369096a3dd74c6eb1ffd9686449 6d70e6bd2eb0a115f51c5f4a1a5fcfb052340110b9db067916687ec8e5f581d0 5708259e096e2c0f23724fc835626eafbc11e98a8672e36360ab580e8abc23f8 64959a011b73de580cf315a0799bd7f4e24538dc149ffe9fa582fe16a1389a4b 987abdaa231434b344aaf7dbe32040701e1c7a5bd7cde69e92aa17800574d6b7 71e094387ec015e8bdd1684f778d544e0868a27a2cce16e3245863e279d36858 297bca83ea72ace5734a5b734743de740fe5d17f2d9a61afc28b5d3174a4d899 4b676cd079de1a9222bb783ea75c17ae3e16bc08b3618e4ff201a9fe849c042e e927115998105d74e0f76b3ce1c3a46b0fef5c68f6061949c3b3fe7f21a321c6 95438c8204ca32bd089293645bc0eafe8552fe14ec29a232fb1ad4fc619932af 5b8d7ffb49e11b950d8cafae7bc3319c9b399dae92de13a01ec7226a7821fa65 a1a64ad18b13235f8ac0c33eef61c152050f8ec646f4f92d7fc9848ad59ecdc0 d728a9547eb0391c9009cfc4ac05409c42fdf5609034354004f0fa4d5441878a 784d8afaad3bf0437067961f1ee47c71e609cada0b97c636aa87a4da8cd640da 9e562362de224ee6178006c3f3eaa0e211fa9c0b9e4a909659e7e0d2a28cdd48 b31b9f4a7e801c99f98d1b4523457a434d623afd8631163f7ee99e14a7b6870f 9164ca548303e3f8e0ff9614e064af82f8cedcec6fd76541210bdef127eb450c 82d00ea6aa39897b10534f681a739925d179e3c25f87d582982988ff8813d4c2 c3cc009f53709178261b9fd064f31bc3397e4652f37db77717cac41430f68b0b 3c6a52119f73f153c9b740b10e160e88a878a15a685c6e426494c1a190bf2ba8 787fd20038a29141633d628f1d9be6c78486db3256641d4c5bff7bc6ddf3787f 5f8f4faa52b8c26f56831f67bed9ceb8a3e98243b5d89944a0e21e78c523be86 ff57fef6e81f2c1d5ea0f39579e30a2330b7b30058d65cb5100f6fb91db481e4 6faad18ac496e6517c09c2ffb99ef74ee010f1a70016477aa92f04f1272cd725 8bf8561b844beb409a9603af71af4ffca292776452278995a2768f924069132f 0573f9fc57a663c0ff3b2cc35f4301465e2c5737ce086d2159adb28100a9d8b1 44273327e8d0447d0205ca23ea4991f0167f82a7dc7dc9a30040eada716d2855 223250e4ee92ab39a66d8c5d10cc1d859f8e129522763c03afd9c1ff982c8d3d efc9951e85bd27fddc0dad8179ad34a125b21a24f0f27dee656e5be965f54dad 2b31f9237409aa3ddd3789f095404bb3bf9557c7d1919227a4d812f2eb7afb9d b4d5426a38af058bed01586fd89efc34fc60cfbc95d4a3a34c722e05c5457c1b fa9cdd9ee59c34fa855609a835d3bde169d545ea653702f0a9b7645c8a8d472f 377077cbe71528c3b0bcf76ab93ba7451da7adea27d31234099289b05b22ab1f 8e5ea1ca5ff58b0d5e73e959b45a650332202e6e2c182672bdfc365b4b19f550 260285a632a4ead67966de806b34a32cbdc69352ccc25299509a3415bd6ac2f5 16c8b44afca503068a5117a0fa39b1cb5866672ced428ae720393e8140e4958d 6ec0f1ba70f1bfd60018d9780c58c09ef50ce619155a91fed21516d72fa65549 112cbf0a773da739a875b800bca212c1dfcaa8a7b006bb0b1cdd72e197dd5fb4 909c49c403a2533b78a8dc96119852c226b8a93e302d9a34ae910a8c61ecbc7a e16ca70091bf8859d39a34dc2fbec79f00888832d287e0bd0a3442a6067b7206 b41959aaaccedd4c7b4ad47f694421a9ab5f18365cf45a88d85dc4076e87c649 aa268761491f71d9cc3b911ea0db0efdc35676bcdd5194952d9d0f1dd82d18c1 cb1fd93f7c9844cfd85a4c0f89f5a41793bbc43cee813fe3d5ca10fca6538b04 9aeabddccfe892522302b7bf3e04f4b94773b0bb89b28f6f19e24220495c0caa 81cdf2134d20d8536596154521f3cc259e5b917c9cc4373c6f7adfa0b19e8009 78154a42cc5ebc11375cc35cab1a274f024301edee8fff0c08b5cdb0dae37c22 c29ad72588fc320a019c6c102e50ed32ff4123df1f8417e0e3ae315b8a6a31bb 5e053eb278d157296654a846a072b83224dfd088d8db57abdcfd8de00b2a8bee 5804ac28bb2465a4a879f07c063dfa4cfb2762fb9015da611c9b36fa62b10c5c 314e8a0ff8ce6e952150be70fa590a975e6629b6d6905e3f6233672af926dc0b 46 cd4012e4c22a50e01d84de03dcf791a73c47e78becde219d4e4591227bfbc7072312a5d901422c482034d4707592a764104ae318702a666687f7de5c136d3e09c9be79575fdc545bcc965d5b0132817bd45ccacee41f36a5b3fe1fc9343e290e0bc922b09a0a33fbae81d79ccc484f2e19183a1329600d918b8076ef4ae1e30692c5fa660e85366ca7cd94b477c51452a47a75346589682e2105dd8ed22915064a05b781d25b6dea5b3baa051d6820c246a05dd68f212a1ebbd5cc2c0722d70bd966f766656f12c6de18d27340562d043636fe84fef490e7c3252f12b9307e0638cd431723d0347efe931a2b4b669fa31f1e62b0f3cc96cb63bb8908cb4b420d59ac287c45b0bfc937035159ba0c1bb45d572f5b7aad0ba76f56ba628b5a52071c43f7cde011a4358b7daa0ee574a51ae3337da45c6b9f97c4ddf1d105cb280034b8eb0ef1a53955f3e757088ef32e045d4d27a01f9525796869ba1c965e6e03f7ae1f95fbf4ae3265d249f39b09e12729fc2b9d759b1fccfe46d7311292fe0ea1e697bbc01bee50d4f2afc035e757bb8a448c2caf1e5a358d8dc980288467081dc4c8d71a413f993118f9f196106c03888e4415c4e75dca657a1f42507a3c071c88bcfe3d8fe50ec67d24c0054bf7a99e5c53034a2059a3ec20f2e93a165303a3c14d32759263e04e16ee728d2dc8af1e7fabe29258362c26f7783dc96715025a78561c5bf5a5778b3bd819f21da302a801f30978d0f37d26c32d7a8681690f3030caa8f27610c2ee32ddf65b239055b98adc858edd4f5432df06cb8e598d0d1db241b17d86f17624c7deda417735d63d5ee0a1d6bb14667223323be8737a05beac0fe81d736f1c36f053ed2dcbf6ce63695429498ed9d67113f8cc8459ff086af07b226e44e97fcf7ae18bfee54d6888d5ad3b298402d07967c3e822470d013f5950ea61ffbae8c8c4113d0e91972cec0894685d7cb7547845e9192ef31e08083066ba0124e3874d287688c888f6c17f8f8808d0e9a1fb893a3d3eb4abbe02a9abb1540f5dfc5678e50abd3e5211676d8738e0131d4890ffcea96c31298d028dfbda0f423e06c0db00b7605a37c495ed671309a3b244e96602a7a77a49510cde945a3192e31217b091e2f2fb23c20c785f59a421fcd32b66a7acb04fea1e00b8244b649098861c7142229d2c9ea0b73df68f8483cea43ca75a7b9895932a0720a7d9e99feca95888fef611a48c96e5a019a74680e9bef55db0c0454f55490a4f4007b7a93dd422c6dc2948376a9b7b51dd32e8724b8ab52154e49817974e0a4cca632f3a53ff9dad31c173afab24e9e22ec0263c185a2c0365576927534d00216440a89435c18a031cafdb2d640a562135b61ec86b2d9da8d5e5a306012c0c5c709d5adf44eb70dc86339551141557069ad0182fdcd70d4639dd7798e9220c3adef439c4b10d1e565ebc5afdca15b754554abd38c3963416aefb007a61430ebb2d66a21ff262280fa34a46511a222016a76c1b029a82ef62ba71bd66bef803b0c6eca0526bb60d8fec5f9fe70745de987a429a93d646fd67deaac2bac8e50b68ee48dcd5d3a0b0fdb03d183e60998a3b16f3734d1260919b4660c5958ed600d330db8c226fe703960124a1a02d21675ec8814e4fafb41841a97753d852f40fed38548cb71479d6c7066921a2137ecee2e5560448cd00fc7f6b9de47dbc350ba0663323aa4b0d86fe9ab61939f36cc607c9853a04b08cec502f98f3ca34980287099b12fae09d9ff0d06c22a9d11246401257eff629faf8c5f68f5c212b5c0b7ee5c7d72f82f85336913288fcdd3d15927a70f8671a64a90c6d05c18cdfbf008ee7a5e8403ecd8cd222311c5426d26f378cbfdc02b06f4751876e2cf4fb7d06e34c1a3c5ef7da1c219506e63f94010e96076c60fa776497dfe7a6eac18ade0bb6b7887bf7e5daca4e689f20b0b2235da7c0cf5ee32d8e23964e5bacce313009a894f857693364b597e1f0c0cfee06bb7fbf4ed852b497886368e6166ae0c403defc6616a82509e3dfbf3cfa8f4b3d18263f0f3fa92417ec8ecee101150da80063e3cdf8942e80b66fc2c3ddc722a3b79ba5ea1dbf44ba436879ba075cf8960066cb02863784d311362aabbc4a751d16d85ae3a252260c7061cd5707c71215022bd79b969468618bebf1ac07c9633de9596ba6031e15ab219ef59d61be19ca033cbdac4f5dd7310831c3d87cf5df2f54a375df1fb4bc0ca5dcd7576c565e8f0217e418e94c38adfa48819550791aedabb7058bc6b0ff1f236870c5328d788305f8504d987b16d5cb5fe69a67d1e63013f1ad47e2fb89788fd8f7f8d72dc6420b33739f548ac6f16e85a085e60d09f911c73270fc30b65b9567cb7a4b6102680edb184aca6c8ef878b0846e926ab9ae53a95d69db2da518a66c98815c6ae8f4058a91cce7d5aadada0cd2f2f1b4ef4c3ebcdfc5c3a7a725a82f24af401e1bd60a3ed089f100fbc1f7cd4012740b83ec98450253260d078db346514dd003abdb02a13b8f23419b3d490b531ea318a604a58e52687182ff723b9f6403f446edda0cda1563ba68bbe31df8f10a7e1fed7fd3ca0202d6fd95e3f7d48ce935d0b6bb0932e0bf2096b966e75e0749086ff37586473ba9767f552029a76ba4e9e6a4df03545ee2df9bffd1150e832d85a1efa01a8911ed23e154627864b7f134b2079a0a4a8d3c9fdc3bf3bfc51301a06ef414b1eb3498e894dda13490707668cfdc970af38c01f4b20315e8a4ec0b689e3f679961e16ff339be7d00c70240d0aa943508e2f3d738bc1c840e80d41f8b73a163106e8abd6e39f14e70bb0aca0963051c03ffed88ee4cac9bf77eef3ff635526db73a4870d132100234210886f38395ce0f7b053606a4cc06905db691c648329c0104684a8846ad9f1faf538d19967ac209d1daf6c9aab570a2ab12da019bf9f024e29251b06f2dd94c6238fbb05409a1045ff4d572bfeaec6fedc9596c01a6eb1b8c369871def5555913254c1112654300a031bec3f16f30a3f0be0e2f528f2bd7f04022468007ce3dd8a3d12615672a0cf8df620eb52b5b07d7c0b65f0488453e8da7cf0179b5ef413936a7017273df091d78df5508bd91980a1e9d0f3d0e253c9ac1f38527e9371d115401fd8577b70ca65869b03c210728852ba5b0d7ac77ee73abecd63b34fd3263e6a0589e71be093e40a9537c3ed77c9c65a0b24fc5a9efa18936eb6514e38f8bf8402c3a233001d44de5b3cb9a1098d1e4af1d684a9f1827b83a235b58a2c9cdc8393be5e57e08b6f2e27da48bdabc2d738cee40fe2797549fbc40ecf13b8f7a9ee38af615af05f036fc9f87e30886f2d3c4fd816802a595be412511bd85f9844069f02410d809a7fe7443609a732833048d40f8fbfbcf3849c6bb8b2a05993cd232f4f4edc80942e621457801931b4975fc4239262c812191beeefeea7c6371f3256ccbee6006b0712be4f7b32ba0e6a1e925ff952917cb38dab1c531c7052c0953a6e834080ba9750c0a777c4a041293b0bb0da4df691ea4813127b61c4af044ec6deed7c607f885d2a2065e46d601c46a5afa395396f9b49a0da4e3de190a09d8648591f30d13279768e12b8b8b3a469bf2c96670e4e58a09f33b4ff2dc9c7e22364014cc0f4f24d5e6648380befee988a9d1260a29d906d575d0f5812dc1432abdbe6df80d86a8c0cfeef6df7e4db54fd4a06841c201098ec4213bc415e6c3c16ade5889060c9252161d128dcf8c5a1b9a05a957815e211d14a0292b2745a3da673217ba05dc671a8479373520266e9266d2a8eac47e3bf104301f47586f98749abec9260e6822ea04bfcc36f17a4828fc9c4ba8e9186af7e17e0bfb2ca3bffa446504110fb5873772ad7e00a70ec68c4a1801092df6a4757142d22cb970741c2f40518e0129aecfb1708d7ae3c5099d2564448b61e0270e2065d9cffe58db6ae620284308b012da359afd9c5e31957bc51f6e5238fcfb57be3cacb439333cdad53845be0044433d562fd482183c069784fd599e463f45c0bf9ad4060b5247a9d343cbff067d8809800598b7858f2ce288688a4baaa432cebed8d82eef3a295b013697280875411e31ea901e232c320efd769c278c67899c55d4e34ec0b9d502ba47cc7905ae1f85f94ad5a69270745feaf47f5157bac4368d2e70d4f13f676062c20d1d0eb255253036654d3a41cf11381947ed2c055b0cc8c4b45073a3566b0f02a96a0c10c1293c386ab9db2fb9d2b606bb46745d7a0cf09242455b77a244bd6ccbb7013f539f936ba4a3e1fe226d3c2d9275e926680a0e6f21e9472fbe709698eb010a10a657ed5430953c67cfd5a44413cf7f904b284542fa8ec030cd318e3d4a630a97e8cf8de8cc695127dc091c26eedebd5c6b1b41d5c69b9403540d8252037501666b150a443d6e90857183126b07c991754a70850401eddf6a5ee2013a2b9d0a2a0390d0f020583f4b685b18704f79de1d8b136ef88b5cbea38f75224c45590d92380561f895c39aacbfb6e4e755613311bf17bd2a8f0fb53051949f9887bd07668478e5ee3166ee2a10b07d14f035464de3aecc52aae0d4e2fa73e37b6e570849297715a686b1d691d617aea6f331ab13140b657a7e92bb9c4e6778d3a9c508ba3c7cc538a15a4d6178b19761d5beaaa29e3b2e0887ef96cc3d3d7ff0738e058057dfd318c8ddca8acba74d6d2f5235679df022c57b36d5cb06aadc1983900abe2166788560ad08856b14952b80539c57bff9776000c422f8fdff7a03e7dd014fc87582c6ab72998e83c3616ad3ed2e358aba5600f3a58fbfcfdda5d8b2800b3bfeef80cb53828fb6f87b9134ce1bd6cb93d6516dde5a0a20ff3c864b8dfa038f93e52e1c42dce46481c96e20964a3af859502a0aa11e8b4bae549d947d9c0adf0db0155ab1b5c39effe5b160e42fc972cba717d7da233c62f65e35582da30d334d34521e5bb0aab711dfebe4ba231f07ff30a9a8c9ec81e3beeff414df980e9dafc262bcaa3f5bdc6a076080d6f2424499939c4854fe3df848719facb44802499490807fb70f83280396d7797a9eda4c574b507e052b92121b0d2c938ce40e16e066b12ef86840ecc14e7c2501b617d682fd855241ab630df8441b7aae8b0935eb92991249446807df71b1f34fc23a004a799f4989604847c01fa59a74630eab214ef045c1952d49ae48b8a61cd48b498459aba83420003943f128ed32d70af12ee4d71a0b08dee149833871b4eaa734dd0c31968431dc5d219f46ed5e97076605c0271654b9f42a1b8bedf783fcc04418fc692708dbb88eb8e4be2865b808dd67e2355f88b72315d86165f7f09c76862e14761dfc1770d32908e8b3abbf042ac2c2aa0e2aeadf6633f49659f524bd2d001e71bf6c7f73e76a5e6f895ebc0bc258c05aa658cd42ac1951603ca336ddc5db7022ffdc3f1d11c4b48bf4a509070c52f62196ea01c29e8b8245a78ee0d5a4ae47abea155acb67e3d6934aae1b0c86cf3d06002a87fddab9a42640c441ee7a19348d8ffa4bea7b04d7dbc68be408ac8acc6ba3c675dfe30f6f0e448516670d2832d3cfdc9f7f019851ab8f920e0b151f9bdf28f716e38d3a73a5626a57c10ccef1396e74bda41cc545c3773a8108b5069c24d678becda68e501738106eab1e970d6fb68e1f5796dbd849cf9f950c2d0ac3e3dfc9732c74606b4cbec05f7070e0f91b15fd11029732f3e12eedd60e853b48557c86938d853a9fd2b8ab7f23c06dce0b451a36a71da8e2820a3d3102af6a81f228521763c8c41a9c3e59d7188858ac6f4715c7bb964ab89cdf376b0885010450d2f72b76be793a97e1e940f62334454febccea892ae1f8432cf4ff0a8aab1c4d5a56a2106864bac7ba1dd0ea32826cc06eb69969f4a7882eef59e00d0efa9595f6c89b7915b1d5ac743b943d23311da69363e030e11f271542ec360fe6031fc7de94e3ef738906602b2160126d8ddca00810e7f2fc964aa83fec190a39a78ea9999912e03926ee55c244e4f43e3e03f182fdf062e26e8788a06ed00f21cc3dbe8f1f5860012a2de12730bbedd99b670974a7b2ce0664ec5a5738c10e5e1492beef70357e34b3c5ab9997d945face86d5fbf5b4ac887ca772c3c0fb041e5f744787d53b18828ed4df1308c32db20a30739abb60ec321eca14d8b1fd072d60b560735826c80df1ca9cfce7f5ac03045851adcb907234c4f4f40bba8908352eaf9c5f714e2d5fc59aedddc4bd04224c15934f84c316fb07e31ca2fb8e0b69d776964a882a2e1ad8f6cdf75c20bc914c653f9b2677d99b875cc431d8370c03a8445ddea816a8c39f12639d10782ebfa96f2a0b966ddc25aee60f664a750b5ad7b7199d394239c6f57cf2c3bd6a087091ee0e21b28a5116d23d7368f4bb0d1036858942d93de4018b0b9a2fcccf39cd44e5fc512a08c2b194ed5349f9c40727c2f087fcd3e74a93145a1142a4949a0217b8216169e9c9e8e1d849681d8506f56db206547488a34f23cf53609b89a8a91494ad1158ab7c607e692c8dddb801c7b4bd192d512e311ed9460c351576d5f902761ac97944860fb377fd323f8a083ffdbcd7e72de3931ec8571f21e037620460a1521f245a2370570bc4ea777d0bafb0483fdbe797450f76314ffbf6196d2e563c7de4a48f1603eb56b7e68dd50fb4639285c8e048c5ab62831f176e4dd59c2a7b909915732ebd0230afa3b06e0d8fc3958bc372b17acf891f64af396eb6c82f834525f3646bee3a5ec68dd1750f9caabca69e6d9c1c55f0056ae3c4dabd0e8417a76d28a5d3eb5ac374b97106018616ef2e1e5b9279c5df57e5f6000cadedbc3fcb2acfeed493a7c4567d95120aa50342c081e94d35fd3718e535213e934553da750f605adeffbdbf977bf68e0bbb45537a3e627bf1f341cde6850c9e2bb287cca2b0241d290d35f88f89d2020dcf26e61ac8c1ff9e3140d29bc1d97600ca6cdba39cfd7b1d6eb5ec956736b106e8eb29c18761d65f9c110ed666804aa58b0a1475858168d176b4a1373a69880e1a0761d134d15eab55adbc1d0a5402ed5a34dbe4253b96ecb3cfc3e9b75cc90850d5e6e64243e1a15d1517ce935f12ec36b950297727ed0a63b81279bbc709033de812fc46fd2ef6b3baffbe863bf86d9b04b9576d4cb129c0bcd4fd5dd12d0564e492f5354104c8219607265b3b726497e56626fc718580bf2d48fef36bba062135e38247e8c7f878b1541e36e61b40f5471b230e7f4e0af7d193b31ded9207697dea38995b1cf24ad164ab40888ea22bb9eec3ec32dc422ebdb526553bec07045f6eed0632a34e3fabd7c30d6fbda90a0c01097d1a620cbd128b7c0aae8e07691a52c2593990225f1939140be7004b14c08ed7f17f9844ed3cba94bdde3d0ec2c421d6f0655d78397aafa286bdf99c68da90f8e32756d4eb88968d18ea8d0d669efaae5c128d53767c7d4ea85b72b4f9c65055059df07ec5f53f21349a1d09e60a7f676bd5ed42093e0587dc541b7f7c8787f33cbbba1ef1deb13082f5200daacbf4e44ba9704eed70f6400f47dd37d9f40707c0f32e2a29c9c9327c38e30026c2f74b5da6538aee15e755ab67c1c166245208f5cb727d570d119cc75c1506f93901c18828580c1c0221f55abf54777634a805740dca19de85fa1cd1a3950d076013d607860be5d4d976bb2cb8dd32636ee2b063c27bc6a78971548b3205005ae3403fc3d1fb2d2dc61c50eee70c761bbfec992dd3999b1977afa5a784960e29f019fadd4e0784c1504bf091d562f5b2c00311e314900afc3be0049c6d0c0daaf86d608472935792e5090bd1a0b9a4494c4a51b4e667e8cab00f1a80253a0485eded6e281f27f6ff99ddcca80e4e2b8fef453ad36bcc77a2cd4e708d6ac309e7da309dec6d73084edb412ea542cf765d4ba789de2d6041b6d78a1bfee4bc0a5a42a5ad72470bd572f2b46be3ffc1ec364744164733e23ca7b5573b4edbc402719b269dec1aa4869aff26901c7db49ad157d256ddd2dfae8b6ce33f57bdf600ac1fdd883fa43e88f04ae3e5e4b6a0e8722aa22216a00cb44d1102a131b0d30304e6768b1cabca571c4b9c7e44f2c2cbe37418b419ae0d52df30c05c51ca430e89747af44b213e4081635234f4eb05fb76a9b8b66ea817282d7220aedb6c4e0a8e92b97a10ae916965b856a11070034a596e434933713d226b3fa626781d0208193c2a3a0d1d5f7ff57b5d102a16463a0929369f56b999a5cc42236bee16710ad2713d5458ccf090f5d52c3e94b2ac228c63928b5afd0b80a1db75c5a31caf0944c7c57a5eca3e7a61e221352f31af52f21fe66a5925b759212437f129f81809d74c13519c40914a90e422a5f1f105e2ccf2d0bd6cb05425bcef93b99c11560a4a77b20886c182dd8179b14bd707ee43615dfd2da94075e043ffcd54560cb2057a4658e8da8861c49e8a3488f16e7e24898119e24e91af568ec0cc3ee572a10a6825549e2ac7042bf1d6c43eed8a7310f23da10a13307233d594311554050d0f1c902cc5bd06f5480b725e145924288aa8f229259e5e1ef5bd3ad8a4f320830a9bdf3066b7192f58fdbd3f4a7563f9c6dbf5cdd511c40ad0fec445df75021e009594c3a3be846128bc6804c389c0c6ae5d5605656f2b55d2e244fce9e7e3580afced95bc026b9c642dc3805abc67e08db4117b374335000419fd5d0474b0020bd80f96ca6cb03024ad4d831e11a3f2a42a5f24e3ec59d0572054960cbac16e0102b872467cd03ce84d3cbe2634cf3d4d756836f41f415e67b5be299adb0d8002564d180a05bba67ad5fc5acf720cc7d3f549f3ade9c4f4a4fcd27609bae2420c5ba9a94ab02666dc1ade644d4e7d0c98d8d6a5ff77d4a8e539cd95badc0c30027bae1043899df21cdfdb613f804a6d1eb02c9eb25fd01aa3bda83edae5c3350a751219e141a25f481cc74b9fee0a854c5700ea695e57a0ffa24e2adf97470b02560d5d4158e313e77b6e4f05f326f5fa6dea5b0db62843e6f5b312c460f53707c487476311edb9fe09ba78302b993b21840872a7b08554df0c6095b0f0635407c2c2a2c068a2c5131ce72db505e1cadbfac426e898a45930e15a51b72c87c606676dbbe9f0d9c7f048cf96a8adfc71d62931f799faf49557c25088b76ff989027610a9c20fea14a186cd25f7afda596dff08da3e84c19494f2988ac80af5e408f094d37d013aad639c6c577a5d5a029ac103680b389cceda16a6dc8467d3720447a00bbfadfb8ca085e35edf26e40aecad699de753852f63b94b6e0c5109640aa67c5723a9f8eacf67410a3e6607e154003921fe153b1bbdfb658f4776f6f8012d1dc06e73309d5e63b2b8bd0e67b130cf4c53cd30a55df6e48f6bd5053d09027fa41a8e2539ec40dcd42f719031953a733a4920a2048a60060cb0864864fa059b58bf9107fc09719c38715ef930622e0c203aba5abbfad4566657029493510e4894f0856d18c0fdda9b22b89087a2aef01d308c3efbd056ef1b058af94ac405d83ad643cccd58a0d56dd914acebcf9ff1336bb4da48b4a6d8113ae67b8e770be8397070b154856d0716d85a101518c128207f47a29f6743345a9534a15e6f07b9b55a090c7299d0688d98756ac1659dc5202bc724f734fafeadaa1edbb9e201e77b59d25c04e6bc16431d0596293e1c6ac6af4b79c4e4244c245cd68aefb105512712bd8a8411431fe0d6f517c88be497c166f4be56fdabb7fd5ab33999550d4cd01cafbd35138ec19f5f8545ba0aa80981794f3568d129c4508a098f3e9f034d1a79e86150ed4fcd7db1c56114abc8e28e6a9fc27ba5d74ea89bd3a1aacf0a72491cb8c643d1a9ccf101a696df0d09b3edfeb3a0fc33c9efdbc91ef328b800bf398303bb9c56c0e92d924e608cc831f84e9aa43f0cf06076f2866560c4ee02f1c3bf7a6a1b4155462af6670ad0a33c7cb4abce3a0e8498a7430e49886f2c09061ea995ebfb435707dc398d61dff7752d67b3263e7710ef8e75cc82f050720c51e3db5319f4362f0bdb83b21d7a707cad2ab2eb960857839dab616a1ef20c09b5cb474ee475a87bf799e60d1407b7ae97b674908407b0e360e1b9167929680b -generate_ring_signature 2f70a3f91cd8002c712cc239ee2488b0046d4b010eab9d35554f4e1bd9d61b0b ad0464c09da58d6bdb00c070a4e094c0883cfb6eec67fb6532093193b4b1f6dc 55 e19f645f9ee2bd327eb51f336a58e2c9db7b1a64a97d6803d6b56ed4fa9cd560 2c9c1ca72314e021d1cebff7509cd33dec503d78b8249bf712cdfd55895a3726 7bc59484c4005b65ef76ca020046038e3a524db3c456d48bd6e6dd45f38a252e b849fae09d21667f19fab5fd6c7d0544b59bfbed39a524a942d48828106cb12e e16fbdc60de52e405d04eeb52b3b00c0bd1fbed1d5af08f4f35be6428bf4fd8d 9ab4b2beb1c9a8ae1c4c33c53fe19581488a836bb933b45bce4884604a5ffc96 0fc8e4a7ac769f9a692ec80395494b43e581f82aa65b4247ffd7a67291fa3b58 af9c421abc18053d52ef23a6846ddefcd717a96fd90f2b04ec33eab670dbd39a fe83d20f114e98d35389b23ec392dce2f3a110c45710f0c61905bc5c77bd6389 ca5eca751a73bb5c81f1f85ee75c0b6933f364579189a9e010b470495d1d4166 678ddee968928b2be7d171f934fe13a681556c03c4471d2b3a0c106aeb16e0af 58a41d7ce0ed959b012e587542acf3031f7a878d0668573f296e95e79c0c56e3 1f98f698a97f4bf968d8c4a073d62bc50ab6768dc8f0253a6ee7d0a9b8e400e9 203f2fdce050523df6932b625fcd99c5d592853d69594c6ced0d8d1a27e2df4e e682c2bb504201724f9d6831aeda7347efc2aafc78397f237ee36a2d609adafc ded39b9bc8849e513f81e5c22bb93f4d7009522eb63f580e9e5ef369a009f63a 9b4566467f59a6f545fd438293c87f895979da6151e0f713f0535a6a78edf605 b16fd389f7553736eab1855cb33822999b4c4a28ad1aff92776724fb2935e91c 1ce0853c4f87e754e05213c1804225ee1a2120494b20fe23bcf133b33166504f 0ba8b536b491f31696ab1d4dedba8164f7a1774d648b6aaf571e7a1dc00962d5 df770c4d34b57b2cf0dfd5e59b8778e4f6821796acf3e5a8a1ef2b6c6caea12b 000cd6a7b4564634f50f4cb3bb34040bbbea725ba56a0226603e12f16c75c838 68d4b4453419af5172aa54d1ff68004e0e8bc785605c87dbff1522a69fcf5eb6 6ea42b2fdfabfdac3d4e4dd520fd923c25ce1d66c5c536f21ac14af60a3a69b0 e6c455a08d17e0f82fdb4b1d2952d1f03bb66fd20b40d80f693e6f1621261b8d aa4da257da007f817e0ef63694a46500b56c649b6430cd8ad2cd98cdc92fd5b9 ae71c3343d5a2b6124deb034ac1c381121b80e9cdd3c1a3f8ca645be245c802f 55c97d73d6cc3e432460456234d554ebc88f4153ed753ae69a42ed8827906384 7d2d00d52c811603b99a492a7a2459d1c88cb5113a22ebec3ca09ffc126c8757 b487a8889eb4a2e809205aba036adb11473ec68efe7bacb6df6fb0ab34b5f563 636fd45289cc28718d29ec8dc3a53ab20f2dbdb81aa8a7e73ab256f16b075484 d4c8554683898da5f9da8a64c30fb3e343cfb19eb88f7fbf567901a9a98d2d71 160b282251304e7cccc5682a64e5f4a4c7356e199b023ff9818c25defd1e47f6 1a5b8c9c7a12ac3f2470048f0642602e07796a4bc7bc095aa88a30976fd3b873 d39fa1a932a00e90d0c2339bf81666c08f299234846b54124942a9db33365b48 d023e5c5466fbb9f9f4210e53965f90b51efaf8181b4eb11f28081781f618fac 441afbbf59c3ce187d0ea4b7f0b8c886509b32f762f84b64d64cb1ae7e4661b4 7d9521c9c0476cb8deb152527709412eab60f79e6dcae7d6a8bc764b521552fb 7753e5dd3407847543c066c3be5b57568a00ce2156029dced77a7d7f84b51c49 643c25c296ff6b10e044502b84bbbb8066dd60c13642ed568cd3a127269cc683 af193ec7757ba760a786e1bd6dea91abf67e0d09fe1dde9636ee740cb64b3881 97c86f4eb82cf9b008f34d77be77474a9dccbc7a28f8af3135804c4be4d7f4c1 42a123aee5df04b5d770c8d135ce837ca7b61f889afbfe459affc364683bbaf4 82443654c82e18c320f5c5a91eddaf6a009a9ab264e19fd377094165553a5798 1c281ae660b2a48d4ce02e48fc43d19d9506fe50ce98be6138ce2a12f104171b e697e05d9ca04429039d3b472766d362bdd5bc09edc5749244a4e1631d24fb19 c64794f14532943a569709acc96c45ad352cdfe950a925f2d6d5d15461e24dd7 215eaa77ce36cb9da74e8b4cbe60fa4effe632bc03b8ac2972219d7197b1755d e45863fc78b8e87aa3e1527121dc382a16697e8febcebf7371975d02a8c019d6 64c05b4302440eebf39e77dfee67a5c556c28240d204684197c36c61add87089 c56a0a14c23839ebb8fdcbbd53da469670546a8a4d9b988e4283fecba308ad63 ecce137e698c68abfc86d1e30b88654aca22c4a81c5badfbe97db39fd069f4cf 0261b71c4342e73833ef38913e92cf7acd43191b63872739d4c419daaf35d193 978a3653c3a18d1f58e3a222176e34d37ffbc816b80d11625e0ea049554b1778 bd8028231d3f30ef112db35443850f73b573f8a9dc3345ca5744bb7b97ff4009 8eedeeb643d1d8dbc79f2f62d47dad575e1965b8cf1c25dede6f010cb2e50300 24 ead41e0bbaf52e05f8b0f2752e22dfc45ca691f21ef63d7fe227f9cdc973bf0b54ce4a2477e93dc3cc6902fa033fd7e77436a6d37a39631a1a48f27b7e76030bde4e500b60eced5381ef1a8ead1971d0e55b1fa2db02b9100385e13f219eff0649c0600ef9dd891e61dc9a4f0b2dc9215c0fd1d3b960f1b9ba83f29b5c21eb00d8eff24be5dfbf843a0bcdbbcac8eb4550f10e123f4fd6443c0cf8663166730e7d231f3c8385ba6f45d6df89187fcdb305de974f9e10af43b7fefb7051ec88073adc61031b5f4da569de4b01a33980b9a8f8ca918c29a5dc52e04fc2e633570647e0b6fa300a2e6b9b3c508154ee95cce07f05706c7e3fcd3090f05c29976608efd15881023abd9e7c949134be1b213ea0a2f84b3ed0089e3959e26dd81233063fc65d105b72d55659031d72a28b89cfd17eb047ce6e3b935644597e6314820e59f04ddf9a002b9caaefc439cf66a3afabfbf7588fdc7da1d9784a7c6375880131dbe82683e52cc1e95c111755a18af4b4eaed2c107cdff2fbeb022c12add70542060b04063b3db811aafb5958cdc3a11e42ca4a4f209bd07bc5d39d28962a03ded6941f9a528d318874fde75590889ca2c20d62dc1091821e2b12e227436104c1b87ae46b6a3d1c50bbcdeb5ef4b4be7454b34ee6375483dcc8c5545a666102b1268dc5b85be9019559f88cb1545ed58b8ff337f3c7d8dcd3da65438e4ebd01bcaf90ce90a8cb925516e32924a34a4e9f8e0cbf7a94a2021837b0073d3fd0062fcd65bdab92b694005e9da0d1b5dd765637e7aaa8bc9e198dc877a18f29d1054f3167141781235ee5092db5b49402284f466eda233afc86bf7f30f3848bc40042ffa2bfb11c71719104bab42a40a29c6d2749e8906299252277c431a710f101d1a63e60d39c08b532fd08a81902acce4f328608f016e745e4c0ee36188ad1031aa0f45fc1a151ce5454ea222ed2e618912610aa1e048d4347eec47712ae64053cd3cb28f8e958c2e8d1bb6c6001e71796b5fdc8f51dbe4dc73b4d48831adb0e609c2c1cd1447434aee3a37fb834ab2dec1ea0b2869408ac8377d0f10ab0560326f29f912bc3d8523726b289efe3e59a61d9fdf21af8429c7459655b9488ce0cdc23a4cc689eaf1797efac41e6bf5c82d946b1c2160a4e45968529f01e95690477aa17e46c94236a697a12d973b8d1c653f88b3f3c2afde6948d0f18e631d30c4d5f6e3e70bdb86003e95c689d1477c30406eea99eababf5a843466b1ae08b0a8d60b3c929eb2665cdd86f0187052c6cc879173db22b1f671d0db718473ca10266da6b7a8b4018f1cef417d910d0b7bad48d903346d908a6d4bba953b423ca00f621c7f740a09fd891aa534ae2e28f720effda033ccd39f80a91029de112b902c4bdf0a7382093d610aa4752ee1c95167db08fc0f0ee2469f1cf7d7de4d19b01920f4f8794f7d5f7dc91f8176232c257ae9fd934dbcd0f9f356eb9c678138c05c7f21e7142d1fedc3ac06bf6430f5c78c39233ce1b39ae8794ab80144258020e90f2d4962ec6d485c10f9522f1b1a337280375d58974afccc41d76ae174bb60cfe96f009df2b79bf44b876086b444a55aadfb0b38eb664bbcdb325d077abbc06cd2a26149da18475703b83171c9a8e915e959587f0bd0069a406ebcb67d7d403a5c518e3044269ab6a4703bf69a3fa8712bd509c806896cd2ae431fbed3cc3085260f657a2e810d24235e9a03ec6f14f48d9a2dd04efb532c6e13dacfe03170d6165ab9fb3456b84284b850207c78b634fbc3f9e47af1f432199b35e2037cf04bd3f46a0ef52a7af417d72cccd106adc44b322ee1785ab095bcda3a4d2a45d0610aaa101202001066079bab1384adbeb79546d60c4246e74de2167eb87c50203fbfb2dcebf5199405bc24c77360efc7287a71ee234e263845b31ebefc7470f097afe33197e972a8436fe84f96b110657a5d2e4e13f30d090fac38fbd0ad45a05a5defbf6f761097a8674ed9777dd808618b7cc02847b6a090ad633dc5508e606c8460b3f1a562c6842cdd810b0bcc9577d2d5694fc3f0ff1392fe3009640540ed90d3b061a25fef05f379ed576d1aeeacda9a8842f0bfc61e24587e27daeda0663e0bffe0db747a1bbd0f931c0f31bb6807949ee05abe9b82d1cf03a7f3a26030d6b88bf6cd145f9c8166ea41f68228ae34aa14eab9529c3282f587bd092700f5c36f6f179ce789f8ee988b293ff728f2de7d8b338ad4d2df949008177cf2b0a3eb6b5e565487459c933f4f780851af650e773c5bc8a37bcea9a6ac1352b430f0db0b53e0158aea3f525abacf7344f2ca55710478e73f2349f9bd9be583a50006f67cda9877cf58245d7c288e644c0e5257b0b58612b5e5f1a2f367566f4980b6393af26ca96880078955e9adc7c1d4c77bb14c24661a04873442044415f1e0fca75f75848b4360dfeae55cb266c84260a85022549f4ffe84e93d9c91ed72808ae061c95e95758506aebe8f8f80700f1b135b77f055af43c56e29053bfc93c08516111a6a5450afad8d18ed7b9ee71f27b1c320ee06c6b4354724e95481f2502e43f14a92f01f444be9c9dc597146cfa4c2d2892917d9b68a9a3e1c25b02ec082420212faca1e8378ef8114ff8ebcd2b67e8906e28a6c72bb9f0d26a5d855103e9a3df5323370e8e8aab333087010bd196f2bc02a52c0db3d0eecb47b83b7106e96850d719d65c3c070a0d2b5e4e6e447667479b58f9b2e47315678cafd09f02092031a2cf8df93c36f4efcdc4cde48a6dc76266b42511a19a08e2ee9ab9bb064cf18f64ee5d14a3e8b96813489f0f44cfc0ce06b48b0bbb0c2098eb50c58e019f7c8d6afe54f2568c07eeddbe06eac1188a9a5a8778912bb4089902b902990c747383cd7a13f18fb1503e08cb8c54ac56bb8991ed8b96282513c0823343540d6f9fbe12f526c2c6aba727179174cb63a9c9db8ae1286efe620daff863a24a0bfbd4a26df14c858a56ce3c15e96406c2576875b8ecfff678b3883c9a57f50309b51ad9321f538728fd8abe5eaa9b6979449a8bd15a05e6ac3b3a54a2177e4805ef4e51a54d8ed714eb85ae9d36d207337be9502719f4a5109ee10efbd4696d00b02895302b739bdde38b7496e6532c7f946fda61fe0ec052d2dba7979f0a4c021e3f5429402c2f2d438d153658348b9db1fa92768b58025aaa656b5a0ceeb8071527a476eb8e8527d1b399f0084c7c064df912b0f5e51a0130b0c5ab943581053bf6e430ea696bc4fe3b235590c7c2e5cd72fc22ae0b765a7386d4c9b06db80289e24dc6519f251573a24f1639d99e2ddc4dadaf8bbb7b375d8a8723ecddaa08f3cefceb0338dde3b2e793c1824f21a89a0f33fae835c0617c37414b4edc6007bb46b66883e8d12fdf3fc5be8328d2b99515c57356165d7810c19b4b56283d06011363282230023dd26a67c78b73af6e6e90257aa409717da7cd70ed6ea6a8040452a5275aaf787ab6079bd4c33eb1fe62dea4afb601c0d99ba698e3174794095ef3b4d3bf633591d21f63dab0f995c9d90f94f6c77326828689ae61823b6502a74a7a717b422a905543ba6e20cb9bbe7746c3dccd2a99669ee6efb67f81d20f1ff5d63cddcf3a48e0731153b27fce5a4d1ca4c7a738f61f427cf22a4ce70e08f0ac3d04b27d80e701e234e518d59c840c3d38cac023a244510c60e44a8ad40aea70c0bcda31771bbb1bb303b8ff5b72eaacff0722d1701f197eb7bf490350025a516ac37a79e5eeb1a74b45ee4da771cb789dc3e1e4bae6a5b803a888bcd408c2a522763686609090456b207b9e32dd9b52dbadcc4c2f94a827d98b4360b202a40d2cec28dceee6b9a42afaae7d5a373b45da6844bd74cebb387f7ed11cfb0230549ec9c130148372b313cca0c0ff8551e59ab3d32928de7170a5e695a46101dd3beabbdb229f35ecf0b27f4427314525d5c9555cb4dee080c026df5a12be0af98358187fec0ba4675442a69e69d9e032947bc4e3c20078bcb9a382c6cfe700f1a3493b4e409b60f92802647b9ff73a7f28bba16ae6b6be6e405cc86a8bba06b8b748cbc4e53ab2e41fde6a0b5e22229ae183a9f685125f1aaec6b557758204072e6bd33d61314e0e8709f0c2ddada2080ca41b4662d238d4fecbd60539b80f088733f68d578963f3cafb584c45a02dd37516e2702c93939a067c78356d1c0616e6c2068c65d185e48cc4c156687421891bcd4314b43688dccaf83935591806458a1dd5b2b52b22b6f5acb0db3b9c30da69f11e31d5c0f1e6204bab63342c0a85f658bd93622e1c87d52b6454b1412dd49e35b4e43b0959cd4f1209f3695c0cd590487509bdc883e2042de7a74f4d05986089671998b5149c98c319d574af05c2db17713ad6f8a70e1a44646182b23ca9f1df2eb4ade81ba0735521e3dfdc09132a57d8a556d9e1428c9beee129d0d559698882968dba55702908ff4365000723c81206678b4c3db16307b0f969b269a574b800d4fe1676e4827ed979ff1b04c71d94f0730406810dbf3742b0965aaa8915cf544754194dae8067fae6a633039feb395d8cf5ed28de3d2d613b2ddce356f68960e989478d459dd3268a3d780386f95381f5fac375ac9204d1bf2a446372c209bb5ae8237430b8252439b6e40bdcf888f06d97e145c8c031dde8996f9dfe1c860fd896c2e2c4b109bf6a2f9a005659e9941bb25fb77d5467a5d257746c14ceb737deda0d5f949d2c197c881e07bb9ad1cad3e9257d41070515a1938639a1c0889d22e9635584e8b50dcbff300f7de6908bbce6471dd0fb1cd915099b968c6b9a53fbcb6d9153d7cafb4b11cd0c2e13219139a130061f6cd6229a28b47de01e39c83f9394f60374249e77e894098e7eaf51a1a7aba72210d9c85e118343089415ec5e64bad66a811d7080f5eb090c6afd4aa2e59b854cfafeb4546254804c1651505811f81e622255c023795f02 -generate_ring_signature 0ba30de6e6d2dbc9fd491a2874728fd0280ced45df74f8c8d32cf2e77768e7ae 0b3de9afe1359f3f83c005c7631c44a73ffba60a836ae3839d8d5927c4471ed0 152 3a358906b6ff770b1f077ae12d8f7f3cde8cc6d3c19a8c9f5bb6ee11b7388e7e 472a126f08b58e6cbcd37f5ac22cfc6c662546bac3da95da2ba09452f2437a91 fb4235440693b05fc30539541b237b7f9b5c7ad68df2955361c3de5dfad8e9b3 591196da4d50c814756f5ba064cbd38d1af3671a407d8a05cd8d0e056a4e012f ae702c49cb7fb2dc5df1a5e1c39f2b2ce402f2945ddf59241dc047b4ef81cefc a585b5a0091db1a7ab1bedf09903aab8d0b0a844d74e67de526bc99cfc23534e e092843c820545862a9e6bb15ded38458d654c953971ecb97fd1bf7603c1dc6e 322a688cc9f928b3499215ac5bcf36b55b781ea58280315c156841adff146995 ed842c7281842528fa2b17d4d910377ba970d1443a686d3742a18816b10b4ad9 daef8362af755e7e0d52afabe3a44575242405bc654d2c5c59517b98090bf334 1c3845699b373abc21d24b1107ce31e3d3eef3e7ea96c491f7c6652ce57465fb 14261e794ec119d49af09f570dabf6729560be83464419a93d02f0da7ddbbad4 33b42eae69927195bc2de2fb5206054ecf6511312a2528d4929a4a9867002813 c796fcbba62668d7a0d2348a1d9b5f06ab4fe9b157b0d846443b41edb9a1b6fa c95fc5d08879a12aa68952ae167d17993a7e2623297f75fd2272c82e32bfc37a 6cb2f098c37f9b3c863566c29d56939ebdbad47aa24cceb566cb410223a846b5 b77692430504c280106fdb7f639c495d8ef0d7d558dcbd75b23c03f51f8a3e11 4159b10f826b40b00b32991efab391e8f6c0222bcd8ebc0ca35f01c56023e0bf 3fd351cd999eea1dae072737a2352de6e0d35cc5c03b7b66784aae6b5e8a1466 9a184ac51ae37b7fd07b91e062c085b6cfbe8d9fdb539da59cae447ae7a7b78e f3542561f7f3543004b64d6f2d45dddca1fcc7ce148f8fcc240e5dd1edd2c294 f84af966409541a111985fa9c0ce4d203fb6d449f337eac3527765a7026ebe87 c85ae572e78401bbc3594bc748bca1f6996461cbaf8c28064aa3ec6814bb41c2 4aa24501b140eee2efa6fb6980eb6bb4b703eff58c9069b54f1b30dee82f46a2 c61f9c3d781903381bbc07c6ccd2631e6592130c7ef184d2936fbb156ec1560d 146811dcf8fc8172c1504d032ddec6798525e1d8a9429517ad73641cf2ee7499 556394b1a728ad1d4cd280e5e56837702e029da3d8718f50e5bbaf6f0b05ae17 0d694f1df6ef6156bc6f8c5cc2a3668801e9a9f29f8436d22d25feb331afe929 7ba9d54920fa1923eb772fc59f73ec3578fa08866b226d11e983cc8d2f8ca631 9e4f1d09796df5a2f21b9732dd9a217ff85b1acfa8ad071c0f8cacd16c875025 77c0433220a41feb333fe7a4c76ebee4dc10a08644bf530426b99b21dd0ce3c1 1a34d9fe7d8b4503fae56512bfb90821147cbd0d276ced49d456da3aa59f50a7 70f6147b9a07429cda27dbac9f7b69ff87293a49a6bb32e48e5bd7c6cece8bc2 52a798ffabffe412acd4e9afe2e21ca0f5c3b7b83481d18e98c03f916fad4f3a 6fafe4e0b6a1bc4e19439958d5b5eb6300e5685637a976595748955ac294450b c9081e39f92a9d9c0b0d120d3bd1bb757133873ee5e02dd6e428083df7c89cac dfe7f7dd98096fc79fab45669c8173226c19f953bd7d7eb2bb2739d79dfa2e23 3c3856df0f874c2d766f14ec708a366d23128ad13df93d73db9156b7d9f074e6 866dc59ada75d08e8c8ef670218581db6a3fcff78d62d8f058c4d7f767ce6e21 53ec7e68ff4f0c96dedaee098825b83a4bf56c3f6e590f787e4fb02a0491a035 66d5a0aa788d312053b71aa88fdb961ef22eb238afa1eb8a50f739d821a27e1f f5cfeeb51f1238168ad45c7daa7caf2632caeccfe64a2d97e6f93bea5fe902ea 575fcfea8d4bc586a780acaaf079f4a75d4bcaae1ff008cd5e961fe7f7a7bbfc feccc7e6f4619532c2b5cfb43a0e7240b35e7824b94086a93c2f8ff76bd01392 7fefcab77bd3284fa4867c63503d1ff728ebdac0574632be0dd8afbe601f3b81 6bc526e9c885d131970279cfc139e14d213ca40384f021541e669b8b64cf6c5c 864abdb33355465fd8d918c99bb840169d53e1532c8d82aaff27ccd1197f8ba2 2d37d9199ec4b6660639f4419cf184293ecddf90f8d40420e61bea0971ea583e 45890e5e880c0607c04b9e703270355c33cf305485b9c15b9927ff2728bfa5f3 e5778f46b5adb329727cc6f39685138b0ed0932b3542873dd4448909f7d3acdb 7fbc3ad923ce86f8ff5c34ea3b657fff3eeaafe9be2e00ad74e02ab4a579e558 a247dd3e76847f4c67f98e8c62ea0552c69410b57f59bdfcb820dec44fe377cc 31f23aeac4ecdd85e4833c8adf88b7c481a6f687b45920ae746796c878ef16df 7a812f1702f24563d4af961789b40be3d5b7836277f5c96096385d5d373c02a8 6ed55998c547fde1f832dbd3c496c4b1de1d17cb2e2cace2cf188d38ecab1df4 f24160e9814de8c26b075a6f11f085788ee84abdca189158a0420aacdd9b6eee 8255924674ecbcd958d0ecbd07c8962bef86f2d2a5aef852ec8f1fb7f9d69c35 129592cd3a9d7e1463862b37909bfe1d74927582b6c45a6afd259ab07ebccbbc 5d44bbe0a39fb5f35dab4e900a447a42ec4153eb53f69eaa47bdb9bac8137d8c 18eceddf52dd350616f472d7d1b8fed16d25297913d7f2f8ed04883b999ea48f 3af3a5785978cc66e200dba17afaa3b40ce4a5783fa9333a6424c4ba06fb7618 901492fdad5dfded52b21c9959a178334f8e7168b6609bfb077e2a7b93ede507 f7d79b9ec77df91be73f405bd30216aaacd3a055c63efc4f86f0b109798655f5 dbd4023695ec11a04d54740936f407fb2c2b6cbe979e6ef5bf0767a801d0a178 93ab27f15725d4630ba876f4a58ba78e4e560a410aad53a8a735911b7196c6b6 545578c450ac54f055d46e661d12d4d0adbe0849a61e2ea03d6e3d4638da7cd9 3391411ea286cfb663d15115d113f7b2f0826a840395939e8d5b611433f2d113 57ef1e875e3855b4ab4670fed3d630e6cda0e1b9341891e59b7e2f9914c947db 658ef5e1bcf430e63a0b9122ee3a84ecc9f47388f7b31c6fcf366eb8fa9d93bb 2658013beab0b98888d1eb2f795f64f16c8855dfc503d3a66239a0047c5593f5 69277ea51de11f6fba8a43c41fd968d901c4e31e822f4b5b3553318f847b1995 77257767504d0df2a8e5500dede46246b257f7a9f304d5e034268bb02c23e209 06dffc165810f727b325edf311d3839cda6b71ef24fefbc3f455121092d7a938 a50590f3d9193887a33303080421388a59ad10c546a1311e16c8a7313dacebe9 4a0796e932306dda67589cb37f697c2810df1c1747e2b2fcfa293b4f4c40c4b9 c5adb0180758e9a960558496d341d36e639d8355e36e076ad31859371c8b1014 9ee7528e99713c1bd54457781063dea844349199756561b4be6c384b779ed8ef ec8f58b2e690c33d2d30384142e787c7b380d422bc7845a99b9d36ea5e1a3c55 151a797a15a5814ba694cf2bb077185e5b1977d9d18eac0ad772e1282ba5ce80 c08da9572b6395a5cc018f6cbd1da4883d6a2f7d3d76502fdfd72b655e1bb5f7 30ab619fe844790c294a0434cf663d887898c694b1bd374b2558216eda20e683 196b383d573af3795f23d86ed51dd55d9c3ae875464825141be817a0be662345 153ef73d1f5b17c75c970bc76c29e4f2ff6bb212d76e0ae7ac38445c7c76ce97 4c8e7f367d39ea4210f0154dfb333766531983d43313240753483e00d1b72116 dd237005fb83081cc37a3d4a781426d7c72fbf170ef7973256d3b9cf212f2f8e 8baa3f9913024928a3c62e82d8d89ef7438fe7110b9dea1dc138c7618837403f ee427f72f1736d9a3b416b363db0e19250a04cdeab9969f6209bfa556cf79e5f c7662161c5e8c2390fb938a10669beb2713a9eb58fdf61d4476bfef7284db7c9 f9bac6ab6e3346e88b5e48eca67640c104dbf8520f08e240416c3ef27a29a332 cbc2ee1e4553e90b60ff83126c636bb047e95d7d17e941aba994bc5e122280ec 601b1aee63a11d7e9f930d9a64f672280b17787131d12e46ab0140ff01a3372e 90fa3f00483356004def6de99c8dddf30d340b96a065d0e14e7e2ab5929771a0 5f762e1c5d76870eb65f31f7751070f2664a90dfa7ae1df568f64f8478368bb4 8c03cfc38212d86cd7002a0947128016a0131983552f6826d20a84bbb052aea6 b188375c340056fb061f1ae3bb28ad17beefca3e7b5473fc9087e257cae927c5 dffe5382445976ae1ec4261b3370718468ba4ecab5cbfb50a22ab6d38285d3c1 7283670d3cb766afff7f34486489d5a1a5b0bb823fab655657166de815abec24 f7713361d1b7b267f55265c03a458233b8d077007687308da0ae02a3075c0d4f 202b3150f80754ff14b48f57a8cb648ac372fabdd91874e45fd0b6b5b26b2de8 cc9ad47925cae8fadc55c3005147ea9f93b4b8d7b1517104c24bf0b893ab46e8 be42bf5d3f94f09be5042260f359c8292dfc6f7c27ab8da88e9d4d80ee553e78 3bd3222539be23ca0edc229d3eeeedc1b41b8ce296978d73c86e762af79aa887 4080704d2b09ab7ed4eb0b8b0e439ec8a2ead3a06f817ce3f9363563950fdb0b ad35286dbd79ecb282fc7cb18c5dc7e7abd53bbcb26c824a3141fc054b8d15db 4aad9cc0beaeef5482e2950883d7253a309ef56e7fcff5df54f5f8e654868cad f1d06103cbc99b6de6494047a63fd690a1feb3082c52eed0b2ecffd5872629d9 3b55b2070aa5fbbc6f5a160de4e9f3c2f4655278f38d02bc6f5c5987f6a5d46b 8b6e16fef782152e2e03abe8a72a0c961bbcd899b27e62fc1c59a9b703aabc87 a027b3419222bce3f34445a08da62ff76712b33bbb62ff25436046e8c97ab78e 07fc0c70593b4023360ba9026368f270ee095f65cc7ea57f459b739f73328ae1 a2bcde7f605fd6d05046e0eb333081e8491057e369713b9f9adec7af1b55665d 687186ddddfd074aeffc932f3d56fa3edcf50479c2c537dc81c712c80f0d60a4 868475fcfd1604aa98ef79866cd4a7ce314850928880af9bc2e31eda14f07f79 c46158842eded9c26dedcc934ca9d51ce3eebe5a6173710225bdded6ffba7643 1ad3ae4a9d113f8991939e7396f74fb0d96280837f67caad692d18722a93a200 0ef7b4ff25837eedf897a910f445eaefe4520042bce6d666cac0b206db3dd1e5 7734da981ddc91893bc8a527007f824a22b40844d0b4537209e5d3d37bb27091 ebe6fbdbe09907e6fdf2c89841e1c57cf69c6e324f8934559cef075f7ac0e2cb a5ffed386db8174633ba84fb978f88f8217a6afda3ca2e5acf6e70249b0b4937 c130b6a8e631f76d972d0f155949fb7fa8d093e576abdafa584cfd192cabdef6 7edde741298c76767208089c421fc96b728a108eb96e60618ed458a25e62a0af 3453fa314a63d4eb7ddd4ed46dccefbc3c939afb8eb2d00a3ccb4592e5e1c39b 001057c3d37ee9247b99984c5e5270911227156b442a879a9ffa598b9c0521ee 337b79ce5ca206ad07c09efd4b90abf7d907be87cc842cf4c33a4a75f40d03b0 13f81b423b6fd6096ae3e0877ceb6fe79453a2714d72f43b8779d3f9ba7bdc56 a6f2f646deb6f9e205ddaee63ef8a76b3a5b54a928ad4f3e1dd88d16fcff3992 65cd6f6ebc27c988cf885cf88705f1868f66350f1aa9eef50e3e9d032f8f110f bfe7674163556b3b7f7d14d706cb97df910eaeac8e523d564aee31a02d63ced8 5fd0212e3214b85eea338d564a336905fa46275d7207da69f5c94cd76444b66c 0745a8db9d2d5d2cd987cf39a9719da6ccc397dc8a979bfe1b98755b83ced8c4 8d47529f198578fea2df336e62bb2509a978e570d20a03d0cc5ddee398b90489 3c7ab76d75cfe7c749c818939a4d00b22cd55d17811f1e81a2943368acbc9930 9032f04a5cfc16c2560a30ddde438fd67b34f03d067266cc1ed0d6b1f21a8da1 82a319462f7be63add5b1de1c57d4f96b7d01a37d37c1402b8ecfc7c23172e45 5fff19c41880b0ab48f57fc5453d4a14d5c22d78f10ae7bc7a5ec7c6d397be7c 7b4bf36034148946aa80874237e075fb735b3a5ba7a25a085fbf70fc19f63f16 5e367870d1be56519048148b998a8da59d93b302f8136ca1ce8265d92653398e b1ae44dcdc1345606bc107fc2ff729fab2c215540b088e6117fb82f8469bcc49 09a26ba5b7ec87989a79d47fdec7febf79647abfd0efe6ba1078a15b345116e7 a53e112ca9565d939ac0f80c9218438e05ee1cbb40e094c4c6b9778cd1ee9341 4480d3899ff9742956f8ea9686d3d52082cef3e150fd8d647768701d99805550 0cc2d147b225cdeb658984df806fb6121de31e3c43d73cbe979c8fb8251b0bfb 42ed973a0b53fd57c7676e9facffdba1fa56437683ea6839537af906ed04733a 00ff260d90022060b2c237a1a68251d150fc53b94af0b47adc9d661dea155785 8d7bd44609717a19a777658dc0aad8ff10c8fac87e643378a683f0097e916057 47f05593d2ee2b00a2ddaf85ba6a7bb9c56e09cf8ade444503dc4d426e2af9b2 caa9a7998ed8f932e5d50c969b38da96ba85fa5501bb63af160fdeb987f6bee8 8629cfd12d7e4d98962a96d5359d6df658740b02e3178842da6536006b11b20a c181bd65f77ad32482f0e347091f9e6469d6867b55ee420a6201fc7eaa5f9c4d cf322dd9ef25987283f3e35f9f457c5a8b04e8ff506851d068ae1b0ff35ad2cd 7a1257577ef572707447108a254387944435ae9e37eee8b92ac3b21164599e0f 57bb7764c06e796b8594ef58c605ea682790abf86cad7c3fbb6842f18c70cdd7 1358110fd9c8fca453bfe9f048ce071eaf67dd7f1cce4f4d52e9cad916554c03 107 584a30952fdd12cba21af1ae8de5c3e4bc94fae49fad3bd1b9e031ca20f5e9047b4ec29be0e08b96990abec80b5a04d641461a7e8e1b8438656dc080cbbce700016bdccd921325c2f17ade748e6465f4792918d463c8868525efa2dd472ec10c78d3636015ae04186e14457f9828cf8c9e36f91c74e9c53e1d2ee7140a071801df3f71c479c531cc0cfb50b605a637c4ba243853ec36a98f89c159269866f001675a6a6e0aeb30a6020387b443c74c0e6e9c9a7d46ea7a0bfa4904c3461b970e4909061ce86668277121457c00e3f67e7fbdedc86473e64517a8b26b2dc4fc05a8c854b178daff46132cbc0fe1e03e3f2779475c707144a8778f0fc900c814061c6c0354cf669897f4d106325074187a346dc4a1fb3b671e2f6fc8b895f93a09c62c42340a518fd411b47a3043cd805e1a6a0b8a9d11b82d3d20d1f4b9472801902606bff2b6d96146a51619a52a4baee56b0513e58be6375e2aa6320d7411044209942a211c663cf571298ba5772f0fa1e2c50f86364952993b339d9c48840332ad1f1e7cf38de7f4d9623d113cc31bc13c4e58af496a92b1c2fca8027fbc06677b311197643b92dbd5e0c10b91ad248656be62b142d70adaf52401c28b52017659dae4e1526642ec34c956571d11665e50fb7029bdeae41097502537ffeb05be17c9e5063c14615fee9b3e4f6633ea03c28f15a6082e433de67eca50ab020ca42a3ad96e388bb8df18dff80ba8d27a9d049bfd9ef6e4469c912ab6e2311500732de6a7d7a4b59dec06e7cfe0f5a388aecc9322d4463ceeeec1b666a0f0c502708027bfc58e35e1e0a75ada8e503209953df7518f893c801464a586481150026fd04894992821c6d0831906b12f43b8db94390efb9a5620c3433f4882e5ca0e5e6ef717ef301c4773b7fc4dcf016188175583d984e9bf9e7b35faa46f02b30aae72b67ae7a74bb7d6bbe8bc2e8346c4d53ca20e2fa459d05aa479c059cd2704591652ce5b8d4d49767ac68977efed6fa452c2e168c7531fb3028f622779a00620d4c335d922b9a7456233e56304c8dfb97955d6297882d76251fb9434bf06087bde6ec55f003283b7b0ea7cd6f0f617abfd1f0a8ecaa0b827585c1f91f3bf012d5d92fcc6f3c587a21e325a96b00d313f21637bfa05cbfc2c534fdfc4e97704dbdaa453b6f4110a6491525493b7e27a3ac2b4b98b2918b4ce27b0d041d3260a6472e85b4370aff80e046092807368186c3aa087c1191b31c3ab1f13742f0c0f11a3dc970160dffbb26395b506ec8c0836f84bdfc5bafc705a6fc37cf3d3ea01a7e3500fabbbe3526906895e49f774e65bf45ab068df7b9358d7439dfa450907140c92c341dcddb72562feb027550f3f0f4d612c550026511ac611c688781d09a1d689753aa00507b6fa69b34db5644b204e2097dd60248f5b5eddaec240f50491e6b0f7d7e61bbc68982f302c4b2a2865f852bad7a357c929fb429411495f099b704ba7f7e6fef0c46950fe82f4f16434dd0e4dff5f807592e03f78fe9f740234ecd21074bc63759b8d45ef770dd26913682213440b4ca926e656710fca7b0f0f6d238599c8617b3eb6d430e44c71809b1d0fc533b1b7b78cac853abb30cd0a95affd83137591b9e60e8cb3089f6552d38ffbbf0477e62bb7b6c32b4bbc2a0177b36397b549887bd7680c9942e9cfb196bf59302d21077661b409778fd08c0e355b8a2b44351b9616a4e913744c072c01a4f6f6cb8d5eb255a6a68e018868092dcb498f436051d42a19762121bb37e2eab36e284f2ee357b8a001c64997bc04d4fd2a32b699e4be8cb49fe7f01c506b6d9c03854606d25132876f202030cc041956fd56c712fbfdaaa0b98ddc3becb3e3a1f3c4adf685765072e17992145a0fb62982a4fdf3120c722bf115cba9f8cf6fc5a441b8b15d29b200f7573426110bd465bf869d17c3ece6f6f2456c434465cd38a2bec5df1d52f5be4e5c8d63d102383acc6699e61a2f5f739833d04b27be450d0c5a5d4cabb1b5ac02eb44e4d80d77c43cba76fe13d68645f87f3858b705e2dbcebbdbb3a7e2a2130874aa82690288fb86ec4438c151e94d04021b6019d483bf1afd4e304fc8ca6d89c274872b0420739a988d0c67cb5b4eb4bfb220fa2634150e8da0f0817d0677c2dcccf01f06d1b03ad9848cd43f09b0b7a5a035daff1018d5607f20f77e180c593911c47c0155509c5e27064165c619db5f14dd6e9a23dd8f4a9775464489e5834e64161700fb219edbc24f07a5ca211bfac3e2db95060bcb8acd38a9a9cb980b2f7bd34b0505733e973b59dfe11c5e9b39d186aebbd98c5fb26004b554e37d68f04262b202af90fa6684beec39273c51ccb6caebd0bce628e1c69db70d495044fe95b18c0115ca541ba4481e26f84a98b884079563bf1cfc292aa2b9aa3c9efda991349d08b680b183aa3d70b3ea5cf61e05b777139a5f630a6365ebb72381dd579712da091a30332e25a88f6ac0767046ae791d75a3760dcf3ff4d85815f6438d681d580d24e41d206c8861b2202f1be514a2cf0156f5ab874389d0a3c83fbab8596852077ba936ff0202215f7cd99a9f61a381198b2faff50548027c73202c0304085001b48b3a053cace43abc786c0476197a87264c35aed341e8d941187eadb8f46c0327b8c38a7f4f2ccb922d9f33c006729e5796d2b75b7e574a6d10a2fe2949050610b414103a767459e11cb446c67dc959ce6031dc6d0c2b1c4d921b07aabfec05f66dfd9102fd806a505009e6da02cb33eb6e0bc87a4eb40ea42e031bb8a8b90e25bf9eb70d0595c42d01aa130243ceba46b998a75a8c3755aff23bdf7c972800eca4c6c8b7ffebc579ddd9308a19d749d48badea97a97c7284dba4e3904af00f3782454b6eea805663af667c50dd1d0d200990a6270ad25b4edc60e7e747ea08d2979680cedfd63daec07142aa2a3473d7a8ef509a769b99b6248236b3c8670af47c8de7332c291028a342561970d68d3a1bb825107eb99ccb5513321488d00abac56bc882a989ac2317c951ebe5250490877073bb6831b52a7bf496ec6b8001eb25483a91b1c3055de525cd4c8f041f9290a70f3d8af37cd98567484069660747c5360fb25fbec7e4c7653506cbe972854cf3f05477c2b64dd9bc28f820e2062536dc9857b16d2bc285b45164fe713301145d2e9f45cf54e165aed055b4ab0c74b29f165fb5fcee76e6d573d43aebd713fbb6fff3c99dca561fbea4816bde005e59f408a1fc08fe5682e9c9e5c040eff78d2f870f50b6da46c745452d46d9007d679dbae101a95378636bdee964145be9461530929cb28c6a45b878a6956d07b2ff162a55ea9952381a7bed8466016cd01f17126c4a25e30a5033042c277f0f73a48441b293e399adef351a5bf941d33509e15e25d3df8e15ccac6be4fda501128c9f1b7425e8e0b499bb2db8912148e7cf815cc81849cfa63f0b3b54bfab08a7fbb6532970445b1133fb152e050362df2df7c7deb00b3a4301f484d878900d723a43f46b9a1b7b16223b2cdd477f087eb0d8e6b725b37c54dacfd41b8e9f0cf6743a9a748739289b8d766f5d3bd9cd0f081bb74301c11e82bd82c82136c408ee6ca8eb5d5384aca30e2fcdea7ea682dbb6b1dbb4a54dbc135140ccfb5154026d8dba373c460d3483e6914ccf0469383f9159a470f7ff956223ffb8e7485b080127904c66cf6b7c2dece896b8d1f9372b413de96729630d589f627dbf6a1f094fa02bb9fa74a224ed7a3688478ea24e5f88af1c284febe0494aebb9a4b69f0c44c8f06258ca3deb8b0e85c6395b8695db04f9adf5078ba2ea8d7dfc012474079d017c3ac2755c6de31a2d70c84908922b6da1cf57d0903d275815182345e80cadadd1cd492f28b81d05fa718fc09d7a7d89e5d61dec8c6d33875ff020e5b30a9d02fbc5192e51a09077766d11f4cef7759941e26ba1b575869172c906fe0b09537c882a4c53dcb80d192ce379fb8ea14460b0c5980edd6a08df2aec386f230d982bb36d2ec34f786af7cbd6d89d128996304c79df4fc2de1ab7877ab850e80d627228f8b0a9ce215d4d5ff7521a14e8c27af78b6851aab6b43be28dfd4b150d7f89268a0004dccded7f038c0ed3ea0c7c109e1fe394a51203512234f226560e74fc25beb9355fff41bbec70aa40f2c9320434ebafaca8d3d85226017e00da0db7fcca7055d9510ae2e01000f4a51b2a34e8cc8268eb83612ea6fd7ebe02fe08180679668a48f92fb56bddd581a5e4a6fdbd99267ba3c59c71a5b4c97dcd120e326583814f2ab331815834014c6fac57a76d5677362ee62df7a25bb3ec218d090fbb06220894460daf1a6051e5be5b2765059f6f4c9e72f77fb5477907966f06e020039e6a3d68f1e162b9d9f20d800234504b03d98e6e275e155cd0f1dba1055fc922da75bbb5223914d33d72d9f2e7f9ef73401de7793d0c938e290fd5770cc160c04ad4de194c8608ade7e11d95e9b8172ca3a5b6fa113167cdbfe029f101ea9bc164187d3e1e178aee36ce6c0475f6daec39d5cfaa67182e6cf8765a16070486e81781b178dfbb443ddde88a5fc9a1f17141c125abf148216a54f5e3fe045fd1e0117118e595a114a40d33908e7752b920e692067d1f454da8f479b90e0b3036761685b2ee96939efff172a4f689d0d92051ecb281157439850c99e8280a217f1eaa78a02670be26b294eb8a370960f251796725acbdb4ffdd1d0a4a2e096c851f6a397338767d4a8cd8e6029bd586ba4ed17166ee437749ad332913fe03f0f65913dda6f71da6f9cbaac6241eabc88b133f1b83805704dc7355396cb809ddb26d34f65536d4c17c91ca60c7e579ccc7c80a6aff0e3d6d90aa76756ad60d442dcc560ef8a18cc8bb536a0457e1a6251004877e0885b0fb4ae7d36ac02b09326f3102429e454b1fdb2c9950833cb0dafee697825ad9159c9ac6b7ed283d03ced45d9fb1b019e965cfb3d090eabd6bdd497bc18dcfecfec270329240be0400a490f8341fa1fafd917ea4cf518423dd4c9597f9672cceee6198a16fd85cd704c2c6bd796fa81a0795cf5cfdab340bea552f501fca791cc497ae3b89733c8704677139a493de3d0a12e927b34899580f88a5437b8472bdf0c48cbd634d7e8600ee0c66087d0223c371ad8abd8bd27a6ec907495a47e6dd211145f40695cd57046549741963c9908878dd4623722216a93f54415f5229b552fa2407d283d8cb0bd3ec53ecafececc414d623d921c398a0388c88eeedd5ecead01d5c007beecd0bbab02442657d58625af2b766c9bf91b256e58cff1789408006b87d22ee3bb003cc645ed0d6789e4c6a689e16bcd05d4be6bed0d748bd05b9f83758d60d216e00f25599362ae78fc7e2421d50d46e5da1ed76282f6df4917a0fe7f8d621e4ed06b2bb4ccaf61c050ac87c6b5e26dc43cef4e8d8a77c9f312d8012f7bdc8fbdf02fa53442032050f6d03f5799ee4383d031508e9aec28e080f5b467df5389d0406bce1dd589d0352586691a258d44e286c63113a750b1d114b4413863b00a0e60db0da3970c76e098368418be1d63f4a205afa22d411358a57d16147ad9e0795032c8320e751ec05e86a4e38884f59e873bf91d06fdef85b08c11a30e55454a0094993c2770cbc4ade979fffc67a1eb628f95a032aae85ff797c426261976b32040e3db3acfff273f1fbb88625189fb5b8c9af42957ac2001932d8e33c8d18df0e8fd33532b7b58934038db5c4eb432b94057931ef17ded6688f6eedc214c5ab0a05fd9560164eda7271de0012e84eecf48b72a0eae42a0972ea6eaf33a3d2f8051db1840f05ea24bb55a0a4349b340ae433abc745d025761e0a9acb80325fb1057d152d1098d51f976930495886f00d1ca8974d0aeb3e6f4a455f075bde85f6018f9d850e291c3fc41f4c09beaa7ac1d59460f531243b2f1f237c1069d0ebf7057942f1b2a2fbab12bb98120f6eb354ac2e704776fe5d7782ae8a03ffe532f50d57233c52cbcdc6fdf9a08b711aa57bc95f983be8065945b5b7225b74bbbff604f71d1aa2e3371d0d7a12f211542d8fc5b43f881a1dfa2fa289c507e57cef840d4b8f00eb6e5dd15ec8c574a1f6c2adac1b531fcee51dfc618478a6d068d0ee028508f93a3cce63f4e64d55cd6d769135c826518671acd846d2234bc0f0d67402c01a7720baa1103e2068f4c0efee199fe83c08a09c90ceedcba73d613a67500f58990e8d38d00aacab52e4c9b6a2e7464ffb8634e86ee4f15956429283656c02fb2df2e9596066170be459b66065e82551340de35652ba7a1934d6310034840b1306e47497728ab4b4a30fb5619e524fecd572a52b736d56648fce1aed690c08245cca2d2f824030cd852d801eff6c6f9e498579d73f55071293a1eb826c75066db17b1737434bc72b358d72e7902a7ea79c3cf7208794710cee58d15fba8a0ac63223eeba966da10e17d7954f1aa7aedff5f88877d416919110d49a338fe409ae80a03fcea94332379322c9b11cf7e341705f2f52460ddb0d197b9b7217640cae45433bc7747ac68dc5e30fdc698e7837a1b42a719096e3524d043c336c8f0cc5077593db45fafe1f99e83621a186dcc5b5931cc026df4bc733287c0ef7030149443a155ac10a8547cac538933ac8440b4e3b66a9a0cd7c6c57cd38f6e5d30630257b6c559884e108d83f4c2f56899d0e88e90830522e9907fb651cb50b9001858120463c448ce5f31b5f641f83f030d8a5c8c530bef8bf9e21c2ca999a630dfa33372732ebf34a070a07435c21e8ec8ef888b7ff1d23b6bcf37902aa321d046e5d7b8f22b00ca1fef141df473e4b26c0fad9d2e03be6b20a65ade1ee44fa06b817802e45fd12166aaba3becea9500b4216d7c0876e0d7259a2445aa16016011757a92c3d3c994fddddf9a1b8c435422d73ff422eaf58b742126f701f347f073783738209a1f2e29b690d09c4a374fecac677e16a14566bde484a5129613e04ed8629fa32f89fa3022e737ef598cf3b4096bb732aebc62e58d0e18bfd31e0071b2a37688cecd28012cd9116e9db95246820e65e7ecb528e00117421d53eb10e76b10a9f6e73629e654413e31a445ac6dbc1c2d7a812476860d09f05aa2e700236e331ac402922b7fd5f4141dce1121365b70f3aa57e9cfdbd00d27939e02b0de1e5c1fd5e43db13384d20dec60bed348a91fba1abce4613cd4b6a3915cd2d01e5ac703906dfd0b5e178336351b552b39fc3e4d0165edb64df25769dabfc580c031b2e7911e2bf6a7d8e4f85abc9d46475267da17c2eca45579f027f9411640f06453fa3cc97f4212cc85ce3139afd07d76e69334d3f11823bf7189564cae701b45c982ef88fefbef89df57dfbf5669e7cc897dd870e13af1ac7b42363e7c000e4be3cb367aba79db27fc47e9b8f43830c6b6a0403236c05088dc8e57e86fc08208176e632c719243baf6c46f3a3f50dd23bb74ef80dd1af2bd651c1c73a430409e466c5d7023e46b9a1724f7e472572c37575910a0514d44d128dbcc56d7e040c74cdeb49d9705629c6b27194f943d1817c4154ae1ef6756c5b9845c0845b0568929a74ff9f3ded10bfd84a6d6e920d27c9b6acdd14bc638b220673be1a060106d336422ca31a25c69391b1a249f9ea6d62bb5bb16c48dc6db887d1093ba805d6c813db53d80d9c2f1a127157a3f63648a5751b650863621ec8bfad932d03024166c41c0c5497cb30ad22e17405453b48d46a45eeb1a479ba1782d1f3e973098de1960bb71d7871a6d6d7bda769332139d4aabfdd99bf48dd34d89a2a50fe044663e445e9e02bfd8108588104599a4c02f2c2088cfa7473103979dcb332e902ea2e265a558b193d7c719564e5718c514167af17c37734f87f49af8436a615006553490e88571e7cd459b61e3e0854946107f97bf03f7ae4ba50d0522d5f430bd5e4e58bf101092d1b798cf00faf6cad7feb341780e713ba710ef38509c63f00f9eb0d57794db7071b23fe2867e4a35ef566aa8f1fb28d505e7ac7ad25ad4501f7ce24732b8c9659c713ab5fca2ef60b8b830e376dc55a88cb146d2163fc5f0cfd138300cd26ed5c3abae76ce35f864f32a174e27c1f9db5d12dbe81b61b160e59be1b1328b9179437b7b65d970d553039081b42514058ced7d9a3e9728b3005264d2e25102b68800fe2799e01da4d7aa7b372303d3e2a11369833844d2bb80dab2d569b43e5847c14296667aac04d0bb02375de06e6986904cdab7bfefb3003f04ba57db09a271caa3a0feb680e15e50476a0acfa4868822d11dbd880915e03293f437eddd841e4e8866cba1c518516c986b55d56768f0b5ab31c9d60cdaa08ffc7bb604e96f8ff1e41fddcc5df18845c8c2fe6c65f0271344bde53be25440a1b4c852408a0152b80c497bf9caff01942d861c1e1f9b6e611025c565e07690871f57dc0bf49cc86722fd8e4f7b8814f6cacd91b988ae53ddfd38bdb44f46b0c4e38008c32fcff87efbe328fdf9508667401d42f0c055aba88241ed7d1ba2f0278d3b7fecf3902ab3bb6be4f39a22051b3eac755841c4c5300cf1ce0c4f26a0a38fab6b7c557302c4bbcc7e873b322b9f3bebeeb9e823771078802b323d46700dc915509a5d1da6709fc9460f5e842d708605c6e54bee332f394458568089000fcf97b08edd311bcbd923b48b318f456be3791453b23c1113809a3885e52d70f395c73a37ac0c7815e9c5ab7301d36d1c89e0c2224ae330cd32f78025afe6b092115c5d35a5c13a20cda4acbbadac38a012757739cd0bd4d976b8169dedfce0fbd9309c0fee595adf8b8708c4ac2c7f1a20a8a82723f5301368e4acb72f68509fa05a52532c8a23fd9083075f91799b7c2a7f2bc7fd8ae578bde8ba87ac4ec0c034c459486ea5a30d6ae860106859d7fabc867cfc034d3f07d876572437da7046775cd1708020693dc14e25fb915da34d018e0d419550f6ab45322bce149d307b49a84a1471a0b90702a2d98b9d619686aa6a15ebcef3492aa4bb3fa9d60b20492ce014d5174ad99571a798e5418a7f4aec444c400deefac4d4c1ae35701a00aabbb456b46eae243fcdcfbb6a65b3a558ecb7a7a526454eb8cc29197f0173702c31e98b4b95893543f634a772dbf66a16a1d472744c914cf950b39ba36bd7a0c3da28f4fd5486c48698b9b499fd2c20972e23e2febddabd24fb7000d8c853e028a576b200a40e1b306f971ba15e3ba3381c077930be6177dd670761239f27f03447fb3ce26c8a3025a296fe3442da134189c3681346d7882c5952c6ee595fc07801c641b51013788bf828223a31d5360fd1c7e3c1b82d47491aa8702ab3c9d0c9642fcd651b7f1d0b1290af881838289689febac4416dad3fcc6e804625c8508b6711ef5e6eb74c43b64d6eaead74e9f0e9f0db81a25083b8053317f0984050ac01524af1ad01fd0748b7421575a3fd498c8b37d4f9f1f2f3a1edd41dbc4290dbf60b8adb9cc16ab64722e20a4dd6ccf170a83db5abe074321df6b960606690bcaab807df67077188a8372aefdb7f47f55302886cf1f1f12e9549f8de887270e975ccfb1107d88a63dfb7b2ad016ee7abf23f7e469d168e9082b231ba2b35f0ea9fb543917ca0be31a4a49d9c9110c60d2f243da195d4a4b80aebf5d4d4c950b446281c585bd67ab0fc980b3901f823893278f399d95817e78aa23797d16eb0d82af30955651f44b699e62f725055f304b154f768c01a4b7895a953b00ac910da3e75257cc56593d3572bc9dfa9ba7077a225d723c43d4df405e3097c1383402dd4ec3c148eb105ebbd29275dedc02fe0a61c68f960c8f638d97b6f2279f4b08fde38b20135102b143bf1881ef4d96b342664fe5d2a4a17bb549682cd441910474d76823ec0da696b1673aa02fb4ee6ccc072f089e6ec714a7893e9d59595d0a6168a48dacf7c073ea4d534313605eb4c9e8f3bf88a59d23bb88f21b1ff1aa0281bf65844f296787b96a9756913dfaadf1b2c31d33808846a648e005d422c204d4122a5786529322dde572b7df683def9d0a230b99874840a2aaa232f2ee8f02407d6c693dddda783c430276e66a3236da2b7fffd0a465fbfa03167dfd40590ee1a98dd2575d201bb78b21d77dfd3ef5dfadf06b4df0e43ebe4df543cd07fd09b10b3b2f288ac9a192fc9a30e9133e207491fe808fe0e86466a7e0dae5821900ee9ec37ca52f280f8c39d64bcfcfb59962cf18495d1100ed409ddde86b082e0d4d7ecede9dc279e78731eaae413538294364f545063cae30452a4ebffdf0480dc1bb065d377612998a782d47ec870cfe950c4035144ee90f9e26fc57538e580c9dd19cea2bd42107c15a87dace7c23bf8bf793873bf5a614e94268dde512fe0656ea60cc6114ab9c88afa7fa312886fdcd5d1d8237f2e701f6f3c72b65dd57059bbc817fe9ec364718ff8b7c9ba27a4e01539a7cf8e0fab9d5ac4efd1e422c0c929c29f56df83e0ea39aefcfcac51250c4bf9da027cf9f8802d10552a28c7505e1957fddf2a00e96b0a7cefc683c557e4aefe1c260a088443715cfac93cfc50b3c4b2260f3113449adda944e135ba8b21ad7d5ff55b722769c2ce00e7b49fa026961cd070e5a154e2d71ea118e1f9e29040d9ba996beeedbf2b04ead7b5c2009a5d12e34bc4a7f6bcd93234d738943a4c868f7de939ff620cea49c9d521d640358c0a640d281f3d6722b2479ea6a102b02cf03a52ab2717519326c28a991120d7195a17777818cb7cea0a6def41aac132a72f34b6e558bbb158a7b0481dfa908f69b061ca602e5517f52b3f8658bb06ef39cd72a92138129c6482a33ca12850eb60661080952771d28dac539d17b81fa88c7fda2ff322ed4b76cd03b1e5fcc0b6ef917d9af5a8dd8077ad14645f00ee052f60ebd4406e0fa1aab1845b6bb050220325454464cd24cdc1460a06d4f857e23eba545cc7bb7e00e28307149e61b0732023baf0b5756f854de80bd8c6a57280da90abc0b7e950d000e85edfadd2503c78dd10a75a1d025c26eefcf8583a6b4c744ed946d949476b6f608bbca0d820e3f6c208ff188fa40ca40f0f6de837957aa67c0e74beccd2f083046e345b5170d8bfabf404a36c3670f23eb530c38716cb5ea478310ae0674cea17a7d01c51e04a06fabcdbc9d93076c49a9c60daa4084722c4e73f4386aaf61b854a11a0e0c0cac63536cac31bdf4942843c94b0aac801886a22cc61700a8b004487dedb6b002d920925be00e8400906645db89eb37f07f47692654f356d58334a048b70fc502be5aaf4828340084211fddd9143c95d1f1b0a03c6f0efa4a1383527eefa08b0357d0428ef53a529d78b6e2293c625dfa6d11c1c458ed229fc08594b7d6dfaa0b32bfec4f5f6b69e59b40b00d06ef8551cc7d2a3e25553a56499e53d0d9322f0a8508eb7a12d12d7040079d1e4a958beb0d5da5576f7cb41238f4e84579fa750226937f81d439f53b0460b108d3e4aa475c195e747ee7d317ea8720958bba150af87d37527d566651f514cecdc086e8b2c33664aca3556c6ea5f682967375550c85e239f28550055a8093d9bc34b97edbeb3f90e10d1d2a7925f52393e07b290c86a6d5c4e8095360a7c1e011730e33c1678af656910cfcdcf2c8cba40f222504be77535cbaf5fe2a3bafb678352f3b931df8085ced17939e62733abb71dede06ec9af96a524e874e2b0c9a54ae90a022c0fbffc569f724954fe29ce30bb9540a9001059de39f3b8dd6e312a2dc8675466351b5a18e87424fea2abda97d70160c227a8756f5335e7cfe8afa1eeeda5db94bbadb7eb953a6372b943d9939ab670df65d5e8d4bf0fbbec28140fc45e3cb7e9a66855a708599d130c577e58f05be00694b451b7b699225940f5f41acc9e4d74efdc873f8ec8437c8eb11b4fadd9f07845d5151764cd9a58432971794ee026d1ab488c8021fd1b1513843e2812703031065e19231d098c6515618494b57b0819e98523349ca53f9c7b6f23c2e38ad0428e22ce47421c4f0fcf1ee54f5dc2b9a6ca02703a47a02570b772ed3176194029e3ba65ff9262a1ddf89eb7040f5ab7126041f175a797ee39efe6282ab0aca00d87fd48fc23fd939089d611f2e693d715b2bb55432c1a6ea94a2da36c1562d0527850c65c97ee082d6d942d5dc589f834d7b619533b71016d114934104c5300c7b744a81b99a9b036cf9d7a5463171a0f61764ff2874bd83595be9fc13f2ce0e9b25c07ceb34b48dcdf0be92ec2fe75f011b3752062b5bb982f49556f9613e0b4aff7b277ee6733456e7367f32d41eedfd7cbcd58e0b25c5ec892fc9a412d40755e88c14959f10e0d53fb9cd9d5f5afb7b00c4cd9eab331ecbabbb5a7655e302cba68026222e802d1dd3d39b1217cf700d06c6e78a5a034d17a9f093408e910307824ca38e0b2578648e1c52ca96fd4a16bf5824a67a6d034a6a507fc654a70d62fee23813a18b57e97c70107d2137fb4384cedb0f9928df4639bd058cf41603dfdae3a7fa0ca01710d33fe4fe9d95c42aeb8b9bd915c4b71caceb4f5129cc0c93fe9a9078b9cb0b405f45afbcdae76286b4587898f9cc0d555b151cfff03e0298d55fd4aff34fb8d55ffbb7e7fa062c8de14743edbaa61e4202dabd1bca5f0cf5b93d107e268d198edf1c0831a6f832153e90506f10f24e866a3c88ea9bdb033b845964b5e5550b9bee32e035f613551f6b84c0b74d163c271d8555ad245507adb1e8a30735796bd5816e0f519a96e13d338cf84c21acbb8d371d07247f6d0b81f29e2471d057a03a74296108776fffc397a3d25b95a9da1e79b1b2cddd58081cf602c558b6d41410d665c2e10109bd123f51bdf39ad3529a82c140a4c3c9077a6f0cb343fafa9d58fe7ab482486eacc9ed4963f9d585507524c0d2735be408310e66b0f5d6e9a5ff031b8278c532930602281b1d9d3b7f15a3aab576e00a048b9191f3e574d25313d5f285223bfe844ca2598643c7c7f5fa53da0fde9d3f0c92f438842d28888f8cb0726a314edf81402edc95993e4c78a2ba8fb63d5b600fd4b38c758de5acd939c2ebce0fd9b4664f4651b1c4e167fc9c33626498c7910c431c2da4629ec6f7aa2e4708841e21faf5667e11687cb42028033f0eadf060017224903eea98c201aba323054dfd232e4146da96239b5e9883b180eb35d57703db850ce0ff034401c4dd7fae4a4e1f5e090f0d5e560de60163229fb79b29200a77710841c35f9c545ee15acf2c5b4087b9eb82b1e6c7d49662836f7af731820effdb63409e792fe14f2c75e782f39f69708499856dfebb22ce374d6ddfabb004dd19b90eb6b35605644c03686ab2bb8d66062942e781aec8370a70086c342f0484dd7e5b9806f38198658306a6c927f6338652387aa6ce880f6dd8e501eb4e0da626eda151997d4cc1eaa67e757d4cdd385b0a911dadfe9271a1bef2926d540c68d1a7d2df72413d0bfe2c95db86ff27a876686d74cd0b3f8310ee3e06b725079fd0345ff0b526b9f38824725f72d1a71384480f312128b4a5b70f843cd6f20146ef3b8c4d8572f163f63ca92c747d1a2c30b036367fd3c37299af229f99840fae53aa9531f60637d17f1fbb06f419333c212eead77aee78988356eef6542c0dae766f91cc980b46132431d27dda3e9fc7dc805d99ad0c67d883fa7f2f66c508b3fbb61fb53a3704600c4b9ba109dfd139e727d88b0d9e78f447908f776b6f07 -generate_ring_signature 70b22a0dad712049354729ee250c77812c8d958133d282a7ccb5c7ce404a7b18 fe481951dcfd22fe788830a0710c2d3aed379ba59ce250ae71ea46a62487f0cf 1 00677c75a2b2cb1c5b946207d8f6a4ad98d9adacea1e5481fb84af52da4c48e5 e6bd4c1635814c70cd87d7b1e256e002a954c151ac5e0e6cbbecb0ab7cf74801 0 bddd09f846cc0e78939e06d2613a1877d1ecff51bc60251ee462885933aa5a0f24615bcf4fcb50f652c774bcf48d1146b6ef2685c3b741aebd9c4542a5e8710e -generate_ring_signature 70c5e47ad8fea9638668fa564ba969be479001f55eadc7869b102d77b5cc5214 af2e9cbb09401113f23399722b7118d38070d67810cbae3c18e82e7b7da6f472 206 a18422fb54e5bc1a14964fcf02ea976ad907a99c9de543aaeaf998f049f92d4b b51182e7531087236c2fcd70ba1c99604a790e6543540582a638652844f962a1 6f8b2edda8e71567c9961ca07b818c8dae7288aec02c148da0bcf1fc96bdf146 7839c78aaf168c645786b1e646261a865ad44b0a83024f719271bd08e9db5cc6 664a1b716e8f6d679237984ca759520ab88db8dad87bf34d581c61137f93044e 3c3120134830ece01517323ab484002429a65eeb0bb17481bffa97774d5aee7f 933409cfefb692004ee42f1c281c0451050964c115bcd81f79dea07885f44948 ad6921f89907b538f3f08e34c1d253cb0d5b8ff668f335a9cc6e718e9964d170 1c2aa68c5a31e9fe7bbb8623570e67bab36522659cd88321c3f1f1dc468cd8a5 66078deab22c8b165e30dbcdf5b9ffd5734f7df003ca3fd515e535107e0deb0c 4330428b7fd83c847e0949054d66632dbb03bf17f9255126175d6d551b8acfae c6a01a0bf78fa2b335f0d5eba32fb0fed7a5453ecc58042f97a57abb4ab93768 36637941f031cf30f9abad0afa6ac9df869c4dc7239035f00b2536c83eb07f6e e582018b9874cb88b09f9e30ec9b471cd154ffe4b79685342f4959c653c7e30e 9d988d6cbacba45ce8492043930ada21ee880e0f9f7c2bcfe34a978787b7317b be59db2de9e9c18d852a319b8021db050c8684e9b4fd27cdcc70d7972f262235 701f1b7057adb0d1a917d09f9703ca267c6523a15a42d4b2903d1c68bc9dbdf3 ccd77941930d74002e7be55e8cf6b51b2efbb534250352f9841cab4b09d6101a 9bb62944367b74b680de5a491449c5f4c89922e21999e4749c63bddb96757b45 d4f1497d5e1602dc78b67cc2410369ca0b65ca3f43269d48a505f35794abb7a8 545c4f9c95f6387f51da3938bf05d6e1dfe24ce7f47d00673047c6d15b775b85 9e16b849e343e81d0a8e4856e5b61f24e5839b3aae6e3bed70218a831b7ae4b9 6b85c3a6f57abef1ee42b1929198b80bcfd590d2927465bf86a708a8d8935016 45702a3b1ea9f88fa318ca774673c9f5305b70f81eabb243ebc51a4d456a39ff 14e17ab717131d76747f7f89529456ed4b7e0b4bfdbefa66b479a1cdfc2cf1c8 b28bbaebe6e5a2082179a5d40f82b858120207ca642c4bd208f86d1981e968ac fde5f21d0002ee5fb68b31f336de025dca70152f8fefc8c96d9369c8657bb45f 6b6250e0a9e114274c470d03b3a09adb267eddd060fd7180a3d59f2b4c84a255 3f912637b519de978a550c9ce554c4cd8aea77b7c67bbb4b61b7d35dfcd6cfe4 2030f7603dea048af9075f8d5f94503afc5e9de977d18606915200f186c7cc91 b243f8f735967e6d2fef727ce4ddbb07260033eb06e22b2c2a6e7c573ea8b957 a4d61eeda78d420b95dff8eafe85b94c4876872f1da29b344d213ceefc4913d0 c6f13c6b465dfac7794fdc9b025461ee81a20a5e12aa1bec8169cfe89c66ef29 a4653ef4b4897b15a9842bff5c3dc1511ee1fd1a4709eb9bc11d479628b5a49b c5b6f5f523e94d64365318fca8eec9f9b69d7517b9d0516273478024c07a552e f9e16f2e35b355a188119e38acf7bc16afc4e0e239fdaa97dc487b9789ad2093 8e32ed09461857c41eee4eb6f4697107373ac5bfd404b2aac521aba53c1e4606 da10a8ca7ec34b14f75c82fda8a295339db1a95dc0ce4d51c92dade911a7118e 00ba18bc2201748eb0ac8a35142ef36a1f054f45cf328ce6d4c3a7399434b51c 77e93d9c6e4d0fa69e03244504cf715ccb6e1771a68c5a4bd01e7a7ce1e89ba3 cdfdff3ba97751f3181e617d7303bf4d8dcd5163bcc40af5390ee071a729370f 36e2b4cfaa03c516f3833c40d9d8ed3e5f123f81fa878aa7f56ee21c687a1c2d b978726e14c14fd17ed98d7124feeae05714a8109e1dbc14f503a7f04178ac0c 29dcd31fd684e980ae3fc46e5da92f7f5e78babdb806087ca36ad8703734af60 c1c96cbb9e90a08dafab66240ec2a427c9427dd6cbe2c649aadddfe2f2cac4d5 ddf16af1d9f17774b2d18a01f21633c88e676729fb92ad1ba2f5a6e4515faf3e 7f3bfe385a2aa865c30d151d1d70f7780b9404c3ce0b80cd8d76ae0bfa2d48c2 33cd6755c2a765f34711898c5cdcaa2ab13ccb4035809cc714b8ff1b49e312db 6a9c43c587cbc9040a1bbbb5ee5b31b5fb15c8e8bcad62df9ee4234c0137b330 ed1e2acb487769fcdabedc3a39a4a33f00689ba64d2853c247f1a82ab5d9c900 a7d0aed414d50b92555e68b1627c84600850b2f67023eb90124900fb54dda7e1 9c6072d044134b540033a836fd2cc23887a8a82559355d7e05934c46257ddfca ff51af4f58762907aa34f99131a60a7d303391b059d05fae1d4d4e52e9a7ee12 ef073664eff8f23d2db789333d15a35901822b75146bc40e6a9a60bc348b0f43 b20fc9ed263e55e0d3b17ee113d43547e373cdd165339861d1a5f00824954bf9 da9614943a2b671472b15a1da5689b2a22503e468857e9fcd5a80c8fa0616927 b1d8c70e8927f0a0949c7ccabe0f8420c7ed3db8d0fdef72cfae6c34ce3c236b d2229f0f33a9fa68062a177416c13ec529f36dc95429c6b3e2030e7998137076 1cd7d9d9e748868bc060399946dc24d156a1b392d6894bc1ecdc54874eb26d64 870f6592e775e7787cc3141ec39a4bf8fc40216d768b215ed2c37c020663f568 c7ccdd2c40f607a04ea79b14cc3f0e1087c1c1aec9cba9be3e9a040153878cd1 4efb40425203e4db182ce267e1ce8f9fddcc7dea9c231875dd2b477f6160f93c c77b8ff4f3990a4a716d9ce40d9dea47d675d4e2e8032eb4e158e71e22b593fa fafea62add24d9bfe2c5678c3dc32c3460686350cac842c68ee8c9dce4befd5a 0d9d2879be4d5c54549a652a0332937b19a171d08b87355a057c155a49dd3511 7d37429fbd0fe2b2188a93c537c7e55283ef4fccf2ef28dbf393039edae68d1c 9647f768f127751f8d2e8df5bdfcfe0b50ab08d002b859a36be77319d2f626b2 25a2befe5ecd5c5afcbee35e708c513e9980d217b9982a894decf8e723f4267c 49fb16a530ab7e69538cd98e63e69dc602ecf3422f854d0bd9dc0c56a6fe435e 9a4cc03169cccbe20d82c8d1c88cdad2c0e16bb32c22fed4a5325ba24c5da531 92ad7fd060ff5e009df4146670aa6f3c48add5eb9201ae12c3545334efc68d8c 5508d45bb35097749c0bddd3041a15f3f30a73f8d8e6bc26edca97d2b3ed8eb3 085d6e6167b1711a5f0246198a0ce033445c56c308caf2ffb67ed4bf492a760e 814453420ac7935b45302b60a76d60ceb3d875e27d84181cd995ab0cf7c194a4 66a3b5485bdc1fe37020e728315e4952d501260adcfa287640a15e4ff29934bc 7d3202c71e8352547fef9f2effa942969472ba6b2b711685f1e7ddf767bde373 c603687740f6353258d1563ed82e5e72b7ef7738425b829089e044312668092f 78a2b79ae83a3be22e894c61f9dd9944816749201ebede34760f0d97bea2d29d 77915a974635dc324949da90d5a6414a9abfff9c6a1fd2de2397ad515ed1ec4d 394e0b6cb9bca29f0889e679730af807cc382f846291fecc23ef891bf4303352 03bf7c7d9e5f15da8d8f4d8c8242eb7d0f8173f66c9a709273e1803a073e7c0f 885a745d8efe981be6a9aa5eba6b24b23032229968d62ba2f680a437051957e4 bbbbcf28ff988f39a47cb062cebac6a1ca52cca0b7553f1e64503c9508ee27e6 e4253508f5c9313bb1519bae17f58c685d49e4194ea7b369ad0e079878d253be 33e5fd5640355e45d35e4dd7f1e8e7cfa2998a9ef48c9c887fc31046881663f7 8aab3224dd2af87ddcb5831c1282df4fd90b353f4269d4da50252aba203d19d4 be6533d22fa31ffe42c80e1a1eb71d65bb27a8e63c7f382d1a00c254dcd986f8 9c0652aa08322b9db87e6b8bea6868994d9ac10fb89a5c1fd79e6cf05e4f451d 99c615eab0941b915fcdd01beba0f79d841455986f64b211a74631c04adc45f0 eb7436280e32dd1c1fd11ec57d44e4b960630debe8fd3fe9b16f7e25455153b3 dab838aa3a6646ae8523bc8e612c5f76bb5cd21bae09af8ae035434a9545e1bc 6b0f31df949cbd288c5d24063851ff1c107938cba425be209728e09dbaad55bf 83eb14b50510e48cb3f336fc4aa0003c1fd2dff27e26484c9e3cdaae21e0f73a 37be5905a9f43cd4e56d974dcfd016461c3d066d3fb227d6411e942705892208 53963e49426946c393d7051247a746783821d70b04889a094a5c99f0107a48fa 3797c08b0780ab73b9f4897a12174a42871968f024f767ad713f8a7c35995b5e 35f08b38741b662a557627dbb793e4ddab03a144afe29f15d2582f528e3f54a9 34087c19447afa9ebfba8b6312969741fa38dce9a3c52294c08d949180cc79e4 7352d684d1137c23c8d55ff9c01d6dac19ab3d6b8f8a30ebf5f9789920e5c3b1 d20ac3c612b2b275c0c708040418562f80ec07fa66d5b8bb166d115e059421d1 b34a94287b0b92f980fdca1985f12feffbc33e22194cf83f7c64f9db3959d810 b00a64ca8cd8d6dd32d2e23e22fcbbda2c979e3a25e095d9bc984b75e29263ff 88905bf24369828cb2d7789ede9efa19100e1de11fb23fff84c5f919caf1b200 f550c0946b5bd38adf6cb83ee5f66a33f6df9a352920a0608cd6973294b88591 b84f4771e6e6bbfde0ba6380d7b0adfd745782a79c665ef158b35f30c3f2aeb1 6125547278feb8959d7b44022834dcefacaee1821a2b9ad98072409fdd1fc5a4 2bca5c5a2f52ddf22fb94c1ed7f094beedec8423c2d908530d56bdc6dd8b33f7 d122a98f60cbfc9afb08c4a3665a4297bea2e28e1aa14248e534792ac9be9754 0cda988e49b35ca39b860348c499ace34a1cedef1fe980bd791e85cc9a3eeaae 50405db106877d4f79bba49df0d00186a34ba02918f38d10a6772a08516f358c db86f45ea5f440499c1fcbddb9e094ec839219d90ff65ec5db7f00e401f294aa f2b186741767357374da23c9c787b00f7715f6ddd2bc13f4ce0c7fd8af871af9 45dcbdacb01ebee6cdb99b6bf299a327d7600f911eb178b6b040e427045893a5 7c665eb4e236b8fc0df26b4e7006ea202158ffcde5bcb01bdc76955332dc4612 e5c13f2bf51221881634a0b3be87f9bd3b77ca6fc5190ba73496de2025d9e9d3 0f5d6fed8deb031948aa27118f24575d129130cf4d54e8c03bdd5b403077b9bc f5f83a10c8257b8571056cf96b205d6e29ae49a045165f9e81661ea60d210b46 04f5cb095be125daa3ad6f020c25bc4eb60fc210c3c55ea6975579c57704e006 310a935599ac84e860c339e951c795d560a8ec6d0a9915e36f54bf7476bcf7a5 02e7263c5f19528ab4807e5cb0de10f754189d00768dfbd2ef200aa1ae8bb2f3 9e7512f100cd122c07927b9797b694e4829ecea344c5aab64c31e2dd4fcd1f4c d5882dbd1118a46af3c624b569edb0f66d6c7eebbbc822a09e74210605d05858 02fa26513c2e35299ec5634188849a3c7876f283d8e6d95875184dcfe90f928c 7c1af6f4bfe0fd44c10c6e17382585eb1f6f6e1c0c0774fe66517839b4e74a69 ee9ccd0b683adee6b6c99eb80bfc58d50ee650f2089b23aad6a53d1514204d3c 7a61e0013420f41c550184125d3810897caee0c1cdb035921461d30c070f5c44 5a3b4acb99a292959c09c3bc164aa35b8cfcd80e8dd1e5a0ac18b4b20c1f7beb bfccdcc76e71a16d7cb5cd68daf3c83ea71ff3ded30cefc9a574d75cd7ed3cd4 0d37450354f70f3421920262eedbb2388b4b3025fa7b7872045e108cfe506a69 7769a6ff5204841123e2f786b9c438157a68763baa0ef3b911b1918dfc908fdb 0281057269fd5c1792dafeb5dc50f5fffc205ef14b1a1b495335d41fe9833641 eb74bddbde03604204f8d59400fe0977d2fadbc4591d25092dd8507ff1ca0441 4984c0958c38d0bf7db3e714963b7ae2be3f14e4247aaf8625a1206bfcff624e 416cc5e0618e97a86a868b745b730cf5082736b28f158a15fe97d46dd18943d7 c0334b9b1bf615d4e50d93c5cd68c004f616c96448640f96abf15a05f259b6f0 72228197fbf42e0bc7fb85daef82dbe1f5e2dd7b173e80b590bc6cf985d5a065 74d54c40ba88a6e7e407696c81045f77b7ca443b5f676f9e7ed2133004c220d1 da36cc9ea4368c90ccbb962bb33c447d7f1c57c6b5f0073f1bc1f453a58a51a3 29346ae924c2c641fddc37b5db1a1cd7d8e8dc6c10f397ac1e4296fc50f957e4 8ad915dcc38e22bbe3808ddd979703b55641e5e082d922ddf95eb55bc1dd1c99 f9602f4dc07dbcae954cb895a266f64f0ae8067aa0de93c828b0b63ef77b1286 127806dd2c84355c8f15d5542dbca5d2f545bf7aeb97da34d09ee353c18df7b4 4808e46928e4e630867cb664a2a594e847858220ca93bfb33e06ebb991816f60 f45ea82edbdbb2b01bf809edd7ed8c7b5d5bcd610eea3e3e65fd3d2d0813e738 b9cfcc23cc02517f75dd3ba4f05fc727681786f67e6cf81edb3bd39e945bce60 e1f5bdd3eb6c2b9315ebc2fc8a2265b6103a68a966992ffc100e00a6bfff91a9 b0fe946f3fd0a6f8005f857f84d96a929971c37caa1b7ec7f8f76781c10f7659 a0f943eaa5354a75121d68f221d7f4fd05820d142c52ba0e613ce40a94ad410a fd4885348d62e21aa70db54056909d38d6a0434958a88bba1b00bde9b5d9a68a 81f0f73e215de76d5d9395ece7e348290fc38c254660d07e5bc1ee3af399c349 6299a39b5110643b1ca175cb95efa2384e6ef5b097e104d538f791b61c934039 7107d2209213b68af9ddf368bc4cb14a7eb4a97c2d12cb016fc4829df353b976 26e8f097cb2958051fab454439aa1dedb6929a218d6ebc0ad71ed240288d5848 03c22c8c6e0d2b2566d02486f8d281d47757cfb55aaddf6e9ce709d0b434bdd3 8f0851c2b5c3ca4878ad6c9b7bc5d0961d2c10181b346bd04024a1c3c01ca783 49ea1b8ca3b5ff382ae05817952efe5b1e492dfad8864520909211db36d9a3a7 4b4077f01a37c8c41d0a841b197f90bd81b0f60e8e85ca241ebcdc75ae7ca7b0 c5094f0a05bfe6bf19b8aa4294ad8ad350c9da12fb2349e19f20fa468a8d767f 28620428aa5d407387a24846a62cfe882bc32222b1e0a11e0878e69142f927c1 469a5d58cda2b621561089a5a84923527f3a7c34d73429b32f12165453fe919c deb95037d7f978e00a400165cdcd21698a9abc5543a86a39c2a3a2a38df0147a ac3a1f62cb0e149eab665764aaed0d534c57bdcdd61badfef5e8fbbff284da4f bb95b6728b9d49af7954d46440c0a38d95f97dc319a6bc801fbbab115a9d227d 51e67336b6a0277b50c6b8c9461b570341de5c545d6ec069133fa52fe0fbe96b bf9ecee1e808820f1733fef3d8b26831fadce90ef25c2c9e69f4ae1583c2ff8d c2ccafa1afd4a0fca65c04cf04372a9949fc82e1cd44d3fb62372c4eef025d49 e6df379d0ae015a6e363eaddabc993ac719fbcbf0c426923cef3ce181bdf91b5 715a8e056a708fcc73e370b8c8189b95be07c48f029aad9d78318bb4211fafc9 33086563a891bd5405a0c4e6c823aa6caa40baae56af2987d0fd5926b7bcf92d f135dfaf3da2b5b44d02fa04f5808eb9d2f98908d5be6084694a0d0f0bef6b6e 86646a742b57067e655ae93ebb181f34b2bf68226725b7fd49b7891c5e8d3c0e 76f0c9c066cdaea4caad46ca803c9aefc3a11dc75aed472479652f6d75f9f593 4d1772db532f9da6aa39d0644867e5ef9c429cd7986f97eea7c253a0e43e8698 57bba026eab4e72dc1a9636f0c63988275e9eb38f66c56b0f6d998fc281f2f0c 628eb7273a6845f9636813f810760b69248603686f21ab695f41304a5259d7ca a83d219f94eaddaf2bbc68d387db7d477f41a6fffd90b307c45342703c987a8e 149807be6e62c586c635336a9cc8e1e12491ff04528da3ccbf4c62dd49014d72 5fced27002f066f6e03292c55c36bc9f4e5be911738f25f889f12d0ab576d690 619142a6d9c825b6b874b1619f723382fb91d1cd94d59814adb195b2df8e2afc 46a9f5f54f2c432884e00bdcf31a1f2832aa3fc08ec5d613e39be20a8e82ace7 1da5ab4a03bb087697f2149715033ecc9fa93f3503c0323f2a4763ee8c8bd51f 481a87a30c3af37b339dc69250c71ca8a922e816667443710671a80dbe52ce92 6f72c0e69adfdaf0dec11ab94da9e9607fe1af9b19d2942499f2f3b06c70ec10 3f27220ae74e591ed12f7a5e53d9899dc3016346747d8ec9fb0d8c6bbb7c2a62 7be4d47e1841078f34a8bceb4a63e3dd7d84a7a15d2180fac6c8cf9409ce02d9 4d781a55176f8de667ae70e9c1bc7cd4fd263930b82f4a4f32ee12957d19b4e1 b5df86e2b91c3dc2f216c0c9ab065ffa1ede0d6ba02fc98df834c77d65b2ee60 448250ef38aa56bbd00f29317b2a48212acd247ea64550dc411ad9b176f129de 90899eabd9f089088d271399c46d2550c63e6b3eee2ca47eb1a1b01220abdbba c3e9bce3229262c0e60a477d16d898464d78d94b5ca4ecb64f769ddf20c5c440 67ca124eedbfd419a80a46588fcda09e2f5fe336b8e69d989c210e613a82e724 f36f0c8aa0d5711221f96dc637e34e8d4cb79166136b77637401449596507b8e 5a7b4baba7c4e9e7b0bf849d2f3734a5b822f30f132d3dd1f262ed9551a2bc58 a528422be9a71e011bb84759bae4421c53e306cd112a78ea9f79f1a2b19b6c17 8a80facacc40636b9445e719bef3a4f732e26fecf9fe9754919ac27b9c53fdee f9cd67c569544c62a0c73392289fb7682d727ffd10e7a79697a4433e06470f8c de9b69ec46282341a42fbbf8de6b54a232ef50cae2ad1d774f3630e60c8eea92 929033db79ef727eb67d6dc4aeea650c01271b3d2a6873afe6e6b0bce5650cba 301ce60b3754afae20c3deb7ca76dc542dcf5e3c37650258e11f666bd9b978e1 b07597015e477302bd1f044c547f7246b74de2e4b069de91314e7023383e891c f6be00b7a0bff65e3d29bee8a690717c412bedad30eeb1b9623dbb7744fb72f6 afefd6e6e0544512a57fd39de6adedbf6d980abc65de3ec99f89ed06315ee378 43dd0a609beb44b6df8459e8dcd769d48e892897872efcdf1783a5a913f5dadb f765aea469de90af8389652bc1cd0aa52fc42e57bb8e546e1618d818128264cb 51e47a387db4adeab17ef6abacc440cea6fc47e00dd448619843ee6aca670536 b2027fd7a8e36de5a752b54332d2449ead5213aebf6c678c54c91e35e6d87fd9 fbc0102c91c3c12182445681f5f123ceebc65bed2d9932afc8a2f6fa78a04b0f 48 593f536a7a16266acb34289e9aaf1fefff916519361acd40b679d38590a5d000f45879c99525d5e4ce423f3103cfe651a76fc005773e0a8fb57da788a17b850e61070342f1d5f1552cb6800500df89a884ed2f29148ef541cd0dcd3b56c25c0b12be61762d6d2ad4504c4071c7fb6def8aceae19cb0dc3acc8ddbfa2e176010c4e9b0a24272df440254e398c4a4bb91759737d6d2234a9ac00e95f779ce87a0041d405e8bd3e54f72c0add38d67da6693e8e55e11147372eb9fd02df79f2af0206664bd2b9afb1105a2b74a7f52fa408b32dba5a9233e51108693ea23d793104f46bbc555ebce3da301e9131a07d2f94e865ecf7ba8b459ec8edba9b50a52e06e2d30bbdd9a646b2b45d44419ae24659352f52b11de6a90c0c8e388a8b5d4504532c7ba163f6b7684ce26ff420ceb6ebb7a2597012ecf948de57f8bfd549320485b6ee8ec1423b3e81af06474ddfb257ec110c9801803836269d4ef1437a240304fff5c6885fe438b5ee575806ff72e94dbb78a2c5abd6aef1db6cd059a41f0503696c50b883d5ee33453cd7936b5874bd23d54e7607eb05a22341f338a06601251985f7493c6ecaea9b57fbc258c681d137ff2331c22cd758ab4142fe3062094c506a0bcf3d909d5a725c10e70c2e2535d1ab1ef110d66f0b5048a8499aa40f0eeebbc032c19d7e3d15425a3faf8d8ff007e46b4852ff7ca7d24b5874161c0fba4b6ef1be68355e021eae199ac4d9c0f1f6a3853dde081f2e54b2c14b294c08bda82a45143468354190f0cda41c5fcab99519b3a5341153860a0258467cf10e11ef98b70e69b496f8fe61882db1ba1536c2d7bb2f95229647deaee7c4dbf205fa624ed5313e81da1543b2ac886bf2d98b73fd92fd90de4ff2a53a43ccae220923cef9577cb6ae29c65894401be97496c67bdc31cff5635fa9649410b9837f042ddaf849eeb591f19605436d28d0cf3afa9dd01538336721094c5aaba6d5f80925cc1946ce93f2e9bea9eb270ac9c8fcd392e855407106a48e07aeb595df7c0136f2cf586350e32bb37b84e48104f85812a377c9868269b484e3c783e6982d0f6907ed05d6a85c7348d9653fe6f4e61c742dfb11c3105829bf3a639eb39c0d0fed90c0d19ae84d7ac960df103cdb08e0e133b37d2a224bdc742103be791734084bfeb7d0ca23f6fdbcec1ad53ea7977876827210ee77271fc9ead3cc52c5e60d2ee56044a07e2c672e68670bd627c9c566eb31b649d0b56d553b6911e43fd30f4e30da8ad66cbd81dc73966e43197bbc8ecb97b5e582335e2004027006a4b109e7b2bac97d09c63ec2b3cdffc7aba4e198af8fd9c5a48dcfd6c36ab3f1914d0300910c197c6527a291b1acc1aea59a62fdf159b88b2bcd99850e3d95d1653a07f66df6f8e2555225580bd0606ce1b95932fbb990dfcbba2a1ccfddac0f30860d5436fb68fc32065e016d7f634debbf6297da1dfa7a9e71574d1217b3b31b04097f9883758527e55f1e64d9fcda61548aff8507b22937685260987daf4aab7106aec89df72251eb83611b75a2b74233df6a2d3ff3771acc620f01cfb75275f00748c5c25769115bcaa4a4c37d51ecb2c122eb11ac879b34742f281429d9566705c31455d63af2037d35efc5312116302d17a332f372ced3c8ee2b9ce27204e8032389ba73992421436d235427e995e9873a6920cad6657f8b4480d1792d395a0b033ca02a8eaa8685f119bfd8f658360985d6d88aef8031a92b05d36a437fd60bfd29648cc7126fc1f22b1fdf9a3e396bd3ddd09d474de17fbe62e34dca5e1b0648655603b0b651e27c52c1c495ddebdcbb280b8d3aafacf00dacf729411e2b0ce143491b8e11a7b94c93d63a61ee2a06a7091ee075b0a51222bdce5ff2a15407470550c8988c55fde828f5374f94b4df62493c9f64764a84afbbeb24a057550f79c271b93503f79f7b0904c1d12699a7c3107a725732eefdcd197cbfa7237f02d6cff74de6ce0a5ee582326fd6bef0f713e0c48c3c01d17991378bca0ea02b039a8de926abcce10c04fba6535dd97b5e8a280a9d591019d2230b1778f669de08b1c3bdc128446e7522facd8191a5c5b50c7e08f8a7ab08abfc9de39948b6150a23db738b9781d13d46be5d362753b2d95043f011b94175b0092ca2271dd597027de8bd3a669b635be879c96eb63c8cd77d295ce04ff804dfcf484d0216c9b60cb7c2b4d673c2a60bde9ecbee1a31e983ef3bd2553506605a70bf60517ecb6906c2866d9de38776fc471b1ede639219f3f2246931ef2f02764b9328c1e1bb3e07c2ac887086ef5de180f352073190153451a6eab0cb2fd4bdc00d7527592a9b03b23bc357b0eaa9f0dcac7e4172c368cf8877bade581abfb88280275a2e60700eb3b23a3e98eff22c6eaaf59228e7940426b14d2bcb37c94e629f87b6a5c1d609dd972eb63567956c45fc0061257cd6f89aa4b71a75b4fe85c7e955236889ae0e285c87f48bab3152866697df1acbccb96e3c2c4d50220ad468fb014aba78f50e7928a8b681c5a6c8a268ec7d2e2b156b57f5862e403dbe6c04a57d4ede42aa0a6763501d27b8c125a5c5e6bc26de9bdcc1172c64b7dd79e6f2d3f4cbd705d30e0b8b5befec0d4b10aaf9247c5390d748719a7d0dcf86a0e19f72f40e904e450c014cb2b2e6241f766c9ef4e4c8fd7f18074f9e215a3665b3e7eda4229ffc1509e8bbec656492ec500d887e9f09a3bb0bf2a36ca56aa788bdacd7f01135762605d334ea4ebb3ffb6158e27bd7fac65828b3eea1b871c4829287c7d59579dc7104933c7bd8ab9ee657cd1f35d31f1a2a9277f3f33bf28023fd9366c56a6a6fe8015719926870df72ade43aacde3a02ac47c87a39bd51f6d2e2b07afa4e5f7a480c8e7055491045d76f2064f435d9fa2b1be67f041cbf2232f2fa6371ccf2bc3a00a66e25ca3699f29fce2fd1d87c79b7791a90008e13ad36fa5324c5dcb78b5f0d19fe905a441bd47e4754a61241a0f837ad511966063e9f8d261d10aa7bbc6605bc0b87c028248c4e431499bf3bb3b0c84aadeeaaba5d4935f8d3da46270ad7055b2e2e47457c5d8c6bb8081bc789e25b6fd8fff65045fb17c342981672f22008660b76b5a42b05cb7bcc93790e99ae74e1cf4bc87d3a4b1961f3e8ba3423c8071dc55ceab91388bd256e567af11925f6eb814c7f938b7901133e16ac7677c90a21c6753e0b4073b2c9b4a03d4db088d4bb2e036939cc4d88bce67e126b35800e0749252e7b24d4338348f9d1d285b2906c7589626ce493b3776c1fd8ab333f0cdda389ea32e44cac36fa44155744f398f1bc7ff4d8615acc54ac1752a2114700a4242e412901f5bf3d81959537400a23c7137f5bfd18cd3ac47fa23e5eef7d0296d64c08518079d51067751accee0c33c7069757c76d9efa0949aeba96389309de5fe41274fe600929d774f1856ad10895d8e364f45e42105cd3a2123e67ed014fd2a728edcd8c7f44ac91cce1bb198388ab24e7201c1e0e1ab03862e3c4660eae6c1e6cb0cfe9acd512980cbfa9353d92730812093d04e53a5c8d3f3de7d9008441d1016191c674f472b0783a58465f5ad0e0502c63c600eb4fbee00bce8f086816351143c0f5f75eb178df26d59bef86af456c5481c2fed66452360ba94a033c6045fd4a063aef092990f9fa270df8d8b44505677c211df014e3d8b6ba850b04e66f20a863e8248db4c063ac3b8770cd5411f3707a7cf2470f87fc696dd103a15c7b89a464faacf5a064dafd08fcd8d41ab3149e63191a0a1db4e1ff558c03431a1d99dd25ce5ee208175d284fc3e339f296d2636484995d4e064a3c6f1907a71fe555bab843901ade5a9bcd34475d7d50116fff7bf26cf2179d2382fb7e06c774946098b77481457e4711e39c6ae234d7b1456283cdcc3708f75336047d067291aa3ebedc800ec8fae2cc402496415b0a9c6fc693a698159dac57fc49470936a132a8d79461c7ba4c52562f59585f5e27cec0d9c449608b33f460a632b902dd97282ce5deacc9e3d7dcc13e4c11ecaf60f672d14871bcb9796eec22c6d40919b8c51dc376db7fa3d5c2da5c09810e0e1480683b513758ce809188887726001b5b6180804c025ad00a3165dc63adc93025d1a7ef741ef7d528dab9ab1b3a0d5f7294cb1ddbb6adb42fdcaa7ead769e5bf8df7f477b189c02a436da75ce1a0ad398d03fb24cc94400359596d8d12927bdbbe07b9325feab70477270f0f1850cf2a682eddb32ee1149de0f113f115ced4833accbcf1e28526c90f4085957930fe66e6f619aa0d8236ccbfdb0d7760c06057b2329ad9a68ec08bc4895aacc4e07dc10fd1a82c0ae15f4cfd7c166302791f79310d9b768c43e2696ae220461fa05670b04dc7f805bcb46dd59338e2934f2f0336057c5280526a433bbf45963950e16b03afc239bb239451d3b6ecc66b429407aa2e79e9ddbe936c4fe7679435d0772073c9446218ccda22dc91d92a5209bf9e24380d63fff1b654d7d40b59538058046fd7bc80228c6286f1adc9c4690cf0a523f7825573dc21f5949965cab34008719d220e4b66e34cd25db57aca713f0a98addaab9bbb2a732c93c3998ce0907caaeb747a79d8341dcfaa0d0f325325b2ee8f497a9d3c5392f490b160c8884071bd40d97804144f15d7ca0332451367bc911afddcb49c4093a42a814985d920303862d76cb55c2f9034241de8d2bd0b195a32e0104c3c34275852ec7ebafdb067f0bd8f1ee055ad4e82ffdad03603cc23705ff791073cce9ff49c1332b281e0d01d65aaaffd628db33e1c569f8aae0e9c535853c20023d7586a784e4b26f6c08997c7aceaf696bfd256c3a852913dd2a24267257f88f2f6e12cd96a2ea995c0ff27eb70b4e2d996dbf333baa5242f3d98ef31bd794d3e4f12c2918575a95d60f746c327675b100fc8509de4fc9bc477cff88a31bb24a219358c30e3b30966e06d1d78f490d9a54b058868826ad9febb472a30f23eb5370d8807d4875b1ff8701562ae08eabcfd119b2edf9f449ffaccaed72b2e7452d9141c91e17827ba3d303359dfdc4346df4762afb0e9dbed82f5440977d63465bc0544551ecdc27ee060ae3a477312bd1d29ea45f233b6b7a5be1f86d33c73349399f3ac5b12545e06e0f72f999a21f80a0a879bb272ef72ddf1db03b2a6a7743014e328be92bdb4f250a871befdbbd06389452aa46da0824150169616c3062990a836f8cbec91cd1ab046e7162a94d6ce8f00649058bcf7fc58952d7c5a62b0328df804576150a10d8097e9491a6c9a9aa18ed37df5fb7e5499b68e2f77a039f4e44485e9f77b907750e3b9fa0f037e20e71a0cb6a419951e1f502b3b1c2e3c1cb7322df3de37b71c604ee28d49d2808e2b91877cb7eddc3d99247180725f2a85cbaf53ded05cbab1d024d3c47bf7effb754cd2301dcc67958981bd419061fcc85513d123e89df815506a2b114a6f6bfc981a7b47a44b5a01dadb65df3f2fda1ec08739fa20c066ec00d4691188cdf23bad15b56fadbb8af22587a6150cdf32fe51e4f300492b1986c001f5ae7cec0264ae0eb7162ba9f109a692ff9c1250f3b4c043919914af079d1045aa661a6edaaff218b732c12d42240bca01a02a6bd8f439354c15578efc307074ef3bc9debab57c29a5ada19c9c7daa0c14e2bdc5ee5db6f38b49f4ccb622a0d3884819f7cc807ca0c3d83b71d4cd44e098d808046a1b643b323d93f84d5b10191419dbd353af71b2abda63426eb00889d836c5da5a31bb00d59728feb0f7d0e31fd6d413ea41b8df781a1d4f08d5479d4f1bb9727f12752b3d19392b131d90137c9ecc126e9ba94b4e99e3f0aa48735181ae39540f9c05f2dac218b951bab00181291b84953751f009cfd89ef63ea398bd2800bbf1983b033820e477f9e40039f4c4e27a589c659fa58cf4a90a9a10e774a2caf5f7b5da8e092d176f6e915068eba8c09acbf03074b45c33fb7d5bbfb40db48bd2fa20aefc50c9f53ee715308a2a37a8b5dc12b674623a103be6e91b7663a1921a841005054e765e49c76270bf5a4293de33fd5eb078fd8f5f162a8ebc32226c5554f8345a73556992fac3907a484f93bf39efaf815c66279a33c3d52aaf53538d07404351023a48fa531030f284491f85c38022707698cd4c488ae42028353c2ba3e5c29b7986b83b7528c01d5e67af0f3cc617e571896f7a262f840696997b83698881541265eefd71cfb0c4dbff05e030f515f3c116ac1b986d153b9d0807eebca1ff958a53a5d2f20a20fb36f1eb9422da5825edbb6a832adc7c8cd844684607bb36c82507689fcc3df06346665c9f871eb0fcefcdd935506b813db1ed87a8a47f2b5415b33c3c0da240de25c5ba3f8cd79327b65850584005a96c24f877edc4920de0ae34c3a0b26bf0b078c90c36dc1c2f0057711f797b9a7d3d4768b1fa8f143cecb1df173dafd3a03b4fd9beb68a64c471101c87c2e157405afe89fa35ea215d431aa4d6e9dc9820e517d79f52333f2b4ea87745e9842c92bd0861f3635dfff2c99f2f5d85907ee08c8b953884406275d9233d1fb81a73acb924a8862f69d5e61f11d5b8bff39a2093dc0c47adefdba573d8aa2ae639ffcea058308b7e12eee07ab322d67c506c9084d300b66706006b91f4860c5789581edc447a43f2f5fae53a198c25f1f759a023210987c3392791cc7cb91b53f964af6ee7ae9c8600bd8741cc2aedd0fe36803dad6169af7ee3e29d1dce1ec31e4b8fc1c9b36929f6ab1bb3414df1be93e170154815680f467c1ec3577de685a2d04ca7b33b12fd88ada9b778949c72794e300e274a3ef15f3d9731dad714fe57eeaedee34157d836c7ce563d94993212dff097c8679b46f53ff1b47990f1aaf9d5b24db0ff3cb78a86cd807ec66e529fa050956dfc9a034b9e94417008cecf2abdf0c25882b718d944fea96a50982a9f43c0299ed76d1e1a4010b67bbd661556b0c8c168d92fb7414c9f6b4e179fd6bdb71070691438fffaa8f157569c774f5b589669e84fd6ab67f6698a2dff3970287a601241514f0239ffb5e2cca8377873e2a5c351d33757ede8aab3965919af6ac5407edbd7214d24e21b117cce97963a9a1d7b705dec257aa9872a6f45bbae635540c56e398742d0a9b33a67a8d50b6f1bffdce64f73c35ac2ab24a76120f335b370c1060f22d47bfd68c2f30617b6dffde54e0961ff6a4876f8f6f0ed84aa8f1c7026274cb0219af5240e92ef2f905489e544dbd1bb2e44102680fae0a760c9a4f0591a246a8183f6099244601bc0aeccd6baea0223b27ed582263f04ece76a1440a321e4b74446ccd7b5c4d53f17f3aed6759d948ec334f3f81bd711eceb7edd000aa9b6b8860a5469b273b9984bc1bcfe190beb0ff236d4d616901ee1a6bf445074a92e96652b40bd03f57fb6b1470c96a1fc0f1cce2d6a3502f13b154d1793709daf5a548db269f9fd6b17c929542c1df15fcef6707e45e1c30195695492d9604e6acbf2fbb66f5f5a4c55682bec702ce4ae1113ca2277682cc41baa600dcf90aae27e8785a88dcd630cb8c9467f8a0e7812e6d39bc5a69c2fa3533768d037e0df9492377bfb2702548884671d9194c361c2987a1029c7d9c4aa20162f4be530eba454794e411bbf30cbe109c9c1b7015009a7a65fa30b29deb285adc21b09a0ef1794c1792f2560572878dc180748763f2d6c277db8e0d7b43babc30bdfd0b003bd420395f8c40eb7dfc63e339c5e41e40c5a17ba0a727ef18c2cd3277adb903a96d535ea7ceea7588f02f63e6ac0a17cf05a16e87fa50ebf288fcb559751d06aff7c56105d4f1a7f8bc0b730d2128b496a745a4b65293039ed47f0286fe3b0c982f61b5aadd03a563908a39972901649792828ae0866dc8500fd75b718b1701508e5f3171e4281a9ec13dfeedde649b208aa760eeb1b55e592b17096751730766e9c4f3b2676926520c5276370df69a462b9e65ee57e3aeb23b77fe95ab9c034fd4d58385d0fc345d2b00a764a77cf69c93c2c5c29959306c85a430d9c3c6033c7753f94bc2ab018526682ed19969a072fde807bd914a947bfd6d91fe9acd0acc4e72641e286033ce10d0661011fa0c052e33bfc550bbeb71068fccd6ca0301d565b1507b2a376ee3e288dd98e7ab46f244ddcd08589b735784da3e8765fc087f768e40a4d4868e46ffb3f6c98ed61ddf138c107143e8ba9664b17e56f13e036a8aabb1e2e6470610d04e7ff8ce230d78c0bf776521503ba3fed8f0fbc5c1073abbe716f3338bfcadadba8f72394a87c6aa53bce687ff2b4690a9b7b6695603fe426c18175e5da7655d70ae9f8ba593d9a63ced5ee9678bfdc726ed4881d604638a04e3d9db727bced13cf385901989e797e2d32c98100ad3f2867e4f4a78045b8d53de98cf13fc1eb44fbb0635f0f181b9f2fe396c71d173b04e2dcc95190035ded94df53df077696b8c684364d48f8774b48cff2b979ad89b4a536fbde00a5c6c98ba3e69b1c448c76807e8dd5f8d76796b0ca353afd1d4e0513e7bfb400219bd2f8ac2ac501a2f2e5d7ad013f339ab158d28c6aedbe25d8213d0df608d0e247b9557ce81b98233c18150539907c06bc3f71adb2de3d75933344836a1e601ed0b385662fbd83b44c4eabeecbaaddcb436dcf48d53e2cada8ded0288ecbd0a99bff69439dab4fe8e4c3b38b07529ac5b313a4330aec2dfa20912545fad0608d6f8a7d2edfc621f9df7590bb8b27c407112ee3627798f305092ca4342bd3a029369e75da33a02ceef1d6235c9f845d5b60900b0f8a7efddf1deb489478cbe0a1e38cc31c6e4b8ea013f60dc2808b06f400be73c20f8f1331c9fbb33b474850d394615e40923d8aeb5963605a8027564762e4c23542be9fb24dbe264e5686a025124ac06fe5602fe4a4d862be4ca1af8795a562e38b4d5e4fd230e94b66e780284a937b8df6a8c0290adcf66e94682c593434139ccf606b524cb721e9575280eafa8f9a933ce28ba07b3159c9ab8d6a8d4f0ede447759ffa5afa0f3523c9ea0ef79405319c6dbcb869fbab78b23d4247574ba04cdc05e3f05b90978dde56790b0f9a522e7d2fabade5b8d75f5e746c4c466ed7d94b68c9a104c179913e3cae08cbf93eb35c5a67bf16ab795b748047f2ff180b41ef761d433c66163db22d9603764f77f0378a0cb6560496df234fa5caf94bf928bcbea7356c001d737607320372e98149de6623f869351337efdd832c45aca76edefbc839b3584ee84e01ea0383ef38660077281fef52bd34bf263678599923b1d9bb7824ff29075633636c0a94d2ac1bbd623684bc1ff7add1b1b8cd0c47870cd8abf9ce2c88bc85f1e59f0113e87bf65b833f116185893cd4b0ea0d4b5e7bcbb72a78c144d332771e19ab032e07414ff347aa7a58beca9ae02049960123b34cb7c1cedc736371d1f1bfbf0489cbe3e447ddf5c9f08be1a7a7bf3c43af58f683e2b119b9b376500e20eb040aca271898f75d652f66ef7a8b4c4534f17513c958707175c8b7718e8ac9b2100e55d71471b4cd538e3616b19ec6ecc624c74813e8be8909da0e77284866d6a00efdf8e020befcc61cf193d0c785dad39c2685e7b53c500dcc28712542ffb96c0bcfcc624a31e7982ad22c917308c2554badc1c92080e746867d191860c0d447034e20ed9c7e3a93857484a420e9814fb5793e7d56cbd47ba258adce32d4705f0052187b28c8c51c1d0e94b2dc525e6673739aa2074d4340aae0629bd96d01a9075d81a21cb53d351c0e494bdd89d437bc528c44b20828eeba444f2d253bb30e0a041f0c3cb8ae55c7ed13cb78c41018e83ef10017eaa4da505aa027cc58ac660c0426c2cc5e20f01616261026b70a19d082981107357aa01ea3288bfe7cc5880eef3536fe39cc05b6ceb12f1a9ffc1b1dab62d8c71756c785ec213972c42bbb06dae18e2616e498d5dba9b64612a171ce762fad186276c20d81975dbbfdfc110b763bc58e8a82b4153706f13f75270309c4829ef1621fe1c592fc230f80faa502f43376768995cbe24716e3c5e53a5d763b14731830332c0f0ffca4ed56a96c08a6ab93bd76db8574d0a71beab256c7f407a127741e78d540981ebdecaf991e05f4d1d5a8ea8439a156106c16eaa711d91173b6f74eaef160ac2d990ada00fd0a0e65d27d8bf002a6aece9177265a133071791484897848aba46e6dbeb9ef1b0b40da2ec380cb44f290b76f5ad1ba2c9cb67b2195426302cb07bd292dc0b33f0d7ff168803b1f492972637bd3ad8c17e5bdb43105b595410b11265ef231cec30537107d31ec4fe8fac99a71f86f7bc9c231992b175f81684ef81a6f378a4e760c16612eb880970bcea6e19ab049cf1635242fb4c7bab6a3fa543feaf00a3cf1091b6bd11528445b8458806c0406d05f8849dfeb3703ec3d46e9f904ff161ee8085d50b7e5938ff6310c7b435174d681e6616112dcff3824d5bda0d70cb4f31e001c147212817eb625287e9cabcd015514bfdcb85be0a31257a9b332045eba6204047f374d0fcc2308f0aed0d6d3d30a76e7710b0545bc1aeebfcdb94d36ee970622adb33eb42f638d45be81e864b52ccb038c84fde4767e7a1c3b17878418820ff2e17b5d4a2f9118058b5ac683f37bcdf58aed1056c7c13edd873bf652fb8909489a6950f95cf6705653d2722bafe56251dd6ceca6ab79654dd3c6fa2ba8240ac24cd15ff96e6bd5cb7292b70587f35f7e97faee84c2a4658f9f3056924f6508b48033d612b2b8207ad71d47a9cde166a5255ed4a574d6fc8b923b67c440c00dfbbdc86bd23bab7ed5485b34021d82ab031651565b771a5ae58790c4c1661e00658feaaee05f00052e1f4758137cae3814dd3e0cbe85d21f175bbaa84ed4540e9f674f6ed7e5c46097bc40ca56d6d098d5307be09ba75fd858485b0efc4076088a1734e1f5c1fe6a00090a11d727468023311bad98d8410c2fda21ef75815f0c31a2ff77e59381c397c4a2d50c1bf661bbe1a4d6a5b9cb1c26be5a4564532309f1bb597fab50c4a55ac822ef3ded3e5b3ea7ac90027bb062063739f63b95c20ba6479eb126fa4fc065d9883ad19af9d488ff9a1bb1d99ab615c543dac5c3930afa77fe3e3871b2ee154b596e3f4c3b51d4ec9c3abd6cc6a0b009729334ef280d5f29950d917defaae873b0b21219fa9f6eb253c0e1642e52f71156276a423b04c9429d225330c00494f3e7d89165a2eb432fdd8d51c220e9efcaeb2483cbcd0d552478e704f374c2a9f3b730ed90258f5f677e0ac2bf3777295486db9bdc370b8bc1862b17c25a0bad39a5733a70a99cb64f872682a7f517d225b7ef27b004092af7ede355db7cace33676325ce8d5090317bc112d2942199e4728e006472708f2289b96c15ecdba43a896b58e3ff28baada93cc328964a7f4efe0637fe4bd079d40f3746db9d744c71626ad02e876e4d18d7945f319eb283c7e7275785de00e763f1f77be8990c8caf54b7db6be978a1f33fcdec60a51c9ae1a832925012f03800eb368572ec114de8ad17b54966072b1eb7ce08bf87c52e6ff41025f235502c14059a49d130bc4ef3d94460ac16d216692c8b38d65075e0c648d20cac97700290d992e07a8957340c8cb7ce0a1165ae750b55afd110717dd909401db0cae0ab2658c9faa835a263e5bfaf4e05b464bb243fc7603f3bbf23d063950085605079be5d620b1eedc701ad79e846ae12c8b503e66ef4fc1a9bd334fa65d6e513e0b525420404ccadb1d30aef8115e993bb1ebe96d17f398e65dc28f6f2900a4b50bcae42530030d0d16a589346820fb5de32e3bdfcd54681f65b10b20d7517861030d6a0b801e9b70577597a75d2c9cf13225195ef9ebf6988c5747e8638a6d1b0c55f52f78f106c88c3c43a7a3e5578764ecae91a1f7af26f39979f0569f2944078abc6e0486b7937b73d54ccbd307eb3a6191ae61113ea9eb55655c7799478507fbfdd5926a4f8cd791846b7fe55e831ea4a1b5edb3f4dfb2c2eb0e26868d650618fff145c3218d7f9d552227063b2ca2c9d73b149c2249029218253b2f05db016f44d7b8204c022bd64c1d63b268e612d3a6fc3d145a76612a90439f89a62c0e9a2426e9f2ff760e706033dd1cb9b723df4f4c08fa3de57f9d9f9c9628bf23070412a05c47b6846765b9786e7e05eb973e2dfa58c82d8fa4f7e7d818e19d6a0d9a44143694c8672f19dbb687e36e934175c56892e96159622052619aaea0a20fce0496f64f0d772726c1b67fcbf0fb3bb04996cf21ddf60bc0adec508d56d207ce48e89522ed79afae285e543d20488e5d2eca2ab5ab67c6f0d3ce0468cdb4059eafeabbc7cde5e0322e40d7a6211672fbfe3c11408fe41b747179b6072d620c500240dbc40e80b6fd89cb38580c8cbd5ed42ca4baf1b7e4628227122d477b037f279600ef6d0a3fe699579d0fc5c709d66853b5827269a72e4cb9581ac23204203060c06f300aa50d47097dcbb4c029077fdefe18f7a8351e0619b32a31f0057417647f2e50765ba218cc4bc65f5a0a531d683f5f4840fcdb68d2c67c26aa08bc0bc6bad8b2337644123314cb1ddd4081a99e50ef7a1580ef514974a3c1900485647b241ce630e78252ad061ba143652bc367b832a9a524b6c10650523286065f97b3a62e93005f2005a87f22d3d3a8081844b4653726d555b690a66029e705c6ce661fb1f91cd00ea7b806f5bbd3d58cf0b635ae204bea1e937472f1927b029fdb35643e2b5c29fe2bfe3a71945828d8263bcdac6612700898ee1de0a5c6081bb93dba5f2cbfeec38ea01f89753392f561af75702c0bfbbf4a1b2f1796f70fe9f1146bd1895769b61f67cbcb219ec3383908406978b340e6520bc85e7ebe0371f91a9691e3571daf314b78d00d0569b0c6ef1f67a0a2fee8786cbf267aa802eeaeb20b77acef6a039350ca99170672c27091f7a2a639b22464b3837ef0b2039f87eff79ac8476274ebe2f97404b1f636fa85f4e75d6b9ab5ff477af9e916087bbe8aa73f7b216024859d6bdd9da9e104260c847770226d2322a7057f3f08017c7d06dd0b310bfda24a4cf6681d87f97da779b81d93c5724444ae35c96a4c027b495ebc8b5fdd2c03b48d265bea1575df38a880c25577583fdb310019d85f068376fd24622bebd505b75636ea4afbee05d327a09334deefd8d29dfdaf89480f81ea313277b5771ea9e5b270a515d44215c0efe1dd52910c45bd16c5d72e430e7178c31af86354c9fcdd905226ac8189441e3fe8fb1cd3849dd4405e7bc966003267ed9f8246b68db6c28171f041f48144394292c9b932dc2dbde46d9137eb02bbe09176bf69a9c454bcd147426addf446213ca4ce8e52541986c5e4fe6f2f0c69707a28091e37c860dd4bb69c1d777e25a61f31bddf36abc522bd924c0e8107864ca6f43ba015739856089d05113cc775f1e6fd7cc682d00751c1db8eaf4509f02a143fc477d9821bf83b248741a6bb87e374ce165a0b12b01722a6cb211b0fa85580f7a8d0b7f01d75d7cf6772ef0e1404e659e6122bdd4caf13454c8c3f0d4b8924b401a68ccd5690dc96c30a51a260552d2172c0e4e1ab63664f8bc947069efb56fc4f6369245135788762d33ff03537d5b7df986e78d822a5b757e4970a0dd2c650713f36fab8950478083701b353648203e8639679d449865dba9a5002e05d4b30ac2c3b08809c4ad7fb768429c42e804192930d3248423ff5b4c1380903fce8e78c60b4e1c8ba6589a3c18e76a0a7ce0cce400eaca7d10f25f7489f042a4e5ac55b9ffd3df13aee62263fe8918b50c3a6d05a98b80bb219e2eb8cfe0ea81cf20362e76f1ffcdbc5e2e37ad14874ce28a01dbe2ef617f6699bfc02870fd029b39586c1c5a19e9320d7e32850149b3d7a4a9fd5ab2ebc59b013c5ced40df1738a22b12a2af93d59dbf01457d7ea9c78e6b38403d807c67852a9b4a7550bf230e7a7165bc5571d5aefa8bf06de25089d4530e93b4b7e86bd53be87594a0b24586cb03a1001d8a8a139a39fc24ad346a8e3e7410072b7b3ed59961d6b200de8aa32bb948c0dd1a66ffd23453888d3fd98338708134492de8462e15d61290df754d56f72da79599513abb8051063788f269aa178e5033fe77a1038dab8de055a49026063a1718771e41ad73d80c8af3be19f73277169ae7ac8afdb31c32702fd19530c12fb986f39368677458fc674bd9d19d8d26c1c76de4cef2769cfe107d7c821a8ec1c61db2a2f5216460ba6e3c0a29a9929a54695f42226473319ee0ba334c9bed53bc43dba9f77cb0f88a453fbbb3dc0fc3772e9574eb6b0756c380081cc7301b83aa9e696730bdb3abc3941731ba367fc5d7f29b43993a9a5fb570a6058aadb732e61f6afc71811b7c01455b0fb1e0ebcaea0cbfb6d36d9b5545206ce91cc0d683a389abb5d115f909b4b09f3032edc36e2ece537c32bcd0a91250fa08fed464f1b79cb593e7b3b8bab0a3aa286d57874051c3aaf8632d1c5ea250ef3394146aef5248ed08ebc76b784f2ac306eab5a7e4f0b0cd485c3cd7963e606d99119f21a30ef1f2f43c6571af00b3956f8bff3ab1b58f0f376f7aa8d5d2400e1704931a93f53db395b36dcfe0a3777896fc716b3114f4f53f3c2599ca7df06e6ae2c6429836be65e2c2c88ed21d5d3f37af84fd79fe31749c78938c7736f038a9efdbb1a79b476f8ffca744a439f5adaa07f68b90a94cbf00ad34a153cfa00fcd305f7f3bbc7a0ead0efb0a10e2d6861c87e1ef0a8cd6d1d2563be45d5370c989177d536715c2d473bec54116bbffcc29e5976d0fa6374ba1a7a56ba114204180a97b58df17fdaa204b838f678db45019fcc5efe7dad45802358cadb0fce0bbd95078e70ed85e0c0bdf9bed9b6cac576f26340b22dd7e4f94d0685bf140d078a31a9e084c60c9b7777f932fd7efe4d908ac2592d21ea87d0fd8c07244ee60d3a218164ca47b947746792a736b10726740616c43bbec2fbe4be3e3ee4299909ab44de3c4b902ba787595171cfec6fb643fd228c40c1bf869bd73703cc3d92008ebd07b8288d6ef6da6c19441557c6d038d3ee4445ddf26a98100778ddbd43044f5cd548b2d4482a5349ee7b087f543e0c69071382ff16d1129fc38c5d8ec50f76e228de2a7f180c7d6fd5a6ddd503dd7f96b5ac4af53e65148381e8d6d8ee06d2a82ce0d8bfff8910c865eee9455535d7181c359ee051622f02bd875a13e408d1da4bf14710f8aa4a06950e67659741edf0f7cbbbabcb05deb0be4b1762f0029335d26c994be5eb3110130917622fc1d97e1228b5942f0161ac955f1555060261f64b149c87bdc723ac4d0e33839074c556da7a668d9586912d289e6d54f4010bd19c428c53d14047b25cd1851f766fe5c7aefbcb8e084fedf2def5edd16601fab8db877eaeb788270151c60484bd8cbd9575a16c51d20e95a4f29fb7f78300ec96e6821d721205631a22a25d4df049da1f643cfec91d603ab1e0b6478dcb061dc0b514714ee432b1b035777acd2d34d634a4c5a6cac8f1327dd3f3a5e4f90113371bbb94fd81929488ae5097af514301477f85f79f32af161492e5c7fed706933eca47a92ba3f29f496c7e565537039fc9acc56743795a3310ef44b080ef027265698130ff6c8f75e410455b0c133828817b774cb13d476452d6781ea10004d4c56f4d627c9b30a46ba2dd1b880738361b3f813ec8421470d1246cb3f772011a2588b974fb22d8a7921d2f4eca2b3c23c69c2941d658646aa2220b904c9a029fa1c566227ff9a5fd45b22e6fcfa7117cbec87787a23406967a61d9dfc0790c5ac1d76fcad50f9ab94181ef0c3c0f36de34973ce3b32f8358e4244c0a113607c080cf9ff97d0b68941de6e75475f269672fbb07bce69b510216bbf2fd030e056d3ff095943d3d2e66f2c3beee4306a1de1207300cb99747e64be7154f79fe0686c94a41eb88214312ccddbd5f86d013551a261b9575c861d2a4cf5c5a81760850c228320d08422cb28a47de29c98ff9304974b322e62109b3614a7b5871cf0044371ae0425baf7956ba9ecc727d467c3a1f24cb6e3f87c26e01c415fd5a8905a081106c9b846dabd5b61a36232659f5d2ee5a0f9c96ed1ae9164d5c69aad70552523f92065410f871501128fea84dcac204db1f1887dbb34d6773824fe590084e2b04a7c22ef1e02ec19dd23ced02daeaf1873af995491bbb4ea455d48039088fbf11e86aeb4642f1b4f2e6c43b8d2247bdb94708bb6794a27098890cdd230dfc78287cb35892018c017c0d6fcc1a2f45585e63c4550c976637c2a3739a2f0ad83770ba4a4042b5a2f2ad653d51ddc670f36a6e258de3306f78d467d5decd02b32314a410fdd37fc0bd1936c6a0757eec77999cb7e0468a3a3cff43557bda0359d8c409f88a365d26befefb3aa7fd86bc7259bd3f2add5342f70512d8a0a90c3135a9e61d4383daae8b94df546a12693290bf276f99808bd0a57be766c11006d94d4abe695665ff2f5ee8f3ccb46f9352b16b6946ec93f59c3ee45afed6630d0fe0bbde7156efedd327259f86f4824ec3501810b809fbe2586cc449591c5001af8022e22eec1d2237fac63dfc9c7a0881b59c580f42cf748be0298540cf8d0aa2775a4c3498dac83fa5e5580036f5442f4876d3e6646445c9b1ec35c4e312009d4471c646bb2376854647e9ab13639b311dfdb4f9dfe72b467a32f8e86cff0866d8567e7ea74a25ba0918646025c63df98874043e6749d08b3ef0939dfe040155738348b85015ef34064cf4c224538a7af23c3a6a300d38dd9f9611d3f9ba015739951e7602f1e18e3921570aaa58093c570223022f228dee5a464d4cfd3d0096e310746cae558c6140e966c41312b1d7d8380336669633f5ddf6454eb97a06598b0588bc3c5fdbdf15f1612d1dfaceca8d5a0632f098d11cf70f09986bdb0319ec0649c80b4f43901c95a1d107650f19db1161a101cea3860fcecf25d3e109bb20ef99e8cb8672b3bb895f41934e406a97b2491addc6d0be4c3ceef6e0180d0d8f7a17675ee4b1748b60be414e4e2aa3c307caf4a288f4799e9e1f98dcf90a16c4f76dce256cf2dae6303e2a4d88df9b4157cb018ce5336db118d425368106ef609f194fead7e73780a96337c223dd3c9751937108bd026c28d61675f5e70742d561f7e6ec62c11cbfa9674e6e9efc5efec1abad297cb37594a6938fcf9405ea95b08d1f86c6f080792295164c96164a603408f9e6dd542c02d9d88af06909fd335961c3ce13dd3e2735b2df55299d74275215901c19cbff99b196070ae504b72f4d04c48f277c9f953745a387804251e9aab855b68f097baae974eb4c710a283be9229daf4abf87ab8ef76aabc150dd2fb5d4eddc9a4738b120f29c5bb10009172601daf6e2ab0a9cb605574983fbe0d13e118e17f0d0fa0a6e7aa447f60488ace0f308a615bd293a350fa353caf8f49f22c66019da41c8ff01bde33ff000df0d19783f8dc840008d1859e31a09b696ec5f9667e857d84022f0ff907b4801d5b520e4f0aef40e1701f9b03f490b75515fb8607a16dac4aacae4de82ffcb026d3c806875c108ba2d5d636d8cdb702bc344882ebdc769f157871851a35d6e0335a5b4478c153cde155455114d1a4fe6d3b9623e994a42c07c7fe05e45d1670a9065fdafa69f836cf2148df7f7014a6aa6579d943bd796c5a22b54c06155b10f09c1b1ac78e829c8790aa0174118128576d104b8a4d34926744a7db3ecf23a09f1dfb4f51830eb4b07074010c43c4cc74d9e37106650798159650edd7bd3a9045e4d4e3b8a7c597be23ac012d3beb75417a787562d822305400454684fd699058960e394a4acd397332f53174b9c2c727c0456b546389cc8973d0a42265600081f032d549a09ffad9e3d2ca10621905aa42d4f84c757f68fe235fd41972e98015f1693073ecc81b02a294b786d4f3cbea8b6cbdfd801bf8ade73cf85a233270487a7e7b84ad3207c56bcdccbbc1123a4d860a47d3fd47d0c44e184b417b19f09976eb746b2e08ae6512d1ad76e56798e8bb3a3c7fe1ef14767befdfe4b8ac708fa3296cd78d4264a8af618ce5c33312ad1a353e7521e095b0bed87e6d9011e04605f19df82f93213d978df322e35c851cc5d8f3fad8b383b0859c484267b04016b1e7c59af034548a5c4c1ea3493db30b8be7e2502a9b56823efe50925580c0cb64aa97227b0be60f69a86db97062fd0c4fe237e2a4618c03b589393db3cf709f8c1385384c6aedefa3d7e31f676d2e0574b50da4482da723149234a207d0904d614f183cecf48e5d60ff4ea092a549cb8176bb70b3e8a3bb8d962a6b7f65e02f8e6b80a68096fd1676840d0867f7015e5c4814e6c27b171e74ede5561dbb30f5448683409bbc29836bc980f7d4e97647938b96af59e5b694023a5a4a160680ee5eb76b950cf02497250a2599875e6f22640dc5a0c976f3f11e53fc2c88cff0149253c4b2efabb335a8579bff8422d37a50f535273c7c0acdbb7e6568459cf0500564f8914a07167083e89582383958786a4c17fd804606b1e03630271318c05e44525b21ca658110ee05bef7140b906026e273eef934a750c276e27b1edc701 -generate_ring_signature c9ab4560e364fa2cbf7ec6e3aa25251bcaf2beb043ccacb0a4624058fd88d86a 5d6b8e078641aea54474ec65784fccb1e95d3a0f6c1b97005d6fb65077c145ac 3 56d63e1b1a487fd4d7975e66dc212e3ba4180fcbc6ba777bda12c4a957f359d1 0ba8218820fc5a47b7449974d27f4cf1fd7ba6698f087a6b007a15637c8c56ca eec4a9fdb41c38be9bbb62e86f0d497b8adef1c297306cc3a209a700f1c76981 461bd2cc07b9c8310b74820b6b7fafb962605dc168cdb11be6995b344bf95305 1 8c6aae78c6129cbd10888d137cd1230ec3b31c483b9809621dc3fb9b0a523a0ee37e047bc98d06c1e75f5cc7e7226fd16fbd55e1b76e956874f6720dddfce908c0b34d3c68ab03f3366755544da56df78d68228b07d77a5cc90b165544f7f506f31d32d1271ac18d85ebbb38cdbf09957b461ba326df9aa8d2f1cb38eccb75016e1a7fe7c7dfc3bb74170d816379bf0deb539bc21e418b0ba1904a0316ec3906439c2169107ac6d1c94d60c43186ec30b1f2de72c2ba7148c94525d13be75606 -generate_ring_signature f075672344407c2c73e656aab50a5308cde472718afb873c3813cd31eb083a22 725e94903757033d3ebc8b23440f0c208e60f8932872536ed80050f0ce714156 1 ac769e6e63bf28b0e214931c70c958bdfcb8b7e662f280fe1699e9570b3e4df9 34a4a033910e6aee4adaf7aa902b9bffaf1ec93c9949bd358485f59c45538306 0 1a42801634ba07a45f8c7f4fd070213d36128e7837c214250ebc50d6c766c0037093c580a3536b954637982b16129b68666c3d45ae943d9318da20fe15de6805 -generate_ring_signature b5c9ef14b9a08e78925da00b25dbfc501c26ecafbfd1710539c0ebf3f0c4fbd6 bfc8153ee1fa82bc2a51d1e74adb672f8354f3e11591ffa0253c4a3a7843d759 42 3cd53e438e8c719ef4087516c7124def7dd74122134893d56fc52917f9ee9b38 f2e4139786fbd913a4f13e19d484bf62929447b8afc84612094b0f5bd043df10 5fdc376b219fe822837a4ad8c483f1f392a1cd70141368a20b0d47ad9fe1e009 727d639f4fd6588673837ba332ad5c0ccfd832155dc7e194dfa9a8aac128869d c98ec078ac512c8be32d11374bed3fd019d46dbfab909f65b47052a0b36a20db 272da4cf42e06b9645cba73ecbef4c57b64b99e481cf3a1867ee7c971364904f dfe696e5068344bf3f7994528217694b8b3d359eb52a362423ab2dd23aa0ca5b a2b507f5f68b776722b35012a49fa542f58eb1f6f5a4f2b03ea507e306dcda46 4235b88c24272d62ebfb9b75e6af06479fdac9f26ee03560b53e33d48a820c78 74f869ecf9de6d2b0835ed98600e6c24794e549725eb338f0243025d9945c008 daac00d7a944c053a2e423047c1ae410cf73d0b3e3a0c57072016552cf940313 a6fdb201cf3f7b8aaee0409bc5098d0fbb791e4da2e87846e69c0387ee917048 c556403a2899b4a1288c81e6f4e7f1d2b8adc7e1baacb2272f87407a5987ce4c f9f08e37a0c25ecf700e25fc7107c9bd008931cf9fdc39c454bdc4a8f66b716e f2c59ddd77f72881f42cea3a1a1053f9b4e4723e20e65a1874fbaceb9fced0be e7ea591828e31b78bc7634275bc73436b23dc1eaeafb86775428e5c0a10cfd39 7c15e3d04367d0638758a8c9ab73eabbcd356909c04377d9c3008e70e1ed0eee 3bde3e1aeb47353edb3a007ac3fb658471f6b3c33a67b8224534301d41d55769 8b778667d673944e41147d049effd5e9197f6943b65d8bc47e251c7d66e614a2 a566e5cb64d80f5eb09d5d8fb0c7bd67fa50b2847b7594a6817c94a2d2cfac40 44d95857ad4b5254b378db5d6f5de3aa0c866e0eba2757695e5b955a71ad425b f95a8c8f71f794894ead92d7a8343b7f92c2902376c1ae65891b425d7beda62a 29ec148b76bad44d003a3e53c1cde766fa9918b011696402b0b5546f6cc98c82 2321dee77fd3f088ff04ef45e65fe6baf7cc57a2e8d5887efde37d4e5dd5940d b094fa11e71f396fd2c5efa723ae3b86784d3b4a3ca28c454807c379fd36d8e2 62b4e2eb263e0af951102eb0db0ac934087876431fb5169f04e3033ed66473c2 3043bd14d14c669e87fa0c47de6b5253e1c9b660a200b528d5d008dc6372ec28 81837f200ac02a8ff2949baf7e2046dd41475237d01dbf8a7ba05435d4f56542 b374ce8d68abd8d30d806eeb3133692abff1cc03ce765f4d2189f420792d0fa2 8aa45f186737fc070eae52127397db1c1d921b3716233a76538ef4cf8d98b006 094a03faca408ec2ef2422d1e8e93ea9ebd15ed6793f909327515725fd245d8b ddb31f1ca2d6a32e1d6ce1e3c7935d2afdf32bc00b185ed92832441705af8ca5 cf9923a125eb3d3c6a27bc8abb15c9a1f5ecd97d6bfd4101395c547c92f53b62 70b0954b770448de581288b0c28ef89c95ba5bc78d70302f1b3f8befda378e63 f32f02453664f8af919094f4d8ddfa34781c83f4e8b8e76bbbdda82120444532 a1c063c2be3b23be7ce11920ad588cff49f0be61f4cf1d86961ad990e0d74af4 c9ccbb04e610a9d99373863ff6334908a4218f3a27fbd9e97b76d5a1db666609 7352167d50a292ad64b7c1911ff0d3245ea58be061baae00519af17ec81f1cea 7340ce3795cbaeeef9586e1be8bb45d0e11835db89cb991b1b9042dccca58de0 8486d053b5274d48b7174efe4246d6e0ce405a90d0101558f9ff98fb0f1f684c bdbba1f4e3c95f849d2cada4a88daba5965c9b1707e3b963cb130bf9ecbad8bc d077d77756520959c488b842f761257f35fcd5190e86c4676a4a44d6ce2c6bec 553cc6b8ef0b6b67e23e2a5474a2c74b954bbea6e8e8e8c6e1fabbf5ae4e3e0f 10 3e49919e30d003efeff8e4d9e2c92d94ee018486338ac6009bbe15bce2b101049745b799a128b1cb0ce3c4a60cd2cf15eb120665cca12f63166b180c62eea50920785ff0fcc3e785d2191cc23356c2411c36cc265f7aef4b17a5858b26c81400311e2737f983a00cda3da11fd51460a730e2ced45330ad28831bfcf835c8cd00e0ff1bb15b1466083b8dc250c07c10c30760358969d17404050e267da5c7a00c70cc59e71bd8d12bebff157a960ebce2c0304606ecde99edc82831db3f08d405aa18a0ccec3bc4ae46433bbd82125eadece7ddbb1a74ca0140f16f9a0befb80d0e594cf3a2313e62f0a4df1f3ff771ea7a9f5b7d15586cf9135f3a969a8d060442e5e2ad7d0f9c8b2c224ab09f342faeea2813a5ea46b9ef543c21cdb173680537f3b70175eecf00709af0ebcb0aac0d42b2df3197b19cce5cbba83952cece08055bd4c973bc815c0e65287a2a2d8cfc8a6f5841d5528e7de19e3e7c0343d302f3a006372ec9c517e68033c8e9b6214db063bb4116e77197960b493a64021108fc3e9ffc062381f21f53b061a8898612c6fe61dd4c70cd303d236a4639579205c3a19d6e682e6ac19b8430954bf8e519284a297477a6aebfc3257272102b5701c8ac36b1928fc1705d4dbbd72133af7332c3cb1e3beaed129eba8ce1b5305f0e340451de1f72c3d88187c735010dedc50a4eb1f23882fb797087548ecfc9e901a7eadd884ac8e506abd6a316fec835fbe74c582ec341f5fa360ca4def1accf06b7fe2e52880d4c81d0ae98cd60ba91a3312943eaf9b90b516419446cf2e50c0a9aabf240bb13d2aa12380b5931364e037cf8c179afd6559d2bd857ca8d0dad00829fa12a8f88ef09de46a81809bb6a15877032567d0f39475f71b74103b11a03de9382343c014b5481830b3b808f78a5ff29b5a9c38161f043b933af8994590603ae3a5a3a32d44537cd71074df9c6fb8902b00a15af0f0eb2488daa4b54ba02b07cfceca8a2cc9a6d60e6ae3086c62f5a52261ad389b5cd4be4945c193cf2041f102034e102cf5fd5529f85f4f8fc6eae2ebcebfbcf02ca48a6f7df1314ae0b95a6c8160b496bef5bf958eafa11c2946cfb778cb9d27da2f550e87ade2a8f06f73704ffc27bca79dd6a6b0b9774003cd5034a02ce25051fa683012ad227300758c433757b5b9dc44bd4ec639aace54b1d2c88dc34196670c198c830e54a5b035c0c1230f6b06ad2fab77079780a682fb48ad1ca6ea412376eac56d7987a340ca6575ae059ac7de1fde59af1934d3e03a6aa684c4f13670c7bdc966fd238b2033729d7411495bbdca215a81eacfe55498db0965adbf2c717d3520c43a784170293022fdafc2a267c3e6c6e1f7f139e0a82e08db9b23d216ddd8d9ecb81b7bb041d3082714ce3467959d05deb3e2eb8d9a69cce999ad186c5a1c5e960768a3a09699c155bb70fce383aa52fa1310cb8d1e92a4434810bf36248b535ceccda890e95dffd6ebc5902d0e2874566d84b1701d0abee21740181250b1d5673f099da0e68b2302656ca2ad866e302a5307d00796bb15d36b9b58b503c17a7c21a17d40f04ab0e25b51c10e9be2a7c860dd1713e4e4f18141ccfdb2f307ca1281af6e409b72c23c52725024860e7b8e3daaab89005f64954029d02ec80654f0e79368c02d2605dc9eba7c8e35c1a60582a1aee587fbd7b3747639772f740b4eb9ab522038b3603de31a183e8711fa19ceaf13a6b069abee09684e1b64a9600302700a809ff3837befed4c8d18c48f86382a82187ff0cd2c7b1abf704d7e61e1d219b56011f3f286896bf6aaeb16ed90dc51ec6f28cdbcfeeb6672b0a2f36e387211ee201b86441aa81abb09ca4be385718a7bd9f8284141a5385bc2207961451e31466052637980bc8ae1c42a274702638f173ba80a62c037037bdc46ec1636180cbcc05af4bf689d2d36349474e8748ea7d4cb143c1890335272074bbca0a7cf6b5bd0c823967cd12823dafcba95c7d7e9eea148cc466c018197fd70a9a17d2a5f3ba0db7fe803145a4dad1e8b39cea243ffc73149753773dba07a7aeaa344662bc670806cfafb536c9eeb054d3b6b339c3f1eacaa2859ed0f11faa26a3a8fee0ea330cf455f39054b9b712922835b4c4c52166d94d84355849aa7162962ac22ff678071c28a993adfbdacac80cf2901b30b9d3ac1c62bb68c37d7927d7d11bc0c662018373b34f3eefdf4a8018ca6bcf69ec1ed2b9626bfe33d6c10633a6be4770b60f963f37ec88de52b3a7b5809b8ee19a3a81e3c5c6e70cda5cee480ed4f59ae303f06600f585d542a8d4583d068225e726013f9eb4f27a416b02fe2de2fd4531018d14a389d4dc3189d3dcb7ab7b69cd85a3f6ff4e40ae2697e1b5a4a9d0ed10018d3eef23c367727e43029bd3c5c84d5864eb2626bdf1879499e178b110409b0cae786f5ba20444ec0f446a23a02dddaa99babaa38f3e56d56869a8b835d4830483f606ef8f3701cd00cefc634eab52140cb350386c9c50b063223eba954485031d537f552b595d941c7d0f2573ecc5829b0886f5602e03708ed509bcc6d6ee0bd3096b106f32819553aff3942f74b05856fc0cf581b13796051ed61ad9d15707e7df82fd5449f26396c2b430285ec2a01496808a1cbfbaeeb64824b2939ea809457998e96a1de693369fe1f00fb540633c54ab9f1072e8ae71e5839c73595504187d45f80ce7367134ae1720b9a174dc991c4b66230edb09f9ba845339a16a0322054eb65d77499dd443b66939ca3682ce1a0261ea91bea69ccd101b23f9f5035f4bbddadcb3b49dd25aa7285d20d9293ae5852ebc2822e12f1928b9d7ba8009758d419412817cd5fbb00843334042d6cbd80b73fce1e199da86cbc6277b1c0678379ff8271a3816655dc1562d44bbe8ad0c55518b73d017615d9c2dc0530400ede1452f6b7d685f8bcd99059c3c19463c56d55a6c71f9aafb42057b04b531038566f473d4e4d06d630d98d37ce4c310afea18099655df1a441646fe23b5d20501b37f1eb378de0199bd94503f83fbf5cb98a50c3dbf8b5d269f1120da3a0a03cdbfa8c7ec79e4e495ed892a13e66266967ab9884469b1da19014d29c6d5b10379d5bf52f9985bf9fb6e35157995f919c69c74bc7a11aaf33fcb61d05e79dc0dba3706efda44711cfe0cc6271b28b0dbc99abafb31f3387a48428f00022e99073990a3b0069432c4d48fae84544cd0ab2c2b8f5e9737f4fd77c2dab2bb807b003c0ecc2a2cf81c74a37f3bdf48b1b49ec51384b44f9d2a1ca3865dc53388520e6be38d23f525f1f1fb4582fe62577befe73424e151c1b34de48d758730981603a7e6c0d10af91fc4a0ff2469fac6a30f5cf08a6c991e8039f03a235cd4b1180ed80f07263646a45034bb3f49c79de6d1b23c37d0ffd03a1d04bee130720e720079a4f3ada0a9f850b46e031d17a5c2a31f6c9940e060cfb0f1e5bae2461ce105c65ebd00fa0332419160c38fddbd7ad775a80b56ccc9c05e0155af34ba6a840fb8b95d57e12cbafb5f1760245f1368ecc1f156d578e2441a09393b2c4abae30dc693fe4df79524076b91ecbecf5c921a5105042fbe72c4c7aa9c106b9d30740a458c215986e9fc6044d5db099b8da8239b1a6e9085014b4f635860e30b6e220bde8bedaa7466ab96c31b5d52f528b9e74a1134e9609cf0640abc1ad98d33f50db641be2fbaf0b48c2975c01af7380f292792eee601c00b7d90562f74f3c1780fee823dddceac2d1c71943df3c2bf6c978cef4110d17158edf76aa851ff44f606 -generate_ring_signature 9b329f5487db8fa896767fe45c6f92096dfe0be8251e4cd36d07c0919a9c7dfd cc275cc9d60c681f4d00425e16466fa9f2c36f5bb333ec2c95aec837a40d0fd9 4 67e340c96d30549d1dcea389f686423bf0eadc42fda684a21ffde3fe2a0a1d3d 118888e22f09ffecb3a079f729e993bbe3a2f8ad9b3ab98943d7b6d039c93e3d 1c03bb98ac40e56b9101b0a628aff1f3ae682a026cba93ede7304f942261f04b 9f8aab00b42766e651e5d5c5a71ea8399f52bab563afa059823ec0ebef99ae32 52b84d2cd9edce72b79d4fe908f59290c7231b8fd6479b11b81e990097c65707 2 703a05af7801090fce02b21653a6ab04ab2a8b215b053389ac3922b282b2290fc03fbdb7240feacfa1b9db9cbab9c023cbbec3b571c758dc3aaeb79220b559082d37cce0cedfca3493347d691395ba1c0efef5740cfa01b3f175ab64cbb7ed0e87188f24ae7617b3de43af067d49b0e61447c5c076b0e9e5933635f8769ed5037ce00c41bc48d24dd61c32af950f6c5bfbdc4910951f828fce7f73052fd96801cf45bceb64f6c0747c861e1f4c94d8dd7ec86cd909607a7357d0fe4af9913e05d148156d0b6f063f5f6cc7c8790a76e8188d5e059224b877dbb01b987aa2a00d9cc6c74bb058ffc748be0835ef6359e37a671acaa309d0ee880d27e1bdc4bd09 -generate_ring_signature 3e8b6dc355913127e29a053eeaa791f7b9b25c96292970b20617a77f2941ce66 864ec9b981e85f596e34ef440cdab5ac85878584c8bf0990753d53c281430f5b 4 f04f4e042649c5340e6decd37d44afc47a81e00907733866c866d853e10616d4 d7ac266b6fd14bb55b3a4c3fff28017b1f2d51591a8a9a38a921b55e0ecb2b36 c579e81fea2b7202b881513f371e0b5cc4ba5e941e7378cedbf2355e5df74ec2 16cd14d1087769260824246f32fbe6c6e8068e7efed639574c837904fa186e57 1d46be9f88c8a3546d8107e6460b04cde2821f0c815fdeea2a5ee8556b36f006 2 031bbdc49e2257857cb8902e5d3c3c4723d8121c40d1f606357a2a2b50ce0708fbda50324182da7988891ab0094d331eaaf1dac9ad77692c21edab579a0e980cac88a7c3cb40654b0449747ed90f330391802fa4d535605c3efdaeb609eafa0b8ce1065b8d69280a5c2cd47ac139802109dd07a9496cd3e358b43618cebea70682a35d3f334433f1c6af4b06da3508dc65c241cf043625c957ddba9c0f110804805668bd680f215b3ab4588dcd050e6ba1b41f429547791120c454b9a8a43d0e4028e102d337102d210a0332edb18eae1b6800907f7abe70a812cf574135f405401829684920f1d116d53807202f8b3ee4ce34b92150ce9edfe2be977d02910c -generate_ring_signature 4eea1e52284c31388c572c77b4dd6bc18080d356f22b220fb2cf4a1706bc0f86 0317dcfb34494772c5bdabcc64b2661b8f061614472914a82ed7860376853ec3 4 35ac95d061c9f478a387f5b33ee56b5c962c622cb8335e3f5b05a50dcccb9567 eff1403b48ec6986a6f3a436fa75390d47df6b75578cf7c00c9545fadbf9f9f8 bafee79e294e0d60c0cc2c4ce4fae48df86d0ba91e46e4a2e6cd033b34c74414 1161986f00835114de03ed5ac2f9b94de944c1a2b9f3a237c8e3bf9f0b048349 324b8b23740f0a95fb37e26b28ade940114335d0c395200e3df2dbccf3fe0b02 3 fdc16f1bfea3828cf5c3aa483c691fab1b8eebe7aec1919017384fc2bec4c50182b4fcafd2c3101d125165244a3ba5b1e1ab56d99df9db66ac4a1cc93b39750d338d88ab3b7f9df6410ff6a1d15ca8c0592c4417401af9106658d2b074fdc906267b6cf6e361ba17669c4214be8f67d9e0130eef08a0fada6e32a9004fdb5c012d666e4d0768fb6058cc68d8b21555bfb6ab9a3fb25db3915706949508fe5e0db36ce4b75fa97bdc10df5e534dc84f6270fc7ee8eb5c887a4f1c77c984f3850d82ef38d71539d1ee9664b89294f1a28f56f9cfc1c0b1a76bc19bcf2307affb00923a56c5f998ec88f6d24148c1d64815d01171b9d02941428c43a646663a2f0c -generate_ring_signature 3897f3826263809665c0f79b257b70f91ae560f59533df45c79aa481f14ba6aa ed20816021e0870adcae45b087cb64978c27a36fb94967dcddeba7878c43065a 6 1f644846ce53a77e39b3e5ccc0c578bba7b854b801815d27e4a279b929752d66 9370f055164552e621e0ea348794778e844013451447dac7420ba92789bf23a1 58d1f500efc8f928bae7a56d15ffc8b07b65139f4578ec2ee706ccc992d46cf7 07c2dfc3e82dd1625eaf9edff3a2fbc75e6e641a41a2ba56ed0c939d2b81768e c2156a0f6cb0f9d9c49abab8124b36784d2f0c676c9af313d1215357e5203e4d 016c41e09ae240dccf9ec65d33ee94ccc74845a9c8c233c2a2bfa220be6dd093 2753960b51aa5cef3e1fe2f7766429bf0bf3cd91268e609c44e5e1779005d20c 2 6306223916f582ab76de2c92fe78bacd5c14b30236357da5a6bb84625f54bd045105ecc02f042cab9e6cb046b53d112a14f30124d5ed52fa01c0cee9ed631c0c7995eda8197d0d455426a53a8ed3306639d65de72d66987a3a4452765c47180c98c42d7515795c2ffad166d926e6701ee8b57c4be90485e0d6fe0a59ed2666079a7fda1e6c4699320c3779013b931b4f2291bab0a30f753c2122df43d101420580dec454456e88465721a55682ff06fd31f14d41287046c42e07175d31b0b30a74ae2bba1aa26ec6cb3bdead6909dcda77685e0a3f8f63e551a2957b526452037153125a0280346e47a006b5ed111400250487eb0af477ec12a23e6385d61e052a13f6f0433ec4bfce14d3dfae30f40296b303c65ffbd3e5fdbefb2d54b58902298fa13bc4f971f513c9b47377e90fc4b7ba6e8df49be1a0d219f59c6de2f80f53dc3408a1ccc0711bbf72fcea1f8c7e90201a5278202419e3b67e144c2abf049c072625736a93ed4a6d4c6e6893dcfccad7bee84f5975044311d62741825407 -generate_ring_signature deecd93de3b6635a3900d9205d9a0445035451960faea63acfb11ab858265584 1157cad423a531df089ff8735908f4cd8d8804ca6f796839239c18df3adb224d 1 888be7fe0293f3f66e8d0bf3606153505f274fb692431e258bea9ca2d140d85b f8dcafb41c048b60f3119e9537c0e2d44cb10b1e6734aae324e473769a63870a 0 1f1b297781faccda63ba80636cf5ed88bc034e7fffd6499d03ca466a57df790a01beb9ed1f28d112c8c96d7e5b2302e919229b73432fc95e9fc702c51648ab0f -generate_ring_signature 81b47dad14107e991eabca238e4b65e7367928f92b795b2f3a2508c79608dc19 c05b11bf0e1c3e0f47038d6aa49ab4daddab874de20e0886bd6f6c578ff6db01 2 b7f3a03806831c048c28fa2ae2f5fcac0e6d4a76cf9fbccdf2ba1d0e3229b35e 6638087a63c6d537b87a22004cf2364162a52246a68162872931a6c0c35aa5ba 75c26eb3a2b4a277a7cd508b08879524621a628ed9ba713b276161ceccd2ba07 1 35838fcb286004c6277ab807af82dfe30a8e3fe7cd38fa9f1bd2925c01d3e7008be43a7a3a0c944bfc0a08a42f359692d4b4f8ba67b66c95cedc96ca4c10220a07c86684d888ea87a5cd5f6e45452c849cb9f5e1f09458b5d4366222df101c059cf27928d763ef237d16b9fe68a66dac15c5361f2acb393c851ccd70ab2fe101 -generate_ring_signature 27f39c66f6a61282ebe04d3c6116a57e4133798f01a738294eb6a52c813585a6 a9750add17565f877b2b22823ddcc5ee4b89f41c728972523d61a69b78e9088d 1 b02c8a057b5cea1652d1a4db956781e6ce07e6f86e747ce176a7eefa3d4d652f 8ea9d363e3c1ed113f8b6ae7672511441cad4877336596e940db05efd1e9ec0a 0 4dfab61c6d1dc5e889daeae42edbae805d0c88a4d3bea4b2d76808fa19a6a207c87636c6757b49d1808696f59db75893ad56b72f6580a348dde280151631c403 -generate_ring_signature 5c37b80efb792c48e4c99395fb19626df810aae0e617e587415938611e35b37f 952470262e4fc8ea399640c866cef6d408edeb219511cd4eca7006622386f661 14 84cbe808524c622505b5e67783989b593fee2b939b5932b8c3fd77bad3b4647d 60f3e7f2287a85996d5aac23f440ea64c6354fcb1f7013a5b25b081f2dbd991b 3f4a81047e7b9b1b8d642f8586c6f98bc20baad33f75a7504afa7c109b0fe612 40d1ab12988d9c1766f01559399c0e0c3451da872781825364a1c42b46760ff6 549564baaec6c44ad533e9f01a4a133f2ba260da2127a78ce876c30e97c6cd58 82961348f57fc20143b0d6b30e6bafc63b8575bd0a55a28df76af8629cdccd00 119798a0eb51777954ca108539337f40554d4b2df47a4d21de97e135b66b9dd7 ed90cf28e5407fab6b8d8fb70a98d9fd064cc0beb47c61ac94845dcdeca4eceb d4b35cec0f367a4e133cb2c6d4e63c07ee92247ea1033cce6bfdc7a67474e224 d37a52f026a589b51064eeeb64a14aea05420c2bc70d5a04ab1d5128819ad3b0 968604beed7f12f1058ac5312bca68c811064117b189e0e4333db93e603c8be6 b3793cc7a8a5daa5f02ca4496390157df25213562e7d4fcecca72e9a23119e71 9ecb2e012c207c1d09124479bdc8ba9fc10cba28ee51301b5c7eaf9ef68fa355 011a63f6c9050800a210f1015674fa555ae80133dbea7e709dffcefce488f691 ee5d09e9d9167824d3ae704ba8c5b4d2236f69da9002c28cc0faa08954caf708 8 7552e76d6acb53106546b0c0f5b16b8604711f2f94c850ef43d87a91d221d40002baee91bebc7142bb44d6eff871b685720da92e504fa7cfdff2b688e026a00cf3092cdfbd22aa77f442601939c822b5a211b80d9bec600b03627458f69dc80b5c932425d1ab76be1d60e2235876b211ed6a1bd19c84fd543f2887b3f31d3d09096377564ba17be5d7a1ff315528bc4a310ba5b790d61d70372f4b8ddd2d3208dd1228c63c1f9d4c217be9cb50147e507c15ad3ee29eee89bfab26fcd61ee106dba860a678f5352ebf1d9da6113766eb2432b2fbfb26022b659046c08dc1f3087b265d7f3566f0e2067f207522069c83ce7e16a3b746ed8f789ad9030a714701c5b0ea2575b6708a67351da7ba690dbf86c19a4194c025dc7cdf0164de1d430db29dd64913e13c0cdded960ab5e31d2255a83c6895682f0eb809a519484136080dc86e26d9c63d9ffe3a3bbf1d5e60749b58563db5c5c891505c449ef398ea0233dae625c67d0886198db0f456526fc42bc8194a511c3e18249ecbb12b789f09a8eee5222720dcb4b995905b0868b4cdf02a0debc7eca6602e8645f664594301706e7a237f9bb7202c4663a422bae1dc7e9efbd5b73ef2fd5500718b10da2f006db9003b59fa88736c2068df1401b6a35ae4ea315e9225811baee801c2994f09945cfb581cf43095258d0aa56f1b2c8e3587353a152f75fc587d8cb49aec0e0ad1d91a788dfd44b1b4bcaabf5ea2a3ad624e5a7edba26c1e9e7a9d196b2d6f04ddb4a260466ccff2b9b1af5f02c8bdb4517a2ede9ac52e0ddbdc30c9bea4b7047f390f9e26f07e2bbdda3b4116ec064bd9ad2389230b81576757f630977c5c0566a6f903bc4ffaf88e4991a9aa38f004162421cb7ffe2c8bda2649eca2aa770c58c43c2001241000b2fa189589d721b64a1f8a77b655bb2263d73aff17412e0dc17f72962927b0482168f32fb4e982ffd7c4850e7d44b0c0ed0722300cc61d02b7b3daf5564d15e43c2c6264814a05aec13317eab65d64b91ff6982e5ebb7a076ffab8882e0417cf221c37a19410e671b260e214344e8bc54d75900d4720a40a57921bf11e12bee2a4147db8c2892d462cc7e73118f52c5e6bf3b69ece95b00836305f78e29e2b1b1c61a1508b16ccc38aa42cc013c7107e7aa0f8496bc0cc0cbd872a5ec4e43b7954468eada591635978bd28e312c9d5c2ced6d6a7de80160528d7642041b37068f15b770a53f61f9c0be8bddcd0cb7382df8903c9e98acf09 -generate_ring_signature c5541e41e2a344f29ecadb669ec32474ec245ef6b2b5f06717612477a8bda42d 62feedb1dd4c876f4e95c2733a958adadf85d278f946e4c82f222a7711ca10ec 125 2804aafd5a6c29ddff9968bc1d6cdb3ab4d346e83d219fc96abcc5729f6b703a 77521a9631c275477f4163fea03d7c3fd704fb7b5efd379320e005840539af0c ff24636208c5422a9c83e3e8de964ed134553e54fb7e8205d2e77b325c66ecd0 f21c08aebe88ad781e69ab0026121fd5f134720075db02bc436db5b6ce9b459e 0535f689f85a203f3612be098e8fa8f06ef477af3395c6a116bf9e3280f57975 ee82c0276beaf45e6efc7ce81696bfff42f557029ecd81e0df7887b7dca4b0d9 f760789ca02a9fbad11532b7cda06619a32e2e6e66973039f4f843a1cfcfb906 f41125c6fec4783f9578aa8fe9fbaad0dfa5612a829c9d5b055c778f404a120a 0e9f1968af9a915ce9c88c0ee9a1c42bc4d6f606262509404362a80feba773a8 43d5caeaae639a65004bcf71222e8ca2d3b2851b152dbf7bb89aca28c456e63f 16a37a71f407d8ed2bd161cb9d3f45a9f2801ecbe67ccfddcaa31cede4cf986d ef3ad750bcb7779904bd2bcb5561d505c1c1891d8c78e46e94cf1d63a0fadcb6 746178b33c58f06a6685b550b789d398862762f14ba9a3f8331b2ba82e971322 ef470ff38a5ef2fe7edfc030a77d086100965fae7a341afd095867a411eb2745 e8d43a2b8c03e0eb1d64109b1bae138ee4578dd584480fbf9becd4e63ddc3ef2 f466151692eaca15a47af224b8b220c1f71c51a78b581b1f12695947c790d09c 0b064774a419a807e737a7ef8ff52dac16c372448243428c321c024aac33078b c4763dbe8d39b3f2eadfd70260acef19c336a1fd80a89d68bf8d5564c467ccfd 141ba183ad8dbde5e67f98828c4adc018d53db2cae83c34e3be314bacf438591 41927644356a7633faefd0bea5fdbd25bae1e720819d2fe79500a950e3fe6d98 90d8b56157bc869813104b12784c4e88e908186e10435d50add8746552f89c08 9f3eef380682bb8b6e0998bf7e3391992492183d624b829ed3340c34a6affa7a 87397030797108c58eb9502a52a18973db5700f8bdd610dadbc80aa3b6c66e59 2c5d5a0d6bbeffb62f288ee87e5762d6fc9074bf787d5df4dcde7a49e6efd1bf 8b2c1a8dd258009d5751dfabecc24fa5935f0d252871dd66d293ad0185915a5b b1ed2a53a023134c2023a77c7725817a9205f831864acbca58f9c79d417bea85 7f55eb07859de3dfc0a8e206d1d1cf4a0342488987a6d2c18f41e97df0df014e 5188fac915e9e03a83302e4ae128f1dbe63b89767b91a4ff3b9295c30dd3bdd7 10ff7af1f258091927149854228a5c7d5ff271bf4bda55a9ff34d1ba7675c509 38e38959c1ebde70dff0a5bf2257a13281b11d432bf7f51d3737c500c36f7f2c a19df45899c18cc81122cbad000567f8fcda8e789293f9b77b5845c03892fc4a 4c33aed9b6b8ac36afa9892e7183675ae5490d21449940b207aa6466cc1e97c7 4292a31b6bed65a201ee83b71915c238b72072ee439b25a2cd496d2cbba85250 77891645ef71973768d5086c5e666eee6cd22c18c9c027277154c294e58c1107 761de841439e26204b01290f022bb014db8df120501179be8f3764d7c19fd8f5 9e711d6b3abaf3a78048a9e1276e6d70aa4ba0df9673d7b7cd653494125e585f f9ea96c8410b15246095f15b1a080578a3d2e4a42c9ca7ac41d713d69c674237 13b6fcf0056f59eede98eadae5e9308012083fe4986c744e1fd93db114c389c2 1ab06de0e3067113d4ecc48c5321fd6928e8f30c0b4c073e21a629f794c930e7 f2ab29c1b2669413a71b096379c3fc8b21889ba95518e3fc576215fce0033592 9463f8f4a96d456025108c444132060d710a6895da60ba9ed29cb0e7fcd222e8 f17341125a334c6f5ba759ad278014e62bab511f9c7fa45890f99a07463d5299 0cc4709fefdfb59de0bd4bce0f3833a2e3e9bcfe56ea4469f9c1bd9e219ad662 cf4a202bf367be4e41350c1b3a49a90f1faaf925a73af48224b02d8679577d84 844e462cb86c7906ba82a2cc5eb86828fb9bfc790abd59c2cf54ca6c0524b6f7 265cb58a1b994444fd2a9ec13e22f1e219ee2910b87bcc809e592d4597cda731 eab37e95ed3dcd62f3ef9d98ded850d36c437ae0f7c4d360eb9bc0006567dbf2 a9bc3a316ee0f0dd1245d33aa0f2b0a65dab787db3b093b583c1d183cdd87388 c593bcdf8162435ede45858a3f82d18b6e08ec9917574bb630b019885eadc15b 519bd964446b95b440fd2832a2bdafa6fe9dd41f91bf9c190827ecd014817149 3c97c72e64b47cf79222f987d41a9f95959b05f60aaa714ecdb375f6bea2f24c d0799a853482f9870120c87d82efe93f6ba672e361244a1308f5a918f8026204 3fbcbe6e8afab9f595dfc5d42092b46ced57cf4a77a7732010e5435df18bcb35 e7058fd902caf725e1937654bcf210f8a08bbcf4c174127a19bd013a4ed13c5a 3538cc278efd09cfcd347eec14091864878040351f128a36f17f8eb1bc1bca93 d023d792c2ecf5e174d78de4f4eb1c972f05fdc31510ddf285ddac03edb6e9a8 e3bbc9156f2d1efe4b0e5bf24370772956146201f29458902f299e7b178e2926 5ce05d70587d6f961dadb852cbf573da00d3575902e5a111204c29c1c1995524 13d6a79c71d331cc44369af63eebe06208038fc18a35d65a9bcddfeac86799d3 f0c18a55b75b0e8b8e34b5efe0d3227be9898fbaf8b270b82a347c477442331e 6d941ae5057bd14443053028bcc66e3df3ef8002ed4889722a619d27af42c4c0 65d401e25158b5d536010e71ddd6face36fa59159c7147f57f98dbabd6c03e3b 236e3b8f14794886df4b2e6cfbe93af86a369ed36ac7a163855fc84a917d132b 2e731fad62c827847155d6b543e91656f33a37f29802b99c74b5ac3113bcd3d1 8bb40fa5f58d155cb5875f14fd8f540b6c5dd7fb230707cfa3914d8bb76a9985 b741f627de1025788557446cd8571f965165f3b87ba70bf9593d2543060b1dce 40c2cddadde1a33d787f42ff20b897470f6c49a2d1cd8fd9c9aabd4ed20d8098 b9b5944a94b1024f7f03d858245bc7085f2bf8c9b559040404b2a8f1f5668353 229f8995a931a23752d3617ba4c06a74e48a6f3c8bfd40aaeacfa2fcbc6ac031 c97156ee1235c56564ba3bcfb12c645e5eef9bcfa4173c3f7efa755646a3999c 207cdf552b7744e8e68e4721ee56795e2ea0d00db61156784b351b07d91d9b16 cde0f0dc90d52b59a17d9f220a8993787153aa1b2dfb30074633219be62ed6ca 2f8add5d450bd257117c2c40f33b50dd4a85ca2946b10217344e842ad29a3f57 198859b069a1c48e96f6420b89dc69cbfff32867574717ff02965e9ca9aaa9f6 da1c7cfc3af582887f4bf517548956f949e1a83a8b82a5c8a396df15e1ad1fc8 a0e5719a9ffc23af748a8e062ce1ffb8c82da607c2ee26993b0e76c01c1951a3 05776203a11284fcd79016b18d7b4544d10b9ec88a861baaa3b980ded424da0f fcf8075b1a91c94ec4b6f8412da739dd74d749a3cc55e9574e0b9d982d21ef43 d567383f5e2709fd6b640dad0915a229e62d8e44880cbc20b71637ef6c883cc2 8c1c065394f4585c0244671370ec3aa9f31c73c109f50ef0290e82b41730f3d4 a6def36e17f8f1bf3225f801454d57b2f8792adc94b1933fa01af88fe8e967ea f61371c2ab70bc8012c87fcd14f12d7ab1a2eb51875c764acfdfe3a2b3f69487 aa0303635376c96892e2e2f609d626b6dcca8b19879f5add956927ecfb6112ec 327a716cbd42649a2f59d3fcaa59cdd2049e94a96bcf21d77249246ffeacabe8 50123f75a9558eb33656757408e2ab0250269a03f9ec1dfa8386b38ffda39f6c e38b6d66bb8d6be42b76fd58aa01f118da2cd9249adf097f1b3b4f46a54d7ac4 108e7f9b934c25bc12d2fbb18031bb211dc7cdf996f111d0a2e56d6cb9c6a9dd 5074acee3dc35437abbeb588f5d942f968d449823b29942c21c79a16f05e65b0 6038f6d5324fa1e378010bf8ce3bbe897d5165b764396e25c243db6906951f3f 969d74fcf9d04f95b465ca2b88e50a79c678cbb3d1e1cfda6002379972eb676f deebec572142847258635858b17708930be57b82404f14d028005099d670675a cc8970b85874d414e741607ab7a9692512cd3108c0622c89128101fde33eaf83 ddc7f19c8d9a83c0c32191ca81a3430383c6de882741218a4150c9779559cae4 2270ff3129579cbaa7f986cf738785ce9fbfe70ad863cb39de9418905775758a 6e2a1ad2fa32e93d2dca5ba62643116e6b0788fcda52a9dd74c656e358370580 0d7b0a579493f836a031ba1a45411f36e4174ce06998bd231bb22e4a61aa4340 eb7759d06fe9444869c5fbfb6276a97ca4c8fa577030618ee50c7684900237da 799b5c7306c3f2882eade612689f64b84e03fd984f229b2bad363e9d22b33895 271ece66610fdafde9ca4766cf6e9cbec05386e840d0b161e5508b1a21d025fc 74528f7ec8243c31361f0c20b8fbdc3001c7af4a03e156d907048fa5b02a939a 5cc57310e3f8c9b79dba293353aebb162d7bac8535ac93366bea3d4ba7421dac d0440a843f298088834e32c073f27400c239423b5964cd4cedfccfccb0dbdf7e 61b3bd3533f547cf1c95755751fed8295be21372be495dbd6adefc160bd8e3a8 0a62af61f1c5b60e5d8e0a73ce851c3118b3a33b3e98248508ffc21c12e1aa30 a2bd0986f7ba0a3e2acd06c9e5bdefd96e901ba630b261ab1dce6ba18628513b 87a2209b6183ebbf569c618925fa782208943c0c4ba3be4ccd1dbbfe4fbefa80 c929225b641df9a42614b29645ce066050299b51fa4f76e4d5badfa50f62faf6 02b21e01e28dd2f197fbd265b82c6febf31d13c357e2e524fe770938786c0898 e224e1d91b7f496640c10c658c983faab5626f2ef388c8573262a5c2b803d737 f7648874f6126260ba9b85d3dd99419ebd95a187a00f113efa1cd8e26e24e66f 70d018da92f36ced31cc9298b8be6e9b7d1873b3443c604e0911be4cd6bdb190 0f7966f0a07e18ff4715fb0d0cd23d99c8c6aefb0295670e86eba3100458c466 327b0f4c5a5d34c9e69b5f3c2d9e32191ea616f31afe0cbad2db717b29cc4e15 e6c620872b328204fc60016bfd6ffcd5f475868e9eafdad2f90fbe8fb604653e 367bc5e3d53ecfb280d5bcdd33d37c3995a9ea2f4488f20b0bcb8bfd84b9e655 b826533c6aeee95b7f8aa6b9bafd6604065aa3f048e55acddee13898e7f9d41f e7a4ad5121e7e60dca0ebf2f49de2f19f622ac3bfccaf7c59d548e08f5176156 7b9802431d797c61656841f32129f8a99e23f625013df856eead6a0eb63709d3 6a7337412e7935d128dc296c0e35be814127f25ffeb42b64ec0dbd294985ef11 2ad88ddc9f00c78f253edf8a26757c38f2ad59cbfe69ce6cc0cb6577dc863782 d8e3961134ba49851ed743543d66a85dae08fa786e859ced4d4f1e9eae6c148b 157eab6090d78b4383f756450430cb3a4a993ba3f19e73d04fde2425aeea0703 614ac9e9b8f848877e3ee8e24d1b4c597f6afc2e518175b02b209a69c0a565c0 0a4d2290321b8aeace3965cf602c32739a0beeaa91567df3ca40efa915973447 8ef0ef41d7e977f0c0f2b955e8868d331a2cc5e1aea3cc014a64d96f1653b80f 6f8dbbfadd4f875bd3fb7860e6b522be87b40a36a9aeffb7d8de0115f2047b0b 66 000e6d3dc172fcb203fc77afcfb4ff5a4f2f7833e86c8c9e8246d4329b0dca0c8666df50301f726824ae476c894f607b986c16b9cc42da70014410a1ff99260d4f511252411e3b91b1f54192bf5da2ab56b96c5f08da3b6b46992b20b8f1ec04d60347a252012c3b1565a3209dddc9f77a2aa2c0bcebbcb0d4647ea57b739a00eb9bb291c7217c303780dd8051689e707424b238cfc24af046324f0756a5c90c539762ddf46a7f90eac285816cd76e0d67a1a3e000796bcfcc047e39f9e4860bf97772f51673599321bf5ae22669ce5f29d3d394e726b86ad3c737b238724d0fe7740688be5254f397b59650f8c1dffdff9a206cc76cb1ed1399277f5cbf9b0da2b634053937609c7f1a7a9ceb4696ed3095c041ed939a3cb2a13444dbfc200719113d3af2a2a7437a4619c5be19d3c5c99a8dd5dc687130032e342633606c05b32d8bdb214a5502e25ba0bb17b9d21222fe28608950c85e663bfa9f17db1907b6e1be14794d6fb102ff7e421097fa01450d76bdd25c7e70f25ae192e72e180a5097da2d7e3c02c8e0570c351c3d33c01fd95dc400558e22bbeaf8a042c8e40f65ef7fb2250400d9f6688e305ecd1b78ec56aaeed13be247e31dfed28b45ba0ff1fc93d00897c167299db889e18ffbfeda0a61a6e76204a4801cb4240bf62c0926f3d77e1120e739a1ad52541b7f7466bf1287bf0d01edd6d4553bec59790c01f42b4fc482537537450e967f83907f2660712e060c196845989da75343bb5d0e6a2aef1d0159ab2e29e0aef953f53e033f7e121a9e7f80386d94b06e76904a0727e011bcc959de3e46db77eb52b1e3de93eb9b7821e0a62e4d37c353554d440165562275e8c25018a4e34afad285de62dc60324b7b8c1de48cd3df64289cb202ba40759bdf60201caa2355372022a4a7798f64f21028854316ccb2e681162c0be9efc12c59a8e8400d32cd4f250ae72db272b0142b964d14d4ae5a7ff138f90008761cdbeb86651c4d23b23bc1b5cab9320880ec8ed205ecdc11f9b581dcaa07f14bdb4ecdaf28d1bc426143c6aa29ec9d767c20955059114774a6de204ab5000436c488490c79a5bd7adcb5e0c035ca124a0f0f4d3fcffabea17f5da59a6001c058f4d9cb7cfdd3b1eb932e3d513f24e3358e51e803aad15f291c4c1c39c70585f6afcedf53152b33cbdb852d9c0359828f5b5fd0df767cf2c5fef16ea761056e71046e46351c9f22cfbc9299433f2ecdb0ea7d860ba5422b3ee6dec9c72a0c97faf66e3d3ca4d4c55853c4b3e07f86ebcd9f94eea1b8f699282ce450871404132f8fa81f30f8f05a3940227e84bcbb35530f33e46b8ce7f01e91c227683c0a40ffeec7b6cce32e1c1d38a2df88ab66e33fed91a02b0389c2827821c9ac2e0f0bd0e1787b2e35930ad19da65d5179b32a3357583a4d86804adc01eccf5e690eed9e068aec35482d8dd2286dbe4287b705d76f973d97b921e19442a4240c94035c5429d5818ccb36efeeec7290d2ab229874558987b824e8efc469d1ce4fcb0eba3ec391bedfc1062a68404c8a22b00e3dd18f9b3e6577b5bc9715a0ff18410872615b1c3531b1565241973914a7ad18db92545863918a70f50869d9d44cc505467bb1f1fcc6dbd40e14531bb3e528d6efce29945ccad0aee92f72254cc92002f381851a110d6ad30df65a1b723a0664d8f2c2bb2882000d343dd9a0591c8204c8ef4382bce5826326b772a247d84c942453c02d76d3dfb4d0455cd6245a6c0aabb0238044cdfd28b64e4223e6c54f04baf11e7ef6d1a25871a5c5b1e878a503ecb8bb2529d4bba0cd29d3d73be4181294d5719ba1d6cd4de5ffd5cc8165560daa8f0f2e81a06d5bf1f69ef1c7271ee594edf59149671c5d7de84c6d5f4535022e84ac48aa63b309f0613c8eab660731ae7aa6f4a68ca97495c96caf7c296c0b9093b97fa4995c2b42fc65c786cd5c7a43fd0fc34af06aafba2919611d3ac00fe95586514b2435e50e401337ec635f547279246cc0780f0054cc8ec38f7a5f09ea47e4b9aead9fe593d2015ca72466ccfcf0eea9a82ec04ea004f93b8a945404af2b76b738c1c79323e3326b28b851b9ebad3aefefd1eba7844cf00494ef7c04fdd277b358f4b3f16e0280cb3831d07c61339b910118c1bb74915f6c190e68022516e3872cde090a2539efe0dade8f637dbc578887d4c0e020a1f80469c5a60606b26d4b2827d81a961be371afec14b7ebd62afa6cd16df685e29de8ed51ba07c9598168d601365ae316abe963a5616abba678e2311dc63824f9c332224e5e05ad5461d9210cb3a769c82223fc2ee0b277218bb04f22b4f2e415ee1df9f7f6026ef9a378018deffe81691cbaeb59896c1a78da3f9ad2d893d1b6abaa0cec7d05de7a35d89f3bbe526ed5f20fc5f3ab53eae2aee7aa0201dfaa563c8625cfb604b67940e550ae1b4ff72095d80ff178223717ce340bc0961843c64a0176cba90a62a5e90c98dd4c5f638e96f77cf17053b486230e33b283f36a9af21813db610d601bf70edfb36ce020fa1632b0de73428292de3a6d74163fde96dc3c426d4205a9481e1a3731b7b92464c11b08d8ac3cdba39323b1a5752ab13ada82ebf22301a7040c1b4a2748b9ed9d78110756a8bc2855e50ccb709da15d7c354298eb26096554c2947677980157bb3d5a420366cf7e7a889694b6a71e455bf7f29109b0012773a48b0da0874863cb0f3a29e32f5fcf72ff6465c9357386e402c653767d03f57a51d3c6bcbbb9952c31bc692b13696781d41b7acf21410dac693d7d96b60300fe086eb26765af0c3f3df66b1e12707b0455fd54b15774fbe852ac7676f707eb250042cfb3f0aeda9dda93b6303f9d71e0d7a003184108cc4ed55979fb7e0980f158acaeb565394c0d3b929a2eac26e9ca79fce8bced33b47994c74c572900ba11df2b119243413af75935ca007d5ec348dea55236ad7533c4e16b9d26a6066760c15a397780eec95951ffaeb333556da1c8bd3f32a13ee1c4b91cd7c9bf0c825e2ae5f11b4a972efc8a91adfab5f3be1668962ab4453e1872d34694dfbe0517065569d8d66a976c765df244386746e09ba4c3d97bce8a13f64f228379cb0f35cea8c2ed5297d01e5ae04e56afa4a05204e47b23b40eddac16ebd7c61f2b06c0059bd19abb86788bef3b9e5fabc6ab93b28c660adf2ccddeb21837262a9b0047416cc747657af3e5acf1c2bd511027df1aa739abaf966b1604675f8fd943060f3ee7f27487048595116c057c8552d34224921cc4acf0fd61ea16196b8c150beac3a99e1a97e8d116387b2c22e52578e8f1890b53c3c4df9d3af727cb17d200eaf1962636f4141b1837f8892b808e77706906a63692f612a2534997c74d4d035a115ea96c7c777d499ba17bf82e4dc590743d6a2a1d706c5b3387d641d41a092449b7432028733eb864134d7534eb0ccbb8c736aaeaff3127c220c57e01040bfbf5e4ec60313bc25ad53484fb58d4f361f618661d00a0f8b44e1da391e4d90216cb5009e1692c65ad6c2d4c40b68bd70515363fe718e79ccde419ec66f2cb074dbc474f6d9362bca07a975509667d83bbee3e8967a547ea74f1c1b3f2c97f02e8f2b60e228aabbacbb5ad657c001f041d080ef2b6d4335397174dd601a40500e0f0c711cd750d9c70e7bbd3d27251a9e7e01e1c9a30ed4a55d1f7bc791e170eaeb63e22fa393ee6278d69707f5fb3018d51f43d8b4766a9f569a025f8e02c06ed2ce8eef86a025cde257199619ad743b7d461b8eb0b2e99e6feb3e4065e6106515fb301aa0a59b684af8051fdb83462bce21c37b5068373e963b20ba3a65102e12a99f394ae06f57b875969b91f71dcd3f17057322ed3ca6e9f3e901a84440c0326872fdb58fc649ae4b6fb685d3a8e37588be8cda1faa54b32efe3ace0cc06535cb8ca57cfd332df1307c0967e8c86aa59b184e12e6120b8034d499165a10bc6d9146d73f147d7c406433ce30ba4d7b47c4e6fef9412af75f6f06ba359660a5cd550e665cbccd650278ef66639ffdfb6f4a43238fe6ff9311db03aff4dd7084e2ab786cf2561521d24ee1bb8a08f274b8ecefd687d04f6a32dd5d1f8beb008ca6365c10fb45079d072f110a65fae76771b471c1d78431cdb6f3b398de4d60bd39e84792f39839f5f84f06bedac1dbad2b8279a2c43ba2abc2efa39dbe63d084eb08cc67e2502330d9ddb845e4b0411c0cb0a9e5dbf0278d01070e85f2f6300f0c396acce8f4f44dce7d079dab154972aa4a0a47548d1d2ecf1358c6fd9400fd1015cfc9d4d31e7e537093d0ea200547974fc5760eab7e10b4b8e5848eecf0cc8a48e27b1caf583240ea88ab0eb00cf01c4318839abff4f1806ef75ce353b03b8484fbe065e820d0b8cf2468a0d6799b9458a61a704d9a2487892d083eefd05af82d17627fd25efb517b3c9094397c678167da5f8812d85953373f9fa336e02c8a314230751e1ac978bbd5e89d67db457fda9818c9086cd74e6e13cecaa0b0bcfbab5ccf240cb7dbbcc2c5029bb7b51e1ef8aa569328f09cf82395ec7b33d0fb91eccd4bbe52cf65b0d425e67fabea5f975529a9fda4ff304a7fb1c9272e70b6f529ebcc60a9b0fca4bf1b954248ea8662d0ce23112b575c02472fa9c94390a68972f339f29a9b85d756ae943e22801d91cf2ba888392823cb1252176bfbc05d7c9d5fee84072863e8d1141e00e30a1ee316fca29a6852b78cf1961a0b61b0fc4fd764fe5a4907e794fef30f570a27405a8dc284ab3533749940eb09f44d40a13d439b5d97612b9d3aa84833129274a1a5452b073936d74c30971279dd6460ead914034e9f37c4bf8ce943de9de37094dac0ed1c020af4f9df2f59e58046b08cc6308f7a60662067f617c5ef217f88504557fc1a4286195e79f5e0e8b2524095524b429803ad387c24f71f48f140aa59b6d26532d0b862c45f1d0f02c75440180d043aa43a44dd16891ec8b42f4e59f9595c0e6e53eaaf1418acbd29f5ef0072a8696fffcf59864d3ff2f474229f7e0eb550462d9b09bef2ff62edfc75290013d3b1fb5c8da637dc2fab23f9b625c106c05697c9788da2cd8e01faa9db7c10d48df12f704fece8815dc16504cfd72dc18b0a6a3ab2ffc3ad4ea9ea5c8b74101b14b82cdf4e79615e3dd5bf88b898be65c5c99e9ee5473b439fe7435443438046b842505923c5897b356d80575c1094a479813628c9531ff0806fa9f9471bf01382ac6cdae7cd422dc3b159ab421579f2535c9f51c032fb3eaf0f81fac8b4c076063e756cb02ac907fbed336341b44c5d81295dad1ba89a8d8660c9b2e20a3005b215ba053aaf82e8350e014a87150b0bf62bb46c9a1386585a8dbe089252e025df605198e68ec65def26639052056a080f65358bf7b86d36547655ad9cb35045450bdce7088ff0c7fce1001f2a984cc4d295348fb823206e01fc44539a18e0c9d73c831f9beb0be6a57aede7d289cb4bc6bb0cd3edb5d1eb9e78c99fe895b0f6dbf7683a52fc62d02706db839518953984e006e478380797a6e02a0dc71130cc2cb376c947667bc27b526d7a770a7f6f6544681d344c8cc2d229e537b02bf05e55fc71f3d308d5ff356d967eb652bf4d2b6a7bbc2179d3ddd1b7b7b7a0e8c04908c3238ed1d771735e01938376c40dc912acea7e0875c70d2890443f899a20550d802f1a1521d1003e8e6979d22d641b0ec3108068fe60c850d454a9984e3071d0f3070c82d288e78cb48875fe85d2487c4e10e658525e0265a95e6c249150dc801a4a150d888b630c5efd4bf2d7d0268a7cd25119c281f724392dcd8ef100b2f02dbc2b00fb56e6119da21594e5bd350137e66cdcaee36abdb9a56a20fdc00f2a8bc36ea687afad50b41876ef9e635ae380d2f852356d6720d5d1e304e68092c932d2e7ded08fb225b494881166f47d27f98f50270fc73905a9e99a870b801a21bd221a4f151e23b7c3d96aa9f0fcf7da1604c9fa7ff2f0650519de1a78e0b3e75a40ecb00b775a7b12ae43c19fd07b60afa599d0931ee9eb55374ce949f0492a320e07edc37f2342d41f0ee7694532b229047a540b59c77484181baf53d06d265e57ab01f1f513c8806c335857e7c57f93ead6901ff384b78615d7ac6870f15e8234b094b405a65ed9ff865f0989c0f003c1dc6b3cb5f1f6db4fc5c73be027cc308e98f8818d19b27033705358d2d8577e49bbfdc2479709fc30698d5f70623afc3f059160a3423e46053bad8f299dae379c1cd1557db30baa8fe5b8f1909205c0f9a6b96bf2e95803de309da2aa772b296835dcaaa1d1105413e87ade0072cdbf38f578284acec09f8929307daa33485bde7be120285fabb4673fd616609a4a0cb596bbbad907f6d52a0e7e535c950d8678dd123b9b504a63349540749088646cb592dd694b817e6797a33e02d4692844be969c369dccda18ab537c1660c90ffc27543e59985a2652f271c535c24b134b15433cd024bdb0e4ba3267cb80646d84a3364aada1146c7d544bc1e63a7024dda6e528a44ee0eb9646c642bfb05ce3aa10e648a6a95e5deca0e94f79d9fdd0ec47b6d84cda43fe36aefa9c0240856ca2d6ba309459089e7603f449283a25879351d8fa34900163b97f36a7e420d88b422aa78d449617bca5853d4cf51c68f36d34dcce03247787316cc12f1860416c6234f60e4be6f7c689dd2da4e5d364e6cbd7496b539f5a0091fa08149910c46059269d67601348b7668188e111ac46185b0361b9c67f59ce865ac08319f09acdfb7ef092c52e5183a1c8396e23233b1fdba13622586f4ef29d5822b68dc00470b05c9b4e221c29cf326247e52ba26b7cff0bd7295ea7fee2bba5fb571360a196a2bfd6a2f99abe91e0590f397231a757ed026604fa387e73791b383e23d0b9d3967c67f56c42314a1ea1b8a267076fe21045348f88eb1515f5595c4140b0e63e2a232553bbfa80f44800e952a1beac79582d2f16a71fa409f5d21a2dbf4010d5bcff3e05f5b48205e987ce4647e9a4fc7fedcc7d468f5a8f1f3a37a0c2e0ca4c28a46dec05658a40857afd2706e9c16b490bf715bca05f32d70f78a0fba0c9f8324f35f47be1ed7f7f117d280a7cec3c63cfc416f9427fa19693730afdd0fd4a59418343dfcd96d2c1f80b60ccb45c29132d54dfe1d8d15680173ad3a46013f5dfe36312cc3a695ac9fe367950ccc92f627e1f47e447c50b10d48c293520758fc774cc69fb063597f65b3baa203c5ac6a415cbd59e471310ca7f7b43dae060e68b256ccfcda9a735e90379e7eb5bc0a11615ccc21997a58b851de69e2be0a72ff93e056df81ce2d67c7985f79926ee4b61e5fe479ab0dc2ee579c825175015ce881fd9279a1ea79183910d3d37441647fc9c8dac921e7144388a0d0185e042d1aa0b080d8e2021254229e156d9d9791e79e13cc0e51a71481acc10e19a90aed0f4548200fa44c8f2f6bcf874eecacbafb3727c3430bd00b732c6de3ab9803b2f6e9122781e2bfd30d5a7fc1b91cf2b5e0d4bd50fbd7e681cec70c3719240926e75f0cbc0eed2b57b671b5691ca559e969bb472ea471a6d8a0844fb4e75d02e5e4792386880f7f0bc685eecca7240960b03d875c1f574f1f3362e107ad5a0c4d6dba2b2ee22e75bc3a3ab533f9a9e6a02ce97bddf036db7ace2b084a08fe0b12c935bbe5f05d007ac4671d96b885a2439b9b4c022532c19a12796611a6b30c2524866595a7ae16ac7bc5a577d42fc2d7f7311966c8b954b6d52237576e79074f87a15f5f30b024c603da0ad08acca32c9f95f0d6546a72b97a007719ded20034e45c56af15f362f7802d481f9488dc5e20b75e48fc38f0e100a9ad55f21e098813cb8ac18144fab035983a593a462612b9babf8c63a76a2b99644ea80169058a30592f9fb56daa42117396777a711403139131aafe6ef56cb818a76207d6070af5492a506c9fc2328dc11f3b23717cb5dba37ebd1211dc4707a9e1fdc72a06de58c65f2f71ae3f3a6291b350419b4a631a7761afffd3e49c95f6bf1c4b5e029ff7d928d8aa61745bf76d147cbd427fc1d4cbbb5904a4654aa50e7727165c0c124d52fc5e7a56e75b8c881450507cfeef3df650e814632c50a8cdde0466b6040fb03268ca3c633cdbcdf9fb70494193e8bed32e9538889df53433870eb0460dd660df91372fe286fd16057d458333666ccec0d062e5c001a1d768cf3ce073030f34713128d87a01ac0cb81debc527414b56136b412ab32608b7dde35fc9ad0d37a64cb6f09a3e9f7dcb1f1376aefdc5f81111ff28362e4bc183d368c18ad905afa509d787cf4d066e844276156e2b1b452f05ed3c0d9a7f1ea34e4f51d057036bc6e9a327f13ffcc9accd73235dec813c675be6962215f1f0f655da5b942004151a5a659d712425c1208c80297ee82839e7576737baac4a564c2fdf4b4d160c76af01d85d6d125cb866d47cc0babd0d19fbe4f14284619e44858fb8be67230ce94d4c5431c4be290f2a45d47e604f06286bf290cbccac8bb6da24d0296c5c00730186ef2b113c3a094cfe76acd5a39efd222082604288994fa9b38dd527830f097d53d9b6c7b6860aafe4122b5f596a22eda5657043a17eec3d7af42ac6ab00b2fe4c7fc430d4125f1a244a60971e80582c50efd2eb8a7649bab44607f7fd0dbf48882133d739a4ca3b3d7085af57c541813108205131a13c02b3dda77f350081e2de16eb3b8311bb7e39135d8d37f0d4fdf84431150e10e1dff924758bfb0f44cbc252a6b55a79d9540f60290d28073f1bc903d262abdbf58dbb10ff5f5a089bda5f5fac101e486df95374e814ec543cc4f7f65c35b0c6d3071983a2c63e083f8eb271f933c445672a7b79ac550fdc9b853de6140b64c031b6b2d700c2950905326de7aef8da518f628593c7528c7915f359b6f1b580372f39976eabe88a00ce818c70c861b520f88887c688d16b5e081ccde690e9129fc979dc3bf242000117d679cde32d8453dd1a2bd87dcc90216c0c47b2c88340351d924d411976a105d278778e3a82d6a8cf44cb0cd8e2d15b6ab1156cfc76ebcdf76818d21fa8dd058afe133708a881f3e9bd7d8e3cbfdb46945395c47ec534a8ac92d19809166b0e7f13f91f455bc7736766748ffd8ccd4786cd7736a113b414d9ab01d01bb881013e9bf9bf24726f19c4030d08e4357d38fc4dc4b21b4461a855a10f725827ac082d994c00ac97265704adb6d65ff336d5af2335d97b9e3bc1b3e204e60a90d30119e32cf602f7b8cbbccfb0e0a0b05288834f29152e578dfc39b8a493c2c1e4035b1606bc297128dcbaa2383a55992588505c23c6f5af48ec9d34bbe1d06a7103af9f3b24940d628c640a367694c68a00a4c24fe52d0c6228d95cbaef8349c10d54d1fcf3e01ced5ebf925f663ace49f4b3dd965527a55e5ed017880e09b05403b79df389b05a45aae5fa8e9623d0dfd38f5d6d25c075b128e295f273b2f4f40562fcba778a63a7fa810bb900ce5ddabfca6313ba17472abfe7180e417989ae00b3fff335dc5fb914bd40c6477b2363c5a9177689767718925fd6e320a13b7c0e1035984125a968d697c1e58c2515d64f6f3810609834a38f888454828270140b724c20e147a22c52e0c0fbfe00e306dcfc191b878bca1e787baa7502b429fc0a207f735e1b3d94c8ad62e5614a143fe3e8d2c62e4ac610900005ad26e0e2ac088dc03936b547307cb91bab37311e1637cf58ee68ad472ec49b36dc504150ba0454f312c38509e9cb8290fb40056ad47ed6c13fbbc4889ee69195bc7fbcc4f100eabd3104fed90bab7624a4c86f8bbf6c1379efd411d7c71f3c84c1f03ba6540d341901123dfe240bdabb3fcaf7bf81ff6f817ec20beeea9bf8ad666cb4a35d09c062a0385782bdca31d25f55be2e11fade4eec29614a34094b5199597c48ca04e17d4da2b6e4c7d94b8a35143e32a927cd482b85fe83856ed71832b9b7d00e093e31467a86b9f1fadd0f9b5880e3d866b7228aa03dbf1d2f977ed960153ba60113f974195af5081c24dd2bafd1ae944d3525ab0d09435f5d59681092fe88f901123c7480f745cd1d3751a8223fe58e8c82c476ec31e875cdd83ac102b688bf0d977ad9ca765d082b3e380b6e2da45c4754cb260a71b595baa4de9131b53f06035b3f844d07dd8cdce1e70bca3fb68e4470846b5f66a531c6af8e67e406a6f20a11047d6e459124cc6140ee6d2ddfbad23886d01c73a3971d0058a7abbffe9f003d350543d289d9deaf2a5df7ee8d162b8c9703e377d9d93c6f5bfacecc5edd0e0847c66898d9f5ea236063a94b6e50ac3ba40919fa4fa5b9ef6d02f641894f0f7206ac08785140792b3560775d0bb24587d8603db1652005dd1a3d78fbe1900accb942dfa4ba61ba1d51d1d8349ab010cc44b54b23911d0c4837f68d07031a091f41e3d6ff5c5b3ae6d9e90b62583ebf1fb6d53cfebd317e5b8c0028c694610457a07b60143267c2c38a62bd68c494abbe80c12b47decec80ac40b07a7111a0d1057f415e5653d2a9e747edddaa0b3387aab9cebfb0d897a4bdd732bb312e804d5412b77538fc535978e9d4f25da771e45d02a590d8a9e0382021e8627dfb701e1a2d03cf47d10ae61a89cc652361bca3cddd6e950f9c0d2e40abd421c2c2d063bd33af5551748a7b3a4fcc1213386ee18915be962680b6d52ddb8b0fe2ee8036ed2750102c62ca04b81469f6853d11c1a189cb22acc2ae08ffa312f918eac068002d0764313851cb14321c34c18e26e4b69eba87f83bea9acaa53d6bd5e1a0bfff377a731b8b451eb36b6d01db3191b79533da5f24b0a9c6bf9b010a1e0fc04de595f822e0895611d0c826f5ef64cc4c63744673c48a50c24d55acde9b29f0e69ebe27ccac8aebf026f55e9114c3127a45621bb00e0326e3b0767d2d7d5140fc61dc5cb8bc549b43c3b2771ae0505f1bc5bb06c76d3ff91323d9c17a53c3a0b504577a09c394de780bbd80c6e6e08fc9b795007bcb129026fde69b5f572ac007cbb6908e75dc717877cc2342249e42144b1fe2451beed6fd06326563b8b0c09c1135ecbb971cd616a2cf9ee85ce5851c52efdfd4f2ce3685aedb72601ce94035efe5a51e1c941fa0a0bc498b1f4abb5d3daba9ba48c0d4531ad0767a9748008e62c8ad0da2f0e63e9bb75f0ff6da4fa938c4b09ed05ccca2f5edaa2501b6c01a36b4d34a0dd7f36ddf4d422ed42abb6f08b23de9cc0bb1956cfd0243042a80b6531e21377aefcb6befe0e6b18293920815c3527b602d6203209a85f800d9901 -generate_ring_signature 6050b724afab4e33fc1adf423689add9c50984ef8df4b93034fe824ccbff6d6a d1588a8bc29b2df0d555b8b0632841b68cbbb4623ee615c9499862aa5b732cff 120 ef87037b6f29edae32f566f14ea179ecf470fbbcabf36b62d252b85af30f35e0 0adbf81b03429adb494d54bad3a726c919e81df09046e8f237eb9b5f82882a4c 3539a4bd1b04de484d125456d2135230caea0bcd47d76beea4d4521918f7e769 e01e642c44600e167c640013562d0d83d62e9eb696b70e9ca217725dd0b46ad4 9e748c90a8fc9e8839f770f94ed282e08b2d0319dc1287bb2fb3b240d84dd055 43001d2b2c696470582c98b0410879d5f5afd93d7bb90cccca625d289bc6a3a0 dbe579be20a891db9f42ecaa18e45ccbc4abd200ab7258131365a4d5c93e776c 69f3b6889f8d6d446374ae878b38da35741eaa4e4a88ef220d16163703c244d9 f8b561dded476626a7c0ba1adea2c1dfd69d96f0dfcace37406cb1bb25b19d14 881e289bc813db26f4a718a6d790d553c8edd952fdc6e70f1434e0cc81918098 0a56f9f412b6080c9bbd334a6a0ef92ae6a61b038d6a09b0b4f1fb38611bcd21 2b8c0b64bab89e760d5a74376f331ff45e5a5756619836c00ce5882b08e3c164 fb0b6c65d3823cffc4fd3fbdf0f7d867017a8c2d2070af7f69753836a10b3346 d58bf0a33db172ad6cd6460498f3fce85fb3a138694c3beb46d48bb5fec7fe26 0ab01cf3ce2a8d87dfbadb6477bcba61ef64981fea057acc295aec2dcc9ad983 3936b64dd7d013462f90d1969d8c601bf6a4f0bf45b443534eb2ccfb03998ede 412213951f3a59b88d161c057933de94085d7fff3fd1e370b22b16855b6df2bd 5d6a58959691c9c8a62c09bd015b84d4fd7a4c8ab3826100512dda43cbbb4ff9 7eb29652b5ed2b1080aca08c6eb2bf7a41e30df6c229fd56b5d5096905b2acba 732bddd78f3cfcfd342547d80cd2010ae931a76500c9fc073cc193a843839d02 e48eb1544264647008711c351e81c32f81e41f5a7c85a7141056c253e8e856e3 7288c441a034b859a5056dfe0fa9911c24775637c5f707c2eb28c90961747514 f06413f8ed307215eea8842d1f6aca79eb1846c21e1c4c243b665ba195a1781b 01b8892d2d57f8bf7cca182387a6d0872a92fb1a72df366d873307ea3927fa12 d729b986311d8c0da3ca87ac439c5fccad8128a5be990850a3f5820846147146 4b4d2bc6efb964a9581dd77f183a55567cfebf196353ded9b03bab6649ecd10f 4f12843900a11cb9802ad2474cfd41edd21405a2bdd740e9739c66a5bbe50223 d93acfa9256172f808b269ae6faf49ef9cc3c86026efba2002d2eb6a1da504bc b869d5d99868014123044ba4808bb55ff416a7590cc23bec4d46f7a517ba5b41 35b15cfed88fbc7ad9034d47394fae12fa8287dc371b78a7970c51c0697d483e 24c214c701ad51ac08e0a57a8da5c0459c2499910cf99a6a851cd435b47627ac a92fb963867312988139b9e3c29d2076d1a7f051513b6f2c3c24d74f377f2e37 7e4054200f5643f3437795f83bdcf506d97221196ea711b1d69afa5e4b345978 a9c74fd8309fffb5f589dd48f55e29653b8d0638473139eea572bcf4356bfa13 c8ffc26337fe41548090425edcfbb21989da5914326fa7ea776b9214252ec87e 3e25940afec823a365f77fb1f13aed2180167df85773ac52edc44f2346ff7c59 ddd96cfccaf4b991b9fd6f819ca1ecf8e0996e42862b9d0ee71f0a446620822a 50090e56c723895660648958c033bde07fddbbe89149b1a8c8656a6a35a4bbd5 74912093994ca326fb15659020de86bbb291c425b8937324a0070ca4c60a9165 68fd8c81aabe602c09f4b37046db3fd2c15267d974c3697c5f6f35f25efc8183 44f490d63003625443747a7243287d28b3f5fe4ec2a273131d8bc7e09c461d05 6359841de569f54123834720941eaaaaa59b8f9c2671fc7c83ded6f831e9fcc6 ea75a676b28568f3330e7d9421ea92f3ad3cb9ea67ca6a1662c45eadfb67182e a6c073f05c29b0ce08c57cf36d71490d5d40187d51d382e09fbbb2a54114a376 f4b02dbfd2253dbe13ada78a09b53ee799fd347705c07a9538cca79fe719845f 4535ea3b1f6aa90a6b98e0b940c164b31f4787b6a847758e88ffc7fee05d5cd6 8772138c613d9fc8f3f62a0373d7040616a372212328d0acdc333d62fabf9a40 a8f52f5a71eb7d703f5478f4f382b2cf3b0efd6538b24ecd632178dd432dae5a 97215a35ff24137d83c793bd9057ebe8ee7e310964ebc5d41eab6d15342479a7 e7ee87976fac8c0a016d83d26fcbaea4c18103ce971a1d695333632432cfa1ca 00d81de15264f2c13b567041ed3a8cd5331cec6305e4ce7772138be437d62437 d452fc10e867349bcd7f3a783012d1b4e26befbc821b29f8fda8cf5e5b8c9b03 96eee1a1c980b51b51a3e47cd893a4930acc2a61ecda8beda97ba416fdbd6f15 a828686eaf20007dd299f869ef52fae1785b0f25810a8b8ef5b47d2294b77349 bcd0fce555fc44ba05a71cde39e36de093d44149f803781cec70a1c040d4e114 e5d10dd7877e7feb14ff7ff3074abbce9b62b7845de46880a1a3e400269e23fb cf4cdb04cb0158c1397ebf413b9940424f4eefc3aeb315b134bf1c012de95886 e99a22d2d2d0bec068aec00b14213757b498140f6a637b3d14244f4a83391640 e1d337d2a691a786e85f0f2b6be24fbbc6e3b0916986de5551f2e04eab58bbcd 1dc8e63527bf9ab5699fe1b2e6d6aac976da2fe8c0f3ba390239067f19168d03 f32db1edd95b03ec7a6cc707383072b2f1d9bd7553ca3213a76246ed050e5ab6 9b6a260abe2e997d96e528bee60949c7c79d22e88e3f97ff9740a40c030e05d2 ba6364da3293a9a53a9068e15387444500d6d5a91c45760b30b900cc5a44cdb9 5739ad6c057d2c16e74312a7123fd6dbc46a1f34067f1a6d3f8751f801fad433 e6bab56a1a5f21bfb687e77f65d0ec0cc0063576708f02b204a6fe716f8db773 97407b2907cb431220e5553ee94eee4b92fcbd7d3d26f11e48399f5c9ee85be3 861f72244c53d41969ca914dd57e15b82158270e5f133876664d689d541ed165 0de2abf3741bc18b0f5937818f3a1e33523630ad8e1cda086b955c2b13c4817d cfc22f7d43b1effacd87521b5463772e8453a2798f2d48c2918d264f15c563b2 26c67ae82b3c310202c1908af303e69ca94c0ba83a8176d54d509781da183e86 75b7727257ace3fdaf28bd08606f4e8d471a805858a4de3af7ff1b295a8877b5 8cb68849c5752cc92d8c718605eb5390ba07013bab0659429981cb8249148107 10e3bc9b4705f0a5545deffd9d83f16268f4963d4d12225086432dce3f317d82 994e0e93851f6565314a797059f6a68746533f6d8afd7164538c279e3983bce0 0026807556960aa20611418df4ad89b46722bb4273305ab5beaee0518bf90b4a 5df227da750dc157f18d54abfe7ea27bb64eee746b41315c843b42ebb60a93af 6c3d71b333bd43ecfd9bed9e9c6a3438ee386283c9de2bf1d9cc6fe63eb3c7a9 5862c6323eeaa240cbc96df54754b7f9b38a982646dd58b29b7674fe280afa47 e59d1cbd91f6a1da83f811068d2c32d3a4dce1cc87e12dc670b3c8f4cd837668 e01b48407c6cc98e591279d215168d45f8a9b0bdce74294c863520a96c1f3578 d123f51fc47d4a4a23d2589b3ea6afacac1523ae68c9c3e987efde55960da989 b24cbf3af9e8598a861c21eb667e305b275091ff54d619de94427c4e8107b4b1 ce5ece118e978a798e3bec113fcb05480ca08761b12f4d82de6aa20c351dfd4a 881e216b3ed4aef9321ea28d6f31d565356bfc4b1df72b877881273b55de257c f086d77f57298e4001ff99f30c1ce019cd81a74fb77dc8d3b5e286a972b52b78 22359fd9b4985eb46e587cf19bb5094ff334ea115c12eedc31b14a0c1a883306 f4ca0ca1c5e64f8143edccbe63ce32279457871600f83f726706a37274f0dd82 2aab22b4fd2e8d361accdb45a9035695c109d53d69ca0bc4b6cbec1c5a5f73e0 d4b27fc292dfe7d49fa7cf49901503f5c6fa53812247a99d09fe2b540c1393f4 136c4bcec68a9a9d675f590e35ed6a9e51a85be74c269c66345265620f5f196e 2903ea5a2ff85ea2f92a411467005858b3a315b6b6b9c7a7b406fcf2906a69cb e86aae3208c02700d40ea0c63ec911ad32aa67fb4d5c7b055ea2161a877cced9 c2523fc728db90b1111de2a58e424cabb7dc53810094cbfed40e0f1899b31cf0 239b2dd969fdffe6ae68c2a227fd066b0d08fa305c8daa807db24925d2a7ba90 2d64e595338b931b929125659133566e25fc56874ef3eae77760e289a4e12227 64ccdc4872ce46da3421e9404faf86eb8abd0046239c71b8fcd9f2b7e1fa88bd 85c0e8646ac761449a58f53106cf8406af576c36b70c76410a038d36762d4abc ea4dbf46f8653be48bd964ccc8171a87d06c3ee1874eea739df7de9c8f06553f 31ef879b28137a2252476dc796f988465ef7640224a5d9b8b75bb890e8a8544a ab061319d1d6fb3a9cc5c2cde8201a208b82e5eea782b88ee93aad24785c2d6a 43d69deec91854813f55eff328db76ca3f1973eb498eb0f1951a15c806b8d9e9 f9f07b9a4f5969d9ced711bc842836faf8049d5feb3f4cbcf094089d0213a079 efb00a7103ff7a73f36a3b7daad7efbd296feae5fbcfc8aea798a5921fd4982c 9ea8ac862144b79a9715c672a206686f4ba6790c08159746b2707a8f20845b44 22f9d130216831692a2fb6be1a5956179794cf0d3ab5240aba781add81c8eb7a 2e43959ec556d13a5e30403182b5d786a6c90b1271333bfd306b15525c3838cd 7dd14d4250642888be66c827013a345b63ef223c79e5410d86fedbe880f49511 075559e15ccdaa2be0d98e6a4e9248189168612117f1161da8523fcdbf62e725 c071bc9b096c8e911e9f6a4b05726b4bd8cd54ee358d611c0bf0caf467764aac 82126c2b00980c6bbca68944706644332100586eae865a99391c3a99fe206209 955eea558bb0799db9ee5994a88e8d323e27a63075def3cd6d16a2ed82359240 23a79218069842b0364c4ca540ca80374492d6d3bdb578515eb5c62935e5cdec 8c107f0b24dcb8d4d5bd749b90782be058a6665c71e09052e6521a5a23148ecf e55ae85f46ff1d248b12fb35c35df934f4da4865c9969504dfe03eac92112b11 9d4c8f0d6c14ccc7a9c9438cd11224344b31f50c9231fab574d3923bd2ab6276 6475135f9644ca979ce611a47ce21f55f16da6edc548a88b7f1f2d5cb828abab 3f78644f896131d3080551c3b5dc669884f66a90069fc02d0980c8cba93dab5d 032cb098c9caae12e929023e907322382e07864e0e03891710413bc9baa6f628 03f519eb0e73ebfdfc8791829aacaa3c10fbf4a69505e658abf61ee87b6d101f 8dccaddabddeba280e5df36ba751d672ed25f786c12a8c725f972b6f11e3c638 e567bc025376197432a4459ab346d82730c42b604ed31e27b6f16fdc61351b01 16 500bfee92bbb60178179e716ddc2ca5827665d277a6fc08daa0987a311ab9b07420a95bc7ddaa45815fd02ce10a71ada7be19e9db428cab843e616fc6d6c65036e662dc88990e990d041d32a2273f7b12e2b22b6eb200cdb33c5443b24d39901601b4456d8c2e78069248c98056d87a31864f6573f22578f6e52bb33e051b7001ccdbaa9e92272cc890c678a15d4fe021a12046c600cf6573e3d959f9d2d4109a9d09d504df479aa0409dc249524983f2d1ccc1745b2a117615727f7b54a3a081ff8e975344e4a53f844212b250f9df26157938b5e88b3176743d3bb6997990a0d4a8ddeda9f44ed77619f24c8eae3c629cff25c72444b38a930ccc22f6cdb01b30e88ae1a9434efd95a6f83f0811a658a511b7ffd516b56feadc495a5e8e9011a91017eaa8dde202b88c570c609f462a94fd8d0660c7901d81e397402f8f30e035b2d2bedb1bad1da5bc7883d612994f2f84041f6ce89cbbe861f637999c4042de22266a235ce7d1fbf207a7c5d109ad7f7f53b1ab85e6925ec00ccfa2c4107ddae90c516d607f879fe71bf5dc3fb676ae9e7c5ae329131f84b31c8fbd55b06caf450fd9411777212daf834176026a33a9ff8af1cb5ba66d389c3c58d29f90cdbbbd0b53cdc90f8f3eececa91b7a7979a291dedba23eb4da3218158a8e0ff03849803e89a3f6cfefc3e220469a79b505772a4fca5beafbe8f907b0c9ec15101b75de2304eca53c4ad01286b0850fd92985e12e220b44636337541c68804830676c2bf964e3472fdde4cc94bef9e46bb71cb14e260449def5f976fde3e62110cf76e9a287fdfa857e7b09deb5c56e4de2f691d8a37c9eec0bdb77e15e119850e0dad9de524ee8e70e051c4b2b3a979bb36d4146522a33a3f860f429fc92bc00d3055b9ed2abc1f1bfd9663f43458be6e6fa6867fb822f7885e1d7b1adf204504a1327707993f19689980ae90088976e2e0eadbb2022bec03b2bb207482d8b506e00d84d21c96dac5099dd4c3f9ab102f483ef884051c841d7c8c19bb452806032c244f26458b540e9c4a598cab9e4e9948a03e2f608542810a7a0802ecd9650e8471eef282fa3cfce8d376adce136ee8a26ca948d4cf97c67d4ada0bcfad1c08088e397c6bd2438a679ff711fd930052bc41dd2147823aad5929e8ce2b9951078d680d6d99f32bff4c0a20806d634242a18462d6879829fad65b927102fd5d0fd861d14240c8ac7c37e936b48da679bc8f4b057beeb417248266d952dfc0d90f36a895ca8f58d9eddad833a77de92dc3e6ff268a5090b81ff9a6c6d73235bf01ddcd6ae5f6b4f548d45a1235c6a638ea8ca955ef54aef9c44fc6443934252e0a41d407f1f0ab9efd20e0967684b7f44202c15663f9bada2f1885707daad20b02346f117dd4d6dc17f27357f5da34cced7cc24cb184b62248d6172d3279cc700c179b9c6292edb3a8f49301df355a10d739018d43f796db4599d2d0b723a7f10421d908beb1485058dc80db7b34a9667945ce9d7c8a11d6b2f5e6dd021cf9cb0e08b824d268f1127731d7570c9eff6480ce4b432a7b7e9865c234e9b715ef43048f54b78c9ca48d0b4f082375f0b4e550990db8dc471186f41c482de929506d0a0ab7f52497aa25d696b1add056d975e170289c7ccd6448aa426fa6b2118ee909176deb487b65c4be70d1aaf8ae4a82bdb56bd4940700fef315e89a7db1c57804dc555103a5528c0fe5dc4352e25cff7a0afb1f489690c80e00f9dcfe4b0b440d812a662b9292ed76f944c456c0eed7de13f8f29b88428bfd549873c98a21e70b5c7611f8a33100e24fc68a73036ee6e958ce34cd040aab6aef49d38ed3ca150b6ce8c50bb91d67db121e1b475c9b5cd11fffc677890fed4c8e5be250daaa6c0613d5fbdf7e3694c8347492e97824787092829edf1adf123c46695395d9fce70d961dd5af042bd322b85b937034ca0026f975be38033ee3b8e6b37efd73d84e0a0cbbb79fe3e54c2fa7d695b76b5e10fa5c2d82651a9fb556125da1ffdbe4e90700248aa8a77e1d89bbe7a66d7a59d356494ecb8bbfa4329c99141b219640d40cb0306b5b6305f430e22be54fe8bd2bed3d0da269fbd8daeff76b27467e54490335be171e16c2553d1b8825d551f3ba5454a9553407b88d0bb539c75de2c52a03aa553d3516b41dd2372f0f046bef9d428b7fba0e34da0212238d315a81d70a08334abfa4ae77da4909ff6b9ee75ba87c6726f14158b9a09db14960091f8ad308751e2b1c4d6714aeac1a4712384e9cafed2e0f8c7d13e23413ff8c6bbb7cbd0f8297100c81f35eb6aed0b12354dc16f62ec9aa1a5579a9d87920d0e45468c206327635e4f377ad8e0e654f4cbd39e358ae62153862c9d64879f05c2f20560f0969aa2d035995b4f8c8462f3cd26d6f109ba21e61688ffbfbd1d17a6bd70b4008bdccd875fee97d9840d220efcf480cb14b4f0a3f745ac12bf14b78cd4b9cec044b3fa3fb035937b679170935c2f542afe98f5e823ff6dea2cf027a37fd6b9d0536b532c80506ed49c2da71cc0432d32d9ed841cc76fdde20a0fbb728b3e52d004ec8266fae951d419582fead9da63b4130311279a17d439f3bbba557bf2b820e8d726313ef6bebc791889201a0779a6e979bffb166e0356466542b89e7815a0d9769119f949cdfaf24fb773c795b7f56d29fd9f72f1cfd025a25485629a1a70a6dd4e81e5a796490a9604566e376930098f038b2b17cd5101480a567baa72502d88c6fcf3f4b728f8cf7dc50da26cfaac90d4b80846f1a1d1704c632dc2f6a0ac622f3fe4e3c1fcf0f337e79828915385e0d978c88c38b8ef7b4da8efeb67901572ec9a2dc8e76639d37b9c5be13dff8993accb5c7086f634d5f50cd47cdba0496a067bbde44e31619e0c8dfbe1071a7d06a06e51a064113f2352c32f7374305a12fa85e70638f02022cb9206e0ce8866f446f7431f81df1c32e694419a833051fb9373c3939de2a355a740d61e855ded0e2c52f22bb7896e48fcafe6b23ef0300984d5b4974fd0421431112de1776c30d03f074435329bcedc77135f84c3202caf1b0cdb7904704d07a1fb0ddcc22c496bde7df179cfe5989325dcd3437890d85537efd1adec486fd8016a5f4256dfde3ba6aeea8a250640e0692ebd85a520b843fb0e1725358e4470733fa2f845fb143ec227ed8f05c12ee3c926111b7bd03d8d16467eac562eef2e5adadc2311d4be59f058980699b86e2bf22f791b70606f5fbe910677966ff3b3adff10d74e47eaa55a43b1863216be494b81ac744670257ce42e9f69a1ce1f03be88d01b8197fd413265e1babcf435958006435323c0c5abef8224c9306413d5f3a8148a49a7fead11104b9ea836f9ddc81a3b61c1c0858452bf130d8a246042cfddb6e911fb64709169ed88c1ec7986ea5bc6d34130746fbd94ee13ca3fe41c5e54789278459cf3f56829a79822e95b434c57887e300e007dce4391a161c62989eb9b35bf2daa57ff1eb505a8cd3cb4bd328a6e2e30cd310eadc819b09401c5a6befcf1512fc82d3c3cb837fdc8501735d659862ba0c7b7ddbf8f438847758f4205a46dd5b24db770dbc1337f1d121b8c2ca404afa09aab6bee5a07dbba0ce316e61df7ab80534549fc3d930baa75355391530049f0df07d4b9d2053825b6f9118caa462f41a1b411e072a2c8da577e700291c133a0356818890af8702fed546525c4554b0b76c6238bf8eaa0014cc3ce3a94dc3c40ec25f3afa0195d40358a02ebc1bc4297106eba901fbe04206ef8af3a095ade70b1941f1997a5a1e8b4d9ffdd58df2503dc8b2d580607bbb59cc2e8f5dfe3d02011fb2e7675a698eaa1296f912b9380bb4836902a3f4e225f3241153ed19003807fd637feb7006f2d0fd58e0cbc273b33d1669a44d54ac23ddccf4d8d09b5b4309b22b26068deec69940edc20254a0752910c9728188d23c132a10a78545971405ae464e3b88f98188e9e4af212642aa78be1586b17effa7eeb7232387cc2329066171284d7b98d48123002e3fa0c96ff61ca56b0e10a1803749c522a48a60bb0114f320a5e144d097ada55e8052eebbc9a212cd62fb5434a4ec8644903bfeb102ccf92fbaba64152bca192585d0c9f3634d28755f565f51b815526408920ace081d9e3b8af53443de673d15d68451223f902886f01b855654c7ce3f900c0028037c8f3fd72c83648c448ba98fcdd166a8e1ffa700592b69d522775fe0a0cb1b04f30ba408c19054bcb54e554fffc0e15843795af5104dd6ad52f9ea2ad410530a556d4779d4ef20291be755db134e42a6c6d0ece5156b30cde1a15f5654954d0c8af95b0142af3708c3c0413049b8f81f689658f56cb29236be43889b3892510a4e62fe25540d72abcf6b72e0dbdb2bf236d1f6df9b8ee4d09f18f2ce7477180e1e350ba3880b82f41ef752ac3142337b963b45b43e81c946c9b8fccb42521806d3fca4a3aae3f9d39a9d322179dda320f1f0f93fab3daad351b98db21177c10ce4a1df3e77633a08958c216d2049363217abe4cb8e07c9e28c7095277c2fcd0846f346e0f98cf1a6895381538606ac02bbfe793326aaab637a55c4070be54e0e96c96eceb23392f5ea40b9a2cb84ff07fc3ed8ad9a3be6903c0d01f393e9530b096cb2bcfa82a1aab8f62b749617aa9916b8ff86be454ffb8e3d2cb3a45e8e07a14cc2ea96331e827345d41211e98c824f63bb122af6a3e37297ae007b4bf9021f9e7ae1dcdeb5c13e1190c6b32503fa09077b86d13c5c1e1d501455f0d73f025d6e9f043fe218ed91a71dd74c591d1d4eaccdf487984c6e5f246c91a1c6e50c9bb981699fcfaf50da865e6e45bf33ebb740b5a2eaeff82b225d46d0a87b380bd561b32f5e6cd2cb84ba75256d67b7a0dc933cb40c8fd8432b4d5fee4c61a106ffd768ccb897454decef6496c74c3ccaf969514d7134cfec8fa24e838bd9660e47105466ff2fc5895bc62c9d9de2e441e1b78dcfd8b88a802998eccafff6e70a722c494774cdbe5a5ba97918148432b1d7ad64163e018c6128aae7ee544af50a0b9ef82815540b4ded09b38ed1a33ab9f008ca0d6db75e4f986fc4c3a0fc080ae7679b04be85324b65e41f02fe5b578cd551d719c57f925e72596ad1863a200cd8bc1f82ff6b26cb04f44005c4a7158aca2e982c37ed0a1eaa017ae05167e408ecc7a63989ac2a51d3877dc14109fb35b2b862b83d3cb36d96ae2e65af1fda05b1597b97cb21fe0f333e11e41e04debe85c5a6a25ada1a3e97d82235d1734a031e385d1032b6cf6684ec63c80c6cef010c08f442d937ec21ce93c74bd0153b008ae01203a2c082435ad484c431416ef1be6eb518053c8ea11262334ce9eb400de54e572811b3a5410cb1bc652db14b06088e4dfc5759efdbc864786904f05b0249f8bd391c339b3bccc22dc00511c48480eecd480ff51219fe5f2f1fa59c3c0894c4eff1a987c02867cdba30ef31ea4b8030b564b342a6915c3a9a89ccc9f0072d6ecdf17e3c3d5b20241f84bf91989ac8fad026eef9bfc68e72fe8491c8400db9100825e8dde068adf9db7cd33f52473691cde4dea14d3a7b67bc251894f20b6e6e0d4927d8f1ef7e9541270b827c342197ce78af8a76816222a2971dbc340d22bac2941b043072ebfdb0d3712555c34c5a82a11095908f8918fffa0ff981058e1b1d57137688de70e0c69415ff0c3195ed79e9eec2704f91b1a9e723194206c943eb079fba9dc05a2b7d2db0d3837718fd6cc7428ea6a5e8de17d9515d0c0d8eb3f9a66cde38f92cfa889ec6df2f09615556ab421efaf657b6ad5b27906508f1415dba9d2ae84214e96ae1dfcc02ca43c6a76ae58f583bc42b5e542b869906171256c70230eb1b5719df8bd55026e426a90ccc6a4342d50d871155c98c8d0cd072a24d7cd876b39670eeb6406d23ac481dfbb99da8bae52e2f50c59be0d20c8ab29f2382a9846c3581fcdf3e7dd14e8fcd53c4bdd615158c2dc71d045ccb0e8d524b71db66be503a2833f36fe941c59154a049c957ea4ff2955dd6c51d640d134a07862968eeaa19e71e37995954e1acb95eb527797c44a93a14416068410700615a7399c9c7db46a34eb0d06346051e5b2a2ff399fe3557794b749f7136025e7512e67c11c7714d10cd6c5a08a879921b8b76fef134d66dd09e9d714eab0f9d67ffd31e694b690a04412b16f9dfc72583a40712dbf840d60a9c80cd2d7b0ea413ac52165704748d376303135865718887d86ff6f0ff469b650c3a220b4b0afc7faa3039c0dafc0686d330eddcb3bd310141155916362f632441fa541dc4023d0aa5aa48a4331a35224dddff52ea8be0cbaddf3c244c950bd70f5a3764050b81e7322ce667166c5c3868eb5c2dbba54d1b6b982323b4ff789fa4a07c987d0d1135dcfe210a8f25c73504966c7f62ae7d84749da7fc23eb4698bdaa4d94870ec5e7c128d365a3c682c3205e509eea68fad4c64fcfd939642bf06b9f79fa6c0852c17f4d989b32c68bfc0fb3d3cd8ab590bd5a0a7a17efb8c58159e182ff92015e0e85cf90938e5ac8c0c64532f2ceb36e41bcd8aa517211390f0115d436e60a382ddbf438bcc94a3168be3a670d19ea024ffb293093c0abc7dd39163ab2ad0d8e3be19b1f72ed43028417dde792a25ff6d1122eadeed1c771a35bb64e87da0d9a0cab215c2ac2e27e080c298e5570871731d244bd1dc1b6f7aae0633d156a02d6ef8c5dabeb74d2c1d72bb62aa723c09f28c9defbc4f7829c1f1f2ef6f82001c6f273af1c1b8f0213a898489207dfcebb48684399f5425d487fafeb8f7cfa04c19d636e3b043293b7e16503230c36b307e18e6dce9376d32d7a0e170f3d8b0a935938faa07c0bcf0bc3226c6a90f64628c320b3fe3fe36d1ae4f5f9cca4300abc294cf8ce1d8488731269ac5e365adf64d9787a44ed58f4d0df24ca73e0f20a238fe51e09066a9c253f7ff229d8c93fbd176efa83896340867ead9de28c4f0882fe3361249b8fdaf16352be941c8d01587b4f52eba300ccf1213285d3bca603d2dbc7dcf3f2e9c7fb8c16da6da4ac8a310d33c3fa4c5f04bdd174ccf522f90b4571606da2c95a84899c43b56b0e605c0d3dc7bcc63158a0193ef12c52b3540ed0a98d255f03f876107bd668a726480c223ef10e50ec8909448a5298865ccd0895990fc37f9f6e6de9691b1c67cfad59fb97ad234df24ec69331525bac93420b625c50aab85bbf8dd401871231c0eae1376bb0b3c450f3f3e94fe3bc7ccf2307281fcd48209c51bfced2ef1298c7cfc23c63bcaf803330abed62fc0b96b11e09daf39525ba5fe74605dd323dae6286f2151e1a7f663ac47083d954bf46db490bc3180647109ff86d33d9c1134c5c900d2aa7f1879992c5573c607603d253140b9bc73677f63b6e0af854c48201812b1ce2dc0ff6376f2d0da8ebee69b985db0cc2ab43fc28f2f2999722bc853775e1320d0573d213abc3fb9b648945f8867f0e4ab08632e5c786e45df4d2b0275248ce8d72bd39052e9edbb685da350221d90a11bc2c754ac674220dd55b1ce608c00096473c065703f879b62198aa6ad901061a77f5365e8c28812ddc2c1fbe9c215f2647ed2624cbd0f9cad963f3f9357f05b061a506becab14789dae2641d56aced44862d7676d61f87ceb3db45d22c8d027a849e0f8424e6bb0c275ee051d70f252062ff9dd7cdcf107bfe839d95d49c06e50cfc25cb7e64b04c1ff5170014130d8bfd07ef4faf96600c956d1fc245280a7bddaa51ce33268ef57e7084f580c4d144df9d06f080db24b1c93af3880476064502739945fe48fb43a05db3bdbf9eb0076da4fe98df6e0f21cc6e25951d5a0af076b82ab260f0e6db120030e698334bf80877e298ff1035126e731d0bc5fc0a48468579c0ddd99ddcde3235db260cf3a7b0c65fb68eb1b8cae7646d5b9b9907f29c3534740dd6a617259e4b6382b0054cd1088f6ec2b8ffab72756dbb3a930555d9dbc8e617a14feed61c43805f07bdf2aec064b6de362ca81bda05889c8502ec62b299f8043c9ff6a84cd1924f80f3d474d342b5179df1a70bbc1b1408e60f40e9f6e92a3d6313c5914f0e00786d1b2ab46a2ca546fd8cdab7926cc21ebc0a251597aa93c949981cd4f944d29aa46ce20b1fb7fd1e3a05826c0aa9cf60920974b3287b9c995ae536e2045ddce2cd4d0cfbf7b534865af4411ec774b953da03c87c78b1c045aee12a71b0d9e37e2ada402829f0d611de44693ac2c9ff6f070845d2c1e8bea6d5d1af778ac5aaa022832f97aa9f2019a65f9bb728b8b3a4c30ab2db37bbf7f5e1aa602909cdbc6ed07746e4bb69825accf2fdadff05abf87f0a8907e4c2c8ec3beed33587d234085f333cd26b82a0ae1a4af05bde04b49cdb02b9c76c6377fde2365e398c7eb567b608a7619599da27fa65df7f7e11093b220c220d66804a3f146c2d77b210fc58590e6f1c9b2fff1e99b6d94ba765c1757409ca7f20aedbeac56d5856aaba165934a633ba69448e9070d22e6a1de4bfae150b75844f4763dcc1a9b41d095a542496c33f6d69143daaeccd5d3529d35df86807d213388d79e0a3c2daf99b7e0b7073f615088106e0025c5f8700bb7b38ab18017504dd7ad23716be61ed7d610fadac9728254a62941e98d80e5387a44c84210af0d80964e9bd2a5e5875fa4c22e9182bc4a1ce358e2fcf9aff58f99cbcd48e0a7d96394fa6d3e96642a2456d73484f2cd91b0ebe14285a3ef80b184fd9a1320143e6a9e2c47babb0798a4082422bbb8d351d6a124140710b63842e984f023d04a176d9bfb60fef802ca2f9a184ad5e64dffa1ed268b467a8fcab9bbf32246a0cfac79e86270c4000acd7373a9d3be681efff5127eace29b90ac31bbe045a8108239666e8ff89bdfbcb186740f6e8588e336cbc954868fd2c33b53b9a2cb570024315b2fd68bfc08fff01ad4f437f4c47f8b04725874e0f766c76bcd190840f011e5d452f4394d554bede29661103c117a5df8ca70d107f6cfa3a3b1993392b0375e37c805aec67470f4d7157c775e6ff3acde8e588ba8c8130df439e7427ed04b683e7906e83b92d54994905a8853758390b3b047065c9b4a440ac5377f0700b4b797492fb0e71478b721fd9e1219919a8cb583b1808dfa99e2ee7ee9d23b1094fe83c4056cb416f16281361ada2fe024f659b2ee5fdb581ba92b4dfcd20c000f263f4997ec8ea7ecc38b6ebe5c84651c6fa542f9105863f293e8febf109c500da1705984fc99044c2e6e91948331aa7b095c5b1fb067067dcf607cd1cf5a00065f757516c47a106dea45c4846c18284d77a581724497069fef949a07d05eb0e33d0945fe45b5be145186c07ae2625a33b9ad68a71d03ecb08446777bfe8080136ae0648d060c0fe8b0db26ff12ff2e15a9b4eb9e8ba13fba262fed1992c62060b9da94596a3876fe84794a6f9591a89379293780f449d625dbb33df5d76fd0af20887fe66bcefcb919dc60497264980ec2f3bf85cd983f89c4d2faf96ebe9024940aafec8df42b54344ac75c53156d27158830adb93be28545cb327ac4a4e0a2893ebbc3571b39fd59b663472910b2c9d2d8f483d5d8631bb889a9bafea910f6a5c5b4e09fbe9df395eaebeb1c3926208380bc18573f9f893e81b2201eba90a8cfa7b71a62d576d9e07ef551e89e0e55568223e19f85be9a381584fea8d6a09308d2f6e340ab7a5870188dc17f80080f63536cb685b7f6870f04d161adb7d09cf4303a18cdbb6bdc88eb57b9a3e80ae12530ef25c6f1e9b3e0746a6e11ce8010ec9594afde7a83189c94b9fde62d9bed9972db0e12f1212e4db2acc9ff64202979b580e17b312d0da79598901759bcb9be3651fd415a4dab3e57c843f73a80909f4621ea4f62e582d9efce3addfd6341342afb5775e0b627a2c5a26437fc20c4269b49ee6831e42096f5a5ee1757f8d28fa5d8f4252973157fd31b80796e80f0b592a9cdf263364cde5f5c0cc90b2739cdbc9786922db93e1dde08283e0b5031712bceb7b896a16e20e3e519caf6e05a46e9b067bc4011a090548e4191535044e468a929b60c38562a7da82494f868217bf7b0489715eae8d441edb6cfb950e5776da439d67f48b66f8fc94ee4b42abaaf0dad1b920ffc67a4507e624a60d0ffd465dab5fde1e00fae5f3f3c8aac90b8b3bcfe371c6f623b120555536e12a0acd3bf5a6b8cafdca7f28056f1f889a2f6dceaf78f33e9fbc41db2233205e8d09fcd18d0a7e21ee9277d9389dde0649183589d7c87c68bcbd5e56e4978af361052dcb3a8111e878fd4f718f32f6ebaf607246728aed5c376a83147fcee2136c024ecea5f9d6f019bfed7e2a769dca53f5fac81d3fd32210b6366ef102e2d4ba0f247516c78581b1e2231bcf866fdafc749b2872296a50c1eea69d67775d660e0d5df26453a11912129ce2a9ff89180364ee7059c7d0782944a9b4aaf25a79e60c939331916f69cbba564956993891f64e47bf5829325a051a0b201165fc1d4b044285172b0f12caf314ced876ed0380218ce20d5ed91c56e1c431b61e3922c206e759338c8531b99eba872f7bcc0424caa0255259d4799f046907692b2dc7e40bc51c5cd4345aaaf8c47f9854672f9f14557170564dc80a2c0c2f3e187fa52d074a26439765a40f02cd99fc1b17c6f221cfd941eb11a092a914df4fa02bb7c20c5544a89a12fc85ee9f67f710e11814160b528d2852d253d5c3bf87cdc912ff001df7df72bbda72dcffcb1cc21a057fe74031f69cd4f7e918d17ddd848e176602f19c6a8790dc472d72cca8749e1989044897a7dcaa7aa86334461f644063ee0d -generate_ring_signature 9dd907994196494b8125e30882384a88166cc89ab3d0c91f6fde9e8ecefb5e01 90dbda83e0a10b0070bde90aec62e4dc65910a922c21ce75f37c425cba693876 1 b8780402a1279c031fd5dd4cb89f48bfd15b5a8dfc140d9fe0eae67a51d18df5 5631b1eed32e0c03cc92739379fefaffc2389bc6059c623a2df77b13f41e3209 0 7093e2320f7d16c5c410ecd022e42b3e2b18e6faf7f76a9768cc4fff110fbe0ab6c21d80bf476ebdf545075af44c54c6c4a00f2b8d821e8ec556396db267ba0f -generate_ring_signature dc57ae672f6691bb5adeef2f47e8eb0cf0c5af0d06d479a1b1294e5b3d41c06d 3025fa55a862d8127dd4c6c3d757a2e555e7d50356c3028f6a0027aa786c751b 2 e6ca090d9d3cad0b144faed63a19b9f263b793259f0a84d3465d9567893893f1 ae20f4f7f9038da72a39b4618b1a9032d77bf1787ae00632e19b50051db8de04 0c51d9cdc712d60c0fa724e226ba82e337451ff47b69da5acf95fd41db1d8c09 0 3d7da055ec6d653010938447a506563c9df3ae13096198042e09548c6698710996270d59170c5dcb7218aa4072f43098e30e6083d4ff9b2ee779dbcb8715fd0d0f319795ab1b93983f4b7b3c44c45f7d7a7c329347ee032e35760effa95ada07c57e9d5dad18f069234580ae0afca8ee33e6795fe36f91b6036ac328f70c4805 -generate_ring_signature 2e3eccf3241ad759efd0759054ad22f235e33e882f8b3a0b8dcc2bad44cc13b0 46083a5c582071ccf8cd1c9f33bddd7b02328a1d68cef46e82f41baa171a2fd2 2 ea16dfeebbb925edc360cc2034ed4a7c45dbd9a2ac5c2699cea5d822a31b6187 64c01e0efec188fdb60bbf359b073f613efa84fb51d764cc716938a6e3cbbac7 1742543972b8075a2df32275289d6be641db11e78bd88f72215a5d2376203901 1 7114369d440eb8b3afe526a3302eedcb9b924d597d46188ba72dc1d055f7f30cb9f25851c28a8b25d89d97bee2b80c6db6ee8054bf2a0069dc4bc62d8d3c01056404829a82883f46c71bed80ea6b3037d138111d982c8bd479569e59e0544d0c7fbac7a0fec0c015044e96705dac80a2f1e35094fc9025a19ae996214e500d0b -generate_ring_signature 5324c8a8ad37bb76e86edb6127499fcbf161e173ec4cba0d426e25a26aa7c2e5 a3d91b89186b32fb14d79601878d9b5cf0289b0ac68d19b474c86f4288c8422f 1 ab633b5c1c80ad6bf183d24f4f37eaba1c4776e8383047290503e920ccf3ef59 a7afdd093c3a4e7f64896c7fee5b6db5f6a57a6f14c04f48df5c4a4a1219cb06 0 1edcd76312463140e43470912ccabc72e778ddeca534bc43e01e9a241a2393093ac6135b166649843e0d1193c847797f27844d7f94dd0415dc54b517cef0560e -generate_ring_signature 9ac64eb93443ad9fbc9abc23cd255253d3f61fbebe818fb9436f2f12397ab717 0d24970317c011442316da2d75ce0bb19dbe3c33ce6efe6495dd185ff0b77a26 3 29316431df8bca23ff0440601e07bac7ca40edc4fb0f948d1cb5a4e62153a1b1 3af36e933fd1d37481499f583155fedbec8bd3e131d278a28b209d5d6938ed7f 5ade6caeaaf734a3f6435719f88871b4094ece978b37ddf6b0554aba023ad626 94c2c27e71b84624c2f55761fd6b36a2f0f835a67fd12bf7b892dfae86c03d0f 2 530701a54e43fbd9a70fc4d3421f329e25f12c886528e83d5f692706761678015a73b223c2b1c48888e781103ebbd03d824961b1dbf090b7011c47537b88ad0755e580bcd1829089b3944abac4b7e320f4382905e4137b9a65cced9516486307fdea39b119bd0fef51989cda5e6a390bfaafbb36760e75b8d26b5b658df221010cb4147be2a8f0c79711c499a8c4d4b9f7705bcb2d11e683e02b1c88e69a7509680de59221d1bec92cc6bb0c1dca378bb7236b9d07c9c69b09a06a3d8cd49a09 -generate_ring_signature 733c77641afbcb27266517b8f24262be24609a4729609ac2bec71b4aa86f67cd c2474848d390a19e55e10d80834f6426d2421d3c0d9348e81312b9c644072e79 2 5eadf01c62bb7ce2e1cdf66e49982fccd9965769d41846e1526cb9eaf2d15395 ba67c3a77e88593f8ecf2c137cb0fac4ce7d2259010854396991fcbefde294f5 c3c9808a7c62b5542b5c054bc98c5f140820efb26aa6adc2f48f4d0550536b08 0 e8dd66d54b4a2c6858e3fed8f6f1ba341591a615e323c8275f6e9836d8de2f00cd63361146c735d4513708b3a787a3b1ba087e85daf8d75ae5458a3060ad03041f681c65d4a1e8f81210f7739f42218ec6d0086eab19b1689b10fe46b73c9a056b415a47d05ce4d885d4f4e0ab89abad3884a842d380b67be4991e453238e302 -generate_ring_signature 1792c680135fb955b884861240d9c55b6ceae3a130059fe1b3ed2e9ad5a9de97 ba66d0c9bddf46eca50a3205269fd2c427e40a9ca780d09d68b953511561b396 5 bbbc21787e57aaf4928a2de53ad1d1c7527586f1f8337b1f3f8d4d37fecb35ff b00f959de68120641ae043cc9640ea2a5fa040ee950858c1e9f16b427128a212 c5d5354bfad3251768658323c092fceb2918d8a700557c79ea5fa638c18fe516 e731fbb5657553ddf378c3d60e0b12b1a5e375ba1154c6b79ad6d276e68f75ea 1936e101b0e7612be29fadb4d8a17e3dc20c44a7f065fcccb1f372bd8e3b0c19 747205001f75da90ded4f907a518903090940432f8959d8ddf25413fa3381304 0 b5a9cb708a8fe730e7c0359a12bd576f86b493f1fab80528cbeacdae29d0b80331b61c41b425a2bbecee013ebbc5f041c824878d69c46745b031d9a0a6aa27061e554bed88c5ca0e5be767c28438fcafbf6f556ad87c18db8180a3bf0116e308883cfa9c19651c20d6ccdb8584102e908514b15e93efc61e627b7e970f567a0f564f023d64a02a339b9870fac5862dfa8b4c9c607b1b08df69082d1a5d627100c425b074647aa1e7c1f75cd5beb828a498971f82e21a902005158e0625249d07fa3541af82ccae70c7a518c6ec6b2bda4564bcc4996b6d5ad547137d7345370e44cb5904a70da55543ce94dc98891527e2c5f73cddc2dfe01f4a93ff47e4f809964ed60d15f413b602db2a5459066beec953e823054fb337e72f383cb41cf806d308688121811b3206c4f81cbc4959c6491611d91e246adc6f6d97fe07cd6f0c -generate_ring_signature d432ceeb293d5ea12e583f0e6a858000ee675b292ace24d2ad53d641988ff416 03776cc64fcfaf6b9a23a85f9577bbdd429b653f732b43a0e8f43d67efa63c87 4 200d574e962228bc5b74f2785ca3aed1662541f7300c818a4907279fb763f5f9 4c89d8fa1e9ce61a0f8ccb300b598c795baa7dc1aed979f22fd19958412c6fc8 287c5c314c26c80e50266bfeba4676980391addedde9789756dfaf37f5d5c120 0c294071658273a2f36d3bf44863fc65c8732d46e5f907c4b68ec8aa0ede4643 4ccc770ed00b9eed0888d3d6346bf28324e98418aff1d7903daf2ed68548090c 3 845c2ecd1617260761bd1717196a2c8669c677bf8f6e8c79cf19a6e2ee39b30bf716ed6167eb035a69ea3c152c538c9d49619bc1ac2bfd93ddf522288f80e00494158815deed830617a064151e191408e13444c58ac38b6b4081112958ef820ec24320093871db1dd02fe0dfcf8e9a7fdecde8d2200a1b2462fef3cf6ff5500e28c7ce5d92a5cc2831565dd32a871967c6bab1fc6dfb8482f003a841c54bdd076ac4aaa9ce3a99d2e05e1c1e56d89f3d420f901953ea4a7d0053c80f09ca6e073cd5e3015c6552575e86d394d4bd12cbdcb90e6366ede51342fe7c2f33a2d80ed52686062a619cb57142f56f11f7bcebda814feeb121e613c7c7d7cb14d0ae04 -generate_ring_signature 5de707be4d1ceba4a9991eda597450d72c752ffbf85f714725b7a53086c2d611 ad59d7abf4332dbb03984884318194af02d41844ade4e9f91dccc65be8467abb 1 a2103cde970a000902d7bcd98045d5a688cea83d1a0cdd1bdbca4c087249f11f 6eee07f5e11d2601d7175b90135c8328c797d5f4952a125a96f934aa82aeb006 0 0ac3bcbe66f1cdf4d915cd84756fc41e32d9f2cc4b915e1efed9fc225dace800849900e22d3f62f815ab096d48d5982879840a6cd8b0305d95d7c8fe1f24b708 -generate_ring_signature 1516afdad27c8277ff77ebe9cc6b9889061ab85d504c7678b51d1b453e656f4a 77eeb7c8666282fa03e473416d2b495c62e9b8423bd30aa054cdac22f0173b75 4 294897bcd480edc6eb3f302bec49fa75aa38de4ea0a5b3ce458e284477a4df2f d7b3729fc615104085544ea277352df10f43e24e8b7f47469893f34df7197e95 9e4ab024bd6a82f3544a941c0dd88b2985601df873cfb6c82c669562d0ccb2c1 768ca9255bad7ce9db858bf51271f6321620424e7e24e5422395b10e3fe48a04 7328152575f971140847e181482617d1220f0cd933535cbd8a5492e8c857030d 2 409f3509735458a43ab57e9f84b67fab661fb176ef966e0246935f09ede6cc04d3922426a197b95a99774d89b1ba2b317864e86c1a4ce8eb8afc07e8adcf3c0982f5ec7f6a9741e76a343fcbe223b485eff8c67563acadcb16f396c28b10ad0e8139eece87f2ee7f0a0705934929dea9d44c59ec11982417b3df1041d048c50b29574cfff6d2b82180d0d7920fc83f796541814c7c3359b4c7ea943aee76f70191fe1414a96269d2a01a9959ac4da8aacd3e5c2ecb59fa970fc63bb9040878019b668e6656e1816cd248002ff65eb5ef5e6aa529feeba3d0e2697072ae3b27062054afcc6c476e90e5c4ef3cc41760e0a28953cadc26fd4ff8731b3d73e60c02 -generate_ring_signature fba004537aa4d6db41185d8c4b12b3256048480c65d99eac669d156968585162 ede0caa2b77adaa6a0ff9cb917a79b644d2587b7d3821e5632955403ba4c9d68 8 62c27334f32b1587534ea7810f3356eec492bbacd7c6bfd2cc91b084dcc38643 c6dcdc2bfcfd81b385864f46dad8d1a1f608eb4d5615debaa651bad8eeab6588 806806bbbe254061c70ae2ab54ccb2c10886f792d9b3d0569fa9753e4c44562c 9e6598f7e3b174857ae16fea6948156db8093c3f991bff0da5a2d3c8f302c9cf b4df03f1889ac5a3b21134d6be2deb378a4c228af70a3317218fb34acb3ec86b 00262c572aceb403da34678e64368766aa091be815bd42bef5db81946488b366 c77fd924daaab02e4f95d653c8689ef3d3b5c71fc70825483bf36eab26280ffb 9af7b0c4033b7f3a8de1ce74f80b1ec03fcc3b92f2212ab8ee5a9c68869b5995 404d2882583e961947d1903d8bf28d3ee2fd467f01ccefac2d2ac3431ae30506 7 8ecb5051fdde8c88d715f835becac741fecf40ae9f61855396a6d3dce87c0708f0a69977784b770c938baaa0b21302712c8186f49879caa24b0be0be947cf7001333448fba69748a8976e1d2138bf0b8c35febdda4b71bde89c251b6c362a90b2721613cbba18fc48536e21bdbc523a9395995e51bbb187c34daa5d81c8d21049f156611382a5b3bec2486b2ade41daaa40a6354b29254393c6abd81ae05b502597a9fc1f48f1ea027d9fb9d70a9eebb825ac5718f022baf5c4a89adf4054a0205049df98e5f98687315124d456d4f8356c38f50329bd31f8657c17176d556027a31a59a59378635a55da0a6026fd726bc8557d266c5e5074ec6b17ee2d826011c1bda6bf8bfeb943eb6b14826e5f671f84c297df49b2adcde7d95cfb28fe00ca77973a598a10c75b5eb101a3721f0aadd2603adfc4a0f0d765f93cd796d140a29774b64e78212347427444afb38e62dcfe2b1ee6e3937544f561de6e44d93003bd082bf8feef5c0e1b0301b279cf3e9f2723faa9adebc3ad6c4bdddc296450c7cfd00844b02110de3be7406194dab771efa6f1dfd0241396493f64f296fb6020a6e4a01f915f8b1ed37364d1ea88354465031b5e6bdf36f930e8691e4d9570b68ff6a51e1ce6e8b39bce4f991a85bb19da9ac4e4d90cd2d8125e74bd747ec0440e974b505fb2cd27a0c3ccd1df2fdf237be21cb43b13ca44566f55db9ca1a0f -generate_ring_signature 3ae3d9ef2c81e08c3b85166fd60d74ae81f1be0abcac3690480a1ad0186e68f5 27e97d10d75e9108d8c4e89e43ba75a843f4b250b3873aabf158a0379f55986f 7 1ef624898a6fb35a3396c517f90afe615d97e1beb426a8bd63de31e3f4725ff9 db1b81edd507d1773035e62c458b4597707bd5f7abe52f6503c7e94372699daf 2d0b80fa6db1598fa34207dfdbdded71c01c7a6e0c48a9895d103ea21f88a765 b75766e4545583a57ba86801b55b63f7f7c875f6ccf2e567048d191230901a02 6c633936bec4bc1d110cd8c1ef9ad728d43871ed5c25496493cd5f853e871b42 37426776747c40a3e60b35730a93d3aa3753431e7d8dbb2b61a5aad33ad8f6e1 79ecff41ce5665dcbadad024b05d9e26f9e6c7b9a7d10d6d608ff0fd4364895d dff0e02f69750454db98ea1c0486d8a27a105362ba9b8679b0bf2725967dae07 2 0b614f42376aa5f690bea1258c2313a1900ca5c8bb56f2b386c1e07400bb350b8517475d89b6864a367426842fdec6121244c2eccbe9bccf4bbd5b753e523e023581a049fe5d01444e1d4478f3ee4aebb5d04daf870e4c0a8f22d22b9415c0006d3e8d22af77d16de11c1de7bb9dc6d5b5a3df99535c0be2baac77a5b1b7f007a4a4923d2db20dbd80a55d23c1210c49c3a2f0ca93e9d58e6c904cd8be04ea0090b0afd8fcc4ae481aa7f32e1bbf9656e22f0c53539d1b95c7ff437a9005a50a8e9f2b290d070b33bbe8eed37c6a61082c6ce94125b26266c006a75aadf4c8099514809a9d1cb686b9597a8b49dc46a39b47b5f116d8f03d044dbec4e6c7500f545d39176cfa3b926eb4d997f886baa9f3074653fee031f25669bd96554e350e0d2a1f75aff85615f2b29285768dbd4a5da9a2d97c2537c9eb3b7fbc7346500dc7e3c917c9ccbad97d91802ba1fc02c197a332e6307fdc6525bac4750923ca08bf63d4855deb5fc02fed01641a152daf55ff11c3e203cfa0a1a0e77adf92800ac6fa084097c8b71db55a51b719b9629aebe36a296a11a6590713795d80bd230e6cd4b38bc2217b7c9114cea413059d62a26a10f81f93f857053308b47e0c0405 -generate_ring_signature 5ee5ba303fba3f86f72178eaf964147c0f4728181baf66d0d69886ea57304f1d b252f1d78bbe4179682604610e30821f9a161cd81ce886689a1aa9199ce14c19 1 cf0e634f021ae9a7741780f42dccdb7ffb0c5dd00e99b208872e604210ad5926 ed4ecd59c016635ae665994d6a1698ba6b39989fc31bee19aa099bca24bca605 0 549b973efc55e355e7d87c29f089c58a99dd933747c759cbfa25ae1c1502b40db11ef22e3cc0e5faff0cc1bfeeac21221ec168e1e65dd3414be68e212fecb40e -generate_ring_signature a168acafc3f3634fb3afb2fa4c85302807788e0b1d6db633457c343b83d4ba02 2ed829909d69d8bff8cb268783d3e844ad3539ff90fc6bed846c15406c237801 3 8eb8412c40ec4e21cd9a4fbd2d7298232370a4ba83ac5b0d1e316888b817b511 badaf9725b5d536b6f3a66ec3ef108a4b3115025df7f43e7ab249f3f58059f6a e30cb9b423f01c24bdc64c739a31419cada83692fe0c4d64fcef6bcb1776d592 7e76841378d75e2606cf68cbf75f3f3db2cde74b2faa01a701a3c432ea191c0b 0 02df73f983a07122c4887d2304751cf1ba62ff869bc19aacf956196cd8eefc0d1dfbc359669c9a5c4a541e36e68929adb723469542aba5d725aaa9193287a10b5a7ec45e99ac92fc19be6b40b430532ed6d6f568fe948262a08de15c8e23b501f9ed111b0b532a111f3ff688d78e25920dd0309278a9dd4314833eebff963d03998c3a1d1d5e3b0d4e614029aa9431979f8fd47d2211219e19f638bfd02ad508d682978ca5be9bc67ecca0476e11b6e315f132b06ee88c8327b3c234cd42190e -generate_ring_signature 76c197a029a4979d0388b7ec7e68ee11ccbc66a8c504502adb011f718f46ea51 b1490f09967c786694043061f7b498ddb9b064091bc8437ce133d71fabac2f6c 31 72b83a2fcfa3b21c53d07c617a198a000d5c4446bc05f142f8d42e94938d3c89 bda3e253729f9b6b189e923f6a5cb3b36163b06e8e2e5c1c6d685cd272ca0ed4 a706710f83c922e3aab795c56883186dfa8dda3408ee314f04816e9b8fb92f90 5c0813cb343b1cb1825ea4378014a6953f5884a8250d5766b23a9af5b43d2dfe 506171faaf0ee551b7c2dad4b1a4edac2239be4e7fe76479cfaac59551deb880 9e58cd307a14f6482f0c00d9342fc0b08776deb879c567b53c9a5ecd58d96364 aaeb56ebb9049870bbf2c68ed50c74c74f738c282da33a5897213c7e305826b1 d0e9a71c4f7b38d6e32f455107a814d6ccab122a3d027f320ca36f072f58caf0 a7672d1d2178777608f058280cbe24887cf1c80d54e4d3ce8c6572600887238b e550d5bef941cea97a3ca5587654794b2cc95e4ace85043435632d6fc9e6c239 08eb2a4f3d70a4e70bfb340724fe58e71cf75de3952c474abd8270a1a4145c67 4c69a16fa040d1904535fc5addfa4ea00c6478c97fcf349094fa4cf540fba173 844f1c9064fd2337d653c08109662a533ca1ba3adf29f71ad52fec1d581df0d6 1f38c8da9e1841686caccb994796b0d462db73db8bd5b023f47438da226212d5 b0a333c00453586b2585617916edfb5d7c1e0e41a06638e8131960b9058e0486 3e1ef31dbed8e4ea54aa290b70cbeb6674f2edd2f1e187634e7489fa52bc1d68 cd5dacb09ceae7dbfd47d22450197370322d2e34c5061ff0f886147a4cb3b444 90b87ee3103b7ca76cb968e6781417438fa9603507b72d5bb1abf1157576bcfb a8977f55e6aff38511520a9f07959dfed92a336e0aefcc6d10d21bc9937ce18f 5efc3ee01e44a3a68febc8ab9dbde69bc47f943de706c9e1309718b18b66d7fc cf1fa8638d4113d43b9a9795c37891dc10d1f6bd71919b014529f59cf2bc0d6b f03eb4af7517879ea5d377e49ddaaf2533e4a1e61935ea0d857fdd1387ebc899 763c9a6765b4b5ac7f6e53789392a1ffb43a0bc850707018f6419ee7fe8c7d0b 8a524e3d21eaa69a18ec74a26dc2d2c6f93689abd3fa4ec469a9effa1cd4bc89 8b6581e491580aead5297d1415c29bd12e15c79ed2d0bc2a25246b97a8337604 7540c5547acdd0df7dea03b49e2cc77d255385817bf3cf6ffce2caebff519074 45dd05c9ed02f168c67efadfbffd2a95f0f1cc84f4292826083bd10dcab56b14 bbe9ed0564e875b45735b0fcbb7e52ea810e54fc1f8fc7e1c704a16183f48d12 0d8c787d3134f90078bd47ec104b03ad4fac9bc6d61673c780fb445bed6c6da3 83cac99ef2be476a29ed5a3af28a348574576a086eb5ce2afb477f5ce5aac752 d5d7bcd8f0c14dcf5f621aa4f9dc1670efbe7b82dae76b43698fc21a2c05b2cf 019a7f813771d3f579ed6e3bf4722ae6ec129e1efc8854fcdff5c992a8e9710a 14 17e329ac83f2f37b59d3d3083d433f21ec75f9c5882fdf5be9b33dc7689aed0f85b72e0dd9a69d1cedeb8485e5407ce76b0e978c6c16b37b6be5778de7fff80c5c84f9e06cd95993c582e08778dbd19d7f0d1a647fde2856caf4b53838b30b01a0ec444c64e99de188946860cac2043c9d88a3e90ed01fcc3417d4f8888ead02accb8fe80eed47e600a9dcb2127623567425f2445da5ec4c4eac851eca5732010e0f3e1af6c9da57653f807ab2e62e834508bf5b66e8f3bfc7070903063bd900df315269cbf6947fc41b47945f1f327f4b0c02c58fa911cf945fc338f463c704f076c10aa74f027aba33788ee468d8e500e7f60e3a7bc2ce458551106fd22d0b159c25bcfbec288d346656b748a8e4663768301a4beb98446f8d5b1c2aba080f086aaaa7e2b372887655bab738ab160f0ab513016590ddf2cea2c444ef21160c7b95757054853e5894ad9260844ebe5e7a8324881c2d53c282e421549b76290a629055187a98c45a934196214157d4e8e9bc1b9b3c4c98e497c9b71ba7e86508abb25d2329c2e895dce258ed4960e0225cd9abada365831c94863195bfd9b600ff6d44e90299ab4fb79dd4eed887af7fbd91b8ab70902912e63c3f66e39310062207ab5af0457f95fe94aeed0efd7ad4ca4161db2e588e9eaf8376d112577a048472fc9a64e56c0d13d56b2f2ba631c74f21ca6103050ad2bebbf01d5333dc03f6b2bc2f5a0f811034dfc03d8ca5e84fc060bf665336457063f1794f455df80f4e342ba4dc4f07dd1e50c2c766e8477d9b136c5bd86b38039c7af4fda90c900779df34f50f1e2485b981bb235386f74125ba8203200b89f510211aafcc6f250f4f7c1cd7a3b2be94e616d4d11c7e1f40fd03d5b17c1511a4989cf94594494b0a9b0d59f389a06b629bf9182064b6c475f146f1d7420e604ccf30204725267a03a70c2ad5b95dd973dc5acb0173e0e783981c09b5d7ce6ae429736080c5831e003b24bbe747d0fb032cdd8ac143ddcbd96c3711f3bfb5658dfc40a1f17d62ea0c33a415410a27ef67de554d3739b8f031a50404d923ea6cc1a5fd652545dc8106dd3d8548705a2475898572b2c7adbe187362fe5ffcf8fa175bb5c8abe21f68000e30925eb5a619a906c0af37e917a40e9772c067319ab74cb2b8947562e3ee0bc9223288a0659dd7b92dcfdef5e9d116ed8f0a4a762e1f5bd00a0a0b0ea5d40ba941f78bff8c0d9b6fd9109a6a1a9555eadc12eeb7710b7e2aa3ec637068e506825acbdeee8aa048381506370238ebe975f4be1d150892e90ba5a68ee11a2d0167aaa1c183dd6813b66f7434ac6b89d5b4fbc264dc2caf821d22f1546a016808d48ea6ea4f15775e7ef443c78cfe50c23af7cc75a1f82c36f00cb44579e0260f6eea4c8bfa2541da77faa58fbb8263da8783c3a34b33616c5f42a414dd27330fd30177c260704cfb64f380cde109b41e7c18d62c308761c0d4ade58512156e02a21bbefa94f4a596f81fd03e7a727f1258a8a94f1dc3371bc4e545747ff6b408bd32185381978da89b2717c6672174d40ec10776c50d9ee194fb81c7a3cb09035f2fe071a62778757a8bad5e4fd547cf9fc8f89f3c63c8f8cdf573851113ae045c0fb2876e538bc2be575cabdf5d8b121406beee8d0ff1c713416fe8ebf73909cebdab8b921446232f1cabe3af68b2e58c358b34911c21fb0dc8fd7545b782092b26dbb00a6f16f501d4b81852040e8327c0ffaf77a2b1c6085c3ed45d392205a9934a94780bc7108ce4fae66e61a95653ac4637f5ca2956a4fbc5d99d2b8f05ec6269049a214215de4ec3b0e2d9992366e70f7ce8c8d410146fb8d1ad72d905d96f873f5f74b13392a71f443033d63560757da0135f51f7ed24ad08d28c4b01fabc6a685c85406b0c41776b3d28bd148412b4bbc61f0130bd0cc629b5841803fa9f3b48b5253850bf5892256a063e090a7ecfd98bb00b450090a00b23c69e03f9854746e7c8ca24ac08a8c984457a42c8eaa6f8cf253f5c872f84662a88020433371dac324e9898d6444cf68dbddb583acfb1e433a47b204e9242a9cc05df08ffb379835890138b7e1f430ec40e8d4f1cf37ae74bbe669bf71a09c4dc0ab705a70bbea659b59280f89f11706f2f30ba93078b0a6459588f277a930278c044040584d889975dae5cf2713fb2064a7c22d42ecf9a6172a4e8ab693b6f0248900586da3343de25a8ceb05f6e17428973602cb1968ba0473faeeb1aefa337d717062cccb2057e2383f26335e9f51075e936de57066339822bbf74f6668879603b09e1a9277811b709ec8c0ec972e1ee65ed0720b3f6bec68e3bfac2d2fdc5e4c809475780d60293408ada1efff85ae49dc1ec47146fe42c2715978129792b6c310ddf09a301388aa34378a19d1ede4cf0d4614ce6a7d36bf66123c1695f5b9a1f05bb81bd0775826725a084c47855c5e5c5940e12947e95ca15f6b0a0d3e212c505810a3c3e3550f08d7a27c46fb39927c191252986dbec39f9856fd4448d7df4077026cc7868b7c668a0171c7186b6d28b1e40b3ec040a6947f636082eb0c0c60850ab79d597a7d94f46d290e84621e9f722d05fa59dad7c927bb7bf8d18d5480d30eba39a3a84ab4ab4f95c681472bf8fdaeabbb0ea0ada3043e0d8275c23910e59dd7da4c1a7c421339c9cc69bf703332cfc4be3f2e1a368b70c985c8575b90c4301e64025eada285f6bf3c14c903467c63622c8ad936c9c5ba789fa37dfb9055d26c21b29646a25564fa9bbe6b2dfa0a226f7951a34207ba1fdcd70a4f2df06 -generate_ring_signature 98a0d34ecad7f93a524bdd5bbf05df6aabf1edc86e6dfd555409eb9f12b970b6 9472ff1d85dc16f590c6b034169850cd9a7c61aa49991a2abdaf9924eefc483a 17 808f7fe95ba368b13e3b5f5bf8375263e293056dd4c0b946b15b6811047a07fb 8b7a69805ccfcea3c45182def8cc422c1f23959e156bfb4b94c5fa3392589454 d56fa8d5afe511e42e28e98f7e24aaed8fd49280766b40781ac01b14e402a94f f9e92df33c3a7deaef73480de48f287a96692bb05f964f331e86a7314ac3cefa 44bd6fe3e84c4b5e931d1751bdb85138dbc2497dd25ee0acb2340adabcc3d2ac 5e0e3b4e8610b3ad968c0776fe185d5821b440b1044c3cad601377b92792dfc8 701052782deb012f529a3fe3636fc4e6d4f3c5319c5727db36b7a76a634b983d f37e7aeda09d6da3b5f9d06dab4c809a9a70c58c9cddbfc81178cdb90be5d511 ed952f75fc7fbe0c454228ba22ee45bc4d54438d88e5544cd74d33c5e851e80d 60efc3617bdbc7957d8511526338e57e34f9a8502a8911684d24de2f76a87ce6 17323e276a428b2b238a5c03460a881ecae3ea9622f35add96322ef1b42c1ee7 9e51a49147a1029412502883ee90d0b683925d611e354913af570c8be1549f3a 0ea8252e6e1faf7fbbc70ed8040f9c57c87bb42876ffd8564032776d81dd5dea a7924de7fe171159aa57a7d22a5647f2e968396e6a4e424afbb070b98d0b12f4 f985bff093c1fd67d92b225d00bc4ae37ca856075940461b790c17c1a73debe4 247c72d2e0916bdfdaab6abd05ef5a877b7f175bcfecb8a499b59c6577b93e43 12ec03a07f4f01fcece9465d8967580241577df2d6da53db9a23f5257a62ea6f 4b9071d06ca415811ebf1038c1bad7d7a2338f82b500a3a61c0312d48b09040c 13 a2d2465482ac7454551f85a3d32538ea301d2f32cb3c4b428c0dd55103378a04e870cd03eccc01b3061debd9772cca6f1259e547a39603f18ebe5d98dd119e054b8ee1d13fa5c04b85d69df54573de19aed9a2a8afe87ca744b3a82718dc260b6b859c7b13d87572a7d53badc9b8871049cd657c1125b816b6b718b98a1b150c8e6931617affe4ddde85c99befce437291836ae6e38bdac4447a447cdb6deb0cd81ca227d84304a6a0aa84248ef11accafebdbc99ee3420daeb1dd9dec7a36093ce9753b2cb8f192d8800ea8f19853ceb025cc04c956f940b30ff4ebdc310406cb06bb5aff6ec948f0dd20c9cdf82e5bf971f9e21fafbb2db6c7aa6408780e085741b8fd096ae340dc3989d60c67ad137fd9f755b6a63c8c08704e14005eaf0580befde99202baf133273e0b98763b0e56d7de3a117e1bd5678eb27bf2dac205d67fcf6a84e2d7e0c815765c1ec03ae1eea84e6a12c0d0974c20985fd2cb17007e9b0b78b3916007c94d0eb54218b65066b9753f0df8ff27aa052c4a33dd850dcf0512c3e9e66a1295142b8c7dcf67f01717bf41db7a68a79c5c1257784e820c8944c65cd8814d97df7337843d93381b30b5d0729147b85dea31f4e2be17390e191ab652406fbd30ee126ac09100e25b396b9c62b4cee73e8eea1cdfab1a710bfc9ca7039605aed48d9ab1120c1566c96eb17821cc1eeae3d34848141948f507d1ac52fe0c8e40a2e62a503785b9c5ac1ca18d323fb699bf2342ba9e9e8b6b02d7502950fe8fe6927d4c121fb205e6a7dd82caf88eacf730baf7d650060eab0a999da80da576d7052c52143fe4e8034660d46893f323ff7423089234d589280528c373bab797eef45d6ae7387e76b235a7684d1e1bf88e87b99852a17c33ff04b013afb1dd6a9c6e58d3048950edcb46cf61fd81fdd0401188639d63337f2d03ee0d45a59c4c7c949dfed157d59fd908d434e7e9367911c3d4297c055f5d9c0d76221f92a5ae9ba5e531dceb738c338491efc38b4bbc85a617b39a181b176707737b6692cdadc507538d5c2b579f016feab76d1adc1ee5077d5ced6426952604c81d4828acb2d2091f04d3c0f8eb78eb32bd68c18604eedbdef6bd0d8970bc05b558253ee0c8402a23d18ebf781f340420197897cb112d812501dca57341a3038339235308bfc6008731638e1705cd12143ee4df82aa24ba120d625ce4b51a0cf24e3823f1413355d7d84acfd556c70a0864ecaa34a4aa34e80d4b9a0b3be300f0b7fc05a4db498108ef0f33918dd8dbbe0e8fdbb12a7aea890d6139bb083c06aae8cb761dbe34f44a1d8e79e7801a47324555e3d323c64040853035b97fe1083c54acf2101af949adc7db6e100ff14a4d75df7e7dc59f0313b47b484030dc0c3f98b9b3cc0b7b57dea80f241a9f5b9bc00742bc3dc539e46d046fdbee6a6f08a0afba17ed0dbcdd5dd5eddbc743c648898386dacd3be4a256845b1432a7330851bbda5a2efa599dd72061f053386841693e0b6d9b0e306f36a66c8273114607 -generate_ring_signature ffaebe5a64fa21b3cae1f3c067113c4cf958a4287801c6a10ba0d63a82a16c0c c44d1d1d668711d4ccc3cfa257b24e2ee233322f078e4e6f82a17753e4f5c8b5 1 47d76caa6562600ac2e06caa3b861cccc17d73eb25df6c34308e21876aaa54b7 005c86d012510a55a17ba3300ea863c7179c91e876ee0138f8e987fb4d7e0d0c 0 4e79679fd2b743c8eb30398c0341d12e3041872836d382df1432b06df442080932d634ca472cc291faa3d57561daf4b8da4f8058e72169ccec1534fa4e2a4807 -generate_ring_signature 73733ec5b86946952811b69e7bf7ba5c37587c91f2d34cb2370d2b9be05a22f7 d8e042940c4c896d6b06bdc0fa35e077a07e6a39cb15c2cbf9ff5c6df99b1cce 15 9161d68233642bfe6035afaba1645dae605eb345093bc46f03d8f142d5fb9fb1 128cc56981de785308fc47e1edbaeee881f818971d65a8a2c0100ff1921288cb e3ddb25dea20e2fae36efcf1c8cfe7d3514b371157a39108f4b0e7609928c716 d08ed18856d466f1ef3f50e49d0986e0cc3718be4a5dfc99311070f7b135891c 56cd77bf457f0ace98ecf454913a8b6940424c0e12e183b5bd084c34122084a8 232fb54db3f3758c5329d8d37726dc8bbdbf44403192b6dccfa8d3783f657969 554b67d108192723af618ec6024f9167100dd252cfff52cc9288da122350b548 049a7ded353d5a0ab8661f1342bcfb123ae5c8369c8921b5589a0ee96ae49512 ea95222a7f00d7c6aff189b8fc0c3844e24375a511ea4d62386dd6d5599f165f 558cad957191b01fae7f274202d8faa3b5b71348c1ef4600302a84f112bd2f37 c046f7d11f1a0eb7dd6f25dc1e83c2ee571e43241fea595625b06b18180e64a5 2f203ef33ccbcdf41813ddd96364179f00d04ab0c475744f66eac76524dcccaa 1f427b4add5faad3d81832802e23ae646a2ae862c51575d6e753b35fc87c51fe 9fa5579abfc3a715e844651c5985800b8f77f4b2d2c1ec4168e6817fd97cec7a 56144586fedbe763b3b0f3676cae4e49507c79fb23b7ea08a8b755e70c46e634 5e508d8a5e99e8ce441898c87d40de7b4a0cfb2bffda4b57c89d20065c9c3604 6 588e21bab86c0e3bde801919df4808f87a8e58888470dae51e84988f22678d0e0cd9b3cbfeec9fcb7cf397c285092bdc7a4b9d37336cbdb0ce5b23277e84e50f2d21acf77e1c43043adf97b6f469d2eb7acc473483695654ae26f981fc11a10176b563133b0606dc1cf556a82df8d7461735717ab1b080db78eea881412bf301ce34c592801bbd5b6271c842fcc39725114a257474d6b749158968b96d9cf0020ae568273cbbabf2cb5d2a0a829a51dd8875e689479e5278ecfe01ce3ef6340e79f64f2369f9f08f6c387baeb9a02b0a46ccd3ccc1ef388b7c43cade99f92302df2aff7634221aa5b00d1ec6c730696cb15a95debcdfcec41b71c7688c33930010c183a6994a757bc2a2b92a2610f803d4fbcc5435d223d205460b569532a00476c310f2b178be1dcdaed154923cbc437fec882c49a5e78d8f5687880a22470404c66da44b25903ce2582079bf3e8ce1223886fd4ff46e3ea9cd2d8266b2c201957e90adbda51b70460387348ac700fb151d4b82952837f23bf1a641e9a55e0c7560fa08b4f41dbdee5231222eda2a1213f47e870cd48a38040ccb5fe407210305cb4bb118836e978a9b93669ca7494ea5fb62004bacb3bfea65ba8106b3d3095bbef52461e4593506344a5f9709ba59777bf0e84cfe7b1b09e60aa1297b34034055c9268589b9331a6d7d6ae1890cb4fb1804b02665b9a1ec9113d6d4e6240310c60f144ec333dab57414523688bf2b24aba24a644df56ad3404890194ec402f2283359fb524b26f20f351bca9ee0b8c17b2a9d1b5d80f6f986a6afa7225b022a4c9e161ab17148a6c6bae7977398999fdb184c5cc07d83a63375bc8e46a30aeb170b4c11c7bd7c29e43231422061875d28497bc2976721f0c0e9c07218990840f7f1f0bd02a730569450691766a1da0e81a6d87f504d2934812a818cbc0c03c98e53fd26cff6d37080463c8070368b3565d798eac0e495b65f48af612c9706a3fd5130aaafee562fc7adcc68ffd31012ed98bd073299396a6d51ca12e2d30a08dcece0858cc80b392213868de96ee757e3ce85173367571000a31da8e188021c4115b9958c1dd464307d84cf98d184e49dae66f0287355375b04021b9df50829837e9aa3896537f4fc6896fd2a2836dafcbac08f2aad4e8ba563ba5ac8400eaf54ba7e66595c5b9b47a9ad69737de48a95a26f18dd03048096d5b227185c0d35836e59ef54c28d706d36ee6c075cdac456e2eb6ecdf67742ae71576fc96d02b1ccb339d0f66eb52971bfaa98514be4480f0805a4860b82ed15c909210aef0238991474bf0af9902e6b19d3578af4a16a8f787c3f0f406fcd2db455d7bfc708 -generate_ring_signature 778bceb8b14ed5f0f51e4483bca6ed959b851aa5ff880a59320d30d0e9c9ad06 d8508c6187929646cc7231cb18a959d0c181e5f40e550c32ebf11dab277cb36e 2 dd0e07b025b266a88482d72d7d3122d2513b11966b5f86b0b3190402e30343db 903bff8f1f2a393c6f31dc6672be383ae112737329059fc46090bb1a9d684c1e 4a41c53827294ab03c79424b60a5faec6f44fbade5e01d59228680eeb4a00203 1 43df753705f56eb6195fa39a4c75c47a842e32c052f1c87f84bbbfd5f82829066ad473225b7e3ace27666e293cffa7cce1d2889527388c8970bbc53fddf21f02d78635e8f92c466cc7dacfa5f21dbfd695e1744cfc92d0f36cab18e1f5c48704479596ebe2d5c2e07a57ecbbf4febfd751ef2c9ce62eca04e5bf1f271c833f00 -generate_ring_signature d0af946a7168a2b284854db904594d1ad9055cf337f25b80c56f218d3c0d8dc8 ce5be5577913123659cbafc8e7fe1b31227e976ec379307282605e229aba392e 28 b5bdd837cc0aac94b3fbcfc27fb023126a3ca0308d6f9ba22d24322d5eb01d57 e1b478d03240bee41ee60b3ce5c2875637836588696ea197fa4cc240d9f6ed44 4edf95184182c3034efaed72684168f95ad8ec6250c25b075f0912e2d4f63459 1e9cadb7f1407262d64ef55add50c2f6875160c2fb645c8c8ac96d2605915e77 4d888e5b483d209c20b60c5e5601c42deba7f05592ec656cd665576a0bd3f742 bda224dc161b87b51264856b6478fd355106105b80380ceb36d2e18e5981e6a9 37f0548f3545620b3ef1515fc5b522bccfd8b8a068598495c4d2cebabff9cc02 e386b54fa02234238ca51c9939d2e3fdee81abcb94853f033c615422babc8f63 4247bd9140878709938dcb0432c0ad95159a00c66902c004d2ea09e2335cdee3 93ce5626ab90afc7f16c8fdd870e9d3640407fbc4c29cc12c11108a852208774 a68380092c8ba661fb24f30b21d7df4b840dbe8fce869394986c7ae4e2edc0de 25be966db483ab07c610bdd83a5e26019a21c9cf65d582d3aab40025284dd4c1 ecaf62457619b1c1362b35dece2209aa5eb9ee8cdaf59ae1d5be775fdfa987ab 5ba393fc4c396411dd2e3e93455b31b17037e14f9564d9896de7455a1994ba29 16b37fbc30dce9dffe460ded732c588b2a863563c0d724cd909cca18df6ab50d 4e97f0370be8608c175717e554f34aa4610daba6cb16fb96c4136a57ba230b9f 86244bdecbd028e5be7c98b2d347e79647333e71784a584eacc308c094cafd2b 25839bc668849bfe64fc9be62997fb93d1188108f7565498c33f3f4ea4e1bb23 429f58dbe7f3eec5dc1bf30ecef4293381c828c5bfe7b7d7bdb6acf942cf858b 2e852cde3f41675c3a1aa901653dd6f91229fb5349b59b95a86669b806ad6d49 b0a6aa5f72e0b404b831b691b29c810c533abad713128efc640b78b46b8bbd6d 8bc504d0ccfc3d825a1de31f005b02e5d7e8396b213ae78dee2745ba94af12c1 4653b7942cb6e119836df837d39f7b43339ae6faba28856d987a50ad2794b292 454a9c491f09eba303fd689918d9ae6ad662f8adf029fb687dee072d8809d26b 861cd74d161b68c89564fe7621ed75778aa4fa86bcfd60b981b8fb5dfdcddd16 da9d169079abbd9219fc9d1ce674378a218433cd3058d7f48e71891724f8e3cf 90e52c87f417b04bdb42a50cd4344c89d2ef87d722ac451d8ce89cd148b9a69c 6226f09a0649ce2c264d056d3dfdc74cc8edca34091423845c78bb691bb6d89c d06d01f7eaea9f5aa3836471b07f66cb15f9abf5191f03245633a73abac12609 25 2aabfc4d03e799b122f1150962ab96878cc75a53e1f37b83398ce0e1d781990e4d5cf9f039e960ea5b4d0d7f28f2d9cc1368476ab33c20608c6ebbc9e32d1b0449ed2d46486f5a47aa99b4b27adccf192f8ff67d35f50580c9145a63adb9c10f1da65941a82de83c076778db6b7209c7d2b9e2199f63152e06ff4295162b14074d4c06f69ffc4457b33b7252461e5e1a1d1665e3e03d6521658ca66e1629980465d29fc4eaf31e067710fd3fd3380f3050e6e785ea8c2338bb16be5ac4f9490e9af2bedde4a947a2dbcdf14e09304755aa704aa618059abfd14db3a173dbcf0044b0002bcea993ce62c12e6626848c5c49ca5eb2cd6edf76386e8b0ad96dab084a387690eef03de9ae45a87d7704b7eace2d781bae18a99c99f2b8c306443a01cb00a9943f013dd1b1f00a21c3359894d1b146878b253ecdd25977ba7d2e510a3c78b70bc94c56b40f4f8bd8e54108b7b3793b7e01d4f0c401c29f75c8608c07915229b3f906108ed1eed4f885f9275a1932c6a0742e8c92d0e7c6fdc098fd02bc0140fa0c177d74551c29c5012c1b8f8b19bf9b0053b184d1f150de6c75be08fde07c0215ddc36c3b9f3f12971382c8cfc5baac60de44b5479d0b97be4a1c0cc290995fe16c011a5fa76492c88e7608ed82d17606e4d772394660a04a810e059ce4f1f81f0b5b2b39be07ee5fd2b57ff87853b8aff3d7394c0c7a333deddb0a1e3fb336f54c6f40d744d782cd0c7c5592c0fcc73ea5899d5f1502f09b4dee04cd402eca7f076d2525d8758828e30811fadb12f3d94bbbdf2affeb73a4d634094d7b461e507d71129dba6dec68c99f4033390cdbddc0b873db73bf036e5c0a03220f8509cf212ca8903e23c96712130123c9552dc05bc1de0469eee101ad760d48ed8c32dd752e96248f4d612ed79a8472dd8d58be952b13aad203aa9229bc02e419ba1969ec7436e29a8d712c554d1ed06a386080a8a89760ae71303280140526b9e46e3f9edafa0422d5a3f5593517bbaed51e239981a459b96614a4b7510b3c80f95477ae3a511044136bc7fe08fc8476fc4d3e0375ddd487f7d6af955b08187e880debacbdd12fb2c96bc3ca874a5c3966d9810d47440a57e84777a39e01121c6cb7ef3ed1c3c7e973eecf5511b720f1bbf13fd89d791a4626268f6b8b04e3f8e43da5a94796726b1b17aa31087a20371e01f89769135c9ade46f6d0b7094b722c9958f49515bd60952d6e1d04025401fc5459c3e2c7aa909b54c0756903a7249e32cb6b63e9756dfc33c7bb6696412751ea121adf3328ab24dfdc721f0d6d216bb0b4cf9c1db96727adc69e0d31bb06c0fd543eaa77161b920af8e7a00c6db6d30a4d8639de3baa092a85e44b0b1c6230b9536bc2c5b7721f4671e2ba032011a1a6d61a40993d4563461e5cece141406838d9639026dbbb6ef43d8cf10ae6b122a1771e7a26bc357179bf8742fe3ab78fe44d133a68534280f08b0f9f0560b3153fabba83b20baca22cfe907bf299f1db71997ddb949d85d66bb7acaf0c7d44ac6ec5cf0f5b8145e44b97e490ab2135b7db704ee8c446ad8f170850ad067ba77a9ac25cbd764ad2fc2bac4fab36149f8ef1d92f2d2fd8462daa2d143307a3d2e22989f2011af9ab524dda6cc51c366b4a63f1761bfa0799622a864ad403688369c0e9e136701911f86a679728d7c49f590e63e8ec118939a3edfaabb50b959690c9f8b7773b3e59212796740eef3efea01bafe0b541e9f2ed6d8c7227005ff6f638d276f2f3e75f49c3eb3705399bd54d6f0f5c5d3e61d18459f9535b07c22b79ef3c1f8e919ffd8112c0435c90c1e3368f7eabc7207f9df8f3e55c3f01cec17fa860c39441ad7339394e3be01a752a9401b7a09427146f6e2b7e58d90c026ead63ac907b72cc59726ef4c8949a2c0186f6d945f7ed49ecfd4418d55d0add42d1b45486da58cec20cc70117ecd613ecb0b31eb4da960b10cace94615708eaddf9277aa7cb4423d9bd2dd6f571017899d6dfbc541541164c515bb92c7a035c151ae2cabb75a3cab34eda53fb192c0857e16e488cb950573ce7c8e0bf760764849435fc20419a4a53d3819c51b981ced3de94a201fbc08692f6f57099c9028a6fa568f5a7de4f0a1297e855fb23b8726a2c45c8c7a600ef5343d7563a170f48b84cada81e9454beeaf01bb65c1fbdcba7a28c091053b8fd34c40e0fe8a6049670236fb509b808799ba505d9586e5b1a339d7364ede4f36e3cb3e12137440ed0a2dec727513f0044258c0781059796324593b3a1cf96a755e98158fbb8d80c76fd91fc4e0b9b7cd107bfed974cc49496d0b350112f7bd3720dfd92891c0a0fa60f2661497ba2cdd619a61dec11a9460b1529a9965de008da1fcf0e7c0aaa030b4f83466b208c16e27b0fecdb5f7247f50688846f79f08c488c5634cca69b0fe707fa74cc23631f5089690b31e04c5b3cf8e095ea686d3546b0459870286b008df432be097d28c798c30a2f8e711e8ca1b771f6df17305b9261d1d3c7c78f0e -generate_ring_signature 32fda91fee7ffd86261cfba41936bb2a57a1babc1e41c93bcce51132d3e9b507 faf0d72af0aa2f1935f9dec6852902445190d777581a778a7e1a92c1bda327bf 7 6e89346180becc891a370752d14a032274b762a037103e634b45bfac60809226 97d5764cdab85f5c11a86546a36a96bae26bd080c451ef596daaaf5ea684086c b60d1e57e2d34be3512e8a2be4b3d00ae2fae3632b5f9b0ee1c71705600b1c0d 3f10de099eee553e1b45f8f68a760d6516e2690ab993b9bb75d57a09a0556f70 070eb1a8fcd9732847154aea827954d8eac4034de82aa78cd391ae8c8f395e19 49ffd7d2431e8db5626d37d6220b630a431cf592bd72faecd522feebba1f2291 cc1127fab637befc072b81cfc583d2d93180133cd0ead88efecddd89bc46e4d6 84eb3ef42330604ceff1a7e02b17de15c717f1a904f34db6c724f1702876480a 2 a71ebd450b921983dd9f5012c2be6fc8a84ff7f1c7d5774da045f913b12e400bf3a34a9c0177ebb9bb6e4f5f7985c4ffd8e9b571510999fd6bd9c3f74db30005f3d0c2f92d79621d2e796cf82809fc44291fb86ee5a297a191dee132f7e394000f90c61e8b7fbff096678e5765b20c42f22a3be1e053dc0e6ca4de15e9211b04319c811242b5814e2b9d8b9e77be1e2de1ea0a793e7653e36a969996661d5704b4bcadb0dfa0a0f5e152a216f7c2a4d5aa8ae85f113ba7ee9e8863a7edc9a2059c1be1bf081a5f31c981930cbcbcd2790fe8ec1cf00f5022ad2ac7fd51c1c20b520db3d9b5d6f30af2ff2baa2cdfd1984a4ba744922688aa8b313896b547bf0f96854547f981039048ea97a531eda340af60836ee79eaeb0c275720f4e8e8b0aa724f4a2ad75c3e305be841597ff93cdf7cd585a898b2e5c216e78306d520e04ecae7c883c256bbf7b0a451bbfbab35df144e963514d9aff4482259a6c5214026931a8f2c1cba9a949e63087d4133f02d7d0d877e4bf39eb79177a43096c2a08c8c845644300f1ad8b253fd63b874e4529083ad6661d9ff2902b5552d113b30ed4a6b0e5007f2103e2dacc345df4a3ba534b7023382a03a4d7bbfd4b9c93a002 -generate_ring_signature f95f58b92f28fe5c8cca37b620c77466d564444b3c4b27bda95be071949ee7f5 0c7a668470179291b39470d30af192a98277cd4c831bf6fe9f3501489ddfd8fd 57 9e703ea6f7136edcb82cedcbf43a68c6782e8f1e57c5c2303b4131d1d5215b78 2ca97ab38e7199a22fa6cc16ceb84711e9f2a41a8f997386ce5b60332e752d32 15cf730ebedadf9fff98d1d7c42b42328259a37645d31ac52a1ef24d67208fb5 bd41a6107e68625847b3eecad7c295f58cf612069e47eb4a713f707452687a1b d95b91d6438f3fb2cbafc7977aa1c84e9869cd78915800f9560d3f0274b2accb 698db750d8b5ca89f0a4b0d0ced5f429626aed733dfa772cfa6833c2b97fdae1 4cadc08cdee26ac4f3f3cb932ceffa9ed5e936df7bf97492fd07bea007d5aaf7 5ff610158afae988f8cc3731b3f0613d1300df97a6822626f845d8a73f14d839 46aa6f92903c682a2897c8d73559f1db32dab440f857d9d3bee4e098fff99656 20bdf6ed73381e3da67d3a8dd62e82bd51afe2f750b7ae124b08583c450365b2 640e7073de6dbb3a8d8497c4a3f9c60332b485645519b2283a238742da08fe46 c9572e5cab158cc073919303f7c3288c2245c49de1129aae637b38c5bb62303e cfbb23c7d6ddab8a249ef1e1dcc823e9bece5a2eada6ab13fbc1242131daa612 8afa786d231a0c09a13d63170fd8226330f81f708147a1f47fee7557ba27d3ed b365ca81fdd650d274f1ea94c492a4a65e64adb7b0fbb6de9528782a3650118c 10984b7aa7bc1ac1e54125f0cf87f802a9d64ef28ebdf3dc91913a4eaf8aafb3 cc880b625163e20cbfc65210c127325e4b3a17af070d1b74126c7227bfb6d065 0344b564c3e1b91487e607626636a5027dd2a9590951f55647853ab8a886bfbf 5c568bf19007ceeac73bbb66d53c2cb062eea710fa82590f76da6845517aa8f8 cf31e75da46ee36623789933c452326df8b33609ff549b6709bd747f3a973ea7 c3af861bc2c38f2d94889e5738e4a756b231fd38b39d9fdfa307a6af6a26fdf9 3dec71fe173c27b02df24e8989cf7075342710a13976e63bb740be0ed3cf8881 71221e35d8ff84de6fbe8a8a23a6c94d4a2e5c9dd5e0806c337b78b40972d968 7433536546f072ddf45b1cb55fbe68a3c3b6154b4f43540974df4c96c16df3ab 62b2725a6f4ddd4e5bbb0728efa86630b41d8ab848d746f73af95767fd57fcec b6f19566fde140878f4f1fdd0f64ec397ffc1ecd66d780af774b35ec329d2139 b1e53d524b31832419ca5ef8960245fcaab7cf1ee7211f3de92737fcd32a2256 156c9189c4541514c557e3767e3ee07429b397cf1cefdb7eee9c978d1b11f6c4 4babe5081a6e5a1dd8ea579522c8f6a44db64bd48229ab6ba7f9758990c333e4 3f6ee7d39a89bb34bc68f02557cde48c0f1e4ec9654d65bd704637d16fd26721 4512838f751891c81732898eb640c8512b6a77cc7d63a3e61ae0827b7f710627 a69bb4cd67c4d72585a8d3db4000429a3ca1691f2da44e01ec0113ff72dc895e b737c73f3572c71efe7cb285879ade2b174b65cea150f180eaa17014d7320cd0 7d472b19d3b3a828dd67749dab91cc686c0aefeac6f10b021914362e9e3db34b f6158b35f8a7d3bbc700d39c43483aa57ce38c8e6ca1195eeb87c673e5c2eb48 5dff3f8b3f1d276f142d69dab6f425f4f78b1a18182d1ad207c027114a694b13 e54fb93a3f15286ce5f2284f284927eab88e48b8213c1a41eeb586ed999404a5 7dfe099cad93d510b49cd166c192e813c3e512a62dc4ed11d9b9f781f0194c80 773cab6f26db9c2c36da474052966fcafa308ad649ceda337f764428948abaf6 aa7946ff4d12c48703c04f6bfeec86d18c4a854109739b3863e4b5007999e2c2 b386b4f889db963dc4daeb14893a3b0b6c3b09a3920a1bcfd04dedd217e573b1 0726fc793b0c7493cc0439189bd82d5d0c3dd117b9aa33d6a0d9553de873e88a 93f9a1f0eda3a75467c99b01005ff16a4c8b006649f77533cef185c4f7a30b08 377b1b8895d0b601d45a5dd36b93eb460ffd9e30a0cf1d96b91f7f189f4d8732 5730b7cb7e779695bd04de02fbc180f8a7b1a7e6d0fcc066bb28d4c94b3aa021 548678ab5e2fb6d870d6c05e39b3267101011c0a842f373b0c9735313fe7c207 788e4bca36e99ac9bd39e7e4188f88b78dce485fd8ce7408ab5d5588a82eea61 c7fa217234c15a4b2af0aca4ea58af4d3762dcd3b17a7be13ad882ea91539416 12c3585925770b39fe686a8d04ee10251d504c94a6c16853f7ef1a6f0a525556 f8748feb866849ac48f120940c6731791a593e5973c9228693397d8e3ad65268 8bb30ab41ee5221c65b21919b8a784de26c55983fdaa5bf635a1156b51213f77 70a7fe521146c4e2c6d0673e9e31f9931c0fc79d516439422dfcb86cd52d6497 a85e0367501bb37a0c8d92a311d3b6ced453790b116246c9c5b7a3f6022d7dc3 062fc5d81991c3e216ddb0caed6fdfff321d73c2c5fe54b9d956d18cbf7b7c53 fca490659c54bb57d385e33e8b67773bc912ed88506ad07f33e4acd1444ff692 1cfa1c7683fd787875092bb812b4064d0beecac871e1af17e72d359f27e2d87a f2a11397ecaf7753c18f0a6970da0573a2804bfc4f48c099b29e71ec6397c2d7 25a78e09d6d2bbf9431db8891b048d8429eb7768ad2fe5f2482a76e03683e908 5 f18a32c19da7e9735604d58edc1cc88c3f81b5a9d8f3122bef969a488c61a50a8bf6dd50314f600be74cf1889b9254ad958214bda3f849df97f096541e15600b68189a49b7969c0cc3c0b029e24fef03e9351582d0ef9fa709f0b5a17715dc08fce5772ad6f8842226362775033b553c2d065ba6a0d63ff340311684c1fb23046a5e2af21d467dcdd4a8dfd8d0414d2eed3c741c8f502c8e5b230f4057491401d64fe13123a8168a3df6f0c5fa729e0f19425cbd8f15843dcf7e2561b039180c37bfd49f021e97bb6aab6f07fae225171a98c19ac3892a3425d358d412144d09a681beb7957425460e151856c0448c55e27b9e8735e55bda89a20dde6cfbe8004f347ca14cbcbfc5097046d8d2d0ae0ef2183c4717673757740755fde4b48f0fc30422be2de9a90bb13949596614cd2d729423b92f2d69671b0b0dc4ad7d710d6b36750242947b8b135f0a93beb7d0b0cacba95005dd3942f4101eb32a0df50b2d7a93c87570a36b5c8bf453197ae7fbb2bb412a5da60b94f146b441698948055d186eb5898750a2b71b4f228192260dd28cdfa77824f948af2ac3babb1c490d97cda45c8bb5e47394eb4a70aa03af6a3191828f8a49cc2b2ed662c460702b0d331a78b28072e0bbfd19ee22783d96f32516ce00c3ad6d0140bbee1d4dab64086461a55ed55b2b5735d742f52c16d8c291fb77031b056a0fca44da3f766bea0bdf0bd43d59d18ad22769a111e02837c39c5eae0b295952d4bba02739e13ea00d6e72c1ad74a2ec80ed83e99d36baefb3842c059bdcc37788fa9dcd8931daa2019197d29984ff638994d8b81384e604ff3b854cdc59f716ef2ba27fd11ebac302e599caf3d9f9e976daf90fa0e7279fdac575eb1a89b0c5bf228136d558691c057257be9430377d158406c81c0416313de76944f5db158d17d3a71045f78f8703a8ee8b13e2b8a9e08b24aa03252523265ae9e2014a3136e322715f034af1ed0983b517a936ffe6d41483db4738e576dda86323cc8b3897683f5bdd5a7775500df4c9c4601a09c88539a1b67e78e434c73880284ca86b06286bdeb014c9d2510a3841625bd6b50bfe4f8ecf5db8bc9dabd641ef1d7c0e1587eb7b7a19404f4903eacb7b8ad5380e9933dd013dc0d085fb077858259fe4dd22f66aab82823cad080e811ad706a78f6c8445326d2dcd3912343e3b4b5120b0c5b747323761614a01ca19ff42e9fdfd777eff4b6e227dc0818bfab44a6f3dcb10563c7b94074a6a0b9304613436d3683a2084a702b869795ce3ffcaa8afc15267b0e981531f1bbf02716870f852cb9d78eb0fe8f8cf6c487138fa6e29aead0564d889ec546dbc260bfcd3ca4dace75d3f842f05888c8604049c375236114bf373aa8456c4a2bfde0c453ff593dfbcd45318d800f658d7c94a1c2a63e94b6a5b739762a3f0985b350f6e8a2c8520d5bb62f7e8b2d40e894162d79f72822a9cd6dda2a1a4828d12760852039be555ea51027b59cd2fc4a4ce0891a2bfe06f30be89fc7b984620b4ee08706d8b79bd7f85663e842980e6cc5313d5ff9b1db76b90771426ce9d244f0e0547aee34aa6881ff594d97114c2fc1c90ad93dc945c202e013eec21ec79b0a00b583155550bca79f90cb3d651843228257b6cde96f1800fd9f1c50be4ace4720fbdbcdf139f71d88eda2ef00052f059144489f523c30e7371595a98308a96a10f325a3309f06eee0a6f814429c680ce4c10398e1cf4cf2979c0727b233c82f20768ef1f1824dafc98758de8001fc077ba58e24f142070a01cd7694ace0aca830db62dbfd48ede97390df604671d4d043dff6ad899bbe159726c656128ef168400181967462722471595003903a7762ad5ff602e266662f7acdd107b72f4c4bd02a54e3383d2169b0215ccec97939ec2d75ab7848a9b914f8f702605066d1d6a0a0377dc8969b20ef4b33603536b8569a83ec3f7a712c143ca6ba486221aa8610bb051e81588312074f5d950e3f2c7e4e4c1f7055aa4cdbc8bcd4607791d40030c1dcd881d31f0bf01225fa288072aa702cd05942239441cc47077b1516ff4670866a04581d1f5a5744e001c81986b5ecfd5af3e58b0680f27beb6ccd822b79207da02cfa57cc0ca6f6392a4ef40fe97e1522f27e18bdac3c1eb4770947019940de76c1e74975c69f82ed6c9c359814b4961b51795804722ccbad3be0444e7dd0271367b75a2688c9a5742ebbaaed0e088df1f73f3ed9a4795486690a2b77b720908d03bb89f1a3576aa4a8bc7f903b22cfc1c5429bf9a9709251222acb4308b0f54a3d9426f9afbca76412d45bc6361c060f3d07467e1fad2bb211d352272d10483dca3720dedae9e3b12794fb0385b3aafcb1734890ad1aa05065ea8a843bc04710860043ccac5f3f681e8251cfc44a0ed62a0d79e895b308c604c6bdca2ee09967b3c5065f386dce418321d4c4ea65a8ec4d09e0ec247619b31592452ea4303eef30acce6b55ebb3f5fa93cb89e1b6504010d6985999abb4d5dcabc8fe7b50fb2a814436cb2aa3ce8846fd11f87c64921476a0952220f909aedd2c1e423300a7f47f043b5d1d7abb2e3e1b8e5c5efd2cee16096e5d3ed572d10d1b012e48b0e10274cddbfb4bfa8409eff3f9f1c675c655a9c97a63088a60eabb1da009f1a01126cb87b9ec081eb1b18ce6430ffacb6f7e07ea63922ed63ceecd399d5531a05048a731b8745d69d14caffc5d6e98fec5967fda5acef3b6692cc8778339ed50c10f8bb6712ad411d8375e4b2e82f2d559aef97836ae7c9d5a583ba01ba58890501098359700bd0d9509ebbe17dda326c6e0d9fd4b797dec3fa5535f811d63d0314ddb87724b3a03ac7baf99dd1efc06f50ae4556c4c5f588f1183204cecfa30e87310706324f4678a803ca6399ee2ca10a9c3bc83b914570d150d5062db7fa01ec7e2597d73c4b31b347a52818c03605354869cd1629972d17a4835d87cd4e02e501a1cb253b35bff1c78eec0d7f20bf77cdcc59a9833a7f86a0650749872a059d6554b43af5fbb4d2998d9ba2934412a37d9a894d743b6b853ad109e995de096c44d79a023c2f8fa4d6170f7e1a7a9a9e4afb38d3756a47c0f1fccfcc7ed00af51cb6849ff882ee433bbb8437bdf9559e4c261698061079d278d7c1f3adae0fcf20c83eb5d28f22927bb1e2561126cf744fbd5d71581037447a1205586ada053e5b2472aba637e6bebd1c5292961aa4744a4028dc9fc07c273a0b8ea409de084dd33b2be1c475a94b2cb5acc7780109cdc7fe36fb663613f02ebcd53b55ab078c15f935c6674c934d917d672c375d7b26399b6fd91a40577a5183da5a1e390750eabaa69c3df7ccb1190c497643ebaa67b1f4ddcd0d935e0d4565d1bd5031097c786233ed314573c743a517013e78d7ca6358059da8a4842866b935302c1d0f61267703eddcbb7a10796cc1024ad23f4c18d58360ead88cdc3cb266f9987a08ea802b36d7db9b539b4d10f4a1e181210fc621a4afce15fcd13c60190dc63f089c06c762cafe81efeace01d60b1e9b9cc2da0eeee38deefe1cb6b6247972560298a7400bc7fc44ef76e0098bc7db0664db554394b863f71fdf9248417a4f760fdbdbd8a75001f2784d0358548bdc2e489f4c494b7c490a8b2464d3be88c9d5035e99d8c13cd4a87b4524183589c79e1a1dd29277bf7ab10115cb67bc89ca89053f710de15612ad2405fd3ee8c59d4a9c1a85a0d3607219315581a2875bc9bb00977c5b5cb9cd0ab2b9361228854d7b65e352c3425e91157601b4f0529dd6f6059f7fafe2f25bc6bb89fc55dc2ed841ac53dfa2fd562714181e87ff46054c120c0f8a96e3794d076a46f3c95aacbc6f8f0a7782959d66d54ab958f4b556231e0d2de35753f07ab896daf40e1d9ce957618b3af62665b0a72a9d85be68e6f640029487cf256bc3f7d6629534e9c2eac72802650bdb24d3b81d1d78bfe96ae9cf026020012bac1c4cc943d22b130a13455eacbc37fa3b8e59a995fb97aa89146b0c23f19ff58a4d84abca01fc8802000b630237b7d93a6eed862a43c1d30d4e110f48ec74b1f9ea4bb013becc0bfcbf1db35bb3e31be5eab7832407ddb281083f078d8b2d07e6b2a49197f38a4f46b9d0d7035b36dbe1a913f0eb01d24fa74703000932771cb325058d8b9e22e44262986ade9f9417d41514d6cc892f3bcf8c2605e86b8dc8f2b9e7c2797d8e6fcc622129660d205e26bfa71923816db271b1bc0aced03183098d3250bdb63edc9530b46c43a03e61536a687bd1a5f226b9e74306c2b14782dc0eb4f5e3dfe5e4dbbf3c4dd2cbce186dcf1d88bc3de108a93c9902921cc5d82a61e7683c615db27ffd7d532172b5f97d98fef739ae22df5d70a3054a58771ed3cd7010f4767ef2f1684a459b7cfd876da612adda179e36fae26c0110e38aa8566e21fc35e7e6ddad0b30bdfec3001a4a9236fffa9a9a720438ce01880d696a1506710bc69566d54f374028348182328ce24fb65ae7f2b113b4f005e8a1ac323d8f391b17af9976c06cd20c9b1bbe47868f3d895b3cd43a78e25906c18d3c65c4b764004cd0d9c396083c793398535be96346543833ff3f69b30504848bcb7ddd0591d713c90e7b105fd71796f0b4de360fa346ade98a466457ee0e24a531b71b891d0fa8ea5e732127f3616b5432271664b334d2b71ce31264800a097bc794ea15f2f14994277d2989d49244c626f5843fb667bf8bfd4f7f8657085775753cb6a97727c4f1cf7321f240e0bcf0d9b404f9aeecf524d4225b34b1075481ffbd87ec20150963d7587de54094e3cd4a6c380fe76f7369ebc9da61e302a6dce52dd5839f150ca5e9749d8c2c60de249d525ec3ea9921847f8b5fa07600fa947c0408bbfcda5db4ab9f32298c31284d0ed43d161ea9a2f4198835b63609fa308d080712487207c5177026c1b04177a6f79d7da0ffc2a81863bbd669730fdb8f808db871df9e7cc34275e692528f60b01ff3c5164ab777ed6271691cd4063388b1a9e3628091250961c91d3067ce25a16a832f193e1318bacfcc6734c20c535494b00c4d205b6a0d0b11e643bde1b376e7c0218c49207649588fbe64870d8131b5c6ff7df8420074d319797977d47da9867cd4f06ddaffb06613df15c809 -generate_ring_signature 3f9d1e8c4afb644f791e59e8cd50f02ffc79029629d27d18462bbf999fa28537 6b176e3de6c8e0d8fabe2e2552415c02af6c8d2352d99c208af199dff42d0a89 1 0870ddc2441fb0194765bef22a548260bca7fe9f8480a961f108f601798dc115 8c1cf72b82661277cadbbedec6b11e35f7bf100ecee3cf1fa9dc46de1d9d1201 0 082136646f5d8d99f59025147abbf5ea4a671b01ce0f418c5d2855a88f5cba09a423730b3587e5ed054fdcffe176190361bcfb19ff8aafdb3f7b74c064f74004 -generate_ring_signature 21021e77c02b18cfd15c18a1a6a46fec6a5473f49fe7f17f408c9c5ad3bf9f77 b34901b76b34408675b429898c4c3adb74859e3577bc512353ce51e338bc1b80 7 bf6aeb0c9fdc617e09d5fb43f98f77c0e4db61b442411f17612d8f7d68030d8c e3134f86f35ff93536f816ee7c71a22658f0922dd6d977141fd3251117adad6d 0351df4a97e005521dfdae09511278b8789a7f095ef151c134fda3d71db05438 31ca8ba0c25b68cc7b0cfb6d37ceed81333f87469880e8a4dc431daf9b3213cb 12887b59f4b5af889620abf19a52a7bd1973e04d7b949ff683dd3da922457017 2aaba178d00eb926ecc67d750d6ce2f10402e59cacf46e1d60cd2bd2dacc7c06 ab66bb5959bfea90f0ab7f1d6c78b6e380a8c91ae5b77ee24357abfe128fc625 9e682a0ce957782de7ecaef9010f9bf77483a51ad18b513a8acf4ef2c7562600 1 a451b12db182e2cf259e95937107c10bac6b631b4da80d62cfaef01076cf3702907eb478cfbe336eb433385ae9342f264a142be6cc16f7dfb32b5f8a5fdc2902d73fd5dd586cf7b1e657f004ce85ed8e5063dde75c2a64e7854ce9cbad66c80462f76732e094e77ab39f571cb2a43820619c374b179249d9124df0b3b451aa013e00d0a83a6ee0a9d8e6c1f3277b60d90efe76af0f86b10e222e01ba2e3c4502e1452c4c9bf630730c3f328a77c047c1923aa4529117079e99df869f3130660f39f633527c16af9b521068e4df354a6eaefbf5756f196df4f7b93f90d046ad03937be0e6d35bfe4b3fe9d6a625b257cfc83e4fa875771b39208b3cfd68b3c6095ff30918d149a3bf2bd4417c3dfb06b22e536b817e227345605116b153e0a3087c7cb67a94405025aef068322df11132ab228c46df0c34bc98942cf84bb02c01e5605072b4080d39e60c46f7493144a490511dd01ba081d39eea1dce58a46203a3094532097a3161f3b9106645e67f4debf15960202230c7cb78d6c5938cbb05fda3ba70ec96d4059ac84f61e7d152c1ad8ab60c5282441b699c9c2f727123084f12d83ff39c66b90eb41a404ba162886b78117bc834d4590632266b20adb303 -generate_ring_signature 953630fc2044cd653f7a0ba4d3413eb62f6e17867d2a24283d65c3ce6318f887 e901f99a3e172a3f7de853302c9ca5a1ef79f5762c13ae654c4259e8cee66e33 4 de453f88739ae0d8339d5176fee305db6c4558ba79cf29f7cd9d578ed6f93f73 2662a16a5721e8273db4eba161eed7f6c55372ce1b57ffab8347f94ea937ba77 9b838a770120cd96e91ea3753b6728aa50c8428a6bda2dd55ebb2ace9abefb72 7555929a6e983390a6d99b9fb21340bdea436104456f7345e6319e772f17fa82 47baf5064573771c80e48605222461043b10259e4b1d5ab00ee4c5109eaf0509 0 39b5ff90ce3a062bbd70b4ee11341b7e97aa31b7aa09ead59160cbef81254e0523623ba3e00ed3045db3b505f3eec5bd239b37fe944d2be3b4e0823cac7e0f0f398f419a235cbb36523c113d211ca539e5aee6d623ac2b1b13731ee0c2be4001e065dc32623e96eebb01e32abb8695e5b7f0331498f6a0f725baf15587234e005a9f0a75fc1c418c26f0dbfc94fd80534b9575f1e29aa1eab32f85d86b28cd0e1cd72900234eee3a1d7999e80cf3e2ccac676d9a59ad71517f440ce90c0300008f37a6a1d366322d573d227c295ecfb2b2a902c7a04653a75a58064e70707e01c5fe94837e8c7a18aeb9ae9f2a957a528933f39e1c2d1c544fc4a5401df0a906 -generate_ring_signature 389429172b85700ef011454e82461e30d9df22fd1db8366c615775683e19d8e0 999b6e454561a04399883e2349d36225d208c3426018c8a5140b5ed688601c1a 1 ae6e602587bb3d69854b8ddc46a38071e6987cd8dbd8e5317bc22ba20e568653 1202a2169995970dbfd88fd2d6bf35111e1a13e6b0f32b8bc93d3b0afd206804 0 6e0e7a04af181eb79f04776dba2ce356f1014b3609e9f30f0ba8ffa6ad9f80021d3e65ea14d91e2082153ea8403d2175da81c0b4a90c52a0bf42a954ad968f06 -generate_ring_signature c37605f12deb3cc9723bfbba6a865ebf42b6f55ce8f3d6f097771ab2b354f3b0 fae39afd0699fbd2a2ab9403bcde544420b66224cba89e6ef4bada7ec9f7ddf2 3 7f973eb742664756b20e8dfe86e1e12a62ea6cce44e987474d3f0d5fd115da7e af79056ddd39d65eba37874cb91566c6ce0a60305380f8007535d2e7dfd7d8fe 720f9e325b8d682a17ec1e8c9d118535f2102e9e03b570f5cc74b50e8ee1d12c b57bfcf6db2519fcc43ca7650cf3248d8385e5cfa280d6d90dad4b2154e90b09 2 d29f3ff67e8aebd7b4f15015eab2647e976a4ab7ca44114fa7a20d998241c40065197569cbea57ff4035dd3962bc0441b2c04f620fbb2492b56eb8d1187dfa03621dc1ee23de16523682eb167ba66486c86637bb7de0f3dca69a205eeb48130fbaa592f446ea1cce0293ff92c2ab010c78bfa216e0f623e755495949749439040d835ffae4c0dfe8e3e3427793b4d6d0eb359442d9211dfc5e66450f96d6720253bd827c746625dfa5467823eb089386979a93421794a1296dbaca28f193770e -generate_ring_signature 37671a1d249e1788e7c6193b699c5e54f6be1ca35714a3a3e4bb5f45572c01d2 e0e86972cdf25612d13ee325998e27afe1f5ca20cc558021bfeee6b8437062d7 14 f41482621e6d7291ee1837f511206f285589e82270c54f3528e9188dc39cf6f2 ba41a5e2f314fc05a8f86861c2e3a05318556814c9d209e4363e363476591f6c da784f4125cab4638fa28b78a7e1040b68b0f4b782bc53403d9b311d1c8418ee 1c765f10d5c8315d648a1ffe3d3c81bfdfa6ffd0e32935b3f4e63f3f563afbc6 107c411025027e2d443aef21a7587dee87fcd8d0201a679dbfdaf4f2ab19a2a0 67d5eda8542bd0951035449c08bae062f28a7eae03acbd4a85c70e98321df6f0 90ea7de0f1638302ba6017957756f60b85de1f18d415b613c0c7c9d1fee2b020 714f410e3f73569ba9dbb4b8f973b1432cd573d33d24ef35ae7cd2f7ad385cee f2c0ee9553a23167d0e49f274abc2dd3976c1cfb91ec2254c8c88477f46ab46a fd5de7201cc934f74b307d5f5f63758e633e9d60fb9d3eaee47f2fd2856fe9d8 086925bfc05dcc018b7b0494d8743328f5dc9b7d6078c3db4f03e2d8be5e2aca 8fd012389212df1806ac761c9a70f1e358870177b639ba7431cf708279e1b77c ae9c72de597032207f452e71b0f4885a4ec933c9908068b41e9a1a5d08eddef6 668fd60e30f1bdeb678c538fb5d94a1836b1e038f20409c6608f857dd3c47bcf 1a128edd23b4090c4eb20ad7ed850a3bbfdbb4bf0ec91066d34d3b8d90433b00 4 227f08dd869e69cea8a82c55730fc389a7fdbb4616616702a9a7744f6c40f101fcf01fff8749d09327d01d9f43555c615a46425dbd8a9a8d84fbb5a6e1adc20b77a98d02443bc95fd1dd0b9fc6a993698db53dcaaeb2b36fde8b907a971ea40e7be2bb93a0f6b9db81b8749bd8ed63e3e913b51d57540e731e600aa41cb27b08324451405e27082707af1d9a94c2dd35a44cca16c7a03d2b0a74476aa675e20063c92b8e3f187af70e52f6dbb5be071a13b82ed7920167c6bbef74605869a70d3e3a681f05e065429f760e04e4edd0e2093dc402266d77280a4f0ac462c50d0e1198f316c354c0280737072c86e01dd92db04515a4730f2b82c3ad102a96ed093198fbd9b164977f7469737fb7487515ed435d1e669759fe58637c197b147c0d696f617c0d1039018cba398ebea9cf82a3b54b795d7cbd2b48c75bd230d23d0a32cfd959a4468b9fb173c1da7ec13d418990c9e428b43bafa08361c86cf3e90a24ffc09aeedb2f37440f7f47b1f710631574d12038547b9777d4bbfe960a6006fec38de2c7a6a648c96dfe0a69535d2a645a584c93011ffa134efa5b07734f0bc55c960aa8677ac168a7e270c19d50cd395e42a3998c5df8a6af738b58f991005d1226172772099436eaeeaa155f823d9f0b7485a4e77db908fd19842bc4ef02ac356b916811843a761cfc9debb9d73eaaeffd5ee37818c6a0ed9deaf383cd0f6a07c301d3b2dbbb85aa97489f47f4eca5976fe6957528b61e9ca3abd56601029f94427e1c2eb3fdb42a76244a53ad4c0be74fcc3c839b1e499d2349fd4255069b446e437a649a8dcb1804fa0b31fb5cfee9c5a86c95ce5349aec6cd8202fb02212be0b7af075cf677260f60ab9c45b441ce4cdf711b7bcc6ae83dcc9462f10b902dfd7b028a209b6f86327e1c5e43aebede9ea38b4833356ba01170ed4ca202863b1118d8c7d11aa68cf3d9c11b7d0ef47f0afaea54fc2c3917f6f39fa52c0cf83831c2627252336c4c18f5f2b0b3f84d59f8098c88ead35561fb3b6a4e09032504f8f5437dacb1e5428e7538fa7d1c5263435801f84febebc77898c0d03100769817f683565704cf34012cfcfe55746ff0fbc93845cb38b8f8b51a75207c0f916449029afd11627d09116472548de7714b9577fc6f2f6641a0c3f67a34c902e5a6281622463b17519112b73721de2d52492e364f5e4b2ddbb2f4fce39c1e0c57021c93fa2fd5f2bf2af02d59aae8172ca12b590317e9f78a8d78f62729c204 -generate_ring_signature abd653f7ad7fd63df48ac6a7e0192cd459c65a8240f9d2cea3676a8bf574d3a3 4ec5408a2203919766e99425df38cf93d465ee4e7179d4140f743b780950f7ff 127 f7cc7ad1d5529686cb49d9a39db137ad1c4c480bad36c87e9874db399df6e2cc e5603615a39caf26768f8f006d8e7fbf75559d8f20f1d668603021cbf5b06ede 35c398f97a395727bb667e2d1b2e4ee561ade6cdce28decb955ea40e3dc81b19 39f7d4ce324bfa1d831a3ac2a26784f4d9858a1a7ee913f0d753560f0fa205c7 3a70187619d98252083118eeb6c43d9f23a441d081c06da5cf1538ae2f514b46 324c0c07fbbec5cf493db7a55f4f2944a88d66fa29495c17d2ddd7beb799a981 8246ceac0bfbc5c3bf76bf1e188c6edfcbe7e8a8b92c0930a0e9f6d7d9bc4efd 46510f6fe33f0246f667ecfac5e9d01e62f9a675b3c9b43267f7ac6e9e055eaa f0531b6c747fa76dba4a033571b5adc2affec1d8ee7c3172aa25a35643118ac6 c3080d681c17a1a9294552e508fbc3a0183a17a1e2708fe32552580a44ea4cd8 dc56dfdce49cdb9221d288b78779a6bd84c4cf8e3c5d7dffb0c5ad507715f174 24b46cc382e88800f74d1eb5cd8f81cf60002b5ee67fdb2f7fe69aa3f921bfe5 28672e60cb393cd812809e8092b639c73e81cc2e0f8867bb2f136cde06c4876f e1ade5706d9f09a98f177a310cd7b8cda1bb72141283205abd27a1db228d8778 be543df678f6169184ce2ad48e7830679ccccfc52a63f8feab3d5ee79f0c1054 b464788b8d9c73bfc942547d9a2726e2c63106f18e322f9b690efc813c68afb5 4fa40c9e5d07a7143203d40e401198a589eb8751b4dda6bc8cd1681586150590 ae63e7c95e21ee618a4bf5f51a9bb1349a56e13045c1ffa54d51fa732dc88a98 788277e0c0c724a631417c4caa078aadd6dd7ccce6a2dd3b6126490f9cb29b03 0e7f64e648f936b92ba48d50f642d4c59bb972837a8d0c698ebd3f6e8d834756 8c9b5957c1c93c492cf3ec80493c9a2ca5c8a296b47c45a6ece5a9c1829249a0 8fba8d91bec11803d4c5762227b2a87248ba4f468c2ab1b4b99d37f605b6c942 25d297ff24cce2c8763580a2cac0775cd77a4625f877b6ac39bf8b8ca06eb121 983429fa4e9308edf662f138a32bb5b5d667fbad795482bba974751c1cddb922 a4bf3f834a9f55d2667a98958618e667207557636d212af92a5f6334c812f8a7 f35b326077681c3290f31fe27fc154716e58b7c9913a77cced12a56d1c492a9f 323c052da3cc1228ee8c14a16ad1c955df440e58cb9811673f49acadcd111954 f01661bdeab726471018f51d247c872624fae8e5cbf34071c49a269957756e0c b67dcde3c5613b2b3b599264f06050fc144b0beae6e234830548b6af5a4ccdf8 38af053d20c292fa28d084721700d138b911430c73f397ad67cb878e115b2beb 27c40d8f771f20559e775713e71ae041abe5e1814e6631ef3eee841539e2d262 7a3a321751c2b1e32dd96af7d33d60184d8d42aedcede75d4e22c9572771d6c0 166a8a31964185aab43c20927b122f4457005786e3d8594cc5512ff88f6778fc 37d33c370f64d5c21fcd80739846a39fad9eba9803afd1e3c3db6c933f600845 68ec83d3d23e6054492ea161ac9fffe1a42ca54f28038bb1c42d3b7e29fd333d 5384f0857de1ed5fa5db5755828eb4e117465dde04a15a90215612782e29f4c6 f93dfef70ec9bced9b7830d6dbce44c1aa4eb671d5ebb3b815d654224db90f1c e386cfd691fd2c855bf8a5c86ccf228af6d9481e38acf5cfbaa9d8accdb6f740 9380536decd8199c51bb9cfeccae5b75d423f18e1de0651bf9419cb9b484084b 4689cb44017f37ef8ef63fe68d4ad26018e7beaf3486bdab9c6c3c459c3e7835 2baa6a00de237bf2bb2fcd3d106601e57f33c27a1214ebb317f53f20052bcd73 c874b88ba9c5e2ce74996d132be4879f2a589b21efc30314e80d0cfade1958bd f1485b76e02c7e9b402c577182a4a941fd4c60618c6f92f7ece4f1f03caf723c fbd10d5ffeacac15e16cefc049ae124ee6dec10c66b6f5b62bf21d8aa41acad2 f48ca5d7ac83ea3e8d9adde2745fd6f50fa7639824db2b311bab61adbcce559d 0d676c1b31a9de877928419723a5eba5d37b2378d2b3d9e846b6cdc865cf6f6e b45daf0605d323651751912084602a122b3a39611bff1cdc36fd74d882e17201 0ef240390d83a22f5e7b9685545e94434d7f667141e55e9344d50753986d4a2d 138d36e66f4cb547e3d2e2e32856a10f04877cc45a4d94d2853c7ccb9c29a05d 3c616b6f7303de221b0eab85a33ce48ee0fb91b125b69c2bc13bde24316fb146 c808d1403d310e1128b18554da2a8dff773e7711104495ae2d4c4cef80c4adef f00a9429229a6c135aeee20efa63cfadc5cb264f965d5bb99c2622283688953c 9f763318111c6e949abe452fc29319e2c0cd855ef13973355a458a3b7abfa5cd addc8266481e686f0120ac9537d03348a12823ba9ad2064c8fd3c056af87d9c2 9e3163d748d6fea21146280df9c2b01c6b46300a40d5d313749982b94caf43b6 09daab1f1bcb2f17d13c0ce3024631c834b845ffc29656cac32decbdceb36dbf 966e9351376dd899b4b8dbcae80f03764a7f67ee0d1c883ef1ecdf62af6a4c70 526b72a160bda748d06fcba2acc635f2370c642defd1c19280ce29111912adad aa37714f944dc1ebb4a5bff29adde7ba2b3bcf1298930cce423ec8bb71b46301 7cb9588e9e2a967a8a5a1486864106a667a277e28f65db460b3086772f06a97e 94fb2fa586d2e0850db9c6d30b4cdabc2f2f12f59bd1c9536fab74d9766f134c 1c466c9515e20c18a56bfdd03bb7478c965a7d12d702d7d4957a63a560d10266 609ef0ebd972a504c818929e4b680aa2d3af312353eb0f8c886fce2c2ca18434 102793b591bdf4a26d4277b57abb796e5066edfc167aea07ffa014cb58960e75 4a94b3f8577cbcaec6b0dbee10ff8f66320707497543846b19aec8f922b4293b 6697ad22d8ce84fdb5fc90cc5af87d5d2d962de46cb53ca843e9938681484d68 18457de093c4a00394f250ae8db6cf086e33f6d5d14dcb3040efb86b3a3f0b04 aa9742d9efbda738d62ab7625bbabcf258ce112757d0278d160b15936abf3cf1 76bc78c14142bd24d386b391e510a448084640fee6d5c89e78912563a30c6082 5a31d2c3cee04912b6bdee1af63d791bbbb9895c97d40f64268d429c29142985 575e2c99943ee27d8962991068c135d934f8d7189ec84114dec86da9f8c86d7b 653841686d1dd0025aa32fc36e2ec002f602d6063433722d32b438d5e2d75319 522bc0507407007529b25d2eec729a59c0f60e98373a0f539aef20df7f53ea69 67feb74336641ee284e032ecba89e02428d2829af28d12b0e5bd9376a60be00f 3c865f1ca2bab0db4eff5eb9e4108ac90e1b33362d43910ed423cc670035f039 d6e4c9d3d9d3d99471e5973327d308fe8faf93727fabfbb0f748c4fc9fba82d6 d70a8e2810c33e4f5ce14666e6282b71db8ba4b4f25984761a61bf21a80098e2 20202ef0bd6bf3faa3dd42b314b620e15fa03cf525b7fafbce8b1f620c86bfba 8576732069b9c355cf922f86b90041baf4d4d44f79055afdb35a958f8ad9fcc8 5844694e707777f2f9499d4bdf209fafaf286fde9040b51657217add3e345bd9 ebbe61e0c6be3cee2b8f2825806423b8368a7f649a0c842fe537873674061d8a eb676ad1281e502de98d3e9c4732a095ad28db45c164ce7893547fe6d776cbb3 ee6ae2f3a4e2c55c347644afd43a8e3ba646259a4939ca5d0acba778d05249bb 377de8f232e2702372e38e93858ae04ee8729ff65a76fefd8b961e64c4eba497 a0ed9e2d5ff4c1c414edb066792f9026692e49855bd6f0c3d69a1ac2413feb9e 1f6c215399aec9b4d8bf289d372c2467f560896aac9b941d3e3c0646c4611f79 a98c0268b55e338c26e4494a4c8a8022d9653647103a0f1bcf91dcd8b3ccffe2 7ee898c856b5fdcbf8003dee310084f8e5d7553febd3d4d9a4dae9728ce7d8f6 e549734c11a2460d6a99322b248dc590074d36a74635666c0167f4d6455a9b10 801df1a8e2aa0edf9d00861bd977b0e4f5d369c182765852539e4eb98d58d9d1 6b17009bc4da1114f1c40a128a5f9238a5ff65e43b0e3ea6be474245c63be927 86a0be9a8e802b0a83eb58fb288e8eb0169085599fabe73df43d98dd538e4fe3 b52cc90b3082ea8bb1c16f4398fa9852f1d7bb3984701dcc59403ed0ea11756c 2e093e60b05a8dffaab61ee017987832247f2980afac47bdf83cf4d4c9b09fdb 23ccba870003d977788fb87907888460551f208dec112af68b493e339324fba9 4336845a60783d068c0693181b155ff8a927e2ade96175842294c5269bba5fc0 885cd6d8605166246c5ab4add221064ac3307ad12cf0d76961527722baff5b45 3640a825842e15ab7fdbd183861ccde121bd494d55dec192307f6e26d7257d42 51e8532b10c76386fa77df1f0972770a67b83d877946387075f159f58814e449 775cba5f1da6c8d6ec21a32821189f46c4acbf8248b236b1e14ae4550607ae54 6a4853aff074835c48ac5fc3288f185e2ec3888ac41ce5fdc6d7b50abfad9f66 73789c6ec8839b4003a2045a47c9602f75a55fd2632f4c1e9361f939178ec98b a548db9423100cba74cacb67611f617ba18b0b65d29fd55ccab9f81a0613f653 e89c5116940301a8cbab6c88056e479ef6ab6605b0ca9cbdffda08dfafeb697e 77ca3397a46092795d90efa06d1bd62da05986b7fb733ef666fd387e39dccb91 b16e1b9bce92e7e72834af0964871ff655d94223f297e2a916bc054b0c4f8a73 b53dc02837033e165910df5ba952c0ba9c3afeef594666dc0994bc8cef844cef 16e983fa02cf9303daa709d001566d3683db349cbca4ecc9677f7da6a6a873f9 dd6a59c284336c7248af37f73cd62638b4a1126028f3088f7809093bb9f40c43 ccebd0ec548dfe03c8576949bff066db53c08d2ea96fb7bc3eaa2e35bc4e549b bad4168d208766a192af7fbeb089e80fcf3ba50783063be866b2afe7f2bb392e d45a233a18f241b0ebc2e27342eb7d059257a766409c5471a725cb13c4331ae4 99dfd92c18d6d113ade3042702cbbd6bb9a79c0c7aa934fcba3382c31dbd7e7e 62ff7d2b14a161f835ba981bf2d97470a2df1438e2c1252441555a898e1e0aa7 ae772f8ce1d252f53aebb7307c17c4e0e7d62f419acc3a5ef596ac80f5f95d49 95abfd3722194601a8532044be8d20e29d027d0e2f4055bfcb51c000439dbfe8 3ea674df981be77af3101ee05efbada58aa84c552b85528c9e96137b0b317f9a fce584e406a2166a6e4bc7576e45ef838e4ce27d2065a45a19320a7baf163f01 0def1fecee4b5330a6488df6f248df22ba92af84bea002746a0b647e50941c07 d692409668548677cd700cda6f35836b205eec4de848518b6a883dd1270839ae 7e1d912ebaaa566002057b1a6a174539ea9029cbd86342a8aa3ffc748a36da7b c281754f3176f07b395486b21e69fe80cbd853438f37a25af70b8becfab39fc5 d0accbde0cfb2474ea79917c09a5c6948393d625f29affecf51bdc3578e93e67 0a299a649e18631ec310b45ee09d737eda2cd32e6ba100ed3bf0984ff88b18f5 e27bee21a8a3745f69a8bb71b60907892f43bdb270af1d4e682a28112c80e4c1 d8f5963afb20f917aaad9e0d0f329df6daa524c0c4d80627c537eaeb7c6eb783 0828b7115e8631dcaf475cebb907e6aac977d9938b7739617e480846e8b0833f 4c50e16ebc84e90f0d414526b053d3db85e9c0f1273217537805d9d7fc03d001 82 22a1741565412f25cf246e25751c30fa3fa61dffe2882bea70e0ede5c1dede01e0962d9914cfbf5d6261ef5cbcb290a2fa490e1015083d90d2a2b1cf2be06500d080d540e02db9bf0f18fa9bc5dae3f5d101929942cab1790fb7ba8ee0b013071a3285c21b3446088f78b06fba3360ed82afdfd89ac5cc727f513a0a553b7b00af9c66f324e04a177290e2de1df7dd552cadfce3eb1ed4e36be5b601108ebd0bb1e43cbbb95a3ac721bbc00eda922bebe28f75d7f4c558784a220bee9b9f79097cd460bd658449da483c785f5176a5e69e880183ce9fee3fa58b466f967ada0315720971edfdd842801a82349015e329b580db963f74d3ddbfd817cd7437450fbb839dbf79d0f88a512b06d897d23b5585227f337a1e42b63017d780f02daa07e6a2c325aaf461014cc10c161815fc8549edbd8a06a9007901beb08c0db44a06c7f3752d60599c972503b17b105f6b4b86c82d31b9ad3a0fd10f8564d625a90079842b4fb8500098db0014007699cfa6fc8458a2eb9b13375bfa68911db9810eeebd8ab0ace35eea753429e900a8f8b7cdff42eb6432004ddb960e5cf01fb70b405271d7c403b793f6c49284316fbaeb37254091a37fd2572e0afaef786a5c0e5205e74f1ebbf5ff1e5aaff99831f2c81b18df5d0e114a284b4f5f17f1e6930e4cf048650bcf242fa42e7a3f5bf82b28d9c656510fef8aa9a05bfd81f0a79f0a39a8ccb72f01b81fd6ae3bf50a8eca45cd9628d569c02eaaf8151944c3851202f796db09bdabcf2024b1e98885fe1ccece1cbead453bff621dc7def4f22d1f06e715e3670da51919673af25774b1978e04d332aaaf98011ce15a187d39542000d8a656c98be845f2a70ec91e6e50890adbbca21a96a3743de48c288bcfbbb905b03df9bcf48c2f988f0988f139c28ad4fb595f79a79aad4a396269e260eb06077af01515485e4edcff7626873d3902277420e8676039c716ae37c6598f577309ed1add1f333d812aea1509603b1418c16cda7f49187a5fcbeb797a83d42ffd01df3254baf3f08299177458c361bed330dd784d3561b171b95a43a440abb98c019f87de223f26c4f3b95001b7a7db54ab8fdcdc30289f34c813a309941d875b0d4f4d8c4077d426ac259fd394e0d3989d132269d46d9fea269c96df066320b00be243fd507b7e3904d5a50a92717f373898439d10339547e6b89df26151a14d05d07a013f8150cb89181413df7fe00a6e27717d71b18d4cf4cb748186f9f78307e30f5e4cd36f3bae213e7c2ff522e3251386b505953e1db23a8e8636a6a51e053038bfe1980993a2411cbdaababa57982cfdc036e4de85f9fae77d0694305d076c9ed207853b4178d03d9139787c8c773c20cb8a8708f3b9fe22b81e2906e30c0e3fa1e0dbb941bc73caa9a0ff7b541e3358bf83c84c2442bbc4d2901554b607d5ec03e265713a3a93b8b7dd68c833f4cc31f3937df6640b7cd449ea6dde0f090cba7db641271d6fa61cfdd2c6e47126657487957502e3ac47bcb308822adc09bbba080625f8f270f6c19b0bb7b8a985af08d8eb8a03a5e4385052293008d7062bac29e8dd27069dbd2e6c38515180abb4967bd2ea611c7ece7141130a93750b50644d5065cb192bc372dd85a9004e98e045d2a0720b4fcf60ad58da5c7ce0096f42f8a3ff66d32c58727b6d4a85939e7e8be232fb679e643f3679b2fb1fd40bd19b07cc386ee67979d4c9dfc6b6404f11c8ec7ca49a111710a470bf6170c50a467efe4ae123fef126ed7eff007b604aaff09a22e15daad73eb33cf29bfb2c0569cd6f668b3dafcb0562a5c84cc7beb68c81abb9ceb2135fa75d717fd2ac450422439cfce2079600cf782b59e8b79bff31dfb5537be706c584ad4c4584884e0f7b546b7ff00f94c5db4762be96ae2eb8707287a3b874aea664ce0bf89cf94604af2f84256f266e490b54d9cd08ffbcbdd252a6951abab3947384c636ac6ab0064d5a9d348e5b4598e804f3f8e877cd85a0be978c991c70afa36057cbef696e05471d1b5d47dc62bd191b1d6aa8bf8b08725ff1dffc1957fdbaa1fea787da2a0c802511f9cc68e2fc833c87b43ae77b83670e09cdfa6dce6c7e8e881d0b2219057c7a1abba8ba5f0210bbbee1b8e7b6d988e34f958ea3737d6c77bcb5db250b00281fdcec8e8056897ae845d67826ea92677bbad3a0cc45efd8826f61902e990114c3635adf2f334ccc4dd3d6e81ecc6ab36ff7fc2808a41fb869516d055ab009f7e9e9dde9983a80408f9be9870403305da4da6a3e0f06a27c715dec99f5a30b76c3c3ec79c1858c859c6d3801a3af461b11cdf7f0842ad48812fe1294d96604faef69b3fa188c3fc5d127704474bde48978c8dc3b5f7b038868fa148aeb2d08ad37fec0a9248f06e54930fc60010d9616b546a6001551c50e82a866e223fa0871d8332224cf9230968c0c5cf4e7be97d86fdc99e037afb8b9002ff16aeac008367902130fa42e1bc0f548c203ac904b4e2dac62dc7bb60c1c1107ac395327097275e2b07ccafeb1d652146ea22ef9be3b1bddd9831daabb3ebadea9268b9404cce58fd26a965e16329559795b348458767e591849f79e2a62ac688635f2f604044a6431d8cd3d3a0ca8db6bd44092135c7bfdcc2d0eaf996213ab3e0983cf068343034a90fb05a182dad9949909d1c2322a1cfab980d37f459df1fa829b8e0a63ab2cb8734a87355c72d9d3a9c300ae7cf99c3847cbaf9ae322764f7c4bdf00bf708c0aeeb4685003976b7226bc3d55c532327b75bd3c5afb0f0e1ce1aa7603c94695d174d3411172ac68615b770bc6a1703f52b75a253f5f727af9bbb2ab073b32a31faaa025b8c24af2b3a8f08bffcf491063cab2d8b6f3de32a0e45a050c547e43415686a65e71bf718bc1b29c6b2eaea654f2648cf152d2ebff02683e0fe3c4eb59fca64ca4531103823d8762d1bbe49a4b63099fa43554fa19d1101e00c7010322d026d659867d1e9c9a2e9ea9c56292184e3279d3f98f3dca676e1a0ad7715c17a56858f7ba905f21f76ceac41c3d872a77ae4dd37d71bfabc3dfbc0a58bbcd75954e3582238f33df8efd4f129ff4562191e29602013cec52f52fc80a6de90f14ffa1d2d6f19d83de850e4b67f4aec10ce3f24abda91331025ffb0c0088a64035a29b3ee8fb1c616b0891c9403f2385cc72f524858448fe1a14fe370627a988f4bd64cbef9c1337b2dfd2b3a42264511f277f20a08d1bada1a3eccb0d718a57a85dfea5f5b03250f24a349399f8d1280156750b23af392873261e490aa489087d729bc8c24d05f15a642afdc94ceff6512064eaac036324b84c6b4a0a8373d109609eb7acbf41dea7fabb47f93d5b869105aecd030341a9e28695ac054c1fc4c72b962fd3b707d2d0d9274caf6bf5db3cd4310f7baf25d6f021f8830c56e3352203a3c2570367dc7535dd06066d6f2152385f178a0c92466747b15b0c4e84e8b4fbd7fd7269fb2abe45f57cdc6de38c303a3f6b9179ea47c53c61e400c327f0839d1fe5762d534553616a680ece81de316e062bb54b2d5fe969488508a53c02249a9dc5b4c6f56ca338d301e083bb84e31365f866b7f9e8035473a6023576eddf3b2c2ff123a4d9d1d893cab04ff7ff16351822840f7268474aadc30acf07763470819557d25ca433dec51d11bcdbf35a7e1ea5f708e2f98711326d08f84e93a10e8eb593de7131371f75a497e682a72fa4e8f71999f38fc0cda83908c419705f275ea3d83ad1dd6fc828fbcce4bacf40741d0f43c929da37a6a4640e3194e2a3ad532239c4522b7109bb2ca7842866895ee44782a9cda54a4d86aa07a4feeaed3708bbe413932c0a96ba2e5fd796dbfe1c21d5136951148936069809bbd4ad79e02d883a6305f7678f0dbf4e16d168c1ab6612fdf48348d8133a5201583f0b3387729e0994cf6d57145f1baccad0d092a03d3684f350b0aac2424b02818f8d677d2eb2f34ea739745fa372d46464e3aad2e9ce70e5a6480ba93b330ba966d5ce99376a59fd6499559679b6ae1e119993bfdb43dfdfaa1cfdde30930e33eefeebba82434e92ea351aa30f880aa4754193751cb71d65338b4afeeefa05baa6c4db37d442abb5d334a8c417aeed35959c0ef427ee204c8c527e568fee05af52aacf3d284be931734efd17e54394e10c343220685813676c4412250daf0501463f0466ae704c4a56024683b105ec3bd4905a1e0ac597813b31d992731906d48cbc66128b367f7862b7d77c5f6e1e652053c2daa96b6821e2511334ec6a05a670778c4a7b7387e953a82d18d3e93eb0b9a52948692c342337467419a2cc03a7a96fc1fb641c5f968951ee925e59750412aceb92cda3e4751e87c090a87e036106fce0f5962a8bc8776f97b9b2ba1164483ec1ef043a3c16987ca54b34ba0a486e0fd5024ef75a0b2047ecc5117ab5628a6981841fe2567afe6ab90a5c230815a427040150c6ff1febe912595d14f07833735edcd648698fcd22d1510b3b014ce17d7a2c13940e8804c4fa6323064206c926dfd9a6d12db6ebd0fc0726b2066e5e328764465a06e85383d4466b979fc2ce829f424fce15dbc94cf679432d01963968fc9e5f726a643d661a6c3d50756e59ff37b1db5de76337c4930264f20bb984617f4493e1cca8ab35b9dd4439853f554044dbf50e0726a48b6c47ea340a25f5a74bd66a2ae04339966058f43c936093ca2b83e75b0c7ca11e99a83bd4040667f944b32cd0a19652884091e531ec7e0501498278d4b1dec6f3323b3482008121f5e85952c030c59decbac85fcbf3d5bc2e08067756f53ff10ad327f51f0399344fd397a8560f3fa0754105e2449e71a28241b3e776a454e6b213fd4d9f075ba77a321b2d3e9c11decef1e3b484898aaf3d743cb5df6432f366cc5cc3410f1b0d80976f1e8d2698f89ec2cd8d16ce41db37ccf15faaf60fb9ac89d306550cc432d27aa411a0a6634d47abedbbe43e95743682b49beb8eb00e5c935c1def0df31504daf97b63e4b5bf788713ce247707e0dcca98b31dcdbaf9196396d843012bd5e1833f636ff66a629ebf95a7bd8935549b70bff44ae71fde5351fba802045849a3709c95e42505ee6a9b43a3d237fee6fb3b0013907a6101b61fadefe50935c7f1d323bc5a0da75cd8dceceeb1acb18f9a76c50434bcaaa0a4132c81df081db1ec129f9f1b3af6cc7fb0de53e63645494adb182e99152eb61a368da90c006a0e3e9b6e9a8578d7feedf2c2c8bc22c00d3b64163b5cb3cb0e57829a35190647422c65eb129f14e0645383929c38abbb198980a93d604eeba11bbd033ea405dd7928750ab4bae88d7b8bb3a55debf054a4f0a622ed515b2842bc332df10000e6a082ccf82d2990ed48b0933b4e1516ee751cdf2898f11e4f4ce5dea7112c0b5a6f19fe3489e55bbcc5b7ec76ddbb2d105650cdfae1a6cef380a069da72ac05ed17d0fd9d9b05f363a2fbd010d596fceecced611f918547e6e9761e65ffee0cdebf7d466687ee5a773b70daf3bf76637454b970f8adfcc57481f93813e2f80585fb05f138c89bec156ae14297061e0a31dbe0212461d5d2449cdb8d9fbaad03ca50e33e68a2ab00f54ba5446a86b346993c2db9b4ec34c5eaf0cb93ff21cb0992e941cc337eb70bfa567a831f83382df3010fe7313b7b3ccf9205b7b773050b12379751e392845593b86433982edc3cbbe7fe41641e4d29a617f7c7650ae6095ab02966c5ccfb5147968dbd884bcd7babc88d02a10110247d5ec6b8df4aa70fab874097193e593f127773bcf81338fdcc0544c1d5f77572575013bd4a4bde0cd8520aabaf93081e9a36c3a95b0f54f4fb0f9784c4aa3aa2544d2e2ec11c5103972607d2a5fb7598bde3745a82fddb5b50479a33f216eb922cc56fac27fae6060c46a661e619a708da82735f5624e5a84b5902cb06db9c2a57b51bc547eee904a1d4b8d3228fd49dce8e2bae065535ed2e2159ba8bec3077fca27f26d866fa0b088dc5f15f7474ffea8cefc56fe2722d7dd46eb072ac4ba2dd0d89c04ebeef02d0ce2d33da408729145338cf0d7eab0f2114c6419fd68ff450fbd80e039e220f304839935223d71eabe7d545e8679240cb96f76f5692fa259f0fe0b206f7cc046484c7b34fe32fac5bba3e5a1831acf45d72a09ed0ac89445100b5fccddc6d043b360c3aebdeb1afa6e6a469c45f92944f80bf660b5185f7f28d95ec5c71b20d8a379371414cdab6df57f17efa366c7383b4e3a86da98ba6df38ad47d47b2e0ab9fdfd6c026ec27cf4c466cbf2abef888936dbf0d76e9bb073750be55faaf70c6189f97be8290377a7507a9db59a0d7faf2ed7b2a5e9025fabd7907c702d04058b0aae32655642605b83f4942c53549258f91cb4a28d166e93bf956f4a4aba0975f70e7ac4ef8f24c490835f440f5917d9bcfd3992b8dce1a15a148fb69a07030a8235013c645c46f18f1a3f7c13264c399fad67f8d7c2106972c6cd8fed620863fdf99cf066db42fc13586437b69a78871cee40cf6fcdce41c66b3aa96ed200c70b2b19d97d21af237445963e4ac6e0ad05a795f0f7eeea3772c53d4589fb090897ecd59e00844a369c1f96600105955b17cbef8eaac048c956bddd54b8b706d764c905062c43f4388eee90ba4c3fb1f65b93167178d7605d2b85b2e9113102eca1e683c206e3e22423410873fa99f25e3cc07f127f48f9a00ad2643b4c7404e116605ed62517c9a429016a32108ad79d1ee7a5feaf041e8d082fe566689b099cb0c8eb82e6f21a2563cdfab50bac4d6d0f1c73d644c4a7374e514a9ccb310b58c366dde0d50000c3b3bf0064cb994767ef371a36d854edd1a3d4c71551040742f62edfd0e9fde69c6a082a10cb2a5d027c944752671be45be8bea2fb1146039ce0131004b33a9656c23e6122c03109590fc7eac9f682c4b3c54dc8d7590708af1cc0ebdbd9c0235e4f2317e4d0d25b88cb802cd146cff3293b2a5e747dcf02e8c27395fc3dd2aa7a53afb3a8d2253e979c3207ab20e95911701ff143bcf5081859a207a498abe4e8a6cdbe359549363304897ba7cf50d1411e98e3745e02086102b813a36e1711d64a915757157a2db6389d9728de67012b720b63a5f10f075d6894d1d2a3937157ddb139d8bc87bd83a93421f53edcc09a2afad1539a31023d16c72d15fe3de7fe63a13869b4e1da23965902deded6425d37a1b5e2267108b51c3c5c0b0084261cbcb91bba33ee44319cdb148d25fa0d99f5ab35a5791e06321f693d2bcc83f53fcb33e520dbc6b3e77fb1a0ef13d27fe36ea246c88ad70a6a11e3cd65b792cfda3de648b98377d99b17e3fe2979639d46ad3a347532e200b62f25a227472e7ba6462681c2701d969cc354dd9336eb2ad23919e5584e54061d389a7d39d8dc0908f1d82317160260493c36dd5de6fbfb6ead4aff0189b603638f36b7cdcf391c639dc02efd0c5bd81da8ba1bd2404a05a67de6e4842a0207d1a78d1caf0ae57c008ed9ed1fcfd5510c73d9e991dce493c122b0ea9ea35506f63cd4b1a43d24d1289fb1a89e7bb04d607b75b8e9b03e9de9362a139fd276021bae8218d46dd173bcfd832af21ecd26974ccb4e9f3e34fc42e21d80138a7b0ebf9f767ac7ce2c7efaa1d99b057223dbdd403361b1c2529d4ac984a1b1a6a8084af8aeeccf801325c75c29cfef455666a65400e021b0ac588ebecaa977150003d28fdb287310fe750c35e045afdd2f76ebcb473298ccf7da7ca48343ec18ef0425a51952ca3a06ba4c621687779a37aba8ebd8758d8c645ef1414cad1df5720652f54e7bbc860d001cfc5b46e0c9c4c5dd121797697d0bcea0277427b45588016b3e9aa776a411d891cb1706ced41b4ffdc2b4aea0a636b412edd9e8e060cf071ef3972ff6e0765d45cefe7f35676774c9c827b9d05ad373264eedc0e3d72405618b07a010e4db68a11971ce53fadfc10c106b8040e2564e4135317534c3cf0388ffe550f7a4abca8db6ac70733481d03b422c63fbecffad43f9d809c1029909b7d913f6fd1714a641b4e7811aa8afd0b05d2c98a334572d173bacdd24abfb096390f6dc465172390aecd7526cb320df4e6cd5548e58f5ad4dc27f28329c710c03da6cc63594a7e46793c5cc1bd8cfc06a872612ff7300344720a3e07917950fc1fe980f6f14b36060621fa11de5e2c0735f4e2de86b1188afefc9cdc4f0e20dc50495fdd8ef40ca047341de88ef05a8fdf71a148dbdb34c407ff5e9a704f40baa09af700314ca47a1d3f27f595688f3ec98fec880d94ca7240cf81542d33106c569eaa1ab35e8a99a45b57a83565eb0d84edd2a8ee64c63e527dbd3dfeafd0b78403ca102d3212bc22e36c95c7d6d73ee37ac707149974743b576d22b22a0080e10fd1481f7f0febdb218ffe3ba8154b3db29d8a9abeed5a865ebf6358daa035bfb8d1978ea9a41a016bbe7ed9a2f67b516a25e6871ca9af72d002cbf683e01016d0b7c14e86ad8626d92e2db47b59b04bdb1f611a5b51399fc85efdde19c04707a1c02677c56d4fc2a36d2e87df570e89df88822cdcd3f413b33b390fc23050d3220215813fbe1868ed0efa3240560543ab329dc9683854f4ec1729b1a6e03f3249bfa4e18c63df92dd4a4d4c80489a3e9a6818b01d27bba4d493b84f5270b46366d510886b6f5c6e7979479cad2cbdf8f662974990e9e2ff126c5ca616b0620eb7dad4b356bed7b7796583aae01c5e4e39cb7694dc004e758741d1665b409f78043cca56e9e9febfb5163474472aa3544195bf0a2570e5d635940d89c9609febd859f419920e5c42eca3ed7f7aa9db308b32d78850d5815e16eb626d91100efa5b3696c8f191cffa8108fa559d498643032e78887532791004df346d86f0b52aa29fbdd2831d63e9c3bbf55bf1a4cddb86ad3c5b029ca287c498f39fc3e065efe1420bf6b9c385f883bdb63b467cdaa1d87d5c554a26c2cf67071b0d77d07e110207d20aa8adbaaa16478f46672f81b1dc83a983aa72b78ad722b030c350e10ca5d34a7f2c03515d9f511bfea0e22841c3007290d3ca649b5fab6fe2222018f0ee0fd7e5ecdbb297298510274d3515512ae34cd9c6c65e0df2fe85e5fba0930415afcbef24ea83216b039c89f507c8bc0ecfc8caa095fbd6880401eb82006c2b1389a2728e1f1bdb4234af18698e46d7f39a0698f19d7041eabc51c7c440eafa0011ea2cded2181546fc51d9b1e43c6abb820fa0c6183c96d29874961fb09cbc6e05eaa355ead01c0851e2f5e4a166d8b3e739c7e60df316fc78e8bc2720c93eb62805d618d910abeeb1fe31419e1cd05f327c30ac0436e6297c4b2bbb20df55823e846d54230e02f1ff85ff30b59249618ca3e465ba00c132afe1cb9470072eb377bb5c017d5747b37410177fc00c885f0f971394a7167e001736e6dfc0ec0b2cd92ce138037ff9957b19c795201077b845e56223c37f876a8311164f208b6aefbebe03f1182bf0c7f42e0d0dc4a25b0540c50b797b3bc1b0361dbd842080e0beaa20df00f2e8fae98f358c1d49a5c7976e9df56da8845e6c7bed398d900e49913745274fa22e1938d5c5a963f91e7150211bb7cd9383749a13fe6dc9606fbd417f540e85094cdc7b70cb05aa99cdfdcae2883b44022d0f419847f6eb6051eeff3312a0ca0d884cfbe38222c853e151da00a1d35f72782e48366bbc3de0a3768d79d6d77851995d0944511482feb2603208228ca43b7cc2542078b573d0c5b9922288965f8bef55f933e26019b600f47b28a9a38ffc715550ede7ff168048ef8d957f4e39dbb93601221c167a97d687c2a0c977709c3d43cc0247afb5f027e8ed662aeb4193a1e3717ed34b72790e2abc050ecd8e6bfc3ebd11bbe29170e81d97341f44a76a48703d291ed9b16172782fdf8f4e897cae15bec3d41b1bd084dede7d415350851dff7c54f753aa843cb6b76e84b8e82268eef197b56b8700e8f8b4437a256ddacc43b1fa5dd22f823e8f39890ead6c0be097c827ecbdca003ec6e1542579bc90d9a5bc31007479bdc45010e48512542c0ef0f5f358240e10631b53004c698e24c0a31d7603ac59fd56a2222833efe4af6a3a6d681bec0ef0e642e5d1d188976f6fdb440126b44487dffd71f9440ca12d1fe12e80f5beff300c6f8f1cf925f38736a0717db0f994edcdd47f28397ab0da6e71bdfe1274c720b60161247863006e288b1ee644e0fd3d9f0b64297ffc9f1bdf19959d19226f2010ad4c24b287d4361756c012daf077bb50e699f62caa7a45353d69121afaf86056db7501ebd4ebbb0ff46e3e5f15f210158f0d952eb80a9d3a6090b0c132ef70662d1310ebac870157e54248c30512e20de8356ea3632c76a8fe3a9c5d10cb90f080531279ae14dbdc8c650391a5c3f10ca6ecece5ac469811f4973a2aa898a0e7862dbb23c81a9d1750ff84aed2311b8d243e17ac22bc177b45c40fedfcabf06082684147d75f1d3cd12353539b84bf3504a8760ac9cf45698219691ef406a0d1672e8cef753181d6739e64a83159c1cff85e5afe7306dcaa4505bc999ca27079bf3c6728be957abf1034c51f2e0324dd6d53376bf0fa25ad632b1252afaef09906a7171a0566f7beb2ac5cef05e6c616a2afd59023cfa530e84f218b21e000062159e17f59547b879574db951a12f1ecec5b8e6c4a3bd0caf242c50157ab50e2ebf3480d3c1f779ad45dd840b577fe3ccecd96426fa60fc5dcd4b1a36d3210052297a61877ef11c4609699aad6e8afe0e065867dfeeac903f2bda9f63cac301e325998a9632b9f04cd80e0097c11805f74a123e8a898aaac4e0bdab86572b0c6eaf34810e5444d7407cec8e9856292e3b0a9053a63bd3b30368e236a5a37d08d27fddd354cf41355de098ca35d905d4edf7781b13afa97f0cef17225bf53e007723b3a54f4604215184c2e2445614661a675e8135e4d299845cca42515a1a0118a7652eabda44c6a956f1bba5343f598e647e915cdbacbd7ade277990f59a08ccd2a7c70087637e856478cb438dcf92b298fbf2d35161ba2fc540338f8605056b1df769095b03820d1a7d320a84d7a77015413287d93f439a98d993d8fd0b08784f15fe71a3d198931aee1361712fa549f4e4901d86df650933abf0885048036489f5436c6052cf9c1290656af4b0dd3af1249d96cf920dcfd3e0aefef28c0edad50a13c416d3f441b40c0147cab42901faac4abddd6274135a968958fde30638a577bfdbb374961534a71e945cf9d1a74747ee043590d8712bdfd6a3d68704ad6a276023d24b5f5188348585c35d153c5cab554e04265ae76843f166159c0de4bfa3b1ce2d1a70ae6234d29941ee5acfc92d5ebff07a0078fd403738cb8607ae2ed5c868a9c0c5b32b6548e8d7d5709298ff4d6b994bcd1e8962c7fd136e0d5e13f7279d201e6bf6309ee48dbb2605fb68471de5cb9844486f16af7d81a509 -generate_ring_signature 1bd71e0dbcfead5409660f7e8c3127a3d0c42fabf9ad6c0d82d88016ea624ff1 df36b0898025fc214088fd247b1b2e2c49d8b7112158410e9c2bfe9922c43dd3 6 c2c9a12dbbd3c694aa2dedff6015a22fd8a849edf52bcc7bbf8a05cd53f6ff23 437bbaa4c47db3859620b779dd5d2a04962d006d16f9bd04a7a6593ba645794a 4a32f65c25000f76a5af3a41ba8bd88d0acecdf6aaa2c55688deeb75625e2de5 f3a5c02e272bf7d1028f201e33c3549631c89ba801b5ad1cc7981424cd1c7c25 ae2910331e8e8b26f7ab2075d3bb9608f666fbf0c4c71063c58c2bfaefd7c055 889845375f869b5bb75e58c169cb19db753ca9f558f806b47c160f1dfff52084 4cd9b90686b4bca0ae584a8c6aa7aedc6d22993c380e03c55abfa047978b4a0e 0 1f340877720bc941230ff5c60fc10403b51074fd754f3d9b344debf33fde6301b006a4b473eea35289ad772346098d0deb46d3fbd00eacfac088a9248eb69f065235bec2e733164804aadea00bf2cb17a98655b313732d97ff8430653a518f0b4511babac57055214c7b89b5c72d1ef284813d6ba67d925bcd86d493bd4e3606124aec794d51912a37dcdf91eb5906acaaa831916f888e6ec5fbcdaace7f2f0be32110c078192b91056bf83cf9371b2187d7b2c60049add10b5a7e841d48e403f4918ce95df12dc28c50f26ef040781742c2882d132da1a6ab2ca30033938e02073696c119ebc33d9b5247530a99336e94d1dca046fb0c1af3dfa530d09a6e0b676452bc973355f8db4d6e052f3b1fd763bcf2d582f6a406428cd80b7912640b88b69194d9f94d57d0e03fc0252fe20621f9f79c5d8e6a0a1d7a04597d8a2d0f629bfbcfa1813a647d30d6094053f6769d65b3d099bd1f0b053e6a4000ae1d0dcf7ddbcafe83746a13028ca7387f09d039f2bd41b03923782fa11bf22cb58b0d -generate_ring_signature e424f4b58872a89f8d5cba2367a5c518d6f33932e829f1b82ea361005fd789a7 3881cd8adb14d6d3486a135de75961ed9ed7cb92294019d81adb8eb07344173a 4 08af7a24d14e395c22d134f5db91bce9bbbd0e05625a0817e7925c7f5d3a288f a0833c919f066178070650518818c09a464418c0579ad6b6f834b377035dad02 72d51c7e3fd7f62b2c2e108749a71a52e59072f9fe9d9eb78a9bb7c7d04b5358 33f26e4db6b10c1ec6205159c9292d852a39d8d4eff9b34147f9021e15cd4021 b0d7f260e8c049f4773e7a7957656bdb3426b3ed2b77a92963e25351afbab80e 2 1cf0d914cfcdd73ee6225ed2053355abd6177c5abe1d622a62c6fca1ee202f0b68d55c831c1e613e386ebd9a831815a102da2981be850a29fc25801f0cff1501bd99fc1fc35e6934306338c65bac442dfe2ef27877bb90ca684b8edb893e0d0c482499c5536eaa91641da509562ba6a1ab4277795441b084ec2400fba8809e09cb284664c0da6472d495752822227434497e3b429188b851453bb1d15396fc047e21bc2f6aa327281cb75fe6a6cfa73117fcec675c3335bdc134e7edf423470a2951f13b91d25b096977248701883ded49ad21de4f29b9e0297860567516c505e66ab6b392b7851b0f3965253b0d70eb916ff8c31cbb9ae98ddd7116de96920e -generate_ring_signature 5bbe504919c3ec458ead546da6a26736d2502e6a05308cb5625c39d40c3b0c12 af979abd7a21ec14aec19f65b49717be0e48846576d34c27404b0df29997b81f 13 d151a6ab8f224c3b9b2bdb438d5fee09275d134d320af24552c67af777d396bf 5627dd6d295bc9bd10039aa49cd520ce164dfdb98b5586dcf38ede1cab9e41dd 1e7968c3440d4a9994dfd89fda5d7f399bd609d7d428562aad4956efddbe85ba 49058138f3cfa317adc8439d479aa62adc11ba92670252048020952d91a949e7 206246ed0a40f7d750ec28040df4d659ae116b1dd5863f4372eda67025952d13 d3c2e54b9ae6962ea49eca392655fbf8027346ac63dcc309a0899e62420dd273 2b13248ac56f9f3bc37b3d49b2898fd783668af84f8a9738e610931e59e66842 b76331e8fac5e727716cd2fa1b24a6c5703bcee81afcd923b5efa452b0d6a869 87f3a2689803f1d825b4b7d9591c4631e2319b9b9582666d23b15e738939a03c 83179f1ced3540b0293518d30fad1b29d8227da77f1a32a19132b638d72d6d8a 7d1ff46774fae090d9433060c2c38b3e0543d2f9291f469f4ac0d75d6b8e4f90 e293f3c438ee82c0bf1d5b7d0cf27f9daede67da84f44436e395aac9021c1d3d 49541a49b7d1df796b7def54c7b0f8fccfb1a6674680d18f67f5e7124bf0a8dd 579e68d5f338d7c7e0e07ac07e0e592c9dcd18b87291a19316e66e57513d690a 8 343099596852825a1145ea009e44d603ea84132759cb9cd2576963379075f40d9c02c70a0f6f5693a3facd023a52bffd5132fbf7aa3c4b0fdecebed33208760db93803f5466334a45a1577b1e27da9b25eac4d849cdeada60f4b1d08405d570ac4c1bf1c01d6e0c9ee0816c13a2b248afbd5e6b1cc2e4da4b44e9a9ce5beea0a062377aaa23ebdc9b3d6b104e93d8f02df7973f7759cfc7bf9ae4818a0c4890ae6d38d05cb89b3611a3117af7f2e0a12031f78cf4bff2dc5172a877d7d704a00d513c70e065c392ca73183549a919fd0111ef3fd567f4144648b3546e56c73080f05ce85b272c3fec124f2f912859ec93fe0ff2abba2e1ea8d40965533bc000ab46926b46156142e05c4bfa6ad2234e2708ecb430bbe33239e90748763ff0d0e192c50c76fe037ae878795ac1ac5f16bdeff6cd2bf3f73f219cd05cb87b3c202e653b0edc52b8fc1cb1267e0427a4f7b557866ffcc8282218c5f467f55b4190b1e4aed5d016119e72e73b15c49f0bbca38c38558f785f83de2103d1e3a76cd09f6e1f47f82c58f221b7ca7440e046db6fa87da4d4f4ec830aa1f59022130e80786fa3b9a6bbfa8a03bf1297105a7b93cea4a282c8e94d87ec39008cd32f945010ba4701965a19998b0276b1552e9aef87be65ab0930abfcffc50d509b19a820d1f044571500a6aaf401cdfbfaa23dfad9f48c6a8ae37d19b823eb0da28eae20320565408df2ec7ab7106b2bf251b561f99f42068894f2f5ec573375c1be95b02b9f7212982b814b7a5f4137a3119fb242c5df8f7f92ad4c9ea42f61ab69696098603c0a3accc7eb6685bfeb90007ece294df95c581783c8add03afbf2b16a50bee9cff5539dd57667cb674e3c6270448c5b8b0130fa7118d2e98d3a32747df0d0e8c3d94e6e03afe6e93d24891555ffa8cdbc5dffb4e63d026a9bc48b588b90236a35fb5a8f27334c0649c1595d116862109d8460bcab53f39dc0c46eb88ee0f8648e15173cf3df1d9860d08ea6a109598d20e8d82075ffd35b7a124298dde014fbb2321be2734efeba897ea1d0115e81872a4fda570f5742bc30c76669fa304250a51b0bfffa25e15480154311a6dc2c8c477a5efde1ab51a7841101a31e00eda10e498d6a8950f2250110b16d61061b751f7cad9f675d3c8fa0ba84572c600 -generate_ring_signature a0859f82b14dfa14725b605a1f66c4e7e3c5703096a3cfa833f996b5e105ea0d cc280d0af6534604494020baf8cb120bffdec0339aadcf45b3d97a2b7965d393 1 13f43d83ff833e7b6ed996c43f01724dde26aa6d0ff8bee55b7f2a7ae080c893 1693e87cf56c39b63aad39216be18df69feef1484ef89a9d20eccf59584e360f 0 966052e60dbeb9b615c07e03596e4b6c6fb6d3b0090c6e8abf5f0b4d24c0a80da8aeb9e007e98ac26f7c1b6a459388b6f818225f93139d8ac3b7d15a9aa0160a -generate_ring_signature 0b93af27e122fda8495915b5038a4135290d292040914c8322751c075859176c d874a4e8dbce217f1f0d503daa4d2833dd8457d2d29022c39b63c2ff18295462 1 fb104b44d767fe74de8cb1c133c2e1d253857eb003c6c63528d7cc67b3f7e49b f39fa6fd80c7a487e67ce834393545ccc91891b663b57ae879ce6ba4079d600a 0 468cd65178cff5ea5df7208c704c0d51b156270e086f009a3dafa859b2be2205c164604494546a09bcb4db40f9d37eee984c6aa2a045fc0ba66488fd8ea31007 -generate_ring_signature 88ec571618126ea8a58e3d9d6d3415253a7a9e2a4833ab4c68869ccf369a1aa1 0fa962ae711149af59f3923efb7e275ecd525f065faddc47b94d5b77a1d058bd 3 147eef3259895073beefedcdbc2d0373150bb6dfdc255b9c58326fb443cf76cf 48b26ceede8691804ed3476374d36b2e820ee44c0792361a511080de5fab88f0 2798e56eff7a2f665c67a1b615e19834f2095580047490ed3998cfa1a5ea645b 55597215ee22059246d3b0ca241f9fbeea9ab39876dc981ff1fe7f70ea10cd0b 1 b64c45a3303fe8b3f938008f940f638c91d12cecf831d39cbdd3c33022945400baf9cee4f485be1d7348b3d45aecb3c657362df8890d3bafc9373b1cbd782d043fbcf0dcc585873dbe417b275b6930a0bbd4d2ccf15b937f0cf3ecc3d9b71603805d070f657866ae20a5ee1c64861f7bae2ebb5b2f0659022d0cfd077b0af90d170fd0a6bc9d84a9565349a9ce09a063ccf4710ffbc3d1e1c5a93a4369c1cf06dc40213c7ef54c2018e7ff0acb0583833890aa7b5c8a856723f6592ddee85b0d -generate_ring_signature 7d0e728c17d84fa5a6380b5d42c2874053f090ca9619c72e8f856aa878300a78 406841c5266b911d87602974d79515ee3cdefdc52ee5e319c934adbcbc964735 26 e134815b7a0d32a792a347b2f87e63aab51b90b3e63b281f4832e2e18470e452 7b5604ae9a247eff5c40efa5965808a9294586dfbd6d96ac962167a4134acf4e e7040750c32328c80ac5608f9a0bf591ebef09d0630f5022e3628558c91ab2e9 3c358a038783a448cf3286470996f23f42d97e3ca2cc4bf4fe62ae3a4f974466 5ce0c0f74280351838242547aa953e6ef42f3830c9763f1f201c95e116a9cbf6 6db16ea86470b1e4bc1aba22ffd2f6a309b8c442cbc82c9ae1d7503ff44ba235 996b564c3f75320433dcb6e93ae0c45f1d1bd63268b2dcf97feccdc2ce8fcb02 2a7ea0d2ab7ecd8275ff6c47dca7ec0fa796fa4abad68d20384a1314ff83db12 31ecb4133c92846d600a7004a619e5867595ff17fd4b4c4b6685a34b0a64a212 e065a64af314013132daf153c9d7079292340724999e9b9e0e2ac22c4be269cd 44ae0fd0320230f57bbe081415831d4da5ff3a4c6fe4ae24a2f9721df86e8de8 8ea50a4f60f0c9d389940d28fe7bda03dc202a8187b2287c5609d0a86d80b970 b0fdab7d002af49b410025b061cd946fa32b4a4fbaa400d8b05ad89ec6c102cf f39415c4eff393fecd622a777af99d13d6ada020e6b5f154b0721f91bd36e005 99df2ae81c858e669d81270858e66861a9bebc89abd36b232d2fc70415f32a66 a3bf1ab959108e6eb6044b8d3cc8ef0fac7c56d326b225948974c66003a7a49b 3296005eac9a12ea9a9160bd2d224e549a050f37f7b8a6643007f953cda3964c ad1056f0da4946f740394f3c044438c016bc31ef30bd1ebeb4da58fedf91096b c69e1a9512027cbd747a968593c936fab41dfc0de4189316f49d731dd5032cd5 cfa3c7a19dc9fe7654556910f7e6332cff284d94787d3f1c142213b9ab388aa1 a8a526d7c0cbf45988a265d3f3874bfc16fd3e972b5c6a02151a47aa44f07394 1b4b025ca5776cb79a04b73e1cd207f7bf18f388bb9732279f870a94c20eb53d 9223e36a7f6fac4560bd6a82a48d6af7bc91bfcebee656f6f014673a0289b485 9a8601702f972d76ff9959082fd1ab159e5607070cc24c09633ece1f65d65aba 73980dab5271d0312d3736daa5ccffe20edc58040be9a59d833ab288f2623b38 9838bf0467b2e4c5b42c2558fe202360778154a2df347fb3cbe950d677b7caed 9da3143e8f9e479e66ebb78947eccfd708b8dc2af91e13ddbccda8beab2b560a 20 85e8621319a7f125bc240a7b6d71438db60c7e65ed644ab89ea459de23606301c376bf2c63be7385d7f75d5006e625619e8906a3129e161b0bd0b4f0d5ff4f07e971f4ab955f6a09b0daf4e8822fc911430ecdac716144039da48f019a186e00e6d9c15a6f415cacaee64fe84e1e7d79b5ccf57b4d32d60a75cde039601e1d0f39e7ec9927468464fdd35cdf11829de01d03f50b6c729a502b819f46d6adae0d5824528ce4e152b3efc8cd8a6f40b3b7a3352c043b422c658c9c73d1af0a560908ffdab8bfce11a411c09eb2957d57df4cfac5dd5ee5fd70c42889e331defa0a88222fd0597a2f0ab75fed9718deac3848c0e7b34de0fd350135f0bb43979b0375915fd90db19194ebc6417af80b739817e23caf21abd0b59a0b2e7b13034103fbd8aa85fbecd82d1fdf7987e3521238dfc53eb1980d89359a5aa866a9e4700a4d5e8393b109fd923b7403666a46c0bb1c3ba808a79064c446110e36f1555b0aba79f84d79b6a3fa75345f4759cb62eec95dfde80e2a9ed55e932e21280300099e27a0a253f1df9f706c59ce95daf205da4d86e248821d23b6b892e26a283e08238872430a5778c7fe39e8497d4e7bec7bc0f65098eeeda3f99528819c900608326f4d8f57cd4f39325d56cb076262a9a055eee6e073b1da8a4b57803917a90d80a9fde514b43be359595d018237b77e4d37c741794d59aac1049f03dca37805aad5f891ba90a16f08d23489a0513620b464c3cbaf594c321c04faf9fa06200c8d23224b2132ad049e87964b949ba61fe3a5b976efa9fc4864de4b555db087003070c0291693be3e40e1ec6bad86f574154b078eeeca5b6672df8f42e5e5950e7bff18dd4f859ddc56e6a5af45be81951d15e1d7fc75f96bf9730e62ee723c0d7c26515cf4f2f3a088587f6f81c6b1dce08686692f35dd6210c1171a3f298009135538dfa3923ff72fef47cb8993ac6c1398fd95ea754f1322ce64e102fe5b0ac2d147ed26493ed82244f5db5191d94f5ed691525815bf3a4fcc70c518a22f09bd4e2a97ec91a949060d528821afbae0ff557838a0d24c06e95e8818bdc3ea03fa7950554f760f4d03c90f4b928f4155c90e2a7e3d83b8337ab51f13c6e29400017cee1d5884f423c2dcae367ac8759877599650e31f52c77912272ebf4a2808e1f400a521e42d264e4920ae2620eadec5fdf2928ed23ba0a79ca82bbe37aa0e64e56c758bf89a0a02c7495792ffa2bfbca846e5a2046792247b715c27bab308e09a9efe14d0eff6246892158727546bddf75be3c5712c415267f8986f30c6058cb181ae97d4ef3eedda4017f6ad93720d9a52d7a3f325cefc20996adc1eae0b92edfe2cf7445bf36ae467a0a4ae8030976d4cc3f743e3d3fac76416fb8464054eb9f1265db0c1e30737320b2fa13145c073150302edeab77c3387e1e22b48066adeee949d1a3981029242e92266b375eec12972e0df2abafaebf99337cf580132d546f898929af43552fc8a060a6601444ec1daaa5c30a05dd5be6deb889f07607d2ce0732241afa61398964ea8a6bec52d19204dbfa37f6252f4e91877b8001182d00b1ce0c46a5c79fd8524a0f1dc9ba0b3aff95ee9e3f3f13502e54cfb067eb6afaf58ec0148ef72d6c7f78ce8f62ddcae5e44aa06fa3f9c87308827b50682f22b668f28104057d8700ecaa1c9b6d4f4902d2823807fe3046b593013b30f422d6fae1503261d04cfc14d656b843fbbb21684df20cf69066dd97b979c3e0af28e2fbc818c087b89d4f83bd1c4eca638228baafff20b07377b630ea7bf490173851cbefdffa0a799a693a18d3854bb784d3d8f30cb2a701fb6fb597d5a10041d9be35ee00bae54494f5f8e684885371829ae5216ad447d370a9d71e2ea230a96737841162b320784700291580546870cccd36c210017e323ef5023c015230627dae3c006633fd1fce5e60d8d0add6795eb9a8eeaf62dbe689ae1008d0a220bec480becacc7ffe00c59feb7b705526b62a84438fcf8e98e6f2162b34962e706e025cb357518ebc287db8b1b69b2c63ea45849fb406b293dacdc0759b1a57e0e8075444185e5f252e479bd57db50ba8616e92dd0b1b5fee0b7d9b0bf14bcb90d545a35ded0268d2f5c466cd00fec5f8b20e310b957d207774879a1766f7c92053c681ae84a6fe4847ffae9ff497e43426988e8e1a2ed3f40b56c47e7baf2a30203c368033f60fa9eaaf6499997bca0707eb335eced875b7d90361bee0c99a20f71e2170e61249facdd1fb12d4f942e994d8ba12abdaffa20587ca16e3b3c7403e8a95e4ecc70c280721e29003e25db3be058c923c8964800b6a7c52734f03304 -generate_ring_signature 65915976dad0b79ec2f06123198fc3a123c37fc4a73773487e857da9a5c54c71 7073df50257577f8b723f2b7268b6d026d172b9fa7bafe8e0d1712112b026e09 31 a2b0a4460179b04820d33421d1ed3c5f984c770738e6e734176c1b69ba5be705 d172bfb94a40f6d5e3a512f19094f112ee1ab673f54ee9968556b127169952cc 39c3b2c50c49fbb9ea7f49ebd3ff1b21c6120fecedd83b6fbc88013dbbebdbf9 36fc7e7fe71707ca4777c30c05d7020fc7da04b333676201efe55e93a1468797 7f1971181921ffd10a03172ec2726b7c95b714214b94d040e914a4a549d24623 2013ffd455d969d46ef413e6cc8114d2b6a25de9e5fff6aabd4f1c84fe28bf58 61e67258e2b7ffab772eb1e68d832f940bfed8d3ea7e56f9fcfe7ea26a8d1999 64e5efb93d450926f1e6115688897055b968f93a6972172719d69cdc21e93e9a 1db41b3664617a8e5dd42df836433ccda0abc7ff1d94590706d506cd83da9c79 722a3ca714a18f10ad273c30a5ed6104a00bed41e5c6bf5f955ab7dd41a49f1f 32367aced52479bf495d60e499e1671537ac2d8711fcd83ef8874c667fb94fd6 d4052c8067abf0112c03f4ef277f7ee5e0c351b245c1f922d2c82bb855d6c341 537ff1cd54e1e52430585be5197b3da8954b631e423beba3e39dd3dc6be19004 66b18195c83e626b6a9c315854420167a09bdb29c02175eeca18bb1996893a0f 6c8982d18575be7948224abf0157ef3f788de7294ab6ff5c1cc6d9bdd5647527 6251ef481c86959a70d700a5ec9892ba415d370dd1fb140eac186f74090b59ae 25642851c0c2540c4a43e28ef53f2923af198546242bbec704db549933ad6498 002c5ef9e09a9065372ec5c408cdf6975b9673d416d78204ca1ff40b3a2307bf abc5cb8512bb1db9b640c4890e68dc3a7e0b492202e48c68c26ab68501150352 0b8a2f4c3e989eedaa91cf4c308738a050b33d28cf23940ed9070a576b07aaba 97d865b7b22cd95e121679e94c5cfb4b1870e4779058d308d565151de1ffc754 b3ed05b092a76c0031c064d505430a15b1bb4071bb70991dfd111e04ec77ad44 99096c10ba7981946e06381fb36672c611c0aa8ea97a3bd8d171a4a188d70e91 fcef5abfcf89053360ceba60f1a0742f6a209b4e9bd3ba23a0491b25b18d12f8 61c1720dc08e8fd3241ed1c0f59562dc5a39337f2a33443ab4f041ec9842e216 cd5d30ee14c6d6a6ac28024b55fbe2c306a0dda2790957ec2ea3806615e9965b 65c60d4eff3add02a38c4876bd36586378abd726502755756b1ce232b7fe06eb 3e7c1ec3ef42e896cc5564c1881ccaa0ba7a5b5e616f069a185cbd6c6643aea9 521bea98888e7645384b810063c034332ea7fd15cbf091ecc24a9b2d629af997 5293f3c7583d6e5f28b5a08282ee05a2aa2e8afc2b35db0ed6c4aab167de4273 a18fc87524c2eee2b4cf3c001ae2969837e11bfdc9e135cdc09a3c5854b3a04e ffbadfc27c1d36aa85dd83ce4143d666a9fef3b90820373fb96f7976117db103 1 a266a57394da375dbcfb5a784297ffa56a1be545e7867f59171d4ac0da95fa0845b966d7013f52474c4bdd62d41718a438ca51cdf40d71ecf4c5b62d2dd5120e1b56bffa39566227b885a4f7832b9856c7fd3c342f2b59404583041eb519e70514661429b32a2d4db712968ebc629eb55073a7da88b93bccc51352eea58aad008359bc5d1c4f6b6f91b66f07c2f95691865b4c00ecad2ac8d95665ac92095705013bdd781ebfa8aad1a05222e00fec8a52b75a36bf850f93d767840f7297a3090820a4bfa7a102e481b9b81029161ace9e905e9b5a181185065b4ed7f6510902ed3694af45c6becaad7e89bbca9e3ae431c0bacbc95f1aec484631be4073530f9acd723111d2568140bcd882ed56384a9e92c2ad81fe3a0b434f2d801159ce0861b6169421a9dbcbbce7057e00c6296d52c6d1ec568c475e9aa7a3c5f716000734bc687697c2420e6d2d71af58b87b0f294eec9c4679c8630b875c7d7af8c1017bb460081ba427309d39527bac59f4c2fc5a6e54b4bb9fdf7b138a811b02a6068f16dc6b30606f74b6b9322cfcf9dc86fa99d5523b88f48199ff7e9c166c800b7486d2264e4d1ca24d1ad9cdc3b7059e6a9e474e34d21eca41cd8f87727be105d5ceaf00a0034e3472784cf0c1d7949414b42c710f0c41bc0258feb9dbf491076075e5201e3c1c2bc6fedc4f391bc7105168d4f228b53b7f75faa41e24284a0b6b5baecf3bc6b959a59f0e737173335cd23d9363e7ba6954f0282c0eefbc1f025e78c0afbfd47a67e80e92b1e046f3d4dcb507dc0bf9f575b68781d61316950d6c1daf6c06821b891951ecd37c3c038b3f37130c6669dd3b7298e09496afbd0a13c3e7466ca0d2c453ba66f6aeaccc6cbad9e2d53dc4b44191d4d726c42b5b0d1bbaafac8b819439d07560c54242313c696902aa89092c7f987a16176f61dc005cd9622f6eb7d6da990965d7f75d8fe4bea327111c90a6c0b8a1b8d55d7f1f07376f8203f2746fad4855ea76067b459d8b33fd3f0a991e66f7aa3c04d9538703af7e9244b830f009ca7eccea38a2071be1cc0d0b6cb37fba820c9e796cab0308d8e4e860a379e321f7984f805dcf6cffa7320c445e60a551e66cc6689f488f01deabc193d101cc49fc2d4a08dfaa7f54a5a6bb33b8315b30136430697fe082022f658e7f27ad5acdd0a0d673297001054cd51a25969d06169f702c50273ea909b74750b454c6407624e5e2b13424ac2983857373fc7f632ff630e8b7fa1a290ce920cd62dc986a1ee61275270dbcd7e8438bc5c468ee2fa93bbbc075a1b95b0b435332c8c916ac90a6b477e6e1f7c05284fc351e4aeff3120fe22369aca5f7003c2ada11839ca61c7bd45f7bb635cfc7969915a9b83efff2fd19e3b63fabc2054182b9c23ef034d4661fd8ef834d46c7696b1066e81991e0d48c27dc792a870c10c47e0d364c0f221759fd2b3f701d5d475b9d00f2dab4118f0e33e5fa1ce80ee98916ae7a9172c547a94c037849147471f8c10bf52fcb9d08444ff4cb7eb903da956ea9218fb3913e413afee5a1b4c4183e5ce22b07cbf47b511bd7a4f1c60cf957d8ff8c449d5bc066145a2114b73da093c1cc149456e22fe9bd4e9a4e8a04e41c39732c1a01b133cf4224ac4d3a0b1b942365b5a2c3a27a6673e19582df0820d5deec1923d0aaa0d95d1306d356f57fb91cdc7359ab7cb680ebd3baa1b4094e4b88585ba132d9ee6ee4204fb503882720a12613d339203cafd6355926c404d076c2156c5b7a7197e8e67d04e534000f20237b2a36029076757eb50eec450900fe4b25d8e717b6a9caa63128921b160be7c0fbefb25a15494eec26838a070f52d7b22034a647358975e0130f8e7b90541d32e86ca85296eb750741c91b7609f928cf658460bee4a75b06554908e1f954330b32925f9015b172afb44764f20144f09b17b12a859e609decb278648627090f27b6c5568c7cdad573fc26deff08ce989d799515a7f1f026f21b8aec04f3a7402b0e387b9eced8397ff83d3ac10e426b54746e59ceeaac26c8ee74fac266f3cec6bdc00158248af11ef0de57530a05b38f7ef72b5728a323925561896c4a8d162a2fb4b095afb9e45f1d4b70bf095264c1526f63e791c67c8590b9df11ebae239f3093cbed62c55153e5dc514904ba815b748f6ae9828596c16b3b8221d652b5210ff49d0e10f811f3d41e540109c1a9b349096d6faf52cd6d25c80c27cd276592c0df2105c2413bd8f06e62f5017461252839ad3574c0c3e866707842fd00d4b86792f77df4a13eebf9b88aae03a8180d782ab7a1b6173ed161aa00ce6f414809e01c94d6b034c8afb90618f80d9ee009ebc782f756d96184c8751087919b60d762602d2d068aaca2a9ef765900cf6b9622d183c8a9f3dc5bba85dc560056ae457a311ae6496747adc9a3baf802b383d76285295c225daaacc15e3f9ece2bc9cdfa1c2092312156f412e6951f0ab34b26c1d4ff4a312494d2b33dd610815d352c7ad5ada36f2f8465dfb33d6e0825a3b1520716666963079c14cd59e9e2bd1195b026c902e1dd04b588823dc10c072cfebba06c3906a3856c45a898fd1d077eca00db76f2101cb37c4b375cf40f60e1ae72102977e5e2fc61ec3f9c0f5869a141b4b97a3eac0391ee76717dc109f55c7a740ae7463c5237ca529c4659294f3009ba3a91bf81f714040e222df60d6e535a7762d93eeb1c2fab666e999dc64a1135130c6ca272d0857db488a4cf0ab3849c704d8ba7fedf16fe382b8494ec7b5e6da78eb9d53ff3fd900fa2560006 -generate_ring_signature ac3e3b092c1e38aa072cc4210d9bb2e4ce013216d06cba6049ca7b679bf4822c 317219905c41dfc83057f3a6a0d538f47add4d4931b087b6d488aee4b93ff7f4 1 a957ab83e1decda794a51d72c6b411a526b193dfcdcb97941651ca61fb40a066 cafd4da25098d3a9667da2474b43613aa07f58c8cec919c02067c70b02f18902 0 68a6f5062f5e82de3fe26584e736d3935c60642b24f07438bdd47fab5cdbdb0fca8a948a2ad17592fbed8c1f9b68999f49e9a40d6bbe1c2c352b6e360f4e5408 -generate_ring_signature 6107adc2ed0507e69710223b8f25ad15872db003a33231b30b13e693977a9a04 28c0cb6ee37eee1a2282c28676b5b9b3ab652ecfd46c4cc29b8b6f4ba8cbf514 1 ddfb74774075531723882e838680b6087bc40a394ddfb6ad1235e233491f4a7a c4aae45c82526a21a7eedc6caab8558a2bd4f54cbbd11e95e72bfe372bd1bb09 0 6865f46628a8ef33d4fbb2fd07506aecf43918cc4a99ec6e79981bc526bb090722ea1e9defdc335f1c42adbb8c148f0756742aeac9ec76ba4069339900ec3207 -generate_ring_signature 2100a5f4e410e5dc62f19a0c5e87184e1d148a8dd98b2466964959ff2381da66 d6d869463615fbf48380f9d11e7461f8cd5bc1fe91fb99f685743aba5ae70101 28 793ef8c181dd024839f3212f25ae757cb80bec1c219db4246c6ee2fa052b6f17 2aba2b63e6491a35ef32789b0c7a6274377cc205254e9bf2f69cb0286dae5523 3fa876cce9035a3e6a4d5d1b7a8c09e2df305701c39878bab4da23bc717f2eed c9e31cf9d3551e3fb2af4fd1608af9627f0a417d3cdaae50424f7ae0ff69783b d0c8613048181c9a3694afd2a02589c944bb3eef2b82b77c250775536449aee1 b9ebf2faf7aa48e64ad3dd91d6c719ac4f765c31b03bcb8fafc869c8c7636f91 e9f8b1b1cac9b84936264a4752dc97d3028a209be61bbe6bde32f8faf5463a72 2e66c163b5b33bc4c675b99736bd883533d8cf58ff92a6c6632cd47898a8060e a993576fd3d80b0925a900c3072aff58cd46dff9d10f166ab52fc57182b7afa5 da828df878c59586d46a4c49696661c3bdbdfa9f36a053d5806692c42e3f36f4 d2f4e92d5e5d70fce216e8b6d243fb32014d8abae37bebbee14ea9b8a3d84b8a 2f1f24c37d5eee2ae72a2dcd3b1b6ad30f5d34734a762fc4ab7b8956ff49dd02 cc7d90c3c1c794b6406cb1fa52f61a0045a2fdfe1bd83636c848f18a100bd98d 481a170059326d8eaf5a6885213b915fac4cb27a8eb5318489a75fed10cf50e2 dd2cd3093ca1a6169b55964de9003e308736bfb3903f3c8f8d6b02f252df474f 7dc1c9c1fb20dbf1c50b213b43df2fc31dba76b9afbee67b8b0faff9a7d269ed 50e9f8e8964df954a2c522a8d373e52d2187bbc76919287a59d9b506249ef137 8902dad485e7e3a7b9cbb784074a11b2f75686b7bd6d613027a0bd91f0a96c2e 33f2908d1fd2d85c821734bfee6f1f08a1b70b82b95a7b592d02a3d09e858cea c70aa472bbea4d8d55b345cc2948ffcbff1f91294eb23062088332a5d78b22dc cf57313df41a81100e9abfe9d50146d4bd97dee3a4633db1d47cda8874de978e f661b18e8576915d55a6a23e8d201ed5286a576f2c81c2fbf4ec7945e9b54f7a bcbfc29481405ef6941a8865ff80219df8f16f972e6db14ce5ae1f05aa3e04ff 1c5b932bed93ed0c1b0cdd46325126b6049b4def7a3bad6b7edbb8c6adb628cb 089949b4a4c24c2131989b09abdd0aefdc12c1c1f62ae87e04fbe0e9da567544 0da2fd4af5735a1b90aa73c411039d41dc2f61aa1d1ff7bbb76ad39e8e2c5082 81073bb6dd2b22897882b3c9d865bec7f568b80ff23cdfcc22421fe9771c3395 5b87191663fc83c7ac5c0a1936e414fa357bd8c83323c08c5aec0c65be085458 6a668a19b1bc83605e4aa62bc2a17c45bcd68210cf2b05980ff205babdeb8508 27 290db4c8ac47cf899e0acd54e6450837866418e1c0e85c3a8e6cb4f2c02de40f2ef132ca45d59d70911f8caf136114bdc06349fed97cec57c3be3c1e791ae1008685926b4f66f73425de762c61cf70903d608b7c5411d00d31d6489389c4d403722f941eefd40b53cd958892ee0753e4a15c8f987c86450ea2a945dfbe83a30256bc9958c0fdd15e9cb8bdb74e2e0a6e56d78029e254f8351ddd8e9f4cced30314ddec258e8e6da2c3c81207a0505e9e52e4d4b3b7ad6ad28fae4ae233f6430452965d22531bde21fbb497f4f6f4085ce01cf1ab5252799c940f7da5f02cde0e909728da39b8ed899c52f37911241bfa22e551c73edb8e56d2661e87b77eab04c9367b784ab7c01c12d743b68a20b8a7f197182983c6ce10b7668274013224071b0242b8bf92a9a47902ca13687231ee27fde4a06c2877a67849aeaa0f513f0df31d4654abeecce6be06c8980f6036684d2f6aeebf064f74a2d5622e5d220d0d65d414ff4d751f3536cf8a7c91c6f55bf1c1590177f45de313c14a55e48abf08d2d325acf7295258b31d6879020528567848289965b228c2560e9bc3ebe0cd0c73d3febe54d145083be23df1a9339ea9acbf3430a8ffab4f83ab5778f3c7470ba07075c7ad7830ba0f6b92f7e98372f5ce0378580e96d23cc30135db098bb60dec4c3348467e15ceb49153c5743955875559f70e34ed5046eea6402df344d904af4c47c265ea902287a6b2736518f0c38afa439739b2981d2c890db4250bb302c74cb097f5013aa18cffc39bf12af198a1670d022f47934e302181925039a207fd6b630434f7153c3ab633c8ca2399baad006534baccb0ca4e50cc7998c41f087f7ed2a769c7ce41cbdca0cccd5d91c5a09a008902b39546d61e4e803b869606b5ab86ab9f0d3856315938829cf8f842b6eace71c45bac99369efa0adb1d930ab6b492bee164ac6d860480dabd543fdb8d5cf3b06f4fef7fecc9ae6502698f0b1614e9378d56dc2960e702b32b51aa510a212a72c07b8dd4918b99921dc26a0b1869d6316eb1713c99fecab8cd41207d0be7d7b3c10186009447ddd9fdefd9073e81edddd0297e7fccd62ffb8999f13ef386ec5c7008806220e7ea4b0b9fb80140948956781c7aa9a3bcd6d98f27d58d811d2b74af404b06a6d1f7cf7ca9000da8b7ea064e8c07d4d4bf3e36f55018e63bef5e289c7cf9b312007d12c3468007d7874acc622b1a1223b0bc8e1ef1e2cddd4b97ae590605306637b4c44466730314fcd7b6a58edbabc2d65a4331a1abd36ba086f48235d39591bdf6d1719168085aa4b696aa834a511f4be96a521595e55d0b2e05110f33a635fc0c7cfa91fc0104ec716c9060d4b7d1072770e204ace067dd67a7af97a7b7dc16ae795f41a50138b6cafd281d141de0a524acf1803d1e4b1cc4953d84df84cc67c8c561e0530458126cc4069875538b89174664a7eb039a5d98e5ea207cffd6d00f206914b203c94ef8e13cf203cc78769f05829d6a3cf5b030d7de79e2edb56531b22c56f40f9a951ffdd212b0346fd2d3c50b6b38386407fe29ce94141c6122857cb86ef907bd20f0857ffbf6c5fc8cd196957c6c1e1a17b03826f9f96cf0560aa1b152b50b976610a52e19ea99a6d0815a8738a89adedc1bc2ac582dca4a5cabc64368d30157873aefeedd3594ab8c40b4d3f871d79b81be524eb3f02959cbe754d596f90f7a440f50c5f6d31c75ba9b8e95070e46d80ce1b9990267d90ac9358a7c60e40c21fd3354f7124b2b6ddd255fbee040f0c8957b3cc17e60a6e2f256823458dd01c819df683fd7f79560c53bec127314bc930b9ca3878d8900878e383449e864019a684c763c15a6f1d6b1ed9be75a7854755b3b8594d9006205e5ebe1c73676023325b1e533818557cf5bf0dc6fd117102bd65712daadfb8f5c0826c38b0096030348d4e79f4a20af8ab0542df4e88f38b851997d5394c2e7ff8257b1088d450a8ab6e5d3952d9fa523203276ce6c759b0655cc4d1dae366990b9340a6268a70c7041c969d202cd104d72deb6e223023122b76271b108d4dd4d1369d1b7dd320987cf6a6f1aee843a1420141f443e110573b7a4367cfaf2917e28dd844bdaf40000eb38a230b441e9c96780c67f93e02448dcf99eee13c50e7286eacf31c8de051d69f243166b3e1a78b81f32f22d128d066fffa0a5329c330f5787dc65e101029c6246f8e6f9e0c50c8b0db60e8435bfd0428f559fa06ed233525991d741f909b97c050a9c868cebd90409afe8ba41f46bef32386f1b8f8ea14c1ef05412870dae047c9f85c6d1d99e0be0e99f8cae6377918046c4fa17b95d3cb8907060b100015e9c1636938fa9df28f6779f219dfe8b1a849e09040d371546e75aec327a05eaee56576575dae15347ff92a7361767c3332f01badc2390df2eb80904c8be02a97a735f6ec630d1486b24ed3a2557346509c898f066cf145f03119dc1f3c70557956717093de1e17f67098a0c77387ecdac080c2337842b584c457b54a93409 -generate_ring_signature af71df96a0ce2242af718f9f36bf7b1e2e54bf52afe541127dc2b87f6ae259aa ca8423fd0e91f8872e0a32bd20940a577b0fe5939ff8c684a67587c699407cec 22 1258734c3315b929610f4c8215a30412459f294317eaa246b5d8867d12c69363 aab540da690563ec5971895052762a8e8998c754f895201694a17c476dd598b1 74990fc817461aa56686cfd5f453b41a69f8aa901fd4e8d4d1f1a0b5f6ff8c91 be833b7186433e5d760182cd8ab18751036d1b0e968396cf61ef0d903633991a c174deb550d5ce869ebe3ec05bb8fb582cf4e27c35d5c190b7da5de77f28dcdf 8dec97e5c5778667e0ddd106fa037ed7c45eca6ae55c3bcf25503d41bd807fcf ebb1afb03b050a92b8e559eb8e067b545f63d8099821e50b1c99799b7d1991eb 7e9eca4eac87463a8a0706af0dff91dd9000aaea65a691255bf3d4891b55244e 51b1223f80ed22dd385f378a33a14111d712ca3018ba967a587bed35d8b798cd a1c58243c6446bfc860b55045ec7607dbb23081b72265365fa90cb90eba8ee23 41910bf9644f3f9fe19c5a219d3b7f96ea6168133c19629a2889a73de302b5fd ac0940d8dce80710286d7b2122907d0c2a8cbebea6493e44d3ebc88b131e98e8 e10b2dceb0a93a22d8c62e6e4f8c7d766c0733316e6d9af29f6a70293b2ee109 f2553f4c0fd73109c497c05a0524430184d0995adaf87df4b35b48dd589db98e ac12b75019ecd593017d62134be2c9145b530b80d0c604f3beedde286fbca385 366ffcc3d9f2704507e10f7945ca35af510d4917ca6a0f320b22bb9bf2d126e4 6e8396f83467caf6c90a774f7a35dcba54461a71e2ee2990df127a5271b8093c 3d1e476c5c17a2a9d875e8e23243deaa16b8ce4bcdc2dda4f11d60a521fd83f8 c52b53a7882356adf4167237b853e0605029776fae814d3fc7fb9f5bb8722f2d 524c073fa8195ef0ca8eeba82d2182b8cb026a4f0759296cfc02e5a240e6b65a 0214ccd35e3432dba23b879df17a8efcc9e2e56826be4d0ed4cad87bafd20c9e 9e572b2ccc41e5595b3c74f6b1a571dad53ce4efdd29e04fb953b7e8e5abd8c4 c379fc436343668d69d752ee38c2e4f989cc013b9926f3c54568eafdaaef8603 7 763e3fb0f30b0cbe800d09112e56683101a46b96427cdb18cec920c0cbf438015b50e4036b449e9aab2accb05501324a46e6d3c18a972b48b518e865c65636005fd9735954fe37eb3a7e1e26f417523ecc4828feff7e01890f74d01ce7e455013388c440fadba9f1436b0bb9e918bf191fbe513a56ae5cf193652b0f6393130b9b324bb5a4c272f74f870679cf8c07b739f804f2cbe3674247a47d1a4444a5044c65fc7444a864d48f1a88d2595e879d55e64474e8e13c28d61176d53dd52908b6a16e5f432b9ac79764beeebc7df8cb982e5e77179fe0ff91dcbda719d0f20dfeadbd80de1bfa04930bef661c69188bf8db0d552017c4a1e889b20d22b8a809b43221aada734cd615b32eae2533bba902eac73e8db2aecb7730cb9b373c430c5502b08088252b92fa87ee4ac96c50b0babca330b28e5ec75b7cfb75474895059523d2b340ebafb5668d9abdd41a5e0ea463f385c18828466283b61caa71760d3666390e590928010a082d0de714a350c4259a292d02cd29438fb7cddb86ef02ebe1a4517a53bbba21c2e4436b74e91365f69f8a6b419bd81f364967d568b901969bf60869395c5ebc0b7f72f58d2a6caeb4ce9944c82ad97d6b2e018aa69e049e440e61724b5be7cd21b81e7568676316f9c7b026aab7b0b89dea15f164af0a02a4f02333638978729f2e8dd0166ac17d7358d9354d401154ff41d24439a809b01eac839e91bd5d86f969aefb1d909d764a22b1c826d75deba6b68311f6130960468816fcd11c9f67db2cca1ebbb7854c24bef4eec790b5294a1bb94de08f02fa871ded3635c9cf814a72bfe7cb0fdb22338c779ffb052e821d8f2b65ab0f0cff348686b620f6ad372208b5c52c3286950955205d2eb1e6a12dd46c93da4c0d42fcd13d8d1f2868c5d2ae507d6de429deecee423581d6fd1baf87a9b559fb0a3b10dde939a9ba43d2c72f41259701251cc57ffeb7c5a015d1b1ea9a18c86f0802d4738ad1b99ad12e17b989ff7fc43e475238e57ea5ff3f77afa3f960ac2f0552f33d7825d5baff0f8233b776327ab4bd1c494a8fd1d85838d39b5e65f5cc096662770e6f4517cb568f0dd1783ebe46dce982f1c7b6995045de5353c30b4c0ae92a5fd8e6188539fcd2ea64e24241f98f8cd96e38e4142c2582a3a2d38bec0e323f6c63143c55bde300072e78a3cd82831ffc3a0830a74a09b97b8d91ef7c00e22efa17fb77612d1b3235e8bbf4c60079310f3689147e6ac3b7f049d69236040a6a5ca8406a6581108e611a7d6d8729cdb2a168657d85e683ed539e32072f0695361c3567b28fe7bf690c737d870467b645d74a01054208db3eac0a6ddddb05324ade8a016e28758b0dfa6f4f7856ea9969cda255dd93424d2487080a9fbb031740e026d90fb3c70397b3d154fe18e8d9c12ad96d1749e975ee39d36d1a090091a718d70d599c57bbe5da7220c675be9f89bc08de0f10363b5b8e665596e7039092f11dc333aad6977e7948c33c16c99d2d9cbedaf76142251743dbc381670da644d46fcb0bb488beb0596074738affebb02f9bdcb1e482256233c0356e930f94ec699f811d7ed98e43107ed541786f6ea6b16db0fe7c812663f29f2477e00f46d8bfe946589315e0a1e60dcbc21aadd1a4fca1134b0f5fb832dfa971c02000a7dc27699709cd03e8d79eaff39dfcfcb5f45b1ffcc33d184bb98b6784820a0c72d243ac5c921a3709adc963f032a445cdbda3552002302b1a65549ce91dfa078328d9a6fae6bd75499af5dbc3861b0465810f1faa917c813517bb9ba8080c0b74ac88cc2d1fb879342c6fd5ede58a594771ccf1921bf7327f0f65cfa041b2074fbad0e74b283c49c7ceba5e3b968cb9bfd70aee408c7e0c21bae964c6476a0622319fe204b5b6220ceadb0cf6a1d06b7ef2b1c8e0a52bcdd22fb5860fa091058b275cf8aecb93d8d0d16a7496711f3e9c59ff66ee2c656d64064f7e4790a006 -generate_ring_signature 4512063bb19daa1ba31eb0145d48f7dc9d06fd806ca89540becd2b5e6826c1e0 89a8e66b8da6a4002c7cbd2dd887bdad6a08e3412d1972c1ddba31be29ad779d 4 104de0915736fd728e8c86d2458441d876f459f995c7256a276534f438a614f8 05fd6561503fcb29ce98b70157dc619b5c4aba732aabb0b0d10b8298d1f5e155 2c1a30eb5f25ca5ef00b7806c934794f03dcc5ac58606aec2081f9522a216749 133392cef31dc52c7de272824c7d487327b3320b2eaf1caa827083872002b16b efcc49cb457ad9394617b802a3be8b9292ed7e88c782508899ba39a152e73200 3 d7fda8eeaefb74e325f37c9aab7f3793933a88ce7d8d6b1c165b879662b77e0b30beae70a43e88e2651a6f6f3bf9bf8e2eba6a3b0eb03d47a21d28ca102ac402f17159fe622ed601ea97b97130e958becc793092f9f007629342e951d3dd7503518ff7a4efba6da821593529d5032e1a8875bee2e2437f78498eae36bc0d410643cd91f491797b409cfcfd3e3a36afc606d6dac4a0f6c262b2d4b72fa5f8e60cd6625ec61141aead0f1c82e0ac502bcee41e6920bc812dfe2a1596d067a5420bc9ba3b55e2350d2b468158671e5b4410c90f746d41dd711df216b6a00013f50b6c406be09f1e62e0865e4d82ac47bea3b15725894bd24050e1e67ff95eaf2208 -generate_ring_signature d4a171b6e8c5a7881d47e5e059129c134a12972e6cf446760d5ea3f5d9d8a526 4995bf794e131c0a54e6b94218aadb1062967bc29978ef1e04a5ec2a622bed38 2 8a1e660522cfbc1da70e1f269efb738b9efe941435fe7ced85bcb8b2b445015a 52a0a2d5f69efa6666a07a177725d76f60a1fa078e7328132ea81e2a1aead5f3 7799976d0f2f488ab7dfce43781bbf6d8f64f4c17c6dc5e357a98ad83f23a20b 0 83a85c50a6dabbfadd6a59c5048b125977c64f0b26c7e6926bfe06cf38384a0ae8b5cba8b886d47d74e9757fe8467de538701118faf961bbd43cb8ac6088a40bc447e84e14ce6bc466bd9feea5293ea772eb45c9041b515354af619e3017de0d06ac23f54ca908f40fbd6e5603fd09b7a7ed6ed271ce90ca6187911dfb50720c -generate_ring_signature af87b1fedee0e438f10aae5befa088392ff4dfae1546b8c8f779fc50ad51da97 90e690a1501ff3a3fb14070bd46a4b4a7175bb31f57956486ca61f2e49cd4ab0 9 35f64d12ad8dd1c71084709e6e868e68b934eb407aae5fcda2505773503025a7 c80dc8d3d3abb474b56a5c054df286f87f5c58881451feb22cdbdae8f3f900a7 3461ab681e2e79261f83d37d82d4982892320fa386cc6e6f5bed308bc1c13d12 46d70d6342274b6fa1e9e3d5b5de51c4627000ba00432606cb4aeb362e25da6a 2b561ad67dd4aa1e38a5fe1c8454218127a73881a206b24862e136cf2b255780 31053ebff5a815b078f8e94d9f0f044f1688b0133cd883d479a94ed066a1d1c2 968233788adaae5fc7466d8390d94d6198074b114e2ec9b52c4d851ba6832166 f668b4ef8431e75554c02db408f359ef81436a0891490210bd509923a7e0a749 fa4407f6eb50927d9c17ca60b21e8129a19684ddb746e4c186b88ad5113dcc86 020bec4c5ca87f397f5c0919116c54fc4fa83c1a650b045ee4385a3d7bb40d03 5 857d033564c56a0d64f0066dbd7cee44aef0392496875d1e21d4aae4c645280d459f36990dbf5c3943f6313370eeaae5401d4e9a10fc3f12839bac17fd49b70854a45543b801057ac84d24218c4e0a0c490188bdd2a1ef8880260db5e6ae600ffdcff73d83f7067f84c566b50e5d10a424dd800f25aacfaa64a3907c37a191014eb39129dbc48ee48a346f97dbf007f1c1d599ec0e50bbd05f3c90d67a4c32057357403af89d6aa073c5e69b437061a646400435dcd4871278c0ddf8fdc7910a0ca61ab2926ea7013d1bdb6ed8d8f8fb0be4bb9cc424c00b7d4985f5169c07041af96beac30643101b7bf19d15c08977492070ce11ab4b5e048fb9911ea40a0043cd96c1c119ef2977a110b9c34b63c725659080b3971fcf2f3caa696a6fb2006d361edf59a644d3b79b1fff23e2ee1581f377a0d130a7aa2ebb75d132e468098f91f561c2aee9ecb13a72ec54e963f45da92e4c4a57d79811518a3bd0311a08fdaae767b1dd2bc33a9bd7cff77a097f53d0c4c88160ce7dd3d4caaa02df1c070562020278b369f4950ec0a494fe33f91c83e290b8562739977371d2e5dda60f416202d6c9b4bdd95e4d48494a8ebb88dd83d51787fe8f2f1434ab3901a8c0079dc640fa0e3f6c3cb687dd7e11b8df4b47873773d1e0eaf139aebb940f98be0c135c85fdb6d54d5055bf057644a7a2dd2227dc9c6646a336b828c18f4240fc0766c0b26a5838db51b9c262040434633979960e86c2d2748065a92bc11187c8044115823bc810f4241754ce1f8ca2e64a49d23011638b0906b8f9e32f54d12d0d -generate_ring_signature 2d2c11cf68486227b17b80fe6353ae59abf53ba0b8bc75f32a61007b557c53ad 35e426514c73e7c66880bc26f90365d770c31fb753926f24d23848697bfc5a88 57 a9919f6a2b7acaa39c078c3b24e3487a89c1e98f72c6deb29190efe461915b1a 97078cc1079d661dd75108310685f568c813d7486064fc35b2f69b38fe6e94b4 d6e58715307fdae6fcf242a4413b3264d0550ada54a02a233e3488d63dac0307 a6eb853278a50ed8333786b3b15dcddd3d8d784cc647adda671f710a3cd46bce 204fc5f49b9e9247b2245d0b29b32a697e6cd5b1c4347430cc2002ee6682551a 19f3496ab38450705b89e6ed8fd2c725a72ab9e04daf81b3e84f762c6f368858 cdaab9c4d1337195fa27a23ae9beb85e1b421162b0175b01e5022a8a6693a2b5 da64b3606b987f43092b844e787c73e147dd3f1401e5d4de45e6defeebc4c542 8636da56ced937d7942364cf6791b38e8819902811b071d88a7e9f31c9158318 24d943b065ad70668edf4dd47bfc174333547e92fe95a4840aa1cbe343ad8c7f 4f633908fe98dfeaf22d5f82a7bc5c591f68f0a5c087df556804a218ce5b539b 550f1304058b94162f04b4435bfc4190006262a55c326750c93aadc37c491239 2a8a480ff6b6e352d3eaf025a8a2d5f7170e8c72063216af052d14050d68573b a7a57ab7e2dbb84b1b687ce4e37c966aad92e8d63a80a67b399c3bc1775bc570 b1b394508b9e74e44ed21f6f38aa8844ff16856e1179b51b73968582babe8570 fd472fe1eb884e3f57567ba1ec8d6a3839e944cab78e639146224f141aecdcfb 5ca43dd0779bc74b551aa2279219b77dd51b6d84698949e23a5fd73663f5e5f5 4e4bbaa324ff9e710ddd5f5e837df67df9ba854afba57819b919b3481fe374ff f9f50fbf4109310755f80a3920a2bb47e297e4420b5859045819b8f2ab68bad6 4bc0b971d4bab80d4013dd6cf6363ed852b6b95a94b83380eceee179611f0126 9f5fb2b0052f5a19f77eaf98b94282e1f62d09b3dfd090074ba6a99d7d3cf0c7 570157fbaf6de931c767a74e01be9e00dd875477c18a556c93187cc9aa9f51b3 5b581778bb48ba15fa9bb4e03524b5206268bd2ad52aaed0ce60ac577cd3ef94 00c51c2ea99904e5d933635fb1715c3abb4ff07642aea7dbd46624fd9f1d825c d50322d0ead2433b3977ba3dce5964d17979cddda84a13acf5f5c824f110cbf1 bf25117e9f951431febfa8370999db5159ca8511fde188cb0166624773c5f0f9 4c64494a15cda58cc9def65c4746cf88207d32d24b90a0c134dd0ae01b09c144 01032bfc7e3f6e6b0a30a3d265e01e385771c8886aeaa6948965c05728b056a8 060f286518d97854b81a3ab11a55141d4edc97bbbc93fb0bfde999234b05400c abc25998dea92a2cc721cb7bca4ec8890b18d31fe9b130e706e9f9c231933aee 0d462aee190385626534a8642fd579da27e8cd596f90b3d5f5e7fe55407bb405 04042ded8ccaebe4ba0dc9d67fb8bfd9f51c6a43f4dc154f3858b2f34624573e a8c3ee54ed71f11805178141dc71af46d0218cafd72ea5adb3b54ebfbd2ba1eb c4d103d2800689c4b1f2b984ee4b0565f5e214b8209fe622db3f0b8b8fbd3dfc baa5fb8c4beb0204f6be08ef6aa3ea2351a81ed5f169315206a089e7abe8e691 9f69cd4518c0732b7e0d34fdf8fca4f449b391d66986f4032568375532a596bb 2703488d85817e84c47dbfab7d96a741c5603fd38674f9a14dbf9c0785f90a3e 18271655568f950df4aeeef27efb9c59680db900232da356f3bc940e77a661b1 a260915c171adc5c17beb5b39d073adb213ec6206ca8cd6656ad0207f0675c0a f3304b1d21a211a6a64be267512752e8fe7e4682fdcfd42676d4fd7fc49605f3 7910d249a3c64181a1737103cb8a0cc85dec99873b99169cc3995662ad1c2397 b19fac0f51b23d973d0b7daf502321f1abcebd28f6f8abfd75b377db40a0b99b cf3cde5e0b30b42c5b610d5087e32d21bbc819a6ee816bcaf928ba20c45670d0 64f3ce114419a4bc0eec99a1f65fcb2096c94c65ef8a5ca44209c48b6f364045 7f757bdfd7cef93d1ad0b987041bc741ec64dc3cd78293506a5bad36e0926d2f b0155d119bc08df378af49e9eaaa856742bf7783f3ee16d9d545a6064f12acb6 5b29fa21c990c8b43186c1d6d842152a2fa26eb6c0866c10014baac7c82367f4 6fa38c3bea221425d2fb979f5666b6b0214f9fe9ce0abd6d39427dd22bc20c6c a6f86b5127deee81e39532d897d37407dd7348beee9685f0cbece5decfa5d3da 1827e21b68bf6d7348b325fc74d8f65289f30a38ec4f84bfb126d3d94a6e8cef ba62987bca2eb991d123ef903a7377415eb9ac77f244042a87b0538e233fdac1 be54b474591ad0d58a89c6a696abdfa17005717e6b9360c34cd95634210eb8da 78ddfa3c43d923ca2037e3774c9c4c8260cdad274487ff71dc1f5b739e0ece84 6a2184d5c8013feb86b8fdcb6885ce3331320d53f4c02245bcc90c34f4f29bc5 db01a4dfb62b753a5ce1109a566604b9eaeca13064b13ac2fa24b4bbdb859236 de3e15f15f67e61b3f05022c387ba7029432f417c1ed96377d0fbaa9b9474f4e da88bf9678f4d1594a8899ebbe347e7039f5e23287dfa64d5809f0e70a359f2e 098a6833175b819831237a5b135e325dc1e10b0d348a07ca831c60ca31862806 1 e508c2cac2b16a29a8d11aa5e6e1e63e8ccd56622608d034c2d9b3d44189d20185b0a84901fe54f3c4a5ce893b3d6c8febfe11e7241e8175ef2d17c5dbed3c0890fd6cec41ec7f3f87d9b223c20b5ec3f0aa3f4533d2946fabd155f68e0f5e0f0a4a4509b1e49a144ba59f4ae9e0f94970c32ebc5a7a613e8c816f8d66b4e6037950ccc2b8cc0856e75968c193a02f2f3935eea6f2d7e267400fdc5b71675000f78fb94a05a5a349bffcdb6b845ef36463eb3b08941118239e243d9e92923e08674347dee5956f30addb45eead95b65c033168c0df7382d7fa2d57070d6da806f7be2e150e525499bd3b7ab02b9c516ba8f903290022a60ff1a415e75ab4450c24f3d5200b74945fda47261b513e80de02a21cc10c30126d812887e0518d100fa6ac743e1e96e2bbc2dd2dad4dda796f9a9b55261f8cb469436c13ee1bb63603737503b62c4e93d9e3da46f68e710bbb83a5845897774b8f2d7b51bf2169390a41cbacbbf152f8ec2545b985521cdbd1eb7dc19004e73da733a6ccc0d7e56100c81782e38e281f5a7683ad424d4c8e84c6e7d306cbe0f6a18584816266e8be00a848a5dfa85553fd61f888d41010545452c2a5d77886466628553741e186620b15708bbb14a9c77437f5532ce20c805f1d38db9b341a5ebb5659d629924f710cc0cdccbb6d4c24b2930f512ba08f206d421d7b4d74965f7a6e5bdce9b7a34603c2260a92ffbb628307871c1924f067c4a4a57283572bb8bee8b0de9c76d8b40b4ad6bf1638727de736337288d39bb22c6babf44e3359cd6c2e017b3c241c300c6be4411a488afb1917a8fa8fa257e02b8ec041b091e763533c40139ba5b1fe0b05a1a0f628e296e63ee6518cd884dc7913f6bffeac4329ed394e342a2fee6d08e58a30c5a066171ece8d32e8f3138f5ee5a93f7bf5440d82e87b6026650c0204e8063eb54c024ce0daa8b9deb17160784f72ef9cf57cca2c8ad0a8b1e705bf04606317b03bf502a63efa8911d1974048e0c2bb74189a9a29ebfa69b6f04c20097343de5f75b418d14bc5d7869da33f8af0e73e3ccbe8ba13bcc4858746e9830ff99baf481e6c93591bc4e18f3d30ae2ae043378e9a702395b42dc67c0e5b5f0068a37a488aeff576c8d69a45966b4ffabab205c3d3b5a5243871113ba3031b0c37ef86f1602094dd0c0ddaae32abedc6b504293fcd91ee5d7c787616962bf409f74f6a7b3b82583a2cd3e6bc4d75ff5d68f0c8ab5265d01d50ec5b5a805d5703f73a1748a1d4ec15fcbe9201102ef445e11a34ca2260be5241456f7359de52062bd5efc717d0201734a11a252a3486a1108703e56902294b18f2f805ec71050e3dd3e038981ad406f10244c35a9ff4a3717b65ec8b936c8c34ad6947767b7c070b748222de979f599ffc3ca54b8a332123cfb7a0695de6817f6a083dc62be402de5c9a8a234deca536f802a978a93996cf7ca22734e2c855eae62ebcca14a3041ddfca4b798f105f88f6498492358ce560ec5d412d0b073a2423bb451df43606fea10b2b42ddca157ef4073e3646af320f1c82dd3e548d6a03b6c65fd678510a2169f657d67c759b58b19374e837f4e775d531b37b4b24206320aafe1b854304da76d85948e65f4cd1802a373c8e0d53c2f47a9698b936db3ac35ad59a84ad04b5cd557acd35a4532ebee896acefad53bb91ec2e5bf72d29b616de4b01210e019965cd577c4dcd24066276304b325b5a46606db3b1a80a66297dea8a7bd99009e041b4d19584058eab3401e45fa62d952639d94964b822b9c47c400498481a07e5e67b56545b598b0a58a7da27323b807b580a0331046057a863c495aaaad40a5a1eb13384fead3107d5d2325511dca8c448e44a93ddf1df024962ca6bc02d0fe400342164b57c8bb06007df883f900247c9440a284d4ec66e31c98af4e3440182994dffaf7fc1d2cbe94e09d02f4a74f0a5d9ff2c9804a1d34e47fade341607c5ecd1f8f01918d10b411cffa3b29216f21177c8bbf339857cbed136b6cfd20d8db0043ffb551821b3cf168455c51939ea784004689d6ca17e6ff87f26ebc809ed74ff82329a98bd670b5261168a14b781847ee873acfb60da960b42f447d106a3835b7158a3e9fe4f11d8ce089067935923c971fba9f2dd1eec428ac4310d00d3f22d1203ea82213c16400b5fc7d5839144307b25cae31b9c154b3b2609d70adfd3d89648ad072967433852c44c07a507bd09bf351f42a21626452616942f05f42cc02cb8416d2719d808f369fb728ad59504433a4777217219ce100159d502755687093e1475ed224485609ab4e96e9b92ed0d2693487dd265a58e723dda01dae8e5a1ab9f802fd1566468da6c5019a4c942a13cc6f5002621e35fc90e50042d4064299f01c973f585c6554bb5f5f7911c86528943af1684364bdd53f8340fd130186f3233f72ef1fd45e5088b59739d230afa29b2f34a24b0c8b1a06dc10d43fcc3db91c117bce99f54247afc218e7cb7ffdb148090e613e0b0a7d9236900e3da11d242d4ce4171a1d3f33faf6b315f38868e58295f553f7f2f87fc65f007e8a2add5c0549d88b6ef6a93afaea8967691040065091f7b38a4edbd7aa0e904d4d6e02d3b51a9a9f7fef8a13bb7696b1a81439bab9a1b27da30da4c9219060a01a0710405814155cfb2a01f2caa8f12ac2748c1691edcf1a03bb4e45e7add0d1d0b44e4e06af38da1f998b55b495d4c4cf7c29e55669822cd16619c64fda60176af6919fea49781c8205210ca3d7adc2283fa8623971638b431a615a0478d0f486c8bd6c56ec9121343139d363c0218f4c2b8d74ac24465c612c13517095703fc3070e677443bcc3c0f4dd9b3ff2d04e62dc955254af9540f0156b023fa9e0f8e8338fd856c3c0fb8530c836e643fecaf501f4acf5c68f3e3035814be98a00dae0b01fe02fe0f2ea04142784444c7769a77299fc3d3b634cd130a257b31200bcef08af0c98cd22ff5c5b1cee30febf62e4bc72e02ce2a3c4a618ddd7c776302c18437e403ba08a4adeb43acf06e806568b06eba4bdbfc81571a1c905fb2db0b90d6c9a373cc9d211bc5075ffcd0f3e9630bb95fad48907bb992b2df641cac003e096fc2d74ff87f7e92bb33cfe45f62635a848830fa42ba62859163d4b1390982e981ae5b12c2bba168800b967b1aa7c09593b4b9daa2d1872c21c0ec8438038e86322d828c6ab0b8b32ae5ce7be0cfa64a97c421a16b6e2970579cc045620fc3c135eb0d8b613f54e03fef8f26cf1df308854a9152ad17c175a3e645c59e0fdef6c6319b6eaccbf5fa5625fe8813905c196a61e6295bf6368d7b7e3909f80b3c5b705c107627e2159cbf4e24105690ef7e4a76148ffe79d671566604b4ed0e75479e2a3407da9172ecf637c6f6e940cc2558246336250ed485fb859286120e18d8f3c7c42e531df6c223adf732b43277b3bc2d1d97f3e89b00f6466242020cfcbc9f3eb1448b9d064cb6a78bc783e585646de087d20a38c0f52933d21a6d0076be1bd3189af0870b85856759a0cec4d12e56cea4376e41e75293183ffe670eccffaef5f875c4c99255fbf530af2df360ea5125c049ee205ec0e3c199c59c0c1a35474e7086f022d2a2e7a652d8cf6a5c2fb53dedf77c15b4a43e11c0d6ca0ae54e46a92fdc0ce9729fb596e3e064ad38be5fef042920b492a502b8b63ece097923be00922ff0ac7b6019879ed247b19e63fae2abc441d647f25eae3891bc039310a4f1b7453bf4812cb86083db704fe970a5915c08805dbef1781c945f740072e7a4a2e03936b721d9deecf18e02da1a721d30d7c980b0975265244a37af0e4b0e884ffcfea083716bdc25c78aedfa0c7d41b5facdfd2a1de21af751c92a02d939b102a8d8df9fdc4bad935e95393d9b658cb9309dae4c91b09d0ce2a59b005f5f5fc060fbbffd6d1102d2c4d9fd695f217fa04c2a44f9e1fbed6ae36a6b0645b0ab7d7c22830af3747ee48e76fedeabe8b41b2471d1fa2e634f8388b59306baee512fa2a289bd4bc6dd2655c29abbc0139fa72f3ea4256a4acab2ab06c60c2c7883e7aa26204f87904de2d1c8337434f0083c129cf303ccc318d3b1dd7e000967d5cfea98f162f8f4018ecc190f84488d469603afbbb07271270561920c049f64a672d0aa28316abc87284848a745eb13bde3aef7cc5c7725f88e8a7a940c981dbfdc5249dc0d2dfb40bb8685e63a27eac0a8c849852ba999295cc6369a0de181a79ad745636217251fad948c8f5521e85f1b7998e88f8167b4bfe060d502ff155ec5583fe7969df136c74e3d8f315f66e69eed51baf0ffce59977abcfc035b46109093024f5488bbcb7e13dcfc09592c6925bab32e87d596d1f3158f780b49672a7cf2c6bb54e7c8de272a6c3a56e312a002551975632d17117530174f09d1a60441a5ad9520a33ec24a7e4c3321fc2bb6102565371c4ee1a2e9e8d8810eb7743165db86c9960050f21b08753f63b68bc71507d18cd5360a6bbfa5b56f0ffd07a1d36cd066bfb0d9fbd500527c14127e36d29c2e99285b49d70d8682bd0892008db552114e0a3e51b31365d1774a5db044dea943610e2425b250abecb20b4f29bc93a0cada956eb5d9311d3e88dffb352d6b2bda4129140547699357a4020e97756a72288de85451fe4045bfb0666eb23b6d2a8710e566daf68aa814240f68b36c7480500784378e03cfadd3627470440bcb12aef642caa359c079647c04f76654d05c47812fc8ad7b9b14004d36190fc09c9cbcbdaa955834a58790b506b5c6ecae68f99f50bbb9b3a4d734bec7c09398c3a9d3264728e3503de0e3a50a6308df7055ac6b9a214b08506847608bde8f21e0b98bb8f664b849aa1fa206080d4251f66c9466997702564635da32b6a0a86e2fad189c56e687d17727d7ce0be264d43eb8cd05cb4c68433c832f40b160056409ba445b6b041824e98cdc7d069fb5f64695bae457f3c3adeec934e49a28fcac905ab7d8112a665ef6fe2e910f029db39f2ca48809bef56b8e0a52f290974999cbc88ea543e87c4bc2130d320aa84d72f0343a1ec7f592dfafd3dd0733aa209cb4446f6cc0e9124448d38fc40d5d877845b6cc24263d829dd7e01eed02078d81aa95611dd07c2c6e6b48aa5e0b -generate_ring_signature 5f538e02b1461f07c98077400e3cf540b0626000edcc0ee4f184bd1f72604f9a 6be7e0a471ad7fee54ade0d6615b22f36f5ef44992917c3981ef7a0a4d561da8 7 4648fa9cfdae1cc58f504ea9031acb69b269d148e581e01294730540f5ff6930 9dfca258921efd4ea725792ba16046d64831fc572f3dc72ee4fca1100b966ddb d7f734b73581e4fcd011a57ccf613b25ab32c3e61dcc3aee5b65ee0d118f3858 e11aadc5cd441dd0c48f584c8f92cd17d1aff66ddbb7f86e4ee6deae1870f0c2 9893530b57c30c46ffd006059cc5ccf20a35632060107d8ee0272da1cf219b7c 0f5a132da48e2f8dd651651c00026b9ae41d909503fc5e7b64c808201a06d2fc 69fcfadb6d9b4b296d86583324f22d124fd6764ce0dc6331efe72adef2ba1a26 02043a6d949d79e1a8c63c231e3db3bf406bf741d9e4a35b14c41e9813e90a00 4 e1c2364eb6d2446de63b947f3f091bc48f2ec86b463dc7c9a331c79d0c23540e2c076ea46a739c7b8a3c2ec3484bfac82ee0df9c62f60d8996cb0dee83c0220ee54632928c7db75036b24c9285408d5ac065da69db304ed30bd29c68bb8a41030b7af12878b19b7e6990512e24dcafa11c1955539b6691b42380a87be03e26033fe95445e26a095f30e7ecaeb8744f89b5bdcad600ef2ce7e3029d9fa6c9e90d86ea9a4bdd61a7d3cdf7dbaa4552ea350c0d3fb071d0324d6b699e73163d37041e4b7a60c29f5744f422f433da5e38e084fc74b836a1c7c304519150105c9e0e002ec6491e671409a7a697abff5ad58b91c709a9f77cc49e378dec4e3a4dcb03d292be27b6ce65fc9874bcb258dcc73a2c65af240fdc52caf37ddcb1ec6c31053b6b5251b2b272bb97bafc62ff8e63ea1989f4ca09eebcf762d46f57e7bb2b0a38909fbacb50de71f8408632ac66d5e3f840143be0790a7d4d1ffff925234d041447802b03c8e15de3e4757254718e203b5526e13f19c23d84f9a8fe6ed107068d2ce2aca3f00193c127caf8fa38bd90235d292e303e8ad92068c8ffd302f50a70b30b91ab281fc548f2d1dc9c15cfd0d178ea865a3673af91d94949cd2a2c04 -generate_ring_signature 39bd24a7043973bd7a7f4c8e448e7787d1e5aab98f333b25391f624a492c1d35 e7551719351d2f367292d49a58da045bc9af52dd31cb565a2020f5fd3333728d 253 87211d95088dcae2b1d8db1e408b1eea3050193114a62975b8228d69364c2e16 d8080eb5ff65b1e88262670190e68166ea45b3b603b37b6d7373cfaa636f165e 241d5a6bf98df975d09c3fd82f28daf686e24e8d577632498cf47a80742fa4a9 28044c0d9ec190be78c95859877fc3c71a5eafb944d87af6657986098d5df61c b5380a0808e51b9bb85a54c703ebec3c1dd5d00ca4c776d3a4d88a3b15fe13a9 9b02e613f1bd267ab8ba9f17e1f44dd254e539ac7c4ae51db640be7d43aee526 b4dbc4c9bfc1aaad51e0a4605f40f3e4d86bc7483e9034c60412b099eb36dacc 94af0b33beeef150aac1ed32fab925099b688b72a6d854115507e893d15663c2 7e8e137d9e882f0fb9e8eeb29e607cdb113578b086780a0728e926492017232b c025e69848c70343a238a934746b0dc0edcd2d545af7003bc00e647cee2f635a 80c47dbf387d8fc014051cdb257585f7b85b08e41364e3a6267652db57967cf8 2f16c481aaaedc8ba701a58363f4574d21fbe7d9d3b225c200b7fa9725f15948 8ea6e2ff9ce7991d7764a92cba2c865f5e2a5e258588a43f76e17e41de039808 75ba819f4fbfdff82330cc7afac927e72fdd861cd9243693ba5745891311fc15 23a6a044fc33f8f2e7f8aff58e94283ea45fc8202f1e84c71914073fefea600b 3662f25a290356e28770c62bd711ad6ad929ecab25da3f515a3c053e6212311a 192b423ccbb0d6d329e42a8d0317c8a00db0e5a2decb455b79f6b7d9c0737c06 054535dfacb051e8f8cca57c43ea456cbf563fc80371b8ab750b4d9e660270a4 892ebd8b4d5abca1c059e3aac1cb2543194108cd999debe33e7a65d112760d2c 6627c6b5e09c06f9329edb030089e2bd60212ec499bf9e00ead218f3b4c4a94d 0323b5f1c8df8fc71595693abe6a5423cafcc7379ea59a1fbae58b1c820f5f66 8cc337574d514c3b74794eeb2cfd25a2b0c10768e631bee1c08499ba34b08c50 42ac8825861673461fc8e350040863a663fc03a8c7822d4d97ed3d80369be618 c4e5dc0e598b2d75087c6fe50b411588803f1924ac9cf53882d952b6bf175e20 f93279c53d909208453b55837953825d8fce2dd9d88d98917e7946016653d867 094f2ff19126af7b3c0f71d8f0d569edb7d4ab103a7b541a85defdfd3f516473 00da2bfcf3f2f224fc3536837ca93891dba7254213f5314015eb6be4f365c6aa 99fd38e293ec436c242c51b8d7688a1e0da8b8d9498f568c8c67b7fc7b03ff35 e8b3c064790b8c6004889e828c28e4a13c94de102ff868c0cb30fa7a1b310455 e009ac341d6cc6f400c9aa812eae38b983ff1c3950ec0ad32f54a637714207c8 41d6e1013a167657aca3c785b428458a53751a3ce4f55e7f69d1c2a732dfceae cb6646dda6cc5d0f46c3eb31b6956d944b63687ffd3ad12f0d58fe4a1412021e 989c30d40c19570e0fce6aba0e6789ec2351436a0deac9f4bf373a5621583610 ddc4aaea5c7d9b2619c2cc955076f63ad67beff0f57cd678a43b6aaa935629c5 7137f11a9d15db1ecab14ea167381fa81bd092040a9bce49661788380f5deeeb 92670f852c2cd07a97830d3f4db9577d9e933d72b46524dd05c65325d9a2f875 d053e29032574a0eb93bd7f30dba833b1da8181e2093e3b50d28a5f39dca6b6f f4e4496e938470ad634e61275427d3fb275fcdef514164975d93a320f5882e88 80e0024f178225cb0b40aedf698b8f4b85e413a34e29df3e3adf9bab15296e0d caa36897b70299c47f3a0cc3aa7433b9c5273e803dabd5986f84763492a41a90 7cd4fe492d5fa570d769270c0cc8ebba8fb7253dedb141801482ecc5fea15a98 a2a76ff27b18546a18b6bbbeafb1ece1272a03e2f147e9bce94da684f018e322 90a46155d50366bdf2a2d7e398aea33224a295aa3455fad40dfb4515d365d60d d08d9934354be29e663f1c8e50b6e3610d8fa15c10e12feb4cbfb1d64a07a059 8555cde97863f54e6b9ed852e3c2a26c2ea56d815d710939d89eff70153d579e 265617d3c9973965a217a9cefc0200f5300f3ecacb1487add5e1bb141400147c 6a45afcb5a02430e57bf0af7572dfff1841b9740f5e5052540db3dbd64fe3cc0 1744e17c9cd258c8a7396e7d704bbc4c6c13b06cb92bfd176ba1567772cd4ae5 9236692660d3647d50a61c688725baefcaa8675ee933596c42886d7e57d81b46 a8a82c4536579ebd4e35bf3030b1d18b00f3b56befdcaa94e6e6dddb00b3aef1 eccdd01e8deee3c4ea8fffc8b41bcfe089da7fc2d0d250c8d81c790a0a340127 a1b33761a1691e68c0ddadae7965b912a0a2578b27eb6e5234cfd1f6bc175dd5 65df31e331e3a1f31f01892821485c796e18ae9be5046c567c2f9f31edea5eb4 f51b804b2b4c7047e874db85fc1f827c78facacef789bba5ae0bac362a8719c3 cefbc7087a409c7bfeaf01713590ab8ba8cf200f0e253ee24513acadd14eace3 5e24afee28b5670695148232ff4ac7aeb6d028430f7e52ec699bb8d2fa423543 75ca63027ccfa35301adda5929b6d34db711287e8c20049f04682940a2574cbf 6d028deeb9f33d1e2dc9a7815aed9d46c8bc5ef5a24ee180c51b992d02447feb 0b4862a56199782c3db8d96e126b52966d8857c030bcd12bf78dcff477de2dee 6f5a8b4bee8f6d5f58db280c52b72b24011168a95f856460018c6e942cb93018 a8866f4e5582371713d07a92b9db45fb32f27fabdb45fe78b71175af9b9abb05 f9abffce30898c135700ac2872cce9820f6771733cfa615a271529c04e619c2f eee59d2fd563cd83bf665f28f327f28f2236e0d7c72e034da73634668014a050 22a58e501959e7d96664eea0a947f95643cadab389bd257649bbcb75e52b101b df4b5654604c4fa3b69a9303047f39149a9ff62e5294ce1cb9f03a6599128759 892b52449e8ade9f51e72afa4d16cfd61025a0b852b4aaf0b62771ca4e4d1c03 e922bf917ec5c7181a864a2b0fab689c90593a47591b3ad0ad442ce8b632aabf 09622f5fb64ce4b5d67a424a280af79dbaee60a557cada15a72045f27c839617 791107d5f62b4de6c11a38bcd84a836ac5799cdb4fc736f1f5e14f8d4bb56d9d 3279edd7b2fcacab9810ac647eac12ce8b16994829f283d3eb83f8e13a4d1429 739d415d0f7d8d65675ab5dee02fba52b22baf769fa331477768e8fa21341d4f b6bfcd65dbdb070b48930d06a83706aebcd7be61fd7595c8402da87f09e8303d 9007464c9c734c561094b29f09be140baa1565ed3c09196f399e82b489ca5aa5 be6092d1c575e39349a043b24d3df2a8e9b4938e8ba67796428ad0a7309f808f 2ab34408c3bacd9bfea53991e4a6e69ef5c6bb25978bc25f62244b143f9bf52a 76a04b6675637ac67513c8e6f9d89243f059e3407a7d04d534597bd0c7e46897 06325655016660438374f04b5190d361b605d30a2df103fc4f1acb7e28b7e6f0 5f3db63b41e7c2789a9839ad5fa73abf78d9ee2a6ed7fffa7ab485ae0b1c21c0 481f3b51347b46eabc5136721c1edf697febbaefadbd1d0d72f8f792de0fabf0 a8fb1a7241ea84c5e50739aaea6761d538a042bb33341adcf37e088aa1d51477 10c28dfb14d43cb7914d21f8a5875d6825d44258b8cd578706befdb2bda02092 5fb5866fb47158c2a5bf8abcb52a9c53a2d4f9c0c549d352394aa96b79e406ce d3c12eeb91a1d62781ebb072b272ab1bff02d2fc5c0c8f2af0db54696121c282 d7cf291e972479633e7b3b3c6fb8019fbcc26888e6048d9e78eb63058a5ea47d bf0a952706bbd8adf29a83b681560fb826be849ffbc69d7fada22e90fe83f9de 83b805751bcde4a3174a5f6531efa627a47b849f702d6e7ff871ab792f0cc818 c3e23cbc18a7aacac51625ba45f180e4fac85c76df506553f5c1864fe17c086c 3abf67694d6026a2cdc3ac07aa3281e527594c551319a2e6d9b257b7d46b58a1 382f03e3d26afee7c3f7a8aff90539d0b05521bb3458161f05e472717e050924 1a2dcee09581ba083f476068b6e59c97b3179c2a9759fd5062f6ed03854e6738 a1837059296356f67330d65f3ec669a77140310381bd40b8833038da9ac9467e b786b45a36f1fb2ab17fa149b179db91ac9214a6331990f899b9c9d607a96948 b3d9d31002353f99de917df0fe2a29bbf9468cb39f3ac6f15cd3693b9bec9500 afd4d04039fc38e10cc42211f091880c106b4ee88cf223d674b72691ef04f0e2 779fe4986cc325cf9d43a81527667f5477bc6b0dab67b5deaecc1c78c3b41228 6967bc33b9751da2b33cb217691bbd5fdbc87db54e360c236725df760271a6d2 20b279076b9092772984b180cfa0984e2ed6588a2c970d6a406b2773170a25a0 26109422f9bc97fb8374c815a93ed9634c8140a8d0e14975d7f1e52b66580946 7eeac26b1ba8efe87bb115537a60ee5a2bf6b16a269a518d7c60c3a17b3f89bc 98e996ba603332be1162e011edfec5b73a3a6184918c34d81a6d8b43a4badbe2 f92c62570ca18eccc7778273b29c5c817e19aa5559a5104cef66e4e89dc9ff9a 96ac32b1c4d83ef670005b00e79131cc3bea1253d5c082475e6da0b399b8ec46 13bae2d12ee9264bb9291453cd7019a57958f502960c34e42017c13a0d428ca8 a344ba1158f690728f90eff634b62ef5f2ce2b8485e7ec9eb3d523df9ce9e20f 540926e4afd31183a2d64de8c688960ee63aad78f8265810d24750989af3703a 827ca48a9bb00510c9c89c629ab3e7fa5870fe2493bc6e07b86390c7f893e91f 7724d7adaba57802ed1509ec9030323f08e6408afb4cdfdc05655161b75e1525 a21c61d032a71dc127b1677cdf43876169493bea45add0649cc9d059bfd20f9e 62ed1053385cf71c7f25369a405d4b30b726588341ed822fab9949216c55269b 0012dfb8eebda069b6b178448c062b01df2580dfa68356ea9b18c466d7c21a1a 2b87f48472bb3c7e4d2cce4ff0cb1ae568db0c8731e6c368252a791bf83836bd fb4c4da5dc1f72661f0af5db7637f1b7940d849103c430d63968c320a02eb9b0 9c52d72249c8cfb3821117962383eda8c12b6e3f8d23986ab4afd3a488437fa3 d2aac9ca8f90feecac1c737690c26d3b3ca4cd90841001749b22b16bdc1e43e9 0392f0abb11ce9258379706f9da4703e71454fc7d8d67536b1f607f207751b32 78a67470664d2bbc9b352cb4b14ef9009d9e90bd4cbb0026803d1968a3cd1078 d3c46ae28f78b2aed09cbc451d9923667908a5d43a8e092b630ee4be84394500 89e4bd7046c2f12dd51effb3d2c800f3ab40ea4da49977c1d552a12d7f8aa959 1989bff2c64645d0364924db8586d990ae186ae9f313e91dfb4b3b70d05c8f93 42a1b41f6cca4410e04819ed22f20c9182e7a9b9bd7e8408c41a2eaf48f1d192 35b4663612712abbe565843fc2c8761a1b3b0fff08fbaaffdeb0e3a7f6853638 3daf4ec635bb2e54e527afe6df275c297f7fe217724781c4f8739ccc9c9b2fb6 5afe40899e4e7b817bad34de512ee2077227facc7bf4d3af21ae52fd13526623 b0ff07a6f55a5b48ac6112e029541eadeae416e99a88a2805f230346eed89629 7d6a9122f5df8c327f111e1b3bb934067ef9d0070c3e34681560145cd3544e61 bba9cf327cbd4ab53e16379ff673f14ff209e9f69fa78507504290352b22f1e0 09f78a7968c74f519ec07af117b5937a93445fd1292b3f4c6c048ed3f2ad473e 4cceabfe35704957d8d765ebfbb4b25e7aef813bbb69918e328a82ccc9cd0ef5 4d072bdb1e147c44696d3ac42708497cd14bd5b2053c4231e0f269ab68801d38 fe2581a5df60a4be668cf4eeff3b81b1535b62a8a073663d25cc40ae7e727bd7 201d6fc7a1d66b80f66b2a7e7461ff48c2f7f494ce75cf6a8fd07994315c7d3d 4aacc4df48cb39506847e47fd1de8aecd36644ea9a0a3715aa6410f90bdd17cf 391c4953fe0d607a5fe0757d3b6d89f377eef9472e2df092d84330dab9032c3b 7c0b6f25bcbdfddef802981b87e2d2602e796ddfbf38c612a1ee279575b473a8 7d918f300409fb5498d22dab33d5fabb006e03884a8120342e8c7de43a0ddb41 b472faed5d81a2ad3b5abad7bc4254a17e227e6cf61dd85f808343ca66d04dcd 56046d829e429c14ca5bdf58c1735f9ed29b23d82dc82b865f4404bf2164f78a a777c375406fa5771e7014fd9c7f1195047cd7afafbfbc4e90895f07b25bed88 2c8a6d21d61e1f11e8c4384310f41a6d170302ed1693767879280e6d26a2745a a629e4931c6b0932c982fa5204e98907abe2f36060e8571eac9faf4214983ec6 26b14959b10a5fc133763c1042da576408b7ef0722e74c7c990771ecb67194b9 33c948c923205f23965294543804368f8d60f355d0b812d7ae49100ac3dec0dc 5e334c7ded8a7fc203e7b4b7e03842582d3f87bc6d2e975091c5b1749dc3afbc 9ede74c0b6637781bd4d617a1ce397c5c6d985f56ae0a3a55292139e636e343e 397a7f6cc7cdd493461ed52f39dad6f3250a8463472c95ed9d549d6d64c934dc 0faba779da6568d7f50b60ac6c02ad0abbc93f4da87aa61940a159b4ae7f96bc ff1543693433d98347ca081f5450abdaa0cf029ccd8654b31920dc8630feddde 333f205783f49fa3f0c93d9b97791d7d477a9731131559f998bdfe978885604b 1d3b152f9ab2ede0ca97f01ae350441b4aab27bc99419b2b65a499785c4fb523 67d42536c5b0cae6b6613aa4f2a9655fb108a1cb9c741ec0e8acc47b0f50bb5b 0ac2a1b9379b4eb1c63d742bf7789f3f9ebac237ca1890cfd527762b7f24324e 8ab3bbc165e4d338e6a06221aa9d7bfe583a02ea4e411c5712f7890e87206362 2b570b3e4505e0c909b0b5c8516bf506c47f4a46857255536e9ed48cb668c8f6 c88352c9f9f561fd50106608d456a26849fc9b94b9b6fe630685500c8c0a9374 2909dfaaeafaa66a84f68336277f17295af6f389bad3ef74fa3f998fe1d7aabe b59fe7f21465882cb99b1005442917cccd40b9991f9ddd1c0a5caaec633e2a7b 0af13e35bb870c074cfd20e6488817842223336da2ac422534a4e47b5096e14d 31c21003ebf459dcf7d60d87619e953d60cbac7f3e56a3006f783d7409231169 58bbdc210a0a3c9e6d7a6c1a739e71385d260acb26caf1971161f1ff129cc850 a0c1fea9d30b676be2bacab78a0a803cb91d0d5b7dca34e7414b623d31d760cd cfd7370b09766f1129c6f7154fa9173b72b15492c96ff5423112d81aaab345a3 1c4adf684aa1c063c88f48b6d755b6ff3e234b4abfaf11e669b4b049f3990ed3 10287329dc43f6f23651cb0b6ad8cf44a733a6d62f5e3bf0bb564f9411b8fc53 eae584bd1ed75c877553e40e71f0fd3368fcf5ab466f60fd51fdeef2f3163ad9 20723ccd0d19029a89eb5a705cbf37c67fa7482b2b5cf32b269713f9e82df0a3 4b435f890fbec0afa63df7ff10216141b4cc91f2ef43e45960fa369643490fae 3946de1a98c9dbb03306cb44d1345352df983c0ee81c599642037ec826a95a73 81f7954e59f94dab306d170894f26a64a3ed794e58da478fa4b1c97d3e7f702a 7b17975da7e5f925b7c00c9e49ab67c1c05fdc84ecf7c7c2bd9052fc1792a993 c2a95abe1d8563d55b71d31539718f81588180dbc1e7613ced3cc8883b7a4ddb 4151bc7f598d8b5f51c06994350ce4992cbd5cf5f0af19131f2eeb2930e0c2a6 e6d57d2471095e9557746dc61e51d13d23a13a39c21cb2f5a025e6e57d955319 f6948af76dfc38f9f369132e61275d3931f73cab4be55e0c74c26f3ac06f25ce cac3b7a21c358d6bfa6b75afe0ac3ccb00c8e1e42c1cad83652d21f687953df0 7f80969a5e9ae3b12dae228ec1a9705e9e5cf8822df26f4b2e01d33d01960ffe 7f88d5c21dd827ae7133e3685fef7cd74348ede6ce206f77c48bfbc3ca8d9d16 0137e2fc4336f7f6e5c58ac7de684ed6fbb80b94f9a566e462397185a83aa3bc 4e09fed75cc9ac94c945f26099179285d323109b9e5e6495a5fe70352b47b23d 014decd861cb19767ad2aabb1df2a1a9c39a90ebfb0f9bba99336101a3caeb04 6a8e6bba480e9236ff6d502a670c9c3214af2c2c54d68ae26373be3ea9f6ba54 ee16d722d807012e9b33dc391fa877d6ff8b4c28d8a60bef98407d595fd84bea 01c76b8e1b6a8bc8da70dce6e764ae7c3a5e84299e5ee08cd549452e8a7ad5f7 7f9dddc19df35383423444f76b977bf5f9f8aeca8ee4d12b1c2d47050c4865aa e31923c564f3331aca4266e02a02fbb38082782bf920c73266dafcf5c584a938 e9e1a4257cf560e8c93178a567c31cc5b0da317989e91fbba69e19fdc144a850 d99b31b583a68991c6a729ab3e014e8efa878f4d73428a3e7837b4ccfb7ec34d c43dd8c6692eba9517699c6ecdcb4addf074078005e66901131e3a951c1b0edc 0c4adff4116912e3cddbd8af1e0a54252745b18c8069f00848c86fcaaf1b7aee 71435bb799f392c3fac1d37fbf25cc104123c4200deb7027a25c83f14e1376a6 106deb263504c267e112d2a5a0119a2c56801262370e5005798474cef408ba50 39287346a77fee5f44ad898f17384b4f8d6fd9f579ae9b1a247c0092ea61a459 2227d6bb59ff0f8b917decbf5da7fed3e6b1da14d1acff753c24e8900dc37e74 2bb4d37ed8a566931a8b423ec83989cbf87d6c95be7348060bece8b781706a01 08092eda20bdacec2c77841cd6cc4c5520f526f95d6f0826633bdf037d9d4113 1c2c619b184d8283b6fae53673fb2b157236ac33610eb5b83071d2ed211e0ff0 8e2cfee0c3e50d555312177c397f1f7e28c137f8c66adf4480320f809feae542 91e29a009e5a842cc42d63fb6df9c01d2d166b97128e9e30747cdd6bcf40b27b 015e3a83975191cee58d69290ca0bb424a728c4016bf7d7a8446045f3b445038 0e1644bbae9ce4dcd9310271cdef3f5a1e50dded3fc252ea67d4622f6cdb3f59 15fabd6e07c43e4fd09387cab79bd79d2d1167254e03ea4b2bc14a79d86d3d73 334c7d74257b32240e6cd3715e5ccad9c9854e499e2a0a9a965b05e66a158c11 931728bb4e71c9ea18ca56741a4ce91611399a12d55d24c5e926d860c9671f67 e8f5006539db80c08d514de53e33c196fedfd6cc69d25cd854ec7e620689f605 184a126faeaf6f5a815c95c47e9c04a7944eb0bf9a333ad1e170cc214e0c89de 30243847c6842b53d49252ec2cb97d6ed6380c6a1b51f38aa5c22734eb444af4 4fbbf2cee95f7dcec26c8dbc0c6cd3933761707f72014b23754cff935cb17bc8 9bf3ae0a3eac604de96bcd6601a8c7cf6e4b50a0cdb19564202c56ca566f7e76 1db713ff96be67e88510ada404d59b61cb72233bc848a67a49218fa7eb03c580 11e055e9d596101236c23c1ec2547d92d43609e34f9df12b5921a1eb86d94c6d d2577aa223e766db511f51c5b2e682bcf7bbeac3f1d62688676eae35d0676a18 94249cd701e10680522711d48a1c700f65ee7a588eabd3d14db6b00d3b0a883a d3565a2fe13cac9f6fdb94345fadbc56e9925d6f4e23a724aad18f2ac389d0c0 e76aefa00ff29a4f6b79828490b0cf6a5f1d80ba4b6a261c1e92db2ca2394f53 ccafb3c0c1bf893669cca0d35e6cca664e3506dfeedbe2e9ad03118c97101ccb 2f93eca42748aca9b75b35d4e93605dbeae6899aff2243302b90dc708b619b91 9c5f644fffc89210281ed0be8025d6f26bc199f6acef6c7d540db9074d22d480 73d571d06399b629e06ddb6570e0f8c2bbd1f77e533ae6299f52d41a5f7338c4 15ce90fd29a054ba8b0b6d055618880b09bbd224a023ea05ab2351fea9d803fa a989382b2acb0beba1b03b963333f871eeb5d33d3fe7458d5070ac2792197585 42f972b27b415208048a2b9a889b68da93265253bab8e0734d669999e1aa6d0d f26d0f678d98edbf10d7cc58d02907283cfb4af65ee711da72d049ca2d45db46 bbe344aad8977ce28c67cb5a68877a4d7c17f386a04a5c17616d1180b46e02bf e0ab79f061bf88fdd6779df98d9dda293f3d2795aa708a2de6490ba655834006 88c2506e0a3f6e896df79e698020e240452b32c6fac52bc977b25747f19f64af b4f594a10eede8ad967bdfdc073119224909381b7dccd689adab2fcec0144fde 397a775a0664b932a0d72a7c98af6d08319dcad3c32db270120b0e277d79a3c3 7d63131afe8aea760a530344ee8deb6b7da613e3ddab7f85c3e1f7035769f782 8a71c81729b06a8c8627850015e8d7561040187685d648d0e4099ac2dc090836 5d17966c6bd9faa7cda178016d03b4aa310d78bdf42eee6de5dbf5466a9d6c58 9791a2b9abca7e54d48afe35fcceac3910298b64474e867a3a3a8593201ec83f 65829bef7805b4703ad88fc1a7cf848b38c3e40c0b16a7b7104bcd0741403e56 60512fca2ed6efb77f8299b3b33688bb3e81f71265a1c4fbe1abdb266ead5057 2021516ef3799cc02c7a956d40f0efa1d4f95ff405ce8dfead6989b80b32bdb4 8fd4b02b418427901456dad0cd1ca52671894ad62abfa6f227ee2fb62538aa27 1be3f560260be66bac669acbe18472ee86b338e045fe6821d0de255ea839cb09 d5ae308119a926b3a4a359e0114ee2f938158c61f484dd750f4489cf0e535e10 e218771ec2e03708e7f885f14d9b233e1b7a7d262c56d4f009311a2aeda0ea4c c01c9457d1ad329799a61fb10620f4c0c0b500ba26ff7cc5c3351f1ce90f5b3b fa27dd75891242297128724a5e0f2cb0050f3952b21c3028e29be67af64d7747 8342bfdd59eb7d54a07830ae4d09a0d15064fc19fd4f174427cc33e46d8e535a 0ced66b48f6efd8b0230dd1bbd8c5550e0019d8def29913096e02d39292961df e98da021b8ec628abb0ecc8a6897f3324d7680db16ffb46dbe762c30779e038c 258d59f6081f46450745f0f7c8a5fdb41fbb4ddc0a0149ddc267630c2e677d9f 0f86b6e98a65ab1d035e0fd02262daf83b21c8ff9cab3cdf7afec0c43af3d1a2 25442f835deb1d4ea6333eff39668c84a94e30c57298169935b5afcfc36d7adb 303b1075bc6b497650521361175962ec0385dd87bf864def6f575c2adc3cd0a2 8ad76afc1d698b50abbdb1d94a5c86ccb49b17d49af4d36f39dfbc66ea953fe3 b5218d6cee39e1d1a74e500958b718e406a89c7f468754a744ffa8a39b54e8e1 fdb05ef89c3a19d21e8bb34783a60888a9be53e1e7672d1a3e6f5c6cda4e7029 1aa8d8d92f535ac3bcc0af296a3ffb2c977953ac1e75a25cb65c4f4456bf5ca9 890d1506854a507ae800a7edefe094979769b1e9ef9e376296e7b328830de625 7017bf0e6491806767601a30572fc2dca74d1de7cd3e0ddcf42d15373a04bc8d 71c0cd41b5e501771a72c57a6868c082d4076256afba7569814caec598602f27 376b3198ab6e5849451279c39453c528081337f80ef80e882e954f60b1410600 22 8adfde036778326da9b035975383666d5530c6b6db2558362add31dd6dbcc1004d74c7e1917724f95fdf53be1435aa337833cd54aff7a82af3fb1c50e832360c3f39bf1061acf1cfe1f1314a025ccd0ea69800dae6bbbc10ec1da2c0b7a6fb08b5189ccdd1eace9c140d068a7a002cdb72a0b437ae76025faf96b3178ce0760dcd04e890cbe63c6510b3f1338e0cd5278b80ac79623cb2bd9cd4d71c2df7120a2fa8065ce116de285567269bfaf2917906c5d29f1a9e04b0c22c8397beefe304fdc5fae64c8ec3e05687b0811f05a172493cdbac717c6c64aa70273ea4f4bc03bd2e22e8bb01c28815a99c126cd42b2d8bd39d432a0531f2ba59b6975d01370c091445e6a1870bbbc2cedec27416310389d96f89c89425338300c82e1080ce01ed29289148677fe314202d5d0d66d94d8fc10404f51f9adecccf42c15f30cf0e646434540aed01652cc9cb4052c161213e1c943a6ce9e5e817267c59952fd60c01407a593c64a2fdddc7360ce1d7b207f1372a3a1a092c43ff0f95269205510d27c41d2dc65dd32b8556bea3e4fb776ab030426aa827bddd7068d184ff89c20cf775c917f10e2aaa908dd0e31f6d21308ce0363ab41633dd3785d679a8b22b04047f8081a0b83b83a9c4e56b8b9dd5a204956ad312ccc2e0575d62ddcfb14704df75d4fc2c3ab020ece41eb995e33d0d911d8e3017fc0d0615b7bd3308385e0df6612e90936221e0600e1f5039e057cbbd4c86261566e23f4aac3a53fc8ecd080ffb80d2a86631bc31e00fb706e9a807d83a137f7a4bfda68784302b1017d6088095c3702d772e6e46cceaaeb87d1eeb2fedc4c7a3a20d3b68117cebafb94806b0269691b83c9648aec5564cb6aa1470690f3ba137a1407366b6bc70f660940ad91035d810de81c2ce73d8f676d4c63670fa44c4ed4d8a6a843b2de530fb4b0cd556e55126e3feb040dcc2ceec7fd792cab33d2693e95d1ce1078502f9bc7309583bde1b0a8bc4a7b9834e37fb07c491704638b5517295a141ee8786cfa32106b553381db86c18019c0fab929343e971ad9a0fa9090bec465a3b55423f87ba0a697cbe6ceee2dacbd20dc64b1dbebc8b0e4704b2b8ebf8ead250587c101be00f9a1924979ec6358a8fa2d9ed91c80a069caf2d34ffe9e4f75d697a2f337c9f037a05925109ee8b20c7a3ff85e2069383e0761d136002a8a1eeca7f8da4e0c102ff031789b87292ca24c2dbcc7f590f03db3c3e6da30b7527db6a37fc76821a06b633488a69151f46e70f4c517b52d0200079b109516cd35e65dade2e85fa690813827f67548be49fe583d871465258f76e329608c4c6f0a478be60ac8c1adb0167c90a324b04603fde4562af0b11c75512508477c91d8a3a524b09ac91c6f90d603f2df56d9befce761d05328972345d925069709da3e9df7043891831f1f80782ec30c8ff3d2b8b75d7961fee3c1c5a7c6ea71bc63192b7d556d672c1e9f309262f60ea9dcf5f3ef4dd991aa10cfe8781bae2cc99571d3f8705ddaeda9cad0b8060e8b77302ecf7335cb00c24092f4911e8c60a9003b87e0b90e09a3a53ae0dbc29e83a4cbf01b574a0204ae934797fb0ba08afbcd30a6d66a270efdb42f10a48cdf07ece1a42e3f8899fbb723fa13fefb826140775320a4736e51989b68f003697a6efeaaff2e0e2d417e52324dd8faf2ccd4699a5ed8789bd9851a094860de31e40931c86cd88e0af74d2b61ae5d9b52bf123c10b755b3bf072f80c674e0305ff920ccd7c86def905978b0819c16afd6574b58de32c547c0e56ed46b3a70c8938e5c75d98cf15629b9a5691cab6258d9fbac4328f99e038696fa606a3260c064a59da90298935c6aea63d8128996bdf8ebe361ae3847c0eb52d7b00934d0d4d84b3f632b056331e33ecf887595c7814b5d57a01d399854766e06cd3970f0e729468bdee213c278addd34c0a343ea4ba71874125d2969e0e687f7495ce21024ee67f35fd0b06186fe5c3947875deab18eca920373f56c9e305c9a522701c074edf5a607a1743a0f6ef6ad36cfabb94e532c4f29b5dc81b5b049cde05e33b0e3b2d0641279bc329114a34b4fc3e936f2bf7f8f32d9aa0f3580f813c92c5de07b1c472fa265fb2502b64ddb6dd3b9ca1d708a8760aa8319257465af4e66f9d02355c6566a7d88ff0a2945f73028446120e94eb68509d141b8e16af4c217d5301cbb735bc43d258b2868cb0ac0a9c8b6c23b7ba0844e6cb175266b2baac89790db53c1eee32531b00977f1b11af67d5a3d862873b14f1e5742ea62b96879a500d1af2b45e70aa864ca4d65faeee0dfe5c0bd17304e1024820801f5ec809f12d013023d897737ac5f20be46c5db8c556d9f6ab9e7d3081fc253c87d0a8c8439d0bf5b0606c784208014ba3e6f2b29e887bcb838dc623bd7b5da9e11160414a6e0fc1739bc3ffff85decd47246ac81bd58b29c80f8e5e13603e1b811775667fa40d2a8db82af5ecde2ccca245d3a1b9af89159100aa79a50d60ecaa09e449eb12019f05a2908098d53e84ac63417d4a612028c15ca212d7a1876db4f013b58206087ca805f797005b3f583af5265a51e62d454309187543d98ac57d36fc3ada7c0623e57c7be802820daa7ba1fd0d2145143a7fa3e44a794e8586fba082d2b2c70b6734a5531f4b290e7883dfa882b0b8c3c5b1accfdb069687be70c048a779da0ba76e632331d468ce5b1c345cb379f19bc5372cbe147c3d14960c8d4a7c3c43052a192582ba6c1c83cfc4e2df94ab0b2daa8df5a35d3f47f71d85cad94ea4620a8c58b6fde7300128898aca3eaa0c0fa6f2d2fbdeae7d3ca038aff52d5f153a0bfb3cb41c147d6e6d5a466c75f5ac8b8366f6ea7638f0e37cfbfe1350e9c6b70f215080042639f32f65556325b9b317afa4b7943839b2b3981377f389d343560823f40044968997a8092068ceb4508dcd3722cd35ae18d5a09dd0e832d4b2ec009ad9dbc5af31e83c4957d4b3d1d10fe717b5cba0f88a626fda800fb65f931b040c88e42052d9d78996c399e42bf73afe8146f20782c56ffa3463e2f5c0c417009e27615d275116468b845c6e757710fc2a00a06ad1b1e69ec1b6bc935f2a160ff67d0cda52d4743c4b9c53dea12c22d778ad303ab055e4cc829742c370e84f08f97b82c153986626156737ef4de57ec540003faab46c57a6c5931e88ec81b30bea775f5ba94e981eb45ff42da8b3d12c55616f9273906b7e40002df19d025901594a96efeb115c8ef194088777e233670a36159005ab680e613aeb7e9d2a8c061e87ace2e21ff03d559fc9269dfb0f4ec2237661eaeb4ad354ea7585cbe9a3099d11b9ace359e06bcb492da9ef68a38ec0cb02935a396f21c8f3a067d81c4a0eb0fd6c227917868bdbb5b1fd81da13ccb6b88e9f4039252f0f2e5e1bc5bd700379635d6b937b6017f237c1c3f7da8899fd6a53bb923d8aeb398aab3ffa9b6709445663a31939b5860e900b49a41cb697da78d22e2d6ce318ff2ae7fde51cad0688c944667ed9fda64bbc773a4de64a22e917e3da586158042589d972a164830f350194bcd3307833b2ea13655ca55fdc35b08cc9b00c1f31dd0d176aa43b2e0c192ff766bc837953a2518a92441c560f1e80d429f7050a2d181d6be28c58d904d675c34a1b9e0907d5ebb7e48e087a94e0ba994b4616bcf147237c09846f490bcc432aaaa184202b9492d6b87e3c5e0ce8e9a05c6a8d997b4202581ac2156a033d3dea34314ee05d96099c637f42902c250a2b47dcae7617d6e622c37dde40015882219fff51b55e65037cbeadd1152da01696c9eaf4172308770ac2aae8930c5a3796d2838d8cb9c834c08d3d0c64519fddcb1382ae0c5d94da65f9808e29092cba10cc5d6bd316e3924f1e2f0da066f3c5b9d93d24737600515a0f559f0a0355aa582e7eff3cb34c5856ec7d15ff34d9812613c4ce65f26bd81b38ba8a120f35523c5acc61ffce273b3e77021d516c49d0901a7ca71e1897784037d1f489022daeaa0564af08089a8372d6aa98e2a9f9fb21b7e8677df8556ce50e5fdb2f07aa5c0e8ea1ba77721f4e8dc25c226b68377ebe5158923aae2c0cb96de616f909b045f0887bb6b6da1b4b3e63cb3d8031b1549dbf445f8799a0c31a27b68fbb0f892c7b2127cdf531bee1e847ebba8274699fe4186aa40d9ae4a4df01b98a140e61eb1fe67643335ebf7d14473fdf371f640d084c4ff7beafd07f53b875b86e0c01a101ba5810e91a91c8caaed620e9e850bc7fe66393e526ec8b27f55c0fb303728a22a7ae843d876b33175a56b0538b5c52fa38792830ced62e80c48a2325018628eb3136846b78de158eafa6089737cf7ef58d8a0c8b3165d285372cd61c0f5808e8a375462a788bed713e58024f3225dc22f6f0424b52e7e00788f2b71d0cfbb591e79eed4f4af2735d781bba8ab1be691dbe29b921aefd57eeb645231c08b735efd3482f95512d79e2fbe7783db856eed2be68ee5ac20dc86a698863300e25e89c7b98228a5faeca342b36563e6b5a71396eec5d1d64b50cc8432be268085994d8bdf7d1888eb677b2e1699f293c7d8ccc60370167c9d44d966193966c0660f16a01e69683c7c69bcd39772790274dd51eddbc2aa664d76f88401545470e9ce83b21bf4df4d32bf210214e37c36c2b1ace13a33074aa6b817881c62c460167fc53e825644e12c7ea4ab21ab4e274361919032c106071694cbd8360e26403d029c88396e3948f55bd5f24713138c8d580a373e46b4fb48fc1e7916b6642099c15899e99256f6f37c04f67a5bc484d5587aec7421df75a1116b290d94eeb07213c876177c93055bbb9222cc60f3e81ed4f34f6913d6e9558a9dfd4367701091192135f4113068be412fd6b9dafb0c08b84ff5d42f59976dad440d519afdc02eaa12f01430e3d585ff5b8a0d7f2b40742a9448532e88666afb0384b2e80ba0894fdaaeadc61809a84956853f9475191c016b87f3269e76978a0a279a5f912096c588d599031257ff85e6c7c4b1db934c54c8b47f25d407f43968b318ffa4706e01bad4e35570badecfba3ed7d908ec70a36c4b90dc6645a1766dbeac37c5a0b6013593a54d7e9e3bc9fdc23c892051c37d7bb47bef8209bd0a0145cc30eda0152529e09c5dd125ad1839fe6a4ba1a4ffae9ea53bbaf6de0e55bd1212475fe08c7cfefdd3b27fa566bd87bbcbaae31d6fef58e9a6620ee69618951fe5dba630cc52aff88d01a7cd717ff769967b920b0d108fad52090237ddc9fbf78def1c80e1507fe6914b62fc72801777e2754a36dd0e8b1440b6f1363804ea224f8b814078416d9c921b2f8bdffad7970495162cc5aa55736c8ac9a285d80e28e73bea40276e15d59e61f89b1376aff4ea10041baa985ce0b693c8a6d57953210cf8571021f924b42f88a166b1b37f212b72e9f37d7226cc833aee2755fd7aab0755fb90bf2296858992859bd63cbf89123e8e9bfba1e2d1f6a0baca857c7149ab6e14e002c51cf03b4c7e45d42eff1951d0e76d5094a3b8b826b66c5df667e442f0d4b01721df2d3cbf162fc838bc5bd93ef4248b4ecad7b47b7c8eee10867f1ea9fcd02935cdd5bd44ae02bf2d8d9f1a47f65d58f6a6216df30adbc390397ce29efba08edc1802d3873ffefbe2e5dd844ce105219a4d420a18e3b577b7a08f1f4b4240334ac87d305158f81013dc8f08c36b8c8ae0bf083bc9ac69e4bb8063577b32e0d3c54e26ee3c99ec3f5937ce97bdb586d9398c45a1f068d1b366cdd5bf8670e0edfeb9862c6a223c8a92ab58b4f05b2960e8b40974e171042e6a2829f716c2c0dcbee4e9fec95aec62e6053b207face6adac77b09c06dfe0f9859519998a3a70964a56ec333cda783c1b554a9324ff3dae92ae300b3ca929a58351fd0d33c170b0b9ba232b06f8a48493fe4942226c3b9d87f073e4c9f2ceb8379a2028acbd60b12b1bb84e944dbe5ba588c107f48b28226883263cc3cbe9b1c26d88bed34c50db565710af85c128017f53b20bda89c5eee04e3047980f250e042908e33a0360d98b75208380e3c39d004d6ae4311c0f0ffb43e3df07f009124cfa5035e67480aaf693de8ddedcf0ebe180496a420313ac4ccd009d3d69d5491dbac480610b50af3fa717202f6df3d57a3de77cd7f30e602b947db479a2b105cfd11b6999d790b3275c94679367de07f669afa644f3efbd7e5ed82202dbe297bc36cdccc6bb50d1588fb08db36edac37b512aa418895e6d3b4bc4fd0ef1a68654eb3a3291bdf073f2cd7b1d405636d1f3b31f78e73f2b2c8af399bb83f9634a17d2f12fd76160ac844e3fc573568ab87083b4345b9c62f112ead764a73170032fe83ec0247240349d8d4d93ae69872c7903cee05e251c47d3c9eaefcdf7db29a1eef1a2c7b950f4d30c2b6d22acf156addf1419b8a650f118ab9812803cb838dc962bfe588c70c057f40612a5d06de2d483a46bace481f3fa1a3a1cddf5e5c47f79de671907a0d1765346cd23df12aed29c7cb15901df0e83e0126a765e3e91029fd5597764306f40e06b53fce21b5c330870bdff0f75435eb2e33739aa356ad2bdacba97a85052dacfda2639f137554f5cfead1f5b20531e947942e1eb186d09c6b4bf3575c0e267f76ee54ff8e268b32b45ee62a947c33fb564dc46d5e0a66f6a53f3dfc8c048e0eb4dec0081bafed110b382662b132ce34040ff0eb6e72d19fcfcc5f86e10f02d7759a9abb246df6f9393f29d759be51990e8d94204e27e5dada6eec9fd109d5c45737e0a926daa76b6e19d44f7683bd691478238863ebf524b9a6d664550e713130e5edee177c8e3fd52c2796c20c0565b8967e095dbc104874951508410777d70e1db8982e36f31a91413053b7c3c58e2ecd0916db363d22b22ab55c200323d6745cae7c5075d7e47ccf4f832e1c01775d2ee89d55438eded25a4c11730a9ae4cd7a512f0d55fdb5e2992d2fd4a26a6a5f015c9e89f9739337d320ff850a7777ff10a35cee1f5a2e6f47e30f366e0967a67e1da23207f2e80cc6052e74096216269b8f91674acf8216bc4287c6781972e66db33a727910c39fd302531b065dd6eab59d52cd58f33ae3ddcfeceb4fd04be55fd3c87652f2a04910c9e42600c1447cb1723f5452d8d266d723a199db81430cece9bc6728e7ba40180814f70bc144aebbfbf39aa6c43f0bf4243494cedf4e0af6dc166e6ce7f18408d034770ce1263b73041c45c6680b0b38b11da26467cdda23c8fde26f062d7a2c91186106a9685c3f856c69caea19c412102d6377a7ad9f0b636f533eb5796a670920290268ad1ede809164c95bd3d2d79dad5d9942aa82616e48160d92a6ff6c49eec10be19fd81252561504f92c5f9bea3a7b48cfa74f62ceaced7be30331b59f530b04952953732cfd2aaf2997804792a251b50eb51855c7edabba366a1049a0216303765acb89a204198dd6c760ac92e10f5afac3f3efb273227dea2d7e2d59380e0fc542bc142834ba2a44118f3ec00dde7911f66e0eac8a57af47a1cdf0e192ca077de41347ec741568afb9e8d2674d6807372e46df0ccc319cee05a1adc7075a06b82c6eda62a13eac9e9662be4759f797f04f4b915ec3202938fffe2a41aa0e0c07473ceef32c8d256ce7e6a9a2febf400c7cdf04ae4f97d78f9ca00ca64d3a03c8795cae3e212ba8fea7ee9bb409384d3c9cbb8102f1d2bcc5c51f5d0f90770dafa39748117251c53a8733fd3d7eab7702005ccd4b595757cd489d83e4fb45057eaab8503f28a219ffc4a65097c310145ff5c997a231085b966976cdd8f1520a293dbf19de403a8f46056f7c72325248b6936d075d7207024de3c3b1cb1f7805303cff70e434352daf505c1f3d4526118900a13d55e448c00b244473b779570f62a14b3cf237d91177ddff1099ef175cbdb2985d45d8ab17de6543eda3726607020f2784b76dddbf78ae4f1187e05161666cdb4a707453ac6d7b54ca9bc4b3089ab90653e17c75c6afe1b22f6e95462cc9848c862e10b461a774ee39e65906046d02cf783bcc0213899d1ce4489e5c850a1a6cde91261d8a87b509faabb2ad09457041b8fec38566f061bf3ee0e08b6f89e86c7b54f3204647d2cda042d53801528988d5ad6a99ff52972330a7b610b3984dee94bb074c544902c072ee6ab908c0aad0e965e3f42d27e48861588adf850ef6f84c4e3cf9a794fc0a06f99bcb0587bc68b475bbc4a5b4125d562aab51d2d4718e57e6dd48d6f2fd467f7f780e0975faad80f60b3d8a1e23f2376741f0f1a9628c5f691821eeb1890704c1a2e407bf0d3efc592eaa2b6ca2542ef01c31349bd59a7275e3685edd9e297683b7d503d04f6405fbabccecfd3a3e7c401342d70d7100801e9eb7a436f20388f2cf8300bf261778f4f88b4ad165ec0e9c3cd5b97b4dac7b34479e6fcd728398fec1e8051c479f93e91a762af4e8df5e514e84e32b60c81265fa961c69737895800fbe004a50edb8a95c60ad44af27b9b9e35e29880bda379aad86662cf45abbaeb77d0f842de263e88f4102ac641b996c55968d30b04cf42fd416cc2eba130e260b3308fbe67ff37403c07e19b37a8d48f46879ba2b817c34b01cd260a78e62c51e2d01a9de05ddf5d3ac7c9016c91a337a6d651654eed0eb17c0bc26600eb633dff9037f5ff12fd7a0b05de9e4927dda0faa4b5ab1147305ffb89196f805106ac37901b09b0db3cc65d5f5fe60fa4cdbd9aa04b309340de7b2a7d01c61d670faed3b0fd766cac68e8de45b4ffa9abe3bbc6f9b63017dd3c17f87e0ae10036c760b290b109afe82eb271139c107554ba53aae4920038559c75c36942187750d5151580ef58ecd5b8f6cb274d7644d317624d74fb3a88c1514c233ab77dd17dd371ea906384004d03ce6a2446b30be79096dfbff5af9e74af77ac0504e7121cb6cc7ad0624d703968f7c8374dd6878415c4e33869e62554727d2a97dd48e8def1be62f0cced90e00a0130fce051447162cb232c9e215e65d324162085929d6851b61c80997ec6666c4c3fa12f1e6cc0e62eeb9225ad22fe2094b000df900c8be2ce383084772b6e7a25452f1e4c60b52bafa5326557db7726e8e75880bfa0ec747ce0c0b91bb9ac8298009151e76475ed1c123599d0c8e3979fb878a16f365297a01060464784b7cf9fffe94608deccaf88035a068beb43da0108e698be8e8823a8c400d74e8d743106a93bab634d61183065cfc91213627b201537b47c614e02b3901067f616229db313065a50d41d8909d862ec764ddc0f263c4093868b5244edad805a2196791a436b87819ba8a5122a1f4fbb29b7b4c71cb493fd69198bce5a6960b9bb3d53f344a075861a7fbefb21542f497cba9128d892c25806d3ec6409ba40f69a1cb76d0529af901607bd23e9f2a35eb0811a05acd86da2bc7bbba99dd63067ee99a6b8f6e0d6a7f549aa945344b9c87a1e1b1901d1cd76842fafd3b6d4d0222f557e6a7658af587a940908ad9b775aa0c7056a4775743859a2f170c04540be2d5d4061b5eceb3495af4105d614785317e442a052226d014e18f56789a1408efeff2f9966ccb3a9e6f02936cf91195112acf5b5c1b4e7f591ad3d7b852b3032c733ee66da1175a81d6ad42c49e4b87bb8f4e000502fb0c709c1bb5c9ba210f61b3537b7215f795774cdcb2f207d3870b5cd59d937f5662ee3babd8a6341d0ae2a5f3b9e3f62814717b24735036827de36fecb78716d1bada8e75663c7af902e20d1f54f648c2b9414135091838282c63b5b2c431db0583ad3efd55c7d2b50d2cccdec4661ef812f936f2e01a45a2fb11f7661b6548daceb55f0531b283610bd665dd21720654d8f5d0f7e9f166f2f49a1e378168201649b46f397821d7ba058206962515f049689d2abe8ba8ce8cea763845684f112b15cadb7c330b498a087146ece683c8435750dbe01447bdc75e073ecc0a52d85576b7a067d794a747050fef3e57500c4f9893dc2e2366a0fa93a99deecc272ad764452174d4b15b870b4bd173cb88a9220057ac9c5e93803400373b25881515fdf2e68e2034ad8572072c14ba8fa46d512bc33c26046b73e6ea67415e0030f968d610dfdb17b538f00ff108c5de40659e2df9febce5a60c9dc32406336d866f45578220fbe740f27e0fdf267b0215076d4e3fcf04248b7bbfe484fd70db162b0b765e9268ab405034033f36cf7209512d0c9dc3782c9aa94aafd84390d972df153ccc3eae40509a910577621b4fe840db9706fb6280cb74d43432dc82d9a8482a23643af827986c670c972d57cdac5d62c1f226de782bf61a3821cf6df8dde959fbe821dacfa2408805f199abc46df195712ea923902b6a0b8a6407b8a833e5f94afc488418364e9601730c31e5db1fc84bb02cec4aa80703ca6a539629dce827fb7045d576a5479a0935b76e86576642b5b897c3aefce58728cadba94b05fa413f2a844a50ddae8006be3a93ba6c7931a044aab8754732b49cc9a1b85c46e0fc13824f28bb1377750b8dcaf01f4a7f1f6fece5be63f9a3625db0ff4156236cc17340f0e4217e2bd4069fd350464a57ec6409d4d98033e76c300da874880dd812af2419a2d7a943580a11201a2136fa8686450b50724f8def3c0f7fcbd29c4ad84c60db268c1c6e4304fce50b96d735bea955a451a0ee0205b198d9e0dd43e72ee58f58d0d72b6c370033c03650f21703f518ada2d64e9153a81c46012b8afefc99e1b6d94e7e97bd08950f3c619c2f07c26b72ab474335f8aae980edf7dcb954bcef9bdf061891ee032f1adda9986f0b45bc57910f709b4125741e406b8c7fb314c6a6257e498c9703a031bfbb9f66b36d2a301c33076e23e35f9fe776d37d1a59757508281455630ab990735e2620fbbe70a83a1ddaf757c9fac200dac34a25e267a777bbc63b6102ca2996b88f433b70852087610b751630f0e4473b66cc057e9b503075219b3b0ebe0905b2d3387348d041e9273179bd33666f0f320232cbd15dfab6122d183700a085204fafa6a075ff69e2d1f654d2360da79c1feab5b197de1c4c08e6471c09a605e2b299ea4dc5286d8a45d7e6969ab61fc085a747577243752958370c470caa73a9359bdc0f37d8ba133417291701d806eef3c00e4f92e24850ef5962ec067ac16592a060e9c655612bbaf162dcc20459df154f3b66b5842f1cd87c914808ec4e9165e61e60be5c52ecc5ac8dc52dd08b4d7b590873b379f0b3cd3f7c760158369b54278887365a9daa596a95e06e275f5d9121d2aaaec89b0b73cffb95048c86b2b62a4b3dfb210bd87801efd7213fd953b53cd050c3dd483c6c6181ee07f638d52a8cd0930c459407a7ec32076d2afdb5dd96ff77d752c887293ef8f9069cf4eb7ef3535fc53b4b149cb56795bf10be39353f6a2b224c7b49034ff435039d2a089d479d0efb5c5675cd028206cfe556cac3d38e08d8dd5d27117f8c640af5e3e48edbe5ac5a86dc81df3ec74bb755a4aae39f6157ec33cd0928aaaa7d0f07d6d486afd43e48b75b569d64ad533e5a7752fd2abe28a8fe52bfc5244ea1034426795cdba772b9b264586878bb3c1f64eb399999b815ace45c949a25ba7709686089a3ec793cf9766d1453bf195c9ae6033526ef3f27b6a8c3b56d4922e8026b56f0d3d7ba125c55372269d5eefaf332499b20116aabdfe2338e1bb41e090c0b7764d7fe92e35aaa69f0a07aea6c762477440c285312a9bd0a8c4a6569560279d96389ea92fd0232bedf261ace36ccbc10dacb54fbd0e90d0d532b1249fa04787c19b964e9b68cf68c5d9adb78bc4d31efc000ea267b98743d95eb1fa33e0c0f99c8a9d2ee0a2b5dfd1fe54a6aeec26c3abf2547e99a3875da346dd5de8d0e12c782cb2b96d696b1af7f93446983b82f3dcb3ac04ffd6d06a65b714b3b48025bc2cfad831fcd95de8c2c30e8da980368003dc76b8ce61b96e2206def2cca054890ab161bbc036d9b20b23dadb678f7d916ae394757a9baabaa32b8110b150921254f9d13b5f6f00709e6582d8303871ae8e848355802b46a2c650956cc3301c42e464cbf81cdc5fdadf7fb86c88afeed047955d1297264e3d393358263df0739a662281f66a41cccc6044856f2239274812c2b572d099453bfaa888d8db707b9de586f90d4decee41d7afe16f2bdf21ca60b35a6ddfc7bffb01fb426bd41022669c35fcaee52385d9b830c9880ca9aa74d9b9f662a2cb0ce7a391bb2d6d408f5ce87833d5a4c39f8e023d2f4e50c4f5c7eabc0222ebe642796191b9030ed0faa7b25246e9a59c4804b8d1ca2c5661c7d31c9e600fff4758478373144c8ee024287c2a9b47b6d1181482e11ec90330a6e633a14b093b250eabf5ac592c4300dc9ee54eff14bc9116b973bf545813f151b0c0bfc225cf7eb254cfc5f32a20a068a4a118e5d015de0fb38f034d857fa5915ef48f8e13277112818dde9703a580d786263aa3c18cd58fd3a7d768f689d1764b98d1feb6be17496249d0bd8e5910e037849a96ce1a183cf2f7e93e346629c5ff6f7b0554657c794a183fbfeda91056ceb690ce7966eabac2f2307bed49a48ee2a3c5634ddc2a3b6ec6a44e46a430917e023b205a95854b5d021f62d44a91dfa55cf94e8d8295f1d92d07228d3410a0282197fe1f24f59392b4e5a7e196ec50704a6d4f69390ba3e7d86b03629f007349e4f5e4db491f7230ba1b540025a84708a0d806772ac081be11c5dd3b2df0fdcac5d78724e349ffa5d14698a4f51a911e8e02e05d35b2cdc89c908be7cea0565ae8c51bb995b0dd34fdf682d1a9f2bf1f3ff55e1f4df5e199c9853772459051deaa42aee3546c2ab4d98b96a515e00f643f4efd25344eb063fda166a283805b62ebcf52eeb49066bbd656f34f803457029629c42fdcfb948bd7cdfa0cf2306a4ff3ec4a7da4b6c45bda8fcf525652b5bd70c3df91af9cff84e45d3eab8a9028721e6702d71fabbcc5c8b36cbb8602a10d31798d0343dc5bee33f3bb1201b02054eb1d614b4c0dc42adef21f990bf75ef26a0678236420ef6406bffb9117c025cbd615f1434688aba22c74f2da9c02441c87b8a2ed54c290fc480bec324c0061d510c6ccb4397257656856158690bdc4a723ac0f597848d38ba6bd481a4d105612abb1c48f7c368c11fffe234bde05a85619e4664ca40e6268e44ced064ee0e51447f11ed371e61cd3653c3e841ac331c12d225a164bc4ba69ff178b295cd0bbe865cc5a9f35e114f2ebd33915a8d1f4fc6a20ef444bb491127dc6caca29601d74fbf978ba18e1c1034b600fc003b4c3b8230ed4a23047e20a8405b5acac80b66d10839e1dc655d88b621b7b952845569d3dddcca56378d34f5f99d039edd03f7385fe2084e75541b2b38f1f815a680c98f67fe8ff97287fc32ab94f3adc3073ba16eab62488d543e61b518c4234f0040fe11b518a0cd0b19b96efe9ef7d80c44751cb9d0d4740464a7d56e4c7d06290c63ae1999bb7d4a79dd209f68132c015a8218d3520ce45e8930f33979b5a19c8a5b2f63b791477cafa01de229d7b105501d7fbd216516a79e2aac69a9c0d2721034c4274f526b7424a481d64af394094d7d2bde5ac33936f5f85904e25375e136b293c189572ec1521e681ceeb99a0d6d6b948674e66ac2a5c2722750afbc894a4c202598c00b24a4632fabfae0dc0c6c68cc651e4fbabf9de51b11b6a189098bad615896a2066d469fadd05ae60f0452729a08dabc59d501262ba74540b4f24b69f36ea35aac4e1c7a09d038146802714d4bdc89af10e22d6c41cc4f516c75351fcfe3d147d897c033f03be8a40b0bcd94b5a1ae74ac1c16a0afb91f12b396edbae1e6f8d1692a6d3386883fa08d037ae821196788f13eaebea734bcc736d76b7e7b45328d508521d75d3545333f036efebb20a6dc6a7917bb9205bdffb8abc8cd659cd51e1066eb553f50ab566904f0d1d50c1fd4d951b49f5e2faa6705351fe23ef31e2113bea7fcbb61f8bd760b5ed4560d6609885b3c1157441782932b1758546b92c1378a632207ad63f63a07d291d926328d8777d55fda0e5eaf848f75d89b3b9a3f542d065f7db66258560a88ff1aea9a4fa6abb6e51394692b0f7cbe30c965d4b16127692d2645b31a1e0ab544c58f9a6af2eb97fe1a394b2b93ce082023beb5cb937aeef650bd32244f0fbf76a8df888e43106cc06403251f8f1689f92446c02b3d4a9170c6089ae8790a54a30f93d8204be7b839082e49c8260d21a47fa7a9bd7bbaa2d84257acdd4b005748a947b44ab7fd9648739a788e925aeb7f98120a6a376b5fc17d05a93d940473d680c869dacd7d5a4be148c120cc9829728ce95477725e37fbad316d432600adfe9f3a095bf6bb422e4c2c51d3491f24503e94cd7a12db5061774184e6e80e8c27df5e6d3194b75e86189a829dc6673c9baf28e5d26b89bca1628e4bfbb208894d62e57eb9fe91f1168adbc469cb6ea1689e6e78f2efae6c0ff3eaf068bb00ba82e5962cb799fa4077e7047a47bbf4e5de482ceb1886e804c957a9dfe79a09539d4c3ed77a5404da4ac2625c6fe4134b8fe00d7ea4d0bb26ad7edbb7567d0c0f11cfafeaa7a9876b35fcb085f75055eddc1812758b8d56e28003853b7afd069b0c09ea17d656445a4e5bcb0aab9d606d0e7b09dac44c35919f72be06578a025c89410dfe242f4e363e56af7fbc23177918168b8df210933bc7cb1dc626520229403de35a615ed75817af9a6fb8e51195edc68be279b9ad9e154f7c3c86d809ca4bb02c4c7234572901b3f62ea7bcd9c1c2fcf134c32d28d4169393ef538b00f6fe7b109ae61adbebaf1d2357f4ef1f6c4e48c63b0c90e87c11905df6da340ae32d3b2936e3988c43708b2011812f569e69cc972c05f8a19a6cf46aae585701db358e4b87da2860518682bf9194007c4e15528b0751e75ac4a2d4f41036960c30020f072d62d6c4f49e7fa4c77c442cea8a8994c0f27291e4834a3bab49fa0861efde30c73937c619b035217d7bf0c04c7709dd031e50bff32d25086ff222079a06db5a419a98e88f607e8ad7a4a5212bc54f10c92bacb9f1568c8b0f4f21054694ff308642f0ca4d9faac8568e0bef504552ae087b6f3776ec9561c6e39801ccb4626db5fd9f59f35731ec93a8e13bd37c8be8aa6dfeeded31a80b0be38d050c670d3d2753b410f20fe917ef84fba7e2752fbfaf29713e98b2efec9cfc320297373677e78a605adbe3beef2da7f627ac077ba1ae6b42ba36064c9cfe833b0d5ac8f53b04ec42a8c28254116f2b44a07372f15e01e5b470c602e60a79233e035d90af93f8d49a262fc1b42b90757227a79d879a61a9a2893705cdd4c2e4eb0fd9a48071dd52726ebcbdbe678f887493b6a944247ec1972b93ecf6bedd07b2076ed7365e57d67dd58207a281f44ede22360e182c74e3f89594fe4a4f4409a2045ca7483dad929225f24321f0fb5f6bb35e204bd0c579253bfe79a09d59ccb701362af9ac67f6df96646871d20b73013d4c2a489086fff034f4ae65fe2e327607ed479c03c4e514a3f045b85061efa42a7d8c75a408ea0531592630cbadc86f0b3a2bc9acb1efe9359e99ad04d490387bdb75dd1d710aadad6a4db9a93431cf0399de3fcadb35fde5fb32908b756b4d6c4489c06e52ea1b74d36d66ccbe11c805a0e95557ce3e3780af613e10b0c45c86562dc1e115cd28e79d1d450349058004abafa87434ef9140ae9b7938e27080dcd3326814d6ee95b47cbab35ee9dbc808ce85f0c6944b74442eefa9648c7ea61a2d1aa68a4c6c577a70025f9a9859fa0026d3cf54ea0217fc0ccc6bf9d745946b9ed413e3e1fba186db55728e35af050c77af5515d5057f74117c83b90bde88105949786aca3d2e40b416af844bac5207500d016be9c7f14e29f143e950100b281ddf282566329a584fc9e5dc8a418d06a0624e3fb3183508ea3bbb7c81f572e61b8cb9ffc02a335368c02a0acf365f0e14b7680f276ab42cb095bb815958b919bbe296e3cf9ff7278d5e2443140eb60df82d7ab06ca4095cfe0dc1f9118d90e243646d39771606f8b9a7dd0e1e978a0e7202ce46c90379f9f9e44893bc2918a52df0b866fed93164d9fa10a18ce1ef0069e8ba95b8f4cc7ac7f7e3df9db49b050afc09202af997bdeda0f8b89b87380768f27cbf0ae26c853baab8d33c973ff4aa2fbf621c45075f53bb590a19835f07863383f619c57d4ce0c9326b7cb3e13d370c84b024ff2ddd1c179541a1757802d764200f6790573435a9933820b997228fb27045f687cb57740ae33e96f94d0d84480c742d574db0615246810eaaaa0395361f58f3d2d124f4615ad84591df0b6f204ec330b64b4913192c6281bfcabff6fdcfade29d6f2d9bf68f2cb12e020c36a01548a8cc989be83757e7ad225d720e18f06507c1affc30ffe985b36f2904f7c3e0090681f45bb2a2fd2f73b4e47a334af18ae618ca840e7494995c155107df75f07e89d54dacbcfa2da7375ca97abd0b5d244c40e731283475fb8a22160b8b2e6312dcfd4d42ebe4449f55eb2457a72b374f1b441bb676402e7ca3c5340e2aa8eddf58d1a98db83dc0db10b98bfa59c38bee383bb97fd5d7100db3521900674f2b4bab63cc9bc955107fa9695521026c766b4f6661cd073d2105be227c08f550a995e27f7c51c286ef74697344af960859c47dd59fd14facde66d0cb360e5bc3221041f3abed5317dc3f49828b8ddb2d6bc07fa69c66928949540684d6018facb6cf247256ebb85f1bf2fab798f3e19596512bc8a0a48247f9a22e8d2c092bf08ab2a70bc7dd8bd9d3f2f474402ef44140c4ec9971feb8309298c40ab70b9d75eef1e6b2ba30b1830aa83614c934f88ae044e056848212a90a356466820a55df0f06102bf8d61c5d128ac753bb3544f101f6ff92b95da31bd1c789874a07b4e551783b56373adb840ab599d695f4a1346469324b1d1196aca75e08c8b50984c099c94204cb73c11cb3048fa9fabc0805872432ce92c502774c9fade27703c583910d7f294d53327bbd3fe291b34a935a11de825a81199413f1667679170dd272ea3cbf1629ccba5316b2564e22b3480b220de737312e0b6aef3d8db145080355da53be185ad49c8731b56fdac1cf2a17c81b974a51257aa736c43b3906098268ce6bdaf2c78b4b726d0a8b11a2bf29481523ca620d71c0d5400bbf5ab00021b10cd6203f7c6c93f023ba1a5b77f0a16565b5e96e81465edf040f93b98907d07fcbd84846295eb07d2e7b6064d2fd3681584ab0be34b508bff4c73d16420941c08035327bb90f83e62450390b7e5f605028c4cce084c0342f07352fff9706fb1b4126e8032909be108c96f2edd76d480fabbfa8b9477653bdfe5b19849a003c2388139949cc687ac9c5cadb5b53a3732004bbf92edfcfc7067b947b99fe0d51b94e09bae98ab59dcdb3401b0c51e9dcd5ff6c704634d7ad70ab2255676d007299e3207088fffcc956f130f30a2e6b9cdeea772ed9db147c9b14d2ed9c240bdc6820a84dd7748706329c70e7333159e7f2cdda385614228631e710395aec00e0630757852b14dc7c13242e45cce7442fefb33c6d0260cc763d17e917d26e09249fbf291458e94d48fbb39c89ea26cb9f1bbe213d482821cb42fb4b8c67f403375f4eb78802c594215ae9c122de51e0a04c1da7ed740c157c87765235a42f0ffe0c8a4b2d47bb9b7865edb71b02d1974cdb4eb4b3ed3e13ddd54cc638083e05a4c34d5ebfe8b209b1b69cfd4c4348895ad12bd5efc7f047ada2567033e0860cef0cfd24bee14034a7a6d0bbc3861946470b63fe6e90f08aeaa407b8fee2ea0bf3cdf862bd90a54ddcd518ffbad9e6f3bde84f4f61182fbddb3d316557af110ad8386d5ab5b510df1a09b17298caf8b56d099299c3cda93369a8a340dd34330876ff063077874fa48a3f6154c1d0c7923f1250ebac437972a55e5af169d4d70ff907077aef120ce7eec7bdb987aeec5cb53e4b9938ed47f463fac2b4f3c6e104cc7b017584bb753da6b0b3d965c5cf0dfbe2d2b2e4b2c4a0bf21059681c99f08501382830fbaf90d50ec765e1bb3d70ba14daad7a9d8e03e87a89e96488e2508258c5574b7d8b1762d6291da7dc43c16d4d61ea26a0556dd512f0bc18c117e09b90717ca155534376f1c5fd3ab658d1eb9f60f2d0643a37a13f8130942d64f01169987f27ba7d98ab48b6a5b58b3c0c507846a4a374ac78c2f064287b6229b079dc63c1a51a32f071659971836fca2e750cd6b09739dcc78247f7be92de8fd0d6aa63b59c92a17c31a0c6d0e362317534aa32a489866239ceb6b2db2e6181c02c0c691066e3c0ae2beb899de62fe25b3d0acaa4246b5032fcb9f3bca238d31003a2ac3bbf99d9a90e9a460082c2fa86053546a41ac91375a7059f5b721466e0872abd779ff2aa305d57aae271dcc514fc2d25c54bb5207bb86a6250ef91b71052daf80b314484b3629db87e7d91c96e4b48cd0df1febe7bcbb5456ff0ddc7a0a19e2d0c6d0a73be134d25d7d56f56f6d948adabfb25eaf186653b2ecde193d0e7b4bda594154e30de8e95a8a44779911530d5b246981d5d6b48839205a450c09e0bae81cb0ee5719123b0cf156ce7a69b38177c60c7cc6c13a47667c68e36702b521a59700f7712e8a7ab119aaf06718f7be3f215f4d7fb84006e00013b8490ce80f4af47892e3f63898c5283843945e681da6b3d909baa2c3917e81e22d030a8eff4a9f6e83db190a78ff1dad78f93100ffef9c2ce8d1efa9aa24691d300603b2cb5a0a74fa5c49c8a43907343389d76c337235625eb69172a2ec04d193ab08a0aa2e86010f454c414d40a48e2a896735451f141453299f76e8b7eba7aa2d0c5a72e663fae4bda145b3fb26cd0823daaac71d95e6ffa9a2b5e07cd2f2cba4002634e3e84221a4325350b2402c3502c9ab4c25ebaa3929028d75dc92e31cc80b095a285351653f85c1745c2aab8059f492ee5e9744860dbf178bd06412b08f016b0b67589b9c0f0e78ffd56bb2149e85f62bcbf6b84d6030b2a30cd951e4c806d948ef430ebd3d085191eb41653ea445bd140ccdba7c5fa26c78e23ed7850e055c97b28450c6de86b24b415592429cb98fc31942607770e87d73c8173688110bf18c90010840cd515f6ffe1ea7ef5ae9e1d5ce887ac6b7c84ea46be647900304eebb026f00431a9642c68164713ffa9f3c8cffd6cc3e1428627813a6a902f30f4dd224f5f13d3ae5e9c56c75871f75670ac9ea693aaf0681e8bbcd64570bbc0f217d1f9ac531d56b3defc20be8f641ef72313bc8950787c7ad7388647e565e052436f53aeeaf690a5dec38713dbe7485a6911dbd6791bac20d54fa521bca3b016df4bb4c8df6810997edf83d17082ac596076ec836bc32e1d60214075b49020af1667906ef57b89ab49187838d60a7fa10d146e24c3d01f0e5877c4930942a0337447910ba6f3da89aa541b020421e2415e9550742af8037926f5ff99060060a27d6e293b6d9ce08919dbef4aad04aacc3d5fc05d6ea8810fa99a0f5ceda730d6e2902b2a000aa6f5eb22038a186b7f289898e86251483234855638f248c29081fa5d7c565fc02abfb2be22d1e0bc5e5dd8a9be4cd2ec795d977d80d0d8cf805d6a1f5033a87a865a4043068bf89b401e6e91e6022cbfccb6a7f8becfaecc7047eb21c02bf48d32a2386a8d96c76e852a269513d0c443c184ab113b79a521d08ee75c480f854f6bd8e3cec7ff162b11540155afe7c43d6959a7dad8c1469c3044e64796c5d66d45785848e8c165c6e2ed6685b2c15e66d9334b0e14fc47d80085a8f9dcfc711ab7577541feb706ffcd71d016343c0aa6362f812102a5d01270956f2596d197b9c2826a8b1102e71716cb135d56dccc6d956c89d180f76c4df0c33f25f0ce1cb4e4282ecc70352ff64e43de7e0dfed2e49d6bcabc3072ccb6800bacd740852f49b6648d0ccaf0781c0e9c76265e64ef4f3b169e6f7869d3cdb079e8fe7db6637135edc5f9c8663a818d6d5d343cdeb35a372084b80ece9f87e0ef35376a391dd0b0ac8b0c38546694fc6a89014a8b2fd95bee45755caae37b50d79341f1f86d949885bd2368537e344e7fb400566cb1a82460ca49dcca014ab0a2dd7dd6ef86bc6ed007f6b01e9154261e17a2ce29e74f51f7fd44e386129e201c6c5141203dfc5844b5fb9644ac1348a2ea75e1ae98aecc2a39cf28052897704566adb45fdf4782dbdd79a96ea5297dd5bf44071b028c1bb7c775f0f38fff20b2810815a4d4895e767d55dde7b7d2ee5b9a2179bd4af26bdc0f621e549f1c302d0b28f42329375e03055e3f927e178468b29dfba8f1ecf39b1933ec8dc7d420f4d4217c9eeb2d6db00e0569bf17285c4a44299108f0cc6ccc098028a3e41af0630b06dc7ff07392cfc9a42105a513a47b242ba86f3f7ef78830f5c78eea9b203e3b3223fba880b498b19f9cbf8f6a17b6e6c52051c70de937ba2b4554b68d20346400b2cc140bf7e522d44b929ea56cadd9167e8e16625ea2136c8ccffd1de0582cfdebf2d656ab5256d1c3ae397ad1847876510159ac2d14ff9a57fb3abd9067e37405b0d82ed9534c0febf9e7601647eea4197bca3260fe11b8665ca44a502b3bbd48ba8323560743076abc6588dfea40a152d5b91b8729208a38368b5b00e7bc113cc3c48b987192f7abeee72a6683336ec62695739b2e3443208bd65e705e5f551358c803ba9d34ce1abc801e00a31e5fcba19504507d4dfa84c4f3da808719dbe6fe10870d66b4e3b72103fd1f2ced71048b3ca492e4ccf6e887f796b04bd137485cb0c8916f6f1c5c2b4d8c04f5b916f0a4afc16cb2434dc2d55d5700417f871c33ed817ca9c450eb97dd29793a34cc3784d663e02b5ff7424ea4d180f99a78fef644e577883403dbdfc7e72ba9fb92324874d55248aad686b1deac503562ac51d1757c46e18921bc29b90be46b343c9b3d8e83c2e19160b2a60b5df0dfe2c09b5933874efad1bf44c70fbfc28fea919e7ae2dc595bfcae1d2f3b0cb07496a255c47e63052a7322d4ccda5b320ef9acdcc890faece8f6cd3dc0880cf0a6006fd5c6bada86c101677543dccbc0d122bed375633cc58dc6f283a04a153007ccd107091527c12f5241541bdc74a16070a56acddc967f10aaca882c384b60fb632230976841dfb91e2b17565b1ba20572508eebd52521c69de4a1417ebb8008a305332a640810ec089dc0def977df2e9dff8f78a38baef523a563400ee3d004466546595862e01824259f3dc1b4f772fa9697ba274fc32118b3125ca94d800961c34dddcb06ecb576cf89e9971eb8165b70a07cd7914e116af433f02b37b0d73690bea67b00ec269341bbbff9143372ed65ac6f269d937ac5b58fa53f0910f6df1baa177607a91e16877e54a50f6dce1949197341f7407c45734de5dadb406e88380dcd021d8ddbd561c26a5971285fbaa27d2ddb7ef558857b3af131503037e6c18a7cdf96a69a30b6f070b4602b0699f8ccca240a6acbcf898f15e44e4073382c4ccbacb0a378bc07d3cf15cbd45a6c0bcf867c79cf3dd08efc6ff88a60ab1028f0b82df4561e47879e57a2eb1561a920298340b41777332d5c449e16e0b9bcaa1632e21a0868d475715e78c10306cc03e1902283e684be2196e5b90190863ca28a42c88fe3d191ab5f370c251e5dc3a0664499fdbef7e358c0157fb6f0064f68398089508faba95fe657a8a7c3de066a8cba7d6e78f0fc14d5e0b13b3001f69c36e5bbfc05162a9c8042f207e11354a9421e4f307ddebd419166c3fd3063c067aa716729ec34c877ae1fc42204b602f55180d719105d65cd9b32876a303da953d4b99f318a156e51d6d6622bf2d9153787885922d3767bf6cfc3582550bf34905986a3c02323248230c3e81a724994166f32dfddb5d1d151aafb867ae0d33534eac63c348213a4f7656a7fb8c79e8079c14934ab1d9bbc250b7a9f3a10c3249971ff0faf6aec2252b510fc5dd28b24d8ef1f4f63a7a0fedce891d859d0a33eced6d9c50c3abad5e5fcdcfbfbc22dbc1efee5f84035eed0c73b4a236590719579be736b97deeb77c16a3063af9927c3ea2a2df795972797568f3da8d130784127f1eb52d7b6616e0c5f26e2658aefa12a597aa9f312b18811bf8e8d6020e00d81eb38c8e06293a5537d6c0940a6cad20d5f69ca83b5e22411176fe8f1b0aff13536038db93ed1d09b3855a5942879fc8cda6dd332a4bebf8c55638a0ab0d621d0cfebe87f1d58f2a07409b46ff434d6016ad03e3bfa62cedfbde60499907cf3f5e337bf8768dd9e46e8b3d8f9b7fbd34267c8a5d3c77e1bea41754fb8202277c3b952bb2a91a6ad58103350aa51c33dbf71bef7612ebbb1da2b4d8545a060ad9b20b0e2329229e8a06ad5c272208bdccd6eedea58d1e326f45861284820c91334e5681dcebd333139caf909b53b461637b97a0f7d73d32514abee649bf0230074a63f6a577ab532a4d48d46f54e48900b5c4cfd5e2371d67ee8ec5f451081963f016a27d898f4d01177df92749ed5dfbbc6876af85f559f736a03b6e5e0bf5da7662eeb861c631edd080c44ce5b97a36924199fcfc4c0c8bc3414bcd9904229c0b2154eb575e45101d8498563a555ebcb84c61c86699eb6a5c4ef3864f06507bebce9adee37d7c16cbe38ecc8aa135e126425ac55c58b0e42cbaeef02d0e1291fc68ff40963233ed92b6f6f3326b83b64e9bd99e222d13aed9226700b004fb841034aed4635e569a62fb4fb00c238319cb47452cc39aa4cce98d27c7e50e8df360d12d657c5e0b7ca82de5e456b26aabac36914b4dfa13f7873bfab38b09 -generate_ring_signature bdd604377d0adea441787f06cbd71142cf15d55ee20ec1bea85f573661c71201 2487ddcc1b463edd2a754b037e4ebce25e5fef7d87a227d6eafa588404345543 35 b089255060a9bfb64276f30e0f156291d61ed74376751cef0c3c519bf97005cb 71344fa8a1d99b228c90f79d26b10a0936b43185ae7e456376f6bb527bff449e 2798b734c642a9ec401cf1a33c14c239c2a59c5f48eab81450f06b8824589ae3 3aed394a9a3077b2b7b6254d4ff647bb96b7438be120fac9204096b5f169673f fc7b91dad8e497665fad7726ccff5cbb5c611400743fa25dc395bc95f09c7704 d4a4dcb19764fe5a25a5963903ff0c0b9134c4b8be2cf7f33e7bbf42b59618d1 38b63cbc455b1340fbaf53d4284ed6a0db201b4e4aacfcdafeb6273b1a307e3f 0b8d7c46fc803560f12ef832de6079adcbc1ef9f787b45de01a737ff5e95836d c50649bf69d8dcaf869c7e42948ab1784cfdf39af5a7e4f710726f439b25fe51 cb559abd56c267e40422864b8e58cd27ee9f9076f08da0fdca1939d20e6500e0 83c0c2a194cb48bd356a96e50ba828121c00be8e57a8ffe7460fcdbd543ec13f 1cf20a067afc5e04c12b6ec9ed5dcd70d647fb13e390b35b24503db9590faa9e 77ccbfb6fdc8e4fdc4b9f1d5b642a0be86ecb52d0760e9d37949c2a08be3b17f 95061c0154e8feeefdc01a2d1fed47012266721d0fc3b81f1e39ecce4f8b2850 2009e95362d812eb87acd566bc521ad12249dc3b1919d8af30ae0aabb389b6ea 7a8431ca2ce7199d1db1a3c786fc9405dde6d0628952cf95174f0d35267a41fb 8fe2a2d24ca5a08caca966a0a5175b9807f3030631c5b93b7ac35eccf25724e6 67a3edbba5207baf306674a7c24b2093a4bc41c3d43bbfbd545175c8c6028bab eaa0962f91596cee665815b64415daf81bfca68eb9cf5eaedb9ac0a0c4ed0900 2f87c482a8ebfa50f9776869b0899613d31302f61bb9a8066182782b738db442 1fb8bfaed14ae9aa8f13807f7a07d705d47cd642349fb1787734233b7c85a3ed 35be0bfdf4b725f01e4f05af0bb9f02ff0dd4f612ded87a2340a1574b3aae315 4a0fda512f352146f7859748681ba33d2760bcc72a20db5922691856f7e603ca 33f200d19c8fad76478d2eec8699beee2061ea1f31a6f28e764cb7025691266f c968d9bc39daba4299986612b3467ac3d45ef0b830dd62b497e52219b175fccb 22460e30d871caf28c49e4cb59cf296682dc386f5eec6279ffcc150faca7cfde 1f92d5efa075ca1f939b27e1b0aad182cbca7f20d2da676ba1992d5696dd17d8 73cf74bf162bae040c2523789a0bb3a96cdf781f1a766754a2e2ee1bb200791a e669e339a639b731135147dabc872053f458a767ca38c35d3e9a3049f7e8908e f8183272074d1f826677ac9d576cb3f558fec821f5e32cb337107ad6cc85e63b caf86118b2e212f253e171c428e935c8eb6cf6ad085f25af4a0a93f9ec59ace5 9dd75314c1d8ba4bc81914778dd77308015880272b65bc37c402b1cb832a69d0 bf83ed74a2f7948e17828735a98afdb798a3f8793d1df6d4d1d8069ae7403dc2 b553310ecd79a8254d329a3d6e2002c89a501c06b6ae3b7feb152293dd26397e 7d483ab7dcec571e016a577f74301a6614a34734be679ecbc324170dddf06aa6 57993693eb82f28e7ec9a7bb128da07bd30e23fe4522dbc460d96cbc87482706 9 4a2c21b9cf75dd7c543e9532c826db787d8fc625675a4c6d26e08595d2fddd006be0b7036d89ba3e9b7635a6359b4341c8838a5a019c2e1de614a39ab4aa510ab83d40d3588c4f1bc989371f0c26ba7aae41e15a70584afe22514e026f9b3a0cf939f18551446ba26d23f48d4c97abd86ebd818484388b2acb17744c9b834a0174f1f0dc253bb7e99d862a998d0db2208e0264bc84df0853ae256145addf2e001b578e761c0e7fd6b63bc6f7efcd1bff85df1629d7fc507fdfbfe43d2f9dd8051429dbe7f7c7f0cd3576ee31c67438bec03155ade773ac7625aba0722514f50b13ae021f0700c54db4cbb1276d02ff3b459bae220a04cef3f3c4d5d5c38c640294cbe08d2b15ce591df0d5545cecf16de899860969a59043adef65d6c656cd02a21fc373b429ff8fa6e667a5bce9f68ded3a31c272b21ed1fc6b525daebbb80c56e2109416c0cf4aa3d17039f08bce24a749c74ecefe429c8893e62e7f7ff40713579ac476cf00fc42814a4d205ee4b126529bb77bdac530a05c4e4ef82625003e7e12671a6117309b4c716d26d8c112adfcfcb89ff92266a7f89c9d292ebb0cbfe64022899c1943d7da369cefbda680494a84cee0372872fbc4b830d837010754ded6e916cb89273c0c72838ad9814fd6c6f66c8f294459356443c2bf3a1e0136da5d3d098a4382eee903694fb08d05832b925e511235066179632bfb45d30b842340fd4fd4dc0c778d903f6623beacbd7010e2530eebe8f8a0c24fad45310223d28062fafb417aad594cf963c71fd112e0b307566c82574360c3d5c31f3d0b167f538181adbb60154cd6bb6e09e3f3fa3e7ed906d25e000a1c810028872f0b942add66077740dcc443e98bfee59148a61ed2c6b8cd9ea20a5adead8d2de004287f4ec7b3c9241ffb2a3e8ed32db703799bd782be044659a0549e7856b8f803d5fa943d8955d67accea03a49fe674868bdaef609c27e76034b8b7d825de3007f87a86b8401830dde13063801c5df0c5618f3932591d1ce0d54462b21f0f9204535fad585c986f54917450db3f3be86066454225e2537d1241681830ea0be90a39a5bd312a6ff3af653d29de7120a6e8bf9b7dcf8210c671d21a04365d3d470fef02817c11b46d9f94a6db763fd55a45cbb9df94eb8b0fadda870c9376fa780b978912017721284c003ed0a48447f276a53ecf84c1cfc92d7eaa8403b8dc000623e8d5f7538b02a409f74b7738f88fea6a66848453e6d9653aeeb639a21d8701abd82f7b57f9707414274aa1c9435f5497edf0e0f9f4f427ff69e7070f2dae0b5c1bbd3fb86b33ea6f9765e9c2bc825a1c053036f0ddab5dc574f4eb52f3fa01fe360c488d5d4d1ec951293e22c8cc3be094321aaf167169ddb2780700cbaa09eced6d0b5860cea6ecae6cd2ca361c5e6da6dbe401f58f6b505a336d1543d8062a1599de6086c28a8ff6562101233695c1def591cd0e5b1323ba9a54fa83bd09d5dffd972385d7a263bc24f634aa55eee73625372cbc15be15339f85ad41f30fccb2146e52b46adcb64a1f37d2a3ba0a2554dce3c586d35666f4f0c8713e110c3fd0aad45bee7bcc6af9d91d68ae2161e87dd0d13308992f36fff68f2f5beb09191085f94df4c11464f71eb4dea7aca71cd67465352fc85275ebcfbf3f70cb0e8ed51ea2cc0b0b6ef6894a1f0039bd9c8667f6424bd0b4fd1b0272f311e7bb06f28db1607e2209dc40045a8256feb71157f72f6fafabea6aea557539a924ff0b83dfbec966374e6c6f301983af5829358d2d2645443c7ba00851bc7ca4cfb80d63e3bda1a45a3f0a45e783dda4b457c3658d528d62681033b153d808f365d007947bca6fe131dfcefcd5606c515680531fdef8f1013903045a624c16c480890638259ed6a4edafc8cddaa21beae0e5b4ff0535ebb1b3900ae8969c26728d1d01ac487e49929782e36ad1c073190a8e41efb18382ab791bd728df83b144ab7a07708b9c62bd3dc94de1758243cb8d257d5af10d0723bfd9781779655e4d661706b56731d58f9dd8c743b6018834edfb602cf52e7b46b0ba3ed38560a572bb5204c389c876816711bb74d7c7539839fba2c476e68a7c2170c0cbf5fb1ffd700e0606e70efa54651e4efcc7386ecaa79c77ce5e274ae852bf848f77c69dc19be90b970433e79b0cb21d9dd06c8e4913db7fd725a38166ddfa3b35370ea4f32a4701384a5d12327521a4de2fe2cd278d751f6b9c983c1dd3f1c8c9a22c7df98d6b0628438c427ce0a9ac6a1030282e8c5dbdfc99ed6560a7a904c52225c2237cd80d2ac1ce4fd47be52d4108f9767455a2a4f1f1e28405415a315457091b8782480130553c5ed11267130e078cd6c49498f081b4c120cc44c12e2d55d0b63e5cd90c586e5227249f23fe58c17dfab359b9c9f1b6deadebbaaef492725df3eac9d00ddb8a165e4f3539ed32041b293ba076f399c03699a463425cb4d7a60a492b20014e5f465244183965b86c5860bfb7022047f066a2218b87e52e926d0d43c4100533415b09b3f6fb603ba78fe9ec57780e5a0936fe778f9b336a5e26dc33e0ca0aa3b5d5ec3d5f14c232e1d90b8262bc40720ccf8e2006f86900ec58eec5fb95009ce267ba6f5cb84f5d0914026e24ba6023a55d42ba4e0a791cd13115905098024d5b5e79c9af0f78953c067b530b1d047e0d26f11a888ff810941c16a309040cb19e75e0b3258ac470cde46f47826a250c367d18ead01cee9d5042bd28d31207dd284b0d3122553f87c74c83541bacd3dc73f89e8b28bf415299e79d500c7e0e40b10ca0bfafd34a8541bde4e219b4faf89c2b21de84509a3fc5228fe4840805167baa09170c14e9c89cca5d2371695af8fac21b2b68f7eb9ed170a51eafbe0c80d393e0f481e05535208742c3cb537ec4f14cbe8e37ead0f53dd3cecb8c2d01c6f472a2977d6da182b8fd2c9462bf81d2dc5edabb8118d9c47448299cbea70d59d5628fda134112ea5b68333cdbd88f49b89e14025c10e035f0ba2c4a5c150ad3c6018876bce5512a72358e9f2700e30727a19f8f5589246f8ab92ce3b2e60fd46633fdd05e58d87321f65848abf42d267599a4511560faa9f1f8337f69ec0f0d142d3da81f673d2803475c04f6539c0ff87a4d61c569daee1af6b848784d0f -generate_ring_signature aca251d46d3b5ba026de943e11da65c82565e3661f1aca050799c330dfad3d41 b26123f9a3df78771873063ddd286b3b07e8c622f56903f2efce7499de4478dc 1 332885ac700a35ecfcfd9d4b87366e052b2b64a9b71cede41d1498ebe448e3e6 e884832fa0894e874fc141dd3685f50e5db4e63743002c9005997948552f4b0b 0 8863a82063317f1764f90ef62cf948bbe5388fbc1904cbe653060c1dd17f730b23ca5ed50563c3cd4dcf2b29ff310fe744075388728e4fff508b74ba40c38808 -generate_ring_signature b775c2fa902004d9f8b68d5e8d02f5a8c1a5368339cd1e9a5509f9cb5bde73d5 7d0c3bbc111cee95905b30f237c3a948d90feaa912301831d3b3d0d765056b96 1 8594f65a950e1a254db53911145528dc04b86702259d209172a832b7886d2d2e 007a01d31abf4ef99af89f0054b67b885b1964b5ecbbcc2f1c9e5dd1157c2a04 0 f84f63410f579ae67179e7cf355523d9bde5a52c641e783b8da71ab1bf20ac0ee788afa8e2be6d1d18fe7c44c4a83e210750de994f9744e68a0b6cb147c2570a -generate_ring_signature 677e5736293b69b1f168f92d1f194d05ced68b431ea47d89d1805e285a75aa96 c7fe04a57b9bc912b8b51cb394ed14d95cf3175482764e5a9e311b79669acd05 3 bd2dd96a9f97843f6f9f67291f4ac742f7dd142e4bc174d83242322ea2ed6578 519650d18da99601cfe0ddff1cb8a6c3c5a3166ed0f4b4d5df08e731f1794af2 cb6f9d2a06625c1ca76b07ed61ec15de053473ddd9b648ce6c912a3d176ed6e3 7fdad023cd867a5981be27fac5ded58a4fad51750510df61abceb2cb6cefbe0a 0 3d93b5219f63d4b32ffd18377b7693247b21b6229fd8858447fb11cdbab8b205c1c249053d6785d25a971def9408debcdf30904d250bf10bde283fca1a1c2001cd692f685379eaff3c9bce57b0eea62f67cee43f835a7175b0897dc193930e0781b91ee35ff20e21de0b86684536e5b5cac11a944ad10c3368e48d9d867ea906fec3195aa453432c3b4a11a7ec2af41a771bd726f8fc58c925bdd4b76b5ed00fc2f27f8f81d798455d2db1a9ece3afdd06974752c5fde9a7b7f7a29cdf6a2303 -generate_ring_signature 2c56d0869d611a98a8583af24571d794a9f7d657bc9bb2668f54c91339377b9c fc4553cdaedadf367b81758a5b277e20625549411a88c7325bd8a7129bcc476a 2 e82c7b44deef558ac8ca40818f843d27480d89f5851c2e0ead9e2fe58eebbf9d 66b8b0b3f84eac9fc67ef4d8f5324dc53526d1906f0f830d21bcea089e0c42b7 7e23cc4cdb791d7b4ec1ceb2ef16f65d29edc81cfe5786a62f4abc457c4a600c 1 efaf12bd9c505de07d2fed7abf586f6ebce41dd24e079e9c497ec10a84f8a5058240698987990c4110a3dbf9d475e17cb0364dab7102ef2ec904761841478306cb96d28334dc1c72086ac2fdcb2d3ac70ed264e7d41df1d0b04aa50234cccf0c5d81c9a954e9fe2cdff13b6a11de991fb9715907e57944987ec54af775466d07 -generate_ring_signature 8446c895fa03164711b6289f8dca06a575ccd8558fb070d1f07a053b6ac62a88 8f78a27ab3ca15e3a040e4ac2fe32902d418b7317666becf389f520169099aa5 1 05291f379474a1eb448d27e66553fbe9df2a5133b15392f6a966ace067a870fc 7bb9bf9cf9611b1609b19513734454f28f49f896fcf29e689a937abcc07b4803 0 454655af64c9668aedd70bcbc9103f774b96b1198d89def3baf5c5fe9042c20cb7dff1dcfd9fe05885eb08d50a82f64e457c4396f36dcc00c6386bea897f2600 -generate_ring_signature 89919f2cddcd14dbbd6510687053b794e5b8f01565250bc8968b781f24739668 0279f476b72eebe10b6db1264b63b2b074144dcdd4c0c2764cd4d1815acaf713 2 f82d16dd5f4d75904b7b9195713ee68a1595aa8d9de4fed48c72c0529734f52e 04569b72e8939aec2b6db3b83ee34c21326a951c83915e1f318435f609445b41 824dcfe5b0051072ef13f8c9e183c0d1ff5f0dc9a2c6f397bcf1bc3d626a1803 0 b2cd517ff0d9cab3919340a1439df08f03851df0ef165f59fa1cc90970a36405e9983b40237abc84acd69708c28047af1cad04b4e9df06ce2fe24f2711ee750b5d36b0aaa948190a72a6ba03417800119ed6646edcd35245b5652f8c239e9f050ba934a81092fcb2b93f00922b719164715f7f0bbd5a74c90f5059426914cb0f -generate_ring_signature 0d45a262ba21084d5b11dc6f7df59cbed5aa38f517ca4a814ff490923916a652 9b86f1081957871b7575cd304b3cbaaa38d3f00703de9cf9e8e184128f864375 197 d0f935d39c2c505fd5a8121bd2d4a741896f55ee2470792d924dc0101a6a0366 460c54ce55f47189b9ef38efcd9c2677e4f3b7705893761ca701d0fcbb17530f 497b4f9949d0021f02b66a82223c4cee3471218d072dd5bdfd3cd75f22e7c0a2 86234ee6f836e7a59fa06e91798301543347f89beb69c47a5610cd6a48b80f81 eb8da56b6a7011e63dabec4649def5c6b1e90ed607e3526ed329dad2d201681a 2d35789d85de0049e0b401689d2cb627e88d8fffa91cc98cb6a0cf41d596c936 c3789543dfba6c37c771f175366171d91d5ee954e9a0626df83d371e1c03600e 19096bc68e91305027ce6f497869c5187fe39a606064f01dc45f696486fa24ac 59731aa199ccc0a87aa746c6e6cbb76295bedcb6269906637ebf0ec791706b0d 608f849ac3d56aa72ea3d80df7d5a784dc4210ef2196ac1fd972e9bc7828b378 e98d5ee1f8de14bccf0abab68e749a9c3288d3d0fe6406d668f912ce03e5830f 3ecbf97e70c7bb254d6afe53e6c15a9e240f26fb732e9c8ad1f13e6f5bdbd766 bb32c560feb78ac2da03f973c620b99dcc06c726b2a570b732c862c95a1a8f79 7475630c541b4b636a91e93ee3610ab5ababc266db827320a8814c3a347b9b8e d0a372bb94a4850540f4796fea6bb38d394a47a935d2dd4a5326d86d501c767c 74c6fec0ee4b8931974d59e8dfdc4ee0e28e3d1418d23428234b48ba8ad5393b b688d964bdd826f6748bf989babd2c55febea15e51bc92d3ae961509c0e336c1 90d569472bc45d3201db602f71869cbf528b2b6b3fe19105278ab39bbe1073b4 d9a36d7afe645436cd02b3e61a86cae1999ab5242d039b4097508861a5e247a8 22c1541a04cbc85028279c5acf1b54b6151b82d09c16513b646a14c6944c3f5c 958bd65d4e627939b83672a58ca806770be18562c061c2a225dad1efe0d95b6a bb3f7a2da142d46d520a1741c9c24d85804ea3da66c691d836cc3b8c5f7421bf b5194a6a410afccb0a4963cb71afe4c177cf90f9a7a3a3d4e9660f1ff064ea93 4f7fd2fe01c9dae2050735a578bbfe329a5f8e6153816ac53dbd34762f87a8a2 6596a10ef0c3eaca356f8873042150216ea7df61d25e36136df4746fa77ec0b4 860402d1f5982aafdee30a7f8830a0f4fa64877255abf4a5e2abde02f43e5c2c b86ef36d3b6873de1efe9e02e2083d3bfbde0ab81aaa37af162f1a6fa0eba0b4 5e7000837abddf0711071b6d205f999327522033d068d3d029187b083b5d88dc e7c7f0e549f921bfc164a4b724f261759c46b147b8bbf0e6e68c39503c198b64 fa55f966b88976d859d6e4776ed2dff074761de70d589a1c89c86279f0005a3b 75900a1d92813d506556b6484ca988d165f54008f1a8b50e0ba8c7b23bdf871c e7bf33d171b351a061ed80d3c52029b6627c7b73167d04f11e339da701cd0d6e 655314b8659808e3993409e198dbfe6555053ed5e0c195d39808ea0f45972b49 89f9d50a6e5f312d92da3763101453a9ecbaf69e84f47c21d855a05bb54a219f a64975ef5c421dad5eda121b3c3e58f818443428a05263053be3da99fbe77df2 5fc852cc8337bd39173274d4f3cf102e0c2fab30ed3d8ad70c753e3c344ed4f0 61298f604f8dd7e109b8600eb9a84001c3d7dca3efae6229dd12adaa4e3d2c6a 07f4d5b629d9c2efe6705f2adb2b9342822e615a9fe6d98897d74fdd89fe2878 5e0f62fd8243023c0b9e7dde96d4ef14e3079f4b0d7e76630c670c76847aad5a 345e572794d11d758bc761d9f22f977c827ed332108e810b21b85cd6378518a0 8afce8fc456c8724baa05516fd9af2ef8ddd29e538aaf1214756f683188a1bef 08897543e204208ef1c1118b4153c450245dcfb2b7746e48e8e389275e816861 d10372082654fb3dec772174e0967ea577c5d93d4ecdef99c70867d84829a10a 4e038f0e07dfc7e752952b19ec3ab3355b2b92e7a39182d3eca79b6a3f87d14d ad67fe5388f5e92e911157fb2b034c1a615a1eeccd3858f255bed067540dc711 c40a5eff26f7221382959ee22dacab3668894870fad1934eb8ad8a7e40ed36c6 ca5f7f63c498e39e135e8c18f0262702de1348dc79f8204c216651a9c7f9ac71 e578f8694a5e92e48f06d5709ccfb681b9c8a738abf20861aa51d61872cd3348 b238cc4ba4613e26d7d41996e6a15b7a1b4314aa7157a984eea7d2bc0706d00f 52a762e09b64943a304ac9c2e110ec56493d00ca752ab2c715ba72f7d69bec89 b5b444ec26de88fc6ff295056545fd65ae83c012a45683b9a22a379efbf186dc b74cde735535b0acd9ac6da6a18c10c4d3e889f1b0ee33aabbfe3e942e9f6321 ae98eaed6da71726406457e4fb6b8fd6577e545ccd7aab51e0c5d51a44265e4c eb366a542d43efe8e33f1f3733dfaacfcd97e215a44ab0cd2aea54a485bc1ee2 710ad4431c43814d664c3a3e0507f59cb8c65c3fd6f6b9382e84bb308ce2dc52 957765427ab11d99da32c054c7c68f888f02f1f2fc10c5883fb866661b3f0e1a a453b57b38dbd7b2af86ba28379d8832e3bd57f3b54897875b458ddff98e7949 a9b5ddbc6bce04caac06aec9761a507f0e09a8bc3f2e7fde93304b5fe5e08673 e8854e7fb42255389b91010d08f90ac2d86da53d338f914fa5edecd400d29a7d 15dd3f808f9f9a4e1f0f68a6157d3541dd24f5e3e51b0eb51543c1ba1dc7aba0 4f187a0886930e1129c97e3373b873c4be9875f745ee91a2e20c2e713a133d8e 64f4cc62ffefdd1b73eba2cd09c5f7ee3f2433faf82129ab79ba6884040fc3ef 735c79657390255c9072a9852bb1d92425811a7703b9809eb3793f4119e00463 5b88ede31e1f160f65b6063921a2bf8b5a28788ec8c3f54089aed3d5aabb983f 7c1252e028be184bdf13a60a46bd476217cceb393d19db69ef94bfe6f3380ac5 d2ca845fc232693381d777903751160a1fb43e818d548f0a0151dbd23815b260 d8ac75db6b5eb39e3f5e23d0b88f82a51552150419ad35e2ce1eeca110df4552 71686dbbe145ed2962b857fe2b732bb0006638ec2c91889f6ff79e1729554bba 2ded0ecdba64251aa08c39c52d0ddc5eed4c85df82f9856cda1e0309315ed47b 765d23651313db76c45f44ae14865aae2c57f8c67beaf102cf299f8f4f5d856a 998456c7aec76489456b3700990c3bf373bc85eb5643a15225819c91c8642b85 340176f1b4bb47be1b0108d75f4f06adc449a28a3ee4b3b7cd58d186cd6c7c19 7d3293cc9c72bbb56250a56c44225e67c63cbaaaf65f2e3671e3a8553889ce75 ac9752288d33835a09b7110f63c38c74a3f24b97fa26471a9d5adef3c4d27373 e152f9eef552515995ef4e9ce1257287e5c7848da1a19697c0ab30f14f7d1d97 08ffc32c3134d2e1ab2f57d3b287a8f6ed83231e756905136b42c1b3aa69928f a5180c65796b1a1ab3bb051ba05483f31e84f49c1eb893392fe987187ef85c63 8e5e0df1415bfe86c31d128a94174622e9323cba4e1adb06805e92e7b0620fcb 8bd78cb19bfc01c452f2ebf461670e00a3c3aff5f7ac6a1a789aa8e697ac324e 4b65071088cac227ca7b21538cfbc0cb09d0be5736b852e44f1b02508900cea1 6256b8a6eb474cb662edf8e72d45598a24d9c9a5d0c2d4ce90e6e4eb4ab72f8d af391f1ce7788799623687e8f64fb61e742273a8a7344a6098dce9f6e1b1d78e e74249c6cc363e2017d459fe26ff53745f0ebb45716b03f735b00889811935ca 5010e69034d8eb2d46d1a8b4f0982c2ff82f6a13e81c61ed262939eeac5cf3b3 88f249e548a21b58d4e63560be38b14054257f5b88adb0c1b2db7248035dac78 925e9971e38bab4865df5368cfe172581a48e8d8a25ffcbbca2dc00a1c765488 e3087dd5dd33f774691f876a3755f311cf77bb6972eb2c6f56928346973d8e99 56c686807919352f157d48805535d6824158026d99dadb72a104764d7983c2e4 46800e442d18322d36f6978aaa3790dd145716459f3ad4a04be9597725af3767 b32dd7aa461a9faeef0e495886d5d8c7fce4f8f5b7fb3fa42ac513bae5903793 b58508c057d5cfe3a4c4524c96c74f5af636b5a9a56cf1bc4cd816e6d4d465bc 5cfc9b6f9b677441ae34f285fe46e36c922c15ae955a9169d490984bc8b626a6 d58b254b005b382262704dac32494e11e867b30a92df690ad0883a5bf7bebedf b582fd6d2430c8651f15e80ce36cec80b79d7283bdcd5168420fcbd779d1e9f5 3e684f24aaba4b871045cc521acb81cc4a9068e06ae0a7c0c4a7d5ec7e2947db 809eac13d43c2fff1e919d0d5a93a17f97775858fd822b787cfd9d040cb0d341 6a6b279a65c84c83083de47e5ddf51965103b6cfdc39be8f0920cabc138d1b32 11753c1fd004eb63e3296009423f7a455019ea6122a22e06d300edd724dc054d 2d32f6d1bb3a0761755bea3b77ad838c0e0b3fa3fb52186d2ff037fdc3c0f198 3f8fc50bbfa21a30257686beda5a777728a60a3d187fcdeb5e361ea75b4d66bb fa0d4059819fe819544297431c1f676d1b486f9daf8611012e8ffaba1030d767 1902609eecfabcb88c566f1f737acc62b41c4131178eb8a8ff68506e2972875e e536199a16fbc159054a32e48cc24c7f151500e82c2757353cca78df3b72d22e 659e8fa4ee5592b00551f6f3c6e8746e6c05a31a46332717964ffbcf24643b30 594bf8cb04d93a3811fe7bd3d3f1a413775aeaeb28d3b06f7f29617e58e3c3ef 7c9d7a620d47d9512b5dad55a6f4fe98940264b316372b74c0d39a1f7b355663 ce45006b86dd046a5a18e70801360901b0d6174a695418bfef4dd036c55753a1 f199d0a10dcf37bf413f9f8617863577ee4a3b86454e3e4c34833dc51ce8a9cc f65ea29258bd880c7924c54f538b08a9795212830240e606e5d751b3ec8e3b6c 4a4093fcf50674032c280905fdcbd285a626d87b9d077973eaa690ca95c4c71f 2f660dc6c1a14877e7898aa47ec02bdfd278385536fd9fff368f5303f1b82f70 05821ee0f3ba4a2451eb951afd27a624348f9bba4561b1fb9089e974aafd1316 af33925039e6797a7d7663d6c693ecdee23099b60ff22c559635b5aad147814e c3662d59040b9dd071b53edf6e41943332e09c6ea7e4e2ac12062992376e7b9e 67c71e230ff2274b8fa392df01d43ee6958d4e6f07da64410754ec5c5ba52881 e2d574143ce458f5886f24d031c49180b3f78c55309365715ebcc8a3620a7575 63160938651dd3ff9cd282fc5aee479afa13705c7a2c43cf8fbaa286cb216070 e51f74ea5fc1d517a12b69e5451f3f6780c5640e5d590c380f18a3300b935c3f ea8345cf437c679dbdc05fe2bf30dba47b35fe45c5ab2e5b68592c4adb6167ba 893d54c1b4fdca988943c4d51d87f37185139c9cb4f8e3626501e36d6a6c5042 92722b7d3ecd3d5f6a50ee26efb4de32b26545b58d83886da5d09518880a6ac1 59255ac1b983a2a2e1551a8f19b4ce70575d7ecf4970a250e6ce5634e7220a39 e76d98db55a8444e9d82f28903072fa1b9a940298b2a2b892ec198bbf576cd39 0c624da5122c9be9c1f0f3d6cc3caec5eb3c5666d13241be0e7f2e0f8ec303d8 0b5b10255ceecca806ddb8fd3f09448367cc961d2cbb6e544811e5a6adbb7b82 264ab5191f54099261d36b2fa67fbf503042ffad98ded6ac5caeee9e638b5a6f 1ea24d52a43ed3fddf2a4fde7d13fc73c8757e7446cf7e95bcfeb16008895c22 a369d000754b106a32c7105623859a2b44d167ba2662474d64ec90103cae1782 87e6981a1eb1f422b6c482628204998236f01cd4f563d9b883decea4781b65c4 26fd4292f36e22a31a5f17f918364d0fa1af7038b64eaf2c77abc071ff324911 1f7fba3490dede66a8ca0b0f67f069f48943844c22261c69d586cafe9d3f86f3 ff7c7a106db23bf12f87313679605e6b9e0c5364c20da190d5af82b5b6b22fab e33cfd1d5133ba44214fb24911a44deb554830bec522fd27f0d9c3084613f413 6dc71dc6f84a8bcbdef2d72e2fe12f21b29fe1253a015fd95dfbfe755486313d 6ba2e3f0a288699640458bb12023f8b1c6cf01e292f386730a7e04ebb17462ae a1ffb0a73d396083ce3a51fae7a47c10bce4e53f736331ea791393d9149ad791 fdb87a472b97c99a0c6bfa8c8ce93c6327dd796f3d71a3a34c4c81926d6da322 91990deb0c917e2d68ab5177ea86fe68552736ac7b702fb81dd994581f60bea2 093beb91bd66615026efeafe94795e5749797c8339f61f9ee3ae96e8a1a5e0fa d1bbd9eb353fbe9bc9c78fcdb9f49d56d310e287dc698ba37f5943660f2294b4 8342b638e5299018ecacc42e496e7b476d7f35ffd6568600573da43331f36226 0de99f05c276e608427855984b17f6a513383f9f3fc5914d59b85a214dfb85e2 c9013f923e6e641de8a15f21c821c00ce771fa5f36a35d6eb1cf71fb4e633296 a3ad1f8872b82cbf7e2607b5058ae202ed1c4f6b5a963db93c46722295f31479 f82e25aecb0428d36f2cd0ddd00a066b4d1dde1150f0a098c2df2c51003a180e 0b12cb3bcac1c79d798fc51b32de043cba54ead89ca92ebacba60d25c1eeb057 d5a7bf94666cca12b65df7241020dadb44f4321f8a1784a3467c2a2950723465 c439609c1bf47a2dd78f7bc5fec76ea959e61a531ecce3c5132f0470a438ca75 88fc504b3792b714cf1a6f09ea06a61fe625aeb30656061e42d839f0edb32db6 e2564ee229515f37c70214195c39dc123f988a4ac285f53676330739b5622da2 ffea0d4088d7364d36827ffd49a02c5e67d2d1fffda60e2268a361f9e665dc41 b90a124da487e998d333f782e11a2811d686dbb2387fdcdda8a5d03197bcb4d1 de4c52684e022e46d24b2085c15ee9750192427e7a9802e1a36cc1ed2dffbdd7 656f299420103185383c44c5962ec031388ed71a73bb7044d789f2ef6cbaf1c3 970fe375583d4dbcc1c7ee1ab99dbb0c96090524ba6eb8824b1675c15dd88d60 aab8f210d3a0fba5dfacb2d7173f21afa3ee0d6a987478f4feb3ad810c5f6f03 3396e08c2650fe809a12b5f44bd599b2cd4fb31c4f46ef409a49fd84a77a01c7 834731fb9db2e5f2d6e27bd7301e26be7bdb804bf83c1937c3c4161aedc86cb7 f242fc60275cc30369ead9e735952f6d7dd15057595ca6f609dc5d58a4791703 14323f5c93b07305aac7426b3799b4887a475c67dba4765ec8a70fd80606a399 dbab57d98c775867d49d6c52423c0b126804542d411ce9ce25cf3318c4550e13 d3311c559c2b7a814beb3beadaf4222bfb4cac71bcd7983d7217cdaa543228f3 de0667f53f27ec36fc2ae62706e8f1f31ad40b7c41c75ed114bf0e330fe87e2d b69135ff45f3dc2aa4801bc87c3fef26d331bc43ab564b85c13b068af18108fc 661d565d6920d157cde619ef12b6a1c5423c76626316733b2f292d9cfda997e7 a0c95af16260540b15491b134d8edba37295aea89c0100a1d48ed2c2b6d48503 92526a2a4f3a617b69c8f512790a038a7108df70693170aad803110083c50aa3 721bdf17a1d57f5d868618a0ecc1b30e5c7c532d4313586febd088aaf1e6e0a5 b15a677ed4b10725e31febc960832c10fe1aa4fdef17c7689dfe10b4ed11981e c556abb91a48f5bf50334d76d1a57a158b724738b1958de18091dd10c248388b e15542597c42cd9489f86844ee28eff51fedc25230ba60b8cc0e4364bcf91b98 d393f580e3514fb5b11a65053286acb28f27681933179ea9a876b5f8f93b438d 89e823eb75765537b62451caf9716647134188fd01e20fcf5d64c91de0fba39f 3c2aa2372f3c532107f71b749d27c2e6ff868b6284ba697cdd6248a2282f7abb 47cfaaa08546aa6c24db8b82a782231827772af391cece7b743e458a4d2f27b8 022d88759e90a3a7fe32cf5d4a589e2b6945a534e72ab513416410d5e062d0fa b1fd2452b06dd3cf267af2838907ad6e951be731fbcc7948b41d4ab9e2f3e6ec 1e669738dd5d5b4c60d68ea7b83543be705962589858f38abf7c94154778340c 6704174c51e37e606f2f59bb4b0d81e7911a7106f067275bccc9cc728167969f 27c3e5582d3ae50115cc2ed27ec69dee741d220be586a8e4a15bf11061f5199a b3e3cff7095f52fd5d429ae13feb9bb07d950421ce8d6eb5545c9c9f37ad7e98 c4e86321804d5a694a9cb01c250da8cd8ad9ed0018a57f753a9727939ef43636 8747be44c733b2774d99dc0ebbadd1d5a265990f3a89ec700cf2c2bda2432055 f3fb7830410b46dbbba5df560109114c9b93cc53944cf66657bbf67a4a3dab16 211000e8e40923d62ce5d876540ef386e9bf3d3198644fcd5aa5e3e82066424f 2013502249fd648ff99a93f5c1084e6b241d048348cacf5c07ca96323798e31d 3785d7050ec2fab2bdd440022329e791610e489f9a295b71e241730578e270bb b58f968cded07722e0ba9b26a0be633d011797d1a98328b938aeda8a43815466 81b119e54b8a5c218ea177dcbe332564e8f58c22c08965e6a697595b708dd9f2 569f521c520a7bf7c26733a9cafa9f68da5c35b0c66f737e632a1e8789e68dcf 20f86cb06e37a7a714ac5dfec96d1b59babed0d5635e700fe647de211074cb96 7878ef11b7770e557684f57ca6bf19292f8249ecf579320baf1cb189643f0b9e 80b6f868d06af612f5b7607126bce9df1dd538b90bd227074a493bd0f5a3f67e 33cd0f98b0d746b94675d42147c90b1bcafe16bf4234388af6cb8cf5672e372e 96bfdaa51f13ab4b987db32c5f6c2091c930e2141eddea4e71060d78da517726 f04903094be80d8fadbc7adfa9dfa821aec0e7d7c412c43752b1b18fc02b2f59 1e1a145654e2c3a77663dd13c2e83bf42b183438281cc5ee4f9496120dea8405 50c1b066abade654288d38684a982d38a346295559d77e14eb87ab9c951b7300 69 ec3acd8f3a992df54e1f15b013057fcd8577cf4a45e512044b766e923e626d0fe876189d7e8ea2e7cac5f57128328cbe91ccf0d51f21fde01457a61cc3317b0070f2acd6510698da30170716e90e2935d1fa7907bd91f378b137799eb8351509cb05ccdf79590434194a7a108e88f6fafb3d75b2eb3df2f47cdd3569b4a2dc03c372dad81545926eed0178af3ffb6212fa3e61321cc5cb26e201ce1e8d925805261be94b1dae393400dcf00b1c44ee9f07851d93247fc9bf85e2cceb12fafd06714e20631b9798998ae6b52a6800d2200af799e958cb3038d39c18ee1a75cb03c20d806251aa4518fc8c7844d95b2cf483cf1434b9af2979a6f4ccb0eeed220957e810a3e7e552b60da7e3323c84c4fe30b7b2a999178d1849550a582951890be3710874d7e4bf89fbfebae724d2f918da8d97d2739355e6d63444025d54b702ae449b20fb89e8fe750f5b9438802f1e9bb1e0bb393965cb2cf2f9ea7458d20c938e967f333e4930c6342f435bb21e61bb5ff12abb7c9e9f22871499d59d730adfdb68d8a154397faef2dc323cc7e376fab89958f4b34b353004bbb56376680b37759f8ff0c82cce930a25c961b31843f023b0849b426ae7b23174496ef683038b63fd6e21e13218157b13262cba636afe50bf0eb765e4f16c75d9fe3b15a701fd09a4b0d1b440b3757a27b2734abb1d20672db669045977f5599048a173fc00f6b6f342adf44f58d3b4ee3e8e152823d6a437f0c76d7e1071f0d04f59d22d05341783edef13f7ff5e194959a82fc619abe77bbefdebc2051e6bcf36435d61008ae89d3db5445cddde15694f424b1854b1416a4feb0c3802009c43697d272d0fd753be3f2dce8d26d78c2b40fdcbc4eeef558fa3ab074196c91885cf3131b10e2c083fce439c0f28104708919efccb764733badc226247ecb33d91104af688038011e16142ca18ebbf038a158947daf1b3d1fca47ffcc2b33332e6a9eddf3000798de654db9d35aaed0d1a3af4dd687068f751b148a04c2ffe8b22da05e3e706bdf9a59bedb2aca5fa3e9a59bef4dc878bd77be6f1ed56d74aa0a6b63bc9e50f3ce6e26b879da60228ee145a6042954d0d0faebca2d0f6b73cf1594bf64cfd05acbf24eebb31388f45046c2c5c47593b8b36b866a18461bcf483676016c30a0dc27029573c13b7d65cfa54c6f86a014828857f9394380a8a51ec4bbf8c93d90cccb685ddeb68ad1f261ebeeec9fa149c2670546c5753f733271faeff1754a601663bc6b869307c8c2963fc8a69c00ece65d368c4c35a7d0cda244283a6ca0309499f84f272d2a3d7f6b067384a546cd2ba5a55b149644a854a0ca90b3dc5bf0930e0854ec67d3529216386bb2e235ede6e3d74bf24cf68e9eea7cb9c24b0770d0ae3901cc94419f4f1fdcaa8d9010d6ccf1742057337f6cae385abfa4c948401ade2c9c750fd7b369fb8095b6c8f08062847c22916588a1d09d9a6e2f4626b0e6d01ea3e683b2a94aad4da76a3850070e674e8b5ecfdf0fa1cbfe08c147f6c00cd1c68541c61e2fa33d429c84ae4be8f78ab68751e86a8c52351e53ed0e948070aae7004f1f785723a04a2eb34e6c31929e0c4e5a979c402fca9c570681cb505508ccf22e425ea227709fbf2de571f82feddc5b4134a87d7dd322ec38b09800ad32122cbf18967891101eb2f81addf04db2ee7555b1fca520fa94be9c135970720a409a6de46cbe888f6a552e1fa6d851caa474c236c578fa9ca61eabc4f9a060925643a7eb22832a1bb7deb0224761fb34cf2523e445da46c6605080f87040ca4b0cc40969770f2aa7496eae59e60b966e197d70de71a292ae1db4564f6a2086b8e3e0007d40831aeabe66c24dca892c87c9533bdbf21d2d4b559c453774a0147234301ad9bfd0197ec6d473b76c0241fb91bf96bc2a05f361679677ae88e0faf0a61d9559c37f343f51e20e4bc062c4e503ced4a4629019053685ceacc86003e3933e94379a31fe53a1b9b6d460e53da17e78a8b2cb9b6c0f00586f7e2b007d41816a8a21510231f9e9d2588ada66647be1d2c213af7b61fba9d56b7ac8b06fb20e38d3b305ef172efc1e91d5c24a4b3eaea00c1b68cac7d7d60fae1eb4b0c14cfda3819992bc241a75cdf9a88379509c8a20236c23d5a5057b101d453ec0b3f2bdee27693af193efb1e50d9521324689c039a43ed78fbe9c8c4bc9560f901fe6f662e6de41e68cf14f560ee8e1b65eb50750516344c93171d3b482f6df2090435fd61bb07d39f67ce9d6c5b78fd9bfe793a1b0e4b4dd878f6027f2d9dd10b4c2ae3425be3cfd7ea30972a81613d1ee578830a64ed1e2ef7868cdaccfcda06eb23ff4b73a20e91f4c68f82a916993bb82a8cf2b721c3317c3cc31fea16800549b3f614bd5c3023e81a694ffe68ce33eea4e1fef3be64924e558585f4107008402f9da916d79031f7ddf7249db1933c67904fe6429e0406e035cfe9286cf2073b88da10247b65d7d727e4a1511e521512d48ee9b1906321892ccb749fa6b6079b6caa2245173c84b2d470c9fd373b8836ca02f556a3ea76e6948f0ce9c18108d598a7c5da3dce608376200b05ab670569ba252eb7c318a3a3e4459dd28eff0cf82968893cec0c6c0f12ba0e36d534946028e644770ec120e2d7384cc43cae0960ecfd80dffe21059554d729989ce08f098b751d40f1b429a586bc93a4496e0a3a5fec758104de9520476d99dcc262429a8733250f8b250f866a9424bed9110013d404e0db14d6527ac2079f9f92066d97d0e9720bc28dae682daadff430250116e500008dfb0d4a4cae569690feb71209b7990504659af1a42a13918286550c5a9b888aabe583cfadb5550bde04fe54ecfc8775924581a3c1fc62148791b3041f9337f37b7198fff80b088ea9faeecc3acf53145f29149ab7e3ec95573a65055eb06cb6181cce9a18f002bce5fd64a984db9accc86d03a76cff9223a315cc05ab98607a8e35598248ac6c575db40a34c563a8a4d68e55304fe46d486d67d604e21a5338858a67848f1ef886c3243189b90c76a3b594c79fd5e7a7f272aea40ac7e8732a14b456856076d1945cacf6276cbb380b868e6467e4b26f2f1e930d06494483427f976c326eb254db8839f435b4085e873c27748c06232b476498d10defd2fd33b2608032c78dfc2b7d10754344072999626e8717cd2f69209d70460c5cfeaf9e21c96ae308c8c291e20f00b5feb26332004c4b048704a8b334ae2000bfbca362c17f5effd6ba9b654c054836ee23ebf5748b59bd171fd3c7606df60d28671d312911404d4e3738163c13849cc23c99e9a91c053260c9e55ef2a614003a3c1494c2fd54bafbb0d15c74ad13a819efc2b247b071e46a29ecc601ba870bef946ae2d4ac42230313c62f7a8030bf4807832f61a84c914dd1b3f0de6f4c04dc6180943dc5afaebe1db7bdb0c5d683118d6640f407221881df650b5857f307a675254180f353041ac63c64ee9cb6d840c6a16af1b4cdb0a1f32f3848416904c1cd19707d576f5934846bfc502e69c91b6eb9e83e24679349fad17fa2462e061ba8ef7001aa89b7e82289e50d89858c593b928dbc250af8ce6980489ee2af064084c304587739b1de0dd39d9745638a56e60f0cde420611290e68913d27440feeabb6456e2c86a1841a0a689086f184251267666fb8fab321780d2a44ca5403060ff5781471be18f37bba9fb955c53b40a9bb28a88c39b0d7f8069fcc4e890be9c33e68823d9e282c39dfb9480f9a6b8fdbea82baeb49d858c625786ea3ba0a4a7bbc68218bd299aebbf8387e8e3ff98d6503d3384c45c98268f00f67916c01584c2b9ccf4d050ea0ac3d734983125299350d427eb10afb87896c6e7dc8970d812b0397ce2069d93dd093e99571cd82f6586818cdc8efb5ec23933be8ebb709a780ae021d2c145e9aac227b1f4de2806f38fc4b75e624954fa2eb908fad13073c1caf957b60e713d976eaab4cea02ca45c2bd7201bb0c77fc4b89cb791eda045e33e2c30f2406de039d7c9cc71bba4cefdf23dd932d7b13cd1b1d674cb14e01708205cfa9d341f8b726170f2ee1913fbdf8a47bdd7c2bfcd8b73fe6f43caf0e6563ec16780c204c6b876880706ba529027086604fc20833e09780f764d03504aa01bb7041432206f16c82b4e0964c708d26c9a67381601510d70d8c71537d0b81c03a5782979b13ea1fc46073dd97d23f6afb7ed0bc64496e8cdef9a461620a150061c7d28d2c360140eb1ea4490a319355ec81061a3aa65dfeb9c60d58600873641c1b39c05bc035c323e4dace6dab5c4dd1b2999aff90d7f27442cee4e70ce73c51300f9883f98ac0343323c69e425a17246b4a40d247ce1b11aac1d50c031ec830b06ac9f19a8bdf4628e0658723215feea6fe730ae0c5bb9b2ed0faef0514dc53c1e91ef4b561f547385b7639de1a074c277406ffd7f2d7964dd2f9710fc07a0e202a07e193856cfef1cac395ca9003cba821b2c337eb9e12cb4e4d870c3d256208dd45c8d9c72d023181923203971ea519d233a759e14a07d0bc3ed60345a2c5be32f7027607dc83f03e644426b60858f7c3e23253309ef2699b07ea0699a8f46b58aa15c46e4e854093a62a8d474c33e17c142e6e167aeb15c830570b028279459a8ed629338291c222b67c4f2975fec98649adf9c6d343728bb2a7047142926764c46c0df4d8d89326b1a82cd6fb14dd2a173dff2d793ccbd115b3010f9e6a356be145484238346f4c3f4b8504eee276868dc92458b6b74f95868500a9180346216305356989e87984a0d59ee92c2c1528b0f1d6020282110847aa00d9c73ead3198e3328e98fa7bf027fbd4250e891bfefb0f42b3569265032bb403f4db71c234646e13d62c2bd35e8ce1f0cd1d0e1f043aeed0161017d16441900039d96148b4f8ec378ae163d433c0de64dad415650b8936d4363e9ee3cfc4f4087d4dac7d52562637c842cceab65afe7b81a07eecd56765f09375b8c2b3563e0e30804d149df5805a1093ec953f8bd0c8213e5aedb9e7ee572700280364ee380c2846a03fceb0b920d70067cf662d186436cc3128736661d2beb57f4ef573410f8adf13d6ab7d07ba2fcb24a38871bbb1d472fc2b1cbe93fb38547ac5e4780e0ba18aa0e499934b28fb89ec39c0abf1e041739cc5f568d2e45d86f1ec641cd107c6c5f5258b3c1952480e485e71c25401e5225473198a6a2e6270e3d4186c3f076d098e1b005890b5815eb8c56ecb33983033ba57f1f84f33f55bed9a9b37de004f3060e0cae079e3d7b8642a67411e898bb2e44db47c7ed4176bf5aaef77c703dc70a1d2b3d735f1e78f19ef867eb355f3419030798847a0afb5ecbb56d926037a65483fb22b8561221d1c9794b01abe3bf2d7fc15f90279ba4a0d66cd1f23032ef0efc8772e5851908a126d7ea39371ed2670ef0943415e5c41769cd950ec0c3c6212e2898dc1d4caa645aa0bbad911411d6f0c0240dcee59422ee5f493f10713a623d85845330de1da42cf4bcb84f3068d7e9a71e6390e540d87a0e4dd18071e78d0cd3b9ff36aedb82151607bf1903672165dd76bcd87192e6192f2e0a000e1c2a55f7c29e17537edb7ca5fac2ff543bc5ccba7612bb7560746be7db79e0eaca3e851e5e02e4cf8f6368f82d7148d90eb05591d4adf9607df293c188d49010a7a0d7c93ba87e0a23b1762fa0331e054433c0bfc3fdcedc749f5cee48dfa0d936647ef4698f457761c7dafaba142f17251ad6aeee9c493a060143aa8183405c8e89ac68b4c452a9ac1047a4a6e0a4db2cb4c0def0774a8d2499d03c3a30c05635677628b68c666625809b6d8ea15dc78d2079a4cd1d757219633a0733511062d60bdcb35805d9ebbbfa174ea4d2885f69c90bd6cb280e5de0dc5ff77bef70607ccc403bb7863b0899ef3b53cb766e17859ccd2665165fe72ceed46de416d0864a35fba1ecc39ac4e49a04b161d9a873178b7631c34c681237e86ac3ab76b04b32e8e4ea522d845cebdd4068343cf04a02d0173bde7ad3b29ab21545ff8a20cfda2f6ce4491d217eeaf60135e3a0bea24162cc9fcb4a2ddb23786f5c13bbf09982d2a4ec54f7d60125dfaf6349d72e2c6ab2fe3a32f37a11e7de42c500a31041de0e1914c690b92c3f5324501e60b2e61324f8e7091fedc7c68039232950503ba0c235525f34ce5cdd16afda7a533241054d529477676dd268397731e0e6e0b75876a8c0e6420b57c22fbb2a935b3762d1ec5e3353168a13fdf92644f3fde000817fc29a9378e0dededef50bf8cc8dd26b3e99312158c9c332157edf2efa003f7ed9bf879fd8e702aa40e57ea3af82704cb34f94eeff9784c99ef87c1e5a3006338db21d032774a699b668dfea5f67bcab6ea497850bd47ea19ef824227130dc35b04ea4d62418bb0a2316b35e6f3f9fca6134770ba19c764cca012d12cb408d0f57b92f5901aff66b2740bc0d0ef372918e7d6d47ffbbdca1eff037468f0047ae9dcbd9639adc493fd731eb580a9bde73dfc219fa5261b99b645e395b50f0f67585df77667a2c916ea27e2f4b109b63b3bcca1ac00d6f0064ed498f0cb600e6e5cf3cad53f172be585f8510289e6ad8e2f38cc6236cdfd2c1a42018e23c80876554589cf41495096f14f1661f9bc9460a77a69b6bd2f34e38335751e4e3a0c5f23695cc0ef26c8b58a9db1b77b782c1c31b2fd7c81f7ae921581d7c8aba50153ecdccfd240fa27a4cd3b7f72256c2f9e7d8e6d72afe49931f2619753b9a9048c57923e5b3c260ef1b4f85f819f942e0c0be341c20218877a8e85f58c820a0329d04ae5dddb849bc19c54543a084fa74443e912c0a1b1bedebe126fd169be0a93ae3bdf69384571f9365024de9d77b5526a10f6f4f663c09a40f7690cb815033a7ea594356b07b6e7a9777852e55de23be68377f4c3259f01a14ce82da0a80f6a0e586ef649f319b2bc42bb1bdcd3e9747a3ace381e6d1cdf17994d9d828f0df2def8bc857d91e4a91a6a9054ca8d93b12692f07b956bcd6fa6ea1ca064aa0a6320a4489cf75070a3be9b85eaf9ffbadad2abf489ef5d3478c23f62d60b330dfbdd33a6987aeb7c657b90980dff567cbe00416dcff4e5647c953c5bd3abe60857e82aa2117905e81d306c5007973aa9d363f425235d2143f3f23acfb70e3303e9ad9888a580e146a897ab04bed8297752c19fdd65518c998ede4531f748e5096e34fce0e57f9efce35592ccaa2f16a19e6f545109a42a090ff91ef818d0f005f7d5f685817682a40c70c1608794d8cb52b33c7f35331baf5db7420da07536015cffd83a8eafd216467b7283e6f427078c7d04b7a229e96c348afeb1c62c2107c290d92e73f63576ea48d791bce9438dea578709efbd9052933021c812339d07f511d524766528dff8423ed4c39af5d29a6463d4976eb89b1270a593caa80f0934e088ca31b3f1aaa86897d6fcb25918b50b7dd7e4043711b447df4f1bd0ae07da6cc609ca5459bf38b35c7cf4bc7477ca70a7f7a8437e50d84bcf72e972a40bb51d78ea24ccc0b8810fc34e4447ad00e73ffb86d81af0f46740a848c9a0f604e3c81243d7554ab1d3fe21c1dddc799e93e73a962ce9e22d98f6c4532a09a307959a348a2f28610c16445c8ec96f8f469d6c98948d5a6a8a6dccc0829c06a007820d8103533d00db99cf403e46a583de1e46a3ed1a23214c6b5901c8a4e51d0f99178194cb1b244c92f2faf4511a8a69c07fdef5fdabe2b9ded48e501b639109e93af1a22b10bf5b3f5c036f4e49a5856dfa2f4295d3e81980c24c8223dcb703c96ca1651fc4ebe855c235d7eceade6932ea25d2cee230660d494518efbe060e605d1231261888e7c950cacf3ac3abf8f29aff0948b38ee0feb7d288d060cc0d1561e4510058b33e17f4a11913d0049683040689bd65168b093a20eb720298043b216475d6a17f195a39122b165da3a8fc675f648a74b90c66ee937b6afb5804023717a792c77bcdb0f633e7d0bb4b7f69377fb80bb923234c9bb066a969af050a6db4549eb19d9cb12995e93faae67205aaa77e9beb6d87807a93136bbd020fefb7b2c1ada259bad481255255651697d68fdc0ad6169deb022ee16f32c60607e593c202ccd0fd198a907a9f0666f7ec7a8f5006715e8d13a43a0d84149f21099cab1c80c9149112bd1c9c8abd842594c99351d9eb8bce0350bd356a689bac0e68863d218419b5e5ee434833c517db618249804adf8e45142fcc4ba283dc590c741f177ced8c857cf70136ff2006703c09fe9340bc4885c095ad7e1c11abcd04780cfbdfbdc48d893437a64c3b348911eb7505febe9d0b9096c40311518c0208b3f587c56aeb9f670114ded8bba027821230732c185af45fac658d9d47440108e69b72236e8407ff9fa01a47f25693a41564c0f6eb9ec9b1a90bcef1e9e6200597eeaef690577d82d2f026cc3aad22f55112a31d165fbfb2a78067124be7ea0f39116694fba8597e90bb18a7b3e05956cc9542c29e38bd7b1433b55463a906050d147d3569fd6dd091a5853855160a6240d9d455141bfa7201387fb4e19657002b7d218c154595757d94b33e883e62947033d197bd50524e796a97471e1feb06888cd414baf3a7dca4791ed5040ad5e5a9e924c979cb58e2d4a2f9296795a4018f7e75762c9dfd8fbe220b41b491aa6acbe16b9afefdd2fd26bcd64c2cb7b7054c5dde72694e032d011d403782a54aee2caa3503f9388756ac8234d95982840af6db193f5dbb10d02804521286dd04fbaa78931823b8c48798e9a30a52366a0da9ba85bd222769b5edc9f3ada37d7ca1051e08c736ff4a637c3ca81ebafd5b0be6014380d3bab1e72c2e82c846efd61e7da6db148821a25f236b489d726b6e0cd511c33e31175f68a6a5f4f690316d0ba3bbac2f0fc273d590c41f377aaeec0260e53411f80ba4a999c36a5f4c577db1bd1672e1d6f5c2a943cced72ecd15e0e12a4802a5f1480681da2a4fd2da7d6f6fc42683836142462a0c52daefdcca30da6d98991274b5524104bc1371cfa8bbca0a1d72bf6304d31eaaf9d78707fe900fdfa1a7f32da871a15d80b03c89d501fafb075d323ecfae02824f33ad056ce0349aca87ced498313faa97be33ad1ce3fa9b84ebdad0096d20b78ad6ce1ca7701ed6d215d18259593481f959ecd79c7ac4724b87a818606c97a4fa9cf306cd10f9329d140f63f3a30934f411a96e3e0106508fa3f169df705cd7599c14b91c902fb5e3a2947fc7497f86a3f6ee08675d0164c838c010d1c5b3fb93b5d45904e034960048aaf1bb9574539272be178bb0184d9c722a781614f56a674e5f95beb0d80c337327076105037c829e24650b92c128cdf6bf360c6f595d7fd7eb8028a083a2324b5529bfde8d966451da5e36ae52d1d49b6108536d25bfff0ceb81e1300c5d85b1528882e0d771cdfb70d1f57f588e8e6d8380a24dfda05876243efc100cbeeb3a4290cff8347ea345e8251aeb5c5ad854c007953d66cba6da20b0b6908c459ed24e6375ca2d7a714ffff0b19c2063b1920aebee69370cf02f44ce422033af8c8d7b5cd634493611bd908e9d52547e93bcd1f3e6e3be4d6912748bc6a01f7c93b0efd27f68de17951f8037c3d191ea6146ea52edfeedcf51c7d699a2104371df65821f3ffa7a5a892613fd1d18b84f406b8d2b0c607269226a488423900cf00a36bfe77d0979f07dd6d46b6f82cbb86bb8bd79185fec59575d3aa3166034b9c99985bc23efb5ec0aba9bf8a502f77dd11d0892796f60eca9424dba9250caca45511f49851f4f9297a5ae9a64542aef477e3c94b868cd2dc8d5a3f337706d967ec3ef48c5188e203d3019047e4f1009561ad156f57202dc1196ad621c80db973095f48ff4e3364d81cb9d14f78115237fda1a72c7c311427af4f55aa0202ef78db049d20794a030568975f16cc745b2ae61a8371cc057b014829ac92ff0ca0690d251072d87cf612f60449e4500bab0dfc5295c3644a706b56555d398d07657fda07ccd87835227e31a95972e3b79d5800c8187b26438c911b84fd861004dd439e01f11d775ab9bec5feeb4a6d457db56dc8014f6b12d619b1742c164a0e53fe7df4c84fa64fa63b5e8fc7304a685eb325fdc000e1a5c00972d73f45300fb7e11e7ddc58fb21c4c9b480fd902d8b463a9257431eb15f45ccb7fb8fa01206de80462477c344df4cc6b36e06f11d5d63ec6f3972d031c6f98c0022ab68b301d0f08b0b7e20ba32dbf24ea0c5d85571c7694eae1694d62838f17e1215e7300e65e150282e959d505cd84f556dd931298339981d9c9ab3ef82f1279dc6072f0c2968dabc5785d972a3919bbe64a647a3450ae418aa8c7a645473b380ff1e1c0ba489f57d263e611a6eccbea135a4fc8d94c5bb0d4c4a046298b3d17989151b06b24eab1ee27f83ce529cf098763af8ac41dd95b170851e910adb45910da9f00662a73e0515c82b2123faf944a8f83eb8edad3738a85d4161f057559e477edb063b973fbcd8fb7e8245eda525dcc88dca15d5eaf892b02b03d5be2d33e8328c0c3f2827b7c711ba835424a4c530105dfd4c33152df4c78fe9213930e526a2e407fa445669c0f54a30180fe5f382751fdcd340959851ac18c5d50b0b1e24ee400a4e13fd437bf84a9191cbc5de505bf7a2dc086f109fc2d2d25f4a9ef8ee723309c68542cec0e1473c1204e78a099cc7937575bb3e725d14f9da148280bb9aa8063f528634925047f019c5df5d81e580682a2ef25aa68bd1bbd01e32b0594be70fb7e23789d1ea98b8f85a27163c8d15f9fdfb1ddbc28d638ac9c6b4909f70a70e49f73caf557cd0f1dc731c0baf6897239fbcc01e552c347580164581df6e9a09846c6d5a9e71e4ed3f15e3c22212026f70f69c7508116d129034d3f1fe96380b43acdb801b422e37c26ae83490111d54c4f2f10e1bf1293466c924f1a4af58009079d950b77cc482596b2789b07a3c84bd483bdf63d1a9c0f2d2c8d5aced4f060f5ab4497c21b1a4f0fff38b56029ec0db3c5e462b03dc07af38dd157080140112f929397b37022f79d4dee0435d64fe22f93f32824db658938bf80cc24a320f0d018d7064ae92fb3ca26c8025f226c6b2eaa95e4cff7ca81be04534214f750ddd3a4c258a59a85740897e5b7ac8596c10dfb97903b8bb7547d0c7ecb0d6dc0c6f91f90f6bb252d6ab38d2c4522c83a8a95b4d8e6a78ea12f8dc9b5503f8e6006b5f34481dfbdee6f1fceec18b16f405254b59a5091d9d0782cb379e4651d10dbc98ed0657aed24f4c64338f27f32bd445b59f2580bb56e7a97efe0840298b091b89169c4649153e72ae5a5952c3ad8c03761a894e097b079ca4f3e2c2f09d0485694646a211d8f9377d7d2f47a032c7a05253c9d684c27f0c59e7ab534d7a01e666c51e05cee7f462c9a4a560b599687869e245c5130b643295649e4aca9807bfde5fbb76afb4592b80abcc1de52827f119af385148e189483c38cfbfa6c9022802c541aad77b6f1a1fadedd7674ee895a6cde2d0950060f4cdf79cb500180d7a182f9378cdc2875760c5eb84b7f31c0597aeaaedf9432f6eb466cad3048706c1d076c0c5198f6a6b004ebfecc316668d0b70234ecda349e6bfcf8635e7df0d3540778079e807126e73958fb9b60645a2e6f3358484b38c5d9402bc45108a00e32a2242ce5db6404ddaeb79f7c8a7bcd51857cf2c3b89d6dc39a7be9e5b2c02a3d50025652e97398e3c7a4a522700dde46c8f2bcd94d8fd392149f1b1b15a05631a28ebf07bb9fd83c0be05fbade1eafea4aa468623f31175b6bb7f889e62058a3d3228143089bc8cdebd86b223fe7ffbae2dfe30443982216bc4b83a2fdd00abe86eb98639094b3dd8471f7527c61900b183be43aa4baf07f35ddc2cd8a20766f64de0e0bcb787403be1967766bde7a44dd6280c2cf9ae650afcd41ee68808d733893ef3b5513f0039a6d4ded0c51e0cd68c4a72b6101571c3774f7b1a690ab110b78b4a3145ffe94b6dafddfd73980b6fafcd9867c60c8b5d162d0f81a30c8515f6349649cc970723b2bebf31e511bf106d52eecf37a772bf360208ec3c07869327ceca72ddd4a56f16d4d3fc8d52a6041702f6b96cb40ce3cb71f226bf0c14a45847006a490bb78e869d3aaf6a419208994634ae68dac4da804e6b3684079f3de3a33fd029b8c22c1d403d2d3ecec994e21da33409cc10eb5a4ff9d71f09492daddb0c96b50ce24dd3ca67acf3ee4eb1a06bba910405275b8261bab72605cdfdddc1b5f471116976804af5361c9cecfe2f111b697453c994038cda7ea507f3639b094f6a893d7d3dbd563b9ae5ed7730a8e2cc882025760d731babac63091a87a5b18d543a9db8eef93df338acacede5cfcf4055e20a27f893b970242f0a9c847ae9ffe796e5cd9ed18593b5ec54ed4ecdda29166362596967f8b9a26707419ea52d41a5f2ebfb2d3db13e4e376680171a0831ed413e9b2fbdce5072350a3269a21fd22d987d283626bfc6f875854915c03348bb729ac89d02aba9241f09683e0225d80667e261c623617b37e0cd0755b8f6394617b9cf20eb50d323cc0f2395e1c150167768c2d39d52b6b2786a456d34218430374ebea428498a74300a2e3def041eda351262d95611bfcff9fb9ac57e982b163293ef834952c36c2506f1476f7eba0fda54d3aa92d413e13a82d7aa221756458125340ce83ec16a460327100dfa0910c58339c0ccd2db3633d8e34d0e053233d87263c17d2800cd2e08bb923f21afe50007f3426013fd78172176ff3131b79cbdc485c079838ac754089e7a0d3a78b035f10a16ad7f0ed86eb19495cf14d0bd3095f88e3ed17f299c07c1e4e56bcb55d21b2c27c2a9f538dda9e0b03e9be260aed6916046e24da3db06add46621482b9f7a6017a6938ad53d79b5d06c406ba653fdcb1252d69607250ede250dc9ebd705c56a519b8bea0787d5b19bd772641cfe54afdd6bdca5a82800b9d37cb510125285811e5267b555d2bd92c2b9c6113ccaa40c48278320a3cc010ba1746accbbc29ab64c8acd7295ca0a85d5a1cf7cf2f7dee39f2a6f33a12902c852541ba51e1086a05877441065409a4f5d6ebce45ab4d04597dedbaa66ed0842278c9e1495a6737f548f0e60513ef87cbd5e83a999366a65034a837f23f70d9a460c66db3b581ba4f1cad1f12567abd79a611f3a8e8abbb3c6691afd9c1f0b12a5e895e84009895741904574b6462e2e4409b840195b1c9ee3e336d32f0e0a9b297dbd8e6fa1ce0c18d016f15ed56c89d4e4d232decd24d479db5310524a06c271289de8bb53e1f4036235d750eea842f8b7108868dfe7de4589d58af0030c69bf749c3ad8a6904205bfec68b70042421ac8969d0be2d3d365d094999b0b00d35f321899a5feb5d46354efd8299572324dc9d53dcf89009774fe8168fce401b154c92a84c111d621c48c7e8f2a76344ada32a399abe8b818ba413397b85d0fb51aa8af10474f834a1f30cd427db59f3d7b748776bc10e841878e8289336c0d477d356c50cecd0f07844f5d4d064bd9319b2e3f57c0b522820f03f8c0b29e027262bcd2a0177869ca9953e948725cee62886e8b8f66c6e17405529bcc85580ff46065b2718ebf67b6370d0e1d1ebf8a39169d16e9e0d511626c543f08c49000f240a171460f5efa88123eecb7d4492fb5e47b25f64d2bf116631358956f960b456a8af53c43b353c6a490846e003463cc85a848d3f40a8ce1bf48a9d914440e81d49e9c68a1f37fa45d30683d3ceba1cdb6b5d78e6d1bbb666d555d29e85c0e323f3d7fd990490ca7c3a4a76c4648f972e6c0f2f6def30c138f38133d45dd0928b5ba8b1245afdd7cbdae48f35a78f5ee530a8e78c79b41e72c283e277bb60f491214db93017e284a85497b6f6230889067a4d010b1a758afa9e4d7b4694a01cfbe9df0d79d403a0c2bb70c7f196697a6ea307e2ec8804618b080a28354c70fcdecef45ab423052d18ad53f608de76438073fe90bb424ede8e1fde0bafed105864d0dcd1f03e180a844ead462e67f3ed2cc8064fd66c872f2b4982827b82e0282a8f2969fdf79acce7715e5e4071f01f5e8450ab526ab2be63a69bdc7b4b00508d6f30532d0a41036893706d178eba5d351c0cb937ba0131e50d821e49e4d09076d479be6696130a4b267fd9737b088b174b1b5d81eb2927ac6500ed2960509ffcd43a3c96ba6fcfe8b412bca27a8ba5229887b5e7350a43b3312595f68bd0038cf35acdcf53bf246cc14ed52dd481ad2b7f6c9d6896496a9c228ea440e52024c5ad8c0cd0f338d084da20d33569c3cc819ab703c6247243190bbb24645100322953f07e891894f1106fd501be3f6ec8e61476df5d0a2e0e40f32b9b5d1150bb833da68bb63e46a98c13d7c01340fb2000ed25c98b53a23097651008e255009424063e382e0adc9351ed5530c99b2a074a7e7155cd43d781a3a22f502a2ba000e8527decb9cf254450449686a4ecbb0b574b37355cb53850c20f1111d48b10f3d5a8e284899b88e6b5f39f744c41e7fcf9c0f4815045624626015bd2873110d4d07b868209a3f8b5ba518f814a8ec889d9a6ff720f33ff4852237435bcdb2062f9bec558e611fbf81bd461dff64df2e3b349bbfe73af651c2f32a994d5a9d0d35cc65decb590d78fcf51843e3c16797e3f22ec4fed7d81299728259d3ee4602dd6246be985cf846d4e3b8b07e79912fb89d66c2ce5f3f4d97b33938944f2a01127376f9938eaf0a19bd138614d8e035004984a9e9e9763cfa1d53bc17e4d802481dc034231785c9b5df30b650972c204e8f2e51082046ba299fb601053b2a04ab93d0a66a1e160fe4083776902d99bbece6b915fb80a3d79911387eecfb5009a6a52affc0c61dd8cb1eb440c8f2755b1438d84f0d965e63bc858c098cf7bc0996305fd077e95326419eda04231c219ae7679f52424bdd58bacc315994a76f03d3c6f1c98eae24719acd88125a0124888873ad91ec2d77df04c8d94e5d80cf03ce21bcd963292711df27a379f9797f45251c2ac2f18a150316a9cdcef83bc509e1412b2673060a777a0c5eb901992a79e7f03dd61f1edd9097da5c4d2bd60503d5cda1ff74a55a66337a6bbacfa836bb8997fee7f3d48cb42b72655a61c90401fb4901513531bfa7f2bd96482de3995f94d1ecabd5de8dba046d187ad24f4904df43ce3c9175a44deee403fb5316905428ce549dcb552bde73fd607c06cbdd0cbd61b246b8a475ec17ed710919ff314cd824a20abf1b2da13f1ef6b001fe900e087b3081d1ca9cf22a67a456e4d7d91366751cd102c46ae34eb90ff8d44fbb0f9b11a00ee554650f0b6b682f1c3326d51a7cd84650e3080c3db0dd51175bed01f64dd99210cc3e8eab1f3ce6519b3e4d9c78ab2d8527166dd1ea8033338a820525233d8c537a5a0da1bb3164508f4091971ebec259baee601eb350a6f91090057d46c05c52d7443a4f1c6163076d2e1ffbf16f9cc86031a809f90c70367de503636c06db19ccf042fa48adad7e9658a712086c2faf5f2bc9a07fa3d7cf0ecf0b43927d67245a748fbff6e222c3a8d5afe6d4ad16ac55aa1e6157c1bad9015e05e7baf567e577e098599397eb7e823075f8dce16edb7004be94406b0df04d420e59315096ac2f28f674453e33c6aedf68515e0b6dcde5dfefc7fae7009ac8e90ece8020009c9b1078ec0697b2cc624db7165a7fd9a9ba0b59347abb956b6d9e0db41d7d0030d0a13bdca7a0fdb2e6a3d5737d2bb0bd1d4686893989dc18d3250a67fcc2040cd39cba9822f38fb540977ec6f8164dfd7f293cd49d9eec5e75100c07baaa86805fd5acf5ac3a9884c89acf89cf58768cd9ad5d4570706938ad5104ae32f67687b80a8961bb9ec7148e1eeca527ff61fb9838e97b6dc634f231660ceb1ea9d03b81846377a41553268fe3d2c048f5c333e5497505c0540e7afb62052a006de3ea7bfa7445873ba1733df1b9e85ff71b205698ec83ece56b1ddabb0737ed6796afee762302e714f3be2b311f10a0d72a4b358107c4951ed58c7a830f05c5719e7948dc35daa78dd72044aefc9a4973de00b37c92b984cf23b2372d0ff4baedfe217ed464c3232173f9e65e0593693d8446552d2e3ee1fc3c0e8e4205a7a55983e5e09835e9f5d90c6d7bb8ccf001af538febf9264ca7d6c6390351028ee241e4618896273a775f5d62994b21f274d6f605c2da4dea2abb477ee8e20fe44ff0a32dd39b8afa67a53c80eb9ed68e98db4f76c9be3d68e7bdb25924fd0fe6903775b4e7b4a3a33f5a2837b2d44e1975c0913a440680d091b578b8c4c50e04f352e2b8dbbdee4723599697ff9db3d2cb99f8a7d80769899af59e8f514f01d2fe66c2c60a0ec368de8d0992cf88aeb1a1874e065f81a70658be0e9267f80b2066bdec0602cdc1d8670c38fee63853e7419a1768bbd0c6b20583ce7541480633064a6ad1953b713d0b68938e565d14c671115d11b833565a96dafd54633c00958d21563e2e4b532769b2af3b959dc5cfa1b874af94017ecc8d859b6ded3d05aefac665e2ac21c489db4b1a4480c1ceb0aea17727d13f941537abe22d523f01358306e45fecd6e9f22ce22c289d67c585a2a3ac71660ce2c8088b6cad887c07017802b60d27214ac9bffec5436d0dcfeee97c066149d84f084bf42cbbdc8c0f83d90a478af2b41f27f74d9899b16986f8e1990784f80d75b181c8af7cce410d16be831ef8bc7a2e66edc5d9f53697168b00ef8cd2d38598529203e01ad9db047d13a6a85fe2d7e94c47ed92d7798f2c827ab8b89f77215122e185f39fe07f002df50befda0b362d17c0b8894456a5574986fc3c1b9a60304e535a555e1cdf0922d997dfb00ae46e78d6b0da4e96dfe547b3bdc0263b1e3eb73424c779245409f3bc065bb2037c74837135f70a93b2838b246a348b43fe5c499c94a8d4a1790cbd41dda0ce5ca637bcd2fb90c71dc98029448ae0eebf5e689b5d8302af91cf08192945c240417ae12819f9e3759cd03beeb207b3d6b09b400cf9d541ea73f500499100793fbdf9d8b77d35d80eb995b39bc8da9dd91632dc5f0be60d5a93370ceff3781922ffa7be8c7dfb5494394afccee0fd18a9249e32856b177be8b6af0d367ad3730a491ae85cb3b1197199827c86ff778317023912b11ec33ce4f586080e183552ce9290ebbe03a6f3cfd8bd055517eec7b4ff2b94f587ed1edc281301ea020c3b642ed38bf1f299b6249a33239da5231c1f6c9f10602b4251afbc8a0195efddbef7a0261bbbc737499740c9e6d7861fe60a1ea6f972653b8f50982f0d1fdeb046c126fe27d4fe56acc91030369e855bdd6e983d81fa636fc29a6d5708226d63907c2237696f1aed31d7cba54504144311cba26611bfe75d4f8797a90d0a500f4eb38840679cf6ba83a3a61fb8f343dad2b3b0b823e1d911b0f395f502ca907e9e03e846d3e82c9b9cea205ece90420a07f0021b5886afa248be86740c947660e0e9157d34cabeab6f330b474e6b14ecedb70bafb25a9605a1b4d8a30e2998d44bab62d6fb75ccd74db84ac8b0548aaf1b667b6947dac2a55e92b63c0cc344cc2d39c5a34a6dd376c7b50ec78f1da1b39ca469e8ae47559877d5302f06542bc685e7eb10d9b029dd49f89cc2e357d32c794466e513b093c1e532802e0f8b6653a7bd809a71a92c8ea60f8f24238e0d17045336ce7c866714efe6fdab0e48dd7ebeb3d4522ef6b93d52392c885a45c8f6ca61dec973c21d3add421fae02 -generate_ring_signature 4b99442b09d30a94e40a661547b075704ff7076e432907efa34f28fbf8f98c3a 8c817e1ac218112b22b9f47fd1d7de82f36e48900926febdb32adcfc4e4503e1 2 3596cb2eedbe31599dd34e5577a038571b60800da674802f977182ddebf3cefb b290773d661bfaa5cc2d1db49b3633963e714e9c1f3cc8d843fb7f9e57e199a8 1452ad276c1cff34d8ed225e848e5f61080b131f2500d6bd06b7e85f85d08106 1 1f88679a29e541d1794d3a1691939d298f52462fe6129db19615d99230dede09b53919725baa2fa810c86929c2ddb6f5010a52a79ea98c7564538ce4dc6da805e5f8022c00ad401fd9c25c3c1b7073be1c489440cc66ab235d4122f804210105b594bb10b7e0469519a6db00c246c091ca4768f5b5973503b825c06fd773210f -generate_ring_signature 308bdb322aa460f8c564d2134e23833f4e567150599cc3cb027083c5172a9ea4 6025c2ce5f391b371ceab7d0e0a0718eca1a660603056c887e0117f0c838737e 72 2c78fa1ff09188a0c4c26871aa125e9078025ac1e49d77593ca0dfa1989421a6 4dfd23fb69675f8836f93cda1dca1f2c55b68a16435d8ed3902c45709f11ce2f 9562994d83657b04fc8a26ec25ec00dd6c2650781d81e866a1e4ebf78e0ee80c f5d9c7e8ad13a830598f6f587c48910b3cd4b8d338c90a3194f3cc6b8c0a1a89 515aef296c04dc838810cd2dcfbf8f4f42ebfe4942679929cd3247f333a7734b 0f0f309eacd4b8ac55489aafba5cacd746a1643399130d56ddfc77e826feebaa 464b240b5fa690508441a556a6e7043451d269ba3fd38658a46dcd78d449b781 662cf10b9f6900077f61ef37bf3b21a4b42e0d65ecabfcd5c593ec23da6ef016 48095d922127ad39050e52130b408147dced83e557b81f237e67864e68e2103f 55916c66cb8086f42769ccf831cd4356d31eb6c3411db78d8b8a8e5d7e26849c b72e843b6a252e3d6cb4f98bccc29bc494779c107d385cb52fc9d90fdbbdd43d a9264eb06d803e5d7c9f5054678d649d87dbfc7a2c826a2346e6ac929b55fc48 df7e5cb855d1dd60f6efd88d00d9439c92fde3b71567cb46aef17c76fe53e51b 5c3b976629247f7f10f4c6d6c52846b9b4de1d7a258d84ee0811d0a417939e98 55a366725e671d0136fd0b711fdce650b77559adf56753f07c4130f18a36f024 c205931d4633f04b17c931fb00f2b47b8b7fe2c9de6def25496ebe5f61640bca c3baf879f8a858219e26c7983a5cc8618af19acaf1871fdecdaba355d6ed48d3 5fb47938e220e2eca446595c8396c75c28d0e180c2b3b6c3b4d9c9c1593d3ee5 ba26eded826bab30fba456ea53f5c34c97a1b5a985dccfaf6718d1231db6bf50 d5c01cedcbd2bdde6d37011766d8a864ca0f53943501cc0ab3933a86ac0a530f 434dd5d7e49617695b786c2ed3e851b1b76a5140369e2a390168d21cb99864e2 f87c29400d724f5c0c8e4e48049538035a4eec610a74d2730ed76176efbd84b1 ff55b2cd5d13d4d8da692ca9a0d1af104e1df3414e4b0b1821a56ccf676d3562 0cd6a4a67367d58f553ed01253582a17d8c85d8434b790fe0d79157e8b7a3fd4 27f75211c127aa1f68a3d22dcd83200fc3916cd1c7951e538600a31d19202599 3a23e03dc653bdc805cad9a038cdec71a0bb2bcb6de17e2db4467df3639ca7ee a1b76ca66834623b4a2445c7f369cedb1b2c0e884f938598bdadd245822ae52d 57e14ac6cf4de2ba8f4a4fa39131a40ff02398b2a2217a0747b4645c5eeb5703 0bd34d6cc73e4824f3b96cdc68b939fcbb3f3d64f85db0e2f404189d39e1e3e1 abc73c606cffe36f175a81afd69e3bdd7a6d534aa8da4541aebae0c5d78ff2a6 23eec063528ed18f12792077cda4ddcd7623b8ad7a8d9f194c0b25495d6d774f de751896b52bc0803437ec63efe0f68e6c25b17211373a6c083e9233cecd4c2f 88be6f30459ff0a926a4d7f57b9a71b55250e9387208b19b235f4e69511ce887 d64854d2a0eedab738b9155b41b30d425512d2165fdf68b60963e6586f0a75f8 29a1235869a8d737511ef31916570692eaadc9fc5a836419d91157959bc7408d 0cfe56ba540437788376011d248c5b0addffd76c8f183e1798a81a05995eca77 1d14bbdc17e017020d8c6696a1780ed8a92c1d25192da8ca83a0835ae6ceeecc 603114e5f9220594cd088b81a0cfe4b54fd73e0ef821e478fa1b3ce7dceb32ac a0fc34ac08c99f31ed89e5e310ff55208bc3fa2b53a750383992d460c2a12dde 83ed317fa3bcfcd2d5119593bd6e24db04b15669af5384d08d1bf32ca220a60a edb90d89c6618a866613546e755fb50ae469b95fc7148baddae72f531090e1c9 d29ec006b1a758c0cc9c6d3d9f2493295c574f90d6c6ee53840ad28c629fba2a 1e8eaaca2c4a8ff4f4dc13490237a0a97f061741c0b06dc5ce2a6acff153bf94 68df50cb1ef38e0ae678b4d449ead853d4845db7c1d41fad198cf2aecc594d13 e01e97370bb7b5364dff8611ae6da78f1a38102bd5e03c5e24eebf24f4ec00cb 5553e29cbf778f6b48e8c5e2a68c9bf3d81442e1ed81f25ec700a9e23a662466 23024f3db8eaf17fb8e0e05330a1c9760245afcadf2efcc400df7c7d17a2f767 a827106be8428f79cea92551f543d37393685ea2a286c66cae92f8182cd39605 0b08a56085462887c422032a20128ae61ed97dbf5e74fd8d93cdb1b6e19461e3 3b2c211dbd384da555d95b648a128b56ab6643b216cd0ef0b3efd2383428be80 ef5b4344e2685e2cd651042f3f03dec4e83d3fae8e19f7390756aac1252e8a2c 6874856045a450c60b61ff651b69e34e2c834ccc28f7f483485e43f2b7e619df 397019b0b5f8892c9cd5578270342cc060f29908a62afc7833f2132ddd890492 56cab4e804d55cfb63ce4827aad3984945bf52a980afc5fe65fe9724217bc007 516eeb63ea2cbd880702aef419a1a7d6b300523168bb60c1c2f2176cf1d3f0d9 0e9ae60794a835ce5d1988d7c461eb18f25ec62fdabce76be9ba7937a9f69217 0cb857ba94a45956dbe3c9634d5c0cfd5bf97ad640af92d0b99fb0544d7693bb 2095075d719d82294627cabd25b7dfaf7158c62ad211c771d3644c29a5e8bc7f d45ef2f378c76e52ff85f3586e3fab64411eb387e3a68d54834160e9d4f75195 28e2f30d2847b093194a3effa41f01428cdad1f2d20cfba6eb5139f9e0163a5f 65bbcc93001475bf68096f7cae42be2412e3ab74a60ee2f4926fe5c6aef83f0d a61e3661ee0c48cd01ada4fd2be409d2b461acddb708ec548cc1bb0f7eae6f97 d26de17c60cdb8043f11c087f8cbdf014dd27c7ca80a08fe8aeef96d0a1745d5 1a493350e3b85b91b47d8f1f52effbe30f2927f6f86b73d13a1bba57eeb6c67d abd5c231d76667750f9d4a234631bf3f72d067f1b88a4b0926dca81004b3d0cc 1a3e09ab6047738e738872c6cb46d5157a0978ba84af4e4f4436fcec61ab6e6b 29231906cfdd684e0f4f21ff0b0ab42e9969bf6d6064b38b735f90428296105a b4f5a4ed8d4169c040d9a4c387eed3e2a2282668f95e5d59cda02b9282432bb3 321e8195768e71c1859bc220da29d8b7315671615da6f7a1c70a65c08a1c435d b8b37d590b0147ab67db2bd9cade249369b44bca21e7027d56b61f375e643a7c 4f8253bd0d973bd1079b792ce8e714edfafc5b30625babd7c6c2a9f04a25f074 466cd2e93630e51c245a31c4d6cd4c6918374d45324eb23022b87214a23d4fd4 990b405d17c5c8583ba5b4967781da5456316385111c52d4c1761d44062eff05 19 720587e0a73a7b2d0d35dc7456c7d0717e2ac03543f56326c83096f77200b40227e4da2f363e5a796f7aca69275d504ab8590ecce9256d234e8998d79d0e75067c0ae79084f9ffea60c87c1cd3e92e90565743f0506f372846905f5f0830dd0e107a5d479fc9e3177a5f55490e3045a57577c0b9afb97f24651824ef83903d0bf7683328e8b82b0ae50554710f11afde85e441cdc878ad81230cc42230e59f00b3e4e3a27b778941306b04b51182e0b30d05e0a131f261eada28bc9788409902f52774c90306e00be1299562df53979be1036b74a29df1c977549323d89d41051509ee86704b546e28846cf70426c45a24c02ee87ee24fe3d7ab1e25ab35130684c0a116205840a411863229f9759178b8ea7769646cd916f41cd9d79ced5b03aa03abf324b40eb370f59c95f0c28292ad97c7f7171bab1e911ae3a2858bb7071f2dda3246d85ea6df4660eec38404313fda6d02eee8f5457ba4ffb9afd8dd0eff9ba583223b1df7f9f7438a34d6282caa4338967e94e3bd10d74f00ce901a0249934e368567590f9cf4da4ba957d5e7066d883b28d66ba21183a427c83545005b53e5d72cd909bc3a503e725761a4082ff2cd959af2b85c0058f49fb14dfa0e1a0ad5bc88bf42c1433bd545afbf0e27db3b38015a05d159be76b28bf3174c0934ba868578219fdaacff832f693f6e7e7f47ebe130214b029a554a7bf49cec010feb9722228bb5b5403be38a10c69e250b69211cb30a40b5ec4ebf2f34cc010260ac6f66d615b83798511156d619351fb2731777a0a0dd83b09aef7753701705cdf0271995dbbf76e8dd3ede2ad03c2f106c27cdd8eab50e852356ffab507500f63df7e51bea2e50f6d01d6244528fb44060bfd4bbcde8f5f11183ddd639e308e9be56505eb1839e27e5d7cce5ba58cb02509727b74a3709dbea2406634c730e7fd513f71d326a79a03489d68779bc0af347205d59a77f47a91cad5f0b47bc0fdbab685698b4fce04e9f96f7dd8b0364ffa0ae3b5eaa86e2560e7db5932b2f06fa0c481624bc676d1240abecab7164305b91f2e30d66d1a4f78cd50a26760305ba1be3065e1ea8df05423e9d09fdae0385ce0faffc7695abe2fbd34b2772ff01cbb1db0663be477e95ec60c03c7d1afbb5af14f518d93e7ed973ba95fa8b550a91b2b3cc8c7bed5040892a522238ce80b5b9949238c61c6c95f974119371870cd716924105944c8af09a67ac1af35928833ea628c6f440afd610f23cbd265b0c13cef3204bb8e99c364acb98710377a3d455e522edc6f1cc639c63ae48d2e307d1d8a3a7fa740be61fe6b1a49a2874f40f1260142f46c2ab7505d08eab9dac0d4a2841c8dce8942c36dd4e676fece4051c0485e85479da6c63fcdcd076467106f24fa54039ef091bdf93a076bc8f7f3b08ca778e3d728a4f8e3ef0680f9c370193b6bf9d4e6a36eb82a8c1ddd456cacaed255a507b360bc61213b0a03988f804a94797f05a9eccd653cc89643eb18669c3875ea5872359ad62b4c110ae1ce108e39f3e07be04b9d41b783093ef509c400f31887c500ac4c3a6393a63ce5ae3097b24836eb414ffc45ce10034eb5cf1ce3bb14ec75b23938c537562b93c2c0e0c3ae49b297201fcd51cdf967019b3d202ae2118732d09ee23160cb0a554e1b90bd52a2273907c566a628fb92ca2c42e327a24eadebb25bd877abfd6cce515060fc876d80a0d005e9904877de5962ed9b914343f9b69af674fb67c1cb094f0bb00a098a7f7591a66ee29bb6827fb7cf285c56961fa2b8a6bc380d430328c12b10d9ea2f56c38c63a5899745871781aee38b6af514fe4af49fe301f70858b45d70ef0a1039cc202561f0c7d929793cbfdba955423eae7397b84f09b9ebbde8cc702f77ecaa26839ad021ea3851d559d3f4828bceae05bff38c044e1cf28a53e930c181d0701f457c47715eb47f5cdb249fa2dd726553c309883cd9c070002709c0ec2b9cb7154036166561d9688b7693e3b1eac12d7cce74f007f044470fb65550517b8446da8e940f6e5e3518e331affe60ea832b86116b57990e647b0182a2b048b3a17907dcdaa7d8c30798bb2054152a7beabc2dc6d1852e9ca3291fecff60c88fc5a7e11febd9cb85b548ae18d7a03c0746dbd052e4b02dd22b2b8bb044e01265289fee1c8c4a2364dddd8f620e9c2195f597321229af45fb305c93924eb05de2cd44e563e75e0c0451ce488d9125c4c3f3cf27ad3cd1d9cf09861c2635709e741abbc4613b5a8a8d3dab8ca7fce63e9c95e1c7297d882589aafa1ac8d3409bbfc30fa99e044dc0a540327d8f090ca5a83dbcad83c53e2650c9872a2ba98029a23b7f4b5fac046bca25360e288b919e268cb91c55af64ba1914ef763289d0ac24e6120cf1009314a6c7d32dd3824e6f62951a0cbbb613d6f98dd2766cbdb0b1d9c037b86c6d9928be4d2098d68eb9115c8ec4663461a5d0edc0aaba46edd03c259e3cbd967eaae3f5a23f8a8b8d213cae697fc10488d84ecaab1ad5f2b4905e978915584d96e663452b875e8f8e42c0a09aa65324a6e37d7b9b4a33bdf0b077618d0037c3a781311386af29efc4b76f2476f48f4db3b073a1d124c646a4009c52d15a39fbd9f5cb2757eb5ca47f722802e36ed757d819335c4fc28e8901a05ff9246f8de47d12c508c5163084980896c6862b8f0bfa81db79429b47cf40200c36e4064cbf49186767b144f85370e8ce0ba4397dd44a9db80ec92d6348b4a0eb6dc7043c458f23f4f4c6914cb2217f8a223249a081b952879dd19bd201a150c1b767df97a61abd9462e311b87c64f0a7d868346a93466e79df7353ae8c1460a7ba16e810e8bc0459574c2e5d6df67f53a635677165e3beb001e78635cb105003d0f5321e22d1c805ccaf58898119b4990486e9725520f3526c5785082a4070eae6691306cefacea216dc45dbd5abccc1cdfd7e9bd3189cfe0ae644c0e03a90ab0557db11f464db95e53307ef46375dd3ebab482aa849d236742909f0c5a39084b0e66149d298be776bdcd4e8b5c0afb23be9b1f47a8395d03ea7c3a51186a00d6f44c0d113d47fed4878d655df44595e5580250e69e92a2730f9f4dbee6dd0d81699745b06c6f5454fa3df0d4ec1bf4438b6279e150104137f71dd9b592e90233208406159b523dc9da6238baea4147e286099846f49074f029ab95d8c27f0fa2725c61c85839f4fecad1bbe31e27baa533c3e98dfdf3eee826033f7bada10c3b2884e06b42b1c75ebc401b395854addca8644be13bde09655b9a8d1c746d01b5e9c600423ef07108768a15d6cc28eac1e25874bbf5f16dc97aa1fb7a60950a6f97b8c0ec7263a8b30a7f0dff24a977059642708352504d0d14064315c0020c9c152a1c2d8d1219f1df7365fa652b5d3f3a05c026b8c616968a404aba0af7012a1d4036ef19ed80ad5cc7889762db53279f95468ef7e504bb3205c9753e2109086220ccb85bebc5adb4d32db114cb93bb48cc75550a6a24dc87386137558e033a65c6da5cd33520cf365d676a84d7372d726083b7f09105d6bff6bcfe64f205dc238edcc1a5117eb596333b86bf1d9c0fe53f6405f8aa4455160e30819a440a9d8f6fa0f8440826a838873eea5f5181897fcb2eacbc04534df5316eaf593604a3acfeb07a1b05ab9262899783cca8adc8d77fab6552b5704cc51b6916ccfa0ccc1c11910059157217df2370da381c899f32f705b71facac598fb5afba14070c7d91a30f8c061992859890d3c908080fd5b727070653d271a87d529f2a53ae01ee087890191a892e9f81c2542be8e82e6bb81f505f52b5d2c94f8c302209e1072aab841c0727c3ce09261b96054a845eff19ea83923a1fffb006f745cc931e093b55c892050b058c721e59721fa1a90f4a8f6da3e2702318853b800cde47720c5ba8f087a98ac04319a5ffb68960617ffb67c3c88d1f1d043d5e75b963ef9702b2b95d25b8896c47647af2ed40b8921d0caf7cede9f856d368b17e1d9943fc030272095f2842566dc4093c6ea48adfbad30563f9729aea02655d2d6945f2f90cd61f47c5e60d5ed773b3b77d3a33230e9840e727ed0f46f8c59ce4ff60da120736abb7649a385a444c2d325f3df5954c4d57cbd1eb2b4b6e807dee3a0196480e9d21f2807d73996b3d0671cef539929428c948e41ad9cce53cdc0430f0192b0fb7580e137ab371be7df275a80b41d220ba0adc40f9e47954b8796c8b47ed590fc3eba866e5af5667da379bef1f23cd826eff67a2542ac70a545434e8394c8e0892b391d7926627752a9f950db3f79131cecdba790d88d65b455fa7373d0e2e06bfb24924abca7cf61a65c27d813cecf61871fb6b16ebfee947700441342ea8050a5453f86452f05c9f6059cc22a924ab15ebc9324065072e0f7e0d737850db0ce003b7bff9494667cf834cfd64c15dce731ece412ec48488d52068dbb1f9070771ebe21be6ca72af75e7514db14b72ca14190184cbc9fe4593d08285a8f8e70f82523b4a5e9001ce5dfe141378afbd534d4a76e271543b06a65eb252514f000fdddcd0637dec749c5d6c00960fbe4d45a1aabd739ed4db02613385c0721f5909f6ff6faf0b7dbd924055237cc866ccf62d0d58913286733163116ef5cbaf9f02856ffc7e6fdd34b4dc2507cce01cd545e3981af3f3298a9789e1b2674eb82707c4152655b66421a889875ee80eb98ed41e20263bbeca6b7813268dd95386450799d8824d44688605cc218e2db672ed4d5651f5a2065650bd6124f465d17a870854a74d01bb1aec4c2a594d6c98eeee00d5408d8d16494d7ab044bd858fbfe60284ab4f3b34ed091c8b2074c93abaae1d73b78f943f81e61af064d155a820dd00cd1fbbc862c6a482014bce3ce1f2655d9ca7ba534b1d40e7e116c5893be0460bfd0155f7a8aa83eae0332465afa1683924f7740e2cbb6736fce9bc9ed02ef90cba43700142f380c3d58dab43ac8e744a6ce4a5f2e8114a1cddf10ced4b638804e1d6bd71e88c08d24d3d1baa48eaf86e3f90d0e54245ef0847e48a70fc55bc07ad9016b4e408bd0408a7b78cee62864f0e027b98a3a1fa1c11ba8ad9ed7a6707f63ed4f90361da4dadae280e32cb9d609ce6601a0180f84de6399ced4851270110bb346f18d496817adf5a9e2b1899b1a9e03076b82cce307a56a06d2a083b0f8ad937edde70bc9f483de1f14f639bd0a655ebdc53bc3b35e310311f74f3590057b9ba0366575faee839319dd99ca3ab8a87d2a991df38511e7590d8d0900004b216ed1b4af759f60593eba516ba64febd75acb474760b598c34ef6a9a908b01ba10fd86f170ab564293418d7e1a203216db515f6502764303f155b8fe9262093f95f0b392682bff02d00f687bacd19189a82eec4802fcbcb069baa95cf396079e8d1e3b003c08be17694a2d027de63a80aa9b290439889a48bee8144e90dd0306871f39c2e36016ed2261ad9f10df97a81f46d18b4fa9f8600fe7184c87f2078c09dc6e2c1e79dbdf3fc6c8c1328a1717aaa92bd7ff335bdf600b6e20118302e4560f64fa10643ba181353712576f094557a870f6f1453d71f3e3cbde1ad203493c93ac2400ec4148ad974e1f7118ce527ea53c07c541c140801a81d7de560ba263971ef1bd70054c18b67594535a5d3b0b455d0945f3941a9dd989af0b0205908ffaff750b2bb2e32a2aa89f1743ea5620af6f6f6e6b7330456cf4794e1509b2c99f6ff81f16e98cf89bbf412429e87d74900877364f8b8fb8a2c6984d7e077a38f0a27fb6f5532bd23af9cfddaa3308f9208f22c0d1ff945da03b7c1b580d3b2f64347bd579febff8bad53ea2d3cba2fe83145fee52f0722195f56ad53b0af90a6bd2f27f28ae6ad88da35cae1f4e7e2b9d2e31fdd9ca4d569d97dc9f3303b72c62981df2311b832abb2d9bace43759b9c69b9413e7eb04271c88a2d198034f660ecf55a5d6220144166d18ba339f3c729ec38dd691726c49da928c626e05a8a6996a9d4a62b6b06c677a5abe9183e8384cfdc52b5d2f098e79741bae3a08dbb4cee731e8f0263a0a6e8a4f4fd0e4eb55973026605cc04b61b42a528176078c02459aa318e962806666f488924b06b1c216a9eb186b52931e336674acc10d56816dd8965bfef0c23811e3ed1cf352438610eee960156f6b32b8c6f24fd20c96e1be77e07e182186d2082f8b97ede9487333b496eefd1ec92cae1ce4595c091bfbb55e2806b8ff45cc03f16b66ddf674594e9bb97ec650fd29093c9c9b99004e0907b4d93ce474ae7ce2b93a9ec37f32d1e7b41709c6372a1d6a4a4b98f6037c59c1f9fc59df37738fe5248fd9a4346de8d89d7e8c40cb59402521dd62e309a82c0301b4ddadd87eb87fc9c002c7e0cd619610ce1cce5f1370b42037952c0c8389381851e7be7870ed6137882b52c0bd635868fb11bb734ad33a968f518204edbddf59a2caceaede2df61772ccde62474ce1ab1db354f4bb29398e27d3bc0a -generate_ring_signature 19b59e689992b33510cb3a4641a0487214eb16b6c4ec84f17a725a64d241eb02 35404cf6de872e69cc675a0c374d23085e607a63b198b243575c3aaccba5110f 55 96bfedb655e4ad5ae088e1170a9f8b58daafeddb84a3d8922c7a695e82e66963 b57b0dd3ab098d2e5a1cb9df3b9ecdc5e05a5a79a40c818eaddeb80ef3502be5 679c0ec82de8ea3d0fbbd4f41f61cfe30d486e8d2807a7b2da091c4e79cd7efe 1859dc973eb9798fbf6c7cd07db81acce29d707577dd72eee64a3d5373103ff6 4749c0e2a2595df0cc7608c1893942e2bf72f1ceb3e4609d0a12737f3268378f 34980ffb911d8f163de0a1f2da648b989d2b38f41e9525b4fe2e1f5647a15263 8bb5ae4cda78148b7b0333f2ea45fb88eaf413880cce4254f6e84dcea809ce23 e02027de37ca5fa4a7490a35a52f26d3e1bc1a462f8357c3ed9db7f7ec1bf6e2 dc30251df7d13fb90afb8a6360bab069a15a7f18b2db02a80da3a3906b96b479 e72e6290ad812eb649b4a722ffa13919d2e4fd49ff4a44d5e1f8d80976553fb3 70caa5ef3d0766d48c6d9a6728d0d539290b4a68452876c9b8554590429da46c f57560f08f8f42d053aad27a4387fb09c0c914d921b518f1d158dfdf16a4e17e 4655b554d751125b469451635239474a94238a5e59aa3ed0766083e11ebfb9a7 259012fdbf5f5453f7aa98d6b021c4da9214f34f297b11e32f1effed4bb13909 81b66e8895b7411a2c7b8390c0b20673b1ae12dffcbda94d5561646a6d461dee c62b504e655ebf52bf1d72cf50cfe4b4e25a67cf6e968c939e8baa09fe05ada9 139ed705b0a5f440be69de7d7d3ad3a2996d36f8f809f02bfd42398b02e91dc5 392f1fe7ff4620ae2a3757acdae3b4ab011418e843153628c3193db731eca520 33c7d48405a1112a28fe51de2e6a9a9fc35669d246f23b66b88fd65480c23a80 2ecd2cff507a023c57776b03ceb8e75536c3ba9d734bb08672b7aca0a81dff95 4608c674e9421be71e557739fc5b2b868c8e46717d893c499a575d6d1f6af1a4 5e89a1023aca7015745d6e134222ae6d36574605626c816396755f0be2d68bd8 004f0204e19cfaad23af654dc4ac284fa89229c2ef95b9bbd5aec1e0e106fe02 20087f0163ca1244c5d52b7d9c343f4859f0fb8151f0ca44fd58e9fc28622cd3 d061d7f431bfcafe091420eb0bf49476ee690e680f63d2cd8e57ee04ec315d43 17107bccfcef930b408cbaefb05256f430fea8dc5fd4250ac9dd5f4ed3fc2547 ca339d35f0fe605eb0587ec378bf0f3bdb44e11438f999c1bb1c3c5637b7e2f5 2f89eddb111d0b6d8ef5a6a5c265c2ffc491d0e9d09d0017f494df2dbc360f0f 88719f6e415b781c843652d420fd554d291f2c0e8cbebd6d7e0fad80c04c809a 6e0b292ee1b4bf0767a7163b61c7d9cb7d4cd45276a78913b07b73f1c3c1d64a 4eabea46bd0266491d0228719fc8073a404fd25a4e837accd5251a61cc87c02b 2017496e7854f8fc95f17df21bfae2c0afeb43aa28c245ffa1b5047dc149e89d 24589b406b4a7698016ffb8aeaf5185993b8502e46d46e3bf37bb078623496c9 e8e32089aa49dfe331db6fc798abbd5c3f2daf7e1c98263cc7869e66dc7adacb 05b2f33d72ae54e95169937adc6420774768db8a9e3d7236edea05e4455e18b0 6a4b66dae9632c12f4daab911b0d8b9945fb75c25360549c23291cd63744c3ac 8f423495dbf74ee63563293a66074bf15713e1d3f3a16ee4d5f33d414bff6fa8 3f53e929272cc0ec8e023cc81acdfd50852273ee9274732acc069ff56e8644f0 f0a84ced9df533d2a34935af3d0bd07d544b2e84ada7c3143df14a529302f09c 39100e3dee3d1985e04694de6a49a082df90f3f14e07837dfd96298ee8019252 895b06215ade6268bddf5c5accbada81af003ff3a8326701c7c4b2e4af1f3f66 a99ddc831aed70624f0e17f8e1ccfcf5add0d30ff448ce7bdc08d6464fce9e1a 60ce654405b62dfea39278dfa49bf1cd1cb640f60d3bf18fe3dab964832bb7b3 0f755692573303969da6af7969a990f4fb4d0d5a22c1c446338c7aa394b8ea55 10da39906513a827a213081f345f6253bf93cbe5717b8add690d76499580ddf7 b363a3561c0abb151066a5a39b623c9c1b7745333dc16e3ff858b227588b525c 6436c0ca822aa4068aa89054638665f1936cd2a00ef7c68c18a200d5731748f6 310c66d3a506da4d1cab6649b14f875c07df63139e6c4424791fdd7ededca9eb 5137254f8aacf7b9b331927b27c932f68a248df87954633c08bcd84cf7bdffb7 1616d9caa4875622e3802ec550aea5d449b648ebf710a77d3c22eeecbbee6f76 56441b786f910a220f80a7312b1f58175bea544c2831d29af67671af474cc5f3 dbb337b8152363bdca9c7c7b15992fbf2797f113c6e035310becf222ab12b970 36fd79a1708fe543dd785cb28caa54f1d2224b6959821294d8bad2ba982ad4ab 745e169e91122070e4320302d73305504072395f02a6eff85884be79abce0c07 870a32a647e2db50d03544860d834c1c4666eba0e351ff13c0198bbfcfc89c46 f708b34270bb6c3817c74e5227e43d13c13ce3dce8162b8ac558da1c2e81be01 54 ab8e3acb00c19c79780d685ad972c73520f98777b4737c21c8792bda44b4480a244bc096b6474c3870579da0863e0bf6de2d315c75f9247658e822bb22699d098e86c634211d378923e41d0c63dbe61f2d2d8bb61d5233107908f6da1d6bd3034f48797edead198766b6ab33c9b56069133916b7426555bfeb5dc7e7cd45c4087f7d2badbb46e79b3b6a641062029eadd7046c5dde17684b5a53090286a61608108cdc94611651fe2f5a6a9af00098c69546b688f17b940e053815584366b00567922cf1f567b3f762fd4a96cc223ca1b4b686f473412373d9c83d727364c30f3c2d283254d6033bf1ec2ab2e5a1df12034a64738df6bcb200c032ceb6ec4f08a915eb11d507ddbbd3ff2d7cb95b3eddfccd9f9172749511abb2f49d39f4ab034071d9297a4abb66870148240cd94bd4227e823dcce5e566bb97f7c9d67ca3040b35df1e582c715072640dbe2b8476954f7d4669b69c121ae99bfd81445d590bd09a9b63270b7fd944a61483b507bc460425d7016b4fa4ee97f40d8ea0c37c0cb8246aca3d1c474747d9cbbfa57b96378d14d1ae8048d2b7df5e4a9df2e6c20cecc5e4fb78c924a4f402d77d730690eae236475663c11f96360249a3cf81a906b52eb6e9fb86a9ee20885e470f6a0f610f0ed8bbdfe9644c8462f93abf596906d2584adfa604b94dea83a169bcb355824fa4f7d37cd2838b0662837ae93f7f018e5ec218c9f54f1370affc985f7c78e888782773725b31b81c896a1d8c6d4708de957bb7377f1f73a415b3e155b74961196f28a51a1efb8b8fc1a2aaa6897808e48039520b41de3b2b90c9dc2ebb0e27521b3de1f92ac79b1b5a4761ec5fe00b1a378c7566838ce2f8be6ac3e6c435ee76a9fa49a0105ebc611cec620ccb7f0bd84db77033e4be24d1977b10e1b88ed9fa85b3e31e03231d5094773f1cb95706a037ce7554b1c6d65f8ae1f8690d1079e2040bcd2cf130f3b4db4e96deb59c0c823d2c0c30ea3f4807b6e7983fc12dba340fb1d58e6de48a237aed50527f4101582d37d188edbc7d33fd2d0b70b75e05bad00f3001f7dba0c7a72c9a30e95602450cb9fde0d839e2f030a101c72f7afaa6df8fa4863374723c4653c3dc8df605e0db4a38431169ab791b151610281f824da175635bec67ddf6837d928283ba0d04f46df63a34b9fcd4803e00b66c645e969e537b7ea103adab63b69c30a2ad0d3d57fd0f4d06aa66405b349933a7a415ced6aff4bdf3c88fa7ce86077d391103889d028cc8d6a40875c3d58b35581a812396439549f03ffa333bc6d8cd7d47053e698cbbc1f6cacc96463a2e5d1d81597f24eef2b571a2adae2851eb1d7f1109fae757094ae239ded05c3836c8926f9a263b465b97cacf3058a72cdf3a195206e13e7f5f88f58dbfc992dbbf97979bc918a700eaef427c8dca0fd104b010d501427ac9c8867fe152a3795aa2dcbd929bef6eed2d85ed95c8e5185791eac8560564f26ad7cea31e505803cedff79eea70f9449ed59100ae5684260375c189ff0af9ab7ffcd88fe1c95b7716fab01f9dfc5ec5661e374e1d29c900a834cbcd2d080f7160de1f2cdc312fc81d4c42508cba95ea422ae5d0fcebc325a140e3d5450442a89fc74a087727e79734bcabbc3727f4992639c60df05cd6bbb923628a77057d187fb207090c3ba682fa7ac9bfbfd076bc77e6710d99ab084a849fd3bd8508fca802886fe3f29b0a6a950e8662ccb7cb6ed661b9d6e06f22dcac2f13103e021b2b93886055380d92d261a9aa074f6a08b6a755b28290cea153c6064ce80d05ac4cb053a18e758de02bf33c33d0d8859c3c31c3629f22f78907eaf9394aaa0777c2517cf52ccb536d174704171082ea2d63719f20c38b653eb0164c7799d706a39b96e5429391d05b2176490324f6b0d35957eb2108cece40725a3bd56e860da584cdc4adb3756faaab80b6413683a67030cf61eed53a68624b60943886fd05a00ac482c0c962c539aee4d82262214d5e3d004236c77efa4063a964b4bee50549a4f6de2126433f363f39d0c1158bf937d05796e8591b48c7e96819cd47e30ecc46767578b49e6bd06f0f74de2d815e93b23c72528aa7a54aa88bb94b903a090928b1f3398742c941ebdd20afa4b9630827a5b902b6f07c40f6e4f7f8860904db879424878458bf4f10ee2acf3709cdccb65b876d7e9a1077c2837c338ed70d7ed41a888747735f4b94ccd011fb5043cf448a4a4e4fc4d48e3a5de5e8dc350f20a19880ce568d8536176e9089b0580c1195b83580d7e2146cc606634fae6f0193a8d2764a0c48344ac384b35c5faf51c5d306fb21008a445d49ec8e29ac9c0c800dc82771fcb96b35e14ab5e96e2f5e5d3ca9ec4ef1983086c1e37936eabc0bab64acd630f3461d49934608f701a406e1cdcd4f607a772b68e1bb1891d3220c079f4a07ef97ac86258f2a83c4b341371a66869b769a6a99a7ae8756e4d1fe0af4886e3e7607aa7e70448ca03673d9731371151f0d546f7bf925858d5550be0591d17d3e509e0252e1b202857fbdea87bc201c6af97aea342b0b08e76ea2320b68cd1ec00c339e874b74c2445b2d26031039887b7b937f61bcaec18a5ddb6d015f1faedbc9ea4396050f04d183c5367230d4e7213d52b4a783b35ea05d40060a5151a1ad1b8b963b167cc00a8bb84bbc261cfc5bbf600f526883d7e58c4d4404d41f23fc9c67ff00e08697d88ad6346d183213c36a801d482973a1bb53fb9f0f502f9d03830142be81dca661b302ae91b3a2fab58785b85e894f4c70d0abc308f78e3a80c7f164afbb53d742ce04b08ee429697342b49d34708264423d407d004a548f288daba222ef5aa0f2313a027fff180fa5d17a21d18409fac72367880f05f6df2c6c4ede88d55c497f584211b954658a259982f14c6fb005c9a95d070f7b9d02a38fd41aea237af78276b7a01f41ed558cf0000e26c87f16246dcc040370d3ea5903ad1e9f91f29b1fd66cacdfcd1b7fe2d77ff92d02a6f85ad496dc0fc21019596eeb3032b9b99c63f293603f5e72e945531d9134206770ba94757500bec3b444a4ccd2a704b4cbe2465b9069cbf394d893714bd4ebb2b068434dfc00ecbb702a1bb4144c0b48443f8892a2fdc09a7ddfb5aaef667ec4bcdec3a48c05d17bfeb09e03aa163a3c6f5b53349ea1e45dd5fa122a75a8adbc93ead39e0e0f921633c3ffa9ec1d619d8c8e722e28ee598d2cac2bee794c133a417ee6bbce07f53bc714b9f61d938eebc228459e3d2e413cb8760d7488a8e1c806b030eee60a2788d4fa662b2eaff83380bfe4606e6819e2e589c2065a1b94770a0993a85606e7d20326c6a0fadc07cdf40a1d789e1d581ce386ee17fee9f788dadde0c70907e7a9690f9a840d98de6ba53fde6353e3aa51a71b8cdbcc916e0e030853e1c90d250c7680a7a5129bbd99979a261c2b2e9566e9ece085ac1242211b94ab01220c243570fca70007ca15777c102fe88b639cf68c44cbf3ddb469c96b56066f9f024055f2f954dc496121157225c1e11b51203df567235f86dbd8d169e5d601770770584903802a9a5bae9fabce3407f60deee01478e2061a7436d8780dd6c4fa040187b60576231fb3b7074651c4181861914c9d6a951a88060d42a51351533e01844cac05cba7d31f158326fa9aa8cf8e421a20e3a65032516950b42b232daa085826125dd0c235c6712791dbffdb6c553ade38d96a12031889b8c78a0094ba0bb9e613496c663fb42ae86506d8fb0db7a534d2760007830d0a15d5a85532c507be44135721448005ec3a8b1afaab1e2b56df7a783373dfda787a6e78865f2b051a596c17ea520f09e8da181cff573d488a5dc110a3f2640a77b5fc2e7c722f0afacf730d55fdb70d51ce62c59fd23456f12a202e61c5675004f6cade581b0e0f694f88aeeedeb95c9fd84e1a9529e5fcd5c28d8b4cd58de37fd17a4aa209fc061491b4e4d1d077ccfae76d855890bb4e51bbf1a3e1aff3603ee575c383c43d0780d7a70398d62c8514823cb8dde292fa8dab6d902ce3b08c316c67fe12f38a0e21e2adde63bc6cb9939f3ed52c4822efe15ed1c61f7e0d2579f06b2c98505506eb1d553b7adf76ddf7b6852acd6b598dfc39c99b88a501dfec834c3e38c37a0527b27645d36374b500faeda21331ec1edb7162307405f85e063f7907cd139a0d5c707efb3686c57b222c01e6e1aa532d857dc81acc47cb60e3d3305f09b4660408ce84121986013b43698ee27a6085684071b0774eecab30968753f1b43dd801c7342b59fc0d21fba0f633010633a41f91175a026c64b45f2045c34ce8cc2d0f3e2dee6c3422b8e3e5ec0af9219b51fafa32805b6f7acf700a62e83e6f6be609e2977d633257551f29923d0df88f4690c187088983f302a3de267adf9dac57085a8dde3f3b9cd4648e2bb9526ee692bd0a1d7a8cb2aa7f6b1675105cba474f05335f1a47c489cb1ccdd9cab95be04b6edbd8392e6fbf541f2a84b0d4fd5d3d093ed9397785a478ceb4594692d72e646ed524b99f45aebb847ee755d455a6da0bb1f82928b8db3844a1672729c1acf994b1ac22b42c5e8c85390b0841374bea06b32650705e51fcce376ec536bc1081c698f7ebe675749096a41323f3c4baed0490dc8aa8f733ef284dc467d60b8d2dc72bbdc44987115663b778ddc62c6c9a02aff0dca6b1d21df3a79f7f509af64634ae944ed528ba05a1ac07d0107c20010b132707b34b998d1645bd07352f10730743763b6fce70e610f50a50d5adfc7e05c3fbc70fca726f184ff9fe1c2d1f6b7a5ee01a5cfd27afa973c04a6594f3c0049d391dde75c55e0462327278915136ab49b7d48376e26e90f26e151dc5088b0f74a1ca61151b1dac8358bfbf4e6855140912d8255e13708c4d041c836ba3f8031191cd60726d311c00ebecd8e53dc925c38ba991ff2e2234a265cb21d112b506 -generate_ring_signature 4672f905fb42dc0475983eb5a48f658a06268290809009c1c1b1d2c76fa8b2e7 1e95cfc5d2d71497ba502d461fcb7ea9f9768e229b9c268e3f8341a4f254990c 1 3d62ad8a5671f7bb33e660429e1a8d65f454ca409bded90f89d659e10284641f 303c8f50c931bd0703582650568fd4d3382117c521de689e1630abaae8a4440e 0 c285d9329ccf56ca829ba1734fb0c2412b03400c6080c75b087d31116561e40e5e36b92eacd53683b7bc597c842296b5175991207e5eb0be45fc168e9181550c -generate_ring_signature 3c562fe4a9bf99c509f2e8d4032d42128a7e92b168392d9538b06fe8f57ffe7b bbf7fab81426cd9d7f448815014c590fdfdf9438021002f067664326bf4c972d 21 f766397c6872286d33fd553796dbfaa31e7b3d1fca071ec0a4460dbf140a9f1a a6a61c5ffe6b77455c458b3cb3e48ec99c70963f3d0b7476f2f3fd49d798bdbe 23ef9c123a46aba32944da02867fe6dbb83456fd81e57ac37c04fb398d748e2e fb3870eee10bc2a23e5cf82e0023c95d7de15ac76adc77a78e4a4c15a743b52d b433b1ea0f29c61a863315d234cfe7abf86106226da82d4de2a59efee82d4a97 90c7cc26068024a6a5ea19303358a9768440254c5fd395e60770dd114003b12f ebdfdeffcad6e468659e5e55c0702cebf169e0b6505bb802101acfa7936b5971 b558862930e7fded4b64474777d7e762f252d7e5ef32cc0862f259ffa0cc074a d10554549af45f28017be503d7f9d838d8c20afa543b0da9cd28f45089401038 939c678793f7619c5985f478b78f90ffc9b3de93a2aaa1674ea9a3b3f5b4f369 e639f8e4a4196ea5094fe047663e959e329ed2a9c5b205d4192213c5f3c45201 1b4d75055248504ac3c2821e2cab34035c4c28941960bbd177208e10499f0bc6 1b6e7a5ea11b4e4a042e2e197f06dbeba08163e681566fd83737e209ecb69678 209c34fe577f2dd7cc74706175211a65b87489553fb104c9da0ff1cf5e5c402d b1ce728ee6de1e258471179dd350967dcba276f7faf60a9659756e1d545811fe 8b8d736c76157dcc516cef83ac9318921e9d6adfc0e4a40cf686fe71504d79a5 96331d76ac46063e2d60b4bd809c91e77b893c8d9e2c53d27a1c378c20ab40be be0d16a05eaa11ec4ef8283d5f1b3ad1cae73883c059d855c7d0355c1249dd23 ae7000fefca7f670daeed94a5f3ad56697fef12313821842c0981271f2142d77 8c5fcddd3f4b3855f1d17487883f33fc05eaec1c7949aa9effcf1e17eccf8895 8900e46d54a0a2ead552e74ba2753b5c8d2b15e381daaeb48569029433d6e8f1 edbafd6cb274f37b1783450c83485489e72afb99e07afebd2853f9997418980d 19 ea411deab51f1c457aefc68fd2b2d381426b056732d75ad79bcdaacc89b13d0a124a135c0ca6053f34bd388b55d96c818ed6ec9e594ed78d164ffb74c73c0e088be32f2b5f63d87395571fc4263e18572a79f79f6e505577e312384dc4858009270b8009dc9b5b6c7be8b55c40d92e0be5b14cd53d7c7458ae5717a0cadfa3041769134a34d912832d4ab1dc26c300c7723fc100832057e5dd22d099dcdcc50cb5e477af3251f6cba5b7a8328d1d4f1849b253b180855690beb29e520ef2c405d7eacfe29bef236661d8138663b4b3158ea39c6b5016c801fe284533e9f16b026714beb6dd1b7855cf15a9ef3cab140b465508d0080501dde4a8e3b792287f00b136f697b0ac2cbfe9a9e9968b1756639c09d6988989daedb18ef672b06b490717e5db5c2579e231aab4ede21c5c23e596036f6e21a5ea4b8f2f1dc9426bb1045f8763f88721f6e2a7141437702e8a38bba87fcdf6758ad7492272c7ea82250a479bb2d10e76bb8912955bcf13b4738c6466bd4713889be420008db8662ded0458eccfd5970f4fe4057fea5d65e7faa4aed9da8352b74729b19ca9d1c09a6909a4cef39e98ef9f3857b38cca34acdaf3a665771015bfed5a722d7ec934dce10e1dc901fa4d73377dca8a4fb392ce9f476c2f0e24867df06a3d2403f5a4adbc06fc6b61f902fe830b79a4913516392a387e8cb5fc76948e25860da1fc07d9090b38b986e9779df7a1cf9ac961443aa6bddf3021ca0069cea1db522460220a3607ffdd7e875bceaef63e1774e8ab1c7affc34cb0d5d0e5e02ab58c48a11c08f8038fc086dc0e1fa62a9f745506aeec610f8a43762e1fb554188e0ef15f4803650a7b14b82b2587679d1d0cf1ecaad6eab40f6f391e5dbe81447f74bf0626eddf03a5153bf90b04f6e4e806269fe5589465b737dfc017574da06c1e019fbf57ae079440ee19b4623fa6ed63866a5c2d0c568f461005d6470a3846ffbe851c88f1083c3b21dc8e769457e2f961ad898b94c45b05f32e4869add2e0fdf22cd513ec0bcdb1a07b16f0ef0cab3d70ac110adbe2ad2ec5dd066a39a4cce399dc5c86b300e31deacf4bd1b78044eb402d004bdacb088a7973d91f8044d0002700c0c13a0ce3b063cfe08b5da05d9e10c9afd761d77ebbfa8cdd58da89e31c0fc75bc7f600b122ea5b3db1725e45ca9fbfe4bee27ce6f5d76a3f7d9e7faac3a77203be8a0dd5449b9b6069cd6f04e59fe92fb406df90ac387971c16cc9c9cd914b4ae7630c8f4f79eccf806bf1cd7ff36adc80a872c50f802a828826e133ef3a7ca8c9be0ee6f3a2a1de5c6b7479c6fdfff9916d92e4961a859f7e3d4fd1eebcfe527ef805f6edd21471b71d915e0f43223e29faf55fe2e0da8d8a9788cd79ca4cc3047b0a388456a5e027ddc14abadf7c582ffea8e8e334418a11b95c3a02df88d2954506cc7b4ed0d1efec79c50fd6ec8438df19453d68b7c4da04d6bd64534d19639e0392abd504704d90f29c62923290908c63959ad93a767274d6e9ca1157554c7609d556050c43f0be15c4db7ea83c562fdb4c950f4b1585d6b3e7ccb2ded4db87049c94ac27c3e3c9dfbb860f9c6592699a379bc503e19a7ff8863fc77afc84da0cab5598823f33bbc215afff0b9dddd541b6246f09da911654b153e37a1b9c3b09723dbcf3454f47f5a9040feed603041aa6536fa389dbf8125e01ec7abca32306a6a35bcc0ee4c602b687c19a68742afbbb6a01b0e567be0a893890d354d24b0cee4b69e3e236c919bb8a180bd8586c76653db2a32831b2c103f7322669280307cd70c68dc18d2cb15f317a6e7c37b83b979a2684e1bf3eea032484f4dde8b607ee41838f083fba0858a186a1be2ef4845c3917fc9db19a873e90ad7bb3cff409 -generate_ring_signature f429996a2c98e465ed627943859394523fd3edaf7ab2a919b0d190789e34546a cb0dcebde38589ac7c0859ec7885c7e6952debb5cd69e6b0922e73eb31ba5068 14 7d8cf927a4b89584ee99103b0203a51636d454aeac07e163065565f37a22449f e5390b19dc3e87c8f2e8adf3fae09348f3abd92f936d3a4c1ea8700b29e31dd0 af63ef0b26af519197bc1c456474274d00301c68111a6552a781de6b5989d86e ad8a74a8276ce918dcfe1f633b4212d61782b38396082709eec6547be24cbba4 dddfd48e4e8599d65a87c7ffd853402dd1dd945df61402d6f5d1ee5997bc6f04 f5ed6a7ca50fa4352ef8b230f8f043d2109bdc4d914492233a24bd51571c7bb3 e9c6b1dbb79c841d2f39407336f0536b30466d76dd96c30634acfb4132718d99 06d9d6ab1381c4b6e3f914ca3273965aacb4479a575468aa84dcc088f675009c 4bca1da62da88b5c0194f893878425959abba8f1b59f70f8fc35e90fcf04607c 537de67dcb5b0515251c02ceefe5edebf29f96c3dfdc4cbdd80f677aa7a86feb 81979c19b915ed61382c40953dc698d9d2ab5487d8cf9de2958dae69d165f8ec 939b33105fbc1114c67916681405b0dffd88c6b9d265023f1889a0ded6b6f715 1da95472cf9ea935e2225531660b0340f21d8db50b8d7f9ef889ea69386884b5 bc01a282651fb0e13e062be2ae48dab4533bac2bfa7a069d281db33ece3bbef8 9f70dd260c97b39abfe0fd3a4773ea3e31f4d4a440c3f998f68482a6a580910e 6 9f6801f8a05777a65b5c4beda6094adc33aaa7d5ebdb2f0eb53405b77a158908c4f15388b21b5fe972b210f1cf99e482b04ab679f72dd0e69d9a3bb4b707c80601224b6201ab48acceef0486c3b4e4c928d34e23ecc163d9ff6d0bcf72acc9012d0d8b21bb98b18d6a3f979a325e8b8c67e025f48c7b8bb98c57af6bb8d83a0991429a7bb2f7ac13bf975ead5974e0687db035add0e02583014c1960cb6dfa0d24089eb3071c1ae33345913b4cd876902c7626529bdc922ba6964e67807d4f086390c700fca7e2672e02c36e80be27f750ae158fe426df4747716832b8d9820cfe6ecf8ec61c5cea8629b9661bf59271f2befa1e84bc08ae759b1e9e51fc9805e38d289a60f604a9b040e724a75231ead53bf712cb8717a82fcbc9f20f63b70f779f38800d96cc5faf80147d5ab7f60d23f9c1b74edd1e4c297a921932825a085cef4f8507d00a6f98878e01643bbc55563a3ab6e249c565e9aa5ff55cbd27076c4f11fbcf8675bfc88ffb36e20261d80a1b62119bfc032faa6a1788615e41012e1b490b8b7128f57cbeb05ee7271c1189e9586d3e554ce46089187197944907cfddef65c4446506c76be874942d3569a6f8c769e6315c8875d7ceaa0684690dc3e42a5b6d51c6cb5f3376c38529adcf45a14842bfa3fba569a7765e0d81c708eb0fa4268b2a9db5eb4216472524716997c0de4ca2a9f35569adf07ab4b4ba0fbb6f90d783ae4ffa53c9ef0eae190f6059b24d9165fabae62639c3b74583a0091b9a8e8d5d6b8b5ddf2acf5b5dc298f0be6038dcb2a4d29e54e5140d80a4000f39a66a09f627276eee4015ae1aa00ff9d24a4ebb9ec99acb110259a272d4f608121e664fbf624b4609059e2ae13083b87d80975316e07be7e4b4de6963ab6a0cb710d30fe4c7eab72aee4bb3a3b4f57d00df893d3e46f1e2ab659345161494033aa75b6878c18b6ae2bc364dd01be84d3e1dec2919d230a2cf941844e9e79402562152f67b6cb838b016874a838deb12beb5be486ea65e36ec804eef3ee4ff0fa9ac607e979484b252eadd34d0bdb430d13147e079e4a8768ec0595e7d4b460643c3139d259536752f2b04a6c470a14838402c797a72ddc6e51235c647b5640b7c60259ea3b2d78c1201cc7746f0b13201ec7c9595ed3a787194505a3edcc30490db343ab3780607f3eb390796ba094cca5d7f3f26dbf16cc01ac072bdb10f0affd43cf2a5ccae647f654c583ed548038c6ae0cc227af066a73c57450b665c03 -generate_ring_signature 8d14132e5e17124e6b09f1e79ae09c053856be240e8c7382f8d6c420f7904300 96eff4358f28c028507c8e7f0972dedb4330b822653b2b313f4730d46839464d 6 c616538cd58a741d5b8a384cdbb2d17de994f61c90f280f7977f1a485c2cd43f bcce7c9de27adf25424520a824e71ed3e00652f7a08dbab59ef38b690bc64d3a d2ac5e67b93192d3714c302e2c933c9a303d484ce2c69c242fba97913dddb0dc ea7b1655ef60438b22f1e7d37324d92b4b548aeb12d7b870f2cfceb28d0977c0 2cb5bfe3c0d79f7fb25ef00316e250bec871a4ad7f103b6a9d0c0ac7cea10375 0d5bbb4a6990c3e1c33f173354732dceb862bf580770829ee361569a8d77b44c 1196427b5966709ba0a1ca8b6cfbf60f91fd6a2c7c7e7a6609a63ee0f801300b 0 5293fd36be926312fac50ea3d9984e1feb15a8bda6ab9c019538169b9516cb0b3b43df41eb8e73fa61701396ec4d011c8c32139bb261b02bfb6267d5cda7250742ba5cf2613267e0fda6258e7da769546a41b75f7347ad0f806a055cddaaf80d37d34bc5e89595f0a0c86413819cdfceae32e442749bec9f71d164dd53434c0235cb94e061389b8a6d5da39c80abfd9a3e6939c559f9e06281c6283e01f35700d0b8502cb342a117dfe675bc8d58986f845b6f8c9f3f9107083b87fcd4c2ee0af413e60352f88377a2b30092589220726414e82bf764967f45b3306f59cd7105dceade154847140312d3050db6a5ed77afc203d9e0d7bd9ab7bad446bafeec01947d03253820e53681e8365d6e7ac398b262905619ced8b8fe9d2791b286dc0c8a9f28947d0d08f424b55e689703f35804118f6407b357dca957745a1fd4380b1f59ae56ab316621401cea1f3187aa8a7e2c0572a6008fc796211b96012a3c0f5e7b8815840a499931d4b2c13616b3cab4816287ab50490ce0e68234626f3c0e -generate_ring_signature 05b70ec90350d0aacf2b9be53ea0a7e0912c456b4e0fe7f3efa8c7fabb1de215 db3f8c06eef1a728af728ce5e028068dbca392543d43b30e3408e70a5301e4bc 19 fd5ead415e17536a9a92d5d8063001eeeaf376a4408d3e4d3f3351200f2fcb0e 05fcda741a631937b631b745ef0451150d9442798aa28288c6a2f7c02d12b08b 04bca31da70f07d8e34aa70087d78dc097c478267db53798b1a4003ca02d9e1b 8001d9b613fc98a7a3fbeaa7382d71a02f566f02e1281abfb83888e3c83c6825 36b1a72a97fe92fff3d983c734cdbd2187f34763c98e85e9bd42f90541aae212 11d585e09e6cf199be4e13c2bac0b996a4addaad8b1451e5b0afe18b53cd941a 766650cb51e01a611c1724099e9999c60634e1a3cb36295c58ed35d4d8ce6860 8151bbc0a9b80790b44e6ce4542749de5e5b91171f0240b2a029d27e51c56836 a84a16dfad85aae7dc4f86f3576d396cf2867961b129eefba1c40a9b87895f0e eeb46b381844257d051435756aeb6e235b92545095136c1db72d84edb64fef7f 78ca044b9a1ef85d91d558d71046768bb5ca5e4cf1a97a693a51e25c3590d514 953c11498b63f8cbcd143d0515aba347c20c61fe47168e7c862466ebe875908d f11936b0f1dc77561fda254ce5ea7998bb1c00f4e3e69e6c63b0795d99961d93 5de09f7a41ebe987ea155d42edd7283ce1548191db18aede9bf0ade990936baf 26d83642ee3546f8334921be1fe4c766739b3e92dd8a29894e1e348bb6fb428b 48a98894200a4b25f10741c42195b8700bcb390b790ba0e01000d3477b36549a 4bf26521935253ad92ba75a2cc88b931d82b3c79fef8e5971f228e34168ada07 6a5cd2c7a32cecd50514eb46046ad996bcb2d1eda668a7f82ea105756cac2b08 63b28fc379bba0a51da8d41d1d89ab5cd2d5e514fd7ce0830963bbadbbbb236a 15af32d28b4350f74faba181075479bb3c5527f1ed7172c00ac28b703127b307 8 5c15d96f21e6f1f37c552e318bebb2eeb14522556c7bc7c79be93880cb90650aeb90ecf478afafe08ba8af6a74519f984510fed90f7cdccc1c02c8eb4232b50c93eb6bf4f8de91eb67dd6501b6cc547d97fdcf628513a23c86e4cd79e4a539040762907545a22bfa406e80f19175299a500e7de78285d3c5a1f7ca530d8a3502711847613b57892f8e79d6539f792e4a1e5d016ab60846ca7d36676009991d0b3b1941ca0668915c1d363691e57ac37143ef313e10c1350169700533b969180d27bd05f4b9d1bf07080f8a95df14bd7d746b99205d39c243d353bc5aebde1a0a84e54d010e82f06cfc01cfd61519ef4de5b870b1722edd0aca2f6a5d0aa55b0039e21804380ecbfb1109b449c0f6e6f1d9009a1b87745ffee997b05bb6f05b0c43a407c0aed0e59a59fc10a63ad2d97087638fc11f2616e844ca92ddec8f19075659c46b2fa93af7d0ae7c8c32749823ce25ed6aed50cf12f59a748c0a421f0bb6da8ec3953e854b64150fb3801df4a71e42e00a459ba9097d2d02366d79560817034899a5aecbe9d25e97fb25d46b2a5e36594dd8603ab4cb4e92ee927e30015b4227bd3187bd957d8031654a9491f27dc312e086e4d168493275376661cb08ba7e915754eca03311dd8a4525b6b793e3cc8eda934bbade7ac5aae4ed1d3300acca58c5952ae556f8b9df8fb459f97df4c101e6bbead70c662e7cdc29db7a0dbe3a7581e41120e35b2af56179379c28f2e2a6740ada0d8dfff87eda67f9620daa304c33308ca27b3083b0b31b74a5e3476d4141d5814cf96d9ce0c3c6c6770765d1a45d09f39a19712e30ce2d0d0e8fdbe9aee7ceb417517429c7140734710316b266253260da4cd301d0c471439207539657b9a2dd291597ebc2ce49cb0b066e79639f14c9a1931b3a4061f38700161b7c25991ebc9a7f479e4e7e845a0f0f91d482c362fa19d38249e02583e8b6ad2d6fc3470914f45f1182705716fc770541a0eef814252dbdf2aa06e3383a8c767a528b72b31e41283bd3e9881536660b4ec4d9cfd557d7ddb86ad60f12b32bbab393cfaef13b30704a1381745c34a20f81b5637acce21c3a7398295ed5691f2767caede8c1b3e31ab616964ea114c904c0894339d7adbf5a86261be663fb38986eb038664a0b73c1fed9ad10b264840ad1e2b9e8a8437f96d93a19d8e2ccdbb5a843467070746aeb09e564e1baf01b09be5bd2a0708dcb4ff9abbe792ce1a7ef89699d6cbb67f5569744ec4aafb1860812079435fc683fd0a497e5a6d0f6feaee14391918df417f0c4871aab7d41630a8f3061b1f7824cc767e5f3920ec957b72726f5b96f2f310e98809a8a9861bd073a00c57262d3c970606f72fddbe5c0f8a48d46be8e834992b8a71dd840f529006bc6037ccfa323ff1bdd3b9206b967a2f056cf8ca4d9c11a8e27a75fe1e4b908033bbee52cc0b33773874213ce079132b3ca8d08958d65262f3ad6a10ff5380574ae35f5809748adb8c1415ece2ec4b43dcfbec288efb7fcc519aa74f80a9b071d153b284701ea8f683bb23ab1d532349a82bc08ea517c7963733d372097790fddf0954e891a2acb8fdaf90bfc7813410e71ed8b95b2f8dc1db8c0d7ea4e4907ff3edbf711f02b7204ec56213a473a12e577ea4e4fe972283701f2fbaba1070ff8db2b296f3f0f5a62c52eccac6a0634d15162bc50867d73223125bad85a180d -generate_ring_signature 8e7b0209eaf2cb145700a9519b958e08189855775468f752b72dd90602caebf8 e128395e02c5766dfe8e16fa00f544a0f7e6cb5e698dc00de3683278a33a9d11 1 702d51626defaab76f436ecbbefb7a1bcd953735c1b585149a4e6ae7c87ddfec 9caeec6bceb4ad72ca57dbf1562d53ea0a93feb6a69236f8166f061d93f2c901 0 0f76b858369550c770ac42ebde84b23ee1cbdacb8156108d01b4fa3bfd3796098c855147c24b4cb0878dbe70afe1c36ee316589cc134420d8d1417e2c6e27d0f -generate_ring_signature 8bd72f709b787215cd41c9f7953b0287d08f4e6a73780b558ccd03c3934b5918 8d3904d2c864c2c47ded0b25d5e6b125a902b075738deceafb17fe51df3357bf 2 ad75d42030a6426aeab0303392ce21c936df265c5557d800604aa7c3c0e390e7 7d31f2eea57234b7d6d6f09d4ff58cc4bb7d4be04d9c40fa925706466bb82f0f 807012275c28d219e1b973c1ffc62aaf43bda1b49fe3ff094879f9676253b102 1 deddf650adf1ad27663c1195ad71e87025cd7340d2f043f61268358ea13ae20822e8f8a267a1e38538bb1ae5392709e094fe8f234fc8cf6ff9fac571a85e480b4c0bdbdee87ee2a1b44a654585b6eb3162d1b14b52bd665646b2e0d42d07940a5d900c0d3a418aa612c6b45e28f23ec5447e8e70461049361e5951cbba77220b -generate_ring_signature 21a6e77355023a1ca3e620126ee0d7f247f9a814e40125e2586bc9c6773928cb e2814a98af57ee26de7edb7da847473008e128902c90ae796a9980863d8e1df6 11 c81a625349ab970763589667eb533b4fcc76f764e525ed10673e5bae74bf508d b2143c5206e143dd966e38b3d6e785a87b631bc5dab23df88c2e357ce6753e35 44f17cfc3e461746a121975377a05cf87c6bd41ec84a480fffbf045a4b17c0d1 737dd0a57c5bbb2c5038fc3d084871aa281d087040d47b5cc36c381fb25e0620 8bd71c4407f1a7e9048cecb474fdbb527f15964bcd2f9a06b540050b372bc542 f31ddccd182e3b9530ac13eec95786b47013c9566499c20fcb8b31d6b3dd91cb 074096dcf159f4a52337d58bca2b695a95f087125676381832a4547ae6161d4b 8536caa0bb2488682c0c5eff2a2cc7723dbe1741e3ee28194228235a363e7f6a 3ad9404f37be6a07cc9de38bdc1893c172c9674cb186070af90ca0cba027035c 9cbf183bc430c397a8eea5cf733dbf92f7ce3a0365319b979dd32cfcc3285cfb 817989a15ed0e35bbaf2d62d94a2a6bb3025c795f830753679c3773a8dcf6cdb f84b370bd4c5aee8c383a1cde66c5ccdb497c2266a6b15e53d4bb6cdc0535305 0 fdcf73436a15f9719e40e4f516613baebc5bef3e16a0c349dc98050faba40c08fdd217834fceea2eeb6fb1b26183f1c391bf18514d144f3cf948fc344107ac0de9a7740949a4ea8027a254357e28b6bb22926809751794fb1cd60d5626b48d09607906ee235d2669f2f9eb0b75523577e082664abfaba3d5bb3b19d29ee09a0f8d24edd03bdad7376b9be8c727eb1cb88bc913d9f24d62adf671f94aa1b3ae0fd62516e4774fd62ab3bd031e3e34096568da1725cfe5b47bac6734f12f994c0168cf5af8ac6dd2dbc4243b4418970fa6365cf87ddb2ed92f2d9ea766fc14430a2b41172f53a5a9c676ba7fd9372294b379549fc476a2df4991878437fdf3030017fa4fc831ac7ea5e0b251f0abf12bc460ee8167bba7f5516c28a3bfb1063e00b8d9ef5f0bb0debc015f77fa0a5be2db59bc735eaf5ac3d40fc5ca93ba09e408db166493edd9b417ef502a8dbd767bebde023bac5f03e0c7d7288bafbd0a2f01b9b4c8c0f890bf04e5662d76380d5efe870aeeb16bd1f848b7a323920f51840daf53c3efae57ea72279c85e8cd8aff4fa2f10bf9d74263fb794e18b95b753801d2a9b241e9481c296ab19a246c1a5c357a9c031b5de0192a6a6b0d80735b5401e295b6adeeb5378c09d52909abb3a7683a8eff68860cbcb3fc43a6e6dd0c9b04b761c51ea626d8a2a58e9e525c061ae0fc19fb8daa0c252e65fb4bb874e55a056bf63e49405d6c248fa3c2ab492936afd7e06f6c8094dc06237f640b9e986a0fade4c1218dfa65025295daf83429cd5aa062027362d96f3d64784fafed1d3507cde328bc85c87d14b9cd8ad94a4b4a04a146c12598643cc13457d34bf2feab0d8de25858e3046ea19944cf57d0f8cafce406618efd7a1ec78cd947cc9914120b558b1ae6b72a3ea581f3a331f48ba5272af4f75cba497bc77a465a55981f080aa94243f1872a8b0765fb471d625ade22d903451f3deea9b39bd8488221eeda0c -generate_ring_signature 267687c04961afdc4dd6108ce46e82c9c5a60a6db9b613cb071a86dea4069053 1f8b194a5dcd594d315df03b43f6e6c6d8ae58b51fcc06678c93ef532a7c2ace 1 6bddec7e07d75ea37fd6fcb93b7094d1e73a56715a904647d5b0d572a6d2413a 83e12ef8c17ad0741746e1edde68f34fc3a241e9d612df464f282e449ecc600e 0 1a09dc6d305008203c03829bf58fa4c96552dfe65348182cdbdd545e57be87087fe382cf122737e2b8e850344a287776140b16bb0852259cb6f77fc09cb6800b -generate_ring_signature 9310255e1d6492094731550312eec4fed0f54ea5d706cb2a611434e94de2f67b a6b30c2ab8c81f1e2b826258e3800503430825f7f111a5643034ddaefb4a9fe6 40 41d5a10bee397e496d882d7dd820e3854bb222240d4fde670ec2340931bd16d4 dad6b13bc8910f762c3badfbef8a686fb787b6511eb20e7f6fbe390036b2d3a6 100588d38cca1dc9c852d2f89a1f11a246fdb955ec2f73b8d5f8ff3e915beac1 b8b3ed919d7d75574bad2a81c5057b596f62796d96acea022784cb653bd4ad0e a0e37438540bd74fca28cddf345ef304b579ca4fb60457b02a9438dab0cf09f5 e5971444bb60d3b5cf42a088f0e318d2b3c22e356f97457dbfb7362d91f866e5 a159413d63e3466dc7cf257d0e5c094d0d5f87003c9bca4a00e0898fb137a364 38af861b033f1aef2c996952cd44a0120bc4d04d179e74b2ad9d21596a9b8730 bd0a348d3280f6b986c74e6d3703729374158bf1a069701f4aba16f73254c0aa c857108270e21b98d602fef841b56f0411c12305f38ff38d14372c8718f7d2eb aecae0afc964ab34c2f5564c220cda5182896dbca21635ac81026d3c29d0feb0 7bbdfc571e773e399b569f4ff2830e8d9b518d0b5d113c66c78b7f4286a97848 d4e44123919ce55ef7753229180e82335c43c064d515bbfdd16af5459e6fce9a e9454bb7137d5562ce7fd1a5d387441792a1d252af2e7f8249da3c143e7ca5f9 f8ba4561c33a80e40c7ca29743aaa5f9ff45831ea41af4e1835af4843311b5e7 562e1b85aca51bfe7fc455fdbc0060a3107836ccb8e72f197ebbd4cae6c38240 314d5ca7078b9cf66cc9a127a84346dc7a07934357ebff1f03ebb46a71c3b29e 15899bb4e5e5f5ac515e9202d3be847d7bc4a5d2f8b10606a5c25d8deea117fd b088006581f476a6f974389367a8d751e3e77db254b44c37007512fceaaabea2 ce728b492bc3de4934263add7ec0e8b8e8a9befcbf6d303804e8d543bd00935a b18e46b791bf0f12877386efa7dbbadf980d2f090fa2e59826a8eca0417cbb8b b1367e0a7711b395a74cbce696bb16102f8bfcb550ada075fdd28ae6f91ee820 f9d2d946be5236f38240e7750defb8a2e6f9c938dabc5773e8fdd8debc50d6fd 4423c9426972d73a83f73448e6bca19ed2edf3230ea018ea9cad3b1f1d76c4bf 97522c269b46cf817c95aa9fd186b9c09f9ab2da7af3ab55bd562d3cf3b51ed4 9f9380bdfa737fa00f31bfaa185c89ddb4a431e18f4a17c12afc3233e236483f e9544769948638dd359c9fee61317f0c93e4d476c457238c7f8741d4fdd20ffe 033aede3e0d40659cef397ad6250db441c97dea5c26ae4771a9f4dbb1941cc87 a436a016cfcddaf1b1f1fd9dbbfacd6979269f0772526309cbbbf7f40fe45920 d40b3ec269a31e412505088853b395405ab22a54d2fc0b7cf8f36d3746ba3e22 5127aeb4f206ecb11b191437e41ae3ce2e8734066ac559921a470bd656d56c24 89b0e22e57e9182a1a7d02ed51a5e6811f3ac915de5164664b296033124ec6ee 5937d8236f189d12fd6cd9a2ba68df4d5ba2bf77e72ed9449bce95cc0105c974 bccdc7108b06aae759d5b4d0e7e03ae74d5dbdacb6c5ae6935289e231a1bcb7b 15b5ddbcf9014f7f31170a82731321e9cbaa1d3b1d374ac8e9baba8e301fa1fc ece21182404939af7ec56ffbc46d8a72ae114945aa53baf864c7dfb925506b47 fec8930653c39e756c9ead78a921a02bbf3cd46e48053766a3ea05efce314417 bc04ea71c8a2d6774c477819f1e2be43fb4e8990b064e6c752756cd8fecd2ded 373a4b62cacd09fd6ed9e3b8354445b8316d15c11dcf334b28c2bb7e3458e848 c81605bf680bc260a7090ccb84ffc13e1f1e78fd94c2320642a3786f3fb2bcbc 299563329c5dc60a5689618c81e1e4edbfbfe96dd877c27c30e96258ed287405 10 8b1725e96d02a7a7ad8de326493d82798538c77d60f0eafc121b0f5f58535403d13c9e19aee1e559e10ada35ec1c51e534da99604e8b038f8b14db35b13afd03051c9804dd3c950bf536513b0688c03665623b24415ed0f9d1fe4b6a491e7f06e6c9f223662a5af048a2cf8f2195ca0e8593edf502cec90e95e51d02df853b01c754b47aab9005bdb80c60e414c61cf16583f7219a23a26f7c2c3893b1741e06349aa1e33c60379f69a3bd9b093a6494f4308b754e5d14722059ec0d34160b093282a1e610ee1a5fad788f5ea8623ab978e68c0f9bd9a5fabdb245f303c1df062db87ffda7c2f83e314b2f282629077a2ce130fb423f6f9d5af54889d84f710835124c3e75f0ba814019226f73cc62b3e06c0776d8086e5ff4ac60b2ef23240c9e18fead0637b884f6a58d0d04d3d367ba6a43f7ad9076532f3bcd83bf8c6e081788bd2e7a80201bf7aff008a7648ac603e531aa59c1d238c215e7a129c2f40a3760b6d329b8bdb4d655d8e93dc7fc148d55c3b746f7726f24acd32a79fbc108c1774c38c40ee3ad8bcc037acb10c45496a8ac35763a268534ddb93f6eb2410d12002030bbf1f9044683823622f0c4968bbe93a38af01ff265c7273f366a1402bb70523e6142f7938724aeb431038645c7528baee9aab280278fc9ef9ad3d20b2e00f104a067b18659534492e72b1f61b8a1c949a84814b23611397c0c356a0032e9f6afdcb932687f4dc93eaa4f076d17e22f2c79679e67697bb7229c8e4e072e6c1c9aec503ad2508cbb64db49fbb5afb74f6e595d3e1dc5249f37991aec0428d0b7ed97ffc673e5a5a42dc978645576b2eedcb2b6ab1e1d7a096d33d20905758e7be72e73203a49b579e422dfcc227999941b2aa77d3d3cd64a9e750d110e3405a95e9687bfeeb43fcc62226e5b399f19558e8dd5a741675be89e5dfca30547dccdd6393991d90f356f86555b08fa096c4f3176376ce2b8d835469565830cd8efd490605bb9261bcde405803a687bb739bfbf0c545d1d3612928639f305050e05ae2c24327816971f969a53ef3789af798135d5b483fd40254ac67294ca0d81b937f3a3c10eee3d9f9d2eb036b3dab3e338668331f5abb9213552ff2e090fb16b97312042ca9d5e83ca5321607c8394fe20a62af832a81a5c3fa105768204870b20b05b23a862a20d17967dd33070754ea52e1de6eb8b269b920c18ea3000cabf7142d1d148faee8e503ed0209c469d52e5afbaf2a598ccc6d379d2ccc505d8129bed400cd07fd74dc33ed5a303005df5d9f087040188f96bb9d22295130f70853de475f0ff0f029aff1b62c9fb6e43e30fc2ecb41c8efbbb49c7bcc8f60b9ec892cc99de654b5346e9c1e70fe59be749bcbcd05bc617144208835ac0d30aaa30de2c570dbe49996faa6e35d5ff0bb8f9b1c94fa926d47cad5d03c716bf046a08a2711a6f760c5f06e0df4a49aea3f95245d28cec398eb1b7b28359286a09a9b640e5ed71b06f7f2bfb26bc6a9f75d8553a34b69e9ad4f7d089a20cddf90d5a3c9eb132e2ac1e91c1d17b86634a27a724d390c115f356d0d6a166fc7f9600f2f7d46a53ebfb77d132d39e4a8e2b10d4cc207b4407c54b58aea342bf45a6043f4b5b5bc1daf1985afa4e1232b842b0289b78e3de87a68645acc6a761ff38088fa5fe5fa8ff700c2c520d5a9864227febebea59534860859f91f82db99c3e00aa3a6571b972db87b51785497a86a24f11f360e542170f10c41ce6d8d9da4c07ca1c61453fc069f9c45c99735d5afdb27621b2a0f19b46b336853f3508b6610670b3c469b157db4b0b47b03070668b60c37dec2abf2e70c5f0f0a1e371c0cd09b64be9fc9a97003c5de6ead72e460773fb976037c9d7a7703ecaa8628fc45f0f9c288691bfdada723e5d2627faae032a011b13088c6c93daace563722eff8c08874b200667e76d14a021138f4b79ab04ec376bc106f244944d16856d92368a009d76c9cbb9c3090648b5ee49a102715e9c6f012092498cd76a7ed34cc5476d06260ca34e4fc3a3b1ee0e99ea9df001ccb0d427de45b77e97a9de70ceda41380aea458b236b42c459f13a1b13591c0c196ba97852024af4a610137f4a5cc9a90fff59c84faf0b5fcc601dd0f62c73a217a3fbe749f0bb8dbcd0f2b745331e990b4e15741a207adb65dd7b8256f2a1fb879c149dc4f25c509113d37e5cd93bee053c7f51fbda2e70f8f5f51b6cd0a8fcfcbdd18e45bbf9c7ceeb1e2babc9ef960059b616cbdbd8d33ef2ed6deac12b347317f3762b7078d0619abd81ef398d170788554d85406492545748604eca1b8477fa13031a79acfd51769ac0584253eb0e183486ef0589c82d7ea8cb716b579f4446c179adca1665b9aaaf58f5e3f186071253ab3e0eae916d99df72d3e4aab2c9499ad7e025ceaa3e864772ea0ed80f01a353ab3346f8b0ada6047cfd05e1c67720104ac39ea940ed82f677ca7709060ec69fe21690cc59933be5bbe83bef92522ea116d991ed88836f3b9c9e2400770bece92843682c62c958e206394407fb31a08ba876cf90ceda963c96537d361d0b91a40651b4045c512a9cbb72f2a240a4dad87f03c2fb410fca545145893dca059b2485ed45734da327a4efb69e50f912170c5204985a1efe73aa9e08c47ee508f055386df94d6a33d123e2f8659e286a8a7537d052a5b0d024592e5a8b5b0c05112e83cad8ed34d792592cbb5ed1cfee8f94d9892e14ab034b0b78c93055fb07dd504f192bb36f02c6f91dadc6007356b17c3f2754bbfa352a515c2b6100db002009e66d8ce5f2a574e8476eaf95e195d4e815e35aa734464dfdb81e7f505207b9d3805cc3e5ca9b672a5d0d715650f9be4437e98cf998de69db3f32ab8d6901a1c2a1ba18ad7a4a0d56ddfd8d9ff963691bac3c39f8a46d7f65d291cf8b1503cec622a744e389570db43d81c66313e5c68601b0b8a8a2bf4bec56589b19c500603c051f0c70016ea996efdea9a8d72cc1d5b5e7e725534324eed869b296ca021483a7a7aaf1141b9860df16003f28507223bb008af2142e2d3fef6b87b29208fbc665cd3871f69ec4cfc649e9a8d7f47f8de63181766b77362a33f872877407ceb01ff6a15bffcbbd813bf0f248dfaa453d6411db1db172d3d56cef41f4e003c497c089c18f400ccd6ed95ee5ad8d1e21d4f0489e63091b574a3b4b9bc7d20e95b79dfa09a0a359afce5322902f4f12058f1626079acf8b0eaa40ba685c100fafe4ad21b561daeb3a06aff79306c4bffe5fb5f4f98481e760331b5f30dea50f0e52c443743ef1c0f2baf7b4656dbc90418d99ca91895a7d4510a22c5035150493676ecd3d30c756b5b93187c4380323e80b60d7b3ecf1e0e6c0e5a5e12720076352f0ffe25003eaf33bca82d73d590a44078eac4edc5908a180e1c63f7db30ec6119164ee7563f1734f32313a61d509dbfd9f90ee46db20a5c5bcad907f95010ef94a963cfa44bda2bb22cae00e6870433879adfa64e99727f108134a1ca3073428356799c36a5dbd2aa694bbee1c8e964a75d869dd40a72cc535aa11d0620b2a2e134ed29551fb78cb54e6590ae1acbefdd7f7c2de560f929b10dea978e002 -generate_ring_signature 657d817c2d4d9a393b1d9eda25141349f6daf11cc4d6fd7b8a1f279c12187e46 e8c62926823b84157dcb6145174f85084425a5c705ed0d37842430352451a394 1 6ca123601f7636c3372154cbba220818162efea295240761066b059d12148b96 eeb5e6852d87a410c402eae49f12242b35702470b648824c817adb253d119704 0 387666ab1fdbbec57f7206fead638de5d005836362d02cfa8c4271980e58ab0ef23736557c26359ba274370859d9708f240112a1b9ec84e1e09a684399c9610c -generate_ring_signature d2ee8055b5e9fd86cd9e9cdeb95450590ea7a48e976337e8af699593d15d6232 cd23327ec509965ac07ac7f31509833e47fc56b96dda646223d917c02c292ba6 1 ae47e38a341217de4e0562f9b146299c0f7cdb971d314ace7522fd4fa3e20fb0 8c4730239e8f96e53ec2e30b856645506887527e7b6ea24fbff166a727a74007 0 c9dd42ea905b7379fad61d026796bbc744f611e23068f99e19129d95c3e3aa0bfd294900481281d63bcbeda8eec002eb9a11fdee8ccdb88a570ccd5cec072e04 -generate_ring_signature 5f6aaa96065ebdadb91cfc017ad7e81c1052623a75dc979d052c43d025572d46 e96c5739f208695639dd5120c89672abd6148f185164bbf7e17c643f2441ed18 1 71b0a7ac6902754f376699e4526f3c9882787ef2ec7477dc2cca2b5d6791af28 0dacdc8c2bab9aa153b91ff96e7dcdd6736e8829632e92506efa068500ab4b04 0 e4817dcedcf759aba2cd024d93555c75864ea0a8b9245035a0b4aa797e11e4084fe217b0929061cedd3e86044f0f160e19f5f1c1647fae652805adfe12ec960b -generate_ring_signature 210c782e19ba6264cb91745cb5cda5ce13b234ed030561310e88f6dc2722b483 b439238aacc9b192a9d7d1b73f845d24f574997d88c949d54d372549419baa7f 225 385d632938755662d282c477ad5bfb19bb1d153e5815077216a4c4e2b394e211 5e180c966833b87b3003a824abf2c6966441760258c201f7002e823306a9c2c3 5d040395bd315a7bde66937473a7d96ea72829d2f2f868258edb4cb265aec30d b5f068ea2e517abcd21e927c7ea996c5974cb5c681805ceeb348244594a342b3 327d212984e2e84026317ccd150142a955be2dfe34d19e194a4357bdd221a9bc 752baf4d17686fa71a2534a6864477b608ded4d869f847586fc6ff4e4d266255 05c39810cc10183513ab20ae2caab4448a0429f50673b239286c2843b43a9aa8 f60a2de08e5a77b7e56777d6ef84a5b6b546e6934cfc3a635378c9af0b7e2f1b 9986783276b0dd845c3b0c56ecda1b29b28bd7a3ea155ae6fab4376012a40180 0b3b7b391bb34851c1720cea7925614177f278deb37b16d52197fe8aa45118be 9d41a41006737c046947fc176183ee7baf4c37114479616e89d7517b9d039a56 d057fc6060c31d7629edc250791ea60c9f49bc5ca3872c6146030bc08ceec217 64bfe3083a136ae58ba33130ffd58a60b338245e368db66335d4844bbb0e25a4 4b7b5ae9dc755559da17de7325ad60d80b4f442bf508e304d2dfdfd9a3853a5e 2a5d589b910884a9267458678333e8a3818a71a45005a7aa3a9e088ebfcb88b3 eebb3f88b3f7951e243d7d8db9003edca9f0d9001244476efd46074fbd904fd7 a9ae02114f76adcf94ad23a9079337f1d1edfd74fff2438ddb47d0cabc1f5af3 db9edc21464cb1d6a8001bbb2933214b5ed3a2824935d63c05a55bc5b681953b 5afb033005752c35a5e1fcc95d2341640f532d11107b3a54c212b610845db203 8ec392866d432fa11d21b4293ad1ebd421fa859ef7c5311eec1acf73da95f366 2d372298a9b62e508013d83cbf0d95ffd72fc8fc6fb0d623db98a47e30640eb9 7d13621c703d7bbe44a196819e192422a9211e53a8a6ee138d99f541c7c45bbb 00a26e1fbae064a1c2c800ee32ff1bec58ea68e6bc3d9319d0e6f7d9d4858943 b3e674609b98360bbb85e1f9b80b779453ae635c894adda35bd100e7c6b1879c b9fd8cbae04c271e2e59702508d371c15f180310b6b5b16bf78f92c30cdf3451 c7d7b20e3bac8ac1a104ce0015b29d6ca9f66e4b921f3b7760d14b0d0ef806bd 50771023cdca6100850cdd2f60cabab98e9e42e6de099a1f399b898efc79321a 736e41ab6937e217c5ecaaf37eb7bb7136e8f9e29089c8be9567e8ec9bc76002 52cddd12cdf1c506e78e29591392fc24e7be5b9c5b6136ac2ecff15a94fe0768 132e70b68a7ef9c5f401b812458745e73c3b1143fbd6d548266d446e8ed21800 a5c2587a5b911408527f894750bc2fd3ad05d88fea96eb3fc521ce217cea614b c36d3589279a9754ed703a49e284243064bb8201823bb1e6c65ce1f1cefeb3a1 f352384e3ee128d4d9f5e53677e193699a4a0b32b87b494abbe9affe461c8224 623cb978cbe7068f3f5573abdbf77acb50f1fa21456d6c426ee7d14d925cee2c fe4eaf7646a7d7edc86ffc7bb3e02ba1bddac47517eb676a73c8cba574ac7583 74564c30fe7b0bed8908ed7550c7a1c0e81dcf9972c5588223a5c31f7c5f24f4 3957b9c95219d16df5af553548404c6ed013feaaaf3311ccc19767979f81afac 52e16912389caae211a4ca91048df9d62541505cae921b1adecd1c92a5e3106f 9aeb3d3ba84f8209795c9fe000a920d7b58e361c2ad09856e4b385cf1d3fabcf 52e159e9ed060899121461c71906a83f47c1337db4d487590c1d7b742e23e29d f4042896920a3546282ce109658cf0f1535d98aec685a54bb653bbb52f71f560 81581d762fda656949b3c072d157a16cbc96e27f73410fed70cc2d816ab76632 01d2d48e6e5599d24534f3482427eb4501508b8c1068583f7354074462aacbfb 323f4706c986053428d87e580cb45426eb38eee25fb956345607f71f76e2af64 c48ae1b3e86ab129abbf82123f987b773cda8a621313d5e3e12a82504aa4b4a2 764a1d46d895c15e2de0d8953d4a349c11ce203bd8fd5853c7acb497040e818e e59cb3843b69071cc6a876a73433694f55ad77999c1f2d9840cad6f4d46cddf0 f0c7fa7f51616a2b8b3d3c66fcd1b999ce236223d899a24d21d7cf51c7f8320b cf103bbd9665692f9ebb884207be5d48260d16cc118184ddeebaf8db303372d5 718247883127bebcf88b3c0aa856d2c82f205f0ad882cb40fc308f011d4c6ed9 12d01937773a71a29ddc4fcbe01005e7f3736b25414dbeb69f1f15504d65cd2b 8570f352e0cce7b04f9a415cbf78ea49b22c6dd1e199d2bf2a36eb7863b52633 72f0a2aaa9927f98bb985d97aec6cc91caa36f06ae37fd49e717f3c465943bf6 955bf1291296b84cc7f1a01a9d38145a747cac1f901739405cc358c1a791543c fdc3c3ca79c46bbd3226776dc9ed47e1ff3f3a7748860b9894812132f0290cce 5277b50bcf94ad33066eaa0d94885009bb6a2ccecc8748ab5d2b053d1c4bc8de c82775ef48623297b0ff928b04c03b1502645d58ed9c111478bb9c158a6245d7 88cd50b04d60e049714273798db1af00d5106280849efed1a9c1e37ffd97e253 d77f732b38cb637343ec20a66da97c5b39c9c3e8ff4b84a444e3d32164612791 cab9c3f461b91e213008d896bba641ad24993f98d1c7c872626c00a8b7c001ca 4c62c3d7c18c0b5f240c56b6f20e1ca4eca30b7d8806f66aa43f5df8a8d382ac a70644c6f3c2cabee8ad982a2994c09e81962abdc94f0fe48a5de7b155941de9 3ca3119e94ef5a78db0e52495b37ee42d3d87917cdee4cbf8ff53989939b626e 0df77c493d22bd419c3db7ee8f464acaeb6d83d607a7bd5a6083a62c1b318f94 011fdb5a7173b0924e03210b8ce3d0bf52a5982574faf3b110739d0288e5da3b 3361b6da469b5eacd5a5699ec7474c764d1426ae5caab18133252b5d186c1f41 eb68f27174f1dd2d342ea6be42eb3635fdd597031142086cef689f143e149faf 1fd91c95a7eb4552eec9f60fa61d3f904ae3c8799fa1a80926dea02cb8b69fcc 2fdce58215cfcd09250e3830dd355ef53090b5f8ea6c65ea7db805d720ead1a2 b1678ccc6288b290116e3d8341eb32e9bdc0e7c4bf1872afee58eb9430f82c45 48eb663ff455d22f43f0b6cb27f7d5eb0943d4b60b683717198002d7b76bf4c3 2423895ba50880058a75cbbdf5a535ce176ec31a14ab32d9de4dc3855156b8b9 06043986a58cfa6a7fa0668668b39f297b00dc2aa7470bc9608021165442b15a b881c686c1b423b1c36eca6246e5bd5d0dbf2f1bcba8462a008ca7dad7b8ce9d 1222668bc17ed0be0cd5f58172d8c058cf3869d26b201b850e46e2d1ba6528d1 1bfa0e56cb48d57372b35238c12746ee52345d52526ebabc79bdc28ac559f1ac 0dbd0538a75a12d385837b24a7948bfb3e7b9ec2e10b05516396041628e18a30 99910ae1888e67a0de161a454574c19c1f008f1c8559204e575db88dc80f1a94 1f000d2ec665610f784536730adf1b3e5b9b9db13a42165d9544acb881b0b92c d4970792197ccc02b043555225c505be8495eaf84b6a70d55139dda4c63b20fb e4601ad66a30cd9bf6e8a7291282a91731c303b459843c73d6b3538bfc3656ef 256a7723b4f9b7d7f652579beb53a1de9e7bee8e75833cf73b259452783f3251 613669fc70b3044796ae69b20a3fb3ad32f2e0e64d789a00dd880ec974e6afe3 0a9baa39c17da06593fefecc921b5a56b08e41dfae48234f0fc23d8f6a376ac3 8fed0f0e6b4d42105fff2fa2f5d17c1a29959a58edb4fa15f137f0676c6ea7be 630f0d0f9a93c58f56d9cd2f6ffd88e32588b5a2ceb0e1888429d3a38db25ce0 7e12a83617a864086ecf6b8c440f108ecbffc96bcbc8df5f3eb3c72cbd5b4e12 d267956f1075d0755c7f76b498b63e7f51c6e1c4902067d3be637346db102f1d d2e828db06faf7dd49e7e5223c21231a3b4cc4c3342cd71a3a92f06d727214de ce057552eb966328262a82f0106561632761a1872a5348c00e2fea537a946a4a e44b42268cb4127b29e47f5cc6499e67b618afd48462eaccc13477423abebca8 f9f0712fed976ab4091ac0dc3c15b3c2dee4bd54eda3e08c6dd2f9a4e4acdaa1 0e65a84589f21ec3b9ab6aee1d24ac1be5cbf7734450bef6f523b1052601341a 12fee1a3b1c49e1bea073a7bfb93c8879582febb9c28354ed5ce0268077fd176 4bc6cd65e4451fae3817ae3210d8a59303520d0eb1fe4c5d5e43d8466dd784d5 b96717f2b1ff0ae2dde66343dc8e00268366d0e8688337f6ab7d18508968ac8e e095f6be7cfbd4143c065b3f74abd9efac0552e91bd66c50fafb075534539ecc 750c8f66ef049a2f66dc488f51a125873b97569c692b9c31040bd4da3759d5ad 7612ac603bee6381390e5a20fff937b2532f880a0e51671f2641b67d58135f60 ebf737d8044309797fb792a059b02311943c12876d3a5c1384166ee2b717820f 80849e5e6d4f4415d77a52dcd6fa289881920aee19735d03fcf5979c2b001993 c97589e7bce4464b1f1921265e8ec6af29040970f09f2c3475487e7ecf1d489d 9c407322aac4ed8983ca8b8dad19b211f0c0770195119157fe44f1bfda932ed9 31fbe13706f2716dc25b149e77da52c1ecc55b70053e41e8bd92221b1b385783 b6ff14454cad1917030df735d2c9220b40880d7fb0ed08841e9e705eedf9036a 06afd0020bd36ca861f8202223febfb21eb5c7e338c907a4ce654e457cb2a315 be6f0907e14047eb8fbfdd1f878550770c6fc2630832b81948927b6f80617372 163f776a0f464f59a6fa6bbbd7e93aca17ab04b04b7ce06d34226bb7e900d7e7 f1550dd1d473f38ec080a6dc9872442350179aa80ebca3afe3286c61b1ab732a 38610d484a664c9cced181b42bb7a96a076921437de67f6be1c922e07ca7ca25 5990aa4aa00146e517e50f3eb04336597f2f8deab18ca1439ee5a90863d4a72a 50d7699edcd2730a75ba37516553f0548e2abb5044ea89cac8851026aa73221f aac4a77bad6ea4d5dab8b739c0a01c9fe78dac1fec94fdfb1168bd5cd59798a9 95022a31b8a904b2ad50d8f60e7779fbed16e47c07b1ea65c19a13ad8ada2291 439ad6b57a6dc6b7dbe15edaf44d7582f0d6b316ff823d41cf85ab381d35b860 38bfbeae4653dd4124de9a1a3e53bfa3066d4e5961412d6c1de12cf77d27fbbe 55992a2b6cb34982966bd89eae451b39c701e79de02d7f3440a8c493a51d9d9c 4b44ea04e75b073a4d597f4d1fdd414179bb7bc5b8ee9001a3c3313eb7a81028 3bd990d568b7f23824d36b441004334bc7d88d6552e1b1b9a6f772ccd02af8cc c6c13ff416fe823ac2ebdd4d8c96c9dcdeb678ce832218eaecaac26d227aeb93 384e2915e3003d1e5a32fe42c165eb31ce1cf2f7972ab7080ff9dc95146cb44c fa79bb9d556b1063924ff37f150aecc8590ebf234550f4c90135fd301010d020 a06676590cf910a41cbe07a80e3711ecea47d43343eeefb106027aeb81f11fc1 359028b96cf085cdd4156f29c48aacfcab8974c51e31d1920648d844c48503b9 f4fe5c34eef748c3c9a3dde8eb3a7cf88f894f7f29275ea11ad9d21c2da7487a 7af8f1af63e71d95ea3c26f57a9a3818945cde9f1f70c77678e501a6ed2c5b53 df5347afdd221d101381ca39489ae4a40c79e60dfe914640b7c9a15695bde31d 9d68f3e4e218ed9d94181094ec24d78d8e5e74462edf23a04e0f5c309a488ea4 f69e67c58bf7d1f54daff66bcb9f967f4a800bedde5c1e8e3388594d2b6162e1 4e522383f361282994b5df8bec583c9076915963c387cd8fb79c5a143b77b20f 649473a9ea78b524cd53b328a181eb40c0cc67f93545baed9fb9d013a218829e d3dc41dd315b7390789ae65cef880d5b87f4b6f1806877f4828313e8842fcd02 2f14539f942ee6e9b0f10fcedffc6c2928737cdefb8ec12d1100296ceb74d56b 9dcb4fea73d041a83daf21a9cf9cecc054749ce61b55161cea8533d0ffcfd66a 8683d13dab4da5823053c4623a860b0f30990ee197a528780a276f81c70b639b 700509bd43a4d2b1cbe027b952587f67bf07617ef194f1d182718bb1761ea6d2 41a1e9fda04748ddd5fec36d1178167776ad5fad58ca7ae08c83ca4fba14897f add8a8ea5b584b8eab15afe856925109b90a73f21700ebe6be92064968ba8766 e6db06fece8a3c69c8345ebcd3a5448348e3ba7b3fd15a05f2eea0ee45783d5d d4c104b6d17052fc549dcc764684c848cd7aa470cd478752b814010cfe8c31d4 e36fc9e9c235d35b997678b41b2702b931498e32d22911d3c47aea55503b0033 c46fd9dc260f6d25ceed78280b2d5bbc08657efa5f7b962e30e22ed3893f768f eeb7a4cde0a142ea63cb9580b0938366b3b95324d18b17da2be4d2249106d3ff e4987600c30314a65d4ba41c132d4aaa6df9bfdf43c9f6edb4aee71afb3b1ca9 26c450378bd5de84dace9354dc11164c0df30a66c2623ffc9821e9ba96573474 3768619c48bbe89a1929c7d18f71dc321ae9147c29e1e9823e15389af035f1fb 65c1b6c65fab69cf2026b344b5b0cf7e312d8554c8ca9fae97b87edcdea5f945 79be69b0f629b18930c38f588b76f86a2c91dd5f8ff77265c58004639539767c f5c4443cae1a272c3ea900331bc349b72f7537e4ebc4ec9995a5388ef922731c b71e544eaa0ba427d75dfa2bbbe03694da48ef2be247833d143f81812c7b5a47 b7b2457939c77d5ea8ffd71ea933e4efe986ca8b455e53e0ae148b4ebc4d48e0 10fca1960f5ef371ad93ba33f223b7d2851ec0c9c76f70b2d629d6581368f90d 988f89d0727350eb720066e198c14229bd9c6b30a4313419df9ed31503ddb02c 2f81e060a1c1d035ff2cd062ff432329b6b16dd5e5ccd3ba1e3c64907ca41351 335def56d9aadb5e550a9a87df2c4171ddedc3257cebd96a88ba2ed0b13f7301 1bfe59272dc577a9821aa956551cb1f045c67d4b9228a04e0b4449a8b462ff0f 444cb383eb3f041882c5ece5a3ba472f6cc4586f101c63050d8ca8e4ccd0106f 39c3b97d1ffcca4f7d0560f49f5d913f67a3903789743d29b49b902b897774c2 fae6d7e20a850723d9764e97e3856dd3863329e66705d8d0ee2b9889aa70c327 1a095ec877ccfd6ad7c098273feb65d098e1f573626e57de6ae4dc61784c9042 d562adedb07334daa8ef9cbbefaf98d64b24c9741dd72445a9e120e183a63c6d e80223440a373cda0d6adad064bc2148b320113d7406e1eada98f1de93472b0e d15f5a5ad17f8a3ba30847d4e85424da0114eb96dd2eff269cccb4b3cc364591 48707fd6560c4cf9cc8222491d45810fe75673ebe2a7de749e9dac85242c1104 8963b85abad1a6a24eed9b63dd46902766eb8b0dc13d02b9e80e3ed5dbfe6511 49d0fe1c91a4037348cd6f6b8c2bddb8da46214c33e8c39c0630483b83305ab6 f803ac0ae722d368b352c47dfae3852904bdfe9bc7d4642e1126a482d5e9eb72 1084f0fbc396d6ac455df3ca4fdc22a24b8d1e00b3f904ffcb6d2fab8729590e 4314465a59abb470b1e05dd90925da4035ba6aa20ea102c56b6365865cd05a9e bdb6292729ed47f044cdc357e7081b249a275ba904149ce9719b57dcc144269b 033d157a3b91aead40b8a4f029b61f2a40d39b86ff38e8e57b52c03c426dd10f 138d03ed71356e08e6ea8d29c57dd8b2802054587a058c80773a75bf74bfe371 397cfd01c6fe89a34b41873338fbb70b4dfbe8a954d895e88cc87ab2cbcd7686 292e2bc17fee6d9bc8f09c710411605ba57f8534063ee3608ef39bdf73ec4db7 8150e00b1e5b68586aff8612515be21fc803344d3d0329badb0cdc99b80a1362 c92a75c221dde7e996ae5f8ea43a5760563a0532dcc6360a60dcb0c7abc13f75 49bebc69b650aeb8ad9b8382ecdd65a74d640153ee047060db71536e5166e58d c03564454188c4a7c29c94b4d128246140bad2a621e400137c7c8566c3ed98a3 75ccedb4415bd43430e80668831b2f667bcb086778ca43805d9b67ebceb0138e 47a95d5a7fa6e64dfd42fc92f103795e8bb26d8548cda156075d528f4ea79c37 ddb756c6e856bf99b3cda839a95509f50a9b18b87c8d9e07395eb1f37f351004 d813a9adfed2a92f4d2a386acba181fd12d26e8cd139e67e39bb1a45775b3f8f 36c6a368a17becb5b42b43855f1a1ec9b958077077a9ae8b2b6186854f2d7b96 5698732e696ef2306c582231ff9d48f919f4f5d59bf11df3e2bc7a9511a8555f e0f7dd725ddf4ca25c9bc32f1d6b33a562325573cb3bd9f9bdd03eb15a568bc7 6ecd766abcb6ddf79fbc0b7b510d7be17bf5497fe57e54fbab5f44b59522eff9 29ccff9bd63867d4bdd3d99c79bc54b9503d3312ce4792f280da7435e22c773f cee276d9b36560b5078d93780431258456ad73e47b98741c4d885b7e917a620d c465fbc35b063492301a77e868a24728a238335f1eaee5e7c94173fd1d5c2bbd b8a805da943c09244669e79bb62d6a58e80e0cdf069445905b332e0ff2e0a4b9 081c277c8956e574ce8315e55a4d1bc6a1f7084e3ef79c565c5c920e09ccf7d7 62a0c62da5b16a818926f5568bdb97370612c34391831bb8fe60ddef00333e80 e98bb0d8e342ff5ec1041a49101a848ca973b3143373404be9a6c2045244abc6 4667b53e81243320173ad6bc9564fc5249fc89410c70b915602a8c164529342c 26c435b82bd68d0b7ce15e81bc391b2b60b554be5867e72a9566a101e27ea9e1 9587ffe6e571e2e36fe80649da50c303ce81f852ccaf2b2e9b7b4664260c00c9 3b28089761856fa94678ff62ef7f5ff7bbdeba5ab033f2bd71ba5850bccdea9a 5d92b8261d4196ba81ffef7859087b51a05ae5121554181cc687a4a9490699d6 297f21664140a48c0cb3fd6e6581ff9acafceb152c76fc4bc9d31d946e2aa54d 87d4a74090844bd351a393e7c9fcd7fdde317947281a8680ef950e5b71cbf8f7 1435d4678846561678bf9d5600b2a787b6339d4ea3b8187a47ab5c6541817741 a8ddb8e7b2ad56a9d46dfda69e17472e610b68af71a3f7ea908c9bd69d68ac62 d8fb6e4733f9fb707f77994667f77e4e3ca818bf8a6af8120e50ddb02ee65f83 8c34552da56de382f71218a665199132e665a1aacf11cdfbc77e5ffaee59c61e 098d1aceb9cc5ef48ba98dd731a3440ee771b9ba272a2c3f07cccdd192f76661 eec48d281e1ced0a49fe3259dd5b4834e22da58794495cb12b14c50f42f352ad 01bf24b8be84788a89206619db1cce831a71cdc4fe0b51c61d0d93144557c19f 53559b4a639f13b09851a396796fc6ca68211f3ecc2a00a687dc3a526bc1efcc efd8b29f481a043c230077c3eb0afa01d99835eb6c5432bfb855b4edfe57b43d 69cc325592eee89f8c1219ae28e35e515f167491b1a1a99987fc1091e3d7ec8f ee3e949a0957a440e8dcf6401c7483a97e78c90248ebb05bf3fa2259fa3b767d bda559c3325659ebeb39078896a9076f66ebdb69b95d21477d422bf892c81c0c 435a8f4867263036f7af465acc3c052161c75a6e983e8ada56e5a88fe04b4a6d 2fdfe818559614cbdc8428e814aef218181d888820c66e8e92b3d0e038b7d971 9f7a9fb7d041cb442104b1042a85085541b4e19332f837a6bf8200ecce1e4e70 97b37dd1b11cbd70c90f0c80fc8c4ae8ee7dffb1ddb1a9b89fbbf94a53152516 238255be5a20745de3541e9e269b10bdc886707eb44e6b7677ad29f57c2e5380 18203e567a0bc5b74c253fe7f3f55632d245f17e1acdd0efbd806129e2c57ce1 183149a1e04a061ccd380488268820b73083007d21f9f09ed559d49b2ed9cbe5 2dc198fb09f54ff3988d9c90d94238ddc96197722d71cb1793164eb352ff4a5d 45cbacaea04505e21430785d1b66aa47eddb172a078a578fb976b018b3902743 98938e4ccb446cfe999b091234c844e2a6648de95ea4b7408320b1b37a0157a9 d6b18d00da6c1f1585aaecfb15f8913fa65cf12466dd5ebc55fa84b28cf39e2a 56fda2b69037627ee3958547c0eafc7f376c0941ee1dbb2dd5885b63727392c0 b3538a6ac0908d7f225b58f2185a1735e2f0013d66a5fc5c7371158fe97b1005 4f4ea02eb4ab62fdfd94367557069356661e2d2032e9ad94b04e9ff3a7c44a01 204 2bc7c49a2ba3539a432839694e63a088a40f783739f52c1c16f59b58a0eab005f4e4a2d9b32826e5f4ac91a7bb029577290bff3bb28ea32ce6ece1684970a302c5c23c88488cd2eda93cb8d1e0d21c85760dce63e1fdc7bf6facbef037231302b802e151789a9a9f2dae420f2ad1b1ff4b7d7984bd281bfcb74adf200d881802865d6a565977f972b069808b625f75ddcb1ee8592e9adfa5a10f1ea09c7e9f0c4eb5205446c879f8269af3da94c849d1f906eafc8bfb6ab7cad110883b80f30c3a43906b94e1b90a2ea6f3b15dc7488161056fb2e05dd0df2c15d5cdddba360d6f3e76812f4067b63d1b3b4ba4eeccada581d382f85b988e19f895c6c5370e008b4dc3cce48c5c89589a959fa39580ae60bdf189bbb5e57a3452144c0d7de706850b14a67dba0e3bd72147ae9b3f67d1c27aa98c2ab75d6b321b5f5a8aceab01bba3aa0e4a805c6287c3c4e9f779929aebfbb700260c9d5b3b31ec23f492af0c20651a72990c4d91e4f5443aba9ad193177b815aa2e20552f21f7911387c110d1ec9b044221adf90ab9f91d01ce1be9e43c78a14a66b09d231cf3785a07f890affc91846327738680e3040829bc2d71ba789925e06f2b412984299468a145703eeba8ae9e7c160beb2560fa77d59eb239626cb886e87c0dcc994bb7c59cac705880014a663567e31ae9c571bc6f493bcd03a2c5c466cbbe9ede1278fb64ecc0748da9e9caabe6893e3674be5c7215dba3447c2867085923661332b6f6b132c0520ffac0f791731681481da3ba861816b8ff6e529dcfa61007e3af93d539d020e500df97ec69e0778cfa6e9379fde1de2e01aaa75333d34ce295a75f90bf1b901762059d42d76a8ea5cb395ec1a8e7b81941c4ca2b4e47d188f5b838465a0020f28b351b0a273e238ab1ae37e485bccca2ee9cab807c2e0ceb4430287dd5c42083307ce25ac70818865c48a8a8ff3cb915deb7cb63ec777fd229a4be5bab79804e2b5e3fe47a6f64aba5ece79b9ea0fb748a8c17f28cba9f0c02cc8bb6bf4fe090d1c3f1f759d358ca1547b416bbac263c5f90555ecdf8bbe2e764cad9ea71a0311089c85b6180f4a5c787e6b9ef978111a02a0f6b0be3ea97e3a8f41e640d809970242be1ef2f1133ce6d82de296221f1e3bdf58fddb81964c104f20dc35240037b289b84642714afdcee50c425d0877fafa15b3fc6e85088d076c3ee73ef90644e9120984187f18361ce161d96ee8bc242b805098f1063f7166057710225905bfd8d94776c70ab0ea64d750117349a18426de549948bc3724a5d36e4f307606fbf572cdabcc2ec1b02777870311354bc54980ca7098e1332a1494e4324d2806b51cb5e95ddd8d99259366b042af00c7b93b40e905ffc58dda55fa2a6707fa0537a8ca1dd45e455fd3a31efccc5861eb74ded58d230299006ead48ee1eeaac01965c461382eb114fd672bb6f9a714387b4845418143246a215205f078695c90f5c18826acb6d8a69235d3df0ae391c6d23818ad77224fbc1c134311f2ff1ad0d0e036ac73ccd57ab3b184769e78c1f492c1be692c82dead17d938e7b7f011108aa176590ec6204bffeee81116877e0c3e488437005cf23ed64f8467b1238e10043852436abe09a2d02a4b6c278cdfe4e2e4387c822b5a5d19e81558f4dcdfb062fe94cfa0d33b7bf46d0260114d2e19ab7fc22ed704ba2511b317aa5766b56086de0fb55d8c05b8a1bc86ee79248a5c48b81b528a2029a1be71da3566ef14c0dd3c73637e03348f2c4385cfb7e5ad0ac143bdeac2751ddadf45458c41d96c200e4c9d5a13ae2f1da2f8151c148767a23f07d8198b98ba95e55e9d49b0fb197034a7a4be181bff19bfcc86b225dd70b066758b424457306ce4ab59bba294b8d056e485f071fdf2326fe6520db7ed667ab11137af7a3fa6dd90fbce341af050d0a6a15ef696314331f034f0847e73508ff03cbf4fd21e87c790073baba59201f033db25052fad044b826a0ab0fbbc07af55db09f8b7bd8627fdd103c0bce796c0f8cb6a437c23d639c27512476eb9cad357ae989775ed12bf9c83a9d784939600fb7a9cfbd65a48a76e7f44a584de1f2065489891e2678f8b83bc04de20714eb0f73af368b71e462d5327e369dc8804c07bfd1dbc941eab85067a12e98201f060127004eb95b29c3b43c34ccb1215835eecb9db6e859d4b62a2648cbc710546d093262a08c71d8414e156a8fbbf88803060ddd2f886bbe8793553b3800e90d2e023a34a42898836629ba93c65d4d0d2d64e8da3aa58af3e75d4c471e43a70087014e0aeb86c5ea51389aae77aa1ce7990288d44c01187857bfb4fdc0689fe4f6073466ff2fcb67df1463045b1db7c7bbdd8adddc33b1261e815277e7319da8eb0fda836e2cc6a7e090e6bef7cfbe48d7ac707269c65200264f2f38bac6e5c101064cb224c7f6fff97f4cdd2b171256d85305bef58f975b677117edd26f58801c0a01c31f899418712eb2d547f582726a395feb419ed2017413f44e6803fb36ab0c77aa30f286fc3f62dfcc0fdcc6f10f551d7dde820aef340d718cb273fabb320716feda7cd4d38587a8c70310d4da2b072a04cb4e43d970807068ccd8ee790c0c7f04657c6b6980af69626924a02c3d74240edeb72171f632b7cde9e6215b1d06440ad3ad7088c071f980be01b0ef47586e1b7738fb7b6715def561801a217b0e28036bf9337ecf754f6a88fbd2200c6c36042998f9a027879a4f94768d163a0a0b82401b64aa5a50299e8bd2333677d8643bcd4266da56f4561fbe7c123d9f0fb539694a6a2243638f626b70541e9b0037ddb19dac7b2cd011b9bee2d3abca0cd1c96b31aa74233b80286617147af438cbacc6ac4e8cb030d77697ee8f04740abd31a5f1a084b6d741842864913a8f3f5dea88806a3c14fcdef007da9033ab00824f53f972008c1bac33becafa3014c41618fbf7af7a55a4b27ad4272bdef00f182f21d15d150ecbf5485fcfb4ce6425d594bb212692569742e28de4e75d530e946379e1d5481737ca3728cb7e738c59698f7e4ac33243c948951fa1950ce10abd549638e9e9c1b3d41ccbbf98ec0136b4a797bb0b237b32c03599c4f6876f0d7a522fb8f37b7da8d01e1924dc05888b1b83e0c613a480aa1a7925bfcdc33502722249b5d74cceb6cbeaa5024c4a5f5d8002420d4898b098ac4497610038980651b563c960ec0b667a0a3ad0d43e911d13c3af110992f19ab94838212af15d05be843bc5e063fd5da5fd8b6681f45562420f542e8df8b301553728a62cf2bd0fa88592c9ca4e259d42a2a1258cd5354872d07d275e2557ef9ed24949c519f70066a152fc86a0a86d1098f3a3f9a4ded3329d10a8b3c88447e3ff74886abd0d0d0e8c75af8ea10b0b1c0b350303b0a906c02b496b64b149a06b5f21aa74ed360cf162180dd9020f478ca79e6499eba164f91b6a52ec4e86b15d85c35d259a300633b8daa01c261ccb31789b36a0d28a71376ca2cad1da65f71845d00a5298d90cc2affda2df98b21cb048cd8a463c4723e4054a5a1968b330ba8235e06cc5ab021ac35eebfb83b6c15338be71ce0b4c4141b0fac70b778f26cc992faccecbbd06297e0fba25b1ebe992a0462dcc863ad0d020b3f880f4e8fede60725144570f03f18cbf56188ade99ab33373b4fe803015721dc17b13b3025782485151b2ab100dd4e857c8e6f8cd3f07a69c85f25a46ce61ad4b8677b61f8aa2080ed1ed14d04057652f29b91eb2d5efbaa632ec58dd01454650c8b6b403aeca871d398ec6500726809b9c4661708acdc0a7243a9c24326253543fb370120c0bc07eaf8cf050dbdec253d48fbd73dca3e382f7e2ccc392f9eff833cf00520a2e877b4ea331c0f8f63ff1ed94df96b32af36ec087d5197c1c9c512034d5dff65a00aa4f2a3460255a39b61c2e8be4879b142204807cb2558a6b08ae493df54962a25761c25e30e6755590e2b5734a3e5eabe52d4faf300b0cb99aa70203b9639243c8aed0a9e0daad5aa7e04f7ac7262f1366be8ffd5093a32c47e8b8b80df61a9b9a57c7e1b08970127619b6857c43c5f1a7f09b8bce221bfe3600830e3cef03765b58f082e06a6a1c320c93fe31ef1ad8e0737190a3c68f4a52f31cf918bd67e163a5d429a04f87e2314b5ff3ae03ddb35e9f4e484de22d4f24ebe925a0899cd4b4f8c1c61002a60212417c67a22897a51d41e4d14b117cdbdd403bbf8a06bdd502bccc11a088fe94d3ddaafa7cbc0d333cc68d0d19ded7da4750ac538592efbf34d29c8070e91028f47955eba64b7544d737803b7c712ac293b5a40c9796e83eb2d7b9d530a7db8c6e0085a45f0ecca5d5f37006f19acc3d2d9c9259c444506b02cb446c80ab1e0f055aea2ed79ee1919273a26dc40cef44c5d7e2ddd8f5de1baa5ed85af0bf0d4da3e11544457e91f30008427a3aff1c0fc77b4999687f070f2cc244d6e03bb7ad849739531db75c4a6f7873fe1f8811cc7358a234171d561e9d642cb7f0a72bfc3b2dc511314ec66bc4b85ebadae50675d9f14d09aac3cf9fdc06d55be0587295bbb514fe8b239971cf8a31b4d25fb2e03d62c54ea5ca07d48310b1e0e0086f5c838c22a70fc29f11115660842c33652dfaa19389ef62f5b14e9e77320080c1f3bfbcdf6085392457930f3094d89b97cb851d80ade96e276bca9472c98075421a0baf0ea4227c65506f815a5c033b4112005b48dea78fa06a8500da7ce00692242c2091a0baacfd10d41a2ed3022ee1b834c9a8c862a8ca0be9841fb9f0cf57edc0f4100d8bd6aaec9c0ff2a770fff704c107370e8cdf61d3b97e16d480d3f6ca32b574822cce57b8914920de3d178ac38e06d785e32fdb9ff0aaa525101ccb683e8b5331b771405ba4d561b9472d4eb87eeeece347835c0014bc2900d03e2a8cf9b3ea59b006a610d1115a0436fba8e61fa44d52e511bf5216e22929d05a88c64d13f7bd6c92dbe1e2c1db6fa0e0f934be43d9fad72b5c5cac8b4273b06ace8587241095326f296815cea3401e3c4838336e810709711e6472898fd3109431134f550e25385f6794aaaa721ae7bcd1170a6e81f62e52e054d747a57df08bb34a8b5ad62a8552ab612a0ed88ecf55ad04a0ddf20178576597138a88c0202380ba5b1676708ba7f2b2bde97ba77c0e5ea430ee343c2eb0236cede26c85f087c3f18cfbe230160e5d60a8e0e93af45eeb8ea6c6adc19702f2ca20c22aeb008d1a4c2e433209b5eba285f7bb9a094522364ba7c50eebf674c85a05946afb30f888a95a67859f57805c70869596b4662a8fccae4d719cac72179abef45537f0ca62af829d1a498a55c3f0afe737e618b401bc7636562d3f1adae0cf7bba7dd067387835bcd1c7693fc428a4621c3bd5ceb0923885b7029b652aafba88ef5830e82c2bf44388c69da50c4dbc3a782c1440c0499a38ed841f0f8109d77feda8a0c3b0b15f71a5102eda43768dbed26659387c0f7952fac983ad57ef00853e14e0e14eb5aaa052ac529f29fe07062cd6f7ab26d7f86570e1e020cda2e97065d7a037b5e42a8eb349aa860414c411bc5da995447584195cecfe7c70fb101dcf54a02a0d3bee2811c6c92c3a42391f4d0be322d44956aff37731c4d9d02d0bf6a340166ee0fe711283a17a50a09075ad37b6d383d12bacc5808ede0c34e7b24f0e002f2524913dc6084dbf648dec8da45e876116dc0106b304c8e3c372992d26e6d009525d07d082a601fee63c8ff3967f4d87384527856153eaeb6dd9f4a3f01cb0d2714f19acf65905c6c931d952351a56873d14061ca1c16ee25af4907ac1c8207e93feac6f69b47068d99686cf6853eebac580cd19356a8d434b17a20516bc904302b29145f5ff62cb79ab6e605ad4cf4ad309de912996daab83cfd73b3697e07ab0ae7c4befbaf9639acd37f65de0ecb87b1eae47ef4a239a326493f02a8be0b83c3d25f64d84e39bc7faabc214e83ff8723f1b8005090118ebee84758eb500d659293c0afacc0250ed7eb53389b11579d0e7c78593751cd787048273d52460067f448dc2bf80b34906d79f06a70a6a342ed1ca03ea4ef9e8fc5c8e15fecd70ade80f277d1040b97f24bdc0504a7bc637b492ee7ceb5d3c317be2cec4327d005b59fbdf862d05576e909970340ebdce06cb0f36adbc2e4eb90f2ff8c9bbf5406d47e6766880d6b30a9a2b258e2298174d57aa15567556b1318337f52dab8af090531180bd45be210301285cf1fa71d6c225b5142a60a06c23b9a270648656c055d62d54daaca6e6ffa41d238958f5015ef40b00662a29082a7c6cae9e7474309e2af2f32a5bcebebe307d5c55458cdacd9f949d5e71ab9e2894a2edccb425107d4e80c6b72e2e9b1b959070b69cd2b2f52caa86bd2e6507e8caa2d2bac13df0907236bc76b69f55b13525a2c0c4f5d41b2361acf92dcaa61af2b870944f7e50ac9f7145ef40014fce509d62da14d4a57ee1b83205396cf966db427c30f4c5a03236880861b04c81ea6adcaca4c3128506b9305bca0ebead5590554f668011105bddaf30cbda3343cbb9d00e8c708135fbab90e3ca9637ecc7cf5c909500fdb0937bcd1c3a3aa128db15dc80ed6b46ef6b509a5b2ccede30385bfb05409e48a0dece5f842ff46945d6852cac916a33b0ae093a1a1aeb190df6fc905062555070ec0254ca95bc9e1bc196ee1c14f96bf84a9c096b9d15febcc2e5b08ee3622e30d6736c593c8b06cdecf98b2844db0471b69311779301f517004e751092e033f041db67cd1974607f8aa154c869f751fd5c705c204c05a333f6d5773e659437e014e24b70a74235826332044ca10e447f8d0161e63f940362441e486991bbc2a033c76bc62dd9b4ac09d5b5c73e684ca2b982764c4394ceecf292b1fbcfbb396094ce4a5ca9a593f75ee3b239de7558f25ad0849d14468654cd08c30aebfd3e70fe97417a8c5c3ecfeef0004be23b504a94378ac04dfb8e9b59b33fc4e49ff7b0cd3cc7904edf441f4b6d1172d7f0b3bf00f45719d2aebac54ee1bf261f746930e54b660eb767e51896b7543e1a44a222da92d437d6f2ed36649911f0ea994c70761103ea3576ae3d914ab6f16e987594780d6dfa3b0b5bf679a5bc00946cffa03ffaddb1f7e521a3fd67ffc1e60ed0991add3812c501cf66058cafe2ec1557d0f89d3db17ad24323bf156b7ef0bcc80d41f4f3b992f7d9da27158d3f2852a2b099bb5658fa113387ab3ec2a015205718ee211ccc93eaae9d4412af094e4fbb403d5f71e6178b50ee7ec5d057da953f8f4fe962cfa4ce969b33c6a48dd9caaa402a1360fbce70f8c87036d4c305aee42447466ea3d7c1cad7a7f4d18192cea490ecbca99ebb21021a6493b46d55966126b9121cd3cfefe2e3651fa0d0601275608a703c91273dc91984ced641ab012719b4e2d302f6c0327a45f59797934fe670e8dabf373dbbafe33896d7f88b3d599c4bc2af4d02afcea47eb65496da3b5d902a7e6517390f9ddc797ce1af98d7724ffc5f0a5a5199de408419a8e63c757f90ba656cbbedefb8120e808ca144e54f6d74806e3e4456d547dd65181655cbcd709541d1485b2248091fbf2d9e0e8e565de692d432b7c2f9ce2593be286f2e5880286f0e4d37164bd100e2d9d89e68e4417c5c3bb01348d37d60ad863265d2502065d58321976af8c2b79a5391154d27932236c7d0b14f9cb7e807484af2f422802d8ac2f2580aab1b564a240652ce32e1885a82000d5fe3e00f0d80d2ecb737d0c3feba1a37615df5b0e2e8c3fab1fd7954dce0084f49e85a4c88b4ea63583090723806762b945a0576665631231d629755e725effdbff70cc9c2432ff0a00d701a99890a7fe3ae1f467584380089a71c8d829b9ee332514ac8743c83839929305bab9f355d5d0dc9d7f8574c1d4f89930bf0de4ac06b04f51f3958a365310400692ec1b7d3d45815744e3a9831968d4d3201a167e54e8b4481fc6ab89379de7088bd203695954769c3e38561605440e37a82034c15fb41504f78eeecf047c2a000204bb58c67bb647fa66f7e107fe653dfb900876bbe5b588e3b8aceadb5fab036707011b880f2aacd3509c0c7a8de2aaeea819c1bf86e25d150137b993d167076ab05257ea59d2239cace3b6663b7ccf510c504037b03e0664775de0b67e2206c5fe07b7d2991a57334ad4e170dca92d5088cb41e0b32cab9b7464c606532b0ce8f67532fe05c4caccd94d61b4c245ba971c8ae2b39f469600fd07a8786d5f0d2f01e56492021dfe3b09b436c4c9fe23e8141f6ef8baaa354e4934a15ed5b204141211b4ed897e18ff593460f605f2727432000d8bb45f393816f6c9bbba8405d823b783282796bfa1626b02da3bed2d77243a6ae6f3e5db15eb717e9932300fa9aef47dd1148ed006c89c029f606de4af5b39471c788597dcd21899e2cc6c0fe67350c8536a6bda5cc3749af1bfe21540261fa4834ca0a37f29932d3108990c96318e956d821bf027013716c9e334a5fec20816434fe7669055524effb2c4084c0db6f33909ad2c648aafc508646d342d87b97486dabe1d93f49556db3f20026aef9f59608402bd9c857c9f86a838a81ccc3a7cbad2fed2049afe5ec1679a035d34224ec82b4bcedf4882a7367ae74edeada6bbebb22a8b46a59b4bd07f480dc142be32758dce59c6baa95769a07401336d472d022979d987ad94c948e69502d62dbfc4fad8795fe76d534cc4c2d0fb60759bf1368f9f6a7dd9c9c9c23835044ae114bf0d2891535a14ab7a8d8d9d0506d1e5d3c4e66a8f285d55da639d5d08bb3872388b6c3f1d115329f2b161144d308e43dc648dfe296612533d911ee908e139aec7e2e6f41bed56e4479a6dbfe28ce5b0920898a06e8e4beac88fd2250a266876dc57c87f9e0bd0f97cc7e5a09c32355bfbc3e1e55047da87c00ebf620084671e1f08914050420fd3d2e1df959c9fdb4e2f3d819880e2aaab6e1231380d006a7fce0472d2c6bd1f26306ab4769705eee06ab8edb6fd49612f6e0fbc0f0012ef3ded37c45d0ea1c9acf80c0d300ccdc9a362d7501ee3dc5262dadccac20b0cfc6237e5734e942b2c0ed66a6cc7f07596da1b3fdd74006e86ad001da4b50df73ecc63223def64b7b6307ae1f65c957ea3dcf901310cdb63c67c57c0768e09a06472b2a71ea72532c768c60d65caa9820928ef313289fb2d3a74e98fcb3f03f9317a02a23970dfde6dae796207d4aa1c2bf30e6d57d80b9f016700d77a7007de5b557177bb0e28483c1862e41e0ff7fab6cc39d3e7d0aaa64a1c03e3aabc04569eba1fea0691ca967a87b8afe017358882b57fd1e359feac0a8351d0a7d90c290bdbcdc50c9b2f9ed58e3bc51fd7230c44d748b1ff47457a6ccafb6677ba059ad9a044cfeb2dd82de6f8946f346405f2ef1d1e9ff22f4973170e3f32432102f11ccb6407f0a3ae7d3809807bb27efa4f535190355cf3b5a8eaf0040bcacd0f47efccf4a2d65ae41e12a7e6b35580487e2a8fb1305dbe41ee03e6bc27de8b0759319d7155230e78666f97ef91afd0493c0665128ec83300f94a68d0172e8c0e75aabdd5880923a3a574d6e5303728681fe598e9cbfbbbc9c6e7d8a2d9856d0e1e2e5aca44ed482bb354f19fa933ee486620b5449c2e7a32694470adcc8d4501a2495e3db4b9d642cd57f435b58604a202249d5656f1ed5dcc0b70b84d6c70059e4cd9052c72243752e02016080737e35f536f110d28def415c28f29c054550782d0102746c82732940262efb636865cd727eb98a039208060d18ac4f9783b0ea6947466cfe810d43abda5777a8602be2988e9661e975d4896d1f7d684425c0a5968b59919968feb6a9c7801fcf9dc7311b597dd7f18fc72c4e773e62198b40694e8bfc6274dfccd8924e0b3dcde8f73734df2d5ea921b0ad39ef42fc80b080d8c57207939f0d270eb49944f44c5a234bec2d62b94db07e4806096dbd44c0f080ab744047b44917ae81878980659c332e987ea6a527dcc21063c149d8103de0214a413c707eaa2bfc55e3b464a968a3a7d0792abdbca52de8a12b93eb1c92701ea08a6755a68f6d4a15a5319f80d55273fa97ee4adc2c05eb7021259cb8e96095a735452d060b6566c40cf3e736b128268ea020b52a0262b9556a4aa24646904fd26561e9bfe84c82b70e350790163f3f0b6c8f8c8aee90c7a0801c48817510b920a5fb4b53e163c7ac179c5a3b201908d5c669ad1a7ec79b131f96562dfdf03641166c16784fbfc9dffa08449146c92fb17b7dd53aea21d4498a8640228aa0cdc7f0a9e220d045594ca8eae17cdbee0bf3475f4ddaa41a0a52a5e3ae78a700b04b7c3704727bd899eb04e722d1163759f391aaf885d2c40a62978aaf8d87a04a3f517ad80faa6db7b79cdef68ba2b977f392122ee6086f731a277ffe495f307d5a4c4a7fde9bc387b18503f76964144d0a00f06be90a4a683f00df35f0a090472f0145000f599fa99b4e09fef5197b3efd7d209ebd381a5fb96bb7cabd60c0a21585f5d1b2d073f50255f7c3c15241a031b25228dc0fa826ee039f120ad3a0e6a813455f88dffe3ef9ec54e33e64b896530f4b4a74963ff218d51ac33e9a50b99a59fdcb6d0498f1043fb3ef54635558270ed2e7583ded3e73d4b5d3ddc5003fb1e4f1929e4a48fc6bfa9e0201f24c29ce2e4c8c7c5d5e053be41a66faab90341c42cf1723b8d3fd9c625c5df7508f18e804c063c8d57d48f4e59f8f7450f0277e471f810e97630ec49fcee21445ff33c37bda200df709914eb7007a1732e040e3fa07b54b636146cd0c5ba6dacdcaaa76ef54253bdee0d614ae1ccf6d0a80c9e47b596c97c929671e314edf06c947d5eec32ce4d0add47283730be7c28a7006fe9d1a67ae74f3537d663187492ad2c7d97a7c2c797e9deaf8a57105688c40dd0207822e1ac0dd0cb339931fa6f7414fdcd510811261e5e6b19d14f5f910008b55643353d36247db8ac9ffa4902bbb2b2fa0644dd7f922a1538284677092803aedd8a4d4a37376e3284c1294377714b1dc369757871c64b52dfc6fa89450d085119f8e1afa30c0213aed32846b99a14ad4a39bbd0400ecdd072470a3f08c70ef4ed8f918cf0017fbdd036e2b5a04a93a527d2ed1f52fcbf6851c62fd9fd8b0fdf349b4c1df00e38634f778385cdeed2b41befc6efaec2fbd75b461dab05770ee7674707d54b52d4d390bf198a6f8ea8d4c69319e3657721713f57607de2bb04e6dd18b418ca85d0bc20fc7961baa67475ae46d1e0e7465ebd79e2a9d3f11200ec826bcd729031afe33a1b80be15522de8b5b0731197f3c4322e796082386b06ff8b6fd51fcbd1377e2a6ec16cdd5d855b701d0b181849dd261029273c14d306810fa86cc91d94e349cdf4b89f9c17bd53319cc39b6d95f1e0fc1d092c210f095b9fbb8475a85cea3d9bcde488fb92a93a2339b8cda0164f7ef305765034d60d3615b0c65cbf4c3230ab2a79e3eeffd756c373eb374ccb4ca14f968a8a05320306d20b08d467b6c0fa527f2322d7de6f48aa04717cb91793640bb574283e700ae24b11491a8e30a251c72f109dcd537b0a3da98cbb612a009cab6c87a5a33506a83bff8f60def49fb3b66ed9ad489de2795e67ef8cf82dfb12c28156c656d00ff5106b880383557e114b87835de63a551daa9d64cd012d4d850979ae3d58c404a4f0b9b7bef8c1025b34f99b99415a8574b6fc519c5350af3ccf4c5e29d6e7003f2e50ca021281d762838a0959eeb1bbd45a66c5e4d5ea227f3fcdafd3a4fb0ec15f1d4c67df1f224c95c0a00a8dd6301b506a6552a44c02e6b5b2bd35d7f0003e8044b4c80e9d7135e2d02c2a34b49bdad2233e114984188dc66d693d1d9c0f2979877264a6b46e36185a6f23e5532403d4942a4d219a108a46d9d90b3bc200ca7e7d5425d03112f10113539af2eb0e47ab2ca67a39f7be25af07ee9f75b503c5cd6696769e6f89c942bbd5a9dc9e665a13fe3ae31da7f62ee597760a42d1081259e5a18fb0ae15dd4de829984842049dcdb819aa4956b368bcb8403677c504a58753d476db9c6dffdebaf57364c2c8ded19b99f4c7cb7699802761cd226703651b1c45d5382562708b27c34a09daea4417654875dddb2fd56ce6b007a47100c66681c4bfbb9a4835f04b7e7fcaf9889d972699ba6deeafe35acd166ac023010c35033aef0db1b1dc3b6426b3d4e2db8ec1ac05172dc037cc254575f8449705facd5de1605855f7830210af20046bbddfdb8da14139004925149b18fe180902ffeb748aa6511692d23d4747b4ea75c605d29a1da1eaae1f885974a7fb43310337948bcb4f3d09fabc783c1cd246a70503d61026317c3575e98dfd2f9739fb0a4ad3cd219b10627173166c49053b57d9ba9ea400fd88b2d34d28f4d9a49d5300a7254c4a8ff2a2495950e3734b1ad0f21df6bd45e17bbdabc54f430ef9924e057cc1ca8e13affad05f4c69c3b38f6aaff2f40f6f661547b6e76139a4f5ff7201fdbcafc3236ec431e09049685b17c8e822f34d30c1f6c8e0890f53712d948c0baf40dd1a0ebb1295443283d712ec13f474aefa36950f525ddaf44f34c9a9bb0c750412459f68de26f6d4a997d9e7b1e01e5fbb298931844d6f3bd98f62b2920fd730f8e9f8979277a24d57ce731ec08c4f69efb9ae282601870544e180dc9901a19390cf0baa86af1789d883ac02077505fc28840d5dbe4ce6c8cb1feb874e065b204388b2d5c8742f606d52f3b3fbb7721ec123b5f48646e90bf5bbc9759e0526a2324d249313d915171197a9481c1d54c3c715b14d9881db1793f0395b030d89a160d4dddba6b400f0a020ffee859ee6c606b80921f09f676b7a099500b4008ff3bfaeb261f1fbb3190a261f922c3f1b53c66762bddf1cb4b71bd26113a50908b26d822170188b1e13dc5d75ef8997a76d9d269d35bd6b33bcb70af3a70e0fba3cecfb0d0d90dda1e2de5b13eaa5c69f2b82192ec3e6b58351a606c564a40ee6e0e660f03db1df99ac66229782518b3518dabd6f5619e5455666625f8875028e6db124fd31ff7242bb3d8451e1c4d0340ac19ae5e462be89572e25b25925063b26d934d54910c412d18f0b8b7972fc1b8ed769f388258959037bbd5903b304db9ed81e70efae8701e2b2bb8e37be732dbaed903664f762989642e3f44b610bb43a3ae136c3091ef574fbdddc988f66f37f6389454bee2d7b15668d207b31068a0472a3a091c72eef865197de98bcaff1846908007defa5024296aaa565d806e9a891f4d867d07c457e6efb404a52c96ce72570de1f60ad9147d691e8cd1605c011516bf63202aff93e9c044c7dd5de30cae0d1289990c46a20fdac4d7f930ac931195a4102ad141c9584743117ebbea379518e8c95a4c895a15c9f5936a20660217e5315cdaa2c182d2087856a3894393c959a8d454dedc1bb3819c2d984092e635c92c35ddb7921fb2dfd16793f6f357c72b05a17629ded211a03f7dea301735b22465abde1e4ef76e4fc697e505f5f0f6dfe3249ba189dc90857fb7ac405a1d4f00815622a2e7cf2bf9c3afb1aeb0f32d208646ebc4985cbbf70d35e9a01e6637999ab39e948404ed7e3a56f55c81189c73a8c416c6dae8e5d295937cc0819d16b97a6a0ef7d27d4547693a13a73294a081086abcb34cbbb9021a1abe6079624cc90b7de93852023941c6f1834bd5ee1898c3d2f2115bd86ff6cb6c74007435e6f1f217becda795b5935e3332de6474b6214200ee1a00e649259f2f2ba067b7244cff6ac395ac242bd9fe648879e62f0cf7b87326478dc9d2d750648120a1c3498f23a55a0ce1f78cc9ff107084b61c5181bf35dc70dd733e57c34895b00d46928c8f59627cef947649fd3d5a13891298df3e63a49e68cd7e100ab27dd09690a33101a282426eade0fcf8f763d3c4ed42fd2904d162e548f55d438161f061ff0e829c70495a60927743fc21b2e8e468b5101e909494f23dbbbf2e7cce90d140198d721da401eb5f0defafdef4df22c58afef3d2d326f7b3b430fde21e30784e3b6e43d12e47d3644ac9a067ae6d58b5019b1ef2778b92a040ef139784d0fe9b3400ed8749177f0cc786710e01e1aef7eb52760682bac8381696197ca4f05c8e93696f26f518842c9ed577799ef13df5c414e71737667cb110d2d79c09204bc0ae19aafd91e3dada68edcf786ba56a2a5943209404fff24125402d524fd021077c49413f69a2e1e5e0c999a800b033a789165babde947f17cced6d01b8300e21e79d2b6bc302870dd61df0fd62c8996ca5cf0c85d5002e7f2c1853af67d0b8ae190ffdb815eb9079edec1de9c9c548dbde45a4cbfde8ff6d818e2ef5c8b009fe26dc2f128302daeb1c58c1838a07c58e1bac8b02747cb03485b4be029e104457039e8670466174afe4453b6035777b073b4255bc55bfcd08ff60946ae7501545473443cc3e1df7e61ef8d6f6622ed9f8532ef6cbc831cc9f4140f94906705e006c57491ff6ed0da7e8d1d9a7c1d5050283ef4cc1ec9f11e375caecc4eab06a3319f8aed1111db7b775068ef087632f05adbdb11568e01d4a14453596881027db6f479d25c02c3c1ef1253bda91a95419bbe0fe1efd84b7558fb54831ce706107453fc44184e00c4904a66adbbc0c48a2da320e013bc768fa1d4fc3e256802b9b84837a318a53648312115824f697891b09b4bbada5e6bfc04fbe11a2e3e06d97a8cf3944966bf8e85a7b9dc474277e18bbb988d09279eb5d05aba1554cf0d395ad3947e6e958e6ccdc8570ce5b28f2945b73d9f537d2d12ff177667ea1c0aeb155b7d2b62ae8507623ea66c9d8dc55696cc18ebea523248430df177028407fa81d373146d0208e7505bb51297647c659b684fcfe317ce64271cdedd8ec00dd69ff292138021afd8cb35bed152ce79d23e04f91398ddacded9346e65b9fc0f0e161b60f213622ce96632f8716319fbb8e641ed40919cfcb1448db04d85f309711e412df8c85da4505fd24bc07e84a176d561b106bbfe2ed523e1f5e4348801e14c822fe678f012d8a59f106ffaa72851dabadafc5555ad0aea3a08395ba4068795430d98728bca20bbdcaea9e767b8052bd835121618b7498172c7c8272e0c21797cfcb89b07c4332d71e81cc6cac8251aeaff6fd7e2457a2a20542ab291024a58e133f60d3c765412d29e7e93535d24fc315af6b6a02a10e41506f1e6f9032c47f7bb43a0ab1a6603d0933d1d494dfac3fc96e01f53fddefb220d8c62e00a39c83027e28431a1ba01fc773745dd6be2c3fbd6ddd4b36d59a269e6f31fb501a0ef36dc6c8fe1780d9990b7433ef4de8c9527c2412a349eccac7866c6f1c004bb342d14e0d6999721edb1013a50f9a6a02550c54f8bfb325bb7fce00d1e8e064bea4f456bf84f0505dfe6621da9f8deaea78eefc72ce69cf9bb7b7ee7035d0c19e6ca5340a793b6595057eda22607ce1d4ad1a95d598f3832d5a86af7768401c5bba1e466f496704fffcd38b97a952baccd3f06babc69c27392e4f84ca43b01e06f84ea6bd6288d5ae8c1fac8b63370e21cfa7eaa4e0eed92cf29bbe424a408980ab9a83a9f2be8d108202eebb7efe5e61dbd22016294adf3896e14f42387064ed547c9f05194d74e82a07613c970f7b3f16a662dd5cd89aaa255ba631842045c829d5cad85c48f78baa111e6f14fa88d2a709bcee77819d35a7feaa4f60d0a50fff3edd8145c4b9ae2d94f57c5a674e9a12739e90efb00314b0532b32b410277263b16a9a3719ae1935251f27a367f1a52c8d6594dcfdbb69ff9d8f1b0e307a17c3935d870a219fc854041380b847ac24a53e162ce786f8fd700556747d7016e23acf5dc438ca0eca914eb825cf9f385a871b827ca42691ecdaaceca3f9d037e1f9a35276885fa4e3ea22302efa173211ace3707d268360f39dfa493a5a20f71cb66995e6adf9d6fb6cf815e85d6f50c0cbfdaa0e04cea49deb8bd1d701e043842d08accaa4b506e5cd66590582b40d7ba53be5086beb5c37aaa0470cd3c08ec2d551980e80b094767524b87b539981c723dc510f761aec2b8d7ad5b28070019007d1638a3f2e341b3711ccfc944e1601e04c6ba5a89e3f23891caa645fd03a3b27960692b511eed9fda3771dce3665a1acfb58cb6611a9c6832f1c98af208fe8667d71e718f9823126e3794926fe77ac3af6f720a563f26e393d5ff33010dd1aafb3045b3584c630274e74f705d072e6b8e3e8da9f4ce05fdc175bedf2f05f87b02af532b9587b37bda0d66a642ebee294a26743d0334921bae431d6492026a6170075b526e5f09d98314c790417ef5c76e88c852ad32d57a2d3b625aa802feaf4bddbcb5118deee2829d2c4131449306479de3eddf0892dc495c5943f8049b5949f6736aaa0ede5a0b602d150c55ff213728b97a21ee5d06bc0b9fefe7047a19b41be006482d3876b20b4987a4cc9190c447c8bdbe735aff6395b6a7d6008ec131c96f9ac77eee2e6459e6419ec69bb6e1eb422fbaef2144607fad4ab90d3581da588bc8ee8858f396bb3d4b4029e58c5dc42480ded003c65b60a587c2073680af2429cf25ddab5bf999f06bc3c146d8707a37150fc0e1825c05f981eb061609c778f90a2413fb3dee5670f7fd562a6d7f8452219a144cfba58091218509b8ce1781f0f66f0f46b3eaa62592e7710f65ab93853519e4283c43ecb789a30c1d92a903206a7fb3062aa0986b5279df61cb76676751da02375512fb6034ea004d392efcc7f116e1f19f3f4751af24a3bc7bd4f3ec37ccdb3f372c0cdbb1280d57e61a6568210b88149d08aafd0b3284a384b48e69774785b51edf281df7490768f0bb50465ec4b1dad497ea0501aa510e9f8284de62659dc28fe41a88a6240f8afcd0deb8f1caf5b7df334ae331325fcd12e37adf6030f8393515cc77ba0c08ec4e736dead991cf2daa3012a56b7da0379283a0a07c1331560a7ddcb787330e3bba86f326c0147962c50d139f506593885d63829be09d7ccb54b4395fca2e015e417de56ee53cdc841e7b7fd2c0d6fe947977c6d27301562a0010ec3bcc4f0199217460bcbac9fdfb8a91e9c0719ea27963e7967cdd9ea45e8657e87394500d21653d6c63e03bd4fb736326e8cce1f04bf191d3d5d52963bdc997cb5fef9d030b75ea92fd647567c38ba7ceaebf3be44d417e10b56f514b63dc401d5c2f9b03e682e30a8c1c0b5107912ab3e1170abeb06e96306ff2adb1845cbbcaf32e4f03c386f8b34e041ff3a46eaec03719f0ace9ae50062b89568047e4b6fc1b30a9058dc757cfbb6505ae57e05d58fe0ec95da36175f0dd24f0568ad34f593a8c1d0909ab3e7263e5ba2d5356978fc0151937e93c6a52a2f2ae25c9acfba2713edf09888a15267a19c59e4e01cc43e42c92926bb2ab18bd7d5727597c42e8652506013009b6cd749f880f3faa1ff9fbaaa2aaeeb22e9d708fc291342310ae8051d2000f6cb740a43b7d110691837f4bf06d691631c91831476df36f23b0982979840582794311d31e492b9f0ae2e0bff6620e59342669e6d91171cd0a7b2a41b315041ca0aa007977f384f30d579a22958620c8459929d47766e3c5d0baa085f3070a6d0d3c8858d51b4847cf7e246064320d88a0c2655c1387a1cf43da498add8f0cc509a03f3f41a14c4dc40b54c8c3b839c1a754960f676b0c08d56a8e1618e90641e4eadd9d8c400763d9ebbcaee7d7bfdaa555d5a62bccdaf7933e0ff9b7010687f5f547d6fbc88a95679a7285429a968846ce33db1981e0ac634de0214d830525c1b808a8e76b7fab7eaeb7120ec2e0d9a1d0699a0f4295635b30681a917e0999c9ebf9304dd54cb8cf570956cb2d619861ea8a27a482b98b1e4aec2a41ec0c7da4ee1ca916f27ebae1b66f65bd96c887eaa8b98a2df1080d1fab0cb2fa92000366b5d943b75d74505d0ece59c9c3e57556a939703257d50c91c31e569c7309554cb44545bc8c5bc01ce7131063ab9c3d30d68dbb3d56f3829e945b41607608ed2ebe13cf3124186498e69efad85290d0ecd460d15b0f56420309c7517492062b4218b0739533ab8176ceb7a8ee672093bfccd4ee6987a1f22b1c6010b750059531bf0877f730583360ee914fd78ea2d9311b641bebc1e555180250e73b4b03b4d4195cc1bc00b69f6de83931e550beb7ef7536756dc7b8f33752804b56b602009182698867d2e0a11be7df9c187dfbca6ac113fe6a147a9109aa12342cfc0aa7c0b7b4f70c371b9eaf96948110cb5131bfe9f1caefac26a078dc1b4e2d3e02a4e8c02d040d6fbb59c1147954e67a4fe2275e9bcf5354d8da8cac82520d1505162fa0fdc19612e99939d2ab5f1f9ba70669be6c8f60fb711276a963abdbe901787308c3397c6750c0ee337353c6cf24b35447fae398bdf029f8f82163551f03dad81f53d4045047fe4838831e077f98fe798532df17bcf5385a06bfed4842070218f64e0f14937a07b9c7b6e42633c59015c773d5df9fd05c6279198e530f08dd130ee520f4f53a7ac261852c445684353d3f5998be2cc4351bf1ad090a9309fe82180028c0d21cb6a9a886451aca894c2b06fdf4f76e5794bbb2d073543c0c76f81eaf0a62ecba541115f23defbcdb22bac2dd602254ce857fb117029d3d00d752418acd386f9f71e9e3fe92cf4e00a26a17a5366c93100a05ef71387e2d01f819d05ea14d266c8311c5b0fbb4f2b2fe8196c2136ef5914d1d5c3b0ba53a08a5f9744a4f2d059349443a5444231d098e754df9fbb90cab0fd18d0dbfee050f1b7058d6ce6c184cad2bf2dfc61d39496245e7db943e31998d45e3865921190b07308d332a529f16aad07a5ab8328bb373db83812c16054fae0ead79c503240bae7a8c3827f4c2790e30ff2d5199e1fef109b6572499a7d6255f7a71d492c20044f6cc03b75352749096fd934b5207a6cab2c28184ddfa0a0cd9c08667f6fa057248f8e5d346e8e4ffed1aebf6d7e081be41553aab60e9255118ca7f76c45201c276e955252552b357b3c2852a2118df5524c4a28b417e810daed99c0a639f02e5adceac3e4a3e1e4eb5af7ccc8a551786b3f530163f0bd395d2a2f56419f005cddcdbceac18a1f497a0ec9222d894d0771e39d97fba3b627747c8750e1f8507794ba914cfde482c42a9b1105e2f238b420d08bae1b628ff05c5042d9895070598c1e0c73df16b6655a9023b41be0b293accddbb67d30e73f5c28df9e481fc0dc9d4216e06ad06490ce0a6797d5b246a0d8851e1c8aecdfa439b1dd036d0e50bb3cc668fdfad104024615db4a4af97ca443d5a50aa9c415b4eb99a04579dc304428c4315f7bcdded55971e872aefdec6b5ddd2539a48ea1793fb2ddb4a2cda0af9681dd8f44a904f754d42a6cde72a15790e997f2909a7401a3df9c97f33230ed94dfa7645f1824541442a67ba0d4b28e5de4feb55142dcb96a7136be15580077cf964154e6ef20549775d822bd8d502464cc7f6381e7df63fab9669d9faa30cfa226b0850395fa57d04de02b171f758b4942fa9f69278c67b3ad6a53d8ff009034f14f76e1a01db7c71a9056f5621806bf0d295a9c8147ec7152ce931c6e601e9a7a05cc91a3f09a59d364d49db691f563ec46819666b4dc98af7d52e30e3097e810f9ad3c7750c7ed87234d90c59fac670fbad64d4fb8ca9c267c001c06908043868c23405e762b078955ddf6206fc0b863b64d657bd8c006aca305a57c405635207ae047fd2817578793ec87ddafd7980310f7b140f1d2b016ec4eb8ba20e485d7a757022bcc90b8c739d947d1e24f98e746f8e5477bb57e440f3d46ee3039ced366b9484ff0b22b07c04b8a4f5467bf2009eadbb6532fd0d19a0890aa90977b9c8706f8f78867bc75883a35a27cda70705f5e4d9cf87854a69e2e3867907ea158f19d7fb6603c0a7451b4abc6e89ff06695a43d80b2170cfa7d23bb81d01d24e1f6682f57c844781fa3c93ca93a92880e0316a1bdbfd8885de25f20a82009273a02d014b8104dd938ab8cc76800e435f2b6235d54d24a833f1370ffc5000e1e5796063fbd2aee7227f6c09ad1af1ba9b2caf9fbfe8b312db45c34783760b75737de3e3f4a80ac366dd9d4d1fbaf83fa0791bb2c3f7ceefebc6bf2190bf0801f56dd0ff021d359d097470935259598347ab9312c65e56c10887ef517a9609ce9887a78476895c9ee185f3920b86cb9c939fe1f818cfa5ae9540d1dde0c80092ea9498351160a59c40e4f2911b784f38c27a83043154e7e9922a7cc24916036b67d8fa3d5e8272155af6d8400041b62fea9a346a509c5fffb8ffdbeaf5310e -generate_ring_signature c94de04cde5cb97d00fc58347c9d43c5e2ff19eea4318f3ce8eeede86e590181 dd0cd265cb990195247bcff884ae711d6d6e27a552da53c3871f9f1ef313119a 41 fa1a0c591987eca8d52794fdfcb34be618b911c96bf95dac8383b875200f160b 5df51ed1944ca217cb3aabdf05f9e84481cbfba603863402a3b0bc3032345285 aab3f16311418e85c428006e09f6ab4cb938193554381b480c2ef44782ab092d fa2d0b52bf1b930a1f0f482813498bd527ce090daa3f4285477aab43cd1c45c0 954e9aeee405d40869ae3c3ac2ffddae7c00e08574f8e28528d9efc994885f51 1ff131463c64c2c3a1b7af41435e737718ceb11fc26df0e48f192cc78b49606a 05defdd1653a375ee7b758c3d7bc997beff91c486ef9acd1f7407599ef9eb509 4db7cf256f884cbde45e0e580207029b4f5fbaa85ad7464b20e36100b70083a1 581797147df7936051e989bb4881f330344d5733220ab185292371438a9a7660 606177bdf0e2889440bc7c26fcbd0f17c5a204d1c20205dce16be9eb561361b1 94a10e3d908cac01a162fa1087d08145fb3db70cacf955bb70d51bd0c0cc2915 a40e979b15e9b750a010c4f924a6da214bd0a0356aec01f392e676c9f9d42a4b e4e6ba7de1cb51d81bf38ad8e9bc3ba7e3059b170566e6188e5dcdc197f424cc 0bb2355397a31022e06f6edbea574aa43c63a063f8287fe3de58a438193cddbc 1994a036f18f48016570cd7ce9c56458df51f532738e2fd45fc8e09dd53ef228 0ab9a2f03402f2b2d2d7e850d682cbdf1a82b34566059897b754188b09f84477 338c19bd0bcbabee027bddd1b819ad49d5c37fc77c7cb6d6ad64862e23ef632c e91daf7d75e20f04693b719c473fc61a15d3076e1e372068316e0ef056be00a3 f547d83a8f49db95529bfb04880da8d1856c39a35ae73070dc84b3694e49c82d a3f131eff367e0c26660772e1598e71a430daa4d7ff274e3c8da018a40b7f765 205875d30d48dbfd44277116bb0d80b478d1f8a0a83ab789ac0404fdbc03617a 92c1fe822b784d0dbfbb2ec60c8c8e9e9a797b807648554bb5105b951a3d9d6d ff72f514b8adfc2861faf09820604be178b7daf92202088a85d2d0e66bd57edb 223c5ca852fabf656ceb333b5bf01d7f1f37f2566b54b7c4c29a86f18d1b8ae8 14a1430cd78fd2039ac5d7df2e3069b24cb36b12f2576771da0c1911f49a273b 69df315a3694a8a96963810c498a765f95eb4f84478bcb38a8775797de768e86 e94d3267a6fbec3acddc3ef82a82bab6ee8b0fd4a7defb1332b936a5f1013fb8 ccf5a0bbf7e6addeed9cdfc3599a5a94e47c5faa02a3ba4671a4c008bd403f49 45f5cd60d7af60fcade4173bc0ed1a36091f206dc28e13844a16c675422ee1af b1907d4d139c71715303888acb530cf952f70e777b3801a0f402f2f0585d6930 ca8744c0d9e716417589570bf615e2f97e01a83992b36558af37d7ff28714523 fc201c54527f971e930f3d35bdc20ced6a666ac12d851398a7fa9f26ca509f53 6efe0cfea913636b65c218d9eb260c3b9ac83018e18623135af56184a418b565 e35ed29a37ee3eaf9d9c8133a0ffe4d0b7b4b0358d5889ac0b3dce4bf4bf12b7 4f336bedd6f2044ffb6866bf87bd50c3bc3f0d814b18be816f51beec2f29f1b0 eb40611822d44136ae6779a5586246a2ded4d005c83334cbeda1aed834b7f888 64b8c042003d59c3e9e1c4ae0d2ef6c1d151e02c4c5e7c36c7aa5b59b7a74357 e7ebbdc1ca40f25c085c1d573b4951e9d0e15cb0c0ad0026492cb6b815ec2595 2148ddd29c481bb590a19dc5091847a0a86d1a1f6cde6a8df6f594be8dea66c8 96c16cd72fb2150c2371c2313d1d24520bd660b248c5cf3ee368f95d7257cc0d 9cf1735d67ccbd6c29457094d09bbe4b18c3824e97168d9fad6c3b97a177f9c4 25cd26b4ae877a3fc7251903d65736fd36329f066ef5043d715032b1244cc803 0 48a38a7db79032494af53fcb2f45b8f526870e3bf45673c00a76fa4372509809aaf5601ec406a87f3e4f441daac39b56f151f6e52b1a3e383be33afde66e070b2a58f09c19923c5d70d3793a51f4b1f85a64c6153e968a1876280872d6006d07edbba6532a39dc1de6961dfa50a7e3a913a7774b760fe3c243ce78b57543990e212087a411c8cac6a31c4d8f82d2e1ad2ced0528af3bee7ab8d1bfe6d94d1805968427b42ed583a20732f5ffaabf115581c04bbb597b9d25f13d8cba91227f0677969afd3cf2e6cc3e3691682342ac721a2a53decb31cd854ee447042fc2f600b467a3efd7048b1ae96431e89f078dc5719a534d15e204962b3fa1de8a2aea077500ad9e253c2f565809f118c4caaf9b2766daf04d04e08e80fd7b305d53e403ed49676b6cbe2596bea63b954cf22e65337e56371ca11a92efb5ea5f3317cc075ba9a10dba2d8d90e7fc143bbdb2df27e3c992aa39ea4fdf328b7f8fa630550e805c17c8108429e05eb9fe2fcd6c5306c4dd212c22e7cf55a12bc56b555d910d53bcdaab386cd4254da44e9da27c74b13474fe7009b09681e67990d28170f60229dd5dfc185f3a6100ff3cd17efa91d806a5c6db780ddf655b9b4b66b4058005372cdd4214dc1b7a36d1a08de84839f1388ef999c489496704554e00b2e2c50195aa0c70801620a2c83073db517f6b0132652d38dfa8524cc5cf3983964b0808f7cfab21425c757e948c599bb1d550fa6141630326628134307e09832dcb1d08329b278975072ab7c9e48c4a19566c41377c1b01bf5c66073366a8e153b57c0956e981739430a5c398146433235fab473e7ce8b00b4f878a4df93068cd4258033d001c85a216bb95bacdd1a5c2fde9ccfe7a885e638e64b675f4c4fe8513b301a61387f22bb18a992aa3bce09b293d638dd7a45442046746a5cd8cb42c9bc70f7163169ce52c6b0406c7f393deb18e912598ad2f07b6b444a5b87ad4e23d4c07bce66fe560a076f952f7f46cbf7cb560c80b5335568de69dfef4801d88a0620be2dd922d8ff24245d491429f3d113e043d744d55075459b85db7ebf56debe30d003adc441fde86aaf9d860f6c0f0c6a8ddd4443b552589162ff12bbee12bfa09d7b2f2037657b79e667755982e81b5c8ad18949b00a599192038aa1486ef400bc9c55abaf762769a9464a26fdc5f53b739ed1f138976f5d93409b71ceeff0b0c598a684a9986fcca93fc78961d44d94de4e3d0b8811f9a4ef99f0538a3f10d0fab64e32e058a65072f6daf499f6a32276c9782cbf277a183faedccdacb09a305d3fb2ba370e279c6576286591d8f7aa7f309c524e3967bb68e57e1958cba890f556af432d10e7b6b53d9fe0d88fd8570e0430f5fd69b051788333cbded024e02e7e1d1c62133be48eb17e7d026129b6854071d34d3e1f7b3ab4f8926dd84d30ed5d46b9c0a580cdf06ffe59108b5195f5d318ceeb46f3f4bdb6ad2adabf3ca0afc56b9a5101206c04ebe44d9a0dd67e1af5ae45fde5290124fb95525f08c7806fa02000f32e34f31db4e255fbb519f52a821e7ea2a145dae4dc96de7204c600e01a08d112ab3167c6867c15a3c99f26fb3362e50050ebe1859319cf9bf3888083939b76ed64cdea9755a2a4a4f58a032c49444a13b5af21d33d3d170c412b10a26732d05ed6217c8843fb803c578290ac3c8b65da2e10e71a435dc0ef8325801e18dcb97f7b162de4cae1068822f4339f668c8f2300eefa4a116642dedc2ce06ddf411e4ccc7cbb95d7d06f73ab99b999161e360e4c8c1654534036979a60604739ab337e8b6b6ceb98b046add1eefaaf2e6b8f8e874fb197a9b84f3ac0106008619b6a2e797f1d8a3758b28cc23f2d3d3870a73cf163f042a978c45bbab9107c5ec30099598194e08ad507fa4fd130f28637f81601f82d763a2c6a8ef4901018cec14c87b29f4f670025c16723499b25d0bfcb97b319631784085d7f6b8fb0b40448f5738b9bf5e5aa70b7f7246c8cb76bb0c2c49214018ea9bed4cdd07fc078b58c6d0b6dc6a375587974b7d4a45c0e609f428c32b3ab95efac50b0ea9590b8dc8f47708c6af3ccb2e92b5d013c562efd630991fecb676f8aef568fb1281023cdc5a34a2395bb9fab7e0b92d51be92b079cb345de7ad39bb9b13e1d3a28d0b354c8e438f75e80df994033ca467c7c596cbf0ccac339a292c564fa14f09af0130d5d21dbbc807ba9242acf8eab9bed296c7e0b4bc1e1a4935f37bbec962730c3017ef97e920ea5f34cf873197198230cf83bed8a4f744c61f6ea8241373b0038e7df71ac43e8085f176c7f5199b49ee1c552b5413723dfb870bc6a026f3300205fc4408e20558ebfd1bfb8c286f9bb5cfdea8121d9d0ea83c997adb1297070fe6ec95ed87c4417e24b541b4002bb18e8b363527bb97cf27113968265a398b0fa88969f551fd9a95156fb175e6791c49366cadff7860480a2ef44995f46f4608abfbce9d5426ceefbda7365c307f646bb5baa5e1ee85a4e2251d3eda5161c205187b51d4f8a15254d85e6afb203200bc4a2170d437bf1b71e216d77dc877fb030a6be8289e5d2439584436d82717af348e6a4a405b60c79c5d204908648fa60c0af5efe2eeefc30995d7ca5ffcfb7f0aefc09b97ef6543090e8206f0689d240ce5a43f24bab1016919f7724095fd722a8417c98f862455471ee3bfd18be988087013d0079f0dee4bb41697712cfafb47615a324a7523449654daf6b43c800f03989ee5b32168f79709c43bad1b6c52151dc4ec309cce174d2bff5b26d3762404fe8f7b90d53107ac294cf7b4a74c193894c5f844423736aa4ab36b8cb5da0f042bd363f8ed374845854e111761c95fc8a875f2054b144183d35129fe35b9ad008c56d26a3592bf230c0a4f46aa8bb6db6fda50ab273add085a3061c22c537a0f4153673281790af8f7bff7912ca3399b00d9c2ab9a59fd11c13cdbd07637330db9d6d0583a19b24425330a2edf329b1961330b59d852005602f16768d2e8240262166ea37417d5d0f7624dcd4187d773203f5fc6aac18049b5397ad0ed458b0b0eae05aec54eb523b28565f0277986e6a7f36b1ec20b06ccb669db34f27c05010a16f88b460d8913d001a25de85f19d59f7ceeb6aff4e4978ec08b22682f1e03bfca92975096de8b513277783ce7f4d20471d582a7159067bec137903f008e067577aec86f5828ea5cdc06537c6266b76e842158b3f368b69cd5e91296a3410a718fc612957014c52097e67edcb6d60e0698156d869f31d8675f5532f774f40ca56a23cb1ab9638450152ffb8adeee402ed6b19d87935c1ca4d7c25d2f616d0d4300683c2d088216654da8c715211ad0d3ddce340e9ffaae34c55e06d627180bef9dcb050852895b8f1edd356de53be21e7af8fd07bf18770e5fbbe5065a8b055d54a2437db50fb3ddbeb21bf381f99da4802c7b31abf01f2c7bae85be95860140a1c9a39038a511d6eed966b30c4c49ff95b1f91f44136ebef08444815318063b070ed4d7bb9c3494e724cf1deac54e424e2616b9d5ea340930f2ee1b8e34043a3955ee0a7deee391f008306b426a82a8201a34c606295c49fa2cc553f72300657f8a3e601eaf8b30b2ca93eb05856198148259460608a46b83477e6a8c6a00b75becddcc6d0d3dbf7817249ff75a2989dcf8ee5e9baccc17e48a21ff114e0f -generate_ring_signature d6ebf139a45ef1fa4b4be6a9def628430e9a0ece08b67e60646b44d7581f62fd 009fd2354de45de4f50f4171609d01f3af1da8c4a966e6613656ab7ba547bb4d 2 6f791430dc6089fe5726dcba4999c2f277ed2515275152c3615756c85ca7fce3 fb5ed1804e5b50fd5e0faea33284e7b91d9d881a69067cc49560ca97e1828d7b 689799e5ac8fae88a1fa82a03c0dec56a2ffa69f983d929bfcda6ea30e735f00 0 e683d76fd18bdc776bc62ffdade8f137b48d3e34c53e711b271230ac03ee95065383f3093739aa689779d7ca6def137662938b25485c46c65d38f31a935dfe03786ed9fe359cf0b1c193c61124bcab078bada60ad9aa7a479e534480f30be70ca919f585b99fb5b8eff57d905c229aadbbfbfdaf75ca387190bb2d8bbe23bd00 -generate_ring_signature 0ec44a65dc63613b9be298c6039f1c13f58071e1b8f642259fad92b82e70ea6b 9c1b7841db55bf2d432d77363bf8f7871946d1028ac185176bd270b9b1c8245a 22 0ac06d71a291bc9cb8a65166f6e267aa94f67345780fcc3a88990a1fa5ddbbae f4dc2655b78a75a5c02d1da6fadc0e7df8d10024cd2acdfa75d6b926d9df26f2 b898a4bc2437e9c7aca7ae42b40b9f55801251be6a22e06b35cfdb9b519fbb07 5fad8c5d05bbf6e210b426f1c85cdf65c610189659bc1dee66cb48e155b4848e 06c86b3de6e5b17a6b18bcc3bea96bba1f0e53011abdfc8d983e7128686aa565 1981523626d0ebc509c4975cfe7855f5935518d53bfbd8020103875bb04c54cf 71c3b087b4ac47bbf525fbe96f827dbb8ff8cf477ec15e188df1f7468f12319d 76ab284bb2b33394e49a7b9b6ab4eb0fa9175f8cc0549099afef7a425cfdee73 035ae37614df557e2879632c43dcb2a6e731f45ed71d4c24813a2fd0b33e27fe 4619e99967c91eddc5bbb3326d8bafb2d91a69e99120dd426dfdbf0fece87b2f 6c25581aef5948e25e70cacf2143b8aa5d09c4f0f71d6aa72b8f97acf504c9a1 035708993aa26e4a03188068a9053b2c4daaf5e7fb6fb5b63e4c5b3d5fced851 7046c37e9f70f71033fd835f432553e867df9ce1f42bf67e883fd1eaaf6fcd91 fc9c1fa387d94e42b000d5196cd36b4d9ae84e1a4cf2de91dd15a8e9c655995c 80c9ac968a6a22f994705609c7f829f80e9c56786ea24c1048814c9bd8ae39a8 4d7eee539bdc72685a3e4239f78c67756fe3972e1b88ec61f5ce7de6e90cbca2 9f080a14a10352a412dc2067d03db1e23a55fc00e1c90ebe2e3edf4c9067f8ea f124e82150067aef668390263f418cf8bd5ce9a01fb2907e6847231178f4cedc d28e3033f536023ea168cc178fd5c9a9e3ed795f8598f7cfbb973122a860e43c b501e956550a6e563a89978557633f9d61ede3fdcb4f5f73b10fd98961369c19 a7c9d688441935ef112dd091d6f934bd055fdc508e39f78035da02849b5e374e 5585b13645eb85ee005065fdebdd45914a3afbefcfa4d5aac0b2807c21bfe0d5 ef411cbe80bc6bdf3cf2c9ac140669cccc0c9355fbd42d32c545a67b3bc96a02 0 a20a7f326b5a3c9424ed93132a2875eaea9730c10ac154d3cdc4b242fbc2d90972f604d5058302e5b4245123d0db5c4eb6c2d38d88cf45618d20e2efeecde205171bdfa037244dfdce15bf69a5d11111cd515ae86c9abc85910570a9a52c8f03ff4561e213cfd106249f91f174c65dfa983748112f043b833698f9c03f3d850327871119b75593a393b2537a4eae19d194aefe2fbc99d00b46b7068e296e1908a2162b01b17fcf4109eb5881eebae4dc62d05061d2324267243d4bc5ff1f740ab1550f007bd5a65ca265037f95e60a29b43c81cc150fbf45dc28c8e758f99a03308ce8ed92a1c538ee62b178cddccb4db5bed88bbd6aed6b08a8068c2f427e0f12b73d9b880c7a2bee47bc3a54bb6aea3792cbfb2fd3dd0cc076a0502b0a0107443b552c8318473588878ea5c9b6f8a0f56d08bc6ebf07d660ff649ab808da01e7791129b542bca8716958c1263340c543fb15ebd69ef62d19e7df2e50da3209503f39cb4c3bf19886ca5ff7319d17b17e6fc1f05c9c8297c1352b195e1e9b037ce5f86f7ef1a776019a92ae13e3d3cd01923d58d37aad6b8e266e470449af03a75162db2fa06bdba544c9f3a4de465c41d20d854e5ea4b4e5dc477c2decc60a272dadd8cad14cac3af3848c79730b299aa2d9c29431c2c6d60412325a0b020427176e4677da5159e6d9e4bae95295d938575cf5ba71ab4f11d2ec6362e36e03d102e2cd038c1eece1360445e222bd3aecf0e87352c4709d3496ffab403c430bc7414a46b36316ea0d60d5679ec5e99f66d9fa84dcf7cb4e5f831ae50b416e057408087d471a859468ceacca8078e787703a2e580fd22d21befc8c4c5cdbbd009579c91a032a602b621c81b749bd9990bd0da43134140f70ba8c4db4092c5f0ee2269ca42d80411cd4bc20dbca36a5c973ad46b98e57f97bec1c8599eb65d107f5a6838b9e8b6fd4d3271de5c46a180d7de863fc93a8411dbbe5b2b84388b300896f18183874902cbbca7ff444bb12b699898b69ca80f59829a195378837550ef76d2a17486dbcb6a75135ec1fb933202c00afae798ed967b521917a3d0ea10bfb99ec952c4ce244dbcbeffdc132b7a32931a1dd4afd0dd0f786fc5b83b4a50637791eee9c61e8c836f5235b31ef87b3336e8a2c660a4463ae93b23078b1d808752a02c965509851de48374f4d815d315054f6076e61adea1f3556d58debf509e635c872d5b9fb45d556aade31586f07b85367aae629381f1782b73cf1cd440ae44b78b444519911ddacaf8d9adbb4326b7437204363a5dc7c80d710fbd459067f206205b8574a8a65f6583a90e3a2a12e439431423161a61e9487514ce83d0c7b478ac0d9419551b5e80ac32dfcdb9b6dfa68f826705f763035ad30874c4103c071149831bb872b97683f182b0b007365e1586fddc4c6eb64efca247f685404a3b85ccd70ece4bd82094789d1062ed1df48b58ed7291b3a39ad08ef92962b0f9813ad3b90b1ff8ed941a04ea7b22937f6071040feb853b067185d456b96e309ac7063c15ffaa4118614691c65ec26c503ba12f9afdaf83ef8b7de515fb3cd04f6bd93ccebeaaf6bf530c3382b80ce6fdc7fae3b4a0876adab9e71fa529b750a58eb3fe7591981a2067bbb2bbf9b12467898300503a9797b5e07bb5faa1120096eeaca0c5726d3097c2de3d5786c952cd15b8be17a9e5c2c57dc1abbd4ad3707b8ad0a93d11c27224a19d6b59ebec1bf9a64a36429548dd02dec123e6661f307abca70834f5ca1c332304446bf11e08cf27a63597b3d73e94364edd907af7708adba1e91f21c5d910146decbb1db0ce8707de1e57e18a6204e0b34c687e4ad0b4abc43516cbe5daacaee9e5122b7013da1ecb78883acbaee056f6dbfd10a57072dae7e4cd4ad5d51cdd1bdf6b5cb978766750adf2c89d6fb3117143352b4970338f4459d1264a29769bf6ea2ebfaa742da62e61a2bcba5fe47eec0ed3236b00a -generate_ring_signature 93493d23fb44416ee09be43865923d0e1dc1066d56f5a3d0de81813af3ba0dbd 7748c0a5f3f87b8261d37a2fcf576f3641db82845d8c9baab1255a025e947fa2 27 2d160aedc903d73ca9290d7a6ab88c0e9f34d8d1eb85ff0c2123e1e619aab827 7bb969822d0b461e0f71f8582559be4532c9f9ba4b98a3bd8037f3ae2cc99086 3973974ed9fab1a8bfa2bd46370a22e6aa9ee366333379802af5647a360dd8c1 d6b7ed18d47a17ddb488bbf95b3af8435f2beecd767e1cef3bc7fc0a4ef7c4a2 0b30df77534bf83ade55d67c5525d900a8a7a4e45d22af5d3fa8036ab3ec3a28 7fbc57d8de03940bc6ee8cf3f5598cf04672d58cc53793c1f62e0f193a2cf264 300807e7fa48d0b41448f7307d2e53d080ca6da2dbe0b49856acbfb5ed7e126b b8caad285713bf9beda3f580194fd57152f67aec0e70a93c28928f5f486f59c6 a07af8e32900154e837d3b7b74d5eed2eea6b5fa431e09f4927256459a381021 73aa4e3f354b1059b14d1cd48f52502231c181cd06d3e377e93ff7d7e4493348 ce6d6cd79f36aa44b40bd05e6a087cde2fda0d82d4cdaa6e2b0cd36b0b3803a2 038030ecee860ada3ba07323cfef9534ea0a90d2b5d30f2af5cbadc1bef72e60 dea728c4b55cd4db70c5c69438fd75ea5180675a5b9d66c02fe1a7199e9bdb69 f27326d480de4e3b79274bd526dd79d24f11ebd046b9a8c5b376c44061d23a9f 136ba04808b1ddb0227d549f18e8322002c8f9e75600a3abd5c73559ce0260ea 601eed6733db13d4cb81bc73faf4f9f5af74ce56405f4bca0a048333bbc439e9 3c0c6f776ae4ee29c1c691d0b57309ed56b96a31a945079f41d3ea89e80f32d0 7249c2ae5af899b9283262f07f522602c6d1bf3d88ed92e0c1b9996d76f0447c 9518aee525652a3dcfa5a37d3844e7b2bc051db5335ea438673b94b01a04f8c9 2843e804e929b6da93b04026f44ee744eb364f20610f686d5a73fbc43b92663d 6b5281b6cd170d9cc7ba7bb56af507aa3dc21e816f0acd1659a908ea9a416366 ffe9b0f145fbc6ea4dce6273b27e9287654cfaf82accf33885fae7b152fca9ce d1bb3f05ab3be0fdee933d8492e1c5cc24e7488e44c4258dec75b4b650a9892d 8203602357f52070b4bcfc3c5240704ed87ecb93a0b4812512e3db7d9d6ed061 1568a18b7fc569e1cef7a211eabd5da6fd118cff94cd726d1b4d988598a51117 0080b6bfcc6e68ab3bd176987ec4f0ebfa08e2d5bd6901cbf24586bed9ecb4b6 6b608f086d9d4c67f3f1daf684659a389c5c9140c7b6e5ca71ab5b107cefdd79 dd1d64ade0adf36ab84cb537861c99b79a6d012ae1f994be411baa76db298b03 0 6cdbfcba6311af115316eac678c8907f541115bbcc35b7461600fffdd8c53f00474cdb473dd921729e7cd924db9c7a88fbfa8f78ffd7af7821f15f3cb469250349a76c034a6879110a01b470bc11890c3f1b33b073e8f59d584a26eedfedff002f2e4c709706751c99110d3ea168c36b07e81a42ac5c0e0d37bdcfc560119501262ff5cc5ee6262c1f2e0c641f429ad0ecf7aa28562bca2b4f05156e05a55f0c6cdffd2ffdd8705edd3ba8e31e55eda4a211ae72bff79a66064cc80f51b4b1071b92609e9d1eb0705f88e17613099773490ef85e1c90e0a7662c8fc402194109815ed64d65df9590c4ecf6a5c1154ac5be2cd23700059f9841f626a261044305ec8c6a0d76f63b6f81cf73fe19648aefaa30ddeef50b19b564a5dd4f695f9004f867d90ad8cb1da98e3178d28ed6fd7cd7ea5e71785a4ab57bf289200d34d40cc28767791943cf309338bffb3aa642b625fb6edfcac29e680b49af7b807c5a0cc7a0469ee647e59b0394a0554eabd1ecaa68cd98c04d093adc43738f4a966a0baf25a7d31fffe2072428f809880b2a2aa244f53a62cff0331b6b0cfd1608b0021941a28938ab0957039bd706021a004b8b52f2c70654bb941a0b5893a4e743092a31d5c5f2a782b35a20d42fad3102947d1325bf208fc28747e11e0ee5c23f09b89e15ca984a30a68e6d59fa3558e60d73cdd2ca8c0e24804b63f3166995bc0f2b2456eba828d234c0630408531cd329a60c98eafe3ed1bd01e3fe279513040fe091ca74360cc117eeff6555780174bb79b8e7e32dae26d4d9d1a26a3307ff0eb2a079799314bda073ba9dfadac4c79d0532f8f99d87790b4de4c91150d5740487acf50fbf65ebfc4f182f445f5ee81de7eef35387a0b30a153e8e2f5a50ce031e9dbc755569a40f1730acefa6e9644ec62273cfa7a2c8efc68607cf630eda0a7ff36ca054a19e797723ac9c0a5bda6a02a8c1933dc6777969b0268d12be0307c654b024f4a340f5143d3ca93e2903d65c20824087ec57723fa4f8e4693fa7022cbab5a8b9f21047c949764fbeaa81ca9e2c916f8b7053d19c3b50edccb99c05fdecf7adaa1205e76199e2ee1c94f57a8480ebde89e7391b3fe947e3e711c80f6b32048dc2ba0e738af9da7b4b242c755a288708598c8ba45643366ca1041b01db823e1dff8d508166ff4d303832bedf2a3f7323d7c1d0cad6d46c8922b019047664cd4f4d8fa9b3f8cfdc564469715856b8beae1214d1c7004c62852678a2072fca0392e58db7a95c2a76a0d6ee6a5409171218ad1139b191ded1f5d48ce80d5b650df38ff2c0f71922474fdfc206d57c6689ea2cfd6d43c10afdef5ae8b00f019e9e8e4b38a7c9168822c4736be3f38b7446179ce2487fef7394a1ae7418064b45afed5bb6f0fe81b8b700d46037111dcf5dff51feac19c0a84a1aead24f035091e4481ae9e58bd749d3f35c16520f9824949c4f25b60d4d726758fe6f4400134d891aaa24c7b2c73c529215bc93818828295c87d743e547fe696fe9738804436884360e2ab60c472abbd067d3033469f1ad8eb0d1a65e9bb9a520c3e8ed026b6f8f9b55e75934539b732fdf389f808cd81751ea72135f7f9df0098b73250ef180f543ad85493ad298c6d7315de4174596058fe674d54a8e3180982cc78c02d3baa6a92a9b43ecffd25c6fbda9b8de184f8ec0d17dd4b6bd606211145b260b20691084dcf11ef566b9c46ce60fdefe492314589f1d235a4cf1f1c1cf471a0ecca9b639875ee3f65bf16de025cec2cd851679810a40583018f3bcbee74c0805c786e4bf7fb6db95eb5642964e9713c338f72f9e716f9e6bfdd75df9e43d31061946064056b6c2ab31f98d1589a4ff7d318a9601e4baf7c670fbc29246604f02461ba3acfbf273365fecef7081c6ae4b243244e7ee95b8eb140e8679770ec70fb3f4c2d5af94206c5d2febfef2ff99ab62da3e263c235a70ecc957774020410d75d5e3d50e8cb32d6e5948c9247a7b85885acccb49432cd488a91e7beb88280c9410349069908d82f8bb74c9a7b289bd3ed8c191868319cb4ccdfed82116da077592eff8c9783215023d063090e9e21ffd339501be9703a391647b9f3edb7d0b9e282a0c806f5dafa74d110a4f38e4829c138ffd3459f5f22fc3cf43b92001099bd43f856fbf68dfaf0821fcc39a5791b9cff8522462bccc14bb14d327c7a80b6f9609589678c3140eca34fac9187bbd051f41629e34134f7be00fead8e4ee01f8e848825350bf6fde8e5ffe62a415a5f0744a6c30023c5800fa12a9babef0007975134c5172fba56b53066a70a39385b70caede3d91d808f02a2d496952b20d8b08f80d6bd57d7f549a13f1554bd922d4545fa7f07accc74ecc011a63f725041d76ce9b3b93304ed4dafd0a5ecac5d43c504b3ea472fc816d2244994caaa900 -generate_ring_signature 47a17eef9aa4f45e349474251fa7db40f4dc3b2d9cb74a0f63628730e4f087fa 67206377b4229f14b901ee725025ad9067500f80708603d343cde543f6220070 16 b9d6ae2e4275913b39ded0e04f23f415e21d0755e6f5f15398ce24db187b80be 7443760d12012330c27c0d958be3f5bf3c543ea087b2e710f1df3aea40a3597d d58c7950dfa2cd1e5d5958b03665844c4ec07d3266238af73c026b2df9c9d749 22c2ac91231c9618673ee8d8cd01a3fbfc915f6da971019e5ed1c04cb848eaf6 bf376c083d6e0298d8e0bee38fc8a296ff9db39661ff0acac51a7cae47a528ba 4d84063c54fc35ef611c69679d6bf8252aff307262372e127ceb6ce28a4eb57a 191d335866139d9a9d70172dcb623d9d0d589bcfaf29d639db9408105350a6e7 16d1388ac89e6e7be13d79c597bab902ae51c727f4003e0e32d6a9944155f5eb 7161b56af7340827dceb9215c1ee38f21479d7deb8ca9aad4cadad12dd190762 eadf42743e1b0f76badbde7f1b5600c599eff2855e4af3ba1e832b56c745121f 0113294d12228749802c3502457a95df6875abd7dd63631232e319cda4ea7176 517b3628d5b1f6a781dc2a65084e515d45f745a55acf671b478d2553006ae2fe d1a16b1897c1379411ea2bad7ff86ac19c519141174182de83c372da21e04676 9c6e206bf2d21ec5f36fdc489e51f9748d748a8064e5ec0ed21e7210b6075d71 fa2fe7158dfba5f1083ff8db88ca7510fa17a08da7da486f3194297ed4ed0afd d467bdfb269e9f5e8c72cf6c27b5c7eb4c2beccacd7c0fa37d22cdbe2d15807b 02584ea85f70c3ef4912ee658341e6b6dba3f2b9e3cef23d99ced95aa50d680b 8 a7235b61b4851c163d06d4440460059951dbb52988a58980264fb76f3520cb009962cabe66b6e9148935c7dc5209c426e7cf7ebdde0c1ee5cb19fb7ebfe3bd02a1caf7d58ef4db232af3de5b9f9137032683379623966a334c86d813f1644207236f2cad0954b39a6119ef4f857663efa43313f6a32683fa1da48e016057cd0b31c31ab4a2ff9c4961a8840877e982f7b4a30b7beee790c96522162c3ac28d04a80b28b3308faefabf53fa75c7597ad2a4a735ed29a423227e676414c739bf06f2f9c5bea5d7286f22ecbc139c5aa39a080da0f98aceaa95c2905ecdb3469a01747759734ea2e9add1bb9cea046c6481eaa90ede82e44547e2e9645b29790e0d17005c6a8366380c41fc78311cddf8d2f904b88d31d02032ccac066c0423740769977df6bc8500f80c89c07a46d337cc29184663a15c566da2eb07289d09180f81bcda3dbe544d2c4c2cb66135bacb3652af9ad0ec012ba6c5b1ec57ab253601a493479ba4026c439b56739a1570cae86139f08518f7f955594fdf2b75f02b03a8e7a35353e2b19d4c184518ac8a61aaa0cc01fda828f05965ec8141c280730026bed4a25fe5930dd63f088b525e93ba11a466b3b201b6f1bc14398fd7978c03f86a65c64fbae28414dfda972dabe8ec7a2b09ac374d4be5c7ba8c7c74d80906d996856655c740ccafd2510ee3677ee97d4ada4e618e948477a034817d186200cee5f025f518c8a50951665fafcb0748e22b2c7da9ca1361dc8a5e09aef9bd00722c1a1bbdf5d6e6e5ae317b3e0596e9e263b8d1076aa5e4f4cb741eae49eb05cd4e5157b76a9c6fe0e3a829163c2463b972938854f043e2a0168d8c5984e10d994097538123d6b9af395a99aef473a8bd71ade5d7bbccad7f675b47fbc2630e0c7716b94fa007bb5e10c6390b9d241dbc1b2cf3b757b85f23acb2723cf750097c9474ba272dfe5e05765895f3ccb0e128b79b70192838cf7718d5818d8ec106619bf3c2dc362678132011738fd03b364a035fa82fe5e0863a2c04e2ce877e03189bbb18254632be96973a3f27b7de7974c0c136b635781b83bdf85b53942a0037579192073a96e431ef6ce6d0b57c5f678fb69ef66b5117b184cea9cc23de0fed228fe2a52bc02f86daeded1060160c4dd91509c3ed4a489eb844cd11f1400667e677ecbe0afeefb2b037ed1e7f9cb37a7dead68509ab6bb6d7bbf05eb94e00698105607ee56441f6c2f933e64d4e0f3d4febbec9ba4ccb6aa363115b8e18020ba78520260c26a9171093287dfd13c36daf57c6146cd2d208f7135fe5e07a051d7bfd81d8e6aad3944fc381b391a3d95b354041b9aada02c1b4706969c8fb006af3e60789696abe9315c12fe53784fd9721ad66cacf56b037fbe0e5df693500d7d99fb55563fdaad29fe99a6390103b8b5e001e248faee667419184a5151004 -generate_ring_signature 9a7086a8ef73356a7a113ae91f791ea57a32bf63cb15766556218bcb2f7582fe 9eb46385239b056e240bc6a1180c51a025b0837da355c5d60055da138d05cf09 75 d716b1fb66fc9eb0d801d13e48a4795600b8afd004cb31bd7bb69ac7bf26a5b8 facaa9a563716b9d22e3e8204e8c38ab90e4329812d9bf33dcf49322105ef8bb 092504da28d4089cf7fe33cbd6cdcfa5ebf9ece75d42805975564bdcccf34497 893aac484591284d041d6cab92ad3a0e187834520d90b3a6686276070ac380a7 4e8670484c0779ba9975ac8bf55da8ae29d278a0334b23e670fac6eff5bc133a 7fe32c5fc50564dd44faae596d56dcb817232c1e50385f248e6dabcf382d0d20 5560a7525b10a96fb989ffffaf9c2a20291d0d28be117be81cf7292344c26a89 7abc90f8cf53e7b31349994334ab3388d209b38b0a1747d656b064255933b80d 4b5931de92f69f1272ace927b49149cc942276296ae99a42e52d7d83de3fd414 a2e67069563571d1eb1f16694869da1a9582511ea81b005eecaf1f63adcd779d 23c1f904a58e6ca0d91c4caa3dbd7cd5baae653299b339f9859ce870200db57c 19d779d89ba045aa643e717f548b247156a41d35e7be773d5775eb452587d44d 68e999d5a013b3d945434a46adacd5a2ef18ced42ee0259d949aaea9f06479d4 f8c85c1538d967eb627ec666563cfa8203b978a8f88a236f4fbe179fa791c596 19909e7839625dbff33ff26ed186110acd7b34179b0e55fafc84a71b7a3f7e31 53312455ea259a3056ef247f4b25e63a807fd2fb463b0ffe1417cff46ff4dfdc 3380930deba9c016b036d71ca525f349ddbf582ecc80ed127da3272f6aeea4ac dbde3d341c05dd02ad333c64a9bf35866b1f44da659a07135875cc65b4d65439 06577af36ac3f0ac371d8a2167f637c741b9d18a79afedafed8e943a081733d7 944ef7b3bab2fd90c5a46437b068c23d4a5074ccf0d0a6fcb960ed9763c0b880 01cd86208d37c5512d16d459a4581904d13eb862fbf9e34c24e8a1c3233da0eb 8a10901b4799674e91e6c496deca1f904b720dca6c3a81364bc2ff03a7e57897 339b3dac7330ec98d76e59a6e414629a1cd465d14402c6404c9655036cb5a010 c0f2a7cacf16277a4c84b115d21db048e8180a15b013af3f295739cb0e5e8c2b 9bb9566b6ebb7e992a2e6138c810873127f3406860cec5d3e867f676f23cbb73 3a31153daf08deaee78252f49b658c875d195343bae2a187c16465b4615b64a8 5047c33140837bc18466fc3d1e178179768860faeb7ff166f65450d53c7dd290 bc66bc18c1d6601746fdbf34d4f9c4e7213079d15eb054cf570edbb66ad70591 fdfdd237e667d70a530fc4f387af0473f99a1f2988dc480ac1606df7798798d3 d982e4a92446e08a2e9dd91b155e580f3d181bde1d5f5e69a6cf5e82d35607c5 f8b566a67779c1ece46c432d20b280c5cb8da1184f7eb242c33759434d7fcbc1 6fcc2b37559dd4cfc22abb8b5fa07eedca9d56ade5372f2e4a038ad1d0b92c0b 15ba3fe0679958f07c6deb5369d356b48b668ec67d4832ad33ff37b0abcc629c bc70e3eae472a511ab3e547f91aefd53e40dccb0b030fe7f0f5c382f41337be6 e74bbca25c9180dbf84ee1feacbb9e6d0514cb6acadfc7fe9269d8f1441c4004 0a681694b9181b8b0eee8388c9714f2920c63b46ddc955643d9ec4781c2a820b 377e5e05a7d1fda80259beb3d2d4067cf955e08089a74a45b460799745a52532 198981c0b8d166af3bff21e762bd85cfaa43cd7241d7fa611905b4fcc67d6236 1c533cc6c0bb0f4eeb97e36be210351f39b1a4ab10b7a339e92aa187a6e08679 955803e9b32dbf08ea6a31dcb5bfa460976d058cd5d35fa2cdbd446dd112b3b6 b6eff72cacb508187b555d67f22abb6e73ef2528404ddbf17411ac4510c44fa6 f55361c4f5dda43876eca6af215d10fab43d1fa4f029a370a08996e08fe5610c 38d3a3e8c7d263cf018d96a9c81737c34aea0b2d3092b63d79ca96a2289046e7 5a6424e37427e45b539c65f46272da6b578868ba83a76e6a0ac240c3004ccba0 80d9e5a24fa55306544acb007c88c87a5c65b6f3f814d3138b56d914a7c1f046 7635087c2fbf829094a693322f3f3ade9c719ffb418e5265c0186f4604a294f9 00ce84cfb403560546340864755a5e399e5b2c061645bb7c12a7c99b6098ae82 dcf0053ad01d6cb871a35dd4f255956e50f487ed81b0135862982eb4843df3e1 ae49f8be1790b697b4e8dadcfdcb5786ae07e2623798d494c18f22181ee1d4f2 36cf774f577b9ed47efc0b0b8d38dffc78e6372ff1c3326e336b71600789f487 e30e26abc6a8adc3d0e8a226fee82efdb0462b698b98a6a1878c640cb1cbed61 11ecb444c49b4a7571625fd56f8e30c9008cdb5ca45acbb8d8b716480ca9bd6a b89f7406af985457210d4e406755e995cc07515a414c0c6f751c4269ecf03eaf 7456e8c3d729f835db509c26a5b20a9349d73cd5a59c2f09978df89650f7db88 378ad7b8655c6acb0309dee66ac1cbf99454869528c5cd45b17cae0386db60c8 86cd4f6a7bdd988a848508a368343b0fadc1193111fa561687db3cac67e2696e b62cc61b3a0e5cc7b99bf1107caf5d810d95ddd4102ed701d862426639bc3735 01b43550d9efc0413fededc10d91ae556a36c29151bf36e94c391f162ca325ca 75c92f49af4ba741ad911d40d2c4e021927009b71602601d9432468069caaed0 4952e76de4f8e470060e655a18c697523d7ae0563ed61dc70a54cc3830e3c988 562ef593d5dd96f709bb82c1b59ae43ea34b365a05026a7344010abd6c419fbd 4699941b91092f0cf560e7f9f35db0295f952f11c5821635676538495f663201 ea0d5276b2de554255c56fc0589e3795fcc9f39234b354ad8cecc554b7b8f6bd 685df7e6f427618f22fd206f70ae5b44d71f408de837502005777fdd92bbb4bf f364ed496716cd88da9cd2b9b404f620868e8195de8d2f367f2c4c80e708a16a c7c368adbe6409674f1a2fc603ae4fdc1a7cb31ccead9b6e1443a6bd90fb35be 248ec8d63f6c5cf526819521d866ab1bbdb6cfa84b0e87dd8a7ad7d1a47a841a 4dc9ca992f062b7a011c372b3e99f87d54de6883627d42cc97bc7a576a551d1b 3dcfa728ad76ab77e634add3524207937374f3eeee11a566b5babddedb647fd7 77fb0e227ad84997ef3a5f33c800b7ad6033aeb0f3eca8d688abc8008dba62c8 b8990b5dfe159a6db927724d9572270071593aadbed7d4e9b9acc4a1458afb70 72886ce644da16d21a4eab8b7d00dd65f639a40a5dea3579ad7923cede155007 6ce176ac239a4afd1d9f61f7cea9286ee6b5a6c3f5d8c76c02a3e6c59600deaf bb67aec3e0b94a73aea72380edf585f9c47232f471b81ba1725beb05d6352cdd bd4034fd4c3d280c619ab29786e48bcf7bcc8e839941cac09d57eee9a67f0cf1 2b9967f5aca1eb4e0601d393b1989438c1934a0b0dbdf515637c2fc7c9709a02 17 7038bc55e6963405dfb1f6368d039da6f49615cc06501eaa63e912ad83041a04855d0e9183a0b1771c56dbb22e6d0c8170825231885bc9a87d974bd3198b750d3e7f0416a895c53f20bebc1b9ff5cac5cdcf200bbede78131c5de2455f8f4a01b77528130130711da19562e361c2d56420cf46ca403f09f6f6f7f57fe25d4f004ce85c85a41e42ef01611ab5d08018e00477112ab65a7edf99d8eed673c17d035039986bdb9e12d494ad62768fc356664a9d7a5dda9fce6929ef7b05070ffe024eec908c066d24f1fa0028e6e0cfcc7f39ef7201a9a22aa702b9242c2afe5a001ae7b75c862ae2317d13c3a55fcc88a2bacdcc89920c576545c102a952e36605237fda5bb99597d3619637c4b8dca3c18f4ae7c04c86d923a8c8071dedb73b0230c510d6e1e5a60d1fcea4ceadd1d17a675ed0fdfe17685f8b65011f70b53b0b4ebda4f39cb6868611109829f4002df51ba55e731517091382e49ca9e1b4e8068865885a09d4fd9e98c3f36176dd118e2abeea70a171f7d188c6677b838d7c0af44df80b10f72be267d3a923ff110279ff330b6ed4e5c0622309851a7686d808023db28fce5c3b7bcf5f15b11898c037145388935d914dcbeeda298281efc80fd2ce749d6837f301124b7838988dc53cd3cd049f03d92918a939ef36f8f2a809af96a6ea45cde070691da9971d09cd0ac57767486570b234353c99f45494f00a2edb966ea65288bf8e4749e7422b7b5aeb4bdcd86c37c6965663fc2aadeb000db06b0d00e66d5c00c4a4908c9c831899d84df3af02af7b5f65b42445e1615701b67ec697a27b47ffe0d432ce260a7ea4e3b98592e4faab32b0b47149796c7c0432fe1abc64245a50d0ad35484676fd0cab9b5582702c9636f8ecedd4854e320861b5bbf6728bf0428e20533128eeb02ee042372fde49a6a0b2b324fdf1561409074c28803e653bb17ae34593c81ae9af2ef3af305e4a8b6ca2088adb0b9e5a026b8aed42fba683e921078b1ac78692784c886c465380c62fdc6fbf993d55ee0b925a5e87e0ce42b9c7bf53a6880f2f04d93a0e732d43e85765360fbd3c164d0fb40ab193613bf83a36dcbd6fe4f673a53b51a92c7067097712e51f0c9b6a460862c147c45dccee275b5677fe7d96f70288c488f3717238a7407dd568d74cf50263abfd820b131c2ef4f7d7164d6b736b54b5e10c2a78e51c17a000552257ac02363425b13e8005f67f267ea0e044541362f410baf260f53c46a8dfceeb4aaa0456f20763978a97294f8a7206c309640fed39110838bb3ee655af0b7de5cbf604971e25ce909fb29666beefdb4e22214508593bda0085e14e7152cb18bcede30cffa6abd16a7c999b5d06078e52c9c8e692d58869cfbff0bf35d8405e77658b0b264bc8ea021130fc3b936c489196c76f693396d2d372096e8ac97fde8f139b0ec8b87ca727dfb4e2a21d7fa2d3b6f8f0f9706344b2e050dc4e0f96022dcc750e4df19a75b0ca4a49b555b492571732e3305001df6b62afe48fc2fd1319064c045217635cc8b29481b138d6a82045f6307b2ac83872b47419c684731fd80c8b06725a69935f331222fe3968f195c47ae3a83fac62b4fe8dd0991927568aa87f0031fb5eb1275a1a0437a1c70deee2ddf7b6744316edd5d94025114d9434e19609f603f6986aa7ac6ff76774d2b5e319711a070bcedd0c164c48efc7d1c64d1c0b5942331e44c686d1c0d93c9eede3c63f073f06d60e9042443076aebb9188c70c65f17b1093e43ade726af09c7134343b54ec947a977aff8962a9936998fd5a06009c3d645a74de49015de95daa088b041be458982f6f6dc35d765c56e781540df8cbcb1c495a4609b762ab8bb8396c9a482420d4d1654d020413e9d637515d06e1e3337d085ccb36b9213ce07ef79aff6d2b4e88ecf91b2e23d486333caeb50f6c4afbb6747cefb82ff106c4f268b25c8c9e9db4ce1140aebc2dc9a777c53b0dc1415b66acfac754656b123fec38fd1cf5dd319a5de0052b31c465bdd1fb760d51737695b6f0d7582fe29bddb173b7bb453f1cae8b5fc8b459667db00b3d94099397ba5a84a282c2b6799ba382d1606ddb4aad7b3346bef070afd7e0f9adbe0314052e56868364cad9dc73f5661a607c29ada3f6d4ec22ef9148ddc3631eae0d4766c6f36d3bdb13f07b4d5f0649b4460fcbabfeb46dde7924fefcd0ad0b360e2fe56c3f38ec4cffc9509e7bd9f5e8a4b1d757d94e1be45d66217ce2a3b036042651ae82d9243e06953494f34cb231fac0f26772207fef194915fb4bad346c0b5e3ae4843cd6b8b25d1a711d50ac0c50fab2f553b645b96cd5d3acd7b052890a4f7dda5cabe169d70c0619970b8bcd44d8232953e984cba0b2f9f7e099c61403e2200a17aedc3e990a98b9aa732684d27993b691a64a9bae83dee6fd506feb0d1e8f2b895db1457e0e3ace39ae5b3ce0263f0cc90cfc7984cc78ef6423e21c005a5917749d361f0d9b235bc6a179b1da48a6002a45c7016b9bf8ce2ddaabdb02f723e1d532fbc4a2bc2f6b6dd7f6738ed25fee29382f2b18a4df2c6bfaf82f0c8c92c2dfa298946106ec8721aa00715a64324781b77b4a36cabdd38575e813043b99ad824d1844980a891a8151e93c315028b38f7af0a80e042516163321270b991249aa01e803aee99fb5de185ae50c00f45c8e64c58919d64c297f413be60579518ef231d0c4908254b0f1d207101aab7fc745165f66e73af16228e801e304c0bd1f275ec2c06507875d85c8fc484b45062808ec1b5262055aa04d24e23b0babb3c23dae4a3b8fa69600af35cbc809b402b529142ba85a13f0458c5e9513079154d486ae1c806fccb70b8aaef7b9191350faace065f79095658fb0385c47080f1110e672be86bfa4664e967bc1ca83263f8a9fb1de342ab0a0f6a51c333a0c80427ae9eb5c342bf2fba7d3c0b23d4fe7735ffd1d2728acce10333f1e71e0056ad32e64e01f90b24816dcb42b0c3bc276bb854840a2195e53df4102b8fe5902476445a80342279423d545ba1e4b370d63a17f40a839edfeae8cba75d2ad7a08d1d33ef7c0943d5aa729d432dee7020aa976fcd4e16392f9f6cbfc6d83e3d10c693701d0363f02eeeafb2132eb1e2dfc8a887ca09de08761efbe2014bc2b5d0bbfa77c10cbf65b3445284ca29fcdbc49d27cc3f75d481e1fcd4127ee5739f10c38cc2373153035546e7590009703f320bd0806c99759d9da18651e461d40960d9ca451c5cc16648ebc92c1bd0b1e039fe8c8e74259c12422dfa89d5165156600875d36add6f22dff89bd97b9348805573221e9661aaf2307f9e87b8452654009d55d9e6faeb10ee52e44ec0f63ace8a0d6cbb86ab13bb71c7f3da4d270115d05be81057acb56ae00cc24711b9633f7244560bb00221a8311073bd4dd51188607f4c76011cbccf9cdc72daea22a93a6005aafb592b7c9c30ff3df495dcc867808198a7d0632f3b34c07bd67ab066fa04493446bd95f55a61761d07e58aabf7c0424a775137d2e119d1a5ce4db60f5902b72810b3d37820acd2d89d530113dcd0b1237b78799c6541d2361bbaa39077a24f2c40a59276e9655e84870e832ed3408d21354e5595fc1e53d1dcec7ee1988b99dca82316857299358f7fa3a8bff340cfa6c1575e9bc508ef25f3e9f62393a324cb40d2bfb4e575de0589169d2891f02d334dbe05927eff3c3f79e4c190ef60bb1a42f672aeffb4811bd07a66e17aa09f1d06d6eb5f5c647be3a6392386c8cf5d9adcec58a9e21966def0a9128dfe60889e04ad58fe527d1f10bc8538fbca40057ae024bd15dd7f860ad7ae838d0060169ca0ef57b8fbbf2221b0b659abe886fddab8bae1fc3ea0513850d2406df500b72dba3f2044dfe68d80b4580914d3ee51309d4f7efec131e5ca463bd54b36f04436d1841f0cb1b2d7ac15867cce3b90fc4ee26c82fa6301596436374a8110c03d0510141e8b76a0f69a6bbfffdcedc7466f90b111d71228c6409a2b5f551a7075bd5de54f1db2b8f215409f920761d69970682108e8155a2c48c6ea6cded4b0437459859bf34bbe5bc0eb096946ed6598e54d7e3d5c5baca93971a45a9d7fa001365780b8521dbc72883e32037ff5f100f9f326919bfc1ff4e7e804a579bff0e38622fa679584692ba6993d6175362ff81c7930833cb748ed6c97b0aa5b4480ea0f54f0b26097ec9e68b40a12ab7b76b32def17c8a4ff3d1c4552036b2f7a006782a9cec308e9bfa215d8cb7f4f15e2c6af78b9b99c5cac0195784fce6ad820b2afdd8ea052fa6bada77afbc2ce6ab69cb46c62f45f06a4aa0561457658fb00e6d6305cbfa1ee4d3a51716109b3b19da48ca741dfd6e6e765924ad4ff49a63018e3b8bd7a1b8a63883d713e8b2f109ea2ee6656121c1eb962ebd18c9140f040268e22acaecdcfb03acc7f4256bf412605441a6869a5ba4c2635a6ed24983cf028a68fd2903e8ab6af9d99330d800bd9179c5f7dd3ecaa642decd29924e25490766c3f983956dad44c054e4063507fc4172b188bea8c5649ca0405507c169470f9140b521a44039c53d1a4be2b4b889207c31b8108ad376d04d64e8434ecf670c89808afd75a05df25642246c8f9138196947496275d91f00c52abe6aa8d72d0b4e506d60c5b4478491fc410352d9c64538effee08942546cc9cc54c77db76e02f1404f3b2baca3aae94e452975db1786f2622291856b938984f2c68add88a905ebf070a1f0d0a6dad302f0bb9a7d92ae27d1c878be1b400b105ba0dcc62b8d009a809ca4d6ea4069de5dda4a06ee1ac73edc7c4c68325fea66a12ecb59970a074b87315d2870caa2a00f7702a740bb695033ffcadf0eb0b9bed05bd4c26b3708118eec5b3b1833aa3592dd33dfeed3ecf4cfdaa2bbce839d182f95576c51e602849d010312bf86f03264688b6ec2d6e093d67003a17372d08e559e8b8cb97902e14da30dceb5c0c524795fcf24001c693212ad2901353d806800e0e72d1b1509bcad27b520890b21889998544dd916a5d4553c7bf1f21c1f67ec4db8b7786d04129cb68042dda8ecd1b7614ec9d9ad9a9be244479a23b2a4273e8066ceb379006616777d9322af6020e352425f5f7c4f40eeae2e6bc1a7dae12169fd707ab4067577382ffa712f7596677c6d247a887d55ac89b38bf55ed4123355b6a743da0acef455e95c5e9aa9448d8b347d0c1f10e6c8bae75eb5ecc02c0256ef74ae290a5cc74f751c5c6e5189907e6ecd01977294f6fea6a71790051f330aa93b55ba06151b48293bf2470457098e63c1e4fda1499694e0eefe92860b49f76cd21c1201886bbe02c3bd63486aa57eb2b45366c1a1047c994c841e37ee5b38dc69029a03318008fd4f7228c62b08ae9b01ece0140bd77dd52582432be33581de46c7ec01fa9812e0642b4cd00812853157bce16b8a136b4b6ec30b69d94135b3595f3201bb27946f167d1fec95ac66ba8df779445c7e51cc6bef85b79380549c99c93205afc4424c728a2f569ab6351456e8f3092cb2d81dd987b7cda5f0f50a990e9202c8e762fed5ca2e15b0b7e4ee18cfb1446a60fa4cd4afa566f7058ba6675f220d0ca6f7873a0990e0865005a68712ff7139be832c5c1dcbcfe227ada00d01e908fb9b6068b9f4f64dc307417e43e65748df0d1e710a718e5cc6136de31fbc320e8549b5882c7c838871be66bb7de6194c3cf6992e9e2ac031d3734fddb410230148b3298abcd027157b1d4397954c7c8634a851d1ed4ed10ff4ac4275e978da0e47a6e4c77595a9375528d7df160abd044c8a444ae878255d7b394c4b4bf8dc081059542a6e88a502c3dc4ca14457dc1d40f1f9813ac9368cfc8d7bb0b7881a05a97f2ff8f88515eb5759129c75123db55be8fc3b6781a728d55a1eb4160a240ef9e782e529aa50d9da5fbc137f5a6d2fba559f127ffb8b3db3958de79d70b10799d272a244b1749d27228823d29ece25810004461039596b575462dd0bbcc40be001a3af90a0f0a4517a25d88b0bb74c4cd5d9e041a5d8d0d2848fef9965e109c46e0c15c539c7aa90e1c59d3130b7153904a68fa2e3555c99947c52c5b5460d87c573fd730901bcc7003328ebd1dd99f626fc3d763ce0fdb3b8fb64fe680a037d73a2b17d50c4c86d241b3c3be2600300551b41c06308d376ddf234bac2b201907eeaaca44f2eddb456b04f8cf115749ccdbc178494788ac0500f685b5fa80b7d834021c69f0f860a91c41e3af02e71f7c1f3624cf902c183636b5dbc6b4e0e496106260ffedb0cdc42846bea410b507203737369c7f8f5252d797174a1230b83acd3c9aea9cd8eb349bf643ad1f6bce64bab4bc8557a1a2bf975fe340c39075e1b68e63021978e50503fb2262e453b9b4bfc13b49dc55be91d9c282b6f700355f4abdc5add7363840a8dedc5ca07dc3e3984540e7f516a0628458ff35fae0256b2a033dff37280d8f7c79290cc0f4036a865dd15a0d57b4f51ff447fb569097b7788b4f841cc3dd33843a1c3d29a75cadf559089a59fa6b114d13452d27902ab76b8a290a0ac985249571391e9264ee9a93fd2e9e04bbf9f406b776f6fc90c928137a00ee002f861cd16b0fce5ca28e3b1a05c62c88fd61801d6f9010ffa06315789a1c1cf8a5d3f3a2d96599805a3f8e3f5d0d0256ec2287da6e8b2fb9d024d5a8079860b7b9b69239aea335f53d0e85076876879a07622f230fe788e980f8206e08591eb3e5e51ce784da0f0445790a729adff5e80ebe5469c6377d70905 -generate_ring_signature 9d3b5964da0a0dafb5a05244d09c44cc052535bf41794549aec9b85b7a238998 b7d7b25d39766c953ee51d4057e3746b82c88731470657ffce7f90bba173c870 1 a0dd3438cf05d1a81fcb179ce77f0a61b8a47525d2faf17808662ae3d2127d4a 9046bd3b0ee4f3b9f6874301485110cc7a5af1806eb93acef62718598a837d08 0 e23aec06c42f522fa27a26c07f1126ef76df8daa2fba3e2bd281d1216e3b8a0211cd314ae476e2eee93b97bba6c2cead7902c19b12bf4f842e8b2876048ee80b -generate_ring_signature 36c2cbcbb47f9040cb04a7e8e1bd128dd73240ae49d6a91e793c624f9a1b5680 faec603891f4e9da1cdd3e6d205a4173aa202867a1eb5959cbc9e93f353f36ed 2 2d266206059c398811fc3ec5e99c37dc51af758c47f094bcc5354884fcf0e83b b76f2f51b0e270cebb200499466d003705c19d8589e62d6f2abce941455aad68 5b96fa1f454618fba2f17fffe6a26238269fbc0cc08b9361aa03c451d1fed50f 0 c450e33cffda6d0c2c20c5f48487cd47df88f46ff8be685acecac655bfd97b0956d048145f354285818aea10763c2612c40e898e389642163550b8dac1fd6d0c45e4bce67a9e204b1d9eee9847b625e1d85b4759d3cf0c69a79b3fe366cf46073cd787690b291ae2946ffaf4ae40fd2738eab9c963debd4025958916e01d6708 -generate_ring_signature 8ec53a5456f968a3c524f9096eaefd8df6bac7b84fa3a94d0bea97696520c66c e55faa7e53c815159484d92d413bcda25408c57907fb97debcc43b82bb72454c 81 4e6ec9a062da749b4c7ce55c1771c5be74038853f1ec913878dc89b65a98ff82 76f3b513e21d14585ad5489f7e7614a99d0f565ba1d9c74d7d3ac6f4de6cd0be ba18df4320a46e06f5092c242c34b1e5780ed295992b8988d8b58fb4f4af8fb6 038270478eba4e0041656da105b78146c1934c9e8c43e54e37e4103dd9b2d84a 1d88989753b94a019df5b0d526a04355b5caca57eb89dafc2ddfd166cc2d9b16 a18d580f24cbc8814533279e50c1b76c62a3f84c8149a5f28985330342a5510c 8c97df0e0ed3cd10d4546368038ac04a493fdb4f776b71f2883da82cfca39ba0 d51124bf2d485048464f1292f68ac4f2f2d381ee4d776f442df04470341e5dc4 76e1c7489de49f7c029256219e12049f8868c0326dcd9fe7030d4cac30567dfa 0481ffdc653eb171f9f868759e310680c494d060876e40ff8259129ea672e213 c086abc24ec1e5d6c8f1a88fc25aa1b8ded0665a092d57678eeefc7a8fed692e 63068e1ba561aa0b5b7e3338583da9dfae07af9198024cc19636f2ab864ca457 2523398a630cd53e849a151ace8438fddefc758d65a0983556ebe4849cec0e69 05715bfef2fb4e3d04fcd6caf880db87df20232d9e6c22f3e536a051833eb97f 79afae0b5d5f0340259f26c0ae49f2d7b2efcf451f721386bcbd3db0cc516cc2 32dc102cb33a1dffc0551aa87f896bfa4ca364d7e473855053ddcedad08f6192 28772c16ef4767cc67a4802d334e62b3cc366320ddcac5db6ed6da4a3a518e48 ae62ab17972d879f26be0dc8a4e0e72cfabbeb48a1f6c862a566744c73f0d5b3 95757dc182b24d6e76f770585632e8e2293ecb0b2e068b1415c5aa6b8844bbf4 a9a01f9794d39e2135c734007c792c0170211b2f31d22ec855de6c2c9bb099ce 809e8daa0bdbf2845adfbff43ed329bfad793cb1d50964b76b0d2dde834cafba ded1d37ef497ee99ab7f47ba850d3d1592469939b25fa0dcccfeffeb6e487256 5f0c974156a91fda39cf74c75e8418f5dd735a30c3e1be7fccc6e66ebaf51bb3 df40904f087cdb5c2d375fd9e9cc1b8fccbaddd8f8b366bfd6c4c73d548c89ca 86786f17d9de29de6a6f3a96c1f15efd1b795d97f316b2945ea60497416c4d22 571c315699e3f94b0b49c31c9603c952c1cb7b4f501ebc975e7cb9aa76d22486 6b37e86d97644021c4aa8a7570dc5c75697d8a5f48c3d1dc40c5d3bc534ab466 37bbbb806e9bb6515f1b506b554a53590b078f3fcdcce8accb1503d83b4fef11 ad7c90bfb7c44aa341d1e52e1b2ca429e29ba4db54dfa33b061cf6ea1eb00f73 a6562d8af16e8877bc51ff84af564dc25a03f7d0d7535bd2c5bbd130b3e762fc 15baabaae493637cc08130ebdcc165d6250f863613d137e986c3318eb1e1d53d 30bdc4dc44932cda6b13dd13848dbde684a136dc36ddf053c021a789875fa5e7 486d213c4c340c96fab9ceeeb238077f728c3ace8b01862fceda8d0552b285d0 31b5688c7137a1f2caa7356b494db41e82e22dd6a8fbbc1d6f7f2c95bede6e04 df368afc6547d8757f35c2f1e925b2401a976e3dd656790c29df95da30aa7d41 5b457edbef079804a73b9b2c07ddf6795cba1dc7b2b378efd4e090206556c0e5 1de5856c9fbcc5b9320f75715d93718fc5ef2e131c202c109ec452576ad8c0af 57efee62fcef10bf49fa000f0859bbb6bd87601ddb2fb92c8d5b7155ac088d37 a66b2987ed02312dd641783d5e0667e1962271ff175d92a9cabd9f8ee5d7efe6 1d295ca90c2ca4e776946f589090f8b29ed75cfd4912b24335d1d3af6904a165 5836d1b2feb67fed61162fc0547a2989cff2b8223a54e304731ddb1777a53ce1 cf6c69da62ae9770b7cb9258bb6e8b14ecfc4d48157742c2795dbbc757bd9730 15d960a1be78b06fef10a1d748f4b7102e16e7b9b515b85e4ed56366bbf4d347 6f4084468f6082c7a3a09ba66ece708020d294c17718c8a15ecd4c84cea2e269 db08f8b2fd5a814a77a829c3d81da8aba108bb0a30d0ec79095f6272fabb29e9 bc4658cb9532f7dd50cbbe9cc3b69c42c684f1baed27c68f7bc61a3d0ac6fbfc 70118d671fc682d4ba0975f6db97a1e946ae1523a1602e980be873b3bdf7e262 100e6a645828abdf759c25a993f3176bfd861ec8b14fee0bf2257c6942f3cfe8 dd4a958e1f2f175763bc424c37138f7f6ace5505b4730c1474f13e29e9fee09e 981f550230c2ecc818356c5978e09c7bdaba2bfa50a18cb65861c757fd2a92b9 c4591c778f2af35031f0b8c10a9ff2076ebf4743dfcc683b135af85c68692734 ae21b69401e1dd790b5d0ab2a9f302f9596d399233c527fb03a3c04c174e9deb 1a479edea2a50f19902e1a6f7222d5dca50220170d3031d932aabbb2a2c60a50 ae222e075a61554b2931b48ffcced2deeb724b0931e57a7fa42c83dffea81be4 e09cffe50c23b6b501416c062abb82c4350142d512fa7718a1a7b4bb0c9c89b2 140813e40064d016f31c79e98732ff2488d8f5203755206ae3ff8b43ae29c2b8 755adafd8cbfe400bc7f0d6c7e9a6745f805094fa56411511cf71a842dcf5e95 d2f4bd013ff2a5614a0f46d8f8291a727974291ef07d6304cf43a3cdfc8020cb 77b077c9ab0974d0f7feda41526b5f3c49aa93128c71c20a32210320e1f64b76 5658127bad4fe2d7f6dd4dbc60a73717d42208a526b6d0cc42a0cfc72dc5de47 d990e1f4737f9389eb33c7ffa8b5ace778993c3e7843fd8eff9e5716d7dc3823 258ea018c125c4f407fff137b02e0b3f5746d2ea37e5b3f0ca6adcafd4d9fc36 15df78d82effc9eacf8420cc9e11aacac8d5e909cc54acc9c5bd7c4c871451d4 dcae2deb75447b6ea9fe455cb32c80b6d9e756547da0645f13d1089069238913 fe5566a38dbd13b5a0a18c0e6b39c5774a98c588d2fb2fbf062a6a313dca8e15 852091726bb4ee8baa253c406c0faf71cfc6214aaaede4a378465c6b8bac7d21 927934f87ed1e0f415b63e60145baa00c5974030a1c5eb8f24b7188f10fea32c c7db3aecfd44d679199341764a02c3be4a87466677263b8b7d05acdb694c124e 3afb101244955d7befd723e91c81a286ac7f651be08f9895d2273e2d0c49a300 2500f40884d6d748b93f852b6ee47039fec6db5d3377d28569d0fe76455ea246 e1b1e24c5a4eb749ae2e2cab6fcf3b86634b426c3743aa9ccb25f50d99ead1b0 c344cab976b25c1785abeca1946186a1b4d5c1ff5bbab59d8dfb6ad5db01a8af fe2baccb1e96d61f5be322ff3e39d5b72d5cc799a758de1313d4793b5a723fc6 83779c08a1567d1ead921064d6637784b8b85a2d18bb3facbb48e530cd5830fe 5560502b0c56672bcea621eb22343c543696ff0f25b230409984d89b083e399e f78636ef78c66c68d74165c4635fae714db6ddd9ad3b7ad51c7c45f125189b37 f344bdedb278b1a78067d1effad38e71921c10c7ee69b0f597e4a481b4a9b014 164d7bf3bfec8c94798fe21c02efb54b58665b26d7796d0ffa3679b5cfa02b31 a7e4fc8d4ec066b75bbe98d9cbcc648ea13bf6b32646dcb58e5590edec327232 03220e9496c7b992044f1d2f3dd65b6d0e8dbd4bf2f5fa7036508d9fcb28a147 6610c3995d2a266a69d4205027ac22196f9817d66da97a547a2d9a41ae582d22 e95249c7c9018c9c5e46e59b620e8e412eab445e832cc46cc99b4c9178100504 68 fec123e2b83baf5474f1831b5e4311e44761e43281187559849acdfed2610f0459e6fb7d0db45de319b99410a2de5f5b35d596c7c439e41bcef77009c3b45b09f290e41502d4d8c692b2c11cae2e7342b4fdb50b8a64853945653ed9a70e4b0b6c611bbf09a372cf942e0c722118d229b4319b149c8a403d37ec15960bee220d56db43611aacae8559ccfc9eb26d33988eb61ab5710b1854f17406e544c6740e28df31248fc0674bf86819a72a29d80a862f178aef42b08c0c59900da1987708d0ce49104d9329eb7f7c5e3a50a1260ec9e807ce7af4bf4891764f90ed1bc50a788088a3bc5f6324b080049ceffae19cd774ed27d7c0cf20694db2392b738e0573b14616ea3bec93c082fd139d98f88d49c3a0c0624bf852b4692fada7093e046e923214055c532cce199e77b2e5ba6f4cff4d091de72fbf2043ebc04abcff09718d00661b5f2dc97703e083273fabeca60dd661b9c7bcaae6bc9bddca1ade0f02c02dd6e7a5090c136e6abc45c04c5e8c196c4c0e15788c96ed8f2c57ce6d028c51c163a4c1511efe31a9a93a95121fbe1729eccc7a1f6607bc7ee4217e080f41f118b2d450f39a8a6004d534bb13c788cc27c520a2aff2dadcf64ed876be02f6a2449225021abc07912d1cb5f5835389db3eded58867236d7a97a4f81f9d07baa02a79ed20935da35436ea2b0b5ec51e7285fefe309615c18224942592e50d934835195062810d520190d688508ab7831bb4b123781f04e1e98cc6ef4f7c0d68930d8cd6fae1af813622200cfd322db53b0b9b700350f7bbb32f1540433e0050ffe8e04ab4b7829d5d873006787d9c326d70fdc883d5cd1cfc21216ceac40f8e45e8479b6fe05ff8880a1af0529631b55abbb15919d99b3dfc850b1718c7049db40b9b33ccf2e73c24f88786b872b94f65f850ad0fd021314a510e0dac270a5166a489703f9c08926ddfc6b6fc71ef0b7286f1e54e15a1c9e592a3ccdca20f1ce24d45af9ac56c9c68954bf75bb6f8eb1f996b3137f62fe6aef5608b973301e7932a97ff004d379ce3167f5a95757619238588ee54d9d22b588687adaa050eb0b9c516bfb3cf0a746e601befa334b9877a60aefcf0ffa88e1fb64497ba2b03e6d802d4b3c1b46c8355507f204f226355ddeec43eed592f3c252450bd5f9e0aee21d46a5b03fdfd27760369f47cbfae94bae5fcc12c0bc916821ac7e832800f4b0d307252ca916594bf35aa27a122f66fdc3362ad5443424dc49404a2fdac03804fd818d8c8ce144bd22e381be9e15cc74c18fcfadf34c8e5dcdefa0228e80e6ff7a5225b4954517cfd41b5b9cdf2243853e259b7c2e052fc5b91f4edfbee0fdd77df83fa68115ca64f111c4d4f49b9b003a650d15b1fff9f218d314f919e0739e103ace1aed88fb387f6b9a058493f3fdb2dcc503f1db50de25433ab6cb9051934f27b99ace264902c99d832fdc5167fd0d2e7921b9e4439516849307bc505b9cef592931f85822d66662f3d70ad2d08f8844291223e45590bd179fef8d30e5f71ca2b9891da9e57e0882c98747a5b6b7256716f94d7f23f13fb9f7d412c0d26f318bfc12394ab77298f481aa72c6a6f551b1417e63d1252e00eb305a06e0fbd78b1791b2b1dd4fd9d9ded99f5cd05fa431b21fa709aa31be21cfa3ba7130e055b957a7eb2eaf5c3f4683b4f253af134926e33359ff035a424d1c3ce531001fe35a1b42ffe357759aeddbcd50575fde0d46ab2c166b8fe883787eb19f3580b3a2f6c6ff2e1e205a98a44dcbb3ca0080957fc55a809c0ad7d22091c3681c70f50dd69b5c85e6b1018c5ae0e339088ff2e2a6964eaa03d6b2cbd6d5b9314360c1c90d1efc99244948cbd79bdb7f4b25443ce85eeb3ebba822285ae844ff1700cd04582c64fd1b4146e3bc50ade085f398ce6368d830e52bf77158c467fa8ee06889710ef1e772f9265736166afd2efedf820c37d85d5063671d5c338056a6d002fb9969579abd204e0a06875839d1b81200f7137876f3e9e85dda5ae57b34d00c3cd38e3c61f499b08bea939402d7a8f29ca521b3d23f171901b5547d2ec2208cd9751c4caaab50b31f40a5e8182f7f31c12928aa3efa9a033171038e8981407f82edb951bbb8836aac7b255a1b3836508df8ead8a3f5bed2c5af6a855c8f00c0c91af886d9f184d4f5745e648221326c70f4005a55ee6911ed41afe1ccf920f5f0d9ddfeb9967061af423f701a58d6953e0a915929dd0c4f699728e5428bb0c104ea0fe5e355a0d5baddaf6b6ca615948f1e7a65dd0c4527b4c06b38a768606a4a7fa856c1b68443f0dc35936f28c11c7eeccc33d626b507a0b0f90126c530973f729acdfdfce2345932af6132171a47079f52b46158c8692e20a859171d70a7591148c70a20ac62ac2a09337b37aafdb2c7a6ab13d25c96850f267cb725f07c270a63649e41160fb53da30209c2df64b2285303bb155e1e4e0124ca72ccd08f81800b87d0ca0b265b41346ba24a20d9d3766b56561ef436e98b272ed6a9a0b7e8390470ee4a9bb87b82180a5b263349ad154f1e188ad8db22d28a252a13a0f6d9e5b75069bea03f566326d14a6e295fa1e3a3c1d80bcf32139f02ede749e08a955fa17af25a23f4b379bf43758da575a9ab85426a2f0437371313b35f89d0e40d625298a9395a8822989e860fa8d667fed4a386e0c3477cd709fa60bef30016816b89f31cbd1bd30817720ec37fc93b045d512b44593458502564346a9290ccfc1fa2b1a552b4bb20a1c3346f5b0bc332a717e4a0a401599aff1858121ca028f10b11657f47f5f05b8ea2d3a69bf36ad2abc30f72c672a3986c10f902c7c0a60d9f2b294b0b9b7fc9e1a28586f022670f0a78e1a68d24337c58a5eeacf5605e8932dbbd836bbcc335f6fd70a825176c7dfbdb3fb53deb83a86ff1dafd51b08ebc4866e385cc0241f19b59237016aab919e94536c76b5392e174c8d77f8110b45b7e83a1837ef3f002c2f31b1d85f0d0aa216240badea2df04f2dfe9731e10bb2941f22738d00062c94fa236aa854de8b87b0abf63321a2e61ad75383e937029bf97f5bafac7ab43cdebab3defde290232a10b5d0556917f4cc1db4788b8404faaf9724f093beab93df0184cb1d020a5f34c453b6e6969dd1ae3063cfcf1509ea366fdc204006354d6005438f99a963b59686662e64bb4db6e46fc51e5d120478eae4bff59c89c5e6670e91a7a98de3b0beef544219394049b8737c5c2c8406060c6cbcc45b5ca80254d2ea2dd1e60d255edc7d6bbbabe45f49108032b94f0ee8747cebb230eb41f17145fa006688974c8ac5bf42ead07af907fd6566e3dd0cadc6c33ba9f1f92e113b92ac52fe3012698b68d2e40fe84239bbb7b45de08d036dd4289fb8af23c3283b09c4ee05f0fd8e8ef07c94d3a23688c7840746c2c00c2f46e20e3520b11d58ed92ab8be2d9653917dedcf841457f93a5526d6462d406b0f27788150bb2cb23c4b96d9820131e317448763982c5dece2d55df3c591c03d74b5a8909f2d599e9c456ec5816fc958b9a30409aac5895eda623a4540a0c0e83c485b1e552f6058bc72fd1b783646532d9ce329a4b227f55f815b812e207073c34f7e64ec0c84b9503a3e6445a0a3c5371359342545307be77f0519a5ed70f643f2d71c47753c46cca87bcf1540f37bc3731725d45563aa49cee4e31b2850373ce47dbeefcb1c3ab98c32f91922671b93969d4d973f123e021effa70da200d54a635e11d1c17386ff630dc3d5087ce38b76ec70c0eade272f15636be59d300b1e648d6081c1b0554f9574a1708b2d5995b3db52e38c3d250525d62b412b60759f070163a6a34fd3ccfd37c70d559030122543de7f28f85ddc87b457f9bcb0248777f8b3c785e312c1f09ec71018ff04f29f067396e390194290e9e71803301b3d533337189a57014ca08864f5856bba469db2722df785e37949ae03b30f901f0bc0fb0e5d6377b6b187c6492bc1109619c72b40f9afc7e788946bc241fb304f375259a32f33dfeb3137535fc00412abf1d4eae2db27d4b4b1224f637eb8f03c67d0e33fe6ab3d3140d491ed9e497a8c6b8ca6c5a908e0976d5cdf4fc49a50b84cdc51091d9c60ab19dd1548937b7f431dbf22c56e1ee37b1685fe19ec1b5044382f3c3c0444c6a76bf5d16a1ef3eae58d3a3a6a6b4a69bf0be07e2c8647d0d118f1f7fafa7a641e1f7ec9f44a6c0a92a9215f1e7b56339581dcfa73a5c1b08c2178d45aafd18c77bdb3560d3710143174f50bf70c957b16acb732f299afa02e3054df3a880c9a8e6fbf7f89cdcd30b250358ae535714bc928883e4626fcb0f1399263057fa66db28becf0ef76fa2b1444b8c0001d18ff7e487a752b4426e03bc04034f2b65004927356b3d07a3b118c85f7c7cdcf81bcbf3dfeee9d5d09003e57cd4a4e1bf9e66252b199e5e542b8d7fcc859bddef3932693d99f9a0d70a08f6d0f1d4cd983eabef305db6c9a9fc3a6887463909d6dfb1be662766c1c8ba04525d5e85a64ab5c6bcbab786f14b4cb7977f4636cfe3b2fd97f882fcf565e1068e6482b8144f888996f15c2a9dba95f6adf3c4bc954ddab35936a35a143eba008ed398e4682da2a06d729849675ee44fe8959925f56a73d8a1f6d079d403cb090b79783b1a6d012b295dfc3b17f360d972e97f7bda6fc1635e71c215c840a40767f98ba7df41636256c1e4a3e6fcd794a92bbf0c201e5c3f1c7abde3df7c1209b6f32c5bfacf62a4239e43214c0a1ffa622752c47b98ac5f373c690cd3d38903647c56cdb6d6f63044b49a4e8e4370ef40849e7c689d03c557866544c743ed003de7eab9159b1e474f4cd0218aa17094dd6676dae597342514591a8289c3060c7b77e445aacfbfe75d059b8a9224b4655096a6419c80cac0169ec73880c4280df4c81a1d4924b7275063a668348e4857dba6fe8cfc6b85354c30fffa30bff00a7bc33581a5a11748d1509caeb28667401132e1e71559a00ea0e4cd9070728e0669aedf5e331e97e816f8562ac3b8fce6da2a93e137733149951347c8ee04e70dcdf3ea24af0a1132de66f4c77df4615c8b7d99abd04e9fecde3ecd88f7c86505c140201fa7b26f425543d72ccf887d132b81cd5bcab15cc26785620433f27e05f4ec8fe763b36835483237998c449c8ff48e3f495260017c45b0cb95c3623d02283974a96dfea296a2351b582168affcd1ec9f55b23644b6f285d8e101847d0ada36d4583c687360eaed51bc8ff5078e721b6b98c10d91229b91f27b1d15d8031de34ded1ec4091f6174c479e7a9a30c3d5d1a94882207fb2832973f4066fc07a93ad83dd4dfa5a9d1d5af5d04ab881b962db58354681dff54f32d19624fe50203bfdd67362cd386ff78ad3eac81e103b05932f568e932392d2c9b3911a09207521a773a5fee6f970eb513b27f3db0abdb5c3ec78ea9f5a3f076b05711ad6f0581736395e78c7e4b503c315c7223733c2e2bcf4d4751e2e22d2025fe7001d001e30150fe387ec821afbc242f4e21c155de60c435f44db1858e854d48d8f2690e1f866cbc4ef541c7ec0719520b046dd64d80c7e541e3883a571442c92d262904b125484138a168f7dc9072a979da19222aeb76c17ab23cd78d5aec4c4a40c40bf270f3c6acd0baca42bf81f34f92f94c3ffed2ab0e0b2f1a2c750009654fff09c1e021a57b940bebbc94298a954aa519effc08bfc595cf2fd415ee8f04d08d06a46e19c07bb87d0177e6b29cebac4e48faa00e351a8d4d1b133c25ce86975d039fe429a81ebf60623bd5cea692d7869a6ce0791c358cf1f864a70262273a5f0942f3a54917d1be05c6455b8fd377881d6eb079e49cd8c59a7b8450aca38105087b55de0b254444dd35ae168c103cf3e6d5b378884d18e353ba11ff96dfbcc3022d708534c794cb22f3ac8a4a5fef83f4815cdabf13e87549d6b43374cb8819007ccfd08f9aeb082069882351ce9459a2a315dddd21f986504bfe294f5105ba0e30a0fbed973df2197c52cac34747796a8566aca45b79917b339de3426757bd005ddc16c42abb5c66a834abf72308ca47a2931c11460e0ba303968257b4487402bf74a21e553592a456f31f0fdb1a657856a510621facf406203f2d58d593d00a721306f57d055f50e1ff9f4a69bbefa1d2301b275ab87dce4339c8209eb25200bea86a2dcfefe8bd45a24e73ab7b2cc3374ad4b07022b621015499c74f6e0604164333ef5e442220ba682e548822577e49f0b37af631ff2782a64817f767f50513db8b66e6a65a3a5dca2c81fce369ac66af66ca4acfac849cc4260364dff7084ed822d6a8589dda3059a7e6fefeee6f7d0e34f69010da732d0466482e7e2e0e6cb9f80cca8811b4b7bae8b01fcdbcf589e542e40fba2d8ccaea4d529b662602ad08e4f717dbc0d2e141bbf427f6ca74508e8658ad017bd70dec6d674e3d5d04561fac982fdb4d161fc0cb7a6fcbd93f78f93e678177ddf61e5061d5efd8b9023656c4f6406f49681b5afa017dd4016c7a20fd43f14e23c55553f3ada01bb70e4a93c01cd815dc268e811197b9730c255c2fbab5bae324b67058bb65abb65f07d0d2a123f93f00b3c6ed8b02260479a10b910e26cb453c9ab8b1004176a2a30e0812e6434f94d3b46e9c3c3e837de4f8fc07e079faaf7eb559d025d7667ccd0a177e2110275fa0bf25bfc104bfd3985640f82884efbe9c304bd5caa7b2563805bc81bd2e0595c908007c25b704aee611ab13dc5265298dbd9aafff6897c583040faad721556fcaafe3b0bc42b7c2bf9de1aa6ea61810bbec720dedece556c603ff9436cce70221dc3b53ab5247d0eb0f297bd0cf990bacdacc73d98a62fe980ccc517df1dfe029af7e99498a427ce374df7596dbc011babb318c1adce558f90aa61e1ccfb5172acbbd2985a827cfb66fd59e4560e826625d618a8a42012e6c04b559fc5d7b31f194fdac2f0ebfdc36a13bee1f8e1b8ddd78af2cc2527f31d4060834d93df7ddda14a7c25da575bc0c1ffcb5e96cbbe62a8f99f6cf4a91e24b0c981af6fe0d36a878c769204b395d5076941c805cd1b73f8e6987a97008e140002ec85c218489f67a9ad6cb641d0bf1517219c2e4cdde6703053ef87c22f111099caa568285a39908c14c963380480bbe3e4533589d0e93cef4d65e5083b78101e4c6ca31740f1316a08e9e07d6946f5dfd62c2ef1926ca600bcbbb9772f8240e50708aa36d0804d90862a0b7e79da416d54805e0bae72b77feb3234b27c853068156a0acf88317a1b9550584689e466e90094b4e47325a85e15e3ff082468c04 -generate_ring_signature 713c4c8803fc00e35bf91007f00baba57486e0d70d332c9608d09b6682d5a554 794244933962c44feef2118a18c784f069ef776f495f075dcc22870eef06fe25 4 d6ed7494656d90bc14042ae12e0dbbd7b9f890d2347417b21830785a32fb3b25 dacf20780d08fb684de5ac429259951b81deaf781a6e31dbcda6ad07f10ff0d2 d0e455ae02f0be142f67090ae09f49ef67d65c5ca67a2edd0e612bff1b04245f 61b716ef95d86ea4d50645d4ecd717cce494c2d4d42aa30fd13a45e3370a7f34 da2cc4f0ebc2d7f6020c9dfe8ede0c1b216075c6c2eda1a9e10969a69a35d10a 1 fdd9804ac3f38adac249797af5e7d9b17ba749d4484ea17f942d3d567e3f19031e3afff6187c4fbee71f1d638639a0d383aa4b70ce75b6eb1e94afbf9ecb2a05202966ddd32231ff7c89ec7e5ef7d50eea7f3c6a88503993b5526e276941c200173384381336af50a19c2698bd86f717ed7e593789f240a9906720d1b557b0018dea9f384c053291dc8f5f7137ef9da1294c8b164728252b4d8ca63054f60b00f3efa5e410d244ef2194bf98f97774e93203014bef4e11838a6fe285746a9e0c9ff14855c8a5ace856ceb4f7dc54383f8684b6b61fc7bcb27066775668adff0cbd8b1f8eab8f856975ae28e50e1c341256526f0d16928d6a1b40cf346e39e40a -generate_ring_signature c24d45d5956e2ff05c5900e0e3cc833784a44af630d7aad0beaa57fd879825eb 359095816f878d15ab2b0a2186ffbf5d95c777c21ca2cb72d2266d083a4a5809 6 bb3e5f6ae5df3c4a43310d86dad1560abc1f6626a09d5a76eddfb78d652654d5 7a4ac8d3528abde1baf73df38ad03a857739fa446862d1e28c4ac3c83edfd15a ecc674b53bb166ac2be28a7d51e66edfc9f95a324277a2d9d1b908e0f65c5e9c 5841a26c6b246992664b8d8b58769db51a4f7a194fadf2c7a15046a0e2770023 75ee9e843ab7975fbbdd99bb16392452a10f7e7b38144f29b9d706bfa22cfd65 3de3c211b808c4235fba01b016862f8305f0deaab1fb58823f1be27f151acbc8 9b8031370c48fb1cbb36f400453a00108841cfc20f4ef9d9d206f33159cef60c 4 5b1e4888d319e4f8899f6171af18ec7b847b45aeca3aa6498291ae3afc92840f989269e0df79757717c4bdda0815ff6b2e2daefd87dba9b1f408922e8a3186037b5ff96deaf5cff70629bf9d1328090b03b8bdebfbfa046e19b88637cdb6db06f20310fab830ac92fd2e13136b063df1822f0bf5cb4c8c33fab24caeb612110fe3c1918e04e8cf05619d2d0e0f0cfde285892b2224eb9b035ca92997504908036ad5f8e6199f22de4f06152201d036dcb50b902ee958ef367c8da7ee22a4d801d76de5c352036c4842016aeff55ab6e5f971bd5e2b38767853018a91248e25024409cc6e2d6eadc8afe4d657e1cd6b44706b85a55a74f4d4854818cf1726db0f279a4f870241547a43c8d7568171b2523e9d46bd70621675d0efd8f151b02d0f5b0e7aa38a7a973237a93460cc459eb8ac62bf4c6c99a66035159b9819d2050316588e1f5a1c99fe093a868927c1153e342737dadc3d2f8216a0e752369a200058e770830c2cc45aef6f9b768619c081c1e2421b1f7cd1aa8d0c87696776ee03 -generate_ring_signature 9b9e3fcd3f946f6741e6186234eccbf58f97379d2138b6ff985ede15f22f113d c3b4076a7f669ba7d594c52e660da73a565bde7416c9bb99e06e9259d325724c 31 c468ab3f7c3268f7a1839d256c30bed690689a39994e4afe3512c765f2b21ae9 9e8f235c70243e95b84ed1e454c1c86b10446b699f059f66c46e5a1b37dd396e 5742fde4cbfc7422341b8a5449fdd125d30e41357237e469bf0fe77246577442 5402efe6137d07e5b71f0a1192ff212ea8959e080d32b012f937fefd0edbf337 2331347eb9271235c789b6d2be8c6dc18713e878968d77f72b9d644b16f26dc6 0bd583c1d0d8b4b2f8b19609c8d928eb4f4196a491ca45fb1f7d27e9b1a92c5f e1841b87e3d529490cd95098d08b589f51f0c84321b656ba6e0820c41bff4574 aa142732d8b972888a704821a22d62e92eb09b620f0de227981a7d9612333591 d80b7da6a8b938599b5dbba5941781b1217e4ae5a90efd8fae4a65c49c30ca3d b6d8d385a96ab10a4cb6a82df537be9ed042c8f6c42ae261baa487f7e927a384 131a2aeaa5db3afb87093ce9ece283ff1c3c8f7ae83f016612a4d3c866035a54 23cd7cbfd909e99e696f371875e9d7d6fe1f99a0a091dec26dc79fbcec6c29fc e496229d63f8c8b61f17f330dfc30417adc1306275b55adebd8b2a2863f72c8d da2db6040913049257b6ced576f35e4bfa431f25ae137c6cdfb562118bde066f b658b88b06cf34f1897ed99a26a20b0eef67ab280913d42a688b3d862cba3d09 da1f11e2eccd81c0de8498f22659197dbb66bb0512188ec900d44e8036bed70b 9fbf6c6e3f494eede2ca88646a1f3c3e3c40cbc0216227e0c474f5ed7355275d 5246031ef62f9bb1399d8c444a9e3ef99b0da76ce5cd7ecc882dbe9847d1d62e 8e4c0645b710436a74749e8d5531ed8719df52a68293713e61fbc45d5fe8a68f 18a5567a6e61cf2d3bdd2fb0a58a39783afabe754ef59dbd81e370e35954f1f3 a4c53ef53fd1c7dc628b6cac4bf35a5b84ce3703c07040924d2db4ca523cdf73 7ade9695fadb65c56bdbeda04307b304a04b9d5876a1ebdc9dfe35f7484a3ea8 82c7f5919c40ac93fe9c30b9b50b6353dac39748634b5afb52eda0aca5c00c68 12ba3162a1d7ea4b0c110e4b2f7b6f83a3874ebc983698258e9bdb187efd2ec6 62f6e20381447fe7c19968f5eeaa00c04df07ad01f96cdbfed367b6d34fe7b33 f27a9baac0ee08596f753298a41c704066556ceec1afe5a3c300e339a709225e 6b15357d01194d299f5dac3ac8dd4ad6cd02588038c3d9a8fc4cf322cca7be66 bcda402e1596fcea31ed893a65251f5fd60cc2b3571c5fcb1c8418bb6ff390cb f04d671aa35a499ff64ac633e18ced492f00946d4e3bda70d6274350ad1c313e 6b11299411b8c35cf07ee188537faaec524cb4ab00b28055554673450183d288 cc0b067ef56c580d768f01f0efc8bf7293e408d0369d5c3f0666a2ee4c70c553 28c2450bf0e73521266c017a04fff88bd10933d316572e91a497ffe3e9a48f0f 15 f2618d7d98451f8071e5a1914ecc72e056949a7495b3b0546643e818adfc9101caad9d2b0ff5353535ad7a7a832cb640967e6abd09fceefc732c50a462f67806d6a9c413f248ccba6962d1f604f5e12ff23b80b72a35c311644ffe9f897dda0bc12e605eb5f17bcec15d7b0178a3a803dc4cbbfa73216be3b54a96303792380d5c0851ee72f01c4bfe192a320a1ce949f0b6f5e22778860a5cb93de56a0f160cd3d426b481777e71097728d30356667dbe1ba4549f1c972abedf0c2ee66b2a08b46ef94248aa0422823456be97140709ea0c52d46751838341f1a6f64e1f7907c87e566c9239caa0cbf6d962831f52d9ad966e959b82c7932a4067f0f3e8e70b8174397f2225b9424bbc89d38af4ee8f3a8de503dc7812bf1d9bc51d61232d02753ec4ebed9c39dabfb3430726146ca9301e12816fe5eda2b7e50ddc94fd580047ee26e1437375ba7b91c0caa91888cf7fb41155153f8a286937e921d5c13b08bf8cfa8ab56b0f3e06a5f7866f0f8f40ae95dfb6c84cf548cbef79d1d65f1603f5e37297b5f2fa6ebc2443315960d6206b20bc324b8f52fe0f53180059034a093147aacb85484062130a220b4443844cea8e65d2c1d257d37358101a2c026a0a5525540237c46e04efece6255443fb4888ae4de7197da4cfe8f89724f9fd64093980f2949b61c6c4a91bb3383ead5bd5aa487e07f47138ffa97c9022170b190cf61fdc74ae0c411717f93bdd42f39e52872879e72f9e7b3cd07178e106c1290b74d64521a9565f69b68128ef97e0df2e32915d1c2402db66aec7b777af9cbc0e48e8935ced3bb62b90bc4dda54c286bbed2cb239e9c01537124de2912516f40f20a93d44a5f8ab8968dc082ee0a579c312e48f94c82a853bbbe08087525f2a003eff91e83a1c55f523793f48f779d66886ea6c3f362f9286901eb9937df3f90d6bab7fb89348b098dac49153de6f15bb1592b20d52cf701b242dadcef30b800e950895b441dcaac722ee2ea4f7b91f1062064fa694021c19545a80a746ba94087b344d02cdf1fb4e647784e21e03a28132953959b06ef8c035322d8179c65a0225434ffe9bc1688cc45b153da0070095ef9180755db8612c9c7355ec9e3f4c01103f91f89644a31caf6501e0d4c20bd767677a5fef60422556e1668750267200f360e5f5882523ece45fcbca6d72a6f0893b172cf3468fddc836cc2ff838500d97f6d8ed9ecf19b5b24d24a154d712828bb63c6c47b2214e069edf5c2f64790a2383ebd87eab6e35bcca747612a4cab943fc5ae18a9c040616d47b97dc2064030c10d9577c2bc62102cbef4fdda6d2983bca3585213cd333c243b64054c5db011c9db9e5b180cf7cad8f6f5ede2a7435dc556f2b3ef6ff411ec098f132194a0291185928fb8d76279e4b6ba51ab75c2ea4e171c1834581bbafa38c17fa18ed077ff4fcfde5b987bef0129e3aee0ccd94de8bf66d73f037cab7dec8345f20a604d8e9a0a7e58d095a9062e600c78d7e821e9347da3ad2358df2e584ed026006067556f2ca898f28e5e39352938e2b909333fb610386ad6cbae9153a0b33b6590ebec39f31ea57c8a69bf09ae4c39c8f243994a4c7c396f967ea4d37d43734fb04ace9ebda5806fa58712ef7dc849312a3ce2bbd7a67dde37d3bd76386c08f37095fdc95880e4317ae02b37b9d863ee31579a71c52db6601f05ed6bd8252aa1a063ada5b920887b4167c514a6cf1292d65d3481ec5733b5dfcad6c9da14f75510b48b077436736add37cd9091f07b3d537fe7378b7ee245c49023b3a690904f20c061606fb2da49b03d7f613fe9071539e25d2a4fae930c7215e6745cf05b03c0ff988b3a8a448f66618b363ea6b584784dccaec6852870686235581eb722c6109d9fd9344007e295e26c016a574759b8bd279b826aef57eea2f3787b7d93410091fb71dfd3a14441f36978815e86ebce7f93e99a6541b2f6f95d49b2bb5720901463fdb377816d69e633ba8ae96f72c7c28e378f954c7bff8ea05245a121f540918f19b8422abb1faf32a7eeae2180b8e9bbbd0e9fa4ccad97b49a528b45cbe08cc408f3d481739c1f704f3bd4d3bf1d4c976fe674fbad75e7105cd5bfcdbb70004b1357c524485696f5aa8d7e283a9660eb9670e499bb922b072f14bed53d90218e03edeec21ea7dd228c8c78dd5a8f9c7b7f5910826e373dc575d065a6e5d0f7483ee99dceab5503aaac8d038a1f0ca3ebba40b521008044921b5632cff910b2652656e6cbc3fcf0702b3f4a44e185acf305b4f8438ee0181822ba5b3c7e20635c0d2f6871510e69afb53efc5ac1783051b922aafaf8d92676f4dfcbf4b430fd6eed8cd92870d1ff5e258a5856ddde5f373e9f6198258b2387e154ea032f40c3f81228f10dc42d54e25a5203874e45fff48063e9da116be56220a6b4d7434014d204f5c8037ba554be07be63f0ead01c39c27852881aaed539092a50fb50d05fe83a01f69fd4680fc8d16c6f8ef218402bd6f8022da2d55ed1ee24b40c4db0911665e324f9c26708d88d6f9aa7562b1ce54cd144c488f04456446167b852a0b2163834bedec9f32eb449dca8b6489723a1918b40d87b22e9ae4dce10eecc40c1208d2f60494945df52ac15ec9760b7f180dfef9a49dbeefdc92693e8e374f0c1336957ac536600212f79a0b270a298f869587883e37551381fd7b0f436a150d67a3c8084806d836997b64ded9dc7d73972c4be7c7955898dca86aa175a6e50eb4c3fc3c5a1b41baea1fb248229298bf276b8ffb9defbe590db76f597769150d -generate_ring_signature f31b094e55ac7802f86f73c3525a6cd8e769ee880a8c63aa2f5992c255149a75 b17df6621687c3f37b1a5ce5ac09d95546679313dff994083f079b5cb462c930 2 9085d0eae6ab0e2b4c9289ddd4b4405dd818dc5218188920df043336d491f918 5048e0efec6bc401a73f920934508f4d9b99ef5ecbb439f178b7e65c7d08f410 39467bd15083c5504b8e2a161aa69a6e54d336270f30814bae129f1a53972306 1 9c5ce73c45d1224c428e64dde9da2f8640759856b4161a5786a9fab3c65fe6046a461b19e7db170b714ec09a91f1656cfc731c2933e4b8f2c369245c97e8090c4497b8f327e851e12ae649def66dea0f361d17748ef14a89ba332540671f4b0f7d4b7bedcb372b8c992f5e4de6786e5f365568272f0332f918ff9463030bf804 -generate_ring_signature 2b1ed38fd7d880a817ba1a9e09166f4ebf6db200b718496e4b0e1611b699a71e 71dddaf7adc360a91390f506b386c69a1cfdd89e948d803863f742d9fb9c5453 11 7670293c88d3439719814fa939089f77b31911384f46863e903d78704c004a4d b0bf008c1c329ea891d807a1d7ef55316b480c19dd90a00233ed6027d566aad1 726dd2e15afbd42415d0bfa6886d84e1f3eb9442f71b4088b04c79747aeab11a df4a86c0db52362d3beb62ee1083fbb3c400553a99b793ffd20d4afd8cea6a14 ff9ca05329874655d5be3d882099ef375017081b3b16a0b7da8cac1ed3820988 88baad2c438cb5208973869e561254d3a38f4848039b445b9a4e8edd2522c325 7285ad0124a73272138c91e701daa44aa52cc647a73351f1f842bd7454efe5ca 0cd91d92f61536a7fe4f7c9a6dc04e2dd74dacc2f2f6a93af216dce3f69ebcec 0062e10dc722a08dbaa346d3c2070973ccfbd511dad2db35f77e8c31eb95b913 a9290ab629ce61f962650140314ba4e6e23ad47e1196460bc33f6ee92b18eeea 6cd25a8510ff4112e5f2c4d15801b7a006db3092e24e2db9cc216c7834fafcdc a5d6b5f4dcfc705c6f42f3ce3854458503a5b82742a2857903bf9a57f297e703 4 23a65c466076e1e95a0381ea2ba6d8611af7034f52042666dd29764055f0e501d649e84623173d42a4b5a7ee3c3794ae6dda54d47a6afe2425565a4ce255c00fb6f44251bce0d5d083ff6cfea3e6122dbc504335c9ee484f238b910bbc1c0e0dd2117c142caf33fc0bbc08bd3f1bf3c1e3a1d87005b6fe8705e25d0fd484bb09e8cfd796ea5caf5705dbe3572b15505d46b33d2cac8be84131d36abef689ff0865acedebe7024e59cc058902bf8aac64fe2624c37c82101469ea9f32f85f2a0d327fc91664ba9c327fac963c6d438ad381f07c3ce2afdf1e8ccefded9b06e3079f990dc7d648559740cd2e295eed8e7a093a0a4d91c8f0680e6ab1764f752a05123ffecd5777fab647739b996c2c0db6f2f45cfd4331900854b4e096ffa5e801acf3f46b98fb73ec9d49eb0af054f427a080935013d163a5b1749ba659df680a15c88fa87036f1b74724a8d5970aa99ee1758965760413a41157d14bc3b25702ced86941277241da0d5a43317b40452a384c8416ccd3081ba3a4bce937880c00895c50ce6ab097705bf767aa5dfaec938014c76a8bc364caf08fff1c1b7b570e3549d741afe0a4e257d522f29e15eb08f529b36bf1d9ae11dd5878d14c8ce70a51553dc072ed3d13475935a260306dc261ddaa8a053815a1a9e0879fb986ae022928f46416f8ce2a36d96efdadf0b37bb7b146309c6ad95203d092f08cbce401d0fefb0a56c7299dd831e2ceec14315f67dc8f364949bada3cd5767016d7d30cbe7d2d29f8522c7adb5e455a2a892a8de82428d7415463e602b9443c8fedbc0d431ee3ba18d5c934285a770cce53335765ad8fb9e628f5def6504098d4b28b00c30174831f8bbedf2ca3f407e9418d79c3c4f3224524ba6667b03ae52de89e05cc60f057bcc5231d205dec3e13d827ae9f740aabb33b7b9a5da6ba539567020eeb1d79754f16a9bcea076337223b1e441f2598090e48495856d9aabc0341a40c -generate_ring_signature c5093dd27848ac8566eaedc9052fcd6197c28bf93c33afebaa7ce2dda5876a00 323871a9a595066bce864c151cafd777f14afb4943761ad5a574eeadf5cccb1c 251 39937708a3b6828e2561d337b4bb62a05329a677891317ef1fbf021efde4a32a 7d853d909949ca86efe934596d599e2d77997f771aa4877b6f66c9ef477cf18e e983c352edee9ff1590f1b148b377e4a2d5acf31be2fe34df8f6a8d4c4e4ba05 7e1871074a0892900d96e11c48249bf88d429d7bbfc5f0a1029dd27ae601cce6 053b105d261dafab41652fd16d5f6fd87e2c3802d396097745891a1f41af5c99 3499b5ae6a1a148a140469d676a0f42c4422cb71202196215c5e35b35dda93b0 cb14c49d87d8a24cfb716a35bfc392ed5ae3695ce41a9c65c34834f3c820ab4e d4d25b34a483a582179cb1c171532f16f1672c5350950936984484e8f2256c1b e657b8c50f8c92b530cbb8d9e77f3db440326b397a54ba56e3cc6a0b123a092d 4dd6a6ef7f8d5bc9f80693c0a886b0c4432eeab776c17346ac19504f46d622b5 130e7e54142cad6b55cf6143df37f9d206ecab657e1fce433ae278806ca1d26e dc7113def3d74145205db0179dd574c8b00b0e708ae5eb7510c2624500274532 7335c7e6cbd4e8153b9c1ee028e3412f1681058440bf04e6613c49fe1f1bc75e ae3948dd1c2d07972a05cb741943e766fa304e2fea461cf909eea69de08d4e79 28408929a927aee09552e8a162937eed73757ad37ba996365672f1204ad92717 2fdfbeda21d8293c0594ba9671d0110421fc35888908603226cd7eb0995e1ac8 3cfad5a488402caf59a69746fb5cd739782d61d6e2cf71c57bc8c1127055fa19 cea1d4b9951267cdba6ebe4c6bcfb7ff7f465582a73f3b8d60726ee1ee4564ef 36049feba81aabdb2b44241538d15f60fb851dfbc19d13a6e4de240be745fac3 d4225c2a253f2cbd16bb65b500ff4369240284ef1a4cb1f77e6e8143300d0fae e8ce1f23cf3ec0f98eecdd9c744f9894a5c8b3dcb0b29431c4bda4658b4ae24f c1456aae2024890d5c914e9e0a2926e7f004c2e2532572ce815a9ab91f8e282c 959ff70881dbc4521285d80eb7941cd4ba94418da70f041ffcc7479c7c513ccc 7aeb49c0c8b4256f7a718665b575b13cc0688964a8c947edb78eddee1b32656c bc18deebf265b86a577800c425c3c194a390b295063a87aebdf816315e04a0d1 e3e9862b6b0ef2c15cd2058dc35e03c2aafed007e25ac3aa59cbe2654afc8d42 00d1abb0f3f487207b0d764a621ec24265e35ef7d5256647b7404976a8bee167 9db522d9789398ae5954197c166cbd9f876c342836c100364716a3d23a3cf885 30a63e8040c68e413514de31646a18847214d70363850e40bcf5e9f5c3d92ca3 2a78fde45151e821a1a924b5241a2403ccfc6ed62d2463b09e7070b7279c5a9d a26c48fc6e7e82af42fa3cfbad13812bb6395aded22dd250b79f47ee7dfb6581 a801330fb5930a20ac3e1d928ad94a70faa44e7d8319cd6c781d3d5e0b7ce0fc 7ced5508691face4c0630aeee6dd57f510911092109720982102ade5361833b9 2232855cb3aac2d84031685bbe847619d358070216d20f681104f55bd100c2e9 6e49a809ca9d63a6a21496ece8bc4bcf3768b8588456ec1d909c2925f60e995e d3a035827bad58e5a65284003b112160f3d2f45c103bbbc13f00a92f101b2e7b 3abf72f7ac3ffb7fc6113fc42e26031badc02e4d8fdb3f7f08b436514c3f5a31 2d3b155aa6fb6ea05e5a8367039d257fa0b334039d5ea0ef11fc56b8117c306d c7fb3b153d7b88597b92ae7d79f0e3185716dba81744ddd9ac1fcad2e0543fe8 1325b0d92f95fa8ff2cd42384cdef4d2976e61e76ff73072bbcac1369013789f d8cb35a8558bec532c929325f135119af32d6e46a2aa9931a7464c6d469cdaac 7a7c8d38b604d98886d467cd766c40abc920d751c2c94574fad91bbd6a8aef5d be6b00d3cb61d3032253e1a7347e346dca1bb51a92a3eb8b071af0c891665b37 0717cb474be340eaaf4a144b64dac510b6e8e63e3bf9e8bcbb3c46fed88b0f52 3542fe85a3882427abfe0b026c12ae8d195a3db1534254a8d1af81d25992489f ac694745614759040e5dd4608aa75f9b570f77d2e166364d6912cbb21fea0823 5cd8f7e533b3c94444c897c8c91212f0a228fc719ceb8bd55a5c033700dbeac2 872355c0b5f2347b4d2d7fca96f8df10e41b45b9ffb72043a531274409494385 ff1d9017025185587a0a4cdd7c884cc70cece019b26ebc15b09f716560dedeef 186ba15a08137c92659ef620170b35f6b1bc9ecf4c029732451394adfc999590 9c97f115d584768d386ce5204bd2d3629aa63ae97628146498ca37b61918a442 79f55796b89a671a06bfebdca61971130b23fb61eae4704d2fac27538ac499e7 b90a0e602d78353e01b476c3a2c35a5f0c7e5f415bf78d46ede20024a1d3c4d3 a670570bf89098e8eab1fd7afe3e00b01c07049c3722d66dff9b359fa895d70d fd465b0756c76e548d9ca89b6523c153bbfefb1ed9f32774f9e114b60a551d1f 936420d78379ac16945964a1dee95d71940e87c2e88c0a309f70ea710ef1a1f1 7821936f936122f4adfe274bbd32089d4cec99401014b67f0cffbdfd3253bc51 30d8216ea0f746b13f5e4a43f6c4d297d7e6db7e19445749d595eb028ab09ccf 7681954cc4cbb792670aa9a21e64b4723d86495e4d6d27b3cd606b00114c4397 e9663cfa339901c3282b9d2b4560015f5ebd7fbbd4827023b224e54109933369 dbe9eaee8efc256b605483f862a3dc3d5f70a4053ccf29fa4a52d008441decdc 633da88c3db244e95ef373d027a09cbb954ad208fe938f44c3dee2a5c9bd7a23 a1aebb8588a8f423fd40bf80763d0a0e5c4b1bfd3fbe76f14f9b307561ed03a6 9863117a21bc64aa4a8a301aa9798ff1fe6febaffb1e2958b259e1783fb5737f 2eec0edee3b2793b4c35d7759054e7b0c15d4d450b06a5025fb65d8a60f15372 8ca8ed9269c7c5c7ffa431228e9f688716fcc4133c8f4394513251ec26fe3159 f345a4005a7c36b6b78d97a2a030e84cc8157daf4a5a02d63675ede07529dde0 f7cfca247ffb2ea7cef102761986bb68b2e5032245960d800d5fc65d3947f9b4 59afcb1a6088b9530d8bb2435c23a885ee82c3aa0c2e685631a21a228cf07e16 1f4b5692aebadd8d08e8f4468b568a94b2eb59d5d4bf26d5343cabeb20f80f01 897ecba2d98f671188525193fe5dceed743d68886ab7fbaf5be3d09564b619e7 bad4d546d08f62c1e14500f7e340a2eeb8d39e5aa99b9f66d329dd0bce311a37 a6fedfca7f611f99b505df971c935955e2a5179efba1f0323340d67962c4d84a a30f18f05878d4815c755fbe7569d5c042331ef9d94571b0e506892355bf79b4 fd7bbe962d8a76822895dc0b08c9fd60cf1e66e7d91cbb4d88a32d876941463b 5cfd62b1a9c70f8805539e08134011fda0bd8512815c04a96ab1b79f84c6ca91 07477415ae11c9974543d373acceaa7ffac870868220a69e06c3b733939b383f 947545a4a8315cfe81d61a3caf96f5f1a41d7cf354d7095891525ba18d5edc7a ae3e36b290fa5663f46cb37b4d8785778b6e7a5b5da34f28f7a1771ccefdc084 54d701879193377dc59c6df056652fd546b4f3accb0fabc6b6019320cb5ea088 18e49c30704466549121a72ef2756c34a208791ffee54ba4838225c88bb8a8e6 e569ad0ca542a034679e69d502cb792d6a6f8c864c6abdee43efbd188008e9d2 54d46ad00303564a2ee8ff3360eef10952abe041485f2876016994dec2f7ac1b 612af9076950f88f166138e7bd7eba7ce342273816ce9f701b6a3ce6a385159c 527ad33d10c16c2164c4493cb5ac7e3322cb31282c6abc8fc60d5c19395c291f 85b25c37f30e9b1e6afac878fb0fdef3b5145443bd9ba551bca138ba379817d8 f1ecc0173843823f14104228e526537ba7d80aafa2a3f048cdacb2c0640001ae 1cde7b25a4c9b20d6915df9262e220cf42af1620dbe43fe677f97a1c5e021194 40ccb1739d05ddd2e883bfe67a4b4b00807aeaf758a68251b109049674eb627e 541beec1429c13d96d5daea9a75dda434c11c07f445326b1132582caae01d282 646f5b9cc6746bcdc22265a54b7c8043cbcbeebb1ae15b9dee7f6d7c7ec0ff2d 71471b752190bd2ced82c80b406ae39ccc88db4ccc597a24cc1cebfe24e42e63 8cc5a53f3eea773c29cd1a51706f768177f0f388b88d74b2b705fdca00d41cb2 dfe6ec108b198bf4ab586bf12f3f337d341c1a727348582b02e9649a2efd9ae7 02a64a9f14c464086daac1deae485eec3b15b57ba73a3cb6829a45942df6c885 7993899437d2e12076f3311006ec58cbdc36498810766a5825434b3cea5a9fd4 8a99015c83d9788fd6890af7711c4a3701a18cb6691d01c2911daf81a7036e52 6bc9edf373af0048417561db069c88110da2929002ea335ec61b7869501066ba 212d2fa1f059a592c9698bebfa9fde4e1a42a7e5cd3ebcc5dfe4cff5e61150f4 eb4d38583d7578738306c3d8d49bdf9536403765691d7397dfb870b056806878 5086416d7d2681eaef90d4767bc362717d1f75738fe63fdbf399fe987db43485 164a5a51776f41979c044ae85dbfe48a718dc637e7f77602ac4745da472cff14 93cd3e88854c1f515ecdb84622489175df9af084ad7f7072cda98f3b4ea76031 83a6a90b6cba9599afdded09ca949ff11c59f21d715dbfe1b7bb69e8cd30e39e 1411f05a4deebdcb245c7fc6e1260963e848d2d5093bf81567abcf9f84823b36 0d7a2530fb5a816088760d2ff8268e77dae532a31d162fe4538f75faf017560d 0fb071cf8e1fe1698f021a4817531138967476a396b75b3cc904856f2d973caf 030725321cc53ccb7f6131762f53c5e56e9267c4b43acf557be855e62ac1555e e552bf40c1ffaf28e1c467817ef78ef8364d939f5005f981e3c15af2a8e1f68f 7e11b3ad88eb6d844a993b9de6b85439e897ecec629a27e001482461dd40d0ea 16f0aa77b2720fc1c010682081d6cf1a4dc995ba28641cf5bb3ca92be1396083 0d74a5621aa39cd36575996f46f70cb2e17a65a48544ef8153fa3ebd092cd2ac b07a03f471b407eae66bbd77e5f32d7df9ba2bd53600644a6c43ccf2da37380e 2453ee3f5ebc0f2c92ec0b9e520fdc85a5bfd1094774e89dbcf7c47f6a3c8e44 d251b156ff019aae5893d61e0a97d790a6f906970232452c420c2accc10150c0 c5a56fd86a0fd7b6a17cc704b241840b377d47f47eeec7a46b966354655f8b11 9cc2e36eafb7a43d447577fd8ebd8daaed083338bce3c7d150a562e5c77c56c7 1f496612b8a0a887f9fe7ba917df15a26c2a0e5367528b7dc207bfc731bb0ccc 9e53a519f3f0743b9b02d1869a9acde5ebc122f764facec61fbb1b344d71c26f 090a467d9f5b6e63f1d891861ed4b07ca59c39957c94a58b61bc31b0a5fd7ae0 0ef42450c94af1bc08b782b5fd13d4def2d845f1340e911cb3cc1a99deef0987 f56cf062d1975b7d2325078fccf313b0ca0f9bf47fb81a1f9496c59a56c38c9e 72a867219b50935dfe77b3b1df2962f2693f35d1786e586c1106f275e2fd43d1 0ce8053f27a719156673574afa97f4d4472cc0e61508e5e46f34d60349d70a24 7ae67af34879c1a48f48338f3c6a43e28b7adea79209cfe307929d2c213d18bd e8bc556cf00960590987c6b8e92c034421699462da965550e042b0394ce5f3c4 006ee6f0266bc3d1ee4de3c4dbabfd237dad65f3d8d1883be6e8261800a3f504 8bef60f2f5d2a4b6f92113fc6e6644e25786cde40c5b5756f0f0c00660905ee4 d5ddc9afb5043d875d63729e1ff1454e33b625c4b8809d6cc78a722fbad332eb eb9e35797e0754a91ce08ea61798563703fe8209ff6689c88b36d6a30440cc23 32e5d527199ef20bcd81d4a66bdc2ec4e06cc46ef4fce8f047267313b26b7739 bcd9bd9ca2c136832d0469c6e38b8df493072badc35ed90bb6063f10f565ef95 587cf5785217edbd9406ee2210fee0b0963001048943eb4360da7a922e09a6ca 66faac7543d5e35abc41ff40e93d26dc3dbbc0225b283e6803a8586a25d8f83e 481a8dd416ea4cb95da47d4df364da485d7733bf9544956082673aa41655fcd8 87cfab1ca3d4e95aa3419c6c42fd3eba0ec55dbbb4c1dcb2f344a10e0a4c1fe5 66925ceed101bb8ba62b1023f59f4caf1f30ce11d98971e5d26ea31419849e5c d5b3eebf8aa62351c4b731531ef851d9fe093327fb7a4598a5e3b0fa11d1aefa 144b857e9e1fd993dc9bc2677345d5cb721171e42eb90749675e782664e6c002 b93b6b29b90149848c2452df74b309a91ba6b67db97f03d85f0337ebc8cc624d b7b8343623c8331428a87549b7b93cf932dc3008a94920a95987226883bcecb2 7b4538e82a894a23770c06c18aadd0806ba8a9d2a001c07cfaf4be39913c7d2f 925df872ca181459906b39330b8238642f0c2ee8455e79378077064b41615316 c474eda94a395af8db75357c336d7093c996d9d73572fb646bafd395c8ee14e7 0d73fbfc77b3fe3a5750280e8d5a5427417a5474180efd55342fdf464a286cd9 64be1caece30b05367337ba9e0cae59a8b5fca0f872a256a2ca529db87bb56f8 8be33680a41f61ac87e8834991bf5bb066d6761d6b8dc1ee3261ab31d9489e2d 181df403060aaa9e45775e7711244a3eee977179a1a8b265ca469ca37ea8b918 c75c38b856bcd1aba8f0b39da20e99454a829a64aeda962211501db1f44fea65 ade8b070d01fa4825714e9cff03d3888b8e4494aa106ece1b5f19ac93ad662a2 be8cb23501c41cc8da43ff11695b06f03a7bfcb556eebefb3ef756a4d3adf39e 635c8c8532405004c47ec4f598eaf557d0f190101f2dc9092cbfb5d2c194a632 9f0303816b3161b9a54ed048c58f2aebd4b50dd1fd74759b77668d55aeca6966 6913a5aa387f5fc1df608509e94efeabbe1e645a7a0b2ea1731f5c2b76c4487b 3439a5b52138d8489f8e4ea2b6eed403591ffe406c92517fd92a0b94699f4338 e37f7d0be2eda682c76b916dde8e1f59e49a3f728d0e6a384a70617d20d67b1d b608f6462485000a57441392ba0a6f3e080a5702a2e1c2f0ee076c89396e626e f45b075f8bacc04dc537a68e3152714f9ec771cca01e8dedbc8ca0556bae3e50 ab88abef0ba0c0f80019a69d3edf35bb3acaec3b4cabbb999c2811fe6c0e5201 4131146f23703cd3a999104ad4de0e6cbe70617a18db2f12fe205ac0145c9939 7533bddc26066cbd331ea83cd47bed0803dcddf53e44efc8d08bb25622008642 bd4128d7a66046ea22c24c4113bb19550e8190873148ae4f8f7c2ce5088813db 97b798c7b5bee39cfbdcdd05ee03cc41531902fbf2510e2173fdf65f77d33e4b dcf940ab3e938ebb84705381e9bd8fbbf28d38147333027c931969294a73c5fb 8847b8216ce4370e2d6af86d9078e9076dd0d1254d39fdf268fe77fd555ac582 1164bc0ceb2209a98e74b2c8b0208d87e7b22faddfb85264d193b9e635bbfa0c 6389eededbc5d132c379c19f329b97a17504c1d15a0d1394f0c5424d71afed2e 23a1015cccd6b3e32b86be91c5cc6e284728e7361365492b643695ca569bec6f 994c0fa0662667820fe339e742f682eadb7363c2f32bbedb3c0581eac6103130 a07f149f06097c6867f376efdae00a1eadd77df6dd962fef3ff384aa62dbd994 132ee638838203d2f73abf3d2d6565a2b66a49182fd1efcf4e74f337dcef8abf 6e88dc3ef36edcc0a0855b50c9b74918c2a97c143158015391435bcd51cd997c 2f667583cbb5396449332bbc7eadd753dbfddbd858aef3427c1a612d5816233c cea8feb61dce52b4289a8d5340b4309b1c5eb6faf13f1119cd4b2bede2a197d9 784b19bb39c04a0c64d8e03cfa02cccf2bc421b68641a909367d0328917b707c 75beda4e48f1475b63c4e878d44172bb4c4c8223af792ea94ff990bca6b3069b 7faf00a416bd08e6e4ab4ddbfdda1a081a00a64fa0392b95e9230eee21b652d0 2fb7d868c25ff5d388eac019245e59490a83650409c4e6e577df9b3c42f0bed2 b69baad2f76263308d1f073fbeb2f1e8c00bc4bd11191eb5def5a9fdcac60ff2 666f450028ac20329193551b9c2610d8b25dcb9fa8f5940f43e7a60e9c47b7f0 84a1f6033685833b55eac7e04c741af9f760a50d56f209d222b0d2b46d42c532 f227596d9f75206ce74f2f6078986571acf0eba96100e76e01333836cca5828f 787811688624da50922ef60b9fed8899a3734ed64a553ea174255c02b775059e 0c38b0514b4afa77993790fd35a2313f70e2a5c7f2e57504e86b15b5be965b92 33e697073eedc087320c51d1ecf61b54d5578e355c636418b217f98ad8fbdc53 4b64e1c35f9eb38ad17344a4165489e69ea3407101b409465ad17f8b47dec206 7d6d53cbf0c498461fe18a8e68a19ab04374d171951c61bebd95dc4564ce1c98 17066d4063e136e4ca567b7a2dd79aa7aca40634f87793656fb38fde733dd759 fb9ac5740f31fff598eee443097156f3790a54ddb14cfe02aa6cf2e5b3263f46 b7f18404b45acafc9bb8cb10a186a696ae7ffb84126e5458f50a2f1794e15469 929ec779dabe57da88b1281ce1792624c729187e4fdc0d50585ab8528702db2d 8cb2b46ffcad9dcf356bb089b8beb2a461e4ad8cbd5d2532780350e37e41d0b8 25125b917658538e1d422595b7c4a5917d81e47d397b74baa5d713d1babbd730 6ab9febd5a2f5836ad625968e702dacc6babc7fe52d428d7bbae17ea7c3ad61f 4874db513848b00dfa108c53669ebc17506166896af48e3c68148d4bf8b99939 89872f5084d9875e216519bf5a6c0b9211d3ae603683f411ecb84b381895e863 6cd5ae3853e0abe7ab6854b6d0b0926d92c144293ef4dd4f9972fef1ce5d6e37 ab3475fb2007c87278a0b7a48147d165640842d5f67169dc0f3ae2fd305db10b 1ff198d5440e28b78e48d730cb6c210adbe74d42862b41c5ed42515b967b3c68 9cb44a35a2a15d1aa9a94b71d06fcba5758f1f691b48d1817a063b4be7f2ac5e 3b1552d31a70d241c3c33b5ef0358cc846b2565427d7c66094f08fdf0dc553de 5ab0a9edd5f873c69035024f5ce6dcea1fcbae7c75a35ddd3212783d0c692957 ad1d6326f5f0f122e8702a8494a3cefda9c5ecacc61daa3b3fc1dd94fd58e5e1 f8545840aa70b10af0918892800f095aaa228d8cc94527e93b1e5040d1413c88 9209e8b69e44f10f57fd5b60ffd7ea319ee94679bda2321cce9251f1260486ae ef5b023e360229edfcb38c0357ccb791b337630d5296f505763d48ca6d332114 641bf6bd2b1b1356498ea723e0018eba1c4126bfaaf51cedcb144ab2d0362398 6f3e871c296ece9eddc5461072ef8566bb4ffb9071eb591eea8eedfd0fa8a97f 2487fa37a0778bcd6560c8a445d09d5c7fa8ea4f5682c04cc446cdaf1af9f579 7126fd6c1918ff3feb8cf821b586142a07680460d7dfe528aced2980dc403076 d4d6a5d8655a8119d0686d5a82f74606037da1a7a2996ed25e9f3fdf77ca391f 8cc8830b2213ec306c201584340f4b4f5cbd3b3fa3eb36f75786c822e22e594c 32d173fabfc93d005f3ac3ad2366703b38907a2672b5bc724241ed87289d48d3 231ea19cb8dfd1538ad9358c94f381884e5eb671ebee6de0032dfbcdb53740c8 81346e00b86ca5b96febb07161273ebe427f374522df2ebac148d83fb1c84b65 6622ad031e4a255114ffcc1b993d8d3e31c6d65e40a59574905b216a352a37dc d74f5f46d0a64f9dfa77600f3f30b38d2a0143a770e664119f9f77ab9d2f09c6 8e653adfaf5064ea5eeb09d51dd65dd8655fa7fcba239f4ec6ff41379d44768d dd71ac0050e36750b6e33c90efa389b856cc6d06b4a88a2ec732bc505886f114 005dbef9166eb9d661b8954120bc5d3a26f0019ce3f3ed9323c45319e1820551 f5b548cc9af5856e732b3d2d40d73ed801d809611be1b95d3a0a4578974b2d20 defc412b437cad2fdc7299390a68aacb97e78252f38a80d9edabbcd3a4abf3ce e0af56e16a967fb94821b853f6197c6d9994ee0aab2b081ec16ee21f228ec0fb 2853b9be2f74e4731ad0d77b05fae3297fa7087f6e9a6a61dc8295035ed081fd 4d6a31f13950296e764b25be58d3f379d79b3bbd6b38f01fb6d3e473a4f51712 623c7cf4765775246300ad80136a7362cf5c9d862708aa7a19f57f56cc06dcad 548e4d75e578610781844f271035461009b327954bb099a2c1e45ac9b08c6df5 191e63823ab830693cf02c68e19d49e9307c7a4f044b7698dd94d476df250862 e8fd2c2f0a999138fcb936569f3f470c4d683580d72cbc1bd1e4b36faea2d972 925689b1d36695f45765184f979dff2f2bc70efe57e37d9226319b50b90dd469 8fd4bb24b442796650c1f0d825f468b955a34c5b61189a1c44b6df47feb40d8d 0361ed2dcb2a570f577b35cc6d7eeac9b617d70b7967fd4d30f94da25ffffc39 d732a6674821514311651afe2142d14be9526596e9f45e9c8cb658cca6fe53bd fa76c7ea0f87084e9543939eba23cad3d3ea2a0df03733044e73fa25859b8cd5 c8ddeb6e648cfc07f8ebf7a677ff6a038e747e37923849c15c45342afbef8493 5c431e3a46089c0b41ba2136374f0e42ba6f9555b0019995b690e02d1800d814 93dbc3d9dd0820e634aeebdc808ccabfea052b77199ae04fda47859928a371c8 df9e4ec31e852616503784e26457faad8ecd94428884bda9ee6d81c575394867 f9707e1b484adfcde45bd506627735685e0b3663ef00a41c48bb1c11cbc266bb b123a4cb6366790bac198d2d3d400b201c75cc4af7a3430c8a546b9891ae05dc ef38093824a8b47c138e20844f923934f435dc18e84cce31a679a61fe68917c0 e81b5c08382f256377a7fde70b56421e7962d4b9ee90378b201666a0bf88c6ab 256e4f321a15a634355690e49bd7ad6b92cd51c8b1629f85c49260c0c3431f63 ca7b3fdca514a286c7e8f440e8353af4a030c31e5f27ecdbde302a1f8269c482 e6a199df5a488561924613397989d00c595ab63019c2bd1d8b5df37fb1ac7ada 4610c481631c285a6acc307c9439f8f8acfc306f47194e2343cd74efb7007e4f bb2d60b1aeeaba077329246a5a406c310a98f94f3c51ee6dbd102f9e6677198a 834ef843a1b0508f4862ed8013d6555cc21a23c52fb5609c2698c20db9912922 f624c48256355e29be4357a424d2d5d6d9d0d4d8ed3045fd28957d1b747d1867 6168d8666d802eab8552a1908420575c4cc9b5dcda2bbad9c10df93d7b665931 206b2c718e2f34a009b1f1ee7df74b9e813831f175ad5d06e520af8d0012d178 61981a77843ffebd3625d850e0419fbeaaa7a08688d92266f33100d7b6c40d0a 141 baadee1a40088b6a12d2d42c4ec0cef90b2ca2199671de5da0d632574f418005a886ce63952934a241b79867eb02f3bcbe1404d5da5308a5de2a30043d32b3055f8026cf0c9482445d78587add0baef4f4df801b8a655a368c39059d328cb3039aea951e7d28ba14fa535f9512b5602691d09e49b287d05cf4d4b7b43501b10a8fee0bcc04474fe2a79f9250919caf1e5b9b2eda5d06c528c4bb1672690a700d7a185378640c2dbd25806cbe1b7672e43be1e0c967837049acfff981476e900ac85c12ebb8808628165176d7a8bb5f51ab29c64e3336c5449f4a594830142e070ab91dc2262dee20c94bd7014d5805b2f62764433f10bcf304742273ae3a9c020a1f3fcbe3e72a0ad34e0ca4a0b9ee224c773a3f44dfa0489440dd23b5dc54070b3b047f304160be47a6bd8ce0aa20fc2653e73bd6c17a84a2c179e90107180e37fa385728b6bde59220ed07ed1ec543d21a6ee654dd5ac661e10730e18bfc00fcf5bb6f6d19624e32c54aa9f0ddf97513a3721f7cfe7fce302a05054c83c40e4dd2e4e75250b761caf0c2295b008aba28f2d684ada2d7005c520e5005643207816558d93858568eaba6430da13867ff23a1130ed7e76d13050579bee6d9d40e240fd9cd8022324d09f7a5edb819914f4bc34ea8de1940339c8e98508c91ba0643cb6f675572bf277caf485469d8281383aee0c60f71b394ebd587995cbdd90a8f709083fad29207556984c852aa6e72070ba197e4a827fb6ded5f54e27b390057e3818b3e357c261df9555f731343fce276663ed527b5de71a69432db4d1305c41eee9109e505fa6fbc1de8b8403489b9b1e00c11660ec5149cde26a0ae660c8fa749daadfe710998ed65741dc686000a72b8db7c4850b668932cb88035b40bc5ea15f115c03a194496e1f0226c3770b73b6df9958e961a565675cb35e6e70420bae26e18081c645a7e66df13268bbce1f163e83db647c3ffa1afc1bb6ce70c46d478a8abc76f3df8eaf4ca636f2890b6880f1149d0a9eddb5ca5fc40455309b07ebfebe6415b782ef1a7534fb271701be52e1713a174d0997a4288afe58e0f2b23cb4ee0097f92c6f2bf93891f1622a94f992425175b8347b9c394f418bb06ac67ec268be6623e322364e1a4dd479ea4d5a42288764e15b30e40ef7f9008050ec5fa7fb3c77d1932e2f89fc41ec2ac4d1a18a14563cf73e3f3a04820f9b20d6d63478ad152a1163f68a0a6bf63a4629c2afc24aa626495a9ff94303b41d70b62193f784b87409c957fc76dc66b6b45e861da42757c4b7488d603fdc3bf1b0f9bf42aff70b6cd377594f8e0603af186aeee6be8d1fce001fb65790776dadc0c6307f67756723c5a13cde4247e28af99d81e9dac67a5c699ddd1fa8393b509006127d039fd9e4995568f5828150165f99892aecfdb016981b00eb6200b3d7d08d6ac34238ecc5b0fc1e3b71b00d7327f55016d266c576e4c31eaf1b927e54e070e3105988a79723966073ce3361cd27c593af3c0eb4d7285a7a1efec53611e0e2626676818dc5e01a1102ed6d28571316a309e9fb1686e2f13f855e47242ae00fe7a68d6f68eff7277c961d80870c505281fc2e20073144f1eecdc571be72f0895ec0a6b89e3504dda22e2914a019161d784e4324974141ea7f6aed287d34c0522406e9a4d595e9b8ca9a38e3fb3a11ba6a1c8720859f8eb7e927ecd3961bf08556ae96bfac1fc3d70b682838268b4ac9212c544f6881076c6b399f1806ff90604986a2d3d34b119491f5e21bfe0c9ff8a1a4b369bddcdb5e5fac85f41fdb102e21ce8497664140ee1bf6bce9bd3c5f3ac62e767120718607ec3cb2022db8d0a508728e62d9ad577609a4ae88b020373d936bc2e22506493e713894bb7419f0a4bc8ca460979416ac94039f0237cfc43b44223b3f43ab76d2134a6e648dc15018f6810d1e96459ac7fb5fe285b3c33cdf14c3f724b3071070ce4310e63249300e944f891bcb72d992f5a049502018b2fc3537150be2b8bb65985e05641baf40122f13292588fd1b7ba1fe8ee890f8e756a0624985bdefe23c71d19c2cb63d60ad65868733e92b5b2f45cce9f56d570dea4c8c8d1da566ed7f32f44b53b01f60a9fe494f419bb480751bca9d0fc05a311e33821804ec95b693c77885acde213017bdce9e9c125ecc8c613628907b27722ec14f903242bccf55cb2ec9578a43808a418d50fafccbc128bd954fb3f0e0e37aac29d17ceaa201faddb997ecda8020a836c7bdac650ca09d16ecb26c39250123ed40d859424c238e26b81de62041f0f94767bdff0a99865c2f79d24ff4d6e77de004457e2d97075422a2e90f58a8a0cb553f8c044bcbfc7251faf2ee14226ceebb86e4d6933429e7bc6a3d6ea41ff00dc381d78863771c7df00128e92b30da9bf40fa8cae8c2ee1555ded0d2a858100ef9bf5aa23dd34cac63eef2d7a0e44dbf7787ae843ab609aaee021a4821719049477c1f446d5956dbd5c8c3b30eb3aa76e2969207beab56e57200dc086337f0f69273e32db56e94673f136870c5b16a672035cd03821e5ffc5e59491e702b702d52482810d41398e3a7704d28112298dddca08f0c372456ffd8d6111e6bd3d0d6850f58277049bd037f47646c6b2331dddd284f003e0aef6933a5c2a75258f0cc16c971f1564d97d3a39fa5d0b802bf1e71b9fc7c91e05073c3c8083d907380102b18ad94fefb4804b9a768cf5153765a1004fd10e479f2ec14dd8c461656b05183a510a111357cb586da1f14529bdbbda94ee337c2d061c15a9b47598614001f8c49a8cf38e0e8f52ebe3c46ffe7cc6727ea838d0ea45089f4701086d836400c3942694454c8fb70474980e79509f3a62ee0baa46d7f3208aa9a88d1717d20af0201b5c1a0e58433345427a6d75c2ec72137d6a53dbafeb7c1b18501a9cc805534c520e47f31fcb027a683534b6a7f62165f2b35d80cefec010f0d7c406130a66da55d731b14011b7d27fb41a885dd404ec75e3514f8c901a61e1fd02389f08c4b0a733b088ae5fa29c50df7705926d6cf594cf3df42e33ba58ef31babb230be0742ecabb3a7b239126bfe5416e7e6b2fd15ffbece1cf978edbd7497150e60c643272ee14b5d27a5917f5cb003ecc4eb9e92012bb354bf5ebb068ba43ba920628e8adf401129b2ce564c474054863ec3a412113dacd1c4354aa15087a5529065e6122290050597a22a2a7ab909532e1118d32a4407d843a0b22dbf43d9fc0059c0798dec28a1fb6f363eccc1814b8cc262cc4f2c7d4f822cb70b29616653c00e378042b409c6e3441a13093cd2aef7b6eabc34b9bd3016f6cd4e66895b1080af8a743a14f47c0e2069895de0d59149743b0edfb4b25b747b686fc241e5d110c98553b0cd757f52ce15ef51d4daea28559a59596cb2be1c2345854c63e19cc0bc0b4187c3c0042a8f7cd481d4540a06a5dd4e1797dde802f3392c4bedad908029b9528ceb7e5d93527a52cda61dbaa0290a155cfa739cad158b0151a6ded1a0572f7d44027cc4fda669d0245235350e5beff34460c3450b79e9b256c403b6c0269b431acf0e29e9ca1826329e2200b917723fe0eab18d9c842b172f02ec7bf03e07857813a0a1642c5798d45dfccbc68e6d5e43413f4c1a3fef2dec73e3c7107215134d463cce134f8277936a661f388609a9904b4a42d0b6c03f1ae3b52d309883759ef632157a6e417755d4dfa3f40676fd9bcd96da4d8ea64016944f6e101483d7b113934e15e9ed2323879b852bda2d333247059fe8103c2ef64ef17120654f0bee564930795a143800294bfe4934ae44b2d6954337ed7c5a2bb35048f0dcbdfdf7de4c0c1f68d02ff4dafa7932cf64bc127572c5020996462063f89fe0c775725f21dcd25cbefe0bc24ba9a152abcfb579ca939f424592c73cf96945e04fcc50b7a35cb0e3dc32ae26c7e7101b35ecba95e2575c54740f6fdeef900f70e917eca779c716639cbf17dbd8ce4dfc7b7c644163bb6ea6e20d0a9c8d9497e06acad00492ca4cf0aa327070b6058139770b0fc46d3347ecf65b2e14d2ef364069075f263142ca4d610b62400d28ba3f12abe09ea5836b4e6a04dd8d89c2f8a03ce740525372e95861f0ff347fc6dc539a1398c0d007c8c9614817369281e0b0d80268ed97d9d0a62eddb80c4ef886a7ab9d177125c58efd6d39bec3bc96ada0ce1d6403b92f1638e0ce885cd030ed92bd9e5d5bbaf6a8547838f8df0ed2dd90a82178c2917ed441327cd131da740ea6f9f82681eb4c3ae247a0c6c6615c4aa04ed77bdd11a8188a1b665b7a6d18350d604e1b63a3eb3581fb0d273085c72640e8ca93a3c242ff777626e61c98c669943ba607b5cdc65508bd00141f01ab75b01048cb7aeca89907dcef6abe9fa3ffdeb3243a378c2453453b15554bfbcff470d37b327b8d4d1deef20f821855bead227093d71a436d5f449b91393e05659a6029d64f41ec2686dbc1d5e4647563d40f6e4657cb4bb1bfbf7eaf74cd04820bf0f6d8899d6765edfeafadac809a803ebeb6d5df9dbf65ae81e73131aa9b77ad109fcc535965c735f9d419ee788d086a69a3483c8553ea17003c78d76b2bf4d2801d33c2be06b519c6c9f9e711bf03a5290bb45fc9b6d4ee55e9690e3448a8e880db76d214543cba97a1bea661789e6edcb05697d31cc775dc896ad8b0acd1681026b2a55bb66c3a6f5e8e31cd8fe073f0e09f77b138107fd0d94e449b2f210590dcd78b35eae0b6a0bbeaebf90486bd81325bafe6ef28bc734a513bf49c7bbbb0219d292fd586d4e07bd04b07ee7d46e87ec2ead21eb87ce366ee2a3de29a5ff0a84e7ff2d4286a4c492e6ef0ee087e78ada8cd5a2b073abe9abc6880b8b066204192c17d9f321e9541561d14035db559dab605adfa50186ad1507ef607ea3500cb693eaf87835d01fdba9f36f3a3b3947ef4bd1121ed289876e4d0b070d8ba906b81e7b753b7113389083ccb577b7b2feab1057ab9b806f7638f74bc87db9ba08fc4070c4807414abb0b2aaea6513e8cf44ac851de531cec4d85eb78e2e03ed02bbfce91595ac4b083e608cbba0b2786a9f188a2f67407e6c9bcd2e8233250f0e930ad337dceb494a8d89cb40c462d25c510029c5c2268d6d8d9ce744a0e30a014a09210f349e3be25bf1c8a832016515a89cfb549b4a31c4fd5866e2a1b1d000be2082387bf0d8c86c25148086bce0fa155076e1ee9480f34d59c50d71484e0a2d161620fac3b9875f82b1c38fecf6374a027f58a9219575d5329d730194fb020b1e597f9c04087705e6ec0acf89c855a42f138710a93e74110561e4567f940be71a2ac9ae3e170686599d2770fc96545ce0370908bf18c14ce53223a2c19809fca24d7ffacf22426df5bab411b669d6d5406b3eef10ba8293b1b6a6c9c2c005c928a5b37d40dd0a72e6b678d2d1ff9e826d439dff4171aa4ca834663fcc4f0f857eaa329751eef364df7a1ad9a6742fbad433d41196531b4bcc0257e54e6e02860dfa816518f0b91c8249656110c2cf7b525c6497663a97d6e658e71117d109285f4b0881a9f9b27cbf305052befd4a1699fb67bd017251cd7518c88cb7d60e128d58b391dea125fbafd81244b68a8f5593c222c329ce6ab83bdbf12404bb027f9bd5e1ad3894d5091ab55531e27a1da663958f7cc2300c7c473bfd9d633108a60b406baba2fc3d862b4a4de32c0f3439e5af7009d0564451c2abbbbbacb40a2725677eae5fa83bf066cab88503ba40c185a5649ed514c67b293e6ec12b720460e421c5a5213b388945a4ddb211febfc377d489163913d31e2fc96e1680d50621f276c9b0827ee64afc11514b6ee5f01df2ed0a5fb0d625099100876c3c480b1b89d7aa0426bc727de57262b217ccf46c3756f60f78f9bdcc4461e2f93a1405be19c73081525bd1d2ce06653bfd7b3d11c6bee650373deae82987627ca96804c522dacf9a8fb57cbc179f33899143d47896761bb76ab0b9596bba671fdda50c90ae67144f02731903f1b6724a6f82dacc88fb9fe5eb5cc6e97c183906282f0b8389980790ed7b3f406e96398379834a36fe95be044f91296dec79209e476609c5ce5d4a127c6b979fedd80db2fbbba38651afea7b85ba328c9605ce71cbf208e9c0d5026a554aae85d94850ce56069fb446bc1416fe02157109f4cc24bc660c950d96d2a31dc58b08a92e369b98dccbc3eaafa775fda54d9f2e57f7641dbf0874be3ddd2a9bd16c65aa2b5cf5e2831e5790069610c41a52a02e9fe1d60ac9061d0c94fb6dcedc8bc83c1b2a0a07345eb41f3b92d9beae78bc3e1eb0b5a90802fbce25b5b225382043bdae5008ff640656d4eac0ed64ba87faa51cd9f36f5001c1782eb8bd37f8edb072a65ad7bb86f94a90fc42ae7f45591fb06658d84730045bd9771a8cdd8079fb8a51062542b3a26e9156acc2fc6d60ae83b2b92b46870104c4468332c5d3de4e02c64665cd91a858f57e181867bf644e54c70c1badb7008c1ce71cb6f796fa7ed072f5c7ae4b2dc0b50e65bc557d11c093273ca401b0030144974a9439cac709f659d7622b1c87001fa5f4ff6c0b2352e8337051c5a60c04b478057fee16e5d813a79809f256a76c14fea95b1c4fa0c615c377f138e200724a7351a2650c46a4512c092edd21979ce41cc51a75c284c2d30867fa836008f7730698bfb3caed6a704602689da076e8366897282bbc49d7fd81922a406d0236abc9bde0bb2ffc182295aa2d419f57404f520cfbc86439c19f01ba82dc670a105160fcf5cc31206f301b5c6d35246904158a58c516a80ff620750268808b0c8f0082bcfacd63fc00fbffc97c5134ab89c2409dc05c915a3696e25558d583071a01701561b34a2e64fbe91ad080114f3cfdb72d7ec67f24dcdcc439c719f00761e0e262766e067cb0a749e9bf78017891d48c11e33364d979f698d178dccc0643f5326c1f143bba9cea98e35365cda3dfdf216251612278873ff1271ad5db058da0a6fe743099d3d0bca90d90d2c5d10d7aa952aeb2aa641ce4e1e9cdf9be04cac6014f1ae683ebf4143f9d1b420a8413b6f7a42a0e015dd77cfcbb9a60220ac8ad1f5af352c404af6e7e10315d8d9a23d5e2d235bf5e5e7d723b83f4de5501b38b37d79e1df7a5c027df76523eefa6e9fbcb1bd6150d496e52472eb452e304181d23031b1669c97eefff377ca974d249f0193600bda88ddd60d1f9db409b0cf64c667652f78594474f0388a388fe2afcb191cfc9387b714c0c20fd5a24100f934abbc02a62afbb1ba125fa292fbb03713b8dcdc21eb0d6216802f62178910559813f4e01e9938f01cdb227cd88c2e21abc53949254a192a4c797738c151c007914f8c89ddfae692fdbebdca2ff12e731cdc0876edb44b229714eba31c2e50331c08994a6b041acee5f42444874df6432e3cc52f3bc9842bbb64cee1b2c960a9c5a491d5f73a66582ad5f5cdd115695289ced7a1e296f1e3a1f2708358848098529375fecd7df8ff7f3d70a5bda7b85751e786ed4a03b674d012449305f2d07e1a94527446f57bf5c9f112365211d7dcb2d2d58314e2a54a822c8b499ae6d061a1b0295fc7f714428405f4ada366e57b224219de8b4859b7d8b28460aba8a02df9dbf19740ec316777fbd2179aa0a3e57c1f8aaec4ab77cddd49c813653420e3e7f25047ee9aba610b3e8e2e958e5b8f50658b88d2b3a17bc9144ddf12c340b9a4ef4585dd4612f08440b360f3956825eae5eef26cd370999337c4706b7990d30abfea11280a9439b9ae960dfdba433c18839e83862805c92c07305f78fce0d509efdb3a77ce55e90ba00935edbe2b3390327c31d3d34b28efe84372dde570cf0541c7973c38a7ca6d44d20e2bf6991e8d2a71f279abfe9a9ed55a218a0f80847c4b04f7a2e950e70f1d4c85458afd4d9c8b94021df1e2b16e2faecf5b29100ea6b02f25121c0893f3ac5525313cf08a78d46625adb46da7369e26641c3ad0575ec0ee8045dcbdaeef06a03243c181dc9d921ef3f8e0f155e8f7674cf99ed0b2486cc63a75a5ba5e3ae6baee70f30dacee1a7c7e40ba18a96fe7ceebdd59d022f3dbe342c8dcd48518fc32525f235578794666f3ff6afa12a0972ce61a03a0cfe72fefb9c405b785c0a916ce0847b86da93b28d53ae395e4d911a2fe6772c00440cebc658d48825b7d8abba760df9a0ec74dc787ae5b1e24d02fecaa345860568cef79d9bc018425db1c4e8bc46e04d573953e294c9c579d9329b5c480574026a48a3dbccbd85f70e5e44073423f4aa4eb60fbbd47e124896e7b2efd048ad05ccbca10dc26fd590c93f677dc1a04d40c12f63cb8356e452f2045b4bdd65fc030fdef0fdb71ac0a9c1a24c27153e313977ced9138451f8c8178e19c9a6493301a0c52025885cf0df16499c0a69f8c6b1fc58238c8061b3d6802314126e6c7a07f0b091415e5c97a1d1331d1ea4a7095a3cb2232b3182681befa43c04878290031ccae26cea240faaae2ea3e830439d76f026c75e175a9e2507749c6753c7de061233582993d2468796b0e9b533cf1b355a135fab5186f277207f3e01e7d0fb0582d63e4f4cb073eca43e3cfeb8e86b278a86a7a441cc5d7cb94a4c868f5f7c0efb7e59dde1899d712784774392c297abb2bea8632cb9da6228df187bec6a8e050bb57130a1c2f6cafb2f2afdd12af1c217f44477b10c481e81cea11af5ff4f07f107439f86f3708c547dc181bd76db7efe1dbb6e69936478c3be256ad0355805672edb8f30df2f5dd71251d37982d18acf15f85340db8ad3920e860243bc7701cb27cf575ab6e1db0e7fe9faae406cbc8e3155dbaa4e48f0b2f26685ceb6fc061e3b395b2c9e82e79f7770759d0f75b5952ba7c8a47446e44c6758921440d4075d3d0c3aecf73cf8d6f178c8d279689b7d4da88d2bf12e9a3d16a607014c84097699c6528017d7f14ae1e8aea8f5157d914c7fec26faefe85aae6dc389ee6d0153b43f913be274f77d3c30fac8047cf75b7ade70ca8d039dff0997f5be241a03f8539fcbbaf8295a110d166c523749073c2d54a792736568b4077a26176e910d070bd14fe9baddbf97fd9ba40c19c0dbcf0e55f3081a7eee75d53c28cbaf7a035acf29a6b7dca0fc2cec688fe826596da01cd24700296721814e3bf0c1884f089f7baa4f55551362498ffc5989c42e28f5a313dfbc34bf21529022ae7473080edeb9fd88690db5b3ba222ce77189fe5bffd3086cbcb4a5e16bee5c1e007fb70f0d8221983a9216f404f5a27a234ccb9842a5c3afaca8497e23c0ff11d0c1fb089d1f832802cdc2c5f58223597f5b95871238b530009d470783b0bc37fa5d1e0cc8c4bf597f44e79028c58969031850549840e8568430bc802fe4bd164315b7091b018bf918e7098380b6f50d99e0c3df3e2c1bd132cbebd6dfecf083c1265306dbeafd581b5faf782f458b12ce0adb183199cc1f218fa46c71dfb6a43a31da0aba38fa49ee58473a4e79a9726646b14a6e301dca59f70c8029e03b679a404d0d50f34f2d0a5815aacce5fda52dd9db8bc21c63072d2286433adc512d30cbc406d0ba1e3402eb4c35d6e4b80e60025eb13f72e5aa91f3657a41668927a836350ccd94f09c4059204eb56b5ce602edb210bb52f340a642385c463e6317e0e6840dd16775fbf5cecb5916b25a0b8fe06a4c27765e905843a8dd89f73afef2bdd10fae4144202a11e0666241e65aa7d701252ecedd4f42682fdf381990833cd62c0bb2bcf2b477bca6030f6454ff6b1faf1ff1fdcc4b27ed4105706c4182b743d70507e8ba2a747db11e6bf9dd8faad0ee27d0e664031fc99e2db89846bc409b4b009e0f38fc3a6a5bc2beccacf0729967997331d37e64f3442b58e1e23490449306d49c52ccb97acf8084ae683a8402df891431c5b6d3c48524086c08b909998a0d86d3d3ca23ac8e89bd616fe594602cd871a94e7e032b79ec5533215c8e79f50700de8f3a43f160e22dfa6d5aed49544cad2a2c771cf7e6cefe9f60615b8adf03143a4a4b16d9680cff746fce52fd6040fd01e87e9245a9da351e8394b3e85f0ff869e7df19d69ba079ba673c735d1a4854787e2e8f87bfff5789b33c0c8dc30baf3f8ef10534aea0dc8d0ed4325f40201f6ab1769e6539aa8acf14c7a6bccc073bbc036e0a255ab2edf5da2d3d446d23afcb0984d508bb188693ec5fa9485e00b60c483b56aa802f892b2e720b96b9f5621d7e8bc8add2199b6dc884bb7ffe0e5124feaba49b42e789eeb462d23fe1c49dba5f205cd474bcea999c19b5c2470485b1703ea3290efea59b413a881cb3c6e80bb6300bb1d4e670e2251efa883600b8312d7a23908faa9a3aec7d23fb73f7eaa0c3ce460a6ba814a1048a5e069f09cabd917d73fe656ec78922b6f2ec37e40f0cc9f9ddeaa85555ecfcd1e317c70a831a721c000a2a64358aedbab2a352f7a432a8ff084fcdd8c9ead8ac667b570ade21aae1b60acf9c428888530f5a14464395ae29817e309c6d40d2660e7ceb07622042925743203bfc54464c2593426850de5f288ce79c8e139777c2c38e5700fb7f0d53ca7290221eb162b4db7789b2994c49ef3e7320725393cde6f15a470a672aba575332216c9525263da28c4a24a83dd4f8a2c884ab63db6bd26d75d60aad37fd9906b3098441bae98ad4232ae9fd45e3c90ef594f6881473f2ffb3640b9d145eb010b9ebf55198b1a72264dc951f3d97f40e99e7619ee212126f7ffc05fffc70be8be858a865293efa697d4a681df3b80091f1ffa4771fae63e1fcf30c9dba5c5986610f357f3bbb7cfdccb67b3c54e5689cb5793230c53cf6106af8017830b21a661708bed697fd8913d465dbae4d3e62e460c4743720c23dcb66e50d3deaed26e9c305a03eaf8d743d50270ac6ee7967c7933b0e061afd1b7b99d50608c06ac83dffcb3ebc06bfa17f9e8f4e80517fd24bfc706b916c601f1c6c9d07aae6194f7097474905b36365c5a72ae8456e7800a4cd2ddd267edaf65d9c240523922f9ebd8b4fe636e6a74584fe0c0a12c7fe793423bccbeae818b83586f60fd16185066c63a98d9e4556c1381d1f9933af4f1036f965cb6c088e50d23ddc083b4f7f6139ff4f662f51e74762aca266343692d04b31d61eef50949a86d7840a1999df3e4d8d68541f4d17be2742ce7b93f4171a129fd9b80104aa353b0fe40d4814a5e6af813e7bd32788912df28295347c569088985f7c48b267a60b4f350696f03c1f11ed8167f1124a5d3d4729a5317927fdd179b0ec7a4e394aff609a0aaa14d45033867673712bdd0127fab2fad68d53a7d6f25af1290aa8e3d74ba7097f8df6f973df06ac48094db52115fcda333374b47963d24a371cc82ae2329801ffca4aad29326c6294762d4b6a459e85e55e7dacd14b983c974bb321cf3ec50241504a04c322fad289f81bc78c51022d3254743f7311ae1b8c68a74e2e96d50cc7cc6da886bf2405dd5b01349a5c76a4779580f1e7fc3624116053b7ea08a30b48294e8950961a3e0cbf676e85a4561977a0c04a3d4bd2a19152e7651723cd0f5cd57d6d8b7049936b2184274bf07b54d72fed5a4fb76e5ae1ea2a3ba82853097af67e91d87d7c7355e322f6c71b6af791b124218b12e07c7f4669704646b5044eb069717c8ac7ac859d57e99f6c15ac2ccf7c91ce7e5585cc277e58b015d906ff0795b160d1852fe71977a62edeb7354cbd85b5d51921b90b751721db4520094403cf4252e20b3872f08d68efafe97f5a9f33209cee753e0521b2c0ce8e3300fdc37f01d0ffc222ef5528426d3efa05bdd8baa174b254a13eaad1fb9af7520f8caf932e65687a25546bd5404725079244ed78af2bd15f24721b9fc1668cf2091fdb0ed0464e426472ffd1d5ca3ecb7beb01142a399d77f9a87d6c7b5c1e080f30510fad457d3c5bcaed5ed6d95f3d59eefa9459bfd5688efbe444b490ba25062d5bd4b8becbbc9816d6517cac7f6fc00e1558e2d8cfcc55695293a38849af0686335147fcfe9d4e3373ddb6a3ff5f205cd9d691d77ff1310a76f649be25550fb6e1a9d335fd3296e560bc70f2494c70adf006db442726677f4ec0cff17bb90f05809b21e81e578d945075ec36169ddcdacdf10d7b4bc22bcf1973f67db082095fec19bbb76ff2d3cb8d7ba050653ef0c2318df4078e6fea29c3ec25737fa20578984841db0b7edbe4c258865ffa6dbf8b29bd121b096bd22683c9f16785c00508b615f04902b95e7b0a75d9d09d2e6c5a1b11c45a60de6ad7b821fb7ea3c7018301fb374300212b96fb8f5be959a1ce48ea94f15e23a22d6475e3a2b3fa980ba5456a6b9407f727506ea0c863960b80b6a8e0d3888e4829b16f8e70766a680a4cf92a8bb73f22b8f87d0b269c254717f43b51966542350f02858fd73ef94e01d4b4cffd3e8258ebecabccae18879b51f491284f9593db20b1e62051b649e808320625a0f926867a894f05a484a05c35334e6fd074381474943b8651b5ae4e03652d6851a93222b5c166d66e17546f92bccbf0f7c3b1ac10431dcaaff52772074fed8278dfcfc38983485b959228eb3a7d11a035f2da1e6d52b0451e4b945002b22bda436e1c7e4f43c5bd36218051640a9f6e31a7e370d907d82a57d2442607fa70638549a7ef7c0f279e9154747ec13cd563edb8254d7608f4764b2642f50711494fe1bcd6ddeeae5f03b6374614681cca6995a61d601c5ef724d1cb4d2600fa30a001fb1ece62b15b50fd7ff54e21f92107836ee7bea91da930646eaeab00e6a9a335e1c1903e70f8b7659e44abbe3143ca1a71cead76e7c49e5f3966b30db753ca1864d08a92e1efb30390a40d0a0c64e4601c449667dbbfe2af0ca59d00d858641ea5c43e3bb8e23d9284d8c9b30ee6baa5559f301c9e9a6c855abd140a3ad7352beb7cb266748eca643e2ae206230438e64214e7d8b26d71f435f7ee0b375bde33af12b63695522dd142849c3cb58066d7a44194bacbc8fe63d2ca7902676c0700a872e116564b2a74d24b5280ddb5bcf456834b8d4a1a0e2d829e170d338862681584de17d08b53768a2856ca3d3c4f3f429f92da951a69147761cd0f5b7f0b7dc1d68c473bcd8d3839ae58866f7ebec4d09412721738a6a05bd2c4086948c6dcfd890a0e3535f571f320550b03437d81d4d5e2628dbc01194ac2320d4ea9ec3dfa6075dcb0d86656574984f16fd61b345248b5b44e441880388e040f4c0db69192a29842090564bbbb7d514723567d9cccd2018b8fccc3be527c3f0c41ae65466cc906a3279a1b208a9d1da24822facb10ebab58cbdb4c8d4e9bd20b1bea1a1d99a62bb70a844f15df35d88a5babbd0c677b25e25b952344883fd60cc7da9cb793447c174db513f6adabe3a9f41adcf497917d6fa4e53ab23bc4090e32aa1348d35728f27982ddc332c47daa7e54e0deacb734a1de2cfb21a54be905d95222ed5c95077153383e65dc47b354af017b3096514223f0383c96185c4e009a7929549a96fa979cff0c4ffa4774a698aef7d00c382b695591c3afb8cc4e081ab3bb3fb3f97a4532e94dfc3c0de63da24b75e453e99199723cf2ddd8e7060255096774f5d8ee9e7c2499bdbea9ab2d74722f13823a9684945174d113273900a36bad1b7ec0e75b203b255193e2239e81ab20d8eb666314368593c96b50b0021271790e6b4cc2a43bfdc7391bbfc7f6129d81512a7a7bdd3fc8592ddd07bc039834c0550c9fa8c6148fd553840f0c8559bcef5f193a3ae8e9b85675b030d20ab9656fccfc40865884f00dee4d39c7e8fd1cc0fc264928dc3e1be0ce2e78650eaf211f9bcc054d42f99f59b51fa695bf102643fcfe881e38435c6eebba4bcf0a600a951578d608053ad8321450f7987fb154d2fad542cbc8012218f6344f4a0907285e338e99fd44339e19180a1d9db4b7797edea57d854cf37c47999a037f0935798d1b2b80b9e0bd7ea6c0697a6b40bf6e7d9867d470257f8558840e7a94050018238f4d77643e10ad254e2181146c70cc571548bb9063f194482d3675e00806358916a4c7163425b1436ecb1088862e0781a85ec2d0c90168b248398ff9016ae7548d8bcea2dca8846d8025e12602c394c16e854717b726b089b958523d0c8953e690c3e0aeb2183d173ccbc25da8ca9e03cd1a1ca48538c48ed5a96d8909a74138723e39daa7421d6ca6560f50774c48109050116506ff81b3000b3d6f0ee7efcbf1afe6a93258915cf7f3cdf0549962ea253978df104266bb07a1ab0600b8c9899ddf8d03b0b4a0af7b4701e2e1aedaa4a0f88ab1bb1f028fef867a4702304c6d2a8fac07c723aff04a69249be5a01626fd441ef30c306d500688896a0e5550ba5f3886259018ead4a18b9925b5f5bb174a5ec13aed90949a22dc98e0091e7df44e3165376a151a8c5e5dd4fa44ce38dc8ca8e00e0668c8aa95d8f67107290e9c08ed4cc37d5f46decfb4fcdb165eb966decf54b75cbce926ec115dd80f603eb1a2705bad8341292a5895a4ab2e4db58fa6125cdd97f73fa925e099b002067a2f450536c1a091863c95152f32806607195d7b2fa1e4f88c2aed31eeda0b4d518e0eac11a19e3ec6645f316897459125ac0464121f774519f153bf5c99068f01f8f970e6b09b43362f2ed2d876930ed96ff55b040d897866c1d8bd079a036a9b935bd13f0798cc8b9c67295416b83c387b360cfdafb40946cb17c93d370b01e048304ed94707b7f17997d94f95a0a869dfde069a4bd0b32ff941e59b490d818ab4dcf6d15c181303c3056feea6b50b796d61e3562a3adc4d06ca4931d10a12606573d938ad9f1f2c7d64769a7f99aff721f49d2e2d80ad95d10e35461608fb1713ce5efafa2be0eb39b36328b3434315b9ea96cc44a5cf79c27493247a02cea97f7d7438f3cb27c23baa1b2ad9f60769e2cb7dd7415a51bb4ae93e55c508f1d053d8922508fa513f9a4330671245397b07f8b30d8d36eccb8e2d982754046b28addefb45694b2af0927fcc7b47e0fa22c79528d1405aa0b72a51ae2ec3017e2a5ddbd4b680a36783ff52ebcd7124090e0397f2be19a2a69027dd82f418092fecda7070090e71e23d7d9978a673f222479bb831a9ff1b60a5c66210927706255ae01690e6e816a790c21e7389c7952f218da1d8522ad81abf5abe8eae6104e708b5543c4de71ed0dcbb2596ac7087bf2b1840156e7ee0a81707d2779d5b0a7a92176036050443e017a96197ba03104d81d7b8b96e5ec54dc5c36c434515006a96dda5ae6adf4bc9f517b569b1807356d2e9bd8afcb6c5a263f3ef625e7b0377fbebe2513cf6230f01b7ab35baef12c1a55909660d76a002ea0200e8d74302c1182c4073a665ca8d1fa26d667b77a51bba38ca65caa49ee4835218f12ba905111779150255a4273ceafd57630c1afbc908d451a1f636e05b0ec4079ab841085d103bb2c9ea8467b4bc0c85333ee0cd494590dc8fe554147ffac2fb38bbfc061d260ca6f2141954dc1c19cf8fe5f0710d521fdc709d0e5d33cf5721e1cfee0d3628f063b0cb37ab769d3b4ceeb0d9678a4546009bbb69fcd248c34f4b2c070148a73590c7b19a7b9537e5282215d101963522c5cd3f0c3b62a456911ffd6601cfac13c314121e01f2d07705b2da624c0c4cf1d5302e8ebe0d65e68ac7bc420114ed87c868aa3ada432a4a85aa1d0ab786f2a2727e95e310b88f88702c00d004e27770283ec92fdc9d62ffb68148f385da1db6a49294c8a62efd6699ed25d40ed7df6cc2a68d08982c5034d2bc5fe1f0da7331f553e67e142b3d152ea78bb00914ec22b64712086b9767d77690fab730212e5d2e107cf2a3721968099bd2220a4e70ee6f5fed0b08703446822d683b04a628a660d34fbea270c8d1954e55db0cc431a3d72cddbf609cf2e43f84764615d0ec978932436ab2da1a8fc47d86a90c94d8e33b1d8399cb9aa9b6bae14cf8219a1b1ff2c79efd1cdd9de0e16968b0094dfcd9acc9b7935df3452aa3bfb38ba24739ab40b85e14b707e26e4333f5710c772f287a64292e3cd8f6471dd677da62541df778035b0c96ef2525bd57807d0908bf02a1911112047d56e0201bb3636e38a6266dbf7b5edf6a7942dbf6c8d30918dc1828db748584ef398e37659798a015927fbd8db47c1f1a2faa754e48d209c810f1898a4075439c69e77013f97293c497f7bcc50398e48823b3bb2dd6ff04b293560d6db6892450e8841c1d05c26515dc8e439de525b93e7755cb43adfd00d20cb824779ac0b0906b6ba6e423c91416b2d3d3b30b66ddab453ac469cc96034f5109b3c6467042956781615639be124b388720e81fcdb8dd6fed959e806900f8b305e7d33f5b993417b2b61e4d30e7edf51e7e417c137b2e3e8bed90d41e0328c0fc75d12da08d3e6c51d17f6754d2a5207ca42c72696b6dd6dcf6d2d4ee0b72c6dadc43f3f6d0cb59d6f63e4333a19ee74ef388d85c48dfe158affc0c7a053023002d24683b77bb7b7afa675ae1794d71af57eeaedc7c6dc88f158159460c7699d24c6f6420c5df2917d138952ac4cfc86e5daab59c167e65ad49e1d79b0ec470aab2c86bb148de5068d41dbfadcd222ce662c90ade048a182465362b3f0d06c9d7934ac191cbc5765ef058b84920fbc009101c302f3ee8ff140f2d064602427748487d5390d6e95918f5e1c0ca3d3cfd17642df84b9d99063115109fe5019c944486ee93fb7fc0d3f9e343d4c233c0421f2384f4c7480050e35682f70a0511be5216882d3f7958f9d5fbd3994685b0709d06bdaae9406945cf266331250055113346233892159be485adda0e880459780aff0357eb43e18656d0db5fab0d1162395d2acfa54a757b83c0ff4402c2fb7c6704bfbe7d0a55569b656448ac0bdd35305124d3ff2bd06eab01b463d8966dc9371b2eff77f597b7317be48a0402e1e7c8c4db309580e612ca2bff954ee211a84aa65c01ef001b2dc5fa38cf880fe49155954165ef330202e20c026fdf2e4dddc20cf3ff85d1efb3d68b39b5680e7696e07095750fdf6a97d9f1575b9ee93533c769797c89c28792183b0d59f2000560a4631b402ad8a0eaf167ac066d3822d520e372d4838b62d7a7666f76a307102a72fa9f931382b391ca34f47aed483b29b473156ec7c4ba6da8dcabcd700351ad6a4e95714f84f12c09c157bada710dc974874b7d257eb40b8d7a0b69d30acc10eeb94fc10a66e38fff6822a1e215dba3560189a482e3ad011f502c5b5803a2373716e226bb5b57ebd91cbd569d0abe416dcaa6fb23b659e0df4e4628f801af0b8e0b275070027f19eec7fb4cd6f1e1627a9c10f5b8af0af5609d5f9cee0937f06610ca0b4ba147be8c8d5f5dbd74e76976e5ae4e1ed6f585e6bb5c28b708da69db14542af1da1e54335c3219c9e4a018846293515a0f86849e36992f8401300526d83d862c4963b2b424a4e71efdef00422e07a7cce4c6c51cb1c0f2ff00ff1eea8e5a44f5a843b27a0c21134ae42a45c055c00fba1326a4284da441e50036eba0b624f5957e52e7ba5464839919287445503eaba04159fce85fc9b6cd0f1b846c46ac11eaee5bfa636afb4950aeb638049ab6916babdbea77531b3ddc01c54c47e3902247218c9352385436da2e55ccc5e543e3d50b7b3870cc4d379000f8a55a6e33661e84d531a9e3240589b7b2119b54b524e6912226ab0efbdd42008ca2373ed94c102da21c68db29cf823aab0a9c9bc580555c4944ba9f4eeac70b49e38c35ed41d17decb190d8668ae4ff37139945031f8b9c7d117fcdec558a09329058c511d59a8a4d0f930f4c1e8e620d728849129e5109bf0d794bdc8ef40b057000184fa50e6367bb4e19985a1059dc5125eaff21832bb9573cf4ead642018c297eee14c6b79ac638fc5a31f9f74441e5521d50dc50b89bc487cdf7400d021303ad48c36bac9057d1fe9037eb2e3d8f9959433154023c7635c7d2321a63059edcd62b9d8234fa0c4e527d58ebe63d8831064e27a346fec2a6b85d309f9800ab61efe39e7358bd3b138846d77ce5a84a5ebdbc339200321175121e2a462100514ce800b4d751e122eca2f9067dd1d828098fe436cab727c3f01b1a61fe04046d86db9b138d0960fab3abfa72dde4c2ffcc92457c8c2e80f284e9a6269aaa0e0d51b93ec6c613e73e8070a66602266f947bff7daeea05841225bbcefb81cf02b5f98ff9dcf8f1e46d1d061baa055379907605e2f7ed9a77f55f6c364fc5150d888bbe7cae8bb4cdc1ba5d550df6b20d7f74a2d4d8d96c0626b4a15953ac93054a9fec2720c6a0256d7bf1bad5796131717766b6365ed918e3c0c800f16fef015ce5fcb7560c2b5628eac294610bc6429815df0437276997c8aa9f5a424c85066848e26d82eb1a60342d4aa40660575edcccaaacb29b1a8718b79be62ca4fb06508a88c08a7fbfe5013f290e2a4fc2680bfb1acedb5de60ee00f29924510ab05222d317fdabdac4bd91fa106baf82838e5648ab0ec92dc78e366fca3ff2fc50cb8c2352a13868fffe66d4f378c90fc7fec8cb55b6300f03630da2e822b2db10c9fedb49baaf593fbc2fb2cbfe25c79514bf3ca240d950dd6997dd8accf4c5e0148e400d9efe8704e07e94da4e8ccd111ecd6b3d258cfd1af9b202482a2bfcb060aeb911c9d65c0b4ff2fba761f840852c1135bcdeee158fbd11205ba60b7070c3d10443f9b31582aba2d992451cbf048924df6c11b39ff64c8221f5292f9d4009a3aa20b8d8e971bff4e87911891c169ad53db595aa08fb7a0a013b9622ec7066800b87fc33db6f63708704b2660124c5097d34995d118030bbed7fcd8878303898834ee478b00a3317419e65018c272560141cec71a66b29fc8472cd063c20097515b2e95a4eb1daeaa1c0c549c6a52d3d94cfdb906eb1d94f5a4d9b8790802f8dc443837a8d61839118e6fd960b75ba8477df05e2750a9ff8ac45454a2a002ebecdb6eec926e8206a60f8efa5dfaa174cbe8cd6793575b7e11ab3e403185068a40242712dbca89bf00189787461536819f71ad4c7475a81dd6f40eb6cd250934b780d5f5d85c980b817496d23507e319d1834e2c9e7991d99b86a1ea627a03c02ff188ecdd04262ca8029691c4a4d76c1a79312d92177a5dc0936f8fc9550de278d3554ee8485835ae54878fefde958503c90a75d3e43e250df1b1ec2056036944cbc8d595c54c9f90de0733430a576b1f59542b2f1b94b859618b8357a7061173885e6bdc2194ac34aae58fcafeb7337cc74432f8bce0c928ff9f73c94b035edd976564866feabc98598e42bd8d16a579933218c4c3bcb205ca4c771f570154a65b8a7e8ba4ca6a63ee3cc6ddb6ca532b11f545763d827a1ae6297126a8068a2fb9457a460cd57c5046ed4529ee6370b51e2e5588dc745ee02a5ef5395a0bb983cbfa08d3ca004401088b1e36d45b971b56ba8c6a39ff7936337b9d249c02138ef9035da16bb3f518f22bda67fa6b4d14e1a6f30835e781824486de788606f031efff2d6529a35f08ab6e883bb85bd0395b18ec23d716aea66b5cb30b4a01c709cc11a874353aad6954aa0c0aed85cc245f834e1c9cbcb3af1ac1d243790baa2d77a66695cced0a9644c192d0396b622a8ebe947d85233d14938028c52f01adcab749d0c9a0d7b4a52f93c5f9b37ae7328a7dbb821c9c8dbb51c5ae96ad06dbdbacef6d338e2f9a141e66b1bb2e517b5c57818564ddfd071b6d92b678240b81eab3b7604ebfb9738440d88a098680abcbe35d15a827cd55a4e52d217b1004fb488a82131cc94f800ca57bcc14a0b89d0494644be7fdd482096bf783bff6032284c8cb2ac8681350f9afeec168e203e7f5b92eee77c959f2fdeb4bbb0d4207de93e3e2cbed4094e58022f66e7a6e1109d9effec5bbf41793acbdf4aadb8f0fdf689d3cdd3d85839ecada43beead0e350d7460f6d8cc132c0dc2add668419069c2bdb398851f5ad52409e5b2c519ff30c018c11623a43f1589e07047a9214019b3e207b8f5f0ae006e4e429dff9f9adb8b807e76149769ddd4e70e920569f08af207e9376ab3445fe481df90da0ab67f5c7b05dd6fe8d96f4e402bfa686a70aacbb51c9f26aad0a1dda18af8c15ce5e2f69b931ed171c8480d89322b249110b5c8eecaaaceb505bb410e9f9ac48fb07dc1eee3b76489145507d804b1e821f06ee4a01e052052b83f03774817a3c10f7975bacd5af567a777b7cc168f2773c0c05a77869f36242eb765b58df3117108d666ccce7c4153e7cc5206b3357bda40289ddbabbf6d6841182f41c01796e9d5774ac019aa5e91e4248598f263086170588c719ff7b78c2165096cdfb451b1c95c18f42beef251c813a762a645d0b7307475fc3259e6d8c6f7f82f3e6b057f723ed86f4057630cb697d92edd674829a08c87d63def2e333a08ea94057459915f038da463d920bcc184a49154b04d39e032114fe6f1d2686512eda90014c345100ccf18de069fadb93bf352b8cb4bc2e0bcf9c52b067527a84c90f7056092abc97d208755934d1836a95270f5df568350428b184db3bd72fa2b7b06fffe86511e846ea3aadf2269f13abaab1f40966c3058cfb5923bf4e000ca8ace802794a3411e7fce9b8431234a38cdfc48304a3780d3e632f3246788fd1e4e6418b0a1da48b4e81387b8777f7ddf3d60852eabd790ef5c3e8c321fb48c9985d91745273db3a6d2906fd0e3c07a0f1c3f662c23bd4002d7591ee11877035b230dd251d18d314adcfe95a714b46c8693f0378439f40016b865157f741ce674aba028578b92ca8bc53e41b6dd21829a3b86f2a6a78e20466fdcf39a86b22e2450422f44aacfb1eae1edc863c2da65374c7f71c9cd83a065f6edf6f8b398d0b53d995b711bf97b48caf3056a318f929cfc2ccfa8044ef0f51c252e8bef8547ea759eb8e025b524be4a374e0fff013123ec1b00bf70fe0015a043bf41163ca7b3ee5dcaf0f1bd7bc54ab601710b8caa3b8e3022fe2c7ac058e3966b3dda5894d7de335153e9e63ed0e105d71d533cc58a4df6c95ee974d0fa9dbe4da3e9968c1bdee8b79a2346a7a5b90818c52dc773dfdec62eb1c3ea50b00909a2ab1468df10ced905122174c0411a6bfc3717bc90196ed2f83ca122f06fcc7b9abdcaab9cdcfa67b09842646cb465c2dba57077face6d9e0f299e7c6029a771e3e1706d1f4ece5e97ef8a37cfd63b06fb7fb04e0edf6086f6842d8ee0f782293490310d820407473d85a7bb59ffc9823c157363b589b74b44fca67b80b8ef636e99f4b52f6173b28602e30c930ba9fe82aa6ef07461ef726df2fa09103b6e3c3332cbc8a9ad4dacce44f489be1fe51ade8553dd89470e8dad0f985d603d76fea87d43641807507f783ae1d0b9611fb741968ad0912359c14c54cf7440aae2e51260460141f3838a417c47de785cad8b7206b65a939fd1d8afb289d380eb43c5ee7c2172e1d0136c3120f243a8b029da9a9181c3b81e2b225fdeb9c4e018625797b0175cbe1afa45e99121e22fc0b69cf625677403073998a9c8c53a50cfb6f57761c86fbe78d133141aff6926a2baca4e93062f747fa6738f7060b81083b9e81b93dcbed6ad2317f9a65d1c46051cc0389b98b60f9c7c2c92b5149d20be1a51df72849b1596076881dccb4a477c22567707f753017b6ddfc3072471f020255b02929427ad7156dd5e8cd5d056aea55ee66926b638cd25ef6248ef5f80769f3cf2db715711e0a2dea5d95bfd243f1311bb9a82d52aa77c1271b02ea5f09f07e181a5d138062d8630f0278d33c5f7d40e8c2bc2957e40aa7e0ee312689055d4ccfd7378966e72ba8b8578d9b810e953463b656a0c829b3c3581141c36205d545637ca2ce46ad258ce528cc14335028da338fdd5c7d6a83da364dd6021b007505ebe88de5cd2127234593d8f9e76900d8be2514cdd0f07768fe9ae460e300528237c51eaf6034177ab6e4a2efe9b7b95f7da4c8744aa83c7b25054e0751009db2e4c488c6aa2e1558d3124d9a05609d97ca33617b1489b7d6bcfd09e945027411d62abc79f04afe2656838ce64dffe8f20f348939b3d58a0b69c49b3f5801323ecbc6f046d0c49edb117fa5505bc7585533b62a97372aaa7c66cbdc63d005fb70899c534154f2f5d7ea909d2cdab252eb4311788989463c921196391d4306850aa7f488dc759b6ed069fcb4e38aed7feb201c6fe4828f260e6693d398390e7168122f1c47bf5311260f3384dc8b13e7b15f737c565b860766baa33e2e09035fb69dff1cf303fbc201b0e426b5bb679abbfe92e55e82ec6644ddb656eea60149b9a985f533a1fffb19fc515d64f90769db8476a6bca6088842df5fee482900890636a0c46866938171b90ab4f372025f9b1e91fb2bc69011e378d8c347df0a18c675f973e148fa87433e59725df1d16a38ce50b33c7555219592bef871d408c1deefaa325d8eb5f52ef31c90a5bf3ff759ae034ef005c03c52fec511f9640452ab71511ada4967ca2a77c2521cf97c8650e7fd390a4d59522863ced29f9e040af27e7d263dd3ff84ed9df454a9af4ebf9d41b50c4718c401a179510ad64b0b2445de81096d9933555ef996ba565affe911895be98fa7acbf2a11cc30314e081de4b26403f010dcee95e4a78acdd51366c0e693bc73c33528d0a3bd7002850a -generate_ring_signature 590a993f96a147077c4f5cc32f91a7c802e654a5c8586e9598d0b6e4fde6f48c cb9194bc59756cdca58afcda4960aefc51304bc4f6ed04c6c2440488f42b6a40 2 87fde03c5506336f088a68e7bdbaca0f4f0e8a9bc143808305a7047194242664 8b7ef8a586c5feec0dbb2e72d8036764ff5da197557bf179192e31aad1d5beff ec722447735f098024e362a928af39663703309bdb68b3a5e7e42ef7601c720d 0 2fafaa16c3f6a89441a79a8c7f68d08e68aa192ed0f5e2e82aa9579ecbe21d097e1219fb5a16c62baec4f08b976539fde2cf974037517a470dce811619d2990b175d0cf9b323b4e9e49be5a8b61ee99e26e5d43bc48744ce9202904cedc5aa0869cd7281bf84478b46b252591c02035c239f5ee2bdb93c73cfcc4f9f75dc7d00 -generate_ring_signature 0f36247a48d15d7ef13a065c7b1a1ee40149510d7297677108020d7b2a36375f b0226919b26683f35354c80532460ae47ac0c70b2f18a7155f9582904058976a 1 24c4f1e4a4d51bab40ff04be8bed03b4337518300c1a0073bd59011b3a0fdbc2 3dba1bb1ca997825b955163be13e92cb7f823c13db9de96570a1b539c4c1f607 0 139cf18166aad0e61f880263059800a974d424569ce604bc2cd634af742caa0d09fdbdbde93a3faaf1e1ca07504520f6c317b54bd812ce94c5f5c45bce82ea0b -generate_ring_signature a1831c2903f8c324b1693adecbe806b81454c15fef50af931c62204b3e135bc1 261a8904b68f5185ce1657f266f83cbbb7cda81229e313d568a15cc55cc86521 40 9f6fcf297cc2dee2ae973a51eeaa6e940388bbb75065504f449cca9b25267d00 98e264b0c531599a8a579074b2d927f85e0c8b62c437f6372267a275c4e10169 42549d197b01ea3544e172e32b102414187e707f3bcabb6b175a48e280ffbdad 5cbc67dad405316a18ff8863ff2bc32fbe25e08f8c291e790733e39e83c42947 dfab77a0540c2ae33cc85efc014987cdc19cc435463a61152c70336fbaae0715 3cfd622fd739f3eb972eaac07aebf78208c6558bfad030afe4b6b7af38ac5581 2625cf12b9544cbbb8cfb6c43d176cc0722487328d7ea20a7ea435c7beeaa4d7 aaf3c52ed69792553ce48eb77242d350a8cd10ca9cfef151c4634a284338c15c abfb16ed19fe8aba99218c2734a87c62966e7450f66592a47199615d8f08073f bddb2d703ab7e9b711ae268e900ce8ffc7ea314ba16f69a839c8784a22dc1cde a4a71e5b7225ca67a6ca194f5f650cee3ab4aad608f27e05c2a72d5ab1417399 397f57af3d7105156e93c47c7e75205ce4b199d3d668e701414f62bed9f30a35 aa89fc94e7ab1e9c47e2be820e4b852e2841333375c7181534c1d9f6338b2b22 12574a1031709e503b7b55dca440095b5ae32e7e49960df3f13af0e3545856da 2cd8ad3c4c1479a755671fc233c1ca15da6d1319d58ee7fb46634cf5e6d2c2b3 f72d700961cde8dd195b2bd34b8b33c27cd38ead4ff52cd65f32597d9f845c25 dc50f2aeb860353b9c32c9163d2f371cc9b1966ea4099b783db95615d1b347db 02f9a85d776b326a14e3c116f3e34706f831e81ccfdc4323500d28e525c22fde e46577f6631c36676b6cc24d154902c03d8cdc427b8eaa9a16bdf1600dbacc5c f61fb173a89280c2c853240f345f1f4acba37c73fab295c6153d5b0c84ff5f29 78d202b46b95461b1f0850005668ddc15e38757853e48bbe6f531deeffb3c5b7 b4fbd260b5c4d963f1f1467eaeebdf072c22141d53d5b87d4b8b98a162977021 496e1bc0694a985482d5e4deb35a8f5b048d3f6af8d0d12a8c7e8d1425c16cec 5ac968233f0006c4849db8c0134ff928fc34212a6955d2fbe6c4741f29ddd1a1 4d29eb966788a97031479b226bbc1fa8b8124ecda89c039add1f48095bbbc2ef 3c721246302983cfc7fc5505fe05ba883e54bb4b50e87eebe01a44322e356cba 634e670cd777a9df99da4be3ab467801b4d87a42d93e057fa798e08fb239c91e 7f972510a0eefdc9ceb8ad6bc4051d8743642c8899ad90f81882a972042aa120 431c662db985b69fdb1492a243237c0b6575a137065ffd665be0df48773453cd f0c20daf7e7d96d1c1282bfc919af7cee54795c544bac82adc854040ce5bb142 b7429f8a8bda10eb7e6993550f7e967b69a4ae04bcf1be65bdb252f0bde3f097 ed293f21c6a1269a56ca8335e5c492d520fbde3f70b877f22a6a780eb1109bea 0a6b31f4ca18c8cd4e1b6860664e445c32a314b3cfab312f7485b35701b8d033 ede2afc010140fb9b7e89924c1a3696d1883d3255da5aecb64f64ba3d471a90e 1dba5ccec546590f8a9ea6e91170f0d0c505ddfc6793b150feaf64637ab6dc2e 9398df9e62ad755ce37bf7d03b555316e7a3a54714031fc3630e997e8ca226c5 39cd3807d39ae3de4b43789511593e57e3ca4f44d3ab87de4dc50fe8c79fbf79 678358781b39ff78d276740e2c8c2cc482c5bad694edaa12b194bbe9dba7f78d f965175ebd6af2efde584f6bec3180fe5da01682603e7b9a54a610da393fda68 793abfdb3fffc1c2669b18d68e3239c0662766c5745a0f715fe9af5530fa6d2e 07a99a07a14dcffe0a3dbd5e14332410dbb5a035cce4a0171ed6540eb09b1505 2 6d1aa8db3b7b1476c006d067397214948dd45ec887a2a82efc18efa9c51f3000ede00fa7442a064a8a544c9641be8406f95b7fd683b42b8c43c0f0290e2bd10bd97a12bdaef77c8b6c01a4e66c476cb8a2bf105a9f9a5f130cffd26fe3281d04cbdc94f7702dbb7dfb6973bd7d5e22ca03b06fe8d4cf87a52c40a7ece57aeb0bdc82e8ebf1174220e93cebbf2b234f1d7ec581f24b43b4af842d0cd129edfe02e9d9e76b81467f54c50e0ad2e2464273152e615209739d5292ae37403f9cbf06a046546085d1dd5c1e9f32a6db0aa7b61d31b4f89ea8c8ce50f45aec2b3f0001ccec53c5f80a78402d0b24bd1219e421ee27eac73094b5657eae1b41faa0f50b5fdb315d87510144c3cc8f6d8aadc8b14af4cccac7410eb1ce29d8d2578e610a594e3351c3cba215279b4994517699d854954552338c9ff977458d67265c5308fb5e1e43c1d3c4a32ada3f61ec32fd42d786d639fbb2ce6d52e5ac0fb1bfb3091755e9ed99a7c5ce6aa5c0390746338c04b321675ef32a359a2e688af21e61002a68b4c6e35000fc2bb97cf37d3c2f5930488aaa552e48ebef2b110ad43e9404c1d0ee92e19e29a49c93645c7592bb803f8e79478437a7875cf4a1449dcfeb0550eb490fa0d8562e39f1168b5d30070a7266258e1d5bbf05a6d2bb98ce241705aa237aa2c91df4871b3c174311562c9389c1e70c5af7b5ab84856f5b8f75370d121bd23a26311954d57e2c822167a1342b00a430037347ead4f888c13551fb05750ad803b111b29fcaeb8d6d38338cecb2c59724507079084d5c68943167fa0241e729b2f533f4b17e6fa9907772f2df5e6c9f43e0736adf7d8e7aeb53ca780301f900c5a592536ab8fa72c402302e251491ef67bbbea82402d8451fecb6510b752fe9d365332a7607e20c420dcdad42b149204a434ed4dedbdf332410b47e04cb90929186263683a2aff116780fbd5eba6188b1f90998d254e69ffc51ade10760061687b3626c9c2c96a36801b7add857ab1638079778e44302e4c49c677807199af7ef095a5fce44abdda140a8e5eb245a4fd338327a21b6675b13b8e51a0b815ca45effccb4d4c13ee40cf40c507dd35a9f9eb77989c3703a16308c04f50a28d549d895201b5a8c774895b481740009af31b800893d3d02611335461149043255da90418f4c913cf115c3157e1c5e9cddf9a9616f57ce8a622926beaaa306207f217050e36b8f2039743ea7af85816c90fed6320c86f687472899761c460ef74d7777574fca6d707a0690125299c5d6df427adf9905c15d876bf1061a0004cd823efd1425b1bd311a334992271b8bcdcad7f37cb49aabf10208705f7f6209383ceed51b03d4a4f7dd6cec6224cc3dc7db2ac5004185d34c00d8a0ae3b4903fdd7f0d1161505af90dcffab5796591a39df54820b8f954201be911386273d0730067b16e1d17e97b3a8a6116308bfa07e01069a4240ea8142ca0c47cc857403ebe73a3732532533f437522056fcf23332b0a311a3aacd2c8be3d6091cb09a08f05bcee24da7eb3cfb4f972c87ac9079fcd083567bc4c3ba41c03107ba2eb2032028ef37d1013717634d3026bcd4e7798543438b632fcca3e9fe3533fa9fa4016a3fb1af759632292a4850f486a8ebab6952b3a8f7b7dfcdcbd0f9e3db376d0d26648d5a71ebe6b2dc059a295a44dbb16ba05b9b9396d4347b8ba6e2b110130aaf76c60477afbab330e1e973dac478cb0735a6f8815c686ba147d5a7ecbb450e1b92b156f3a669cce836322d25df7f5b5b34a7b8bc8e8dc81edf211b29760a05f1afd7442b29d5bd2546657b99790e2c887383724d9eaa4abe62723f5a20c70efae6c02c0d8c06cdd633e146052bbef5594c3f0de3158c905f12074956e04d0f343747525a5f19dc05824400104860f01748542dd1cfb6163e03b00fbe58040c6738d23aa00cc6a6fac93952f2c84d89ff4b470f37dfeb97efeaffac8b57ec063af999b982ac5fa421addd9967b8ee660c99fd528fc6d89a13a428dba6c5ec0dad2f08b11df66ce6f1f4b5996941f9558501c0fddceb7e284991370186350e0d3a26eec4945208f67f62ae8996802c7d0c841c45c4d3e2eb48a7c3aa2eba0804159a6753dca74653c99e256c1353a921979a3d58a0c15bf442ba8bf763b40f085314a5986620a0324414b891d80674b480caf6b08b21e9266e1109509057b508692f00d0fc38bbf9abd3205047250d809f14bb1b04e354078e60ac1d5a190d006d7dcb364558010103a215c826c05aac97c48c6a943d70eb98739f7e16ad5303e3a5c5bf12eeb0cfb474f3f73a666509a49c008f764ffa0362bf3019bede600408ddf831861f3731777c1a0698093a95576f7ca3e9ff0c8a1f314bbd23192606dd97f17bcbb26db889f83238c5a7eee581f3a6f3041dbd4c9bd656b58d8a0e03cc13b5059612b4a828457c10ed73c7e4a8bbd86e9671f2a123ab44eaf3900f078f68d24652651b2eee2aa975a90af754fd6889a6e9665fa35248589e83a4d0061a92bbe1ab646bfc157cf26fdb7dd59196ad20b2c651ac6b0b395d8a9630310bfb9c780b8cd0a6eca6bef5466180f6979405afe46ab1fdb54ed0c99adc9c270ea08f77aafe34d062d15411c99a8a64d01e5cdfc2d2a2f552350193440104e00b07867d7b9770d1d2b43c7878a67edc965734e7ea7b9917d8d4555e9fd1bcd305032b0c714293d006bb1f3a91a462af261e390a6231499da64a7da74029ccb40e2429f67c8cb2035bfd38ef42a54db3a3155d9acfc120bea2c180ea9c365d0507ee643a763dd1a42599c528964b732babd0adfc4a6c0c4f8d51eee98f57375605b8c92b363b34bea0949f8bbc534f351b8e67fd0945e26ae92858dd6982eb190e8afc3798635f5404eadb091c0fbf7a876fd45e476b0f1afa7b8eb2078fce4f0e194300cda2162d58ca6d6cf7ef2280915bb7e38aeecc38d11b9309c3c741e20d9c4246ed434d8439a1580b0d0ff714dc11288831ff3ef498b15efaeb3de6c408c7e81504ace512e24a09519fe0b9b0f0db9d38eca94a286835072368f4a93f0f47db975bf98846312d58aaac5046346c79f8ad703820d2723f76824b160bdb006f1a3965d99667b17a659af4c11b4f0aa6df6f6c9893bc3f8fcc8e2a947f5100339defec60a38d638d5c1957b026936610b864962edce960fed8ea1c161a3101a2641db3d45396e0a4cdb77a90e1fd7e50eabe7acb24dfd0d1b88fd32bba7b042015c9e32358cd8d309cea14133bb6335633c076d15903d4d522630d24e788028f11d19201465b8495310c027de2fb53acf6780f65d507dbc2262039d78f780da6fbd0350d447b1e046aecda2226a02a2557a487a19f1cc0c267e3d5ea51d7033da7cbca012d98b8b9893dc2333a2c695322fb802837aa1a478ef5d5670cb3093296ae9ecc041ef0b2ff08d0b20a543aea00e66ae92c827bbc0240a59591750751fdc3405d462bac5f2e52a423bed1af6736346a8fb54ecfc047b510e08a70057138b6b0d6e5bbdc51b6a7c0c6b2389bedda4edf0fcb403a347b3c7c412aba0744fad9ac2f8c233e31884bd5746bcb00af04c9d54d54bce1bf2e1a003f6e1b08 -generate_ring_signature b9ab0b10afa5860d279a36ca738754958805e5bd3290604096442b8a4dcf9730 425b3eda70237941990ea68b9889230fb9badbbccf08a74b7c4a59ce552adb78 1 a15898bb31142d00cea651b265aa4ee7e73189b383c616eb0f6e37ed23ef7eff 84c6db89df73d478f2725b0ce36218d81fa68d1cd5ceaa8a1f8f2f10892c3005 0 b38f61811771b4fdb6ae6fc3073d599116b31fea917a12bd8e1d5efd0da2e406ae986646c96c6e8ebc064dd073197c3d757492a92c6e356a3b441fd1a747230f -generate_ring_signature 77f35661d7757d7d03c765f11350f4df7bd28708a37163e7520bd8c8fc622503 2b129b323db1ce4fdb915ebf2ddd4be3ac0798b4f9fc3e7188846d9993764fd8 10 ab241334d6e35fd6340511d9c72d5a18159c125336c76cc06eeda30ab23ca126 80832676389cb95756b34c8df992e88936b79d89114dfcc8da1e6eccf6320239 c5316c4465366ab16e1922a835d9a8b2f2ac89aa43e2f6c254c703b7cdc88519 74ea52737c3db895c2b38faaa7d1b47496998d4defde5cc72d070a15d622908d b1d7742def18ea0edf1a0bb3931b3777d85902d969b9a2a4ac30d997773de9b5 8f6ed97932d63ac5c74dcd60385536f4d3905e5697e6c27ee1fbe15edb0bf7e8 be4a83371f1d524c26abdfa898e92049d4f3471acbd5fe9b03cdce08d7fb3f4d 3d1d9750e65c8d279fde02c4c08a86c781880d3747b0d1a9ee063bd978c0921a e9d2250439b623b02e4915ba6f3eceb0a74bb7af6bd8ca70ba49e477f69f5cbe 09aef8291d5f369067a350126fb37112cd0550cf6534ce110100066ba0a8a7e6 7d6228970998328079db9ffb11b5340bd972a39cb5fd090f400fc2f4d9261008 4 4a746b3e0e65c0eda373022eeb59b7eb444591cdfb65f8e34927588c8f4c2d0ca30f8a380d0dcab2467b6494862eb03aecdec5e1cb89740b28e6fcf85b7bda0f0ff923f3d399f64b7a1b08b17fd73d9b163f7370acb5f707ce4dfa33d02a9602de10cd7c5c75a85971b51aa3bca35353e335a62da4bbd1da107860ac60c68d0a9ff7aa166d68ae2c40abf916eb1016675ae292cf9b13c4e4804b904b5853ef0749bad3bee2845ef151010f2a751459534c094bc79b57c9f3571a51bf2140ca05bb721cc5b46edf826be10c3177f11aa4f93c6509da64a281a5cf4371629b100fcf8387fd520a510ab59921f9e6832e8fad969f0b64e5b5bfb0cae5c8f966190fc61ce0e9ea6693667143d2e3c07c81833c32212386f4bc29fa470db389a15d092863a1a8706258e19414d34f0c2b169880e3420ffe3fb7c486501b88a12f130bf381c73325325ed8f0beb6c7be95b9056b7c6d92b4ad6afa4781a454e031680cce9cd6acc635e4effc31d4ea9f297fb77bb7c6b9cfae906b1046a17b8cac1e08f3288eb1771b29653e07901ec3d7f16bb6ea3f3ccedc3fa1c7b07ce1a686a8062ea29b435700eefc01c22048700d460b2423c7a2897d9e3802f371f557007306e7e09451a831c7cff801b1d8ef9b2ef7042859f71533534946c32e0bcf57f00bbfefeb62d4b0ab3b1f534ccb7c3d632c823838025a3822b8ce299adeed4f0f0b4e05fe38a0d39e5a69fa2b3fe9341e468a4fcdfb83bcc752949eac010777a2061ba65fdc8a08be1e26b9ca14c1406a00c7e23170645f37893c8c8b287024b60aa665c676099056ab7562d43ccdde0c3e4fbdbf8046ca65f366d51781a37a5c0d9ac4faf147b751b2d7be538f453f6818118b65c69a56ef32038658fb935c4d06 -generate_ring_signature fe0e906842bbf5445baa4ecabe98dd6ef27226ecc2b5ad9834e08c046d0f578a 0e4320a4732e89ca8a53cea98b44492a77168316f9e8ce95fbdb5197e9d14d62 30 b7258c82c9b3e0adfadc33a81ca09cd307e70ba065c16144f46f552414a9a629 54d94e93bfa71f1ea7c1d64902bd8df7602d7687e43b983ec3ff6ab88b4765f4 637d164f669b1111c41a5533ec52a4b05a515ee5f46f74ab8e50cc481157edc7 66fe4748dc0ea965eee04b152c0dcbe2a5fea11333951d568491103296730f02 acb9c2c966da9415c9b09bdce3492e7962d10ac1c2a5e46938a6651e09fb5516 c2d7dfd2f498249427aa0eea3b192f4802a3d212f7fda58c6961e231ec0ddad8 c830fcb8aa75343dd3722955964dcdb9864c7922b872c1a209f477e923944379 bb23e3cba803bb4750739171050a55f31d479ec0ba27f4c0fd5e8a207257fd29 a6afa3958567c006ac5a61fe4d40b5e34aa0f796aea13c2ed3866a1f12fb3e0a ad6765229bf2c6c88971c254e49acbe6cfe0310049673e1cdf346cf09da3f56d 0edae6331033f401c42e320be461975a86552f196f495b3a4e3b0f547617e963 a920e55b3c7f3a4a8653d75ed1cc731e31ac880e175959f6e267fcfcecd22955 b4d509383bdcfd434c407c5070f125c46a46733da20a3f0fe9979df94c31d5bc 25daaf9f93a6284f702e02395babcc7598031c6940e7914a93a8e9ec722e7fc1 2f1c7e264b1262b445f4178156119b3fecc1f458f7044cc4de63304e14c97587 a487d33950e0d251e92691a34a583e13aee472b4c841843ba5fec5b3c7656ccd 48971038b01b0fbb7ed269349420385f1231bca51ce21913572ddb33162c9619 f222a85417740af8ea34ac66322a4f494ca309d7722ffca6713400e4be4fef81 696f40d88004d1a539495a61f35483afbde4fb225483f1bbf7c2f82c7e59dfde 9a3b1cf51901567f6e504c0c8b08a503b8cd633d6f542c9020cc4d238e6c00f5 3fc2d5cd18e5788a96f016cbff76554765afc0428cb260219b81dcead3edc978 ab62d587fb8948401a9a3cf874a024457791d3f2532803e39e00b7e522cac759 3dedef9bd6efc320fa1bc5dbf0b0313244efaec9a7fedc18667bed47a1e97af7 b0342dc39ce8e3f1b5053747f6fd3a450319d7568779dbad042e76b31dc816f3 8b3365279c15fbb8c7d2758fd0de17503c48d87aa9802593c22ad59a27d845d4 4566ca51354a7da4bcaf7f457bf803c6e32ee109a939a69c342ad61534831c44 da8e0ff478e7c6b7fe83467d5a5cf90fc769db0d8230eeab2e79424c262229a3 2cea0bf3ddc9e0ad18757bd6efc37a6e5923dbbb76d22a06463f25177223f3cd ebdecc636abbbd1fa2b166bbfd742ade21ad6a22dfc49fd667cc807b8f51db2e 5293d8afca91f70430a90bb0d05bcc4e6062b20578ff2cae721c767b6f929fd8 33c6805f3997d25bc420affd33636816459bc9b8cbc2f2c65fe2ebca22879b0d 8 0370517062894a5dd372787767a097b79e05e3bc8735aa6788db1e93c605010616b76298c6037f1fadffa004f28430f6f91b29529cfa9c14435860abb19d2804745a877e1cd673fadaeb67f5301a96f772e13782b20a6dac8257592b92813708787552dcbbb00493996367e3fb9f81b43613b781f95ebd4fb9a43fa914778f02557f566b436023a0e064aaabd80ef64eed06176628e5cea5f113e536dd1b11078225641da701eca671b73fe882161f9499624e345ad1c9b4e4fd03d0c9c8040bd786f89c2318d4ca25bc7882e06ef13ca5f3aeb1be04913914e3f48b99032f07bf2b15903bbe325fa2b9738ed2d52e366a6615a9ab9508c4db59a8bbc2cc1f0a2a149480b39bda7a60836120ed2c623ef22ea9b52aacad8a63f21706e93a9a0fbe0b28307583a987821b7ed8a5aa8d648b37e0e92644aed2a029e4b03137c2081a31519296d26ff70fe820639fdab592d1dc77d37c0037e5856c44bd34e15c00654f98122033703d81cd726fb9c613f96589b907472a4855635a69f3b04b210e7b4bfc09b90de3b3440a33e05a0ab2f1b2008f296bab7b82d9bbe67b353ff007aaa2ac4508ac0e3b07d1f222af52842ca0a2327821d2cdc31b6fa96bb7df78026f0ec670f10136442a4505d6b3e51f51881b8e22e2665b1da17b6b111dcc900a10b1347fab42abc014332bc103b437f866d017af80a77dabf72b817ec5e0b30c652e8a0875e2fd5e37fa02c3ad513340085ad6258776b7dca34e3dfe5aaa05034fe34731cf04a513c1f0992c7e29a05f04a4d712108cd1d02743c0c3a3960b0e4ed0cd2360688b8dcb868d7af2bf24d54324177d302c1b8041713e8161f4520fc952abb2ee09e70d9d98e71368fab047dd10357170af433a482eefd8b0e08500db02504f197ce42573cbc892e05c86bdf193162154aed8d9e752b57a4483290ce55f2f13a3a3ad922c61947215429e49508dd3893b3d43f2be23674e0ac01101b8e045b1d372a5485131abcf8154a77a47796a34aed37b33f164099fdf296d033270d766dcdcecf38ffffe82045bc0a05862e7a95ff50a0d60d873cdedb92905382f27a44c9f1f0ce1b2ead533ef1fd399405ba125101b12c3f32674ccaa9c0c794a3d03de77e71f49d706d3835d02f863966e6cf1ee553afd2617dfea07dc0b57830a51934fcb42cc35728b88bea0acc97a0d69771d810d82e944f164280a0a3aa43f3518a37faa2cd3376a75e545a96f891720fd34590ceb73fa8a1d6d070f5b926b12ff5178bd165796946aa4f18a101d0ecb96cf70073641db94e6debc03419a75b85e09872931d383a4607313a5e1ec2965e3f4cd4f537862919a16e2011f06582b5cc17d02f3ad744a1ecc05cbd9f71eb764f01c3b82947ce424b8a80fa1d4846858a42c2a197559d1976c0cfdd4858127d7c3c9594553a80b3fffa207824adf4383ec38f21846c1fd8cf302b383afe1c5ca135de453e9ef3221458b01bf50d7dad6aafdd263027da59e0d13154777e3fee83846c43f2ac5f99622300f706aac8a15270104a1b33873364c706334762d1095bf1b0335e527ad3d2e550a7465fe93e6bd75fccdfd39ffbe5262953a5fb985f0200714917e823972e64e0e8de1ca5c9f2a23ac77dc6e91482637bf5e350fc5a65375359c3f07327dfca505180ae46983ad8dfcef9ee5175cedad5e9db59560a13e21ff6153ba7ab0222c0e11c3346cce137272a435f15c05ad214a1196fc7bc419a3396d09fd42fcf63e016f365122540a77cc4d45221d7b2e13a0c9ffd8c27216cc0074786f03e9f42805adab651674066ffe25a0c8c7164e56184034fd2da93fe76f80429d2bf05e1e05aa8584dbbdf6b6af2b26782e586972a7d3636c1b4a02eace5368aa91bbeb120421ed19d182db71aeb26709e098fc1690eef495f841f5bfc4ad0a50602e24d407dead7e53ab81074af13e10e06a2cd4a5b0ebfcd98101e5900fff2a0d454c1a0e0c1fa930eb04a8e51ad96e00d9cc8f84a592e4cba4ec9ab3abcdf01aca33240e03170b72456142da8cbbe0129a72ac562fcfda9edf2552602ef2eb5009fff20c3e29dbb78d96d2d13cf0f5ee1906cca006644943c05870e58524a82c0c2b660bf5ec3a2c4f5020d15f41e001194e09e713a17d221ac7b4328b25de1a1c56c00427939918c41c2c904e34960dffb9f86edbc40175145ce055702b7650c98c800ba8f097b11947dc4f1f28196ddace5d3bd32b5e1e6f806ad24b82a1fe66ccfc0d98dcc042b94af7bbe0cb0290299c73bf0b1c03aa15e8e78227d7943ba2337c0ab03ef8d5c0a21ea4c462575df4b1ed64ddb898caec1958a482cf0018b8e8060792687cf12ae43350556dc4f0e4bb5ec359c8196ee43c55f4fe87299556736d0ff19728a6f7e768e6c04c0c72acc0d3074d607446213265724d0ce5b0bc23d3058be5655542b2a657e3bc7daa99dcb76754fd3771ee3131ec122e1f716eab5b0707d3ef6d9d33cf084b9178f5759bd344df803569818791b34f87e878832d270b7c65476244ede557b824ed54304ebf4e855b0d98d9826f3aec948e221ac99008707b785344931c583e7ac70a3bac2ff44f779fe18a9aa184e9f2fb2d2321030e185aaa7432bc23cd32e1f64e68ee4c96082dac27685bc568b9c7deb41fa0050f79db2e284dd2390ebe75be42e710432a837c02cf5f59647614ea27fcdca00805 -generate_ring_signature d44366265cc5e6e8a13f3d2f55c79baae89befbc5ac99d063e6e0dec1d5f8665 345925032111e3150664a24dc32937232ef1bb457d164c57c24ac1160fb7d6bf 1 0875f733705d14f8dc398a529ca0bb633914572d8849d084dde057b82a946fa3 f51168e99b11a987b49c956969326d5ab4bdcaa1313fa7ad146b24932208d607 0 554b462d1c629572a98bc59f8a625c9de19f8c112498a550b078ebd6d8c5640088b85a8baf2e9d0bda59cdd44055f3aeb7b7740674b119ceda5f04c537241c01 -generate_ring_signature 9b43013f26543ae86b8194bef3e6999d7a787ac7d9e69375e117de5f6a963a83 ff20e8a898dfc4e8c3ee2864418a757fc9c52a5a6941bab0c745d23846a4a0f6 5 551ababef74f99377fd5d68b2ad3a2bfe14ec6b4afee4c39686508184f87cc8e 25d84441eef268e987eb34f24749b4a13044e341e6399a5cd6b155a87e6ff0b6 c3850ee99faa03c06b6574ec52d2916e32b399aa82286fabf4936b39a916e431 42dd11fe00b24c64342341eeb6643d229ad1673a51f09192b3f9ab4b93256cbc af4d559997026bb67abd8ee6e469dd35577dd529ced3a657726d0c02edc3d5d6 b34b5b99dbb26140e778ba6fb61449721e4c7c8c1bda6e70eddbb6c0672e9e0a 3 3290b43604813da826ca2137398f338522af64bcffd8a026b5807705be6a2607d1516aa150a3299bdddfec5109923543724ede193c9382b3a002d35f39a8ab0d31e21594e26dc4514043021911c102968aa0c76ecc5c5709c6e4ce3c405a0309f0c492b84810d9fb513929f94b2e01a803043548549b2dc5669f3ff19f7daa0e43a0f317664ae4c1828680fef4ed11037fd7700a58e830a219f5646a4b17f704b9b4acc35e661ace1a7d50be841209f5883008646841fd49745ca37400a8cc0c39d231acd5f3610cc65f7b741c1ba7288b935e0ba3f6780437b9687165621705a983219641a895c0db7b9afc4ec7870ab23eb7879056a56b5de7178c9e1f9802beb821b64e5e13caabba6a0864afba25f474d50ed6e63c35f192061bea6ea8082f1456fa0df87e9875aa68824d033860e835de5cebd61c840d5c15ed403bff0d -generate_ring_signature 3c53431eba5c16dbdd1fb2f98d6388541b7b85cd5dceac9238d6aa7dd5e8ea8d e9c7b96e13b6225b03c9c774e90d2e3f71b7be553dfbd22cf1767888c4f83b45 16 70b95fdf50d68749f55586737433d11e0506886dfcfe08a82b5b01c173dea87f 9a8bf41dbb914dc402d80b6f1510e0318fb908181d765b578776b034d6359ceb 87a438d3d769aef03c070d3853f17c4e470a4a7ec5e4a118a8f00bb0966490e3 0ff0242f7714946d5b872f9fe6873d237f1f4fe501b88119795f64f744e8428b be4c9bd9231f25e1743419a2858c014ced12708662761fb36083aa29710d1120 94b62bad7ce972fc7ee236c909cf19203dcd46fa48552d86f85a2dd6379e4e6f af00070c55a10d7ea2ddd2953e593fd5cfb29eab6c6b676472d029152120fd95 e839d97bfcee72acce077bbf20856232fc8e31d27fd71da70a6f0600f85dc9a6 f35bd0d64ca12db2c9548255626727e57b4eaf37b42caeb5c4160e5053b54903 7b23cd36d3ef9cb17d8afa4346fa949be91492241c2eec7d02329f4ef37be3cc 7f5580e1ca9c5ad57941951ed0de16dce9a031761dc476b0a3f4733bfa76eb75 6bc5897d154e23c56cb1216534610d1d75241d7462aa11b8a80d6191dfed1354 99884db56a229765f038e573fb018af4e2c4e75e9750908a6dfcab2d4ec7a821 656458506acc81d90dfe82decfbfbb6b5cc2462b6e18ec798b486add559ae372 0d165d983a91a7228b44dd25f84ef8246fca9530a6f7e15924c102d4f4b9c038 83169f90ec014494f2e8dda969dc1dd5fb60f47e3a4386945b787928e56817d4 580db78b04c538d490ba0e0cabd646e357f6bbbf3752f0ea350165b3fae41a0f 15 7be3b294fc8c72bd0fe21c7f868bba88deb53d88f8315a252a01a88f3a0f200c0392400c625abdafa68fd086b34b0e92798e022e446b46c3e1f505fe75ec530694cca6e9f486d9e20068dcc391cc8cbcd69dd9e2d473b72cc37281a04a85c3066458db4979c3a4e05472f673a08e75294f596238a78785ae64b2cd954d72dd029f4dd04b1a7d88bd9595a1d92eecd018fbb2a01d5db8e5a037ed5b7ec76bfa013bd26461526ba777eee6167183ee427a5be90e3de67db43e456ce4a1e3b0e105ba33e2e0ff225735ebef94789fced1d3e1060949a799332f052d3734228e4d022619651f336cc6af20e8dd554b9de89a46c699811ea0b274895621225eea4100940e581b7e4edb88e56ce2b5cb28d93c9aa1a06bf8204733bb818cd051f8310e6a83e0ce5c6d1ba8cf4aea17fff414cc635cb675cb7f9c8a0b6639c503f5d20292f7d94158f71019839a9d8ecd6b021d342d848fca332cb610e1afa53d2aac0973cca51a044fe4a36d369339a3df575132b9303a92c5ec7ecd0f7d05a3c8c301c78af2721481311cbe15221414d19ac3774ee36cdeaa553503f6d89b3cc3190fa5621ca46b291a408f4912aef614d8cf9af4f4594a9a6c717c2633677dcfdb032c11f70f333ee56e5f642e24a409e7fd0d0f75ec307a43de6cad21696a93a50df6ce7e4f9039e1aa4eb4f827134dc2ab91da0815cdd54acfee41543590c2c10423a4a0841c600f227c78b45d356e7d7126f1b7ea4bee29ac29506824204ab6084790f8362d624434596de5ac4b436b4bfc92827e0ac249e5f3f087088ef0ec03cb44b5f6c3c2afc078ba33644fe7dc1b232ad799f3dad382e7005465614c540c3a8279ca3592ca89a0c3c69ed5f48fe08907e7e4ff394107fcaf150b1b1eb8040d689f30a96d44d068d20dbe29cfcd3a57b154817c9589d213312fde0e09de028af6ec7d317ee49f5a9d7fb7d91e5a62bc8f9d37291e59aaf9fb92884d7c8b0104231b694c565c0aadb7a7aa995284611b066e0729da1151ecc1b73e2b4374011e9bc628f7280add669a4c6ce4a743a0ca5522c923bf24e2f5770e2c3aca3a0093a39a161dfce4ebe8a12bde70385f6fcdf8bd8a087f485ed2df87fe7fcc7c0c2c0ea1d94702aa7816f784c30b50590fff94d0601eae57f83d20b6bfda8bf80011bfa2c68ac1be8ea8f87c8a0bab41bab193aa5643cc154e088507966c3a44063bcb3d2054df471f943cc8bd4d00ad5a836e8351d236a343e6120444127fd002a7ce6ee2c027844e4305f92c35293aaea0b45b2107e628079b1c15848e9f3c053c26f2b1b5761ac24af738c439e7672c2a48ca366a381fd901713ebd23f6db05ecdf579c8068805361024cf7a13c34e4047a36d1438fd59303ce7941c3187205d1db9e768647351f012685702d6562b8070a85c62bd3bc74b3fa1ad68736a708 -generate_ring_signature a2848f3b36302284378d0f599c7fc0b7911a94281e33d4eb4d47f7554971f7c4 dbfbd2c763cc046137ae025d4fb22914e8e7182f3cc727d2177e5670ae95aaae 2 184816f5034af3d627d6d835e5c2913953f985f597b11370c779ca34048fbce2 85172db3a5e5418276b0b57bab2962b26cb6bb159f3608bd31d5eb6fd507b799 d598ef43cbb2381ebc6a1585786dc1afd6f3b2fab6ec7c7c020b934bd44f8405 1 0449104b79691eeeafe974110edb61df726edc00ad052b104533acd4b3aaa90dedd69c8d0163e3e51b60cf3a5a5a4710b2ecba0c68a3e290fb8b41c8acd837031909606fc222875c3be02ec06c85f7cf9dfa1d96d88970cfd6d020374f9f460ee56525fe33e3d981706258ccd4b3c391fa8d1d5c30cfe2388eb01ae96f0f3907 -generate_ring_signature 3bfa708865ea752237a4797383b6ae2d4d3c41482ab53d42ac8feba9f80b62a2 2e4d774207848de566e97eea6262bb6ea87dddf04f5dcf911ce9914e9c97f528 91 38f7acdbe90c284805550b51f1e9929022e7d533bfe75bea0dc33f717a523e4b 817262805accdb4febe4c20a8037f50aa1fa7a00592836fcd3d213fcaf022f45 13c55f83cf89d5272dd1d638e2b3048653167468c11a8bebd335cdd72e7158fc 71d23b3dd7bcc81b2fe24b32e9d3e57d638c21ebd4039a73c15bb2d6455f3b8a 2d30c73750bf6f92c3bb59771cdddc7d0d6e0dee4d4ef60b443224b418b5689a 5c0eea3463693e350aea35b9b75c918ab6782971cd4aa830180fff60f6084ea4 72d4ba8604da472325d32ddee00e21d844275c3896911c26f94038f30a59b305 2d09540ad3704fb6791fe07161f12cf7c634b173b4348dc2fa2b2d91b5716d6e 6cdd2f19ce8037aa1d0a1b933e2c619c65947fc732acd11f115c62d40e77ea41 db94bbbe5548ba971a53006889f253361d986258b9a641737b88d6d1c1a83aba c23eff960fa6ce574cfe1f74248c29b10843be110f8321641c2147baeccdda1a 47b9f3ad5306af76c7045c93b914dd0a90370ff9e3888129421ed8b78d5b6725 b749de5034ccf7c0c1bcc7a76fc3b71a90b395f8ce74189101c8b78dd08f176a 9c5c2afa18b46a710f5c2bc387d61eec6eba8c59a00781486a5504fb35b8e46d ea7a0f8cc1e7896b4de7ad84a8f2223c1f4cc320e466ddf80112b01e9f7383e4 8408d1626f6123a3c60271334ab9ce1568f4361c9e8961a9813b2f82947dd0bd 5ffb6207a836af8db9df4bde8a1fe896f864adb6d4a1699cebdd134250d3627b c719c4908860d4a4204a100951cd6f6a5f523fb1c25af8f4e1a7d91566042583 1588e45a7cd74dbe403112737070ea504804a951b592d86308aadca05e19bf97 2bc329ac65a06e56aa65e146f5ca37b15c707a1a856681f1b6438b876a257a62 6cb6add63061cb03876eee0de9733db3cfd194b529b92638d74ad1e3197ca55e 869f1663036ae025ed0696a551fd6153fd0eab16437e4f5ebb740975530cb4ea 565c6df71b17757170cfd147fc7ef7ef5442d60f420779de08537e4854242034 ee1361f2ed36794b34210f0084ed1e853d79403287ac7e422a9e2acef5d43171 396a2402d29083eb8514421bfb8b841f699d607565131c44aa72680c47ae6cd4 bf3e71778ff37c494d0d588e4c240a7a1fa28dec561e5fc95ce52d0795d30b0f 0b1c1afd2a06b982d99df49358e06aba690af29ddc2f1c6d5003e6b44b670eb9 0967624c84307d50ff5823e51dde55c2a0f4dd3e54c478bebfc74567bd7a1b27 12a0cf3cca9791f9d9188e21f8d5c4c5632ea737e4a548a29c6116009b1f583a c26300d73a72480a4553d325bdacc91f0c3b10b7dd53d490cb3d9a69ceab15da f91ce4b28de23f339c6ffdd175ece69c2718e2a3d9b22a80961f6aebd990f09b a5b04ed97ddd4a071ad49063c159c6e1e5bd53be621c2103efa9944a504c430f dd1ce2e7ad25135322058928f7e0176d77097063eb384130b19e30357b4d1a31 2987d815b4fa2b7214a0eca3d95b3dc8bb79ba433ac66aad97f3e5ecf8a67575 dec31d9c26307ed50d5ad50c5698b88d561b3732303d0ab09f2ceb106b2f0af5 7ca0b73fd6dc919d8380b22294516103ac26831cacfb46e0907223290d5ecab8 aa97e38b8e804aecb8e2c1da77776e3d0582341b2d39be6a45f8c513c99cd5f7 cb32604020edae3f1ac43f49a50364a09e9277ad9481f1632c7e446ff24d376c dc66cd2e4cbdd497703f1bd0953033cfb6550f46412250c4b62a8e44f20ff7e5 d713e361ad8a2b8715bc3a8bd0a64cdf4fe19d6f4bf2c20970572a5aed81c14a 1952d47e81a3e2a02d59f47090da2972b89d8952d498bd95d24256efbcf56fa8 33ffff80dbb6a235d0d6c0cc52dc0dcacb4a692cf09cce398423eb8742080853 1e328b3b594f3748288881bf6df6c2400fbad42e1dc53099f98945d0776357e6 e45396f2fc37f54c9ca3af36970ca0ddf908dbec065b232678d5fdf9cdc115cc 369cf4a82d27c83b295dce181970028fe53d6265380cf87c01819e36d1563ce4 7a68d3887599907fc20337d5f7b7fc4cb4f88dfc6759a0a35bf7a2a077659f20 0e579c35139219430eb86bc0da2d37f269893ffb6d10834a6d04f0a7f283dab4 52101d8a151e0a535bd2f5fe4db6d1826c47e6ccbd8a06d1ea8e05537fbf421d 3cc96ec853bf5d99eb69530adecd6aafbe9a95ba14ad506d33772c9de1cfe1f4 0ebb479ce8e81559493318897f9829cbf14499fb639f99c5a6432900b955befb da8e0e7f63a5f460d64b71a38263100afca62d6ddfeea1ac0db0a657f1296af3 119a595ecfa1514529f07f7a90cd78d4b7a98544fc4ed0141ce1c7fe2a9acee5 d9faf2641c4726c711f0d4136ac280e2972425b5ae7a3c27bf5b1171b378909a e807cdc919a38d3c286abd67ae7f0dd3376ac8e9ac55716b52b5c44fca5cb5c1 59c7d6c794047bcc905097d7fd8569a5e4888aa751065b66de32d8d00890bc79 39a5852394a61ea8ef293b244f4a2e3251939dd6dc383940e4b0e5f49b963c10 7fec3deb4d91e02f59d75bd1e7ac2e92b5f8030d696bf88463288e7a12bd4ede 9b1364077d41d4c6fc13b5e4a77e52c27b2fd1dc7765c1696ebde46a07c4abb0 2f6956d8fa81990810a2ed1f01e04b4484c0fd3a76cd84ddd2a9619e332e1d88 5e135779ff608a35cf5da0e30656090fe76c2cac427604b732fdc826b7265198 8c27f69dc560a8e8a68310264d9e65e7ac88beb237a9abbb7708266844057ce0 efcd93dca818910bceecf477ea4334af40922e56c9f4db357cdfc3321abb0f68 fdba4d25f7845c2858ca4e6c1b077a2b7f0f54bfba930670c255f254c277ac2e 04a7e77ee772a997a8954b709cfe1f660576adccc0bc828042a6bf9978594618 917d17c135e20aea7caea1dd22cc6752e4fd5ce05098d2836a146c793eac61a8 b45cb5ba80a17e6c0646adaf9cbea1f7dc5f3799a826c9f088722a5cd527b82d 29a200178549a18673e0eb92e8bd384dfaa695e130c54dab6cc44e826e7c3112 c8303040ae302f47eb64d676166d16b6e38b8996a88e49a0cf1a679da3dc40de d0dd2eb2093b1bb607c5b53a9df148a7af9ab8eaf50e56a9a19c4366a4b873a2 54107acc6285e280bebd6c5314d9ada8311dce901bc812d700a3e938fa628ee9 5b6748e854d70e02a91b3642e600fd32898279ece0f10729127de0bafc7db0ed ecd46f5a0d0719d60a71739ea93af27400770994db68ebe7fa98f9cbb97f5e86 9a5cdd5c941a30e5840598daf4e0e4dc646775ed5080ddc03bc88e74e57dd7b3 b76f4e14bc66012de09bd3fb98724759e0fb3cfe68b17ee2f236ca35fcbc058a fb0f82776ca7899d5cd705d784cb0a30d9a705fb5817497d66c048709392fd8c 6ced641a3d4c402a7bdf2de0d000f9512d6245cd946971429ce8abbf13ff56a8 fef493c6555d43b0f2303815f086b7de4c13eca9fad0bc7cdb75a118bd433392 3ab468a92ec76d5acd3837aa83b7ca933984be9c167dda2a43e86f5b5a70cd01 155bd8eb9c7efba4b2e45083e6e70539f68b0cc02a23aa1ef40d94424b0cf257 e51320a48a75b74d223cbda29a41403195300d6da467c8d636bc909bba0d36b8 3fffc66702b2144fdf6fcc3be9545f7f5ee034e399e4db1c44f7d7960cf0580b cee707102ff886386a1afc90ac11eb95ff796df190068216737f85da4a99a85d 27a33317c49c01e292d23a085ebc2af814f406665c43f9f8e064234bb9022a08 de53a7f01a8b11731298523138bc022a5def631ada76f4c513a71e5398674225 585c96a449f6712c701e231b526202e28a7e7878d8c09c79f103f2c734b106ac 55dc8746379b0c79871bb5c3fdcd4d07f16567e4ae20fb7ec2e794d27785facb e9a7d69695afde442c58da9339bb8d00fe3a90a74f813f00880f28784797a309 6ba12954a1b735fd842fd5534d28304d9819625ac0425b128850b5369e2c88f4 d41537e1b86925fd180c1dc27f7c6491813f3344d4b9e7bf26062f3c93a4ca24 d04d80299b1f40f97eef90c85799b317154baea13dee98f721fba3d6736e99ff 9629084c2f4e36d15a963af5ae25a8ae70a20d6d4029b19f313cecae8c810163 5eb7b30d7c80f24c1cd46f488db56a4b930cf1c595878682025f0315d1595401 43 72e2e5fba80fbd300bc91864f1e5c3b4ebf2756fabd99eea55883b5392a7150f5080f3fe5f0baec76c3e192ec0ef67f7467c9c5bdc422b15ce5e0472c4d7890e454ca019b6ae1895cc9c6a328c3e3293b18319617046a7f9277d517ac349520680b8864dd3b2fe5484a85cb7330fcf93f219eee052c3468fd546a335c8d0a3019bdedba729f23104d093bee3b6e080273db3ce1d3397c35b213f62b9629d000f1d1aba7093be3a57de67f5472fae2f3a16a9b0eceb3d62aaaae7fd14651ad708e05b763912f0a483c377860bbff18ff483dc3864e4123abb4275b2a3d9587b05b8a20421ba24d56a60abfd2ba3031c7eb49e3b93954da692f3c8a165218acb058dbaa4dc38538eb7bf199856cfb2ce7c06ce4d690dfddb4520253f098f958d09cf78843c24c9f85064d29ddbb37f8658e14ff93887e32162b5dc17e818ef520fb73538dd1ce299be35c21128cb957c327a34e272cde7504e585c09b51c18f40255e3eb37b1c4d246e70379f89462f8063f992d473fcf49ba5ac28a31cee77e0cb3dc29967ff50afc8fd7f6c73a488690bcd6efe65bb7a83e5d62fa14ff3a030b77a7cd0a08465a85c8a083fc9e8b40d68b88aff128837d2e64dfd861b8fd9a075ecb6a577335072889a2544a5a5be86b5ff0a9633fab31a679463cbd7842670244352bf2e863f02bb4d63549646d9b2842391ea6aab5d264fd98af08a171f70aef3b974714e4bce98858708a7d67b120a28c0abb910a1d6e30e2aa4573db4f0b939bf4dc5fd9c8b90ccf97456d0105181b6c72a15506a2475d917f8ed3588006c9479535b5500205e658c503345630d2a91d062dffc8de3a32cfb022ef53160f9bed5201d8cf6b99469cffb507b57b251bca6916a2aa5b2bbc2d794008792402d49067f9f1c0f7c6564a540dd06d6d0676451366faf51ef85378a987200bc1044b2a7658ebd2a24c5c2de634f13fa6f41a9b6eaf37fedf80402987b8d732660b42f058e7318f1eca158d5ebe23be761a6f9f43d0ecb71602642bdae2d05cac04e4811899ddbc598efc0045b5a345bd078354b95d8ef212fb80e412c90d6921048f588949f77f6d3be11f90d6a3ce60fae39e7eaea1adc0520b7440369925250c2f9102b4120009e59ab2cd89af6399684bfbd841d6b1d05e0fb270abe3e64909e779cb35b4f300e83e2010264ef8c38006e7050539030a424e8ad71769223b0c350c208d201ac4c8d5c20a076bc821bddb2a2b3734ff979abca03e7e40601208acf627fded1a55bf8c26eaed63d9e6179a42f7b580705af95704ae157c7b6f09d53100aab71e6050a049daf445b5a251a233bdd048fca4c3f8fac71d4857bf072278bc34179f2f6f18d07490cce5d63418e8baf00297d764ba7d81b9f0357b0f4f18c3331552f18560a7fcd8097301f5d578d249c1196558f339f636cdcf2f08829291a2a00f243e64927c587024c05b37c565a21e1adee135cf6c053176800134082058eaee9b4f4e51d49e0e9bc77d207e8568604cabc3c76a46345778c10ea6248c83c074508de0297484f2a3a41d0da1d1ed7dd7ae9d8facad3b28592406f579ca09c258c4e80cd5baeea3800234fb04e5f83bc3861120fe2caf48cb780e83a0c4e07ec56df9b783cd741ed691c99475f7be8c92b06edb7f146b9d6c0a07b8c7e6d6c117c14f489e950f46f4ce44b42d01b4b8971e28fb9c4f2fa049c00391e8bee5f5a891ed167780eae4d95120f1852c7b6ef77a5d07637e5a0b67ff0545295a6880ab328dab17c8af6f94d5f5d5dd3abf29d2cb331cf5bccfea160a0039e13e8fc50d048bcd170b110fadfcfa2421214693e45d7b64f532adf7b354014f74abed3e26b3c067f2e014f00b27d58c6fab621562dec25d2c0d5b2998aa02096770fa39fb641a942696113cb9e35b7c65dc2a94437487c8d780fed65d6108143d7e1ecb67e4afdf79ea1134252742d8799db488a708a19b02fb6c9fcf9b090b33dde86fa0371db4ae9ccf1dffa75bf30d2413b61ae4fa3d0175b421ec810f068bf6a2050c3b4955fb16e8535601f51e5193151aa7e1481f1043f7c4470207567a7fd5d15c1f7a9451cbc9c294a94dea19323b0d62beadb56da2fa289e0902f6b71d658a2063feadf2f338e158727f4085c6c78499ae0b05a78527cb1db803bd5abcaeb862ee964479bcebd7776dd47f4a301db227d8492abe3c862b9a210936b2c756bf5ed4bd264b944122717673008d178839995c56f597291be8cf5200be7bf44973054c4daa8eeebd1e9da42f40cb6170828715add7e37acbf45e3e06a6ada9856f2475f10d85d00e571ca62cf9b4c869f5899ad95215ffc20640860e2a80e593046c088a57e10328e9d36873d54ef8b9e71907e36ea5e84db7b9850d6e8a856a433fef243c5638c025b61b03939552ed7562de97acac7f318435f808aeb4d6867f7a54c954cb3d0773580e8e2cc7c0baea3ed9a67f08954e76b4be007c51214f9c00a8075db744dc8ba6f7d03c8f4829e88716954ef7cb0ab2938e09ad4c00bf4eed127d142c6dbb2b05455b1bc3e0cc56ce7bee3c4765d5979ef9054050cf7c2b6b69d11dc7f3a55ff8eac2cc1579b6e511f7835bc33291875ee9044b93453dcf5242cc6190f061e261aae81d159f17c7bf546ddf2ac4137a726d02dbae985d733cab26450173c3554de10ce4f4ea42ac5a44aad959f74da67ade081fc0d307fe246b71840025e2bc95bcf6193b07d74d15253774a008f7100c220ebbeac577aff66bf8fa3825701368af352c13715b09aa829d742686a8a499350a28b64c58f77aa730ff080bc2b248438128c731598ba3bf446abe0ddfeee8b10d60d6d3423ded388d89e6e3d5500d2a09c977b54f25ab3b0487634f52c013bc0bf73e25e50328b8daeccfebf841c5170a558af7dcff85250dbe3bf6d7cb4e0d07d698aed4d21b4368e3b49a770827e8183a9d5c3703fde651a777fec712ac15042a5abb5f10076a5cc4926a423e6b20d61bdc9dc3300dd036c859754e03c57a0ad6b3fadc3a04ad875416f811c208172f781e54cb939ad56498050b49a2d4030051b794136155d7ca49de22b3f052cdcb2a230ae8c94c03652ce6f69b76827b06c8bf5aa4826cbcae9100d95cbd3839e06587e34972e0a46d5e04cffbeab8f600449c3c9b892c0a89607f06ea43eb55afa951c820fb343ec6d53172f6986b9c059fd5be4289bb92422017073695b44a0f03248d42090b0657759e6043cbf9870bef7c1317c1e8c5c0ff308a6ac428eea20120632a6846113bb7f9d101d7690b05dc332177a12a6260363939a171c2faa7c3d9e95112914c86a72cadfd8f381f0ddd5fdab86b9e86af6b31f42120d176e0cee2d093b0c9a852fcffccbdf357fe047180cf0534ed8056fbeffca782148cf926e74fd7e049fba091310d1cae446304d7f6d82a2bb188b7636ee4dbea5d6a7ea94d174dd1c1577073b04370ac7ef307da0473effca5812ff77709491525c99b454e7c1dbb80dbaaf0a07b476d0af306ef46ee7820bbc27c3d128157b67c096df6dc7d6d45e63e406758ff9b68588f07bd8b677cdda18236a5a80ad764b2be55aa0cb17da7aa1ec4a40bdd478d3d3b02e96255ea1815ad045cd4a9221e563f6c47b85ec6e8ffabdb5cd01e1cb637c909264aaf0c25716a0171437cca8211582b478b950e9a8af9e1b51ace0bec107608856bcfc67c0d7858559791169255f9ca39d6216499b4a04663f1071f41490d039600ab3a1170805c8e5e5deeb4ac03061aabd5e9a5f85b2ce1c12d6669ca870fa1667420c5f9a9b903e584d3aecf47ad6c65453b198e766be5ffc7d30aaa9a005bd07a014e542f6a57b236680405992568c5bc0cae038439a7344f9fcab2400c0facdb32387de80ed1925d88eee09590f5bd3373cd9d053e6ce4bd430b52ae08b8c49a7093d68c7d8182125bc1b9c09ec0698c97032246ba8b1feb82105f110e806847408576ddbda8a03247424886b8709f615948e5dc4d74f4224297de3d0274537b092a75395a3fcc19e868fcc552a68637fa258b62d96dce065d14fd3403b153afbcb5e0857ef46d5dd8147d576c65617f2c0653639d6c7cb45789c29c068d08144512ccb53015019a4306b045775d3cf97de6a2873a154bab3c5161110cd8e1b84e33535feb94ca7fdd2059aec71b3eba70134d7af9a64a3b8821fc8d051aff57926a27d8bbf9848c93d82c9cfb5fbee9f2f6cb208d06c7c032399fa70d621abd0b2e40b3e11e804ff8eabc790393fe8f1188ed70307b6c80dd8cc3f100db21a5dc91b577facdc2242bade8cd9f704ba3c55382481960ec64d23a9810040ae070f1e5bab7178e7159fbe0c09bfce04e0058fe5fd6bd47c4219cd5a2cb0ab4c9626d1215601c3e316cd83ad10ff872e2db971e07b51f4e6e08f6751ede0ced93a88eb4acf2849edd1d075fbfeea2190359df731b0468d0a12d3b78846008182cde0e6fea4ab150f750c1e9086737cd8689e5aeaf8db3c6eae20afab9080bf70b30d715170d59f22de27ba6c8b7ad72dd624e875544dc9b96e153c2544d0746f65752d884bc3ccad758ed80566b3fdda2681830eaa05e239d619c5e4e5a0e3c6b10dc6148d0b27a1e7a13464d051546c35d38f1fb8e3ba939b430c41dd90b5d7410ff587c4fe2d469a95e87f14d368a5b12d6e425f0272e51dafd27737e0fb00ff7bd86721e464daea9e877558637b5cbf75b38cee2cf0df04d12a1e80302ad97ba46ae1be834fa64f42dc8d34787c6eac1ec32f5bb1a2798e58607b2580b3392c8a6ad3e5a31f4e24149293275e678ef04bcbc5f002b98de4d5df6339e0f72642cd1df51a196e16cfe57c7fe40644d4515eb747c890b60dcff9b94b64e0e6d397a592e56ee0d418b907ec8d220d2e767175dfe645c5c502f5d84c401f80dd2c7b057e75b8650634abcb272360b326cecd7b1294439c3fd3732c4a9c64102c1d012894eff6127c79efca84f7a8aa44749b50d831c67b312eed7cb8bb47d0c03b0d497935a54df1ac30c8b72e5894f9b5a2d1cce78042638506984e7d91603a63aed1952ec174b0159d9dc4437ff784a38392e9a6440664784f28212d29802e4f82b75f8b82abd144738a5564eb2d10c5d04ec075199fd0d0527ae13d30b04266a1ab5f7fa15f79e336267774b36aa2aa5e1ddb9457de14dd7662041a7880c46eea1ee062d38a36438a5404a62f1484adedc139844a0a144b071c52246280379f76dfe89bd41edf52cbe2f60de137b0a589e95b1adda88f08aab62af49e80d37b637e2564af47e8ddbe9fc7319f314edb8dfd1579c24eeeae120548f03140ed76a27a0ba70c6c7b6b878887fff804c0b890d16971a9f95c4a92acf479e74085c0629f1cffc11470ddc1ac9301a6dfebb45249eda4cd74e38ce2c01491bda0d00466403a645b91fc1162a642e6b6a8706c8433939e7ae97cdf5701880388f059b906f5420c8a5f992911cf13fff27d124b634685a5e30379796233e914d3705c4f87b48b5308a73faf53dc46b4dc0d18f6fd545b629b821383e0fa3a315690b78482f392cbefdeccca76866d3e495954d6712a6ae83541d64ec1390a7b27307c472e1bfb061d3a0411e200b16044a70cd3abea8ee13c062f77243c1bde10409a2c79bf48cc349c8ff784fb692acea26afafe352a7f5f05863a61c4845366a03e361bef2e29dc00f0c63ed7787771f3b93460a6cf4f4b84dfd4bdf65f414f10d5454387dbbbdf79f35595624a93e35ccb7ae47d41a787785befb69b0605d4901752f5a4a920e9bc3dc14d391eab7f14e67d3161fd25c046bf85533a201bf5a071df13f82479177b110e6c6021e7774ad41913992cc8502cb7ffbf2dce79dae0a176059a07a5b279218fb92e701856b8ef53124e973921cf71656561bc2ea7009be9d80b166b3147c00b15a9d1fc180367dc70ae8e8a06966cd378eea10e244011d14ec1949d20f7eda9cf711941215b049c70c4ae7f66661176ae2cd9868f50d28e48f7aa52038f4f348996fcdaf43f0a17f573d1962d82fac79d2ba0d1d7c056bc82dcb19f5fa4e24be0b3050828364a99f0b5057b79b1d3bf16fc3606dc80e5a86f006af398c90d4a5858a0a933e43f8fe23dcf88db22abda73a9728b5a90c72ef5c5402ce70cc8c00b0683939ee30e45c6f3ab6b0db732ea058d19c0b24051f19bbbbef7219d8ab8bde713341d95e81c6838039a06813cdd11af60130740345339f2deefdbd162bf8045e20fb8f273b1a220e80e7d82293bac6960854f307a4ed6212c75b04a84680a016180f209efa242e8fb8976a22d53a48f2035a380197a517035ef6090440d6fb32836631081c1f993b400c825533c8919adf989205513cf737f8ea15bf098ab7b91505ce2c7eadd9d598bbba62b40ee28c5d1f4a07cd3395862c504c65fe74784a38a83fa64a6281386581e1e2dc230aeb740ccd01cfcce4b84f6e316f756de9666a5d0e49594f4b3a93cc4bffc8883ffda49b440bfd410cd3b05593e944860a15494d17fb5ac444f5f1752db73ab695f2f8673d0c7b2974c93cead899b31a020398545f27c2753efe9b224f841a1026c439bd060ef6e9c537dcbdf24890945a3386d3cc7fadf7f0202c7058f23cc0d938f7107d00cceb62b97be2b6c66b183887ee0a6850b9e124d44dd61f3447c9c3e5929a830639da4cfe878d471cfb3e29a0e0d31c0997c8464d04f5a37c5e707e1bb8269e03d703718560a1d4426e581e8ee6a0c2d7e26667c6cb7bcc91106e8946a01d3b0426c2e4ddf6e907ab108ed984e3d23a9fba15ac0ff0394627685bd3477b1b6302767cd20acd9bb903d9a6cc13578d48208fe7a5a32333592494b154a3e01252055068d358e844be916d8cd289130ae41bada5d5034a0481ea6ef68eff7b6bf602a21e5988a17cc638d00e86a87215557a7d66e7e664ad734624afaa3dc3b03108b2f93c11c0af4f3c983af5a908aae3dea6b29313b91c97f8d127201ddf64fa0b9dc2ba210cc292fe46f9f2183f396f8221354d2221984dba3730d2557995bf0530167347b58c4ffdb54a0f2f8341dfba68fee0b46b69ab95cfc71677bcccc2010963d6f16570538e2113ed1f67f23d1459624f0741cbf0b413074cb7801c2c040972361f72673a9266fe2f8559e046207732f16a6fa519188638aafcf8ce0d0683b55f3824a470981e7cf03a560de9a645337135b693c36ede73629f9c1dcd09300719edcea85d1b80204c72920cc087774c9d28947a7c46ba139da97980d90f53d4ebf42f63bac03f37f62144fbff25424e5bdc7f3af7e96694bdc970e99505c370e5451c583cb530414e46bad097f596c203246d77c377cd0829159dc33c09d2b95884bcf1633774df16136596985a6c9173a9941f98bfe7536c4a8207390602ce864d8c7be698dbf0657012961f389ba56c03dd0b0cce4adf88c3454a55015b411fe0f3e6ce93484882eb0d16f9c2e303ab08f70bd06c352a6af4d125ea0fc03723415224734e9b1275a6f5b4388e257033a8739e6f27fe5a9e373603fd02f03f1fa873304a2f7eb46bfffce3201720288a8364f7ff5789c44a5bc2ccad07414f8686c55dd3d538c16cfba102e872d1e202cd1fd81cd1f0d8351cb16dde04739e225f1c8848e2739e8d744cab69ddcbc01b4e18f5e1af2d52899c09fe0a0c48bd25157d1de6f3972bac8357d123c32a7867bc4d83565dfae3f3369261c60b6b2100dec16327da2e080fa723381cac50f4ff22f829430fa1706d56edd49f0f9ae6b2bade01726a5451c052b9c23864e85ca289f709b9c7067010544604a00b0ff579ba10483552ebff9fd83b284ac88284401f8c03fd88def6e6cd33a68d0144c752ed51b606d46fe00ac43251d2ebace8fb11a4b5ebc87391c955fcde5f0d59bd109f0df2d3f9291a01ef65f8fa99e70d51946960e2588a35ff11a7220804e6b789e42688eec25b1fc0cd54eca7f540ef289d5c10136df9887beab485000ef37cadc59deeea6a44950d3f923aa09415296b5cab604e279c3628022579580df59e95a6562c36b6599f304f0f66da24e111a48bcb73f9aa2dae6f17097db606b4604e8eaa39ad6e8bd151a3e806afe7103959d73efa3e74b13f4950231fda008772e5f79f702661566902b2e3073a74f6346c44fe30bcd122a644b391632f0a585e142707cec8a4afb9306b22862c7234c5263e65e608f2094ec4ba33f8a105 -generate_ring_signature d9e4680311d1cb9dfb01254b08fe8cd0dcb7b3c503d6859c59dc47e25615024c b8f8617d742f1a96ab3b79e141b52c76545b39f00fd00729ed4325c2e8e839ee 35 c67863dc9547745b0a1835bd2bdd3c7f866df86486e79b5b23c60dbf330dafbd e837d13ecbb2a5a0bd620f59334475bdb332d44f44ae4220e5b30f4134416a87 c00aa3422f0a2f3a5a89edb8eb7c99d642bb33d2b5d4ae42a228ef3dcf990ade a132684aae1af1909b7d32035c7853359deec9fd5ad14e5548cc2e241223988d 39b05e702beb2ef5e6f02b67d46fe13923ba2629cf6dd4d813f92b88ce94e049 d3f8732f7598b1e7d51388a77e409698b1490ba56ff02df6c9091fc3723ace03 54929c78aa0ac34a796a230b66ae375a443a067a07954cdfdf4f34dee0a08a23 81ef7d3eb1c810cc7fdec70dae0a75ca8084651cd9a446005f7af0a37ff68c8c 06081276051edd0d6f829948a77a1ac5c8eae9e492880cc52c3a3b7e9442ea86 6b8762b54c942dfc5a701842fa7cb5ecb20ead13d5e72ed2475e00ad78f292a6 031c76372a7bb536cac58af77c11daa08cfb59f990cbc8c2eeaefc4ac1a598ee 660ca8b174d65ee995bceaa95e922dbb701ddb6c4df76e7cb46a75fc19b13eeb 81822888504f072acfca4da1c30575bf5efd17a769aac9cd0ba165003de67927 ffb833d7a1d148e158172ee880a850d83304eef3c6746427a1ef1370fe147474 23234646c19c1022929d56a62e494ec2d45e7188863f375cc2ffe681701112d0 227bed9c4db50f0e54a70edcdd4be63ea2f6e7b3563af10e01c9a9c5997fdba1 d5aff7375658c886005af953d831d6e4e2898b1490cad7d44a1b428e87e8c2fe 546e5da5f5599dfd4cf7139f92c80d70e6361249ddfcc36a64a4a6a4579ed6f3 4617768b70f157ad344866c8f0d9575c7a8029b2f693ebd75202fe238c9f334d c505334dbf2103cb24205e0ae4a660a592921c5132906cd7f634b78ebe0ced01 688761e17fbe6024f8e51c6faf00112256440cc4a2c7d37888ed4f5692e26b71 fc48bb2d96d7ade8b51a189f4ea1a1a427669fff066da86108899e2e069c37f4 e80d7e7298e64f4f273548bf0e2bdfe3cf8ff76f86f1291d28249052605dbf46 23345ed9e65ee1d2f9f0df21ec20f4fc506f837f54dd024ef770a81b12588302 b572cec572366f8fbaf5ad8ca07cd1cce301e56ada7e15fe2ceb03d6f7c615b0 ad65e69ee23a2d216d437521cbc41a946f1a0e2e350eb1c242e53d845e664a19 9d652c7154706b781272ea4ff1a5b3b84d83b2ab86389e927b6b0bbe611a207e 05bd82e4dd30ef4ae5041ba77652e173ed932b90ae8f26f0c7a1c924dbc16bbf 95f6523d233a5180cad52fa70fea8ba013a1f565f9fa5d56e6099b99018a5948 e376bffabf575bcc111d39638e0559a8d7e6055e594f5f43fefbc5add7904697 022b90003abcdffe3444bf899962aa0173489163c45d6f3a1c023fd8f089cd6f fbacd6c6ba0fe19d9d48ecb207c66a7578648a77e9c7e4475721510afbaab99e 88f5947ac68fa3b16c1c768a9bc70ebf76ccd2800e6acba428ef6d8be78b8b34 c4a86247b999e7c8460f3784603080ad902ffe2e71c986278c7a26db99c32447 1b83e2998db2167cb4f9133ec411f62fcb2246a84ec1ea4acaed25ba5fd60d52 a3480165d43b66938e0169faeb99f7d09b4c918620775b48e699b9e3e78dfe02 29 a352656c805ddff9f20d31dc44c4f954f23624078868fe5fac12d446a3cbae08df7842054c1171c09a8568752b91aaf8e751e87faf14482464a1aaede5d2a106e0acd9f9e8f5e00d3495b0b588809dfec7dab5d0b8d63fc486383f9dfa9cc606297c0c013b99fadda30d7bfd62c88bf4357e687ef34da3f256521121b719490001cdc41989cdffc1c759c3b64a54d1a6c6271fcc1577a4bbabb93ba168f0930a76a1925c82f197ef4dcfa0e2de8ed5dba792983193336f60edef172af1477303415206e2bd90ac3b33c2398f3130fb1212599a87d56ab0da9476f1da7efae60392b5cda5fbd642ba7b11de1a9fe6bc09e2daa8c2519c97e9431adb8f883eaa0f4261e89818a67dfe5ccb9f7d13e08e06073c5caa1cc786a3ff6cc3961569cd0a44bb18c0ffed756c41fcc05a6150c6f08215016e176052034b4516afb32b1a048f2b9d91ab0d7f5e07de05ffe814cfd2b2ada678c2becab2c4bf1a76020e8007a2eeef6f54d927f62737fc3791cb741c05ff2b3243f1f17e521712066f8952017c9d4618c37eda2ab63f460293cac516cb377ee8b17c854df8da2d384f036e0d9ffdaab17d602f7b34b3e5f3e2df7b6d7362b5d8dce5f5ba837544edfaffe506c95d332b39ef24d71818390e7015cd09efe045d8463908c9584dad3c1736d3032ac13f6b82f6cdbbc5157c570c4aaa8ed29514ce6414729c96b767ef0e7e310c7f509599241f99864ea7edeec12a485db429c7a5ac37a275deeee27b82106a0cc194c7a9bcf2a6e1ba9a9e6af28b55bec610adc27471b1eee84d1a7a09d1d10d0791cc764567302c74dbb37d78b6c1fec4b572da8aa0eaf632d5a6246a16390627368f425f889a4b810020f5e7f56ac9c3e9e13309e9e2ffa6daa346e80e45074aafdeed13aa1a07da3a9135e17b5521692b5f15bb3ebd206869012fdb0cda06c41a358f74501fce2f2ed649ce2236468cfcd0eee3ca4ff4bba1681d43607c0ea57841bf0af3c9917263a3e06f7f1b6b66137ee5fee0626a399337ea0a371900ab5458176ea748a58a629fea6bd33d4dcf013eed25add96e614ab370ada2fa08c112057ae37513e820793c24a146c2f09d84cc703554c90709f0bd16be877e035410fd2abd17210e46a7db15de0411530e9a2042acf4ff86e6ff579a7e9da707add966ac0ba1720c49b7a4193c3c1fc82d8a3f22153639a911b43fb06088ee02b2b6e894ee189fb70b3dafb3c16f9fce983fa4b8f2fcbebd9cd040eab3a12c070b2756dc600bdc44707de949773d37e855a2e191927493dd79d0f82cf8bf890e0dbaf04e27e358936dd5e8b4121958fac4ae10b5d9a27552f236b94f3342be06f8d64b48d527881c7abb89d1b183e2767ba3fa311d8c4345d44ee52bcbc5fd037bbd3e3e6cb34a014e7b91683357b2860af691e135455ad0789f4f1bb572f60a1d749af29df925d9c86e594291182fd30124d139b2c11e1604f3dcaba65596079e5f701b9bbf99a4740b21ea476e4a3fa7fd8072bcb0eff26aca80a77ae64601d072dc34b3737fda37c604823640f60febb753073ca744be5be36106d5e42104f9d529f1ca168a74fda5ff366ea04198a1e3a735fd4bb42cae36cb2db9a65d00620269d8f9c9e0f5e453125cb1ca4747249faac403c3c10eb529e7e6a355c802c292b6e7982f5e536fdae3b650eede28d42df3feef7a4897d275ae642eb942058937741ff0213b09f18e3ce3bd35dc9d6454d347e76f9e48f1b86306d3115604b6c308a992ff8b85768e61030c9fabc5c3df23607db471ff83a97ce0b1898a05ff0e45b923fc18e52029ddb18c7550e8a136440940757dd8cf7bf0ea2582c60d269364edc87fbed8b591f7fb8ac8b507b7b9f8c6a2d5b16a22e79eff1f32860736a72aec2150a0e04f52b42495a2660702888afd4302326c4332f3808b281a04a91c7f9f895d6a83e2d2acf7a3f0e2a1cb12a5fa13f56302b0c316d195404501b629e708e4926c04976282322ca6812adc7beec3f7a262df05345bbbbf301d0b094dec47746c6ab5d5e13b4d3c6afa725474d019dab48bbd5c8f8fc187b50d0219d4be461a5f057801c0018af8f1f562761cffb5b36f3ce452eafab75b69e50165867eb78dae11d781e2185f6fefc5a359212a102aba93d4c9ebf5eb9257af04c2e4136ee9ab9407ccb953b8b18d61727715dd75f51129683adcf440cd196f0a226d42e79e94579d818eb36fa39d43c573f5c9bffb7e0af6c6f75028ae65cd0d3fc7d2c2b22c54817d51661c578dee5342cbd73158b3ccdedb09cd1bdc55c30e7e286af48f174af1f52657a042e07104094db8316f4115b7831825d1006c7a04249aa0723488783447c73da79a35d6417018c17254bfedcef44ec1b8cda53d0af04d73cdf9a7647f7bb658cd517a1f79b68428fd56deb00806c27a4baff38e0e9aed082fafd11753210896e84299ddb8f30c00d3cbb9910040695793582e86034c339209eaeab7d1cc4ffa6b310cae66a706a137d88595e45bcaa1938b604e04917368d12bf56cece99c7818de5707a8bfb6638c62035cbcd411b75169727008924f4c802ba99ca2c3c80396fb9daa3e9df79e24b0570f3bf3819895beb65b0a50502c37a927ae3eea82e2dde74be2c5147256fa1cfb4c9b1d9163914055000020540570915eebf7e5a756e7b81e5bf0f76ecf64ccac6a56a5a6ec49a06ef50b3a03ef0815fe52e5bdba9537c5eddbc04638723f1f468d9d1da9f1d732d2c706b10163f76d03304953cb8af34eae51e2193bfc80d9c5db027ab4048299a54200007f24b5a8663c440f3cb7a878d3a3d8aa896552653a94a0f7fa5a7b643cca0e015ae4afdcf39f00d20d4b523943b766e812096fad9a0a08d92255bce8f4b70b65662e273c39d047248a6ee895d219dbd69dd9bc013dc1491d4b0bb8084cf10774a16c26d1010155e196ee68f70af460b300f9542bc3a61f222c95d406f6450166f8b614c475dcbe8d897c28bd5df538d66578d8eb6c13629dd116450debdd0e24c4bd3588ab6cf79bf2e58bc983bf1552e967c0e62f19e494e6cfbc67e3410d9f8c63b47af3469e4b13e7b5429dcf78a401ef3530589e0c4694c71f37cd010133e9f168e9966974a6eb3be88d0a0349c82286fbe2eb838d62adfda4b744f00a -generate_ring_signature baf768d9e42fae2c59729a1d2b8c1c90345ba669b954316a5674d1de6220a76e 4d7d7d439f8fea0f37accc6bb4b1d9b3d2c6a8f1de4c4080d058aa9aaddbdbb0 1 782b450055215a1e6bc07b6003187d9560cee16e6f5262521df26ece593c89b4 388fafcce2f0aa9c54a2cdeabc47075bc4c6dcfafcf8bac5a99cda75c3e61b01 0 3430f9b7fd78d0c099b8f7f13a9e4a22048f06d8735d004cc08247f1aed266026c17ff42f93352a5ee7775c0b47d1599afafddd415db324f081252854f5ac200 -generate_ring_signature 01916e70c1a168a97075a7d873849f7ac03e65094ee16f44ae66ad4a3e0a9a61 19388d755023e1604e7a357953c2692ebf537afcd3c5b175b18b4480c1770ed4 53 242cfc91401e49fbe717545536ff1288d9929ecb3fc5fcf431ed7e457bbc4b39 5720bfc3064c22d0bcb46686feb470b9483cdec909c4b70c63a8144c603de1c5 3144d1064b96500b9fa8ee6ac4f95292f6d92e7c6ca34b5974511c380807739b 90aa403c95a6a5ea6866ce56913201a0aa37d5c09e3e0c0150b8f23e6a8f7074 29a6167a49da7587f4a4a05e534e194f7fb0858c8b187e681104939611678220 47a0530a95b6e548ed923ddaa08d8cec94bb9afd43a716602c73f7a563485426 d7792a71e94ddef9dfc4300d460fa547d0318c04a0f43513e98fec2389fd03a1 84ef4f5e3c509f8110ea7239b531431da9420ef98bef880020ef1565915a9a10 1a32deba9900d23de6dca4cfab511391e4bd2ecc9d153f2cec3a4ce4fa4c54df 371d5595b2a49bf72d7824711067e3248f2429e086383d77de2035a5da111c25 68bf85ebcae2c713975dcafc9200acca99d4d740f05bca766d7eb82391767f66 837dad3d55f3fafc1717b116c0f7e7121ce70b4cfb542b5452a08f92dc59ad55 d66ab88fe75167e16bc30dc727e2862128b938292e5bc60a5eb03390edf0b133 32f67f886be12e0bd1c98eeeb1690acae74e7c6e321ad14bf6dfe7bd843176a6 572c46495fc92467f1bf1142893e0edf08ae501f806402ed1b8ba58cc767f8ec b845884ca93520147dbfcc37a28b5d899faed2f821235f2b055ff34d2ce0ca2e 00617cc79d54d01e4bf16be365e673c3cc6bec51a5c337fb736a885ba0acd573 6a8278978430c6e99cb857f4e4512e8278aef7fe70e4b2cbdb0aea6d5baaf14f b0177887294e8717c7c4300e3daf4e5d35721a54fb85aefb99954ad1cf15228e 5d0a7d23e13f32762fe5e66ab9be9aa73336f9c385772269046132a11795caad b229f7653c5630d1b2ac5605a5b2419d7c8ad9879d6d296eddd14a1ed788eda4 97e01654bef78ba3a81c4bcb36b07c6dfd723a867bb2d1e1009574ca5a4a4444 05a16ea1786cec3520ded847a0addf9ac3dfe96c3315e785927d75834f73b5a5 995498a9ea1ba686429c39efe8fc92cc2bfe7a4a3da23891b302d56b18c61075 e4f2da569effe699c788939b58b7271eb5b63347382d7b9fa650e0c16caf7dbd 62b2acfa240fdd2888dcd3ae4b3b60d9aacf48d627ad4909f0ee3614d2a1d8bd 6d52a488709aa858e528c36acdd35cac6cc7a1e0d612ea4dc68a4393ef095a56 da6aceaf9e86d64910f2f90e7e7d30a3459260c9dfbcf427b0704f5d848ddac3 84c11c1ddf0fbe484e80c375f0bbe985133c72d3be646e4a62691b3ddf02c93b 132529a4a7796849c67315835700cacd444e85a11b9e71afb5ca41fa81dc8373 69d1d9c94d25a965f96c89bdfe92d5bd5a10a145312a46d990d8747d9b54ce72 a77f3f1c05b8b70a0203e373031adf0596eed28b5a434cdf0abaa7c2221c5a93 a611e3a402a42a33cd8f832dd46c794c6adc9df2bc32747686ac7614a72d0c2a e8f60d367002f5fda4a024992dba1b161fe1802e680977b2e2f55a9c3deb27d4 e887fde0d2c22351ec22b8aa8889a80c0917613ab397c978b2871af576c76fbf 9a90e74a7e602b11e1085681ef5e08a59827b7976ecd8e14f5c7a81312a9b84f e35b60b7564725d9a7dd34a6ff0dfb5d488e3cebd79f8260a7057b2b2089c233 71a3cdd1e3f0840ce0cbbc9049efc87f376938c4a6fefda0e4364684176e6bac 6dabcb53034fa276be2e73851d9a39aadd74b3ac7ecb0eec92b43608cbf4b02d 13477a1e0cfb62bdeb1a30889489446cdb3e37bed0e868906ee91da6ee4de0aa 1bdb6348c889278979c6064c256adb5e3bc76b61e332b2981a49fbd79a14dfe0 e3c6ad547fcb3afd9dbce9593931897d8f736b5044f543475757c7775a58168d 379dfebd4d07db29982a708e4a03ef03a583e869e704f36dd62da8fc41867cb2 4249977ebd5483286439256595142ce68bf2a88d40fa2a0e204bb8918b2ab1f3 08710fec6ed8004733e622281bf186975b7c7ec6e2a85d9cd7fd146f9386908c 0ea6d13d9634d24f6aa872f341b2bfaba220045e6dfd752f0746cc1b5e9732b5 201ac295b1b5a10a4317748a29f94ea0e5b6b069a8759eccf5bff9067bc1c829 623ea94a4d6de8a3db5e0c19026fa2623559fa878030c98495111ee9b2951247 f1382fca2d626d971af139e3873405119843d0b0237b48d08287d893e7f003b9 5e536ecd5570c8ae9165916d9f14688dbc8c82130e1c589e23ac5ecfa8a39435 f18095254b9f272c5e5f10322cbee3cb62cb40e75a900bfb69b1557c254060e5 d1c0c862d42a95b7008215dcd1cf6c4516df9830ea11c217042d0f3df720814e 4eed5da8eda2ca0a8436e337346fc54f3dd38b90748bd4023580e7fbfe271dba 8b58a9e402a6a320edb50825a07279ffdb434d5bba8c2b93bdf568144e098a01 0 6f2c22cb261d219478f1652e3dca33abea6d499f89ef0fd974a634efe856400d2ba867eef4218101dc6aa7a1784a0e5417169c68232b1ae60273c5df9f3ba60b2731ee9e01f2a75cabca7cde5e1c07ca6bbacf47e8200ea1a42d74febf13b00fbfc4a75d0bc7ce7c6cff9d98723998efa5b7dd467bc9593f8d3079581b011c073207be3f020b62ae3042dc16164cfb0db0baf3a8130efedafb63cb8d6a954501603dd3b6c8639a08e0558ced9d19eb3bcbaba0b5bfcf9b7a0494645bfea2a1070831ceabb102505d420460ed138db3c899d7967ccc5275dc18d3ffca4bf93b077b23a2307da8f5a349d9cad4c606de3f98987376dd412ffbaab7ee231d2d450485b11ad2112b7e00ccdac5ff4d0528edc403dbf98fc80fa222a9a2cdedf26a071115361a13b89d6f1636629d54aaeac8dd14fe0804d596c714778ddb8d3fe10b88209309bf63164d8aa4d8f1f660ec3b20125107f64be1b6fc28c2f37b594209a657ba42c6c6d6af7a7c6b86ade02565537a61cb673c49924570db043115850e3b533ab427337418e45f71253465ac2ead43dac05a6ddcccee7c35df2b9bed03f032125ac29b919d0ba3f8d66ed069a5cd50c0a385b7837156457e38279e9f048aaf5619dfe24e6b6d5c6c09996966fa1058342b2cde0e9222e9d3589aa1d40d2d03048e6532fbf0626592be6f688451db989edb072a42ac07c4b373ed6073095760959b284b7303defc77d0a76434a9844a8cd33be69935bceeeea8e6a90c0c412a3523f8dbe38ed8d6516bc8461859940c72aaa34a929e714c49c6ddef1d0c593abc75fcbb4760570d757cf706c766aecd911fa1f39eb363ac0faaa51c8a0656033c23a6d61b7022a324ff65b023a3c36550f3a60e28c678267aa0e2491e05c745e04fabd9ea474f80372fd714e7ef7196f038c52571244174093dd3393a02b1570496ea8073f2de704698533c5fad5133b820b26571a0adc90233adf7a60c76f97b6c97fbd5e45c86f64bd56de1d9e6fcb14a247189e260665a7566c02501cc9cdb7d0cdaef929a9e985f7a1ee0397ddf6d28a48618d8816d154f963c720119435363fed37bbfe3c0ba90af5e9a48711045fb7dc74c48822ab9ffa4fd450799bc71c8cb8b1e707e5ac73caf719a79964563dab3a765a34e4249518f05120f8dab7510475aa30e26cfdfcd6a86a2b38bf2c5d53204e254caad7c1dc956230fca7d125dcf347f56b0d3a153440b510697ac5f8751195ffd1f98a917d609c10ab6d08a62d3687c0de185f14bf9507a7599a6bbe0086d5e908aed79ceb5afc308117cff4e47dde85c24740a887f2a3d2997c1d74b23f7250b984c2d5a5363a20070c8d95d471189b9986cb23c85f2baa5e97aac29e17e946e50d7b8faef01fa03ac2a65f7daab5049d6b776d1e6b341f99fac90b4a1ffaf2425c437cedf7ae80a21326e32aa74b612349d709db8df7926982d54fff0b3aef125f4c7106473f20573b55148efc1a62126a33c97640ecf4b049b3fda3e2cc9deb6abe35836a5a50edd40bdb651f0cd7c1ac894b803b8060ca4a10ca15810aecede6c986d112a7f0e5815fb38549123c5e6f8d8ab9a3d7519b92224106f69620483247540831f3e05bae6270769f83ff1fdfbad940f2a0cd42fccadf887b7192ed9951d4f394f240d6a0ac5aeec3d86c7f4c8a188ddc8a8dcb2e16e29cebaa6dce9230784588ba40374343ba5a672f37556f170914241dd2fd80a81ec1ff714bd2c27ca02e7b3c2039b7f467f72457064e7e0f39824e732fb9e45f6377a52972171b9dfdd298bb20e331c49243ce32986a0f8e4c3790ef46f5ea8624dcfa581f87a1268df8ebf3a05e8d2d779110cb9de7178730947d42393a9d6725a5e9553f06f292ed0f0229408628d43f7be7320197e4cdbebdff97000ba392a4ec801baf68eb2bea910e9870955763218a9feb91974663fea637048ebc7b2625d9a483a3a5220c2aeb4ec9402b3cc5e939ecaf0cda3c1138310a67523f98b809ec69e16c54bdf2ff5ba53a80e8408377384c963ee06c88e48e322fa31e5c3879df6618d4ad5842412f2a72a01c443c860a5fbe0d348f0a302a1184eecb2e1a1e38baa22f1b31a7a91ed1bca029e5f0a3e587e8b42765d8a82dc5ea40ad4fc48047053186204da7433cae9110d34a73c453fb712c111c25854c43a44dd694ec9fca15ebe8b3288d6158ab15505cbb12d5e4cffb57ca963bcf4ada9b9bf6c48047aa4714357eb21dc2b48fc050bc017dc9825eda51af429ea1eff540613a6408924c3b71259a0069e2b8a915200bfe8ceabb1f2b2dd302523fe467792f6ccc6b47e672676280f039742e196d70432c66d999fc777f18f53ebd227fa831c08ef29680a5e7f2e677bdd294073da0e67b145e0a8fe769add8693ec366dbd7e3a54264a4d1390c3a749aecac30e1902f01f64adb050fb4650f9be43f7b572ce46a5f6158a1732f2e2e29cfa6d0a38079f062dd91540c1632a23ad4b575baa18184f330b89ba63dab1ad5de8455d580b1faad52bff18321dd1c48288af3d90624422b1981739c7f39afd6c7e3feac90edc4e529798e72931c4df1bc7cad406a36a3014054c17da27f81a3fea7a93a201e66b522fc8b53c0bfd6a646fc436f65b787886e3813548dc8b77c633b731330d6e73548eb141ba958c8a0dd58012805fb24814c2b89111e685cfba98ef32c7042dd0dd8904aac6876a9f8f57d5a605c74bbed8bffed8630ca5d2fbde6b39930733c4597d0f74a4906cf848513d123ddfc80b9a9f2a7df0e07c291936a03dbf031a9be8407c38dda941ed36260f6d20084b28247617d6a9751727e549e3dfe60074fdc10c6a6639b125462190b78aab331e9629fc280702f85cddd94b49414c04362c1867408b32eb7ddeac44318d7a3815aa98e9695ce83dd44f18b89a2a8301b006b1987d02c4e22478dcc98f6018a3cb96b35382bd624fa1192a56112fed0e463acc273dcee086daf909e5be59576078267ad831ce366c10dbbbf7f414f106b6f063fe5aa36607d437f200fed767f2cdd7b4188b0cba257e9b854730a8700ff4f07499e3b7a042be35de7dff42b927e1b441374ecfa125ac06ead1df19c806f1d9b3192c8e86608b3e430e97c26d06d96419a5ed15064804fe6f0588bf8f0178f0c55524a3b80e8f61acc72f7dd6050df2f70721641d70b831f21e760d520422cc485ab602b6f63b6bea68933611c2eac5b2d99ba9b9542c8492f7f98a1b07a32eeead9e21d2fccbe1377e9607ded7e551885144923169a102196c6aee5903a75a78a610f140c6bf859c2e635598eb4a561a1269e0d8fde694db98ac9f410b4bd87cd5587b523d25d988730007eac4b115eb5fb4f69669aab8cd1f923dfd0ffbe43e28521f39e8864226fc7e2ebd8a1fedac100a0d58765169e2c40e86a90733f6cdbb5226303a62a2e6ee4d4008cdd998352f8770c4f043daf54dd19aeb00d35b420545d5af4cc086eac3915f3912a18c3c36b134991215e33cb7d876130af09ad824d791c5e6b0c787b38234b2af51f96a380ca8ee47e78cb795bf7f150e77b060d98179d4b61dfc47401eff8e607f492590fb27a485940f83a1e358e20b0eb755de0041fe9ac8b26048c7e8e2aafcb07e9b1d436fafbf76c1a993369c0e177f67dd60774d68c46a66b6f909e05d7bf00c7dd12abeb1f496f87d551eda037864727aa2565fae4b9f6575b1feb846c26704ccfc99e3c60ec1970056f0d70c73f3eb98788d7857c4c2d2f5b101385fee387118e447675c141186a8448e4002ca6b32e572191a452a53ff5f55455eb32f24efda517d9ff34c95d66a86a6750b0cc4f7c0226c1e53cd69277dd6a194edfb3e36d15ab88dbc77f5a5403e23900a3966884fcb8577fe1b14a0654c0d46abd7dd87bda7f5316e378ac49be4c37d00cfb6f533ce332f26eac3c29cd00b8a89b9194cb03ffbea9ebb9d32cd5c5b4d024b649120ffe7bf1bf7aa97be7eebec57bc8f5fee4dd7df8cd481075c17d8d90489d4137ec08b9e6d5e00aec5168c3c9ca8639b3ec089fc35e4c83f9758808906b5003bbdd212ae7925e47df75de84a8902f4f70144499c40337e454398c9170eb1d8568d2e10a511a92392d87389870cab1db1575c4cb9ecfb0cbb8e479e3a087e6ce076ecc5b406c6ffa58a6df3e42aa4f2c01eaf05951d38dcec5e05aaa90ea918b4357481e3f00d14220d8d2c7687c760548ef024de88c4b0884e555d970b0911bda3e20019ffab35d2a3786d093768163d88930eab6b8f0bdc1402de93046230b839c12f2f4181d0dd27e35a4d512064315e8ebd3881468c5fef44da9407de84d4e999d12daf51e3eaedef852235c2db889faae093dac8a33bbdc5e07c03825e131af304af9202ac44675059e4620de0e1bd8acb9eeaf59291d85ed7830c3637445d433a639e8b4ccc56910c92588e1a75cfc266badf62467aea94bf7004f1ddf9ada69a062ed0d34bbdeb6a53fed66e32d761157386758d7077273167017366d4fcd00d9758e9b2d143af4097502b90bd61919de651a54436170531eb0039ede8b91a8b36c05852e7cef7ff20f96a56f9abf7c4fcee2072d71a9fc27c071447943e1f8f9cfe2c65b8efe617af338bb3cb6ea9049c4e0159d5d1c6df8e00e7a13cfd6e9143dfef1d5214f399fd566aeff4bd87ae741d1a39f245bef16d0e62bf2b1f760ad3f1948b8d7955467c4d4e70727bfb5572fbd984b5a098479401d86f4a0b13c4cf54a923ef78816db6091139843786119989dacc04304268bf05 -generate_ring_signature 0fdd09cc2513c1d346346db21cfda3bf3da4d65bf66a91dd87c83fda9b9d407b f96cf77c4abc31236ec66ab0e3c56d25860cb2027897a5fae40622a7908ab147 16 a5f687daffa9505005c73221669b1a728930db51d4d40b78ae6a821e9b319440 07a6ddc48dfad0383024bd700f46f56c88129345439bda5e7bb7f356ce11c8f5 585bb3d7f53e474d75cead5d1ebd12c68757bfe231091adacb877b09e34cf3d3 bd06689f501f10bb2f4d5e3cdda3954a6d55ad18c3ad485fa98a4e83b163fa15 0908f88df8ad262a40203ba72994f9bc919d79acf8f3847c5ae72205f8ec29e7 6ada309079d68bab1123ec762dd8ac04166d8ea824acd9408f502731dda08a8d 80d9f89d04bdf2c205d6955cd4f285790a4282a6d75bb555b1342138b42c9364 77e56ccbd4d1ad228e32d2be7837e3871495df21e94dd1328428251d7bc3729e 6387f7309b7005b5073e718b24b2b7fef3fd84749fa98e91dd1f83912487cf56 6b7e693dd187ad5c38f73079c040366abbf2990dd30eee6f685f0360ba22eeaa 819f739bedd0589653cba095292e436678a1fd38767d4a6e9449f3d4246f12c8 5b0d6f09b5ad89d7cd8ae24e8ba3c2c4440cb8858c2483ff1363037491953fd5 e3a579a3dcaf3ba92fb5f57cc4469d1ac00cf048f0f3950819875b1805e43a46 0b650b9de62dd063d88549fb717d3ebb8fc15b3bf7338457c53b7cb4c2b1350e e8ce03b5b89b392e9d2de32cd39416b7c189f5ba81954c37bf64586dbe707ea6 feb0d23239826ed5813f0d76ebc0934a4ad72186f9e43a7895c54e5e03c55b32 e01f9713a2feb7d4f9a06fc9b89f1f438d990269f8650286146ccb2df251e309 13 ac765696d4988ef5c1851a2811754c799f8bd590cbd94c33561740ee8ab7fe095d6429b664db2b308cbebe1e11cd1b71026dc16f0d7a2c77185767279f5661064361aa63bc00ee9f82ec11682eb89d2ebbbad5a40a4edb46975fe2acb62972089d159877afbccad3afd8b6be179044abf8cc810729bf32871b03fc8ce210c50eb7d4035fd61b1e54f15313ea7033fe16b5a5d4689ad9588a8574dca40621f1044c46468a11bdbf8797c5389a38c4e61ec3ad5a72fb057a2c4b011cf79817eb0412f2fe5eee084f66b5b3d028657f3579b5427109416d8a3f5b2a622e5cf2bb03399020eb1e2dc14f82dc12c3366fa633a2e8110a84ea8b5024bdb27bf00c100d7563681f9cff360b591b6f7319f63e63c884626f33fc84ddbd18ef304256230d50e3dfcc5e8cd0cd3a70f5daba24746df00f0bfffab0b1c9be7532b65f0a590b831cba17eb2ba938708b6275dd4f32944df4afcb0b8642ddaa5a9624eb4cdd0ad49b5e3808fd15259a9f9947882f5f354f3bd57e315cbbc8aab22e180f752401ab7af69f4965ebd53f3d16e5eb7de2f2d1a7d28c1c107121f0b70a613ea2b107dc918908d88a70eccb4d3a073059fea44f58e13e10844b62ce04fdf1b19771043b972a358a06b18b87b6a0530c6c2a5aa34476d99326e507b565e35dd3849d0601d5d2151aa89b6fa46f5ce7a2f069577e0f8a858ce47f7c03b99d94f20d1f036e6c3322e6d78104e9fa5dd631a008e1a105319eed375c0d44a9bc23f0a792094cab183fdae7198c5dc3e308a725e6e77779a3e74a30b5fad9cb1b74791154098c6c388de36b5456f5f4ac89b2042323c2355188aecca9118cd9c37575e7e606f42e16867f3fd94de72657dcf5ce054b546c41020ccb360b1bcaa4d4c75a3201cbfb90415e0b2260d7a13b70c9ab7ef350e57657a8c0a50ad7efffcd913ad403e68ab5931587e539ace64a6e9d8caa006b1587d9f4431b6db73115ecc96d270d33ccbf30b89bbe45785e3438840adf1a940ec86d4cbab1352e023ef351859508e9467d72d0baa6173a0453c2a75357f97606e48076491cb0d014239cff25e50d501fea05fd8a520a11713c2586adcbfb3ac929ddad43b0093f2fbd5d3a1f160d5e9633ee2b519752602e51ce146c9dc4f943a9dd5a0bea027efef575c5a18904ce893651de05976f74564f220ff6d054fba9360fdcf80d7e53e698fca6f61d0943a26e8e7a6782fddfbea243d02e58f9d490208eb6c0ca202c2ebae0342b820ee9d2051b982087bf52a23d898a8e4d1bf412e6a2f1ba5ed7e4c83dd3e466ec074bc3f63932fc711b0defc517ad4ca4f8d38410964f15265d946047d854d190072eb73a4de876a781f6f9263242e93afbd1e01b8c5476e377a2e25ae736b4d30aa3c7ad95ab5cfc774d066d4a5d54b37a50c88c2c5b50c69e073d602db84efe08 -generate_ring_signature e3495fc2ad438c8128a655249d4d6b69b6c17ee1dbf96ed1acea6380d07fb724 2adc1d2a97ca897fe0342c2165b8ac198eceb0374a7320da794d1bd38a45c962 5 dada0d56d195390308e9eb9304b877ed930a1c15de8c0a28ab04066cb0cb379b 85e9284587832bd7e59fbd7d3fb9b37b4b63e801522f187fd5bf45cc4f5454b9 ca11ed3ebfa24d358807b3ccdcf4c623af574b8c07c825b4b4b6f68cfbe9bef9 f77887b1191a51859ab5bfba228a360ce889059a60aaa740a5de8e9d0a7a1e57 c445962d9a4260693d6307b3305fcfd65807ffaeaab251349e02ec4dcb4f2d02 5d0db6d9854ffe9d26d8df64f9a0f5ac01aaa30ff2819e6377724f76d5c8d802 2 9726edc17f4d20ce413103b6f81e8e072f2d8b32098007e72e344d388323c309cea34448c08ab6dea51e3004a3252091713dbee920fd4b2bd41890e5c0bf590cc13450809d7f54f02e880a9e2ea1614c1dc9e354395c01e9a16a709e6308e4074f85935c344701c7a1a4576633b468e2213e766f2e552359728dc87b22a2f709e0591c4e50b0b8cfbe82b653e22bd53bb960fc25e193490e5f266d973ace360bbaa0710a99bf7d7fb82bbe7773857d9813a6d9b8cb76dba48cd6524813764b0bb49038e8c2b5c9d776e847c5cbaf31c4b48c37d222dbb1f483831367b4bf210336b51a0ed9560e1e49263dae8c6a880c80384a10bdc3b87ccc844dce3d08d00e54dc88ca78fcf3b8a79ece99d0ab959e24117a79ddd6df8d7122d68262166400710d37f0f40fed5ca60d12a88e5335ce5b316e3b0432fe3579f852d17390b00c -generate_ring_signature 923a11626de7b0b613c07f95a600a1ae1b62ef2b0bff338120f4aca08a518f0f f12c207e70b2dd9bbfc6988a0ebe5f07f915b5bf2b1f1a8ba23c5db2475e8d26 4 eab9a7d80ad2d7e9060076bda332b1e2b31860426710e7071e5f34afeccd51e8 8d48bb5e57d8c7b0950384093d6187ccd03b0c535a0edd6da35a6f015f928cc6 079e64c24eee1da696543bfb274ead55ee33e3633b9601d9e3512371194dd4f9 6348488a0884315336961ace563bf106bef9f2c5e745e5204531a3e00695d5cf 2d4e461db0a3b63a7c05d4699b1c790f86351dc1fc96d1f8365ebace89d9db0e 3 92ded4fe90d6bdd6f2793817810ad82fd6316c3d5dae08a6a3c21c554872c90f407eba1fe8f74a9fe685cf9a950d5953967e6721446e9de528b7a31204dcba05b56891ac75b91d0b3db4ed9390f2da951aa0fda93ad333665d099b110f50fc0b6c79e9e795369aace191c02f5c6830aaf00eeb2d0ec2bbf6c97afd2842c72b0eaf3a257d6747b5750b2c8e80e01214d75412879129e65eea19e9ca51b3a56d0a70bbe2c0786ff70042c5af0be5e70532c4bf9f556ae6f6f6a701ac2490dfc9090c22fd4191d8bb585c53bbfde6da7b3434f8594c9fe091970b4d702f95b4a701f1dc5b5d5da8d6de4ea4d8cb3b075dc3b5d19ca727b7351f4a688dde31799b02 -generate_ring_signature f7eeba3fcbdc35c01d69774f6d1e4ca270680e87e1af40b2773dac9e9b0a56a0 6f1f04eabe8e9213c7c5d4c4a4df1b5564e63b054d3e09ad81eb6c4d930ef44f 7 ecbc3d0e415e2c5360b40747cf604fcd6e4751e125ce2ed8f7922a317bebe282 60887705484f05a205b5e6c98ec1be1df8cdd4115f0f3edf9b545370d81b2283 4310521f1828d60b124567720d381d203d81d07b8c66434011e29bda436bf7d9 4280fb5b19e595916a02527cb77814868af83620174dcb21bc8da271ef680004 f2f8b898e39ad301c969cc191c11b162995a5e1f813dd63aae200a718c7cdb86 867556a39a4dbde3c7fabd5180eb0e7ce275ea211db2fe90e13c4c040c0cb28d f6c1111d0bdc89c508faad4ef2015ea98e1f5416324def724136e030ae647603 f01ce7ed8d9dca5629b5666899c8aff6ce0b58ad2fe04b77ae75c2ed6b9de30f 4 d1d5c570070ec989388ea4d44364ea27aa53ec4c11eedf83758f400ca58acb0a0d4eb20141613145a633851b8ea16201d5b22110a2266a2c741c215387e9c80d8e0f9ed256e3c9b4eeb05587f955e55d75dd1aded253f06d6e0d04d2a3244f00b25e1003621b78852f0322b4d6b01b6d575f53c1dd15afd2da777db269b630060693d4cdabdca455e04ec1848d8aa7284ff21cc76f28ca23d802746d7d78d205453269094b5e447d25b542b82971b530e9d29a0062f41b89bd7e2fa65659420e2052231ae20c626305ee1cd70a0936e6eefefe45c6054ae1456adc7bd072b40c43f1bd106771a3c60a089de39917b40a1bca7ea2db73179bd83053819379a00edee0138265135e9dc5112aa2d0039fd46da55dce99cd7747c78d1ca54e4d700877a33ab50a5461715385117df6af1b445491eb912f4276dfa9f29cfee8ba7b0bb12da734954ede4898f69ce027ce93ea727c44a4826663e515591ab7291f510b85ee8206b08eb23e248a4cf1946889ba4dd044c5f5aca34ea97a88ea99a54609246f3af9370417b950a0a4854c08a9e750d5b887418cd7bbc7ad8cbc0339880d9df3d2619df12ec19813d790f8827d3ead780ea4131a3908538ac673d348c806 -generate_ring_signature c4dee30ab0373ada5dee59e1362d805d6a2eb83f1a878dc9e82e1e9dda566d60 66250b52e8ea438a69f00cec532367c889ac73727ec2b41f696bfcec68b5788c 169 f2f2b60d7675c181f68eabb0946ecb76d12c2ae176c8d7f2b2f5cd7e5390b732 743abfcb7c102fcde3281f0989bdf8aa23a38aa28370e9caca172f5befcd9fb1 6925c7c3b98df3427d346866a2063fe956404bf6dd718ad32176af5f55fa4932 e13d9ea26cfc623476efdc90ae5e746d5c01ece1c662c1b93e9cf19a70830b7d 05d454706821b18b611d100393ec3307138bc84b0c0958cea0bc4eb1e9341236 cce2645b7ce500aa4fb89088e6ba8b90d488e7adc3403850b6fd762500c3317d df18cc2a78fbe16f092a779a7f0503cd26e45a8258379e4535d97a01e4f3af7e a9d9437498986268c6e2594b4932f8ac8798254bbbb9f17e9fc5be12aa35beda 9a8f7b61754085f10a5ddb5a7bc293ad28e8365f8b51e94a18e974b7b536a883 3295ada3db3830f1a2887c4f1403eee920a836108a42a748414da663e7fe9108 80545b9db676fd424f717135f3978b39415e045225c2aac5f340af7169e68b06 3d9d86d009b0be89558db01f5ab33c2bfa32242167d6a7afe06ac195f6c500a0 37662037db5136dbe094d1eac43c18c9239969f9d3e0039df9679cfdb4f3d18a 3bea446ef619e281d5fc24c358629581d78385b0556d72fb244c66d97e2c6483 a28b168bb36453c5fc8ac19c55ed117077e0f450eab8972815682e1fbad7327f 6e28882488bb1a06b52d716767a61584235e7351b839b84fb2d340230dbb75c7 52a7690a81457f57c9e79fcea48a019f3bd405bb6b66e61a6f63d9151d719a7f 9a80b4b0f25383a2c2f70d94ae5a4da7b15857d3242d05669e84f02d66e92edc a801c63fcd5ae02c607cc766f3ed531cf1e94316ef2d2989ebc611b200a300a8 34e4278c824bf5d404204de86aaa1a806ce70fd062eac373faf51353e9fa6202 4b6ff12fb5ee85425a5ef473502e22bb78aaadcf63ad4d93ed327bfa691d5a40 27dfd189f1ba9b2fc2b0d48bc6086b1a4f242130ceeead85acefa3a92ef86ce1 37b68e0abc377dc3c967591e5615c78e49ebb08f1b77952b5bbc8d9696f2aa7f 6bdc04cd1004485d5c703f63811fda02ed60d3e90e0dcbbc4e870027807f7db5 e106081b39c6bb35c5f5607e57b1eeedfba6a6060f21ed360faa2a8ca5af8d1d fbfb46345f0bd2e1711359b292ebf8bd6a7e39e531f986591954979fb09fbb2c 37b5b8821be52009c9e93c3ff1010474644f5b2872a8736650134e21b387ab31 c83fe1124f489b72f050473d52932fd5d68f2dfaa2dcc21136599cb918b135b1 cecc1ee7fdfb34c04581d11c61b300decf57a42e280edbe70320993be72af60a f2b51106e9a9d3d144df1c152a80a090331df2efc91e310403f0a05b9eee87be e17858155d4549918dea501437c1148710a310d0f90620fea070e83e02e53d12 46b090083bd9288b6acc4812e363601ddc83c03ea67b482eb55d2a790b77e4f8 bbfbcc1daff1f526118950dd3e2709c845523e19026123c5369a13c4d59939cf e075c3c1ea09cc5b80799103ec90cb60576c33c3b93a73c24b0d1ceb45d295f6 af65af5d55b0d0d15fcba45eaaf58ab174f985bd4d41c2d20b7a4526247c9714 80bb8136d6e99bd8016b594c39e7b137326ddb543eac6fedd100dff6d6b4b5b1 5339bc37bfce89a3dde58c7fa4db847a4158508bb50bafcacf80255382ac7671 c81b42947cc5f69eaa5c203eae4df3b3d020dd3a0c5018f8a9f5e34348624d0b 6d510f05dbe39d83d6640cac35d0a60068331cad220b17c520f23fa4552fcc87 d25684244293b811b3551f6dab2a5050fe9dc2bce52c15740a2467009222ad5b 9d87b136332a86b9c5bedc40467ccfb345ca8baeea24b84ca338928ec5eda14c f2e3f7adad9febf75773a3c857d62d3263120100e2022ae9b77c58b3c7b894c4 d7d5c94f7e248cbf878428a2b144af620e4681da88e766ae43b1d6dea2169967 30efeb1e0e068b2c359373c7add7676425c35d7c7fbf3bdb90b82dd824008ddf a761d5d202b96868e878551709c2b5ab877d7fe56995504bc9052ae0f0200bcf 55aa7d97269864c26740e4be038efc3b0c5befd21ee78be97639826f5f0f28ba beaa56cb576bc41b36f5751c3c7251a70398e4db0a3c09ca35791e8981ea1f91 83d7a2343fbe5165a375540f07a124513fcdc269f32c397a138f8af02a44ded7 26efe640c840ea1a9322378e8e90f84a17627e792190d3753a4d87bd730b18b4 f1a97f521feecc1049496d25dd3648443c601de11949d575b0c550d8baba51c0 d3a56987efaf33ad73e78557598c79892562eccd7a36207e1b4a8f2580b77bcb 5d74020c3d7c93bbb99d658b8fcf1cb18497208ad1c44bcfbd8a8b956646051c 0a0c3a31f1d7cdacaebb20388dab4a8ee07765177f8cbb039719985a5a942e74 e8fa5aa4fc7d0821c6c013cbafeea5d28eb6a24c9db8779e0be56f87383a3acf 78c803ef1f3df340b6cdd5855289dfde75746ae4fb6e6f79b902da3e6edcb510 5bde60d09e8518e581b09e441d9e9ee10fe68d206984d8da451be2969dc6351b 9f0d4380fe0bfb2639c08219c7c83b8d0dd4f8a37b9e7f956315cbcc91df1fb3 ee57237455930f2d6a13257fdbc4010bd95502b156592117befc744e68b6a4bc 05845a51848c70ac4ca85654d54962644005fab916c14b572137bfd21c90e893 cefc22e0ae4f7bdb5981e9af5226769bbfb4117a75bbfe9d0c0aaead403ad02f 79df1350f974a43e500894a2ef4d069347a199579a198bfbfa7309818f11cd3d 0043576d4b1652fe8a3006e223fe2907221a22c73ae9dfffb4af218b970c422b 178c74199c65363956e4c567def65f8ae5fc0ddb7aa7cf04873db552a18631bd ec64d4014cdce524f52ebaec746e4cb7985db07efee7f230f7a45ba0ebaf2667 61b2796c0984c0d366679762172eceaf6cacea13c448f10c01d9b95a3c0bd932 78b5e5e15304a5d7deaa7fb92d1dca0db3e8caf4a621596fb5efc9e45b56763a 8307fa27a58136ddaab152cb540dd912a5f9cc467f87529fccca7be31e3360b1 bce231185b0032910b1983e47b427164c87735004579b80ff136b48dca7dcaa8 14b20e79d48470cadb4c806df5af9b034d8df8097a3be1f2d7928fe34d2b3cef 4247fdc58e6f242735df1b4fe9ea9fa87c0f93ec02b7a1f57314325ffb849cad 45c59346b2e561a5ce513d69d53d60b0513d0c34fca33d1a82147187c05da5f9 5f7f797554373b48cc0b9fc19fe98fdf53fd7dac6895a3f7a7e773f616501c35 aa2bbb46678024ccfc9a9087a282316f6af2bb04c3757ef7228831d9833dbb83 dc399f899363da744d9d4153065b6b684bd658c393efd3590ecdff3d9c2ed52b ef6d0d6e6176b560256198b8e349dee4afaba86decd2bcce727dbe95e2b70636 d32ebfc10103684af4d1d40e94d595541aa541f4580b1f772655c653a02051f6 7eba6d64022bd3abc8a5c86a8895bc2997d77dc9a1945ce761b65b15da53c6f4 cff49af65ffe5745dca4cb015ff2c06015337e90d3eee87ed64de0490989c684 4c63ffd66a6dadb52cdf67ddb33d710dbccb0207c1e5566ac61f22a21bdb129a ce65e5281d0168a505605fdb278f1898e30d0a941f8b0f9764f13179b3401aae dd903cde97be0cbe8286e4fcb959f165ade330f86c365bc5373cc5d6921082f4 38346157f200df62a9caa5b51c0b39266e67e8c8b22f31876a0737ecb3924c64 7a59f714f0e320e8decd66d486b6aed8285ae48f77cdbf8db8496e43cf327dc6 43549791da8e97a5b368cde89d57dde61b4c7b1826d2bdca03c98547d6c1f210 5a8087729bcba594809cf4a31b6d2a66713d11f44282fe19fee4555fa38f33a7 80de3bf86da79e5ae3c839ab0a723291d3495dd3903c8f64a1aec3c388e774d9 2953ca5866e7d6eda4551ca518fdd1d8b88f5621483cbb9ecaf29097ef6876d3 bb9814f0e3957bd5dc7ce3fadea884545594cc240806159fe497ba5888164654 335a5909bb1f4401db26d7e55f726b209d154f632e713e1bda691e1346fb1fcf 34865ced6ca72c9e1ccd2635f6ff2a3689d3b968bdca016b9775e4df7e58377e cdf0adceffeb97350dd41a3b6ace99484a4f4f7980ba08c3c22bad35b2d25622 1095dd8c0c3555876d83821ef96927bcdeb3c33f5785b7eff6b23012c84e6bc7 e04fa71c25a0e3484b479825afbaed78bcca81d1ec16d0e02b0ed26732ab70db 383dd6af2e2638681fb025379fe1a58438a5b71ccfb44f62082fb711937dd982 3dbe16c9dacc2a7c3d4f2a7aadd7323208de7e81cc92dd4f8958768574ba8d0e 1b724b508e6f688287fc9adb44f097748eebc537b8a9ef2e5ffcd5eb0db256e6 a8ed0d0d34c15f7b9bd8539eb4f0db1a73703f7b1a684590737870417889d75c a1ab8437038769c0677d621cf9bcf25c02fd4ea19386e18e7a1cc4e27ac591e3 d8263c35b3be8a8054c7bae2c394b6c6dbbab44c0a3a8ecf0d705dd1fc8cba16 b903646b44e144d83b2a5338054fb318a0cbbaf2d4ae28f4e683241c72a45645 f66868f006d85e3cbea999cc1de9aab9dab8ed420e887eb6d8b8bd5cab1094e7 f062cc8d8b539657ee47edd97f11ff9b863157b951af13a736b8d16c82a84363 8c9fcc3758b9ab2b89d1ee8457b0d666087169ac25bbac716414ba5587047695 bfd1f74391c25ee492570d813899533797a11e321ea9e3461bec229061206ab0 455a4eba5a915e05782377566b72dab647d9c633dac408aaea6f8ba97581f044 e0c54ce9fe9341fafc56791db8d5ea68e8cbbaf222ff855120afd8dec9f62695 377c0338298c3f0f80e509182a3fd5a40c81f40d15b46b762c0e5598eed8ca41 1c107e944813fe33970f7d5cf7e1b12539e36364181450a0f60631c3b10f9813 8997bf02ac05b9491cd822642d487c72084752b5e4150b0c703e032418d754ca df6b6f5942e2fd0d674c709dc04297c935b33fb597edc3a5734af6196318a617 ab7f3b2f0558840dc839c5f281a190d8277534848c9f64534d9b47f887a54809 78ea46342cb94abffecc038e4e00ea9a5699dc34f89dcb625b35c09c31c20090 209130802e9d6511db53dd050c8c20963375343d5e47823fa970c40ffc553c3a a7b4251d7524597f481c5a5308d1bd3b3c0254d352a9a5b78f18336eb123acbc 7dc00f08f1299dbf52bb029f19685213291c8cc9508a1b2143d5e7b7b18b1882 ea3bc724e789117a5c484b5b84441b515b758399062f2bd98209706bc9a8c3fb cb0f6539b7b8dc5dc22b2301428b9e9a2f6ab08787853b664f6845ea9e85d9fa 315c2d8496df14859b7901592b800da7ae4db9f8c92653dc6d0c108e8b4e0eb2 980a0e7e3ee524d557a0bd194d2b81c5c305aba1c8c195691e4d4ddb4d257f52 41763dbab5729980d1bc0f26643e657852421493e1bdde3b0ecc756780fb6c57 3ba8348fe5bfb2577e997df67623e132d5a290c2eb381144bd42470baaeed8c4 6d9b9089ee7f98aae119f6c6b622bdba6955abe4e874a085245635b8f2b94393 4c82127d23c46e3053c6490ddb8aca8eb567ce3bb1c3972e26a4d47dde728567 5a9a20efb7bf87a0e7c91eeffd12efbbf72d7ae4c0a8c25b9d9c3a4ff8411c67 1d186b6ce7892fa7a1c3f4f00a8b1fdafb82d97e9e7aeebe18750f45d114069b f367f4b4179fa6d22d6d22a828c33de8ed0298cde5fb5d17863509f9417af0a7 2811d5b3ed14736137126bfbf65204cfe201c5298f70aba9d4d3d0470d729ae5 cf4b99cc377fcff0b7ddc95b4f4b523c2310394ca5bf1a17e802afdeb906cf32 e6a2fe52632d2367d577c8092a66c377ad37ec2cb209c7786886e5704bdbbb25 4ced786fa5fe2666af4caf36d267efc92906c327c06d449b601005fe4ecf2ba3 c742a00349add70c374c25e31f1e192e97705e210bcbe81294380e8df3802b51 6bca8c87b78e364890984fddbccc8c407e512a5e691f8c9e4e04f526eb27db32 33f41b7270fcceab849b60835b0c5b8f3d0a3362db18f6428ac8d6cc629a52e9 f6ccb30dba421f16c87224c1fc6c910c28d928947aa80a4aadebe14b5472e3a5 b75d3c6321f144f78bd014df235649645bf6eb8d15a0b412c8791f97dba6ce96 1ee09cd0a0c0ffe069acb5feba91b0d1eb78b810977acc83131b03415a3fa2cc aa4181c9559f8e935047ce91591939a372dac2b5daee47209472f919c6035a02 147b4551c883e364fe17ec3d1e9a40592cc27f8052e1fc7d17a6c5ec24e2494e c9e70b872854e07077e80d0f66a062e15c46c3e108d7cf656a5acbb5097d38cf 32e3e959f6dfb3e9e3bf1b1fb3c822bd8bb584966fa11dc1e444d2ce4cade8c8 842e0eee7a3fe30185d75956878c418aaf33dbadf2d95223031edcd9334a14fb ce6acedb380e0506c71df56f08e37c9cf6a6a7419a0a84d67421f8c56524ce82 c4dffae7b32ae71007f42fefa57835500094a53a7e59b4003da57be6d752a1df 7c3a885c38d04251b942ae97223b302ea5f38383fd01eab497709c42d5798a6c cb5531c9cae36b968f165726c3918cb6f0904779851d8ed46fd9504a90a39776 7229ff35e6048fb93a2ddf96abfd5642442d91ed4ee2928e2dd2d9819604b79b b79d0f869bab272133ec14603e03fe4cbbe5fafcd859b8751d5cffa2f13067b1 49759392147b1f835d36bb592ab1b387a433067c4ff3ef27c65046c868028b51 466ad6d9a84e0662e61b8a8abb0bf5df7beda276d68909d944a28ffdeaa8dbd1 a3b15d7dc0194fb2f1aab6a358d2b113f8e82a5effc3640a89671c76e6a70dce 43b84023745566f7628a0c30be507cb86dfd35b80dc8eba994cd1d2e0af685f7 b8fdd27e4e758747e0d303623d0eb13a255037f5154e0d869fcd2912436d0036 1070a72d4d5efdbef3183f5a0ac7699ca4f91f3d17883f44bf7fbc51b1fae1cb d4506ec0ae0bb3ebcb7d481bf5ca8a68e19125b15df4c15d955dbee943294e55 858799a67150b2fedf3544b0f1e6e3c3d26f7f886926f57f53dbca535c8f6cd5 3c2938a328956c51e42ba52e7bbbbdd2d85cd9ab1807ca2451822ae8002a9f47 2a1e89c0299dee6608a8652416d21ee18b71282dd0772c65d6d2af8174de8890 c710afe34c83d62d6fa2713f8ecd695ba6f7c3556f7a84f9f9e817abde3fcd14 9195d5d5b1a96ddb0f8366a2ff9e0ba783767d0bccc52449136ed07c05eb1729 8324d8cfbceba7901fb55976c5fa6acb13005fbd838dbb42e77495c867adbffd 5dc1c7243b65a5a6de9bfe9493ee49c369548a1b0f7a707c50b49fd0f9273e5b 8cd9443a7faa14d017876ac6116a4de3633059124c9f55e71687317ec4a4d4c9 0749cce6e8c6eb0b30cc9599b9d1e2e8f536af0a52e3883c9b1a0eaf60bc61ec 1900841d34f0df254a39a2d586c69ea435e108c903e9dada712075752da3c470 72fba31c4645db0e3fdfd6c4bd4bec66dccda0e3b1f39ca0963dd6ca237eed10 00de416b0503948f1eedb67da56b1b6b529f648c69f43bcf1a91bfe88a8c0e15 40b401518efa872876a64431ac7463a297acce71697edbf95bd5a4584385a91f 6300f0d82da691b8d88cfe2e2766f7547cf1f60ffbe70ab984cf1b8946334fd5 fa3eafab921b0a825cbf4841a68094cdae5734aee61725ae8c598ed4f66d43ad 45f89106066fb315ab9004ae5031b1f914599c3b0d9f59cd27ae15c9289dee0d 51 3c6442ea39ddf970a32ec99981cc22379353da6e9d1cab8e8e50884607a0c602e9061c65e5b9e672ca686424789d1c9dbcffe22fdf71803e1fbb74128599fe0f040bb7f29bc4e77d5a9ab59861bd404447bfbb67754f590caea92dd1cf4fc9013990b695f6e568e7bbeff770bf91470c512990400231d5b3d599b4280e93ec04af6488c19b4a3ca06496deb75caa301c11ffa55dac24b65410880469819ebc020038ca92b7d9e95593db5b18f3b21df81cc09c8a2573553d0f9db6663aaf4a0dff23beab7cdea3d884c5120e3655f250c73bd45deaca62f215d7bdc8fbd5c4029710911d11bbc1a761605af978652d0e5a5cdf10f72d345383a71aed96afa4096a5e30329f42a47ad0720eb90c71819289be796af926c1cbc47ab7ce55e68b0b26f41ceebd57b764baa0f0749276e9a36e130fc7ad16319654910ab6919d860c5f9ff00fe4a05ac94194b76a0bd100082ecb99d4527f6695e4343d82de893d0019bd24a0fdb9f93d32f57f561f8fadbf8378c23552ce198866545e3d8c5e58010f70fbcc6a681f45a59efc1c35e0b78cf21f215af8dda66491e4182727ee5409e9f9ed297a93ac77f407e7dff99ac87e293b53ef61d47ef8ac33e7cf3c2f3b06bed5e8592ace0a8cdd0c8b34b9c4ebf1f25df937d9719f28ffb70be58969af0b6de07fdab3b3cfd96bf71cc40856fcbbd83ebcf9ffcd11e04c23e90a5606870cd04207d9c39edef0551b02f7bd5b8ea5113402c79e44902e2f6e951d326d840259ba877a852645c95db0c43de422f2cce641aab7707f6ee91124e480851ff2086884962d87055c6af3f0cbc423f13a2a207724b78b891d009cff24a39baf920c964bfddbc79500db37ddd20a86c264ce5b02848f1880487fcd8b27bcf02d9a05d720ebbf15fb81199e7401cbaf70a4216208dba20e80731c459376abf8669b06b04e3a83e818b1472709f3c2394794545a306a4b3b3767c48dc3a6faa649c5056bdfb113f882c384369e2f6adb727bc9a7946cef0f32c8b48faf148178da5104ff53d76b8ecd5f4007a6c35ee49abf8f39f8703ddaf4f0c2747df46816b45b0e8ad2b3597f25c3cc12b55b518f32f6a5d3ab7f1e799b99c15b2323c62cac9d0f36108838b0aa3808a24b4a8ba8096679e04df0a245e66a07687b23025be5eb00172fbea7a4dc32c5146b3ad68f4320a6024a6162ed3fee48074a4ad94e17280c35c1c4a80a0da6ec450f98844d06a00b6ecc2997e915f9e9229717228476c6023385a43568e0193d74a195cd5cacb70655f2981e5b0eba0820c7d2c54f030d0b3471a08bbe8f995e877553e929b563a3c588dc26a2fb9e0d56a072bebd9e5c0bf1973715cf7f4979c7df0f60fad6a09b2e5671f45fb0e1c927c981fe11ec5c03ac5f336a284e55129225b189e72d5535afa014fab4135a450c90b83250e3e7040972372e9088f3e027aafa45b1f2a07c72dd34d684da4e0d0f3c3122274f0f0fc6952ba6d5206612f88b34ecae146a278f9b1b040fdacd66c865460a6d61b0061c747388bd4b77fee87af4985b4a209187bb4ff003770ffb5b97a371dc29cf01070525f9047161bf19a0caaa5365415600acbbbb412a58544963e98354b4830063e828d46d0af3192c4f90512497bd7d4426c24393585e33c8e40268c4d0440aaa728514e34efb27a4ff6139c55bfac7a016ca9637d54c68b598fce6c272730c8de30a61c4d4f1a20b70aac96d9631932b6ea54d40dbf3525a2fb01a8e533504560e95c2875ae26d127c83ac59bc851d8f9a6e2e8dd7767b1de96cc1388fce078159ad92b67e98859c7435c7f1283ba62fd2f31ab2c7f2ff964a2aada5d5de0f6cfe623499f34d44f76225277b85133ca3d9ef345362c809f55bd7da5b780103d1295a273c2dd679845a61f45ab5bf4f7bcfa3cafb6247d2596fd0ccd73c4207c265a7b0702168aef5c160b2b4a4181f28be1506ebc0608d213d317bba66930f7bcfadc80899814d8d1cea557e6bfba6b9835faa4bceaeab6fb9aa36a6be3c0971579be8fc5a276c9729a6d2eb7accda508d454089babfcc6635323ff9e05b023e10d8ec1b81eb32f957e80fb590c3bac818c4346887b7964361a7389e3d6a0270cfe65ecb137b012498f98238aa4aa3ce21e5ac5b633f91c0eb3a8d06f60f05706294084291588533456ce3fae00f2e8a7c2e401246dfbdf792742ec5cdf30eaaaf69a2ae7f2de0fd88d22060331ec8d4d6b07fdcd3b76d7d60cdfa4aa1f10c44d500920d0978f4b2b48b7417d71600c8bde88a2a27a7a5afd3435a66e1390645ec39246dd6604764a6746500c1b51b38fada0dc2d5ddae6bd7d988837e6506ba5a85f804389cb95409f611db128eda9ac732dc6da557986d790eb55d93ea00a3239ce81767d9b711a0885cc6ee6ff7c317a4c013a5dafd487c2a2dc02c9409fb65f4409b697727055ce3034ae493c9416360b672ef9b865e451eb2c093b30bdb8495d3124aecf44588df1986cc838784b483500e089aaed53a041f908b4409ab193bcb2b84772e6d329ed67001def3c9ffdb6f37a78eb6e7d80b3d8cc9880e9d7dbeb38b7dd7ffef3983d70a6313f289be6133d84bc275e20c3a5819245a0a6bd353267a96ee1ff9bf8d749d8da8e3dcb565059a7ec5dad37d74e8d183bd0a09f1ff6e9851fc934486185133f9a140698a6b93ddefeda94559106178a9dc03fa636fa66c2e8acff18adc1b4f907194839c4a0d7ec68b1e48dccf789aed390710d13405fae2fee90417d0531457d761e9f0dbe93b85adc8257e79bc5d2e960e9c05ff0d35986b19af811e08eaad9f8fcda9e3da726a4cfaabe6ff1b93b9dd09265cc33773e8f8f675885b7c902dbc569ad392c1c801612382f3bb342eea04065fc7c50ddd2c9312745df5d88da3c9614272290694cf0bc022676d2eb23b00067f1cb0994d6f6b05d2674f3186228d2c24b45591f86a083f5aaf283826386b0039fca8b83633a78c54c66d9463295319f7bd20e314e85fc20b1f11c2ced7b40f65b0285093fc2a96920545a6f4db8c3aace0b53b348f8f4cd26a56463d210c0b103f12c239b59c0455b490fc4b14a8eb0c0bdc5e30340859f965272a15306e077636636e3c8110a6ac320d546479659d2771ac6c7567ad3e2a4583d86181e70d4e1bdc7fdd3fabd855a9aed2e476225755750312fb8d36a6fcd9c21aaec6150e2fb10e28fee90d52fa914bfa462168db1b4cca4fbc9cc5b4815036a831ba0208240f974d43175681afc3c46f6691679c669c077d8bbdf7f38f9301743d9d9b0a03935168a8be34465c124d8c69a5b293f3734f327132ba9b8890f253cbd74f068d75fac8adfa2cddc3ecfca6cc8309e8f4554728e2e7ddea307fdd4baf42e8002cc89691beef439c652969dda1b77f6188df0cca3272c6f93259243d40477208eb5e19d8523edb2046c34b8edb4286185a31a1ee477636241e1fdf84acfb050136da75250acad28ae9200adf517438c9ccdc1502f4fcbd29cb93cc7463696f0ec6f6ab06d509c80947e5fc1d0088f1f4b6cc93ee31e386038cceff17ea9fd009bedf172e946fbb070a1105800013e27c478bcfb246702443a232d3b9e012a10ee8ebeb51b4998669ae0a2ba516689c137dc383a582fe783cc894eebc45c0d2025e8b79ec1c37d359a710ec5815cd9e1eef3207fcbc371f28182c69fbf638d709629b98d657c09331d3dd3e109504850392f47b911f5d5c8c56cb9deda20f1301e7c73f4d643ac20a0afa6d03f51f60874931bb1c19e22dfaaf5f9ba29336c90a659e634f0ce5fe4735e3555b8d000b05a59ede105d693ed1a42bd4df3af3c80034ac956f34a72e7c8a7c441c36c713b62ebb5050ba8b96504de23857aaef2b087f3c0bd490ebdf3e3998640d5c25992774de93f4c87092ca31a8e99f2e5aeb0aaf667312ea9aefb8dc3e06b6caab6a8a83599c40e7be8fbcf233d263b701ed00f5be08c919445e4f1934bd947d84730547ac9c3356385f6b42fe636d227d2706206f0394024e7d5f87dadc3fcd90f2ef2b4c58bc76197e137bc94c4be71e9e0ded4cf93394ffc3b271ff672cc26818d1989b3cfe2dd032e38218fe175b280205333efc73762546bb99a2cec78e04edf65f7313bc2ce4fee9dce900da5b8d850b6bbe1740f86f996cfbc8f910298fc42869337526612d36606dc50be86c770408bc0153344528b91eec184877deeb66a30244781f816f157fc75630c55eed85057b5bf05f5a533bf4ebe7f316fa71161eeeedf5971f86669416ab28a448a1f40a8e2cc577e283f69194a54678f10f8bff39251fe1c9906c4fae2626f020fdea0e685cc657735b3e7175672b86d12319d6f2e3b9e560ceebd141f98b28727db90ef586867f0ab71c2a797e104d58c85584b755b6db0bc595ea3dad470fde59b901c3c7d2f7f3550ad8dba68a76eb8c75378728a5957abd83e76e8cb414b51f450179bd1e71143b8f3bbe827459924f4755ec1470748997c521e85d6a54dd8ce003af2f1031c72dd8b3a0b762659c4a8ad89f8a015fe1fa3601fdc7cb817cc762094345c4517a582bede2c49dc2ca0efd3d918cc72a38a87dfb94a40d13fb8f87058ba1e26fe41bd2437b77ab0155ca8eb7008f093c0f95536f9e11b8642405d800c7b4a7abf8ec6a515062d74019c27c9a72e640dc5bcfc35833e1571e65573900dab64c2ac62961c4ae1abfd5adade0df63e81a5b06b0db7d7da51207bda7cc0d646338b724258c01e527010869a3cb7a2b2083c684ca0e138bf6ebf5457e800d57edd6593ad59c3ab29a332e406aa412052400f7a376ebd2e84af31ee5f83103c80519a27e4122474f8da153d5a8a1964215554b1d2e6ed155cd41f748f221008d80a0386fbf49f2cf4fba206a9eab19d126d8c76364d4d5c96a3a809d88690fdef26a611de7e07f6232596614c30778ac53ff6e1f972f4a22b2ba726590430f7112b5477870fef08280d2594c7b1a3c838c2487493f3831874b04ae8e262c02e541b0895a5ca4fe7ed4bc57b39c74e172fee1c2d850fc7a93a66cda29642006e79f170262aef3a0c8b071769a03ec93e1ac5cf093fa1fb86a780d32e946b1026078880722f3cfc4484556059c773e73451cbd0c8e33536a3760520873d7b709b9811bb2275c20af98f619fd506a713bba9123002f0d273949c3decc9b7b0301d7e4ea82518404082d8be00dd14b95d809313449882fb4fe57de3b2b059bba0d50d339cc8fec4d3f86f321db085e4de1958922ebc5ba5ab0aa412794611bbb0e4bd66b2491421698baeac7a5d31460a2dd5f9424fa877b2fcc883a122fe25d0458efa2fdc5bab93689937b6125959611f2a5e23907383b90fe4cd6b3bb0e13078baa20757530607e8504e791baa28756119d23e7e297003427ba634fba7a550cb520a55b6002321f2c373231e903b0043ea67b0753eb3b532af1531d4eda1000e42e54b35c794162fe4168a9fb725e38a204c89175e10dc9c5331c2acadec40608e976794565cda35e8baeb99144ab822d19ecec11683509aa99ce1c01def70139a14ff103a069931f3fd3c6af739fa09f566afaa47bf8f594ada93b20acf9024b1f77fb0eabbb6f2e2851ec10adf7b553c2510c2b114e0a3eccf85a3432270f7cabc434f0c2ad10e825d753e1ecf4fdcfc63e7036803641eb3b27b10cf8940c73d01aad67d2e4d5a118d6e9a20b92d74ffad3d8ea49eee90e4ebbaafc2aff04471a239c04ed6d84365bcc6470ac3787670dc3f1c827de0a88f9e0057876ab05d07ef687d077c4aa8280c0b824f00e0358a7be94b322b3c72cbbed9dc4ab280dc79c9c9df7f0eb21cb3de02d3adc6565425c1e05fdeb28978360ee7e58da9a07ada373599348acbc308d2823dd9c9ef5fcd3f12877ac7a64051060a276c4da0a77c4a983a8b66cefd5d3e1f8ea704a8d25b745a80aa7d745408093cac592c507aa02c93c5469e07fce33041bb2a08b9f3067759890daf487a60dfc042aef860cf486525b1d623cc6c6848eb2ee95bd69ee4d6bad6015b88c027d54063cc69f0da7612c79a5e2d053f5d2784fca726892e7e7fd42e6e1653c494660f2a0c31f0042211aee84d7eddec65f608e29f2ed27b69c98e662d115878b58ec1d1833c406116cfa0f87da7e73133490e9d64a9e48adc2527a4a8c96fb95ea8fb42fa7b6099db94082d6deda23bf7052436cc1ec94fc403a7ac7e0288bcf27d5f8a8999a0852cb23c90412060fd83c8df6d3ca7472273e3c540825829aa4658d854a93b60d68b4d48fa2072af03ed134448c3f84a5a0c9d6651c0643660842ca801b16420a46fb44ce678b383ee44ab633e89352e85980f707e35e2f53f43a9691e1f08b021f5e50f79ce241a3a800412ed2bb01db69acbfce6718c45acefdcf0f1b54f60e6c89d7b2b1639b1003d6e3857f28deb70f9434ef50782d36c935c4c7d8c7d60b4ace2816e0f4a3c242c88aa1a51a07f3bc344039fa520179546db1187fe33a0875d55266f6e3d216c54278c4cf9d81463b9769833b56cb51091d2525b1a041084bdcb794389eacb349768b9ebbc2cb29536dd193e95c867154ce7b9970eff409972ae2bfe6c42d8fe7ea251a59a1fa317fd6f090bf48ce5072fa5cafa0feb10a3ccd7ee2f7a3de166b39fdc7d7154759680a68fad5848de49360c62233d52508355b1c6a0575beb011a82eb093e1101dbb66815c1f81b4684557ccc3902298033d8b70115936842c0a5a15cbd3329a7bf608087ee52c1da871ffcc20beea580e9870539c824e5c4c14f96db84d9543aca4cb7faf21984b7adb46bd557362080806e6dd5f324683890c97b726e42a153ab6a7d6f56fd590ea6626fa4809538905a18c2111cd82ae8fe253de387f163e2f231448b46979e2ff22ad80c44d1f940e2454ad5f17603b879096edbe4cb1066d86d2e2086d04d884ff62d93b0e537c018a0e42d36b3e3095d60c177c0441611849bc23f9193b7abe1289c23fd8d3a906f79aa3250569e1e1655c9254960a015cc23c0139d9d8a85e70ce366fab470e08a595fdbbb0730a347f5d92e22faf16f5d98c4be26f5dd2abd8d88e99e7d77d0d43b179205190529c7893bee2fa185c2e8d9bffdad1691b730a3b8efc76a62f0baccee97a21a9eca2e003d7844723f2d0a041a535ff89dc336c008d9323c72b0b65e16c5e51251dbeca5c15c7ec8e57af3f5c28be09f12dfc4bfc81442dd107025fca9c28ffd1b6423df5df10b2b7e8f9944f32ea7da93c73aafa52aee761fd01386f9bb6d8a8ca2351a21b41aa5de3b7cd8d99100e6a122447df92ba02fdcd0af46686ac3dc1e3d280331a3586d86b55a856d6d4900ccb1f94942af43d3ac60416d80514223ed19391be78aa403715594f530a1ef2855d86dcc8fd875e7a4e0b18d7c6af6ea79aa5c53a33875650e2b3e2b3d2b2137bc022ef78ad955d128f0a2a81b3a33d1458c14374d7ccab6923743f3042a31bb85cbde1e2bf83ab614a03eb9f89562aec0202dc34e7b5c5b77fc02cfadd8d344e3f5a44983da73c8d140f6813063368a695d03e3be200b3dfa483813be4e38f35f3096e0acff9e6cf2d0016c040d0140792dd9a5a2a95824432d5f239146023e7589947285a96783f0c0e1d3d640e75a3be9098175e41abbab9eb554e432cfb16d5ed38aa2739f346e809537bb7d5a1f0a84d536f30f1f40fe4f2142d2a314b08172cb04998d0acde850f14cf76783e0e09e0413ab6140a8f1a79a0a32a7a90a02d8db075a4221182a9074020de16942242404cea4260555f63a3170798cdb713ea8058b639f7899e130d3da5dab6ac958b82d219dc1f5f5e9e9e39c98cdd8d53f941f3b67bb35a8f210aad905eb849610228bda9b6da5a76ac99a8dc0d982c64e0572593ce4cf8a7c10407d9e43ee5c1f8f779a4732f1446dc100de1a3fbe35f532df95f9a7e30f8a008457a029e78f6ceeb322de8f24481a5afdeb383336fbb6d90b71fb55179326307ff7a43fd3dd7a0ef095ff1e1d84a23d33192b5fcd6580cc3526be642bfb0dc0ea1e634b43d29561f9116fb2c9bf563fc4544b37df57413d386c823d282cdef0aafcc826f14db11618e94cd8a23cc432ecb2a9ae4e1e59e6d68230a79b96f200af2adf73bb59c7ff09c2af19693beff22d97e756dc6150c3c3a9ddb585c49e104dd82b64c8007246941523fb94f54e1295edeea6ca71c3517ce522d7a01680807c4af8eec74cb0705657962aa3d0aed3e66a29dab6069e483e68fe6c019035f0767a1f069dc359dca1b9c3c0daa2be78e40abe4338d22c5a04bc207b9d796420cf5a2696214a8071bbb27476554f22bd1a0321407c9aecd8d8d71037f582bc4099f432d5aef11b85f4f1b1ab2bff407f5638bf4710a176dbda2e6afec42f48f09bed0fc100a4035de7b031bdd765d1accfbb95369f35fd5111499e8b11c131104205840cc13728a65da4660b519ae22fe68e21de947028571cc42f82f1d199c0c7f1bfd14627d5198df82e27c75b3464f0ef42bae23822a9e96d390f955b89d0e5517767458b524495cfdf336c068cf0cf2b3434d41255693192862e28727400a4a474caddcbc7bd0d6118fb84fedeacf78d675f65497c97d4403901c163b980e5053582d1e7c029b6aee11498677d81c8c3c099d78f775942a766be6fd5d4a0b5d548a870d4b80525cacd99a1b582a1697949cf6583092bb72fc68e48710260b73480c0cc9a4c8ca3d31212d9f90e92e8464410feb191595a034aaef949fab0f1af08ae18d5f0774416baf24ffc7c19981281fd1255c0285a7fb5f8290f3080a98c3880f12881bf917f184551e3b71cdf50ef20371d79b3d86644ff7ed3a4f08e65700bf8e1338db34bd788f01637db50597543369bdd6d3b6cc47a1f5063f01e9792588c9e7b1f1916a2f175c4d8f5ed5297853969772178bf9889d1dfef70c4c1e160b4fb981c428aa69c9dd00c4613d7a595086ed755bb58c4a807e435d0341d6f15e8531e9763103033e682d5abfe99ea8c1595a367aa697dfa97eb3940ad339d4702ed5bf60d6f4bf6651688f490939a04e867600ac2a4b46037a4e9a05dc1c7e8caf119dcad60984c317c083c375d8e1cbdba823d18210bc92c4f4550b51cdfffb0c2bfbb24335e7bbe37fcad11329e56ba3ba233863a6862e081f950107f90723cac25f14ac2e13d1f868d2dd0f1f949fb5cb9ab7aca89d0922497d07e2c5784e0578e4480c04f1208b67ea924117f911a4d7a974ad4037b838c2710013902389e241a20fcfc20b7b9f487b4b717671f0f966131ef0170d328a2c4f007b668398608a608bc7a97860963ba31be502f449159abce2047d5d60f632c40fb75aea128bd6dfce6f37e4749eb13b37592406dc4cb281b52af6139428bbbe05f00be8135cc17fad1c1495baad496fcac34fceefc5d0c0f4fb5fb80864c2910004d24e5ccbafe9f1e42495358a6e63ad61de372825c965168b42c0c9f0429a03b58544e9b7acdad2c70023ae3777d6b0ea4999b71b03a4f773c38d65c2b40f03a5a13c587dd1a89c3a75c1e262b18efd365466fc0dba48817f10a8054e7b0f007f612725acc9eb8cc9595308ae0c10d4c0a460d9038a12e469b6ae3d110a240152d167d0454e9401560070a91797ee4791547679171ac64acf09dde5a783960f07470fef8bfc9f06da07cba248ec24c4bd2fc1eec410796f0ae895b769730a072032b952452008087f84146d890f9f3af18ab51fdf90a17c85900679fa1c89052ec146a18224d834e821102416d4178e5e921063225ce7a366a3b5626effd90794a6736fb4911d02530a4774d349831205e4b9030ace7e369ec20cb712e77300428058d56dee1222fb0d0b54c4f484cd74e0819d4625e07e326f585665580f08ca0289b2ce2e9eaad9c91753b4fc0a7559d808ebecaf506f434b20c748a2ab0d244159b3b9e2e36ec99478d1cc91b1c4f66ccc2c309daa5194c3fc849ed38a071aeee0aa3b8dae575edd6fad0f703e48bbde0c9e80653ec0045e424905bcbf019961405ff7fac7a987fa0ff813bc340cf35e29bac54f7cb22c42a05d6cee9d08786bf259b27dd2670850a58113d31495d089f805449f801166fa03a01d657f088a50363ba29662c18a329f37c8b2ec2c975ae4a53bae86039ea0d91586b27a0c78634bc53e9aaf4022229ca4a1fadbeb828f83b44c241c63b7181549eda14c07104a355e1aa1679ea59ade94c73f04ef4a106168da2c41cf213785b023fbfc091ec961abe67c9ee0837af5ed5380dacdd64501189b6ebb461cc7b530835d1b01563ddf2a6de78cbbcd2852406d53c73870b5a96bd245e5eff7c2a49344683c04f7b993a31ac8df4984a986124fbc87b3c93abb155742b85b1815cb8b2406190f02f9a2b1556e8fc0c2939f4f494eaa7ea8c5c85286ec955aae44029c9ceff0086832d9c13283d1ba135cdced3a85d289177483bef407150e0ecbabdda7c9770b0757e1228a22c2988863089195f709f76107c66aafec6ccde5313754e0a7750bf17380d21d75d882035afb80e4e27a53ef81878ca62d7c382c728f2ac1d98f03b58f1f58ba119c77c745f803a82829ec064b0e266d45523b4970d40e4cc2610e91a3c701bbc8aef8b2c70e9f302774401f89ccdeb87206e5e7e48f5dbd815702f6d1dfebaaa5d4e0158167161c58232d3d7bebbf3c05d3c215e062a4148f37031056fd65266779fbc654621eda2be996145763d511aeda0960ee44d0e43255031f341f90d4b09d476fdb18d7433893553b8ec5e7358f9db899b4ec155cfd6b0750801db208e8f0e41a4db18ef90a5440933cbe07752e0039cd793f93d63e0d0b2a2fb79f27a26a732e4e508a445b05dbdd60df873e804dbaa4026dd573fd1001aaa8646b07b4bfa39243be9d94b1bcb9bd544b0d748ac2ac2311325564d45e00b12054d0b150ae0e56a07910fab1ae5763a662a928b515bbcae8adb91174e801451ef43adbae9575938a3d80071c5d0efcbf533b603c6915c4a2b2e80e4f0a044b09c8124d61805d8a641464dbad3b3959b8c16a6425275f8bf7ed693401780dbbba9b3c8895a887639d6073ca1370a567b09d94b347c8ce729c7d7ba79d6f0f83397b624ea4e63d4064cd6d129980fe471f450ca28dc84388e25607931b470870c9546ec75220f408ada0a8fcf1a28e52bf649533077d031256d5f725380307081a75ab1140a11fa1cf80051691356d8ca9f4e88f67c7bf258dc4c1af5f470ec8bd4312ad2aa9e7b6025faea230d10877442d31b103b16a2806b968d4b8f201f22a9b62d1d1045efe0278e77d23b0078b22b56004b92e5311461e7db9cdc30d1a3a596cf944c5e153c5febfa59250d66b30d72533f3e9417e69c31757a86802c4fe4dedf0d7291591e9bdd61a3b66aded0fab37b21309ae394e52c5ae83960a4ec1b1a2ea7ace221cf73b557eb755d870060c11461e1903f9e08eda59d0f10021bac5b11d6c3a07e8fad520f8e3fc9a5a572d9421285f294c9ae839831d7b03830a0e1214742c2c5690bcef83a8a378cdf21689b750bf0cddcf3c78cfd15d0727ed195cbcb0d9e3e883ee5d2d28a848763e87a727d2056bc62d579614d9360232e8256724d4f53a9a771daac6d77102d87b38ca6b73dec80b29a9ce74be6105f737de75bd2913be3f3092e2035abaea0c8056971f7df4da01e239faf5f2690e5245cc03d363d34a38fb2a40cf590edb3a651e00c813cf01af3bb0ac5ebd85020096bb98cbbe01b05951cceea3366d99ee675c10759c7c72d4c931c2a180e70e476045a3107ac79e910ba2aa30b18cd942412ecd73820017e08c3b1f61140b0a7be7b5305ee1eb4b67d5afa6599e62f29e64c03825ebbf9f865cff1c77d5350c28f1386742a6a4510473b46f971f58c52c7c6bb692bc742b853f26d3b135200ec37aec1af2de51dfbb9c60cfcf6ecd146e8c93eb45b9bdc9aa3b500850c0ed0e4221bfd02e39bdff4671081a11b61c959d8451bd7b19eb3d3d4aa3b9fbe5d80d460c16fb2de6dc51620cee36a4d75dd141756b8704fc2edacee36dc5fae7660f41e1ca5ced77f03d85d93858efdc7539ddc5bd5b80142b8f7544a2cba9c57c0ff923b49fe4e7696a8415117cdc3fd66c6fa50550d3caef62d6063b12c90fa30f38159f7f2891c25d9b91782e8b8abb7d1232cf02ae74d3e9ebe5242479830b040264d89bb6a82c7c76a372bb2ff7623fdc399b289cd71d997a63498df79361051c455a4ae9f14b5d93f64e4a256c78b38200d3260fd311b103d2ed512c2f7d06ed669504a316a256fb3c760b232c8df121eed55682624cf3847853d72e974e0534ca0b443ae5df327c9c987a923cec6b2934c6052f2e26af52d5506aa2afe409457177e28706872e84971837654b32c4f042497c0f5aaa25da09510f824106002aacc9f1df9b95bf23ef44d7691a1267ca263c30b0d5077381007055333f2106bb3a5b6400bd3c6eae1929043bde8f1c40a61908e1d6c83e89aecda62efe1e0c21979ff65e62de1020ef855f9b487e9648348e1e3a57512ef7c2df99cdf5b609c715f8d6cd8ae41f70420fdf48c125fe662c9d1f16d6a4b97b9f00e54e01d4051b4085202db3b9416a2c26007cc527be8f617e42d67c30c1aee35123beefd40655c6a9c24110c8f9c551ead3be22cd6b9e0c2624387b0f6eccb6fef0c4f85a0c72b6cb1e8a33db206c1ad4ffc2e2224f8f0ea52e54bd119d2a4a393498a0c501b99ad5c5734a3bef8fa30ae14cf64df2376e77fc51885588416f593dc481d40da219cbdf549cac9d748cd464f70c38fb5ce4a89d78b9c9aa7012ce418209800555a1d9db093ec2bc2bfbffadcc17cd61621bef958f5a10d47bf7f2347d0e89037bccb6e8ac833bc52e33de2867d06a113a014962d6ed943c71bafc99399be20c3cccc5233059714aedc2cef52739e5cd285ce48d7d8758d6f455afc7c5d3fd01335f7bc97841bb4b45d35ed27dba2af5993148099ac6ea0c7f5d1f76c91c390c04903fde8544891e5062ba94603f0523b62d0d4269517b3558bb73e7a8bd8d0c23850fb55267c5314a464399c1e17cd8fa13aa5bd2fce7e074387b92734cd702ec5b6feafc27efca2aacf2c5c72704638c2f7c3911101c1a0f65ce85352cde0601ca21cbcbfa5a8f8470d371a8f3b1386056350e14f61c0115aa70c6e8c2480d091c56c4f6779c6da0883850d495398d2c5192ac60a200c7738daae355a33b09c8e1f3a6253980b06ad73276833ff65d1c56b1e468908544d7323c14c85b67048cbf38475b5518ecdef7f7d1dd47a069f224ef5a5e3c8ef84b7326f64939f6023f3dc70557b36b6cec3de86be7f13c11492351d1a3d37c56e754e48cb47f9c0d4208f79607e47415f5b45f5ebf26a2645efc36a0529679b27a6b75d14c31f20091377bad8816b534120923f9184b8419a9d74d7dd77acbae474c2716936e920451730bf3d22838a84da2eeea0d4178dceb7242f80656a6b3b98c7701bf5e290055c7f98315f90f4f149d2e1edea84c26636af297e4779780e9132d71b4413a0e6456e0606e23c5621d2120351b92a6eac1027e7e46e10dcace9476a6b807f6068b143b1397a42ec32867da9f181e1834138afef61ba8984efd972a2d24cd160726618ec55d1892dd4345dd8270cdaf8f948dc5056411d8c60f3d80069ca2ec0ce545c0d05cdeb5d0c5ee5002eb3db168f15d7a3fa468fbe542ddaca69fa1050f99e1c793f6411dafbf19968a092596f096099522054bb7a0f53d7eae8a69350346f62ae77bb9fcf765ad7fa42c98b659e19df6df6d069bc3ee150a3ed2780a016819a107735a3619944178ec75da2ca504be45e0a788889aae71e25aa650080f4b4787f6e70704a5005a875d9c1bcaf7434fc2d1ce147c86ddea5d8fc643ae0c1b32e55e27ba9153a23c547b5af162c22fa85f8667cd20f7d9b2955202538d0099b7b43773f098f69909adef394aff99da0c6be61f4017116eb38c9a9a6aac0f12a299dfebcdb5a45c4358351c1a6defb6e29ce6183646dccdd5f5425e215d0bf83ba135a93fc7fd2f1ff7b7ecaeb88b303de520c07acca7d33ca28d8be1fe0bd413ae09c1fc7bc05cb6b38e334cc95c26c01784c588f81e47aff640b88eab099637a1521d8d5ce28952666da4690d2cad378b9319f721e2a5f3bb62f8b1ca0bf23f56529e77d21da1a308db9307749b31c1260d45b8212a358af7ed19be1f024028edc41d28112d9e2531e5fbeb31b46235cd35762b7621b1c77c51ee66040f92fece52522178fa03220b0743b41430ef01ab0ca6fad1ac4638123f04570e0bd5ed1611fbf5c09ae34e6b7742e4c5f8b61ba386e78bc74208268d63e623ce0afead8ebc80ea55fba7c6127be1ea79a220360fea3d3c8ccf8defedf3f78c02013a2ee53436452344785a990f2ec426edbb2fd3323989ff298067df92946af60963ac149a302b72940d50f9ce6cc75beea374498472d624561969245135d9750f40b1ba856b25ca4e45f2255740132a5cb72b64ca321d0eae5efa9c211c1f0401bc40f1e4f444643f40951b1db510499b3607edafd9b97fee8b60b0dce673aa08745ce69f19c09412b16c4843db738182b2b3ce48076b8f8c33d8bb85dcd1dd0fc89baadcd76a34c875ba42cc2b0d8aae6b6272b37b38cd6a4529ae8bcbcaa50aa758f0b4e8b9c46d454084eb9ef8ef089d85905928fca190721897fcf007cd01bf6c1264b6ec0c76381e230ca0de61f735c302bcc7e1b692e5a93e0c691b0609ed30893c589bbb7d099b283d1ea7f1e43ec8fbbee89515019ef4e3729a7c2200be5fc75045c2b7c276972baf41ca3143f2cfb72e3489c663f14a9b04dcc01507b7a6ca30cdf8197bb299316668bad141b5648cf15ea5391e519bdd950f14fb0c9785a22f504bacbdd471d84f6d32f36326a2b7fa0b7dd4da55991823f0fc3e039b9307f21a21f9fe6f5b65aa9a735f4a2d7b1d2e22b303bd93b78fbacb0a620fab8c11cb753278e9c6084adc46a4c269b232f9a50b05a3fae0786b35c5f9ff0d63ce23efbc4780a208722a35d386a7984ff1f06a3fdebad7ea3c1a7897e9df09dd10356019e7e87b9b38e000f3e75c489dda83b327fa637124fb64ad6445f10d773df80aa06c57b8cc00c9b60939bf4cabb0ee53b16f30ce07ea4ef72907ee040069c8fbcbfa3c0e58ca70feef9fc4df7ea21359c7aeeb407843327e5f920c07c3182f45838fa1b929598af7385b8ec6e13514bc2d1df90f7aee458bd0bc2b0d -generate_ring_signature 23427a37b63e16df40a94a569475e086585f88042e3c6933cf1c7c089b041a45 a680bcc8da17f610deaae851e49a1153a41cf2a0e7b4df452b0cb0e06fed4fc5 27 76ef12c44e822d190d8591a0606a6246e3e339c3c0d09a3e4ef9b9f3e537c783 dfd8e238c9d58b0b26de9d6d577cb9ee7fb353d5e624577d8a7089dc7ba75c2c 75d01aa0ad629f58acb073ef222e20cf3ed9cc497ca0c0faed5328da617201eb 039651d4977f68975edd0044b8c69aef742b79b553550bbb89d03996940f6a65 2e20014f619c3253a8ef76ada8eb6b6a90b48d80a0442afb5c49bc677ca30633 b6172170bbe84ae154fc5815d4736578dd9d30e1e0436501dd36b09da27bfa7e 8cb4fd174df4dc7db427d121a19bf938b4e8aaad7830e9e638f653ef0011ab54 049bb382150f20b7205ee71165393d59fd4cc0d1a690a088387f8039867f5b08 a3ea8ea364079b4a02debc3fdf1b6d7a3791981efb1688a876b97008d3aa27a9 ffa5fa37dceb7570f33e6c757291119270ac62a9caf79c61a1f73347996b42cd f3d0898f92b979728369d0d9abe73aa690292800f3e4efb66de9544e95fd11b1 29291a457166cc60ec27368846b57c40876d1f573ec9af3f0adf44e478c1d561 195ea8899ba4e879cbbdcb3946d8158b0db04d9f147a07342e73089a7aaf5c53 0cfc96a760e8bba3e0a54bd8f64868fb7230f025603bc1d7517d5238f309223e 0beb14a39881918f54137e99f016277f66d022faf43dd0a4312de3611ef62167 87e660091dfe3e75636c7dd731a7aceef1aa6509a4ed0a5d9dd70b5ff02fbba5 4e1dac7838a601b8bf319f75d333a2488feb652d3f2c1610fb24bb66b5dbd727 6e2959ddd5b086f020527fb71362b3d902b030a27a873654c22794471312ee44 2c75319035311b2f58593503dc6f6f0f5c9d6a5b414586818f48452f5e7846e5 f204e336a2710b43975568558f6845fc099637b05cc8921e0557ef5121672e06 83210f8e7869415f49bb722f02ecca7b1a39aa5cb90ee5f62015c26b5aa7e23a 3ccbee824b4d6ba42ed29e238af0d36fb4213986ce70582e2b52e8e519b23c1c e42edb639429562afb90c78af73c0966e4a38f892a13e29c778af7febd30267f 1bb904406811cbe96212e0b34bea451f8fc119a7559e7bfe57eca21b43eb55c1 8414918482c2104b258f9095e099492c5da1aab97173d21dac8c083206ab739d 65bc67958d05c81c80d48855041e321e1e74aec3006c2cbdf0d9c6adc0549271 12fd3cd5de5c0cea07d92c01871687c5dabee74bd383fe58a0721e5c0658127b e3bdd2c25f7b73bd7570bb5c6d3dc6469dac206a4483a6d8dc173bd2b23a6f05 2 b43ada77179f4769309ed6690c1b0ac9acf959d597e0544d1d49306ff3e13a0ba0a63664c8b00c811117544e7253a3e20e162164bcaddd27621e8d80289b5c0042747f29ddc4a9681a1429577e56e85308c54682fa4079b455c5f436e12b4f0137fa1bc35c4c6393f75da95b2b2ab8f54b43bb164b3d7407b222896b654a070ab205e6fae441a360ff63bea0db21b442c1fa9c4d97388bd27a3dd3ad813dd402002e1b71b57184b50439c0b6756b6e82a9a5899ea893368390505f61c27aa00e79dd6e0522a288e721d8ee2fd540d836f9a55aab2d8d5ce25da3aab85c60c30c9a36a0a31536fa80b121ce42d87b58b607b6e9a4c95b1a8644cdc6961cc72602f3b4306eafd214c57d26f0a1f9d49e53b32633232d0ed8d69781fb53b6e376068b6c58a6d05ee85e4e1c10dab918bcb68077839e547e567c04052369bc55a30e173229f2ae2920ee517fcfb427fac67f9d23e2a565b689050070fd1bde12850a99749f1e615e5328c14b22e5422af62f6a1242a85c5fba0befd65e0ac6e0220f9a3cf0de01e4ec2d981c24dd904085a7299051d4a90357ad34eec221837a28027e4295d2c71c79c6258bb1a56cfd5c7260ae5b5ed28d3ea342a45d72fa2f2d01c484997bcd16be7b5446bd79ff41f899aa653218ed4f53cc011d27b0e9bf0409321450e02488da9f1c057064821518b26965641a553e7b6c330ed332c9a6040ca448e033892c30b46ddd8517ea8c5bc5ebd832d6784743929fc55b5607867707fa86c7e24a443a14c03ecda653a78462cb6aa7848627fc604661e22a3bdd4503b25fb806e2e551cc917a7f3f20d32b57de4d9aaca06a99bde6bc66df089154020add5c1dea3f193670d9b640185f8192be710c66dbd6b3c2226ad463dd1aa008f8ac4dd32db34e8af7a193d6f4bb3e97383324e2e968f3d544359f2a87bf850acabd0b35f1548fb5cb2ad8823a98e7d317e0671a1f24fb263c317c11f589de0df4aa94fe1f447db68e00a1bfb7856c7c59b2c5f74adc9d712a9445600c1d3b0552582f08247d36feac1482775e486f284cedcb14d9770fb59eca893daf03da0badfea951d1db4290dbc48abda26b848a5118e7bd59c0df157c88be3984c2290559ed808aadde2918f73fb9f269bbd2a84da638e83051060d63b5a24ae962fe0c5b7049f0b2a47b13d7e38d1b5cf393081923884aeec522951fb9d49e2b205404511f6c5c7cdac54d2a28f54489fae1d9b01de3b58c6f349fa73ea454ce31960b09679315cd1019855d13e6ce898884d8a7e1d703fae1703e1b4a28a4b588390aa9e787dfc21d16c883df3da9e0e28a434548bbdd8e7b9c216b243269cc08a40c5315d70f30a355fef8da895d0ad589c5c4bacab28ce2b23ea27146fd76d2900cd97191bb42d36e21ad65408bc9f2ca1e040199f05e4786df1c7d514c05b3470d357d903b1078e6e87408e898c5d7520071079c8871964385dbf3346462235108e0f3bb0e3f832157071d05ecb440374252702c13ceedd638e25d7f9a2946780937610fbfd389f192f3ee55328a36ce92516d52d5ec5910afcb43ced36c34fd0998b59e431691f7ac71e1a161b5af8efed0df58c6dc6c834dd8cde657806bbe06dd42d389102cf68e262bcff5b89579c61dfe72bcf76a676046daa7537434ea037c1b4cf31dcf6a31953601afed600693f1f3824c1a74ce6350842dba3033520997c7c22a4c448034abacd0c0f5a2b8f48351206d5b2b32689d12e8fd46d7000a848fd4e7d2a86a37ee3d712227e1d0ed04544381e2b0990d8e14266129388207fb785fb7c137828851a81e66120785d85120c010a4de8ac8d7d317a635115308a2b8ce68ddf07cf158db92ec28576855659d94d030e125c460fd68f54ae7b008fbbe63d34f1b00481f2ccf729fa3950bc4560e551e6e0f992ff6a81e8f3ef70e6b4413711a59b763a989283b433dbdf39c45e788862b4ce398c21faf3b44e60100c65598c1cf9cb89dc12a9d7ec6d16150ff6c898f3a857529f4127a0b606d019087cf25761f555e5d89c1723741a67396b22625ad03c1a1e57b0bd8c782b105dcaa439605a5e89a9f8a20c378d7395186108cdf6646566d51467ff6a549a40c17cd0ed428d4d6f4d51eaa9bf89e25977ef8a34401067f3852df2eeb9932af0babbdbae7ecfdab600e72f3ad1fb1d6be7b2a26707cd519e3ed1a87cc9268d00ee6745df7b3e678350bf629b5685ed50400b56d2bbf37b49f381b8d9ca26ced00ba6dea8f095e9726a06fd8fca84eb436abdb67cdd4b9a3a49c1af079c7cfe909654c694db7094d22217888b1614bd876459c668445194de1aae720fb742c2d0dae55618d43791aba2376e67141648f4c5ce53f09a7c2a61da783b533deff01049d8dc6c22fd9e6bcd8217aaa5dff7af40b22e2ec2eeb7003015c196fd99ee409 -generate_ring_signature 4a79e4858cf84189b1a787a39042ea6be11532c2bc4c1e87937546cf467d05d1 e6e7e9b18970c931eda06ce590197f03131062d3d1b5a6ad71ca1e5abcd18e3d 12 2fb5a4364780ab0fac68569071652025fd8b48e74aa242c27adef75f375d94e4 3c46616f02f3b6d9fcc70f74f9319547b6114a4b2af80da69a665b9edaf6a4d9 1c4d1f6b0f21a6b2a975e2a76e7c4c735bcad9430ae693d69b99db3623944c1c 2be94122483ecf69df0dd4920519ea1ce71af7036f03e27466ba7272421deaac 8eb99f028055ca92189f78726acd9b93ab452227084fe718ac4dab9e8f4389dc 226f4f356c64efa694b8311ad07d4ed444d940d98a54920d2d1f9fa687cf20a8 4bbfe2a4a7b779fa5336295fff9be27b165d9a01920c3fc3ff28d4861997c3a9 3c50a93bcc8b0ac7cb7904cb370d850c81750e2de67ac9d4a769f551bd28727c fa5880974ca82b96b3b64d419674cc7efba182815d23f8424e7e354cc3e63dc4 710410134c629f3565f3706878f34c8b4b049625d520c4efe0b8d0ff9ee02fe7 b0d02c0f6de3ec3774101beb260500d636a2a48c5be06472d17723377f82e0c9 7fc89baf0a1f51aec3e249259b8f22194c406956d27410ce5ffebca081fdca89 1d8156cb422b95597ac3b4ae844934b8e729b09dc95d288c4826fb2b3b8cc90f 4 5652c3666b00dc7efb76c431a25d403f71848ea7b81d9607e4873c8a24b29d03a346f5b06991ce8d333635d42e17f4f7b40e69a2969ed05c780ec1c25788b2051362c18393cac95cb1becd88b374490ee67f7a2dd67d7354906d111dbcd98d0d27e3a17beada0a83569038e26e466d4a1ed3f178e976cf0d3b735a4e5a47df0d92b9ed4ce2e2061a58fe222f82b410f8b05c6ba110cb980dae1112b20bc72b09dda21a37e95bbf69164cecca0153263e42e7bf712ac91188f9e4ac68fb560702761d7c59d937e145ffd7944750cc6c10f48bcce0d3e1bafa2329e3953ea64807c0e425be0e1834e3caa8410cbdaf7e0a002e2e8b0eb3b8cf25854320c499530331ff11ad2f104cbe05fe396d645eab854e54d5f1b6dbb07f3faf1af45e76b80b567808ab6c190570473c4e20a4ba88c9d7e1caa858d9093ce02c328baedbbb0d90c28b9ec6c54f43d5c0eb329df49f661b79bd3820e5702b2618151dcc8a5c07a68ca6fc7122d989f12ff0212ae240effe37e6114bf15c32aefa4a7228875f0d26c026b7da686e77fb5032ce4d9c9edd88617131a3a688c55b55827cb991a30c3656ea417a49e4719ac454f351c989621e61df24b16400125a74788294313205f24a973d7b6e0513e1a02d7c2c56f924dc8cdb0d9d7a9967f58f4e51f4de0307714a4f3758078f5e88c3a5a11a91adc138927c51325dc2203206dbdc7db2800d845ee9fa832991be73ddbcdc524da46e9b4e6ce0cc450c70a3180f8fa060a9053e3de5ad375342ac3e00dbbfa6c693116347d91c105edff8d99352e987022309b568435dbfc33dc35854f580b123f8d8350d325e4464a91c0994df7cc8a22e0b8c75704b72c5de5ab2ab7eaa0514b1e2d6f65f9a6d0605b3c1ca22d146ce6503babd5dc58b4d5b12fbacbe416c841aacba6117cc250872affe84735d7cc6b40df2d25aaef27fe3334ffb2ef904dc6186a6d35acc5c93f5f42dfe585d81b88107a8268933e8e48264cb61c0937c5612bba0196e9fd578455cd682b1674a26310abad7a75852c22677e35a0528efedf45edc40efb60c75ecf6ee6f02b14a85760d -generate_ring_signature b3a73b35af9a583cec3e5ae026c0974460a27f514a1e57863ab5b18303b49bfd e40c3300a1834002a79553eedb269f35f9d7041cb646e3e6db9fca5f153dda0d 10 ebcb749e504392105e194230671d48c40a2122d199dc6ae4e0ba73efba82bdad 7859f6fd20abfa37a3c77079adacf378ca5e510c878c37c588a9646e93fe8388 427f5fbb4eab74be396e86079a6f7efa6cf50d058e9f050686421abf936cb4c1 0d6be5c57e41a58ef7f994a0face6c73c93aede764b54cb3666453d57c2ff4ca 7d0a29ac592eccd5310a9f6ef5cc6ebcfd854b437325c38254981fa83a35e333 67d601df6e64415b9113817e05b525ecde13d8f59020680a700768f9df9ce0ab ca677493bf0e87103de142670672bab6afe534f83fbf59bb7efbead953f2ae79 0146d12aa7dbdf94c4c6c9fba9ed250f5edb2042dc0a6b6d143155f2f5352452 2dbac320da8dc26a2e4483bcd01eae0ad7748c28e4bb6793f182f976f80108ed 87db7f074e179a7998b44b864409980beee78a7ea3befcc7fd1fa697735562bf 35b98eef6f934ddf49deebc1786ae2a7c69d7c061a108ea63be86644aa65510c 1 a4b27bfa1dcd8ccc35f56e6ec9dff9e235a0ecda2d9529a60c2e1229681f0f0ef5e6b50a2653e5e7ea376802a446f280d757d626609ada1cbdf5a181178e540750626012998206d8a9960c765cdadfe838668e318e42c99226f309303db3e50ac89dabd36459757c656f2326b3ed98228e81ba2e5ca514ecc0806ce6c4a1b9020d5b17f345062c5cf35c242fc8331bf4d1961bb0c52edd8ec4be508194d2f609778810927d1ce895024e9a918774a94e0da29e8955f62c0840b6ab6d665d7b016d9572211f5b0cd270e2a84db69a580ccc69951b5fe7d260f3a908a193fc3f09ee9b0682740f16a4c1439819270b7cfee359390e8a7e01b2aed3070acb382b0ee850eae68f81b006b88aa3cbec661b8b64cf95c8e6a0f52d1aa6a8d8959dc003daa69aee8da4338b48748277fd07cd1ca17ee6af345737ba4ca71f9fe5a98e0ba637a1b827750023c7fd15962fb5caf1bbec3fa6d6de7c1c632871b49ff34d0c163a07a7b223e94fd68a3f64815ba77d83436c921c639ff1c46bc505febc820b363de545a914ce3827c193def01f88f62d038baf7d5839b2b4151f7bd47d4b04ba9dcbabc7c0139c89ac3c21664a05a6b246edb351439a09f3bfacb1b4557d03c43647671ada6e45f9eaf58178035e6f384682189a84934cda1021f384b5bd07b94252bcb879ac412e5678b9d885fd1d1b5fd20632a0025be3bf4355e5136306e2ea5712a1a2beb2d7bb3336ea62230b205c30058404c3ea87ccd47c2614b6022041762d703e672ea0c5ce283cdb506f0173bd8c6814eb69ce9366ee924df10f9641a3a43660e4b0ce07cfcd2f861708c39322404bd4b5716c4a2634f6898e0abc1741c87e1f81c55b800c52b10f3d73a37c25836eaf0e95a72337842512d104 -generate_ring_signature ba2cf253cfce5ac728cc5bdfa9e02621cac271d7d640ef00c7bad3612f7ea07f ebfa9411cd1911793cd8c3c3751fefb1a4f26ca477020409303e36d624ba31f3 5 a8dd3253a8e5d1aed79cd6b19d6f8f006a6f755c8da0b20e2c9187fe203f6a18 f7cadf8be983339ac7aa073402ff1d5152c7db7a2b1ab927aff355d4ccb97538 beb5217ff6e8217cfa07a89e132171eaae685ffc7155e1349bc2ccb6289970da 6f7165dcfd5f3310abed2c531905a5c69b118b2d708ade5721f0a452248e458d 04201b3245cbf0b7225718ecf5aa71db813eb5ea54b21423f93fc9ba202399f0 d5d5570a52159caf96724aafa56bb402e9340bc605d665b4fb283c87f3404306 4 ab760ba9c293762b69be42ed73afbe0f7042b99a0ec098d729140ad7f8b00c0f57aea8cd26dc6c7c75d0af39bb5cb494d617fc822e7b52c1b03521f5eea9cf0798131f16522c4432a6b684fe9c21142e605314725b4ac7d73c97994f5005a205d10764cd6f3688c41bfacd4dfdbb268aaad24ef447c8196f20966fc4d99d35076e25ebbe269b317d48aa6b3f8041a7d74f2557b5cd46d0af02eb2ce25395f609658800b5074193521bcb6c393c299002d03471e66352ac84d5fc851011201007f7549b188b77fe807bac6db328a0392bc3f70b76aee1362eab1fdd64734bd60a2e0720cd19e9ec5d7611bdcb41246d7494ac7592078693dbcdf08f94a6ff62064f453f423823c3a18c66bd10d818060378ff33d253f398968eeff4e10fea1e06c320008101a410935dc16bf9d33e0549477d3725cdda3d5cf54b28ae95700506 -generate_ring_signature 07d546720ad955c6ac191f0ea16e5f7d843651501ec655bb7352f0cbfc540cb7 9c58ecdc8162a18d21f31152b55b98c21529b1b918752e05d6440062b320e36b 34 c5de097b79f066a3bae592e76e4f681f9f9cbddf2d1bb9819d8988f12e657b28 3f2e00f51eaf8c766ea0a750a72aa798c3323c630bc6bbf66796d9eb1dcd7814 0108036469ee5180b39ab609a7cb4a4ba3f4c6ee1399f5a5475cd7abf56f6562 c607fb7f081e02725de17473c3358cc3863eebc8306403fc097d8a8cdf56bc5a 358cc0d47937f554bdfd5cad829d6d4c04bd7b4bdeb8138387409f51f5bbd233 c585912932e94cedab2246ef0f35ba86de9ac8816c72f39fac1b74ec849513fa b967097a873c451309dcc10c8a7716802451bcb666e7aef43c23c4886e958f59 edf0437f0789c2f1280f946e027b9e9d3cd7a7450b77c0c29cc2d1a3a4048e59 6e96fcc4fc42ec8113e21380f6e34dbe8a1fc39c8ec605413a6e4ebcef95a5ee 3540744c779e95429aedc594ca3e7f144fb36f2354c82a215ee3921eee58a687 eb13aa3f8418b863153d0ffa0a185060ea881d0bf26ec385511efd7d5af9a9f2 7f6f7a45c10655b51e31b6d8a8d718eb55be8263be321b959bd31d8175067833 403cf1c69bd4293df454439957b0b82b4fbc10de841225dff9bfe9f09d0b1e00 8e79d62b1b537f5eb4f56e876c25c5419aaffe859b9a281792009937d39e3a18 2c69ecd10e42299b2775570761c7f256084b0ae90d49e77a84f1f728e1ed9eff a15aa70042d5321cb041d4bd2204a7b3cda423ecbadc33aa57c4bbdf22e9464c 8a01554c542f2df7d9ca32dbad3ee49473c064baa299d3b659f30fc0cbf19b9d f2a1a19157f813c81ba6633b903d7c63ef3aafdbd1001206e1b6b1334260eeef 577dc9dae2487db1bb743f56d1a016e8352d2eef75b8439f7a0b06d2484e6329 c405f1ded4cad4af0084231859177e971c5525f1bb1854113ad7c5bc62e21dda db561e3f680279ff441e82c2a7251ac1e763ca0624ea03eb64fa2a6ac72b7b93 a38531b78756f9ba162cff58ca174efc1d776e9c3375d43a366c5da19e854a6a b12e648ca8bec1098b935a509295024ca47bb4f2f0f458cc371afd286ded5bfb e0e9d1e673bbfb6785f45e855cacbfc081725ee4f9572b152976c70166ff6f92 be23ab7ffbe883d989c97c708ce49e6b7847a4b42a78d7a2a122de8e7ed05aa0 d5a6a40407c2aff52cb6d80fe6fcee2cb265517744e655dfcaa45e9b5b159e3f 90e4110de219aa004caa3aed8b67108ff8888aa4857c54ce6d7191e8b11365cb 9dff22c48301e57a059f72497b5b04695bc6f69ed27d12650b8b5e48c9f8f8ad d056b92a46fb72170ff6d95012156b3c75abc6b19a70969ec132f93e628ccc4f c6bfa573278be6a28702532d85b56cf3ecac307a53474f1599bf35ce0c39f692 cbd889739f84e045de92495e182e47d1b87f2979de5eda0e24eb15078756ef4a b56c32d9e333345df49bcf546e4b7555e5820429035314341146e2eb555b736e 1fabeea644179badca2d5a7660fe89f61ef1e1975046490dcc8ab54a35b2ea12 8576859fe085697bb50b74c16b8f8d75e00ec1f6bb6207ce506df6b1bf8dd8ef 85c20f8768a15b7819a3885ecf1c285cfb1b5609a93007fde3e1964d014f5405 13 70d40d620e5e01e723fd74dabd2286c4a72159a0f2a9965cfac981b63a5377061a30af210afb97043349740f4df27599d6f573b55975758bcf69cb6e979c7909b1784503f9a26e9f3d4e315cdb5bdf44f8d490f03376afb2d1365e7f9ff0fb09cbd5b99992343ba9832cdacd925298d243dd0a18a791baa2e673932a4896c705cb48af075a3e0a629faf3c75768ef950edc68cab2b7f3eefeeb95995560ec80e0cc9c8e679d0c15b8259fc94b6ffb90c87dd5ae34c4c1ae4adc95abe7b8bf309fa1c5f431f22a3c5cde7574569daf9b1c87c0a629ea01cc43fa8a2a7de2b1d0593ad6aeff73a911e46f69e32e1ac84c8184429e8dcbac48e0bcfadc89191340731d94795d7e120af1da96d98413bfc36ef34c46eba70ae450ed650c36b68500210c9c1edf887fd576be0dad506fa566704c30b86bb5a709d5a9fe9ae558aca06a1c59ddb7223af3f90f54ea0ae41a7f4bbbd6436e254080eed7b223865d4480e5247f83ebf27f42967c69ef5c65c167e530c99f10cbdb41cc062d2378d26e90f5190b5ffc89d2150c357d435889802a3f64e3d2c8708dcacda1571a02ae25c00d44a40b6639f8c7f6b00bc0ae77e135c104f21e6f44c1b918a445c61164d3809d091c228433ecbd4b6eae066e349864489049bab539ffdc2dbc6a4d937027e0eef6231356b75495a7b10d0b8d84cc6a593e1ee3dcf93885252905c3348d31f04da3a6b79917313a87da325c1fcdba0f6d9eec63ac18c3db794d80f2f6266c7070be82c1f5134c551c3f4eccd3cacd009c281cc24b53354a063640f864a6ac00888ae8674cf4b6fa3657874668e2dcca4043a821f476ba39fcb01a453e648d800ade970fba4b4ce306797fe1b9f2cb70e4c77e4c08f3e2deac53209b92390aa053c3f1f3ba7a5c62a76ed9267d0f952e2fc7c7dc87111e97e6e249a8baa10520492e560085ab8344f58ba4150e66581c65ff8b479ee69172e31b820e44cbe62001be1d3e8589f353620232b10a6e3e966216b01916df88f354d5eb1c75e8e4002707dbf5766dc7fad8198d88784dbb47310b8e66b42037cc0fe4f282805e41303c48aa801a54d58e7caf6cc3110a99607e5491b348674207fbca1ea006f4a990d6f8b8f5fa4249734067ada90dec5eb295ea3e7a2299a47ecf65784e0d0f4f309d144fc15c21bce6646f373d2b0292e9bbc078f6216498e0b093da05c3e99b20ef5093808c06009ca35097d4e5280b5e344d988a6c4c8719bbdd421e69504670d1fb01e775c7f2b2f1f5eec9169346187b5424b18e62592ab7350ba2f89cd68062dc018318cda179b5f0e418c9dfcf1a82cc75d293161afc6023a1cfa0bd1380712003c49527c40e26229802f0ce92bd3530f562beee1bfab0fefbf416c7dfd0beba1a6ade86c65c642b259ffce6c5e9d5ebebe2410060c57b26f9e456ba7d204cc3572e2cd2f2777b8dc59b7e6602b4cb401e06d9a72c740600e0f53af02ec067bd6ea4d51f6e6a0d79a15e9488f13aaac0a2b8f6ae7d7ba87dcd674dd50f50756dda37609ee5560f16351d77392228795cf35b7c250ca7ee01411774cba9502956ebe24254dbaed5d09187655318d92bf96671355fb9d67fc03ae0c8d2d540f17653418e23605ad68301af596a2e24620481d18224815e09a52b06542e68203dc8524822f2e93a41ac1247707ba530ad9cb424dc38ef7d2182dd245459fb208b0a06b4426c129d36b5d4d95508d555d11d09e5ab1b9fd5ada8009b2ba554802f768e7112afe6be17f1319c002e8a6eb98c1ab3b1a79cf3e82e8c1def2c69100b8ee3437cc6e0ef01ae0652409c9c87aa7c4c0f28dac05bc8c966cfe42de5b0528e08a8b3eb274309ad3bf56393af21a8d6f849e836c27b08065db986d5ec10e426a38767adf1f5c976b436ecc87ea554ae1e77fd959470bbcc761e53af4780dd1bfa14282e032a217d3ca2710fb0907b8743fed8d10cc30a9cd150cdb6110029c813376dde0a15633fae29ea842f6ab44f90146f3417e0a7c3e63a971fd2d0da1a746a365e63caca1bcfe9e4bd2e708602caa7107ca0238597a51b2f98f920044ed34e88d05e501e32ca0718d8b7704dbc24d8537a6f9a4584c72ecded0fe08c20e7254ea5c6c6f7fc7eda39b36e113a91373967872e888783d9a264a82ce020e09d6e7e9e47e6b226184b059a47c9477349e02b4c70a757a73f9101510d008c8cbc8d6f26b693eb5e987f8318df5c0d0e808bec3015baf71a744a5b23a770025cc44019ddcead5d326d1fcf9883f706d76f7708cc7e6c89e6dc9d81b124d07c11c3bf8acc185b4616011479eac2925b9f4a40587f8719b9f3206c50fc8aa0761488b856095e02f49e907552b932762c446e3c84e96061a7db61fc21d815e004c65459e3098c2d6f7a16ee8091bcb180dc45d53c29ff50c1aa6b53b7b64230c3a56e0c4e8cfa18881a18b08237dcd942998cf826d5cf0ab92b0af51bcd3c30dddf731a33ceb76a8888891744b09332c23c5e750faa4524fa62bd7d66d3ada0d72aa3f9e6a53c4939c8613f189f60ff86591eb3253fde987ff61c3b115e1740eddf4acbe922615253190d099febe6adab1ee6d5a4e96dc597c489a7052904b0a9e787622ad8c16e8467ff2900665d158c7a29abb3a6d86262d6465ef5951c702138c8341c46d705809d735766cff7bfb1aba827d015060ef6d613d08e7ba570ea63637937c4feb22686ba487fde7c54d99af8dadf3e154ad8309302476692c0e2ea63748272514bccfd90e6a598f2ffbcdea4c8580986940ac8f28e38fda4d0ddf784245975906814855a5fe724f10abe2a806555ce2ae5a9ff6069a7814ed01253b3440b5e545aa4e4c4bc27790e35225d1ae865b23af75cdca9fba24a36504283bb799a59c892594b6022084a4385a321d02821e015763b182c61d2bb66e0ba188fafe0bf4c1352babc63503b54d8b5ddd58ba51d32623a4eaad3fc2a66f086ab86b58a4f9ed38bf52e79cebe3c4759c241ed50a0aeda291af2a92829c210bc56d2ae58919e74ebbff6950651edea1c064a09c8966fac0e918c9c1fc25c70e -generate_ring_signature 0b1546a77f70112073985f8a806f191e83e9d3cb9f19e41f15a07671a7d7d173 0ac40cb07f49b58cc8d7c8f9eda97937c5cfb83fdaa0e897e4aa8507cf55f2dc 56 fb504b3a12b0e8237788b91816e395f566762e9d85e276592b9f42eb46b932b2 2de49b9fed2a1379e27ff9df1387c5704728bd81a61381ce8da4d0190e310cba 4b42cb7ed8b175b0d82c6f6577b3c2ab65bf0e0cbec21bf00a6d0a532d508383 6996b0dc98d6d30c76d3424d16591495301a6981e8a4afe7b0b7b0f1682c0c8e 8c3d881ee3a14b52541c96ea7ba2200a17a9ae2123c15774efff022603808f16 c7bc7b4e978d13bc12a50ce744db1c964ea624d53d57ed21aedd761fa9e5dc56 84230301ac02a11ef46e5e7dd0e2e73faf19b28e3bb579374a805a3a07feb800 0f5062ed757ea9f3a1d9680003733cd69ac3077de75e715da4c72774fddbcb78 da0d99811f920053e451849dcb82c6f9c5f6f2e1e0435a52265dfa695289bacd aaaf445a2d1bcf716480c3f552efb0335d76633b4ab6b71ee6bd9460fc24a1ec 1f7884f7dbce7ede89ebec68fbd6765b2cc92ffa7f8c819b1a98c86c0166f142 5e1592fd36141e29de682b90ffd13d68aea02e67c8c75e872ba66690187fa6a4 78f92af4fc0e7fe8c117f1dec9d225cc1a690abe427f626466ad5768a1578932 5f34d7ff989f08ff0839d1df276a0905c9ddee265d652e2be032f59b86686d95 7525ee1aba8272b610df2377c0c275202fe06cc0589b9213e70a127a849e198b 43170ab7baf0ffdd0fe6bd391dbdb7e2dcd1b89707bb00dca9bf12e5025b0fda 8b84df55edbb4cef851c9674e435639a0b147456333b55f2ef083e572313e347 fe394491438a5a7860e525035b70555caced54a2ee0d0d7b23a85abb6b8c6a49 682a71ed5b369424431725db66925de5175d081d65f6a4fe049a5748836737fe 45ad3881f548cedcef87954b2c02363e0a4571bba5c5462d16bf5ae4f2dae423 8c3947dd8a8959059543c20250f6e83bbe841ea641759cf6f6cf0da204dc3fc4 11f235ed2bf9246af8ba9fc067374693fd3150529e05b138dd6b2942b7b8ef0c 57a1abfacc42c7ec4f8933bd93b59fe01f0e49690621379993bbb03ac100f036 0ed848133ef3457e15f0977f10a76d7e535b26eebf9dbdf97264a964214a05d2 fe282180e425a1bd0a8b7479be259067de45fc6e89243f0d22c7565148acd310 81857e68f244e6d5bbb6cde0c7ca8e5b8662870472f78cda46ee11f21ed85b48 545ec00ff569c1ed27563608e09719ec282f47e8517ed95bd1398ccc0f7f0404 015730048da5e50a112a705cd3110fa035634991efda22bcedc9ca6eb6940979 0e3c5354e7e30d619cf5f89a31817892507e64ef94dd9ddc36296da893c60389 9f16a094d0358ef39bbfae78c967677fcb3b701245f66de2faac17fbd9a50b3e 3ea3265959f6857f1b3de569fe573268e3ccd4f4fbb3fafe0b63524c13df0375 1b510080c98faffca1796b5937611e55d2d91ce491789a65df068ba7a06e2fdf 2d1f63f2b1f54755b4a3d328552f79708a3fa6db59dd970eb068845d8536c432 d9f5b0a6f2f92e8be14dd2ce300333d20579d3fb6f8719ef8a8d5ba5a3e72cfa e4d7b759782849314e443316bf297f0ea63370eacb54839ad9a377a54f72be32 be3341838a0f2bc12009e688ca22178361db99df8060bb520740138ddf5645f8 11d8b6d32bc7a35768648d631e4d320ea8b56e932f1a2cbff0d40095fc29f80a af23e51d53106147d060673d7fb4f2a47bb2ab72b2cc255797fcd818f5135220 3ae4c97b960f65673fd5e01d2c92b3220f6b285b748a546027a8b1bf59e8ac6e 76d0434adcad2173c4259116139ad0fde9b2ae309c1c6e332571794cb588d152 08d4b2c32fca4c8879102396427058c720babe1e39b90279bf202931a0f621ae e8e3cc79d0e05cfe951707e1c903524084e4aac4c30f9179c0be50b7beefd7ee bc9c3ed0a2c25b1e59d70d738dd9da4ae3fbf7d535fe58846d523faf47bc42a5 127d126506b2c410e2c8ac9ce472b1d261932e3fe361dd90880864d003812be3 c894d10d2ee5c17280e3e53c026084229c4e9e875cff20d608a1ef71e4573320 02b412846cfa7350e024294beb8fdc1e2587557e8a7b4452d4e9e9eed14c9702 c5fea97e5de8cdfe09a77f6f78a302e9db64df2137117a848c46c79079b6cdcf d7d0505214530ea4cd905270f664d9ffa08585a7ad28123156f52bed892f5055 fc78f270e04a04f38ecc4b754ed926fab86d7fd5cdb959e91b9ad15ab5677b6c 6fa027b87aca6842606c8f8bc4159221eb056c00d56553ff60884ade17445fae bf5e94eef26a03762c215cee6dd95140ef2ec454c2faa05d2964095992a47705 29440b94f3701df4472ac2647af0de415c2fd191c23dbb8b8f096d3f7ff490ff 6d6ef06df5e113380e27e8ea590b8548c81a715de4b54dc9a5d35d1dc45dfee5 c8ead1f8291072758ec6bf2c68a6d31e5f18a9906b3f22b9de6541f4d09f4068 179c75dd86d37ae7ad1ba856d31f36d6a0089ee1a363c540f0676e8dc727be9d ff186cd81fc7dffae43c919c60cc0da74e722d269741c3694c8ea925ead329e5 901ab283171d866625e3b45fd7fe13b6b829a44224a37795a61683e6addd1704 25 da5b820bde38eb31bfc3d0b2b1e39b25090655e686f2d6052d1b79927e0f2306d4b7d48a5e530a48610ea9973b0de934ff419503410ecc9c91acd20c41f83601e68738903420cc9a8da0511e6e70cb9bbef434cce8ca854771b2d7a472201f0340783cdb8f906721a7e359d32aba722fab5e2a7f5a08522f0b1e994483b74509d4373ccd743edd347582919269c7f2be6933841d6e32bb670d57c223701d0b0b1e279233831500ed654c259d5318c1d9639ae8ad35f41d9cf59c07e2b4590c0af1f42c2d90428a0bbb2d4d54f3f6b3f0507379fa4b16cddaa04fa92719fa9a085ab1ef5187f34bf397502649870064d9eca0db720216957b98c00e1e3b021e03da570c4876b2d614716d2dcaefd73a7c63dc8e09444af712ad4fc6bc38178a0ee076451c941600d530fa24c9ab9d522c30d4c5deef7b80614a2419e990fd9406454a87048ee194487a09d382fef40ea0c2cf5ba4e576634300af49229634780bead71965b58778ec45f5080ac8f9cbcc7f272811d07e17b37940908e9e1b870f8123b17cc1a1669afa816289889fa1bf961d1dabf0f226e0f3f8df48c3c3bf002277aa6f88cfcf760b1b81481bebf6ea1996c9eb6e0e0cd9d0672548d33429095e02fe13f3ce8b894331aa632e99e4f628283fb05afee6c8dea2c383a61c220f9923fc61786ba32b2dd9b16924e268271fe154425ebddd94dd20baa7f2e2a50ef5a6e27d58d295b747cadf4ac44e7f38a9c9c0d50aefe6ff10a72a8e1c4ae100047c11d65b281e5da92cd8d1fede7887a0a05a2dace30da2c09dd36459c17606d35020d7bfd33e0d8b836b349129ab5e2a6615d6dcf68d7d6bf44627ce83d30185828be29959ded7b81d326d6dc949b3b12587309fd1bd38f805d4f3cc821a0000151e089938da02e0dc44f6ba3674ddc4aae4997b4906c828266619d6dd7a05dbc44a59f86082a2e0430b32bc91041e5d9c489d03f08584a29d3d6419e2ac0800686816564f654378abc3af93a4ff3d1a4d42664d7a0cc960dcc31199a8b10f514f07e9535b41eaeed6f772f8479630ec77e356b751daec86fccc48c61179089d8557ca281bcf72a1b6d21d205cb8855b7b65d99829034ee6e5ca30ec22f50e75650bce7d504e5e0f4a32c8cfd7c4307d2fabb23713d7e009cbc71a582c0b0702d575a8575bca09ce25e2ffb0a6ab02548adc86f4f2846445e46d79ea414307d56aabbe47d32e1cdfe959442776354eb0f59298ceb547d6246319a6d6ac2f0f7f40f4588e56576ee386bda7e6b10d779aa2e445a7e47ca7f5ef209456dbec09170f0ac0a956ccd439294acbee90d765a9f41a130b224943794a73cf7f29f802733140958309f48e4b60545f9981d27471e1a492bf78b5e5aef6dcd3af663c0c19d20b3f7abfc5c5fe71b4f8334e02838d8445975736e7c6e373fcb81727bf077f3c16e3c4e53869eaa13e0f03e4a26c12a87c27431aaa4096fe84eca7936c0068bdd1be6d8befbc5b25e4434f3e63bad16bb6782410421d51d9d7eb5790150140a944a24b35c3eb888665414debd0ce8730018d2447f13afdfc1fb5da0fd20905cb98cd5479ee862a024c87ecebda7320fbae371d2cec529d9d816c1ca9cd023e304ad718764fe4d461d631a48214c09239e2d406f443929c6af39551799e0ae8b15211e45a5d3c3af2c7d0e7881e344fdddd35349b2da4dbf446ff1ba05e07f15ce41b82694e2c1677ee90275863d67cbfd2d4942b1e2e547bdcdf783a1607713c204f66e59d04bd0e07dbf3a40e0fa0de421df54824fc3d80bb21b71e9d024622e152674e82995ab3c9948ab2fa1220de8a22abb91b1c3d1a618da809a508f2b926d18fd34341de00319980c4aca1b97fac74dac4f2f15617a64a24ffb30d8ff3eb619af13f199d377b8462c9a18d0cb7d02d3040d2368731efebec8fb30803daf3ae902d6c85a63ccd6e5a8d477420106e3a63de537495e4d1e7ab5bea0659a576f1412cd21fd06c109f284855bd79b7be0b3d0a6298ebc956d3bd683f03b7e16c7555fa5cd55c7164729433b3bba3e5f3aa41cd9951d865f43e2154e50da193cca48861ed589dfe1c89ff621121a2ec85b4825f34e9fec84fe72f412e08446b9ad71591643cb0d1d0da58493bfad3d4a57e4b1b6ba3f02cb9b4b0439f036cf0bf4a44793b0828401ba51a206d65934b3fe56200053f3a78ecb9e3837701f4a1d0594ff0575fafba7a9f430b534a3338ea44513630bd3b9b940e126dce0eeaecc75bde18dd8539009ce2996501ae3573027c7fd6553de697f9f3c0b49507e1489aa4ea5e8b37f9f3d31a72ac26b8bd2fd07f2bece088ecf090399e06290486a43d855f8006445450edb5995aa58397550206d5a348705777d542eab2c801d2cd14626cb34cdcb9ade4ca82d73097a4956b50e6db42d7e8bc249b30bbdb09b349fdd836ff59058da8723770607f5fb1178978fac7421f675ab846d98d72086e8849bc938c64faa28d15cf9c2c06f0f9d4e19e313d0bc2f1457856ff3e9b0212074fadd6d246c2a90d5e98688eb887d7503aec3cce39c0050a2a44d84a70007896af0dda0cc034183b3cafe8402fd9e191be67851c2af9cf6c80418fa67709070b9b385d42aa5e05adc6158fbdfd85f96d0b8cb55cd43ef8b4b65b119e9f0c14758747a72920c3daf4b27bcc9324b2d18da35841bccdbf3818990e394b650174fbab5911500d92ffd2ae69ae8a6049f2ce30dd716a848fc10dfd1efe2507082c16f9de83c7c9393e1a895a7bb3b2f705f45f69550d58895d9fc5b058fc2e03085a2637e2c8c71e97b5843aafc886ca0a9d712d28731fd1e7bdccf624ffb20acabcddf8296429d1460d9b56f64d9d7103868801fdb67680c1086219255bda056d531b4d882e1505f62b54487cee9c95899fbab8c099a8b0889cf7c23d5611071359c3d737e7b79379a006e8c495279329d452a82cb2985a9e793203a3d99f0a3b144e156fe4140392d7c9ba84d3cd0ae20ec6641ba76b4c758f8d4e78e2da0518a79d1949b7e11da1a6177afe9c89f14905d8595bafcedf07a92f7a9a195a07394e557f55786ba65d9a5223d247c02cd70ac3be5d0eb6da0b40caf1d5b5790f513b08e09011df1e13f4a691d360676d0ff6dac0f1230c66eb7f53e80662ef0e9786e76e220208021f750e8f4ac8baaf34d1b9dfe2224520a59ad0946f5cdc00c99aa7797db1b317e50f65741d9aad5a766a53d2075feeb729f73ac564562a00c9912bb6f97f034cd26103940df43caedd962c634bc62f850560a1ac2ef4850b2c4fe0b14cc32fa04e79f223d18f17645f495cf4d0b6133d4daa33c3710c440442d5cbb9cd09b1bf4cf1059055d6b193875bc9a4f17b5c0dd7a2ff3eb42fc90649d28deb32cb44d58cf8f8208c3fd44eb7ff2c9310f89b3e55c485dc404c4206a7945d6de34b94cd94a7c28e61c4f7664ad07b19a7e8b7a69bc7e29911e3b40815595b03d32198129ecb1cb1d4c610fe6835dbf90fde4190e80493f900ee4d02aee8085126d4408a5d2158ccf2d936cd7add54d044590cf274253ed5b7e808078637adfa49b1d083e4b6fa86c05645f7e960e1eea146d75cfb56cb44e4794e0fb759ca264d121b014dd874b50d28a5d1cfcd391e9ab6cf7491c3edbea747390c8d9cebf596660f37f4527c1cefa8e51126b9148725603eef510b4426bbb44603c68f169e3384df4cbba9dc0544d7f70dbfbce3502758a79f11a5965492d3470dfeb6c4ddfaee29f9fc6b7cbcc39da7241ce2e127770057c11e87654c6224060fe2eddd61397278d2766dc94fbb5c99f35efc4fa4133bfd1950a065a914d8290817be0ebfd84850a75fa0bb73a567f00e14d23aae2fb4db5d20f5dbb7b7fd2402de2f5e4c57c460844edd43bedf7808c03dd8126b4f959cc14b5e5d408fe93207c2d25c87984b3369febced1d76d6c7c33230aa27d916e846cb3c007fcd81480bb707098cb0563361d09a857771c999eb111b7755f0f557315bf5ceb25c57a0005ae95eb0928e756a4640aef3c8e26d64d4c35c7e02660f41e5fcb6d9e324490a60f488bb343be2a8c8886a73816df50fd05570edb97ae3b62b976db81eda4104abf191999959add0321ee9c82f4f26861fd5512c8b3bf007570a7ef2613d190ad67646371b47849a51a8d83177200a63ffe4cb06bbce5dab4f472c72f6fea70507682fc3d2537e47e5c954fcba184d28da5c65d97f619c849dd0bca084a1a500c0d2e06312ad8f462d4c21b2731b47f7ef0c6ee43727847c3198c6af48a928045ec8d489598e5d21faf5d78fd8d71f03d85dc1d693e5938eb6631be2a50ecf059f06010fca778ea847726258269f9e179186240d60d986a1a49aa8d90e8d310c57311ed22f493cece94ebbcd9a4f691f59cda97152006a5213d09d95179bff0bc7880a8e928463d7faa7e880e65dbd26f199c8366cd424b70ab1bde53c077f09595ccb1ef0c05301e3abf562db74eea16fef684f5433f06408eea82f24480c083349dc83c9d592a1d42c5b6b34cb9bc56b21eddf9e83837ebfb91477e93bc3073d890ef0983e9ad71d7eb5609e06373c9978b99074697f1417ed053c8fccdb036013c91b14a4e31027a81768d0286f2786f447c42f07f01e436b7d03a30fa80b338bc4243decf693a9d1a8f34979f57f609cd9208c9744b3be72c8bf8b66620f66aa8db31e3fe208fd8485642642f800185fef41f3b5f41465ea0d279293fa0023b670e4f9c49dae6a37be67a191de4fdea257acee99236368bf1a153fb011068e74c617cc83be3ddf1fa5fb40eb1f3f2d20a22cfcd9f21c6a771383b446110bd3a44c62fafdace018e2f140e73da46d99d610f36e80f903dc1d8787dafafb03c7efea6e7c4b9c95aa82d28ca08ada518015e737be743047c24d587faf571f0a1d102df0ea165329f529b0717e639abcadecb65aed32759cfaec33d82c9d840cac94d10b0505fa58305fdb4e68e70c177e01be7dcb77b8a28a525dc34b27b003bb0f1bebdb995a056c63eabb79a1dc13b54da389a1191ebeaa2307325c4bfe02 -generate_ring_signature 246b0d03ba218cdcb8142ceb5233708fdc1f453f1a492cd962e0dd9b191ea00b a58a14933f07ddd1da220d588cd863ac95d4745b46b0bc581c8b608b8aa48f94 198 0db161f672914d04b3da1ef8fffcdb431771826fc0c197e8825f2b16f0e1ff2b 6a77d52d3688f88f6b8bdaafba20b78065cd9a9fbd05213a96a125ca224b8eb9 e2ea7c4d07361af255042d42a83fa1ed8c49a9f454c7a8f6953ac4a25efd7803 fd944b028ccf14871fa10383afe9e30660a86635d20503c3001960c37de2e69a 61410433de0ad6345b5991355caf4432ae0f6d68de7b8b92fbee59a8ef3461e6 b2a13b81e18cce2587150264dcc9a4b7d5db29199601ce267381f912f3950b7a 54e61f2d4c10373f8ea74c8c262df8130e8d9b92529d9f4010822561ddf431f4 243e9f19ea8dfe283cb4cd1532744223a9530328854c771da9815e411c3ca262 f7c880c639b2cdb9f5bedc069e18dd5891428036391b3872de804d2347e06bfc 7899ec95851d38036fa9305b0d06221227ed571c0f488f99357e74d0e97f2f41 b55d7ec0e21c1b1c397775e2877af9d50328d9bc1d1d9b41075b2c82e59c60d1 86da65cf84ffa0efc5d851f16927ae1ce5cfe37f22ca1ba6bcb0b0d0eb64e5f3 a0baec146642142849fc2bbe21b3fb8dd36c5bbf4c4c8400a9a718732be93895 18e7f44ad2e9dbae18937a04deaf986794c4d1de98423a0ffe9e838505218ea2 085fed1c67c7a5446f05ca2258b1dfaf302d2c357c5626db2f6ff1472be6b789 9a7a89bb811e02200372ebe05f263dfea069011f51616bfeaeeef4d06b471123 c7da7fabfc31f636d3357b0153cfb6dffd2ea29dbcaf79a51b0c8d2b9ee141cc b49528744efb738fbd350f830e7c94d14fcf929490b1a7a7ebef9cd3c0506bec d392408898105d4fa4ef7a113c5e51fc9ec12e73d3a3b681895c5d1f8d64f9ec 14c14d0a7083d434ee67caee6b865733aad7e1c6b53260adc4a67ae429001630 d0a66f5a0b9f256542b2f47d20d9ef0c9f0ce902762e794cb6bfd0223bb03309 53640b7ae4b07158cad551f8b87080c9665cad08ffae960cd538eeb202566880 333c1d4edb0dfa89b137577862e6280720ba32d5a8985cba281e9708a350cdc1 9c85633679a683c2c00a653dd983fd6e6939e9a5b3fb1a91857226ec720de3e1 bb1f5067b77b9f97c17f4a94e9f23c83ce86290fa675f0af3a88bdde6a41c8c6 0237b12838ac6dbae1e8129a75907939d6995c2823fad75c981dcd983ada76d9 5222c496e1219b242efb961b9061080da46d7184ea84ba3f443e5f56e6f8e301 1a73f7cfd54d0c215775175106a40c23a418861b0ecb10d9aac208b5cb917dfc b2ff34e86f46a9e541444950f04822c87a9f3fed09eda2e354ee00a25adaf5cc a37896dbb89f13cbab665ebe009de96110741380b6ba0fa3303d025eb2328c60 2e123842381b4b15f6c08d5dce5b7cd375414fd0309fef0f0234da74f928545a e594b714f1b8cb58d6c1784233d1889c036ac446f4a109db65ad3e504759d3b8 44e1e6f6481996fdccbc53330ec84315b23f2e6177a04a838557bdb2bd967ede 21c095ad1a318b4cb437bc85018015dd88f7be33d7a8dd9866c1748e68586c5d 91c5ccad9252630c2ea99586a87420e8af58c3348ee379c2d1ddf0a221b8a6bd 406ec1214dd65890ee515b98612c06de1fb192b5a9c79a62a30119622a63fc92 b97aebdab39ded19b5151cde77e0dba3bf884b20d76e989f998555379e279d1b 2ce46bb4e29bf4020c8f89041a0127191c91609c94d10167a8bfd0a05436c7af 38dcc4af14c5ef185e7eb728ebd9856666ff74e289176bde4e13f2e89c916b64 22858147faef84673a869220da42dd7ffa011b3b344c2e78d5f060207d030e34 efada635606131016da8e1cae06032667d020a4647fb543730168e424a5b8e3b 267cc134a2557c011b156d1b9d5f9d12d8cc1ceebf5e3071fa96878aefb28980 ec9eaa5c03b8773659309ddabdaa3cdcd7007935175ea3a6f5b3b929b4b45994 1832d72a8c8092c0d1edff25076be448986ffb145a7eac5f4bc99665bc4e636d 76ec16c44937efe375a1923cb4cea0d42a394233d00ed4e77461c0bb19591f46 387cce667b9796139aeffe1f79997a0d019299a8129b946a849f5f537cdbdf6b 79106c954375fcd054ee0e558d8959a6e8b100c1b2475c1f2a30e12e936125e6 7bd06529c0c944410e0027d4b418105022bbd479d2791066aea07763a2beca9d dba510ea42aa855ccdebbbb71fcadeffece08803f3c2760c1822a1e5f4fa9663 c98f55e44c5213fd51b52e869c7096ea5fe9620f8b183d484d447ff8a7779778 1dad1ca1319f93621fba40e4b7ffa7c939cb43525421df2b096361be1b8b1ddd 4ba49cac2da0d71749bd8a99f60bc231bfdc4304ba7d33323a362ba045003af8 823f604a64a454fae850acd9267ab3a7eb96deec9476d2ac69c2f013023ff2b3 afcc56fb9d925c8c7172178f518f795dd100013a35a22469aa01850d5a2b8933 3b02bee1a2346707050462002b2e0624a7f3beb0f61199e8082503ef7bbeea13 a5bf9d17c3fcdd0678cd6e02a3a44cb04127c3cbe0905dee1ec3e2f207e8247b 167cf06f2b5cb320d5d8e82a25312c768d176c1a8d7c2a978b0a6b355d9291a5 121f682795848f0cf6881bfaf24cd161e1d48ec64861a2d3d6f5bd5bdd8d1c19 1c9bc3a726a992f1e7afff27ee6cccfcea8f103dc018161d0bc3cd3367bb8424 4a5293fdea6a697bc9a561326b2ecb6a364a38d28d5b51ac517f8ac36a598633 fdb2f1a88848466354fb68e56ce89e17026ca60d049e063a67d85f6a29848aa2 8056d4516ab1042b2f40fe62a3e1df9eebc350f4cd39d7c60744a25f127f9f24 43039a60e66e80cd341f0d915608485413cf54abd00d6f74ae7d6b400740d82f efaccf178d84c5bffbe97a3f0af3f982049f3b4f7ae981e80b5caa19d887b606 532b117cf74e4766c38d3bc22de22b831a81496e04eb9f30cad2cf1a08c2e371 517fa25d585f74a45202d4583f084d656ea1854b0d03219792f4f3a28f23d58c f472a57780e1b0043e1c06079f64664ae911603ea663becd80d6ee23009000b0 eb6d7f2c9d42f5e9c5341ae6df2213ca5aff31308b8e56b6d9600fa2b3283a29 224239609d5215fe464286cedd2a04cf5be2b4865f3867df83543b1b9405ff32 54ce5094e3d07710882a35a255de37cdf2cd31d9b9c121b3bf91ab7bdf65c887 a0665b64ee617e93e6b2656ec248f99b568dce3cdb93d0cde77a59a63153f467 7a87b59a3b81eed644cce872635415ac56b14e8ce1d57758e3b17d208f802ae9 cd266d34ce426739a6817e41b47da687da15d0fccb44bcb63afe3e6d21808304 91d6a6f62da7e9086bb77ef0fa1652678f4112bf8c58966141812a41bde21014 7f11b0dce98b3b5b753547cad841a49bc70c50979cb7e26931188b2805562c11 6ebddd1fa58696e991018d0bd21507c9fac9670dfc09b83460831d0f99d6dbc9 d2a836ad7b06d482f0473cfcca188e2667241e2fac3c61446616d85e2873209a 0787949bbb01720189c8c681c73df597e5604d268f8ce00990f2e3d5f21e2cbd f7597c8aa7d943292de1f422b9ab12ae9fa26e6b029a8001fd6af739b8c5d16e 3938d0588802af284691bacc09e8924d5339abcc01664cfbec99510906fb8f06 0655a083cfc8ced5a2428b8ac6d90fac5492cb59d492b50c82ec706a9ad621f6 e67d8a5f62c1d7284daaf885e438f79fdd3f473cbbbcd7a12955ef107ee85c17 fe075fc6115e419d5e51a05bea69b864323bb3223ffd2cecef6e0c34e5b4e1fa 231b075c7044e047993781d57fa4d637f5df0cee973f033702ba468fd11a1e6e 63be8069804734a71417e6c6492aac78377a94411dc38f6ca94c1687fc0fa574 a71d86fb97b927e7a2c37baa95b6be30bff078fcdea7cee9ffb2ae3032631867 2868f924fa5a7633a1465a19064f6fcf49236cc893570256b5ff83def889dfee 9327dcd74a80d8e531c1355dab619058fd5cdc4fe01a06f037818a975a7fe74e 2499435cfc2c9432b3888b7a2807f192ba19b62b45d7d273d7f5b0a204d3202b ed5ddcc23a3f887ce3fdb9bb25d758312e7909f9535f9a3ff1624cd86f903a3a c32b0e534cbe8a00ebb0576d80b80dfb4b75f5b17f8f38c9c84e3fe49d13bb2a 9fca90b55ba9b6c5f2c050d2b2f1dbb3bd9e5e51e7b8476cf1355421b0f59b7c e7f9c9aef7db8e8dd1078201a3abe2293b43236ab521c8b0792d56bdd3008246 c09f4b85470610997c9946ed24c314d56689990756c11f8544420cc87826e683 38323e53c2f779eb1622324be8a5fb006af48ea5ef5e3ec00148a147b00f6542 1a06eb4466f0ceb6a73be5b1d799835e86f6ff309a8397a7738351b9f5cd1a00 f1ce554ee1a5e1bb952ae702018ad3ca23eb11ddcd1bfa11b0c1c55256b1547d ddacdb96a51e0a4627d47459904ac22d64a220a1d1bdbffe16f5aed08820fec8 0022e83f981f666993ce52fef3d82566be91bfc1954673b07b7ca3ff5fc58e49 4fb2031fc2197a88d496854b3935daf08373093ccce1f3e69486e4b35c35961a 12f5aad6c8f040e99b9426b95229f2498481a69f4555fc2a593768b68d8df433 a356628599009e89718a1d1d6966f28da025984e3b7dbb310de1e39945606db8 35a5213725ccf4d076a71e87981b63097228bc1043506e1c2ec429f13355ad88 1ec779087cfb19cc36ade2d26f64f9bda01057e81ff21fba482aeeed4ac12335 a913b76c8eac995496954feb8bcc402d6d6f4e800789ba28bdeff1025d818bff a75d75ee55b992b952ca5cd226383b3964322796819dba518628f2b4ffb2fd84 e2ea18fbd119c70d15f4223d594d40202614979a08ca9177ed2c8c76862b2e22 a89ec97b2fe0a79fe66416bc90f396a54b020381e2c7942fede9d56c8b2a36e1 1d27f8fda1fb4ebb4ac24e91716e636398c702214188591e68243123e126186c 02cb80704c2f7e0b1776e82a7e16258f0a8a4c3ebdfac82423e3f72b7ee56d4d 9a524da42a15229a1c21aa734d09e4a71c0ee202f5334ba972c59e00b0871fd3 04cb489a417d31f07b8aef25651e7ecd6ca857605c720860839562f66bdf19d0 d543251d5e015dddb344028dc9325456e3a65d33c5de7d3ffa0bc4f77bfac4fd 70f3c9efd0122620789d9b67d84eba1218f40df0ee75fc6e82566e83e888b990 643ac4025e417233bdbc3b1de190bbb1a0cd3c2dc8de7b9ced271bf3a067e391 1f86b62e7e9a8373f99508c94eae94e160610de9a2885dc75a4704f05d569b37 a329f36a793abf7b70440a1b88da55f88de436e4b65e75cee79957eccc454d94 20493dd3fc860598e2953e9fd29ef927ecfc661443285e7f44b55aa2853b8b40 dec1cc91cbbead300e627e6aff37467273a6deccfd077b4f016bb2ac7f54e591 49bf801c8c676cfe1313e692b56aebedd91546d3e3037d72cbb957253777313b de0168b980e3e80133c7dfa74f76a8eaf7a6e39c4c6f5e2bb9cdf9bc8ad94055 26385c685bb1bd0e809fc3732de977a3fdaf31e8547a78e18225b83120bc6e69 90dae46f81b1994f6952fe1639f741006af0c02c44c72aada46d6fe9aab0b879 cab19752deb880f776f0fe03f32025fd7cf1258638a487698a90302727eb80ce 90a85fe21ccee828f07c00b4c85f6d54516cd4bafd16a8b6a6ae81b2b5466785 430fc038dd9548be476dba2c31c6748c7a354e3c67966d3250900dd39c809e28 49a2d2e4c02a5b94b5ce54609f7dac32aca0a4b2051b784ade638db8df4eecac 4d6ae1194d575bfea52a4e09266ba41676b7a552346100db98908e927ebf18f4 1b0bb0578845f9c1f7c568ba36e7936729fa907f6c1a8c1899229cedaa1aa306 10d3aa986969f9c45562adacff4f74cf399294ae3d6a56c6ae72135f51ea90ed ac69f664f096b6a8684c5f014a8b984f1eb9ebc2cd7edd2b28b37ef25e3147e7 d348cffc84741e63d73f26e953e1a0c6dd8349008edaa22da7612600561a5ff6 d8513404ebf2c16f6f7775d3586391168e0846a973f2229d569a12a06429eb93 fdb70b83761ac9e5f0efaa7af09e44edb8ffa0183b75ce52ecb5f6dbd0abbede 595b8d61b840a5659888c9bc06ab291df02f4cf5199157e0a5b509c382198067 54cf1293b535ff5aacc99099974dd99a1261249472edd6e6b4086fc1cba8c90d 97c875ce3d7224207c85371c7b1def7eeeb5b4690762b719f9f04bcfb866c479 c348eb0c65094a6127d7fc807507c8302a1c69895a97364a2f01ca5482a20cd7 b1308b60a84a9941dd55462ede7f9a54be15fbbb3cc28d7d3479252d0aff9563 c9517163ac2088e0387a0cc36e85345d134fa4104cce020658fd8f542e836edc f64f3d2a5ee687e1f8dfdafc7b06b3b62f1829d4510ae5c41d1e637141b45f80 44a63864b9473c34d0eaf8ae941eb127d8413c6629b8c36fb08b0ca4dc214df9 93a1bdf9e791ee990bf5508b6bf513409cbfb4ebcb16852a38fcb493810ae898 fc94f570800fb6581c4eb3d277be0a34891423b22e1f3391fdae809f9e75729b 12122063caa42018d7beb28e347bf7c0831da5a492b9faa318f822ba6b543008 0a6544c33ea85db30ab5ef3944aa87b5243c6e44a3001f64ef98b86edceb9520 64b6729a7871fc0f35ec31ec51dc6e444b2a5afd02c508e336305f46d5eaee60 bfb50061c92ceda81088d5698f56c4ec4c554354060a38711460332d38e21a33 d77091a9d6d90d9e8d68d1070ae328ce59f7a6a6599068e7520af35fee82fc94 d09449e1b37600ad3a249b93f2decb928c3ab1916c38cca9b56c38e93ef4d987 bd1feb8de1340e0ea9a15ba6ea5bc4f897f0b3d86ab4b882743620cf7133754f 00ced965f3ad93b796fc644f608b94f5851bb8be1559a5ef9a072f07437cfadd 610ac68246075026e34fe339cfd31c413a4fdb201bd7cfe8773875ec5f08bb79 10ef02726da9fa5618dbabbe19e3670ee4167b7c9228f0c9367a53cd8c5cdd95 0137deb5bc0cc9a05e0f12f261da6584942c556c0598db0bbaba1704362a8fc0 f69de7e6f5116310708c3395a3e4e95e4e58aee0cf39670a69d167cb308daa8a 17a8dc951c11526dc54a5b877c78de0267a21841a3fb137f0037666f996795ed 28d84bbb485761c0177ff4b35cf8982f98f7092339e8d2fc448aefc8845c0876 540ffb7709f0e719fb5125e443b79c3bfcdac655671d33187b750bb3a27cdd59 b7e545642adf2ca59cbb67c5161f4fa5c82d42307201b55b55dced00107de1d1 bbabd53bb8454036ad63cddf866f7e6942a5e65d740efdbaf15393a46cc2a5b2 6a19d37e31a1d58fb394814334fd5a85f9fcd1bfc3b816990292f8d81eb2519d 98393707ff0ee3143d5c0e397dad545f7edce40c334f791df327ea542d31cd75 1f71b20fe2cd0064eb2688696006fcb45d5ad0b722891bd7c8077d9717914e01 3751f297c85b2064882e0afab06492cf1b103824cce5c111e2d841d8e8f459b6 ca24b137032418a3d5805e53f04ed21bf57524caaa5cabfcfa19d48061f5f612 b89270edc915387df517efe1ef3d13561001ac485733775779affb438e71d8cd 051a33c231cdddc05ee22d707ee5e8eff9d1ea12dcee617f7dad29210e08f6c4 9119b076330d06caee1dada7957dbea09e03144a122ab049bc0564818823ccaf 04e925699f33291b1ec77269f0f3a02ac1013939697ef6e1ed1c040ab59d213e 9ffcdfcbc96482ce62bcfff69719e9a6ced6e3cf172d0cd234ed9c7e8e5586da 9c35c55dd07ce906697dd1b1951fe1149ebd04cd7f31a0261804db2990cb6274 359f282831d45f2d2f915dffe9daeb8acfb4d99c5e2f1be17ff3671b378a80ec 1c10d7e23136bbdc3b829338ec8a11bca411d5faed84fce332ce88f7426c43d4 6a60603aeef94a7bacc061cb070fda7db41fbdcbb654a4999637af158acdf1c1 97ba1f621d285ca0222a2e31ad7685dc7aa40921f8e9dc0925bd84854d05dce8 6ac558b2d25d64f6b3ec05a6f1515f50474071883e3212cca0f1a84deb99ecd0 e490ef89f3ae5442e0e7afe3bab29cc80801fd82f32b65237208a5644ba942f2 7a904de44a30db4455676a6cea297a0bf420f5e9cfb9fdfae8aa833995fd7c65 58f12241a8a1ba839dad5b602c6ba45cde6113125e59f9052da4c832d14a66ca adef9f1a7a83deb20af284ab72e4fff3c573bd3dbb991106d40f4f725f289c8c bc3930af2f43c34e6d904ee625bd2beebd315d583eb7523c18f6958526bb131d 86e0d0697f039e5271ef13abaa28ff801f90e6bbb79ea905df55801bd0462286 c239f19f3a2be47fcd0a23cf95ec09547157010d8e6efc3315651f23e2655176 99e7300e40160c9b8dea5c066c1578aedbb8c9e35eebcc8ca77f2b2ea2b02dff a1a55f538026ddafba163ba90b5f348df85a214e35420cb11477d8391f62031d a298dec7818c28ffb74b359efcc38eec4b32c1e73e0e8881febd3bd04da66040 b2bf08059fff6b7ac09b835bcfebf106c4386ffab05295dddf769653be295a54 6a3938a46207a8cc52ff35a881a7ff5fee19b918c8d354ec074a2943636508c6 4e96f1e895e106ca0ee4fb9ccee77b63ebbb14c1d514269affa917a0249413b7 4f1e9be08fcee2d1ad61a1fd15c8f0c9a1aae378a763e1a953c96254f38b99de 8321c7ce4d12a3ea5003bb741646c771079875712d53d9ad50068e88b7de9fb1 d0a5e457c374b7d188834afff3ab9aae45c86f7db697b26b21b7a8220563c46f 4565759b70e9741178085228485c81e1bcfd8a8ed97b9e80850b7052c3bc7ee8 133d0f3f9a55c3e714eecc0d582a6efef92f78eb607e03bd505970d631714a63 83a5bcbd0ffae9cb2f11def4f599cbec5bfb02705415ab68dde796c121523a06 c9028b0ade18d0b941882b9ad366b079ff9b1074c0dc2b9b1286f2e3b8e6d15d 3fdeb6bfffd1afb9ebfd745e369eb82c8a328985803ae385f265e7c7c5417370 e24eb8851842daeacb0f9710304506f90d32cf3c49ea8a8463779a8ebb7add0d 25 00d673bd9ad8213ee07b5cc2adbf4f7371cc43d71c74f3874760e5ebb253fc04ddf9ee31ad868490e3e26d7c3ace2ade3036b119afd02465763822f05d2263059de00d20145e1cff9e4db12646e14f85a9a89a7fe27d5e8492267f4b16b6030c0045ffb0c63c639644dc0e0001a2e98f06a3b6a683d21cd4c8849479d45d8001da2897f02c40fc1c1db8af1b0d676e123d22c3b50050fe8732884e2ed4142c0b126ce88016c2c49d5302cdfcb63175a0694b5b1e2092f8f1fd6925c3f42b140692550e3a177a6938015bc839e18874b75ad972123d0302f05d16a0ec98ad0e0f9c4417992db1bfb2fd4dd4b769c332092e960d604309f2c1b0f5e9281d92ee0edc7f2e8624bcac48070aebd1d021f73405d5c186174dcf71f141e3f1a5e74201b042dc0aed2fa2be9130b5ad9721cfc84a5f6d657ed42558c9b3dcc747f8450356ddd8541529a83158d149e7606238075a3fe68a44b04cb3c8546278ddccaa0c3caeef1d79d329fc151e4fae90e91679d147399f633d06a163d4f09580f62a0c4d1c1a8292a3e2ad44a89c40ec5383b1f7e244e492577eb7c5040cde12b20a0c634b2418e31b9c18d64f7e64dde24d093f29da4508a2d78ac77a0b9dd61e9d038e8cc4383f3a826ccf482e4af76aaf0955e9711e341f300025d0ebe2ad4fad0c525eb6bc83adb10bc700a1038d80931943b895c01b3c779d0e65132ede960906f1b38f26d9db93ac4f2f9f5a83e5e9427d0dbf3a32f4d516a7cace88db5f63080817f61a2e8b7769668d2cfa1d7b3fae50ca261f68ce687a88a8ae4e0178490dbd087e3ba40c2b92afceb63a105ff97d7197cb26434b56d2e6f9a03f3b1030060f2a96ea7ffb24a76ce2fd42c6682789827369ddf7b867849103ea49b36b220067d9fd303d265c021dc7be071d6d61907a35631f122d02b2a0254dbc22c2ff06f75a6310489dcb63cf97650cd931a7f4f0eef3f5e7befb4174a7cfd4fe245202e10e05a24c7ddfe33a8fa02544f8d039e5f121fb443ad08c60f3c482f15397052f86b7d00f9a78209921d1367e4e63dc538050df0ede09e56c98f7e5c1cf9c0fd53f5336363198971797b527e6b00e2c6bc5e88a139aa14c5f8fc4e0ab8bc30801c4c6e1a2927b8558fe756aa57aefb347cd8eb7f148c31ef5e6f4db067c1a09d8555ef6a893ebf61cc6fa3576660996eb749279a5211dea00acc66da7b3020c14bbd033a0d9a90dec0a5b55054826d080759805b922f0df881cd9de9656d204a6fddffbf214f3fb74a4be0fb64ed0fe9786f912b86ff9065a8040ac8cad370b81ba955589c3472bd0c9023d49723663b4ceec8820f23e102c09150e71ee3103ecce7559586bd7f9f9b03120cac314894db5bd0b024989a616e1152ae763c5053ea63d5b02b64df0b2cb5319cbb21d4db15c76bc9d48ea153165f10a57d4410755c13d609d7274544b6508c6d766e9fd90db2aa5d2edb31be7566af0106f4101bb1733a5bcef3c22d9e350d6f74d1757b4b3e96918b583201e81a9e204412b0e54351f9004c68bb23058c2e0752e4e83bb0b458cceaaea9ca4fc6379b98fb30495c8a4a022c0c1f8205f03c2dec0be3c10f876845f96572abd35b1f4dfdd160a574ea04aaceb434eb6a185d620a2b3910e4b11f7e71908bceb2cbf7ea7613b0e7c20225161ee5a9b6f8d092086ac633db6aa54f9cb05c6244355f212088d610b8c364c13628bfd2a1f0ac63c9b88f7cf030202e01f54f49345f89394e9cf8407feffe229102acc4384a8ce76f6a0af466243288d22da4d1df237b8f0dd9f540c78c74c4cafb39278fc61525d77fe12030e38c2af193bc6e13e85e5e8fea79804e5eadcc3503f6fa1766e0167eb9d043b3621bc29fe36238525f75223d526810aefcca66110ddc08084d28431b115a983ec4ac1ff83f270cd56f3df6ac6b3370f06748781a2595b2591ceec08969624c7f78c5dbb2cd3430c12f2dde9dc88a0048238063f83d195648c1355cf1ccbbdf3f286bff4683c4ae9f992150d2b212b0312fdb2c6cab121d597400e41c4de7bcca069ee63afac912979d3bcfe960fab08c3fe91c5e18cfb1a761459fc37bf01507eb20d0c0b4931244b9786757f04d90797f2265fa4c3934f2bcb7ee12b5a1e9f58a0c2e4dbe8e9850e373632264b5f00b46094c15b96288e1958573aeaa8a24a43175e06deb300fb30667e7cbb9cdc016d8ac35f9b3aecda560af04ed4d9bb7062c98b19062a21668a5954dc8466fc036ba30d9fa0db79b8b5c6aab1c8145a8762107bdfd5461012e9aa8de80754ee02e48bd7deec54c580e7c5ff90f60d7d8071723d2813b5c780dc11b6b58f55c3026bfbd924ca63bf12fb1ade101b5c8cb84254a0d044240a1e204c2f14c87559073b7ea57b35213573a7e6a82a06c36aa5d61f843fa6ac77b54628fea1cf3f950066a2d9e088046f65511993e27659fb35640dd330fdde50f357f9ec4a01a9820fe0750974f5d7a1c96e7eb988260c0b4e27bee266631b54fad01e7f061e3d02000b4c15e36d39d95b75f5ea29046700950ed504c1dd023fad525e31939d14110c78d087dd92589d1e934e51fe63b9745aee54e5ba57357e9508b62856b256c6049d91bdd6149d78b060265cb6355af29d964799aaadc1ed626bf770c451410f052ee8416448efe93dfdfd8c69f9642514148782567134333602c038aeb92e2c0b5bf0c98ebb19ee57d3d2f8e33dda5ab594808f51d37e4a171a761d13175b78063f5c870d0cfd7f55143ce5521c14423b2d104a2da98df35ed8aae6597b21940bf6c9d05fdc05e4f03dcf1b2170cda16034553f10e6a2950739e0bfd418978e057a101c1769d771be7b63338d0189ca4bbacbaf7a602cfd6f29452e056048db0ed8e2a5ea65191732fb750f0f88c342d1e5807607e55b33654a9abab5df3e830fea282c0d6a97187ffebf106f6d15a2b797a5c27908a790a81b2994736725ca025278deab772b93e965c80e44f3838ec07c83ddd669be7e0f8582fc9225a7830e053fb57c4c38b48ecb6884db63993c658acc22136df8c1cf9f0f687eb066020862ad3dcb2f1329eabe193fb011611547fe0b6632a351bcfaa2026d999e1d9401c5c0e66c67cc8e167f90c8f12700b79121388b8ad5099ae28da55e829d4d2407e8edaae6f9879602efd707f5420dbd3c0476f7c7135f9ed0f97a8aa9c666c806f2fcd4be704b92dff399189d83460b13f1299799e6a9c4ed664928f2dcb59408022d94dba79b7d21261e1a2bf1d2659a9c4c90e6b9e8eaca04c74c48f196d60959a5067879df612f2ea095a656aa777b886aa22393bda314795bc4637d9ef802ec2b57b6a87288d28f8275cc31b2a243bf5e827f1a8639ec5c78d4fce6de71028110f953b286df1bc0b71c489903cdbc89966ad8d7c6999649f6bf692526fa06c995a7d5ed9aa2b451ceaba6fba67fe490527604bf333a1145da446c062e42051ac992ddeb36674d5cd9045f74e578edf20a61d602c1a27fee433cb678299a011c7f07d0a35ea9bc5adcb3931bc56b7c150a04ea483b013f7a7d48c940bcb700e64f3f5c51cf74ca95f75686f44335d06026a3a351e54055e8ef9133b7be800e7d09da3fec9f692f3f3b796593bd4536724003d5625939ffcbf3b89f0a385408089780472ad87f204ac2e4dc113fbac318b22d254c12de167297b70445269d08d86b75e2087f56ab7e78661fd7b5f2cb832fcc0b6f6216c52f10864b3636ea0776ca0959d20cb48e492646090df985b3bea3bfcec1b519443d11c711d34fa80b6372a1fb6b14f5e3f24d904e7a8fa30eb06ba93e75b819a36e99cb375a42df0e08b04dee4ecc8b2312a159b856aa9269d757973d770cdd08bf2711715775220902d4716b220afe6af8a3b6b73d33ad7f0c6b81236c3280fd14fc71819bc33c0ae93bbfd893fc9aa863da442fa9bfe2824c1a2f6ace752e5f3c1ab1fac68ccd086ac425146c16427ffb171746f2bd270b75b80a7170d980f6ac0ec6e891590d0a630c9e2be69331903401e1674d26cbc4f9a7a99317218aba0f8bad4a1b1dd20c762a1ae63ce72794d9345dd482665586524939540b8ce0c53cbc88247d4c8b0155c64a2b02eb286eade9d17c12b63753c91d84ef2d5297830f41427c452a54058652188b60485a782cf015f5a8eed7e633e3de983c1c77efebe19f9fb6c43a039da7c66100b1145f61e51505f9e9d366258cccf20be68202e03798f6846b430e65ab0beaba10e9fb52bd0adeb380cd5c05a9400957f925513c53ced212c9210c88b099a8ff56978b32aac025c6446209651d51b6d5c243a52ef11050bc80e106756d850042de11ae267bf65cb72d8019b2a5b146f6b6930cfe9c254e5a55b4019b27866ae02fced4a0d7cb2611219652c28de11baed3d0e4d83d63084e341506f4d13e21660c7f82976689b6c03986dc0c7656912fc21cb5e0df9d42ee39eb0e037088ecb8b06b10bb43750646243820ca485f36546f7630aaca23182bb78806f17a7fd465c909c11850d16d962744553792d07d09173ff38e739be682fd4e0eef8aba45fb882ff4951af2860cef04e86175b339a1f7cc3d929b0e43b31b100ff47ad2ff0e84bfea4b7f49bce0a044a8bca2003e53719b576db5b331dc276b072e732e09d787c0c5e1148527f04245c9e23a3d0e17027c3f0701186a813f6702e2eeb72c457522883e0a71eb553bd9a1f7c7630613f9149f575420c5e3852a0267e815e7effcd01e64751fe634e7764f720c4ef249990369019f0d902a0bfb097cd416d0f5c988ec62a8ecc87ab726c8c18c1cf2c0bbc08c8a6e806ecf2f11020647d0b948a10405f88bf9e33ae6c19f798ae58530b9168d6cf1a9357ddb50088e0bfddc200a8d0472a32ae0c25101eb1a4d3cb3b1a4f6722feba0b0ecc7cb0ebb83c9cd094930235d4e1729d46c16d9eec8ea1c1681604e2949be1d0e6a1d0b2baab7adc9716dc3b8382980218f9fbcd6817bddc116a7e1a1d4aff2b2819b0707b09f4bdc95262d8660621ecaadbab32dc3911e52f793bc0082d3c406bca00a3913581133e1d4d2386637574527bdc5aad6da03efec821606620139b2cc150bfeec22802856e5dcb3a543a4008fd3b0e2e39ba1e8694954819144221f52670554137c1f8305909628e18c2dd51c87f9f86c4d864cc25bd828dcb4f7dd75820803cca73d370dbfb282230051fc35f1022e287169e11614a22152ce5da30169052793e51b88c6faaf0d042058a17ea7d6712ef46b3977f8d931d09a2ed31e380b096c56acfc2b2c06a77f325761cbea9b0a3929fc00c33d49a670cf9a7b2fc90fde3db6034fe2c822ed96d3132b60a15b7c3020ff73ec7bf88c413233fec6b20f978d7dd5e1518b40239538c5558a0a932dcebc0ca40799e35fc188f967c6690e5c96df57ad269f21ac5bf2c75984618337331b46bc500ce41c1dab7e11b1fd007d8518dbd2755424bfeb525aa195bf337e7e45030269ca1f6e37f905635fc30501e38ab404bfbbbce675ebaa039939acb9d18c75b99f7e18b0cee5e5d97e090ab361cbf5c2e54c1ac0db5dfb08b90a83a8e7681482a1a88a50c1f2cc125a2e0a634fdc009c713a8e33521e3e4fdcea33df4bf38c954012305860b723aa735b08b36443f548b9dab3fccf9da52fa9c09b85bab587bb598746878d00154720e70eb557c7d7dd92dce1962db49f5f6878e45e966c665534a254dd3441cfb79b5507ac7e9646d55f6e7b5cc7ac4662317c8e8208077cb20cca87c26fca7401196e04fa7db6b53a47492cd63059b291306c09ade2cc5a3cb1bc1c900e8db2b2b41504c87d20392ad6b88b5d46831ec885cd7dad3f86c1550e6c00fd995c8c513eb00528508d5a2078d490f8a1a07eb86e0653df10219bbdd076951c31fdc9c25bb00e0d90501fc672b9f2ab816ced22a9e54404dc34beda3660bcf96817c6a1cce00d42c59705174d6aa099bff95ac8cee8ef75ee3b8827b5d7e02f1a21775b6e9e0a8691453c734ebfffed43ad2fa62c799849365016f40a92d04f8fc3e6a54f210bb194b5e1f4abc6fa4c36f85ea43563fe19d4e9032eabe4bc00b42bb5b99f870e4b21bff49fd14665142529d1e78b1a63464e135aa4e06cf82010ab2a03867f0b8060bc827f1b7f623c6943502c4717b17bb51be8bf80b7848904196cfd2f630d33bf5798540c50a2b4c7417e5e497d3a22b3757422850e292a442f5224215101a6103aba2356ecc5c6682271c47f73c002ccc4e02d36ea5f745b318707d2f20378c77f0a43e737cfe44e6a1fccb079810962cc2d81087c7ec20ffbf41398190341d5e564b7728870457d6e37d409b10cc2fb4a2049d0de0928685e03e6cf3e0e3a3607a89aa50c9b7e1118068010f148a0e7e73e5cd61ff110015ba788b2e7055dccb9776582d968cb3973c2c6823c82bfed18d8eaa7a4e7e2b8e193ca0f1501c133b48078e07598c4cb66c8f6a36308e0bec1d7c259358c1a641b8d28822108a7245ebf5af726a6f7079c420d9c8bc36e964c9211e6211c757af9809658c60c323a692a656bd67fc0cf692b9a437af09f131662cbe87c29d7195eaee48d2308c28543326ee511919a70add3f5309ea6d15f96ad8702f1e2fd6ab7a51010bd028f87ab998573d70434a6f3236bb0ada8b1c88cfd7406e4ab7820ff3c22f1c7047cf69c67158296cff23951e3ebf112b4b90186d7d3dc709394703760455d180065ad220535ccaf02bd21e4920e008f8983ae0aaf993789be429d07eecbf21206e479bd4cd6ae2a048a84e5bc96231deb2ba6035c73989601b728118941efd806e34a68e2ec4f615e03d631da62386035e767eb52a10eeff8262e3c060c20e1070eb10202c5ad7992ddabffc3d691523b688ee64012ca8bd6f7e7de2840e23600b0e4c76ac50608b3514fbc05bf68d9c5f287f5f1bb1c29b04aca9e7ad8dc190aff40ee8bb888ea533b74da1839780e625f754e68c023c0458a6e8a3d593bd208081f85c10bf15845dceb399f9c7b0d832179b7da12808bcd32f3bfa0efa25700554e40198f6d6828d995141d55b83340396fefcdbfe06e25c522403a7920100cd139d26c37fe9de9181118443eedec2008a2c58060ddf458a892aff48f804003829a8e56af0a44176b17bdac97e6e063612bfb49dca0a6d9b5ab1144deccf10c8e08ea38c113ef5261a542bd9fb196da7895b9ea85e8a877451183aca01d6904c47ddc4d830e20f55fa402d9edb0b6cdfb85019640e3a0884d39eabbcb0b910a9fcbfb1498ac7a183365b3f2fdbee9a679feed9d0813a2aba0581dd8decdcd03cd7b986e1257fecb85faafa7cdf95fffecadfd2789e9f56a5cff4c115c879000b7da8c6d8275f46acdddcfe14529b5238175e5886280e5cd93733a1612ab1a039b4100af818be6bb7e9998863578e39f127e3c1f7005998e69956b5131dc510a5b8fbcc7ddc4e1f0e568bdd9f9f1a871696efe4ae368a77d73d622a56d5a9c098e535f275f199c0f59c91b94695b6e78ac7b776f7f6c72d305ee2313741c4602b590a6fc51e61e4e08b1af70ebfe580248ad7a0cd1372f5ba1e3e43514a9e004723c245d6674f0af48224ae9f5fdc75a37869fd8a617b116ca743937124e1a0d98c01ee31a04058e9bf89b9523ed5668fd1829d829b706e2350ae44401c9a40a630c81c6124c2bd4d223f1213fd267811973addf55c000a0afae0aed3832b206a54642f1ac5b392f4cd5e08b200d23d83e185ff2e19e7990b565f2939f921e01188761ab9a19b5d245f927d8e88dc51fa9cdbc3f9390cf8a953536f0b5b9f80047a573a12caa9d843f7a25981cdf5bd80f7863ffc7c6521f8bfb88cf0df4300c546963e480e373adf44064dcf549edd78748b352baad384dafc65498a46f070a2895d808ca0f7cd66a95c1a372de06a44bff284f268a311c229e8d7a19f2cd0e6e6dd03d0368b51e243318994a085af9e2a2a6ab2a892fcb3f1246a3b5e87f00e193a007246d36774023b1728f3865953479f0a909890c247cc7c4902ea58d0a7f58c49dd84477a3c2009cca743a3e56d8ffbdee6810ffcf3468bf8973dfff0c3dafe461ff0278483bec7c84e8b5fb514b706a23fe29e2222c1e5330cc980f011f4e2978691906e602bb9b86d99f25b2dda98f83b2d6455eec79620029b1ec0bb2cee11225436294a6a77906fde5591ce6cc5b9060fe8edcf845a38f690eb502462eba65f19c9767e86cb91f5dcbaefae231117b5b99f3b14aa71d97e077d709fb494d64466e5870b436d104c8db1946009f4fa3e6dd78b8a2392e3a824c550608876f69b420c95777834a2bb57ec35eb4d033dcd5531f196d8eb12cba68730c2c0bb982d65a42cf19eba1584e719f35bdc2358c646a1972ee2b6bd2dd06fb0a1b87e859486dcfbe311c0c6376e72060c85866d4ca4cd95b590cc826be90fa079e029e2a4d2feb740921575a268cd413b1a23bca8e07441b5ae8c8e892344f0fa5a51eb4611e1c61470aed22b4e2cf335e37c6ea9f88b6dff8249039c841c00e981dcc8e5aa17a571285ba171aa692e1b21f82f412012fda464a539b423c950ba0c8b5be40de8799760a864cdf128a2aedf4166fc62df7f5eaf0c53563b0930cf585c50c1a26a11a324d10ba4b304c3b7c4eab59b130dc0b776aec630a7354063f7903395af3a87c37c90382d521bfc869bfd2069b9de5f67538191baee606007d5d2d328d9399306ca04b2b64ad59d411ab2b97ba44f790948042c1ecbfcf023d4c34a1a315746e4e22f0b77f33e10e4626f27fe1a0aa1f12a4ec85156b17066be6133ad36c2c114a4ac48ddf11c61a303f687ef7368d0f5b66cc43bd96730ea6d1be6b4f4b85156a2f3274165a5844714c56b6e1a9661c0a584b2199df710435847c91e739405af21f4532eb88de03f30fa6ffd7ffac7dfa5280b262b337081f3871ef7c8c5802f4cf7646a9dcdb34d16f5f007e184ccab842450c3d66ff0fe78bb941e0548b709ca58065a9cae12831245a3fbd91ced25795d02dc8027007ecff3e997eb7c1deed37c8617967bca4e0b67018206b607c7051d59c4f61140b7695241c9c2f0c7272f04d5fc656f04ab8c497d7d0a9745bd10fece75f18340af16afd0364b0ec85c21e5e2827d63bdd5ed6b428e47471ac1619c2725cb3a30e85f6dfb4f86f77199aed6c79c80e7b03a373834b1780f541e10917ac5be0a40f985763ce802628b93df1fa7595361db6bdd3d26203d722d49270cdbb51b5720e7a0ee20ac7e23081695f2bc654d350f598354ef9512f0e34a85ee2af9ff4b706dc59510b5d65ab4d32bdbe629f3462c4f263c169bd2959c9dfb485ef219d39072f095f96fbbbbe6b6ae51d1c9e13bb8d413144b4f608388f3ef3c431e4e8c1073dd0c034e171a5bbedc046befce769bad64ec37120125574767645aac3c7320476c0a4c04e5f392f0ad319afa287f08342266d2812213fb955383468ea3712030dfc72a5c8952a87ef57d7b6b9ba289093baed86223c417b7e907a44bc56e609b05e5275a17fc9d308147daa36e5b432c5ec28a8431c922f3ec67b2212fdad0ac33863dc4d48d1bea89706bb4994507586a104540eb2494a0f922b6951c2ae063b936d3b565ea2de36c929e26239de8ab477505d3aa0b72beef0407f6715310ace2cf0e1b6ff767abb3ace0ab8db0923a0a184b93dfc7d8711a54950d607f10a71fcc4f490214e51ac16b90d4d18e1088261ddc50ea4b72e7fb7c25412325a0a24acc91066f8ead152341a29c23b613ad9fcbac74ba03f62a4aad9ab28e6f90ccdb9ea3618084f092a352b2ca21bc66f0d6da48e3e40233cf6fa46fbacb42308563d5456ba28d2824c6ee3768939afdc10235f08879addda6462c29b83bb690f7c554b8a148517fb65881bdc130aff1432b843907434bf059bdc75b459262e0ad5cb9f699bef18d6a6016e60ad6a1fb7694c56b37f789ed1427a8569ad8b3b0bcb71e1a8d9bb96417ad4be95e97f589a5fc3627279067d5d07129dccc219ed046fcee9df4b8d029d0d5f115e0e6175498235793b05ad474eb3db831821cf1a05daa42d493ac7f520d3f17a0f29320a4368de95117da84984de7fd166beac0003f6be9cbb86bd4675318c0696ca127c05204ceb2927208512f0c7b1f5e75ea3021355cb51fc44ec146629bb40ef2e2fcabc830c00b0838d26502a3cbc5d8aed080e92a35d1b9d9a7e1e589f4c6f483ab275905b55fdf61c56b2f39e10fd6e44090028ffae6da636ae8bcac0c329fa0e0a54d117767625c7978160ad7b90e261041724795be1918d5a985951ecf9efe6567db7515f5fdd6e6d34bf063fa497c100ca62a163f89a57b283a69e68860eb13951f26c7c5b8a4143e7120be4c786c50ce3cfd98108532a914a421609f7ac1410731059fae614672e35fad7ec83d3740ddf2e469f135f5bb71ebb90b7b190f35f52e5fe65f66b74e9a8ae6dc48a7f7a024274ba9cc18e2a58cc2b430b544dbb230531ac467f53b89dfcd580410666fa054193ae346b16a386f339a8c8fd5b53494880a2ab31195676cf810a345151ca08f5af7dca10b38584bc4a05d7d020cc0571dc2858a7e6427105b935e9e813dc0613a0f54d4469b020b6a83e8bc3b6984f566c9980908ed49d2d63851ee15f01024240b4b38d313491d8da2bb5caa50881769151de58b60d2bd7f48070b850f801205e7ac213ea4c82607534bb8ff434d92f39a1e369950846ca8bf9be75747b083eeb3dce2fd7f06eb5b89708bf5c563f507adb203afa924e13a6965ef42d640edb44610f858bb420ef910c476f5fc30a098a0e6b8a108342a385237b0f7a76041d358077d18aaab201a4958ed26cd06fa61f2b44dd5b7eba67027301032e6f01ccd9f3bedf4a5eabb4cd3b8e9b67d7fed8123c3310743ca98700f3ad55f8930b0838ab2fcb5e152b09e2753dfcaf82bb4cab799669c17a6a67a9f0008e321e09e7307a5b8c9d89b2001a6847de1510267ea947cc3600501dececd6a6cf1bc507a96d4af18a290edbddd96d58ff0807c71cd806a05b02019322ea3b9bc2899807b44220316933801c17a68683e7982eb12edbb9c420d88108cf6478a6850f3102b0ed9d1726bf9077cff0f3b20721d2b051ea275f649a49793655605577de440bcb871146364ad0a551b8a260f649974f193ae7ec1e8beb1f5510230913528303959f190b49a79fc0727f4a1508fb52d6d1faf8a890aabe4b29b3a18c32e051067161f45b3a1b2d1313c86dabaf9f8ae86fec7f254ca87fdd581a1bd8f0535402f0f1aee74509f37a601b9d343122fb1b1679299527e3393c6fd316c3034e4303218be75d31c5821fb254d7fc1386cae8e4f08591bc5c6f6a9319be48c2f68605cde13e256ec2b2c3f4e0dbde440e3aeb7633046b9ced1f94c567f67fbf736a009780e4ebab9d283ee85eda6a1fcbb34c650a2585d4aca56095ea0d663702fc03f69d92c8cbf0d2c27c5d3fc091f897b8694301a89e4641abd25b9b893d9aea081bf5cab03ec81bb467c3625a77259971bb4c7036b2509ccf438367cdb84cb608d22da035ee17297bc249e112bc83b9213140b543e64f22797c3da5295b458009f0faeb165f304270a249373f7d294c10960b51552f63f727215c56931e3b6b0883758da47235802d91e7883734274bdf07195870ca00e5eea324c79b638113059ffcff83fe0f72adf53886dbd7413b71dd5dd14aa61c37501398d8db2737b006a73aa847f778f9d34a39748ecadf6eb1544d105777c08e29400978d07f544a09a30eed4c7d765a8a6c2e013815ceade3eaa4938ed91d5011f5adfc270945720bba12e2eca40d380c71d8cd684184596458f96375677347875513dfbd7f179201cbae9766bffc557b8fb9bc6fbcf9cbf3dbb89160bbcf96f3e10ea632e7855306045389d78d272e9311fab8168131c41fccaee8f475b3d02f15144d5d348ca304c6859b565293a4fc72cdd864fbebd040d3514d7dca6d1055e616ef85644fc00fa01528094fe424521ddf4fdd5c0bb9e2be2388a30465e9d2876cde2f29751509efaa528b985a3d2d728b941183a2ad1203447c46c40e82fc7d16ffbbc35e3f016f72f9d30a64604c102f08a4483cf9e1ac3fb4a1d9e89dbcb90d6d623da87e0eb84701e9b2c8851cc5ed71d6c8e9b6b57d5ad163f1a5b18f5ffaaf0360afc506502fb38febdc2b7cee5647ce822b3a933e88a1ab0bc027ee486622625068500f641348b791d86f925ff7d5a69b32904619ac42a03fdd1d51c352bcedd084d10d3fec39430dfeed6c107ef26e94d5a4eedf094d05f4c7658c458b277e02ab8b0aa5852f2f9372d201cdad5c56a546cbfee7b3538af61247f070e26f8900b7d00496e577555a199e4b97549a2bf5f7a422fdb2c12b74f05d7ba6cd8288e83fd3061d6417c1a0e21507537bcf82ae2b92c558c34ae3f22da0bea99a03aadda9cb01e400afd01b408fb285cf2799171a6a516a579770a01783dfb9aecfe46542440ebc4277080765c88045e3ed8068da82ff22ba3ca9f527be496449ec8b4dd3d40c78e00a475e78bc61a0255ddce1988fd3d0a6db3d9e3739ede2cab6143580b803ccce7724a5c49b4fffc5659f7229e77eab1f369f95e17dc40194fa932b98a30fb2993dca9f2a3c968c03ec93684a6904e8060eacbf5f90d528fe77868d168306a292f718cf26ee8807219e8ef48e205817b2a3be422698d47848278cd20b7806a434f92027c530aa076b7d48efb2c296777580614ebbabd4f9bd455cbc77f70f1835cc8a0c8a72fe5445b3ecc3094cac6bed83ebdcc59baa7e98d02bbca4dc0e868df25c974aa879ff01cc7f9555a9bb6fd6fc9f4a7f0f69e4dc00fd5f5f6a0444ea75e096dcccbb5e78597edfeb1686d24bbbb3c61bd01112186ffd332e0e0e618e9c075330731da36d61c103314ad44948f6cd4c2f9d0b5e89fac84f3223018aa5ca686256e02164900cbdb3a2eb7fb21ee4fc07d05d8fe4a2c56b880c5d098a31bda1b1ab262747a9217c69937ebb5029879a8178ad32736e601ff13a750878b48671dccaa1e21ce441a89eeced716dcbb1780b6e41328871365e8799aa0823f8eb8af8f104a003bea8f45cff780344fbb8b796c1481653ce819609b57b014830a162a2d9b994f231b9408cac54ef572ab72bda1984bb53a18a12071a4d0f03d0e79874bd4ef751df32a8532ea0d29706609e0d225e75d16fc77eeb662e06bae5c8343789269b0059d9989936711bdec22391047ead89362fb3cd78f51d0528c3cd79a9c6296ac64a43749003205b076ce17a57db34d2bbe8c26a7eb2ce0489124205c8ae23726e9d712eb459d03d229e0701d6084fe1f36b598fec3d6d01324bf16aa42c230be9faa7563b8e6cfa509b18d394c7d2d56ff5d66121169a05d66e068bcdf19da89c96f2154bf26bf6829f706f7d7fbd508e83576eef028406b15319f5dced8f66b7c77a776fdca7c11c42c66b6cf111fada3ed7c62da5d908c58bb705ae2d3bb8fd61be6de84c29c9ae7ce80b660c661e8d6b371e2e280202b6c156a220e5b5924cccf8159ee063a87286d4ed696b02cf6cf65efd56dfc900d377b78d22a7f07d0d83b2fb7516d35d8c6871adc61afd3bae8e4c1a33db0a0c4b25037aa81a2eb811a61f3bc7f982c208e7a41a93f2a3a09b9392035360dc0cfcb3306d281793179bc585d2822a49429ec2df1365f442bb8fd841371d8b360fdfb7348f860e003be5db590d4657f49f4548d193c2e9932a0e5357d377f6b103ed4559ce8cd221428ec947eeaa4e973076860e5b17890c1030a1524feca89a0e730212a0686adcf99547b2970ae8d6b39f6a53d7288b7250ca16aaa122cd0b0fb7b66be8133f01e8ebc2e18f3ee654f0b0763010fabcee657129c682731047073446981bc90fb66502385456278ffb98cfa33df5b136ba71e5be913880204d01c81fef1a8290ba7d701cc7d443617d26920d9ee00013a12f1e23493f9e207a0a0803f66aedfb700653c16d3b76f7c10f65e5df5dacfb192d6c0069aa918a2807a175026dbd3c7cb935f69884b0206d59be76827a70ad1de176e5263884a1d303d01be689ace06e350e18d3065b8e2784e8e5368f72cec3901c905ee0bc43950639a8b10fcbfca15149b77b09b0b81ec855c5c255f9379dff66292ab72f9fac0a1ab96a9bf2f2decab0fb5471c0b8213debc34df85f2f6096378d9e1029174a09406b67fc974200b69f802bfef9f66bc9150d8008f18e9c8e89c07da067d0100cec0d6d966245cb5fabb5bc355b899eeedf76d371ccd25f78be97a628e7166d0fabed695e5ae4eb12dba6ab7be4559d7fc8ee8e05bdc19094b74e2e703a52640207c7f044be994cd75b7ff3f37e43bb811ab22a8ada4a5ef154710bf661b11e02fc0add509ca47026c315443bb5e2ebbd24e01a231fa7f84d57756f07af5c0b0d70110de9aa798c77de491ec4a9167a46af95a11fc971ce92c194244fee586f0454e5a8b8d636aedc5b47d8a7b03adc9be4927aaef1c22e551023cf31b4fca10ddf57365432fc0ff8254a58d13196b29f0efa47032ac3820e116816293ec21b0218e0bd80c3019352d77faf55c105ef80f2a16a937a63f1bdb065f0c7eb7a6a0a3917a4d45e81804fd6eafa94f71decb2b5346ec4321074dbd5424a520c03be0b72913a49a768dc20feb8c41ca10851f5a6ef0bc375fa9642f97b2b04cea03d0304641fbc173135224c52cd1abcce0e6574054a956a6dae2a6678d3deb5a38603a940d03b6cc54a8d058278da3a07a357bacb5abe0f4c6214bfce5354a42c9b0dc047c7dba9c0b9068fea5d70c37586fd0f2193e6b44cf04b5470c04f0984120029e417eaa3906a1072981938815a58c823ba2473f4103c4f0d8ba5b11c17090ba1f79399ee60b8899157ff6dc56a7a0c7a5d24bfd7eec951c7227b3ead811b0f661dd80ebff1e826249120025e5747601b67ca1095bd6a7846575c86684578061468d2e773265f9b595d31afbffce810c6422b9ea3397ec99d51b530727067081609e74e2a1446eb05edfbce9cb1f7a5166d1c773d0c4d63a48b1a6916723d03815f9fb9d213e302273baa3af448d3a91de5d1686af795d9c7b3ad739ad9910f90e2509f082fba3ae486c67892d9c95d187aba26993925b03b52334ae20e230c0eba9e218e061029e36de795d01bac13749e3740c12c658eda78b6f5a4825806202dd2f63e55c890ecd1a90f245d47c3f2566a8624732a6d413eda57854b2602f331309e4eccd173a1f785b1256e1d9df888ddc261ae88c3ab2e62878fc253032344afd565b5a8e20d000ddf27786246b642677a3a7624771008c6074b9a350f9df446551dcb4c0c218922485d1aa2cb57c65031259a9a0371245d8aeb8d520db0edc1c3d47405cece6573eeaf77d1c156a5308b1fbfb80604fb538687361001f442b9abf045dfb1a248e610ab5a2df801594a0a73d4538e66152e11a7d9e40961ce66847b2fd008a0eff23a322419abf5d19ad56060c90db2eb0773eea36c0df61cfbf4d2b6aa60cd99c692cbdd9d556c40a78b1bbef66cc5453a44e05d470806b0c8ee412d2afb741ddd12aed61df8e177c0b267f4d904d2e5d5cbf147800412fea2c2811874b545ee5fbba8e515e8bfdcfd5402f9305c19c31236ea8e48069d67eae2f21d2a4c3bf06bb5b4b88043f095764f6d669c42415d303ef68b7d0af1edaac62d5e55387bc0327ccba9c27e7c7e492d72e5bc29c90bc6e116ccf50cbf9830144c8c637d3e2407b0bf915dad7054957ed8bc933fb8cf6714712daa009bbbc2804ac9b9d15471e96faeb3a095881224c24f36935c15ae9da89bd41605bd7ab6bf04c333aba2ba23fe95a578338d1fbc1033558c1e6ace746b78d7070b6e29fba6bf8fab0e29ba162964768f6149536fa58c75d7dd94c56e18ebd893009623ff67f98192c3ebd0619d1959de6f622c15c08859f5911a06de1061c8680a76a28c2967e107da3b402e36d15fe2d819eb79b0496825d20460f7cb0d9c22018d2e2199747790d9e7510eb7f9de7b22662eebfea8a6e64c19d7a2bec79d0d0cb87032cfae98c60e25ebab3148eb7850788e0637410408ad6b6c5c6ef27c06009584d0ebb4b4b65792a52228144c001f4ec8237f2b8498d3558f07b5313c7502c44573ce8730dd5ec6f89f784ef66fedcde12735797d9f9176aca7e76aab7102c0912a166db938b0145075ffeebb9402da23b619cb10384e2676b89ad3607f02a308bc432b8471a652b23ffcb297ae9c5ea6b3c8a6680a754891405ea001ca00b8e82bdf80b05edc29a1981d98e63119ed5ec46f465dd36e205e3bfc729bdc02de68a36e386c2f801f1a524a7d1c8d42e8f4f5e19560cbb0e778514ca61c9b03260d3ea2d6ac21ae1ad98f1e0ef5bcc1cf21d279e22e0109e0ec9a0cc044ea0e6e1a1a8b1c59e4ccd6d429c7904eb946b64a1a6a482cfa61bae0cabeffb4240c732fe8dfcff214b0c5a87a919667b3a57b4776167a63172f4e2beffe838f9c012304c3db9300b01217d9022e1f3e9834486cf7e20169390dbb269ea668597d0ded7ad389b663ced387b6a8c4a321a89bb695240184e997f55ccdc7a49f9a540e32aa4ee9333f0a66ade335a3c67fc21d3f1a09d0913de36c501defd7b08bb80266e80f490cd3e02e9211f0af97db2e4af52111d7e5b73b4ed4fc059d4220990aad6e4a0e73c128adc4245ae60e99e8e1647eaa7efa273aa8846af27d1ca20f0925daea177bb293b8ac18253c13c5c805e1de99fd6b39d66b7e6a63e5c58c100158ca18bb5b201c3d2bff570d0f3681319d8a225087bc3cd20207d641a967520ddd360c4fa469b72e8af0fdd03af77f4d3232536da564dfe88ce23146d547910f979c97da6c007de30695dcaa352d6c1f6b538e4513da5d66d0e3be1d8867070de7d707cc18b2450b116f44b256b17414bdd9dd565c99e8acf0dfac660ba6260607f959b8606a1f9e3a0d18d63555258c6f54b7adbe751e6e88c35823a1640704379891dd2aa844c8adc1a83423f9176524a24b9c245dcb161fb0b660b1bc960b3587a14a98e345ce1d98ab2029b8fa68fe6043a8a989bf9b2b02a882ab484706177990b5436cb07b4c3bee2c0f460f38c5a9e9688ccd5625ca93913f33e214036ef58e951a6b9e49548cfa5ae092c3626ada1cc1e249838a4380b33350ae240232d5f50c14e1c6296643309f3c97b24f23c8d0bece6780732f12f80b544cc006890c1c26581d4361b1cb19911f9b2285bd28155813eb72faa08a8541a1d5960ea1dc4ffae1ba2d9d283694f95d88ab169de313eadf3a4d8aa0c9dac7cccddf054b2a722ae43ca885b6c7f95d566b3f98e80ecd29543679b73a3edef6bf27ac043f685eb141ccda88065a65c936e6686d0ce5ada53db0cd56c9edb0557b012f03620a733001c2483bcca0d468093ca5466e8c3c4c3e06774365775042b5cf330defe5d4f5d67bfbaddf7b024e72955d2bcfaa27ebd9cb3db35988d14595d5fa0b5a5e7cdce37166549492107a89d969a9d5031eb3a0892ea4e5127d22ef4d1e05dd8167ab8ec248bb6c66983a113bb5785198ba7b110c2bf818b7e9ae3cff750f405ad3e6968544acc9cdf28a438e2c52957d787301dec87e751aa23379cca10794f7e4101a869beac722cc749576e52ccc63e52a6ea3a6ead14c04326c7cb70d788b5e43d7d62ec48e94d7a22d399c797d7e94ebb47fc4dd67618f240a719a0a25a5c808105e6289404b7d8d327167a3a19eb279a7e10320d89f968df8c2e80ab9f4be8943f0e190551866358f8603c66404ca9bbd103326eac239637bca8205c8fd3f9041dd188717bf7a483f3d5eee632d4327c2bdc3eb14241449da82a60d -generate_ring_signature adcdb7399ebe573a2e88f555775d8df55ceef28b0f51d1256cae8f109668a24c 447ef2e9196b56579e07bf5411b980ed2bff6414024f79852d9d4af70a449870 10 9152fedaec98aba200129eab484df0a6366db49094419265a78ddc2b57380e30 b79be97e80cfdd3ab77c6855acab29c655e3d1a07a6e2b53462aa7b9ef83bcbd 69864e7c079c36ee8698ad1b54f66bfd101bf1c835670beca641f9ed37736f99 ca0520d1c7e07f43082c5a46ad0dbb4aacb97e88d7246a60a8ba3667f2458c9e 053d4ad64725e121493158de54d28826417808669ce746b807d65b1f11839a6f c299d75d2531a842aad42ff488b2b76112caf12bbd500d503f43dfd9708746bc 04dc77ff1925242237f287d9ccee87c27ebd75ac6e3a45c357d9bceecd937f63 ca3895a0d1560e67451efdfd8cafdc838c7f1fbc22a4504323ee47e7436ee49b 3a8185cf5f418483607610c29acc87d2b5b0eb5705a25252dc151d28168f65a5 7ccbafce5134beffb22c2906ab4008a62e784b46bd74ffe5bcb2386a990adcb9 f00cac90c0731f2c2eda3e61a2bbfa99255c22990d697a1008ce4a53d42a8600 3 f5b17bd39061b099b82a924350033c9443f9182e39a71f2f4ba3dd4bdeb72c072b390015382e17d0e4574105939e326609c7b8a29a28e9b746f3741dbe670b0ae80edd570ae48b4edf2d6c49566e901a83d120041ad79dd1a384d2e06be7e307e4d059e7ff9f808e5a95e3a4a622af0bf9bbf2e7409e216b2e5459ce33003a03e870530a46200f90267f0f8ba23de6b341ea93c12956894c34e107924ddec90c5e281278333600a18611b95e09f0ad017dd8c9cd0e9c7db469d0bee9f67e700ed2a18e8638e65336724c3f01e4c9f5950fd403480598a35ec8572249aa00d40f5e5679190aa41870d02bfe37041b8d558d4e48c11f58c214b4b807a3cb70d3052af1425e7d28aa64a0379268bb2d3cba6fed543077bf2edd66cb49a3f0faf90bc06d4196e2f23ae4642960de37fae038ca0aba32a8158a3d2da02b0f6a45540002cb5bb10d3683f030f5b06eebd7a216de197a324ec9cf26ded4647176a87d05fdc5d09bd7842bbfe6b9ee2dc61b57724d650edb8fe93aa92f654132be7e070a347ed37f9ef7fc7a79db05e264133feebe319ae96ca4cedf93396db4a4933b0a65fa7b8b9861d52e3940f53e9abdb3a80d5b130f28f97f3cc7d84044ecc1b50763690a2513b170f6c55e821e867a258f855a497c2c505c408adb3074e136a4054761b807e099783cb64bd1d9110d05f5c184aacb5b32b4e0cf67764aea703c0717ac712db5687d1ebbe5d8efc99f7e243e5a2eeafed6fa7c6cc574b82bb1520249daef60ddc79c20bed3262245b4429c10ddaec8edc5dcc7fd7812c6d26a9b0b1b3582cb2fbe34d37ca8ff312dff814eddcc454793c51c4f2dd64cd9aae4720ab07dd086c7073a38a204211f3519be043f361c2729817945c477703bfa4e2b03 -generate_ring_signature 8603bb45eb1799e1bc1fe6038b19a52bfb9ad7cc9cf44446f164acc3ca40da1d eea6a1e1451c63be7dc890f76883f56b1fe5b41921b4e9a5f7757b92df2b6a49 10 278124100668fd040c4da5aa9dcc1961b3e51c0c1c427757e00938d4584b9a72 1e7124d6beceb0fc37efdfb8e2098b24518f431bf8c0b1d5ebe553402c025b99 73f76c9a7b6cb8d8bc3996ce6fdaa00ef47e3c4c0af2fa6cbc794153d4ee0fc5 59bcb04b3649b9d1c6a697ef8465128d7ffa20ad00d4bc2f3651ea8893e75e17 8a996eeddd61f604a74434deba30fdc66efe9a92ec938471b11f88ded7365510 124691caca477ab72bd7b69ab0e991ed6f6d30e910f34997a8c826e5f9c3e502 e80acfb48cd74ae789a6591ec70de50049fdb9319c8b4cbd814e463113e97c23 4be725c440060881b1dfc58d506b5c16bdfdd539bfcf02ad5dea50748ca0350b 57f42b791c1ae5f5dbf20e303be722ea94fd993241ef302e94da5cb017802ba9 dcb72d6d16b55946296733c9638c60dff04002c51c55234878846bc1b6dd1864 f0dacee794abc5fbf38c017120a19c7e1c65637c65b4103fcb8c148af9aad201 4 10cdb120fcc7e93dd61487df73f67535a01f55236bca95d00bb778fb78c0d205bb748a233cdfe2d461e0bd08d2915a3123d8168b1ddaa46796ba1ea412262e0e8295e8f542c67673dfbb91a1c5d91450631062dc743d5dc637490b076bd9ce09cca2b811e2aaefb8ca7c27bc7a579b2811ffc9f52eefd38d6c62a063234a5d0ca6c65d1afac6be94bbdcd00be2f0a267b80f0d7fa2f6ad413df5ec723a0d4b0657680fdc474ad054c1bc9936ecbd1c8f7f54b0f7a092a69dab108d377ca2b5002afc381491ab0a8b218fd021443c7e416a755c8054f9266b1296e3ded52bbb0d18791b0f60290937e3d652fa6543433b189b5485b4ef63f81fdf4339e9b976096057d5d0ba4398da8c32df9161756f2fcf29aed7d9ef31676ca0af4425016a0929be76f8aa6484d3435db96d0bc4ad624750b0bab16ea52cc746ec630120e7073bc380b531ca7e43d3eded6e3d2785ffd5b8e6429012bf059665eb62ed54040af283afae57da2ae8e4dc861a55a9c07f195cea9cf372494ee4e5a356728734086d7b3b232f7f9a8d51a3450e50077501d05b968cdce9d1c63150bbab9b50a0085ffc7a3125aaf806f1eec01781e9d024e6a5d0e8f427c1aa65114622436b090bcba6e48ac4562d6252a759d609206172dbd11eaadf88491578208b2e4d2cba08415faee3abe9dcee7d31205ed702ce73d15abfa7571915393cb0705519d4b00db319212d7e64e1a40be768a65163cca2f493d28d90b041ac523e73314133f80ec85c8a044d46c44067a3d35f1d57e021007a2b826269384a12b62b054780660ea213bca11ba1ba75c77cd54f5bae76d4ed35fb57d7eaf501acf5107c961b120ae96bb352f926f28ea2747c8ab0750f399b99cc70bd3f27b07789d25e98cdaa09 -generate_ring_signature 393bd6d7e3d18366b43c7f64f94c7f3e803c05a357fd4c9ccab41f235545ee23 ad175b4b3efde3db1a37d5860d51d8c1e71bbdef25a0d88f29fdd6ed07309665 4 beb4f6cb5a63cfd76a7e66a78ed35d788947613b90e7bdc25ada761b87ea430a 0e37fe8e05c17b8ff67e4fb79a6849eae9631a41fccecca28618418746d0a9ea 087f54a5745a7fd38e45534fa0a878b582fd7d16e6705e0bd195d9c11ffe9638 3c91a5cb9c704c6f70c36cf483ff5dfb14b45958ee50b244de2f8c60e3931a82 c022e13c7fad4dc5e547e92919763f8276ba20a839ff4790f7423bfdbf60eb03 2 f45340ef1eab8b105021d39026128329b9213d8a095b3041cc455c8866238309e3eef740df43f6f941a3f814b812ac6837acd8b86354f60654650471566563099240058fce763c2aaf0615e7bb4ebbb9102f20fe387fce55fe55e78f2b526d0c962b9b6153fae64ac50ca0d51ea88deb1256353ecd8b2c56c29e7ee67b3fb905f760fa6c59ffa434895e3e7219d160bc180d892b0eb92e87dc7ff642ce9f0701e4ea9708c6c04414d1ad075f8971e0a1ad235b52229dda06daf8b15b7637080a2f2100b42e0ba73e746f4a9c0739d2dd4ce8dd8170da95d6197a9bba9c6af307e996bd712f886e4ea6ed6fd13b6dab7a8fe97d65f760e90e70dbf4c806d3ff0f -generate_ring_signature e565abb91e365168b01e67f4a3c568d32789fdc1e6dab2f448e9f38d091a3d4e 9408b7dd8b3cad102645c2011cbfb0edf8b012cfde69948fa41745d9b2e0d375 1 353fd42821e9933571099b5e6cf214b0f20f3b6d012e753818641c618d20defa cbbbb2095f92ef10834a02629b43f7cdb9c44d638c6972b9030d793472a63207 0 02f8c04e3643cc44497cf0ba74642b684d6d77c2ffdf0d6a174233633bda560be7a46414ecb3fa9fd18b6564f2d2ab97104fbe007b033e5a9c8124d23188e802 -generate_ring_signature 7ca2998d7e7f0adc4ce35e5b474e47699d5460663aa16ab449609fbb00d500f8 7fc9c544c3e934782785e9f73fe032d96973111f922034ca48bc825cc27736c7 64 483168d096ddf7c014a148e53be2d680079bdf94ddccd86b087f8b33d357b7fd 86bdeea8a6d0fb1d4c7d5a173d5468a55f6787f612cc997039307ad56f2ec6f3 c60a76b900e4670551e578ad687fba402b25416433d72af5291ed69e630337ea caaacc734c97093c58dd64102a4246b2a5840c1c813c4e338394bbac7dbebc75 1ef3e4e217cce535fc694fd8c6d3e113914f0a519038690ce7324d06e4d1f8be 916111041980474203cd2afa9043cf5d9c3d636fec3498e1f6b06115b668bd1f a09a5afac52945be31fc7a2e7046862c8d4806649359fbf8b5f7c69628b4f167 6b5d1fdc1a962a295746d49938647583ea088de7f8e241497fc6b41bc9121e95 0400eb6d12f71290e0bf2e6af6ea14c37198a7e686b63e0569f6741844f0db28 235285f1e1e1a1bb6daf5820e3f9db2f5ebcf8a3c1dff2a988953d60d77c2c90 847ebd598b288b0052a82a35c58fee0e477556cad2bbf15dc60208ed14712b08 a8a54162f2a6e4fd177b3c727dcd2034643d6a43970f2bb9804cbb5f24366b5c 37fcdd2a3b035e5f5f4951d3ea120dabbdc219d9a6c385d03643a7037eb26af4 c3c68afaa4e7cc9c7cda7569cd3b0e880fc2b39e418383c4f7d6aede362acaee a14729c01c6f8c78c68f4863e384162a863d304e780cf1d87835369d6c0f4c42 ba8de594a2ebd411901147db78dce4c6b5dd35dd2c85ae6ce2e2f48f3e47c122 f9d0351c1c41aedcc6899d3c963c410b3e33510f747c1a3cce8ab1823a3414ac d8f4ac38ce7be2c45d3b74880338bba84e256042ddcdf30ff25089726edbcee9 4b33b840fe9c767bc2fc9b8f989aed3d8012edc993776b1d2476f6542ae15cdf a7b39b9014bde6702c0c7baf60a3245a70f8db5abac0fad30bf98c36d8b201a6 3905a5aa52ca2d61f49e770c53c6663687c2b6565736b51527b8332730d283b5 e9932651c0ae63cf3738fe067908dd46644f1d2235972e027beec99724d013f0 804f17dc20b27060756c20a2e3b1bcbc8fa01db1672c7aed51c20a868b0360bc 138c0ac27cb68eac5dd51b8f75d1c31a215f26b14445bcb3642fd00a176a6639 0f283696b934fbcf7ce30f78f8664d8f28c986a202a0c340b2d7d5782cc7bcee 36415f082045b4c3b1b0f805d2e6ade990723d39db0a53cdd723d10a255d41d6 16a9cec53d35cc2861126070e57d228c20f3350b93ea4b46d8b62d01f3209f20 a5c9112fefba9ad954476e87f670af6dd626898b3953a31cf3ffb57886f10d11 ca10bbb819ce71f840e69d53750dd43adf4cdc2c785e517c7edde54034a3baba b1f8df73868c6f39af5d6a71b16fae7c882cfa335115f62f0b093873c308d06d 557f9033e27f5ffa3268c4a3f3fc94dc73c2e3bc70908888c3644143503796b3 f2cad5e736f9839d99cf515859fb8464fece1f85450d5bd5558ce750e60e2942 9becaea439d210fd173a374e51586d125508c458ffb0eea0257416ae62813a81 a56164a43e06f4da3d2cfec1db2c1fbc2641b9f86dc4869e06e55f0a9ffcf751 c8a1abd87b2837247515c9736804424e235d61d6c41f90e748e72859446633e6 210074f11031e5ce0d2ac8cf51e9fc7981155fd1011745ef1e74b342672b453b 8fae668e0c5e30c77823534bb06c660867896f016907fa8b05f5a18452c7714f 0e1a32d6619800d7c2fd5873d124c917752f058318d43b66b2512aa1667c5151 6803d84d7b5a3574328f3403b2f4a79b5262aa31e6f1c971c179c4ec2c8560ab 44c61ae79c631193c330a66d64dab906514cbfec06c8bdf57d5cdb1844b2f789 4000fa60a283326fdf215ceffc849f6f58cd74b60bf19798cd6e54b6e3d8d06a a78e6293da0c64e26b788e55a0afbd8e4273a226a514cab6e3b4beb8d214c9ea 12980cd87170fc759380afa60290915389ff1929ab5de05f1e8549f014fb2b44 a1b83d3f280f162cc37feafd51e90cf6e1da6c82d25ca9ae12b4acbee46a949f 6cded174a9447758824a50352936a539d97c0977ecde4eb9df83c0244a44b89c e6a9d7f60b9d6f367befcfb0578605aefffe810cd55081232176f23cd4901ba1 13fa490eadd0b74699af3be95e1f5b2e488826a104779877768d5e10a506bdd5 5ee53f2482db49cd3373a9195fe2067e018a081189e9e0ba2500357fee9f11d7 0cec68b56dbdd3f61c5b5f242928af02ac0f1bdffc5461b5855fe58b582f54cc 6c17365304dfcf711494cfeeed75cf5e121ee6b59cd67219edaae7ea07db4250 6e801ef00a31b59ad896ab91c68c67ec24aac24b03436c7013c18df945666875 8326bacb4818132db5ca61d73897c1ec0769d9833b9ab677c42bfb9b177e0f09 2b460dbd272c99ec3ee39938db0555df0f23e8090da8e115a912e15d51aa913e c3cb48550117623033617d274569087b44d1da2dbc0257bbf45f35e3581b536d 110d6abeaf7b20668c3891fc61240bb2c3d67736f5e80361217156a55e142f5b 79e8696c0f50252f81b5f218eac91894ea15a95a48ca68594de8aafcae37d7e1 7308404a054e1005bdf9eddda7a5d0ac0b395efdeb81a655f4b8e8819bdc583a 38239bcf14fe4fdcb14d6943c464ca8cf401cdc46d3d212b4f1ee52161e3b4be 3a8bf4e796f2cd92a78240df064c3520f4966257b89e49ffa2ac6b4f70322a97 b4310494a59ac8bc4ccf06b17f70ddb789b387215fe2b73d180b3dc1779350ed 51275e0ac22a92d506d12e7969e28f060ee7fcdcaf100134af046d622ef7c8f9 d509aa1c71eaf0259e74c41d404c25ed03fbf75f249214b7525570eee1eefda7 388f6ce354823892c1cbe49d7fd79d8c62627308a48291af34e16bca292a6ac9 7831cb8fc935ac1eea198f48f6632475f8feebf686406eb00d43f4054299f8f3 d62eb32ea87d2f0efc686cf9d79d111ee668a65a48c3ae2606bee28d8dc54202 15 6476fc6ae0454ae8d0c7a3a3d5c85ea85aa66658487a3d9748806d839f6f36087769554d5ff18bebc2f22da9a3da615217d29ce2fd4e9436037b62b6829e5002a23d1b512105a633471ddba9e8f437a23bf731e548a7611cae69453b9c85fa03ffb7d55749be9cb54ef51cde8fcf3c3d488be5cdd055441d3743ea621d6d160ae0880985cf17062f758e835ca66bf5023b2285aea7aa215e269efe082c55a00f082bf1a886218442320fc489da2fee771780a36fd64fa49cb950c7883be9f6056e598e1320f3e8ebe6ee4c2d16e904cf8129217aaeaa6dcc7f2696ebce196c0b6902c3d5e32e4321d2c5436667ef6dd59ee65f66abfaf93585aae8d8dbd3d30f425e120e1925b6f47450453a5c4b86264408ddf91a96660585e7655137bee40f1c0d3dea775b03f4e5168f33eeadc29c7a4f01895ca7c23662ea3c024a198a09b7eef424102d0c1c8cbb30eae35dbf5f740b87d0d776caecb434eca8e498c40db96ed96f6870c46128787083142ba1cca016afc58548e1e6a870080e807064000ef904464b5e56c5df1b1336548226053cbace1bedb6a6f246fecb725bf728047ff6a1ab92ef3e2cdcfa16f7284d3ddd814af60e0c9363b41b4aff6fb928c6027ff3894536729fc8b814d23f749dd91dce2a45f58847f9073287f4afdbebfd0ea545a55f20465263946634bd1cae97a87c443ab454c7ef7623848e65cffed407d34f9ab2ffb89cd0ca058f4f06251ee1977134f84f68dd453b03eb731bb8f1074f38320470a8f0c14ccf787cd0b18a940ef2c044246626cca6d96c7bc5d09b05e941aea0c810b4c5d188efae89ad283addff6f904bbe3bbfdbcade297439ae04baa5347358c2aeb2456f42e513cb5aa558dfc409855cb05c8ec6e57287eac305ab5169454a3b1651da4e674bf39eb932e8ce488f15ee7f4f2dc0572b996c0d08e5a80aa5ef77897bd4e172df1f339f0007d532621d8ca9386f47480614a2d9033994ae0e99de75133d8e1774c75b6988021e710e95703c77e3b89b92b5f8fd0f19eeef271d69f40ed8e71332afdfbb6f8682c3f5829d2409bab2739fbb73d7074da18d2de8d5e5aff45d13947a89a544c02eb7d6bc23c02cf6f019135abf2a03d0e75e32db27d240580e44aaca4b37b2d42d078ccdf3b1cffeed62544c3bd50ca4c26491fbeede270aead45bfae4314c3494f25bf0a0788b9d0bd3a74473500cca1d641efeb0045761d8d051067be45ebf766994ac20b98f70093165d9e87703500be876e3bad54c32b0dc84aabc27dd630b47f75087c50e0f27ccc81e319305aa75427925d5bc4ce7a7a9984d472146951cb611cb2fa67ecb79787de77ba20b51a6550cdfac71fb45cd366e2de9e530c6f8b2256a2c1d73c889a2a86d0b9a0407b30c96036a2ab1a0bb179a1d48d2bae9b290c953a47af4752528a089fa450c4a8026213473f5feebde04219d28bceda27dd43184e7aa81779a173aec749e084dc11c7a6e263b2cc1f97c19f5c37730ab750e5e86e170aaa56e3d642fadc6083b9923cc8c713e80a9a35686b2ec269f9ae2e93b0f7a0c331dd26b729ca32d0fda4dc395573e7d4036226988a7f7bfe1ef4c3aea30fdd4531c6d982c7a4de204f40ad4efd40de026458629909c4ce8036215c486ec9ebbd0719e100943ebd503c2aa0ac774342efc18968feec47ec624d4172d3b6bf2d8be2320f32ba803a40b65a5e5eab2c3228c4182a8004ba66b8a517edb5eb28234e94a88e47d8f607205f674689460d0e3e4d4bb30b6f5e2d3f46ad92195246d8ee77acd0cdeb8c46704cb89878fd98f6d353302a5392c3b67020c2bf974d03dcf3535d3523b3d7ede0f38adda013f9d1a5a7ce7b85935a686aaf07dedf30716b39c4f07d5dc1aa000003d8341a6a2a92028e1ec0a19a873c6bbac34551761d223a8133e5d547429af09e2ada491faa23615cf60a2dd7649ab38f1c71d1c638c57f7ca4f6fae2e9cdd0b3b8e8e25a8c4452dad5a43278934df74705307ecf1baf2de9c3519faf6436c0870232176e0db682fbbb0465e863b0e7dbd3c0227cf90dc4604f149fbb860d3076e782772b258d394a2d7881c3a1bd7c1eb0b50ad334a8e6aa5e756294bbcc40005b035056e80a261b7389d42bd41994aa80bceb3c06eb5db2652ed88f6daa4098ea9af978f527b79f7beb2c900620fe31f25d6a820f47e1cc81ff891c7003c02077c3e0cdaeaa2e64b73617a3a7a215e31bdff8a8c8933dee2e1a93513560208a70f11c9253a70996dbc4b2b9ab43698e6b0706de4d9784e1c218e6d9e8a6809438d847667c8e9a6f4145ba3f3e869cef56bb91c8121bbb7421f85ecacc1b50d8f122ef254cb08e8dfd5eefaa7732bcdc15b6a57de32825537ce5f66f66791086de761480a2d4d97b6161e681cb90ca60476646f01ea04f6bad4e2f01fe5420c7fad7e36138affe83db129547e008a15781a1620bd6b065db09f348a8e46890b7726cd0f286a6da2cbcb79c01cbef12828e2070e22533e31208ea1903b58bf015febe73699cf2c2494b1741462eb7273d72472861e4536c68e37933e66276e04bf346751f39ddc768b2e8a2bcf29b1273c5b59d78f592b13990340c1db39a208e5f35e933c9551c6cf6f663aae7954b9a8a8d4f2beb00c98e945add40d2be20482d9b57c549c86a64a0facbb28617e843bc98bb105a4b7cfae0f6ec1f25de10f004096ccb9ecca710e6930ad39cd0c9a3be51fa5144766937e91364b2804270ba44d0899a2b443d8216e72ed2c41e494efae0a9442a3121c8bd900bc14a1e105a24b03e52aa08a9543c32983ba44254222c95e28da1475e01888460afe547208592f2bd193d156c7e67c9c9ff0d8bc62b0d7176e196067aa8cbda1764855ee0b32076284b9a29d348ed0a859d994399eb7a4771fbb1b9893d30fe55646a2b80c12cbeeecb6032fa56bd3687abf1baa63ee503bcc39f88d24288a8bd1e6172b0c04994b9e1903877924c0912b7a226566a6383d6d4f02ff1812cc60f7341be80088b2d168b43a9fb3a058bce87484fdf3fdb72fc6482e66a4c2f918316ddf14019def15fb5ecf5fc448fd5d8c1992642c7e89081310862b2d0f2b926ed7396b027ad6e2c0daa5294f0f51df24738cc16b40996c0d485206feb4b735f34811600f54fc20a77f6007ae14771ec04eec4a9dd43522e7bdb8e8a77c2f6c1d5bf27e0d690ff461a42b4b5111b4255af56e452bfa5087e608150faee1870f81275b9a0875ec7c19dcd71f1469adc8e4f00d7abecda2264ea7655324b270f6a984d4e906bba39c0ee4613dca752009ea4e81a1feabeca6ea44dc90ee7545536871e06801faecdf619938b1f3af220570510dd7f45bb76151a7ae10ec7455c47b7671720fb0aae18f291cab73f47f485dc29a4672e6aeff36b4a21b6c66d0841359320d0da36848d9ed53040e873ca032e363ced08157c215a709463c8938668356528705e5b046510d8ee066cfbbdb4c39fb7200a699ef3680d9051c428384cb9f628c02aac2972fdd5632ec4aa52f0bba7a3ff4177a5f5eab237da9f118aa8601d04603df4ca7ed01a54ea55e902ef980385f103e335fb5a862cea9fe5eca2d2902180a77d9c0d0d6c1a66dc55552cdb9cddeec06ad671810e2db5a556f539483297005c4e1d4aa8f8a3ae1dd218f249c970562e4167349fecfdd46b45e2326740a7208298000b1556eda1437106ba9787a8bdeeba410d3f4666d00151996f0c6555e0e9e77935300195664db2b1f01d777006d7c998dcc5fa40507c5acd63abf9e4905e6a24ed8c9858669874736b1f5e3e91394811b78207bf8978f293d4a7d879a050817889103ac07eb958ede6b6f8a4ce076cf91db54fd97373f62ae91c076dc00dd48e737b2049e034d399efa81fc931ad6bdd338e085108f2d665a7b501e020d6390ed63100084a3d5516916fbbeadb11e0f6c4af23259a6e2417cbee0528c0d5f06281a9691ee34c2842c719fd6da389ee661b20c000d15ce86692e25e9560deda7f0311f29b796e8a53fb5d5dcd5cc0015ecd763d606bcfaab9d338690790511e20fcd96c2a8b7bb60c3d77c917edaf43793161c3f5ad84eddd2baf6e5f4099cfe6d6765726477c6c4e8237212e8ba277382b091ab5d2419a052679c235c01042290e042b798f86064c373f582cd4096bbe1d1ab278c9af3aaa213d10e0508ff437dfdfdc921e961729470e4a0558289e84b1760321abace87b7ca5bb0e708e3aaaea67a32f090bb738d7610e7a3f5526c3ce6e9dab7b9d8e8481fed2237017c392f04cbdf41028142b634d7f8f0ce50cfc6e0c3afb935c9a101cf28db080d67363fa97402a7810dc443c5461dc7a6fb4c22ddf555c6f06ccfd8cfde5c890279af90369b80d9d0224f8de6f18524847ed568752d5cf436da1cdce1437ad400c6a590c9352460942c2e61098e43eb1878f0829da2b3b564ebc525688eadc8090561d4ea54841ad1176c947701ee77ed8cc90332b4ffad76d997a0a8f3bd7b0884c3a1538c169f1b468958efc23ac0851c52c123516b46014f9352168d8a730468fbff52cad9e8680f2778a9fda93321687fefc61a82b6218c899af4ec73880cbf901fc47d8b1d4f509c549dce758d244e1d07803e54b3dfc44795042a59fe09678b13233892430122204ad66c5d2c24dae37d9a40e64902fceb41ff2ff5670abb69cf52517aa8df59555755a03106444708daa8db26af1f3808e93562fa360638b8561626d5c5aea7ab3ada0980e0ecd62f0d4ec0cc386bb7a517d29a59970e95876eeedbd96eb046af8a0f4fb2a10493dac52b3daedd261003cd1b2fe2800794faca3c3626b32cb8a08285811f1fea0dc1c272de763223b35c712f37f467062a7e976946c3812681a56b6412cbd75d18df7e79744aa292bcfa5117bad88101b7da5c86ae427debc56e7f506f89ce2338a3adfbca1b8b4b3f34bd90247c0f08e2990518872272ad550aaac685ca20dc98d4486125bbf9433a12d26c8b0cd10b564ee52df1dc71863953c452bb0eaf169a32d9ad61cc1e436806bc4cd18a450b82e2772953604cbeda4485dfdc82e5fa5f7fdcd3c4172b46ded663cf6734d40f369dfdb46fcd7a43d6198ad876e5687e45e2beaa9a7c39714bec6733f9f56809ab560f2856ed0c384e756dc8856fed5c7068539cb18086da28bc3d75897aad0f025e014972a1d816ebd8dc1ad271034471c44e0acc543b1145fcfdb82129db0390891e1229ec5d02eb4c342fb3860d168b549c2bdaace69ba20f5511aa4cd30bb60f2bc14c5894bc945fe7fc967f96082344044a68c0a8af97f63665a008740c7c6f6583932d3c5fa1aa0969e42741eb7e3efc2e8439ca4fadaf601c1326bc078b02f2b6a21535c3aa640d793c1ca0d647d05635dd85b0f6023e58148da9010101065181a271e01b9efbc65f9c201d4725dd5446a891deffb99461957b9e4f042ad9122292acfcfa390a6624c55f8bd37ac8d438188637fa73fc7494ea1d0202ae41b372f8ace5367a4afe5516ef5a8f363ed70be4308411f73364cac845c700c858954605c068618d9367eae45266c04b568377c893efc71e6c8c0bc6167b07df9e2cc01a9fe6df328e02a4bec5d8cbdf4b9f996b6695949941911a8c83b5089a6785989489f94c0c42d4b8c6c8ec8d1e334988c4139c976aa6fe849488f300a60a1c5e910a68768f30d564b932c37ac27b64122b6763e30ac2b1c072055b057a7a1a99cf51361f667970dfc832fc22407aa042194063a5a7fed5eb97a2b00e -generate_ring_signature 4e6823185a8771ea63ca39e1aa4dbb7578a6fba3da566cea485c82b3c0efc115 0f0ffd85c0de4993b1c69e5b0c21a3811b49f6c0f4097a81e33448c1149afaef 2 cad1c5a19a24b36c12ba2812652309ba4234f04ca20812ef2d76405d7740afe2 4ff3063f234d5d7b4717b2c2b9e4c09966f69f1c1083099420e37baf84d8c9ba 071ae9ca27b1072d58abfc39b4bf143d61845202748c25189525172e1d081b0e 0 25edaec9d801496716a0cd9aeb1e5757d5718f8c76108d108a8a1a9f8e51070f640e50129530644f64e09631c6a7082fab5e8a551ce32b5806e74091322c2f0c27c93cd9b8c53b561c58007173393a8b859bc0e4ac2ba110488c6501adf1750802f13377e9f2059c623393c2ab2ad3c8f500e87e97dd35b8b480d9aa7f839305 -generate_ring_signature 91cbc9e360956a6ea91cb542dc70d6cec885b8e2a1b763a3e352389041be1aa4 077849a2880ed750d786a1f1f0a17cc01c256f896373c465779e5154b3f7d955 113 1f4bdae2817d67f27dc0ceaf976717df975fb8353daf7fa01fff17ae37770146 fdd656b891d9e758069f81b2b2daf433fc84b3dc8eeefd2d295b7f5ab9d75c92 0859b4fd8c367923f1061c416b7e8e9ebb99e3829e50d8d21fa659ca4078330f 7e2da8ad81daf87ae03a1f42ccad8d8f3c8e1d4f75dd89416119b1c71e8a2416 c32fdedfa2464148a62c7630da8f598ff39f13628b221142f7f2d5b61fd5efaf 76737534abb4dfdc6ba72c40f3b406f9cec3a51fba44b56284fc62db05394e79 e4e6d4b71a8e963a256a300e4ecd265dadfc6ffb1fb7c46a68b9badf18e3e4d0 b4ce84dd04dc6ccc4b85eee0925825b4436237ec68b324f66f5f9684de4f66ba b1c379e91b9b72abdf75c4d25174bf029926cfb3498beb075586c9ae3cc105e7 2267492b2a3131012bdf68b84d681f03824aafbba96b2b9735343abbd74b8cb6 00ae5b1f0c470c254facb1b12ba8ac6a31191cdd01709d2ee6b37c1caee74cd5 bd859849cad2bb97e2da3908dd8b90578455599705674fc0016ff46c3549fea7 bb21060aae8d8503dd930512d7a080a6828223b5fcabda0c161ec3ecaae3fefc 66bb5f6b1c081e0393d2224ea522cf385115dc03b1970c2e7e3a5c72d8e7bc50 33cc10d158229e8603dc7895a81a8b6c55edecf6f43a506625074261d402fdc9 43a36814c2c72f6a273fac7094f9b00124e0bbd332b9ee61ca41ea231b018816 1d7f55694bfd332d08164d331fd276e56d365f2a7c5fcd3824c77a6809b65385 c2eefbf1779a3dc88c493b6512ac8d2393b2a65f195681baa41bb05e2e58f689 981ed644db662419628670953465ebe2ac3997ebb4d8292b24d2ddff64fdcd86 123dd138e0f62fad60aa9656d484aec34ad87fa623b69ca4f77f36b5b903d71d 29976e6ae7245518b0565907725a15e1445bd1c0730b2c98433c47e13f34e4ad 541da01c43ea8445433cf70817e7fccefa13245c1d6ff34ada5324ed8dad3d04 82a982aae68c079dedcd3455d266bcef67e4d39028afa0df883dfdb4b36ce979 7561dd51d46d617281a314a4e112228a6abc1ec8ae26f59f5d691cccbd96663e afa6f7640f981e3d4ed2b85b74151061aed19ff5390c0ab3791cccc1a21ede59 54c84b57a8749841027cdb4555f8a8c03dd9763957d801efe96096ce6a58a1ee d4186b3f8ab4c4fb9256161f632c9fbae63912909c22636357b001ff34486de3 e845c35b7b319a99099f122f0acea3e39e82bf536c9dc0a7fe5875d6b0739c74 faf9769154330257db19465d7ae0f2f211f730e4c83220c48d7648596e91aba5 3bd9d7b85fc7ae7d7bece942d0ead33b882d0c35d96d491eaebcd6fce5f345da 1cdbd85712849004e3e4069802d7387ef46a4816ffb84fc626d5c1b9b7a60a23 d284ec26e2df785de3542990946152a736f729a787913dafe58f8745b0d263db d23bd5efd82b47c8d982ed9e1b4dedd39a3ee3badfee703dcc33d3c32b2075d0 f1eb248b21f1a5abe42327d1f203121a0a113fb1cfd61281e639aca1a15798a7 dc124febbf2b8b75ad19408ba905ec2660f8641608048d4053b137c18118285f ff0a4193b301b36562d8c1ffab7bbb9c62834b0116a48b992a85c1ebea6b0a81 8ebd75cea0d1f657d506b3cbdb855a9db21f87d8e0cbef42849383f093b7eaa0 9e254d83820a33fd0971a3c7efe2ca64c35a3d5d50ac3884bbdf7d6f77d7dd5f d2a69bb8e79306446c6b2cda1ad9c3d1bb571720b43e55491abb505b6aed4f6d b60e0fb3e1013aa0c1d196cb9d2fce8c1f201c0a013e6634fde756f6023c36a7 262e4efc7730fa981014d1567de9d429eda860bfee22ede1c9941ef94aaf2159 8a40d305e04999c66638cfb169b2a991c0995c814ad978eb9f1e951bb3465c40 088739da61d37ad97d467ec8bd7ca4139b311f7f8b6cea6469aa4e8a4972ba51 6874ee85e75171ed81a59ee5030a8f5af72373719a8faec2680c00af75fbe0ee 0ec37865399810603eeb33048982fd83b25e9e1fcbeaa91c0ed06fccc47c3f80 5ea898f527a48bf7a4fddf87f26d13637557b6214da4eb1b0e4e0495111f9f36 b1dae8a393bf959ccf33a86ce48214f4250458661892fd8bcbcbed768f792548 25a26575e7b663bc898396ed9ccb961e9bcb00d107f0662edba72d2697a78d4d 927ac8a2052c2635eabe2602f67d1e024ff0f04dda5a09a1abe3475cff9b4f78 ef6c5437357e0b31c6a3ddc3474ce246f49a669ce06acc00232a4cb170e0b7ac fa6ec2d86e6f12a008a2415f6bb6d62d8f4530b7ee264e6335421b464033b6f1 77027c9a7b41d51ec9851dd80e52221d541330c4aa65ae9604bb924874dce56b 6229ab07ca58513e9a5fc1db57e4c1bef34e4007206e440b26707594b3cf88c7 137f3bea2550d2307d8a733cd7ccc1c6f80fd9b4f307b75583193c826805588d 48f0e380c36fa89f755054572914efcc6ccb415d77348416a33d5512f8376380 4f5e4429027959682771f088b7101e1cb07d0183052627e44191ab65a4d11e98 d40ec8d941cc48b7edf706b778d736183e05856d03fd929adac4eebf3268cb68 8bfda414414ad854211292ba745ee96f200c80e3623506fb8525f20fb2271fc3 1a615b1e5534445656037b118b8b7bd13ae02ae23436b4ee338d92d37c3d4896 9bc163d094d4f87154cf3593c1edb359a903dfe7509524d9335623a29c656663 eac712841f6694776756f6a89d043bb062861a4378cfb13af76d301c71cbc6ed 7ef33bfc75e86390bc080acbe35bbbad5ddc51da165009bd0daa10db962ba996 054e6574ef55c9f9e94207ec107352a0dccc240ef20c033dd6d1182f7f62909b c32880fa3ce928700ac24530ead3571072e12b03a49b26ef6b3d35d0f4c59d53 b98d0748298b80029c901eb4ec4cd8a436e83c7e01f440c786fd16e15107ac49 ebb0e6eacbc9c5f35484faf27ecacac6cc694ef4b09d807029ae6f510efdcda0 b490c1cb481c3ae08a73f63a1102b316d286d93852adf5648cb0dcb6818126d0 d4f4da3f84eec9c39a4c7cfe27a3bcd39cb9f291005614f1487e0f45a4f1ec00 aebf45b322432696c7b67d3cfc0aee50ff8e40c51ce8cae8a626812449d3a54d f606c7fa4e43f558fdcd80fe7a73dcbb249056b368f5a44f3dbcb9324b503c34 106f85af18b3197ad3351fa8fd8425d260e7aa0c05918af757bbd3361bfd69d8 32256ba58423e1ea10dd4f501a5d238f31499624872406fe500f498e66d008b4 d292d6bd8bf7432aaf9c61d7dc96c73f0dae8132673e85fd3aac5fac1a93431e 52b24f1118795f20e46b90c1b047ee5ee2d3652ab2bce842a9cec609fa62aefa 449fe393def04380cea7cc4c3db08cebf0482f2ff57127ffcd987d92c3fa6163 7a8d3c4d6eb1516f2fe284e496574b3f189b5015a489fc50e54a9f414dcc1b0b b39077b6c733c1de5db7ff8fb3873f024b40fca72f9cac1fc08af28b18e1b932 0b203cae9dada2034024effca44f99b52a36ed00a2f4b67f03a4e0ef5b7c5dcb bb56bf108fb574114fc98c31e713ddc445cbbeab6b01d4e568dff3bd169fbe2a 059819ee8457141574c6dda4edeecda5e62993b800bb2148b16529bf6c0a7d13 79688125c0e6b72e56ea7872416735714947f586635bea64120945fa37cc59e4 5adb1c6cc2ad2431222dd58e8a6f3571063b3ef20bb123e3a1071fcc60ecc9b1 cc3dc8f1ffec0e5f45293b94a5a7bf3ae58a4d4f572a9f5ba8ec3b4f34138369 3999f2af3d71f38b14e98dc84e2530f037e83e7394bced0b6d681ac73029e563 67652807e4ed16e014757a70974b4f8db9e6f6bfae39fd0614ad1f06e525c081 d3813e43348dd6866a69fe1755162f250e8e3de00b7ff0060cf6475d2445836d 438dc90ded371c003c1038b170b925cecb796c3074ccbebcb98e3168af4d39c9 740cdd1999f72c2a29922d6ea744085a4d9f104636ea931643943ffe673fb15e 585fbe2386bebe0fa46eca5ac524a3cf4665498c01929b98854634b0a0d113c3 de3706dcad8c44c755bd56ec422c99168fb5f8683866a13d7cf8c2145a49ba38 da4157d8f82d2a54744daed564c987c996120211b30d482fc0bed011e8f94fd9 721a665b9b6194d077108c0797031347aa6b2d6183812e890ead8f7ede04fa99 67cd0f8158601c13c2f85d32fca57f324985a20c4766cca265693c77d87d5143 9e8803213f74d6e5869fb505e8309245889a807a48327769b8476f16992a8198 eb535a0a39ee12899ad81b8327bb17194757e760e134a0bd34a38ca32b7fc9cd 1a94815e92a49195b30d5c6d08c6011f615a876d9f84562d13e20c8796bbf944 ac695022107503164727535604598ba50cfe2c8d2555345948521d94851caf8c 4819c71ab8e12d613df31c3e4302178dafdc21ae33baf2ee3a9f2b161aadd640 c5b8a211af4dc95ca5ddb7554270215cbdec87c34dde54c484e1bf8ed7a5500e 375f63727c1dedae8a51f8424156f97f9ee3b54301423c8be049492638476587 cbe6d761149db8e9231283d7d3e27b6bb107068127e49ab0ea318ae47a52d941 d78379ac13a4e4e05a8ff9cf451d22fcc16d17bf4b06f66c55f8da8584b08fab 6edfaf19c5dc75df69facf72eb82bc2c016b2650f32c4afb731f7f3fdcdadaff 44d3fe29429f69fec91ae3e2e33b227fdc00a8ab78e3510b2bf03322f20683a1 a859389fd668d9b8a8a5fcbd3ad6c49c751032078462fdf6aac4e5600b8bd37e 6808fb004f96d638abd7d164167fff230265214723a7b65344011667814ef5c1 6c3654047c65a6312b5f785a9246e064f1d223913b7e11db00bba27b8d6e728f f1383ffe364151b77625632904f34bda76a33df6561c30d591c30cc492b0fc41 6cabd4505bd1a98ff5f7c1c75d7d4e328d691510f4a23ab74098c3b54a08ac03 c4818a93d8e531cd4534694bf915b7bd5d65baf66976fea183e3df1ba69b65ed 4ed680ad71299f0ee843625af25736e020ed7bcf82ee266c0e34537b1cbbecdb c8a92d1bf5112f1781f2a7a528b196a3bf4ad426a48c30174be6ded892be8be5 6ad5557af5108ef9b99735168c733031ca356c49e9a4cd26bc6f3a332d9ec219 5c0ecde6ffe8646985d4552fc134f6d013b984474f29e62d6626dc941c23cb0a 31 b38d6310a252bfa39e3bddf4bf33fde99365ac6d21ae273cb494c23b0ee4160dccf65639505602d3f3208e9161eff865a03fb2abd685c5cd2606d4a05e8aea0aaf62da0e1b9a9971825742d3d7edf82b1161145e14dfbb961962f339c83ae30e1b2aacb4bfe359b012aeda4910c243fc39c2e18f319a6e48d9a935860c3b500f8f1c709fd61dd45314fd69a19aad8f09fdbdbd10d97a119120e2df9ccc7146002978d254b6e8d1e77828e6f7da9e34f8805138a089eb4072932d1a72b735cb07c4221c1735a7977069779812721f7b86d3a2dd1f1a19ae2e25652aec0ccf960ab5a47ffa60be7f774366ebae1ccb2597361886f7ffd809a99e3d384ddc6a0301fcad0c23d185f79597b9bad4920f92c75d40fe12b98350c1da38149d293f2f0f3ef38e08523529d899f19b45a31deafdae5a022967d80dd7c0023e3ad7c01908d529cde962c12f6c289ccd6e559e73dce826806b082aee4029fb1a1866df940e2c4cf69c06231a6ceb501af9f4a805a8efe7a22a8ad8e52686873000d863bd08f2b1bb10798ffdbb1f62bb96a1a1f5bde61c1f3d03cefc582df802490054a90a2d9c9e1a0c8ed31c686d78df81d6989785ef1435f15fbaa14b853a0220175c06239068ff45ff77fa5d1e7f8a489273b45947ea2fea80985fb2500df8219d500a67123c706e4e3590b2660022a7a503ff19754ec8e6a6b13b412f92759521eb04e4d5b98c6bb71a5df64b22e90cc41e80abe8cdf66b6118c4b16dc79b3faab00b93a217528587ac6944a743276528a934344daceafbcba4d1c3cf8d711420ea0ec91583566d28fc00d1892a705eb5bf2ca87e9575e5899f35ade02fcc8bf31f02290221f41776b56e58c07b7df50c8824a880cb47b3666d2f9c49883626202006a88fd8f5f1e6c41537a0014a1f1628b6dc38514a76ae4cbb4b45db4b1eff3d08933fde0dc9b0e53a61b1ecb52ede8be51f3cfbbb0b820b405c3ffc5f591f2505c8fbfe492a9bf3be8ae8644485b81d164884f21269b0f7bd4a4602ec5d03ec0adc1c8c069cdc9e1d32bece18cb467669a981e05d0eff8008d660d55acff87b07d70c2092db25272129bb7079b768887bf812844e2e8763c092ea56c556e19d028ac609b2262d99ad8f053e64d31889e90f0f9bb6100473db53be47f4553fd6067e4d1bc03887c0e19ce0b1817bc2173460ca45fb4eb90ab7e5233bf67b4810056d04f3c495907256d07013a7bdb584c82d74bb8eed61c93a540e4a8d68b160053f4ac6cfda99d929dedf89275276af4f2c4a04d2b00858f4b126ec0c52540f0348285c02c481f5affffa8db5ac8271451320a605cab52f569068918584553e04babc87d23915c5b72c54544be36902fba8440e6c6f4fdd684690c440055b0f010f12101064d44f4c00cfc0a3a873dc73ca2e1762095e4e19c15bb70b7f52f6085520c4ec263ccde79fa86427b5202cb6f864e1f3bf8626a8c6097359884c400d56aa65e8146163447e870021b7c190ee051b553dfd5caeb09ec462824033880c5820f497b34b27ece4346be05545fcb748be2e9b0ca5b015b86e7e4abbfdd90194c6bcb3ac5c12fd6fc3dfd4f63d6797338e4f92a4498ad5a83c59d1c1e872005f6d129db6f13e6ffb8de42df965900980fb49605952b8991e826317ab84bd011aee22fca75947d93194b572c30b9ae72b6a51f54cfbcb39aa3500bf87dc03040d2bbe7299ffbcf32aace99c9ccf5e485fa006d79b2b84eca4819e29ec9c230da435db3e909d1d447afb4c47499049d24c935af799c042e1a21036056c1d9c0c17570b9bf823fa9707f4eb98ea6b231e314c338f588e2b6b3073f7d00b79f50cb038588b78edd2a1ce7d3cac71307903c3e1e68011f3830c228cf22000eb4d05fce67c7e2e8fe379686e9a6650b5bcdbc65c4e35a11133f3078aa03bde5cd507cc93e7662aa2d0edb6a18aa57d067869680fb5b83dd48ce2a2c31490ae45e20d01db17290fbd0e3b6645e812b5931870619e27cda3ec57cec0edd6688df6e304a66f0e388d7f5d14027d92a8080a763bb0b4865a2fda1db197b504395a7b2704e93f67a50fc5b46e9817055505100c764a50ab87081ddd677d4387912487930a3129f50065a55bf148396e63bcabad494b319f1ba4d95fbd818a48e12d8d6a016eded68aa7fdec1cf1b9ec7046943f1ccd12fd280f4b2fdd866dc6317c8bac0f43c46c831ebd5167499fb30bc750cc26bc2e91677fad3201fb80c53762eeca08acb155b6bc41cc8b9e80f2d702b38dcc7655d1977a36056da241fc46c4e39800493e89d8a30c814d7961f3aa70db29ffd51de5c1bb81916a23ac70d7f7d2ff02873e9646eda16ba248d44c2b8b3c2fa6ec97735b14526bad9248b25066933605b961bad88d9e6cfea2b0e0665301e8aba2a2eaec2a5fa6a26ca19f22f1c99801aea30eb711e85173848227aadbe8aae7f2eb8f0fae5dda34c5f9cf52c1455d0a29e72371e22691a49c839e0bdcc9f39f1409fb639f9d8526d15068bf5166f30e535ed41be834c4275a68a75ade3e0c3330f0ca67347f7a82566ca13f62af45084bc7c8f08a27c9e85a7f9347a8a1951f55236666fb70fe0b0a2d4644f0b3d30e9536821bbb6e1bd1828f0ceca4086d06b8c6c78e6006d5a280af038090680b0a213e82f2ea6e9ed54ec39a14aee50499011cbc9c5f17096c86e874219bcda40b106582769ca6da0ccb7c279d015efbb75dfc844abfd0d9262cad14ee14fb4409bcaf2f4522c382650b7cbd7f50c181599101f6f4ef7e4cfadef4a7b6fa581e07d5d7b3c549acce0ac51b5e997b348a70179abc2b6866eb9762333ce1ac599d0a1a31f6ba90de90a8027e124eea02dc6f57f563eac76fc2867bcf84b4953d1a09178f05abd784ba6358a4d6528be91a5f89e9f6d678e20795db265918998b020eee275e9ab3718082e0d899dc9a92ca52b51ea2a237057e925ba2182e2901eb01e86f5c7eabff65b1abd2904340a0c0dd3ed37e98c2e5bcddf794322a3501ed0b4df6f6e2d91d3605a6503e999b70ee2c52d9be80c212f2a61e4311c938d29505364ce30a7aaf4474bdb05562911b9f8ed55a1620cd5dc70d688fd9b38ab58b073657e86abb619ac2d2b8e9fe632f34f9fdaade28106c0daad703033920c8b60b8e004caa9833242f5b8446b243fada1f6c897522a6271a6ed2619985c17a670a41b273a506faa0c4394d34483b4d0e5363cf13efcceb3dc18b8ee8b7c2f14a090216da0e1c8f5f09b3d730e7ac56633ea73034eba649e33a968ea3ffd93f0c06febcba6af9545cac14bd60c3b6a595b02d4651d5837ca4e4b4714bf5657e590f76944c0311ffb29ff5f7bae831dc2818c002331ea4a336e6ad1afc16998c680e2752a66a77fd428450d4d5d7ccec08f872324003af4db29a358a4d559569ac087c10dfe7829153ba4517110b9a354ca53852622667139968d4f3ac49032d46048644ecc77b8d5a20df680faba0d92bac48f9484a3f997a3ecc0ba9d126c04b09dcb7c4c65038c6de818d3cb7e150e0d294e509b8eee55cc4267fe5bb2eaa020714a10882bea2f01bae5d7811dbd7d926e18a51d4651047f4cbe492a9c6c9aa0c3105225a2b3ba277fbdf6c604ff7f4e158b377ce960d7efba7ba6ca07a3cf60bd02105589b499de1efba4cea3510725213549586ef56f7d6fd861f4bda839b039c0af0782bfe98dafe09534580e7f4c70f84bba40a76b0870f9a11c14f87c303cfcb6f1dce35d6c06299b285a4cd9b0b7a8693cd6bda47962a8d8bc94529c70546954fb00ee576572edfdb59f8c012ce33be8be81a7654e4e5474d2e567a1b075c01a726a9d135f8b575dbefdfdc0695b28c946b28e4ec62163acf9cc67e8906dbbbff394ff8fac0587d56d6ef12228cf70a1ff62c14672d5b526a69d24c880a96d4547b09a0b0c92d5d7d43311742a91f1a94286bbc64418efb036cf03c960bf5195259dec76e6fac916171d2863911054814ef62f95169b8d0d1c5d2f432041928ae36985da03da353133d550fe421f925a0030b7e3d6b9848b01b20230a02584800d2a54b7de8bf9c95b7f6289082d01582ab1e9f2b523935a041aa779404103ad416333e3cf811d4deca54565e2755fda1843a647b97c00b6e11eadace018131fa51c22f33b1a4c481e285041740abf78856e28d01138cc32544528ef30c85d7fa43a0c3632902e49649311970edf9c00914344e322ba6db8e1c10403a02f200a3bee7c79b7403794634503ad3567482d47463ca71044b30ad3db5482a08225878a86814b90ba2ed5391f5c6eed4d2791fca84851ed03279b7e198de2d0a05226e1f12f58176082b7491d828d08b5c05f360c199f8124876b26ef81d50088a6ff51c151b7cdc9e690eb572e7457f76661734fcfe59deca8120d737de2a0f29d94bde183dbcc47d68f8a56c2002187a804ceb73fbeca497d28889ea1b2402e71bdb76780517ff4d219c883a5603c9fde8edd4c2e8364413b0cb5ce9fb3e08e290020e6110605b986b0ad466ac757eff8a31e039dcc4aefdcff4d9d666e00bb5a900867a7b38f01fb4f6069ea02623611bd8cf23a5dc98e683958930cdc00b75ecee6f61cd787e4cb1580f7c1192f40478b50ed69825b76d32616682dc2c04e55e570606b71f8f64f3dd07ed2a72f55a3e88f341fd908eabd0cc84569599092cff8e2d898f8776a555a538c4bf4406225a09a69d91364e699859fa5829dd03ea8c998fa9ae212e2d6f90644945e0e4e5440d2887b8354f5de2021fb28e6205d3819b91728d27ff708dc20d4f050328eb658e5b3be75d9f8dd4fa2d74d6010d7196a81abe97dd01aed944767e366da04fc02b46fe551d9050936d8be562ba02ae1d8391991a5d20ff1d329bd2443991b34f8f5139b0f8e9e4a4c3ace0bae00a72ed32bd5e871f4e232857644520948c4560e65c7a6b27c6ab2524c4fc238c0d4b5dc487294abb79a1e1fd1ca506ebfc321e44e6da4a6533e73190536ec933030421a2aa0b05691cac508fef86d97533719ba1c61cfb7e619f917a78b710c5036fb50cfc7b19e4b852671bdbc0752c01f31225e4a4a1ea000c73bbda7c65350d106e6feda2cfc5fa88618836ba8ab927aa4904c9fe2dcdd5551c14f0e2c07704c15ff6bc5035f1c3ad4cc1a839b07c5dbe911051455d2b28fa1aca83f3749f0f6af5ed983f4ad37dcb57c7a8a66bc6675f36e18679fe48fd2856f129cf0e890e8b735293b2509895605fee9edcd9c5a3da720da8a21747e409f773e0d55bd9069932c2c613e8f9d3c6e7330945274fb8811dcb70e3c1bd8eeaa4d794b24a2009a9c08f006a2e84fcafe20f2adc532c141e7342e13ea281df569fc3a4cc136b06cadc70176b941e28b8da21ac63bf3ae040c7c67649165b9c4437d16b7bfacc02bc8e90eca8b2011e3e89881e912827bae49cea34262206bf2c0e7d6f2001ee0ccba992e0554447873e603bc7a41746e4d8fa833d630cda1fdb659fc768fe0d0ee44fda72db7e22d5ab518103365cac9e220fe46988745a7c14bfc05fe12aba04198e142b41dcfbc7c4227aa981d1cf4bf8eaba72f5e990294891c67ac07af70b658dd1412bbf9c8223b6a6feb2036c182b7d7eee55583edc1702c67fdd25d60275d90f6728d9d7d1afab9f1d342e990f1064d951d23178a99bed2a276c2e8e02d435434cb36292e968567f30779c13e6972ca3bac762b45eac4a79b81628390ec81bd94286ea6086ab34b4cfb5ee57477247cf9fef0e08c126a47ff571028400637b05ce9eadde8896dca1939f70a74f3165aef16e3c268aa9ed3dd16fe1410abffbbd7addd3c9d5ef3e9f59b7c39bd8d1c4ef1044ea5a398d3a3c4d98824e07f6a1af3997737af13a4354344b5dabda1e7aca5d410d300d0cd1617e5900810771bb7bc2bb79da3d95ed86aba740f528f5281c50db2d23474c16f74d7e6c9706b28dea09ae10550e5189c5ab4e6660848a4c63a656d7b963d3e8a3ac81ec030d85e74d484f65ae513bc170a91be88db2dc0032bec95b6344a0363d7493c4d003d55d1c3487d82e6cef092b228357d09b9f0eb1d8df1e945285f1026d54f97f0688eacb8e0854ebbabbf48f19b8696d2201a97aaf64ef3bc206827d15234d150ebbf0351aba496838269b30fa73e172fd39e6cf0e4e5f5d0da7973ebaeed9b1007947af87a881bed8de2a0bf9cb2fdd5779b471c05edc72bc6a418755729875050ed111ee8bd52f08d87610b9827f36ae56c632eb7a33ce83faad54d6ad9c420e13d6997e531394e5b338d57f933a041caf5ebaf72e0cf695f2438a270cd0680c8cd0da11aa2d9226c67d6225b2d047bd9bdf34bd0a798295ae84bb43bf8931092b0b7d9cbbd962fb20d258c0e5509a7eeed80493d990334056213cbee005150d1eb74bb65562e339c16e8c54b125cf8b8d9b7eb0983315d8baf2bfa3adcd4d09d2e69968b5721dfe18553545cd02942e3233dca7de2bd73900910d8d230a8a0393060761c96c537118ddd54a82ec195e9f75986ec6a40806a25885d5cafb5d0ace57d93a333c5492e53153e76a23bb4418391cd0960ec66dc60d7183c7d6dd0fa8a235d329264ed7925a700c9b08f430337447bb7772902c3aecc55cfe5b6608330f59e8f13ebbf63ead69210feb11f2481d7bee2b0ba7384d2efbae1a70eb0e0a8ff9bd2b36a52de051563a7d45662214fd6ab77685a3382337510a06ef3d02137902c6cd13309f6b8c77014fa75882d969f7b09aec0abbc388ea33a012bb0d077f7997fb19ebb3abd76a020e7bb5fc51fdc49dc36e5159334ac7eb1b305a0d9d72ad5d181a1ff703dada5c5ad959f15ca094e19750c7b29d50db8256edfd0ecf2ad9b5f09ae9600be8210dc0d8526a291f7b777699890fee0dd6e7cb5ac809441c0ebd65ef59d0bbe511e863b4a3c464a3628b3ece77e730fadf878c6ef200c0acd6ed0288e0ad53a5e89187f369f0062214c5f8631816e89b28eaedfc6c02befb8cadf224c8e69e1153eb836c957e6918b961cf05da58fa16f8d8f82e7e09dcd476f68aa03d8e649a2fb64e9ac1fc81f22ed6e1648ee8a0cfcee90d411b0acc1ee3ad79e9540193a5ebf61348f49bcaf547a0a29720e1714d804e34b5f30b847d5c65498b35868671d83035de873e008d6203b1282415874b06b407eafd0788f65e00a6b1027120a9c67189ad90341ef929a48eaa710ace60942e55e6e10db67a288068dfea8e681f313d3308e1265427822771d96758fc8d4455582ce50ec444b69973a38c5d3dff3044d425b2d9e4b03a1fb394a95bf847b879b059060f3651b556a7a09ed2b1893cfc8a466d6b5605852df719c51fd081e7c281c4d201a347a72061194b89369eb5e2a54cb34056e7b89d96bb062140755a55e6676a078c919b356b94ef20d6839660b7939418d648a5b32fea37f2a97a24984c0274015eccda895add63b0df9cf5c8db50faa860fef005c26c4a3306d987a1deda800832185179d509c80ca36dac2692e4d6fdd7f9358c22f360b4552c7db57b61e10ba2f71118b1f2da30b4e8dc910a53a75e1f98fac806f5d4c862e2f63b41417509137932a6386b7b2105fd33bdee2f7509be6ecace6b219b7d2449519c9f7c4203182b56d158a038627f281f364f60056ad3d66e67ebcfd4de247e3622b9172f0b7ee7fab0f85a78896c0671c5057dde06f5ff39e662cf201ca3a54d45963bbb071552bc2cac2608dce0bd33400fa357b4a19bf116ac572e0cf5510344429bba0b5157cebc25333a90b6ee03ac20fed93ef7b7efc980853603b2ad90ba634cc10bc4c21f5838a185e99050c7be89f646d22267d559ef40b160e5b030faab3ac808fe2ddda1cabbaaa6d81a9e200c3269d9d324994fd9f3c30db6e4d1a272f81a0c7ecada75cc1b5531e7fe287ab20dabe04cb0c07693302a864742cbd51e3f6c007c39c4ab6ed2c7da798f3a84b01ff2e019f6f91ca68637443a3fb7be2a7e470c47531f2c24207889a583b641367c3e118298f14e3e645198d384eb5a0a4a640ed7bec37cf6b09566d0630f8b3d3b006491d5aa61779c1a067ecdf9d110b7fa0419de3664f9a27a122df8f3635d48073197ac563ed3088d1de8e57a80173abd0fd552bd79e10b051df7765b524afe21b9f92efb14baf983dfcfe203418f9cf10fdee1b16893c9273bf7346b8893eb607e60858e94c512330e8ee3914991efec09be209cd4ced8cf2c4395fbc8b62b7d29dcd8fc33eeba99830564325ff345f602b1f4f0667f37984639f2049a37db72ec3cb8348135508b08c969f97bdf433e0c2e89dbe0a544144e8b0db26586b80003819c59a35fae85af77892ee801a9a5047f67e4212b1085d83123cc220d1c4a98e925e1bd57944e3db48d341311041a0442161dfe32b4a42b8adb5ef830633521464882d8985296b46e9d838949d0e004f61403f9fdd94105175969e757aa16b9c9a12331c4d9ac4cebb32373042fd30e5e2c55639fc2738d40ba676b3f1f7352cb7d2f60e32df854c7303e69afced00c50af6ecfd866f4125993d651e73edee9089dc022e57fac6a8cf48b5600b2f00e3a85e0bafb23e9b9548b14e43f466b1b08becf0185524b58ca51c7f16069b9034bca0b6889c82eb8e3d2ebb267248f1a05ea3751c095affdb27315234ce79c0365f1acaea4dbe6a06a04144f11236520dcf7c79269457f1f7a960cc239061b0fe1178fb29241b1166f7d0b8325569f70fc6bc281ec8a4f10061aa02d43c04903f0ffa549483e6e9b9810fdc771f875663da60877bfc2d9588deb59e4471c920df3db8f216a7f6cb935f3fd8ab40e79e4a8a3703dbad1026a9e3caa27b2c9370ed9ed410f1620077e11a49c892f333f98c9ad46b85f1758a3a3239310fe8e8405100c2d0390409c505bf34df2501778c3759ca557d09ceba4cd11ca2aaaf9140fa4acddda63a649cc38dbb71ab1bbbb0b9cb76ec734baffcec605047976899f06feaec9ed4574e125edb36281025718d424ee2be8d897afada721e4a0e882760826cf05d67f1e7acea55df8212aa042b6e52df287cabd19a5f85d50479f849d027faf805e149b77ccc4037af8ba88f2c3a2356e2ac8d415e5816598fbeb74fa01e1bd8117012cb450ffa242fe94ba83b1ddeb2c2d1cb8e6aea813082163892f07cb10c11ea72b42efef51165aaa7bf6e56e2633c9066b75bb8dc20da848a1720b3aad0259111f51c6ecc5899d2c62d1acaa1ceb3d9820d8b9be30dc826fd2970eae46f9374d8f3c83e572afc520ab22c66b78a03cf8b3efaa917bd64fde0ee601dca42aa339e0f115ee291c32e5cbceeca265ffa4f7903d246508e9e36cac81016dd5882e9a8b296cc4a243a186ab344fdcbf3256a5797cb490d86452c456220de0b47bbb23e279719af76db70af20ec4fc33851db035f50fe986cdab311f0c0efc97d05721d558c56dfc8cd82eb9e015e63001723fd263fdad8854fad14b9603e81f1d9e4ab88cd8d807fe405c1eec32e2c3bfabd26d0b21e7382078d66bb60f5b1a07e07bf4fc2e09d8c02bc5c5e5e98e45457fa5427455625c03ea15e9b10515f76d3a11448479d4b6567ce60c6ec6c5833efb114fbadc79090b2a57197b0db8567753c2df450b583795442a1847332bc1d5348600ff221b4d4aa07ccaa7063791fe5e84884ae9ebbd56f3b88ffdb739125b93a610ec8b996d4ef9d6acbb061177f54bf48011c77365343a8b6d79deb353f2e307c53af10f4d8839807af006b084ade177cb02a78d9714b131576c9ddcbd0c98ec79c472e5e739d2c86a390ad99288aa7a9d42b5a21819f9075e57388ed09c6e68ee463a7da622d364aea607aaa53e284112fa7a56c426daf5232e34992e7b5f6c616d247073f643f120b00ba2da814b1f404208e465b07498dcf2226e1092e821b2768cad489b63e40a22095bf0f6b2f5cc0ebea7685292a43f03a08ff0e74b953d5fa5c5d9c0d770afd3010f4756d875b98d57dbbd610d1758423092f1bebbcccf8a308d3111f13fff8c006a93fa826be910165ff5d15449b13a28831d624a60ce37f224f05aff9c5dad03be9f3c11ce24f7a658208cbf857af1a7e0ea3095f228c605593da4688f7df20992de401311ab4fa6d21ea428eaed7daa7b9cf813fba806c50e7fb92984577705f52a7324d0e1236a41be7c9aa511c6ff6688b182225e5263fd6231967e8bac06 -generate_ring_signature 5354b346c632fb825e3d4facd91c90176cef0f333295379b68ae2f3d646796ab b232e87202790b0f139b5abf06b5ed9dea9fa10b45137abb748bcedec49957d4 2 1d18a97357d5c6f3660f9cbaf1d05bec77fc60225b96c643af06888b41d560f3 e4943e8f3d243545a50e1fece9b992cbee891844f6920144321f5026d51c010f f0967d0fca4c4f72d2cbf40eeffac2483d32aec3c862f83c0e938e8788e95602 0 f89a4d8fefea2ad9a95fb3a811c3390e33576bbd75a0b0783113b32360c06300528705f2b5a2647438fc9c5ada50bff7995456348b8e7f66682ecb91d3e5530baf14edec93b76f68a74831d4a652bf81144aaceff49af43ef76580bae06f900b3636147d941586bc331199ca501bdbcc5c5736b91c9c0e518a42afbc9c4eb403 -generate_ring_signature a3e9f19a6821d12b0cae5d619f939c5d9beaeb9a361f9d7bf374b3814ff23ac3 5be7e561234f7b6bdefeebaf9a512ac4d97b77dff058f832100634dae4b222f3 4 1385acd49f2edcd1d00c3e69d899ff286d76ba1ee5e7684e480d914d31f57a58 5c43220a60eec00974abc48b6dd95e001d4b1e362aaeda5b712d8b6aca644304 02365a93fcab00824d25ce4e4bb58a49b9f68ff7b1748d1f03f8e5edd6fff416 a1abd0f71d7c05bc675f911d3c4e54c1b8618c603951a1de691eb38f030199ea dd3b77701253a786191b9d59b556f2b96336027fe8b0de80bd2b059acf610d0a 2 e021c4134bcba64368a26a56f1bb05c1bf9cb4d589d10bf3ecfdc42210718f07f0572d248122a84336ea6246cc9597963a773195bb73dde9357bfb2a5f5b1107cba2e958f3127ca96ca83f58741867e834203ac86625fc57948a8e80e42da50aa9bab187c3b9b8923e418276fea5afe5272976e4862d9984191fb792d20b950aa2cfcb4a4f8be1cb8f0df51d644b5fc59afbd9ffa6a7a1faef01d3ba21676b0c98d732b07fc9057dfff8a3076daed561b1c9d454b3a644778cd49d5f7804270a0bca066cf92167b0ea910b209de29c7096340f85f83ed9907c2ff1cc86d2d104a90ae2cd880141266a175dcff82c6ba2c9aca04b9d1a620a2f5e16ca9cddee04 -generate_ring_signature 095549371963a6086a8533f8182dac9a9288863a9816d457484aae549c10c454 034fecfec4129e575a8f79e4a2f329ed0d6e804db3c6b30178a298acce98562a 2 80e4e87a2b4f800d02c125ad32c11b8d667705547775533c48d73f12f39e1c41 c404cb7233f19eac34d072f1f24f9edef448ab74024a8ddd8d72639079be844c 47b6e374cfb1b6ec4adcd3183f7b9f54812d7bb6d87adab41e1713b89a918c0a 1 bc8294c878957b7c740f5342c503018ee020eafa7df83523f082e2b2f77a8e002c1d8cd0f062af3b9b0753f7af2efaa0293e1343286ca26ab56b217a0f3f7a0f7ee59adbb026a0c151c373c7696f5af9e7953efa8ce474cc747d93228656250f4ab0fe33dd238de41f4d6bb55063db979677fed916dcc240976df29e73cfcb00 -generate_ring_signature 765d32e597159be85cd75d6f4a26b6afaf0eb7618a10bdbd64c3629d5f37829c e5729996355a7f45566941b16bbbdc3e9645df09d11c7a4fe0297d9046d80876 4 ab4b00cd5d57edf83bb7e34a17d61fa41fd2bb00eeb8b182e03e05fed0d63047 1f121a1695b39b438807592a0364e537459d9ecaedd7be6ecdb71b9534ef74a9 02849420f9ae2c064385bf53eb6a49ef1ced6fa4b2a386ccfbbc279633f3a4d7 4ae5357926a9a9e9204bc6f2c8d3c05bceffaded63d44c2e52792fbe87964b89 fbb4fa26dd9caef9daa40f9f451d01747fb79eb9bef92b680df1b0f52588fa04 0 db3689e1e4f5fca30daedd5849ec1b73262307ef350425bc1eb81e8fd3253d0542a82e5a208735388074d8552a5702d7b098f774c6a691cdae124433b792d30aba8eeffad30f9a461e0dd234a1fa9d7090bfdab4d9e1df0cda9ca105d1bf2e09b42ead55dbfd02e5229f75b6df5aa692867b518555adaccf3f5020f15fad6a0812609699dc2f4df2add0083066107269afa6087b0fa45029d151347113a1200e46d6f385ff2fee5a20908e89fb13a70dd626c674e5120d8f0bb3b92dae975702b1f9b998139e5e35f2e7960a6d54a89ac3ff95953179ecf910e35c5de012dc09671d4f91c3c87f9c6718fc94c46c23fbc28c6f6c658e29286f8685ee0350c208 -generate_ring_signature 5607d2ce8ec511c95bb7a1ea452f4574df04bd5bdbaeea20ac0e508f13feea37 66293e624f791235be8fcd3944938a213b1449de9b64b840f46ab25c244e2eb8 15 252083243400c45f44300a90a5a72cf124586df2fdd358c3f2d8a10a9d3b9f84 dbfc0f9a60359bdb13b1eeec597ce25a39019303f53e214106c1b6efa039f41b 1799c271c54721cb9b9aff31e770a27db2016d3462e8f7cdf97e58f5d957d1ea 0f3e0b58254ed1f16875206a5fa71d8b2bd3051cadd875a85c064fd43fe972a6 db61f4aaef31bec7a76142570228880bc1ba95aa3e130efb815c392daf1f648d 69a59d101b46108998f7147556e1a489d50a8de2822cc4b616fb316ba6308890 3216e6c0b89b0e9dc558f22b0646340901d4bff8dd6d0d37b428de10e1fb3d8f efad5c615373f47a5391c88bd7fd426915d61f772f8813fa1998007ee605b032 aad7a20871ffcf814a70a4964aadea64a2ca043ea3ed90c04bc0c581ace03197 ea57e5ceb849204391f7896c7cc93ac01645bb955a35e7295a6201d3888072b7 ef11e7753f6973b1f3c06b2b7dbc596db6f16120715339b17824086c12a18686 8a8442aa38bc171b5fede9ed5892c4c6f8b7834f6d84d6818134a2f61125f86a 12fded8c7ee0e7511d3f37d7fecb900ee662c3f7dc36756d64fd7bd5221ff607 97e073bf305e6e79f257fdeb5d5b126f12f818edbf3502a129a71948afe94933 71827fa61bc9bdf166378ae6e479ab50eff55f8498804bf26e92fce047f1a76e e1c93e67a747dff61c24a89817f62f6f84ebe585721cbb46d0139ff7d410250a 10 eb70bbebb7c2dc13930ecfce0dd9a4d2b4c74f76574705338969a4e4ab4705051ef9107d5bcf53105f1cc89db703b7df5b7b96a7555398ce95299984f8e0410891160f3e04a10e4bee31405e6a99df74e10e7fe4063341a18e8fda5661778d0023f90660195bb3b86785d4a94beef49ebd1a49b1b549ce87915a0068492b180f06db36de4acdbf2696657a50ca4426b27bea466933c69d7537433d35df494b0b6820e84113b53664c05b5f2d69daa2650f0a1f63222061b4fdd208b579e1700946def763f7f13d8db8cdfbdeb408cb1f0fff77c2bcc8f2a0368df913874dc4022093e14ec0f82db6c13359c2efd0eeadeff8c5c672fd9aec9788e128f50cb503b6b5e519d17cf3f1ce24dfec6ad48e7a41c0feb946db4f6619be90b1060f8f06a1023e4b078bf46a2de38b5e2bca1d3ab37a4a0e10511f4b047ab4d6325e850f219ea4d87ab278f7c515ccc5ffdc49b295a13d165d3aecad8f4e9cacb03e030659851e4ef8428e0b3d98ced0cb704d68c54bf8180ae23a57b24226176a7f3502f12f634bb8713b68b918e42f1ddb0825ce18d02667c0ae9cdacbb3d1aa7d4402bed6eab1643ea25c05a4a1255a82a14a9ca9c53affccb451c2d47287fa70150ac8ea9a7f034773310eec5906a2a15480e7d4dd96d2c99e9f9327fb41f66c020c9cd8e65314531fd4fdda191793862858d2ea8aecbe60542266cf361901b65a00beee47fa3f5733ad0b538bf3048421657e5fbb749797e8a1d14462ded9c4af0164e9fbf417f721d58546fb5d4cc467e2ec8d7d640d317a6d1d69dd76974ab8081c3f3b3069c9e1f9a37e5919aba6876cf6c7c8ad33e4b1e032504825dc65fb0d04a75968087cb27d7205e22571e2816daa33a89cd5417badfc7ecf25a0ae4503a1e2a7e33a2e0bc370ef04ca445cd0666c09e4f40e860897e05019ab97e51b0061470490899446d9eefe8378e0d29f5926e441a88b08377c4d3e879010ab080137fa124658a6e37654f4506f6f036b9ac0c85fba6cf2c8a9abab54aea1b2520077e10b3942d73cdcf93f0ed26cde6ec701f07640741b3852f1838fb6239fdc01cfb9159d012b4e3f3b6b92029ef20f0bb0f15fd8928f70ae480787306265a80d803778650be9ec7a6a014fa27b82a85773d13f1e6812880b94d208275009130681ee8a9ce27eae3aaad40c9d569b7f2a0d9eb70abf95bd6ebc6efffa4163a40c7a191e2830002210da48acf04436b2827d1c3b148f72534e6a9b1a24503a7a03cd10e88b2d88288d258e5db848276d3a1b19860dfd3243d97da2604e107fbe0086b6419af4ddb1ede8c35472b05174f643588b7a026f6489e7e724f964fbc70f -generate_ring_signature 781f7aae6916cb86442605ca1c1329f08692a28c5cdfe0cfa95d964ecbe6f9e7 dabd3b459a7ca4562fe5ca42191a402e51cdfe567079cffaf7ebe599459490c0 49 6d5bbeae1447773bb341d4f6253c84a563d776c81124bab9c82b8b55966ef9ee 5f3582036521d737a102ab0878f3482825882d869168795c8d29501d25741e71 52f9596059344eaf95f52e49d2ec46868c01a0612413866c50b351c5628fc8e6 57d139af17d71882f6a385f6a87caa657970517595baf226175248877f5ddff9 a9f4ebc8be21b9348b62361a712c90841c4ff4c595b36049efe2d101c4efd321 e8415eca3390ef846f46259c0e0fba6dd7427a7b5f1af4cbdae1cdd34abf7569 4f3e26a193a4cd0e338fe5ac6b4451217ff963c1ffc7ed2afb8c86d534b0bdcf 438597f124d1177200a93dfa9d8d0a9cbcceb2cbae2d93640147ff1eef7a2233 f5339ce166c0ceeb926acfe4a017911f9e1e3650e4eec6457e20cf52355ac4f4 4c59880049b15c24b6f54586a69b8935b5d8893a7dafa1550c5123cf0a253cdb 18a2d68bcf6db85d092a4362767cffbc973fdeb9eaff23d260c78777f8f57681 0d45035f8548a968acc0b0815518ea3024360446580b0e5878d75b0610911340 724763763a18220ca705b3221d8ee2231cce67235d0a75ddf076e8ad5523cf60 65236dfaaf133b1dce26fb478a81305bab5dd30e426ada4a399e29a7c02ba4ca eb7717bf4521a54e02db70fcb96c20d9d4d6673378f6b2c196bfc22a3d5dffe0 6752e1853091c6b4aa5f88df39696cdf2b7ab14d3e4b140d1c3905a6f86766a9 ffaf2190b741ec45ad0bb7070e66235764c44449ab408c6769eff87ee92df421 9a3fa68c3398397d68f7eac4a19e7235bd4256243bf8667082c4bf5bbc06b386 c0cbfc6b9c6b2e6c7c696cd7a89e4c79c7dcbb394199494981a6342fcffdea0e 661f99232ae0a6143a994c679146f2756cb4dcc6c465145b05d7bcf57aebaace bcd4b4ba71a309874300bbc35f9a9a541a7ee8055f798a4ca4dc546ab021f25a 8937c53d0262f927ef14ae7dda2cbd277363612e98669e58604547676a862af7 bf01ff591c0a495d7938ee4e3aefb0e20b121823451b1d902dfb8f2b460aabd4 6e0d51f94745295a30af485bbd46ef078fc52172f142f1f73e68d5d0d9ab1222 36fd64b91f05287e299200deb9fb4cb0e3de6f0240e2dbf274912579a8a75eac e72d9f05f370aac2cc5e0777bad931d6e4e193179e822583f02983f8c6bb54b7 d589025c70a128d8fd4b6364ada77612879a039ff49a61b710ea5ba856eb4cde 78c4e48f85675999752f7e6114460354ef4615e3ec05d2f6f96b85a0feb2093e 50f02e55a8df0e9bf0c44c66403ac989c630d21c2389ae73f3413f1d07f8fd2f 9d4879da832b2027ebd9dfe0d5d361a4dbf32158d039d6951d20d353d0129c6d 9e5f72821c4ac472b040e19d85234f46aca39613013ac66788c3143eff365d85 9d1d9d4be6eb21f591cef27b387545a22dfa1882a7dcb30fd5f2141b398fd2e9 35147ca542ae0c529dc9818dd8e4bcdc300a867254b9dd0317bf74a860a66817 17c55d7febc20cff8ad649ba7ae32318cac1750da6b4ec47c31c0f49ee1643f1 f24f5e7d6a9a0e9c457a0ebec586b4bb0a3a5564559cbf377138f64562df2484 952e0f4633631f3eb0135819fdb7aae7742ac5b2024c5f15ca1cc65e1cd08f24 7b9adc5c33bcd0feca677012599cd9a7fad17de30a30efc0981029e15c4fafbf 11e437c3869f6f8d822ee753c68b860560480a37eb57706082d076e0f601e54d 7b89c9df6a8a9f261d298a996c3a1947a6f676c2c7be4ae7643c2ac5d8e89c62 ff3e6f71a711fb28a24a2ea10170ec29d4fc3f3e2f2de6a8698783b0664d941a 919436ecb0dd620ea94686ac3fa07bc17a777d48f9c0224dde1703d7a2ebac63 7088fc87666f3a284b325d1a50ef179e47ce44f546bee688bbee3377b5462e3b c4bf7d3009908f391b0a36d2768394e429a4afb69d789d64bbf1377402f39b01 5626037572ef6d5c06636c8d2c7a56e19e4eaf2bfd6229204d4a60157b7c3d9e 94c3d100bdf06065c3d6c21e4d743d925065888271afb52777926c1bb49698d1 45591bae02bab2a3b21a83305a149c0f1ad63d16f23501ac471fc5195eb4c934 fe5634f879e4007ac0ffe322a23d96d428ad09df6eafd123e67009c77d3826a7 e131ff9519e778753cbb08ea67fad0bf1349f8bcdd3e2a9e30556d11ead0dd5b eaccdcec9531361e1b4559e4a9ee176075cc22a6770864c21abab1a28fa062e9 b6b0a5cb6c1acf7b032fe3401997b9be29ae854fa7d6c1dff8228a85426e8e05 35 93c3888f5366b3811eb99c12e48d75e5b156496deeb9179f95323f8fd54bf502a5c60f24e64f68346109398cac87dc824eee4f2f79c8c585a641eecd2d492a0f798aefe426c3277267e976dc9053e06c8ffa0b3651b028d3b8a938fee636760781eac9a87abc390e1e52051d6293f033a95776710b6d964678a26ca53e31f304767c0a5bc38a4fe74bb2e893d0b2818d2520b6f5ca03d98b1e5a0f9caaf23f0117ab67e80ac2f8e5ed463c178e477c31d5ba7747747ea929d4404eae1fe62502d5916bd01e4b433ca555a00191c7ee98a6ab4876e3fb756abf89cafc4c6e2d0965de89a8353e82d8a6c7e71e80bb9152f854824f046823d22a4afdbcc3bdae0c8832138fd3bcbe238d21ed29318ecbc9db1934de24a8449f683cb97bfc7615004a7ed4f1adb76ffe520e2f65242ba6430e12eff1e3ba757e96e34d38eed6bb076ccd96cbcda1f3a440054c95fcbe1d0057feb0b1f0231923a0690000f34a270628ab24614d456e73ebc344324043972eccc9cafbd1587e92a02a61597194750524ac9b889fdcb444a7233e941fd064d732010adcfa8f0cd561f71113a1efc80ce17a4dd4bc829f4574c047bc44451920b5d9c33a2f91b1ef0a8f8f9ee8820d0a0c7e8de67f781f350ccbeb33fbf38f868c7662daa154d48f782a154470bdfe03a19b9f56409bc2698b8feff1f5c42a4cff69680d5a46d54d7d4c646447b46c09badf1bb306eac33d4e634576b14dadb4d508238ccf9047e1b40bcfafa63bd40876231ebcea85f49d45da626c4abab62537541c69671ff5753093ef691fdb3701f51ef9098ce1ffefda2b13d030ef561bd7ec6674dabe8b4e99e51f2cf323460f920b46cf82aaf32072accd7c4a8b1ee4704b790d9e3de093345d1765fcc8c000bf53e301d9d034d3509d81158eb5d8a4fafe5f70ced208dcf99481b74f0c5f0a08a8748de728d70759e779e1c63941ba6ecb44c1db1bc8da958430c0380875071326284e65a1e7c782a11648e2592ca7888feebb0a82cfa56bd1e394adc9350f7ec897707cb8dfffa8c355680b951ab1acaaf3e5ad8b95c32a4f0482687c8604b2da1852c6d25fdbe7729f64ec4d14056f616882f4735456980494378673610d69943f0b071d86599b8c3d2ce96657b8b3278a5d12d318fb2a80d057f77529007cb74baa9d88e8192be73af2dc9efebcb2aecfe5a25b8fde39ad0b98e448f404ac9b7491f105803d80680c04626db1c2cbff3d62e01640b2e2ce9bf9bc01c206b0446df64d8e0d6c7658133b23f7176d44cec7db3b07254d87c5743e40fc6e037db751b6862775844a80e90f44db96a49c9d9915bd3c4b73673f5c29471b140aab6e2405cea4d49eb47a379b14578b8524d36e9c2af97a9bf6471552fd83cd0eca669a6fbe1ef0e95059fe11630e6fd50211fee54d228f96a06a395c0ea7da0d6fd3258e34f9bbb22c0575dc1cadb272d30ae02464209a1a7d16d3982483770c46b1774e6386cfabf6c5f15aa41b58cb2f4cee2c6ee1163baf1737ffae792f08644fcdbd8b95b45e742693863e1c8c7572d4b4e323f80ae277ddd94da8e2020b1dfbe0b5caac2264677be7df5ee2f4c736903f37a51d9a8d29ca37223152a80edfa4b3de549d9ad1ab986bfbdb5199119027194129aa4b1105615288f05ce2042ab777b86e21d47612d176ef0c2e552e1b1bee9658f317724a7fddf56b8512018f7c0e7c3c2c0854f31a28c66f58c3daee5335787532d7ef973d7bfeb27a0d00c537a687a5b151e8fe027299498fe368f52f74d55984681c7863e9d3930be402d119a3a0d9d1a0e821efd46208918a8fbeb78fb15e902f858945f45748707a0685c41925e8d405d63a2bc10cba9ac882bbb14264edbd34fc0c66d42eaf63ab065c8f157fc8d58409be865a9407a453b0b46521a3dfd9ecc893895c4b597b8a076fce918ffccc50e0d75b163cc2c01cbd0d98ea1b9af7cf49044aee271f7fd10ec964cde6b3f902fba7bb442b693c8014dc40097b64476984d8ddc13ad2dfd506c21993e99f3d298830ccb988a73c445f77dc3d50bcc8f3d374d1e4e46799b30a94483923106db9296aaa47f8ef757331b229b37650c9a04569828bc8ada3440cc7eeb92090c9dec8396bfe4d86c0a54e810d3592796728c7f62555c27b8cf407cbb0e4b833363254c20275b548cb4ea414885c00c3e8019e0d31cbaff384310f727796387be295a439bca86a413a35a2e89eaa356d763bbb0cb38b8c24553b03cc1db55c9fa81ed1302cb0a6eefb8b1b4796ff3c536f372b7dae79760145f90e7501ee1fd011347f05080c8aed0d16ebbc99740472186ca262734fb606fa2e0ec1376657b8a17bcc3d35c29319c4bd01f901e34350b41e9914fb1174f6c40d0a5dce7d5c797ad8de98de8088b96107850d3e0b244b4352962e1a8cf05a696a03e25cc2a8768eaf6f13b0dd75c0bfb0f5925c8b71c429c550b7a5c5936207e406dac01fe56acbdb47fac950449cafbaffbd29d03ae29fa193f542c5739f0de3096b03906822954117a64d8c73fb9b3e111fec9b20e39413820387249fb6f2ec03d5383c9f1843b84e276216d91abedde856a2e25a0c5fd0672bc290a5347a8d0210359a34caa1679308378eb7dcc060fb82020d680a29aeaf59ee454527d8520eae56ffdef605df2448830da33474ab9de5bb03520ee53202efb0d7f143dbcd0299fb11fa7a22696e9d74c38c083ec14ebfe926cdbae09e30c8353ed325290d05e7751c0b2938f4cb38d7941f1ad88873e2d38a724c0900972128a250ba15ae095553ade49b7f603ddc98e6010df032dc085126128ad310c3e36bcb86e345d00f5c27fb2ba230cc6b54156f52b2576f0c9859dad33a117d14f92306392b4ad502b950c585aa48794fc99dae2ebb5d16aa0c09dc6b56db16f43099d1ca53ebb20dfb715c570ae7299698d610051d6f4a9705ff8449483f74a08bbc9af06a3336024e5c0dda5478345c3513d1f3be09df2b2332e0ca14dbf93ba4061d3a4e6da802a818719173f70e47438f8ef121b82bc43d3d9ed3c0534c40c1abae7c2d83ef066a36c5740c0066b3987d35893601072fee0ab5683fe7441343546fecbab19c070763a3e8401e3f56949b1f9c56cefb19a0221814902e9eace4e34ab0acf7c50b0e9472042ab13daeab0eb360135e0e9c778f240acd343da06570f4f4ce4ee80d363f695bb44b06d80070d48461e19108633b69c38b4fb706861f6b77da5ab003ebfa10791fc429622515d376eba355837f8a29144bbe164efb6d4302ee05cf0b2dc576f8be029f89a6ae804b7ca2b18c6d1e19c9447a8d328ce248b6b5270c0411d1685707a5f2deb12dc88d20eda06b4bafbf129c6c172795627286940ff80ce4c8f31a0780981d13ba77b8308fad2ec8f9af4ebe06ed44f60cd3e6355d4d066cd8fc14b9144f9d9c211b11129d1adbd2e292f66816f42b40bc1910eb13860e3efa5543277fecdc41cc4ccec5497f02359a97757e624eecf9ca089da69bb30da173e24591756d644697a1af48955f0c6f921f018a5155873077fcaad53cd20664376a43d9ea5cce11f0a1c0a2587202ecb324923e21bb24f2c9f5e777b97d0ff2e6dbe54674cefe7978de834709d43dc818f74b079c1e9e8519d6b5dbff1e034e36003f9aa537fd32f03ff521dbfc00d09c93663ba664e5ba17eae9c227ca07bf52dddc20c5fd3c002c3676cc717596badbee0cca2aead81dd5fe619f5b4d06ebad8ca85b21b00c1fdfb8517828c94ec177fd780d7be7482abf830b3645e40e2f84b284f810757da394d38324945fe6a25aca815cd09f8039678a4c22cd450b48cd382ef8d822fc55085a36de2b4557c6c5449006d2d967c2c3cdb125165f038b9e012dd1fd126cc1c018b1f97513edc43f1ccfed8fca686f21eb29173dc4017649f391480046753b7dde1b4a063125a0c5c0b05799a16080990d6fe995ab027028efd492c31b5f535f8a616b10d3e7f21acce57283f0c933f0d22e1467d6033f35e4a120bce8b7d0df6fa720de2e7e77214b8cd0a3219c293e544b63641d07385c1755dc6bd0e8f59f209044b41f5913abe05d9f81f86a0ed46a1bba47a10c90218ede046894bb9a2aac3883f83bde00d3b75b5c0225c87e4e9b8188b8930464010272bc7e42c6b9f4269d2d1c0557259da69eccf1dd388c11fb9b7b2c840ba6423c9dcaece7b50be0ef9b76fa5f7835cdb2b37b05fb158e3ede5368b1150749d8ced081a22afb66e2e6498af7649644517d2ba0c06332f98b7c68ad7bd707ae4f821c55cded10ebc20f1b2f07542c7c548495ada0cd5dc6a35a176366320cbcce9d9dc58be36728bfd8d9edab17d16b4a812a71b24658a4089edbfd3e2d0415944eba3b72d613ae0378a2033d967d66bec1c484628e9a74f37879d1944505 -generate_ring_signature 0ca6be8442f73c39adb0992700397888777437a7de0da8c8f80ac7397c51f46b c2483ecf2cb22c97aed88f73d621ddeb6f26c3417ed263dc17a486ff77274806 1 03b86aade5f2eb2b8868d951161eda03d4a47efe945a9dff7d630de9089a94f2 ef2190e473cce3732bb33ea3a62a4d8172a78336453c32f4419d1437ad3e950e 0 c1f782ba4f341d60d220f9b5e0ca9caabd2e5695647d3d37a3816d0faef8d108c154a4863e0dc093d51fa0b19be6a79004cbd7ce29c4d97bfcc0cb4a1e8e3a0b -generate_ring_signature 79c5aa9e030b432d9269f7e35bb8c1ed8eca493372526a02ea1d6ebcfa52b385 9b9a27939cd35b70d56eefed6bf3473df1bf5e1cf3ebaf7de8ea58ef212170fc 16 1e6c15fded661246e31217f773daa1c73c6b5a535dd39cdeece6347aa89981ed a2dac02c4e2ecf3f06cf95a267f7e1db2bc014bfde9b8d5e4698279429848a43 ca579ff489a24164edf6a84c9bdc87374b47efd80c3f994184b770fcce575296 2e247431d695436c7107a280e966eaaaa4d90440d824f93d1c96ae49bc16d4d9 f2609d17a8633257d1cdfbae1690892339e49f9a46eefa9e7b97d5b0570f9cf1 bdca7e43816c3c6fd0013557b1e0f2dce14ead44b6ceaa381986cf6c3055adbe de1b17fe16b510a67e0fc049a5cf4f801a9d9550e7ab8a5e4f74ada85a4057b4 2e9281c688bb6d020dd8a4079acab780a40329c6c54e1947f0e0e7d90b614aea 51dc71ffbdba2913ec8e578384e4d25fd2bed760dda9649155d4ef3f49898c63 88d6f98dd9e6c2a990aa0120152a372da00c455c97b22767803121f55b9d3433 6ce4fc262a6b4837e5f7baea0a512112a2d72dc6d36570cae857511197eee9d0 ddf3cf1ccbed6da657be10b878abde58598a07eb6be65cb870978429bd6550f3 5469427cb812861380add098f93b02d766d7b9de7b5c34d17bf8b2105bb99cc8 ae7c7772243d1b1a0939390bf562946634fb8f3a3df045c15f622aec4a19b05c 62f43f2a86d3cc9a59a82ce2c087c9442c8f0de3d648e319793e8c7ae4158735 1fa26c1a7685ce10b3734969ee68b9d8f441b0ff1beeda2a6a5ce41d7857727a 7f846dc0a1378202cd26c66f6d80322192979c9191c9bad913accbe8f3b4030f 7 f6b2826c6522ef152153ca57db1539552447c4aea942b0d0ab4177dcab2060082a64492270e31bf2fc7a6b88e454d0439e96636f0dfba71cafcf12e8daa52f0ef08a908d314bb0299073767fb535b6ccfd443a84279a1d417c7d2f57951373000e9afd33989674fbd62a5cab9dccbac40085b810909c75ecad2049c2f43fde0b80eeeaee62401253549fd8e156b0844579069c43da75dd3377892ab9d18a930384e3419928380a16d3749e23d82d4b9eb117547e83f2efb5c7b17ad94650680dda4c3603eb2822fb63926c0835e8f23efa205e16c886b79ed3c5f6f4a7e836089a0bd608dd6b21e8accbe3c15d507d9c1052fda02dcdde8984515258ec1eae083f91a2e9b97fc65634e8e41e57c35b7a66620ec321d3a2c823634d6e8cf2b3024ae90cba19d5f1f6f05cb1ee6e60220ced97134dcf19ef5935d087b2996e7a00acce8452800fef5b8e66c543ee125cdf01e9f2152669997fa03b06d3ad90240ef4d5cf978efa41a25ef9f36e04653c5a83ab56e6e78730b30d86061fb0dbdc0412673a903576ee21e5dd3d73dd5ed107bb2fc1a70a0b45ca7d5384718db0220cd137ed723c6688f24f9a2d401946d61c8907db5eacb3fbda4ac2397c32ec37076d26d180116b7caf6bb06de08de8593abdbd8b03a47b79f7bd9c6aa0d8b64907a2df6c17fbebdbd023505bb03dceccf1ac6a3085a15b78e322c07d0d6999aa05d589ae9f4b66b6315bd5fda3667d49160ccec668a246e3849f8d97bab3a6280e7870e325750033d0a842aad8c672757e82f4c6911fef291c5490d2dcb8f17c0f859252c33e93384ab6c9fad94efcec116f7952f3a5cd106410df36d591c2bc01aa19123b38a328fd2d7646e73ea41098b530793cfb6229d3108e9bb235a52f07a675b022797d270fe36eef2e56aff93d2c81cac9194638aac956b9297cae9604faf0c1394a4fb3f871e0c223b0e499b3c182963c7cced7988decced05eb04f0fc0c18f58ea67202b4b48fde71a2df499563f1020ba8591d59150ddce69c6740dcc6d5742692e1b1cfb2dd05945e94bec8448e8cf1d4488d9b33da9340b99fb0360d95194f3a16f5a3a0d285f11a7a63fcfa85a36b03202cf353ba4b6e9ef6807a62b4565f8a6f8736f14f369e89906bd3b82a9c26a8b5810147ffce21844d805346930d4bdbd79336c04f58e4c0ada34daab156a3d782a3a87065b3bfae4280e224e1a38e7ed282abcee79cc44a4f20c0a33461ad12dccf364bfa4985aa40c08e2973c211a208492c6263dd8988dcd9af2d1d1ca50b3f7cbeee46846b01e260f9c7bbfcd3bd339dd02a4f4a8638157766382b0eefec52cae54f948f6ffc47c0912137ce66b4f7e6944bc994baeb28c4da4f075d993447ea0302f80a4edfdbd0a425074c2315180a259a0ed2db91e35884de3a27dfd91c45f76f94b7f50e62504 -generate_ring_signature 80e43999bc762b25fa9d83c6fd111032ce5582d573d6f0f3e82e0f989fb6b8cb 1437aa8974c08719cc58a5334921c3201e9f45a5290f2581b26345a659b86475 17 40584e3d53899817ece3a763d73fbd5d5a09cd75ff713d3e06c3eba2fc853319 e4b87de4b1b936bf74e0bf1aef738e47297e65f2df50c566a9d5177c75917534 98449fead3b69ec5ab2d9f524ce8aa5e6d84f14917b3b03575aad4d4c291a634 23619e368d268b186bef7a5fa0165c31ad8a3bf4b0220bc262f484067d9ad4c7 06268863803442caa2e603b4791a097a8f33c45683ef55befb3c0d7e3055c36a 61ae5b158be8ad2e489dd7b0b78da17166a97f09629f96342e8712788c1dfc0b 4852fd68ec5cd1231f48b79a8bca21277e347d39af3349bce848312452874d59 5446a777840c6f3f75aaf437d1f84addbd22b5d5c848ffc3b7fe16e1157e4819 29984210fb9b7462a3cc598bfcfaed8a0d704d02851f1ea7d3e7e3cb8d73d3fe 93d4243ca3db959b7f3d7af72a0bc85e27226d1c716dcbe1c5b3743ef5c289ff 7efab8e221de5b67335eb804564b60b5be37ea20497329bceae89822d9e530fa fa084a1bf2c317822a31681302722b4a8001fa2aa9a0190e45b68346298b9aa7 bc15f7855be9e63f35567ed80b629c270a1cb1de56ed670714645261edd0acb9 17749387d62c8c3f725f1acd5b44c955bd4b35ece33bb5b3fb121ae52dd07750 7b83783748d179d74bf5f8a177a8e400ff4783ba1a24b7966d99b587f6e4210f 235b414cf437b43c611267566496a834045ba01b3f16c7b83a838aedb35e5863 0e326a30399705ca5c7fc10ba5f072fe06656388cbbf8f2417032c6d3e1f1519 0be7d9b1884397738bd4495a62ef77233f1b262363539f6bc3834b3430c84408 2 6e54bf81c8292b422953d11045e9c16a3bb5aa4b7c8f18deb779c3f30043a40a666204755c2a7b1b71670f1bcb77b0ddad6d66f6b2030c972b4af3519d02d5020ff170f73660232a18cee3c9c4cedc3793d74863a39636bf3641debd92690b01cdc874fda760d466413f581ec5fce2b575ef9f2633a3cc763587aa07e210f3031991cad652431f89e252b1e41161c5aa796d22ad697f34a07e2d16916ef5e90e9ef919e3028f9501e900203bb862ce3cc165645253aa8e7172f1cf210fb0d7086fd3d9898a5878809362048f3c41e0234d3fd8f0d3f34f40b818ee56bcb7b501411655b017f9aa444c6c6d3216f2988e1725f42e67609a5ea7646f2ab073ce0527de6735c652465de619a6e2cb1fc3f191c49b6d596bb47041031604310b1d0fab2e05c4068192eef029a54cad58345af6089e41d10bc6cfc2492354b6e3510478fe4dab7d96f497820f7a3ed419fcbc2d624d902e10863bff3e6960807d4c055aafa3c0ada813bc6b951536de7b14e2929c3f79742f32322d1443e89de5310aff8a8db5842fa447171fd9fb81ea354484bb20c30637395ae309f0cc5662a70c1071dd1f51efbe0d3e068af3b3624e8f92d50bce34518635b9f8ee103056ad00f70857959339ddc403e4881ca9074ba2f3f89cb4f1fe119124fc13f114d144053d4362214b6af9b2282a0217457705e5fbeb516edde8484aed5d2ce1dc6a550ea92fe7f5b6b783131071ed29563fb27a8b1427099f9fa604072617d4eddef00a9cd30eb8012ed0a664cbb953b0acb4e5da853a077602815158563caac4ab6a0fd77e93fcff3d8f0ab7a219f38d6fbd87cbf7f567ee971bbdfa65d4818360d609d824667a828100cfc4cc9acde65baa0ed1ee48e41e298a6e83cb68bd7fd7130ee1d54265da7722a917b3cf5c310532ba47d1abc497bf684ccf8a559f4fcc4a0d692e209e91b065f791b39c525368eed1f258a0713dafee785cb68e94b59e8e0d3b7a670d4593a9e85b0816701feb9414b080d9bf29a9d5c4e9605e93a4c6ac083fcd73ec5362887477e36c426f36d8f33b21b1525d8c6564c888e870ca6d6706befd9c761e67f10f1a74ee6882e31501e677732060d2a553d7cde9cf4198d40012101888acfb84047035746b94ba0bfd97fe06ecb01c70e3909ec8ab4f89e10824bc5165cdfe446425536157870f92d4b0e4a591cfb3a2327d70d3efbabdf108d89b52901b8dccd845314a8003952785cc7b7a70b88fe2587ebc6b3d73a1cb0441eb18cbf64c325aeb66866f432eabab4c28e99954759da70557c3aa0b81c5013acfd8ca9ae58ad92c96a2dd78cc59e92561c01b7dd7631c02ba21a508f1eb0b403864a65a9b58e5afb5956cfa22d818c23e62ab60ef29a6f18a2e8525019d09852047e60a9fde2a52fa91b7aed3bbe54aa22c1f7e1858534036cce5cf3609054b03b7c3b70a7c99235f563c64d2b26808a010692ece7baab0c865573cae740b6c9bcc4b0c3c707237435724646cc7358ee72649a7e5561b9779870011a60e01 -generate_ring_signature a1cae1149c356e23100ecd1987c21504dfd7eb93e828bd3e67010b2618b67d12 41ef904648e97ded7bb6cdb675c0eb7c3e265920e0ed3a43f6d9d52b9cac4950 4 8af0cd069e8ed1cd5b5210d4f3b1234fba3701a51f71223a642afc1ab7c2ccf8 68a106230168fb6b767c70b9bc50adae625609a4deaa23607eb431a125d62acd 7f77b78b984a927a28d64f19feb63bab35f545c419003dc6804fcbbd0a6a7d89 02e2ec9fbcd3da1d36c4426320a0ae5dded7b03e360e801c842d6da1479119fe 9bc45f2111dce6e47ce84bce74bdb15374994ccfdcc33c497c8c6d02621cc402 2 156843c218f3860a2769c3464e5053d2664fb392d5351306d09aa7cfeb206e0e4f4ad7d4bef3823a354070e62040ef3ac0b7d86be55d5b933679e736429a3009ab5793bba0a835d02b34d01fd957c9c43c6093452473eb206a3ab5f024fb9804f07365c0251c9c2e0071105902d7a75302937fec8dbbbb99de36294c7a43df076dbaca74d71583232392c221d96262552781c1777e886394ceb3e35e7fa58c0f7c4f1df0b90252dcc5b18f31cce4a8c9a51a7454f740911e3b110be4bcfb380e378645af31d907fb7bf01ab538619589b7ee188ec735927b77c95dcf54252903d75b00caa3c6ffdecafd83553c69ae23a27add068b6627be48b2207b72836a09 -generate_ring_signature a4a40107ca42f136d1a311b9dba01b70ab6e9b488c9d4bb1f82137a8de045b0a afaa82207be08f51be6d9e58d2029c812fca1215e9a4c9ce7614fa9b1ead0e33 1 21eb68d3bc8a11f481c4dd1aa33a7eb137bc0266faa99ad02035de1940e96ba9 4fbd33282caac0fb0207af80c006ae9da1993618bf7f2eff764426fd0fe7f204 0 c776d9fd947af4adf0b26a6d79bd13db8f5d5e99f907bad4a5d5bb4835e2850c74b54d432f6c7ec4290c92621e3944c49e0326df1c17b00e7b4672352623c406 -generate_ring_signature 856142e92819b975d12e0fe26c75ae88da078d345246b985f3d2ee7b2c2f7c5f a7869efbf3517f7b3217cc10c8d34aa79dffcbcc332863aeb9dab30c14f2c216 4 c2fe2e312d4d1b483105304f0790bd832bc2bb194157b215d265ff8f1fe5688b b6db213bdac6431e7a58bcd7bcad322ac7dc04a2ac609b2e2c1942aaa2acbe7b a9a365b5196b89e4ead8efe752dec4900446c49a93fcaf75c6e01d6155adaab7 ecbc8dbe05d49b8c9ab8538a959747d3c965abe9bcdb090829e109aa9af410bc 4ab503c0deb81e5867cb8acfe683c713eecdeb27bb51b5cabe78fc3d287ad507 3 7b5a58320ae655b07094aac9330722109c0426f447e1be423e6bd617870dd70241375c864bcbbd336a207d6957abfb655a767e3f20635146560c97f2a3b2340ac91510e5a98ac51e9cfe14a78023dcccffb780a60f5f040bc7e8a6cdbad395054d68c25752b29c858f5f67b5ce2880fa150b3633a58bf9461b7de01cab220e02fe5cdd8ff75ed67b718486e0a3406045f890854f652adabc70cb1e166430f90b91ba02283279d577fb16a183a7f843d53ce20ae8325837f8a93114a260e97b0383325acd1bc9bdd2760fd9ca0baac82f7fec59bb3268a14e3b1c1bf0ed04640ead596f59f97992e63d6455a5e4eed92043bd71af938b640af6c68b0ba1c81800 -generate_ring_signature d43a0443d5c3325225fa5986febb44adbda0422c3198fefc5c6f004c12eac890 48a03abbc676e7c1e6595e7871d3e47c65756783745b6c83beb3966c83b24426 7 54c73df736d858d956c6ecc78f71bfa621f1668529798aae794a2ebcc0cfe8a3 48f1b704c8549362e4bd701890d49bbc96f029f5b51740cd9af23be3c8e330f8 34d8123aa5765ff6fab0fc962b525c8ae0bce152a59c8336155f62b5f8261ca3 82f6e242df725fa43241d94c88c371eff160c9bca40e27c1ccdfd5465f8d1195 75ea08cb3c88c0dff75d744fe587524a5b3cb012c2f4e7299673dd24a07cd85d 8a3935e33e50f5f9ff79a529f18ed5c88340893bb688622625bf7007e15d5fe5 3261bc8eac8e3f468997a2bcded49c910fdf5b97056bdb5e81535665a23cd13f 55cd53ed388daa9179037a611a241494ad182883ba0fa337a9d6608a6f554309 2 11be034c9a2239d491decfecd34352b7fb370cd2c793302e7e096816cd75b705de97e726a7b1705bcdf6570b51f57f057c0e7c0d726982743626821a5e13d90c6781536d18d4d951ef72057e33ef5630bfcd0ebb97c1411be638b8334e8fae027257dbac258eeceb8b9ac2af58e078cf9de5511cf879346eec992afec6aa100867a6d841dcdd1bee994d7d46e0f12ce84cf3cc4d96c0e022d6fe920e456b640408b30f1754c6ddbaf25c07b53461f1389500b7f9e6a6804065bd8b705683970abe2decff602b629e74ef51d3d320fdc749cc6a69bc0bbd36107a0181fff7400cd0e9372d7b1fda95ca7034d86e2a8089f9b4efb2c46603257d238cf1131edc01c26a988bd80f5761da723ef71f5bfcf62a988cd82f131b83afb41f9575c1f50169c6a5dbf382e308388837ff6e5c69c8b34ecff7cc73b3bf0e37cd256791a505d57b8785b35f5bd1a61a012f883f6b540bba2a966ef744f86749fcfa6dd0b40f4b0c56638659e2d442342a9871147e29c6a819905d8ed723af3563aec2a7c3013190a442ef1c539bae09bfa5db942ab699ab06ee670afdd882ef9961058ebd0a5098a7f0c4764ebce980a6f13dadb1fffb8960db99a0d02a21d1f6ad967c1a08 -generate_ring_signature b2a6d43314f32e184b6224f1d237368213d205e110a06eab248d8ce14dc66b35 589f05748c0e2fe74f2d93e8bf91c6dd062456acad1a8c440151142a36e37366 5 d1015e1ee08e69977d25d7e23692136b840a702afaa2ca04c7a1ff3d844ebcd8 bbde63dda3a37b486f0f12757b2e5a560d47a041f4bbba732ecf55846ef97288 839a000be768e6908d46ee701b19eebc912e346f98cc523c2d8ae63caf9c6c57 10191593db747ecf070bf88ee12a5f8c16f8ef506c6f5caac4fc0e0d0dd2b6d3 446c82636100dabcd3fd93bd36eecd42dc10ded9be9b37fd1c868ded993a8bd0 c188a0b57f2c52f5ab7ad0e98f85d05d317a631d20a45f2ff8d65e4dd9d00f09 4 b0a6293bea8e9755d2960748bc6dfdf76495ac6de8d8b0bd6d0d5d7d96b2ae0cb0ce07dbc3621f08fbc32281a54e8dc5767dd64d2a0187f5afc87c8981a3480b424f9107e16e479846332f28be65e107e567ec9606542d0b42fc3b2da81b6c0280d8c2a6ef162dc3dccec0e3fbe863306160a7d2212094c9e5e734cf694b6608500ce607c79c51770432541141d5d62ae15fb0a2b5ea912b14e825b225a43302de6b5178edc9b45ba932a84358fa0becdb5efc3b93bb82ed6cbb80e18a3eab0cfd4d9ff774895fe4fa667910fb72ed966103114b8dbc10bf3c38433332c51f050edd1ca753b6d66c6682ee8a81b585c6d861836475aa2f55d817143049723b0aeb09c07f3120d59dae206e474dd9b88a84be8e3672dbc9dade795166988bf40e6e69514a71b3b617d5e742142debcaa94452f054291b1d0646ac9a3f0e4de30e -generate_ring_signature 2cc6d7e8e09924db18a5cb8454d9d4651e68b7c40c10f2124c4178819c166731 8a63ba236ee52e329eb0cec80e8281fec17a4553aacee39d386e7023e8c49f1f 211 402a41ca99cd815d081c2481ea92da40524a3c0d1115380d080643fa3d9b0261 c643929caa547433cd00d1e0cd02b77e37f5de0bd28fd21018040431e47ceac9 989a1efbfa052314e3f222b0d6a1870ad23a7759208790c4fe0dfe84f9a682dd 28478f7385c01c31b55f667e04f71eaafc7615d65f818155e4da35a160cc4d85 94a4beeecbef6b573e9fd9b3e959fe53b47de4d0da7b9030066e8ffbb31357da 090eb96dfb16bdeed9a02046eff14c0b3da54c8d93d4708d336ad86355ba116f 7b77baa8eac66b27d8c3a7b0156fcd94d958be2551351438d075b91b63c33a53 a58ed775d98bc67e07945599536d3d2b4cc06cc37e4f230396218bfae0fb1c44 29320ad589c514098a8ec72b9349f0940737f2bfa36e9330f2d42c00a0120386 3a9a27201c7bb4bccef5dc6806d040c30a8230d34eec0d685a6c4e40751f49a0 13f2730c4bf1c2b5a9d35cbefc51d4f74de8ca3271033c7c4a9d0944dde08c1f 73872a978fb3ec59c4f7f86f5c008cd9ee9f54736b180ec968930983176bb464 a657bf78310f970b68ee552e2bda5ebe5da601b5245d2454a30c0c276ec18efc 5ec07e85ab34bcac5d5172a700868c29b9ccbfaad9858276df0f2eb75e142996 9af36317b0c92e94bf9f88b3cb32eac5e2022d67c5843fd761328eb8d2f85783 65631374959a3b4b0fa5538d510a13edd1f616c1a7b304f1916004695dea5eb2 3ec33d71054ad6db9e4c10680d2dd4dccaa408e8796ad3f04f63e6fb6c9436c5 eb7fcde5107a6e168a6bc7bfd51604c60aacf0ffbc41c4ee9f004eec92a441d5 4d6264610c118362726519f09cc02b1dadc926ad5d3a1d334d3203c42a7eaa9f 0053001c4a77bbf3aebd3956aac9b8420bc67d84947128c32d651d78634f2890 98c403100bc3d1135388a8899718c67ae72d86fbe32bf4ad6430eb0e5a871f59 1f0f066060620f72fc27072fb04d111d7890db94da780753c565114b28fc61d6 c7bc71f7658a142c8c2f32112d5f2fa2bda5140632b577c4ded16dc0e0324bc0 e1d43f558301785c73f060139a114b59ba8260645d3a42256fa1554b50d7d1e2 92b26634220d7b331cf050d947fc8610d91f129412b01c29969001e7eaa9b770 a6db9435005617130ddcfbacd5f432cd688054c7bbd5d6c74e35a6b87b36fcbb 8fab60e3f2b22ee77a896e2f244f683e0215baa6c22124227548473e8226ea42 231600af4603f92dceee9d17187e0218993f6f60a91205a361a63a1edd23f31d 41065d0f4a7c7bc1e0e1921ae8db77c6e8bc58ed6eea0cfcbcd8ed75b7a657e9 83db2a59e355af551f2385866b1479993bbf3297877032f14a53893db3c0eacc 1f286383aa01569fb3cee63a35dd32f41c575261cb68afbbb832b3c4eb9135d8 14157b52262d16b5b2b03e0bfb051211b111776d2a777872dfae2a1f3dfbf780 8b499e0ded8c18e93b012b857352ca03f7c53d19a7b662b45bd6f0ce0fea230f be7c42646fbf9d17dc2c09924595162813a1cd92b41ca029465240f105ff498d 5bcc54cfc168b31f99d15c2e23cbbb33889107430b86dbedae47393121c7e575 9115043833cbe33d775d09472a193354f00cca070b86e2f2c55a3c587ecdefaf 62dae9a21077417d38636f9766d2b23e8f31c11d371b941600a68123a0e5852b 2f880dd8a7785fb4f6584ca858b7a9df171393eb1dbb997e049c3c74bd5c28ad 36f51e55611ed2b0ad671bc3b5a67dc32e9424baa52b7dcfa886fd16e32a4a92 cb88c24731f1c01341bb16711cccf0262711c5d538b5ee3d91e618a0aa94ae57 774ee3b25c731b1563ab1a4023808d1083f4d019438eae312a071e0963320b73 80f340dcd9f0edfa672adf1028fbe5e7933a5f412609062951c9c13984477c1f 846e5bb742c7d2c5e540a41b112dc6743c145a76dac105de39dfec0c03804934 6c3731202f8f8da0d540616f32fb1261187b0d5dbb8f4f86e36324adb50379c6 304c36bc24065978e078b24b16bf8811bece463f53926d8bf4cf14141a8accae 6e39e3ac724c929ed1fe8d2a1bba08b81b757f32ee119a07f8feb66c990cb484 2fe3718912375419fde8d55acc1e9f7ae8f3e8e4f7e8c253f180967ce9f6e277 1422514f9fd55ec628162935e2736205f7b19562e10f09a24b671fd3d363e807 ba7c20056d4c14ffd0f93dda7c39233f2b9f005dcff265a9ec774d54a16f21c6 4e024cfd9598522249b0940e0a532f8a2e460a06db24702c8503a7b88d8ba9a4 8bb41c4698f0c4e1afe52d5110012d704e063471ae568e4ec407fd6a7f97f179 1e5665d9af4551c32f5b41cb376f6ec70dea9a3b7c8568a8ce24c525ab1cc65b bd2af664fa7374d46ed3a2d6de4295b2b6d6d241af3df086abe5b9ee7765c8bf b598e6304c34c0a3158998db4877c8025e27de6ea53305f445f774a7235c60b2 7817004add781de4c2bb6afdcbcc88165a8620994a27e96e822c8b599cf9181d 402afa1c572a3e19f68ec8dd5c3f2ad4af9ec3dd0685019da41b208fe624dfd4 e1ff43e5787e2832904d4f55ccc3f24a896a673b5841cfb82590ca8889074818 2b8afb146c7f80094e3201010ff0f6c0503d4c916219e7f08523b6eaabac0627 671fac59f003840250f4070a05f89d73abbfefa4f105bfd8f85f377db6409f70 bd10a4554ebcf405fad8a7371ea2aff0589d0c554e83dda524a04663a7d3d694 81c03045a55f1c812b2280e0cb4fb6e122466d47ec60e10975eca58ab9a13d1b 1b3dae6b495af9875c3705bf1ea6695e7bdaf39c20947e061eaeb820026e9920 d89c734cb3226910d0ce056da4cae31648f5f7689f4879cb440afe2b783cc92d dfe58bd956769ab6129bc2966895f3eecf98d596a15b90dce4d41fda89f51908 90b7690e81a72de5b64b94be37faf4cf2e053c0560786cd3832ac0a887b6c2a0 97f5440f4fd3fe8928b1da92089fa2f2a2cfa22ee836e84bfe6f0f0ac2c0b639 78733374ee6577eb4aa1fe1dbe30fc49a26ca6709515f9fb9ca30ecec256e525 0cc4bceca6c04f76c8cb0579cf41869b523ed6dcbd0a7cca939040b113c5ecb1 a77eb08d2f61991f1c5d2d5c17ecff62b6f355f2c184586458aaf6f8fe1b0c47 e904d9ccc3b5d0727786b623997e85477572f374a157dea7a2ec15ac0b8cbd5b 529a15c0030b5ef91a2b172d050ee093b6a008726a5f30b4ada8e51272c89e61 e484b6e14d164bde5bdf5e11ea0360f4b3751add6dcb86e0609060903db9508c 678782c7d1fec628538b8bb22e04867e1cd37b82fc3bd27af6376049edd52027 7b75224203be14b0d9648feb66944b90a538a38977dc9def8515fae3d3b65446 471cfb7f41e6716e40f1e43fd3bf60619bb61cfe9647bae7bdcf159c6c011b6f 9a9abab7e9ba931b3e07d8b5f2a6b367e83b7fd27b876efdc15ab64c56bb3e99 3907d8e20f2cbdcb064d0f0b5a9cc39869aca703fea645e487ae7569fca87229 46a2823791014ddc54c9d56e45f469871e4f14f33443f7dc337d5910beb1aa19 b30317411d74a37484df3768877d0e92b5522e5622cdeb19db037688633a764c d63f7433d699967d4dea7f0353aa6d83a1db6688bcf05eac535d806f2861136b 05e06df4e9ab60de679307e3b12231ef37c0a7e8bdcdbeb792a140e0236486ad 543583fa163484a44a43267e2ba34db90e9ed2cc570b7a25c0e45b22b9ca1eac ca42a5c54f83e796076e644a436af8565132ddc1d075b95ed355c7f2b0b556b3 f3f5efe8d35449192c8795d2cb1059b720d0e1044a3ca69b1f57962fc819d56a 89bfa2c9258aa739d4fdbc65fd3b6f0a544d9cf4b22de25be25b036e9d9ab2a2 092f55c0c3bc3a275c4ac457c0b15d676c8d5b2a072472b624f1874fbef6c372 11595707e8ea5783c592db350724d7463f8cc141fcbc60a9863ff890d783131e a381570de73bc10fb7f4b4ea3876d21bde840c0cd5aaed2c2521cebbff77d258 c629d6bfd1ea0dcdd46444c5a560a4f386735c7e1dfba644e119da62637e6edf 4605ed8bc6ad9c8734a4f9fa61964edb5b73f1746b568a3525b088b7e4e28738 d2bd09313210ff4eb285b2c1171af27b1f7d3a14cdd78da5d36d5df33821097e ddecb9ee20cc01e3e00884943e5b284088589501ac73c033648430b6403bb0bd f983fa409d58a694b709658f79f488051a5a3627a1f5d3d8ab4f29b88ec85591 11b78bd153835d415546c093859342a069c2f9179fd77487ddd724441f8a1d00 a1be919083bb6e1f0e9ec941588f3ff0cc80bef966f32c1e57fe51f9d740ece1 c5c5a8d4f9a3c212c8ab2cb9bea0fe4a2d1b28ef78d0d92948ae2bc58d879924 2ba7ee31adfff772224a6915cc1026733d9750ed78b0a6003090d01b5e4800c7 f31856d475a6d9e542a8c52940e65ae76aaf239c58c7af445011118479ba2e97 d8ee080399b781b97e9e320be1efed8826ab3332ccdac22c412b96b587e49f3e 046b156f6016b3e8c4e28ec46e6a894a14bd085fb98d272f954646aa6a68cd8f 67da4100ca141d0b335ce9aa673c8ccb1c241d54e8e2d33cefd70fceb5b1d25f 93bf5b1c7a6ea142589637ede192df057328a7ff99325e8d143f5933e0e8d094 2013b7002841ce5b4c162f543ce204e55ca37c290241703cd444638b7e1956e6 e1fb9f7f35c2ce2f064db2652a494fc4132d67f1435d2c422651282e2d5936be 6b84444a3fc1f4ed71f07fe9807aeedc3d765e86a308cbddb549e41a07637540 6922d129dffb6f2e7e05e25d67a675deacf4a3eb7591cb8f411b4bb02fb352fd 807fa83b273eca0d0cdc181b9839f806164eac0da439354f27bd4c999fa3e112 ddea38959723583da1265d5bbbc23fd21d95912ab44ab7bdd388b8f816d7c950 d31e177c298fe21d24cda414ce09ab970fe07a16247afc1931831858f682913f bf212d78a9d4b8141c0e1e03b553c0be1bdfeb5b17cf2b9b109a1dd2f08fdc19 07bbe8caa6552571cd4fd46f165a89c5eca829e04ababadc29fc862625ef3415 43339e682fea6b57b40f7c199386f392488077429cb491f0380d649a87324411 46c1cd36c1efec4e065c8e64700fa8e225fdee7e7b5d0455184acb459785e924 09ea83b1d7d5a7b580b54e8e95f3204374bae8ed92e1271a8573d9d72baab4a1 8a7700e99533fcbbf456c9b22d519a25f1f5bc3099c71aabefee801277d0c19d 909b328ce25f2e5f270ed63fe45b43d242c03c3f74f3ff6001978ebeb418cc61 f8e3cf614e7bbee6985e1d5604dba2374630bbdb9651e91dcdad5ab53409630d d6052da08cf10157fb2a2e3b44c85cf76df325fc919c588086972c6bbfe193af 9a3b931fd12fd1f0d68fd76d68e44ac2de9f0d82fa476f50f0dacafcbc38dbf6 f07f3e41a4f7f79500cebf1b351ac5cf8346b068ab1cbef602fd60136b20cb6b 15f00a7af5e20b918db78299f63ab9ff993ffd9b9c3e50d32b89d8d68f1cb6ea 0402a24ffea45621cb1916265651c80c570a55e8ef7aa8577afbe2b15f3560ab 0ca97cc9867b2edec0d46d9d0fd14071c4c904025fd0c5ceaa5b13391a7303c9 755f312086e70719cf72de5564ccdb28288ac1fea0f41611f5437754cbbb360c 4f865ab94b9747b5219e5b62f06c8acd434b8ba71f9a49656bafef74e0fbe000 eacabe372936f28985b08db8fb4c2f8c9b4a48c261d698e77ecae3c3232c69ba 66e7c6a7231b1d3fa2771babe3dbbd42f1d32cf5f80e8112dc65ac08ea630c05 826d230e93e779a6e5d9ba481cead54668b91f7934e5a47f6d2067356d88a432 38d8c90a333e211e35d18433fc8e97a2912a4481a31007dcc3cfd7d02d58e445 b89ce70d879d1748dc572e179124da2922b52a9561c4e5d4ef787e65c3092bc3 dcd03083e2edc956b86afac0b6b72eccf61a96c0b9b1d8c49ed5fead44c22164 0b3b6bc0b8dd2aa42ab1909e31585da82da55ace9c06a9075120bb1584c4e65a bd7ded926e2626afadb1ba4c6a6484b02fbb328965bee31607f24c1f11cf35bb 1b822414e321327b717a8b73a68759528b22454ae4f1efbbea4acf27553bc7b7 6fd47667514ba98fb485a526bfd3eabb680eec1e6f6ee9b0ea242f051dfcb62f 10eaafcf82fb0ed3f7ac45f994eebacf1794bc7b484c3bcf1d4385534a7ac8cd 2f874b03e03e634e4afd81c541ec6061d956fdffa59940360b6be44ba08baa39 415a29f704999c6a1d57de28f50b6c8a28aa3a45e0f090971f0350169cf10442 65cc454756fe9f0afcacd7ebf5ecef668966b8d0a689ee9f8a80a0d672662d93 ae853a35374a5f088e4811ce400ca24b7292209f5591fb286a840f811a1a3a4c 13fce6e2bbc79ebe247e21edb3221333aea417be828cbf7dec641711cd009c0e d0d1c7e0df37e6dff7fca30ed034b174b6395667162b7ec2f81922433700006b 11700c2375553b574770775e7ecf74d356024c6cdb73a12040f899b072febab7 9f2045dfcf371ec181d645514a7200019f6917be67e5b29b27a690fdf434720e 9211009b28ae0d38739fda0cbc46242b9c091dc864e3849463f7d5cb6eb4224e acf85b5927f99c616e7f0119c2635f9e56689f1cf771e94c29d2bc6af30e3b73 fd8889f59b8fdd47277bea541ef26a7861a890898a15fa55f19df6eaac438054 dc0db4e6550ec7cd5ce876416f54c3bc59447b83c02415fae918adea6df7a4f1 29f5752cd58eb91887c95c38cd3691a9b404c99c5022e3e644947c58a62405a5 90d99d090249b1fb30482224f3c6d3dbc285c8a2f35a417a1a4559479a5af592 81c3b1b54dce4e4bea68f2cf57d5d10c6eaa396eb66880590a9d68409ee96e3e 8e78d814062170b9da0f2df53876e50d658e8abf5175be44f1a847b25456adc8 5c8e9a46ff66124cb418455a16ae3b78eded90d8d0e6b87f69e0d2dae684553b bd3bd4c32d490375d0652524597c89eb2a8dcda40278e9e17eaa0895fcf3cd63 37d269d95abc4010b6dbb4a1f13b0081701a625fb496a554ef50fa790503a0ae 069716673161003f4f4e03e91e61e4c35307be3285e9b9f4f96fc87d9256a112 3bf47bfecfcb4f13b54c8a5388623355fff92f4e991b1439ef4bd2c04c25abd6 8322ec655d08bd2297a67c7d218f22caa679139679f03a0bab4c0c07e74f5a59 4c7ffce48eeb64c719286644bd4dc84a6ca8a09976cec4365ed8a9f1e52020c3 1e66c12636713e73ad0661b537ec450f53bef1f34c814f53c780068c3cee944b 3606237da7504cdb79140ae14dac5e9970763a3c9f4a7d74a3721c93c7e1431f 1fa3ee9449761613dd39dd33e274e6d86709e83d747edc926b115659fab77121 14c7b97c2462aa3bec7a691242367004906e2b2daeed42dd2ea8c0aac75dbed4 70c011309fac84d7b2e71fa0702b49b21fbfd75204605c5bacc0c74dc5e8dc33 189ee51670246ce6697a111207191553afd5580f11cdf6859746993754e7ff80 1131c9a3307f39ca82c6232e5852e4461d0ef37afdecfa26b5022a93a7e5abff 9b130da9fae53bc05605bd8b9b7a015713699c5be1f02227a9c74da3db31a018 2ae654a588871677c065d03dd1d1d4232e1fb67b191a32573926c62d70ac7b34 a1a6f173a6aebf9df3f322eeb425a596e71bfbd8b75148232328b593059397b9 deb8c6156bb9b1cdaddcdcd83b0286e746c59d2888dbfefcf77e5fe1cfcd72e3 d007cf856dad279f734fb20b0e5c97839ac2ed18a607f8581b43fc5b0dd076c8 990073943718cc401ddb885dd082049c0168da46cb6568a56a4b287c9d959fa1 dd5c9a93ea17ec7174a091e41d91f3a03666ebd3411e5ea74938ca33b05dc162 c182c5d4f1d8be0d7a5f1b6ee8d126b397fc5d7fb52dd58b0f42f9b1d65e5d55 7adaeb0744f933f647464c92a39bf738067f6e1b4b996b4f2919289f2fa5ce9a f9adbfd726b517b55912e28c83f19d182b8df969ad4b12347131fbd7e0a8a2ee 0073fe5086e6319f79cb9243f359519f00da4d3be8855fa79f4233f8a50c5874 a6c4da072b6da74e25de82ee19976195b8483fa6a054856792d89b1e1f68ee44 d5f638f1b8eaea5e2da06bb90bdd5466830f036e6093df3bf23d03145d315529 1483358755a29f66d238dde5e62a125d50f6d22ce820553e207172859000a96c 72e77707ee4d52fd0ccfa34c57ed5d2339a69f39e6aa6f371080a3d414e25505 b7e9e15b52f4402ca9fe210563a213e1dba8bd02d0da57440a03a98980550a80 4b1480c24c531e5bee169e7ce570d89ecb0f0c7b3d041fd93f04abd1ca8bdae5 682b6bfc350ad26c92b229abbf92aa82a3d1c789f124c63c166555f4f960f592 94e2a267bcc92476200aa4d5fd0b3d6fca8f2c6a2c69623a22931b8e9242ea40 8d09501afab7b3142a44a531971cdefe348be311412347c0ed1ff2be5bae723f c6a8695fdc9043bf1a698ee3e2df46bc211ece66e0a5215bc16c8db3c676ae26 fece9504781b2b510ce438143395d3ce8f6ed69dd1179b9615680746b4260b56 7e43c14f5b5b2dbe7e3b37f9726c32a04244507d09cc1056084afbe29b97ea99 feff4768795d67539c95ebd9a89f8482775f595d8ef3852a988ab14fe95f5227 e88d342f5edbee919220c56ce616a548f801da5186ecf00a3fae3ce4eb163fe8 bf8e39152682f122fdf93ec892eda4246911194534a15293badb5e09e3c110a3 6654a6a6b4a0f3111b9c1bc051403c3d0a519a5721718e370190d833f63a8b88 a05d4624f0b50a2d8cbd071ad862012b3b01e98b69969f9e5d17b7660396eb4c 4bc3d8c6921685728332e49f6f4ceecb659504966c1d3bc49e57a79422c1e35e 3c497afa0fefe3026683a39fb701024d7ba204a2077ec0daf03d6bb1ba9ec005 3034c04416c57bedd19c75e57f79e9330bca3456c43418e5a6bf002d969b425c 5aab8831da8f0c192081f901a62c8768ea530c96a3b249aa9cd0cc83d404eaf6 ec54beb7e480aa22d6fd6f0c785e45c7f2a191bafdfab0bb07d5b48f092d9c5f 9839537dc7da3a8141b41c159b91c769d0cf2ba6e68b94217c602ebdbfc6a599 ed86db43e4a4868efa115d887acf04af9da75527a4fcd3c6244b6c4d61be22cb 8c507a0484046c536423e8d9af4e35f74ab7d30e0f3d41378b95da880febd57e ffd1a7b87836064b4247e77e6d4e88e944ab3aeea5c6e069161227e0d540482a 038230489f2a54958eba36e8609ead2529e3932f8b89c4204fc21ad19920adc1 ef2b4e6713f76a01478aa2d01d3caa45467de753c810df2f9b61caf0a526b699 abbdf138313206132f30d3c3376318e3c2a1754d95ea64e14b22e60d8dd8af54 5d3dae9e4dd24dff168fcbe2b21ff0313df82a1180b7d7aa0cd4aa1d9584bb5f 1df861a9b7b185f0eaff1c005d7c2bb89fed2ebe0f32f478d2f345bd678b49b5 829a61bcd2a3ad8a8d419c5c8b7fc26f2d13428fd97b7c54d6f888255640762e e6fe97cc23d26246af4db30b1860072a4841915bea8e575ddc5d7739a6b96954 7e5dde760460e9b910e216540728b4ccfd67182c070c6ec2afff9d1bcd0674c9 cce6c582a6b4d913727ed464544472bf199cabdd81769de5009814887650660d 170 dc16da5a5116a904bcb12d8017b925983e300fd8fc52c628573fb3b6d6b10502ff8ac889755e86b3fa5b890221cb780d6305029835ba74cbb706b9ee41f05804ec6bc2e046f143d1b79f58bc958ec2aa195ee5a41a761000939f3c7fb7ace20d1bce9415616b1c347d153f3e51a37c29e5affcf342ec6fcd0dca0e2f0b632b000f17dfa8c28b31371ee415168c18ad5ca9313ff118e46340a95e64db68263c0ffc21b05e4b70f9da75ea2c19fe84e2ebb520fff9f26c689e4f7154fb2fe3b108432c6b8afa87fa15272d94ffbfcd39b302abc1d26cee8f905e9034e51e7aeb04c48650d0789a806c07794f5730c4a71ee54d013e4e6f11c95a11944841edac0849f01e65938083a57754ccc4e375a9fca93bd85265340dc97f2a8d2ffa1ed90bcff98a70c4939905e5028722a63a41608ed6a3128395247dae0c488597b1b50c7681f64d21732743d715a56ca4b7ad2dc27b10b24544988e91bd955244f8a601900db71e8de582799b8900431ad614a59314e4ace6c7acef64f0bc5ad5aedb086edf4052a2353fd7b401b9a4f5311fb8b5df48930f577ee76ac68a51a7847c024c6c4403f39eeb4797da1a90d9caf12c6910e2828ba57d917b523fccb7520803b9eaba033c2c474c54f42429d1029595b9958c4c65010c437911a37bc7dd8a0ef25b1948d5db071c33dc19d0cb1f30a64e1c0c60dd0d200984c8536641ae5800f6ad3b1dc4ddd5ef4b51926ea517b5faa4c6a3dd38515c937cc7c907c0d8f80926ff063bc0455296117c3e873585a87c32c6796ef206d16a27910219e3151807b5fde3002006e7e009b0e8e7c688e68735e8f01cffbc4557fe44660cfaa68301416a897f3c33f57f0aa7773cca2cf9fc90b127dd66f6be4594364e10f4c544005aeebe32666cc8a67d932b13654de698c01d2f250bca110f25ea6eb2f46082088fd76078e069c41b21aeea6f42943dcedbc8488dcce4cee66e8239dca073ad0b1c3c4982d61f66f6373ad60cd26e261d8cf72101db948f26eefbd610a630950d62545bc6eab23aea84f05e6f9f0a0db2a0ab82dc867873d50a72f5e9a208f00d5465ca5eebe6b364fcbe7089f24cb152facf99cda07794e380f2bbf38ce24e0f4de46bb8a498ea5c52bbbf93a96c11936f2d08ee1318cab6b6e080066c86600db9a5b7b44dcb99ed2d793bca8e63a8dce51fc67c60995c258c7a36db901f700006c9f87573551b14756f828a1a3a0ff118fcddd0028fd93952c42da82c7ff101078b95657051757b7852229d14b12f82983ae297a5b02de47c98e888dd1f830145f18e5f59313c1ef18aebeaeb5d02774d100d76ad80d4261495adac98354a07df5cab85dededdcf7afc3ef918f3dce6dc4542ce78d2da2cd6a79d9ddf1995013e3aa93c18affbf404cabb4dc0663d2b92977212868f836231143cd180a6a90decae67ad7050356b54eb418b1926eb433bb754734704f405bd7009b9ab47690d148e27e0ac492793ab3384780fc193d0fe59ad603844c2242216af14fb13a80a36128e56eef4be3a4ad07e8e3bca722db13c938a0485720bd5714d6c1c2d7e016f95039272ddb3438833bf1eccd3f7213779b35a43d8f05ba6de09d5ae9be50419063a02f6519fc6758bae1ebc8a2febfe0ca862e0ed48319302dc46e873e40965dcb62e02798e0ed48c4dd5680bb7f26ced30a234de001821f6a6fdb35232044ce91545bd425c3ca91e2d767eb766b610dc0121c8be0e0922f3b5cc0179b00cc71031fa6669983fe4ceeae69eb8fd084ad71aa5e4bb7f032438acbcb917ca0c09130ce0c898d145e4d6e49387506bf1f4d2c25f1b6bb51790a7938be556120a6a9f41c808b3c5b4a17ece40680531944d4d75ec67fb0d73240b3fc0357e0e0585c1e5a50b8313dce1725eadd8351b1f9d0c8e7b0bf684b1633cea607dc0ee049de43fa76ae180cc9933c73e1c9fd615005e46e87cb17b518538f5aa14c67e0e363fb69d3fca4c7e7d5f11e6b0042ce4c6bede2ded883075435ab7d253700f0275576d38e4d8a677ed0badfc97d8a4c9fdf0d89348c94a05a780ad1c50e54b078ff871191b64fde6f6c02929fe3a2d1b4a0fda8e20ecf70773e93ab399db70016e766d669628028347c75039ea62ef62082b18274104e886a3f57a06f2e4ed02425c1a1d6a0a8ff701325cdf7bf3901c4ed4685c6cff56e6d6ccaca9941a14086ca6d10903dd47037be31b6146c70b0e3dabc076d80939aa0b3d25fe5b41df0fd55478232081943b9969789fa48cd83fe700ba37fe7cb432463bf432fa929602e7c4dc59c90e111e1089007dcf1bedaf035743acd96e4d9f9fe645b754de3d0f5480ea5fbbbd530259ee10b8215716027e499aed4770b6787da3a06c35d0d00b616d0df4780630232cfbbeacb759459e23010908ace43a5fc5e4c4813c44f007ed903e3024655f0b75c37ec1adbf94cd7899462dde36f51cea9c9c284a19540e432fea18a6c14c6a2075cb2cf16b814dc4a801ced8d1393f03e777972de4f00f2c1d85babffd7b7d99148a7f0382e9daa9ab43e13f60adc8985d6dac7706040fbdfc4c16985f6f79f5cd116a4d2ed7f532091a0f6f1e42940b7301b8fea0990adb1cc43eb9c605953fa967f0aa1544406616cc22d11209bfcd20fbda5dd0d40578337147f44f880c7238ddcca4871778f7d4b61e69dfb354dfed3bc641fa730bf35759253616d01d5811b5bb42ace708a375c30085a485c85cde178594611a0b50918760eb2ee21530f0b4efdc20d9dc790d093bc3cb6b7d13e8fc36d64941094ce7397e27d9b0e90b945fe6d0b600e1577e14064ab03b4fee121b583e90ac0a246319c6259e5e9ccf7cef8d485700d9d8aff762ad3e1e2060a701fbdc881603afc45857f109468041d1398e74d347518feb0fb47b94c16eefe4e6887d6b0d08787a4ce4084b9f258ee719cd2677e59be988ed472da014f5c76ec41395988f0b96cb9a861f69d69e8f2901cd456498882fd36a7ad96ac4ff84a7082c031b010b4055ed550653f1a75b58e6bd436f2c1a9cdb16b2516821e3e448b40248f8c7068862c7d53bee2241a739b9e9c22774497ad1518402bc68823d453ef47d13e10e44be89e0b40774cf71536e308a82bfb56b1123b048f695119f41595f30c83b0a4917e9ddc713be603c5d785a7056e5ba65660adc1ec67c03d1f2326cc5247e0c33d0bc5f67f56c14bc96243704c3cc8e439dff59de8a14bcf987be3d1c5d990beb18a694538ccb27a9f5fce97df7f6788c6ab145b93578ac70a2c939c018720de7221f4098a4124555916a2c0ee0d0b2a4b228c01e5804a6dcdb839d33b4360f6dbb26ab99d977a5d8b09d52841e13f029d0efeeda3930af717d401ab89e5b048a3a8ac764f0131834ffe0e6bef2b53a28e2722a34961633bc0139fdf79e4b02024b05f0efe2d205835528d5514f436c68a9a8feefe5bb73145578ff2949840e8864dfb0fe259b47f4f51fc14be6a589f2645d553a6018f7d4f35b411b06ee081933a7f92ef1b8cba091b1272661306d04d07f08d3953518395567098302e402482f17849681729cd0c84fab6b982c254c4fa2f99a5d9037a0c5a0d8829f6b0716758b76374dea138efd430398c2a87940cbe60394a5502edcc044bd8bc7cb016ed3c2971fd437fa819b14a33ec71f5d6dea89d5b773de70fe00d60db88f7302ea5f6e75d718a82e7b6122639b083c48e3a32a8ee075d22e88d72f6902a0da04c28d9fc5d9e28fccb03a8187d8f5f36c6670ac5d2c88b041abc2700b4fdec30c34997fb2ac2f984a39881c5d70a8627ec9f59d03abd3614b8d476952f9edf50993e070d35a577971145efe2fa51e126e051b1c27a7cbdf6aaeb0c13341830d0d161da253e14125a366fdb6f61c4da22c900a2acf0454f21c64e20b856a5d010294d76ca628171b3fecba77cdc07816d6f507ddb513b0cc3ca5a5d15274cbf5044e34490c79e42c8f260bb57f2c23117b53781490d5e35c7d94b866f63d79580f67f50d5ada67c71cc69cb17679b30b034e2fbf8634127eb7698a636ca826360cf3322218156f34ba6a6492deabb8b757182bb9344d8f104bf3d36ebb99d4b30d4fe91d90504fe14411df2bc8267b3104531855af89201339e24069d50b4dee03b2917ad10493ea8a7c818e0964872d7781cdb1059d509cb5049dcf2f81b28a0deb111c8a376e700b77bfe10567cdd9eef9be07c2a2b2b17038a0bfa038d9120f3ee1f37ab79baaf3b7e6c015fa69486e3b7c8e5fb398a26dc680bb1e40e7530cf270ff718b8982d10040d329a191746312167a2681f528b3a896aaef9c20a10276d5c88cabf20e7fbee059c6db1217dae95a7d2aeb0e4bb232dbaceca79a65017d4318a29fb4c025041b0fb749355fa2f27627f2e039c8ed08f447f3d4195e04f6dcf4687d4efb7f286a2e091bbceae385fcb90f2a32343446190ad46f81100d25dcc373cd4c56b5650d1113f330e056049d3de8e5a6c2fd434de53bd806920a167368357c24471a735a9d0431d60d9e9e014a1e5b98635a50b6c2566ae16108e3c007338d02a0d14ae3eaf6fd87e0199ad8365d0b64c3930370671bc27a130993e832a12380159b500ab073337025193d2cf888183fa8b3603adc1dcb16db05ac20f6b801e248bba290bbdc4a481886973028379eb7e67aaaf9b6cb54e5e502e1fe9148cf29f79f4280c63b723dbe20de1468ca9fae1af9602afd66678550010cfeabd83c25db974065f9aa202e870b9e94ad1f0910af3edb81fcb734a79c073b6a53bb14be6b4fc03f088c1db4a853396d7ae686c5c7c8f08a9e7663f6e70966e1be77ad5e08a706fe1ad5c6532f946b01170888b5e2311f0852d27aa1480e436ef1e3963a893d206c2f2daedfee6923fa6bcff94c2fdc95e623ae43b31001ad69a1390b11c28f47e5e92835f41761033a8a661c1268472ed9c877aeab6302de9369acdce386b1e3d1a6b9bec567e0373e94709029815bdbf73382af453a063f56fe8ec42bec43aea3b70f50b82065889dca0e5902cb54d58fe04f3441dc035b2850644d64fe2aa5baf8a82391a5115733ffdd73d8746722b5bbf132007c04f7c8c2dbd90dc3f3339717bbb41402ad0af0368df73345e4e460f007e6f05105cb686c621e788048aef94f9d8ae9e68d84bce741bbb559b6963949e939b34508fd336e5ee7371e3ecb810753fe22df978b1c77e88b6bbc9d814dded9e933ec01db47882adaacb94582be13d279884182080fbfc39ce870216afdfd397674ac0f9907298e8a857a2c99d5694a7b16bd2e6a80d184dc8b8c6d5332e92a8a37d9041b788f322e7705663b2c640bdcc20b56faf23ea4276ab15b04a62a840d716008963060bfec45bfa9820edb70fd924b9c507f9af9603c11bf9dee35107bcf15065644157d8bf4dff7b27af7d7497dcd40370348a25e6fe6cde49d18281f76630be89a7b7916d96223cd63c07681a36308a9ff3438506d8a1d697e71540fa716077f1001990f8415aaf81aa20f82a0e720efcfe9599fe0cdb26809d8527076370151c47f46817761c72bb51e75694ece9844deba768a050bc1d5d6f1fcac505b08d38adacac53f9f3f6ba3211a3c8aeeb2a206ce08c23acb0efa84ab20aba8d5062b4a89c9b96c0d89fd793498986541ba3f95c0f04e45d56bb3f401450fa6e90f8b12a7256bdd1bc6f36d987e1f5ade07f0b1c1db88d12445f919ac3302bc950a3feda96e356b1294d9306f07cf4271a1b7f60a084d8f4b2e504b40443a313403342e8d346d93d907ae2cd7f8c62d03a3ea9d703e46361f17fbca99a4420ec5058d45c5496ae5226137eaf1a070994e76ed054e4994029bd7068672b7eac5d10fa4de700c96e21059880d9cab23d070f1eb5a96f965c380650d02ab2f9414d109b3fbc358d5595843eb589dadac90d64c14ebe836965fd461a55c20edec139a0207e5de217f777e46f90652e4178b23229074421acf9f0090e31f70c2de104d04cfe44d183b201e15550ccc26590835f79390e0e0a7c5ac619a77bb4410c6810232107d3ecabf757c89f779aeefa1a447a72356417eddcf4673d9a2ec6a240a09721ed2889cf8abe225bfe32758d1175a6f271a6a1efc387a282fb02227dfc70fd9dfed06c60fbb7c27e86ff7bdfc6a592f61da3b21a1746a2252aa00c75fef0854c082d73161d41bebd59e3ea250bd435e2d045391ea424aed2c8dc5af0912011cb9e0e321fb684abf520522367e61a0eea69b8c3d1e164fd794d711763f930a1db3ed4ad52fcba1eb2db7d0a8177da0bda01d83dc6c4603c2e7a7b772e8e50decbaf8a41f6e2df81c9aa71810ad441ea7ffc58672508a8d839d80dfcff70700e422e2f9a14d6db6e338ec1b89acb196c14b9aa846b9b088679e75e9cd04ba04009abf05f485be206da1acafdd8337311729a83ea8556f18e428d7602e407f0fa944266371a3de26bb934930e0f0fedafbadb41eb2fcf83a5f7a27ea45058d015bfe21b278746a028a0461c4bb1049642923de376338b8551cf38df775386e085c349e4219cd432e351fdbca584272ba7e98313ecec5fe816fa9a8066411ff0c1722dad9401191bfc636e943b1bfcfd2780fd8ce8136821ab5b48eaa2337920045618ea605b7e39f89fcd943b2b6c909a172a0684a38552a02acb35f2811e408560635a4884124e8c60226fd696ff782eb5ea6633059294de8b2af706a4b7e03f9ec5df629f6e68820fb237ec013a2432df1f551580234ca556e16c34759560136c3445048f8fbe46b56d396105575cecacced353ea5b509f19fc74ba326cb09cf69e5e69d6d080003c06c3e42fb70793c09c364ba2fd757d7b417e2cc4b3c0e7d0575560e804578ca0e6682e1cbf638c68b11ba1742941ed6fe1349044ebb01de30b0612566848b6be475d814c4913b62b0f77190abc73c13533869e981640fb3454f9ab3a817c31fde69ae5f8c089b038e3f70fbc66b35e9f9e89cef0a190a07f0b100c6243dc5df2b977251caa393137a92004f47ee7b49c87397ca278f00624f51204a2f7edb1ee3aa7ede5830d3648d857756eaee7b78097c9a42fd0807d7323e1683f6610e542783eaea8f6126088e30a5fdbd7e703cc07cad5b332101e5f235080e2cd090d464dfefa56c3f4ca37a6456db696bfcd39d0d99cfcdc70f8dd00156ffb109173999fd98277abd6e66315f19a3747dd90106e3e0747e21054fd6325b5357938b9d1a54768b5c140c99deff86ab65699e75c7b38aae83e103655feb442370969d75a9a21ceff557a94be36214c2637004b4eb31704b6fe90a98c9c9f0512b1d47b0b86528f7206e565e9c2cdf5493f891edd2b087af71e10f49f9f0d2653cbdbe043b4bc302a2baec68950708a26067a6cefc1ec2068601082564d01ea0346e6798e920fddfe54f8a33fc7a7fb1a9e9983d98e47d3dc44b05907e8a61ec1695f57823c69ea81abb4143ca0f4caf0c79a05ad17c935269890be8a6cd111b508544d58b1ec615d35e6ae188fb762daf89a815d74a2627fff90a64eec8bc196dd840b7761286f84e6e0fd34e22f0e97628ffed9469ff5315ed03f126ec43c175467b066fc96de9508511842cd58627856cb909d3f7f49451170d4ea22ff625a8db5546f558e33d52e9bb9bbc904eaceb00480e655dc11488170621ad81250336b6737dd4f1ca0d2fd35e6498e44efc52fd5848b2562082b58c0cfabc57316e6d43a3d484a5b10c7c508ec32c422d19eb51f9b22b1817629d9e0bc3d8cc19923c2ce3df81286e946a24472ab6de8603ab3913e4ce7268ba4028032b58335b7974b07f7c8d76dcd88a83a6c60c25c4e07d67cddcbb0a277e3f950ad872dae081d67a065ad4b1908f038db7e7f0b462757ac883f69c32f1f0cfae054795cb0d60b4946bbcdd815b7c02aa467ba8546b20758459af95656f4032920a95499d175c4205106322f05985bc09eb52b8b6ab80a31e79ff8658d548781903ae7b678de76bf3fc6f0ff48deaa1f4a9cb098b1a10109610646ccd9b3a40eb00ee73a5af4dc48aad64132796783d7decb23b97238f1a4d649a623a9228c65b03dc4df33685220b140ac3bbfdbb29e9c5b1006e3c97a45e434a7d210c1c63d90c626b9434a89cbd5aa4fb23e1def7d4858cb2d1793ffa2650c6f7d66bd6bfa90e07b3aa087ac073d513ada3ccc2d9e0f539b1a5dacd2612c8540f1aa59f03650c8a2f0db4b47b83a8ac27c4ea1938dfe3899cae2ae2511549514bc646f37a5c05d2c964e0108bdbcee4b8528e25e2f0fadee132d62b3931da503ce4a7fba3b7098bb132a2e6fdfad2133fc48347b79b3b05a1d46130f31644bf5347cb2635650f6a048b1051d42185d32a546f541fb78999988d043edaf5f4d09cb7562978770a99eca0712d1f69d344961960b9f6f7d958ded9fa8b27869d785bfa25aa4e9f06539b47e8271e5748acd7947aa0dedd2baad63b19517b9506696a57464e2561069cad0c5a0e6962ee713e1f097127f91a2dcca289b83b61d0e5e60e3b3925770069b390036ec154f5d682d51649826a8c44bab01f9305ccc221741b0e319d340505ab067161c06f8f6a6bbb5b5855a3025b4e1a41fb84d1f53f0f0cf28547f2057b5515035c05351cba147354723c47872a1476792ed879a107393a18df48d30d3a1d28efa83e0a7946f6cab63e98c0334ae8dea23abf016f7ff7a10fd8568307d698cef3ff2c4c7d8ed61e84f23f77dc8a4308c772cf364e6737b75f36fc640ae13d6d8d3a31dfdaa18bf02697750afa4f87e9466ed8d54369fcdf1a427d0702b5d6ed185a49154228a46b94832d524e68eb8ffb46416182623d978e75c48e02dc52369767364c568d845752a7897c25d74c860dec4d5eaa48b46d8aa407890692e8148cc91e33d09e626cf4fb2f854fb118f82c34a4b1c24fd8cb02f75a0104b0d47dd5b485cb8d45f9a77696ba2e88b464e3cd5066bd4295671dba46f9e3007b5c464cec797135201a28869413e63422414266f65f2042f983c16ae649960b4f020bc8c9e23ad69458d0186bff6c54872a9d34f5778a206239bdfecc501703e71cb0d69665d8679fde0ebf6ec433f04dfbf0123cdef9721282bce9a41fef07575709fd624d62c2c9938fb850c04165c6fddae47f0ff84e1cf46ae94cc702032f124ec45c295659511386e3ff94d7e341be7166d0375d212934ff74a80b55044b1aa461611083f86a03b273ad96a68d3d0a882e128835ed44d0319b916a5b0f98596c3f379baef2f338c63499d925ef61fb44968158f4108380fb211ae37e05acf36aabecdb8f91c029314b625731b4053e5508d13958bdfde0f0e6a363790783de4cfca065e267e3bd64dfdcb9dcf9571cc2490be6d29e730d9f518a18390a5c47a2a48d860b622b0a128d83cf38b9a92344ef0a4d7175a14135febe3de20326a97282e74404a4d24882c4a427c7b244c3ca6f0812b0cae67d1ff93ba7af092403e416f2f2c8dc41422f1ebafba110f7139b15b1ed7c38ad84da9e4437100c6d27777d353214a346e0ab63a6527356602bbff02d8f34ce21666c4c95003b04ed157b503cc479010ec5bd06686c0a6d877407b0e0cfb00d1155afedcb2a56049df670bb571fad17a4862eda1a5d604d3a907ef09b11cd371b3b920c376e2d0d5a0e922501df6b0b7a9df3de564519dca7b4d50da497112425438ffc4a9771073a9bf9b4c5c073b893d9519252b7deb3993260cc06391194f094db0175550108e3f3819731f7e24e165ce7b4725ed83da6f2ec9b549353c0fff0c5d53ddd2808a0114beb03d87279f28bb955090d7d2201d921c7cc37ccc269b3ce328c10f60b3c9983252f6d93d760ab1f9bb200f68488b6d01c2571c4a90aa7475614ca400dd9fa343707bbc2c5cd0848324ed7197c992bc3a5523e13d58cea30a2d3bd95061100bae37bd31847c7cd663f0eb2fc02239ddc38993fdb890465cf8e10223f0fac757efe09aec4fb6bb5be35dd72e2f6991ba948b8cf0cd1fcdb26e1b0ba64076d789a894581d62e7c393219103bbd5f70c6a646b46c63bdf001912dfb9dcb0a5b695ed08ae4eefef344d332cb218647870e15458cb4dfc154210c1e564e67008147c272289c9e9d7625dce91db3e0c490a9ac24effb490d1680565ac159e70425ce98f4b759f07f7c83c82a4537a585b2eaf7d133c50c952498a4bff63a350754ea472002307f370f923d868dd7e20e58688a95e930a205d2951af84e2b690ca799f3e8ba9d7b9bef8caaaa972d31810fb22c826cccd8d6d150bebc21fe3d09914e03fdeb8960e93fb86447a97edb92b3728fda9e305b5b3e7f46039a4d5207d24f2fe91530c35c485700229aee830539e72cc3dc307b57b685db598c0e5f00f2a2d97592f90f2e6803089e8ed88b21a7e465c49aaf3d75ab1e9e8897c140032bd1749e03931698f7a4ebfde81556e8e01e2c6df7bd145e4b65581b39d45c0f3aee313a9562c16bbd77123200f11c8d7a273d07c476049fc4da94044a095d0ed640bf9a3a40ac7cb140b0b5e07723fc848593a916273094291a893bd07d7506af6bd136f1afb2fd3edd3d06d6ffb558a302d91b7622211506d5377bbe3b36029a7be6bfd2a5dd05bda715549b9deab61a01a64a1e39d16688a7cbab56c39a08482235039094991a776b67c022f56579e8d14a2b9ac09caa4439928ad96df101c98edd6aa39a9f1104534d5f607fa1634de51197061365878880760d7d4f94088ba8d52cac9bf76beb10be0c2cee1f8eafa7b61634d12ad11a90a4e7c0a68c0bbda5fa3e3434d63012206bc75ed5a6f0b0778e9af2780168177775ba7adc8a0dac7925913a7a02068b5828a9bbedcdf0316cbdd9c95e7b79196119b6f8a5c90fea4fca0d32243a9176b1bee9ef81beec710115b4392078c3c9fdbb1ea41aff09a41757f3b84c0c8a01c066daee0209b942fceb024e09e63117ba31a96c49f4070a59983bc227e7bd094765be7018e914940338141cc78a6eaf394ce13f3302020f44baeadc0db14720e0a7234962ad8a0aa3157393705a1d085360f6f45a0005e5f06e8de2aa9725073a254df82c71fbb0ab5e27e74fe46b8cd4d99c89a2e701d4fce819f5ea13ef093116bb076fd5472c307ec48cacf8e933c8341d9320460985bb6f7e01e14d927a99b8b240bab44408e456f7fbf0225f37efbce697e67b06af132499029adffbb929e9bbd2d2813f4b8fa1628624a93ab78d7201174a49055da384aa5ddbb22e9b1dcb82841562f0ab14562d2313e2798c4da90dd209eb0f9370f961e5480813ebeae5b0da068ec5c25242831d8eec5009bfd724d7740202a50f2c208dc1c67549faf65f3df0c7548695a42132dbabce2fe8f163bbada90d7e47943a73ff8a3e8fc8582453d0e86b1cb63d4775b37622f994c6a0f9abbf0edf96b7ebc9905532110ff1f3c4f97cabb517a76e1d34b4d3e975e2a44821c802d0db21b33842cbbc9e46c4d0f22128a2502c60650fa78833a52ae59e466d130b7cfa13bce95d71289329a90fa3138da0d99a9b2e1a4e477279388e877ce06609747ab5e552d029a27f1e34fb78a48b4829e757cf98c7fb5c6e67e7df7644e50118dbf1a8c906e2322aadf0c4ee59382b8972978ab58b2b40b161244c3bef8f0b5810814106856099844acfbec7f7785fb5a68a46848e1ad6894f7dd8d6f8db0c450c811e1e873aa956af3f62851f90fbc9378fe9fb2cc0a0ab7a989de826bc07a885f3d14770437a11491dcd09e78b1eafc0ee0b8789f5d400fc32a8c713490171e328de264e842c0c6daa653f5c937b845fa4e8acf3b44f056d13b836b7bc0f6f02f7bad0338c29f270757d77216b183d86841ff1c7baaf7494777d6aabcc0d1f3f33f9b4dc06feccb0f440db99425f2004e18c73d44d74529b2b5e1ef28101e0acf008962ce84cd0d79b12063e80c89061d9cf536ba76b839b1ee4c36d8c0e4e51d511c2781b84091821b537b9c86718c5e1f06355bb1e2d9453941c900603446f3ee11bce4ce82b2a033a3f995d6ce9220abd025edc908c7476b18279e70cb9beaea739ef96e5a86256810800a5b635dae4106f0a6f39c9f0b87caa6a9709e6d36ece5bb78d40e82b6ec6e52825ea5be3cab5d36f27f0575f9adcd5dd0c0cae259f065202d60b08bede44cfbdcd342f4d7eb8d542b6848f88b34917204b0b77ab9258f32e3aa6b3230da915340796e8737af65914d8f8c57baa63f2eec10dde65170149897204c67edc954979f10445e029f60aeb34da44c0fd943e07920f54eaf59c9a43fecb9616388cff5ed3aee14a10f6547ce3a2422aeb0201328e00dbadad459c01ed0e9afafc177189b7b04178fd39e5108bb8e049f06ada69f301935ac8b2fb44af1e69c74e8896ad52e994e19a87a64588549d36fedd24daa90e899c3e3e581076c1f2a0dc6ea74cb73f36f661c1d3c1c7c31a8b6812d734a60fe8a24700bdb0d8f6771aed0a8e5dff98e53996f2df698f0b9064c0f567a8490ae6450bf2fa7803a6ea070e1de4ec1624b865e5462d9242d8c9ef352358a3590672e76f46c372f38343df9f8590dd87a784d5cf70981e070d4fb6183d1143290eec74cad483f5b9e61c8aa08835dd6e67f1e6ac48ce36930e56f1eb07aab5a80054cf3470eabd15245d9a9d935b3da12868a9a97a659887550392e6010e3dbb0f7d7a8e15864534b40ec9a7849456bcd70619f23cfa10258bc93f016a7a825008f3fdec04404d171a5747512d2e111e32bc24f616c12ac8a0a51771d1922e4e0228a48dc826cde52a3690b279ec5286e9b2397ea4bb65d49a4bb2f7648761960509c551d451d05e93f96dd1fa0213b8ae227e59149eccceef5f063ec0305f6e0905d2563ad1db3c462f1659e65fa9402d41436b9892eb06a71e6277064474b206c5db0146c8d4ead43df58183ad2c929740d03504c8cd1e99387cd1124b4acb03b01abe3379bd06c4170ad7c2932d6012ee18412059b0c0e99660665e72a86f0f2312bbed0aa3fe62640a508e1355af9e347382ae54e01614fa3edb777ebbd70d3e5df7a48f57a64f7708b41579f6028dd12050b32aaba8cfbcd66ccd6a8acc097d9a0c5ca5ea839bfcd0abf2619ed8b11367248838d80e94a1e00171dc32f2056fbb35280630a4a1972cdd723b8ed95f94d0e60abc2ab75471a8779d63a6310e38a9b7484a8d47eb8542d15365de6888a839794fdd0739867d63cbda4a98df0679abc6576130ecda7a889def4b5422512cfb53385bf47aaf9cde665759d85206f89d95c8d3ade142a881fe7133a5649dc3121e6cb5087cc42a89abdc8df4a60380938b5b153081d5349faa44f2ace083a44827ed0f52956e46cf5d36f9e33e096eb318962467d00f11c7c0a70432d63bfaecd5ee6959f4cf8e2130aa56670304d70804265f3414a7d21a888a78ada5c6f79aae1a19deae3a31096b0cddb0990bfad5b96359ca1eb1b19d7c2db7b4a8fd77f764142161b6ae36393319e97fd80f895484ce4d2f591fcb9f3d23926f98bb541a40c17017579789f113ef6bc3460e1fdd287bb22788a517a57d85239660951bd7a40013fdaa0ca713b714f31d8006406ab1ab332bf4512a3fc9f0fa2d800d73debde33b59a4ddd535d7c1d0ec4e0de08a264b813eb7db4f7e9a561817db038f787afb19b90fd045fcf1eaf5c1340a80991eaa65774478f0e3be856b29b1db73b8ee9fd8c168702961919f45626c0f0a9c55222f4334eef62f7445a2bd073b08489f5c40eb8362f9c8399415d4bc067170a89e6c0acc5f9ccb7daa19cd6d73bdc6741ecdf0e1fdea7ae01e69424e09cfe4aedc1ed5be23b7ea5f614ac9c01ef3e3a305e2a8628226ecc5b5279f830911080511c349f0d8572f194338196baf2debec3e0751a20c5cee5ee4e50b660d7d4e1a927d513a53b881d2663431cbc2ec24f5b535bad00aca37a83269864c019ebab1e85102cd80c5df286693cb08a63e4c9818a17ce8d1d27f5d1e9616b107b30dd33b95e5caf112d233d8e6759ba4b5d2e9677c3afa086678f767f785b00258c915bd27bfc5f9a3ab1124d9fe59ed4eaeb28d3792aa3e46889e858e2c6b0dae530dd60fe95154c618fb2c345b676d47fa5a17253cbf5aa18eaf4b20f5020acdf96f173157a1f053ce6b3aaeaec77ef924ceb1448a3a80956eb7bb8f0065061c056fed55bd35bc709c2fb2d56b79c3d78b0768e2132707ec0469a458d3600e8443c132f8530a25d2e1fb94991de2153a94a41aad751bf9539970e7bec20b07c888477d99bfde531086923c9c4c7d3d2e86afd22d6a26966b24eded3ec1de0dc0a913ad486d217fe1ffa7ec7cc76fdf236f62d101bf90874fcc9965f417b4073f9e088c1fb9f3681c366d3385078e53ad29bc38675f817259107fc789d20706407917dbbe9126d18c604250bfda018670dfef392d875d9395b20ed15f891709816b2ece0313dbbdde0fe9be206da5c88c27ea024881a59c4e6e2eec6094d60f347ccdbcd866acdd58c687abfcc04760a747f4991aaa6fd3e2b4eb648507cd06dee4fcb3be65e17f340d1c613cd90b10a91251e219343eb4fd26b2ce645aed02737e13eee1470ca1d78eea12df1686bea56265a2c7fed2da879485c819f18a0a0eb9f7f65cfbe185529d28d1e51ae0235c43c93342c2b298bcfdf01690181f0267a134884c2f8ae5fb6c980b5734cd17be29d5fbf639bbb0a22a4c6161950e0ab5d3fcf600ead2680ec0d82879065c619ab42cdccddd67386dee4938be750404f51f67ee428412811ecb6a96a8f903b64bb2e0b993cbf4c4704efb5c1ed73e0338e51d38912b6f0d1cec637e5dcb493c2c94847861402d4f0cbc85fd76a813030352fe9441ed653bb93492156f13c4e482185825c574324010f1d23a0143b5042f70bea7703d1686baaa1019fab300405397c8f17873c5170a6707754459f6019c5c2801d1740b2db932d9d725c8c94c8a62bf8938941b037d2926b17d3378019e32d7bf24ea5ed76c12918ae123bfb0b34345cc234d86a54ff880d66ddac500c174b0892d67605a9aea00e68b8fc74c2b26bc7c03c19aa747d64ca2a83af40b409a436093f55e6a05923482b324828e09bc5450c6d404f318b34d7b25a09a0b449d6555d603088945813b84aa6b2997c4d3e1c6187e08727bbcff1e16f8490e94b626342623ff55deb3ad6cee503f0942993d94947f3ad5eed0ede98a67200cac3001162a721010df30048eeb8e37f8a8be5e080f6d56f853cd99d69d608709bbfc71d82a913dd73e264de312f30fed7a04a67e39a08c9b82db55b8cc0ea40ec35cf5ceff66eb72c89204a449b1a7af985ba96704ed2ae82435a7ae8a99e1015a6b824b373fea8cf74d2b48c3976a5323bed0ea2466601b9bf83e943b6deb011e0e8a4d297de3a19ba1fbfb9d33b99508c50008296a574230c1891ad796f0003d090fd3f8af03ace4ccb4ae0f309af27a648d3c83ec4b4aca151f8c8905fc0dca1384e012d622b63631694332b1773eb26dce6dbb19e7fffa7c53bb93cca00e1d8ad86809e41bf3127e96aa1896611514544978d1762b831648673cbadabf06bcf23fa75a9ccf0a348c4a3591cdd71310d360b1e9a1a39759f485793ddd0f06da554977b8c54636ddff6bd32126c8cbf929fa4138701d6790a1f7eb6ad6e1028fc21691a6d011c6afd3ab96ea312b0bd9f845e8170e68b0e7111b6e6ab2dd07d4874c42e86cfb61f6bd4c95d75f4fd3dd0c467d5a4a2ef569ae3bd7bf93c602cf2484ae5c448378bdaeb8aea142430f39db5c3de95cb40e86858117c1ff560e7942aafd8a6c53942bf3898c1f6db046aed9c44cfafe142dccebfb31e5426e0ece3df41146dbbcb5796d451b52207296e9a92e9282b05171f38e87be76e93a0c7eb1075ffedf1354412d78c7385422eb747cb3fbebe373c556c2296420ec0904fd91e28afe546dc8b6622cefc3ed176feeca677870066a90a5b0602944f22a0fde7b243a73d3418110faddb17f6c1c82dd3d5828d28d30e70115b75021db660f826e28cd35082b0a24b394a1ef3acacf678b687abb872acfd161bf70d1545009d5ffcd8f825101670d571069be204be4c75278d4a447c72729db334fe0fb2e0439364910b020fa8ecacfe201336aac8769acfd53327d766a3fffc24f8c921607ae9a424f284adea1e5475b70dc052a29f49ee0a06aa832e7b8743a436cd31300d02d45125ec9e00ce5f1fff4fa6db35cf4e88d856d2e01a78d02e051792c8d0b4a52f7e613f001b44d70c2674925acf698a39c311b6fad238ead125c3d8c860f974ec9ce7f80e7e75694855a011da1722e66bf0ffa56010d2430f4b602cdfb0f5e2e36e3ef9fd6fcfbabb1e37d5396439643897b918af342b4db44a4788e05022260a125d3fcc4b31322ee66dfedd123499c9fbcaa9d9532bed011af7da4b101c57adadf32a6ff58b13de5482c0f03b4198adf7b92e5e0824ee5b4d81763650b9bf5b072142b1567509d07e98d83e2acb3f8aa379d7a347135e96de6c943bc0826ab7e7becbb91231a2b5726c501ff44d1d609773d7133d2093cb131b1daec059b7f9ea1705811de9fb51bfe4b7a38fa08a3aa5dbde92ce1e25c1290c7754d0d59e54f1dbc5c965fdbe6fd35d74d705c1287ff7130b4a06418c0d0bde4e5740ffb38181d31a6b463a0d471014114c4e76a79c5cb8ed5445643b848d13c9cfa0df45519feb5f90ba3e1c63839bdd2fa4f84dc4ca7b022c7a4e323f304d4448b044f52d6323974161110c715345c9c78057cb515c28f911b76dae5380be8e5970de52590e23323a47ab778a3ccfe4d083a1e6ad085ef164ef6492d95832d502e0b6e59a0f1c596597bfab489e0eab5aca06a5658a2929f4b866ac7d4993779af027a11d2940cfc186798d2ffaef5dbfca0e3a3efe67325ad184c51e42edf9c2e07066b171db5acb4d3d9d03e91a434941eaeacec4b359656fbeb5db0302515a1025b13bd8bc2c530dfcbfdf1b430e4ea7e9d5561f6f189db531ec722355b390803846d161a14c3d1f036a61cb2195384cbe5a21b0635e34c6800ced665133d1f06c11bd6b68a972a2b91e357f7a07d2f12a89a0e41b7bfbd8c306f3f12ad8aab0add498e409215a334843e2a439810dc8ca602345265e0c47b5da52a283b880d0211f4646d38284d37edabb3fd7c1978322ab67ecaf5b2bfddbc0531cac461d2051318de5359269d779d2b6917ea09e64f675ad7f346330549948c57e44a6e4503736023e6a61111b82055bb237c93c1a01cc68f1ff839d4a273f3b3c3e4f8fa061436e80280c7d24d0b00f221ca36e8691e0bb805fb6f0a5c0b6cc171a5ef9909c7597d53564ecfd62c2dfd2b16974ee28357ff064fe080cfb7c66705bbeafa03b9b4563a22e6c51a6b3f2aa75fbdbfcca3298b94736acab5d98ebc35ddf45b07edb8deb2bd6ac1edb8ae31ac88071de7906107c109acbb00a46f7721a8635d0a79117669662fc0a18dad6fe9bd90135a7f87c2882b2570c12b60a1ed1354a80a70a7508d0a8e0e2fcc9136e104f3406b0f6e09e7398ad1f9f71b1f934b4ad409e1d27cab0f95283f52c8ee4be7b77cc1802e1e742c9a3319e6c5521841b9ed0591fffa4fe3d1733b7bc88645775b58bd06ab4bb98cadbd0c05945e4bf60d120674126a47b73fa10af1e209bc1bded0f93cb710bb8f748b4a8a67da76eb63150246fbc4dec00f42ec588eeeafc31a67bd28e31b5c0761eda39c1a6f63c1798508e2443b565cce209be2f37dccafaeee72e001a0487917427c6f2d5536c4460f017cfdd414a946af17dc6176bcaaba32ae273bfc100efe611ddc224802701ab50420131738bd9b36f9072ed82296d64d92254bec19b37c9af687ee66918d27ed022c582be44f59cbd10098c6819314ce9fc037c2856d6d6f662a629e15bb101606b06c3e40390eb731e6d41ed6e10467b30f483ce0c1d330bd27ce2c751de83905bbc3f4185219e44f84314322d770fe505b790ad0fd1c05771422e7896ec70f096a515c83dd6a5d3b162ddc74d64e64b008f816796ef244686807f8565019170e703572ce6b6c4af25e46435031e0f9157afcdc39b1dc35a3b943230cf461d70e4abb30a7438ac3b8dd78c35d2888c91135b52ee4dccdc20c2c9c576a01950b0930b08be459ddfbd7274336f1b204d461f86088ff61ae1e4af69762689481ff0d4a0d10408bec80fdcdf49d901185398b55147b6f9b1b742f37c6c68d1b465202efdaa243cefc286df052e3dbff83a0f1fc1e0dd2004ca988dfc12783c1718d02b2fcba5b8fd3843041507c755322824904ee5823f5ad3bc03236e0cccd5e450235997f2e4a87551396d95544f0c5d8106a7e7d7a230a0bd85d006c42d161c208d12db21a7a5a2778bf04380e8dbe35cf996d5a06b612625d9fc584d1ddcf8d0b014cdd5f492a02b6933f1348299d80a82d27be3842a34b4c99ab785e96ca2b0bbc2308f8b65152502dfbae8eeee5c541fa1a3e3d05f8cea24a5b1c59f6747b095a5cad05cc4b8b618cb8b3d8905c07119c7a2b424996b98a5c09badd9eb13a0d7f35eba17fdcedb20c488ec698b8fa7a70c1b986e724479243ef6cef835481047019e558a60b595502db5c213817638ed7346fcfbf8ffefbab523e68f2bbaa03479d594b1af8d8a1f7a8a68385c834c7c2c77cb4ebd6f72a951a1d78c5a824070d9fae94cb506b41dc725640a9d0417247c157685e24607d297aa76671d0c1055e851b343ddc05a532eef3fefcacf802445944eccd55749dbee5bca7938e6c0683a34b2758c56d9d1d6fce74d19c6ef88c519899cdd290e89ada20381980290873d8a64773192b9e07f21a9569e652303d4135133c69fdf2318f5a1ef3220a059a5a42197e50587e80c97c9f33be17703c55128e8edc1ff3ce8b74b5cd6f76056105beb0b3febf9477d9c90473e10b5e11d667bc6aeca84049176922222b4f0fef3db7b7fd069760ec67a3a9ef2839c0b641f29c6787e9b22e2749c75afa4c0fef19e3cbfdc4718ac038f725cd2e5baf6f48ec789cda7a71d2e81e2703597b09 -generate_ring_signature 68d6b1b9d874d51227c9f0a28cda04d8172e7cae79e06b58aca4cb5fdabf36c0 3438eed5f743a6715d57581bf005efe4fc4cce9447e6d296bd602250d8c16fa2 32 97f5294c86120cfa379c931f3616ae5853e5650dccf93877dd3d22a949deae47 20456187ac5551528a6c1e1e6e6ae511a8835a1e17c2d8376fb0d3d68f8ff749 0b8117871c4b8a996719c93ec132408c440b9f0a73d36ce619e4ea5099f46f3d b258d44bd5ecceec9ea3b4fc8e95496d997234a63c7b6e5128b39bbefbbc4bed 7c68933b4898deb06963efe87110b3a446466a13b18660dd8f550622629d0c0c c2a30c8b5167c28eb95b91920dc363bb6f933bcd64986c3434ed89fc077536ee 3c98ea89ace2a89cee7bc6e06f63cf5ba9297273b9044d4c6ae81b9e411bb440 cfbad8a65a06a22fe90f2bc11ffc003601392504fb1b3f85177f769e22481b8d 8085897e5683d6b33672d6e2e7532337eaf1a6c47d6eab0832460f5460f9f495 a4545f4596f607250e5de364cc2e0e070e37d1c330a6ac6eabb3d03af415209a 9febfeb7e7bbcfc9ed9d663b81671638f03555fdbad469150cb649f765d60d6b b7240cef5233c3eaf7ea031d7ed769d65a7362c90448afe2fb5fba997ceb6d45 0343b7459f99c7ac11c3d9f829f5615c5bc60e35569dfd97b78e6ae2e00f5d8b da9d89d8f350d85ce47516472c0e73dbe1114aa92de1e847d7c6a1903651671e 3bc5140c5ca0e3df7a69f91bdb268dcfa28550c35d64da809ba9940c03dc842c 85eb184236053a9e30b23e189e446a46c11be28359c0b4f70fa696e88a425b39 d08857473602cf81bbc7b1b17766fc09a7fd97c3994f8bc4de94c6aee03f3e99 7edfc9671443ccc734800a1479d6e7840689872b8652be4e97c9ea7aa9971e3f fe172c59587584facc28059d30713ecbdb2f38d75e967899cce7043592ad3e02 d073e626d083698553eb8c7d60e5a526e06f9e288f2c0216cc71535b443a3528 6621abc1d1d77e0e65d363b1dbea5a4651a810fead6d57fb116791f83824d668 fdffaf67600740168caec34bfd37b0fbb16275b155de1eb1bd7b39f1150d0c88 dfddcde4c8ac2861c78bc7df7cf627f7bf8d115c825a10d470ee665ab72bbb22 4d93013a13fb22587ebfdb63e9d8ccb1f7c04bd51c199346461fcf3314094887 f008c557a5edfa69389b32f0da30d88b2e6b91a4e37600c14772ffd578a67930 d1ee1579b8fe43e9faf76280b8c012985465491c20bfbc29d9b2f2e69083e16c 5056cb0d29f7e3fff91d6f9ddacb545f2266163f049ae76407df41c25101658f 80b56590de9d2826db4bc73ca7f18407fd0e5c5c03dd9841c9ded9b8dafbbea6 62571c909782c70567e9968ded1c05a4226a3e04a07ae9db48e0153a56b2a468 fa3243902d44e63e78a23d70a6706efb6e453d3ed42d087cabc7655fb83ca3e4 976c32ea460a47aa3596d836d70bb40a84e6f3d9ff1e2e01a516cfeec938a2f1 04d573623956db0dd22344c139dc7170d8ddf6296ccf558cc2bacf230485aa05 041c83987a208dcb9b7658b567e4baa29c0fd278a85a7ed885d26e4356250908 11 d6881a4fb226387d6d9d37d28fb23945fd91ffd118fad03ed39e607ebd4ff908ccb8119c9894b797c24a6c403bf3d5084ee75831670a412665024d90b83b5f02931e1ddc871b9d1c65c0d0d32f2b95421e90f27bd82f07175d8adb0750d7ca03207ec13f800b9461dfa5c7a3af79a3fc293b26de71a54128fa161a60894c6b0e063ab0a4c994f0b1dc7f9860c68162711b624483046c78dd05fdfa01cd2cec07ab3568f58d060ae09cf4d192ec60d6d775e4b40b1c8b466ff310ca4b79e86401151dd09df8fc2e1dbed2d80f499213209912faf29a6319d1252b5d5f7efed303edcb7fbcae45e0985da4553af47c95936169800e72aa11639121b4fe98dad3000a1cced5234918af9695068029de94534c32af24ce4db4a8f17ee09918387e044d1d5a4e45a8965b6231fea39ce799b150747b3d433f75af225ff50a75a4870f1cdbbe65f19211b9bc02e82679970e140255ad6423153677c70b9fc0e61e0400fe4c4c2779d8ea6cf97f0073091f7d9a743b0b54280ecdb952f31ec96e819f0cbc00762d2100a816e5b7a79b33374a44f6aa8aa8c339515519fce05a30d55c01c027b4e2de53e8515dbef52b538b46f70960fde19ce9f6255a80c1552b843402a6d802f1b1a0046c8358f062400d5e2fcca5f245968ebb72dd1772df85de2d0921f4eb33cb6a6538cb119267e8bf1fc36667d5c002f5d23b4dbdb2d250b0c90b843cc937fdc67dedf722ec63aca24c88ca31738028e542a7f9f0220a2005fd09d68c630b1eb546723172e2e9f96323278c12e01253c95b809afb60719b8cd309003b9a8f980208aaeddcdcc3303b77368a549faf984ed2c8cf36a2fdd5d5f60880df6febbd09c97e9345b692df0d655075bcd69b3b6386551762de7e2b5df903bdf8de2c52eaf6a77780a3fd50c47ca63b10dd22f64e9535ee90b172d254b902d3e5583454b1e4aa54ffce0299b1ee9593100177694fc9b45ac318e2c0fa03047272891f99e9bb130b4ddfafe2f60b7d9e5f003f504eab763282bd91b665f5084a8f2704c08da0b557b01d0e0f2d187d6e89803b0aa8be3ce26aaa94ca49750bf32b8338e2b730819a88ddd206a26e10946bdc70cdfcc5f022244e3c71ee1e041f6ec9d338dc58dbfadc85cc2502614752ea6083adc8e9d3441b1ed610394f0cb2a52c3a039d37029b2d26a69faef3cb9e3f3890ae3b167a8894e6165373600863e5b30f7fb9f017a8198672bb71d0104e63f68b42e940c6b42b216ee2d7bb0936cbc1f957ad9095584f546539fbe41ef35c0a9cd671844080b09182e6da3702276b4ca1924fc5021ca3e7cf00a59e29c33c79ec75de14691a16b2af528ed00c91f1439d0bc4f213949fd3a8b6e3da7fde65f9ef16bc85d1514e27d32e25020df2a9a75da5b8f1059a189c2b51af132324f6654abc9b76281feae25fa67afd00c4a3c556673f530bbf17a58dc1ace776716830a282293e54b5602cfe78ff750bf9263151677d398d68c4985526c8868835dcd8e5e236ad1c6eaa0eb3192ee40b1f941777b2b4308c3481494f34fad5d9283ea65a888692926899076e6018f406d7f4054a7e1606602fc035e28649063d9c20cf38b4d1b3af60275ef95f2eda00bad817ec9cfb6fbac673a265d6802fe86a96473f3af26a0cb2feff153c61740af339cde94a284b8345558d0ddc3e5b31976f1fe8341f892e93a1928323cdec09aa2bc8e4a4ad8b934cd128fd998ed660f829c39bf93d11e47b1661abef61e2061d0dc17774bb50d1c7d4c625022472132eecba05596887952ede93460d74a1059508ecccdbf791610e4d45c95f302d40d4b2d15c750b52ded9dd9eeab8fa200cea4afb0ad02832d61198d1bbd929a762a2aafdd6ae07780773ef405058cb310b24f3ed1b735809373aa235390c055e31a4c7b70e6d512fa5c97610c1849adc0d85f27483e27ce4106e29b54fca87407c1237313e42bb19662a6524247e533300d0e5bafb732e6a558daa3b196b8b07203734e9341b182042bc0384bc478b770c423a43ca7ddc4aa04fd44fb22063e8763981565c865d9557d0507293e45269020771254117ff39cf7babd48d9b3ea1249ce11baff155f0f9bf28d7b3b7aba1000f7e7f00e415397e808b7a48ceed666f7311f382ff47c4a835271cd31ee1500146a0f37d0edfad2fefa0e44db0718db2585d252209b3145cbfc6616f71837e04b422136d6c252d6ae6da812d6182edd6e05ce96db4c26b193c8b5345d46f370ad20bfd1b2e72fa186ca7e16a230df838276b971921351798da31e50682bcfb05588148c7fcaab3173a8f82518b65f0c5b2474d3e968c56d7cffe93ff8457680cc6a984fc0bef3f4b6d667bfecc822f9da8f2d3ae09e7c616691031c9bd08b9052a64f1e457a3167c9b73d537fa47a14f17a84fb32dc4c14e26818afd651d0d0cebcc0ea45af12611430ad160297ad0ece1c088596e774335bb565733fdf1c20df26cc841f4b366067c553018da68e298481b1f5b8ed68115bdbd97f89100470ee3eb72d89d7127a08a4f601a3735507d1aa5392f919ecbf19f196dec026ddf04591441363eca68448bd439e23c87c2751cdd7106a7d8a859c9c2651c81f4120a126b312e525e3d459c61ffac816b50122d0edd7b7a4839414d1319321692710c245c23ad60a12a254cc3e3f458b468265c3a1a519ef39b57be1a6e5b8465e102d574093f022eab2d35f16299b2f717935ee4b975926b4845674cce5e8c31560d2a27543b103e51924fb7b1c51491d670482f721bef72383c0c858106d83ec709973a9b8cee589c53a4d5ab41e0ae2e6a6de35de526a28356570f7ca83d8d8805b9aff52a2c743c87153996116562cd9ec143dc6dec91a9d2434094b891077b0b +generate_ring_signature f23065c28a04de5d38606f37b7cf29c680c06c2a9bc00b2bb3bac696f9df5c61 7046e48f17ddaaebb928ae39dbe62c23441f01ed40d23fbf5f07e5a352d06197 16 9cf7b3138ddeb90d60b31bbad6458bd58bcad71192d37e69444b28f9450ab37b fe25432b3e417e7123b3b55661f9d8e01b9238c801feec00d01b0a144439126d 1af5776b1bddd8ea5e775e035ae7a3fbfa2e08401f7bb01162ca7d2c9ce21aa6 8138759a1d698a79ba0ee523980c7899f5833ccd15f9c224a02c44accc46ed71 00cc8407088c92ac28aaeebfa3de0f699de189923fb7fc18daf1361a7356c203 6493d68aec79197563b65b4d202665da993a27318538b46678aba6df7475e6ef 2193c3b3e8c038d3ee84d30a3e6b8ccbdf29b25f9b576953907b28a3abe41689 3f4692db3e0e194005497b99f5097938659fb00906b308e4413ab07d20d21ca2 5adf105a86dd1d4579bfb3bbd362119122bbf9517c089273570189f368919f60 8e2b0684c678a5e9500332e375fe28f9ce127e1d847a56aa198f81d02afecc3d 75cd3c47a8d0616d4f7374f3b652e2b4dcbedc826b8b0f22c8f2b12d8449171b b26567ad1bc3e39a4d59795b347cf852c35944498d5ab3537ed76f63111bc196 a78edb131426ca91e0a25be0197578a4d4d2876d18313a940814d8c8e65f493e 943aa48aa6dec62a8f30eb16e796196e3a35569203e58067228c8b496a739d4e f9d1ef180af3b08d9ae7ee9dbb687591bdd3f8fd2b1912de622cce6a9e0d25ba dc6c4e0b458a8f3b47d5841dda4b34bd01c845031421c8fc34d90f2356564fd7 f7738841783117a5206b00b4054df1949312437643b353459e02552d2c69f10c 6 35543c375bef62446decba5b701b9d6ab6fa9ef7496a34fa69dd7b356154e70b48d8f5edac9e6747108e4988d460dfbf8e01b5a5bb1092128602117ae1ea63019ccd049cf57ad4ab59814ff218c68373b596effcd7ac9724c80ef24e9cee6609314223945883af4b688a3332e184a9af97fe5e32d9b1935c20e4f01640833b04fdbcd33f12b644e8214c0e614feb23560912cd9331ccf18ad2192937faeb280c6226e186f3eaabbfd7a91d17e7d601fee1ae1507427378cdf6606b03b49ef1030bc1f997c876d799ded665411db272572baf71147d98c72ce9d74586242ed70cc304bca4eec61392564be36699228037ae4e0f30a3b6dbc3ec04b749a8c7f00a1039e06e5448a63ac0f19b7210ca123b8c6391d9d480fe77ee3a3d12f03a0c0d659f299507c415e168f12f7cff9f3a7340616f59edd1814208fb226dd17af0088f35efd0468bc02d1bd4bca546c5767fb776e4988775cfecfb54e5d2a2322a00d6f600c11298a426ce745fd1c8cf83125ed1d0999e62fe8d6b97b1ace6d19905b92407f16854b13f2950c692a7dc2bec6754bcb4a2461e15921baab1b29451003ff160934f063c8a9fad444a2ef25a767016d080f5ac72856a8b58a63e0fe809c3e08eba1d970a3d975d0a0731411f6fa073c2fd9cfe587dd76187643bd3280b96fd38e180420bfa66b7c84ebd30bc9a0258dbead563ba6eb9563058b5d5340ee1d00b24d4edd26b0884e171cbaeeef242cdda9a9b0d6264f55edc671d0c5e01197bfa84ffe3d19b41a8efa617bb340ed0deaba427a803a980665738de666e08432f9e4702bcb37a7161288bf9a4a15f1c7a876aaac365c6d17589a1b63a7a0f80b9f6c1596d9eff3bd4bb4dbac571711314b9dfcd37f1e7a4968248aec47e0c4ea897fec99aa22d8a24064cc1e4d5c53ca2c4b2c2f4d21af89187edb294100e00568f5c1a9423b58a98e734a084d097dd288b63ade91a39844a9d3ca828d5086153b0ea8dea0f853b12579dae0cebad5e5cb47fb1ee2382351c61c689be6d04a82c71c8c6908cda5bfd613378d2adae734ce11093a4420a32a1347e82ef4e034311fd57012c4378908fe1c71568647703d5c16451bc7422ff395e1bf37c3806f4dc672590fe3dfd459796f2661535cba870df5452843db1dd6e98533596080148f3a3d390249fe7d5e40797722496f97bbb68544b38e9e20aff7e99c6e1aa0a9bbbbff6a2cecf105f1abad5f41323cf60a2ea069609e3f480ae1f2bb9dff201c071a7892bd5e212d17e8af5d050058daa2b2a2d380ff0612bb00bc30332b10e67c5e5e5e97be66492e1b081243c53b094ae85d13d0a6994004a9609745b740829ee72857f6de48c90b06ddb3284050c7ca1fa73349295ed9c255186ec64c40b14c13ce99a70f75257d5a3a608429506a6d2dc93c3fa08ea455cc4b7921bae00 +generate_ring_signature 89226689e486049662075f55d46361d821c5ede1fc172581458207aeb3d7374b 4048d63774cf0e3d73059b76c1160f5b36fae2add758c0b5d0a76eccf459081b 2 68943d3665e40eaa5d8ce9a3279e70e9d00afa0cea15d6671e024efcdad2900c fb89cf7108eb3b68243e732e820d716e11a0baa0def8d2d837ab998a9bd642c0 f75b48b628a7f3fd1dce1055f7c0b81c36454e012dc7ead8d5528c11cd52990d 1 095d3a0d3188d2d30e9e662efc69e56060644722f1b37e1d24c831844b322507546a80f5655838f461937500ce799358fe2e589e12175a6614897e8f52ab26068fdf38533987e67574068cfe9f4336818c30667aa4b9db5cc201a15a67899d0945eb2fc328d4a5f9343b2b3041492c3584fc0c63c4648c948f3ab4dac7d9b40a +generate_ring_signature b5d8229ce74db9826924b3c6e5caef7e7e5bf671ed19fc11be2989dd3c47931b cce45b0c2c85b4210b5b11644afc9395fcb69ba98603adddb5553445e9ec7b6c 144 0be075ebeaf99a172e8e39e394767c3a3f987767dd4980c63801034626594ae6 73fccd68f4dddcedf7206f4928e50477501c1efd04ac95c240bcdb1311abe7d7 82d86f2cee19fa0019fdd5680e9f5bc9a0a19fd20fe801693646eb80029cd721 5028c84c1470faabaa794544bdc8cd2c711465def027442c68cbb51d8b26a173 cd8f47f244cc2c434906ff8a3b05cb0cc6237dfca4df80f54f737ed73a8de6c9 b5820b71fcb114db2922d51ff6ce2243fee94e0b2b325539c7ca31701a291b9b f8935af82ec3e9e25665a07e742a0db3ab87cac8f9aa8765bf50edde6b039370 3ccbd63146f9d7b10b80d92ee369e723579b31d62d93c6792de1a4eb498cfb3f cd2d042ef2fe9c3cabe3092cae589b3ac93bc006c44191ec547645ccf67afdff e2d93bf17b1a2fea61e957bfc3e8a9f354ec7716fca36d9d3147608eb3af97c2 8a48888c11261fb5a99f7d446d679b545f9053b2daf6c82afafe43f8ddc2b6e0 1d5a580055cadbb839eee43d38dddb20d970ab5c1a9e2f05c553d299cf495774 f502b134474771d28c63829de5e2cbb68f561ac4424979ad81d9561c77c9a8e5 1d0c147249493a9cc4c6a08a5e1b787bcafe06363736cfa165b1a01347160495 5ac2b45e81b6098c0039a2af48d87b98e39923f3140768b797ca3d33929ac7ab 3859ceeb597d189dc18be59daa68d39bfc2361b820524631bc05e22b78ff775e 96dcad77dc296cfb3929634e642229bb0f748cc674266991ed53d64956d6677d 3035e9feb91d2bdc3917a8c58b76600141a4333dc94499e36b9a75509ec9278a 78b57f73d0d54237460f950433f02b23a8b73fd686db4872dbbaccfd93e4962d bbc589f174313a7862b5cdbe0bdce6ddac58d6167fdd906bf255e3e98ec79160 cf34f7674a32ea2273517d25472e5bcc686f3f461c734e4e93a302b4c9439d0b f880e06bdefa123e7b6a09ba0e0de33fde7cf7ec3b792abb07ad06107e192f73 988521245df44f9840aab96fd90af3c22455058e24eda4f2e33e1269dff4430b ebf20602e0afa497d66b8e34e5269ed831e970f912310e178c1414d25ca71c35 5efd0c9703fc2e020379534b469c526171091147c59d2552a871823c11821e6c 943dc0fc80c35556a653903f09cee980d4f66d9e7179dc26ace15aed32cfb9c0 274eec520fb3250395785dc104e5b914ff88a4d7f9da7e07da6c0830b2f3a1c2 560f145a25c13e0a1f6d9d79edd399d5afc6b09e3103c09f1d77b11a975e3b2c 3b13f3fe0e4428737929f2e13aa57f675e7e3688d8b7b0475d0a950a730d354f c0c4ddf2f2fbf1c0c8ec3873285ffd498b594cb5714c851c27e1397d386e12bb 81ae0f6596c54518ccad5d592123c5960e17b589686cadf3b530b185d93048f7 4ca1879a6b680cbc2024fb37a2a17853767e884cd8e2ad20c2499fbdb53d57bf 56d56a1d537d1f9bdc2505bec8cd238d765e1bed5c245c1054422520d7204ab3 b3c4e1763c0bdb20cd468a13560fd0ec0576d167a3d3d62523067e8e23a2ece4 90805b8b9ad99246003275c1dab6d1bc2dd6bb12454ac381c662048c5bc54738 dfea8fcc3267c854025ec6c917b696c1905e23282ccc042d81ab3e37782380c7 d86baafc5adb032b78d5a7e2047b619544d0d97236d79002d3a091897fa0c48d d1f6ee6693c03674a865ff5f24316e48f844784161afffa57f0bec4d098f01de 9ac006713f84fa6e3bcef13946203f85dfdddac7a471ccc417a65fb929c52d3b 9cbfc2677c60a2cf155dbdcb8065778c7428f6ce8524f483d67eb45e7e9ce463 ab7bd7860210e59ad3231d0309b591d1032b6af933d59506641e55b659efcda3 7d5e773ac67fb198cd86d94f52d8949d3a44bf213980e2e9355451e65d5d3888 3f34184c4cc8f2d7fff24871d02347f522a690866c695f2c7592cd37ce48f9bc 6674fa097442f932e1e25123d663672e2de17b2385b8c41a6a509c46f89e324a 234a35e2f0da5e2182c2450e90101cf1690235894fe8ff5cf7036e03d4423865 f2c8967a786fe891000838ae56ee84891df8dad799971bd87ad9e3aa0dc1d03a 0484249715649583e496f21fec59eb4c35553761ab82b3bf85ce18e67c184ade f6104748641de70509aaaf7330d390f45ed534ef38bb11ebf52ce8e6b1f9cc42 beb938142360b3b1f0933990a5c0e65180d77c2923b2aa2a3f6250c153c8dd13 518d918bf968bdcd664b6bf24e865348f7093ed723e0293ee86a2402b87e5df4 e86e665a415da43c81bb03d895151814ec4c045147a4bcfe37a014a846f26436 1a11a329f7530ab7b31198f03d8d29754ffe53af384fe5d4db3fe90ffa3360a1 cbf7a677e205f9ad968df137584d4c90438505a629dd6b5f3c4f822564b41c8f 903ea1173296e05bc70d6ecaab59f1a6ae55ba8b428d5ea9a3a5e4217ab5dd49 aba326ca541d77ebc99dda5c1ecc21a07010c2945ce9d5d18a2bc753fcc4156c cc007e712cac3f9d64f16ed07dadddf934b1b9373247e0e8d350e5a2551ee0b7 14200d798b42df0eccfa9f36ad0e18f1aef0021e9d3ae7912e71bf4efa947f29 658b34f8f8f575805b075dd65b5493b737b1d8b6ba122a55a25f7053f23da8da 146f311fe86024f1a81034d6c9bda14c3d4728fdbc030901a1ad577bef3ca5a0 9eb53c235a0298a92643833c1705b6bbbbf99e254a74e93a63b1443934ee7aa2 ca1831e48ac62ded5bba90fb71d61fcc7704151495bd72e05c62f780cf9b519c 4d1d68714278a5333173e703409b4303912cf3b44133102fe9eee8842306fe08 4ec22758002566d15055dc51f1386907f7104dd011c8337f1b36fb29e881bdd6 0c90d65ca0d89c7835d5cc8f185c6abefcc68c0fde058a516ddc19aae133f6af 548f850b0f17d7b21e8c1a0d77785253b05737045b944364c157b942d094e5b2 d0607ab0ba606a0765283abdc2db799ab04c5c63edde5554396e28a87c5bcdd8 51d421c5fb161cb1c883e2e81930d9f848c5eb587aa2092431afe368fef3e0ae 5ece9e7ca0757f3342408b957d8a0848fdee5938251626670255ebb0a75ae101 5579c81801b58b99d1238f2253651b143d8e3ec4cc69f0ef5fd5483d216e1319 7bcaa05b83bb272070bb8987bd618c24e45311b57085b5b6a33f3e1ab804c4bb 89d7c2d2338e0b5fe50d17efd1a567460b484d1dccf9d528efa6757d5233c85e cef2cf9153cb0ba4f39713613ba40ff9cad9cbb7e518fda13e721c329e6e38b0 8a831f7a9a03d40371d569427e1c00b4c6b59a804135c90849fb0abeeb203bbc a71b0481ffb4425af168570f2b0b415ed1fc1c181204e21a27fa5862d8a63c6b 99fedf8d43745d034e111b8cc7d99e54d18b53c40a428d2628365b2b720a67e7 d2575e2b349a45027aea08f5b423ae42950e39fd365891533196026f6cd2d496 e081393f25372bf4f1aee37b8bb04921fb8f6aa0fedc1143ad9fce6de55243e1 ac4168afb6887345c8491a395b777fc2ce312372c6655295faed65742ac69bfe 5472335979b474db10d62e368bd14c7fa3681e2aa3310ab5175d47b9b23d4861 3e70485ea10a5d0763c84c5dd9e09a25adf926965c59c0d7d1345a60d0ce6024 ea6173b067f377d97a79537f532b8d2343567e92cb9dc977e772cdc6129447e6 da151c45da028df365edf5ffb819db00f38ceb10fa687d629f958d653c7f7753 4d94eb70a30b0c63513a4f09907f972fa82a2f941be6bd9ca914641ebe8553bc 3907706663fca37513a9a30facd133017aa772f69731be6d4deb4f40047ccc48 32d52817b46b90f7cd04c2aad2ba6f8d44c8082ba397c60b0d982c5c7aa9a8f7 8abe3186bbb742c573ac355421962942ab2c75facc6c898ee4a19a03b2526ddc 9bcbd8770aeeeeda57a9a2dda3b526e5abd65acbfe8b68ab4fb346da066c476d 010c68c46034de748d583b050e2715d98ba852735272e8d11d6e527bf1a40cf9 3407b8a4d8c5b985a20d33d3896cde2cb823320fa4f7a8f2385dc320d83d5ddb 53a7e5fb8541fa6abcbe75a39cd9b0352119fcc84e724a515f3442d4a558c55d 73becebdf4b4ddea87969a7d672b4778586a5711226e7ea72b6d615bd6eaca29 96cc838b65be8f2cc82682aae4948d766751bdcdb162b399f73b2d1b28a47492 2a0780dc42a9aa0ff3cda976a46510ea79ade528d57c66131da66f29046669ba c0e71216cc5e4dbd872b783181fc19b8c838881b8c7feeb7b679bfa700c5f00f f7b1094d3f05868b1d8f82468e3e63d6ee98a2556190e8ebdeeddbe739662e70 7cd1ae917e9fe9b2250d118b320a611c42907668a2a916430086e79fdc31624a 6c02f8d8d410727f698344007fde07f7bb260eff705a031fcca93387c0538136 d0f96ccb71432fa05978ec91f83d522bc6237dbc29a28ef02c9019f7674df4fd 0754988d535e9b8e840d17088c97eb0484b9086fde51eb499f9079c8694b9092 b45a7849a1b66581d71acd062609a406e78566c94863ae128f4dd89db838b09b f2c392a9b334780a36990057be799cd2d96d9544b3388563fac448e2a8e61ba5 ba1ac60d28e8fe4266f7e99ba5dcfdf2c3f03688836b6e39295b4f08356e5e39 3cb51ec951027e73f76394226b502c12549883591c82f40998d70a24433c584b 6349964aa8486223d4b70c8b8fe69145525bd91ef6628e1c6cdd2ce0ba04c266 290f25e23204297c88d4e709a0ca24e3cbc54b657393ed8569e1d9f5110cc2ef 0ff7d6198640d9e511867fc6f6eb733728d4087cbf803cd6fda69743415dfa0a b11f6e8f2fb83fd784b1281372023354d58d84ff26c24f6fd7701537406c00be 0b5a3a445240b4151eaf074d6be3c50bc54fbb69db6f1eae09b6c146e81f87d2 a70b418a5ea1f707725dd25fdb646fc10411acf4ee3495c160e56057d469be7f ba5d677d820274a39f795af23e660a8fa619f6213d0c99051f93006a472105a1 da947bc68bb177db418974a50388357ba6c0c7f7d1c269c6c7e7ac12013aa388 b8fface27affd9eabd46e85658c00b2d1a187938e70aaa0401ce2645e1faadc9 01950f516b23727d50e930d6a7f982701200cc9b4f91e5fdc24fae0621df0648 d1525f7d9ef2244122bc256f431b58a53f348af292547b970214c78677d96748 928e5176379bd48f34ef5acbf25898ddde6fc2de80783d71d01dda12f38a85e5 c4e9fb2607e4e77d6be6651e8f3252ee3b0cb4612adbf38432d8cc40a25b6423 e8c78da2f6c26bec3eb9d0c8e54d7169a780b4eb3a67f60a7be45b3b347950c5 5f198b0d848e36526f7159ede1b985ef4ac6981ec31d337efe6fe896236b48b5 d49ad6a6b2bb67adb3503179d1c79163385eee4ee6169fc060058b8ddb9fd401 80bef6ec30689b27bfee132087ec9308d11746263f7dd4313c400cf98a45724a ccc998e5dc245cedcc864285e40a6453fd2e443a687e0695e1f36887168dc5a2 c6b95bf4bf9d0514ef8f69abba93cf993c4731598e9bef7ac7d1df9b5cd868b0 571028b516c0d6f51d1b7809fd5066494fde172fb962f2a95d9091cc231bb462 f039561da33396d6cf7f3d5234c3b624981a193128904f296a2d49539c5a5354 c254db3225ffbe528992738a1c2a2d659a2b69b7823d5dca1f074768b2d0fa1d 26e8fb8d19c19f48c8a4a6fc8c7ae07d81a20e338b26ebf71eb6fe9cbedf82e6 20c5dc8182e869ee3b7824eb8f3aa8bbe41cbe85fef2416a116a781cc761c459 627f6fa349be0f4fcc820ebab0c373d346081727c41b3e41f6908401c55ca8d9 dd81b44f36eacc86416c791de3b6bab04d52f8a001cb805f71a09572a8dee99a 621a8ccbf534ee9b37d39f83a6f02768bc0bbdd56a9ffad301c905f43f95df1f c0662a10f78456cc0b271e278563c6b269dbf1cf6ebe5cdda87c07b735aa2e64 49f3a3193e87521d12becc9ef14c623e844f1f1a6f55fd6c2704390268f45d90 6623968079cf82cc14b56fc0eb73faf93b4cb5a4f6e1c56f12e2978aca03f6f4 c9f349184647ab482890e8f118a92abb469f62c2756b599706898a7ba2709a8e ab17adae0cb94f56924e7aa5e4dbb18b640fecbeb23f3728694a071af7efdeb1 d3b700aecd64ee52833e70c957c2d6d3f3325321786e4ec6fcd95a4645e016a8 a6e161b23862fcf9f036fa3627f2887089f79bb4e9df6587a23cf40079e07a02 44354eb4d4a96bd9dee8a8e74f2596f5cf117eb2137057758b012ef761ebb94b c66e82e556e827481441c89dbb03bc57e7fbeed3b684ded48c276056bec9fac7 1376ca9c2e462f8d22aac18d284d5ea20b8614a86b30fbe073baeb41074cc51e e098ba48f7f5f85336e47ff10a3edca5eeda1bf9717de3f4262a80e77ab93674 2779e3d23b90fb744d69d5812b0c06bd17ef83d509fd6d5a86cdfd534dceaab8 d66a3585928fcb67eb352804e9905ac7bca6d6011fe5f7e3c15df19a6f9d55c0 9892556bf022ceda7b1f04aaf0328ec0220e468010a235b760069df477789167 d089b93676963402c9ca2305200908ef13f6e5e5539cbd865b44cf6b32499b02 106 12ca358808284501952f9d157c7ae6252b38ab014d01f7781042dcdfda02e708b909ccc8c6ae1162c01cd0616b02665ec2b146c8776e4503297b488531b2bb041e946ffae962ad1ba018cad7faf9d9e40fafa26088ace24e47dc61fecccc5901d0a6aa2c60b46f5d85c7c184df48f93a13976e4937c4b4aba35ace357207290b533951d9f6c6ac7c9639fe0a9a6e3a636d5a2018417b1611ef71e4b90b6a6e04f0228e8ba1d9feab59d8c2c4ccd50cec13c9220cf741f33efd941a17b2af5f02b4c010e33221163023d0b53f3d9caea1e8f42b1c0fa6439ff27e281b03ffe90429d4a22401ac332ab6a4663d66cbc568ab77ef541767796e28213fec2fad380976fd9e62881a69c8dda41c0684cc1bf23471bfc67f5c3dfb3b1e18e9181f310c7fa0c8365f54eb77eb5e642f4836173c987ecddaf709f99a59808f2f091a950e8bcdd07c454e97b82a0e4d14a4414436fcee3574e38234778e7eeec57770b401746958056d6d066a78b66ddd3ab65f31a02f5084bb71ccee24b789e5e13c7305c75ec1ec0f5397383f79bbffc16b3149f7e9a9c5c67478edd0a099bf5a17bf0d018167e32fa63da653646fdf400314364c030a132255eee57c0879683871770cae9d5feb91e8e948d1e973bff68f4cdd78e17134b778ce5ebdd658664533850369bd04189dcd5d94f903604ff517699922246ce4ae265604fbb845347c9d330724d673d721dc97b486fa47625a896efefdf5fe3c41e2b792d23cc8448aac8704f88353b139d1f4e749171a854755a600fa206a1dcfad73af9dce6d9398f2320b48e7a63c58e960fe5b63abd9c8602b2cd36ead9964320e96f746985cfeecc70d4fe1bea9512f98590e946ef76396b2f39149f8506ef93f1be530b92aef5bfc05abc726f85a930f0dbc87ce4123a9ff3b7cfecab3ebdbec6362e0d09efcc8200ca33710350165506a0354d7bf52eb935ba2092289b4f0bbffaf1754fdbbda3a004120813d4fc1e280ef9348c521aa2c986702c1e27a6454acf0feca067147a60ac68bbc269541a50be4c3c5d4c58ee5045ea260982cf8860f4f12d317a42cac0ada69f1b81fc961f6e053caf7c9ea205548d6a7a2a19b531ce9c02573af35ac07fbdc7030937897b8280d70b23f50be0e974179da72a4cc901a88829052d00c0edff5f34b1132999a30d339c079b682efe2270a6a8bc80952ea40117cf0418705b495a0aaa1de3b3a00d04c24232e71449406aba31debd36e5704bbcbce16300826f9623ad2442a239e608ba8eff2a76fbc8b20ff0c60055d1c6fc63f8b72a90c54ff3ee9addffe43e2764bc2d1dca5a7a717ad3e7f395324f88ae7213131ba0dca4aafe2f3c2a5c295294fe689913ccd24947e839dddceb7b7ee2ace87d94d0bd45994ccb16cde1e6de3042d7f3a81244b5e16b03c39fd48e8227c57a294db0f1f1200823cd481fa10cbf07a80b9eb4d5033865e678fe641b90cdf25860a4b07650be4a63a78fbdc1928f147c77e3e1d254dd82bd21e773808525b51b2aa4a0ac2d9d5f330514b1d5bbc059b2598237ab3e419789690b9fd98a2a3311fa67401986d1559144238410aa30acfedfea75b45e65fb3a33add72a5f838bb4f0b04063db28fbd61d4b6c63b69c24c2dd7e6f5f6ce16b914166094e1bd6043c5885208cc5d6ff63f22416e3b7741de9deaf690fea9e4e8642861a2a6a601699bddb800070a41dc4b951351279bed44eede61bdae90f85879376dc7303a849ee0360101ccbe7d4b6dd26c95c5805c3a5ff9eadaa415f751b77cac21c34102e03899920a569fdee21083a63dd9616286747fb47f7faaa08346a77d2f5bec34062f8d9905975bfd3bf35e757dc3752d788c3c112f5765db63967fcfc9f2f4ea5ca2009d00a8a2339796ecee04e4028e9b4e23ad338a6629d4a64816d80f7e55e6e280040f0d2f5e6ed0a59be64c16c193902a1336c05a5999f490e9f3476c9f897058e50039ae25ab0087921748d1f5a38a55c86c3b2f792d057e2959ad2d48fe7fef5103b92a7cd703b264836b89295fca913526bc2922e1c499ab4a85deb5e1a94643067ce40d6b4cdf9d45ea104e2305879bb0c0d9f15c2e4e93c12c569e5299d0bf0559a45b658444dc1e7f2c606cfbb2c544cf63f3cec1586f9449a5bbbe8fff6e0fa9e595b3fb97337b69c58a096e4196d0e9dcdceb68d1c32bd91b0eb5b8744d0a2b142f169253f3025f6627bf22ac3e01e7c6ead6f585af692999a4ff8e9b6d0473c8786a72f0f9a11513d163b1944040ab80bbc56852dd8dd47b880e57eedb06bd0094cfff52ea997c6142034406d89216cbb18a6663d3912e67803db1cf730ab8914aa760106f7b7cd3bcf1b33d1a1cb4ec7c3ee6dea62fbb0c5f44f610f30b3647e3db2883b0534e992dac3909d23b51aa02ac96663414a3b09f53b0cee5043307e95d656cb9ea8a0e000112bb160e39313e9fcefb7d764f4168a19e97030167bc345f3985ecdc0ae589687f34018e622c9385e775bb81efca3e96bc5bf0032c331b3a4ffc143edf7a4f2bd55e6a3ce4751635b8bd30f7cc212aced57db80df13f6f5d273cebd940fe7d4ef8e1d5b4d79b2520408a6d61e8113fc1d9b3c40084ede09f602e7de1397382c6263deeb994b3f956207a53031b506559ee08c200f49977be6e025edd360167303c736d118b1e1796146c169d56ce0792a8290c00943e4cba899c458b051a2d0437b2a7da18a300b5343ce85a19675584b982650b4b291526b70ce2a68304dd5bd9cedcfd4f41357f267ec839bc24d3c6f5906f03d197a921815e124cca619cfee719c432823cb9bc0a95abe35b1c8b4a563d3b06126f8b9673c78ffaab50b9bd4bdaa3926c0561d32b33cb6ea34908a0ad53a705e57f73c18b3ad711972eb968516d32491d98e554c5fc5d531295a549e50a850d09d7a1783ddbb9d4ac2c463a9a85d444cac5b283fd860b6bbb6ee4e74121dc06538721d7a375142cd0b619aa84d1b4f66bb97769847c379df060b4df904da108c045e484cd96e3b3bc7c92d313d7e8133174005806a4b1eb12eb61cc9b26e5026ded5e5a65d20c2a6361de60e93a991814ddfa2ac987d6dbbd238658a620840ff3cde5277c9f0d3c4e06ad1ae6da8a0d45033a6163a3655754d9c5124e80670f30cb163d3e175f199e1db6d54de4ca15910002f2d4cd807592e886acd088310d55b7960c3677d0bf7364db5d9faae209122188961c0e19d47ea116eeb3389d04fd5e53f8fa3ad2b72f302152df460c07950094367a6bb1c76e6c6bc57a463405816f4a3ea003d322e04206b1d79fc07cfee9c26899b71c41c572cc9846a2490f3cb90d79d2d9b13addbb1744aadfe6473f585ae9cd37d56f50eac555b5edf9007826b85fe5cd787ea3744ba57ba705cce3c8f1c172fdd8d10aef72f88cc6900bd7cc665ceaca2f742e129d6d457c77ea8630a56670950cc5df597e3035e2bf02925e7b8555dac8940caa133da12cdd0e18d348f1c54a8b20816c5e17631bc107327a04e107d2181f7a9b86e36e8c1dcd902029a662532303174ea41871a0e4036fe809e71e57ce6b82b3f0a5d6af93e5fdd82452a9c1ff96a58efbf5039a5604c27493b8927a4f318a4dd2c10d5d317a491d63d8d522886b750affb5335b0f0af956aa02414dc94a755ee5dbb21326ea6524e0fe5397f85041e405846ac13b02719a18005ad8cf9a92de839f2951b739206ffb75ab4d4f98516422ce3970dc037241c1a85f18a2c774f0905356743b6f994cf09ccc5db2b89b79689858c5ac09394a265f1bf60f0e4a426e019baf72812e6c3ab377d17b8483630979a2d9ab07139a4f4ac5b764e174a7f4f1388d42bbb7f4f658dbfe2178503384630f018e02130c924bca3bd982ada71e0a7d24a7024c7ac3b61ddfae959ec41b5a44626a003c0e19524e8058007d1315f3278804dae559360574eb4e162784f99a7062d9098a23f04bbbe96d806eff78a217573a2bb6da2cfda5c0f7f5ab9337f22322eb0c58e7cdc2a0f23f82103f9f64030b1f25626abd1664f958ba0f2c6359253977005373a8a035698f6c9f2e592654470687b77e4b9535365250020a82348d3cf7052066b3881500f5c46ca4db97dbc0ab3783cef970a1f4d4f1b3ba87e6e90c1c0961d6638104a1821fa03b30022e66031ec15d668e3ea0e3ff72484dd337f2e30bf606a62b4e320187c95edb7cc225b3c428b8098765fb5b028856eb4f7cff670d04d102ef4a28d1c3cb141db212e1ade287f8975b036eabc2538bad8baddeca04ef5705055d00e5caad11d51478a6e53a34f5b8956a9000bdd51358d5690f5f01dd15e0b0884fa6fc2f7ff5052d5af34dcc4eb596f2a62a068245dd95301f6c026886eacc4dd7c770c94d626ec59b4cc49846ba34aae25a25fd3f4705bcbe47093bb01765dbecbb0096fdad3e35fa76cab6a2965185515ba474ae7eab967f2f01bb9bd7a4335f2841ddba012b5dd9741ee884425f66a477fe46d71e1e6c002a0d7099c010c316806f99ffa8e73388f0218d6b9da1c37c370cfd699a5ac460fb06e81ff7e4a1f835b0894e8cf3646c8cb2b5963b3c9059142277d70b1f978784056666709772b91aebcafc0f2b468b90b7a71a2a56cf1043d308ced8249990490392bc459edad192c5220388a41852bdd62c8831b8a93907eefdc3eb376aa9d00309d5c81c1241fbfae37b68002a0d5c436cb8048c677d691517edc681dd956a0046476866472751d616964ebb2296389d16cd346d67d20a11da12bb80dcfc3d09b6de8a494dc39788dd9780fafde080a7148998517381aae05454c249555a910bd91b65f157041086ac9d0506bd4da32de55a64d06327bf8d225a41994c279c04fd051ce9bef0810d0ddc1b4e9dd1618ea9657adb8259f7878437cf583e09ad02b86fceeeab290cc6b812d591f7d4f9e9143d55610fd20932c17bb39dc950cb09ffb1e40b7b681605de2a915f84dccff50541d34f34f568ec629df5c4b185df04b95ec5f33f182eb4e25af99f91af440df26c6a397e990b93f7c441ac11ee8b0ec5a5832e7b4aa073d0de29f59afbdff435aa08384ccaa65c8f70b36faa0172020e4c23df23f5d489bde5f319ed31f55a85974fc5330035939bf3094a9b3c4f016ce1dc1b99076516849282a50790aecc381f06128fb588e78da1ba33ea9ca60dc246eb01857d17db0a6d669b08c3b3e2967851e43bb886cd33cf8828f5d6a8067147f9a9ac544c315cad56622bc9ead8b69afadc81fe20b632b4296d45d4580aae69e644e6c23069ffcc93d8cf45c7205b775ded3032bfbc2209e84740bc9d077630f79c3ade66ec0c9c4d7c3bb848fc25225bdea462b00e1b8bbefba0b50302c96c69ac2c9c9e57974a733dd2125e6ccc395ca7c7a87bdc7861a12206098900aac2bb5e974bea2f90cfc592f964b122f5525bec0453ae8af8cb7896ddd62f00ca5fa63d050e757a738c34012e18f327bbe622dd30f6e4b54108b9b8f9f47c0109baf7f34fdfd95a61d4b2c7f3c59fc5ecd52961791f297f73d7623d525cf90b5bf0cd7a784f1e7d8eca92e509d4feae8f556b1626cbe8c047d5d290ed01a90469c140a3445aaf633fb2bed3c2b2a30312b98ec18fa00881857373c6a7b5510db5b319c4afe634b7c56395c5447a37b32091ff79f244290569847d1b5d1d920efe05b3e8ada5e3cb3add037dbcb32f2e3bb144472721adc042cc8906ecbbbc0c0e65ad1678b619a6148e6f264379d1dd27545fb43ff133694b0104722bda9d0954c0d5cb4d4ad62b1acedc844574016f914cb7261ad693a398c68664760a3d07acc185fe4bf033f50338f410fdf64199231521c2cc907c11ee0b72591f30970c7f6a05e89b205c71091cb66718438b5d7aca900e0a43d183e81bd2c8916f85060fdf685718e72f06950f08009fa7fde9a4edb0905288b49c366eb2a74b74f9059b8790114ff22c88688fd88098790cbc5d9f7855c9f9c4cefcb3b2800e83210d8e10864f1e5b8adc1427b7769d0fb80050e5faf4e75aa2871f97f59eb7e3ba0bd5eea6e4ae1ec85148e1c6535c1c97902251a05ba805f9716990785e400eab0ebe6a542869fae6089701e147c3a0f1057fe3a8740e04371b47f658daf896190c1743591cce483e4a95e9687647e5d8f24e58ae184717b3db9dee01449bb2e70fe5ed585a5b1e82dc52a25ef942ed8610255f29a5ec564a74b524952548e19606656f08578dae8a9b702ffa20959ed2d8cc72a904bde19ad490720ecc8a5a2f0079245cea98e69cd0b5fe83de7223f87075dc27e0240efc7a8c4054c152e3f30e4e83430bad0b9df601f1173dec4f4a41fd86c9da322b278d780cffa9e255c8053c43d5f6c29271dfeab06d7c104d727e80ab3487d5cbd7489d347a547954cb06439e54d365a7f0a8866ed10b2c24872a5dc31a92f15c5ba43b643080a4d33c0684c9c6632c70768b85ff9e8e7afebb6971c04939d50a272d6824a176b98321008292850bde080906ccf687b3098fd7f1a674c3b6527d409718f0c2715285e80eabad6f381653c4203106b6fef8c313ef0467637bfa6941270e7daeea13fc66091a8e36185b216b720de9a791940ce5cb12bbcd0ff46f870153425ab6454ab90baabf1450feea5338a34a6bd522c6930b9c6b3449ef6dc8c03722df572aa67c0f9c8efb9c8b7198ea5330e2affccdb140a9f826c581b0b027d07883578634670a7e061d4bbdd58d5857ab0c9d986cf306c4fcdcb272d265e075bbcde4c181a408b42476e2cf978b3600353f31ff6e662b3944fd847df1a8a3473fcee22f7f0c040e7920dd689cd49db14cb0728c95fb167ce35a559dab70cc09a5f452c6ef1f0fbacc213d5427aa83d2c63c9bc684131b4907c996c4268a13a7bb0c8d5f568c0434c872682b1ba76a37e771449b836ef0a3dcc735a4f7b1f6474ac0bb1f83880f907ce30c4d53814b3ed8301e4f80e0dfe0f93b02b414d3ad092e0448e984700e97edc1d530054568013e5b75a35e53e31b4e1859a45bd06a8bfe2b0529e3ae0c247755958b4017459d25c7db42eee699e3f5b3662f745643f951bb4212687006ab9547d3d295f869b368fc2d79da4823985e3c102a08f3c90fc2279cf7bccc07a10fbb2ac32008011503d73f8a1350928dc17d99f9ea2a60e10c1849b1d9db011b46ad84459f09ac9b181591d7c812979c60559c71f7fa297457e23fbb85e7038380cc22aa0bfbc334d25edfdbc557aa132f3d6eb5c81b86cb4166d893369006b3631002d920ef7d77440dbfc90c73b2e924e546d7352c00047309f68a2c960fc834a9aa81e81cba608d4f67351a6ecd4bfea5975c0718b63321fed600d96204559d4283a2dccbb1c69c9820767bdad4fa1f0ceb7c2344789ba31bcf3803840d77ccba76ab24e507d792be01684713569d0bd89d18d889775ac28fb5afc52c096e39421d23eb728fd897ab9361592d32623ecf5a9e50e30bcf38740c0c9c8c0a0159d691266f9d373392883203fee2ad141568774463270aec890720233d050568398d1495f04ffc7f8ab8c5062e54baededa2d6bef603948bb45bd42fc9e906e21f374dd4c8e3f9f6b6ef6a25f2fddfc28cc0a26650a427cc1d2484b48d4b057e673d61e211a92a1476f8a49409d1888401bf3ed7bad2bfac236afcc2f7dc07e2d6a1ee882e4e1ff2ecd346b62cd7fa96cad3b8219661deb2cce2c96d60f201a7ebae5d5013b888020978f8139308c90e79ecdfcb659104fa9266a6ec9b2c02e4ff5ba9618a02c636e01c29661d6cc1de76c9d635c450c2ed9b9f41d391910d0717729c780f064ee7a1ae8b26cc9c3e0d0bd7e0d25419d5826aca18a4731e0238cd2afffa7baa3c10701073f6d9d474e8d3671b7a8b4f36f75ee984412c1e02cdfa21bbb5061a692c7d7f4b571aad1247901cc7567668228fc26de45a3981022474182a3eb16e922a00f54c30a44344a1a9b96cc839dc849f1b0723a3a04c05fd65770c644910c94651b72bd8332266aa3506f62461b9a15b39bdbfd368110bb124f08dc469084f7e9e132ac61c33f2ac7174e51d3a37f7b685e2a57536240302b5e58ffdeceec86aa75608f9b7705e55040a165a021d83ec058e6005a6fa0a780350f3dd779389b2a7e8926a8c6e2b7394addcae0266bc76706954489c0c003dc292098e5c4a49c12f000b823da13f5e15cbbe2c1553bb53d84ec4996cb00f8db5042d1c2c592be597839dce5b1989f93f4a7c30bd0104ab99150519b41e018cada1b7bd77724db7551d20ffcb9bd4be1618dc95960ad2b2b5ddfe405b520736104c1245e15b3792afd4f70bf24f20c5084978edea150818d98b96afe03a0f0019c4baf17a250276d745507085ba3ccb0ba4a72f5f9e413d1c82b8b980880959be53f7bb88d06987a6e48f14cf88444c4f082a1639a4ea77cb3cec5f593405dcd8840bbc906f2afef9c452b733311c31fc86f71c39f222ca7f8a2733fe3d05d47905e7f9089f9e279c89a041a3d62ddc751fa179800932bb53f26632232706f1d3457801391443dc5ad2df07fe3c9d191b02ca459e0c30a73fa3bb1899280c0078c67117226c2ee9992a13daee82cddc136fc5fd6c5b1ab2e76eac7003c304521da52df2c5b1b613ceda50549e589d08870d26cddc46fd91df60aa731494095416779cb7927bc3e82f02f01f14cfa2b5f838cb46be26b497d799bd5b945d032085b5a36d26e797266f12a94ac496a526747589a5a0bf2aee4693892715940abd7ea0167596a6bd5b17eabf3e0d7bb0947700c0e1f93aaefd7b4ba09ae38c08e7253f7e5fef906a1aa77b8dfbc359110028decca1db222dbdbb98bffa4faf0e4698287fb73a159242b186d7333af5ba773014e4590a0b2df739f34925778a0e2eb3ea471969630637a872abb5c7cb6f6e43a4f230ccd55f29377a1ee945fd01fc97aff901da84bf65a2ffc68dccc8c6ae80d7e4eb97650fc9d793276cea1b07dd4d6b8361aff22758e78fa21ad5ba314fc71a514fdba96117c7975cfe0d8a0cec4f89f12f5224d480c79bd55a7210a9c7801c026ccb885ab769051a3fa94a0633ace23b654a26e77c8313ddaa82dc2155f3e5417a1485c30d78a3bfc25ad10a93a0277d24b07aee0407d4283d1c8666fe83ba22262ca39655be9d3378f0af044ba52a9a72b01b20efc515ce65314d6728e50775ae83c16e7a05e58bbbfceb0d2990630f425232da59c19c6b020ef3d51e855d756bae6c4d11205de62efd730268be7cb5c64fc50cc0f8717219f2eec52a7cd06d4b3212fea635ba138548d20099d9d806ab7ea5309e5d76d35e4a27ffce2355488abcf8efae2b94a8715b6108c9a4c24178b694379be9ab9aa317fbefc2e3f67a99a3cf33def91cec49ffb501605fdf79694e12ca5a51b3398935e5bc195ffa77c09ab164126cf19d193d97062d610aff995c3dcc6410dd7258298c71434529215fcce74cc8d70d9018e6e00d707fdfcedca40050908a5c977b1b49c46d45a79fcae9d6134400d9b294e8320cb97b2b52496cf80a1f06ea9c7178d289d54d957ef8198152a155682da469b60746b139e6b52ce56ae931e8bb2a964a2584977936383bcd8783ad53eb1f51c008705be4be7ffa7ec6f055e53e590d0f2f60ab705d7f5bcb24f8d2168282b8e3031a338324b2a38d55ad44eb4026ca5adcc45f6e01aad25858f17f16e7f3e1880e4be40597bd72caa0a236e51d6a1216be205cf05b66a3dbd02d0d2fa507a3ef0537429ece34bad65101bfeaddf6288eadb3303e4c4ee313b7267daf20c0e9a301207e9f572e9e37ef376f0b1019d493e5f3d7bbd50467b8cfca2406902d75ec0579b6425f4361ee8c8ce4dc04f045995bb74527501f885563729aa9c84d18a10a3d1170e2111a6fda3b4f52cd1ed9e4dec02f2c81a0a1f1c6f4601916daa4400b57a507ed3a677ec6dbf95dbf7a5ed94c0f933124e073ba5b959f3652d27e6b073cfb78982410780c2300f564c0b4e689530a6464508f1d52c361225f3403b909674b46560d717ecf83896f2d7f9288cfdd34cea3df36bf12747f59c8d176930f1fbf4a13694628c4d238308c5577b0d42c1a7ca9aca6c623e460c6829de02c097ac48ecfdab55f4f7a43b0755749b792c36f2083c35b51e2af567038cb5ce90ae29c99d8b0bcc8d1d72c31f162832c8b8289ddf4ff393e790f7c49504be78e06cd638dded60e3fd27815e03ad7e1cb9a4cc2c46df00fe0719ab0a4f4c9b418065176e8126924f3dd323edf30c99a39e60e89d350604cc47554233e02e562590696dd7fe1cb307ce6d94d5e63f7872f751a8bb956b111c5d02efc949165f4b700fe766276c37bbb7d2075bbc2559e71cae5e42729f1419cc7f34292b8bcfb750835395b7271f667603008f358a423b3effd543525b7cd45a9e2d1342619f0580520051fd65bd303470e17e6b57a1de37ba1b4b53e403ef242252d60a7b626d2094b0a6751bdc36640511ca8aada38d1c80e97b68b9daa85cc04cc66aa48282004ddb1e09a179c6a011e39d42bb593a604edead19b7283cf93625676e27883590e0997428c85e25f69bd82ad882455b4af8f461b1074828bff4e4b4b2e4d25e706a113ed83382e08f716771910f88f3cf3e9ae50c7591527008482cd91bd0a4808d30e2258961fa2c08caca1288a766f88352bf1ba5bc383a28065ec63feddc1007e259ec20eadcc56a9256aac2a24f4294fb64ef8ea0d070ffeee714f67a3f20cb41582b5d72012cff3443f3aaed1a9bd2b83ae4005f7d1c7d6b48fa747546906d5186c7d26a5fa4a8f98b2bc74a541bfa0ea13073fc3549fc559046efe257e0ca2d4d75805e9a179ae82ace920c95f817954db949020974c914b5414423dfc0f440f240595f3d3d8e2b596a02beabb0a98a41e33b1c1de7f93f1275cb8395f03c0b02c94097d4b64d805029338fd15018e6104aff703450e82ca501023e29209b10eccce95abb990080a662dbcef3eda5ba29e37953583fe398c345fc5399d02be48a37bf3bcee1e54f13951099deeb0a95176225487f5e0d6fec0b28045a105fb739ce47c1a7029e70f78d1e9558d29f91a974890dcecbfcc38bfd9a2446c0e87b0f2767b6ed56c8e57cdc6bcb2cac59ae9e1996b11b50f96cc0f083bad6e0085270b9f0d882926588be9b46b2e2fe2528fac404301488c1bbee3fe2fe85b015406c34ac576e2efabe850c4f364dd4f3219c353196251ac4fe4fa998148b00a36d304107c7c64b4b4e9b0fe6c140638be41527302c0acb9fb51ccd5746dd20c1b7538618d428e2cc35d6e74cfd8a8198d8f193e91c98a9d56c3f2f89cf19002c8f96a047328e1f6f68e6e510e50b09f9a5a847fbda2f922aba35edf111f2a0983e741ee2e0cc2476bb912c2df27fee86c8c0a2640894b578a8b67fb3064a0090233a8bd6310a39a6075cd752844cb5ee6f924b410a84b41542f17d88af5010a3b912e5e37bd4ec0433c6429e944b0e1232b4ecde6bcf1b96a6d5104d770c2044fa4d1c3885bc26d780fc66a322353888158d18f6e089af0b02165d6f72d0006ea36ffa5f86627182ca1faddaf9e86d85521e3baae571bc0524987513f0b8406d353ede948f9920c4172a12143902caa700e7ea9e2d91c8dfb681da16ac0240ace547c18836051f38a34f8c406ed36064ec88bde6712d7e6cee851fe4ca83b0fd6536d7dc540a442fb92a96aa0c98f3db85ed731608cf86d7329b8766bea31053204e0635f65d7d55b65c76a1100ccfed34ede7a456872636d6ef547040b5e040aeea6beed27a6acf5e4a0f91c56ac29b819648959d56a06e822046802687e0aa5c4170953888b62db3b528ce3e2b4ce9d86b3a6bf4c190d589cd93865eafc001170fdacc5fb7dec5df5254f7c5278beb2bea0875b7043c4a5d04bfd7bc4400ce0fa5a3f133e33b4f66728191fdcf2e9994773a6c8846a1f8f21fda4993b2c0378686f2cc5ed21e59afe37f98ce0743b92143c0e83457da706eb0efa82fef60e6d0e4e72da366d9eb614207e1a509789f414d1b0a05a7c8e91aaf1fd4550b40cc0d59139e181bdae41f9e510a650014bdb24df5d9b61b111e408b28c9ec5b808c242393af934775d6b7cb48f29783e07fecf21b3f7ece63b712869d5c6dd9b0ed47ae62af3517698f33abfea850b1f34ede4ea89bad89919e73ba5488e120903db1ab87a677a101a03fd2cf2031bd5f27c943966f4c38bdb2f8ac3b85282260c1fa7be5014d236465411f906db6826391ecd77703442c294af2c6c19ea347a0443944798199b945a432025b3d8b1b6e07843e0fb424d8677ab849c2287dc3d0259d60a342d8607a23ce03776aae6d5a19db6ff8868071b9b76d90f29dc077304b1a82125d513992687f26d0f80aaab57c924035843514153bc3476b19ac7700093c48adbbcf83357c4d8bff882b466e9b49ec773621bc22e7d6bb9155ac0ca003e4afd8419fec08143d5924585375929c10c9f6614613c18cb86e9c32257f007c6d3d556bb6b80a269af77e665c01a76e260934b35f179d98a2c03b973222b0a7a48eb439c17d011e66708e35f8302d8612af85d4cb941ca4feff430fddef001155c30767941cedda2027618f381cf77a89801f8d8ea38454b9f98a5c330550f3a79bfb010d0e451f7ae598c143c69d8bb400192d0c5dbdadbb183a08ec727019f521d7aa990524faea91fc83950380d37becb055809c177ab0b28cee792f20e6744493028bbf4acc8f0934aef8a31e9c647a5948742e694d088faef8f5f10025ccaf3eb9e6f00f652d92f376786066b34a56dd2a96b7a990cc03bd5e3d2670bc182ab95bc9428782bdf3d24f8efacf8014a5da84cd0d43d6c55b799223d970be2610fe5c0a06b48fed19eb8791315f88221416c193fcda5857dcdbac38526091da71286ff9f828d7dabdf409014b904a9ffa677e0f5d5c0ce6ef97f88860a0bd0fec565ad9bdfc070eb1aadfd81995549dabcf1ad2261501808b6aa6a2b820f +generate_ring_signature e8020add6aebab8ca5f8b49f2072f631745887fa205d8db1a3aa1b5a4e232d81 dd963b8d2145bd369feed2501aeac1de2fa16ecb1b7cb93564535a6dbccbb3c0 2 9f1c652d42539ae6c7a43a5118d5890a4e1b386b3a400ebd8f2dd5687626dd55 90a86ceb7951f7b5668205c22e8d2c9953d0e44773f7d0159618a8a0238a53b6 5e6dd5608134fa4c64007aecaa340a0f379d1a5c6d4adbf017a239eba303c808 0 a6805f5877b33f74a206d4092d7eea8e2efd63c5d12a63fc9b32e4689d846c02b7c408786c434e9f21003b678e952d6b863c2557bb894fa8aa157ebbe1f2c107c239cc506f5fdb39e31f953d45b3856536ed5eb90904e91b43ea15aecede950853d506cd5165102b11f920a9f25058836404ae1747fc8d1fb20c6837b7fb8107 +generate_ring_signature 3e592f32788ec54e3f10560b1380761329df534096ec12251382a96f8cab7eba 0b72892038d4248c430d1ac93f4985a8bb9b43fdf457a83618b7045980b8e6a2 1 526c98024c9a4fa8c6ccad5101f4afc972fdeb228620d774669c13ec4d029cb7 0741f944319e0c4678f4a414a12c6956cf9d4b3ad04a40cd6030e47ab7e9700e 0 62661198eca5aa9e2747f9ce4242ae394bf740a7cf8b837e27c7553fc8c27b027c8f99a5290b41f7f8c5da350cbe14a399abb7785726a2c3db0f291033a54f07 +generate_ring_signature 701268c079222197470218f73587bdeb19c3620f25d1511042edabd0efcae448 c2b7340137942802b68be0a9cd0deb826a43f7645807c8f4179c64b11021c36a 1 515cc1cbddabcae11062f52ba676265114a44372719b9b5dcff7865cfbbf1568 20966d94502e95dffb45bf07173991a5c0ebca48c0941c8c6705f7370bb2340b 0 6379173fb83310313f391b13b27eb636613c7676ccf5fe73683251ba45acef04f64b90c1f8bc81199d8afcb05892025a39cba255946d68ebd0b03d3fa6dd9407 +generate_ring_signature 847a7cdd3eda4d087626d16b975bd8dea19bd914d35051759ccd76080649a606 a05d0004f793a217255613e5b533efd58bf1d00e9ffe7d1062aa6eaff00bf614 29 60fa9da64e6873d5585d7c6b445aa994181e460fcf9c4edf9c2562432034c418 319cab68723c9c65b13df37ec1080812e182838e83f12a49d72aa8b82f8a906d e0d78f852a658ba8e2812978927a42caaad783594e463b73d25bad7ebf602a5d e5d6e07c783c40cf86479b179ae2c3064df13826f6ef66d1b5bb458788f628ad 97d175ad6b1daeb14df659240d2e567b269604bcbe62615efc1acbb3ebdad4de 5915b9e450d09664efa12f6477a526084e9a1c16c814f366e4175cf03f0bdeaf b08b9168207c344ec1f1addf104b06f4d396fe0a703c6fa22676549e221a9819 e28dd982bb083b405c5888a05b7ff932026a2d163febf55f7f8425df7a2381e4 b6cc0bc087d836d4e29e5ead0bfe51998943e147c259a851e138e931d6ee2cea 6bc6641ed933fd309190317c1b43bf34a7fa205b2cfb041c9f8d51da1775c09b 2438d50597d45379e848462033a037b7281eb6b3acd259a5190d9279e79a3d15 eec728d14ce0721d82e552a13d446866f3e43c5fd45d8459f6517aca1f703e38 6fbf0ca4da28ff0d2457d63f48864662748274eb304d6c22e3bfc83296ae0deb 51aed9ec8aaf554276b3397a30f18f76eef129585a5ac056ad9db42d6b062ab0 6d3b31ca3c9fcfc03be971f1107ec82cc5f5be67a0cf280c899ab566ee3c3cf9 acbf10cf496ccbba0ce691cfcfb240d1392c583288e0dca2b562710f0acabf5c b227447474e2fdbd382e6dc62061f4313c040bd951dd46d380b1fc4b8dfdb341 93ae8143cbcd9604e8862ee5d5942e90114e5133fd55052cd2d9c266a858dc85 2d90f8a4ed2e98285e9ab6938ef20da123f365b84f3b269b3ed03dc885e2c86d f7a9bc8f7901886e7f5b6a62c0f09022281d213e043df961c9bc61554ef4c8b7 58cdbcbb72356615330903011bf7267c8f2d7d61abfa88789d43567e51c7cfeb 742e0b7bfc9382859b7531e43e11e99c1ec1089ee901d502f116742194f78534 bed2b49b3170073561c62fbbcc1397c1e82ec0a252299234709a02b5badbe17f 6c7802b9d92c53b2b8ec8f761a260e680f92b4577485b5f516fc594984381047 3f32e6f5e41eb10431e1113916f94976f4fcc9db76edfdcde37cbd20a22756e3 9031e2a54db5f2d4358cc9486e5b25626c40afc5c43b20e260b0bc301d85bc41 02dca47cae77e6ec54842ae1a80f341adda1e0f5f78d016a06d1bc5595b7574b 65399dff20e45ceeb08f30f42a1e4f45c7c086e2a5f5c30f0645acea610cf4d8 894d72f759785abdc31f519d816040dedbb4df5af332187e9265eb1d4051da06 6f6da025d6473c5c0f30402add3b401e177f144bae5f907af5febe3ce32aac0d 7 11c10414428f2bfdf99191225d6767088b905a4b596dd124289af61b099d6f08d47e7b9a89e2812dbc0f2a49680edd181e2bebb5e15fa87f01e9758ebee6350d8a5d984a00b76a6c82fdd828a0631b4c271befaddea4ec77982da720e02d5e082767fbf8ff0ffc90572ee67f276758591416170972b6501bc83a009286d1cd000c6f7e3b509c26589d2782cf5e556c4096b68c61d72043246245e57277e240032143c68e626a892e073bef554aa840977d9e637098dbd3c149fc92db141d3e0ec4ae1c4300b47617e692f63799ccf08faa33cf444dc33889a068c8112f11220a489a634cdbc16ade92da3bdc970926336ff3c0589a495115eba06d023fe1c80edb81dfcee82bec113d7e478c54f89a25c4d87b33b0677a08ce1647da9fd5a70d85f5b6789e95e8a9c5c54d1ca3d56ddaa0cc56571e98dac6ab82455d1ffdae07c232de878e49181acc0b2cc33eb51d02d3540cae91135333d96b15e47a1ade0285267963070e9303e693d1f1cdb799c045a80ce380cde6a08a194e69a7db16055c92773704fb7fbf4929cb25623be299b398a6dabd6d028014541fc54613000422bf12e32d5fcd899aa4600b0c13e03aa95b26ba1f976b6b33a735af0a94e201b195216e3e2c8bf23501a74589f55a7bcc0370fb7928c76d44d493576d745a0e69f792b234fad8d472ea3f9570fbc297759d70f3673a6932a36a8d42ae3a2d0632376ac191dcdd0f9fc25abfe6ac8e5720f0ba12c13b473da46160be19e34f0b0d09e59545bed2e5b5c2523ce95c2e8ade3ee566256ad9a2dfe68e044c28d80721dcab0a0410570204bb849d12f5796e5bfe4b826e3c0bbb8210fb8dcfe1ad09a77969ca81e43525c5e9de8126cce2d34dd44dbcaf9807bd48464250ca18660161e0c2e111d0682be317670b272d2d698e6c4df1af8eb89daf6975a1710fa80a43bf38befdbb32382c28495af05e15623b6a9c404835da42f6d908d7b14a7b08a136dbdedb979f2b1b29cb9efa5aa58a7ccb839481c505ed76acbe83ca444f09cf15e2ee6139a4f56482f974ee9bdc1b72b315b31a8f8f40cc799902c93f220f30efa8cd4c550e6954e98cc93edf724a803e9b1b7c59c9f3dcd2476a5689590cb0a9e07dd0b4671d8ea129ed9f6a8ed7bc182f549b1dc87669da307c062088050dbc1feb24ef1ca535a8e0d16aabae5aaa6a92edc7151365f61b8139e3ce2e013cb588a239717d7fbef9d6aa0a46411dc3d26ea264d9e579509e45d6f7ac430e891401287e65713cd8939e85b4bb6eb292382d72e20333ef1e04feab76232b01e2b5e39c6d76cf8e678781e13cec166ee0405105174738b535d75f49b133f50aaf5ea2e08ac22474622cd7ab47d87d9ef026f3b4875f9206cdd0b4fb67e7b503ab99d29c2abd8a57d19c3b7225fa02eda9f53479188a5abc4ef7c47b17b0e20c7b8ea2ba759ed56b60a3bd77d0cabd871a0d90f5dc4687e1a91a56ba5c8aa30f9455d249122b3e9b266b251ab7d3b3560bde4ac77729eef6fdb70ab6f0f3420d4c66941021d6b0f888459af5d8a85dbd623910f8d0ec9c8d8996e4197903140a18818b582c110b1e17a1ceb342e4a042344b5ba7e3562136ea695d671578020c8677fb7b87d8fd1750429ddca808393f73fe8d01df03215de4eb0f594f92a605212e90f5ba99ceff394d935c4343014f0d25d855f53510e3043ca5a9003bca09dccd86de85e691d62a48402c6c6120f5a5cf64fce30149e569e01a68b42940053829fcb64a97a4f5b72930c8aa53c589b77a7f1a049e8ae41db16e50098e5006a3d97942750f7131ec74910bee4355e9532bac1a8f0bde476a70a2b7e6105f0656bcb519cebab51b2b34f3e48790f04c981c3424e626a9cee484acfedd42230935e1ab4d9f09860856fe1a3938f348008aa004a92a9ecace3ab54e2dae9b27026df7dbed07e7cb94a78a9c1c32eee7b54c81a80b8c3b5026d55bbf0fb561480a1de994b35ad106b178075e375cd701082883d6d27d5759fa388307562e8596059913b1c893e7f3e156b98aa3e7c5f5fd2a86465e069c36bf90db125b1198860ca3d4947fe14081d916ecb615a593ac25024aeb307eca1532536434fa1d139900e9050ed7af73b9890a34b9e7acd17cab1737085a9fde11f66b54154d62c4c709f71d041f19618482eb9baea9b500cf4d82c1bfdef1808e692d634c85c592670a8d78702b6a6fed879fbb6a6d95ec70dc4e1d3db9e47db31371b54ce682cbb90f8b364e970d343b3bb1c324a5af0a29c7ce439fd8b75bfc95c465623b5ad6c907a64734e690bb24d448353588fd9390a3af6f793aa4d9c1a2892534442cf8aa059d11f4bac838da49957a15a224626d4ed8d6a4a5d45ecb10e6ddeb9a969adf0e33be898686ad6043ae392e01a8b5bef2076064b91d998a8c20bba36e25bacb013b8dae6750a0f85bea108f5ad2ac20775cec6e45a6f5063dfc228295f2f72106efcd080aeec05dd8b026a1d9fcd673238444975d4a59331e991a85032c45e106b02dc8eddebfe9330263b407207da6a304c02a27b9e8d0f4c33f59160dc8b20b93a58e8e4d0b3b4fd661d4f97a0b10038c863a4f3377db58d575d1b0ed523a00 +generate_ring_signature 21b7918f07df71bb61eea02d6eaf44427254d6929fed711b69702a07f2581ee1 2170c653c773adf0ca992a320751335723a37f418473b51e05f39f4c56ca0158 198 a7310ff48dae857eff6a14d2feaa21e9d2002f36130b16460b6c39a4a7f9fcfa 638c6ab8292f8350eba3e282099ff5bf2157e249d6d8f4817c1710f650107663 15a1047de62574f925b50199dab771affb8f588651f52d19fd6412c40510ec84 fb40ef2ea3d9c88142c8014b364e0e0c7a3c588e5f45c8588e54450d51b426aa ca504e824c28187e327b5d7ae7199a246f73858407bcade0f3ec9b4d51678d6b acfc1fa1a602a4e8d7b40f5db21b12cd4fee3db4ad9dc873f6b2f835573ff911 a0c976e070ae6c6ac4950b098f25fd6152cce78b02f9c781500fc12714ca63a0 4bcd175516e2d4f9b189dc916c29ea08625a10439ea0a81ba3ff909ecf188edf 8c68075275c103c3581e9e0c3b70687278dc986cdf603376d8257e4d5d95808a a27f6a62d4aa751c9240553e0a3997eb518033004ffad5ef037ee51358bc6d2c 738354933226891098ecc542f68555f2d16326e5dbb8f8c2fc91db0d93db6c11 37b94158c61cbbd44dca406bf67c8d4c13e7d0c8c7e9d6794d5d29f576c477c7 dc71ccc63377eaa777e6f591ac890132d59e63dfd9fc26d1ac5d90e995fe2bd8 a9c3655c5e63cfaff27b9b7553d34b8dcb632d8d7eb60685904c589665e02996 35b199d68a146841cc6b62ef32f7fcd470c6ba3ac20bf4bde67ab988875ca553 7e00d706b974eb2cdec2dc4de1e71773dc7c7dea234749b3dcd9cb6ceb3d68f8 515e89a78026305838e4e059b0ee9573c61017b25e92bba60f61ecfd06118880 9fdbcc0a4a7b68abb0cc94571b407ebb3300c1a37cd1d629dcfcfa8ec982afce fe328fd284ef8f8ef08babe0357c39c1afc76657b3fb3e76948c546dadb5ae55 473046fbfc71a93af79fe516e88dd2cd2e4a129346fdafeb8458a887434020be 76025b240fd55cd3f0d7618cb987353c22ee6ff017f68aadf246af80d8f53a1a a3e4ef02949475560168d74e757d636ce95390013a9f7598e5751c785a0a3558 ca46eeb8fbe1b6f71eb46039987aa3a0333b2423000e28c8ef91bbf96574b5a3 756fc67e7addf14dd110af278b6025f47b721604970da8af89988004a74f30c8 430ee30d84f2bf7dd3d73f6cc7d25970b0da0e44b346203eaaba6b87c8d1bc49 f0bf365b8ef1c4af3c1489f969def37f165f60bf90c4b1e7d1e64caac02e0d00 90d8d60b866aff84be78562d69efb7c75ef8ad43a6dbf8180c7f3bfa55385f42 81fae7a46b3206690ba6592ec7ea31908b6b7426eafcdf4b72a7d867db80c290 47dd69f8405178eaaeeb253b81d9854ca3a5cf4dee853c9f09e5c018a888a990 3dbd9c96886f5ef5ea553af02f558c12699a62aef89f0959d36c11b9613edb5a b6e92ea527b2d00198bf9d08c2349346bb9d2e52f9a58f6f21886762a8bee77c 5137c2a7ef939c6dd71c516956c0bad82ecce452fbd92d80ad2f893d5db044bb 83b98b57a098b4606091163dd68af52900d995955054bfbf8dc5be2e285e67da a34e7a7bd0709f83d3e8edf3f4703e8e78f5a8328b8b537001ccd78a1fea0099 04bb7b526f22df2a7fdd7dd6d96a996dfc840d3c52e51407e6e02fb48653c17b f85f3534c07f3ede1fe71fa8ae9c41a7a6a8ca0114807f27c3a648e67d010c0d 30772180f96ce761ddb6bba60e078ad2697edb3b6368fd6b5532cb556a14a927 246288f7cfa451ceed0911a552bb87eec77673a9ea63868c95fa1a5cd2595d28 5ed823adf2b16c339daf9a63307143086bbd5d9bd48ad7884e12342177b93873 98d730c7b3ecf9a20d36270912edfb78e51f7d57783caa5f0fb73aca4a98faac c9b1c8b87c927d1ff20e8e11be50ac120ea712d56c79bbd2b0b4a7cdc380f0e6 e016f3e3bca138e242edf9b041cc9fe41ebbd76758fd4648b617c8566227d19d b4785c29d4cf5ec8136a91d82b37f00d32cd9f5c0cc4ed9f98d89e9e12321501 d7530c1336052f7fbad4f88f42f7b93a70d824ffedfc0c677b021ba884f54a2b 8a2b6f62da42c1c2345040a426a328224b6e1dde8046031bc38aa198fc946495 8ab5343f36337e7292f19f4f0cf0ed20f7129cb685d46960181ef8ddca615f6c eb77146a3a92895571947e4624b52f76349eea5415a1f0b64cafe44a1d86d427 144403c29b2301fd621b2192d294770c43271c4b1a65aa8bed9820928939f9e4 6fb15a7b933b936a2b00a1c574f114c8f1fb55109c0db8fbb1f6aa0cedd24411 5c098c6ddcc9775715a8712cc941dc760043b1be592cc1e794ae2df2674bd12e 3d3aef339088b60d7de300b385eb5740bd7c45dab071724717e85d10dfac35d0 1f965c8bf7cf38446d5cbbca496b9f4e1316598c9714ba751abd6279ba119fbf b50c8380cee5ab2db21d19d3790fb04dab6abc77ff660d9249ba511823270917 50f3f18353606ecf073b404fa9fd25dd89081fb2558fa739fac549332d81f426 b317387602d238b8f8e60dd59cc63418f3c911bf08e27e743802a6056d7dbe6b a67b3f52133c365193b3d0b07a770ac1a9ae2f968357f405ec0c5dc764d9e89d 51fb034fc77c16548c523577fb83026394dffcda641352e07f261046aedc29f4 cda39cdbcdee442b798ac1c3eca2c9fd9029387c5b9d6e4931ea007c69c1438b 5eabf6be3369f04af85cc3d9ea07eb7d36d5e8c917bb4ac82f65f783087fbef3 b93813588e0e1c30066efbaeaa1305afd7f3bd335fce47e11eba8613c2332277 2ab4bef429a6b5516c179036065c2f051e75cd0d39f5be376db50fbe7d7b524c 4ba8c45569fb7f9633d85db9c3e614fee637bc57f7d8366cdf43384b1c07d5ce 1c6be33f9df30233ee408d93647939aec0a74010acdcb1aadcc828378bc2ba7a 3a5440df19aa23f49469bb5a49f03e94dc8e1518f9acfc64d2992a424c3ecaae 9e251605352bc394ae6cbc7075e8d83fd2c7b8de90c327a3c3682e1b146fe87a 806ad4707c67355f8cef4711357215018aaa16178497d0b7870c27d8319772d8 f6ef4a8771330a3b101dcda998edd72efb655dc5a061dffa72f20fccce62a05f 458caee8a3eb73594db714221608f3ec2e8307b7bfe4ae6076a695ecb329c105 430bf3feb7d8c15d59db56c928803fcc586b1388a9f55864c9509af0263b1583 3c481cab8ee77c6c9182c23a0813c673b5a79f9e39ba968911926127b90fecde 354c79b857a63799189c3fd2ed192c9c5ad45649389191442c4d5d3d653430dc 06f2fd30509dcdbeb9f1b1dadf07a2c1a0f07f0c09ed89eed7aea5d177ec4293 185158f86c6ae379c94c02a972d6a2b7b509611c01fd464e42d1f4b6108312cf dc9c395819a1a1a4d441c34f826100fa164d746dec8e4ec67e2957cdb66bf496 3b2180d54f65060b21d0e3908eb9e61d51d401c3dc6ba7f9a6b6601ae9fc25da 1dfd4f86dab80474c115ced9f14e9654b8c0bfe960d4783ea749a04af99b329d 2836b7ec30ceff00da114f97fb42827adae4f851c960a82f3afc7649b15f9af0 e2f4901c3f23a57de9bb273e7c475a6e95cd151b0f124913ff8793a54e86b00d 3dd28896b87d391d35d51a898a534d20f08d7551e7b6e94bf1620e35016a8d56 29dc089a27b31456ac0015c122b2f01fd3c2df6a7198068ade70e56790b5bfe2 5f9f159b1955da1e199020a276d8775f5ddd405986545bfaa6d760be0b4f43d2 ddf00a1730b611dcf862d320af05aa81bb4f514d36c7debb5ba42e098a1afcc9 68e9c66c54501f39a8a15ae6ae4264acbab0b5d80de355e451d5290baae5c5a3 7b408eb753d2fcaff19e4660d42d3bdd8ef59e2064b85432b324090d59131996 6b0a02f9766c59032201c2c5b04453741921cd65d5150740d99155d2dca6e647 5c06dd72b563394a34e43ac353e54551486d2be717327ec3907e4eecf84f389d 416ac45ccdbcd8b039df9243151e7b150d9c8d5ceb663049bddf4fdc5b13d35c eea9d96242a44976de1526661b40a94932999919479b04d5c542b354f7fca5b5 2d3473d34a84358e2088f6aa6d9fc1035e3bc513ea613fd6604589f0befc56cf 33f7f9c42a7a1f400f64885a3b8f754f7a37cd240568ed89eb4b3bcfcf627adf b10bf05f99f3580191561caf04194fcdeb7b2354aba272159f16dde7688a458d a191c822cd757e136bf17d7b3255d2fabd55b92fa07c3fd2e7769290ccf759d1 6dccd76d0adc94bdc5a3ef4530edb326e205dacf527efb8f46c7be4e21ed9a0d 6f6c9c24f0a9dfc75ba05a829af930df9c90641e35647ac8d9dfe3bf162e345f 4bf557d5904b29c5ebfd6e8bd11eaa8c1ab125a5617c0e26d999afd1da4c69bf c5e1cb8996e6e9c445f5272b25f89ef9f89be61bdf71688f066a8156701b3c14 70e3172770643e04ec34ac004259f8d2219259abd222f89650f41861d47b1fe9 e6be521972ea378c4cf5c41d0776217078f891f04f630497c98e9e4cdcf0eee9 5bf7784a3925c89933199fea56841277e029bdb8e4dfe7ac78b8467d8f84c97f 70f5ad88720513a888301c9bd51a0d5dbe12de43df5d52293153547de0d4f8fc a62e3849fe1b727cf71319308e67d346bb3fc735e27152893b63ee238d0af39a 241c0ba2326a2cb22561110bfb366b7e6a6eadafea230a61dcfd4a9f6b4dcba6 568c41fd4e61caac83dd975631219405341201cc7afa1abc9853ae67a0a80713 b50eadeb0d534b9d1d089862655f4fb15c60763edc542dbe8b55bcded4e16027 f96b13a63ba656428ec73c7d5f6b22a5cbaaf54426b49252428af975333aa980 087e4e0d721080d22ae9b7163c846daa8f5b589a6691e8963c5da8025f9624a2 e24e5da5e60393b9553fc0a0b7a519fce15c3909ed4d79b1204a67dcde2e4569 8892499bdfe206daa5cd2355d77651010f3d4bd3dba9d9f031bff0754c270479 decfbffe3e059ea424c13a18e0825f19128bf74d3bb8bf0ea9f16c77e2ad2e83 cdd209bf4caeeb585551e50944ec07a319f3dffd5b09a012d18f5f96468b52d4 a85025abaeb2656c57b586cc1b592d49b01b6041f84b7c76d74de7ff226361f3 4698f35cd4064410373adcf01d1a5cc9975401fdd593451d29c8fa8685284791 776ad642d6cd0f9c3272ab6cc24064c5ce287d0c4c0c970bf796249515244965 815a7d461b39d605d0cf09624a01741b100d7d8fe7f5a43be593ac631e5f1ad6 3e06f180ec6b34f02d9fb05ad6b1e18f61c86b30ff481ddc5998f454d1e66557 053342a00d7f7e7d63094f10f2db6aeea8bc2a4604bcf0780b06f670780f5fc9 0e0b9d2d83b917845a3f5e73a04137a499d9e6fa8b867ab98bfa82b5fd76ddd7 8a7478a0b93dcc275f706f458a0f3f7022ac8812b1a0b3fb59a17ff33887a926 2aa41062284cb512431c3da7a24fd4dfca3b3b9d4746e9c281f0a6530c77bdb6 d2aa71095261507fdf7ff02a66ec2f810fb4cf25a1cc061f2fd9e6cd991d29d8 749d7d4fb454e0441786b0b83405adc7521b9133be1e9069e98b0b4a0a019742 9402fb817cb1bdf0ec3b3ea7f0e6de5af7ba40e5dc449a4604ffbddeca3d4bc6 7fb5ad8c1c39dc05b6b9f5683b0bfe3453a36ccffab3d7dfe8b0b24c9affd6eb 1d5beb87c516b2f164cf9b133359af4170f80919ec18b0c32130ac1b72384813 d7a29f1eb4fbf3d5e59c92fc82de7b21ae3dd5592fe5bf14ca947cb07f91f294 7c4414a285d08785d7ea218d851629f3ea64cde0db5f122a7d1234f2846679c1 da461281fb75f952c2d820e634628c9b7ad8bc85b2652066514dbd5081a8f001 24ed659f872fe2ab6ee3ad8e503aebee8871399ef6e8173a9a93f3c927c9a006 302b8bb71116213bb79816efe7111d60102a43e2553085ac726b3e9de9ebca63 ebf8a047c74fce459c1cfb3a267c973f22be621e82ddbdbf5c0fb8e94d8dedd0 82ff33c38beeae7ebc65ea166ca06e0838209719d9f68574bb0403e7a07951c9 856688e811d72ab66cf00280841d83d21d9a5982f971ca1b1406e5c205ccbeff 9314feb63caa7c71cc0c2e1957cd334732875c8da47356dae4982c32eabc902c ec90e690723155d6a5ba149f7070c142f367020035f3c9f7fe394f4c67ff5bcc 222caed12c17c95bacec54ca61a7eb3e37710521dd268815f025c082fa8249df 532fe8d1a928b1715dfc99a1e2fbc51fe9dc599badb35c31bbcbaeb53377253d d41e747309374c7b1d87c86affee4d0747b7a1d9fd1d127b54891d896008c6c1 89170b10570cdb10d9e191a8a73cd46f8b9698db908daf7fb1dc9c7ea34e2c92 4c7c8409f431e936708106858f52c7c54b59bbf7485f66533ca079bc93128903 e2e9f74f6d1ab74bcb1c276143e6bacc3656fa372ae0c55d86365d4bf87f387a 71370d72ac3428b928fb2eb392b7f9b08b1e332c5571b720b1062e60600d4566 3fd75f265802ca0b4e7dc1b45f2248cda782bcc3b60571b72ef62b3980fe4801 094c60456ae08e383c95c2dcf3bc2e3c067762548d57b89bb7bfff28ea5de068 af19c103bb8c17788e90adeeab90d5744cf8f7272b83c51dd1c5076e9da6568f c51e9883759ff945e2072a319879dd1940f8298b2752fdc99c3eab5162a9b9be bea441eae3c9b9554bbdd4f797920a478705209d2c837dfe6a43f76df9102375 68ca55b4f18a7ce36e32f68f31d6e55e0f6056eafe2066d1f4f10a1d36e4aa61 746d492d44cdee213b7b1ba63f380d3582cc84b187c115f13489cda5fa7e56ef 89407326a649e5a0904d2f02dd4e488f88a020c40a9f91671fd3f7c2212f00c8 a28169cb023c0f991bb0035546a78d954d0cee7235bd52bcf88a5df99b557f56 19c17f8474d5daf47659a9864cf1af09a594663270eca53da879650400469a92 10ffea22bad2190a153341c2b0d21cb3a4f907b8a7e926ab8d0c41382e4b8058 5e0f853d144bd9e6f460d06a4a3a39f150448b88835b4532c41b2587fd4c0af7 31754c5684b404654bca97ea38f2c00ad3e5f4ffdb97fbc8668ec58694724d82 b32e6d9773921282689cc369c0312f5a0451e66ea941e6448c98f8e981d48acf b147c1c3510eeb037c72cb71dc37425e963685f84f32a50cc01fd408d0bf4d20 57d2c9657c3e9a7296bb505297e55796b77f5d3db2f4fb1f09d23771ac62f398 b25cf9566236c3997d4d997f3d5670f676c13965d4a4a66fdf6900fc4883618f b7b93b8daffc9d5a4e47f4757a70e21a66df2f452bef667140299482f3173478 7098f7acbb510ee3f9c0499b2395d96f852d21376186f275f02cd7b2d87faff3 8c6ece212cade6da775e5f059b0efec5bb6136903d3e5ce07edb1ae45854b6bc 94887bf7a0fd4b7fd0d2da3bd78c13923ba3416a1e4e84c90aec94b860541a56 176b865ea64c0020c7e8371ffee45df70ee0f2bea422735c30201ee2aa31771b 45b58bb64bac42231f7f98469abbb0ef075568d99de4d3ebe2a480fb52cc6c95 10fdf36454f77b8909acf5478166555bcb2474be93a5cfe6d4c882c05e08d3de 4b0d03f74f8a5dadc9ac19dd2b0811f5b73b4dc60972dc052a656803510b8e86 c275c94455f1a680592271bbc61593c4dfbb38fbcad68743c1500cacfb542d03 bbde1fe13a989e8ae0ccb8260d4c1f3330a337dec7cd5c20dee2ad781610329f 50c2ae0be35cad3b9f25102f68b7e69310e1829d36ff52d23a7a000b31caef02 35bb8d2683e7e25d64547774b2c9e8553c295c367129a53cd2ed7447393358da f8e1f760cc5b69685e8d68d6d023fd5760790bed603f69b3148fb4d245d07e29 ea4174e4e5a6844c93f511af95d507c45c939ceaee14b0a033f2c93fe8f5a040 4f117a7382d9d5c50861b62a8ec5e5905ffa6633ec41b00bd61d0ffee78dd05a a88c98a8ef1d64f1395a7029525d0de4b131db3cda25b1a401c723c64cb5e357 617280cb8f8e0e0f42c62c53c732e87ca4fa192b578c1120e40905df9e8d8194 c409b94d1cab6ccadb8460b3d131cc18becb84159897e15edd646ec4dd4fdb9b 2e42617d8e1bea79f0f399443fbd5f15947ae5e52abe17726ddb03ed0d256d86 6a73186410df4cd8670032cbfb4b62b0b7f95ba4d2c3bad16d5a053f71515852 7205bdadd2305aea4b0082ec7bc8a2a8856edee2560e02741a775f7a0ed2e1b2 cc7ba0caffb42807788c1eb7944ab97f8fe62b73c41300131853c968347864f2 78d4acabd2f716af0d52789f27bf35ac8bfe3b95ebdf90437010ef89324fe1e2 98198945989839786c18edf339f538c065e83985d97dd7eabec1baddef389457 603d891e289f2b8ca96b43ad5c54d284beea789a4ae319517833723feb4ea9d5 8eec45e1c95eb6a99c6223b40f6b9e740709941c848df5ee5c4a1b23f100e68c 948afeeaf126f4fe582998b6cf014b9ce21b3a7e0cd439f40be5384f5c7ec905 2702a8ef3481001ff530a56006930329e095a1f73543b515159a0412d53303cd 1d0c05dcbc95d9a736871d7c12f9bf6b0455de0c6d19a17f90680a39e974b96c c92cbe487bb69a8bfd16a8fd9b4f2c272f564208f1f6a39694ef3a72b7f1c236 5d8db733373e9343d771cc78b6f7fa5b5fac0e5b7b55ef6115c7611fca121150 ff9701d74d4540e1b6494044cb26d0d1edc2c6df60c5f646753b256ef187fcf8 c414793da4b592458d92c8116978ae7a5d3bfaf0adf1a0799427907987098949 83184b7d1d49adaccd9662c4004306d458a42d88d16088ee50ec8d82c9d23256 35905e97de54db871c52e900b7b28c9524050c8508f547ca355122781dfb8dda a177e03fee74823cd5498a30bb30defbf7fcb04a77c7a03ea9a53f166196138a c589143a5a14b61399ac8303294231661f6871f01fb323b645415f9a6f854e54 223d256da69a41c4391235895d0afc14513ad9b8bbfa6c19475a07287bd591dc 6da1397c2980f41a9802a535ec35e3c605cc0af558a2c08a44435c086491c85d 2570acf82514e472c79a88e3baaf0f070386b8e68f783292237d920b3155de1c 59d4fd0553f2577e6817728152a7c6dc652fe69f78bdb2a95feaaeb37e8dda0d 65 8619ccdc28e2c6ad1d78acc92278d57a8f67b2f17243c989d806ffe59d470e0154bef575da6bc9213fc861f00b0dbc60cc124632454f326ebb0edbf5b6527705e9e8fa3faf0ea5fdd4643b19b8a4191645b2b36b254157e273c9f2d34112740e751449ad399729c427232f19bbad4b21080ee37f7812bd2bbb174aefa587aa049a397458ace132f31682b42b50678a800f038266f590468358708702607c8c0b9dc74668e4adceb9365329eb3124df574a7301f380d1465f20129aff946dc40ff8f798a70db172f131d99059aaf02f41665e4f254c9118fc038cd5b8092c6300a821b6e253d4eee95aa27c4253e930280c4bf9bbf75bd99837ee057acc859708a9d71940c311eac800ea335c5d310e2e8a19596ecfe59a7d3531cdece333d000462c667537566ce1ded1c0dd80c6a70cbebb9f02927fec35f316d703f72e8c0fd74b3d4a98e56edfca97de5297c078d66cb9668fca12fbf03423a81cdb3a300d7b61e9559f2e94c329160fd948a5b7204b45355aa6569a3e233fa11a82d0dc0b559b57d4855cdadd13898e09753bb96f57404868dccaa0633f4b3e301c7a2b04147407cad47b749acddbc47cf5d3f307baf60b61212ddefe25e0edcff45996035726b23470e2034dfd16d66d94afad189c770462be9c376310e5eb0225248b0c24ff29dcec252b9843c1cf114a648a9231aa00d5aef14a030ce316d49bf03c05387d203e7e5d9494624da459fee468acb9b7934ae0085370637fe52b7ab9460b14cd0d520dafd6bc2deac6b6f1c13cf413ab5db88f89c2a8b4ea9e1a93aab70affc11fdee8abc3089ee6e1884ceef07750e84e8db2248cf102310de1eb1fb90529ef9d91a7d49a0b6f0f302f37ce9c90ba0b3262616eeeb07ee2e139363d560346f3cefb3fef6c230d941e02b0bd2946ef33739d306eb474698204084a9df50d15e15605c3ae43960c8855a435b50bab454eb029141dae3f0eda3948c0a17d064a12b1f40476ac9010c208006f2a8d795d4a595575fef8a7257163312956bd0a7adfc45a48f4146771713cf7c02385bb4d46398a269b6818be8e391e2b4bd20134cb5f46d5e77a9405a6cd8d9f9b5c0a118ea14284e54e45f4874755de3b62045e2eb67c18934e466ebb7e9fc3fd7eae3e12777e4d7cc88f0daf5fa4bed48b0a80d6fde30f428409f0ac45f6da42eea8a92704cf023f81180442d199d7112c0c1631d4ea6dff7a25fc2bb7e20fcb489655987c6a11f17843e971057f40f7c106fb406ebc6cbaa4b0c89ec9aadd5464d75b61b7b9747fbba7783cab8c352dbc04c11cebcc22b9cd0b8c78b48fdc1acbbcfde6fa95844d20c376c9951936b7b1057c4414feb1b11c8e6c202a73bd927fda047873872a0c8da681d88c71021101026cf7e33c1ff022ec9b564780327eea91519e662a3340c5ad3162a372ab528a04c8638338996fc1a3362c7f05c4cf1a6f74a5fa61a4f2a3dc0a3306160387cb08ccc82274c00a0b42f99b9b1d111a5f8561b217ba8dbff6abcc1f1cd33015c607c32c5abf3f9d1c3ebb6f31dd68ba054fd550511e909124a2207c601168fd2809db97499c12bd13a71460b334ea392d563d1d80226a003e6e945ba6cce2a5370da0026e4e14a612b7285fa13b8a5852022a9faebf32f41acfecd049e19d0e7709671bcb9b962392b13c2e3ec8a2828cb0dbb1abe6b4ccf15c1aa97dd85fe86600626e03b74ba14ddf3045c3d94535784e48f7e20711b6b3cb5af8616bfb65f202f1bb7fc50dd4f26a16444647726e888fc7171405e08b2fdcb54878ddaaf1d800408064d928a3750185c1fedfd6b10a1975bf367cfbebe3029328c3201e3c6001ac793dfebbd53e9633d213f3f5ba00ec363bde58c60579047ce155233c147503648a46c96c253b0c250830b48374b9116fdb2375d6eec013f6649f3c22f33a0ad452c0a066fb8129ebe3e2299907fea24be3aba9f9d6bc743c6a08d92aa53800b54774478c747793eb01de09e64d97cce9f5a4614e2e27c7be4a57e478f1010f0b22061f85a6e65424d788bd560f0545de3a3fcb531ed19973fb401b8411d0089ac008d4d1a241779c7ef6568998815c55771fcab927d1b98cd9efca0ec399047a4c32e548d8a412dbd7f5a7b9e4f53cd645dbdaa5671652075211672a6ecb0d12ef177b2bf9013d34de12d92b8e1bda88905b83c8a45bebfb4d3e257135530349153fcdf84bbda1cbbdd403a0bdd9b3a33bec2ee9218c929cf691a89ecf5c0837ab939d7fcd31166cf83c90b2a677deb9c95e32814f74862fed3f0da8d77c0a0073ecce0431b0bb1158474ebb47f9fd237b3c45c1c63427f65cd3d6aee0f6075965df8f4ff7d2261b767e848a92bff7a0b5477a8cafd81bf91f941b9790560c6762a4db9a3805662864cd9c434780f239532f963dcf037e8c7fc5dd90a8c40872a9d160cc02d3aa8cc5ee51e55cc7251bb7c16d26efcbde48250ac7a6025708ebae57af842db5e3f30490bcf3054b5bf29765275d36ebfa6ec14e6692ca7b08e79658911350f679a4ff16653c3a7653a6f6b996d75a31d39c9e3d8fee15ac05881b0d067849c1381ae8a90024690ffcf8d8d36159c89caa2075b28e6cec550f75f0d56f322fc053f167b7d5de29867ae55b39f500e3127dd5eabc414b788800d7a4869f8548a77c68403609874d7ea599f16a04c9af7d1bce9e9884a27c8e08de49b964af7acbcf4b6a64ed4d6732d706c773d49ba6d15d15dbbea0a1e1f107e4ad91686cfde00e1406a5727d392e39b0528a3d9cc8afe25c55019b648f1504675f5c655f3b0d5bd7fc0de79daae393941f7e3d6621936b38d75802665153095b9e2dcd8c5e495311516b6e9a29c68af47687f1e2fcfb0e7dcc3118c0ffc20b4f0030e725b3c797ea9ff31dc2312bdc841bfddae3a358081df509e35e32b7038a41c1d2a015a7479de99e7575eb1ac25d09495f3b2962679d897c6faa113d01f8bc4c637577d93105e09d07843577d77400b049caf01ab348b70df2534aa50032fe7fdc815ececc6055c8d665b89acaed8186f8c728000a6fc4a068e9d2d2018bc2edf6bcec75f602c7b1867dc4a44d86843da1e88ee5ac82fe8e646bd5fe03cb14e2075ae8c1ff0667aa25ed66d586538c9a2cc0c05e4a5cada5949fac0405f9770e5bd48b9df949c2dcb334fca76358b899b27aad42902d16368b2775f405ccd6a893b9219967feb9e6f78b65cdbeba2ef33eeb1850fb1e8c874331b5060394d7bd816e57bfe6d425c78009ad38d5959eba0907f4f623414f48a64ed29a0b3150dab4744e50b68deeddadbcf0d5d6a3f19357b71713795b9db801f9063409a5b9bd2889051ac8082c8406c1f050a3f7a876c32dfb3d8edee7496eeb470c038c544b91224a3e2278092d54eba976eecd43668a24424e6309c9d5766910f80a3a714c000349003201a98290cb27478a1cc3b0317ac491a77608e0d7d3aa9f0b3cd418e7723de85fbbf3047595abb95edda645fc9f8d5f524756a25ae92f65054b0c77e21df51b08ea79521bcb9125ca1d4e509e04a0bdf44ac57c9438b834069d6b058e49fd85809a264f917fa25bf18c42bd6e95f7b33f0e83667178d7c0035a31b535e410a4a8fc1c46ef3b9a8ef06cc8e0e4aeb18aad6fe199e9dea04c0b482d778577827c300e7f03ce6a94ea91e6b41f3170a6925d5b67912a483e4b0797b7224679b8eb9046a594b0b19eff6fb0ecc68ee65d4f1ddbeb99fc0fe9430bb8ad16cf8b5044a0c27f41dd400b4a04acea2a2ffcadc5d99fbdae288ae3470c75c8da1a399872cbd1cdc12d464f7665d99fbd1be64c35d16d029ae0a15ea0007966aef16ed991c5d4a42b3b18932d43fc3ccb35f6a1ba3ef1d41c0603ac350cd33241cbb9ee8d02517b23fcf0cddab42676e7a321107287133afaab9a5d820cc6da5fe00f2eaa6888df02005d1534855c27d061b098b233fb46ea3e08e9530df53acc8397c577edfa2d270052fed476c5d822a112b6c18c2da94b11fb74e40b0fe0f9e2cf2209606d85299d00961e68688ae637d73a5323774282abef3bae021655638e7e794ce2791257b7621426e23eda15900a51cf4a2b2ff06d1b03ab016887196f2924918fc7ac7d85d355ccdee64ccd37efc65f61fe08b95afa6ea707cc12cf4b2007a589d0a9cb0098956f6e773c534e5cdf10fc0bf595e315e0a10023cbed13710de59ab19d0709682e0a28b204ecd3f0e993491b42ace027dca90b03a61c06b3683280e8e3a6a59d45aaf3743fa0fc480ce505a0c1f8f750a51a0bb4fe3845c35c2c79e158125123f4eb6d062763aea26b5da6797b29147d82c90cc7a8384a2beb58f3909b191ca5cda1f90f23b9452a6c59ca88f28b9512e6db0833ad81ba4c035d1944373aa7e845b94ce5950f688060422422812a8584c15b02f6469a4314f8c65fb35748832a4607ff723cec81cf00483bbf6255d706dc8d0b1c4d2e41b5cc7b5f11dc642ba4b1b089726d61b5e2fd2d20fe6e19b9aeed7d03f0d5c389041985e7a55f5279d0db38950831517df9d963d1423b84b7ef2edc0865c008f1ea007294666a132ba012ccbec1bacb30aaf7167d9894cdde49541d06bd257308c8ee2ecf9b40f66892cca437886328e33ea0501d2ae4bd9fa8255a0ea68b13d1548020eb4a96a265fe2b0ada77f2c45c8e31f4d8d02c73e168ba510f8e9a7664f9b51a53a3f4719c702fb114118a77a886a18755cf650b74f510780dc7106e4c7b7796b6965b1b6b070f7570fe673fe5dd550c6054d42326154aa9051a6d458e3fd0bf430de5661e8759aee5f40398d3b2d04fbd767d36ef544e950979562bfab1663605125b24cbc31847b5f1ca6ce2dc1b841d170bb331da3e41018d341439f63deea85e184f2074dce5c7ac98ea6429a6088ea88a423cfb731405955b752f27f57a68beaf0e49f7845cf5be6802b2ad8d736652f8dd5240a8950223f25bb109f8cc9f0571ddd95afd80b693c08ea4967583430fbf49409df5ab00b6092f8d11774439830a1592c34fc1219bd3f146af29ed45838f4e9ce60b7508fdf12ea11f8759ae7d496e9294189ea5454407834a14765be3b879d5cfab880e51b97a28f8c03ac304e1c047b6cceb4b043f39204d85c16b62d9de496806ff0f3e64ca2a09768da9c8d87c1a48f6734929de357ac5443882b45f9e8a0c6b2805e8583bffbde9e414ec5c8828942f8a250348abc75b7486c0799f6f7cae46e10cedb4db291dc472948c5fc4dda302071395bfaeb0d9b835a8c93057b518ae240be119af0d5b12b0fb832e155a04292c3edd4cda39c3378d6a6de9d1f18506ba00951a0f872c11f979255978ca843314548a40c265df2da63a110b2b9453886807ee1ce3b5587cefd18b73ae783002cd32ce04f6190b61ce0a246fc45b3c875602258f3f709de654cfeba37d15464368959020480aa4801d5d4a954c7916ce890060393b1549c5dcbb22506a2f1543138d18358f56f89aa45c083c4ad2594c4b054825506b4a7600ace9b95e53fc6062b2ca26272f8186dec23d33b2ad49144a0763a155a15cb6b30b5db2baeb241ff372a3680eed3e18a74bd7037e588292450fc7581de41966fe04ac299fd6e165338cc84e99ef5eb2d3a922aa4a15fe5a0d0c8c2f6a9c9c66e8b56eabf5e59273751a4dcd3994c1f6a9d8c2b4cfa00fd70002b8c631a4b1067e3aa48a3be7386652d6c93b0b9c853e74622d0a85f4aeefbc013d43c6d2c986c6346c2d0630a3ecc57c3e6605bd405dd20971ea15bae56e3c00ea3660f570d1752cc93cdd6c363233eab0a08b39d866ac6915160e955dfe570d8c6c1570bc35f55e28f62f65be59697b21fa3ebee111d7ef89230a4e71de7b0da7bbb30bff9e204acd0e6167b325099efbd85abf1f5d24def8d41e261c3c7f0b626a6f7f2508322f74a53733d6617f44a01de0f5502770d50e2d67d9562ca90a6e603b933cc04dbb66ff167c73ba11c4e325796ca86b6963e46ee5f44d2af40c72ab07623694e5c89ba23f1b35c3c4ac3c75548e1937f0f8849cf387fef0b409082cebd4edd726121be4a0c143e6c15a67f5e12ed65a807074f89aef087df00972692dc609a25c25ec1dff2205d1ea29ffcc023c7f48d26c0fea90c343a69f09f60e62f272d0f250b7b54f437fc958a1a1dc5a17b4fda42389a8de1aaa8e3f093713d8d876169a96de91d5bcce65245dbab31bb28d1dafdd3e23aa0288a55608e03791dbded1afd0a6aa4da0c2325f0a9aa2c2596ac27ea93119be3e7969140c03613754b08717fd3396815370e51f7391c5c7d080ebb910c12b6042a54f5909879d5ec494e4bc4a5b05398036b4b5f18502b27a14f4284b4cb61d380e2ae5075171d9394087c2eb4dc8b661a9307213acc391d81d624c154bd318fc910c6f0fb3307a50ff853123da75c9b4a1b4dec32cbd0de838fa160a9710ae3410cdb909e22182738123b078cfdfd1e1bd8eaf644658aee061ab568dfb5976449d87e10dff356bf6603976870a325e19781ef9f50730bfb51f2a75b7e9f4fc6d32560602cdb294f9b3b75f59aefbcf291050c11984c3d771ebde6a2e43b963408dbf720b4b0e91b623f0e5313babc2afbe23bdf8859d10adc00cf9f603d09998fcfa61080dbb860e66ac12b00e5310d28dae46b0ce7ac9ccda376c846870038b0f6f6201d7247ca3a707f2454ce652d7cb063a2c7e90acefa22ba4ace0c65acb89942f0d50f27e1e816eefff571c553d884bd1253e4bdd7f2e37c3d13bef47498264f0040408146405617b176719968e1170d301f28506b1978a1504b46d456a2407cd0dd6bdc96730b8ec017a7d4f6f82d55843d53821064a2819d29d2e92390f56120f987d2469caeb61bbebba56ca01efc2f6793e5da5fecebf3efaa2ea11575e2a0dc6bcd5c56a89c7e5d27776c03ecd85b941d93b0a7305b075f245a0080e70950c38c2cc6c45880dc5248fc782337bc42050d574d1a06a7a7fccce5116067da00feb64ddb8b2feee90a8e86837f074f3002c9ce46961fb8f612ee9cbc1d174300f6e3ac8469a53dd1fa5415796b77fab1560322eb4ae4e3a8106d2a850a8d33606937b62904d2b9062dd6eb0c33849de483afdf44231d33fd3ccf99d5980c1320704a5f76c98af11cf0e511969ab427a46862ae0f11f81161b3e2f907c3d617807efbc8e302fabbbaa4a0ea144b9d1a6fd82fab540d50273031bafd0a91424e008725ce7614bebea99bca36df951f2b0aac4bba241a455a4ab67ab1ac34c43df0ff6f959fb16e85e96aa007c1cae47ada270e4e2f75c0ce67ade4711b0fb8e0801ef88aa18f3e93e2db9bb83f37ab0bcc501a4198bf621e0552d322fdde50be3044b738bf708f222ccd15ebcfa958a7c37e0c42ac868ebb63f02f17f73ac0eca0e3f53d58ebc2f9a53b525648ee5b530e85822c46f45d42c8691ce74e28ed74201e42ac54558906c736f171d29ae2e8daf214d99aaebbfb07c2cc989fa6743100bcb5694ce121d8ec922db8bf98ed386854239e9c9c1d47bf3f57361363701b80bc1e2ef1b6ac58376bae767bde780898b42b12e92941ff9d15e30d28653af42086267f504bc795caaa946744f357061fadd0bcd6309562e26716560c7af2ab30816b8fb2c166aa2b5e62fa63c3b86966bc8c5668a681183621e5420b962589d062b448e3c1af43e20d2801a043417980a333734d0724bc34732f55be216373b0d20914859f22d99f7649ae5d4f74033d1918feb9b4f195789a3976803f1450e08c3439952413fdae92345a1245c598d29c098d1e73ca1432c775d966b5f82e50c300b503f9e85f354659034a2c2084f8bfd2df1467363d92de86507f5b03ed20637b8d97ed228fa49540c73319e577a3a4dbcb7308e5a6bce3de199ea088d160e467df57311278e27b0365fad5d715f975eb4a8d726cd28dbfed324a97e90ef0bb182ab8f03b6208a475f039ecb161218272c9c0842cfacd93a247c9a7c02fa0490a09fcc70e019be229aca0605af8d1ef594c3885b224dba3dd8813df2e07c0894ca783dbc2d65c8197910cf6ba8d51389dc6f5216eb96682eb935f7a26c5f084647dce628d3e98854e1f469760f0789749d1cd880f922f368ff28617a79e10fda7e049d2ede21f6c2d65bfa67a1b959df7e8bdbb3020889f9a041704e230106c36d7a491ea04dc03603a16156eaa80ef870ddc6c56e55e25b781042c087b507d32a7ddcd293c361b502b03a219f36630f2d63633bab05efdaecddc9d11c690d97cae2eda7a09f76515645d297d0870a527348d40f2a0526c3f5144a86e0f00d00a5de45c2e80c8c20da3826ce6f26cc05d4bd43642d78fd2fdac2d88ff20c0603466a40858264b68fccc45825230d3fb92407fa31255b563e2aae5da95e250a395402f0a0b5cdae20ab9fb0182fca2068cb169469ecae7ddd8e9f35cb52e10e997858c84baf694dd0e64a3e85845764cf118b3e47d757d46098867ab3c3d905fbaf67754e7be88022cde8be52fc45fd2b7e57fdd95ed51a27edd6ec9a35f10c1709122eca8dd74305fdeec2b42c7e1b811796eb55bb0419a0a973e6a58ecd0907b2895750bf9523fcfb11da42f486caf875c30f1438f5a178d7805245f77a02eab182431371e47138ef925319a53f1c00f8005ce451b110c109da51ef4503060c3c64b7082a29a9dce8c76b836100a20ef38ad60b67bf1a899e55e5627fcd09d84c37bdb751339b3951235a1501eb2c16dd4c9b9b6ed55fe12e631e90a98d03517197d9e750a34a33f8a6b7fc98b06364fca1af3330a9162dff90543e70f500f02c3c570cd00cdd196f82a8544651eb05c7b24291a01c39c7d27cdaeee73104fc1d59cb7944c5b2bfd853ff20d2bc5c1751f082c8673eb1712a67bf0b984602aaf86f745d1b235759d9fc38408c3875d8fbc8742f8131ff700db8f2846f1d0a89d95fdc6a36eb97d27fe0c4041cd9042b0506ff8896c325f42c58f4a39ea007903b8454bce3f4e634cc39ae01f5ecf7d3ae0330e9e3c91ad6280e80601f1c0fae0527dd18a0888bf05fb6635054a68147d93de570106f96bb2610e1ccfecc0da6748c9ad8083dc79bf2201aece849503015f6c426b0c3b349acb2a469424d0f905731152f2a2d171917405e87ed37d48e823ff53d03e4ab48cb3e5289afc306d34db3792a13cea3fe62f50d59da6029266eb8b63aeaedf9ebde9572a5a4da04d84d28417aa4128f8485efc4cb77943bb99dd408b11e0cb5b0c13b5b7a537b0df6e654723d31dc9865331d759c3b5a8ad8b0ea3175c3f4db121fa8e30e4803044bd283e711f19a75b5239ff019a4bc8204ece6c3578fefd9dcc54014f2a65e042e6f6a4d60eb691a86017edde3e1b2d827da7c097d36c5c5debbb52002eec604306cfc09e9bd2ea65e5007d445778762137bededafd865248016044f13369b045e8e78aca11d79bb9179f593bba08505517fc8c0c413d995ca170ada7241ae0fcb056ec90c62805059555b5d61d9701df410c7399050e04b3e04d5e04c6b3303709c963901a4ec97bcc8332f3ee7f1bd57d63f7527c138e7b6a91c4cecd5f101d272b3fe1b8e7a67f2aa07e618e260386a6e391107c9df1bbd2989f8e4941804571c477e01e4701a1fa24a4eb30015d6702819d8614933bb9dd4700f6375f40e1b925839ee72af586aa73d6e32fbec1fe09d448a14411cd5dc292b9572151509821d36edc1f2534da4d3bcb9ed3adc1e9f706c1727474d3f214a07f01b22550db5e9e90b7dc50069ed2bb578f43d3d9421588361fd9f0516cba3787360cbea0eb8f968333db3c32a9e5f3cb3a76c8ca2fe4f29a0463712620de1dcae7c10880966996e8abcc12e74b213f4dcce7cfe464ad04b2d13c2741a22bc644fc730e20d5ee18d903f99ad6e5a3b63be8aa8577fee5217882c9e37288e00e51c61740d0e4c8a4a7987da408caaa41fffc403013cdfc1dc8c20886d10decd3453e521c60a07af3ec48551a4eb3f2e205ce62130fbf2c2a33d9c70fc191410abbde079c70fa78b4c393723bf0821d161c93552c3efd44ced20819ed504afb6f1e062f6020ccb9e3ef29f2d31deb64c18df2adb3c11fd3c85580db856c8c969efb5eae98c0f192bc444c795856e7c505aece56c907540d27eb974b051139dd977eb95abec05883dd7d2d63941ea51fe96269bbd5d5b513eae06f05dfd16c87443d85166800a41156691ae0624f17753d009f617f393788c45a61269f075873c6f8397298001c0446b0e65ac06b2f1638ebe323eae67c2cceae871a7d43513f40106c1cc600991b2b540f9ba0791a62f653345b84e60d9af3035b5547339e5de94faf1e1c7048656e3c02058d27227a8bae162d58a94a1b0826105c8ce4e7fa47a63373314041bff965ed9b4865377974b56296095b8dff9d370844c83828b18f3fc3db7490c92e529529e6d1dd8dce73b6dbeaf4f3dc1a0ed214041967b263397d0025d1b0065dce22184ed29d330daaf1e2c4aeb92f26d4cd2547e49e8ea046491ffd8290994ad32ab152b6bdf786a490a5a787d8a2b7914aa4872f0dc8431fe510dfb770a6c7b96153f0734dec6ba7b33d0a96e4f4b84292790682ae8be0bcb7a3975500c4fe6225ac229ffdbabfafda099d8d74863d2193a40fde9fb085031a5c2a0560f85cbc2bb6f2965447abc910d5b2ae7bd3302c824b8aa6c60e8278b87287a23080cd8ffb18796b4ed7232f5d61b06cacd65916e802dca4e629b58e642bea251071d4cbf2a64d4e17bd50ecaf689579cec3728ac6af1c9b8c1af0af27297eff30dfe1dd9cb3a649ff36c6f435b06c75969379ba03a44c8be04d1fa34f5f719b0044437b3b01533d0047171e60b40deea816521e87dd71713fdbea62656b5d28a07793089a9f2aa5d246ec6a67bcda429333f980dca81acf675cfbb802cc2965606bc0e9ac2634848137e0b6852761d845a65e1c080ae8c902cbb3d52f769ae9a0f246f62674e15f0a4f39536213d2b9a1332cfb79a9f279fee42972630200c3d03d12343c11c1ed4e9a99bce2cb4fdc7f5c43e453f968b35450238ee85009ecf0cce1fe8bfef4e21922da07be3c1bf11e59c74ce7ad2a327c29c34494b6a212b02b8b15e885e48c15778fc14508ac006714ee9f068b0e4ca94d9c67d1f7cbd570876c9892d293668d96413fe0e532f289d6212f53fb5479c54e28a7d930be29c0c30ae890d52a52bc42689c909c2de568e454ceda568d69e829a62ba831cec280e02c7f32a223d221a7504ceedf7baf8afd54d33e5aab241cc088a6f6ba068c20d825824334318c894e5f76bbcba1ed965de24efef30ee2820fef0e875b6c78601916fc12f38e0e7b61ea0cd8e616a0bdb478781f94a7f19eb0d8ff14836eb1a0693e8dbb22f9e8a9d1ed5589d58459d37c8d4a144ba6afc5c4f6d381db86ac806e3d1e6974f9b22f564dae18a830f7fd3460ca6ef64c12e25d9a4b2c427a24c0903302905106e03ff340492459d9a110d60f1edaee07a761cb225a24243eacc0567238e3362e4c1351658c2d03829d852d92e27011c657e7a8d6762be6f19af0157a037ddd48f20ffbd4bdb98bb35c3d82f903a9b4e428a1484fe191115f2890c7f0ce4a27dd1a31ed687d789399e1205c371315a2c9fc32a42a51099065b480435b7115dfc38115e861e6cd42b0a11e1a12965f598a6d332d5720743bd455105bc886ddb9ab24a2c2423804022f8697cfefe0c4d49bc9cc6f9c254cc657ad200f22df5a306948d6dc8db7e460d8d3f4c58a6c7fa4c993b389167113a95da820cf648670dc3e8ac1db2706a22f13c5c8337e3b548f57b27fadb16a1252696870485c769cdd1dcff083b494972661b3f69ad3c093d40b9efaf40c0f420e02ac20e141ad97c2990473e015ded0c0325f364b023df9974521ddda33ac23adaf9c2044a0ca8a7c4f85e25726f239225925d8cfa19ff4b080639e171368bcd78e39d008a4e23d98b94af3d8d7d86c3dc5f2d2580a3c4d65d5b75d0f080a6f21c8a390f7c3649a4eaddb6097fed596aef580ecc5b746cb48ff059fa4b972d30dc6c18039fe01210ed4449f8b2799c7a84484f6c3ab72aaa331fb77ef6d20aebd19a6f051c19317c09f28e311b3480f5af428beeb9a5f8274fe4829166c36d1b30586008a13711cb0879a5730a750ac2357ad688625fc0ac6eb25e5388bc9e206215a5067eef2c8822160eaf6ea86ee04b44936fdbcc51a15a264e98f21578695a8ccc025bca762c35c2bba953f73efcd4b59306e004b64f570f74043d8889a6b950aa04aa7acbb7020c56def0fe9991767d6010247391023ddf9d19b3d9149412147c00d9c3defadff5936e0289b5615fb0f2e5575d11875b00395e911a2325d3a8b50150700099f6f6fce6655bbeece1988be8786a3157b48b488f8a7d213ec3267d09dc087e8f02393db3977eaaf0a8cb41417832d485a46cf1418dd99eba4f2e7f06a32c2da22fd18c58d7efde55476e207f53ab708e7ba0818902b145395fc5e90d1ce303b2ad97692b34bf025b850c4bf7695c95f3917e39183318033816d1ec0faa8e6d1ee814bba5c2f106ac18d7a5ac720f6bbc57745ea502fd6ddcbe901904c6f4d492d55422b1d939aa37f4481f178c4251622e60382d34fbc42b28892701a5fa2b3446a8cc9268e22ffa08ffe8b28bed706cbbe8d18848ed733374c3e00ae00a730ad101ba3e26ff62006157d2389fb51bad93e6ca8df9ad860f8cd35b04875a7942e67f030a94003b2b1f89a6a186d89dad03cbb69ad9c4a213c9e942032786d1cc3e27d4587da9e989818f2265be650c2c9c0493d75bd843650cd056043ab228b14401f8e14de34ea7ea22c67a22a66874950e966f593cc96996678009a6371cd3ca47dc5c8dc9a293bb94864e2190ea72cac53aefe69c49b30923f20e957eb8a89c25039bcff8e3c88b3bc1506f4b463f5e2618d02afc814aa679a60a77958a78bb1b8749898d1e6ada33a359ade49990094f80f117145e0ad7e05a028b2005e9bdb3b4af2046b9c55c5e9d50d1ce418152b83aecade4453c6555fb0dfdc9095edd62d6f5cd6798bef9a0daaaa7bc5d482b101eac0d7432bdd501bf098909a5f0d08ce4213b4cdd6e94b984a8d99d9920d07c624343aa5eb5565acb0a8a6e1992229d45ed28e01d3a174997ab83b036678475f20664f26b3e74ec1e0165301cd5959fb5e47a65f77c7e07e1070be6620fda4fd62c631a75761c24200ae87d177250d67405733e9d3985d51319b3c5c388bbea41d399caa208b7d2b20b62c9cc9681f7a6fc957f07727b873ab8902ae07be2f8a8434c4fb6e3a58d6006d3949f9ebb6b787a3b7364327bf9ef36975cb13e54ef01468f9d6026b25c3508f634d81840a616cb80d9dc4760b0f1bfd679e355941c18652f1db8412d0d4a0192a10622979b03330152746d9e1e1f0e2947433decf380d7893b99263de67309e33ddcec71f5d3b60cd371feafed4bf4c4977436946cb40ae8de1c415ead3d02e2912195a67b6dc2323b7af8d68dc6b38ba484e09f20b5862e54f4312e3ab2003ab0b118d8e91c5251c48ce461ea48845e059e37ec3d74da5df0742eabb4cb07eede7482fea99b8f7115ba538e132462f67baabb32c045c6df60acc33f144a04f1774122b913ecafe6ecb941f7645a0f9c8c2490cf5e0243b45424074bd44c0870e2406a8693fe26a3f4239d4cb96a03eecd070184f419df7a325f1920efd50848e21b0a9eb6423046538d2af0069be8383a4a7868bb88fe41ad1001d81a0a07aba556c51ce361d4ce9ef2805486f12083d3bed46a2a1697c8d079b93294ce0aa32a9ddd9fed90b13993b3467eb32a355b302ce65ddb27a2c7fc1a43828d0c0995ba9f35c26baa2a1382ddef196c88b0f5da61347fa76f726dc0bd41bfb31108dfb4b0cef03dd95605d0db16ef2659a9a75f875e82ecd12ebd37beb400fa1201c9b40660f31603dd2452965816ff34d5a688ad3345e5e599c2d5f8d2638ce10b4615d4c53f9c722c878448847825af375e6dffbb41dd6b034c784ef6303a340ad741d62841d9743a7c7ca5a9369a99e8d2af76598057ce0b86f52c200d4b6e07fc352aef26ff5df78b20b5c4d8c88c328f9a837e24438d778c2f9aca54c52b0bd02a99e4db1b1c69d052f8e866b58387b06656522d26c66450bc9616ada61e03e33ebf0ae51bba5351bab2a0216927470e07044330500597ca62254a3164490abbd69eac97dbc210646b8c9427f356a370662164523464b4df8377b069fccd0526542a947a61059c6e06aeeae3f67e86974d115e010c36033bce5077fbae5509d760976cf825131ac8977393498c26bf1f6d1494f967d3a200f1e7b24c774e0301742a0521b549345c70106df78eec3542d301a4d8b85aba0d799ea3487cc70ab2630e249286faa9a370e2e22dc06e9f591421e0b9ed9b8d2f31f1e39f1e05058487aa6ae732ca7396505d0e413840b37dd5662882f569539cbf589c2c22b008ed6ae4711606c183dfb8c2da4554eecad31e79dcd0f159443d476ff68283e605ef15df0e1cfbdb7ce7e7e448620192f9723c3e935cf618e432c843c0112aa0003518e7c7fe49c5e6f23666a3ad14db368305a7438b8f19fe8f76c19c993aa20e8adda3139e2884e426479f48fccaf4b30798223157a707db2185c402bafe960de39b3e8641f0958bf7522e2f7c5880b13bdfd957208c58937210c9821ecdbf07442766cf49e759e6a0b0cb7acf82dadd5787e850f2dff3b5ac1ba94a7e765404c04821d5546ac9019350ef9ccda1194d0b2c404e0a5110ca58db45367ce42a0a6783f1bc7248d916872b3c8cbbc7cafdbee470ca3902341ac3f3ef715b40f206a264e816aa64cd0d2ae620f4453c6d2cc9bfa7f8dcfcbcf85a519016801612071ec0cf46f0f96168c3cf05affa63fe0c35e7b8334ae9bc0a8fb979a3f8bd66033ce6b12a8f2642718513de717581b0e96fb705d33717c6e8b68d9b5298f4b4036672ccc3ea040017f47f053e6ff4eaa141e249c6b3823c109393d01ff695d0018898bc980e07fa21c4d13d1f7423496253378234d207b8df126453ecda45510ee8a9c72f3bf391ef02a1d5d22fcc2679ea15c556b2e65e353ae5ffde93c4860e3e707baf5dbcc7b7a7b90d62866ca9a5f32c57446bc6ddffa8a853dac13c9e075d6bed5611a6306a4aab3c6794eed8ebf211e626726571b4418f8d3a20841c04a7868e746afcfd209c9202493f43c9b4a1bd64ddef9ee4a32fb7a5a728d5d9058067060228680bcfc8ed2b61ddb0bb91cdd3b8f5cd983555784c70dd72fb7f0e2cc8930ab6e613a9ad39ad96a78fe487a591f17e44861e372e4aa4cfbabbb60acf6a57090e4d7920ae8b3eac3bd851b5e2eed9280711ee2dbfcdd505ff628105e215e95fa96f56479865cdf855b2c88dd519804fe1d17b6d50c6aeaaaebe050a9564a565b8c651e1fbc257519323d3ea81db8df0906f4d75d540a93a881a580cefe685b776710f5d0892fe810d05690e4602d96bfcee5f1630077ebd8d3c0b039bfa6b3e0bcbab5320b9a02f3469e4c2e85e1d8248ff47d0a45453ad0842450bb76227a4cd005089f9dc5de3446799bf6f6b595098e864284fceeae5e736ec04341ee35f04371f9ac88d6745e3a39fac3be447a4528a95a7203e7503a8d80a042849fa8be0c0b70fb18f8605d6e266a67b13571c3fcd3cf97be922e97c62250afda7d81dbdad5426de95f5a504f825ca7efbc941513224ec52075ffb35e47002b413313c358797bc3be7d1f52a28d7f5afedc885dcf8eb61cd257433be9eec08197106371052b755febe36f540f35555af99e4942e300677f8071c733ba15607f5fe58d2a0769637f0c9aab23c3b972669de8b1b87c1462c896c6d6301d50f0cddc0329ac8738dde283ea9951e989c6a7535b1aad713dab64d8f5e9d20195108fcecfa9c89b1a201ffe5218878c8540203903ed4dca085c868b79b088b1192032141fab809fa08e920a0767cad8478a3cc96f259b81397e91158f1a7f05b0d0aa3847dec955badfbb23a4a97d18d5cebcb56af68af0ea428508bb0ed1093e207a81bae5776271bc27d88ad08139c0f2d560e0808c450ae986470c9137f80330f1857d6c3331facd095111c9fe3c44314b59b9653d231b7b74bf699b343fe3b04cfe233fd697c63b84eeb2df4dee760237e1d10cdc3fe488d8833c04b3bb6570ab92ef5298bc82bf8d01b24205a15e27d487ee1501c2805f9dd9fd2d86a04d10d0159fd0ba00c339148ef6cd3cac552c3015b9e48c89ffa3e02837115b0dfbd0eb169b1c14bf56a53a2ca2a051bf9dafdaabe677c6105230bf0ad96041b67140754b2eacc0c9886b3ca369a0700bf4f3f2db789016baf7be8922129e579f3ae03077531acfb6254f9162f111301f060cab2699dba7a05ef95acf896a1af71fa069ab594a8bede80d3738e1c3aa48f682a1a4cc955672767a62f60b35e6172a501e31a066cfc40673580eb891e3a81983b2427ddfef901ae0049d7bff333e494092bfb22b821f4936460338f50402f68e475df8dbc9592fe44f1a3da49e683fe087f28033ec7c8913ffda7f4291e5e949defbf9ec734883af192124cae8573130c6d3c87c28c62b7e6fe5b0b54808e4e8a16ee60a749abb43a4baa3f2693e7c60492d1f0cd10fb27e19af3252c7d4e82c38e26a345a57befe541b771477b839204a6b73ab27ae97af8014d1cbf7cae900f63f8613883e584402191f9a7baf84a07fb5d210f2f240c2bbdf1d3ad98e518302dbeb981a724275fa084872d96de6c024e2b9c22e9db344bccfb5e3ad5825a1307738265471d98275130afb08ec6fa0cf43e224b17447304534f24af08ff0de68ab29d268bb47b81d1a543e7a1f37f022b417701a2d39fdd05446ce4761fd20e1fbf550ccdddf6d2cdfea409200dbd09e6b1058fb68bc9d2a73856256a80cb5583dded609adbad45bacc9f89be5678006341b862439fffd1b222278e884e6679e8ec99c22972173bd90576629db9eb03def1c4e41f0d8199094214bdde0b37e3a010295eaacae2c4649ddbaa6b7b4107c5f1169b96f135fe7d3dff750f8926ba02150e86bf34014ceb9a60c06fbf620fef05a11601f9c686a9eb4a4d13d87ddbfc17c21098932eb793b1e76017febd07627337536611c5402b7be621a79392d8ae62e3d842cc93cc621ad76d256f4101bdd6d5171a01d087467b4996e49db2ffba41b6912eb9500a05fa287e98440b06fa7ab58a0ec46de5085a82907ed4948ae740421598ab2a48731d15a40cac9e0d1f9c9a9954eeedad3a6c0432e421de87493896d2936e72cddf9905ec2392a508d69e11a75e0dca5ca047215504de09373cfec95216a2c12498f02be10f500605bc1893cabf0034e4402487c30857fbaebec2a4b975fba5ba1a5774d519f65803b6b0e207092c6c6beab86a2b38c7d77280f0d4b2f9c1e4c29c7b25222f0d3c088f9c77980918b82c3c6cc72225b8ccba0f56af2a22213c1a1e098c4558c92a078be8d6426b66fc603ca58848192c8626e87b9ac6af978d87ec84ce603c0f9a04e6864df7cf775796650d164be2f3ef3e5874f841916f81b82cde610d5f06dc032060d0e6ccec47e89e52e94c32be9cb0d5dde9bed80ffe3e6814ea1628bd470b1424ace2d760d639dceacaf5d84787030e66d19033bc2585744aca9c3219f40da5ca77015f25078d93376dff7e79f99851845e08144d1bd75e67b86472d7db0b13a2d10d70e9e95eea44e1ef8801ad6b8a1fd5ea8e0d7d41acbca35fd5172d0426aca11f22227d93ed45177d729ec3f20cce42043a1e6268a8f4bd686179290ec06ee8214c0b85f5fe0353423e5f6a68f96395e57455fe900f736da8bedf7300 +generate_ring_signature 25dd893cae76d6ac1932f11696af4d23ced5c7f8c9b26c443513e7460ec16ecd 5d9938750d6d01d3cae39ec888e440f33fec671feccd0a5bcb83a0bdfda37022 6 c29716ea74a2eee8270fcfaab52d6eb58c02084e6c0ee1efece22d8ff5ac9ea1 85179c988425643a848188ae78fbb0c39eb912d731fd789ba04016020e904dd7 5dbf0ab5804b5052e98871ef45eb1d79fc94177d6cfde3574f118c0418a84aca 3053056e01b44a45d9d4137e3fc8cf10b86865dfce0f9773fdbb459b0b305c41 e35ae435c1147ab31281c1a79c2d3a31678be7e87050d041c61aab1dca9b74e4 e451cf5197c665cf0ceb5a9d7b93d139051f56e11bc31ef258f56c9023b94d80 c53355e6c89359655240817ebebc88e2a7c039a53e718bd076dadbe8b166fa0b 4 d9f5266b90401909e533b6bb9a2dbcd9517f34c6d8bcf830fbc95698c2c0710a786cfc9a950d1ab2cb4885b3c948eba9fe36f1fe42091fb6f34abbc41c6ad5071a09761444050112cb6ccb9fcfef7c9dc7fbd4475e46a0a63005f166240d8c09c5b83cbd9e453fc71911f51ed37bb4896532cc3d53a6ac00c1bba7897330f005001d2117bc7a36c4f41a4b8c90f7cc5b32b425bfc1e4ce222df4eb71d73ed200465fd075f642b6fb1fbe431ed72d11007711038fa568281d82f90b4970f4900131f1bcfbea19ee8e20b0d6f827900775ea58af01191ebe7db53e48716561540263b7c513a5ae81f26ac31a337b86d383e272ebceef0764e89b6164299f4fe20d271549deea01834bcc0912aee097eff70f16d138a4f40ddd54b9085b6150b30b6596979bbf97c1db3e592efc328213060041bcd113a10b1522fce9524f34830070cc220f71982e10178bf0dea9bb1a80e8e363d8481bcd1b9d237a769ccd6807beef50c53be11df97b967e32f7954e08887adea0ed8c32cef99fefdbb5bf1007 +generate_ring_signature 2f1a0d247c8827dba4d4ce33f142f157e2f1767529fc7c19c789f00cf6ffd688 3374f89c3f99dad856ba5ce07e7c38dfeec5b312427bb33c8e1b8c372c6f61e8 15 41a94a5d8abcebc4c87b58bd560596b715bd33edb75aa248955b5e4602960ffe d30add0397722f8cd6238bbc36ef02a1351b0bfd5a462151a57cb156e477a909 8ab0a8764b7ce7c3c10a7e6d201732c6ff4fa5ccc9e2c6e25256db8f88c19467 c12136781dc3aa8a1dfe8fa588c768f3f5b73acda82626cb380ebc0e42a3edce 571d28f9270ca75dabb42b076a8f7febfdecdc966b0c106c4541cb6820380f2c c9e639cb8b24ec739970c8f7d87bea123690fd4f1edd394cac1fe570109db396 84321e4fe650871a21b17061c6581701f5acddb389d49e4cfb8a65bf37fadf7c cbbc7e4105af837cc47494fc5b5e78735a33190f1dce55e2657d2d538ae6fe95 d8d75ba6364a1ef06d0dcda3ab45bb2c245d7abd7dca3574a5f48c3f5c10f3cd 74effe798cf28847c01bf6276a0c24f5be7e6c52a3968b7df9b8545a692bd9d8 72740510584e7b51328029f4bc32a40c715d892cb00f70fe60f7912cd7b7ccfb 518813c1abe65ebdf8bfa96b139e171b30da00b3619fc258770bb316410d8488 bfe01ebd9d90edab131dffccbdb58cacd599d5f0a3fab24690c7e932f2f06835 9d8fa00ff4874b2f6cd130a224a2d2df214f27977224132d1d84a5ea13a85493 d780603fca86efa77fe4d7a9d35617b56e6b1f1cbae974bc5ca9ab4b37f94388 783590cb03f14936ac520b8b90b9a59ee656aa1a35a60774c23c0b120e0ad40a 12 4795468c5f610d05cade4a06010821bfca0b8e9dcf16bf9c69c51d6d89c1100c184f6b85e4a83b59275e963b26aba2ebe3b47ae682a18043eaf71d34cef8c10383c91ff64f199a422f471c9d82c8a0b15946d047c2b09fdb061da7d17ddec40ca7d70cc4ac563d3d9c1bb82b78081f49e574408d79efa00c89c589d7d09d1003e17e7144d1a7d0a47b51953698a34aee5c7e82cd9a2fb4824329f13fc52401010856cb7d009b399683444b19c01b5c2822880175a286471b7b3073c30c98f60d54b16afd9098d3c81bd703733ed3f660b11fdecf6e04adc7a4c61054373acb098a50d8fa28e392a45acf5886e9fd1ca5a55718bf375dae3744b3f0c1af778e0efd174dda8c174ae65c54bf11f5478b5d2b487251f03a97f960e33deac289670df962c2c7b34fd8c9b734a94f2024200f1386de23c23cca5fc416e2393d19550a3bd81744e8d58c08ec03d6bb8e016918b40d92d54011072e8e53bc69e4d2c602cbe593bc35f06cc7256d87aea75e48eecee9d5277a3f38e7154cbb530ba6360f88c225473ff147b3f5fdad3edf8647367b9ec35f577687fb3290cc171b70560cfc171eeb216fa4af87417ab49e842aa8f5ae11041142dc239aefafc7b2fbfc0867c5ea0f842eebd1339dbe9e0731805ffff8c3119a895b35efbe760a503cd5044d1efe829cf952b8bdeecb18496e0ab500e011d7a33fbc90d907b8699abd0005ca6ad6a9eadf74d402e34ae6daf7dc96556d3ef46ac11b9ea94b88204f80ad074e4c5374da9d10f1dfb5e12fb9bd559810c2a96b00484d0b69aaa098d6f2020334fa86d03b316c3ee47c92679bcf4dee94228ae4b52502792ec47f4af77c650a83421becd7269fb177ad26b8596d5ccb37a825d270c9b4838360d94dcd31f00567eed2d33df5614eaa0c6bc94e76cf9d608fd90523ab9164c85dcaf731965801dddd7ddf01b4d5f411e6c8ac484c464d06c28f06d0572695337935daab3ffc0e3dbdd30abb3b182b610768847ebc4b118a4f3d71e8edf474afec079409b7ee0cacc2dfb73400dfc39a9790a1b4d1ff6ff3eb961bafc8c8ce189ef8558f71fc06afb3401e3be6741c8c536355378797d52013031a092901917bf0eacd70d24307ed638249581ab6ee5490471dbbcf857818787fc7c8ff4365e3aa73b244993b08b4dd9cd47b97ed67628b4c2beef1d1fc6799eb4618bacdbfb6c1069c49749e0e337c761992e99221e83f1397ad42df28b35c9fd4c62278a01c44ad00bfe6b50834fe93efaab7fcaa3b8014e4d9b82f66c7a5013755790096889b6e90130d0d024c23c7eb8bc9999a3bb45464aac4f27a857b7a72f487c9c6bbc952ccf24aba0f +generate_ring_signature da547499dc76464e031e5ff7ee251b23e86a65e7ef4a702f49430dbc1a3224cb 33d203426218d7f5ba88cfe70ee0255b22f15cd23d1e7fe94e02fb4d7a835de4 1 86b01d9ccfb40a5020a4d522c02855924147e974f4909a1f15afed38ffdea8c5 55adae1b399bf6a36b775e369894601a26a5395e5092e93f1717e75dbf74f60c 0 56605a7e0eeb0789131bf1a8a46e034a040e7acf98cf30e9224db6fa7821ff021e650c9ce8b6edbe6e7897604f9e0b5673ff50aa491120d00d14c16a47967b06 +generate_ring_signature e7e69c7adb279f38f2e2f05051fd992a11149a8998d1369be08f3dc277fae761 85bbeb775e94cc97da84eb667dbc70a26e60a5276454ef41a97e0aabfb8c2e2f 56 18d09a4f9592883fcd32ff6593cec77b9f973837f03d16996ca2cc33651992c6 974af2b4ef03f64b3051a9a18ee09ebe5ee860ade199631490bbdf6d8de269af 0f9b58c9eb9bb3211deb53ef5b997c8aa68ebb9357ae4b827e6042248e07525d 582bcbfbd1c46085fc27008c081b8c3190d8295ba3ee29a28a811c8dc7ddf87b 46e691e35acd711297c564b251cb5b279d11936e67afe44f321a83e7b2362157 254a0add41690908429a4349aa13813cb4b18b08659017515816181971d80324 97f30a5f6836526a6cff058d570b292595e8b056f740b325334712e9c197b8e0 5848a77ebae6530b5cbb1d1f479109604dd9491182c4f4537f3d857b160f63fe fecb6d36c025378dd87880402a8ea775376954bad2bdd78ba5614c793c0f9c8c 172a5304befb1ceefbcc22bf34479c924f29afd264a3ac3b426cbef3df1e1903 7bdbe1fa6447623cc54d957d4870e2be682aabc434db57e31db09b1472774c85 394ef3d1104938454158def95ef99ce4614a373235912fad5dc6bea68e9d8b41 23e1141711a551670210262fa8a59271dce5a05939d906a5d3cd7ec5277c1da5 969dde51d1fac502a370b8bf4f40d13824fc919670d4671b402a294b6a89bb08 0509b587aa1566227473c0867249948630965f4f94a6b024dcc64faab4cd0e66 7e0462bb2cb462c7af329b6bcc6b9664a2643cd6e360cfd14bf7eb53544c999e 738ec5605320e83f6fd439853de054cf99d9b320916ca3c26c53b914e6aa490d c1613e126399c42bae1c4750d814dd533c406db959abdd275b3d9c3e72984c24 d588bbe6c2c2744068758a5d6e259d1cf02473af17a1368e62932688eb7764c1 26d840d38fa305151e8873b14a441f42c864d873ffd7aa2c7c278109a446bdb8 66ff84c63ae19639ccacc7b493892f8f26ceeb3dd6d8f063859449da9d5b13ff 828787793865161872024f6ba77dba847f0873be2562afd85a64bfa0f453adb7 1874b21b0a18b6b43318736c3b53d871bc491695f5d86c8811ad8b2aa8464648 8f1df6bc7f2a2ca7a7d00f475fa487409a0f4155d2bcb2db1e9b007cd9368b83 40b39d643696c1517209c946e7e797b4c1cee0b339464c9e8b13ae28d9434afe b3621ef2364d2a8a9f38aabb9b624b932300046569e9fb026d36c7e73a3e8fc4 3115f7a4b63d229c5182da0a3f624de6c7e2b50e13a7da14a322f0de69595418 7146996c40e7e9570671539d471235c7baef7387ba12f1fc06602fc973a3ffa8 ddae2cda3fa2d25bb4a412f4aedcdd206274bcdbae18a3d6b4c25d978f8f5dfa 124c68d03ba3fd815102f4ec1e16016e6335ed47d9637771c426c49dfd87b1b7 19a1d656e101999c3b1457e995d8a0977c18bed779deb21ef832e7795d4d9044 b69f73ce720530c3fa94f7f6f0b1c6c8e5466a89b97be80739187695970f6871 ffebe2f897378da204d5e8338caf629744fe910569166768481b76d0934e5e0d cfcdf78c7fe82f7fd14a6766d4cf608d6eb92af30bf11ce79348bb2422c1ae8e 7af0a5c4137717bee0db0faf10813515b700be3b04d639490226b047a32b660c 8e824733a807d4fdb63c7621b6838f70054b9a069c3e8b8e36c7b7a2fde41de4 b51e86c2ca33303c307c9d1080f5066ffd47b67dd1a1a368d4bfe5def1fdcad4 f271285616306d959f3ee158276b7c35364191836ceabae3d5f532d1c2f6cb0b 0634672a39f8f196a86e8afd3028b1ee852026e53b57ab5dbfb36024bb3801bb 84b77938a69db65b4f705507f9a052aea980fb5827062570470bf1674772cbf4 b3f1ae595027df180e6d1ec987e7d4faaa5832d72c759f6c4b33fbec5c197248 b3aa197ab9bc1411a0088271908462c745ad08ba0b6bc52b82300760d1edf3e2 764478333c006629e5be2ede6050b6a9e19f84bfcc9af0cb606bca2a7fd6ff57 42ede624cf6b0648b2219d84e2800f86295b45b2d62c400911936b00d23e8eec c6d188b2593f276c9d8af38786e8dc83fc3f4888357576e0ce1f817c6f4ed0e7 e54f8c15e52301951fae217b400db2f8a42aa824f182bd97e979d38cc6beac71 815eb621be2111a52233f936689d47fa18695b52431435cf969ea1521abfca97 c39db1d00d3dfb57a9e61d8921569686b0cbe20e69915cd65c9530c8bc7840c2 88841ef6d698114e8a73b56a437acaf9dff3fae7edbf85394ceea8d0a0abac7d a10006d29887038ae2678d8ccf83cc66f6e30cea14f3776dbf3b1064b572680f 867185b15807549d9248eec40382c0c2efdddd0b6c18a884ab811bf0ba074dcb e2408a5abedc5ca9c397b10df9eb1d4f327fd875932544769b9b6ba124131669 390f5c7be19c6f50c0645efd3d416b1c740d38d7f95fc6983f5297921026518c 5368d661c76b57e15f8e4cf676aa0c8179bad2cf815856a97ad48db1deca7db0 87494de9d4062f589bd263b6fc826dd85c6b81b66583d6abb6fb6cd5954dd296 a353830ac2b086e36537b57320da789652df3df9b6bbe6f963e9cd6941758aef a0d9ff166385807b7eb58ccbd283e08e6b2194e0dc82ef190b3dcb15bcb3160d 15 6d5be642649373a485d93b635e7b4b608e15ed664094ff04ffdbd9ef00277409df1cf044d760ed37391cb3a7d3dbe2332ca6b0d652ce2c5d1b35b95e9fb74d0f797f35c1916cb24c1ce85d73502dfd68a769b20ffda253e770fb27b5714bd60a8d31acf18cff3d7a2b763bd939b21bc332ba28c068b1760adc7e018a1c3ff70142d7e1178b7561605bef5a3b6a62f410cf385ea585da77d7e0cad50ecc867d0b032d81ae2bdf963829d776d6170ba2e38748a2fdd54af88fe026c0e71f3b400d556bb8959f4b6b87650c265bc69bd7ca9697b35193be51bef4a4ca254e642608961f2e99fcfecb08d694c4e5df1041a94516bb0472d37fe4c4bb33c934a03f02c80d96894faa8deb1a0a69dbed56586e82767f4f8a31490caa5da1ac2ab2ec07172aacefe8462da0db1b53aa3c8f16acee5b59ebaf2f2903d9bd119caf67a50ceb3b384daa28947e5e027a54a3cfb754da13b9ca1a774544f6bf8395b4ca2a0e969a54290577430ea00809519d78faed7555a4a92ffa7874390ee882a197f103fd4fb021a80d8cefdc9c1a790c99dbd68db603c68231298b55cc627514b0740099c4ca2b2cc40d33e34346dfa24fc0ce0a28c6c0deb63a3b6af5a591ae75b400d73c4c8ff622bd8b4337e809c4c9c90143d8a3d1fb69beba8cf049e7e55a28018ea4f895f41ce920aff00f6cad64091797d75ed9611bbd76f4c1edfc2fbb9109185901047ee6603e965c1afa0ec7d3fd32f76cd1955951a4587854aa579ad70759b1fc9504ead94a40d72f184812e48bfea4d48d07c045f27869a0e96a467a00729b9482b9a81006a19513a71ce69cfa5b85fb2b05e9f60050ac9963dd197c0ad71ab74b4d9fbbf1329ec64e12b3b9f3cb2af9fb341468267bdab35721c8ef02287721349eaf9dcfe74fe3b7461d396c69c69acfbd1e1f2beafb83b69d9b580ce8a7180c7fa6613da4d8c5964199f721fce9ae1cf81bf624cc830039df3cab0eed2b6b3f7302193417b0f6f69c537cbdf35aa5782d2f7ea2120f353d7737a40e60f37bc9e42b2295119a73b26a02ef305e19a95deb7b11cc9ff2816ba54afa04c1c02a86298dc2e635877ae4b89bafe609b4717a7055413316cd3b7d8e50e1093e5c399d6cef49fa008500881e08ca9ee88eed262e9f5bf8d23b1685e3017e027ea19ca4cf81dabcc6870da3dcea89314baf3097d841d264d70211358c519602d6d3dbed6fef64d0a54011a6a9a5a9ceb76f0d9342d12099c2ca3ce24c5a510f9b437400a55a0e64c66c8cca64674f96c7ffad87d228f1c25f111f8c8a6912006b192025c195651aa235b4dd768e647eb314efe335e682041e44b1667775c80cee245a64a7f7d3be3c120035aef4d2e6221c1cba57f87e940cdbfd4bf51bbe0676ca07d04739a081de9c87bd212eb87569cd67a1db6d9dde4717d59937149f0ff8a6e7da7c33eb9cf085916e64264310211232975e7cbb648aa375a4a8c9930d1fb29ab9bfb5fb6e4653d5889d8ca50fbaa8edc0cf9c6943403a5b755886b107af9beea17a8af1aa42cd45c0b573251727fe4c9028a74bf5b44e3c5e1322bf0b6388af808d35299a46a24f30c267bacc064731acfe5b84ee14d56064b9571c0ecbc6d2e047e40933cdc8f80cfcea96abea330cc502808795f54291a87ce1c10a7a626b83d533cdac92732035b66c614c73f488e349b221a9c2d9e947dc97af0b4a1207fc51203c1099ad7f45d28460bbc90d217fadd14ce8a62e794b6be28b02e57e34e51a30072523d9e8dfaf5df9b6e0db98096429a9dd9a865e9f0bbad4058ae95488e213cb0d9c3b01847514cf1fb3c8041baa1232c15d980fd59fb2cb0b55dd785d86ac352756241355b71307563dea63f95354ea6ada82d292582fba01fd9e3fa1157eea6d21f72fe6808dfd315137ceefa5348632dbdbe2a2c18ecf0d3411818f032acb787f3878e7973ec56a1f9d11eb3f80d12212b9c65df1090f0d0ce8e33979626891b94125d44bca4b4e1a819a393f3769cd35636a561972830e1bd067aac618529b3b740817f813310538b427934b1d8b5664f51bb81ef69006b756ec86a0d1974c468b81bd261a82aa7cf6c3e5dbff7e47dd800699ec44250e5e7e745345230d76757cfe212205102005e1eecfbbd174f2ff2817eba8272b016c1581a7de86d0674bc977bd2126d3881f73bca9e5baab80bfb9297441a8f4095803e8e9f9b130b5a1d3321a0125409458516b9ddc776284906e2d11978b450583228d5312c26f06407fed2530b4486f5c0d4f7fe69621d0a517d112400a2e03e56ffaac6f2591b36f2d9b8a81d1324dfc4a5ee127071879eed655fe2d142702dbaaac7ab12f8f9925a59907cd8e215c753a2def62c0edda08f4c8f38c07e205e141e359721a07a29e38ad6656fe976fcb16f5ac48d4dc54a4aca18549f6780788f92d6f37fa7d740cc28563592e4be3beb36e7a19d425b513f680294dcdd600bc027bc8b38023ace1e70df496d6a97222d206703a961ae58af861946ba5310fb9b9d93d1849ea15dd3d67965fa8e37c2406226566f36f7204b5018c8d6920000c37e799d501c0da22952a51409377b74a6cbc6a556dce192f036dd570234806fb5c747c12bb32789602d68b972299d430de3f924c309583a6edfb933199380d4681e5df110e82ff578c03dc2942285c882f36dbdcb1522d1adc05b65f5eb50d77aeeae6961a64c94f93c980a693b939a5a672da14f66abf068742b72f898c015e984d533ec98d21a270d368d1589ce066d630fc7e0ed088a98c9cb496ef3703e191f76973085f5a72963a765a2be72e6722a9e6ab3ea9d51e26278e37bd63058bafb4aa684d718c77d7f14e27128e60d9336d59079ca1feeefd1df6ec46b808030de2041d368cfb418241b792982df1479d07bca84fd1d626fa6c6f23605603a1a857aa4b4c552ec42e71e38bbd254f85dd9c1e3d62d99a5c1ce74861e9dc0c30ae397eef0e27c776d30d3ebc9dfdae013819905d7d7f20395159ff0095be07ddff48205ec3bfe1b0b645bd63b5e7d9963f60592164fad0ffcaa152a1634c09d275d541a0abed1810a30bf6025a231f62621661e0692a6526cc6dc007c7db05af9b4f6a1b83d9dde96d1ec97eb7fe8147c7bd6fad8d5a113806dba66d330e02b30078367c0cc49cbee6d0a8077f1458bc3280fe6f596554622c5b50e43a7d0d8bb02bbc9d2384038874a58a3dbd8af35905369a76ca1a84b73e12d0dd62d20e7fd20a9177089eadd274ed09ef2be773e216722f0c76119022ad83f885009001e46091a17abde7b3af9a74ff5350783153bbc488e0e6c2260e918e1fbb121407e29323952c59ddec35b7a760c4c89bfbe620171287a4aa6898c9a187b2827b041efded978dbf705f18f424780e789f2c74bf010d9457cbf89673c275cf79f70ff0b0793da151462b629842e81d8290fb17b419bca9dd6b356ceb415fb1e40f0efba14eacc9702368b688b0df12bf0229a95193f3aeb68727b9555d368306c30b9f4de41c5630cc09be6a34770814535f8b8b5b0530a2184216c62e64576d2c01d4fb7c0f7765fb29e1ab5a4a5e626285e9e8af0fa6ced707c3c36fe2ae14fa00e5b467e54bf7bbe2da3f6b37c714ed21c7927beaca338b08ae9094cca8d6f80cab3bde49b255a8851a0bf55024c9370d65224680a06af15ae28645fc4be38706894be22ec8b0859f7ba6883bd06c8ecec7e5d29a1f5b3d71f36e544b1cd7f500806e1dd4da697aeb779a8934ae6c83e59e81526a02321b790736c26b20002a082e60fac8edf157cb37ec8aa66a6658ce45459a1fb0c3299cff18c4120371340540b1216954dad34c5865277ea3c53e70d954acf265b57e8c2c0444f29a12ad0866cb496a4a114616bcf558d6f2165a2bde3fcb08e6f532761970ee9d99722c0dff77a3c3f10810c91c2f38069ea3aca2886a45af5a0ce80278c59bd46cb4260c410a215919f04b38d7112709e729e41fc1e139410b96017401135eab589e800d4e5d175b4ccb3f09144224390be942f3e8cf8f77c9a7bef263b90df2402fe60276d0dbe9f18c6c682869721e30e1fa326545983d216abdac8e5813ce81fecd02a889e6497833d270609dbb4318520a80a80f0b99a68d1526c3bc9f99371ce20ca85ef2c02a89c1eab25d6e5b60829d6344a031bf0fad62a670b7a53ac21fcc02ecee08fa66123a0be39535a89237570b3c8869f97fef1b2227443b13e586850a7f90e6453e2f7908fd432b8c2425c7818995c423c4ab7d92f88893b8388b5f06e89713115e636eb34c216813afd2027e1daa5821aced4c156f600a94d03d1b0ec0f7f3d44c59223e3d99ae164321dc475bbb9e27d2be9673c423f36e56504d0173f0e54eeeb4497cf84098028817e5c86b6f7b3b852358a65968a1d93ff468025b044eaa2648e1c5353cc3fa60de7aadf7b9e066bf65cfff7257209800241d0c0292974c4ab2f65c38d5ce61e976cebb1d20a894740be4d37a8b73dcd692130e7be0fc910f784bd2b823e55c3c3a0f577fcc0165cd54b632bcb0aa75cfbb4906771735ae1c81ca52aefcee14ad3926b2849ff7f3e1ecb7d31d117f3668a6f809bbce90d190ebeb9f5409b81482408cd107dc82ca96bf65d8f231721f2daa6006d66c66b2c1a0b35cc68a9de2b998595dc30595cb6a29366cbc5711b786c0fe08c47b44fb7dd3fc8799d9ffc7185e064c0e8f57a3e79b6cb0c1a76f6970feb807cccde40480637b909daf8982d5b1c0b661f0173948bfe8981a7837736f880004a4e9f5e70831fe78dedc73ad08e08497b7bbbd84496a821675f0718937f5fa0bf2eb93c5466fba6888abe3ef242149021a5ea5fcc18b8a4be8cf065d6dba4203d9bd771b46c1767256c7782a3c582f9dda2db7b358caa4fb8f593479ad77c808e3edcc61f64433b090450e0bc34212bc28eadcad8d4b8b34c9d42b071be4af01c9d831e88bd88d13c9fd56d0f97ada04e3672a8d21d464cbe916929ce401fa09ba83ee90dfebaffea9537c47ce6b1a7a0c4166b35cc10fbb5642362458416501 +generate_ring_signature 8d502bf01a32e567707adbea7c5b07946c8a91ed1231dabbb363bb6c475debe8 797d78be1bd8f1fa9f586e9710621bd3d480427b45588fdfe8b845b12414974e 1 bf227dd9be0cb17b4cafdeae98527a2bd5f22d56f4a4449af561a5c7e9979411 c1d63edbfe8e5ccc53d5c54f571718b81448166bc241c68f09aa1fc0f337d60e 0 55678fbecb38f36b01de860d0fdae27ffcdf030d4b252b87e18cbbd7111a3901be5451378335f8a4bb33f178b30bd2224f930d56d4f12fe6b7cdf7d4b4166803 +generate_ring_signature a360d6c579fda6e77d9d8f7c5670c5a3e56dd67f9bbeb9a2a51ef4fc846c9e4c efe52ab1e05aa48518f70282135dbffc35e61066c0ff77f69dca8f4f77cf26a7 2 5f4e6fe57d228860581b1597387794778018d9508ae293cacef275f3f380f949 7f855f04ad22ae8e65f0dd48b9140295e23165c1bdc44c2e9caac8f7a4d47690 742392bd543557e97a5c79270150105e0e05099b7db61d2f07f2a4ae1b0bf301 1 78aacede410e8f9d85721ffde4f07085507b6f54df9ae9a8861a043a1200e803f03b472e949701e039688103038a5a525aac1dae6540aa91ed4ce9c2024dcb0547a1ce7072e2d6dc7ae3b9696873d977f87eb7fd722956ff20bd539fc3169b0a09601304820fe4e691d1b50338281744ee1080a92a13ffb432c0ba8b8681e501 +generate_ring_signature f06ada276b4cb15bfe0baf1973b4159c4ae03306b3d1cb6624410d84ff75ed8c 33f1bb96ac7545b8d506cf2332fb5c1221d7833e8f1a58c7e72e7ea0c3a85f44 1 59ebef8302f5943bdd11879b6b4e3bcf098eb70a56e44871a3e6097b2a38f682 11e1c3f3d5d9187ead09f8679cb2172afc020766ce0de7ae29998b7af9895b01 0 fbdbc9c9747925ab3f1e2cbe191652533733de632f24038155d03e5ae585450d1bc1ee2f4990860677d2c9a3d39b4658fef54562ef05b331960239718359d908 +generate_ring_signature 4bf0339d9f8d053c5508af5620f121860191aefa336297ae61c07ab16e830e95 4b74cb953354a6d552dd2093fe6547018f2ee804b950c00ae2cd959d9edffa02 249 b273d48608383a643e8f4fc7a3218d79135807e1f01bafdfd131a6ebb066f53f 13a87abbe6ebc9c0a6a758e2bee754d629c0f0e3c9a8c922f2d6b84ed1d09563 41368a4bca9b547d015b4c88063bc4c61bcda812a31c5fa1380cb9917d91bfe2 7adc855900644fe40f6fcad939d2ef3d2ad64c78c67081eafd47aa014fbcf569 327c96fa149e38b3e54f28f3462c9e66df4c74a6ba7957eb9fc52a91fdaf7dad 421af8feeb282c03a8fb30a7837837641c153fbd43a8331acc28b1c4a9e86bf0 61281e80c633170d98119dfb0fdb35405f8ad76b0f0d867185ff43116abe96f4 2e25524e65bfc6c97f57a92766d15c394e46386ec49d2749df2d45bf52daf1b8 47ee6c2a1c29f513e09d89f74fa7b86a39fc4468cb648f74e0fbc1e48d8f6d98 09e04617608530d57ace72efad388ae1ae3947c0466c5b430b9dbf351f392867 ddbf7d3d8bf539aee1727a3eda3737f18ab63ffdf80651c5597e10fe6676ec56 135947fdaa00c8fef0bee2b1a8e843746094317d6feaca3c12b15d10154979e3 203b1c508ae96f42485d43de27fdb3075f3be201748e51ff0facd328399ff2e0 c336b25aa242b63ae73f67e1bf193f50fa8f40e59110b69e76db94ed57886d6f 8b285837b4f1690849e2b7766d9f530218aebb3e2ff9110a7b149378dba033d3 563db591fe4810c041d6fee4423947f244f35e819f0638b691b2b9fc77cad430 03e7898c213d50755f382dc62e36599c1d3dd87b6c9e0159472ed2f9f334c0c0 22e97507ef3b946b063e25d97f5212b6494d2696f9971ff5d54475ca9dcc1afa 229b32794f12ebb14c367f5f92290fd8190e14a65aeff3fc78aab3f8637b2f5c d6a8b2d32e3fd2fb5d1c71bf9fc51c0095033217feda93d973e49308f5694ee1 babc6af36f56c37f9d1cfe50d88c6e7a1403d9ca9cda788a01669a8ab6f050ca f07adec8757f7740eb2403d1a4b1c29bc591f5f90aa3c8cfa17035b07f0e6937 040390e90391a7eddab4f52925efa58dfaf935ae202267ec0f2aa535266dedff 84a82b340c5ba88f1359f113fbf717a945ccf2baa3b1561f347a70a83eb3727a ed06ddbbcfe612935bb53add5ef26d3e763e820f62d549af2713be22c6c6ee48 422ce5b6fb4e39f0846ce3a429a03fd2dc53df54f806b5ecf1dde1f2b004b074 0de9902ce29035edeec28692133417787524ed249b1cc1e2a1c000c2f35d563c 51c3352d40a920458a2e73a32198bfc93a72732d434b1138050ab8574cbab540 777b6aeae382f6d39fc2e613dd90a4228c2e8d69b3c56a84f250996a3ccc401a 2a324732d151575134c39e80275ea6aefaeb038346fe08314bcc90cda45b75fe 99a2da7d0817ebd800f5c2dc62649fc202068cae2692760a4b5bc17950249a05 3b8676f56b6358890bd11968a18e40f0fa56155aad8f89967a9aa37d8297b177 c0c464a4bd5d573e1bdffb58dad5697660e675a9e9e63858449b89b267271a7d e649468d35445f60819fe76f8780023b5a1ea23f116398ffeb4b4620c68e344a 05e7d38bebe36c388c980a9df464f018f48681ba297ba940cd46853b52300819 56f4fe13aead9eb250187d3f55bad323522841e0b5fed2257cf419692be899a4 acbfda9c0407409f6363b3718432409a2c56ead586ea6f8625aece0d48fbb29b 0bfa3d79778d063ad3675a485bbb8f8baca3296368409d81aa25ed3da324199a 800a29bcac3c088906ddba33ed1cd9e8ad0335d5075ddcfd28ded381179fd5a0 7dffeda14c5e22bd9cb1269ac3326a6a617b1bc277a77f5066aa2676dd6b9d40 8b786c7df9fb0e695678e9cfc79771bc84badd695e7285ac75bff9d6a07c7b8a 11bab52ae903cf6d7eea8a8f14da338ab52829ae7abbd6826e0c96cb1d8ce098 6e0219a5b11dfdc9ac7e6a8ebce4bac2b775a98a12697f8ce2afa792dce8553e a45f1f5479673fcea8ac0680f92089b115fc939bbf97094a430ab849ffea4dd9 e5aaf69d15c087793936a53d679544d2634ff9db1b81cd2266629c5b5d6048e7 bcfd31af3666aea09e3255e835fd931019b944af779af8e22428b713d942694a d320bc2b9ee9a777749dcb6c40e59a9eff375737cdc0c9e63539e89264e922b5 00910267ec94f2cef9ab012f1884518fac66acc140e82bf9684319d438792eb8 b13f3a4fe4429f8c34ca50fd9649193a1d9d42939280df3d40baf029c08fea85 79ddc3b230713949fac670ca6ee8442ab77d3987ad1855a856b20ff8c184fbd0 b9cf848cf9d1aae5f0d066e281b3eb0d447851c688123e4823dbf3bc5ff84929 3ff30f29ca0f5d56e3114205f032febe2c7ce3dcb778ed20d0fde85e42d44a69 49f1c860b9653e21a699f11dc10a11cde10d311512294622594c7fdce67fb8b8 8b18920f526da2a285481fd308428fc2a125c257155fe8b9c439e100c1f4299e c3b822f191fac8d362d2262efde806e5bb0a468290708fd31d6fa83890363a8e 114fc1213163e40d2f246906455a7edbd3070157b2089bbf0f23ebcd4576e7bb f39aaa33d3a77cfc664fab4560c47c230af7809cbff25f6806c59918296e95c4 c5a3eb0d203d502d830ffa7ac764c029b2890cd5c7c48aeffb793dda0e35f206 ecefa373bd1cba2ad4b04d5f2921218abf99f0825cf482c3540f94432cf01c45 3d4313281bc933e2bedbd7cad99fd60f1748256df79c08936d2162bde71fbeea 0ae2cc5b6d3b36b676cf08f4a9b4d754339dfa0d0ea5ce5abf5470b4726d09b5 6990d8de84993016435ca8f23aa18a7e72dae4d8d7e232994f7fa5657d34fcad 3cffa4c6b3aec0efe4e7e977e5f8a7294ee5c99dd5c0ddaaa3fe23351c941d85 a2d0b812fd1c7d191eeb61567298981e36b340c134c78da944f8a2203352e8cd f455055f5002146f5bbded6001d7ce6cd58e7e274caee50cdefba3613388a0da 477131a096bacef938be8fb54088a9004b29aba49f4579c33ca52deac5a8e5ab 67080c465a248f515e86b58bdbe91ff272cd23b08b2a7efe4d69f47e0ac85fdf bec5ac0c0b01a028ea0b29369e79e1c81eff46e9e69031ac3cac91aaf4db462d 2ccacdc26da82afaeeefb377043620a661bac86ff30ac720a847275c3b22ef41 6b20b96ecbbaaff46371c8c5094ee3af5250e9171c3207804bd0ce83c486b5c7 77d1c2e2a5b9048ad9c2200cd77495505c851fe1d0e4872f46de66b7af209ef1 fcd862e7cc429f954f422dd13ae8bb48dc029bbcf9b914244b7ef0a20102a155 3ede83a52d68d2fe4f8d6e721aeb809bfdcc0b38882d41e41dc2087c2b197f26 e38fa088d56b933da20b16848fe1310e4243513dd3c729f4032f74d47891ac8b ff44c21ca0af4dbc2e0dc012ee8e122de372f0d530db0d68cecac9f2a7c33b73 3b9903664c6ef51dc655f45f82de37b954dd70a37af1a45eccff40c492f797a3 1f70a93f7b6b459fa011b5e7bf421d5108430c443b1849f09e23aadbafdf2b22 b957c51631fe2c2dae71100ece8911fa410088a255dfc3521c0b1299d1ec89b2 b4c2e3669df39ada556b367b6cc9ad80b49d3ee765b54f288f31eaedf947aae9 bec54b546520ce1edf6def1e99bdd92fe35fc090f3d7ec249ea4ad7ddd998d5d c0105b38734b5a317dbeeca88fd34ea91633d4eaa90171724a420512a1769f48 34d20eccf3554bc5cfeab0839a5da3af1026be554b8006c91e495014f47a98a2 89b6ae04aa15388f5286393aec911d4ae16fa9e1c6cf991a8edc93ca27511c02 dd8ed17a2d4ed78da78446870808e79b46bd9302b686b72600575324c9a18e72 9127b0709d801ac54b8d550285a6a4e30a77f84a532eaab6c337a1bef97dd1cb aa0c38fd5fad2f1302e5983a9a09ae83edc730a32610a7dd102475624720588f 1b614f9407aa350b5cc5c52ccbc788e943a3f3d6e6a75e332103c7cb52503e2b 7bb824ad656dd32ad9c9ca1deddc41a3589f3fb9673ecb1621da283659139d26 8bb69321746fb816425ac5848e3d941decc629180993180572a2d673766d217f 17a6eb6175bd025118254836943f75898ff81a9cf26cd18202665bfd15c98e16 efa75e34a18284a2434c5b59d0c28891b998b2120a274cabc2d6757225c1b746 4866748e5ce204fef81c801a58339108a51e6d3fea7e5e2c879b22b358aee2e5 eadb7e4fa4a23a04f7eaff1faf8cd72b98215fe213ea9404398c3d942551249f bf44973880920ae02da88d1293672bddb4c5ca8d2c7d128cb244e480618cbef8 466f9c575f9eecb3f26664d593dff14960f4c68b3545a704b99594cec3f63060 b57feeda8456b15539b5d24c1aab860b97cdb498b629f81d57ec233e4111007e 4915b38ba83896e3f34bd08451300e26d5ef5d8d8af7518ac0592d19845b4464 d0524fd7c02630ab477ab113cbd21b5347063c933529fa01813d43a0d7d80d5b 27e89f490f81f8b21f9a08539970ea149b3a18196d54edae47105e2c21262498 a159d840849c5682a7fcc45335e329c3f4f65a0370bd835c70b2ebdc08d2eb83 b6876311d19355a2bcb0990f7b3fb98639eb5306af6624767cbde5013246cf8e c9351e40f566cd7e30de47c39154f598902d0d2a4f78eee4b37cc5412b2a6aab 1e30ceae50d00e8a0e87c28e46713011b4d911da0642468a47b95b98c33f4cdf 0c0f7add9e8f5c60117f50951765739424cf52757f48455162770e2642b44c90 e72c4aad3a76f21c7f40a8f7726b4f25c887fa11de547e5174e57d146462e764 49ae88514ddd003ef502e13b4ac56270888953a5802a5859961459a4c702cb31 acba3157ed52715ab3c0ce192c822fc4d317e84e360103c3cee3115c70a601d8 b05947d43cd215f923d48dc386c3f992351c5034c9112763bc2f89d9b4c323bc 0995423fc0a5997c95e07d3c4e16bade98d1885670ff48e59929c59c6c91fdfb 722c55efaea965818c6588b5b72f63a5b5036964ce66c662b0d01e85417e03fc bf2a2562576c9108ff535b04e023ade892ffe832c81deb4ab1cf69df767ac1fb bd6bfeb30c62acdf53af007c5fffe06ac6479fdc1582dfea2e5100dcda23950b 0c597284b35c159e09adee3c5f2b4383ea00488302714851b99f0fc6a774b7f1 86af1fe8506d70599b9f8f277f4a9467bff5f11c7838feeade171b6d62c109da 3ba725fc3c37b7c284b280ecbd3030d15b99008537d072da46609f1118322170 0185bccbc5d27113551e4eaf0dac7a724fc0c5bd8e03ab58891a5af79d0c4540 a03be24b919fc712ccef8e92f8402b32a5ac2e6ed435f13a01ac0adbe4cfff04 13a37f625c969172b64012f9c07ea8f87606f9a44bca086f77b72126dbae08b4 c370773fc793e240c04a6c6b44ce8c6bc06bbbf85cad228c299a0fe0a30ce63a d1c0b2fdcc1f83f9ff3808ed8246a6bcec7818eb8aa2f985a1cffb4bc71240bc 8df94b166ba97d7b5d3cbc0efac70565d5fd8f1bb7676765433122d3241476eb baed16c73411d8331650189ebe4b53fe9bf2237abe48579f14fd362fa1eca68d 968e2c6632c17127cae014ebf9c72942da77624a3c7e850b821a3b35cf3fc089 042d32521b49938a514044f1d65e69d2e74e3382f9044dc58d33d762f6a3483c 5f6ca5b45a1bac1ce6f04d42b6eef09ccdad558e1b3d5737fdd4c1947d57580c b8f960e7d188503d9c23b47eaac1ebb6ecf00558059a2062d3ff4916dabeb00e ac773dc9ce7620b6372078649fdb96ab0036c6524d2c24af9cb2054372306681 dcc1a2b1cdd61951167bcf580126f4d91854144f410ed394af5a29732c430935 399c3cff7cadc89baed9290842c4d689768b41dbd3402aee4751e4e6dc83cd83 cba3fddbbbde39fb6f01534b8049452ffe7e1837d28e466c0929c7726b5bb3d2 a525c1b382c198a32c710f6d1bbba6ed75ed85bad57ade17efe1deef3cbebfbe b10695a3567eb36d140a8c81b364477e724812c5d5cdd77631f756a678965fd6 100f2e875351cfa296de66052c4a83ae00c2f40bb97eadd1e7ea0068e524c568 65373bf6d75c43fd82031c030df1cbb1f2e54fe11dd9152d06f67784437a8c6b 10900f24d787db19021c7f6ffd05ccd2c1485bc9ad50bea77b5f7e2dd4d168b5 9206d8d7772a8042bd9b3ccae4d1e897dee041227269589a85225df73d51d8e9 e2196925ba83980352a4583ecfc2609194bea93e43702e1efab71c76eb99a77e 5555834ada3f35ac16555078158b1e129d347b2815d0cee6e17bb1db04fab190 9e411227937e25b05de329d88b29a3281e1471c26ee51636edd7b2bdb54532b5 cd812857f0c9592e899749df1d1ca06a4ec898e2d459fd463b87644ca1a80952 c336f88981b39cd2835bb54ff9d40dbaace66bb32005c266e4697d072fcc59fe 2f96e2125d594ad89999559a2d49f196ae3a075651d34351aa729147fbae4c21 d7f34bf026e0f3eb8f989d50f7fe2ca31e3e84f322bab7d956379b6ca67eb8c5 4ee15b8fecc0cd747a0813b82a98727e6557d35636210fa25046ba2ec63f6778 5c024d33681e72ce084e049ac978cdf05f2f690f9eb9c3c0a2f667e9feae4114 eca951b655f40be30b5139801f0eb195e6c1236dc1de5eb982a64ddc5f3e0ee0 b1ec1fe38059d22a2361a3698b04b3633f8c5e90f94e1b2e67c7b0a83d0ba135 249dc9cd75683ea99a1a6d78b941d25f54240f025218ca113c1d10e83e1e0844 395735c24e6403b5f9b3bf6cfec617ac4afaaa68e3861320c6ddc0c3dbba3e3b b88c05ad20d89d50304c08620f6b7f4ef80c50a95cc2877fecd2b929658de37c f4d13f28d1c3d0b5424b8ac845becb99c0c63fdf0d50c36a72a2b49bd64c4486 5eb59459759a26c15ede5971790bcf73fca4ef0bca2b2d2c712ce5d7551c7bbf f1eb0ea50f8c27d0914231446e3fadb7f0f4076b236bd76d15a2178043f15b1f 042c13be06da921788250ae0fe2169ccfd7b8f58258f6a03f0eeeb9ee4b056cb 3e8805215ac6b972a2007adbec893c526522a43ef8817e16dd09637946cdfe01 82f05de9344173995217794bc4d6a239f157038e41858fb49d73eefcc4ce979f 779060ea9dcf24e80010259572f453b743c818fe22d78f73c178f4f8e15f8340 8578dabb1c0d7d98f5e8cdcf2aead724a8fef51d24850eac7acc4ec67de107eb c8959add000fef8da12a32535810a4629c9f3005990500c472ca0c115a0f57aa bdbef5249d2f3fff40c7bbbf5293987ef976bdb15d410a62dbbc80f5066cfab3 82cf3f9a8c5131e29b1b1cbc9f3d48594fe30a5a0faf9f7b4d3518ff44905567 cb5a1a999801bf308a00933438df9adc4ec64b82a91cc819f46fdfa4dc63cd10 a2b5ff7c5043db55d1ba34b94dfbbda40c407393b2b5cdade2a01296d6a2d6cc cd9f89e414c961caf06f311ec8dd62f3941f116b42ee2220f6cf8730af87f225 94f778566f9ef6a3eb371752f76cf66523306c2ded5612d187210140dbe8ffca 1fee562d0e19adb7274d1bef2616122f37fcab620c31b259095758c361749193 2753541527a5f28886b6079e94031216be897533dc3aea2ecfad2d3b34c14a0d 74f9cf2e7284f89e175f7da2ef898edfb392ff850bd0469cec5b2999ea27e413 0822f78b4c9670073a76bbb206aeaa9f56d2d4c37115cbfef360375fc2bc498a e3bbbc6efaace48edb5dcce9a82bdf8b1a01ffef4e0ba272695ed17f31ee0ba6 9c249607b3ec14755909b1a4b96576864490c7c6cf077222f40e604089c2ab78 845830b099308bb31cf8d202681d2f42a709ef7a7ff29f25fcb340b8f6f1af21 f18514f95167651c539492ddcc5a154da18999f6fb5ca844c6661f113f36505d 1095e5095d519c92794caecc036b98052f8f8ab7dbac113cc144734cf6e732c0 4fc7a8f505c45e9cea40249551027ea08f7dbfc56f2cfc7dba28a31f8ed09695 faf4bd877cac477edefae6746b50518756026be3f58c3e283325f8b3c68546c9 ec3ad5ebc4dacec3ed641ec9aff310f11f6528b186175affc0dc3ac494280e90 6762ea15a154777659755f3e952190a37166ece974ab87c0f0ae41fd7e919646 6218aa86f87c50c1d29d74530fc73096cc5770e41c34bed1094ddc66835326cd 6c6e8e95cecfdbca5a6a3f81efcbbec3f6822c94abd6824b0b2ff04d8e005a28 42529960daf5e9fcf69f0378c7fab63dc04a440b590129d09190b43adb33877e 34da8e22d7ac3501057278a7976adcdfceb2f0946f9295163c42b9969f349de8 3567172889fa533c80d6345fc3bc669824b16091528a9ac6363a49555b3b1839 83d3c5a2dc852c1de0fdfddfe67bdabd232e7e786aeabc536e469cdc5a96100a fba85c323676f6f3d712a7fe174a74291ac8a7093279ca4770e2fb8b53292245 9f745cf4495ccdc23902bec531bfe44ee6608599bf528860fe615b5e64cf22cc ec2408378662a405bda9cacafda3bc482bf0ca4a5c3ecda11822ed66dd23ccf4 e6e2a769bb7bc694d5334699e807b312b31dcb84f12c098e9ebe730aa0aba197 86d5f8f8b1ceb931231e7955ac295f22b7dd5cbd5474c6f17ae9c67bde08b7a6 872a5aa1c00ab676ff697bc965082db4b84c9c5912b13cd73e238205b968f6c9 98e88fb15b4e7149d36a4d96369605702421a82536601f0d92a8a22a00196a74 b90582eb1cbf5d567bedb375902a66f452aedcc9fa4e610361b1994ce9756c9c 46f7eac37dadeb7d586f1f2154fb93224ffef25dbdcf5a50fbe2058fe4af4fa6 1f7515f2e3b6b0adc19d28b1733b596f408b3e01efd7a1c32d8a730687c83303 d9095d067e48593259474e88b1bc9d7c1b8f7c9fd6dad38541738577faf674a2 13b7e9dad168b21952ab494ae468f7eb3622eef3c93754dc85ea5076395b8d6f c53c69379620465b2ca2c0b55e292b41771066169b131d8ede8d958de335a124 f0f67b6cba0a5e26f0fb871f3102afe16c95dcfb3984d36f77158c55c2116055 83d6462bb79bd75ed33ac87b5a7792c49f6a7c6ad2fd9462846a7dab0bd5ccd4 e105fda7964432c084b06d5a4ec3ae8bfab30b85fad2ac5e12bc91f570f06c1f 7281cf80a74a1ec7881cb6173e8c82a9878a07ff0642d24408eeda7de48bf6c0 8841e7f99d72d4523a6434bf2f7654354f5d262a9f25d06dc5387cfb53201fa0 7221681239e07f9604d5e68a46965faf97af8c651600abf993177a340f6e88d3 113159560276fff26c42d020fea67f3d02536350cbd7b4ea36f76c70c300624f 14152448ea110c2a7f6efe4bf7173821cc7436646378634944164141e862c083 3f85f4b21ba09576235a74bf59ee99fc7690159934ac709639927a12598de4db 0ffb0791b151164266c3f61fee62371ad4b847681e39f11545fd7e79c8049a87 deab05a92357a34e87684d48570d216eb3a600a40ce746851bd80e33ec1c68e1 704537fe1c46b6175756a847e8a6c8a4eac68c3834e18f1aea4159e867b4afe8 9432b7a32bf0e82599f4c89b837400ad18bd09a29678d52f9ef0fe672e5db9e7 72915931e01acf984fb517b671b1c2a48bde0650bcc8dde11ecdc957deb2ee0b f0abb70c156aa44e30f7ecbe678f9ee9c0036c8912b737f31356714014a28026 93af6cfb7d315152248a3f5832795c53ccfef807bf889c9e08d7159ba5c7bbae 5cdcfbd85daddd4f1342a440b80663d03545d138fa31afadf45f8f97d6215145 bc67d0bbc9c1e729334eb1f625be3f76271f67075e080ff2101f608cfa6c9243 647f856b3e4a7f5b9f0edbc3c2086a14d97c4cfd940804e93e7fa38d4657fb8b b04035320466e030fe02365fc9835b0a13c75e171dfc1fcbcd058af05fe9a193 f804d2682dca7dcdf909155f817115f585dceb66b4cbcc513460e48dfe2e0ccb ca00d1eae1ad641fd240af58d71dd5d1b360773df738f76475f9ddce0743c053 99de32f96953e27f7fc7d6ace8082625adad8f875af5ccf98271211f6ffbf168 cb72c37155f98e712331a709f85b2b4cc137c3065c93b9a8750612e0bbc55797 ac942c8126db13a0a40d133df902ded357717d0cb8791d9c2b1b62586ec0e7c7 09e99ca05570543f80cbcf9c6381e19e8d5f21c00d96c9d04620219b842cb88b 6c754834e8f10b4f7c63b15b62bcd51232d514d1cb6542d44b84a84ce6d35c14 9cb2a9c1e1b3d50718d06faed604122745d6a0c4b7218428d2a40da6694b0b20 37fa13a595721d0ffeda06edcf2a9192aca945e8f845f39c850361060779bf8a f6a1fb231730587cff8331eafaffe84c036062f58c23313d0366d227f936dc0f c4352ee007fa39a2d837e554c96e3ea9c5f30bc131ee4bfee4691af41093e8f8 7888f2ce93dd67b62dbd401a649f2bb53030cdaf63958062a3afc269c65aa1ae d4beedaaab33b1ca7a62d8e3922a9066cc57cda6f5f53a1821c4b93663acf2a0 86c01b16025f966d766445addbe03d51be961d39f02611b1be6d52ce2e65ae03 61f2347d273c611883aeb9d63b502cfbb03935e4dc57815f89b2d960ca5f852d 88de1640f8120b16fe3cccfc6cbafdc3b2cad4aad54cbbddce71dca7e5a93d56 234e16921e47d84dca0ecde65723ffac02962de45664eaeb48f886f502f127f9 a7cc083d50070f1f751506a25c920ced9af70758dcbc609018ea29d2bbd92378 2114d7509873f58a86c60bfc37cfdcfd9a9b3d6bcd9adb7ea2f872ebf105362a cd8022df16cfa6aaa2e4ba5a2a387ea45695489724e472b48af3002557a76ab6 8deef1f124f6471ba33742a17098f6bb74c0d78a02ef47d303d616e393eb4519 48b56089969f9016e90b1a0aa3bab624a76ffd2d5bf7f8938269b114591141b2 9f18576b44e23d3c53213b7b7e4ac62a106282fa4561b3909c2e8576dedf414e 6839dda8b939921fce2234d2de861429ef0e896e8bf53734c49a7d9f9987f2b2 b52f6b08edcdfe3b6c7913e9da807d82e25a549e36202b8a8950f0a14fbc58d3 558d9906fe2fe216003200d96b368d4277f950cba6ec1949be6e42f0bbf522fa f369d7d155899f1fb4351c2a580c78ba591f7389ade0a8be48104a149dba7f13 06ff79895fbdc3db1d298456ae015ed3b7d61ebe3c69df56878fa9d6bd4522de 6e90d3ac10d5efe134def9d541f0d840c09ad7cdbc80e7ae326c132abdc919f5 b51705d06714d270bc2acfce33937dd15cb0329952c899d6f4304c7476adacca eea364ab4655adc1d2af1dac26b24fd8a6554abd57925dd4bbed807d6c40dc3a f815a1d1b99c386ede5a04765bfddcd83bdcf4d3c43cbe2eb768e9064bca8bfe 542eec4353c7aee91d9415ba2fd07311a3936640f91f1b061f1a1fb62d0fdd0c 127 f6f5272c51d08e7f2454fbb8213aa3aad68414f224bdcd1e0fa238ca82565a0737db268e942b8221d43f6d5ad5f751e01d3864fe74c79fe973a6dae825862c07cc550f75dbea9f1044f346c20b727c1e2e31dca662203da8878d752edbf2d30d84a121f2d7cb38553a2b6d9ebaed085c06a89e2603eb0cf7e73073dfeaf9100f578b6615edef6ad73157a48c635c1ca974cc0ccdfef58df188ca036bc7064209af61c971ed8c191256bf0563412f83045919d7829a689bd515f113dd76f4ed0bc231f97abecdfa358f1b2c2aa38cdf661413f4127dcd6312b36840c4b4db16035002fc9e92a5bab000d78aeddbc23ea79b04044c457266f908770d3a1190ae0be9d330cc701054c5e829810763157646421e9600c96030ae9ba1b65c6cec7a00ae260fa5592851022883dd31d7bb527512e57b7978c10752c90dd5b9ca84910c4599655f20925c01dbe73d7824b8d96cf59b0813ec0f2d17535365a4f125cd00df621e6bc3bbf50c57c7ac08c48874c0c6d5b96ab3b24f22c025377260e64b0cb6b85c55995d1692049a9fb4b85a20befa6285ba0005b6f272f7ea52488c9002c2c99f17fa41261bf5eef9e0378abde20a562d9c9ed0dc3047e947337550ea0112949780af154b550bf663372363cd9611b61c0ea6138b4e6fce02216bcee503839b440d9e199636a516ecfdf0a3bb61ae40cac3b8f965a2708fd5cce9446b09293285edafdba94dcd7630e06bd7f15d65b01d9e9cf6a9202cd12a96db6c830317f950ab055d9eec8caf96533802508c07a4e89ab64b73bea2c83d8628906a085682584a09356cec579d85c62b891431532fc920aaa713474df0f992c9464f0b9355e2f41a7ee0e26fd7c836012d1c6cbbe5b4cd4da0cf33e4040fc2f438b2016912743580a0d2ab9f6b86e0558b356778aa2f08f9043f735ea37b826ddfe2029484ca684535826a460fcc3b3ee3b698cf08c8ebc7f8e5ea773414b0895f830bccc2633a7ff3f73350d99ecda55303ae712c9942a9c1c6153e84052077e7e40b9c8f247d4c2b84a6dfc34ecd70975354802f8cfcdbd528122b210f2c4e15830bfd42c45dc9a3db6446ee242ba0208eda031fa6fa028d3b56cc37b14e2110880f16fdbf0d85a4fd50e26a0ca5eedeae8df2e88b3c7699d57b9daf18b1946f1a00061a75b9bcd55c5d04fbee6353f0e002aa24321779cc9c05e9e2a305be81e00dac9f8dfa9b1300eab133603176e4b97acaba4958fa18a1661cdf97382cf46204e5b6f454f5bdfbbad171049b065dd9b8181b06301b1dcf9fe37badeb9b238b0756052a6d5c160caeac8a0ecc84e0958e4d9569cbb2e7bf0189b4f99651939a06d30accbe9e34c87019d24e6621fdce5fa06196b6151b584054d370c0ccf5c107edba400f99f70873468f72a31056a8be38c4562825402bc674af096bc5670306137b0470e771b7a1b3e5e7c98dd10d99d93ae87f2379abc9a485568b4827b200e992660b0aba8ba239080c46c9f81c4abffe8e9851812c9397f4cb7baced450f2752189331d2926594927b159436b12d8aaa120673c1e4719ec533757251e90b07e0964caad10de6618cd27c815535d12d900402f00f766b6c7ec4c8df8bf60db4b5fbbcf8dc387a6828e1fd6bedb3f577fafaacca60ae124e8eb8d199027a0d2fd53964fe753c0b22c02aaf67f7050330480ec675e9e7d8704cbceca66e9d0d87e6582b7ac5b8f2a72d497eeced7e441b9db7a2bec6fbdd32a8154337bfda0482b0c935ad4fe720bacfa874af01f4375441eea1d2aadb2a666d90ca4dadf20b6108bfe7d794d5fad99bbc8c96fc58c44b7a597584fff96ef9ca2a31191cf102e432f92a20e47c898fb778dba7fd5eca605d431167d1a8ec67a8a07ef061cf0a0ba1adca9bfbce010d94fcc84e3380ba5e91b1389e0fd0577d178f2bef9fdb0efe5fa661963ec5f75d709d829c23029fcad65d229ac575d690263b7d708fc50a129a04a8ef845885cb390c1240b4b5a9f91bb5e7bb55071297c9a1fc9261b0080ead26f8188ac0c4ebe7813ab5cfe0579940a8fc5270d06bd090ac40ba663c09e43bb9bed41831d2642ef2478dd4140123418749b2553d990106417e5ad593051b2b21c2710fc3cb572ea10401c1b3b7cecd75aa859f4f3c81ab25e318f7f8086bcd6b76f3c532a83d67c72ab6fa27d5c583e6e90add31689a97ff7020f1f406961f23167a25460e7f59f67f039b2ff65fe687bfa4833211099d37ab8907fc0cc0d6a2317fea72da832ecea55897f8cae9fcf9f53381790ed7eb0703eaffc207507d1240cbcd8614317f6cf3ee5d8ac6365da5816685a6b2913a92bdd2b99b0036ca88f66f1977e7fdf35d0becca6e21c069273f38b269a510b65d06959e720865db712649429c3dcc0344b6cae3ec645e0592d211eb213ffdebb8a15c0f8f09278c8c2b6e0df86bf0d2adecd7cbbaef29a043d0d4273f499126c3657ac397041f492383154eb1018a44b6b91731a710dde36a94c31eed10d1f2271293e2ca05a58d0b258cf7663baa635001224517fe0b2181961d0d988c3c352db63d4e1a0fd50468edf239b6052c62fcce17fc6fa7dfca201e09423b332d82db8dd199540e8bb6c8aa9f0be4358beffcf6cd87b0e7ef1a33afcd617e5d1b94cdff69834c0695be85136b5d49c063ac2b09b06abf03a45698e0125ab938cc2448f1e1118d028201d602f8294ea1a94682fcc8e86d9a20e908fd75226c7d3ca761c78f9c90068b16e334b0530ef5fc980c8ce52fd8126f1964f9fb40a3cc1f84aded8080740573cb4d3fec577be13253d7e790de0c57190774244348d28bce8bb5f395f10d0507a2189ed7c500d5b8fd26a16a5ddc5f06678df168a5549f5b3be5898f24a50ed94e8c841aca1fb02f2dbaa74d07518f170dfa67a1c364d3e99d193ce2ae120bbdc028442a3d83d92813fc8364ad96216ce029474aa35e1b4f47744b7af9af06eecd6547628b2737906c10de34cdd61de2b3f30dc1c6179bd4dd6dbc97c1660f156b932010ba9be9606ed98d63fbf97461799fd1771208645bc384b992b0ce0378e9ecd4d6fad69fdd36d1aa789b60917b46bfbe95fa388d25657ab912717c0d9caabf5a2010c0e0d31048e89fdb72c02556922a04bfe49d568d360985da360576e4f78bf949ad04f13df9fb3060437d7c39f12aa7e373960ac89511eb49470ef5d9dba36cac787ac18ad82d6ef17e2342661122d93887078f054fd0d1ec580a7efad90580186b319445d40695985d9ceba28f6d2cf24ca84caec98ef7a08a0d3f853f7f8409bb2b75f573266df603e76c3bce920245d1a48d7ef3d103136f0b3001e074374100ed373ddc5a32b52fb9cebe98cc173fb3c26b9bcaa9e1df880e529b1b337599d9243ec0f1ec8dce857cba0d8d4c9ae0b0a2be67d57a948114037fd6b02b7c1cd4a7f48bb58158e959efe95608f06ba39ac5c26b81dac2799108e907f0ad19176d2b92ae91b6bb765ee25edbca1ed61b722350fd394cc518040b8af75d139ce28cc739076e52f6045ca8b6d956e5d054fdcf443eb747bdec6d039f36063f87d29e3b29c593d5072b63d5677874ea22b5127c7c4495a85060550eeec8563ce78a8182a190e23a2d17f1147b29a87464a0ece325629b0de7c8b60db316c071da14a4f261391b6887974ce0395f9a5507cbcedc8145197b21053c0b70b006aec59af5ce8a7f67a8e6290987a345287fe1c095755c69172a899a0e08c10fcde691aeb320caec3dfeb291bc3d893a77a6a59b1df837f71dbad054630ac6d5a3b7f8da3200bed089078d78a29db62e66325d737f0076c0aa724bec250610b4eed9f6152be083d56fc480b5d10166ec6a769e692b0cd0bd1ced906368025fd4bd1c4f24c8cc6d2d7668bd5e33e858b71318c7d9d78596e753d536c9060619f2d34b3237a75eafca86b9de47d830063a981159cf8d9ddc927a7b7a21fe0a68d25467b6e3dbd08c7923e8b431e1149c09237ff983656a8dcf78926e214e00eba584507a4321eba6fcfeb88da46a100cb6b39235e7ea4f1f4b6c4b7738cc0356eaaf013735b2fd615cf3c741790e9132617c4a5f01e7359705bc875489000a5e39248f8b8923f8799e90ca6cc942b94bbd83c43941d0b10ede9b6ce0c97700ce736a399acaba6e86bc2efb5bae695bab4f63d5bd55af52360bb02c4a10fc0564047a8cffb4e4b9b3c939d7331b4f17ca5071b24851c1bcd42ada4785b52e0a1ae3954fb75862cf31db7df00265c0bc85426c21e9f5996a789d45f56e35c304fe5063a297ac690bb1d529e6fd4ad9caca5df5ed74b1a82ccc7655c6c5cf1305ef906cf807693044a7fa18a2f432286693d40577757dafbe9665f8b5fa69200774c2492237e705316331f5a624d3400e4e7971dda2c5d4cf9f112f67753bc90cf86f09d26b1a9ece4fee6c6005e1b6d9be8efd92c43824f470db90e94763af0cc8aeaa0601a4f4b7f286940235bbb5eecb60796dfafe1f58a53f566ed4c1df04bff2e17e3dd053a89325943da3685cc01cd0671b81d7890ade28ee591e98240c3c97bc5ddcf320be785b8f3e8f5bbfba6bfaafb65b5f4973f7ac763b80f1c80d76752408a19982d742492daf4dfa1079323309a405685c25c8bf0db264e72a02dbbf683f785744fb9d49e3a0b0f3bc4abd79e8da34788c7c4c9a0d75cd09ad0b308c3f9e869924a1ea21b4e0404d322ec6a0eabaa4654da8d1d72375ba34010c0cf4b230625c002cd48db7258ae08898c00de27a6db101e08995e608a6b7830d5b1e0d60f8089cf0c6056a0ec6d53d56461b681b0af3b0073a6771380b92710a7defd12fbf2a821b13985988458af7e6848b21af018ee3003345afe072bf0a0c685b4bda91a0bfaca848b854ee91e22f107ab30e9af064f0326c92bdc8b1c1025cca3bcb692866578f69f498c130710274d64da47d0320fa8bb8088a15951f0ca0a46abece0a151ef2996c3f359f9e781151e60820c9215d45bd29bcfc188402ce1f960541d4967d04f3fefaf06a0fadd21d8622a36896daed78cde05ca9a10ed44dcc73301c8410bd83ee68add1407f268c44d501aba7039e03b12673245306ef7bfb910d3f7c53bbfee36fd8850964ff419db6b144d99b740638640468de0383a1e8b75d666f943b5f640a5ae0443442a76ae0daff5ab06dff374577edd00469382c476ad38c8248966f702284ff6357a320c17ab5147dd654ded11eb2020f51aaaa2afcb5be03a90fa799e53d94d2768b3dad2bfe3d3d020895dd08869903d78e554bea74e08ac15f095d189b72b2467b365c76ddfcc8ac84855902b5fb050837eb69cc78ba404b020425aabe290d683d1adaa1fbabadca63a44f69bea90e42f4f9f7809f1aa1ddd76906f9f1da1e9a21dd5caab197ff0c048cca1aec290db5b1f1fd3cb20ef4bff26b417de9a754352af14ab44ffcfa4b8541c4beac6e089ea0beedbec2fdd8115ef3b9adf9388e35cdf597196d86bc0679fab663b7c00728ab5373b5b822215c6a8ab23249c9e863b7c72ec29c1b1cf9fc964c5f2ce605b65062a1d546660eb74455a4584959cd66eac8ddd39af96d273f1f21b0a6c808cb9a5c0b9482b248fa98a04f546cf0f5e177125d137ae4b223ed40531fe13d0f7c265c4f1494ce3ea449faf5f416af4564354eb23d2c5ce0fabaeb2244b0420ce1c19c752b4a14f531574c851e4002fa20bbb57405ff19fbe48edcfb2eea6d08a6169e3ed737fc2739d15d99fd722bb76f6ccd49a6de5d0a2cb8833f5288820898a7358f4165f5fae0372f4c857e6cffe477e2bfd42f54848e9196f18dc9fe0c6dd1480014dd1cfd1edc84b2c7ab3df7d7b77e66d8346ed9f1d701ea8b66350ff919a35ec6bed8324124a21bb567369f979deee7984ad81a9d4abb931d1c590c44e058e154508a6f7e5d8bc2d47657c0863f8f00faba53fac0f328dbe6764904f7171500dc91630d7691408f36af7a36c521a39ae7db3e9ededbb9eccffa9f0201a6d22698036497a9bd9af50a2907dbb9fb5b74d9bbd3676d2f74f06bb6500bfbf9b3113b9a1a660813d7f4ab733d6270dd78ba376c4fb81281386c1616d3011ee894d15f41b0df104ab39f9e081e8a66428f9fcdab1b747db91b18353c6d0c7ad4c4be69155d31afea6b5ffa1d5c07544722602546ab26f357eb501f5f8e0293cd216f24a61e600d29a8447aaa426c3cbb8e23cff63b72c95cebe1601dfa055d81e57dfcf7ee3336565992e8c9007ee5adb57dfe6fa14791c68d8539144b06369dfeb7fefee359122e8640668cf3781f13c32f1e2818c9b88954f4a3e0df0f5cd9a1c909e2e15c52673808680a917bf9a8eb6c5ae4fec927661c1d2f63080b4fe29b8e1cd3c872edecc42229a8429b512844c3eb2a2293f1bebc87900a9b0c43cb43e52b1b94e1d2ad05770c89d56c7253b596bd6fa9fd678fe372c4b38a09a530040ed0cb575afec3ec1312eb467af7e7f12638f5355aa41f2f5490346d0db5615192b833523409103857a662cd4fb09549a927ace386e4b56829d937410b49dfe90e1b335f3e94df0d279fd8e9ea05918e9e47c06e057ec5d2abaf6b1a0355b7718580bf466c2f1638f9f1ae6180d3de3158dca3f0a3d1db2a9238d9700bb961ddb1ca3c094279a872e0b1dbb54864b56a3bc644c6d1a73def1c966236023babba52a55dc7fe4e20a7bd2f02734ae38c6d63074327406eb0c2732b6ac900145d11743d7c53c466dca4fb94f539d917375f9faa8627a88cf9e77d8a51e90d33da02857e8da7f02394c3a12c6056fab7f1a1ed17b26b3f08f47cef6b58e60bf5c2942e27fdeeb2aad46837efc1042dcf14c39c93156395bdd9c4028f26ed05baa074e27ae32471222d38455de395c75af840f30085db30f330bdd4b8b1960ad257c1847a5a1a184a2f3afcd3e501945516c030a8b16d37d9391cfc550af80208c7d821db20b25a5deb2a430ddffb80a4ea65a63a738d01c363beb18769760949863e1271c39d812e1067384c50a2fd66eb6491694a81045746bffb1615cb01c77761e32963cf54330714d55524a498832f338065f8a6421b9722cf05e1270a28e2a6c54c3aeeab40ce7177e1d398697c192c3774c9ec8e3593f6bee067130c4a366e51e325360fdf41709f5b6e75a26ecd4349409871af244f9f208b89680f7bf91af4becf97ec1c1a049e5e0bccc380007b4679986dc232b62b85ecd6f105c71450705fa92e032f1ac7b49f27e1be078dc039f5e678fe2731195a20f27307e4303b096fe9772d397c20fc3f19c1cd148609701ad90a6c567bf3bdc507a109b6e45d36ab4c78f27b29a84274d0d52e2202427b5863fdd958c723c51057f20288b0b8a93cf95cf10841197cec5db2f2491535e72026325c23dfd14f6ed8ef0387a12ba95c642a3d198d8ebbbf0eb1de63d4b481d721de518769d0eb79d90809b4844ba44c3317e623018efdeae26e261d0c9e739a5c266a0e8f4eab15593100d577e005c6095f0ba18b2da8d5617d2853bf9f2afed1cf9d5a4481eab86e940de066f2ed877ca8938eb88804de011c14d48d740d47efd1907120d01179a362026f7bf35a3bdb64aed04d2ac4779a0faba8e9f705c5200135fc1ed93924db780295b3a2d9ba83ff76ede97eecef7c8df3e2d2b6808508628dd07b02e3d95cab0fa694555f36c424d754a16e0e4c6b9418e6a4a1bf37848b3ebbc2586397faee09a1b7aff4fe9567009a24209f21079c2b28d96c3a11cf47e45c29b7d98b01780a947ad2e2d43a2fd2b1b8d4e6672af2565864074a06c5580152da38cbf13ce50183239eafda1e75efc406b1830b3daca498e1ee2b9d4a0b298c2f1fadec1f08099a45a7f6637f40f413842f9c7e97d0ae25c59ae9e67e7330d1b4732e01072503f2ee1fef93160b5f3555f5c79c87e9407235198dcdbb378eae108c3e778728012b74cdbd81178ec8ca4dcc907729da68aa45bd89021760143eb74a2303afdb0d85228fda3f71d7317d801e3c36d381de8a5449c58e91c8e74b4a40f8dbc34f0932e36e529bfca9dc7d900b68fdddc8ebca8540ba98af4606a54dfd63442b900760bb60f37d4502e20b777195aa3b9ac6367fcbd24517d68c635067ee72c7a004222ee374cb9cce3dfae04908c9a831938587d62afc3fc3bedd7ebd4d1985900b2ee7742c9759db20e5103a408003251ba743b32a609b6e4d26c4c3539329b90837aac13d5882d3f1cddb23ff159da0324b5bba80584955e25cf74743cf0358029e5173c4abf7fcb45b3f3474ad5fae005277830bb350cc2ac3ff23022f9db500761a4d7c865dd3879e70f9b95c30d33dd3f5eae15c6438ffea0565c042b61403238839bbbad45acac67c7b2ff31639c8fd940ac5f82e2f924130671035412503ba250c182f0757b2f03ef3c563de668f216b3b47e5ad0b338f0734351ccf9e019326de888ddd9e40ae7a9cf16be264a44dbeb550611d638503dbfd367c6dce000835fa7b54ff314f6c7d5ea3c86dbb01f8e600bef4d212c915e39ff84bd46402ed8f11207a4dcaedac8302a3398fe5c49ecafb8f16d69aa87074694b7ffd7004d0a0b05e0fba91ba7eab1dca90b2e504d4c3716b92a3a5792344eeaf7c67800f88ef4bdc37ec0b8bd89f13f8a0d823f2a9eeea9857bf5a58e5314d7dbf1e3e08676c80988456341c0475358d4d0606ad1d65cb5ef340367d9ee055788873500eb5662cb7c47ac9fabfe1c28d71f1a8daf2390d71959db7fd31adbc6b1c6b2606b6d6114d7ff9f33dd8734f1860276d98d43bfac59171a1a4328efe5724f12106a104982e892c3b6859673a1aa0717a51f8932122f1623f2aa8cf3af9e8ad3d0bbfcf2169589266f138171dc5fd4006f8d26789b89e77b8311452c0d08a994804d292e4cd9d0c82350091a198ec34ac08e6a290d1a5a01530b79ecb2095b99f05fab30c12837c0ef97938c7b9c9f6c83495a6fa452ad45eb308baf37c11558303607ddf4471169bd91b3687c71dab024ecb88d48176932c2e840928e2f2017b07508af8d56189eb156493e63c4384cdb6a8a1ec78043d517c43b8d06c32c5ff02407f6117a8c102ef670b616d068dcf557c66b04c140377ec3ba5a9d1cd11230f3d8e6bf14eef37e3d2adc17e3e8ee8c3ebd374f6f3c64fe6d90dfb2c7eccba06a2177eeac7d16641ab8bee79eb9d506f203f643e348c2beb3ce4943d28bf4a0c4e9a67536e8ce38ecfe6a76a24d72f9ff5a7a7bb9dedc8d70caabbda5f16ef04e792b848696797201fa1cde6519d9eae1ad056911566c62b40bafc0e50c0020ec0c8d282593c702f2d09aaa782a341034194906ef0529e48a31c25a024c49e0c96ce4fadc96bc72e08202636ca3de7cc9eadcccb1e37c3f9f6b19b2df3cb8608e4c9ac778cbfc638983a79973f17168421450890221a725927812146afe57e0f346e84a38f3634549088048dcd645389fc3802e3386cfb5b3034cdf9904e1f0e567bd2df52d7ba5580dc6b99a738f3d64979055e4f13915ab582908c12a9e60a1a60f042bca9518c9c804bab036e5b45993a1bf1cf8ce5f03551892ab398460d437e72c8f301bcd9e9a61159b6a5e0bafa7d46c6c908c23a3902fc1eb05cc103ad80b7306fd55d753631589887742ee5611a147ba5b4e01a671102e12c895309097e045a6f80d7fc456d501e6d85d6e8b0efc4a31f03346d844467a6f042e30bab627e91cfca7aba0f2adefff8916f4608f16c28d9b8448d422041129832cd01c163bb381f696b0504e1d5a895443da2a606521b443934c44e64d07eb33eb00a1fe0d0fb1f79291639fe4bfe4101eceeaaf4f32e6a241256a8f843c97c14ba07ad64446868655f4fb78873f81e388c04286277f9ab1c620966b4ff2cdc7acd0ed8ed072e45b7fd48aef7f7569aa8311a19721b2793db09cd4799afaca643bd05da67356950a26836259c009420d81518a54f7debd7b1bf1570e14f11473c6a0bab9495700c5a4aa68eae805c57482a990e9657aa169d1973b9140b54632d2c0ef4d08934c09dd9a2604435a165ed47a27b9422951d0fac8cf9691ed3643f460be1fa096b6cf3439d46074891329cbe5ddd1a8748448873ea1e705b3ee720b50613011f3ff0d7bdb41ac162db156e8ae50f991dd10c9cab055d6aeb5eba58830730c767e0574887d79e58df086e2fed39b9538a8b8128bf57df57a78c6b971200f6a776b5be33ac2bc9242b4a65d4f5fa249e2b788ed808990cf010d42459ea06038df864091398619b2600d79aafd88aa3e99f40bbb1cce8ba2b305c50680e0d5ceede4cb5fea7c327ffcc7a9a6971bb6242831a2281b401b17933318cf5fc0ce4085772d797bd0d5c67b0db3d436aadbcfe4341908ba1b663a3b7b3c0a16c0665f49a03679da9909d84732a7b481af771fb1f3a172425b189e58ffd898a1f004c277bbeea7b0886f13bed84cf27a497367be6d4406e7939e3ab878d6cc96600d520ba28362821c7e61161ab8bbb90f770d0d4a08be8b8bab1dd68219201600c7315a3bee80212e24b16d8ca0ec6472de07726ee5796947dc0987186ff4b910751cdddfe4bf97da4767ba5cc098c606c7553c2928ae06d8e7fc714b3dc90b2009d1579e5253688b050fd10e0643f8e21b4390c0b7dc94a75ad95d727f417ae02030fabf2b30d59bd0f2c3bd1debf741d6287e19a989e6ccfb7d677c31d7d2e0c6d49703f63aac66123dd81ebba7bfa2558c46fe3cea4f97920f7079ff43ec30a3a8985904607379f864d700668e0855000d03e95324eebed56753d9d575e7006eb15fbbb4369263916869299b96a665684b99b43fd18d85b0e1642018c00e90be38a4b08893bc4edad8145701a23c922586ae398012fb4dde8d1176d1043050ad25f5784763cd210e58303641b9d42bb988ed9e738a228781da76105afb0ea0be9f31104bc65f28847d0fb25db9171a5f24c540e7b5aa0a737d5a9d7468a5400e87af021e9577004a8240a65287823cc13d327d603bf6658ebf66c8b6bcfb70f3cdcb4caab4d515bba51bd982670fd81a510a17a8c795ccd9c75890a785f83091b07cbf70d1a7c42f4d914fdeecb81004a41ac79f737aa7917277593f510eb077c477963bcb9ed7e345225ab6f08ff7d96b48543e48f6e0e5c6dd700fb173a0e5cb13bb26b35d0f01f02b1665eefb50d684e6957e4bbd9bc7c49d05452e8e9083270b4311bad39b944309d06a457d360f8bcea6a12e3793b56487e83679a8505be1a1e01baedf7229f1a387bef9fcef9ea88f3383047d8259430108d0df2f20737117f584c047ce37477d0c6427f18e353d06401e43df5ec45daf6b9e405d40d5f087ce513d30f284fbd47cb2bd24d853396155941c1ea0484a68d651b5039047ca0723348881f2c7ed69dc869cea6ae34b5c8e78176211676e1de6e2978e00248d8b06414b569ef0d2506f4c1cb672197a63c2852f51d20681516f512c9bb0c5afe772d9f7ec463c95f2380c4aed521b9ee115ef9fe72a4d744fac2ec86790f8ec3935737a80b9ea172f0610010858314b877c714becd2846cf3a77c7204d00de2153cbdf65a00c510e88817c8bd36c659958335c8275df2b64a0ed531d490eea214806e57569773b395f1de784b9760d721a1fb01b6621e59239bbf65e660cfc3bd18b943e13d8f3141508c1593d7c4154f4c03d139fe5e437a94ed715d50e54861407d588c70710ec243319f70844333810db88d39f846233a913c56a4f01e3165268c3c2381a7f4387eed231dbbd104dd88e6c7d12beb04fe9b2151dc807cde1efaf306047b51c1cba7d4e2aca517f63fb76947678e597512cfa9638610289925501b11044f779bd89a28fc078faff70cc5a2556730f57451d4f938dd10c761bf3f74b54d16a7986dde2cf74d8735a4e4d10d0fcbbc34b3b11f3221f2f0341842fccbc5317c243a7c7e71d04fb9fdbf77d62a4124c9edb387bcfda2e2207f7c1b80590ed0dc17f5c99213e62fe333046e53a5f199804bb113ce591116a0da14138759d1be9d78d966b7b7b76cd8e0bf846a90e929d63d98ed6251be5e60467e09023caf72f15b5907a87eb7a2c1767a9c5b6b4875654c913cfd9b414f60c61b042042705a0fb4864af34cb3de603246d193a23bcd28685ca36e7df7dfc0c879640e4196ba8090693e0799dd4e7e744bcac839b5c0f43a376039709016d03ec5d2e4a800606228b0d3334b7f2af41c383f2f2744461fed225332e76f7e80afeac8486c6ecfa8fec61fc9413c0b46ba83a8e757f3c7af9fffb980324f194013425cb1b0ab66ee3d4ea97acf13dc7e30c28e99b87aa0c1813e3f34a31e9790198702e32d1fef764baa5815a43f8ad47305d01fd5546ea965edf1fa2eb07940081e383b0d8f51d7c92c5b40fd9280049d802667c6b6ca54db5eb0788d06119016a5a0ff93c3606ff7c8ecbdd5ab2491895641283dd384375b535dcce6ae1670372e01c878f54519c394998e89c7dc05211c954cb1622d5a69ed2250fef7de0063da98aa80bd252c2eef2de3e69b0c7a491769b92dacdfaee6debb70425e71e0bd0e9247bd963f2f0da2c4e89b2db8271e6c7d887fa11b8ca1fab0dc7d0ef8508222e223daad8c6c994506c17d358880f5e659d2aea50ec0a2f6edfad3fc891043dbcd783595dd8c2f6bf4cdbb8785a9f23a02a57e398a8b476507082d36bfa0e4fd218026ef7fb57191f47e204212f0b7678fd7d2fc6262517d74dfa482ac802739d140871c445ab7ecd2e586b10ab534a1637deb06be35d2c4cc1037128b700e16bed7f18385f34762c706a05761698714d07a06ac144b6c33bd9a1b57ccd031a9d5979a9a0c6338636add6fd4938e1c9862f4efa99c57165986171e3c768076c52591db5adcb2116e3a7ec40704674dc55eb8bb425024f451165aca11ee3094cd39d0f46e81ce7cb78085bd0f7209acbbcf098877bb77a891bb82e93139207057a2c8280da90a19edc35aad99ffe53a8537d3fcbd688906b94f0aec0aac6020fbe00766aa389917604db0d2fab72d7a1dd226dc40e2c8ea028be4be9fbad056b1e5991814332c37b59e8f9803db4e5e112ae8eac808b144f8647b4aaf806046dcc70f6a063dd9b4c353364c3007ff4fe4ae201e8a09a44a71dc9be05d5440d56da880d131df7c07182c6d0f4771ef2185cc3c1117e6e27c8b8c028a7d2a200f67a54c09cbe831a11f10656eb29abe70865a58ff5b16547b66e43d9eba55301bfac7e816e1086d6aa5d945edc47aa2dbc3a05389aa5ef71eb572a9bff765e0fa99eb8a787e56cd76990a887f0797230595f9b7722cf846f666540a7b6cf4e062fd4d13c92ceb2734bbb3f9e6a8f96f0d8310ad06af7cefaf1a6b8fa1d207c01265e5b2c50119661793b1cc203cd1fdd7d4257dc6535573460769e70da9d880ed5a462582399d4598704f2447d55f2e5678a181383b880f8917f81ea95e24b0a5b793d39fbc49da3eb1376184f89b5d48e3f4902310af5a17c3e997298febc0faec23707682363d8b9ea42b296a753e1c60a680e9f322fdd43119f289ee6fd0f442b239a2a8f5573315386f4e3a3091fc828db38ed8fb2fa7cdd9b52c2d7c40091304233de71dafa3008834f9280024d64e71f1caf8ab56959584f0a4ca86f062fcb56937b8c363dc0029dd5640bb7edc956e5713cb160f16c470de501af3101b06cc4bbba50ae95ea9fbfc2307b773dd17f1784cc9666b6e12d0d76e4866a09a5e711a12649993fa2acfec8c87ae7848ddc7c19e0202760232cea03b2a3660be2442c50595a3f6e640fd6baa44aeeab598e23890ce9aeff723045a439e7aa056eff3f136a7fb6c1d8f668cfbc8381aec6e502888cebd59e86be130f70ceaf0ae99bcc212cc504c1a822615356dd59b7eaa2f2006a67a0e2784bd57bf13ed90711f977c7d878d8ea2cffa7fcfa0802857873c9f06c3f549c33ebddbba0d75307da9fd4af0ad22c9affbd884d3d951fe7ea0231edc769d4a31a2ca2ecb4d83d00b660769c4416a6a442df1b6b4337a00b26161a08a159d38a988b6bbf409cbe0a276ab86eefe55531b030f88636a1da4a49b367c8625223a021b34234cae15a032088ede9e297b984dd15dd9cf867852d13455c92e7acb814421c1d58b9ac6f05b8483a37ad1f03bfb44c8edbee017793791dbe4e7a8eee99cd9037c71444e80b3ef718d6e1c8700b7e4febf9af0802e995b9193c90c482eebc5e648223b70707e3937b32605b460958b1606050f39fc8ba8d509e2ec1bdb44fb8abd9e086d3077e284e8401d8b841f59b750edcd097ed6813eef543762b4fe8d57c837e73c008a61f7b1b8d79d6ba12b1149d62b04ba74e26555809f2175b0239a547fd1cb00d803a1fc30dc63a5a081787ee466e21e5548aed50ed088db550f9807ecb51d508c91edbb5a03bb916fe4ded89a4494eac0c4827a3d0f9e6d83c701b7c024feb02eccc294ab17d746af62284a19bbcd5246c334dc42a47859463b1d81f5a4c5b09ddef8a085401deaf6a92bd0abfcd7d5917494a7393166153dde6402404d4b00dab3c9b2f8f49ab31d44e9ac2a30842843c40390d92d0d1867e0110803b0e490c7cf611e6c1a940fa9b751f52f421b6be69da012d0776fd6fa0389b764dd63b0f46925671ff31c16487617c103731ad8f4e9200f8ac3d2846d96838d07605ee0a61927185da7faeeaf41e94965e85b1b61826fe781035011014bdf4d57f6aac0e5db1d12c6eeefe5b8a1146d4f3f2722384637286c8397d8da307e60986add30899b97c94311a3011bcedcc8a4d9808b3b7e01e24e734976b6810f2bdeaae110539a9b3765f8711d25908266824982f2696008ba5eabb13db1962c0ebcb6a2d0128771967353c97a8f3d2f3b5eb83506a66bcba62e1aecc51d701e502cdfe62060cb7895e73d9538e4223bdfd5f9b84744751b49d22150175b434e649884a550b1a583b02280ae89a3debb6392499dbb7afa07e9426f93d729cb30546168ef00ff4300e49476fd32411f4a9ab225be8a0e8981e917ea0c66b29b219b68ec98504b4bfa74e6f4a079df903fc696deddab0554f84294ebf615e0e2896fa0410e6081f44c4fa89cc57f9c879ad72f64c0a29294d89573278ebed4cdb8540e1b1930eb292c28a9351932f919d1822cfd5f9925a27601a805c62004a6d07c305cf2904581754da372bb1eb7a4e098fd72b164c94bc77df17dd129e037c195e8a45350bf548dfd637b2a441e3d13aa1bdd73931fc76063733498c12441389208a31320e0f03e1ab2bdd4c4264916a562d5567c2a7cca1896ef63685bdf2dc2cd493e80192f688999c983d4966eb2e4a79c12544aaeade2b68193ac1008e16207cd63907babdf19724c32dff882558a3d88a31e92746bde83051fa5ed97cd744ebcf4e0770fb70ed4f7f1876660e145cb04709636a36a47adf0724582c43dccc08f1cc0c25a5d6b3463c120eb9a26c0c1b38524f39c57efb00a41fa0e4cfdcf299996803d414b588ff20e9544a8bf6a3e192035ed080d8b6996a1036bc97516589816a09be4ce86af24e3fbad7b8e36092461a4769bedda2f052e24dab1a6fd21740b90f7442f46ea9f845705f1a6230c67cc476c386c4cc705b7df09f2e2852972396064f68bb7a89b7fb511c637d92bbb418809c548744b33e4a48eca2c436c0b4250c24432cdcb38a5bd8e9f93de511584b25d4e0089905fc7df7b2b8bc26a6dc830a6a086c3c008c24360947f56c95694002da7c19214a3343efd3bfc816a244c608ada515200e21642c0bde287ea3561fce1b550a5b07f8975bceb05d1eaf9335093bf549ce7d8db9133f415b4e57200b43fe72822fef269ccb514a27e86af55d0c403ed72ee1708826aee2a6fc5747b73d839a72af63e01c3e8beecdab49e37f0389d6fa5566c5ad7f2b7204ed06409c4fc7bd9891eb4a9b2762ccb6186f6738065b7149a343029a5677998b993842c24f99086c80826de1636e895cb4ea529408af6de27393b35d724106ad6df6f9d55da7dc553931b2a8cd0c664676bd7cd80e7304927b2338edefb4ac744cea67bba59882014cd5f2a7deb155bd13165e9a03d09e25c3ef33cad96afb67c8d7668a07c7efd4a5b8fa2edd9e5768a297cb070f379580cf977d1e52d2ca1b61daf24a5ce80f656dfa089916a902a9da8c279b076e193cfdb98dd4a726643cfd32c2d926c1739210d3bacd4d6f93d73323451f0525592977876e1d0da311a6b2a7912ead066f090a8aacfb1449ddcaaca3286502b951e1bdb5fd1035fd825cbf397b558b665a2f5ac91c03898845e70c91e9d202895f7bb57a6cd561752f8f5ebd03fd1ca406be3ee19fe6d0d09755b7ed69ae08fd1866ef43621706bd2003c795702bb5151772b7bd3543cac064d3b65acca10cfd19210f992ae64c6389eed1c3905af1de4eed95e257ee8cdb2eb19d6f76dc06530bfd8a3a9d36fe1eb093d688bcc59d146c1d9c5de5eb6aeed0b43a5175fb033930f539b022addf12951d9819afcafd36c8a89fcdbf9c832119796f5b05230fb0cc5a0bd1c7a9b831f31a4d44a5ebeaac29e94bfa6099057d5ee05b7a54b40941a55703d40e45707f616c09ccf95a2785278d335f08b03df960ff97369d6800df3c1e27ee35295a2fa662b05e844d21f4dc319ca545d61be3732c01daf17b0503ddcb047282427c699c1fa5cb72b0d4c156dc2d5d31952e29b99de823516903714309ac840cc4a194abab1d4d370c0daaa26262a0c467398bf8c492f3fbba0a1e00adfa80b22310b65efcd6a8ca1f6070a12a74e9b3d1bb6cdc2e1e0cde5a0cfaba84a315ae1ad1aebc725205f10d925278abf3aaf053e88d95a924983cc10a20224df24ba4209ef7a2513eedd9a065e9ffebe3cae6d6c09fcf73c448c86a080b2936b8910e02b2c0ac9a0e19dcd35d8a2a8b72b1d0306f66aed67875f0b30e64bf180e8c9996622a50f41281a39b401b8c40cb00bc5ef2c8a13068a93da409b43f5f79a7d933efe6664837e1b7d92559b3b50847f71d87a127687b83f91b0619e3d2ad152cb0059277f9d221bbcc1e46d36757523b5b8256d7cf0c3f63ee0353b4f35e9c8b1b1ad97063370ff034e56623ff50ddafacd0d0a83140b88414087130eb92d138864af151615850ac692de53a0ccfa76d8b52bc5613a67d068e0d3c2a307ea6bdc7d3639b99971f94a0c7d7844ba75b796f1bb53f919d9dfb580f238817cdc2b4d5d6dde99114cced3fb5c780358d4463642ff0c656cc2fcf03025b95d9f07211d4c7618d7578b9626cf2d432bfa7e8a523a933ca0b5098e3dd00ae9d9843bd62ccbcdbe9ec51577b83de3355b8409f9df755c3d158dcb76d2f038a0e96db5c220cb20cba1064db7496301a0e876e563a385762b02afcff8e0f014268acad0c91b5df656be0af1054492995fdcb9d5030d4f3cec4b05e9cd3590d3b57aa520ed47149d5a39fdd0ae41168ff0db03eb46d4996fb41769691b24802fbcd8d03a02a3600ec20f151dc53acb43ce49cf35096d44340d18471216d4a0f339cb39677d5608f1788ba30190c000fda661d0df28b76966c1acd077109520cb236a7aafde0343af9693f4645c1b9b5b22f8dbb045af0c08a0e3661f8a3c1038cef8a1218be58327021838ddcfc032b6fbeaedf8476575acfeff95aed5d5f011154814b4ab400f7137eeddbb45f311b6e4dda472f609bba3633e69f99186305eeb293c0b19afd542f67efdd09e35efd3953bbc1999fc0444e94da0e232ac20572fe2abd5d3742135e85a7323f0d15540e890b26b1a9e3a426fc2aefeace4009ea9678e36c05c9ce9cdee9a1b31c39d6cb080f41e11b78d71f0e6365cec8c609b188a19dc5b6e3be592b0887d371da701886e5aa688438d98f0e38dc24f9ba052d1e422cc8093a23500de0945531edbf2026205a93d3c5a1be946dab7669710a8f183ec5df11d474411e952c138a26b0306ceebc71924efc8a1a1987c9af7e096db3d82e56fb4df40b862e455cd5276c7448bbe49f7d8f4c8a6df97168afa40b20e30e1ad005451fb88f7abcc5c80a115ec131376c868ad72302eb98641ea203610d3ca88d67e238833084249a04a5977285fa039ad1b2bc555aab1ac3ba2d077881438fb75f4322c2331bded7a94ced0620b0ecd0bc9333ed34a958555949017cac51cbc12842f17479432a460076cba44f79e7a2ac8214cb42c03ef4fda80f188c02823ad6e164e70340cf13bff49b17d81f8b324aa41835ea00add450f30e366f6906d09a32154cb4b4108c0adc78b29d0297636202a8093f4617a5be6b091d9c71004ff9a9d80ef062da4d2fc24dd3116b9d28a9b1493d92368f0cf5d60bf8237516b36d6cd93fd6f74cf93c1a8bfa5f07663bb494a53629e6958ffe1c0f692894052a574c6dd22ec996883c7aa4f6ba92f0fd16f970562d36994e3479014c3280402591a4778338e11b262d8092a03a95b834708a47d313297884993e0db4701d2b8d52c3b6801889bb3eb337683ed94c6d52430aa40359a743c7cb030d25cfb0aeaa122e45f7234360f8641ce35e3a9e556120cb8164f29edf8b249b0904e2342371e7acc03cb54891e23abb8ecf31934027720fd96879ec2f01fb3c0e7130c26f8b4796f71794f829d007b33d821d6265a3c52256d89f838c160e900b534c8492855b9a3c396b732f6cebbeceea7bbd45d13f45e6583477a3ecd5f30fc26717574be178e7b88491248b4bab741ac8648aeda84c788b6c7514cb74870ce6c73d249eadd3864c674d5ad07061757b71b8455386667ddf99f9cfd2012904a5ff2cf8e33895f81107b921a403bad6829eecead7759dd0848319cdf24d3c0f566f170146390e66944c7c30090f89772f26eccccffdf5a55329f4f0f6b99708dbc23cca128087e4614148dc6cb4d140881a447ab05b0a81eb990d478766be037029e717883a2b8d086058c7982be9ac8a6b4285b9e129527d82a3e2cc2d510f03b5df875f98d64912902a2391f2130ccf51b38049dbf9c09dcb129e177c1409c535c25792b5a06d9c04bd3614a24678af538e62733eb8c84b96da4334cebe0b007e8920c587c90e23c016aeab216f4f2686a48420dd27535f6604f479062809c6bdf4c89ce740c5e4edb084c58606749fd8ae668c899b2baadcbfa04e872f0be5cb4fede713f963bfe26bb9306cb6eafd66cda1d59a9c01b03908e67fd6e6081e7e63b6c8c5365cd556412ceff9a960e0add9ff593d5c93ff7de119009d4d0be9f17982ae291f4a484bd008988845f684f2396e081026fc0e2e389864586605a220bd787a6d0af480f87ea13f9da4f80edc734556e98a18a0f5a00c0fee3f0daa4d10c2fc0086092f430f0685dc74fdf9a72eb9e7cbb9af2c00469d25d9c90aecd0531dca4da73c37ee32e7a29d8eec597971002fd9f1b848725f7df62c910edcb2ce33c0ce5fe29a870a8a3bc6cdb8cfecf69e6d2e6fdcb78995dc7b400a075d0cab2ddc617a2b5fb1fb4740602afa055bbde4e255cc6dd8c93264a185400c44392912856cb50c001e778577aae3cd5cc45ed4ba415d81b0ddb620de598f02511e40461b530d2d4528143d29c6539849104e1030b161f274d28f398bb9bf06f7e485f08dcbb24079d2891af11a4cee246cc00e39be0ecff091db9accc50406459d589a094d071a48777e0e67976f936f17d9d2c852af130fda59389c5556082efe0e1f82379993ba1b78ad9d74f3b5e3957864364ac1abfe375d53949f5e0d154ac58f75d82542c9a353506bbfb43222acd118919a50afed33e4479f0e3d0cb56aac4e16e3dfd1796ef25934311fed6c63a03aea63a15bdb90929819bf790e6a8392abdac98bbaf7190b5c23e0f9e6a1b17278f63c25bb3b4883eba7a72901337e45204e438fffa2f9432aa56358aacb1752b7762f107170f63e125743db0e71e347a6a372d42088437a82a3c8c822299cf53a2d232cb022abace3d9d10a0279cf36cb7f22992ad1c932332c4784b34b6dc6280fcc5d5450fb1ef83f06ba03b575cabca822f0f2619e6df17754abfb17bef65863ab7b9767559e030e7f2b09a495e432f207e86e35bf5511d275c24fcfa39e5c33a3bef45fa4850a5f754d0b05f8da75998716f11f2ab3e239d9b92414e68aa2514f4ce2c5432cbfa6b7ae0c88be1edcc887b83eb25a24fdb45444d7c2d80178657bc2965860314681bb6f0a47973946b7971f7c0fa30414ecb1ee0b831797029daf7ae7d2a9996f1c24150f01fe5c11c52156a489f24979cb6a9d81a602ab8d74f41049b2f910a2d7d7110472b0ecb0fff0977172881c3863a317430cf4b6f0587397ec0b551d485f448307fff4d0dd5a64b68a51246df2185d5e55a192c9beddf15f6e4c52c8af6cafc1082cf0822be81c3e6106f20347bbf89eef97977e3ff36b373ed5a5c240b0074a054739634ef47351f23b7f9b88e532de50daf2688afe8f3486d3f0436c12e6540ded35d9e359b031ab6fa5894587b310caa37c388f292898b59f5bdb188b2149042c102623e44458c0ee65da3eeecb8cc414fd2deba119a3d63d98cff3c5e7450e908805c2feecb5d4a38b30d064d634a826f30118c758d128f5ddb456412190013a113289a6f8288e8ffc52a57d76ee2773572f990fae8885f46236a501654c0c681ba1a96ebb0263413ccc676371bce12bea4aa571f75007e0c1bf2fbd5d070f10d6ee56da9e33b666f9aa1bcc65e77446778e36b90d73fac0ed234b99b7cb047e0823ef0810bc0df0d112fab6cb8adead50546c9ee51524303c5596c278ce09ca7bac9adb097a5107e7d12967b9282837c0b3a2fe23965995ed99e0afd03a02e48176d2520545458b1f97894cc12013e935c8d4c6b3753e722f89d27871c801fca3b2b28b74834ef660436aac8db385ddd527d56a0cd1281238ff12063c410f36d0e69d6c833e0c5506c3c6b74b7cc89ea809652ce34f8f0cc7f8716c1a650381d495392f65ceb0088a21db78c17b9a7071a4ab44f524a03f2a760b3c10a60c3cbeeb5917879f9e16576bb728c9ed1f81cadab42b7445a1c3717e814c413502f249cbfc3cce057f2b81ff8b8f2eb881b485e0b85117670150a6f27ae08140030b744d3360273cdd55a3b5b6e61f3a2558921f677c641226eccb0987311d4700fc18c5e054cdd2353278441734289f43eced9da67adda821906ab15e6d2a7a0331ae751bc9a24c0a7fcf50fd4a3b0869bf90f32ea74039028df67ca5f1a4af06c8e3b90654cc103164699e2557a0e70b08484d3710ec5eb464e09afb7acbdf06a2384ca8a5f7d20f8774f76f8953c9efa1499078cb5522dcbd388785c3dc6601e703316a8feb8248fcb4168ba07fbb84cc5f8c7a53fc216aa5fb8ff5326ec60ba8b52ec669604eecc21bb1d9196dcae9fa11be70ea815c810039c8dc7768750db9968b55a1cf80374cd1a040d3d905fe37795ff48540aa5cd692af0e5685cf042165352d19e2a18da963e2651176dd89338890a86e03142685a6e98c44a0a208e6b8ee605a78bb905de42ccb197b4e35a5aefec5971b7f30d31f782307d2820dfb1f8523fe42af6c77eb4c03d1d5750d808dfbe69eb66b2b3bde178f57200c04b4028fa57fb748566f32f86add2c36dc4fbf21a7466cd51c1e925d73f312d20d9054be0672bb3884618739ff3a1edae980b54984f3dcc5c242bb34b3a5db6c0bfeec3fec1eb158cbe99a5b1b20f3b074f209184bc6c1a9c37f510631132ba90410b7289be357be0d939988323fe8048efe4b5496f918acd999b993e9567b57005e6456035e81097aaf85ac6f79ca25de0eb12cc36a30153b53a085454386610c595638d714ea3b53e767055cb2baa80b2854307b68df1580d045d25ff54cc00c4ed60b2b14d20e37a8d31c7006344dfe6025d2f4300315135d3c921a1230a9044f8e3d52a2db7ecd35541d49c94a1de24f669813dfe40fea5b54526c317c4701949ec3b4a38396bfa5f85c42040d1f5bb9c2bcb87db3072df7e2ed50111fc10b539eba3da5855afea320f3aeec539445a962570d9e234a7cf155bc207f97f60ff50f5fddbb337d8c24a3b8b9cced609eacafeb6b4ae7c981094437ed5c61e70b1b2f9186074046f43f58aeed909ff45d26ef4d5d8f8624ba8978ac93360f650d5cfb71737230c505816f1d1a819e7a7896a3fc0cf3c768ef6b3a1840dffd8d064e684c6e660369139da4ffcc213fbfdb0c400f80efc6a3d71a6da7d02c20200c624aa3e3a08dfc5bafdaff23c062ea5529b3c9d759224fd28bfc124bf7fe1b0a3b1db8a825b89888b305199a30ef93ada4d7328877d3214e2cf731a13f854200cad72002d8a072cacacb7c8c45d78f63d7781beef1f08f7d1a60962ee432ad02c8dd50bb503a47d1c7bcfb9c94fa17547add3fa2df94dd70b5e12b155443490c152e835ac2c957a667837dbd33229df6f66d94ea5737e538136ca0e34e8e13052563ce91fafdfccb437ed12f537db1632c3c86e234e91baa7f90904ba3fb4b08 +generate_ring_signature 1013e6d2970665fae054a3bd9fe5e1ed9e80e081cdb6ca240c0f517f1b415cab 0af5247217901d271584c54159546c10a1d682cd243e66a945af30b5eb427cd2 2 8eb838cd7d2596dde1caac7f8c29e081333098096355a3888f6cef9ad54b4c43 ecb9686dedf61fc294b3c96c56ea43d6e57500ca683d8ce93e4212bce07e71a3 f36383ca3516a2e053540ebabe435e3b57cebf3890478f221c4aeb0b8bd4e30d 1 5484e5a1a5eebab7fe9c5ffebc40638130b616c1c0e871f738111a67c932250159a2df1eafcebde2368b6d7291b768c20a68f940daa05fcc367fb2a615f3bc09fbfe3e0d111bc83ca7f45b95d327c42413c3ffd4bd829327b72bb26c9e767b02ec6f64e6fd42859876c8e413adde1602e0df2493e231708871d470bafa019f00 +generate_ring_signature 0b5ec78693ba8b68d4c52d569772840dda48ff5fb748ffa833e5392392c55dd4 be1f92e110606c4de9745e00fcc25187beeb7caef967abd9c355cc032245326c 2 3957312d5af6b690f318ccacc696d12ae70a51a5e59169a823de76c16418908f 4c4a89e48bcc7beb356d78e9d9455a67c53f4624c53254135d0f665562c388f3 6360f4e3a558da8015355552ef2b95fe491529953bb454449caf53a3169f6f0c 0 39446c0f44183224293aee6da91cda0f8549d4f9a6e97bf25885d6c27c54a30264f42140efc29fd3f9cd0ac126447d81b63b9482d3efc9ee7e53249049f1510a9af989c77712b05c7f140163bca32ee53f45cdc7e154f9e43d5bb42125f557035a5dd8a275f82824428f7cd221d5498c4eaa93d37ead3dccdf37fc0951b7a80b +generate_ring_signature a8ae168867259e89dfddb607d8121f78d9bd1afe3b56bdf7f8d44f328c407626 29c15d05cf7408ed0252e1d25c47b8fa0c34878017937a285dc037fe48c3d9dd 4 38aafb4066b703586c33d7c055353712cea2a8fc42bb1af683af4d52194ae229 e258caa68a2c25529b6d845e6477ddd241646d89135d3e860f9138582b0e2156 fe25ca5baae2ae89f703b0d17d0e9ab8c314eb69ee1b5ad3ccc2f178f7a13a89 ff91b9cb17606444470257f1b2520fd7c7eee2e9f62fa284d96c423fbb32c32e ff3f205bd350797a39e072c715c24902a909dab05fa8b7e3338d2ef69a85fa00 0 0d5b8e10f9212477428a4040ec94961378f55b92484cbf1eb216323e6a7e5f0d5d4bd9f066e33af0048007d98781c7591a91472fdaf00048eb43be05f12ef900e68928686d330670987b84b71a12c3245d2e1976309b5e669a9b05fb5601720f38ab2c3814697d6bd0b9aae8d91602cbd952274ce4fd09c96f6e7ce4496c6001a3a4e04163dd315434bace9ef858a959fc709e12256c62068a866eaeddbf8204137611ddadeb5812a3c965e39550d0e2ba2ba93b11c1127d99946bfb80b17c0e604987b7ee0533d46090cb0264fef70845ef779d667b7dbc4ea6b32340730d0c4b71db29fb59eff5b24e1d157fdd12b65e61e4874fcdf4a5246940c67be92a0c +generate_ring_signature 6f7c9d02e53d619372700dcfbde7105963e017372fbe2918ff0331191453ff60 11bbdfd15b4d972900e7d088fe7971daa6acc8e8d97e2414e8f44fa0acce27f7 217 a9d2c0e8a6674d829f4d758c0dfdc0adaa073d9ffa6456c2d06cb7ed9c7a7359 ef1c7c48c75f1ba091bff82de93d8ff68f7bc31da2ce87c8569e7443d359c5f4 044e3b84c6acf627a821ab990f7085c93abd290e5930767d6a177982d6ca67e9 781b4c9fb9e32d73d2d1b963ce415c90cb1844416f7dd13280e1146ba1fc4e5e 05d8c1d8841d0e5eff67f665ef58e5aedd799e1b9949443cc414932ef536fd46 7cf07260320c63fdaca80f7b8ff9eb61bb4f9a9d3a27cbf0c93834cf93fc259d cbb0202ac483c7c3b838c1fd4ca2f1bb47e91b0fbe6bd10b1e45da586d6ef64c 25ddec4e59d2ee1da3a0736e7aaba1969c8c4baeb25b45f7414ecc3a39f94e98 0dcc6715cb35ec3f1d7808ff671b2433657e4384ad77c6e24f3fb40ce407ff9b 207324c43b3cb524a6cf244595d44d9a9fb6bc5dee499308b95ae8a226feaae9 32e51b3211ffb05469bfaf5366e9818fad3265923f4fb734acfdebee7c183342 f3745f3cc4453c17488dec37617c06e24c7d05bcfb9449c21e93c310aefa4a44 55cca614e77dcc4ba78cec6c46f38d3d0172ad2da45c4693733b845449b7211a d01bdb20dda9148e2ae0b71cce5ea5914848f79e3072f0b248ec7950fb0cb32d a7b51154c60f197d723af16e4e0f324a901b4b26393a71669d0e5a621b6a4c26 038d389b297f53b1d35e64b92e579422c93528a82caa3742747cf55212a7d9f8 9255e412cd98623b34f764d2a55c443d3675ddc1d67e70af7d3903cfa80ea18b 0dfe3c5dcedd604a467aa13cc1ae316c1f077eabb02803dc06de657126484f3b 5ed0e9819d6e2d0d113f1edd978e543056ef6e4bc431f95f2076d0d2fb475d2b 18ea161052684b291829d5246f410d2a10844b60733a978571e2aac29b8498f2 7e2e83d2284a89f30052c7d3607e88bf658f9fbb1bfedebc419fe3aafcc484ac d7d0b95c8140094761ed4460afb84bac314b952bae09bd0031961209a56791e6 76492fbe42a3e2e00f6b98c207bd06dc8a2b9211bd27154f2938fa0e81d54589 dd2d108646f82c50e2e1a84de94cb2bf50d660d604182dd87ddf620ed3751c7e 43b77c3b8b7b4f57657194ea5c26858d5d60849b5c2fde7b9e2db9b2593959af 68d833ef2995aa4b3ecd512361d4f99c912d20e152976be8471b18e5f82d7a5a 80ea13958354db6fb72d24ca091ccc979e37e8f5e866097a199f726a15ed0df7 568bbc6de62162897808b97c17cdcc6bc1986534dd5f3e48a8c39a0c2a5a345a d052a818e54496d2db412bf4ee41ed0152510bc19a0863a59def191336be7393 e9202d3178b1ba5cd5de88ae45610a543418e844e9b99b443292218dc1abf21c cdb6149924a134413884a183d5d5e4f88d37a7c7a85897f5d7349ffc66571910 64066a9b8f720038d5094cea5c59a4d87f09a121bbded687e2913561b7462d3f fb6d221ac1fcf65a9d188165549d595bff987464ff5e99ad035deb282d42dc93 a4ee1970e64f9e52e882efe800594e98f29e8aecf4ea3b662f95fab5b9e94447 ff21f495bd4634f38c0e1e162f3946cc0a593f7c738833cc83737a3cbbe2b808 5160ac5ea648b08a8219878f0fe2a2dead686c3ffa490bdaf76f5aa70ddc8474 7d432689aabb6ad473dffe72c3fd10f332fc667806187a1d0523272d69671da9 17e4a72c95f54d277c7ba2bad16f634ee82bf403c3b2af475acda08042017fc0 7c8d25f7cb1a3c7c76eb0f1a0afcff5e0acd6bc497c99c2aef04284e58cda7b6 a2f0a4e90c503240cfed6db5ebc3730fbe73dcd04e083c5c74e4e992b4bfcd19 61731b0a82a84272a3484abb794d41f8aa35f7ad7c15eda62119994fa82e1be8 9d0cb6dcb296c7a1d9f315d9f3cd1ee319a32cdb26f6ff29eb60864d1653435b c57d7d5187cf78f06a2b4192f4b2af9e6e3b5323f1923d520d675ad716b67e7b 66865357df4a203d0365078d80e8393f91157836e16592ce129aff41de009be4 6fa3380a808f73acf4636741ddf415cf2968b5c66822a3d9db9231b981319118 4705319d29971da7992ed65e5edf5a5d4ab65312ee5a555f92230dff85a8f6a8 84e4c913f2f98cdf68913c688ab2660ac1f77034c2007fa33bce2832450129b5 a2d9ef7d02cea31f517947a914a9731b731ed66c50eeb27c152ab8f3cdbe7f78 a4ebe351b09a08e8c9f508938034395f032d7992a67671a6feca145caa354cf5 584b6866fa954938d5a134b840a801dd0eac1a17c24e45e13f4566bde3e9a140 5abd93a726c61acdc9505a30eebd91df1453e1d7cff7d2ec334d986b08f71672 a6931ec1d2b21862f1cb935fb0d1b52791d8f916c2afb9937b51fa2317f493c5 8209bdfb0f4967a4855efb9c48b019d21ef2d17d2e768ec86de7bc57f36c916b 301e708d9fc079acf118ce1623a4d2eefd5ae3765883eb7ae06f0df59f0a9225 7da0a633b9c97d588d0c4f011db498f7fe262f15de189c77d0052e522e624f3d 69680b2e40a4ec561e8a753bf932feed7e401ab505e55305226121dc14084cd8 1cc70e12f5b2cc86a4c5fece7524a4bb842a4ea57944518d035f3f2f68b44b47 69a0efbd545b2662c8a6b04aeeb37fa58011570dd1133d95b47d544cafa3c81a 91ebc540692be156388b17ec9936efeaeabfa760bf334286cfce9ee9832a299f ffd00880cf9aff6051b7a16388171b8a2fcbe54862015476c77f7b3fc46d7086 c95389fa96f525e20aa521de82876391681fc1e62c92c80253f609775e0ad584 a72805faa42fac4965162a569cfa86887b8e6df3f9155d6a87d9f3c8c03c8cc9 e4f035aa88e869257fb5938c550db6d3400f4a1ad9338d412d04c2f60ba7c11b 9284397d95cdba29d6ea6df1609a48b96117cdbee19e841be496b79337d63281 012c09af03e3d3d7f48221672c5bf4e05dff3716d744647807627d9d7ed75e52 b9c9d1f834c86d1113d8536412829692e6794731e2cb85934f3add8ca5296295 70f4428a1f36c543107dee9931b378e738af47b4c960e2360521281ed01db82b 2245991898701a04d0702da058f30eac9f2eb869566bf5b0832f41449834994d debb66dadd879e7875cdd1fcc2e0c73a7164e295829ccd0af5ca6a44897e4ddd 3a094f9d7b3d011c97897ca7cb55c1e4aee5d2833ef5cb56a9d7c1aaf043e359 20072e748ddbda7f5a4f6a9f30c4e7a640997e5ecf97f15bf0cdb068c14d823c e5dffd9502a42b8638da63500706cde697dc42cf941636a3775d4ecf5f11563b 2decf150e5e635c47b67ff2c4c2f1c3cb74d15724c050b985a0d37e01e067646 8ab05fc816919f8017dfad54062e9f93d2f8cee3c011cc077e442df75c3ba0f1 5929f00cbc961bc693eb17131b6f89de73cd2137a6112d482346fdce4da3072f cd245415fca9190b3f5fa2b229f8ebca3aab2e084f01d0638afa99edd2c817f4 a71c52bd92541494e4a389f667ca59f34923911e17ef4ba923a04d238f89401d 8ae0b7bba43ec4b225e4829ffa00d0fa6f995f4ff93ce9237c645a7d7708b078 3a55518cedb46f2b1edeecdf56914a8f2de3605df30995fb2c331100dd73063d b249212b901d73152d5d6575a1ef5f27079ee8a6660c6dba3f0d70ab18079d5d 09cece2cec55abe14f6c5d3681e6a3da9e3bd75442369ace34915c8a725ec880 1710aa52ba925feafc4477ca73dec7dc11805e393642e5a444442989be1def0d b993ca971ea66dad353f6f5c338b6a2c8aecef15bd79ba03838a82c845172440 c9d8a3bcf7523dcc38d1a5ab4ca71c999310589e9712f54d8bdc13075d9602ef 12e8d6c4b4ccbab7ad98baecd77ccc8a1666474a1d9717e227750f22d6f46016 04a3246f91e86c0cea76b123bc08903dfdce13e5bc1710c656cca5d859148cf2 92494c362575eb4a19462b1b14067534a465d331ac233bd770adc155a0e9f805 6baf277853495cf5482fe08582d79a957f70e0a878e86bceaa9af967a3a1afdb 2b5fa12f48497ae1fb670b936287f03f1c473281dd42b594dbdcde4079ddfbaa fd9f84887a617768d79b3492844be5b8da294e3ea016c1e51bce26a36e72577d e7da58d7aaa13fea9936eb28114f9b7f0ef4997414b569aa895500c0afab20b0 5e0faad0e8d55ecad29d0ac6316e17bcc9eade3bda7aa3006d03a2e9fdef7e08 50c555404f87b93e52de103c405c9fb1cb3c19586a0b90422061c4dd513cb58b e6e9e2aed640f9c58d0e2645f8ff308af33d8c8e8124c0a61c8ecc9ca28dbda5 959d3c613b4151b28c1fb35569fd3d89d1e6d276af4030b77cfe5ee22ea7c11a 404dc708e03dfe8f28ba1ba88f2e5db8757a3a67edf5c430ef6f889711b1971b 12256a5cc36afdb2cde8d58f6b6c996cb4dbb3eabc9ef530f9b93466d53d99d6 1e423eb530434bf15bae5f8a7c2bdbaf7b1c10501a6609dd2446636d90a332ea ad60a07f8e4093ee17b949b1deb91f62e1ab72595402d45062978df4c3266819 1379f3a747a040ef4e5073b3ad4e33e8c31902c87dd3d61e6c08c94c979f78f0 33efe37673f13b2ad5a982ba809af30d7480efb4892f14364b1275f8cf4fbed9 d4271609be30b70ce95cf10b2c25bfae6eafe50af6dea2d8306a760db35eac85 c7d1569541855b69bf0546034f9b48afa641bbd532d38388e96cf1768b9af4e8 2aad5b98da163fb561c7595a5949383a6b9a0805f8b0e2a8844e045e11cfbc22 1358ce01434faf7c8e9b2dade15bd514c7e92c6dec4855c2d10683f45bc6095e c3e37be5881f20a1ab5ce33605713ace69020f4d9969b735dd84e6aed56c86ed 846c51c4b6eeaf711c7aab321c05262e21d1327d367bd5fe08c0e92be077c490 ece46633e4e417898ed86f20cec69668d6e04f5215f80fd8d9b7fd1f310c337d f104dab4913ff4d456ef784b0ee10a7bd79f6693e37c670bde2fff363f2a090c da677636d4db6c721915564b13e6b53d12ef075590f76dae676e4c53d67410dc 04a4fb6c35c38360ce5fdc268739e2ef6a2beed8ff7bc95460d62b5adf68f8dc 5e7559290137fd3d83706e5e7c1121883c359fedbbb94c8ca4b2680733d8b0c6 479b7e2475c7de034efd81859b61288b803c456684be12537de7aa99b5d127be 402fd78886359830d6839113804b636a85d473ead1920c9932cef4d9cea5c6f5 5cb4b5011651d2c69e636f81ca0d841807209d841a739bf2961d1480c4edfc55 d07f7d8beae6750468330d553fe1978cb404f4a1e2488c753a5d0749ae524636 7295ff8a66d12b4818b5f35d91f2b0ad70ccc7443130792f4e484a782db7f8e5 fee7468ba7194735b76a6416aa03e803afb3bc66077f65a4fc666a8670813a73 b1b2854a6dbc4cd826dfe66b8c34253af6c3860502bb629ebd8d4fe071fcd1a6 7df1fe7319b51ad76d1d6e9115456c2100b8fa74e46c3059ae65b32c756464ed a07ae5f7e8c60a6ee0b6bc0cd2b4cac1e60a964e98b4da967722bd3d03409a54 2c89e51f3e4a9ca6c0d283ebfc97ed96edbf16fb0f327548617e86b89e5f8dfc 8e6b6ae609a6940736bfc9a50a7b299b0274d9ccf03351d2a8f7694e398abb2e 2ea8b51ffa4de90f3cb2bb07ff59893aa13de7c32b7726ce1006dc88317161fc 904a28f7389923ee6374cb5d32e08197e899cfc066c96682cf3b0fbc3b72ca1b 729ed8fb818480801cb35ce3253ee767255cac0031cb70b47ce4388e1c28a3ac aff0ba9017e8179fff3df916b714c215b85d094907124b4ff5899450665b8da8 27f59c289add2c9470af149c956ff3b04f5c6388926b2984c24e5b6756c847c3 fec3cb7c1fd7fa60da89c019ead2d966e06fc052096442d3142226ff0fdb82fa 42d091f65225d2e064d979404ab5754f7ca4bad7c51406376fba94c4fd0b88d0 2bfb9b80fdadd222bb08bb790e6d188fc54419aa279c9b21c0951548674341bf 2916e5d0c9a9138cc0166320366ff1a1f7c1f491a64159f903e7a2025441ce63 405628a15b6d835366899c6daa9030494dd33216415a88e6d9645aed8c89b0e7 3b2656ab204b8472eaefa1dff67a0f1302d9aa9a1ad8f23b5481a685c6a856de c11e154bd9f80cc376fa95f3fc8fa3af606256855b354b639b4df67b8e8640dd 8bd2ebe7318b454747d500f3833246e8a3edda5913577d9871e7501f4c4c65db a720d5a5a7242952dfe68637045142b76dff814195f426dcc20d317213720e33 ecadd044840a168f7e83a8f2b3f67f5d49131f75fe3322cfacaaff9ef68a0e86 a23c3b773dc8374a0b50173d6dd2a5b29ce66b0d1d62dfef1560462e5a41cdfc d0a8fe4d361bff92a2c8d77aee367a097c301b1a0dd518cf136f8dbe5af9fdc8 f604bcb4374ee29b3c8e4041d27a08ef094ebbcb4ed19559f7515401dca0744a fbbddb30feff43c8798c2c6ddfe3e8b19ec2d3ce405e1f0ea1d6c148e9491dff 54b2dbdc5399e72856119bf552be5cb3974ddf1a7de015d7e5da1ee02b0dfaa2 4eca7debb202e342051c049226f8cb4767442f13e712d3a1742857e3188fe879 829dd2aae457f9465995c79a10cc1c03488a95de1b8dc12ee74ca66325a7be88 ad6d9c33f993edb38182239144b58430aced85e701780364ae9faa243fa7a217 e78c8e16cc112e8b526ae3496bc19f9fade6fa6778173a88dd0bc18f7262e2c5 31ea3fb778c29313a09ce9665a52949dd6ef52d9fd514ace880300a6f2dd1f2f 8615a6a3d70967d960dc0bc48e8ea0ebf59d8b39a5cd8a99266288f4e8316284 65f76789b071a4e9354db6f980cd2c35f1eaea62e32f34fa1a7e4b341606ab3e d5f40047e69e7b5f819b276bc2d277a11c9937813d5924e467b8c9bc81d215f9 355b0bf3c48b6f3b738c8562887e0ddf6f7fc04d1456d202e6ba02014a4c90c2 d137ab92a2881eced4293511a819679cbbc89008d21075ee21c6bb5b046215d6 86332fbeb6b2f50aa906a49247b09f207c425f17f0d2e7a8a2deb585bbf0eaed 58bcdce587e6eed7609d388a55b05e4240a61b568e9e5141591ce8629c238fe0 d6097f96caa48e380a7ad94df021698e525aa113791a2b8be91a7974d29de2f0 a9cae6409dab5099d26ec34a41f9d646663cdc941ef0862ecd384d27cf2e9a34 2751d3a661a51328185df4ed66658f02393f90c3350eda059a4c71b9d1ed9f67 a989804de21383e46dd2155ec0dbe2f5c127f7cd97a127ad4b7c76f017493f87 7c337941ed522a8f61149434e903942c595e0bf68a19eee809e500395ecffe04 d531d3a090f7b9feeaf254438852eee405bf412d41c053023716198d98f8d0f5 625cecc397c1b67441730cd1701986484c2b24b5c32ca61321ece2c4283c6a05 9f0fa0d4e8999b7adf27f9b31824a7c309b23d2be2ee83e851df8c2b660360f2 407b0bfe39971b9e0037dd79db93d164d079f8e00dcb8d4085dc8b41f91772a9 5455a1f8e7e7cc1374f610eaa089086fa7446610e74aa8bb4afda37715091f27 cbcb2b6fdca0f6e72f741e8ca88271c52d913d93784d69e70f16d6da7477ad98 db1e3d9df06599d6485e2ff371d4a600b43ccc249bbc6309c3f9a36de1f3af1b 406725971985265d7a2888e91b955ed40f4e3d1cedeacb710f8051c53cbaf49f 8b28e249da7f5c9280c01249572cba2e6eecc970a93bac53bcb4b9c8e6e640bf ae00617c7b499f288834e52a64504210be9fa398d113ef39525d6a85ce757c40 0677a6fd22fc12cf3dcdca106e05ee8fd18759b78598eedb7e176ac44d65e2d4 8e60bf4afee39109eea9edac28875c31f30e6e280d898a00f796d85de041eb6e cc7249762d8dc1bc2cbfe83e5a3a28842a48e6bf29c4e78c9f115cfc27bf8bf7 a6810203173aa88357dfed1f35beee6d73e38da78a7583b329810a06c46dac5d 431f2e1fe54a13aa3aad164e339cf6463b8190db5f5632d4c096b08e100246f0 e03c6d93874e0ec2c8ee0d238382040d455a55dc0923fd0b2afc1bd1f7d399a6 73b62b31b6ebcc9937dbbe4ac6907609a68d6eb4feb51f7e70afcc912d22623f afde6010128e5d4fd46a0c2afc6d12b4216b51e092dc050097ad9cee3b6148c2 cf660b8abca78a389af858c41ca235fa127cb86098964a5dda309ace3ff34c32 f74fbd3f2d3da14032200763a61edfb019f6a34492184702cb800ea889bf37d6 e74dfa1a4004ddfac92d41d388572a5f255982d40e207189e2c1892dc5af1130 6bf7dc128af4e7c039f52093b80cabbf0342f923cb372712fb012edc469607e8 ea3f3eed317815434b64a8c02647bb49933a341363351fb463198d6bfdfe1ab0 bf607ade73d9fbdf67d6f66f4757bd85f8084a1de1a5411217720e2875b94722 91d0180661702425c83970e04fdf5bb86511516c400f6618c9f66e60fa79b70f a4175d3185abd7a03ad7d139907322418577644a7394c77cda9092e937a1b401 31f2b49a2f3df0b3966ea20984843e4d40df5d3a115cc71923142d2cc9a5a339 12cbd557508932dad07402d9fe599cdf290d8e13404a7f0bbc30cb0e8f6fb051 e87e1757f7257a30b7fa51fda581127a5ae9df6f3a63c79ea0592b63dd431d51 624357831afe4f7f4acac70c457280b1e5e3d3ceb7d6642a7c5062f71bb73df5 beb1b43f58b59c7926aba3b5a873c614d008ae6220a12623680f2e7d571f2931 61ee7bc796acbfa157a882721938f4c6a38ea7c5780bca69f09d11bdbf4c6bcb 6be6cce2d2daa1c1dc937f44c7b290880f72e849a178f0fde7c451bbb16cc960 c6701e6389ea37f45b9faa73b44dc412c43e15f63827f73253251048d0d1aaf1 3bd90e4ae4ec59c20955a8786436fe323545d7d76dd86e977fae235710c2770d 86e7e8fae2623b5072bee6585705ad8c6a35467053011a29a2902bddf28f2e97 f975164d0117adc42dba23154292e0d9907eb1300524f9f146430763f27fe1a4 b1f1177dcdb89c3330fe6137eecd4e3f9f613456e32db51fa3f334882640166f 507565d88391e24f45e3219a723a2201a02c40c5cd88b3e16e7ab8d36d3f0442 682acb51bb734a6c5a8184f716a0b2bc890dd14d80a268d9ca697cbad6d2aef5 d6b05b529283e658771e12cb8db03e805a6f377515d2ee11ce71bee3f22b3f58 a3b057a1933ed0381d0cd89166e7b2cc8c3613f9cdd92cd109d86b4ffb842490 bd337f0b6ac1634f12c067493bcc741b21581dbcd5eecfd1386b4b50803a5beb d46971f3d8b4f9fbb71a71cc887646ffdf1ac95e40c8374881aed95f4e60d6e6 8a3249e79c08af225720d75c7736a07fa6cf863eea4eaaad647788c43b8f4ba6 f25ff9ae361b090d43b63274a411a1b5d4536307bf4c2aa857c3d515ef26335c c306224eb6323a16ab68eac94a38649236d1124f55b7bdec67c8961e411f1fc5 fbdf0163bdbb52808c79b0670c091b39c906583b379eab71153db54af0fa64db 01834dbe726bb20cfd6e8304bd14ca1f2ad2a92c6e0676640ba2306661d886d8 163b7b5864dea3f7fa0a7205004588c2f58326f0a93ec6f4ed42db249b46552c d4c93ebeb66d6eafebd24ff97dd405b61dabb318122177de4b20916eed1ddf99 68800a101d30377b7cc7ec21e5e5ba8f31098a1426f09c5713c59838faa86a1e 697c8ff36a824ab70fdedf3b4890e0e4d767f4894c7a4fe02753715e4e5504b6 169089c3de0a304c16958b81bc597b22424e08e4b44effcbfdc53cfb7ca32592 f7f4aac8edd8081a5d88e1bf2a1d8af7fc495f279183c1cc8ae9c2a2559acd6e 102abb0de31c22bd64ca47ae288a2ec0945640c9d8736361c8e7e5c70193bd6a 44ad04132ef42640626b75cdb2e4aef4bd6f8cbf52b5097db6ed3d89cf980c5c d97cb6a5a0a76ded4893fe31663fb9e463d6734281adbff52fb386a34c14f60e 109 2ab539574397ba927cb3765db0725c77fcfa104ebc28f5cc7df9590017f50c0ffb69b18235d8aac8dc6ae5633bdb798125e4563374ffe1101f3091e171c05b0696e527af64a6f26f5772c5e761662fd1be77b5a634cc90aac53560348d1ab406375a6c25a0637011115b19fbc97ff87915371a465a4e7b5c5cfc4ab93351d30d03a7f7eaf533d1a565b0466c6677447ddadf1917c7fd74d820680fc45ec44309b2eaa4afc831c86f5e0b6739f3ec3611406493bbd856bdee63bafbccd3d222079bdf9224f907b2932091c14b4966dca07037e2b940bad1f771349a8688019303e279500b5341d9bcde8b026ac5bf20adb4d863641b115f91cc05de3ff5b88c017c17f1ebf944b3ffe9cbed76cb5e62c4024fd9edb4789dc5242a85024f10400bd5e08d49c0da204e8fa386f0684c76fa71a0e04e0e4c85d243c35bb862e92502430d93b77a7fa933d244a3d5e1454f5156eb33d0afee52d5cb6ecc0a789e8a0a1206cf0af1a2257460156ce57d259ff26d97de532a85cfae7a32d9ecf6146a0bd90b978e139942308728387b664956d3c0772cbfb0c21166227af1ed91d7be087c2d1db2d674c9b6c31228ce3bcdb7749eef5be76a58493f55e4e56f6fa86209fa93497763dd4d43bd51c9e297c1deb3c45bcd62816c07319ce6a570e741d805cf4330989568c73cb019078b783ed9cd28f9ff8eec3fa2fa593cc75367f0020b904cba6412c12559102dc94ee4aac8a6a213c7f6413f9999feb72beae3dcf40b3dc3e3b6629e25510225ce4d4be09b915a5061ea2914dc90156b99f7b66e76049c0abfdc7c1da5ad18b1b7b4a5342a0070861f9d5794cfedb180445b926404017d8538feb0d26d31f2e68642a05a9842a76676f24813c79de2e9aaaa7f77db01ea67345bc597f1eac0f31af9af264bb9a3de2776f40fb96c05ed451b86a41f0d4f93c16e63cb57ff22ecc58b68481bee10ec2941f07a46973d9953b1686b0400b99d8fc707d2bc99c664860edff5c4375a0a6ee51219d96d1aa5b55142992f0b8f50d63dd0c7e18009b0e00e4cc5ff0a7e56727f8881d67a26372b85a92cd8082cea588a2e1fbf931f4511922d1bcc49bdcc334886a8cd2e4d42b30be533400df7ea0a629abe35b25d818f06e191de7576aa67604f087b3b593bc61ae30ab5023fd1d299aab64ac0005ba51e9adc25a0ccdfd61a9774aee4387ca5e08ccd940647db087089d6de9c507a5c370a9eca57014a955152166210061e6bef2fb5400a0d945ba0479d964e34a8321569d4ebbf86b5860f199a3c071a352097a3cc0003fe90986a13e3741408b5d2cc1d1e2dd4b3de0e6d943f7eea895af9731587b40df2d61cc1fea009fb5b4d71444a0062e54dd1a034820042a5a0c92329e4dd680508922657fcf444ab8cffa20ad32a2d9f9346c15e9113f51d93123eecf47f0802094660d26a37b1e572308238bcc0cd9d8fc6b76e85002912f36daf80ba94df0c41f1f63bacada9e91b96129f3bcc32ceb7fe39f8e3b35f038fbff14343617a05fdfcbdf0fd7c95321c2e30656d0dcadeaa0f123191959d4d3a940b75cb5e690c95f01b74b4e507fa357ea52cd17dd3909ad06738a5ed827ebda05bbb779b5d01a237b61232b70b62a386ee666a37e516da038d66796c5d1deb9925e7d38888076f7520ee2bfce81e1cff19db41e2e4b433a568ca2d020e63e9ec32da36b03b0670768237eb9f9f2a1e08823922726dba9e360876cbcf8d45392b3a069f3dfc061cae79024b95f0f0129f00ac7929a7d2af75c8934790c5cbcaf478ab9a1b280da048fd10f9b3bab48289c97c0b4338c0509e8f33f401519c19b2d6a91bb1a50c219d5d8696684568efd5d7832b7067280ce6de10de865cea3b9fa64fdcb5060a4d9eba424bd850bf3c3e3bdf86711b6717443fdb41a75a7be98b521ce4f3110e2f5e1923feafbb7c2f543b27465efe799991557b91aa02521e17903b3d9e890256d96357d42a8262e0753f78358d4e1d8f9ec870b721753c24c360ab7b96010435d4fe18f9033b4e01b90f2b474e0236e4d7e0f8fdeea3ec2ddf42c634b4f30eda2b4b57664a1feabeb6c5b830cd528d2faf4ffe6fbb5edc2df3083652f5300f00ce253bb745a9fa3e68f74c5c51bf76fed485f91e7ff87b189d5132853f7b0eb3731bedc94b8d44d5095a38cb5112615093af4a6c5c5a4055a1bea6c57dc40197fe3bd02cacc1fac9cc171b3d3c93d007c589d32c8ce39a6395bee54ab4c70c49ed8d98549c8ce7cd777195cb37734cb7c5e4906b93ee7dc030ca89cee72801b28ee0476a687aeb29892612ec89d4a49ad9113ae7d90e7f5104d1bbcb8db40cac1b7696e00f0ce928abf27843c3f510297d6e61771b54ff05df506a0b51250b5cc7ec66a96eb6628c114b76772ce95720f8be86e70db1ad9043316189ea3a0427f34696bfd245a14fc0de21c86b45ade67b638f262e9db2f1a2c37e56e9e70e7576ff2464ae7aaee52187953e75bbca98808661124ea8fe023133058caf320aa1dd4718a69db643e74434b2a95173adaa8b368a2c5be1eb9b8f9e30c55f5d00035c36f18ee46d4ac09e9c5eff16c4ada6ddcad77b4c8569ca691ff68555200a2b4dd099ceecee0b2b201f289009824e05ac1e4aea0ddb5326326300934c10039dc637d91ab8cf5a54e5913fa620681a3002434126622bf19e6a97b9e8a7b90f9e3f006b897d570cf860a0b5bd967e04e8e8495c9dbfbaa2a5ad3a02115a1f0459149d0f19bb5bf9a79923e6a5be882f96b4fd0bc7f82c560fdc003735cc5807a1625f508e67e150e7c941205d0028c7ebb1f2c092df8e4a68d68d3dba310e06e68c82cc25f595a4881994c1f80f38edfaa8c8375b2acbdf9c99808dab3cb30c000195641983bbf143df4d0fbdcaa368370be391a4dfb67608d9f708525a8d047607bfc17a6909a140fd052a2172ec5b4a09bf4753579af625c53ff427fc480062cb7b2b600d218fa80fb69c47f1cca0b24fe667a5fdb18549e342c9626c5209a33dc4bd4cf490d5cee8150294a62e6b47644fa1cf038e7d8bf410bed465b002628ac65dae09897921645d3babafc0b14e33cb1658c8b1d494eafe549b934d0f41d9d79effdd520a743978d95c1f45bf00b7937be5a5e11d8c150ecec6a5b2006cdb59a6f5d31fa6a9044b3481914678a4185ff2c8edeae59cc5f794ec14d200e0b02a5e054dd69d2e084ec1e3f19bd18045a3e7d23364dd701457684a1f5d0983aea621ce9b1d0d4c2bfd6f4a8aec14414a88e72b2b4e2e60586d15b31a8709d5a543c2d40a68d425ac62eae33a1236fe2fa4db40100697c86e378afcb827066d2fb9a2a8f99b1471fdcf57e0b1b4579663504ce8f5defd276042bafcfff40e5085a85b84e62e90742369840db4b840b002ab0aaf9e3dfd66bfa2840292d70260a5ab3a2dd78cec36eae2a3fc75cfc7409a7f25b920c47e3636e51a2948430931737e2b54317ff7e7a39330b3de5d9ecd168a853604e4fad95b906cd20e4e066be7ea8dcbb55a7460234e2d25287bfc63a9e8d2b483d6c9f5b384fb1be43303b4e0f05965f251b27075d0092abf046a41f9293a546d055917046b6294dd8a0cfdea4e4849f947601b71cebd532d1122bf1168352aa3b47b804f4df5dcded80a9e4a7c5ec51ac379e5e6e44506a3b8d9a8ccd5edf4dfc29b8d45510287e1dd043bac541747509b337340c861b668a96b0caef40f839af9c3a2ec7d1a0b003d08e5a862203299a6b33c7e90245d6a97d826fc3ed8aa956f716102494f7da57c0c80e6e6444c7bfe81845b3d6585449ac12feb52be0540db7ed561969beeb86100899600da327d85edc873df996aabf3850d10837f2cd2cb1f52306d36828e4903ed07edb1335be9e2ca601c8a769df8c14544d570ef7b82c43e29c2c756b15907f748696e286230c1740a84b7b3109d71fa0c9a13beb7ee9d7a5cc7a22bb48a0af50e034e682e77eee558e3c4e0fc7d1997beaa8bb37e273e05d93137417ee407cc7d0f4e1eddff640966d0899cc9eb4a15cfaa5cf7ec4053b91a148fd9e2010185e55f18ccf8c6264735d7789995f424543e9cd6cf19a90a40e2e8b3b5d6970f60898d8ca9ee619e1ef72aa59801218e88706dd2f559128931868935f36bdf0f531bcc33d1415cd6b371987378bf6de11f38f21b517002990bd5c7a216ba660b281fe162661405551e7f22c522c03e4a84715f030189d04060b9562d7e02640bd7d46ccc62a1441086994d03de0f439908366f42063e9bcdc3923587853e010a8f4037807311f557e91492309dc1fd978029c129a93e6ab730ffefd4a7e36b09b6cc6e6cd55f5f66c103d73df7c0bd5529f5ced6db1b80ffa1e7e4a79809f405f0b941c1426aa3aa0b986eb9b2fe233d069374b6129bf43164a578f8bc97180abee95966f374a152519229421b10c4398fd75f81b7586e061028e453ce97e5048a6c30ff82cbb1bc5a3842d355b905ce071093ec72903008bf21bace5173740d0a91a24717dc2da92ba1566197170bf5ca68bcc983abf14c9f724ec11dd38d01e901fcc15e7a483eab8390166af3777b404a37702b30499b29b5b8face9d5505fa43654b5768e86c762f34dde105419eef3d278ac44b1ce94b8cfcccc9b870057a6cf3ff9bc832ac073eab16485d18339fd61c9e335848d7a67e00885538b70da4755c62c32bab34d733a42a378eae4065bc7468af477cadbc0a436883e3440a50ac5b87ca7d006ca26dc9d4f9f18fb69a4e9138746a619f7021f2a84182170874b35e32be8ffe2ce6ad40dbef331872c1e37cd8d5aafdbf52cc9ab03b4e9e08f7e01288c6fba50a7d796d421743e2f0f0aa69283995e497cd921e7ec4d03f0cd72315c5c6fab09ca88909d87fdfbcdcad94386fe69fd7bc597283961239c50f9bbf205898b551a48538f2c404573a41a472349ee0e0ccd6eba51b50834480001070c066202a9d067672b717e48c12b56084ec5f5a2604f1147ad7aecc08d3091d6c665fdcf6e6d753f8eda978b3a6ab1b0873946b221db0aa32eee159dccd019422f7dc42d7d8bda3536ceff5f5d309fe0e52072d175a409d2ceb5b01088c0ed8e0b6a5e1ae8108f7255dec01e61e9983cd8293bb90afeed229e3ecd5d412022535644b031ed2e8a600988b1eddc0aaff553c1a37665e34d48b1ae5d8855e067e54e07c65ac11abd632e0fb85ce84ab72582fa1706387d97e5c90b8402221000ccf28ca27d89062093cf40613e170aeb382d6fbc4e5f3c5766cadd962d40b0d275ff1e57060049cb23434b8591505e4ff4f33b5ec7c52cac57188eb69511d0ce5bafe933ebe096290a54b2e6ff47b6793897f38bf3924043c20d1cbf0300e010a73bc31a7f986f6c24002e8f65c2a89bd124f799fea9eb2a5c960f14801800ea0d204a6734af867a177bc0e738a4763dab837f947a1b2607fc1aca9649d290fc4e9eb7e6250fbc914e68485cdfc5178cf307b2e7e7a1b92a7b99911bcf10d031cf4d8a00c1edd02b79420a85215896723d67f4040c9f64a90482cb2e727e20b20e741ba840f844881f0370fd093d978ee6b36106722d1e4bf3ce6b6ce804a005a79bb43bc9281d0c2905387fbab5759128c6c0729679b57daccba60655bcb06d706e0f3dc843ffed0493ba5808c9fd2ba910e8bbd10cacc89f98d6ea181be030c794a9ceea7f1234f7a1df4a04158099c2bd10366b75753a457f28bbdbf0b003ee45243bd69538c7808ae0fc2e6bc67ca08408e476beff3f31f96bfb12df504c5452fe546ea8c4fe2735e945da1a5ab9569cb0b458034155c70ad9721dc6a0a79b2806d32bed1dd14716b4420052bc4a31943e5781479ffa95d04d4569f380a0667b21486b9fe028ef0e38a3ccc336c148c6a8eb5abfcb0cf891cc202f98f0ac7bb2faf0ecb8c3fa04bdf3cf97a4840707816d55f176612969b4ae9ddadc9033cf2c771ccbb27caf62871ad28c362772702f0801e62a29c286480b15b0c0c05bfc9222afdc84b3a6b376643ccefd0cb20c0d3dceb4c71828f1d02eb1d52a20490ef35e86c034002a6c15ade69bd290697dff156c2d32757ef578ede89619b030622ee254a99c7e6b4c814db98ec2b840f9f855073728a46461cd20b5ebbcd0ba39a7e2f663702a896a2a8a9bbde904e3aa0109e771b589c233ca3298e2f780b5b7ae2690d9c83ef2bef308bfcc994b675e22f55ccb02d577e775af863c3d80c3aecb2f71a2d6d4150c6b2cc6230e735f8895d3758e8279f12b133e2f784a0027e884d42b6f29fd99a2e7a650a80c458c3492cfebf6882dc73c4241d2d3f270f6f86ad5580198c8747ba0350009d505a69f4219d0005175ad000cc8883f76a0c80563ee30dab3a1fba92df4ed28e23a43641b62224c741e4c854344a5608fd029eeff1ad9be6a3ead5536770b88c8d8e7d5b04773d24a249a3f3f086110aac00ad9ce3a2a45630d01c9563256de8941c17a980b8d8ee306d13f5ec2d4505ab0acfd5463d61ef55ddead0ac93e5127513115c5fabb393f292fc5af4711a6b2908c58dcbc1743285aca1b3213e368d7eb5270118a58e7a41cc3ffbb3a7266970060e1127ba0446330b28f5113760753c74783edaf4a7291ccded3627faf64f3100c3892e6e07ffdc4f2bd7836ecf1af568b56b20ca9fded07dde63bd1e98a0580f084d79ee2446356f43b5d6a39b13defa6baf4a444eb7b211d24913033e30900a73c1544e023ccaf14aecb16ff7933544d9f27f8ed0c1b3fc8014635d6db1660955957ab5687d79cda35d6b0c48f4ea3d504c07ad46ff79b29c6cf9d22238d60d3b3b3abece46a228f53b3b604c21a669cafd94db64bce6a6ade854a99f813c07bf0b4190910204a594416588e483bd382144c989be97dccee2fab64c29505d0a1637f003e553841345bc0738a4bfe6a0047fae501a2c4b765cb829bc415bd20f06669b32671e30562adb1804c9f06efd81a9791981f0e66669e481f06ab5e60ff1e3d5d04f77541a8b37189299e9732b2caad7ec05d6d539c6a973c72a061603ced3b45a9c30d479dc5e2abf04094e78444de011630bc14ef46ad9397afb010e2d631724971ed168c92395b2e4a4faa4eca1f1a4f4234e1d5ba1db3d38382a012bdc552d7a85abbb116e92c0904078179b9356574c0776ef995bca962de0f600d97b23a31c780efeeb90710faf591f55dbe4375719575b5ef47494a4236a1a0bbcb903928330b031ac9e26df4d1117391e58ee746bab3b9c001749f13a072904c861dbc8e98d6638e3f4c9bfdadd8dff8fbeb02319f139c662a0f64c238e53007b065b4e40476922dcf25d6b940fbd2d23d0960133bbe7f5e76c14d094ffcb038c8aa436a74ca1c794adc53a8f8d88b75f870dc58568c70b88803dd35944fb082015403989f8d613ff4c73cd3073472d2ecd5ffe05c62b17708b85d4cabc380debffb1dc47a2c5622ae60fde5d8b42a0b50ec65a96c3d0ff5c0566118936bb0a066218c4daae3a29ca18df18632675a1d41135f3674e06e4bba29700550af700ae35114614576580d38aeb360fd15f942f93a198fef462c9333a7dadf4a26a071af4a5f762c1761957889da5d2c46c4b765dce4878d0582cff3cbf785263e2031b81cc165de524226bb781c10e1fddd68ba07b7ba0b7afd34e43467f63bd1902e49f59e41848402a46e6612aa84209272305cad2cf163f507761438a356e2d08521fe168037e6e4f0551d5daa08e3fffa8e759dd7a15d05395344b4bddcbba0ff32c5a448d978b757b164e64bb13d06c1d191519282befc4e8c4e117d520dc050f8acf7d136c248a1e2737aa3e68a80a18c26a602b2bab791c66374723b81a0881ad60279ef5c9111ffd86b839ac13d65a6bd9fbe32eebc9c91b53947d4ce105e8a03f2f0c1f63b9a5f2a2879e44307e1b9349a03ee622e55d7d9b029dac07027fbc310b1f84bdb801f6279b40827d384b818ba4bde847771bcbfb9749dece0f1fac2936633ec35da024fb8d5bf7870845fb5d5d981050e4b6267a4563af60050784cc9c1ca6e6666965aadfedd0850b0195f3730594294a72c6841b0fad7003b2ae869bf6f02baa394a5c4d5a254178c36c0f6c5e07275d78bd9e3bb8f35c0ef3ac447fd2bf1e93c8441a965a40210037156c0ad09c3e2cd5d6fb1db715e206d85147539c3a2a57f6e6cb24c172d4cb79d2bf69c4fbbb186619be3a3ee6d90685c668b6dbcee2cd48704aa5b983478fc00a273458e044b167706718b3bf3a0887a5aac2c299f84bfa50347a6f94298574404a89e17a6621177fdca18f3b950c81076e0d0ed7098f20bfcd9312bcbbf9194ee3b3893006a9d7f6e456ee8b880d9e4c0555f82b6871cc2001bc2013b6247b9a11bcc469d268660edcdc7310c4015a55d2c16f3e1e4456e02cfb15852ed8b08d69981bb5ddf2df9dbdb983cf8a07ddfd0d4304990050d185268b1b5a513cd30198a5d2119e719674f1f210b8e709db3b8852af23e1af2f80eee291136b3c8855741e85f1bfbb4feef6575f7afa08820ec92d1e25a0b3dcfad4a398952c1e308db2edd8c1f8b9ea5d431930231102f61d0561c6f156e78f2c8fdc8868ab80e27e9dc346b7a894a2290f5c0a25e701a468128b291447086e8c4cd8298fddbc5a6bfe724b30007cd6d5d96ef34a290271643027b820a2b5a421aa861c5ada4aef3d90f41fdeb9f90368635fed309b0c703aea81773b8d5bac2c149504c8e9e959da9f55635954eba89683aaced77b02f6ea5e532dd35717f9cf145ada78a03a0aa2fcf2f96339c3e566c3249350d704decb459da508308724aeb097fe49507db4c9a241546ebf0444a613055e91fb025fed75f11eac2d11e38405d2bf322e097f82bba18b0242b7c0fa0300c0bd6705d8b2ef4602765ab42403c26e2f93fb9c1bcd5ca4eeb8a720181190062cfa8203314ac686cd33c7d17a0c2a4ee391f17a917b2550a918ddc3d48840f807e35604671d15f822bd84d6fdd478ec8e3883800189e5b9333120babb27c2d7f35549074d5533eb3e9effb39c34f1e48410144c8aad730667b327229d9bf7b34281a60fe692ced291ca32300721c703d2a5a05bd041dcb95ceec68fd38c5070933aba079902287954f15e3a267fcf74ca95d5ec5bd730da32970d3b54c398bfd8aba902e050ab0cb28e0cdf5a778a9555aee22bb282a19d947281e9e5ed4ebe51e62c09508fa400022b75eb2a053b3ea18793296a812ee39d545e524cef6ef5a9f9470fa8bd8b05e62bf92619c20e66b5c40646ca576add5f396a71ef2df6597a8569020ad23caca82e05abffb7202c57c9874eb11118efdd549bd99005db53d59f350a4c8098fc00aee7ac921a4910853a98ac739fc457a763cbc7fdbf30d6b84d9c03ef4c4e5264f6f4c40f2b4a96105f0a24bbf9f783efe180f321fa21ddfcc1f000aa92823ffc2b573735e1377e71c7e30681d7e4c5ad14df41d1d440167d4bae09fe1ead3fce7f59f5fa8204b7b4f4276a90eec6d9a84ff72a65d2bbfaf7429a066794987432996bf120959f9d11c73975d100261d4dbc79fb3a348512d4c0590724824e7524b124e893cb36ae31bfbad002bd992fc0872fcbd92fe4709291750e50da7cea4dacc882851dfe91d7c517bba5b37c444353f1fc375d81d8f885a0066086e8d7920a7ddd8ef8769ddd9494207569369b56db79b05158e8d83ccdc009f989019f354d7c430210a5ec0ef60cde654f923293ce5149f9d0653fb90bbb0199e30b2728c8d27d71fe48d75b8bb58e082930f13903d121a81032a3f36dcf0e0959c8854208af4723bf52cfcaf160ed079d2e3cee5ed394cce41fdc3e81c200e7fc92eace14c5d7b3ff0ce264ada30f9dc08814b69c19d9127aa16817e1270621634c735a4a3016262824c35ee1bc28ab98e5c9b61a10f2f58aff80174ca107ed3bffb66b0df1d42f2bcd206c12bc0963ec2a7b7998b7a77894f673970c9401d6843acbe4814d40e21c8e5b14d841b80c55db86830054c1a8136760b43ff90dbcee62840119d91648a2c284c72375064e43a18412d30e05847fa7bd4623cc0dd0a5aefd575e4bfa66c2a43a44c1d11688010ef167ad09f47d4ba99b5cf0c002e832384356159c23ed1c87a8d44164dbba2a6db51ed8f8e8c018aeb9daf2ad04aeaaa8a38bda081ac6bcf68b223e5607eea6c35675ac83dc1f63d3bc1b77da0ecc20c5d70c0455484921be7dbdcfaa1880d9b0cd42d61d044706de97b8540607f8dd6ce1b8d84a76d3be26feaccdb57f4975efe99b88aaa91c66d4e6cea9210270fcc25a96d56557195f108bcbaa312dd90b19d77139cce0d45884a22622920497ae02c4632a97f5297e3a348251c023cc7bc89d3920f23f9009b17782ce890bec77fa359f14cd967e85a9357157321e820713200555008b10929370506d22017850867feb8ba0f89989a2cc71810b612a72d7f7d4965911d9ca7828a5772f0a90dafcf4776117ca1a012c3a7445e2abf6620606414516d3e562a6925bf83a0e358c22d4f2672f1648818c3a35ae3009cbf3dbd34e7eecd69f5e9cc54d646a025faad585ff6dd20a83d2ef8433578277a4ff1ddd1b46f9f35b2e6909bb446d06987214fc9a1471ff80d0d37a4df04e285354263a2b9fc8a842ffe5e23c6ff207c0c031af56fc199028005e87628c897a0c6ad4254ed87643c9a5f4dcbf5dff09b432867bcd7ab1040cbdd17d72036313fc24f6fbffcfa6684896b92aef4dfb07c6c28b6b31315831fa6481c7d2b4d3ab3ddedcc0aaaa1bc79171eb8ef676590dda214657362641f440ad87e70854f6275a7b6da27918e4e8d16c8957c70d650d552424ccd52431060b64f996b319a91d4257531d8b96fec2abadda7c82f8ea0c0f2bc9c457cbbbccc2efe29f70adaaa233ec94f36a1cb08b9dca32e51ada68074d600f10b393db2ce0de4fcc6daaa2b9296488f8cf682e3a4d49f1faf8fba804351847f218c87dbe85687a363911441c9b558cc5ed454f9d9fcd263633f287016f21ce184814856dab7936bb6e1501afa43657b5cdc69fe0fd51addfd812870bf81d159c3b19baf08a76aeea0fd6c0dc762e755309d1d6f3d2a1cff366a3e807cd549d514dec42bdc5e615d8ee5783983792c534dd9da4912a40ce57c6eda10ec29c4fdab90101123e07ebd65a35e91db03c107019c05811f919fa85e6cc6a0124f7eb2bf6e97e640d298ea69f1a651a98db3aaa6dab9cb3639508d7bec2f40a0da6f693315d4b3d3a1d0a17bc1be300a3ea90a8ce6350174fdfc9d74d4e970b8658e3f18877a0d52d234a809e62b04607c7183f44060110d65e3faaca400004427415c8d97ebc15320e4f79672e8f959a29376bb5546a8fe539bb5fe344df06498ecb20f169b2a6c7ffec9c48e23e95772f4e581d678351e51a37ae3c131d0cd939d9e23d0c47475314fab9b8e1745f383089c35ae4d3d8a47703a628a76e056b34bc57e918e55f2a5ecd35ade34eca1abaff86210254e51eaa6166506c490d85119b88d7726c17c38878a4c7bbb4b44375fa7ba85d47e345c97cd643e4dc0bd3c0f07b5e4aba145fbb608b9f47c5511b83df8ae9730e92019b0d7d62672d053f904c8affb4366a819b1f057e1d0a165e065e7b23ed0832c0ce4e4379ed3e0a96b85b1c0166f320524f10def2803a4f767b2a90f579a939646a6de77693930ff6eff52b7d9b962013a6aca7dc9140c8ac8b2df68fb8b72fcab97803c280850ba3df73574602b238d826033bc1cd2072c572539b47f71bb221c2ba8da231fa0a76dc35001b0f3cd38cfc314fe79d78b2a1d18474c6bab85b426d08c1df8faf02a38f035ee4bffcc4babab8558fbb7733f273bd89f6bfb1498fca8ee78b971200e7bd7c06c0c83518a2db75fb6fb0f765b6a5fba1f76aab60a210dc26bafebd04d105925b78a1f2f5fd83be5a9f4ff0944609b3458bde93e2f0d0599b79b9aa03b80ffc056e001e43bf73ed2587754c3fe62a1b0a597cb5c3f2788e41368d110082045b3ec5ff1cf549d2bc3a89b1ec602b1983966b618e74782a506c09c1a208ab8aea966da08289ecd73f9ac6970e84dcb5c99fcc13ab1938eceb7d146669023e757e2e27abe42b28d20cc72da606aec4d895a6c6a2734f85b6e78444fa720cf30ab910d4fbf81a7ed6c858055f0655f4d0b4fb37041a37f97d5cea13dd0c007b8164d579b377bba70c9bbd4f98a9cef1fe3d7c5c9435daf70ecd1827b3fe067cfd63b611415417d20e85c1e89740592ccf4a59844d2ba40f2a21ea132205029b63b09ef8f5367272ccee350da5929b671dd56a30e4849eb54b369bb45d880d9b822a4c3dff9486f05faba6a81f3c0655df686f520df93dbc1ef98fad0e600017a5ff725f3562cdb4f29a9f4bf5d0b51a3345ec6c7d4ded504a11076f379f051e539a6d9ab6d06cf26bdee71283cbf0e741ff8e10c9a1f5d53750a836e3c00ba31af2d74c3f2bf44a644aecf2510e21e0b4e0ab997ce26c82f2f699835521046edf1258b097204b62c1a070dfba6daf55c67c59c48b28d1a586cd493713e801901d6f1e717123b75b9ed1164ea44c07ca4131f1cd2799bee2cdf29735b0b80699ea5372d18635fe2bfa3c950e0482427df03d40c4adc50f1db42a3e94a7af0b0e479064193f147db1ab422c340c6e1a67389d89047f8c5dda887bc78600740094716c074059508d333eece20c516a628e3deb722df58009d96475639319ee03086d26db422c9b7fc7036d7406746a8f368b2e903609b930fceacaf151e9980f9c58de852374785c2d3a14194bd43f7c4833bda2260086c4c7e594be2326b60f05cd27166aa69154f1972251606ac671751623931dd32f433990ba44f4d1570c266e1c5464a21b0a3c5cf66a3a9c357b230ec6be5b1a6df533746482fc0344024ac8e69f61491a50861e7f17edd012377dab92f11283a46ba0540d21221e040fcea1ea30fc16bb84a302260211ad28067189996e5338433f3a181eecddff9c0ddf7f4ddf64149b69e73812adee8265368fed21b6c588d68067b4886c8fccdb04f304fea744c0fa2af7b23b111f839223ed247641b8c00262d506d7391bfa6a0c170c14f5602762f77f5251ae2367540d3f19b8b22eda0cc374fda732eb5330001fde81195a6d12e7e1e00c2148c308a11cbf26a65dd4666596933653b33adb0392f78f4ce83591266dfd94507280528be5d1554bcaf86ccd6ab62ca2004e39065a79b85ceb47600ba9cb33d71248bc42e3ce447db2b8090958704907b6871f0ed7a8e6c47d315c39389d75d7158bc79ec293c1d7725aed037f7940764d85740a8454e9f1de641e90033843d67a19938db831833a079a483dce50a34b89c4bd0f92061e9acaac00860aa556e3a6d77c1b4ea36477547b452dae3f4657f798af0cb14870e5a6af4a77aedcaacc8257299a3ef81682391dbefdc7282b4f052f550be7e0bd6dc73e9833c905f7db40c9773b4f658564bc277d683950f2ba6871270b34880168eb8805be7b5c90d0533c811bc406711dd529b77cae3ec93566adc407fa33cddc6703b16a4c1756ec5eaadfb52e8ece9af6faca60bc70a79ed295b601caeb1f563e667145f0dcf626425e721b1aef9321100d56d190f5451df30eef07ce08d22b213d1414c3682f8baf9cd4cd40a6aaec89e8a2e2e823c18eab98b90268308ce9b4e55a45fa6b9e67eafb71b9047a6730093f4d82ed71ac05d96e810dd79ca714eda18deeba0f7fa515725ff1c738c9095abd384044de3a27fc7b21006d15b40b55340a94c0e5724c85007ce4787d37e2e08a62456aed9b79ac8ec30224d011447b9ab3e45c36b2ea8c233cf4085c6d72b46f6dc461346d0f9e802104c99c0b44fff1f27e8b54c87b12bf8debca6c534c83e4bfc1229d25ea5b160208393f9210e4352ee401641b6836d47fab468c34b6d67ecf3cb35c92893a361b07d5ad4a9f3b8378a3f1326a7d0455073aafec91eea2343650614f5db977f5a50adecf79bde069710420e8fea3bbffb97bf1aad738d2aa439588eed39fb642a30a532ed98fa2cd1ba0984b4ca47ec6c01376820985bfccbadc84feb67c5f10b50e801fe2c66f1a6d106dfa416b010fa353c656a18b3cc9e7b67ae64f32993d8806ff36c7d0fd9dde762f0832fd177ff6abdd023b79820470c4d06c4a216b4e1804d6e9ed4a9a30ffa11b933d89b92fdfbf8b1e7e103fc8899027046401d9e1f905652e03458b59fa8429d5c40feafe7c20cb5753efdc6aeccd55b0e32884188602f170783a4505fe8961a3fdb8698eaf5311b5d4cee97451db95ec6d835640eb07fae7a1d5b60721c2ebe336577fd0118b3f8cce7a6d279bff09a9259d66212408bbcce120a2ce1dd45d9c01605a6373e7a9ce7ac5e8f756b21b518eae1f128f05f95d22bfa3b4858e9dfe7adbba447ba09e90c7438b24c974c921b82b8cba310ba8f3b6a17c05aaf652604c15a2d17f47915b4548a577bfab8c5de6b9268e4d09cd6e5fc3b4d694d872392983d4c88647b53807c72129c03b429967bacab4620e38b7a6994d1451b0d1bcc3694ad5a98cbea059fcf9d26dfcd87c9b2611c5f30ed80f22363a671634f82ecee8a5e5735005c1fc9519b8d3cf79165654e21fe601be71d6c665af686b8940e4a5ce7d7460f70d35812b6dddd2ca1b92e3db3883094dbbbcb55a3bea997e9e60c4bd80499eba49067414f21156aed234b6c1a69b0f30cb8d708bf34a43fe2d1bf764e84a2e22d23995b89812fc6d1b2c47ba82720f7d97c279383135c3876c1173f021c312be46027a8228e931a71ba185f784590d9a09b789bafc6604a37b8a0b0b3090ed02a8c886f39f2bff159121b3ef0d4c07dde6fcda2c176638fd5d08c1678cc8becaf57c093fea4bfbf9f173918de49907f8d44fd477ef07fe43e5bd07c6cf2f8ba369c4656a5ff86389e928c62e346b02c7689696549f38ce7c9461a11405ff59b813b7fec52e8fb4e55fb6d3511ec5026378c74d73ea9a024af2c3b4fd885c017914ce7d0b0258d3fbab210541068a035cdccda0df52fe83ab758bc953e45096fe20cc575305acea52cf30aabe9d5d0fadedc5c16bc5be2e7a419f13a525ea2e482f20e14ea9236f59fc9bd5f8fa7a0dc7b7c8c45ccf354fa2467a91211dbdb2c73fbefb9a33fea234add273c2530009feaea50320102f4313301b882c080d2627bbe5556aa6b005044aac9f1008bc0abb7d0beac67928a451623eeb09db18332e60aefea1d07c6e719befd54da3a70f0e5d2b7ced91ae5c620e7e50dd109244bb2a49325972661b0084a3108969940197bc542f896909ff33c8eb4afa44019e9e28408ac3082bb41a53bad633f33109bc54c7dd2c34eb8f7d222d5ed688e313581673e26a294805b035e5eed2dc5d0cb3170db160fdfbca232a8f216c8e2c0adb9dc52d1411c42aa1b3de2746e4f90d6de5785c50f671b158f1c383829f4a9c7ff02b9db0deb49389e9a0d67d3f660829a5d982aa0f9f36bc8c43a7f73d745e198f90ab5522461b5135fba79249550ea9af2ec14a4412c0a36db71e72071e47ab14770c29cfac535b352920ee4e240c6674ca76be39e63846316afb101602e3f22d9776b7f965796cd310ebf1fcc7001092d4699182ad99b836b48704d663c493fb073643f30a50c3e982fd6342bb035f41c3d846aacfd45ea0584b656720fce0178cc9b55353fe89a0861b5e30280fce01d5ac0ed99428b2a20dc74d37f6036282eae971a2cec5dd4bab5f040aee0e4f4f2cf412baa3f94416c77c76e2e2d60287b5d4071dc88a185a83bfc60f010be5aacc7c4ecb42830f80d8e23e141c3c556335e0fe4ca52c9728a0d5554aa90fcbc533ebf11e39a5aa2e3c67180502f8b84972570e44e38a333ad9e25dce2302700eed4ca6d9536c579767a1d017ed28129d2c11d5d5d838dc73ea9406dbb50c18c2b2fdc8051a596fcae1b960a8a80b85ddf8b0b630a55dc9407d1cde281d076bc20384046a5dd2c0119c247a0f54bfa4aacff29218c46e6784e26962d0c50555775330e97640982447796a3c9c780e394111ce1257e50eaa8318b76598010e6458c00d070ea208ec2c6b84ab84c9b96056f2b6b806d13fa78e5fbef9fe9a057b77b3d089f06192eb0d97c1255642f2a101b7e2bea51e2c7243a257717f0203981213bb89ceba129d8b3da69b3bfa361e61d27d6b33b2ba5a9640365583fa056eb802e125c4d8b05207c11a6b63c1902ee8061d0acfafec338ec7ae405f500a0c948176248899ab96289407b2a0f9fdb7bde951c2f34c0959361d71e1dab30300ac4d93cd7c61bd2aa60cef26889a61de24f2cc526df392671d111f047ffb0a2e28074dfe315108c299dc32f2b528345cdcaa91553f580679ecb9338625800a862d1a96c86d14e25b920cb996b706875b5dcd58c2fe9079f3a229cd63506602c6ab0a980f23c860a21d720ab13bdb5dae34e4bbdd2b9c21855e113f8bdede0d63ba000e6c5f251525672b99176229dd7246d3238f89ed34d69d7cefd0198d03ea3658ac8fbad80f44b1602df08369705712cb7c5818f87d4063a27641eec90cadf9ca154871222da6e60815f2ec08ba3acc31c21ccd3357b348450ef0361c0925f849e754a914e6062c1d96176acda35fc6f639d967aab7a825023a036fa4004cd7d7cbfa9769491244409616bff61dbc200d250324eda3063a32c9502f8d06a26cc8eccd445d1a14abdafba0635cac7978fb18fa7fed7e2e31bb4ab9806606585d465766b44ea8fb5a516cee70fade1751baa5d4bc3066a8a90ee0b7cc7402ad642540990254b86f08ea45753ea104baff6b5fd57fbc2148f92226866f1a0ca782835dcb7599fb614f1c26030e7b2de088a2bf56a95adcfcb297f5ed63fe01aa5b95d13cc99579c7e6e8c4e502d6887bddb3b2c5f6fce7375e73070f91cc013085152f71b4763851aa83d6c829d7f72a2e778723d89ad3e77149047218ff077f3cd92091a1642c25b02c581a5cba5d047e7370add238abf3282ecd76946308dfee51a8914216eeb339d4978180dff40847869922f7f040344a77b76b5eb00333595a120c628c2ff92e4edc44b6e5efc359e0024b2d8459c1559149ce1de805e4f057399d93d47404178649356289c7d9fc29893bea6e90afb83ce184771b0b21764b1add18068b4e76fe8f73138c2a66fef7eed06d005b59adf5ddea68f60de53c378e744ea3bd6fdca265dbd761cc641698204937239991e1cddb49b89d0ad558fd3a776a9fec7b75c61fd39ef9c543749920a08dd8b89333ae168db1d608e8f9d5111fccb0b6e210e8e47dedc92a6f40880377613825ab4667fd0287710f98607e291246fdc9d0dfd5723e56f521f5c1ebf56ffc53e498869b74c40dd106018fac9787b0a836d25170986fb2a2830d6341ed45c99bbaf4b1a3893b30ca0f95b4962d0d5d966d4b06fb53a9cb0f99fe8f2f07f8175e9f16c6b74e3c5da700d650138abf1c55a43e69fbf2c9bf83eb24470ec1151baf6a1f829d51ae73030d9d5cfc6390d49932e4cb31974aaa2012d4a3a8c84588c01fe1147e463265990ff26720d70b4c6db955e2c1fd1915e47c7089820982c5bb81a844c8e012ebac08e1eb3a460f00a7a3435ca40f6a5c7ba8ad03411f4b2e3bfe94707e44f910cb010ba2b6c108d102c66f6a4783a06bba3797057881f17856a157b2e4a2393f3f0d1fa764523f363a7a8b59f10dd79275107acd1540775918677463e5a4a934c801ba7cf231688d9507794486dcab53bd886276e9ea13a2d7740ff0c387e2dcc104b532e24a39c6e3fabe529399597df3e85e23797eb505b17440c9f227625a6f0a8b73c9095082b50deb4c6c105a1e9b0bd4dbe0384eeddc8ca12f3091f1aa4203cdc7eff07ff6f27a0b825b192859be9fc6f381894fe98cfd1034325c04c59602e927d948965309ae621da6e167be1e3f2c9520067de4ff6cc2564691c6038507d3c8fb09b10e96e7a8847092765bb5d6d38aee5d39940608837be71cb24d8f0fb564e354e6accce164bd45273ce198c0d0c819e78e868f992170b0efce6bcd0ffdaf09c94b129113f8c6ecea34f30fcc76383ed9e7978e9806395ab15201590d40aed8a7bae628dbe33da05152d5be7d1d30b634bc98951bd7ff95ffdf018a004375f5eb45f5d940d98d8d1a22eb4485c7fd0857dbbe18805bd2a255f22dd60d83c26dc38179241aaaf3c92b23b61861c21d25ea5ad5ed07c471bf8b8f1bbc0118754e125914f3c2dc92e05af909dafc5436880f8f2f6e2a922a39cc54b241079a9a796de6fd96cfc3191c5429d8ded0136737462f822287c7730aa972155104a16f8ff9696a14cc8c83ba3dbe17fcc453910df037be3ed3829746ab68321c0743b9877196268c16f9f51d09f0f89f828f28ecb171aa13f15791399dcc8ee304fb20cc3b23e3330c442460b809468688b23133bb28a1b9c6d45ca8344ae7b70c40b7c480d4c1beed41d66326fc8b40356a285cf81876230fb2963cbf86da8b0e7f40fc5892b6103f2753b496fb65a4109fd004814ed6814fb8006922f6633c0e91bd34b4e2150f9ce1b5a264f3deaa5ee707126736cece2455eedccaff4a2406879de383ccaa21aa76bc38c9451746cd1de6152d3ffac10aa3d8d3880fdc3308b775283bacf20df252f0b77b1ce0dd87217f0c6045cf761e81733b3be4b55f08c14a19ffe72883161860d83ea44d0bde966e5bcd8cffa6b2d23cca316121ee0d544955a46cd3ddda4adaad6b3067c52e5cf27a47efc0ffd238f04d1725d00e0c6750a28bda45bd6a7230ff62057e561a0798aabb90297eb9e6ada53b5b91aa0aa24b49b81877ed9d0496fa6832b40d44872607d2f9f757c64e1b61e239f21502f7489c3fe2a5c4e0503a37b880f63348a81ffb1d9f47eafe2f281eccecbcb601e632dc45b91aa739939f67ed85218e974b0598c729a598770de520629d9ae80ed53c8be585b80456e19ba160fc9403f5444133b54ac109697bd25f524cdafb0d7cfc6579663cd67d01806becddd1e41f603cd71a852f443c66f5accd61c8c901d6574233f3729ce9dc23b251e345f3c9e26a9fde480c80a86196195d820c8d0edb3d643354db09efd245fec2b5fa87069bde57dea4385b6a30b8125e7576640629685fefe514a297bb3fc1705cf630540cda0be703efc10d14b3150deac7fa0dc2141ed9e2f0231bc073d74b7275991df189ba67a6a24136f11c0605bea51a0e88a87a4b0b732f296480abe26f8227febfc48469b0f25d5f89a23cf2c661fa0d363f0720339f76ef75d0ee7113b966b596c51326fc450d70e44450c6a623820f06991c7c896c7b0091ae83b4a4fc277e1daf6c26bf7b632fd97d8504f6536b0c4a0112c1ed00b9efcd4c088146a13b555c9c682a49a2aae35f0bff031ab070029a3d43f80ca442eec5a208dce3b9107ae2037c2ec22e8925f2308b0fbc69bb0c00ffe0135414b9608090d1bc165e7ef850d001a777bf7974b84e79170ee6630334a814a7eee3a48d127c1c4d0bb530eae9cb0bedeb64db6b7334b44c085fa601621177ba74c7f241f3a4bd8d6ed7e47bf25743b07dc9bff7702bb6b3a0714501 +generate_ring_signature 7d40505d96cc9812cda2ecf10be26e35068ef21403fc13a3fe0374f1752bdc3b 58399ed2f6ab0ef8e44f8baabd4b7668e5d9e0ce163b55fde697dc1f57b65176 2 a0f1b1b883584dd18faeaee8affa2076a50d7783086353a9b7d4877f0c23bd55 9c840d84c4e8d918410d5efcfaad94d5be97b5a16738f9ba27c1301e30adc367 bc309edac23260a587d0ead081229e22a1a46029e24bfc99b8d264f0cb550e02 1 d6afc4599903292281883fd892d8372b724bf3458c823300b6937bee4a4f370efafadcc48b857117b2a637573ac27b223f5d6771da6bf22d49ce4ad0253e23026687f79d6b1cd60ff025077161a031fd03f0c681ee2e1b0fd0affa880ea4c8019ab724976ac6539647755f19bc8cd84a236a9da390aab80d2064774dac0b5907 +generate_ring_signature d2b1be9c36ccec50df11eb89ccada0a0d065fafc1a4b7296d85180598d0208fb 790464d130c590b15283bdb524cdc611c1dfe90ed958cfdd1c61f4a8c610e971 3 c760502af5bd01024b414aa8e6ec3291d24a119c069e5394023daa44cf502a35 304177d3c97d2ea31afd6600fab36a15763c2a7d6fd2b179aaf9adf4a6d93288 5c01ec55a849da5d9c141a1da2cadd189c7cc4302866056619bcf541c0ff4c0b 67567e52f4f0d0b41c1b633d3ff41eb5a3af5abbb4b71ea61b57dc39e433bd01 2 b4a0ab8acdba36d8ad4fd610eed13b76770f511f8a1eeb988d6b69a25b647e0e4629d9ebd3e3aec6c364169cb13f4e4f10d4a98b81ccda56791a9d49041fe50528fea8203bdcb4e758fbca191e924dac1f617a5c0466496bc65109b36a921b051533fb23aada0dbd4510e2c8800cc147f99ff12bc5ee255e3412facb2987dd00afa61477ad739f781feea48d750df61a17a8ad2528529c8f34ba0c824e8cb60f3d206c54ec1165646db5cafeb9b547109e7a48e02545a37744eb0b8b9a6e850e +generate_ring_signature 6a7157bd5b7abb0dbf254d14383ec158061c39beb909e371c736bc053821894e 319ca2b6bf52f500ee46fa4e19e07878690597cea71cfd1294d689377803b4bc 2 f579fbadb5fe3ef087c03567db8a70b8b40670bd8e77c9cc58a519e55fa91265 75e979d40c24323138df595858e567668aa8085c2adae4cd9a72ce08499f99f9 0e8eda5e20345e1e85b44af5a172c57f1b7ab0a80d4323bb560058b4bca9aa07 0 2b025b95211a8775c1312a47c4ca764dde880d6084695dd7c15536245370e10624ac36a4d79a760daee6394f5566c3920037d1b3a3a44191b4c979f43a398f05fec59953f4691ef03c31ba26e718982d229ee32101bb940242ed7df0d5a6cc0a38dabde82a143358d4a6044e678583b62509b28a7aabb864b550c2f0a332a808 +generate_ring_signature 2ff3bc41c8184d849397465ce855f0331b2fb8089acd03896ecc40f13c26c6cb 3c0d534bad069aea7a7a99f476537d5fc7d744827fb3c5b1747bd5e628c0c060 1 6a2dd8917bb9f1afe10a52fdc06a7f1a8be194adfd27720f52dc6b68e41dac13 9a8ae3cce36c6a7ce062ca5008b7fbcec509d750a7af1755506ca42987e71f03 0 f67c2e6a42e440b6fe30d961f3c61802ea30df966308074be432bf06e81d530cd8d83fba45f50c2450918df418bb142da8b29d200aa637c7bf7491a9d8611109 +generate_ring_signature 4de455ffd336973df436d8cc5400f445edaadc720ff6edeff859b49238aa2fce 0d974c6f5c5852046fbb400ecb528e463a9ac8ddb7e4f87290675000e4c29634 6 e119b88581dfc38a94fe46a82590cd9b062d321097017947ab2a039d77723a41 7ed1ae0e3e982d596cc6b1c33913cab5e0cac7b70a22d957ce5b51fb86ce1535 d74735989a7d20983a2adc2f7913caebf4fc12e1d33946c96d893ed9fcc41b12 af83956b543c7ab0878eda74010bcce3c086b748e6ab788628c78eef075c90d6 357399bcc7afcb24242b9a1eb62432c35ae6cedaefd732cc858d7eae7cb92e79 dae830a72a324ab3113ae4913a820523184bbdabacfe07bcbd687ade7d8659aa b5558d1400cf33fa98203b0af326975ef9038e19f64b41299a32a0f135bc2804 3 af9c35857b1b065cad69e683c4816456db4398c1c673035a008bbf7c8c029a01336a4b17d5c4a57ef76bf98255d20969d6843dfd2bad7f42aa8660a0e6f239089828de12fcc34680d55d5980c1a934437b48d0ab4875e8a76550cfff8eebb30093d65b7c311961b754df5e33ab418ef81920fe162ed0d23f3a610acf4d5e810a96c9fd97d8a936eb7412fb34580939efa836d249450ae728843632141c2ba103573c225945b429ae750996a64bbba856dcaf8d4ef290846220130f8e8349a5037b93dddde7abc478c2f5a425aceebcd80183da27e68a9602218bb9dd34b0c40f196a0a26f32647313e966dc60d3c20bf8f64d4dd5f7a3c9205d89b4e689c7a0986b71a13ad639b7b3c7b6de3e8900b44b25187f7f614b27e14a99bcea2070c0b0505b4f1f922e00962683ab13e083bdd3a38f7ba35b0b3674b2b98db23ec9908c2372420594da0550d0066901936b886315c95a867b60a77966ba9cb2c876d0d5fce3bb2c38c39099655bf1673658a3f805e60c377a74df65d8731b88138cd01 +generate_ring_signature c5a69d4c24d9ccccb2b7f24569071ae98acf6e03b925e5a9cc09752d801c62bb 3eabbfe3695bc2dbd5901d017eb2e54b9caba0945720164f342fbe3e6ce66b78 26 f43a0cea4cdc1617fdf4dc9aabd74562d8a295c3f85ed4acfe4042119367ea4c f4b250e120df0108ef321a0ada6f8d56a290ddc4164803d62d92e06524f4487a 60c9eaf854e68882287d9f28241a6ee63496342162b018d85c7e547bd65ff258 d3bbab657cf5184747c0ea16043192f6b16743a2aec28a35fb3e03c78114d01c 8b00e6cde3c562f5d9318949c9eac03c685090b8a3d57ba4ae5704ff3b5d1f02 4e16fef6f635620955f7ca6ff8f3077e146acc5591573501874e0f7ec8a6d84d 1ab363c7267a5f871fa39b58b3f77f132c832a148255fa43d488e145b12f24e6 93d47d397bd986ae573e04cfd6502baa9f86572d8fbe2fcfc29d35a6c55b5453 a61daf279f18faf9f001831bc06539fabea3cf733b10abef58a1523414ba2496 063029c65fe66102a1c47575d374692a459a416b9fac9450b3c102494b85b5bf 095fc13758f256b4550452a379a65f8d1c23e670cb5904394d153e4f9ef6f0e9 8be1d43efa5494cd054ba8f7a2ab134fa778dde275d453564e2794af637c26e8 146719dffba2869567d19999455814cedc3384b8a104dfb6512dd434041e8f04 8c21f868f215881a4d8f8ecef15a45ea9c5577c96edc5f2db93aaa97627267d3 c6cd4788daba63903038377a7748208b58351ec9384c96fb1c85558063ca8128 3e5881dcdd2d29e42ce5229d59c8427d266e9c0bfc48bd849a22f9c563ff572b f2ac153edf0d83e0b04bf20f7fb6c70881a8079af523710983e8aa89373700f6 0bd89db8a50f485379de7737d43b3f7899c254190c41f8ed38c1b82eeada7554 50313db4b4816e421de0dbb01a534131546b64539e2ff471e63e8b745b825c5f bed722400190efb4c21cdb4d76679ac6ddd56ef850ea89f5ab43f89d4f2c02e1 c81d8bb33ba874db043e3d8bf303b5790192fea6459adb800a76d48614937fcb 01563d30080870956b47ac0514635364e1c5f8d45e62af0895c405e96be1516e 909d70e1e8813e0ac85788146a836b2cf2d3ade2d2c5b18c9b08cc41b688990a 2da5ed39c9928eaf2ba35e5195ba789f8a01e99bd9bcc55c81c5b1c7bf6caa48 4668235408de2079f6d3111e96e502117b10d42a5c0e7790e9eebb7105a792bc e6574c97ebfd1ffb756f4b27c71b787e0c332b830086081ba57cfa1a699ea613 66dfd1881a176aaaf91af0a512636d9c0049460df4ccce64a4f7527e2d0e0702 16 d92476d6e246736d9149686fe8bbbc0f551561742412ffcd02e24aba4ed1f006cff469eb95d3187a5b29dc863a936f16db138855d94d6c5db0ff7e88b8ef93038d3768aa6a3d964722c39b15f03c92888d547cdb9608eed212547a5fee785808df6dfe582e0cffc872fb55b29d512af69c88b90d381d28b4df087804cae76e04b927234355f252861eaed8cd00c07042e67614948492193ebc2d298e0a956902b3d6c9f965fcd053392481d2c3922a52bc779e321cd8c811e3157ea73c4908003b691a43646a250e06840a88121069912d81df4b8ecc2aa04231cea7390c580fb445db6236ba09baceb89f34686f1d900e1a4fc6a514a87b596963a6efa42304aa6e46a4e138bf34fdff9909d392113d4a117e6a6df512c3069a837f4d41550b6bb4ffae037948d1e4d5642b68528d8f5685e284067195b5de323cf1a9b1c706c6ef640917cee6c33df237fc981b1d82bc05922dd83708a71fd63c9b410bf500f2e4c1d76f26bcc19f92a910ae50e9078f24a10b3f029db1d59977baaa370f0128014b8f78ebc2bdc6c4509f94d959d041f6e8dafb06796be0ca8efca1030b0a76abc3c2f5be6f1311b9f2524ac6f452e57b374fe5ad4199b70482811035e00958c6779c4390b4c7c662243b24c2d79645c7c5b9d52e9a197dd2f4ab36ebc005dd5bffade647d1e77d127edff0e1f651589d483a8db87c7e5f002cf25db63b0b6ecd5140350d792ec8e509e4f6f301c8b7b238d8d21601e2c49c5ff854b3d4025902c35c1fb4fb9d0f1f246923f69e8b3eb2f86b7796714537357b3ebb92b504ac0dbbfb776cf9166c08abc1712329504be6b823add45ba92bb6f0deb289a0038dd6fbcb094ecc7d699f654cb6ec051474942756277f9ffd6bd2e47d0b5abb013e86940468337f70e113c1410c12019c6af7699710fc267956cc8278f8e256011d2a3a2ea94fd34d9a3c2dd7208e2026222d83f7c1a8393ecc66423cccf7b1013f07b8e289fb12097301d2f2e68a18b9301c1aa90103e4f5a32c982aa7d0970db8703bbacaffc6d4a0d9204d362d1982cb42eb5b6659faa2abb2933a7295d50888ecfdffd1be43cbde1006bc5bef47b4ac17e5a0c4ff69d9965520820dcb1b0465f9c21fd07faea80669fbca108ef83f1abe51240fdf0f9685087930acb6e104d6d59d0d1df2a6c00b91fcc7448a8327b621d5556d5efee934b52559821eba086f085fd1adebf5514a126b402d28522edeb5fc4d76c5989c63859c58cf32370e6f320f35fdee15f7cd069776e4c0d82f5f91947e62e523dfb690c6596f0dca0171889ab5c7f4f44195c86c7e9421d20b455fa8019aa389014939e67d0cbe5606d9570d9e3c2c1c99827993738348e8b87bd47c23aac6b864a448d279b675870177c3d3e0e121c3aa2752a0c8c9db30cd31eb01ba9e64636f87360263120ee707c04bd17d9c7114161a185df71c166a51b4182b29faa681c0cde0e0c43e2f8e0a1b8aeea02e72f8dd2cb75c6395738b4a2e183dacce4f58e6e55840ee2f2332041eb213247b66a959c98c4cf4d6a1d42dd66e5f2a7e80dee0d1c716f9650a6e0628e3a3ff87288ffe89bb952a6db212ba96a1620cf14b056961d452381217f50244648ee4f8b501f0e23f76a57e27d2847b0bb0e9be8d4e86431c5d9602a56c0a373b3c97de97157fc2d0ee29e29deae82ee4c93c97a1fe990c1e9c0a963cac0df5e09a4d1ddb3414ca331c5335630dbfcfb07af8ca2e13889af0930232837302625041547613385102a211993380184b3c586bdb3ff43986a744325fcaff5a0f0a5812d074e1900d41ce24324b4f30cb31999f43f7df60d5231342f7a0744f0882f304fafdd5ba3ffdb52323a7418a4c1b246dcbfcc7e71d1f5f37682796c90bd65c2430e1dcc2ec73324b1a88321aaff72dda1e8bd78449984cbcedb1b5ba0fcf3c54662a1b4ea9d1a62a3cb612609ada804f9a9d7d70055c24860701b97f07ceb68e721193c8eaa9632b5b3f7b594be8a9930a9538c8f9ccf2b553aeccbc0cdda71a58b16d594d2df16fbc8829c7632b9b8687c518f2de428fe87e7935e709ad0d2583994cd6f7c9354cb38b4b6b511ab3ee8a3e544fecfa12886f91ec2f0ec772c9bf641bf57bd640ba5f36e57a685b4b548ded06f3d008f81210e02d420296ee50c102341a0ad38e23c312618fcbf11f960c75c9ec53c5ae5c113f0e8602a93b3ad40f211e1852311f85984e3ce48f112b3f469a8ddf5a6fc194b1dc310cfd5e567dd500b3b6800001668fb8cbf88e7abf0fbc38a432a2a3138f29e5440c5fcf5d543d455abe76e536d777331056b00517980f8e3a68b192f7a30f1b5304 +generate_ring_signature 1a25025edeed2967079043c06388cd736a6305609ead156e76233de39443e6db cdd7e9ef74c4f826659baa13cc7027e9d3c0b4379472adae05f36e99ff600541 32 18cbd5ae5ba735e8ff1df944cca9ae3bc07187947d3a0377811c7e0769cc46b5 2eee82ab6674d77e15df8e3fef6d52f2159d96f510aef9f03a7080cc1df8a551 8c891c48ea8e5578faad92012e8519be5415a6397f92493cf368598927562aaf bbeacd239001a036995137abcd6f6df6af0d42517532c177cee3d774a14bd50d 6a7439d1ffd71e17552bf2d14763bad542aef8690dec9a35e7033d941df846cf 99d0c3c86698a62548085e50acff48c7142218ab6c51308429e5d516d27c1e2f b14165aeef86209407ffbfdb583485fd28f6edfc09bfb30000a69af629660478 a1b08ad9ac3f2fab9ae1c44b64c1f8c0d89c7ccfe524220a63c8fd4b80355937 c6f39774cabc63d8a858d76d2b67968c57754f6cf539a0516ebdc2681bfba6c8 e1420e8805e8267cf5cf14ea411f178feae61416ea6097c2936cf255d6da75af 68f36553bc4e176554dcdb4d40911c9d2fc1ad535ed5de68e61214740973721f 36995a5cc2d48acf783e851bc82a7d33b39e8d5fa98a28d053496431f62f4e8e 6e5a2e1e833e72907908da061752783d2bae0fc8ab5a5269693ad104fa23a541 e4c81b9504412585a70f804cda57e30835428f9e7b33db9e303cd8280e1eb133 b21e9d751bc67faa423ebbec6bdeaae824d66f1fd287229d15202ecebf972ada d85dfd2d4c2d681e4da972cb7ac70b9472b5a9cf847e51c7257beaf9a71b6c7d cc68be54b9a80b51446d0588e46f314f0ccc495c7d51e3d271737e76a465c33c 34305ec1efbf3dfad30fcd4e67ec2b03687d889bc2cdc77ab48e0b5ff9cb56b8 c1e4997cb9f2baa62de3da1f39b14b0f81dbd2b5b1458d4017e9c3d78a24d406 bec351ba97f26bcb0b2e4c2dc0c42db166d281a7907971bf5087eda6fd4c7092 a61009cd579acb5a183f65cb11309fbfc2c83f15a8e9a3733dbcce4bcc9b68d0 4652edce62b97c125f033318f97c9d0b6af10e054d424e0054da0892856f8a5d 089d6c08e5cda9afc0e25c942156cba8e1154aa6e108f7157b21beb0b1dd9c71 7e67864cce0de6e0b36e85a0ffa8839316dd5257f1d43a4bdacb63723937940e b7924cc3f1eb11c19f0f92c4328e485ff5c087f7cd65d56266882437cf36b3f9 b90b30b4dfe9bb2e74a319d3fefdfe16dd466c5f304cbc7002c0b406c75f6cf0 f0e309314c00e1052b94b8b61b8215385eb018d80f7d416c7ffd3e303c8355f1 532eb3f1f94d0c51c3c2084e1459abfb07ef8735edfd8f0549904a66bea4a6ed 00c8322013b025dabc91953fc9d2c7558e2d482f7d3befba5ade16bcaf12e2ab e073df0ab60437b296be99f91cb20f4e2739ed54d3f387f1bef16935a75c1712 9e3706f7488756a2813420361f139e2e8478d77a52a07e0e3e0f2f85c873eab3 021fe8333e571ffc9e67b99ba619a42450b5c1fffd3139127f63412b9464a57a 1bbbc450a6b3450d412637420a225f94f6a76e0de606ccff43a749e255e7a803 28 5dd96369e04a442e917f63b0b22d8b21965977601b4f2d933633fabeaa01be066ce1dee3f1fdf05983257f5527964668720c122bf18f832e458efb1f3d4b4b08a1e190f557bc1efdb6cd4e3d347b97e55d491a7691a1199520c8e69054543800d8f1768ee12d514233b1547d56c4031a5d37ae7e54cf101470f525cd268e5f0d36b4d5019b24a559679119dad490e9404c2728a054ec33b39185d2c7b1cb090e8a15ac74761f3c9b5e7a194ac4234513f33eddaa09a747fa2da116852a2dc8089f4fbb21fe317dcc242f6928e38eacf8cc0c802c3469959719304c7d0d27000517f4fc7cef3868427ba8724c6ad934bfa2dac6ebb445afa1cd832dcfafd8cf009907ffe2c8f32a6e6e68f6f8ee24f8f8eed6aecf2b4826d9ca7b0146ab5c3e0468cd93465a7b4102c3fc8b1c00b54561b078b422c06de9acc86c968d9cbe5206890d45c4aa0e1c352e2a219d6b0002c82eeb2706f63a37587feba6a0ab34e40477000502380c2deda391802db2836f9a2ba46239be2daab6892440004b63700d98e22a6803e6123d6e131a463cf0a141b552626045e35d37a2e5bc4f2950b803eb8113514749979854f1895705aa90f8ab222b010d2ef466dfce6de455409606851b25e1100dea08363eaf37c155f5af2fafbec2fa52b0dcd9d26e04d8ade30f6442f7249620dcc6215dcf10a6d9ac491ee410a9dca05dc7a9e9fe4a47318f0e5423df12b719807196abab3e37ee861751f7392f3840ce7fd85353f3a8543708e60012672b91402e4c0204b73628e1771a4a8ede032d6a621fa039c5e4988a0c6d4844bf775efacfb70085feeb8eab5645aa5f5cd7dfe5a5bbd57ad7494ffb036a08defc8af797c9400fbebc84bf619269aaee66a134830f0938c4f40a6fbd0774faccc3fdc72a295f5f06a1c441f4b4db0e3f997e9fde22a80bb7744a8eb90c3bedae519f7412ac666c7c4d5f8951b55525e7327b806b6e8d3e88cb380b4d03341fc62654d6d60f3697aaa49b5fe3e3df7f086d54f6e79d0fccd7404ac41006e83cc4d9a01e84c9db0df2ef1c593f20e64cf5f9545f5006ea0bfc8f0b51020d633cbfdee4a65f1a1cf8d5e3cad8dcefc75ea7cf8c546bb833efc3341898430e7835cfaf33428952622bfb8b6a02139d76ec4b11c75bf2a9d9cb3bfadbfeaf0d18beca3fad9111e8a53157d920d05f3996c0b5cf5d4b41e9cc11f4edc5143b0690451a6bfaa983bfec2e604cdf8ee1b3916889e348eb7ded95a5d4a969d48303c7fa4643c9b88527555b7653b203058415622b0d585a45e00984cd948f9dc40c01024675e29120a7cda2adfff6771244d6140083d76b266b8d9da4a78526880bd8b2e02dccbe6cd5f769c1c0e3f3e4db53ebccbeb881ac5a15afa6e492b1c20d39cca6e04f09150f2cffb7e632879b3860083ef6fdbb678837a5dd47388fd20eb250904c95a0e52ea7c7253e2ed2bc27a61533743c72343f978d1c348f42790e2faddf6964acf1e1e6e8b8f4274b8e877636483d49ad801ad7eed6a9a9891405e0cc5418493cddf8d07919fc110448dd61b7842ba9bb9b60305d3d142aa8240b9e8132722e926f6d002e79eb63fbbd68ee624d9c5fc516400499eb419ecd200ae6f8f7179433d3444a6c5656bc5ac978d92093d92bcf7d23263414f0294a6507760db07e4e1a7a0f5741c5a3fc75815e5a9d3b28b540cacef7515b8c512ef102ec8361a89b4e53014ef0dcd5f4ca7c51cd334f8856cc3a9d734f521753dd2a0557ac07e080678e2537400d67c61d1f0fc056b37ed8fd40fe64fb4d24d08a8808df316f5d45d6c45b1b7c0e897af619acfab1eb1e2f24fa04c96cca43680b100c5470793570392bd07220e0c79fcf83a0b69c17e6f4314261c76dccabcf7ab1014d96a776b6d93411eeffa82b4a44c49717fb7271b0a12748f8bf634f5be8ae0d9b0dd99ce695dbc4366af0986d12a0ad8efd629c0ca4851f5e054bdd673e210e31efe35636506a55ea2476395a88b8a44d4b4ee2cff71a76d3e295a8fb8ecb0c0d51d7b12b9036cfece89ec5e1adaf5da32e3c5a9f176c9e0e7d2295d905af095bbd4f0745be8e113f9e889f3e7f6dd83dbcfc2c9802817a9081ae23480f420384d98489b3752c9c754b87aebaa4f90d74dbc8470506d36f4dbcdf8b1686c6096af43a7099f47d58585c711527c41472b71b617ab69616da0b51802b0260410129a09819c7adba2caea93c856641569916654bff27d7cea29900f6276cb84b00bc985625879d686a4e74b612b2711ee37903e40739dd2c818ee56f9f85234d08da790491bbb94895008484824eb28d6d98db9afeca9a711298c49e72e1adc50485d23b61f2c886001ee4fb1ed079cc014e64950d7255318aa1fc85d4a78959055a7e80747b9c2ac60e1cf6cd77639463a949b109cda36379149b6777074ce10fe7ab50838f4d0bba7f76d021b16a1f897752490e5945e41bd748a2ff34b67f0285e33faa5120ffeabb69e584b30c857cf024ad6a6214b361c3132c94e1ce870720b3942f4b30a0014b9dc1f3d966d32084aff1287cdb770635c5b070e5b15f000694f46219ca844629f732ba861cbbb982e18512a1a5fef494ef31d4c913ad03b44baab077de377a0ba7a0fd25f5a44809f534d9086c7ae6b8175df74764a300db173c82115cca3f68e1eeca4ee4721fda53583b963a49ce082de78cda39020e33f448b8b5bfae1e8dffc235335e3dbf4c5f6fd6d51065235a86e7f654c7700f06281e2f87bcf94218dbceddbe704537825663f7dbd8cbc2215f05117be12807dc2afd452ba147fae7d265fea4b3d39954b5f3d2627d6a96470e47e3268b2f04a0c7eb05909dd3ef41e863643306682c10ad5c04f7af6d097bda99afd9ca4f0a +generate_ring_signature 06849cd0519c6f1009ff2038da647ab6c1db4158bfee7644db31219a32de2265 5ab33b23374071466d658f4931aa7ce87e2800bee54a77b3228c07a8d1f9e4f8 50 f4dca6918c240e30306a9f94dba59fc51a8ce538d71996d1b1ec029c9b32acc1 3cecaf5af6facb8438a4feef5999cb9fdc28fd4e4ea8ecd67ee057c70679a00c aac24c3bf580a2047cb608e7414c28ba41923980929864f3a59c5ddabe1813b1 178bc14f54597a5d3216a28b763597cd9593deea8e59a5e4ffab0d3af17dad67 cb648c43bb1ea79463c31058908db6360ae89bafede111ff9119f619ad40be49 645417d726d5f09e7c40ba1b1b1e17de4b445cc6b443f106ef8cdf20709de226 42fe478f50153993cb0de879050356c81055b30b04c773d07215ce8a05e86b9b 853818c89f9822f8b5bbd1116cb8fb192b49db52881a63fe91be8828843bf49f 43996d323e85feda01a303ec77bcad68cfbd45fa93ec6f52994fe002f3f63320 e80f906975529b458402d90e871d4fe391325a3506880ce3590640c1721663e3 249d745a3bed28cf3ab726767b78266a4f0df17ee72e4c228dd4ad9363198416 127e4eb44765ebb96929285776f2f28b8b506fd2f802709b960ba5122386e4da 47925a49af8a140f389594c6b0f58c0cc7d1e9b2ddf8b4b19caddcc3c191e66e f2c5333dd0c1fa1b3bbeb8f76e0c3ad4d90ae352e5491733577f53653a512a47 41ba603e6990ab4a6b38194ee651f33b1bd811ce9b415f738a999791e9ecc08c f0436f4175cc6fd250f1df326cacd940d9554ad61ea96fc21f7338b5314c0f0e 165eee723e8a32ed375e2454dd569b0a94ded444d743180b81883e7faa4a4f13 a85e2a7516551b743a2485984fb579351503bbf3ffe4a74a04877f9dcbb53690 e94ccff70bc813f73e14eae7093ce6610bd111710c284aec75a04b0f4e931fcc 03671c6ea4b79883673021463f66222b4aec7c91f5bc03ad7efc562fb6fe2db5 26a39e79ccb667024f0b7c90c1dd724da2e3754d810cd5491eac2b3cde14a1be a250f4bf3ac3e0b8aec3983660251dc7d6540d59ef7d05ee4b2c7412c166c98f 914bdb7c9c9b0735dd1be3f60171458b8e312e53ddbbb23e541660bb31fd1a24 a67f611b7bf550f4b54065c0c77352d3c49a3dbb71dde20777788eafcea84513 a9d5fd6f07e027beea34762f024615ff38a74cde6b009cb1795346607e5c1ec8 7c894a201bb20226d380c8b58342ab7b946782bed4532f9f25c40f639f1b1312 c360d3b1274db4fba04b8c6db76de6d52fd4326f7aa8ec955be3c62aed7188db 2be5959d53e19c49a6960aab64e2488996addfffb2419e3c8cef3db1f3c33a35 ba81236a84fbd39181eea3b60b1a85d6947007cf42c93ac15ef7a2cef6a4d3fb 8de16d964a3df51e199b8df767470f4508ccfccd27b7f6d78b62cb9398bc55ea ef9b3fbcbc282afb025ca8db586de74a30264588f5f1060d243c7865e4c18c6f 4dc1a34353b429f5e1811290d12d6e69f3485e6af9b389843aa1b5a067ced872 c8295f4fcc4620e37171d648e229e07c0ad3cf81fbdd49e895c08ca2dbb1a73e 39ed7eb8e5da66c5b688e7447508a283c96bba5c55ed8ec000e42ed4872ff145 4f7f18ec1e2eeee07658a986008ba1086b3a1f030a0dffb339099f94d4c8f3d1 c05959888131b964bb63476d63904d04bf1e62538e8c227fa6d6a84b1b2c5755 9af1683e1561c8c82aed8dc257a5f301eeeb76d538cb62c80d85825c3b58840d 58b9d74270c39051b00080c63d5d306b6c7e8ce773b912ae34cb528723b31409 75c8f3e9399c3e6a233b1f511138af008476a4fef6749b0d039ad92edfa6cfa3 8f2d018ba38454006905612074115d7f52f021de849afb63834f621b0cf86813 edb4523261f4218904cd2316470a97269c10b7dd6278345fad3c78508947624c 89ec4715a6d64497ee9825ec916c66ea04f4f6005f2c09737d094667e0e2c134 60d872a3dd8bc3dcd1e620a448fb2ab021b5dbd0e42bc54fd1a22173b7af796d 7aa0e6c13aa26d3cf23d32b3aac2f4b9f07dd8975532d326fbdce20cd978b271 f20616d23430a3a6df1fec7eb6ed20ac2f5f4d17cc4af342a92928939bc220c4 30ee3a8103f3b6869b385f54aa458acb4fbb8225b8cb1ba1ce0b313b9ab32830 d2c9241543810e19e2d25fb2b82f1bba7a7da6d72093f8aa35ccb1a7eb9d1271 e716caf713eb8e1c3f8c0c02dfcd682faec217cd7fb0a872f9a6e498be2d8f1c 8040cbcc375bb11d6c4da2de3035a95273695f28b3f766a966966a217af215df 118137a60cac4033aadd331dd3c1d1511d4c6b3ae41b929c05a31252f43d097c f28183b869e0e416647dfbdef14a6e7b1276ae55c760120cfb92d5333cbb870a 39 e322e88cb5fcf73e3b2fc0aafb3d20b0d479dc0700138b1d93587c07ce8a000b60fe0465a999d792750b4c6f2eb782e173b8ad94b45cfdc8f0d9f09ab62e9e0a4b01893156ba67bb00089153289a9b015b2b6bca2cc4e2a047475b8ab59d88032d6acbff2a28f0c1e79f82429462c9404503bafe9478118a11c6dffe11d62b0ba4d7846dc3aa9b8ab6c96a8a10bf56ab68fb28502a6fa14597076265361f41094528d8b06288153cf8a0ad7a480f7142fed424a7a1c720948980ee4e10553904eb151e09515b3287ad6d61decad8198b66e85bb47d08a5da3977c36717e89e08e0fdb015aeb9ec358de6cad1cc9559bc0f399d7a42a1529f6a7affb18d568e0d26b9fbadaa2e3324b973b02e7735e6e19903d1cf5be12bac70c36af58a02e40125116de322656259e1d747103704391333f7440c96c5707fb2500732674a200fc80ae86019e26ccd6ac8e3969f5d87a005077f54a06f78fa5b87d78e9bc1a10dc1af47d2f98fd5fe5cfa1117654fa6eceb69ebf68cd243cc31c5642428244a084d49d48c54fcd80a79082c49f7c18f3b0f55bfb75d306e1c08b4b0c8a9940606a6f2063c5d084e6ee6870ea2a4b20d950f69dce8327507dff63cd770fb0490008228b6243865ede83c609e3f5e378b6f3945464c887f7e13f2e534df963dd90971a3c5c5c12c74dfca1b55c5c971c15a13e94c55d8dbabe9ac34fdd259122806a959de5db3b43abb79039c00e63cd8fd068b90597253be2709937b8c23eeb00d3121b82999c6ba02ada2aa745ce5a330af3a22a4ffaa238feef63a9b5ce0d90148899e3268a123cf79df77561ec94220f0c27a770a80fb436a5480fb02095a0398ae5e5b841dda9e643553358243e4b02cd93f8bae7a8520ae5e52f8e3c90a0b9468f6e9f57452c324bd9e1b4e602abe8bc682fff54d6a1aaa0d069f156e4203058f3977463aec20b1e7fe1bd4d58eb0fa9ecf1c7989acec89b65e13242854084d252fb445737c4cfaf0214ea2dcc224ffd12e0631d7ed2cc08386092597bf0a65cfbc691249732fe95548ed68107bf2cfe6296294cb92451b01c016dfa8b10482ba6f72e18476ab8631b52a8d5cb5864e4d2fe43b47e0aa1950c60cc573e308dd0580487c2a09ef5e9d5c6ddeee1264cdf6a128f1b347a338185bded919ce09752b4323c15c9f582222c022907fcf4ab19c1a6b60feb67350ee4ab1ba8cb70bc39105be1a56a14db876bd8b0fab2079a4de9f3a8da13cbf47e79456b97549080d573aec171cf6a35789063f3c5a80b4b3180d2cfff2a1586f617a639284660484722e2eb293a49684f98243e7f25b6241805dda23d5b65e28dc74eee3a7e4079e8dbe531db67724046af715f5fb95e89e835da3b3e84f95a5a7f68dbf1ccf048d0169e73915b440cf6d82db32ae5570fc830611a645d8c754686e4c8c2895077bf0fb0825537a787763f5d026f692fe6d2573d90010295042890d68f76b500864d1bd8ae10180168d64d8cfad5214418693ca10a40df601d7e952b1d46ceb083a09586ffe05b46449160e28a624b2dc797bb47a842d3565e5ad0ece0ce2690ede7ab2137bc02b59a84724e65fba52b58df548d9692c94655ce8b7aaa25a690a81aaf24bc11b3c68797e6531b8904e8cfb7a44f8487e4341c468f5322e43840673c507869135a04b033909dcd70d1aac810931b1ee217e6193f1cc1f37896b03f27f1b6c1b40d2850ba70dd0e076ddc9d432d0d97241142b58575bcf5b55bd026a8d6ece53181a1507db92b858ffc1c04f90206e6cdeda67cca76c56b87c3f0ebad09b5994aec7550dad847dfe7424ee56be526a416b9d9584c0f5c5aff0f205f00a4bc23d5953c56373ddcb1b3ea1e2ac823486855bf51be4d8cd749014f40b4c9de33012ea589c6b0ef91a467abadda3d730286506c5d3561933652a61c804f211455aa4a7513f5a23d6f8a5df1833ce4257536711e2a7d5468269d2608c034b06cb5204f87340b6d7aff69d6eb005a09b596d3603e56e8756c736d084dc0e64dff02b8ff16329362c903f418f9f9314641a59ae97e81f9182f03abc41ce0b5db99eaa901a5a04cc216efbdca56844c7ad930966ff80e57962018f358ee8026c307a61725f67f0204ffdab2acb34d7c136d5f53cda20219b9b1b3ef838630f6ad77d15c509c8895a8f4a409ccc21874d998920cb6e48b57d3a3ef208c3500fc5a915da32251801483995f7d8a8ad39afee832afbf660feec4e5dde80540e05b707beaf5d649de8519df9d8194373236e7207785abcfba18cd8483686768202dd5d1f18f514d4ec8d5da347463fca3e712b6c166cb2cfe93f865deafb1ea405848cf98f22f14335529cb1ae5fc43e85137f92b9d151a8954a3f504133dd01097e9c8f2437c501a9a155926225cde9ee3b5943275ad8e3ab97401d967271de0a7c5881de2c8fe8fac5a89d1188291ccc98c24a68abe93ad6ae8a06b2d29c1005028a340bf2bf53798163010d20a84954ca8ae2d740cab432fb3d17a536e17801cdb744545814b97acd0842dcfa517a8cb73f0eff2b2b4ca6cc6951e810c8f5068dc6dd8dfd6b361fec24d99b8113ee8aaebd39ab36e201c933e45a1a83f6570896f30e71a0e6cc433a422c6734fbf090e178dffe67ff028c09e786a5f82b8b08b2a3ecf1065b8aa6b621b0b1285b716cdd8205728fabc26f389f37323aa397079b073b79d39707bbcb2d350d551b27d64b331770e6af8b91c8178168bd1bc10db48c661458f4b274eb7a275bc21d327eb7669c8df7901249b088133fc6c6200f5c4960708ecccaffebe4483259bf669da079d9fc2d82bec9db4c518a4113b70d6a2d7211f2d264064421d54a402023f70485e5f0ee2ce0d41190e0a041f86008096b0d4d212cb1dbdd66b04751ce6bf8e6b43656523197784abc39c4503d600dcbf44aa810617405e214d1190f77334f2a3e3adeecb59409dd2d588f7e5d320668e55a7f595e71678d0fbf0d3b2535a4aaac3af2553f866b572116003c8a7a0a2bd0e5f1e0f3e36e749e2ac1d5c0127c5355a7d61e34aa76a07370358ea997066f31106fe2c73d2c966896d819330f9fcd2ba14641a1b3d679782224ace51f0d7fea6bef41edaf29fc5af8035f427c00a64cc2a705acd5f9d9b64771c7805b0d8f9fc456ea5c91ccf3c20fb901939a53badd780d4261eec5de7bf8a8cfabac0de00085ae134d46f176afa458a4b42cda58435fde587a675d59e499d58368a8072f5488bae58a357b193b1112b6841bec5d9e9d17aea4afab799a820670aa930d29c69c83c332a403622e6cdb855ff57ad3c0189d76ee017fef6a45ab884c510e14abf06a1dafa299f293a05f66d06ede7b7c71bd4845edf55f0316aa2ce6e909df19ac96e071c85e029b8c9dc64822d141fe3d7a63f22448c6d9f97e21d70203106bde1fcc26a5403d7a1c42e3589e28b503f13fe4c828f4bdea405b37ce7c0293dcc9f934edc67b69ade5757b26c5a660bcfba263d53983a8c229a3a4bc19018238db3bbd7b8af80575cdb6cc399d5c59a9f2073b5de1579ace69ae52a09206369f8f2062a7d3c65b13d55ce5d36a4e029dfcac43a0673937478e445344a50dab8ac4c222eb55381fd2eb44d38241aa29c42698911eb227b1f7bc8d061e55001651375eba227580f24cd8cae5984bb4bb09ee57f4e8c3f3c98d9b57f68e800ad07e3fcbf7d30c27f1cf3c4c47f87145b70a634f513b4aa3dbe7b75d80770e03f263abc6449a1e4ece70ffd79bad231dc7a0979f8b5654c4952bced59024d6008744bbb31113115c8ea87551371cff633f6a8ed4050318af956c11a7d9596b0fa34da56533dfe6d91719131e6a098d4c854e2be30d637891f3eefcce020a7908b4778a70de196233e5b49030258ddc7b128cb843ed6bae6f2e0b6430d169100e9c5180102471b3f5e6804d8ccc14b8d4dcbc72832ea6ecfd7194a427a1707b03339ae7bb7a52805f721a512db6e257c48626262ac9ab7434d36d1e0de6d8ef0a13c87ce39b299e73bda10dc388cb10feb0e79516db6e3640a1d82917639d6b001fa4cb430b74602b0167812108cabbb9036b9160d2979f1d18caa2aa4ccd750b4286783f67d33100f0dca786181410b66fc8db3b9dc1356be6f8fd00adde80068ce364351e316e154a6d87cbfd0c9fc781cf0f6c8f98b7f7284577512abbdd0898ce4d23351ac5f1e812ff7b9c092bfaf5f099c88088873e1542f5c556dd8e0adb066f1340b301547349c04c95e320d9802e27597ad1fd449d7bb32159453d0c12f75e3e42803fbe4f2985444511a292b78e95df63de4c68a1c6fce18c8278056a701bf228be9669fb31e22fdd9011472d23b23b7563a0c09ab45b4d396cd8093b7af9c52d5d254ad563c49b18dca0ccc9481f39ded5b726c7d32837a9fb3c0900853277728d2936e4d56b6b105e76b03a14b24b2a0eb73329b8b9dd306ee604de09549b6ed481df03345d8eb2ceba043bb262d42231cb82950edcaf47067608 +generate_ring_signature c3ed92bde9e8446efb5b9367d42ec2d1422a2cfa450a71141a7097a49688df52 f0dab29e41e047f2c8183acee8e0708501ef4ef0ddfa6e7f17a9024b6ccfe877 63 da39f87cd0110e4eeb4c3569349e603a31a6fb69dba927f28d18ccf5cd877d42 cd374e7929e2a64d6b9be6dbd9cd599d84d20f7612ac652f64372b73f1877408 fe80449785af9cf3ee7a75a83a43659c6f5ba4c18db3270b87fa75164c859f0b 1be6eb1f5707cc1869a2e6ebdaff53cf48ae08eae0a8b1e531b888542cf293d1 8ee5d44e7badf8d5c406edcdd8c530b16e4e0b3b9b9bb9355befaf2efc077aea 3d438a0fd481a0eed145afda32e3476b76ac92a006fb4fd7810ad329a29b858e 6b76c127a388232f2df579062c1bf1a28528be805489d4f2705d66123812b2c7 680039121a8a1928e28f3153cb1176401d5caeb5dba958c24fcfb0474cd4dea5 a19c49f7d41672de74e063361060426d2a8b882349b92bfc808ce1e61ef12f11 8303590b8103b58ae2c77bad446c90df388ab2ecf06baac717f0afc80695caf6 562b47c8f013f3b19c61ebacd5526acfeb1cb804935d358f7c91ae04c5837419 ef3a80c7141778f0a022a868bc60312f647d43431992d300194ef4ba3c35d438 79b1823612b9bf0aadaffb6084fd2227e72cd3d7e4e9c01ed1b49a6676ebaa48 0dc8aa6f08a8927375135075e0bb400446e6444e0e8417e6124520eddf59716c cf05ad9b936775c640987b5c24e97747a556d79438e8c971026419d7d570ed80 c2a77c27771f2b48d872ca607557e68b5209646fcd51d33bc2e7354ad20c64c6 bfb6f453161cf18160d66e15b35b0cec92e8ff6f280c7f8a3f7f6c8bd7c43ac6 f51fa0445e7aea779340cf8be0bbf08ebd182f58075bac9d6444c6ac43fc2f5f d007b1eda8d4fcf518760ac888c98c55e95d31a80f60dd2b4ac977b16ec218a6 621bad8defeb944bb8d45d8e639188f7126c575c99181c490020483bade02400 4be49d6eca74a476c53ea0b10339efd3f1c7b3b7ec94ed1231ed9d98d0c06535 e290e3eb2f3610c491129c37300cd2e06bf327604430c38e4ae74c2378521c01 404bac8f26b8258897596efeb6a2038769e5fdb5bd9f025879e62de6099b79c2 d831751f5e3f66c566623b38c3f42772de731327660ad06a3b074007ba95a0a1 1a82cd19f29451407682902139f2c23830fc162b921ae2ae5aa91829559610c2 15d0830fc658ae55e4dd3911034f674a1a401f09b7cc11c3cc6f4d2245dcaab9 f48765e2972e935d57885af53bca175a9f7900569120d1fcb7986c2f42b3a305 e3e2b896e6bc92b4079ddd3a3490f9c9fd0e759fd4fc2709e1d742a6196e49fc 02c733dd69cdf8e3890d006ea71c1555a8b6407fa2f3ac6d967f6e43db4df100 42d84b667f691ba1f585b49d1d5fa029da5520b0686d8798c592ea844137b3f6 5e6605e0f7abe6131b382e6a8759c4ca5fae77ec8291fa878935962ee02e7023 43db8314dbb6db17309b12392187087f92934bf63d7e052ff7df1dab66251e46 c1de18964d5b564728cda7674a983d85b9e61af2a5012489473cb9bd048de0a7 80972ee2f028d2f4b2a15800a16152d42bcc81ca3c2437145bfca495514cb69d 83ef50ea7e02045bbf4f158d32f4b942284e63e5f2b780280a87703e4f7036f6 87604dfb8212ad16d491f0d56d7b311b4b24888b360c36b5c1a1129db3f3a8aa a3a8d82b787407f866a125572c62efbc1da5db0ef23fc74562d4390c68aaff56 01fa56294a12caea1c202ced527940cc24c4a89dc8270f49ae2d18e842400b28 9f931197ff4f05f68b25ae4a86de55987756793579d84b5d62de550150164c44 b61458c7d49003834597f78d4a2592d1fb486482e8e7bc3c3a57f49ef2b8ac42 d554e6418a72f0331d05757d200c3692558f334802719b53927648acddcc3351 cbed989db9983b608bd23cf0f5dbb80015add6f0b67b1b6774dfa96e70d506d1 795ad4295025bd980139455246590abadab9203a035c50846e4e6bf5faefc729 75b50ce254809aae0d537d54c0156197e20dd20c84bbd9a62e2e301590ee0e3f dd8006d8f8db4216f3a0fdc27a872b9f2d16c511d450ef8ef9204f10860d8453 9903457d3aab9decd7c2e07202cbeeae6191259e348d0e05fc8439f06404ac53 40ef0206e8abb47158988a82b39fda91d6f52d592e49ddf90181bf959be81da7 baf85bdc56b8033c1e262e08e123e5353cb7f7ddb2b7ffd74ccfa529227853c1 a4d2ee759a828c54e976a8bbc55294e0f971cae0f30c4ff2fdb04f07de39c7c1 a0d47358f5ffe1d742a0eec679ec72cd1481f7190cb0aff2baaa141b68899de4 9cf118c576775697f38f65e1605978c2af376fc5e87e18c7438b07b48cb4ee82 9f022677e170ee00cb689b13de994c7ae9f5bac789ee8c8ae1e4c5e7e179e880 9064ec6c92d15bbed8c3ffa9c54dd4a71c3e940373033de467234d6ba7e863ce ed04f38c3d6180703052c3cb1150e685ce390ab1491c85cb317533f10fa252fb 5b71fdcbff0fd2e0d68d25f03b750e7ec3adf0c4455b0e06bfeae8437cf746af 7bd75fd7e511a36f6462dd1242f4c53db2b6fee823cb5bedc26bcc9c3bb52372 3b403b21997c01407a6af4215fd3c8e21b4badfba425c032623b182bb2e22ce7 2d15770f93f004a76c5e4ccc25dfabedf39e57420ec152fdfeecd75eea79d139 f1d12a64020bd216a36776fa960a50453b3a3cf398ac5cd64ede354e8b882868 f1ee519a49f43b4d6eeab6922191742c70adc22d3eb88f68f285114600740f06 8b48ebb921c0dcea1e01d7600fcb33fe4c50a1646de0f5feb775cb9bd87b8988 3485e0a10bfed2eba8dd4dd81e2ea42a7daba5c4ec4a035521004ef90274565c b6a73a9b1b67ebc907bfd0429107f58b7867a029b9e48bde23b821102f2b0ffc d5daaf0ec5ee5a4ae2a10fead09449b44b165bc5e657dbdb9aa0ae2a16824e02 58 a583e89fb43afcdd5e5987dfccb123c297329a19aff3335a67089e7d2de322086f9e14e74198106ca353e31fa3426369f415a2bacf3160ea4e40aa3605e3660bdd62726dbd0706112964a0a28ceb7c11c26a0d0fe05c7090e600f9f801455d0d694fad13006c038eb6b63beafb831e1016c0a0791ad13ed4d17f0a3914bcba0df45cd71fd6eeb60349298f944f6d4016cc72a25c5c071ebb8965946733dea30b870a4804a9ec89c052e8c42d168bf6d74ca8ed846240a904e4260db89d12a500a56cee04ab6c5b5c467220325189b29bd36d544a1d400d41d3b989fff7be38072f479c12f3da5992a926b36f30cea493d9dbd80b653a0915a721c5be1b7db605b7181d4ff939b629488b582cbfa8b55701b955fe7650ce9bf2907c9d87f62300811ee6dbe80a1883e316de9cc92d932d8a4f640aedf587f842d21d6105f7e50052f1b17b79af70064bde9a5657483c8ed1e175de4cd9d54759ce648e7b88d80250986e9dac1372b1571a4c18b0eab28f7f73246daf72468d4e8e34af03934f06eff2618127417c7254efdaf71c2e92ef962fe47e9bde2fb8bab49679681daf0c8eef29b1b1b08181631ee9357281cd5e58a771671e2006fa5389c1190d58c600f98b78cd75d697f132f4240009ccdb0fb39df1239cad5d84fe43272220d0eb01c8c8e989e12b766e784d3fb263b6be2e984e98ad1e4595f08c1652a38763b30e8e23d3092ff96cf7c40476684dd2193139600d0c8731528a0e9c536a8d26a10be91dc5671f96518896354360da84916eb057c9897347d40aa555c6921116e009016af319005cdaa2029196e31d59e782b62ac252fdaee47cbf0b2db408d673051f8db6fd7d6f2198d5c970b2cb39cbfe3717c55eee111f636d39dc1dd6691b0be2a6c90bc560a43196f2399fe935c1ae69e0c90b9d72d6338d5db864ed2ef406f77030daa83dd3f906a9d8cb39d74874d14b022b8d96213b6fc29c06e8996409be201c650d2d65cad9a1e0a70ec11c480f15dc4ebdc69301184154867221c1081f41b2f5e34b7db14463af3c3b635e11eaea1cedca5bf0b2a689a8b0f127ad0c0b6bad404f396161b1510045a6767fa939f33752bdc619bdde7fecc4b76b6607166c34f75ed8d3e9b329ecb231ebb036bc4877e92c8a9a62d1b931768d7db703c4d92e4fdbae790fd5166841925d7e4c06f799698fda93f013a755f7c4c0990d55b4c37d89efe352da93cb83fbd039abc3b6ccc79db99fc5294b5f496df6b40eb83a720f00a0abc43ec32d9ba4c6d12d1f3686b454fa1fdb45bffb332f0c790e32cfb2ec8cec7f124a3b882b086dc571af66d0e766cd1c900d0b9b8dab5c000bd71650bbe3eeac4fb2706d3585aef87eb210b5a888145dedea45cc6ab71ed607443a6a21e1798450b6aadb733dd23bfea8ccf953acc30e16c5a5601cefbec008c6d2f2212879e706274902464fcd25ba7558f3eae26b94864855e53a031c540021391dafbd46b469485d397f4f6bdf7f8f9f217ed09f79245fd57af741cee90f6141935381ce7d6b92c50aab7ebfe848bbd275f17a16876998575487129eb807f51882700b56f162cb94ca3ab246dad56be04d6ff05c4d492922976be28bf30fc4b6eb7371e707c622fbe5820bfa4855cd1901a1847720a6cebbc8a0da338b08db493f6173432cc1adff4d55a7ca09f9be4db6f10b8397887d408e9e6eaa2d02435d33e0d33041e87988f06eb11523afa6222d8c559defdfcdd1cf9a3658340b3261abc4d08d803e254ac31e1cc52dac9570b2f78156bcd8a2229b8881ae5607921d704baf33e124e9da1e82b3debfbc92ffcd91b68a433aa9782a0f5a2c55033fdf8376d957fd308e066ff49461bbe78818b0ca22a89a1c7634206f80a47b05dc0c8f97374a31d3716a76ac6f0ec0a433acf4c1768421338c0f9b86977eb80d7a1fa3c8e5d4588b30d020c78f357b14050adf30eca1b62fc684e50ab0fef906bd3c4aea4caf4bdf8a0aa6ac8ab8637da6fd58bc58def2213f223ebbef60ed00cbb61af502d373c96313cd71dff0c25f046025e2322bb246b07298d640946d031c4c3fded92ca9e97b63ed24a61fc4485d41f99503ccc9a404a9168e4046800aca24ed5c6b2dc4ed2b29e826e66b6f209d26f78d1c9d19a6053df5c4da314b017e084041132bc96b582eb10f4a8c8f51153e74fa254e8ecea59e57d354e85009c10a4c0ee75b67d256e6ed681a5419f0887f2819ddd6dcd1cbc6416a0149dd0bff4db9808c28451662e7f24f76f87ea514e90879e6ed1037bbaf87b285542c037e42b5212d964876db1b9856d1e594e0c081d01934a37bc7b5ab5f999bd4a901079811146918eb45c5420dec19b201fcee8af4686d6a5a67ef0539117632ed07b4e9444fbe3700f5223932afef7f40771003c23976a1766c86a5e9531f249505e41e033cfe2369b04f86a4b63218ea396ed574393f76c0a6bd2a27876fe38e054f78d3fabef5fd3f1dd07b47d332c3521b891eb6f11895bdc89d10908a19ff0bf59166d2a9ef7157d002ddece97d2763a2894e27a5976f4b6c7db82fcdabca0fa2418ff53207671ef2b578a1db6db2ce3d6052ab898d858b96f2211086ca7e085a0e36d46a71831213482f8fc37d06cd6a28093f17f30502c9733c510b56670af649be55ba2dd11a8f411cd19c26814952c864ead414f349d5b65595f8c2e603f1bd02c9623a13b1ed294f8c91bc97df2649ae0931da9544b0948245e1d96508d9abc0dc043514a001f1b924300c19455b047d060864cc490d6e93e1ceb75e0d8ebcceb43d10d801561f8ea90f2392b2033cbd75a1fbd22336013f0465ebb906e59b7a98b8b61f14b82aea2cf6f91d6018f13cc2155fb8ccbc90ffe3cea927063f5e461fccd62400c5614874166bba9d3fdef284a7d4f2bad526723fa8c05c0d512a00105e27d32d61012a8b7ee4111a3b74d9a16573711e0f2ccc646dff380aef427722cea046e514b40d74436c05f191a121ed300bf29c6672e873bd4ef90b21d7585f1db20d2bf7e999d42dbf25e4e2200c7a1d1205543ee8bd3983446d07ae943a4ca1b663c9deefb77507ebd705730d32db113808e9cbaf9aa414fd700af4e54d6ac84981f816b6255e8292884d2095d0fe283fbac5283c211f107ce70fbd12a1b63f3b249ca2c9b0573f2e750865fce07f14f65b8e6bd33df9aa0a6b0577526d63b91fb6b1061c7ab8016e2f77b214583f06d2f5de1404f4d798945801cf2e7e35de62e5d658d48334750f53714695b61ce176c7f7a67b9b3fa86d750591c0421998c69ce9237ccf726488f9f1734da635493b472d2268363b44eb3304f91fd50cdb687a9f31a5f57418c84e40a9748d6f0234fc74f4faec80339cde037ed2bc4953fbd29a1ecfc9a60e25f1d218dafcee9b7753f5dd6fe0f231d0930ea384b470b0473139dec1ab627305d7bed373f94ec58946eabc9135fc12f8c70feb6df221ddfc743e4db05b60eb484da13cb74f0e8e8b1499a40861c16391830a52c7918a777c8362d2fc046ea88ab07a39e34bc8d3045f5594e541f64b52a301450896e7358311c635039cbaae02a6ec528ffb93c41ef228641d70c38337c409090beafbedde8e79bc04b67b91818125ca7b5e0065695d2b247b781aafd15e07f3b227baed57c9452e1cd9dddab46d8a0bf9811a172c4fee936307b3b340f009827297bfb4607ca47515e00d876c7e7bfe3da156011ded770bdd718ed53cf009b3f1461eafb79063088481fdd25db46ebf1689b3015f4b5bf2b4d62d27ef9b0ea993abbcb9c5abbc29f3c682862c7a86970756a058d56110c2cff31342739407cb03dd8cecfabbf23122487b6cff7baff24de7e6603992033877645364cf82034a03e86a9fad14c214fc747212449bec983bb55f7fd28dabb7fb7cbd380ea402104080b3442800a173f762f5bdcd092ad7f121ef1cf5e6c67d9bb5d214f4b808230e9c141c9303ad4ee1ca832472b0780ab425eb6eccb723d6e331e031926c0138e922c74b4f9f0cdd2ef7dd2bb997c2c0e6636350eea7aa021675694a4e3a0f5c6115fab39077cfb2fa06520886a05ce151fa9220fd9ef6562dcd8e5f9db908f41a350172ac6c8f0f52873a67f983e5e5f52d8c07d3e5f329681e36b1190701b217db4e14d0cffae5b8b3967f80dc13707f7665167b93e58f9aaa2c6e485a09a29bf525ffa72fb05b847792a95b03f704897e45ae76849292657641af827205cd8a6bb49a064b14a592efbabe073fe8da513396ef58497fde0a491446f4740331781a6df64bd145486ef305093324202516a0eb08ce7b895b7ec7162b25ab0e24215d6c6c7d2445ba8052280953b86a288ca16c95531e4f6e6e5a569ad80901b61de428146c311e16c566de1202536ffb30b4ac36f58065406d3db3a59e9a03d6ee9835109ef0fe7987845dd3e5ac50d1f673d203a4f69a3f15419d78a4380917c7c3a8478a40ae6d98b0312b185d16b948504fa463bd312a5fb29a4c5fa406d4aad66171b71c116efda93c43e2c345a34463f581f8e85877b3168c7a17470cf7b5ebbc960eff67875b322b9e3f0046134478d0fd76308a60e4423875db440e01539c5a71db4ab4dbc1f1219b92cffe5adf5678dd4598c4574fd791275c110d26e993f75c8bef05e71e33314cad4c037b788b45c3da12574ecd66386a341d08714265ac4cd9ab644d884d76cc0bef7d904f5a58c46792dafd5cc22a9c8a9e00d0cd2a3066995a912a6738e4d1ce09653d6672194ebfa50a93cf7a1d5891d504d31734d114b6463a61147d6e780d60b05ad17231dc475130c7929e5dd4366d0f5217add55bcc70210a8b816dd890a824ffe0ca5f4284a76b9ea5642a4e39e30a8f1602e638402c6929f9dfb4c5d5586a911246b7312452d074b3c59a02634a063a3c7eed78c6c349dfbc0417473be8cb95a11976f358015eef5a1e036b05430ec38972696115944e3d5c5b31329ece434d6d443be88a9fac015291e84692260a9ebd33c9e183100dadca7b76451c31d4286d827aa0be4abbd5dfc72f812fe80f6b886d9d1ed3e1d231f5ad743a10c5d5c2ba48b142ea885d1629e4edf2c94f0878a55b28d7268727a7598db6d3184f4584480018715aaf39cc8c19c7f393f90a5a6487efff93589b4b36019aced42bda34b0a5f109ad8c9a6fb17201c257090bede0ba9fca628369f712eb3e80d1132c39b3e854e5783f80699224a7c2dec009eaa489fd1b692dddf7ac417eb9cdd7138b764cc0b7bd075b65fea073b2ebd1006cd6e3f6091b983ff3f0f57023311b411b843a644926f3f2c3519e2aeb70d2094fd5d05eef05ae6938dbf5b6b722ba07ade6f39c915e38a4e012f43654c19d08e5211fc6d65030dfb21ff0f9e8c95bb689a7a601d2a63306588d31b5e0ad7a01f0a05bacd19fb77dfe57a9241cccb784cb42f1861ca6e7fecf6b9767a812fd08750dc52b9c5f822067860dcb756702ce86e0ac1758e1cf2e5e5117544d8d1308efa70a858bbe8308f42f8a585e333fa25a70971de1752b235b52231667763b0dd9229e538abe0bf30d7cf05cf0d991454ef8461321209f1c3e5c462f352f2e0712e3e609dd7359a7a91b0aee20db249e851a0f475cda61daef225c3a56e1c50e317c82843320359fb85fe525dc936efc92b3af1d02a0298a42f5d2da20219b03 +generate_ring_signature 33bcbd4c5fa68af0c24eec449ea51bb72ef7190c0ece9dffef244eb90d6765da 732fdd3197aa6855d284d6bc4749adbb4c265336b62300984ebcf74d03d31ca1 1 878686a68ba5dd5aa696fd1078dd94d627486bd9c11d6dd328ff0a9b6c091f0e 658be715fa02ba1b7f4eafc02f985fe14c9f7d25f6bb4663ff87ebdc2d63eb00 0 9e33007e5336346e5106d4050e58a43ee43779c369ccaf8885f853d83fe7cb026e18cda48b640f5cf73067b65a3274b33afe0886693a952186f6a104158bb404 +generate_ring_signature 61c98e5693c58afb5c7311d89c4589e4669b52d8ff4571f293ff1f98af9b6224 68535c66978cc85a04be5b88c000167008d36429547ee67230abc78d01b3a07e 2 244b5e8cfbc8ef701d79624bb3499515be5273e45b73fbcfbde5b238121567b6 04cf46394b267c0bc9a5c9e2e5f88b07d59cd5a9bb056f94b46e816db4789be3 f0c934fded787d254b29eac8eea8e249d6217a82e8e9d5bd7565611394277404 0 967aa76d9317162aa629bc41b43b151ce71d2da0cbe8297f422e4cfca5402604dfb8018a2a5d1067d3c508f7f3efe77d249687918f1e6eb4e6c8902e89862f0b24586a542218dfd3218ec750d7e5d233bd231271ca805db28da94b55752662008accf21f16dd7a29fb4c93d510cf33d424b4da8852761f4df5be34b54f6a6802 +generate_ring_signature ccc5dbfe3a1cdf9f9c21948ae48b7cdf305a2054183d05654f6a5b8f1d93b6d0 406a3a1d1b23710ed1555c857e26bb6185a783eef109fbd102728811279bf26f 111 2921b78ba30bd3557e4b772b3d61ea6a16138631c4ba2d8613419171e99e66e3 ad1f075026848ef6bc1680d01e676f8fc292a83df780b49224e9b62c215f6eae f0ce3cecd97138d3819ee595d9c3edebaca5e340afe2f07183373d0dffa0f4be 26eb1a7d2b2c5bb31b7fb377b2523ca36648014dfd16c7f695bed93b718e8733 92523fa28a9a5267ed1801b4d606e0fa5c1b70d54c4634ad0365c6373de04a81 19c3b3475cdfe8c9e801d0be4def7e2d002cd570fb9d9234fca3a0011a8bc5ee 0e9593813368b47bb51b706641310e983b927386a90a00398108e8bb790ce5e1 5d46bdc035314dd0de17318cd46d886dea88c67150375407f34c0039989ecbfb 0e8d745fe8777c695390abcbdc771c9a7038cc327c417f3b754118c65b6d6f99 8b05713d11a953c083ede9778e7e7bbbf4410e35fea2af5b4320b0c7cc4ab789 13761d8cf0742d4b60bbfa4d205c49d4231eabf718ec4234f4036ca168d9d1ec cd2db44254c778fc88a7737e400abcce5d893219d8460c6ab3511b42512657fe b4534bc0527d545be7a0192467837a675c47e94db7ab43ea0773bb968a5740f8 ae5ce2355ada6f7780d89d1f33a249d4dfe7e079b5e102a82314bac3c04c3f6a 3838cd930a369c5458933289ba73002c71134a5e9dbe16c9dca9471987882c26 ff437f8c5906e641d47e53514ff94977417cab286d5f9998c0a7b8177173616d 9abfca55d6be1bc9d37e412d7c35202301060b89e1e700d5514d02d9011aab29 a3232deeb8e763d369ae5c94553aa0e264705dfe24f1fada06a1be1ebe93dfb5 8f51c13bfcf2486557450ea30970171fb0d428c02a823c7f9c704da2402b6594 1791024939f618fe68b0d73e853103b1fcbded7abdb65fa15aef63f55d8b7f44 ebebcd04a35a3eb5c9af95cd67d75289e616092523b8674c659d55209b9b2d61 4702673f7fa85237ae5f20e68b966f4fa9b68c8b6c900316e9f3864a2720b509 bba906384ec35445dd4fdfcd246f38654e8b9d1e3b24911e8c892687d1bff04f 1242ea27621579afd6aae5311988fda8be14600d5fc64352af2ebc4dd383f7d9 7dbe2bf95b8051ea0adaa52744aada889d89e6ad189ea26d8e8c355eb469571a 4cdf70da3f89ee7210375564f8d46aa5fc3a3c47e13866abac7c0d618346a042 dcd45fefa6e3de7ddcd3e105f8888bb6749cb565b2d1fe67bb1b56fef790fc5d 9507267fc1696e5c35cd3718490dc2a853cd2f6a249827f0235d6cb95ecea192 fdd370f6de86d821dd2ea27158eb2ba1a9f6bbe1626485fadd01a80a92a3ded1 575e8265225649995b947201fd76719e51e56f15eee0d5e6462331f69650bc7e c9c8a863860d12415cb601e9d8362990ee855a1e5d1d70cd75e15cc53c07e7e8 dcfe0f0f4119063c29577224bd9a7a1a3800d645e0db7547ea05389f45e73c95 76b4cabbccb35f51dd5f61dc72c717ea336795fc71d7510feba7a84e5beb5fab 0f12bd1eeaa102f5531d9fcb6b79423393fc47bf0e7f753c814f5c273a97ff75 86292224747d0e179c14b7a3e65e4561281b6a827aef6afa2dcb8f8b16571b92 1f9378d5cd626274da52a2b71027b5ee6335871550ffde522ff0a6f49986eaf2 6d5daac0ba917f50cbc0c8502489320d6130cc4a2b332b34556086c5084a5324 9ceb51482de662fe631eb9009212b401fe7138b667b7bd8cf94d9181f955f3ac 958663234c70ff625e10a10779504e267f643aede28861c4a0759eb01fadb1e3 d906f333761bfd3b113903255c1589cafdd20378d85665af168d0aad0367f83a 0635c64549ffedc46dab199ebf89fcc8ac599c8d89970f7133ab5e235e19f832 decccd21d763536ae6d62e9c56019358437f7176b0c6620b51bf59c5f2b0a518 62bfeccab34d00e92ccf718f671b735d07fc475bc5e4fd783e487666b0fff721 adaa55bfbbc3f50b640ccaec02646f4b152fef46d323065efda9bdadaef91ba8 82bfa52412799a07d83541bf6a3de59bc0af127e4310f09fea4d1971af8c6aa5 4c796345e19c94182721175a198a8ad535ec0f547558c7f07cbc3127a1ba0d87 ac9bc2d8ef7f43f80ac9d87142545ec4d14e54f64d4af3aae305bb5407a819fd 934000e2895878f33474fb25b205d5064c181277719336e36e688133a93bfda2 1b3ca7399c0b5508f80a645e083627435056ace47a15f8fe9f9fda75a0b17baa 9e360d75e54b81e3434caf08a8341bfd70abec216f1760388e7d68aebd87e6db ba19f181e552de8ee972884cf82d87abf397949851f9bf867a7bd57656fb8d80 a3d028b2702f532fb6dd99154d310afe882a709f8d0f194a3352f800b855c91d 6c477cc3e4587c389469d20bd69c196613bd50208f91324404ad58201f7549b2 a296d83f65b17869870485296b1df3bb2245aa4245abfad85e826dbd93ca4e21 23bea6d1f3a996575696f69c7b4f4e4b276707eed13cd00cbcc97bcd807b9bf9 5273d520380cd20c951e725147ee7aa942b743b8873b0ad756bdf30851dc2d98 85aca67f1e96a7e3fcf7ea9a64c5eb63dfddf35b692f631f89d179d5d3ecfcbe 84240ec649ba87138abdd7d1524c2b580c95d601395ecfa75ddcefb52fba547f d11fb311986843cef697a42c1db914979eccde0dae94d1fe7ebee7d4974be857 0af06bab640a620fbf4cc5c3a777e209c5421641b8ab610b3c5a2a1a47ae2a0e ba30f319070ffb436f5a5853d00f8013241fb6a6bc472d142f303623d33ad465 34132768b3b2dfb6d97c8eccffa9e4009230eedb6e84ddfed5dafcac3763adc3 c5903c815b8bd030162bbdae429032faca5d73777ce3a8248dd717578f0b8541 adc10a53868cc31d0d81395ff25be93e04212b1f45c00ab5f6eebc4c9b28aaad 1b841fd24b0e7929dabc30e700c6d312a8e3d976c95155a3bb3102fd9e0227b0 56f8165751e6767f607821a630ab9d2132661b4ac81e2f693e5c4f60114ffec2 6865e341bb94717a4de51578360b59cbb07a1660f70b0048d9475d28d7adbe3c 3dca83f1651d518dc458d331c9b9e7c2791ee1019cdf392cd722214e169ff2d3 72099a12ac2a7852c982c2345a5b13d7ee8fd6cfcecdf989430e365dbcd758cc 400f30e5179063981ae05f55129898e12a61dfd6379670fba47f4af1f1abd042 771d8631769dd24075741231cfda6e1bf41d075c4c60af3521546b2f6f2f7da9 1a4ac92b9c6b0ceca0a0914c21934774314b7610eb65d81639971606e2109a13 c6555b566f15c912b0fe560cfa4b5df5dea4e44624b49aa8a6209f9131109e25 944e66c093a3b22b7b64434839dffdfda5b8e0ddb6e93b3fb4e6c0d64ec80ccf 5682de8256dc5f421323d2e18e4fe8e86810eaa87c6d58535dd2c8d29d27d6f2 ac52d43a2c0fe2b23e73c67f6b0db159498140621b13b4bf2fa6311de6cfee8c 24db498c9238003c869f46647b4ec96fab351438cee5fe815ce700f5a83aba83 75ba2f7f38f6bbd6f58b59357fb99a74b46cb4252758fdd4e669d327a28fc693 74d45357edbaedf1f26950c22d83506ab3477bc671e685583418c81aa4c11f4f 61c814f9c7d4066bf95ecfc56a6b60d95eb63b0cdd4c0a45ff4d4f83367aff38 b58e16527d8d0435434e83e64c6ef570cf3717f79f8bb142a3510dce185286cc fb771fda4468b3ea8ee77c23ffa586486cbccd347fcac9fb97960262fc4ea76a a5147a14b7ef157b8288f781275b06f0b785c0888fab930c6a78d94ac818ff89 34557754636e143800091dd6e2a233b47f140010f36eb4ce27b48855b74076ba f95443b3d18f130e37fd03856ec8c6e38ac04dc9d528220481cbb49ed27a9f03 3a6d4cd695f507a0a8a404c02d1e0bd2ac93f4ef2e2056da9ef7a73e70d82f7e 5420f0d5dc5f296057a98a879fbc0d64605806aaad6b574da9a6e2ce9cab60b7 71b23bafb900f95b9d8d0deb139f0cb966ccc1078542b32dceef6849de962e8c 2255481001bf79311d76a4989dd98ff8c7f2660ab70f273b52024560d7d9fd66 2d291106b2157d69bc98c89e4034cfb9988d54b69a413a543074613cb1cf7923 3e38943714f3d3db34f5c12f375e73e99e6947fa661e86065c4041d15408eff8 7c79bb5943468383d769a23b260a0179587e9fbf3224349369bce1e5a49e4869 fe4fbdee077a24b3ac3fabed5c8708f4a5d9f844a8741cd6b2f539932d1a34ff b3fbe8f600c1f5a192bea732df2068edf5ffd6d766b2eba55ab7f6e8a8e4de8e 242b451f824ac319d890214a53a0c84ac7b2555ae80a02cb758343a607bfae93 2dcee51f5cf0cdcae0fca85c195a0f1ff1b3d4e468c9bf8d5dd6979b46bfac09 e35f7bf7fbeb114a5d73bc5450a2e24d171d947268a41c4da4ef322f387a20e2 5247a3383b66dbecec8e74af6fb621f5ae3fd77554c3c3f34f0de68a5802706f d727e6a804a5321ceca5df85777121c501e00cfc1beea4455cf94c68ca3beb8b 64a875b077447051c23ede119427c11392bc495a401d0631da8eb3ddc6af5c79 a1f1595cedc5db42062066166a58c45cf7065af7e52d54c26e16fd98c6defa16 91410d274f804c5ad21393f0f1758a43d0003d604e34fcc9ffd545a3d39b35b7 61ae9820c5041ff102e5232cd43fba22d68bdb5bfee2d997ed97f59edfefee53 d32a713c0ae6079b57526da8b355edd35cedd2ea55b9192e6e2bb97f2fe10119 9057bacde18dd4271192956ae16433cffdb67580478f52fb9e4145658f1fca55 8bae0275e1a2f5b71e2c2beb809e5a98d02b22abb8ade1c5356aeaf8587926bb cbb581f34ad405175e6b591d7f0d68272140a303a4044ad386bcd12489cead56 477283eb5796ec76bbdff973d760fb3a25f5a776d2cbb9f9d3a6e2391064dfb2 2096412af720af30473b8db41ee7c88ae13c2954d7078d28d284aa5bc76ae1f5 2086b5fff76bbc94f0df920c81d101e50708d68f0b18f3b2214c84b137126a8d 68b883a806d347d1ab2efb8d975c12725a68dbd162756d50454408fa12390cbc 427a77fa841f3dea30b971fdd3e26d79a6b1378674651569ce263b68e3654d05 27 c9792a474d8ed94f315117adb8db3de00b852f158492edaa1bf1a9345ce64d06954d455fe19f5736b7fcadfc1b8f44060356e97f3c043cd9053a650db3600e02702e6e2c54decc8c7ff405955cb15fda9628751b81a3e47109b3af13a7126e098dd905b97657689eea887a041a51815abeb2d8a95a655c80d358d10c3efa83011b39c7d8dd6188d040b5b1c507f76f6f4297cbcecb291282a13a72890ec0d60518a9149904268ae6d5b9377405b9d65bec14db2827422d939cacaf30e7015600931d98dfe59a4b676b0a783c1a71e64c5ca7d9f1efc3e4ef3e67f5ba046feb09b017327d692d3519a24464b2faf8854909324b129bf46c2726a12f686e3d2107a6c45522347d38f960a1260147d6bc3c2ce6a7d43b2b81584d091943358e2802a5e2dbac5abb1a7729783873c528d76442c102d32946f86f28bcb538f8cef80d663286613741470885912d1502946fbf9f44fef9fe2f9a983f36cfd16f7b360d28754ba2debba5ef534a566a34373e7830c5c23ec7f63bef8172b0e9d6ac8b0a359a66120df7ef14005b6338f6a7eb9c33104019b592ca048372c324ca3ceb023ae4324c1e52bd475e2a6411c3d117bb21277bdaa719ba24ed8a9a88306a430e26d7c1aec0c3f5e0812f6774265ed99c3ca0cec7976bcfe2ed7998e52dbf320b4536bf38c4b3118a08e5bc312f4df0881e73947c5794835de5708b1e149dd30ce5731cc878081724f896144c4f17a20213f30fe49e20c8d2440e3bb0e8db35063b4f5a3491011032165704f56becfc4c7c01b053ffe8e984d3d6a22eed7c820183b16410989e896f9482997b29b7909cd5d43213175084d9bd40bba761cda303ab5caccd7fac7167b1b2f36ff1d67ebe0949c52e48f851c3e0c1a9fd20706902a5368771f4a3324204c2c1d379b45526bff6e0a21a8a7ddca4f9d4f59bc09a0e3cced0ac95786c93f3fef089a641017292797344347956557642f548ed720d0f768b0d50fb9b6bf723e87dff55e702e6678d07b98f4217dd47cd1e8754da5402ddd7dbef15375f0143030a9f48568fcca9ecb507463db9159b65ffc73937860a586a7918ae4c07958e5f39a4a60f5ab5facc2acb9796775555f22e87a9947e028742ebe38181035551a6ef9cc822a562aade0c24ec78a4502c66720bea45ec0ea4485a61ee9da2a2d151a446e454b4219e84b4e16adfe962adb0c6ded6652e05e8bcdade1c95ce8ede4c77175fa576440a5ab051c75de6a75bcdb5c05bfa560daddd7c2fc2bd8aa8aa65b9bddd6d42bf679754d8b7d27925e7902ec7b1db76059c0da54e82be1c3a2ad50789c84113925717734098e3c2c9faa701392f76c107bd46b9bf99c40cabc36d03612ebec1537d240e0b6932ed021ee97575e98ea506cb439ffd5198266dc0438308bfa88328da606712bdd9e4fc10b91e0ad9a9f40c0974cc094cac423c8c9d974753e707a97cf5ae1e3112ccf4b83ea10cd94a2b076085ae1904701bfee5bc105135d3c5628c9e7692cf3c18312b4b7fee1db10a0dff74f42ea71820bd6e7e9217adc301556b9f4b5d385246bac3f273116b782e0f207b983842766e5fc9ac41720e4fbf2f5c61a6e48a36b0c6dcc5078c88e6ff0b4b0f69a644b6c0b0069d5973a171ede818c3c01a174476de8107772c55d8d80df0b86a14ac2dbe733cf6330a0c7d7548e78ca194b363bb64e2fa1dce1c9321064777cc3b0c65bc8def0c3a11a0fafa4c1ca36379bd35869e85da25d4a7b70707b5b09ab0e27e6bd14be600797eb2d43bf6dcd8d212b8f34f8bae2c949166ae046b36cfe12b3562e55f73cc5d890b5825a46bfe0b9f0ffa5df2e59fe57ac9f10863028b477c9168a75516affebcc52e08356af5157e836ad28f24c3cb38baef0cb87356190a59fe5e973b581febb29ee36644b34ee6c5e660abcf96f15bae58066b2c5021551c52aef8ba74764f78826c77feac6b3999c5d7247d26f01926c70a63211c894eabd7e6eef35773f528639b121864ab30be1e681fcadf1eeb3b330f2951a1e01cdb3a595fd2af1ccf09ca1c3518dcf5171bae0c9a8333db5a086005bae6646960a2f1d1ac25435f2d95dc2bcb5ae9b79c418a23acaa0649dbe4680628d92df7831b5b28ce03b984d429e9486b3c9ceffb045360b2fd8ca3387e300c712263a3c6daeaeb1548a52e581a5078082b9129cd63c23899659ba4bd205400a68c9583f4673bbfa613949b6e7464a4bfb801f546acbe4bd3325f944a316e0e9b6aae590b8f39595b07751f16e21fb4c93d5f3b6f501484e15fbc2134f5a902c082177ea10d152b969837a1d773c83b42a8861142b48cfb889970bbfcd6ec09b01a0d633f3d2426314fc45c3ce964c1ec08792b54bea439a5e91d68b1fd800f5bfd03c3248329d548a51269bd9c68b410926980066e396059ec22501411090d3f988795d400b511d8d1b5f6048b9d3c32c2c4f7cc909414e8acaa281ec15d03fc83f78c89100c35d604bd6a9bc55d542f57596ab9b21646945c24cf6a3e830fc4e61e6a2aa0a944eb65f6f208a35ea6ca146dc0092a2ac90ea9dd47af4b0b079799ce05938c7aeeb8bfffcebea7ad23d2c37a072eed6b5cbcfb55f77ec31803535960213ba8d2fe9a9dfb9c7963f92010789a5d4c45de2487f65d85f28c6d0c38fe761f3b6add1d0799cb918044aa041e545267d5a190c75fdb613fc67f3e0dab57e9f7e7e923f955364dbc9ec0df23a83e362d4bb7f2b687544fa5c4c14901e55441e0777b6515e4604ce3e21490c48a2642cdf94e4c2f1aa5bde5f4f9e903ddfcf2aad0bfcf67cb8f851d30af74ed495f1e6515f2fa89ae363e1ba7b1810c56c1495364922aba32f85deed3d107f083e2773bca35b9b158b78538a75082009349112467d4dff104df5cbe15382b84b82c9b698e94cdaab05603f45f139309b833fa81c9026a4898ede2169b2b962370371d16a4fca576811c4d1eab34a9029b9e2f23a418cee7208e9dc85b6cba50ca3c373c813f0dbb1612d67bda7ed00f511fad92b30ac627de73a83ac15a7389502898af551915c3ec0d0f90c4580c07086085ef82ee35b21656b6f79bc5e41dfbf4d9b12295fe3cf10ee27f7154f004b2204b698347180b69315ca862242b3cfd668ce7a566e65e01e90ed3d18d290d6101d5911a697a6630c1298ecefceda2daec744d9d489cbedc12711a0f6850095b289dfbc65540cbabe34f564bec71a3930d031500af0c08db812ccf2aa55102d3ed80206bcd317205bd52eb8e178bdafa2366f9127f0b5e8250208a20a5520b4ba3d45e68fe65f1569fe8a7802a226db0cc2cea9c39a74e665409a9a21cd90f6696b4a2848e7efc440928f9659c9e214eb428868180401fee2098ee17aabc08785d75db60cbf57cf664fed4dc2483dfa862e9b88fd36fabd9f3ca412d703307f0f12daa2918a798d46f8e3cb37df15d1214ff409340d07992a3ba9b18694a01199650f58e4217c012cdb3a03733d0c6f0c7ff2a90d2d55606f17e2a1e521201e7ce83b87c08746f1097ad7468a8e7b9607c41b0fc9d42204f7139976576b10f1cb5889eb5d358351b98a30db38f38480b0b36b3650c6e0c2249b746ea01eb0fd82ad8ee894f4d14e29c08bfcb115fb314ce63b0319a14c0b3ac58140e30ec0fce3e717c2e2a22676effebb4b2dda1140c9a40bceea3750563d764b9ea470e04bdb8b6772b2ac29ac929d4b6b92d8d16d5477bd6d90b196a1c48314a72f95f0fddb9ca36af3d04cb96ca7c22d0156055724c0dc49f9517d2e65dd1b3c68125024c63791771d94c05cef483c6eac799fa1dd412b347ef47392a20dcb991415003d3260a4e561b2203ad94baf2d654138e162e0d7ade81a93ba7748bc548e4270293ab8fc15ec63d6d9d7b1d80681d1b58fbb36b0ea7f810e8f46f4d7b7b47040aa682fa8c17dc21c4b77425a6c6511d01effbebd4a94bcbba42e7ecfd0ddea50c850eb64e9828100cc57eff447173b770426af2ffdb222e4e8e2c3aaa1029c2066cdea26cc2d7ae3f3f95a407b95385ba9207190d36948814d2d58050f6aebc0f6025774dfbfb328dfe402b3523d67766fec93a061d7e0d9fd7c24d16f62c100bafee7bafbf6d8e9a0557e254eb56fd53fc10691a800b9267bf50ad88280c37049d9c2798b7ed9bca0fb999ea43c307a5f5dc499a1b0a4737572b1c00541c1b0c4c3ae48bfa48a79f54a56bf97fc69bf1ead3296e790ce0d6368a4337dbe757083dd376ee334adf5307e775d4d14872e9acfbb5887ea23d602c175677ad029f07b01b7a5c9cdae1e2efaeae791a6a0d59ae80abfd8270c594eae360ada371bc005f351c69a4f3f41bf0241e61f382e0ed5e08500e36136eed08e9723d7786f308f8d2053ef695e391095a5c2113d8c4b2955797c13d3b49ca93d9392de31e34079aff4edf22fba02e49bd82b667c6e53c6c32d666acb817bbe5ff941065dbca06eef17ff089bbd3e88245b0fe6db5ed4f0f36c20a156687f135b75e05b5403b0c628cecd89f0e682219e900cc157a0b2cd21d9814aa40667c17f23c1439ceba031517d78a9c5acc4a5f41bab12c31d4cef6ac61fb4f4da7347ef67c3da2c2f10722baaace312373fe3e6f0b393de9380478818ff63244dbe32460672c82e928064b536cfbe657f162c7250c47cf15d8a4a4ebb7532c24254b4d06f7b30196180aae8e4c1fc9de4cfef7e764be27be97e80984ef05ba3c71218a0babd22d835305fc7f432eae40a45ee9a426fccd42f5fd816afe46b6e89a2af7eb5f2c21fdcf0af8fb6e9faa6f8b45e249ae77e70b4c1f4a114cc80fad5ee6ce1606787ce1bc0c70d2b4cf9afb1ab48c58a38a3bfdf886b95ed1f0a9fac214398eb5b863d6f401f58ea3c5b267a5087904b03c87c511f7c477c226d3670f6b877e632b3306280564c7457f15d32124193c6f91836e38f1e51e09c670ff4169abf0f962eca96a0a809ef331f2813cc51cf5d5f601acc4a491fa910a788c778d69425db9a6797c0a4590241f68bdc0e2dd9a677e89a2cfd65b094ecf9952474ce9c83ce58bd0290f60ff3d728c1513869de646dec36b863be4d1b6a0608c9b46e304818fa2a44d0d9dd156209a299b840933229b15374ae08c74105694c78e515ad6adbd1b459206b538613fd322752d9de3fadd52e39a87e6a8e48803a1e858c6217841b37197005ec2b50b1ee8a78294d6a938a8a3fd35696e086840b8e2f85b388951068a6501e00d845f383bc3650533041ef0242dc73ceb8bb6c22df4d0af63ab4746f4cb0f582e8fc3a3acb2fe86b18b26efda5ef34a7bb4364195fdcc2673112884f11a094c8ea5970cd1f9032b1d8dc7e1291c4dbcabf3237f8e4aa126f84f7b1d9ad500fb96369348a58f365688a763bde285aa67c4955367f024c841473461da445409c6cf4f167a2122528b68a02ead545186307e435c0b06db0c6a88da8a1a940b0696eeb1a2127c4aa1edfc86af82fc3fcaf9922ecd7e62b9a1524a732022c7d00cd5f53a678f1b8470981137145014568dbf4c141e5ff8e5e6671c2591f681730a394ee2aabc2f7248071870aabb4b8206d46311f0d0eb2bc81fda4998441b64013366ca32536a7df7d5d74dbf0f65116fb133fb29d5a5ac2db6ed501f1ffc5c0ad32cf244233a1ece6de45020cd38b0f8927dfc7fd909ee440c7dc1476f4732015ffe4201bfb80a505df9443164ee829833bb94acc64610f3d441d8d000626004379b94ba7124bbd1bf88171116263feaa514d1bd31446413212e2bb40b2731036106f4a6d14472304a134a022ffff9dc6c5153793ee7ef7adb46e35f99f1a3015ec639e975fb52acf24846e0b4b8852b2976ef046cb139f1fdcd337530c6250173163a19303d101a9198a5e86161fa34ad747a519984620eaf11d24edc6cc8089b1565affdc9ed903823c9692c1bb4cf60eeba36b39a49d00924f8c670e4190e398383539ca06358d0de2a2158f4c5745d09b4308bc89caac1448c512b2064090efcd0c8c7afca41d5c8fe5442154229c53753ea3e333b660c94457da36330081f41b0bece83a0f684c969cc905642e1628ff7e0ec1847328a1c3efdff3efe05f11b6a12c0fd6c1990816d9621f2c533b16ac2474e1eeaa87b8477c4a56c9b040feeaab08331e5be5e6c1f7ddde6baf7a6a50f9e37fceb04bf2bd588e79f3e01a9bb1016e88a66d44b0e38247ebf5fb1a23377c5b4927a8853fecf4fc3e3fa09177949bdf418f69fb761ef79f7a919f294ae06c0dd6895abfe7e6943972645015c06402483a5aeeceb05d7a36eb53e1bb9e818a39c49996c3e0e3af16d477e006cadfb03e5f25eee3ef1ae1da9175c09866b35832283067d98783f222478f30825f59b188d0f69f916946f8614ea06de4a2861a0eebf6b29b5046eb8a0751807077f9d1e896df8d0bf9445172113feb82295136046d9750a464bb808fe13470693b4137b5cdaca5bcfb07f9f0b77f05bfc9643ccd597c08ef4d462baec2d8f02859db7248167550fe484dbc955a4695080996a4901802f49714c956b6a68950e09fefba153447b8119f473570430c9ce4f3e527f3a830b602dbb00d75cfaba019b88177f871bae33931ee3b26c1d94c73eddce9fd9dc9f90436aef8b1eee2e06d126ba313a3e75103013972b2e1e30e6ed3ddcbe0d37479b2000e6126b70a402abf09b5745233f3966c8ce0bfa01551e1b3c2d4c6b82040e9a058202b678a20bc1d96941ffb83b725fcc0b0d1f2dde1f5011007d47ab72376c300a872cc7280fc0e187bb5d9362e2481c03ad1da3e92f7edfc43f17301a7ba9105c42d2154904c91bfb95cca5d801dd9dd626c6163a936250f8704d36d7c94b84b46e2ad2b30ecb58e100d75b274d322f0057e9a48934e581f2e3702c6bc1e619b783d6e81d036cd4b2c4612c6d02927220af8014c185dc4250b1a3805e6ec7a9d3200ced7206478682d3cb52ee69193aa3d5fa750220dbd33d354899a848d562f6926b28910e9f790ca308a6b4d4c1a9d4eb0284318fca65eab48c13bfb8367a19719b7b9d0ed19f3de017fd75d0a8a73fba37cde8a46d7d1d4339aa4300d88f03129dc9c109e3d6696a7236925be8753c126aba9a65c03ae9da6ca19b8f35f1d95f4db28f0cccc61a33c4622e28e7a052a62847d9ea5b5d18c7fc2c9164e7ecc25f589f690bca6b103184be4a0bde9e0ac32bd57096ed602875a17e6a22cdcb910674f04008cad41d6980c53e5858c1fd4796ca7ad8d84aac462aaadab2408695b2a8b539085b20de2ca4968a85b8fc46f3e1a026f7c97b4c0c0e0d7a9d4af11a69d1cb260af5e292201b60955c328a7ddc74e2a3e31f5577864756636b85906e723042c2007ee2033612b209af788a359f96a0c6f88f8e391f6457caca778105fbd5c37c07f90fba02f89ff93c29aaca3740a8a97ba9f43ca764e9fb3a409215b5a708ec00449080bae2cb785e63dc3036178478fdaabf22599d65c6a3fb390aa22e285c09f219bc2c37c058373f37c9b8cd9ce7434759878b1ac4ba5defd4d185453c27002950769793484dd1726257176e110f3f6e86850e267015776a64c008668c52030de7ae99aa19826f71717fde57bb3b3e0b41872db27d215af9690093f945180a97340a943f4b60f358033438242d34a5f36fb82dca1bb9d73538f16123a3390eb524c35ccd3bcefea87838b9a983270b895a03f4f9371533df638e30d361ce07e6d1ee021f9976729f371ff08531b0787b89802fff05854e48761537f6db060c6f6e3e80783a3a5a8e3070319c7804eff9b99a47442dfec29f0185019831890abdb8962e60fa00ded0bc44bd838f595120d29dbb886083db7152938caa91da08fee5d2cfb40dd9a44b35e91fdbe204057d5e44e7cfb5ab62e8eac5483826ad0dd396f62b55de1a0c7585aeb79b95d9369d1db4aa71bc662a50736e1d5963720568bda234b54e68d25004f7fbbf7b55193e7f1ad3f2039c0dc194a977d0cd810a69515f1449c27a48b4406d624d63056ff514e759a55bfdd11041aee06cc5fc007262d75f700059177824fac664911899017c2eaa33e7349cd4b2e6d5b3d2ff06fa9c3b4d70288cb78ae3ce6f949fdb4aa4e55d08673e93f1c01ae69ceed3a20209050e37a9d62239c0cb772736084b6848ab0b151fbe15944345755fd8ee33005b7361ce325f1102640a3c2febaeb854741517126cb93574b61d0bb23939090e016b8da7878857e1c3e0fdfcc2eeec5f5b4ec67ee5e34c4c809acdbc9f6fea047ff1688c45416e3e57e9ffd984c15eda5d935bbca1b3fa79aa84debe2b60290122997dcd973bbba4ec74ef5f4d1bc96993bd23776a5da08e9bb8082361a464023a8a4263b83ca52a960c833fda184b3dacd111c44316ef4a8aec095919e7ea041e68081fc7606cbf2a91e0463b94356ae15202892c48bac0e0fa03e5e5437b0dd7d9a116fc9e4016996f4badca5af1b5e22712dd2d9af083c8f72fe746905d0a4dd0d54f26d73da6d941bf9874ab21e6ee408ce8afdf8e309ab0e711a1279c02ee67b0f76326ad7c61dce46e4e665852b8512c77f6c4549edf6e3fbfcf670c0495f72de17833715b80d8f94f999c5d828298373524620ae4fb7c005e992ff602665f38b113729a5d35b91f7eba475c4e8edc01a526d4ad2b6e55c45a8daf240fa53dc91697f479f49d8725bb16d25979247ea9e249ef22d814172264eedf8a01b775a40c27a551f95ee229f153449214f193b4c46fd2cbae881fda2733fa2805c48a2e2aff9dafb8a32fc33171ddcd876d155832b32fc5bc009415503d4d930f0cc0001c7c93eda209c95d2d3460f02b200cb5a7e31778f9750f3bc6eb24930e19769b93c25152cb2e572e294f514c44eb67b42233fbbc1ecc0cc3709faa0b0861ab09f3e8b9f8d7faf9d42d80d5651bf74871f5edf4d5e48a85cb384ebba400d6de67f0926079f56ccd18d87487065cb4b7261b56d8012e1350b8ba4870fc0a1b61fcce38e9b10571b176b381176128a73e20206203d191cd15723008f00005cd318b58658615d6f34aa6999ff88de209944ec1d0a3466fcdd6f804d5ab0800003f4a6280aae95d97cbe9114a4c47d2615e611344be4052ee933bdfb4d2c50bb06f17858bb352e8f01d23b955532135e62a98876195551a44d42a92fd18030f108d1620139d0bf80cbb174fa5662dcb17131b88c230ed7d269c69a7e45e55096f0c88d4e5c83c4490402e00724c74fdd9edcf799190147e6711b8e9aaaec10b46e247353ba07e6d042b42acb6a46e0ec0846a87eadc9d53e4a72ec24c7fe00ac48e12afbfc4d3b96684025674e85253d88ec4ba7dc66a08d5f263fb290e2800de617a3f0d059608cd803c6097e272e726a348be174bac91d8c777a47c6a8e0f45715e79cf92e407f433a47df0bfcc17cfd1a9f4ceefe94ee4d09940ce7f5e0f2d05a5ed85876d3c62b21685e955c5a69643450725deb39462f63c6c64646708c61aa914fa2e95ca21c9364965a11f19f7fa44ce230bf591fd6b84ea91aa230b45c249fa3d6e7fa4ed826b54ba82dcb8e1b4006871d0bebcb626abde26bcaf04ac9af0a4167299c0a5b49f5f273aa947a06bb6aa492f910c6d807f07940889042322f819555f4d177835bfe6453d464b321501833e5b81169569a6ecbfc4630477d071de15c128b7334680234e608976847249eb38eb5d4530347d0f10abac0ea677e2125bcfc92258a8c8cbab325124d23a38a4bfd8431d595fe546e09c0202a14d7cb39b3b84af9cedf731ffa42f321805bacf31abfcc5235133a0d199c50e7d8905937c6146f8d5b9ea80d5e82e4aa3065fa33945fca1d496d9a95003010dfcf59bf5da37320935a71655427b8b175e1145b0771e6c8ee78c13b07ecc8906406c9f13c18a6fae156e901dcc5f160a5ca480a64ee4920a224f455dd5f5c50ae38c644e51909c5f101ab5b3c4916e52b527f82545e7fb27ce98822cfade59055a76f2642c986674292d35b911208374a76e5f4509735c372e2fb28a758ac708 +generate_ring_signature 15e1e8785c6672c8c9bd42e8b0b9dfa73ed16b7312e067bb23380a5662884e13 df79782b5900dfe6d529fffee15fbbc1768ed0a58b535232124ffcc89073fb56 1 5f5eedb18339d1ea19675e00cf95acdcfbfb71c56a86c51c473752b1a6eddf22 d2494b222ddf6a368339062b29abe06a0fffd651ca408844a13e8e9c1483c501 0 f486ea9525cbe473ccb7d36b4f39c98da0d5a11435a4db701277e89205637a08c548a2a5a40f8e878c48c1fb84fc975aee4f3550eec44c75795ac4c9f1bed308 +generate_ring_signature ca505427f45e3f3327ca3206a0b5da1292e0cd7361f6a625dd9345ea0d03da47 9fd40e7e4bc7f5c892c8000666b514a73ee6603def5babca6b51285c40f27309 2 839e8769b9dd788743f455e74f7253e687cca012d0ddad7cca0e4767e4bc79a5 72e824806950403597e8dd402e2913d458aa49d39982318165e32e5de46044ae 76e8413823935c97bcb8c388d61482247026874a53b8d31daa616b6f244d3c0b 1 47f5bb2b5512a29d55cce31fdd206818b0b81530754b6c8d38c5a14b10a41108254bcaedbfdcba04fd66805b030c12d6adef005219aeeb251c4b0d422821ef03bdaa824b0c8269586c156aac66fc1503a72594d88c366239bcdac96dad3cf900aac2f1a5027224bddc6cb26095841485a9ea8ded51f57f89d337ad328fdc500c +generate_ring_signature 640933f68d149a459fd5d65fa800ed1bc733ccbecf94b17a8a6678dc6e90e3d0 7b3e6f1907ad536d7bb54b41827c3d9483da8e87b5041faf6cc7fd235237f3a7 199 e1fd6f2f9381f01f62f9027760c178dd9f5716898395fa150bd04f9c1bcd7257 0c32219292a9a10f7902af43cb0b0ac820d2618dd2efd266e36d6f7bf375b546 44bb2452da68ccbad071ceddc6bfaa5a7a0471e67235b5db25ae0c401c848ba7 ef310ce28bfbb3868d079d856042448a1890589fd919796efcf0958a6fcdd49a 6138d4ccf0a22b094bf5f6f4bb25431c73194d4cd455225e9f90e890ecc258f8 6cd95891a2d29e19eb1f2368ca8c5bb0e17765269ebe249c2c9d6e4cfec2e701 518bad14481f59ca818908ca5290b892474cb9da9ae78dcb4684df6758a12fe6 486ef59a6090636d737f22e6f3c6786d600d7dd50084fd5a6802f4d1c14aa8fd 7b7ba8bfca6105c550f82244e8dc88a3d14f2573721cbca36bd2abeb5dda3361 6a8ab23a0f9a41e4f83b815e83295d23f704b0a46bd7787e017fb003111952c6 6e629c6afebae95df966e02ff8ca6589152dc2f1a8d79ea4ec806578a861bfe7 bc0df98eab5b4834f520506a48092bccb07099a8a57ab0d05d73a1b644ce5a9c 1bf49181176e9be00603b8697bd817908ffcaa7f5b6d3483e4a20f86a14ab144 2ee400bd275960a00316ecc4f4d62b0c07c77b5b39c9ba35aa21183e01ce7576 643c7057cf4f13239a05fb4daa429764b6c4812e6b31ff31990182c486537fa8 72c592d097ea1d533270ab38045068d9f8d3de716633a16cedbfa5de6ef1d94e 397c51ac125fa63b5872e6ab21da9a04edb4769975f0eb50fec6022b4163ad79 0ef1c332ec45f12caf7434918b5df896d7ff57dd0c5bfd517a99d1e45279dbd7 b85a252d88a0f3c910836ebbc1917a5fff542a8b6e193fb17de53aa7806a9df2 faa62a1b7452db23c98f0e36f6a9978640636d2a9a0f16713e5f10c09fe3e508 328f24953c88403511a3544cda71c6011fe7dfb9d56d3f24f0ff21368eac61b6 df1d055e26e6a5ef462e906fc0deb64d8b29598bc9bd85b5458082089bc96ddd 6c212c22396b7d5581bd5fb81bbb666ce3a184aec90d58d5e44ba1de5cde5bdb 4ce80f8e5c3097fd18e3bdaf8ba2c9319d2c038fe2133cb567699bf8ba797e32 da98eb0d9f2c7b7f966f5160a318169c9ac34176486af194cfbcb3d72860fde7 4660b2e8c795df61adb43ae7c80fd13d4f3291a55af278dbd315450a0c729656 b2847cd4e5196c29d705c1117de6dd6e517140346ec2331f88fff66555b453f8 74ae53ec473006db85d690d8241a46b390c62b66038ab2cc19fbb9557ac410f7 60b875aa3122e4bf0b8404ea36c488f0dda20ff1b4a4f3a23d13b03e1224456c 781f2e0c5693d7ffe37ea904f72274a969f3ca5362dff6587bd7731e3c8af2a1 2eedc4786cced370cc96010b5be504e587357a9fec120022c6f417874e885aab 57f3046490762d405a2cf33b0e42cc077f962b4762cad12d73acfc7812e53752 95d804b52fdee15815250d2de834ef304b6097fc5492370c2e2377290094ec84 91e241d7a90145bf0320818a46cee8638b8000afcf8a79c4573b9d63f1aa75be e5c1a8968de7e4e022a29419db34f969f0a32cad3262f609633f6fb7078f3836 da909ec5404bc643cdf16d774275fecf796a3f114c333dfe55b5b5f3cb605571 3977d5e358dffea1fb052a1cc7aea970ca6f1d5caba77633c8a019c099fd5c5a f4fc33716b9d5a9aaec8707fdc0ce658cd2f45b389c8d0e5cfe68bd986999389 816a38588fb04069185b53c8e7a152992387822555e6a42678a3aa09ed256b2f 73c235791b37af8bc5d20251695df714e2001301c2c42e6f4f0f6933b68b0b3c 37a1f5ffb8877b39e7f07c497b079918e528943284a86ce6ae4bbf92111c63ab 81127e3f9e51799349ca316fbbfc66b9615b81e34c1da957a2523cbede998e5f d022b7db7aab2fa830ca163e9db126143615a14d00784d029c1ee583b95d4ffa 518e4d4eb6c823e6ab52675ec57090510eb1cc5171e21ad531e97c1ebd9c84f4 bf18ff3b5f363af34722d8703b4ab8f6c20a5a5c00e70b29b4a0e2011248d830 09dd17995fba7f2bfcaa29dfefc5670acb38139f95c015604150699ede968e11 a10518e114803f176d8cac931c689a5b9aed0719fed4f3cf724121f2396e2bf2 840cb0975d20cf39161e599dffafd50389930787e3c9f9d5422818074490d30c e079333d9d7a6e99f86881d2383aa810fd16b12bea9762f98a3420f14fdddcc2 2d966bd97f8f3942e1943ece7b23d8125c4ba8899ce0f9f46784a2d1b38e4d7c d65bafd2b7fa8030414db8d167121da88d4e623e43aaf7aafed24ee837069785 672b6a10e067b982811c24a5e83cc2d70e92d45c2de5b499f906a8a9d695bb6d 508d519efbc059adc7eefe047c8eda930330c27d83fad61c2a41b502f07d4e64 239b57469cf421c31e765b9891a5d1a42251bc9e62c5dd55be426c500f5d7e41 bdf54bdc32858ebd44760b9afb27ec803fc6ccce43f5cb72290456c31a120312 4b42e3bf898e45da9dc4c59408e1f5aaf40bf11b8a4d09ccc66b4d626784fa04 0ac47f6b42bda3688fb69a48aa126d607e26ac91a443b2d6fc7669bc52a8dc2f 940f30149014968e9cc01fec132785bb6990f01ac0b7bc1ac97b3b5eb6256183 f81895b0ba67d9850828ccba479bec869c188f2c2d254b5612d627ef719a88a7 854708c1da30f9f98cb05ca5b8b8573321d4f729a7344921a9f376bef75c9e51 8e34b500038eae8d855ed7b80b3a81c05d6664958cda88e2518bc9e7eddce863 48fb26a2d6987b3b5dab2991b90e8a149dac0dc5021b198d2dac0a5900adef51 ff8ffee9e4ce4056d5bcefd56d6b06b481b72f96430ae41fe3320e165d122d7a 0f35e5b2d3736b6a9be73a6403c6ba0f98a938cb6e06be3f700bacf83e2540b3 07334dca089b23c1d1cd601e69b5d64c1af0347861f3754274d9b556ac9aacd0 e02f32c3a031698d5b6eb871798306b9f1f083947ef081e37e7f58226b6d09b8 6a0ce596efab1e09e24f83c816a2910c0f18d054a914fa059c41b1ea4c78f6b2 57f619f188c0a869d043f4c41cf43236c8e1ce80f12b6168233a6f452b667392 417484f00663dcaffd323823225e1cd0cf76ad7df724db16cce584492173e15e ea96e9c65d7759c4a0a9be30e0ad102f4dd0f47ab4dc75af9a8fcac816528d5c c01db326db562a48369f862a6057bf152e12154b9b71233f0b21b80462f56ef1 32050cf1fe635c0df1b584a947d370f1274e290fffc192dcd6126203518e47ce e6f2f9c2565ed7a334c0ca91efe58820010aa1ec1241e1b8827b23935f6895cb bc851af7e8ee6501beb8b4d03a7f0062442d6033ba37d5ac2f25144f7d01aa45 59cf88f92e93c57c35d98a382be18f17c24df3d41d8c8db2a91e6d5ebbb42e12 31f592ae1cae0de2e4f399339798f72f287afe968e9bc56aabc323a9ca0dd278 8ee9c8c5b1d3f378199fc47099e415fcd802651e208afece1284d94b5f45ce3d 508b5b43b6344dcd99d4c13df8d80f02055f094ff3e9d7f6db3447e01bc441a4 74872c9591414526444e50e95e0006a77b98c41b70c56a647772160c77603d7e 21302e5c387885ce1359e32b0988d0c3c0b645002d361994ee2031a1c9c06834 9bdf64b862161a2556140305b68339c517f6f54a247f96f238d45e1834596414 1693c3cd7e03df5737e6f206435972fa8c100d68e5d55b1efc6f26ae85e16942 1aa6e0e8cc7840effa32f20a21c92b48cf8a676f69e3465cd1594dbe800e798d 97598a6b0042768ffcd605725132c323250013d9ffdd4cbc46c71995803fd6a9 be9c2ff2e3b3e788f89547a687a2d8990d51c1cc70be9715840030715f14bb6c ad23094b33c609916ca52ccb48bfc68cfceb423ed2c230b4230d34409b1701ee 98a4bed71cc89baf62675cf7a8617177d6ce4f7c62014274ca9a574e974e7a26 c71c798ea3ec9ebf1f59b949e539a67aee11b8def522f6ed695f433b71b159f1 b298b7f06fe863b3ae360a12e6cca2d5ad1d105ae139fc01325be21f78d1aa70 57932261b773012ce60c5e26bd7a2a87fd66d47f0cc3b94437fea1cbe0b4145f ff35cb6db2c5ebdd155010503ff566ed6d1f5b1c9a36626ade48d76254250d3e dce0046a37851c5026c7a4ed303d870e9766d84a9e6d4e5976dcdcd60f34d4bc 6d37a87c1f685b27af20795e89487f790697b5c99b97dd0a19ef0711a1ac3733 f7266a5cf5b3f7ef28fb4e2428c5547c670a056b110bcb9188ac0a4452e39c25 a2b19116391e6ec0526b4af1f0fb04508fcd4306c91c7124196162424f6191cf cb1cb1c1dca53fa0e7b104127ab69699cc55aa750a9474b9517fb375faee3862 797b0ca02fed4c418405d88223eb001882e2665c0e631dea72205db3249a9903 104956bad09c091477f9906ed41f63490a62fe6e81898b8bc7d2044c02e7a620 74773d104bc903adb07c4e8bad78bbcdb44b5eea9bb052aff7ff96daa8f97365 8787c10d273fc7c43d5b055cbb631cd6fa24068ae2ba576049e029a174527222 6b13581a144e59149e402649568811cfe67d363c2466a272c2503861219156d2 61eb9b1780fc73fab757095aa7731b0a5da2c813cc36d1e607363a37fef27d0d 7672f3451e80b4fdbb01dc2efc655ac562defd71fe7e9c785f8e2d43b7ca09a8 2402e629ec86218046a59177c682f07d298a2327e203b9ba29aa3a7cda2a905f 2862572827a76baea9b113cd3057dae5021169ffaed7353a8e8a150d99914941 95eeb5f30313df74b524624cb8ca9397c97814698fd503eb80a915325d4bfd65 02d6feb3e2e024540b00f37b5b2198d2ff05d1398e1d1c1f3b73399befa20a15 5b52a566cbf14cd15d44df91d26109ffd242010a5bba7692d8b4e180e79aacf3 a713f0c2e91fb5e99e2974efa91de6fc0609c64a0e0cd2bf849cc0ee7fa3fd36 73ba4abac7c1325dd63f832e34dee41d4189d90e64f775f904bcce4bb3db793c 0a7857cd3c77a4c8449abea91b3d424fc8f7b1a39417efdf5f1b85827e65621c 0bf93c473af049f8496ccef2672503d3c75092f1dafcb7da8a60b7c6bbb1386a 5487792ffa3566c4c3c551abda71b6d5b461add6fd81c2db459017af4d3686c1 38bda5d44463d0841416199cd4c7b5a924b1965a77ece07a069763893a9cb75d cdd3fdaa97260218c247273eb293d4e90e4b0a0a2bfa1f72302fb98e1249fbf4 cddd8cb104fed1e429b08285ae0b471d5d0bf74fd1606887171f7176bc9e1253 eb84cafe4857855499e373ef983a2143e1b1d6155e94a3407e2e85f164ba421c 46f8579c8618a783fa34d0d6768d7dce4c4460b1ed21a24d4b48117ab148ec3c f8443fb9597919df3d9caba2a9836b1a3076b78675facdb890a574090c94cced d297d85af7014563f80c4f1a0d1886f17af1a43de53b34c449d660e35d08eaf6 1855741e4cb18127641e4d80db0e585c6f3d357eaa5ab9cf134e52f3023bc2cd b3eb8bb92cbbb2248a347b7d439d6e7deba3e11c0f491dc8dda1234d33ff7e59 48eac61ceea14de3a054bd98e687cdb860ac9dad6f3aa5732c1e2b8c2deb8c43 05a7638c5fe710f66a02e55b4cebb837de550a5ce2ebaf2f036b371f366baf47 6781eff113890f0dbe3421870aaf39fff6f64a9773208450e3c6cc5180fc0446 c52ef31914ef680e9940a5d04d6faa9d5a3bde6f30e4392a9614b4586ea11fca de390cf19cc66beb0772ceb5713fc9d06233071045499e7baa08f0aa398ce9ca 50a4b637e93b15704cb72105fc9e0a8c08e02d4bd386421aa42df597cf32cbb0 5e5cf1bede2ca83f83b04c7a3ceb54283e769734431060946b4092a6e3f1043a 90107a3e5bd79cf0c9d14f7c41afad86e1564a2bc87b96b78f64448278dc83d9 5c12fd730cfd26bc35831b953ef6c88ba99c02f2ed682fcdc5a7b45eb2995e4b 6a5534816c314480e918bdc6ace3924ec8b3ab9f7aabac1e46931df705d0d6b3 d33821f1f4480e39146c5d22c5f514d12de2acac4ffa1add24e94d0aef01e3b5 0ccfdf97d8f4d1c3bbc405a6c0a05cb10c84e68a9e828e91bc95613184285887 d8a28eda02b6054603560444fb09c586c851f6d7dc9cdbd62b627f04ffb1d125 91d7a87a8f597ccdb99c8a1fe05cc9c025aa267c63a8512f4ad9ede0f598817f 095efc60133065f2367438149bb8fe64be7a8e2ee6f0b6d66e7c1d44546863d3 096b3016663a9ba25eae4b5420d5dfd2752753526b5e300e6632a52f581246ff ce0f9ef751a001b0ea6c95c8f93f6065bf242b4ae69d8f0300243e223023235d ce21a3b1d7cc54cbee8c3b2042e2251d2476cd0e2f73119ecbee806492eab3dd 93976f4fe773f35761b0a3d56b5f260358c7b04960d6b01342c13319e11c1ffa 09c8b32547300288d6e9c3ecaeaba6dd15c7fa037a565b201c961f7aee9ef1c1 decb1e86aa6a956e687aeccc633c688d76fa9d76ca8d7b99961b1591b4c3acec 530080918050c0f671d21b63e4cce47b639790caf570a32c88700998b61f77d0 1cfe24c75253c93e025d78117ce0c2a1571e2e89e1687a28220c222b4582718c c4ff26336a238471bef959ffa39222ca67bb4eceaff89a104dbf99671b92d7d2 5a306e5353c2f33d596fe151c616ed716d9bfd68237038714b53dbeee2185f0d 7c0699c7e93eef0f2cbdec06332b23fd9804512d7b9a709790cad1291ee62329 07b843fbe8bb4882fd4bab8c042dc86a597985fe6c031bbaaf14a1ea7b1d2a8f ddcf47a01055394119fbfc49ba1dbe50eaa73f3cf19cf8b60a96326333bd13b9 6884f9ea3a3c4684547f6f8613d1087ce35d5b14dac078dcb27d63a5c3a7c626 c965f6b81f78472b1730b1444d9ec15c9fb6b54918b7b3f4993ea21c9610bd34 bc587636da4ec349e715ef7cedf3ede92d7a3cb0905b2dd35046f01dd12ffa6e 901f65b107e0217ce7569b32cf50ad1cfb72834ab06803f5b53db3464c6f05dc 51d7b3ee2af84871f009a8caddb325823200ae4462e88f3bc9c94b9bff00459d d85bf76e475ef81747d3eda905cce4fa6e876605e102b9b2e8f72cda9153c8b1 9d9a63453c0525302a0d2e7737449abd52163c08f24c1e37f15a527d6be68c95 b6e86edd5fb169911f22137f1111a253fe2bf493cfc989dda1f9a880f3d5ef9f 7d072839af8950bbaf6313a4cadc4a04f152f16ba3e84ca932d1445f9a34afc2 177f7508e693dd881969727b67446fa7eb9a47c26e826d43bd3a6e0ab5834bf2 901823191b933476f034f77d166cc5bda91b0c41daa76c781bf098e13983885b 922f40a9eab5b333199265429c4fcf25f459e09ebdc63d5e077bf733dc7412f2 319fa49be35d561499b802e63d8e2c61849b7639c623785266bf3d6dad7e3b18 07cc2c6e39549e7f76fdd89aa4a9becd09d31822781a94839007720107462159 dee6471f5a489e8ebe6ccc9b1e2b16a2f6d7df7919372b2115498781ff90efb6 29ec1cedd8d6ef03d011b35c17debfb9d4b202d964164928daafc80c66c0de08 9fa044d016d3ae76f4479d8831f4005efcc6daafcc78b0bb8cc8b65f63c8e9a5 768c5cc4a18fea67f416195fe7e1af269ad1829cb8861e19afa6384239e6894e 914752c736372d835b803db11dcd48156a4123895095f124b39279da0dd5236f cbeee34f7cef744beee1fda22bff75f4423bcf170e5abb20fc7bea38cea001e2 9023bf6f93a11c44043ce93f694351453c492f86feb6b44928d253473652f5a4 124a47bc0b26b26c43f6f23a65fc74d995bbd282e1044f56298b54b599c2cbb5 5c2774eaadbdb04a732dbcf9c23c77c335bdbcf686369e6e8223cb0274253508 f46bdd8fb828d1177c62dba969af55e292901774759d2c79cf8a5f1a57acbb9d f2af226a6a889ddc46eace4b559a7ce081395056ea030e1c0f4547b539c4b0a3 faf7b038e862ac015e8190049ff5cbde4f6b437adff070df511866e804ba4959 827f334cb6e5fbb142890c745e63905e0f0778d2a170369adb64658014ecb0ed 7b43a29455e7bf9bb18c406d8220c9dd42e737967e159697f38fd77174949c27 cb633ae5fd91d1769d88557b5a428183ef703293f5065b578e9fe3ff3b1bdf15 fc811147b7c40d424c287edbebaf6d8e7242d285103d1b6f60487287afb4d848 32ef999686ffbb7655c4aed56d167d2d7292f711448e8847a1a05e55e6424735 86389e37f7bc902081321af80c748af26665076e553bad8bdffae84b5cac4466 39033a1d13f762335650c32db88aa2c19a128014946330e09a0a03ebfb06f604 f47a687b06ac083c8511527bb3dcc57b8483e295ef2f24b3e84280e739f7baac f8451f5f9935a8713f1eccb30d1800915f83e0736b8c71c62cd34720c75dc7b5 227ac01feefc3da4c2e7b452e1cc37b20e7b5f4effe6061ecde47332385355aa ced1f5d61e7b24c1f65297ed6a611d92f233473862ad54d1a7134007ffc35e03 cda1925c58a914d8b11ebc4d2668340bd55565aff5c118f8a707141a4313af09 29932f04e23bf8c4e235571f2ec07cb384da86c855d2e3b45fec4051a6bda5b0 3fe044639d70fc0e5e6757db4e45df0af07385c3a25aa4dc8294a477bf39be08 bd398f48990e8183d93117a6162841d3df3eb78811f8a84e101e5931cbca754a 52b8ec2637c8327591ce0f41a81f77603ca5d508353486a5b380f3c495ac2b2b 2b6fac9a5788ac550314d5ef4a85d855ca12f866dcc8e55f63b7898ee77c1c17 880a74bd63ec12ffbadc16c57915d3098476cdf5b25c4e8cff1de4b35009b7f8 96bb3115b7d40842c7ea3631bfe35184e2d16af4261bd3fba60c2913e4911c9e b3d442f2d897e782bcab66f0a1de170fa5171f588761a3ed7f80a04f59644abf 4da03bce5db4b41ef54d3a5dfb5140fb1794332c20c2cc80e4184549ec38ff68 cd6abefacad45eaa7c9e295c08857910943d9b0e6945ba77199ad93b5668eda3 6eb4e11f2e62cfa6164dc0ae1ada61ebaa6949a07cd88303c633835fa98c1e53 e4705bda82e65f57781a596d1e54871dc587e1a614b77eab59911c1721d0090d 34 eab35b0eb7faac20a8c03d21a2d7eeb758c3161c14552c74fc1e4f516a31cc0569defbdc2be4ea96654398883db97bf8d389970c8140c9c708e416216cd1710f66e804907319770fa0cef00cae6aae36fee7b2c08d7737d2df20a76d5627920d0a52c47e10047e6161be0f072e8d62008aefe7c2b2be8605fdb974b04bcf000582aa1d13b07305313319a2c5d85c61d3a75cce95ef64e5b389805ed9d05b5806f23e326a855b7aa0afde99b7ce3ac3ac8ceafd320570930f0bee91596eeb0c01d6b107e5ac724491ef9985797930594d9000e9b0e7dc7221083558474a7bed064b18513aed7d3d0a276e196c7cadc6bde6c71e8513a5e49e7406d0e05744fe0abe1618a0acc19b5c9489be7d2ba5a6269791b743d93ce9098060eac4eec41c0c98191ef618e472988e2f76410caa48128a31f14b6256f650b08ab24faebdb80a91bea2bab9c13baa04d5b779e67e055258b231ef14b71c717b0ae8041060f601597c9ef0124e61d978abebbdd4ace0acacfd7059ba1a606beda4d4d6eeefdc0fd6b18ca2e9390ef07869fad1d161ba24f522352820d2f1c24ea97c3f9e6746008f6b38984d68ded0aa1eb332b4e1b0fa76734ca7925cb329c6c95de76855e60bb6577c3ed4cab0aa65a2a6da4122bcc676ac5fefa9b86f06c822bcbe17ed1f080bcffb11c71504542ca054c0c6162da4da2d2b7c1484b2f3652acff2ca94a60051404d365ef03f79b628ba3bfb710b193e63aad5d7f0f4bc69249c6d8e13ce08467cd348b5e89ada62b9e38ffaacd76736d13d3c063879667f19fc24f552260757df2f1f0b4849b0f31ec64e85804221a994276bd803f9390a53e87a04e8770f0534e0d0a44ab77ca558257fd22b4ef26b74efec0a43062aafafd1c7b0f78d0b1e043ac6a1275329cec962257ffd71ba6519aa7cb2ad4da9507dd8ea1f0cbf0bb7b5eb7f56017acd43533f07ebd90d736f0da9bba8e273c954e171e2bdf96b050255b4c28e773caea463d2061142fbef5dce37d0b8976e5d990a93cffae8d009dd6e3779ed5462d258649bc0da56f1ada0750bb31caaccbb50c0df4d063ede00b4c3a77421ec734358755befc2938b4199e2c683aa8fc924407fa151c32e190b62a1c6542c6fc39ee08c3e7fadf6847788d22632d917a372649a605ba662540b396197a097c6e5414a2d44742fccbcc9e991ba0d6b57c3494a60e65eaa6ef406c19aa0fe9f584587ad09dcd0c2e3c22b12313e54d3db36ca8e60a7b836f5470cc22791fb15394228bee5cbccc65472dc62eb4e6963733d04651b713ea8239c02a0ba61e5f1d61020180bc5633bfc536d9ca0d630c6ad3f5084573d7bc9766a0f2963b0f1adbcdcf9b53f5e1354b273e8c0d142a1c1c1b6429fcf7a8ba6322b009d2e2af428e841a5ac67a05dfe304d97476d37a968c5993cfa5cb62d09f3bd0d8dc5c94bdd0b81a02e53b728a5eaa24b96591f70abef1ae98b7ab07d0e48910a7f56f40ddf7919a6824a2f492f95a2458de211fa488cee1bd34c5698294e850ab454c7dd6e82d547d4f8f283c57ecee87584eebae6b177d9d3bd8d9c68d93d02046bd7d99d0f726fbccb4e24b762a01bad30aa9b525d537d878ce29ce38741045079e2a813f843832576efe11659baacc2510cb21e88759bbc772294ce4b41058a9b5acf73f73164228f76c9fc84f74fdb9fc0605306ada5991b090760c16205bc9b587093298d7f780e45780a0f969fb7abd3eea7f3a56335c6d9bdcb798b089cad88617d7578834ef1381dc38eb1bb98eda40c6d2fcdf1236acfad70dc510640fac254465a4d353b0619662471b6667e2398ce7a4a652cdcb9008d84cadb01dd57e070f179a8d0ab14f92b16f9599dee5647d7b67d46044d9120b46de2a00e6e52228e540c8753d8aac6068672abf64a7d499fb5ab4db53fe596b3dfc7eb0c4e03be965cd11ff438e36a67fff418ad7219053f5a017eb30a635d8119a0f601a9fa844b4b908a267f185690b875abfa621e9c124dd98d2755e8c0570522d80fa5a0fc1cd2395adeee91f2bb008afde5759b97ae23e7a45359d5a01881eaa108f371c4eb774a63f63f2d835dcb67a02eb55b153f467ca47aefafddbe49a8f4018c82adc0a7aa5caf94f5f86cce3bca731cc5d8b3168a92d007a55221c92b690dd99605bc6454b4a2a57529d2aed8fd7ac8402fa3c34f2219820f2f7818d17500b663cd13990fc3d19bd6e933d83abc531366bceccf6dc3f6e678271f8d5ae809fea97b731887dfab5e168bb0c95378445893260b85ba775c3f5f5e2f293ad20514c669cbf62624087651b323f2848076cf88393bc63c707b6357b12eecb94e0904644c9de8742f331756ac61accf7c37784dce1de81d399156e8d51d8c35c70a80c654a623cfb1e8c113f33ff274192dfc75be91365f6d7c1e32a327bc9bbf02d73268f24fd6d59d3e9cb29b46999b330ae8e47de2f9af526f7016979bcd27077b15dcc01fed4af99996a6a169d565306a573495e3602085d4064549bfc61501ec9a11cbc159efa2ab3f708f693684d657e8db33c645896cb644da4f661f3908f8d0228d5dbda20f6c6eab73b7b259571090bcb5cc4af91e180bbe44ac99550a15f6af4d16f80400e14ae4e007942257dc5ee8b444862392e6372815b2989a0e810881afb76f12265a0c72b3535a8e9ed7ab86c4b838ff7230a68905e54bc10c38bc5673e1458269614026fb9d635701af26def6241aae2efa00793e4a76850e2ad6b2035c90131792142d410f673147650a95738355189575cfa523a530ef0cc95c98920ea45f12ccfa512041d3142e30a7978c4c818f563f6dc21e9886ea046922aa220bf17ceebc84e6b65ac969aad536b43201e2ba5627e6a6b458eceb085f4472333da95697cb94da31688fad2de9bb1b0a3026d20609a91f6b1e220f0532c3192aa07b64daf0bc21716792564232fc295c36e99cc26767ed25d9c2eb04f2a1eaab5f9e1f50c061b6eed766f7023649fba0e05a4dcecb356cb3a0a4d503dca2414b253014bade51656835f41b64337624625605f488fe986c8c75a48404b43d38f334e194e4b9e0c70ee0ff4571dfdf1be1699825b18076c23477bf790eff233055e4b65cdb628783b15d2e3fbd2c680eb3ca297faf7cfa0e16a4293209938cee000e291be6b19ac6dea46c532762140c237ef55fbc22ea143601ceb1000434a6268f1fdcc484e2495aa0a2bcb2f46ae85f00e75d122852a9746b3c410c81d10645a1e1cd4d2fdb783418448e4fe6ac22dff3569a498584620fdacdab04c964b70e80d45c6d04036f9d6f50cf0e11d60c3dfa2abf1725f7863cb389bf0e6347a96bfbb67e81f379da165b020f5b58745d4a87035f7ca8b4a9a9bd4d6c0a58015f58779b921bfdbcd2ee1e7592d48512663c138913f4c04580a16778930e4a51ac2271a3942f79ca265a8df10af008501e129048b8b6575184d5544e5f0b0791c5a6fd02990a9193c2cda4743a48649807c679f349b20167842ee39e41004cadf3fa7c17771b008d396673f7de1f502fcae72ba1f73ad88614410108240bcb74f5731b254a9d69aa1074eca75a9af09979d2daae1d76c9718b6ab9aaf609394dd906f1d778af8e310916fa8c09ccf4891773a36e6b0b8c0d45cca9f4d20ae20cf93f41c81b51122c123ac74b67ed461a9a9dbd486d3a90ff62b48ff4b30fd49dc8343363ec35aeacd61f8541bed134246f08d09b295bcc9fbc7668e1090b151fa9c0aa8e4700db71aef6aed6a21fee34463edae033ed97a38124c13db80bfe522b83c56d38ab982e4fab8f8df10aebd284f4b14214b7d758613f5f37c80ee53831bde2d5a94c4063f5a329f0902ecb5b0bb38e599dd0d890da04516042022c2557b93ad33d5f5e80a16692d70d0b8d0bacf61b10a1ac12b668f16db3c70977554cc398d34422cdaa93d19b85fdeac17f337feb2cec7051d69b6d7267740928902ee3e253c307359d2ef17a8031ad472aa4aa4cc57bf5bf8d1b61826bd401b15588a414a35aa6349c69ab5c8866394a0f64e8ee36bdc83b4970b46ff63f02293d8f64ea8c0d05d5f9e859e48038cdf33944e037ff0a082e18b226fd4d7404a87cddbdebd154ca2b429c7e43217872ee687e29c42e2b6ade8ce2a6e98ec2022414bb10ed9b278d59af11a1b5df2849179c50f2ab410f3b17ec75ddc52c2f0e2f90b38642eb70118de75092cd5680117646733ded0a43a281066a27b5712f0da08efd9798f0c2c77e04525fdbf458260263bfe830550178d329f368ceebed0ce18c2c49211a5093d86e403db13452a7ec4ac04cd274a668e089bd80f1f61503501f0ff65cf5f963d81d55000b4235e9ade97214d06410e8949ae6de698f5308fbe56e7644dac995b943927cf9b62c44f8cca03b1ca8b0a9b9591c4daea5800f2b88612fb294b178b95a4ad96d1efeb6ab13dd42d75971239de80f2ee533750e17d92f3b15d7c922d72db517a0eaa91185ed4019f91e41fd39ee41ab58dc9f02561713db73c1e3afbf17bfff5bba516d4c9468e48f3eb67b4b8f4d562055ae0b223a016022b2b6378457f0f3e8dde196bc10d2e4c040d1983ead7c57039f1c0b755f8f63d86d084fc9601223e89bda043adc5839779d7fa408d625fcc450a3003ff5f4f8da4303c82ba03e8b5db1e5e1bb95fbaf18f6ea4b16856a862aaf14089da16d7c35f229c48ba351a449827889d84ef124e1bd811f87243ddeafe482080711354139a90591aca01bded11500981a4da6a6b81c894caec956e1a47aba0948f5745e7e15b88a7b80080443e342737ec997125713c9ad2411e5ea7d34c902d31a412291f52ad92793e3c4afa8f5d90a04366045ab5f975efb433e9f3a57002f5daf31730ab1b73257fb8389f7f260f94e318013a9398fc2506acf5a282806972c721ea1cec5bb5eab247e178119b6a8ff3e4fb5cb5a67a850a74220bb0c098592100f75b2103b3fac1a1629c7c26e5140059b7636b34f3048316a5f4e790aff6e2ee6b59b124cf7aa21a34d9697718495db4bdb65d7d25ea2712c26cf710d6fda52407236a749d455fc172e38d3448afb12de70c9cd18a319cd54d950430471af0367d944920028d0449952ea565e2d53f09fedcc7dc4c2940fe59a494a032b7833587757b7c5158f8ae87bd9d690f779a2dfd0139eda6de8036bb2509700391ee788e5088296effbd14e4953ef3eecd6c0a1ace19d7cfb1a0af28385950b9060b6dd2b875407d4a1a1e1fa3d07fe9b45a8cf841adb323baeff578f0c2c0e4d6a249a512d1236da0135d3f9408b5800d52ded63bb119e51f8fcca4118930ba8d90aec1ee1be2dfa5fb795814c4f7e333925b50747d6b0c686311d9bd0c00d9606b30b28f742426667daecb39197bc003191f92b7f0df945d3324eb2d7330b20729f32f7f182e782e13e570f0f0737e8cc810986986e490ebea392ac391f05f823c0056aac8314dedd570dc9a623d8254f0e782973c0f9685f5e6af5a2400e20e0effb97e2abd96c10c040f1ae0a99666ffb51550be723c2d30767c8fcba08867e75a900c35ef3ecc3c3a28c64526500a6736d250bd5433062e3a8f3443f0374eea01fa289d85adee1a9e2b0d76705a4bff6efc11d97412d28bf8baef14f090755146a46bfaeba4ec6a10b27cca291f4016353a0fdaf9e5dc29afca2d85f01c6ecd40b5acd71f32b9e30e329b9e0dc055088c6de789518875f2b5c4219f30b0bcf43bf91fd20cb0150f7654a123e2f1a12a3d03be327e9610eae6c7fe7d90ea6f866f8cb4bf2772318dec86e4e70fa548321833e4c773da257d28160997802456ac823100705c0cf5a7c6a4aa8b7647a95ca59eadd1a02848f5ddddc8d96068e7a015756bd066721f758a438f041d915865dacb86ab3dfd07a5fa2b0060a0ddfd57bef8bdcff7a37153a69155b19c0b07d7ac2c9b54efec05f5d3cb21fa40905c03e21b8d203dc00044e562f571ca1ff6727ee585699977ff6d1b7d7e45c0f52766ca41327d51ebcbda56d92ba3f5e716aae70bf77c13210d82249b46099015f90aea7dac7bc56f83cc23ded6ee71b342310003f11690ee180cc9f4a6ba701cf52141890f067e5d5bb57aa2f4b07c04afc3e7c29ef6a4a157cea168ce09300f628df435ca397927dfa5df9bca36ebfe8615d43cbc90ba91d0b679f0f696d0905b76efc5b932f13ecffed9fe1a01155247459b02cc2ce60510d5cef99888b03be252493123e0403a42e73eb93b3c60dac8fac42eaab7ccafc0fe2d2f4c7b60a754663a499d5a3423ab74588658d5d35c8692f4e4165e2545a5878daeeccbf029d1d7bb94dc3f91535f262ee9a11de7326031510dce9f5624f6b03a5e30bd808cd35565ec0f1899014e66301469385ece1f93890301c4d1e33e31c37bd9ef20e3011b5cf5bc24a578ecbe6d4f7f5de29fd06e04cfe3e790172bdc63aa2bee301c2a7efb5173417553d2f59389d73201b1d6536fbc963dff96e2b995be723d903e00619e3c920cfe455a4942a0a26fb4d43704a8d61fdfd8738b8ce7bb1b3840f669cedd06ce21de81c02039503e2edefd1c0ca55fe4c5a526ff489d720a6e10130d7539107110285fb6fa54532a00eb7eee2d3986652c348c188e821ecdaf00ac25d0bec27b0d9a4ddf5478676675afcacea9c8b9cc7f4c059b6c85a3909be0b39a740381d9008fabc6f7e04b50b6c925565f0a00cfc71af58881061d3e517056a4873a477f9562da01cd499c504d7f202c3535fb804b1765f7c7e2f6e00390ac86894a1e34a4b0913f315a9a9f18eb10c6f4a3051f7928ce78127413860cb06a183342f910339b8e1ad9805a14c1054f5b4fb2c9b25284363c56cbe32eeb80f736154daf9525492fc776484cb0c104def46f9456d1f1951e3dac1bb52d351088a97d5bd580e1ece6fcc3a8d3b79d591add4df080de958dd1b5c52fb7100c20725e748052672e3e0ae2241ac8c75f70654f0914d8338b03959fc317f95b7e801c21fa172cbc875a2a3b6d1e248344ab56eac7a0bfed84eb013ce4835ed9d12035075702878cabf3286c3838cd05283fbffe21d12b38f14ef6c331b9e7a4f6a06fdd451981d9b50e4757a68097b26b8db036f33aa889b865b0043aae77060cf0784979b396c263e528004d0dc5b665315e994605f21814c89d8428e1ea8b87e05125856a8d6ec3fa6f38ba862b65831463b5b8fac5888039500497043a930430df9a07b96f0026b59a2dd495a5dd13a8fa21bac340c0d78d79fa6c5f57db5f40264c5ff156f58d213b6c65ffc3040db37922d9d0bb9fe6c8ac64e2896cfdf620ec1b232cd5d23a5cfa7e9445cd8876828d3d840229dd7f1d9241aa9f60cc32d0fe7ae7ab4fbae1f572ab13e27f17346bb43280c5f59948e33fc66ad39fe80de02ca16f65bca4b55514c460528345482c4ca2f82937aaddb8a6fb32f09b17a5000fae2f9da3eaf2d3b79aad176178d220292fa6e5ea3effd69ba872c7b3268490bd9e5668d6f18f142b5a58614b132b1e323f32849547cf1c5efc29ff6c447210b748657658bcbfe7b306e750ccc2bb6a82aa41912e369bbe1602f22b65d3e7f07bd90fcffb2ae1fb2f73687585fdd4d947eebb0795596feab9e74473f04e37f0854b62134aeb671bbfcfdb696534c2c51315de51295041cbd42daa08dc9619d0aa39c897e3922c5fe8e45d59933c9c60e6f2bcc00c2b3c5e5c7e8f2411edbe1063624f26fa6763e09e9b5c35cecbb96df569e8b08628ff789813d198833b52a01b79cfb20b5253437b66bef30fe184144847d0a6e4a4aeb4a141a9d6a7c63930febfd7829abe1277cbc8adf2d7711df55834d9511b863d95aa49d6ac164ae4908d1be4a68793711ff9a69a31b49e72f54925403084aa6746ca35eb64cfb9b960fe2ca1c4dd9194f9542b9f321a7941330e6a2ddb4dbc03d4710adfc7d9d6c1d016edc722a77771a4487ceed1d8dc08535088d3f6e3d6cb0a176a29752f1ddff0a1718e2aa5bdd6c2370e0f4f5c73bc4d2b4a64382da48b968973bafa31f2ed00bf456612516b33b043aef2596c33bcfb77136923c7ca0b7820bd76fa3f520d30763d6f76bacf654b1cdcd7eef49dc82ecda4488ef4e3101514380994935ba70043e8f15bcd425915cb0d045d617f86f03b7a48725b1c074f0c6c8fb8739aeaa00b50702bad512597cf5d2010070b1b07595bdd1f251cf2ca0048ef5c3f0c326029f31d92a9be48bd82fa45373d51f78d330ee34b48ca41566f5d0d55a9a117e0c2d12b176b0db888da562ea3290c8144b8b7fb2b39c8ee8a2d7786e9eadfa8e09eae0957e2e385f64ebedf24f2b086528c74f4bf2d3907d73fa38d3e829db470bcb3e7f5e3b9650ec9e2879595872adb8245bc57f323923ae088d5ba0554d540395a8a23097b145115fc1d7364509bafeddfc9ce4b7521e5c19f9c0d21b6f510a8f0c747b82a36fb921599e88d0d276a2d65468259572f8478f7794eab3314a074e5b43009b55d5c61369c35246e7fdb548d3dd6f2e55787babc64ef4f9c2de0864906a765a50b406ea7e157929bfa9a0c38d3d40776070942405ff5e52cc9900606e3c1c55702827f7d57bd2cd4f0a0f65b92297ccdc3f2b76cd93b918e540078279a2ff80f0b35023f06e61322ff75237cb0ac8d60e475341b17d3c92dbb80773ec441583f4d400c98ac6845e8542c81ec4b8fc8c89fbeaa1f860ad476e9b099e03e0114f58ceca9cbb1fc3ea5df9e002f6c32c9db1cb3d7939a066e06ac800ba4f9b587482ceaa7d3e498a756fc8aedb8fa89f8a34799daaf2c3bcf09e1702b713ac06e2ef92db96dcb37bcf2893e88d48d2838c28e4bb4e86b6aa66d0ee0a326075128cfbbf7625066751ab00fb4352a9d0492b454463dae298b9632b660722fd38245e8cc023ec6c134bdd40b1d029b3c6b724e7d9a5171b618ea69c7b0ec4bdbeb6930fb9dadbf4d65d9aba1e9a30253e4bc85903f4d3ce2dc85077290fb45b9f586848587f5c60694fbe34f9107fdd21edc71faf8403f563a4d3e4750c06eb4c82e30604b9645606cc9f3cee535ff147e5be9bedfbbb4a9cd64fa5fe0f4d980faae30d889907e1f67b8b400bcf3b0f2d55b9da858afbca8da48b57310e019c3dd275865c319105ce95d59db7284d205d242dd66028f94a4140fc1c500056b45bc6eb4a6962a567c2a3abc7777193b5a86376170d4f17c2401e2f91c300be2a042d78a7145dea718005dc73fff75132bfe013d9d8fbe39839d8cf6bd3086e0cae4af81591facd1dd20b304513b57986d533e1af38f4a2f23aba50be530d33becb5e996ba173fa189f45fb4df11657faad34f838ff2e2c07df9f569f3a0497300606284757373def299631f71706b8a8013a6cfccb1249a797817c098507963dd16286e02fe1feb3c4534642bcca05c6a3d35764cd166d603ff18d33be0d9deca603b0020bf9775d811547a3d5169c36d419f30036945954391762d1480123ef24cc93b9bd52458d55a274ff91b359457fe9188b77c2d5395eea53e42306bf8aa8a2ee36aa89e04029693f8d24dc31398e86210233cb8447c7da5214230ff416ac2cfd08fb31cb7bcc7407acef6b6d1ced36bff4a0a6e437aefb008e04043429c489392c40ac316bb950143b2f1b3b8ee1725fdc9fcbf91cce00a0fbb8084a28897842dc2a809d2b06a17756a622affd8abd40d2cff0bfa96841828eeb06169b780508e9d8c386fb8da4c2d63e03f53c060132c20aabd9a60dfd5c0d840dccd5e395f9395b42f15f698d5c4eebe8dbb517cbd089051bbab054917d9a60044bdf5b333dee103500f6af67208d5039d5e8b63ac6e67849af2b2c1b3665c403f6656f2be5c08a580f7f8e34af051e156ac0258daeb411759c39becd4211570d15f31d238ffbfa6dfc6157de0e7100d58ba5a4bccbb75a01367a9af9ce6f99034d92a5b879c73e25d81e3b736791f987029fc698fc2f40399f922c68caeb8f088f54dc16fdcb680238273f7b9ce618c6e3c1ad609d7781a0f80fa3116c83590c3df53f8e01df16c3b30f12df42633707f54f8b6c523ad1c3d3be548163905e0c4b4a75c783e2f0bc1e401d54c3057174e28d9c925891ae36ffbeee0184ca110a9eb6da3198a02db5e88e9f5fbd2c73810ce09d5ec7723fd6ec34f52100e03d0fe75c8a9f1cdad646942832fe70055688889edb86e8e0c18aecc5894e05472a04d155a070f9f05a87076a9457f8ab605c8363321df1f525bd19e561ece67e4105f58453971474653d49b70acb9c9b4d53d6ed6b2664a51c2d2e50d9b947bb8d063818286da859891dac3dcd492e1a86ef18ac0153b272feb3bb32c7d61b56be00e42856593ed9eabbdb70c3172d7d70026151a62e4115670935ad1605c211f80fad9f20a958135e787b36b92b9e057f999106de228d7543bd0a0e9506d0938e0e77b86cf4dd2f9faa3578f4377df649a47b7a19532e0fb64e10ae7aab51506a040b5094f995ef556bf9433c7f945504d72084a8ad64e73f40db40ca1e1a487a07ea49d395c64642eb8c35f35e55a497675b9a683f33a11e585ffea59b0fc880095dfbc9ff9ff00a671494d9833ae8b0062c0dcf2135cee3b6a24234b7a7cb170a2acaa5f5378fc4e2030c8ced0496049120ed285cb57df2f6609a3583da40a50e08b9ad3f2535275e62362bce099f575d10c6fc066240ce70a91152afed87280079293a02f83f5b4d592329d29807a7596d86fc52aa6c8997023ea515dea81909afe4316d1e189fe9e6c0f49e165a6f77c7681534ee26ebee26ce12e437ff4c09b694b119794afc5d103b63f32b26ea40996ea076c4652370703101c005c0010f2a5844e6d261736acb580f210ab1da9e37f3497cbbfad97b2567b80430bf960db480076ce40396aa7d2a88104322af9ffa868065664c36b5ee277e068cbba30fe579925de2da9aae4c74efbb9e37638e8e5e5b6246a75623bf12fdbfc1c30d0c567b59303386beb0035690e34da91c76bc30866947cc1c327821456d731431063cb7f993f96c2a47ed70bc6e89d9e59f5209e767b7be3fe32c5b2bd15dba5b05765a2a50f860436f23bea831633f3f5e589a0e2dbe0f15f27f752dd7364356038f50f56a62cb35af38d1662d972278c7fa5998a430b5d6a15ee95a24974d120b4e6a03fd13c2b3bcc0229dc3d50385845bbb16d0945bc10557719ffe42b8510159e5eb0e14c591d83b31c955a3945282a69cb6d31dea5fd1e483a6da4226650e8e21569cd75a37d9b75687afc7d636d17bd0ce2241fb21a0a069388e331dcc04f3cc266394525534f55f6d7a4e508b0080520c31d5aaf03494877afdcdb05003f1a6d8d31ef8e07c66286cb260699f7f018c017b11c173f513fa1dcab063bd043dbd213a41db96f8fcc402b5ef4c5f4df947f481f3960b96e5b7c2045b56d20a7dbb9cdbe115bb40d27369287063941d551e891f752044b599130f94103a3f04e5c9a4d9e7e829fa35db4a7afe66e98d584ef7d73d3daa0e743b6b312d46110ad3bd699bf2935043ab2d7a41ab7c3d95a45db76a75c7e0d949c4bba5836cb5042cd4ecd74cd6be5f64b8aca6f0e36c90b9b946e9642971a5a18414c8d3063e01d8a906d49756da16f69f456d7b5e84c22802132572ad06efefc250c3a0fac50b2cb5d17c198773437047ec7452a8ad89dccae35c8c0ab4fc6ae43618072356099ce84afc5b1527e6fccde920f73b242e2e22e187f0c9134efded66c3c2030d08182af5b5d1637f214fa808f8354fe39a2f1c98a2595c78ebd758829d6558de02ba5abc8475f4860fefe1450571d9c5c8ce04b72a79b2b139f5a1d6484b6a9f0f04b8add7a7d8bd8b35593f63b0e4128ec3b1623d5a602067fe78176ebfb8fd0e530d7c7e298d7dcae7fb3a1e24bd2345ee515dd30484a1b738b4bf11a07e27060b3385e73d29fccaa464e42bc0835e781f1ddc79d53e2a5cfba5e2f2be117903819ef7a93e9e37085d2fad2305b3d1f315a80db604fc467c00dc4fc8b462d30bf4145965ca6a511fc378e587daedfba4f9984f07b4b1293c16d91355d2c2070de485003e58bfc9b0ee39035731633c5745575bc4d3c66e6bb8986a4c7e143f0c7ff7b4efb46c95e36f10ca9443ad485766e9687350732626f1d38f4241f6a209757955ac6f81a4f8cc40b61ebcb88c97dca2774ca40132743a5196d23cdf6509d454e11bd66116aaeb70fe63ba577bdc6bf7f8d619a4990046e834eefc55320fd61da98686829f22b2e70e597b48de276904566ae221a8ce6352d93b35657a01a8a700fd041308c38ed8b35e940a16b7b481b51b14fa7d9cdc8123c8d78b8d0d3154fd72e954575f9add4ab23b5ef6d63fcfa923065a0e164f008eb4dabe320a51339013fb3fd8cf9fb1acb930a3018eb66c7038ec8e9f391342abbec60e830d0500479c6e825b59eb5965b6214d2a76e774ffc1a12dd0c78a141438bacd930da2f82809cccfa517f2e6a97e79f83c27067febb7460384a6094d38fcac97ed028d25bcd904234a9ab70683e0ce71a675846705994c8fa6caa9a2b23725f2c205825fd00c0b71da03e28eafefa2ef65bbb794f603b46ac425e1dcd060a86b210dad690318a5b4b6dc22a1b02dcf3e08cb6bb005288a267acb880253cd91a85204ff6db55410b720c2075773eaf6f415b3cba32647a3ad70eeec2386e355c39803c705d22106ac35c06ee8e6a05fd1d5f3913424325c9b0c869ee685ee83355b0b39e02f640aea1aeff172e4a12f8f6d996b6967dfa10ce1f70944aa6eceafb605f6350240249269b83ba4ba4695f710ed6ec9b3b5b7b7e322883f24d712cd2403da325188651210c0a88b8b443257a8f760627b3633be35314cd936a3f09ea4073aeaa7d8baa8aa301e435981e035dc2d212b764fdcc9fb65a8c04c0b39086d09d42abd7a8d438dbf16bc1e4795091a00751d2e5b554bb0e9bfee1ac62f014f09593122988773fe35936b57d59db5476dccef7651fdaa0d273fcb0a5b106c3c08d9ed733e2fcee4c97f649380b5665f111047a6fa4736ca70bea8f3d1c3a4250bc8a3454f9a88323bc670d0b1ac52e8e08b66c92505adaa95ada0b80929d4d30475be64b13829d5a746d2b3f3ac654388bb8f63e722da0740fe77996751cefc06558d95bae44b05b1351c6852f8176e3475410c4adfa7923da407735a44b71106c5cd8fd617cc727530c0982073b9c94b2b1d7ff80a7c2e93b80182514f2da10a122fb6efa275d9aa48a14803913174b2c5f947f88891adc4e0bf3878c9cdf90c18f1a17f51e8ae766efface1df8fa0483e35a72e95883d9efc4a443ffbcd350886c158ed55f7631af84dcf9974319a87bf6f8478f4bf23752271b2df831af700d9ed970afd8d9f0cf599b4e5ea3f7236a104ba3a82b54f5122441bf8158b550b86367ffbc6d71137dd9eb048ec554ce0c6a88fe2cb31157a94644f1550ae6603e12f853b47924103dc9b347126d47595afe689be4f36c3340aa07447987e4a06aff100f13c793b149c03abb26390e7a26a89a4a3321ff1164621fc7b87a999000208bf895c9193f87ad6d9649e9d6a37a642ea3e1b287729be0ee6bba6f9c90ca625305d4e2db3c2252a5b6a62e8ef322ccd2b2886e78a6ca3f6b5b3822b1804cdb228d98043d0e90a89406e0e591b2f90449a33860a2a7bd985b30497d1bd0545e402218e840ddb7b3f4fffb00a90295e611ff5ea05e4c0ccd8c7760f110705c88f997b8a3a43922e5df0b61be09b236feecb23c40dcaff412a81b5d1e1730291d0378e9fa5f737c4b8adb94ddf9acde32dc6954b2b3b8890254ba4123e18059082c254010e93de39be364e7f31f67cd0c7afd22b9cead93d7cfd7eb647a40dff518c859693cb36300d8f3f425fdb3895a407fac43228cdca6b431e551df50f874bfce2337677232892b364b8d846a6fb7417e85919680b2b7644a0eec22b00f7f2edb10a7075a67ce59acc52cb3c0296005df044afa5c49a538715cbd9ea037a14b1ef399072d8867f70441c1c12f4487b7795413f5c4b9390dd59eb58d706f6ea3212cfa6c883885bf17de4ce3b38788ab699becbfb6096111f74ba624c04fb34419b03daa19acdbe83d190b6772ef62a19a85a59c43422d87e9e92d59902fafe568ca0e9f65995d75dbb75afe39b7a04199be7a1d3eda5569be6da107c06f163728d3e70758c9824f8ab7c6dce1af9f7efb973d1bc92b6833a3b16e01e0f57199751c9c678221e4723436fdbc32622b44f9e70a77b1c461bf7a3709a900bf39d71a686e5aa8422a20341bdf318490377000e467e390d2d3fde090eeeb806abc8eb321a1f3473f93222da9f7cc423b8259a69ce1a7b897e8e000fda4a5f0d2f9f046bc6c32f4460c063a0ebf42052d50a3ff445f9fd1a7bd546da18100d05f2edcd3d4d8bbdb990d4ed11d66184515eb595e2a683e348d28a65cc8634de0b4df2a7229bfe47ff76f889330f05c8aa7d7bfb628b425f2e0ca9cc2c1171ab071a8cb373b96533d892896f7e19156bfd20197d0ea8419009b1cf22458dceea0dde173822faf0a3ac17da322c885782b3593e39f440742f72397e8e3f8c15a30494dd94997e13198abf3e37e7e401be4255646a942adbd493fa44f40c376c0d070b67eb0cbc008b598eee224a9698d99fc11e689ec51fcd35b9bd2a38a3628f0f8e5c0180ae8219774da53ac6c2e71ae9d84a083c3371bfd491497d80e6a682055b7840e0f61df614dd1e22e3a87432162888e7d15b9f64c5a3820584f9de310cd14a6dc3002c6fc5e2c80a19d360ca95b384ae33ee8f8dcaf29bf0e2e7a78d03a137f4632eab694b217cc3bfc5a1a0ec1baf4ce03f573b0b4d153936ab82730990c0aebf8e429444c26c7dd8038eb16e465d7d51545a9e6e7330cc4e9e9c720529e3da56bb8b077c013bfad5b86dbd52401fde7d9d9e1c3798652c6a1ee0c00ccc74087e3a9fed0cb78b2b6532c645658459239e2d410c4383f672f2977d3f09f63c608b3d76ef20f97c4b57edcef1b1ad6e982625a67907c214dd4d6151ad02d414b1c4abb3f442c1d91d42b83dbc5d64f55199f675d613e6c6cdd7644c80050551eae8b0d36d8809c1f4357ba3a4486c4f881b7cd8506bb0a8e3a86b57ca0a1c85fbf97e652972503ddbc33a8bade7e0859d9ae7fe1d54fc6d211db533050ef9f94c06737eeaf8399ae4d5f7a90ec6b1c5d87c6938a982d0951c90df7b2300c4222e8af0b157f89e5889fadfacb299f5dc5fa8ceedb5f392090633c8059a0a9c9e55da44295482fcd02412755951bc77f979c768344f436abae112227394067066199773c7ba88fb6fd73b7740358bb3efcd93c279edf9972f6067c8c1940074f4e5195b3deaab16528994bd9965148c39196ce2753cebba6bd464c4c8fd06b394b48323b1e68635e0faa4280cccf9f15571d96552c29cc5a6ab34516c030006a90c34de9ef01deb2a692da67e3c52bcc2463e9f5636bb38eb32812ca7ff038b211269e6eee27c1f64056defccfb589186580a0e02dab10c0a68fb86edf00e6207072eedbfeff52c58e90c62cc38bf33189703960d60d6fecc9e8891ca85028d464a4efdcb52f5ec9898b77c2adb62c0fb434505084ca0410e2c804cae3c00e69afc48bbae84f62969737ff395ca03b3b31bc2026ceddcf7bd92a3e67faa07ba8c65b2bb2258137f223e21ec362f95d39ee0c6acafec66362f0bc672fb280a6b883a662361413609978b11be0c3064c71f325988143042d9b807b47f268d076d5d4e807a5d0a6984df81737c479fcbac6f6dff715b2c5d6a343e495ff2b80f1719a721a9b8826939f8c4aa7b4b83f8fd6378f07407aa082aca25a64431f500e9852079d1380ea05fc47ca6b665339cb9256e3348ab0b78b7a74f0ca2c2250826497d92cb02405fd9dc99e4e1fa604aa57e11fbfe1f2aee3c58d2c0f141ab00b141c9cc659d693b4a764a298da0e80f797b88a8c6d0922a10ac650abd6ead08a3beb5df09ea0a2ffcfc03e02638ecc1014a31a1f26e763abe94a153d2f20507e5b3674585dab8a95a85351f6fa5466127c37ac57707f9d0587fd2ea7e6b8100d019d24274a0140619acd9c0ba0a20ef09702c4d873197d4db29288a586a9705948a71d9a4dc22d209f6481aa913f1a519ec0d881ac3c1ea07f5ae2b88691a0bd43618e093ca31c99ee842a2218a1effc76d826c42c11cc04d301983c4e3a10e21b101867a044da01ab1654670ef513f55ae830124058d814ecd359aa175ba06854920cf7e3b430f064f9920ac09f5c6167002aa65eae2e0af13985fb3329c0809719fdd73947bdf84ff5e935cbeaed2b6b931e7d477b314756e3d18b6facd0425a6a7a1fc21261b51854cf6d159cf767d9e233f4a1e2018fdac49470cd18d0f36448ffee53314787c44607ba4b71128b9279f43eef41d614b2615fad9b8f70538d67496496365b1bd714da324212dd9c4c2e60ea34ba1579ea54f9620d8160a725c27bf2fe60bacb3130284ec9e3df41243be21240bcfc82d3e8cac43db8606d85b8c7309c7fd24e6fa047d1f5c40070c1928adcf55171a9fe5a95d46cebc0b9a05af9bd49c2d28ac5da32765b3e2e64f222f680fda5fbce47e3eac66cc510444375cf3823302b3cd5c97406896ca931d78b9696fa88a926ffab60ec9aeb80a42fbd6f741568cd1e2cfcecced70d8d2c93d1e4009003dfa49af4e844216f104f1335b65757bb822548525fa68fceab34ab40bbd5f925c1f94b53c32c17ea6097fb89cf9e7bd36fa3977797ba380843b08d479fa47b5857c152bd22e7977b80e65906fd18a5268546e081e85d6befdaaf3c4bdab8d03429c81f4bfaef06f110123804f7710fef85cb19800a5b5c381cb85fe81ce4fde5bf71747de61943f1a00b9befdacc4ede14e0f7836813830cbf9ed38a90cc7fbf21864991b85bfd12e0bb241875594de4aba4e9d6d0a1b36d5b139746d34e885e7f8e3bbf6678fb411074634b7b7557b66ae7571ac9cbc56951df39140b3ea7d64f9f9a89d4e0a4bcb0562f9eef689e1bf86c10868e952e06ebf619ae25dd39f65555aaf244c1481ab0dd995640e281ef327cb9532b2099c61856ced7d73904e0972efd9bae3c37b8f0c3a6747c3c8968622bbf2b2aba8b3ba85ba62c526a28e736143d61eaee37c95038cd1b43fff3ad42aaba089896678c1e29844cfd7a164418b7d34d7e540f1930630d68e1b62a2eed14924c1860dbfb0320848abaaa5badeb2ad70782428dc0f0362370be553d27ec964c927e75bd309e6870c7815b08ab4d3511b592eabf561080c2249ffbf238bf7ebdf73e301e9b3142ab71c42e374d9b51ac24fdc4bde9804a2b87a298e5384b8c73db89349f3b34ec0df8f2ac5b18bd83f10809eecf067063deb995376b3b92b51e9a4fbff1bf671afe97016895ca72bebd874688bc19009d8a14e268f80dfba9edfcf4f2e17a1dbd2233b79ddfc3d5c9388e10a830e1a0984c8c9e6487ec31c17c2403648e50cfef67b1d47825540d72f37da28f709cc012937fef6e2d5ce5905fdddff04bc275f8ec83494e51393d78144e1628512000a6dea0714254e3d68488a25143010ba0e1f5c42828e6c014158af4d3498a34d03bb5f7ff5326ddecfc3fdb8403e0462339d679a1904802626724b9be3a747c306a2c4057c30dc64937f75f3991d22688c919f4f6b993b4c40adffc49827de500b8564f3f020f9add5627657e9cda655a725c1a81c1b065750ed35983ab73f9e0f543ddfc88c0c9587e47a8352d50daf3bf177d7b8258fb02c17097efde166a00978b627b8442ffff6478ad6fda3820dcdf8066edeb1223d7d76ad8bd32c48f40be9344499ef950180ebb5b19ff218c0b322f7ba009179625076f64fe087067f090a5b33c004add071f32e7f253086387632d822faebaff2d353b05e6310a5f508d14c5634d649c161938cdb4b55395e790217918fa145171ae53de2e225433709 +generate_ring_signature 7864676e28d3c4970b0debb0e6f97ea408580a98d226dafee3dd81f5a3487cc4 40d7c9c8b56934e5a5d9a07621a15fb22374327fec4fe966bb846f06dfb62cc6 1 494838f14fcf86139012741ad7732de1d344dec1773830005b3602c49e363c25 39dff26442e1ba3eda6724c73174a46d8fed8eca7523ced7781fe134b2f4400d 0 b07021b3ad94950c2b00abb941b78f962476e6060c5c517fd6d459ee96b376092bb36bfcb036f60c82a9edf627bd65efd735d65826b568966a6224d98754c509 +generate_ring_signature 2e56246c092a45724cacd88af27591f7b36558138dc9f84551e343f3e5f9795d 79b775c12d319addb422da0e67ba0569d1d04cfd608980caecf219b65feb465d 1 fddbe3aec2ab6a7c85a14e5a631db26aeb656f75311c48450ee02dfa54a7a16f 2de3c5a26614cbfa5cebdcf016c9a87afb79822485174be1ae85544aa2a35003 0 16386f62533726a98fa5f36df670b8dceee04f824c35f6e15a0dbb6e3e4bdf0218412c5407c4fdfd442a16f03b4d85641d0d448cab0f9b1da3e27b121d4b0902 +generate_ring_signature 7815f1d4b85b2fa3d55770abd44e7d7111f47c5571935d9e5abaee6bee223095 bf1e67b2b20d957c19b829881f3654396a78adbcb44e985f7aafc4f5efe39231 61 d3399fe1944fc161ed48c41e5199c0d1543e1983de4cfd5d2c410f45311b9f00 e2166c3a7f15b793bd2403656e289d1689feb65f8e6fa9b7d46a34005b261107 b800cca2346900c2e4a7fffce98677cfc8847b83a5c69915a3ec51862f302555 8f8b9048a0d2f4d0e0de1d8aaf9760766ab3b9e7cc5fa3254ce88f951029b04f dd638dac03e070da9c635e93ae6250be11a35cfe9bcfdbe7df3fc5041b11e63e dd85beb9d51168206681cb9e2d07315dc15c19594df7e2119f073566d80d8d97 31e92e147b4549910056a05d60cb44d95c258ec13b82e57d1348265626c938d1 f6d071a1f002aa6b543a8f3c5749255d8c9e659e1ed46e4800976e953b82bb1f a6b081eb3489da635b97f6cb6878848c085a60d8b567f08b001ae48ee1591649 372ce8f0c084f59732aa7312dd360760dd3019a073c8cae656dfe2fe499004d3 01c7206287219eb32f90879dee2d05f3ea36557f828e4b91ba14d30c6c36273f e8220e44b09d67dd1ff189de878989324d964281b678e238dd5ef329332c1b19 226dc13531650b0de4c419171f4265fb88bf9b645396c39835a627424d3162ec 1eb7fdedfa46a4eb222797815aebb70e1f3c0a1bea9bfa3a5bf999d2c2588684 53f0fe809deee1f45adade3acf4b4514a1d437639c98cc9562951b7e95d28e3a aea6a6fc2cb0b865ebeb9047309270eaa1442c10796f8b2fced7458cfcd0e57e 214b0b9af85917ceac889de8f197bb26c5226a60aae4dcb812395e9830b7b185 59ab7c5277761232bb784b937b97290407dc2e22e0712270c18561322dd25297 5da1f62493bde6c3f398172047c7de1217bd463249a3279193df9b6068d2af55 dad11f986df301dfac98557bdcf96e36efb105e458cc3157494319b9eeb90cbc 6ed23b5f3108a39439aed73fc45b313eff7b198585a0de020ebc72543d4c735c 90936fc627704467602d21b46ac283168dc6a8081d0ba0330e0e08f7d8e9a255 0fd25a20ea3a833592279b5342ba758ed433a3997677e84f36a96bb0ed48c985 48b4d1123003cd14d0a9e7c323d1220824f536deec12a4b887a31a35fef9707d ea8c6a93e31262c5ba1be8c81129da5c5b38f4b60290a43bea72ba8a64bc498b 17b8f47c4b2ad10e21427ce62990cba355a82827add46abd01b0a9c6c2bfdea0 de19153bf9d58038d4f0c6cb613e037b2572c641f4bd71b1447aa4c93ac75438 26b47b7e2b2996a659afd62b05add0068672d724a4dc18a33ccb083bfe30d760 fc8adaf826a43673e332af17c4dcc381d69c5f05f82241202f799ab0c75bb58a c25fb0b991261f8b531bc5ad19e62b1b8055db902c2b1a435823c3e9856ed4e0 c46d960e1541d03db8b945297fb8cfb6febf632a151515ab0579595ab9977695 0411911de3280f44aaee23820e05effb2b3947eaba456a246a29d3c41814f5c7 1531a1532463934f0bc7ef7b30eb947a65f7b6770c55689506176dbf387553f4 f620c8e1ca6b327fd3646bf64dd2c94e9acfa3b4d52a008ccc2d0ad6db5ef896 82a462271505cb720f9f9234ac367fa6608edb83d33c1de3d92bbb3afba19b16 d443003d8fca22d127eb6acf6b26c0e8c4feaee60bbd276e19cdebfa2fe20acf 2b3faa1881c5ddd2d4f4b2d8a15872e4c74c1c6d3bbd54c5d1c2e334857369b6 8fec354ba1f19ded18abd2a28812c09019bad7f4ad07a12fe4c3a0f7e5be1521 ccd228e8336c7edbf92a2faf42c221b890a6c2f5d48ce59cb6858551f93429b5 c36dd157fad2ee13582891fb642397b4ab159de4b55fdc5fdaa6a01dd6763249 6d750ef9905133a6073dd0043fdcd7ef1f0d0e7b68aab2e79321b5f904a9da19 800055a80889361483f078c8be0f37c455919ee300b6fab6e637d31e00433b12 0ad3cb33206d1f80995b7ba6518a7626739c1b4057d5169db2f39e78de3e23a5 8e077ff44226cbee60be2f4d75befbcc46e691cfb60c664c1209e1bbb031f868 1d4fbec878de15c747915bc2942331ae6353e2ea2b1a562fc93b264730dd6c24 5681010b12e8f47019e0308f2952ffbe4daec078a63c6e8452bb95cd2ef6ef09 53f5417119d15c1df61673eb903a9509011c0ed83fb8a6863f8d972bd0f1c6c0 a5479f031a8eb4eae48c0881a6fee4fb3a3cc4e2a2d372594d5ff3277044fb02 c07652126a9845578a02a1512c77ac191d20a8318f1f1756d59d14e9f32f2830 8c8bafe3ce09e7721da3d52ee6d63013650d18c8482e57d501c3fd7ee5b0e9e4 f865858cdbb9fb50898b084136dae15caf9574aac8b712679d4318e9edf7b04d e318fb363e54aef5e7ddc0aa2340c2afdeab63e262688c95dd6efbc7f5a2aa1f 4b01b80161b7bcab306ccff59258d11e994258e9163bf55cc8e652416d817be8 5975d6d8d944547346f31768e953db73941c6bc1a189836012f77b200ed8bdfb f9e4bf1d84793e3702091f8ae0d833c7480e474666b169a0884cb84d6e5f55a1 3b161766200c121ce33a1ace049b0b65e29fdd307e1dcde2afa95a4dcac3750b 9e7861e59e8d3dc063aea6f094eefa2914851e0a96f12332a4dfed98f81e1f7b 363cc8d62506cd8dde52bdd490a3d3de6c3314303166ca2a8baf8e753fb3db0d a1e2e309a4dbecf825cc4dc6cb4a6675c1c4c66bc77cf63c5e19e9554de3e70f 282113e165a5ef3f6327066b407b464d2f23abdcc0a6366bfdbd10b1f65ecd07 7522f8708006f4930053063d182a25bfbeac061d4a499d1aa7687f910a2b6274 879fcaecfc2dd21a62fb8478768194e0efe3a920592237ecfad527bdd042880b 3 3f631726928147ed30c75af4bf2c5864eb04b4b4772738073367c36f6ffaa60111cbc61cd34915a4fb8a406e0fe4611ee28c99951ac18fb722e81602d3f74904326d17759dbb8dae65c3647044ccbf5f79477220b76b2cc219932fda9cd5340ca980505cf009be3c9a496d68f93ca3f52c99d49b3dfaaf71bdbf71d093efc20018ba9daf425c152a6cf945b30751ec9a964e179d67b01db5d40a1ec477de6001c19a0a336fc28a1cc77a433694bb1c543ff015bcc6d180c0ddc5d8b2723c1d034d6ef3d4a0473f869484280883e2e0a8aa210186cc4b687b28b4970150023f05206e35ffb427891d5284f4614312b36071f0d1e805a8b54ff6c500ef99f73d0e2d87d138dec9ca8f1307b40253a7ca159828782e07a06a8cf9e15c09c5365e0502c104a4acef1df809f439d3d9ec7f4119132963b2e66e7dac6529102b84e60ea05e061f51e3e358b723810a962ad21e7e3c68b826321c18a676e3c8776d4e0ab854c34a34b2dba312a0b8a662126021825672fd6a8ec3b6ce1b2bdda2aacd0054ef2a648c52aa2c8c0e97284e8d43f6e51f5aef1c402ee14f7b34f5db7eb50b1fd9bec7beafcf5a6f236b62617ed41df044a59094961c4673a5026a6822bd0d6af74dd471e2d4a858ab15b8c6cee50e7edf2c36f8a5af990747c01807f8860c64d48093d3a8d6af04a92b881b6f2dcc017507a44b9986d27d9e0739c4478c045e3980693515dc3397a90628e1c1455ff74ec892121e650ba2ce6f64e2a461047abafcbfbacfe6ce332a684e9ab44ea75b117a109b1adcf202591793f2907408273ae83e5a3634715e13bbd0271ec1259827b7d3b8052e989a422ca7eda91c06f2b67d7d1893ac1180198a00b94265ce7ec5aaf3ffa3a9c4dcc013d256ed5606dee7686092feafb1a2628804b1fd5a840eb5a6f078c9d301862cbd1f5bdc3b02e5e6cba8797f50b49f573747bad9390d5e547c7f3670b05148461dffb513ec0b9d1f6e77320b11c883c4d2624f253f99c2793497659c5020c9c498e6a653e60b38aa00820f9b7bae02a3ff08ab17f89b48a3b59f2ced6ee22dfa478a3dea160c79836ea350fafd9dc40747d2769568aa93560e2f7e681e1ef0758640756d770405de9f99a838fcba6afe49b4b4a2d651f968318112c42bbfb26fd227a6a22e0fc5285ba21d30bdeb3d4da89888de0a0d648b249b54e8898341f6775e74ac3706690e44210447497794b2a69f58c6ad199829b4419d96ee56312c9e5ed3972f0e83d70c7e4a6bdba630c154cfd8b10a9800896c278735172c20c86e9d8f48a0070a7f29259a84268fa2ba607d8aa0de19d767599db6fb03ab4c0d7dfd2df97b0c98870dfedc4b69aba8d88cc436f84788fb2dc300783508cae34c999846553d0bff9c1a02ecc087cf45c2fd5d4c99045858cae3aae821216298da845265fb66061fe1b852627cd0d16463b320c6abcf36577fabbd0da0695edd51926ec7a21b0910cb5ac673662aa3a31d10f2f1582037d28332b30e0ae9c0bc39ab57a0bfdd0d8379de0a0edf65a9fbfa4cb6a4e707447603cc1087f6e7313a24f498cd61a6089bbf6cadfad44c1062405c52bb8139280b51582b5660940f6ce9e70cce5d21027b0493e6f2645bd327a40b367243765c72fb1e9ee190628e14f7022c5d878e0a52442d0c380e43b550d713d68da1fb9d598f7a537ff8cbb9813bd2a41ad0cc0b1361a9f29a3a7589436e71983b8a3d23ae2f85f0476cd0248a946e0bef9f870a16c0c0620e30882281d8fc97551c8083657f7a979f21654fb73ffcfc62fa9a04b39206b8da73342d892360798344e321a60652adb7194690b9d7b3093f538d014a7767c5baeb5959e5fd81acec9973749547309c674086d1de9405d34c4f56049497667487ccbd19834293563b66d934db21d8ae5203139aca821207844f660ba8ce39b383c2ed9e7f62d98472268feab6427bc9bfa3543437b9fe14cd73680e6eb165d92672fd238a7cf1d64567d148e6c6fd100984cb4af69e05e5d9aa8e028d72ecd27ffc1fcbf35ef5675ae890c0788665c554e607307e0ee7b1c27ead00bb782fc919cf21e09d6ed17107384a602a26bf817bb4c9541cfac1b907173e09bcebbf86af32a79d2908224ddef1f43b3d4e2427942a9184bebd2904ecbfb00ab020d696bb077d174eccde7df10a6bd3784b78f1061e0af5f557a76858160b051f409fe1317ab73ee9febf68fe787b1227ed5ee50edb259397a1cf2188e4330981db66828fd7ebe5b42713ed7ce4780d488ebe7151e2e0061b46f94e532f4307a59e5470885cca0e8895d54a7182daf327e9cb3eba38896ba1503dccdb44c7094df1651e06370a2ba30c99a2fb6578b84fd36345e0d52188a3f1d88cc030be0792f17a6f68da8bb2bd0e7c86285dc359299e8f01dd0cef6926d847555a06db08c27e9e16ebeca374cbcd8dfc871a09f62bbaf292fca50c957004df6a04ccdc0bf661311325ab198825bb380840bd2e60ef5fb38ae030a077204aff3c7f52b80377fa4c8210e3a4a2f8b8a5d4e1472106733a6a48f0fb72f6e942ef30626fe90e0c15b3c9dd2ddad58b745c6b45e8bfeb479293df5a5a810f4545b80e118665064fb6d574db8c99f9c599c38629d38c45aae348d3d59a423935e7f105846759024af878e313ba20b23eb3115ef2dd9cea547bce638db4f1aa3772e5823221920700e76b84986685272ebb81fb7914365cae882dd0c99a9b872d718f76fec24f0f6bc85ee6b9c8377f94feffc047aa07741f748f09c2939d732b3dae046e0a68060d51e2f82ae09893cb904dd61610aca06b5e5f4abbaf2e626fc54bace3a9e4059113e6bb992dd30d58af07a8ef042b500f4fb6cc1cd8e55a833e761d083a3b0b07cc23c17ff527ed768cac05c498a1bf34d3ef9a6cb781127b326a9465f2cd04e2e6091f29b3dac0c422dd23ca54d1b992e844204d5394452bca2dd2f47c1e09592cd6c516b7f927a1ff09e873556ee8ae2de87efd8c5a2684ca42f8c980a9006e26d71717c42c2392ef59b60d912a766041415df373c535104dd402d290bb021fb9504471832ed06aedd6674638017bb5d714e88bfb6b5b473efd87c24dc50a93ec6626e695286de966932c299f20af54504dc1b3d7bf93ff0bd258a03038058c926928760a30db939cf2dd7cba2d8e3ea165b4ab9bc3de22e4f1bcc48d5205efc78394d64c654f345be89c9cf4dde2a0185833058d2136d0a5738fc380cf0fa623cc9ac6a3308ee2748b6503c26bf4176b08a3793673c78b7d09b86c100d06ee5ba85af63e8dfdda1ab9deea0e6b98a63dac2efa4baf37f61dfa5a12bd8e029c24505f8f7dc577842024f8acd427fc12ae0f4f091826ffc0dfdb0e3942b40fb15fa642ca4cd11669a5a39c90b3ae93524019e380f7df2cd89b62b5bbc61f0d11ef3299ded766472e37f8346ad29945ef635566dd663742279d950effc81d023fd458c5dbce160b8ce2f9c6e3ea270322938b1414a179a65caa8fc94d58bd060fea849a2819331d73bc4592142c17999e5353c2897b402340a7dc653491240693ac39ead4012645fa7c46ef91d53ebb7c17ffaed6c325f266a95e541737660df88f8838eb6f055c838d2990c3dd55eba13fe08f1d0f6c3dbe28f4ed74e67f08e4d79bd76def62b94cfdc06788c8ccdb796b3bf02706724e81b6f24f528f030c31a927a5b35477c67eb5eadc06bb822b018efbad736616246c4b395a9bb89a00d57f42bddc9f8ed6d258a99d3a8ffcf6b71e6e00db6c357734a51d0e2adeb30a317a039d03c1f31086f1eefbfa811261638c5388f86bf5ef7911006287b92d0208f6d4ca4c678e5ade8907b7f0ee13615d306bf43c16a04b38ed6bd5d4bfc40916f42dedf5d9005d5fee45f9208b4b0596154561347f909598723ad91930750b0274ace6998a031bbb1c124ebb481dbf6afc93b8871f8b76a15ce4a876a33d0fe5d182c78c599211af0ffe283074a8011a5531ffb4710628afcbf0b79da9090d27b817f12b1afd9d84400bce134e06ef872554e29c0fd4174fe6ddcf0dd0520e7da6e491846303b78cfca2ee55ebc7994b1960d8c82293b7efd053af4930800870c5cc62d7e2228f6a6bf10df0aa2b87eb842296ff9854d48040a9e7046c350fc9b60f435c30635941e9140913fd2c1b71cededf2fd42fac3c152ccf87d8010a4e3836c427b16e2efddb78efb203ef3074a5c0d55640bcb6c1d925f225204207af9d8725bede99d3f7d8b2b17665718c50bdb49922e56132d7f20ee3b878230a8427acc57e2fe7d52f87d9e82391625bab2bef706cd62b95cfad1d9320f15905a207ff240a7b8098016fe8cdd1f88bbd8ce2f26ffa73754678e89ac17322270050fcfb359fa88971c7777b8190486908fb0973bd521aa6491e110eb9f173c90d23f2c7fe33daaf3e2c561fddc68dcffed8a1d6dff66c2d5f27243ffbf2aaeb0a6838bd775348c53a2ea251fa4c69d3c998aa1f18853bf30eab667cbe91d94200f5822ac7c1c00966763b45c9f5939cf1f9796417c299f76d67e14a399542240e058dd45e96e8aa5c8504fb567e3e30f7b88183bd42198df8528670e338a1b70ace2485524cdf704a80c73026f8c1a205c90fdcce66c073dc6d7ed0487f27b70ea3c3154d127ab3ebdd84623cecafef0b63d27692bcebdae98abfaf342c8a3005c51ee09d0246502096d2a57994071ae266bcd091e766fec6b84193316e3e7e040ef4b2628f8cfbb7a3f96ee76c2adb53b0a0d024649e6c0493bb0d3b2ca9110902bf99ecd39cb6b0301975e8e66b6f704d1c0b2cb179614d98d2a10425853b04837ffac3267bc9555099566b7ea1553c9da7ee1d20a8d055b490d9521813440c6e089bd8bc4a7494ab74d9cd435f4691deeb7f653e18036cee654bca38df160e61d6f37252c317ad6ec1c3056afa60f7c98709df765224b271342916fd5f2f0dd020e56c68e20a0ef3e16597481b692faa22c8a0954698d89c5b6b624853fa054dfaf7564bbebd9eb56f7487314786febcd47ca77f6aba31fac285be00c1cb0949ee6b5965ef7b020ef8ab2f4c8c632b64fef41d3505e92e8c58609db0d88e092ac172befa54262d9696defd21e761da873f7208d656748c8418324db0fa8000764cb1bfe49d1e225ebbfbdcba65fe6f2dfaba654a8d36b0b0c30280b97fdc0e412ebcddf566bb785573d44e2aa58c5e6a9628b65ebf2424f8027ef62794fc07b6d11ac0c261dfe6163084448412eefa21a0192f933919618abc9b1b0c14ac0538fc99939060d17646ba8943c78253140adb7231eef26346b182eb6ca395e20fbfb68b2d262752a6b065b312e76b7db6c0b9b5e13eecbd0aa5fa3ad51c57f808c11e4847a74a4d058f69881c8919c3c916a39219b7d032139f5a4140693d22091cc2eefc1b7733ab74352d45cbbc891b2e85fbcedd2a13db08badbe20e762207b0165d6d61f222c1ca7bcbebe010f9a1070ffca89bf61f77a916563044cd1d02 +generate_ring_signature 4c4e0ad8f2598b7d77a053302d5a6795c381364241b30287731d6c7d063e5253 765d90a45d3bedd3c6ee22b5783e15ae9d418c64dabe2683135027a4e0f0d47f 1 f0aa7e8a8283be703ec37480d9a1e445ce42c58dad7d275752bd8ffaae075f9b ba659efc83361288e75dace789db89084f3970e73518e0e8d03f939eabb5fc0c 0 7059e63d3f212eafab3b404bf15149aa27b59868331ca891d2e6bcdb53d92a07a0900eebde58eb56dc6d314a4a30001391d789a25d5f2cedffc2ba082dbb2505 +generate_ring_signature fa75d79801a1ec122a36862796c333cf133a930d25ba2faa3cfd8401b1ce65ec 64c40ebcf87adc0ce335ff113ed46236769df40a1bfa9dad5ef83d8babae3ffe 1 fa560cabefb92c708a2018c978743b79a348ec2c3908c74c44f619d4a437c2e5 c6f86b60576c5b4f0f03bc9fa8a52482cbdb490b0eb9d99b79e319609fd10c02 0 bf3d4cea305878a216195cf4e757a054a60076aeba7766e2d28964f12390540e6ac9af1e64d31f80a26ddf856af4a8b0a390624c82dc46e27b4098df7cb1950e +generate_ring_signature 7c3fb9bb3ee4c42954cafd7b2b7e8cdc0bc46262e1c01911002435bdbbeaa392 bda43de0bab94f0f16ba11829801217e25b467af70657b746544a48667d928a7 196 3d7deb9d2aeb577cf0188c2f8e3226415e5c4f641e83da62745d8f20120a2c46 4f74fe1cb3af7a94a70f3c562eb98142765e1dfef4baf9f38bd368b922515c10 ba66623e0a2b563d871badd3c5dc6a1492d92839cd3aa5d2c0eb5f70c5f79581 71b8b0d917a73fe39ee5f2f3113a90b9b26abd07f56239ea5b326544359b19d3 eefaa73d07640c1019bd2f642cd443e98832f82d7890c49a2ea7d01abeb71455 f9d84b3c3389860cfd7499393b13ddb2d1d47ec3c782fccaebdf892d7341fa19 1b0bdd06f4dab389ea44e9d7d1005d501af618687d00940c2443b84e40e72136 3e1f0672ef333d52cab1cccdc601f87d010c82611598eea47c8b445823f5d489 a8aaf0ef9003a56517acab915650d7dbbdd51e702c2ee3d84f3e62fe0d8c86a8 eedd4498a0d2c6810091b0dcba02ab3e34e8dd4a4efcf2dff9ab5d91056181a9 d020f2842103fb4d38bc130cd14439b4a4957fcc666dfb3f8e9cea602d0648ba b2dd26957db1ee390466b7ff7b84cb95d8989a0aeaa0ddce683b254390d6a6cc 4b72466a6f2eb480af8715b9a37cde092b5beaa597f4e6e00746ad6d2d05b398 f921877891f56881d08de5ac9c2981c54e6c1d0ae2b16c7cd1a677b198864ccc 41f35918409892706539e055bb30aa7f2fa282f432e13fe769af5b0f636cd69b a2b4d259f5bcc195075eca1ff07997b0377cf25d771716bd4560238839867e8e bb4fc78175626e852e68fef83df8968ca5f5c4f81e1e6112ed6451b9d5f5bd7c 4edfcc81529f6e8888764bc5e41a1fede7049dd960f5a5d197f229a5bd1e707d 58e5461abca133964190a634685bd0e149628bb202ca0d5445c7a852765ae82d 0fed57463cca6186faf243735eb3ee19296afe91474c492f5489f8ca8a9028c3 7274df448dc3ec321473fd02de58ae70c0e27cb95f0720826d7853fdbdee48a9 add935fdef42a91396a34e19659a13fff9ee76b437d4084594e7575a3de21b4e 993ebeda5d18e86f53235876c9af7e6d10f3fb65068da9bf3afbb1abb464f52f 1099ba8fd7f7cc772abe480fa5f216bfe48f46e6340736f452f5e59b10182349 1c5a734cab5a13536221c7f83bebccdcf2660425b358a30a13d49b8a2c832cf3 c0d64b092dc9db3810f59e3fb8feec10188f38d31e227adf80496f775becd04f 755a97fd034508b8bc2702ba1250f5d71ce48e86c7e351e86790d2ef1ff825d9 e47db52ba46c257338824e3adf22706fa2445fe44805d4bb94b5e0ad8c47df7d 6b2ac3928ca6dc8ef22afaa034f66fff380bafa2674a4cf490960fc8b560dd9f 9c1aeea7d25156d1a58b0252704b71b3db0228f17c4cf95ea71b2b076a8b210d 74215b03d16e45d3b251a4c6367b54f8ae438f6965167fe5ec3bb645a5885bc2 013aea76e7a4f7c15338d048a54626c8fed0bd291661963e1f3567923b1eb93f 97c048ef8421b828ac7c75253c51ccd77e427a6ca6390d554abe773a947e24f1 a28841ac91711236b0e4e6f12fd88bbfdc09e269a6e26d7aaed06c3702efcfa0 e0b8ac3c9851b8aeba1edf45b2fcacc18d7834757c4df91b1e14b102b215982f cfa5948f6bb9b6e5678f5fe5c45d266c7c9fee864289ca3dfa79ce248f0c0b79 db95bdfd0f2429c78c806dd772fb934c3acad4fb5f7393e25b521afeb6a35c38 6200b0a613b936303102722aabeb1212b78f6af51735a3f0d96bd76058ce0267 79e29d799a6afaf916f0b5ce30feeb0eae2c9952c1ae4d7708a24067169b4ad5 20ff221440a6bb518535f42abdb4d5df26b3297a92b74eabe8d660510d103919 5fd147c22101ad4fbe7fbcd32931d55b13c55cbad3db68a1ab3f6996ea25d03e 01f4dac1ecd9e01bf1320587ec8674b88d34205ca51ba8483ecdd27b07f7ce02 73227647ce4ebde477e0cd99631d977fa049e3f0d016df930d38794d45ce7c32 829d500cb72ccce0680b7445877d1b1ad89f9d643f7429aa103a655670501f43 3f3f0ada3115f1dcf6079c0867c0c9e3c9ecd63c5d02920b12581cd67bf4251c 9c0a5f030055125abf6ef7631709f06c386af32ba9bc659647f6f6b349588245 56ef91cc69961d6b417ac27d82ab9a7c1b56fe8ed7935e6802487153eb8bba15 6943def5c2ada8d0d78fd8da01a138c16737992bae209444f4b02d5b96f3ac2c 3517f0a7f5203714e1b2da2723af527b99acddc6b48733b6cf23d0cfb6a4afdf fbec53b91419e5f4c1e9ea47c499808c27055492101ce15c85592dfc9f87fbbb ab7a6bc753aad0d5485f332d260f351b75115bcdeb32d1b0a1fd0d8421ee9ebb 6d24d7851f0cf26a3ee3d3c29f8e69f4546c90d9cbd3402fae623e07d69b022d 1ec91aa4a3898b66a1ca297e32940ef75bf5695d62408b11e46b831b25617f09 78b9628ae4e16b74d36ededb48245ffc70fb823ea0e2b7effffc34d4dfb8d308 e9980856b66d6e29bacc3272d8d94ebd638117ccbddce2f8357dd45ca32934c4 3263b10bce76a36c53c3ff5cb9d439c549c7de8d9e3e28cc29ed094ea304ba73 7d15f57f8ba68ad9c179b94a752888ca27480d439b13bf7b22e0f09c30890c29 76806ce6f9f66d86d585658d049c90b7a55d55035e7b9f426f2afa2c295fb6d3 da9630706b7b980a5e17e988ae2ff5dcdd0928d3baa3b736ff8fbcbf5e6acb4b f8da823972eefbcc84a68d34ec0cb69538491d96d8b9a1ad25ccd89bca2eb227 2649b2831326133828b22a7ae2dd1c2d794bc80fc92fb219bd038e20b4087aa1 41dbeab376c0cb3badd9ef47886d8e91a16bd176babd5c93d4222eee59583951 507d4bf45b583eecd177a2d7de1d303e908a9e7b326ee66c384074a19d0e8986 c4327912595750e1b4c774dab82ff6251392fd3f2901ed495552ab943896afb5 de7c4677a46a08361d3748d31c1df86d6a677a46b2fc52fb18cc8c17e631f9d8 bd154b97db57d73e1489e4aece2e891cc90689461a32ece0adc0252f2ea8cd0d f85947ac2d27b169e3f8668cb5d309c93474e2b4e39d303ef00cd22bfb516488 ce68cca1c16786b5f84d2d1b6be185c50bb7e06b6587fd12e88aa0ad55aef1c8 fbc677f0efe1e33b870182a6c970805dbd587f7341577ffaa2ee0c20e73e2b19 062ffbb81d55a0599d07c2e7cbafe69b85f1562731b7b6ff29aef4910c83f8e2 516bbc709109e873a712e6ee502612fd247531d24de032f46d104c6968887c99 147992743e4b8c1e638e6c84d6b426544cc49051f098366837217ec6839b2832 283508f305a819032db1b7d8e5ce5f6e9c7591fe555558d5e18c1d8308acc36f cf7ef848184c876862e4414cd556f787f880025486a4932ed1428ea22ea83e5b 688407f0f2c3c3e1d46c2481b4507245be9e897b6e6bf2f71ec42ff60cac5233 eb82880ae90623b8601b37b8f7f1cec0cf463aaa4c7041de35da6bb1e8dc3383 be18599f4111c5d903acb9dfb4c435768a3003f1ad1428472263292dfede4e14 036c74f81ef0d9812c60b77d4d996fa358d1059b806a17686115cb91fcb891a7 56c13bdcb883d9ba092af5ffab17ace1bfbb6a020c562e12cb02c8ac4aa56d78 c0e519110a5cff1bd58e27124f7c2ada7c4e337bb8c6df93c71ae1e01c862bf9 58596e856040f9da1b9996da7ecc4eb82201b92d45c7fd70f7a365b95bb58b1f 58c0e7620d52079f74559b40e84bd5206ff697e8a96a236017d646daff386596 7d9971daa6d9b033d5fb18153c941f468874eed0db6c31597e7339d0a6abb824 1266519036a3cb221de554081a56e9b6ab0da26dca30dd1cda0a08b8f3ae2e53 7ba8112ca27bf3efe3fe5d6f839dd7c60c904013e0c299fca13447cdf4046a17 23ccdadf7fb569338fe0880f84c81c843d32fc8e51d53d0e6cc06288441c12f9 89ca3b7d4bcbc5d983a9210c8e25a5e9ba55ccb51399dc4f87bf766e87a803db 8e1df71b815b60d01596821f77f20e8ba714e13680e4bb57c3650110994106f5 56b73066872d8a4b86f780e8d9833dfa56419e278da45ba5e2db20bffeb66577 2a47099d3dcf25018cfce34ceafe9d87438ee4186c41490fd403e6e42b5c2d3f c7a3399f0fa1dbbd9976ecc6b4f939c860b4af7115a14bbda763632b1427ffd4 9e95ec90e3f86a6ba34930bbba2f93439766b2c1aa20d6b372f53aa785ac6503 77790eee24121080bd6cd771633b58fea9554710b771021f74d7438a99bd783a 53a887bb398735352c8b991478de995167951f2a985275f2c38102937bfbae4c 1bca9a852dc22ba46db7b0bb5793346a905a6e45bf2a7373dbf8960b5b42594e f03ce64633d857f3d0f4b267c6ed14e3a82664338b26159ff25dee6ca784ebc6 7d8524ed9d52118efce4fadef93e0a0bc66011bbc9877e66c9649ecb16aedcb3 cf65cab677b16ce2e76b2e675de6f2b324c6cc4ea82fd3d9ef646876ffc59185 d84478e2ff8128c73b3afcef77cc2f1d9f08a49ff3f0587a3a6d473f55e0eb53 f59c7d8fa9eb320ed90a86c92a02ebdc016390dca8c9722170dd5198cd1e9579 f0b0ede5391b6c9c584a61db76a0cec84cb1640472365fd278f6cd5da4946921 07cd121ae416d38443e3f9ec281774098c4779d712b0b7a85d31721c41a7044b 6f33a40ba6f6c8fe17c1f387b2a15a440afc0b262628c8c189ec936c7ccce868 93d3c1f4187815f7afb9bfb316594bbb7bd194dabec5fb906b83fc2153b4e02a c0514cde62fbd830f585c13491d6567b0e287b3b4a7ee7b7816b1e91a34637de 175954ea8c7a717813d62813e3af15ccd10935e95aebcc4ac60473f3d24de202 7f3f6c6efbc30d15995a2e1ad3d5baf1685f65463d19b98d14d7c22edcd33cee 8da94cd37f45d0993b6f37761dfdbe699fb00446e37766f1a8d2fbed8919aa26 ca869ef84d9196b289c84b0a0f90281d787a4505157eaa91f4f1f6b9e7443a5e 2eda08be6e1d26b9d6bad8360c5f30c2f47af50e07b647d69e0cb6991fa37898 d3ddc6aaa0883e6c155f2118c5e2501ef8d182593518889792e50b1403a04246 944b3af2047b28c5bf51427b1c29242cf2a2a3f6882c6fc7060086bb72ce26e3 44579a1b2fb42179b9e10e232c9d11e1cba5e6268e3e27e22fb292c75b550732 6713824a19a140834e3181023eeea4d9090d4c0ef7938ca5ac760798807149e3 d99e86bba4142a398a2dc7852aeaea64d710e086b241fba4fb70ffbbfe4ebab0 241c2ea4ecd5936b02fadb15c2d763aabad2e7531892a1448c5cb1b2e540a928 6cf74d18fb775f01ceb9222574d0d704f7efe797b096dae146c2d7f860d6ae60 2df994259a9e9279a8c08f7f37d1333cfeff7d4c9b00e273ba0bffe67ee91d58 e1f86f218491f4e1297187b600e17dcae2d71362b0aa051bd241981b4b694902 a85a6b762a221ae263dc0f8568b23388879e94454b9b588752e1a16105e4efeb 213b0ee105d7df0ca3e893dde9f50a57d0ff2e91031cacf2cdfc6547c07d189e 9c5879307b9d6957f504d1b2cf3160fc787f747122abdc423526640327ac59a1 ec4fd6c81d070ee743061c36568f2e02cd1e1b75caef5aff94a6143005a49994 2002f3a945a9d21bb9949e8747df9e40c1bca2d793eaf979ddae2c712ee6f85c 3f0eb01b2613ed363e129a4dc8d820fe325c25911d8a480a62159c7c53ec845a a8164cd59ffdbe57cb56c7c049b1e95f3693b0536831c840febbce5a723fc5da 154861f7eb6cc3253cffbfc3bf8e5dbd79171aff9244a205454df5a827bed731 224f573206dbc13fa4621c6b73b036a241ea25619c14628b0d9767743b7c967f 6cdf85e92acd2dfd4399aa1bd45d803ea3d0794c76945e2d80274f53cdca5f88 0d3b885c422d389a29a0928d6b09883f4a309bde6573b495c6f6a268eec2d519 428e5249c0abb96946314d6ae67a91339e8920d2d6df6bd2ab01618a81d353fa d699f8d209ef4bbfbde089705716e162ef404983be581dfe016e0c49a9302029 9eda3826a1477e2c37e40e687959331664ededc5fc4937d03e81da5c0b34ed68 f8fe870c71cb553166802db6027fca173c85ebf0f9537430371b2ed7c3a3764a 622bbdaf0a8dbc4430d89147f9092de7a2637d0c5ca77d1a2269081d85c3dba0 5c372dc15cf0c42e3f359fd4c1a5c2e1a019414a400e0ebd18394b96a40c98dd 91bd292ce4c3c68a5f2579f90b4a8b09b8030e31f7e773ee844c720b079d1e2d b2c171f58346747d6ed7b02fc65873f33b7e08ac8183144fa59ef23f2bfc959e 5cf6a61116fe8d0b9f4804e6fbc07af6ae15207b392c8783e404d5fd293c543e c6646612e88da493c7944943e2e7bc2ec432410e0f43bf7dbe63545a306975b5 ac6f8b9b72dad35d5cc636b93a3b4c3dc77b0e2ddbfe90978be8a66e98e3957d 83171e32bb5e628424811c615deecd0f89377cb0279186374c36d874b5ce2a2d 474871f34e82e8aae1f236fd7ece6b9bb8d9627d12878e64fba94ee0ff917e90 697ac8f1dcbf582bef5162d945b2dc2d1a1969bf59161c5aa4e6ecdddbcc6b75 d0ac304d9ab860d7ad0ca2ba9b3a2c2b47d937051992b547426502dd8cb74fb7 775252729d52b7fbcb724b63ef65d2c14607fcd3f57cd9277a10c95aefcf682b 8e3523b50d6ced51c5a82b2aa13f2d498b504ecbaf220d23665f5c55645faaa9 40460ae2f74bde032b31812d53a373111a7f3551e0360aee6a0a5468324b29bb 2a971e6b6aea6dd849df590900fc1f2f527ef624b7f578ce23a43b7da1766ad8 64228053b65371ae1d0a5b3823c3bdde6ba4e9ab15cf84525efc24ab7810d7f7 673629e04c5f248c138f987e346d6f62aa8b70142768ed8faf516280f1ea6b56 aceea696d75ecf12f55924214699453432418a6ed52b2954dfdf1d6eb66028d4 124b855dc5d1164885ea22ebea5849b513932aa42feea0df88bc146b26062102 2043c7629e30d8554739ea06709d746206c08cd7998b6fe7278022855ec96f8a 114a9e312df49f4650807d8d568da9c868508d970437d843c14f23a5b5c745e0 c6150dab7d30c4a57bbf9f74bddc9775d86d26bb9c51496b5fb0887faf32ad8a 24335733fea5102fb726536a5f8ba5cdf20ff37b4f7640b116dc72952bd617c1 bbe2bb771c7bd5fd1c79eb68e58bf4d49e0c45c00e274bd5876df1049242a729 a3ecdcdf9b4af0bef4676d65a25188742351af5648c9233a76e1c75a24eb7a8d fa75c3f4785cb295c8e4b7cf4f95e9cdd80cbc14a9dd67a1ca7d544afd7ffc17 b02c22ee4e0d7d797a6d7212759fc99de5594b4075454495f786aca12e919678 c81e4d5e4af72bb134385df0131616470a16e312f2e660610045f5d3031884ca de87536fde596ff29d65922de1d67d596238e5a03ac0d4e5251dafda619a838c ad6c7c271779001b676bc498d945e90fbf6ba0db589c5b2d5a2e4a74bd6e8d01 4aec1dbdb3e2c9c272606ee77ecb170e6e64b0c857ef9790d612aec230a8475b 82ff743e813f4ef077ebfd66ec61c32a27afb8978bcdbfa69c8b8988998acec3 5a0af108c17d6deead0149accb66158f8246d4939930b8683bab88c17d30d30b 0f46615ccbf367984b3a89a7648d557ac69848376fc56e77ee796dd124ee47a2 5bf94829a2e3afb01140433a9d57e8a59899db28f19e64aa46ff28f5ef64cec1 3e01bf0f93e546cad6eb526f1249c1899a784808ae940f4d15e0a896191d4e42 86e649034e59402ab71e9e7e577d427d917a4257d65a283f1d7ac023ae559fef 29a914d0f4951aa24e2f0aa63b1f537c2081b11da7799a70258d7651a35c5747 3f0107399325655f8b6b33023796e8e55d75525a31a9e5c256f24cb23fdcadb1 6b052938fcf90cea2e5caea0d9cd4e183990f1efba477314b8ed59faa89bd34a 1c6a32ac2c6d3cc6541da9afedd51357bd81172dea1eb2d03a1264a988f761dc 889927d51a0256466f031595d94b95fdf732708efd504b9d22c6ff54aa3d4c59 c612ac8c54634bb1d7cc614a177c03d4b0bcd024ba4a74a43827af518ee607fb 6bd3a9348b3e2cf91c5e9431dce292fc2c2322d07ca626767479639c654365cd 9deebf112490cc58fe510d7aa8c804657bec78642f236df79317165cf2f206f0 dced935b8c9df181c26a925613e96193d52dd051f2001e74f9b7e6348d9148f0 3f6277b2f63bfbe985ece514161452767ade8331f20c40f3f0d0a1035419d533 4d95e5dcefe000349998e1a3130b7a7357453129503423a0f02683aa941cc0c0 84bf7e3dfac85642c1004cbf84ca5f9efead8b8fb4ca98b779e9e5f5445fbcd3 c3aad5fc72c27162dd18f9fa38b371cfd98b70b885ce7e0c428527ca7d8b7569 c716e52b55b748463b4aa1af2f5e178536b0ffb6bb62807d0a51960ad5cff40d f1bcbedc26b50cc7de6f7ef24a1f98d58564336922df6e6f4a70337e6670c6bd 65323b25016172bd154dd7336dd6beba5ef6f45a8713797a6ee39d0979c746f3 4204c9aae42fb89fedb2270e6e29eea1df504c91a9a50f33bb3cabb506412d56 6113d4806a2e2f16880724d125ec7fffe7c1292ff38cb6c6eb8a8f5af171d781 0f022e7bae4d0615d72527758f20be97b6ad2a574830b1d6f6c754ec4b478a1b 25178ef51a568a7d23ce35b29ab02a618185fc258aabf3ddd01fde845938aa64 f06519e655fa5a6ed6cc80efaad8db30ea29324c7dc983cbfe0f3e931bd5b558 46b4b06ccdb3670d7027499daddb0bf9a1269378856cec1700cee91ecda64497 2399b6d1babe50a8c854a3bec66c0e7cdcc7959b03d8ab9f7963063e3ee7cd25 a7a4f6c54713d2f6305c2800a582092aaca32c34b20aa07d0a968cc1ba45481d ed82f9e92b53a19571b5366ae203f7dcf90f19fa2edc87335726fc22dc09d81e d9787436fda6cd8e5791574b3aaa0283ca7c4a5ede9b2433376ff45160b58a0b 82 c3046b44e97c901b3466cdc59c3166a15f398c66264ca3faa98a5ad51fabb100e57620f66965bbacb87c972f387b8b0b39c3baca36184e7852e871c65f5c6509bc7944b68fbb6cad83db7cc1ba72d5b2ee986db7623e03e85869b1dfb259ee0482a8d8a015c2515fe9679b40fb802a373a5fe4211f73dd62a4711101493fc904ed91e4003c52a474f8ae17d46852cb5031d8893342d0c1b1cae31fb6e81d490bfc53b218aa0f57b2295e497298e2a148bcf219a114193a0267904fe39d8b410529ed7398186ae46ffb616e886706107c48de85cb0d0064b5eac81270a07aa403ce8028665fc1207d43f80af777c2cfcb74793a81a3a3225ecbfcb42a44490602fcd34898f7dcb96f0ad809ef44bd860411ea1840454957ea3e00dd8647894f016b6fee01616103e7e5beb0e1054cc759b16cea10a27e1d6a6b652de032425e07f1eb718ffd53c0cc1cfcce1131a0f72f9b0f51fc8ea5c0aa40ccc8780fed3b0262dd65e03960648f0a0f94f7ce368f1fe1a43a696382e75b8d30b864e5c88f047430a1323b2f333c864aa37ae4957237bbcb6350def725e7b377ea5d2e10d6028fe66d7c08edd6ccc2d37ac70da1cbfc66eb916d434ebbf7bd064d864c8193002f1c8b911915c3969ff4c0456063c55ad6b47583ed9a7f3240f6effe383eb6071c139d9b84c3ea59a5748b4de21aad17e739a02c2f502fd2c6d1256d6fa9b302f05475b46f3f57a36150e7992ee3b77ff09643c203fcf5352628ee107f7cf008628a1cae518684eeb4921eac8aa687fe28d408fcd9639cbae20b3cb97b52230b6ab7b5648e58ebf225c28db3859a2eb5212067d83a9e6f9fcb47ea9974c0d20afcf09f3da6dde29e54a33d2377e661215402ef3239924edabf2b6fe3379b36095a20a4ea32e2484445cc3fc9c0cca14adbd810809c0a8ce8314f6d34e287d50feee6f2015e42f3d2b016079fd462715aea6cbf1e668a66858e2526edc5ef6906104ec309e2e0cb2bb445df9e36241d5933ac651c7e752641e1cd212902538502e06ce87f119514b1f2da2cf79bef1c2fe78a34497aa7332e11f13f06d8020c0a802677625b98d235204f156d26bb96da067530cded07865da982f2da44391d0434d47afff7e6ffb6b0b514769508798e7fec4140faa426d2ddfe5ea8e8c1f3061d84cc1bbe6537fee8a6698780139727476cde09a4d9f760dabca66a84acd2088f962bdd0b3d60114dafcfdd211682fc5adb32b2d103aba139abbfef76162a001159fef3826648dc2f71ad50084c13667d9fe58df188471753273b188238120aed066487e983acf19e1f2989a21d80fcbf0f3a635750e013b5b5d102649125067d1fc40420bfcc4cfed689a98ac15faf97da41457320df6d0243a9cc7da4fe0411bd9eb65c600d80754e24dae239df11d1e1df8db4ae58a317538c39879bba0abcfc859636eb0752e46b463944be1610fc0b7b4d660b378fe963d1053592ab0d56d1b0726123f126fcdfa71bb8580c53b97e1dd1318690a3db66b335e7cece0031f06f64ba57c176c3623a3a126426bf3597cb8f153058a6a7d0e463e5c06b0e8d8a5d750dd8546136753e60d982ec1b8d85fad1dad1a90824776a525a09e10de8c3bc2ac53ba8e3c4955031d757e1a45d0ad85ef522d2d55ac77ce8183c950600bb6dae62a86841b01edb43ad2316c95a6146e710435ea0d9fc97eb44fd590f19d92b06e39f5cb524853fe5968a908704df1c1feabdad9be6a3124beddffd025f7ddf27722c14632e84c37828756d7e8070ba6cabdac7fdbc72b75a0af3cf0bc11b1dddda783e360edf4446387f8e2956d96f34e021cacc21dc19a366ac550f87503dabdb62323980bfa31d7d50aee4531a8a803c1fb226c7b5475ffb9f4e0dad6dc42021a1a38f200c46d5ab2d72f9746335816b53cd1107b780c11c41420acd57a582c94fdb4427f08610f2611dca98d640f3edb5df29ddda610c0527800ecacec251609f968891e8518fda3baa395a0f0c07ba31a44127e990f4c5809409010c7a0b30df7952e63195c2ac3f223202100af6e9178b25c1604b69e6707404aeb82a2080a7ec26ed640c7e60e7d016e02aff112ad0cc40000f4c8395143b0e863586522b75d2d17b1ffbd0b5187615acf1081bace08deecdb8f876eee2db0d7d88764f7121f85ebf449da2203f3d81ca0c421bf27d4aa0c513f487c363310df6f9f72946b98f03c293889f05807dfc9c9ef8b1ed63a4552b0a1aff4c9b46087a1c0200eeb7f8efa1dcea86270d2e4af7b9a0846d4c130b88efc55ab00e0d0a49dc57723b95676210a7917817b8d455310eb36fda6faa5feef4eacf73b162067fc9a42947cf34f8310ccdd4bf5a2ade82ede0856c1e17298cf894ba88edad0eaa28aa6949de6c74f0392f1c4d6a57faec8dcadf32e26f5ff72603b6cc943a0994262d59a82cf194fb93d3a0cce6a8f4b0cd984ed3c4d7d5ec4d295097baa10e5a3abba0a426d1c6fc251b31f87ba14fab8acc23ebc0bd31a0711a5b5ac9ca07cfe0b01819ebb04a3ed1221f8f7ccf1fd1b7323334e69319e740338cbda9db0c7116540bb42fe3acb1395da3701d97ba64701f7f5b01e9266ace35c9ea95470218367055639e99b33f2f552ac1754e38d407906980b94034ae796d95dd6c030f0cea9ada0c6bf305638ca222cab96add245eabe309c845c595f8f025d23e600023792304fe86010e89ca50b7472517a3ea33376a7a7fd3c0cec8cc7035d8090a0e71635c33f5b14aba950107551087f95e921b46da4195cc48da0ae3c0e5ce0311a0b9d7e12a54d12cc703bdd700532c3248f2724ca23c01041e2c63b9523e028b1cbd189664330c746b46ba81c7a469beb6a9372a3484b3b1a2c5c86bb50207bd12f7d66b346ded14e6db68deb4529d573f00e29d9c737343f48f9100a32802f83546bb43f2888dfc1ddb0694cff1b8e24a7e4f5fc36460564f96a7dfa2d9058d6a085ca04696a9d5e278c10c94e2df302995c785aec22061fdb66d87d14f02c9852578e3c75b2ec58e7909c25722b629bbe39c11189fc4dd7d0fcb9e737309985cbc207c57bb198e5a09e2b2e7a31a7346907b9d9835740c34b284e315d908b475c3c4eca4508e8a169a6f057fc28d6a5922990697963b3f896e571d2f310a58c386682ba374d597bbb80335d0c5fccf1c77ffa15f4b72c1684fe8f1cfaa02ebdef82fab044a6f8ca690f207f455ffab777cddc22e4a8ab29eb2af9ecf600579161f91e2e0cd5b769937066fbd66c762e45ec01985e0eb00267b82db081203166d6a41b9e1c03bdcca8d077a21384eda20ad684b4a5f553b644a6b8707b40fa97b4bd4bf48eb4a7707911b41420dd8741563c705723c972f50dd6561920f038beca2d633c31c7788717f2972ab347e7ac2d7d8d7eaac5d206ac3069eb8f508def2568a9487775d61f3f3a085967cc4c086f36def4b4ae9fc486dcb9d145a088b135d8c59f6e5c07db2d994d69c8e033ce1bc46f14093fafa6dcfe7e31bee0d7bbd68a2f560105f5299c355b9f57ea6fe3b41f086bfbecf7072b90ad9a53e01b8bb2e6d51fb575cf0091a5953c74d6b1544b712bd3b5d6b6f629b993f71be0461cb0450b27e8025e736554c94822a0e246a7441700cdded75307e81508c7400fa13655c949ba22bcf757f5f6760dcab8a3afe55b4d36cdef505486f6ce38604f24944e063602d02adc4db3474edeaa7f108871275ee3b3142aed3896b61620aa16b9c3b9141da7a6e2f01244968a99c97f84c60a36c2ea1885d01ccaecbdf0b6835aabb7c099d2d0cb83e3212b39484a440b0b8a8b0b0e6b81fadbc2c155106807662b09e1d9271dfd335133ebf1a2ab1bbe74a6dc9e42214c45e0983251804f1a85b79b4a9427e8220f43f7f531548f0cbeb074a0a469856ee9ea3f10def03429bfa6ea5a638505796a86c37d292cf2229f7199583cba4a37cd30037499d05526344a79ed005dc1d1928e86a021f6913df7448aad9bb681f47c07f11ba1b0ca492e5a9452532f45ed466a52f2486f5f63d879f82c4880d5d40e03ec2f2440551839f4acb75bf239388e2925176230efb301e53efb13e500fe7acd4d2bdb2031faf0974eadd9ddb36f8f5132ffc9b43f90c3b3ffa5fcbc4d32ba32e1e31bc02948f3ab608d7dca6158e555a92ba95b1708358c0486b3b9bf68c166dcb65380678655371b5c2eda2c7b479ed9470a76c8e9b3c2bbf5c22f34270371d48e40e0ba19fc418674a4a9a5a5118956ef0fe35d98760b1861737ecc43f941205077205205cc6cf354daf3eae1173ec197296ed018227013ffdaf9db645a42da368470d6c53e736441fac1a7dda029e94f68d853eed9dc7c0548fb81024d45fb4657a055dbea743485ef4749780b01ea59987e335aa363491a0740d75705c8317c84504dfb6368cc8901e5ec7f69908a910fd5ee6636f96d2f25739f0c5b226cca5b407631290a67e79313176b469df0f41cfaecfd6032740a283da7d0d9d4db20f090ea2d7417fac4065f797372d99b4dc046e53de7d1f18e51638ceb0a98ed0a81b07181ca8124edd86bdf213640b57a907fa3d2d0432ba8f8494ca363227c1fc98068f8b96b7e46bd7548b68295a2a15c3b3b046371e3b5e14504b5d90616fb695015d41d821f1be56b5ab42e9d54cf085c24a2ba33154ebdb8d2517722538616d0daa4fed30e8303c3e404fcc995bd7446464f180ea7ec9f975ce74e3968641e506c9474f56c0d5dacaaf52e9a29a88e5105992605b4abe44c428dacd75cc77360dc39705c50def1f5f3161a62c0c80e75dc3c0cd0b6b7e0df64e7305b844bbea0f275b74b9946b4acb5992b035d38b58d7a9f4e16941e6be1b8c2e29dc801eab029e0141b5abc0484e7fe0199bb727bb34aed76ff51eb7f794c550d3f89d55e70cb54cbe4b9b39c1b4853beb22fad7b8c13b5eacee435b8c0af7b2aa10d21fca09d8e2133173eb652e2ea2ee7c386398024c76b33d68c79946e774afaec4b45a0fd7af0526fce288de2b375946e65e7e7c29dedf403c27adddfddd2fc84747e90a96fefa397169bac7d0717ce49a450ba86232faae76e7c1121f36de845ad76a0eda51ab547f17c264c2a0748bef659d71d4dde4fe090947e54f864b97108bf10a6176571105cbccd03fe904acd9abfddd737b4ed4c778a8e02af6cc415c49690f505c73ef3ea3e3e31bf4444600667772af7f23731890774aadfe4c39a36a6507a082a646239be51d8a0490d53b631823f488b4c74ed26295d66e4622d43a9e015f6a671bdd28c768bef1ca14b24e02c35f9a8e4237a677c1857444f6ef0a8c0bde6cf83fb5ccbf0d264d939ec88d434f60d78c682bdb1fe66ab02f0ee0e50f06c3a6cb85bd9858677900bda38fd541866f727bf04e04f14e22168b8b9500e8078d5b540dbb2e91bf1ed34d917954a74fbec2fbd78491673a4ec31bef383b82031ff314f852761de725d892380de08b842a49309edb4864edc26d8a6417603006373f3fd3682d9be4f8d69d310f1176d692b704640407cb43aecde4251c432d045e66da5bd8db763132c2943c8d419ec82a913778dbee2ec9b3e8ae52161bc20a27bbf6c8aff0a82475ed5071f8b5c7e3ccc549fee25825abc749bfd771d9ef0a2f35b5647b25b83371555ac8221d814573386b5a6b73019cf6a8c21176afe40f5ee24d2e7efee5906e966b8757dcf98bbf5e2936450a207c289ef47226d933017e15384c7d3d8f1d400787ce8f4c3da1471f05f18a2089d0757d856ab1e15d0f41453f431cfdff7b6783fae40673acbb8930002aa115d1b173557c8c4087b40a01c63a382123fc6f26b28ad75e53dde45f27e1425728108859a297815a4baf01b220b05e789fc09b9792090fe91986576cf0280a916c72bb8583ccf2faee5903972aef86002ecd629eff90548ac10c1a1284b7abcc438bbca0cd0f096857bb0f1067a764476c4f7c97011a8f1aa4eddcce8a396c0beadcde37fc15da2f108502206a9605ad7a54249fd1765b446442578f2ffaf7b4001c2071b03fe5be49e40583a9eab074267283475d9f4f494138e6b159a241bbdd4b7bf3f16bad4075800d13fd7ce92c25542ce9dfdca846946921590975e57da0e5d48cb49966d57b550543f1b3a0285d9c7230e62097d418fa79a4b7245378fdee40c3454d6eb213f00e5ab1169927e696da6f9b0d459026143a8a0713a31b55049e571e19c5ad427a0d660f450aa89032d433e3516a968a8d990a38b5db01af52898144edad8f39c304b4af83df4e2dfba14febc75d829f13349984d71bfa7bca7f0ceefb6315e7900c645268f5439c0800530d10f8ad07011e50aaa32f17a7f4b40881da9f31337f03562bb186bec0f06d1f94a90948806f3b0251a407b5aa2faa4704eeb0478342099254937360a83a3201fa4a2c6d8fb1f1601563c783d52966d4a988da36e73d0daffa9d7306adc6cde0aa51352a3bbbc594f61b5fa750441b293810d87bc52503337159fade850c8e86e1438f5ca51378e785faa1de0960f67940adb9fba8fe0cf563b3686163f759b8e95e5d09f93e6b62071360f77d6bad08a9f2db8d5a1d01fd406c2a9918e3319a9a2074950f7d40a5560f57e30dc1a8165ca70ff01ca404681e67524cc8827acf723df2b1c05445e0ae822b879f4f1689ed1dcf8748e909e37ae8013ea05607658d73506192601c905d88051a82f5a7c93cf2405013cb02913a16c9433500c21d87e4c619c4cd0779a133f86d9b6979ce41d2e74cebc7076261b041335337a3d1e5172819fc2ad48d54f82115737cc798789d5e510fae018cbc96708f3cdfdd5df3bf18409a075632f05929d6108760fa5a81bd46c78a015d94dbf62dcc944890d573e4b8e551ed61dcff4f6a2474b71cc6b10d0f96fa0750462bcf57bfefab0dfadb15b5a2bb860dd3a00a3681c35a646b1f81001f820bc6bac2edf351d525ff409d03cec61db64d81e670740a2974195e156208b27d0e4de7540b8793618e2b94c8708bdcd5c274aeaf2c3ec1a37d00ce263fc6f91e0b272e75af30574ec0f7c1e23cbf98986c04b0813545ff7cf461972607d485f70ce7b7aadd0a64f7e6bf60f28cd1b3a1ba96c6dc55b16dfb1cdab3b13533bf250e5997a861c32a4f46fe3c463dabe4b4c574cbb618ae9f1e8ff78baebb6f4e570762f69be45b9c31eb090f0800f1a3b345ff03aa936973dc055f2fadc0e5dd25004274fcb701327c5c13d16fa15919f7939ee45f5d415e5f3b671cf9e7e7c8d90f1e0f703414c22cf144bc9b673a400f5af0a294a1b2081a28b5b4fc6cea86d10bfd485356bca17c9357ce3d7872736e8fc56df7bcbde4624bee3005e30be81808e07a6d18996f09c9a1166b271e94637dd3f916fd22bd79e2aadcb120d8be4700a104f5c4df66d65ba7d88d5d9e385314ad5c260d5ad947c1e6142e202a7b9700b448c29eaa259a0ae9cef0d8a4ba51374a6be1c01a4b45f1e91e2a566567dd0a366853b358e57e6588c34492ca19d6dccd778c0035a992556397b281c44f48002fcfa2da45f58443108fcd1affb1bc5fbb305269c783096bc115de2f375f4408c1a9537fb2edb86265cf2da37cb7e53c4b1106733fd54a2c3bd9d1b1d1ebc206b2253289fd600e70c81e99f8df555a29775c670c23d805c892fc6c8463f1230ad5d14de955990559bff18c4a0434b031c655e38d8026ffb6dc0db2cc0c870e06fc10eaf9d43615a753860fc453022a8a8d784cee0eb52a2a71180efabef2380c3a6950c0522fad2da8fad28029c0a38a8108934929b70b870575228bb185f30cad8e6b4d67e59b48305d6a8764cc98a097f51bcbf8c7a6c80a207cc80058fd0cbe6b20b4cef59d8f5d54131fc238bf1d97a64412d817f86a21b22abbc17e890ec147beff79438a5f73c5446d110bc26275d769d06f464633262cbb12ee62cf01bd2fd4c9ded992f30438cd41d9468d59c7121f7a5c581c41e1bcbfcfda9e1602f42554a5ada6275e69b8215b4bec1e55ab7aed6e2ff89d5ae7aafa0e6ea91404b3c7ed0066d088a8c0efd269de18ee8e48bb4d776f8f6932db0f939dcfb9fe0780c61e39d1c867a22c97df2d5eb4abf8129d5979c1347f33cc30921bad8e7f0f62d44641550da0e36c65158498b6cec865ccba214315c76c301e1a3135f33b0c062e1b38864fc2fb578c58a69a34271dccfb3f9f2c6c9d090c5290b87a7ecb073d2596b07b3bbddd67fffe2f287bb529ddfec683c42aa914adda638116bd5e0cfabd6459fb4bd7cc5c6e7c1764d838a1012b187e9cf8548886b1c7d593444801314db5d4550521b39bed0fc19eb1141f10ef694d164a2f21b5110b6294a3ad060cf5ffd035e81000f4a52574a7fc0265ab1f95bb73d996c0e6857ca53b073e0e405207a4dc5dfe5338c8ac19bfbd18b9cc8777cba715daa8639941d559dbb20727611e9b270b1d13e221b24984f36b8b4773ace4628a84a0a077884214d4c806c0b1e29e56afae63743f7747f1e6301d6292df851ec0d0ef40ef438c0b4214083fb941088bff92d1ff2c18347101b7101ca97d0fa91aae26e257a22daf40ed0f20e6fdf1c223b932850898d3e55f8378b97f040b833e51a8be4ee6be54a7400331f0a931793912ec77b55b7bb18c0354fc77bde55465933ae036ef6b83cd1208f99fab41724bb5dab9c53224c939a1d87896dd01d1c7ce92becec09f8df11a0bbfc92ef6658335cddc8f60d2bdf559d11847f86603ef1ebfa9db447d7e63000214d828a0014dfbaa1a89359a43d0f7c262d8fc6f263141779f9d72dda7793d0a795c26f876562d6f666320956119feee80af68e85278dd25ffe320f6894a800fa1449fbdde07184be07ec769163ec11a52162bd69b3675c32c3dba94b1c8a009f2327b4afedc18a722673117402ba9876c6ccc81e16b62ef06d91664b36d4601746f18ebf4f4b6a380c1c0717abb6d89c0a13ac5e2dd9c44319bc7dcb8698a008d86f97a4b4f28ebc8ad83d0f8d0a396d5e1811f5df2c43e041b782b73595d04c76fdb737ae9b247041f51b8d37f4f06211bebf470d93d8aa560c7a989f65d000c43dbe85fa194a36dc00246a3f4968501ba5745309870388b603aa865615102c8d5abc6c8da5fe814451d0b4cfc2e9bad59b198ccd7bd4ed80c2b36c509440dd8d9e593f86673302b2cc54111814dee6c3796693627ca43093631b138844203ceba574dffd835da3a5be309168750b242ffd44266a2b4667b9967dce3581a019a7ba4184e89a37386b715ba9c801368d243daa408e8fced9114dbc31ea4c40926fb1a2985277dcda4169bb0ce5bd9cef3c4301fd3de21c0d37638912d194e02b1402be4317accb61c411ccc0446964e0e3a9795605bfb129a34562dc4969a0425868ba0728c582192f02bfb3dd37196f256c6b32c96cd0a1509ed6dd965eb0b90dc350a736806acfade7c23055afb38e50b76afc0cdb1f324e79f852db4e00e0a89f1a0318739d50b8a1cd6ade01e3d6b4b63bcc244819ab9729ad0cdad4e04a865f04a59c69f7d82275ea317bbe574e5a42aa31a733e67d235be708eaacd0269fb932855f30524f44f034ddd95df4c19ead1fe50beb5a709d822a259b1590cc32216512d49d4edd7c0ed12a2506e79185bbd9e0dc0b2564379452f723ea503b4d30e235f124d92a6b49d5a56fd018e90e8dd3d169f3867da7e2e365b93840c76bd3914b3318fe15769e7484a0712168583492ef40e5cdba5bbb8069620760edfbe37a8f42100eeb4e930569f3471b3492d29a1faaa696545c50c5a1bd62502e4b7c2c9c25efc4f10449a791d6bc71073986b8443c526c4f0551247bf5cbe090bc72feec42635343a63139963dfcce212f0d0563d2a3d4488515708721f02080be78c58ee2e40a014885eede95ad1cb825de8683392a9c7d0e0656028def30f6c89ce1c27c0342739c2a8dd9392f0a4306dd77547564c44f68987a0e6ac20008d23a013da1d12ee937bbdcee0b4ec4cf0bfaa13a4657fc5f3f694cda123a60dbb44b5b432fe2fec21aedc83fdd66e2a74023da8430ed92c347f247830cf3603631ea97446237ebcd26d508b62cf6d9e276b1e92360ba014e5467052b15030065df45fc5cd67a5baa144aa60e68d36855ee4998889604998336cfe85bede160a19fa485ebedbf33ab73fe3a373235e40baf7bcd1ba480b9aaf3f33904abdec003e1f1a9a13befeefc8d4c3c0c72e7c246f6597e2b22be2e5313e1d4265d99e0c7babb43c8fd6ad5edb2e959598d934dc1b0516d02b3e5aafa75817456bb67c01c4dd6d38a37bb46c327c8514e14b0feb3c65c50f9c43f9562804f10e5f5ad70eaee1c9d3dc8562b2e09b6919c8251cbee6af2f122147a763f7e0e0fc7077b50d45a93e491c37b8cca7f49f1dfad2cc52add97631691eb1c80650219de43ad80569636adc4760b89545e0a0a5f4fc75adb5b251b3a1b97501a6c4fe55e82e0307785d62b193e4ce8410c5b39d4861058a221b77f64a8477062c4a8a49bff7b50088039bcdb453ac577ab0952b07bf05572dbfeb1920110d172e11e266659f7305276e467f940101ad5c6ef06cffd3d9e8599aee30be02845eb1731ca2fc916c03f15c04a6ae5488540964d71123d6fb1b10405dabff16081e94394b1fdaba860e9bab2fd1f4b34aad547175b68d461c209019dd2c6ebd29d565ee0a1d1f312202cc961eaed416a4495985140d947180deb28e739c7a86e3ce80c6010add9ce20610865f9518f63f66456c587d138293d60e382c2532198b1e1b8e8c16dd9ab2077f76649dc6127bacdc33e1265759d48f93c39bbf798f1bf116cb5e82022c880e1d0f8968981cce1db021e00b78a4516eb360631466892d80c13141805f806b063a1f141ba661e8cd96a66a783d9d2bb762eb626eff7cb2ac20bb826b2aa1300f6203c331934eb93f64675f7d80fa540982a6db494cf240b40df950ea1d06ee0beb03d1b7304113671eca73e5ff0c128bfd75a10d840701dbd631feb64bcc7e0189c74eefc3d069b55d656ad81af5fdf846bf9c169ef81d790b98d9e886f2020386e2227bb587ce4f28f54b94c492d88f047a498d51e941b5e6223a047b1d9601c76eb05a8b14faf52011bf63e59dd950eee58e6ab9d14af92f23a13cfa06e40dc4338f6183f5e0281c341d051ba3e77e2c336e35db65efefd5bc829352de5008766801bb0cc50f410d1d3542ab471a40fdc78e1665cc04b70e0819dfd6817e0607a026e7bf6f215aa37e7a242d74a13ef46233212a85bb95a13296585c154201230de56079cb85b9d3915166929548feade266b69df96c9d1734f6a115396c0319cef2f5c36a6bfa34b2c0d4762dcecd618a27344eb8efb9e4960a92eb724d0339e06ec463ace0ed8d9f83c5fa5048321908ad619098a8b4fed5c823c7523d0b7df8411ae9b141dabed269568f289cd26c9181c6141916eaebab2af15fbb190869d2e1a68697d0d82d5f5bf72a7993995ec925da9181dcc7e70f4fc55f16fe013e07c6a74c39b454cac5dba654df4adb31849a7b981585183230329a97ceb30baaed133843bb26454b15b5930883a5d46b0955ed313b090e6de8ba2790b76e003d2404bf066ab82bb7581d869ca53168a46aef1709ff96b682d0128187bf400ac2afefd4938645991c2222e453159bcf7f9df786d4da14b6699efdcb97df0701e16442d73100467afcc6cf64832a8651bc527c0f08c4476920977db779b5c60c8bec26cc1adec998a601a4e003beba0f3762bee2b70437130340d56cd7bd7c0948b394ca7f7aac147b77831819cde8c6db61efdf4df5cac7d6481656db3e2604afe2a85243666e26a6b52127ebaa9c4c8ce62e732ec86a9cebaa27d2ba1e5105bb511c1b56cc7aa42ad24e2c631dbbf8f2d3bf42422411494539ea9abb16aa0f899001604bda965bf3629be7eb7998e36cc9c7570a016290e29afb3d8c4d5503e90e0232dac5803237ce1a738bd18a14ce1686307f29576ebc0acb2d5e9e2701c24aabe397db8d3401bf0e91ec8b55e55e115f3a993697f170a54f9a5cbd6a0abd3696d56f9eed21eb86bf6419d59ff35652552d4f90a0dca304b760fc5fd101235231cce4802982dc649f38b51a4ac5dcff07d99ec7610981a519e69826240d72aece8bb8879b86974c9d17d8d468bc7f40962b385a02c578297c56d0fd1a0646cbe64f6bf8ca304603ca241b938b06d6412e3f2aec081b999e9a13221bfb007a0ae3a4a1b4fe26ced70ae972ae08e8684a6c02881420cf1886081a22d92808f7eb73be68ca0d1c5eef5edee6d264d335bad8d38675e6a071b5775f3f32700038b35e7b505937fa31a0e479a6e9fd0324dec74af6d13bdfe4f8e05565882609b7fafea7c3c45d7073e1bbdab18214627aa98292d93aa7b82adbb1cca6c0700c96f218588018051ed0afddb6f3b39a96336ff6d9e2da7044b46df13e7d815f0e36ae5c857bd873e6193b12eacb2eaa7381d7082429b932e7cb58a0a4325c0809037df603be32385b2ebab08ce07fd4c51c66c88aa1b497d4b3bd2ae8a2270f01459a8811576fce6a8f477e9ce16222038cfde19dce9d64dbf019618efdca100857680d301b7f67079f39536e7857d750d2f5c6782246eee663f8a544d342620ddc1f1a88c9da38a67eb939cca8cc9bfbbec1f653d0964ebfa8b6373d6f988c016f2b673cbe3900f0094d3c160995450d54eebe0ba9850d48be2cae3d4984db03b3423116443ea6e82f04dab1fb425a1d077f927d75461eba92520b93ab74aa0bffb148c2ac06acc04f90b9cf4e282b4b5492c0232b74d381f99f457c6613df01ecb2f4e32427bde1ef6e1244aa61009990fc247e3f2e95231a3f8d709fd9250368c4316fba9074e6531594c9527f2395b78d77a07d29cd0cf607d3ae0ea983001a48c146e8a10d2464e4f9aa8f5d51b4bc369b85032df5fabb6c1283e476d70ce4a9c5b196577df55dd1d610fc3acc5b64b5dbaffe30b3790611922c304fdd0017165bb5b163e57a9e0f05f96966fd602371c6f471df0e32d26ceb410c44e6067deb9de591fd90a2b433610b255b22d5d2598dd5896c35f2f3cd90d5061253045b4ac689f33a7f42009ebf2f59cc74b0103058bea53704342b57e8bba210a200c131d5365395a83b91222e5440e6a696f57b39a1adea63677a452685e88dd203c799a4a792d1f1c16ca9239c7ec00107049d45b124b3f8888940c480bb968800d839779bac97e86c4e438dbaf48cd62471a227339c2d2c4d95de56f9327d080127e0a7adfd10fe06342a08f5182597376f114ba748c7490b49690fd9047a620f5baa78f3494b392e645c5976af1d036e70ef7cb21e4d6ab3d702ee17881e72097fa5e0c1695bfec4f1a64eaaf3e9186b79a441282b7ec4d1917921aa087013049adc324fe14cc4634f56e6e6619a59c37a1ce260b3f089ff7ac6225b92c7100b1dd697c05ceccf65ec1acfb57ed849d8a852cedcfe00ddb20e02f928ea1a90015eb5cef88cc8a88dfdc94707bc2f07d79519fa00cc9e387973116d56346673040d062b5460d2a20ea2e6fbe07372b981736f8ceba7807276b93932ef6b47a20a6ae9da770c578c56cef6741ab2cc938ab6ef4b2b43f67f432710bc0a04cce60b5358714793a5a1c8ffd54993e6cd4a1a1630d697c82bde9275c0a8acb4e8210a45813e8e3e3984e259ad6f224d8a40e8c476b2f2595adda2a0bae542b11b9309c2a3fd7d38970674d0c38de2614dc651362df4103698aed29ccdcef88338340f62eea7b6ada86f78e7bdd34334230e9ba1e10852a965ea50c9a500aabd575c0cbc3e855dfed20de9c9ea1dedefefbe3790fd88ac21078cb61c919721ec050f094be144e6401b58936c1303cc43a4e3308f1ccf39b44d4a413dc4151afdbf050a0ea5e5aa4e4c3eb6ac9ba32221fb0d945a4e555128d03e591a029b6738d57a0bcf7b8482032bf741b46dc0ca5fd8019cd721450bf5b5981378a39e6fa4a39b031cd4233aa74834bdfa15fdd65d798fe8f31fd13d428cf162c97100181a3d4d048dd9d4477e80d6ed85dce7496a23f1d4ab3fa28ccc9ca29e57552b4d1e49200e243de57989bd4f52d3bd59a00c7f71c090f743d84a16bdb6f673f34c065a3b0433f863405d24ac07a162bd6f7679049bdea8ccdee7930aa0911debec26de4a097b2604683cde6475217c3477615bb2a8d0dd136dad3142b65f5a79e923becd03ed30c2a9fc83b62d3eb7e24fcdeae4a11803a9a295dd4cb17e3b7d5f61781a0175c02031a60fa336e3896d497cedf922c5b93eb9b69f1bbfb7c8b8310773ab02e8f0dd96bfaca269e3e2e494a28a16cd2576b6a94e2d3392cc4e3bc1dc30400fcb46046f54332962e59f82b8dc3064f935626b06fe34b6101be66ca92b846b00dab39822424ca36445e17116067a60bd9bd65d1c1d06c59450e9e7d6028390007e11a696a8b3ed26e85ea873ee849feb11d3c7a794dc348609169ef852f20100402e537262d78915788d6ba8bf39bfac0cb7e8290c055469346eae64cd6a310523f10bdd4b32d6855784156028d1918696ff188b5f1f04be078ef3b64db17a0fee064c737323c1c3bb8eaa6a77e577c5a6d7ec353af451a29fde6f0555d679024e542d0e2e0c6256ef74006a9fdfb36591e357ca051294d38957677c1827030a7085d7c1f272bb302e6ee55b79012289afe9ba505896d234f31b3da41ee7480999c585c425953cb57973af592189e7d38c7112b3a81962294673957bde3c2006b52b9c2030646d43c276a03592e501fa7b590448c3d7fe44ef835c73162eb200795625997643762ae42dc7cec3729e4c32820a1880202c1a9ec439af5f1f880685bf74beeeddcdfc279a1526ccb0b82fa2d12f62e2c5b1338284e93b47be590abf0d73d4725c4933d265f397661ab8249200ce7d01675c6767a0e411ff1b460231242465e5295bd9fcfd5814b2766bacb1bdf8bb5fe5fbf6dda3a7943d925806c1737ce82e4400f4c7412d819a344488756a292cc5a27208197e8ed95e4beb089e627ffc5ae59671affe6f12d0cd62fc4f7888de36f4c22d9cdfbff3620a270357aa906800d87e3a3495deaa701999a652b82aa581651b0f3ad643e3d31a090f30486bf44c7703b78993482e8e0bb99faca2a99d108848711f79b32e37b10307e6c8b2041ec8e07aca9e6084edb35b0e641768fa8acbbf0bf3e20b047fa7ce03842632f0dee74e53bd04f4d2bb6259b97079b5ce04935021c0d79f06fae8c0004d11387b1b2d75cdc301e4c280c77d237219acc0719cd9dae4045d0e8f828306b91af39536d6f850273615b51aca9465e8b65eaa7b4eb05ef11ec9990161a60ac6439bc24d1c0fb445acf4b4cba99f4ddb40d5dd82657c518fa95a0433f4410c52492f96ffb7aa814ecdf0ec685a66170816217f125a4a1fa696cec1592fb701f8de55cb334e9f78915a1e3dd30bde5c66c9f6fe1435d7aa908e0245e3ba930bfb0f02960cb3cbd3d331c2be7e2dc1314a8bf67898cce0bfa63813522c4b900eac9f8fe14d31f2f71cfc42d5b0bd48ce3c6e8d5662ffc9db50e7ea7ed0b50200525c20ff6e6302585cc56b729891c98cd3dfafbd729f7e6dbd6338ed4a8a810230b8b373d84ee0b7a4d3c28614ac3f934c366a0487ba62c4ac23dd4224707a09ee2e2c66ed53f84b026c17fd2396a3be1be6aa6150c9dcebc8e13c02a4ef700e34051aa473ae5df96a0a1cf114c094365893059039713a22640e872ed944da06b7afdf02b93c04aa84ea90c695774eef32f50b55e6c5ac2c6cefd0a42025d0073724cd5cb89ae42b26ee3ee9c0c3e2a8e7cec2457bcb77aa09d7e3c9593e7c0fd05492f53b24c297d0314a93d9ee73dd7393ff197f455e73f633edafd8d62b0b2324b4295d1b14d288f78d42cd87ade5aa2beaebf9a489c061956e851ee28001abd5bef6f8d865c620d3e29533fc149697c14a5d5d16e7142bf3dc413429680892c5be7ae973c0db15461c0a7eb4c7c6fa93e9eaa9bf635b3dbfa3bad5251a0ee943a16be6e9ca58ae2758c0e73ff6038e515a5a926e7617059e0867c85e89078d2e2ad5bcde287e8c860edbd671ab942778bdbcc1c7921ffc26d337d9af1a0e115f43fbd394b3cbb5915aa47eb3246af5ac1bb52a139b02388feee54eb47004e554bb49b97774c8097fa018a7d56a68f670b35161d2ff5bab6f53bbb009350ce62227e052748a445c4a4fd56d8e69c9a05b8a36bf049eb788ccfeb4e3385901912ec481893a01db0ec5a8a5b4e40a1d6120231e218018f8414a84f1befd8d0bd8de86c55c0ec8bd529dd4d3e7714c9a98c5ca227a337549a04268f00576860c1f75cc3f4e0ff770e84b6840459a884a3dd11a5c4a9f2bbd477dff1fdfb2e6053ec432a735095e59215e32cb897e9f12cca62bf65daf38660ab17dd3985cb900efbc7323d1ccea72299084ec1ce0b0d58a9419be4d65d09bc24a8a2dbb7a4606846b000c73ed19afc1fee71aa3c864cb51434158c696c9c76023b5961e2016041e7f621d17a76e6481cb8bf28ebf04c213c8647dab68667b9d596267215e2e0cc2c40f8101abf8e4b919755f51c8acc58da76fcf3ba38eea4003bac28f366e0360bf5e632a3538ab92e07fcc4f154660e3bdb270dac301f92e519d46c3461d045ba6befc421ab03f907849cc21563e05ece092e26d725c4974157cd55c1abd0f8e4946b723824364b5e2898dee78031483954d094688bc0925e486c4b9102203dbbeeb14d648db7e79de18582de6c5c7bcc7a1fd11daa8586f685755a4d1b50acb47e43bf0f7b0a919c7bd4a9b6ac8b1df838dbd556eb327516a04a7276a5d006fc4e5e57eb933a93f7ac57f2143f3a4ecbb01bf894e50917e8cf04523c1470a141fe37ad231f9a074fbdc2adf2699ddb43afbaed1894c27a0e53db3ace41e0fe58676b2ad6198c31c31d4c2c9aee45f3a68d3085eef2e7748ee4dda795a390d4e17ce308d2059433eade2147578cbc92bf877179e62d11feb03448a00daf80e13323c0796bc7ca639c95034ed47310b5bf98fadf5aa628fad189294434fde0e1aa0f413ba0b9408aea01da087f516314d5b65ac164f6242fa44b664c0918405c5134d7e5a1880a358e4912d3d9402f55f2528e1007a2875f74359223b6e490b7cfb31f3b348fd689fc57ef24560f0b20f4aaea47f04994bf5c8cdaba93e9d0f4654a40603a34892e68b0bde87570a04f5d4252701738e1161abaa507b4bd40a7ca706b235a061dfe9caccc6ab7a77f3959d28e70d311a41ffa12c8e0553e208d4a9ec6d841063fef1b6a36d98bab2f3953b4cae4400c32a1dfe79ab97f51209517b630ba3c6e86bf110fd27fe5576fdb17ccd7742c48c2e5652cde4f4242206702e4e864f210442545db89086ed156c9e12e7312b6e6d55124ad65d7ad2910be9cbcf85655bae5c348a3059539ff1e13d69644cd6b2107fa1783ff16fbaf5042da2cd33c52c9737c80187d23b8ff82703becfa82a8436d113e7408e475c430d5226648bd8ff368129e0721315cde38c6df25c8bf264faaa7170f9d36e2f8b0c6f2b09d6dc03d1a5ed21b9661d9dcd4070d79217af70769c03c29743929bb809ebdb5c0334231f372e0f241cbb60cab80b763a019cc65f40f466a4cb35b53d016badc7705a8751404ab345f139eccc757e4e226156a07340cfd03b6aacbfb10b +generate_ring_signature 6a7c21315ea9a0794f13bcb92ebfe303e1b0713cbd6bfacb529134a6ed01585d c86de54a568c92e9d6a2be843389eea6397c740fb17949b70fb10a9a58842e2f 53 a4eab03193f38a2e0e982a87120a234f90b0bc7944f51cac35a25176fd58b777 d23b91b0325fe014f6e53b9fe832d97042b4aaa25cc5f1247658419c48a8bd49 1bbb20d6fae0893cf5485c19db5e675d999a0e9dba0e0718801dd3f2707793f3 2b515426826b80e919c7b7acf9616b6dd5572e0d5dba19bf9e4da51c00335487 7c9eefaa09435f31121d5e355e4fa554668f4837a61973d30dab74d464cde65b 37a99f9bd561f0192816187d019987c0ce5f147d4c418bc66f1a5cdfa0e883bb 8638bfc48383464f756c3664e864d326bca7855cd4f8741e29c74dba9cf4847a d67f42eaf5b97140183b520d329b27d245e46cd7bd00c2a171ffcf3cd8a0c9e0 4e7fb336b76b0304d312f2d75b8bf75834b7398db6526008a74874e4e56e6aac da1afcb4fc99dbd35c26712046956d72c6ae8500c3f916b34d670e6c9daf89c3 3d982fabf70524a3fbad979afbbcfb2d0bbf8c2aff2305bdec06e5c79d98b47a a5f8ac98cba7822b50a7073a987e71de73ab843465b744a3435b4bd005522a94 08ac7bba764eecbd335e48c24dd1c04c69e0f2b24165b12a408901dc5da87043 6a9232cec3fa118d16cd669e6dab9a8ec07a0141b3d3b5c97619289400168432 cdec045aa9b95db4758e28ef5d3375c539019188ed4994d59d932c5147e7d84c 76b7e24b615b9c211c78a8b212655d2abd2ba6da10ced987e827163c9f8d80c3 48da2559b00cab37fbb05f10ebcacf51650976a020f045d7dc83024afe1b2f6d 4f16a0f0e1da96f7b885a88f5d203502d5bd717ec8b5836707b2da85accb05c1 80d6ff504ab0531ce476105fc6c6ab4cd39e75f9ba0bd781b7f6e9ff483f71dd a063b2575fc0ed77466d817dadeb051e1264af664b3baf2fab0b3bf3c7b5cbec 69d8a67beb3f99a7844a980df676962f97b9971a1b084a5220bd830cf279e181 33acd6c839c0c7f438189e1f67a8313be209413fbc7b0b39d1265a6d757f879f 97d4d0006c8c7deb27813cb1235e7c914a20da278801c2f3697b462071114276 f006a40b43795409e5c5254e5486c9522ee2fd1a338ed26e9b40ec4e1c29cd52 eb6c0ccb68a8bcf2c2f322652f6e0635d46e214f0bc0961e6ee431d9a6e70e6d 10a6835fabd12b737505f67c3194489ad1f67e2f75982d50c5eccfac7661c050 2743f8484a17f4b8ab423d9a06e648638f50037a20b453f91da821bb8dd98737 ef6cbc0d2dfe502227ce198c737f0bf0f7a4931444bb8ddbbd82cc317449dade 574b33bf68724f9217faccbc97a60e2e6a7497582224aabd262dadf54a73453b 1996d428c975b3ebc9504f329e76d724f2716fd2d6b09436f0241d55525dfcf4 4aa00e19bbb3aa17adead7dc76b7c018a9afc20b424fd11b53514193ebb48568 346d12690e9ec83837d4f402b0fccf731d999b38cc66fa1bb8be1e46398b04d5 a819b6df989fdff76ab04fdd6cdcadfa2751c54e14c359b6dc63f285bb545ae6 8cae8ebd0995b16248b6f1e3c7c61d9f0de44b582ba27f155e1f315b81c0af60 0e6ab1fadc1ce2f6473481649a3a500a86e4aa00a042d0257506022a7abd0965 e2adb81732a12b14a36d92d5ac877c7eb23b4d365a31f6340ee0e85e3044cd83 557afb592dc585c70ad082f601b97ea5d53acc1e8c1cf2acbf73cb2e4ec8ed20 6e2edee22b8a26501186cf35808472bd9c5343c3763d921322398abd5b524e4b 321503c0c52728139ef0c745f510897978d1805ec1366245f14c12472c8f7926 1e94842bf9529a263ac929ac90835961315297f2d01d83fe9212cd0c5d86f662 f278b17bbe1236d3066be0bd4befb0e914a524f85bc146c117e05db1abc54126 d62acf5d9572433e91cfff5d9d319723e0d49dab4a3ff4a7a2c27597ef4171f9 1cb00476f95508a5c4b27c7de7ae82386bfed926cb335dc6bc934b21460abd7f 32855938be63eaf809c44abd425f69aa8699cbf7f8ddb2a94817692574f85b31 6d16cd460eef86fdd3a4ca2642866ce01e7f09d9c7e3158ab61fb4fe7b1f1866 1382892cc72eaad23a9d62e3dcddc3c8e093177fd99076db600ecfed64b60977 d7757e0cabd39e25dbe09943b20145c8ebe5710004c53ef8355b9e72027f6cca dbcccfcb3708dc00901fda1044bba7d6528a966dbbd4c7fdaad104817a37428d 42296a58204e10349dc6b9e58625ed27b7a0fc30d56f275e5c492c78faf3005c f40a1bd0bf96be276a9a65ce584aa7be335b762ac9adef807c31630587ec85f6 4e4e6efaa2112f4a9f3e2baadd787b09546ae6090f39bb2c2c701ea2bdd8a36c f74d02271c39444a3d2742e7e4015fe3f3a7f3abd96e4c04d719ab0324352638 1f89028e20b6bbd28eb46ce5a94886c0fd79ed9142147991750b7ea512c03be5 fdfa920e8a966735aa26527da416a60a777c4e9977d85417cadbf32400d27204 12 362f1e874fa8da5609e479cf975600afcaf36fb8fd6b420d1a9e1da0afd5760b12d7e4a82e8b590e594a0c40b32994c2b2572904126df150a76d0393c2264f09fe8d7e57b1515326e70d09496f7411113b3f8084d224e4b2be5a7b18874e0507feb653c395a5353f1ee55ea1f134aa4c915632de158e6699af6e9b0c366b580e4e4e29f290eae1246dad892c9222b90c414bf12d7596354e55ed2e8f1f5a1502b655eea199db586efb1a415ebe0ae929a552fce35c23072a04a1c2e0c08c8e006e13c97a272ea2c2b1eb91d7974a448e75ddcaf3147723249776abac25814d0a93d772e28c853f001c864f4cb3a1c75c1ae34b180bbc3c638ecee1230d8be30777800b59cb4242b9c9fc859e506edd2f26ac3ed2d30c64c73be355e07810280bc7d4a8e3dcb26281dc6e95cfe861c5b138292809a77d0ca1dbffdd2e2066a00f8c7b79688e1d24aae7e18f65740b899b5e3767e27b36926faa7f1321791489077439c6132c8451f169ce7e96d0e05dbc9bd748ec339749478ef8756767fad10a87a271d42874b85c729f12d514e49c9cb26d6ce7c5abb96b3df4c99a1db81c026039b0ae079dec47b1b861c0caaa53ba18f050cc86214af7ebeffbfef8908b047498ceb0cfae3c52f7c5454d337f11322d0a9d261eb65aefff524a165e3cab0885091ae69aee81e0a85cfef61a0b596fbcc5b864c07dc2d7227a6cd95db426021d51e7a8f1c56c9f786a59f2ee768c1285e526c23d30f9f6f087b8115fde8a009cfbf59104be29e9dfdc982ff880f840bd4865aaa5147df8ba3d2739ceeed900a1069774454116bc7f689381a9fcf5aa2c6daca1b743c08ac40f2ec877b9e80ed491321eca693a74d6c5e19fc63f41bd9124891337dac863266b5d2cc629c80883757195377f736388e51c8e2581321112f3792a6d2b4f64af3502127c6e060e88a22be0e27210da967c91a0954bcd9bf5e09488402254c144ab2031eefffb0a6d09a7c24f8409876937284b23279e91a3d67e775ba79c887ae150372f00dc073dabdc3d371b60eac924ea4802335b2814ef81fd3f740ecc243da8416cd6320ef99f21c94ea31b0a3fc44c5677ebbdfc3d0844cba376f931316780eed6cf590de5addf1704eedf96be395710ea0274a6e52b648ad77f85303cbc408b67c7d50a48ee4f3883629fdebb7347295b7823115a24e70cbaae2879c3eabfbbaecaf50c38f9de0b60bfade3951de997742c4b997759a25951d08ed2b721eb1f4e8a7e091b871cb07485b3861957a21201f9372c5e21f06d0e12b6a0652c3745f5dbec0bda07cf43d0e9c70f40452d340738bbc883c5687bcfdf918840fc3553cc8ecb05624365a429a6e79aacb5055c298cfe4db2c23a0be3be9ac39010d71eb2cb3d0ce3db316df6852cfcffabe94172f31e513e25092d55b944f5535040080e6e920f77fe99a7bdf20fc71ad333cc8a5c59e99274e5a972143bd3633432853cec0b076b31e9ad695b778bf14f5ae3043da1d9d3901b798025b5e62c592decebdb6f044adf0117bbb960dd7ddcf9a5d4c0bbe338139c17bcc5c13974485487e6ebef0cd32c9f153acf7be0668b03ade1e3020b7cbacfb2738893731ce73f9dec630600143c099f919b16be1758eb553ad9c780c8290e2fe1ec44d2fa6a2d7758d8fc037ebd2dad517cac1f698052697e10abdf35948b0d23332f7c264f9e55f2903a0385f3b8a38b40be2f0e7a4bda96f81f60bcc82ae6884b28f01218dfa6e450b2062fbc622d4bd472aab4ff6c8da0c254370aaad03a0852fa5a2dfe58c31a0fd400aca0e93c7dc9150fe5ea164b773b5e5c1df27fb9d72b1beb82f1d11f0fea580bd6273c34033b08a16e927542941576198f359d8f0e5fde177bd1a6ccf4ec9e0492b56a484be513bf4f73390537dbce0dbd6d7e07c68c128376406b636e61ae08843bee3021f19fada8791ec911e9c879fec2ee6eb8903db50c2b9a71ae18b80fd5cc566d9493ab01078495f8163fedc996a4a13c7f1170436e14b1d914a76c04e0082e8b1ee2f2b3cdd86e8e3e0f68c123cc8c04bd517a25e80a681d9a75bb05b74eaa1b946d1b5af93d42bd4a1ab49f08d33a1bdf0516bf110a1f8a064fa507122c188f2500651d5f94b9d44b449d12303442153b042a4769ee3dd90b01580e6f41de0c51e66430a23bfdb67378503c6a7b16e5b491efc4296254070b099d0039114200b61fcd7d070aa2f756fcd1d6674ebbeca19b7f04627afe036012a80949e6c9387941d518081094b9fd1cabea95e53289b62025e801c5a2cd1148160ca04c483d4d8a61ebf93dd9aae43ff8ad33c344a983f80babbbd2dab1a6644a0d54610f7a546840185ec5493490da7142788a19d6c4286fd32d3566e12166600fead97175768f8b81fb354f20e923fb92ab563d3b67e238b8c04cacaca8323b0e73b68b7f5b71e13ad41b5e9e8463da272b5363679616231b7c3200b21c09c4098a3e241e35c65a15fd6bd2cceb3e82e69fe4780b3c963af318c600e020cb9801b9277df3df1cb89ff13a6e51698ec34b6b968bce214fb26f26f9a1a05ed04801e822587da6d43312701ca6e950ec74a4a47404afd14b89834834a752afa50206128fd543095165f46e2fc25e94bf5ff6b7b8355e9e4788f26eef4dad0569e70e0099fa6f66143faad21af9abd709a65cfd00893296d54081c055078e8b6bb70dd791e00d92dca8073a4e6ebac2f3a663aff43ef9c1823cf2844b0173079fae0cde03ca32be734cf8c5f908517ee0473a548d7a7fa2e3f464d41910480dea390232942a44c00590373277eb80b03a6773331770e824e1fdd26d307003f9d3f008ca4bbd93084017ddf51053611ea8cfd4bec3c809e34481bfc835ad9ac1daeb0a1174640168b4dcfefdc6408f46a94add4275a18e3ba410fd5777b81d8bc1a808ffdc6b6a82680a0b684aee0d7b2814381d9e0f281f6f6d19ba5f22cda814930ffa0f447be2f7ff2cbee3c71a41726484b8816376c192e6d8818803c6633f580a458e4489b6278c9bf4e6dc37f5b17ba5afa092b3fcff6f39794b8a5aeac3a0000687f690a5662ac5ce6cf563ac7602ad558c35e8618e2d0eb2ff8f2f5ad508083cc274de7ac5c1661f5865bc14316700d1bcda79622a6c521dcfdba2152f8f0f79c73e27aa5ba0a352b1899ce188868cfd959ba6cde3f1db5a82c2c61bec7e0e46b3bbc7ae294395187a3f6ab65fb646be3595609ae8190cd82af93602848307db1cfac6900d65e82ca93f66e0222bd254e017d1f23022f76b22dbc696fc0f0c699aebf95bfb2876072221a838242efa4c41e47f8cdb231b7b6a4aaf62b3fc0ceec422bcc71e68b4085de03bdd36f73bad8a715b975cd6ed87394a667552750f4a7c45065ff1da6e221006fb6176879e2e6cccf4da34abc5741f0e9c9bf7530b1a3ca21a5c8c7420712cdb0ed3336fd4cb70620e2037bcdfb3193d6feb4e070fc72c6314917a1c3a051781fd5c138706d0de066f959e03c3fdb32e84ba42cb00d1f4d50efbd76bfb78eb50017674e3fdb2536f7fe1d86cf79fabdcd9ea1c700b4438eec37af8005d7f22cebd13b496a0d90eadfbd4943b0d120b4fdf9e890d0aaf9dc3ca85c63c7fdf9a8463984f0a6411324214b7c889cad2f618f537a685095cfbd0666b116c22798bfe2b2ceb0f247de8756c3bd5f7ab17c158658b10cb09131f92300b295fab075796927244d23f1f04db1da1ed711d4e745d5bfbcb9a02ce1a22bf3002a1a4cab1dbcd49b606e40c53a91aeaf6bedf01fd53f34b31680fe11f46a5cfbb0996ea26efb4aeff5a8c4befc90a3e76edcfe4c5eb3cf7949e0b9a1ed381c8e6d0b844a7cd3d0566c43d3c87d1f0284833466d9d49c6d8cd9a064dae2bd409836c3620755efcad2ecdae3b87de6be8014352c9e8d989c66b3906165fecba7b494f11e40686c2d46a28602b3909990f2cb38534e37da6499db901a6e2489705eafc7aabe77a263b0126e36a288437d6b744777c9efb7c1814140944de7893ce7b02a50bbd9c3dacbd4d3a832a1849f4f690ef437ca32751b5280880e85f355e0e3a7e6fee12d74f3aaabbe3d800f76c7a99aee977da97c8168b0761b094b1501cc2d7c873153b1b689f091eb471f6c7053d1ef0c14c61c50957085ab26191f2e43b6a3bd81b94778fe73a457ef7a092da38923cb7bc87f1ffe405fc1d3d74ec288436d39a11b7a937079d8802d53ee484de06071a3bf755ec22040fb335cbd138f7973b8eb5124f4f4408f7bf7061e728bded1a9dfff4c36c18036d3d7a2b8d3dd560aeb72c65eb5a180a04c3218fc3a9b25cacdd0bafb2e3d00bf7e9b7136c517c6da294ca2dc0fdd27ea0bb8c44a79cc72ee009cdfa9c80c307c3fdaf30c70efd881aa74f3ecb22edc67c63ac557491f5d4d037a14a17390f0df51b9cbd46cf4ba88d64edde5b0d31e781e270458dcdeb2586594c1a441c1b0f3d6e9fabb7d8098e2e576a4095d3d233512dc5a33bae8ced3c4b602dded84b012e5cd5a7d801c5a2248173380cb6d2044330f1b833397fe85a65ba0815d44f0bc5dcf3740089f0c2fe6c52ace47c649021404f123ca9db6a31c3eea5e1145607161e8eb5c7324c58061de7593e9cfc9cd82647147c9350cc9336e811df37760790d8ec6616021d40955aa6526a9159d1004f33ea9e480e3c1f70766a174fee08d71c7231079b69bfb86cd32eaf18d1e166e70e9ffe92f299cfd29625fb3c20077c0bb0a04e0164c0c442a95429964d062dda87f103805d71f273fbd98093a905 +generate_ring_signature a4f99b4aebecdbfa9f6c5fd9d62aaf72bf17ba6b85b2b86f6857d4eb5aa4e2a4 903b964eaedb5d9df0b0b34f0b20d58cabf1279e7254300089735c55779f6d06 2 6312750888bfa7ded17884f70275bf7e07062a1df46e580cac091363ce7030b2 155806e2d34207010d5b4149cff7eb191ccd3d38dd5a1cfd9dcaf19891878dfa 04ffca61416f5b7b5d918e456a81e4f50a9d4bbae583602f8544be5a74c35006 1 f3bfd665c411530479d358d573ef0a60367ea9a39f3b14c478c6a6a3fa36760d50626f2f9535e6e341e26d1f5ccc0a69c7acffbd1d902c958b2bfe234002ca0f54d6461138cc02ca1684a2acc7191f7a584c35b3b77f68c92a5e0719cc8dfd097918dd169214dd829ff00faa5d1e1a8a7e82351a74b5b740a65089603cb0310e +generate_ring_signature bdc62a6efca6c735edea0f7c692ebf61244271d37dcfbd89d538eec558460cef 615390da874162142d31fe5efe83e353bb9a35a06e861b6fa96012afb6606e53 3 f0927d23a105b3550d3aecfa2c3115c5730636fb068080dab5f9e2a91455fad5 c7a262ec1e963a0c885a967fea2e3da4374e2400ba9645db84ea1c785d13d07c 38a3d5ff005353ffd24da4db56e22636e1bd2c0182cf4e08a84cb50af7752351 a31a2f30ef28f312d39d35032878250a53ebf5c77325eba642299bf98b567d01 2 b4dfada2756e35f729f0cdd6464e2a3b55eaf2f617bd1f2d22cf7b4c37588a02da8c071cd2d2def07efd3dff48f3ff417257899a74d6c4cdc9c77c1e0eca350fc5804070564dacde4cfa88fa549520d6357738130732ce6e36e40c036079e00138d7e752a8f849a9130a10f4995ddd5d6b56ecfb598964fc8b933f68a3d65004739d5c0817c5e0d6532cbfdba5e2277b2f495a7b88b8678f852e40fbd94c1102fb432a3955212605264172d37eb9ccc3892ffdc22751e30c19e7516414d69b0d +generate_ring_signature eab1439501745d1a9b34fcaddb7a42e8d09aac0a4070e49c193b7eabe02f3e72 6ce8208563bcd24b2582f5c8686df7670c4046c6639d9948bd844d83c79e6813 1 df7698d71575f97f8c3699da593b61ed18f032140799e2e62c7a14e191ac49f3 004c41b8110c26fb97c3ceee44a7423e7ff0fdb3e7911af60bc4a03f2efd5f04 0 60ad30f6cc0a8e1b3f1eeb95aea3f9a24e582f37db8aa73a93241a491ca4460024ee2186348587ecdc00744b6ac04e4898d2feb937477377519e19f55bd1da09 +generate_ring_signature 50d6c84602d35a33a30fd22db0d5cafff513d3947624c0185dbea80caee90c5b f686926e40d3a434315e5f4ec24c8282275b89773f52a4ed817025943ff7f022 25 9a30a185b0624525d23c91ea4f28bb3721a797a790c5cec3a7028bbb8e191027 8f4f792489a3796f4c9ae40b0e7fc91a7de29072db0dc09d3c5890a294d01a8c e12506e001bc9c5bafe5301f3df40bd27a974ae162d1446fcdb268a2406193e9 e250ded73cc3dbf89e920217cfca4c614d4eafb5a40c6e36bddc349a4c686597 b8b0831d43d7e2af8aa0d607b3ac194c33dfdf7d3efe78edf3cf6546011810a0 76423bd48cf42c31fc156e3091f20982c6bdc3db8e792893528279ecf06384c1 b7cc082b2637338f2530bb5340ae7c4fee3a2143bd2453970ebaaef83c997592 f85494d2c98b7f25681f18bc32565d0039015aa5266f65dd232dd731b09b4cbf 3ee9148bb2f4dbceb9c0869f3c4e7a5245e6e549f214bcfe4d91f78fcb15d6ea 1b250b0f1e2114b6cf2d6c7fdd4c7b9da1f7bf721172be560e2fcefdfd4813db 9d1b3233a32073ce52df7d1a307de6b836582afbf0474351e7f0a2b2a7a9213c 83c98a063202a587794fa6d5b0fa0d3c18a21b81a43cbb366d26a91735117f86 537697e77585c2097b51341c2811cda8fa3550cfbc44e529f0a63d63ed9290e7 5da85646a0b264efb575a635de1ef2c955a765dd336351e1b2ec3994d2c04e5e 832efcb83d53c171206dc4cfac510358bba39225f385a551897c4139e5eab6cf c6c37d8fa87c45ddd9c6d9f1b770c6faf56f903b59afaa8803ee7fc6ba5a3e14 6389ab8261b9771d991039e4aae4bcf58fa807749905593d1479a23dcf6d2e3d b6fc1558d3db2bd362d2aca176659835a3d7f34d5ee2c6da1209bb2355b4e954 91c3a0980db59fccc7a90a69af24680af4bdc0d17e0a72fa7fa5da59bfa997c0 e6f8b0e3e397895d57639d5aadc9881aaa3d8ee04d31fc68d2f5b9e411683075 0a4503f9c48b731d1dd8b2280a575c15e5fef2ecaca934969cffc5cd115dbf26 1327c1f73ffb2ac17233d79624a17b1aac6b645f16f2407de8a92077c19a3c7b 6dccf4059e35ab49f66e199315e763efe2815a503eab8bcf44f4d25534f9d200 f947e8966541a52c0835c2a064c2cb71bbc652dbcdb1dbd3314fcc8e75a01f3b ae46f670527d4c0d9f04e0a4ab67d23a4e4089107c3250ef62c9a43a27c1c4db ab8a1a9091fa8089477cb325d13df16b35fceccaea164250309204415733760c 21 d2bf48116abba1d2948aec018e1178679133c4279776f7f23f980a2916a3bf0c06339e395be2bf4d55ce3dee813d12836183ccb9c4c15d3581187ac8d7a60d06d3365526e825344e7a6bcd490306d9c2c9bccdce0c64f65fb6ae948acd3b0e05fac2290d8a9ac6cf8efc42a45fb308815386c28afb146bbf4bbc95c1786bc30a26b70c634f23c8d67bda12b0a169653a89050928f9a9388264db675375cb2a0c5c047b9d0ee8f1d270d153276b591212daf6744ca9492d4ab4680ea4ffa94208ab89c2192d4d093374e5589759e21fa7155a88ebc1a45a49f780287fb31c3c0da1467c0aaf63817b7d9c8c3e9f8c4e913821cdf616b4b467c11cdaa86ad9a70fbb1e59c16bcabdd8cfb26e842c576dded2cd86dfe983e4ecfc5a09838227430c42faf2da6aff8095579656ddd5c073ffb6645ae2df3e20d93de1fb7ca21d5002082f4b0085237d42d51c484a704d3fbadc1c8b413440ae771685c97102f9bd0f548b5a270a5822ab2d23aafe270831d3e2717a1a6ed4d73b7559ec497532c700ff1f48e27ea86b838e5b73268f91e9d8321361f85adab7e907082f806f704503b2620155cdee7f98951233db488799372fad0fcdc3e14506c9b9ff085961f70c2db280b6fdfca72f4700df1a92f301d98fb9ccda81dd1cfce1c8950a790c8b0fae6ecf403b5f9d3499038c8277a11d05f58fa3a071345ad978ab75069ab6ae092d0e65398f8eb45299d33d282a91e7e5c34c849883552762352fe3a0017c340bc5b8ca7cd9b6773e35e38c6dc65efa4ced0ec4153b9e12985f8df1d6ec76f10f6a40b42669386ddaa15e6fabb47a5ef982f78a0cf243f0b26172cc5675cc7a05a9ac88d74ed7f3dbc34992b20ded541ab339b9fefd97f368003af8845316090f0ffa5e4656d7e3c789fce2138b683de1e7a10fc885b438d8d5911b70e2126a0793a3b950996b551e835a5f097bc5553c0fdf6ae6fa138257c490173b4004120fc534d58bfbb22293767d56e8bc866dcbaccda0fe473882f8cbff6ac34d2d9507de095f0e422dee20c57b104d3b08459377d37a2df822e21e64503f5b0b037f038f6ea6095e8f0ec3e33fd424c852ccd5775c822bb3992f16ab58a14674c50608a0d65154ff4edfd430fdac3c695adff068a0e4bad77ae75690e5636cc678cc074a3514acc4f135e7681c47cde9057e883c8d8f874703fa0033aeced1e9d7cf0dd9d844dfb47526eb72177eefa470d3b9547a1ab3afe41dd8a346bf3c28bbc20d0c317e0c0ddf9701a099cedbc245867ce2fd113726535a0552a5b88d5020650fc3bb6b7c835267e8225eb651b7fc81489b6da3dfa5286ef2727e7dee0ba4f6088200b248731ff36ef6478a91595246769f628b96a919b82c6b7c394056d87701f62bf204e948a8bd693d22aae3645a02727d950ddb752bb86f2acfe48ffbc407eb3315311243c8e54835c4a8019318748c61d64b3c6ddd62c6a1a0fcd9ff5f0ede06443e60856efefdabf99a6a1ac5638d69fb8395aa9e5457815b8191ef050017b050fe1288526e735b94d1934c18b3d9f7156a45d073d7cfcd58aff8e69c0afef5da22bbd78edc19cfd9e423386fde0eb62bad363cf2e27247654abe58040dc7a5d4faf95bc31ff5b54ae47ac079ac916f90bc47ad6c542edcaca72d86960cef4522713924447330672851f0e135c571b52eddd4fc327ab4599037dd7f0a05c6c58ec3e72f53556311265d817c769e655130155c6d583190333e0587929d07aeaaea316b3398679227068022b1720b327d4d0aea82985039ed20389d5b2901f2ae0c48d2832a9f516cbf49e53709d28e7237e148f788489b91daf4df267708c34d148683e551aa5af15bc23646f83a84d145ddf210d10373d0abce5d2b9208444ffef792299861ed26b37a4d0db0b4776c3d2d5d6bb24761d25a5bf2a61b0860aca7d99a08f892c474111b9dd79dbf10f0e3f69d5beb498e5aa4454754620b5532c6f8461db6b3ec444ded07fd430d20c0bd1aed791ca02c9a251289fdf60e0b67254803ee39204629265014fc6253c2db7a3d0a6c4acfb143dd8338f94507276760e484a3eff1bfda5572f7fd1c75da51f7504888853eccb1035a31e1a502f4e3faed1810628b9296f127bac4994c0f7bc106a3a12779e40a8fd8ff31df0e072af4413d10941c8126d8c3fa0e927f60dd4377463fd434ef3896b23695de00a7011ec98f709db4e46e9502279f2474e548af10481a6411b730255d81909e08 +generate_ring_signature 24b6afb3f6d03575e6b84afb6fd3787b6554e649223fcc94f0c3f8aefabf9457 c34607c491db77c2a81d12dbf15e28e3fc50190e43560765d84b1d88a04d763a 5 37604db2c71da54aee4ffa062a3ce264f6f968bb22e195de69667a1f6d8346d7 92ff0f511601d6610f31f3a53c956265053e53a94a9b08e841da63cef7480fff bef1ec8aa444f5d1702357ccf31de57d9d08297471c8071ca27d3e9f30302f6b a0dc011ae5cdcdc8ea827b893a02feaa74556f6a50801ab1d5994f13ffffd133 77ce2aea1d830beb85788d5b9b9ea39a1aa0383807299c479547e16e7c633acd 1c5e52449305df865f8156ae662da8e69d334ee7d0c9ac835a359ce5ecb0eb05 4 d17137beb1f1f93628afe1ad77ea7ee3184aed0078771629797403d6e9b51e0fd8102628c57d5b2474ff412e65cf0b6ce2d2f10177e1da21fae8c3123b4fc900dfe40327694e5b170720fd08ab091013b4645bee213f42e07fa7e95214631a08c186096778d2c6df8173915ef1bbe39437d6073e4409524fcbf5045ee260ec0d603a44976af15955995f75a773229bdd7889a5aa0184f292bd6b2ffe2bb86407a066477364b553440ac6cf9f7fe4636b48ca50e89d0d8891ef754134d7624c0db30d2a500c99277d90089542cc5058edc224f06b87af48853766926c03ef2206bf654cf9c3e0f6c74c42fd047c1c025a2709524087259ee25306bb861316cd08d0ca9a8c80ceadae6fb8d257b146f909d9f0691612ac6f0020b6c30cfa8b04073acb18a42c3f2ee148d7b2c5cb36f6908cb393f92e28fa5ed7a188190e33a107 +generate_ring_signature c5629b2c40a6c6c737f5dce8411acc9c276e6f0a56e707f8e57d60a7071557d2 743c872c39ccccc663ca95a73a624559e470c89275f9451e99b943a12c917508 4 7675eb1b2c42c881247435df509b540e8af4b715b863f696cf8f1d7def9492bc 2225eb2d6559955c01988be31a3dcedf4cfd6eab26c1b894f0c89b6625bc207e 287edf6cc3280b1a0e57115d79b5b762b3dd404ed1749aee4cdd0f3850bec996 601df0bfe882a553240b1bec04f63aa6592171d4f1da9f9bfd6509f1f76b463e fa9e98f42c3d58380216f5c6aa0ddeadf5a8f7f7e94b0f5e8d68215838d3bf02 1 83aacd68aa70217780e0cdfb64463cb38de519836613b8f6d6996402c45930041cdebe5e9af616c368e9f10a036c8ddaf3b6fc9d3fed325b52a59fdd5b83f80b4f3db3eea2d13dad10f8b35ba0ec1d299ec849c5247ba5a56778caeda7bfbb07efdd9310d839aa2d42f372ebbb9295023e16e8e347374f7d254938095d3e260e04fa15b33e5b64536294982e0e20b8d36a06605bd040de744171af0f020e2f0815f9e6c52c2345c8966b93324d503e40466335b79beb83d862b30e4e3e15370f29709581eb06f19d8def04d6b3c123a84a5cccd9246d683f157bea5f25f7e00e9b4465540565c021401ca6346c7452c106f4cb5101a0c8d92889bcf6ea18fa00 +generate_ring_signature 1a4197dee423f190acd42720cc120eacd6fdb3bf10b79234630b8858b6ca9767 b4267203b4ae75d442d1ff8149369c96273da7aa9eb82e40b905e1dd1637e96c 2 8dceed51a14035531d0438a6e2c16815792a3e4d76f0f7b34b5491e4da661fdd ae627eb1310b295996cba5c7958b1d0f0fe5bda5ebe6bfb1b4b75e8f82a46b10 0db7b3861e845b76870c76b143d4f559cf6449fdba4bc46075817caed034420e 1 d5a9eaa390fe15432dad58e0257a97c766446b1dcad9e224f70ef82b42dea309fc8c558fc5ee33829a3e671d023f144ce98220c7df65d490cfdb9152fb85af018292e9a01a1f5de0a47dc01346cb7eeefe372ff34a2cf4fd02db30e926f57b00257cdd363c629805034600e5c2e377c159230855b1b4a17d1991f80771d6f707 +generate_ring_signature aac5d207c0aa7557360adc8f06738e1881fe94b73f57368d845f5682f8096f4e 552caa3ef09555e0a216cf8a78b2e18271373231c4758eda7a270b2ddd71b92b 53 6fb3972559f40e71e36a3f1106a7866ee3ac12c0782692e78c10bca229640457 e883af1eeabbbb707cfe9f543956f5bacf9bbc169183279e901b91a1d2481e45 f1ffd5bf8f58e9e1474d1305a55939efef176dd58763423eb3dffa481e103c71 18e614da426c877dc72f4229ac2b4811fa69f56feeb194b73b1df699b14e79f7 a1ec96815246af7cd69ea904d218f724d15dbc0eb271235a4ff1be126925d4fa 344278387d740c1965901597ba56b536d71aa9354b209819bb4a0feeab3deca4 9ae49f5ae55a66e5b3a4b78e1c05756c13c7f7955805e984e8955bc79a7c8640 1c0fbea44d7ffe1a9f1145a760f6f42f7470e8e4037ba9cf4da94228b91d5380 57eb35ad73b6a99178453a88a91be2ce87e886f38b50167b7d121906883bb4d7 2a7b54e7aaad50e8c817096733116f69a22a72aeee7d23b9ea99a8a381015bf3 bedf676e37a4e4dd53b92a8a469184ddac922854327d98a02e5fc648232bac01 e46ca855f80c8e707cffbda4f0cb6ddc8875548aa8d30e1699facb2b1912ef43 89d6e8228d7a16faa1b37309835249ee56870557877394a039fac2d2dfaa27ef 2315f8280152ce6b1e5634e2419b22dd94aea973193b61d9296dd8bfd93b9ab2 12cc102744cfb58b2c388e52fa9c581c2f4ac84e34073a2a96376c6030880a62 f6974ceea92b921fd60f6cb02e275bfa7f500eaa682c915aec114068d6db88f5 138b0b7687520bc45d8bc3e6d9bfa314dcd1d8ad51c48c449afaaf7e7f056d71 e3cf7715e05664a5cd498f79055b41a5bc60b4b8827fc1fca083ee55822eebd5 4bfb5cd9dbfc3a1c41e59f9e1c00237a46819643fb967b5b92a80e7e386430c5 04e91cdeebb6eee9643337c58c719e44d401eec9b044969798f92fc2208d6ea4 f4be010794473e1db3049ecd4d84d05627dd33be6858ada87513d89bebd93778 b422e821b07ec1531698e2b89d50997475f78f8c59ddcf7452fa7094f8994412 8ff811aaa240794bd5e8cf5d21ead05fd755678656e8f0e7ae030a896ad0e3a1 b2b31ee946f51c58dc3d1e831acdacb308da4c894ada26d92cf885b466da12b5 2e333a1e3ee3b906b058bdb67db9884f2286d53bdc14c5143054b1e6348ecf63 cdbdc45b81cfcaa2bdde6d4319b0234ca2b8c464e6aac3ada947a181463a27c4 846eff34251f5c0076e4e2c854983c4044b6fdb836b1d6958084404c842541d0 31b2b6d776b38f559c1e8f6fbd7701a1f9cf067bedc98b91d5992579a6fb20a6 753c3a97d7a7ac59c3e32b3245b4c1e6a8c65fd79e2645dfec9e08196bf5c5c1 facda9cbfff0b7f8bdf25dd6af2534f64f3edf18109def802733de31abe06579 288da7b095382a572bf2b269f3d2f0dd91468d458a2c50be1139dbc014572af1 7edee65275e7cb061af3b8538a02dd1cb54c3a0049d722b3fc295af17c95814a 90f1b088ff7cef4436e5cad92502d21075b84748c2b4758dd0c1fdf0396c45a9 e1e9df368aacda8282da3cb81ea1758599413bff17aa9b3ed4d4c58a26c27857 7d59b675fcd945c0cafe8c2ba2623064e066fcca4127b04e1750baea465528f0 74e8a9e84b57bb4b07e46a3ed620a85ff609dfaee364bcb6c1dbb28de7f1c812 14fd789d3530800344d64a8b0173693bc41a2a909b18cfa88a5ca700acdc726a a6b766d16ee81a8570f6e7e3fefa39954260442b42c4f51a3b83ab6100292baf 6c8a23d96bd60ceb6b63e8353c2fc37efdd85b4af8ef9d6bbb6cbc56b78f3ce6 2fa3b181f1d202dd58d0b064348bdf31c1dad990dbbc8e059a1b414cd126e918 d2621e0e30d4c067701617544e8c86a0d4e2bcee498a419103b500868d9606ae e654d8b768ecbf0b688d3e513858f9c9515de599c70e4403236436768d1a1660 6e05f7f1c282324b171d197a03ab799a0bfdc371ec291a5dc8ab1407bc0d7e71 793bfc7db631e1edbd61fbec38831f9b5e324d2342ba6fb39e1f066e2d8a812a d869a67a0a327f783d72c395cc8b3319fc55edf427f2233b40200a7a76b3e4f1 823c7157e173f079713580b594edb97a0cc24feaa2356b5c493d927b25e576f9 1f52f97a0f224dae75244213223daead4262a21324f336affcb1186301dd851b dc4d8714ba6fb00db603e63b71095e0c0f48375647d8328bd1f1a98ddbd758b1 52b458e8782e3afb49cf0b4142fdb82de0b9a9b02bf66e396291581a96ac660e cc7a1833b75ca30eb566ec98a16e444022cd0537e9eeca574e5279c72cd402ba 492dc7df7860ada5e7fd7c076a7ac5515934d345ce74fc7f4461b19ee0c95dd1 a393560e00ef2187f5baf6338066076d72d170d8f3f76d53058804ff7ff76be0 77a6d6591d159cc018704b75d4ed14a0040fb8ab7924a6504e31263404ad7c20 d68bc46912d5f41a65b74c8cf7b0f387363e2d02b1cac744cd4bbf48c072e006 13 1668a872c614ab34cf519a3a44e3945f5e693f83a0308417e3c35522a2f5290711c748c3d9236a119f322b8f70bab552d3725ee62ac7763ad19b1d111dd8610cb18a28ad3aa5086e98fd9c8417ae8000f5c30cf6ac2d1c2578cdc8f9363a760d63dec8b0e97e5a8e65405d1bbf17c11cf8dd86ffafef84511d61468c7d1d340527ed234c4f98f170d045aa30463d9425cf76b602d8480d27c394a5a3fc6ef902eabfef77815e10843d3e5fcdb5b4c68c9f5ffdc113cfd0fc47e7a80fbf91d90550d696cc326233f97a8e460f75dc229c0e962ea75b4183c75527d8d46ae6fb004cc12bb4cfcc6752c7ca98a006be5d5a440fa4451f3adae49de7e32cf495f9001ff1c785b3823f3689116cfeb584c3ac1ec8395dfe95aa695040411244e58801af3e7b94e372a2cd01edc5646267f7721a41834c7e67440819b5e9413f43010a5b07229c103aa6e71b18dcad9c92cd1793c72a9cf2f3217e53374cf48b7d820d511d417e577caac31677c3160a4f8001d30a28ff0c8fb9a92adfe3290409c70787bb1fd985dc5596801a864f2b6da927d4bc47bc6fe74852825b2fb3f274e60c65d59eb1981502682ed9e3f6b66578a32ab8900e560aebb1bf9e5d2957a22a0f8913f24f65fb90264f400302731b2eb959ec0e5530fc5a2eba157bc268516e0af351cca919bd51fb5327dffd52acf8e9e46711eb6fd0852b9bc6c5cb3350c50de61d0c0d3e0507cca71e0c9adf0fd456b0646254e42c8143affcd4d630cf3b0f9611dce910d92189d4e0535bfeae31f2c2c41d376552eb6b1867cc701df9d10afb27eb66905421fffa2bdf2fea125ac0f132128a6856dacd2b450140b2f4a10b619ac214a953b719dc182035e2f2763b6caa4d0c85f924325c25258ceb936a09e58b7f8401cfc6b1b4359f4a01ebf03f683609cfef9e93a547ef3c76a56a240bb5b41b235b44d863695179091aad432fbab349e758e1456db4b195fa95deb20c3c6ba094317e9828c5d229b1f878899bb37238a35034902677efa451280f7709f8e08099470dbb55db6a56db92d0cd6a110465017d3d27c3d79f2617b0b5b60731682ad7801a6c3bca9c4942abff0242eb88df56e904bb820c6825158154900d4a6d2161c999870d541e59b5f21016b3c451efeb418c152aa3eb0edbd8205e0dcff0e8d59b7e3ed9ecc06211849e0cbf78130bb54a0fc274542137719d738605b674ff39e13f35c4bd855fc02585fe64834644136fd7627c9a5674afdcdb6b056c4ddd69be693b2fa5a70363325953799feb1a9cb94e03e2a0a506f4c0ce5a059aa16b454ab7bf602bf46d34b69ca3fcb1fc0ac9486a3914374120189b183407b99274011778613c4140d08096f03d8b8e161562f98e2ecbe36a1acca78f240703e7390ffbd248fb7cecdfa77cf4328e17412f6351342a3dce3b4814affe7307bdf318d457f330e83f3e7c4b3170cee1a14d9568405d71aa491fa763efa9570946bfbcebf5f5cf641d35124e05f2e61ef020cf59bc5786476561b409bb6dca0d97e051c0fe964e2734b6b4ba9cb92a5b78b186d4e17fe15c07b3e3ddfe4c5b026dd745862425cc1eecaa313a5bb29cf3e3733fde098dda9d18adde0eecd4b30373c3b908c685d7a56420fdd6fda2801be0b92d1a66139002ccd5ce7e63c7b80b745787494d576c732196e4a1473423a75465c8f85a1519711f87b796700be805e962167b4684336656e266d39c115ca2a7a0b768eaad2f1ed4598622a53f230d8b4e5ea984d85493b71223b52d01c9b0525fe69f92f38fbc1f3d6414b3066c079995ea42670a44c2ba4ec7a8ef8dd8a86053aad07fc94614b8656d347a49490bcfccc471878c894f758dbe4620a523ebdeb45d5a0d5ee4a6bd9d1c197197cb0a48d85bea6093cb5c7bc0c42b39342fce57e676638c52f64fd37016bf0e6e590f871d04072d18d8cec1f4e05e8018e7be7f4da767cd1bab7120d002ea6ac98c0fbf0fac99b40fc525d81e943f06faaf1fec84d13da278c18bd43397490c076d050153778e2cb166009ec9cc1ebf954dd930c00815e87f4cee7f969dfcb1d94a0f7b378b94e35ca7c7a31ef10d4b5aa83192ab6ad240edefcb18e13a5c94bc7e006d8a66bfc1129d90b0378ff62b564ea62d26d92c1f14775e44e4f453c4d398065af33ce4b4c0a00b4b64665f91a7d7d84e361c284bb9cd274e85a306b9cde304c0c0393d8da71d7abbf2fde949934468a1425f19b99adabfebe1287d6ff9dc011b6824fce6974b2fe60cc84499328134d386508c92644e63b51405aced3ae20871f7aff4a88951f947517b69e1f0f465f78e8bce9379d90b90a3e2cad4bcbe0eaf67b513a582dacb284ef738384f41246d5938d75d4ee9a39719121a1e56720a0df68a0be66ac9c36e9837f0675ad1030727ca886fb510409ab1ce2e6e0ac208ea0de4d44eea670280248aafd93224a89926170760211f38c13edbf1e41b3a044b7f59d32468dd82865819245f9682df76bc82c44ebb8f451d23183f23e281033bd7f336311f658b9f27fa898249640cca89ad18aaa2eca5584dad9f4ba25005e1a995bb84d74ead317570faa7959b89cf0f130d96b34564d74bd3814f33c50061c30aa0a4d723189b726e89b7eb76aae0f069e4f0bdc9d6d0220a614f6fa100677f62769d2d2f5d9049d333559637a920e92f22e1e64c2b0491c7c3257fca05955a8ff46f4266bc634e2eb74a5f56078e85065fed455eb9a0a6624456a3d70dbb37d3238fef783782c547a2b5b8b91b2039cf6dc21b2d89a1a66417482d3a0f64d32411c2d58c9556a5aeb54af513b88bf26f0b857f17c5c21443a8641cd401d9622ecfd21180593228c9305219d6ce91bed1844b1c6775ca8114f4b960f500a4b54b2902ca36407ffea886a99f8940ea3dc9cfe5e24f451cb19585f6e68f02df59cc218cbea3e0ea45cc72e574d4f9cd113a429f44264f794fafb88da9560e2c9f439ca1fd76da39be85f2f6b953c9c92821ca5619084565cb920d23265903539b5e8abf713a316e0e01ee758a19bb4f8882c9488f8f088bb7cd6f3b6a320b8cc38ba2ddd50598245b7cde53815e9e31809513273b1436b47c2115d5e7260a5e072e5b5d35f3587cc3a763333e712ca50b5189a9714473e061037ea8746e0b94047c5f9d7c99b849a09a1ac46aa80cc56848f1767f3af49d13ae66af9b5100a46e93d15cd4afe575a7489891a49a182b2cdfdac82780ca3a8ca17d52c6f40731cb42e8b48328dd749e2ce5e4ffaa70db997b2ad141ef5b5dae346996a75c0730611859c709c494f09e397bf5ad42380f5f1507278920abca2e5e71357cc2086704e7e93cfff3c27eb4e33ce02efab7ba1c02b7ca2ea6132c032429b937b309b0b0f7bd409dd7f7170c3509cb5d2c3dd42481652c071e44a177b64965ae980c8273f599d619cbee3e91e8deafc7aedd2707783b44ecf39ef3acdcca0c1b00049d88e22ac3d56a07072052e3de82415d004fafafcc2328d69635847e9e35e400f86267bc4553352c6a21e2fae5e4ac47a1a19cea9992f072961321983d63330038abafab2f704fed4a09c8585c6eda454ef38b0ebbc1adfb6dd6c026200b7c04951c45ab5c63e8e355f1eb52d14d7281c211e5afbaa5a1c42a70ce602c8aeb0568f599d03b038f5679fc3ef0871faf4d5ea9bc33a3774cda9c025c0ae300b30319b4b046d467fc820bd77f9a6dd31ac7398c2f7fe769d7808671a997dbaf650c961b152c4f5b0f65dabc534bfd7901b36b5aa1d086fbd374b8ddd4e8ed8fec0ac5e637d0f302f972e8116fdfbe4efc3d71f2cdc23d65c21b73661f83845c2901f0395f0cfd7c7bb0d056f246431470914081f2b7f53be8bb39d4f366ab2f040541c8f1108135f5e2109331ef71924debf1507e41d6ddf137bf2447217f5e1c064b575cc4a93766c87f87a21fef76840f3b2c150e82643d066314be98df9c330a42ffcc9a483da1a136e2dc906cc081692703da848de6736cc27b8270052e390f7c06c837d56eb78ac18c4c5ac0cc20e1e4562da82a161755f79bd6035d720407d54c467f8bb1a70e23b75c06215828cd1a5888db8e6bd5bd43b9f368015a4d098cd41a2c9d60dbbaae059df4a0044636de663c65fa06a0661d6449f5275d200fab38e6c65f7e1af3a15afa1205b453daf1ffa2f621cfceded82dc359be74c30b5fba3e1a0edc73a7a134682b2f73d3fe6a0392e239f66803bd167600abb5650763d782f12f16b3deacc5f31b37e7a60aafc4afbe258b8860591a2c889c42ea0e786c6373a4faca9dcf6b0299db5531ea668f1c1dd81c482d605d34ce7c3bd3048f6ba7847127b72171e258cbe3483171d5c0b511fb054e2d9f5bea46bcc7590f3e9c216e8c96ce3c6bf86b478259593e6b6129b1d95547a97c67d615e8b7920b7c6ba4b2c7a093787ed6ada578221ba058bfc244251449693b240244328c97034b0b07423844159bb77ed30cb5f76c9e7d36f1f8ec864ff03a8e541c81cd540588e4622ae14f99258cbbe0c788984be3f04f066fc5799ab49cae51774fd37a0b5c8eddd8585ddd5a30017c91fcd9b217321def9551533e602861b3b9433d9c0f9181a8023ba5af0cfc964162dad655ea443010473e1cb524854e46f150bbc30b79933768d9a8a49f26058cab67b5ff540338605a193a70ad523b800e7a27d908c52e7fa60d5c9e746354373173862a9de283a764db7d0e4c16d1a88f570fbf0dbd6940100f3c96868bd852be5db570ca36d5391bb32bac33ffb20bd54928230e +generate_ring_signature 1b9c52310cd90de6486e3c8b73ec646b0da895b54b118acff6ecfb69afa6e346 fee5ae2310a6b91390b1f30f2499faff560fb74ebe4b32f965af0edbb140a740 1 8084c2112b5d153f5086f416d6d1c059843bdee8f391f547ba9460225a69cadf 56320a68733e538cca2d2bc41b01634fc3b9e2ae3f9f83635b2574a4245ae00e 0 493ad5e819821f0dac10a9c5de44a5579dcc7c5a6a416da10b7279df37a11b0f1dc2a41e8e986240a76abfde51a8c94d965de621dd9df16e224256665883e904 +generate_ring_signature 65e7321376ed6c6696528042a65a9a4f6516d65fcef80214808c0c3230a52087 fcd664a50e9adb47d9ae24dc83f707e5178d3235b84c17e9a6f586c0bbc52a47 1 42fb2300246f28cb82112974815f4c14cdca8750a20b25ef861621effd08c45c 8e82a603216e1835a583482e580e635a34b431012284d709815768785245f800 0 ab228495a2f462c42dacf3691305079b6141fce9530df961368e680de31f53022dc2fb8d8952c2467c890275bc487d1d7a8f78237f6808d817b7f3b126e8c101 +generate_ring_signature 5fc776eaeb6aa0303db0d4397d093d225c8cc0e00caa55d5e3110e9d7aba1aa7 5745637caf7435d578fb680de1416357dc6c6ef0ef7f00c0d98952cc3eca7d74 13 31b7451326d0a7a224f17225a19e946750d81bda5a2f56ef09fb60bb2172a44e 9665dbe2e581c02838c7406b1c2632ea5898f527c27aee64001f13fb69d4e46b 43522177f2705f00ce314e061859663485afe95dc1103d184dd8445b51f8d943 d087ba1e5c49e3d7f627e402554bf365e115eb049d1f7101bfcd02bbffc09d5a a76ea4f37eacc9214defe54f2d59017a3d410499c3a6c7784c538d480764e2ec 881632bf12f572675667d5b546c761b3448453c771872793058d7e74975d616e af5bd86056c8494bc987c9c38ca25b9479c98749dc1db4a352ddfa119c436afb bc6d378f748e912b350e8716c0965c91fd00798534a25148a9cc8308b0ef1922 bb0e87d6c20206a4237b34b1deeab57891c6750e4d152e28a6477400e4d5b3a8 3556aee9033cd19cac0d3aba95b99694deb7dec82a18509529836cb013f7e656 1b0e39b149d8425fb452857732ef05daab09881647d5e5e648f57650a5fcc5e6 c2a218592130b95abb79673da2204d97970894818e0cd4ed17569c973403aabb 3a3a3c9771727a8adb3029e1022d766357396ac444aa17f75ecd13b7dfc4f25a 20fd014c0ce804e8bc23ced4b22b168bc3c94b8f76b7b0f2366c91e4196e070e 7 f13f64d4aa8e207d2e4e36e0bc8b47c619d3cc1214ec53f4854ce26fef3c680ffb3434a75358b5c9920475283acc64a141cd7a3cdada076c3c753c9fa307d901a118c34238801886893a205fb2175b7845cedc638b8f80a4f7a4a619fa6ce20d94ca17cdc5ef8402a585937083ba55f1c971933f293eeec4be5e1f5d2e7303086da6dbe3834bc8750bb1472438133d40b68fe2cbbc7fb83a7d4d65ac4fc476029c78beda3ff8482417349390807c669ad878c79ceb45d71726aef09649516700bb19a45f8503b7fc501643ca8889a9679694a288bea16febae581f2b2353d30c25cbad97b0e3b33f679604e3a50b43cd60b367605464cf53d79e3c2b24f17206d298e214c8bfd42b86b2f4a772c517d9363851c45c8e091e325e9c1d61f53a0dc64842ed4c9ee025d1caab83c3868946ea3449193fab6a766e0086b8920f880782a3bdd46a7087d95ca1d1856c9d4f437d3638ab6ca6de7d2b1c7e0bf631700b24f40f78624cee8559166b65ebdd8a69c0da92c82b0fffcbb776acc052fc900258f8330d168224a7a96770faf0e2a987a90c251d4e76d5bd11e11833e908f308d4d4f595af5b9a4f678c72e2b9deff2180f96e36abcc63a57a35e446c5623007fd1256aade7c6edba27693774e1bdb601ee4f97b94069c09454bd63868bca10f7639d4c1ef711541a0b5330e11c1b37e8997af2aff9405b30138dca26d9f6f02064b55305d6a0d85cbe95d8921bfd208a1b6580a34a6286566f292f2d2f88e050349dda72250da36bb35fe0d2514b347cb522d817c7d76e43581bb3942165300dee52e6fd22237605beac81126ff4cc4ac1cc3023cffb5dfbd88a46e65130b0b93284a5bb0ea6883c336f28b0e2595a7a8f97a74404df21e2d9a6ad41689a8054570ee404e403cf8952f3e5c78d2b311743fc28c690db778d4c77742045d1108b33c3460b6eafd30389b360ea934a5e47396747c583bb962a730158441fa650176273d2678872f9d628f2bcf2fa4e55348c347f6d5cd5add5d16da2dcfbbad0a9969da01f59edcc8d997e9baef87fac85cb845d627a446494316f698c5156b0bc526d485ede63f84581f04554a7d4d49cf27ffc42eafa570130178eb0cd40d0324129af54a9663840a790dd79b4dbdf8ab468dfe88a09522c504b88d01193a03 +generate_ring_signature 1b670fa7fab988e6f8fdc8fb107b3d075faac101675f315f48f069d363482811 311bd02641a786377befcf7f879163d21693f0eb1bcb162e016f16961adf8cc1 6 81b31e3da8c61e445c7f342caa50aa1f14e957cbbca3d541c909dc5040436732 bd9772f8dcb21506b557824bb497ef50079ea93d74b936ae6ee40fa1ebc5e261 7a31c4a1f85d63b66d38c7656d27611f99b933c8b97c1181376acb149dbc2446 763dab71cdc26c00204f5d24ca3dc0bbf122401d0bab86246774d04adea8d2ef 19d20452c993dc154c00d764ea28ca9c18f92df93067a40b90cd98cf6055f2a2 0765b3b2cb35ad43f4b64d7da4ea43c89d080c201bdb45d5b546f1ede6eafe4b 8ac480f5d3827bccb2bcf68e45cb0ca7cf3e9c876d80405a7c455b22bf68b70a 0 627c61808f2e8c64258b96241bd418678f29ad5e5bb0b54c998bfe0b6b6e3c0f853a06b285c9d6920c822865727d853f19f814d3364d16b37ee09f551cd2b60aa74dc88ad52de891fd4cfcec22815e175e9bf5718225e8d4a4e14d3effceb40dc40bf784b80e7ce3acdecff718860d31c099bb2d73cb5b10360558e32ae05a0cc98f34e4401846088db15ff27c4d615463bce242ab4cf24b42676b53d00141028a1f65e7ffeee54af647b8826ba60954d85dbab97e59d6087663a14c66fb7f0069cda230bcef707cca774c9bd6df0a0346b9d1eef1900c93118aabee9d2f740924339358b4d6e6f861c969b2ab455536d1b70b65f1ec9f16d960d106c1af4f0551e1e45a607472113b6b88e381156fdce641fc66d66539561c0ee6bcb539d40ebec532499c6a5c28966f69c43d6c25e0a208feffad242ddc800b8b9c58e1f80482b48ba52f661e0c28383b5daf6999fd1c6c1bab96d42b4da848b37d2f4bcc00bc788fa7863407b8b5aff00c1c50ce435e058496e0c353b264dc39ebe9a15b00 +generate_ring_signature 89102300bdccd553ce352ef36bd8cfbdf13f099c02bd18596e3d42e6239bd3d2 66b67ac092566c2a368b9a8d9ca4c4cb6a98f299e5f410c662017950512d6031 116 aacf971f4cb8083e4592930c84c2f7e1e814359c56cf8595064261e3a57062d0 4286968d242a26dfb7144780c2399aa1b000e5c64a7fa136663d9e9129974657 914d0875d0b0ebb1a309ba776dfe0a0e0c3ccb37d57da02a0adff77911fa6cb1 25960a259e5be8f63b901767d00eaff9e3b5b920f6080b957913c065036357b9 f42f1612df591adbbd3075dd4d7e13f632262a2aa4692fb66a9b23f929520ecb 2cc95b8fccad32778dc53e534511749bb98138621eba4b49df2ee579c12970f8 b613d3379b8bc47e960b43003a77dee82913090ef169ec2c24bc1e2e849c6ac8 a2fdff8411483e8012060880b6b2ec460ab8c4092dc88b48bd2111593221a425 3991843e89714483490b27ae31f275df2dbe7e29b0adefd7cd2a985edc5e69e1 b04e1160aef718f8095f6d74b592429a04998db7e3235af0805e2393ab257f47 f56c8839ad45f551557e2f0207ef083f0c5ab8afd19a093106f079304bf1fd8c babf3b67c8a305bb3fb26c0a0cbdbf11bb96df24e36903e5d7931d8acd8a4def e08078fa1b5592ea810f70aa9ec2b5af155d158cfb72a4339b06953d203a98f6 24de47d9b5fb0f83502c076360a5f84ff10fae7c178dcb6899f0e54d04c6f744 bf656a168108aac05e7489c23b7d3aba492ddad0a7305b7a235cda9b5f28d62c 20df1335f52c54cd9a273021327eb3cde3927a2a1c589c0b8fd2bf5305542255 6932a7468388e9361a00b61444b2fb0eaf68d975c554ffe92d301fa54277a9e3 c6878450d4032a0a632133777ba8904cbb2e6034d05d5ccfe183d9189403d913 27780f528549913bcb7d6bcb96e14c6cfacdd4a229135dc06d7c5102219f0110 92a1e11e5d0a1e83ef1aa41bd739c136fe4d3b2585c0d8ad15061d93de77d704 7ca5bca6af948f20929a81e95d75faedc991ba2faccdc3f2d942265d0e60beec 7d0299200ec4b03843d3c9c78d33d9cb871902c2da6e871f5328e1330038991c 95250393ccfb12b976d783e295fe5a0986370b05c48e564c2aa5d5c3e3d49a05 81ca61171b3f4f312a2460fe2f3a9a3d70a4c648a1549ff47ac4f621ec00e8d8 8eb43535fe2ed47605beb83118cde781c3699ec00473a56ed1ebb72bf177b356 03e96f6ec4eea800c6f3ef8b2c43f31160570d383285ecb1738a481cd3d5c335 209433cb2ab66891b1f9dd9fb74a75dcdac2639611f72890bc4c2a90ee0ce990 1c64633169c011513bc0a3c23dbab9ebde0d84ec021815b5efd749122a316d39 aeb62e7eeca2e3d46381c5411a47372bd9ed18db465d99a055a3b47c8be3aa1b 8253bd9c2ed9610658e5e339f2531f19fd147d0782ef9f60917e9d06babb7e68 2728a4782111a52f676bee5c3baa8bfb5c346b560510f38921cad69f9de907ba 12d57f3b4d36edf9656e67cff6dbe2d6bae85ba9a4365d04c066cb9b85eb4c11 e458bfe87a0561429dab19d989c37b09726656c8a399d609559db03680c946d8 e273d0effd007e930b1e9e130b4f87497188c4ed48fedd3bc34d169f83735576 31b09e291aa05bcfd743b3da58eaa02c772b817aff414d469a1f668f41ca908a 2309b29e52ccaec650074476470410a53fe1091621881ed61e30240b7d8e5ad6 f26e0a3ae0c0b5d5c53b4fde3a017c0dfb1cc1e67c7e9201c484256c02fbf584 b2369e479b65aa0e85c6d61e312733724eb93cb2c5b34b088adfca7f61c939c2 bb0e268150f215a3fd664363b2ffda9c7f8cea4c47c1efc43a9118f09bb66cbb 71b08cb319616d98b8ecb176a8d109ca5adfc90e7d88838cbf6a92de3e8c7ecf b2e5781e43a4b2c32d0acd5c373cab979b290928728e9ba476791c4dbdd9be32 fa305e51d4f7c53dda4689c34856a691ae3d6a9d01ee043f2106b4bc3787dda4 fe970cb89108b4e704e492c8db64cacaf564956bb1c47610ee25245a842fb210 336aec71dbefc4e4e44fb1e19239080446d9299b0149e1fa171c48d601a41c8e 554b235718cac2e9355720d4acc8838de199b59ace20cd1ffd575f0f4631841d d3ff0d68b3ff9683e5bfecdb8e8377de4ec37ecb155cf39c4e63dcf94b1ee979 f2521d0a0e9836e48760f0dd9910f229175740e2c81ecdd62e3859a0f4e0c7ff 66b07816d226538b578bb1df23e408d754a8a07f3ca5b6374ec386c5134d2697 ca59cba4793221d8430c41eb4ef32de270d0c89eca0b858b420a57696eb30be5 c3c213d1c7effc9e43a51d247f0fda635055491dc26a23201ea60758a37feac7 cb0f2314b2ad60b4230957199a9de58698da7811f47847ca973102a66f0d9498 c928cf24971c0890606fdb0b56f17b9409595764e7e398a2e729ee7838b0de64 b189e86e14c4ca5ae8b4cb715feaac84b921f3bed879deac20c9b27799176d88 65e86d5133b1d43edb8213bd3bfa7babc6e23628e52e36cdec478d39759cd6a7 3407636c7c04d71490e9edb5839531aa4724402b3c663ba22857af6daf6c9353 3445879109faedda39c091848b3b49cdbf61ca6e95ffdde56bcfa28da6d9d27d de4c9c94c1a01d5d177b47355118f70a68e7cea90d162a4ab67d7f8b8be582c4 cd847f96764801152575a86b30e6ed3a75acedbfdb16a5576d3a094342f599bf 6890ecec3024edd3be7e67817ce51b8de27b7a4feb288d8c4aaf6c91e5e909ad 66e74b36d7939f347f0acd6699214d4a78a016d9c2ca512a6f6112fc4be6bf91 a582a1c2948b238fc1de90feb779b9d11e57b06410ef58e74f565b6e3dec4fd2 1d58c0007cbd13072dc8aca24abb0b6cccf238b47c897a1010a300cae267c021 a309f20016a70430700d55c29521f51dcbb9491ec7a7be3666a0183f7b5cc032 23c15ac582b1d2443eb5b4e1aab30a44f108491057a87064a1784eb64023e96b 49796b6bfbbcc45aee369a4cd875e73b43fc6e108ee97d813dbb82f4227cd821 008de8724f40b50fb90cc68e804d395de075bf3f40888741c98fcf0df2cff0a5 c974385a5536e2e7018885cf4efe028760084ba063279d848293ebf7cfb5cd90 827792ebb3a8a1283d94fb063877561a4e92c709e9541b7c87817b28ed185696 2fe3aa80686718b9739256c1a433a37109c903f0be3abb74819cabf35e0c1bb7 e93816be20d1dcdf6ee39748a1f08e06f8f39490991d3058abbda941329c3be8 ee1b391beb2319d8f67e7376e68c069c0011ccf712f5351a82c13552cc890047 eda56aa82c4e114c8aca9636db3b356e7ad9c76c63acfd1ee135d3a2ec3393ac 0bf5483e4bdcf85336f82b77f70b1623d201ff601877bbec4b7b4963268ab62e cd4567b648634eb4ac39958e55ef352570122929639894f31abc61adc2609fdd 0ad0e927718cf2b4eca2e50e0d27d99c75cb7cfd33fd63b7fa7ae38387d92e12 6735f7d46c51519a4a2d475899fcae2f47e08c91a08d28faf81ae21611548f64 210e26da394508be17607c6d23d08e6d93ad5009f0677228c754d212a8e084ec 70bf02ec4ffebbce2020b5db776c6eb463b24989f1f3f8aa2e1bca91f2a4e315 498b3093b6563f383502e489fde1cc1201acdfd6fe776bfd68f674c4e4a2e8a6 8e3a7e23a5cb28de1bd8e28ba5d87a1f1f234fa405dfb5f9206dcc1df31378e7 9dc0a522c312cc2a6c95d954ded0023fa95e049848cbd06cfa85798b91d9b96a e84f3f5612a23c4399389dfd2ed6972e6744b860a8f110ea83ebd77a3a7f632a 670b5b7c96794bf0300575edadeb40dc40f30c52ab560f55a8f7219c2c7be945 b97fdaf538c771ef870edac92c52b266eddf2f41a662c3b812483423f1172f32 34fcb11256ef3dc85e4100bdcecd84e89b64d9c39da22b7f7383ff1b172f0327 0d735c44c9c7c5502d4b4b280fba770b284e2eae7f9446d0964bb6ff7b4e2f92 08f03cdfdeb880a685504b775045779c86135aa6dcfc8e06899bda0391141946 865c08b9ca9748b72018189051fb1282dcd1662aef708c62593dce09981a45fe 647f241c0da6d5e897f96f709e4c5e0ee7db1a90026e297b2b98739662599656 b424e2d17bad69bee7e51c8c8c017a3689b4cdf52cd016af5e65e7084b3c380c 671455db63d8773c5e95e71d37c3bf7d3e6316a39cb2fe7d234899011458c83e 28119b556f4048b9b31e7946073be2b62f74b74a56cbd31fee1cfbc2f5f512a5 a8e89c237d06199bcd94c030f5c58ad1a3caa2ff5bda6d45de213262d038cd03 4d6ae40c4e28276d908f10fac7db8fb0954d0b09f3d516cb61bd9bf9b5df9fe1 7fccc1bfba50e840563c220d2520c9f77afef38d9dfab6e01892e6345ea100e2 28e363ec69911e6cd79ef7ec1c6899a531ca6aa1b00d00f68d71cfc038043847 21dc113dbc94fad84468f32b6b654976e9e2b7ba9caa011fc46394cc0efffab4 513c7e81377e13bd283918a5a44a5ea8bb253f54d554fa760e04523c27978471 f121a815f969fc0597b1b5c518cd49ba4589394fc511633cfeaf0554a33e5bf6 19b4883add93f293a640f138d0599d8abd50027f605f68e78e398568ea444647 ff453b1975ed16f8c0e1411dfde72046542032a9e753b4cf9bdce4cff141b734 4d39889a38efe6d22a0d3569d79b2d5d27a2caa9d40f8d4232db0ceae78c4980 471dd89be76b7673c0948e9dee567ba70acf98d02ef82b8ebcb73dba23a25aed 21a96e9e7fed5e8301c71774469297fa1d0e095ba317b916501070d18be35eff 1529c4c48b69e33c78314005df30b4e24f25341538b4e4784187f8492a95ca4c dd5756c71342bc91146a6d89dd9a1755b2e67297dd08ee2ad47da6f88712cfde e223d3cf9f77cb85b9443da601d925305edea5d1f86b69db533edc032dec4132 9df21b3f335c553a66f4c6b1dd7ac870dc885de1b3323e86dffb116d3012a430 c545e90cde0ea37de654dc7f6cab3042b9c53ebb923580740bad667891f34556 cf75bf6c505a45d0bb8b966e4fd8fcdb60caf6998310f13ef23d48e9f8da5f2c daa6ca75877e1e7eb9c9a1aa990f850d3748ed84dc65be4ef4509ac2dc15a50a 93d1f3b4a03ece48c32a6e8f2ca589953ea4eed5c53798a388e51f9a6a5b9e57 0e41584b69979e002262d1cca78e9ee773e22a4458ebaa53de795a9885051fae 4aba7f5eb551ee6c25d9290c10ec73cc2d1fbb0fb3646f2c3d2959e620d6543f 6e0fea29e401d70f40f8be4d865f7db6add2fab1ab82090f3b85e53bdf9a6cb9 743899b36c46807eae625a8fcb717ae77e7119dd436a6cfc11a7876a1201309e f8fc487e376e13bba04de3825648ddfe435e78b4bfebb43e5b0d40a33859a500 6 0b8026574ccbba296193f1ca37ad91ab5ed182d556aa674ba2449f2627bcda0c6dc6f1dabc8796ad1690235c2d3784414be879838aa68c7f3deb69d6c00f000891d847f551e4b81c697b6fbec00953fcef9062cfe894b070064a5d7deff78d08445b922062643e71ad988f4b34dadf5972345bc8b36697021b9b8868de765a06b6c8b5dd51ffeebae285546da26165e8c096cc90882c34f770552854e143d402c5d7f5c26285cecb7b3166b4dc5aca3462ac707a5229636339c26bdbdccbf40de5ef3fde8afe304d7a2aaa835b767f7cf0d669bcdd476bf3224355b622af3e0cc8759050ded35ed5076d552e4adf9348b56d2b027b5f2b27f127e99fcb77ec0b0a2c9f8621d8326d6b629c8480c748fa989dcb0ac5f448b7b69f8dc32628ca0ebb967d66aca42fd9662f9b35b541fa6e795a4459e8037c839f08f4acab9cae038c2b9c9cc878d358f95f10b179e1bbfb26d164ff665f4bd7f8de47f19ec45f03bd669aaa546df1756350eae70de45e4f66cdf3363bd4bb07e50be2b5223f92014f7e82cb599377a56a2dc6622be8e940df1458d4cb5cd08c839d34a86b86a90e4bab45b770e699968e44c32ca24ab4719e77221c1d5b186d816b4f8ce34dbd029e472426cca9e9a72f773dc248a9ac58433cfa7752a431cf1d795a1e661d4a04ad9404068c21d6cbfa11058fa46ed4682ab4700e0afb893d1973c4a13624cf02e128a4730d146a4f894d1441f3b9fcb0721695bda019ace0a24d53bc19d82e0ba579adb76bd7837ce7243a0b46160862a729e73806fcddb4543e8c06ba88150a7cdfae57aa221fd68313cd9209947318324f7b3e55a11cb9a26602168ffd0100256de511cdb926b31868e1c9f9221f2abb4c799b8c1e1bf9cf67338b9e8c690763eed483e67dc7b9921d1c89de3af49e2dde297c06cd3d5464b58ec9db90600ccb892367604a44f4bb8872f116df58ae643771b357866874937de0d7bc26810efad047ec17247fa1f94bd5d7c8538e126ce3b60a5e89ea434fe879fb07447a026a4ea6a569c0dee9f677473c89950ebd2324839deaf287be71f3fcb450912b0a5cf752193bc2e19ff9044732eb4c46a6f5c1a1d25a2a1c281bb8868732fa800e36443f3ec420b06d04ae628dffc8bd50d0887c5d79f4f84da5a5fe4385353f09e72a36d717ee34a23e5025c20f7ecae38b0092ec721e65838ba575164298ab041846a38162ba033526ad1f90e9a3c05d07c3d8dba8565122b3a99eca239fa30d9e9c618bba7bd6fd0098fcdbcd2e9f215eeca594b2d670b3fbde8081f146eb0a8a28f1750a980b595810f85c5f3f9654d167d71a703c20e8b2ef93c92ca9980a2316e37ddcecf447431b594f6dbce33c4b580f100b3450e08fc8e81a0bc6b403163584d050ac517fc2baffaa5c48d5b673f1b8574c745861f2774b04c5665d0cea4a3c6729236227d5ea8ecdf624579ff8abb604404ddc0f9a81c5aa3e0c3607025106ca3abc098c34a83945d020a0237fad8c73201f3dd751b282b4c3f62302ece2d7da870c5d88643a7eb4d9c9b8cc93d86c4774ce0c583ed98c71c8937903945294478a131f7b2fb66b5f6b3cf4a2c46fdf89e39219c493b4506987694400355d54afd091e0f6eece93deb42e5e513d569fc1f2a97722d9051e0374f9c80490096611769068f4e97b2416b7e1cabc9ec26e34f134c900539266afd1915d0408532b389b8eabc63af405bbf5971a3193b759aa2ec253d4a82de84ad487320271afe8dd2c60d07221f412f2be29a0779f7009a0bc6f62eb7a87bea43bdb3e0a1ebdae7ed155a0d36fe41374c8923d9fdf6b70dd823063b08dc1012d803ea10a49305de83002b6027b0870647bb15c084c944e403e8a1ea638b29ebdb0fdf50509eb7b4aa6fea61e0300085682add4f1e63b10047e0e66ebaa4e9d73a47b380c180c8c318dc111b57893078c35663532693591bbebfa1064ec9614051c6b62091dba74033baf6534c26201aa3ee53f008122de11dec0c7961957c775137b6801ae45151697be0942c015c17b0026c14be13b9284b57baa749f1b4ce250c3680e2027823eaa1bef2ecd308c43d6afe849bdf020ef0279956189a52a880871bd03a311585e4e9373a4d7d9d5b0bb056e24e2b18c473cfddc6dc8f3bf1147295f0302a999e2c615dc1aaa24e254f6bc36f04d58ef686ebd201b5dbd03df0655b20dcecdd14451a116deff2edf2c11f32e94068372c8cd1630c486089cdb2e18050e3cff40c25a3f70d9c53237a20c4507c65dfffda88e4c422b5235d25a0b2a050ad1dc55126173b506c8e7fc92a6a6c9ebb4d1b8d41ff825827d311d4715163a083e01a141aabe248cbec1ff118c93ff930e2b1fccd590d6134ad4b5f3020ab406ec8dc82f6f0763b40c04705f322639f6ae44dd3c44c3bf83f6fb777110880e00336bcfa9675938990fb060cb7d668f9c3ee5bba02ffa6a672beb6e42c441f3080e361b068a1aa4a9292ebd37aad580e181cdd511355d039190491a7ffb70870d0d128f09f91e837f4b712fc4844a5ec90935b550645b4fc17cc8a03ea2c39f0b956b81a7260317cf7e39b3bf61fbcb68e497fa045831e143b0aa80aceada8b0ea7f3c20f7489305598e24403ff1a1ddc5481bb6bd77e2c6fad8a0c59846afa04943b4e22d05bc79cd58c81816011f6945b34c1b1d56f22751e6fe9b22ab3570bbaa850caf74561fa5b7715fbbfd9ae7adede713cc5b2a864cd2b397430c52c0d0c6b4a9191aa9791e25f56cbf26c48e3aea68352e9950a6343ca396b1ebe8704f5892cf44957dc513c7004e819620869cd40bd1f3c1e544a87f91d71e06f2f05716b742fd086ed3e7f58d9b2c09001762a96e3b7e7daa9bfd7b7bcf4ee722f0302cd9d25af1aea9cf4247c7c749280d6ab934a9d1247e978d3d3269e22e16c069ebed3661f043a257242969700885965f99b34444aa31accaa22ca51b19f010446012d08b61841b1940a2ecf0f9ddf1605109e6fee9a77bf81c16184722c3e059c49020686d6d63537fbdeb4ccdcd52ee5c74ce635fe6e8314bedeac4899cc0b46696f07422e14ec73b0e9517bbcd9ab4d5c300bdfd5243bc51ac4b98148020fb77b95c55470d9eb6d98ffe9a785f34ccb0c0e669027fa3578ad3643e9e0590d2c4a0631ef7beabcce3bea977686302702d629422d0f2efa8901acc221ea0709660e50274992c65cec4b9e9e8083a135262741845fbf8879b7ecc59659d5c90a096fe4c7f6bc15c715501f970a309a6ae9b302bd4303457e5d3db366e6910200cf685719c94311c9bd9010c2fe29d4bc7dac6610f41dc65570feafbd1e602a0df8820262a6ab3bdb886b1270abd83ea27540538a791dbab3618f49f2248ce60966e6643b251b214cdb2c8769556dc9e87ffc74cb9ee70911692249b72b863a0fa8d80c39e6fe9fb98c526ab6dc71f6b076aa58758f78a3f1a8d34229ed99630325aa4e1fe57586132cf310fd250501a16a93d5fa4e96927989d65c42ac92b40849f5a5a5496806d3deb5c1764dc1a8e79006e5f96daba8756de10757c5fede006a5aca2408dd58f40a4347d0cec2e0c7cc073038d54eeb6566e77f0d969f3c0a58526f0fe78355f8e21fd37dbc9005c91a6040a9f231251da4ad375ef3467307b93863bdbf06a4e6536024492654206804132eec96fab2468a2674d8df5918005776c7ed12a9c2b9d3ba0da860fb35376a5b6c55e8b59bb26e26ed1f94edcd0486a8561e2fc910c4cb9a7c348a028b2d167b2acc54eb4d243db78c686bae720cbaf7990c189b4125039450f14403b318de0416e85b78e0f4aa2003efaf1bbe0818fe1cf1ef04059288e527be172f4f154e60fec10084f577775d03d8d0911a0dcf08cf595b04d52d3f82925f8e6808eb60781a3ebc1c877a8108798c6e4be40b76b3b743494bdbe1a28e5a42d256a91f7859b6ed43a3c4ff7722f191fc1c7b0ecfd0adee13dc3ea2fd79780159fb1c096154ef9ea3bb02aad18851d8f870a10d50530d5c1b50c1fcfc784350137eacc142220fa028419230e00ba4354d916204173fda0603a66f55c672c0155d049828ea04dfc387dfab5d5f9d331e1c4513091986d1a8a74fa3310b733c01ed012db2315a6dc68614d2135f1eeae13ff872027a6ba1a177de6d619f32bbfa41ac2e0f9c66fc5a565d717b40b4b0eae5649500a067a4d901b99011f862274de2c0fb6f8d3b450340920750a6b9f1eb5a29a70fc6d7512ab7c02147da9dd5512d4f4adddb5620ed8e356f34b5750aef2d309a02241ff73998ec9cdfa9709573b6f7b00f6252664770a90644aace5bc2e19472054a78e9f9e18d4a29d972db99875ad3630f8b8e9a947bf40da4ec1350a352d401f4d53782615dca11a635391692bffd0f4309411ad9b206a3c5c9adce5f57cc0698201351fb4ffc8230abccc674d3cd8dbbd959ccd8b77fb546634c028713b80af4d033fe66a787037fcf38e23b84c8f48b0d5f8a48c24b1992bb82f3c5cb1c0f0f1446f3ac899ad343c187ec29eef6328d6bd4a07c6636b5349faac50facd906f7ca340c935e677e08682f39fdc48da4d8cdbdab86ce696e3bb9458fd40d310b7e06f49e51e729ae2e705ce1b5cd7d0fdeebb8da7dae9ec721c77abf0fee2f05b051f9feb49496fdded0f87f2de1186773cb5fbf3f32f9de67aded3f2deab60469584cb1d1f696c5e1a994f19aaf5596d9cc7193f908a2d34391bee6f4711d02b093ec04e1a3b796798f1a65a656f38c43f851236eea699e610a87df289b42026e9eb4c5263d3693df9458ae32c85e0e778bf7dcad72cf194101dc2aac61540b8815ae39f5dce41cebfcf4f2ab4feaa55406c6db38d3ed6c2538c55f417c2f0d1fe2a0df06dd3351936e7ec94bbcfebe0476a4ebe090c7f34c3712f13eee0906b3918da84b9520b5c889c0924239afdafe9f49a39e8f40af461c8fdb6623ed0aee010f345fa58bd1442fcbb177cc23aeaa9a9c80aa311f92cd6754ceb3598409c071c7557df871a5bd8efd55a77a7fed0a2c2f0b4c4ead01769eac1825b8d40556f8609669c8d04cab252a288048beca6dbe72df33945f0b436d20740c3abb0548bbd93ba41cc3e5baf1fd825aed918eca542b21af05e284fa5d1940f5d95e01cb226242af359ed089f607ed380d46e947058228c818122851f880ab8e7171088bfe7206f84c6dc247981abeaa724ab4c34a21e9557ecf8ff6baeabe6991470b06365d278c4ac03fc5e9bc4a06f26acdc12294fc2bb7447d2d2bfc4ca058c707a26332713bf61117edfca06aa7110b9d036ff090c32556d671d0b4011d139801affcd2b3552ebb196f01210bc97c59b5783242a8b0bbbb046a05310e7bb2850e7f59eda6bb5216d9ed9187dc9536e51b8cc7a1b41c805e652eda91964631060622cb5b2e464bd5ada60d57952e787e2d535df78c812088d088b7f14c6df7df0ac69469c699541ae3863e80bbaa2ff686cd5d2ebafed1aa85c3fd1f5de51ae30d637a4c0f64840388b4b4df37b38f77bc22e9e37a8c03e027b748f0527e30af07a00b09c1cb6b59259f247db4e4c00524831d6d90f433cdbd3a37e7d6025ab200256c9411d1c1e1d688563a84f4fce67b5e11123f116dd78687baa72fff53100693d284374489d97bc23a32f52de697a219f945ef9d27d48c4c75275758e37e0aa8067054df6a037fb51329942667df79958c66512bfdce8ddf6be7b68c88be0ab509da2b26304cf0b9d3b50d02c2102266dbb90e1be618c9c61e9efc151099050b20d3818c5897615a9c99fe42de4c3002c91b3cdc456a825a0cc7df3ae6d503edefccaee3f2953ee2a4823c97dc1184f16f6780059dec788b502458795b4c04ad511550d60312a882177660adbe84ec7827c8313bfd67de62df338f391a13032ceb01b49d9a107c23e5a789eac36b77f900f66cf096093595ffabd43a6b9f0d68c2219740b0549fa2f1662b33f4999a542fee6aa7a3426c8bfdc862a61435013c298cab85f68773df3ca2d58c872c7d376db95faba7a910f1f7757f24618e046c299c43ce8c2977b96757063844cb76d2ad209badca2d2b701439704979ef078c02ffce66f8e49f6123b56cd83a9e645c882f5002fd268aa30ca99260258102efedc6e7529c0f3119bc75b1c4190b9cad55d4dbd4c2cbba314215d8762e6d005b1b5104e008331497c958512cd5a11a0e4209eca49b13862b25eeac38e403038acc74c8d9cd65ac2a5615ce141e156d32bb14620cc151a0aa2d798be6668901bfd8ea74df2a427e3d04560d3f5a82ed25773bd080f340cb7e34daa3ef631b0e24c1cdee1be4a9cec177f3b2d059d620c48729d53bcc7c26f7afdf64a3a0ca0c29949e2ed5c20585182ea370e5f2ba552c503b75211ae01aa44b3bc29fdf7c06213ab3844afdc91f09e598a5f08a2961758570c06f4e447d27307908016e51005c58bc9dc0affcceac613129029eca50059b527395aba3dbc678ace92de6b409c9f4cb5fe59d3db5aa8975a6bf44bd52a90f31bc8a6dd03e53e42c6957cee101551a54ac6e216bbf1a7b0e26186257975b7c727201eb1eca65a8e024f6aaa301f773b438c69b1bbf8b280ae10597a4c7657521d699c70d63019a0a708777fc0e55166dfe0bfb1261e96a433b6da50c7e31e2d698599ddff6da02c6576f4bfa06fa2d834d299057616c4a98871313607f46fc0a1f1e419250582a885e75aa110c70a69066771cd2374b5f5ee40f2cec562d603f77f0d7aba9324ea584cf40fd04292e5db9aebc3e124174a57ea393c2f5a3f152a6fd83531b267216bd225a1706480b6320a639aca669faea87a5b95a8349f47a362620db4da044b2bac38d530d45605992eeee058fa956a79a9a22423ccb959aa4e0542c5e27814b3b9072a500f88e4579aa80e453528f22157cfa4fc90d2399169a00db3c054096813e024a04a358cd176ae873af48447eba47c679e01c292d17d6a743c4c65dd58537bbe40c584a8841d1334cf4a011d713114904ecf79d29690fd474e02e02a04886a08f051ca5a5a1423d16d952c6896187c66f809ae4eef226c0096d352724d69f76ab01e694e178bb95c20fe9f4d32a0166c2d12c7036e4b0f418354b0484e41ede20075347c11f7f2e88f9a017beef6bbe803bc9b1ec967e4f29b13323532fab2e8b032318c6d362e1926ceb1d1bd102e02e9022e44f4e09bbe64f6c84f454a7bca00563236ddc08ca12c1185cddd8900aeb1615dda54ca2b361cc15e2f91ffdce1f0715f16ff14eeac60fef63ffeb0f7859963ed8a1f58f18414e932316cad38229060746a62be7047187e78841e9b534d58077d972d6253e8a4c95cffd63932ea30a391100bb7599e3a5763544db299b6d84a6201dbf19c67d8b3a1ce167f9f69b0229e1568749e08ffdeb71ec520675d24a8d935118a4ceae340b0a7ac8cc0fd90b07bd9420bf2f79baf2c835300e4ca929f3b1221e210b1b03aaded917edfba4073f5c55d23f560e23815f3c190de64b1fee1027da74d36e1a166f043f8f492f07ef3c93544a02cf1e6ac713a1a33002ed2a7d847cf61e8cf990a72d31a66fb20244bd91dbf2d8d0b4ed5daa02a68c166235eaf29ef8da8403a63483137afad90682cb208b05f6a3ea93dbf61887b770daf247b1b13622299193b20fd313985f0e7f15f3f9c8000b257589217a445df19e2fdcd71cd01fec01ad71d79cc34eb70dc955c996f1d1a1ef973a009d28c4ce69d7b00d4b39cf17e9820286c8ad992206f9b268a512a5b909ee087fbc25516a14196ee12e88d7b3119fc4cd3c713dd20289247912b66dead861ad7cd73bb24c29b6b841ce7f81d8d1d87f4fa42e4ac0087073290c46b78366fb297c4a6fc0c3122368e67a94614dd7ee079afe9a2f620961ce78f19c11003f734bf48df0eec2cba8190c98bb7f40a9eeade6d2037ebc0dd21b41ba2fe453862bef3be436a0fab704144d85ce0a9204e36363339baa4507230b6c76b4401ab7871b63f4ef73d568f874ed6c89e21cc62822098141e16f0fd8f136d4464f016ec7abfa58cbc4e604e3d425e3adf2a94ed6cd1392d56f2704b14c86023284c029c04f3d2cf0fa2c4b57980e4a0f56cc5ce2191dd3068cfc0e8d97eecedaf9497470064cc0ce125b9b1e1bacde5a9587fdd97a6bf54fd3230b5523a3b5d85b3393b584b0c7ba2dbd6bc39f799de6d4130a4c830ac98c0c15083db6baf4e2a7f2df5440003b4e42a4e254492387b3de04890bdb7e3ef428c401322bd14ff8d538377d1800fb39623441aa01b9eb22a8c649b20614d04b591c0b2bce9770ea16458865649dc9e0fb34c1ec8c2db7cfbde9de58d3257c8953d00d66ddee0faed1397a7365d3c5d8c26edf42da2e7a64000e4d2f6e17d38d4b3d0b2fa196680e30856f7ba26055cb8db94f1f959f0b0c0e6c03fa6768b1f630e404768b1a88ee8bcd1863a044710449321166c9b5655c217306ad619e873b153b0ccf61eefff7482121646c59ef2973dcc57fa17560588500609e6da786e3e2350e923c7945ae5e9fa99315bbb6c99d294b42ffe48dc3bfcbf5a0b82cf4c2ee2607534bddca825e05f452b1b6412bb05a98363240aece15a731815d6f0f58dbb50b13cd03e2bbc9d2a6a9163c897e5dfa0a9c2e5f9052a64947891598dfd76c050a04cc7e2b6a2433344c4d2f0fa0bb38e7bcccb0ddee095a79371a3482c6622900816fe80f7ceeb7eab85876196c4925f04a192fe59175281f70c2a98afebf230049e6058938c78fafce6fa11e77b5e095f4740a99ad4d61e270bcea34b2a3c60d5bf79ef4c99a97fd6cc9a6db88d23a0b0c6e075b983babb8e33a5cd1295314086042bebeaeeffafe09343825867243fff629fbfa5e43dd8c814d83b041394501f76517f7829ba6b27ade1da765539299cc3ec55aae8cadb9f388d11f5e46540b50c1253f2a74ec837ba6f0c889130a5fadee2c24aaac92b7dd7c96cf3d4764076757092486c32d401921756faf0ae54f671806f57baf20c7504ec27fc840090a345c46de1f0a213093e4feed0c2474e8d00751533eb38f21740f90a532cbab0938aeb2eab72983201e67fb8cfba1522a380c2f7e52c5ebf84d480312a305fb0267a32d20824dbce4d04c5bc81c962417ec834df5b5a2a20b9a64f312398ebe084c1fa73c8c4748a7bece777ba89e5289a266edb0f190c0eb204a2fc00638150eeb167782f024f5bd2a675be480742265b5ecb2f0c7cb89ccb5e5302b6110b801e998f336f53fb681e6024940431dfc8f0760be8c0dac0f4573e2d4aa80a0b50012b36b36a95edfdb7172b4c12586f2e90f0ef029ad46d351879758dac532200d011093124331d057ae84a3054399cdd872c39170944434b270730c09d6b2360680d668677be702c0c4b14117d7a19051a950f20b8c742adc3eb29c2fb4883e0544291dc87e1397ad049d446638b55af62efad0585257be515bfc9f1a290f5f0cc6f20ecc2908782197af37edf432effc87bbde671438723f7da2e1133a7b95015b645c4a02a4aba14c370b23cc006a2ab5132f83edd6c59c7a633631b0279e0464741eb9e7d97234e4d859ca4fa9e828c502af81365d5895a45955ad814ec8036121bb5395aec20006b840b4b45bc2037e9a5c7d319923cd64059cd21139d80dedeafdab448b13bbf0ce43a0c51be7072e5d62a91354f5995644b88492723505cf077984d1994785d6acc1854941849b926995617de6aa21ffe87b4c0e897802e1600678cf650c009cf9279077202b4aa01f3534194af2f2299b426205a1a0030390f0eccbed460d9ff6961c475206db0339323d6af2665c97c56f4574a8a905bf80a7b8c169457211ab0ea0fc31b71fecca1ebbc2db659a3549e84f519d2a005d33fbe158caf318e557f39228c8ab0efedd6a6dce9e347fcb90b0d213b1c807a990953495688d41396f238720d2f942873e42bb2ff280298481f0e4f3f8c80f97a6669d384ce3f333f3f71e0d591f107266cf46388eba3f4d8b8bbe9e6ca804dbf782bff51a998792191cf6048add724e3c620083401b09c59104e482f759012e81e47bbeb8d6b4001cc272ec43248f3a03f77ca61a9a813d4b446be5ac750cd9662c7e398b7862de1dc0e9571394bf5ee5a3cd905f18a58afdbe5555cdf401ae6ca5da84581e9bc99393b68db21a6dde189a8fde88d89cbdb8a78be85f290bbb652b01b3fd963eea02d3c6fc92e9778d585a927da38fcee52db599b74e86014ed68a16b58be7b05b4827d65edfa0d52926b3074a3bd033ecd4bb11a5a2640122470a79aba24bd5ac5ad7dd1363403aa6d08ca05e3049e791f633015b6e6907299eee090468dce011cc5831e50b854ac27dee76168c481a6d5f5f343257ec0823517da298103681ee0b76eb36fa9546e6fdbe53c1f30470e8dc8d782c10f20e1f98c739d022bcaa1f3e9d39447368747b058b18c1ec82dfef49fa46f5a3e804 +generate_ring_signature 39fef726944368533d166d84ca3f2ed35aca6b33e5a3ddcbde30f1a60465c61b ba1907948b591f4d5e7faf586b3f95d9d259ec089ba3ab71ef4b57beb3e5830e 4 cfc47ad6292db3248c65c78fd2db49eea2c2a6ff4c6f50771decac7631c4f866 4454dc103cf1ca4cfba718e2d34dd2489b08e0b350523dc9477dea5f16743e73 74d30aed08a71ab71277adfc83d8e0de28b2eba61edb0e94acce6309973b6d45 2aefe7ed1319282d5e1e01220850c933406384c6a931b0acdcf4957de3891eac fca4d6c4cc291b9db974d09df386a7742740068d0db8e96b1b4faede19f06e01 3 0601bb0d7875d6d59436c781c80b0f563f77445936ea7bfa99c9b202b6fc7e0b18594363d1e66674a09147cd3c0602923d38c6e59ae3015ce767ea7ff30f4c0adb30d9a00c10c1fcdacd8bdde252e42bb633e8cf1e802f37908ee2c6ae05f50a8bf771ea62e4ab6c691f65d66cbfc23cf18e7664f40e9a81b9cbb856cb33cf0f51926a1e0446b105f8387e8a80eee286b643c3ad48b2702164055d529ddb9504d4a44c6f357a21edf9e0b6c6d1c9a2f50aa700c0e9f448b1cb37d6be73263f09eb288235e120d907ffe49f0737c0f2b7dde85f9799374bc02ce177d3f824f00009af09dd5398e4ded6351f9b051cf675f3b51918009824188a58d112474a2a05 +generate_ring_signature 7f0c29493bb36b01a82a2f7077d547890e5bc7831ae6a35a2f31a0f902d4854a da08db64452bb864c36174d98a4e14781c78b7be3ff474071c0afc444fec84d6 1 bcf34b4ed58ed15b02861434750fb724d3101346d5e8303437f56bcd5eaa5c8c bfe6a1b8cb2a570d84f6df232db97b7a8e4fa3dd8e6af54d09fef6aa6f67d305 0 38a871342fdc49ee6df8bb03917f67018139515d6bcdeac2846b174f4e8eb10f4dcc59224258c944cd85720d61c024af48d2b85e1be302f9b00a706250c93801 +generate_ring_signature dbde6983e1560c26fba2a47c5e5d55dc182a12985b43766992a1e1f119947126 bd9bd3dba5bf6a2d3e986daf25c2cad584ae7c3afa7c27f5a98bc220ed0378ad 14 0f89f5a886f94ae576b8e5d8b947f0e1c7430d6187b9cf04c718ef3e99d945e4 d3e5b4d8755f61d6ef6943f8ba572fca1041346eeabaaaf4d53b6820cda40b3b ef305918a04cf633ab888ed026dafce004b79e96ec7bdbed5ed73e7daa14f5d7 bca88a261ca9d4ac8b51c8880ec327b1893d829605be97a299e9f29998a90d42 aa06e88145690e0e77549fac710a5c2279c1cb330203a88efc44dcc81465ed19 a2b43e260b1ad8a55ee794ed03fad1cd4a7a80371fa5799451593dbd4d4c7685 2fcedda6848e5af9ddbb3c94e181c8153a6229dbc896098e1dd4892f4d4db92a 117ea92dd03c8e8d8b54d680f30ae97884be678c4a868e672c5f78de1012f66d 20b0511d897bcf5c00b6d601dadd3bc7a879fda5fd41644dc07015fb95de9a13 38ea2cf665769f2661fa7e08165a9bf94cb807ef946d2cae10fb0cf314919620 d7d6c773b3294e5aa5436ed9c61ed5f03213fab5b30b83ddd0e7c12a412533d1 de39e3972890dd3e175e30880c96f497e180070bb64d726b968970d2e3be7ff4 fec305745ffcfb15d1e52cb29377b9a029b555b6a4470f0b8165e32d7bc8afcd 623eac57d7111b44d17da4b98c585b59eace5b904538060f2e4527b2f08d7d96 7817e9be56a20810243bf96eed3585470b75dfb717ccd52bed820321c9999e0e 13 6d39802bd6e4368217b7c660085b2a73fc81025606f0b7fbae61d078bbbb9a01996de5f69ab332095c3f69fa953133419e4e27c321ed8172f66a2aa28e9fa0029b8d89ceda19207d321a6babb00b14ba41c0f90556e6d528b40e87bcce10a803d208267c873dbdd050c6236dca754d516630b964903df0a9bdb1c16b74ebba03abd556a0ce6c12d0cc7a0aca849b9c2bf51a54c721257e1cae3b6c5091e9e00a2e0fcc498465126d4be33eb978c8bba17ead82de93212aa597eb70de5fcc920f6947781427e35107a86b4140807a11da8f8972af5ab00c0143a898d2e83a640d68b9c7267b29efefbd5aa6dba5d69552aaba10af6b00e28a448f86bf1b95bc080a1bcb6dc37b990723e056422e2897ca1b47005160b0c4f9896642526dba8a038bce61eabb2945ae1165ec98a7456dd91a89fc2647ad3d83c78a0db98fdeef02d8dfe1a2a69ead13134faeea677a5dc60056b979538758736e4c22be4d1c0b027dbc2f9780a49ec94860aa0fc0fd06a88df8c79c1e81b802e793b38da9ae8b066da8cd15937fcfedcc111a355cef7fcac20859b4e020660cc1d568290ab8880e1ebb71904a36f1fef6f19873a989b7c73ec96dee503c2ba3c5af2b81d41f5f08ee2c001ff5d1e278ac5584fdf4568fc29932e66e4b3fd36b441a8ff3a265e108d6cf11dff6571af308637cd7dd4143489760cd5e988c5de2dca986601368a50a6728bb4b4ec22ce0107d733923f74cfc6a3ad8b02946365f9945018f95460e01bc4c4d47cb1c21526b21ef7d855eb4aa5916cd514a73e7db3e02ef9c260b46001dbfac6929af65ab9dc15ef98777936d5fbe345977dcb7f14a65a2c130914b042d6f28c4cad41cec7a58cd817525c22236e1ac903f745c28a034eede566e5200197f53bd9d85170e47e09dbd33d44e9142c15d02b975271b08d7ea4f701d940e65b9181698cb3a9de7a119ad84468d2f496487cd5dd94bf0dbaf6260569f0a0735270f8632614d89a67666784cafb467c664ae0b31e27d4dca9ee596e05e05034988369abe99431eb9151b98edd9b52d7aeaaf58cc0f0a88483dc06b769985051532c1f167b0012b6520c997886c1941c54cdf7bad5c411345eca8d0dd7c9103999e154cc35456b21fa753d270edde105609432c546de1f38790ea4ab6b49c09d6d7105b5c6a473f20924f4fdb6442643a2d2d4fa474959064a35cc64e90210cb62f27656cb2bf8a82c1be2852467a8640e23c8a3399d98b157132a3b104bb0c +generate_ring_signature 4a049f0f8bf7379759bafc11f3e9326258df0ac45b612ad5088bef00d28ddf03 d4eced528b619b2c7e3478ca4325d630c3e79822e59327310e1e58f2a5bf46a1 1 8e1cd22c64de56e319bddfd26af3e8a6213dabdd844ced186eba1fead7172564 ec5b99eb1d24eaf612b507786695acdf1d15372e3acf311457d7197159059d0d 0 2677efcac75a0c9d3e07a23712994352751fd133769e8c7589bcbc00c7495b0b19242104115260fe9cd7eded79a9c7d95414a83317822fc1a12741cb5304df0a +generate_ring_signature cc597b17e1ff8657ef92c92504a0a1638229a7960b286503597687714d90c0be 55ca58a01832cfe103ee846f2a11e237bd11e22a4baebed2548e75ceca9b2eec 11 02c74957d08764bb2dce5202466669f53b66570d75afaf9d6394bdf325be7c93 7aef7a7f9f34bd0716ea1483a0a4372b437ecdf14874b98455ebf4f6ea4a2314 affa26c8824fc326e2024d05c1286ad0797e4921c3c75009f80f965ad0b6061e e9946e75782aa240166ec2d39b2232303b0f79774d2a9a08310b9336eeec17d3 a7c3963b8e1784fd9004e58fc205b2de95f71d24ccb474cab1cdb15080931520 80a6a8dfde031883f53bf598dc1c47061487b20f393d0a630f0b15fbc71449d3 d9583c14e71f5d28c474e25fbd43336c5542ee1a4ce603f5299e0273f889f9d3 2ecca88312088b7e4e346b72d37ac2c9f312d4d82a842eb9393ebec65a0d7fe9 b59e25c663d8617339865199c0500feadbcc5e2b9d00819a3b91304266cc98ed 9acd42dc028bf3b39fe1fbd98d8eb7c7c3ad85acea86efecce7e41831e5a0e03 3304c25e953f30bdbfb6ed159e4a344c804dd52aea4d104bcab5a281d6a4a594 53cc0ff1070cb40bce44ecc8de8f6b189239790afb0545d951992845cdca0b05 6 79f96449ad1cc312d124f83e2435238456297d5637eb1a2bbe8a68c6d1fe490e06b05430d4eeb2c11b3d93afd95eee82e7591872f544d176b44267611c1bed0596f1e9f4b20603e00bff0f1701f672c987b1df0f673b994bd655d2a3ea9b3b042b249fcae65340f381137e34639cba49de5be81eb329153617f0ea0782f4d6048653e96c9b27491a074fe02749a33f0ee013488c9168b18c20582bd6e5e0320abf521fb4df547c4f7dbaf94c6178e2d0d8907ac33e77031e9bae1a80d017a3099e9ba3c16a18b9aedcbfc8d1924618f7ff58135ab0cb3f85295b8766ed0a1d08f680c539cd0ed7b1b24630bc9f5c0cba555f65c4e09f4712da4941a2c733ab0a08f79fa6b2681ac05035c24e300ab3ff54dad61eec6865cde08e69ec735209061379c693f85c3688f4a6e04601fda6cbd0c028a4a80839f743e79eabdcb9ba0c8c333ca605f3a16524d18e3a25dfaf2d06b4cf109e66e5d92f0af1822139460797383edb729bb35ea05156de7ef609ed5ab3890b5b80c46a278c392c3b75ff0996a05ee80ba1a4e50b85eac2978ef8a99f6c7a04d80bd13592ec6f3538eb7007c32822355461ab48fd917fecb647ecdf5c8551e9722ed0af5f5026d98004880666ab29734d77238e63859bfefc960953f83454bd809375ef259dca1dd030130194b4f11dca8767af16c5762fc0b8d300b9e603eed9b68dd52ec723029430840177c555e64fd7024244562b21f7aaf4bed96ca623b58b4e9cea3434401227a20445176bfe361f413d4194693a275502f6ccca6c2f8f36f9db94a6e7e6924e950d802e1e22428971f82e5d68275bd84096ce901ac53dc44ad6dcaa3836d9a8ed078be51c9de93333316eb59a5fcb86eaf42164df35a9a5d4221286210078d5cf0779f249d7bbbab6c7a77dae733acf47082617d8f76b899378d8200477d178a80fc69e37a4463af1e35b45ed62d09a2a53cbaf5e26bd752f0321f091f1e6d9ce00 +generate_ring_signature a1a10532175cdd68eeb82f0f46b27f97cc2bd57ecbdf6e339a92c61d67056fe7 a7cf65fccb2a26a3470e9f90ef57f9c406df48f0dcf5e9819553605479d54cb8 1 516241f236a13881ee4aeb786cf686b340ea9f3dc8f142ab7939b63ea948f9d8 0666045383e7f9829c827c19f36ab1b57e58988af20ce494064ec1783c85fc09 0 213cd32c2a1503edba39aec9b731e2e9a95eeb95253129320cbe05fe175d600982bc615801e9683302a3049e8d5a3e1126b02c14061662a44286ae75913d6e0c +generate_ring_signature 33b853afc4a8fff7ac0b290b524bc23068dc3bd098dad3cf44bb96470ceaea6a 13b719bda608fee66c92ed1f005cd7993c144ce764fbfaf5349de4ccf5b6c319 1 29b8fb819c8be48673c0b35b8d3dc52b81fa528cfc91ac486b7751b1ba88ea73 bb666988157a61e08efec906b79f3e1321fd06d1f401712c0b4c47a98de66907 0 7d0b0a661f8268d9cfd64917f795e5fda2baa98b67fa3215d620cc601d478703049f726148eab6cb3edb8d77bff23686579ab086f79f3bc01767d12bc1fa9c00 +generate_ring_signature 7267598c7cab03345e1d5e770ea5cb4fb41d934079557f538b0fc4115962e89c 72a4cb672b3233588ce82c8aa41613f0e1f087a370871ed79b72983d414d2dc6 20 1fbc0e3d207a90cfb27c1619a4db82fa106454b554910381521071121f7cb756 3abb94f2520ef80147aed005c178e6ee800ab93d91c284f924871ad3baa3d607 180edd37508ccb4ae708ea73f0e56b0bf4d4a0367a7fabb67803e1e2937aaf70 947f5548e9c8937ecb3b13d7f8c17889369ab8302ffb140a742659c2d1744043 54c8e1ffb3f50f9e364aef8d7164980cec2ceaf36edce662252f53ead9a784db 613208344207cd0665eb79c8e0ce0fab7f8b864bd49014c7d3fd20552194b947 338aca305d87c2f8792b82136cbb7396bdb218213d5e282bb4d29cd713389a33 ebb825b0cb8392e3dbd324e009fa3cf5f22662c70b35ed38ad32155942d2e7fc 2a6a57bc6115aecea112235dfcb6908a5305b54c3e1be6ad47f4286b4d94fcd1 1e9131251f0cbe92c17dbf1cf67836afd9dadefeedd0011f3d53292e889e750c f1d339e8dabea718797095a461fb2eb70e536fa2690a639d486c027cf5af6cf5 4f4f6582bb2e5eea2f1b799635680b34e5c7aea6069dd434a5b1df7ec53eed40 00ef708cc6a3843468e01fb87abc5a78fb3e44d1adde671ef9f977652d93f0ad 1b441c5ffb9d966ebe924088aa42718a58e5f21129fbb5ab4710b30a5eab0bc1 28ea41a63a7c05801ffa3f0775114b52a07540e024e4f437d15c6f596999a54c 5d1635b9996f202342826c0462a030f4a3926923201d97e2e97782dde4232e8b d63ef61485f58a8a8421c1030c63b13e7f9285b3d17a97c0ec617156f3d87e12 22e861e8e46894f227e0c5aef38e7144d61ca338a95c8ac00c222afc157a012a dd99fb211e2e4fcfef75cb569e9a5f751bed5f29c5f69e4afebd558e1a915b1b a79ebc1f781ad0bc716d9d20ba4a4be3a41babd8b940b57419d4090ebab97ba0 77458fd9ec7a1208f2ebed73ce83b8910072cd2e4658cffdded55675eacaa305 8 0480c96fc5f281c09bde6b836fcdf9cebebe890a9c7d32b2fcf51dd184245d02b5520ab2ea5dc52943ce647a24d0f460ca71d5becc99e6008f4e4b5504a3a4018548fe0df0b71550f15f6c780c7a46f2e58354080d83748a3833bea7295a4707e6135516e2eb4f825d0aee624639c765f57aab35bec7fe6ccb544152edcecc042bf6919f45cedc9c638e15919d511fccda85ebe6c4b8f3ce48f98dc6114eef0951036f38723bc901ac2a27ae2f399e625e082a7da1e8fff3e53ce08ae798050b9cb7e886bfb14650b98cb2135f836f9991c714f88e647fcdf4737a8ab074f60d5eebb0d22f1a6a156f41d47817f8e71a213a2cda9fc9138b2141b6dd1bd1b40a789adb540f827cd2c481f27904ae6a3c2ecfcbf8871e4cc4886f2eee673d280954e12ab5491dd2505156a77d0b9ba4bc5da27fac8117d2df4417d082152a0603fd80070a826088f496277e838be997851aeb56d6d0684aeba687a62932b4e20621a646f3f799526760c39a0950ed7a43ba336212abfba34540c77a28d9f4bb06ff979e2a48aa1f7e3cf50353355c19fd124f0254c1efcb7fe6e96dcc99d36907fa24037bbb55bbdab52a3df1ee60a950ed23863f12388873f3a96294f21fcc0a7e0c619e8b8eaa2c92eda8c4ecbaf2e0e459d4f956f73b66b126cfb123a9c20b6671bb24889ae896edddac4dc7ecaf6ebe6384501ab97b6cff92b6aba5651b095bdde9bb00f13203219bf50f52e21a12a4251958238f86d8832f29eaff16bb0ab098c7d48c0653e177a106b7709becb2a0b1c0fb1fc46326c9a5c134954a2b00e2c3a8452222627a30dfe73f709fa4697ff5f024796fd5073ffd3fbf0d952102d770dcd629b37a883b0feff338c0ff306c6e8752c2a821ef4e19eaefac504e08695977fc2405e2db07415ea9a157f5f27488a735c95066bcb050a1c583f58607b0796c0732709efa610c18f84eb8a90b754b8d2fac4e77ecd50c7211ffac870390405d9e281624d87cd12bcc435dfa080177aec9e6b5859887d1afe26898330e1fa4ec1473a73d78a4ed539a39972c61d87ffe78d915cc95cd3e1278d7177d030ad3e6a8d3cf1cf28b4d38a529356c58b78da55e5c48103a801505a040307c0207c05942b29ccaff175185691046060ae4ba48a4b7b804bd0506e361732a7b0bf71d2f6d35bc418b1fe686ca6d3915f0c5b270872611e3dea817f6c49b4d0502ff3ac42ee57dd553bb74b0463c1a04920c549c74b2acc966a262f55cbce7090eebf981b139f527c39b6aceb80905f186629919ea8444a89c8a3d8192b5bca702995636ab36940ef308beddeddfaedca013a37beb2fd41224b6fbbcfcf4453b05d946790e7fe83f4ac68ba7fbfb6db4f535eff2cf48dfb8ab991813a6b4d87f0eb5578faa8daf01b197582c51bfe2d6ce1050882fa96e56fa8fc968af560c7c081f02d4d3d0440581f59e940e9379ddbf4d41045cf5759539ce8f4288933c0b020f481749ec39ae928a674cc65f61f9aca32cbcec456b9a5ed457e900e772f40e1be35221de2c1351f192521c4d730620b37ed34d9ee4a3ac7c6e8bb783e87d01fad72001c6b344eb08fc5e1c2cd0be939ebbc1367c884e4e2d40267958d094068ec11eb28fdbb43b57cc5f70c05dfad08114acc8b8adaed02661b5cdb471b009b86de0e47d3e690fdaf72cdbf0c078a20ab46c68a68fca8c6b7db0198203030d8e24064caacd01a80b20aba98e3d3f3a51cc53c998fd9e13efc7f1764507eb0861ee72eaf6daec8b1f639d7314a972a0d2d28af36790b00f851f51b3c10a2501 +generate_ring_signature 43790ade135a2bf9ffbec0b7dbf243f6dcdd40eb7b420d849868ff7aaa0f67d5 f454c7087f82361c3912a9120b4226db15f0d752d96a5f445e19ab88d0bb32cd 2 5513355588936c635e602614de59f0dadd566c4e7a77fa151830e454838fb72c c00a25f291c66d07365ad72d80b0562e7a91e990305a0bf0e5b01a0db499c8e0 30c8fecaa0b7f217aacb273eeae2ae495f65dec9dd62c17965c5f46beeaaa705 1 9af906242d45b508dd511fdacb50df785908d4dadd67cd06246c1759a6f8870ed73acd6dedfab49a0132df0c22235bf78050c5c44f37067a317afcf493d1e80b99fc9ff29b0ed56128a93950e36052ef6c931ffa59299f584cca699b96c0ee0cf7ded1d35c8ea93f35eb760f0aaeb89eb1009ddaf6fb0448b39a43a9acfbd509 +generate_ring_signature f745e1b2dc48174a463a7b510ac8e6bffdc6303c2816ca05195d9ae739db3ff5 eb4669ddcb40d449366daba63b22670bf36b64ea51970a796f07fbe78b6e1af2 6 fcfa341e9f83038e4f10b48ea65db8dcc728b2324140891a0566c0cf16017af2 3abe9c454ab3179b0fdd96e644d37285582e54423cf27904fff0cd060e3522ec 202812d70556c9afedbfbcb2b863e47bbfa43a0f0e366d50fb369cfa9ba40496 f3280be164b445d7605ed53195350d332b6c4a0425993782d6f5974520f433e0 8b368764599684ce8308969573d800ea8e4e9dca46c25429005d23b8c2ef0f67 dac4c7e0b261d9ce63a7afd3a1e7f1d3c04682287567525b1696ba305a26b42c 40e0ef7967fa1af5d40c8e74b5aa2ce841b04634e8eef869a3e25b4920c6f405 0 209983d1d175ae6fe2db2e42545b32411d1a598d1a2e3d684b06887ddffb170b8e390725e399029826bc3b81a049f7d82841e554f34780e3c650d405d89aee04e5066c618c555963e38b930d50be91312d8453dd719481714d3333148c86d507c081fee225310379d1b58a5542511a5d6be472cef944e9697ddf22e143b3100bfeaee842eeec4f27e344b381c00cecd86401f5c0b8c9d2a378963c0b7bc54d03e1ccc05965a4a084e0e90aaad1fb9a1813ec90452c85b86900674adbd1d9f50b7eb522e8a3386a259f12ce42ef2b5c7f3ee21a0602e7a8ad330a3a4f20bf5802e74bfc22706f6fc1d4e5e90b3fc0fac2fbc687f7d1a89e4aec13255b6c930309c9956d81b40cb57db49f7029e029b49de5b2eac7d0fef966cb01461636682801e55f60c2d380fe1826725718c28315f3f7d35aa542de5323337e47b6c7923505611676679c592eddde060f26b4f374410a4522eb35f064d071081e0e9e64b50dd800e9121b7db458900ba7c7fe160cbcce78eb3904fd27a05378b06b7d204502 +generate_ring_signature 0c1ca4f91f49d1955400c6f4f0ad43a05b4b8e5bdf8c10da751c98dd85c47d95 ed2b4cce5bd48fa761dc4b884062ebfcb4d51dfb6add0bcda77488c533ec29a2 1 c31c24cc6088cf25e09cab4318376a3202945099c5b2117ec04a0d03da2afea0 7297a530706b0a9826fee21337f341f3a7df6d4b53e78d21c0ce596ad2c4fb06 0 b79c42e9d09657c39cf189bb6e1c94b4aa3259c6c82350e8a50a910a7f749606248b9179bd07fb8238a0561f3da893e822e813b071a04da5fabc92e668451509 +generate_ring_signature 31e7df067e668a7056c57de9230390b3b21832b0aaf26c1d136442f138615387 5a53279d3b67d60495984b9cb94036d0504971c6f5d582b8a9c868bfe5856de8 28 d8e11f5cb385732a6b28074afb6337a101560a0015e9772d3f30d3948adfa268 4da2764bb6febfd8c8e04b2cfa881095a1311336854fd54fc4b6b1d3b50b3087 03601a88b19d5bc27bab15b33d1ff472d91e7a928a3ac537c709dab71bd56e0b d18d584e85ebc1788a95a43f24242a33cc9fd281a25029934850752ba44d1cc0 a2696d71168201625a49011911d328a514d6ffc61600e807450349198bad3d8b b1c2f0574ff45ceeb330de965a3ccead9738da629a941436b22f709939a536bf 7d024af0ad9a8bc11b1870d4e4bb34a3aa7f615b47728c80e366899088627861 5e16ab4a356ca7682ef9b89c2ff32ab2233393c1f6f8ec39945fd8f68d29df5d a4b8a7093c7b2f11f4ef98f3e17f398c05daa7f6778c4ef6bfe8da1b6ad8638b 0f3c7ebdd53bfd02e2f96ef56487d96efcd6c6d791486816317d624ee90f31e4 744845074f17c4d32bf7b9c0d4580e94a1e86f2c5521c5f3a2c08a815a0978b9 31298b4531ac19393a103da8df5afdca6312fdc9bd467bd488e4aafe0336563c 9127fd630d5000a395971aa0d908cf98a5c7711e0f01d460a2a7684f52d059da ecae915500777293d1a23bdb2d70c17c964b9a63ff8dd4ce6dbcf45772c3ad3f b48d34c124988a550b37c4a1d823017ba38b39b790880379ab0d9cc0031f934c 2de61fe61cbcaf6ea1f10cb8ba8f4fb8df780cc00c0e517bcaf9284cef02bc46 040f04ae028b915b6d1c7d906be11eeb401f7e352def5f7613e211fb0fb558ce 551d509feb45aec8e434baea8603a2ff77b5e3a07cbb0d0b527c8558c4c64708 d073b898ab4ee3836c67d0e5d1dda2085f4c12aa416c81bc340b85c3b8b6ed52 435503b90a2d14f405096ebca6b42282534834fe9ea39a6bba5a4caa4f079766 2acd80251b3515fb625a9a18d2154b9ad1beb8d8b010d8be83ef0b749c5dd6e3 e5215f531238d2c273de542c6f4fcd444e82134080f087d0afc64f6fc8bd44af 22424ee51118bf80a8cef77b8e113cc0330e24a3844dfd84917fbead9b8c254e 2fd2a01313fc8b08b276862efe9a959c3b1a19c26615d90530bb81a9f7bb86d3 ed488f41eab4fe62d9ca4b7b84264cc8320704afcbaa174d0cce1799d577e741 4294f3ccfe624c32b9b2274e0c12e55daa840db023085c7e25ec32e00e889bc2 0f8c0b190eb31fd4afed7e449e6110176f33ccc4d986e948104607a326c879d6 3bdeb0e9895a3814004480cc6f992bb3d35b747f697bb88a04147e419c5c2c72 3e8890b916eea212d4e1e8c72b2775339eae8c42e4ae8b43ed09e24c694e7e0c 6 ccadf6b1749d19fbeeb11a13276a7d6351bef5696f107dbe5459a9116522e3090c204a95ee2e62723425c5355dfbda1e6c0b3d56a8742d52a1ec6ad471e05f0a7d9e33d19a221ff68085df2ee5c562c6e0e204e1236a5b0ebf43584bc232690802c3bda6bd812ba50e521334c5f69c3bd36a5d10afa89493032b3756f2bf8300bba5a1266115337e3c30839b58a1babf35f687605b5ff5892a8ca5124f25fe09d7e05d56341323b9cf6862c9fe76b5cb9e23094591f32d5b9d82a6ffd139cd0e10a05627435f7ba82099355f1833b43184b15970748f4df35bab0cd0b3a927052f2674dbe1f68c5778e04eaeb07b0b8a5d032c4d143064b4f2ba6b02c0b34a03a6bd5fda8766a42ce12917dd4a8cebb0dc120148ce92d6fbc66a98b00728b60e773e83767cdea8576017744683dcf211d6687e174827445418536ea8f8277c0ea10606d2e2e78afc78d00253666ab905de8fffa97860c9ebb9ab72adc7e89d009725cc85ad44f0346f6fc67561a6854c13873516722d964e93d5f5e6b80b4203980f2a574fa5f7545ad67a397e1b6b78144569cca612f895a2a7f6ec3f74210e7d6dbd6e25a5fcb2fcf3ea4c4ba8d358c7bb1524f3e7169f4a7124eaedacd0012e7808c64bff2de2edc045e520eb6270dfe865e6a83de0a967d07e42d710a90e6846466cf9605d9199e4b18253ed635c453285f3a98aba257c01d98e845a2609d056cf8a13c17966e06cff17109e3089cbb2fe04e87308c64cb85e330b9922070a64733849d9a9816296966121e894724ae138d629051de2ade2b0ed3cdfad09d1cdcbc0acf0adbbf36caa14033944640b4d4e1da76f7e0cff7bff0940bef80f370109976f394cdf02f63bc042181edd629474f9ca4bd6e5f777108545ed690b11d5f4332e5b8a04a7984e526123a166a012d1cf02cd63b3d386ebefe90f120c774dd8d60dfb36da55f6f372a20466c103fa6c35f004deb76505f1177980a508d58f83d31c891160ab486496a1e58215274ddb3c9d147fa882ca76c3b50d690a8a06d03195369d0c99bc096fad0672333a0f56ac38b058edb707d7a9836cec00f3a2d1810d4b9f5d88f1f5e4cb9e6c0d6cf7ddcf68efdd0f5c922d301d951703b40171346860d16a37fe8b13ac2d17ee4f3e8596bf095d97f48dba9b453c1c079c2eb2a126b57eb6e2ce33a5cef25cd2f55662a5cdda73aaf60c635b0ac3560cc28f84dd01010300e847a91562fe654a337797e40287f4f7e8c5ef44d730be0d4bd7be57c3229d49e44fc13bddcffb4c74ebd0171cd51e2b49582c9801450204ec07084defb841cd1dd52cfce5599a76d3a5aae0a7030300bb338002757afd0689341be22f73572c5e430a2fe8972cbade9bae152cfc1ad9da8a8fe82f28f401dccc75a32d68861f8fa4a07f653369d0f3621fc0bbe9d65997930aab8b25ac07cd079d43a0b9cdd4a1cf1cce46839d9ab0a26f9a02cb0307066521f4496f110cd74db73458078ccf6391be33cef79182859f04a93d9a92ea48d384c99e48b90282d775145370ad05c3b17dd9357f4149332efc4498bb4065b5b51b00de6314051ea6bc8f61492773ccef7df9b0310c210b65ff7d4dc41d79388b5c4cc6af7c01cfa3e0a5144414c00fe006fa1248891a86beed1b7ad5a055a8b85a33842aa80f8f8f91fc2b9c987ee245cc00799aab1984131d143527a0e1c1a123906c896c0d7807727c60af304bc2034c129cfe002e0a72ac225a2e13b050391d24c1629c0de86afaa0762d65404530585ffb1090fbe04e314ba18ad1001641e82babbabc01f5cfcc92ed3dea9c042b8bd12638d41b909fbec2cf471a6c275da8ff2c991a08ff2f8d0ef14a5f7005dce8a070ac41f9ca74d9f85e5cc7d54dc13277e43c28094a674e4ba96026785f00a1309a1f24291cbd0e1c6aec09b96b43aa18c0b25104208d63cc2cb2d7487aa4045e75e61b999ad56d7412f174ed2676b142523be20c67672c1c55632e379171744ae0ba9ed5127ec93457e9b240231ab97a6bb40e00be85e3ac91470e13ecebc8f2f711592e9f0a5c51fe87315ed53e61e182c3b7062cb071b7fd42976670a7c0e939cff16f3efeb9749550b9b4c3ee4628afb752042da7e1e318df6075336f018f6306365ecdd6e6da00ed1407663a123a81c9c30a7b9154344ebb0056cc69acce01391cdc68b3b7369333b56babac7ba8f12d3c0ea18c88c7ba103ea701691eae77b3ff399c1319c939135233c991371c48b3e9039503c2b3303655740b8ca7823021002d877ea64356e23ec30a3ddbed8f311c084a00102e40e3f03d16f56be99c746c4cec5d855c7ef77587bc9ae58ce52a1e0152305ad3871dd35f73f20cf0ed7af7786d5d9366cd55e03964bbbffc2f0adb0e8d47952b21c1585666487cef82d40d6a879f86c4e1bb39beba3f680122a00d0ad0453b5f68c46469ae6e8a555b0ac2fd1b5dc78f505080479f9100d1c936ca0ae56f610bbae7b68842bd33789faffd7c2ee4b83ee5af164ed694a44fe92e040a +generate_ring_signature 74cf9d48387f68da96ca0b0be54b436b877a40b8d7ce35afbae4930e435245d4 21d51622f92eecdf0071d205840bb1e1e1b8b26d69942dac91980a694df9641a 2 b06cabb47b0111da68931631552e977c5d0f7391abd7e835d4865ca6b6ddc4c1 c59a24c4128d29bfc1d676496b3b275e1623dd226ddb23f8b4852eaad13038b7 880eddb85bcd967112fc751b1512b490b11abe5d054e43126990afc8226dba0e 1 04bd30cdd64126a47d8ff7c8788afd5e1704091c20b9fbd5be399f0dea62850bdc0104759f7475654766b09b7788d3e1a090c59628ae2a3860647907a12527020952fa76b7211af7088e2e3ac5b8f5fcfd15c4eea22420cfe25841184122db093f362e41ad9ebf70074e590d212dc202c1eaeebbe6d4b27095f571078990b70a +generate_ring_signature 9207d102e70ef77349166326193c037511f13940a761e9caa6276c186b3eb2dc b935299ab3141f7bcea9047224771b9eda89b7e65d5bbc85ca163f1d7defb31c 1 764d6791a583a488cdce815e5fa424f70de370e2669d6767ddd3d553c6199343 f6a1a2a2a3dbd61d40508ba1f192c0dc37fe3d297f3df14c9730a6f084779706 0 fb190dadd6f34af307dbe0bec1df71e8c4be3833d096925232453461dd91c9050caa89d49eabc8558e14320f29867bd3543c653757ddb4df5494e416bb294e0e +generate_ring_signature 8f1205e1c7bb2c98ace5d21dfc38b0e52715a4dd21f9b7de40d9dc516c0483fb d27c1b3252173f413b070231ffaa38185f412f2fc8af2dfa34c6923146cb8f8a 229 52c5784d3cda705f4866716a833c47355b5fa4898cdf0f719014b1e06da6e6a3 913c3f015930f8ef9ef6e7113a6d9dc22bd50d047517e2551fa3eaebaf108502 6492b0bc2eb958caa42949a7a154c2900bc56ed651135dbf4b1b170fe9cd7045 15511be2cebf723622ccd3683485b7643fc64514f97a882d9254f4dcba06aa96 30b0cebff8941b4295fceb60692aab5cc46f9080e27da45272545311f81f211d da45fd73c50d7fd41205025c634b92098ac9d86dc163fbc5cefb2bc8c50ffc5c a6bf60e5242b2fed9b365e5e30a7a400c1ed7a182291be8661340f03a63ee265 5bdeb86000a7c98f5c7c3570e807dc75355909ddf07ced6b1b8a49b8ab5b4b3b 7626e7ddfac740421e4ec2f375da22498175b569cb9ecb7cd0ff729f17c77cb9 33f89703bcc1f9888f44ac7af120c14ac800adf93302e7fc80b6953318683540 15ad941b050df08d5fbed5646703b809a4b2bf59fc806f68be975a1659a3d67c be0d234f31dee1eafdb19d6219a12ecfb0cba4b276dbcb9c7926588efe936bae d2c6f00ce060342eda234645e3302365ec1b1634b24a3b48bb83f7c2e38a4c58 68e9268bf9af8e28e84ca36852a5cdabf74996854218b84e62fe1821a6fd3be4 de95a3015e45e1e1a20ebe69066248bb6d9091c33fb5f0fd99c99e2c98b3d9f0 175f159b521035a0d4244455f49c292cb6a84c4820ddfe691d34c960ca04f7d7 8c2334a87ffbdda21fb11cb805d7a0ed8ce0bfe34974660956a8ea56395aa4d8 c7edbee25c17019b2202d4d0536bed6c36c0cfe9368be0a937587792be18a73c 42b6ab41fecf4dca2b1150f4d8952ff6d3e26c124d1abe310be551f2d5f5275a 3622119839daf53fd79c3d53a87e92b7174a4ca560cbad31f906b8def47149d9 1ae04ed9d6599aaa3208bb092eda99dfc9e9b48f08b73b45211d907038f38aeb 01cc92d5068bf3d9ccdcbd5622d574a02bfad10f4efd4b38d0f443da3ae00e5b e862f2cd0bcbd35d523302201f9bc7d15ba68b2c03076e475005de87bc5bac25 0a9d2fd156f8c3e893f28bab84b7b9384e31a15d1131386bd66fc345e5599653 ee3144f7734cb223aca913d15b02900aa7122f079f9a6247d37341be18c7493e 49630627a6644da9ee51fadd02c54dba4abd237db31d8e7f911f9b3669c71ac3 cc1323b8356b0134cee1836eb0e42282f78e46a54ec1406a7d28ea639fbdaa92 3cc0a3b7da1acc1ad6d518029595b1221e9b632ac78a707ed136b597d18ea8e5 1437804f33e10dcf092b2e2de5ff4b0e541b103b05b160e6416fe5d915b3d867 a28c48c79a7694db14669c83277b53961a86332fe3e488074b3b4b637e7b13a0 0f352d25647efb575677787de2e358d579be0aa9bea73dfbe3dc6217b12706c0 ad3a5789bf9a2500ca930d64d72f5166516c71f42534055b3a945447bb0a4fa2 0e338d303c59926ff532b127bfe7f5275b6ba6110631ad6932059d028fa49b7d b68a76cc12b0825d96739793cc5365d02bfe50ceff68afd0b9865f65cb60a3cd 018de5869e7859cb475b48aff57f07f0aa8c1e1ffba1e8e96e87fb32c6d684da 06888fe284c0789eb6e33303797f365dda3cc8f2a2f9ae7754ea1192ce14d435 7f41f8ccdc6f2b5194de7e0477302d1fdae0a74239d94d481d3147ebff4f635f 1ff844011bc0f9a1044b23ab46fc2acbe00d939a9fa90b32dafb8d52e25c14c3 1559cd22ca6b489236f6854add12fc7c2946c2f1f02d378539ecc9305700f87f a597e24bc6f4dba4354d97eec4ae6eaa44428e3f35ca2d060b85bdc2785b2dd2 861635da1e9955e43980a21e4885bb8998c8d86a047f382f94f93da569985c35 66f0dd261e45dedbe26d5e060b60cda4da5aee59cc0ae63387dbed7baff10649 447477689524d8c7a55799d1133c47352dbc5d1e71f1355822fc984ad6cdc2a3 1011c08840a7436f9366091864d940e93e6ccdf7b04189907ae27e4d7e00d96a 954b68b72e15fbb72d805b79c8137d01f8a83546848f8e84d99d7e049dfda8c5 160761e382b3dddeb20b285a60335140f03f3d75acf9005a8274769d3d2b3b60 3fb8f5f3539927e5c4d8d254d696ad4a2eee6d8850d9ceae857851a608dc11b8 5597e4539febf5cf5572df1ae65e58ceed3d7de6c7d8f48d4fb0f3b895951b5f 5c84d316987cb2949689b99adcff5ddb82e0c1996f9ff5e7ef510ad3b679ea1f 2721a20b915b3e8b3263055b1e819947c8b6acdea2fad1d5f2799ec3f52f5423 abbc7b0ba883501c0d07f898df3d11080eb4780ad610e1e8155b4b31458b93cc bd7254abfae24aa591fcc630b3fe13b2f6ecaffa09d1f220ec3d2cc166709ced fc3646f77bd6635c64a53e50517ce45fc7a60e84dd81e4ca06235693d8cfb44d 9fcfeb091e1bf9cefbf395dba14848ffba2714efeb80f49e9b00b00c9f05be86 8c6ef973374deb3c15fed4721bd7aab0e6cb36eb543146d69ba5b99cceeb2ee4 3313b1b042d283088915c9416960722098d20dc06e0805f9d20dc2eb7b31b46f 9e44b4c47f110e2940b199cf3ffceb69c711876cf5f44b3bcddc9424ddcc6218 52387c696e8b95cf5ab7f9fc26fe4a66bf8e1d84ab56b787591619fa2c3e44fd 47ecdc3e664495b46990625452266fc65083351cee005e2a769986e161788ad4 93b8097c3c625640736d2558ff7678691cde0fc00fc2a4e0c4e7bb1b54463447 d32b0abad912cbed3ea7bbeab6db254e521596bd6fd944df06144a2cd685a68b 6b2a2312bdf27d04cb2aacf4a26994dd4b307efe7dc2b1393e815fdea8ad6e9c ebced6a281ec97f6f7ce32d1e62da1ead1173bb873770ccb11f79f9d196b4307 16fdba74bd5741106023583564de63f5b77fd0ae433253ed67d554bd7bcb2826 f1ed0bf4e6f4ee1fd98a9a9b69645fdfbe81ee7908dd0c21cb1860b62e356ed7 681c3a1347ad98aaec4d7031be4969b3f26bc09b03e30497d58d30bbf95f036c 0b9211db6b2a938346aa0ff7df2800f81b4c45a58e5d7689503d584d0a0a5b0e 7dddd689afeea27ecb36fb61df6a0f088152a5694f4cf4155cce68fa2397f6a0 accaf8a5f922e97e1e740b5e8f60674563ef8efbd6daae9cecefa048705ca80a a79aec40aa552d5d2c8564d40860785bcd12aaa001bb3c1beb6846908eec519e 6a8c92275c25fbc29c8b9e9ea0a7eb2d50dfa48836a11890adb27053114e730d 604d081c0de894e8a44412d641baa0e067fc038347ac1302aa2a9b0baced9311 0ada2bc88e8648a5e0c01a90a72ef1ef334849a5cec553309769ab2ff98618b1 3e89073127cc5f0fb823d9d0c8b7ff60974413a12754775b9fba0efc19efbb3c 01e8a98e41bc850b040d84719c66b985e798a624749dcbf8b358ff7d64c233fc 6feb1c7182cf86bd80f8eab7f4681e6f4af6f2701a024d8dc6e581b4a4168ec7 f8e67fea54bffd71c8629a90d50c7f6134a0c2b0c4526795ca2a5e07f146f392 069158fb4cc625c32daf2608bf603d73b7cf3cd344670e218f3e8e54ee691ff8 4cd66ad442a4cfc55b4d0e913daaf33cdfe7dfd1da22b6c2e3a766d2e60d772d 1d0bf5a9a0c93b83efe4143b4cd759079984ee4b0b56512a3a0a5e2c0c9d287f 4df2310fb9561413b277873f38428cb814f221dcd694af87c2a9dfe676a64b2f 370a267d62385479b270e46f8578fd2f68325de6b7c71f7bd6ad678b061b71a5 7695614d59d1ee67af6684e4e9ba5e82cef5880b53b3070a9ed5520757fb2f2b 5c95cabe47f6be580ba29dfc59a01fe24985b9cca5ad1c8f11d94a3c231efed8 520519e1a10836f288b25c7c32c15469bbbe2618d5193df95381d1cc32860847 0d1f46f19b2ddf805247e8023194a97e1f25a23d7c9fbcd9ee75408b369f89ca 6691f76d727a9fcd6729798328728e9b4991ab4ea23a0e8fe35566985dddc1cd 1e9f2fafd07dbe67ce07bdc4b81cbe6bef69b5f69e90db19f37bc88680d67513 3a44c7187849ae084e96d6e6fc5607550a96d597a298c34804edef0c611718b1 f6d7535c498c428a792217943664569f4c301481b400c11a03c03fc156fdcd51 2dda0f1b716a5a96014d1ad7c0b0d6c3049be66082acc5760b70db40591b7059 97b342dff13f2ec104afa7ef42af22d053c0d4724d5c150a600309f679783c10 397b9cd9cf85af509978ee6324ea25ce2ad46f4b6fffd70fd31376c411c98b8f a38a93655b55c9ade39d4d1e6a81b5e3014066fde7e81e91ebf5b82f6dd139f8 1cf789d950a4748478226d2cdb521f75a63c6dc3edb4cb561c2e57b38750cba6 4644dbd47839e1e94946ace12b1dcf5c64920d0c0188649ef4a5fd11b5075599 765b04257c56f566bf8748ce698d77f0961dce48440c50673e838a9f24233a88 6b61314fef1282d0eae6088f5180abde895a022e6cca980cc193597a2854ac6f c2baadb820f257ca6f3d48de4c9768c4b07c7b8d4f7342acecfb12becc6879ff 0cb01561b6391e1493e1910575fed3fc3c314a417313256912de21c61610a077 1a1c943bfd4be26fc17ab352e30988407bc7de6de0899e3c8aac2a575b84705e 0b2dad0d3b05d15de7f035d82acac91d21c7772aab52cf2f35ee17cd1f7496b0 7e3b55ad8e3a235dac80156c46e290880d9568752fc2843851e6de584d7fce99 2cca8f02b11d269de7a1790ba4ab530b9f0d893d6a6cc8369d183170d940e860 3a7191d183ca61fab0c82a531ba5cf5b1da51ddeb1bccc8a3a5de1b3a08ed590 3ffaca59a42c9a06585132bc401078b1e69659c151876b562918cad9c66b080c 482a252d0b29f7fb876666d565f5f67aab6d39f29317d31236129632585b2ef4 4163654838c2ba6d17590d6710cfcd1bce67f76b7298bfcf92139d2502b899ea 94a1dc49c5222983b4d843765d51526f0f1cdb54bfc9a68f62bee58da9cd0270 4887f9ef5441a1f87896339506af632b7d8a9c2df05b002b42c5b6c32165fb66 c53bf4a93bdaf03f2e3c4561357ff9a247cf019dc38b621b916f2c4d3395f9b9 f94b686e0d068cae20b55014454f50fd950ac9f84ca705b629dfde4baecf1ffc 2a3f77e3c18ebe3ec45879fab23e3e3f1db348d06eb30666e3a185484a91b7a5 5a40ba01f1427edd310b9408c5ab2488e5d05ce48b8bc85f8c0081adf11cd9dc 75abf56aa4921954d4423e957f680abbf1e3f458a1717f21b69d26bc7132d484 08a037168f337c2f30ea5db11153eccf6f2cc847e052ef0f2dcca49e446f9c2e 9a88ed5ea6035728e6cfa1407aa9467df401bf31e5b9661160bdb48ff7e1fc36 5c86516712f28f11b0a96055acd38d7819d497ca2e4283fec8300da62612ece2 b8a0708e9b6d8586fc9bc3279a5f0a1bc99505f627bbba52d9cb4e313ebf4d4d ecc9011f2f931508fd5b199f76df7bb96ed8c1345a1787567d80b249d2b0eb55 18275f7f1472730153d052fe574b8e890a4a32b012934ea236b3663dfa28f14d eb3014649405efbf8bfc962ee7706e9bee6a4ef5625eedeafaae0051aa721917 59fee3ebc15513a0c63a4040bf31bb02b6c20c9246d006b4f026c1db2e7a49e1 8bbaf1b46137d97273c26d93918ac0baabba90084bcd209a0a172eb8e7ef9b65 8b2d7272c1ae09827cf434578ad48fe3ebba74ce2d3fa00a35c55523e3a9de89 e129b195950d493e3c9e498f8dbea5b3e1eb4dea65fc03ac2bdb26df885e4dff 3671edffe7f5fa8fe14ccd74930c7ef620eaec25534f1e0db04046db3a09d3ca b086b636c71efd2445cf6281fb3fe8c858c5a345a6ab38314e3d4cdbe962c34d 7ce5992f4a5b9ab21f3b00bd9d03eb3b508c4e8d472f6ce75a624bf69c489cdc f709e736c33fad590fc82f9a7147597d3ef86a3421c0cb90d4f7824f19ee0643 0a758da68465fe3fae79ad29012b6035395373d0959fb27bb440dc9468683f19 5de2ab6d01977528e26cb98e22ce5205b76ec3fe33dfc38beb8ed1ca8d65f41c 0718b9494418118f2da64066b22439acef7fdf1e0f8b14f0522d42d4ecff4722 52ef5deaf9620304b0dc480da5db8e3cf017484061de018008e2ca1c2a6f0675 cad10726ba8716d69051940c377ac5f6a82e2e9a23096597cebfcf06bf515e86 9fbbd93d71ad8ce532ab9b2024aa3cfc17b89137e91f4c3f93097e8a34a10e17 d7d5f11eacdc7cbb053d15d3f63bd0e33554b9dd2eb8c83800f8fb9d0b2dde42 07027433acea249fa21af4cc3df4123babd856022b1a6e7e972a80602a5292e4 4b1cdfb44d28cbe272563682d11e1489f4308bdb836b6ff4a61d0d38be1a959c 9f35f5c9b5b382dc4b6476d912351a322cf349d8b5861df181163a46cd393fab 210cec3467f2c3cc24ef57caf3f7fbf6b7a8b46edf272adbedd8cf67ee954aec 6d5fafbbdb69bd02b98a2b4bdf24065092c0fae33cf6c6279647675cf20f5fe8 6814c20e384dd2298476d630badd43d8834560a06e4a5d328268f03b9f65f714 d8bdec2059ee9a599affa6e3548a00524dc582cd194a66b5740332d8404e43a1 8e446db99e8e579e5059ac28440d2d2c6a6f1838bdfe074f2da61805b213c11e 50a06a07245c8319c6f64966086e439e8445382b79a31513ae10998537568235 5efecc1a91a9a9df0fffb1050cb1ac2905100ca92c0ef87074d50e17a54c4696 ddf9201bb6ae23f18cf6ef069d183d0ecd22b08e481d2f1c4760f3a029a17ba0 6c1f2b0a8ccce1c65d4f411584c4a4811894b96a7c74dfeb4ed9680e4edf0259 300e358000c2b357e48e532f4e02dd6901dea80a371cf4b4856cb5adf2f97c5d 49ca50fd86f5ee0445bf9292fd492e4140e550fc6a520024619501179732d503 026d202c9bcda09cef16ca1c5dcaaef6da40fe42e533562c530e1b4a69c17277 8e4d82f0a0384babe7fd0a4326952955832a37d1152d0fd189094fd0922cf106 c3ccae4f7e60f4fa82bdb101fbff68cd2071e2c7c5729b3e9d7a0a6a51809807 e2c970b2577d34c1a4849fd4ddcbf48bc88627d51c8640b3cb2ff73a0ca852c9 054bdbb5411a58a04ab389997a4c85e38f2c714137252db2e130b80947c505d4 491f4b4d406f6e89833f4255c2f53103a26e3fc9f2d3f04ab6f5e034f9d16a3e d7f7a5fe433888c71ba0567bb87dd28d719e92c71254a1f84238c7a78937ea2e bc03db54203b141f413912cb5a0cf4aca06609dedd432042f8775aa2524b0a58 5e59b2c9319d9e6b450020f4b19b4c30bc5b2be4d32f4f635005f3efdb40ddc6 4d3553533863be129705f3008ffb8cfc0477860675c0ab53a642a13445185e4f 10837c90037eee815d50df2304f1c5f35ccfe3bef086aac31ea953fdd5b1da1b 5783cfc3d851b2043684d08af46bcc28c218c4800aab3e4a25192990c3a3c143 a529b861e4f2a800c334f542c958072804073d33ef707a324ffb950a0d9a3bc7 533de95bee68271d28cf19a0cc1eb17310c12b00516d8cbdf11a0af3d90f6ae1 ec2a1aac02d4a55ff2436405b7e821d9612406a4e1aca17b3e05f54039d47fa1 cfbb9f0fe10486fb902b329603cef2d1ed5be4e50557e366dd2d9c75cf535129 b0fa5d3efcee11d9e75ae5e77cb0363f64278fc6958114c6896c77f776ea456b 92562a6e185be15eb3dba54eba90080b2b556ea4e3244195e97c289f2936c09c 1130fefded4d6afe6bd3c775c6f3d92e7c49dc0b5f4a64ab09dd94efe5bb27cd 03df5ab7e0e605c91b33709c625af020147fd3d14594ef3928524b3e41956276 fd4730ba5fb0ba5e26f9bbcb8e24be8e92768b874ba84bd850d3d6c6bf14fa36 ff340888ba7d0ec25503b411fba67bd3eab35e778299f4c7b8c4685ca0e4073c 7cc168482cc27ace90bc3f551975052082c33d3c74244cbf423910d00122e760 b67e965b6227c63f7b1f7c1cac3f4ffa044b499ef2126be0e1874db9fa24a6e8 af6ba3f29ce5b209c40565ca02b58dd4380715f6f10d872006488359e5a468b6 4267ae8273f14e15165df0be29aeb50a9a3bd312816a9be63bb554bd6b9f82df 4ea2aca4800d87b6df9995e3d83240df97e309fa4de089f8b12582cda536ca05 bbd135a87a41a425b196297e060306fc2ea93a1aa2b9f3e6ed2b66b52f4c1f89 d025ed814116ab93c9bac3f9a4272502bb77bf9f71562de1445129e041bd2831 12ab2d4d0972a1c1cb4edb0c36bb60f3c347a8a651b814325f85b326e48707e4 99f3623de0fedae77f920301cb2380861f9844fdbf0d71991b0942331449f9f0 a0f626fbf3ec494ab01485e7e9e49f97a28784231070d4ae3c6b7a9df94c37ed 580d0cb132d0204f173abefcf7caf3abb8bf62bf65bb4954b29f4451f642bbac 8c97a982ed265af9a10f3f8093be8fbf23bf583fbc5b85e8bceb0aabdef3217e 66c73b6297879ab3b614c47308a3e64501904c7bb61e2896121b3c97b12d3ba8 33c536581435b1ab7a93b47ad2e8ee25d3358873c7f13e07e38ea9d1fde9581d 358af96d40f33c96c703a54970c329d9cb71777c66a41b3706b161730e178e6c 973bd3b32a458e0b238a95feecc044bcc7640de370863919a517629d26a0c100 9d3ba5fac2f6aa8fc46a7558bc756ffed5a1a2ae57aebf71360681fed878424c 95898052706ffa6d889c76a090e8244d2e6e7d339610a429da38c3e454465e8f 6604e03547ecea591a6d36ed604582cd8abd72bdb2b49e0a917771da20bcbaaa 38d80837cb55b1ce698fdd5b8d44ce9bfccad9cdc19e0e3b7bb7de9f5eea7833 16bb6df962e6127a9b053dee33aa890210c2afb63058011a95d5d6217aad5d41 09d9cd42fbb4bcafa7fa122ee44a13b420e77c58add73a305eb3c2b89f81348c c5aa1333b34398e500bb2cf60376e5dd6126f4d3db3034259fdf55bdbd3ba7fb 3fae5690d4a5bc412b86d7618d36dc4ec01009aad3edb77ff776c673109c7548 b28456140a3e985c226ab99a6c80f87afa3519e955faf49227c7de36f93d6d5d fa133b36fbfadd578d68199bd44c7e2b902c2ace06dae2ec52034875ab963d0f 3a5c007a828fd572ff8e0e713c46fc41f5aee958e186c1b88198135b116e5ed8 de33d23fc56054e68f333fecfca5f4d576fec6c500a494b008591f99c2a6c126 8db348260e9bb621ed1ac68622e551e852db4e8bfefb048e31e606e71cd9ab0c d909650ed9a50ffcc7f26fafdcfe91064dd187eaea4dff027bfb66d734a94bc3 2275d3df45c2e67a1831735a9852351d1e40d7903380cf3b33ca7ad4700ad8e5 9bf506cd884abfccf74b7ac9899e5430ab9be9ce1dcf4339453cac9308ab4867 044e10b3a3f390f0e44eae198a8fc246162762c0303b9326cdf01ae02285cf27 3423e004c3572f016a2711b9b7d5cfa4be8327185f4782a83e719fbd880ec73f fd57d3173d91f4f5b7991b70a4a7c17993e783c9a250f909d8a452fa1d4a7d89 4fc2721e8e5cb0f0e49791437299a6969485895295be74f63d8253c3e0897622 c370fea6b41b19482dbffa79495759699404f5b8aec95c42d022e710feceb4ee d76fa502cf3ca64f27d37460208b45d8bdb4343aa09f60bc86b43b45b3bca456 63fa51cc0c431db9458fc137148bc7072aff9e34af67204b0646139b5efb43b9 09ea32768ee291622d18b4ab9b182a0158604757150dae3692016c830f19b508 f2b2230fb64c40711b943000fc9faee75bac64677635f21b141fd76aebb38dd2 5e39938c9c977fbf84df5ee0f5cddded6d55f17eee0a38dd5bf01798a9df1c01 855f49af12792ec85075cd22ce20f3bcdae31749371e906bd96504b1662c1466 4fd587a7358b0de45ad268c237bb9b299d6a9fb06414aa30300e237ba1a3f72c 3edf61a65c3ce5226dd877d6aa70d639d52ebea7fb4f3aa7259e0efc8a5ea1ac 5846a56eb003d2b04f8d8a5cd02391ce10bdc43ea6114b740f590070d28a7003 c842f7022d26e8f0c9b832102efa259ce5e77ac2453fce14a364c590d1ec5d50 f90822ac98bcb91651e1f71f9c0849f821e2810743ff1e8e7aeae5909908f451 45091874969d3c9792356d245536a8da824e9adff04698cebcb99ff8e9e20f3b 9542ecf82586fdcd124636a4ee4bf2c4bebfa0f98dd76b45adac28227b0f455e 38c69a23cfeb562bf698b66058d99e63b5ecd7b8ca0b1354a57e9d60140c35a1 f7f75d344ba6ecd4ee5072363a6eff47161aa6334c86e4f3ff832074fb064b16 f7815c6dd6946ccabff6b93effcd8cdb8dbc9630ec5274dd476b27c3344438f2 b21ed1f01a74f7cc86f2ebb376b203a82bdbcc17a6acc4700d314012d3ca7e6b 54655a6c6b8e909278c760828a75b2daf5bf809aa10d84fc68bf4e1d3636d5f6 6b69083974fab0a280295796253901ac813b657efea7348aae3e2eda762ff923 fbfdf4bf87b4bb696d14bc2b350acd77eae89f267666a092789e0af07a32740d 38 226ec6fc0c32130bc13519edc176fccbe844aa9aa9a3f31e2de4ab27d05a9701c30982ca75fe915fbe58bd1450ee11090e3fdbb6da76e2fef320bc0cef7cd807ba545ccae4b8bccdb79ddf4c0b30aa01e845250f16d2723358db03aaba996005e00ee290691f63ad61fb224198e56f410c348ff76841f3bc2b93c4454aaf080c4e4916fe9d734c952848135e1c63dcdfb26da0f8c4ad92ad2056df19184ba005d612e9ded7b8a67c6f857bca5e0aaf4947966b8f2591f40295cb429dbab62e07ff96cb9af45acc89b3b2f2f9c3a87de522b337c066f51897bebf4b9c2df5a60861ea24083d3997560705d03df070d9cabb381ff9828f94c3fc65af8a19e51104b51dd2df8f341354ea419ea1fac81251a704bd29d347b79e12e33c8a4012040f74c28da721d51c73d200b7af8331e91d183710203d07d3326106895c85abb60cfb0f7a760044d58e2f7c4492894a9db17e659b303b15b57acd5c852b1e636a0e2665a972d2878f3d1999a8e64eb9236b9e08b39a5436bf69a86304e56ee0030cad6b4a9c82e8621fff95554c6ea8d6afb9b691a644d570c34c20d69b49511309f4a62245fc6204c41b8fb2f27f3227472ae1b5096bccbf981516719159ba810254cb039305ed4b83b8e97265203a049707a76f5d69b2f836e298f3f117f0290da44bc9a371bdea377d9274d50a5638fe2f77b0709b7bc9e22a02ada43f38bf06438828e6d4e0aedc22aff795df50934bdfb3235750fe8eadd9d4a542a0ead800a8b529269e00ab39d3b208884bdd3c1bb2d491a1eefda07e6886ff21f031b10d6a1cb099b670aacefc5bf51222b7b1643ecbdce466309653ddb712bc8a65fd0bdf09c79c0e125a2ee8cc038e9b185e0c9906fb76ac4188aeb760cc87ee1f500e1213d1af889a245231aa0adaa16865d2b372c8e90e39d6691859b9bf2e57c407c73c43f7b84856d6e26266916dc7c640ffc9fec6434b300b43871497f680600603bfead9c4d1959291851a4e8adf6d7ef6309b37e54a67a129cc14de00b39308b0bcc499ab843d969442cd1db6443bd0c5945c6d36c965af3125be4c4b93f704e2d7e8767dd7551249775e00b348af8badad1e67c95434ca871da77910e95f0c6cfe063b4a339e8bf0fda3b3a1a6a05391acd34f29e94589398d351c11ef6405f9403954b7a1dcd357b9114476ecaeba72b6f902856df1dbb07168161d64b70d68fda987397d4c11e3e4acb52e6b34d701da68eb7af0bc5dca60900c12354704ab2ce39f67e6a98338a6cd7ecce590e6d40f081403ec9a9889271bb4372a5e01f700028c1237040867b6b60051a32e8729c0c4430478aa67ff8b6a93c627c40ea9d027fa2b30ee0e93b6f937e252955525c8091956bc31eb539b813cae5fbc04b93e4b8b045fc09ecf3fe79d1765c4d0160a1fffb36cb12e386d826e73807d004b3de97c96bd29829f5244bbe74f76118027ad699e2f66da9d318961e0a2490327f1d757fc9005ebc3008a4bec7946ef692ebbab986f7854fc2eb02773399106e21c6cd0af51a6da094df8b017fd943cc0c191df3b8dedbbafd0a7c3f74dbe0e4a538abaa4def3909ca7c6a5c93d37230237ab903e45eee6471c1e505fef8b0cf173108a6127e5017f75341f15d08947ea6b32ba5d91bed3861a83e6a7ed890c2ca1c4b1b48193b9b579e8ed409abd2b702d78d2ab03f5fb0c72758a28644e09f32a8099da4b6b05feb1bd7bf20720fbd5bb9a6dedeb822785dfca363f78ae0e53441b46d77d364118e5c3408f6a6675e1aef56bdb490f4dd95e91b7eccde5084bd5799f7904eebd47ea9f7500ef803a0ed0edce33921c8d64239c71115c61045b1507ab6b4c7efd8135ff7290f647a6fb6770dd44801386e542d6659ecfec0d301677a504c29389bea096f699ce3989174c4d576b1200737bf35187e6f0db0a0c16c7a6bc6b5ea500721d437e398b16bbc0818f28c47e90e2dd3d14a879ba0d14bf09dde41526f71019824eb410b74e43eb3a0e76677bc4fec5d20eedff200b91d506e90ed00adf535df746f22885715642bc425ad03894224160203d2bbb0ef70b23c6289161ad59419742f227294032a78ac7166ed7bd3fcf7faf7b2ed502074e4d17bad33bbfd31d8692238f43079d7d4fb5e92a2f74583ac0cf9be3df0e4c1c5ea6b5fb2fb41b6d0b2cfcb23fb0ccb316529ef6fb1feaa90025aa86b80069c1cc7a7a47dbd32d268f2afe7dc7135c9123a9a28ec8cb9db5b3fdfb2a820b7df382cc5b962c845ca92132d6e3effb5b7a78f12433a85e62693b2e81baa8078bf6df3ee9584ce4306668330bb7625b9f55e995640c839cff378855dc3e6605c376ba4201fe0c18777d2ff98c5f59f012d7471fdc94cb1f317e9b081e4b250eccced3c3e24f940d3c0e51f6fad44d9fbce3a3f41a8d44cea9c06a63a2c2ac03f24faf7af58de7d54f52c2db72d73a9f045aa940f8cf3e96718db99ead9ad40fc5ff99e00cc19fc20b0f4caae7f9f6b03c2ddd7c105d92157f094933c2d1b704a8a6cc52113a1b23b25f458097a0da568536995ef8e794a88ae3c88e49d92d08ad5c65ef8ec62c758a1044a8ef5d086ce9b4d7af97d69de2425ceea3c79939076ac2ae8cd9d2e178688fb1461bdfec2c4ded9b4d18509cd8d15a8eecfb8a730db1e6507ddd993c72f0e54a731fd6afa225e51c5c1483b4b0c6e9e51845cc1908b8ac8b8e4fa4f1bc62402c759b3c7b59cbd6d71baccbb1e883248f0db119640633994ace444814ab140804e9bde53c5a7d1279cf711f9c7eb3d47da09151e10d2addb78e055be6d7a1b303544a10a64ba02e2545eca5b6ff741d8bcc4db98b0de347f9a56997817eb627013a27c185ada17b4c027289263cc36af840f9adfe0d43b76ad46d668de9ec0ce7d4a1e33113786f935bc5c95e0d3d05c9f0e363b2055c416728b8e5439154becd4e8b3b4df2f418999fdefcab7ba80c3325a5c71003ddf1f3730eea84565b18cdce7fdaead6a99fc98bdfb182f90eb627b87ec28801f6c24b90d0d89ad5f90cf00da0ebe4bfd4840afc081c3aca0a661d93624e9102bdec1265399977d48cf0d3d47dbfe5ec96b0cd7c8b2fdca922390c653e49e70e73b23f64cdaeb13e7ead5e9b15d8122decea957f2aed9f71f7ae03d70c55640590ea267c661e816c465115e1a81d115d8bcbb950f7a375c530d7fb4a8c7b620cbcab1c3663e07803cea73f86ea461deab1c75b76248aea6152ee730a7d56fc0ded1735c98048cc316fb16a78f1a51a39468f4c1ec850d8c1c30fdf1eff11a207271485b81d8959e3211efdc037220bd1de4000daf8c4982a6f1c53f7e4ce230ce35d08b8492ae14f2b3cbea4e34ef8daaeec3498c6d6a5f7de06ae79e6681f079d3a1767d5c7bb49724d349729669a91e3e8fabbe565c2d2f5808a658cb0a906dd961a2806eac3e015802c52c6e7022cd2cd188bbc6640d4ea54eb08283df603d7219564eeb0e9de3aece5455c1f90474aeb37a5f325b9a4102ceed1b57dcf0e9f770a283f1545bbb5de7374ce4a751c131cafb631e371245b7bf3003f6bae06fe0d685e00bb81ed7d66ad9d73afd5b7a05d7a0782b8164e211ca69af3d89e01fb52ac96b1f71372ae54db73244c24b8f68b33f03c150a032969e11fb5fda906eaef3dae2226bcd99a085d1cd509b51d08622edb5474ace3fbbc8faa88ca3e0c5e9263a188698f82635b82eb665a354329a3fdcddc591a8a6eb737ff944cdf0ecffed76cc280bf01847bab2579d92d57627a835e1dc2c52280fbca07f849500cc3f056a6fc89df39055578335737c013350f3650811ef998617d713bbc1c1107f5b4ab46fc11a4764d111762c17f18941f8b706fee54537f82c34efed627690f4e83ab5a6b17dc0471576b2e85894793e635b2ecd2c008beb6ce0e1b1a750d08a36b81e710a24ada5990d56900937db3e471adecb06f9c78b20d8c0848f18009ad7e0000d383b3214aded17407b2920e906fb0a2a762d423a8d961b1405f790b5c549cae9be9294d3456642c225f646f35848b9ab332422248b1512eedf4060a40bc57df6fbabe3a57dde3382296f5e5c45a62da753cf4a42d623fb67283d5087cec9a68bae7a9ffcd6a2af4cac2957a4d7d895a1f3e51a0ce24c6c035a75b01e1e51436943b6171811e43d91329718041b64b81bb0f4bcf49076a4b8f8cfd007d6594bd937b6edfd0bd190baf6ab2a96679340c038ccd2f5194293a78e14d02aecbea3589f7a70296cece754f58d20d9fdd317d3b8b7455ae27e8c08df0e80afd423d75e311cb8364ab4404702b9f19d3be8c32cd61322309d929062871af0af6453c2a45dc99b3b723667358d64f205f23a4b9f60212f1464fafdf3a994e0f26efc55732992623186135bfb5ad53eb7b19c1c20ff80f1be94c0a68a2248108f16a1d9476e69b21d7b2e8e45237b4feea110347e266b5c12f904fcddd13810bc5799ee53c47cc9469fcc71d7c670c281dfdb37da2f3b590732fc8c1919697045d1a8f76003d5a7f1a0f519ed8a862cc4bf267a731151166089d74d1e033f00afece9ad7dccc774198a8dbafde6464f77498cd25f073dce5c6e302cb4cf23101590ff03ce9d837c1d853619f3c272e3f50868c7e4c7a756c251cd9c2a5d116071ca2012660b7498487d17fa2c79b98edf53b7fa77797271d76e057f050199909f343d5e9ffd52d98df35b722ae7a13c8b4f6c9c5e5371d0a988cfe7fa77a250cd7065407e6e7041668483bfc910af18c948543abeb6958928ab32ea024ab050fe0ac4f8e00b26e065dbd9bfad0f6bcaa43308f59b8e9ac87096073c5ddbdd90d14713af4b470054ee3bdd95cad78e22adb63f0bf7a4775ba69693ec23bbb7c05eb3dcce7e3ebdff42d0e828500bc6276a7ef75e61c992e7f573bc13004873907e7c620856a6d4d44aaf0e61520fbc2c59573cc01b7c5eb138d92b40da701980c1561351508a9a6f1a0289c2c2d547d63f294d01a4b4578dfea8b9649aa87b804998462bc5ab2d5313079f42f91ddb31f6eda7bcbbfffb024c04b25841b88780275fa3b145e089c079f41d6344fb73af2818316427e864438cf2b899b9a0c60062df1583e756a669d0dfa5f54f530ec2d6bb1777e4e90fe3c845794ff8c463b02cdf42992f50f6304df2bd9036fcb61b32abfd768033867ac46bfc08a90bd3f08c85b6dc30b63687948fcd4fe63391245ae8a83ff33d01a23c21d654f55d18a0256f2b28cc791159ed99f06869bd556621505805be8f151d00e6f0d89d970f50eb0b0196e9636d186eb89c989f5f2e3195d284a9961a5c35c194f87734bd9820d4a55d011d013948e7923b11d22f762459970cd62fd289ab3edc1f631a546da0675c7f877d8fde2341b3374c9947aac2bbf72a2b6dd59668ee1995b5190973d0583a51f60b09bd2f2668b47161aace20555905fb5f178d23881c688945686cc0056ebea714cfd75acf7d27038a622f8b8f319ec60bb44cdbc0a4e8e0e3b47560948a8d906f379380f1d4399c306bb0abf6473e368250307243c3fb62d6fbba50bf316c2981e3936b5518d4956ee8a460e33dcb4c40578a054ce0cf2c32a708c07edf12e999a005030e675a4a0d40760bea4f0dcc7e2cd65eaf582df22f521c50fda5e9236cd8f7d671ebb5edba063b5c6332e2284123c4ecf67760357be1af1079fb440483d44351b98c6ea0b3b72f44ad7618b252051e18ec27453e2994c4a05c9bcbc498113f6356c434f9f16a4f53c19993cfaa0a66bc2f2289abbe015500d27224389f9418db7a6dd306ebdb3fe1074ed221be9360104d2a3b5d56072440893830f08ae9942cba1f36cea473ca8265e87e3bd34de605e8344b6f92bcf970746ee8b5dd837f1ee0eacfc99f9811fe8ec8a2d7fb77852051148bf711dc6020c101d7ac16d28afa399868679d227eda9c93e5227f12309b17fd37e399ea743021b7ab1b6d2ee4f7fd4ad3af7dd2725fa6f4320c1464f428ee30abfbd8ec75c00ae16760df49b4d71f966be497734cde62bb7fe5bbba8507986b10bbc05a74304fdfc3e522287f38806308a06d86203aa211db6c1fd5696ff87ed7c3b759e86059eec15dbf66326e00a5aba1d140d17dd3a1f97e2dcaa5036d26c977d68aecb08d35b6aa339bc09d9e46625d1e7047649c3170a557b7751ce32b77ee74ea0d4050f1189a1de30dcbc65530960ba70d46c16e9ff73277786d8d2945dd193808b0fff56c591cb9f6462a6419318037aaa0bc356890f1f4042f7a1743bed497db100755dd5a649430e5799f055baf7eaff72019ebb3eb10d955c56fc6324a8278105b6a0ced3c827ff6bdac8693bb5d51926075053e957f5167850b7af7105f0a80fa69e67bb13f411d66f3f0ad07ddde933db701a1990f75b1fb5fbf1f93e08e606d369c896b30d39166544ae7e19c5b0a958de00cc2f403d7d4a722d2813f60a083dbed9cf6433c5338d10bde8d56699f89e4b75e255872e0b852de4a0d758ab0846301f7f3ef92152c9dfecaaab1e5b253ba9ce3beea6a333cedf9d6afbce19030ae3377565ea9293ad5885a62fcc86bd9ad24130f7cc2a21627f701b64d4fb0dbbc9a1917a2bdd7e386df85d054ea4b9fe82a71e52b7e9526b9c75a4f51e80070ebd5b8e999d6f1b11221f60971d1e8793d15fe9ab02823393a413018e36350fd4664808044c50c0c0bdfc87137bdd6b6afd6fe5d6527bd6308d31aec39a1409b7327171cf05654b9d80ece30e546d605e8a5092f1bcf7a6875ff29ed7f71a0126a767b8996965c873d09043b4fd2a26023981b8fc05a91ee91ca9bb790d6907415e9abffaa10eece461b2e4bc806ef718e6584f81053d4633cb53acfa1512068e4c07207ed936ed87679fee574a64492e938dfd738813ef4ce02c01b0ea77087587bd2fe291dd0adc371abb2adeacd97de12856fdf3e4033a12bcae71388c0341b82dd9544ba729bea1c0f3e9908536c00ca6e08babbe1a82330eeeafa8d106f8519dc2d530f15e2c8b8c7d20c705c428423c560e036040fbf15ac7636ecf0039b4ff7bc87a3a459e1ec7367f31ade3b6e7cc9f3dfe8405a8758d2e631e9202a33e79ae93762cdbe2458636ffe68c17633d3b49192d062f26daa713e179d90a4eae783c5ad5268d734d65278b26653d564eeeb580ea2123c1789b0752e57d07a3a71d2ef9b0ca3a29c962537b086ae41936be90768a1398aa4c7732cd15e208536fc831c214d9863f224f4c4e46badae6766ec704bf54e4aef06a720900ab045c0628a1d3dfef4d6a60e442a1db1dbaadaf0440b7833d670a515df088949309ca6e9b7f92b8f7bd0af0e2ddcd682ea421c5cf7f93df7fcb750a8747a3ee0c09f46ec265095cb46305364d9357a2d3b1bb10993520b9c54b78649f6e65c8d90c5e700cb96ec53b09cd057cd4265be224025d4a5bde024144e0f47ee09e2af7079d6445e4a156ef9819ec938dde87990854fb2d66c7bbbe7c32f8ec316d0a6d0b945b43608a0bed2b8f792801f40b24b9126df545aab60b1c0b7cf63ed885ec0743995ff7bdb55a930d8896352084cef8b3b94313db1bbe966de158464ffe9905bc08f9b7bd261f437bd476ea4d4672847de617d7c1910f525562b1b17f13360496144ee3e8c93bce388cb9e823b98840994692ed5d249e5533a53a581888f20813f98c7bc7ebd3d35e9b481eea89106ed53e1c0957f6a6598ba7f1efc6917c092647d11360121667ea91f0839749ab84f346a7f35c3cfc06abaf7186ddf5fd0a3b75cd676d0dcd6f2f56a1ca7a495b625b72307443dd26c065eb41468e703a07aad60f535e2e7f060ad8a101788fa3a9d0c1fd19d3472cae3a7e50013354d407ec00b9168e6dd72a87ec7c241e8d8b4a87b3c5d5630a684eb5e2c6fcd928bc01f0c195550db9795091c85bed3c8d12aaad12d3477a9b157afab9be4fae8d40033f55dd084cc234609d3cf469aeeece5d1eec7cced0c4f27944b2d58a8b9cde0135925319d6ebcb54325a34def61354e8c17b03e1c28f7ad2f3fb4a83c0f7dc034fd27c1125cfa56c1c9e334750c2162221e2db0840512dcb4e089b768ae2fe060993029aede6a16eac47326a5c1daa83f3578b58e52fc36ffe0d6034c6124b0bb59fdad3bf63b3f731d0a17344e9a3a9301b983639e8626971dfb2700522e404454ef5e0a40a579fed42c423bcbdf43c08ecb4ddf0bb647986640eb7f6e57e0dd2a02b03521983621cce0be340bcdeb03db94adc99e9985534276f4379fd04026260bed58930f997a461f24c131d5e689c4ed4213e28af4c28afab4e0f672905470b7e5d4201ff7c393fb3df17d16a6a744abb39e334522f283d401869fd8504dc00937235c5ced65fc1722a3ee8e6518ea38ed597ca8507eefe6a420a5fd408657fef85d221e008b156af68d2070e40c8a447afa34c90edd9daf5f9b032490e1aa549cb38764022680fde63bb08f49c7ccf42513349ad93141543eb8723c4057492079215faa1e457e41cc85d004fdb4b749dbfba20aa639277ec5ef44c360f98793539d94f2efff4ff9379d0d53fc891fda3df5a15aeb1265c77c916e60705d5f2fe58ab789cc981036da296b0dc311cc30ecf81c63479a68ca5eff5c6f201bc7e5b7e533f53512c6269a99f0a01cf95eb0276eb6f2b962fc8bcae02b38f04b23b890dc9e0cfaf7e47d39dd6ada779f5fe59230957b6020b755fbe3efbad0ee1a9cb4f583bf144619ba1de7b8f6f02b83ec26f00847454a5e73660cdfbf2048b5dea88a03869d90bd2596cffb17a074871e553e0e67078fb21636e0e13e9019e7a3c3239ac2e3e2137193d002c3fc4a593449e6f8a6ea25f05aa169bac5d0ed0babf744ab79d3dbf1475934fa02ded36b7b615f924d90388bedc13a8ebb201076da73121f10a3f12e13b41284a95469335592463f6712ec909f1a568a1fd045b2a46664fd42a8686cc65b674395e3c6104f8f072d6f08fbdec560cc0ccad0c27a6826bc5546059c982abd75c4764d1c5fa7d90a8ece40521564dd07f257e02554a8ec68376fd1646056b2887359c1001b621f0235fdcebcd58932f069a12012bacd07c0aa0b78da0fa952b9551e5e5223e542780dda3885a527d82a46399075ef0697fb6087b8b0530de6e85b87c430080ee788d965932028620aa95a7100392978c3880ae3412c57fd6bfc7f21821becfc968b4274398d4c08b95b1f9c909d3e63647833eaac1bdb43ab9598716aed4622477718aea434e56589038f7f20e4e65db948a69e60eb24ca706a932ab39aedec10ce00d7652c23cc7578f41d200c9082fec6d13f3d74b88cad2866cdcb8ceaf29d124516923bdd7ca930c2b110799a8601a0c066760d721bff206f60d317a9c4aba466021ab2a213c4e70a5f80bf2f77b6cc0bca573ca1d8b52f40e72df439339cc346008dd26934ac10ca05602386a1e2c5905a97f5f76671cc23bb6e6045f757998c424b6a1ce77ba0f04ec03cde885e541be8aa2f0bc40d1c067d9857e15ccb3a7f4c8352b33e52fd7edc907a84568508239b5dad1d95d23b8927605345946bb7a295a6cd84152cfec6ece056aa39f2e95dafc34cc7b550277db171d5131635b2285e24720478477189d030a16f939a1fb74c7597865734a88b0b749365506ab1d7a078f8ce2dc946ddd310fb5767372faebac4d4be7118bdb91fa58378a63eb703285118d2675aaae777c0e236ececdf98d69d79a414f99a98853f94a4ab5e13223cb1cfb519b3c127ee307c435745533cb3d016f9908f48bd6340c994a3626da9fa2bfcda0968d526ab60887446cc871a01451bbf7dcdc4c91f3d378a0a29d5e8f42bee345f83e5e6643046fe23634b2ff359783a20d94df69ab3a7c7d9b89719c59de941e9fb229e6e80cd71cc4c593792ce04fb9266ceacb39d758b06daffbbc42e4a247d75a0db17b01da1e94a2a1db829d58caab5d25c9c0f458c93df54314c7d7401a25e708d21c07aae4c196f17ce2ed2c9ed8fee96fb6c9b1240afcf52ff1666813dc06ca35b000715845bb324b326dd5163d08c23a028a790f444a79e51a4062df5b395ba339093e1449fc3afc61a5cafe3eba4bb86502cd0cc9232920319bd6b1a24de670a807f6d2969409ccd8adde436d72d92462849eaaaa04fc0e24880060d63bfb96c90a672ddc3af84ac3fdb64a21f908ab54dfb7ff5d0148ab8c8617d331130274840b5bb9ca4ddda77074f85060cccadedb0e8e424ab044bd34c9043d1c29cc904e0a40a07c7caff75383ad205f0c47a1b900b665f22cebdb653f506fac082fc606080db48a99756ec03acee8a9ba52f06fc4598a94e4cdda110562cd614d1a53fb0c9aa5a1939d3734fffb2b04dd91ff7212ae07473d4a8024f52f7f9d6c4de03b0e5239012fe1a1ae1c8fb53718065db6de23a8f0e3673897c5ccba578575528a0ad26be05b95b4b5a703841df12a1156a39e637fdb2204f4a6f0b8d940476f660f02b621bd71520e66c582b6c93a37c24fbf1e4076cbcc682fe2f122a12ac9ba0fd7560ea6b1c8d83a032e889a6a4a900435c1b9c6f7979ddbcb594da8b928b20767731943e701dcdc050c02fa46526165b82e33b37482355a3fe934996afc2d03830a73f490cbbe876a1db7f2b19216293f240f60654c50c94d3cd57e5b5668026919c7d8b3730af368b999e27b58a8cf2eaec6cb80116eed284952a288aee50d760f6f3b4ecfc82784961852fa301f8e71a668b2c5a9198cc302ca6599b0640cb07a49c71d3fa0ddb6997ec31ac9cefe4082433acc71c928d7f461123c4dd30d73a6feae954222c711c269af99ae40f048e6f6b1cb92db3ef82152028bec1b0e9618b5e0b0e107865e0012e3dab4676177e1f1c9a5f5e67238e6b77c9be2b60c5fd06ff0a2d07d388de31772ad047fe42ebeaa4a6d9e2814462a8627c4cc7e0458d4db2d36f47ae440dcebeb6b5dedeffb1eee50b97602e06f14c2cecc56570c3ebbe1256a85b1fa9682cafb31745cdab5278a1bf0c41cea863ee158e166bc004f39c387c0fd9a4716db7780979259d2db8458000df4db26c84d0784b970fa0cf5f7532bef0bb545665ee021172362e7c5a68e4abdc9e0376b323953b31daa0d0b0fc9ec34a2d31fc267fd3eda7fa743f15567ff331a22baf37acac1a1095604ea5a898cab0cf8dba8c20bfd8561827e8e4589e746b53dca0795e2c7c172a1005e4e552d34afbe881e9e0769735fdafe0d237b499b1b417313eb9df1fa13950e369238dec465e8a261ee192f7fecf80351b1567f5d8e5b5b9409127b9ffc18033e1551f94bd2c3d6c2b58943b0a335e78302dd72023ad77a4dd924ad5c55bf03b4036c03ffc45a5c76c4b6db45f2455a7f862e34638613fb85d024013225ea0df500f16cfe58e7948968079b5fcb588e4d9fe8a09afec7bb70294421d0b16206cd4f32eefd342a3d1742451fb286c629b4123c400b36add8914f3a3b0b17cb04307a6927d6731fe23432697368b166a9500697f50fe408ef56cc5fccd94a740a42b252edc5f4a8bea37eca9624bb286129aad3b3ba8ddfbc7f20dc1dcd0a430319f7ca59e8d4059d1d1d70284bb3c53a7dec8e2f3dc6f58a1f1e95ceb21ff90bdc35f9603b44baa41f6d137152bde5d994512882bdc71a80590ed2e3742a2c058022c0300523497f0941efb90d29a7d131d9bf4e75eb1cb7ca1960ee565768075172fa1ac29f3dc07a322752b8b5ea95e6ace26d18ffbfe4dc819018144c8c0dc49323427ffb782769c40a757f1f6253e40b15a4828b242032c7c877227bf0087e753e620da8dc3db98f83a1c76691ee92ec7785110bd86ea27f0df26da06f0f2c862d403ee510de00d36cf6c402b55474fa4e692a061dcd552f74e7c1277d05174e3243ecc50a10ebbaa57704f28effd9f80ff94a4119f2ac8ff5b1e0f9df0381a4598ad2d3e3d0a017e01c7c6b45eff04546ed910027836e316c92541ed104d4f47faf3d3599dc33527e144e0f5c123b1d3fc565c488166f4d0de870f9530b54c4836a0ebe17db565526d226e44d4bb53d77df98bbba7cf8e672fb35ab6d0c1b797983dbead52feae83476ac04b62d1414939bfa03061550d6843b6d0a4d04ad2e73d359891b09a6a7ccaf99198b572808a7ef52df9601839a1fa6f1a70d04681311c2441e0fa321d5b4aa34548a9e1012254c434818cf076fb2ee5e03f50d086ae081b87f1deb29fcaa1c08c791fb24f7bd8223d211abf914aa04a325da00911ebec8f4d5ce3d127e733c6143817d814810689d68ed1cdd24eaa0eb3ab407d1b7e80f7af2a88489a65e435e08cfef4d6058fcc75f939a834fe3260c8b3d0594e7a76381e08be446ca51a6ede565c4ad05a2699d01cc6c7f393d8a846d850c20f44e5d6549f89df33e19ad41a1afa9dc7d59f776707e5f2b9364ea15d4ae098c57448f7d38a5e063778c2c7ee96c22a1fe0f4d6c9d56b62deffccc3dc5360ef84c8e04edf660cc59621d029c355e3b0aef571dff402eee7ed4cecfb5ab2308c5ac8c492ba2a355283a9d6661a657c496d97835c8d14da3286d04b2bc874f01ee2e9137f43fe3439f34bf811e0953783cb371be6a40fca756c18413afa6a70af73758dbc3f2bcbd93ee32fcd504fb13b2dbbb2f10519438a7fb7284edd4fd0daa36e280d6a3c9e9d3e5042e99d73d921b902f2a6c863b065f40d4552c47ee0cbc9ca97b741bfc8e2fac7a21707cc801e68e41609754879e776aa72bf6831f0ad2b8adb9d7c1d6887a34f38ec0617155001d1e911b943324d309bcf562e36309ca2a29605219369a382eeaf2a14baf763bf9d0e46ed1da7624787659fcb1d50e8c14a9e67dd40bc9ec9e23f0bc4847e8573317a3bb8fd1a052d6b2e2d3598d0ac2c6b0732524d496d6d093f36493c6a82d728dff3b164deb5de2577c11f07e053e323b7e6cd815c13cd07469bca408f02318ac8d5e540a6cd96be9a2e4498e0447980bd9f9dd5aa803dd03ac334028b0eb4396d750c5ce461dbc15f9fa2a2b06e9f5710384dec1ff5854f2abfa8181fb5a676b70526d72a666bcc2db617fec0f695ea529fc0663362c471ccfca81a9e829546fea5c035f7e8dc412e8344ec90484e175d0986347f0c02e8b9e3cad695a985fa31ed708682a8b834bbbd427ef04450fe37337fa55084487d0cc9c459b04fd14508991410ada48c546e0518c0801dbfc8ebf6396eeb8d66f7b55251c009d7474cba53d2a72a1fb9981a6e5ad5a09620ae7c1d71b4b26a95989359a2bdb7a59b1c97d9bd4309d374acedd522f090cd356d695b800728b331cd32c37d9d6a709f34ae55a8c6fc7b97b7c506618b0062a4c349504beafb6cd3359ae2b27f8256c6597524e9cf287d9344ef9b790c804af530d75bca79d1965127ddcd1bb79f13077c0f37790fbe18122924817ad860ce1dff09feb06d942dac26d020a775f95fdd4726c762bcbb0d8c399216ce9190469540f82f88ea7cca298b721ccc3f0f5578aef36e9b4c6473fea33ca06e875012f2b7d40f2c48feb2e42009e8841f451db2a5f23ad6285ed2bac2792d77e540b58c337ded280fa8d728401a0d84c4d3186676c8f328a89fcb18c2086885b8705473dfabc0ac481a4bceb4c144187e1f3f2a3fcfd6840aaa08b389696fa4f5b0e0bf26019b574c5032994992e097c204fe2c1378b9f3b4c52e65a076b90dab6027f2f2ba47f96ddf5dea8f10b831a9f4ff092b020ba469178a7852caafa341d0a67004b91e4a92e5a18b9d0c962ed6893211ee012be6ce3a3c704ad4a70cb950d71c3481ebabb4125e224c612e4d6550c8ab47a7f34e1f10e5e27d8d6705bf30f41289c66ee07f12fb35c22161cb8d216e52c87bcbef8686038f31886e4e5d50627f78267978183f3aa5e315bd5e0642666cd56a1e759d1ca884d205c0083ff07beed3d67b4ee1c17ba52ca73d0ea56b967754f223b9ede1f3c57f439e41d930e04de6e29216be06691aef455607b2c17dd7537be9baec2529dedf1586f5e90039c8788fd3fc9649a081ec8dc583aaf7b1ae5146935eaa0b4c897be88ea5ded017a51fe4f4c3f7c2f8421108e0dd2a610a9a85ded2c4a0ac2894a6c7aafb4bd042baeb387495fb151484f6ed0200a2a5941dd7b2beb0140beca2592b75cfc7a0a69922de9e6f267bc6a69e05a4b00ac20bcbb6e6c986bd32dab570eb2a3051009c708b6dbff220c6e0e6e42c1abd42084ec51c4670ecb8e501d9f1e8e73a7380f973586555cf862845778794cb465481472141ca3fb45770a97d079e0d27fe10fd20500fe4c2386cc425bca4fd5a51202c38943b3be428753ba5daefbcd6a7e0580c20f4913eb9fad486cee11637ec662c254cf7d3f4e0ab008bb8bab1f59e306d2b417d3548975f449eeeb0e73038da0c7a051e24ccf268a3a646db8e734d80e6dc63ef3da73f8504976e10de048f599be95026ac79b1508b2c85b8a57875a0d8085190d2c0bd903dc7d3d8000ed22d33fdff4cc0b50741ab1078cf6a15e4a094a006cac29f11e33d799601103a19eb1517427793e90e5fe1a6c21a3758c120530ab91beb030ec86aae1438a1444d1353ce07c6d2af53609b186973c5cf05902acab15c573ae94611b0c4970f1ba03567a8bdee75e8ee200096dcf3bce228503c7b826a5685d4c50e06b6693683d9d3ba36ab89989accdb020793f9404598909167e6f5b83e0cfb4547a3dcc7dc81c0a76716530a2ab6ec2cf6802cd09425303a03dade9f4810517b471d4f086132200b5445c590b241bba071afec7f86e0f006b29329e7e64905ab8bb32497d8d929e45ac441079d65a76971d5593dedb8004eb42eed62d4fcaff967dadeb4a53bc07d518d487aa04a367396c51861561090e4bdec5bafe793d4aa39ffe2fbc853552589f3f80acecccaa175cdd82940e450596cbdc37c5b1c3598af49da2e509f88657aae137b86153f5d296383917f39800bbd46d03c138269f3f45d3559ce8cce7671a0a4104fbc7c1b1ca1c1501a7ce06e37a89865aaac6ecba6211b2fc80841f0ec430cd9df75df232f65d59d853200473f878957243105f545b1bb91afec1bea9dcf0c84fb7e117582ea2271222520940600d19da80f5d36ed73970cbbef20fa99c2c8fd9552dc6db8b864df118840903d310a43c54c946776d6b822deacb72c3836ff77c14a198c091aa6c2e6fbe09a8e2286691a9128ef32786793f4ceb75cb5378aaf74c92faf7d1452ed95b9b0b4e9c19c1e2196426f4ef36add5586ed3d65fb7a7708ca8fd49465a769bb7e609ed86d9020b4d4246cb53c84b63f93f77d5eca34ead1666dc19e621c5543e88004842bc12327618386000a00967376ddd74ae0e5a6dce99da58221204e8e81108df55619be27c6a64f9c82f97f4b4004df09ec82c3fa4ba43a91f1770d2a9ac0c89e5a50295355ee8f53edb968f8729d4267c7c0fa0a1c7bfc929acde8f861c0d3d3d1265f66d0594738f7e1828bcb560bb232b539d3740e7dae5e32f024443095c0d1749704aff2324c39c4b09c7f3ed41b333ef027e60a69cf23e82c82a6d0ec4045d7c0a4da39e3e1fa88b1365677be76f3dfa2efaf137c4c541d5f00a8d004281b3b8b3ec3b41112165032fa59de3c026e67b0a8bf5ff2c9f44356c54620323da07f40a5020981133c933ffc6cb61bf3700e2e8b59edcb79ef28545ed8703fac42e6ea66625aa8e0b82eb39c974816818e7b0ad12b2647da24aa98ddd240a7f6b7322379951113a9341fbfaff02716fe2f08e50304aae89388aeff7d44106448e0820013de42853b9553577347bf570af530e26795971b1aee9a75de92c0a310af83a427ef41a55ef224a49d600dba2ccbf30095dd7f56149a3dc142de20c9d91dcb36b467b6ae0b1b12c5b47831307a7ddcc2967f5573b589c082497e709ba99cda53215192fdd626a09c1390e83e7215c0d3b467aec4d4e2b30e5874809cf27af46cb2708e9c81e0e5c18d41a5afe263985aa658ce7d285cbe7845ecf02d110edaa9a26bada55c9c10a5ef7bc00a66d1fef26df48961faef442566faa0f29929d3c2a2feb9d75b1853f63c8aafcd12148a674b0066d6f26c6f167770d0f6916d0480770ed37fa4d6ec3ef29ce67bed101124bab55b824686f7a5a963f06ef285dc6831330f62f45eb895896f15c3009f85efd45e06babc216e52962390b80eaa9f8b9d0005716305beea0852185c507b09d391090fb75a502a877981e00811f18d1ea1c75262a4f538345adf2b98b2617dcfc96eeb09d0c8c6d627c220140247df13d122376dfe1b764d15d6faf70dfc7bd2a124aae7629308e576bb00817c69ac56cc8c835435efd1d83df31d2d71e6c1884cb1e8b1de6459b3c83a306a649ebf25fcca2f0df87c69eca7ac637d1c4747b13c8c4824a8b32129917f30a7f41cc4c91a43902ef40f8c6d1d7d9d6f175e5a70991c2968d1b62f31493a8001b697112f68956049c45fa5a56b01360ad1ab8ff8b446c77fe30fa7b3b643b08dc0790f8f8b87f79c5c1a3d9752c1ac373c93d24edc2523455629f22c8d6c20a52746944d3806f1f817d75e9f78f113c682426155b8a5efac535d4702ba35a09bec613ceb8b2cfa2f838701e22254d7a26cb144b5b697b3e2a740f238fdf450dccdaaebf4568d74349ff1e5e8c9d3a902d54f6937feb0b6ef8a13360a43fd9083abc2cfeccb41f575fe2a921dfa6f9a4110e7c37f1a1e78aca2f70602bbf9e0f4c5d82f67040c22f736cb0715559821b5f1587e664ffb12a1a8fe8b4a4c1c50b5aba150696176d6db6005c6287a1873d16d0d3db7b9d272d6da1adda52da3e0b49e01203b84fc55375730cfa97e3d24ea648442482a3da4d6629dda8b9819e02ebb4a54385958db14a9c5de6636d06b13af64026912dc6bfd9b339c2b0c6e900ebadad69a2e0e312bfd422c36277e065ff78c4b55b1ee67ec903926c8df117002cbe83c440ab86a67c6ae8f1b15ecda6562daf53bc2bf22bae1308da10ddb4018c67f590471f05a10159410b47657618fc3e70917a9379c84b1aa1f96494e708e00fdd82fb759a83b4e741171ecb6a698b69779a17926a34ebb25a04dc7f48082b8cb38b75418689edc8c2de92630ff6227186edecd1f9f363407ecbfa84f40752224f589689e896ddde22cc8efbd9b5a5fde3387f7eb9f569d5e3234181e4062e57845ff75b0d095abf284e5038d14ddc0bc8ebba5637ea7a6068c99dd5e105e781c7cad2ff62c2a06c866509f773865328d8cc85a86221693c4a3df3698d0828411f79573139717be4a80f2cb7e4ded5a68e3a95eca8963552fc8425f696068106db04216602e3f197a14998db49266fd0087d0761b1efe35279d39a9ce0002e6f2807dc15ffd8e5bae2e38ab7fe08cac64a7698f849ec44d39b7829cbcc0abaf33881e0417f41fee7faecab25ff830fbf4355c2c125f3c2c0654dece8fe0a9fdbdc49bc8df5c8086fd7f6033e4f45a2ee6ddcf1417a6493229d666f324d03eef5dd9de38acee97a42a32000dc89586f5c8acd42519d27da5d6d41f355500cbdc78451ee62c158b09dc2b901c92785ed550ea7dee29b04fde0d5ec993bf00f4b1deecbe41987ff73b0320efc8488e8627ad075872eed9625ed128a3810520e5f90bad449f005dc829ccb99e00517620295bef693658ad2c792398ffb54360832564d65ad58b252dc06461e27ab42f3b94a6a60c63246ccddea4774b16313013314413406c79ae195ab6f421694b95f9dce505d18d2a1f8049e9c50212b2d058f09877ee1dc51f439d68e6aba3a9f53bb450c0c13a3dba5dcedfa7abb04ae00eb81a33070e893f6a3f66a9b01d4dfcc9384b5a58afc607e2f355a32b318e40b78f5c44671c1d834ebb7010a3716fd99eb74c612f34ca5049e9e43328af9060c6fc468e1d0393b55f0ece74e85a57a9ac59a36ea58a9d24de69a6b0fc28c9e0dca02c44ce19e886cd19b33c3efad603b7a7bc527e0c7be1c2cd823d6e3c815069144b0854bd80f27fac1d308688b5340da080016139de9d0b43f263ba559cf08ab63ea84a85cc3fe52f91b04fcfb728c848888bf3878e5ed730226e370ea1a0c6cbda91b0c6fab36c4d95148c335f0d303a8b7ac42b0a20f80aceca2481ff50b68e1abecc9db05f21fb9fba04a47ebe94f78c51049fd9619db5998047525190a8e989f7b4ab4d003f0c6f6dc9107a1b03882b287c49259b0ec38998aef62830176d6524ae19c8e1a19d19c787a1305d2145831ba455a813b7e29692ff293170b918c8503d216b7f88f05cfbd7ef20bd78a90a1e353cd3bf78fa0eb66869fa8076e14e5084dc6bf07808f7eebf07ec1ba84bb965db016624411f415b71137f80192f28764d765f748746652f139a328deb75a7fb08b39512137f888eee5c70009a6129f5689b787054d399df0238a18429d60483304ecc85984f09b124de1e10d6cc0cb0e551109c6356d89914cfc84785a6d951fb094212973d76614b85fab0216f7ad1cf97673b032d92d9a5362326b790f5c45a0f9b5df9808385e1dd08502da953710f6c7e31ff5170333cf40d30022e04d68e917c459a9329c2c8a53c1071308733466e8ff9cf631d973c14838dd364a1e0fe77d6277351823d2e5b0ce047c6a1ce56ec2d126d47454ec6533ad0ea2b121793234ddf99595c80777144c07ade44f3d81b2e26634e69da5b29e52fd7863089d949ba5321b5faec4ab3a33097e440fdec037f97a6000acc5372b09011829a16dadf9eb7dbe5047a5ab28610c9a04267e2e622f742dcf884867316f4135335198e67a4ec034437b8e98e6c107cf0a537162d7a2f0249076be0c326f6a4b9c7c0bc050974287efcd3213509f0d2743d2399bb3e987ca46593e90513faae5c032bdfb5219ca96919bcd57f3270524c74afadb045f3a36e0cd1cf68e3bd68d0efdafec8b0d56f11afdfb04cac704c7365a596c169fb44ed7d1fe690c7150c82aea164557d9bb05ee53ad3cc14107e18afcd2f153733f296f977ba883111db4bbe58b6724346cad4b7f720e191f06cc98cbf3a023206972f9f821579d997a16ce8dd7a77e04324aa8fa8fa65f6e0c8cdbd1c34f64ab225f2c1ce17615cb049c452882694934bb23a30dc722bb0903715370dcf2cef4cde7db004276474ae36bd6d244597c0adc6c794b3e1ed707000fa82bbc98558714f01c9ca28c855e1a3ac2ccbdcc090287455383a74022020d1be04a2b5f7abe2850295f49e7c180ac8e99ac4d7f8626b997bbeaf2db4b7c089f445b634f4e51d96b9cfc37fc38fe84dbc2fb24c8372cfe04fdd54f03d45c0cb39d79222eef1e5706d32eae767b0a68d5915049a3c583e16243e0a5272a7002fa9759cb6bba23e2c2c041a87bebc1221dc3474d609803c213aec7866676ac0cd3b0bb9f4e360c5d0e9670e5e3ef6ebbe73fc5b9e82b28c0b1b53fc194d0940c08868b1150303566229a9192e32bb8bca9d3ff8e0d240d0745895f7e9a2d2401e2d70008ade16084f13278a6b22ad92af563fd9c709af36fb9cd8e7ebcb3110f7b2762d32a2c1e1d662486351ac820b32adcc80523db3405fe448843dfd59a0afeea65c2ef6271e374827ada480f037062b7a0316a1dc8952f6fc133199c280544a125457f9a001a45fa4760c763756fb9ba0acbbe777b429348d834f5866d0b367f5de43deabf8aca9682a161d204ee115daa3e22d1dc19c8a981b4eee804010b79937bef1f78f3cc581ffd9991f87b3349dcdc69c1d68bc673a6464e39710935b3eac50aa3b85fe3cb6ec793189cc617cd30eeb5cb57429281e69961121d09fcd189c24fdac0e7b9437a43a68c76a4f7529d86a872fc8a74b1b666044bba0deedbfacbbdc6811a305a8e985ffbcc4f75ef1aca4642b624aaddab2bca1be107a2c0717a976c1408a4a5967d14f369af318ef0e1e14e2056eb12324ef207310c863bd9421654e802e4a5f90f8899776a984de4dc88c43cd69cc4163fac367c0a3b3a1eda93a52457409e7c9898c3d63cdc7fc5684dfc68ae8b75f11c654cd60bd595e06bc45f6975304f88264cb6ea76fb05b4529c246feaaa28777dcbac6601f27af7b803bc7d2f66888772f4eda421d26979fe2c69304916c26e226a01cf080275ea176e0bd2df44e1bdcec64bc63141066b87d055ea85b5344c08c314d50b11359d2c67d80aceef4b984d08234a4b881895109df15412f1ebf5d4d4355d058352f701c11756a476fe8559f6b6b30307fa96ca39f8418bd8268550a1321108897aed8ab31fae2c6e1aee769577323e33bfddb05e4a066fc704c6fab6f3680b2ee0891f61e1d827659df21ac609340e17ebb73a2f7f270e54cc65a84fb8ef0de481f11a136fa80c18b7373add3037a737767c8cb30f314d9eded61b35813f098ac2c7ce53216a76c6ad0ac9c20c5c2f9a9aae534986f6efb26df8153f4ff102ba0ed11b06bef078241b195412f44bcee5633432ebfd0dfc8791c51dc16cc002553b32be3f0879e82607c58b38517c4dfb8acc6dbed3f63bb0805f7854f6200a63c90bfb0e86d61ea69130358a6a3d24e0cd991d448dab0a8e24f2cde55ac801765b7433b15b5cdc2e351861db5d4a01a31a8c76bc9177e01871e7fe832d540f82b1617ccdec3a44f6b2d610655b10c04063cc243af1ce1993e3545d82d02b0a9ee0795ba84d22eea67d6113cfc3e756fa80fb4dbbafebe7ad261b0c7d8c3104 +generate_ring_signature ff48702510ab57e5c8d1b6e81acafb78f7012e10e03ab15b0f1827ef34042dd0 82eddeb24599f7945a021098f99c7a1cb6edfeaa937e824e2f529708dc25ea77 1 c2e3e864568b78db05944261bb131881cd05c85c69033962d3c4f7e2a25a98f9 bcde8c2f9402b56df766a132e18cea4bd2d2c70cb537b599c2e829b7e3af4700 0 c571742a37be6de025f5ef62548fd4eab4bacf2c20aed554fdd33d105a5f4909084bd42a13e701c9187e2981482f72d75acae930d237f7f56afc63826748a30d +generate_ring_signature 417b4e63adf44a5a461ff2ff030a1757fd342e7f93ebd920c25b4c8e6d740187 bf738513972b08c9078a1b21cd261df99f52670326ec59dc94279e626c0ce9b2 2 f204e5533b350d6232e62308092afee62c4ec0f4fce45938a2bc17ec3bcea3a6 50ccf363fd401389de4f584b3597c3893faa78316cb8799ec6c20a37596677c4 a1bffbc16adc7bbed80ece0ae2b4978a0b3325257a96a7e3c3c097584d096e00 1 0059dcce443421cbb13458781836a8ab1d64451c83e987d7393abc34c198fd0a79af78da553af13701594af9f7d49d9908487cd2ab7326f2d267951293be9f04d0b0649f4c357a17c6b74eeac81b0c2925f513fece6769b735d5e1fc91115c06bbfaf5032d0055dedf834e13f9107658a05e273237320f3236e0b0237943ea09 +generate_ring_signature 030be7e561c24a2c9a75cf63dfe5bbb49addd7023362f7688d24ad9369967bea 158ff2e128941383275c97ac9c0206450dd2f16dbad1a0f30b045cbdbb76c2c9 5 205a0dc801db4e3d217a9a9e91bad16ab78ef983f799b8364ff9ab44add8ad6c 0a6f06fed33891ad9f778c3689968dfb1283af71b48c1690dc941ab115ef4d69 a19641d3c2804dd2450d313d1ab0ea95b16c4cc1cbdfebec643ba05604625dbe 93bbc7d65e865f094d1f7e7cb5b07f734016648b7d3e104db00b0d8899d19e8f 68e6e3257e7e974b4ceb1f7c4461950e60c1e5d1d8e935e02f4116bf89f5d842 0e0e85834add085696b849ea3ac806740cbbf7e2dba4002934fe022efe25f60b 1 7245e840918957f7a1d4ba91b88f77b7396af9d183a5f77add127919d78048033fac954ac51d5eafa75871446e071b1ed2c4cec22a87f3b764b1169fa52f8c0123b1bac004fa75a6960e34171fe44525ff7a091f47de2844eada697cf0697f0cde7307fb9985576001673431b4512d11977bf10185a75eac5c7814739a77680d7cc2d004c200dbe824bce46d848de436be9f4d776e628fe627741d1278292b03f8d325e471b62ebe4de12ee5adeec74333af0ce3ead8dca6678c41fd9c07240624a0b7cb1a7f0d12e76721976b45bafd3e73994edcad29cdaa468fc0b538a407fe640170b06423d575f761b003ec60fa058a896441456b112abb9f253abcf103f5519579906e0e0d11b7c4f2d93f8a5a7e50beb53424f04110c47caba39da10cf3bf5d06cdafe550fc5e9bfaf9f8b82257197b8bcac41f3186e36ae6f743e10f +generate_ring_signature 676d9d3f03cde540717f27be188a1f3f089b9de4264a66906be09441f3910378 1b768edfb4e27ae223a513c913ca9122e2da6bd2f88efa1718149068e6a000b3 1 fdb9b839c1c33c111ee96c1fba14a6ba9aa851737e89a629ab9be2c215c1bc5a f8eec233f256d39bf9d6b86bc1858e47748c5686abed539c8c44457cada7b202 0 f8c2b4185fa150b774a465cb1abd8d82f738ea939bf12cde5873f158674f2c0c9e68745b4093c9c4adae7f008da842cc62d5112a9714a6016efed540e8e11200 +generate_ring_signature 6b1284d1a3858c8b7932de78cb05afbd1d97616929e31905ba975dba70d7507a 4ace77c3426f132a65348c8f2abc450307cfbb5aab6b83748cdd9da470b37162 32 8e02737d48fdf2acb36863a26c00930d9d7022156fea101760d6f178ce30e0a1 7dd3e2d772fb4626db9ceea8dd6e455d147d86a4aacfe0f8dccba90937dbc2d0 413747d1db30c5a9e8723aca5d8e2b105c8be221c1bb4337222689f184c4a6de 297bb55ca843b5b0b291ec07dc6af5746d7348920fb57138bc2a6ae208239223 eebe4b70a7669dfd49a6996775053955a4f03e98b74f1ac029c1d3ce1579fceb 62d36715fafc49f61c615704f85ef67bf6c82c876abeb029e875c3847e9e8d21 c003d25e8b139b9a40b1e169a8006e335b7cae4a3736026a4ce0ae55f1d1154c f89a5190779476851312b1297ff0d8bdb0d6032edcbae23b48f9a8241fb7b54a 22a64918ea6e8d63399c1837210716fea97d6022c3657c869d7c9255cae853eb beb2dbcc8a4ebb25afade190a4b396df94791d873e34bf4fdd04f0a5a5f86753 51779058b46699ed76f017f551b53beb6259dce7238fcb23f62553638c0a0721 077f6bc7e6eeaf2cb3beb60ac00eca6412980fa9eddd932b9a408edca4981ec5 82da8fbbdd764bd252885dc704945be6465fc45cc30dbe3eb6fff9692ed05210 1df2acf66b87bdcec1b2d8e454e73fce0e6edf521d9c2ce5d8a0a5d8e7c4bfc4 c4107473cd90199469995334065883df47143ef21704bad6fef7db88a11e4799 616adf3cdbe84f04cf90b894eaf7a3e420d8606b3f5638feb3dc57684b63f606 180972d031b5a05c43c1822aa2f391de533069c98bcda58962e8f124a5816f79 e7fadf78edcf92bba764a30e00c626eecda4fe7b732e588f1fa668dfffae6425 369a2989333a13a52af60cc2400536292333a78735b5e2cf12ae1405d8e29c70 0036cac6d8ce1a2a5e623db6f9dd8eaa231184c85c1b7661e4a391288d82e7c5 dfdb8d0684014a38ddc314d51c948bb1fa93d2f491a0cb71c0119e7c3c677023 dae655dc8a1f360aae2d2f97cccfc95244703e91841372ad4e19ff62bf7ef77e 79d75438920510587b7e8935b56e1182a6b1736ec651a624385215d8d461820f bc7705ff99d0becd021efeb88f032a9f1a8afb6e4215da04aae62d4ccadf4b2a aa92d90286a2e97e8deb50bd1f88b0c17af828c037f5d6ca756461ce5aa50947 8a93227c8c929fe5668877d141cbd701b9221505a0af068d7de66651c3633b1d d8c07205cd999930a681e3df4c93d0f0306d2f7af98f54b7b0f9cc6c856164f6 71027190c84d4ecabdf9c6e5e4e441532b31da01076940d9ec6f8212674954c5 16fa8ffdb9437d1ea18c12820a0bdedc301adfbd3c525408784a5af4fc6334d2 dcf6a3df67483bc91e2cfcfdffbd967f1c50724d15754c08f46cb3afdd16775b 6982f0182fb32a36fe007faff4f28c411a686f97a40b44c4c825e865f9487400 2b4cc6cd754512d8987ebd79a820b681787aed6cb7bbd7cb5ef792ccf181b228 0b9d7dfac5f2c2203dccee3bf001cda08ef2b12f8cb201e2883df36f61970405 14 1e166cb3b92e27e5a7d5c1739e5bfc00970b2e6bc90c460f760b210185dd2d077ebefc4eb216ba10debe0feb86e5281506ef231fb49a4787a946eee0cf268c04bc000cbfecb4d2a9923d270ab34b9c8222f81e63aa73574937f59a8ad84194026ff39ccd97650182451124dc48a5f30a7f67187014e976438a01eb0881435307ce236da50436c9116e13d8bf8096abfa94cc7774fd8dfeac3e741e750a5f0d0fd6dc9d52d702eead69b11c32f46d3a739138f27b0533e9222a2238408c31c5063b3e64285824714097b78f9c9bfae30250add81c11677bb9f26be3a2833e5b075cc3f0559de4b3a415b0a971c36da19a7978e85d905d21a001bb19796130b100e5f3003f382a9b7a56ae251a95bc8dd31ddd20e4b8d1784573a089bf32f50e0142f258948cb600b75d719cd3ab341ba0529bf2ebe56658d3c787eff6619acf04e9df5ac6e5376a11f22d20246099d1a55e6d74af05c861a5292671d30a25d7088abf432cdcada442d32cb37852c6b5e885b460da78d2a19ea030f1ee4a018008bf9a5589a284c7c7055eff2757f905e6eda950a5dc337f2bca9fe04f28d7260e4f8574d3b62c2cd5fb3f6f720ec8101c18e1de5be99047836780751fafd41b01aa109292605e1913d02b300d263a7b1b7cb994d70e744feafd1b288ce8b94e05e69059e098b42900529473eb831354436c56a5431530b172c60647491ffa110a7476bd5a42e8cb412c0006df2ab7221cbfa371fa66c6b71f595675887d809c0291014a92c329c42e2f71ea18f45acfe18bf398c96be94e1a147e79f64c0030075e445a671121810b393481a7606b1fd2eb2d224db7d29b01bb63a2c9c865c70d98f598bcfc5853357362fe888b4a959d36494787e73327e5ab8d8b13db59d707d78c62d721f36d6ec561a17b651f52c24c0038533fe190ecd07cd3957aae5f0e81b559588532c205fdc48fde6134cda60fe1db5630015d3eeab0eab1e17ded01b3cc066608af7444e4c54c189682703b0a59ed8a89f4ba48059e5a0cf6e2d60bbc2706c04cb5ad2ee6f2e1a01606f45fbfdf7942490440e0afa00bb01cf5a500e28635216f752e78cf0f51c7958f8c8aed819379a3e839d4dadfb55171329401334a54ef085265bad5a5c17d283b6043dbcfd4c720859b167e704c90d6f7d30de187b76df69c5fb76c2f5722d119bf1fc9117ce46f3cac75c71704e9a41fac07b8264b498a1541d761c7ce5ae88f69fefe807e7afc836aa0a90e04e8a6005d073181a8ab532c2887c656497af3ee9be4565c47ea71fbdd0c10b89f083f24d90f46ec587325352f8beec540f36239132d9a6bb2ddd6f5ca2d74f1ac5cd9a4660a47a96286e94f1b99649bd611dbb061b585c214ec05c6f03bcacd5f1f402b5e091ca3e292394394845af85526efa20c8ff54f96510c2eb1c8fad2fd2071e3e208e80d5d6040a7c04c3747d8be2d744ba39f40a90aaff13290fce0023553398b0e36ca418ff92002973f3b5d520bf0bed2ca00ce79d8d89c0ba8394b10ae44290a397f3e50a86a64e47f29db45d3e3a642c4bc8b5ce2f8ba6ecf8dbef6f301140a1203fc5d9d76e9a82ab32dda8b4159c6b06b314f96fadc33ff58844f55f30e0f46ee7d6a0444ece436af6b74efcb447e50b48fa80c1805332dc67510b01a8e076a44fecab6831026cafda074bea61ee36ab18a6b2885faab940ff25dfbd5880a4fda7aa4c607d65ae4fcc91f98c8ef7fd31127059734c580faeae0c9a445f10df39229869914bece44d18bf3fc8b504a06c4076aa7054cea1449853c2f77eb0304800ab486a51aa7e789616ce7b1f2f4576d822e7f0a496d81c4e989dc169e04697a53388baaf75b8b505dfebdf2f348438355c026a6cef96574ae956da06f028f5717ec26c7f9c8794e808fc03ace6921865fa532074de6e2b86d531db04105a1f85b24cc8159b59043c558523f69c5773efdb35e4b165030c5494d345f5d0efbb419664861b37ac45d86936fb1417e25427f57fe63a34668c3ea856621bb038b79b1becf4dda51c1a7b9bbf561fe2fd47b49298afe0591d7b7fb4db026e30e1b5c76e3f9d62a3f0816f63177249bc85f6f53cc4316fe1aef1c987834c10c02ce8fac4b9cabe0cf5ccf69132f42a09414455b3241f34bc85aa4a3f06fa93c0f6589f5f2594172cc802fd03e04a8981c7282b6a5b9ad2cd932de76101582d406be03c9c20466bb6b3eb78560bfca6457e52520235dbc114268771431e8d499090522a2b60c769de3baab55c1f7ab1ba0f2e237f8b0780d47969b92b7f42d500e98aa2e11cc0ff955fabda4769b52627c178c9681bc8ccdcdf5983ac1cb851b05ea2d1619f8385e0a23e4ba3c993614017d74a7a01e16e68cadbd29c9c9d3fd0defb22b68bf6ab829aa1bf78ebc1f8d30e6067f005a8f75b0fc6e1e28bc0e29080fe70d937af655e3189dacba1528775183febcbae8b3fcd78523eb6f10537d0603c4ee2c39f1bf1bd379092d3cb42b4580c5ca3b278b018f8b3c9fa868055505363f9bf5d94e5b55575f35daac172617c9672ce781030246549e10f9a8449e09a8259e5d76d90f681564a98baf2316925936cc73191059b478d80972d19bd60b6be7ea56a240e808513811ab998b13bd170282fc439b421f7c1da5f497c0230d29be81e9b091c1bcc8ac92beb571851fba0f23abb783b4b95b1e57e943f58e09c2745e303ed17c24d99e319996288e69c52e1683c98150dbedc3867f74333503ba6f2dd8e5b8999772fedbb111c5a6c4db6d4db757a6db807599e049f78dc20e2322b5859278d3347c663795a6e2f2042c6d1a3af3e2d7462de2d17d5310530ddde5a7a0304b4c0adb2d80b9200d143d90c3f5b93d7028b0ea9e2779fb0acd03 +generate_ring_signature 2877c2572723b6c1de6b392c1fd7e6b9b06be04a33b2c87ee27559a4f440d301 6be1605f102b685abebe480e495a6ee11b401fc4f8ed2c7a9d59372677c51133 1 43f61d5fa46d25cff1552dbd032d2072537865b23f7631b16355d6e04f57406c 5768afeb110865a5c863b330dde198fee80d775dbd33ce56e639cc750825e60c 0 9cf705b616975aff3b05be50dd3d0bcc5150195f3f9d9aadb47ba3fa50234d01fc53b9c7b464cac22c534b2158f4478ad873ed37359e45cce80fe5fa0e04c80e +generate_ring_signature e85672e6b328687d087e5e27a222d908708082526eab8d9d1ed9c741309ae642 1f47514331ac0da99d0bb8bd185aa97b127a5c428ead41a49a1df9795df80a21 9 5d81cfee1bb4c5d9f12691a0a7f099897a6d2b5db92a5ad6e4f16ddf67ed1546 b352738407c3d1b642ec14ce4e87783c3461995d4a759f213a302d2f6644be10 2561702e1179fe38a61701da52b2c6470b7ae222b7be59aac2a4850a00a0517a b8e838d471c530f3c0eb2b6ef7a5e91cba4fe4ae976f5b9dd34d388678ac2c7d 00e859925f2b79171a96df93368feca24d15d9160aa005d86343f69b2147319f 59a3b6c9ca5cbca2b1b27ee13a158565ecff5267bfe407da2bf94c863afd94ed 51eb2c685edfba3a962e435aae95b54f866eb1429ddc3ffc79573c076cf4a638 bccd0d7379c0efd79aa6f27515826d350b8cbbb6713802a834a1f34f49a09fb9 5b7b1cc1529d4eebc5fec0b0d478591c0bb0bdae91eee28219841262036a8a60 4442e0c6b2c4e6cf3f5a1a33cfd304e8fd6c0c1cd8047061a19d83525b50b30a 2 bb40985067d244cbc7a03a5624fdc0a225c41eacf3276cb53ed5e6b108a0fa0c55bca1ce5b58ca218e994057375ee04eb95b589a3af45033316631483ffd620532f80ddfad405303f9dd68dd88ce5462fcedff7522346a63eb1fbb1906d7840540e55a08348992783fdb36c530cffc272c113d96146cb5d73ba2c5d4f25b290bf9c4bb91440988ab2250a4e5ed791198f73684ff30e650aea40c46235e08870359c27fc6277824c679a91013d99bc2ffa5aae07bcb2bb7ae6921ee8bde25cf0c87b9a43d8cde69813cc46378e464c2ce1fcb3c63df8a26fc42b1fb79449356051e019daeb4533064bb7f62539e2f6e9631acf12a0049e19a5ac1dc49b5a4fc06e0e5b56710c89b6905912b5d93190d46fd14c77416acd56177225901d85f160b6f772504adda9dfab49ea1aa2549be7f7357e8c57fd4b950a051f1a448bcc40db7bbf6f8668441411c756ae84cbf1932c95891859846cc181e28a746310b1807139982afc11cff2962a9410528a4c73144e56109b56fbc55f07075ad7dc91500982f633d6a0a1ac393ab8566266e05780f949dce7e3f438c29b63d6a05b69d0c6c52564680830cd0ab643cd730839bf5b4a3ee623e56dc927abb9a322146ef0670fb48c4adf0a26aedfe3a1441808421022c9974e3136097a07fbf2b37fc700a557b7a68d8fd39418b74499ddc7ffed68dd834da6af967e17672685a06f255015b42eab4b8669072a7bf00946d8890f2e1512645fd1885d845fd64060ca9e30c6179f5a48ae2377ad79765a25d458076862b56f06aebcf10bb15f634dbb86e0b +generate_ring_signature c960ac5d7f59051cc139e56674bcc5a3f0fd10f6d4525685133ad71da9505d03 d110909f0a37135c260f7198ab302d84f9980cb0f9fcc863b9865e0fd76e88ec 1 5cf0e409f6ce507bb1d619490f4501603aa0b5b11c2040681f5691c5e7af95b2 1f795ee966c8a672d15337e590b1d327632928970274de649f6d9a6d71cf8906 0 13f7c75611ca96fc467fd475a9c79e68105f7844bc97725f334c1b33c6099e0d281461587ea3a35f50da6c84d73dcc60ca22877aeee26a23bd0f575f8df25f07 +generate_ring_signature 00480a8cafb58dd4fac3d4e0b78615ed33fcea74c26ad9c23d6f610dd131879f 2b7fbd562eacf805c3bf9aad99e90ecbd22a521dddc5030c2777dc9f139219d8 2 66f670b720ad778de82b255292fce0291b5729b4dca211c9f0def3ecd0f8c143 58a740373134543502818f2326766e0188b935de010814e3e61fa44a2d1944bd 118bae54eddaa218b8e7b8c9079fa5e42666d38b08677a64606708e0d6574206 1 f5e758369ddf3a20458883dca10e6fd1ef5fa3e19012869752c5c30a4d022b0247e585ecfe5458932c060abb6165803952efdce7c0333bcef3823947f0041202c1adb6ba852ea57105a66a7030acdb6e3ea854d3bbbc8305b6a2c0ae1fd5d20436df8f27009e3cddb943c75c3abf35560c92beb367eb7ca344e80b804aa62407 +generate_ring_signature f92442a7a535e39ea68305835767d33b4a1527539ad2a184188fa473673e583b 9f256d40277f781e035f26613b45660acd3f41f24a8179b60a56999115a83d77 172 fd881da6976a55847f9590a313352a1a24a7c5b3c8c568bc37b920abf088ebb1 ce48a50c2e6ce8edf3a295368278eec1c9b73b56435783886ebada20591770af c1533314426eb66f59ad0f7469d65e8019f902b053502e03b0dac849b7262414 284bae0be6d2bd1ecdfb9ad7c6857ee9a9ff6ed1cf50233e87a997ee2bfdf909 060002eb3f3b22f173e4ba9a50b9140c3cd5c7f7c3c307369d3c87466ba9ed8d d7d32b301bf7bb28ead0c666282fd23a0cf445219423997f8dd30afdebaae9b1 56367289441de9f8938588385f9a3a9f4f9a63f871cd3b72c471702b8712cc2f ea5c570305cbb52ba6d889d8b7a340e9335972d520ae71c9c13832e491567464 071eac248b47a2c4653eaee65e55030239c5a4477254f12b2edf3b0be8367ac6 3f3b3221a7fe3ed062a4d5af91c3246cc7d2bc30e9d3f85947970944058d6be4 bf078413d79661eda1d8774f7003a5816a93632a8cfba1f3730d8a127e28a9c4 e889d95ae3eeee41f3debf69be540254c8e68347e8b2d19d96b7759dfc14e377 f5dc25d2ce32defa29030708ae1d6d36673f8b41146cf3d89c7aa2f0f2b6e313 5205a7793f0a4b0d6e1e9257dcad29614c9d0f886fdb16ef4dfb4fc36af632f1 cc33629b3226d7404851fef79927fbb90c1df81cd95b7703e38ffa526ddb206c a37b372b2c312ab90f771dff4b2053d95ef3099c76a6fba984bf916b29b56341 f4044c8f9a9801a5a04a157b7c9aafe40c019207ee66d29fbd3a356bf710fe84 dee92a17e7bf79f7ba3d6b6d01b213bdada853621087e1ae1247b115a98768ef 0d696f6a9682a9f2d4c0e582925281368f16efb8c3cdb700154db78114377dd8 57728b15184b062d286f942fd3a163ba68cfca54ae3304e739ad3076e1cffce3 82ab1f9afa7f7f77289c00080b1f98a487a14a2f073cf36bf96a30440f20ca52 139c5e0efbbf51c5af2b9117b7fa58d6412d46d67eab6997857701230a94825e 4e17a9d3cbfff868b70ba95e4243a2da2df968ae5b0b67fcde527eec0c084244 923577965437137b6a933bd61505ccfffd228aae996d25fc96ae9db5b98d07fb d841ba1679e8ace379aada4547333b839b808c514f6d19e38895ab48b1a9d16a 8175b06c4a1b58b90b367aec2e81dd9c41db930a96b3e08cf06ef9162a21415e 818abf49a345f93c1275d55321fa70cb50f458e9e67fe911aca8e9a4a253a34a 2f521138d0edd239f6e54d5e4d94efdd9749f6b520fea327e642a0fbee8a5c04 9f46910d6268a0dce884a538bec4a81c3bd550b739bf5d02035174d1b6707a17 64656d4ab3a6f1c92cd71ddf45659176f2f37789d0338dcf124b1e7ad1030f71 f8c3b190e7a9091781da8ace4b6019b2d68f05dc92e54960a1a8571702b9c77d 9ae039b7b615745cb4e51634a5b8a10484d73b81a9baf555e11efeebdb5fa24a 75157319ba0ed1b0208d3764bcfe60cddfac56aa2330c76439923d245bd7d81d f997a99abab99f25b18d72b7d5b73d4dbc1902dc3e1e01d1fd0d12b633772533 aad3267d5679ef6eae0d094ae5df02b5228d3257d9a1edc0af7aaeabdb9051b6 1eb4d7c23a4c40827eefad193fa0c778f1f84a07210edc51b42d3150048f90bf 70e8bc3d0136207ee441333ab2f463ce6a456198aa7d7acf7d1c966f89371bdd a6041cc33222e72a94710efa35235937f843d4372ac73668217849e4f908f4d0 265d61d8b2272325b2b69dae4f728d3fbb3073da4c4006d49867abd4c1575806 502d8c2e6f00025493227c87f12a1319199c3283503748b32b23af0bd20eedc6 41d762110cf7279095cd2116f10ec88f99520c860d3ced6ef72ae99c53468ec3 1270155b3fa3602160171da175a36e4ac52adf90a7f52ae84d8be8471863f681 f4bfee7e8374801d097ba1199f24c2750bee92f29f5f84174200dc8f25234023 ab8ece6cf4ff2b2bf545f8c57791685361182df280ff1bbd925e8fd3c16dc329 04f1c90fc0b45e41ed9cc34a012d623d0a71e726eb61a157f7e5fdd331cfea20 ee65cd19e5b17a0dca4706ebfaae7474929503051fb26bc70c7f78f986311e2e af5af1a0079b7b62f0b2eb03964ca314a21e0be6685c1c6e6dde44bd4346b943 96b9d0ba83bcd6f976a964068dafb5329d349380910d3eb67e550fbdc46ab884 1956cd083ace90b869f0fc7041362b47a5317164ff30d5d4597f205d53a71474 402f7334d4db7dba7b1ca8572cc893223cdd7fa520459080f70cefe98a093635 ba1053f093a6d06659433238b107558e26a28d2db942a84d9747fd05839f5056 89b8acb7bba7b8cfdb045e158837a4beb36c65fd064fdfb2715ac78a95dd76c7 7d16961a37a25cfd9020b44566facccb9ee31c8663c3917ad7ba0cfdee07ceef a731d84951d200dc2dd94d18c77712c558bc3704acda6085d6bb46d0021dc7b6 a14c510bab57377909416af4573bd3b8a551d2d35421a706758408880bc4871f 59f8144bd5690d28f29907c8a958a1a70f8bd305a36162e310f04f2413ac55c8 0d56e71a5e55aa4fb86756dad3e4edf87f3a01a81987d1e13f86099dfc3b9494 5510c0c357755864b4485fa43bf7dba01d33052918604ec41533610130dbdc35 9fb86f44b481cd504258ac3975fb5a8534673fd77765f5ed90cd310cdcb4006c 3298a93187801ec62be93c5aa5900916e6ef57a4e19331597b82dfb9c00b2d9c 55b2800d41d2641fd75a214efe80a565fd5f27410783232f2a44f7a774ef9f2d d6e2629ebc2413e4f294af37b2fd948a7d1d71728fb59794fce78ab173f32a5a 23617f3efb68af4f70ba8173f61b95356389c4cf788e3b8f72cadc0d681975ff b06347741f5fdb84b017a177fd904913a409c46e97cc00440ed449900e9ec2f7 e88ad1d059d2d9aed7150005189836ce56dec709b8a22f63ed2304ad6c42c87a 2fcb144f29caa8d4e19a07f977ae6917e8fa0c830c52b0d4748ebba4801edca9 a432e7e9cfa465440d9a0b1df528d974cc32d4c7f25ceb53440e5995e9b8feae 0b0428e6f8ed8c90dc54832b15a20fcbfc82cb5f908f36045b1dcf6b69cfbfca f4064a72fdc46314d3652c0bdfb6f08f1b248d5c747998fe412cb86e97c6d83f 45cba132b9b9a0ea8525b70a158d531e0f4bab123078906e1257558b9904a24e 9b41948b3d7e5d4636bb670ce3b66d16a939f34320f0317ce20ede8a9651b700 bbccf33c2d44e1e59f62fa8144edb353961bd522a1e380f95ed006172c88af0b c04093b6c7bcd3446060f9c18cb62503acd1b1d4d5c4e7818292065bb29bdb88 5cb1f7658f73d542bfe7b10a988f45db4bb9d777ed6b0f780049ae54e82b6ca5 3a8cdcff1cd09bf1ba1952514ffdb00b204322613221f6c6efb969a36c164adb 03f8c02d0dc951aac9f5b70c1c044c06c9c98b0f61c5b30d6045f9f1a12c1fe0 0e7c767d8a26d57dd35d5aea58aa9d3a6098dd8ef2732c66bdf0e183ba23f6b0 1d6d4190d0b0f4b8ce1c53f1ab48c601ac7f4c487834f2ed355255a62813e11f f609212468d6b96a6ed41dc61708c2521aa924711029801e8e994095bb5361da 87f496fc9296d7e4a2b3757e555083f89192935073915fc41bc8f4bf447bdecd 1741f6e096f8cf208f002cde14274303ddcb25c2533b9dd10f0728e41f28e501 b091eafa29e0509f605d0671e4c207a74acd926ca791824d0e8751f58124bfa3 8fef9d73d54f5984806448be3a3b469b1b9a50ddb6c7a69a53e404ef3940e32a 5da7b39dfc6181a2cf1ee4f9f2e695b5bed73909dfc32f575eb815f53c8c0484 5e95b7c9f65a53d07781d57093ab9cadc813bb153e669de752f5c97e05571507 505ed9f5b5ae645ef41900a40d7157b332290bdeae3d19f866af304989d5d8b1 1f8959af3cf6788fa599a3a00d84c323bcf29dee02bc65171681fe42c76d4b5c 443b4dbe3217e35f436a76531ee08d19024edc0453c2d341991a8390fddb1568 7f3f7aaa9da801b08f6cf47eb23b3cc7d098c5d21bef44ab449ac00fe6a19299 6ab9c1bcc391acb14e701e26b32b7a6f7fb09da69d51b289469f2a977bde80d4 8efffda82d82d52bb1cefad9ed90fd82956db431019d040f6100ac161eb7779d 59cf1ebb7bebd9e07338a515fa9882239bcc77f6e8709f4765b463388a7f99a8 efd4bb0ff013d411e7a7d220f792ec5ca72933777cf416c5d7f2a2945071beed d1daec9942d6cfe1c2a082609eefc2c9e831a0c07dd21570390e2cca7f2dff50 bd3adce1a245373d0073f2430729aa57954748d90e3718399028fdbdf68b05c3 b93a50fe5a835ce86b56209a1057babf323e6dd32fee9433db6ed3695de7d9ab 3a1f69a2825eb6d2c90b438e9d7fcb3fe8c1e38983aadb0126aefa31bcf6c645 ad8be22685e2be87180baf881ab3dc90cc636da658175b732adfdd968e6e0246 9d57a848b3079857b0bd7c7f961e61f3d6e3d52c2194e3c3bff28dd1bd5080db 1c1d0bcc04c7ce4cc76effd19c8469638abc1e044dc2a79c7eaa0613528c7c67 f562f27b6b50c3cad5d14e7c4f12ecae046dc008d5dc888a89d796aee7d92272 e292217b111114bcfb21cc886b628e57eafb81a6f15eb72478b3a58c9fae5ed7 0398e72c9373ab5a0e21048f4fa031532ba8fa7072d08fc12fdb9cadaf0f160a 77c364f1d84650d7cb383dbc06e3c4570dcc2a343ed4f0cac0bfd76e1a0d250a 067c1fd7d8f49457bfe82088af2622f1d164681e70865f2b317e8a39b5b1d5fb 32a06673ae1fafda5af9e2a4400b0b29397a71e148ab0a10588d97cd408ce759 5a9d9c4d860c661e86742c4b8a2f9f641a50abd42b85a702f0f9078e06189fa2 939d92a6c0bab1d47d8eca4c7a2637498e41d517bfe0af1578ca6ac2d43fac67 b95cee77da3467db2e20ef20a9b88a31f1160cd4c96b6f08cdd41f719471086f 36ec08a5ba086104f1e20aa1bee9d911e499d0a83ee3273a9afad03c64d86885 d322470a709b5aa95a584918ef8e80351984e58799fe061c8112dbb8ed166e00 3bf94b5d1bc3af26948c9e95f0efe70707b7567f6c868a26fe0b3736f4eb84c1 0dacec7f9473b709d9d08eb0ebc09f8d14153596afc5f2151e21ad5f4ffc552d 91f9062ae066dc0cdb72a9caa0dfa82230fa2db6df73e69d4ce042a8c060d813 689f47516ae100226f511b2491ce607d33565d036c270dd879f725fac4f85ae1 38a3a226fb70015ede495dcd52d5317c8f837ccf80ea444e0872742b959617d8 2698e10a18971e5533259238128acaae4db48bddde16b3671e0ca608ead5705d 41e5a86e6e2986ee7803d5302b70e50086b379232a81b8985de0c6d8072ba128 9904888e211357fc304f14a141953565774a8681ce718cd669ff3d0cf1b796d3 6f7470a432f921806f7704af9e2ae3db09e79a98691f5fbb355ee869c824a9a2 71ed2c4844a82c9bb02a0ed2ceaa1bfe5011364c90ee2bb86bf2eb9ea4656198 2872fb459f19143c897500e78f4e03ed59836d14c8a99abfe1c45c25fd9dc225 afc7929206da56bfecb5fcbd92f504b8840ba109f2bed578179316f2840b7493 740e6ac8f581a0d48d1c890ae1efa021348711b7668c2bf44ce4df9de3900d8e ab637889c7cf30038e81325a13de81d28096719222b2eab649ab4dc46e306abb dcbf3053b800c54b354f4ab29aa1470d2cb41f516deddfe7dcf37a32c6baa4b5 43f4f3389d1301a51d22acee3a2bc19d8ca3b4051279ffd1cdc43a81ef0f3120 d53a2897da965ed3ec8fe3a64c84e94148f5a44e26aa618f2d7d6ef6e1fd5f45 eb36446a8f29a00e560e3539910c595e84159b0097ca45e1251e211efd4457a6 fcfc38062f786c990627e08f21824cb2d7aaec22f7b375e5f9dd32d82fb9ce7e 8b639553c2a999201727a1b7402b65f75e048993ba308690ad9ba2b65361f507 e71aaa0bbc4e864ae50dc86b9ebb8644320d91352025fa0eb0f60d0a3d054334 3e0aaf77018f0588e9bbf0adb974a83cc2fa77f42cb4209c24766282cff72286 52d0a11678b725c2199ffbda6814f92e9033c2eb5c5ec249d4fd4fe94e23a726 8f8c15d5ed07695a7d4e815cdcf4dd3dda0926d0d752468948ed919e4729ccf0 aaedeb70ef549d4196dca9e3fbb7ae4bfb81e2908969840c384d2afd34af9f1d c4033ba752ae96463c8c6d0d2b84ff52be33f3215de8f78e89d817ea24f3dc76 f1207b72213b423c405c200a7a5fd7f8023e1f239c8e606ddf71031525f23b37 baf147ae7bdea07b53a5f5e75ba469179543dd0ca592095ff616ed04ef9beee6 2cce4a9d993261d22aacc8b5846160c0d1c4fbadca7a70479fcdeb550c959c83 c43edda5a8413902774b1f8b09b27182bec45f69e5889e202060dfceae6e9ce5 22832c1ec22d2ab3b19093ac102a7aa2b8b0bd38952b9e35e44c223de1269a0c ccaeb8254c1836cfe80e63c41f1b0b12cba744f6252a115ce428368eaa19f561 aa56531344d04ac78cde18fe6182151d31569d6989ee9d5dea5292b21da8a3d9 543c8af32bfbd127e6719470804887617c42fad95fcfcb653d49009ec615b5c2 9c54714b1fd93b07226cb356e7e29e1fe9c093a22f3832f3498b6fbac03f50af 0a1d9f709bac257082747850c6adca588a7497f5853cdf90843a4deefd0ebb61 1accb6cb2ad322986802e1cf695a77856dea76a0aca440e6f7ddf7c6995bf1d9 5a077b219652fe5072cd730e904db6069475fa9808c9cba94668189922e3fe14 90a84ec912c8cf108a484d9305d0435f6f8aeaf48bcc4e11f86f5bcfa27f1f94 0600ebf5b2f516607f12411699f6dd8c8817c88cb9f3382d854d4f943ad23f29 2559aba16f4afe15749840263fda8974a76f6ae4e5643e3f6b797a3c62cf58ca 7ab8110bfecb7a962ae0c9c503a44ca616d3fe93adbebb1daf376ea312adbf6d f43d50e52ee7398b0fa244e144ee76d30d39a41e7199424950d4f00e9796f9ec 78cf72929a258d65087e8568e2088a6ceb6cb807a9ed07f5af1af426a59c5eb7 1d840f3efef11524a202b6b4c55d15f152ebec1fbf543292464bb3de84a3c0ac 244cef52f6ecc47defa48791392fa2300201d8b10310aff5450038c14991948b 74779637e05d6c6634b4639a77837b33a5931db6fd4669514df6fa3a95db181e e2f711df4622b4ede67c2a90f1b54e90e8cd0a9e9233c30ca82ebab46c68d97f 935c0b5866b20fd6094efea23ec5a44d9f38dfc5f9e13aa13681f6c944903946 d7c11734281c2918cd59b872819af1fe6808b67aea22aebcbf9dae2289eeab50 b8496e518ba54a9047c61e1c88255305f43ddbb46ee1952e5261728f6f233950 a12f64dd6adcefacf9a68dda0307f97699f45069750fd2842dcf49d30d9ed03a 8f98e2e04a873d27b24032446f3802d96921a3d364c825e6b918f4e8f3e8ab85 03b646a54e208cd8e396a18b43b96fc1e742843437aa917e6cac0d706b0782d6 30322a0e721a026ef6ff39158e4315aa2a492cbf3763723e7bc78657d9573a3c 4060d5f0af5ce15d7a8d1479313e0fee2b20f8e968cebe186761d6c01317bd05 825b5e2c98d748a4749705d0ebd93e0baca50c01b072dba309b71e7c52be9e1b 57deecc6a18a1eb81dff5424b8da3f6a6e34ec7af818a361d583b905ff7296db d576304b302f68dc38dd385a2241268c204f6d0219615dc123b0753ba0cf35ef 13f693db38785ee165adf0cd566a7402f0d4181d2bd633c081e86c5509013fd9 258de71f92a436777203c25e57a22fc51750bfb959022cdbff5fc1d3eab8b41b ef87aead744d41649ade14650cf65f719a7a1083495fa3908acb756f84468a0b 99 19bf3e88892fe67a104217c1ecbd02648d861a44d6c128d55c56acadfa037b011dc6201b4a13909db017475218ac303130174040563a855978e883582c1ad8095aa23b1265d9e7eca8434455649d4e6770b445992fe368b15abbf5240f318c0adda4d6a8f06ff6f89328d45fd772e6804c4658567489b5818a1c16c241a4470dd500e3a9883982b9da0522853d835d7b411785c9eff31494bb89af48c279c505a2e2dfc13aeba8ee39aac52038bf1571c79c9033b269c8264b39a616a85a50076faae678254409888f5b1124866f01f3413dba6ef86e7b77d9336c0fe01794032b4f1e048288dc1dabbd94b0ee7729eab754a0d35eae60345b1ea197aa1e2608a352ea941ba65864c883e406e512f2befcf8faaaff3c29a8cb39277fc1bde70f59d45a6e7efdcca8f35395dadc7db8c91e869dfede900888518e62e652e9be061a5401571aec991859219bfdff864e6d10d0e74c81530c5c856a0cce0eeb00000818ae303c1e2124e11b949f6b0fdea7449776394447c7b807f46f89974dcc005291af72b726a8a2eca3e26b34dfbed3bfdc372de904ea64b88129a2fdbaf300055fbe3230ef2b290ac14c9ff173741b008a6fc17c8961a4a431bb583627010717403319af8a383ad64d0c68a2c28ed80a503ea473c199b49e06d1f5cc056005124ef390367e389a6ba862d827bfa0e64120322949f27d5d3522a4b039943b04f0bfd20b793442f74bd2781afd6bce8708e41ca6aa9b0894d4b4480e5c5929090874ac770c68b5ec20acc49077d1e512732b591ec7e7668d8acbdb5f4a3bce056720f06026fa04ed2a8d928a6a44417a983dcfbc28cfd8323861d2abb6a268018b3755d48c49df8e7fa8bc81c49618639aa6d9c0eabbbda56b5546e68bf0bb0a43153fb732723913d66f34f0fbf9f2197fa21c0b5736b845c3761f3ab6d71c0174b5949ee5b0e8e4c5139ba340ac0a874b3408b6ed930a097bb4c528706dbd0f2bdf376087d3ea5e565537fb91d89fb5c82e34edec4de79bdfb6295e1194630d3e4b16ffa9e030335fa329d3de09e3d61a151b7bb4f7865fea02847af6b58c017dd5d09ea2a92ae2c1d146793ec17d82990ed31270998ddd8f59b6220527d709b640e8102d3a1b3817d3d33e3de6d12a11f66ba893b266f68744463fdc21c209c49410774bb59de4fcb2b496cc5ac85bbb99a68844618b9b5068f2e1e2452208acf255c33d8a9be21eda174c1adcaf61a08a4cd19c9bc148a85f78e9ff24030d492017bc46d829aca317eee3f82f1a5d4f48e99aa17efe065c7215a656e1570b56088257b444966a3f257d692aca91be53b2871e6638440de6bb8f31e469b8084d9403a11f219d861c2cacdfc33b35938181d6efec16620b2f56662a4506540ebdb6638648361d0c46d03ca92969a087dad38f95c6155b3f0ba08f7f972f520fdfe68848f59bf320bac3f6fe8f0d4ba0ce1e9382694106a5ea6606794891ce052031e7cabef2b11d748f53b635db2f2ecc4e73f8563c55399daaa6bd2ea7570beffc472b5f4cb53231f2fb4e3f2676ff6c05eda6d80c76efafaa4d1c770b04072d8442b5dc23f3fefb0810861b1abddedb4a604f0381ac0ac3874401f1ae9f04a5480976abbdd70182ba817cef39fe7f0046fbc7c70f37570588affe7ffacd080c0741c25bcef17183d7cf83cbde7d8d0c9b0fededef7a924e07b75ddab0df08ab25994dcbc003c70d8b7c2d56613f8bc6f89bd2cc24f5302c87ad9acef6e0080eaa548b00d284a587a5d21cce894c750cb2487212b97a839c985eb0d71c720879227ef868aee3c57207a5cbc5b5b2cc7a9434bf665d11177c237f4b0f7bc2077b9d7f51e2edc55bf66480afd0777644c287e7c562dbbaa3682c868d2681ff00fef3abd2da8266689d3b005b1d812f55041a65345a783621b5e399b20a33c00719ea615e443fd40e431565115a0fda5aeafae37f2bf622e3f4e74c456cb3140b63dcb1523d841413687be06b0bdd2f783ca208672193e77d93245225286dd207820989c3783c5c8dcd6accd3731df33fc3da5a9ea4cb1888fd9691ad0818c00bf9313c27f4ba9af58209d6254bc7db105b3110963991be8d9fc2ef396028100c592ac03d67a52113ea59e9bee05d2cd55a3caf55aada1405582063357e82a7083b679d947f0308a9aa22fa78a348a0c84e1c27afaa0f49259ea86a72aefb4206fd3439199437e5219b9e3f626389fd19dd779e8f260646ebb4079fbf3f9dfe0e47c23d32e51eb553e411ed708d09844e9be693c97b1f8e04cfbcf8864502fe02778c725e20a52b1213b49b1908e63c5a01e04735a6aa633f149b53049785dd0fa341f0a72003e112a5a25622cbe636dd81bf5189616ef90197a1f5cee5ac0f0c8c09dbf72d2cf6942fde5e7395be69e97c9a9a831c3a90fb3020c39e2042170cb472ef176d7f94ba117c5f81fe49adc7fac348ba27fcb00d324197c7eca4a504ad10899c8e68949ffd867990984c1f333aa965feeba8ef4295a844968117e50c801ee46dabb9844ea170fcd7d9d2e200cdfc337fc01b3fc8d45d95f39c11d203bd747c153b48a51d995ccc7c545c3c2eebe79e54b6a99c7c81b41809d748d2096f01bb503680db4822d764611dd19777244eb6f0e65326a514cb5a4594b45b07afb9dda3f4949842e244af88db19cbceac5236b29dae48853c19d2c5f7b7f601578f66701d4792af936a8f9d5af36ae32f66c04e19d2053c3c618144ca9c9d030468fe5be2d4bfd8946bda43ba42ad3297068854d532a5ae30d9069305ad7209c80b0ec1c3c555ecf8168c2d07c108ced64554e1dfba9c847e6637b36a3cdc05c6437a8aa793887cb3593cf64e58a32ccd3afe2a56b423bbe1316788084f6b0d5f592cb4f04dec5fcec736ed25c1e494452165d299deacfb053135d23940fc086f43906197d023f6af703835583127e316c4ded2dd4f2905da84f64273e9b5032db5cc85e3239b363305a401dd5ae7b45da846adcd7e9b242ef050e76639610e0dbbe40247ca29feb097d5da8cb83e3c7d5ca44a87daefe25467866282b0270ddb175d4d8b2d58a00e6d3a1be655ec920a47ad2a1b6838c20ae5fba3b85d800961b591fb9f656da834f2ed5cfc4513d3577d2a3e6dfeb06d9ea306642f99b405ad8d0ecc1d7b6feb92ef5a0990c6e2ac6cfa49b5f525bb65cad4fe7093d8b7072c3fedef9cd270e833289c67eec91e8cd36532306c84745b3bcba4e26f26a207e01c97da39f5c284271b83db54b7f6e0268b9700f83ee8b8923287499b31870621e7384e37b698a5a6e0e6340e8c46cdb0e7da7cd6b04042aee93e282bb63a0d86864900277b779f949f679806b8e84c62e5096206bad213c86480f7110d1d0da30bab6e4101306ae2b1aee6c6827e60aa2424f6ee7dec2ac75c419e0454690b1ce88c87510d00e01dcb69eac3808a3df2cd3c4fc166fd174cb3f505f1382408c3f644ac5ae85829b62a7743b7b927fc7e3ba77a8d3a9544da65055b07f85506ab72b78e733fc115733554fca7e1f72e6a2b6187513d8c2f8101dc5325488e08de8c01266473fb088de76fd9533412c321d5ea7fca27d3d7a86940e3f861950eea427e79a750cb7a62c5cea77077db02628a55700f3256d04c74c89a4eb8350798ad31fe813cc138760418bb7bb070d31a12e6f554ef0ff1001801074b6d510b49e6c4b7d5e5f148138e58ead90cf74a85d847e0028fe2acb386a1cd0e74f504647cce52c056f691445f8879e38ba016a394296a76c64696effe043d7574d00cb64887703991a0ad3e05c00fa9881aedf2a7dd42bb8fb0028c1d8bc80bd5100c07fabca514b38ad34372d732dcbefb185ce06086c0dbef0a29fcf19eedacd900a1432cbc35f0e70db1286d60b334b9c706156c43a79acbf6b60c686a4f32760c4fed0e56a784368df788c466ac3f42bd1183fc505e151754cd3dc0fef66be40804254e961905ce11326d87085d8a2b27e4fe2deb60ba6dd0a70f25641b52ae0cb27b2e81beba5a1059c51c232e84478b8d1fad044a1c2fd5913cc9d2f435590884eed1456a21f7a32a122ca17f77037e385e9bcfd26ab9f266b9f0e8ef25f502a8b078fc067a70996d3ec5979c9e866694940480324303e0ea9afe5fdbbb8700f0fd271e18f63b313c8926a825d8a7dc4e9264da9000d6f7c05470237b6cde0bcc33f51f5812444ba3a3effe65fb5bdc75d1a3025cf0a7d902af749000db4704f1fc3f994d991e9d7c898a19a9d03f9e265f34a8d7d503066d6bdce43922b90b753377ca54b7ff031f5f177f4aae7805dacdbd351998a27d4213078c568b8a0040ec2400665e35b4bfb9b713beac4592662535180d4275ed7659af611f876c0b3ddc86653724953cef780dbbb150d76d8efd3f6bde2d26d3861b351f13b9e00efefd79f2424b143bfe7f0d3fdd346e27de4de2fbc6f4a12173ffa68449d97c0cbb859850e7303e597bccfc4b372c5ebb2d4b591088b98e6c9db0348712261b0cbc87167912ae868e85ff1008adf3ab59b4a54089294585cdee5117f486c83709faab527c50c415d26967101e613c25bf0c9238ce685f99c6f83611b72d073a054034dc2066fe5216a887e509784c9c6a794f86bba684e345d77b595ac37e320b482fd755886c6ce4f76c22e73c1c9cbb8e4633f6a97882d5764da0319905c20f5e110622a0157c689be8b35ec4484d903679981ed0ce293015a2ab6926a84d0b7d91695756124810049fd53fb6759c4c15302f49e75a73625ed1e43e4813060802d1aa9f4e02ad255a200ddfeb7aeebd1cbba209b4fb31ee77c25808df013507994a641b3b1397cbfb66c77e1a8b8308e35baf1bb57c4c66e687a2aa878b6707c2ff0024c004402bb0680fe16e27e020a33c4b8b63187db36260f379445d940afd90d0b209412b98d1ff3dd82504a85c1731e1ca912b23ea491062e441bdd70de74ad8b574cad5b38c740718938b54072c8cf1e6e2b0aa840acf4ef1e0beb104610b337feed6d95ec83ba94d557a09f808494eb73664518f47edddcf1ae90b0b3d07a15529b7f8f38489b57826d7393219a234365917755ef5bd58c5fce71504c584409e6b661441d8b1cfcd274214f448e92e3dc60cbb1c536f0463770852077e8605756a28056ed72ff446952a3634ede88d13f6cc3a0667757862b5d81b09d148d15844eb504df6a682e4e6cd76b8246c7edfc94c7b33e1d24c558b29510d5fbfb9eb946b4f9eda707226f3da34b365116cb953e1333d44219a94ef4d170e10ade3e9211d80eb971ca6b91b214ed168a8e29335228e6a91bab45ddf08790f6f4b428043d21eb53371544d2d7bcd6ff1a27ba7f384db5810003e7e73cfc107d40167f4b7992df9bc168d9abf68b495460769b84767869a36cab441c0255b0d48f2b51f729392c7eef43304cfe541605d34525d32ab013a3a7817f451bd4a056f7c4d21c56a1618da1ad2fc7e379372213ce4bea71c3f18f0f579273ca9640405d499b0d35d7d410b57e01182b59911ec9e2ca7fb87e6bd4d9bd079c631ed02484bd18ad90c09aaf8670506373e54544c2fac82e891b3324a1bd1ef996ed209be20a5a53da64d8b1c3962f268f96d08f4b81e3d3f3eb756560d4e468a50430736895be5ce333264aca116707b5d200182cf3325808b078b3f6dd1ee20429d09610efcff54684579bf487675dfcc11fec86f6559d1da7e8e12125fd5315b910700ca2573dfdc5beb5862db5f88ae4dd72016ca722218e9a0f13226448fea5a0c0bc7ca27e3ffdb2f2f7ff80a8952e0871b9070e2d3d0b4c466ed3b7e3e146c0ee97b85607a2bc7de7253fa5bf1d5518d7ca6bd92a246502710a0b3a13bcf7602c2df9791ed0744c75fc227bf74c38613e1177d9617904a244eca2726f824040ee1d2a7c3d8e5d430a43ac409ef65fdb4dfe4327d41f213601e8348384221220368593d426b0a33d4933458b1c207217f82cbcf8650cd0161482d69e66a0f810daa72f1efd3c03241dcd1e75c03da07b79cbda10e06d68798706c9145d82dce0f261d3f1ce448f4f342038806ce3fc6428df94bd4325234a2f45299b4f168450ae38f81af1c5e128173891e021d7a87906811d2c688a8c188f10cff72e3324e0d22095d9a89a30ae570c3540621ce1f28e3b452fd31d410b7bb8a462e2839cb06fccf49086a4d22cda1e5f023d38e79cef825a4d7d9258e071bf0bdd8087dea034185ba7e86b85b5c902257d1616b7c94960b64ae8538880bb59edde568ef9208050f0a648bfebb15cf919e4bc29ad0ede506d8121d974c56b07b1f63759fb7073b7800e9e5bf895e7a825e61d4d1aedd0c9460acd391f2949ddefae69f451d0b3e986f5f8ae455df05a629f134e9f813c888765cf95046c6b16a6d505cc9170eee008feb0acf2498583a9be0d00d613a1fddc81b8e534185a79d7157bd50ab042dab11a214f7cb3e9b2373233a7c7c1f9a1f1ee4f7158ef559ebd4965aaa3e0d5e47aa407383b97a8cd5f13e786038e92d900dc84c86321e19133c261af5df0e09a47015c1e5aceefcfbd9d757de9d873c2ae1be3c36c5db5cf3b797d8d760081ea48e1ee12f8b67dea697feb474fa182e2060c8b538e16eed5de732d251aa0a4aeb90e2eccca92b8778a0e1f504dd4d75ad6e518cf6906d60200879bff98907a2325b509ebd5e5cd3b53c6e2397b22b7021231ad881dbdbc03365058a572504b589295e9b33eee2f2b0dfbd2482b00a5fb6dfe9dc8d54ef775880dc11674400bde795a8e574808ca5ee84a936e01d4acbfb9cc0bbd86cf5bdad7f6e090b020085897d3f628c2534362f770a5d20350589be62f77efbc7da311ed79d9d2f490fefd460271a8addee32b9fd4e6692234beab893bc3a3613f350b091a84502e908ed83fc164ee45870e27675cd8df3707ca09a49c26b208fb5ee72b3891a9593082c2933aba8e0868cbd1af14e6fb72b36af8ba1ec6c10551272b036fc6fcbf302845b3440c8d8cf3c079f368a6e653b07045b026c54ef7fe987daceb4725f9c00f2bc1bd1233574f93914bb3c9042d7c4b0b0c272ddffa95e096eb7b64441320ce36904b5b07df38d1ef9a22ec5b183fbc3eaffcf999112a16c23b8fdce5c0704eac81e503110e59156fbf64561b857cf39e156476e29ef87f4a6c08640fec30b7faf06bf4a9eb1e19afa39057bf5d2d00ea352fa0681d64fb7b8325eba168d0dbdc5d283f631b0c55f575f76328ee49c99a65e3e7a0d329d3c16559d1de4a602cc02e44f1852631e7e190c10a70ce79fbbda7bd8915dcbdd7ac1e23f6e34a900ea22a7cab6b2d19a6802803fd9d1765b8067273eee4b80f603bd701c5957040f81e74c0eca1b183d7e564bdafc9d460cbeaa740241f72d0eada097aeb61fb9082be81ba42d22f3741b5c4d28cc59b99be2b9b0485acd3bff88dcaa5cbb15cd0e91f601ba0eec61f606d8c70b87a0fd02b769619da91d88d9509dde0cee67d900cdff92781a293fbf52d10fcd52ddf7089c3a9ef8be56837e52018a7d7b0d3c06c7fdb183a424ee4e38d59cc7ba522e8e2fb13dbd5a27d9e7b9df116e97bd860aa04f8045d8cdd10ff8a4a13176f5bea2ac1f4e699f12c2c6f6072d793fe99302836771cf1f28ae03393b150c12273cd6bea973cc4d7d8599a1aa84adf3bf4f0bd4f37564cf4aab35de09f4305a3ccd12ca7c647278e5442a8ee61b1eaf9c5c0758d015bc699791c37508ff52835e1da95f55d55060c6dacb4350c32942e0cb0d2d8f0e1ddd6a393f8308f649c294f3c505d429582f66b676ee50873f174c630c221502d922650df39c8454ce394ccfb9df4e1b5563ad16f3ae2615fc6e33e508818e79b782b533d1eb5d79e929f1af9d38bc2452bd285e268e19bbfb2a99250d760a50920aed383fd1636e6ec4babb5c1e7f0e4beaedf75281180f1344934c056337040d33b411f4459b68020a9e8cc838af1e4f027b9f5cc17074418d082a0f525cb684a6a54c6d61fb3fd404e8b8c20a3c97802da110b58087253065cb150b93e3a27fdd2c89b7e269d708565354ba73b325d112bd1ebdaf5219e000742a03731a79df35c40b007ea43a6494c804614f12b5c0a313ad9f575e6e1a13ed3a0f692ad2ab1b2b1805c074c4512049d58f8abd80dd76afba02fafd927ad593d60001bbe5e795fedb3d5687c879953046a9636b8da6e5679553b05871ab1394c005de132a9e652a0cc1073146ad9be58c573d9a777b84329e1ad3ce61a256e05c0f8abc4803b2012bf2caa0e00048d9e3db981241d4802aa39c75103e8a91f3df032f12d55d2bddce8533317df105b4d8dae3b14371d13ff3c86669188ff2af39005fc3bf577463f492cb6a016d79312365111f9f3cc1cbcc14192891a2d6ed860064acd4d129e3f334c96074efd67c16863e93bda32fcd79d242a12d666a3da80b2937f1b966baa2a853a49e15a427f0ee0adc14913fcf7eb73743282c0a726402964b97d68c45baa50aa961508c3e430f44061260619c51ba9d7d0d67a434a4039ad6b26773669810af7b97fae7da6cec672a644dee16c308a692f4dc31906b058b1ef2d137e317a87761c18cfe7788af8de1038933f3f38232e18aa5165fa50aae0545aa4d5df4310bc004155527ff40dfbfa06a7ff79ca0c4978e058bc57f0f06ea87b9a75c95f34399a9300dd27b3bc379e1178caa6bd445a5b08f7e8cb106c6e81e9d06fd2057df5551e8235922f7a7cfe9095198d40b686a7971be7c7d058e65bce59cbd2e5ee05a8d45e7aec14cab8b5849ea0b6693094274a5303522089e2a4f6e79ec1868e5389206db372859777be80a10577a5d60a472b79874530a5c4bf979daa7c0ef9eed0a296681fc67d0a02419e6c9ae2053866b6ed7e92f054414e3a54c8df51ebc2ceeb10b26cb10683145af60f395bc19bf40436f1280012c4aec3b9fe42a51755c4f463f2ae37aefa7a671c38e891d5163d7efb0bc0200f5872abed6312f1c4fc160cfac84d5104f9cfb94fdbd2246a4ef08a2ebeefa0c1464a1a08b018c9763ad03ddeb6d6b0174844142ee62d803a7a3f07a68f58805c1c9404f12a06947e22fca1d0ad78e8b4480cb7b3b517a8900eaa66e525105051e4434976348d91fda947e0887d888968bcb4296776e8b5ed5885889da06e90b5dccc745e24ecd6de28a08ba6fef685ab2bee8aff6cb2538fc6f00a2fc08330fa6b357325eba9797023711e612c1e56f1387fae09db90a1b4a4054794e9f4d0ae0a9ec6e46587de68b6203eec353976246860faf9a60fc88b3f7c4191b6a3a056bfe51500bfb297c3e2f4c371aa9196de46c2684d1388a792b6e605034253c0c1bc7d0925ba6417c141c5ccfb5a424438649cd70d8a35ffb40290365de01270551b7dfba790a506e528a8282165809648ad15ab29270d5297ff13497575b170e214f2fdd82d0d6c69cb32a457f2057cfab1d8f159aaa1a99c59ca17caf9da8070d5683120e5e9618cf9e7f03e198ecbae43127d67c7a31549b5c5c978f7704031f101fd1ab5bbbc68f5af2a782308fea09bfb51ce4d60a68714c920bf337340e3bca9745d417541b02c6042ab9049359a497bc5f48bb923e05748e1f8adbcc02875bd792eb643ffaa485ad848a32168c87a4d1d4f95414b07a7fb07c74bd51060a048d6932c2a38bc827d87d1c6d9cb479c2d1049665a51f39799f5ca0d15d0458ac85a2ec8235cf5077b049565a678eab6c523531a15d233d50c9c094d3ad079e3d7bece0a9ad9673a77d95210a40117c8918ef62be61afc746626863bfdc0481c5c4b531d362a625da44120c0ad73bf452ffadbc1391d3c66d12a5aa0d430112ac8a6e2ab872bb81fbf3061167ed45979c76c391d9f7a7197b8c03825b660c8886c9888a17a622272997746905ade13e92774e5b1768e3875012a9d1f63d04e659c0bdeafe671f6ec4aaa80d810fa1d574fb7cdeb86e929e42f0f5f92b36018397e5a0ae76721d994a32da4a555a8e0464296f511a8f9e050e7d1804ee810afa5fccf2ec13bcff5cf7154fc7b5dddccdcb3cff4aa08e0fdeedf6d53cd24c05e3fecb18dc5526727c4202d669e7d6fbcd921a5cb6dba90887227f381fcb5f090ff84c43b7778e47bb5b8b1265321817f505997531471290766535624c2acc099fc28e97363d3766bbbda47638b958471c2075d48d84a863f930d457c5e76705fb2a6b243bc6929f2777a13c6994332a122f003eee154c24fd60f971e0fdd20e34805f76b1cf51809b496a79fb7069a542c05d3fcb107e1f2632795503a6a30c24b52b6b2abb96d3cf61d4aae158bc62c00e0a87d2c2dc9a797b8edc1a3035024d4fb500efd5fe823a379a15593c8d67993cb7ba5be34656cbcca8035f7327003dc1ad2942463e474eb6b9389ad1e6803fdde73925e7f8fcf683d47d8157c00c50239a1c4d207d9621dff960aab87dd18bc365b36a13b80d937a809c3ecda4051a5e5df9329d89b03e771a98ccf4833615f5dac627c7e89aacffc0ba065eb7005d542d8d48ae30766bbe9043ccc0256f7552d474569bdb735778d81dae3e32046e3a710f970373195c12c728f437a76d075ffbf6779a0fe7271645f08e37e7094a13376737fbd66fcad8fb7e1cc2a9a02ae25c66e0f03d6b29207f5b9c51a5067ff044552a82caa4260a34df846f6927dbe34749af17cb57e7d3c1b26a683c01136d66869da3419345f3fa323d7cab08cc1ae43296cb217684cd0242acc41b04246ed22fa78c7b83200590e9d4ee706ac36d5b764cae469022ec200340df9202985a9775b2c6e449e7baa996f1c69015a7a4381061ca019807335ed8c017e206029731585d6976022f7108dff3caed5718bff56f53ba8f109f7ca24964c2df01f10a0061a61b58da384953ca0465f4f0c0a4b48ca78f8b2ba1649bbba84d540a7e9d0f19b1250de23973101c0e4257cf5b91f1d8433791b4a6450ce77de5d001a1cf52e0a728bbd2df289a353ae8aa2f4fcbe78877251bcddcf8c189d14a1400bd4a65d1546ba357d1c46199ff7e4429cb8b29ecd446cfb1ea59d7c693d1090eb01c67e6d7a2384835e71454bdd4f6b151b27388a21a536f9133bedbf805110e814081341022f0e2a38fb646f1d9307978cd395a3d69052a427f40dbd41af40e045a4475d035c0b03c1a0955a91d8f691979afa99dbb9c0cc0d5b632c62f1d0b3923d31a37e8dd56ee3b4e3a6fb8ba89ef135ab5b347eaaaa72834e61cac320f6e72013948678e2858f39d29a79a9f0d89abf766e8f73bc03d0dc48aaa5495048566650dea62bc8a382b921807b5e602ce1b81f60d7170c43141ff49b848b504b17c5ac85f789fd9abd0925622023b5f03ffd409b57f5f468d5411493294a001d2da9e4dd77a2f730bacbe9a3dfb624a5ac8faa39377a7a1eb8d3a2eb5742004886dc84967f90ab000bcc5132e7487ae0b13ba7c2b226d622505600be9800c0b32b4de820e742c3358e75ff4330a3498fecc808b31a255a1cd1413a478cd0e0e985dbf21b92fb2ae1cdc19a7240e3a646a53102ce10c5e4c71a24b637a47bb037ddacb3e84c98f2c9d6d9b26d02b3a270be18bbac13d58972fd8e1923aacea0526c8c2a6af34ba6867940e58f7753e6d7181b1b6ec9d9bc5315108d43e0065041b5bca8b38b5d4c55cefd44a9dff77d440e76832fc951f376afc47e318452b0d1a0d71f26b57af2b7d2cc77c7f410b48f8bce9e265c6acf07d188d99554a4301a07aeea9d979ecf35398ac4fa3eedc02534c50f8e6841770818dff3225958602abdc21ad474ebdd429a6332d803aa9bd3fdcaf19bd57c58b1e7bab9fb9fa8f0f3806b837c259acc0d58c6012a75314054c84167a56e057515d1fa8b2fe5ed500cac8aa19ef1878d4dc5cd9f1b489a00754f60e95bfbb4d4aed701f70c9bc5202f19357d3ca4e51c2a58a163e2d715fff10b0d7288bc557d45bc82cafc2bbf10fd7f464d72a2a80634f3655eaf675755fc0e81f96a55f994bc96d6e7100c9970fbbbd7e875fc3588162194519485476b1aba3329cceca66c7697a15a30c48b40eb85769620eec4ed22db9ab7a4beeb196a94e4358464c39d7df59e8fc4852f9012a68ac10414a132bbf5ed54c585a9ae8e5d69414b4e4d8c044170f369efc0402ef1067f385fa6a6522dc79f7e1272dc2d8817e326a5dbe8183e4abcf6b33a4083b7f531f641e90c9c418644e749c9c564078c0cae8150313f0056da5bade9a0bac34c6aacd391e5c7eba8f8b9c9f5f562ad8a16e3a92c0f1974575d0cd39a20d90f7efbfb164798ddc03c0312cd3f7fd69fcb880ad1b5e73b78f8e61442cca094322c42c27943df2f81ec138abd29ceb7cc59e703ee881328e50f26818f5870e7e9118cdfd85cc34e79965750d74f7e4de803690b346395fa634f865acce780c8aa5289c8bef404a33d345ec0df8fdd8668775ab09d7d057d05a1d8bc103ca096e53ed546bd808915900746e38ba9f118fdb43e768c0c02d9d21e1cffde0c00953dca684886c45b3d34ae06b11434ccec98b85b33195b9ea23a7d6fef26351073eb7ad717d4d81574ce32e83e775b67430ee26f5838ba1964b810315106d7f05c1bd4cb51f5c893fc78a92cf2e711e39ae74683138a0dcc0173ecb930e11c8005002a196a236963073c8e1026066e738883588bc04e5178a04a6d2dbd256960735d218d4cbc5c42e68a5f7bb2ba529dd8aca08bd3d3393f33a3940baae3e850495dfd762cc5feacd8c146567f2ec17f86ea12f79c342dc5f70bc5b123b6b2c0b28a339dd26fd6c5343537f788295a16f23e43063424f7353d2524714d5415b0f32a2f87c9bf591ba1dbfaefe2f0e06b60a7e8c73848216ab32a335ab09eab90ea86e35405a07ee026fc43d4c299b7d158665ef55b5e31fa9a31aa149144aea0324cf58148ff4122583868123edb10517799f9a31a3ac6e6191b17927c9037601da5059c37d2e37cddb48643f25af43b9ac5e78372343a50138477ab361c4e90f1d83c10bc23a31bb32e5a2859a32a323673c4d839bc457a941c52f5869b2a401f22b108839c2d02f51cc6779e577473969fdc0a9e1505dcdd45a57dcf9239700c2f7c0931953f2d6e3d958af895d71cc83bd2ada24a287574ab9ab7b030c84066077576bc8a9ca5be6f43e8566068b7f3620234f7a119bddb6b714262321eb0c39fed73029b828ae4a0fe92f6d0dddb49bf7afdbe251107ccc0f6e42b320aa0c3db8cfd704111ccf7fb57e33ed71fae0f866b90ecf090043a27c0630f2943f0d39cda3158180f1f8fbf77da142db4291fbf8657fbb0a2153b1b09d5d9a778307325c6eac179a1bb6a54db77f33f0a055ac57a91956b9f758d35a8ed6d5756e051eefdc28cfa90541c8894a25c6bac132f50880275097c9b402b9630006b33b0dc3904d2f1f0b5ca838725911b227674e9816012386093f8b0ecd771bb615f4027c5e21838b6817cbb3335f5815a44b349baae068d4e38ba52f57f9831c90860e1c449665183f7a22a0c54551831c02ca83876f9f57c860f4b681cf63793fb8098a518875d846dc4fc9e2a2304213893c9783dd8c743bdd80764743eba8b2350f2e59c48cdd26e6d8cf8e4d9adcbbe034eab37a1dc9ba9040a1a2c0102074010d36552b1cbc7717179728abd519987c011b1318509dfeb60fd42c706a77023b05106fee3a72395c528f72f02fb3dcb0fd493f87c8a4b356989147ebe416784007a55457089945f608435aff0d0a2d18893fd810a4616d6030270545436adde809349ac3a9cf2c927ec40428614af36868d14d627cfdd93bbc5f6cf8a21cdcda0fe9874ac773be6eb5022ca057a7f1002b6b9e4dd67ae4029bd16d6bfa620e7e0ba70524683c7ea28c1128ccdc8ad7ca4e558027fdc195607aa1e2fa3cca509c02dce1864f338b8ae1a0e6c4d20b9261755c0649eed775ab774a8dbb56f82ea70b4f1d4ad58ab8aef1c222736d42b8a39be6353ca29981119a1a7404bb21d4050d0f698e272311dc1ba5eff85d07688f190e8709d008b047cd44ca90f716adfc00f345f2d648ef2acceed6499bc335082b1b7bed72b9989426f01f2112897a1d007af110ead6941cd742ef1a0dc4c90963407fdc5f10d8898acb21be43d275ee0d31737d92b33fdc5cac1ed8f5838055fb13f467c8eddaa2294900fc1cc808e40b94d6ed317a76f3788f0c857ba75a8fc6ce752ec3ada66dc4302f776e6cf8ed0aa7b383becdfedecbf608e3df224304f93444e6cdb936fb148ada29635087b70802eda172f60ec61a883b9fe424274396c919f50fd07ab0754c8170a7ccb62c081ed8bb96c8381cb68d3267bd6754b6995f69f896151f1633768ccdb30ce8ae082b337d82313bd5172b7cf3cdaae73a08fd4f2d26f1e96f140aa062362a02e2034246a8fbe2d5ee0b6b82b15b6caed6f8f54f539bc3b593b9d8ef43a7c1574302fb9872aee45ae0fe267a22aee24cdce5deab4e408919d91a8b53e75d95473b08c82ac4a5175aef72e5ce6d834c401c4f9413588702438e95775aebc970c95308912b3f2179764d1a6db3785f03df237b745f5356c5fb018801b42e74ef6b830258d46c290ce7f3d619d75e5110ce7602c32dc1541bb74e94aafea42f003110067513d74aecacae98cd1363871626668d3575bc70a8c05bafee08b5df24ccf30cc20cf10a3b9d9d01d88a2ec9dafb733c077fb90cfaa9848db658981188088b0208b3faf042e2b35918b6e8ab3f557b9eaa11f0726f6c4d672bc43c1f61be440f6989955bf71b92d45c9842666e9fa39f6bb7450355755ad2d05b6208b4ea540bf516d02a72db654b8ae882488d34c3772a63b121ad9881db19de51a6b11c0901874af81c08cd8de93d47cbcb3fc4faef512885293f23db9c1a78270b0f800f07a37f38297534629fb1e36eb31bb6457bfcee70aaae60d1f6c394e5848b12190c7598279433edd0e90eab560c442da49b8fa7c557a33db151b045ec941521f10fab4ad5a179924b52dae1fe703cd06eb83f1b4755f2f5436c8aa19117cd4419001b35c0b04653ad2308831326ade6b82bc05edd2b4ddf57001a3341fbec0f9307ed56a77740adc32cb20b7ff73a1ed20b0e11c860d857146de643ca1664a2b602edf49af1993946126b0523703f3d979f7ecb30a5f677b9a672118e07f1abdc08730940b4b8b2f7dd8851b3aa9a6c02894e90d3ddc1e4f20fe3be395144bdd00504ffc8dc8fcc6c3eed60d43eaea4dbf3f3614759b86457fad2ae4c3b0e031c0b54c44234dfd529514bbd76f952bf1512a6d84b5bab97a7f5390883de21aeda0792eb6cdb747cbbce8a71725c79a283a99a93dd5d10fef0afe2f9093ca39f310976fee51b6bc5e7b8793c74d75e6bf10e86e2df13ea32ac61526f25e3ad85b80fe260f0e874edf0b7f62522209676512cb3f88bc31d17d1e9d981162da3651a047a8c006d50763acd5ff9ec0f83f34176055e198fa75442e9366999c5932c280f8ac50bef654479571ebe95c808f91410a61000508d1833b98eedcd29a96cda04 +generate_ring_signature e46bda5f8d177a85b3d4f3f9b52ec30d3675e7239eb271e9756c1b9dc52d0c1a fb375e609a51a1a54d30f6554e5481a94158e49705233340357ad15f25c832b5 1 d9515348ed96ba26f155412e0b4b7fb190f48c1adff6d6f7bb0d68c4ebe432fe 70df55444de1551ddad38c3ba80c388032316cba280052447947c02151dbef0e 0 454e4c6db4545872d762426ade93dbb89c1340a249540451b887bd081d90950e883b17bd17696372f1662b4242149a2d14024f3c1a6667bfb0609cf42c475202 +generate_ring_signature dd3a63be8c1d0e7c20341ff51de1ae69be6ad9a4af2494d969de4b5c5a3d1213 bed9efb67d0318e6e2fa22a32ab381332b38f0894f8a8ada7f8b4372ebfc967e 4 066b617b0fb79775aa1b548de8757a121e7d0e38459a6bc5dd0a587a88fa26b3 193a48039163ffb32e1ffa931abbee6e1aded29f388aa635a3ab148c8d5e2568 4732c8e3a686c813b657069e304e06df196259ac5f1634bfb7713438345f053f 4f1a2284c22c27aacd12d68d25ab10b1fcdef99a708fe2903c30e185fbc99c28 79be700bc846a57a8404540767e0c3f22a32f812668276c2d6b9a2938d286a06 1 c20441c768a58a8297250b384d6bcaf2572d9a9b91c8b5cf3158c39b0fee800de9a77b7643fc79279a5dd6f25ca4490fe3706b8ea32fbfb4f98f0896a3e52f0112ec54955efceca566e3344e522e09f9a85f312dab24cc897595349f89e5e00082aab587427b2a4458fbd81531009b086b427d2ed28937d5cccc4f01382bc90605b0e826e4f171a61690cf92779b948b54f41a3943fc278ee0ab8479494f770086006ad414290d877ba9650d8782679246e0c53092c8b4120e1fe545206b0c0a73bcb1430e95a6ff86302047d5176196a24890d7d7c8e5da204ccd3554547b0abe99de271f9ce6b4256f733d744b282ab35ea86c6034c18ef395f7b9efeb2907 +generate_ring_signature 2a3fecc5533445d1ff407029a75e714626b2eaee3f6479c67a27201ed06b2b9d 01c8dbe8167151ed13b296b133178b80e39a38b0c0c38051040ade484b69e4d2 221 8b859d51a272d7d7f42eecf840f7fa9149a4dbd6a7724f6afa17c4a1bf26d8ae 8d6f021c956ea9a626077dd81b5fb6e459a370b56deb4f4fface816bfaf00668 815a27ec056a040e436e299d9632876500d90f3dde3eaf1107d9503788cc0c95 a24932b33273a1ceda8f5b32f270fc6e0df415fff64ad6b39017af5359242b6f 9a0adcc60772f3f654b3b417943433b00b6bfc27e8b8afe5fbbf844147e8c5ab 90f1e5a5891e354d4fd02205a33f49657a5de15d7ff4ddc31f39d8643d62aac5 450f08d11598d355afdb687cce5632cbbf31e3c1e0766765ac352dcaf036a67f 3866d2f6934814b1095f0e0a1f147b93c50f15716e254c9e7d74645a4df78e0e e3491b5ebfe4fd7c5bd3f1dd3a40ab2825bec7c4d827e0599f1145fe51629f3e 62c9bd5660490a1d2e9ff39215eb040f736051a242963bf2f272eb9d73fe132f 71e01607b9b65d973be08bf5983d862bd42ef2c3f0fd6be7ba33a54a5cb909e3 d561622d76d33431f0a7881258e4906e5774696441e93194eace2bede8d5d095 9e1c44bf1c3513bcf7ad2c087c0eb6f0d19ac9288a21838cbe629312543a7d32 506331bba887f11ea4ad8c9044e332c082302838c750f0b0ba8936b8dfc70715 a1f5054ef80b2ec1f151f60451f4c982280f128b8ed831095ff4479f2adc8b19 19dd0493e1890f8e0ac05b9fcdaaa0bd9688e2ec9f47e2432f02ca60992e4bb7 8d2df708b2ce601d1bf7d762da0d31681643d74b8ef61e061c1f67b8e88f3975 b765132b1552f0c34491edcb6d601849aee7557ca805edc5296334860376838c e4e824505152dc5a1800451cfa73437c266b6a0ddc9a6b13f8a82f2a308195e3 33be8cd89800df491acc53ec6eff8132fc3875a454d2fccd019fc6a3d4aca291 f7289f047047bfe7fb28bd1466daba43a6b73318aaf082fe49a9703f2650c54f 85aab4738840c444824cadc97dac6f1b738102ab8d495c5760ed553ec8cdb8c9 eb87a343835083e45d815729559c3e1de67d69ce50f0f1bee874a3e90b8701d8 c58861edf4520042f37e873b69016220706fed7fbb5f4ebc1e21b9fb5018e20b 9febb8ed1c5a2cae82c73149c0fd7accdac0451b8fdc81121b954dcc500e5dd0 c5825602f9ec7f01f1d7905c3b3e7a4671178863672f3ef2c83e1abee6788522 fb3fa6e53af880f0cf31f95a65b5181b4be788265f9a1c0cdd5c3f1718b5bf83 f6b63d638100268978d89ed0f6005d24a1b143c112c73a918ad27b6eed6f0f1d b4589104a456ee51feadfc532a6f52e5bca5eb9070d0a77a02624d774d4d2eca 1fc0934d38877661c56143af7277eb837868e8465f0a3351efe115d1f61a6b20 bc4f9dbd0695c462a8b57fd6b0af3974104bde49c6062b5c4bcd09615d028b7c bbefa2f48cf69f7b1df407805e23764ac7266494449ab9fdef0641b4f13fefc5 1c9ce4f9a6a3931a3ad753ba874fae5acd3e85351b3456dad3c0a8ef85028633 b378c247b6855244af5246a0760ddd1a703629716b16b50f2f7afa5ebfd57e01 88adc9c2bec405b609d7190671cd84ca990e3f2794ab3550fcf5ceb569009fa9 d063eb0d4f7ab5f0b7bff340644c61590014570207189c73846322e0b77f8392 7ef5e21552383ce730b75abb6de94d8623ef8987e2d382d85dca46d2f5b94324 7aeaacdda090144ac43de8e04075421917f57f1044871738ad85c3933a7b92ce 73efd0833c7b48c215c0f92d2e72c9584bf1a5d6f15c9d99e19a6f6808a9ed8b 65b8c07bc87e20fb9ca0fb72787720d7a6653b04e11d4d31f6c699fed3ceb5fd 250511f67f3ec1d43778d1c712a6b72090c1812349262f5aadbd37e6f3cb69ef dd168f71e4b227d7417b3a6d0c6dba72b1fe04e47b3816569f149ff7e736c6d4 e1837515ea0184def7085f6455cbb2ea7d957942dc3a67d8c6c2ac5c3f588528 be818f8f702a06b5757bf5620b6ed5c110924a73252d5818b4903036b38e9ac2 9348070e09c4ea8e76401842a74161a94a84a46143a1242e60bcd7769f8c10a4 775b7d0938679d7a173f56742e08d0eb911378e65e137548d9ccf8ac6f61e00a b44cbf01cc2e599fdce193fc44b58c7e9f67532e6aa0cc18a771823b7709f379 2452d9e33927a56350678ca3975ed7b2c8e6d4d567ad7ac0e4126b1a55dde552 75b837f6b344b941183721fb45bed9ea0f5ad890e05f465b4f0078b8c860ea30 626ef8f4b4cd5a655548792ac7acb47f3bd0f22411a91d39a6460773b8b962d8 173a57cf7b632d55ba237914c471a75ebd5023b471406501db025318dd7c5afb 6e50ed5141d31c5c71a32b01dff6c0f9b4d1fafcdb59e7363d0b0de0f6f22049 353c625beb6b2e3ad123fbb9a5b2596f38021bbeef7b5e0c10ecc9234f6d553f cd24fa2bd9e36f5ff131b44852c0bcb99d82f055792ed8dff48e837d1b4212ca 2388231a621404db213c4060ff612cb04291828799919be7462d9b0ce8db9642 f05660f72b5d1b9c8713a957604cb32b1713a8cbe32a3ed4c30f9f581d1b10e3 a9604a2437780030a1ea1fc481a7d2d538570c08f355696156a8313c4dd6e00c b759e38e1dc01c64386765f177c8aa429cee8ead275d6ceb7eb13ba22780e430 aca51ddfbfb7e7d73b15a6a620bd5be30c5ea0fff58d2952f2764ea5378f8e15 7bebea82546589b5992f3990f088c948ffc03551d65275de4cf379fe08a231f5 637176cb1b76867a4a07c3f4a7e972ae621c128cef42937a4f367b5b52688ae1 444b6b8d2ad3f031dc9884f1c8d33bf92b0f8e51a9decd2d1853a613a09ef956 d2cabe5ea3d6b8e8539f6ecea33ec239ece6b1d362ec8ace8900c7f5d32b18d0 12ce5eb61291c983d787b75b4bd4b4fcec42870f7f1cfe8d7864d955705863e3 ac72fb9e0b6a14fe14a7e4313c3df10a5f6a4e424c8467547d02ee590df993b1 8e7757946777d73b44177c05b9beae59bc440eac16f548c95931ef4d608401cd 649919d443bfae5542ed3d35dda9d4a8f5027f8529eb735ed8cb27ee408783c6 59510fe2efccbc9e071932f0a1d6295e9f0c23375af49e4e1ac630b6b2ef7ef9 bf061ea258631a7bf55c1bb2e782d745f8fef6433688f4a254b5871f1a59cc8c 37542533284da65b04bb83ee315c0aa265552d9583d7805f02a4d683ebe87846 b32389352f301af4a588d53580b2af6d76947705d45414e18c4ba8865dff0f33 a48963364cf079b4fe0128013c3d937ec06ac6d3cc4adb28165c39a37a0319fb cd8c533905d1ff627b1e69130e3758472c86174adb5002f0220369d51133aaeb 61e6c4d613863a1bfa1f9124380cce995ac5dd90732f5979bcb29d42de22eea4 2d0723b5254153eb2a17896b7fc15b8059d29e5005020ef172aa50a9ab7ea7a8 8a743f0297f0efe2ddca293984ee8d3db4f5cc461c2aa9a487f13494b59fa25b e6a3d6ec91a5aa85079c7580779b393a04c54782b61e0bceb0d0d0275cacea69 0df4d37fb5c42f35624003a5cfca5c3d6eb240758569b5d26f16616551574ab3 c9a427a5d45f34e865751f516eb16c1645fc0d34f9d3f29d15defd41c90d7c11 d611c94f2cfee7f21c74272603f669eed93b9be74f5e0aab045bdeaf02218e11 4a414aa192df1dc95b7bccd5352c3dacee9da798a587be8d0dcfc2d932e53c23 d432cc97206bf2028e875b0c39e098029670e2c499b88ef451b95c526bba2f09 bc4594e40b1c06c2dce163483efb045e8378d73d819d4364f8fa4519c8704a16 37e6a276d50c83cb2ab5a9f7ced57f2b229edd17e2c91fdacc32d93925f10145 290c5152fffc9d95e8dc1d815fe633cd79b1427d50c8db67e8b68aa2c4b98470 8fc57c77048100572648ff75b9c5d82dc1c0d0fa5e06f05afbf9f201712adc4c 0bded12b4809fc9ac013e84373728305e8464ef4c098932625294a4011450e25 b8e535e32f58d625101ff5318e30492fb54507ed21adce526c366d5d3e6830e0 f8377198a04f2faaeddb95b3184eda59ab45d52583bc8e6277e326817f5e288a 8ebb388549bac9d0bc58a50efa290860bafb5c558ac5ff97014e5e91e2b5d6f9 80d45483e6d7e55efe1cf485514b7c9b15ad69550f8228adab97dc7dedb77f2b 860df3068a952468d7de7b480ee3c114e2af6a1bfd57718ea70a532156d316ae 1e7db7133690a920da094aaf299bce31255ebbd1ff89019334069563d97a428f 4f729843f70c02f0aae4848573fa65fc44fea293545643effc5a0414cbd02b72 f6ea093551a609547278602ff331bffcd788474b51a4ccf1576e9c8f4c351d0f 0d1425e162b53dad767159eefcc68e3481e7aa15cf5dde15c560e86e9cb0735a 0e8af77622167bf235723615c1c3d7a819ff60db6c5ae32e80f5a2856a68daaa 1e59be38167c56a186e49d712150083c5c1dd7588433b428e23062de4221fff7 b43c12e5bf504f04fdc66bda7621af9e8660bdfccb2816075159ac2713ca3f2a 9f7666358a33abab5fd147f4adcdf57bb32e12150c079be07d8fdeaa9ea8beed 521a52a0b3e192d19c0d35c561f27f6eeca58ff13e58d529beee488b93d2f3d7 7e7effff3b399f59166c50e33f5cfc0bb5260ced230184bf8992be8dcf887b10 ace89f0a5fde0334b8c4cd6372814c0e424e477a71838d4ee4be6b063ef83011 c6f19f9441c7443c9aad2a73076f466f5e4f905ea61c3f294bc20b588959ee2a 2a635eef90451e80ada03b004c75fab54e15e69f569f9c3e9129f58c713f2b49 4e822815ba216e4e657c7a4ef7937215b6d62196d294345f321589d5dce6e209 a68901cbeaa21bee99f17aeda62c6fe986a2938494df1fac1741511c6c11cf88 43d3eb9c030f4d9964d5e9c6ecf2b639525d633187e11f0da531bab6e5ee8a01 c0f196df45d950b38ec8d2629d12aab07856874494c29f71c6881c1d9854d2f8 43c9afda75cb063deb114f4b37dbfb0078f8cbee6b8cb740b97227cddffa6a18 d75e8c0dd232a4a241372836d8f12afa216dc4aba3cca2f7238d37cdaa54e1a1 edf772170c15f645d820e11095642e27a7fcb2a1850d1fd08f3e8f17655cb480 9c47f9140a0615c1532835e0f0bd405ce7ccc8b385febd11a1bdf68f3e921259 9d73a698ad37b84a11b02b49d6bfc96929194baba505645ef1761bf23fedcf61 2643526f0e6e1cf9f2af821278710b7065f0cae0ab880e9058c329ffdfdf3ec0 bb76790fa787e08a246e71857c921d4748b40ac6e8451baad291e7424edb40c8 35e56823de9d39318ad185de2a1d4568835b54a6abcf49eecba5f97ec49006c1 9c705590ea073318a4982ce47e67d422e751ad1043f7510e6a104fa8a7a81496 1bdfd29ec07987e54e8c928f680cc028dd1672ee6415727a8d66639cef804fb6 6b389fe787a31b450f1818ffaa0db20cdfa9b6880fa0ce0a7c541a90dca40242 dde10545b8e2fd54ffdfd483c18afccd262e204605538588848ea1c0c99efb03 4a10e9ff89c15561cb1157fc992e6133281504420f132623b9de3174658d28c2 c05d11b8468a9c285d195c40436785b680eff853135ad3202d85fd6b3b71efbc 709d67e136f6fbb15c6b209d56c894a0012d3a3010bf785ec6cf46565c7ecaa1 a551208cb6283ff8ddab393d47d2f930957a931a70212bd55fb83eb03497ffb8 d65abbf9e83cd429a72a2c0b11e878e5a4de06c256eb6ae8b7ece0ee7ec4e899 a03de6ac90e23e9470213a21b251e70e74ecb35077f2485ebb2f7d150c1f79a1 83ce4ba9a0d68fe3ce9421f42b8a010c550fb6100c2f8ab008be42902f3c036c eb4edd84bbf5d3299f3dae898586c55b4483e0afe6f4c253a779b59a928d21a6 10b0465dbbc0d6ccdd8fefadffcdc6cf636c7d6eda6ba735718e6e0bd4ea0ee7 f77556104aa4371f176d65aa01c0c3dc4eda40e10df76beb443b450ba666c16b fceac9a002728d8175618729f289d0e9a1dfe429ef2b9758aeaa63990daf74ec 96ac9c4c4680607dd545ce646e48ba793e146988aada0c9c0581a41c7d9de1d3 0f749c6dfa9b9d40996764cca834443751a654c1396161ff146f145158946816 873a8cfa29fadc9e3fa66fcdba54a99c0d5a645880fe162ca31dab20003988f1 a9138ec342b3cfd9fb544a5e5c148d2ca59bc62f54d27fb22c39e6f6915ccae3 e43bae09f5a5fa2596ea6f61dce8d81f0b9d51ae509cdaae1a24b963c274ef40 7a1b36d60806fbc951852e66ef531c403925e9bfe7ac324fefbb2fd81a43baf8 fa159a7bfc0fd40d2bc5a70a1bc9852bf10bcdef8d8e2e1ca9c62161b719e1e6 ca533537e8ea96c9b417a07b70a3ec07d0ec256f1bd14e8b34e4d1c89f72097c ae8e7c402fa13e037cc53e9fcabf06811b17afa21020b647b1dd686834a2d67a 6ae329a6be8103e2e4976e0950fc024639b41b8bd921ce630667216be4359db5 1a5b42a72f462555ea91f9e43c52f9154feac0f2b4ecdedb450143d090b93d06 f6e54cd2ebb518244dc737487da1d95e303b6ab78196a6c9c59212f18824b0a3 51ad7925ae3bec9cb057ca255ae54aa59698c225e5a0293e09d0e83fc4e5b7df 4984ed3a617023b33947a5644cd4bbe8ca79b562a091f7f61080c96de880a9fb d9fae11e8d2eca9d88188fa1b5dd6088ce1875eedb85335003b8aae6abef11d4 e20fba3947dc0003f5b6ebc49a40df8c029408546659375d5b22974c7d3ed1fb ed2616df75233d9c496a1f0d130ec9bb0a318c0b05047fd8bdcf1ebc2edf62f8 482f06cb80351780d5d49898014ed4f4582348c4ad33f283311027382516c54d a59efdaecb58328250fe5e506a5afe36662a5265cb4782eac0b3839761e87b5c 8870807b28a03b28321d06f6296ecace564cdb01d7f37592168b2d2ca075c7c5 62a59c15f0a3dfb0c1949dbe156c2294db9f840dc3bdd4b342b8405aa28391fb db2b5cf69db1da72690f3d0bcdb621e1ab6bf2e8e40e1d0202dcf35d49904067 31aa4a6adbc872d7d62189c83880b5ff945bed0da282b75dad30a60790c0f33a c93ac37aea14d028789d9db7fa08d8ca9856c4b1c101e5f212a2bbd71e1784d3 c8bcacc9a538112417d16664b694a837e6d397b1a4de3aab80db4d326750cee7 a784aa9d92f6e9379e576afd1175a0e5b0540f3145173fb30784a830e85487dd 0a564ec2c2d56e504bbc96330a884de705a47a25142e44e039b915264d74fd3a eb71a630f352135a48b03da1d0cf9d96832fa4ce940e6bc1ff73c47371b47754 36431d4d8a33fc41e52add24baf42d705860d5cfc83aadfccc6e4fbe7c6c972c d16de71a298b57cb77398f2f0226415bf28602c439bffe0334b0d68905d219b5 4ffdcfe47d26d0d30ef567366c6eb46275989a8f847b39a8f30d89a74ebde012 1c1832371c681f58df93c6c4fe33292285a801c2e21ded6e8191f80800b981ea d5fe4823f4b5c221a275060de1d1bdf72672dce31bd3f2e0a474fa8c38c41077 bab9b85dda37d0559958c8cb542609ac95a5963fd4631ccf14a6535d320cf9e9 15f589eed4f67eb0593ac0cba466f4b1be1bed4fca49aca54536a4a24a77f29b ed67d0fca43e7dc33c83efb68375c3a19fa8eb4536c49acc27621ff542e86ab6 ca9dbdcd0b47bf2adb0e773a0cede86e7dd2aabc5e6b95c2fc3c1b8c5210ae59 5e915792e97503b657a9338082f5ba9b24e2fee4d1a2938a3acc902e3dbf4997 d633420dafefbf863766da7c63606d22239d6548894e26f06d891bd406b21524 cf4f849796892dd5adccde35a74999c25c6ad3c803718becb4243ba6d8c7e1d5 13167c9c0793e87af248369823f245d99592a43c60a15349d44fe233c5bb9f2a e183538bc5ee8ed570eb66d393fd056793e269b109043eae5c01a0a090d18b3e 276b50ea1b15f2a45809d0b22cc7834e406c1c1054c0c7f7d8525c32e10719b3 191e9285d6d24e95fd7934a09d44920af2042291e4c9a8586e0515225c8413e6 de9d26522bf6f4ed113d33a1443c5f160de08691daf09272e7ea03232f4e6e84 d333341edd7b38a52be376f8287d13f89bf6b01dea8a50aceb421d9f602b05a5 b8a0364136c444f623e4dda641be4088ffb270a4c455c6e941d341681c6d6d6e a62fee4d42bc6db241ff2513f2540f6c7db4fee24b129717e3004f95960397af c259025eb09f04eb7431f508b3c6999a3418bf6bab8af9b55cb7d3fa4eaedb87 c27c59ea6664edb562515b3f4c71715483aac96657e26017b054f01777db8887 f91c66dd8483e994c9ebaac3688076e8797d4d67668909cb2192d279d42379c9 9f58c7886468a9c83ba1642052eef85ed2a45ce928fcf10d173f4ff4c89dbb44 4653a8407b6d1cc000eb05b1b840dd14bb6188ef1d37a8ac72e12217fe166472 93b14e264350b8c30798d2a39b9072feee77dda2b52e375196b3106022b4bedb 5273509157346436df1b7f67faeed0c0a43e999f398466c246bc2e9286b87459 10017a84966ba1304aa896dc9fc5708330f19054d064687b39376a9fa3241fb4 9d9063ccd0d2d540300637243750c6bc652c1f119651f81140e5322879d82f8a 940eb9dccd7636d1f891ade6e4306521419fe57030ad7175cb3c8da0fc72304d a820c82bb599fdf8d086d39acd194e39f132a85f18d8300f4cc56a8220d225c2 233233f3420506c6227ac4a72e37a4bc907d63b291a4f32d4123a7da47159514 7fdbf1d6f41f271391410a05f7a8d30e55e1777905673b8d4b3d5219a47d656c 2661848fb57f9b929c0982fa6fb2ed2e504d2d44448cf087952dde5ef2ccd758 07621d43208d862972ab719f654d757ceccec4f52dd70f013e2cddbb6119219d 53f0af85e186307dcff8b2ae1b87f9dbfc5250f5bfdb77f36ef3aee934853150 34c6dc33ff9b31f5cb61c6cd9cac5a57494ac89ff81f6eb768373507e2331f2c 9dd70bd2b9228331231be2a27d40d3d9a1bcef5c5065ea456f091d554b47e5ce c3f51b37f9ca58c36acc1832c7a0ae39d1a4cff8fd2f754d3d05d546d2c0c561 362b58b15e123a74435aea20c0c96ddb5ffc7413018c4ff6540b6c3b941d1b88 445a8726a0fe3b24c15b4d1df672059bace0614e07f61b3984a624b9eff63aaf a49862a6574460fc5e9b3432da844458d407ab51632f28b02aec0d5af356937f c9d3ce57149377557ebb03ffd48b85bfe30f5fe44819e2d589ebeefd05f4da1c 9184c6dcbadcc0425ca7f69ed9a5f120cb9fbd9126c761cfb10307c90f75af22 e5b3ae461948bb71e5d5c1c408cb68a6fc1773b89f2a16fb06bddea5bebfe84d 78717fce1d774e4857b31faee17551a3c70a161f9733b9e82cae3839de372e16 5259c3ef1249d01c1b4270759a51df4e9c04dda8a96eebf5031b9b49ad47a5fc 2c0a3e8f2ac581b434d56652e75893f86395d8e34e0852ddec0496e35e286b91 3b2af796e8f040399653cc212a709de714d5259e74869c8ca1879c4c35b16acd db2975a318a9c2e4e8fa5be0c62e49d88c4b6606dc1d9cf71538c4508d68d92f d5c78555d2a2a033d104c868c3d908855247a0a9b31047b29d169b1be2eb20f1 d1a7e0ab08e9236ee740f2c42c57212be40213c9a5507d1f383d1c8371b47669 f7b7d65be98a75555b03e382aa01c790f1952dc8f8b019a34f2d6418ff64466d 773bc5294e59c1a624f89f25b359df72dc603234815e17abc0afe586dd33b264 25806ec523cb87bd4c3f5425a78fd7f879978b9d0a5bc2efc4db3a80340b91c5 1a0d6042f55fc72643c05ef6bd3a0c3a05589780b9c37779dedfabfa12be6926 4474180e2cb3f2d3ee664c63cda703a82d49c0da75dfdca8aa10c173a85d04f7 d2f254cb36b039214a42b95fdefdb45ccff86bcab3f38bbf3f06f7bd6bd6c324 fff1eb7b3a6e85f22fe1cf95d3d7e28b2c750a3e711b75ecda1f21279301cd7b d82cdfe7dbb0d0d15a39d392e5529b61256cf6dac2ca0c04f10c81feddcb5502 1f7426796d532dbe452a65916e56583c418ddab1d655dbfe9e4047d5fefa95c6 acd10a2211b93dbb4ab82079d09b712f7060980023a09ce67440af525afadf04 190 91dae06180a32c3b02c5218eae4ac14ce9c629a65fc44ff15afb2f50f30f6804649b4dbd8f7c746e09977f7ffb6b29b3a7e8b4aeec0cbc1f9db85a9124fd08099dfa126d9f9335773b043a888f7438836e105cf31b5b35294403ebdab3f768046da009bdf6a9950e0735f88ac3d33bf1a8b38b109afa3830991e3e5a708ecc0b5dbe69c6be585527e96c50362a69404b81c18900aabd1512201207906036740129a050819c2932489c68976ebd621614b63797fec6dae88d1e80b5181fe66706ff0ec97149908b97947f5ccbd5298c2ed346176ab7adfd3788d277bb403b4d00caa8f3fbe5cffdb903d9ecceaf81e5db0b0151d6472b5e1c2e976ae5c89a040fffda036ed7228a7c40ccbb6918f270b227b1c821a73fca4edc92dc486d63ee004f1ef59f87f491ed3fb801d96b499bebd8a794f8c643e14ff0c0061486b58a0b100c33f4b94e26912b06a009bef9a6541b23311b865aaf1a719844e9f9761e010f77bf3f76c6c983f88d2e8d31d5af52899fc1c1d37cd879109781937b0a240ff7c791420a9efd7cdc75f2bd4bd78cdb73d81d3b08b0aee6228653bfc12cc908d72e70745e29e0f1c8e13067d1d02c2a680c83dbb8baf6a290fecb1e70bbea0bdaee2bcacf997408db98949ec0b36e29eaa190fce14a510ea2dbc6935daf0c01fdf17211ff4e74e7d0594f2d9ba5b99c29ca61a0a4b29c1fcb5035cebd171d0e43eb63200bfb73b7a2e01049a5f0935829c03e5333e158cfe40068f68ba4f80da446438ab8fc425f9a43844c085bb7fec6e5cd47f56b15d63ff26b3b79542206f24d3445cbc746207a9456d3626bef29bfcf8e67316fd2fdf65cc6864de8e000add13b62bbc801a87ff46a2a2773fdd883e463e1e34a73c6d7595f0a09b4ae063942ff1fb61d3479f0a1c0add58ca93edc7bda90e1198b5672802d9093c6a00905089c50c440f7dd9286bbdf24ef57a4714ac09cbabd7e6a19209b9311d6650ea87eafb06ae4a8125bb3c18c81a7519de252d0edb0651364a4499c19a2ce5602e6f101d0e1c5d4029e2cc94816fd3cc3c7cc19de8fb6795082d55e4b592d480f126b5ea3d12e0ca7b4856aff1707a19402580a79d5c241f59c0956c15c96a70fd3c54366537a68e5c689b934c1ba22955e65359ecd9f5812f9827f0507e7ae04fe6d660a566dca1d40b6e8c91adb94f3443320bd5394ecf9c41d4df136896d06f3068bd9ff11899e62cefbe32968127e19cb4bfe19e01d100c95f253315258084fcc6c02d5a9295b123712de898aac3db16be9ec5f4447ff6ba26558856cf80e7101a8909d9933d8099b42a4a80ea1f1fd2dd65d1d78bdf67ea247a64f6cec05669d75f76c57afc346ddb6776ba53f41f65fe9a1f4dff9c8238710738121e006529e87db539acf9df114a9220e1e5f0e2ce25dc5ae4dff504ae3dd5aa7a81901a73e1e7b9d32c9c70d5b81efa57b6c259633b1ddc9ef98b9a00c7d341f5dab00fa30796922db48d929d731a713447927059093ca4f3bd9e2fc07f5fe4f873f0bbace7386c733fae904e0e2e528efa66c9e06ca5e3330fd980c41fd72bca38e06520d6f997f6f6bcaee71602b7a5ae2f35bb6078b6bf6d815179d32f723b8e40f4cbff32f1a32681aae630fcb454e9e0921921bcdac9b19dc814232040bf7ba0be80152bda1b084f32312ec0b33f0628101e5f2fde33ed1a47a170a233f988f0c5c4e7c069c66894d8ba3ed3cb9ec47d5d7010ff1c162484376d1595a09dc7e00d85454b69212351e18ba29afd9bd672867316b78b34192514ad87b858b2f5b03379979a57c3deb37fc8e437354956609ff384a5f9177f9f29899dc5cc3647403b4f3f92942633888f2ddfcdc636463a3dbe0a4bdf8b37682d4cfa2bc03a1af02b243b3ed11c7e56bcb8518d2af6dd97f52143b64d3e003ebd5da95c14bf5040d464ae3d976e7bbc143f87fd63928b1cdafcac2e65d5ab5a4e7c063dc576fab03920bf2310814694920ad659f87290b245340124e5a179ff7e8e1979383e5da069f7458dc3b9c47917d710b431d26c3884d37c188a9cb3f9638ba323fe8cbd70df3fff5bbeabb10ed8dea39176b3af732d56c2acffbe52226557b9d304b87c50d108cd063f135568fc32672dbd1986062ca90724e54adf7483e5206240df20f0588bb84c60859b8dbed44dacff238cda8b9ef478b6396e096485b8d52601395062578d736ba6ad91747d52e3f228f19b6eca84cd4c984190f5efcb3b3924ce60478df2e20845626432db7066dd9ebb07836ff742b8b349760e7392104231a8c010bb6e5db488566176e337aaabc15b4f9d963ec59f41f91567f7f5a2c5c625003a8ef2e0d4c0b471384d4ad6bb561e881992e74d317f8962cdfddd3afd1beb90d16264e0249ae0c139ad538be083aac8ccd8e6671f3972fc8942311692bf7ff06721a7e0c4ae333e78d366eedbd3378487a4f4f137ea6eb4f221f5f6720fbfe0f38f7bee76cbb6a997330bb4791e244fa6fb9a75731f90eb812c77dbbc15ffa04241cf3de420b6e8b2545998109ad3f35c5b5e50541116c609e6f1ec90a957001ffb794427dc5e4dc913a7043240c27a3350623bcae1f431f6f8c03ea41fd240aed5c0d77ef9b6785e866267d8b62599933da672ce47fcf2e9ef796b8a87fe50b1b8c4f1d30e4f6c103367e0a20a53e9ec0a1c1d90001f23fde8bac1363dd500e8d0cde78240711c2154baabc630c5dfbedae5d1532d5fdfb78d2b7331791e601eadd7a2f191e6061dce3f95c8490495d51b06f13cfb8a2086b0e3f89b8c8150d4bf14c576c1cc8cff43835f6154fe91b2df67686daad2201cb59669fede1ca0aa0d4f554cd58d7fc1ef49669dd20e24968ed73a92920482ae11df6c00eb6950437b49ca3c12db5d12bc2fce95900b99dfe45be731f9eb58c04b928d3b4a38809b709a3f34552904b14dae710ebc4f85274694ee77d7f721457904caf6a3c9308b4bd79276abeaf4ccc26c98173b96fd3fcbc905f787d2efbf9228ccb179c3306090e008d0d1b1e93a9414bf24c25d3311a7cae586d1f30c6bc5f4d2effca6a09941df962a5cb2a765663232e6720171174c0caba8772e8ee4b619e9025f106017e4fb3a6c32f7c902c41ddba24630f64b48ad42f57c778918b17eba9d2b853081f18f2c8d4e6b0fbe1d2ee08c1b6ceba4761a0e172ee2c940fdd6d0a7ea576060febc836130cd04f3e54dcbbfa430556e9fe547f59f394e565c96d5936dd760c02e61a15c1fed0d247bd33b57353977afd85f6322fac67292ba1a6b4f768e802a6f7b248d236f8abdf7ac55ee0a107eef738c31bdcd8d17e68520e151582bd0789baf0f9b6c37ac95c0dc75824b9b1d091fcf9e42b80c5fe8ec1c35d901ad506119e56966b63eee2e7ef053452bd66f5059ebf6aae5ce0eb0ecad8e1758989006f86ca3d8d1c85897a09eaf95f863c9200a6236305ec79ba2fe9434fbfeafd09fcb7d0fdfecd767a968099e1f755d085dbe36b8a247a009e654488438eb4f90ce71795b6e5dfc2e095ee0adf68338e8eafe6a7842bc8a9df00c945caf85676057d9c4cdab14a9776f1c4e765d30f637eb521710bc8462d568e6410e4eceb3608751877fbcb2b2bae5247f0094bfc1f675e160255919e21275ecbec348d21bf03dcb3d184870ea5b214abf0a59e8a81fe0971895b9fc8a9a4c1abfe7af26113083423db4470aa9c5f2a17744c1ae408eb84fc56e9988af5a3816c36446f041f00fc3d732dfb776c5ac88c82fd914641e608533461a6adeb90d22d6d20a3c9c90c98eda024ee8e7516b0bfaa4987662eb804672895cac25483960f010db61e5402cebd00a1c2a212d1077b33cf684d70804523349de60c69627d8d208b8ce0d20276d58c8ad7600b9d0ebc05a291fd9a7b697845e9a4db4123996414dd58ff130079941f0b3f6ea62ec2f7e6bd2bcf6d171a13965df1b78a63ffb9f7b4fb07780534bcbbda694e23948855a64a6dc033fbf9836b3d2c8c9e341dfd5b098621bb0d9eb1915a7c15553f9f6f7322cd57334ac147ea26a32d3f8fe8b3eeb59df78600921d7f37df0a335bca22f6d6033036b3015df9b85c704a713959bf7b123ebb0b26ba2149f292eded1d35f269f1cc97f5ed39ad726d2ffcf39f01635ad365050e2532822ccf263df7a37f57d4a92d398e3c69aeb2db4333a4ad8a31289e602c072f88c7deeb21faee4e2e5edc8d44e6726298971ed614f22a7d6e194ddf94380279678a5737bc6c66757c35ab471fdac89dbec0038a8ae1ffac103c922ac9d903ae97d68692aa51fa3b0a124b737949fe6b837446e14726fdb37aeb8d32d6700932a8e3487de282779e4acdca23c5fc6d2815b04680a88ad77c7f107a60096e0d900094cc338ee81c6c9a31d3ebbf45e925ff3701297d0b2e6bba4a8a7b392306e588b702ecb907784bce3f0d3925499fec8ecabf6ab3a1835bb9c0e4665c850c399cec505ccee4f8d3e93a8885c481c4e49ae83dca2e8cbf4eb9c731717f740a0fd06f1923493c076647bf7e5f67aab18ecbd6e1c5aa7bfd11638f3e3f58c40da3e2503f4f2d6e42c6e59854f0908ffb1abb35f118ff05cd38efeb020236c90f06b4e18c247d2a4ee1aa5f56055957cf8150ab2c0dce289cb8a3e4e8574c14059dc9a121d7875d69c2cb42dbd7e39b514b699d8864310cabaccf365e3eb993048f84f60bcf2467035c7e093161e39db898470ae91c85da85db40ee85cad4d702ade9f6e8eb2afee1f69bafa935fc89fd79593da0203764b2e91846593d1aa6053fd0f087874fc1c3b810f28b23c2d16be52d9e84ea33fc49bb730731e283400a9fa73b07dd605bc5e3e630ead6c8367742050db8b73022953d4c36ea2f21aa00a2bfe7ae0f5beeda4dd96132a9a84aef3b6b37bebf67da239e9dcb420b014b078a692195f4fac817c5506ed9c57dca7c1299876a7fe6d6ef1a1e54df9a987e0ce083e5370a0e920a72138ae8d67ce08732389bbfcacf359264d6aa07345a47009c32ce14458e89d7a3dff9000ab78bdec0166e18537ecba2e6a452b4de451d062066fd747e158cc7e9250164e810c4d1ec397f63b68afe3a5877a81acae32603b0ab12da9e0242c2d641bcb3108bf6c99702d701cd371f6ec7361fe9d9cdeb0854967c62b2702f8d0012a9b3f4fd8b1ccd49112213d85f7160f59e17c5df080e8329dea18ef49c6f513e4c24b94e62fdd82f91373bad888326cc0f818a18e009c4fdf30e59278a2c2b0e8ffa1539aa8aa97305b52728d3f329f60b796c7c5f0fc8b49f9464658433767c31c544515d8a321a3eed8bbb16007f4c019fca010003b1e0219c70d54d3b38b2ee4778bb6b677dfa61bdc7148964d39eeb7fe42a3809d3b4ba22d276169ebcfaa3634592f6aa7affb18fdb31407ddfe25b7cd910c109c125f9a8be911fa2652fbb0160b1573bb50dc86700509984c57e3fa5b07bce08c8a5050a89325a27c77e746693ac5326d2d7e52a9483115aeb1097f9d3c15f04bbc1c22d855059f3ad26c336dddab464b54a6d551f07b6acc9f8d56b103f630a6bbe13fc8d0753efb2b0ae40beaa06b009df2a7f6ff59b3cfea5c7718989770cb0caa371d9bb42961e3cdb9df499f1b415e659c5c81da41cd115afea47474204044c863ad26602504db19ccb016a43a7e222626a16eb5a0ea55576dea2c2790fa6b2ca68b9383c8d1fd2162d3f15f1cfbd16fbb8171d2234d2ef54b7c81f5307bf8dd8c21e4e8f2cb0544dab8b8c38e01ea90e133a7b8bf3ecfedfd976517906c3b9a213ae7a1ff8f0727bdaae386f9f855b457ee7ab219904f644804d21100293cf4e30e650d8d4358afb5008f6b76294caf87d5f6f21b087fcab93bd4fc109ea50572841fec74ce6bb93d540aa9977f48b74b52d33cea53513033c3176e60866a8aa56d33a3ef01cdca4ec142667b7cf9f9bb91aeeca2db5d0942a80160e059ee319dab36e41269c2af1d95ee90c75a1905e274b9a46c78277fdb6d7fba30f13abb9f6a00204c22f1c478de8d537fbb7d7a419e992c9247654f13e64a11009bb2f2f58b705f7d3fcafd5f273d235ad22cd5a5f149e637624810f2a7d15c9065b65ebfcc0108bfb281377eaf0465a9d5f348283e06139e9c31692a88ebb6704867750e20b49f577fdf565ec146a8627deb36137ee3cd19af656c2fb6a8963080118048382a852fcf6d7b63883cf7324dde6e80cdeda1073582cd7f589498104521c71b8f12c6c9391b632a0288077ce2a17c039e517880f9ebf1a54fd58de0ce25c0baf8bdfc83b272864712bc50052317968ff8d0fd777eaeaa36d51cb1805690d394eb19b872274e73d95aa9e1f57d5af90afc8de74d10b4aa27b7e7520051bdfdb548d7b881831f49b9bba55b0f79030c99c53386d9303bba4323b5fda07fc5f61420c049abd3a4d8539276ef14c5b46022b531d4109ca4a2c972912e707925cf05655af9fa2efd347708d4fea96e5330a70a5add156e9908c657fb56909616570ff6d756e41c9c6470f19e7f6c1948f159442604ba5dffdedf32866850db1dedbf17f4ef852ff232c126c566de1e8e10fc0a4d544733135652e4b7be601d9eaf2a2b25146b92506074ecd91d3826606bf63cd80451ac851d95dd0c996076e8d9e8dc544e3ec75151926099169f9fef5fa3c962e534607fb810310c89f029c1aecf615c3315fcd0b8f5498f7f102bb207d19433ecaaece0c6531372edd0801885f9822ac577988db7674e4de1f2ba7966527f24b62c777f41e9be516210f10ad775cf62753d62ac12033d4a415f65d3fd56115ded54544deb37a9839ae00afa1f722391aca00dc92439b14a814d801de52c95262f51ebb82d07f6b6f1b075fcb52ee33f6cd9443ad27ec75cf685002e06c8cd1afbf0be2f7b35330fe5f0ba2cbee426af9a537498ff7f6bc8f28233e8fc3db1ffeb329a907eb650184a705dcefd32f6ff6dfead03dc1c8c7da9bb4eafa4cab334a22a11be3a47676f4280d83ca57ea87fb46f5882af55b56bcc5ee6025fa4760ed2e74e94e9e5ca56ed007828910f35ae0c35b4da2dc805f064501872c4e5a2906524bd33e79817b28f30e29d29f097340f2121e2f27c48fe55f3ed4d6e11411b744ec4aa8cf6ff0078809a4e92071957baa59ca1b7a8f80284ded272c12f2238921b61f591d6b5389e802c68ac968b598bedad618d5f47d692227607dff4d1a47ba61ff6ecfe74ddd200644e681f4c35a164d7f56070ae61ad807186ae9874f058c9fc5a2d8f53e0dc207b9fd3a106414a8a2daebecf36b5de9f057d20ed61143f52b1adde856d909350c1332ab7a5b5ebe7300d793ca298212347a538caa2aa174b11bd66844bdcfc90ad42c371483d832d49d2c17f2265628ff483e88bb416f2c0996638aeb365ff6051b56deae4a8ae8662d20b721cb81fd96b999d0fc594ef0a9bc05c1c98da3db0343966d2630a8a96480fa6d650061b636a576cda32602fb9d57b54ee9deaf4c036678e082851e7c88f14097a1bc2a87e0d624f4fc9cf7aaee648553fc1d94580ef565fb436a2348da9e153e052d510c464e9adc72aa5beae76b65668c2f043807fe658432492c64c4eec2d55ccda78ea16a687ed971ef841a1dc6869bea84e603b3ab9cd290f41c3df348c0856b0b52feb35a9d2688cc7a3d3dc2f7f401275101d73828ac2a674b879e250a5248eb50ef970f8b76801113f2bff003ff5bd90904ba35c9c0f2928c3ec58adddc5e1c0ba50819ad468b0fc8bdcd76a53910568302c8eb13fd88367618391431bd63301441c55646ab6cc2eedc05e233de3a7ef40fc9f873b4d36d6a6839702f56af687f20d0139aa616052edf007edfbfbec0270febf061fd4cfca4e4cc5cedfdfa5ae3b0f60cd90219ba23b9963a5ae4bfba2d0a11e697d37f2ca997dc723c21551083690b0dadf3a8eb6cdfe80b675f57fa610f52994497ae1e7213332ec3a05049db58b825f9ac0df696e7567249dbe733e40e13b0bf7a574bc7f344a3bd75fb92acde599ef715373d7ea56ac16f74c00fe50c3bd54a9850ad2281a0de9629a8042feb256a0fb737ccf9e5e7223ad53c4e7f0f5e8ab2dc6720378ce8f8dcaee9d430462a813a6d73601d65425cf3c72ce83f087cb1100bc71a5e4863e44877b0db2eb83683468a5821fcf5623077eacacd2d000bbb5669ec44c80c47d44691f03ff5492430edd26ee9ed9c2610a235d36cd804e6aed960e4abd542c2393d149b4521ca2a11470dd8a660a8782f167e87d3d90846c330e39507ea7ad281acd0e0842c0615f953e476b6aba89ee157b904fa6105dd713307414d80018002cec2aaa20f70848e25c50cc1cbff7f890811b5397405d657b8f71ceac9da056102c67b1d506da30e8e703e367e4fd3f3b9866ae7a40aaa953272b2ab61d93e6573f2dbfa34a2c894a925ea42048a5f29d4c36531a20c3846e8200f1e7908e143fb738ba457a39530bf3264969b56f21bcdc093863b01cd9ba4b97fae73c626bd1bebba775d9bd3410da5e7ff35c161d0433be4403600a9a0e1abdcf6d4d9fc2a54b7b7c2fbbd987efa816f3fa503b1783bec61acaa0d2f44a73bdb0ff675c6f9ebff8a541595e4dd1c3c8f9f77f6a46bb2e575e0b80941c88bea17b67aa047171ec0723f2a6177f72e90442309100cd2daf3a8e91b066bf83e119430d3925bcdbb033651a31def9e0ef1b278b2134143d8d43257940cc70992704c00231bafa60a9ecdc893698c62653c6c5b147cdcdbcd23f7023407914c502e5111df0b196aa9c20ddca9512c3a66103597d1bb36b5c1abda9c9008f994493efc87d1efe733f521fe415765696691cc26f9a6d1ea4100cfc1c9b007f545d66894af9afe5339200927cc93abbb5ae09e4dee03850d141a8291d88c07527d5788bb4670a3016dcaaa1e31ea5cc8708d3ccc1bbd6620febfa220e8d509f9e134dc6a91ec51d62507dfe8e5dfa291c5234709e4177d4d1c13107035e50bb70cf190f4e15491b56196d6db28c2c86e7cd1aa908e6da422ba49ca68b3330fdfc2fc547a7d853068e2b43ee434b96097b42dbea15bcc3bfb85c25bc5160f0d53a226bd44f3c2f0aee264798307e579f1d8c9c16f51d353e7da0722066fd90dda88dff6a09b5bf860c998bef0063ae4c26112d434d72913a1ee89df7eb97606886116d9a213de523004a7e817cc52e61d978caa6b2cffcc43443ae190fac1010b6d73517196230af2c9bae8efaa0b440343edb7e02adbbb26a8065df63dd60e52d9f64fa4a107bfa05179fb551eb245f399dabb80816aca08266d0874f87b0a331275a71adb02c515eab7cdc96db6d83d5fff17151b1b7c87c210814ddd2002525d4eedba5640f0a3447ad10bbeaa22c75af875a6c9ed5b08f5a891138cdf070b33d2aac362a6a078e8ff0ee628810d5b1be449d06e0e0054c39a59aef01107ed271794920905eb30a376997a1cfa6671dc172e972e0e1d4ccbabc2d7994e0aa3aa7e65563f5546749671fc0d91789d75298312c45298cae158a30d5da4790da191ac1335ba4ceb5b4f649c093cd0c1929029bba3ea62613ac605578a400a04d9d88738e20a44c8518def60d06fbe6bc6d8820d724734dc7535de7bc95501045cfa791d55fcfafb5fb6796af6221f1ffeba3f0bfa2f867c8d773a15bbac6b059440d62401ab2bb315ee3fa2419e6ee57f586d60849e5f3f9d4c72ebf2902108f59233ff89bc30e2a8752163464db4817080711d54f58fcaf848fe2d0a5b380bb74bfc580a047bd6a46232a855907b41edaf556df8ce6de61102fb4db7813308e164104f51ccb9b8bb92a52c735e820ed703a709d19e77b04c5e3bba66d2a2030b305aea6ffe10438f6307efd7ff43ec44998bfb91a4f6c9dee1a826ba053707fdf442cbd7b24265544b42c3b242eb6580c3ce47ff8897648dbed8a187777b0b978a31f16975ccd0d0a6a44a6f6b63070da2dc155960dc5a906107101d3f8800794862402d395a0ca2de95d7e4d7a21ee69706e6ebf312e713d9b395080ea905b79c3845745d4c382826db160faf5d3d4bf87f8621a2f2983cd6f72961cdef0a26e216a99b499e351daf4b803db95c6bc72b57a54b17db5c4a28fdadb3d40d0d40780753d1f9e7fe7933ba267dc9399020e5c2e3c623d2a49a7a50513538180402d6f21e57eb71f8739efd4269d80b6396901ad8aa12878f7302a3600d3dae0930bbebbce8948af3aba9e2066e70b79afbcf5f3e036c5329ca4447c696e4af0c8cac596607a20a382beb88598e1c5544392b7eeb14734d1baff9c00e5d546a034afdd9ce9b2cf891a521ca6030b377d52f1790a5b30f3bc06af012ec26df9108e67a76dc1e6c748a35fd06f8ba5947a4c4ee894896345c0bbd7dbf1b632bdf019bdc4d3741a904daceab5df2f650fb419c46483ef9b6fa02640c5c80aef11f0c1b36ff945cd67dc78da67e90e7003d95e1dd2f343a1a302ce7d91d1da7c26600d8e0bd5f66eb9fdf11ae5871908809afa33a4231a34a3ca603247cd623ce7d0ef8b62e82d48c5d258ee253f58dfc01b999e6de56666f6f9ec2356da0c9abbf0356a404246b99adff9e66f32e2e95bfef63a46e4e6caaa9fa7f1827beaa639209ee3c6b5ccd0f8df3d584f0519430ec449f7b669d34844b2a1746a688b040e60af90238674d8d2ec47900141a6bf470c823cdb36b717530b52df807b7fa01540279022b502bc50534603d38f8dad001d7378a960152454d9f0336e8df9cf38e087662ec9b90f4a43cb141ed49be79fbdbeae47791067673b01ab1cdf587886c0ac9d70a206f65754dea18586d384a9bca6f30db22b04f31d63bd919b6980fb006e106718f2f8b7d8f9f2c431b5d985cb36047000f8e99da44ce6ea50cceb46305097c447bd959ce3280c3f6bb4e455c172936d8b8bfca3c7baca17c63f8dcc20ff4f5f64268a13847ab2aab05c587bb0a065ed3b2c2c98944a7736aeebf4ec60c9de4c7ef0aa74044abbd78800b848de18a5f33f48f74890895d985ba470d2c0a95921d22e5e88d7a746c84f0096c0543b2f400eddfe4b06a04b634b25f2c43060e4b00151e0862dfbd0c882a5baf3875e3dc574d849853992fca4e8edba64a0bf0c0913a5c2caefa9f1f372b543e2fad581659bf2b86d04c32b115b57042e601209fc0dd5ef64c69ba67d72ba389bf4dd478102e99957a1f8a518d27951343068473675f372ba861b895a3d102ed8f1c0c1daf15951b78d154228391f9427900aedf7311a79fa594acd29311df3327a4bd120f13cf5ffeff1633f98558f5090be19d0aed620e35626443a1d0fa9a652016852742d2ba5c8b9844b1a766fecc078e2af3a65c49bce92f6fbf377fb44d824ab944699d9d26929b76b8d42648e8039d42b6d87c1a57beb98ca6dbee45ea89876e6231774f9ea1e4e9820b4cfd5c0b68486b6d0818df67145d0568b5d8c87117fad3b7f94a3a3db2a6802a574ad804c8dde57905880fdc9938901acaac91fd867d2337fa278896e962a0e74bcac60746cbf9ad449a07ace8ba6df78305ee8e1e887733849b09d2df7c4feaba75920a7d82db7d14e3452c0ab08ed08a4d42d80aeaa56f41d656592966b6d6b542890efbd63209bb69a53561e6e28176c405a699d3dd358dd6e00228e824d5167ea406a9db3738fc77cfb68c8a57392186cf3ab6bba4de90493c4186f3f4cd06552d098393de955a26a0b1e600d2357c1bfdaa7e4d4002172f6f6695bfcd0aa752860579cb787b102c6531a07aae7bc8e379f3171be8076452f6948a47142216267405c14339ddbec39d2263c423a5c562e2d89daf434d3a8159300c14bbc3941ab00e4c21e550e23e9c5e6925e48e83eb1a9aca0026ed9f45ab4c03bad98cb110d40d11cc7e57610316979b40c94a91cbf83f6200ba942a8806ba71e5b82726d3ce04d68229f83db82468dc6ef41f40c50e4609d06dc817b3e26a751e6362994e6b0cc599c5b863f4d7ce46af7dd8168335207e08e6aaa0f364fa8140851bb2227e0e5cc3f3f2a3655040d32ca9e318f93b86955f67ca2a0acdba5a7ffb4580a66b08896cb3776b658aff6a7b2f1ebb25ab20d39d87a5f1d2a1ee7ac8b7dfb66848076ce1807db53c6b6e58740603094bcaf45a4f551baf0a2762069ea1aff3186c0b18dcef53704f18d4a278335f80b0b4a34c07a936456f4771d1c5a095a6f72b0fba9c665f52b552fbb94b064d161b4f1e173c9b99ad9beaac3017b73ef1d3590d0130db21fa48cd48cc974b963a2f1a0f143072747412921591ad23f0c3e672046f00914f388d1c0ace9a08982c0a85d7c2c855e846825632f329f627bf031b093db8f97e2cf66318fd1029c7b6f110697a2def0135084edee2e755bb8a38cf0115f7ed905d5e878d20e2bebd237cc6e1d8700675f6a745a9e031fa4826e2f7052d41aa8697b224afd4efc0afa3289f74f86cee9319cfe8a5b068c16bc96f740579d4a87ea6e20cc4894b491d85f3f6bd5f53adb07773fce2f6ce54392b5a4408bb6b3d653e40d81997058aa9c62317c42e788bbeb61b523fc774fe8bf675010778fa38987d06b237d0674b9f5d0ac8ca47fd9ad7c9f7f9a66b96a6e55ae3bc0ed08a3b43186112e735d00801b77e3a8f5c330e29db8dddcea26cb28409aca509faf1f4550c5d8ebf707cfcb40b4c8873e50e5b69c9bb214c100336474589d80d27a8b503f6c916f1e795b7193d2118c7a4157f8d198be9cf967eebea6a57a205ee133b0ccf95531313a693c2bd64995913a6625f6b83a91bdd7e83788d6b010dadaba59ed93c4902a23babd4f0246e87010618ffee1aa720d5587d9648c8150a7563e51f340de57f6218ac5e719dba15be03df5a144d52a6fae34b6d7f9c7d0c9b3c67f28b7fce791b2364827e41caca673afd3951b83a5eeced6e013d1e47094253c574029339e7461941ea2683c325e42f8a6a63b7998da597fb81532771056ac413f98c97ad74f263605a3228e081f0e93075ef8f09dd8fe863c964c92f0512face777d8d42478d812f33befb43aaffa3b03e801ebb7234afce6664bd230b96a7809bf6ad1dfd5462d5931ea61fb8e55daf4cd999e1231c2b4e068012c50ad1ff32c4b54efd4c281ee54a950b2bcc1cafa46bce884bdaabf277c8ef589606ae1c919a4185400c197f71a67c1c3b6a26a8baaff87a3c4329d5da13a6be2908e892daaaa07a9d785fde0992d089a2cbeede5e10fb1afabeaceb8c94ae3a3a08a3dfae81709bed09b3598086a4a0e1e586df8bf03e23a0a376a8c6db5246a30f520d8541f5e0002ff082c94b081358e8c065c61e0d251ec97efe6e8dae4bce0ec14dd5ec3d784dd5bbb65489a926d794401af4464a81fdd41c10f5f01ccd7b0d45417765c89cafbfe6723745444159dbbca31e6ebb0fc637b34485a207f5f90bfbfca520d4bad2b4ff3b91bc29f0458c2c3b8612fe29ed6d3c0244c2d6c22f0fdb13e5088ccbc1603a9b1e2480a5a8b19b01898f8695ef57bb7205d8a9287a0783c121f221bb615036e9a333aed788d4ef208df46b1cac1fbf3273787e055c0cfcb259073ba9bb09310520808821f9280948ca4e2072662fabee3f062eae940ca0b0fe6d4e2db78fc1e29e18d9fbeb12d6d7dc4adfb72e90488d0e2c77478901af88cba160a94b41cfde1ccc7f22ab3332801628e6882830a6e42641ea02d90d0310238561fba2423b43ef75f003606fad9afdbf2a040831269f925715a0c3034027cc831aaa5cf28686c36b3b89389ebea5e0ee36457211756aa3dc9bb51c0603aac2a84f1ebc9e676c94e17dd0750ec5ec22fe87a5a30737d04899a627f00c74c7837892028f8091b4b7be653f9cc6ffaed035f7a253e1f21258c3e2698607541c66f1a6c42778e9d6b2b45e53329d4bea3cf04bc8b83ca250f148fe0b10034723e0e38513938a9f115a576d385a6dcc3251aba17237e57b5d214a5b19de04ce4af3634d5824bd124a48a727f318404a87b9dc9cdef7f6eba710e46e636d0763a5a5b5e482a3f46efe1c2cf410f5a77e0e7f3f8bf0fd361f3e7f956da3300800d47e4fcccb3e7d60cd4c9c224acd7942e693e7d7b807025e8d2f8dbe402c0db64e5713ce1aba30c0eeb1eb99cf680b4d6139c26abddcdeb94f796298038b0d0ba52832204a46bfa1f1721bcad8101b914e712bbc1ed58087024e514fe0e307a4416935c00a4b811ad4f78a77f915e25145108c1d683ec8ea85624a1fd94e07005a282bd0e9deefca82dee1c1bdafe99f3030a15860a5a33e2c043e3504c10da2cf11a1307a92754b5b807907913a35a9a3b2c3ff565c5183b38cedd4e5900acce4a01df53611bbfa8c9676e11ba13b1b5b4f44121916f87265fed991350503b9f090c3afa665baaa3b7669eb18403bd23a43d4bf247c3d9a3533eae3f3160dfa97bdf61160000b2f35291aa6c6847fa26d3071ebec8793c66dfe90999ba209b68b502eed1db8f075aba8ea7a02040253a5303709f41edaa33bde016882600c2791e4a42f8ae4a55ceba608ef1d0a9e75557ae8a66a6722d668fe29ba229800b5726c5dc041a76b2771a3d4781df098635c8271d5d0c5574a66ce24e56bd9040e321abb4fea1269df0333724ce8efd47b15e9cb8d80d2083c6768a96f4dd50a4f958c0dee540104d3b61ea658c9fb05d371a03ba802dea9dde14b2dfb771b0cae45da2764f7f6ded818ee6d0a1032500f327f48335c3928f7fff6af469206011450dcf4f203ee43a6cc8b41d331383eadb09946cbf5956f79c8861ca610610599e858febd45996caad51720ac8d66e075ea0bd4821de154053337fdc4c3940e2e6acfaed305400f196820eeabbf17876ff2e1b9070a4f5c011f138bb0e797065ea529739b3ebcc2fb1a5563f62844562850ca9f994c235fc088c74ea9fe7d04200cddd1d260614e60de29323a0984bbf44522f14f9b032d8c8ebd1f9b06ed05f3d5958ef7fc1da6e2d01fc0f402e4be55b48d3e2448e5841ae2789f719ca004bc072814cafae46928669642c7b75add8d184f88c5ec2465a6c587a556bc110598033b7b6a9454754abda131ac45dcf195d300bd805244790035c644a77cfa039b26a7b87be7eaa3193b660cad41695bc6f7039aa2395dce1ad76434d25f6e010136a8b0d1190d59a2299bdf3955480344716df405d804affbd32b88db283b006187ec38fcb5a16f4aad00293a833e68eaf73a4ecf5afa60ffc507f709b0e309fa9c51aa73e4cc70f927ecc5675e839176ae7befcf86fe179378b4d1898ae400d20ca7c6c2c3e8967369e52e2ae41397572775ec2583386f3a1c2a3d30896e0d07ef5f31b4a04bfd61f08f76e02a9b04bc75a849155e8bdc9a3249308ddfc6007007ce7120d228bab4190ae42a121c69ffba72f122cea31eaf9068a1d355b1054a6295dec77ffbd824afe5590c8172fa12fc6ae6950785a101b6ea40418a2506b76b737479e09af0f99186d14de0f4513fc4b80457b7d80f3f7b95ed9d3b1e07870ee50c593513b6296ac361fab232d53381d251ce8ec4090266fbe5adb8d7034302647da066e492641819592424f619a38abc7d12b2493823334cd3ef612a0bd8625da2d21a37dcecebf1d7a57c05a6e8bfb385708533b8084505257d888d02fe0f7fe52ffd1877cc357433a603d29ebe9c6b559b4ead2a3fa7bd7985d21a025bc4adac4aac357cd8b97546de4b24bc4a21c01e0467f61115ac3a948b73260ff8ece22080def2552d2f95aa2deff214ccfa02baa34dd42169260827215c320499ff5d6fe90b58c61cf0cb52c36e182f842976b1a361c12e61b859609b0a2b03c55a425106f31e4e344b1ca3e2464ad705964e89dfac1533b0c3f48e829d7e00724cdb27b3ab18217911f2d347fdc35785e32c640425a8a41b30224dde40650fcab569576fad67a98a70bdc5a9911191bf731d855ada7d2ad19ea0b9588c800ba18e098bd0631f13e54a03d4b1c28210db97b607b74ed02c6f312e736169af0f3fa72678de72d720f86a198d378b6873581c186993cff37292bd66b2a2db75011b0dfdfa67ebd77e4c4e1387742e1157c5f9d0293066ebc5327fd010f9e2cf0859c111085eab993e42c8adbc819e19f54a315534d6c682900db08ba33a09b402bd0c0a3697346b315521c1cb49e8c8c551c57fad7f54fa61195708d6ed1ddb04a967d8e2c35a9addb5b0616650bf3256d11dc75a5c8d38559cbb8cdb37e6810b046b997049a2964da6fa2cdb506e79afff50230443f601257c90fd826dd1b4035b0b1652d980dd8d066b247dfe379d5c47c2b6779b5a8e9f9753f863287f7f0fe3892d67f4cc4aee6835b9d818b118d09fdfe827f5998ac704c1982a76fe9a0bc9369c4839f9fa92a10e26345cf3bdb893693f60cfe9e303f926b22e3726960043f71c42a900bd067598ecc4067f520f8c877186efb67b43dbf03690d14ba5072c618b198635473135e3964fe55638e5306eb8b89447a2bf33b7b5ded2dbe701ac43de1037c40455cd2bd0f4b92fced20560b4544e4623de1471b9b129323a077cefd2bda2156870c4d3d22a2b82952af40593d364a49e48beed8ffdcad2330d6ae95f9bf156b0931ff1bcef9d536ca619f31131ffbec74636d1158cc3f6d7059bf30901a2c96cd87e1a2c77267afebc5b75e4ed22255deb1b005a3cc1d2630ec6bedbe4467bb2ca4b73457f504dba5ca4bbd928b80798e770d77cd74831ba051f3e147741f0174c68029e0d59e03bdb3fd108a2acf8a96e4f422d537f03f2039d374c851216c433dc0a6091e68aef707eb76951e4772012fb6776e96366330c1399d6470c69d1b04dd51c2320fad7e804587e794a0fe56042bf729553e287088f226e05ce230b1214b3e605cdbc8c1060ffc130168d1775e7972a645b20ac039ba823b5de8eff9af4399ece8112f06a8b5c871918d5977f2879ade0d225d408bf66042a6b1e6cc847c9f7865228d3737a19238a20bac1dfff6e0ca9752d960c25a8301a0ed70ac6abdb61e1f987081d5e9b834626da0c176fa66681e2164f079f0a5e5a0f4a205b9603efbfaecbdb16ad34ca64df2bab9c754e087585cea304d159bf4ec959d6b56984fc59217fc42e97315ce059cab3623bf45e16a8d9920c751718d330337787a9ffc9556c5e2792383d915ed6fa92603fa49c4fcedb1606c5ebe129cc5f351f94ae386fc54d251826d1d17983d92719dbc48ad1ba7de0035087449c2b5bcd9f8121d4774de48371fcad032e4477cc16168a38790487e40f87c62abd33b2501c81e0b263e1398a0ca031f10f91db4416987bc552470eaa0a69ffe32f38f7469c44b4e6cf669a25d9deae27a32d8af292d724b026508377012a24f8fd21ebe2183cb662b1aaaba8d79c1f35e4f016764dcd2a61e9a3e7c80bb9c337b526ec23aea6fc68fe9e80c6b055f331191fd317adb3d83cbbddb07002ee1bedb5829d89ac4651ac3ce3318cb794d5205fa1acb3ddc6033c60dd5c020c67cd78d6f67b8e28212db0adfd4ac29d544effed0bb6288cd009b257d6f76e0e5f10c57ba00f9148bdcd1616adc7204020124890bdf01652617d19f9676ed70c630094ebe0aec7c9ae2210eb6a03c169d04b693313758ece011070c08ec6fa08c50ff04f6929174d8f6bd76d52fdd9a19956347530318e5aadbfbecd8f6e430fe9bdf03019c9bda49b8b45dd6a593a137a6bb42fa12d00491e4653300a59370362dea01ec38a685b58406cce0500540595d79351adecad060bed02b404ad840638239903d414792102828c8903232b4c2cbfec9d1d53c0fe33d8882be347a4095b7ae9a982082797c98b030be27bff6b4623a37d6b3396a4f56ac545e74a5501edbacd9cf5c98846a47c6af9e81fe9f622b260d3aac0b4342293df67a068750712712966dcad17f47bcad7360c90c06eb81cc61d1b16d840556c24fcdfbfed0c54274a05458a995d58bb0fa421964a54b3031fe9887f13c7f80a8418993caf0b06a3779dbb07846d617c645732809d3a286a0320a643b56a65ccce0e7788240793119a50d2d47d4113b72256a90d5feebd424dbcc58783b7ae029b533df9500ae60de13ec48d30d6681c94596223a3e6f31f67bfa1ee86e054006ebcdc813b0148f4548ac1c448348f26878acf8f49e896a0aaa75e44c021eda842d35918d50b7e98049b4c636cf5c6c2f14361e071fd5cf082e0eb6f957cd02496390a212d0670aa22b16e29eb828361cdb104973a1942c0fa525cc955712c39dd56c3dcf10ce647ca40722adb7c18e1dc458d87724144b89f56a61036a610e54d7ad7eab506b2d0de75d48ee90a5af4d566b784b3d4fce7314e95564553ed6c4bb4b579a8050398b909d7330b33d28aac3ea097cc4ca4789f7401515afbc0d65896f4761601bc7037b40e746cd17aeee78fda1edc72011788403b33bdf5c1fdc7f01358c40a0656a64ae6f9e7e123f3feed0eb4dd9d972d258404a1dc8bbc05cef2090c3304db27144e75153f789feefa8f1115b020233906c8160bc0458e7d347ec404440f9f541aacbef1b9f640e60e1ed4e473242e47988fb941d3bcd863d160bbfea800f59cf1fe64d6e5d959c7aee993129887e520a0dd4b4f9b1598b97afd8e7b8c0dcf475f46b8c826f9c2aa6a643fd44d488f13d10208c16d7d881d5297f758870f033e0316414853e507699f407073af47fc524af1d2ecee69f20e3e2da789e80fd5cd632247aeb256c22d8b8a2e4d73503063d0012ce4eb967e1e4d08e9e4be0c886d77a351a4c3f82c22ed761eafeb87dbb09da667f6b01ce23d29892615c604fb25e9360bb86b7719638abba883b449ef20809bbd01d6ded1581c3fb855740a17ed02d464bef0aa12df75897f5d1cdac3f1535440435bf0b5a8824bfe6f0208976bca6f22edfebdd7cbcd1d94faecd549da31dee042de099bb57028b2b3e1039d0e072c7da8b7cf3463ea9caef7b0bce770d3f6a85cbfe153f58dc1fdaf8a09e5f2df380e2d27f5278a0d5a2b00410a563b1e6368d88b85ac38ee3411997d0134e538d976bb4b8aed41a527ce1b2926f101891b102800ca34aba4a3f623df0e92d6f0214b3f7bd4667e766e3e83a54efecb2e9b77c99db254b054f904b19d0ada49713d1e24ff4a6f200681f0c9c11c4463e37d2f366b15a54ce815341ea5073a12f34cdc89cfcca90f727d478b0d67d4dd755199b9493f16fdc9a50c373d07e08df1bd2d52a65524561b817828940eeec49aeaf86ee22f81271aa6640ff1032c342935c51b37f7f2bffd6686a050fe16241ee8fc836b174f08ab15131a2909cac1988f337211067071fbd0eab76de06eca02b7d7e77b65aeb641b0bf1a8f03523681161d5777cf307b6a3170fbc548f9cfdcd668b17d02fac72e4f5fca3f066a442d561e9439bc4f96af69467b27ca6de1e9d2c365548e99d7eb2f4087ca0c2272b75753907c43d9c08d5d8d25b00778e2c0fdd4893c2d85770d09593cac05d50b61abc9c1e73bbc2abc8892975f70b2f7aa25d80e7e1962623021ce594408a0e02f3fd8e0c5dcf3b3deb43fa79062bb026aef3756bbdc2782eec9fdd97b05790b691d20c2c30d695709bec05239afd15d5fb220279365714ba75c5f874b035135cf1ae832d4b62587ace504ff21b039a7ce70941633a7f5140674dcca3b0ae9c071d6960e92259fdd7a90cf28758b9305e6f1e4fb5c464ffbc81acd32b50603cf348028ec4961f8a0730478963f1145889fa28e9ba87c4cdc7dbf2310eb0a9d94ecb72a228a87706da1e4c9b3564f69172a39663219c68e4c7daa8ad4200beae6d421635f5f24274f3460968c091c6fd05efcf2130fe3e775bb14b40c6d0bdc77f81728b5982cf7b46c5951d2688168532e377ee2466e6295a96c35158206c3dd86e7d55e3c3760a9a9eb42a0aba66435463705f9fb747a4c944f7950d007e5347501b03293147a73bc7cc312427359d8edfca34557b70b4dc1895d80a40f +generate_ring_signature 67011525aec41e62812368de7e56ad8ed15cd4845e00871483703bf9dcba31ab 819d1090586ddd1ec978a2f24412bab7d583208042f35c68d6ad5ec98752bba7 76 dd964f5695c3a008543c185f0a1b5fa461e9c22fea98d1af2a1e5d60cdaf6e01 d1b04957e8b634589c062eb1b743fa1cffb725a8e272703bc10f50d1f5a0642a 51dbe51747f44800638559a63971289e43845c9286aded8b96d55d982fa22637 81e0b148f3ce2bfb07971eb4acf845ea0b1cf1c08b27ace8b0714a78daab6097 9ba5b03511558e8edd7b3c0336d9086011c5adc81aaa03664841d8c62f1fd2f1 3ba3b659ae309970f3494eb0ec612a5cf70da1357bb5beecb2fe946a9913f6f1 882ea42f44c98b4e0d85af542d9de44ed889c3d65f9ed6d24c9eb751b640c12e ce38c8d666e28094548fdd3dd3a9e9706e0ab2c863d5e1c43fdbc0682bbc00e0 b0998d216a9ea0224668c114c8db2efa0a565838075f96126bfcaa22085b4745 c83836b02f86df9d9c8f753f52670cb80ec5b6b2720cb2b7fd7fbbfa2d838e5c a441967d23071f5a95a1f82f5e2ccecc2dd3d31bd1849d137c37e9415c01595f 0322b7bd2b2430e428c8f1273fcc36f45f97892b5572bc53163675abcd987753 b9e1f6ea22472fa3db4957c448172b1d58aa61f6048aaefb75d3c647743c87fb 348fea709d69b0752a6f382577e2149d83b39c5aeca6f0474d13b1d4627da825 50360c7ac3a214e93473db297a3e1211f08704061bc868bdf06976a95db2e6fd 8a5ed26f5ceb633b0e25b2a4410178c89ab38634fc36175aac9847fe666d6876 9b2690089def921d1d5cbf63ddd3f58b5d7fecb4ebb54b9b900032ef0d687084 4a22af02b3ee18ca29add712fb0890f5fe0bcd52848f1f8e38df6f38dd5c99b9 46b943bcc3d9183b473a4d522e4a1febbd713e23b7ec8959e5a4fccbaa9a4e58 ce8f7b05f9e53ca34b01d6a5bd63ee14991d7ba4e42a236a7248855832023dcd 851d02e432b19150a6f451c82eb384d730b0c3ce3c35a4505765785fc307ff36 7ede31e63912f8434bb525222bdc771cc1539e42cccf8953ed50b7bbd152f87a 989a4e49e2169c52b2e8ed72378944c7f4ca80cdfeede413096d6bc6bb481987 8d197160b9241e31906172e6e59aca990939999d13f247f5a5d63feb35218761 cf685e95fa014fbd3a7d23c9345b54c8918a82cf04519dcce9aba2064e703bf8 7b7bf2a667d37550e4e55a37e728ccc78cdb856082abaf80f30bf1b9c75805b7 b63b938e1ae69254a31620ed6db8a682a73755ed347b020e245209f4e5aa344c 8e1bc9db8e3ad41682859991867e2096c5e39067bc4157bdc5564c3391902df7 998290f1542e57fb84928d36aa67ceff21910f22bc8c3558a0ef10398fdfd3a0 f4aeaba0a6d239ea8a01d9cc7daf6ce0109d26aa92cb30f4456bc5eb2f3d6738 17833eee37eb03a9ae32973d56754808205ef421688f18fdce0caf0bc66e4f27 30b5341b038b23ae69a365a896790cd64aa19eb31864bb1638c08285909d764f de2d29581951dfc59b9c1d2a2a4ddf67919dbabf8a58651b2fa527c414a5cddd 0457828b3514870bd16433cba46f8738c173d41eb59ded65acc6c7dbaff5dff8 5c9fbb3ee625a4c846821074213e505f9c51d5358abaf3148a8d154cacc6d6f1 6c4485b140aaad6b9e994c7d2e5023d0940d85e19af437fd21f236ec3a4733af 88e354a64180b6869ca2192aff2b69fa306c43be47a4f110b85dcc5495f1c977 6a6a1de9b1c8cf8b9226fc675ddaf1a032205f897d0727829da84b8b89b22d51 78d1502e1cd1fe7c525ad2bb4f7c9f742d679f7908e1a2d4f7bde67ce95b8ecd b6ec7b175273d7aa77355401a3c36d43d003f6efbdc0a961564bad698e8264b0 b4cd90e8ea0018b56c069551b6c43f45d5e330633ef17161b85acd7418294d58 25281c2d7629d25ec3b0bfe45d621b5429c1588f2831611c642cc7dabcded2c6 6dfc0172238b46bc6b48fad9ffa9ca9e8dffd0667ef95611bdeaf212ecd6457f fd8836213387691d192f743f779da56e093a435d355426422afed7cd71d18cd3 d32a9bfe7024fd29b185a1308ac3fe06e21b181ac51ab2de211270a503ee93b5 d65eb25830cb331b5bf837c591ea4e4a350fe3cc01e795e0bed07d38b4f84991 c521b61b43eb63632c484c6955c669f84095d769f226524f2b324d4243398cef 3c282a717091456d676299112eee95a854a7a6a74e890c295071033c4086760e fdfa169265150e45aa61bb1450d7a1ff82a7f9f076bfd1ea5fcb3fc35b527299 139659524a57f89d2425bc842cb106d8c84c9dba73fcc03d61ef2b7e6ce2e002 dd28d9609d1d3e4f53c48cb86fb6af584275dc00ece5322836b89209bbbef2bb 139e90fd22032ea389767357866c7b2ee9a19d9637d5911e74556e89687e79ca f09a6690d27cd15743f95203885b89545c0d31d6f745a0c1830e9c2c613b94f4 b39a46aee128baa794634dfff8157c6d3971de45ea87b210a568b21d4cc2cd39 8f63b5d7703ce9dda77b5fe3a6569482d1a42a2a7e7aaa46e3f50a1bdd217e12 14f4e2881ee2111945d653a6c5ca978361671932c50890bb355f277be25d2a77 e29737b0d67fd4b843c907975e594978ece72d400eedeb0133e875cd65710c3b ea3cb93a2202ce93bce08902a70149ed46a56d01d646ba50c39f8fc122e19926 e60ded286f6ce5f73348f53cd99e4a3de0c5e60297be0d7f066fe19c08c13c16 77754e38b821d49702150ebf7f43fafb0889c05cdab297341578cfa18128133c 434012a532762ae1ebd6706d4740128e4f1c4b97488de8844115de50d7b9149f ce7d22e2a914b82c673b65b28172af3760540a84a45b62bde70bfd5833c33509 7af0361c2aed7982fef7782873dabd2d0ae6adc240dbaacbff15409d916a6a01 d3bebf533d2922ea2539d196acc3e160a8c776bd0305992eab11cee72b51576e 68bc2a53fe9f28b0fcac97ff57c0dd6469b3387741aab8bbe8779b72264f10bc 97741abb965b119d339269b289ad2b630e8152a2e8ab33bc9da9fd155a2453f2 a549ea88e7610bf3c7d92fb93bb175146bca920a8a566a6b8bbaca0c73fd31b3 fae4b40e6dfda8cd353c561aec3c9c8da0954d888b9c6adf883621b50c3c99b7 55e367e48d3ee954646ac39548bd79e9e56d216b1ffd07d7cefd5b99bf08ffa3 b5611452300c2159de3e38795fd01a3fc57dfaa516ade34e288e984ef3a0d604 3ac22c7a0b7ffc3611cbbb883cbb1fb6b4fecfbddf4d43c2c4fb484d4b103868 94d7a77a15bf45726606d1a5f4ebdb46609b7cf029cba1e51a629e1083f3d76d e7c85510fac98d7cdf17ff2c1103f65411fd196c37571a4d2002bedb00258cc3 023a807dfef8c1e036c0a55e6b1b09ba9aa0637caafee1fd1b283f48dcc7d88d 72638bdbc1acec76e018aa9671b04f82e1e0711f708f6424bd77f9c048060ab7 f19bab5af9575092387a5070f63e2c8bb72c14cc9f1ffd5944c2c24d82e880c5 b5a3a51d296fd23efced3457f883efcbdce7b2e7ac1514e5c51211be8c328604 24 753bd8c42b0d38cc3515c05f50ef12bc5d34cab0cf6820669abf85fe66871c07e344df3f74ac05bbb7270e51cdc2768725c5d50f458e7421c37de6bb9f2cbe00e66e46139a1f40742ad48310f09e98601ab636000bb2ba047df9f232e29ff709e8667295b7ab8a1ca7f21ac429c6742490492c53f4440920855cfbd132df500a2d2825b622ecd08a3be6a421db918bbc8a394e5b3fa21b1e9d6fa4ef6b4d5c0105ad1278836f10ee948fbd4cc7f33c76fe457a187b11fad96f07a5c3b4c61601c062a9484c258d37abd2da09bfa1f74aadd31c850138b69ca03ee2ac73362205eeae22f7de0fb04782cb2a27aea68a31f31b9639af77ed8cc7c6e7ff1fcfb307a9800c1c10dc42efbb2098df81096ff82b4ddd6fbc1c4f4972a252bb920a930965df63a2f6c3a554b571ff23ed60dd5d430f59c60761ee92883b27ba5cd3e60074227bcb9b44997e043addfab96cb438f4feacab64aa12e06684e6546bd8e608032ab771fc1b7433fa0fc42ccf837081151ba0346322c587c7df4aee726e620667d1b509d5b093a3c0f70b130d4be2ff196a16df768446c97bf9f7d6e0db4306921052ee334b04c29e790dabc94674bbf9b8192eb5586ff4aa0c94ee66121d078daa862be6e228085e30ff12e4d17b50b57448c0540d74b0da051a241c7b3005f6e54fcc2ab4e1cd20e0cffe22f12152ecff46aacd1482ea10f56eadc6363909e256d2beff3aa78337008ec9723abb40ce6a8dbe17d43d0ce8d5f062c999600176edc662c93a3ead2501295b827039bc5aa621ad1ecdbcbe0d132602cb9c2505482336c253cde5639ce3fe67a1d660a4aa080a74ac876d5497c448841434cd08d2fba2c6831f923b1634a09e599da97a7606d10baf902346a5c3b92ddca0d3078606e3986d24d5cd5c1bd1c7a3f9d44325f081c388ef8d2f76dfcc8339fbda00779486cc3f8f7becbdecffbb5a918d218c30ba32c2a822f63a8536bbb9fda2085dedb86ef1f3567b6d0b2555f9504fabbf5f889fb1a517109a71dc961f75de06d8519d69375d802b9cacd2dece92e6b837668b52ec32d41e093e184345b8f70f3f556d19653b10b681a67ec93c54af13f6e7608a17e688e65cb1300510baf805f64f49b9571a855318ae1ca8fb05824ca7f1492db8f6d835033184ecb185e804b17f4e55db4439eb2fa94095b7ae4228f7402452b3efd473afbe2460ea269a035df5ed266089f216b9d7e16acd0bc1155d92d3b174deaec3b3dd1f3e2b968b0a06db920045ac4fcd7bb6ef27f7f45ec8c1366aefa7170c3d404b293d6339c0054ca8a6ac94c9d75eb68ac095f9c7688cc3efd08573d0f72317f3e32f0e3a13028ae25edbfd3103c13f0d1b03e326566cf25266276a1b64a379aa775354d0190cdfd8f2c203ec0b1298f0fa835405589d55d4b4f08294e6324a5ebe1dc1ff6808a93822859fd379872b174f8eb2c20c2bd7c307c81aa37e1dcd5cd2a0d4de7b03e0c033161cd653ff9e52b7c13c9fd951af4642eb4ead0587e2ae20e37c6b640d2255b7abbb9ed77314dfa8dfd97792537c0e1e7e3ee70a0329666e49b88d8104baa00dc0e4b257d6183f5b063a25e9d305cd46d4440583d6e2bb3ca35c30c00faa5a78f62acc5c0d6386b32bcfcc1161321052403669bbc16d3bdc414385b60f394a2301b76e5c3f3f389ebfdb2e0af058a7090be2b7c93620dbe8106284900eecddceb90e821771977e29dbc75bdf1c9a4b16334ad85ba19b8c0ec932cf79007c43dbf91e4eb769e51361066b79f94de6c83ecb3f7bec8ccbabfbc2d266a30380f1eb10559aa693631a4233239f7b08ce11e3009e5b7caef714638755b5450374644432183abf91c2b05599e16d4ab248ea9e19115b810ad54c602ffc69e50b644caadb02d241a7f50e8073a0eeb8e784e12273a8d90d5d4f421c13a3d5f3017ee68883f64a6742444116f1c49cc5adb8cf8a3e294d81731d9278e3f881b406a266bf1cf9c14e5e48a6abe135cbba1174051be1d95bf6dbcc417ee654b8a50bc76968e04211bbca98e7289d418f09667afce3064622bb5733f6801d9e93d20a0e75d6dd6df5a612231bb868ea9ca70dff09bcd8327b81eef9077dfeb07fcd0b661cc09d8c28c94566888fa4e91a38ef9d809d8cbf7c2a96d42b81c8f32a58049a04b71d721cb7a4012a4e54856b0ab788384d52924973cfc319732acbab550d63d9a934493ce8d2a0af58c85602b974855b31a8540c108848bc780f68253f0114c1372017135d52450c930ec01b4c0ed2075e24792f16f93c6a76139b9342023804aeeafa75268c4fa9ed9f5839b156cf54c79fec2f0d31fd9a117c780e1f059f968b62d6da1561c19154e3f0c37f157ca7c7e454248cf3aaa6ff74fa465108c27b0d60c09ba38e9373b014e91b39f1ac35276c7f136adeb73b43300371ee0d0f6a86726f2bd80e55766a4e2f9e1f9f2c25a81412d489b77708fccbfa7bfe030530f72653645f666d82c9674cf61aed175801a191fc1601a82ea0009044ba0dd14580081885e3ff96775735c7df0ed9b96cd3d8eb2daddd8d3f528143070605962084b919317a1dcfe053cfffa5293e76b4fdb8b08bfc2c0d9da67312a3530b6ed45d5445fcfe67ebae6da3e1c2a58de06e6255205a27fdd130e476c1aaf6026007952e4d2646c8521f761ad029083f2cadc38af0b4a9b1dac5c3fd36f7bd0a85067ae4b23b9ff85741ef3b53a729f492c0c8bec7f9049b4be7bf1949649f0464343951c0184d3aa11a2b7845a47da4ffeaf75514464466cb32eb921cd2680a0585f660a28d21da67eaad8bab25fa084f8a2d9f362a057bc4426077a261040b8cc31147dc38d10fa1e242b1d66aef3a370223944a615732861012ab156d53048f40d37fc4611904476af631b7a2b77a356b4c45986a4e4ab9bb82c676115a008492833044d89a4630ef78b87b74233b1274c76ad59a9871e7e335ca1a0df90bf55ed0c80b84f6a8a43e645e1bca9ec5e22bbe0d8f065a3110ecc1fda7534f0cb0b3a408477e93d04402a9f19f64701114664ae871f6483d734bfb3f7074540d168de1e3aeb0f0d660592cdb24dac3731280b60372459cabcf3bfb1fed2bf2047173d1fede108ec244eb1216c75e8b0d82f856f3a05fae239533c70c657177014be0201fdef4c4376d35f7cea673d61c643430085b24c667d5d1e8142dc57802e16b847045db744b472f0d409f9710b571111d8d6062464f867e02a50271090bb7a80dbddaee992d3b09c9ef54facfc24680c9fcb3c32e3fe30f7fc8e4aa8b0f12fda6b1faade1deb60924fea0055d24a3a689698e794ab8e4cf32a42d5ccf04b84d4867e5a9555365fae23e70ec38a183cb74550c78cd7996f209c04f10bd0acb6eb3a27fbd90bb38e53a10b9eb2cd0c68a2b2a5ddb3df6c86696f52c9f0d0af63671d453db964ffba0f8239c02131e2910bb4c6a6874a45a32e3dbc558ed01b741228be039c14eca86427205fc5003c9a75bd8c908634f9dc5eacf3c98c60dd79a5dae7ec16b432cfe720158cdce0aace0b874aac03a75fcbc9d4f91cfae0ee84888b230ebf0dcede39fd808e8ff736059094da5ebea2dad746cf199b3550644b9d1daff0da7a1255ee669c44ebebd115788cab8d9baf09454323a507e0200dc75eb17af2659cb89f17faeaebfdf155363f5a0a4feb7400047715c9cd68f09cd659db80d8f77f40408efa50dc621eeb53c38e39fa7ed54d0033ead2101a4023b510d8c2b0ea7867cc566b2df81d226f690b179cd526439284a8261a8902f04b91dd0303410fe78cf091d87db65454ec11d9a4ecd78cbf51643838e3afaf405b620010e2a0c03711af3a53e463a1b4ceec7fea2ec8dce0c9f3a09ec2e9d1d02a5f42f16f729be1a25a2353917ea1c599d8b64b99827ce87cee56a7d364f0c08a65d7ce5ab9984cd84c401ae3cdadbca83f6624e6a4db2bb730a42a21075cc03205dcc9708260173b322c19eb7d53dd446314b0c5c333e990327d7293495e20003326be5a6cbba34555404a114883d26741d4318cc6859ffd88e1abdeb27200a470858b413ee882dbdb29fe9a6d974f3375e2ca484c32509f5c017d3c4058c0fb8fed6c5e209bb0e55935874dcaf290eebfa465347c1bdfd656898f67e8c240ff5484de93d094a7b10b1987845befebded340f90480f677c2d7ec1dc2bac3c0f2f8d1cedd8483edd8dc20039fb02261d375b788da6e4f88bd6bf95e1d4b25d08937515e629c155c74ae6242c02daaa1815dc4911ef0e160a13384897aab2360dd53cd21fed9dfe414cc82e279a47198bc0798e003260ee451bf0623c6975160af198bc474af3b7f97c606d96708e262f3bd9b6d124981893914d7a6ce59d0d0b4c252f5e5578762d67d90417560ffe5d3d43173a78394c4147b2afa5f803170b2d9ebf03b2fa4c9e1e62352648f5c2c5e88150f5d8e7a22c7fb10cdaa0e804048909cd988eaf170faa9cc9c81eea6779163ef4507027673c3c87f876392f270d1876fe7cf44709165611c7814a7d97aac477af52d92760be03fb9bb5a016b00d8a124238fcac4f782696a92bed8d97a35ee341930f5eb96bb83b441dcd968007a49231469ca502e0e39eb68792bab8200f5eefb2e9bc0e6540703ffac6459d02f7ec9e749e1ddcc134ea7afbd346e3109d7b746bac3c63ed47efdb9e74f61f0ef10ba3bafba5b9a53fbdd84c25ca5f1539a1baa68cd44529693d474c4a451101250725a5940f56b6b66767f0118baa1a54642d610e9e4f58bb18a07e7ca5da08571a351763d9fa4065f9a36021fdff0d1042c405ef847c7303cdf092269a680cce3cc2e688a60b9d83caf08661445b07256d790d6982817f051b97f4a593f3093d2d135cf780f7d0ec4d58ecf201b9843d7281cfd0c6e203eaa0120b473fec0ec85328b811e67170eba7ec33388c1ecaf5885165b4b181690587f695e1001e0da014d2622285a91ac52181365aa1b4f4307dd6f1fe4f46cfa1b88bd883b79d0298f7c3555e9192cafd175e351caebd8c96bf66a6164c97b6e2cbf2a8ceeb89068924b3907220711c688f94c001b59169a060634c4cb77e7dab09f7b1a768ac0cc4f194c0dbf6f4018e087b6875b8a66215c52499ff22d10c71520ae9f4f9fb0f3223fc16d58d04d653b3ec085af7277f28b7f08e1472613788a1d43bffc2370909d3a1f88dce132ee2df2a839339cfc502a7201d89635ba243832722e5150f07749204cb85bc5cca30f4e177b578c9621660d17966fdd6386f4dea45e825a20f9b59b8b4de70bd3b7860b09e8167bf88377c79fadcdb40563b91ae3f7da6a709bf4922a7ac03682d7552e9d7396be0a5c728e57757acc5ed9923036facc6ed0645eeb7f2c6a07b602907a2ee4da70f292a219404a0997b7729a52b3f2f546604e2a3dc4b12f6ad6e2e9915120847c1c91d46a0e13b1715a92c7bfa48c7d8480a73f8c3698228167fd686aea41c27e0ace3b81cbe69aa74bff26a324d6604d800c3ee9433b0979ead3696319102e3024c83bdbd35a5f34264becab480b4aac50414e2ef4368df08a5d1d3f7e895cf2830c1c09e8ff5ea527b966c75bb07a39a0b85dfee234520e39d237446f28ae1c7b2110aa098050fa3cb073b8ef9a215a609234129d7e9ce505b79b10db50ac955c6e8f757adf170f99a660a0f5b6317770c584da3a871196a929f2585bab2462508e40fbb87e0a7147c8b9b2f9a5c36b40e22adc78a4f782e75f160fecfaf866f34a92627965e0854ae75e1ed5e243fd1054f74cd38cb66bf0149ed7f98542bf8dd569e0daeeb9c46bdcef6d6fad12dc00fabb4cdf0515fead723efb704b3a4794aec05fab2cf19a4e80a0c93aba79854013de5a1bce843641c5b30184d2ca391f0a2230c62bd004f53ba2e4011566df1029400057a21803d83301aa02e9ac6381c5255bc0e05c0b44ab2dd94923aaf50046b06f0ab3471b0f8b235c5b51b90f4e84cb3ac101bd33fc3f21590a8fb70d40df846bef3b97de9960c9b32a9fb021c271652f2d0ffe5d0ac271ff1130123910adcca86dd60c54f2ea150842a7ad876e28ebeb7854785ac25ac83d1612d95200fa58ddadc4403459bf5c8de88bda35de29b8eccf8b6a2011317c70cd546a2b90ee6bfd5e96ea87c7845032ca252afa6629ce46b5782ad901c1f8fd7499fde80093c98a5098dcf97686e5ea7000ccd3894f4ff67ef913713c6333faa28e172a00aa7488aab58a6e00bb629cf5218308404f95d3a3a63d439e143eb5b255c57b30261b34d39ac6c7049c6aec81131a410d641244b6dc897ab5e40668a574315170ac818616056412516a73b94cf9f5a845c48b38be6775c0b76f27aeab53f45a10e2c546a8aa21d9641c6de187a60a987f39d5705a409eba6b5cef24a70a47322004b4a2555987f48a485255fb2f236b6f2ac959f057b032c8769473d0437d4c00bfa14c7037b331295954a883098122fc61a206d8c7641c15676175e6518e2c6000ebdb435c702cd60a4eaebabd9c28c600111bfe89aebb50a94eff36de842c60216b8b01fa94b5e50072f5cfe6d89956f7bbee1965c375edf9bee628c9f8d1702df04215204ef6894d814bfa0bd88b7157c26522b3788aff2dd58181bcf98440a6fb883ed81261dea166316e41193940f5930cb4b003d473a66b5e4c52102ef06438e5f7dd895c693a7c69b3acf0d4bd0690819ec7938a82df134e0b95da1210649e136ac162f85682c715ecc1bb947118d73aa13f217cffb58b80ad12dd8ee0760196ae2479c60932f404f2ed1579688c723d7bbd89055a8c6f6d75b370f7201adbbce7de6077976939268a9f64357e6f4718e1a09525401ae5602a8be9f2d07 +generate_ring_signature a204b66b297db8196c5e43fa172a9cf72d610c1a3e5c14a7e64b6e77a902a7cc 360b1eeeadccee686eabb2475524e968331807e166ea52bcbf4b806e056ee332 6 3a819e2a86cbb7c93d82eefb25ffc9d557aaba2e76ea0cd35ef017a0f9db4a74 fe4bd986465cd8d4684788e3abfeed9a49365cf9a40225a9b5f7a8f0d27d4992 ddb1f912f0733db94098ad017feee9b550c2afdae5e0bc4bdaab634dde7485e3 ea08a323f76486e70dcdc071e43b7458b070d5e15148e57268b4801fc0e9ecdf 16e7e2e0736b76d75013cf19835f4b3130b7761fa2f1243e513b1940deac38ed 9330ae8956224cc4732f9c94d92a3470737d4943400c6b837d8a5e4769e4c2bb 5f961793fa7fab2364e40a420192a778e214079b163c235d48364ed07808b50b 5 9f9c488877ea024fde8cb4fa37fa85dbb51003d7120c6cba58a17371f2d9200ae2667a2d3f8f5cbd84f45c42132606fb1746bcac8c0cc87ca7e94d8dca7eea040b984e9b6f135e316fc91a92042025183eda4acbebb37344f1f88e7ac60cde0e505e1ffc6e946b639855222642218d7eed129ac7a3b97bc18bbeb8824339330b01147768e63e31834ef9e0e892a5dac8fc67fbfbb8c9588c78675deccbca0300a5e0cdb626032e763179942404b9ef462fa24db12d6fbec69e30b71913ccb905a416304aee4e78ba5220f6a75f9a25fead1d2ba414822775de23e81d590dda0ea48e81668c4a2a937eb3919ec320ddad7e0745908515bad872b1967507a29400646f55505de0fb9d6b25e5f940166c858d88ccb324470683ea7e86bf28247805826fa9322eedd64e24dae1539267e425eb720cf3c28b82253bf73cd26bcf820c76df3fed0266389f61b37e700868a29a9cea712e6f7ac0b48175d079166a620d1e0d597124bef8c9ef7170132057840025df0762210db37ae05c10f10fa25a0a +generate_ring_signature ef79e077a9e0e9424ccd7c150414f789a14759ca964a0e13fc01fa5466c81e78 f946890532edc31375a4fc9e0201f16121acea77989bc1f44f7cc9e27d618cb5 7 7833f5dbe94cb5307fa63720d352767c4b1eeae31307e89dc2bab1b483eb07f7 a7c0d7cedf45355d0d2981b5f47390c3e452becb05e015647e39fc4221dc242f 883664c5618118cc6d2e9982d3e3b6d28916edb1db5db6db7bd5120dc2ed1f7c 1227bf14d8642d1ec64199c7b955557194a4bfd8962aac9236e38d25a8fb47f9 eb41c810fb20bed74105bc9cff98bff7526462572cf709a8b85ae65462769d60 55e0ae0f1a539622d44ef3ec8fa1565017e9f763c45f27eb5cbf6b926a414bfd 7af96fcfd866484934851fda064375a00816e21466ffa8fff2425997adb7c190 0b0c659145f52a67e664d6c06aa96452c552c1884cdd0f9e5b7819adcf173003 2 b2c193d0310a28bfadb3c726c54a0c196fe466c5e2c8ced323a322c488745e0ded55711281a776ac22a3bc7c5cef72fdb8bd81f6b51695a2cf53832fcd1e4600909691417e737b8f8994853277ef4b5035845c3a236293daf42608213992090c110cf0352e38fbd28bde85b126e3229beec14be1418531a4a17ae7e6eb0c110d4989c520e60c2a47bac25a5164103e540ff0eabae6202e023dd252a8a0c94b029ca74148a5b0aa1e01e517a9b81cc0e0e6059809be94d6f9c144f0c7eadfda053ede2ae1e97872ab4efb93aa448cf67d6bbc8dfa918465ddd93174369e82910da68d261379b8c536247121899a3db0c4399be8282d0c2114b72aa8e3eeaa4208a75a6d45d091b7a0f73471080c10599dc9f0948f1d485168cd5bf862650f3f06d3af954e97692ce6ef56031e87e3afab01677769865b1543756554e1b5c88e0eb5a6d40f4e5b1a0dd3093cc20f4d9e4259238d3de00cf9f13fd5c32a702b590fe3e0d624540afe8aab9c71cddfa39c40217fad437432654c75d409f0243352032f6747b5eb02ff0871895f86048955c2a895e3226397003b17acdf4e6fcee10b6ba7a94de7600c5451b8b964911d82de6175b90ae72ce80d043fcfdcdeb6bb04 +generate_ring_signature 78e0172eb16bc072a3b80ca90d93b44df26ffbc7dce563174f5865f7c16e5f43 7479b29e23de8cff8858d8ce192a594e7c4c3eddd8b898300b3994efab06c5a6 4 ca91beaf2146e06879051b0b1605935a0df5b5eb1ec9ea4f7f8d0c698e7c5bf3 567e5e858d217b840f50e66af8547d3bfd23c76290ee017b1c5ea07abe4ca354 c24d4eea3e2e636a9a24934ab8174a05043ad5612cef38bda772063f291d090e 7c51495c749ae14fd8ba542a7fc76aa77d4160b73f84e67811c1087eeca84d05 ae5a08468ae07c2ae86e46255bad3bec88fc27a5697b88a0a426c8ee41372102 3 e94e9ecf96317298990c34db47c7cd0e72fd480fdfaa6bb25bc2d1c2866fcc035937e40270c9c8730c7c053ffe96e02567b64b496198c6b1bec8f36dd8800e0cba0e8a8001f274105017c73fa0c634b556431f24e3e72b6c82df468aa14c7903bf9c0a7568cc59b6f5196af8059fac305516b7d8ca5a636e5de7a04d30dfbf0fabf8b0b9baca85c3672f674e94c3b1d1cf2de1ff1b5ad03d5105aed3e3940e0ba2855887982bb0012ffef9bcc1b788143b9300d36e3155548d4d5483141ba40cf1784181339d6658fdb41e9f0a5e7b0b1db4f2d29d52ed7b4d438a3238764706e5bbd9e440aa828913aaff250cf4179ddcd3d4800362fdb8ed3bca70845a9509 +generate_ring_signature b2b17f8eddfd010da161593db87e352a00d0f915b3095657ce31bebcf0531d77 6d9765acd0f77a17564da4a5f7f07a0b918bc309f136baf6b452eff47b5278d4 7 0dec43ab1cb7836dd07b41edf9013b8f8565502509bd853b73a3fd7c2d8332d7 d007bdb3584697e80ca791a489452dfaaf96690715738e9d0a9772761622910b 3bb2e27057e5cb52140c29f86ec9072947931c4496da0bf88eadea327b45e13c 85fb98fe49d5489a55a67860a4dccda34a81b00bf5796565a5bca4258eae531f 3d1c25052eaeb43c950506c73a61cdc464c3718a502d77dab4bd23eb34093bbb 386800a322ab462444d1bdb3cd943642b4532cb4e6304270d56606daec593bec 898e99644703e2ab485bf42197618479f68c7767f1a81a754254194df7bb5541 8ccf33c9539ce3697c1ec001f6374d950dc0f5888fb2459f0bf5f1c12dd3fa09 5 5d55b880fe032c4b5f0a7066573260b4b2db33ff8ae15b325d6ad85a82629d0f1ebf913a3bd8ef76332ef71a5ea7e60e4a7e1d2434e243ef70ff00c27f4a610f3aafe8a597d791d503bcae33120755248d622a5fcc9c815646ea105e0342e10e2d0f4b54539a67203ad6a65963083f6daa7b3f9d80edf5dd910ab2fda07e7b05435154cb94c1ab236bb3c9f898a51046a9bb2f4a8542b4dc30115ece5a7327072b79e76d8604a424ee065a9fddb2ee17e85091339bd5aedcaee25e606ce07908876d81f643ce0cc276d1d6a24b9b162cce63c39a9f868aa9c631e4247a2d6804a449286340b2a0cab47f915c301765496bad6eb1059cb6d7fea2c8dd461277085a539877cdd601cb6bb4e15aacd0cf83c3991c29a5aaa63ae98cdf12be23a408f22753d6d5e809e0fc39ee3d7f384b5ae57a03901df297d019c74ebb4642160009a87da730278191f8a7b29b8399374257a4947e0451aa05e2913716c595e50716f5161844c3f98fe8e6dd6711859a1cff84b4d0db0f673740a1818b5120200cb5fdc3a18713428e7b3134d5dce455c6418b0aca3f16d8a1b8f099de21515a028508ed1f398e780f0b53cda46a45561ad37d3bef7e2b8f0d56e643b318523f0b +generate_ring_signature dccd54de1c3418a71890e09bdaea34de43197a723146a148dfce50806f6351d6 beb57de477af6fff12b965fcaf9ff7229f670af5466c040902abbaf20ad26281 227 de6f898395f9d3acd113f747c1ec42b3c669642e954e078a84625e04f1203a86 50e0ec8c3ba80da72d39132072ab6db97355bb6d689df8d49323d6519f7afe76 33214ddb5c37f8ee1f71d77ef8f8389cc93ebe3e0750f0ca750067116ba6dfef 624036187cd190236a3af52cbd8ff1551af9ea618b7ad46c4039956372f2b37e 4d8a76e47fa3de3b4230220d19f147eb2ac1ea9cb0e815d5210310474bd722a2 9dbcb6dccb566b9b4ee3d9f4dd096ad64b86de505861a4b27007ed26bd7fa057 8afabd34bb7fd38de6ac26b47d135d6a0c05b890061de8e26e719e87d1140637 7d8f3290f75e81a26454b06a408ad4a5a025586eec86b8f1d4a20c006b0cfd5a a81cb1aa8dd7b2845b6f62cb21e37327eafab75c305977a487c7dd9cd9b72285 586bd7a57d35523981bb88c3a05c38f6ee6f7a4596a5c70d9bcbe72ff94d4230 75c17101e9fd6868e7881f5dede9b009aaf2e6945858c286ed121c3b912b9466 bcb29dbd41f103217282fa56c1c3e49a30bdc22c29f490a0c87f15979e90db94 10865f15bbda6df7774693537a26ffecf4f8ed4a9be5ea8256fc5128a5f5ab24 79c038c008b87d3425473433d22a6ee12f6a94912b2b473be3525df20304632a d2ea981b421db4622dac964447a5fa0f0b91253cd985fa651a0a94bb06a2e531 40360e8d38645b50466c06c3d2f366fd3b41ce9dfd854b4b6a6bbd91b9f2bbbb 85386e78ee0cfdb9c053759b698aee59487b080a3f9bb73636b6e9a36e80ff91 938980150ff4af1e402456d64d15e1a932f5b9ae0d03b7122c952a35456b6540 aa274c87ccf47e58a227424fbbee715aefb91a6a624217bd3ff62f4295200849 17ba30bae32dc6e3aff58c7b865f28ccfe8ad56d1779c3a40c9091dce280cd21 40182f7b319e2332eec3a96ae080935a8d06376a85d50e1dc1e54459b00538a4 18772a0589e44334bfdb0154f4ab5168e7cb33c2aa77a7603425da7c12d6479b efdce92d903112472849ff28ca2f7e312873d9325d28a378ed723d5fc2d0dde9 c874df6ef1ad049bc46597e1de0533c878070fda32bad8dd6139e98b04a44af6 09d67f7cc7632824c92065fea6024d80f1a5c9c2fc97d66150a5e77e9b069fb3 7bd68164f521e0c47994aa974693f8bd236564ad73add045a1f06d5d9a42da0a 7aab16af4335f06f3f24f38806b10c20ecd7f75e16905a97caae144393c880e2 096bfb98ae3213965acb6b92a60066b8f8ee495df93e5a2944de1646c369ac30 460ff31e4a5363b5aaca21aeedf944a713196ca387f438268f5817de68c22293 2ada78c71e899bf785980d8cd94f027e365684eae7c4292cc7da633e00edcb90 b84693047173cbc9166b0ad16a918d2dd5cd5b4dcb3175343e2a061f83623e23 7fcbf67da417193a649c5ca188c3c330d1615cf12e6854cb45a7899c24e08aca 1324e3c54e30e3276fad872fdcd7ba4f9ddc97cebe3f886658ac3767d60843d9 e555257d3f2fd95dc9555f8d89ef1dae813d0f6a9ab1e181f7d5ada73c8efd3a e52d868d66be401ee50f7eb5d74746beb94f677a4254eed91ab753f738e75da3 1d7355aa5837df46db3e8e8993e73c4a294d646d190a73ba40aa3ce841a83534 1aa2563077ff97e778155ceb8f8b4c98e45295e005fd9a5202e62ca462f995e2 f4e6929b34d3833940d5d30d2a7b0ccaa425360c42effb1940a10f49abb4b8d6 960da78fa1fdebf415e0baf03b63eff38b81ed99639186748188c348a3d5d8e5 ab52db15249b26d53920280b2b4686454ee211ef81bc330128e7c7365728d36d 72182c9dd9736c191efa97312318003e1550e18af5ae7b5f57b75a78d93462ae 8a822606f03874e68a1297636b7ddd645966b342ed11e826390a85d6fd13c3e8 77ccb0cb46f40123a379e014f46103cb21169b8f8dc376f34602a61ff3a164f6 c4a730fb9e281d183e142c9c6256183bba9f5871290071ead468d2b5479e2cf1 def7aa66dd688039565fa35a223d34c83aeefe5a6e702889eaee554028efd3e4 c42af9fb89579eb04d706df14cb7c47aeb844fa1c81cbec70512323ab608eecd 6f7331a1adfd3751d3edc5dfa02caed59d35813e646c474854241c7573beb1b6 91573e6964a2f69fba125d7897ee25fef6b0227a51a2deff1dc9205ed0778ee4 ad9bf2a571f586d8368024107021af31d5f5751999b25129f660f5b63cec9c97 0837392fd063e15910d2192b3c180228bfc7f12dd2428a81cfab8f91cdd44bb6 9c363f58e7440049d17164c4bb1f5a8a1ff444e1c26cc1ff97e43f4fd21f1b56 7e3e818d6a8d02896dcb8a7d53add542cf205c46108773495810bd8848d13e3f 4788b7a8e7b695a8ce58a3d61a7a3bd37d2c65cbcddf0f8402dfb8a5b1996394 fb352441f47b8f406cdb638ac64e7b6e8088eefdc9e757b9eacc7d3f72222a82 02ae8186d25a835ea25bdd979a85e2ed9681180b51fe5812ed67f431ae4c5f62 099abc67b8f2b1411f365db32e1973dcb2f6193e674f5ba9615cd3665a509a22 f140bb04214d43c84272acd28a16c49e2b6ac054ca2edb06542c2c2a764f6a80 cb2a56e6e72f1017eafb033c66e4899cc0da2b161c47265124ebaf0ce5c4fec2 838c1eb32105b74439cedb6bc89f68fd1e1359841994b58a4a7aaf5475c54323 e1c28c4e44817363335668ad1b2a38b2cf8ba1c0da151c1793d6cba07af2c5c7 3cf16340f818400832e2f4d3ac53242aa822b581835c701be359124fa74d51da bab07fc6e7065cfd2ef24b0d99bdd0a37b7c3a94890a0ba6e112ecefd9b59fcc 69ae4430e581881a5666177cfdfb2aa4d076e7c7fb1fc1df85f4a42631b652f8 5cb788dadfc2f0a8a71a53cdd95e973eb56ee8d8120caa38c9075b1e1e00620b d37ae42bacf5b181aff456091f7142680b056d866e1dc537b4c9c7c47d892068 5d8879fa016d9badd76b8e69f77bd6f02ad5b0ef79323306e8cdb3d3175fab59 b7e0d03bcc3356971ff4e5f2014a1ea9e2b8a3f87a170d25cd334ef440f1cca3 1cfcec5e2dfcb2243be40ae986f597f5e62efe2056fc0ae0f093a1ef8eac4c10 d45fa110c2c973d529ee68f8b00a7599c9e9821af0006cedc1ddd6ab3e37d9cb c003ff2afabb78406fdd999a1b48796082f4106fb616ba7afc27b475f2d78b79 6579d1ac0b18977a0b4a3184901abb13148a833b29445112fc0936e7710834e4 d2742bb8b5bd2fc348adb9121eb048b842ff428edb4cc0e628a51b235453c183 bf09a5bc58fa250176148cffd82244e614b0fadf94c23e53abf288facc92510e 6d37834e2372f26ad0ba8c510660ce2d74a495419065d78480332253bcb686e6 56d85f362ed1b9d5cf0cc9b22b58f704d7a1d18cd9799621c158e0cf6eac263f 15c8bd061f78d454c02a67973a18883014ea1a506740e3231a9b942b405099de 444188e9d62b697974066852422a091f9f9687895db8036c4baddf71bff17b63 652c85a355ae256e65bb47ff48c0f421633806c127bdbbc99b11131986a17476 d6d364039ad279968195fc81f2b2b1b8e315fdbf21618d05976eb434533ddbc5 082d940a66f319b4f2a26578dcb0277c9dd241d209f11059aeee396c767d6943 0f1ba3b82c1278d71c7ada842514e9e8a6b03d49925f279a55d84cecf4b8ea97 64369ba0557e73fdcb4fab986fdc58db1965fcb1b5fc2baaf7c86bea7e4cf0a8 c612977b21bde87630763327ba5fb9bda6312a03fcfed6aaf40b56e65c945789 8463e1860d65d4b64eac0d5367a5af5bcfa203c16dbfd083353917a61a74bc14 4ad00dd4cb6e7b62c74a36ce1ed7720b8c6453b570677012091f47ec7632759f d544467e93925c0fbe87a630d5a2471f3b67e79abfa1f8d3e89833e36d57c7cc 1127b1aa8e3d8bea0b4a9184720eae39e1879fa973d87966cd3fddc8901e0ed1 afae49672415a9cf1aa4f5d5a921e6ade299a517a5681c5888e1eafac35caefe 1bb1d53b9801aec3210277fb8af2e473a8d1026495c0900735d89f91adb54508 53c979d5d09084b0d729b5e740ef1ed51f99cc416b3096869eca4a287598cf60 42da4fc63c86c7a13bed979e30195d45e73d0b959865411465c2ccc30983a0e8 b0e4fa801af1c5c2834b31cf80831fb11d626020255f2b0bbc7c96465f523288 2af31e59dcb552b4eed7558885de0cffbb5cfa7217b423c590017588b488bac0 6acfb4632b22521dbe49fd353a239848f53e7265d43c89b59da8b0cc3f8303ff bd720e79385f6d2ca5f70fe141d02b50e21648cb3f959daa425de1e4392e5c75 0e3e941a4fb0c523195cc6a0dfdea6f52c566ab82c25483b9a10a9d11465589c 85fea41341b6d49d4823e309d4468d7cb655811177bb8a53e236c0747368b819 32593bb228b2f454aa091fe6b2e4716bfa29be3089306895310b1f2f57230c56 4ce8bbf39404f890dd296a2718624b8845eec23023437ad83dd3f90e3ebeb52d 434fc3946e64e1d07b97b77988cb48b8ae1133912dce2b9c1902bb9c83f63886 14354e897eba43297579a03878669d513e37ac42bee8e7c64e354ed39ba6ac3b 67c9bd298f98c083736bbd6511fed9fb238ba09634d630acf8c13fbf5d4139cc 3c77c3a7286acbf62237efbbae13eac56c4c0bd55f7fae2176550ae2f49b6b46 5149f6fe63a8042b6fe16c6e31b5b3da0245bb49ec249045152b1814e777efa0 e9c51ac176861a0c5e72275c34686f38e8fbcec99f81176a4f280e50c1d68de8 6d2de0ce0adf4eef46e9bc7a082ba9c45384c5e2766f7cd374f6e7ecf767afe4 7defec0b1eb6aad14ba9f886a3bf94df39f8f9beb4bf3dcfdd8e9f30c74b62fb f816bc04b50cb419e5342f8d9c1c45f61e8f7ee1021271cab7f60eb938619856 4e740f39bde4b3ca794f5218df1045ad330c992f198de22c7b1581e585f6e8bc 2a707dd3aa3ef5a0fb89987ef90884797e9b679941563b1b0f0fe482df2cb704 780363e8eaa858601ceb6eca2a95ff77caccf4a4ea15893dbfdf1d97f6196b5f 7954b47f519dd86d64fe572f5c83746dfc6cb7ffe388e257e5a27242705a615e cd730bc5c83ab9fd608d5e4d0ed2fb3a090e993da29c0341120f94940fb31a54 50eaa1323e56b6cb217c2728120f51cb7fa19c2dce41be26baef95c6f61cc276 811817719c3c0541dfeceff7756e80f7c868d56d6bd06feb1027a0c37b9957ef 1221701c18e4cb5e606260d2a85c99433c136549ffcbed18cf2e843ac4118695 972eada79c154e43a5ab8797202f266809d163f8a1496bf750667667d1f73659 812e63c9f4f8225eb94880915c2142e26163ef6d27d4a240b9bd3424ab0568ea 65e81550555313646f8ea5d2e1d998039c087825103a1cc9aec56489d6e7cb4e 4cadbcd7d0be79f6f32dfaa6a45a7479081bb824b5029c3efcfd51cd218791db 163486ea2d379505656ed6065a355a2b5a0bec2f1c56121047e2513708af0ba6 e312f142355698ac9e4705286e24ce259986cbf9c475be9908484ded0aba457d 5cf113e03b26cb54d5f672b6997a44b28af8421d258d1a51244abae07783dde4 7d19c56181925f1502281af675823ed9799978028ea3fde25e7cb7f2b2bf1af5 561035986edcd4fb791798407f9351b9d15ab7ddec1b1156211e4372f65c081f 9c2d63e65438f187250b110223fc1a41600f1516b75299d418698ebdb278972d 8db1c5443b19e83d124cfb93cb7659022e0ab39ca30fceb1cb7eebccc04c33e9 2a0beb68b0cc5fb35a2c8ad37619b7faec3a573380101bb1f3f8f930e22f17c5 95a8467c529e347700ead64b0f2b2520edba9317e1988a5e05b48d88de6b23c1 1f07b7c84d37af770b0d4dd3db8031fd8671db285db5fb292bb24b4cf03a66c6 03c26aa45c9344f2108ed8bceda99db51e2b3e476a9507305f4a264cfc22487a 7acc92bf21c416c9c2853cdbcb75912d17768c0558f45a4f9dd89f9950c04925 570dfcad88ccc890f989f6fa90445433718d1a33c2f6cb9d06f01e105641a0a0 0e703be79defbf776eacf28e3c02faafd451295528af4c3c5abb85af27a26c6c 8868637d4111edc6b9967b4817419116ff4e08342f94a1c919f88cfb66511db8 ad34c041687a0aac2d5b95645c3be9a092854f05ca306fc4df87b66fa20ac854 3c9c07cb12c72cdbd9c0a53a56dcf49286ccf1443bb46e389bc7f98e9c1e5a08 0a73a13db0b8a4cec8adb5f49b3ef1aff9116ea1b40907e7d7d53747914a6165 301e0e2771166d45a7db4739b33e67ded6f458140e03b77f7dc37dc10e5795c3 456ac73e71f674bd8436300971c616ba64522aad67f199c8600ddf5de5a5e6db cf529e39c63f83fdeaf309fe226248dd7c0d8ece4889249b1ee3f42802e84bb4 6c593de6e012579b5cc3f43dff7ac21e30433117f39e1dc5d17627dbcccfa8bb 2310887b528cdb93fbb62059afefb5dc8bb1ad99a6c26f470e8dafea9a21b75c c874e7cda400a4a7d347b9ccc3849b871083bbe16ba4a0961b5407d885d885e2 7d85f52aabe672cd6374d1f45ee2b8bb64bc9aa713c9d5b11164358dc0618043 ee63fc1d84e5ce7b6fea05733df25621d53e2f8b59d46cf6d3c206e0a1a4bbad 1945742ad66ed67a7e7be7619553eae5772137b123efc125cfa56771f73628c6 16578d8f6792c063ffefbb9fbda79cc2cf4a217a15d09fd6d9e8c6f5cb3edfb7 2fb93dbad3bdd8c52e66bdecb845ac12df250668987a46533e64ee1702586c11 0dc58bb319f98d536c5aba1b410dd591de2cfbbcb9626004e73f953989a09063 8146f9ce2ec99a5581481f5f9ccc32e31550925b16239c20adebe48c2a0dc8ed 6fbeeda5f57a6c3bec5266808f679ebe1786ad8556df24452d9465ba0163cbca 434b44876cfb72d61daf97f0bd8aa85d323af4320f52b880dc55a9c06c3c1463 c13ff3702adfcd8acc2a0b6e4e675b82f503549dcbf14a7c29f66393fad8093d fb76c1d8836c2a1f718106011e3dc5ad544b893726fa72b941d123ec6a20f785 2b7ecd72aea8643057327b542103b08e00b79ae6198592dde61cd62929089e83 e7cef581b5062606b00a2ae4310940b5f18d56d2742705a8bc3c7c54042c1694 d5e7cf1bfe62c0c6abb750e4b22fdc9b11355e4b34e884c94571163d21a81d6f ce69ce14cb9153cacea90c7fe3e8460653d27c61d239718a937f1fca35f99d17 b2aceb0c7bb9a7f865b3f01d06eac0cb1d3ef32a4e2145a49473135641276c0e e0dd5fac25d7e724aa18512adf237fc463676a5d1c9ce42c6a8b2c5cda013506 f7e58fe431e81b6fb8d5db68f69a7e4596c76227c0291f73f9754640085c7675 b63f41f757fe3f6eb32d2b7ecbcc86dd06d3ed3c68f22e41db42550913169a45 3a143a89e143e860c93e71c71f42a04ab75dac68baf92da4fefa6c6b173523c0 7417ca2eef7e735a3ab12e4a0e3b3c2439c71dfad03014200b8192206acc78f1 3e0136f18d38405b67567e2ea5165d8782a1ced39df60f06980d3d2ad9d24f3b 4a3e58971a4e4f94ce12c054249d1fdf9dc371bc7b5279f78197800c2a51bb46 269cfdf63c238721dba6bba1b839759bb0fe9e83d609a2901d141786090c3f94 95eb48427cb6c4f471c2ea568ed9df6633862be02cb2ca025c73b2d624b056af 421e453d565a46650ef8227bace09af7c068466da857357cd9b2873ad1ad09d6 822692d9465ea2005914901f30869c138f9a6351ee06b318ea9a26d443d97d39 fa23a03fc5b0b6e1150a54fe2bdfc78e925f4a66443f7e4a918a7c44cdcd05b4 c40394856093b0f40c439a94508d1fc3cdd5d6f1747d7a876c3250deb24261f8 0082526a179e1f85f2c456b87355d540144ae21c53cadf9783c5a00cc145fa73 91265fa8f7e3ccf55f695c17a0004ac429cfe485463c2dca00a116b84883ed3c 81dc3104800931b6a4c5cece8d3e2e8629722bdcc210bda4cbcbf9871a43edc8 aeeefded266eb59949423a42d3ebc1af07873e1e954696bf44daf3a75e7dd5cb 230cb94219fe81dec527b602ceb442d2fd047f5f69f94e5aa5229e18ce6eeb72 e495df1e8aa30144401c58dd35d99e206efef5800b42ae2c855ba2933ed1641f a3aea8193f840fd72d17b18a89b937a4892fd267a117d45db566c334763275f5 21df42a8e0ebc06d0be965488fa6bc6fd56e9a8071e7d7066e9c0be8a18715e9 199b10cff8216a7ee22ceb37cf3c03d219233e65a473f26fef00137d8c52be50 69375507f886578c829b830f5964a71243a21a88b9aea1555bb34b136776ae64 53d95e59a7cec2ea6ba975536d48a76fb560f61e988a7d5e27b2564c4e429450 9840ea72bdb5e374a4b515fdbaa737f50be2d4115e4192d0973a652c10d553d1 ad8d699290c6ea3864c6dbfe1fc2df282a7033a115ac050af16cadb47f1814fd b9b84b4655239182a7c635dfda8444a090d56ad9f36b39e49c9cc04c9b903d5b f5c7569fa43ac9d2639d582f90467c15ff8edac68da3368307be710019cc73af 52b6e9e81e004a4d350d87b19d86dcd759429a09954105b4b884508d0fd2b022 194d7708a9ae555cb73ddcd4b5e41ae54e297c19b03525eadf28030b16c47388 d158efb1f35f7d182a24ae62a543440c932d48b056d62a6bca8da8fa388fa251 6aecb71bb4a7ffcd6c4d15bccca66e1f2bc16d4a6c3d75990c3dbd30800d0282 5ecab698f65743d8bcd619885f36e574f28ec1f665847dd87ed66c7bf94bb8f9 5824367f4a299d2f99dba1bed610b421f6486ca776d3b1b3e8ac8e9c7567b4a1 9b1a0a3bd2e544a7cdec5b13861e77c8103386e57710ee6f31ee7ac777d3e089 eb852b565d9a9a4b1175a30bef0ed763c6d2d0369dee85bc28671c7d50dedc79 845b3fa7475922b76431644b6be2f0cd48c997ee497b0e3ad7ea8c3d1061e1da 1e9b85af0b4eaf78c85ff0089c6888b8cfcdd54f876d7111a3df6f96bcfcc678 d30f8e5b89b088b901c943e89f093b40838b706757139eff1b47c848edb286f9 aafba65a11d917cd3b38bc357cea2ea44cd018cffa6fcb9045e8b35501f93ed0 931de2cdda585f0ff0f7f81e747c1c5b20ae49a930a943871c8581dbfa5467e3 6f7ec2a05580bea3790eda02264a371e19ea10a05818b2d1f9df342945276a76 f5a08ffbb5b9317ba9281c7c77f3f6b91b02a17b8bcd51b7e0b2a84bfa0399de 4737d5fea3b9e3567f1c74a6527c2be489002195ec96c75fa7a412fa020c4b90 8693790f118dd039b89e51cfa8b7887c98bf8145dba4a981adabf3e354e45d77 b8a660ce9846d73f1ebb888faea6345ddc5c2124c9eecbab120d6ed427c59f2a 4b1ebf45036321b68ec8b0543203d21bf9a639027fe58261a98b44c0af19bfbe 24b6b654c8f53fbe508e669b3804cb9cd55056540a9b7df24ce403888b3999d3 fd9930b13d52c5d70440304d0e1e5b54a4b6b1ca2a435677c5f118f07e2b36ee 717eaf9e1090b2f1894e4678602a3cb5c0f374858af6b0d4fc723b396353ea1d de9c034a546b2c6cd88154fc9eb766f30b986dac38c3cfba22b16ddcdfe9dc7d 93c221d3be2d20f4ac343b9e67beb6dbe181acfa2e03e80b536b74bddd754f38 4e3424bb19b878f1845783d33117f315a972e4cb8a4f5f39218ad3eb081e07d8 4f254a776c0f3bd7779c82c22c33390aa41909c89ea9bdcad91dafc6e4003050 2050b427b2a2ec9dfba6c86014777901852742c5783c8e89d114938ab728ee18 9dbfb19eb52ca6e2ae0ee44e5df65bcc5e7439af40389244cb3b54fd9866aa26 d8b8df21ed6caea91e075382344d7e63de02addc86beb7d76be24ef2155e8353 22662c59ef7015896560440498100035ee0763c5a6de0b263dca397058523c34 0b8d591914e20c40bb5fa99de44ffbaa993b8fb08938e3f5033c07f9e4bfe881 55f9e2ad6e2699c81c525daa2008c323a0978a3b85dc99d591dda69f0aacba72 d228415156119c0157cccc74483b149d443a4ce32971461239627b7aa02a4b89 e8c7e96a2ffea407a803eadcf5ddc6254795c329a6f7332fe8220bd26cfc83f0 6e14ae550d01199ae9b978ace5b382fde0807d8671827a75f3951b18827527d3 259bd7e461198a3ca17d4336ed9f6214cfd10404d3a6f6a9a0bbd87c26bd9076 25e4b2650205fe5dd3c4b1ec5677389b00a4912b5810622b505d72125200a42c b38caff798124570938f7d2e92aa8b0a37229020355e0525069cb2c2ea16eebd f6d09aef0f9a2820f555475e44521817a2044a407da71731aac20fc4b6f2c26e 1fd4bd9f88327912a6c6ccf04009e9bd3cf0fe43fd654e1fe96382ff0ca4c509 35 7e308f9108d9f3921d5962875e9b937cddd86c67f606ea8492b5f38c1502a103121e3a84f0121bd0af0304d84533bb273d4d7c58550988ead7c9303ba7360d08eb80d12d809290ec94c35edff2b817b85473f87e4b58e26a356017bdad08a60ba11aa5148c5501c75abee978b9c04b5fcd775977f8455e76e52044c51d6ea503ccab1c00cfbadb06876ae1e0c9263e459188aee53da19294f5ddf5f72b94350f52d3c66d8394111a8e6924df6e039569394f84dfc973130a06155f5803024b043e69295f2637c10e5ec54538cd77f85077c1b0c097cac40f437270c289b5350dd3cddd37bea0ad347c914fd540de6e52fcb0bc585e045ed0dc1c92de785c570e9eea914820ad7346e3cf1b1ac5f6b4569797fa85602321442b6856f8325ae70751cfb870eabcdbf06feaea72edbf929ba302a5f658031b6234b5261adb534a06da6dca88ac50da346c000bc249f183a09d23c79cc6ae9dff2c100c8b234ce40f1b4ed62c34da2f877a8686282e905842ae30654d7f4e1ce7aacca0ba8c1f780f69e34c2de0a9839ae12627afff7280c23e0db49b5d101c8b342f3282edd917052e466dd7b71f2e1f35dd6f33952527c28bbe51138a21abb702b5a24f4425400bdce16ede18534548eead784e03ce7e331114f7ba36c5ae3d4a91f481861b0701ce3d999e29e2c14378a1fa7064abb54cbca5e6df0d3f290245edeedd8ff3910d6a7848483b0f8b623b1fc92abc4630e71a333558d846e86e53a73f14abba4409c23cbf48ce04822d7db21d501594aa59251a5e932cb75c968b5c752c3709c4064aeeb62813a9c66c3d67f9f55329bd2dad112b6fdcef7e58a9f18a9dde4ef30ccafa71f2e7132ac62217a4b837ece3ed2f858ba5f244a1c05fdbaea5b7cf6801e18c1902c1a4c4beeeea4ba074e786471c553252e479805dda2d97edfe7f16033d3ebdb9c86fa3eea9c9ba992d0af03e98643ecf495bfd6f7a7c15143510e70e833719e3713de590e6837552da1ea891034447e232818b7eb8dbe9732a4a4905996b121cd8fa375f761c31cf6e3371c98f71bb30c7765bcdc9a4aa6f959b0000f3cdaf5d4d2a12e9cea4bb5428429a1b0446f1b6749461b18d28260a494aee064f8a1f6f92d8be76dd9056ed37bf0dd0b8362c026c90ce49a85a52bda6f7340a900a44e7a673b10b42e398a64c8ad309026acd9866d0df03a46ba1866077490eba015528244082dd00cd98fe0811fbc9ea543ba296826632ba3d2b40ef79be0a1f5839d89034afca07073f656d3742322b0545c5f2c8aa300bc1868a7569190c4fe1e9ccfd5bac13fd99b797c9b0571a08bf73ab8adcddcac3654c24f08200024b5a82ef4b1b99539b7dc11e57a18dd2cc744e12bb19b3060355dd0540018f05b1894c3ba57c3df94d78367bf49e795d3d3918a3b71e1ddc31684939fe49380706ffa3267749183987d9dfa191a30c79468ca27b4e48ce7ea29ad6619a35b2046442e99e8772a5478c5e2668322825267cc60e71259e1ee21b0fd71990911a02b3c0eeb2e32f589d42a9d7e15bcd18aec78f11f6d3bc31f0f422cc2f0afcc501f2932391447ba4afe5da0bbe7d783936b8b70bf758f3b28641f07316c69af60782dd9f89a4d75596ecb66e27bafed76186a56156b2c98fdc54f96272233f4207fabe4a1f5e647add04ec9cb25de359774f844a86b69f8833be44beacefcb200c143534426aa64d28068e1a84ec617a0ad31995873e47fa91180460090e4c8206123d39777fb7d83ea96d4d13a78b6e45076e625a227c79a6fe0410d2e42d080ee386f376655611fb1e9ea539c991cc372140183fa45a9761bec5de4da7353c031a4a6463ced9af2c960b6bc31dc43750350032be1a509fe1a9f6b3a7b0522c0ce9340b12f8678452c36b065757c05c4a2690c4d47048d05cc49d92d667e8ff05df1fe2c5dc184b4793a8b2552b4123a35c93f661d7fe78441cc377fb50c3dd06e6021e4a1b84db5463e801ebac2cbda82904bc076e33a78746d94378b1fa330475b3f01b6523524816c3967e57af098b681e46f23c34fa9af4191b6fd5f74d08ce536a5d770924f875d886ae885695ff96cb6d1a4252a0d381fa4dc11b92c904553adb374fac14369cc66101aadf70317ef7d0946d9b5b6019aa85681d49bf02433b99b625f001c652bbf0c1256b4a83938410297f8467a6b035c8c24cba2d0afec80bb6dbc6caa0a4ca980298e03a2c047913f23ef07b26cdca76705ec3650d630fa66f40c088e4a82f517e9ff3c26cca3518953405d194e54726a3324a8a0cee9257d0c63f1f6f07119a5a3b400c1d7d9fcb0d3fc4470c84eb40b74d14710400ff55fdb4e27e4be497c28c96f20f962901fd8b1a9a25d2f93b6861a1afb403855923ca02f030e18d9b8409f7ed72a020fefc0e59941662bfcb5285382f420119f6bfe18d79b078c30c4d2639001f578805abd1f5b022df991525acde1abf08fc3435eb89634ec41b40baf9a4c6b547d2142cebb06ec2ad2f9e0af044655f0323aa305767a24c301ef4eac4b87f9724fe9d9d7c5427cb07b12b1270decfd9062976a2b8e5873082d95774cc0292d361d863d96915f61be5e02898255c050f04e6e7a89f7664ec47899e55497deb3fae8c9dcda6dd8d07587c65c78cbc93db0cd65dfb91859a94d9ce830a9c84ef7b52ede7e721a1c0c3dea72e26fb7582ff04c2fb7a7ed8445136ef37ba1b91945adf2bec1f3da834e3a3157037db7634800dc108d32bd426d4c1045275a2b6094aaa58f0b68b4fa258e8fcc4b01b02b8ac062a37c09be69c54830efa33c46b00f07816e7a845494896f96d8f6cec3faa8d0c222b00da1784ccf31019e409eec43e81f865d5767e35fdd9ac90725766321e0e02cc4335d67be30c859e2e66d09f651148e3250f3bb15bf444b87b503f20210b44e03a233322db8ce32829a58982b70439c56130214c7bda9fbe3d40eed7fb0e0c3a9e68c02d43a26683cf166521745c1935b039b3728ab51a9893e361057a020cd3e9e91a11b8c0df4bf8ab8f6c8b0dc15d62506868e1ea72d919de2b0f920ef3722c910032a1a4cf27870a7a0ed6decaadfdd529d75a4e742bd66ea4a5ba048e9d1d8cdee7f0dcf3299bfb01032aaaed959a6f7b7b2b2738ca3f4788eaac09202ed8db29b02d6a7e92c01475fbfe9253b897a908bee5ed1d0bec66a2e5d50865c151c70eb28629407c003c314df46afca3aa82f207b690ec8e4c28a3d5660e42c335d028a33ad9ddfe2fd4daa8a25cd2a5d8c213fc69eacbe64a97044dd00a5434a5d3e20e771530c72065c2a86f2353d0600e308a8855377da0c64f6bb90e9017294660d13ed05302631086e5c2faa87263ab8a85f620c6835898c6b86a0941843df16dffac01d9c926ba0f02ef85a58dfea5a99533c95b110c4e4c220b0d5cff8e6cf98bf37debaedf04cd25bd591368ce6ceb39a635f695ddca1a69ac00fd3b1668e9e9a851da6fb6975c5f01cc30b8a4ad91c13daeebab72f15bcdde0e2a2728a60369eb1dc7e96f3b5ded85ba47b89bc0b080cd7638f64b2c1992e205f8170aa457f84c5d831b2a6bed84085ad9899ed0a09b1a88aa0a24d069c98d07426dbafd82b30ff6baff6964d0f41fddd6719ff4f13387a89acaa62e3bd2b501dcb0681bd3c08d9b6682e044dd30bd25086191c2e184a5780279fa69a79f550d773bdfad65b31b7520749a0a2362c3347c0263a13313f7dcb9ed079786e8a503e678fb740aebd8a8f0995b8ef1f87ea9e83e3a459167a7ee260d52e6f0ab940ea8f018fa2c052bb5ae444fb5844dcb914426750fe05e99e71566c9e44f0cd906bb2c20c508e1bea70ea03310b53a87dfab6884530f76b64390954ac9512788013a876cf8fccedd160493c05ef77474050f19570b732fc23c14573f9ed628d00a4abec2fc43ca826b7a77e40e4bc38af18d04c1fbc695465923526cf047947c016901b45f6cae865346f580da8fba6d38731d95155e111f0f5ab4b18377056d00289a8d9a00fe70082e1e7dbeff8b8e5c8827b30a8dab4a2f93ce7526dac5950e9303a08e6c200cd95b25758e5cb4caa5dc3d3ea8814a06338e8e753d6847a10b41810df0afdfe1b4564b148867ae98dc136b0bf3d6e7ce78b59e38e885d3c6001de7186f6024b5d1df8a5798af652345e84d99540c7c03425aa59add7b10560b2466bdca353f3148eac51f0f020223d1241091426709afbe8681cc9a378c9c0f54648c49751971e97294f14d4b80075a2876911b80b373a1f181e9113f2fff038c893ada1cee7b7d1910f1701374aa255e5d991cccfd3b6c3936495fb37b7201935d6a83487b882e62198ee84a75fe9f6ad09086d998b0f2dd72067365db8c096030cfde05b6243df956207ba3f41dd20b518d16ebe94e17248df8d6ed3654069861e13c4da96cc3d96d67d609e01bfa42c7c2bdc6106b918ef7190f20cc730592bd1fcf0a338df807152712c4f88695d202de2c132c4d00696a6f6113bbf1090d3b9cdb98cce551dd3780e259c9be821b0d71defb7f5ca9747372f589257807a18fd5cda88586cf49ecc6a019293cfb04e7d542106c74c79791d3cc763a660539cb649c4cfc8593bf95e131609cdcb3b51dba3022d429949d3ef92112b9c709e815a19337ebad3156fb2fc0d93bb4f2042a012da19b7c6baef7b6fcfc6ef7050a0c56c816b314472a28c30739fe5f5c34b3199b316a34625ec2a916278122085b4a5901e753908c60fad34fd5309d2897d942063d748d73c2c34fbbe3cc5f0f562af9a32ff51222487ad1a63c6e1094f459e648ae2ded85c8cccbfe8f301d0a9c9c2bb1b3d99c2f48bb4b0c35848f4bc1835cd5df5343b17b65eb51dd47c2018fe1ec2f4dec7ce976bee8a5f10a0d41669c773ae5f01d503b8118fff4a0b90fcd4e610588fc781edae15c8339b8889e2df6ebf78ecd3a8d4677043a69a2fd017a083507c67396cd1fdd8630408d71c16db0254b5ce5714e141ab8b4652d34087e5a2a1fb9be9688e1bed67e2bfb38c9ae4fe14b394daeefb790625c7a5b7103845bdec58e32783ab54ae7d1890e1c01fc7d0ec9062441d7bd9249dd53dcd102e9f19db13d5019e20271d38b2b9ce8b57d321722a6c6a986233937ed4d82ef0a0589077a5ce9980c4c5f64e5f8886f6744ef0392cdf81c89e5e14d4ba81f950001200a976bdd964481ffdc47e0e5a28a931c95b5d8734df7f93c3875c57c44058974d666a4f3077e13103b52b3228670b8149c4a0e44d07809e35342d126960863c3afe8e2dd5fb7940c235e5bf535cbcf1cdc6facf0d37de514ca07c4f6b80ad57e57cf80499db6ac9d0abc5c3b282a0bcc65f642ab02358f12dcd0f89a11093e808f6470ae209d9bdf9f3e1c5246d9669383183cc94e8f7d339d48adc7ea08b428bc89d26cfec99921488bce8eed207d9856f1cefa9ba69ec9316d04fff50e4a297db97f966152fdf849724e979a529e2fcc98acb39434bdf16059095133003f05ff953c3128d2157134f539f4c68e61a09d60a0b188f28e05acb6b5b22c071f9c54958c3ef46c281ca99ed65c7ad6fd4304b8a2d927bc1e0cb4c82208a204fdea679cb68cbc95382598358b4e8036ad186ea9db67285f590d6acebf3c910fc3acfdf8dc27a6360b3c095f6bbb14e77c593f834d237f45725477ce97177f0c61aacffcb65509e3d5a2cf96c17df6c53938a51efbca665c4b0cc9383ead280f2384427a134de05aff0df35a3fdef397e84d4f9c46de86b1282c24523b6cdf0e0db46e5b33160606f2f1d06a20076ec5020701705110f9193dca35e480a91d0389b3da164d94668702a5db7fb9fb11960da05f77d112278747ec9dd7dc725c0deb8808ed2a1dc37c24ee6dda732fb9a57cded01dbdb174522cc8f48637ecda09fd23953c1ab7f7cbc47ce6dc66806f14dc8ad6db2e44a0cf7522f51a8b8771022e80cc3188012f3d388ae648b47756970d7e74c1aeb1350e1abe2c876fc915025368a506ba5dbbf56ab8a019d6b63fd03b4a526e475cb0cb0aabbfd2348fd30e2280bf99e868d2220bd936f954037adcb6d93ad2e6578d24872db2afbf25b40aff7e03d45003dea6bfd76767aa4cb287b5d8b8dc0571d7aea2c9be170aa7f8096338f26eecd554b123e6334887034dbd86523c6ae1db1458f13d152d6d066a0be1a6add88ad6a2d7e02fdcfde77392735e686ea8b198059b5e364d72204db70aac27b4f6239537699bc716c988a8e0efc0cdf883b967aa84592093c2e3c96a0373d1525cbd7f3aacc02908e0a5151aaf6629f0b639e4fdb44e9e6ff5932e0b003c3d2a13e09a13f77e4a8dd49e5d518f72ea69bbf7491bd7a25ce27d896df608ac898387dc5f4236173f4caeff65954f6bfbc90ca1224c1e7ea2b41e7f3eb20547fccb087c7295adc740881090273371979a2202250f8615bc4b843b629be90413cbeca3532b7e02db4baa9c8cb2ff62b7a8f17c96da0de8a96135b01313740758a0e52e1d12b4e077f3307b8be96239df0628b3c47e152bb986d2e228de4502f761e22a2b74f011c8040f55b56728e508a5c0d4fc8946492f8152d9d88ae6049d45559752987c257bb38c8443b460864b0d6addd11839b5c0bd84c1862c630f37ba19ce48482fb55387611ead9e83cc89555dd188c0357e48ffab30287204085f0a81d9fe7d661ecdfddf37f19c1d2f2f4343fb6e8bfb3739e96cb92c4ae40ab8896b390e8a3f5b936508206799d96483423d2bacdc2cc6af73e8baed1fae0d6769886aa370f610f38b028746887caae32598b41c800571f1994bf3b490ff018655235dd40585dedb9aea2611949f8680c75bc2f5a27ccb162a95a68e95ae0f788361ef0e67e927db71a2bc083eb94d6fa3064bb287b6197a725197cee0f502382c3a1e9ea372d0416185dcfdde027d512c796f25114e5d216aedaf0d41400b4e9ae7f7c97b68e53871cc90130749bc38646d9a031cead7bbed1706d1abd606b9389d13c1895910e520e86fa5bc8a37bc60b92181fb5e83324029eeda6aca0647e812c5e3688ac421cc919126a2324b345049418769507170d324cf131c5d05ee76cfd78cd9265eea08eb6883303ee6b87fcd92e6ea4fe197ab05f74d7f0e0f87ac1bc6c2f7856e493e3dfb28b57e60ba62a7fb3e9a779cc34e3ceeadbf9304ec7c441b6a5c3cf2a51b45c80b4b4d39c7f76d74f3c39dab5514cfb5eb562405747ea911ca5a0243b6b0df4deb8cd31d0c268267ddb82b25029fa52c9cd32905bb531375a95b2987f5ea4e6c82db0205fb74ec3801ee725a5869829fe5abaf0caa5412a2fba581aebd07ac193bce1be8a6ecdc06ce7fc1d53005ddb72c7ff0048a8214009150d6e364cf07243016bd5fd7236c179011af2c29f09b5cb2abbd0738c4d84430f1f330c16d09653d88c396bbdad126ffcad281eb210821baa4de063fb32f803cd07afcce7f83227b3556276d113d6b4c0aa41b06d766334524d805726132fa59e479aa3fca261fb1b0dbf7bac6d3040a8cae36a50628daa91e940a6a8730b5ebcbb8b384f5ca886e9ca9767c567be0bb3bb6049c0f8b4b95ef8c031c80add827b75e22373a80161ed441ebccefb66d1b023a7a6cbfec90f9c96b0a5a2d5b1dba2b308816f2906073cc5cb9c3244306c728afdf17f3a6269e8a540d69f8a60aa4c2f62368a5cd7cb9d9d44b6a8e1e9910f53f8cfc49df4a4c26920ea41b4f0f862a909d6bab09abb805c11444f56d4faa59d53bbc747e4c06ed6e075efc463e802fb9e6e74784086c0fd8796ba99f30ac047cf418f40056862b9b0ef7f31f409e03731f97a72b1c0fc7a438d8baf79f8d0e837ef6a47de55e2dc10ab691298735030640e91277a872bad7a760454ec7a8f8cb5b6b6dec410468a30d9cd3c5ebe00ddcec846681133a97950893bf4034145fb79b5cae017e643b5d046c24f40ec49c9d3271d9a9047b477954955065bdf3a4c0690cbdb5c79d6a6f087c8ac80e88876e8dd21af696a0a625e6c8367bcdfa66acfecfb352a2fc926403a5278e77c75781da2cd38525b339a255dbce80e4ff72215333e5f72969593501a2f0cea5a24458ffd04ae63c5e60331a5f49bac3bc404acfc065b26afe8d9a08b3bec0d08b594ba2bf8a984540cd1d073236fffef040711a7d8360e59e5e7e0b1a6216fbc82a015fca730ba100fc9fbaa9c19970378d8735769d644dcc19450d19601d5a90783afd8787e13c78d39499e1f2aba14b0b89935613049242956507a3b2146549e776d9f9ac61f0024d4e0dfa14618030d758c2edbbc618c9ca8b0ebb121a9e5bad7410f82a49f4cb8b7c6fb14edb276453318703f5253d595dbe0c181ec17dd0cb73381342ca4cb4d5f81e0721f7d2d1e44539b04b1ccabeffc90e91620d25d659b1addcdbef9a851b0057b0552fdda4bc0193e86bf409ce65a10a310a9d68ddbfe3fc33b95394823e74af06880920ffa47b3457669368089be10bc7db22c5f64025c24fbfacec410d6db94bf0e20f6cde7e593d33ca980ac2ad07c0098f7479cd461f3de966a6fb63907a705f3828030a1a37714001eb4b68420a3c032988b60b4967bbd86906397734af2a9f1acf28e6415fc855278904fc0e08e272f2f150e42082e9d9c64af3df5376aee6c4e9c65f01c6f67c1fdcf7515f035995db08f465bac830f57e8768d0d504f713f26488087e1b4f4d83bb4a959a036e5377118a3029df6a8aad18d79915da351166bce1626ad3b70fb103d7187b0040f36a7cd387c2cbf75ca42e1f3753cd914b9b17a2d7468bfe6cdc26556a5e08bb0169076e6bd98244d58a87472540a28c8e3624b71335c02c08381049140e07e7064668a6f7250cba79241beae7bf761b6fc6620353a6de8f827b75b2f92e0ccbe815f1c48d41fd29dd3e1f57275bbdb06275c7255fda1db2da004ac6afc70b43aba531b3d40e8172eee3c3af0cfed3d0b3a4d4e7da00d5fe4679d37ad38908ba924197b8d3dc5036178151ceca051283187965f381e7fe2ec8a14fdfa53300ee5d983eff36b87ffbe125b87e9c16df9012c147b6a58e2e395a182159c3da04bddcb5df1231ec7f3638ee1412e3d06859f55abb79af3b0e760fff6805d2b30f6901838ba2166f9ae285d296e5313a36b9739901d682345c2933ef7eacbf80069490d86f9f5a4f44f7e73a2c7d0a1e486f56bec7d1fd67475bba6c1f0a5813089cef1f2de4f3ab7924ddd0e730b48101ba69240ff0a99ed04bc1a999c8dc1d0b117883381cac06ab840ba890c65aa93103aa29045aee55e393d3084f42b14201353194b6e05a91a886c22292d11199b82e1bbf3e30b329b2e03e544df0e92b0ca3c203f3675ce8197900f960d0d1c23f547b88d3a8ecb9022355609239c56d04fe8ba00b10bd656a62b56509f26ec99769515f9c58864c483b33149890c27504fc7e041da197e7065a40e938bbacb8e98dc180daab05ea379904536bff5cf409145e2a06de08b3701ce310bd2a90af0befd4fbdcf9f9ed1218bab1d2f7971a0c2956d4e98415bd16f3ce7c2a92aba3098d8793e174db9603eac09cd22c2704061159aed9f3d9a2be8f00d73d8e73691fdec4fdb875b4be159b378494dbc10405c3421ada30693f88ab3ae0d34a9e8ddc4f6bbacb564aa0d8886bdce425c9560cf6be7d4028238ee8608efd4b5c3443b8bca5cc897f6ea173e1cee38de66b02020d2882379f321af12062f9b21c022cf0b32a94ed95236deba98f1946ad4b260ff13710590fde31f571e3863dd71dbe6adfddf81aecb8781ea482466ec30997010bf656cc40d44509840be524f976965f1f5a84a9e71ca305c4d33737c7702d0067362b7b1c25d23d50eb211c94a5745f941ad46ff21803261b8150236de0b60367f00942be190e9b6d5120b8498820b5c464947596a30d691586baca77adab0ac1c342e42fcb0dcc28bea9fb9235bfbcbc210da8d424e25b3022b8feb70dcb0223e6965a516bd68002b0efa7c81f3d06d83b9249c9db8e79122d7ea899a5a20a4bbae76c694bf2680e31ceee22f19bf82ee098d5a52f5870380e7373ad395501fb9a422857d1fa766ed2332be2a69c9d8aed47d6137193409e2c83308bd48d054ff0de9fd763701e6159ff7a315f3050ebde11692dd4d64e901569ef2c68680b0a02f78e5f65cb748d0b75d533837feebe3dedb8f4263871abbfa2bef3ba1105708c0e4757badde88f56f5ddca7db74d7738045b95a6795ee7a31cea52340b0aa8a62fb37cd0260fb5ed73a45ecf4fc6f10749cde7e52d50ecbdc69eb6005a003685180b2ec910ae26216e2c0a1e96a4fe69a19d6db41847fcfab992532d5c0dc233f5ee18756651209ef425e2acf995dd44390acbea1a23ba52e0a540650e0a041ccf345ef07c4fb5d78888cf248e758be46d3ec90d0a69ab3af5d85fe91e054370a75edc76c5b20bdaef3a4ed654356c9d01f5c9fd53e30f7404f5f9d5d706a699b87e59b6e524c88e62fb4d28753ca6c53b96813a1fab2890c10da873e30eca4fa370c22a8acf94d004d78add522c4c462aec16d5c8488f39cfed78b8bf0715d40f78cb86c849ce67c80e851b4d621d0241c0fc33b8b7b976f139ac4a6206d8ac43f84c33ec00bbec205a7742e8098bce09f542f116b9da3bd36347d6e60a8745cf842d82ad31b0cf60153777615ffc03708612186b0edefa9f1e4c877a0f18cfa7e9899cf5f70670a9474f6b1eb19cc5adf6b30297530408ac61ee3f48073cfd1f878da326a384b563ee5beeff0af89be348310f6e1b81f1e8b972eb6401969730d4acf9a806bfe10b040389a7cd994de800be734a27dd691d7194d1040de3f0260119c21e8a8f764944bf95987ca37c20fe7956d483a45f3a4dfeb1be00b2387d40f2f20032e805f8e52e8647bcb5127c012e7cc037c2c906641ce74709c30b0d00a68c6ea654e56f2a348ee3380cb709ccf20a299295fee486df40e5096569eaa86d7c7d0223deed5b9bc94763ba679f8a0d7329bb5b3fdf0aa660360de7901d2957944038147802fd36b1c5f0b24eb07dc4d208bf4ffde380953f5c07a7eccad3f39c4e1911b5ed640745e71f052c712c6ddb401fd8ebefc9a3aaf408759a209970780cf39c62312c8690bd77cbbc95b87f3fffbbdb3c1f95efcc150a9dcd37d73af6bf1ec4328e63f5ba6c52e3f2da253f96608f0531ed366ff5290d58e2c24bf594ca78f0e791f20172aaaa838f61b22dfaa332f36e18767b018f06f34b1ac8ce7ba882e996d6086c3a264e636ad16721e8f008b5b19f06a2de3e0abad7dc659d340d8e36c2fc68097e99e994255589db459cb17c361ffc303d420eac111e8d1601d6bed243a4d2db89fd5cf3883308181b3f0b3f07a5489544ce06a27647744548a2f25e27ec1651de0e962a00812039b5c6fedf998076f57a880e9519651dbfd803d4d723e17b7040c05101e3b69421dba0f7ee6589ae8a696d071aad4a8faeb258be7ec6c456ca96944c502b8ffd6a97e421599e8f94d5780904d8871304f8835e74c2480892b54e9f8e237efa1dcdbf4f1fb8ac81f3dcc25c0219efbd2fd1ef67b23c7da232b5bf2bd681686005bec01b283efbfd524198570d62e58e9f8e39d383e0caf88ea81b1566ea75ddcbbccd90adcfb8ed63a9b40c0c8bfcf34a3f194081585d90f35a79082bed4ea74f739650349ddb49595a980d01a74df4f85ca9bc47401bac53dca8548ebc97207663fb0c5fa62dfe948b98a10a56e78f138c5d07c13b6a7252058b3209b4f178e010dbc39e62caea46f49dcd0fb53eed92d2f554c26e7362a6b1d8ddbf0e121639b9eee14b202e485a2ac3e209e90581243dae633c655743ccdfb9c34ce1e901a30d9095097caf2bcb475ad301efdc4540e55f7bc810a02aec6d3948dae0b760237999476f2a272afc1fafd0046fddf000e67a2d3efca78ac096e9dbf05acd314e099c9382ed9d1592c2335708ee198a353e639a57ed639bc33dce4c0646ab8d3541eba898856738ef00411e09370a0423451a64bf09004a8b5cba32f7d94fff9eeeffe91df31a5728da88ab0b581c7dac573d08ff551501f7c975527e58bb7be29a9f459343dd05c29773ea08732a6da07be781d0cf8d6549cce95647f8fc977872f541415ad6d442a01c360eac8345d49b0278b83bc4fe3563632992e4538092c28ecb22d0606acbf07f20027362375eb2ddb9cd367b77fb6f909f4998fec5911fe39bc7b28f6c2a6e56a701dbc21dae903e89b9d12656f1dff6f1f67416223012ec32878b8c680d69017a048117a926c571cc9406ba3c9787891809437736ffbf1d29c6463d00e458e1c60f89e1b9498d1321c2dfa55c63822499e4cd4529a033f382da6580fa8818b79d08e49ae3eace2cde08fa76fe0a496750c89dc682c278062f597bc618ee6b079204551a3e33a5023076ab12fdaee5da6db11d3eac4df762d8f7c752f4b3d3439d03de70f5d58193b9e343d4413d45ed52fb06d22408411c4eb2b77134376e444e0c8e93087b2cb4788c3fb6ba616a82c910bef9dd3a1c953946b8a8bc07f876410e69ba45634dc5334ca1bd8ed1461254c134d03d2b3f79201810ebf0e2baaf730e3aa6ae820b6e2707b84d7bd150809b3b17d9848111facad44822750b39d0c9006cd6a570ed1243d09600cd85f745be9713e3dc6abc45a28461b3e66afd88c20a8b7a5740185cd455bb201f6f6f9335bb0ace02ddb89b409d5268ef929ce63702324c558ed020218018ea3d28ea64f69d5ad843f3edd19924066d271d6e44e10d67e20711496f92d51d9cba8837df6255170de993ae0b3278c70c520cd4246606f04de5444492e23ffba975cd4aaea44f6c1b2e3e6051e7e62485fdbc68515205a9645802b9bd82839085424b0ee46a8878b1f402849f56e6b9bedf2415fa3c0c0829d07b65cc44a6610a0da42d3e1920754ce696e8644af377ce57489c2cda081356c1b8629a9488a208bc0811b0bdf79d5ce0977995ba728e759ce8fe84830cf7b996258b07fd6de1403f3168baf815b3a189f6d83034f3866f6e7d3bc20503c4400620c75d9164efa7b721016f8cc101252b879e630eeef8554ab186ce550af6075198fe8fc899cafd03786ea7c490f57b44469598c1f5d137d767855a0607cc99afaf6e203542bfcf48b1fd7d8cea2f183f6ee47213067efe336c9017170c2b25b21554710c31a2bc76dc3780a036c1c1c6e05ef351e29133cb3d1428f808301b50b062bf0a34fc525fe7736dfe2711969650fe2910704d9ea74e9955700756ee0193549b09764a1d17ff01035094300590b48e2b461582fcb97f89e3b9088380d720e4b762a8f54184abe5558e2ed8f2291a0399147202ca852dbed31e0bffb652976cac42df3a41bb80a9205b8d7531ef91117661012aebc21422a4c007c14dc4a0a14043d3c9bc2ba41883ba53a5fc0a4f7cf95a9df9cd54752710f7095f2f4ac4b6e3f855a08603f1d75c412918d7f3dc155b3936dd137a6d266f20093a02241cabbbb78818a1af24075ee409c2dc7790ef7d0448c05ef964daa8f40780404c0b6f4fc1a46c3420ff2ba8970d365a3dc6986b5af4d33c57e2a78a970109ca32d1ed14f99c1cc87a15a323e6f2341f4720360681bf9da6d932f2d77f01a56f946d5256e4219baa0434f1a2a26824d3d21b105b6ecb1ae32a50eeca2708dc6f5064eec90df5a4e586a2ed0e56189aaab152bc823c881e7baf3a8dbd9105cad4538e70d403db8fd633067ff869d5439f9ce186fe5a6ae5d4161e22e5050f00f1eaee484f60428731b9df5f0585becea0fcd1101e50a0dfb20d64f47bc506a42c7e512784f414a8c93ee1bb4db9217d7bc60a263429935cd8d78615973603d8c90911c72f85b2b0db4212df4136e56e6929b75464e0485de93f5955b59509fbe6a66ef75d35f7f5a48205e743cb7df54ea681f6b3555323562a0b2be17d028e4d43bf6d5cd4107b9cf3368da9f7b9ef75d27a2bcaefacf45ffa53f4cf0208fa1e45ea0151fecb0ca5ae78123bbc374c120524ba487b08515016a49323130210ac4777678f6a757533e55644462cb9d2c371c032cdeb5df1b3ff03bf2c5f07a64843da23bae5931a77a306f816d7a0034e06ff77b1bf3b4bcccb40819e860e1d6866df65c00006257a80c8e366a827658f9cd4531c51f51b92845a5d41ee06bf0cc8a7b41a93ab19ba40f6a24af1642f297b222ff5c0ba7d53658a62322006e187298e0fb1107ca216ca491a0c8d9201b00384c08a9932711bf5b082b3c40defaf5eed1dc613b70ee379cb9db6efafe068e4691c950bb5bcc4bff4929aa70e8b51aef378bbdb8c91c6ddfd366c9c2dc1d002c2efe1b275b8032167dfd1a606fa30af25f40f655c5c5c256ee850acb325952ce59fbd71c8e63ab81dfd89b301649bb53e38f01bcd2c9d2cd2bb7b6f8e1c3f590ca020e0093d49da2abf4af70212047b62352183e196df1083a1c5b164bb7f0ba00ec4a6b7f1e7ded07c6dea09ec2b6172ab6c3f0f8f79ec8583ecadd38c54e0f9eed122f4daa1294698ca7d0a1669b616f2d2bcafdeb73117ca1cfb9c40e8b32cbbce61e7f1cb478ec2aa570a8387c3bf9ce73315dc907b9134b4c2a842799d0cc9e54405d7a42db32a64f00322129969e226463e13f1ff4a7b055d12c718e1b6c67d1afd8a3d2be623b7e403762f4c890ff6e40fe71a6bfdcdf9301fa4de66733429bf135e9c0697bf507106b1278afbd8f4576bc6ea5dfdf9789890501a0c392b578632983f6b1c7a67fa08b1e13a6000c576f8f4c549b5308ed1f3123c22eb1edc9d2ab13942e86e9b6f058899a673624eee13326330126b7cf121c35951dafaea8444af2f547c91a2b2076ea0a791ff0c7718aaad3e759266825d9efa222b9d5684baf9b51f1c015c4e0248f28d1b309097b09d0929c5580b108319274819b6db42953ebb07b9b7c1530509502f194b66f34a982bea46e37619ebced90ff09ddd23ce0511f20cef02120edba8bb7aa844199b034bf02475e022902e18029789ff4e15e880b55b9ced560a824bfa6e7ad37f5102ffa70022b2bb061fd6aea7f038f8d01c9384097109b50bdb3fd5a3a0e1ccfbc7429a3e1d9ee8b07db2af1b0aad79d8f1f8fe675bf14e079f0330f116655f14a2c2c3572a69f4ea3ac8e7750c91501e435d059aee3ebe07e2fe3661bf5186b7c4c5bc0dad0954f17514a8de8a4bd022511d89c645e2170906c347ae4e4fc77e40c73786bc1ca49f6c37185f4b18c98296712a248c23c909becb1a4251b343b5f5804ef4643bc442d96246244b3a5e39edaf929f9675df03456d8f0dd9124656fa8615ba5d679e6423a4a5adde1b2836434c65591b6bc20bc3c002559725125416492b2d937cdd895eb4eab2eba576e3a996de79dffbb10cf6c48562c604c78388d135c88f856721dfe5448a4c88d6727d973ba577418000a3a9c7a260276daa1ec7514d7cbe7d47aea92ecf4ac014e3828f86cc2080de089babded2773d64dbfb5043b5ac220e37d3aa6401324f107a29a1ab5e919b2b078a5f31b00e931d9f6bf5ee96a5df1064c5d96291da01dbbb135bdec9a9897f09ee1c2d98fcb2df112fe3cc53c7679215cc0b937cef825b5bf9ba2823f673030d7a7e86ab41c616d58ddcf22c58ba5f58948d745ed31b6f92d8eb2d31e95adc0a66b5df50fa836da39787433f70ff7de475c6cb36db478cebef0f2ef8873bfd0208e3db699097ae772ed80958d43c17428c2a09602dd260670a7e57a8d9774006f3cbd405764d7a9d33017dd7dc41503a90cc6ff984263b7aa5ad8a7ff7d8400e31cdec158c95ca6a389e4cdab231510a76160514c24ba73d7cb65a5ca1c57d0481471330e55c072406520c26c22c8f7aea87b8303e1a417181dfbaf1cb4ca10c8776e513a6e4412c3c21032e9d6e43e77b41f4a8835ec5b0093bc84f4df5140b0d7f02e6de0072d7ce8f8935279cec4a40e365d296f838ae92cabc6004234b0e2a5a38a8a2ebedf1d6f61161f0bcfb875ac3dc2a11a29c71a6af852b863f3a0e19e3e15f2182a31d68e8e194f42ed97b870eebc6ed86eb4a7520615a23fda302f4d7d756ca6c22494029dbf44312a860106cbda5c88a737aabaa359b51ff9f0d86616e89f1e292fe3a3dc27c47d717e68617e083c262fa3b697c07d04806730b4a966a6e57618e33f278364d919a5ce60a8f174e0321802e07938601ac3f070f948accde198526df578b916c2e1e02bc562128818c70f5d48e077b5171057c077f4b36854b01bbb63430635b5aaefbb06461449af9c4bc5bec48b708effb3a007a1c02d16b40dcb20b11e166381efe6dfcce5cda786354d66a6ec9bae8c58e0f7955b6fb065662ef797e961afa00538259a737d32671e5b34d85e9c3a748fc047cfb99874fd6e6c82a5b9272cf2cf29c7e05750da17819c0cb37e8ba7c00f50b14f1302cb747858b197026b77309c2bc88b0dad05693ee76551abee1e6d1c709578ff4a26e5689d8e9a7fb03d7dc0409b205196061b2edd08a6ce6e73774b40200098dc823f5c393d9183f751fc71b3aef4a6bb2bebe5f54345f7de75e39c8096c12bd7e7ac994a4cbd70903d54c817bf758cbc5a00861d60ab1432873ab7c0c957249d43dca6ee291143297e28db4ce7aae348458b9268d8208ecee87999d0df0eaee097736bc5aad5c7915afd956b439ddd4548b11cc2530955e5cd9af2505b36017c712c32b7e6d9cf74daf01bfdbcd85f06c949ac1979609c7a427c9160a35090f7a102bbc8057f85c8bd21acf0d688c45a95f886f066745944cc6f67b010de33898ffc94e879db9f2162c564acde3089fcef5d446f004d23ad0f116310481bd07fdc4f06bf0ef0636d6d64755825c35246c6719de4a4b0abcccb088410fb18af86edaa62f20856211b700ee1708f2d65dbfd2cfd6ca995ee5c108c5740c36b92d8e65b4959008b7a726e0d9aca1acd7a6f5e7c543061eda54c4beed3306a39b9cb892025d99ba58a8fad99dedc462ece235b74b8d1f90cad933e8f4260994b6b5146c97887dd30ecf0618d8cdf40f30a05b4043bf5412fc56336e34250f7346743d52ef21865344c531731bb9fa25993567171d56f453edac0a2af25003a08e35eebbd71d6bc371df17ad61918a0ad6ff7586909a4986a818d3adc0a30f478b0f261a7f0276d35a93106a7cabbe33680e71e92f3baa6f8ada72234c860dab3d4f698d02723ed8ddb0bb6dc43da792fdc70802652ac1137bb651879e560b51ea5156cc57a6e8390e1fe5f1ddebb858ac12d67518af739d17bbea1492c80246ebca7dbd471690e9d9eb4f0d7693899923aab1731184e127f819bfe65a6e022f1aff8e5f0f3a9ea9aab568e6bc0a0bc009e7d1a358f6d6c13e50b7a78532037f9958900545259239ed40383365dd0ec19b422a18009f1d6ffc260a129a7b09781f054f4701b19241340cff7033c00605b12e7c831443274dd25c4608e17501731c9001cdc97f378cf9e49c9f064735639e9e7cfea635e953b728f4ad77c20ab387ec5510beedca6fe2ddbd0f830b84ef360b683fc0423c8d660b477977a808ed31c3b980bdea7897c7b53161db0efe895beb770bfa603a9470f8b90169f40e6dd4dd9bb2485e166dd18f5df7d73d938bdcea271321c32660db40053485ba0bd598192e3dd41dee39252cb89c5a3e09e2721d9be1d11b5a1cf63d3cea1eed0088e9664de8f95d2ecb59162473cf3e3a72331038a307f13a563ede0bc13d0e023a680e9111151d5327e902b2ae9349dd1f92aca2ea562a92ac857c6fb875f40624ec08548a7397df8ec1de57daf97037c37aba167f663b81528c91ffbab9ec0922452216c801cda812daac6f354527241d573122cd856fe597d46e8941b23702abf12c53f4bce240553016f1c782cbdf505fe8520b924f6d7574394b3bdc3b0734ca8ddd85183cdfeae42b60b4d8fe54303ee2d369c6d1faf539c194dc2bfe0652ca395f7b8b8d6493d5efa117e990d47368f417cbb219dcbf5e8ded6e252c0bbc4d25485705136d017dcd54da69832a2eeba5c9b95cdd24a2faae543d9b48097bfc26598dc19a44410c9fc550c81475d0aad3dd3b57046a7b8e4a1664493605854b091280acfdf0ea45dade294e843afc1eab373c45d44a2a7e8cf50273c008d270fb42a8c85e99cc7c04c631f5f784582b27b0e500bad48f6faf9173caea033ddf2269e6d08b9e228279bf77ce7ee597e838abf73fd5d4f30b67215f9fc406816d76174974ad7d9079cd22ade3b8f8999084d7e5cf088e4f2e699ffbd11704a8f610318e7ca6dcf4caa241a6650f9146d13a7440405682d2422a9c7c03de04f38692e460096e2770c20967b2075b4adba508cd872128acb869d12528b91b0752965cd9a8756c5c994bd2759695eade0745b9fe16bb3470ebe68687a8bd49062a78f0bf36e46c99205374a21cde0fe9c1d1613354546e4a9aa9c0fd9e03b30f8888fd33152a35693d5c8e1a9f87587885058b1c8b02934baba31d06cb62720263bee8d91f98e2cf5daeaa95cf572024ea9ee551083c88bd5dd7cb26b3b52403bd17043722db40515a966dd2bff59698ae17a92b35e12b8df651d6801bd28d0d242718a2915569874fa517abbd2ac08098fa3f17484c146a8b2fd5972ae9e50e7631612788326ebf00a7e523b72e02b570e7b2c1bd01878fd2705760c4b3e902614f0d004943ed7a36d9cf58224e8ac2c3783cd4db22674815af9d0eac2a860f398dbdb8037e3df5c43830998287e5393bac6cbdb852087934725dbabbc931034dde1d50549434ee6b3ef41770f67fba6313b970155a8a8f58c474136e896f005ccfb28127af4ccf76a42190e2f4a30c891bf29a42263f265728b7a6523d6b0f6c7bbdf1a9a3bdf7a3c252c9b591060399aec1598fed9dfdfb6581e37b72990f589162219d1a5d8acf4ea8ff42b168e3919cee23ca365a6f5060c2fabf75240b7dae95b17b4b3dd58e91aa05ebf1da0d99ad5b9813278d3b9e3caf40288daa0d34302f040ac7db9f659e13593661ff7568218f60c26cf00c36b979743ecd2803cbb9bcc007cb4042dba333f23db423c0f31e1f3e5ad34a6041fc4a0b6bb2110c9e76530befb7bce295cd79d3226314fc148aec202da8d7992d6c058f6fc4d90ba411f5ef862e9127ddf48c42c27464d5aac0aaabd127f774f218111a8c308902ca0c0f4f7e2006097bc189fa3b6fcb4b7c75cdb7910c9d72af30cb034a7ed2076b223716865f71d2a54c362ed2086ec2ddbdfe8953467daccbcc9337d9e42c00d63252c8be531070b638b5d2ff2ae2fd307d481d7b679c14bc1f862f26ecb302c56620df8ed8b24c031f8806918b2e5dc83f82f2bc8604d62039219e527b4f0bafea9ab8f880bc36aaff06649fdbe1a84baa029362bf6112d69022c3bb4a40045f2cc48aeb932ea562742eef2ff63a5a8213c365acde7e59db54f48c6dc0c50cbd04c187e68661400719187095e8ebf509c8caf823a9d0cc932fc267025bf4071488645280c04ff840361366fe3110f0a765b03306ca84a51a30cfa0f722fe0b661002a6e210783b9cb4929466ec6f72749137f4b88b6952943228e4ba7e75007e6d472952dc010460c52fd0ef1c5909cd625733f6495fc3c2a99441aad4e90fb78c88850708079e1b8467efe3f38791e3e6e494e7b7d52fb6b0c6f89859e70bec7621d076e3849a527b5faf963f1e19cd3a53e6a5b0f1d03e8ddba6f08cce056f14b474ebef3e7473ebea0b078a4481679669b54da1c15ad1245df986857404a73c2e8ac84408272110efdeca8aa91c394ee911535228eb9d2ebae9cf2baf0ce19baa2b708c009bb8beacc220cd91e2848da73cf6debc8714572e76063b05020f72e4eb4e9a991b8d0a1ecb30955e4342e026910ac49c4885ba75039e01400f5b9e012af76691e0dfd584e14e2f534e209877bebffda30f66948713f2516a0f5cbb65c96db3481cfa7eb35f9170d068d81129a24b82cda3c4c04ce76a3195067bb653df0c40741cdb45b76e33d842061b64c4141416f5af7e78f391fe90e402a864419bb8a128afd13c2eb82b4d303cfe7931e7acbfcb60cd88141c08c2bc047481cd80a1209a2aff8cecc9ce26e1f51509b2af8c54b9f6322553d36317ff0f270c0f4675aed8064f88b5b40636ec95090c32605adb0f1a0d67b951f16f0f06db419d6dcdadfd0284d080623ed5cb70038086dfafe87278683f3b92ffbfc1094567ef31d92ee9ad621052bab4cb36b3e7abc242b844d391aa06832973d5d10b3be7e386257c2d98d5b838af5d8a9decfc07c2b46bdc8beb4d4972f69b8140013a589f32386732af2917d8e367b18f3ddf73722727b72e927ba2fb74c4bd3d0bdb36a7d302f6f2cc1b849d36bc1291c1deb3a68c51fce69f8bc1a87e6192ba01af34971c4c031c662a2c6d4552d1ff849b6abd49d0272dd1367844ad73994203d1946e548e369f6c6f5258a117c8a6ace7cc5c2474ab80b33f6508e16d42b00d +generate_ring_signature 511e40c694c62adccd1cd78dc759ad6f7e6dfc6f65c233300edd092548e6837b 1f100b5863c7a8cecbca53896f81bfb37129724d1ac0482da5e3c041ff57c7dc 1 25fe770727b3555d648a78a3e962b1bf25f9733c0fffed8837ced7e2b3214a07 805b31538ebb4de57afbd5abba894511ef846fa6c94906865803092d7302fe0a 0 959d747113240dad99f5ba2b9b2734587e1af86df7006b417f6a719fafec2c0a3b20635db2190709f935f5fd6f6e5b7d7031d81cad4af84a16b2fd8e13d4a000 +generate_ring_signature fd1acd2eea26d5ea5f10a0b1e26b68e4e89fb9760cb610bcee9a0e6903a25904 984b195e9d1b1019e51a1a00ef3fead39de4a03464838690084b5b1f5b49eb4a 26 bed2706f07b60e6e4ffa986002009646f5ea5fabd1b164bab9644b690893d9e6 a4fb36914b96a15fd407598ec221d6d6a06bad7c21bfd0079e91db6b64048f91 ac5d1a9778bcb740b0a04211516231df150d714a929520183c6dd54153b07eb0 c82a344033f5709ae3398a87d909ae637b24c0b355d25b76a04fd436c0c01488 56bfdeae80cd043158fd30b06ebc4605b4dcafcdf90db2982f1cfeb7e5494ddb f6d0da924cabf77083bc07058088dd8c4074e33c1839d7137b59cd665ed18637 d4897cf986fc87998d75784803bf73a1e9854612b9cabb0d5a91754a9587c83a d0261ce00fb5edefe2cfc4b2f1152a5802e64bd8bed7ca4d5ce7ae8e6be671ad d5d7d51abe48bb01095b73590a322c95d0c2d8aac5a30cd9c071d42fd222bca9 f6fdb08161059d27e267b29730e633e405365951a79a9af34e64b2831510bc5a 5dc9b05fb96aff4ba4d6589ad93d086044a0e6483528706bd65550ed154657e6 a7766d3acd6e9abc9c2925c3d1060214894f6b084d42907bcd8ce486a100348c d681d22b74d9caab9d5f8a39c2072a899fe687e74c04beef4dc50c856eb18407 45151bebf7146e8115b238efdd4a62e9902d8ec3b2d17bee96e177e6d7aa19ed 9f0f1064920ea54f315688deb2138113e19a1ca6ce6401fe778a01749a2a8736 e862feb485334845db565de4af0b91d08cb2e4488553054918a4fcc5cdb99ea7 c07283ca458cd202e47d486a82345801fd8351b6d511a7bb5f4ada80d3c6109a 7ba6f60b39d016bf3ab7752a09d9a9bd72b34af8dfa098fdb1f75e565e5323d4 75a784cfc3245741caf9e5d074b516bdcecc8e27784230f92084850460c6986a 1b852eb3b1840382e80d8beb354567e7d6ac15e5abf7e450358b70f067388d68 9b06b82abcd972ffd5bb38d7c6bda7a4e50e79350d0d22e9ba34231db2c69d6c 0a65a8633f5152b3b3514e838be2c0a6cb63c686f3be5163044812ba73372779 e535860453422af9b12fad4a50fcb1c233b303c4c1cd254923b7594ec0e1e95f 5c70df9727b368bcdf48d910dd9bbe2d113d468e22eddd03bf268c6194f5e46e 934174a3b129c761d997d0df693377e44c5303aeb2036e6b26fb0b6f7c688146 a3a8c856c73b2c20025a9db3d15ed505a5a3cb453c7dd31f318a76fe5098f59f e7def0f0562dfcdf304d8ee8b7483c1292e92a39a2b6616e0763719aeeb4c10f 9 c6822c67eb2249f3a442dbdc2633297b11f2fa073bb076086c0ae5344b40d6093277f11ac09b6775117872f9bd66ea450b148effe47d8a3d4f28f6a252d8860db528078987e3a16da014a935a111c73cfeb23bd53b6740501f862359fd43090bcaa76038c8e479d9e4cb303c4fbcfa06ba26b53a035dae3ae36fa6efa1b8a9005ec1b16f9d3f17dcec0a089ebaf66c41e335bd4469aa3369d95a7277f5388c0d2b28ba53c01d7deb416d950646221f976bb29c3992d0ba9bd4f0267dad66db016e79123c8982509f46e1d72694f45cc0678ea1fe396db43a715e0f7883cbaa0450af1a7f190fa8844a7ba235dfe4d01b5571c07111cf491d5f2801b83b4e370bc3aab84513bf9cdc84bfa6cdc7af852054ae9fa8b2ef1776729246059e18b902341b373e955ea3127d0d5461895e6f4c7c35cbc86f4921066fa62a59d3f00c0d6129a8b39b45bcd3fa0e4ec29a5f0a2440ae7592518e5464068744e3ef547c0ad2b9ebe218f5f8cebe2ffe16a98c335bf7e09367ce4f7e38f606223d9179280a124af3437f031d41bdb44e70f5a1c8dd14467aa0742ae0a12c55dae83a39a80bb3bb951bbd367bbbfeebf304349dd228844ff9dae04a4e8a359cb9eb89084e0885b1164ba7f5393ae319ef93caee9891ce3f3aec1d5edf2f57315d0f06c8790357ae1b362c443729ea1f990a1549bce040de2c8e94dbb369853a412ef37794032a1538cb8ee4f637786af56d3e1f2998f15f1076b7dee2b55ebce5bd1f026801d4c12c5515a2600b11778af74641174dfddf99c3a0acf1809fd63078dad8d60fa6598b0cb1f74765ddf46a9f29fc2d99e6dc2f1c9d8e5b73d3c5f6a3970699093be2c69f94ae2d73ab58fe8a136bb7d3800465e525486bb85b34f43ebb9897084f4289c4bdc663c62f00349a22eb9e80586ceda2fd20378082f12d54a92d2301023833fb85903e1e376d45a1df47f94cb33865e23bf120b88faafffa5b76b30332c0b1fcc7a76d735d33104d4ceb00b3ff5390b1283e9f9bfee5300200a4d706bd8e03f46373fb9fff891decfb39d58905f82fc53124275ad2719eb121814402cce6dc9d62af0cd1e78483e4a6dbf72f6aee3da68329210713500853900f0a0660bf007998ceac9cbfaf6cc3c93acbef21ac3e84522b4177262be97f4b7fbe0a4fb4ab9f79f84c214b6e0f306e51abf05763323635ba6c3ad407641d4cc0180dbb6340d0e7c7ffe42f64ddbf87fab6880b044a38a7f0f49ede342fa6622c5e039c127a2a115a35aca8bac60fb060dadfc7f68000582baa638a861e5f7dbac50a5db8503d00cd91662bfb55714701d38d812d88e091c6b63ecbffb866b7e798001f4f3aec6b3713781b99527b36871e5520e87a6c0109e3adf92a7189d301a40a7894cde0b740db61d37e53fab2adb066c00a62500231d2a3a47a6c053da69e0c234847784ddbfc80603b16d1463b7226d3b61f457baf9ea0521b09f92f350f088ad7ba2d43507c70ce1126af4883e7d43876cb701968818c448b4f65f8cc150cdbaaeeb5b3ef7164c6c5954ef8dcd9a553b86aa4c4676685304f2fc45707010a50b2b431c3ca8a390e54aa03c6a593c5def5cc821f1e66a569957f5a68ae390f7bcae0e4037afe17e17a02e19fd80a199b167594c1aa50d0a8447b0318c74806e8f9315b78fdaaf486a03a7edd1efe9c309c63de886afab3accf5e3b5ed03d0d7d8fe554623e2a4df5f22f7ec9bc22f62a0b2a716568749a7ad3e2b7a7c01e05831ab5a8811a11de433b39fefea883eeb995eba8e00ce4ae308c6fff7251f20fbeec3d1a72219b74bfa609fbca57b6af8d553b8e823141e3bccaf76f553e9904402879f25c086fbbde800f4477b8501312b3c7c8b9581fd805508dd6ebe4ac00dea99795b05efc8886a02dc69a5f44bf4ee87a9ac851dd4b572b6aaa5a996c0784b33e066abc1d5fb6f1f07c5424b782764b3507735cc9d4af89a534a57cda0573fc73b32d1b2d3d1d2f3c072db3b43fae0573b4513f11912411c1d41bdd4b0af0987aeba25237b47c5cc352747906fcfe8ad1a563d6a4026006c5101d1e4c0786f1dfb9bba6356b8dd01b05bb40a61b29b4579fc92d92143727548f980d05004690d4c4f3542503bf483b122fe98b98a105bb9e09a49756b62dc3ebe029ee0cb83626a67cbe182476f78411e8b20fcdbf4f5756e0e608cc7590e0dac0e17d066237f3845f78fea87a312b6f6b6f518bdbaa014586fab04d0f400241884d880ee215ba9e24b20d6cdec807d21d72ba39308aa2313bce08b0550894de2589330d1f6b31d8dd215cf7682605026da9589b10913996da5ce613de86228dc20f5a03 +generate_ring_signature 5a353b51a4b8e73c0ea6abc2c657c04a77fdff66fc6725ef72f3ad627f1d475c c9900fb41f580b8dbfe112df57f0c06e1ce7fb0130751cd61c16c51120cf2805 31 2082c30f4fb72d624bb617a6a03dd887a65bc0f54d266caf486fbac1aae5442a 0eda2f4ef08a2c05146633a297314ad90dd695da99f4f765c18c22c0c808812c 749d789d269f4a8d46b4af5ddb19cf5ee7f86c54e0c08c6d93691d96a1fb348d edd996b1ac97ae752bb02bdd19976097ddcba82f6039f8115498fbe93cfc09fc b7123f37c63a6ba41ec9b4868113d6f66cbbbaa8d80427ee6964172265fc2e8b bf64bcfb1fd3669ac6664c3e9352e5f30ed3060fd80c97bf43105bb062b9beee 991ec7c190a8f7b9accadd7c2b7ed031fe9c579f343499814c4c4d898855a3f5 420ef41f473736a88f14d32ff6416ee906a4bd91c3a573c9299a4f1c054471f3 236a9c9deb53c00f8668443a9515510ab1e9b968f4be278a94c3bd17c1bf06e8 0d96fc77c34ffa4ef13b0de637055983224a12c1dcd206b283c797aab5e7127b f11026dc54d653154b6f4a9c29d188e9c8d4633605e689426cc3fea64d70b114 04b312d50cda555c47641f109b9f4c6778d9a7c52e2ebd57bbbbd32c459b5c96 b1ba5c22e1a25e62c099f43f3e62f3bd082fe17b2e0334d30b45bddb8290fda6 b69eb8187d101ca4ebb8d84c72592d77f57d04943f3ed6ce93f56bc370b915d8 a0c257d7c690866b100211a0555956726db6192ef9ddb6569b0a452e7764b0ee 99cd8de3617f2f157fcf318c43e96e37df9ed7303c27b039bad46a8ee337b684 e929eebe671159239cb71bd63468e94a2cb90b756400a389b49376f84aa269ce 8f56d7e009725f04ea1ac3521329a1572dbce6fa2098cbe4ed7132fa5ccea342 47e13fc70a64213fe9d3278cd08ca6aeb844bbaf556df1c57d927c30f6d2c75e e4632e8650a1bd477c11dfb50e3c48c77c0bb5ffbcc2de1c41ea85ef50214b7f c8d0e4cac23ffa3b13127de2c01f885d110620b899d6d1ed4fa014c809642284 33873c867e6a07e795638df98011dae680049a28824194c4c7d5924d0f7bc18a 7930cbe3c49a2067ca5a88383454ada7c1529ac75665be86acc0906a63d0b84a 803714d531449220c83f1582d3a0b96462e6a1c4fac0f73bf486fbecfd99a216 6ab2e19ea796cd40a1973def711a3c370edd3afbc452afec3a27375c468232a5 5f58473e62a5a53727b3556b0d537a5210c77effa6ef4c00a00e6ab328f29133 228455d4ae9f1cb40781b45e6bbb26562bc01fa91b81f16f46e018f299454b57 5ebd456bc28a81eb4399fbce59a8c8ce0d765543e7d1dc876ccf2401da38e483 edb57f628c6675f649fb2947c2b7abb9504e00aac31d3be494b258daffff3935 44d94d6c395ebbd88bd008d0ff9802d38e06bac61a71f3afd0c1d6b75f5517cc db1c12682ce582aecb3ae22f6040d3c804d0d000de0b051c8739587850534ecc fa0835dfecc3c5213938d2abc5b71467e1412c22cb0cd2c42011a03f15af9f0f 3 c37c7fb6e87a004b2f619e89b682dd2b0fcddaf4e2b1ddc7374e49ad2236d00300806a887ccbb198f7a5d483fb2fab1e76d5367e4139c0830bcf343c808dd3043560c44abde6517a4d19f2ad0c34a21e4b442a002e3e3de01b1ef2ac5921030d44e36711cdcd8696af838815455e49a852a8e48ce6012abdc89214f555a974013a9e625cd686887a071428caa54f7e41dec52190ab3e76372064ed35982b5a06c562db689e42e295ddad75ef892aa9589ff69f01a537d0cd33c06726e7272f090c71d0c55b9246994fb82cf541de4c0d7da069d9f9c8f70c33d61c175e2ba301624c56203d1f65ead50c8c8ab962eb750df0dfbe16121bebaa3b5b5c633b8807478d594e8639e56e4fe1da783bb733a74f5a27027c85193c0ee2aa5248a5590c96ae810ba38cc3997649dc90be361bdb84e711af41091a9dc8a3536657b71500679c30461f74b4246b0e18d00c0e0526bcef750a3b4fe50c87e6414484c7280c12e3530b236684545f3596c0b32bf83efed4ae47c209f6f83c257c7d787d080a474dbae2c47d543fbb0ceca72dc7cb1eb0271a6b4ab1f38304df705d76f2330e199dab48d4c985de0b3f85de47e09e0dbe95b4e2d1beb1bde359f59253bec50fa4e389b5345611f537aa154140c33f86c306b7250406f57261e194eedc5f2f0c8e3735a618ce013f0158ea52d2aa0415427f83f1e1251882fe6a6bad068cd10c4905eef24a059aff58b51b87f3280417aabcf15c3df498c1587e7d20da0e3e0003528982d52fb5585c5c2ff24c974d5b4c358a3f472c366132aa10c9b342fd0d089bb3ab6c161ad99081d05a84da9654b43bf20f012e4df7a8fe7b352abbbf0834910b6736160ab726a10216d06988610bfe43a7eb48051f348f819730730f0687f9b960b4b956411416c0e97a950fd0b556296520d480c8dd5454fdae9f9804ee643c30d81a88f34a48c1704f4a993e371edcdc194e0b69022d014b87849e06400056cf375c4e9c4c62400653cd27b2f21dd1d2e41ed7f4bbf7ba1455a9c200acdeb86611cea04de14cb3004e53148bc5c9f195e027b2b0e6e5fbeac886ba05d823e57d600e6cefff70d38aaa02e0231326386f910904551535319bd249b5081b4592d5576ff1735ee7af8c7388aa5bd96709350ffba4f25195297a706601067cbfb228af229e363b0883f4cf87da258af323a14e007cd050f82ec0abe8030be3c5ba50c52f7e729aee539a73ae989137caa58fbc900495c0aa5a8a72b3c4044c153c7d659c440d8986bea7c03a62b83006d55ace4ea136dda030cbb94f7308c02aa29d52a558f3e798fd45c6c1a9ccf731382afd37ff62dec5066c01535b06f34ca8f726b18295a1c077d1831e51ac0b20ef7cffbc5b84f3d5a3f6c58ae7033924258dd46d6c86e469006076c5c100407c80efa4ad693bc76fc17b44ad620df51054cb47037e7cb01838eb585cdf4edfc79504de2a7dd98ee7dae95071300be73430d5b23cf0bd463aba726e2c05919e8fb8b736063164fde649fe5a5e1c0d2faa33c957418c9e6cd229bbdc581f149c5a48e53b904abebfdabfbe35d4e9017f2af353a51b6b9694ead1dce4e0050c871f22183281e9508a1633b873ba4802e6cb728d118264d848fedf0b6e0fd32cef9f4c9dbcff7e46f7cc1477419d8a0ec097a9e2aff6586a7eb9704d931de04b4f19fb2ee125071357cdd18b97ac3c0badac0b1f35dc882ac42cb94ce34725d27af65876e042d9e25a1cfcff0479540fa9fd42b736a94371fdf4058cf5425108f5e3e4c3fc14f28a0fecff1608367000907aba24099bb53285d235bd7dd24ee976904dd301733fcbc0a0f9dc4db79607dbaf2a06f9a48248cd59ef6a29bf829ce8eb5e58fe78da2cdb8f00ab96def10d95e38e3b5a105ef1d4092329d7d56cf6c16a25fc3644aa72e52fab26ba6ccb0daff69b73eeb0a3b1f315b52961e865b9be70ff95c38a18d73dc86b6771dfca029132a46cda910a4826a3142e72a1aecae7646d241139c2ae99c63a6f094b72003dc42510ef14d0c3c0c1dfa901b9e6871d97bafaebfaf529be2308f3e36cb105575f85c40eec91f18823b92e5079d3ec5c15a8fc7179ef282c6cec79fb9c8c0eae6a5ff61aec22889b283ce3372e73509dbf156db41987cba704e4ea30de8f08063d4d774cc494c204e570eccac5706a05ea33960a8f6fdcb98f4fac9cd16e0cff82ce221fa5f2cdb6ed8b1b368cdffd6632b612d4e6361cd3109d84be576f08d5fe4f350fde798530a61dd297d75422694f0045f6eb4c6807cce283de4c8a04c5092e22ab8d0586c5e2482917370bb1106094dc3a21c2df7357f45c4a06a305cc9ca5641760cbf7b7320d3d353362c71b05f171f510cc3235862be20f19110196e97a157077da3c35d25ac91ff452d9855874b4c7694189e6765e70017dd40f9d86592dd045301c51fabcad41d3c0b43ce137092ae87eb7edb32c9e8542be01a2df9b2d0a3bd8610d75eb90adddcdcba1b6f4450fc149dc01ce57cdcd00e8026e8f019fbb00badac170329fe83306ac7d244963ff31bd2b7169436c384a960b2475e50f2678ffe9e0143374f8e29b2d9475e25c638301fc2daa72a5e506210660eb7e094078aec0b907a4e8be2b84b4a51c1357593bf7cc94c24de5b21b020aee576c484d1d340d0c7dc94bf02ee222397b9e933be7bcbfbd9227b3fea6820244e5f6918868486a070eaad080ff566c18fd7c40de6e15cfabacaf2f32977d06cd807d306adae9f1005e441b80bd2b5afb8a5536397e2ec269ffc9646889df00 +generate_ring_signature 6b0442ce1572d31322392813b8f2796600e1bd3a7f311cfcd5d18db158df3f83 005e98bbb03be89de4e329438452ac2466eb114dea37227d0583067af043e361 26 d753796ecd8caefcdb6d6eaa0b9f198541cad7e68ac70b436a7c5fa4db496797 6ae845a62d710002eb816996c34cfc5534d1323ec9b664c20151c4571617185d faae0348d8ea4771b6855852bb5fcc60d992a0fd9ccbb4418f9f205fb3eae956 c51b9891e997455b4b220b2f1b57dd8cac0e35489e9f8156cbac6b7792a35f42 eacb4149226b18cf68ef4cf0bf21d09d3e094002482accc00b213d0c32010d25 da150b24184ad0285d59eade87dcb51e73222301f46a0b8cff4007576858e11b 731a7cfd4b0bfc6824e2b5cb6e3fd66a77981d707259a9d2dd332168310e0128 b8ed9d7e6d9db63cd5dd9f5effb80843648c18a5c63c32b67ef6232240707fcc e611578ee243322c9d2e3210f57abd215388bb1e79319fccea9944dc8640e294 410acc54a3f9762e069471c4686a7e9e3e92c61811900962d6ca2400188a6ddb 87d32d08f2338c3da64bfbdba28f6b75d46dc4ad7fa2f018f033d09b608871b1 bd0219ef9562064deaed5e8d494ab9a22c32f468c5573258d6f8963c5402c62d a56e1100ea7ada74f25f46b738a3101732b7fb2a6a24e860fc42a122cb295603 b5c1a4655e455a01e68e3f6002e6aeaf2ce8e84d610c5e4542fe2d3100a01df6 6d324750ad63ef0f6be7ba47418601b0a5b5c1317d151a9449c53d27425131fa 46deeb14d5108ae9456f2b8375dee122dd93f8c03fb326866e6409586b44c7a0 ff9057e3940c9016f2dc47f986425e3d4f7f751ef2f54ee3aa1aae85a2f32460 9598db5a457095e3d02ea5c1ea0c56f8d43a2febcc64a089907219992cc8e4e5 afba70859ebbf598a74ae65fcd63fce51abdd43b5b6485bc8dcf9a2eeff66d7f f0d872a622b64de9d6f5db16745b7b7dcbce78595217317b73637e9c253cced1 c244cc03d464699ce9f3c9fcebc80a8131104d03f2fff938dfa1f76dcaeb6ec2 ca824fa57901f10a1ad1fa6c51a444064870adb920046153246b60176229569c 2ca019b674ed3157c84475298abad0f6b62dc1cda2a11a92fab49f9743567a24 68903516c9127c4e405d1583738f2329755d88724995901269931006aed07600 0f8e7fb6d91889ad5a0aa80048a6a1d941f666e2f5c9ef5b322bd3729de419a9 6f87fe92f681328fca521963539878521e1549077f5851e04b200e3e5d9859fb e4887420585b1f8449d75da7de40507abcae71e73652b880ecfab2d43a9fad0f 20 f759dd4dfc3335ba9f866f118c4c60af808bb6ecc6fd03ee05efbfd5a867b90573ea38606265a5c902b961ebe0cdbe99d8c22c014d8a641a4b8b807eb7cede0bd7bcd41af4ac0e3fc4925ef0aa7a34181ec477d55ceb294d91e647cd35f0e0038419102bb6b448c89e66512e700a1a979876f65b5c409a97878f4f54d6f8490fdf211c368cc4d27d905f80926e52afd618beeffd666e8b82ba957a0745c95b02939ef192eaf2e1462a8f785d7978945f0b6b66792fd1c7104339a044ae94ef055cf84f84b4f2d2ac397550122800ebd533b904b861587fdd7a7ed21ba1ce950185031d63371483c4a3b725f685a004c6c50d248a70c84aad172adeb42312da01b9f19df0fff7071223422e86494cf0d3c6b32dfbdbe9b5e44342d1b1a05af70ddf792526495e999f6830d95f5fb1c042e6c3ab8f3281e46e94635846f5fd1c03714448126c5096a9b0a16b35c2999d0e35bf1aad48428967669160654038fe0b50972a6e4f9327950ed21c6daaa3163695c909f83faa4c72766f10d5e89c0d0933ddb7079d20a3ef2f69c19478352a34c07c5271aaf22dd646491b6e7339420c6ad94ea0f676ad968077e30950d4d67b88ce7232236b4a5817191623cff2080f85d72b67cd24a5b1f1dcf7b201ea7e7737587f67266a2f224e50e40d169aa10b055f7bb7b0c742b07fe62b1d72da02e667fa5ae60bc27a5c93ff4a7fac1ee00137c17561e4c1741c7403b5352999d560737003c3c6d7529957bd669126ea950e0021bf7b8396f7bc845ea2c88d41503bb8e5a73550ce30359c6c1208fcfca40e9e53b8bcec5912304979ddc9a4483a0393941c3324b01bcb175bf9567fc59c0ddc66d9b9c3eaf9615d0160428a6151320675e104b047be7e4d20739758704d0a7541543d0f86a5da0e42bb18a7248e3413b0e51a184291b2226de651ecfd420db854df5273bfebbece2e31741311008c3540af4c1e157d0a09d65ae3ee6e5c06436b2d82e8c8a348bec14601db213fa843c43887576b1655b8f522c326f9a60c1feeea5cbd128c8397cdf3c63f97b843a02dd368de1f919a6c7534d0d21bbe075099dff3aa75ec9474646a6883da266a550fa9b439cf4d1b9313ed3242875407e7c954fcc807151d6d16ccd83fb5ac8eeab8ae43af5768c73442700616e04900e04b85325eaa695071cad0b0c8b685e053a06edd4ed8d82e921b6a6e6ae92909404f36a771f95ea10e3910e5da87a79bdd09f68c42f2ab581a79114023d64908d3e2f95c81ecff1bec33dbc54adb8ab66c1bf1ad9b4def708d077623462c5d0f56298adaeb0066c19cdd2ab8a3c12a01791091690e50d0a39087ee09856bfb066726258fcfe26d43046fcaddac4880ed85d2e4e125586474c43553620b37d10a3e81f6137a17356ca613263a387c1060a855f175678ced2e1b2f0487e9d4a00300aade217e03de51a135f0f5678f32e0307027a02fc8ad219a29e86c495f0a0a282a92d4cd6358ae229073e6164481451b48fd5820ad04637d48f23d8cb346069af074eb9a672d4fe5177270bb64646dc24c3ec03aca505f8a9cc4bb21e8590cfe66f8f59d313cf86bed554bd8c8b63f06b8e92bea191cc37a8339fccb2e51051632f84c0f9870d2968b9ec55bbf06c566186ab36bc43122340c99d74a07700b512718a6f701f1b172eb6cc44435a2d4120c66282733fa7a22e116391e51ab06ed314b106cac92ac73b7bef6962d156e7f9ec23cd2b241536fd8dcc8138baa0b11e3044a60fc69f10523ffb84ae1a435a583ac3e028c588edbdb75e138f03504139c5f9cfe2c9973f1300b4353a1480e26e2f9728b45a0487a441e0f8168d30eb4380473998f635109eadbf6812325f1e897f5cad7364ccb5088790a54343b0a6271ffb7e88d9ec23873c16fcc1d92798235496f182b2745bd4df1028074840e06a1d443907f515d834f79fab8082273ffca1c8703706ec07302d4a1dc5b6901c3579ef56cb563c8329bcc739005bd7d057a2e6ffef889e543d6d20c0c69ba05a907c05bd2ef258f61649daec2202b531104fb1193431d8f12b6464ac5cf520b809f0b496e0e34308a65a25585cd8d4eeb6dcb7d73b3389464862fc9164f48092f74b81a2c99a57f306055f8bb8338b82627b19c35877cd578ffa6447564c704912b88dc9be5fd8703cf96ff3643c7c8334d61f89077f62eae126d8a69c0b406298cef11bd378804830e958ceca623ad97172d5f2ca7b5353eeb6c2d9cff0f067d1c7c29b5fbc0fba774220384a7009e7f8ca0e8db8b7bf73992afedb534080ea6df6a7b0d1e71145a7117a5e16f607d132f2c2512c3436580e809c45f797b0f +generate_ring_signature fa878e98be7bf61dc3b35153e4ab01e1c444b109729d4428288fa17dd1277318 c711a0a27fef0f9531030c710cc93fcd675120c2b44fda635fc3fe58ce68d5ac 2 8705fbfb8783ab5987596b73b4d1ff0156795c951e0c58b8091724678d9d4174 aeccdc9ec7f198a2f4824e33ed96d40215f9b22fd69cd2f0f84546093fa44c67 128378ed23f8edc0dd5305fa3e39996706a4c77d63b5ffda8b294b057c3c580e 0 111247f22213a60f396a26e60768d4772c2c674fc227efca69f0a9ea88586a044e59bcc34a4e844dd232d9bca065653294b347e78156207e54a8531efbc73c07b65d3a9e6ec44fbde5ff8bd520b9b29cc572e413a428f9005cad9df8183f0601f23537d7c3054c51e4a95cab5acc4f1aec7db14b6febb75e3266367d5d596e0d +generate_ring_signature 4dc38ce91c25bd6cb03ed5c0900c0b3a3d171ecd3f441d8bf38218214082d784 4279f878215bf5455ef8c99444b6a8cf0484d54010b0d1e8fe7adfe97af85f10 114 bd939bb5f29b32bd0b9b884328cb04da64b7a77b9d0f1975a42d74c06222906b bd64c1eef3f01b60b4c60765795c12d9d1e67df40fee313bc7b0cd7d769bb669 85917e8366e4bb6557e7e3d6d0851f0bb72cdb2f5605cf80287dc503333cb4ff ae20b8d284cc742818c38e284160f244b157b868fbb088f6125b8d039623d282 67676a9bcf5d35194deed9e14a41c23c81f5e96a82ec4aa28d9c54574c27cae1 c57610fd10ed400b939d00134aede8e719c8e4a8abcd18ff1b32f7fc96b9c937 494be2c5d57bab3e0e2df1715d4ab09912124a8eb65d44745b6ed4bd50718f10 d89243a7661e6cf3d7ef223ad3eac26916c71f9337e02d56db8bb710a41dca38 bedd21b55f596d4fc3ca8577ab48af6d0e4caf878bdbc81c61368799da91b655 c18ae2c77b4f87e07ed8564e9eec44a7fa841b9deaf425ea1be3ea7c800cfb94 789dbe3cb82cb86630f62da491b353336731b43ecacd9d76841917db6e265b50 d983f5d7e4980efdc335a6eef84da9d194aea20eadaa2b1cb2a2dcf6e510cb24 3fe08f0aa6a0bd5ffacc337d8cfb3ffa5ed8b1ad2717d0d51b929d9a33806790 686623a9f75270dacfeb22784afb28c0fea864434b32fc76a7c7908c378f1e8a eb2af211b6ec6d3a7583c0a264a4de602ebef841008dca26e06a659e933994f5 1e02ac91f995e252099050a2f9bf0a107500f689fc785f1f9d221deff9354e50 526b8e6185f40078ccdf886172921d276549dfbdd3a0cd27fc65f172d3bdc2a0 8e3d39b85ed3a05da76c47c8f70b76020002823548f8d45fdc27df8e6852af9d dc0915a34b7411206b4694438d629c17f84c2c07aa745284b1e84133765b88b2 6f1070b084301889189f7db4aefba51bbf037bee8dbdb086ddba9e9fa316502d 8cc4b03df9b06722f19ee747f5e4adb7b6a5ba3f9473bb33e98f06afca0155ff 7913b5b2a3371c554ee75295492f120e4fcc1761a2e90c163800e0bc8566530f 62a6ae54a6ad56bf2cc867132bdc8ed65858367d7ad38ffba12b25bc2f44a923 afd37d8701510ff65f36a4089d28a116e923640d83cd013c9ce2bd232f689cf6 d2b980b075ad35c7080194272558a004747fa3d4f54d52e54d7eb15a55fe47b5 09dde581825bb3e27bf96127461b67b50bb5258077ab5773544f29fdaa4cc48a d5e555016be782538646c00925362d280850ecf85d9c1975254025187739a0b4 099ec3a3c0b2448c583d9279b8df8d6f9d95a524d7ce35e5ad796619559a3c6a b1785018039ed1f0e47624cb17cba05a29daa6af97847e985c77b5de9e2f90b6 4d1d4f2af470f9e5c21fdf9985312b217dc866cd3c99798322d4332a75ed3f83 61bac09466cc764cd2549229e63b21ec0e00125211c18d2e784c027edd279198 e401492fd7498a1281617bbb9750fa4dea2f7ac1c05d4601d7b4d6f93f05e05b 4b50fa467dd7bf98cfdfa98b87a924aa5122b6b09fa19e2c6b1aa82e4f109d71 e4c8c681e9f7f7f0607ba801fa44816a802e81e8fc42faa343d0c0ebfe8e3e78 8a0e62086feaa8a6259ce3a21c4063fd4cc807c1beb371372f45e7aef1493123 fa7a5f9e71d90eb5ba18bdb7a9337243ba0d8735c4721bb9b65be880ac4ec6e7 8c89e1ad8f42ee19016d782eb7489ccb70b72be6ed9bc3861647bd742fba528f 339678b140b4c32ab322905eec017d40d039ed7e02c9ae57c68d2fa7e8584626 6c2ceb1dfd2d66f045bd54bfd7e5908a57bdd5216b7b17ea808e479c0e4d7d22 f36e853ea751ae307fdbe0837663f823106aded803139620a9cbf8d65d85c1ca 1514c9820ad846074be5e94a1a460efdd832703bab36dad44b04f6723e6b983f 4734b3ad5971011990b5ef237b152c24a493f304b337f556afba97607746fd81 8cb350acbb7d6e8c4da9dd936645a8750b052a29414c450a9bc886f1ee58bc9b 90f095ac9e66ecd8c321797c8cac0b6ead2328db02968466cb2a813a14b97b63 b6ca5b532fb55b28dd5788905f16a4c017617e45bd9b59ddb916a910c004c906 2acfe7532da865b1eb9f8ac6d344f535c2dc9e6f38be192986a02ff8968af635 e656d87021f86642a2f147b58869913e1721e8c8320ab77acdf32775ae4b816d 45847b41ac23724e860ede8d7fabed5982702ce646484b7c008a603b48b08a4d 6d0c71b58f122a7c9e6f4a71c0a8ef6ab1d598d67118486fb704c4d1fa8adc99 abc2b523b9f0111245bfa413450546125e29293df013aaf0783cfda5db038302 8649564fc912d27de342dec94c8dee22da9d5953045463bcecd0cf111a2e19e4 7ebc13266c2d167736b1fe30b6447f51d915a09cedccaf68726bc2dfde668816 dfe2f28066a7b2489f85153ad3fe8c42301efc3792e55764241f0377b1df59ae 53337434326613e0a7e86c87effea7123084637fd9ec488d6a5cf79678418d71 2fd4edb314b9ed004f702e97673312cd247ca6cec9dfdcae83ad266cf70d70ef 63627d0e850e46d81bcc136ad2aa095ad1a451f2d40d0954b5b9a99009c9c723 22d3231a1d7e59481188324c8a300004065010feb4408ae9bb8c5a6028099005 00452956c834dc93e022257d4ccf84a981262c21e5b0e364d606e5b8b35ef10b 8e4624bafddf2d0659dd9568425153ce7249d66dd796ae9d9a3958c207ff621e 18e20d7be6b02026da91d6a20212fb83271ca5c0e41d31f7742f9e5d7c9f75c8 0321996b983d708e35a7062ec8de17bdb8eed2aa44a33da0d2d1fdad633641ce bd8512058818d365f2a894bbae3fb48a8d8f683cb5b7ea86c20876fbcfacfd4a aa8206c4c30cae28a9539f34a06071b321f3bfbb1192890beb6cfcea066224b0 208e753a3e2ad56bc88aaeda6673781d74c4adea302c24bb430bc9d2ad99ffeb bd60707aab510cfe9aea55674eab81065c4481d823653bfdf4ceed5aa4cbd2fd d73cf31ffb213b78260d22a47f44376c188231f066635944f43b5ae6c487fdfc 04acf87b977a025d7aed687524a1612b4ec58f042f570dd537e2b7bf1f374819 b67f9de08052c5fb3a0b62ccb6ba87ea75cd0ceaa251708b40844267f6b014e2 1ba734db12fcc7ec682fd0ba5e50f8e201f88bacb180be02ca298bc501a04334 36d7d9519faf0b07448f93704963f2d5be500feaa60567408b62c2feee1abf63 fb64b38df7b1c5c931251663722e1bbcf64aeb53da9d97989e62eef5b3d33185 9934ed813d8e4ff4b96ea8691f2a73701443643065025a46b8d0ffed57deedcf ab16da45b8ff0732acd013c1a3574d7a76d4f8c510dc7dddf54723f8bc9235d0 cb6174b0170ef65b191639424a1d18c67ecb6366d5e789d006718ff192b74e31 5e832d1026ceafa167e47f8e846d6f9fe37d337fcd63cfcb0327ae049c79c253 7495dff8ec7290d6672d5ee2f5bef600fe0b573dd51d4d356dc3093801b1db78 218cd5ae20837d084034d5de568343e729c8c38c4c1aa9a7129eadc0b155b2b7 9466ad947fce451cf7fadbe9aaa5c7b6084f72b3421abe042121d6947579543f d221ba32ba6279137c88c55d22a192535e263d14033e775ef45eea9c076d0dc1 c428cfb99f6446666b862996418db4a552bb045ed8d8fcfd2b08deae775398af 8769dc3e2d1ad107d4f0d96cb5823602daf842cb159238809a9088752d340ca7 4083c2735f5c674f4896893952c90d275c6d28ec96961b36e75164b42d63469f c1f161d41107d3e258a1f77985876fcf02f05d98183ad77c5c943944024c4ce4 7e5fc3484d81d4f393f6fafe0dd4357ce73d08b6db85aa6d4a5dfdf61586f9ca 5c39e84b62a9dbcbe60d5625491f3cd44621d990ee50b74b7b3a7862057c7c48 34383e8918f547536c4a41cc91b2f02f642905325140b52f53589bba81f16383 1cddf5fc7808b6466f75be06c1b19b90025a85e241a44512e65a94b275058179 141df5ff0158ea408546d801ba6f0c6af9bcdefdf23ac134cf932835091c2f22 d476b262891e7be025046acc191b5950f598d26b940d883be486e5032cb31445 522d85cc71685afa492a62c6f4508207849af1987a7116cfc90e4c19ebe400df 1d1f78f69b6744335233e84495b0511e32d3c720e2373bf669c27a4990668e74 905d0a24578ba9bd1bc7b3686d1b08848b470ab12f4956824cf3b0fcf056c0fb 8a3768805b5a09ebc505e5c0d4242b617adb9e0f3865199bbd2a7be7579024c2 e7c80d659dbdd822a62c5a2b5580932a55c9baa6dc0818e8ae5467869f87375b 1b32d833d664cf3338c60fa3205c119e1ddc8c9cbeb44702459a97512575c832 12abc274c96f4ad4062309fa3060d05df560ec075cb78a80251cecacc9000a8e a01aff1dc079be3335a3efec1cb3be3eaffb22080688a4fea7ef634f8b1e5bac 1f4f6d983fe92cbac643d9a027741439190bd7d911d90c50a86bde27d8f4dabd 5d82c7c2e907bc4f46744c8bf1d8e441c21e28d3e194ad9acabad465022496bc 8177edd83f79988face73a00ead13d6c0ecb1db4f365b2e3f9568ad58cd8c641 2ef4116bf37982032ec32ceb4e1415331f4fb988957d113425ae1268975157b7 58a2d5b81c539077063bb9d470cc5aaa3f5014295ea7790b6bb002ba368ec551 452b24765b3f19045dd8c1b37e16823a6ea3c91d9500709ea0aa83880ba159f1 2e967b6d4aff868718cb1a7b26f50384b1012eb02313e8947d8e9a7c52d28a7e f160277b45b9b94f4004260a2dd9b2682f13081a1490cb873207ff96874d296a 6b9995916945798f3a838a968e36a030e64a240737da517d6c29fcb56e2e033a 3d3a67122086e15daa676f97a1f704008dbc47084ddd75bff2d52de83ef44377 e2c2c8614448e6b57b2bff177ce01d466211ec6cc80e26e9c4a9238657b13932 240762e3135c801ffc2df0714bc22bac52f23089d3698fa14c4f9dfd1eb09377 1b120c957aea684c4babc04fd213fcf10b20f56f8873a808dd707982c73b349b 468c72e026931e245e55e916ac2d0235c52e4255826f113b35c9857e171b2901 4dbd62c32aa0b9fa0cbb9c7f8174387957f23d2b7cf1386f39400b48f40802c8 a3d0a864f3226f810e69cfa3c6fe0e2967af9d69f12d39f85ce0c2c43e6310d3 3e7bf7715c43237de8eee1de54dc56b73f8e7982b30c1f93bfecedd49be9adbe 00ee10408bd89c7576d8e154cac0c7f743aad43ec26fe575f3a005ffadf84606 44 ea036c798a978bf880423093d4929c6d4b244a9874b5cd3500095b319b8e0107b33b4d9c60dccd53ff651565a687a88dd5f960efdcf29467b5d04e321c2773083602313ddff9a642f4ffe8f147d99a9eee00aa5ebd66764d172f2ba22c038c0811bdecdd5788bbee3e4363bada8ad8830d75d2465b4070f4312716c5ace376096929f6ca8867557eb0ce0f9012e6aa397be54579234001dc96fc2c7cd03250098ff6e080b7228981376b93b6af674bfe0d39a6c9df262a2a80f73b9531430301d262e638a96aa55fff4a00f5ceaac44159ed85ed37e475c524406dd096643d0735797f98e52db02565ba93388fd3e75c38b0c4d3276327dbbb12a51c1569c601723682178103a17295b81a060c099371a837569f362984a722e7a4914fe046050f9a015a2f91a2fbd6a0a76ae4c1b072ad3fb9a6122d25375033ada429b4fc0f0d38870229bcbdf4f94adad372b332ce787a3ceae44e84514bf26c6eaad4ca0437ad225442c2974481568fbd539f9059be80fbe69fad56263f0f427689f6910828e4679dd1ca75430b69897fa58065811e1e6b1b2a89ed7226b17f31a7a59a091aab5c83e2fdeb2c8b7bc52d84af68d4e3d57e0e540d38a3751ecc3cf5111e0c24e6744b8b6e2b61da27d8d2c40d90e01740a900df55f5f5dd2d21f1e2d4210d097c362d0d66f38894b4997df63b06d41c8094231167439afed0c23349b8b0095573ba5c72bc1238ff91d11a7fbd1bf5f03d64b72f7ec8e8d0330cdee9ecd5026f23829ff8175b81f03066bb2b7589c37b88230e7e18840504bfee024148d10d0d3e246b90670c04aa1981bac67ccf1c0e81debb920c53e3e4bc31d56da46c015c4039eea9c3bd4e9653ae03c2e634490f2693dde90aa6a38525eade34478508e4d8a6062c07b2020eaf40b43c8216f4d0730388879518c31f72bfed2ab8e105fb78a06f9ad875e380f32ac2ee353cd5a293047d14272c9c8d046bb297be680286075eab2fe1a5ef43ef61e338bb7b487a788c609e8b74cedf4fe3eee9c2650444887aebff0c33649a23f25281f7393eeb6e4ffd211edb57a0a6a0d2ccc2eb00509427ad297934ead29d912b86d7e47f52b975957dec8a3aec22018f93248e0771094b471b590fc011cfc6681d8d5bb21ce58f87e355cf0fdc27443b05ad60084c7fce3e3d401865e252df9725c49875cf206b9c15f91269bc68ba2b9095e80beadf571b5e016cd8ebd2aaddd3bf3cd512787a6fde7e3d89033cc1f30e5bf20749985686c20c612c7348e2f37fb9356d292337d90f0c9a495ad8b447e031fc040dfcb46edf034cdcd7f7317b2625396dcf70f384ab683c8480a6254308fdd307c8c962a133d2b685fcb688d7e0d6764f4efe37b1e3dd6f6aed7bc247e1a0060c061811214a3d0dcc33e7a9519ac4d7f1ddb1334c1bd28307eafea3e3131b6f07caad0dfd03da229cabf9a0c504edda6d3bce3ef3e754d5d4cf078c9361a5fc03d8a1b49ef905630712609ecde497c1b54e008b3b38527177df60d7e0ce88ae0287b34dfffb0daa0f9ff632c779dbabd8edda87d779ca6c11296c168f6cd49105ab86b375369ce70a82a0c1e65338584db8d2c2f007dd9e28ff816c95d9c4c10bc439acad9a05fc90a4190a10b1a3400f97b1c34f2bf6630bf279183567ab24018d461817de728b1b6727fd62228fe3702e67310bf6f820483b24a43dcaf226005761495e717fe30e5292f051f82281a9e85a8cfa0d9a52680a056ffb53c53f041a7da1db4830a5b14899fe12c8337f1a00f88796541e37a85c7a8b6ae8cd2c0db4064eab0f390d15608123eecf7cb49292021d53a28016238073073f743de50e6e01f71dc1c36244724857c176c7340a8907a195b2b19f76a0287d42a9b5b407b570bf698b40386c641bae6f0c2cc72afcf7706fe65d9b78192fef1da8ee690f93ec8ecf413c06bead493b824e90dc8c5c958f888c11a6873764751d17f9e301998d47b7e4d1e00ad610eb772150bb77fb498ef0f3a844c7d45187e9d04db3075e6b9ac21daef6e3df7314c6627cf646c71c85b05dc0a0ec2ff4a0ef58331001d32d3124beeb1212538450443bc5ae823506d4077eabaafa414509e302e03b08335ea3964782b982c078dac2c271edca8cb798a68b974962753935a822c72307774fbca91cd19be4bd8b5ab1a81eb007c2498db6ca94e74ed61b1bb5f556cb0419c28bc88e8be8b8877745e4d050794b2b1a5c549585afb5e4022872b614c8088f05ebfd7cace6e82f9d069d74a04b67e083e58b1514cbe89bf5cac23593590ab744712f1c80ed6499e25ae85dc636c965872c7441b96af6b9e1766a4502d60436adcae780f3ee4d097c6b26591c62c10eecf62dcfe8b9faed1cc3436181f9096fd3e6546b8a683d04ae9d35a5246e26c0c3d67a85523552ac135e8fd74c4e08916a7051715b4a6d8bab0c88cafb55ccfd4d77684292993ef1a62ddf3ef00f0ff1f3ca2be5631066766d923ea5d45c04513b645471d2c698fee5c11449cdad0420191d2f6f7130db95889daeefff4b2307f441ce2e1d2c7b9dffe373a6db4e058796df6a4a485647a8f9f660adb6f35a9298c0f427f3cf212757ab50a2d470008bc2dd5249fec9d0f2f401b8ec7d172436705d79dec78ed38d3c2f9284413a07db64c092b429a9921c904aee753ad3fcc1c5779c2117fda0ed10a6ebf77d310414ef8c089866f7125f66b759d1059ca1c98ce2ae4d90e68d7af18d7931a5ee0ab40c025e00fd8c2bd450aaed9d6d16ed08c4e499ebcae6a45cebfba66656260c8d5dbb7a0466e3ec9f214327c2746c946af769bff38da55dd5ed308424691d0c1ffdca53753ddb7be6ea081da0b9596fa8ea4d72ff582b86c3778012f5005207f7f8dd669e9108306d477530008c43cc8cf08c6497210532f729e3f084fae80a8bba574ef14b654f503d66585eb2acf19e2bf1a67fcaa554786452e0bfd75f0d0bf6b10c066f6de7081bb5a8b9c4e613a3391abad46bd9cec3317f15c8d12100d4549ae6bf63dbbf3937a6278b31bf80a987a10f7000de1d6181a7a6bc633d01fbae3e7a41b8346027170806b2920c6cd8e36e62eba3103947fd45988ac07d05fcda94ab8708da35c84fa5e4cfeb59be89cb51afb39a3a6ac28af8172a547008076d47811ec74c8627c4d0306ecf1f51ca03229d63f31de267e7ffd299557f027cb8b4b8fced8d08561aafb32970bc3dca5e617a91861d1243ee53e639712b07ac23e107742b7316f184ca6ff68efed2ca5bf76edb065a0080df2443e488350278f065b68920db1efd041dc7663befe068b35da3c3e94e04dbd13d929dafa10fd164072166633844f21423095e878fe5902418c2a2152e8b78a009482492ee0fb521d632ffe50ed732d0005f9d47b57181f7312b1a41b1c877ca02162d66d601aecf6ca866dc9c8f2a4430a9b9bdb740f24014593c53eb244651b0b09c605501e3228f67b83ab6ff8fe70eb5a14b66a44e2ac7180a4976fc37dc8227d20a2007fb88b9ca14c8d298fbe9d7eb42ec2f53f10c8a1c54cbac585d169a11ae73c508b097333ca2830915f8b90673ca90ce52d2c3fa9a8a4d756a2919d3c22a67ee0d0478f2776bfb69796417ece78a52c998492d7832080f5f52ca5691e16b0eb305f0bf5994284bf5a83cdf3f14cbcd4a5ada94b0b2760f3a865ce2e736a527b108e1757d33ed4c7f13fea7e327207bbd437f2f9372f941b53874ee6249c8dd46066659d4b020e1edd486de54d73fe49a1e030d9374f0139a0a544239f5716d7a089b2f72002b6bf05886408c2064f7c9e82bee35e0d2946c91805c26276f01830e000ae9b9532053074c0fa5949c2fce2603b1ad334387f0bc64959c167da5860f1d8fe87a047d577be053459554790e247b46a2e0c537bac03440b9a0b49dd50168654f834fc64604a3fa582198f8cb184a1619c5ce144ce86033b14f520ef30f882e24bd959bc5f3ba43f809d3705fa574bc4e7b8527d85e1a6ac58f735e01074e8dd639aed41ca1d533d8a74b7e691de1dcaf11815940db6767cfbdc842c80fe4502989de3aef26a3b77340ac584cff691d3628ca727bed46be34d186fa19064aef1892b3eb4e5265ad2c14815dabfca977c57412c2ef14cc22dffb4e975a0654b43d8c4b288d4ab7b7a8766a6d37f5f534dc3c61063bc3d2e01ecb534de806e6bc1e003ae518dca85db46d1f3f5b1a76ac554cadbe80d83ad96a1ef524880baf80746fb9253e0f56c7eac147fa9ebb98c66cf6292806b83d75ea8bba8efc0f3859c2632502cba122db858f09e24d8b2332e0166848be6c09007dbeeea91e08c61cc0482496f4faabb08ee57b1826843f13bc204cd1d5d30fcfee699b0eb50603658426fb52f84b934e900fece01c168a5f0870a1cea2c7387f9ae232ddc40b611915a52f77d95e2eb5892bc2d1646387a1428f36981b44e38987040d0ab10ebb9887b6f0fb63cea6debc60a9c569bd56f38f5f68a57d51adb6fc0aa3a0e700b9051a49969343ae55625fd1be7d46f9c1c399aa82b6c57e8fe808222dff0e014e15879a6fc3c0e3d49c6ca77da12568f78ec40a3dd05e466035dbf987046a03bd1c7973f04d98c8bc232fc8cc5b65583f3944bfbe212dd961caaa0fe48c9d00f0857609e752c014e83a79631e866f86e7796b8b2501f82c81b513a4b59bd10c6f506c4bd45457f45a332132c9faf89ed50bf74faca6f79ce2ccc63d7e528e077874de714a5862df11a193a8a2397928ad3399a6d812511bcbd579b40871c60af3e87e9fcde3f04031f8ef4e1581b3792c20df1480730f57c9e0b56ab8bea30b6fe472c524ee3096a5ac53830d39659254108d2d31ee7700ce45f8566b394b03269a3fb390646237f7de1d9c6bb9f916a2353608d354d0ca77478f29557bda02fa89154b3184b63fd62d953d52f4e1866b72e38fb1129c5a7ec57532f0be18051861c0b9ca8bbe81d60a3f931b731603a2f2faa76b447ba2502f000c1e11460e8925203532a0b182b2287696a430ab2102e919f6c38142c685207c4d7119d00b6ce2da2cf249fff1298c815fd41768873677207c28e25bc4373f1cc32463c00540812b690dc1fbc38cae1573176b9e08c59c0ff6c4717d459f234ab209b426058b25e7789188f29ef01def5ad20237a3d0d2197da121ed2920b2852288d6bf0ce025da3764337443243d16c5d2dd1a6557df48c4ef8b6ab1a2c215fbd904b308be4ca810aa8a0f9acdd70fb3ffe66d2430d1ee242c4e1fe2c3dd06a7567f930d3119aa0e6843460f72e60c67844a2f5d1e501dea1c9d3892dba51f6bd7b55803560212c751244f0b3f97c2100b93b0f096d0194aa51d2f865179dc00e6a2ae058f9563a46c8ab11dedf879f079f6469056a0f9e193f60f4dc6e803dcc7e97f03d292472a20b336971cc84c0df1ad14384433ec148b0c78e1d349e78cf00e440c9a7afc466cc762d9656973ae25e19886182b5320bc468d71c955ed6013d38c04cca336b21e593e0b5574a6a5037ce4c8a9d7faa3141e5e57f6399acd04a6cc00e6a28ecdf54f8fe5be58832ef1e2a0e9705974253550c1a05e69c3c72e3d6506d4431b8b57769412492b1a9c9fcb7190d551d7199d023ff5d858ef92cda2c70551e3b7a0f4a32cb83b5ca111e4cd1005616d8464446c9e70d32cc39dac3d3c06753f849ae0d60d8655418e94c88ebfbedf0051727936cbf825928a16b0b33d07e08ab155ac6b7d07daa2a83d61548c3a27ea09fe450de550f0ee29cec2f03a08388a63328c23b1a0b66eeb266f837abf657b9a649eb35462b461bb533e3c2d0b69af1ee57a6b1df990a4574a6bfccca5da1c23eed9fef2a9225663b63034190008f42234965875731dcc76803da1a58290fe2e5e8f88f44d9bcc035809627e0bf6632a71eda38b9e4abf69fc3cdde99a897920136517cc5be2f3db7043925304ac27ac718da62b7c550e3e8a908831cbd0a5811b211d9f0f25b612811b7c7a08c51e7fd191b966d012f22b75d950e2ee11eae12de1dddb061e504c2bde0cf50499a643b46797e190635206a51da4a9e124a6c0fa78827db20d6e21df67441a0223e7cfa5ea7a34a76823550084d3b8a6a41ded0b321b6f795a792d4098d9960580d89db4dfca3eba564e4c0743d4e117e8abb7366fd0bd6a175dcd020770930bd8e6dc9856b3ebca486f38190f5a3d8e9bdaef525a4275b2cc6411cb06f2c30700c365101ce07900cc0b580556c0afcd0ee7ad0e3c42273ea776662081b9030c94a8e15630532444e843927db3b78cb90771d3b411e19bf7ab09dcbd3ee3ef0a6075746f5afce41aa48e77b6680310ae06fd532ca3b1121e8a1392a55bb9cd04c8ea8bfa4153b24acf1be0d25a9c1371f43d3fa02d6ee4546524a9e3a1c8610694519c16fabcc3940f665816ebad79c5891f68a357b8cc36b8fb3950ef42780bf0954598c888d722527f0bd89c856e2e9a99868082ce00d22136172aa2201f05a3d812e20cace6d618d8f27af5727117fe13159d7b66ece3eab1926e5a22f50bbfa6f0026ff592ae7f2860f78579057db8f7d95ec685b07e47e1505cf7674c0f33117901aae02d8cfe31dc75f74bab3acf5d708e725333a586810e5cf70647030998795eb6bc85216ceb58f1f3e966c52e1fd1e641dfd6a389f2f3baeeebe50eb464d2e5b3b0586aab42d33449638378d83c5e33b68a72c581a3f73f851e20094e0fa8196166d1a392f6318ce7571c3052398b82cc427ed01a1084074b85f40a592d22f3afa34d5b87c09804f4d261b491b8eccb3ad0b6ddf6bb30b83e611c02d018f85e808bfcb5e85388e1a80dd63252bd7e995ecaf60b800235386ee7660a4cd6c4c48adb98cb487994c4bae683b0c4d21e0e6b297d4404a13c7ae74c030f54825445da3ccdea4916d52c9da05c0e209838597688fbe1fb71c0f17d1cf00a07c909ddf3515bbd8a971999f0927f1748b535031249d1e5c61a97475c09b60d13b2c49ac131d2fe8c5b20681921adcfcfe0bead3db0cbbe9600966bcab0e309e0da71d8b3f00a0dfdb0a74f0337101c7b7666acaee601ae248daa8c6555060ead14fc3a792d05e35b99321429b1b81a392faf52e7edb4ffb0375b7f6c39e606df5df228ee7cc06b285a2556de8e5b7fac02fa1dbad6a1d49f89f23087fb2902b46ad07e8b68857543002ef7a69a65fbcd04886a944f8fc358fc5cd1bd8aa5087dc0d8e5a44fcf955d989504e837c846173cc1966cec776108c991f097c9970db3d8cefe26db33fc78cd1556b6966f420c6405f8ae4082c62151af10e7ea730fb1e7e63373d0e9a8e0ff77bd4a2bf63f85a2536ff3402997b7dffb20af408409c391bc20afc1f86f1aceb81b04ef5c22106a24900cbae576340c191dcde898018989906d357aa9c9bdad2b288bee23d9c77ecff2bbf35d7f2a6f097bbadaf50a6f1ed67abef43550ac80267233ff930e9d7b313d1c21745854e16813fcae820e8f4cc8725769aac0e6f00b1c862ffdb37dd3f8fbc62532bcf667e1ec86845207077d85868156d541ccb0e1ebc9b96d63660b0b1a966e994e61b572302f88e908f85092fce00b56e943c54d861705ce9e0e5531f1a66bda27078eacc36ef3590eaf00c63b5c1675ae6ada64cf378e777e7956074b04a813b34fd929d0acab10030b8f4d6a68cd8d7b835a83a6b0ba7a6afde6862bf328b62d67a8218dc20f190cf57e0b5e0f18ddea2cfc96293d235e5943ed9f8afe5c33746df67e6ec905050a6024e1d446198252bbf4014b5794b8a7d9fb74873c1dee6c60330ec1d31bc60dee15c391b205f71a62da15f7dbbdf52463932a191884388609d9bfdbcad0d0055f334ae6822bced23d07d303cd35841aeffc65388ab80fcedc66461261a81b0dd8cd34270be2542eb2d4eabcc42a9e083a07846efa79b351db493c5174d84900bbf4e442660c051ebe059550e2c67a7a8434faab10c6bfb2802b1e2f04cced0519d047ce3c502abe2ff94dc8be59f6e415016d2c77f1b54bfbffe179c9ead70aec1fa21d4a814abd41f7d5d19bc6b77f4ab14298a1bb14f7483197fff2b6390e2c3230700ff95ef79681078d49f0ff3a8f51138771989c3c1a8e18e13e49a90a3fcbc0a7b8b2458c0b6beca04c5dbdfe86b538e57850222a24eca0476c73b70654f3b8e29e227998fd54ffe06ad51f831fb4dd0b07258a17a9d27595738c060b4a087c00389c596fb33c838f4c7581663317da9a3a941056d6aa000734e380005b7c550677862c23689eef181e8107d1ff986c9491de02d8f6c001789ab222032674a1feb232ae673757ef59122249400cc1ba29a953815aa8da9e8275b3de0f3361574f998cb95a4087c880c87c8593c24d82772867ed9f2437f8b2465a0b0e13e36a5a15f56e04f4e861c1dcec4913c789a96e556e1b8155fa9e472c8007024f2f0d7978a7aeac7fa54d59acc6bea8758dd0b215a240e8fda5b05440fa1c0c933d982cbcb6e543ae08fb82f33f97a0509da0f42ae235f42698b1244e6c9902b5cf3fbf95688c9ed3ac06b5073a03faf591624f4f4e76cec2a590da26d60306e34eeff5b3f27e6d8cf3ad0dabf417397883c7505b911519b5bd8aa9f9d9930c720d534e362a1582b9e3e008c339d43e1e1c3a9f26cd7800cf4752491fb2c70a3e49b2bc960a44168bd04284b7abf6506672a29e5a5b42f27ffb1596c544af00709b832bcb891e9fff8511d0c75481db9e7a0392f16db45873fa1d45d7a85f075d05b31ff42bc26cfa9c87a6400939e9f32a7388a867877de470cdcc39e213029cd6eabcd67af7bc6af0a981d403f2cd2a863fcb3d31cab9e34f60115f5be00a5fa74fcdb120cfe179703ba26691838145a2c0b1043d0296d27e88f173f3160cfd1fb21cad26e39ad6dede6aa15b40cce45443a1d9b1de22b9e9c63ac9dbf4084e1204fc9cd654f68572705fa66eafb934e924f64682ccd4c65cc38cfdb956079dc39002ae8995c740352ddd5b84795b53a7262d231d2c309ad1888301920408173699be4eb061febb4351ca82398893cfeacbe55e41d7c2a7046846ec0b940bc6d1df129c92618fc7da7d8c7a4f5b100faaa52ab538d592e7d38f446fb005032356d8a445faa7d655efe6d6459d05480d88472df5ed85d3b34ff34ba6a59906ef7b7849802186ba3c50abe2f2995456da2803cd96d0d97478e640942a63e603e22f2e89274c79e8fb3f5bbdcf3c91426510988730ac9637ffffb660ea1cb70ebd66f0563595214a563452163252b3db9be60d5e129f90f55bdcbad2300c780ba70b30678e11e349bcd4d5b5a80ba85e80162dd08470eb7faba473c3f4605d0c92cedeea55f423c1cccb7c0a9a2479671b96c9ea1a3329c1df5a71b9011a3a0941c3b646395255e0e65ad84ab2de4a8e04c54995dba0a8c36bcc4d56971e350b71d7baed141a9ee90672ab3eccd024095a354915bd6a3e8e6aaba2194ecb8202c3b328b8fbc2b8587b75d0ddae8edc5b9129f883e1ed4210358a99b74fd72205065f05f72ec6f988ca2a5d713761c56b85f808d36638bab082c7e670f578b50f60cc3cba90bfbd2868ff15909e92da705f38a6e4ba0b0cc904a4815930232600592483bd5d4b550eeefd8a1567c9e349159adcd3c41adbac9c55ba8ce494c10569410b9aea3b33dd25b887d26a618e5f3bd323672a56f4cbcff3ae8889e7ce0aa2b86431718210ef6a49cc5083fbfa5ee538ff6b95fe4dd7cc6d565228f8bc0f9e3ab33a4bcdc3c49f8f15a8084b9526e874b71989ec8be22245e65944588c016cc701f511e4c5445eb7e40dae1011769be29ab8cc5a3a67cbbdc60e35f48c06ee90b79a7d05059137d32fc78ea7e7d302c616a5bd1d88fda2a6ae963f7a5f02cd71f007a49718f0afc04f7b0edde7c517cefd623b89bd6b5644d4afe353ff0be7ef516ff029ad53713ff564b808388cbad04d200f396d69ddc9f152856d930fb36ffcd1919822df876c3a43c9e867f69efd77b5698cd7af8d63d1ea7a5b0d030f6fd54945de51a6a99aacf4c1be292bb858fb44c2e018f62f66d78fbb5a5a0f5f8bf74e1cec27f4fc3236560949fc57577978c2f06284e4dc5bf61b501e650f09a314026731c54b61d84adf4d9be32e88ffbeffd4f094df5c5f0068d13fd40c35ba9e69a21063aed2846db36ba7b0c6653c1771f53710927bf5b58faf7fa103ab86d54d4d8b58fd7271845a874ac1cbfeee7bfcf0b3e69c466d80dedc808c0d8d704b4a52025b34668b8bfd8d50f00eb2b4229f7c4a4c6a03d36771b093170f +generate_ring_signature 52ab38afe83396f4a7abbbe478732d39260c8086f1b221f37018ad83a56295e8 52144c557bee42c63d3943a1da22bf53c53cf47e18e39ec7618060fdfa2a056f 113 2667878a43543a286946b627dba44ad682f51a0ec0dd5885c0c17706e1f27962 56070ef37ef631a894808bf2998c18079451cab0c36076b94ba7046928c7a1b7 09d61f61e982a1e500603b81bc56fc727b7ab3822a8af511a213619776bc559e 3d16d0811f8d8b1ff564124ac0735d77c2b62ed3339a2542deb20167ac8d78dc 8d1e73a58271f020e21201b77b3e1a0d5802960cef0da9c30f88331d1e53b7b8 b15c3a90c6d12c50c12ad1f1cd4f5cbca77abfbe26dd1003c5f92d256b7d9a2a fce4121de27006176bafef1742169e769f3a25a075b413ef0c3f7affd5150561 23e7a79af125e2cc0907abc7b61ebb2b6d712691a6c556474ca79c1cd2873c0e 1806559d6d2e9812dfa4d97efabbd8ecafb8331b2a836e8b94212ae21d9e935d 3d825ca551aeb017319626965977dac767f90994d4574ea52b0600086f745c96 9e5d692b5e82670f6284fef4efb6a3dce320ca9644126d52782f922e2f1cb62d b04d172573b99e1b8074d362c5f6b997b05ca74a5aa4d35c957853f9078ea90a 451d26bdd2e49368fca9c893cbf9a2551aa0bdb1df3d919a75f16da4615ac00f 39123ff66a0a8cf47643625b997f3febe9b843028a733213f920fcb0fb224321 2e344a31a1f4abb279b66741e22b00d72342c93ddd66332b9480f38ad010dbf4 174dad431a88a820abab2a8ddf30bacd6c0720da30f98ccab7c821f2ce13570a 8fd6651dd86f87c6e9baa019e3222459a3495c481b5983bc1f3bdd43600538d4 70bd22972b4e8c895d93c5313a2c4de3f096b2832401b34492d8b63e4c828b93 724ac189a333456efd5ea95bbd6c3174180204a0541f56c9d3c4e82c1b9a9430 ee0295b969e97cfbe6d47f879da1a789cb27f9bb2af514fd549b4c8d8db67ea8 6516c5de7a016a83efaeb52dc3810e147831cc6a7bf6d556aa711e3f26ae74f1 3f8aa4e1f31046011579a2efb2ff2e98c2e94868bf61f529b28e0c9dc922e65a 40f502a1fc081d3e0870baf688962eaeb053998be9e61627a99ea713bedb7d33 f5de2cbe86740e20007d273d240495720423986ad67d13022a69f67eb65c9db9 a72c9067245c4f1f7d3c6411578b2e02e18c373f78957b0c011d1ba817d5fcfa 2a6f3ed6fe0238355cf8a1bbab5f2c1f5674c2432cd8c964f4bde62d1f84089f a1be89b4e0cd03a7080617e61c08203f935e63429a34f8ae637c6290a5f855a9 dc25b39ea362c07b69225919ec631e1de3e539a0c99076421a03888c136040de c42663cb57da0d67ce5e40583deafdab17af7f37f4e9cacac7be29510b77ae60 57e85e3d5bb0bfb51da145a2345412b3d4f1bef0a9ff125c2533e3a76c2fb261 51d82c0f6c42340c6f37f6a4d24eeddde756700a2a674f0e5745b35b0493cf47 d6239694cf291b89f96ca1f448b250193f738547b70c7627a72d6d8c3edef2a8 3e3ef92d08a0f37954c6ada45ef29204a7dacc626c7181d7613f2fa613a7d515 479b439e8cc29f89d9dbbc3acd98fac89603d262b014a2f876ae4646ada1d401 ac9c823e44caa76f6d0d2ae5da6684a6fe0fc4c623cb28bb0ffedab0de052047 bd47406403e535790864051db0c1c2d09b0e7a0fa9df52d23036106cc5eb915e 19abfae288790ac7d77c0581167f78415897eacc19ae8d537d118c47fa2b1553 b5a5323c096b566ec9e91eb9f4c870869872499205d05cf3d7b58811d60b1f58 36d46886bca44dcd834fa408bc22275efd1264644143dc3fde9814b06d1ca44b 758aaa67d8781b59b605c687e993e1d43913e3693a351d9addaff6a21cf196f0 32c1e4af3a5597eef97f47511fd377a961dcf5e3ed4c1b8ffe981abe8ce31a84 ec3619a0a1928ce8907e344b0c7a8e87e15fd53ba8693341138a5714442e2c34 c6bd33f733cbaff02918e91d14855be7966fbe29a04b6149833010293b04920a 6b8a52bf35bf9bcdb80f5c22fa241b7342b4f6bbe085f78e5096f0647f0298ba 3222525c78358332d2571622030a2c91873a8912f815486ae7f1b38418cccb2b 5ffc8527b801db0fd1f9fa568e6fd8915afd4d2a12c9e760f8a0e3f65c13fee6 65cd0954290ad98ee8fc0ade4ca8931b1f6832199e69496a57d71b25e0c4da6d 519dd0ce81bfb0e2e2aed5c4404fd08cc5dfc32cdebd2c4eac8e8341a99593dd 028506245b4782ff02e531921ad41f33458316d2a04701949c9a1130170f667a 65479d0e3b8c5e054a42165784f71979e44c5ec86edbd9360d364bec827c38ad b056a8e8159cb51dd0ff6854f0b5cb7d73eb6af8a5056f7a157000fe950956a9 121bab1f2128a355510aff20a2348ca726015f44b14247ce79d17147a4c4060c 67b7dd5f52f743bf4421b83e735123e1c17173c562bebbc92f241f748c45c682 1e8a956e74016f5431b773efa5ecb58a388e94f91b9a5a4d928c90b987298361 928bca6f3ef639fb399620438c215df701e992d50c32a7d519c9c4029777d8a9 4b928f62407d82ea63fe4ff44757b0e2c99b3d809f473a2d9856a4e7915a4048 2638494a4220023d7abb69e0663e321321a39f10ed88170cad8c098adc2b5762 b4e4fb545e5daad738d95438f9caafa048eb5b7b6cda4984f0d98b218f8ecab9 4e1da4093412883d96f6080929c73604c9b918e4b4eb3263edd50d07cd370ac2 1825bd87bd4c8621c5d585e3a150b810734249c5f8b98826d4fca4fe17a479c0 afba4add454e41364dfb8f3f10e613f4379125837d809f9617c0c59557d8ba06 be6c78f9549f725e77166b209a3a62be24858c6fa6730b751b454d8db46513f8 d72dae253fef5793e2c4b21266aa5fd4bd4bb298ac12024c3136bacc698bb5b7 3cccdd128fb4279cdb8782fe4e891c92b9212ca7df38c1281a7c23bf0488c7bd 38d08fe57e0103171b5000d5f7d6f741eb51c405dbcc7ba351ced61d610b5e0d 7877f90fcf24f4cf3fca2f456e942af7cbb7c713aa9a26a437870bb0dff5adec ec4175d3e5369062603878430224568d1d9f6b01b48a0377dc82f66f9ca309c7 eb039d354117c3df90c53e9eb0492b4cf2e4f6b4b57bfb6cc809c22621edf016 6ceb1f244447e6c4fb2f4b7abebdd227271ae511383515241e146e3401b47afc 65cb32f44ecb9ce88d08bac89c7b36eeac2e52fbafae9c104b65851d0d3fb689 7ae2db115d82335fe2d49f12ef8248994b958fbedfde2f99dcae864daadadbfd e547d52a6eeb118a7cd1147895f5a75dd0dfca914d50706f6a7033235e3ba07f 6ab9a84a88f767a1cbc69b2740c05acaa9fc9d84ae58cdc77e01b0900cb19ed7 601efbbfc5cb40884d47fba0a5e1e5750685788e4225ad69319f0dedc8053cd8 151f98617ce7dca0ac7bdc7910fcf5afceea4b8acb144110377b458ee877eab7 7dc10dc41ad38ef8c879ac0c434ba7dfe0c4ee458285bfc0b07be3127c970bca 171ce0ef0830d4ed99f42e0c20d063ccc3c6ca50b747b36f24572535d28c77ae 3f55caf66c98c9d773a90472b2432e6bcdd84a286b518a3f3f95e2ff104847f0 98ac27c0ec172728c1284698b62bf64afcc2cd1535ab245b6ef9e39e1a1ee486 38a2e914fce5327a0510a04ebfc2843e5ca25b995ec616bc1e214df14cdbb2d8 6ede2050ccc3ce325d911fc26311bc74f9f09f51096acb61ffff4b751705e68f 527e46d547a1337da68feb3065d017f1fc3d37fea430714287ac3bf13de5e43f 8208fe6fa4af1af795df8b07f36dcb08d87f9b5fdbb06059b6ccebb6cea006ce 41cc08d2fa9ca82df528502af00d1d8ab83840d3961cce3a000909322c393b15 c04393bc4ae049524110432433f0290f23a88dc4b0bed266bd3c139180f4571b e41fde50f6a402f874e366619f17cf3af987c3641720207c0e48b30f3321d462 d680c870d7b43c6ae308b818dd6d14b29758aec61195bbd3e354221c897f5458 352582605e62e595ee051a22e47c7f10c86a0ea59896c907afd853c418893bcf 39b1b721c361d5cc7d3455cfeb782aec7f122348a62caa0d67f4fdbc98665503 66e0b54e8d0d0b0b61a6641685c1f3acbe72bc1e55254d88a2cbcf50399fad68 c3afda12547982d6afa86121d0eae5a549d22192469079100f272ab6d8831338 3d8021bf75cd1a3b64192e532e21744cca23a32a7a50b459b635d8cfdd6e28be 176079f943ba5100d89a6f665864407996e7ea1d554cfa79322d38300fbf3cb6 735b236716beb3d302ad5518901b9a523d51fac099752fa0c1714fa342ecc944 d6d69f6f715109ba6b81cc303efda0abd38a312f526b49bc058ccc44d076be32 1e2dd2d69008482d8e7a02b3d026e549d61455b1add013bc88e131dbae129b16 e02a717aae13cc6ec9a9ae8bfeb952c42d8fc709233cfd1ef7ccbc1bbf912bbf 009b16fd9effa67a4c4c8eda2b15fcc67adfb5aec379d592c6b80c63d3750a9f 40cba48b6fe177037c2d2f652e35278d5aa779ec0e37a01a3338154b0109c7d5 fa94d28721ee024c3920937cc597b6e440056d873da9183a4121740079e91c52 b87e5b2635dd6e4bbe8d22f83e55a9b088fc79bfc79b3866ce37aa3be95a4783 705a31ef4484a1d621a26ff0b0250ab8f4b4feed12227f709c41b03aef4739a3 5c424ca54db641680633f09638dccf23657a0fc34f09c2ed97b3ff0193c330c4 f00e3bff74143e2eb81b5aa10d4f8e110d6f82c12a6ecd870f786d120a863ce5 2b57a48a8c801c44fb5a193e27b51a3b63ba879a68a603f282ee29bb06dc7b20 b02e81daa731f503076e99d7e975ccd900b8a4f4a20768e03225a74ee7769c6b 2dbc9cba9c5b1c99149fff8f97be8faa42fc16d0be7bc5ac77dcf8886a290e32 0027178fea8c180bdedbfcb2bb7e394185b37a42977afbb70ef8cc3f5b2b1997 c417798c3869d3bef770f05c7eb4e89948bd4c76031dc429d6a52fb199e16b02 829c84deef8ab603af8fd2a06bdd0daeb12820c32345488d074fdf0c8f004879 4be25ba569f1b4aa9c55a3e6dcf232d3b3ce4cf9990a6f8698b24db41ca20eb8 37165ce3e5c4b593456f947b9813754b44e40e8407b81178db50716df2352374 b899ab2a46ba764730d7a822b6e24afd6d6d276c4a73ee66fc39eaaf2ed57b84 fae3ccc4f506532923a8f03d2d227f60d7870ca2c855098aedabd89abfebfb04 108 1a5ef4371a62115c4738ed0475f022b26564a53f4fe400828c73b632922d9409e44ce61e1f42eb35de0279c8f2cce7576937a66ebf13c317ab8d1e16497bb50e8cf4a3ac4f69616aaf24bc2a788032fc98a81e31eac6a15e7d51a1c356e9fc06953254e68fd218334d1425d4428a417f5a787d848f95df4fab09b3a46abca60db13ed3720b4ead6923126bd5d801fec0ce795ce4f2e7d65cfe67045f1aadf20e713db4ace52a22c88d526dd37bbdb2debeebee5cf8a526bcb6307812d6da5105f8f5271fb36bb1aa9ecad5639d5d1890125a34025bec5fd3d3d347defad9050ba5d4cd28c1b83afafc7383601a631efc3d3e7b8d9324d9ef2e482af55b4d00005e2f48dfad0757d0ea6868f66fee9c06a75f8097c417bd52b5f3fb8688d55b0142128c7814c5770e39e0808fc5e683953a2c4de55d481802e53cb005c16374036821eb2b0ad3cccfe05880b051e6ef461ed4c99a155152dd5e4d2976837575024572406d50d62bf88713cd45406e59b96fcbb227f41b9e1d738300a9ab504b02745dc8be0d830b453eb453dfc1351396ae66e50f65468cc40e93d58b5be92805a11c74feff56deba891431fe7e9e07f585a266c506fd79c8f7f6011375a5220991a3967e449e92a90b1d8c823170401d49e721e433af0a58d7e46d3cc534880fb8757cc001a8870309ae882f765d248f7df51d9ee0d0f0e3338d01c20fdaf4007c83cbe4188f767a8118e855a805b386ab766529b86f7abc3bb216412b94d60bca2d0be6bedb4a38627cab5c3b23576be308c2d6c261588e4f2f97907a472c028345c9fa3e9994cb617cbf788faf4c60efe7b4d97f9a88635d99b1e9874ae4017eaaf6bb0e0a508a7012a4aaff74f55fb306af3c06ba731f53f5e3496c40810f93afd0a700d9635c74afdfcf71d3256ecedcfb42752d2409a43d8c2fb1462c0f920e94edc3e3fe99994a5f81cdcba5cd20216d6898e76de5d92d708a8756bf0e93737aa75140672b92cb03cd32b276101f3f11fbf4a99f643ffd111f7269e409d9d3410d720b7781d23815d4cff5567b6d4e46228465888386e462f0a00b4308f55cc7c7ce54342b6cbd055d91d1a8f4e9439d34365feb193873ac8dc89154083081c38859913fb9061b781a877c7edf186ea6b0f2d2e52d8580e908b39c8b0621fe0ddd6977d109f9e7358485f9f9a52c3809611c646e518ad1bb8ac253db029fef063d2da49d4c2fdefc611fd41c286e3adebc3c10a6638e2d1417015dae0257dedc7b75644fef8954191eed5fcb84b2609cdd906d0e819a3fc1ce5041d20e360435c0f0f4030b2928eb9cb1bc466e9b180c2c5c496b6c738df0d3060bb10ed00cf5a74fd092bf8eaf5405f8b6dab8e769247a172293b4c031c062506bba0e3aa9ba72e1711c53ffb12e91ee280e357ee4d8f3cf7cbe521b804c7024903b0dd84f7a2c7b35c4d40ccbf07265b043fcbe60c5575af5febe457d1b03e0317d004c6b19add44caadf00f38c32f4fafee7e79ce7e1d88ac7df1ca9222e0975d70d36b8e303755f8281563cd489395db91ff6e79ad496088aed0f69e3a75a24350e2880261e04996a0ae38048ab94f55e7cfd2b8b626f0e974064603f9de1cfa10bb16e4d30c7ebed0467cc763f9a11bc5c365b95e32afe92ee602f1bd087ec8f0779cdd310ea136433fdaaef52c8b1790aab02901cfdb877f23aa9d550c031bf0ccefa6ae93f5a7f5fa29874fa7db6dd9cf7491021fe0d0d44f7d2ef908af2fa0b017f313b514f949751a675513883c675c3906750901ae6b6c8b606519b33b603425999bb393e414d361f2eca1f78ccaeb595a90ad0f54736dd52f33428eba701cde108345e261414b0894a2dde8e608749dad05b53ac95a9971e9b1558d365054ec6b2c2bc3c0f19e626d7b28a1827972565c4a27528ffa288a5ea2475115e0b42cfce09fce1c3f9c3af277a5178e1ec17056ef7503d61d3eb6eb8a7ab30430d51569d165736fcdb0e2e132ea336e01e4cf4c601ea6e8e87dc3fe2317c4611052208c9db676af426791d8a7cde51314ee458551b02f44e54960d55e4def9f406eb755aac9bbde7fdd13d8c697149247150f8f6601c27b4f44653662a102b9201edeae8fdc3f7db4ac6e486b7647a496f1f3733163af928202e87fcc2bb6352036b47ad21fe16846877e873f082dedeb92698a418db3ba6f66468d9f7a9d9420266661dae496379488b67bff119c0bf8bf76c3e1c11b3e5ac4e28b77ed9f99c0f79232081f2e49cf483d098e55b4c4c0063e44f16ff7ee79ca87e0791f9966e057dc60dac78e8033b4c5112f7d1fc487663394d60725f485885961a5979a3ac0122204b36d47c0721e1e0f02cfa75c9dccb1d336fa120eeb10b4d4f4f68e5730e3f8bed25f378742697bc7b88a50469414d42973f02f49d16a2d83a7b9c998c0768ae277e57ec5cb2f9f8be5e9247bfc66be5e8187902d0d96571e2980ccf3c0768e52ad84d08fdbb6c8f48df91fb5d7a3cd7434f260a2a3c8d9b43c9a77ff909ce44c4edc26b5cc82ae22bca3e4231044a22b31e396b803fb2a03e9e3de05d0c75384c67725a0d962540169f348309d00a501d6d05f9f1e44acfebe40e21240087813f2649e03707797db2871da837d1d5f187179e8fcdb7bb1d7618dffb2605bfade539bfcc9c06c8167cddaca619fc400f0412b4db4ccf34cc51ccdb3d1505e0b96631a802f937e3aa2507afb006e5dd996880279e6c9b32a6d27454394b06ba8a4050e39371697b019a2dba3c494c579be7879071552ebd6f19a947663b05cd40ce1eeafb7c8d0a374735c4aaa6611c3abe4cd648219416eb2df625b4950adaf0b3891a8286dc9e732fd88da59f3a4510777da9bb8021c2e947520a716c05dc033297b8b210cbfa750e12489a3ec71bcf3a77aec537825a591d1c40a6e30bc471ccd02ce021a7b03fb5394d4dcc437ad55e6c88c9b7a441cb085da3583c058beb975d8b5544ac8f3c24a73010b3918cf3359425773f99e787c1060a0f3e099750919f5c779fa46882b9a2fc60bda1713ef87be883986e44e6e3f7efd8e60aa97970164451054f3a8c7b09100cd62d0c28de11d28a012866dc81beddc2e30a1a98c6eacfb7a6629e2027649268de17236194551e249b6a5864be33fde6ad068a832b22ce00ca7659a6e4497fead24f915eecdd73358dc87b2d10049afe46082289a6d84121a1e9f91048dd0c54949d9762e38775e71faaa3c1b433859266062b633f727cf2578d255f1e593650db51b64d4fa94aa63744978a6330290fd0025f2929ed346288aa5f405b3160ab1381ec6b16d1c7137187e9c603e14c89140bfb6402e8e72e90aeaa3f04a13d9109d81b19bc171a08b95658d2e4f83e9bab0c3cf7871d77ff40b8b5062b1b7b3742489fab3f4f8b14ef0f8372232e5a559c0d393293d9d70518b6b2cd4c9d969876c239ad55c349ec8976e9c03fd21c42c90f7663a834846650d7d7b143706bf8a03c247800806b5ebecbe81927f6459bdd0a50effd74f5d581692b6aff20a94e7046dab342a266a5c9259d8100cf9b4446046c6b4e747a1e2fdacd9e454f6059a35b4bc8ded4912f0001afc3e0be9882b702099343a596c45317c8944fee535abba364fd13910bda3a07781ee2f671ee0a01b531e3cd32413787e48785c887bc7ccc8aa85c18af5d4cbc8629dd3eb97806098789c054a723e5a0b7106d0a7ecc0755608c74f2423a701984009bbd30f625076beb2dc8b99e3d60701191992e125e635c0153403ef6437d6cafdd84070a610bc1ad231726f7aa0e608914509da1cd68f20f71d2cbb3d13bcb502dfeaa65fd081a837d233a2fb9809e8971baaa1466f189be405df4f402e26d0f28974547290857a6cc7879193e54f8ac2dbc42f3acb16e24f3b6fb8eae215a439b1bdf6e240723a1dcd6a8a1f088979fe7076481c6a3a54c1841c67abb4873613858a09f1a0e8129523d6cb0a1d2aef5a7f3b090390a12b4796ed1ceda834f9c4997750a3d00eba2f76a66ba699d20560ff27f855edb77152c8551c5bb90bac6461105801703687107edee576299ab5e468f230cd84309b5db5ca0818911d91c7fe988272a08c52ac5a291e45e6890a52849fdc5ef90dbc3d93b3c0ef67305712a56af8c2203e337eb974bba80feb7706799e77cb5e3738c9c424fc820277055274e24f5c4016f165ccdd3e46eca7e328164369bdc95c68600adaefe90cba45ef24465740508a2f8a712541d6fd3631d1ef1c96223d5d74210836256ad28fb247d110b4270034c404bb4248c5b0279a8d0ef775bdda7118925f25bf1622bd952bbce96522b047091e52481d2857532c2d30e0224c27b15c9c8a9532695a54680bd387c7f650d4f7d0af3912fc3ad0abfc81e4a505d728b8d2d9b1013002cc9f99bf5a969ac07c92c62cda3271262c731461cdf2d9d5da07081c6ddb6f4fc4b19c43ad0127a032389eeade1076c02c9d10cb2cbfac86b81a005fc89441a58613c2016b25cb30694c775d142c6923094462751cbf5a74367d11eead92bd18b3fe39e0ea2d63c0f9b7108c4641d977daaf015558a577bc700a6260a62d1fb6e13909d829ad54d0b7904ab823fb89816d93dd8c5985da32a7d91154a1222259ec29f8882fb1e850c5f1ab0e571da4661ea79ccd08cf91a43c3cf6b4841298760e89c4e8bbcb12a00cb9eab296e40c90404c8180efd65d1fa65ada931f3d9670977d4a4533d0875032d9744af2e148a05737bca54ef678dcecc8777a42c0fb5e9a519717f05bacf0761595651ffa03809c719163add0365451bf4e12dc9466ea51f80e8029f61af0b5c57abf94b1366fa3d686abeaf57f7d8d97f817e0986a4c97a59d5150bca3108271177fc026a66d9bd5961a2ad61bce9d1800c41e6ec76e5080d9d2472e7210d828a23a7a784bec681829377af5fcf6d232bacc453b50b13eae91b7d8283c40197ec181c777c907e3dc5aaa61e581e037a66e6996266f2b14d48c108587ea007d3a118bd8e4fa7658f86d6a73468907d98c6c5c6b4148ad6aa1c082867df0b041b4ed7eb08ca6ba4b92f11b54c3c2a1d176c4d135d020338d125f659555ad706d50ca12df34eee61b990997a26fdf2df2f003226694097c431dd0f1e12f2110c1bcfa523c55dcf3ccc9b6d93bc6e1a99ab46d1bc57e239a11ed3f20f3ee72407beaee8aa6e515dc08268e0a8354ab4606269ff6435f6720ddbbcadee5720ee01fe0e6299167fd1b6da222b59ff7f375efa176f3de30e736b703e0975c191e604b8bca01e671dad21b36271e63e8c7203fcb83b61069c82190728537df4f8570bf49c88ea1e4608925a207fc998700851a1ad3b50909c5cb1c5ccd2988ef6f7044e9e9c8713df6823ab7607ede709000305930dab1f297793f54ac2ab4b8397003b48e6ee50adf19e27d928d63c0792fa2362e3e278115bdc0a528676af00820a6d39e813ed3898c4b0c52fcd5bd91a0ad9b3606cdacf9635080348fa074b6d0dd9d1cc3fe3b0c4d606739d490986fa3b20e6e17ebc208f3a2136de78b9bec308b74148b7ddaf59d650ff0906fcea41d7c04ccf60834612867a24bc4b7c3013036d3d501b933c9aa8bfc4fd92db6ea8982d10011c7b48cf8d463466264ff2c904cd04cf8a2a684eb6358636883cb47b7a17e5202bfdae039f29fc5157cbebcc0d24d47c1d64213fa64a17d69b25dfeb926a7a30a5ba4a69c3dc0d86226cd4e400cf06fb56af9342d892d9c60c395b7e2359fe40a16006af791fc8d8f1d4ae7205d63f6e5f2589c30bf87a12d86ce777428eb0be137ef96b46df6a545726983904a78a62aa9a53e10f685fbd8a72c77dcb99b5db596adfeafb86a66d2ab48cb908ac2419dd4bdb9743a56effda54f9d202763cd4bccbf974600ba9774b410c0c01e60f890b8d5cd85fbd72deb9dce592dd0f629ee6d47e22ebfd65188a59147e0a5138ae8abf6962dc5a617e9a8ff173f51098ba5052d2416accf7625d5d94df029c38119eeb6f19efa18debcfc986ee9b8d4e01d5a6d68d953594f3bfc4cdf30bfdb5964d7265a52b3937fa49d6b6d971b6fa93389c0ca677180211a0301c190272b833b16d18bd9ea3bfcc675a65170ae6fb487bc1be44f2547a0422867bb20e295d27df2e8658b30de1ce16d8949ddd3b27d04251411fca62e2b52230d3e408444729a1ba2e421c4145865d8b99f9b873694453d629233cea095c4c2e1f100d17ed670f8a87a66480ed3ec9f32b50b95b38cc24bc3b297882831a0a70996f0160b7b0750e6ce880ddf12d50738651c02d08b5fae1e78ea4618d39ec4ec97f06be1d7eeffcb6d87e9a219dde2e3a9f5ea4811ef5e02baf4cc69f43dddfc2240a4a51ff4671d4826d2a3f592d3795e0d49ccdd1e19b30e44343f635544d70e6095ab184356778cc98b32816756e0dcc4a3e9b0ad18cbef30aa93d27c242c601084a22d0c9d061c25350ef592e209b03a5f4edad466d45166d54a7fb02294d6a0fbf70279b0149927673d629cff01f95669d7c66a3e52b39b62433f2f81fcad00045fd498037b5fc8da56ded0216a66d8cbfcddf2adf0baa6ad45913fb8c6b0b009ec2ebdb6bbdbc704e634288ed6f644e9eedd23c9afc74fe20037a27016fdb05047777eb66e0c0950ddf98e26ae8e51051934e00070eb3bed3622781dd2023050d00051ac182fc638afadc3cd95edb75c80c38680d43307b7288461ab76f3909f9d8fe414fb7fb68fa84612fa4c266962469dbf8745f76bc0d2e0ba83b60b00ff78c9ef8be32f69036bfb0849ef9f32d742b3693d102905b43df77bbe5ab070e52df078746535674b4ee2589f79e819f85dda9dafa3b4101beeda425c283870a95767577553260ee68cdbfe364e63c0b3372847e00fb5bf3c817dc3ea7182009bbcfbbf9930323993a38df5030a9f258c7f41ab5b803eca4f1245c3ae5aa99038cfc0cb289f1b73b4ea0edc6b52a4acb24fa2e1bfb5c401d27d8e0b70ee1b80dc9698285867cbf7b1d1ad5bd23b3a1d135070e2fe8198b9c3eef7c3e1badc50c1ce39ea8251ef0dc91e53724b4e84e0183ef753df684c4b460754dacfc8b160b8f34c1c4bb1de364353e8efcccdfb26d4340a1a5bc5a238e75571eb96e43030b8358de0d88327d6f112cd6ce0f640a95b564ea7999888466638b0bda438b580e950c9b8fa38899f091783f5c0e686a1d306b0c764a78cc7c0a679e744d813e0238b4c0562c66affcd5fe659c27c8ca29f0c5759ae6115069575e924ce7843d02df8412079582e12e2be58019fc6f22456b6367c9c9216eca68d7bc392f08170fd0f67f9a142e98ef570dfbf4a7abf9d01ec40dcb7fe1594b4edcaabf04e27b08b239643e5d28923166119665407586cd44926bca5671710be5cf7bfc6173c30ae8a8d5ff90f8e78fe98fb2003a04a43a7d3ea311e808546edb2bd978ee349f0bf1cae6bdd5989a93a55878ff144836a56dc7325834b1f06f0d74a85ff138670770e0b67a92942ac86936dd20b1dfac9ad4dc1b2c5dc459805f6afd2b973d160ab0ecf481faf1bf274a6978f9dcb1acf32061005f8d38329ff1ccd828c107c50674c77fd7f4c784a81c6a6b4b31e22e7be4148bbfded3cfd478f8706acfbf8f0428b6b0c3c6645380a8e6044bba8371c18d895572e17b204d8e54a6e687f9380236eac290d84acfd089ae8fa82c995b292950aea1c188a0fa35223e38d73085091a8c8862a6936b34a3723744e1da6f2fa41dcd912b14061b8de4b19e5b032309d0e99c5462575b3c7343b6a4aebac0ac2072ef31b9ea8bd47fc089e862b04e007fabb3f0cb4e6e1217fec843ab8f4507666aff47e882d4d367f14c17d0d2e90c7ed7048186db17dab048e1d9bbb9efc950382828ad6a5417f19f615aa500b101baee484c5c2ae6db821dd8954a31a9374093a8d61f93b5de70dd8a78d747080f9e3f9c55e5c094bbfeb3bb49ebeacfd00c9961801fee73ded65e132a60e8fd033a74f19de4ba291127b50303982034bda3ad174c95c72a019e6b4c6ad439bd071b4bf2b16c4e24cfa7b2f696815338b733c6b441390c0fa621f742d90e6cb90457ad1bdb020bb07a019992a750750dd35c9c14342ea53139eaffbcbe83dd840f01135546a59793bbf054327e3ef6849fb60386542f306be1d92ce0374387cc0155a0ecab065a16b7566ef9809009c4e02eab1bb11e65d56efc370d5707ebd301719bc91212305a3c1e15262fcab95565b2851a42a8efb02491fc0ae62f933a0890878f126acfbdc21181a05538446a8512bc94272ac18c21aa152ff0e1105f0ea96806de8ba2bc3bfd2e8e398c0d4b559f34805fda6e6d64cd9f35c5064c5e0872edb30bda7f405d4ce2c823e70656089e011783d77f1e95231185eeaeff210b1df82843f2b4a2b0535c38f415ec0e93cf9785c4e1ab9cb25ba793a2811d460ee6de6289ad56de3f8bb9cbe628ef301e1acbf394d1373461cc8318049ce86607f6ba953dadd15fb6dea6aabbfb119687ce8f5cd36f905d6d9059535efe8d7502c9384a0f5e4ef7645b36a5e516187b20192cbd1c6d3278142c2e829a9980a105e31324a47998c1d0c6885f2e7edbe71913ba6dd8b79e651566f402a2ecb6070b12b2c7e93b62384efd51a792edf02b63f0096994754e846c07a6dd804023680285f9654c0fe2f3cda3a9e52b8e9474a00b8e15f959cd151da93d1f11c40c540b918b68a189e5a4a102fce3731e12ec0dce5184d3544ea659ea5211caed66430616a965962cfeb1c38e31c3948809b1fd8c77082165afc8cc6da51893a3d4b901aac0afa2471ab5576f183514902f39d6779a9a54e3ba5816bef557ca0c35dd0273ba3797375490799ecab1f196edec4f6165e998f83d8e97362559fc8cb5bc0b230ce0876b73f8b82a2d07e09a3ee8987f66516739c78b5335c163c55d257f0c5cf569d28acd154de447d970b9e3320b7022ffb2c8fe88460d9f81a03f62260e27c916bf72b86d8f07185e3f72216557c616d5251c7a8f4cfe45c349fceff305438c8e334ead6e2c5794529edbb9087b4a3ae57f68fd01d979da3d065e704c0e6a2780bbcf62f286bea2d8f78c7a63b967b629d00a16a5731cfd5cf1339f6702c61561248f1e4651d6914eb5db9228f1cff0f04c9a173e910ddf0607b3a48603fca6e770f42a008d5a4d239957332f887ca4db45e082861aecf991bfc9bf060b11e036d9caa2ae07af619eb1f5ba082f0e20ec6f24279ce752ec7a6d75e6b301d21affd3b23ce9082fb68ae24ce9a04614c072371025c820b4014d530267dd062baa78a8e6e8987357d9197fea662b9ea2e6b9f4bf986287d987d92e7315c00a827066c843697d7646186cd6e446efb1a1c36a259cad3f94a279ab044c12e50dd418b145fa43296d01baf33c3ed9ae9a402cea0ee2a4175937c8800496375c09bbe92756b2f40c0dfee7ea8cd0a318c209a76b00c0ae42b0787be71fb87e5800efa4ab8f1dd9850e7873de963a5ac14ef40348909255dcd3c90b18053f617a0b50f90298a404163a9c3bc1c041de2e3caecde9bcf4f75faa854a543e0dab060bfd1127c40f03b73526dc16f68522ebcb9fcac9bc1ee66d80cf95e5cc43b4730db44fefce108ca64be56ba53cff6077cb222703d9f28e4a7cab64a8097eb4ab08776b22eeb9e4b724cf731096c1961dacd1b42a0c966343c9805df4c9248dfa036605945617b3933d10997f1756bae444bfb8abf4a29778ef446198455ad5360001a41e33056d7c984da009f066acfa67c1dac27e4c0d847397e853bb869d2c09baf820e0fd26335cd9ff6435dea48cd89bbda4eab984ccd8aeed2fc42ce9a9096b13f572d951c83a2988f97d6e702a81b789f566a4c8675ecc4fc9cd7906280d1f474dcd6b1a12a1ec7c5e09202e17453952518393443a60a3f500a29fad820237729e5a805314c114580684b4cf8b80fa2effbe48c43bbdb4d07d831a2412050acf1762ccf5e96d6a8db03dc05b21e3adcd498d0cd7bdbfb196e5ebf33ea6074890ae4330cfb01beec7751cec929ca36c53b46b92bdfff1a9c26907c9bde9089fd18dfd62c528b04ca7637e3c291a0e24ca9565c90ca6cdfce6a241ae68b709cfa503146b532d8b4c44c906d56df944624cd79f8880bdac10102d8d63b05d09dd363ae9fec9282cb42ad80e1fcb364f7582ae97654f232b17345b723880a904 +generate_ring_signature 869c7247a0b0a1fb90d96cfbb2e3d17682be83f64ea5718718768a7ff74bdd41 cd4c787f13645ac936e8e981b3c725d1bf042959eab770f7c28179f6f37f959a 50 64a0db89add71f4dacf4b369f61a58504533ede4d3740cecca23b82f3a75f4ef b37094f708fb5ffce04e866a09c3aa4ae4e9693abdde06a7b22dff21330d2865 e80522d9eaf7c40d7ebd82c81d893dae50ccc030c67eae697aa0f7df696b87d3 603bf0a35d784c9788ce83017240d87603463c6c41f61896f24ff3a54e5183bd 2879ca5cbcd35d10079dd6b0875cec28e8f177fd12a7e89a597d4041c60b28e6 8f16a24090b39a5295926e9f7e1e73b85fd8f12f96e58862d5a70b5b8067fa74 af4aed48e14d50dd553871225b4cb21dc488041ec203ae1f4140c3a46595ba69 91619b1d38238a1d1ce66c10b3c474893e0484a3af833c55a346d08df4520b2b 4fcd543fdd76e2a7d5a31e6d0d39ec96e4d70b14ecf3419d8f59f8a87e31a26d 84fb3a395fc0fe8ac6bb8fa697cb62d3d56cbfe568c24ed6f6c973cc674e63e6 2c9215c4af4e6ba4447a7ff5c92670eaca21bcc140aae3ad637a93ffa4e0c707 5384ef060ba220e21f947772eb03716fb79aee73e70298e74003d6150f4fe609 ef2500d30e8ece7ae0a2ca2026d4f176ace683edef6326a16f631e42029de6c2 4702a38b0795b6c71a0b8934c2b43b0420c29436fcdeb1ff04c5d44470e900a7 bcf1c1b26acbb158b8d4444fc9951fb62388c9e2e37cfaba70a676a0166bdb04 32d56ab2539721810ada48acaf38e0d28a2dd6e4eedc2dd9c82cac01786111e5 9c3321f4e62d07e104398e1e405823e9e1ed36032ffb78ead40900ec52779db9 8b732bf86cb9368873cbc81c0390eec33c88cefdc66d4063c54b09ba3aa2e78d 2dc91d83ec7a70f8fa663e49c70102671b43eff575c4ec9dd2ebf3abd7c03d61 f982af7048136c5075ba09d0d82da852eb1ff9879a49967cffdb181dbe08d576 a603ae7cd1acd05ddae21bb0803fd7d4af1a4fd56f995abc3343b3d86827093b 296b597866b55b7eea58f80d58ddaa00b7be7058e07b24563c4732a3de2c23d1 1a0a0ebf748500c4c58716300a78bc636d8616b3c6d4cd9d27224ffc6ffd21a6 2a2ccb830271f5438d1a91b5255ba79c629261eda2dc9237d63963c2650e7038 5644c2851b08f83e350bd96db37015bafa8100cdfaafe8d9576b7e7b5d52f76e a69c652a4d177a681277d5c1b4ad6b9ef22737b1d49789380dc890069aaa3046 3e7facbdea7a85a4b59ff60f20d441179fe72d3e07719597b11f544341fbc223 55358eef614502ef018661f3092b076e66612215a378f67e060fdd3a625bd41a 92db53a847ef3a1e0786d07ec6dff53daf37c2358d1b76174406a75bece6a192 6c248b764ac844f7f20317da6ee0ade57c3addf491a6309e19954df847fe0b57 c2b3c177d65920dc7e16352f4d4a1cfa82da74c45c502ffec6148240b048da0a eae88428fa3f81f6a565c6f71a34a1a282550b543d7af367ae568cde957a5adb 347737d568850ab93cdaba356e2f2c932df6a8b41fdec8b05b9104950766e803 40473c24f38722a5db64db65f713d670ca27db39726f32a728eb93774a89ad9d afb366a426ce9f1906a9d3673aba98e048aac4e4e668dbcb17054378cd42ab44 e9f3ab459f7194bc370c362fd7d8a6a75ca199b9ab4eecab528939d27dedc48a 0d4b4bca9ded630b3612d80d468b504d0c180d0cb434c46ca42c68ee98dc2cbc e1a6e0b00675b3b136853539a4e7f82877bee50b7ecc3fa26b8bef50ea81e5f9 7ffb6985097f8b43c0b1d8e645e7a7a92c44f72d6fea1ac90e429ad8cd6bc389 9cc6611070bee3ad7b9ee4cb7019a55e89981fb33689d4ff7a876d543d0e9112 e7bc5ed886bf0e3d7f6f5c226f8fcabf9d1935e7e2d609932ba263f451cd9d27 1c65db720c1282e65a0e49d0b06ef80fb5bdef56c74df1989eb020a3568bd4ab 7cc29b80758c5528e9534bf02225fe899ce2d91bcda453e046f275cbe0f1f9ae 51094251b02255d1ee35ef29afce78bcd0082a50dc86a164aababac34c46bf55 ff25ef58937eac01fffb1d4c73935ae9852a5612d26c13174f7199bf48a6c25f 69c882e768b9f5ab02710125377cd5f2e137d22b25ab1e8e3ea7421e262391b4 c4fda0318a3ddcfe2017ebf0bd023beac85fe29e49ea175b75846fe20a97d461 49eee66a75a60155e24d1337eac7f8110bcd122a9a25309383135d43b37a80ee df2a2fd55bdf1941f0a7d40293da98f050c253827d420f6e020d11e81613a0a3 d56f97969fd7002aa6a3b9448bcb83354ddc3cf148c4e9a5e36c050fbaa7df42 b61e797b24014d16d9cab92b84a7a057a1a05054a1f1de374cd61c040fcc7c00 2 4183e534383995cfdba1e890eecb79fb62785c085c966b0a95226f526cb10a046e693fb62cd3a2c16acd4692ce06ab668a178f0af8f4f126878fc69863a89a094214f094d6c89d48f7b743bc9766706c7457e64e53b1e1020bb79e9b0c691406f2f62e0671f61ba2e18f2aabae91e692170b953ae2e177e73c638bcd726ade019909d9f3e028b07f3a040f43647803c0a01f1cb3b2544edadb5180e8c98c9709eb21e4257463721a270d0cb34da4e4d99a441700c93bcbc70d84c505cc7c9e0c85a172bf87bb0f6b9a9b9cf3d861577fe87570e459710dd0ccec0bbb0adc040f9bdf17fca8217cee35bdad24e41e4619ac237010f54c60686b252c2e4afab30ce89281b0f28e1afadb7af51fc2b61d51adf7432a6acca555c3575558e6f413032488720c172e0865a0eaa3cf8a11b7bf714d76c9eb068f4edfa3270df2139500281e570a4ddbf51d5e487bbb9962c1f6a8d552c4df717ee1ae3b3e4ceb05f00afe1eec5bcee035b1ff64b5e5508c162ce396daa938a952a29cbbc2968dba390ca0f5f97c23a2c9fc2d22ad49d1cf470d3426c5bdc5531b503c11576a9ac52c0cbdba208e30e2d2727a7fe893aa6b1df6e327c64123dac1a08ceeeaf860f04a064769c973e446e0b50b55693c0128ce7d42e3963115070cccb077a2eda5cc5e035ecc8bb77be23e6e15bbc040d6d2275af2e1a419cf6b3175aa0d914e0fdb6201d8e5642a492887bfb713b5a8c75d182ef883020b3ba0936a445685b46cbb74089c605f786e8226fd38f3c92ff91a55322639584ea423df71bb84d7aa87181e02424f4fefcdb1a546cb6bc78c9979e546fcc43bf2468a85a60e69603ac7aea30265b5abcaa22f9cf79e01f6f15cea4ea9fdc9e3c450a5d4f16d8f907325ac680ef926a619d8c4cc94e847305c1b20ab3069fafe02c798e0a79319a84080ba390cbb4ab06c0bdfeb70f02beef2bd04be87eded76532aee885a4e24b23dba353601a5dcc790aee025bc3b4cb5f1072027c58f2f639cdfe2d1b8a6b58b7084d22c033320199435aed1e54ecd0f57f639e0216b93c8582a6754a00f7a89ce4f688f0e497b954a725b3a62b7561649e53e73671d61cb4e0318a905f08b4ffe002a5e02307015b2c3fa9e6da1625620a925d2ff3727296172466e9a345f8bd2122c82098be157bb4cd58e9edf0fde10e652ab93d05980762ccf6a3c7a4d6745a6dfe80f3f4d3f2a610ce844cea7887a7b14d9a105099b7e39140b90c17b3990c9401c094aa5c6e1e8c107dd1af7e2958e2715494265d81ca7e362cd9c6bd61ea538ad0c4ffa4d8d38245533454c9180d0eb9e34fd68f1c877540ba5b6c7d8fdc4cb2c02f5faef49567f8cfba896f7bcde64d7e90bdb27c1fb88b114f3ba0794ef40120686b79edceda4109f272c54b6f36bbdbd496ea8f32aea7c86f9a78113423e970853571082a4229478d23fab17558550efa02a0da8793d1d9f9a06c77ffdcf77091afd82034b8e46db301b248545cba23feb78ef3591853ed78b657c0e714c9c079d65ee2124bfe03f2230d2828d659a6d0424e1cff8d5780f8a3a3a7f59a41c03b493d0a6d22820f98b2c58d1ecee371b5d123dd3247ade42645162c9ba9ed109bf8db464ce6cfe11d9a8234ac3b34c97b16bc2325d0430f5cdab7e5a7d6a760385e186dd93e84e57b2ca645a2eef108d259e2f273f273632ac20e0152e04140b0f3ed6e69d6af352f1a06b49a83002256dcf99ea88a4a1ef175ccd96ce93820177fa68184d3e9cbda09d8b7a9027b0776460b61492951941715588c29bd01101fddc9a4e183b4fc33af3d2d43d33927987889f6bd545d3ddda6fda16262fa30a6c2649fa2cc30df112a0476fbfcc909c272989001b17fe1c184eabfa355a1e06e558656cbb731dabf8d483fb9c49ea74e49555d25a910c1fbddc301cf3c4ef09e1d7941fb5f2a5276040aea5e4ae54361101de06e3139e94e24eca95640cbd0dbfb8e289d335b3d1daad70b96710019d98aa7f74b61f7adc99993f20abd949093b03bc3ef2987b73425df926586b3e83a7230371653d60a9a5f7582bd6c0380aacf28742e3909997d16b7b528f25fa475c188aae67e51b152c2e245744578806c359ab4773595a8a2230b9979cc37267322892233db91a4b3b7c781a179c93004957ad257a75f44ee0988b6e787750abe90c3095be44aa35ece10cedf5472c0df33dabdbfe526a133d0b07798898babbcd3f09a3efed5c91f901cfb8adeb0b0274836bbaf074d7caa22163b722a1e65c8cd301b0e69a8eb232cc7107fa6da60e2f75f65b87979894bd9cf3a4461970a7afe77f105c120abdabdea1889fb8820d5b4cc2bb8011f12123f89ed074a19f89bd7e19e5730ca1d95ed5f4b1ff38b80eaffa55c2a2c05b76745a3cb58db2f0d6f0db013b4c457228786953e5a13bbe048d954b4728e6bdba9f8fab277e1a008b676cb36ccea12e45739c3e00ed031a02e8cff03b7ea8beb9035b98d2459a250b69fdc9785635b42d1db42b81231d820ea5eb39aa9c4767f6b43b7269597c6462c193760333d821da73e6935327fbac084528e307cda0bda9947fcd732ef3c54246e3ee3b394ab06608b87626bc97e30ed23632a6ced000af0c795a1c9b5722417f17ccfb58bd80b3c1a854a9326668099e433639b9b28552da5b1b832afa54c83859971db2887d94d33201e31b694509f7f4d23d772dec780b7148a28d958ac2b4afb248434b9ec5792ad0b04045130c473e8bf72eece12bfab252fb6187f88999472581c8d7ee5730faf7c161492005ac2345fbf684e1e755e88b23b8fe3d0dda4bed52da6837cdf637b2afb8e78105dfc2ce19c4021f908d5b5f42938f367dc6b7a8bc8781ef28dd9b80b0230e420fa925e61db3ab214b0358012d1ae09d7b8ecfd4c9c5b66f5533c4e657c7f5370ac6016a6c9ff46c9f1d29be4d14b77813f3764da1360f7a85ecbebdb6f2607f0524a000f731aaa124ab2bc9775582d91a8ec11b227fca7876bb2233e4fb8a1603154272974ea41da40244cdd3108e182bb1175acc9e245ecf0120c9e335fcbf06990b8655896a4d047f0f63ccdb30a9b99d76a62560fdf7a7c8730380f67823096e0b1d698367eb78832f5230e6688755a549498e971b8c3b335f57c9d3122a08124849cd8acf22e5b6193eb778f29e013ef6f20ce309bcdc4cde42450739020b7bea62f7e877d2d962341c7c04a9cb371c8920d8bcaf3b5b1577f357d4eeb30550d2a7b035b5bd3134026d84f6792524b66ad775da003e06c018b9e6e0622002eb6c77aced104b6a71c9fd2fd5e6dcff4997deb79436bdbcf5f243c9fa613d061f9f1c57de7ff728244ee73744722b3b1f8eff492814f3369b08e1b51c51590afbd8ea37cf8a23a8c4419fc39b64e049dd740523bd6b7690b32d8bdce0bf9f07c75050c4b2c509cb3e4e84643394c978d600c7ce1985805bf8d40839de3588070cd1d0ab303c185889ef8904d2affaa1f1aa42b071b6d48add35e27876939e0100f999941e5b8b32a22208f7a09d753c03bb4d7e5023ddd422ab3848728bca0d864bbca616d7b434f3bc10d86e069074bd618754a5d42b931abc861a45ff7f093360324ef7a6764d7e4d41993d457873f40280327c990536adde132605d60e025f0fc8038c2c577a16b9f7868269a998f238abc1b1dcd86d91d15f9c6b0b63011ee69dd5191c43ebc5ea6b414226d1fff0fc98b5859fe31063c0fc7f15dd5605875a262a79986bd5371db23fd2f6fa7b2e033a28d8665e46fff39c1895ea8608cd0aa32ebe39e8b0eb8e96cf594419901c73de9b168756a3a49722d83c2d470e916b2aade568434d123bb37aa30211a9c972164efb900a152891c20768839f0d2afd2fabaeac299405a92d8941731d86f5aeb7149d140f17a6e950d92361ab0a1244341273ea65ded016704cff76d539a5560258ac11e612e3c2fb8dc2b23b0b2caf309e545b679cefa2200cb5ca63956b7c58e7e7ed79827fbac2cdb188d7030388cd2ccd1dfc41349bb5aa2356ec4c44adbf68bd29f06210717f6b6a854d0182278ccaccfe43b2aeb66d58ad9e4d9575f89b1929635cc7824c1210a377850c605c943293f9c15a890d384bf42f03ac233f2d92028b02900f8cafbdfb9d0a06a7f2784c0c2a3df0ed61e9c4c48d570bec77599f3fb3c4cd60d27c0b41f8b10e087e8c4a9119c724a918ebc5638be8c94a0d88151434af46320031bae18862018b5c0a5cea422ecccfa854d0c9ae4fa41ddc2cf5d1ee646c7ff652de27acd703ca5b9afbcbe46a9f046a3b4711b8dbd272b46b5b2e9860604a8a4ec7175caa0b9e791b3e35c72de0ee3aae342eb420ccf1e67a925ac619ba5a91f9837ac82d06ea9f783db129eee6e93d51f679dbd6e90c39510b6e390c4b764c218df059330dec562368f8e82c244fa6c727ecaf42cbbbb26e3e677af63c80622ed55352710b538273db07c6fb56c99f429c7a1af2913a4ffdb2f52db11f8aa6878c6c5b2b0b +generate_ring_signature 1d0b119ca5e69bfccdec2771ff272dc2c8638c187733438939525ccb8005f4ff 8645d7285139bdfe550241936b2fc8a26757ffcaa7bac9084ff8a85cebbd1e89 3 8a075c4f74b5520b511ee1a487cb31c4cc93befa5af0887414efe0115fbac0cd 5407168cd81fe9cba6a34e7e30cbca17f448f2226131ea57acd74420675830fc 2ffe6b476ff37d8d6487cc1c7e5e89ba0295a5d0128e6ecf8ae64165d6d14d84 ffacc2cc992ccbdc7cb0cc1a695ca31a92f6d6a2563b03349688a63a86f8ed01 0 d56755bd0679b1be0919c5e0e278196520f2bce1726837f3f0827eb65670f501eb7af0ca3a5c41cb0a1e845518a63ba7dd071ee35c2b1e82cf8756a91218a4024603069ca1eaaec9792b0e1bdb87d12b8e5136b08143072c6eaf12a0120181085ea01c5864525b04f2face202dd1dc4f4cdeeeaae228a37e6e93dd62fcc8e507ff8361c115df826824707075c8ce3863a5ed56bca381b16a29d3ef839d4d8c0588a634fb9f19698bde67c2f85960b546196b2d503f941561af764750f2ccbd0f +generate_ring_signature dc0159dbf72ca06954ef706baaf14f7c84f6d6e96aa47cb0fc100a73f21506ca b0dce2128797d8cd8ea9c4dc13be5ffa76e3c68278d8721835f7bea75d42621e 155 55bdfd958e609cf23a67c353b313cbbfef67c4b9f888aa9ff96473fc9c841e61 5cc2a7c274ee46db7d80a45e09bcb4701ca0b6c651a3f71698df8681f565da1b e1ec3ba3b9a5c1f93867401ad6f4b8f45a81fc04b057bf48198f93507db0c45e d26059d3c9f334e298bc08d99e64060f1c68e8cb0db08904f3f2e989c8eb0da2 06ff2aa52649f6205f044d65d691168f3bf22da79ae46a294a362c23fd674cad 376f687a817012087ac04320874b9e169806b3a384ecbab35aa7caabe11af3af e6f0454df292cffbefc002e20637b78ae2c52993e2f1d7801fefe9894babaf97 965c5c80ee12cdc2b0ef724fea7b86aba5ffa1ef7a8d371f634177fe0c77ed69 5cf4164fd1d89da37159323130a12b0dc4d22b6743f7a14f37d85675cb7711df f43d9857a3c0ced069e7e66e99416b0f4969010ae1ec4f90568fd9bf1ab42d27 15a59c1dd5958f6b8b48471d7fd113b4a761808409dd5606b5eedbb3fce5749d 4e8c6e51b88c5df9d2f41a66e80a5b443be79333309de74e36d26867c5260f29 84d85995ab7fba0a34a50d10c6c9ba52922b7f5f1541e7740dfa7fceba592b3b 04409b6c2ee649e8836796a2d0413dae06f8f41cc352fb00bfb108a6f608ca32 9c207be7f2a862125bc38aba0e04e98d16d0d2ff2d36461c0480716a95ec0a36 39fce2d41d67bf1f182d1ccf23e140ceaba4322266b10ca3c950055b6be5b023 fef4c2cf18221ce0ff9810a1928d9168e1b88db9f42f668d74c2876286c848e3 d1ebeccee551e9c25aaf9c6f1f6334fb5471e5e94f46bbb1d80729919c5691e6 9e7ab6f141bd4ecaa6d7885514127168338ff935f1ffc56eaad972e19b807aff 4aa10541e1a9124fcac6896b065066ed0d73150c9e9455f914e4d96ec07adf5e 32ca3c20ab39e82821420d6e2e37cb9a72f19d1df5908fa1fedb306b16c3273e 8172aa6cda51efb16683412242fc3b90a3a37c9381e4109c1f00521546fced9d 2f9f8b8f71b8e1d5cb244c0f0a14caea1c5d77b13ceb6a2ebf2b600ca2d236e6 719b3d1256ab1d9f286f9cb4bac235da7131be9dd47388b91c6eac952c53312a 14a64d08f79f8b03c321fdc291fc633fbeac766125cd5bb1293028bb7e86e4cc cdb1c8bbcea2d12159ca80158b1d8944d839d82f94e1c0c3ec84c71d00a67659 aface084d21571606d8b913198308fc78bbfe4fef087b2cfdceb8c335d756d43 66502a8fcec2e95a45c10fd5288ecd64e1f880a6b7757a3921e14f432ece527a 63b5a9153f88dc28a9dd325a81edb0bb2fd73fa2ad2aca31839f9350412b357d 7c377465d285f2f6d31834ff61cb8a9cd5ab2fe99b4ff9c330e0d59d305b21a1 ced17c63ce3dba87598c1adc062f3a8af81d4937447ff8991f7c780bfd9d9631 d33c03c444dad20c86164d634bcc2689be5724091a95c006723756544a5f88b3 42c6f935724c8f7f3f5c53645123165daf862cb05366a895eca0a7656cebe76c 4950dc9ec99c46c3d605d97f96407e9de344b0bfb7b19f976c9c84a18b3d26ab c813aa66c11bb66586ad3af0cadcfd7f4678f9ce71478135b8cfaf3f7fccff8a 91f5b936445454387f607698a4b2ca38191ae36dc6fd0ab0580ca4b80b058d8f 9b8741d58259a1f279a397da968d1df565d8e133ee7c69e1ab72448660625adb 4a7c1752977c764cd5ac091751bff8dcd41500f38b7c3baf458963f2133defc3 2d2b4a25e1f4a864c3f45e0bb5808484bb51d2e35b9180e3565dd2945c13a9ed aaa64e83a28293a0f8759ed4cac37082552d3935c827293da30bbb5d3403613d 66452a289e79a5361719e68d911741f20163ef4896f0257585bb9c832069c5c3 a14e0b54b0e02a7e81fa314190c273653926c342c819d74e9aa461300f238855 1c1322db318ccf855d7a5ce5a6aed6bface24d4086cc7b26ac67733be55570ca e10ddca61c7790d94984c7ed066543eb76623923557c610ec2f8de6f79f635b2 7588b14ea4f0e4a215d9a982eda345bdbc6aa4cfd04511867c40f9011556434d 409d5ba7c80a7e1c29df9bf923ce011ef47e284c0bc462533245be82213b4b7a d5e6a2f9f50e6e03b514ed898ac28f24fbec78eefbb3dbe0a6a00627f72aaf4a bf34dbc0ff7eb6c8a74f111324fd8eeb2be93a93c8975635ee06b526ff1b3ad2 ebf00d242b0667a638b7c93c397e45f92d1e8acf73bb2abf73b03490b3b6c3ae f3688388ec40d6680cdee04c6f11d79bcf9eb4e5ac2ae253b4ba852826b76bf9 0ee543f8b280ba4381bca59e1fcfff6dd425851c4119e251a7732c99b8d2f168 d872db69074557c2102d81103935a90bdea94ed0b0200c3ab0847b526e29b4f7 d4db1b4947a3e86d998ff66601af2f2da8cc84e9d724441b464143b171d7c89e 746e4cd0a3813757876862505807b4b78270c0a170b888a3bb9e3e60c0101908 8d3606dc0218adf7c809f52dc2c19c9aea02db66e95b21a4dffe48680bad98c7 721174c2d937c62637e492c02253ee2a7defa7d4a3b6d5bd47bc6f0addf40ef6 97bc500b936ed73732b36010e062fbdf149dc2a5cce48c1a648828156c956c28 f3422baed25571091c63261b7f854a389392cfee89a8aafe67b0986c77f933f9 3224b4758162f6a3ad94d76f096029d96b8542e883a50f48d189fc9ddd927efa 8634ba86f40b66d5e0ae98089bec9c49bf7ed348c857e9a8ae6af9fc1a1f1f66 afdc97548124eefa649ccc49f8c4895e1503a0b86e4732dfe073b18a14c6e0bc 5290911f8661f9a415535158f2effbe89a9178c4194c922b660645825e234584 a79798f044fe2f369b259d2b59c949a0cff4c4eb54b5464a1c07862032180273 36f35784685306db02646934f6ee0734226dd836ee407bc83ca232539af2a10b 1a689224e864e3777accd246ff0ec5ed4f78b44c7af8f551bc9b411f8614693e 37d040f52915b13ecff79dd88fb88f109f091a5716ec289f72adbcd9d1877221 3f8d7c20a19fc64d9ca3b1eff52f6c4c062d6ad85e9b242b3d16618cc5c359b7 8fb07bf7455db11b402bf9548947b6af97803a02577e538e9ad452fe51960335 e457e7a04242381202e5ec2f466205ab1cc3fe0fa20a97eed9cb1afa6f04664c b8b4761d93cb707c2e0fc386991643d770ce3309203f1b0c8f182ef797fcbd3a 4a5c2e5aec7e342cb8e6cf16f17678553cfd06e5359be5994ee6e50bf572fb22 5c16c16457e9fee9a62c4ba6b6dd7e46aa1f3f85d68f08629ec7a96b85118869 b7299ed0fb01db3149fcf30dd1d4ba8c99b9384bed66d70c3580e042a6f7833c e6c61646a287ad0a53d582ffb300a86755164342b83f3a6e5fddd5471068658b 21e7442030bf8d818a79e5674f1b5798a576fbb1ef84472add4689990f62e8a4 7755f1fcb777b9182eebd528bf3f60f6a2da148514e90695acf897196bd897ac 8f50370b4a8db13017296ae820c4b728f46e8244824e590293dfd9db7888ba5c 2fa322e48e87ea8a9ce4f70ac45fec4bad47ed617817a39c6ebb38bf007f3802 add067ff6442a2d93b12b6de049b5fa78541e45dd0a0f74c2223ad7c61a7198e 46554f1886d0ba1aa9e264a5f29b08c957b5c2896974664b222e60b6e47e1b0d 4e0a19f0b65791c9b0b510e49c670cc06bbb324d67f657a4028ed9f03784d8d8 386291c7799ecd8cdcef7fe30f6021f42ea2918b06be3fe8df2f47a0fcf2d616 2f4a5d75e0868c2b8e523ae394049ed0ca390bc64f641ba203b9b4dbc8d41d2f 841f17f2584641774eb003c751d0ccae29152dbbadbba8eb066a2ded5dabfe98 d96e7157019eb21d9ca9ddad08a2c2cfdd752fa10cf6aaf11089c180f6dfb13e 7360d3476419e86cb99abd0a33264327b0b5171557d812bdcf6800ea0bab7131 463083e7e2a8fd2d956876dec974816bde097827365b94a1494d5f7169f69686 44e8016a6c0e2e21241c67bcc8b844793daea164b5e2a14baea66c3c7a3cc773 22764f41e82557790205bc987553010dfc5bda9d8b9cab62def4c881e4ffabd9 9a52e8f5b5cf1bcde87297728a79c4154c272fab3a31505e9de06cc22db12b20 45ecf071a3f952a3886e388374de59aaca3109b8a9424b7cc4e9844de93f3c38 04bfda9f253526a91d0a4eaae8e8449bdfa9d200346509ad9fdcc90c9bf72046 baec1c08bd22e1d1803758f86de1dab9d472cefa662c4db5da8bdc3efbc19b34 efc658768831e5dd37bc425c2d958f14c088f72d86651f63099b6a59fc956aad 8bb4c7e070bdbdcf0bc3ccb85a7f636c40f6855886fb9e5afe3663c78ad39122 a4a4c96cd0361853a0f6e464a2ef681a05d30501c71191f74883a7071b38ca74 1f0eaf33404e3d3acd817f9f8a49200f6610b730b03dcad2c75829a92b67d8f2 693c7c1e884e24acb6ea9f79df9a4d82343d2f00a36617e80b2341f4a7061e9c 57e1c0ebdb580b1ef2eefe50d96d81d9182b45cec86b8c2bd8bc70d8094c3a64 10b2ab4153737474de7bc454ad5f247fd398761bb29f665cba97dd2a446f7397 e80e6ca216d809401268bea920b105ae5bdd440577b7a9ae2981b3d75b39bb55 967fcc98c8830d8374f52e19229f50d39ae130caf369227941c97f432d6a381f 6276cd137870acf4b11d90c2aef851894fd6aa8d8ef3a2e6a7464e0daed09c87 43a43751bf077cba57e404a62eff1082d66777f1e304f0251dc884a472d65730 199e70fda56e43e548af5bff218590ec49467db20d932ba7b93490df9bb79ed2 75621e025d68155e2501289bf8caba1ee9b89e6389fd2ee595e72baf5ea3b34d 6b7e8ef6c4575bac539bc5fb12d6428cec7286e03ffe1f00d4996ac4214051df 3a28bc22d5954f34ab59bca16162feecad41b3a25ed1008b21729917e9e4dc4a 8996cb1d13337dcffb5f57547eb7c6be1cc2af29d9bce46c4dac048159a15920 7b0eb9a962b2673a4c3385f17fdf9059f202421a36ec76a2190eec8ac1680d1e 6da2909d4ba18f512cbc36a2221a2e16d99a78c6e526e802fd9fbd38b8608ddd d989d9c710f84bd67771a252197af590b63b424ad1c89d623181ee4dbcc2acb2 5f03667eafa7c0eee3c741d4aaea93246d9085248002cf92b4fbf664da891e5a 4e885b308279d51ff6ba3294f150cfa79de993e4b4fe240e0383926eb1b72a7e 48310fea7a0b977e646e440ccfd050663c1567bbcf716c18738631be861bd928 2d8e36d9c5d5bf1a121d9b86dfd22dd40df765a8640383aa3a98e86815f6b202 c2ba1b76ef0a6ffad1f816d5a195b4290ea5bc5a195c21b99a2205a0fb0fe19f 9264af1aff612cf6c5423fba55d78357d35f1507961a1e5777cf9324496ce5e2 ce48bc8a5d073ebbb81bf4e4a31d30b43869d47aead425642fd590c2c6b71ad9 415ba1f571226cc741585b624b408f244b226a3ef68112a787b7f4ab117b7070 02690c0871b65ed57af53dfe9ad9af13fffa80e17d559e3ad8bcebec4e3244ae 28a00ccffd9aa89903398ac9b36e8bf24e77b25000ec0cc05435f70caec00f5f d71cf74bd0d2adb4721b4c2a2601109e25b41290716c2dbd20cd71416560042d e497022d82a79c271eda123b3f2cb12d7e050eec55c7ba3ca5c18f90456df49e 5593d828a1c1ad5fffaff2fc0e4b82c488a4a4fcdee2bee513fa7f2b8ca21402 ba7deb31a63aa2c63a9b6de0d018061bfd5bd1305de6cf9be496f2d0b38f0f1d 2fdf34d6c3f7b14cbf3d6dc057f68f9238a01843bd79f65c343f8888c57e032f 0ec1bebdae697a17fd38bd3c2b1d466c6a2cd62b221663dc14450fa9645f8f0d 64b8735f9e0d05b013bd5649e8df55455cf90d0a32e01a5bd47ce4bae2e2663e d3210f6fb94b6157defd835fbf6e24f335a53470ccc876c8f24b9cabc4bdc689 854d5cc84664a763d4ba94fab9f6826e4b6795b2c4c4644d1272fafcfb38dfe2 ef8341e4a2cffca78f4f85382c1bb11231f98e313e8b4b158f1f3858e0f1bd89 721bcfd1800fb89d20114f080b739a7f577466853064735708dca02bce6c08bc 2b21a17310ff3da504aba575ec91cbeabfe7cc7ddba637140f2b7ea4a087e5d2 c9002d13dbd6d60da440acd4e2fa2a96a51cb18380b2304c45f8d55d9e3a23c1 9c5743359406a6ff0228502eea24354ea96edfeb39d4149612a9f84320c3752b 4fc972f7883ce8ea63a0579fc59ba3626876688dc96b3b2774202f8c2d514671 8262e5c2fdc9e2d7add1a28b1ab3acdd49dcbbd4ad6b733de427fdca8bbdd9bd bcb2d7f1ab48ae12ac447d9006b14953c7b75a45eba9d7c28f3b596ae67d90d9 26cc343804e2dca0d14ab0d2a2d7d03b794576bf270c1e79b737191a78dc02b8 9acf167693db32fe00a5d4ec090129992b39c269833c6666c1946d8523bb054a 507659e6454ec03bc6228a469717eb91adc0c14f2dafa266c56522e037bd543f 6dc86ba6eb6846cf225842fdfcdbce3b702041a03dede7e5003cc402df63335b 1677fad719b74408ed9e1b6e2f9e5e6f0d5d54f850f8e4a4307c3fd2a5db6d0c b27e2f7563d41739dcc27a208e3c1cc6d7925820e953b36906bd63456c23a4c7 46a4d38ba7ef139dd455ed55953279c6229eb792833d5360a449e84d31ec9d8e 97d72d2c3deeb90f2b7bbea277bb5409dc5e185443550599dee57628b152cbe1 06cbf2f8915bad92aa93e100fca048910bac864de2e67b786924a5dfa47ed2dd 5f01c55600492999cd80dca1fb95bdaccc1ccc4807e274f5e2d76780a62ab9d3 bf3525ee75abe62a109fb35e297a6ae6ab51d430756ca8a44c2aa0dc558623a3 3a78768168d7f695ea5c96485f80efadd5d0434c256732af421ca95921cbee87 1748b5a61482181b63b42252c7663b9c19d3386000b73e440d49a2602801a192 47fd23ed6490a2ca505a01a2a0216e518b5459c621df2fdf9f4090575ecc0819 22cfab5bcf95785b94f17529b1292b25b8ef368d6d3bad17438c9fc4c41709ee 3309c171ccd892d173085fce4bda373b15bbd84719baa5c86b00bcf2c04191f5 6e8a6e2009014f498970673b8c94765896e485f7f5fdc8d36c97e1daf93c6003 77 964f1aba91908bc5383a86e42b1d12a626767ae2266a7d51abebd85e71adc90a5eacb3cb318b4cf9841c622a66267e4f849c1da7c1918f34b30b981051925c0c303cbd855169d8e3929bf41ceba6ac9083a0e4497eca9f90d858324dcbe1cc0197e01e96953944ec5eae98109276c148dbd8454b843b3ccea46930324053a80f53fa7ce8847905357dced42de31f37569666981abd8ad79fd134817609cad30838cae4119e243621403f3ad0e648f8c554e70c50a573a9b12de6d063a0ea710c6b7295caecc2467b14e6c23d119cdd6a683988e27a427d641097d9b3289b60082c07a17d3d2ce85e2d81a51e6fb6e72564c2d83b3fdd8e13858303f211f82202a25825d8f5df3bf90b4c44476cb27b5afbc2e1332fa5d5407280d268e6d745018b48606119d478b0a6c17211004a141a9d260d762340eda14d37db218fd5e00e9379b467e3d48fc6362f66b7f96d30f0c9f54a56c7bdc925fc19a0305b30420f1abb7c6cd5b70b2f6aa4f7c93e3db7475e7eaa85fd89d35ac303089d4076b5068e60b1df9495bfa5189ce85e9e488cd50dfd8396563710225c182f167d882d08479cb30929b8f8483976a25c39308e4aad44202c71891f0293b6e6cce8b7810fd44c048f6783dd77aa26a71050ee3d0c42b29cf387634081d5170503919ba102f5d88eb9a6a07da23ca431e20c5735a6a125e056e645c23aa7d9f98fc64e9a02392dee0d10ea6add93eaf2a61730f0c23697156b41b172197c811186efa0c60cf2792c23c8669dfc57b631f4c255312c01c5581333abb0050509503ad762e00690c6f7b793994e837934fa5a570073fd26fe4461e57387ccec4828ecae56500671adc03ed8ccc6965ca95d5fadeaf27ac9b16c877eeca9e4cd0151c3d279420c70b55f6dd6717129d8cad62d1accbb33b5008676d153a755db1ac8a13ec3d7029351c1a17c4eb10d2f675677233af56bd5330b76b4ab3da25ef737fe3f79e1011d8dbb369786c10854a06dff14982cacbb665c6cc959f073d4794ede912e1d0537dea0239cf59cf01c02a85686812dc24af2a1c526bf2e82918e92b3b6f6a00c024775c6fec3893dba077e51269e15ec47b961819f925726e0d18f8d528b7800b0093e7418641f60c58213f6005f7656afbf0070c794fa8cbebbc411a8b9700f060e12667176ed4dbd6646412034c7751d42ca838db3c567a75303c5d275df05166f1f1c8ed6124529297535a7c469eb27dbd934465f02b563205c553c442600f7046e75ff841607086eee7cc70d93ba74ab011909e678bb6b45ae91ddb0670ebf3289cccbeb98df119488e228cd0f9d7c1a0be9010075faf4d9e2ea0b72e90d007d466c688664c9fa2061ed008f4a3614b9f651ef631b429d660488b0b30e033002a699da2408dbb877ad5b1d8e0a7a651df2996d9d41b10e0234c78169e009f3ede121b2c80b7832496679fc1219a7cdb048ab06be00839eb740cf7164110c11e1ef1b2aa204e8064272ce4c142744a20e93595bdb9d0964577244ce79bd0addfddd902f85eb5ee839809647cb34e13a0f01c3cc1a2dfbd7ec8ea477e02409e44617e6fe7981c882919114241212aab2013754b7b66656e892d63cb3be82096c421f809ce17560595af57e4578345602d0c448b40d32c6192a05a8dc377309f6c03c86334397306f231c3b63e3ef152b06f6bd2553ef10fd02e89d9c0e0a014489afdc53cd7445d3ff3ae1babefaeb5c580b9cefd1fe27322848854756d5070f831a406ee809cca27bb1ce73027879a639abe6433489845c8d8060c8af0a01f2b9f04025daf6cdbc4639cc9a20031148d73ca3640c2b96d7cfff9044320b07446fe1af874ff741fb7a626c9a87bd99b4964c1f52e2876c6756f78ab8172704d0afdfc4d93531c9ff839b120346cd31352c31e4d1ed6b4e41f4d59a1bc3450cb0e6ca3c711e3efc8bf73065f8ad6566b9d4d7ec6e8dc1a74733ef5d5a7eb20b73a11159b4552d6149a5cef62161f389893d9bee70792c1dc4328166aec37c0f24bf63a1a1cf456576aebb89f405fa1d5e96e3d3fe44c07dfbd583ec4fd6c40ed080485d844f35be2b856bfc9d9c444beb68c3243180da045afadf292d41250f01a0ee65f73131fd931184dd272b957f6d01c7f1fab57539cdbac06bef0a1d0afead4e2f8b46d2e3183fb001ea2467889e20c5f666e2f5d47589eb6518dece031b0db48e33e87a21d6d258bdcd24d9dd353226563948c40208656f889852910a07002b8bbb7bde2093466d5700f124fd6d9ca7f8d00acad20356b10439fd410e9e050614364268d392d975da9d837fe281e4b78d81a0a56cff2c30dc844ed40254d3c73aa2d51437e276c56d6497583bf42c6ffc7a8596428a49f5743976450b2e3b4823f670d038e2ae12db8c650ba4ec7962192f479b859956bd952168a808159691b912f197fb98217426a8b6b5385d93d6627ddd6be0c3df6aa944bff10fa32f7c60444f6bcc460417acbbff9adef5280360e32f61c35efaaca7f36a0401b50ae6673e0646ce64f527876024381a2a096d3b3d2d3d739dfea7afd6940604c27db89c4af12437451ef23cf034ac05944617a19234b9107eb65bdf54d7c702e2b88a4ab3fc44c7b0d797e70bbcd73205c20a0d982c3e04c8b2d65ef2e8f2045192b64f637d296e789ba72d4bdd13ef6621a97eb692030daf6fe37b4191340883761b28ac501f2196173af058be041bf42ed2dccbe507005edefdef1db7000ee6a70f3751b08a66a30f8fa6e1676636a33e2776e7d3281760013d1fd4e1980df6ab95dd154908254b248efaea1e63d279b465319e5f9312133da9f157496500ff5c4f1c88461d1a03392dc1c7103ea33e25976ed50fc2d1d0df87337bc95d0d4b8372735aa002bb94838436b278979bd3e37d46e7e98b31e8b2eb00dfaa080f47061c6e5275960c9a1b27b54e6fc391ada47d3b86c5a1d03eccd8084a136b0ec0c2a17e22738baca1cf014df191de8eab17680d8971b7c3f8ef79245dd66f072ae39c4d1f2688267a053c604a5b761a30a904fc4526af5274b25cce9b206d06cc840f5ff52531a81bcd786762117cc0bf3f8d32b79d602747f02b8834a6e809f5bdc841e92e7ea9106852a785d0e8b4b06a953d44117854efa30a18b3600000f51a8b106bba84b3da14393c9016b8fb1d2c2b14f8e9056a183de02b4963130948e5228ae038d24ee2804995fa2269a447e56de83b42e24589d11a4b191f0205174e7610a308d8d7935912ab83c63e6047055a590b374767f6dd59bb210dca0e33dd7f8738946f59c2a33436c51f71558354b51fe9ff0e4ba465fc2b71e9790172478ce72ddb2779aa3fdc7b6a650b0fb0b515342b4a553c2ac8a717a526c40873a2c912ff23ef5f4c3a3318f51a369ce989c167688fa2ad999744bbf28b730780af0836b2e88138165511bd17f1e77465e9785d7f4cb814e683396e20da37040ce5e77b5ff607fa38e769d448450bd30adcb397d6771ad0c20b0e9cc4ac29063499d2bd323094aa3ee406184c6fddeac2469ba61095483f61be306003a6dc0a41e0a1b14d6b65ad9c2477a884062dafb551e42464928898606d702858fee8027cc101e683e444645f62c4509d6cffe43c9cf259850b2bc02b6b9be3b92de409295338805b407327e8e2b0ab7d40869257b2675f1d8937cd486746aa431de20c7185be7e083eeb8a6283929cbb52a7b0b53c21f5f706d1c36f93cbe4d3975908425b7869a85374e0895d226099e3907405cd1858ffff3b5e0857d488b2a7b40a4fd5f14a480fad18d1efbdf2aa0ccd269083577debf4b1eb73a76c93f517440abdd1e3935edc2fc5143e73a908c8ee75e5c78b1c6b4a38db93a81e4a0f9778050be6f9e19682af9b9da65ec9fa09807549bdf0d12b66b4e55fc82bfe0bc7320c3d5c4a82fb9384c7565cfb542712f6190ec3bbd89b5f0abdbee8c6d1bb065b09f5135f9f0246b5f42d664da17164b9cefa3ed421a45170ebd28b2856f6ac4b0c5dccbc052b3c4a406046f5dcabe0b0c444ecc48e61a2cc0a93ef022dc25ee5041f439cd52bf536e8d05e469e2ab59772a44f3b5597e23d7dbd36db4d616f6701887da077d80fb9c8c7eea8f21eb699dd4b7206fcdcc696c5cf947fa41794ed0fb88a914c9cd7ccc54a03faaae73dafc3ef44cd0cbe9334135a0c919cf7103c00ef004e7611dcd5444005e9770abfd4b9dd8b779b2152216f9351d7e7ae10ca0935f03a04d7f5a6433892200c2967f7cf95714b1d3ed7a855550d0891a5920c052607a40df1ceb2306f3c7afda563f481155738cc7eb0fd4a2e8e297d02b5f8095e72d4f7673ca79d84cef4c54d8232a948f01d2fa232ee25ca68cca823d5340c641acf44703e17db8f7d3c91ede3316e2374681f4a8ce817733c10e575415609761f1dbb03ad6d761f7e10201b8f067aee6d84c3b84de6d64d401c0a94348f05551b0d811205aee855c2d675fe4bc2f1991a80f8e39baa7ec1ca9e1b8a48c10c189004b1f52541c38f654436f971199cbf0fbc5548a39ae3d0058caf64eac501166bd366cc08fd9f12622683ff7de0d4c3acc33685b4a18b398bbc13f32aad0eb513b4944dd4b006adab78178f71ffa9e8d9d3217afe19283ca5fbb1d4383609078010b21954e09edb71a5ecbbd72d0603e6395ce25d2c066a97b94d6732280f9c5c807e912751bf5b36ba1dae4a3f725feccedb740e7d4a273da96934b3a7030b40d0a18fd3932b14fe482906af27924395f28763b4e068cf86ce8ecd20eb0207147da79d85c77281ba79ffc0876441ab14b5d23e13db04ae459e752a1a3b0b2c41fc23499f98a3aeb0d31c2e56300301363d932a8c48e6582ed07cac65e1061a7a76a8b7cea34f5b2ecd278a86acf3e7658343fb93c354a5c7f0e141fa050c0f516dee7bea5c35b398524f3e49c8b0f078b35a79831d5c548e4a56f6f0dc0830ea88cdb22d257608c149574e7e39404bf0e75fbc2dd2dc72cab53d9c099c04621f903d7d7bb08531ab702f05fc8d984f122dbb6cb5237288252d84c3d56b01186481ea688728b45b91224e10fd2a3e68c798689916e27d270777b1926dd90d32cf47b1b7589575edea7c1bd9e6a055c087a9109b358526480f6470a24be406c145337c0f2205b861920dda31f6bbd57f17bb7dc5091cd4473b5adb6aac590f7cb7e8e2a005f4b8a54252ef4fe3768568ce894b56174d685abf80fce209c20820b1c9a4776d04d9781fd3b49a4717ea8426f4d5ab8675790fa1d3537c41410e41f2b1e573691311a1e1ceceebca95155566a3d154865a03b429a2e2a579b70e95eead357d6178b5c2ed300feafe558f40de06079780f7028e7923b0279e0d0b2c2f2aa06c1bf9f2838b7d4b6e5da99daa9068029595d572248a9ccb7b58970751f3d506c6b8e8fe436b562a28656cff90b56df696b178eb56f102a63145720eebe88e2f471d7fbcb6b9f367d05b653ca0da921dc52654593b53d3b44fe2e50cb98a3154befedebe973f33f9ab36c8b79cf8d2645f6ff7aed1c739406549700ceea093c34a5a040e39740546cd9b36d43c0b4883154e74f5a2ace94ceee2610160d133137b6e00b8d075b7156033ed718c4d7375ac4cf35280726acd13d91503ffa7d46c887c2fc764b39a184496eba03113b49dcd57b5ad99032a0749736b0d2219fd70594e2e4c371bbc77510f10de9c517ed66b313758afca036487ff18023696161642bf57f5a2aac821db1d5949810a0a1b722e0436deb2dc94a67c6001da71a2bee39a5441a9b90cf30f7f30e675375e422542bf6d217d7987432bcc03ccb5c143e3140dd0c1636e93801b1e7d7d361734c884145973f019760099df095c8b19af1c46e8cba8428492c46ff802b720a11b97f6d55f3157718ac1b47b0f7742ab6886c5ee0b5de548ea69b2a451b31d39d641bf451fddf5e98685d6a40258cff4c156a85e292b6f452df7cf4cc0946edf0e9268839e4175f2481665e2083cd23003e3b8f1d8da11730cc83eb8357f4d1312cfea06dc47240171e469e805cd472b8269b22ec99c1792594436fafd734b265b4338567d6054888727e9d708f297035ca0e4aa2a3f2bf5960d5012d01d0f7e934da0c25de43487aa42605d0cb9f5504205005030429ef943c5e4880189aa6d96b9e3641faf7d19a7dc6fcb067fd1b6182e38316e2d8351ca1ce92945b63541e51405b224fdb9f1e0b6c1670e6018706f263c7e8ae43a221e41f664d1641bd89b2697dc4ee8b2d6b9974c400c354b7b5d556faf76d5b926da008aab440545514256d1867de6ebb672e7cef10ef07666c0c575228094bfb5016fb28553042eb1bd880228a48401d0bd6793560b0186bb00e0c8ba9f9960d068633025609e6709289c6c6ffbbec50cc20adf7409b6ee102bed2e085cfd1d9160d1d3ea7581f1023ead418c24d89d4a933a4f110585a54513357e8ac7cbd234a835bcd53a9d1c5f4832f07aa5ec213c9a6736d70419bb412431cd4dd4c5ca857f939677188560c4c2fafc6053bab6e4361d7350064b2473e267d14f4b2bfd87aa2ace397ca1ca62ae417b35e497795573a175370ef3ee8626412dcbce1593f6e1f7f310b550bd8b54c4f12296c87b1fbc6ec3ff0d2948789a4ca19105d67235fa749467ac14682d5a5680b6267bf38bd4c589e9082759940fdb611a47eab4a2b92e5c51d63ae2f17f1aad8df975125a4d43e79c0c00421cc83193dc826cf90572e57da088a0262e9b0851c5e974643263e445c900d60f3d55608f081b106facb4d20b1bd93f80fd95a29e5fbdd0886487a2dfdf0f1355cd98bfaedf9e0d65ceaf59fb575b346b70d74f4a099966f8bb77b86ad60991767c375bab84e221f5b11bb693114a2f03439f2b52939fa7f50bbb4f804302ab40e17a7f04a23ca0890b6e77cda648c3dade5ff6debd9bd6ba2edee9fb660d8af65278d73621f0122d2f547929bf826004f8037893b5458ea6b82c4b4b9b03241d71e04928f505f940f1ed7c196f246198054a24e41a0b99f790dbad76d3026828665e0f5c0ea785f39f6c18a29aaf7b58a0124483c9c33a474b597e317f0bce0b6562387280e261d94eb5070f2a05c8794612f1ff1dfc3b7b9b39963aab07367f70d908e4bcdffb3c9f0a2dbe3fb3d4209c7e046bc780163e628edd68650104052ffd574e9bbb478f73e558e70206c04ff0fb625b4a537ab0062bab7ffe084948c3db15498f5d4a5421d7a7ca5f4ed6d6d8c13969ba65e50ddafa1e3c4f073ad0be8e94a7156b642fd05393d502ea31c7fc6de80c540e4410793e5496bc06b2d74fbb6acd6877085df8174566d60bce9c74a055a1b4260e65379f2ceab908076f5d551c5f5b7d8c272f573a0e0e77163fca0d908e48b6394da44811c1e20731635492dffdbb3f625f0d73cc968a1d31aac3e4d7b0d62148d1240d0cf691041ff63f9bc0117374591a7cbf75e940756ca26f9ec2cadff72800b50af95b1b0625215639d06ebf0b502e6fcd9cd3cd9ea58b899c5ab9cc11fefbf2d82914e10acb7a5d88d2976df2fcd6e76b5777a3035a7a0ef8d862363bc1c352e32a3b25082a6046fde3ef65e4057d0d5b49f5a99cb7c9372469cdc8c52514e57acb4ff20efae06293f2650cd3cd15bf74edcfef458f16b9f258955674d5cf336606400b0fc363c21ce6e1665bb8b3394ae3ea1a9b5e36678da68b8956ac0ad9f904f2780bf03b6db98470ef4561777798009e858b53d83d5111a90f58550e73dea455480d6476d06b41182c229fb44296b8b3e06040ca154f31a41c8350532c6118eab30585c145aa3252bae17c5600132412e6bea08d343815e172d7f10386c9270fae0deb06ba0e995671c98fa279141aa7329970d53e762d1308303726e1f067a39d0509f30be03a052e9ea26e93843cbf5c5fb4b925ea78a5c8ec7cf00b925d079e02dc525662e01e6facb323ddbef6f6a47e30d4f2839b4d5813683a35bee0035907320f0affe129cfa6640645a12ea630445351d62ddc626d7ee9b9ba57a4005f0a890595e1f2c4a61c22e3a8cba39f4e8e7dda7d59f5ae9a1b440129898d6f3208b5502afdff42029b20a98ba62723c16fa2f07291a6d769bb062fd08e054390094cb4b87e0fb32100ea4dfbab06f8564e4bb4fa5d9454bcc6ac4e1cfe615d8e0446d904249e7ff62315742268d35e23bea759fc0f0122e3fd9d820d429a505a008d833bdff859f59493e5e47e7fbf0d1b4012fbfa778314a7fc88b00fd23bc1004f7957cd14d2f28852305c2c7db50e7fb90446a72e49f886251e20dfa48c850b4ce6594a35d33578adb837431ef0ca16303bf35431dadd8e0481aceb151df700821d546f0902b173475c562d65e85312266370b97aa05e5ef6c9b5b5d043ff0249d051fe80450c1cd6289db78ea29638611fac124253fab2b453d099fcd973082bf12b9e810cf5587bd43b2cd50c53a1bb544df0a674792fee29c925ba04d40bb9caf043f967ba9ef9c03a668fcfeb9774c5914d231985a794d10aae7837b702adc438a2cebb9afd2dc7aa9ee4d8fb448b16a8e963015674eadf289338215c07d66e5ed23f72d02bc316e861960c414a45eb69309fedf7685506a07add025b0cd64e3d9605d9ec7106ba103b34a498c03b10cb0e2ef5565c45a72b7db6b1d5067f711f044b2aba03b6a78bd42b07ef71431c462e8034a5672682a0619c5a6301f55f096a3cdd405e538e92b10387369412497586769fe931cd64939cc5760c0c7bd12b399546a1f7140795b6ac8ce0652d5b2f8ed6c44baae2d61e4a216239073661df3a8121dced23831728e2cc697963aea388fb2bb21dc86a8a34a09d300591c9336f48ec25cadfa0a74e66d190b7cf9f4b825f67ee9a5db593481aca2a0672481feff11eefc7b1db169173b591efc318c1348fccb31f21e71faa9e575e02f862ffbc8578bcda117226a5acca369bbfbc50be304ef0e774e9488b1279b305541012ffab6a015b55f22748158a72ec38c6292135e52620a9b8c19b30b1e907dd6862505652b018ec29a4f268a538c26ac2b07c76fdd58e6f02e83356b1d100b5327f65a24e2d4c19603f4506f2262893e6b29bf2e5ea6b17bd816388693805be4756ab75f2538e89a4bc196395069a0354112284730ca74f83c4fa2859820d2999c33aeaed013c54c1c6b53404a4ada1416060a6625fc23a2f31482dea97028839136beb71935c1441e7736e0cd282ddc65ac21a4364c5a15573f4eb33290c5e3cdbb84c0f4e79009a41c7a10de61dd6a65076c4e239ee947226be924f6f0a5cf8caafd27772b248a4eec556c7e7f490ac9d614dfc8128a6d1de5078137e0e824d7061e7303327224970670c613b843a5b0d48c9692c18d6554baaf6c6cb00c82c0d3882fcadd5dd73859c8203d0465d2f1cfc0d33801beac9ac4f3645c700690bd497f538d820299dc08b509dbf9914547286f558b18ad6ad38b0c90b4c0e10014d5a975b1ad6096425157ea0a970c5096366c5e03284e07a4d2dcb1d3605f141e7f0d3c0f823844cb20de86266da8d5471ea51541f84fe73fa7c02bd5b042d56ff58815b8951f723f63b635ea4d2db7d1284a613c71cbeef9c0c13675b0079a22f520eb7ecd63b819dafd026ba49b442646067e1705ae967f46612b029045d5f2b3542afe57d3455afb5656329df7ecd80632205fb3b6de3bf8433c3800bae113de16afe42173241cff67f4ff1b634c8fac81d9a14dc736b99669b27d80072f987a19626b820dcd22bc88f7914f1a9fb10a456b106aeee1e9263cb1c6c079a8180ddcba1ac6a1b411967b9fd716defb6f456e1508bbf7d2808d4ba559000c2b57f62ae16283ff7e2e144ef2847b5dec2803323210d78b80b5cd798e3910d6813363c7c3b3476d0105f21591cdba1eb5df9cc1c051e34c7c6ae7c186f2908f12abbbfaf07b6fa0184ec60f18ab4f5ff5cd331029c7c632bd2bd9aefaa480c6a59bd4acf9300ecd5f2b352ad905c3fb0b2b1ae1e9aca717caf9f5dbbb3750393de2d421f373bbaaa6d2772839d8e5dc45fbe3d5bda2f544ab58d578351b10e14416add30cf23ad95370d404e703c4f64b042c04d355671666b3a90e631ce0f11e7007027a514823f7354976272f14191acc00c6a937a518057d79b4ed26d0aa7880bb71fe6938cf3c3911e83a066a658fc653c97b8fa00795c4a95d4c5ea0452f424303c656066fa52190f738679389910f163221a6287956216f4dac8560af631478bf56ffa954e772987ca8c2336f8855b8dac6a547090bcca325c5b020f29bee4240c111025ea6e87e97301fbe0f23b81095902eb6ff7528c89abb37d037796c1def752c3c6e48e5addc2f8294deca3a27e5b02c016facad87eeee52c034678fa092605ab1bd8b1bc246f2012acb4b10b8be70a6cf0210adf5542d377041af11e95833b3d069bb9f56584b64ea09f7d8e0c81a9843b18c902ee5d544b0c1a3cab85cc41071a96f3a532f9c1ee0f4739e2c6644e8db17211aa94e658e5084b3d7d096b543340307467d13cbb2f19f4c29f9907decc7a1506e707af264d0528a7b66bbee451c535ab394691f9a960e85cbdbaa98515f9322ca3dfb11d8f0f954e8179164072f90cd9e3607001ed790fdfe7da2f0be821142bfaa5246fcf0f2bfc7950dffba24847f0f6dcf83f15f6bb03436e5e4f7c90aae0205255e6c30642d1c5dc71ca6895df222a16ad4ba71aea8d3e08e50003c96e3fff9566a6670a6debdd28130b43a6c4ba97aebdaee39703ae4901742d254808026affb5fecf0d182d9a0accddf7becd0561d3b09ac1935845037d618690788b54c032e9583a00380bbbfe6c327309c65a62e1d52d9b310e217f66b795e39830bce54dde81f800d981a58c89f0fd73c5360e984779d4912137f1ac3db98351bf31815ee69f51034a0f34050d57b1713b95ff0e04f80b5c90c98e62c284d5d163f0834611209f09a8f439e5fef0b8e1700922c27ed3f735e04c4b44a7e112b9a93e0d7a35eb3d0c9271818541b912251e336327a90351b94234f1e68b16f2d5e8a449fbbcb50d0179cc4c1262f883c1d8ec610de1f78a51964278aa5cc682501e6cc26d069d070d372946e3e647e44448160273a0a0cc576e3ec9f094eca6c2803a025eab8cf50573d48e9714c4c13ebd8603dd4c1c5b790bf24afb8a477cea592fd278e9d07d05b75ed86d75bb89e340f620be4d7eee61402703916a3dfd88285af2a5c3ec370a20941c79f0ffceef712d922c9501d61a774cec163e54a54cb804c5f9982f210d00489a33637c59daf1b428e3c4a42d25c54a917d206e5ba5fed7fe572748b50c8549df20fa19c5bb041e303ec58f4fa2489b4ca4426b6dc340ff5a6636d53806e05d03998b5bb41eccde4794040a564a76590068578f2315f9eb5013b417b909d62856e9ec7226cded9ee1b9e3a0b138aa06eedfe4ba0ef50676d6a4dc4aec0d1d14e4c0ff95d02372a3d86e00e720e5da906e54b8d1171ea45a7f457229ae0da1c298f4bb2dcb65ca2b6ce6314f329b1bceb8d537d193441d2177ae5f6a760830b77a027fc52de393d317696b1e5f5539e8c03f827c0bea22c3ae55b5707c0914b6b46e29e8ff49b1a1d587f862cd923e02a21930c056ab1981dea1e9220f089f66de862a1b847433f09c8660d1fc0410e96d1d8b56ba5955213f64a0fb120e9d0f1931cf023f7e30a83d035a19fad0eeb7a094e0631025046b25da73508e0ff9a6a84a4e9b43dab3a1a81c412e554e99f5c5907161f0ae6abea1ef20068e060bf9f999e47a021db0c1160ea8c51710c10f934b1b923c4c8f80a4adda58280e161cc8901dee6ba80f8a88c96921f3896ef991d935a2ce8cad2a7c9b410ef1086d1efe57b8477a8b9a9419dde319bf226d0a2c2716fb414bc0feb40e940aa50d274b052d390123c3daa4f7559bce7be2b14ed0915a5fad3a6a6cb8fe4e76220c4e0612201d877a5c1533ae62bd2a0f6573220285d7ad4d04eebf9c35d91cf607c0c28010248a208cda7a3bb44774f6129c7edcbc73339c1410bbde758ea21b0a5ca8d90a720f2f1a8f3935f353abfed8331b50fa5c6bd296c9b4a43a5d451c0fe35da26963bd5ed54cf9ae77c7393e7f734465ae293cfb945a061184bd5548003d4bddf991e00bb3ac577796d32f28c2e2c874e7dfb732bb51fb5fd4764ad103a244874b963d33fec20d1d9baaf5ebe99c7ca445e0b86747e16544e1486b260bb43e048046520161c2da0408fddd4c5f164040fc5cf3e51f8f1d687f00f260005e22593815324593c150e274c2fcf2d53cdc96c81bff8729b06a82ff76e2800756a33a7ace3e03275473dff91df6e5822abca60a960a2faf4ac5d60a1fb7750cc2e6a542eb7e25790007cd2c472fb73ff102b0a8037416420408bac0535ef807dacb574c20d9ed95cbbf7854e2038e160df701886ba0622b21dba4f1cc901a0b74c8be25e7a0dcb23b729f669e0f2edb78d90c9660b05e11efec9199bde9960414dd33c318df89222adf62e0220a31ac169c7234685e72f47e45ce1f82c3d30bdd25e810eae298e73ffd99eac44f9869f1947c39b1ea43ecde0397a9e3b49b0f1a3b8766a34c777701debf292c2087c0478e1ae7b9d21fa69f76fbac53aa590185056b7b86993601c2870615d23364e1cbd851f47f67eb72881c31319f97ac06636bc1fae90fdf67e44c614438e446a3f1d404d722cea1a3a4549499c0d4c80505e5913484cd245b170c4d7875b3cc7adc40d1f639f92075709abcd0afd07b0d9354f108b09191ce0765383accf5eeb0a3e28a8496de4801f3fd2e458280fa02d53d4b41a36f85eee29805b18d0906ac7e23af7c713dfd7c72810aaa8942a706e946bb57686c22465993bbd160af52445e1d35fcefe4bb50bcc84d7f0c5f5404fa189ca866ce2cd5f131340e507106c25a6954c8f5696b00d4d6f5e094a3fa073e41b50296530a0300fac26f8e4c0a8f36fcfa95487d0b9a06804aadfdffa20fb6a78dcd773f5c7f091e655384b570b948d13c9db744d2ad2be61762fd528304a55c93cf9683dc11f35d683165ddd1e708f5373ebaa137e40731d331688f4a03272586e31cb38f45bc4a51b5b48e2ad29c6650bfaec726412cd045e73f78880b37deb590d996bea622d8c6456a92f0b1cee5cb9f24906fbab482eb69782fe0062584bcddb549a91c20d1271e7b38fb40f59bc2840ef42670d7454f048822d207631934280442fae0b4a69ecf5cbf087d9d00de490fa703413c2476abecc5a10d975e8fae0a1870300fffcb9e8ad44afca4b85ba3e42cb54a998683991025ad0da2a01a3135ab8321e8276caed898db03008b0d2a1994b2c7cc8078244b58fa0b8e5a9cd24184e2fcc23e8549c68a0351a47da14028b5ececfb82f366e76e2e03c75bb8bf36e3983fe07b2f10f73bf6746c11b19798a1cad183d7c3cc36ae52013c9074b9755a6e95f619abbac953d6626071883ec5aa91af02c41c93adf6bb017acbbcf94505d6271f02f445f78fc3f081e707ccdb4c6f271953df358b6f1008ae795c6401cd319271cbcc0b1e773d7d89f5cc4c20a270c1cd687a72cee8f702863e40bcd13e274e4c00019da0aa6e42b25b7b52935765100f63f04694dae6010c129969c7b8cbece68d081f1712b23e4dd7f4eea2cbbc4e7ecb6d851f084a03f88007f4301ab7f6d14eb3707e2fd65bc1ac9d61bb264c96d38c066016eefd02ece5d026f6a23c78aeed4c515d18c85dc3c98a208ff9c31a87338355a1ddd4032d233fb3aa34d66634b910e7b0daffc06e582d91bdf0bf43581eeb34f60943091cd79aa5979bf310cbef72a315ad6cc311ee8d570501da0c3d01d1a09e72eb01d175620df6e35279c642ae384f090c2cccd0d2e7427c7c3f72d8d6c531f3720b1348d9e3bce76471479a6dcf62b52be88deec325685f484985e04848b8d8f8087eecaa711fc0c71e1f359a12e0ece74e7892ad33a53cadedaf896d914f917909 +generate_ring_signature 62d9fdf998799ad5f0044a6a7c5bcbb9deaaf78ac678f0ca3750aee518456f70 e85e6d34b7a44c497b42634e4e8be3a56e714cf5c44792c29204903769a21091 16 21b726d56ecad082666ffe11fa61408df99af9e47500483421b47a34bcc410c0 314d10d957af2e4da98a98de73d201ce595460aa9f842237ccb0347e9493d3cf 9cbf055445a0e7121e267b3a73e7449e9eb5915b70922f47437dad8f4ec9417b fefea43cf649abf557a369c3f7a11d4812394d1def349ab23e7c2e44d019288a 48360109d5b6da985b6701f47397ecbdc59d97383be7e9b067b318acdd109ecc a97375453701cf393e2a8e95e5b6c2ce365c461eaa62cf1449633e850b5a889d 7ca32230254a075dd5d166b06603ed21cfc4f5215c94bf84ce9ccf3721c44df8 14beb62093b4712b058f10f8232884896e4e85d2ed8ffc1fc6e03b6e1080e2b1 a453729d18b0d90e519e241710b05547b2569dc07ed744dbe3a5c97942e3ca75 5c9896d9305a062aa12eee2bb9b6c8e3be3641528c6d19847081172694d3594c 574a15a29cb4e3c1b244fbb9e2bcaf0cc6c964d19047d3dd44efe0d5bb5ea1ce 0980b5584b6a796bcd0d1e87ecf3be9f1615413e3f1ea237bcaa17108c11a5c8 653a3daca423c001f6f69ecc804ed50a82d0d95cdbdb35c7483fdaeca547d807 18f5a832f606874954b4316e40f69c9df460e505c0a7506f0d42af45c58b483c cee4f248d50d0302575d03de3e792cc5ca9a2cdf58abe5b47be7e1e4a801771a d63afdc5a0cfc9a531b4f099fe8b74b2f9c758c39c1cfa3fda7def01abb4e422 7c23e8fc27aeb5d238494829871baa490fabe516081fa922f3a4161ed0e7c502 10 05881846ec67d764935d6c040dba760a8e40161753de1639dcc9cb740a2db107fa10da97ed599be265dce75afc232372e3a8df3c822682f146be03e780f1300fd24e1e33d1ef5967386c9d20b4a360bfeb1b4767c2dc13a35338aef71c312b0d5c176d26abb5b92aa80122339200c4b7cc03963c34bff697f72ba071385c1903ef2b9010affcc4c76356e6228c3c76047c70905d987fff388239eb48c25cc4012af1d7c05f3b33dee915065392d0ed1c5265935350cdcaf638a86db2d14cd00360fd0e6e2ae2fc172c73c1ec090beb344b3f54db1d0824f52d7488a9fc214b00dcce1443dbaef00ecb07bcffd9ecf8ca45cf4e3569e703c94b2540655e13110dc04bedadf1c305c8b27e7392bcde501d294f75cd47bc58855b1318739c7e290cc87beed32932ea4a48293e47f367cded0a1c3a4db4e9c99daecfd1d1097cba05af0ed127ad8f4a3ebf7784ac7477994bc3ae303b79d1875947ddc6c0da61fa070a8e59831c3cffb03488ae53cfce79bf4e6b7bc98f71e274c44520140bd3130c45e28fe5cf6bb4289649fa3538c476cd294c53bb40d715448e43d0bed5fad50754aa52ec4d686e75400e7286dfb39d6150a7272719d89ea1f922657bc5dfac0f365c468633f0679343b8e625e36b18bf9f2c5d2d6feb156945e862948f6d2603719f830650ec2c2bb194059821c9b6c74a1f064abae3c72ca801439b03d16605506e7524c75c84b97b6e7112c48a1aff4bd563fa3163b6fa4bd1e415cd9d480d98be4d519ab047b3259c1cb99c1e199142942b2ec1fac20e457419d27310b20ef5822441014a18cbb9aae5b75f5c07e73e17f90cf1ead39bda032a8424ee0807375e9f69fc74723b1b11737fb37e06e1419faa9c7b55dce9a3593d215d6a2904fd41afaf6ef2e2229f0c27f094bf6bbe29f57332811f031dff28a71c59e3f903009289db494728d72d5b10fd7bcacea433bf2804b53299b76574916368a8be0ecbe5bc089fa687dde3091ad9bb81a9a52e0de14eb6fd680966d3e5c55e29c5011607feb7514a3f436b31bb480e7a2681c708833dff97a8bff685a1b15b2fa405b14c398582cc7d730493235e38a1c3bfa5705ec909a36d66309dcb87347d3d0ca8c784a70d4608ffa0f35ea3bb2d134cd55caf098c6dc2c44d4379a3b78e4c0877464d1f3ef97e1359f005e5c095fc9649a17ef962e3f3d6c9aa0caaa6fee5072fd4d47675fc1b2377f599dfdc0d6cdc5e5e54919f1d0c9fbe24c896b8984101abac9e1fb9e2cb88c502e5d60f46771195903411a48fb4545a451453871e4c0ccf001f74c8c3054b604a9fd097da2adcfebc84980f9aa493a8d57acf959310016ac9c84254f0af0c75cff4b9708bc1081ab6e9928f6108f736b1b49812ab7d005af0de23d2e687ed153e43b176f0f2c7a95f14ccc2ee1082ee73f6b97c86ff0a +generate_ring_signature 10f3730c2f6a2514d89bc556346e2a37fc87d7e83757248623edeb9fab8ac049 0add4f035c3579eea25efb66d55d73cc4df230ee3a749cb5ec8ed434d913e41a 1 8c906227edaf3c2236ca3f0e9b6aaf208bcf39ff871919427a7719ff092d26f5 1fd2d19d0945cd70b8a3104d3f0a7548a9acc311648967d48ba83203f213a50f 0 bd6a966ff889ef342dffc2e96befc46b687bdfbb776d823fc5e58acfc6691704f89e6e4e90ce5373ad30a99d00a92d2ced7b6f755e5a8a7f9b0ee9969d9fbc06 +generate_ring_signature f4555c7a94cab7fa9604d4c106969a84ad66a469599db08ba49bc24b1e1488bb d97745abc00bb8228a40b47a0344c9556d8dd15964fcf20f0fd82341d6f18670 171 cfc4bef5eaf34348f327005dc7f809d94e495153532aa7bd337e1230de4974da 79b0a445dd281c6c167dbc468a4946bc46d7ea55b1393895ff1e8008f2f53a3e 411bad6789b69ae4a955d7d5bee956a5b7c73ccd3481b6e331fffa049a45d625 c5e8959c5e9f915e5af54c1f77a81fe3cff041c001bf163243bf59c206b9b78f 401660dc495dcb63633517cdfb4c83f7c1bbc39b510caa85d77861bc3f221618 6ca7e36b209d1e1c5b20b78e26ca678325502c504c43e51fe2c9e12d2ef289e4 b295c552bd1495b7b21d99ae5bb7b8025e507a3042b6fbd81cc78f2ab2878927 1a10dd6423205ce8dee6302ca9a40f2257aacc9e954bb6fce5cf4a14889b00fe 4def6357385d6989e58e06f38a50fa51503aaf3b13c2bffa146d2824f93f2fe0 8efd4d3019835bec1143624705a46b9f54dd4f410ad23a75e39f868728585a6a 448e81181967b7c2f211960907184ccc9dc01effeaf2bf0ff3246cf715fa7841 e12006e490cd7037403ccb0cd61a54d6016103d8228ff5bc5b178a4be5b3d5a2 24c6dd871e6f6d1e466e1c2be9779ccf2567abe7533a6d0868b3497346d6f08c 4dc82719c9af722b68c9c9b7a6eff94600c7e6a08240307ec343762f9d24b142 f72f1ebd2e930a6f94e2b3487e3bdc9f8f82c78e8a890635c76455bd12d2620c 508410277ea62390fb737108acb07efd9b4afa8cc3f0492f1735381a6bdd85a8 f98a6c409991d99882a77806afee0f5a4f479f4840279447945b2873c992d257 32e736d6c58b4a36c91cf1b8d9719430db9182e15184a30797eee75dcd966b22 f6fdd93cdc90c61980ea7069933b6dbbd53075b5a0dfea2079a461b4b4ea6658 38cdf0a99c31eda7fc48ffd68989add1a6224503e269a5536701b11e4028708b b1aa00ffa31d1d3d7e645dc9ab2c9d724b3a699702c877e7ea610808dd2ea9f5 1a8f990377f923a024effcf088cc532ea6049d533d59563e398140dbfef2790f 635710a81651e374f11c728151a6b81f8904f4036f70cb3be54c4a1f394780f1 d4c8b7a93f5aa7b5029651f44bfb87ed4e762e9a166e37db35eed7ceded15017 5abde930e31af431a396c120cfb04ae6377f2f653214c76278b5e93ee91c7989 596acbd0cee6be1584cd61634edd8ed33f0fadd3918e8b3398ddee785be0d2e7 197c6902281fba43aa82e272c5cbeec79ba8b28dc0bbb1f1ddecff2c91cf0444 88176dadd11bb0920f9dcb0b53cd9bc720919e663588317d6d48c38fb4c6a795 c623bab71256ec174614b343d79a6b1c7c29f89a94ae9758f60cd5f2c01fc40f bbc6e9dd8b262f08d09935723bd9ceca95667d6000b64d6566985d8ceb02f5f0 e8d10be27173f52da0711d82d8a2ae34925fdec44dc232eacfd24932b2e459c0 05d9f54e15501e036c89fc60c8fdf9ce39a7520d0585e02e852f944a76cc47b4 bb548d6df5b6e342173a857f8378450dfd1c1526399648a05f81b7e72cc5676c 037311c8983b23d7ed1450a24706026c1930d7ead062b4c5806127061c96fa18 41d01eb664f28f27bd51f1959a06120d7eddfeeed185fc4b5b9c67593ca3f750 ee1fa901b60359598a0985f6642fac8604aa654e78f1f3d18a1fc4ecc87536cd 7d69624959350420a65aec671b481fa6ca9f3406e9080acafc5b6dc251e50a56 5315e45e258217f50b9bc1d79bc411aa350d3427080b3cdf5c97ac703861d519 a4ab5d6d62a398b29a4bb0ff983060158e2ed7a81488fb1271e380839d5ab8b5 05985546d407861f5c113c25061eb90876cb7b1d043a74af25c62b406b821337 bad614857399ac6ec7ed7887bd1e9e0fc0320b595233fa0c6797f8dce599ebda eebe0e178a0e1fe78e6233eb6d86d3bd701173831d7f40da0d64cbc572826cea b329aeb55ae0de7a468892cdb57aecde5268829aeb84bb88245111fb438dffa6 fdac8651768a15a0f8e502319d3cf2dc41fd737e975c2b4a428b42db1dcede27 88d3032b6384a94593f5c782e3e7bdb027414c3fd99d8730b2252a7ca744a3a7 1771480e847547152d3aa315218a1c2cfd01cd45ba95182a1b6447103ccceb38 37b27bdf4740b2a100f54f9a289cf58c2b66a4e4f1b13e577095403c46ecdf86 427e01a2c9473a771c6fa5e2dbeccb62d351e429e2947b2cf8c228bb10da4649 15762146795864be7e50a35b2f3155b05eaef4d1b01c61347db5f11d5baff073 69e02c54680cc7205966f38e4a27a37a4a7357026fc38b1b40bce7e7edd5d1ee ef15e86c3ef859f2c85cdf828cd425d4f4ab7aefc11f5a0f4a16daa2b5fd22b0 618cdf2644069de2e8275619d923ead50f93789a4ae2ee8b68b625cfb206b39c 50d292bb61966a8d4beb864d9c120170f1570bd63cf07b41332797b6c29f5c9e 0a34b827392ba1883b610d1422c11696cc281c8b6ae75e961c42be3a3671acca 14730187723cc634969ee839ecf45452304a07bca280f7dae82ab0edc0877c29 699a24951c6ad1649350086f99a1854c89bda4228cb4a472179987daf854cc10 09aeb8ef6645a359661d04a03b7b7ca72c17a0a31b88997264cb49a38b8d7c13 85b0ab6229bbfa7a3b5b7b23811785805f63909500c94fb51d89e8f0554e70bb 3b665ca09ac6eb886900c99aec8e95270ff3ea73c1d8654cafe3acfd40019003 cdadbf8b0087a497967578999992fdb5f6dfbca21531dc5430e41151e051ae56 0ac2710ec9c037dfc4c3f93c5191a14737c0349a2c0ad7f6a44796b7bab64ea0 ceb2bda229231c4d5dc7c031cf687e6bee4d94c75313fc3d662dc8a6925dde77 a530dd5485fb70af577f782c9bb1b3694f832fea5555309dc992c65950137c14 2f55b2ee169c1115ff9eb53e24e1a240b7759af68bf305667fedce45e3f0d619 69aeafa707c68a9aa05a273a1b3fc158a5fdc20e143523cb6362f6ca37f99c25 2e03f9603c7abff4fe2766d73c4a39fdef14d144b0019896ec3685684443f1cb ca18d53a70086097a653b294e2eb531efee7c58909c0a35c80202224e4dee356 aa7b68adc147c53ae015e29d898a72b94ef6b6661f3a54c88a03075c73fbcc81 70cd2e3c6c589814e7a3191ec0f20839bbb2849436b9108110d689abb9e0027a 7dc0f441b3f1c7b35cb5ea301dffdb55e8bfdbf0acad34bd01786107d43a847a 3f08b78c2866d0779ef8a6751b7814b6a509a091c99e96fd3734886a6d001ac5 11db0478b0e9a19dc38723770b1798e3addbf704cf8f5d2e2764d139f0d22a51 f961c491a8525f841bac81919a905dfbd249a7a60ee6257584dfe276ae7950f5 28565b59bedbe41949595332aeb2711fc67733117120b1822c2e9a327e977125 022098f9fa98ecdfcba9ed3a8bf49c72ccd8117cc14d21dfbd22db54deda7ba0 68f94489f7a92f0a9222e201d2b4bba8ad6daa41b4de2c57c7ce9b393dc9365f 34b9147b28a3ac76dbb13ecaeb3f6d6e722a4a8c7d0bfd8efcbeeb187b096a79 b9a3ddbedc533698050e8c4dd60e05919b70fc5d487df6d01b62858b05f48aa4 c214edc1e0d6f77f18a3beb648b37a547ad6f929f3bab96586ec3034d6f9fd8d d04804e17f6bbd4fc3248c049895d3911d3953ac98790ed97f7d038928be2946 08ff0bf8daf6b3b57164169c2b48ca64ada70baa106e164513ff2a5e00a29ce0 bfc8cefb5c9e90ac9e5a1d92e9b7529d813f8041e519fa527b224151af806e26 a07f1068e09111fb6b58ad945f1c7705afe8ffd16cbe5488bc8fb94b8d53caf0 a451ae9ea7500d137403e431646e4ffdf6292489608606042f1a0e373e062f7d 6fe4867afa167c2f14ef976eb6403af05c632057e0d0c5884a3b83052a9cf32d 2e2e29ff51678630871baf5f4b1c97d6474647749de46d887ff31916194cd43f 662a6698e43f9c15cbeb38c3a8f31fb131e1d9b476f0f3a0bf40ee0e8e6a11a2 8421394abfa917e685bec5736e2492f825e7b259b587d6e568929d4a05ae2a0c 442f5c4e472fe9a9ae50496e3ee4281a7945d55a585aa296873e28e2cae5fea9 d09852ea077348503bd7995157301f844afc568e86d3cd18f0b6d415643e3e20 268d7e0eb3a46a11012dabeaba1f7e3574d1e32074bf4a2feded65c12b97453b c122fc5e2fc268e3b10f328e4ec77edc375399c1100dd10acc894903da97b9cd 9f39d95d583cbd4cb2779e0820d40954218f009a7cc5df92e030ecd9329564c3 98e17aee11922bdb6a06e8d5b639097fe9127dbf79d12aa9a8adcab208b36a2e 43250a86c378ab45ea8f34e71a451cdcc8e0709574d40f77357ac3693db3d4b2 f33bf249022052081d7b5da52abccd5fec3330fa176306958c7e3d6291f52984 020c044368cad6dd0e1d4b563ff0d334233c6a875a652e551d0bf8855c9d772b d32198328b301c208a8ffc272e512f909012a07cc99488ca9bf08c8fd8940f83 0832c436f450191a0e0ae66be14f07ed5081fc53f66a0df1315d7358cb299d52 fd2d84f1afa4fbe177b4da82bf8f6a5b46406e4dcd6f308fc54c6eccd018d707 c650a776d5e5f98ca43507f46d2d28048a3e21a5196dbaba699ba1904ff2571a c87c053930f772f7cb317a3191c3c114fc323220d74216c28b9d1354c35a866e 7dc1e344d66e726db6bd8d296dee9d3f04ef75862bf5380e445ecd43b0932618 af34f348b06c894e95c02c6a16fd7e52da6b45663df93c6a2533141959729f7b f99155d1317e0a8014da9f95ef688226097cf59080edfb3cb1a7d8c40c236517 bcffc6582fb43aa29d9df01cfd043166191ae64ea184af279d3c4cec05e46572 dd56ac7d1111f96f932fc79f3b9a33bbe5ab7c2dfac8d3c082c1d07c31147ba1 af799d2346c63aa6e1aff8bcd560b70bf99b3a84780a2186b018f87cdc7becb8 a613368b100cc5a85c4955ad5bc04e5f8d95fafbcd62908e699b984210e548a7 f6db5e3e8cf9f271b129038c706e1a335baed4cb85a23934722e22ed7aad4814 f94f6840a410db8d7809f6f9b6e4898bebe6ee6c792f046cf08a0e192f0af4f6 cb6060273be798eb8e209509a44301993613f4fd5b56b4535c515fa55b4d746a ed1c8d689f250b24e66c8153558ba88317c26cc02ddd3fcc6c9c59cca038dcf8 68d13f2e581dcaa275f2523c351febf927fb6c93e65ca9a293ad6e812b413663 46f723ca4398207c1531aab36297a3f437f1a26458eaa3b7cae6dede9fe8bf91 d74bd9c293e3022a7f7a2a5cb8c22f6da99c4dfac585bc91d4a54428af645a8f 30eb33b7a3cfcb4ce2bb552ff92711152b2b114b9a3dd027a7367b8e8426d5b5 f61968957b10bd9c877b50c5f7c8beb6791800f1f3bbe9b78648d3fa0faeccb8 04e2acbd42014dcb32cc1e7690cf83bc017378fcd3b391ebebc56efb6b515ac7 bbd85c1e26423b3c6c68739a82bb5bb1bb045b8e154ea69faec0c4e55400b79d 87e234d1e02dc9a3cd8167b7fc36b72b2cc9a510dfc160b87e4c3f08046e504c ef996edd8c19019436b04acd173b66a4a881ddffefad2180c07abb7534021af3 2b75cedb11ccebc4875dfbf2f4b18ef82ed352924567cf9b8bedba2e79ac9b3c d012005892ad3bceeddb59a34f98d1717cb85daf2e9fdff671d38d29919c3306 5934b1dac37b15070667de83bfcbfb87386f93dadd3d90abe02115ee1be25511 8c8367ac396509cd1bdc3baf341cb8659e85e597c321cd8b07d8e349cffb27fd db8e1c01cbe556892ea60baabf545c1944bcaca8b03c22d4a1f5e9167b5749c2 26929b9a191e52549d227b3798974397b97d8127911f6945dcd52c3a915df871 bdf06c632061e00b667dff7405eb578912508ce8b1cda679ba90637a6ec9d755 adec4d15f5b6858c8c7fba49da88d68d4bf31cd5b92b34ff29ac5d7cde88cc98 d70026fa9b5753d42069c8def719a909d34d6cab3b1949b5398564bf6f408d70 ace300fd54f1eb3b6428c46feee6824f1ad1b7519c099800f8c90c92f4c52833 0d6a0b164b9b02a3877f8326ba35cce82e4eced1d21af4d2e80f86ca1d8b72ad 3e65edc9aa0612700e957251c766be20acdad8ecbcbac2a645e286c3c42889c1 c797cc975661a163376b9d0ac24abd415490f5c0e5f70f87c40cdbf7f0c76c29 40f0a14168aa61ba89b9e5c40a8548858efaae2990b1a102a6810cd4e14a4200 422dc367b19a024000e1f102653a2db986d13c1be3b0fd9a6d9882f6456cac0b e69f2c3a2328b8546bc48b72f489d782e4f669f0a95e759e2c58101b10fbbbb0 d0b9df63e05f270697be34988384816f750d2cc5f1b058637cc4e494bcb247d1 cf39c2a55ff7c176830e14298088e6482b864bdc9935a880e54000ba2eb0ce9c 47b5d892a0a98abe0646e21bf3acc9414e08e74d9d7ebc46b8c5127e5aecb478 df6c351cef6ffc4738adf60d0f7f995a651e93dfa38d18b5e8be83d6cf7ef6e7 9f765118726950e8cf8f2f6f6db2cae8e49556b0432fe6960fb31f4262acac57 9cf2fdb6049dfecd5cca8e4a5fd17364a89ad0ef7524a28f48aaf7b32309b4cf 13cfdc82e184d2626825ccd9f10915fd95ec8adf1f11dfca92f5fb5771f48a0e a3aa71ccdf007f88880835328b070265dcbec5b8b537af52774d99cad5aeee68 3cc2fa6550177b8354d2f5e241f8b721d2a5366c34494df2cab5ba99864cc23d f2ce0c858dc7639de0154418fdc0f5f36ad7237830b43a200eb1e5b8ed713345 08ed90cab1be91c643d1d9c620c3bf85f3cfe1173c7cadc37ad2dead9b5ad176 00ad99b49db1adf38784bd79bf313794e45de8dfbeadb26dd8a2d475b49a6b71 37f121e481a9282144254a27b07b031e44bf34a47e2ef1bcf7b9b8524db52050 e00d06925df9f5236e69fa2a78b78de859780ca4e8345d50efa819179759e483 a4f6188c99012e9f26fa24abbb7dc87a3057d1bb4b99f4f384e5decf9c17c36c b5a5e73fb99c679b4357da96990a6d19a881e12922f1466958a4daf9af573e88 4a52f4bd038ea6c046ac554f842f88a654a055e5e9bc18bd6172f78dd2c97cb8 8efa9ea083dc3fa22da94cd582a3fee99d6a51db50444b1aea61c4bccc2b6140 ce192be6d8d50cc467a4b520368ab399e880b3eb4d93b8a96bbec74070e7c155 f22f7b08626b947b8d6017b846b258c75d55e1098d381bcfa7440d5ae51ff91d f11cea7db46c8bce9a69f5eef55f9a4131bcb465ea1153c9144fb698b1d936e5 b134a75ebcc9fa5b09567511151c8cc6cadb20af14e11c040f19519a6f613102 59b74ee6ed5cd8ac35f7dcd3b5b3497d391a3cfcbd21afa40a72713cd45b23f1 7f31beff2c2c80200ba74222781dc3b00d1cb9c2a92d0a0deae2032b8b6ebe43 acf94b13dfb0480f64d8f24874daba01d93f09d2d29be66a48e076dcfda39980 d31da219eb8bd9b7ebb4d6723adad876c59e1cb458f66a11b8b1e494940ec782 0f493b671148ad3cc994506460ee02bf973068429f9f1073d2cb09096918a5c4 e7464582dac622f032f7964927c94b2d801f92464c59ee34d533b474e0c4c568 faf9274a260a3f80554a0e9b6015ead2b3c7e39d9d5041cd63f834c5af85f398 424919660a91ecbdf5dc035759894703844ee28939b9eb004ced281901dce148 08df61a2481c760147c0a65a188ebbb4c0a3394bbc7850117317032ba5f7a31a cb51316d6b4d9797ef4ae30b6ea1314aff7d91e1e8b0cdc3d56649b9037cb193 fb517591cd9ed330972170eb93c3563848becb59e905f421afe8fd8f37e5a1e2 9cd6bf970dd2da5899dc28da8446cad1bc6881cea822a9a20debf919b23ab20a 103 c4005b7f84099af86d99f4c4d4d88c86b1d309cfb457d2836b09d6507427e205584ceb61fb1c4c6a9f09042fc06342c94f48ccb65f3b33af21a5ff4f00a0bf03cbdd1e1bd5097866968e0e30ea8f5178ce5240c22c8a974e08adf8d0fe3ae40af32492a7a679b89978cb637ce74e7cf2f71f206d504b0f531ee50bdab1960209c90d925d11af2ca2e502fe88c91e8765d05ff658fb23e51acd408e3ed5542b0a38ae390169c8e8926503662f2a7255ef2741d0d4fc4da24b22e24d4e4409a20533cee9a8b287eb4e7c1d868f8efe97cdc470401b0991df22ec7e46644b3efb0dab6b5db335ad574f218251cdf9f2416f7150781ae7b5697b4064290a284606005d93dcac56034235ff268ea7848befebfcff1084b1016505f65dbc9da14a180b92a0025050d4a89db2dabaeab5ec1751e3dc2508068e22f4d213afd6d590b80959a971ff2425fad3bcd13992dbbdf9785305cea18f04ce131770b4d2e28dba02a00e3b05de5d0fde61e63bdb786ade3ca58947073bb488764606b3ebd0600609e9d9c0adaf2373024a69ea44acdbbd9a84ae74f25488df0be2738eec3409b70045ac54ff862a8c4ddf5d13e95ad3898925280c2485bbf589dcf12a02e1b20a08f8699b92354e4a384cb2ca5fa840c04accaaf7542b9c049fba901a262c96b60beec5d4c2c6022cfa86c49f9e35532a66c94aee232245fc805bd3001cdf05f107994eaf7c4d48018ddcddf742f73dc706880354e36761736e78592a62b72916009e2f80ce8ade0dacfe8655dab6e1d8d4b239e6779620c613778c93c066c6cc0fa61c8d21e5285eccadfa5726dcd0f2c56fb7188ff06b0245ce0579ca01d5e004a9ccfbe6c1e1950cffa962a60c61910dcf778ee4ca227011cc7d7d9c0cc8380ef863d563834a3ebf8cd61a2a399d83ec0f5eaa5c2ec8f10fe068e72892b74a0d88a34a33380f16bdea2437889accfda618f3838ccfa393df5af6d97c7939c20c2224da8f7eeac5107361b7083caf977c138dcc8bade5f68d2aa2caa075b486091d7969480ad8b535041b2c93c29dd893bbb39ec6eca8242120795e6a8aedc10245c83d9f15b0916326980003eb60cecbc47751ce56bc9df1dfb63959170726008cfff9ea58554992292c866075f5778ef572dfabc69a82325949eeb8177a5f09e9aeef78e9fe58d6658d18275548fd0e147b8d60d6e6a577265994b741a3cc06f3c03da8f869a69778d9327d094a5a41c06e25e49901aa8b194aa08660a19603767b339e28050f4bf2d2dae2010e8ab75fa99a1c3b29253a8601009e17168c092ebcf4151f2d51070fb2fef8943cfe456c78cdc4bb7dda09fa768984a88fa30593198e31fea0a546d46f22c7de0c0f6dfe11226f27691deb01b169adf587530f3bc9270b9e1d78a8a67e4b9d70d9e12d9b5f692bd108a2644b1be761729ca40cabcab2cb7bfdea3362563452db9ef7c884a82d0dede94538f5145f313c7f1a0c73cc2eba8c27fcc0b8955d920de6bc8c92ff3e4e7ecc0d1a1221d9407e5aa1034ec66fc12a939a0f2767f2af343a46852fd64c850d5fe82af921eb60840f320319de593ba7c3ec9508c70172aa3b9e0bf2927b778b42b413920fc22d4439d505b6e6469fd6448fd92284c69f912ecb7f4ef0ca274ea68983cf4859d7bd847803fbe21297cb423a6dae7a947384944fcd0c510e149ee52a751ebd4ce214b82908aac302a458417da82e3af5234c925df0551312615016a197ca727adf6e83740282039557233a0e92788b195d13bd59a6b348960dcfddf971274d913d7166a1083dc6ee8c25f1510d056ac0106932d9fb49b48cb1a059e70ab14feb18a15455084252a94d7520172a28ff09631273278741808316fbee95fa1a975e5070281705867fe68506de1bd459805414cf2ad43ced52da167396d1a5104297eb19cce901dff29dfe5a9de9e56f1276d842727b6a1fc2a3b003865b4be8b6fcc44b31990bd9f4c703a5c1081168c57ccb92e969272294d354050d3533b51ceb1c9444a009c09932b58c6c798abd336c413de060aa96c22f32baa3dabb0187d5b45d69270359f61a9832c53ad31dbd9aa5010c9d6954a95128fcb64ee5c4d7d2397188eb0d685d5e4b123494f6094f3fbe103c6ff75e07ff6899b1cf82e17420840e049e03ccd1e9e3849d1e02c9dd3d17b6db737f56b669bc67038befc638b1699bf7240fd7f62de49cf873da500655c3380865c72905f4e58036802850d5d2ae883d220c7c222f15cc7437d37d44dd891c20f361b7d0e1653b3b359d504affab6c25940089be406d304b3cfcfbece51cb505bda6b4488fcc9c9fc7da6babc9bdaf0d2d09447adce0101b60bbd76e1c0bff8b9ebb25932907c9e5e21e88ede4a884e01e0354013f067ec762031c18d4ecacce884da16b7ec49d753e636c9bee70e6b83700892d437b7b6970de0cb3f632a92dba085ba5bbd8b2c02b09a94787daa3e0c90ee4015aae33f414223c3f5823b4500b517bc050dce4b6d8f8f17ed44d8322300b30abdfc14942f17cb13e5da605266c40ef3b386faa9533b8d042999f36f68e0b7002048f4bc81912fd84ceaa12fcae1957d91fc9195b995ebea3487a88997903eb932a8bbaa1df1c7940ccb6369eb47737e95d1ffd34ce9c51488db480471c08fbe050b02dbda9253163237f14ae7533e1b29a39fca035fc1f102a80a7d4160d3feae8f5739ee867d319648fea77094c707846e4541b607e4dbd4f0bcfecd801a76606bd1cc2edd04bc51217f693e873ac4be75595853a402e1686bb5726790a0c7da0075291bcf33214bf86c9dc3fda63861373713abb9490d6c3c7b2a1970a38edc410272942f4758aed302f2184945bfeb26e538788116bca0133e5a79300be3170da7e3544577de06f0ae3746a9e5fabecfe62e80d7c830ab081dba8c1078ce784d26e7850149dcfa5c4be93a6c8621ee9fe02029de2813c5263ec4ba70bd04b6a9f852ec02e101de57b8a7ddb6e7b4e812722c9e1ae1845ef8ab78b800351b4acc41e2160825036c58af2cd05166fa597281eef026c647c3acc69b9170a408783f88f183cff88549c049f3c54f2a29e3b7891aac7d1839346378139c505781b02293baab8a2eaf7c40d11e684e7c3e8e7bd04b5b376c663d506a42faa0f2352dc209c724c6b45bc1c7491065864df2c7948b7cacd0abbfec7597f930a08046a347b7e18089e75f50b65878e59a3ec8158e8d2623828215dcb45a97b410d519421f7367efad7d0b6590c23b495b9d5f751ae444e98153e7929cb75c6ba0b86c63e16abdc3eb2b0f802d8834dd66c36a2b899448edab4df2d42bee0ed7001c952706f90052599beae04b67833a556254b6c505a92052d4bc653f1b7ed2c0e2268b852cdfb990a561d98d1d621245b3ba56f2317dffc97b2a548ac66df3305e51c094fa1ae62ba9cf528bc831439e17c2c707dd900982a1e873217b1ad9d04684ed87c7a14bed1b3e314cc1ce27098d38a9f985c2d575e1d73e5128467ce081fd1841d5dc5d495f2e559032f7136d1f9c2c31c17f9f54ddd0419eeeb9f6e04656f902111736211a21d464a249cff3c7f60bedda472df2525783e2711571a05315cf172b4c1fed6dd6551a068d17bf71ca0e65a945de61530619134b3b0740d335c10e604d5b69dfe3b4cfae2f75f0e915221a7a944243dfdbb7c160f6e350d1a9982b0c172a914480550e778bf345729014189a2e2a7fee38e8db067cec807222476bc4ebc7b409915b9fcaa2dfd3e3011dc50443bc110053c7e434fa47b0649f66b5780e8c9e07f8cf1eecb416d6a078dc6b5e7b6649195552612b91fb603738a109038de305169b62869f27422102d29743d725e72ac6c38db98d5b543040ef47039dfa2b7301efa06ca348c69f3d0a1343cb2b81a9bce0e5e7812631302545adcd437356948ed0440efb14476623608bd17fca782b8df715a18a80a8502203555efb76dcaace047db7b58e14a340a4c40df976cdf1a611a800204eb3d06dde5af61a81c2bdedf93a0982cec2d7e7a2e06f05b0b29df58d22ac0e3f96b0c5312fe5e33fc0a2d8889612765d7a461c26ddc95b6344d6af2f1f6f47b895100ef828d42b6947fd3584b41ceb237c7e9d58b81d95c2e8fda86829e7caf89390d8b7bb98c8064b32e244461952823a7b486d4c77c84f6f97cc871bb397369b40cee4ad48760a76d265c236fa2033fdf937d7cade969de7a63c93363834e1f9f00a5f37a535dd2c1ec403f2475d91464f7bc5caa3f4d7474a30c2cd67c22515007173f2aa863ca6e44adc2edf8b70f6b0112a4134a4c23eb2a06baea34befb4d0e4c6f879a3fc297ff740310327926312228c24e3bda34b1f6d96c97cb415192092338896c126659933003f85bcdff65572fd081c4507cb56b138c488b9a41540835a8368cfd81e73988b1a68c7b1c3f02d3f6f2cf3f72fa4c2a5cb312deb6a10555075870dcf71bd3445f6f234ec13c07fda4ab1b5601c7de4710e9d9de7ed10d0c4ef6661650815e44ad4ac7cad3fa30906caaa9eca732c7fb834fc06f768204039be141561016e8811a36244dafd4477637fac2d8739c4b65527cb540a2980dcde5a701d76c311809cb780271fa03abe32d6a29194432e19d1ccc5c3b821e0617ad6a028ac4897c698d730a9dc9e6b1f5214fca28b2962be458c5f282809a030b0055344efe8483fbe7357e9e77fd2451a524e2f5013c86bde4c748acc6d60525c22e8581a116629e190c6f216d42e47b2deae5bbf9e21aa58066c11865a30df9951ca6d8a4b94745b8042ff09d3cccd4fd5048dcbae803334f81849e07510980a959409402ec713d84edf4cdbafe8dfac332615004ab6e63bb82273101b2027785f7d4014a0b380deca671056d4fb9fff4f637e900ed312a2896fb0efda004b6abac0a75694b3cca9cdd6240edd6b90d338bd752880f4d66e6a29f0c47940053100625e39ae536797a4fe909f5b779a4ea7fcd23e6ce9b345f2d86a4d69f01829b711cd78ff96036fbc6082b74a41de3953080fa7fe34976b0e3318a6ae3075063081e3f7d69721b08b18fcd9bec5374cc049a12018f726d150087b36cd60d513af6a58e3466c8545e650e4bbb7a6e95a7596a8651cf3742291ccfbe21a909321081dc89d0f26b1815c04c437d304e03629370974f109a536237ab3764db0491c8251627f14250e62c371c5037eeff2b264ad4b720ace967dcd3fd7a0bf70ce9f0910bbd16387cbdf2927bab2f77859d94c5988b6d740d2315f9823d987d00b4dda02907ca773852999cdddaa3e74069aba75eabf06602ed315c341993d3090dc2b077196cecc070d99f374f271535cddbcc4de93addcedb3ad21d370359084bdd3df0b5ec967e2684c24dba4e50fc38a6d14aa8fa4490341b4c993824b402ca3313022f243268d53a5628b894cea7e8021ce4312717d649241319f04fe90a5e91f8ad38e9795bed6536655af386e299b45e7afc3198b090d4d04ef203cf09ff64995ebacf67b4f40b6cd16f023c2836b72e30700fc9bd276163227fd1aa0062fe4f494a855aff9fee649872bd75d4a254c1a1849a0f6792ecbee041ddb60fb8f42600e9bc83a81a88b36f55fc966fbd86dfc2b8b9876811c36d830c29a606e6f79f67842ec1eda59a530180eff96872eed57d50d5c223d7b10b07d70ed702a9e7612b81bc298554a16b86ea9fbf491f0d03ec4c63e9d88570d10ce78e6600adcf7ee7d949cb8a51277f7aee1f9925c1327870161ee54de401a89b0e54e30bfddffe9f5a45e8e5ce2c406d8251f8ee8519c0cd949f189e2a805c8681e05808ccde4e1f20d48efacb9f407bcc2f30130e0d94add8c89aa02badb1463560ff00aa5faa03df299ae067bf972a1910c7ac2273281b3cdaa76263eea27f432d710b206c19c819848bc0293a4f1b57efb5c5d14cd075772c3e2c0f3bc86551137c062c9588a7e2f98bb2f6328cda55577be501450a737ecae4155d031b5b4c616a09e4b596583962a7ec631e28c49e4da6c7c6d45260d74f128ed7364a54fe82b205ae56b8c6fd079cac3886abd2260aa96ea1850320c3eb90c90e52b79d4bfd4506417b8cb3335540665a6c35131f5fa5695a6ba89e821ee622419f9c3448172906ab19c61f2f190166419a79a00020691da7eba2414531c5d94f6c2b9eec972d044b85f586e75ef500d84accfba411397d141b5c0898dd0b5f4e240106e4b01901ec315d143fee6cb7ef1129cd814d4baacd49e6ad380c58bce567209ab449af03e7a1d392ea99c810ebcf757915f601383fdcda4ea584a05e0b6085fec3305e0ff11e11e298e7bd2db47d8c40558bb3568fba5503703222b8947ca69b8c63300c72e257655b8a905cbbbac6fe9646c61fa59193358037201064e84ed8ba6af408aa9bf4eefe93893d7190153524640d8ed37adb397f0b14067dd1b1f98cd65b085e53f66f5d597d4c467651826b72d5e391c7066a6559375271d7fa6f12ceca0be2af57575eb9d729185ee66d07072507f37e84f20954d722402f3d6a88418704d78ed32097d3a1868f3dd38b1690c6dccfe7c18085626dffd8899e860f19ee07d8650453643ba6728997c9228077e63225ff99019d6645fbcab469039e5a1a07c63e611ab552b6ddd6d6bcacf5bbdcfa08a2a0a02d11ac2ee35dbccfedb46c0c1022ff7873a3a3ce1cbb5c7633005f85c352ebe3fa1e6aeca55564334b776f0e75bac8f1366d8debb23d36d306a47a6c4c1a5e6caba7d07d0c07fb7c90729401a28a7001e1be3ef09b597fad5bbeffa10cca5871ab50797bf58bd31425d40e05cc7035ebcc23cd214f9a0882016a798cf99473f1a63bc1d650f5da3bd2211107779497a9ddc7bf0801413e600fe02c520cc9647dc48917bd2e0d4c6514d678071415954a00064f863c6ef5981bad9a0361f77d0920616150c67c9db21b5f7601af40b4432d6ffa8df4a1a1a421e0b90dc29d10ecd50821235fcf8d0077da0f06313ba80169ea4fac296376a43ce092d73d597292548e3c9f0cdba6daa27c230453b4c83109c9a38b468a99185261adef5f60c4521e168bc092108abd38531308548187595217a1b1bf20d39b0add80fe3513a1ca2d4ae118f69dc8c2433b230583783c168825bd87feb84a40cdb826a66d85ce7ff06ae07b828e315da385a903ab156ad4c68ccddf1cee86e01c9b7c0144b311dc09737d57089cdebaaf7905056e809f18ee6130f241ef59ebd6252fbbcc1048efba218c877c1a55ac7945640671e0ebd27ecab1ceba6501b8878c9a7c77ab06704d665cad597f0a4ad6bc4e046bd3a59fbace06d3f1067b18307d42b278c6ddb961ff168c205c0f4f872dd10fa077b7daa4fc25e2bf234c1d94848689cf4febeb7dd0817d56bfbd034524590d3cd69e780fca7f49ba6ceb76e8c1889a3c2cbbb0762bc8b434854c0ffdfe04085adc5d7fd8341e1d3f5ecfbbdab41cd4c80c72b3c6dcd8ea9630c2221c0be80795e46530e21a828f26cb0fa55880c127b059b968d910dcfb59b044176d82ca0f2c67ff28e56218d44896a861228e769cf2616d0b1ef07a598cd8a940ac4f4f05d62461c9c14241b9f5072490b9bd09a4e95b9f3e26afee603ea6a4ec95dded0f1a2538b0efcff1ec808e73669c36df0bb9de2a7b25470b28580807fd7b685d0e1cedc076ac99dcbb166c2349a6046c4d3e8aa7ccad710a15d6f7ddc461b7710182402da2d909cf2eee7427ab38b0cdcc9505928a76a2ece24261b9e47ffdd60c824e4ec14caa27c5f185878a5b5832bee3cb90b8966f08e1df25d3913c72c505d5936b43051cba251785724f3f21a114e0f184d3ecf62807260b2ac86fa8840f4f0861554966445394c4082905e5d8da75a3f1e068366e98312088a93e111602ae3bf228c29390ee8aa0706245750336bf77ae40935c24d92e5c4815cc36560d7926e77238bb7aa2605ccfcf4d2399d36d9c0e9b8c1b9e973efba8e1fbf0c502c6964a26ba707ba9e743c5f05dcffd4f2f13b2bf187cb12f92a2a96c68be9506b79903e78dbc97b451f113f9827377a33f96a89e05cac3d5d7b9e2f3a68e610dba78a42da14b4ae6d4de224f535933875e98cac72a322d83897a46de2601a70a0935b938a07b2f1b3c3229b8c213d7a8454645de3b4c0abea05b177cb346520b4b57ab4dbd112c902c295d5e9c70caece6e848c26250e1bde26147370089a1028a385f4cbcf446f2e4eb50a640a6f754cdde3154066d657939d89560173120045229b5ad986b8da057649afadc8e7d32a01974e40ac0aae49b81f823dc3fbb0fcaad924dfd5d020df02dea72c5d9d6d4e1301f7c9031ea2115e88003b210b60f0e1557463125960487477583bf42678c1e36b5cc21a57f562a42e8759d2f06098bd1a87edd22860919dabd94a782a2edd71dd92b90e672243c8ee039d6498b071efdde3575f9fdf02edbbaf1b7e4543814aa416e4005d3367dba7777f0549408fdb65bbf538c332c39f64ccbe47526d3d525a4e5e8418660be5a68c29308660cbfc3728d762977266701f873128e8c0a1be969c1bf3a6d0146b76d79d6bfd70940917542a0807e6ae41122e41568e9defb378aa97e81563357a811ade702d60adf978f3cbaaa5281b50dc36f6cbaf422dd3782fd2fb6ceea22dfc1daa58df00be4590cf1ae0fbdb601913511ba4001340c859bdea4c27b2019534f3bed911c0ba285b4c2e86cb7bb6d7a63d42225082ab086da5330ffdc31c7110cd803fffa05a67f387eed0e2c8ad597bc70dbc6256581a71fc783c6a46482aa337e19a18007bf9254a3cea617f52c0914ab8c55b3ca424797fbef9dec1b1968f8d3be700d044c01602a7fa630cb1333d93bbb46a06bacf33b41fd9536f75c9e2cab52a99e077983cca9680c838d16ace80671ff7a8187996ab3ba728967f0be634ae4452905ea248df97cbecae786d8de14787c6d9bb50cfbf0a0941e181d2f8b2988538d080196b32319c11afa12a1fd670dbe1da4da1d512f16163c1cc24c81a2a683e109c0855489e7339e0c02501454958273b551db94e2416e30585059f36f7abe75060b55f4195ead79c0b47ad74b3358a05f4b8f48dae88d363aaaafdc639ee36c000b634b766f638a92137dc1bd35e515f4faa738285f6e62aee8e1f0b479172b099dd621fbe820165aca0c4cab402cde02666486f51a137b8c9b5bddf4c1a70c06798bb0b105c5e4ca6669cd74886cf41542419b4012567fdd220d488c4bf6d30d8c8682347026a1aac889c74c31a6c23287ab862f7174ca0b95982fbf465d8107045b4741c6cabc2a6c804d49ef00f82ae33860249d23e9f35ef8a9d09c92e90f07b569259ed27da7636b0b515ba6be18bc9c11066e26808a3142e0cb8b5a5b09780d3f92cac1621d3a78edd3027a191759792ed3e2c3346132892040a1f48b01cae02f2fc732fc280cfd11f7307020bdc5bf3fad212919d49cdfa886eb104c0c914f91ae618347b43ea84a7dabdb4673f9a779e61137640c8581552e5bc14f0dc71b08f4ae16ae354b00939ff9b9a934a96872c4ac5752f0a4558322364efb08a8d6e8d473a28efcc482b5ecb8e82fae20ff6dba6be658e31a1ae789de6e6702f5bcdd837241599f6b674bdd17e07c1b5bd3a9f49b8ee4e8a071a1f3049fd5095f799898915b64bdf7b95bf6107af49108e37dfc27f5b97f9dbb97d36cf39605a870cf8ab5b2d6355c39ec8d707fc4e0be499c743d7ccc657a3b6abe096b7a0f11cabea39c3a61cb1a42c88d3bc36d26206b13d7c53e497726e65f454f18ae06acd7ebf0d8bb2f63ae1537373ce69ed9c65b4c46b4d582cd5471dcc9d285d504fb0a8063614fc87335ef44f77020a158b53097d72ee85b19239fb866aa86150ae870cd6c1f8bb86d443111d4248b945eebfe6246e3cf17fd5b13fee1ff35b50eb5f58f2c8a36c13e8447075929ce82e509ed19e63d347c144a2dbce87d33e40bb53e7cc2a31154de4ec5ae76146db2c3ae5bc8b53a472625a047b986ec305007ace4e9309794e215a708dd43932692dcc6364c5f94961221fa978ceab4725c004925f96dc6405ee85d0f7722b3075360f2aa08994947add14ce08ce2cf69d4043db4c8dac58fa3e3e13a80e9e1087cea9adb228b87b40922e8bf626b50a531056b7c29a9dbc7f2c3c34cafda827cfb8d1a29608f96b629bdc612ba08d3c1f407b769ab8f820adca00226bf94f10572c09c16dabef485574b27d4a592302ba40ecefdf9e16472596adddd35539fc41e042574071fd579ab487ab58d4fd8d8b80673e3809fb09e00ab1cdff8d628df800723db33095e9644ea0b078848051a6c0815a597990c66ed58a49c1a882f6fb98beb92c5a4f95263f99acbaafb310ea10eb572d3e7fa254af28cba87fc73d12696d52a9666e1d74b2618c61f61427e4e0ab06a53fc1aa297e1c1cc97b85b3bf0a76e1ea2d84d46a7929cd9c33f4ee5220a689c0b99f1b77bb677a89d4919f85e152302050ca441234d6668294e8554bf0bf05c12a024dad03956c3e71b77679dfe44e049f105dc37be6f07f7ce034d8a038cea4756c5574ab2b08af331da602b123e4c48ec5fbc79a1e1619de988bece0ebc8d4699a8342d74aa51de0297ae4c0bc372ac8d5ddf682cb6e8c575d9ed7407071e6c4844fb93ab55b8216d67c329ecba25361f54ce5f129ca7d95af68f5907c7dd785f2d44b735078d9d85c23d24fefc3c2fbfb73428b37065ed2a37e4140745810f5d09c84029572ed5808d5328bf647a9267493fe6bb986d5dbdab587e08c82f7d0541f61927688af3640983368df5fc4dd7f12406f526622688477c5a0a7a999bd74539b9d8394039574adc34481b8baa55194ae8ba963c7aae52f92809cf7c4e08bf94fcf0519aae4b0d9018de1c5d54b74093fad40a1e379c6d8bad01524c0a1c6140b8d0980c6cf202bb9600dfa914e025fe1e60c7a4e81af8bd3b05c3892b87bb58aeaaf3664d01e6cad4c1e699f50842b2235c94877be821f6af08ec653bdfaa75288bd30eb8951ea1d3f1c07e6d95e153185a0183b9519d0e690f5226a0abebd8426f3c8b81a2ef59adabb9acd956fbfdf1687c2b8858d4624707c642a5b942c628a46be04015703a0ecc6160b94a68ac59eb428c7e730c04410240a1dfc9c2d4f86ff0b515fa433e607ea565f3316e356867dd6f284dba6ef700b7bf16825d6e87ae1da1d260421ca3a2402da69511cd4b55dc0e276b4ca61504c483e022b236f9ccfa169e20c7b799631a1a8144245453b8dfd0c415cf5c0301d7c954434150048d2db19e665c4eea2620bd8c92eb4a6f0de4be567b2621670ecf253972dc86805691f520559edeaa18a3158c145f73a4174b9c5e14b9139e07fc680cf861aab7e581ddeadc5bfc25f3abdfef060341138449aeaa4bf8844c010c8aa2db9345a4673488ada9b03c0732d796f74fe391ee337b9d391d126c45035a9c6285292c9dbc262c43d6ea067ad9b78af6ea05d7e372a0c3395199fa300669de109c9b3a4f73d6feead31dfd04aff779d51afc58815c3eaea53fc6312202e8244fe07792aeb38a23d267cb90f0b62f871d63010ff351804cee6a4245c009d5559882150c1d490fd72c5ceafadb96518bb31dcd23ec7b3e67110e815ba905f8ba7354b08e9a9442552637d6196ea82d89d85c55f8a2aba141883e6dbf22043fce50360c6841f821550820e178d7aa65044cf8df8d2e3ed0165ec0f17c280d55ea15df2f7591c9b9b5430bc48154966caa556b124e9c82a259b54417afca04024a2d4a8404b72b42ed3aa8a0d58681ded9b4c79f9e2f7b0f65fc0f067c290ed7a813cc3ccacbc6ad3b3beff2e5db5923245ab9ce5980e04fec4afcebc3e5086db8917d93c4c36ea6dd8fb832b42ce017d242979f9622f4dfb56f19b710c50c86fee367f08ae398fa12350cf6ea9e3f02304ea51a4480f0b6e5db879912d70a5083d7fbfa72d9c2312282dcd14bf6d7df02d76217a57057c6c018f00b720406d586aea6b712aed2153b71495f6788566efd13ef83af4b97703852722696450aa5c95eeaf7d233e09a7494a622e976a36b2024152e3d2550e3c16481cfa51f0a0251dce06d84a8356c4f44ec2aac2ec3a6e318f91154262b9a66cc83d4524e039bf7661be91a8a74ee96e6667db9ba9b5a5b77fb3fe01c942441027e6cdd010ca2f460d1a7b80b60fe866bc4de84d14891360afc0ebac9b712f851a420b2510c8f6932c05d2664e86ef37604ce4095a2c24e4920298e31188f0b22621b7b010eb751a484c683ae034c334c912cbfc31f06cd1492b03210ada53fc03c871cb6020548c5e502ac55873bc1234ba8424057407f418de57b5e1e65e65e84befa2b071ebf7b5b205791a716c24d60fd7165e25ebe34e523eefbd6a7dd55e6334d0209644f2a77e4326dc4d650e42e2734ec12f943c5554d480e206d4c02b326ca3401cc34f1812ec0c138a8a1adc4ff3f0900d0a6e3ac781e514739816fd9b3ebaa01a043d3722064168745ed1e7abd00d13db2f817f341699e1d95a057020c9c9a0c8471f8be4d78c21e0b8fcc4d6988683e399cbc46a77460f899d698e2769ada0ed6304f05cca2cd8d492107ce32328f6454e150e1c568b9cb9ae76e9fcd74da0f7bcd4fa301d47e6ceeb64b513d203cfec1a14b1b0ce766ca2b7b76344d01e00328dc94f66262f4d4e9f05d7ed3f631b71980242a3fc3390f5b2a17931cf0e6020dd405ab2f6dba054f20c15ae5b2530e20fc9d3b5cff6ae03bb2099ad8b6650d47e551e29840c7a3af5592b12bee0ec826c88743cf17319b9237933544fc30040f467150422d493469a8bc7b290419a3b5ac0e12dd82b185a3d689b51308d20660471b6e27392468e6bdfbe1c8e97e71d28f217f4766b392e7966ff2e734ed05e9434ed3865228e9bb974410a30a4db297c1bf6acd48116b9ba6c463a673ed046610918f4fb74eecc1befd3f2e69d3f5e198097bd0c9842a541779f92eb27506494ae483fa7afdc1191f394ed70cddcede4819039dd3d6ef388858050cadb40e5d7354ff9d1ee52674e8660b815551415dc266568213832ff3244ab70292b108d448ee52eb8b47fb5b71aa5eec54f09d595cbeb021e56baae6609348ed70d40d234d65a1bf4f409c859a5f2365083398fd5463ee536ecc9755b4754d69692505551ab8ce1f18cf3c1c176ccd3ed94c53964801cc9f45857303fbc1ee56d8ad00ba6aa5a54245715de37762ea80e11561fdab977b48f489526c7eacb11de664077c831ff5716a56dfb371a87926be80c339ec86f37bce3f4b319b339b7271f70a928ebef14e8bd021f30b9a2627ecce281a0d2c21c240231a77f0a78d8642630d5bb20dae55979d6b6551e3d8892008e2af2f4da750d9ff2bf0e097f6d0f80109fe016be02b7279117d12ac833bcf63c46bb274d71dc3582540d0264924a3ed0b88e1b27c02dc0c2c6b94a01ec02ba6ceee7c0d74cfaa0315d607f4dd38112d00ae7c1091daaadf9581a1d95e8fa16a51690ca9a32cd1067bd4bdee63cbc8f304e3cb7e2e70ea30f9cc30dba0d50c26318bbc3c4353501636a4fd092dcb52030dfb10033569457345cea511310b8ac9c3f7638dd36484040f9fd358568679af08d8c7355b9855d515f47999eab3b135b39e611f94887e1bcc278a8084b57e8e0fb2892c956ccc0e008e84e167a228508b03f49bad890e1c7ed0791cce3a7d5d00c9b20a71b62843ca34c3445aec9c6e76c4adfca122e998a82323ea609a462600cff9a28597e67d3d19a1d9ba47395286ff836bf343a042a6e3012fbf0622bb079cb5ea29833e6282fff347a12b32ccecb2a7330dbe6512be4d4203ae9ce9110de41d3681cf43d0a80fd83709028441ac63bcb940d33fb7bb0c01bcab116f2f0428eecbf87993a73bc55b93912ac8ebc23631c44a4a9006653d035d196a0a2d09dd0dfbcd4f07c56cf47da88e48c2f67d5396364920b8ea9b3ae86a7df54238077a4458cc51589ef2078b3ebe4f8b078b00c8156d637672a719edf12e5fe9ec067b2972b80ec2f9747270748f042dd2a9b2a35e96c64407b4a345b6301f590407d45d9828fd360043d9d0757b8d32a5a5fae234bb43645e5ce4d95146c910e4072b9bdf9d3f555abd7438c20821630e72a62e640a374033a971d52c8612053705262560181519e2822f9bc350514442c58b0761f7f67c5726025e23550c9da80c97ecd16e9c56a8e8e2b33d4a076973d7cab7b9c119052a20cba15f3305007e068da9c16fa464531a47021c6ed27ac7a6c28caa472a306214fd2d6bcfa9d8c50f80edda049a9b927781d040a13ea5446a334056b00f7581f58bd0b9fd6c552a0a2a6c3913903791b2c841fea4321776b647d3c367a710b68ff661de9518646b0834021bf0119e4b2e55bcf4d5e7b04626a92a3e124c8296382883ecd9a9cdc70a058760d77b227eb5cbff83c631346572a04cbf4ea5f101ec578ad598f2cc0b01396f8f7ef41ad3e6a0610436f71ce32a80d2272426a91be8b6899a4fcc6af107da520e92f43a0c0b2617918f2da88e50724a69fb8f08ec39e43ebc86355c0c071ee9deecf828f27f22c72b9dfa50a330cc16637722dfc94869c8a03395131201f61ff990e94e936579d0965a7ed235b093eb1239d9303f3216726153e4b4e90f4a864aabc64cedb7cde7309bb29e8f1679e1d35dddd89914632f28b789747f07c23604cf99a7aea96bdca25a7c0eb1048f0d0ed7c538b3ddd7b17b9d663cc806a822b1b6c20c748743511e29bd6bbb63a92905be2fcbb9cd59141cff7c001800bb621adc38dde844a40136d2e792c775833bc280c2b830b68ea2de1b8b833a07d7a7ed08645bda54ba4301f26e0f29e0576b66c854bb2b61e11f4332fcf7a90f03346f16fc29d2190bd5c3c1e5c3eacb0b1acf38a5d805901173199e6e4fc608da64284c5a840423aa43a32e39615cb717c924176b7954da847eb619fa6a6609f39281f51130ec5175209361935ad03f32f59038234b0555d87eead6fb5a2c0db639121d4e4003caf2e27058e5709f78a53f6cc4b5b0fecd7266e528f0d70e0515845c905b136b752975689bc8604a188405fed19ce6d003b829c601bb6b17020c4ed76cf1c5e2b2c13b36011f308a7cce19b2ffd8bdefd0297fdd2ed96de705e8b5e0fbdadcd83fdb76cf89c23c73b325585a17772a5bfdf8ac93b94480660c2919ea069fa4b0288669633a3d54b930f5fa395f321cccd058a2d7981253000940eb471834db32fe7700dba57426c23dabc2499ada11aa8e5bcfe568c4f5cb0b0c2a9baff4957d45964a7ec37e97450681308f270dfa347943e3691e8faf8e0ca196a02732a75d0400fee8681d2a49cf83e04b162f25bdd825fe058d4493f102a609181b56261bf5d4a195c1f14c07dadd9a9d3ad47e20fbedcca3d66a6a3005 +generate_ring_signature c4f2c0a39e10a7d2881c8753890aaac6ac136088a60bfdb67d9f4b17f5670464 8330fbc47f5ef2d5bcf3109e30396640363dab70a4760847e1ede29a99a660e2 112 e47318b68aa07faa35ef3ae5b8ac38acbb0fbaf3def0a4bcfff10395650c1eb3 0059e481fdff16f9b76cd2203d707be8ac31d48614585fccdc232a1e89841d2a b16f9868b930ad0f327f095f2f58eee17344ef219e5119c2a4d23078f3288af2 d375e7d98e7f286e38459c0a90a742c0e9db87f173609b3e088d6b0d1eb3a71b b8aeaad7d8293c44eba8932b8d7ebdc654d48ee3bb99a2ce9320b5356c363aca 016437d4ce1c2ec84d278ad1151e86005ba0757cdb110145110fa26b74038590 70bbd94b724d740a11d7d4a816c4c87f3eb7bcb2b17004c0ca11afd3671d6bf0 8346fdf809bce48d576e43e8005cbd6290633094eb508f7b1b9c2a8225a169b7 d66885f9ef8d956b5f7f65641651e31302075ce1b5865c5a241fe9eea9b29d22 8314224a7d53944a56d8ca1db88ce4f86a63b4663eefb779cf8153e2561fe761 bbaaa6e1e54ca7dbf72e37893f2154e027f4fa16175be97626efbbdc69586e6c 113bd03d75850ac055e90308da8b18f336ce2f7a33a88403dd26f5252506311a 652f8202dd69d0d86855a85f39f01eb961046f598588d8082ff4b4bea0501af0 24342da5fe22d17ca26d88f21357e5d375dea5e293ea81bd8ddcd5727213f196 392997ed25a6b43ff9f800a04131a81fdd28b839bbcf017337d407b145856907 abcae499ea45e168aa579ca1d7f5cde47450cf61bdb768b74f38684227be9a20 41793fa54c828bd0eb3a127ccbe27c4400055b2e7199ea5c918b88ee0d983314 c57bcceec6f61fb7de098dd8ee46f9d5cfc42cf864306601ab6dac9229efe5d4 2f9c53e941b12af7d6d1f49617aa8984be45080837d56c015ef28041d70bbb6e 16beac4e9d17d70a7eb517f110d8e7ab5cf60b4234f0064a884330e7af8f8264 fbdd6593adcbedb594273cdb4f71d628e6922f572ae7b94d560ad1ee543e17e8 3cc322f308c404283bc3a5476b262744a2d23402b4c8edd729b963a1417f08a6 1cd533cdf11e6c7078cce85de6290555fd75411a556c128a9962b7dd6f4d5c09 51ecac75cd208d0dd87edbafff96006fdcc82eaafc0a6a4084444802ad396758 1409b737766520c2d4789b6d59c26341c7aa1ed815f5674e794d9d0af5d83647 ff676950fa8d97c9c227cec21314b2970fd0e7fd688c64737a246eee1ce7167f ee4cef48d1ecb46fbceb2db3768f26d5d41424c5c7263e5df8dc206642445681 635b8c1a27a5ad357b46ed43f5911da74f20df7538ccab7afe574a4ec152b4c7 8f1ed5f84e8d913a43bf95b9f1566cfc3a067d30a28194f3fca347d2b937851e 1b97e52ae79c274e456980c2930f47174197392c8576111136cb6354d3f844a5 114069c991bde006821a4bf4d7981168046477d02fd97826d812d9f16f77907a b655863086ef74aa89ec9dbeb65bd766bc59e600f2fb91c06de972d813eb2ce0 a0f739a4e1b36c8cc807d771c84e081ea83a49ba98e194b0604d2d516665b778 58fb8b5f2a78e34edf2caec39b3f382d5c5af7e6efc6260e55d139a7371d25d3 821efd6c97ca6faa08c9b14624e4e143e52cde411d27c05cd1ad1c4b126f6e67 b20135c3919f7efc856e39bab403fb72d11970c31660a418b8aa67a427a0b303 fefdcd2c9f62d2788aed73c44a3632486974a7c146d5814229132b136e21e413 db6b3594fc8d6e4086aaac78f29e786d988d88818f175d2ffda41d5a472711c7 07b7567fb88ce0f9f467d8c42f1432224fff4629e4e46ab69026340c6b2a7e43 a1644980560aa39caf5deec4a899557be279f3d9503b973450f08c4dd6e82fae 74eb226428afdbcc7558ada5d79a5b6bfdcc9bb8338e75b750a27ccc083d919c b9239985dec6a42c08999efa4e7817fdab6b49301f04559daa1b80adf42b355e 10113e293205f5847c7c8d8f41083e82f3a525b5f56de04359706e5b473babe7 62156ff12c819c07a5b399c6a5694cf81b3cad3182d9c89fe9493399df4d7fcb 0d88e5842e2a1507642926ee6f947772799e27c4276e84a0a377f28e8787766d da17155ac68b93a53b3450cff5c3627b633b2b7464d3f0a74d9f47bc7a9b63fd 027b33143e2bc160670358bd4f206b7650ab3ca6120f163eaf7d52b1f6419ad0 38dcf4d9c7f711c8c3f333ecd93ec97e22abc07670a8c8658e6277d579923d7b e88323c9eaddcd13f3348bf85f5251f593f034ade1ac3b5309b0d542443a5e37 fa14c4f0c750ce4b52d4811caa95d3c9a7ad209966683cbf083e24d00e1765f8 838365eac90f707d698b76078f9ec1d896590f9adfeb5c0dc219d80b42929d63 e599e66bf44e7fccb2722f3a97df6fe782e80811e957a6d79108dd700d46deaf 63e1ac5f9583e1463db1c0dbea5f5780eea86c3863fb7f2f7144b094d52713c0 f30040af05b4f9d55c2cbe1de1c2be3d0a464a3c2a07590ca5248eaa76947ed8 f293f6c22c31534998d09fcc9f83c6711345ef9acb4799297af1a30ba50889e7 6adf66f4a7655d9e80dd691cbb6ba6b0c34ddf01e00bef4020f5fdd993bfa62e 7413a65c5acb2f77d01af852e1d17d05544d767f62761ec4bba98ebeab91db0a a604ccb842e25ad2739ada5513fa55c922e98a1cb47db75cd945fd1821a8a84b 760744f0d42cb0fbb23e3be8e04860eeff2c489c93e39cb7fd3e644313376de4 d3319b1bfc438b6c652aec300eaba43a8d1ecb0b10ce7b1456732042b0d6c219 f8a118056a6be318825229a5bbc9800b6002522e577ddef8382d272513672bad bc8b2c33bb713ed0d4cbd68d7c118e312fcad230965cb8086dcc60e0f725ce87 1d7bf114a3d4e60c584046097d308779be8ebfd81bc4cc2ed7434f700d0f877c ad197648d3c14b3a127c04a02b1bea5b8dc8e369096a3dd74c6eb1ffd9686449 6d70e6bd2eb0a115f51c5f4a1a5fcfb052340110b9db067916687ec8e5f581d0 5708259e096e2c0f23724fc835626eafbc11e98a8672e36360ab580e8abc23f8 64959a011b73de580cf315a0799bd7f4e24538dc149ffe9fa582fe16a1389a4b 987abdaa231434b344aaf7dbe32040701e1c7a5bd7cde69e92aa17800574d6b7 71e094387ec015e8bdd1684f778d544e0868a27a2cce16e3245863e279d36858 297bca83ea72ace5734a5b734743de740fe5d17f2d9a61afc28b5d3174a4d899 4b676cd079de1a9222bb783ea75c17ae3e16bc08b3618e4ff201a9fe849c042e e927115998105d74e0f76b3ce1c3a46b0fef5c68f6061949c3b3fe7f21a321c6 95438c8204ca32bd089293645bc0eafe8552fe14ec29a232fb1ad4fc619932af 5b8d7ffb49e11b950d8cafae7bc3319c9b399dae92de13a01ec7226a7821fa65 a1a64ad18b13235f8ac0c33eef61c152050f8ec646f4f92d7fc9848ad59ecdc0 d728a9547eb0391c9009cfc4ac05409c42fdf5609034354004f0fa4d5441878a 784d8afaad3bf0437067961f1ee47c71e609cada0b97c636aa87a4da8cd640da 9e562362de224ee6178006c3f3eaa0e211fa9c0b9e4a909659e7e0d2a28cdd48 b31b9f4a7e801c99f98d1b4523457a434d623afd8631163f7ee99e14a7b6870f 9164ca548303e3f8e0ff9614e064af82f8cedcec6fd76541210bdef127eb450c 82d00ea6aa39897b10534f681a739925d179e3c25f87d582982988ff8813d4c2 c3cc009f53709178261b9fd064f31bc3397e4652f37db77717cac41430f68b0b 3c6a52119f73f153c9b740b10e160e88a878a15a685c6e426494c1a190bf2ba8 787fd20038a29141633d628f1d9be6c78486db3256641d4c5bff7bc6ddf3787f 5f8f4faa52b8c26f56831f67bed9ceb8a3e98243b5d89944a0e21e78c523be86 ff57fef6e81f2c1d5ea0f39579e30a2330b7b30058d65cb5100f6fb91db481e4 6faad18ac496e6517c09c2ffb99ef74ee010f1a70016477aa92f04f1272cd725 8bf8561b844beb409a9603af71af4ffca292776452278995a2768f924069132f 0573f9fc57a663c0ff3b2cc35f4301465e2c5737ce086d2159adb28100a9d8b1 44273327e8d0447d0205ca23ea4991f0167f82a7dc7dc9a30040eada716d2855 223250e4ee92ab39a66d8c5d10cc1d859f8e129522763c03afd9c1ff982c8d3d efc9951e85bd27fddc0dad8179ad34a125b21a24f0f27dee656e5be965f54dad 2b31f9237409aa3ddd3789f095404bb3bf9557c7d1919227a4d812f2eb7afb9d b4d5426a38af058bed01586fd89efc34fc60cfbc95d4a3a34c722e05c5457c1b fa9cdd9ee59c34fa855609a835d3bde169d545ea653702f0a9b7645c8a8d472f 377077cbe71528c3b0bcf76ab93ba7451da7adea27d31234099289b05b22ab1f 8e5ea1ca5ff58b0d5e73e959b45a650332202e6e2c182672bdfc365b4b19f550 260285a632a4ead67966de806b34a32cbdc69352ccc25299509a3415bd6ac2f5 16c8b44afca503068a5117a0fa39b1cb5866672ced428ae720393e8140e4958d 6ec0f1ba70f1bfd60018d9780c58c09ef50ce619155a91fed21516d72fa65549 112cbf0a773da739a875b800bca212c1dfcaa8a7b006bb0b1cdd72e197dd5fb4 909c49c403a2533b78a8dc96119852c226b8a93e302d9a34ae910a8c61ecbc7a e16ca70091bf8859d39a34dc2fbec79f00888832d287e0bd0a3442a6067b7206 b41959aaaccedd4c7b4ad47f694421a9ab5f18365cf45a88d85dc4076e87c649 aa268761491f71d9cc3b911ea0db0efdc35676bcdd5194952d9d0f1dd82d18c1 cb1fd93f7c9844cfd85a4c0f89f5a41793bbc43cee813fe3d5ca10fca6538b04 9aeabddccfe892522302b7bf3e04f4b94773b0bb89b28f6f19e24220495c0caa 81cdf2134d20d8536596154521f3cc259e5b917c9cc4373c6f7adfa0b19e8009 78154a42cc5ebc11375cc35cab1a274f024301edee8fff0c08b5cdb0dae37c22 c29ad72588fc320a019c6c102e50ed32ff4123df1f8417e0e3ae315b8a6a31bb 5e053eb278d157296654a846a072b83224dfd088d8db57abdcfd8de00b2a8bee 5804ac28bb2465a4a879f07c063dfa4cfb2762fb9015da611c9b36fa62b10c5c 314e8a0ff8ce6e952150be70fa590a975e6629b6d6905e3f6233672af926dc0b 46 28ea3d8c5f1bbf699fce60869ef4ba24c3a92952ef89b25de660884ff5145404d6e87dae6a06b41a16cc020fa0840653bc7a40cbb248c58383940856809c9e0c09add695143f7ad570b8c5190355e5ebe8b2a576b98d7968b67ea0e5df6a5803a263ad7b3a2a9d9f0e51bc97de6d7ba61410fe17652da30e76ba09048c90b20369e091ea6c9627e01d0c8eb87be56123cb8c8643ffcbeffc5bd0c10078cb950e13699713db4efca2b055c2ebf3bb08d7b1f2e6911b58668ff34e81b1e44dd2023e494438fa17cc3d7e2573149a648f81fa190ffc06b9c38a4694d49723181104520c5496eed090e0cba75fda6813b95ce5a07803e1cb7666246783ab3702e30a10bffe445304ee6250edfa9c96193f35763b9ab21eebfd78d75a146ef7b27c09e6394e928331aea229cc0957ed93bbb3828370e7276674876bcbe9a40a4a0c0f9678a22ff7a4d289555b0b3a46d0a2923b1d99d06228f0da93c15813de6cdd0a99da82ba4172a6fe8896b605e0eb8f5b156be2af8afe083258ce2a38cc263305b521b2dca996c041fe1e999884f467aaaf2e5678941074c05a7844a88f619507f49e0a88ea12128d9ff573280d65470d385a4a14c3b461257d1e6611fff34f0b5a61d745cd34f6566089203452498690faaf2392bc11c376e0078eb16d09d3003f2b9d4937fa80a9f2b41f09f0ab08d46d84dc51080639f4d8e08195b03de20b0a9db2cb9a8b33ea4bc495f90ce781563298ff78ce77bef39ea96e37d66abc0e2985c5bdfc1782d4b6a351da491ae3303ea79278d07deb9053c3ca5bcb05a00a7fda7e61e1f56bc195926b9fd767a7813b3eb0e96d3e4d8c7b44945ee58a8b0f6c421ddc255d106541ff9505fcc73ce198c5db718426d207e9965c2f665a2e0faefdcca951c06887238fe0f63c67f38d3caab59cf140942336522b7476f7670977d1d2aaf6ad40b177ecbbc3e52d4686401c4125c5fc8afb2e7fd21ebbe746050dbde8816924913ad82e86ee96e655ce994bf9303785712248812282670d0e06e4a93bef51eddb447f7a65791d64616658a5ed0db0322e0f2b24a1e1b8dcce0583e6e0f87b7f93a5c2011b65f9924f59bbcb2060a92e36923c6a9d13b2e665089eb0dc6114a685fe05c1023ae256353f7c1ee96b04a578704dcab7c5e17acb087fe1ae38602bc3ce37d402421589baff5dd56d0a0a66f3df855697f4c888c20cbc3172f2cfc795560c3e5c3823c7ef1ed9e1515ba368ebff408bf6c3371a1406255d67bc70ec69b86e9738c3c26c4967fb1efc493c8cc04bb45558a6ee8d470f4fb720eff24573dcbafb75c533b54a4729912f067a35823a56c790f457f6da08deceea2354bf5a99afa8960afcfd733d9e24dd15d4df748a24bfd11bd7461808c8f9ef61efebef8ba60a37e484ff0ef885381970d0747b10ab3e5f21f6b3f10d658ce849b4ec0008dbc5e1a2a0791a19ca0aeec6896b9e8c152ac4452d72d903ac9894a9a3049daceb988d6ca4a6808f9079b58ede63dfbc262f94531c4d2705397b08ca15958b64f52b19bf1e98ccc2326c688c10831e269303e6b22b47ca0dd0934de1902e302230a452904d545bcca1e181ba797f977ef43d8a1f9a627708e3354eab711c3da5bdeb95d32db8802d93d221e6571feac35d71c331c4aa2902fb90e8749944eba5ea59f39414c20befedaf6c08852c54dbc22e033c98e137087d1a853b3f61a59bbcfe03f5fa54828d13208d84458ad9b0f46180179a7bcc0aaa75f1305409d50bfcbdfdec8fdf1dbf618a5db620a276c55e8b222240373703400f79bdf32a7f392503007743c75f513b645e9e0b1e59427a22690faa284608bfd12a949e1c3f5bb01b8d2ae604e84f2f4c5f75ae996f49bff9778622c9940c33d27fcd39d754a1c961b4dee0375cbfd42d89ce9a57efb356bbce3d5e1b2c0978894b43385366888bece77f0160fc3efe1e9b5a7570a74add6cd94cc2899501a9bc57514ac9dfa1f0f72eed1334476e7559a6c7709a2e9199df8b4e4978060e14f535bae6776868a9b2d207cbc16c1c8fcc0a72963dc4b1dfe1a3e23df8d9025fa3558683789af8d126282de4d6ca9b3f1a61644a10332fb068b34bcd9e210fe0e202e03fcd5efc4b1e77db443f75f7ab7e09cd4f111df691d71a11aa67040e993d6c61413c6a8980f15077322ae80f6a2b715b8ac75017ce5c2afddf9fab0bc535d9e90a43770669be198029c656c412c2a923483e6e6daad77344a2819a059cf8f651c1519ef1fe38b55dbd718f99195a3e36c5d1527061ac851378a3aa0dd6af9658355a1ac6c15a1e45fef3a511de69aabbd6bc083f554dc8786d5a190469d7b057fb58727fa537aeb0b5ca9efe4f5d89f210a8198e6a9081963ccc480b701c8caa3885521503c80512f575cba6494017b740b55deeb3be7c49002c3e0dc25b51b2cb9b2f89bc37baf2d71339d430b0f9f0c5c7bd4f52cf031ca74b7706af7d4dfd2ecf2bf07aa4765266c1e7db0d7cf946425d77d918161affd5f22d02d18fba12a1f8cdc5b7b1a57515c4edbe26d3c11e386f9313458ffa8dfe11210edc973608a9b164a2fcff94fece921e7e52b69b08b16a5d79584129924e47e709399b868d00f6fd334e63c19aed2ba7a7067d958d79cd347389211bced8b12c042281692d047d66754f0b51c4be4c173edeca1f7cadf6f4e2e4c93a5291149503cd0b12a9e5804a9ad7f3614151744a12126bdf6f720dc16c5e60bcdadca7f00ef615b9c50a0a6fb224acc72cc04d3dd91372d6622023cf81a7b0d815122a680ab92390263a254424a9d30d0a58475a5e48b73538394128a66bfd944a7379b70ed5f8a69eb4f9f22f076a845e9113d8c46689235a337fb4cdcf33a2be85c3ec04621a7f2c60072e4d79e2fab3246dd26d7b4d00ec1dee1f149d3365a64cc0510caf4e6449a3c7fe5ef8e485eb5e7abd30c60965d149819ed4d172bf1b53c6ee06c10294716b69ab466964b50a18eccfc4f21a03618f305194dbd168b999a8390937b7a506aa191904fc8b55ecee44bc54fbe4598b0a47f96fbce77bb858ef600b9753cd7f97fe1cfa747a7b4e0a35d7d154f88cbb58d0f6e219cb9849ea43d102035d6ea4cfd79e8c4df0c9b0251f07882dd0bf56d845d4e77d3cb5e06cac41074eca8ca8be72041a753c682776286c0f1a5cb1e462f61c4fe34b8e1b3a5d520f6cf7db15cc7fa7147d9a4aeb9bc9a12d32967d6d731eedd9284dc356cb0f2d01a59c9d3f3fb1738dd939549acb35cdab7ec2687028b46e90384009797eeeb305ac0eaf530329635c0b3127fb9f19d8e7499618276306e2b3a529b2af617d9c0219cbce0fec136ea8f70471fbef448a1bfcc99258c7d11523094fbbc7d4bce90af19e38700a546e80b1a9c2f59c0be7947201f26f50bf8f2dcec61e9b331e1803dc0cfd5e7900b9048680f3929880bb250152e15d269f387de0a8609efed38f0cf80faa4738d2ad80e41dade9f4a10a86e2cb5c53b075c4414a5c646d64e7e40e01cbdfa9e743f7a020ee7138dc8fc3179b9953cb1e6e889632f0006bbe10bb00b7dfa2c47840936f11021dbba0b20909dab77852c7f91149e348c2cbf36a880aeb52d41fa689d20e9e693d9f3300f0e46547c63f06533f41cc84c8243ce7dc099fcb2715177e2bd057fae0f563d17daab53569be0c0835110b41ad804840d20971fa941f4eb15039725d160e085da2ed5f1e392e2464d0a02e1479cc21e3d508ad3fce67fed18525be8fe5bc1be202ecb79e7635484b35fafcbf19fe806e410a5bb070d0dc93d28bcc6c754f0a6ac66d0dee99f64bdb77abd4dfc413873a2c016aafe7abe0dd2c8b3d5314d39258d61cd6a82a3dc810462da092eca95737530780d4597c9b255564eca32c4c1296a6aff4f5a7da83b7fdcc65364845e25c4e06ed6435808edb9344e6d231d354835301f342d32446984f76a73b36c01984c8028c7002f0ed1f1c19b5972e41cac78625cbc18e6d207ee7769d955a4167ceac023dc0b85f4ea3b0d7df0925d5ab7dc79d9f862a68b49bf7c3926454da6751fe0ae558adf2c6e0f685a5f248413b31269d552b887ef939f4305cd1a0b0d72f9302b257215ba01b4ce5c4a138475d95d6ba7e16aa70ff64f680526b4f5289afd00755542316edf852a3514a76fb53232ea96d54a066d9f4ed948f6dbca8732d77021f78763bc87684010a7db33ade6f3ca5474fd6c47ec7577ee20aafaacdfacd0dcd366946e23af146249884017d934c78aad10f4af1c782980a5067bdbfef8702092be91187b89aa2fedcceb1518f9b71210aaa487c810ee62c1f4696348596030f4895cd2d8fbc904d43dc3493a913d2fe17b897db84c180874a8a0fa4962f03d39f2b8dbde2b3c699594d83eaa986933f13ac6c6fa10b3f42b47a516892450a2f6cfedfaad75ad0f24094fec0d41f6aa7c6e5b7458ede51f0961d58a160ed0db6a7935df9d522f23268e4fe8fb500e4aece2a3d240a9cd83239d1f11f368803e2aa58163bd9fdbe880c470f633225cb64d61617cf97b017bcedfcbc6a64170e2100039fefdc23f7af3544d651daad7dc1c3baae68519a014dc737a5fc0fc30e251515605904d9129e97040c503fe92a73b3d9768571aff0c8ec8a322179e603263652ccff21c655d5a205df7dae200975ae9646ff95e1a1d913a6c74842ef0dc7d472f63c0a177e028f1716c9c6d3a22373d5a8eedf83dc185bc23b90426c0635116f61518ad605d53c2ce996244ed2c6a57f522040ed31aef386a1a3b3ef0e6d45be7c1be2082fd7e498825a0a8260f16f4ba6c70c9482a1b061e6cc310c0962f48d9fbda1577b892da605bccc93808771ea1d51192b309714f4f2341e1d0764b406047e1337ec83af00448df774ea0634e5dd556f20af18d69b163db9c60cba79ee212d8c54a3696646f9bf15bea4100b18170aee86bd35da56777c51e10d1ea56c440e99142cacae9bdf8e83478cf4f7d1b340932d2f816e26ea20151f014163e355e1c237a905357f5b66347ed3e27f063f2b0b0b9ed924cb0075c6f5090d1be47f095557390873e0bd46b07d7ae94c9e8667eddb2252a57df76b3f9804b8ec8dfaa02396ed96f177bb0a41a02917428b8d601ec2d6b95e4d27e754150f2cbf42b99ca47ea9c322d10526314d565096248f202554270fa93e842628850506abb962b3101b35c8791cc806f0f075279793d207c528a6c3d6f0d3cfbe4d084e3902aed389cd0c55930341fd213d3ecc937c0a22690a8579e1724e5354e90951c5d8800df8be11c8673c1b8746ef18f33f87d284007ecea582aba0d85be30581e4187e29c2888d457b4ec0854ecdef24401115db8c16f106e07cdf427fcf092d567284f44083a7bcbd6c35eb03ab7fe90e791290d4d5c055d466aeeb2dac0d9c0e917857d695d444e202988e0e198f15f8e15ffeb7eecaac3f950405249503752d9d1b1f86077d9827edb660dde77d0d893eb025e2dffea829a510d12eb409cc44ac404f310b36af86a7f7611815231491b64bc8d2f2c3aaebf9cd736ab50147b0a8d2ff62bb9ae013b5995695d19b80397b6b4fb23446a2b34f9da2a495013a7203f86af6ee795e68ffc16ac60bbfa684ba1a5f73eb67be117788a41b0d00d9c12ee62845d4f3cf4992ea253e0ec0749cdb421646aa7f0b532d97d8bfd5023ad501a901d6acb77fa267b675b323c1e0e0086d9b38b909ef402ca7f94f5e0286f6cde162bbb220496f01711d1f5692b8d5f50cbcd8035706f812eacf3ea50aeb739c413817378ea22fef8d031ec12157b88e973d13412c08b95c3711df570f77b99391ada37d4596bbadb80396d2e715782c014ea7cce672b6bcab8482a005d3726cc8cb2139d498a5ab05f44d6d61cb4e84570dc73c6c55658a34d77c63091feb693618eae5467d91da37e5fe68c044336c31ee89e5224e0a7a8477fa31068a6526189705ac7ae14bf99caa56262b564568e3e396715ad90e36ac1777b1037ff41ee261e4a1ed11c0b3819587523dcfd36611ae9872ad51b8506212172d049ee4a1f0a13035968eb60e4bfe87c46e1d7083a2b564962e11bf8cd790349a0e0c370320723de812873dc4ad195491310d1a877d6855414b994f7fdb6f56de0fa27677ce0980485cc189c477915b5b8c1bfebb850bba88434031bfdb29cbd10568bb1c70f25b127cf2dad52068976d99bdb46e2b349578b7e533a7d2a2fd53028ef41438439928dfcc1da355ab9c21b908c45b3e1678cdb403da9e5797d1bd090c5ca2e5a5b5a332951eb2feab1039cef63876fa208f9bc49badb1792f79c8064eb3b02ba1c662a5563e44d63c954f44d0e1f1eb86842a6fdd9b8015b67387021af5ca34f2170c85fd3ce4f26b2bd79721c13ff069ff53e6c6694c6973f22d023661f063749e120ce23800e47eebac0054d8b262248f17bb9f3437792d725d091f4f319a2f631788bf75d16f33889c502c5c51a469868ab591cae2c73834000d9f2ccc60973c99a4aaedffc0df9025eedc50e8fd3000bc51051f4b04e4eb5b0eb6560f9ac97fec4a83e36e98bd49da09afc2a464972df66320ffa5138d237c0434029e6b51d2fcae707ebff362d7ade57290c2dd591acdea5e71c1a05475b70ea7bcfad566e8b55af8687aa76b5da905b0970048b92aee41b5981ded4dc8d50070182b30c13e3b4a18b99e0ea92452a16bae195de5f91859298292dbad2144070a800456a64222251b4e5efd4e9be2f715bdecb37b64d9c194bcabac6b01a70ed88fac5880bd593f2ad2f0de9132687ccdb7aaf6b69cae0ce88ae817cf542400dee0dba2c2dc53651eb79c894951893c3e76a05764020383b906fabc799691069f94103d9074f2f7a1ae8b369bbf532d6eef8361bcee1f482b01452ec064e40c05f39e295a074eda84b5eb646e84f09765ddf8a4770e091bda48dcfd26a85a0ab075ae6bf2231fce46d25a87e520d379da873b3bfc23620f611140b8022fc30ce73ef3378e3dfc5aef08510c39637442f9dd24c7044d44a00ff4120cd0266e0e31cc7a09e5b2a4d5087f0287e7f53a0cf2b590fe8a96e31d7941dc41d105f20b92214b21a916c6dd7889d634d5c0f8fee2d3a307dc82eeea1c1f6606472a2e08d8d0092d118676582d44639a7dec783126eeb889bb74fc4218642808cb016a05769d0117ff78e8ceced59e8a65570f26d589efce998c4256491b86b77b2391037bcc66d903766c6e894615180370b419af30f5798921faf9707662a995b6b801e4e1e044fa5eb81ee59d70619ec428bf36ae5416b678e61f62016b9fc0c04e0417325862c9eb59b3bcc338cf9a8d639b77977e3a75b81d31f6c7ec8f47b9320d97b01f87571705eb3f3e544af04e300f2cd02e60efcece137e0aed7d19d2e00604eb7419d6d7a59cd6449c3b8cdde052cda100382e5e9014ce71763b74bf8106a0f659a8b0f03c18c2a2a51b516e8ce7fa028c30c0980c02cf2e6f0a4abba208f4d99858bda95cd632236fef30a7a94223771a91f7c79b842cb4c6917db14f0b4dc01258f1eee375c1de7f203e87d148ba893dbaf2415e7bd7412345a02c630f04778e72b1c93f5a3ab2a0355bada13ce60bf92bac252bd9f7e2208aaf3d8e092c77d4a1e7a9ce39b0434cefbf36a7efb6ab9d79fdc8abb0e43810cb75f7930c20d5ca6b6b5f83921a33d91d4836c1802a2b5973df88285aa21074158d24c500bcae16a86ef6c7aca08e6685d8a06c5db9bb30e893796a366428c81151b01a0e018d0f6ec7686819abc26ee15d646fa441981db9b0a0b97151a629590dca5000a612a24a2617927bb95c0b579eb0e383355d6b913625f90116bf1e216649040d59ee6c9f806151878117aea663053041f60618830ed344845a9abea0e57cd90c4737572fa1773f2e71746beaa3787a9efc487756f2fa3d336fdd3506af1c0a02c0bc8eba12f315926a0bb9f81927f23544c1529e58800b954f4d02426917a10af2bbf3e675c41baeb8ee6016a6885c6cc869446669a5eb0e9a0b252ada0562035379ececaa47f0f81e61fd74bd1fb378e12091fe1c8bf5c13d2d100d9a59620c35e7478f178ac8eb770488a2db4152188e05dacf8c544e4cd48d7b24b2275008986e38451610aa74f9f9e5aa366392a1789d7366de07204abb5aa274ebb8b1001e1ab30b156b3be2c74e77e47e3a1d8fd48da9ea4b4fb2acc2db2bc10e519602c850b2ef5ae991febfa7a60f83805c4aaa91f5fbac4071b3f9d62bb43847810051a04e88f96e8e259d775895d0ce6c4b0491c9856bcb1dd95cf599a895694b09f348f64c643bed85c714550aa6b134956b22f785f990dbd227e4be2d32646e0a994326eee8b82509d9bcd6db7320aa61f62cb4f2d98a48c9085c6deb96f6d508cb91ae1ca2586879def704ec86066b2cf446bc4490332a4746028788a10eff084d44ffcf25af752d22d010d3c293bf5e7961bd8fcd1872484c2bd4206793230643c13fc7aec0cb2f2b544188c505b4b35f7a111c3efc977b26e7f79284fe20093b41591fb2cd741acca1a73372b22291f1549308d02b652a4501d9043545320661d35e132677ff12b0248703307a545cb8e6475e324635609c824d1cf74bc50e33f1e855370b4cf7be8f92e9f5efd7f235670098e351b31f4bf66a6237dd3601d3cf305d2067d6056812c7d71c7809febfe24e9ef206ec37533f77dae26eb00b92b41f1bd57e194fcf21a85c6adf084ff0300e7387230975cc932aa5ed1e9f03c3bc7f5d643b93b7f4aefa2c768e4b1f9e8c45eecae224cf79efef73f533420a07305fed8c760bd5bbbd929301d43aec516f0f47483312469b24772027c11a0e021efb35fca3c8338c8bd1834df5cd9163dc6356e1a0fbd6e209140284657901a842edd438d192266db750f52de081c8afe3d2eb15ed02863fe2fef41fa035002c92322e3dc56d3be24fa75d7a43ec4cec48d75ca3a5e822ea9625a42feaf900996d37d4d41f488c263651414c5584168adcab739b52b7688a2c351088105903556e60b3fd66e879755df3f1dad0ddddc46a9da8699240db2819b5ed5aed1908e848c84cf5276d8b5df2e564ab36d5f1a9e80f847ffe9dceaa852577b53d110ab5cefa3e9ee90e72ab73775b1b7428269d171b3ad8ba6a1696a77c895b8fc90049dd1b5dd899c125486a8545d957d1b20f97e3be534f066c4661bd4da4a1bb0dc1d7a31b2a2733402919105bcd1f2310b7c1ed4bc9f2ea3ca5b05da71da82e0d749b2bf513ae3c6983691001ac60ba7a99b42cc7d5d4665a15456cde340a600c3951559e615638adbad7f9eefd6e5b97462a8f805016bd6cc28b1942b569630a5c71325e319533c92b343eaa009823f8503b2d73cb24038fee9462c55c7d920ea11b0c1fe35e0d8cf0d1c54494a50511470c991a295b8f1409fd7bc43c3c050df37179f8d27a8e8056be89f5dc6d575769a2665a11e1e5bd93f0bacdef9ed70b96c97f833342aa5c9e6464d2a559be3f480093d690004dcb2de4060999c79a086e30663e4bcae4a68ce731f408e890af7ff4a9e7f0abded0d9f1541ce7e7f80920c03e125cfc30b2558fa7ba6f6248f7c3746eb33329c772fa7ae63ef791350cfa930941e51ee7171d234b39242dab32b7a95f28eff35b652234fbd0629080091cb3816dda3d746ce0046410fce064614ca6066273bb234772e639e7f6ff4d0f34130af8da8958233076001e84c540683d1845b82e99a98cea14136e611ee609f62b7247d849f89f9d8f99dac928a5a8744f843fe07fa4bd5e263615dff5a90e0ecfdd8feeb33180e0f72e5a687228b82e0fbab979226a0f01b53672db4a740daf6e233407840063542bbbb6fbe176fbd48da75c12a86f0f87c7915857ab6b04ce70351953dd52cddd9325b9a045f9ee0a4f8dcae5e902cc25165838df9dfe0bd79400496b81bba078a2ed8e2e4eaf612831e75b7003a0638d25ec52fefe0a0400660bbb895ffa8cfeef54bd26607e006a6539769eb7fedbecc3a44ba23aab0671b26e7f8a12e08fe0cee83fdc6f629810be178764cb1a6971fd297b9e9a5e01f91133d0c5686ec65379052c1bf04d61b997a217ceded857fe4756ca27c9c204 +generate_ring_signature 2f70a3f91cd8002c712cc239ee2488b0046d4b010eab9d35554f4e1bd9d61b0b ad0464c09da58d6bdb00c070a4e094c0883cfb6eec67fb6532093193b4b1f6dc 55 e19f645f9ee2bd327eb51f336a58e2c9db7b1a64a97d6803d6b56ed4fa9cd560 2c9c1ca72314e021d1cebff7509cd33dec503d78b8249bf712cdfd55895a3726 7bc59484c4005b65ef76ca020046038e3a524db3c456d48bd6e6dd45f38a252e b849fae09d21667f19fab5fd6c7d0544b59bfbed39a524a942d48828106cb12e e16fbdc60de52e405d04eeb52b3b00c0bd1fbed1d5af08f4f35be6428bf4fd8d 9ab4b2beb1c9a8ae1c4c33c53fe19581488a836bb933b45bce4884604a5ffc96 0fc8e4a7ac769f9a692ec80395494b43e581f82aa65b4247ffd7a67291fa3b58 af9c421abc18053d52ef23a6846ddefcd717a96fd90f2b04ec33eab670dbd39a fe83d20f114e98d35389b23ec392dce2f3a110c45710f0c61905bc5c77bd6389 ca5eca751a73bb5c81f1f85ee75c0b6933f364579189a9e010b470495d1d4166 678ddee968928b2be7d171f934fe13a681556c03c4471d2b3a0c106aeb16e0af 58a41d7ce0ed959b012e587542acf3031f7a878d0668573f296e95e79c0c56e3 1f98f698a97f4bf968d8c4a073d62bc50ab6768dc8f0253a6ee7d0a9b8e400e9 203f2fdce050523df6932b625fcd99c5d592853d69594c6ced0d8d1a27e2df4e e682c2bb504201724f9d6831aeda7347efc2aafc78397f237ee36a2d609adafc ded39b9bc8849e513f81e5c22bb93f4d7009522eb63f580e9e5ef369a009f63a 9b4566467f59a6f545fd438293c87f895979da6151e0f713f0535a6a78edf605 b16fd389f7553736eab1855cb33822999b4c4a28ad1aff92776724fb2935e91c 1ce0853c4f87e754e05213c1804225ee1a2120494b20fe23bcf133b33166504f 0ba8b536b491f31696ab1d4dedba8164f7a1774d648b6aaf571e7a1dc00962d5 df770c4d34b57b2cf0dfd5e59b8778e4f6821796acf3e5a8a1ef2b6c6caea12b 000cd6a7b4564634f50f4cb3bb34040bbbea725ba56a0226603e12f16c75c838 68d4b4453419af5172aa54d1ff68004e0e8bc785605c87dbff1522a69fcf5eb6 6ea42b2fdfabfdac3d4e4dd520fd923c25ce1d66c5c536f21ac14af60a3a69b0 e6c455a08d17e0f82fdb4b1d2952d1f03bb66fd20b40d80f693e6f1621261b8d aa4da257da007f817e0ef63694a46500b56c649b6430cd8ad2cd98cdc92fd5b9 ae71c3343d5a2b6124deb034ac1c381121b80e9cdd3c1a3f8ca645be245c802f 55c97d73d6cc3e432460456234d554ebc88f4153ed753ae69a42ed8827906384 7d2d00d52c811603b99a492a7a2459d1c88cb5113a22ebec3ca09ffc126c8757 b487a8889eb4a2e809205aba036adb11473ec68efe7bacb6df6fb0ab34b5f563 636fd45289cc28718d29ec8dc3a53ab20f2dbdb81aa8a7e73ab256f16b075484 d4c8554683898da5f9da8a64c30fb3e343cfb19eb88f7fbf567901a9a98d2d71 160b282251304e7cccc5682a64e5f4a4c7356e199b023ff9818c25defd1e47f6 1a5b8c9c7a12ac3f2470048f0642602e07796a4bc7bc095aa88a30976fd3b873 d39fa1a932a00e90d0c2339bf81666c08f299234846b54124942a9db33365b48 d023e5c5466fbb9f9f4210e53965f90b51efaf8181b4eb11f28081781f618fac 441afbbf59c3ce187d0ea4b7f0b8c886509b32f762f84b64d64cb1ae7e4661b4 7d9521c9c0476cb8deb152527709412eab60f79e6dcae7d6a8bc764b521552fb 7753e5dd3407847543c066c3be5b57568a00ce2156029dced77a7d7f84b51c49 643c25c296ff6b10e044502b84bbbb8066dd60c13642ed568cd3a127269cc683 af193ec7757ba760a786e1bd6dea91abf67e0d09fe1dde9636ee740cb64b3881 97c86f4eb82cf9b008f34d77be77474a9dccbc7a28f8af3135804c4be4d7f4c1 42a123aee5df04b5d770c8d135ce837ca7b61f889afbfe459affc364683bbaf4 82443654c82e18c320f5c5a91eddaf6a009a9ab264e19fd377094165553a5798 1c281ae660b2a48d4ce02e48fc43d19d9506fe50ce98be6138ce2a12f104171b e697e05d9ca04429039d3b472766d362bdd5bc09edc5749244a4e1631d24fb19 c64794f14532943a569709acc96c45ad352cdfe950a925f2d6d5d15461e24dd7 215eaa77ce36cb9da74e8b4cbe60fa4effe632bc03b8ac2972219d7197b1755d e45863fc78b8e87aa3e1527121dc382a16697e8febcebf7371975d02a8c019d6 64c05b4302440eebf39e77dfee67a5c556c28240d204684197c36c61add87089 c56a0a14c23839ebb8fdcbbd53da469670546a8a4d9b988e4283fecba308ad63 ecce137e698c68abfc86d1e30b88654aca22c4a81c5badfbe97db39fd069f4cf 0261b71c4342e73833ef38913e92cf7acd43191b63872739d4c419daaf35d193 978a3653c3a18d1f58e3a222176e34d37ffbc816b80d11625e0ea049554b1778 bd8028231d3f30ef112db35443850f73b573f8a9dc3345ca5744bb7b97ff4009 8eedeeb643d1d8dbc79f2f62d47dad575e1965b8cf1c25dede6f010cb2e50300 24 eaae9417376a2727ae456952b6c77c5f38b9c92e4fd3efb869a6cdc502010b0396267d2697d38dc2c0d3f9885fb0d4379aeeac7984dcb723528c058fe7fccf0acdd9ffdd8405dd4d9dc00ffa6ec3672a19d8f6ad2d0458eca37b2e6c7c6f89083e9aa5422f51edd7809b07cfe04e6efacc67fe42fc77787bcf1b2d59ba0d7c06859a2e4232a0171db5d55785d4b9a48fe62455761947763acdb568b2b099f007aa22f02dff0da9dd7b90862fe231b57a967ed78c686ec94e2797767b1d36db01222ebd062911c5dba991ee6faf24229925290dce2d25b3eb8b878009b5332a066ba4354a86a64ca714063bb14589b6b12c891c3532375334fe553feb4b32b6056340bb3305d8b1fbd423656b6bf75af44f3d386a3f141166bb65c566c8bf80007ee029310b82e1f2efd028caaed863b6b4c649992c5462e82ade7ce699191f06f152c4014d8f79d15e18e0f6bebb9701c14d261f3b799329066b5253cd64370af0cb6924072f3fb7dc1cd392eb78e71e33c7772ef6393563d12e73509c9c040617306dc1a88f66e3e7f988647a810efa4e4a264781504020fb8b7c4072ce8b0b89c17f947b2b17c92f71e9476244be865262ee2407fa3339ba5975ea97f98d0c883e2e7e9539a06cf25880341e885e84d572c14acfa266319b0d37ca83c5e00a1ae825930d8c9f0f6f52e475222e0190541602df549615130b9cef3867117b02a6a2195c5b439764938e418a0e08ff17ba4aecf71a15a48a3d34028c897b000fdf8d73d8977464ddda4eb7eb4768d8ac75193e56d05b6b66d16537985bfe3402cc900fccaa26b56a1edb0193f1db92dd70daff618243bd88ee1b859fc7a8fa018bfedc642ac92a75f45897d164442d937801482f835012377acdb6013e6c9607036d14fab4bd810ec7c52f3076e2c3312eae3a6c9d9455bbb5e03272f7abc805fe0c7930ff94027f0045cf34444969e84289270c63000977de2d1eccbc4c660bbea06d523eeb97cae1bcc36ad1258c23dfa3f98a15593db287f89905e587aa040d16849607f0c55658d96f82b6216a62911594347fe259b81b1707cc963bbc06a8c0a3a2667e0b011d448f931fb5351aad6dd3579578328aadb9b6e711531208de59fed8be1677896b15a46e9685fe60529f7c19c1209175c87cacf4d5ba1a0dc47ee237063d4a8275d60ca98e9e2e873094df5be087464536ceadc366e9f80a3257ef387248eb702b85ab4cff32c55c25bf52ec106c744e6f482d5431e9b709468be2303ab951539c1007988523cf47f32263347de210296975c7183848a202aa110f4d57801a1cee8b8f80809ef5a4c620e1f66b148b8445301409157ad203809df456ed951adf7e693d046b372b3a106f5d162348e290f650762d793b850109951f7d11cf72284e937ea6bb27510bac5eb19a722956917553e369e3ba21037c52cc32506c4e4e54d2f68ce4a36639cb5b8161eda46329e0f6ff9337b8a20b4c82529001e10e9faf5520aa8a489b93f5b6b70d96e63df35c85acfa5ab48d07e161ae389dc35429a9cde17e258e7d83acf149190505d13db631216ab9e67b095e818687fe94cd4df7c6e0b0ff177d71aa27f8874f320ab0d599f635ddc0c70227fb81b00f38f23633ca1a5c43d672ae793be916ddb2a671cce2a57fbde01b0921611c08e2f0559e197b3a6a98fb2172b81e3f50b2dd9119c13a3ecd0df960010857730c8997a3839de95ce0e0681c4f0458ebb22ad2b9ee6bbbd2f6849ef206a716bffd9de5e6fb898393c3806a80c000b20069bd363d940fd3538a58bda70db5aa711c0f919b7118ea3346c2a796f41425d126a8189dad387d72bd7612150c350c51ae9446846bd499a6a1f53db916f633c273275cfb7260732a949a4e5a0141e7ebd4ed46c0db37a87a66c8ebe624ecaea0f2c3c954ef090322504544840e52857fee8bee484f3f633dfaf1aa06b2bd654f42899d551c317534b8c13597021e736bbb6329e9fb48aeea0ae1c8fb5a1e7f79f2b1e68d80412ff757c848b80483b19b8e3d350b37a140e8ac974fc6414223993d77ee9868456e09db2f9b880833519221072db682a5c6ee98b6efc523abaaf3290654a399674729d6cb2ad80fed946c2ef29c0cff0e30198fab09d6683487d1cdb7c0a26ed6685eb96ba8d500a3bf3a9a0710e9b41d8a3c7a69c0dcb51d7ff935d0a53da7799b2bff122dc109f9298f1038fb77648e15422b230d848edb699b66c3dc11135f02b64752d1250593ac5dc0c5c36aecf65b197de594b686e33cf9d3882b5586e31ff54dacd9ad00e3d57d7e9a7671f761fe15bef823b48b3de7d07e4e51a0b2a5d30bdd2fac5c01315fec3dc7ce5a002168920c597bb91a4da2024b507a05d29a75fd82def5470531c558cd909cd07ed0cbec68bd3a41d745b663f35b962bf92d1912218482da01dae9d9e7d38b5d5a6d76a8b2bbb242466d91a9b3593f4683b83f2671ab2f1302957e815c7ed1562e22fcfb19b7f59853691ee93b9fcdffa9ae42780a01f3430c0f58ba37d5ad4286c5c755048350d01d3f5f8f02ec83205d1effadad700d6b0eb93ca6c851e711f1201c4a5c60f166cd81133093409a6072437c0a9a778e910cf7c392bf13dc9475b203d02225d98220435904a145f7645532a24663efaddf05dc2bd08ca29b6cbd79308f9e79fa50938b16b91db53ccec2acca982c7960200af4b2740914d9f8cb018a451e4abe62d7834e450a8b99d16c8017a5127d2b4708bb062672f28ed0a90250c4cd754e04ef17e7956bebc857600835621a854ad506165904f04af967c5cdd86b276d908eb510c29e4b35726e40ba2660994debf70385c1995a4fe36601451926d0a76c7beabbeac83fb9e56bff487dd07840774e0e7ad674b8a7fc89cf6d26f14546f7373fae88cdd4e6faf8015f9ac31996fe2908a3dc9dcd66a34a025fdf4eee018de32f3bc674eda88923da8afd6744d7e0230674d3e99b93d4affb1c9a9bfdcae352556e44b0a4b478a2c8c2a809b27c9202056a40cddfacffd4c4d31804aa715771c022ceb6b1b55ecaa78a8d84d6c0600805e2eb0a8177d9080ad7be1cfe3b1f6cfee69ade4341eebdf3dbeb5976295deb07ccd24ab4c7a4809ef48cfeb8c39db8f931a9fdda4ae52e9ae6b4e91a075a19090b84d65381051e02d0dca60ecc80a315698608207a8963fcdb341af8242ece0e3b75341340e2feed3f74de77d21f0b2befa50f8f7e3d5059ce39b5aac6958d06c4cce49425dd6b3a9df40e9c1fabc82529d5733306332ed0d111670fed29ea07ad6251d7f6e3d5937c865fd319e230f236b29c8882c8d68b310e45d79ca5e209bdce9813668c97d0689b58433a73ba8f3bb563025350cdd2c64c0632d5d86a0fef746c43766309dccd9ccf9e5cd5a793055dca574597bda93935f62b96d1630a7bdf953d3594587085e7a77acbc10e83c10b40b78928c98cda3c172b455a01085d198bb2560d587910055135a549b0b73b488d30389b055238fed433b6f3e303135d34e9796498ed2891a9ef78d9dffe0c68ee911eacf947d2e1c216d2e0740739543419d67c80e479b1904d751438578b73d9a50b63b8563553eee59f45570c95e10187bbdea4f19814e1859119077e23147ae8cdcc0e2ebad55222b3c1d7054214a3e7a34f6e161521cffa00e93d09a1e339f9b2f22844ee1124f5334bc5003360185c32550c06eb51ad2a01db6fcb59bb9b39cf4f46f52e146dfd6b6655038f241b7397d5240f9c2942c95e6499cfeff0a5eb3e602a9d56b305689d7cdd03a8bc9e601a41f080c7e1010e2c7301ceb2c8bcf2bc5eb0daf9726bc815aecb068009ffbd354ce575f0372b1dde38c125559f61bc7dc8903b991fd67165d75c0fa2c7443145836fe814858b5fdb77b7f096bd461e2a1687045ad1fc9036baa10631afd54699063ef52f9de8c2dc11e15223e0f01af2258cb6b404f665e04ec60b1d6e2acbd2193d702d63358604e6ad9d9c9f1bcd0a3601232cb970258e56490a810aa07de8c58f212fd098db0818d13ca4bfed5d0b44a78a34c5dc625d045a0137789e1cc12c5d2ff583f3e2c7f5b845ea29231a23fcc79e8b80450c6c86840257606a73f3cefd3027d88c23ccbf6a2303d490ef5ed6e1af0a849f74364256099e7774784153f26129a683fe15a87970a9698faf4bac120754fee7218248d30d6e7c39562d4fb59bf693dd22d89c248d18b90938358f4eb49a2e3633422add0385f735d14008fba4fa218c2a834ae780f360030b0a1ff5952ecb8207b304de07a896c255f098da3fc9701e89dca066ea3c72692a8fd0e8027b34bb0b191ebe0da61a9c1f4fa43a993a4d37b6d33fc80b5e841f9aa39ab459c4fba269c9cb0601b55df58b2a5b3b22844ecef8d8d1ca84d73c325955264197f2b142983f503c0b16f97c1bc644280cf6c86263f0ad2589ce731e5265c22bb29b7930d84bec8306e213ea4ccb67ddfb51311796f5db70fb7b8eef92de905828c349075e91eb3b0a84b2b8f91d23913ef090620b0d9e5bf504d9cffbee67c48189f6d38e92e82c05ae13a4e54d0ed852411ae332dcadb4ee45409187e19fd812eaa4b614edefcc03cbe27612876f3a106173f1c4d948da216876826b4096be5ae2b8320bc915ee0fa80240fc1843348ff92025f48d6259fcaf2984b2c46c9b631cafd366de874a0370fd5e519256dff86302bece079e9b1e9bc1f3f063d47b41b24bbfad6a44790007e7afbcd892175bbf1a106895490070ba5dd74a4ba6e97bd9c5f1dc5e6d9d03344384259cf3984160cbb7ee78765bba146647e994fe29569c8ee9e551bd3302f4a7945d157b128ad73c8658d060b750c404f89a868e4a7113b4e8026fe9f7006918ca80ff6544497771ceaab420caab0ec60d9cb59a44fec7a75914a88fd7028180a20fb2d9c91e20abf0bc4b1d9b1c1caafdf9aaf0580631d64ede07422507 +generate_ring_signature 0ba30de6e6d2dbc9fd491a2874728fd0280ced45df74f8c8d32cf2e77768e7ae 0b3de9afe1359f3f83c005c7631c44a73ffba60a836ae3839d8d5927c4471ed0 152 3a358906b6ff770b1f077ae12d8f7f3cde8cc6d3c19a8c9f5bb6ee11b7388e7e 472a126f08b58e6cbcd37f5ac22cfc6c662546bac3da95da2ba09452f2437a91 fb4235440693b05fc30539541b237b7f9b5c7ad68df2955361c3de5dfad8e9b3 591196da4d50c814756f5ba064cbd38d1af3671a407d8a05cd8d0e056a4e012f ae702c49cb7fb2dc5df1a5e1c39f2b2ce402f2945ddf59241dc047b4ef81cefc a585b5a0091db1a7ab1bedf09903aab8d0b0a844d74e67de526bc99cfc23534e e092843c820545862a9e6bb15ded38458d654c953971ecb97fd1bf7603c1dc6e 322a688cc9f928b3499215ac5bcf36b55b781ea58280315c156841adff146995 ed842c7281842528fa2b17d4d910377ba970d1443a686d3742a18816b10b4ad9 daef8362af755e7e0d52afabe3a44575242405bc654d2c5c59517b98090bf334 1c3845699b373abc21d24b1107ce31e3d3eef3e7ea96c491f7c6652ce57465fb 14261e794ec119d49af09f570dabf6729560be83464419a93d02f0da7ddbbad4 33b42eae69927195bc2de2fb5206054ecf6511312a2528d4929a4a9867002813 c796fcbba62668d7a0d2348a1d9b5f06ab4fe9b157b0d846443b41edb9a1b6fa c95fc5d08879a12aa68952ae167d17993a7e2623297f75fd2272c82e32bfc37a 6cb2f098c37f9b3c863566c29d56939ebdbad47aa24cceb566cb410223a846b5 b77692430504c280106fdb7f639c495d8ef0d7d558dcbd75b23c03f51f8a3e11 4159b10f826b40b00b32991efab391e8f6c0222bcd8ebc0ca35f01c56023e0bf 3fd351cd999eea1dae072737a2352de6e0d35cc5c03b7b66784aae6b5e8a1466 9a184ac51ae37b7fd07b91e062c085b6cfbe8d9fdb539da59cae447ae7a7b78e f3542561f7f3543004b64d6f2d45dddca1fcc7ce148f8fcc240e5dd1edd2c294 f84af966409541a111985fa9c0ce4d203fb6d449f337eac3527765a7026ebe87 c85ae572e78401bbc3594bc748bca1f6996461cbaf8c28064aa3ec6814bb41c2 4aa24501b140eee2efa6fb6980eb6bb4b703eff58c9069b54f1b30dee82f46a2 c61f9c3d781903381bbc07c6ccd2631e6592130c7ef184d2936fbb156ec1560d 146811dcf8fc8172c1504d032ddec6798525e1d8a9429517ad73641cf2ee7499 556394b1a728ad1d4cd280e5e56837702e029da3d8718f50e5bbaf6f0b05ae17 0d694f1df6ef6156bc6f8c5cc2a3668801e9a9f29f8436d22d25feb331afe929 7ba9d54920fa1923eb772fc59f73ec3578fa08866b226d11e983cc8d2f8ca631 9e4f1d09796df5a2f21b9732dd9a217ff85b1acfa8ad071c0f8cacd16c875025 77c0433220a41feb333fe7a4c76ebee4dc10a08644bf530426b99b21dd0ce3c1 1a34d9fe7d8b4503fae56512bfb90821147cbd0d276ced49d456da3aa59f50a7 70f6147b9a07429cda27dbac9f7b69ff87293a49a6bb32e48e5bd7c6cece8bc2 52a798ffabffe412acd4e9afe2e21ca0f5c3b7b83481d18e98c03f916fad4f3a 6fafe4e0b6a1bc4e19439958d5b5eb6300e5685637a976595748955ac294450b c9081e39f92a9d9c0b0d120d3bd1bb757133873ee5e02dd6e428083df7c89cac dfe7f7dd98096fc79fab45669c8173226c19f953bd7d7eb2bb2739d79dfa2e23 3c3856df0f874c2d766f14ec708a366d23128ad13df93d73db9156b7d9f074e6 866dc59ada75d08e8c8ef670218581db6a3fcff78d62d8f058c4d7f767ce6e21 53ec7e68ff4f0c96dedaee098825b83a4bf56c3f6e590f787e4fb02a0491a035 66d5a0aa788d312053b71aa88fdb961ef22eb238afa1eb8a50f739d821a27e1f f5cfeeb51f1238168ad45c7daa7caf2632caeccfe64a2d97e6f93bea5fe902ea 575fcfea8d4bc586a780acaaf079f4a75d4bcaae1ff008cd5e961fe7f7a7bbfc feccc7e6f4619532c2b5cfb43a0e7240b35e7824b94086a93c2f8ff76bd01392 7fefcab77bd3284fa4867c63503d1ff728ebdac0574632be0dd8afbe601f3b81 6bc526e9c885d131970279cfc139e14d213ca40384f021541e669b8b64cf6c5c 864abdb33355465fd8d918c99bb840169d53e1532c8d82aaff27ccd1197f8ba2 2d37d9199ec4b6660639f4419cf184293ecddf90f8d40420e61bea0971ea583e 45890e5e880c0607c04b9e703270355c33cf305485b9c15b9927ff2728bfa5f3 e5778f46b5adb329727cc6f39685138b0ed0932b3542873dd4448909f7d3acdb 7fbc3ad923ce86f8ff5c34ea3b657fff3eeaafe9be2e00ad74e02ab4a579e558 a247dd3e76847f4c67f98e8c62ea0552c69410b57f59bdfcb820dec44fe377cc 31f23aeac4ecdd85e4833c8adf88b7c481a6f687b45920ae746796c878ef16df 7a812f1702f24563d4af961789b40be3d5b7836277f5c96096385d5d373c02a8 6ed55998c547fde1f832dbd3c496c4b1de1d17cb2e2cace2cf188d38ecab1df4 f24160e9814de8c26b075a6f11f085788ee84abdca189158a0420aacdd9b6eee 8255924674ecbcd958d0ecbd07c8962bef86f2d2a5aef852ec8f1fb7f9d69c35 129592cd3a9d7e1463862b37909bfe1d74927582b6c45a6afd259ab07ebccbbc 5d44bbe0a39fb5f35dab4e900a447a42ec4153eb53f69eaa47bdb9bac8137d8c 18eceddf52dd350616f472d7d1b8fed16d25297913d7f2f8ed04883b999ea48f 3af3a5785978cc66e200dba17afaa3b40ce4a5783fa9333a6424c4ba06fb7618 901492fdad5dfded52b21c9959a178334f8e7168b6609bfb077e2a7b93ede507 f7d79b9ec77df91be73f405bd30216aaacd3a055c63efc4f86f0b109798655f5 dbd4023695ec11a04d54740936f407fb2c2b6cbe979e6ef5bf0767a801d0a178 93ab27f15725d4630ba876f4a58ba78e4e560a410aad53a8a735911b7196c6b6 545578c450ac54f055d46e661d12d4d0adbe0849a61e2ea03d6e3d4638da7cd9 3391411ea286cfb663d15115d113f7b2f0826a840395939e8d5b611433f2d113 57ef1e875e3855b4ab4670fed3d630e6cda0e1b9341891e59b7e2f9914c947db 658ef5e1bcf430e63a0b9122ee3a84ecc9f47388f7b31c6fcf366eb8fa9d93bb 2658013beab0b98888d1eb2f795f64f16c8855dfc503d3a66239a0047c5593f5 69277ea51de11f6fba8a43c41fd968d901c4e31e822f4b5b3553318f847b1995 77257767504d0df2a8e5500dede46246b257f7a9f304d5e034268bb02c23e209 06dffc165810f727b325edf311d3839cda6b71ef24fefbc3f455121092d7a938 a50590f3d9193887a33303080421388a59ad10c546a1311e16c8a7313dacebe9 4a0796e932306dda67589cb37f697c2810df1c1747e2b2fcfa293b4f4c40c4b9 c5adb0180758e9a960558496d341d36e639d8355e36e076ad31859371c8b1014 9ee7528e99713c1bd54457781063dea844349199756561b4be6c384b779ed8ef ec8f58b2e690c33d2d30384142e787c7b380d422bc7845a99b9d36ea5e1a3c55 151a797a15a5814ba694cf2bb077185e5b1977d9d18eac0ad772e1282ba5ce80 c08da9572b6395a5cc018f6cbd1da4883d6a2f7d3d76502fdfd72b655e1bb5f7 30ab619fe844790c294a0434cf663d887898c694b1bd374b2558216eda20e683 196b383d573af3795f23d86ed51dd55d9c3ae875464825141be817a0be662345 153ef73d1f5b17c75c970bc76c29e4f2ff6bb212d76e0ae7ac38445c7c76ce97 4c8e7f367d39ea4210f0154dfb333766531983d43313240753483e00d1b72116 dd237005fb83081cc37a3d4a781426d7c72fbf170ef7973256d3b9cf212f2f8e 8baa3f9913024928a3c62e82d8d89ef7438fe7110b9dea1dc138c7618837403f ee427f72f1736d9a3b416b363db0e19250a04cdeab9969f6209bfa556cf79e5f c7662161c5e8c2390fb938a10669beb2713a9eb58fdf61d4476bfef7284db7c9 f9bac6ab6e3346e88b5e48eca67640c104dbf8520f08e240416c3ef27a29a332 cbc2ee1e4553e90b60ff83126c636bb047e95d7d17e941aba994bc5e122280ec 601b1aee63a11d7e9f930d9a64f672280b17787131d12e46ab0140ff01a3372e 90fa3f00483356004def6de99c8dddf30d340b96a065d0e14e7e2ab5929771a0 5f762e1c5d76870eb65f31f7751070f2664a90dfa7ae1df568f64f8478368bb4 8c03cfc38212d86cd7002a0947128016a0131983552f6826d20a84bbb052aea6 b188375c340056fb061f1ae3bb28ad17beefca3e7b5473fc9087e257cae927c5 dffe5382445976ae1ec4261b3370718468ba4ecab5cbfb50a22ab6d38285d3c1 7283670d3cb766afff7f34486489d5a1a5b0bb823fab655657166de815abec24 f7713361d1b7b267f55265c03a458233b8d077007687308da0ae02a3075c0d4f 202b3150f80754ff14b48f57a8cb648ac372fabdd91874e45fd0b6b5b26b2de8 cc9ad47925cae8fadc55c3005147ea9f93b4b8d7b1517104c24bf0b893ab46e8 be42bf5d3f94f09be5042260f359c8292dfc6f7c27ab8da88e9d4d80ee553e78 3bd3222539be23ca0edc229d3eeeedc1b41b8ce296978d73c86e762af79aa887 4080704d2b09ab7ed4eb0b8b0e439ec8a2ead3a06f817ce3f9363563950fdb0b ad35286dbd79ecb282fc7cb18c5dc7e7abd53bbcb26c824a3141fc054b8d15db 4aad9cc0beaeef5482e2950883d7253a309ef56e7fcff5df54f5f8e654868cad f1d06103cbc99b6de6494047a63fd690a1feb3082c52eed0b2ecffd5872629d9 3b55b2070aa5fbbc6f5a160de4e9f3c2f4655278f38d02bc6f5c5987f6a5d46b 8b6e16fef782152e2e03abe8a72a0c961bbcd899b27e62fc1c59a9b703aabc87 a027b3419222bce3f34445a08da62ff76712b33bbb62ff25436046e8c97ab78e 07fc0c70593b4023360ba9026368f270ee095f65cc7ea57f459b739f73328ae1 a2bcde7f605fd6d05046e0eb333081e8491057e369713b9f9adec7af1b55665d 687186ddddfd074aeffc932f3d56fa3edcf50479c2c537dc81c712c80f0d60a4 868475fcfd1604aa98ef79866cd4a7ce314850928880af9bc2e31eda14f07f79 c46158842eded9c26dedcc934ca9d51ce3eebe5a6173710225bdded6ffba7643 1ad3ae4a9d113f8991939e7396f74fb0d96280837f67caad692d18722a93a200 0ef7b4ff25837eedf897a910f445eaefe4520042bce6d666cac0b206db3dd1e5 7734da981ddc91893bc8a527007f824a22b40844d0b4537209e5d3d37bb27091 ebe6fbdbe09907e6fdf2c89841e1c57cf69c6e324f8934559cef075f7ac0e2cb a5ffed386db8174633ba84fb978f88f8217a6afda3ca2e5acf6e70249b0b4937 c130b6a8e631f76d972d0f155949fb7fa8d093e576abdafa584cfd192cabdef6 7edde741298c76767208089c421fc96b728a108eb96e60618ed458a25e62a0af 3453fa314a63d4eb7ddd4ed46dccefbc3c939afb8eb2d00a3ccb4592e5e1c39b 001057c3d37ee9247b99984c5e5270911227156b442a879a9ffa598b9c0521ee 337b79ce5ca206ad07c09efd4b90abf7d907be87cc842cf4c33a4a75f40d03b0 13f81b423b6fd6096ae3e0877ceb6fe79453a2714d72f43b8779d3f9ba7bdc56 a6f2f646deb6f9e205ddaee63ef8a76b3a5b54a928ad4f3e1dd88d16fcff3992 65cd6f6ebc27c988cf885cf88705f1868f66350f1aa9eef50e3e9d032f8f110f bfe7674163556b3b7f7d14d706cb97df910eaeac8e523d564aee31a02d63ced8 5fd0212e3214b85eea338d564a336905fa46275d7207da69f5c94cd76444b66c 0745a8db9d2d5d2cd987cf39a9719da6ccc397dc8a979bfe1b98755b83ced8c4 8d47529f198578fea2df336e62bb2509a978e570d20a03d0cc5ddee398b90489 3c7ab76d75cfe7c749c818939a4d00b22cd55d17811f1e81a2943368acbc9930 9032f04a5cfc16c2560a30ddde438fd67b34f03d067266cc1ed0d6b1f21a8da1 82a319462f7be63add5b1de1c57d4f96b7d01a37d37c1402b8ecfc7c23172e45 5fff19c41880b0ab48f57fc5453d4a14d5c22d78f10ae7bc7a5ec7c6d397be7c 7b4bf36034148946aa80874237e075fb735b3a5ba7a25a085fbf70fc19f63f16 5e367870d1be56519048148b998a8da59d93b302f8136ca1ce8265d92653398e b1ae44dcdc1345606bc107fc2ff729fab2c215540b088e6117fb82f8469bcc49 09a26ba5b7ec87989a79d47fdec7febf79647abfd0efe6ba1078a15b345116e7 a53e112ca9565d939ac0f80c9218438e05ee1cbb40e094c4c6b9778cd1ee9341 4480d3899ff9742956f8ea9686d3d52082cef3e150fd8d647768701d99805550 0cc2d147b225cdeb658984df806fb6121de31e3c43d73cbe979c8fb8251b0bfb 42ed973a0b53fd57c7676e9facffdba1fa56437683ea6839537af906ed04733a 00ff260d90022060b2c237a1a68251d150fc53b94af0b47adc9d661dea155785 8d7bd44609717a19a777658dc0aad8ff10c8fac87e643378a683f0097e916057 47f05593d2ee2b00a2ddaf85ba6a7bb9c56e09cf8ade444503dc4d426e2af9b2 caa9a7998ed8f932e5d50c969b38da96ba85fa5501bb63af160fdeb987f6bee8 8629cfd12d7e4d98962a96d5359d6df658740b02e3178842da6536006b11b20a c181bd65f77ad32482f0e347091f9e6469d6867b55ee420a6201fc7eaa5f9c4d cf322dd9ef25987283f3e35f9f457c5a8b04e8ff506851d068ae1b0ff35ad2cd 7a1257577ef572707447108a254387944435ae9e37eee8b92ac3b21164599e0f 57bb7764c06e796b8594ef58c605ea682790abf86cad7c3fbb6842f18c70cdd7 1358110fd9c8fca453bfe9f048ce071eaf67dd7f1cce4f4d52e9cad916554c03 107 848c0fdacf662cc9fd1cc58adc663b801b1819348c70d7050c3ffa19f2818a0f7733bbb9dbb547894d181c6d11baace57991c8de120fbe53ec12033b6e773807a84d6c6bf1f117ddd8d1f610501e24da2fe67de79ec69e485b383239b43b1804df1450cb1f2176aba74e58ac5b30a123318aec9cb1b8e0108d246bc489577a005ce98fbd69220bf9e31c989be15d0a3f4bf9574c4e3dd7d6f61f7e69ad7d5d061af789ff84cfe334e2dc01e9be3b6fe460160fe76c7fabc88a76a5c0f357cd0c85e6f175494476ea8a54b4fc342510e6a0f90a699c53c732477a91c97f908e0b7d48672e301bedcb5c8b0130e158894c1e14ab2df94268ee9567539247f4f30465cd47c5688b42e321e8692c9d6a6646b83db2ddc67de0f75e2aefe06e49b10d29b9af0f392611e6ffab054f62ecb84317f2a907dcf3232dea6c3430f67f77000beecf89388987705dc8c9328c0e2d00100073cc0d299cd22d824e5fe1e7250db49a56fe3fc9279b7467e1201f75fe684de2ae1113ee0800a385356853f36303ba7b9d43f8887aa372e86828778d8ab0b7be04b31b8c028907d9e808c3cffd094fcd5149150f914109e69b0104d3b4ad23aa2085625d721028a11c460da9370b517fd19f66b72626c9df5f8ad35a4492826f9cd57f35f0ff65912dca47bb8104db074ff6df7701c2ffade238672332bb92a46c2c6799fb1a63904818cb654b09a62e96e9a9c362d0bba455474bf7e9d6e8f20891b85aa6171d389b0f4425a10a670f82788cef79b731d35383387ed525698b923960871ccee2eaf214fb4c510caddd1edf31c1c08e2d6364f718258e928cf1ce118aa8d7b722502fa20fb97801f227fe677f35841dc50ad6eaac30fa74fe3da2bfd5bbc2fc85e40d26f54bbd092ef1830d9db570d1672e2de8a6c6e10be99e3038c00b0db4fb77c6be170a8801abfe3c6747b04920be58a8a0dae9ca15193c5a551e6506d541d4209ca859bb0abe6acf7ad5e7af8df47f4d77e59dd550a95a1c0cc0b751a1935fa9a8deb45b09ae093ff9057c2b34cec119e6c343c6735a9f8faee189fd4941eee4900c782200c2685a5091dd2278cc27f89fddfec1ce6ab2081f5f38f3fe855c8dfd2916560cb7e91aeadd28bfe333ac9eb06bbf7753240df35940b8326524a929745164f605d6d04196efdb0b93867b4ff6a91e533d11214e31d0262bb0099e373451c8f403cb3885c4556b71b1b0143c1ab3bee5d18efe396bb9c2087ffe6f1092c7fd620119a5b3f2db9ac9b5ac608f46c427b6bacde43e05707e0e46ecfa3bd72f26dd0dcd0487db71c83a7275348cae3ecbd2255263860cb6063b7f80ae1189f966030ca1da4e230e18156838fc7e674144b09660f775233502277d8586f2d81582a1074287bd247e03420160c2b3e9a7542e0ffa7be9c199df46c3e8c04e2cbb49990e735cf17afa86dfecdc9d4ffffccfa5e2808656eb5bde2c9b613b86133d21640748258e09e0695e14adb92c9c48bf81f3dc5b6cc638f240242ebd66752a6c9c09726bd5b92093ceed83c4e2c0462caa3a9f9437ad942c33126f76731ff9398c0049275f1d186e86594b8cdca8b7248ab82094869daf35db534c980de8df27fe04d72955c77389f8a12d887a578b312d024a154a1b92b87e854d50134b9e29e50eb5bb752789d1993dbdbda5160c02bcf3a49489f959c9d101ba657aff043d7b0801683ca20213b780edfe4b5edb32e5c1433375092cd808f3aff7d8db94e81309b1a300a7abb258e417124a2e902d2790ad17d31b534de0475f74d998628b6f05504da3910e262cb27c35f06a724802a321262f2be26e6d2dac4f959b2d928008a22631cc8d75341e48393789a9527ce77452da8519fa394726b17d83fdeaa0008f7125d0e787c9ea999f0c9a9622bb6de26b7ad69466a0ca88075741d3296b015da065b467b731096ff052b2d22b332a811f5a35a13de51e363afc12355bca0fe065881920f117eace034e78c7345bd8fe56397816630fa43efd7227d67f4707df8293b1cfd588f0db13eeb16edd25e264e292121b25ec031c95cd6f8cec120cb79b0caee5b2fc39ab83bd6dc69d2deb40babd36b5ad647889252eb0f911af080a6608f0d76e5f4c5984e6dd2869f5ada91cfa0faa23182350f423879ecef8057486c664ae11b6f7a578b48e2bd4f68cb857716f3dee5470d08c9068d2c6c7095bd09bf5c48b5fb8bae4b14ab1eb28527935cc4f9684b0b3bcb443344d00f3008a08659135815cb29cc1c975c9af8c5c3631ab47700a5606a237835bac9cf2058f87e6478a060e1e30215538d9a174d0c9247104ddd4a1953639d693b49eb90e84ba9175388fd9ffa986d0b9eea4829bab68f3d347be37a9bd762d2a57afa9065e45e91e44114f5485dfa154b2dfd11328fe88da51883c65c3be1b248a32610f8050ad7f30c41b0e73db7fd3b1b7b89ddd25cf804e33f6bc23b774284ff6e60e219b8df25a1869713d23439b9d5661fd30ae9176a3bd4f7afa04933dd5feca0102834c162776b07649845a0bc8f03e2ec923097fa1e1c0f64831e2a19357370256e423d20fd5dd117f20e8082c8859ce148653c2694604e93a8821d309c6c903bf82b5becc8a905dcd923ac964882a119a2de0ae2a31f31707059bb8f11c530944228c218ad16fbd11285833887d04a627bf8eaf44c4f55d9671e72378bd55015b03192e5ca22a6c048d9b888def6a63653ba6a5a286e2981217b333e3bee403538d475bc8c9a03ca32a229433ce877093a3099886bff0bcff17a7bb3f4ba205e6bb3579533fa92154aed047cf14579ced6ae4a550a065419264e7f793595e0ffdbd40119ba7bcd8f144fc8fafdb1a57919fb207950bb842a8d0781d08cbf70a2430dc76d9213eba9da4d6c927afdd2b65cea338f2a50deaf1592374b76dea07e827f1f6d41cda7b176be2987c09ae7ae6bbcd95132d075cae27dee168d6060f6a8876b9ea0a7c5c241c8b5bdea491ed694f7c2eade495819740fca07ae590048da949a90964e43ad5be2adf0cbe2e865fad6f48c2b8e5cebc81d21b974a060f2bdfb5ca38bcaf8a40beba5c36a898721a74549e570ea3bf7e3516a7062c9406aab469e0448096d52a0b1a84066db8780746c01c30659132f8ff264122b1b008b029e49dfe3569bbbb92998c409326d98bd3acf5f11c4f2d647845ce2e54b0012d619f4428abca24f5255cbec003c91c70b72767b467728fed9daaa229fef60c7e182a0acb350cc1a6cbb3e9ddfa4854ffe7473a7d0f9e8cd1881871ff69990c0b20f40090db92a22c768d50dc8c5fcccdb7dfc18f96d2acf6c3e1374533dd0d1d65302779242fbf2406d92812818405caa3ce9ac3df979fc078a40e2ae9700c5549087db7dd29aea603784ee2e5105a689ab84857be90ac2e02caba82df080a3b91183bad553d7461409be7535a4392af205380fbd9c933c60e3b3870a89403387b5d6f33131b3c5fc251495045f7fc4cca8d61d386f10811ff97249e5ea70a1c0b5962332c27ce1d6f378cda113b9dff1bc08283b8493d13757f308889eb0968347fcd23b9fec4cd28394eab2ece663392bbc7b8df9f6f7c12462b1986640f044b820929807bf0d107807580bc6db0d52b4dd89b9395457079ff982961630d25a1e8ac911ab8bbeb1b61f7537ea0c48bfc6e20d6207e3add7374a29dda490c92a429ff6d174d086f7ba00ea22d9d14f3159d48948a696d5a86bacea90e2800d32dd0f193935273f09c498e25ef22512bfde8d8be17a0d87cf85086c76a010e0b5a4135cca42f2b6d053f16480a25653f9ff921513087028b40bb759e2de90197e55599327a5eeba01dc4cf5ef8c88be88bec1364d011e4ad9b860fa4490a078371a5b1017d7c3c0ed07ac256025084b63c1bf8478c8dfdb7bea8bba6abb30d180d07b23f6c055668146dd5524819ad3e3919fb7932c401fa54e76724ae2800c297191d86b1a9740d872eba9b27259b870c9432545d9bc1994ee6b7bb84d60eba61b3080600ab778e08564b76d5a2788cb874e1d2a176b907e205564422be0a3c1f9e07648bfac95a698763c42d34d345b389dbc629a34a61b62776b7eac80ad18840dc7424e930a4de4697c0b26a3a2cba81feb6e3784b88131dedad5fad0e1f5922daf2506c4cb6bfb0d2c60afce34f546cafd14522edb79dfd2077501f0612645d312235698c37f1e4ffaf36eb4c9bec18f89283bbf022b943f644c19d0652a413ad6ab38947a8e2bbb1a9b8ada2fc2fcda51ee8d163fc7cca74ab99e3073f84024893edb8dc5280720a729cc8773f93d93ccbe0a2506f68da2b213578070b693c9aa896bfb690550dcd5ab9494be35e14a7d814b7b4d399bda90c438008e4416081d79c33826ac671167939594560b3e56b23cf319942e68ac86163c9053e5a9a5a38537eccdd797f2ce5650e204d447ac152d6cefe448c95d54efaef07ee67b976ba89009d9c7da32150fbbf7e2b57c60cc2b77edcf68964f0dff1e404eeb5463f7e0a6e612b13407922e2b69c30f3b59358d52f31ce60f37e40798701f251d2376c87a530bd497e2b4a4bfe80c839280888ff1bda10541c7dd182bb025177deb3b9bf6c4597c8551cce3a5cff2b8b6332bc4677c48e0a589d0f46f5074ec287bacc81357cc1982b06c7c1975debd9ef4ba815c93dd1cd25a7ec985908e59d7b7d353f0c19b16131011275cc5d139de46510f3c64f6e9514f51368830ced8e08b9c33d0e56e0cd2dbf6eda68eb0b67126df0e0224b20e03c8ed5a5980f4436207c314a9af8ad76868c6dd0be717e8010162eb491b16bff18b3454e7f0c2a54c20609b403c3d52b09795c38286d8f08d8ca6665c1c9f1eb2d7a6871750d6a0faf7e45e3cca6b7e58a81124ea6378ce39b3e350780abeaa01e5b190393045a7558fce19385867f17b50429321ff5bf229a2889ec96bdadf7db29b2c3940e6d6b10e0b4a58890ffcbef0cc87fc466d36105f6614619f254b6fe895bd15f0048864b3502d902ca02d03002a2f2e91cd5c5258268185a43e51ba911419a7f05c9bb405342822338f13a0a4b6597dd779ba7aa1c293c858ca896badf74a6b50da4c0795ebe9cddcc95acdce55b9a36faa9870bcb4fad7dd694216ce07973040aac8d509dc86fd616cf50b96e0cc30bc9bd0a48a400fe3a16abf875ec30173f07af3b795a4e6ba9f70b36c78bffbe9f1332305d2db3b659dff4cbd6fd65abd207eaa9985c37648cdefc6abf1ccd6ac210118555593561c1a7e2ac49512239170bc76cd1ea7bbea3601035d9e173884f722d2657f3026552646a259498b4dfe60c1b8a9823a3b92c1d6c169cdd654bb5255066ab180effc1893383211fab1a2e0a2e65904c3797293925a3d4c32ee26194e6429c778313c518f9bb64ec4394340c2caab05339016465f9476c7eaaa201ac8ade7c480a059d6f291f32521d389d0402da00b01d55f131ceda07ff92c2fe2229943f40e31ae1c9a95a6e5a4bd3180d393115f29d77e5a11fb448c180fa7f6ef89666e114bac616f2001a2322fd5f022b389a6c55d1dd49efeeb32eeb338ba3f3f8358dcc0fccc17579ee9cc4b70a0ada47fe156c120395cc19e9a4bed46938519a4b274d413e96a4d631ed291ef70ce61d08a712b10bd11f78d9faec82e69609251ace440636fb17377ec6e2dfe2061fac9d0fe80c04d9e3b4d4681baea3c5288a06b007d933ec2216ed81c73add01c106acc2067d07b54306ac7e4d024ccf257e5647b62583fc6adf52ee766ebc041af8897f7ed6e09b4c9cbaacc99ee3d3a1f34d034a3934e9105d3bf623cba40a07b17514d664a009067740b9fa2e0e4e7a8014ede8a6e71477e2a5c77ed8840374bdc3ba5d844abffe8989f450dbc7256ecffc2a85b6217179c74628ffddb40c052b3193929fccb11941d18cd50ff1e47876dc7d10e4d48bd4261d696a8e3a007f245f9c48a8365bd64b6975f0a8abb994559df4b3213df56ae7ef13bbcf0104ba6408a7f3047b718a83ddfea0a76d954dbb2dd747ce3e07eb1bef9aef5cb0007fe30d47cbbbf0dd5aaea9df5feff1112f4d56c817169c6ca8bf8faed26f210cb0251f9cb82413f510c32d06068b942021f12cd116e61275b97505e752af7008b1b55dff950dba575586e08c3817b8c98496b3c8d6e04ada9e2c25b4b75b810ff7741130fa6455307291ef8ac8f13ed9b3c93d6478cc42370288855beac3fe04235fa66f970906acdfdd7756594a388feed42505390eb0ebba94e80daf126a0b78b226c5fb7da592058f9e624b5131167a4f45b30041028643ceda71f75fd20689077661b4f48801fb6dbc9a17437565d3250a85266bd0f3b92130677970ec0d8dacc889e16dac31063d2f8c0a64b3bc258485ec7acfef24d21325dd6a8be409daf37555212f985769de8aabd4f4987208524a08b310fb82bd2d77a49e32ee0d08e4289345dcc750558297dbb5ce64508b70493735f96c9edc10ee45b0fc6c0a117ca42176f288e75c599bc66b1b2f7c678d17fb9f649e49c1cd6efba829dc024246e2ea3db76fe309420f69e7c62ab339f205f3993f9e75713422e25806bb057b53eedb05630e1213d0fee48cdf09f9d6a561dfe3c856eb2ad5e19a7d5fc70064cce0d3a57732c6c6bdaf95605080f190d13592f22cfe82da94d0a634a8080090fe251b1147b8f3583c3af98d09b8c8f427fbf92e0baeb37872628bf5ff8f06663e2696b3130a46d05d6d850be0a781d16596d87c5f3324398b83cba840a102a12597bee995a49c7809b9a0e8912764b0963352a63b95fcf630dd2400b1bd0d206fadf62077f953d5dc3fa2415e49cfaa29f07ab2cada25a5bd82ea0afa8b0039896b299b694da5dfe5c9e0888eb7913bb4a19e361bc634da386a41b5eab5057fe9aa84b4a4888c82f737d5bdfaea35e25438ff1f5a7228491d93e2f684d809875bde27cae3870448a70abd01ad15c15cc253496e4d225b18d30387e7b9d9032ace4fb89513c8a3bbbd8c95b071fffd7527364330a4c4c73312a6b0b48ef90b0166747924511a378511cfdb9c1db2878e1b822aaefd2551a0652a85a2e18f041d23afff2c809d6ff422e455e1f40227d753c86e1878c98430e62bf9ed28850ec9f8b5760276bf4e4bba1b416b55dad0de5309713b067a723f8b0b741c68f00d2f3ae8a07a555d77b90bbb70e6fdd03f1460d2805a5167cc53668ab705fc9d057c5485138a89fbb6d88c52c89cb324a727134b10fef2bff959dbe3a29326360b90891be9325c73a69cbf8d918b634198de770c793728edd27cacadb5c0642e04f203107e109616e1602eb9f91b6f0cee7716387615f9eaf2c4a2cdac13a35009fa2603c2e7df7749f64d901b6a6b356c341d44a8c8489436cd1f0150afc00b0708ebeeb6c1272e3f840b2c3bd0a97dc54fc5e58fae98241c43f53f0a5a934a093d77f09cd0f2c31d5c993868e31062ad2698921b2f6f53d4cc7601246a04b3086f65ce2e21ae1775007a18e117184cf079477dacd79d8c12fdc4512947f343026b8fd5f85a5e2678ed3ec8c574841c9a5cfd582b8d7f1794f097ff3e04a8e00c1b05c9ea3a145c80ff849ccb8ecff88aacf3dfff6f37bfddfec05457167cb108b137eaac5e17922ceb0f564e06e19eabda5e99ecac8c5f1e0681026ecd3021027102d0fdf04c0593b69854b0468adc2b0f230fa0eec90a59ad74a95cb71210080d1c2155eb51154dc6b372523c7fd8d7a95a98724ce60ffc3ab5bd7511a86702475bb2267c4f632521692ca34d2c2972376708da2805fdc72b8f5cf6baf3490e2fe6a70a09d4ff4bec406bd1ca11e6d48313819dc488196d0796b7901c3ab8040048f6212b847707491c04445d6a5b6a25b6f6a6fc85b7800a5ed1d413b75c0a9a1e0664c9a8b2660b238d30c20b872d1c65b724ce6c82775611571fb67e850de732d8ab1751ac2f9b308f6e758263997cb12bfa83ff18d2a54f51335ce71108394233b6b5cf0924cebd82fd5e1ab5bc8f45a090fab15c09bb0f375ae127480d61e0223d001a07086c7a28a2daaf95255293285a56e414b594ad4bae49a21a0038be0ca7732e64364015ce9a271c570d8ee9f5893a50b77ab04e0b9fdb34b3069831eec9619b63d1525d16f2a70914ba5011bd92c586c9129790006ee91b6f0094b8f0c2d75aa3ea399dba73e54c73ac7afd02de9a6ce6a81d5d13bb1ca5f0078bb9ec2b63ba79b96df33aa64514184aca3a2816569103ffd38274fb9fc47f04289bab6adea974e2aa6877950c3df0103b302da4ec181892e78fbbf4483aca0f782da5a872afef5468d09635c24aacdf676d840554c22109e781eb2d09097e057a21b25ab8bc39c281ebc21480e6d9bc972e2b1ffadfa450051dcb1e5d637c01d0391506787a9cf0bed6273a3efda0a0321dbf48c5fe4a1e5a470ce108d5480ee24c3eed1330051c46874a674ffa202cd545f5b22354a9f91576dfc7024dba0642080a5e080c5b9454d3a0025fc80fd315e32d83f9677cfbd8e0ee5039011307d1d3a9e90184a12be9b390fac3f9d8242d4e8fdcdd50caedbfe6b73c9401dd0e95d112b28a604b94bec5dd802eb0db53d8ed849f81a85190de6e71d9894d3c04782e2923c57977b30aa55a58f7d73334537c90d263838279b939777a657b570a7e1d02c2debd71258676e57c804ced408187a2453f9732b811e862eac282690f6061e39d80b0ee493d603780a60e97398fc7ba7b97b853b74dc58e38ba53a104bbf6143269c99075bf7bb68be828857ab394b0427925bba7b7c881003ec7e50cf3706763308aec4c363ae11d6ebd93955a06105611d3fa76cdce4184e81e45067ca70f26da138c87643fbaa0cee071b69e01d589d18d2326392853d2a867d6056c3948721d1b1b489047a2895972917e14f50ba4e787d12a42b4cc27c43e830a88a6d787a62f27d363fa91c911215bdd9980c08974b720d66b05da3ea0a45d076e6914b5237006a05d3fa7319f690d22f9e08cbc81088a171159db515962730463d08020499a59cbb41d88ddb1d301cdedb7491340fe0cf9656daa03f2e24f0e36dbf1874d8f54ababf888410598518797d4b1c96358b9be3b9a4bb39b4787086605bf5b4e91879818f499f7648688738cc417269be27ae828c1d9edfac8040fd409057fcd2b97d8f024a6b9573cf9e5cd69346ca6fa60f9b9574d23b8d0de073e1c60b0a8db7dcbd964c058ed804b019061f9298b9e9e80d28d551b02e9630e2447e309497fecd794f5b430c0226c429ce6b5fcd586954569366b0bef476b07e4c1d6049dbe4838a212421c97496b9d2920d96f6ff94dff6fcc965551d91706733325b3c48a360fac99bc2436f612447a7e74655acaf68d6c0327829067770da573351126a1db507326ba31404357c53208386916f9ef8d7662e8f6b3471b01da0e2886169c9215ed1cb9fafd1da3a2cf00917aa7007f8779ec4455277ecc059d933e2afce960bd9f7222e7d1f4dde040fb4558f69048bf3a1b46cb78237801f91a5a0676436ec0b7b0f10b1bd89b790214bb54f64f5867ef8deafc51004d0f3cd89555d9312a768c49031940ee3e108ba6448eb24fe02bdd04d0916ba0b203636ae7e564274559f211d61f15528b646b5e0354ee792528d224351d4f5ef30c94e3fb756fd7334448cfde5c4ea82da49c587a914fdf69bcb04dbe6fbffdbf07f31dff4d0740db30e78b70f731bc361aa4c8fe84e28b3b0dc62fbb9a7defc20e14b9ac25d1e90d4188aa783f6bf5d11e2bd288136f8567e30a8662de1fdd7d020aa61bd50a63777c9099965b1825951321f901c8f513ff03b5cd9f2132a63b0ff8573b857a57798978fbfc1e4935316e11bf2dcf6d0a531b56ad312604dbd00aa8cbe091dd4b4a632052488e0b843bd302d8ecb77a39684b49564364205bcb09d4b2d55b6f2610c309f4f4153defa51a2c71f7dd40408faec0ff3397295a7508f75d9085940f7cc626c1b342f73f056885db0060ec6816467cb60b885486160d7e2e775b6254b3e13310a4229ce0d52ae7af95792a7bf11d956f6b77cf58eb0fe937453c16ae218429f759b617fd1c9a1b656b4dfb6e37d0590af9e76d5ddc04caed59fbc5cada04ceead6c84e3ee30cd46b01a71e25fe56f5d713735d80a00bf8fa9bfde7093f88aeeedab8968fd0b5a1d0bf9ce57d042853b3c79767c4c90e898c551fc62dcdd4ffcad2f53970c17881aefdefc7b31672e0ae7edd48b1c6000f13a3f63e0658ef31f2d9b6831062ccbd95c1077fb366ee5cd482d6f199b10be0da73b421074bb95b7d4d4fc77b27741a21891f4070ca8b6c47bad24410c40f51cef80f07ac0b736b81c72f489bf1c98d5452c9b1a376acc02f316c3d6ca2054d9ffd7b9a43309e90b0252719090073389dc92fb283bae482e10b00bdf9740b623f154f2e73c1012463c5d1bc1fc0821d4b73a70cf147c3044462b0ee8fac02ce9082e7552eace098a227cfa2bb3e770abf744b8b7c5a92358c853bb434b906d0636040b6a5ebae45bc63876faac43885febabf59c8d3c3f1ed817a158e7606e522b17a5f61b2a5828561f52ba7eb9c15917f07ef58dc330fcc27e9097e390a171854443c37d603f1f3d1e263accc5b219d9a79b7b415c8d860b9c3624b210a89dffcc7aab06f1d13cae86c3476f037f5209968e471e888bffb41f016d1c50ccd68610e402564580a4bd54d55dd850ee19ddd482b5d0994460691a62de36c0dc175138b7ae6a5ec21d0f2aa78393081ef7d638517e9a11cfed9cebb1dffd60386eb62ba5a7622d5bdc7414ef4ee279492488e8a06996c85a7cb27c06cc88e0318ea764c56bbcdff92b07747de1d96f12d29d3b0802182d7ace3c08f7fc9890bb84b992cecee38f8d251aa7ffea6071b1d2d5310ef322ba96f577997ec15260e11c20204aced8235b879d4bcfe9304a4032768324549c77726474ef6574a530aad630f393522c8e2e1a0e93940ddf368a7c62bd0ad0703eb24cf66db597a9d0d2390e9424e0f119b0940f3bcda29686f25fbb9b929545fdd075346eb88941c00105726fbb36b4fa9115c0359bd227aec612f9253188377f8db9751ba768076071881159d2f5d1bba54825afbe244665e4dfe7ea562b62f28b746ce0f32623601682ce2c3229a5ff14dcba206cf7d8ddca1e6cb93a29a414b2cc65826faa7e706fd37c4063cc19776c4996c87bccc37538d45a0c80b907b3720ce606c42c5850f0f8c3d34016dd6ec1762663ef3e83013b2c1655e4e4c20e72b1be7f069b2bf0ee1d4858c37ea52c3a2980ed79e78cfb6e86aa8f85686299f23bc1fc62c55570108992b33d13df169abd62a25595453e57b5c6e1c2724965c06b7aa506fb4ec095e0c86a73db10b2b16a3667e045fbd8c4ae66b636156be75418be3f71904c1089056026d2c527458eb04744b316018d3bb17b8acec3a0f8e919bb259fb88c70303c51da13dcfc9a1fa8e5c13ad5332b30ab297f8c71020d17d97d4bde7fef30c3478d97a496667c876cb4575e00868ed4eadcf01138450aa42287afd5f188000d15fd50f43e01508a008c4f306831995dcd88cd80a3bb9d133f7eb7112b10b0bc5ff61a955864b72309d25dc0e45053bdb884ed93e6bb9f0779a225680648b0039bf608b39c3da583f5ad7670077afbe3f41279c9df7760126e74ff5055cf20e0de870fb41126b9da1e1e789fa09dfb2cae73a5f3ec2c9c0ebf563e7502f560e0b24fbda4ce29f8805c24d20303e9a3a94772a57b6668c12340391b0a466f60ad6970ea7a5813f77a20b3620b9e1e433f003ac04988c2454b776da39ff89920d9d5a20fa2b3d4bfedb40d4c384a91b719483af62ddfcb141230cf957de6b5e06cdf17130a8c3233aaab69a4c47b3b67ce20d47a39df64f424e160e6cc52e210f9409c8ec5a6711b97f20096ec1c3f102d4a9daaba9c3f867dae846cf11583e028fe0738057a8a0dcef9a6ccf88510b7dca83260001e3253092f3ff25791d6400880359ecdd45c79f48a2acb125552dac5e5926faf0e7262f795704a61d3ae2088570a4dbedc6ac3b6f58639144faa2eb8fb09a6f1f5a8ceb42e1173bba76ce07bbe535d782d42d4ef315ccc95b92cf8f8dbb9e8322b17730cf7e055898b9f0096e5dc2ee04ae051dce7e9a85547f7a85f7fd17d2285d3b49ee0695b95ee6f401440f8529ca8f2503f2169e95de3fa059a82b7eb175555d4c057e18487c97f6022b2ea08c73223f41b5179cf0a5ad89314f12463041e13d3f1812bbff34e923008e684d70a5d6887fe9648ca59172d1ecdd396f278476b9913e029037dd9d9806e3c6e1031cf6e76251c7dd239b9110ea6fed10572bfd402d8e4505c8c689ef06decd16ca06e9ddd74422d0105720b7e89fd94037b3ad11118690d0c7cb66310f2b6b355304dd4f37f41e78c02385bc2acbcefd4639f73cb867af108cd9d5e400127f2c204dc5aad3e7e71f8d7eaaa4ce6a82fa6ee58d2300dcd3c5b2d09a43099be2e7eaa2f85e53afeb9a2c104b1058bf862d99c0c0fcc7861fdc35ef5a710e45c496802905c34e71a170f60c0d5ff5afd149ae285bdbc1c61f1777c71fb001bc4f0d378204307ef691a36e5b3bca6ca2e1c45b9059a7d2a54b7245a6ed540839976d0cd7c33dab7d617c22dfebc09372cc3afd54e6e4b9ea78ef9d5a797e07fe5689dae6a53c2a790612d53ef918df706e4a120906f56f7bdf446d84733c06319a7cc3712dfcf83eb43ed60cc79ce7c8a6f1e1949d7ae10ba40cb6ecfb290bc7f1766e342f4dc52b59ca2430293e94ee1fd8e6a49467b7d05f13e7d7940709a8198613d885e9cb81535d1bb823f37a751871ff44b2635dc251736eeb868606feaa52ae24e14c231b641ae1c7e224f3c415a4acb46a1d358bd1c17307ebea0aa7dbde0f80df0414b68c1797b5cbf208a2c1cb900005e79ad08632247e501509ff883b41224ef381d7fb414e58e6e4796e5da5e449baaec1af97b14a1e44880d658d571ae26ca9b0b9f9df5ef7cc5ee161a4c19768619ebd95c5c0451895a90912b02449d9f1242f7d57c56a07dd6cf7f261954ebc33837fdb3ec53b443c990128e79f778b674e83ba210e8b2f0146d6529b348a2a884fba193a00746894de092e7c6a084202f8c9ee82eeb40de38d1c92ceac085b032345b00dee21f4f3c10066f6462ef5ba113918dcc1d785c6cb095590df263ae60660f9704862554ec80e2bfd353b7ce3ee7fee8c27e46fb3307c4ca19ccb3a76da4c242fc2d2850fac0961e358bcec2d97118c1b5337eb35746c0f13156682d5b20eedcc23f421da6001ea5cf0aeb54ca01f457e9e5f8007a24be9a5e6c6f644030c55134d25c704af0b2575a5d4df990c460edeecd8eaf7e18ecda4c710824cebd9492a0d4398a0790d4660854973ad4311eb71a9cc41c97f6c3cea1ebebaafc7adfa53bf9736444c06ad9d63355882eb4079d88c79422a1b363b555a413803dafb9844cd7716ba3c0136bdd402eef6669328cab8e858fc24d831d44f6f35b6ac15abe4925615fa570fa60203ae74cd04e55d3940dc223bcede1984ef9b9c9bccdc3a6d020201381200fd7545ece2194e831dce2ffed5221372ee9fce6e74063e7256b6681e3ca2f00b9a4622b907ce1f05c280fa192d943511354e29146854331554707539f4066d0c09899617d3bbe68d3d881ad6826a592e972ced5c50aac80818df150180e31b04 +generate_ring_signature 70b22a0dad712049354729ee250c77812c8d958133d282a7ccb5c7ce404a7b18 fe481951dcfd22fe788830a0710c2d3aed379ba59ce250ae71ea46a62487f0cf 1 00677c75a2b2cb1c5b946207d8f6a4ad98d9adacea1e5481fb84af52da4c48e5 e6bd4c1635814c70cd87d7b1e256e002a954c151ac5e0e6cbbecb0ab7cf74801 0 bd3eb811cf89f0b1f6e6af53c69bbe51f2c3330395ab3df88ce47f7e3563af0de58ef47505a0845bd45366c1a8dee5712614087c96146edb5288635431f49c04 +generate_ring_signature 70c5e47ad8fea9638668fa564ba969be479001f55eadc7869b102d77b5cc5214 af2e9cbb09401113f23399722b7118d38070d67810cbae3c18e82e7b7da6f472 206 a18422fb54e5bc1a14964fcf02ea976ad907a99c9de543aaeaf998f049f92d4b b51182e7531087236c2fcd70ba1c99604a790e6543540582a638652844f962a1 6f8b2edda8e71567c9961ca07b818c8dae7288aec02c148da0bcf1fc96bdf146 7839c78aaf168c645786b1e646261a865ad44b0a83024f719271bd08e9db5cc6 664a1b716e8f6d679237984ca759520ab88db8dad87bf34d581c61137f93044e 3c3120134830ece01517323ab484002429a65eeb0bb17481bffa97774d5aee7f 933409cfefb692004ee42f1c281c0451050964c115bcd81f79dea07885f44948 ad6921f89907b538f3f08e34c1d253cb0d5b8ff668f335a9cc6e718e9964d170 1c2aa68c5a31e9fe7bbb8623570e67bab36522659cd88321c3f1f1dc468cd8a5 66078deab22c8b165e30dbcdf5b9ffd5734f7df003ca3fd515e535107e0deb0c 4330428b7fd83c847e0949054d66632dbb03bf17f9255126175d6d551b8acfae c6a01a0bf78fa2b335f0d5eba32fb0fed7a5453ecc58042f97a57abb4ab93768 36637941f031cf30f9abad0afa6ac9df869c4dc7239035f00b2536c83eb07f6e e582018b9874cb88b09f9e30ec9b471cd154ffe4b79685342f4959c653c7e30e 9d988d6cbacba45ce8492043930ada21ee880e0f9f7c2bcfe34a978787b7317b be59db2de9e9c18d852a319b8021db050c8684e9b4fd27cdcc70d7972f262235 701f1b7057adb0d1a917d09f9703ca267c6523a15a42d4b2903d1c68bc9dbdf3 ccd77941930d74002e7be55e8cf6b51b2efbb534250352f9841cab4b09d6101a 9bb62944367b74b680de5a491449c5f4c89922e21999e4749c63bddb96757b45 d4f1497d5e1602dc78b67cc2410369ca0b65ca3f43269d48a505f35794abb7a8 545c4f9c95f6387f51da3938bf05d6e1dfe24ce7f47d00673047c6d15b775b85 9e16b849e343e81d0a8e4856e5b61f24e5839b3aae6e3bed70218a831b7ae4b9 6b85c3a6f57abef1ee42b1929198b80bcfd590d2927465bf86a708a8d8935016 45702a3b1ea9f88fa318ca774673c9f5305b70f81eabb243ebc51a4d456a39ff 14e17ab717131d76747f7f89529456ed4b7e0b4bfdbefa66b479a1cdfc2cf1c8 b28bbaebe6e5a2082179a5d40f82b858120207ca642c4bd208f86d1981e968ac fde5f21d0002ee5fb68b31f336de025dca70152f8fefc8c96d9369c8657bb45f 6b6250e0a9e114274c470d03b3a09adb267eddd060fd7180a3d59f2b4c84a255 3f912637b519de978a550c9ce554c4cd8aea77b7c67bbb4b61b7d35dfcd6cfe4 2030f7603dea048af9075f8d5f94503afc5e9de977d18606915200f186c7cc91 b243f8f735967e6d2fef727ce4ddbb07260033eb06e22b2c2a6e7c573ea8b957 a4d61eeda78d420b95dff8eafe85b94c4876872f1da29b344d213ceefc4913d0 c6f13c6b465dfac7794fdc9b025461ee81a20a5e12aa1bec8169cfe89c66ef29 a4653ef4b4897b15a9842bff5c3dc1511ee1fd1a4709eb9bc11d479628b5a49b c5b6f5f523e94d64365318fca8eec9f9b69d7517b9d0516273478024c07a552e f9e16f2e35b355a188119e38acf7bc16afc4e0e239fdaa97dc487b9789ad2093 8e32ed09461857c41eee4eb6f4697107373ac5bfd404b2aac521aba53c1e4606 da10a8ca7ec34b14f75c82fda8a295339db1a95dc0ce4d51c92dade911a7118e 00ba18bc2201748eb0ac8a35142ef36a1f054f45cf328ce6d4c3a7399434b51c 77e93d9c6e4d0fa69e03244504cf715ccb6e1771a68c5a4bd01e7a7ce1e89ba3 cdfdff3ba97751f3181e617d7303bf4d8dcd5163bcc40af5390ee071a729370f 36e2b4cfaa03c516f3833c40d9d8ed3e5f123f81fa878aa7f56ee21c687a1c2d b978726e14c14fd17ed98d7124feeae05714a8109e1dbc14f503a7f04178ac0c 29dcd31fd684e980ae3fc46e5da92f7f5e78babdb806087ca36ad8703734af60 c1c96cbb9e90a08dafab66240ec2a427c9427dd6cbe2c649aadddfe2f2cac4d5 ddf16af1d9f17774b2d18a01f21633c88e676729fb92ad1ba2f5a6e4515faf3e 7f3bfe385a2aa865c30d151d1d70f7780b9404c3ce0b80cd8d76ae0bfa2d48c2 33cd6755c2a765f34711898c5cdcaa2ab13ccb4035809cc714b8ff1b49e312db 6a9c43c587cbc9040a1bbbb5ee5b31b5fb15c8e8bcad62df9ee4234c0137b330 ed1e2acb487769fcdabedc3a39a4a33f00689ba64d2853c247f1a82ab5d9c900 a7d0aed414d50b92555e68b1627c84600850b2f67023eb90124900fb54dda7e1 9c6072d044134b540033a836fd2cc23887a8a82559355d7e05934c46257ddfca ff51af4f58762907aa34f99131a60a7d303391b059d05fae1d4d4e52e9a7ee12 ef073664eff8f23d2db789333d15a35901822b75146bc40e6a9a60bc348b0f43 b20fc9ed263e55e0d3b17ee113d43547e373cdd165339861d1a5f00824954bf9 da9614943a2b671472b15a1da5689b2a22503e468857e9fcd5a80c8fa0616927 b1d8c70e8927f0a0949c7ccabe0f8420c7ed3db8d0fdef72cfae6c34ce3c236b d2229f0f33a9fa68062a177416c13ec529f36dc95429c6b3e2030e7998137076 1cd7d9d9e748868bc060399946dc24d156a1b392d6894bc1ecdc54874eb26d64 870f6592e775e7787cc3141ec39a4bf8fc40216d768b215ed2c37c020663f568 c7ccdd2c40f607a04ea79b14cc3f0e1087c1c1aec9cba9be3e9a040153878cd1 4efb40425203e4db182ce267e1ce8f9fddcc7dea9c231875dd2b477f6160f93c c77b8ff4f3990a4a716d9ce40d9dea47d675d4e2e8032eb4e158e71e22b593fa fafea62add24d9bfe2c5678c3dc32c3460686350cac842c68ee8c9dce4befd5a 0d9d2879be4d5c54549a652a0332937b19a171d08b87355a057c155a49dd3511 7d37429fbd0fe2b2188a93c537c7e55283ef4fccf2ef28dbf393039edae68d1c 9647f768f127751f8d2e8df5bdfcfe0b50ab08d002b859a36be77319d2f626b2 25a2befe5ecd5c5afcbee35e708c513e9980d217b9982a894decf8e723f4267c 49fb16a530ab7e69538cd98e63e69dc602ecf3422f854d0bd9dc0c56a6fe435e 9a4cc03169cccbe20d82c8d1c88cdad2c0e16bb32c22fed4a5325ba24c5da531 92ad7fd060ff5e009df4146670aa6f3c48add5eb9201ae12c3545334efc68d8c 5508d45bb35097749c0bddd3041a15f3f30a73f8d8e6bc26edca97d2b3ed8eb3 085d6e6167b1711a5f0246198a0ce033445c56c308caf2ffb67ed4bf492a760e 814453420ac7935b45302b60a76d60ceb3d875e27d84181cd995ab0cf7c194a4 66a3b5485bdc1fe37020e728315e4952d501260adcfa287640a15e4ff29934bc 7d3202c71e8352547fef9f2effa942969472ba6b2b711685f1e7ddf767bde373 c603687740f6353258d1563ed82e5e72b7ef7738425b829089e044312668092f 78a2b79ae83a3be22e894c61f9dd9944816749201ebede34760f0d97bea2d29d 77915a974635dc324949da90d5a6414a9abfff9c6a1fd2de2397ad515ed1ec4d 394e0b6cb9bca29f0889e679730af807cc382f846291fecc23ef891bf4303352 03bf7c7d9e5f15da8d8f4d8c8242eb7d0f8173f66c9a709273e1803a073e7c0f 885a745d8efe981be6a9aa5eba6b24b23032229968d62ba2f680a437051957e4 bbbbcf28ff988f39a47cb062cebac6a1ca52cca0b7553f1e64503c9508ee27e6 e4253508f5c9313bb1519bae17f58c685d49e4194ea7b369ad0e079878d253be 33e5fd5640355e45d35e4dd7f1e8e7cfa2998a9ef48c9c887fc31046881663f7 8aab3224dd2af87ddcb5831c1282df4fd90b353f4269d4da50252aba203d19d4 be6533d22fa31ffe42c80e1a1eb71d65bb27a8e63c7f382d1a00c254dcd986f8 9c0652aa08322b9db87e6b8bea6868994d9ac10fb89a5c1fd79e6cf05e4f451d 99c615eab0941b915fcdd01beba0f79d841455986f64b211a74631c04adc45f0 eb7436280e32dd1c1fd11ec57d44e4b960630debe8fd3fe9b16f7e25455153b3 dab838aa3a6646ae8523bc8e612c5f76bb5cd21bae09af8ae035434a9545e1bc 6b0f31df949cbd288c5d24063851ff1c107938cba425be209728e09dbaad55bf 83eb14b50510e48cb3f336fc4aa0003c1fd2dff27e26484c9e3cdaae21e0f73a 37be5905a9f43cd4e56d974dcfd016461c3d066d3fb227d6411e942705892208 53963e49426946c393d7051247a746783821d70b04889a094a5c99f0107a48fa 3797c08b0780ab73b9f4897a12174a42871968f024f767ad713f8a7c35995b5e 35f08b38741b662a557627dbb793e4ddab03a144afe29f15d2582f528e3f54a9 34087c19447afa9ebfba8b6312969741fa38dce9a3c52294c08d949180cc79e4 7352d684d1137c23c8d55ff9c01d6dac19ab3d6b8f8a30ebf5f9789920e5c3b1 d20ac3c612b2b275c0c708040418562f80ec07fa66d5b8bb166d115e059421d1 b34a94287b0b92f980fdca1985f12feffbc33e22194cf83f7c64f9db3959d810 b00a64ca8cd8d6dd32d2e23e22fcbbda2c979e3a25e095d9bc984b75e29263ff 88905bf24369828cb2d7789ede9efa19100e1de11fb23fff84c5f919caf1b200 f550c0946b5bd38adf6cb83ee5f66a33f6df9a352920a0608cd6973294b88591 b84f4771e6e6bbfde0ba6380d7b0adfd745782a79c665ef158b35f30c3f2aeb1 6125547278feb8959d7b44022834dcefacaee1821a2b9ad98072409fdd1fc5a4 2bca5c5a2f52ddf22fb94c1ed7f094beedec8423c2d908530d56bdc6dd8b33f7 d122a98f60cbfc9afb08c4a3665a4297bea2e28e1aa14248e534792ac9be9754 0cda988e49b35ca39b860348c499ace34a1cedef1fe980bd791e85cc9a3eeaae 50405db106877d4f79bba49df0d00186a34ba02918f38d10a6772a08516f358c db86f45ea5f440499c1fcbddb9e094ec839219d90ff65ec5db7f00e401f294aa f2b186741767357374da23c9c787b00f7715f6ddd2bc13f4ce0c7fd8af871af9 45dcbdacb01ebee6cdb99b6bf299a327d7600f911eb178b6b040e427045893a5 7c665eb4e236b8fc0df26b4e7006ea202158ffcde5bcb01bdc76955332dc4612 e5c13f2bf51221881634a0b3be87f9bd3b77ca6fc5190ba73496de2025d9e9d3 0f5d6fed8deb031948aa27118f24575d129130cf4d54e8c03bdd5b403077b9bc f5f83a10c8257b8571056cf96b205d6e29ae49a045165f9e81661ea60d210b46 04f5cb095be125daa3ad6f020c25bc4eb60fc210c3c55ea6975579c57704e006 310a935599ac84e860c339e951c795d560a8ec6d0a9915e36f54bf7476bcf7a5 02e7263c5f19528ab4807e5cb0de10f754189d00768dfbd2ef200aa1ae8bb2f3 9e7512f100cd122c07927b9797b694e4829ecea344c5aab64c31e2dd4fcd1f4c d5882dbd1118a46af3c624b569edb0f66d6c7eebbbc822a09e74210605d05858 02fa26513c2e35299ec5634188849a3c7876f283d8e6d95875184dcfe90f928c 7c1af6f4bfe0fd44c10c6e17382585eb1f6f6e1c0c0774fe66517839b4e74a69 ee9ccd0b683adee6b6c99eb80bfc58d50ee650f2089b23aad6a53d1514204d3c 7a61e0013420f41c550184125d3810897caee0c1cdb035921461d30c070f5c44 5a3b4acb99a292959c09c3bc164aa35b8cfcd80e8dd1e5a0ac18b4b20c1f7beb bfccdcc76e71a16d7cb5cd68daf3c83ea71ff3ded30cefc9a574d75cd7ed3cd4 0d37450354f70f3421920262eedbb2388b4b3025fa7b7872045e108cfe506a69 7769a6ff5204841123e2f786b9c438157a68763baa0ef3b911b1918dfc908fdb 0281057269fd5c1792dafeb5dc50f5fffc205ef14b1a1b495335d41fe9833641 eb74bddbde03604204f8d59400fe0977d2fadbc4591d25092dd8507ff1ca0441 4984c0958c38d0bf7db3e714963b7ae2be3f14e4247aaf8625a1206bfcff624e 416cc5e0618e97a86a868b745b730cf5082736b28f158a15fe97d46dd18943d7 c0334b9b1bf615d4e50d93c5cd68c004f616c96448640f96abf15a05f259b6f0 72228197fbf42e0bc7fb85daef82dbe1f5e2dd7b173e80b590bc6cf985d5a065 74d54c40ba88a6e7e407696c81045f77b7ca443b5f676f9e7ed2133004c220d1 da36cc9ea4368c90ccbb962bb33c447d7f1c57c6b5f0073f1bc1f453a58a51a3 29346ae924c2c641fddc37b5db1a1cd7d8e8dc6c10f397ac1e4296fc50f957e4 8ad915dcc38e22bbe3808ddd979703b55641e5e082d922ddf95eb55bc1dd1c99 f9602f4dc07dbcae954cb895a266f64f0ae8067aa0de93c828b0b63ef77b1286 127806dd2c84355c8f15d5542dbca5d2f545bf7aeb97da34d09ee353c18df7b4 4808e46928e4e630867cb664a2a594e847858220ca93bfb33e06ebb991816f60 f45ea82edbdbb2b01bf809edd7ed8c7b5d5bcd610eea3e3e65fd3d2d0813e738 b9cfcc23cc02517f75dd3ba4f05fc727681786f67e6cf81edb3bd39e945bce60 e1f5bdd3eb6c2b9315ebc2fc8a2265b6103a68a966992ffc100e00a6bfff91a9 b0fe946f3fd0a6f8005f857f84d96a929971c37caa1b7ec7f8f76781c10f7659 a0f943eaa5354a75121d68f221d7f4fd05820d142c52ba0e613ce40a94ad410a fd4885348d62e21aa70db54056909d38d6a0434958a88bba1b00bde9b5d9a68a 81f0f73e215de76d5d9395ece7e348290fc38c254660d07e5bc1ee3af399c349 6299a39b5110643b1ca175cb95efa2384e6ef5b097e104d538f791b61c934039 7107d2209213b68af9ddf368bc4cb14a7eb4a97c2d12cb016fc4829df353b976 26e8f097cb2958051fab454439aa1dedb6929a218d6ebc0ad71ed240288d5848 03c22c8c6e0d2b2566d02486f8d281d47757cfb55aaddf6e9ce709d0b434bdd3 8f0851c2b5c3ca4878ad6c9b7bc5d0961d2c10181b346bd04024a1c3c01ca783 49ea1b8ca3b5ff382ae05817952efe5b1e492dfad8864520909211db36d9a3a7 4b4077f01a37c8c41d0a841b197f90bd81b0f60e8e85ca241ebcdc75ae7ca7b0 c5094f0a05bfe6bf19b8aa4294ad8ad350c9da12fb2349e19f20fa468a8d767f 28620428aa5d407387a24846a62cfe882bc32222b1e0a11e0878e69142f927c1 469a5d58cda2b621561089a5a84923527f3a7c34d73429b32f12165453fe919c deb95037d7f978e00a400165cdcd21698a9abc5543a86a39c2a3a2a38df0147a ac3a1f62cb0e149eab665764aaed0d534c57bdcdd61badfef5e8fbbff284da4f bb95b6728b9d49af7954d46440c0a38d95f97dc319a6bc801fbbab115a9d227d 51e67336b6a0277b50c6b8c9461b570341de5c545d6ec069133fa52fe0fbe96b bf9ecee1e808820f1733fef3d8b26831fadce90ef25c2c9e69f4ae1583c2ff8d c2ccafa1afd4a0fca65c04cf04372a9949fc82e1cd44d3fb62372c4eef025d49 e6df379d0ae015a6e363eaddabc993ac719fbcbf0c426923cef3ce181bdf91b5 715a8e056a708fcc73e370b8c8189b95be07c48f029aad9d78318bb4211fafc9 33086563a891bd5405a0c4e6c823aa6caa40baae56af2987d0fd5926b7bcf92d f135dfaf3da2b5b44d02fa04f5808eb9d2f98908d5be6084694a0d0f0bef6b6e 86646a742b57067e655ae93ebb181f34b2bf68226725b7fd49b7891c5e8d3c0e 76f0c9c066cdaea4caad46ca803c9aefc3a11dc75aed472479652f6d75f9f593 4d1772db532f9da6aa39d0644867e5ef9c429cd7986f97eea7c253a0e43e8698 57bba026eab4e72dc1a9636f0c63988275e9eb38f66c56b0f6d998fc281f2f0c 628eb7273a6845f9636813f810760b69248603686f21ab695f41304a5259d7ca a83d219f94eaddaf2bbc68d387db7d477f41a6fffd90b307c45342703c987a8e 149807be6e62c586c635336a9cc8e1e12491ff04528da3ccbf4c62dd49014d72 5fced27002f066f6e03292c55c36bc9f4e5be911738f25f889f12d0ab576d690 619142a6d9c825b6b874b1619f723382fb91d1cd94d59814adb195b2df8e2afc 46a9f5f54f2c432884e00bdcf31a1f2832aa3fc08ec5d613e39be20a8e82ace7 1da5ab4a03bb087697f2149715033ecc9fa93f3503c0323f2a4763ee8c8bd51f 481a87a30c3af37b339dc69250c71ca8a922e816667443710671a80dbe52ce92 6f72c0e69adfdaf0dec11ab94da9e9607fe1af9b19d2942499f2f3b06c70ec10 3f27220ae74e591ed12f7a5e53d9899dc3016346747d8ec9fb0d8c6bbb7c2a62 7be4d47e1841078f34a8bceb4a63e3dd7d84a7a15d2180fac6c8cf9409ce02d9 4d781a55176f8de667ae70e9c1bc7cd4fd263930b82f4a4f32ee12957d19b4e1 b5df86e2b91c3dc2f216c0c9ab065ffa1ede0d6ba02fc98df834c77d65b2ee60 448250ef38aa56bbd00f29317b2a48212acd247ea64550dc411ad9b176f129de 90899eabd9f089088d271399c46d2550c63e6b3eee2ca47eb1a1b01220abdbba c3e9bce3229262c0e60a477d16d898464d78d94b5ca4ecb64f769ddf20c5c440 67ca124eedbfd419a80a46588fcda09e2f5fe336b8e69d989c210e613a82e724 f36f0c8aa0d5711221f96dc637e34e8d4cb79166136b77637401449596507b8e 5a7b4baba7c4e9e7b0bf849d2f3734a5b822f30f132d3dd1f262ed9551a2bc58 a528422be9a71e011bb84759bae4421c53e306cd112a78ea9f79f1a2b19b6c17 8a80facacc40636b9445e719bef3a4f732e26fecf9fe9754919ac27b9c53fdee f9cd67c569544c62a0c73392289fb7682d727ffd10e7a79697a4433e06470f8c de9b69ec46282341a42fbbf8de6b54a232ef50cae2ad1d774f3630e60c8eea92 929033db79ef727eb67d6dc4aeea650c01271b3d2a6873afe6e6b0bce5650cba 301ce60b3754afae20c3deb7ca76dc542dcf5e3c37650258e11f666bd9b978e1 b07597015e477302bd1f044c547f7246b74de2e4b069de91314e7023383e891c f6be00b7a0bff65e3d29bee8a690717c412bedad30eeb1b9623dbb7744fb72f6 afefd6e6e0544512a57fd39de6adedbf6d980abc65de3ec99f89ed06315ee378 43dd0a609beb44b6df8459e8dcd769d48e892897872efcdf1783a5a913f5dadb f765aea469de90af8389652bc1cd0aa52fc42e57bb8e546e1618d818128264cb 51e47a387db4adeab17ef6abacc440cea6fc47e00dd448619843ee6aca670536 b2027fd7a8e36de5a752b54332d2449ead5213aebf6c678c54c91e35e6d87fd9 fbc0102c91c3c12182445681f5f123ceebc65bed2d9932afc8a2f6fa78a04b0f 48 c752903af2e755bc1e8858338e2b765c44f9404e4a18fd0f91b3596c9271f406bb75e09fdfd2f4904455c4ea70b054b751fff7136a773d93839d4f02edfbce0e56cadeb1b6f6eb529d8242cfc8994a58337c28d01228a29d31099263f05e8c09b807a97e73e015d71e133b986f5df9282833690c6536db7e7379a747f0b48b0a84e235c633b3331840483b7a8702a6fc3c0fedc43d4ff22e0da1d018d389700ef635d82e8b5f530984a6af95acd2d0d0808d698fe1538a9355b141a9da3b760c8535800cfb9711700786653cec731f1774a04c4a950a6a109db39e2d6510930210e6345da5412f62cf67a775b3d62822e78c32c9cac5cab36f3f30d3edc1d30f00324828b0ef0be92bdaadc599097bfcefc43e4e4c3624ddd7fda7b52a22d70241e3153bf2b6acfa043eb9eaeb98054bf9fee0c0f634c18a3ba9eb34f1279a0bf6bda8447a37ffb5964d43d7dc3746e680f9971147fc988db3a90e80f0a5c10d1ab067fba00d24c912db2a7d9efde5b169130f124c609b2c2994334d9ae6130632dc2b46a184502a09c2968d07ef8dfd6097ef014091ff2ebddea5e3c09efd087a2ddc5cebe7f41f8f54bbd5d26b651291aa5f53a3a825bb8ae21e9a6f78c501fdfc728e3591fabfe66f1f3a00295abec0179d9cdd9e6d875621ee870c14dc041ea89f7056e2344698cf7dc4617f694284d8b6a2d206bf96febbb49438df6c0dac8e19f37e64d0e792713f28e771dc631a01ad06289af2de182282f6a1e89305beba4711b86a82a4ec54ea0fc3226f07c2966cf908c8351ad685228913863900cd07e8f7ca27e1e5c2117bda6be169dc92a392031af88c52811d169ae2137f0d1639c41b4739d4691f8f57d467945b0b1dc7d82459fa678f75debb643b2be20fe216298b5d526c1f74b503514ddb32b4c243c10813339db16eb91c578ed1670db4a9d2e7fa13450b67e317ced5ed3eb4edb3b848985dccd452ab6be85d29c70bdcb1d40b3646753c393d0a93c2dcbe9b05772f3a2b2701c30828c541ebe0a50b54df2180afa9bd2ba0d8925c8dd9f4fc6cf66bc6698cab1d5a522d57f90afc0e250ce977176561b3e909d7a2a2383edc06df64490e39b503d5f6ac44a3778104ba7674715304b05df024969725c024c55f01ef131fb9a0048af416d68cf8130dec17a7afb80bc03912e4b31d2eadc915de1bba343c007b5b5373cc32267ce806dbe00adf2c2b714332b3eb9e9102f20f2aad2f232a9b46d251fac0cedd64bc0764fc6a982a589d68e0a8f787e41335ae22e6a1a5930870ce413ca7cf86d13f05c90b6d8fdbcc74c35a34c24c73b936f918d5ed83b4b7f61794b033a5f5e81a0fe189252508a23582fbb86b071d0de463923eb699fef4e9d7140b4a68787f0f054a4d81f7498be4010ea8514782d16b8a486d0ed80a7948bda0e0273bc14b2403496513885c5417bf007a5f93fbdfba9fa3ecd5a3a871a77670a5ca794d7c950a50d316b3350a4f1f0ca2ffb150fb729c88905dcdc89fdd6a5d13d0f84a8e7e0b94d15f66d5270f4dba5b7e809bbfdb8cdf50e0d61311a1d6e5b5c94f5901a60e9ff691632f0aebd28c989ec35f7e2f4e234f7cb9f2961b6211c5563ea95ed5063e8ef823f742950b2b93107ed3fdca7e259225c1a5d6d8915f2af9da37feae0df0f4a7f3efd4cd358529cb0aca99ae40b8220cd0b14491914a5ec266556bce0d571605481662abed71571f011d6e7c9f31f94bb2a2d48126d8478e0d49ae2004e5559a482a01ae8c1ad0c736969891b07d17a878535c1bdb572bd08b23acd20a88128123843c4db57b52f18c880f3e3983f93df6e1b60548cd65246cffb118095bba492204d33367da8cdea1439d5f5b70376d689f42b1ad7aae4ae71e658a09d2db14fb0473d848dda3980149322d3d0581ef979b723ac8cbeee5ef6e6ef30c99ba14abff111fd533e31dac4efeb7e95a52883e9b2e17dab4765ae77b6eef01e1ff2abbc2bec91e033c5957b53255182492a6e95e4c3705c0c95b4e37eb3b0aa88e306e3e2e25dae952b8302f0ad9a635655884e2edf73735864b0814299f0743a6db7d478219fbf168fe10905c6f868300b7d976c04cde92da5f82318f440bc3fc2328e9c4eb7f4c27994cd45916939c0b4eaf610f1670fad5ed0fde56520f8ba2093199fa7050e6dc397c68a1628ad0526ea89313c4c83a52fc6165934302d82568e965ba51758994554e5ae32169189aa0295d3452487ed949b2eed4c10bf63222b64e8a7662643c115a0ed6abad31df5b8f59ce6d1da8ecf4a7cadc1e0b1a799e0f7c3290e2338c17674a51e150f609de86a99ef91b1006297b4c37f80ca43711dcf7b6180204e14ce0b147753d55047729df62bec151d2cdd24dfc540f14bf6e1bde71d4eeb25ba6d6b652a8f3f9618787cc665be918f1759601720c0ef386e71627c954605269024bcc08dc713edc59ecc568edb6af317cbd9448c30b5329b0c0d950b0e1a59a994aeae062f6385d638ef3af45b9ed1b121cba975105a042b692c82b722cf674ebe787980a4776b150337ece086d26d13a74b04b4205b2003c5819029659d532ab6328924429cfd9432965e9caff62d5daadd2d5ad00b95f6f1284a1a276dfb75791d92cd4dfb75a497a84979a18380d17ac4a4e290691032287351059ebc30a157a7828bc987a271aa2a094be0d6d27e3b5248e9a035e1365092ffdecc6c35b5d6180c32b3f9ee5f98a3b87e0744d598534b5c4980d2320c9b07c0294d8fa323585f7d61f1eef8fe92f027af2687277949222566606305c8ddbcf05bca04a157a9513642789db3dd674f56225569870ee3cbe18e707a4f9a5507aa45f53af37f707bd03bf1ecd90eaf80d59d7d6d7452db69578e20f0caf1c15368b7796c18b5d911a317563b4ba02e0b877e1a94e403301b27c8b01570e149da07f3b5e5e1b3b8af933cfab70d971b15dd5ea2fd804ef0b0762280011765913e60ee2972b05e81fb1a17ecf00ac29c4842ba264ef3703395eb62909f32b5d852a39234cb8a77ef60db93cf564f9a9dd2aa2c736b63f79e8e5508609febb550a2dc63e6901f599a02a43a5256ba9b1e57e56117fbbf3d67a27bc5c0de2e83a44fe167cbffb1903c3ac24649221375a7be3730959c96d66928f907509c00e242ef536f72adb1244230cd0c9111f0464e98a1d8db78005ec45aff6df01ce2e4ed27770c7b1393c05dad6a7196d2adcf18e7f89d819a4e077973fd3030306faddec05a6b0aeae8a196d5407de08c7a65af9b6646b5930168646465516000a81c7cd41620892da603cfb9c5b69a0a5519c0e88d33da4db2fe4f7c8e19d00be28025e2be62a58997d7071cbdd29169b0040f1714ead974dfbb4944abcd40b03ca39e49153973d4b5db8c9086523897bda887fa239c6f0276aaee5a594280215cb987d1a9fe3531256be856c094393222f2ae31239e7c6a57fd751f4fe22045849b5eada89f216fbbd0e70126029a265bf0b1331ff5c9739f5f89d31a07a08d9eb922701ffd0a098ef09ccc4ef2c8672d29432c21bdd9bd1b418ebbcf55509d65d16ea319219082ec9d7313f71c78502da080e78465ad72cf983a8da8cc10b2aadc39e74d5db35b65c5fcf169fc2ec82799f5dafdeeb9edbb234095b3a5b0cacc31fc48e0add8b664bf64a462e21d68e1a7121036af4160c09034676bbe50b1067847250f781adb2cc354ed0e1b16d1a8eb0609f3a5d824d547cc5ba0891008e57232028a283ac84d511918728b20f8b06e0f7f000526ebc2bc92878ede8002f0a3b2c179609a23548130bd942991b2fa4b63ecd135ac04cdb685ca41d50084167a81b3bc3a772eb1e8f55deea9cc055e7acfedaaf7dd5fd31ff7a0fd9bd0425029fe6942d785963d0b7b37045de8598cd19d12be5133162a004f2afcb5e050ffe67939f08ce4bbc9249eff06d402cc814c36ca12a0e7dc8ef95af6b5f4b0ce3ba69f5abea7cc5e74dccbf9cf7bc431ec562dc8d0fb6c36bf6d64fba64c004d0889af8395674f043c732256d1993ab6f861aac75330ef56921449260ab95097a92c1b75cee135ea8d0e63d1a0d5b5121f082f8fc4ffa2308521a584ccb2309541cb395ae243bb1fcd8be306d1e2ec6042ebad97b12126098e52be84fca7509f003888a248256796f038f30c59dfb2133e9f42f6febf4af86dd0e815e73cd067548ce93303cb226edfe90d637d13f06d73f70f5d2f407fd60a45a6e01bf32085a83e9b88fbae058442a96c6843bad1529156b5144cf36f40fba36d5c5e8cb0af59028e1d629b55166c7bfa0fbb65354970ca7eb18ef7053d0e6629a7a75d30a5eb83db2a8852456e8932ca68285f0fbb3297cea2964890d7326cf41dc44500daac69b7d4935c15e3da87eec66585181fb697dad9a510aa07fe9c01ab68d350aa3f7ff59d3188f25f92c0376f26d0d322e7d65038b96b9436aa1b95cc4677503be84e6cd5f01359e5eb862c11ebd8b45cc18872518326b1f9141515b8747f60665015cfca5c04715d9168663a2b190cfcf198b9f43fe0e39b48d9e458502d80b9209bbd3a50c3cbf58d9620b725a249a9c849d0cb8388dcc7f2a90502e5efa0dac0f9eaea95b72a31afef787d38b3d1b12753cd5427a3549f97545918acbc10a81ab5eb0bb841eab9100a7c81028a32c8d3826b836136942234e829ed2212a059e9ebd5f7b37417b4fcbe5c29ad97256d4c8839b18f930e88492ca2c24b59b070460826fcd5185c05edee9740e6c10733a047276de769b55d352d080a7227d05ffdef14076d65e5f51ca1aeca5611f7628714a2be8e482cf9533ef9040fd0e03ce97908629bb2ff05b40da50d3e52729793df7b1af7b80ade2da610e1cd3af0c171b287af79e054a9586ee6dd208c1236c301c7fba9edff173f423187c49f500b0c405ec84e1d116cebe34083e7875a80a3cc273111239dd088a137a3e4166058ef6682eba4ddd344d3a2ef203fa59213ee8931e73faa6d87ce98d19737c76009445ae9f4d31bb8a708127ac42daff428b8479416f7770e26297890b292a9d072e087239be25a2377479481aaf3554cf74850e77afebe01f64edfbe5b9147101aa8afadbf7a7bd80d25b2dde042a67dd45b2b39e99897473e858a41e5ada7b08431fccf2e2545bff71f76b763bbc44014718498a0bb4af8c55d2f63233192e05bae827de253f07ef551c81a3e75db3986e281619696da63b623e29edf998170f069f1c4702977d87d5914e6491d5876af99b0b70c778b53123fbcf92bf10030df768f6b40eaa62f06ac2450e8cfe45d57cacffba3f938c3152c3980fd178650d80532104b9e0f031d0e9fa15d679df8264762bacfd18c6839e77b8349b462f09ccfc92c01f5520694ff846cee53e587870710c32d99e25b2b11fcdcfa096520b1a244add0e6ecf2d69ac0c9f661efcc88b72ff8148e5c4669f8b9c7e6321580bdf1c857c0af522d1c14b86274fdccac009a83ccb0171e449af8451505f80d000eeac91ab84eb89f5827f0081a3ad92f5eeac3176e65d29ab4bc2e061a5305a0422441a8a146fa6da58f08557df4fbcc7d227d5bde237ee5d4fba7baf132cac0e9f5d3db2ec2f5e42776b8d455ec93046ffe391fec70f3c327f09eaefda8799026dbccd6c442d31efb4e47488acdcf8b7cd15c35de959f181a083c0c02b759e07667805be536e47ed617a228dc9a57cc50c1b732ffd44b0d912845ff3835a0608ae492f7981a6ea57c20c310813f675056fde9bb1b96dde014acef3fdd137e20a961766d4410711df70bd5ac52f24138425d77358a3b43aaa5edcad2518dffd0c6bf1394b8432be78275acb5593582872617baffc7aea38dbbba3c9beb4dad7061b2cbc664ac0ec7dadb6b1ba3e234672c8b5ff79f15e2804036a88f2545b70098946bf46e0bbc30c7bf95fc42d4fbc6b0acbf3139086cfb21dbaf85875d78502feed91b22662f5c8740e393e8bbe0bf3d3bbd44bfcb1fa0eb6de4e2780c33706346cded5a4368d68824ca575f98ef74369846de062c0fb855fa1758685758203f3e3604e55eab54d33d3e238d54238a7a3871e6309f0278b62ace6b8b9990408c977b6eeea0310dfd37bd1f3ef165340ec8bc0c962682ec53b66b615f72b16048537d80633ed368d483199d79100c10e1185a79c30afdb47e1d202a25655d004ada330ace94a5640419af934888f83e6a60bc88249d44a791589e41d9cf1170d2066d8dddb16be86c17b64de3276bfed708bd8824ec15408eb4fdb87d5f8a107305ff5d4c21ca34c26dee48f22d06998b03829d6fcea08a1fcdf0750bb2a510fdad8ff1429b48cc4f233422badf7eed7db6f04f32d711e77a4a1a21ff122430dffbe9577805a689770fad814643d398aa5c8f82799a1b45e2d001b8ffd0981004ed12065d46d0c4613844cbff8bee75c592e89f1b3da4383f8ba00ae6d28f60f423488b01fd96996f66590941d325d4fa202b04c3f073f74c441533d16be1606afc3ee9824dada8d7f5b0d27b66d2f300d0396a1834f8f4d5f0f56ce61c4c50a6b12f9a2a4930f9cab4dc40d1976c727f8d785963711a5a7545149fbb7ce5b050ed4f6792acaa88f182a5f6dfbd3fb2c0d565e7ffb3827bf31a03d1ef7f6f900c7a83cb2be7d1c3c3d3bd3d5a4b68fc2d8727796572758fb1c53e082e3b60b0e8e829bc6916e2e31ef802e1228e5b095f9cde3d9cecede8d5c1e846a40e0650811f0d6b1668b97b9fa401a70efd3313e8a86cc47f0a4a6f090d99ed659b6800d7e657486f4aed9e00d05e643df3545855361b5c556f8cc13477f2925c1c8dc0af4c73a0432f416f04c0907b89f5fac84c1ec8cba4f3dfc58617dbcafb512d601d0edcc5898e1124364fd41b1d513dbf5131ea81b7d27fb89e2a69b4b8ba35f0789d53d4a69a43885473a7d194ad09489592b194df1c93dc8419ca0313fbce20ba9d129288ece220a546a28e524a9c19331bee27c73e98873786044d9aa60460bd04e1adc411385b7a0894ef821068143d8cad40ec0cd901b514aee6d9b2fc401b83675f28c36a4b181958e46254059e0df2838f2a2975a18dce87a34ecdd100db72c4dcc5c9e09f7b57b707b91886ebfefb8c938928f8927268d50d61270720b476b046ba1023cad1a2e3b6643e1204a229d82077b5db9299722aed9496a970ca3bbcc18da52f0f56008df59948ffe24f9cb95a132e60a1ed30c4c952134a90fa130f15be236c08f7c612590ebe317bd911d71ab56e27f573eb22b66f3e3500431f87fde1d4b85a9e63dbbae60b8c4979608f669d367f382e523b8dcd1787b0235f9eaed9a352c3f1a95b8423696eea1115865794f0e68c869072c07518c9301187d9143dd7c9abe19ce1ce324fb321ce260e1365603f0a36ab0138d9c40b7035869608a42c31e3fbf9a53a17f3ba787a63dfe964b25fe1a7c242af20d65440d044da61d7b167467676cbc69174fb78c9ef1cb0fe813771ace6ef039fbf7b40cce1cef74723c9db28219cb0f609860e8fd6c0bc4dba65b9208969f6c7e665e0450ed8ea343568a74d063bf7d8d02efc81a840a65595436952ef934a65443fb0a273454e67654237a99c75db85819442ebcb0e01ccf6bb4e7135882c796aff80b1b137e7f2e4e1e17c73fd95e40d8ba75484b5c9293f70e44b0f22c531af1f7036445cda03cf6e253a0179d4392669ff380ca9a632349e04613f5cc1f171e5008bacd84dc144ba4b3951ac8975498dc129f8c76155da3982e8bc1001a07ace3085c7320402035732865dea7630a66ffc43ee7a8ce372fe00a04c3ac557e86950bfc40e4a28a9aa2c83fc323cdfa3100570fe1c811e9cef28229b8c571e7fdb2033c0b580468a97e52a6450c469299870d80fcb8c22051bf15f225fbc254141608f4c0b673da5402d2619cd1e0114fbabf6997a9f111f7dc56482d398cdab907024fced55a0b847be69eff5d491eafcd34f1e29fec721375da9153dd3267ea9d0cafc991254c1f0a3923d5fb8d63e1dfd41ea1fa27c0b3c400335758880c55ae0ae4cd55795145706c3ec04a3bc87eb8e46858fc600cc6415539ae986a60adfd024fd93d63e1f3fc5d32e5bccd21630aa700aee62f7b1863b468e33243a7fe2c0be096261e629dd6a788fbc4b3c2474d444c0e2a3353a3193d8a66c73806abc60f224ae8f031adfbb93fc59d36bff07adc81a53a0bfcaca429584080d8627b4f0cd66dd0556186af14de6c18456c7ed31c4e3cdddec608b3c37cf182bf885e910237f2281ef98c41e0b54120c5cd0a71a6aa89c3a007b02b3165b23ec22d22c5032d0a64fa68b4d7f79d927e10c056a5762ce492817547594a8b12c81ffdcb3408a0ce9459ae1f59f6ceeaf05d5907accffcb926b8bd89fa082bb2a0636ab6f20dfc7b22627300cd52fab7f48bf5ec2ec66a97ddce816bacccc21cca27fb26fe03bd234330960b687fefe0fec7d80cd0f3a2ed84c61825ef3f7816768797aa7a021c76b6c42ea236fd4c50d80b4254f472079bf1009cfb106b10b69b79480beb03572ce46761b729efef9374690df2c6d274d74b122bb8aaf2c8653cb1cdc3b4045369edb043d9a4b9f21dd2625de372c74d21395df7753e97f910459e6064d20021216ac20a1dfb58a0e3abb7db16dd001609ba833ff20f63ca6aa0fe221db305cde31f51fe24c0d049045dfe7669216576a9a1fa9e5a266093264c39dcacaa0a5bff93c4fe782c44149171f03b3265f9011bff9888081b8537baa41b436c180f21532ef05670c2ab267367369443630c67a61ebceda6534ad52e06e2a5dd560215db5849fe97ebc71214e896d017ffc327593fa538cfd85a80fef032ee8e7d09009f066ce7993dac042d11fe630da9b36a570bd13c7a9069ea3f8da33fa4a50fc1e5c215d8ef5a08c1c3f7bcb3604a86062f52792f71e17b6df3f95b116bc60fe793674c39e9613e6fd530678decc35f6bd0c534ea90eb024826086e8be69005e26970fe98f04dda93d17ffd17af637a780d44a0d414aeee146b7cc03ff14f06235146d4d9fe85cff4442743bb3bcbde3e313caf7c6fe38f496411e1ec9b2009b28c043af665bb91f4d67c0a23a55ed28cacc344b57c7b93f1f3220592b07e08282790a2f4d4da49d5f393dc6374f3de747d6764d066a401be349994bf99c90a18a739574b1ab0727fd79a09e11e33e5b1c53eaa17c97965dcd5078b9ddb2a0bb380cd9faf8e2e5fbfaa036dab63b132d5618efce5bd8d8738b313dd91086006a809d52e85800e9f098afce1955ddb561ee71e4e966789b8de8ca46921b41a0e80095d88ca7b7957825e7d0a5313f383f9b0168e675f6767dd9cde055576190202db994c851ea2a5e7f5b05dff3828b994a6fda52928ed1e1709506a4762280d983941fee08ccfee9da56404c73cef174a122c468aa7277f31cbcf20170bf70e278d9a40eb8866d7a554bb65465bd77a75d61bd5b038068dbe36283096ba4304a99342bd100c5100c958ab9b986c6a5eb926dfc30dfc7e4afa8ea4d1a5b3850e6e0cb85aaa97374ef0ac6b863ca699040520edb93fd26f3e1e2d5e9153ef4605210a626d5a6d87e27e76de71b1b6795439e020dfe0e1318dffa1f912f6e9490fbcc0a3c3715a06e43a70da3a46511212fc94f32e9224697df4bfd023f334110388d86bbd40fa09a1c0ea819a617a294f6d7234ff959fe6e4b6f34b85d02da709375a67fe558ffe4340d5a44606fd206a287e2812abf1f30aee96d3940d0f9c02dc044eb15670fbad317a2801a38ae9b572c31f5409ef67f20955dd747fd021059403b3417ca0b6fe1d6656848dd23be31e945a4fc864d3064151bcfcd4df5f02e8b7519886fc6d990d08f923e712d3e971f590ea0cda3d4194fab45012c4cf06fb090b6b6bbebd887782d56cdf7e92f6a42bc8defaef6a7879594e1b4b9d1f06deb368ed5576ab75ed61eb9ba4927195c683184c9bf52cd4704ef11c31fd7b073cb1148f263d70d0c20c79b624de1696b13376badc92eaa859e8b59aca7fd6067d6a310064c6369df31ef634bb0832d1655a5cc7d8b4def43d7670148bf05f030248b0c189466b497d6474f421317b520d5bab86af02733210a0293d5dad20089e4fe71dcb644146747adf422d8f279d2508dd1781909ae042799798d3c7db004667384b154f3891ca8a155e41837ad53088b0ff8e9e8e63b7a00a5ec26c910dd7e213093cb5f9d65578a11e909f26d9c21108da3b07fba09ed7b57d69f34a0b5a56ebcc650f624477c4994ac53b9e328394265e72b612184be8bdd57fbb7c092c15c3622ee553b32696b78fd789ae40f50bfdaed583c129b7cd5d098a3f6f01e1a6e1272ad7e47a2f9253a313655b409bd9182992010f5454f20b07014ede016d4bfd954dc5e98462bc560e4233b71a35cda8bb502ef582e09971389375720c3c730af280c9c3d1ca4f960240dd1424b1ff29b82363a6d81d93c4561e69d00cb32b005ae3800f2e90b1495dbb31b609d784928d1ca9a1817ad515d640651e020404da4123c876e291a61012a3d7fcc4b3c257ed1c3c36e368a7316b6fff840ba7526f19dee288d1fabd20a86e426c1b94812f1501dae2e3143e5a723b7a59068f42717f3c53bb509822a200c026caddda75b5f45611ae0427c68f0883063c021a084c394fe33e5fbf73c3eb4d7ede5137fd1a3dceacde6f049ea2826b7b2f09567db7f518aaf8a1190cfe2207175fd6b475bacb7e7e27c851c9e7f9d702b20fc934bb7992540f75d7e5f43d9a48a0ed0f554ccf4e86f5f9cc3f92b8d725e405dd47a0129226965b6c127007718e78366d1c19d3d7fa4e9afdeed5ba822ec90cae5faa72a5f69a6d360970d76ac37fc458cad791278ef874208ed91ff4c2c50c8897b25d9ccfc208245cd5b4e0f428f5e49d8286c2f862ecc2674cbae2a29903152a389262076882bb2a227ed5a3a14a7e09d9a291da498cd7e8295c77b66c022d2319a3354c453ec3ed86413fb267e2b8cd3ea7b9c69a898423544537e20800e48e8f72b7e7ce02aac099db89fd629e0bfd312d1b06eefe3a15648654ffc20c74513478a8ab14e76eae374162da10679c907f482fa4a9bb1714921a457039020a651493e796719ce61bdc19eb2ee5968655dd6698919e169f595f76296d4001d8307bce3b2abe36c4b630eb7e21a4c3c47e3dfa8dbc82d2c10288aa750be10ce9e247e5037c4c21893db408c5daf1026cba8012c8a717f4ba6346bc58dfe102fd2dc28e50207139418c6171f130aca3aaad5069b2de5ee91cae621bdbe1a60dab4c22fa1ba8353c9bdbd2987b021b55a97e45a52f7c391849d8387459dfcc01424fe127902932560127aa7b853a31bcc3148aa9686dd54a6781be7e92744209d337f929a0a5e66083bc5739f56537663a0311163eb1a6be7b81f1681dad45006ce86e013b66909e5be7f487f0d249e70a8e272dd4ff2b25e473ee8cb7596402b4b2cc077fef5bec3c816552f8651ad1f29196b6ba7fe202cb4c43d5055d4209482c2b19d03b179f9a40fbab0f364ade937c47375151805bc2bcead236c8cd0681d6a8285e4e88864975b358c4164c2ef6da9d4441444ee25e2d23da9847820ccd4dcb064d69c5bcbcf23b5823c423f0f8a4f97ce2405705e848dbd67dfd1d0f9d79542127e6cafa454b807db865c810fb6734248ef1d12d7fe79495b5be7602d901b23f20ebb0b997fd22d581c28c92207b56966f84f8560b8a2f2188b7db063c903fc578c2e36f6ea58399153575961df232839b30ec1ec2d9327b5a91a80f255bc0e671d85a3edc916009e94a362ad8f57fb9d9e8039a53ef7e6033ba9407685dee8de695a409352bb98e09958c0a78728732884f4503c891a7ba224c6d0a488425e05265d745f55c30dd2a67ffd410a9ff3b4ecf38f422f8faf9fbc6ab0413d489af4a20a553f0fd9ad69cac2d09f21eb74256a90d4061d6f4d33468ee007491c9201545b58dbf7c6e630790946c1b5fbe127f36982e081052a1e0f54c0c52facab270b821cdae76c49715bddf5a1bc27f9ba3f3370c838f0cd280807f003496f81b9171ac3672158e9cb9cc23a03a260f2482538e7c243063a4bacb3f00f364faf3ff9410c8f88a1ba981a37d17595fdbd2a965907aa91269b58f933d01ec2726f38973aa54247d11781a0f34b4cd893fb278c2a98c37515b8fcb74990dfa86a9ccd268422c4acadd62909f11f49f9dc60ae24f97d511a80e7040aa7502f1eff2615252a09a5310cec8afe79c35074e8d8c425cb509041bb165edaa9a0bb48adbd37cdb77846747a84e3566fa5cf06354a9cf189b20ed289bc3edff2f041e8e0ac5d81f24bfebd536b90432c4ad2e4e275fa9f8167289796208be628204dbbf38ec58cd78baae7943acb57b843ed0af82ddf40ed88edcd569074256d00f4b1103b503d8e33a13754eaf9e133282c737fdf59b0584b6ae7741424a3626029b705618d345d6caa6c85f3905d8e0b786cb40fd5df8c7fac652487fd88bd40053647419e772f38176af89a43482a0f7ea57300b047581b22600e1c7d960bd0b530bfc4bb4fc0c999e6c01b60bb9db93d813fe69472e06e6d4d30229f761650d37b1a08ff1d6b06cc3e30ec9b712b15a85b0d403c2f67f2d185159c87abb0d0577071d668fc7458d0b9c87b893eb000b5e36b4a8897878f59afd3c718db6b60838abe11728c29fe9ca7438b5ccf8db1cf472a7437bf3bf7156f033cd238b7206e428b653399946d64fe8253298e0bb825ab9420b91bd6fa03a2e05f3fcc4bd09621d40ab275f971b3d559ba4b7f6d9a41ee00652e63d0e1a55aac6fc5f454d0de9fa69b91333924270d2403b42b3cd6c4f2ea20a5e895b46152b2d6c463bd60d903d692db29fa7ef438a8921d05b1ca38a650e3bd91a78ed66c96de3176cb60daa8b9ccc2953e59aec1925d9f51e1671fb6c40454d0e3d1c4e122849be16920ee7b9e5b541643b577a83788b74161bf9909ed6e72f767925aef9e07826e7b7003a100c97416d39ff3465a92b4582c3050f3988756c80160b7efc697f1da042021334bba7df3a859af711978d365c0b03cd8e5eb8a92bfc52f0c5cc931306370038d5ac79d9b0357de65b1789113d65b1f53a30241fd4397fd32e16f3031c590a3a3c94c75758f19b9dfc25851d148413316d822eac18fd698dd2887e5cfc6e064ea07a82c35b7d5b30bff932fa089105e8e4b1cfc41f4653bdb5d992404c70050922af0d0f735a179e92d39d3422c6f8b4ce3c8aa1f99bf320c1960a38cf840901f26a631433301d1f2c8aff8907fb860a3326b8ffc816d3a71af8f7ba93dd024d573c49e9b0fffe2468e5f410e84582dd360d4ec50e29955121db4844389c075198aa1fb3b8dc7a43fbb1eb9811dfaa344f1eaf21d2e037b3769675562ae00899064076cdebf34fef46ceceadef79acfe34b1b170282d8f26208d84f125f70c722bb047814ed3f2d07d748f112a8e085290e21d1e56e809f330717b0e7bee09827991a35d04dcc9bab009da86c1c3a3b98d237771ade3a79d7bebca3678f80bbba4ce3e2a349bb6c9735235fe88658426c46b464ab5db2a70783287b2fc7307390d9ef9cfeaf1732aa297de775f22ed56c4e8547e66f55617654716a652fe0d066306458d6aaacdad6c9d0c9e91986e4022bb310dce8d3e3d649915556f63042f0bdcdb49f62fb0be06df83aafd3cce1288caac6d471ab3b2fd14e40b947207cba62596b423d7839292f383b1c6b32cfeedd33e83095213c466e48836f12c036d26e1f407494b9a28c887e351fde02141bd93355ecc9a35e8485b3358c8c50757b56d582157cac6bbe507956b8a249519adafcc9249153489e9664094122d08554e98cd6b0a0c236126c8b30430522e94268d57b92d19e0f1626eaeb88f180c47381f988477ee7ebf4fe78b56c6b44053cd10ae8c0fff2cb462396a5832a403a701da48f0b1c8ae3120e35f13973e9ba93ef1be1fc2138ba0fa60d8b3db6f0ce006234c894491cda1992b85219f7c44ff7f9f8170b49effa4dc8f3d2f200308b06d8c6e9517ebae3318113e8ea894b19b73c1ea3c5a73db5af999a012651f008fd4cae064b076b0bbbd04f501e8e2afd6ac0b7308aadd18b37cb86d04e1b606c8cb1d27040522b195d3718cfc9fc81acdb4f0390db61f2ae62a852b84b6db0bdf29c7cf92449afe6854fa0f2a9bfcd27daa41f061d881ec0dcda6bca5136c01a2407deb420b56f4a5e299c116ad6b28abfcf7b1b93a6dfc7146a29665a89b0268993a701d4bfe97ac264fd4c8c1fbf5b08520e801bd433ee3291868d40bad07fb00f5fc513485c6fb7867dfb34d5bce47cffa3c890223a8589ab8a104d90b06adba2d7e86265e0b9ab00a26ccf55616c92d0fac50a241fb8f09c3707955be08ca9c589e532b2bf928bd54268149f09c210431b1c719fcf2785517d86348da0103ec98a9944b92546f4ee0d4982c8356b4c58d72086f60438d8615db634dd80bff4209c07add031c5ef00e238c17e36717cc636c787c6ce609946ce21f62530f9ca43317fb531c1c050654cf901aab3bab440b05a50acdb96013038bd2cb5b032c75b8cefb1ed5a28c989efcf0c8fd2d27900c17bb3bc87770d1132a1f74b80c4b691bad4d3e2e165d35e9e53b8ceb891ad903f030777b484ea7cfaf0a18ed0a510e1f7b2c3dd46bd292d29127ad191466cd0f3595ff9a0979bd4af445cc7a0d4d9411617c2bc0efcf025c13386eb7c0167bd90f5c6e1dfcef9c3a028888970786be01ad670910470c4537a05b25d5fae80f1e8e5fd544fc151f79babdf43f0f6f20db2885a2eb017bebc02a2080e25655ce8d63caaca184a6cde01c9273d00e34c7783cad2a21f57cd423393a587df3f7ae9a191b7fd3998c2859723ca428073439f8b6ef77f7f3e6bc894712e595595c6a9362b9fb159151310237e8c2a80a771bea5352efcb34d37f9cc378d71061d605239be13d53aefdf15eec2101950d60a78109c64c8ad13096d074d9e8c571e374d456e638fb31c700ab64d7fba305c85d95cfbb18f2e364027a26b24d013209a0ed549a74083f0ac2128b4be294051675448af081286cc57f185def44fa5cc92518bcf26240b6623de279d1575f0f7d2ad23dc8bf69b7f3e13fc28b8a497f51eec09f311dd0521e4f33b722e1bc0025800a3fc6ab930d32aa4b4dd7c514425143f3690781eef513557d1058c2f0055693aaa2ffe77332bde0a50c175324047770da0243d87015667a58d48fbfa403c5004f7e538fe639bb0cca21f3ac21b63194bdd7a991b8697d02ae3de88acc0bdf021483c58dbc328c607c474eb638b234768e9c6092cc5b44c4b6b41a99fa0bea35e53be5f1f925dc09b520037a14b4d85fb163f59a8e3a9217c16b8edd0509887df1fcf3c6d2d2569702be90958c8a5f2cb2e8d5e177a48ae46a1026088709aa3aa5aa0e8f8b9bd41aec8ea33bf0de416b91a6833c9e6794840aa2d59fbe071ae86a3e884002f7ca2480f2ccd0c8d1a50343a61eeb649f30f79c2a5039fb0f4ea7bd1f1b633cdb06df788387ee870b2747bb74405fce0e7e43bc89a432b800f3ff885a43f2898ff2e9dac151ecc8e1fef69fc3357ed8af8f5594064e3dd20cf888a2239fe4b31c5d835d6729716171924a171ebab8d6edf94afeddd700c403626e5d6150848529423071b15abd0b642746d5946de05a0db3142ce6683dea037e454e9814b67280ced1cfac9c2ba145a600bafff7fb96cee795ad5e910f3a0aafb5b426bf0faeba4baf5d625d0095e3f5844694129c43fae7724afdd84ac00f10c9bf35aa1feeca09ca43787593efc6f3e9ecce378e084a7359c564b1a66b0c23f83f0d4567d5d6feb806bc689f9e105a4a501b5f2db7fba02eb37a435c76042397c92ea3c0a60b596766eff99eb82a83fb0543ee2ac766c6680aed4808c90bda954de7e4b897cf7d937b2226ce78f5fe6486ea9dd0ff7c66cb6060cf638d0bf3157b43ecbe6a28cb8397f62bb861a605b168bd4722da0490d4f0bc31aafe02d67bfd2cea74e01500943382551189d50f0c15039545e24b7d8a5305e5fe530efd5f7a275dd7a474642724cd319a5aa33ffe0e5924900d66edba8b86cb0a1803a96c79efa689232d28c83364226cd8fd14db16792c9968f8632b8530b87f030b2e7185fae3a4219f0e552585c5a74542b8c41ac2c5a34e4fa3d19dd5c8288d0ee06f56d6f72ec343ef5de521d8e75f2aaee739e5b56ba3e6d9a89b37fb9e800a27335dcd593839163840cd451c94383eb1b7779dca10cda136387e5768570c0ea0c6242630b25dae727ced70f35e9385f6f78aaf65743d4c0aea6045bd59b004bc76261c573629f23e889f4be4c50c2dd6bd42e8e5b34f5ef5938203ad28a10b80706883cff45a206d00a9ab21a3b784fcc4517753ae2f1e2fb1a364ed33d10d6b99dbd07000bb490d08039c7d3a0dbb9763a4158cea2cd7c5eb24bb57570f0163a7f01a9534bff3d6c9fa1e4c82c4440277a9ba25adf7f6c9260be16247790cb65d629c89757a353935c999c51fd8461e44ceab8495d75504a54c71d09d1f00097fd82a0d9b3b115759d1f0fd76ec9d452779316ca09c158418be280154710968597ac3fa1f7d8dbeb521691d9b993f7d70aef2b1d06a48cd20fb8c57d60904d853824dce3011bfbc07878aff4dddd8d6c0c6462287aa2cd128821a547c4e0ee3cc7b6f4c629f06d532df3cbf76ef4612158f3e19d1c9db7a1ca6a32aef3209f1d3cdd52aca595d2b9e54b5eaf58ea97cdb1ecaa2b22270a7127ffd96163805801ad6ddc2346f146ff99d4d160f0cf8f933c715263c69387c695b28aa6ee2043a0b4239df708840080ad08e5ae177340fcf91420a1f0f6a8195f3b2fcc79f064de6b2503105c98a69b64b512b3e2e4179af1ace089f135d7cf06c5424e34f0fc8c67d10648589fb37c31e151b18bb121dc40cea2abc88e21cf512a411f46105083599f08f8ade88564cea215ac27f4592a556cde38d407972baa5d8e21f5a0df73e2d0943a79640b004d6eb1b29072106613d4ba8b5af8c10af96ad2059f4027bb5be10aa600b16b77c8628db747cd898a11f017f9badbf8db345323a67f20a6bc8d24ce2480a8469ae192b12da5efcf79eb97ebb627ba0e69c35371d0be40b03d2a10d9d91829b145797b42a9b4fabdb6623ac2e2d1faed5b48ffc8964a6021b2ed8a0bf6d39a217bc47e2ec052639b96529ca89891a0b0977417d0d0bfb02b0372195543e6be33359528cee86f5738ca51db216599280a8d81cb54a5be80031343edc6af0c80fe21e32907c8350e166cf0ec7611c58beb253e231d88d7c0930b87e9bf63dc74c60fbfb75566b9a56cdcaf12fa2bd2fd2c03ab3925b31b50e633377272ac262eb63ca1d9d5bd3f5e455d4752db11ccca6ce28e153b75c2b0fbb8be1083ed1d220c92ddc2be37f8706f8231cd194df98033fcfd64ad59565082c286bd5ac7fcd1712fc5357e05cc0508e4f9f24a810931bd6e7e4ca8b7225044b21a47649e3db8c415c7dc82c3e601f1faa3af3789b3e9a71c7a72a01425f0eaa520a6b751a71d5f0ff7f0287a9522f6471faa5abf93570d09e65b7d1782806d8160db43c7fbb5af25b04ed2fe7746fecca5eedecd66b51ecfed54f62852b0a9b84960fe643835c6918c0744a248f8cd9551f712021d6af55ce62135e314a003127543a4bf2fb4a74afc81a30744a9f4b72eabe45652970c11379c995023203bda96f2936c21fcb7ebeacd50a4a0427f2bbdd8f0724260f59e2460ac9f31c0e879cc29ec9da960488c964a8e28ab4a0484d8110137ccd30832a9644f8a3a8080bdad28be182a714fb0f8a13fbe7e200826e44899d10e9c4193d3192d605be03edbb18fde21d8f7ee69dae1ce5aae5949451c0f5efafa900a28959cb7bf2c00df4decbc27fcbc9fc884a7e33c33d2e999fd6c25c111fe0fa1ad6464315276a0488e1e906792f06cf78dbfab354541314abab22aa4af14d49320f196bf1dcca0255fd42b86bd8faa279e0519ac64ac3408c7cec0edabd83683e56c86b27a5d40f190e001ffe97fe14bf9a5bc97c41e3017e755fc12db568733717b3c85fe2ec06793e60045a0e7c7336eba35db6e1597ec5809ade6e1a043bad9713c20f549b04e38197c186524f08eee4faf1d1e8968c4a6c5ed4b2c87a178e64e496d77f440855b2967846296961ff4d19da5b4d269a2fbd52060ca13cfaf5618ee600b1ea08c6718b761eca00d1bc84320e14d81371112ef60045ed6eeee7e0515dea378e0db75ccf8176aa75c7ec237d3ae4004ca8defdc7641bc924dcd33bd128755165067a1d6023910b36a406af67ebf2d25a3dc821d23e6ef63c6a012a6cb83408b409fb851cd56a21494805c54cf6b839eb7b4be17334385ee614a90876d4dbd84e03f3e23ffe0d26aef43be2d0b475f864df3c2cd9e291b279728d3bd2a6970a6c00c9089ae1c90f5d9b8f66281e981c67c69f915e3c24d45dc0b05106042e07130690bff522ba91492aa07256ccbcd8b880d86424dacba5b69fa8dc403c1c527b0b417bfae5d56cace665919661ea0211a4cdcb8b823442c7cc207f09642dcf4b08 +generate_ring_signature c9ab4560e364fa2cbf7ec6e3aa25251bcaf2beb043ccacb0a4624058fd88d86a 5d6b8e078641aea54474ec65784fccb1e95d3a0f6c1b97005d6fb65077c145ac 3 56d63e1b1a487fd4d7975e66dc212e3ba4180fcbc6ba777bda12c4a957f359d1 0ba8218820fc5a47b7449974d27f4cf1fd7ba6698f087a6b007a15637c8c56ca eec4a9fdb41c38be9bbb62e86f0d497b8adef1c297306cc3a209a700f1c76981 461bd2cc07b9c8310b74820b6b7fafb962605dc168cdb11be6995b344bf95305 1 7056d61edf517715249d71f2d06b2695a3bea07d84401b27fc22e5ea35afd0035c73b2a96346cd8e12ea502d2de1010afe37a84921aac4c95ea197437e7f8d03e83eb0b5d2e7e2cc5f971b125893b998efad29de246d1afb0fbe4b698d6c7f0e56480a3f8de3d84082b9cd157965afe3c3a0ca6a3c8c2de283102e19ad31910c82985bbf27f70f84b65eaf1102477ed4b93db467f23e91e982b7dbb80fb3470ebeed00c63713327f01cf4118e067f6e8da3812b2426f37a8aec32f9e059d8b0d +generate_ring_signature f075672344407c2c73e656aab50a5308cde472718afb873c3813cd31eb083a22 725e94903757033d3ebc8b23440f0c208e60f8932872536ed80050f0ce714156 1 ac769e6e63bf28b0e214931c70c958bdfcb8b7e662f280fe1699e9570b3e4df9 34a4a033910e6aee4adaf7aa902b9bffaf1ec93c9949bd358485f59c45538306 0 d497a27dd0187186d3b5985b14eae557f19b1981f50cdd9410b803dd226a75084be605f6783cb961a6a8f1292452406a862142e5367671bf176c9b136a9a200e +generate_ring_signature b5c9ef14b9a08e78925da00b25dbfc501c26ecafbfd1710539c0ebf3f0c4fbd6 bfc8153ee1fa82bc2a51d1e74adb672f8354f3e11591ffa0253c4a3a7843d759 42 3cd53e438e8c719ef4087516c7124def7dd74122134893d56fc52917f9ee9b38 f2e4139786fbd913a4f13e19d484bf62929447b8afc84612094b0f5bd043df10 5fdc376b219fe822837a4ad8c483f1f392a1cd70141368a20b0d47ad9fe1e009 727d639f4fd6588673837ba332ad5c0ccfd832155dc7e194dfa9a8aac128869d c98ec078ac512c8be32d11374bed3fd019d46dbfab909f65b47052a0b36a20db 272da4cf42e06b9645cba73ecbef4c57b64b99e481cf3a1867ee7c971364904f dfe696e5068344bf3f7994528217694b8b3d359eb52a362423ab2dd23aa0ca5b a2b507f5f68b776722b35012a49fa542f58eb1f6f5a4f2b03ea507e306dcda46 4235b88c24272d62ebfb9b75e6af06479fdac9f26ee03560b53e33d48a820c78 74f869ecf9de6d2b0835ed98600e6c24794e549725eb338f0243025d9945c008 daac00d7a944c053a2e423047c1ae410cf73d0b3e3a0c57072016552cf940313 a6fdb201cf3f7b8aaee0409bc5098d0fbb791e4da2e87846e69c0387ee917048 c556403a2899b4a1288c81e6f4e7f1d2b8adc7e1baacb2272f87407a5987ce4c f9f08e37a0c25ecf700e25fc7107c9bd008931cf9fdc39c454bdc4a8f66b716e f2c59ddd77f72881f42cea3a1a1053f9b4e4723e20e65a1874fbaceb9fced0be e7ea591828e31b78bc7634275bc73436b23dc1eaeafb86775428e5c0a10cfd39 7c15e3d04367d0638758a8c9ab73eabbcd356909c04377d9c3008e70e1ed0eee 3bde3e1aeb47353edb3a007ac3fb658471f6b3c33a67b8224534301d41d55769 8b778667d673944e41147d049effd5e9197f6943b65d8bc47e251c7d66e614a2 a566e5cb64d80f5eb09d5d8fb0c7bd67fa50b2847b7594a6817c94a2d2cfac40 44d95857ad4b5254b378db5d6f5de3aa0c866e0eba2757695e5b955a71ad425b f95a8c8f71f794894ead92d7a8343b7f92c2902376c1ae65891b425d7beda62a 29ec148b76bad44d003a3e53c1cde766fa9918b011696402b0b5546f6cc98c82 2321dee77fd3f088ff04ef45e65fe6baf7cc57a2e8d5887efde37d4e5dd5940d b094fa11e71f396fd2c5efa723ae3b86784d3b4a3ca28c454807c379fd36d8e2 62b4e2eb263e0af951102eb0db0ac934087876431fb5169f04e3033ed66473c2 3043bd14d14c669e87fa0c47de6b5253e1c9b660a200b528d5d008dc6372ec28 81837f200ac02a8ff2949baf7e2046dd41475237d01dbf8a7ba05435d4f56542 b374ce8d68abd8d30d806eeb3133692abff1cc03ce765f4d2189f420792d0fa2 8aa45f186737fc070eae52127397db1c1d921b3716233a76538ef4cf8d98b006 094a03faca408ec2ef2422d1e8e93ea9ebd15ed6793f909327515725fd245d8b ddb31f1ca2d6a32e1d6ce1e3c7935d2afdf32bc00b185ed92832441705af8ca5 cf9923a125eb3d3c6a27bc8abb15c9a1f5ecd97d6bfd4101395c547c92f53b62 70b0954b770448de581288b0c28ef89c95ba5bc78d70302f1b3f8befda378e63 f32f02453664f8af919094f4d8ddfa34781c83f4e8b8e76bbbdda82120444532 a1c063c2be3b23be7ce11920ad588cff49f0be61f4cf1d86961ad990e0d74af4 c9ccbb04e610a9d99373863ff6334908a4218f3a27fbd9e97b76d5a1db666609 7352167d50a292ad64b7c1911ff0d3245ea58be061baae00519af17ec81f1cea 7340ce3795cbaeeef9586e1be8bb45d0e11835db89cb991b1b9042dccca58de0 8486d053b5274d48b7174efe4246d6e0ce405a90d0101558f9ff98fb0f1f684c bdbba1f4e3c95f849d2cada4a88daba5965c9b1707e3b963cb130bf9ecbad8bc d077d77756520959c488b842f761257f35fcd5190e86c4676a4a44d6ce2c6bec 553cc6b8ef0b6b67e23e2a5474a2c74b954bbea6e8e8e8c6e1fabbf5ae4e3e0f 10 4df8d42fa575ef8d2f8791c00868d126a13611e2e723f2de9e0a65c52938ff04a54f81690e76e29f41c629d02cea978cd1bb318813524db6cfff08f25592da08cba6f9bc070fc52f371cb4a7cefe0cc2eabc0f59a26faf80c2592adeaa367b0648693b7d5f23623a820c98d527a2d6e911251d0f23f8ca8eab944be874211b0eda6b16811b36552a25c4d3ce5d12b537959c985fcc0ef435d3aa66994bd2890d0b24d1956a2c2997acb633018f38a362c53418701d2ce92bee50666636b0f30607dc1a48d7396d8ca51b6b07ddc35ef2627c8c5231f3faf6e7db34dcba78ff095ac35abefc2b33edf1608cca98631634a04914d296b8b74aafa442a1a0e38f0168a90b49c74f9b03b4b08bac28e985c0f121ad175932f202ee027c63e842180be407571b4db2ab7ef165e33a4b6afb1a77dc042482d734009d47d629ea4705058c4f2ba79c3bb3f7c2c4ee3c22ee979ac8ff1c66787f0e58ae14cdb11b3bd00abce3d258e999ada5e2bf72b551299749ccc8540172a38456c8793d59f1b85e0bca5cdfb40573b257d12ba020402ec3af7ffa5a0152af83b45142ce727c3d590929dd1d4ca76f258364c46150c8f8301512afb6fec95af1336f74a7537b3ce00078f5f36ee6d02c65ee966c0e8d06b263c044a59ee0bc5bcd3e46cfbbb4fd2004fb934520cffb00d5bfd389fefd5a6af9d0e4695f30ed4a0f77911f2e2d7ec10bd085647604450989480445bbe222d8bcd0fb84d01a20b328dfe8c1363ac6e40dc3165b4057e621d42108cd35d1ef1bda1b5c82e925d4e61ed8bf5bca2b096102a7ceff744a1bfcaa750927065654f105433516f6b533592fe90ebe34b78ecd014a665f54e3189b24de6c2c8d2231840d0606e4e75ec0e961255c7ec554c9fa0dff4ea2a6b6604f63591aee139617b86136038edbb3df02aa79d9f2a8d0a43303212281cda7610d9a348de7d4263f9f2768f1551cbd77fa720a7273bf57a94b0a19ea5b978602cd52803ce2b66f8d2e530746b57a35da4bcd96c2fe88be7b33041208a6445854ff23897751f4fda6172098d872b46f62e0df23f1ff933b832f036481068286e50a28ebf24f5e47da0f9b1bbb51f8ad7975111170cc24c89ef806aba96b08d4a274ab16fbfa4a1528d72b6a9b9e20c58ab2783d8103247295030ac0a4ca63103c146df9615277392c6379012f692bc957a015200f6ab669cd630ac7c0500b67dcdc9a8f62bf2b64197f394eab6cc01d8d68d9de76d82defc3a80c5655dac9e556f3a009212cf0204f244bb35d8d61eb56fc33896ab75d1d2d7e0b7c02a8d08b6716fc5e5e19feaa2c9c2d9d5fdce6041c583236d5cbe8e4a40e05911219b6e21efc65e1c950c350c98867e7c329342996d7398c9036b4a0f56502bead90b8dc0bf3e88e8071f10f4112f319905256b9b0d9c2c68acb731493fb04fc4101d6af21735c3f6c9de0c653e362e48dcbd6e5e27438bd4d3ebf4e44bd05ab7e5dc585a1878c5d0882e759848c97be87da85be3efec25c9a7f304d010c07b21bb74d5216f6351a72c666d4feec708fdaa4c77360fd160502b115ef06d20e17513476aea0368a9e6eb863d2ded816a01894a511d33f22ba5c2addead5300f184c4414172e0ad247f7ff57aad66ca351daff60e2c4e13f5e4c8403c4b3b3085f0ff40145530ac4343b5b13e3ccd47b1ae9f645f90ce0909ed7e37ce5d1180bcc41fc4a0740962f5b45b6761c1d2fa05d5be6b8c0816c1c3383f26f02c5e309fe9b3e311ffcbffaa381c08e3f8bcd6d130c58c0730f56e9dc7293f9e2a859057c3ceb3e812822f8aee6f9acd2c34809c5fec7b9e6ebb0331f012ac29dffb00492741e3aa236116a98e25be43effa716ac850021cc5be6339903591dd2ff670d00bf17525fdd4967a0db619773e0370aebaac15d1651cfa3a3c2d76139fffa073b68318850b8169be86170f9e59c37627cae33bb6595f510e5bd0fe525ae1204c15b89fd37b1f7fc89a190623386712363f3e96b39d108bda90da8458d32cb09415f6d0d4c3279947ac5e45d105ad712b45301a100fbc1e8287a07fd782b720440971b8897de518bd40765e5540bb8a82a6232ff7e9c63624748756e491f7e024b4100a5c9754df4d1f52992b7529db545b03444332b5b3b4afe384919c64608e04f6defc8b34bbdf283b3b7b9b1e8642b9de2c4d9498a068094ba6563d89007863de6d863feaf14d71405610fda0d21a05f5e1ec6edeac4d4a742e73794fb0a585fb55e3f8e2ccd0438b86b4843b7ad6a9d39591e219087541448fe15a1f10e3efcac14b790cb60628a1be3b9a63ea4ea98673f2b5145a0390e0a73f1cd6e081034fd0e787ff0d5f2e0b55f19c0bbf333886509ba577a5d13101f70bda15a0ee60d1589ecc0818fc9c00e67b35e306fdf237637461c91f69e05f694052d5e04ecac599c8a07ace93cef69d604849bd621259e1ab16ebaa2d26a7c8a9441900e46c36e14a31de227253abf35f826a1b0772ce60d98a9758d000a7f8a94026e03949af06d2528a82d7ce9f9153ee0d03dde08c8f4d532f9cd3804c5eca63eda0a149ce7afc28ab4ca5c7b0ec53597dd311f1e507de2c86c7f4cc47ae22270b401ceb4e7b4a7dc92120bbd5f18864578c3179c607212bfeb7b258836d81b342d04ba37ac88879c8354674aa021e8109f372e06fe9d948974f5e526d85767966d0a7b6b572c1f9548f2c024c29dc5d7536eea97b0bfdc938d8cccc2769db32eeb00921d03c41c62fc6c0a222ef6e6b4684ab505f18e7f08ec6733b2dc5115ad3703f2a1e817e53114919e606b1c71b0ef595673c52e8ed0f4f6b17d09e08e72d40cf69a68a179d1fe04af217ec526afb028906d1b5dfa73c847d95b9bcb4683cd0914a20c8b15684d4f8f91dc4287122942a5d0cad863e6a9c100649d57878cbc0e914fd43bda744193ba84437d30afc9e3941125c71a448476d63211389939c50fab39878d8962743382673c015c0adc18d9aace62eb3eeb6964831e322f7ea8037f67eb8eee9131e950dc4f259adc15f4c300979668b7bf6aa69eb4fbefbb8d00e1edb3fe5c07d6bcdea9597b6623468ab0f816110ad72ea5bf2f167ffb96b909e9e7a704496600992ebf26928f200867e4b74b2c93b8a4ee0a2a8216ba5c210ac13953d4bab22f7c00cab61809563cbc7363530c63cdbb922e0836c2a179580ded4a7c9f91809fedbd1e4d12ac684defc9a8f58580cd6a532a03960185568203bc0e2c75755f9796e13d21364c86c7591bc63018dc2ce7122a2231eb667b4d0189a1e9ebb297b91ad182c654b475905bb456f251f92b67ed7663e3be2e89a701263413be601e1b24cbb683676c977d7a998f64eb05aaae5504065ccc2bb421055a93468ee30986ea0f71c933cf1d0de159534c1bc113f664c83c6460285aaf062610f9bc21467827b64c0eece7684c1bbf1fffe9889da6b2c7e21a03bd3d7306ec1f71db25ca599c89c8663da4b882ae01bfdd05831feb158e7f2e4cfea47c01ad9322f3de86b09f7747aa3f44d7b5d1b0b4d26c77bda4381e26c6c5bfb3160f1e75ac8a363a209b5c37d5f93466c48cd84d120ff94e112fd763fbdccfb0b10d1b2a95f43f2f92ca47d4e08aca57da5e2adc13c103071ac95daa4491c0866e030acb0756abcfadd58c09dd00983ba0d6871b9d3264753d12e426f688858e640b46c70f5b81e2291c49be1ffa2964df355781f5466e1eab2f388382c31c98ba04c788690f3bf5356fceec3009f56dfce2a137dd7ec8ffe93007953dc048ff580e +generate_ring_signature 9b329f5487db8fa896767fe45c6f92096dfe0be8251e4cd36d07c0919a9c7dfd cc275cc9d60c681f4d00425e16466fa9f2c36f5bb333ec2c95aec837a40d0fd9 4 67e340c96d30549d1dcea389f686423bf0eadc42fda684a21ffde3fe2a0a1d3d 118888e22f09ffecb3a079f729e993bbe3a2f8ad9b3ab98943d7b6d039c93e3d 1c03bb98ac40e56b9101b0a628aff1f3ae682a026cba93ede7304f942261f04b 9f8aab00b42766e651e5d5c5a71ea8399f52bab563afa059823ec0ebef99ae32 52b84d2cd9edce72b79d4fe908f59290c7231b8fd6479b11b81e990097c65707 2 c1581d9540631ce2966868d0fec7e4c676906887b94a4d16679852b625fa9d01a07990ba0a3c457e78dc493427e406afb97b002669959fb1c768c55a7f7b96016c8da559c0500fdfff680212db4b9c30243aaf27271dfc5046e9a313aa40560eeb8849c6a58f0617dc59b1bfba8429979784cf63bc1e7bdec89bcc44eb3e0905e1c306e49e593f9d349b75b5d4f6891b863c4de88219ec5bad1399dc50a3030b976fb8e5501612a3ecc517b0f2c9ff9a07120586398d169d3d812aa3cbf81b08c8fe286d93a6b1053b5e6f5e478cbf2dd10c258e1c34fd4b431e56581afbd1084e71e97da3c2a4511a2dbdcd8cf7d8ccb89c264714f941f49da7941fb1c11a0b +generate_ring_signature 3e8b6dc355913127e29a053eeaa791f7b9b25c96292970b20617a77f2941ce66 864ec9b981e85f596e34ef440cdab5ac85878584c8bf0990753d53c281430f5b 4 f04f4e042649c5340e6decd37d44afc47a81e00907733866c866d853e10616d4 d7ac266b6fd14bb55b3a4c3fff28017b1f2d51591a8a9a38a921b55e0ecb2b36 c579e81fea2b7202b881513f371e0b5cc4ba5e941e7378cedbf2355e5df74ec2 16cd14d1087769260824246f32fbe6c6e8068e7efed639574c837904fa186e57 1d46be9f88c8a3546d8107e6460b04cde2821f0c815fdeea2a5ee8556b36f006 2 83a241054339ca83784a712e34d3a686d829e50de74576380be5ae1c1cc99e077fe486fd923a435a19d9b6555ad855734add72fbe7e719b9578f63e0f1ff16032abfb77cee40451dc6e1d9d5611b7f54daf589966824cb3227d55bc4f2b9fe0960acbbf64e522ef2f738b7fbc8f3cfb4ec092e7608e6f56c0a94afb881f57003a88e35516894ed42ea1da2ee62b836d33ab649e8c11adef7149efeeb40536f0f108f4740761f9186f96df95e6718cb69538c367051dca6e656a348b10977ac0138bec3b830e9da4dfef7d9d0316d4803ada80812518d6db64438dc4204b6f20f5fd2f368d3def5df71cb8f882b283393b0965637d8ac400dbf97147197503407 +generate_ring_signature 4eea1e52284c31388c572c77b4dd6bc18080d356f22b220fb2cf4a1706bc0f86 0317dcfb34494772c5bdabcc64b2661b8f061614472914a82ed7860376853ec3 4 35ac95d061c9f478a387f5b33ee56b5c962c622cb8335e3f5b05a50dcccb9567 eff1403b48ec6986a6f3a436fa75390d47df6b75578cf7c00c9545fadbf9f9f8 bafee79e294e0d60c0cc2c4ce4fae48df86d0ba91e46e4a2e6cd033b34c74414 1161986f00835114de03ed5ac2f9b94de944c1a2b9f3a237c8e3bf9f0b048349 324b8b23740f0a95fb37e26b28ade940114335d0c395200e3df2dbccf3fe0b02 3 2c5102599b8dfb098bc41dadad8553f3d8de80b97f7f825be031e4993720ca0111a5cae362cc831a2fe2886cf766382700b8cb3b63e667ef57c99c1f7e964e02acf16e1b4858d5aa24afd02c8117f8841013476bac8bda6a87731fda498efd02b77b439f4258f3d88a9cacd23fca62eea7cf6bebc9e74448f2979b92662fa9039f751f3d49138cf3c9f996627b3654828bc118c761e2e674d6acf2b4e892820b9eb34e9c5f3755f0d84ea3a75e57947cfaa2c1d2aaf9e391906a92f5aa25c00395ccc07ccaf8abea39686715146bcc1311dff525684bc6538a97a7738c427a0905082f90b7cc5ffba7da93940854ed5e3572069360df887d52e3b093b3bf3909 +generate_ring_signature 3897f3826263809665c0f79b257b70f91ae560f59533df45c79aa481f14ba6aa ed20816021e0870adcae45b087cb64978c27a36fb94967dcddeba7878c43065a 6 1f644846ce53a77e39b3e5ccc0c578bba7b854b801815d27e4a279b929752d66 9370f055164552e621e0ea348794778e844013451447dac7420ba92789bf23a1 58d1f500efc8f928bae7a56d15ffc8b07b65139f4578ec2ee706ccc992d46cf7 07c2dfc3e82dd1625eaf9edff3a2fbc75e6e641a41a2ba56ed0c939d2b81768e c2156a0f6cb0f9d9c49abab8124b36784d2f0c676c9af313d1215357e5203e4d 016c41e09ae240dccf9ec65d33ee94ccc74845a9c8c233c2a2bfa220be6dd093 2753960b51aa5cef3e1fe2f7766429bf0bf3cd91268e609c44e5e1779005d20c 2 f738c0489fb544b24d02a8d28e231378f0dc1cf12814571c0aac80f14eb04a0ac7f5632475083d272560ac01537389afd92a8e8890de713a0d2bcf6566c72d00229672b135459b0b00e180db7feb3f48084c6b03eeeac5e2de4c97102a366c0d645c9f2df8df0d6bd74eb96020d532c00be3ea652572d0f7be8eada19a1a81061f3ad675397f54751a62802c8a9f1f6dacadf96aef817144e16dc7e7359fe50057de3b35380699d25fd5939a5884101652186bdf46cf6fc3e712ff0a6e056005e52135d38bd7d7622e928f7fd375aac38e5fe379d04efadfe315feecb25f05065591cc787da2607eba658a5b596b3854a805ed8aca657cf1ee00a9fc9c0fca05485eea448330e740647790c3a658040d486c79cfe4d7d59365099e680b0d160399c8b4ab33083b1f9b2d30edbc4eaa7cbc9ddec02dff44055920da09cf69a90893fc900c2703aaaaba3cf9dfe505cb8173272660dac59cd86aa15e1ab8816d05e842d89aa868173666c2e077b942f733052c7f2c971ab720599be50fdfba3b08 +generate_ring_signature deecd93de3b6635a3900d9205d9a0445035451960faea63acfb11ab858265584 1157cad423a531df089ff8735908f4cd8d8804ca6f796839239c18df3adb224d 1 888be7fe0293f3f66e8d0bf3606153505f274fb692431e258bea9ca2d140d85b f8dcafb41c048b60f3119e9537c0e2d44cb10b1e6734aae324e473769a63870a 0 0f20e6c16be22265ce6241f9e0f5be348dec032def584279cc0cce918567c707cea6a5a7ab04df2caf8ba67802e3dc4d6fdf0d8102310ded9495a3bcfa0bfc0c +generate_ring_signature 81b47dad14107e991eabca238e4b65e7367928f92b795b2f3a2508c79608dc19 c05b11bf0e1c3e0f47038d6aa49ab4daddab874de20e0886bd6f6c578ff6db01 2 b7f3a03806831c048c28fa2ae2f5fcac0e6d4a76cf9fbccdf2ba1d0e3229b35e 6638087a63c6d537b87a22004cf2364162a52246a68162872931a6c0c35aa5ba 75c26eb3a2b4a277a7cd508b08879524621a628ed9ba713b276161ceccd2ba07 1 f386cc4ba9b309500795c7a56dd86fc359a7a099d5af9076c0944c08422d4503815597721bfac1b774e9b0ceace12ca6f8bddc8dbf57c5cdb37047b924cd2504a0f7e6d53b7ed4cd092d685d6d1cdbee60a06cc9e317995ac505d3f7b346eb02f960ea6d977a8caf6e4239670952366ea226d3b22f5915726ab5ff1e12774008 +generate_ring_signature 27f39c66f6a61282ebe04d3c6116a57e4133798f01a738294eb6a52c813585a6 a9750add17565f877b2b22823ddcc5ee4b89f41c728972523d61a69b78e9088d 1 b02c8a057b5cea1652d1a4db956781e6ce07e6f86e747ce176a7eefa3d4d652f 8ea9d363e3c1ed113f8b6ae7672511441cad4877336596e940db05efd1e9ec0a 0 98d385d75e74f0d67b4e56814f0812e6f201a04fbe17d6b189b1250e5a057f0f49fef4b0551b7e19e6aa8b9874263cb821c10f3a73168a4b52f2ee5ab3a66f01 +generate_ring_signature 5c37b80efb792c48e4c99395fb19626df810aae0e617e587415938611e35b37f 952470262e4fc8ea399640c866cef6d408edeb219511cd4eca7006622386f661 14 84cbe808524c622505b5e67783989b593fee2b939b5932b8c3fd77bad3b4647d 60f3e7f2287a85996d5aac23f440ea64c6354fcb1f7013a5b25b081f2dbd991b 3f4a81047e7b9b1b8d642f8586c6f98bc20baad33f75a7504afa7c109b0fe612 40d1ab12988d9c1766f01559399c0e0c3451da872781825364a1c42b46760ff6 549564baaec6c44ad533e9f01a4a133f2ba260da2127a78ce876c30e97c6cd58 82961348f57fc20143b0d6b30e6bafc63b8575bd0a55a28df76af8629cdccd00 119798a0eb51777954ca108539337f40554d4b2df47a4d21de97e135b66b9dd7 ed90cf28e5407fab6b8d8fb70a98d9fd064cc0beb47c61ac94845dcdeca4eceb d4b35cec0f367a4e133cb2c6d4e63c07ee92247ea1033cce6bfdc7a67474e224 d37a52f026a589b51064eeeb64a14aea05420c2bc70d5a04ab1d5128819ad3b0 968604beed7f12f1058ac5312bca68c811064117b189e0e4333db93e603c8be6 b3793cc7a8a5daa5f02ca4496390157df25213562e7d4fcecca72e9a23119e71 9ecb2e012c207c1d09124479bdc8ba9fc10cba28ee51301b5c7eaf9ef68fa355 011a63f6c9050800a210f1015674fa555ae80133dbea7e709dffcefce488f691 ee5d09e9d9167824d3ae704ba8c5b4d2236f69da9002c28cc0faa08954caf708 8 a77f66c1bf3aee949169c2fe8d9acb131e32f9c2ffd23ec56a2829908d562601f3eea6208bccecd66f31f596fa9f00d18a0a15691560866ef26d3d430f618b028916cfdc1fbfd1afae37da8f99a13edf81a0ff3760af4be77e4240afd58ee80bc7127b404467ff65bbd3a8f9e4b642d471461279091675c66ff651d1c42efb094148b87d094133ba314676a5caae8dae51aa4733ff8974b913d8b0c4186e3d0ab3291402abc9f06317057cb2c430f6b2a4908d8f0337a4e562219ccc1d086f0343fb1a2d4f512e98973760206403f6315a59cf5f57272449d04507f4c7330106d3704033300f50e6224fbe3b62dcd74bf951b8a7fa0957ecf4a226b331ce7f087bf4b8d4e15b2f4d5d0b1b82b967405b9aba037efc362198157f61c4646c1b0e2c398117c269d0777bf546cdbd438a9d820bbd538d2028cdbd8d9cd481184a0e65edc53e29ef19d014298a3ca602259eb53099265c4bf08368466ea5ac9f8708df1c7796544e42665a130828257889c42f0f87a7e703cf9a4ba6a73f0c75f00e79e6a2077f7d711cc50a9c70ae1a111764445ef92e677f1008ea236dfe232d03e4b26105b27f1ac86c2a31ac87cb176e415b529438607dbe899c8d0e1c2dcf07369f9e4c1914b03389c910d8ab43b81ef611b35b518a8854e7be3daa14451605378aefad4ccff7dc639326ac778ca35538671bc8825a110d5339a6054690920af37530856183484de3260a2fb5497ae21e27856766e3e678cf7454dfb2e35f0ecc758c412d8d0be58f3b9c71705dcd20e81abd4a668b51c4f7f165d045ff490326d7aa625bb405717826284e791bb9cf4deb47fbd0ccb9ed636e296a28882002961bf7c850682e5a5bce94f66aa244cdbaf5b69305aca483fac58546d0d4e10d3396a42f0704bbc70dd830a500ce3d1484c41c475af49ae31c0e8e8f4248a5087a77d595078a1035042868f98373f7966456cd8109711ec1cf4f6a9f44dd010a58ed9578348fcd9d1c8cb569d81b95146d6b42909d46fc52fb06528344f3160c0b2388c2ed0ceaf32da74a5547feeda44f08bbaa7d04628109e73649b080120192c9ea78a19e3c1cde8ce487d07f25e2c4df78ad19f41fefcfd245eb03af380c097add1152d58c51e97d830ef654b15a93afa0fcc0f319e7304f7e8fd331aa047a23a03e544c33471cc97d6ef893b61cc74295d03d1dbfa105e5a02207ac47035663503fda553319ab0d830737ecaf8ec2b27e23cf5034bb003176e98319f40a +generate_ring_signature c5541e41e2a344f29ecadb669ec32474ec245ef6b2b5f06717612477a8bda42d 62feedb1dd4c876f4e95c2733a958adadf85d278f946e4c82f222a7711ca10ec 125 2804aafd5a6c29ddff9968bc1d6cdb3ab4d346e83d219fc96abcc5729f6b703a 77521a9631c275477f4163fea03d7c3fd704fb7b5efd379320e005840539af0c ff24636208c5422a9c83e3e8de964ed134553e54fb7e8205d2e77b325c66ecd0 f21c08aebe88ad781e69ab0026121fd5f134720075db02bc436db5b6ce9b459e 0535f689f85a203f3612be098e8fa8f06ef477af3395c6a116bf9e3280f57975 ee82c0276beaf45e6efc7ce81696bfff42f557029ecd81e0df7887b7dca4b0d9 f760789ca02a9fbad11532b7cda06619a32e2e6e66973039f4f843a1cfcfb906 f41125c6fec4783f9578aa8fe9fbaad0dfa5612a829c9d5b055c778f404a120a 0e9f1968af9a915ce9c88c0ee9a1c42bc4d6f606262509404362a80feba773a8 43d5caeaae639a65004bcf71222e8ca2d3b2851b152dbf7bb89aca28c456e63f 16a37a71f407d8ed2bd161cb9d3f45a9f2801ecbe67ccfddcaa31cede4cf986d ef3ad750bcb7779904bd2bcb5561d505c1c1891d8c78e46e94cf1d63a0fadcb6 746178b33c58f06a6685b550b789d398862762f14ba9a3f8331b2ba82e971322 ef470ff38a5ef2fe7edfc030a77d086100965fae7a341afd095867a411eb2745 e8d43a2b8c03e0eb1d64109b1bae138ee4578dd584480fbf9becd4e63ddc3ef2 f466151692eaca15a47af224b8b220c1f71c51a78b581b1f12695947c790d09c 0b064774a419a807e737a7ef8ff52dac16c372448243428c321c024aac33078b c4763dbe8d39b3f2eadfd70260acef19c336a1fd80a89d68bf8d5564c467ccfd 141ba183ad8dbde5e67f98828c4adc018d53db2cae83c34e3be314bacf438591 41927644356a7633faefd0bea5fdbd25bae1e720819d2fe79500a950e3fe6d98 90d8b56157bc869813104b12784c4e88e908186e10435d50add8746552f89c08 9f3eef380682bb8b6e0998bf7e3391992492183d624b829ed3340c34a6affa7a 87397030797108c58eb9502a52a18973db5700f8bdd610dadbc80aa3b6c66e59 2c5d5a0d6bbeffb62f288ee87e5762d6fc9074bf787d5df4dcde7a49e6efd1bf 8b2c1a8dd258009d5751dfabecc24fa5935f0d252871dd66d293ad0185915a5b b1ed2a53a023134c2023a77c7725817a9205f831864acbca58f9c79d417bea85 7f55eb07859de3dfc0a8e206d1d1cf4a0342488987a6d2c18f41e97df0df014e 5188fac915e9e03a83302e4ae128f1dbe63b89767b91a4ff3b9295c30dd3bdd7 10ff7af1f258091927149854228a5c7d5ff271bf4bda55a9ff34d1ba7675c509 38e38959c1ebde70dff0a5bf2257a13281b11d432bf7f51d3737c500c36f7f2c a19df45899c18cc81122cbad000567f8fcda8e789293f9b77b5845c03892fc4a 4c33aed9b6b8ac36afa9892e7183675ae5490d21449940b207aa6466cc1e97c7 4292a31b6bed65a201ee83b71915c238b72072ee439b25a2cd496d2cbba85250 77891645ef71973768d5086c5e666eee6cd22c18c9c027277154c294e58c1107 761de841439e26204b01290f022bb014db8df120501179be8f3764d7c19fd8f5 9e711d6b3abaf3a78048a9e1276e6d70aa4ba0df9673d7b7cd653494125e585f f9ea96c8410b15246095f15b1a080578a3d2e4a42c9ca7ac41d713d69c674237 13b6fcf0056f59eede98eadae5e9308012083fe4986c744e1fd93db114c389c2 1ab06de0e3067113d4ecc48c5321fd6928e8f30c0b4c073e21a629f794c930e7 f2ab29c1b2669413a71b096379c3fc8b21889ba95518e3fc576215fce0033592 9463f8f4a96d456025108c444132060d710a6895da60ba9ed29cb0e7fcd222e8 f17341125a334c6f5ba759ad278014e62bab511f9c7fa45890f99a07463d5299 0cc4709fefdfb59de0bd4bce0f3833a2e3e9bcfe56ea4469f9c1bd9e219ad662 cf4a202bf367be4e41350c1b3a49a90f1faaf925a73af48224b02d8679577d84 844e462cb86c7906ba82a2cc5eb86828fb9bfc790abd59c2cf54ca6c0524b6f7 265cb58a1b994444fd2a9ec13e22f1e219ee2910b87bcc809e592d4597cda731 eab37e95ed3dcd62f3ef9d98ded850d36c437ae0f7c4d360eb9bc0006567dbf2 a9bc3a316ee0f0dd1245d33aa0f2b0a65dab787db3b093b583c1d183cdd87388 c593bcdf8162435ede45858a3f82d18b6e08ec9917574bb630b019885eadc15b 519bd964446b95b440fd2832a2bdafa6fe9dd41f91bf9c190827ecd014817149 3c97c72e64b47cf79222f987d41a9f95959b05f60aaa714ecdb375f6bea2f24c d0799a853482f9870120c87d82efe93f6ba672e361244a1308f5a918f8026204 3fbcbe6e8afab9f595dfc5d42092b46ced57cf4a77a7732010e5435df18bcb35 e7058fd902caf725e1937654bcf210f8a08bbcf4c174127a19bd013a4ed13c5a 3538cc278efd09cfcd347eec14091864878040351f128a36f17f8eb1bc1bca93 d023d792c2ecf5e174d78de4f4eb1c972f05fdc31510ddf285ddac03edb6e9a8 e3bbc9156f2d1efe4b0e5bf24370772956146201f29458902f299e7b178e2926 5ce05d70587d6f961dadb852cbf573da00d3575902e5a111204c29c1c1995524 13d6a79c71d331cc44369af63eebe06208038fc18a35d65a9bcddfeac86799d3 f0c18a55b75b0e8b8e34b5efe0d3227be9898fbaf8b270b82a347c477442331e 6d941ae5057bd14443053028bcc66e3df3ef8002ed4889722a619d27af42c4c0 65d401e25158b5d536010e71ddd6face36fa59159c7147f57f98dbabd6c03e3b 236e3b8f14794886df4b2e6cfbe93af86a369ed36ac7a163855fc84a917d132b 2e731fad62c827847155d6b543e91656f33a37f29802b99c74b5ac3113bcd3d1 8bb40fa5f58d155cb5875f14fd8f540b6c5dd7fb230707cfa3914d8bb76a9985 b741f627de1025788557446cd8571f965165f3b87ba70bf9593d2543060b1dce 40c2cddadde1a33d787f42ff20b897470f6c49a2d1cd8fd9c9aabd4ed20d8098 b9b5944a94b1024f7f03d858245bc7085f2bf8c9b559040404b2a8f1f5668353 229f8995a931a23752d3617ba4c06a74e48a6f3c8bfd40aaeacfa2fcbc6ac031 c97156ee1235c56564ba3bcfb12c645e5eef9bcfa4173c3f7efa755646a3999c 207cdf552b7744e8e68e4721ee56795e2ea0d00db61156784b351b07d91d9b16 cde0f0dc90d52b59a17d9f220a8993787153aa1b2dfb30074633219be62ed6ca 2f8add5d450bd257117c2c40f33b50dd4a85ca2946b10217344e842ad29a3f57 198859b069a1c48e96f6420b89dc69cbfff32867574717ff02965e9ca9aaa9f6 da1c7cfc3af582887f4bf517548956f949e1a83a8b82a5c8a396df15e1ad1fc8 a0e5719a9ffc23af748a8e062ce1ffb8c82da607c2ee26993b0e76c01c1951a3 05776203a11284fcd79016b18d7b4544d10b9ec88a861baaa3b980ded424da0f fcf8075b1a91c94ec4b6f8412da739dd74d749a3cc55e9574e0b9d982d21ef43 d567383f5e2709fd6b640dad0915a229e62d8e44880cbc20b71637ef6c883cc2 8c1c065394f4585c0244671370ec3aa9f31c73c109f50ef0290e82b41730f3d4 a6def36e17f8f1bf3225f801454d57b2f8792adc94b1933fa01af88fe8e967ea f61371c2ab70bc8012c87fcd14f12d7ab1a2eb51875c764acfdfe3a2b3f69487 aa0303635376c96892e2e2f609d626b6dcca8b19879f5add956927ecfb6112ec 327a716cbd42649a2f59d3fcaa59cdd2049e94a96bcf21d77249246ffeacabe8 50123f75a9558eb33656757408e2ab0250269a03f9ec1dfa8386b38ffda39f6c e38b6d66bb8d6be42b76fd58aa01f118da2cd9249adf097f1b3b4f46a54d7ac4 108e7f9b934c25bc12d2fbb18031bb211dc7cdf996f111d0a2e56d6cb9c6a9dd 5074acee3dc35437abbeb588f5d942f968d449823b29942c21c79a16f05e65b0 6038f6d5324fa1e378010bf8ce3bbe897d5165b764396e25c243db6906951f3f 969d74fcf9d04f95b465ca2b88e50a79c678cbb3d1e1cfda6002379972eb676f deebec572142847258635858b17708930be57b82404f14d028005099d670675a cc8970b85874d414e741607ab7a9692512cd3108c0622c89128101fde33eaf83 ddc7f19c8d9a83c0c32191ca81a3430383c6de882741218a4150c9779559cae4 2270ff3129579cbaa7f986cf738785ce9fbfe70ad863cb39de9418905775758a 6e2a1ad2fa32e93d2dca5ba62643116e6b0788fcda52a9dd74c656e358370580 0d7b0a579493f836a031ba1a45411f36e4174ce06998bd231bb22e4a61aa4340 eb7759d06fe9444869c5fbfb6276a97ca4c8fa577030618ee50c7684900237da 799b5c7306c3f2882eade612689f64b84e03fd984f229b2bad363e9d22b33895 271ece66610fdafde9ca4766cf6e9cbec05386e840d0b161e5508b1a21d025fc 74528f7ec8243c31361f0c20b8fbdc3001c7af4a03e156d907048fa5b02a939a 5cc57310e3f8c9b79dba293353aebb162d7bac8535ac93366bea3d4ba7421dac d0440a843f298088834e32c073f27400c239423b5964cd4cedfccfccb0dbdf7e 61b3bd3533f547cf1c95755751fed8295be21372be495dbd6adefc160bd8e3a8 0a62af61f1c5b60e5d8e0a73ce851c3118b3a33b3e98248508ffc21c12e1aa30 a2bd0986f7ba0a3e2acd06c9e5bdefd96e901ba630b261ab1dce6ba18628513b 87a2209b6183ebbf569c618925fa782208943c0c4ba3be4ccd1dbbfe4fbefa80 c929225b641df9a42614b29645ce066050299b51fa4f76e4d5badfa50f62faf6 02b21e01e28dd2f197fbd265b82c6febf31d13c357e2e524fe770938786c0898 e224e1d91b7f496640c10c658c983faab5626f2ef388c8573262a5c2b803d737 f7648874f6126260ba9b85d3dd99419ebd95a187a00f113efa1cd8e26e24e66f 70d018da92f36ced31cc9298b8be6e9b7d1873b3443c604e0911be4cd6bdb190 0f7966f0a07e18ff4715fb0d0cd23d99c8c6aefb0295670e86eba3100458c466 327b0f4c5a5d34c9e69b5f3c2d9e32191ea616f31afe0cbad2db717b29cc4e15 e6c620872b328204fc60016bfd6ffcd5f475868e9eafdad2f90fbe8fb604653e 367bc5e3d53ecfb280d5bcdd33d37c3995a9ea2f4488f20b0bcb8bfd84b9e655 b826533c6aeee95b7f8aa6b9bafd6604065aa3f048e55acddee13898e7f9d41f e7a4ad5121e7e60dca0ebf2f49de2f19f622ac3bfccaf7c59d548e08f5176156 7b9802431d797c61656841f32129f8a99e23f625013df856eead6a0eb63709d3 6a7337412e7935d128dc296c0e35be814127f25ffeb42b64ec0dbd294985ef11 2ad88ddc9f00c78f253edf8a26757c38f2ad59cbfe69ce6cc0cb6577dc863782 d8e3961134ba49851ed743543d66a85dae08fa786e859ced4d4f1e9eae6c148b 157eab6090d78b4383f756450430cb3a4a993ba3f19e73d04fde2425aeea0703 614ac9e9b8f848877e3ee8e24d1b4c597f6afc2e518175b02b209a69c0a565c0 0a4d2290321b8aeace3965cf602c32739a0beeaa91567df3ca40efa915973447 8ef0ef41d7e977f0c0f2b955e8868d331a2cc5e1aea3cc014a64d96f1653b80f 6f8dbbfadd4f875bd3fb7860e6b522be87b40a36a9aeffb7d8de0115f2047b0b 66 4dc0004a909b0ea093f3f5a7bf9395f1836f675f63225958ca66db4244c0610824c5ac795e078a3ff4d70c021ccdc506e522f9b57d6ccd5c61a14e84bf7bec07c458a7ffe6df15fbdeee0f44e89a762e47229748764fd8c2f653927dc65bd7008e6a6807b9236c1f9c8b4a1fadc0cca4f821d3f8442743f1df5cea766f4d030278a5fba36f3953aa802e48cbb5b420f34bc3d29445f7099462637dd769c1dd0a3a01e6d2cfce3b2bf86dab33d4167d307d1d25339d10854dd345ab54cef52600d6e377bf9c92ac9528a4ff2bd4931a5860e7387523cfc7cb8f70e95ef5752106299d63a321d9bbebdadc554982029f0663e8392a53829475308fa7ce53b431082023ee77764f52e79ba7cdb268deb5d8a13cae9921bad88597cac23a33d931025a12788a0254eeafc6f242646e7723ba124c3d30476ee70097114a7147599605eb282bc86b5c012e08176e40cf3930fa956b0f340af172933bc824c21c177903c70f70185bc2fe378e6279e1588a25f178ef000ad8a6008647d53742cca5ae0871fb150c700cc649c70d19262811dea8912ec0900429e554e56a6412cd492606e2bdb0b2ca12269727ca3e3b2e8cdfa738f8730914c63725af4c7a8f7b8c100b414794dda4fd2477d63a74f167131088cc070bb9aaf9f14173fc02db48a2ff0a8d6a78a7d3ed7ce29b4869fbb4fc1404fb4a9d2530d4e0cfba4afb242758f5004b7b1efa019ea7e30ab61f25a8ab5bf92c0721e99700eae5ebeb9a9fdd3e66081ff4ba01ac9bafe46db58d196c11cb9304b14ae3d7a3385f6cdb7faee3dc1905722eacd1e4d770923bcad99a45aebcc75b602151d36a2a83e7c625cd8fca480e2b969e32a19ca91c13b003e1d038f1b1b61dcf5c03a44d46932157d48e77d50430d0294cc416ac98a306fb9adc248d16cd6674baa2b24998860f366e3f2b4f076d037dc636ee28c2590cc6544ed6e520a71c55d6d458bd6013cbc7156c8ae405e1b1b68b33b8abf7dee5dc42cf1fa130b9c9e5108330d374b37f154599ba290f9b983c7942f66d50bea032c6ee85b9472c186e50c53689d09a97cc657697e9018a3207bf4d9859058074682b23d7603fab003e18b19c0975174be4267f275e01284d7ffb807527bde515b9161f7708a30ef256ab7dceac24044a918d8ec01701282e8d37142e45b9d6ba348e23e2aa37cdc6c0a4f882f48fbd93ef0d843f6405af3f86e986e4fff771a532d0f1def54ccc8c7fd0a7ecc6196069ed5235d35505c24d5e3e1a1c9b01d130f1eade76aa4643ac0434ded63599bae730532253d605fecb9834d901326f3928733a002d9110d737a79f397630af8be30a5e4fa027046414c36e1bf9188419d80ae52f9d309e1fb1f45ab402aa2b4544d805a5c19207fe45708f8f01111502742e23a63b61763e3e81f48f88344771d8b50740fe940edfafa34d2da5d0312d68a5de4fe27f95b40382eb048b5d17edc58c5117b21c0478b40196daeb4e191b9438401d51b96a43664e8425b1ed3f3a834b7866a5d00dc584f7e68c4a54a1d02819f4855ee91a8639513fdb94ce05d936461013a4420c8b14ffd7189c6e88124bdb69855e23790cbaff2daec526ff7daea1cfda670c086c269540d92290dba760b24ee696ff5e061e6d18b25da69a6cef3af82e08f708c027bea729340395a89a239d4636e7079f95c768fba29b7d2c97c7b57d54590dffb96fe728d290829f9382e50bc3fdb14b832fab00ce44c5cf2bcc9382c3c3038fb4e05aa99d90b3e82c8d097279eedd21317307779aa1217d4b9a65349cf209394a10b7fbbe87dcd98f9defad1c39f2e59d2b5b511b0a3cef72846f339cab0a79e2b8fc82666e6d6164ee050ff1a74a586e9bd7796acdb337e154cc35feff013ee0337cd1b6291902f904a3a0c0463723ea2efa0b530b2748d30367dc51b7081cdff2cdec7083a3df621271930200412fdfae3a1cb69ab25074ac6a2b260209f185c74313eb8986e4d99154cf0011ef57778013d80205d8bcd4ec3f3d602f00ec4d597d9c207b6ea53062a36e0f7688de8641cfd09c418ce217e08b7f31a10ca52afc702255a518e24f2d9d78fea290ae9eb780ae51f5f75073948960fce00d906fc4e84a7670bc547708d2a52b3ab3381e7a540a77c1ebfed53de87dfc81046e8c74e582d864379050c61296d720be792823eabee0fc7a2f783a902ea020099c1e090e13bc4b92db2cc059b32a9bc73cd598bcc023509f6191728f353c93069d637c734d5f860f801b4855c9328bab9b9d59eb5902fdfd2a67e1d26ce7a30d5cc693fc0bb0100f246b24d59c784e029eb490518402fda1cea9d3cb5bc62f02a179ec62f872374a1061d9a0c05eae1bab485edbe8e53ef85ce34b89de471d0888133bd506de75de0b29d3c5c40ae4c78f15ed4168edec1a0d931fb30b457e01bbc51160e27c79bd01a440bc79d0b6f49e58bfcf3c1e2da8e11b7b4354ea9f067d3de16fc82e9066d181744203447c84bad3bcaeb81a737ddca73e29405f7404a30b2cdcf2eccb50365d45036f902abcbedf6e3b9781993f3d64880ffb65b5066fe8c78c26c1f4090059db8feb5aeea02ccc86cec3a6f18379465f18fb80b4073c6fcd99601c397911d4a053407c4fcc742b6043289d036bfab167ba1d85840d9cc14e881e52f68e7ef84f895986f5a5c4c82c4d1974acfcf348bd382e6cd1034300598fb6b438164d7997c93f3be5a3c94c4856523209a9768a5ebac7866406e2a3fa1b21d7684afa80afa7ae616143631f6344264be67968e4ace35cd8850b11b94c9711f89b43172d0fe7c4119e1480b50900399417976abf1096ac278e08af40882eb26f137446c107b5af3de323287915b727741afeb8685ce8a0c5160761b2397955cfea1e719fde1eb180bfe4375eb36d4d358e4ccb3f05e1e955ec01b5e663b12629cc8d5afc25735a97c1a8c53d53deb8c49014423f6f953fa86f0ba4dd5194abe5f6a1ade9ec84a152e4bcd95c909a84e709cc015ee37999a4ea0891151e185cf33c68854011540159a89ccf582ae4b605e168ff3d7c447242dd07cd19610631cb4e817e879ba59c637d5e0eedb8d37f75f90274fbca5d8da2d6055693069546f92530d1225a73e61c37197d224fb495e6a8b2dea86d9ba472a30f6b207101bb41f5bfbbef12d043281d6763881c7f58dd3a0ce83f102cb1e24f07afe7e3132a83eb35d7506b842192b407041a219b8ff76757a5acbcdee2847700696796152b4e794fcd481520fa47fa9ffa14da0eb305fcb5c8c71de1446f370679bcffde16f2c7a30979e1047855e8116a4738a82ae4fb2dfc3fffe893b8830a63b1587bf7fd448a3cfca6ee640552adfa52bb32620a8b01fc0544f6473d3602bdb97235dcfb7d12dda8a85dcb4d346b3933873948f3929ac8f54fdefb486d00e471508918c1f7426d654c32d1ca8d2517dfa246060670e7ee56b2e9d7a532073f356fec3a2c9d94824f3f702c036b58e71a76ba9f309736076357cae37be606751e120e05206bd1133d022734a2ca465639b1981a2ad786fa791ad4f6fb3900a5de9268dfddd7cf39a8105c375d4e5ab6003602a66d33e63d99f966ea569e0a1f221471be8cba8575f049ead182886c339e7af1c4ad797906faad46997fb508f5d288d87511e5963fd23c89575990e7f8eb9cc237b14740a16987b99cb4220caca046d3644e742f8d099657cf68945c4e1e502195c382daf2429bed25dd1b0761d9141faf6256ead78c7aee9dcc42a505796bdb35d94ee565e468bc7ea00006ca58e6f4c6dd591dbfd6f3306d2d765522f68bef4ede7405190da56d2f3f4907eb98883dbeb5f022bd4190e8a34b2d77f7c626e75675ee08b51f1693af8cfe03f9ea10294467d8c136243972b0fdbb2196005c6a1f3a1d6b303fbc3e4ab33c0c977bdae51d5731bdad097ef740c6b12ebc634fdecb82090eee099b72966356006c89681f1fd2776c29950605daa95cb1a0fea3ec6be1a7dc91f8dd626c570506fdea4eb5cb013ca970c104e0a39096d5e4a7388740ce44ca0d64c2c6c7448e0aa867712abc79beb2d5366f368a75edf4f6870094cc06c091b0f188cbdab84d0e2838297f675506a5914845b282bdf3c6e1120a64a64fe0ad2ded2f5f4e86970d09db1b11d7420b2aa592ea3ba101390beef50d42596a44042abba6b7d8f2f40d7c7468f1048c90df151aab5192309ad06a3674ca1514d92236d7b22e1181ce0f4eefb20b710fc0dcccdd141b012f9142ab7a35051242fb329a56e499d1dbb80e1ea14af799a5e00d5245115081686a6e16d867a37c7a0ac864b4fadd125e0c02b80dafda7e91ffb641ff964384b9201c0697e3fd500babda58839f453735eb0c872bf6dab726fb303ff13cf7b2730c0bb8b13d96333bb01e5e27caaf4b6f760fbecfc17f0359b7e33745774dbf05fecd45519acaf6d0204d582dd60df48ca30946e6f2ad7e28ec2e62d3ef75a32b7434c723c28bc15ff71d0f7f8a6e2f6151021b4502ba6ee6042d56cf7d8ddf9d7a45728ad1be62aa7078e9baaa6cf2cbe80d336d7422f29c6354f73292f3ef0d19dff321bdcfafab53ac9afb5ee924e31f07dfdb9c832f2eefd29d1e44435e8ad9fa64b41b612382b1362a3509d78632e104167cb17bb645a4e261db047ea86e6261373e1d67844fcd7e87d8d6b24b0b99083569d0497527fa36c53bc9fe0bc4ab94347fd9879a79501cd19565d81718970d9b863026e088463a9181254bba9bf233fcdc22c5a5e3379ddfc33e950b11c8040ab1557a7753244175a3d434862bb07844dbd2da6087f9c1524e83c7e8c9d60ee56b8f55cf0b61cf122aae9592d0af9a9abcaa9dc366862018892676cb9645027c17e9493bdbc53e5ae8bc29ab6751f25c0e041a4f8acef0a2b6534b5036420e2bb54030f05959225d4c9d0ddf2f9fe0ce1841d4380e52ba5fadb5e586582a06e68acfa26cc07bd8b918d4d59bc9900cbf525586b2da060fcfc9dcae2fef270e25a28ff4f1fb449aab653b865061d4a3789c3201d6bcf00c13f8678c934d0009fea4ddaa82e5cb4b270b6142572eba727903033a45267567a39c86a3199a1c0eb2a97f5c5e1dac1fd99a88eff6f1930f3b5d32261ef6ce8bb3f721ec58d2d10dbc8b669044368af81efde77025e31ee9c5e226acf4856341a47bff97e97d34093db3b6d11d4c3a892fded7f9ae333749e1003df3878707f371444dec9f756804e3414a70d52120cfedd38aea649fe4d36ef2dbb4ad8b7e9c58a2dc586a416500c8f96d8716eafc2310c17f6760b6b3451dea0a31194dc4fd84a877b6b9c41700d2ed8eb936139d08bd0548dce4a7437e6aa6a17b528f5a9d8f7f01882dce8402e7b19d53c2f8ff1e2738dc2bb6680c1be82abd48ba09f9452ece81d35d1ff9058c38d2dfc00b6b4dde5df1b0e59f48302332e35b5227398497aa80ce4dd891019879a820f92e3ef301842993ec1d810016d21148543991549c110598d2c7860d2b4a459e75f8a5a3bf8aed975bc4335c4f6aa2da40b9b4e9875125fa7146c60d535f56953630fdee6df568aa32c406e0fefe6fceee7129353103cd37d977790fb18da1650495640fd49772dfcd2f8397a3d01b9a76ca4139ee8e9f6fb4399e0d3d14412fe02a78c0cd3873ad2f3a32c31ae05a98427bfa42916223fdac912b0190111e7ad6fd7c3a438594e9a2b2264abea9639773bd22be11039634a35d3b00e9daa8823e4fa8b077df4379f1ee1f32f31c384db9da7590b3e30d1cf7225b02272a19e373cae8c7a1d9e7310183af3dbd3ae19a6060d3b23071dc8008487905a0ef9011dcd39badf5e1e4092ef2bbd51a247d84d77820686d38c26e7630970fcea629f12bb7983c28be7d73a92fcc401435095ac66bd40350056445867d7b0f37aef3134b3d01444c9e670a6615557c283a7592ee5f446480c40fb6ae961106bae3137c8145e7514374e55cfde7001fd1276aa41a36b68dd62aa438faa5070cc5d8e3f02b870c254ba2328bd9d29dc8fc1059d5dbaf950731fa620cb0ffeb016e0ff1dd7a9cd8f278b76170b8092ecbd63236ccea97539c4708fb212a4ee5001f67a175b927bc5951c9c0d708340248e59103b86b7d32c591251b55f753e30d404bf8dbf40f0800348b301ec6b6b9b508667c8548cecac573ff2b9a2247630c51f58d56e558e39fe65d104cc73749d686bf8071da26a2abce09f5e91cb39c0c4f28da63b74a1f27a4eb9fbaf27732a3341daa872f87e74ab38a694f54da55095bb5c9b4ff005615c7cfbac47a55a79000f3765b20b8e409a664214ec86e5c021412f6505b59d347d98c644f289dc752b97a20767744864d6c85e46fdf543b07290aecd9cf9afd8dfd2cde0fef08fc85c3b5fb1c59f3725869b1e883162a2406e3aef1e43e983f1fa91d03d6b24cb8100717138a64561bb2582884d7c0cd3b017f135a7ba72ff7fd095cea52906040ae914499567342d59908544a8d1e7c830af8892a55f6408f8c45cad16a23a1ed76262a3a5dfeb8b174996263a5a39f2b0aa458079ac018f048658716bad78a2f263fb041d322a8a437468b5292ed835a0ac46664fdb56173d4922102414e37cf65473b0754ee0112ab49cfe997323a61001f581875efd538f8c6ce3bd16d191a3515c8248f4f5c4b6e7961dcda9286b50350b600ca80e76d21db282c2706e5dad59a1d8067b20ac564bf5bd6fcece0fa02e00ea91e70f1ef97f0109987761778ab1ee5dba88db2cf88c652941b5ea0ea03c0d41a55845fdac42fdbbb91b4c78ca2fac40f2786d3445a116f50cc22519606ec742626a24257239c18640c7d548b08cde9ba01c2890eeb975bc1d7e90f57088ca8e24a5261cefd62eb6cca15415a23c050d27da45cc0066a80db5bbef4d90928178802434ace8553ff4f54a12b8f11981f757be7776010e6436e0c72820509673ebf236897067cb144ab2a14e7c1a38815c08d7ad3809d86f4e4ba3f12060faa3cc3d56eeeb259feb81c1e19dc6ff6dbdc19b59c8005dc530915453362880aec9cdeaa0f720c2e1f6d9dcf8b2c805a42779ff93f4f0281e9191573cb29ae005b0b3f61fb7a1a8672ab2386a2a258cd3d579cceb7b81cdb78c82030a497f80876b4293ab137f0459119092701cbb0491306e89753a8fecd8356d068f843a500b3924e55199d1de3be74c9d722864018f47ace0b74dd4f9d639ce80d04abad00347ae4583990c44454d7331cf8cccea96427a4de05d34a3a955c4f1e746a430c834de19404a77fd33796ba12600690bb4441f7620a7b21e5252021d6f460cb0c59b0b022c97236c98550f26499beb6a80c8c5c6c2ab487d15d0d285b366c1d039378e2710597941242a64a2a9d3a3b9eb72118ed58b18a2c5bb1572e91806d0cebb74327e2e0760e1e81a2e61792e39f4f6e8e374fb015d57219078f8e8dcf00ded933de3cea84f7ce9f47210d43cea3ef0971b5e428f141ad0dc58ade09ec0eb36602bddb295784497b2ff23d589a72900b1c5bd596f7f3eb9805c9ab6a7e0dbe75f936497cbb41a029ee027086da196ab971cceb28662a1f0a7ef8b5fa1d0b1824e3a45f9619b5c6beddaf9d6de9fe6247094e73667d6fb0f86340e9bd4b080ac02f73abc702cdd9ff4cdebcc512621fe0e6e533c77e96f15dab1766a4b00a15e5d2219fd4339ac2ce51b33d8c93994f80b6940812a97d6ef5d75f079e3406397254b1aa03d06464fcf4b1175e2986f76f87eca47bf9a12526d750084f75026ce571abe2aef2dcaac4d34fb704fac8a375b71cb24696e51d783cfce68b6b072775589a34609c5644e1282b6df5b59365fd7b6e67e2a1c38dd3746eb2d56800ef7fec80fba750ac12e0e6253b0f47011d422b73796ba53ab7f47b3ffb5cd300b0b8ad0aa2f416c22257e6214159935a352efe87bf1407856784f23fd1014f02d44bfd5e56094eaddb138e33d79fad843507acb4df64dccb9a3e08cc7f352700fadd7c8fdee2868d5f6ecb32da1d12d4d782e788ebe071b78fed25bdb41589017fbb07f0dfdf9b45512c972adae139476da1af30eb19c34dc1b09f846e4b9e0d435cd56833b2d7a053e5e3475dd788e6f184b75ed53c9223012fc7385856a6038239e38e3b15169a049ecd44a04c3dd7c4493ef64bbe1a4c248eaa47917be80833b2ee25df9ee4f2748fd67439e41309304a8a8c797d14849d939bdbaef2c706065600ea940ad7952238c7fc512d9cea31e927a2b6799aed1c2a89a5d13e8607b49aac43bda321e980538c88bc5698c7141fe6feff5bac98fea8633aa96d3808606d545f289dd6741ef12fab862b4ae4e50400e140e930540dd81cf78b2f110123dbb0dd77cd6481a245e16d242f2d9aee4d1c132beb8ea45ff2e33e6ad8a407e20028074f1a89b6eaf6c9e2c76b8fffa8096603a1f41843de0130d968cf2c0b9ce0b54af3c39ae5dfbef615e8a437aefd3b4e8f276edb6a79b802238d995602ad64f4c7dfbbe2803be40cb3ac3af6a67ac86e1bd115500ffc95af0c68885102223c21be4e38ba59c440d3ad778ea28a312d93b8da981b615ec14eb638ede802f6f8b7a9659a178a5d0e428710b407ea1f0f981806bba9dc7850b15c3371c3092b299dd69bcb72f2167a50ebfe66d75b4f256fe16254792f5518e1389585d80c141d1cc2116189e70f5d790e58fdbe1055e59d4670810169d19ad0f5eb40af0dddc7f10e86786ae05cbfd4a5897d00e3f175203ecf39d6e33e252972d10ebf07559e89ce2e7b2a997d2dc922d5ea14dae614c0fb362029ba1c439fe104d0cd0adb0d2dd9515c7577b2ada453627af0762ea281cfec691f15daff0f52394fca0e15e77e76f95291c4fe218a781fc5e59c686395abaf78919f65a89c907a29ad067c30a33e5ae5c6e7d1b11e2f2b18d89f5a765467044a6ee5c924f580529413016a8be05349fd52b13382abe407f08fcd76e82f874a380be55e02c92827783405c5a4c201e4136d37527fdc5450fa536dfc5c029b85fbe2df2a451c65fcd1520a73d6d78fff06142f260eff5f68f6c6d6a473985d4259a50ff54a15a7ce0d9a053621fbe346a8fd4535137cfd5198a1ecc5cb2ec7e8bf4f7e48b4d4fe8abf9f05ed467dab5a373d91ed646b7183fdf5263dfc4ad173714ae7b236d1fe6ea45e08e422649589ace8c836c991b959155a8a61f658a60f4c7b1771a0b56bb37833065e3712f8a4d0f41adf484a03a2db86cf3ba761759581e9f339c8723133f2e707f5415c1e6d32c65f6917497a739dcb633a51be56c46baa4196e0057feaaa2f09efa4138cce6f362fa26d4664700fa5e7dba5a9068dd05e75279d7862b628e503d6999aeb7c7455fc1b6ef2bbe1605e0cd615f80c17ad4f50f568c75b8b34d40e73b271c611f249a76150db0903d82350b73d46fe2da93c618fe16a8c0991240295bb38dfe42b9095c27d51a8d4ab6504f2b3a4e88912dc7d44636ed6dd1d7c081ae6b0f0c7ae51fbea62feb5c5598265532b4a6c361fc45346f02fb9fcdaee0bd094a11ba49e0a635affecc691e01515bdc139a5761e263e87cd26bbb4480a04535d7a04daa59b88e7b7772c450059531b7aad08f1e313792eb5f106ce52000a078ff032112aaba4b6b461b650be2607f562e1f00578e0043952cd49db32100e1401f313f1f56ee68795aeb76b51a09d8effb36343b73bdbaec36ee10aa6a909631477ba215bedb1a9aedaf0f0ea6a8d187ccaa3394394599da419e914822903399a12a1ef3fd6669961ccff82ae74bdb2d35e6dcff068c2646c78847e69e604145e0ca5bf43956d33a228e877ed4f5fdeb3a618bed80c5e67c12f59c3c9c0008de90e6b9e3444ed6b027d8bf42798002622eff383220f1bb884fe3552040c0c6687e2bf0a5da43ece1eed36784a3e31a7c6e8e57643b4e84f7896e7104468048cbfa14f2758539eb3c62041cb9c511eaee8feb4581e0c31b1f56791986c1e09cae2c1ab6a4158e83016d29542da36fc7fc15fcb13285badeb45b462c231f10513667d398826b6910dfe0d65e57da0eb687a31893228f85a81b61a08e77af30aa24302fe34a3cf2b4c200209c63737209619bcee377a31f436036767d0cafc08f7e95b09820071030e49d7e74cbf0e6ba0673ea8d713ba881381231fb19c470cbf069190607b448d0e948a18cd75bc5028b8f8fd3d8500fa70c5aed87ecae003e10772fc6cc5da07abb8ade5c72d25307a7edafcada5745637398666e0538c0243ccab54690242751b84a34f0f7cbf6793eb7c1978d57f2c4648eda51929040ca1dc2e63351b213ba5b9242d1bb82fe5ff347629f73dd7e846533790de4a0c0fa51576c8b5939c98a57896052f84e4158ca39d8456a3d6ef78ec751a910ae70a1ee20227067149ed052e4add5edb294875b5b8a27206ecc2036df19cd42160028b23d984d1900112711083a1465576eff6b0bdb8184479079ddadf3ba6da74058118583787c7994d247562a67ecd8e54e316890cddc8698562a909d74e48390d4bdc3489613e9b6a5e48006163f1b0d8f93c4baabb54d3b19e557f93580a1f00d1d9999d7f6c510a267758ea5f4e5155d364c47ad0405589272c5ce3b0af8908b129947c63c6de0658084111b8e82daa500f2c0d487f4e46f7780699d9be4806e77798a83d366511797b48294a314ec53345db6e10ba721cb3070c2d3b17e90b31ff22e89ce20bee3e7f0b665b3cc60a8ed58a413b13b7368da5ff89b32ab104612f092203d286f9e8339a998035946b6a165f9182a6eb66d2f7478abe8b660eba8a3cb19164ce0f43cb350b3547bea194fd120bd5110a100fe6d9738207ee0a2d8447a7a9de8187e2b303003a246b0a7697e4bc3e3ab082601b9edf190bf40dfd084c664397d05a644a5445e4619ab71b75c8878be8b638f6a0f4d09e81f70887323fd93acb4b0584e3ab1aba68655e90427c9a1a2694ecd77a80d6071a8f0e811fdef1faf5a2cd2809eeaea033e3f918fd51157d0538d61a22571313ed560a33971875c62813364e8aa6be1fcbfdb3cf8e261994ff2f3916ee0ec7ac9ac70dcd70a95bddb71fd57da3c4e3d22d657b8ba8b97017d531c64817b6c659f51309b00fb5794c7086b8b9fd719af69074d01f3329ccebecef1479ffbf02abb6860b1c6b4af6bdbb7dee15c2e5e824f1a3d8a19fc8e8fdbf17cb541ee6c8edc6fc0972034bfcdcd8e0cf6568c5dd8815f3821d254a8fdac4c4c9f59075fff2b3fe02f371efc09679bde59822a4d17f37383bb528fb0722018ed742823aff2944830b +generate_ring_signature 6050b724afab4e33fc1adf423689add9c50984ef8df4b93034fe824ccbff6d6a d1588a8bc29b2df0d555b8b0632841b68cbbb4623ee615c9499862aa5b732cff 120 ef87037b6f29edae32f566f14ea179ecf470fbbcabf36b62d252b85af30f35e0 0adbf81b03429adb494d54bad3a726c919e81df09046e8f237eb9b5f82882a4c 3539a4bd1b04de484d125456d2135230caea0bcd47d76beea4d4521918f7e769 e01e642c44600e167c640013562d0d83d62e9eb696b70e9ca217725dd0b46ad4 9e748c90a8fc9e8839f770f94ed282e08b2d0319dc1287bb2fb3b240d84dd055 43001d2b2c696470582c98b0410879d5f5afd93d7bb90cccca625d289bc6a3a0 dbe579be20a891db9f42ecaa18e45ccbc4abd200ab7258131365a4d5c93e776c 69f3b6889f8d6d446374ae878b38da35741eaa4e4a88ef220d16163703c244d9 f8b561dded476626a7c0ba1adea2c1dfd69d96f0dfcace37406cb1bb25b19d14 881e289bc813db26f4a718a6d790d553c8edd952fdc6e70f1434e0cc81918098 0a56f9f412b6080c9bbd334a6a0ef92ae6a61b038d6a09b0b4f1fb38611bcd21 2b8c0b64bab89e760d5a74376f331ff45e5a5756619836c00ce5882b08e3c164 fb0b6c65d3823cffc4fd3fbdf0f7d867017a8c2d2070af7f69753836a10b3346 d58bf0a33db172ad6cd6460498f3fce85fb3a138694c3beb46d48bb5fec7fe26 0ab01cf3ce2a8d87dfbadb6477bcba61ef64981fea057acc295aec2dcc9ad983 3936b64dd7d013462f90d1969d8c601bf6a4f0bf45b443534eb2ccfb03998ede 412213951f3a59b88d161c057933de94085d7fff3fd1e370b22b16855b6df2bd 5d6a58959691c9c8a62c09bd015b84d4fd7a4c8ab3826100512dda43cbbb4ff9 7eb29652b5ed2b1080aca08c6eb2bf7a41e30df6c229fd56b5d5096905b2acba 732bddd78f3cfcfd342547d80cd2010ae931a76500c9fc073cc193a843839d02 e48eb1544264647008711c351e81c32f81e41f5a7c85a7141056c253e8e856e3 7288c441a034b859a5056dfe0fa9911c24775637c5f707c2eb28c90961747514 f06413f8ed307215eea8842d1f6aca79eb1846c21e1c4c243b665ba195a1781b 01b8892d2d57f8bf7cca182387a6d0872a92fb1a72df366d873307ea3927fa12 d729b986311d8c0da3ca87ac439c5fccad8128a5be990850a3f5820846147146 4b4d2bc6efb964a9581dd77f183a55567cfebf196353ded9b03bab6649ecd10f 4f12843900a11cb9802ad2474cfd41edd21405a2bdd740e9739c66a5bbe50223 d93acfa9256172f808b269ae6faf49ef9cc3c86026efba2002d2eb6a1da504bc b869d5d99868014123044ba4808bb55ff416a7590cc23bec4d46f7a517ba5b41 35b15cfed88fbc7ad9034d47394fae12fa8287dc371b78a7970c51c0697d483e 24c214c701ad51ac08e0a57a8da5c0459c2499910cf99a6a851cd435b47627ac a92fb963867312988139b9e3c29d2076d1a7f051513b6f2c3c24d74f377f2e37 7e4054200f5643f3437795f83bdcf506d97221196ea711b1d69afa5e4b345978 a9c74fd8309fffb5f589dd48f55e29653b8d0638473139eea572bcf4356bfa13 c8ffc26337fe41548090425edcfbb21989da5914326fa7ea776b9214252ec87e 3e25940afec823a365f77fb1f13aed2180167df85773ac52edc44f2346ff7c59 ddd96cfccaf4b991b9fd6f819ca1ecf8e0996e42862b9d0ee71f0a446620822a 50090e56c723895660648958c033bde07fddbbe89149b1a8c8656a6a35a4bbd5 74912093994ca326fb15659020de86bbb291c425b8937324a0070ca4c60a9165 68fd8c81aabe602c09f4b37046db3fd2c15267d974c3697c5f6f35f25efc8183 44f490d63003625443747a7243287d28b3f5fe4ec2a273131d8bc7e09c461d05 6359841de569f54123834720941eaaaaa59b8f9c2671fc7c83ded6f831e9fcc6 ea75a676b28568f3330e7d9421ea92f3ad3cb9ea67ca6a1662c45eadfb67182e a6c073f05c29b0ce08c57cf36d71490d5d40187d51d382e09fbbb2a54114a376 f4b02dbfd2253dbe13ada78a09b53ee799fd347705c07a9538cca79fe719845f 4535ea3b1f6aa90a6b98e0b940c164b31f4787b6a847758e88ffc7fee05d5cd6 8772138c613d9fc8f3f62a0373d7040616a372212328d0acdc333d62fabf9a40 a8f52f5a71eb7d703f5478f4f382b2cf3b0efd6538b24ecd632178dd432dae5a 97215a35ff24137d83c793bd9057ebe8ee7e310964ebc5d41eab6d15342479a7 e7ee87976fac8c0a016d83d26fcbaea4c18103ce971a1d695333632432cfa1ca 00d81de15264f2c13b567041ed3a8cd5331cec6305e4ce7772138be437d62437 d452fc10e867349bcd7f3a783012d1b4e26befbc821b29f8fda8cf5e5b8c9b03 96eee1a1c980b51b51a3e47cd893a4930acc2a61ecda8beda97ba416fdbd6f15 a828686eaf20007dd299f869ef52fae1785b0f25810a8b8ef5b47d2294b77349 bcd0fce555fc44ba05a71cde39e36de093d44149f803781cec70a1c040d4e114 e5d10dd7877e7feb14ff7ff3074abbce9b62b7845de46880a1a3e400269e23fb cf4cdb04cb0158c1397ebf413b9940424f4eefc3aeb315b134bf1c012de95886 e99a22d2d2d0bec068aec00b14213757b498140f6a637b3d14244f4a83391640 e1d337d2a691a786e85f0f2b6be24fbbc6e3b0916986de5551f2e04eab58bbcd 1dc8e63527bf9ab5699fe1b2e6d6aac976da2fe8c0f3ba390239067f19168d03 f32db1edd95b03ec7a6cc707383072b2f1d9bd7553ca3213a76246ed050e5ab6 9b6a260abe2e997d96e528bee60949c7c79d22e88e3f97ff9740a40c030e05d2 ba6364da3293a9a53a9068e15387444500d6d5a91c45760b30b900cc5a44cdb9 5739ad6c057d2c16e74312a7123fd6dbc46a1f34067f1a6d3f8751f801fad433 e6bab56a1a5f21bfb687e77f65d0ec0cc0063576708f02b204a6fe716f8db773 97407b2907cb431220e5553ee94eee4b92fcbd7d3d26f11e48399f5c9ee85be3 861f72244c53d41969ca914dd57e15b82158270e5f133876664d689d541ed165 0de2abf3741bc18b0f5937818f3a1e33523630ad8e1cda086b955c2b13c4817d cfc22f7d43b1effacd87521b5463772e8453a2798f2d48c2918d264f15c563b2 26c67ae82b3c310202c1908af303e69ca94c0ba83a8176d54d509781da183e86 75b7727257ace3fdaf28bd08606f4e8d471a805858a4de3af7ff1b295a8877b5 8cb68849c5752cc92d8c718605eb5390ba07013bab0659429981cb8249148107 10e3bc9b4705f0a5545deffd9d83f16268f4963d4d12225086432dce3f317d82 994e0e93851f6565314a797059f6a68746533f6d8afd7164538c279e3983bce0 0026807556960aa20611418df4ad89b46722bb4273305ab5beaee0518bf90b4a 5df227da750dc157f18d54abfe7ea27bb64eee746b41315c843b42ebb60a93af 6c3d71b333bd43ecfd9bed9e9c6a3438ee386283c9de2bf1d9cc6fe63eb3c7a9 5862c6323eeaa240cbc96df54754b7f9b38a982646dd58b29b7674fe280afa47 e59d1cbd91f6a1da83f811068d2c32d3a4dce1cc87e12dc670b3c8f4cd837668 e01b48407c6cc98e591279d215168d45f8a9b0bdce74294c863520a96c1f3578 d123f51fc47d4a4a23d2589b3ea6afacac1523ae68c9c3e987efde55960da989 b24cbf3af9e8598a861c21eb667e305b275091ff54d619de94427c4e8107b4b1 ce5ece118e978a798e3bec113fcb05480ca08761b12f4d82de6aa20c351dfd4a 881e216b3ed4aef9321ea28d6f31d565356bfc4b1df72b877881273b55de257c f086d77f57298e4001ff99f30c1ce019cd81a74fb77dc8d3b5e286a972b52b78 22359fd9b4985eb46e587cf19bb5094ff334ea115c12eedc31b14a0c1a883306 f4ca0ca1c5e64f8143edccbe63ce32279457871600f83f726706a37274f0dd82 2aab22b4fd2e8d361accdb45a9035695c109d53d69ca0bc4b6cbec1c5a5f73e0 d4b27fc292dfe7d49fa7cf49901503f5c6fa53812247a99d09fe2b540c1393f4 136c4bcec68a9a9d675f590e35ed6a9e51a85be74c269c66345265620f5f196e 2903ea5a2ff85ea2f92a411467005858b3a315b6b6b9c7a7b406fcf2906a69cb e86aae3208c02700d40ea0c63ec911ad32aa67fb4d5c7b055ea2161a877cced9 c2523fc728db90b1111de2a58e424cabb7dc53810094cbfed40e0f1899b31cf0 239b2dd969fdffe6ae68c2a227fd066b0d08fa305c8daa807db24925d2a7ba90 2d64e595338b931b929125659133566e25fc56874ef3eae77760e289a4e12227 64ccdc4872ce46da3421e9404faf86eb8abd0046239c71b8fcd9f2b7e1fa88bd 85c0e8646ac761449a58f53106cf8406af576c36b70c76410a038d36762d4abc ea4dbf46f8653be48bd964ccc8171a87d06c3ee1874eea739df7de9c8f06553f 31ef879b28137a2252476dc796f988465ef7640224a5d9b8b75bb890e8a8544a ab061319d1d6fb3a9cc5c2cde8201a208b82e5eea782b88ee93aad24785c2d6a 43d69deec91854813f55eff328db76ca3f1973eb498eb0f1951a15c806b8d9e9 f9f07b9a4f5969d9ced711bc842836faf8049d5feb3f4cbcf094089d0213a079 efb00a7103ff7a73f36a3b7daad7efbd296feae5fbcfc8aea798a5921fd4982c 9ea8ac862144b79a9715c672a206686f4ba6790c08159746b2707a8f20845b44 22f9d130216831692a2fb6be1a5956179794cf0d3ab5240aba781add81c8eb7a 2e43959ec556d13a5e30403182b5d786a6c90b1271333bfd306b15525c3838cd 7dd14d4250642888be66c827013a345b63ef223c79e5410d86fedbe880f49511 075559e15ccdaa2be0d98e6a4e9248189168612117f1161da8523fcdbf62e725 c071bc9b096c8e911e9f6a4b05726b4bd8cd54ee358d611c0bf0caf467764aac 82126c2b00980c6bbca68944706644332100586eae865a99391c3a99fe206209 955eea558bb0799db9ee5994a88e8d323e27a63075def3cd6d16a2ed82359240 23a79218069842b0364c4ca540ca80374492d6d3bdb578515eb5c62935e5cdec 8c107f0b24dcb8d4d5bd749b90782be058a6665c71e09052e6521a5a23148ecf e55ae85f46ff1d248b12fb35c35df934f4da4865c9969504dfe03eac92112b11 9d4c8f0d6c14ccc7a9c9438cd11224344b31f50c9231fab574d3923bd2ab6276 6475135f9644ca979ce611a47ce21f55f16da6edc548a88b7f1f2d5cb828abab 3f78644f896131d3080551c3b5dc669884f66a90069fc02d0980c8cba93dab5d 032cb098c9caae12e929023e907322382e07864e0e03891710413bc9baa6f628 03f519eb0e73ebfdfc8791829aacaa3c10fbf4a69505e658abf61ee87b6d101f 8dccaddabddeba280e5df36ba751d672ed25f786c12a8c725f972b6f11e3c638 e567bc025376197432a4459ab346d82730c42b604ed31e27b6f16fdc61351b01 16 818312cd8553bac764c2aca009f760d68a45c0e9b6e8ad23cbda753edadf030ed290d87a9d7d91c864655460fe6b91f412f51263baad7a3fb9cc564556cfd403d41395a65b03e13a2a5a0d318f728c494548e83f0d603ef6eba9f0b30965f8021d9c1559dd557050df1aa1a2b7e2c2bacf2aafd842645871d058e273d4802e01e5af08974742816515cf26a531e17834ec7e774c7dd42b1b7e327c0f1617f70a9e5aad969b628dd5549c859d76f097e0b047a025beefd8f12ca947df02c6070ea06f65a7253a736ce1056ccb5e3ff713e451a85f97ce54185b54019708b8930ac2ac265b7559df6d5b13354b015c967c0227816e7a881cfe073f2d30d72b29014953ce2e87f4f7deebdb9d415a050e8f891e8059bd9c923d861b4c479386b4012c0f25b5c00c54989d377d4560ee99d973e57f90009766834f0765768e0f480f414f29416dd53d1da19c27cb4b18fa8e6c0af16d34e787a463e7fee3118ca90377b1a51daae573b5073d0ef6af05640f78317823f11be982016e042905da000d598ed64481bd862d689b25815e27a97674cb25f4cabe85803be0239bc1b2b909828417a1cbb97af967a3245657318f5c73cdc436738734bb9c0954a63879330f26551a552f0d9bd2fc9ef55778df4f610fa08297103a2163dfbe15486e908a05de50591c9e5c000e3c89e45d37dca7fb7d81e42d213b0f8ca6f1b57329f7390cb92efe1bda555d83f484ec9ba003df8e94e16a58b88de6b475362d28e6c2e305e9cbd35fbde879713d5f3185ba07f460f0ba71977fae4e1e8a78033da53bda062288073960b02eda2a5188b919342da3947017523a52ab1a4da5b421ea726e036c75a5801a209bfc18bc4e010076e51f68f557c14e4f41cfaa25a163bb4916034a0904e7d2f01f9203fe6e89c0a90158fc1cc11d9eec28f931916cd4e906ac09caf1c454d72a44f52e141d3a7448da808061b59c2b7f7fae9cb96f762377ad085994f63970df0559ad8f8a84ef17ff34d3646a87641935fe667459b8744a3f081238a79b3e399de8560f4f7f86e29ccb2285b5e66119ce55df50b8d86c926a01f8d2fcaee416919827b2fce6b6193d912d282b35d4176224da640ec70f61c406af8dc514634f852cd5364cc625b9b3bc6158a1ab8281030b5c8555f85cef4f0bf32ad788cc4e7bb0d506a8e612e439854ff7e8725779d89041806cd73596ed0a29fcc805e9a15aad6b8595f2c74b48191fd71996460563df11a8b16a6d047b096d6eb0dcf97c901246b2405d6e081aef854ee7513886e9ad523c8e9876f03c0da84e4da9a01b4a577168ea293f6cdc8da22637d5dcb9f33e3bbfce6fb42cab0b6fc84ada32d366fbde8a5c3a2275ad295a5dee6b9e69195f04f197c817c1470a4544d9183502d01ba36abd8e90208131c4957cb08bb7994aa66a61caa798940ed89579f116f86a4a8b3cc948265ae688cb6fa73774bea2d46a23b944c508770961b84c17e5b4e691558c902408a7dfba96df06987836467f71d33e219beea20aeb3404002393c1530f63991f175afc675a20cd4402ec1a1bada9e6e4853d5f068300314303312a498fc5a2eed1f5257b5d9425da7a40ba93283fed77766b20069453839299fec4eda12df847453a20f26d2c614e39c8c0d2f36dc08af23c470d0805c49a1a04a71fd0eecb1c1a39f95003892f1101daa46738247362efb94a06bfa2a610e52f7e798b3a98d0953f76cd8b27a58e492b1e78aa64abf30a316307292a39812a6a3206e8bbf7787d4a5cd42e60e9d0bf59e949c058f954efcb57040ccdabf5ff5244083e4074156bfed2640b75fc694b3e1d17f52ee37512d0e20802cdaad46c3d7fa436d6d8b6323566b6cba14a504983eaa6d0fcf0dddb53ab00ea2f5c7269b6a8b5796b55566c51de992166ecfbda52e138e7acf1dda84dff050cf4c6e9ec192d9d774d7d6f51491b676e8afa592e9a9dc52586d0bd7686f302695188350e3620e559ecae000c698292d7e81ec8bad50239c0c0c8863a85c00d286115c7de5139ccb1c889e2a5e6235bea499c04e5ec369e0e761e2683d5b8060036916866dea40d1860c5f6ce5980e8dab61aaa406b1fac146300847a089806d91be7394c54312dcb6bf0e218d88bab339298d8c69f6ee1ac1b24614784bd01c387dbfdfbb056e35a5fbd387a6a7f9ab5b2c24aab2732e4f25dec1d3f31fd03aa358c1a72587296086743bcef616d05fbb25292c2d3cd3a9da32b2db6a1700986338e5c280c660724497eb0f0751b42ff55f2a2fce5e6a93ef68d348e20f40386aaa3dde23a7936a74a47b632f8ac89bb7f32a3279d00022932f9c0ff32d707cc91c527a5c507cae8b4c904eac893879e9a09854f1688c7143fe688a35ef809bfc914f8a2dd6a54da70eaba6ee9440639458ab12234aab3e44ef953419b6c0460474681609cca8a841ba607c2ea91fd20976e421a2d340085680bbaac63350e02f48fd6a8c6799218416f698f13ba3e14ca3115aa96abb4b7e89f000115180d9584c1480b2616b9e7ec0d37c2eaf7eb0a2ba7e838de84281aa980bf67e60804f4955648d75dd6511642fd1ff772230bdb65f8e755f719789d1701c30360bf02f1a23132dc981fb0b897f2c93ed635311de94829f024a7e8060cc74714ca010ff8ae0413cf0f4a378bfd07323e318ad186f7bc25a833c5ed36d3209032d9170c1cc3a5f7953fc3e8edefbfa835258e322967ccb0f8241034fa6a36b93d3e5e0a7270c01c6dd4a3d576d464dc368c41e96ef686d31d846365e18b0822ee7afa084d4c276bf832f306d69fdefdc419dc4239b94019e8d108543f4668078050820ba13f469fdf41b088325fe90e1eedc2c03158fd8cc324feceb27aa745b7bca70eabb4cffe24e0b5fa2814e7f0b625eb0f5d4a005d7cd7528b9cb007af74917e0f20d1d9d2a1003962d3a6c9e33d1e36d15e81a26b8b540b35c944774a5109f7037e28452ade490c56e08c14e7d9f481ffb49f7146dff13b2e48533e54deb6040a8e710cce12434b5d11e4f7abdc7963528f42741b6b7a52bc1eaf81d0d6b833084eb864c30a01c60fbe36c4bb90410e0915c121386c735a7b59d5eabb350e0403b7489d204d5bfe8be85e75436566607c8ecd0bc8bc53cc41890d7b20b1db950982aee4933865a304aec8c81a83e49bf6a1330d0b0beffb133add63fcd9ecc400a2719348f1000cb9b296ed33ea306dc9fe1b5e639c689b9959640b70b47d9906198c4e3eba23cfb7f2fa45c9e5cd4d7b163aac12b139658795274d33a7f8e90281f78b40762225e68cc880f4434890df6c44020027325a8266ec379c984d8f09cf5319b419fc2bdbc518abe7740236853e691540a85717f061a218821f235e09936e427dcb532fcc9a9708c569587b7701652622ede9bb16efc9820e82cc2804d976a866ac237d4955f39e27c3f0cff435173e9fbd778237b64460e8d502dd013c97b8b863b888b40ec9dc946d2e58ffc8e554b0f4459c772962387d416c6405ecd6a9b167198aa5bf4e92075f00a93fc33905846d50182a5500253c9d93750d8d72ea9d2eef4b12a570ce1ae838dc902693efedb9c8deba1a24e4ff43f79906983b3b062563a6b359eb8ccca0eba1767eee2fb3a66642d8fd058d47c9b42301ce5d9ca6db48b8929e222b81b2faab5a428cc8b8f477e1e464ad62998173240a03795ae90f21c6dfd19bbc9676ff9bf07d97dedd957fb71b96c4eef3c2b3a50bed78ad13967b339f1565f682da373ed3496d82be68be00e59c81e495cb089501621e2e2675a2a2d2f335808ea4cdc236823cd6a3868de639d5869d18a34fb307d9ac341f8360e85fc130bc6b8755fe475c9ac6c88854d7f2a33298c2d9892b043d1d66008622f34065e4d837c2444356fadd2e26ece01a6259f9b6e0c4e1010033ffb401f4056724a07769565531a0a6cbd73f66bff3ebad1cd6431d33ddc90cd904696c52c71178dde83a27601fd186916d781a44f552c957ef234b11378005e293310e3194c0ae6e0dfb764085f9a2372da945e01b72bce7033238e15cf20f62c4c9134b9187de1c5a263f542f238328842b4e126972e6a3886385582d980306e07ea52c75618229ca83279865fe5e5a1d611f2efd285deb8480033ba8170fed74e9ead22c04d1412bfd610bd320369105bca72a00b4bedbadd16a50d58c07c84018e0da506ec11ef232079fd2f9ac8d62bf4d875f2dc8bfe62dad52c7a70bc7d6b6c7eb6160a6d069c3c596a33d5647a14b5d1461b41c550d74a9744da90b783030dd3c2d2f904261caa44b26064b223e071c99ea2b5439edc42521f1ed0d52c7beb8027334a223af2d5e2055c1c6b9eb43c75389ec246b82835677d6970e121646e85f7401e30bc65266ea2164eb6d8534ab3827a9360a8848344ea27c048aec4cdd1a1719ff4cf8075ba4f4c3cc522385e319157f444a00ea63d16a470ab0fa44153fa0d211371acfd5c28d85470ede784da43ab32e79e335176ecff101968e9e0d77e45f9efd4d54368c33773adf9f189a0d4b80a2dbff98b1f67b5404aaeb17d533d9e0957adaec0e8a3c8a34b168d437485c9478e7c4b19c51e6810c86497f36d454de4d95413c6d02efe3fe638c65b4c5204660f7366f2358ef7202b49feacf535dffd2090db55a1f3351825d94b528db11b4f681bfa7b3df42f80d4502f7e864021ba6707cadbee6746e0b2641ee0692098ab45e509b37d795550e0c3effffc44daa7227b6b708a3d628498cbfcfe13eb0549e098de5ac9ed9d10f01a3877be157ba860ed9001ad4a68b2cc035d2c2234810225c57ab774bb1f006fd438ba034cf055e278b86cf584109f85854ffba9de710b68c3f835de3ca1a0b9aed77ae64f4d752fcaf489036d5cedff31036a40f3b4f16304c6a31b4b0d80c77b7b5ba7f8656556a21a856538cfe251baf3a5dd820dfb2c1fb453f0caaec020e0dc0fadd1683b92eccaff087dc9fc6977b6f639ed2eed120e6ecdcf2880c050296865c9cd088ee84bd52917fdba4bddee173346df948cdc89c64a4b861d5084559666c6ff130422d0caf43db44c3427a4d5657d4226ec51158b690ec6cd50b6ff6a016dc880a687a10f20fcb2057317faac6506dee3f906b5fe32b8b416c0fde397c8750a61388c798b2641d09f0bfacf8a071bd0e32db64458464967fe101a3cb5bae64b48558e1bcb6d01517915de66c140f287ad79c39a957b20ba6f30fc36303c1cffe0f37606eea932732a7cce7d0c8a3e7f56cd254c429f8634f730d2652c9a6d4088ce17470bf650649691b980533e671301cf7a5ddf4dacd2c3c0bb972445be569528ac53f5bb3e0b426af8ae8fbc2c1b0ff626d7670085fc75a098a5c7d5cfead3ab28a57ef6ecd3aea2b66c79b6bee959288a9f6e3967a06010070e3433961e33ad162ea260dc5235cb51233e52ffb770dc922b361b47d80e6073c1c5cccb26c0a4e5d7af61ba1d6ea820cf3859595c3d994f16fecf1826eb90b23a191ab80ed8f6225282ca112f6518649916fcfef9254d542e30ef2d76b1800361d6fb6a9c559096314875aec90b1baec12d4bd8e8a5eafe2902ae146396404bb7523b81d55d59ea7cd196065b89ca9256f34c0878048bc7baa53763fb07b066f59bd9397a9ddafb4986fb5bdb5c6a68b52ee9d88412f506917a69d7a098303de15e1349801e828be2f3d46cfc51f4acc60d3a56ebf7690f6ec6e62b97be202922c4ee870906dec7a7d81a7575f62309d191979eaee2dd8868e338a17b18908ac26cef563b488c775efb792bcfe501898e4b1081f5d8168f43851ad7571800cbcb790963072d28accef73fd99da11a180a5e73c3b136e088dd3109a87e9170cfa181cb228d01588467846faf834fa8caecb758f39910d8e96c21432f2d61901560d6084e30073bb63008b25323457828048a351e39d23577ba982c29dc2c9084dd3de94dd6fd57d3713969b059398bf7ed6f3088688424e03bf0f5ce056110dbf84cb9eef5996e1a2a934a158c23a8bf009fa8ec1e6463ffd67db8628bd8b0e997f21300fc9aed4008581b1d67e2a5afe9742dc2e4179cdcb6df9701af3fb06a28007220cd9f8b83c811b86f8ee31b9f8ec9decfee25ec0cec47465978c8f0035a90e7e02cf8ad1ddcc3e41af3f90a78b0f5867a3ffb620aa412217dc56360642d86ccc9a0d98b0d9495f150086f5fc5f20dc09b734aa640faaf2f6b6247a0ef18ab7835892733f1716de94ebfda388a9390eef1f9338238a131aef374ae00df939552ef1075de5a600f917b0f13e69186a075629cee5a3ebd4da6081166908330a6288f1a53ce312275226f5aa382168f5ec6444ac8444048ce859df7e820b4f50d9ce8ef300012b227075e6e2a2cb818a3eec6c6d6a5dae8038ca3f9bf504267d9ce0c37e69498039124891027c11278fe22774524a0932ed77f99d8bac0afa523094a8d0ecbf6b3930ad084f7cbf5dfacaefab0e56c27a1becff63260208bc624befb99b5b5f3e63960941f370d70afd8d513552640978100cfc150b0601e584d598e632cd2fdc39ab91a0d5cd9cba480d6682eff3389a2d3e168eb76b0bcb2eadd8b48e43df853309d2ad2783c76e50e6e1710d48ff8446a7640073b700a4eeda435047a1fb5917137db4c21d7d2a46d4e6249dff862f0f8cce3a5e1d099cc25a374eb88386ed47152f1a6d67fd1be74d91a23693eb7aa6d5bbfcd8fb0ea587266278f698a6500c4c23fe8668d0827c05de246d2f0f59bb005a3933680d214d1bb3cb624b226c21e803ef2f69cd15bdea593ecd27a2ce550b1046eeb50d7f4e7fd5948dd78a109eac27a0ad76be9d25aa229dbc0469eeb57a170c9c6e0700d7414dac696c2e043877cafe52d3f3e396c64134b1dda19c85e8541a73540b45b068cb3644afebce94a24ab65888834476fae8d6830eb7416e09787a9b1000fa17e7d99b2a3ba5322bf100cba7285accb4076dbac9585908b044a925101c0dffe83e945403fdee3a8aa97f5acf6939e6f9893244e4459df32dab4ce705150e0fe137b81018cf05add94ff99d166b3dff07e9481bc7d36045985f5ea6a71d01e5dc8a06d65801c31352095ef736c453456700b51e5334f7697398ba1f9d450ae40892eefff78d676cc3b291aeba484d7c6f101747326401931a1fffd9adc00aa637ae784ad424e1b1a912f3a7267706d68370761f3fec379a2c4f1f2bcd3704db2d17db46ffb2bdfb82c0e6d6e742cb368b39a4e76f663d801004c31ecdec0da4eb79ceb526fe005ff8cbcb33a7cf3c8fbb6792fb5fa7d3878357221997d8034e126bfcd4884f8a08557637bfd2546b7cc55a3f76cc4b1f7ddeec632fef7c0a16c92bb4c42dd3cc93dd211f1e44e42ee309ab5f1be2b33872a3e46df8671a091cc70bd9db3be7248a4acc2c39193d448cc29d63be907a10041a25dc4a7dc508fcd71212c351039fb80200b7d031b0e098a3dfcb19627ef047ca56cc5ba21303a0279587c03a87cd1de30e44fb3d3baad883e7d5381d469669b19d38800f7e033e1e78dca43f7ef5b2e58a13cd9df531b79d6d8855eb711bdc52258a9ef81009c07df9bb9fc3a582bdf0609cf76853581dfbc7c068ac8e47b9be82e7cfaa050f44438fb534c08e84bc3052e130e10f555b85ad1e191f180042920f9e4a55cf0e2a6403c5eb62502835dd66bdbefea6892bc4758d6c505dfe60756d276493ce0c1a4304b4bf614ddb558a841ab7c08db5b4a6d721b0009b03adcec52e79b90f03c14caa0ce55af33efa8cbd9cea826a4df383b0d5fdcf0702a640e99b41a077015b84d7415d461e0c3f1be93b544f5195f775968221df61b9d686f14c35552b0e8a1beaf68827c429e578b5b73a3c5e2c5f0882cd3502e93b51d960eabcf73107f030c1e9dda3ab056dce89f86a5b246edc40392bbc1384368fb7da7a988ae50d3a63dc437e03e96727f43337775dccba1e38c70af957b94290fa2985231f4d065582a14a05614ad207b9defcabc16628cc04afd522a1d7d3d8321ae9c6311a0541bc7af99c98196fddfe1eaee74c3b1c9cb033b77fbfbda3342059439fe9e9029801c15743af9341d3e49e55c4bbbdd818b1b126763bb899cd1c78c62d7e220ddd1231220465d7ab33e66168338a9939ce17d415877437531b3424b201c0d4065828734aab0415dbcc08f019b55f5b82215bfb81077e38831dadf4441ae0c504394b7e89836939ff2d594880612f3dbb076be9970bfc037c88eeacfd25a8c003873383523633818340e4783327cb55ee182e056535816c4fb1dff9a42997e308d09bd6d61cf01e6523c92b59400c0adbe6d45b0f9fb7a13871e5779981e94b046d7e80484f5f1611479fc592033512ddf3d0469d104ac149b0968c3ca4849f04b2ee55599d73b29b911175b69e2acf20bdffaccd1ac62336a67755f107a4cf00b117924e05e5ad2ca073e9fa719003f6c0c325575d4b6b99c469284d1dbe54091d16dd8e654f02be709b8e6f58e9f90a58c5c53710ec4dc8dfc136abf38ef3033c1cbe049f2361fbc192a8a3467159b9a3403bc608592ffc1fbac9eea8148d043cce444bfa2716cdab74bbdf2e43918e20731ed1b6f2eea45676227552a69e0d5e32ae384626bea99ed6c775943850d9883730248c7fe65cc6e862e7093db9082168a910480bb246aa6c08e47ef9376eb9b6ea0b471a8da411eda068c79af5097d8928fd852770e8afc67936024f1eb489e610da2a816f5c4230082290104b0f023a2c810981c26d9576146d44ab5cffdc414a92e309a31a4cb78e2da6720a0e4eb3318e6f58b44716885449175d8fdb343ea1d96bc884505a4636d49214a00f3dab8c48730000c4037d77e15ecdf1153fddb97e3e14938b17858aff4b892b056d28fb3284f370b94fe9af7a319d333eb13196aa4055d529a855d9976048130c24203da087b5e91aa9df7290fc93892512cab93309e76020391ed56c0641df015c008a9dc2b95fc44f8cf0ac69be7c4603948f1cc4b5072d19cfaa966f8cf507bf9ba5604d6247d7b897d0f32a8fb035b7dfb5cf276e42fe0b7442d1f092a1049a541fdb1761bb9f54662e80b786c027da502c39146bfc5287b5abae1721b204b2b9af8a8c178937d6155fc88faf7f1404e91cb7e4d295d8a8287c21383366058f0306529dde88ab8a8c9b6ca3647e0a4539c9f479287bbea67c29fc352f5b0097fd382d4e0d68d10d85eab181c7a1f43dfb5709f5bae0adf30b245fcf25f407086822d2ec8cea0e72ed4fc1dd1615e5dd2b79b28d0e9a97914f24ddf85d930c3fd0335954ed4c83bcc9662297b6c32dbec08fc303ca208eea1790252c838d0bd3fe76c6a91165051a39d775c01d65c2d926bf6f250f6abaceff71ed884c0304fe05a755cc465b3bfb04afd6eecf0966db8cacd64953969ba2f688f5858a32045692d0b393cc68de8f9e014c9f8cfc82d18cdce0e2184345a4b3eb9f0992d80966b9551d5a8aa0866beab77120d7b18c3b8eb943f9a5bf79a5441983bcc3250d9a095ce348b263c67488719795c6a0261f0296d85b5bf9374040274da4ae320a5d50ad8f96ca0a5f644f837d55fc1f11ed451576ed2feb697814c5e72f48340d813e48912c75000b35cd14a0459e90b63ba21e2a7fc4f3cf69078931ecad0a0ae5fb72e7f4c4453ef29ae2133ac7c0aad70afad5707b28e657a76bd8daacad08f58c4382a374887182e8e167ba2c09a945ed23c3938a4e806026d53b01186508ebb44636a3a81c6d88974154bcb981544893275401958de595867d81b13f730ddc9446e1f18ece26e0ea789caa8e9725a4aaa277dee3be0d6d3cbd65bbef7c06c973dfaca004dadb831abca4aa30e7d318fd1dc05690242f880aa9949bceeb0d5bf0620ccefb0e50f709f99874abd55deb8432ecfbb91426d4eebb959ced4609ad0681d81c230df81f5719e8086acb122f1cacc1a2c679a2d27aac5acb26a909c986a83890a49b1bbbeb67f1aeb77208b00a201d70ecd60f3d5e51f87173410ab3442e056fb2845748e308daf5dd7b60aa6ea973784c08913d5e970f32e3160f65947585d6abc7034ebe1baa675487c236e718a0e4f92f586a7d415b46c52e05f46be003058d4fa734836c07b660024ddb827e0d81b7f836bb3deec6015eaa004d4808475ee9b1835c172801a0190cfe4b8ef3279e4ac3dca3a95f5b13884006c52169cc390a884c45c7fe6666ed9a068c90856c94ffc8cbac0530ef5e672e062682d76d6d6335a0abecfba8a82ba69b7b5a26a2816b8673967d8d56fa5b110c5a0d59a5981273aec80b7d020e55a448a7b7cee91dee0e78933cc6ee1f41090f8caa25915905e576aeaa3e8f2ea92cd989ec2e18787996f772e119703d80b40943abf8d7135ccde0324292c3b576014ed5289c6e3c7b5a77e8711c4433a3aa06e9f5719f2a7e09631a05a4ff817db2d0546c4ae6217810dda47e5f6309928405ea9ee251f0a32f4510db2329585ff35810babf02f3c7dfb6fc22d68f5540b00817c00512526b92515e0600797a57c38998142709815c46e36315a77e6e689d0e3b8ac6d2bbb06f1a0a4a99c45dd4033b7ed8d04fa3b1e84f66ba63c73ba6a40e549b5384204930e89dcb0bd0f41a3ae7f937076fca2050a65b4e4b4319625c0cadb73a9f83389bebd8ebd89134c027cc197a55c29224de9e07a284f539fa1705db8da16b637f7264f3acfc0bf4005bdbf492b970d619ec7155492f8f904d2f05c0848a739fec2ca92543777cdc1dddd7d5b7d870f0ee8c2e1cdcfec75abdcd01664ee8f4b84679550e782481c4a897e9ef91ce092cc93318fa74e2c0a3104806 +generate_ring_signature 9dd907994196494b8125e30882384a88166cc89ab3d0c91f6fde9e8ecefb5e01 90dbda83e0a10b0070bde90aec62e4dc65910a922c21ce75f37c425cba693876 1 b8780402a1279c031fd5dd4cb89f48bfd15b5a8dfc140d9fe0eae67a51d18df5 5631b1eed32e0c03cc92739379fefaffc2389bc6059c623a2df77b13f41e3209 0 e3e17420d786d08aafb0db583d180794310b6ab26952ccd3442358fb72ad9f097a93adc1ae1e707e100277e5d049f0907a1210cc37a692c0da852c7b684b3309 +generate_ring_signature dc57ae672f6691bb5adeef2f47e8eb0cf0c5af0d06d479a1b1294e5b3d41c06d 3025fa55a862d8127dd4c6c3d757a2e555e7d50356c3028f6a0027aa786c751b 2 e6ca090d9d3cad0b144faed63a19b9f263b793259f0a84d3465d9567893893f1 ae20f4f7f9038da72a39b4618b1a9032d77bf1787ae00632e19b50051db8de04 0c51d9cdc712d60c0fa724e226ba82e337451ff47b69da5acf95fd41db1d8c09 0 3d16afdf2addec448787b2371e0c46471b063ecf805ad36f78b8ccf0dc2d3a086f3cf9bbc1f2377d030fcd11504a40d992e266ec843e07fe6ab201670128c80243ece5700864c385d093520a063053219629bcfffee6c2d16f1aca8f5cc35c01edbf3e94a71b4a4315653b12d9afbeef726beabf0f29b6b76d13217438323500 +generate_ring_signature 2e3eccf3241ad759efd0759054ad22f235e33e882f8b3a0b8dcc2bad44cc13b0 46083a5c582071ccf8cd1c9f33bddd7b02328a1d68cef46e82f41baa171a2fd2 2 ea16dfeebbb925edc360cc2034ed4a7c45dbd9a2ac5c2699cea5d822a31b6187 64c01e0efec188fdb60bbf359b073f613efa84fb51d764cc716938a6e3cbbac7 1742543972b8075a2df32275289d6be641db11e78bd88f72215a5d2376203901 1 36c7583ed471c8612970da5033e94d7e40b8ea94504f48b0d552f4e9548efe08b76eaeab49c551f6e4d318e1888bc33cdbe394d0214c41b6f2a9373544f39e0f7b04e0b55e33ea864b12bb0a7897f04a2d224f19edb2c9a743305a418918680a3156c3b8028a0fb3aae2accec272932413e75b5c6e19a42629e3f44b76b61407 +generate_ring_signature 5324c8a8ad37bb76e86edb6127499fcbf161e173ec4cba0d426e25a26aa7c2e5 a3d91b89186b32fb14d79601878d9b5cf0289b0ac68d19b474c86f4288c8422f 1 ab633b5c1c80ad6bf183d24f4f37eaba1c4776e8383047290503e920ccf3ef59 a7afdd093c3a4e7f64896c7fee5b6db5f6a57a6f14c04f48df5c4a4a1219cb06 0 765463248c1f9976535b6160effc3d8ac895d41cf27d38e8da4718085b103d04be968d56d80159572c67703c87eb3282569f3c52a533523694a03e2239f71605 +generate_ring_signature 9ac64eb93443ad9fbc9abc23cd255253d3f61fbebe818fb9436f2f12397ab717 0d24970317c011442316da2d75ce0bb19dbe3c33ce6efe6495dd185ff0b77a26 3 29316431df8bca23ff0440601e07bac7ca40edc4fb0f948d1cb5a4e62153a1b1 3af36e933fd1d37481499f583155fedbec8bd3e131d278a28b209d5d6938ed7f 5ade6caeaaf734a3f6435719f88871b4094ece978b37ddf6b0554aba023ad626 94c2c27e71b84624c2f55761fd6b36a2f0f835a67fd12bf7b892dfae86c03d0f 2 c03cc94609c87875401da849432e482449ae53ebb44940966d4f65793069dc027fa23a5ebb603a1729a86e964e5febf65677d38e7de8ef3947c156608101dd0b867f4a9fd07e535a152f6db6ee3488bdf6245444c52d7682b146b3da79aef2049591a5d7eb40194f84d019e18dd626d367d7890f59b8aa7e2d4d8ff4fe284d004e6c3a86e85b41ceb4393e2eb45242ed83b0ba6a08b2650bd4f851b1d3cb5a0900b00ecada1ca3cd2c0b8882e7c8469823b9c2aa327c0fbb485fd657ff4c3508 +generate_ring_signature 733c77641afbcb27266517b8f24262be24609a4729609ac2bec71b4aa86f67cd c2474848d390a19e55e10d80834f6426d2421d3c0d9348e81312b9c644072e79 2 5eadf01c62bb7ce2e1cdf66e49982fccd9965769d41846e1526cb9eaf2d15395 ba67c3a77e88593f8ecf2c137cb0fac4ce7d2259010854396991fcbefde294f5 c3c9808a7c62b5542b5c054bc98c5f140820efb26aa6adc2f48f4d0550536b08 0 bc9a215c32f1cb318427a4fca6e12d2e24726293b5a98a608fd9609fdccbad06e8f130069932a32fa2fb4e0fcde31677a77e617c14a5bb983ca2af358f5dff061199dee6b9c040c9de8e48bbdc5d9b5fe703d401d75ab92702f83fd3b7c54c0c4a7f5388c1dcef3e9605c9cc0166fa830695a6304383e3e2a17b233186e5680a +generate_ring_signature 1792c680135fb955b884861240d9c55b6ceae3a130059fe1b3ed2e9ad5a9de97 ba66d0c9bddf46eca50a3205269fd2c427e40a9ca780d09d68b953511561b396 5 bbbc21787e57aaf4928a2de53ad1d1c7527586f1f8337b1f3f8d4d37fecb35ff b00f959de68120641ae043cc9640ea2a5fa040ee950858c1e9f16b427128a212 c5d5354bfad3251768658323c092fceb2918d8a700557c79ea5fa638c18fe516 e731fbb5657553ddf378c3d60e0b12b1a5e375ba1154c6b79ad6d276e68f75ea 1936e101b0e7612be29fadb4d8a17e3dc20c44a7f065fcccb1f372bd8e3b0c19 747205001f75da90ded4f907a518903090940432f8959d8ddf25413fa3381304 0 be388bdf42211c52634000c68ffc522040c889339f3f9fc713fff18849a6e50183e72bdeac7dc201ce9240d31e046cbd3f211253723e2f8ac2d5436f8b700d05ab9371f126709020928a85fe94102f8508b1641922f5a5b8657bd2322fb7eb04b98cff5d4e0129f5b3e66213a42f42f50e45e414018ed5e2005722e7222f180fa2d4e4eceb08b7516322209a8fa61a533e7c327a542ca7ddd0eb0cc51b65dd07b468e2ec7be89f8d61d3216b4d533b69ab8bc7308d224708f7ec713ed89de90a5cbc132c5dfa27d4d253ef77afe3d2935284840d019ff78b6172eea4a8ca5909435e89e3fc34b6a35f9f017e2c91d0405fbe77b99df628be43cebf79361d9909469ddcf9e6729df07da7024731f0d6d1fc35ba29b0fc6e265cf8119801d6c90d64f938eec1fffb3b11a7146f979944170da4223149936e20b2e89eacfeb71303 +generate_ring_signature d432ceeb293d5ea12e583f0e6a858000ee675b292ace24d2ad53d641988ff416 03776cc64fcfaf6b9a23a85f9577bbdd429b653f732b43a0e8f43d67efa63c87 4 200d574e962228bc5b74f2785ca3aed1662541f7300c818a4907279fb763f5f9 4c89d8fa1e9ce61a0f8ccb300b598c795baa7dc1aed979f22fd19958412c6fc8 287c5c314c26c80e50266bfeba4676980391addedde9789756dfaf37f5d5c120 0c294071658273a2f36d3bf44863fc65c8732d46e5f907c4b68ec8aa0ede4643 4ccc770ed00b9eed0888d3d6346bf28324e98418aff1d7903daf2ed68548090c 3 29dc937dcba837e605950f9e441cf7dacecd515f8a3df4920d57559910dffd059f47828358ebb5cf25a78ea3af7248fc6f615641a23252ddeffef9f3eb96ea031f94ec7883d39ea6edcb6d63afd8efed2d39e17ac655b81ff63209cc456b3603fa4727205e613fe4f2e09761bd0e2e7fec4acca582a17ef208f250556ea930081adf925e36f15f031ed1a91ff880b33077d7dfb69d0a0dde81ff3bf8b265df087d8d8d3f76a993480a00dfa24d3baf594df68a136cb341c4cfb5fe057b413f0940b57072f697aafe698ed6651806a452f58a4283fa3d264ee9cf049533bbbf099312c28fc2034fc6d4a162346fa3e00c33391667c46e50a6229928d68433210d +generate_ring_signature 5de707be4d1ceba4a9991eda597450d72c752ffbf85f714725b7a53086c2d611 ad59d7abf4332dbb03984884318194af02d41844ade4e9f91dccc65be8467abb 1 a2103cde970a000902d7bcd98045d5a688cea83d1a0cdd1bdbca4c087249f11f 6eee07f5e11d2601d7175b90135c8328c797d5f4952a125a96f934aa82aeb006 0 4181bd761eaded0ebbc3b8e7ce7fc2f6fcdf842afe478bcfdf96bdcbe464b303b9d5cf86fa9e6f75c99501fc3e327d94bc56d94dd1d16081859146c94696960e +generate_ring_signature 1516afdad27c8277ff77ebe9cc6b9889061ab85d504c7678b51d1b453e656f4a 77eeb7c8666282fa03e473416d2b495c62e9b8423bd30aa054cdac22f0173b75 4 294897bcd480edc6eb3f302bec49fa75aa38de4ea0a5b3ce458e284477a4df2f d7b3729fc615104085544ea277352df10f43e24e8b7f47469893f34df7197e95 9e4ab024bd6a82f3544a941c0dd88b2985601df873cfb6c82c669562d0ccb2c1 768ca9255bad7ce9db858bf51271f6321620424e7e24e5422395b10e3fe48a04 7328152575f971140847e181482617d1220f0cd933535cbd8a5492e8c857030d 2 1e21afbe9950b0bc98ee57693e5c62ecc2ad3e8154491a6f1c567ed277461201a1dc25da476c5e6952221848af24318e94b6297c80a82a657824539de6814b0efab54c65e1111b735cfee5a90b76e429dc84206547a3a73d42ae322f39bf3d0529a8aae101f990028b54615d20b957556feec904aac2aa2bd2cdcc7064be2a09ed5a2b89e4b5743dd24d6ed4c5624b3dda88db6922e625473fe751ea9087580c046a3c258371e23f7e5a53f5dbcfb9ffe6db119ab3b0377b1e21c78fcf737904c54aefb383d4036d11f800c03404a0f0196929bdcc009b3ed3b05297a99c970ebf9322f5371c9f6009688ba1600d1789fea18c18a197e1a850a4c9f3df7a7a04 +generate_ring_signature fba004537aa4d6db41185d8c4b12b3256048480c65d99eac669d156968585162 ede0caa2b77adaa6a0ff9cb917a79b644d2587b7d3821e5632955403ba4c9d68 8 62c27334f32b1587534ea7810f3356eec492bbacd7c6bfd2cc91b084dcc38643 c6dcdc2bfcfd81b385864f46dad8d1a1f608eb4d5615debaa651bad8eeab6588 806806bbbe254061c70ae2ab54ccb2c10886f792d9b3d0569fa9753e4c44562c 9e6598f7e3b174857ae16fea6948156db8093c3f991bff0da5a2d3c8f302c9cf b4df03f1889ac5a3b21134d6be2deb378a4c228af70a3317218fb34acb3ec86b 00262c572aceb403da34678e64368766aa091be815bd42bef5db81946488b366 c77fd924daaab02e4f95d653c8689ef3d3b5c71fc70825483bf36eab26280ffb 9af7b0c4033b7f3a8de1ce74f80b1ec03fcc3b92f2212ab8ee5a9c68869b5995 404d2882583e961947d1903d8bf28d3ee2fd467f01ccefac2d2ac3431ae30506 7 bd7910a0bc2f158cebdafb9ae577daadab7160d5a3a4bed0f63064308cb8e802f59be263479ec1b4a7cc3514dcda06954513737b7d6afd30544cfcc548ec7d08a32249e0411f75234533459be0534b730f82ef24cf49b54b8d0e67358d1c2c0a050a87e88e745b0aef72da495d72afb76397b15bd6608002802fad039657c70baca8dfeb3ae93709762eedad733ac13521d5da1aac34833b2d4ff42a10be6b0e514f401001f1c4319bf29f40f117c66b0c5788fa2de6a9830ed970c8719f23062ec54f1ab3d47ebfa50cfc9895669390d1c09cb553e2babf43c3db252c929a0fcfc2a1c406c2a8574b9a49c728e4701934a91e59d3ea050d0c6a34c785d6e30c695bf977007100a2013474c7e46366171697b7bbd3e049917f86a2fa59888e01148d356627024a4ceb657bd5562f87492845f234f09db51f19c5e67d4ba61f0179a23e12fdb34b15a16d39b9f9de4e4a03353a9e9480a782cb984cfb3d815e031d8b648a8d2dd103f9db0b321d2d43a8d1fe84f98a1cd7fd36e74381c6e38e0717331f4379c732c010d0deb02cc63893f973ab5cca9a2f01c97a3dd76764200ff3a5f78a3910710c4e34c41230fcfa77b0683f164122baf65a43ce3a5cc58d0af6ba33a62ffbdc5b9a11293d46cb43b4fea07d0dd19414f6eb1cf1ba6dcebb0d51c939753e8f67b08f84275340ad941c54150b6751e3705e4c85dd774af56f07 +generate_ring_signature 3ae3d9ef2c81e08c3b85166fd60d74ae81f1be0abcac3690480a1ad0186e68f5 27e97d10d75e9108d8c4e89e43ba75a843f4b250b3873aabf158a0379f55986f 7 1ef624898a6fb35a3396c517f90afe615d97e1beb426a8bd63de31e3f4725ff9 db1b81edd507d1773035e62c458b4597707bd5f7abe52f6503c7e94372699daf 2d0b80fa6db1598fa34207dfdbdded71c01c7a6e0c48a9895d103ea21f88a765 b75766e4545583a57ba86801b55b63f7f7c875f6ccf2e567048d191230901a02 6c633936bec4bc1d110cd8c1ef9ad728d43871ed5c25496493cd5f853e871b42 37426776747c40a3e60b35730a93d3aa3753431e7d8dbb2b61a5aad33ad8f6e1 79ecff41ce5665dcbadad024b05d9e26f9e6c7b9a7d10d6d608ff0fd4364895d dff0e02f69750454db98ea1c0486d8a27a105362ba9b8679b0bf2725967dae07 2 deccb28ff9032a67a1aac0c4c0e3e0b4bc56a86fd342d36024ad8c72398c580acc594cfd86f3828b70dbe6a20269617537b43115d0681488847674610d2a23007ee786cc4a1d833d446047fcd6aa100030158d60f1e35eb6e3cb64acd7756e0411a2e376a1d1c6d055885bea1717aad45dbd3c25340833187d6710f04261a502c4d9d4757bb719bd0b9ffbd39cc2c8136126333fe1388b35e60f5d328c3b8a0441d4324b7d98cfe53a3ff9c34cebab953915a0f086cfaaad1c69367c60a02a08a200f94ec13a6a4c5fe059d682cc61f8606cbc370ad52060b3297e664adec70160f308b42d4ab41c82c26dad8f57686251cbcd5fb4b4b64c631c857e9ff9710599dee2fa471b50ba8981dfcc5c270a67df5acc2dedf010318127ae7bd358550e0570b3ea79b85615c44edb393d3191216f6ae6bc6de1a03fdd00a097cf24950bd88ae2ca1bba92ee207186040eb76ec6c6da2f877e7081b0e48cc661096bc003a3f4778e2f20e34ee38093f9ddf03f7c1589549c606606f01e912fd2705e2d0882583a8c640a3daf385ac7995b7d0f6e790af5e16f7aec2564a2516d8dc2700fa69930edc68cc4bfe8b15a94016fabc986e5535d4846faa0f6b6fd6f89f5f700 +generate_ring_signature 5ee5ba303fba3f86f72178eaf964147c0f4728181baf66d0d69886ea57304f1d b252f1d78bbe4179682604610e30821f9a161cd81ce886689a1aa9199ce14c19 1 cf0e634f021ae9a7741780f42dccdb7ffb0c5dd00e99b208872e604210ad5926 ed4ecd59c016635ae665994d6a1698ba6b39989fc31bee19aa099bca24bca605 0 001a778568dd97e3f6e3d1f5d073a5f174fdd6ded27b267dc0b1630816dc4d043164193e36db8e80e5406841afe1b9ed379f13779c6faa8ed71a8d18e7d3800c +generate_ring_signature a168acafc3f3634fb3afb2fa4c85302807788e0b1d6db633457c343b83d4ba02 2ed829909d69d8bff8cb268783d3e844ad3539ff90fc6bed846c15406c237801 3 8eb8412c40ec4e21cd9a4fbd2d7298232370a4ba83ac5b0d1e316888b817b511 badaf9725b5d536b6f3a66ec3ef108a4b3115025df7f43e7ab249f3f58059f6a e30cb9b423f01c24bdc64c739a31419cada83692fe0c4d64fcef6bcb1776d592 7e76841378d75e2606cf68cbf75f3f3db2cde74b2faa01a701a3c432ea191c0b 0 7a99869b5c979655b5263ccdb4c63a26e332fcf5822c5905a261c7509556d30e99ec48c2e5244fbfb3c8aea94886aaaa964105559b4e02d60bba7f8eca137f0b2e82da4609f47eb803f442a3a6375c9e0b6797f0b9b858e4c1f46f3ab491620d115b42ab21664127e12dce3ce6da36f7bcd00b137500a9c36aee81dc4449570da5918134b6462c76ba245711478f74b3c5d2b5419b4c7bd795b4cb86861a8b09be7ae7af1b12d704d53055587eb26a20d9fd0f10e69144a746e4e44086ab0704 +generate_ring_signature 76c197a029a4979d0388b7ec7e68ee11ccbc66a8c504502adb011f718f46ea51 b1490f09967c786694043061f7b498ddb9b064091bc8437ce133d71fabac2f6c 31 72b83a2fcfa3b21c53d07c617a198a000d5c4446bc05f142f8d42e94938d3c89 bda3e253729f9b6b189e923f6a5cb3b36163b06e8e2e5c1c6d685cd272ca0ed4 a706710f83c922e3aab795c56883186dfa8dda3408ee314f04816e9b8fb92f90 5c0813cb343b1cb1825ea4378014a6953f5884a8250d5766b23a9af5b43d2dfe 506171faaf0ee551b7c2dad4b1a4edac2239be4e7fe76479cfaac59551deb880 9e58cd307a14f6482f0c00d9342fc0b08776deb879c567b53c9a5ecd58d96364 aaeb56ebb9049870bbf2c68ed50c74c74f738c282da33a5897213c7e305826b1 d0e9a71c4f7b38d6e32f455107a814d6ccab122a3d027f320ca36f072f58caf0 a7672d1d2178777608f058280cbe24887cf1c80d54e4d3ce8c6572600887238b e550d5bef941cea97a3ca5587654794b2cc95e4ace85043435632d6fc9e6c239 08eb2a4f3d70a4e70bfb340724fe58e71cf75de3952c474abd8270a1a4145c67 4c69a16fa040d1904535fc5addfa4ea00c6478c97fcf349094fa4cf540fba173 844f1c9064fd2337d653c08109662a533ca1ba3adf29f71ad52fec1d581df0d6 1f38c8da9e1841686caccb994796b0d462db73db8bd5b023f47438da226212d5 b0a333c00453586b2585617916edfb5d7c1e0e41a06638e8131960b9058e0486 3e1ef31dbed8e4ea54aa290b70cbeb6674f2edd2f1e187634e7489fa52bc1d68 cd5dacb09ceae7dbfd47d22450197370322d2e34c5061ff0f886147a4cb3b444 90b87ee3103b7ca76cb968e6781417438fa9603507b72d5bb1abf1157576bcfb a8977f55e6aff38511520a9f07959dfed92a336e0aefcc6d10d21bc9937ce18f 5efc3ee01e44a3a68febc8ab9dbde69bc47f943de706c9e1309718b18b66d7fc cf1fa8638d4113d43b9a9795c37891dc10d1f6bd71919b014529f59cf2bc0d6b f03eb4af7517879ea5d377e49ddaaf2533e4a1e61935ea0d857fdd1387ebc899 763c9a6765b4b5ac7f6e53789392a1ffb43a0bc850707018f6419ee7fe8c7d0b 8a524e3d21eaa69a18ec74a26dc2d2c6f93689abd3fa4ec469a9effa1cd4bc89 8b6581e491580aead5297d1415c29bd12e15c79ed2d0bc2a25246b97a8337604 7540c5547acdd0df7dea03b49e2cc77d255385817bf3cf6ffce2caebff519074 45dd05c9ed02f168c67efadfbffd2a95f0f1cc84f4292826083bd10dcab56b14 bbe9ed0564e875b45735b0fcbb7e52ea810e54fc1f8fc7e1c704a16183f48d12 0d8c787d3134f90078bd47ec104b03ad4fac9bc6d61673c780fb445bed6c6da3 83cac99ef2be476a29ed5a3af28a348574576a086eb5ce2afb477f5ce5aac752 d5d7bcd8f0c14dcf5f621aa4f9dc1670efbe7b82dae76b43698fc21a2c05b2cf 019a7f813771d3f579ed6e3bf4722ae6ec129e1efc8854fcdff5c992a8e9710a 14 a6e35db50defd44043da303dce83cbd5a0f4ff3c05a17bbf1bd5c9bdfc191104adf136de1b8662cfd4e8570c738e86632f26ff93d63863283b89722439f86f0376d5f5c9d9bf4a97e7acdad3ee41cbc69dd76c7e75cdd166e181c1ab58ab9e096fe849972d98bf267a00c7e6eb86750323e935fb28d99e1b7a36650ea89f3100f51636a4de174fc899480cc8615637d34845e99ddeffcd03262383fdf35c6302c07eac1361a586e306a911a0bdc8ca70eb0fbb599c462d6f6a4de41751c98d0afa3acf3a707acde0c669fc91d4465af29c41eb3ea365f6bfc653019d91797b044e186d097cce7ebd06c4721c06d8d5a20828eeb11ba707417de1d328ddf36a031fb1de95f29dbce1d377dd7e5c8f1e2e3ca3136209e624b588effa2997856904f4e1937397b743050215c000b227d110bfe5263e96830aee34dc321b58b49107400a45ef4344c27aa292170c52cd93a11b9ca7325094d17a414fe00080676e06097625b0d262a2142d29e9da764eb52c7d5b78825815e02e070872ca62b5020048b1221dd9c23d7bee7af6083a267cc0c200b220c8d959ff32e4100421a1ee0b787c29f0422d7c1bf657c777d4240c811982cd1ee38d3fb3cdf6cae4fd6225073d45caaad9ff3975a5e561c728a2987f3763ec609a224079114b61661044670c1c9cda2f3a9320e833db5beba7d82ea79a3ab41fc14596280244c5bb75b63d038f2bd7d605f0be1e8a7823d17b2a4451c2073d617026848f6efcfd8643ee7d0c427f8d1877767f2013d87a4dcb9d8ce9a8ed1fa0a39d400abd2f25c876cff10432781675d3e1854509e4f02f05f54c5aa0004726802b4b6203a5a45002c299033b1ed32c6ffc69c87192d4b15c02a7ed7900f697a6c0d9afc0cd4fda8cf1e5018e01fcb365827eee1c9bcfdba067587131e395338be94ff70a5e1af23c41300da5cbe9ef39047fa4f15caa7b8a48937790faa1d9ecc003c592335e11b753e00c85799bb114b2d77586f32f085a202b31d1820794470c7086745cc047b028d90e14a0a74df45f4d325443841b1418e58e98edec29512a6b365dbf26f4b790260bcd51db03ae50601b12221043f02a374015cd69d7255976e4fa164b83f980740f368be694f43183dc2ff469e6cccb08f7abfac8b02b3addccf6a7383151fc750af1cb3591e95b12e58db370f245d60038df752bbd70f65139ae11ba9d4a6fc802242d15c70d96c5b307cf673c67a53aa3b885b36356fc55d5301f6a9a66deb20e2bb0d0d66da832717b8ef4d30fd36984ea881739ccf243d1e0bf84ef8835790dbc92a2e736ac6c687caffa57787d7e21842f5100e1ed9aad7588a83cff0788082982239837eff586ca787a9716b35112f0e8a1085c0a9c8f39c998472c69040f915174193bf367ddc9c5ba00df82dd92d23076df3889c6c1505083bf8fcb700b4f24baf11a07a194a1ac2b0d6557c075ba82fa4298e19d07972910fcd6f595066e5b499b5c032ebd316150b8feac64837d47d6cb48f1e62ca369de4878fe850cf46cd3ae66a658c9c4ca7286cd1db79e854679e9c4959154bb642886a8cc23062a62d11b0458974feb9ad40df7e9147f2d4cb984082d64017c0d826d7d57d2058bad8bcae5b0ade7f4d2cee051a97e04f5e9f8272a3cdd068141ade0c2816400865707f4bcb1db7dd7cd89e856b6a10005a020e514024b55805da63c833c1d0a32d3ecbc38cb9851db07f183046a8784fa9b6700798fa293d942879748d3a004035a93080dcb8b93c9e656434ce3b759615d9ef6375c9ef65da4e469a3e13d0be93ce82a99940245640b8dd1343adc3c902d13550d0009076610f9fd5ae2a20ff6cb10576ec43074cca73e39a108eb8be2b771df9478c1bef13e018bed8a350e53a3eb816116f57123414f791a170b83298f1f60dca8fa84d8c1083d200a910bfc8131cb692b924a41ead51d512bd64dc7f925a7cdc5d789b4ca860e17a618087018bb410997e4cecec255d1fb709b9fab7598b02f56ddcdb603e170e054f10e43d5455e673cd080a167344cf5896bec713f814b2de7839b69acb53e5e35c10b77200e508b9fdaacab4ee81b0ac42491d3b48179b17f4034a94ec80505a2ff00c3249e6bb42c067949a4ed48b06842415449512775e53c07f156793d4dc0ab004fd86e64fa97c8353b696c6a81b294ae66e0336a8aee21d301a1c2cb19ee920cc58a234e65fc8ba21605d397278a5960e3f86693d6283127e3ffed187bfbbc073b9913ab6a4aacfef80ff3bca9d6de3299f6783db8146047338d113159b81f06bd62fa90a77f4dee7bca61ebb3ba6e963cd9c5c64c938b2a0ff56bcfadbca90215a310ac4ea3f2d37ce487acdbf5a70c0341d257bf30d5b75b30cc033c07570debe16ec26eb5539dacf6a06e71b609cac5bb41bee606a7dcfe26ec973c412701b90abf86a434ce93f80f14c1fb739b645413c4146252a1a6c965416ca6a4b7054e1c3ed057106e6f41901cbbf8759fa02495797692e744c2256b77322ef12d0345c9b32693250b5fe75a4d25b82a04e3c7349356c998e822fe30e92b2367d408f3643e2617c6f022acdc349fda1003d1d904a1d611cf47578a02fe55db4b09039497d45d503eab3f736b8de10c744e469bf8a205e281f82168531563542aa9047a5cafe4b5ff237877488e9bfbb29ad53764eb80264d92966f305cf515842101343236e743efda7022cfada2ae9ce027b9d07c7edb6e0f4fe09b4338a111db0366e2a32b0f7c3e240eadd98b09c85d706388094a8cf1434fee740843e257cb03 +generate_ring_signature 98a0d34ecad7f93a524bdd5bbf05df6aabf1edc86e6dfd555409eb9f12b970b6 9472ff1d85dc16f590c6b034169850cd9a7c61aa49991a2abdaf9924eefc483a 17 808f7fe95ba368b13e3b5f5bf8375263e293056dd4c0b946b15b6811047a07fb 8b7a69805ccfcea3c45182def8cc422c1f23959e156bfb4b94c5fa3392589454 d56fa8d5afe511e42e28e98f7e24aaed8fd49280766b40781ac01b14e402a94f f9e92df33c3a7deaef73480de48f287a96692bb05f964f331e86a7314ac3cefa 44bd6fe3e84c4b5e931d1751bdb85138dbc2497dd25ee0acb2340adabcc3d2ac 5e0e3b4e8610b3ad968c0776fe185d5821b440b1044c3cad601377b92792dfc8 701052782deb012f529a3fe3636fc4e6d4f3c5319c5727db36b7a76a634b983d f37e7aeda09d6da3b5f9d06dab4c809a9a70c58c9cddbfc81178cdb90be5d511 ed952f75fc7fbe0c454228ba22ee45bc4d54438d88e5544cd74d33c5e851e80d 60efc3617bdbc7957d8511526338e57e34f9a8502a8911684d24de2f76a87ce6 17323e276a428b2b238a5c03460a881ecae3ea9622f35add96322ef1b42c1ee7 9e51a49147a1029412502883ee90d0b683925d611e354913af570c8be1549f3a 0ea8252e6e1faf7fbbc70ed8040f9c57c87bb42876ffd8564032776d81dd5dea a7924de7fe171159aa57a7d22a5647f2e968396e6a4e424afbb070b98d0b12f4 f985bff093c1fd67d92b225d00bc4ae37ca856075940461b790c17c1a73debe4 247c72d2e0916bdfdaab6abd05ef5a877b7f175bcfecb8a499b59c6577b93e43 12ec03a07f4f01fcece9465d8967580241577df2d6da53db9a23f5257a62ea6f 4b9071d06ca415811ebf1038c1bad7d7a2338f82b500a3a61c0312d48b09040c 13 859c1e1c5cbda894530f44e8ded45304fa80f98800fdd4d428af310f674c510b466d49d63c20a0e17cf5b0f9064b01e366fe8200a7dc9ccb8d0b8f78a168b409699898a81b8754dc6e21d0b8d3ac6480a75e36928468f66332e1e4f816e89c02e177b57ec9a0a8e5162d25c28579c79cfd0bf52cc100d75d5d2513344cb2af0dd10a614ffd7682bfd04e879696dcd667764045a30b2f0be6b4ea82dca29f5e0c1a7f27c3ceaf4d095f7c52ad678002f3cd1064f2571255166115e0bf92cce90139e446e1467a363e81f8246e019eb0e0d8bd4f7eb08e8fa06e14bff30fb5f20b2cffc01b01cabe03605404835509413100dd34d4190ac0482879091aedc89e08d1e80e76ba33679093aa6dcf1520da84592ce7555ccf219ab3e169f58945d60d0f9503d5d048419bb9d45e0ceaf5a7580e6c5375cd938210be5b509e5522d207b620bfc3729eeffaa1568695feb283fcb0cd822eaa8b22fb1a0b59178d2fc306571263f570a357ed0aa7d2e2cc2f1983ecaaff08323ea18daf7e850f2a26ac08f5768a6249e31ebb16c745dd6a2ec86f3a54aef8dc4acc63fc9b01f7a6af6b05d0b25cd80c41d4e7c012e335c93f5de6683d86eecebc8cb662c1b79f724b380cb55dd140afc90f4b6e06679defd00098ed175ad20bc15b8295c076479fe09c0a3ec14584145f2ce63210f0447890e01642e738626a19beeb6dacd55ca322d20e4c9fdfd9595b4683e4e3e3a602911d629f7a119b201f131916475532f7839a01ba69e15f4ff7a681152175ec1ebfbbd40e2d8ce5f27aed2274608ba0e63b8b05530c9c1b048587436bb8c9bdecfb34abcf8b20b977ef06de844bc7234abcd401086653aaa4bba433347c4693e062565efbdc280b313b4f29370bf495b887b50d3b4bce34bb4ea94088ef2e073d358765e4daf994689c6107fcf7e5dba13b2f08db464a06ed3ce6bfabd11cb5cc0b2897446b38c774f1fdb59cbb8c97dadc100e296a999880693ba6a27bf84a0ac07d6485fc803cd52887f293e2e7e5ead4df011f36a05078ed3baf55eeff5d1a3197ad465741c36c51157d8f4674d5abdaa10cd04675451479f3bd2ee43ab3e9f7a574cea3558babf4602e34210b0ca9cf680ea08ca3f5fe73a030066f67c8904e5ace16a590da9f0557ceccc2012f4946a10b025aa5da1cf41d42a7a630649211c9c6a9afd7166c64d0fb5a82d91dad52b60bf49c78ff9e52752a72c427a594fa5226dc98e45d82d7cd4fe1993a42fbf00f09b3421d70c2b86ed644632535ae1102da43661347756f0a95bb29967e5db59e0ac20ce20f5370fe18456c59bbd29159733d89645a0e2e2be82985891dbff9fb07e31f82dc4d660cc440733bc6ada51b4e2073a18820b3cecd3b1a9e8024a77509acbfa539afdf4da2a4fcd6b15135038f33046cbd005032adf44842a9ea12680cc9b88c1e3af379575e40899d2adec5cb83e5a3a5f640486422aaa7cbf07a94008fae45d390690f1f8c63805df6a21cfcdcfbb23382a28eb8fad0cedfa1255704 +generate_ring_signature ffaebe5a64fa21b3cae1f3c067113c4cf958a4287801c6a10ba0d63a82a16c0c c44d1d1d668711d4ccc3cfa257b24e2ee233322f078e4e6f82a17753e4f5c8b5 1 47d76caa6562600ac2e06caa3b861cccc17d73eb25df6c34308e21876aaa54b7 005c86d012510a55a17ba3300ea863c7179c91e876ee0138f8e987fb4d7e0d0c 0 28142d4e0903609bbf490cd50c1317501f90f26397aa3b85dc72789ce2c3830499930cc6eea1294daf6b25c7a27c1ffa0856da7e9bd49d8b37b8b7aec1d10206 +generate_ring_signature 73733ec5b86946952811b69e7bf7ba5c37587c91f2d34cb2370d2b9be05a22f7 d8e042940c4c896d6b06bdc0fa35e077a07e6a39cb15c2cbf9ff5c6df99b1cce 15 9161d68233642bfe6035afaba1645dae605eb345093bc46f03d8f142d5fb9fb1 128cc56981de785308fc47e1edbaeee881f818971d65a8a2c0100ff1921288cb e3ddb25dea20e2fae36efcf1c8cfe7d3514b371157a39108f4b0e7609928c716 d08ed18856d466f1ef3f50e49d0986e0cc3718be4a5dfc99311070f7b135891c 56cd77bf457f0ace98ecf454913a8b6940424c0e12e183b5bd084c34122084a8 232fb54db3f3758c5329d8d37726dc8bbdbf44403192b6dccfa8d3783f657969 554b67d108192723af618ec6024f9167100dd252cfff52cc9288da122350b548 049a7ded353d5a0ab8661f1342bcfb123ae5c8369c8921b5589a0ee96ae49512 ea95222a7f00d7c6aff189b8fc0c3844e24375a511ea4d62386dd6d5599f165f 558cad957191b01fae7f274202d8faa3b5b71348c1ef4600302a84f112bd2f37 c046f7d11f1a0eb7dd6f25dc1e83c2ee571e43241fea595625b06b18180e64a5 2f203ef33ccbcdf41813ddd96364179f00d04ab0c475744f66eac76524dcccaa 1f427b4add5faad3d81832802e23ae646a2ae862c51575d6e753b35fc87c51fe 9fa5579abfc3a715e844651c5985800b8f77f4b2d2c1ec4168e6817fd97cec7a 56144586fedbe763b3b0f3676cae4e49507c79fb23b7ea08a8b755e70c46e634 5e508d8a5e99e8ce441898c87d40de7b4a0cfb2bffda4b57c89d20065c9c3604 6 b3c351e57cea5c5759ca0b57884fabe028dc9c6185cc8b4d287dcd69c6eef20d0d3f000bf5b9f494261e1baccf24c395c3847ec397343c46958cc67efca1bb0e0cf2d6dcd53918d59651a6de5b0998d1bd6b7b36a2139d37536e20a10618ab0e18a2c1c53670c77f096c3a6076fadd9dca4c463c546d682bb39161fd41b2d101fdb829e55148ccd65804db07eed4e90bd86f7bb8710cec2526c0abbf9d3c620227574f5d1be708450be128368789ac3ac393c052d9975c22e81eca28fd02eb0faee0231339208ed1ead0b1e6c9af1a5d13078045cf2a31d3f9229fae9ad9c806d9c80784748bc18cf6773fbf3ac595b926d2242897016cb2dce8532c0ad2610008998882fc468b2eaded12d9c4f7b488a434d9a9ce7a609a7031348a904eac05170cf699f45ae5352eaa2ca804fe6b8f68f83f2df8a08f7298bfbbf630b4b10ca42aa71bdb7ae231490fcb5a65733afe50acebfdf526a2a5139cb63e21c2b9018684d7137bfc9d3da812b8321a6840f957f4a71a629f52881bcbb6c495133c06860d2180609d495d3650920f5b5a4a979890f54190d0a1ad45119b39aefea60e8b3053febe287cbf8b391f1c63054e1cd6c0feb3ac98ac33b39bcbdc04daf00de50fdbef8d14398e33df891ea4cf25bbaf32612a0c8a5bd8c03bd428fd5abe0bbace413f9217636270a3b6b83036b7d98ecbb0d0ccdf64978feb7f3afac426085788f852b8f4cae321dfc104c6a1daab5cece5f65360de4fe5fcb82c89d45906e4059ace4b54f91825912db55afe71f2d4734db085b01aca8f2c0e6a3af02805ca764ca6ac38bbc3aaf92addb8d918d269c0d6da32931836a4cd41c755dc4903b1355049469fdc2ac6251c16392b21962614c446650d1472ce5404b6bdb0a100ddb8b582bd55b883dda3521d61ee48718d2fb924f74ec600308c3130b4d1d807aa92e305c02b74466647d3f7c39ee8368a3fe2a8a73b6743b5c1d5239f4db60e349b10ae9902208c0bca72576fed11d77d2cd51e120febabcfbbb2093e7e0c053e9ed7b820acb48f2b95fe94cf61e95ca729e0a7d03019c65d6e6ab01e6fc10d5191425ef7421c768ec83a4afa8be811da71a2ef50aef054e8b4999012fb9b04d148881fdd57b3294b09d115da884326eb0839e96fda767053b1eefc5716e9018a61796bb8436d12c1a7e629f0ced6006e5b722c31d914431f61d6552f11210af88519be0c5db6ccb80d7f064b998d29dbdda9bf32e77d5b1e677a055744f20cfaec6be4bd9dc4fe43336217cf393024e656d98b1a9dea814174b0bad1a3090bdba8c7a7a284dd56bfd115516bbffbfbecde1401cf62692c4c0110cf1f05ec06 +generate_ring_signature 778bceb8b14ed5f0f51e4483bca6ed959b851aa5ff880a59320d30d0e9c9ad06 d8508c6187929646cc7231cb18a959d0c181e5f40e550c32ebf11dab277cb36e 2 dd0e07b025b266a88482d72d7d3122d2513b11966b5f86b0b3190402e30343db 903bff8f1f2a393c6f31dc6672be383ae112737329059fc46090bb1a9d684c1e 4a41c53827294ab03c79424b60a5faec6f44fbade5e01d59228680eeb4a00203 1 860ddad8565df50b6cd6d246331c76a3f9ab5153910cf7f91c4e5852789af006e504ae33ebe172677529875fc95bc74d874578b9d37472016895a31a5f9508019e7d06c6e26ef936fd78e92823224fe7d77bdedc83021dec123405b75edbc20f1bfad95c32d0858d8059e31ccf4a45986643f07c730fee1a87016ee0b30f3605 +generate_ring_signature d0af946a7168a2b284854db904594d1ad9055cf337f25b80c56f218d3c0d8dc8 ce5be5577913123659cbafc8e7fe1b31227e976ec379307282605e229aba392e 28 b5bdd837cc0aac94b3fbcfc27fb023126a3ca0308d6f9ba22d24322d5eb01d57 e1b478d03240bee41ee60b3ce5c2875637836588696ea197fa4cc240d9f6ed44 4edf95184182c3034efaed72684168f95ad8ec6250c25b075f0912e2d4f63459 1e9cadb7f1407262d64ef55add50c2f6875160c2fb645c8c8ac96d2605915e77 4d888e5b483d209c20b60c5e5601c42deba7f05592ec656cd665576a0bd3f742 bda224dc161b87b51264856b6478fd355106105b80380ceb36d2e18e5981e6a9 37f0548f3545620b3ef1515fc5b522bccfd8b8a068598495c4d2cebabff9cc02 e386b54fa02234238ca51c9939d2e3fdee81abcb94853f033c615422babc8f63 4247bd9140878709938dcb0432c0ad95159a00c66902c004d2ea09e2335cdee3 93ce5626ab90afc7f16c8fdd870e9d3640407fbc4c29cc12c11108a852208774 a68380092c8ba661fb24f30b21d7df4b840dbe8fce869394986c7ae4e2edc0de 25be966db483ab07c610bdd83a5e26019a21c9cf65d582d3aab40025284dd4c1 ecaf62457619b1c1362b35dece2209aa5eb9ee8cdaf59ae1d5be775fdfa987ab 5ba393fc4c396411dd2e3e93455b31b17037e14f9564d9896de7455a1994ba29 16b37fbc30dce9dffe460ded732c588b2a863563c0d724cd909cca18df6ab50d 4e97f0370be8608c175717e554f34aa4610daba6cb16fb96c4136a57ba230b9f 86244bdecbd028e5be7c98b2d347e79647333e71784a584eacc308c094cafd2b 25839bc668849bfe64fc9be62997fb93d1188108f7565498c33f3f4ea4e1bb23 429f58dbe7f3eec5dc1bf30ecef4293381c828c5bfe7b7d7bdb6acf942cf858b 2e852cde3f41675c3a1aa901653dd6f91229fb5349b59b95a86669b806ad6d49 b0a6aa5f72e0b404b831b691b29c810c533abad713128efc640b78b46b8bbd6d 8bc504d0ccfc3d825a1de31f005b02e5d7e8396b213ae78dee2745ba94af12c1 4653b7942cb6e119836df837d39f7b43339ae6faba28856d987a50ad2794b292 454a9c491f09eba303fd689918d9ae6ad662f8adf029fb687dee072d8809d26b 861cd74d161b68c89564fe7621ed75778aa4fa86bcfd60b981b8fb5dfdcddd16 da9d169079abbd9219fc9d1ce674378a218433cd3058d7f48e71891724f8e3cf 90e52c87f417b04bdb42a50cd4344c89d2ef87d722ac451d8ce89cd148b9a69c 6226f09a0649ce2c264d056d3dfdc74cc8edca34091423845c78bb691bb6d89c d06d01f7eaea9f5aa3836471b07f66cb15f9abf5191f03245633a73abac12609 25 7bd2c4792bc05abc21bcaebf0923eab275c9144c80ae806a781653c40abaa003f8b3b085c8c653338f002969b223f61317162fe0cfb7c02df508e539adf1bb090afba55f7be6fbe3d73164429c946a29acfaba89321368fbf1ec88fa0f7cfe05ccaa3169e9e9249298b47730a1ee026f0a1ce38e84f94b8d79152f6fc505ac098a89f2f816b1510e983a61e6fff890275a5d4b3f7e6cca6856ae85d21c0dcc08acfa7a0323ce302ebe7c597773fb3876c37143e175572990de6b0e66038d8607a52cc9e23ca724149ca49689b65b9f877f0910ba87a3468fbd1f089fa9a66104f8fb2087a7ddace7d2817e91dba58878fbab281f4a69d273fed6cc4f556c480ae527a2b69c2887844217c6381065f1da64d5ca2fcf42dd4193b2f156adf5810a601b5184473ed3b918353f29c1fc70b2f2ad706f1367d17cc2a8fee13199d70d986a1583b2033055a81581688a8f4ae84d676e160e3cb0d77481446bc41f5907f39d9cadd88a421412342994a8226ddee48256c08ba37b7849e824f885c5ac08ae60dc153b19c11a605f7f4f00dc48749dc447e507aa593065b2d4da1299670b89c882589707c218a5def3ed377575fb159160fbe2059bf32c0ce988cc43500db3dcfae3c7388990b1aa7535382c60e82885b86bf0bbb8a1b47390911cde5303e9e16a145e17a7e4b2470d6c95a063198579bdf344800676b67e02e40318f20b9beb98709c039a2cc7642f921e367bed48c3c5b03e6be8b056a8e7726752900e5b0a139ec47ff32a493d12adb3ae7c184a2e88dbb7212818c2c580dc465aba03eb465a4c9e17853e6a6e617f87cf294094135ec48b4717c0abce8a61eda5140d506eee7d820553c62aae4dd01b404e85feb69b61ed525e0e8a5b288211ae0b0b89b3e4f9a3c042f5dbf09ea88cee77e359207ef17fc2767b6a7cec02d3fcbd0a88c6fd3fac1f8617686fd61caac6ac90bfcb4362fded2da360701b319f1f7605e3669e59a1d191a63c991bdf24c50d3f970d648c763cf50769192cf72489ee0079946d777a82adfcfbe5954b7ee6c2cb334c8bb2b03e2cbc7f23f73fbd8adf01752c5faa7c0a3fcdd6e591a5fbd3c516edb919f4229e413bc3c50fe9baf25b0535098eb0e315fd63671c5b248d203a71a1057f5688fc8213817f3fcc98b0db0fe15983768c57a5d4c28aff5f7240c484fc1f2763d9c2e36664ca3f0b4d2e3d0cfdc0c42f84deb8597b9c223d4ce75587447e8a7b3f08827704874c65e0db6e06742caa0c741e16dbe4c60e9554ea42c8ec97f899a4b79a934fdab4db1cefc40be3fb1e63b85abf67b1b6fa6293bb93ca95a6e4b13ded72255426d2721cd2960a72d62c0bb5431dc77d79d2da0a47bcb922b638ea10574426a83d9056f7ea5d0e322e2548caf7286f2d7badac6d42ac61b2b1d0524bbe11a81edf6c98dbc2060174a3b5330a88c0b046bb042e6ec11b746f03c35305889ce2152439a01afdb60696bbf66b6699c03686d161d9eadd1605f7ea34302a4a798fc6ed5f71e09d8e0f69628629566261f40f41f753dce47533d4dab4d3359af56719e4eb8c108beb0849fdc0e99041c2a64b8444809c7737152c749742d3745e3733493c9c1c768508c94103969397af990202b3d5e45354a7766fabd502e8ac65cb9d95e7766221055b497766a15c0fdd8ced4664a0f178631315600fbeb8bf88d4fe5676c372970ad15387638228f0fb751ae7a675e206f9efc31b3783b5809ddf508c840d4d11035e44f6c3cb140162c3e606b222fb22ea62f15be1b4d3d228778c71993b0b9c016ed70b6bdb0885febea0eef3589c83863d45e5143c243f33921a1db96a11d00214682dfe8b8d097ff779e4d05279182433fc84d884afe6d59bf1fb3f904f3c02fc35df786e57a2f2bb97d979b60026b49d7765378b394d854c70ab016e8b170e3450a20dbc46af7995de979e4c776a2abda7b657f2a7a606c76cf534258da806ca1ec84d596b13b1cd199a120e5ee866e240dd51a23b2dd184cf96a09c181d0ff215f5148823f9d86431d7e02735ad4d553da0778fddf8faeb8cacf52f765b0a3d9a873a4f490afe2167ea8298824647ec7b50b945af20b36ef9dce700ef740ff132ff39df353020c8f547580f4b7e10275a1f8a29733c7eb60e9bfb5ae84002eeb384abe845a2d71d3efcd97f83f6c51bb4b5b66f25b9f78aaa2e76897e130900f77f4487d3730f5671e2badb83a3b91dcd33c949ade6537a86814fd226f0056c59312566d7a4dd098ad3b3b31f643ec34057cdc2aa9e90f3cd30ddd9c53b029d1988b0335072623a7419fade2ee80eb2398fd17556796e4db6de3481a0a309d133db7c07cc91c9d6f7caef4bf7716f2ec6281438dd86e751bd943b145c1409672aec2f673359ab6e61fa2a2a61dd16f2951d6b8e0a900016e1828e2a79b50b8104e4aab9cebdb578e8bbba5a6e9ed3236b655173667faac09dcdae12fc6c08b3a69129ea3b203634a00a5aaaed6fa6f14e73795bf0f5e00f1df33020e90a08 +generate_ring_signature 32fda91fee7ffd86261cfba41936bb2a57a1babc1e41c93bcce51132d3e9b507 faf0d72af0aa2f1935f9dec6852902445190d777581a778a7e1a92c1bda327bf 7 6e89346180becc891a370752d14a032274b762a037103e634b45bfac60809226 97d5764cdab85f5c11a86546a36a96bae26bd080c451ef596daaaf5ea684086c b60d1e57e2d34be3512e8a2be4b3d00ae2fae3632b5f9b0ee1c71705600b1c0d 3f10de099eee553e1b45f8f68a760d6516e2690ab993b9bb75d57a09a0556f70 070eb1a8fcd9732847154aea827954d8eac4034de82aa78cd391ae8c8f395e19 49ffd7d2431e8db5626d37d6220b630a431cf592bd72faecd522feebba1f2291 cc1127fab637befc072b81cfc583d2d93180133cd0ead88efecddd89bc46e4d6 84eb3ef42330604ceff1a7e02b17de15c717f1a904f34db6c724f1702876480a 2 489d9b6007165452979e4ecea64fb5764b1c0488a6b9489c5691e4575caef50fa42e51e1adeb9763c5a84edccd78da62e5ccea264d4f84bfb38d70981b713a0969d670a07bdfd488eeeb02f722e281dcf1d83c29d5fb5cb1b47b54fc37bff00b340ebb0aa0d0e7b2b067e9030dc66646caef70b451c68fa40ff7c7f3f3ebbd0e56f144734e10dde12c0f45e70841cb7be099fd3fe3957e43e66d15ec338e890410727d7eca4da979913a9a3f22b23a5760585d09a4a9438cf99f518375dbdc079f220b487d36e27564de0a7795f89f842e8cca4d8667d9a2fb2703ef54c78700cafdfae9e01d8fcb90555f5dd7f0e7a05230b871d2935dbe51dfe4dab2e0fb0d81c551b476df8be09230549767ad79b1136d64a6eca6edfdfe9148a3e649e50131bb54ae13620964cb242989baae1902e2b74c1b353d51d084610194963d83042bb8af66a24a2e43864dc4a7730585acd16f2a43bb50d8532446937b77cd2d013ddd668f53c94a12956543ce125b4237cac099d8bc8bd50414cebefec3c0ad01f7dba7104bdc71bcdfd275aa9e55980dcc3bae1b33ab776fa84df2bdf0886208a112a3f59ae23ad19d1590e5df17a93d145c8372c6875a3dfaf134d86c721d0b +generate_ring_signature f95f58b92f28fe5c8cca37b620c77466d564444b3c4b27bda95be071949ee7f5 0c7a668470179291b39470d30af192a98277cd4c831bf6fe9f3501489ddfd8fd 57 9e703ea6f7136edcb82cedcbf43a68c6782e8f1e57c5c2303b4131d1d5215b78 2ca97ab38e7199a22fa6cc16ceb84711e9f2a41a8f997386ce5b60332e752d32 15cf730ebedadf9fff98d1d7c42b42328259a37645d31ac52a1ef24d67208fb5 bd41a6107e68625847b3eecad7c295f58cf612069e47eb4a713f707452687a1b d95b91d6438f3fb2cbafc7977aa1c84e9869cd78915800f9560d3f0274b2accb 698db750d8b5ca89f0a4b0d0ced5f429626aed733dfa772cfa6833c2b97fdae1 4cadc08cdee26ac4f3f3cb932ceffa9ed5e936df7bf97492fd07bea007d5aaf7 5ff610158afae988f8cc3731b3f0613d1300df97a6822626f845d8a73f14d839 46aa6f92903c682a2897c8d73559f1db32dab440f857d9d3bee4e098fff99656 20bdf6ed73381e3da67d3a8dd62e82bd51afe2f750b7ae124b08583c450365b2 640e7073de6dbb3a8d8497c4a3f9c60332b485645519b2283a238742da08fe46 c9572e5cab158cc073919303f7c3288c2245c49de1129aae637b38c5bb62303e cfbb23c7d6ddab8a249ef1e1dcc823e9bece5a2eada6ab13fbc1242131daa612 8afa786d231a0c09a13d63170fd8226330f81f708147a1f47fee7557ba27d3ed b365ca81fdd650d274f1ea94c492a4a65e64adb7b0fbb6de9528782a3650118c 10984b7aa7bc1ac1e54125f0cf87f802a9d64ef28ebdf3dc91913a4eaf8aafb3 cc880b625163e20cbfc65210c127325e4b3a17af070d1b74126c7227bfb6d065 0344b564c3e1b91487e607626636a5027dd2a9590951f55647853ab8a886bfbf 5c568bf19007ceeac73bbb66d53c2cb062eea710fa82590f76da6845517aa8f8 cf31e75da46ee36623789933c452326df8b33609ff549b6709bd747f3a973ea7 c3af861bc2c38f2d94889e5738e4a756b231fd38b39d9fdfa307a6af6a26fdf9 3dec71fe173c27b02df24e8989cf7075342710a13976e63bb740be0ed3cf8881 71221e35d8ff84de6fbe8a8a23a6c94d4a2e5c9dd5e0806c337b78b40972d968 7433536546f072ddf45b1cb55fbe68a3c3b6154b4f43540974df4c96c16df3ab 62b2725a6f4ddd4e5bbb0728efa86630b41d8ab848d746f73af95767fd57fcec b6f19566fde140878f4f1fdd0f64ec397ffc1ecd66d780af774b35ec329d2139 b1e53d524b31832419ca5ef8960245fcaab7cf1ee7211f3de92737fcd32a2256 156c9189c4541514c557e3767e3ee07429b397cf1cefdb7eee9c978d1b11f6c4 4babe5081a6e5a1dd8ea579522c8f6a44db64bd48229ab6ba7f9758990c333e4 3f6ee7d39a89bb34bc68f02557cde48c0f1e4ec9654d65bd704637d16fd26721 4512838f751891c81732898eb640c8512b6a77cc7d63a3e61ae0827b7f710627 a69bb4cd67c4d72585a8d3db4000429a3ca1691f2da44e01ec0113ff72dc895e b737c73f3572c71efe7cb285879ade2b174b65cea150f180eaa17014d7320cd0 7d472b19d3b3a828dd67749dab91cc686c0aefeac6f10b021914362e9e3db34b f6158b35f8a7d3bbc700d39c43483aa57ce38c8e6ca1195eeb87c673e5c2eb48 5dff3f8b3f1d276f142d69dab6f425f4f78b1a18182d1ad207c027114a694b13 e54fb93a3f15286ce5f2284f284927eab88e48b8213c1a41eeb586ed999404a5 7dfe099cad93d510b49cd166c192e813c3e512a62dc4ed11d9b9f781f0194c80 773cab6f26db9c2c36da474052966fcafa308ad649ceda337f764428948abaf6 aa7946ff4d12c48703c04f6bfeec86d18c4a854109739b3863e4b5007999e2c2 b386b4f889db963dc4daeb14893a3b0b6c3b09a3920a1bcfd04dedd217e573b1 0726fc793b0c7493cc0439189bd82d5d0c3dd117b9aa33d6a0d9553de873e88a 93f9a1f0eda3a75467c99b01005ff16a4c8b006649f77533cef185c4f7a30b08 377b1b8895d0b601d45a5dd36b93eb460ffd9e30a0cf1d96b91f7f189f4d8732 5730b7cb7e779695bd04de02fbc180f8a7b1a7e6d0fcc066bb28d4c94b3aa021 548678ab5e2fb6d870d6c05e39b3267101011c0a842f373b0c9735313fe7c207 788e4bca36e99ac9bd39e7e4188f88b78dce485fd8ce7408ab5d5588a82eea61 c7fa217234c15a4b2af0aca4ea58af4d3762dcd3b17a7be13ad882ea91539416 12c3585925770b39fe686a8d04ee10251d504c94a6c16853f7ef1a6f0a525556 f8748feb866849ac48f120940c6731791a593e5973c9228693397d8e3ad65268 8bb30ab41ee5221c65b21919b8a784de26c55983fdaa5bf635a1156b51213f77 70a7fe521146c4e2c6d0673e9e31f9931c0fc79d516439422dfcb86cd52d6497 a85e0367501bb37a0c8d92a311d3b6ced453790b116246c9c5b7a3f6022d7dc3 062fc5d81991c3e216ddb0caed6fdfff321d73c2c5fe54b9d956d18cbf7b7c53 fca490659c54bb57d385e33e8b67773bc912ed88506ad07f33e4acd1444ff692 1cfa1c7683fd787875092bb812b4064d0beecac871e1af17e72d359f27e2d87a f2a11397ecaf7753c18f0a6970da0573a2804bfc4f48c099b29e71ec6397c2d7 25a78e09d6d2bbf9431db8891b048d8429eb7768ad2fe5f2482a76e03683e908 5 27f7d788a2035ccc721b7c41d23a974ca4d742e1125e99a1d4a4d36f449f1b0846485922e0663680e42c890bd46625c70fe5fd55e1c7cb02e59c9951a53a94005ef3caf7a7ce2c650b2113c33e25b30957c94cf0538b7bd53436ad2041156c0d8a60259468ea85ac380d57b0770569210f00d6b9129d875e80fdcfc27cbaeb0e74ec0d7fb08c28f14b03d2f3a5316ac1de3b87c0daa4d1447e911de5d284fc0d7b0f0f77824313a58d624c2f261349ebcd23ce34351685260126f2720fbf89064629c7b7b28e84c4d3d9f0aaeecb9b44c7443c569d88f860506ed8f09cba35055f9ba5151b934d4c0243448fa1d72ee4a1460043da70349a534d7f6f3825390a1348788daabe964f014cca277e72f27145875e19b13f9bc94d2be573ea1ad208587204c10528a5a43198bb0a94b990ee680c3bd6016e14ef2b789d6ef61c1d030404c14e6e79867d75e0d566d77041da9e01700b9adef76f73241455aa384d0e723bc0c0478109406b4d47241eaa7c8674102770b31a03b5c021e3eee419e20402253888463cc64f1902c8790a0acfc653f5abbb1becead628556c770affac04a6987fc9cb4eb55179b5f5f36b13b1b4dea9003db2202ee5a7e6c6bbe6fd4e010034f1a7ad53bac65dc590436e12ee080ad65d819b143e005de20c58c746730f04ffcb50c4af2dd78352ec4639da2e152988e9b03318b33f39c736e62b2afb0cbd3ae7759629f2d11304551ba72801ebd8a2adc7c071174feb134ccdd46c720b554e0c00d6421274ea7f45613cf1753fe5385181bb4584fc96fbcd5ba16f79000f7a49f2bf7529d577f8118a3ad60aa200c4e54d9b5fcfdf0144db9639bfc80c39c99d618d969a8df3321ce8da00692e7bf7adf30bc28936e3abc5d89deb11037c38e1534b617e9eafb9020cc1febe2cc619db3573ee3239f3d571999b198802ca6bfa30637a1d3794050f9dd9176121c3d373a45cb54c08b706617dbc4f4b0a5abf5cfa37149a54b30475d95f1481ac287375f46c9e186bdf0307ac3d2a5f07e7dee417e5a48834b45f26b5745bc09bccd92ed1c37c765347e648b401321600869411d9629f5c141edfdf1a9e1187cf1fcf505c22a12e5e1925b5828d2e0f03f849fcdbd78579deba7ea616e5eba425697f5904b660027ec4731a481dbd1e0443591eaa4bc33f189d3483cc3ae12ce0b1dbee50c0c5508398f6ccdb21e3190e4c64d2d920654ac28695e84fd6f7124d0faaa1d772bdc09b88d9f115f642c707ee2118d042fd17dd46eb049fcceafd63421216b301d099222407d642dc64330dbf96b9dfbc4616c8ea6654b0bc4f51c8e08fedf0a0ad25a08bb9a544ceca520540c2d4af6eb450c69e0b720ece8f6a8dfce12103e58461f7a34df2aba942a50996f0526524d39c46c2372f88e19bdd0c56ce41e6d4ec3913fcb72649131df8001768bd61adef1d3259d2edceabe0e53ba004da1c518b61119d29d4db0f16d2024631bd556963760c923aa51579921e746fd669aca97eb77bd3f478e82adaa30f7ae3a2f0a55f83061b6eddf5339c72c79bf8d5b00942db42b70fecc6b947630461bfaf14649d0dfb627231678c10c9a85fd661d078640bc88e03ac4b1c71cc0cf90306ef97b4817c368f1fa9c4dcd66fec7c272fe89a2446a8fc93e28d81a60c86a730b89e24f970987c83d4b7c364f0c905516858c5f750b3fcf894f55cfa030281eecee1b921e2d0cb62d63d58f5797cb42be3b65adab953508824438dd104642f8df0fe521048882440b71f168518b27e87da854b3b056d6b45b2e04dbd06ac7d4f733aa0994993e0e598695835ef31d45197e699b35060ca6e4cf807a602c835666bcb80b84ffe021e5425a3b20aed0f73a46697a0e0244e88a06cf4550107e2ec78af5c240b856de50e260a3a4732539f9f70187a64b6841aa12dc5630ec694ff2f617cb7c6b4b7cb858230afffad0dae587d0185aff1dfd43077cc640ec912bd629e5455a905f11fee1f7054a2dc1852bc6ada4e933197d75e5837240dff6c2c100507a72fe847a8ab6318358d307409c36e0ac8b0600e0f8c555c8b07b0b329ede640adadb5ddb7aa2389bba1dcb42ea0d4c62221b604bdd10a20950cf9c65f05236dd3f3176bc06b0a54b1e6475d3d8da7e3f6791a8811ee49c30b05368c7f6a8a1b99251357a8421878936e06723cac41eb4af36f4b2aaddf233c02a7359f323817f3a46266f4fd0eb6ad1dc6091569182dae3566a22672e360af0c02df92f3aeb714b2bfd08407b307cc14b12562492c6e891baddabd8d6b20de0aec5e6028f3a84c7da60d69a928bc9c89c15840c3000c076f484dee413caac605501a2f3a307d1dc44c53672290c4d2973c1bbe63503bbf617baf643021434a06add90c9c16ddbb49bab63ba8890a546d3e6d8f86528951f9a0d00bd20335f004a6ff52b2a95cfa75960cf61edf80b71d38b2c866efcad95b13a2ef78cefcd00b6a236aeec96e01295664a8e8bd04285918790206ecce3259bc89e5d766e3930fd351e8d68955a6a8190651687356170b93257292a99627e4b520e9c85d43c00462ff0a0b6cdd481a0d8268ab5602b663f9804b18ef5fe3b1b9f4fdf361f3fc0b0800ab519b986c77d595ea12c4dc4a78d71adf643cfca4cefc68bd038e2dfd0c50874db65c84b38f98a4116639922bf29272c36f32363b850a19145fc9faff01cc0bfa12ac950d2b2240a83110a080c58bfe27c57f4939b7bdce9a4badfb28097e1b30ca6701fe17171369e1a318ac031bead9140d80bfa4f8e52434418e7c0fe81f9d0d5bbe329bf34e25d08ebe114aab7b9fc1fe131636d977a083ca1e8700b4d52cbfd16e0edf3e90a6363e88229d9ae295322a2e22804b3bbd8da25aa80ede85d8b0199a6dbb3f3cb0abfc92248605831fbcc1be9592baf51b528ec65e0245b8ea254a8fdcd84d13ba4707f48ed4c4439091bef66d82ea19feeea757b90f29cc9d07e235245d0f78fbe7496ded41621cdffbd675cce3518f0ac63c1479015d5a7eb7439a6cc20313a7bec7171a9a5163cc34682d84feec5a1400c590ac0c68bfdc88ead31c34bb1001cb945ef21b36fedcefcf9a522ba8231fb9ac7f4807d17c3abc706da669722eee6cf82fa9e534fb54c87c0808fb158186b3442be8064b3c32b257885c4f20eac683f0f0adddb1398866464a6f9655814164857b7c02008184866c5972acd64bb760da5a351fec2f04f16f50a041da0e68032f2987067b1a101732a847430a698cd39bdbdbd59e6f173956be1fb7852e0519238e1f0d113de9b1c3e6d0a4992100006a02ad18f9ff6188930f58f98ce8a88712ba48026f0d2321b20c9c84bddab2a649fdab94bd4c0eefe7a9b4cf97ea80ac7868b10ea48287edee1171db95e66fdfae79435846d93156508d2b1cd34ddcfc788bbb0894112564fb6567caab167d4f99bcc7c58dfb0f744a966760212f22c73df873042165f9c59d69212120d250ecf5ab45dfd634debfec9270800a009921cebcc900ae88fe11dbb0f072adbc34e02b408c41624e2b05ffbe91b1cd7b784a9e12a00d34350602c97d6f12a5078709d34c0092516400371d56d54938ff8208921b9a0f805a2d106a901f11880cee0fb0904242cfe8a281b0ecae01b7a0f564e77c0f0bcd7d27f21c15ca2f6a54737e9f5711d34ed2bd2ca9e5c9fcbad896f5ea6c6f0e8c0bc8c74bcceb6d19ec1259a5bd3e931c06fbf835afdbb865f79120fe38ad0a7687500f22e5066ebc51982e5468f80d9afdcc9c8d15110933ba465859bbcb00203f9f06ea6377eb73471f746fd58befa74dfcc0c86a361aa56f3d5808942b062d624eff791432b4a68a453d7dcd1d76c76e4616e3fd7144bf50f764d0cf990b9ca9d60ab5e3ba2476af33af73e5eda3b05c69df5628f4d06e0fffccaaff4c050e511841e5e4ba66890f31b7b4d650b15461e96d3a054cd8800e569fdd695503719e17b9e5c1405fa9478c0e594bf656fa5517a2e1315b499dd57ced95ee8d0a9f41ec2c66e7462613a93a0aa29cd136d89ebfa4e7b2fbb4bdb81c0b6b4ee30bd8bea453f78199758a8dae88be26a4d6056169c19f0b8a669c4c01a20ac5aa0809c03f9b77f4cbaba79b0b3bb267226ab457a98f3db49b5ddd76938e173045036ab73092f038ba4b8137ce824bb678eb566522f17a5b5906e56591cec091990267877d5d53d959c777548d2a324260e1b02a6544214bc0079088937967720405760e32111d911d6d74675bd5da8cfcf23971dc2f569bff43d9118f9229921d0b01f9371cf50e56699e72d4ffdf8bc0fde7b4cae29afe7e628894a2d1a83c160c4ef81f235b8055c2cd10171c86f14ab13d85a075586af8cf950554eaf102c403ca8e8b480087aa11efc9c07042c50b29a66069e574a38ccbe4196233ae7ee807c875e24b8f056e7b25bc7a16bb539ca72c8c9cbe9454e1ad0417417b0d40e109542e18e3cfc116ab87f05eba020e1218554d63e841feaa7cb2063beb9664dc0b366bef65a49456e91f554f627d51315df7fb8bcbb97000443e071ea82959140bd7b9d11d8cb9535a5afc47a08a87de27e18291a803326693a4f576dde707ca02b5ee92a30fd8cd837ff094060b75490f8848136453ac603a5508f9de19044101404a5f45fb167be250b9dbe50bb51217dfeabfb2f36fd402a9e9373fbac8d30c50eb415cc80e5b3ddda0861bb015afeee835a346a755d06fb0f83a043462650f549bcb0b353d421de2d171d9bff3dbfe67bad88fca379752a76d7687c987920e008aa8258703d05597dc0b0aee74d0e7f7e24552e62650383f794d422a92da0163f1b9586ee00f40d8ad8ca406d892b06cf4325fb2f8e1c319b4e1ac3ff43b0765c29d68efb289478a52694144a02fe0607bfc467af3d67dcfc7f3f3421be10bec530ae754f26939473c33f1cafba3964e6ab44be00212d0f2340f7fe3912b0bd8c348471ef8d6c046e4f372910f1c6f86f667901c9b7f6145852f392f2243074b74e1ea3922197654bfbb8f3e7ecd737c2a3c54966942874062f165a226a70ae64bc92d4079e5d2badfc83089ffc9768ff5f8e7d17fff1394edb4aadc9e9207d6521380e7a6503d3277c97ad1f287aeaf2a26fbef9a792f77adf052bc297e09 +generate_ring_signature 3f9d1e8c4afb644f791e59e8cd50f02ffc79029629d27d18462bbf999fa28537 6b176e3de6c8e0d8fabe2e2552415c02af6c8d2352d99c208af199dff42d0a89 1 0870ddc2441fb0194765bef22a548260bca7fe9f8480a961f108f601798dc115 8c1cf72b82661277cadbbedec6b11e35f7bf100ecee3cf1fa9dc46de1d9d1201 0 c1a128dad68dcfced3423cba8dac8cafb6c890e6bfee4595a5db341af268410257754821b4f4707e1eec8e99e072a5d4924aa408bd0b357c96d886448ec07404 +generate_ring_signature 21021e77c02b18cfd15c18a1a6a46fec6a5473f49fe7f17f408c9c5ad3bf9f77 b34901b76b34408675b429898c4c3adb74859e3577bc512353ce51e338bc1b80 7 bf6aeb0c9fdc617e09d5fb43f98f77c0e4db61b442411f17612d8f7d68030d8c e3134f86f35ff93536f816ee7c71a22658f0922dd6d977141fd3251117adad6d 0351df4a97e005521dfdae09511278b8789a7f095ef151c134fda3d71db05438 31ca8ba0c25b68cc7b0cfb6d37ceed81333f87469880e8a4dc431daf9b3213cb 12887b59f4b5af889620abf19a52a7bd1973e04d7b949ff683dd3da922457017 2aaba178d00eb926ecc67d750d6ce2f10402e59cacf46e1d60cd2bd2dacc7c06 ab66bb5959bfea90f0ab7f1d6c78b6e380a8c91ae5b77ee24357abfe128fc625 9e682a0ce957782de7ecaef9010f9bf77483a51ad18b513a8acf4ef2c7562600 1 b84e4b2a4b3c7620863d63acf8f2b62e6dc236b590bc8912b651a840d628d808a0983263e06be3a710fd7c6c2072657daf66861adb5c766c7f58169fb93649051b8712e084d52fec30a99117d5e8e0810548b58fdd947162b9493913b8856d0de65920ed19777857382ee3e463349f139c45a2b2b53510e379077e7a371c1b0566a9aadc5e070cdab52d60b41132087a2bc1892d8f5294515c595ec5306c68035378b35c912f0dddade9dfd15c93ce31aacfe54c61f8ca69c8441897ac46370b9dbe7e31917c7235b2b81fd805bb907677a52de7a1a10db66bd5b0fa822fb1073c5894288e20a392c5e3d0db38f295917f71b0d98af0786df5392169889ad104ac1bfdb0a87c41268fbb66995d7f6f0adc60f945e70283c2ea49b8eeef38b4051faa9c714b412dd76c2f7ea358d7637f1c295ec920e7630f705ba7f7373fe50b82ebfa730640a9f28b5136d14420304a62f19187cafaf131aecd7fd3bdf1eb0eb667b357b2e0f97a7bce57bb99975b44921c3aa43a23332a0babcd73e4365308cb3d66bab782c87ddab879ba13665afca42cdfc474956db57724faa7371cf90cabaf45e8f873564b4b606031bdc55980811dc78a2094bff14c28199403d57f06 +generate_ring_signature 953630fc2044cd653f7a0ba4d3413eb62f6e17867d2a24283d65c3ce6318f887 e901f99a3e172a3f7de853302c9ca5a1ef79f5762c13ae654c4259e8cee66e33 4 de453f88739ae0d8339d5176fee305db6c4558ba79cf29f7cd9d578ed6f93f73 2662a16a5721e8273db4eba161eed7f6c55372ce1b57ffab8347f94ea937ba77 9b838a770120cd96e91ea3753b6728aa50c8428a6bda2dd55ebb2ace9abefb72 7555929a6e983390a6d99b9fb21340bdea436104456f7345e6319e772f17fa82 47baf5064573771c80e48605222461043b10259e4b1d5ab00ee4c5109eaf0509 0 9e8b1456efb777fa6e495d19e01d157961c573f860c58bf690f59a28ac19b607be3169f6a38372d28f9855a9d92c5805908e18ed15b2510745c348159f262905148e003d9aa5839d2d4e17da5ea6a026eee750de02a7047b10154f30ed812407993dd3ae451da38187863c025e0d3a8f0ed98b20a1200c355174e76b46e2d908b7d480d1368262f147f71cbfd8f5e723a739677a316b6d9322cce582026b1c05d2a335ae99c77389ad718df8601f70ddde0e5d323aebfc709d27e6574193a00ebab62c02fee131511399a7c06ca2c630616aa1f626feb21a0eb98a9de5d471076fe3b331438fa57f4e0b84fbccc55b598874ef3e85bd11bdfdf6b15353442a09 +generate_ring_signature 389429172b85700ef011454e82461e30d9df22fd1db8366c615775683e19d8e0 999b6e454561a04399883e2349d36225d208c3426018c8a5140b5ed688601c1a 1 ae6e602587bb3d69854b8ddc46a38071e6987cd8dbd8e5317bc22ba20e568653 1202a2169995970dbfd88fd2d6bf35111e1a13e6b0f32b8bc93d3b0afd206804 0 3cf5eee0e7fef22f4ce8b9a84f366f10df2367d3f1d154aefe306b5e3706360854b704291e0010f786d30af6bd9c4832a008e9146448cee2300d91362d875103 +generate_ring_signature c37605f12deb3cc9723bfbba6a865ebf42b6f55ce8f3d6f097771ab2b354f3b0 fae39afd0699fbd2a2ab9403bcde544420b66224cba89e6ef4bada7ec9f7ddf2 3 7f973eb742664756b20e8dfe86e1e12a62ea6cce44e987474d3f0d5fd115da7e af79056ddd39d65eba37874cb91566c6ce0a60305380f8007535d2e7dfd7d8fe 720f9e325b8d682a17ec1e8c9d118535f2102e9e03b570f5cc74b50e8ee1d12c b57bfcf6db2519fcc43ca7650cf3248d8385e5cfa280d6d90dad4b2154e90b09 2 9b0756729ca85252b80699d68190879b0dcc3bedd62628032b5df0437b04ba0ce941f1d40b98d3c51508b6007946b6c74686241369caf65df71fc64c970cc503e9e09d2069a8cb439db5c3397e4dbbcd0ef7fe8093a2a8ab28fd8ad40412d3050ea154ae7995347014e8e2b7d44f5243092155ae20cc1b7079ef14b970d6ab0625cdf3a8d97f49ef6710bbdc01b5b198644501883e1f273854627193a5d1690eabb98183177abc826cd9b01cd7a58c47e1a33e40330359158635b2258132af04 +generate_ring_signature 37671a1d249e1788e7c6193b699c5e54f6be1ca35714a3a3e4bb5f45572c01d2 e0e86972cdf25612d13ee325998e27afe1f5ca20cc558021bfeee6b8437062d7 14 f41482621e6d7291ee1837f511206f285589e82270c54f3528e9188dc39cf6f2 ba41a5e2f314fc05a8f86861c2e3a05318556814c9d209e4363e363476591f6c da784f4125cab4638fa28b78a7e1040b68b0f4b782bc53403d9b311d1c8418ee 1c765f10d5c8315d648a1ffe3d3c81bfdfa6ffd0e32935b3f4e63f3f563afbc6 107c411025027e2d443aef21a7587dee87fcd8d0201a679dbfdaf4f2ab19a2a0 67d5eda8542bd0951035449c08bae062f28a7eae03acbd4a85c70e98321df6f0 90ea7de0f1638302ba6017957756f60b85de1f18d415b613c0c7c9d1fee2b020 714f410e3f73569ba9dbb4b8f973b1432cd573d33d24ef35ae7cd2f7ad385cee f2c0ee9553a23167d0e49f274abc2dd3976c1cfb91ec2254c8c88477f46ab46a fd5de7201cc934f74b307d5f5f63758e633e9d60fb9d3eaee47f2fd2856fe9d8 086925bfc05dcc018b7b0494d8743328f5dc9b7d6078c3db4f03e2d8be5e2aca 8fd012389212df1806ac761c9a70f1e358870177b639ba7431cf708279e1b77c ae9c72de597032207f452e71b0f4885a4ec933c9908068b41e9a1a5d08eddef6 668fd60e30f1bdeb678c538fb5d94a1836b1e038f20409c6608f857dd3c47bcf 1a128edd23b4090c4eb20ad7ed850a3bbfdbb4bf0ec91066d34d3b8d90433b00 4 23e5f4060305fe03b4d492d0373a2fedb9484be4184b84471b5ce2aba441570082d6a4c0113e3a5361666e2ec206b3ac148d7b6482bbc5e74fbae3841169eb06496f4b5f529d78fac4f1a59f575b5ba3fb14279f87d5a37f78d9278ef89a5a05792932728e7b18c071827b9559f5d235486ca241b493c0e565fac373dce9af00b70b72afca797e9eb1300ad9c8728f48045bfe249a1ee8d9ec772dc5e99a8000041605e39d17ea3b20b2844e0efaf113f5574092b35da1e338aacc48c5dd6105691990852e49b5052c83821156ee2f8cfbcdb30c7d95d2f2af56c47f67a96a028955e7051907435e74216e0a14770ebbef577155afa6c30d881df0ed04625204c61527a870f853582ddef3d3b0cafe64c83e9bc18687fdf21090402459b95004fd2924630e75e2f476df7bf10c9d7586543ff728c41c523a4d97641b7a9240052069adbed7a8ff2d8531f5a2139422794fb07ffd9c6bab272b322925a4148e08f3dc866d6c34296225cdc71cf328f04119c468e73a9be25d6fe611635ad62e0719b9ab36102bd1f17d3e70f80ba7098e146d80a510b576f5a0c1c7e21c6f38046543f6d0aa261413de0199f12ea05bf03f0412395e91c63a48485845e362360fa5d41f56e746084c8ef2c6e6631c2f938760d42c18bfe8ce2b830701e1a3ca09a8493e9a7dc9ac1db6e2c0bb8ad6bb5ea0c95faa2dd81e563a28bab999fad70735c14ae16d5e117e7e31e0eda7139dc3f3a5b75f78cfe2a898ff148b67a99c00740982d7425b45b2e572fa458dc5c965c5f3551b3a89ecb2b281faaaea6d320c0e9333342bae47309030877b42c6ebd3f798289ad26560dc2348637122630e0e39797c36c61d362444e3001ea8c214d6e343b15b0b071e2445dd8374adc6790b7c6a36fc24eab5b7e25fe33899af72649328d30e302e7c79969fa4bedaf0c404c680e37dbc3b2060459ba126c845ba1b0a14d3185a06875a596b737de3f0ac08f20677c99968b8630258e19c531aec38818cdc490e1790abd76eed78194fdb078d4e3327576cef04c1e2ebd2e8ccff0669e305da2d76387de39f0886590cfe02aeb9004ca8702de77a68b87327cb5baccbb773350617b87c853baef120a41e0bc666e3c3b50e914195085ccda3214dad2b79e2a855191c6c2eb8e50713a108092ce90c7fac40636e542c1957e97ecd5d83a5db1c56d4c6b09dd1d0e880c97e0fcfa64a73c3394fffb771ffac7b429ee67b97d81d041975f9ae0d925800a5370d +generate_ring_signature abd653f7ad7fd63df48ac6a7e0192cd459c65a8240f9d2cea3676a8bf574d3a3 4ec5408a2203919766e99425df38cf93d465ee4e7179d4140f743b780950f7ff 127 f7cc7ad1d5529686cb49d9a39db137ad1c4c480bad36c87e9874db399df6e2cc e5603615a39caf26768f8f006d8e7fbf75559d8f20f1d668603021cbf5b06ede 35c398f97a395727bb667e2d1b2e4ee561ade6cdce28decb955ea40e3dc81b19 39f7d4ce324bfa1d831a3ac2a26784f4d9858a1a7ee913f0d753560f0fa205c7 3a70187619d98252083118eeb6c43d9f23a441d081c06da5cf1538ae2f514b46 324c0c07fbbec5cf493db7a55f4f2944a88d66fa29495c17d2ddd7beb799a981 8246ceac0bfbc5c3bf76bf1e188c6edfcbe7e8a8b92c0930a0e9f6d7d9bc4efd 46510f6fe33f0246f667ecfac5e9d01e62f9a675b3c9b43267f7ac6e9e055eaa f0531b6c747fa76dba4a033571b5adc2affec1d8ee7c3172aa25a35643118ac6 c3080d681c17a1a9294552e508fbc3a0183a17a1e2708fe32552580a44ea4cd8 dc56dfdce49cdb9221d288b78779a6bd84c4cf8e3c5d7dffb0c5ad507715f174 24b46cc382e88800f74d1eb5cd8f81cf60002b5ee67fdb2f7fe69aa3f921bfe5 28672e60cb393cd812809e8092b639c73e81cc2e0f8867bb2f136cde06c4876f e1ade5706d9f09a98f177a310cd7b8cda1bb72141283205abd27a1db228d8778 be543df678f6169184ce2ad48e7830679ccccfc52a63f8feab3d5ee79f0c1054 b464788b8d9c73bfc942547d9a2726e2c63106f18e322f9b690efc813c68afb5 4fa40c9e5d07a7143203d40e401198a589eb8751b4dda6bc8cd1681586150590 ae63e7c95e21ee618a4bf5f51a9bb1349a56e13045c1ffa54d51fa732dc88a98 788277e0c0c724a631417c4caa078aadd6dd7ccce6a2dd3b6126490f9cb29b03 0e7f64e648f936b92ba48d50f642d4c59bb972837a8d0c698ebd3f6e8d834756 8c9b5957c1c93c492cf3ec80493c9a2ca5c8a296b47c45a6ece5a9c1829249a0 8fba8d91bec11803d4c5762227b2a87248ba4f468c2ab1b4b99d37f605b6c942 25d297ff24cce2c8763580a2cac0775cd77a4625f877b6ac39bf8b8ca06eb121 983429fa4e9308edf662f138a32bb5b5d667fbad795482bba974751c1cddb922 a4bf3f834a9f55d2667a98958618e667207557636d212af92a5f6334c812f8a7 f35b326077681c3290f31fe27fc154716e58b7c9913a77cced12a56d1c492a9f 323c052da3cc1228ee8c14a16ad1c955df440e58cb9811673f49acadcd111954 f01661bdeab726471018f51d247c872624fae8e5cbf34071c49a269957756e0c b67dcde3c5613b2b3b599264f06050fc144b0beae6e234830548b6af5a4ccdf8 38af053d20c292fa28d084721700d138b911430c73f397ad67cb878e115b2beb 27c40d8f771f20559e775713e71ae041abe5e1814e6631ef3eee841539e2d262 7a3a321751c2b1e32dd96af7d33d60184d8d42aedcede75d4e22c9572771d6c0 166a8a31964185aab43c20927b122f4457005786e3d8594cc5512ff88f6778fc 37d33c370f64d5c21fcd80739846a39fad9eba9803afd1e3c3db6c933f600845 68ec83d3d23e6054492ea161ac9fffe1a42ca54f28038bb1c42d3b7e29fd333d 5384f0857de1ed5fa5db5755828eb4e117465dde04a15a90215612782e29f4c6 f93dfef70ec9bced9b7830d6dbce44c1aa4eb671d5ebb3b815d654224db90f1c e386cfd691fd2c855bf8a5c86ccf228af6d9481e38acf5cfbaa9d8accdb6f740 9380536decd8199c51bb9cfeccae5b75d423f18e1de0651bf9419cb9b484084b 4689cb44017f37ef8ef63fe68d4ad26018e7beaf3486bdab9c6c3c459c3e7835 2baa6a00de237bf2bb2fcd3d106601e57f33c27a1214ebb317f53f20052bcd73 c874b88ba9c5e2ce74996d132be4879f2a589b21efc30314e80d0cfade1958bd f1485b76e02c7e9b402c577182a4a941fd4c60618c6f92f7ece4f1f03caf723c fbd10d5ffeacac15e16cefc049ae124ee6dec10c66b6f5b62bf21d8aa41acad2 f48ca5d7ac83ea3e8d9adde2745fd6f50fa7639824db2b311bab61adbcce559d 0d676c1b31a9de877928419723a5eba5d37b2378d2b3d9e846b6cdc865cf6f6e b45daf0605d323651751912084602a122b3a39611bff1cdc36fd74d882e17201 0ef240390d83a22f5e7b9685545e94434d7f667141e55e9344d50753986d4a2d 138d36e66f4cb547e3d2e2e32856a10f04877cc45a4d94d2853c7ccb9c29a05d 3c616b6f7303de221b0eab85a33ce48ee0fb91b125b69c2bc13bde24316fb146 c808d1403d310e1128b18554da2a8dff773e7711104495ae2d4c4cef80c4adef f00a9429229a6c135aeee20efa63cfadc5cb264f965d5bb99c2622283688953c 9f763318111c6e949abe452fc29319e2c0cd855ef13973355a458a3b7abfa5cd addc8266481e686f0120ac9537d03348a12823ba9ad2064c8fd3c056af87d9c2 9e3163d748d6fea21146280df9c2b01c6b46300a40d5d313749982b94caf43b6 09daab1f1bcb2f17d13c0ce3024631c834b845ffc29656cac32decbdceb36dbf 966e9351376dd899b4b8dbcae80f03764a7f67ee0d1c883ef1ecdf62af6a4c70 526b72a160bda748d06fcba2acc635f2370c642defd1c19280ce29111912adad aa37714f944dc1ebb4a5bff29adde7ba2b3bcf1298930cce423ec8bb71b46301 7cb9588e9e2a967a8a5a1486864106a667a277e28f65db460b3086772f06a97e 94fb2fa586d2e0850db9c6d30b4cdabc2f2f12f59bd1c9536fab74d9766f134c 1c466c9515e20c18a56bfdd03bb7478c965a7d12d702d7d4957a63a560d10266 609ef0ebd972a504c818929e4b680aa2d3af312353eb0f8c886fce2c2ca18434 102793b591bdf4a26d4277b57abb796e5066edfc167aea07ffa014cb58960e75 4a94b3f8577cbcaec6b0dbee10ff8f66320707497543846b19aec8f922b4293b 6697ad22d8ce84fdb5fc90cc5af87d5d2d962de46cb53ca843e9938681484d68 18457de093c4a00394f250ae8db6cf086e33f6d5d14dcb3040efb86b3a3f0b04 aa9742d9efbda738d62ab7625bbabcf258ce112757d0278d160b15936abf3cf1 76bc78c14142bd24d386b391e510a448084640fee6d5c89e78912563a30c6082 5a31d2c3cee04912b6bdee1af63d791bbbb9895c97d40f64268d429c29142985 575e2c99943ee27d8962991068c135d934f8d7189ec84114dec86da9f8c86d7b 653841686d1dd0025aa32fc36e2ec002f602d6063433722d32b438d5e2d75319 522bc0507407007529b25d2eec729a59c0f60e98373a0f539aef20df7f53ea69 67feb74336641ee284e032ecba89e02428d2829af28d12b0e5bd9376a60be00f 3c865f1ca2bab0db4eff5eb9e4108ac90e1b33362d43910ed423cc670035f039 d6e4c9d3d9d3d99471e5973327d308fe8faf93727fabfbb0f748c4fc9fba82d6 d70a8e2810c33e4f5ce14666e6282b71db8ba4b4f25984761a61bf21a80098e2 20202ef0bd6bf3faa3dd42b314b620e15fa03cf525b7fafbce8b1f620c86bfba 8576732069b9c355cf922f86b90041baf4d4d44f79055afdb35a958f8ad9fcc8 5844694e707777f2f9499d4bdf209fafaf286fde9040b51657217add3e345bd9 ebbe61e0c6be3cee2b8f2825806423b8368a7f649a0c842fe537873674061d8a eb676ad1281e502de98d3e9c4732a095ad28db45c164ce7893547fe6d776cbb3 ee6ae2f3a4e2c55c347644afd43a8e3ba646259a4939ca5d0acba778d05249bb 377de8f232e2702372e38e93858ae04ee8729ff65a76fefd8b961e64c4eba497 a0ed9e2d5ff4c1c414edb066792f9026692e49855bd6f0c3d69a1ac2413feb9e 1f6c215399aec9b4d8bf289d372c2467f560896aac9b941d3e3c0646c4611f79 a98c0268b55e338c26e4494a4c8a8022d9653647103a0f1bcf91dcd8b3ccffe2 7ee898c856b5fdcbf8003dee310084f8e5d7553febd3d4d9a4dae9728ce7d8f6 e549734c11a2460d6a99322b248dc590074d36a74635666c0167f4d6455a9b10 801df1a8e2aa0edf9d00861bd977b0e4f5d369c182765852539e4eb98d58d9d1 6b17009bc4da1114f1c40a128a5f9238a5ff65e43b0e3ea6be474245c63be927 86a0be9a8e802b0a83eb58fb288e8eb0169085599fabe73df43d98dd538e4fe3 b52cc90b3082ea8bb1c16f4398fa9852f1d7bb3984701dcc59403ed0ea11756c 2e093e60b05a8dffaab61ee017987832247f2980afac47bdf83cf4d4c9b09fdb 23ccba870003d977788fb87907888460551f208dec112af68b493e339324fba9 4336845a60783d068c0693181b155ff8a927e2ade96175842294c5269bba5fc0 885cd6d8605166246c5ab4add221064ac3307ad12cf0d76961527722baff5b45 3640a825842e15ab7fdbd183861ccde121bd494d55dec192307f6e26d7257d42 51e8532b10c76386fa77df1f0972770a67b83d877946387075f159f58814e449 775cba5f1da6c8d6ec21a32821189f46c4acbf8248b236b1e14ae4550607ae54 6a4853aff074835c48ac5fc3288f185e2ec3888ac41ce5fdc6d7b50abfad9f66 73789c6ec8839b4003a2045a47c9602f75a55fd2632f4c1e9361f939178ec98b a548db9423100cba74cacb67611f617ba18b0b65d29fd55ccab9f81a0613f653 e89c5116940301a8cbab6c88056e479ef6ab6605b0ca9cbdffda08dfafeb697e 77ca3397a46092795d90efa06d1bd62da05986b7fb733ef666fd387e39dccb91 b16e1b9bce92e7e72834af0964871ff655d94223f297e2a916bc054b0c4f8a73 b53dc02837033e165910df5ba952c0ba9c3afeef594666dc0994bc8cef844cef 16e983fa02cf9303daa709d001566d3683db349cbca4ecc9677f7da6a6a873f9 dd6a59c284336c7248af37f73cd62638b4a1126028f3088f7809093bb9f40c43 ccebd0ec548dfe03c8576949bff066db53c08d2ea96fb7bc3eaa2e35bc4e549b bad4168d208766a192af7fbeb089e80fcf3ba50783063be866b2afe7f2bb392e d45a233a18f241b0ebc2e27342eb7d059257a766409c5471a725cb13c4331ae4 99dfd92c18d6d113ade3042702cbbd6bb9a79c0c7aa934fcba3382c31dbd7e7e 62ff7d2b14a161f835ba981bf2d97470a2df1438e2c1252441555a898e1e0aa7 ae772f8ce1d252f53aebb7307c17c4e0e7d62f419acc3a5ef596ac80f5f95d49 95abfd3722194601a8532044be8d20e29d027d0e2f4055bfcb51c000439dbfe8 3ea674df981be77af3101ee05efbada58aa84c552b85528c9e96137b0b317f9a fce584e406a2166a6e4bc7576e45ef838e4ce27d2065a45a19320a7baf163f01 0def1fecee4b5330a6488df6f248df22ba92af84bea002746a0b647e50941c07 d692409668548677cd700cda6f35836b205eec4de848518b6a883dd1270839ae 7e1d912ebaaa566002057b1a6a174539ea9029cbd86342a8aa3ffc748a36da7b c281754f3176f07b395486b21e69fe80cbd853438f37a25af70b8becfab39fc5 d0accbde0cfb2474ea79917c09a5c6948393d625f29affecf51bdc3578e93e67 0a299a649e18631ec310b45ee09d737eda2cd32e6ba100ed3bf0984ff88b18f5 e27bee21a8a3745f69a8bb71b60907892f43bdb270af1d4e682a28112c80e4c1 d8f5963afb20f917aaad9e0d0f329df6daa524c0c4d80627c537eaeb7c6eb783 0828b7115e8631dcaf475cebb907e6aac977d9938b7739617e480846e8b0833f 4c50e16ebc84e90f0d414526b053d3db85e9c0f1273217537805d9d7fc03d001 82 d0cabb9447631c59067c5656d09ee3badc392c06963f817317a84132048d1100f833db1780685eb559d8b88e804e597d9d3f178eb46f7ea3da4980798291560fdcda32f5070f1f3462d1fba3b8662955ffc6b1249912a84519ed6993d1d5920a459c587bb6be73a73dabf95e3cb87f608ce8ff0d26f62aa02f8469c284f7af0bf15dab84d8eed10d1152f500cf24a358a7c52f703f078cd5fe9fba6a328a3b0988ab8d821c5c0538d14224a6829c88d43194254f8447ec922ade8aaedac07e03939440b9d880f5b78bd216df10dffdfb4562378fa48ae2a9309bd887b0cb1d03f995828e5eba9fe7e2db66da3322b19f035b2f4c79f995523d5c6dadda9ba20ba1eabce9b12cd26c80df1e7724c96371655dd0c5772a0a652e762ba79407290dab34b4331aba23d95dc8b61eebc132eddca71e16f9241b6435906b97e4cf54084977bd6e420fcd253edf6cb11ec2e7aa189b3d02163fd94c3ee64552f6c9230dd16639ef54a3f205ef719abd460f6d4d6667eaae7edb6e87d717977eeb950e04a3eae2c2fe832d539ec3ae4de06cc8bfaf2054597f56623e466190d0fb930b042bf2dff9d73d31b3b8b2d6a713495248597fd4362245d645c3248dee19a5970bc0dba8a070cab0a0b478fe2053c4faa28bf295baa80688954250a2907f8db20d218ef14fb0035fa4f663cbac04d8b78d314bb0d6ab1b3e7a53a7f760048f0c069069188e313020720bbd60614a5d68a2d0805c64d73d3762e44ae6a7b83f79036e1025915823feaaff5696db8cab673ca6ecd4707386202f2d1cb68016b0650517f7cb4fcab6f58bea3c53ee72d245bc6e27075aa2027f020f976f73f76688081ea64d9ff6b730503800af08ceb57529b0ea04e07e994a81d5a4b9e51480a10e08326a66df552b572401e2b2f441a67f71e1fd0771d204981d619351ca0e1e0c0d41636e99f7e3f3007c8fe8b75c0b84e2756b2b86f7785ce81edd3a7c75c507f46b3a6fb8b9c993225254bf4f848f71121baff7ee788e808de4e9222282000cd2dbce4ec700c4b572ede4794dc4ac19dbb0181c698134cb06acc69c0f6abd073b9a139e444cb973a7d0b1954fa10286c28c9b7847d831573c2c247be3ebc9064ac329185935cbf456a567bc6752c8a86e5d89ecc2e82e4913ea4c73d4fe9005b65db02ce67ae8e2080362c4e41bed6228e9fc0ce4095c3290650a053ecc9c0404ff3d1799b999470aa61fd44d06a60c34c08ea4705afcd7d5b86bda1cfa57070ab4c723ff193653c5bee763d016b952bac17ab62062bd60bfcf024f5ff92806eab845e34c83b374a37d962442784bb061defca3f86bb92d3f89fae544ea6f076f15fa92be0903d1dbef149574c9976297e5f0db8c67a2b380c4b25573f7b30bc472f90b8deb89846be7e4458356c5c65a97008f34bbbbb7101ac47aa75b9f089755a98602a5bc148cbc882783f4a9c72c5ca4a9e6209585898497b1f7488200bfea01fa829fb56834b0e0944e4d3491c56948447e038efb74b9530f73205a0464ff8ce80092e9a4edd49f578e697b14f38b675e829ad1ed5312887c5d644707a8d5f8784c1b59a6239c6d155f12b82d08dc79c14b1cbb83476ca859365de00e9553c678d595507f2aaefb206e8950eac162d40e342c07c20a2b3a3e272a720fb82d333c3e66aba6411df3a35f3d1f7e2c2181901aaf53872aec11b4d2d678083859e17b45758983b2d3f6d2c143c3f5c40250428a3d5610d73471884a309607a5ce1ba1db3ae9a61ee61b992e594b66277f7172dff0a517c96e245c2156ef04f75b51697dc2d3d55c017906ba365db248774833538edb58a3a9031de8b2150b2cb1f8f9c8aec9e7514e0bb38fd7e7dc5e429f4d10d3a8591b8d4200a813e107f08aea7cb9539c59d03ece09abf6997de541c4e81d0ccf68e3058d5849fdd6038a6e0af5d7f8e5487997b62b5c4298ef4c1e7a99c8acc37ae898074d0a6137049cf07a1aa9004bc2600b5a239d79048b835f86cd51906a95a4e0a336ae5a330fe026d96b15011a4b03a514c66687aeaa0c13fe27e3b21ea3d04bf98bbd088502f47326e436baac4a794f1ac63d8657ed66de24c852d6e7db6f548ad4e1b8bf0f2e29604974b48be27fe24034f8609edc3a481d3b298b4bc5dd67e5493258a80a667e230413426e0d82f6a29d240977fd49b506603bb766643f5a23791a57c3093b5aad541b63860cae683927e6c642afa861403d106e55fc1d97f5ca8f80590d6da2cd340900d6ea679aded9df38c442a19501f24a4ffc4ebcb8cad7cbf4760bdb4b6a1ec37f60ead4963aa29cf94a8a817df929d39b161454025db9ddddd70362a33d3733aa874f0daeb710796b67d898064da0a20e8ba0f69e21fa376f220a6462b340c0e40ddc53357245ec302f85822f65d839ab7e3737876f2a488d5b0ca65fd2be689044f32ec0376f14b1a8e991ba8691534f6ef03708670033beb00e1c8cb1df6caba8886e7c616352bfd4dec0f7736dd60a7c9572e1d9a75a2b210029bbd260ceb701b099acfde721285fb12c0264873f43d6d22bab3e3acfb86706d51db3b945b88cc2bdbe7f79322abf30242e551c588d53090fca816f2cc84a05149ead4160ddbc28ff63ba1db4e1379b534653528045fa03c329bf0497f1210715a37e457b737a053bb4bbf79b8467c4f08d1a4b710f26c450db42d2ea5cff03647c8e59d58e92ee4a069ac47b2a2b417a345dffd439e317f258219eab3f4b08ba431e34c9d7a500d188d827eff15effb5adb21d393539dbe558b3917bb30b082722fdc84249d1450bee8feed23b350448c9c81c3a55b096c165468f3edfca00d52c67cf526ecf33935770cb960cef064b9fa5daadfeecd2071a3bb944af540b776b93777f1511db5bae6777180f2c950ab7156cc5dc1801b4384ab57781090997fdc8da59c33248fb118f6f0ce73b9f0d704a037ad7f461a0f41a55174f0f02d61bb1a125c6aef7960495b75016937711df3f3cc0988638c1244f256b307104283ef6e1fb0ae4f410a2c008cbd11f7c83bc605f1a944b7d55732fcc175e5f07f61e47a48f7ba75f56af869ec4c35ee8c6221388a6fe8cf903a7f82226730108faf674db9c9933fb7f472401e2b0710516c42cf225b4b92dc9179b5ccde34f0ba8eb0100076d1f0e4985eb3899cdec838d2b77b211077754a829ba5b00bab306b2533aa2cd9161453ca0ebd2c3b01c8e84699d3202c5e366c6e00af61f9b11089a14ef51b95e8f69191b66ede5cf9761928041a325e5b005165947c4c03bae068343da0b8d6787cbb76a210c6993270be23f9bda32420df5ce92ed71e1ca5907ba11d83d6f78a4cf82ba4bba67f680fbfa8f21d3d21542c1a64d096133b50a0263c13e3c9a0089de524433171382181a2382bab9b843c4b6bfd90fecf2558e00051658a22508c135be89e332ff4e9501f859e83cd96332e9244fa4a03a0f8e03d798975f109c75a588b5d17bc9c95e19dc09f94d922a931af2ccdec06cd21c0b2739d739bbe1db39e2da9d6ad72042eb236ad1b9d6c9cb7331e15cd81b728600a0511670c8f1d0f9498e36146a8bfad5bd565ded10c2e4350fca85e19807750ec7433f0862b35e7136183e5cadd1bb3789fa9c45c62f4841c7cf6cf8ac1b5a0cb3216cea185191b2697e15c38c11816ecc530b2cbb067d70588c2a1a469b460865fa32d45b876588e90d397557a83a30ab657b6e7e97478fca81e3175414e20d3f223d3df672806509b676d369bf8095221d2e83cf05852887b8562f27e85600a52534ece6ffec518585369e603468a0bf0e5267291dd2d2f8e1e568aebe2a0c9dcc608708066c80e39112a5f2a8898f8adc590d9063a851d1be1f1771d5ae01aa61fb93ef39d9deb337c570a3626a2f3f4931365376a56743a22ddd35f47d016e12bed89505a3db2396c45e23cd1dfb7adaccfa1a25d9d6afec6a6d1c10df09ea56357ee470be6a4f5126c638db97c4f2a389ddaf883ec020c619562f003b0efeef365236c9ff90c259d2cc29570bf31afc3d39d680a30dc2551f6ceec9d607a973ed9b8c888a43e77edd806ae982cefa1cdea0a0308946627767595ced46001791e317ef3f5f7c2de8f8233bad1bf1532abc1b27d75d27a8023f8b85305203c6ffc6c0beb7c107c45e032ecd135fb9304d7ba9fc145225cbd26bfcd33c5602e86d8a55948bdba71338d5e2891504376c4993f5c428666b77546b0ee6804b08d2de92c2f5cfea0a0be135979305bfd753b840f201b3d4c657571c787b61680910e7e79063b81d6e23261b62694492ba077eff11b21e24d36b12e979d907880b668547f23450358144c02310a16c7ba2cc3d5ae40f21ed1afe6ec48412bf8d00f8d047ab9e959cb708fa5732e59d826c2769c8d39115acb794499bd720094703ddcdf0b0085a0715c8678df6e17ae5d335df411640de5cd830d7fb83fd8b44092121275dc2aed3e52576953e672bc6e8008408c881b61205d29ccc377431900a863ce783e08854cb296570dbcd915c83278a5c06614f447d56df8e934b5e5a09b1fbafe326ac97e45fe2cfecffab74f64d7ae75e31f750f4470b80dfbcedc703af0ed97a656689ad2253af666e385c04e2254184e294e93fc55459e0c8cfde0faa7bef377453960b8bd144e4a67143aafeb93f3895314bc6eec354665d74c30b536717010ed0e006d614d7ea4f66a72872cd91cf5cbe4f96f4005890ce513c0d342935d01316c7c203e3917cf7a0ad1bf1750ff37f146a94c5015712dc08470b8266473b5ff202a9d7b4f317381686d2d58af6f7c8693cd5b517eda879603105c307c62e00a6047378319c5cc013cfde28950f8b7804e126ae71801fee0a5c00004e7ff8f81b803bfd6f47578e1125ad2db84493095f34ed602297991e3ff207bd54419cd577cb09ae5b6316e25ac1c2cfd4f89a3af6e7ccd2a6f91a187d2d0cd6ccf6e0e98928855bc6cf1e0c78abe76a64d9f300a78b06748a9a79d2832808f8826881c1f4de1c981acacce079bce2425207961420e88787108ceb2254a10db33f7ed9abd936ec22b5fa09103c20864c9645c48f459499e2b8de99fec29407c858c429b2495de220fe24113ad3f1979fb6f15cba254c6bbb452a6b6638f90f8f388018205042d7c26a1385115b6d97305e51649f88415ffefda5e9801b7c0b280127828fc62f771da09670078bd53b7a1c0f7ab483c72dc881c43c59c20b0bf443f077861ed6cd8169ee62b95b0ab5d8fcba335a2e738de0202d7bcfcb8f0f665a25bf643d59c84c16b6de2ab3158a64a56e607a9419936b8bdb316937030576c90b147ba80251ff45acd9362deef2347a46e30d16740b7d6f44263c1c1c0b6540e2983ac304aa152a28690dcf573107989b3c7e72bb0dce9d9ca747ff1c07b98c8caba334ac334c77ac11a35ac3dd042f4a682697b7e21a461fef0f584004b8b77c4f84fe8901f7ef7b1b469faa05e1850c3de75c389561b70e03ae8b25087d60095c59766172690ab5984edc8d09aa26858b9a7515eb67f12c75d6071b061182c4a3db65334ea499f5c0314f794fab84a4c9ba692745fcb93726666702070975c3fbd68abf7d829e8f011821090233e298fbfddc3771c4a32e7789d1870cae55bb6725c3fe23ac65a3021590e3c873db6eb499b13a4a5b9e1ec6c986e70e48008b04e7fe7604aca5ad73680b55515f32935b2a9af9dd3e4929587f65df0de6223ceab75eaa447f1a5a710b6acdfdd84f85ed4555035471600b9e4ec5e404861b299295881bbc77cc931fe0433f5b7f3bf59c247e63968820e1e9f601360a6b032bc837e53a6f362f0de6fb58a2631e3a5747ec8dad4e263b5e8349f2c50019158309cd5f64239982f6b70167bc5091919879289bd552afffaaa14f43030fb924ee73fa988566e6c47dc603e1a6e88ccf2a0a80ed55302b6bdf94a605fe0f32f7ed12028f5de76c9ca6dcbde595222e594c6b93c8c8be341d6c49f513480ffba7e0ac8ed96ff53aa6276702b9709dedcf24f4c2540a5efa8f5125012a1d0361c0f92b7e14f49f7fd3011a5ca3d1be0e9e03be1ff94da43268ed12af4cf906a836a2bc495bb653ac4dbc8c11e79ad34a9ef66126536f38b7ff40b6d725140ae85396b4b26fb72f19b30e805c89f0094898878fc48c926e71c13e204321600759fc9546bf3eb84ccc62e20c1949aa5ee092c1897ec237ad1027ec2e576a9700693301c0b8d4e9e831bc050d1ee8f415c0ce32a24fff5ef50b79c6060f4a2e07785956c6189a19763bcbecacb60026d3776af94fb88d76186d569f6011bea60d932c0b09186301d34c3a39e1ed256bd7ae5c05b3c13a42773c3f37eea372a40e8f189c5aded55276bf1006cc0d0a37257056619f92995f24ac70f6e1d7c4a304c9cca712ed6ec96bae5a17b3a3dbf089dfdca2dac6e23b0b5da1230374c5a10ede5032e7de45ace504e48811673f485007470633b160ffcc0de92a5268ce4904446ba2fd351bf5ecded525b8de01e002382ca32200224e47f2997cc9de7e9b0721fab6ab692e58fe49bac57b8951420774dc85baa0f214fe681d5c6e34d8b6063a64d70dc6bf4245bcfee926c5cf78d3defd7f8cf6e6e3941bdd4cf220f27202cf18f1bbf87f6f21abb7daff15222df52b62856e7f7b2baa284687a2dbf62f04fa376b5d93a0663854dc3390b505001368a54755ed4a3cc91f2564bcc1d63207651def03eb6a2b6fb686a1ac11c6ea08e8f02383b21b0bea3c57051f2246c50de206142f69ad7a051b23aff871904a74f971bb016d482a99c30ac8889d0768010d6d9e94a1ff0eacb229ddcf9ef67b5f54c9c04b3af634e1ea46831afec0e001fee3fd46cc10d02225d04d26b34d9599d74298b272aaa1abbd8a1e338935ea0f1cffb6518a9e7549762f5c5b4addc6576a77302c7ca826d49b517b1947d2cc09e2a82a758d7acc912d9de4795c2bd51b988648d7b11b6a1a221a2b70b412120de8ed32cb44db2c43c6016b3767acf4dc57cc27d10aeeda01a2669bb69c9671078be05f0081a399727a500508717896c24b7d92416c673d24f30336a4841ca60adc8e12c888064c120c1f5f8edee0932374ae62a8e0634188bea1267580746106c715d88b86401ba01b5818c63e52107e24a1239effd48a3afae452b050322e0a30dd158b7d1617e54b9c4f239eaa722f670f1233ecec04d5c223af3417966f03e107ead4bc0dfd6a931a6eb99c2d5d6cb7d9f3797902b06e50049da9e31b3f02963cde5e1d06885c6da542ba65f332e09708a8e3bc2ebd5571e9cc45f3ff4c055a41ecd74a6fb856049637c6ea6b50b7272cb023d805f179296b642c9b22450a285218c637538eda7c62bce016474b6ddde8f3f5a2cbab94dc99ce982d47240d34ae96df2bb80a4501514df26fc711f87bd43136896087f8f70153fa75478308dcd77d7b5fd65fdd1980e4fdb1b162af9e5319ab3a7359c436136d89274d68022406137fbef3aa02a430201edf4315dfd91ee7f768bbd0ea66e8f35c04d816019555159ad25039f0eab12b2a244b9a496f689426d224c33ea1ffb57b3564a20739920a5d7cf66ac54d47c45877c86896dd96d8b530a9edbd7035c1722069010cff98fedfbe615ab12dfdc89b954d3b9bf0bf8d3fd7bc9df1f7c1dd36887715081f1a5d6e105ecb8804395cd65b4134b28b2b7d8116118fc8c05b95581a0a4b0ecf90224a639824bddac80460d55ebf8e62917feec850d9c5fed88371708c090ec874c63a31d958df041b9ca37f6bd128dd7480bd26bb33e9d97ab028ba736d03cf471e66634bae963a8c34fe471e8259fb17e6965ff2ebbb279c40a55cf0720b78ea31ec64cb4e9a3242652b016eb308a34ab9b779fd9a3862221698de03380f6cf16e297fb217dea0cb6ac8eb8f22ef55951bbab237407f84c748c1ec6b750ec708a2f5505b8f48f1de0625fd58d4498620345bca8952d3e24067eda38cef0fbf70315d79da519acd58cfcdd0b29ac016bf744bc86b9967c6ffbdf7cf14b206516130088b246c0caf2f4592dddff59800e121e32338839f50d0ab708dd01600366adbdc79a39969f231bae6e76d9a6dde4bb01517d1ee41e0c01efb4b3ad0019354130b9cac0b34dcfd2078af5b1ee29e0f7a04162835cc88ee3631fb170c0c77eaf9213eccda45c0ed9f486369d40a989052ad711708d1935388d99212af01c452c1dbe2b3043e8c32a82f868cb4a2169847ef3cdf231c9e5029776e2cb00f98dd11063242e7c3383454acdeabe2f22b824c908cf8d5f3e62070d696390a0c417e7964cbffa03657bb37e42ffcc64d5a0b42ecaf103af00d3df1a33fab10070d9bf58a728c05e274264966234c25a33a876a87b02075a1c7ab00620e6110030c7231c34312960938d9526b3c949ee0534082d332cd459ee4649a1fbdaffa07e2719e544af834b1eec3eb1c64fd29d4e1c49c9e80910344c9f5f342075b2308efa5a3d2e7da8644a1637bc872d2f5b8abc5deda5f52cfbcdd13d1a5b2f87d05e3926b6344069ab238da7a62638abed9cbcbe7751e15099459ca9c874468120d7acde5ff0a32ab0bc631a11a11820f8c23009b6c3d123493cc37d2a111a1620d3197fc09d0bfaf39b50937eee037703d9de092c0724eb6ea761566893317100a6900ec0eec169462e681798c160f3097d94216b0bb8ba238b112e2593499d70de840aa37c673b2e1fdf7b9e654dff07a556ab53b26201ff2eefcb46dd20f040a395fbf2e10c26be9721b08e67b7757827a0a67b889dbd0960decd7227458120847953fdf2bcc35215d3af046bb92142ec4dea4d008d824926180608f8e281000d02722f6a884f947267d2c896c437fae2f92ac73f2af64d900c5b63d9102f50f7aa5cb7974f26f3ebdfeb28fb7792a2ef9f1ea313c5f3f81222a49bd1da0840a0236391a96c281d360e866a5c72e477bebe911c2a06ac338e1c57d4b2aec8e02b8e0b2e8610ba747e7495c3540d2ea35d673bcbe707fa87df23b016d58b0eb0042ea961ed2d75c79d35083b9567e358ef5c069942456282b1476cc4a4cab660b4eed05dcd63f6e6149f290e830d1197e621ac604830d5ee81c281f443d6b7f0db8b64e870f915ef180522381195de0ed5a26f259592dc693f5a835e6f5899505b73af986f032b559932dda202a583b388fbd4e140485ba385e43b755d7a8350508adf1317b6f16ac0779e095bb4fe4397abb1f0e68ade8e37954272a57910e036488be0f17434b1aeb76473d0fa69f4c7b524db6e1943290d57b935ce3eae405bece6e6f469e8d09e843054a94ed848fb34d5956bbf721baa011f91fb4c66207e2c76d7ffc59104022d43069035679855419a7a7984e397edd38289ed536f50c3ff95bc7305bca5e56967136af04d92f11dd9eae0b3b27f76f719f71309b3f00762d9b3ff2bf30db3e455044cd4d7d570ee35872411a40a7939556f965bc030704cb9b6e07e00246febe622536f6a5a5f0794b292a8b80ebdcf95ebf52e00a07a09ebc9db5847e97ee4a993d5f505f90b659a9d0203470d240771bbf2ef9530d27d57830d62e55cfc7d31e9884e915c5b7cfde2b78c0bbbb50ca4401ac6e790878d3296e91825409f2aef37bfc3dd4b74ce5f2784ab829ff2bd8170e110cb10a93c206549033f45f6a06c7df431d4c4c7e92bed0886ef8a9bee5134ea9a68804317f612e0da0f23075b53d3310000648d33dda6aa770153b0a0c04179e501f02de7b76c59b663785e74eaacf61762a7a1cb6b74d7728016c74742cca5e78de0162fa65a69e56504181391c6545e00ff37a622bcc8f013882dcd9010bab7e9f098085d0ae0d80f43301ed3be3ecb7b6e1a1083faddf87fabab3c78851c2697408e3c60fff2148f9bab332925e6949cac9ba74ab260813e69749ad24d18d378105021b7d1aed602be328141dfc8cf8ff982dda82a5a96c40b4f3ce751a3cc8790e5cbf929b4e43cad2e1af6245cb700a265b3bec4b61d1a8776020a5560a070d0969f33f8629c5f66489e3f8f90dac2f692864b752bdeca029221592d88e6b7409c3f15f25c5768b0edc343dcba5939f3c94565589e8077713b9a0de02d79d0b093cc4b4ce1db3eebe85eb1a44efe2d31ca2ffd9fd780d6182d0f830393522620bdb771278798511bd8f8eed10209b0fb8234e59f1e68888eb2ada93d10113bc04b1b723a36c961469377e4ad65aeb950c46f7dcb9d74f83f3540859e64728f40f283a331bc661e6ba172d75d2e0f1aefa8a7ec5a394b77fe75691e40486cb46057cdc2ea549c85fd72c81886698799115b19d689109a9d338d33f817016577b08c13865ce1f4fc2497200f9bb495500c3bc2b9b9cc1f86fd9a9c0af1c8e4a630031c3b9fdef6a1da4790ea9f05b99df5bb2b065590926d263a530aca8c2b0480ab50647b857f0bc80ee5e0fffe473f59a6c6d4b685fadae2a1b7611f719660f0a1f4413a52bd14663414bc0257002d2fa7e2e86526e3ad5790ee3690f14d75b0abae817c13196e5ec62fed760709a3e579b66863b8a39cbb0ebfa063f0a7cbd06eae1df4ed46c98b756353b18af6b7eef296863a3d31d619d34ee80e3953232011a3fd3fd645aad0e9f2d38af95d27f5c976a5816f76ef6e20dc70c80d31733092402858b465c1cd7fe50b744af8a58e11388e3a5708ab3834ff9d3506d0ea304851928b1e2d5d05677015fb3ec4e9beda7b97b4d2586a34840629c26f637e4052a9bf782c974650c2be26fb96282180550a6d90ddab197e9fe4d91952c934c0d87280b5ca14e21550837643977b73ad24d9cb03b3ee618794cd2e5a3155b6a02b93d5c2053f2a17df7092788bcab93a731c93a831984125ccecaa14dffd0c807df6ba0a49cd5d4c0caec4d670bfe168e4d923741367b3f50a03c1ea1ef863b08b25e4d118fcab730177457e73b5cf4faf3f4443c8521bd07a7df18329a71a001b56ea45a1663e3f1f11d8511aa49e15e3cb137254e954b7ca7f0fc610d26b20ac9b5c43c58fce8e4e28687a1805af6334a9a11b736a2402cad89712dac6e440572a993e0f4d4455a6863e6c6b10a5bd58d5d9172fa603acdd3c488bba9a2640ec71d62f8665458e564cb4523c1a895e096b320c5d5f34aa19a048ccab96fe20f50aae4942be9343cb68633d88f0bdd5ecfdebcdf347a55553a4b97816ee1ef0ae78786d7dc7502f778542b163fefbcb15cdac5cf9c6accfa0daee47d9f6b1b0d233da8e62abbf61c666044dd77eb68f3c5a79eb3ea99755fff33e5085a5aab0ef462fcd4d67f9c7b40ea18434c01390e6d3cda376622a166e0d7b7072e3f2503d78d02fc2c60a3f64a287de1d5653bb05d2da6f32f1b86b14772693d4e12060ea80457812ca2554ed9e44c8332b80a7383337f81e2a94ef97afa9a83451e38049e9d3c04b41bdebe0e1516a2ab8ca0674c707d01c82ced0a7c3102d0254e7707 +generate_ring_signature 1bd71e0dbcfead5409660f7e8c3127a3d0c42fabf9ad6c0d82d88016ea624ff1 df36b0898025fc214088fd247b1b2e2c49d8b7112158410e9c2bfe9922c43dd3 6 c2c9a12dbbd3c694aa2dedff6015a22fd8a849edf52bcc7bbf8a05cd53f6ff23 437bbaa4c47db3859620b779dd5d2a04962d006d16f9bd04a7a6593ba645794a 4a32f65c25000f76a5af3a41ba8bd88d0acecdf6aaa2c55688deeb75625e2de5 f3a5c02e272bf7d1028f201e33c3549631c89ba801b5ad1cc7981424cd1c7c25 ae2910331e8e8b26f7ab2075d3bb9608f666fbf0c4c71063c58c2bfaefd7c055 889845375f869b5bb75e58c169cb19db753ca9f558f806b47c160f1dfff52084 4cd9b90686b4bca0ae584a8c6aa7aedc6d22993c380e03c55abfa047978b4a0e 0 0d39061d4c0467ebe36e13a0b972081e52828e8f0378b1a567091f9a8376f001d04e9629b22965fde5dc22df7180b4fffc614310486dad7f4bdbf66e622f3b07433c34e8ed59ee83e2d6abf144beb911ae28c47143405b67bb2d8d9084c5ad01b5e212f418fe441b5a353876d6278c135e7d9aa33fac3a93733b93c61194e40688bf935c1ab6162866348fb2740f8e67bc33e26abb12b825d24798319303b400ffed5daa25e306074f5a09f88b20fa6bc9ea86615d9e98c2c0a82078eed95003498a2fc1933fc2996c73d82e1ac955948f3e8460947779bcb5257b0fc41c6f08b03c2d4e9f9a437d2437325ba78782bb0de3442c907a27b7f2a7ffc761fda50407d99b7bbceaaea57875e80f26126ad88c8d09789941228a3ec34800d7791906e34f21727ecc3ff11534ef1c7843946469e33d0c385644a082cdf94077d76c00977d380552c2e6629a96cb8793ae9c58a3f88e04312b2760f91ac5d692bd1e0b766a667dbb9771d955572648b7dcca0a75305d3bb370df1e577b291698cfd107 +generate_ring_signature e424f4b58872a89f8d5cba2367a5c518d6f33932e829f1b82ea361005fd789a7 3881cd8adb14d6d3486a135de75961ed9ed7cb92294019d81adb8eb07344173a 4 08af7a24d14e395c22d134f5db91bce9bbbd0e05625a0817e7925c7f5d3a288f a0833c919f066178070650518818c09a464418c0579ad6b6f834b377035dad02 72d51c7e3fd7f62b2c2e108749a71a52e59072f9fe9d9eb78a9bb7c7d04b5358 33f26e4db6b10c1ec6205159c9292d852a39d8d4eff9b34147f9021e15cd4021 b0d7f260e8c049f4773e7a7957656bdb3426b3ed2b77a92963e25351afbab80e 2 3cc2bbfc7ed47c0b84402f29b0634632eefe188e7debfca0cbe7cb48b3cfaf0bc768e20c4014cac3e3ca15da53537c05c5429e04a43060c988663e4fe1aacf085f53f0a55bef34192913942db876affbb3fae1ed5f17e1ebc9f9d353ab1c08014a0c3646e77b5f25ead7ccad33c4d40c75c09fb2208886d077c2bd625eb8430e0ccd2f67acd092b07bc4e90b240b9f1485af4efe3a164d5f91d7dc83e8017d0f9447ba608bb38a1b282ce925bc0f5af88bf62aab2a08983492b92217bbe78b0f5f35a65d3ab8ef7ffdede43a840919ff9cdeb17de0e60ed393712ab339d7e9089d2ddfef4864cc21d7ed3f96880e214c0f8ac8c7319ed7dd5d8152075e95da0f +generate_ring_signature 5bbe504919c3ec458ead546da6a26736d2502e6a05308cb5625c39d40c3b0c12 af979abd7a21ec14aec19f65b49717be0e48846576d34c27404b0df29997b81f 13 d151a6ab8f224c3b9b2bdb438d5fee09275d134d320af24552c67af777d396bf 5627dd6d295bc9bd10039aa49cd520ce164dfdb98b5586dcf38ede1cab9e41dd 1e7968c3440d4a9994dfd89fda5d7f399bd609d7d428562aad4956efddbe85ba 49058138f3cfa317adc8439d479aa62adc11ba92670252048020952d91a949e7 206246ed0a40f7d750ec28040df4d659ae116b1dd5863f4372eda67025952d13 d3c2e54b9ae6962ea49eca392655fbf8027346ac63dcc309a0899e62420dd273 2b13248ac56f9f3bc37b3d49b2898fd783668af84f8a9738e610931e59e66842 b76331e8fac5e727716cd2fa1b24a6c5703bcee81afcd923b5efa452b0d6a869 87f3a2689803f1d825b4b7d9591c4631e2319b9b9582666d23b15e738939a03c 83179f1ced3540b0293518d30fad1b29d8227da77f1a32a19132b638d72d6d8a 7d1ff46774fae090d9433060c2c38b3e0543d2f9291f469f4ac0d75d6b8e4f90 e293f3c438ee82c0bf1d5b7d0cf27f9daede67da84f44436e395aac9021c1d3d 49541a49b7d1df796b7def54c7b0f8fccfb1a6674680d18f67f5e7124bf0a8dd 579e68d5f338d7c7e0e07ac07e0e592c9dcd18b87291a19316e66e57513d690a 8 af368c7d42a787fd8127cdc6a412c802524fa4329af935876cf1599831c42c05e8ff5e0b65c6e46d3826cb2d5534b419ee2c6b2a2672ee1d92b42ce70f2e7207fc91498fa72cc44ae7913f8a6d42e1a2db89deef51c1b41fee656718b82c71081526fe4675e2fcd87be90a1287e71258e618bcbb7db48f0ab80498801aff4a02e2c2a16a5a177ff3aaf50096023b686c547d084d0056b10e49d4fa85864d650a7ea0e3e0a7861c49d673ac53266e9043a6eef45357cd268695f8216e26e97106f79c7cdb585246396d27f3f65a0be7542470769028255c0dd621b189e83508031aaa83afdbef5eb9e86714ed8d8465eacd04ae11992f3aa8ecb1ac9efa8b4206153255d219812b70490ef8de637d6dec98f19d87b330b614fbf011ee6c0f3a02ad49d1178157061716615182ba12c949d379991bd6eed839e105fb88233d660bd20f42b5a1555298475c0270f2b7b47015bb29585442ecc71e7a929153ce7e004380838f0c02a7d3e06b6639fba7d250e1c975d89c1cc3bb38d0b19845d6e504e6ba41fe5436090affbddca7707fc95f60ca3955109bdc77601f06367bad4c04f4eeed9fdeffb79cd8e36fdbb3038c4c624715fd07a48dcae04356b577bdb60dd5fc6d0816a9cb4c9c7195d88569f4a6ba4b1f62dd2375f7add1b85dea22530937b9dc7ab6169a0c897454476bf10e120fb5840219344c4de65d0aca7e55e808e77d8a3eaee0d406039ded391570a3c7b8007344b7ad342f5b20d862fe93a001d0eb127452a0b85cacb931ffe51255bfe6fe80f0ba32c51b5faef13444a67602300b125c8efcf418b312d23e4f15293ac3194af866a7b4709baee85de56c6408a2fdd94d0c7797818c2d2309209a6e32724c9f1e3c8828bcb52155982ae6a0000ee4211f48be5f0b2b869729a575068549063d97f0728e197831c3525df51c0d2d87ea585677e743812498b33595786860050bf07602795102fe20f05a42bb0301c2a59d3d6df160c3c7fcd44ac27b6a4acca59a3163f30295efaecbb3ad3603e09cbac03b271e2fec9b5598f612c1d093233cd790ed9c5228ec5e6edfa61607dfd29302f33b84786c3fb17b6febad25767f153aa2755aa3fb0c3e06d11dee0dd9a22a3bf73afdc14a126872384c64f2f636aac5d8a87db1f9837662f6154f00 +generate_ring_signature a0859f82b14dfa14725b605a1f66c4e7e3c5703096a3cfa833f996b5e105ea0d cc280d0af6534604494020baf8cb120bffdec0339aadcf45b3d97a2b7965d393 1 13f43d83ff833e7b6ed996c43f01724dde26aa6d0ff8bee55b7f2a7ae080c893 1693e87cf56c39b63aad39216be18df69feef1484ef89a9d20eccf59584e360f 0 f2140da73e59aca72c93618d0384e734c49998b2891ff21f4b7cdb6f9cb543084f103fa57b8d61f852da3dfd5afc81aaab78d8a6d82ee5476ad78590b1f50101 +generate_ring_signature 0b93af27e122fda8495915b5038a4135290d292040914c8322751c075859176c d874a4e8dbce217f1f0d503daa4d2833dd8457d2d29022c39b63c2ff18295462 1 fb104b44d767fe74de8cb1c133c2e1d253857eb003c6c63528d7cc67b3f7e49b f39fa6fd80c7a487e67ce834393545ccc91891b663b57ae879ce6ba4079d600a 0 938ec36160d9a37319b125ccd54668b71e2532ae16d932d284c72af1f7fc000453e3058faedb6c5b6f0aa88383a6f0d64c29c370a7b1a59f20c09b3712e75d0d +generate_ring_signature 88ec571618126ea8a58e3d9d6d3415253a7a9e2a4833ab4c68869ccf369a1aa1 0fa962ae711149af59f3923efb7e275ecd525f065faddc47b94d5b77a1d058bd 3 147eef3259895073beefedcdbc2d0373150bb6dfdc255b9c58326fb443cf76cf 48b26ceede8691804ed3476374d36b2e820ee44c0792361a511080de5fab88f0 2798e56eff7a2f665c67a1b615e19834f2095580047490ed3998cfa1a5ea645b 55597215ee22059246d3b0ca241f9fbeea9ab39876dc981ff1fe7f70ea10cd0b 1 37494fa889ad5c6686b0a69f0a573538f5a7b86543dd67d8b960d04eaab1920341adea81b6b7d4e0cb2eee4d56db0317556f98aa9c266c0f4d6ab6d09ab18e00bbc1cdaf5bd37f2958034781f9070e507705e30ae51da563b71145a6d554b40a2b94899419f4553e984c59e918535bb99cc75201467084d8c12b6b97a334d90fd34ec919165432c50f2dac7e1ad1cc7f2dc387df81457153ab759b27740a04043f958618e8fbe419a0161f0ccabecb820943243fec18856b0979f1691e40d404 +generate_ring_signature 7d0e728c17d84fa5a6380b5d42c2874053f090ca9619c72e8f856aa878300a78 406841c5266b911d87602974d79515ee3cdefdc52ee5e319c934adbcbc964735 26 e134815b7a0d32a792a347b2f87e63aab51b90b3e63b281f4832e2e18470e452 7b5604ae9a247eff5c40efa5965808a9294586dfbd6d96ac962167a4134acf4e e7040750c32328c80ac5608f9a0bf591ebef09d0630f5022e3628558c91ab2e9 3c358a038783a448cf3286470996f23f42d97e3ca2cc4bf4fe62ae3a4f974466 5ce0c0f74280351838242547aa953e6ef42f3830c9763f1f201c95e116a9cbf6 6db16ea86470b1e4bc1aba22ffd2f6a309b8c442cbc82c9ae1d7503ff44ba235 996b564c3f75320433dcb6e93ae0c45f1d1bd63268b2dcf97feccdc2ce8fcb02 2a7ea0d2ab7ecd8275ff6c47dca7ec0fa796fa4abad68d20384a1314ff83db12 31ecb4133c92846d600a7004a619e5867595ff17fd4b4c4b6685a34b0a64a212 e065a64af314013132daf153c9d7079292340724999e9b9e0e2ac22c4be269cd 44ae0fd0320230f57bbe081415831d4da5ff3a4c6fe4ae24a2f9721df86e8de8 8ea50a4f60f0c9d389940d28fe7bda03dc202a8187b2287c5609d0a86d80b970 b0fdab7d002af49b410025b061cd946fa32b4a4fbaa400d8b05ad89ec6c102cf f39415c4eff393fecd622a777af99d13d6ada020e6b5f154b0721f91bd36e005 99df2ae81c858e669d81270858e66861a9bebc89abd36b232d2fc70415f32a66 a3bf1ab959108e6eb6044b8d3cc8ef0fac7c56d326b225948974c66003a7a49b 3296005eac9a12ea9a9160bd2d224e549a050f37f7b8a6643007f953cda3964c ad1056f0da4946f740394f3c044438c016bc31ef30bd1ebeb4da58fedf91096b c69e1a9512027cbd747a968593c936fab41dfc0de4189316f49d731dd5032cd5 cfa3c7a19dc9fe7654556910f7e6332cff284d94787d3f1c142213b9ab388aa1 a8a526d7c0cbf45988a265d3f3874bfc16fd3e972b5c6a02151a47aa44f07394 1b4b025ca5776cb79a04b73e1cd207f7bf18f388bb9732279f870a94c20eb53d 9223e36a7f6fac4560bd6a82a48d6af7bc91bfcebee656f6f014673a0289b485 9a8601702f972d76ff9959082fd1ab159e5607070cc24c09633ece1f65d65aba 73980dab5271d0312d3736daa5ccffe20edc58040be9a59d833ab288f2623b38 9838bf0467b2e4c5b42c2558fe202360778154a2df347fb3cbe950d677b7caed 9da3143e8f9e479e66ebb78947eccfd708b8dc2af91e13ddbccda8beab2b560a 20 325e456c11776379052eec1f5e5bc3c6f7e44ec5d292e651f5dce3111b1d4008e6f9ba7a184ae6a60adc9d8393b43ad77385ba8117555d316e2d7dc3fceaa409c9147b5727d66b95eea2ce3ea847e60ad0be4a1a455072fda76748fbc424170ef92aa6ba23b9a63f6ff892f5c536b1e9b09291e7cb1b47cac2657ce7cb1bfd0fc900c4a89f8a6e676fa39538ddf0c8c572181dc12ca66862bf13b0c8db29e10792dd4c1de50e0b3ff1d7cef0a4446c52bea00d794e999ea5b244c87d459ba906b944fba8dd559b2df25967379be7e631f382f8d05659ddf1aa61c06231077a090261bb6cda9b85227b3803db6ef276da6a412cbd524ac3403bde9396e401870b7b851474faa7b4ecf51087fc5a6b56f39812914a47961c8fdfa0cdedfa59e3075b1687f01292c8344c7ac61d2c4cdac3c02ff7269f9f6c1ca5eedb58c1260b0101d6b9814538b5018f5e5ccf0b30292d73560d63c380fb73fcee38788ec378047a6c994ceceea7ef000241d7d9f2cc2cd74b87dbad5d3d66f32fc1a37749fe0148690210f553511464e003e689ac33b8e1c8690d330f66df4a48fcbc52b92f0b3fedd7376e970be7b1f4288ee94fb9c0711008b032fbddd8bd8086ec5d088c0383740ac83379df53bfefaf7803224b8951e1849c31ffacaa16465cded123090108a76ff73d8fbb9d567abbb89cf8166e6ecbb8ae98f282193fe587eb2ab0cc033de4716df0c6c245c5c31dd4be2c89a2d9bde699c0d7c95516dcc9e409c7ad0c37614ca66964cb196d4fea602bac76a60c75937de567a26796a53be834a017013a4600971dcd8df3c4758f6bd21f8ead8ee17ac0f7890b19a0bcc377b172080815c16f420f0b4d55884210f6f153b5ef5ea6f1f663a3991fcf0f25966d044502d06727812ff7f40ddabe2d2d6fcb83d2ce79a65f4883f21990683a037272140911a5462221a79dab36eaf72c1b6cdb249990b52a0fe7e4bfabbbb12fdd1c22076a88f39ce2ff234cdd70e97048dc40fe21154e09df562b78e0768e3c2ee2fa02d774b997d2b49245b13983d2494bf7705a1ecca7dbf7aacea87daf47bd813408b3db0c6a004c47075ede405cf729874015299950c5bf825c7908336ef0d5e6071de715da2e8a476f73bc84f89e39cabf496b41794002d5f32b4dbf5a9289f40938b32016eb8a665e10c59f989cf089e9d05d3d286b2c83c5092d613e56abff006419bfc0d0009cd5c3ba8477a02abb4c9cc6ea0cf7482daa6f3935b896139b01f5cd778d413aa5b83ae4af0566e02135c2e7293b75d7866a273c6aa8f1ee8606058c6992e269c9b51116da6d6d3250d79c18675c2b5697dc3eee68f617420d0be76ec01a40aac669cc29f290745181ed264b9446e08c0e99969d46b1556861091df36faf365387a864db8ba7154a5453de2137a8d2cfbede31418522f709e60fac6b782e3075fba83a26c6c001610e043e7fb2f4d979597bc50f0c87a291b901946aef3ac851e81d0d06bd324f1843e3a3b9993d94e24714e49b24f48ae5960d6eec300c43e9ed42a5dfab3d0df7664552657ea1bc52dfa00874572480f7a904f58162df0792be94dea4ff03a5586e0041156b7fa54a6a52a25f6eef830511062365eceecc8929d9dcb3517da5603b2b01b501c090a871c2fbc55c76e7f380011b86838d1f2e96b18c35634188154f310917caf7c243ae1f6b437e9141c8330e8d60b08f5a9536796602137d18834176aa48fbdc616a27081a61fee25b317d08b6a0d9ad0f238e0af6294b066623a782d26cd6de9d1de327e8d6188090439d0a8a146e88a64132f46bf21699316ee271042f90016145e23e423996f64e43de0b401ffa59dc03f7a2726f8ec61b201e36dd12dd7984b18c9271f284505ab4bc01469891c447c680aebecd3f92ca45b838cf8fa7b2dae97749fa0aa33d5a6df00e645dfb81149422dcd4018550464042f2ff4b6d948cf6bbd15548d39b3af061059f3a4ed82d641749cbb54decffec80798916b3fac25cdfb33d772bff165fd70fd2b11d7b465c4ceadf498f7869acd2253d694e4c88aca2f5f1ad426d920b7b0efac57d3e87c7f39d442459f7563d70f5191fe57e582c635fb90ae1bb580ead0c9ca4bb61b06221858253526ae3683b9ca03f69f91a85c806324f56a56d4dce0a4ebc72bda5955302fda39f081180030a79acce778fd0a881642bd2beca36ac0fbb83309b5f5113800d51783a244730d6b642ce3957a9ac1c8ccef5fbdf156c0291d178d45b21ee8c2ae7030bced30324b3304534924284468ff909e35c08e303241b1140a84f7312a9aea26de5fe87cd7eb189822191f8635e7514cf69fa0803 +generate_ring_signature 65915976dad0b79ec2f06123198fc3a123c37fc4a73773487e857da9a5c54c71 7073df50257577f8b723f2b7268b6d026d172b9fa7bafe8e0d1712112b026e09 31 a2b0a4460179b04820d33421d1ed3c5f984c770738e6e734176c1b69ba5be705 d172bfb94a40f6d5e3a512f19094f112ee1ab673f54ee9968556b127169952cc 39c3b2c50c49fbb9ea7f49ebd3ff1b21c6120fecedd83b6fbc88013dbbebdbf9 36fc7e7fe71707ca4777c30c05d7020fc7da04b333676201efe55e93a1468797 7f1971181921ffd10a03172ec2726b7c95b714214b94d040e914a4a549d24623 2013ffd455d969d46ef413e6cc8114d2b6a25de9e5fff6aabd4f1c84fe28bf58 61e67258e2b7ffab772eb1e68d832f940bfed8d3ea7e56f9fcfe7ea26a8d1999 64e5efb93d450926f1e6115688897055b968f93a6972172719d69cdc21e93e9a 1db41b3664617a8e5dd42df836433ccda0abc7ff1d94590706d506cd83da9c79 722a3ca714a18f10ad273c30a5ed6104a00bed41e5c6bf5f955ab7dd41a49f1f 32367aced52479bf495d60e499e1671537ac2d8711fcd83ef8874c667fb94fd6 d4052c8067abf0112c03f4ef277f7ee5e0c351b245c1f922d2c82bb855d6c341 537ff1cd54e1e52430585be5197b3da8954b631e423beba3e39dd3dc6be19004 66b18195c83e626b6a9c315854420167a09bdb29c02175eeca18bb1996893a0f 6c8982d18575be7948224abf0157ef3f788de7294ab6ff5c1cc6d9bdd5647527 6251ef481c86959a70d700a5ec9892ba415d370dd1fb140eac186f74090b59ae 25642851c0c2540c4a43e28ef53f2923af198546242bbec704db549933ad6498 002c5ef9e09a9065372ec5c408cdf6975b9673d416d78204ca1ff40b3a2307bf abc5cb8512bb1db9b640c4890e68dc3a7e0b492202e48c68c26ab68501150352 0b8a2f4c3e989eedaa91cf4c308738a050b33d28cf23940ed9070a576b07aaba 97d865b7b22cd95e121679e94c5cfb4b1870e4779058d308d565151de1ffc754 b3ed05b092a76c0031c064d505430a15b1bb4071bb70991dfd111e04ec77ad44 99096c10ba7981946e06381fb36672c611c0aa8ea97a3bd8d171a4a188d70e91 fcef5abfcf89053360ceba60f1a0742f6a209b4e9bd3ba23a0491b25b18d12f8 61c1720dc08e8fd3241ed1c0f59562dc5a39337f2a33443ab4f041ec9842e216 cd5d30ee14c6d6a6ac28024b55fbe2c306a0dda2790957ec2ea3806615e9965b 65c60d4eff3add02a38c4876bd36586378abd726502755756b1ce232b7fe06eb 3e7c1ec3ef42e896cc5564c1881ccaa0ba7a5b5e616f069a185cbd6c6643aea9 521bea98888e7645384b810063c034332ea7fd15cbf091ecc24a9b2d629af997 5293f3c7583d6e5f28b5a08282ee05a2aa2e8afc2b35db0ed6c4aab167de4273 a18fc87524c2eee2b4cf3c001ae2969837e11bfdc9e135cdc09a3c5854b3a04e ffbadfc27c1d36aa85dd83ce4143d666a9fef3b90820373fb96f7976117db103 1 e1e031054aba103d309ff23c6e1ef6ebfea2ecaa4d134b3c5a26e750bb5e750bd1ec3efba130c1c0e764ae0541877432a9a134890daf637c6b7aaef90bc5c70e6df8ba664ccd004f2af8ee634ce569f6861b8911ae25c13b12c3225b2f069b002164371203234771933053189f76d06474a90961b500a796a35d176efa9bb90019d3e595b90e8a646c8e1e5005ab83ef9284d499d6420e069cd8b78138137b03762e1d0582f50946687193b607f292c96c38ed1903914cd771f45b0da5514a0a1ce1c47fa9f5745168d44cad0b642c7f2d922bfab2dd1b502cbc85d4584b1d078d9890022f7b1962400bee36f4624944f0b7f995ccc87a6dd96dc6a430e65a035cce61b1991b4de039865a7b8fa678ea585da815b26baf5d60ee4649c8e89506ecf7af186c82e68333db8b420769bf255bee1e53b5abc0784724672225658707449a66e0d7eb33bc9cd9f6b913077bbe76c3c1ef2f649fc6846900a9e4fae30a333186d3af0819e34bec9f01f278bdc83debb2251c456df0b84cde6d16a2ab032811f6d0c95fab9b7ffbec0f53147971bb28d5e70927dc1b595c86baaa15c80cfcd3c79b16e0095e76466d482300294cace148270a790a337a7dcbaa430ed105ca79b271a80af7dd38e28bc97adfa8155bed333c671461d9666e2db21d64d003a7126f26c022d3c1b36804fa0eddc311dd84a7bfc4a0848c701b1bcc9076160646c69abac06425953fa682947113f33720dab0c7e3622e7dcf5a520cc7f56301be0cfb1ee90854f8bddbcb6d63bc83e6db96b0296c2ac97905b1c0ac9188f80e6d6acf6a20a80a24dd41c8c1cad0303200999abdf2b6efd9816ae29b812ede013d5a17e3e2653e9fa4b9f5ad693fe06152b9314ecc9e8b8e32d8331cb09ad90c4b1ee92bc59d66741bcd904e6b204d87ef3cbf135e19a7438970b571109a8702e399026238b71f5a5a458e1cd26b32b1daf994a48a327afa5aad003918e3740dd9dd391ace6d134f1003c570c059241f8c0683a303411c7644d86209fb99ef05878e6643f3f474276b5ce4c1d76037fe50bf7321e995cdcd8f07994e02389e0e5fb41ced19511837ee37e7502fba3fb5e4ab33d2caecc2dd8372cd346e0de50458105d8e32bc180992a6231a3192a686878e4ee35037a2f36d583d7dc910bf0cb09a6072a5987797255cb632ce8de054bd9c2abe3a772c0eb2198bfbada57604e62f177c630b9cf78dd72f85ae17b5584946e8d929e418fa9cddef5366ecb00926457109c1d6a920a7c053472b5d54e0c410af44ce0c562dcdb194c02fefea01513244b2838132196da731943a1bce14fece3ec9bf22e704a2cdf259c918d40115fbfae1b09bbcca0f753519b5f7e017a26572d9d53ffa376bd3508595c41f08a10612f500efd9e10ce26dd9545d696bf411b98e4afb80e84505f09b8cee0c021f03aad100e80bc15ee1fbf3d849e1d092be486415da42f0a25ab82f7df576076cc138d9ca6ad45ad33239f334c242aa5b750d5b5595ed6d6d9c94bd7ab91602c6512e7216bd8c8a990a7cc0e5f9a2866f07e3b6e4df0a001e5dd7ace49c91058af2771e18f7672d8b48615989e2c7bd47574983d57aa46f9fd562d8d7442100779eb8d30ad91f5e9543ce5915b47e0b9f8c4a4eb294af64da76348d29a6ca074a7d1d89d95f82866a40a85bb5039a597269d3ce9ebd8f3d6c71e4430382bc0f4eda4ea3b3616a1b94636c7d492a82e9ff868e7f261881fdd241faa5b38f6102e22a5c69f6b69be687e52bd487bbe64a75abb66a58368aa0b4652c133f9a3b00c92c2306c3cb5b8f5f6c60f26c7ed101738c5328ee0cd1c46c10462d2b4dd0078f2ab1d86be6fd2710be0fbd1bf5c148b574802a9aa0038b38f2d82b264a87050b770885637958546d679eed705b972dc9ca0395ee8fdf43c3b1d76d76f3b10785a6f6a9611f43d2f09a82c544f462c982dfcbc32429080e2d4f05a3984bf9075b8516307ed325cf84371a50350c86b1e155c480578a1130d5df634f55cdd20c79df9b61f6c2cfa3e038db159e8b36afd4a5ec5b012c52340edfc63255e0e30f564f6e12c478b0989d45029a975f5c3e5a7397ec3b41408b4bd212735ea697082650d2f76929631250221697812d9f29dfb2f221f868b84b23477f35b8c9dd086113c3ef243b321be5b7a07b1f1256075229db03f5b4fc15aa00b08effa3c40290dfa937e90acde15289865763c9ef197367bf716a79b47c598105b08708700099743822bd5a049bb8be2afa285d8bfeff7992c95a5f28f2749751570cb3320ce4e62649f9389b222370dfb832cef4664f19e1c5dec59bd0549ecc7900aa430b625be6b684221e26509d038d58f25c6d3f32db09efa6afaf9f0313a12cc659063e89654c1d590cff19daccdc5f94675f1d1375999fa39f3dedbdaf2d65fec105903ea624e0d9009e1f14af218f669cf9a97a12672ed5ecc52b746f1fab78b005ed4dd717042c4a7300baffed39eb2c2fb5584d7d1f442ab3d741e61b63b8c90d87f896723315285a01cdc8c237ab2b558caf27883b2250978cec131c8eb4bf0fef06125b4bd986b095a07e4d756b9dc2b8a9e8b7ec749f75e7b030026a6d0b075ec4aab8c3808112cdee7c16fb29154a1ea143a57f45d468fea51171cba8d504075849f5d72b9c8e395ce119889c0b5293e493b59d7ba85cf95963d03989890586d31e41ae2affb2e6a2b96a372866ce646fcee6c2740bb0e4726d601d25af0e56ec3e33fc94286c0c2102cf35a98e8a2153a31cfa5e9f7bdf52225579009006 +generate_ring_signature ac3e3b092c1e38aa072cc4210d9bb2e4ce013216d06cba6049ca7b679bf4822c 317219905c41dfc83057f3a6a0d538f47add4d4931b087b6d488aee4b93ff7f4 1 a957ab83e1decda794a51d72c6b411a526b193dfcdcb97941651ca61fb40a066 cafd4da25098d3a9667da2474b43613aa07f58c8cec919c02067c70b02f18902 0 dd277ae6f3162599b15d772ea0539729ed4b475fe6613262b8e7dc6955e71a0e7fe1ec37e52d3555d84977e85d6584743d60c41121e813a6d1bd171b7be19209 +generate_ring_signature 6107adc2ed0507e69710223b8f25ad15872db003a33231b30b13e693977a9a04 28c0cb6ee37eee1a2282c28676b5b9b3ab652ecfd46c4cc29b8b6f4ba8cbf514 1 ddfb74774075531723882e838680b6087bc40a394ddfb6ad1235e233491f4a7a c4aae45c82526a21a7eedc6caab8558a2bd4f54cbbd11e95e72bfe372bd1bb09 0 a01ff14755aae9594bc222e3c0a1e0f2f57c8a0aeb01e963e1a8a6a594db0e0b83d4158edb1a54b75515dae4f3b33849905d03308bdc2ce0020df8aad9587c04 +generate_ring_signature 2100a5f4e410e5dc62f19a0c5e87184e1d148a8dd98b2466964959ff2381da66 d6d869463615fbf48380f9d11e7461f8cd5bc1fe91fb99f685743aba5ae70101 28 793ef8c181dd024839f3212f25ae757cb80bec1c219db4246c6ee2fa052b6f17 2aba2b63e6491a35ef32789b0c7a6274377cc205254e9bf2f69cb0286dae5523 3fa876cce9035a3e6a4d5d1b7a8c09e2df305701c39878bab4da23bc717f2eed c9e31cf9d3551e3fb2af4fd1608af9627f0a417d3cdaae50424f7ae0ff69783b d0c8613048181c9a3694afd2a02589c944bb3eef2b82b77c250775536449aee1 b9ebf2faf7aa48e64ad3dd91d6c719ac4f765c31b03bcb8fafc869c8c7636f91 e9f8b1b1cac9b84936264a4752dc97d3028a209be61bbe6bde32f8faf5463a72 2e66c163b5b33bc4c675b99736bd883533d8cf58ff92a6c6632cd47898a8060e a993576fd3d80b0925a900c3072aff58cd46dff9d10f166ab52fc57182b7afa5 da828df878c59586d46a4c49696661c3bdbdfa9f36a053d5806692c42e3f36f4 d2f4e92d5e5d70fce216e8b6d243fb32014d8abae37bebbee14ea9b8a3d84b8a 2f1f24c37d5eee2ae72a2dcd3b1b6ad30f5d34734a762fc4ab7b8956ff49dd02 cc7d90c3c1c794b6406cb1fa52f61a0045a2fdfe1bd83636c848f18a100bd98d 481a170059326d8eaf5a6885213b915fac4cb27a8eb5318489a75fed10cf50e2 dd2cd3093ca1a6169b55964de9003e308736bfb3903f3c8f8d6b02f252df474f 7dc1c9c1fb20dbf1c50b213b43df2fc31dba76b9afbee67b8b0faff9a7d269ed 50e9f8e8964df954a2c522a8d373e52d2187bbc76919287a59d9b506249ef137 8902dad485e7e3a7b9cbb784074a11b2f75686b7bd6d613027a0bd91f0a96c2e 33f2908d1fd2d85c821734bfee6f1f08a1b70b82b95a7b592d02a3d09e858cea c70aa472bbea4d8d55b345cc2948ffcbff1f91294eb23062088332a5d78b22dc cf57313df41a81100e9abfe9d50146d4bd97dee3a4633db1d47cda8874de978e f661b18e8576915d55a6a23e8d201ed5286a576f2c81c2fbf4ec7945e9b54f7a bcbfc29481405ef6941a8865ff80219df8f16f972e6db14ce5ae1f05aa3e04ff 1c5b932bed93ed0c1b0cdd46325126b6049b4def7a3bad6b7edbb8c6adb628cb 089949b4a4c24c2131989b09abdd0aefdc12c1c1f62ae87e04fbe0e9da567544 0da2fd4af5735a1b90aa73c411039d41dc2f61aa1d1ff7bbb76ad39e8e2c5082 81073bb6dd2b22897882b3c9d865bec7f568b80ff23cdfcc22421fe9771c3395 5b87191663fc83c7ac5c0a1936e414fa357bd8c83323c08c5aec0c65be085458 6a668a19b1bc83605e4aa62bc2a17c45bcd68210cf2b05980ff205babdeb8508 27 648d6924a11369fc5d2bade59f09b707b28e90f1dcf89e52276d069679ab940dccd995fc380e89ad64efa795a244ef66441b633d286b3855a4a70ea63dcb8e0372cc9ab9bb112dd6b596eee8d52ab50cbd5dd7fe62c03fcb14554ab4482cb60466c9a0565eca33f75423ee4ee506df9de4d2ae801070b8bc3bbd0298bddfc300ff7fe6ec8a3ddacea1ee7c658f08bb97e264a944cc17bfb2e5212be4ee9ce10e329f88c610d9205a43f5b690fc17ba426971197ba0b7ab6717503c9b50af880407d3702cd16495adf74d067f3c67b51d29f3a130cddf8ef785af58dc7598830a0a7fda5bba20a94a7dea5262be385b66d999070b3e542ae9b4cecb64793c8c0d06cc61b0aed2017c34529ed19fc056500db5030dcf684c5582aaa353be6e3c01fe0424fac328567920ce05662c2b8e7ab7ef3e4acdccebee49f3e00e8bc9f70e30b829d77645f9f76db0d2da4d1d6343a3b31cb1338a7655f0161127b74c4b066de546b7a96b09f516c2b71e0cdb3b955ca4795d385eb223f3f8a0650491020683edacc93b3ff82c8b14e468615d09f702a6a82de37d0e09300c2c8228513b0d10225a10d16b92d46b1116dc764eced1ebe164eed912b2dcf835866038f6be075b5fbd663b44e16b44c864585b74c199f8c960dda5a456a7c471233d806fa8096e556b365384956823c73ee290c0c9fa4f41cece32cd60b72c26e9e9c7b6b00cb9c01f645e586a2112cbac077143f9f10fd1323fe5612ad0fd868ad4fbfa0a00b5db0edd482c8f70ac15dc3ccaabab86f23f389101524a48b93270901e0a59052f727a159f763909ff10073ebe1b612eb5f5d4c128a13285c30fc4551626d706e514e3fe8c0c410a003ec217ae1188e3c8b317030c21cce978ac9193caf00a0501a22913a76d47f5c6e6e53a88a792336053e7e0337d3d405845de0ce32d5b06f7ab9fdb839cdd10956f59e9a4b595de1558aa57dc305e80a13a781b209987088f2606e5bd4caad396fa2852a5479a5b7068fedac6e79d8c68c97512315ccf0faca367b8616b7e954cb434afbc335e7e01ea7d552e77178b1fc800c771b79903ac9711e1bd0772efd34c41c90f9bbf384064fc049e431c6413b23bdba75d3a0fcb79ff0bb0901fe0321fc41c70325a33b702c56375d9bd85945bc7b833c32200b249c959d814f048be4c7c5414e5fe801324fdb2a660240da411172197bf4b0880627bd74f967b4c15b2d289740fe4362777e7beba012ba7e3c4ac088ea35802a5e42e695d86be71d3c48f85598e2d547c6adc27925b16a6e6b6c51dd29f960ca5574519107556c3a36722dfa78fe6f217c9d15b5494455ca7b7813b3f49e60b19dd6f01184120ee68f4bb80cb7b3c043b32ddf0c8e67d70e1afbf48c0252a033b410a7913402b87054b54b759ca6bed3acda99335218077ce1d5549b81491089343c05de444ab18a470761a6a19e918fc8538d7b5b23c2d6c76568e65edbb09abe66447a7261b689a8c5165faa9dbc6fe384980148adfe99a4e7222ed929407b71dbaa0a5c448a79d80a085ff5035a9ece54ea89a353f5ca61a0bf57703720e6e4ac852485bf9a029e6c24fb973f3f3840a6b532a597a91595f89441cf4b60979b40cc1d175558e2da2995769b12ca6bb22444730601acf9f2d575baaa91008912e2592f41b4916a9f9627c1421864e3f19ca0eaca32870e47e402c4f114503e2d0f6dfc6d1733d090cc82aaf8ae82b892fe1da599acb3e8db2d3eecc14d207ed7cb97aed6d6aa0fea3d7b6f314639e092a91199c9b77eaa9d1c363c0857d06f572aaaaa4991f7a55a9f036c373fe299e3b8a85cad50c4d55e01738acfb1703ef4f1bcaca154e2767f56f40b6b01f60e76072fe91b1e8a0405beae04e103c0f5afd1f9667e1ff33bb40fcf6bcf3c760201e39912dd8149cd73abde8723d2406952d166c303c7f822195f59d48e42e1926b6a35085ec3317c1e9d024498ee90d3d45f5c3aeb18a6275cf207caa116136dba92954a85adcdc5d35bb2a4877e609ab43fb2f51792862dd6e42d2ff5215f8599774d346dabe90f1b1e6ec30f9180cdcfd1d2c93e3a88a48ceaee0bc5f231be2976529be0fea473c6c9b0e7910a70308c2cce6d49d0441b6d0869724f81c7cd4e565a55db9738cf4d52aedda5d6502ebb03e4d191fa13c70901e22daa333c2eb7c259b44798303ae974375d0b46f0e11b516dbdc8fef6b5eefc4923aff4483becdd5d2b8bf34ce8cfd8d0cec5e15063a5d0109e704ef7dba81eb3d27798cd9ca77d607cf18251302eeb0096795b205ca8e9ed46f8ef0d81eab9316bbdb265d7d25610239e52e6bfcafb598a01d950ad2116b4fa5c436676734d9a46b3a483f6ed2163237bdcfc30f343e74bab739015938ea48af543ec234debe7061a50fae07698d0592606b768d1368b9d9ee970df52a28cc4ee6ed16ea9510e55394df485f0fd539a2ae607795c4ebc3f46c3307f6937b8f648d034c662f2343763d8e77c44c3caa390383d3d57c4de5169eca09 +generate_ring_signature af71df96a0ce2242af718f9f36bf7b1e2e54bf52afe541127dc2b87f6ae259aa ca8423fd0e91f8872e0a32bd20940a577b0fe5939ff8c684a67587c699407cec 22 1258734c3315b929610f4c8215a30412459f294317eaa246b5d8867d12c69363 aab540da690563ec5971895052762a8e8998c754f895201694a17c476dd598b1 74990fc817461aa56686cfd5f453b41a69f8aa901fd4e8d4d1f1a0b5f6ff8c91 be833b7186433e5d760182cd8ab18751036d1b0e968396cf61ef0d903633991a c174deb550d5ce869ebe3ec05bb8fb582cf4e27c35d5c190b7da5de77f28dcdf 8dec97e5c5778667e0ddd106fa037ed7c45eca6ae55c3bcf25503d41bd807fcf ebb1afb03b050a92b8e559eb8e067b545f63d8099821e50b1c99799b7d1991eb 7e9eca4eac87463a8a0706af0dff91dd9000aaea65a691255bf3d4891b55244e 51b1223f80ed22dd385f378a33a14111d712ca3018ba967a587bed35d8b798cd a1c58243c6446bfc860b55045ec7607dbb23081b72265365fa90cb90eba8ee23 41910bf9644f3f9fe19c5a219d3b7f96ea6168133c19629a2889a73de302b5fd ac0940d8dce80710286d7b2122907d0c2a8cbebea6493e44d3ebc88b131e98e8 e10b2dceb0a93a22d8c62e6e4f8c7d766c0733316e6d9af29f6a70293b2ee109 f2553f4c0fd73109c497c05a0524430184d0995adaf87df4b35b48dd589db98e ac12b75019ecd593017d62134be2c9145b530b80d0c604f3beedde286fbca385 366ffcc3d9f2704507e10f7945ca35af510d4917ca6a0f320b22bb9bf2d126e4 6e8396f83467caf6c90a774f7a35dcba54461a71e2ee2990df127a5271b8093c 3d1e476c5c17a2a9d875e8e23243deaa16b8ce4bcdc2dda4f11d60a521fd83f8 c52b53a7882356adf4167237b853e0605029776fae814d3fc7fb9f5bb8722f2d 524c073fa8195ef0ca8eeba82d2182b8cb026a4f0759296cfc02e5a240e6b65a 0214ccd35e3432dba23b879df17a8efcc9e2e56826be4d0ed4cad87bafd20c9e 9e572b2ccc41e5595b3c74f6b1a571dad53ce4efdd29e04fb953b7e8e5abd8c4 c379fc436343668d69d752ee38c2e4f989cc013b9926f3c54568eafdaaef8603 7 9992f500931ff7832bbb50126894780e600da117a548d25e7e98850715e9b1073ff3d31215f1d22f9ed6d8ed3c501585a35a2a15abac89cccf016100842b53044852569b4ef3ebe7f616721151bb96f8b7c0605e2f6db08da5fd9028ffb77b0fa7933c55e7ec5aab3f476f89dd578895365a15434b54ea22923185ea4195ba0d9c1ab9b4b168dc8e619b811129c12d6fa3bbf34684fb8b3a2e26e42ea6ace309368fe6a2f687df4ce7e55fcc992d54891bb9b158a9bde23924206fbc198e090649da00f34ae324111749b7a87b1d932bcef1bc86d5262f79abd7dccf6e2600025dc1f187309f39d5d22617803a0c332b11ddced9bc6a0e6f13ab3b6cd40a970c2211ce4663ac96be5a19065020e41d5c9ade02ef4092f178d507e8fc5e05740587a23d310036c02d3fb3f6ebb3b0f89e338d521edff6cb43ca48d9fef7fe7003e1cd86995864359357325a63826f23bcee9261e32429e638ca44c7a26832420f6e9a25520c0658a24f727383e0fe4ce6ef8f664364ceb69b67982119764f0e03d51936fcfa210351d569193956f06fe0945a6c206d56b5f88f466364efbe6c0e32b7ac71c8c912c7fd291d4a30ab857d9fdc38471d0a43d97ea4c98da064f60f4520dfcfa7ee94e29f2a71521489a09f68ea92f7c5626a9f1fa370d18b1de807bfa99afb301ee05041853e33c21747656992d5bd779239249060bf42c1bc170c4ed19720d9ea9ed4b56e41ecf233ffe45b2b5fa0c560fdb493c8ce21a4327d033a0b44c105f1f453cca7526c80f726a77eb3ce460d4ac74a10053d0385517a0836b69d48e9c3622e883d1805c3ec2eb867d34cf15e27cf5a06e203907bdbb50c252ea6185a1c5210188e55c5a5f269390d46de4257dedf6bfdfbfe3542db170319be1b50ddadbec2fc0fa5d51572b5ae48758f5d0c0ca9b70900822e8bbbda0382c27cadb9e52e2e85e62ede48e10055b29204a882690b2c0867d865f210db02a62f5f2934f86c9f102d7dcf8b8542fb1b0e1007e4ed5f2ca7114beedbed8307df73093abd8d1fdc654d4d054f0f97a669312f086ec8f921f88fc80870d6c20309a5ff593f5ff3295f4ead0aded9ae766692912b8781c31d4839d407be330b0eba7413611ca47f2f2f5850eb1bd405418f32e0afdfa938a3161868312b13640b8c2d85e64db452f7f8abee33c16e41d3ff5f5ee6fc92dcfdef484ec26db23d0ef99aac0ea09e2510cf67ba9026c35d25afb3df4622f0e9fd90c83820a710b3059246c6e621e68ee3f54dd0be02cd3ef6b8f7123c339c99cae1415bde70dcf80f8615f3b0d1edfa80c36b4ee4146ab9c5ebbb8a98b4b06b8b9cfb651c01f72f0bb08cf0e2280dc1df8c959689b318c85bf26f36d4bbbc4af33ea8a1ffaaa7970d21807f71e356534536607e1552c248c5a2059837c4ef95f0a68b93e98896d8042c4021f37da1bd996516b681d2e318c812d535e44e0bcf03a3182e5f3f9b130d0e3563b5e520c269ba709823784272b111a0aecdd7fe8c716a94acca97f41e0e8fc81ee5ca1e91977ddbbf4c74e1ba5a5f4fa6e88c7eb572633ae3b626c19c09a55f2a44da0fedff7dc0d24c27bffaeb448e90079e22d9827526a05e6d119d086ca98786b81bab9a975ecb97186697af5d18261bd74334e6c153512a3c9fbc07eb9463ea338eef67fda11fcdcf32693066e3b40dac22b12da3bd874aea89ee0676090323d26a5dbfde226b4c7958d9055e8151bae38d0cc23f443d169f49bd0ab79ccff97b47b3660b41db58351cec4bb1bd93589d1520788344bcd0a0c81f0f270679613d96a024e27530e45e96456833fdcfbc4045c2a93f8c43798f82e000ccb6f485d88d592765ed40c5775a9f6c8e59636d2181ab5bd07657c856bb9a05fa70638081a029acd938887391d4140cf35218382de51b58dfd6549244ead60a33382bcf3d1a98d7d644216b59005880927f4f4b592199ed9365b0feec5cff0f +generate_ring_signature 4512063bb19daa1ba31eb0145d48f7dc9d06fd806ca89540becd2b5e6826c1e0 89a8e66b8da6a4002c7cbd2dd887bdad6a08e3412d1972c1ddba31be29ad779d 4 104de0915736fd728e8c86d2458441d876f459f995c7256a276534f438a614f8 05fd6561503fcb29ce98b70157dc619b5c4aba732aabb0b0d10b8298d1f5e155 2c1a30eb5f25ca5ef00b7806c934794f03dcc5ac58606aec2081f9522a216749 133392cef31dc52c7de272824c7d487327b3320b2eaf1caa827083872002b16b efcc49cb457ad9394617b802a3be8b9292ed7e88c782508899ba39a152e73200 3 c3b6e40232ba8c6aff262f04bf4d4668dd28ff8356bb8615fae335445cdc4e08fa93846c258f76146d969c380a591c88dfabf9e201969a94f250e4035c66db046a373ead5283dfd066616f1295961a55d24af8b7a75384ce05ee3c67103da309ec4c2a1c47d5f92aa278b1ee3bc55ec919e394b69687e756299aeae435c20b0869c137c6c933f2bd088d5180e580f497e25ede00d2c4785efe9c6a5eada7900087147e6b6cce9666513bf97536a2ef3014a63b5a18ecaab4f7f28d98f561850250d2e477dc42f5b6150c4bfa4681bb9e90bef930ffd760e00ce7182e97479e00990e014373710ece4bf82f3c76b344910591eb61fb80ea09aa754ccbc4c4160a +generate_ring_signature d4a171b6e8c5a7881d47e5e059129c134a12972e6cf446760d5ea3f5d9d8a526 4995bf794e131c0a54e6b94218aadb1062967bc29978ef1e04a5ec2a622bed38 2 8a1e660522cfbc1da70e1f269efb738b9efe941435fe7ced85bcb8b2b445015a 52a0a2d5f69efa6666a07a177725d76f60a1fa078e7328132ea81e2a1aead5f3 7799976d0f2f488ab7dfce43781bbf6d8f64f4c17c6dc5e357a98ad83f23a20b 0 58a9a6e5c44b00e1619155aa12e3d145ae3c03fd0eff58c8422bebc7d7cd99021039d15e9e448d4ce5aa0bede8167d4bac58c847c403fe9f0411fcedb6b1cb0ea8715857d597305120950eddcacbebd54a2bb7f0287dade22a9be012eb84f70dfc1a0b459b65d6c14ae09ed0e7b1ab4466f5a7623ba746335357ff157d670f0e +generate_ring_signature af87b1fedee0e438f10aae5befa088392ff4dfae1546b8c8f779fc50ad51da97 90e690a1501ff3a3fb14070bd46a4b4a7175bb31f57956486ca61f2e49cd4ab0 9 35f64d12ad8dd1c71084709e6e868e68b934eb407aae5fcda2505773503025a7 c80dc8d3d3abb474b56a5c054df286f87f5c58881451feb22cdbdae8f3f900a7 3461ab681e2e79261f83d37d82d4982892320fa386cc6e6f5bed308bc1c13d12 46d70d6342274b6fa1e9e3d5b5de51c4627000ba00432606cb4aeb362e25da6a 2b561ad67dd4aa1e38a5fe1c8454218127a73881a206b24862e136cf2b255780 31053ebff5a815b078f8e94d9f0f044f1688b0133cd883d479a94ed066a1d1c2 968233788adaae5fc7466d8390d94d6198074b114e2ec9b52c4d851ba6832166 f668b4ef8431e75554c02db408f359ef81436a0891490210bd509923a7e0a749 fa4407f6eb50927d9c17ca60b21e8129a19684ddb746e4c186b88ad5113dcc86 020bec4c5ca87f397f5c0919116c54fc4fa83c1a650b045ee4385a3d7bb40d03 5 4bce93bbecfbabc33366568fc36693b46840df702626579e62e9516f0e4b9e0202ab6b9d8a6f2823b05023fac29ace4b15b5ed8b8c6bcac06da835c106aeed0c1638e5b29b5572b72acc82611047287fc2b3a59093cfd318331e6504950437094dbeb8961078eab9c99d8fa5bbe5bfbffdd8527c621daa6e7dd5a1a0442e240cfcd5b5868bfa2da07b12bca77492675dc42cfcb566b34a359091d12a1056b70a7d6e449a11ea39fa27d7d7a696411c95f10b6551896611b1d4c4869ca2ece20364266d3b028c0435a1f136ff012de7c1360d18111aa87e7f12aaa71f78d2d8010fc6b0c6e87def7a42d42dbed8834da0e67aa29d84b721396fae096582c822068ff9378df71cafa852910d5011168379fcafa138bf745931c08de73a89a81806c8b08f388321ec17a80a34c88fcb33d38760d46cd3facd1d8f22d511be64af04563fc7c8b44012016059834897aab1bf30b7c779974813b003adc3104cb9950ec03719ff04f556a8a1dfde07d48a6f1b10147f9c035dba6c2a673c3d9ac1d701b121222acfbfd61222a230256ec665ae0f9d83f85694ae5b48bf2d0c2f801d020865529a730c1385a82dcbb77e7fe21421f5dd0b15340bf446945f101b3cc50fc17e4620dcda2c8f46db467c3e3ebbacbdd9758a4f6f81d1343449e7535de20dcb984a9958e1ca69466298ad207e287fc363c19a60779bc9e6b1cdffec47dd0c8e4255bfd134463693bfdd636398d148923dc78a1d53248df34db30bd8524500f91f907930cbd92a38fffa0452d7bc42ad1a025aa8e8b6372617ae071f3e8204 +generate_ring_signature 2d2c11cf68486227b17b80fe6353ae59abf53ba0b8bc75f32a61007b557c53ad 35e426514c73e7c66880bc26f90365d770c31fb753926f24d23848697bfc5a88 57 a9919f6a2b7acaa39c078c3b24e3487a89c1e98f72c6deb29190efe461915b1a 97078cc1079d661dd75108310685f568c813d7486064fc35b2f69b38fe6e94b4 d6e58715307fdae6fcf242a4413b3264d0550ada54a02a233e3488d63dac0307 a6eb853278a50ed8333786b3b15dcddd3d8d784cc647adda671f710a3cd46bce 204fc5f49b9e9247b2245d0b29b32a697e6cd5b1c4347430cc2002ee6682551a 19f3496ab38450705b89e6ed8fd2c725a72ab9e04daf81b3e84f762c6f368858 cdaab9c4d1337195fa27a23ae9beb85e1b421162b0175b01e5022a8a6693a2b5 da64b3606b987f43092b844e787c73e147dd3f1401e5d4de45e6defeebc4c542 8636da56ced937d7942364cf6791b38e8819902811b071d88a7e9f31c9158318 24d943b065ad70668edf4dd47bfc174333547e92fe95a4840aa1cbe343ad8c7f 4f633908fe98dfeaf22d5f82a7bc5c591f68f0a5c087df556804a218ce5b539b 550f1304058b94162f04b4435bfc4190006262a55c326750c93aadc37c491239 2a8a480ff6b6e352d3eaf025a8a2d5f7170e8c72063216af052d14050d68573b a7a57ab7e2dbb84b1b687ce4e37c966aad92e8d63a80a67b399c3bc1775bc570 b1b394508b9e74e44ed21f6f38aa8844ff16856e1179b51b73968582babe8570 fd472fe1eb884e3f57567ba1ec8d6a3839e944cab78e639146224f141aecdcfb 5ca43dd0779bc74b551aa2279219b77dd51b6d84698949e23a5fd73663f5e5f5 4e4bbaa324ff9e710ddd5f5e837df67df9ba854afba57819b919b3481fe374ff f9f50fbf4109310755f80a3920a2bb47e297e4420b5859045819b8f2ab68bad6 4bc0b971d4bab80d4013dd6cf6363ed852b6b95a94b83380eceee179611f0126 9f5fb2b0052f5a19f77eaf98b94282e1f62d09b3dfd090074ba6a99d7d3cf0c7 570157fbaf6de931c767a74e01be9e00dd875477c18a556c93187cc9aa9f51b3 5b581778bb48ba15fa9bb4e03524b5206268bd2ad52aaed0ce60ac577cd3ef94 00c51c2ea99904e5d933635fb1715c3abb4ff07642aea7dbd46624fd9f1d825c d50322d0ead2433b3977ba3dce5964d17979cddda84a13acf5f5c824f110cbf1 bf25117e9f951431febfa8370999db5159ca8511fde188cb0166624773c5f0f9 4c64494a15cda58cc9def65c4746cf88207d32d24b90a0c134dd0ae01b09c144 01032bfc7e3f6e6b0a30a3d265e01e385771c8886aeaa6948965c05728b056a8 060f286518d97854b81a3ab11a55141d4edc97bbbc93fb0bfde999234b05400c abc25998dea92a2cc721cb7bca4ec8890b18d31fe9b130e706e9f9c231933aee 0d462aee190385626534a8642fd579da27e8cd596f90b3d5f5e7fe55407bb405 04042ded8ccaebe4ba0dc9d67fb8bfd9f51c6a43f4dc154f3858b2f34624573e a8c3ee54ed71f11805178141dc71af46d0218cafd72ea5adb3b54ebfbd2ba1eb c4d103d2800689c4b1f2b984ee4b0565f5e214b8209fe622db3f0b8b8fbd3dfc baa5fb8c4beb0204f6be08ef6aa3ea2351a81ed5f169315206a089e7abe8e691 9f69cd4518c0732b7e0d34fdf8fca4f449b391d66986f4032568375532a596bb 2703488d85817e84c47dbfab7d96a741c5603fd38674f9a14dbf9c0785f90a3e 18271655568f950df4aeeef27efb9c59680db900232da356f3bc940e77a661b1 a260915c171adc5c17beb5b39d073adb213ec6206ca8cd6656ad0207f0675c0a f3304b1d21a211a6a64be267512752e8fe7e4682fdcfd42676d4fd7fc49605f3 7910d249a3c64181a1737103cb8a0cc85dec99873b99169cc3995662ad1c2397 b19fac0f51b23d973d0b7daf502321f1abcebd28f6f8abfd75b377db40a0b99b cf3cde5e0b30b42c5b610d5087e32d21bbc819a6ee816bcaf928ba20c45670d0 64f3ce114419a4bc0eec99a1f65fcb2096c94c65ef8a5ca44209c48b6f364045 7f757bdfd7cef93d1ad0b987041bc741ec64dc3cd78293506a5bad36e0926d2f b0155d119bc08df378af49e9eaaa856742bf7783f3ee16d9d545a6064f12acb6 5b29fa21c990c8b43186c1d6d842152a2fa26eb6c0866c10014baac7c82367f4 6fa38c3bea221425d2fb979f5666b6b0214f9fe9ce0abd6d39427dd22bc20c6c a6f86b5127deee81e39532d897d37407dd7348beee9685f0cbece5decfa5d3da 1827e21b68bf6d7348b325fc74d8f65289f30a38ec4f84bfb126d3d94a6e8cef ba62987bca2eb991d123ef903a7377415eb9ac77f244042a87b0538e233fdac1 be54b474591ad0d58a89c6a696abdfa17005717e6b9360c34cd95634210eb8da 78ddfa3c43d923ca2037e3774c9c4c8260cdad274487ff71dc1f5b739e0ece84 6a2184d5c8013feb86b8fdcb6885ce3331320d53f4c02245bcc90c34f4f29bc5 db01a4dfb62b753a5ce1109a566604b9eaeca13064b13ac2fa24b4bbdb859236 de3e15f15f67e61b3f05022c387ba7029432f417c1ed96377d0fbaa9b9474f4e da88bf9678f4d1594a8899ebbe347e7039f5e23287dfa64d5809f0e70a359f2e 098a6833175b819831237a5b135e325dc1e10b0d348a07ca831c60ca31862806 1 4199f8d6909ef38d2e8b6cc6802dfd1ec0d06517d3cd0293c18281e2b81a0b0513f67bc2b37fa38522d657843b27a6cf762ad6ccdde704a1fcb18e18047d370a2d3b2959819f0f1a86234badadcb19ba33c7c7a5be90ae3c834e360dd86bd50c0a6a454360674710395393d161464f7407bb47918de45b965614e1e783b0ce0cad263b58586f566f7fb002c90ae537386e7df7ad41d8398fb7ad0b7ce6b64b0d52832acad8c5e6f25599d4e90079c0f0be88548cd09ffb976b3c4e87c7d9ae0ee1d14675476b15cf87290a5d1a0bc7e2376c7c594b727d1d0d156996bbcb280d69c3353a6eaa79815ebfd2c49c52df4bf46fc256005023845e4bba0750ca80070c32ce3d6549cb135f05bdaa3de1730cb3d1793a3907b5ce1bda53a317ed3c0b05f780848676d2df00a812b2065e02910bab2660ea9525aae9fbce20eff9a306e80a54b06297c79ee6e29d574d8d5d5a6a8e6e7f32a74e13bd007d9cf247f40a116a086c86fbe1e7bc2e0afd9c3e0bbc3ae7b04379ea77cc55c93c279903d10d3ff3ed6aaddbacc75779fa9146892a2d1ea3901f19fbe83c191e634e62e8660dab541971156a0d7e483471a40e5040a3b75f1ba6df46abfef2ff2ce075db8d09065452a13ac563ca9a828f945308a74efb39a0505090f01e02d396eb82af320b14eb31120fcab6efe256c00896cf4b84ef1b6ba19b5691430b6cede209d6a90c63f9810f550e346999233ad550f5f7fee6a55364b26e51fda1cc5804b41c0c0ea52909a62b7f00677cfb7cba9c18f14336c61436a458478b7ec4e06c41f828013f4f7559115967432a4878bce9f861c377d4243162c601177255b30ed8f2720915581c12ce5f7a3f63c4ae2eba416abd39523e85279ade2532a00437f6130c03e4ebf29b8aafcaeff4908aa9b587459670a70623b9d56ba6c1ffe800e17bc90cf5a926310d71f4cff9c117bf5361c6229f960b46245554437b31a8bd26de2802f47f15cdcadeeae51516fc0cbf7d8de2b3bbde11b45c3c7c5cb6a58cc564a70d377cb8c1dfe198a2f3bd9569c6a01ff94d71f3944a779cff6ef278dbe363800073cfbb463b0fdae43f7005111fd85183028ee8644250c9670be031939da7840d08e63e81378429db095cab27595802554754996bdd2fe580350800781d64b80c59b87a01fad1383a5467c719a1c0ac0c0217401b10153b476c3ac6a818e80c0228b215a75ef433a597ae3292dae49d532d1b6812ee9291f87f248ddf47e99a01dff46bc464540adbc59cf39fb12e4b2dba9c64e0cc207a78d50d676e37d9630a575d1c5a8bd171d7211f76f00121bffc602ec6cb132a431c1281ab99e9cca00f8b653d76ef6c38b81a2f3e8092d1de12e02ef2d866aaabfb59811921651df206daa281d1a854b77958b955c68fd4d536f3090a8dca403ece38154f812940d101cbb9e4d85b2d796e84c2124b4bf364b2eb88bd80daf50300e011ee05fb132808493ed12c5752d9834f3281da388bcc75ba5f9b8cfd709ceabf63d50e700d8107828c6f5304fb6fdbffcf874cf05dfe16a7b2a606d739abe3f991dc68a60fb7074f32a9cb2affa4c1dc92a95b19bb4868f3ae4bbf4a0629e3cc5a46dc259c0d00ae4fd36e28036f1e947391326644498e1ca4e7a8f043057d455e53dbf31d0f0c741407b5adb73509e838bb156f801809be25478a7c7433283bae5a5a3864dd0bd49a0a8e9d0cf8d2d0005d6354758e91a66f45ec95e74861195a156dd0b36f07b8076a14281fe3f5c42a18928bcaf61cd064b36465aea87b7aa0ec75ab38f10a4e6fbf370031a9428baa85d5553e3ac0a1b1e4f56ade90d7709cd4f12a4c9b05c02511ac95010885b8a6001ad6c46497ef1c1810d80839662242fbc65c2b700a82b2b06afdcd6c4d253f3a06850ff1c1c04a4879711d9cb7ac593f53c04f5303f03c625938d81cb575e74921534fc7023792bbaca28c85f456d6c85bfccdba08080c7787f2e66a5dd660f21356a281863eaa5f1424312739a65560895fc0bf057df124c8962b5ec2dde027f46c986ffc1a427dd428c73f2575a3dd8288cefc08a062a3c67240e41978f3ff93436f89777b58fc58805e60f683f9bd6faa864e0126ee980fd563b7c0442a0cae99d33565a28a7372469b371befca5466a3846000d87056007539b2f0d79b5f413a182af5794ac9b950f1981b36bb9ba50c376604f2b7bb892a1317ec666afe4b2eeec32cbb7a4044e944d718296e74c1426d940d7997512871325ed1fe29718800fcbbc009a5d93da5c910e60dfe1b2430eb0a04e558e4f769f93616b079c1b5efd25db61c5f71d44f0346db37380a7f7846c60d29362665b7474cc37f2657dc2b7d8e495fc0e8c75158c44a365dd7deebe16b02e36a6dc11b4fbaf9e2d74497ab1978052e26360f8a07c3ddb70f7d73b11f0407fc1d285bb960a853f3d7db8fa5a9b2dd07a664fb62de2ef4c4a5ca9ad79f30046fecf542355b6dbcd761e55eb3ae9ec32b969f64eccfc5ba41c1415d0763b90703eb78735c7ff5372a65236e1043ff08e77170487acd3476b82e5f0f21dc1f040c8460813abf7ace96d536e4061b64d08208dd3855d3d77ff296a0193cfd010dfafc42748e8f44bb89c2c385ead56cb0a6500fbfa83aa2d33c184ab37f2ea307ee97f0ad6899e1080396c7f5244d85a972b7b5dd7b101a8c601ea71e9a56ec08efc784bd9e755bfc7081763b2bfc3241b2382cc2dcc746c1c2adf2e0cddc4e00748b299641fb1749e4444e1df71397a96219d4f371edf754ae1c3bb859130d031b73e213d4d533cdbc6dc0702476273ce93127c95e87f27303436d3ec424870d58ddb13cff028723a2b211d5ab87acb3031f0ea5a03b3ce14199283dc2fafb0a8b8944d0bb6a8ba068b6848bae939569b554099a8f69b8e8aed526a4227dd90f2526c3bf44a34b679a9eb033df96a0815bbbe911c1171b4777f6ff6bf516c707dd2c8e59bdf6110df9f8ba3349e23deb479199b5d9998e61b3e78a57032f6b0ce3750f544215a14a72b434246083314684946b2dad612fcf550ae0e7a60343028c3384754a692098c30d38ea2100a4319067f73349a8156b94dee3a8a6baa50c8a02a684cfc3882cff7548cb608fe0f7652e7c1502370656656552a0ca7ef708d3ede8f5e472606a2b92de0c19d40f5fa4bdd6e24cd15bb0319ee66b13c8560f8f915d065f3542d6fc7fa4b28aec3ee3a92113b75c7bbee2ce7b85caf9e6fb0cf78c919c2c372535678f426be28a1b6aa4c878589e985b62494f52a20be773058c5a4319b5c107d135151e0567345139a3592e071fb96a035d8364416f8353085a4da7d68093a2a6adee33f8951f8697095ea093b5a28d6a2a6a1c09115c920ed3b40825ab8755e2b1a3bf2a57662aa3dc78404731ea989d385b674ac4ae8f0cf3ef4b51b8c82cb3bfec29c12934f0922e45b5ae2641788c82f9378abdd21506f5196b1f1c4e15f36c8268c2865681f266e98a616b69a19882d816dc5ece43027afb68d919b9514e18ed47fa5f9906686ac07b6e15642d7acb17f1ceb03bb309d70be29ffc8f714169e214148b628e014e9ba5a840ebcd48cb45361332f57109f69a1ee2cbd31ae2b1dab0a86269758f5ee68828b9a506539e83157be2d078001a88662286d5ddb163f5fcff2a305132f1c643d644bf829fe5fb2d1d87fe5b0df774f29b7ddfbacbfa290d638eec8e4087ab74f0d0acfc9ca6e19cee44ca9703c39867bde6b7ac6a654acd8f799e81f551b7df6353f844708ad15026a330410202cde7fb5eaafd7141150e89af6e23c31cddf13f430cb7f5e174d121cd94c10a2bcca9f833521405ec35f9bca031710c4405542d936a4588295ad30536aa8608c63f55adb8bf1b5478bf3c8990d9e367786925a2ade306ff8a4f44310bcc7501594cd0dfe9bcee0d004127f13c125ac774fdff5ac9d975e1b773a236dd02fc0ebe3e81b7c3fa5e104dedb761831388b49a08559700eb53be744cfee4ae2fca056e78c14495b1a1be73b257bfcafb3fcef8ec6410bcce57457355ba470b23f30add59d49217b65f06b77dd23bb70a3bf4cace891a1c8ab20dce3eb5889c56db0a912b27a53c3e04191fa516cd32f3d48f64d61e316dc4d751d4e042c07f48ce0de119e10d7ee53a6468d03ee9acaf0bd36219c1c83f693090c039cc53befce1065901717de9718811ba5ca6ebfa807ac0936a5d552670fdb141c12f19911bcf0afd846f5e476747374a516af468711dcd39fbfa923f061489352b4763fc554702d737d75c6b70b925cf3ce8100069972d25124039a5db574b4f76929ad3b4050a06712d01f2d14ce08ab83389ac5dc8798277622fcc698bf97a36e13cd4cb950649193b7ed63a830159a33585539f1d22d1516659fa3891f67c281deaaa888f08583bab5e9d2f4eca6cc65b1eae7e7935f6e4e85f709207d4cac1d5394964930e37874c30b18591a9b420c5e0b460e50f8cf27e929800b8006f2be503763b8b092825230a2e884d2d9a26c140605fb7953096f59968af356b661acb65cae151071996777d3160cf40204cf833cb24aba69d73127cd5a6f53e101b293c3f07df0127c4d55cccd4d1749cc5c9612e128b8e108a0c34011687ec96b5e2cde281e60d776aa652d7f08e73b93512eeb14e14a60a93995fb4518592f1eb197a879dfd02afb1de3926e2c3141742236ad35c5e920d4549a2a8d6a970b1e1ae00db9bcf04f5e0f08df98c8f7432b531492c926590ac6136fd56306f8efe7043614ebd17044deb383d2e219fbee78f9cdf63825ae6f757561654ecadd5b8e28bc4e7dc3f0589b26005d4b159a6a5d8d27caf9a5507eb7aa3f2374c0e2656a3d1c088341106d1c38c1217e72b155babd4a67d301b3a109cf932ea59fdc27811d7c0c4762a0956eb4940c88d113ccc65681de6c854102d77a82c6075f53b7966d5421c424b0a8a85f1e5fc72a59338b59cddb3daa816bf6445f0ef0c02ce87c273d2e5de770751328d4b5869440e0fb58292223886ae2de3dcd35a0350eca38ec536df24330d83204fb31746c6283c8c61efbfa7d99e4cb17c27f3c9572014384610236dbc0b90ebb3c4c6be726dec00443f7e7424abb4edda50598e658b52235eeeed929302 +generate_ring_signature 5f538e02b1461f07c98077400e3cf540b0626000edcc0ee4f184bd1f72604f9a 6be7e0a471ad7fee54ade0d6615b22f36f5ef44992917c3981ef7a0a4d561da8 7 4648fa9cfdae1cc58f504ea9031acb69b269d148e581e01294730540f5ff6930 9dfca258921efd4ea725792ba16046d64831fc572f3dc72ee4fca1100b966ddb d7f734b73581e4fcd011a57ccf613b25ab32c3e61dcc3aee5b65ee0d118f3858 e11aadc5cd441dd0c48f584c8f92cd17d1aff66ddbb7f86e4ee6deae1870f0c2 9893530b57c30c46ffd006059cc5ccf20a35632060107d8ee0272da1cf219b7c 0f5a132da48e2f8dd651651c00026b9ae41d909503fc5e7b64c808201a06d2fc 69fcfadb6d9b4b296d86583324f22d124fd6764ce0dc6331efe72adef2ba1a26 02043a6d949d79e1a8c63c231e3db3bf406bf741d9e4a35b14c41e9813e90a00 4 2ff1b37eb77c41161ad4554270d6ec2d11c25dbd4b36d6d943429fc4e4b52902b081e532196f8597b497d578828e3f579a97962d7ebe328da5b636628bfc5c0be8dc2d0db4e88cccc1e0cef2c384e43c835f58cf2219dd3de0870129082847090804eca45c7059e07b9e4febd2c77c3bf02f5126e8921ec357a36d5fd46ec30e245865416799024c61adba5501d1e6a1810e27ab3baf2013da31949566b397035ddb58031ffdb585eea0fb301b0b67d739bc80c86ad4ec3c809bd49dec27100381761103e82118cb185f2c07a2a64f9676de0e0534205fd79b0c9a968ad9af08ecc95a4d782cbf56b15b7213082c25fba5a16f3999574dfff4cc9703792d6a007943ecea1497d8114b1dcccd008e61c2cbb315444aea5e9c09d9a9f4aacc370d3df96a8dc4987c11f20476ff2f9f58c0f50ff3076e3a4e17d1ad4efc8ac1080edab3016868bb2e68f0c0d2aee37174665d2783a95f94cafcfb448d095c89d9098ec327a99e893a1ddd6b52e7861ac018fe68a2dbb8e7e1a03fea94af644e1201601450698ed41e83ee306bfbe5859c66d028082c4554eb6b4394c5fb9ee06102a8f4e4497abb3af7b99264021ca8d2de203b37464284f672299b3442c7a2ad03 +generate_ring_signature 39bd24a7043973bd7a7f4c8e448e7787d1e5aab98f333b25391f624a492c1d35 e7551719351d2f367292d49a58da045bc9af52dd31cb565a2020f5fd3333728d 253 87211d95088dcae2b1d8db1e408b1eea3050193114a62975b8228d69364c2e16 d8080eb5ff65b1e88262670190e68166ea45b3b603b37b6d7373cfaa636f165e 241d5a6bf98df975d09c3fd82f28daf686e24e8d577632498cf47a80742fa4a9 28044c0d9ec190be78c95859877fc3c71a5eafb944d87af6657986098d5df61c b5380a0808e51b9bb85a54c703ebec3c1dd5d00ca4c776d3a4d88a3b15fe13a9 9b02e613f1bd267ab8ba9f17e1f44dd254e539ac7c4ae51db640be7d43aee526 b4dbc4c9bfc1aaad51e0a4605f40f3e4d86bc7483e9034c60412b099eb36dacc 94af0b33beeef150aac1ed32fab925099b688b72a6d854115507e893d15663c2 7e8e137d9e882f0fb9e8eeb29e607cdb113578b086780a0728e926492017232b c025e69848c70343a238a934746b0dc0edcd2d545af7003bc00e647cee2f635a 80c47dbf387d8fc014051cdb257585f7b85b08e41364e3a6267652db57967cf8 2f16c481aaaedc8ba701a58363f4574d21fbe7d9d3b225c200b7fa9725f15948 8ea6e2ff9ce7991d7764a92cba2c865f5e2a5e258588a43f76e17e41de039808 75ba819f4fbfdff82330cc7afac927e72fdd861cd9243693ba5745891311fc15 23a6a044fc33f8f2e7f8aff58e94283ea45fc8202f1e84c71914073fefea600b 3662f25a290356e28770c62bd711ad6ad929ecab25da3f515a3c053e6212311a 192b423ccbb0d6d329e42a8d0317c8a00db0e5a2decb455b79f6b7d9c0737c06 054535dfacb051e8f8cca57c43ea456cbf563fc80371b8ab750b4d9e660270a4 892ebd8b4d5abca1c059e3aac1cb2543194108cd999debe33e7a65d112760d2c 6627c6b5e09c06f9329edb030089e2bd60212ec499bf9e00ead218f3b4c4a94d 0323b5f1c8df8fc71595693abe6a5423cafcc7379ea59a1fbae58b1c820f5f66 8cc337574d514c3b74794eeb2cfd25a2b0c10768e631bee1c08499ba34b08c50 42ac8825861673461fc8e350040863a663fc03a8c7822d4d97ed3d80369be618 c4e5dc0e598b2d75087c6fe50b411588803f1924ac9cf53882d952b6bf175e20 f93279c53d909208453b55837953825d8fce2dd9d88d98917e7946016653d867 094f2ff19126af7b3c0f71d8f0d569edb7d4ab103a7b541a85defdfd3f516473 00da2bfcf3f2f224fc3536837ca93891dba7254213f5314015eb6be4f365c6aa 99fd38e293ec436c242c51b8d7688a1e0da8b8d9498f568c8c67b7fc7b03ff35 e8b3c064790b8c6004889e828c28e4a13c94de102ff868c0cb30fa7a1b310455 e009ac341d6cc6f400c9aa812eae38b983ff1c3950ec0ad32f54a637714207c8 41d6e1013a167657aca3c785b428458a53751a3ce4f55e7f69d1c2a732dfceae cb6646dda6cc5d0f46c3eb31b6956d944b63687ffd3ad12f0d58fe4a1412021e 989c30d40c19570e0fce6aba0e6789ec2351436a0deac9f4bf373a5621583610 ddc4aaea5c7d9b2619c2cc955076f63ad67beff0f57cd678a43b6aaa935629c5 7137f11a9d15db1ecab14ea167381fa81bd092040a9bce49661788380f5deeeb 92670f852c2cd07a97830d3f4db9577d9e933d72b46524dd05c65325d9a2f875 d053e29032574a0eb93bd7f30dba833b1da8181e2093e3b50d28a5f39dca6b6f f4e4496e938470ad634e61275427d3fb275fcdef514164975d93a320f5882e88 80e0024f178225cb0b40aedf698b8f4b85e413a34e29df3e3adf9bab15296e0d caa36897b70299c47f3a0cc3aa7433b9c5273e803dabd5986f84763492a41a90 7cd4fe492d5fa570d769270c0cc8ebba8fb7253dedb141801482ecc5fea15a98 a2a76ff27b18546a18b6bbbeafb1ece1272a03e2f147e9bce94da684f018e322 90a46155d50366bdf2a2d7e398aea33224a295aa3455fad40dfb4515d365d60d d08d9934354be29e663f1c8e50b6e3610d8fa15c10e12feb4cbfb1d64a07a059 8555cde97863f54e6b9ed852e3c2a26c2ea56d815d710939d89eff70153d579e 265617d3c9973965a217a9cefc0200f5300f3ecacb1487add5e1bb141400147c 6a45afcb5a02430e57bf0af7572dfff1841b9740f5e5052540db3dbd64fe3cc0 1744e17c9cd258c8a7396e7d704bbc4c6c13b06cb92bfd176ba1567772cd4ae5 9236692660d3647d50a61c688725baefcaa8675ee933596c42886d7e57d81b46 a8a82c4536579ebd4e35bf3030b1d18b00f3b56befdcaa94e6e6dddb00b3aef1 eccdd01e8deee3c4ea8fffc8b41bcfe089da7fc2d0d250c8d81c790a0a340127 a1b33761a1691e68c0ddadae7965b912a0a2578b27eb6e5234cfd1f6bc175dd5 65df31e331e3a1f31f01892821485c796e18ae9be5046c567c2f9f31edea5eb4 f51b804b2b4c7047e874db85fc1f827c78facacef789bba5ae0bac362a8719c3 cefbc7087a409c7bfeaf01713590ab8ba8cf200f0e253ee24513acadd14eace3 5e24afee28b5670695148232ff4ac7aeb6d028430f7e52ec699bb8d2fa423543 75ca63027ccfa35301adda5929b6d34db711287e8c20049f04682940a2574cbf 6d028deeb9f33d1e2dc9a7815aed9d46c8bc5ef5a24ee180c51b992d02447feb 0b4862a56199782c3db8d96e126b52966d8857c030bcd12bf78dcff477de2dee 6f5a8b4bee8f6d5f58db280c52b72b24011168a95f856460018c6e942cb93018 a8866f4e5582371713d07a92b9db45fb32f27fabdb45fe78b71175af9b9abb05 f9abffce30898c135700ac2872cce9820f6771733cfa615a271529c04e619c2f eee59d2fd563cd83bf665f28f327f28f2236e0d7c72e034da73634668014a050 22a58e501959e7d96664eea0a947f95643cadab389bd257649bbcb75e52b101b df4b5654604c4fa3b69a9303047f39149a9ff62e5294ce1cb9f03a6599128759 892b52449e8ade9f51e72afa4d16cfd61025a0b852b4aaf0b62771ca4e4d1c03 e922bf917ec5c7181a864a2b0fab689c90593a47591b3ad0ad442ce8b632aabf 09622f5fb64ce4b5d67a424a280af79dbaee60a557cada15a72045f27c839617 791107d5f62b4de6c11a38bcd84a836ac5799cdb4fc736f1f5e14f8d4bb56d9d 3279edd7b2fcacab9810ac647eac12ce8b16994829f283d3eb83f8e13a4d1429 739d415d0f7d8d65675ab5dee02fba52b22baf769fa331477768e8fa21341d4f b6bfcd65dbdb070b48930d06a83706aebcd7be61fd7595c8402da87f09e8303d 9007464c9c734c561094b29f09be140baa1565ed3c09196f399e82b489ca5aa5 be6092d1c575e39349a043b24d3df2a8e9b4938e8ba67796428ad0a7309f808f 2ab34408c3bacd9bfea53991e4a6e69ef5c6bb25978bc25f62244b143f9bf52a 76a04b6675637ac67513c8e6f9d89243f059e3407a7d04d534597bd0c7e46897 06325655016660438374f04b5190d361b605d30a2df103fc4f1acb7e28b7e6f0 5f3db63b41e7c2789a9839ad5fa73abf78d9ee2a6ed7fffa7ab485ae0b1c21c0 481f3b51347b46eabc5136721c1edf697febbaefadbd1d0d72f8f792de0fabf0 a8fb1a7241ea84c5e50739aaea6761d538a042bb33341adcf37e088aa1d51477 10c28dfb14d43cb7914d21f8a5875d6825d44258b8cd578706befdb2bda02092 5fb5866fb47158c2a5bf8abcb52a9c53a2d4f9c0c549d352394aa96b79e406ce d3c12eeb91a1d62781ebb072b272ab1bff02d2fc5c0c8f2af0db54696121c282 d7cf291e972479633e7b3b3c6fb8019fbcc26888e6048d9e78eb63058a5ea47d bf0a952706bbd8adf29a83b681560fb826be849ffbc69d7fada22e90fe83f9de 83b805751bcde4a3174a5f6531efa627a47b849f702d6e7ff871ab792f0cc818 c3e23cbc18a7aacac51625ba45f180e4fac85c76df506553f5c1864fe17c086c 3abf67694d6026a2cdc3ac07aa3281e527594c551319a2e6d9b257b7d46b58a1 382f03e3d26afee7c3f7a8aff90539d0b05521bb3458161f05e472717e050924 1a2dcee09581ba083f476068b6e59c97b3179c2a9759fd5062f6ed03854e6738 a1837059296356f67330d65f3ec669a77140310381bd40b8833038da9ac9467e b786b45a36f1fb2ab17fa149b179db91ac9214a6331990f899b9c9d607a96948 b3d9d31002353f99de917df0fe2a29bbf9468cb39f3ac6f15cd3693b9bec9500 afd4d04039fc38e10cc42211f091880c106b4ee88cf223d674b72691ef04f0e2 779fe4986cc325cf9d43a81527667f5477bc6b0dab67b5deaecc1c78c3b41228 6967bc33b9751da2b33cb217691bbd5fdbc87db54e360c236725df760271a6d2 20b279076b9092772984b180cfa0984e2ed6588a2c970d6a406b2773170a25a0 26109422f9bc97fb8374c815a93ed9634c8140a8d0e14975d7f1e52b66580946 7eeac26b1ba8efe87bb115537a60ee5a2bf6b16a269a518d7c60c3a17b3f89bc 98e996ba603332be1162e011edfec5b73a3a6184918c34d81a6d8b43a4badbe2 f92c62570ca18eccc7778273b29c5c817e19aa5559a5104cef66e4e89dc9ff9a 96ac32b1c4d83ef670005b00e79131cc3bea1253d5c082475e6da0b399b8ec46 13bae2d12ee9264bb9291453cd7019a57958f502960c34e42017c13a0d428ca8 a344ba1158f690728f90eff634b62ef5f2ce2b8485e7ec9eb3d523df9ce9e20f 540926e4afd31183a2d64de8c688960ee63aad78f8265810d24750989af3703a 827ca48a9bb00510c9c89c629ab3e7fa5870fe2493bc6e07b86390c7f893e91f 7724d7adaba57802ed1509ec9030323f08e6408afb4cdfdc05655161b75e1525 a21c61d032a71dc127b1677cdf43876169493bea45add0649cc9d059bfd20f9e 62ed1053385cf71c7f25369a405d4b30b726588341ed822fab9949216c55269b 0012dfb8eebda069b6b178448c062b01df2580dfa68356ea9b18c466d7c21a1a 2b87f48472bb3c7e4d2cce4ff0cb1ae568db0c8731e6c368252a791bf83836bd fb4c4da5dc1f72661f0af5db7637f1b7940d849103c430d63968c320a02eb9b0 9c52d72249c8cfb3821117962383eda8c12b6e3f8d23986ab4afd3a488437fa3 d2aac9ca8f90feecac1c737690c26d3b3ca4cd90841001749b22b16bdc1e43e9 0392f0abb11ce9258379706f9da4703e71454fc7d8d67536b1f607f207751b32 78a67470664d2bbc9b352cb4b14ef9009d9e90bd4cbb0026803d1968a3cd1078 d3c46ae28f78b2aed09cbc451d9923667908a5d43a8e092b630ee4be84394500 89e4bd7046c2f12dd51effb3d2c800f3ab40ea4da49977c1d552a12d7f8aa959 1989bff2c64645d0364924db8586d990ae186ae9f313e91dfb4b3b70d05c8f93 42a1b41f6cca4410e04819ed22f20c9182e7a9b9bd7e8408c41a2eaf48f1d192 35b4663612712abbe565843fc2c8761a1b3b0fff08fbaaffdeb0e3a7f6853638 3daf4ec635bb2e54e527afe6df275c297f7fe217724781c4f8739ccc9c9b2fb6 5afe40899e4e7b817bad34de512ee2077227facc7bf4d3af21ae52fd13526623 b0ff07a6f55a5b48ac6112e029541eadeae416e99a88a2805f230346eed89629 7d6a9122f5df8c327f111e1b3bb934067ef9d0070c3e34681560145cd3544e61 bba9cf327cbd4ab53e16379ff673f14ff209e9f69fa78507504290352b22f1e0 09f78a7968c74f519ec07af117b5937a93445fd1292b3f4c6c048ed3f2ad473e 4cceabfe35704957d8d765ebfbb4b25e7aef813bbb69918e328a82ccc9cd0ef5 4d072bdb1e147c44696d3ac42708497cd14bd5b2053c4231e0f269ab68801d38 fe2581a5df60a4be668cf4eeff3b81b1535b62a8a073663d25cc40ae7e727bd7 201d6fc7a1d66b80f66b2a7e7461ff48c2f7f494ce75cf6a8fd07994315c7d3d 4aacc4df48cb39506847e47fd1de8aecd36644ea9a0a3715aa6410f90bdd17cf 391c4953fe0d607a5fe0757d3b6d89f377eef9472e2df092d84330dab9032c3b 7c0b6f25bcbdfddef802981b87e2d2602e796ddfbf38c612a1ee279575b473a8 7d918f300409fb5498d22dab33d5fabb006e03884a8120342e8c7de43a0ddb41 b472faed5d81a2ad3b5abad7bc4254a17e227e6cf61dd85f808343ca66d04dcd 56046d829e429c14ca5bdf58c1735f9ed29b23d82dc82b865f4404bf2164f78a a777c375406fa5771e7014fd9c7f1195047cd7afafbfbc4e90895f07b25bed88 2c8a6d21d61e1f11e8c4384310f41a6d170302ed1693767879280e6d26a2745a a629e4931c6b0932c982fa5204e98907abe2f36060e8571eac9faf4214983ec6 26b14959b10a5fc133763c1042da576408b7ef0722e74c7c990771ecb67194b9 33c948c923205f23965294543804368f8d60f355d0b812d7ae49100ac3dec0dc 5e334c7ded8a7fc203e7b4b7e03842582d3f87bc6d2e975091c5b1749dc3afbc 9ede74c0b6637781bd4d617a1ce397c5c6d985f56ae0a3a55292139e636e343e 397a7f6cc7cdd493461ed52f39dad6f3250a8463472c95ed9d549d6d64c934dc 0faba779da6568d7f50b60ac6c02ad0abbc93f4da87aa61940a159b4ae7f96bc ff1543693433d98347ca081f5450abdaa0cf029ccd8654b31920dc8630feddde 333f205783f49fa3f0c93d9b97791d7d477a9731131559f998bdfe978885604b 1d3b152f9ab2ede0ca97f01ae350441b4aab27bc99419b2b65a499785c4fb523 67d42536c5b0cae6b6613aa4f2a9655fb108a1cb9c741ec0e8acc47b0f50bb5b 0ac2a1b9379b4eb1c63d742bf7789f3f9ebac237ca1890cfd527762b7f24324e 8ab3bbc165e4d338e6a06221aa9d7bfe583a02ea4e411c5712f7890e87206362 2b570b3e4505e0c909b0b5c8516bf506c47f4a46857255536e9ed48cb668c8f6 c88352c9f9f561fd50106608d456a26849fc9b94b9b6fe630685500c8c0a9374 2909dfaaeafaa66a84f68336277f17295af6f389bad3ef74fa3f998fe1d7aabe b59fe7f21465882cb99b1005442917cccd40b9991f9ddd1c0a5caaec633e2a7b 0af13e35bb870c074cfd20e6488817842223336da2ac422534a4e47b5096e14d 31c21003ebf459dcf7d60d87619e953d60cbac7f3e56a3006f783d7409231169 58bbdc210a0a3c9e6d7a6c1a739e71385d260acb26caf1971161f1ff129cc850 a0c1fea9d30b676be2bacab78a0a803cb91d0d5b7dca34e7414b623d31d760cd cfd7370b09766f1129c6f7154fa9173b72b15492c96ff5423112d81aaab345a3 1c4adf684aa1c063c88f48b6d755b6ff3e234b4abfaf11e669b4b049f3990ed3 10287329dc43f6f23651cb0b6ad8cf44a733a6d62f5e3bf0bb564f9411b8fc53 eae584bd1ed75c877553e40e71f0fd3368fcf5ab466f60fd51fdeef2f3163ad9 20723ccd0d19029a89eb5a705cbf37c67fa7482b2b5cf32b269713f9e82df0a3 4b435f890fbec0afa63df7ff10216141b4cc91f2ef43e45960fa369643490fae 3946de1a98c9dbb03306cb44d1345352df983c0ee81c599642037ec826a95a73 81f7954e59f94dab306d170894f26a64a3ed794e58da478fa4b1c97d3e7f702a 7b17975da7e5f925b7c00c9e49ab67c1c05fdc84ecf7c7c2bd9052fc1792a993 c2a95abe1d8563d55b71d31539718f81588180dbc1e7613ced3cc8883b7a4ddb 4151bc7f598d8b5f51c06994350ce4992cbd5cf5f0af19131f2eeb2930e0c2a6 e6d57d2471095e9557746dc61e51d13d23a13a39c21cb2f5a025e6e57d955319 f6948af76dfc38f9f369132e61275d3931f73cab4be55e0c74c26f3ac06f25ce cac3b7a21c358d6bfa6b75afe0ac3ccb00c8e1e42c1cad83652d21f687953df0 7f80969a5e9ae3b12dae228ec1a9705e9e5cf8822df26f4b2e01d33d01960ffe 7f88d5c21dd827ae7133e3685fef7cd74348ede6ce206f77c48bfbc3ca8d9d16 0137e2fc4336f7f6e5c58ac7de684ed6fbb80b94f9a566e462397185a83aa3bc 4e09fed75cc9ac94c945f26099179285d323109b9e5e6495a5fe70352b47b23d 014decd861cb19767ad2aabb1df2a1a9c39a90ebfb0f9bba99336101a3caeb04 6a8e6bba480e9236ff6d502a670c9c3214af2c2c54d68ae26373be3ea9f6ba54 ee16d722d807012e9b33dc391fa877d6ff8b4c28d8a60bef98407d595fd84bea 01c76b8e1b6a8bc8da70dce6e764ae7c3a5e84299e5ee08cd549452e8a7ad5f7 7f9dddc19df35383423444f76b977bf5f9f8aeca8ee4d12b1c2d47050c4865aa e31923c564f3331aca4266e02a02fbb38082782bf920c73266dafcf5c584a938 e9e1a4257cf560e8c93178a567c31cc5b0da317989e91fbba69e19fdc144a850 d99b31b583a68991c6a729ab3e014e8efa878f4d73428a3e7837b4ccfb7ec34d c43dd8c6692eba9517699c6ecdcb4addf074078005e66901131e3a951c1b0edc 0c4adff4116912e3cddbd8af1e0a54252745b18c8069f00848c86fcaaf1b7aee 71435bb799f392c3fac1d37fbf25cc104123c4200deb7027a25c83f14e1376a6 106deb263504c267e112d2a5a0119a2c56801262370e5005798474cef408ba50 39287346a77fee5f44ad898f17384b4f8d6fd9f579ae9b1a247c0092ea61a459 2227d6bb59ff0f8b917decbf5da7fed3e6b1da14d1acff753c24e8900dc37e74 2bb4d37ed8a566931a8b423ec83989cbf87d6c95be7348060bece8b781706a01 08092eda20bdacec2c77841cd6cc4c5520f526f95d6f0826633bdf037d9d4113 1c2c619b184d8283b6fae53673fb2b157236ac33610eb5b83071d2ed211e0ff0 8e2cfee0c3e50d555312177c397f1f7e28c137f8c66adf4480320f809feae542 91e29a009e5a842cc42d63fb6df9c01d2d166b97128e9e30747cdd6bcf40b27b 015e3a83975191cee58d69290ca0bb424a728c4016bf7d7a8446045f3b445038 0e1644bbae9ce4dcd9310271cdef3f5a1e50dded3fc252ea67d4622f6cdb3f59 15fabd6e07c43e4fd09387cab79bd79d2d1167254e03ea4b2bc14a79d86d3d73 334c7d74257b32240e6cd3715e5ccad9c9854e499e2a0a9a965b05e66a158c11 931728bb4e71c9ea18ca56741a4ce91611399a12d55d24c5e926d860c9671f67 e8f5006539db80c08d514de53e33c196fedfd6cc69d25cd854ec7e620689f605 184a126faeaf6f5a815c95c47e9c04a7944eb0bf9a333ad1e170cc214e0c89de 30243847c6842b53d49252ec2cb97d6ed6380c6a1b51f38aa5c22734eb444af4 4fbbf2cee95f7dcec26c8dbc0c6cd3933761707f72014b23754cff935cb17bc8 9bf3ae0a3eac604de96bcd6601a8c7cf6e4b50a0cdb19564202c56ca566f7e76 1db713ff96be67e88510ada404d59b61cb72233bc848a67a49218fa7eb03c580 11e055e9d596101236c23c1ec2547d92d43609e34f9df12b5921a1eb86d94c6d d2577aa223e766db511f51c5b2e682bcf7bbeac3f1d62688676eae35d0676a18 94249cd701e10680522711d48a1c700f65ee7a588eabd3d14db6b00d3b0a883a d3565a2fe13cac9f6fdb94345fadbc56e9925d6f4e23a724aad18f2ac389d0c0 e76aefa00ff29a4f6b79828490b0cf6a5f1d80ba4b6a261c1e92db2ca2394f53 ccafb3c0c1bf893669cca0d35e6cca664e3506dfeedbe2e9ad03118c97101ccb 2f93eca42748aca9b75b35d4e93605dbeae6899aff2243302b90dc708b619b91 9c5f644fffc89210281ed0be8025d6f26bc199f6acef6c7d540db9074d22d480 73d571d06399b629e06ddb6570e0f8c2bbd1f77e533ae6299f52d41a5f7338c4 15ce90fd29a054ba8b0b6d055618880b09bbd224a023ea05ab2351fea9d803fa a989382b2acb0beba1b03b963333f871eeb5d33d3fe7458d5070ac2792197585 42f972b27b415208048a2b9a889b68da93265253bab8e0734d669999e1aa6d0d f26d0f678d98edbf10d7cc58d02907283cfb4af65ee711da72d049ca2d45db46 bbe344aad8977ce28c67cb5a68877a4d7c17f386a04a5c17616d1180b46e02bf e0ab79f061bf88fdd6779df98d9dda293f3d2795aa708a2de6490ba655834006 88c2506e0a3f6e896df79e698020e240452b32c6fac52bc977b25747f19f64af b4f594a10eede8ad967bdfdc073119224909381b7dccd689adab2fcec0144fde 397a775a0664b932a0d72a7c98af6d08319dcad3c32db270120b0e277d79a3c3 7d63131afe8aea760a530344ee8deb6b7da613e3ddab7f85c3e1f7035769f782 8a71c81729b06a8c8627850015e8d7561040187685d648d0e4099ac2dc090836 5d17966c6bd9faa7cda178016d03b4aa310d78bdf42eee6de5dbf5466a9d6c58 9791a2b9abca7e54d48afe35fcceac3910298b64474e867a3a3a8593201ec83f 65829bef7805b4703ad88fc1a7cf848b38c3e40c0b16a7b7104bcd0741403e56 60512fca2ed6efb77f8299b3b33688bb3e81f71265a1c4fbe1abdb266ead5057 2021516ef3799cc02c7a956d40f0efa1d4f95ff405ce8dfead6989b80b32bdb4 8fd4b02b418427901456dad0cd1ca52671894ad62abfa6f227ee2fb62538aa27 1be3f560260be66bac669acbe18472ee86b338e045fe6821d0de255ea839cb09 d5ae308119a926b3a4a359e0114ee2f938158c61f484dd750f4489cf0e535e10 e218771ec2e03708e7f885f14d9b233e1b7a7d262c56d4f009311a2aeda0ea4c c01c9457d1ad329799a61fb10620f4c0c0b500ba26ff7cc5c3351f1ce90f5b3b fa27dd75891242297128724a5e0f2cb0050f3952b21c3028e29be67af64d7747 8342bfdd59eb7d54a07830ae4d09a0d15064fc19fd4f174427cc33e46d8e535a 0ced66b48f6efd8b0230dd1bbd8c5550e0019d8def29913096e02d39292961df e98da021b8ec628abb0ecc8a6897f3324d7680db16ffb46dbe762c30779e038c 258d59f6081f46450745f0f7c8a5fdb41fbb4ddc0a0149ddc267630c2e677d9f 0f86b6e98a65ab1d035e0fd02262daf83b21c8ff9cab3cdf7afec0c43af3d1a2 25442f835deb1d4ea6333eff39668c84a94e30c57298169935b5afcfc36d7adb 303b1075bc6b497650521361175962ec0385dd87bf864def6f575c2adc3cd0a2 8ad76afc1d698b50abbdb1d94a5c86ccb49b17d49af4d36f39dfbc66ea953fe3 b5218d6cee39e1d1a74e500958b718e406a89c7f468754a744ffa8a39b54e8e1 fdb05ef89c3a19d21e8bb34783a60888a9be53e1e7672d1a3e6f5c6cda4e7029 1aa8d8d92f535ac3bcc0af296a3ffb2c977953ac1e75a25cb65c4f4456bf5ca9 890d1506854a507ae800a7edefe094979769b1e9ef9e376296e7b328830de625 7017bf0e6491806767601a30572fc2dca74d1de7cd3e0ddcf42d15373a04bc8d 71c0cd41b5e501771a72c57a6868c082d4076256afba7569814caec598602f27 376b3198ab6e5849451279c39453c528081337f80ef80e882e954f60b1410600 22 0c9e8f9068544fee2c1de301bca8fdb4e7a996ad3958c2335078618d8ffa470ba59652c9cdadcb367d706b0010e0a38d88d1ba52ba4ebc93322c2a8c54bb010f2d2499194362cf761d0d594464aa63d8d3590b5c4c3f8a0228ec02e9a0012f01b7233fa6f1bc29dd211463fd4224ebe57dc2c557e7b3fc50a8ea38721513f90906485d1ab6cdcfabdd4d599016aa9a0fa8eec1bcf49943292320ca7bc4a1eb0b34f786e8a4dc7b81ca378ccd121c09f2dbba0ec20f7145aefd4868211a3c52040649c49d07147be02435df395ed18daae787448d07671bcfc8697e624b12f1023c5710b65b7de9f62c9e253427b4819e17dee5d7aa2a509854a7d399ef1d5a0d08e01fc2fd942804c21a5ed4271d98ce3686ff2f7c1278dc15cbdccc80168b052ed1a0cba1e14c8f7ba7b065953d6ba340cd56dd8d886a9f8b1e1ef2ac3a3d0bd5009b9e2bb20feab1e0440c989a2e94829ea0f01ffc5dae5b45b2eaa25bc5054467aeaccf6170db78aefafab5aebe9bb3cb240141365a933c2194c40ef38c0eb6707ca694719713d74468eb24887851174a44a53cb8a7a4bfd61e8e051daa0ddeb397c87d204400958cf72841ddfc9cd71268ad962a8b815f030d057348b40d14a599aabd3dcd85755f4fa178b80a97d3275c1cdae97d26f45d57810e75f70cc0ac9ee4eefbd522f56890b3332216c642a7eac26345d1c4572d3146b2b4f50aff57f73e97d7ccfa54e3c276affb495ff39244d1eea42efacd11c3970ba90508281c67476d48e73048674f6fbda6c80c208df1d01c460ed8728947260dc7a80e25e9fd2c2c8155f0fa5dab86d9c2b91c417f18e8e24845646ba5fbc96293d4016dcaf2621d40fd3c3d38e5cbee5ef24ad15c26175152031622b4a7f81bd015081f425e3716c2f940f7d1b73490274fc656243526180f8be43be13c2579caad0c98472be6173becc70707554feb37925c96ff08b9b97de1ee95eb976f915ccb0bcc0ae009b6e8d0f116394e7af83a1ed6070eccda664f038cb4f6779dc20a690ff9296e1f0694ea293627acba003dc4dd884d2f4189997be8ba66988e51057b07b6c9471cf71d25ef0d756f3dc76c4baf1c9de13958174af259d92c47e186cc07e18ade94d882809dd2c577a14f043fffe5fcd7b6944e56cbc675b88f1c77420c92c625567a08ede95f185735389f4ddfc9853e7543bdc483d878eec058860d0c537d8134777b23cec6bb11b7cccdc9092d446cca977dba95c830a4c484a70f0e98e6ea670901800b150877188330eba41c382b14c62b9ca58a65a8a11486d5009d2a01a67f54328c3b009a156b44f27f7ffed30e0b04d38cfbcf091b91e0770e53201f3d5e841bc8677146e61ce3bc647a0acba5d3a940568e0c0e74027a800a1b83c25be06ba0269d9d52cfc16e511303071116c4841965c0de3105e5405e09e7b2dac14b9fdf7fbba45eb948b280d73c83bb8d4266062f4951034e1bd14301e510fb1c7db15e79dc064757d4f4d2a4747f8bbd5fc9d60404bab83c7b34420c5909c6463632342ed87cb3b00d0f18707e37bf1139d1c6e854089e09421522036ddf534c2e5080a60883538b31aef144660e92daafa3daad44503ae5d231a003b56e45558694782cea04c8949dea9ae1866514a84efdf850b1500926feca2e08d4a7c7a7aa1c3a354ad7ac1926087185c19d41df381836df5b743e646c24190f7fa5e3a1c404d8f27655c65a989f97764cb61ffcd0624387af29ed0374b6df064de9a91efda7e504435d7ed70cc65a47b3c5c2e5532453211846d7e607844c0f42ed7301493ddaaf2cae5ed67b5dd1e05bdaf6af277a4001641c19787d6e780d4742ce77456da93ffefbf7feec6a2102063797715ad167cf0d9e5e90f5d4f503ccaa7bb17bd10015444b0a31395fc23d5f62028240593fe2ab9c0c3492dc4f087dc0f63e66a6abd11070ba69045ef80a1f2c4f2cd44e8662330f56ef58010903ea719bc357ecc139f62cc71bc722f585895d0c019709787d4c3d3f86a3938503754d3c9818edd7b0328d8392435f42734a84ef3c2728ff32d7fc84949302670a8179923c4137a24202c0b4ff9e682a5aa158e8172d1efdc523d021ec9f37ef02f16c5dd35ed1b7484920b33ae4ff69655b89f6f30eb1f6864090736761fe4e0aaf8d5cc7dfcac9802da71934f8a9b50520badf21e07dc4571dde08cdfad57a0c9d9dfc0e74ef75b7333fa7349a07485ddd0580ac240e3ac2c78a86d47377700ed83b138fbb77ccbbc17ceba465280bf9f3836f91f8c7eebed4d419a6a2fca40282da21586766aca928221900268c2a6436a1c0d64e18fef0024688402d86ab0abca985171f1a977f8270cc60b8dd5ce454a3b3b3e9231aac844a984a4f151e01d601870e91a3faa786aad7a028f5b3e5e12409838e2b2168e46e17e57f583603404e9327ae88b00b94b914dfec1e54088af2df9d8596a814ba44a0875aebba0290602e11a52f55913c0d20926c23715b97eb54acfdb4032e3b36531567d3cd0d8b18909ddd7000586c003b4ea027095d12d3bd9abb19561f1295bce26cd2e609c2c36d424ab894da43c47cc2deb4f76dc570d67dae88ad482d8d0f93b31a1d0efb1b94e0700d54deb6c840d2db4de8ae397195e29d819a5d4ececd13a5f23f0a5bfb181ffa6fcee5d86333abfd95b691d0d951f8bbe36297ec8607eb00e6110153cd7ad846c15003e8c9e2171a979b956196c175dbee74fec593f106a0219302786f7fd0b4d9e5a9dfd944a1513899a0414ee184ef86af17c5ef7b5dac02ec0d820906640c1acd6464103255ec96f7a76dc90f1bfa65dbcdb5893b979444e609ccbb165a73dc70f88abffae3e1a3786e9cc75840d5243afa0de2d835683ada048524bceef407639edbab890c6fd2695bb8faf801e960c21bb1b5e23aa2e94f05e915c1d88f296a34c4fb88eaa3fee002b9247234a65d2b3d485ef784d7bce806a7db21f14adbf56dcc3615d95a0671ce0e973754924c5ac891e4029523e9e106f00f39ad39afcf02ecc843d9e93c229249ad547f1f6680e2ca27581e9d621a0df918ae6366e80b367edf6e97cc59773d7bb9e3691e167f3bc34cd95b1a8e7d0d800012c97cd4e67942d5cf249ae36192acb01d8b48d04e0cf4475636c7f8dd0dfb09890bb32b263c0cb964d703edd21b4e3a509697d7d2589074b930f4c5de00a9a347be56e0f4c4f59d62e091ba7057ac9520437bcb0b26aa9c66c668273c0ac75cdd7f3fe72d8d0792a87077a018744c02292c603a6475201886ce83d8a300e89304190ead434e518057f5d8dcbdb277c65d247b2d2c668bdaff700e783703ed92ba2a2e276265a5c58e70fe1b418c1219d2411a15c79d936263e26164520df441948bef0404d3d1c8797c84811978021d296b6926d648ca8b28463a840801e7888dfc2c5c21a6cf6e28aa0a74101703469b0af8086caaa0f3bddff0581d03acedba8da5e59f57dd354d413cf35f65479031aa5bd508f845de206d7d20c209755c12bf15a924e6a3a40f4f7846c5d02750bc219a5ae2130b8385ea8449850b3055ee15e2f3d62eb4c98d180b0a7aeefdf485c06623c17768007620bdc698044508f5c444bff75e8e4ac71094f0d380b616b74ff5e813b033cc18c7f2d91006c64b36366f0ff561df09cdf0713d8792c98f6dd17395ac14447381c5b90c4b04d555124609db930c7dfe231909a766ab3ecd0eb32c4d4cfc073a60ba5ab07c0eb52c10613d3ac397920830c13e7e8f6d09293ec1f86b5ed1772115352ac98d0896a47a17992c10ce9ef5e496e3003733353feec37de5105539242e4efb4519021ddb78396bbeec0eac62ec54cddc4865a468c1ad02c07f5d0c23c1b9a85a5200efb9c51d6e3ea194c5c8bf9f4c8d770b92035582679893cff995898821129b02b9630e7419b4166d3969654d8fbdba6f0572849d30d8bedcef91e0b472b21c0d4a896f9e0a080434df790f9284411d11ed1ff5da21552a6f979dd7d738d9cc0865fb58d781eef2cba1b0fba6cbc06932de1b19d1d0e4904118453dc746c1d1079828f0d4ffb5ff1e1c6c3449c230777dd0cc5519dc702ec8720852c63280380871985c651fafef2a7ba123b4a864affd6cce48fc2810e667d30722ff9f19b10115151b154f7c7983deee1e9666976308ff77ed799a9acb7588c2cce94db8b50c61f5a839202c67803ec7370dfc5f3d0619d578fa9aaefe2c6ce58a3eb1729a0eee287dc746f0db3f6131f5aa42a98df719604721ae0ef13a0f0c20775c53d00540d049c1e70e203b672331e66c1e25ec54cf53f46e324671af11422ac599820a9f858b8fcb4e4644cfff99a776ce4a06938a12d1fd04d76bcad793335509080e34534fcc2839197ddf8512b1bc7c14aa01e137fbf0db058762250271e7bed10d183517e6972bab43084f640d0895d03d06675997063cc373a0e055508cafbe0661ff6fc761ddfb10063b2c61b37ff53de1e8cd04f6ac8b4fac4f70a5e9f3fe0da33d92fb6c714e3ca71bf5244f8ff0ee9d51256a0656e1869f28e9f36cf2b700cbd0a10cc20f92baddaca5689dd096fa4168e0b59945cdd4219a5854dc2fde009fc31a0a742be28dcdb8de52ef5cc20e6026bbbc01a5f742f251f189ad853806aad1adb69c2cbd12cc0328d76c696e88eac8e252e873f738b5031efd1002d40817e9282d946dfcebf2af541d66e7ce645cfdc8b693b4653206ca88f2bfb81e0a74a916617923709274e8e787707f81295603f196d1a1c11ae23d10ddec60920f1c86607fb381d156f909d9aa607a6589fb4f6d810e51694f826e56ddbc49280228090e40fb7e170fec7092ccb40c23267ffd1045b21b58aed25734b6a62d47057941a0aadd627c4507bbe86b1eab8f4787bfa62a2dc480a6dc255b94094c210a26b69430aacbd7d4ca446aa2dc8320719fd765c8a616dcff7456c925c7cea70cdf6eff6cc7480f53ac9a183d7653be427ea4addde8cea992f41a90f01d1ae209f3116b823a70cf5d25967c7dad95c637c1b81fbc0ae77ab23684af73fd70740e7b92490dd2d02e5ecc03f0d2c31a3514795668ff3cb4c785665f063ac8e3f506a6a1de911d46e32fc713c5fca9782c9041b4a771dfa4a7e000fc4ee27e89e10b3fe9ec821cd7631f108a1e7a1980ba068b73810829c5768de2441131567ff70b4b5e6138a341055bba7fb16599f8704024761931c694f6974ffce9af0f8f5a061aaa06ffa631b457f3d67b17d946195ed83019826b435cf65aaaf4cf3164e405fe9cc4b25ae704d682823f77b9a6a3e242e26341c51bff6db15ad61945a085030b93baf4397ef103e48e97001b3fbbab794154ff7151f369c24b7d737241ef0d3f88797a365a787a916151cd9392e76f06c0fdd2da2e85ff8e3013168db9e20cace5780c92a6e85bbba2655af55341010fd0f43b76c513b630257a1eb2c20c09bc1cd68c5015a5f73b1254bad08bc84c7100350c7cafd43b4e10c402835fea0f6edef05971f166899baf1ebfb4e0096f3db17dd8f9387e45ed58ed642b3b5007aae7a88dc8491a07f33505c6c850662b292a3f2c3a65c1c9ecfb8cbcaa8be100db4ba3c506df12a9ac8c19595b0c86add4c3fbac7d45df6e91a2046bfc5fd20a52ab6073f99023c58c25bb97aaa51f21b8b94012893b3038b25c93d050c135036d57cd81557832dbd01d2b230b220d0dc7a84e682edca4a48690a017b3fcc504a41c40fd023e565aa6d15e99d504232a1c0f349555b8370d3421acea2e66af0eb82d84175c4f98da1fcb80fb7f56b4f32836275bf0bf790f1c4ad3b17423d704a5062fc8c654285e6917bfc9bffd80e68e665dbd74b5727e1fce02c6cf060e06dd479fed34c5a6e6c085729ba45a34d09c94ac9d7bb648a6b42d679134e8bc08f32ffff623557c9b53960cfcf2cd78fb00f0b4e9262814b24799d9a44b56b002834c91db8d6fc45e36df359360d452aafaff806d588223bcbe0fcddfca51790beb54068dd340bb4dab0f345e3d2ea30c20151d37966e546c23f6bf8d73738a09dc9c1d8f43c6831d30349221018ebb901b44bff7b43d2a174c4adc08d28e9b0240aecc1374723aae047fe34fb5763cf0aad158b50b6525a27d18f6c06d6d7804e2e9d39f7aab825de64278ec9ff183f08b7d20333944401c6b5b7a6ca2a5fd0e862a0cefa42047a48203d4ae4e427e9a6ce04c23c5dfe00fac0fce5eddcf76024f610cad126018f8bc319bb82459216a36928d964d6916170191ca9a8323dd0b141a872419db99500dc6cff91528d582ceddcc245290868a69b7f6d9d812a509a28323917bf9e0786dd77dc22af6bcffa6d6fa908dfb9789eceb96426739aa04e1971c48c9ce5418df5a0fdeb2ae84da5d5d053ce5eec9324ec49930892637064b60a44e917c25a61958de20d994d123bb71c9729d48a8704e1ed199d842500fc8325252a751c9b426433fedf63344ad62e7517e567a9edca5ba06ac1c3e4005c28a08894f1103ecebac79a7308d69023a995c2ff73bf9648235b78e22b2c1064def363aad8282e74f15cbec4c82e705d64bad9188b0dad43e3b4a7a495b1c09a1201eaa5c3aa0e23628051a8b2cd91f170c69a56a4c12862d60351018462f0abcb0513fbdde9caeca851a6973f96e7aaa31f86efe9ff92d6cff98b1bfe2800308fe607f53430000f104c31035f9dd9f21a6baeb63dae98fa0d957accec70102313f304bc6cd2248b7f7dd9b497b8741c0fd746fb10811781591082fad2b570ac869ef721bc8909a06a9d08a665579164c374cb839594c0c1a0fcc4697be6f0e038a34aa3ce5356f99c75403de0f4d18e5524c406aef8b6ca85aad16a6ecf30f9e74b05d12286f3b7000877eba9e9987dc920d7588bdd0fe9cacee871bc3290979947d782eb432f4fbf2d6b0bdd2184ef72a04cd2316ec824e6500d788ba7e079e5cc224a7201e4083cc231b00fb6549627cab00b6d493ebba1cce0d463c99042792d05bbac96cdbb416604809e1fe1b27f7998685d5e49c27d04f04efac3b06061697e839712f098f9596d0cf32f6bd1ff76b7659d1b627fe7b3209078ffd0bf1aa7c7d48bcf4a0f98409e8b01dbf329855783838b78906feacc8ec6d0758091aaa46d79f13766e433b0dfbe42ca183797aabc3c738353cb9181a09cadd2805d9c46e32de01c6725b550e3cad4ab4d5e047716cdab08cae95b953ebf3da130b06cecadc0db4601a60b44f9847379a5d961926095e6c16f9789eae958ece700cf6acae2143dce6ce2ded99e546386630ed94ce2879cbf2082dee3a949d38040f0902a37ffd8627cc9445d11b05e473b11921f958a83f2694d85f6aa6d5bbc60811c26449569217e887bedb2c21672b5bffe6e2bd4842a4787f96dfcccd06f3007aa8ffebbdbd67c54aa5394ef8a3b05c37c0acb19542c87bed99be92a7845004dca73a6be6de8f03d86f78f887610a45c22b375be60c89e3bce66025a7070d00cef35cc273425624bf81552d0c8cdebf21853e1f27d7ec30ce58f0baa63372098fee116ae8e719b0fe6ec51fbf254ce382e47cfbc768d12c6d9bca35809e8308e46579c1fb9b931f1326cf8cf58bc4ccb06ac4ca00727aaea1ec496b6a94ff0caf2a051331bf31151433ea38536c9a64b84e856efa7651ded40817e78bbb6009660cc8e1ccc97167702577ce3c3efbcaf5fff94745cf737379b42e9fc2ddd504080c3abf9c6ec85867adcf51656531833343d33f341d2eb725e3c5e80a3d6b00b90c38c93951fa8d635e24b0b0a48b2777e13e28e58ceba05c7f9bc8f3322305b530f6a457319701669c0b135583c05a2c44c029988bfbd50827e57dd002130517f4b8c06b1b64d89f44708f9f9be94d0e74905a8103dfd8f82f5dcaa168d40a7da2839690c9beda2cc82f8b81467f92c3c478aac981ceb429b3a6a3e5d842016361fcef9522e3fb1f8511a9ce446a37ad86972948629d783ec7df5767abd20625fcaa37690501207d502eb1729f30394f4e1688e7e7e266b85c317ad2ff340ca56d7a23ee1df0c73fb27bb22965c03c5cf5cb04cdce8fd3b1e7e5e05578410a9579eb3ec10a76b94236f2920efdcc72d3af010f6fa23612808f660bf2ce7c0a87af607bea164a8765703af3d16f4df4d20e69f987efc9edb5a555124db54a096572308654574d0612f64676c3b223c8e0eb2007363d134609c8d6a742968d0522b2252f8cc03a25aab5774a64c5df7a7f6711546092487af84c4607483a2d0b5bde96f702df14927b9907ccf6eba88977ec7b24834253dfb6657c351321660848d68c7fd33113431c12089375bd914a449bc95526aa4ae83e3d165217dc020cd6e3f60c57897ba86e29c056396c0d27b491c6aba1fcbc2f402a0f2a1033a8057867ecf746a1157ea58891e314ef9a4652d59e415ad82b9b887c19d138f9650753286821bbf782f934a0064178ffe6bda710f4031456b48cecfe4aca1bc42602e918aa46e512d94adf4f218b68f955c158794a54a13e69375c0e48d0c43b710ac30118ee440d7a501e724c79bc9d8f08dfaafa4b658aa47cb624990fcf6dbe0e26611ff81f1804487257c6094b228c555d5330f9eb617294b9614574e2df9c081905a187b7333629bf71f1bc89aee43494b1b44746716d301f1b0fb4bc9d15025b2686b4a6fc04d026196a8ed9f4444864fe4d0aea2a8b38a3d2dc39019c060f27cd572411b5979e3b4519aea6540c21ba95d55e11b9b3ceb26d522458bc7e022d6b0359b12ec355dea821c82ddf888473554083d0f36dbba8e9cca999effa0c8b0f4e13e9559ee50fe42d7bd071ca97f3455a348985c25872593b0071e196059bb82db11518a38ff6b91fbb263f46728ddda2f357c992b2d803d5faecbc0e0891ef4f92ab23d624dfd30a9efa25fd88456c4e84abf1bb8c11f268b5e0ba4209844e72fc68a6c3a26564d0fbbfc88431da1d765c292a81391ec5daba34d31507ceb8fb3044d9d1d1f99b4941a126e0580a255fc98fa2bb7ab58259d23d0a5d0a0df0bea82ecaab07f30d97d769d5b40c7605b11735ec813875e61a551a192304f8fb030c02c6f182bdda1d6ae85b8271260bedc2cedc301947bcd8a19470eb02969cb572cb4290989b0a9cc0938bd92218c65bdbdf97101cf7da93be851d3d03debda376dfddbb869e2f0c669c287e71de604a4682b30931f8178c70b8ecb8032401da4c9474ee1693df584e3bc074c6b3284b0962871a784d94012f0ca2890923f0f4be09049085029f38dfc47bfb9f289202b5d00a3d19de77ab0d07d88f0ff220751f75ab02690e02afc8bc3472c502ebabc9a09a20e636ac86ffc96529013c74764728f136711f653a8c9c3c2b08e6c9822691973a41a7bce053b5002a0a84e5e6a91e15ac847330943855a0aaf01b9a20e4da42cda63404e9d1a15b1005daee460c950776a19cdc223b1fc6fba907a787790f598ca5d0034c8bdd9c5a0eed4b0070fbf1e81b572499d71fa5595907ca348b232a56b19e5f14031f08ce0707dab2b3e7c7056ccd9b259f4401c6533a4a18b9f6bedbdac04445db0e4b4a091548d90dcce9ab43e248bfc01034d28a19ec4355d927849f7553b4b289830303043ef8b7fd5e3d1dce67cec39463a3859de87010a75e964cf2c09ea2d983710b055fb8000c5f8ee2f7c10d973d010548ceefb1699c5c80a24f9fd264fded960f0a5b9fabafca8659199e076a78283d6f291d79890dd0568cd654fed2d0eeb600bf96b8306a3df449b2bca6d176073a106e53e3a02eb1d5b12ccbfe00341b3b0034d3a83e9aff008d2f029b1252e62d55b5a7c0b6b30aea3b78ab92131fca1b032535f849044c6567bbe70ebc388d898fa231bc2e61fe387ba4df0070fee797005c443acf774e192170bbf7df813650dff1c4f38374e4b5a6cd4c1636103b220c93405333c6622beae311836860a889aab363eac2610166c6912619b6b4b69e061a720f5f99dc9556b8031b385f732db873767b09d7f550d8ba348a686393d208589bf347f450bd4a3e5e02936cecfbf85f3d23828f3faff9d94f3092c13d640e33b4ff8e7cce8e22f86ef223c3d20685137817bb092f3e9253b3fbab2431a20c33d7999bef831c3a149bf9378113389e162d3a8229fe5194e8c5f72aec836309cbe02c4bf37b6804d6f261b6d0d982a7fc3b6652b6834ff24e01925dab1a100474ef064fc1efbedc116ba9164398db2b55f59289575a9a30184895c0caa13c09b271afa9455ba5ad23295ce604c2623600266922eb11d82b0a3fc7ff5700ce08fa19ce5e0869a99279011ff7e58dd46f0728d2c481d4e25d7298b2a57755d40b9800794d6eadc7cd744fb28d72c6fc830e84b71e5552af17b083120c6d688c037be9909484db11efb31fe69490e694c903f1bf93671c8b92995347ce56d5a70400779af62ded79b10fadfd9ac1764a8077d7c8949c477847701459f10baeab0da8367608f69e61e6c14ae2ab3280042697e03717e6b734d70280bbc9e60dd309b4167d9e058328db60916d48bc61c35511f642fc84bce5c485dfcbe6b130f60beca65c2b424da75a24a4d3866368561520f8c315f67a14d38a47b60147de8a0000957e317d015fbee9c1721829939c95b1387b72023e209bf4ad3dc55d7cd202fc77ed7f91c81d1b55e74c6d56f5584b68161cfc978f406898e5ad417eafad0813aa416e5b34cc21838ddcebfb8d276bd77fb0d6a6abacdc774c676be02510001aa2ff2387d1b6abd1baa58c6a14668797a57d5487381dcc4abba1cfe90574000ae7ef1a808cdd6efa461b36b54c30881bd975f619772d5fc9d0b3c503637209ffe76828bb602a12106f1d39944b45b8fb516ba7ffd6769c5a56900a4e9f130f5a64a5173f468db7f56fc52d189616825dc996e3b4b2ccc097b3b858c1251106e0dc8d665150ce694a3277abf1183d62a7a0730e16286e0476bfcb32e287e60b61ddf6724578e6f6ed5b221c565fae8df2efc90bf5b90ebdbc1a3fadc6f0540f9f01b0865da5c8b83bce5e7d2b1c1c3361cebc685d97445af9eb36596867a0084b483c0011f0f414443909d368fa8e1d3578a88e69b07dab67c7f2ccc9eb0e0df783767bdb2ba1cf25d60c2bc544d40fa98389e22fa65879321dd289405ffe07237bbb2b33aab42aca679795161727fe472232597323df6ec7e7d424ea4df604a596fc8fa2ec797d79e6d59f84095be5c94187717dd7ec6ab419cac59415ee0ce437583baa28679f2eee32d4f6b492f7645c9586bcd19c2745e32e7dc7320b0337512313842faaba7e55fdd51b419c2ae5c805d8288e66852ab2c331fff8af0c48dddb147aadf4943e045e0448b5d2bab786a85a11c710c51828d6ed5ee9b504a53a08e5f472dccb019ab8875e76e73572efa1d6c88c849a8b975750a334b301c7d6414ae413ff6dea7778142dd808998cf3ae2495668a74326dae1136052a0101f1da16df5fd0bbd321d7f5f693801757d6f12a20c251791d6391e3e833a5099960b5329c02e88c48608a68af41d360ea63c73c95ebe52fe624ad1cfd3a1d0216a1de5fe4fd39d0b9521fbbfd78eed2d3703f9cee0208f78250880c9b0346090992b95bc551edd606545654e3f4859b7b1a1e023c4fd7f86d251c282504870e56b3ae705d3b3537d4b1cd114c215ceb56731451aba5db7fd8edc0a2f9caf50f17af24fcc8cb6af0f0c06559efae18fd8e38fd51ae7a6b03db906783a9e8c10e1da5d4e0e37d3548c9bd14134761a8688dcdedd7d9ca6cd03e9ce2ede7d0ef0865f6d77b4e472ace4126cce423f878a2581bc6d799433f67e7ae39862f59a103d0c5fc5a9a86ca2cca982dd0212bda765a48a0a4551252ea2630388b235ca30498b517fa80fee0f458d9b1640b6441fc6078100d268b7e473054834c52f3040591b845f08f5473419c1d234a4a83c57aeb9db06e65ed3b5a42dad007bbda600146a923187dd64de324a1cfe85df417742db730ab15f6ce4d48c28b59872e8b0ceb4e906ca27dc1badf99679376535e315499d9abe3f1ae766ad155ab40d76103efdbb3b375db760f2f114fea19520e9c30edd14ed8ba79af1909838af3300a0afb231f18b59f2a416840b92cc8480657ef2df2226a170bb5f25dd9e4ceae7a0856ffe10f5ef5dc1fd449decf21b5fb558e556197d53827a2ed88c8976e713a024cfc5643398b158365216dbfc9c1f657c4a81d999d63df9dc2259126f4ef3e0503432b9ea78865b14b8413c6721ae7290535869ceda71d97a5665459ed9e1806a9968e6361c1164968dd4cf4bcc9ba28c482ea38c8ddbee268d3d9d50163b4013dd5cbc611b7f6879b068672ce19abd9bdd572753bc5b5a61a291ac6f4afb202016d2c80b2bf39952739796afe4fa699b762c85587d11f18163dd3f0f308870514f4306968e96aaa4212bbd29ff39ae075f02b2a0ade1031b30bdaec186e9401b615ef26c4badce802cc714ad5b808913e41ecfb72163702b97bb18a14a93101bc9dae3936df14e170670545e460acbe1e51ec756fd5542bcf1ecd9e50088a085f1923bee4dd73d1a82a3abb492d4ab7287b9f33c548cb0be80e6382a4782f083e9a5be2709986a52a4212e235438bc28ba4e8d74d5e3cb0ac122869a42f460e3bffb1f0022f7e8c8947b2fd677d3c148876bc11bc0d1ff48ab02744736f50020df4e2b738984dab9ece08d4cb6b2244466a8005cd381f766f888d6edd123204a4bbf94a5a92add61655834a959e9516afeb5ba4f949464713c9bb721c64ee06c607705803724ec1aaf927352d90145dad07f1dade956c0d4f934a0df14b0005c5e46891291084d940792cb48fa2f0b985308a941e9e83375a1fa8330157530652a878cdb1a0747f1517321424181a5243c0786474c331b85a2c599b1a1c250698223770a4a461af7897491005c71be734d55572bd22fd8fb4fc4e2fc3f6c7024144b0a950d58e2b0452333a903b694c24e5a668e0627f24435f66c3aceb2d03fa326f7d5f96939c333003c14b2e1b076162cf9d1beb1136a0245bd225ca910f0627d8392c1b13a2d8ca1bdae05ae4c4668965133b15c88f9b1bf76897d5900290996237522e41abc39bd1c71dd8aa8377989d5ac6215f23a274b7c1bedee40cea49f60cd0fcec773b3dd99827f3460b12f759e592b817a4211bb6cd3282bb0c47388cfee71250a08ac6191c7ca93e67220a25cc7b787f8e48093e3dedcf030eaae63a1e03cace1f807e7b6a3bc4cb4d102ae93eb8309e5e4aeeb08a18af730a045a4b83a00a258300f506a99963773439c9c0bff4bdedfcb4718298645455072ce38c10cd912f31e78d6d0508ac19bc5daf3c00273dce16293b5bffcd2b8c0c936a4941d7f411a0420039da80c8d0677730d4e2d4b8435ed7fbfe4ec042be08d70ebf121fcd9581437ad540c0483fe1b7d012a9d76e9e0f7b202ee521027d07181d57b7ddb57cb608c41653b3c79b2359d4c7783bc2f8624e72691a26d35f0df01c422a9c744e8267110efacf09ed678fbb539743ad19251a82eae5a2cd840e15652a22e6d60f9636df5c168c34841c5aa475f8e2007fcee564b5a445387c07303fd415b2ea74aab647febcf5c73c974c6f321e52df8bb827357beb42e60e0f43dd97c23b8e73a45f9f3046b08290008066d4bfe5e8c770ba4af256fcd0f003288074bd65529500ad54ddfb45a6e2ec5cfaffd7716f9ba14550602f3de7d801dae6f1a480f7836b51381be68a178f41997a8bd98cc54afda137c96003bc320c0bd4df74e3929b66a2a3603737a6274e1b03dbf7abe7a814a0c1a91ac6933701328b142ae5247f5b91d5f1d6d5e676bbb0b9a790da87a4571b2d7f69074ff60c57665303e316dbfa008c8e9e7686fc2c4b7e76813def0a2d007e7a4c0bd2e104a2edf7b87ce28011b78debc7a7770f0f493674f93dbe8b6cdde89aff353e07095a6a14090f9c4215bfe23b3867843b6370319c9beb1b67af02fe44f3806e500b1c2dcbc93a3e741c8414e073b2290fa3bc21aa90ea80357d73a8ea5bff5be5012ea41b72dd7572afa81d7b09845b9d5318f7c4197359f0c4f40925b5fdab8e076b81407b1006d0de544fc16908fe6b59ea2e11c621bcee70d9f9cfaf21e44800e2d57e48c6daccdb6001bd23693e36bab674cca1eaa27e3abd617ce2cca9040de7a7fc5fd0549484847b142bd217ec43959e092d950bd4956582886d84689f0c8d8b75a4abec6ec3f6beb0d82084d018c33a57704107457961a2bf8f079ea209f90f391cb50dae3c2f1611263576448d70449fe30645af946c86851ddbdf18019727f506a36b00ed5e35abff78ffeb2d106a8da732d64917614ac4d5556cb6025b971b11a5308eb18bd74aabb8270b7ed94d68858020ea416c2075997475830a3ecaa554b812c104f67472a94b02ce9b5c766a90f40ff0d35950e862ee3cf50cb1556e62bc04f564a13045255c7a80fbbcbf8c8ebdf82af3fb8233ec80c4c705539d5e6ae645766c50e0905266b734484e032c529e1747242d5b76bf03ca9209cfd5d35ebd5a4b311a6d3bb7e7afcf340c18afb53145556a8cf78933360d380963aaa2a945c47a134d5183d31bcaa204c83af7458b9a357e0adf1f2f63a9010c7903f3a4fc2189b3374a7c27a9d95c5405fb787af01864db6986713a25ea770db16de51d9bdc11eff18ff26f871fd4e9920c57eb7ab537f56898a368e615b603d13b5cb665164fc6696b5667a2ebf828b37993fc9d3865f4ac7cdf8d97e52507afe89e453cdc359402aed1f7f4b45000def9c1167ab4723288849645af72fd0a146ae27c0699864e20224c6f5e9914425d2599032aff78ed9f27c240ef218304d9992cab0170546dc6b6b0ab40aa5ee824e14169a5bde5a42dad7107a3d08e0f8c2048d9581cd470d10049f3769683634f9e6b387070b54f2985e3a87fbe920f9f89de81ae8b54a62021089c9e2518bbbfce05be6214d3cfeb59b74b8acea001814b7d09d8b0cf8bf103ce787daf009ac204582622b7959384d7973d6da30f028d64255fe55e2563943421848e7db99beddc76ac1415089c639e21f56f7d8707cf9dc0187da082d52347469bf04fd685b69960f2736d6198a710c8ac92c3fb07f73dd4aeb85751f6a4c52d8498de49087d717a994e2005b34e9360f1471e700b523258b3667f2a9b43caf46eca4e48d78ad8e46750b175fb4e034581c7937c0159f135606df3e174563b8c4e7d6c90fb6025308d68651363e0adb803c42f0e03aef76901f55ec1512cbfc47dee2c871339bd6136eb99d95d7198bd790b3cd9036e8a7bf90586fa8cf36180f9badbae530cb946375ea286472093964dc539c007c05345a441903a7c55c92b92d932afee86e8b7d6f923a7cff3559dc2da358c0fbe2dfaedfa165adfc03598f176fd1e420c281001727eef40452725b7888cdc00b8abc3791ad17c7f29ad6f1e360d87ea0ab2349f33f049e26b7154a5eaeaef017c06ee0f4fc43d0bf8fcaea7628b42a44d95b2c0ae979c37cc57733c45ef1b0ba9dadc8c65a1061b423ed1e53d5828d6e63125105723cc2388bdb794d590da07a50778792bb4005601deebe971214179db3df008fe5d97a49b6365d916950406e02159a59b1104f72a08cece141b7df1391a81e5e62e92102ec74c93083f7c02bb126758ce4b5072468a8c9e78633252a49f94b9ef0045696ecdc31cc9306c036317b48dda42e4445f307ff910d29bfebc640800c6709ca062645489fbfdad046d4c364ec980070944cd1eafbd222c8357e2256f4606718377b72fc42e80df07f6314dc9a9fbdf81956e69969e4c4a8155bc1618f21f5745b6e95dc232462806028b302d7dbb6163fa3788c14ae1b69ab43853f463364a93360cbe7b8422410c861f4d13a3c445665d33a87da86405287fbf82a45e26e5c10b5550469e9f3507cc1d15fc4f5276f22e03ad4155e8ba56f56bf802e3927efe024a0258227232049cf306e84ed23c7419ba12fa48d05fc4cd7d0f9fc5e0e5919dde94aea775aa0d5a698236d3f3653499ddf3e6e32246e7e4a0d4bdebe50dd99ac8cc47d6e3910c5b724397812b387b0151d9c7fe1db04555b7bccbb6cea9fa037d70673105b500b65f726d2b3928dab55afbc5870e25cd176aadbbc845e1883ec1d8b14dd780080a5756b7a820455393cdbf75aa6da11648dff063dc1182915db97f0cda33cb00ec955b8109ff41e7e2fd519859f72ee2199d73d831a66f4d0919c637c630d30e0a8e967514c71d2736fc49418283d979f969c39a503c84b607f737ac1aba8e0b48aafb11ad41e963a0c7f45211bc524503ed2017b97b553448d69f343ad248050ca9669c8be7ca1615c1d4836d8e2a4d424cc2c2be302b236c8a46d8bd30960949fe54726ad3c3871624cd94ede0b6aa69804ba9f55544171db4de0d007319019abccc7dbf158a8e628708bc25b80ffdeb4fa1932436f50ae7cdde5bdcfda602db79108872caf07874914b459b246417f8aac5d7a563351731d29c21c4d6d30605008961747a04d20db8fd7c6189811d672a8a948ba843e6db59d439c0809e0e69e4cb7ea493121893fcbc58ea3afa7e9d4909e6a364f536d1fa1255f0f5580636dcdad4d6ba5e15f2dfaaad2f8f05345a02ac39d715a5c22cc5817ff9a72a0d0bcadc40c6ef13ce30bddd72f146e5ddf77e8f6d3b5ef001d9f87bdd160fca0618ae77ec6cca2f7ff437cc78934157f495184a7952a69d0581749dbb4a4f82045f2f1f55776cda7fd275ac14d1e62abfa868038106ca8151173df33f33ee9c014792395c1d3fce9a58a6da428f660fe6758999fdd52475bfd5e1dff9b387200b753006c904c3de0bba37650a66004660f51444cf0ff8addf1987abf051d4df0eb3cf2c718f12decb2d3eccd95a53e087ae0b85e47f1da52966f9e0f34a7812028b0a7ca592c7df6ba343f2321c2217fdaa7a659a9bc6a701123176be3f2177086025e5bc3111ff6fc3141c9355b1cb1cc8be1df7afe29256203a98385110aa0cf5610c1a996c4db92024a57a63707b81574e40f31f4a7bf92b3954f217f69c0dd997ce5d1e55600501058b578566330d00301001ae2b74aafde3a6798c40110438d5c2a30d41d6a1e2206bfbc3610990e92aa9e986bead84d686f9a9e475720b2fbf9cd04f95fa2af4331419aad79afe7407f653619a85e3619e624aa6565404876651dde1474db090ecc6e7977d8bb622c286c9dcaaef4c240c0fae20f34904e4da7cc4015e260406c87f9745a2cb6eee401630d8287954a1c91d0843a1f702ee57a0900a7f948bb21396f406e350496f5afe83d9abcf8eb2483d0af453c30a5226324449b92f753aef636ec185edfd3264f7d2e0bf08d31caad1dc418de50579661eccaca9d2d300a1752ea1b3001e2be58a6aaad0249334629882ac5d390db863a3863a6045e98426924dde4ca7eb9aacf94af1808c4cef5152372cb0590585a3b668edd6dde937426552c854899115ba031f3e411cc3147e4b4593a5bc0ef9d0d24e4d20916927c6a0e494934fcbbafa48ee568dd31f920f48b45deca200a08c9ba8f441c2056c25e8ecf0019e39a8a0580ffcc21f3f73e5bf10d620150c54d0b9adbaa036096e2df0431d983a8c059f2920195ee805b85a4e8d24885c053b0c16025bfc6ca3d0ca125530b1d945a3b03bf149d2cf3fbb625ebd29795d0e098bb366a8dec65ace29acf8aadcb959e02b77796cf0db16877adf112383ca0964420dbb01ac01aadce2502e597e147c5373eb38191f76cb547182ea56bc870451409b30f88225e808ad47a6bf20dd902ba982ee34d87103387638d11e3e0b0a9c5f61b28897074c4f551b3e1dc62f30b020b39b6805c270ea68ed9cc4127b0eefb2fe1cbfaa9ad1086bcf8abd803a02059c02481fbb8bedc26bac402449d9080b5e7cd3a15789e2c5149cae4cbe88d3e0608697c22fd0428073bb2bd015250dac9aa8f6d149ba336e027d1b8a4d6819c70b3b2a04e38731d95f583fe4cd360bca59351eaa03712316ee6645a0bcee7ceebef343758744eb14c51499600ca600a6edc358e4a6b166fe2daedcf8988d4666083da05522287d98f555dbd0945d05ddd34e4dbbf0014d122a14892b0c9bc0f35d79e2770bc0ccc6203b60b11a32029e42a4fd5fa07efea52d7b50aae093d08fe2bfecb9fae99ab33c8ea08f9097016253e5d9f991a21b01ae845133556d1cfb3585d0855979d40c3e7de4c97d5e09e20cd0481706d2307b7626c5a593008198f758efeac817c985495ad96eb0ba05ba6a8a1a1a9e8dede26043d6c3f549d0f6164c11f8953030f24621eabad6880a96a6fc846a2a662fb0eaf75d89609d7b5362ca39f7898f07dbba0ccdb82b7109349e150a88afc4131efcd2a4153af304dad2dda0937197194b2ee7544af2e4070d91f697c71e237d0966b7785570c1e548ab63d374cb26743d4d540e299abc0250fbadfeb4982ce690d2283e56481d809f6f0413b79f5b0a6f98bd6c7b237e0da579e1ae7fb621ffcb814a3fc1f99689b78a8f992f47bde4d355944a47e5760f9c17ab5596f5a2a51082b08c5ddec7d55256ad5071379654e937f141fe5f810177628c21daa2af136e195d0ad690faadb41debd67ab93eb1e514293fc94bc407a234183739693564be9b873f7a7f788969c1da1883336569a00fea857dd9810e7604f45c6cb96865ad1503dd4b512d703bf16f652bbc0a653bdd62b4b1187300294c876f11c30210d95ce56d61f362aeced7ecefd73a9ffa2ed6396e8d98030917aaffc8b00c94135dd010a80fe8e8f8164c86fa28b18bd9caa5e5fb608e7d08749c8def35aefd86a151db658ad13008dd7eef3ed3d78260aa090a680a7d000220a5b95fc0af46442d1071a87be783d477cc43dd93f80d0a8e577e0c51a4180c5f633b3ecf5124a5ba1ee16f98eae99544a37e17e3397617ee364b54ea7faa0a0e21be35940d43ba8cff07abc1eb338b9dd1804259686c9fc344310b93956a07b67c6f9c393d99884b6bd0578e394637f8f4bd330f67826d57e095869ab0ca035235b55d90b1f0811c2c941f03ccfa51f3d0546e1c3ccd955d4ed8521a40cd0e97e331f03ca75fa8019486cf625cc2c8c5751b896ffffa4b932b5a8ae619c805add99024dda69d3d8839c7347e899f2a70658ecc9ac06c8fff77a7a4355fb005edbd0802061ccbfbaba8490565952350eabcd9573fba632b0f6edda7430f920c972d48ce4ffbef02840029d55ed21dcab4a0c090a438cd30ca1405d7b878060ce36b2fe99c9544d27e2cce11e9e3d55470cad45df05840f29861f189869b2c0c0a665ba6807d5b10028d49ed0ddd2c7ac0d411375164217251f9f2853d46740121fdf5642e94d76a7b46515293e98b9ccf1f6f79c4b1e338b616d1ed8d72890e9adfb4bc23678e19917268a07b698ed65946946458ad598ba604000978e0dd05872599ed4a8f8ad2d0a190eec904161d21b69095f9070f12cfcac3755441a906d247128a6f3246f7f72b119e8abf9ea022c946181dd86f04dfc85e871299fe083d31c7866ab001f247a6a56b1fd5341dde2dd660c463fba4a72dd3b06f99d2096918904767a9d31d0296d8d043d87b0ee73a3c5be6dc2abf871adafb6393230ecddd27402000636724d249809ed419f4e7dbbea5c4d2cca76150b016309c2b0734f866b46fe186b18e0616facb1e4db47cb070366839fcae651a172145e3790ae96113f75419fbe836769161e9eb59e768f6b449fe84a68742d88ef103fb690cf37fc41843cdf1c3aae75bb779742d178c426eb1c41b0b89fd35b01621e8f003f5b71fb9d881c5bae0e414f4b869052d53af1e1188cb6b3fd40cdf5c953181061e4f3299f4ce6aa95bef8bc523af17c64e16504bedd089d736ab771f7e7cbe0c1b9eebff64bf0337010609833cef60ddee4abc46d34d4f2a2d76086006f509029e7a04822798a49af80dfc53fe3cb120ab5e7db0384ade1178e04a8100631a0cf4252d454735765dd6ed2909af333f4f51bba560aa7fe3a078c8c6aa8c2900024cbcdc41fb28934b1134364df1d8f5b49cc964db7bf6d2cdcb61514f8b540a0bceb7f71fde4b917b7fb625df5eca84d9418b7639b3a6505d98bb3b047b2aeb06bd6ea0f6cc677a48c79d4626658e5a4c9f2caf520b60b2d99788b79bf6886808c7bf34bc98ceb9915857a7d8870b15bb6f2ca47ba60d40c63ec12e7816bd7d0d9af43c6e3850bc454fd3c9cb492086466fa603f39f877cc28ad41686bdfa4c0165948e78b6cfe916913c45cb1908b918b15a9ad22d811da44084fe94a2284d0fa53cc2267b53e5d3329ef53d4531d60e51688a45e1556cc38d6dd787305d180779323e6d837cec5fb2c3dd20089c2537a95523bd12bc1d46e4a985f64fd19b0aa7ee84e9e2a0cfd4438b4949e44e637e432e3fda35b6d3610948adb37ed84d0deb4012fd79bf4b34c0d3b5eecd20a3a9beefedb224afa0ea1d61f5fe288f8f0540f4515f2fd4731ce06be5a1e3fe319b97bdce5c9162a4269efaa55f40be760eb711b1f0770f7a8f3851f5eed2403193c57859ef3a9c0dde5c7ccfcea87607079bafeacc171cd555b4683cac10174e7691b6050447811315245ffbdec8b10707a61551307b1731e08501c4c77d8705f69a0fb5f51d44bea7a1757ab49f04ce0ce5934ce66033bb3ee52ed145c02852f0c817579ce9bbfd0dde35dfdeed34720c73713c4d18a4620608747f8ba3d7350e5e607ca0d53efebd3521373a49cc02063164c9bca26f0837f32c1aa1fc5c60bb118d1e760bfc7cad26aa58a106a30e0ef26a4376f7c81394c686869e80dec659aeb631521e643f8a561fea7b2274750982c647d92cbd9ff751b37249af2d628290ede9b6c9bcddb4ee2b64d977d2680e765ebd18e7aa240fa0e7033a84c644ef686ec0c92bd4b5aee22cdc307b2b2608ce0a6937eb4caa3624251e940bff10e433e85dc72752f4fbc020ac9ca0ae160ed6e345bd91b7f27fbd398d1a271b2a910025ef90186d48e134ffca401f6a6a0beb675c4c8f54d2b1008c103147f1a034486a05e7150ed3fda45987430573fc0ba4bac3352a84342f17d1da8eaa5b81014fac3e5cd165e4659beb9ab6c2b4910b3d906e77f7a8b34c20de1348bc9cfbf1ab4c6f824f3fe4dada7169cff734600733abd9bbf0137aef6901d11163ef1a0e130f918fac0c90e8e1c280031c070f0bb38d38fe55b844581e6677d313fc84f0bec9feb764b9f3dd3f6e63b8ceb84f0e2f8501f8bec43dac4416bf2af1246b4acbd734abbc7404905feb8bb936420d01c24f0058a5019696d548db36ca2a8e8c30d66a6887f37972f90305aaddbe5d06ea9bad064434673aa92466a2bc13b729f1a5acfe1e33bea1552b283b2eadae095aed91a34452ffd064e1033868b7869829061046022bc7ee97882c1233536105f25125c17a81fcb43bb1d3ef920cc4abcac82328f6f01c8fa1fa763e2d1349074a244833a3b184f08fc04d8d4bda3d5c157bcac3f1f6138b5cb8eb989a9f3c029f2cb8c010fbe3743cb906c5ff6bf93a881914c930b06f0322f1f98bd94c08050fe98befb6c4776b52f7a58a114181e511108fbc262c1af6d6a922d82e4f9505e59fb9b74428c3e06570930482f82b2e2fbf1ea57e149d828f251ed2c470110f89a0593b75e411f597b5d8cf22a24f207dc4147e2cfbbc520e75bfaa6f1b0d0d68eda97cfdd52858f3859c7bab7069776194d714bf7de7416d50749754de3f087703207495a373c29f096a00ae5bd03daf4510392578bd580d77b355d0a9b40cc9a2a25c03770192bb645d4cc61499b58f46b8c0ed3eef8e855a2fc7deefaf091af3501da6dac9a885a481e4a4e03fb7a1dc3f6cb919d01f564e7a6270d88300af4baa2a140231fdcab55106e91db93933164d60d76e67f5b100a4404dda420e56f5bbd0d49c92ca07515e74edd629030f8d2bd2ee0a2deba9146dc5a7dac409d089823c8685ce1bdd7672ddaad2330541e7c69c224b4bc560f50c2e6fc0c70a8ebe3d2117c03e88a9edb8b4e322c1684c0890797e8b0cc8feab549f1b9b3e0bb83c49bc82addb1a69f533e69c40e3ad71cbefb61fcc4e49ec1c0cac07a943052c640b76fdb3d2bd7e921e932bf28432beb3763614530a9d641122150a19270e6ccf518acb0175eef69879627637d8a762ccb87404d391d4dd217eaab0189f079ff8a990ce72431af49d4d5badd7462b78a606fca18f52a23a56d5ecc7c7c80999bbd1564c9b43b04efc80a665141ae0824d3e3e071153bb7af1e7bfc2b8b30996b9aede9c29d8f2f1c7694c9c465f62a441051f581fd0e8b9dd2efba7543e0920958cfa74b569da0fd27e9837637589d66e272f4ec4b8c251998dc10f8c90094e817a1a4f64047bd4e89abdc3856a30bca6465a0107ebebe62156803c6751040f4da71043cefb5d0e2f8c72f8f9dcfa7fd25f7338a8806edd293fbcf25b630d1a3297eb00c3bfde2a32a45f3c8ec85c83799d8651d5673a5990564e6b46590c90283944d53fac2c44e4f5d8355b1d3a09bd39b4e9c648a97afab387dca29d0a31aaef5a786c2890ad36af71099c8531f57cbbc75e15c505b262d05216b77903fcacd0689630d18a4422bde8c9d929a0efbfa2478c6e7ea6d825c12fcd815c09f6527c09a43545b6eca924d94369d33df092f97760ba1ecfbe962a5f224c1a03be6324db6faea93ac57b2c7de581f74f8b729ff4a6e1d8de33ef0f24471e6408b3ce4b12361cfbaebb894a6687be08a04544b9037690c6648c9d29d58030e102d40df79ae96091c3370832a4ba2ee7166c65c5f74731cb4fa53f795c6c6ba10e +generate_ring_signature bdd604377d0adea441787f06cbd71142cf15d55ee20ec1bea85f573661c71201 2487ddcc1b463edd2a754b037e4ebce25e5fef7d87a227d6eafa588404345543 35 b089255060a9bfb64276f30e0f156291d61ed74376751cef0c3c519bf97005cb 71344fa8a1d99b228c90f79d26b10a0936b43185ae7e456376f6bb527bff449e 2798b734c642a9ec401cf1a33c14c239c2a59c5f48eab81450f06b8824589ae3 3aed394a9a3077b2b7b6254d4ff647bb96b7438be120fac9204096b5f169673f fc7b91dad8e497665fad7726ccff5cbb5c611400743fa25dc395bc95f09c7704 d4a4dcb19764fe5a25a5963903ff0c0b9134c4b8be2cf7f33e7bbf42b59618d1 38b63cbc455b1340fbaf53d4284ed6a0db201b4e4aacfcdafeb6273b1a307e3f 0b8d7c46fc803560f12ef832de6079adcbc1ef9f787b45de01a737ff5e95836d c50649bf69d8dcaf869c7e42948ab1784cfdf39af5a7e4f710726f439b25fe51 cb559abd56c267e40422864b8e58cd27ee9f9076f08da0fdca1939d20e6500e0 83c0c2a194cb48bd356a96e50ba828121c00be8e57a8ffe7460fcdbd543ec13f 1cf20a067afc5e04c12b6ec9ed5dcd70d647fb13e390b35b24503db9590faa9e 77ccbfb6fdc8e4fdc4b9f1d5b642a0be86ecb52d0760e9d37949c2a08be3b17f 95061c0154e8feeefdc01a2d1fed47012266721d0fc3b81f1e39ecce4f8b2850 2009e95362d812eb87acd566bc521ad12249dc3b1919d8af30ae0aabb389b6ea 7a8431ca2ce7199d1db1a3c786fc9405dde6d0628952cf95174f0d35267a41fb 8fe2a2d24ca5a08caca966a0a5175b9807f3030631c5b93b7ac35eccf25724e6 67a3edbba5207baf306674a7c24b2093a4bc41c3d43bbfbd545175c8c6028bab eaa0962f91596cee665815b64415daf81bfca68eb9cf5eaedb9ac0a0c4ed0900 2f87c482a8ebfa50f9776869b0899613d31302f61bb9a8066182782b738db442 1fb8bfaed14ae9aa8f13807f7a07d705d47cd642349fb1787734233b7c85a3ed 35be0bfdf4b725f01e4f05af0bb9f02ff0dd4f612ded87a2340a1574b3aae315 4a0fda512f352146f7859748681ba33d2760bcc72a20db5922691856f7e603ca 33f200d19c8fad76478d2eec8699beee2061ea1f31a6f28e764cb7025691266f c968d9bc39daba4299986612b3467ac3d45ef0b830dd62b497e52219b175fccb 22460e30d871caf28c49e4cb59cf296682dc386f5eec6279ffcc150faca7cfde 1f92d5efa075ca1f939b27e1b0aad182cbca7f20d2da676ba1992d5696dd17d8 73cf74bf162bae040c2523789a0bb3a96cdf781f1a766754a2e2ee1bb200791a e669e339a639b731135147dabc872053f458a767ca38c35d3e9a3049f7e8908e f8183272074d1f826677ac9d576cb3f558fec821f5e32cb337107ad6cc85e63b caf86118b2e212f253e171c428e935c8eb6cf6ad085f25af4a0a93f9ec59ace5 9dd75314c1d8ba4bc81914778dd77308015880272b65bc37c402b1cb832a69d0 bf83ed74a2f7948e17828735a98afdb798a3f8793d1df6d4d1d8069ae7403dc2 b553310ecd79a8254d329a3d6e2002c89a501c06b6ae3b7feb152293dd26397e 7d483ab7dcec571e016a577f74301a6614a34734be679ecbc324170dddf06aa6 57993693eb82f28e7ec9a7bb128da07bd30e23fe4522dbc460d96cbc87482706 9 eb06eef8d06cc359802f960c4475585f95a05a1ae87a181d896c034d996a450732640f58830f6b2c4ad3ce132b4ab0b6c8737546eb45a08585bebd3ef20c0d01da3a2798cdf9d1a734394f3900953f43a1c62eaa2d26949e2f9651774f61a8094ffa30a399ef4204687a3464f8e64ea56aea2f06e17e2ce9d24403c8f1c23b0a6cf0fcd37c5836d9d37a50eeee6058790f745d7333e0196a6f12788deed3f5078fb201ff59faf7a567f09a9f03548b5725982268c6398ed0c67580fe7df5ce034f7de93e3bbd7001dc6298ff56b885b21a1ac6cfd4fd3f750775bdad9f913d085e004843bef5d9bd0ac9d62cbc77265f7cf053ff15e81ed0d481edb801103a0762e12f0e6867cdfff1a3f03e8f99dae3db4fbc5b90ed791d109bcb296876dc0f1344309c66fa2fc37c2d5aec0e831c44cc4acf3937fcafbf0266103f007dbf0c2de154e44e94d8cfc797b53e05dc2d3d61115dd154ab15610d74c3424fc78202008398463eaa06bf687780829df04b59120def1b22a915d0790e664f5d8531030243b54f4824c77418aedb58eacca8b850d5006a8943193349c3d74811c6570dc7a3b596d308d44fd2e3e64f804160fc2f797c5c15ff4fe1083a4861cdc50e0f07b331b4f32e15a5aabc617c74eea170c6214b2786876d670a5e989e4c006f027c8bf25459e7cd40d2f9834a2eec52c3dae4b933fe39703e8b69ab46c871a2029b96c2e758cda159248b93543dd85e3efa10a003f5bed993532f43dc0179cb041e5ef9cb37a0a5f10dcdc255523228cde8d3d3636d93f906e302db718c415e076856b278838e7f08e6f624a0d5ff180948638a44eb4e587bb9ce5dc2c68d2d06954815a2caef8445fee06bb3e502959eeb61d36fcd7c0224abe0ef51d9b9b809a4c9db1137fe5cc1b444e04d3a82a3f1d5b7fade44c00dde807f54b2c1298b0b0da9dc829b9c7798e10688bc8554c1bc148c67f53d10eb956d9e9f7ef1fea50ed0e6c3831d3edabb06228742c3ad6d314926f2eb73a5efc9cb396b98a2b3bb03e848bd4e98e46920b99512ddaf48d6476d59d199180f389c57a08eaad39bb009568daa0b4a6a7998f0ff6675f207309db1d30a565bf50c03b4063c3ae0319601faeb5b69d5b7cb0f5cb7cd7b7de440538651286be6825d087270759649b2e50260c794e1f45bf0837934fb0c565e51d659af70a8133c0e9cc734e856758bbd0b3cffd96234a40c5fa95595d5f421c947db1054066909e409a24c13ffecbfb50e70337af56c5282ee673c0dea3af3599667482563d53f174a32a2528ea4c90c0b04c6d9d06aa373e0efc6d4eade376aa52da5119410b62505da31be7e37954805ca39989ecaae27e8f36fcadd566acdfb8fb28e0bf8b728a8b16bb7c0fd5186079b6c7ea2d57dd12cf0e7d2377ca1bd3188a96f163b05e1a4c7f2a9a648f5f204e733187860ac9ff9ba9f5a41fc8599f090a11f34ddeb90d3ce9bf69dcda4600de7fa9c30c0d623f858d4c009c9454929cd853c62ee265186513c3524457b1b035b3a6d1ca267be5a651b7024306c525c27d298985280d8137af5cc583d59e50980846774b9977597328e2e565d65a206be18ed5b4fabcd32f830986ea5b806079969bc84d4ae99a7a5821ac2f0fa11f045de8295765ba588cb38eecd98b35e046b51a9c9a7865574cbe1911d04ddccf73c410265f21efb25883bf2ff99860e0805f26a7c36026ed5fbdfa85faed307b08e05c85bd1db3811b4838ed24882ea001a647b6c993b623181b2cd84414433932091252ffac639edafe643e05ffd2a0f2fb4632a5dc478e705b1b0d6248067e9face7cee3eae57c0952ae5d895fc19048d989393e3fd9c83440127e5fac6bed1e629289fa4f290f1abc711dc9f74e203508cb5427d5f283618ef311364245897092a8369be7550d4064ed8bd283d630690f25e786f726a96bfee1a7d062c7086a5ff2d38e01a863c44c8a2b3d88bca019c3e5c6e8399f5f58eac9890ff36dc343cfd89d59aa0e0bed2faa718a988bb040e5bb8c301f30b9b26ce46974ce6c6fdb0679eed96abf6c46d60fddb4cd37807376c5ec9e6d541d11d3d91eb341ac86256fac22f59ec73ac87306b5d6b1f9c0c0b44f4a886f0c929d79186e9c38ef0350bf15987a98584521b17fc7d89f0770c97e42dbca4eac4c90596fea1b1201a1053611d088f3a2e524d53705365911e016cc3303b6aee6ade52cc6f88f54a8d103945a2e17250cf49d252c34a7c00a9003842fbfe5a664d518aabf0c8147c870d8fc0b70b4ab432cf968398d52494be022fb016dace2e2520fb832491e793a19ba8c7da4b4bfed3c1f19f86e2dba44b01d2586e6e1d60d869dd40c2b08ad1b743ea73a6701fe5b1fc375393e7a3788c072f9c5bf84fbfdca94340a9731223e44e03c7936d7e71314a6ecb00599d3c4e0ee15c6a4efe7f11f08220da2d758b4d444db3892674632a28934c29952a143505e8d6ac31ea886d59e6cbbc16ed12c4240b95b0a381d363bd1c73dbbed79edd069821f31a189d5cfc742e89318eb6134431f3c746f17c4f04a3837647f11bbc00cc92d981b05200c9eb0057484a8550be48d8be660fdd90b827c38a551b6ecc089c2d4ab9a37ebd9fed1c9656880accdcae9db7b4a183c8618a5b7b3f07a6740eb0ac7ed2276bb1980e9957da5813af0fcf3bf453338192050720ded52a56a3034155bb47c3592b977a46062d4ba1e5468d30b7223c8953bf1a3af50ce29d4d08f0654388f8735b2ae0f12cc96a7f71e1acaa12e0f2fa29a99dfb48076e549d0384e55f60d9c18e407b2cb1acf2bc34f06c7fc49fa3f6e79911bf885764a34f01a568e48ea4b09382d815a20a50574c760b99efb97a0a43665b2d81944e878d0699717339a18cf15d5059abbdeac15b86f2534048c0de4f079ab6b0a6bfad0e024f9be3b8b986da1f4b05f85604b38514bf69360f9632cc373ec1128942338d0ef64ff19458af3e94b8eef46ce2559c1250f0b0b12e3561d86106c284fac78d006e91430a08c5112f5067ba27fb996410cf6b3766f2c7408831f3023e96150904f4770b73eac0f83bb16f4b6b77aeaefd0dd2ed2f1511a1bd1233780dcad5490f4e3989a1f646f1555fae4561333e6cd098429523d84ecf3bf20aa27ff41a9800 +generate_ring_signature aca251d46d3b5ba026de943e11da65c82565e3661f1aca050799c330dfad3d41 b26123f9a3df78771873063ddd286b3b07e8c622f56903f2efce7499de4478dc 1 332885ac700a35ecfcfd9d4b87366e052b2b64a9b71cede41d1498ebe448e3e6 e884832fa0894e874fc141dd3685f50e5db4e63743002c9005997948552f4b0b 0 f1e70c597eae68ed7ecfb6dbc2e55ef21405edf3e1008c7039cc130172e0cd0acd8313afbd9f598980f8cfe0a86ce6c7f010a875fcc662293ea968350f7fab05 +generate_ring_signature b775c2fa902004d9f8b68d5e8d02f5a8c1a5368339cd1e9a5509f9cb5bde73d5 7d0c3bbc111cee95905b30f237c3a948d90feaa912301831d3b3d0d765056b96 1 8594f65a950e1a254db53911145528dc04b86702259d209172a832b7886d2d2e 007a01d31abf4ef99af89f0054b67b885b1964b5ecbbcc2f1c9e5dd1157c2a04 0 8e65e615a34134d21f6fc41e56952e9b28f7d6fefd633ecece9c819e00645500b048ce13c1e513cfb87584879d920da56a094bdcce7b1716bd0feef968fef106 +generate_ring_signature 677e5736293b69b1f168f92d1f194d05ced68b431ea47d89d1805e285a75aa96 c7fe04a57b9bc912b8b51cb394ed14d95cf3175482764e5a9e311b79669acd05 3 bd2dd96a9f97843f6f9f67291f4ac742f7dd142e4bc174d83242322ea2ed6578 519650d18da99601cfe0ddff1cb8a6c3c5a3166ed0f4b4d5df08e731f1794af2 cb6f9d2a06625c1ca76b07ed61ec15de053473ddd9b648ce6c912a3d176ed6e3 7fdad023cd867a5981be27fac5ded58a4fad51750510df61abceb2cb6cefbe0a 0 9c841cf647c280baf63bcb627a5fbe62dd601950eb8517602fa1e2f4cb7f060a81157febf210786651b9f7d772a1cea0c3f8619523f780498b20dd4614a9f3011d1831136846d7ed5b5a03dadcf02b22060f2c1a07cb51eb1012dd711edfdd0f02158ac98b5e16ae4655164293bdad41a73e49fa919cb18b3540dabfaeea6006d04f61cc1c6263a3ed18c9be162dddc587604d13246356243ae3cfc11bc7f00fa8370bfce21c305439b37c109e59eca60f93ab9638d47ac0c2d1f72fe1914105 +generate_ring_signature 2c56d0869d611a98a8583af24571d794a9f7d657bc9bb2668f54c91339377b9c fc4553cdaedadf367b81758a5b277e20625549411a88c7325bd8a7129bcc476a 2 e82c7b44deef558ac8ca40818f843d27480d89f5851c2e0ead9e2fe58eebbf9d 66b8b0b3f84eac9fc67ef4d8f5324dc53526d1906f0f830d21bcea089e0c42b7 7e23cc4cdb791d7b4ec1ceb2ef16f65d29edc81cfe5786a62f4abc457c4a600c 1 3d3efbed657c2f625b44d21d3390d2faf56052b71e2431583191725e902eae0a12756cc894a7341dd1ed147d7e57b9ad6daf653d921558bdb14afd0ef601330e759689b79e77bde4d641f1853df9c0fdc7fe827bab030f0687256ee7428829068bf7d985164bb91247e92426479ac788dea2e16755a89b611ed6b3a0745fc40b +generate_ring_signature 8446c895fa03164711b6289f8dca06a575ccd8558fb070d1f07a053b6ac62a88 8f78a27ab3ca15e3a040e4ac2fe32902d418b7317666becf389f520169099aa5 1 05291f379474a1eb448d27e66553fbe9df2a5133b15392f6a966ace067a870fc 7bb9bf9cf9611b1609b19513734454f28f49f896fcf29e689a937abcc07b4803 0 76befc22f46bcde58f10aa79595b3552dae008d60dd5d90058fd59777506cf0948cbaf4528f5680f585a3b0f59f97c8cbb0b49cc593e86eef7e9eef2b0cb5c00 +generate_ring_signature 89919f2cddcd14dbbd6510687053b794e5b8f01565250bc8968b781f24739668 0279f476b72eebe10b6db1264b63b2b074144dcdd4c0c2764cd4d1815acaf713 2 f82d16dd5f4d75904b7b9195713ee68a1595aa8d9de4fed48c72c0529734f52e 04569b72e8939aec2b6db3b83ee34c21326a951c83915e1f318435f609445b41 824dcfe5b0051072ef13f8c9e183c0d1ff5f0dc9a2c6f397bcf1bc3d626a1803 0 c8852dfb86649336b7e56f5e20b7e87f7ca1d6d39348992b52bb108ce183a809f1c5c1e3acf7a49ecc4bc8d655e877d92f9e366a191e1522d5e1ee2d9a5e530aeec566ff54ad190212f115478e7584c4ee7bb51c0165287f484ffd846f335307bb675c3e0bdcb3b0feae1a2b66ce58aea59fedbc7637a0e1cb8ae06314c55d0c +generate_ring_signature 0d45a262ba21084d5b11dc6f7df59cbed5aa38f517ca4a814ff490923916a652 9b86f1081957871b7575cd304b3cbaaa38d3f00703de9cf9e8e184128f864375 197 d0f935d39c2c505fd5a8121bd2d4a741896f55ee2470792d924dc0101a6a0366 460c54ce55f47189b9ef38efcd9c2677e4f3b7705893761ca701d0fcbb17530f 497b4f9949d0021f02b66a82223c4cee3471218d072dd5bdfd3cd75f22e7c0a2 86234ee6f836e7a59fa06e91798301543347f89beb69c47a5610cd6a48b80f81 eb8da56b6a7011e63dabec4649def5c6b1e90ed607e3526ed329dad2d201681a 2d35789d85de0049e0b401689d2cb627e88d8fffa91cc98cb6a0cf41d596c936 c3789543dfba6c37c771f175366171d91d5ee954e9a0626df83d371e1c03600e 19096bc68e91305027ce6f497869c5187fe39a606064f01dc45f696486fa24ac 59731aa199ccc0a87aa746c6e6cbb76295bedcb6269906637ebf0ec791706b0d 608f849ac3d56aa72ea3d80df7d5a784dc4210ef2196ac1fd972e9bc7828b378 e98d5ee1f8de14bccf0abab68e749a9c3288d3d0fe6406d668f912ce03e5830f 3ecbf97e70c7bb254d6afe53e6c15a9e240f26fb732e9c8ad1f13e6f5bdbd766 bb32c560feb78ac2da03f973c620b99dcc06c726b2a570b732c862c95a1a8f79 7475630c541b4b636a91e93ee3610ab5ababc266db827320a8814c3a347b9b8e d0a372bb94a4850540f4796fea6bb38d394a47a935d2dd4a5326d86d501c767c 74c6fec0ee4b8931974d59e8dfdc4ee0e28e3d1418d23428234b48ba8ad5393b b688d964bdd826f6748bf989babd2c55febea15e51bc92d3ae961509c0e336c1 90d569472bc45d3201db602f71869cbf528b2b6b3fe19105278ab39bbe1073b4 d9a36d7afe645436cd02b3e61a86cae1999ab5242d039b4097508861a5e247a8 22c1541a04cbc85028279c5acf1b54b6151b82d09c16513b646a14c6944c3f5c 958bd65d4e627939b83672a58ca806770be18562c061c2a225dad1efe0d95b6a bb3f7a2da142d46d520a1741c9c24d85804ea3da66c691d836cc3b8c5f7421bf b5194a6a410afccb0a4963cb71afe4c177cf90f9a7a3a3d4e9660f1ff064ea93 4f7fd2fe01c9dae2050735a578bbfe329a5f8e6153816ac53dbd34762f87a8a2 6596a10ef0c3eaca356f8873042150216ea7df61d25e36136df4746fa77ec0b4 860402d1f5982aafdee30a7f8830a0f4fa64877255abf4a5e2abde02f43e5c2c b86ef36d3b6873de1efe9e02e2083d3bfbde0ab81aaa37af162f1a6fa0eba0b4 5e7000837abddf0711071b6d205f999327522033d068d3d029187b083b5d88dc e7c7f0e549f921bfc164a4b724f261759c46b147b8bbf0e6e68c39503c198b64 fa55f966b88976d859d6e4776ed2dff074761de70d589a1c89c86279f0005a3b 75900a1d92813d506556b6484ca988d165f54008f1a8b50e0ba8c7b23bdf871c e7bf33d171b351a061ed80d3c52029b6627c7b73167d04f11e339da701cd0d6e 655314b8659808e3993409e198dbfe6555053ed5e0c195d39808ea0f45972b49 89f9d50a6e5f312d92da3763101453a9ecbaf69e84f47c21d855a05bb54a219f a64975ef5c421dad5eda121b3c3e58f818443428a05263053be3da99fbe77df2 5fc852cc8337bd39173274d4f3cf102e0c2fab30ed3d8ad70c753e3c344ed4f0 61298f604f8dd7e109b8600eb9a84001c3d7dca3efae6229dd12adaa4e3d2c6a 07f4d5b629d9c2efe6705f2adb2b9342822e615a9fe6d98897d74fdd89fe2878 5e0f62fd8243023c0b9e7dde96d4ef14e3079f4b0d7e76630c670c76847aad5a 345e572794d11d758bc761d9f22f977c827ed332108e810b21b85cd6378518a0 8afce8fc456c8724baa05516fd9af2ef8ddd29e538aaf1214756f683188a1bef 08897543e204208ef1c1118b4153c450245dcfb2b7746e48e8e389275e816861 d10372082654fb3dec772174e0967ea577c5d93d4ecdef99c70867d84829a10a 4e038f0e07dfc7e752952b19ec3ab3355b2b92e7a39182d3eca79b6a3f87d14d ad67fe5388f5e92e911157fb2b034c1a615a1eeccd3858f255bed067540dc711 c40a5eff26f7221382959ee22dacab3668894870fad1934eb8ad8a7e40ed36c6 ca5f7f63c498e39e135e8c18f0262702de1348dc79f8204c216651a9c7f9ac71 e578f8694a5e92e48f06d5709ccfb681b9c8a738abf20861aa51d61872cd3348 b238cc4ba4613e26d7d41996e6a15b7a1b4314aa7157a984eea7d2bc0706d00f 52a762e09b64943a304ac9c2e110ec56493d00ca752ab2c715ba72f7d69bec89 b5b444ec26de88fc6ff295056545fd65ae83c012a45683b9a22a379efbf186dc b74cde735535b0acd9ac6da6a18c10c4d3e889f1b0ee33aabbfe3e942e9f6321 ae98eaed6da71726406457e4fb6b8fd6577e545ccd7aab51e0c5d51a44265e4c eb366a542d43efe8e33f1f3733dfaacfcd97e215a44ab0cd2aea54a485bc1ee2 710ad4431c43814d664c3a3e0507f59cb8c65c3fd6f6b9382e84bb308ce2dc52 957765427ab11d99da32c054c7c68f888f02f1f2fc10c5883fb866661b3f0e1a a453b57b38dbd7b2af86ba28379d8832e3bd57f3b54897875b458ddff98e7949 a9b5ddbc6bce04caac06aec9761a507f0e09a8bc3f2e7fde93304b5fe5e08673 e8854e7fb42255389b91010d08f90ac2d86da53d338f914fa5edecd400d29a7d 15dd3f808f9f9a4e1f0f68a6157d3541dd24f5e3e51b0eb51543c1ba1dc7aba0 4f187a0886930e1129c97e3373b873c4be9875f745ee91a2e20c2e713a133d8e 64f4cc62ffefdd1b73eba2cd09c5f7ee3f2433faf82129ab79ba6884040fc3ef 735c79657390255c9072a9852bb1d92425811a7703b9809eb3793f4119e00463 5b88ede31e1f160f65b6063921a2bf8b5a28788ec8c3f54089aed3d5aabb983f 7c1252e028be184bdf13a60a46bd476217cceb393d19db69ef94bfe6f3380ac5 d2ca845fc232693381d777903751160a1fb43e818d548f0a0151dbd23815b260 d8ac75db6b5eb39e3f5e23d0b88f82a51552150419ad35e2ce1eeca110df4552 71686dbbe145ed2962b857fe2b732bb0006638ec2c91889f6ff79e1729554bba 2ded0ecdba64251aa08c39c52d0ddc5eed4c85df82f9856cda1e0309315ed47b 765d23651313db76c45f44ae14865aae2c57f8c67beaf102cf299f8f4f5d856a 998456c7aec76489456b3700990c3bf373bc85eb5643a15225819c91c8642b85 340176f1b4bb47be1b0108d75f4f06adc449a28a3ee4b3b7cd58d186cd6c7c19 7d3293cc9c72bbb56250a56c44225e67c63cbaaaf65f2e3671e3a8553889ce75 ac9752288d33835a09b7110f63c38c74a3f24b97fa26471a9d5adef3c4d27373 e152f9eef552515995ef4e9ce1257287e5c7848da1a19697c0ab30f14f7d1d97 08ffc32c3134d2e1ab2f57d3b287a8f6ed83231e756905136b42c1b3aa69928f a5180c65796b1a1ab3bb051ba05483f31e84f49c1eb893392fe987187ef85c63 8e5e0df1415bfe86c31d128a94174622e9323cba4e1adb06805e92e7b0620fcb 8bd78cb19bfc01c452f2ebf461670e00a3c3aff5f7ac6a1a789aa8e697ac324e 4b65071088cac227ca7b21538cfbc0cb09d0be5736b852e44f1b02508900cea1 6256b8a6eb474cb662edf8e72d45598a24d9c9a5d0c2d4ce90e6e4eb4ab72f8d af391f1ce7788799623687e8f64fb61e742273a8a7344a6098dce9f6e1b1d78e e74249c6cc363e2017d459fe26ff53745f0ebb45716b03f735b00889811935ca 5010e69034d8eb2d46d1a8b4f0982c2ff82f6a13e81c61ed262939eeac5cf3b3 88f249e548a21b58d4e63560be38b14054257f5b88adb0c1b2db7248035dac78 925e9971e38bab4865df5368cfe172581a48e8d8a25ffcbbca2dc00a1c765488 e3087dd5dd33f774691f876a3755f311cf77bb6972eb2c6f56928346973d8e99 56c686807919352f157d48805535d6824158026d99dadb72a104764d7983c2e4 46800e442d18322d36f6978aaa3790dd145716459f3ad4a04be9597725af3767 b32dd7aa461a9faeef0e495886d5d8c7fce4f8f5b7fb3fa42ac513bae5903793 b58508c057d5cfe3a4c4524c96c74f5af636b5a9a56cf1bc4cd816e6d4d465bc 5cfc9b6f9b677441ae34f285fe46e36c922c15ae955a9169d490984bc8b626a6 d58b254b005b382262704dac32494e11e867b30a92df690ad0883a5bf7bebedf b582fd6d2430c8651f15e80ce36cec80b79d7283bdcd5168420fcbd779d1e9f5 3e684f24aaba4b871045cc521acb81cc4a9068e06ae0a7c0c4a7d5ec7e2947db 809eac13d43c2fff1e919d0d5a93a17f97775858fd822b787cfd9d040cb0d341 6a6b279a65c84c83083de47e5ddf51965103b6cfdc39be8f0920cabc138d1b32 11753c1fd004eb63e3296009423f7a455019ea6122a22e06d300edd724dc054d 2d32f6d1bb3a0761755bea3b77ad838c0e0b3fa3fb52186d2ff037fdc3c0f198 3f8fc50bbfa21a30257686beda5a777728a60a3d187fcdeb5e361ea75b4d66bb fa0d4059819fe819544297431c1f676d1b486f9daf8611012e8ffaba1030d767 1902609eecfabcb88c566f1f737acc62b41c4131178eb8a8ff68506e2972875e e536199a16fbc159054a32e48cc24c7f151500e82c2757353cca78df3b72d22e 659e8fa4ee5592b00551f6f3c6e8746e6c05a31a46332717964ffbcf24643b30 594bf8cb04d93a3811fe7bd3d3f1a413775aeaeb28d3b06f7f29617e58e3c3ef 7c9d7a620d47d9512b5dad55a6f4fe98940264b316372b74c0d39a1f7b355663 ce45006b86dd046a5a18e70801360901b0d6174a695418bfef4dd036c55753a1 f199d0a10dcf37bf413f9f8617863577ee4a3b86454e3e4c34833dc51ce8a9cc f65ea29258bd880c7924c54f538b08a9795212830240e606e5d751b3ec8e3b6c 4a4093fcf50674032c280905fdcbd285a626d87b9d077973eaa690ca95c4c71f 2f660dc6c1a14877e7898aa47ec02bdfd278385536fd9fff368f5303f1b82f70 05821ee0f3ba4a2451eb951afd27a624348f9bba4561b1fb9089e974aafd1316 af33925039e6797a7d7663d6c693ecdee23099b60ff22c559635b5aad147814e c3662d59040b9dd071b53edf6e41943332e09c6ea7e4e2ac12062992376e7b9e 67c71e230ff2274b8fa392df01d43ee6958d4e6f07da64410754ec5c5ba52881 e2d574143ce458f5886f24d031c49180b3f78c55309365715ebcc8a3620a7575 63160938651dd3ff9cd282fc5aee479afa13705c7a2c43cf8fbaa286cb216070 e51f74ea5fc1d517a12b69e5451f3f6780c5640e5d590c380f18a3300b935c3f ea8345cf437c679dbdc05fe2bf30dba47b35fe45c5ab2e5b68592c4adb6167ba 893d54c1b4fdca988943c4d51d87f37185139c9cb4f8e3626501e36d6a6c5042 92722b7d3ecd3d5f6a50ee26efb4de32b26545b58d83886da5d09518880a6ac1 59255ac1b983a2a2e1551a8f19b4ce70575d7ecf4970a250e6ce5634e7220a39 e76d98db55a8444e9d82f28903072fa1b9a940298b2a2b892ec198bbf576cd39 0c624da5122c9be9c1f0f3d6cc3caec5eb3c5666d13241be0e7f2e0f8ec303d8 0b5b10255ceecca806ddb8fd3f09448367cc961d2cbb6e544811e5a6adbb7b82 264ab5191f54099261d36b2fa67fbf503042ffad98ded6ac5caeee9e638b5a6f 1ea24d52a43ed3fddf2a4fde7d13fc73c8757e7446cf7e95bcfeb16008895c22 a369d000754b106a32c7105623859a2b44d167ba2662474d64ec90103cae1782 87e6981a1eb1f422b6c482628204998236f01cd4f563d9b883decea4781b65c4 26fd4292f36e22a31a5f17f918364d0fa1af7038b64eaf2c77abc071ff324911 1f7fba3490dede66a8ca0b0f67f069f48943844c22261c69d586cafe9d3f86f3 ff7c7a106db23bf12f87313679605e6b9e0c5364c20da190d5af82b5b6b22fab e33cfd1d5133ba44214fb24911a44deb554830bec522fd27f0d9c3084613f413 6dc71dc6f84a8bcbdef2d72e2fe12f21b29fe1253a015fd95dfbfe755486313d 6ba2e3f0a288699640458bb12023f8b1c6cf01e292f386730a7e04ebb17462ae a1ffb0a73d396083ce3a51fae7a47c10bce4e53f736331ea791393d9149ad791 fdb87a472b97c99a0c6bfa8c8ce93c6327dd796f3d71a3a34c4c81926d6da322 91990deb0c917e2d68ab5177ea86fe68552736ac7b702fb81dd994581f60bea2 093beb91bd66615026efeafe94795e5749797c8339f61f9ee3ae96e8a1a5e0fa d1bbd9eb353fbe9bc9c78fcdb9f49d56d310e287dc698ba37f5943660f2294b4 8342b638e5299018ecacc42e496e7b476d7f35ffd6568600573da43331f36226 0de99f05c276e608427855984b17f6a513383f9f3fc5914d59b85a214dfb85e2 c9013f923e6e641de8a15f21c821c00ce771fa5f36a35d6eb1cf71fb4e633296 a3ad1f8872b82cbf7e2607b5058ae202ed1c4f6b5a963db93c46722295f31479 f82e25aecb0428d36f2cd0ddd00a066b4d1dde1150f0a098c2df2c51003a180e 0b12cb3bcac1c79d798fc51b32de043cba54ead89ca92ebacba60d25c1eeb057 d5a7bf94666cca12b65df7241020dadb44f4321f8a1784a3467c2a2950723465 c439609c1bf47a2dd78f7bc5fec76ea959e61a531ecce3c5132f0470a438ca75 88fc504b3792b714cf1a6f09ea06a61fe625aeb30656061e42d839f0edb32db6 e2564ee229515f37c70214195c39dc123f988a4ac285f53676330739b5622da2 ffea0d4088d7364d36827ffd49a02c5e67d2d1fffda60e2268a361f9e665dc41 b90a124da487e998d333f782e11a2811d686dbb2387fdcdda8a5d03197bcb4d1 de4c52684e022e46d24b2085c15ee9750192427e7a9802e1a36cc1ed2dffbdd7 656f299420103185383c44c5962ec031388ed71a73bb7044d789f2ef6cbaf1c3 970fe375583d4dbcc1c7ee1ab99dbb0c96090524ba6eb8824b1675c15dd88d60 aab8f210d3a0fba5dfacb2d7173f21afa3ee0d6a987478f4feb3ad810c5f6f03 3396e08c2650fe809a12b5f44bd599b2cd4fb31c4f46ef409a49fd84a77a01c7 834731fb9db2e5f2d6e27bd7301e26be7bdb804bf83c1937c3c4161aedc86cb7 f242fc60275cc30369ead9e735952f6d7dd15057595ca6f609dc5d58a4791703 14323f5c93b07305aac7426b3799b4887a475c67dba4765ec8a70fd80606a399 dbab57d98c775867d49d6c52423c0b126804542d411ce9ce25cf3318c4550e13 d3311c559c2b7a814beb3beadaf4222bfb4cac71bcd7983d7217cdaa543228f3 de0667f53f27ec36fc2ae62706e8f1f31ad40b7c41c75ed114bf0e330fe87e2d b69135ff45f3dc2aa4801bc87c3fef26d331bc43ab564b85c13b068af18108fc 661d565d6920d157cde619ef12b6a1c5423c76626316733b2f292d9cfda997e7 a0c95af16260540b15491b134d8edba37295aea89c0100a1d48ed2c2b6d48503 92526a2a4f3a617b69c8f512790a038a7108df70693170aad803110083c50aa3 721bdf17a1d57f5d868618a0ecc1b30e5c7c532d4313586febd088aaf1e6e0a5 b15a677ed4b10725e31febc960832c10fe1aa4fdef17c7689dfe10b4ed11981e c556abb91a48f5bf50334d76d1a57a158b724738b1958de18091dd10c248388b e15542597c42cd9489f86844ee28eff51fedc25230ba60b8cc0e4364bcf91b98 d393f580e3514fb5b11a65053286acb28f27681933179ea9a876b5f8f93b438d 89e823eb75765537b62451caf9716647134188fd01e20fcf5d64c91de0fba39f 3c2aa2372f3c532107f71b749d27c2e6ff868b6284ba697cdd6248a2282f7abb 47cfaaa08546aa6c24db8b82a782231827772af391cece7b743e458a4d2f27b8 022d88759e90a3a7fe32cf5d4a589e2b6945a534e72ab513416410d5e062d0fa b1fd2452b06dd3cf267af2838907ad6e951be731fbcc7948b41d4ab9e2f3e6ec 1e669738dd5d5b4c60d68ea7b83543be705962589858f38abf7c94154778340c 6704174c51e37e606f2f59bb4b0d81e7911a7106f067275bccc9cc728167969f 27c3e5582d3ae50115cc2ed27ec69dee741d220be586a8e4a15bf11061f5199a b3e3cff7095f52fd5d429ae13feb9bb07d950421ce8d6eb5545c9c9f37ad7e98 c4e86321804d5a694a9cb01c250da8cd8ad9ed0018a57f753a9727939ef43636 8747be44c733b2774d99dc0ebbadd1d5a265990f3a89ec700cf2c2bda2432055 f3fb7830410b46dbbba5df560109114c9b93cc53944cf66657bbf67a4a3dab16 211000e8e40923d62ce5d876540ef386e9bf3d3198644fcd5aa5e3e82066424f 2013502249fd648ff99a93f5c1084e6b241d048348cacf5c07ca96323798e31d 3785d7050ec2fab2bdd440022329e791610e489f9a295b71e241730578e270bb b58f968cded07722e0ba9b26a0be633d011797d1a98328b938aeda8a43815466 81b119e54b8a5c218ea177dcbe332564e8f58c22c08965e6a697595b708dd9f2 569f521c520a7bf7c26733a9cafa9f68da5c35b0c66f737e632a1e8789e68dcf 20f86cb06e37a7a714ac5dfec96d1b59babed0d5635e700fe647de211074cb96 7878ef11b7770e557684f57ca6bf19292f8249ecf579320baf1cb189643f0b9e 80b6f868d06af612f5b7607126bce9df1dd538b90bd227074a493bd0f5a3f67e 33cd0f98b0d746b94675d42147c90b1bcafe16bf4234388af6cb8cf5672e372e 96bfdaa51f13ab4b987db32c5f6c2091c930e2141eddea4e71060d78da517726 f04903094be80d8fadbc7adfa9dfa821aec0e7d7c412c43752b1b18fc02b2f59 1e1a145654e2c3a77663dd13c2e83bf42b183438281cc5ee4f9496120dea8405 50c1b066abade654288d38684a982d38a346295559d77e14eb87ab9c951b7300 69 c400dcd66c9ce4ce86685cc8acde97f62a69f516225283c4aff3db88a91cf305197f9b94edcfe8a28a4e78b14e4399564c04cffcd13d253eb700317fe6e0c8063261649161e04ae8179cb4413e0078462c2cf275683e24fd045e28a0fa505f092eac2d0c056a2366ff5c8112351b25e24d34deaf2b6a949f0d7acba177b5bc083634802751410c043f6243854921c7497d229ee0969c589c2fee4f7b3bf61203688d748e066e7caf88d71369c6a683e34a8da494e17ce743d8b159a10d7ea80ab05f4d852046c5cb70efb4aa0426d602e5dce0432656eda9863becca0f523c02fa44d25787c83770581fe7e4368a3e3a4b30160847b701631a10593634888c038b142816d58f2a7e43adab6759e7551c085a6bb3111abda63cdb21f4bbabde03c79fbeaf3ef2ca3626b21d96f7ced93fff24ea7bb0d6da1c5133e480baa44c0afc2b61f593ecddd2a22ecd7405f87798a70aa3e7f2ef617c1170f850f7d0180cab1414558b2c17c4b9e0716f39ab6722e0d331024ac2f2a56029c9efa062a00d4bb1ff9801bf4066c3233428a9f7f968cef7ef5ed9c3bee97758651005b4250385ec154a1aa38deba0def3d5088cd98b49ed9d7b689fadad356fa00eadf59a0218c9d90b4adf0ef371b4b0e46ed9ece4d7a6e2c7de2070476f871aef20bb1a08b983bf4cf20e174971ac7bd35312ee8104d7ecdd2f95d429dfc51520d90fb0059218483beb000c8506c10c7f89b0f818dd47181a52b2d09dfc3c001c9159780d0f257285050863192edc696178fbfd8aab58b01e0a93e28d472a7a64a33b8000ee37d2d8cf1697180a1829335b603d06adcf68f746074853c9644c55dd9d0c039108ff7a9bc98a6e8d4035a05f0f9011157d9172ed1353b418d34eb29bbd860abbb7cb38ef8c2d28d103ea47ec924b3a90321ccd44f948bc6d547aa4a4d68a05deb5694be4844d0776aad0ea3f05eda75ce3bc4a9673c18caf57cd761f95b40255de7869b3e4d33a87290ff6b647d57b9dff39ea65e8339f773d7e57295bb90f96f4f4701156eaecf778ad2a9d2813699f6f16488c073efb5b637e4e725c2d00f9f5c2040868a7705fe6b395356cdadb21ee9ebb8504f6a4730050a4672dca0cd4e3aa473ffed04bd6e13b36f6602e7f5da33b7683eb5b20de30ecd754eecf061bc1752eb0179724c1dcc45e84bd62c92d430ef2eef0f3afdf6832a4c8946304f8252eae0dc9bed1b7f971b13975459da031b81a7f68834da247660b7390ff0afe5a4b2f319201771e5d97b9c4eb01505511ef60860019ddc7168bcd3054b6014c3eb6364ecdcfbf4fe2a2e928105a4d9e0596f029f708356ebc8f7170cf2203f7994c32101ac0f20a49796c3522ee0d6c87946b0aa54eebcf66a87b411f4c0e92fbcc534837ea3755beb2338cf05651e06827206c8531524621c96eeba0fb06eb99f61d215d8e873043cc0a39dfcf4a917c94fa00515c4a60d79f8504e5140b29081e52b82cd2d9da48321c45aea6667dcaf982583dc1f46cfa052fff911003f2be27cf931a1890f9484d8044bc65e4ec9386c24ee17e95c00512aa9b1e40048302e62066d4f64364f8c03f783a3c21f37fe8e6838d3ed31c90df360fee930710453502ff90f05fbfbb3d09815b086c082b385ac0affda9a2b10b670fde6f0bda34038f56622c38ef7cb0ddf88c1a62960cb344df5ddbc4e9e7c8a4a7669d07eee0492dbb5b9b3fd1fa48047e24974c6991bd2a98791a340a0effcd42cb8a0dca0cf4a41f2a8b7fd62b8015ca06f2eb68d2c9671b0a59cc9c6a78814397c0022fb2f61948dc917375ef30ebf8f8c8d65dcc9b744c68e20c0504d294ee1093060fa8adde244a30a558a8f48fcbb0d928ff8cf446315ccb460159fd1b53dad90a59bab26b208f99019054952c65d4c70e80d58129034b01697e7dbb72345a310840143dc46d5f19b4bb14692e852edfe2b0aec265ea67dc08a7eda3c01d05fd0d3b05f28eb2dad0194ead51efc141a77be97135f38406e932ab1e3257b731000adaca02e1faf3bb7e5501fc7b327df9af47925e33de27a5179b60e4338ac8d505ee41ac311b80df97716990e121b9f8e1433f341cc70d086bdd781a527b1e870bf27d43c38485aae5757903b479de59124cd255323f0a549f64a78a5f23969809deb5daa333afec4042da25df55f5d132dc2a7f941738c7fd196dfcfa76db1a0bb9216ab4d6f366f9c68e17153e36076f9ca8c5084343b5925e19af128ccb210e4292fe29190c5ad23d35352704e0c1de097a60914e1958253dc885aac2e4b108906b103f38ea55ba036a60595e09213237211181bfa4b3e002fef08749b1740f516f1226477565830de455436ef4ba2b15af376ccb0a8d090d2204780b30da01f728ca300c5c07af81422a25ec86946630cd61a2ed574e9b42f11720e937b3037e6154a3d38f08c400d519756360142750accc313f0ca989c747a8488c17fc0cf2603c61a31311e83843ef53f9cad5034b35e19328db0b2c9338dc61afb340031fca9bd320a3d5cce21e300aa4510198d84ff85a4d211799b16d021a8f4e1f02f345ad7fc08566bfe20edf13f4b113107b20a78cca1bb99c70f06b8304f6bd088e2b380418007535e18173c1c7e85ddf5d5816f23c36480f4607cf4e0881a409b81d11f310ae7aefa24ffd06ba45f812043b76fde0887d256452bb8d6822740540918ecb1c669e373ebd518a40e8ccc6e2772657a03dff6ac7d6b9dbc42aa00b6c294cdb557cf06ca1962f365d5d94706cac4e9ae3a5dbd0c653f356a753330f4f75e995e211fe14583db569569ad5ed133d1f2df69e34b83e5b25be881da507a0c65f5fdfe4db0b964038cebc1fe9aaaec0e9df0225743a1e1354152cd5590e04f1014158d4048db661a701241793f54debd8693d671cb9d09b883762e4d20e5f0e9f72136447d2205bd1e465b29dfd37664a5b4b3bf3803f6076487352a30e69c9371c8bc8611f446145ea9163a9810bc63abbf8d6b3548fd2ebc3ba19650b3528eec70b1c92e39124272b41a3b023e87fe10826154668f6566950341c4c0ae5c00cf4a94441321a7c3acf5a6058ec455f299acf966942bfe74cbe75e5d2079b3c87404cebf90870b59e4552c447d8cd50d3dcf913603934d06ef0b668c80f3a65e62c34ddd7dcfead42094e0f904347036cef98e8f87a5e0acfa7c105da056ee1dd462a18344cd39f60637ce06499cdeb71e63eda7eba803ecf26ff83b80ba905229f448bcbd6b4df58e365ef53b3116e842f5d51de1367a74e64d9d1320c2598b8ec8a28210d5ba475d17f7abbcfde3d04b3e81dae851e32ec9a7cec2c0620ecb02a2f1c3067199071a533cb967809e4fbedf147c2a38c82f5c40193250af758eb6cc4b732d92ef6cae9c7e4d3f733ab49f89fe923be0c3a8cc8cbaacc08a4c1ed0f185f6d66a8e6e7438536e9fec766a89d8ca89e16e14c58ef9f25220e43436f539a30ac553a50a81cb2d87e832dbef46467eb62049e7f80e87ec0a208ecbecc910f6ba99e929096293accfb020b843cf006737d0c69e6b4bf10b17e07f68bad5fe751a7e6c015d46c042eb0f100e6df524d05b2f123a3cad2ff2030096ffd9cf62314fd6a88382e2e0466ce91d929614fe0ca6b64aee977618e65ff09fa70d7deca54c729c009ea73105b9e6475017f680cb34bb5f2a255f868400c0193c376f7ba24e556641cdcd64afdfaa6f3f0d378903f2f841b320b85fce9960abf3c360a659951a5afbd16fa9d4a0fee00534831f5ecddef5788bbf26093660267966454f49f775500f05b3a5321bfc2e7c6f06474b50cc565940becf78796058e20ed7ff0b0d721dbc3bd3e4bd71ac6b7db831e4bee48762f9081de1603280b7027e9364c446689540f1dd3e7d0ea034e7e76357468dfae80b4cc2b353bae0d2ddf35ebf0252f281cfd23e61d6b289508ee40ce1ce453c2e1e2cc9e3a580d08978a2c48997703991a3922f68d36a147c817fd9d6eab228aa1509b4b850de2078c2415df86a1af7d953a31f97f7ef99bb9bf43a8a2c1a2d553f4b016a694410c4867a0744c3ed5e2a7ca4aa77f727ba23ad4c8430bc6d0eb7e674ee493271e0f29d882710b6bf73c07c1fec97bc84b7816b3189e9a57cb7518178079514b780105578f6a783ad337ef71e2b90134d504b6bca5446b57d357f4b1a6ced2813309aae612c5bcb48f71e538a22ba88b2793955c71b3bbb75de05f10941e83ffec03303d7a221e4ab1c09bc767f66fce1b0b165c8d6fc4590bc047252c53a7eadf08a64c357d5e442298cbb39d7e744d6365ae4869efe258d3a775af25282e175203db96e2c8901365cf0f37521f166f6aa35d71810bd4a5bc3baacb9eb3fec75701839bbf432e8bd02da0616216dd1b3b1bc1f4a87acce1ee084e00c4388d487a0b906ff188b5aff18b9282a16385bd5d8b27f929e9f2bf4eda70b5b40a3a067901b336a0f76eff2e4aa73dfbed34fd46dc7e8c025a631d7274a5c7f82c1eca1f02dd32bc8a5acfdf2c59ac7c0ba116e362f2d8a8291c11fe7ab43c14d1c995eb00b89ada2f48000c4137b330688739f5bfa8fee35d2264c4bc512e078fdfb28809be3938b9e91a0576ae7fdba0f03b04a73250bcebf651e98aaeb19657101a7e0f523f96b01f89a52055c5173ae44af145a36b779ad1acb70ee397a2834a240401f8448e04061d0017e6b6d34d4b96305f238db7ac6dedca3d5643a648f672b506430da4ce89c8a33e8be5b60d2dcf6ef0a204f6a6e0867d0c3344effed8505706bab25c7a54a57891c97429ec649b7978d4c415d29662dd2147f37ea97513b20edef5d683963c3145fa73a33c6ebd93b6c9dace9ddd69191321cb5c2ed406fd07fdf31309530bdb101fd46ce639ebd158f2e8ba786fb1fd62983d22276f167301ebcb33991d030eff9acf56497021a14620978773e3ce6f281aa56ac154af590829745ff1d02317709745b2e50474050d2807ce953dbfefce566673a7249b17034621026a6df859880f9c4871745ce520b89b7cc2ff558f17f93f73cd2f21870d609a4d8bb34ab213dad81c15bd3c84eca4f6a70f548fe6f4387585a04a6e7f0b5207b947be5d6bc4c223c614422331cf9ed419c2759cd0bc7bc24b2ee5f38009f8c22893be48b1f8b84159303d7f8fb70a4d155f9c9155fdd3383b0d933e6108dcc17735c3a90f667f29cb7db6789e9f0862b89b51e9503dfa76e300bf4cb402b5c147e306994904b828f39e11679e41ea3abfd3afef0f813ba98c9654d62c0501edb686e7c22bc923c250e312569e102b721a55bc72f61255fbdc19b23c640813d9b35005aa8536d0cf7ada2342a6c034d44d180e2a4b78ab986ccb64a79007e75cfae974cc46e90c3ebd3e492ab761c437b914e53f6f9b399a30b42c03640f48d7f779f49d6d2f2315f7ce6a40ea1db9a59e4d69a058abe94e7708ab8d9308da8de9c07cc5b0b79086d5f9d2611fa4c383b80b62dae2b7012148a694d138035e22d55503a71a5a8c51ef6cb730b84b694b9d640f302a767513f3647716090140dc7e9ab86b73d4d72ea069275a168667bebf74bf634b7fcf38c29a7645fc0e11ce0e5c7bf32e418228bdf5114efe80538fbea9c54dcf9d0b38aeb0de60ae0a201dd20af7297d3701c3369ee9ab69fd437ee170e3e03328942403bd67ca6d0ff833986e2839904c00888a2a61e74dfb3fe7b9dda80aa5b8e31f1fd2ec154e00fa93819f02e63bbc267a621df251f1c69a3404d497921a3b0cad359562a1d801e2dffedf404be7c36caab6526ccf425bcf939406e815f85802e7e5f9a3830d002834feebcc0cb68c56615b3d5c69733b63756a5238193297a1c15efe969c6405f0b82683b2201a9bfe3c452b67788bfecea92a4511a45f812e639b7ca78aa40d77ce773666518034887b921756a5aed92b79d8032fec550e90c5002a5db1dc023d39359979ae9dc880e715d2f25f592be9510aad4c52cf33f18d9cc800a51d04c20b8bd80b4b1ecce94c453fd73083812f05ba69c5258ff5b192c6639c717503166d197a5c02174eb0422e58ac1d2461b5e8bd0f722178565f879d723888530b543ac81a6c07e4a62422f128252636525b7a78f3a60745e5cdb1783338be5f0eb70c53c769c047fe8e479326b8a121d4e4e3b4d42fc833ec65b6e6bd7109f303ded7aa4e749c68a8a8b05a7765c172ae4f0b541402817ebf08cac386a228d3076e0246c6aa2893a7f2a11e37b8b3e02c6905e9e18992affe4c36120e94e1050089cee0252279797d24c076ed9e67d9a803e978db88593a47eb0b542af259da05e5673cf2b206faeea1aedd05fca70453210515cd09c0dfebdb0d0b532e8cbb002578e18b6620adcb39cecbc79d67a7ddfbbd9eff51bcecc8ae5693cc632425037e2e097ed14702da2dd08f154d2c6df01c8de2f496fb4a1050db0df38f4d8a00a9c8e79cb3bda8ba286177d77fdacce0011d48bbb7f9ed536789b5402624f106fde6caab2d466884f4389275af6bb4ed4a5e845b37ccffa5c2ce8930c1169409ae87c54edb44d322c07bf7cd485804f843a6f2aa93074c077762f45572e02501dbc53906d0d056785637d272572d9bdaddc5218f3a42a39a2ddd7f092352e2057628cf83613674832e56c552aae03e85074bb6191100a0c110c627ddc76b2b06ce9258be5b2efc22be6867f2ec28083c210a886c9d3d02874b9dd986029fe90ca7e2ad951dbeb8933785d70ed6ad7f2deaa3da8f0266a196714f7e7ec72cfc02ee14a6c48780d75db575610591b23a5039cc5a88f3116b2c9d619976c399f30092c2a906fc2d60624614123496df4916dabaf7bc7a108c4cdf4ee1fd5acf590589a394546d5d930d6921ad89059e928c84c1d831b9dd84837a0b1c0e218da6094a5f9e784c0d288d3fb08a26dcaf7b4fa01a04cb96019f9c7d67dac56eeadd0471058cfe766cb95a438036679e2389081963f536312115991b956e92631853019a85eef2ea4a792e0bf9940f254dd411ac7856960a8c46a5d4ce16746e443a08ca4e288772180219ac48cb649f284d875a6d26b09876d54b23d3eca0fc04c5080af9741a9408fc857c9dabd101fabf6765cc4dcfff839386f9347ddab7d85b0d7b12b438ec95d130c169cebc81f0460961d124447c06257f7b016d3537799a0f9317ad97415da98570f615923fda90f5a12ad6999d0bb1b08903c366f0ff1405bfbd5dffca22012908ac68f964ea9ce9bbc8af6da6c00a35fd7970c6c70c40003e4e6ebdc99fb03928b98e49bd25a2f5b727809ace603f46c76b6f75f15f2700f418d88eb5a195267a536db9ec99a6855aee5c120cc198c67113b1f9619c1e0af4f63a9575290b71dc0efbdad543dbc6d8334e4c85b47d8c82009bc477243000b0a5a27b25f6ea31a3ff0303e53ce4b46ecf3030ff3e47849478882e8d4c560983ee5001be5c72112ec58a0fc19087ccb075f83244d38fbe7082ec952b2d4a0a19b1cca4d5844638c28e1b6c6c57fa4203fb6677184c750d04242b8e216a1b067bc6ea154d72c86215cfb6eaa611d3d7afbed45308cf1aaea4bf4fb463ac1b0064d4a9ece22700d79c0ef69f28cfadb95110b187b8e8f7337c50d5f51ec6fc0d85e69751c968ebcab44dcd37971e732c889802b1d3e1796e66c23da159f5f8015ff85875b6e43318d59042601e668f5c1fe995dc6ebddcd46e5d65b2efc26501842244f5fe28e998ec051274013966785f76a39dc7be21121c1a27c1a1988f0f6bc3a157319c2a129b1e5eb220e01873b5b3eedc88739a34cb157434a4655b0adfa7fd337dd235e44e062fc75ec7dee81fe39169777fc246cf29bceddd2f5603488472441d6329d7a7a94af8fa7d01804d66fae0b1c9c987928c025ca7b7b203522ff96496fb70086aa61f3f12a9d94fbdc20ff88a3dd524bb0c369804c3120ba47d6d0d22e2c28a65b7c376e51f4bca00e307f4d2a5be7692fdfab601b24a00690eb26cf2a3f305fc565edb277c019340f5cb4ae72062cee928f40e3905f30ed58bf864a824bf8d9bb68b46b1e1286883b3a974e54a8b77f0dd5a4476949403a1b0da02e687e6d1a21d39a0f67cc8c5c85c0f299e8db42a5b247825787f180a6ff2feb3b8ed46743af099d36aed6f9a34fefd9be2c788b765ae6b2e07523b0518047035f9411699c63e8595a4b9320a481207e487332a87d736c91cf4eb2d091eb33911b92fe4678e1d8f13c19ca251bb8f7b20441d47e6c799dcf4096e6109a05327c481e6f79df04c2ccc993ca297b824baa8470e4498cf43b826aabbb302c43fdef413815ff25fadf84e9e3fa3086705b62cb710c74da79895f723a7330324816407de1271b60e731977d1af4d86e3d034a14a3e352f05f88885766cc3068ddb16ae7985db7b4edcbdb70de4cd4eb5f94654908c4039c3e317049be20c0f448022c0e3e9f04ad699e15a8df6da8ee0d9caa317cb489dc5ec276e77a05a089b0a0861d69e58788266f7d1bdcff298ab878ea92172354f08314b2a3fa10b0bc46225b16593630a998e044ec5a9bb1643056e3dee4c6f2f5ccf9daecf6a5e0894d7030025f9fcd40e05c9e73f6a54d730362e1b01cc9f2d5db45c336b18b0014fb89a21a8eac0e6dc0403b9d032022d44af3671489b8b6bfa7fafd3df8e6b081116778f7f3ef02a386730b643bde59acc2db7ac753c7270cc46d141b7ce900ca1b2b6ea29934ec3b0cf15f8f5cdf964f373156581e78eb6848092953b24de03cc082ae4cdc71501b994c65c48b9df73f2c44c7092bd53532b287dbe7faef701adb22b822c36c19af77916590bc395d298d4e6ddb1e7ccd7bb7933bf8ffd7b0f682527db4141308c3af779b5e2558b66ce7d5916ea7af096418fe3d65ce4300a2a1328d5ebaf7647c6ab32399151e1b8adc25e0b12d4ff960a440d11f5b0d005eef67c33ed3f4b4744d7d98f1d1f50d368dab30c16b1f7b112d9c212fb2c6a0c69cb437b2b248f68eff447ba39e4a7ac8d97273f2c507378b168ae88779e6b053455e7b29e1294a4c8a93124532701c84541d428a84de5acc2b2e9d7eae8dc0b87289445352cd29aceb27b5a6f0a9623e127f84b28160642ecc003aff0035b0ecb224e50bf2a10a84e9268b6417b7bfd12c33bd27d7c5a48d6960c01d8eeb40ee0431f73805bc6b3516780c77b8a70cf5b003354c529f39f45c1211647a2d30c8a810c54c0909d6482a5aa2e4a9f0218ae551a20a93980eb11c80d584342b30e0ffb450e790246bb91bf0b7ead7161666c2cc64f79b745c646f514b45c0b1b0f39facb1925899eb80c2a50467f301e7f1b7789b6206d9658222b85400b19990e819c1ca5106625324e25a890e46ae8b5817b0d81a47f775f06160844ae8d0d087779a213a0d6e111a2fc71f860b9f80df3c38e08ac7b71fb3af53172f20d2004cea1ac76af87c6117749bfa054a3f69521b34b31d58ae8456c4ae33e36f31d0d4a5f3429a5c926b4fa2e356fffa44d9ad1a562d6d9be6a1896fff390d5194e0b94c2375aab43f8a147c5825f054932bd9b88decb2a01a65a4618d26a4862440d3e3a8cd082c654ee9e011c3531f2f4ff33b1a9eeeb2025075e3b9fa6ff3d2901a2cce1d29cf64329d601f836731b2574ddea2d89738016949a36539a4969b4032b29b12c8e1029a8875f675ef74ddcbc88773b3fac28b6cd53fee4cb47821c03d4968eb2ac0e2e91b41e38dab10cb030389e28a7f73cafcd567429a8418e2105ef8ac5c2ad71f3f7e2f7682a73c66d087f67409f736d04d4b60f68754e1dc90c2412d86bad431dfcc8a869c5ee1bf96ea7b4d2a35ae1d3e0d3bb6bf76cc8e605241a67f2379d7aefd2bf3abb9a987c565d88847ee6f8c2e64eb15d1e3b2d0205b922d042c20bc376cf61a1386cd9b079d0e504169794682fddfaf877807cfe02eea07662ca5bb79def9b0edbf6b15af6455c3ff18f81896ef039f28c195cb50565e96c47072171879bea8eac4e0766a59d1d19d404366d897bcfc55c06fa100a72bd1b764d04cb5d94ddd553eab51caecb49380fd56c3bf23c40dd8ff37f7e0e852a6f78760a395324b1deaa1ca0c4a52033bce9d37665cbccfb0d628a0e4c0ad05e7c5d37e387d90cea4890d7e9c8f976fa4ddb60b3de212469569db616a607901fd285abb313c11a8959080b3d756450b1448a494642d5bfd29c1d92175506014ce8419cd49ceb69b9e0cc11e78001d4387c16536dd0fe697e4f49f3d7d0009722e254ab780d37fecf8bf58a2b5674a152b4ca68e3fe7e5baf6a959395f60ce3eaa6ecee3bb34c47c84f825433cba087b8c9e2f0de0f62eb2c755d0861f30a05c065a53439be2e794c3d5cc197b5e25b5124d743d9468db8484c65bad12503699c33d0306b5e71df09cc4cf50cb9bfec7f8ada291d7ba77d079a0291e09e034e8da6a27347d48f5b9975ad92a4d84c7939fab99e89f319719e3487ec8fe409629eec569c9928663cd4f4efe35ae8b6876122ea4b1e8fc4869fb16ae58ba2052f4ce344eb030d2f5b958b8a0a230639b6025ff6091db523d74654bf6c22830a77663dc6e3c17992edcf882fec55407772a403a66d74aaf7c4c8cdeaff486f0056cd005772903c3e8873c2d98b7abbcd35b7d154795c2f73dcad880f390eb3035fb9fceb50a8e82dbb11e9e27b7d08d5ccba4b7b825be799c14204c9c3cd990f0182abd0675108e7641219e2e287aee0e453026618b384f7ec0475b6c2761d06539dda33e894dabefd0595654ae0fc1fb42c70077309fcc58a221630bbb86d0dac4048b707e179797fc99600c758edf10ff549089f7d66bc096bccc7859115013be21833ffb1169cc1df865999c6e18428da149ba4cec11e46beb5cd8fbaf40412b4b42362bcd36b56b78b04c882f99ddafc0e41e0675fd463919fb10cacf80ffb45859b8726ec0ee70fb2ab873caf5b333ea273ac9d6bcc59e4f15d63891e08c27fd61a9f583b90d4cd19d8c06b91a78c6bc6eef4f81920d4b001be90a9520eccd8b2018d1c62a38d27388bf91c620739055d174c16f36f0abef8828a58ae021b174de9664d41dfeb6206c394f5a0ac2bad288d4951ef45d09028c07aae8d016444bf52187752e9686e8d07a51a906ffb72f0a653870e29c38148fdd60da50771b65d9c65eb306efd0e6037d1f4356baa6f0af7858e1827d6cc7e0fb7a6970217f6f1ab34254a47a80c9d615c5b1de576350345eadc68cb05eff8fdb7140d014dfd4977d85ddbbcbabcd33844ccfe1465a180c22d844ac250c6a36cd33d5505411ce0f59e32b073e75fb11426cfdca258f37fe8ac4f86b237910ac6b7bd6b0790f2ae95924abb00c3adb71eda0f96828f0a548c9b54f4b521067ec61d643607f235075a90e1d6605c3342c26173f941234cbce6a6bc94f8110b35b5d58b7f008d232250f6a4fd39831a5ef6f11245f3a36cacc3d8f21acd7e113a5363ee91095c4000176df4c981dfb3f976b7d3f90fcbb7f9180578278ed148bc10eed10d0b663b50375760ab577f84172d0c7aba9c8f85c0e805f123fe1bbd8b4b3438640bb3b1de5d94203dba6a929b33c4a7e8694c7394522a48337af5cbe1e0c203cd017ab565f66f3ee9a2892b2da2f4b905a5d715f0cae5b66576a592830a5c8c6301c445e5bc769310893c33340a4ef72aec9ede613f6ee25c58068427c7727ea104d163ac5a8441b156593059b6670ded5bdbf51a1a9a8490fc680eff9bf6630504a09e0497de94419c2862d9a70441634557ab3706f3b3a57377c8bf9327d2ba0a8ff2959855984b7f85a425ca73f83c1504a76c9aabfde872c1de60089a873b009327195aac6b916980434bcde81fdb1a2d635e29bf77d0430d27b2a47ab6250c422fa9fbca49e7f9c3ef8cee46522ea6f1b1dee38e91b7261c28e48576c9ad0302ea8fb85c0389060582e699a416dc503562826c63fd18b96aad2f7150356f01e962db92adae0cfd872f3e14216606ade6df351dbd477751dea063d05438390d9ba2b48519031a0ef8bab9560f27fa18add5aa559e5b7f3b4660c121a990b30f1605ffcd2907796dc5a83a2967474e33ba236f4460b519bfc5eb1ef9dcaa22056460a35cc59469e3d11b5562a3311f8dee918dadb629186b7fa80354ad09920bf1192144a9d3b55c4f617623cd705b8f87df12701c740e59d608620d051cdb0a681240f5c3d0738eda7b6992e6b17d314a46b99467c56e7dc3cbce4421d7a40f6f22eb6f5530c938b11387aaeb7e86295dcff2bf89049611a2df51d26077020ab668a47ba24e2746018dd3e0a95376045c187064a5c09fbe38796fa5beeca80335dc4c3b71385101bbd8366eff8240bf7043b7452a515807d7976d754e1dcf0dc3f86be7e2b5f7767c2f5454cda40b2514ab9d7fd1b2c2ed4ed832021481c8028c3525173166789b5b6e85a9900bec2bb826f6c469ae68033dee628716cf0b05213960bf87f65fa170cf3981bc53d9f0e4e7060190bfcdebf7047f881b1bfa0df4e4868b44120186524cbc65f081ba132c094d990c36c53e78a386b89d89920ae39525506af0312d9a8d53e3288d69fa44f5b7efb80fca91f71354da3fcd0001d1cc5efff003db4208994c93b1c8ba8752611ced0b6be64385dff3e3a1d99603c3cd630aec5182a7628a9bd583d3e0307e130c3c056ce53df7823781b2be6f0e68366929e3db1d15163699cb52edcff18c7754746628ee8b83c94e32fcbb540433a3e1ce3ab88fef6f402090efccc0704f767fbc8c37e697445ac53c3c7c500d01c921f64145178fe1482a7811ca416a917f8c708317df8f6933c586740b5301ef611168662eb451e3c87cd9cdf97ae0ed16c7e8833f617af3fb288eea9b3009df80cc7c9f3e0d87d11ef867474a9f5e2344a777db658d4befb424c855ec0c081461edbcae88127fd292f7b1cf535a0fda7690d37b6945161e8ecc35f638f40bd0832f4aad78ebb0e584cc30bfe8ccc2e75c99c0777443b8959fa21385b75d0958a141b7fb96b9f2685848d287ca4304bb4390b3758b856ae6995d7e67a0340db71239a5993911ef80895ccf5b36338fce1f4fe234b7c722d912ee0e94daa80abfe2ab020437f57271af71cdb218d2fbc924008b5e667a062fd08c6db388430c30863da329717b25acc19f33581fcab0e3bac58c208fde5ab9498dbf22ea1e0d3f5247b9e1549e55bf786de6e1a61ba4f12f19ccfc53e01d79fe76d180b657092297fcbddd5e079cd763b3fc25c9a65fb0a7e03061e14340e60de0751285d705dea83c50aac7de7821758e4b754e5fa8449c7a7630addd177721a8a51634bd0a08e4497e610d8ea8a63d317e9ed4534bd7773e6596ec0ea2b4c2927a17e50f0241f0bbb40456d859a1278340f9e48f7bf9bb7b588a290acb19c9ba56a63ba20ab053242b08f86eb28b911f313913979f06f912ca40681ac2b01066410108ac06390582609857a672acb93cc2c50ba103cce1b4558430c8fe3741909bf5b011010847f0b8083577cedcd5282bdf132d8c3f556b3d3e31e0e2220b7e87742ea2017a5667f2c7754777123e60f783f2c3ff712b88a46462bf36db6265469cd85a004473e6ac6729bf13285a046d8149f2ca793b39e90531f02e56f7989c2671df024c7084bc037d70b28959d8a6a9b8263ba30325d42348a4a066c9b2d56fa60c0e522c097187ba5909b5025d3c578746b97e411ead24a457108e905d2835bc2f0298717ca1e9a337529fa274d92c8b5812806f9da9885ac354200f9f289f33d603643b05e0eeaa0fca529ab7f8234a9ba3c67bfd146f29922422da65310b8fdb0bbafc5901aae1573ea8b37eb05e4c0d09be63d4b62dc13f3077d58dd3ac43cc0a9de82e3c83f8a234c1f2b72621b09c190f828219c364653ced8621e3172c57062f32e42e8134da67e1191e8b995d06cd6dbf8a7e8188edbd7157e8f1043fee06629fe0c7e045a3c28cdc99caf78f6e27bdd8273ba5ad7eda480b1da10a87c9057020e229e972f294612b0e4ece4035f6db503d6b5822b12e82359d82e2bac301d8d04cf2ae0b891e2cd4a98136d211184bfb5bf5ba1b9de0f93ccbe270fc1a073d3be3d8b76c7faa81e6f585bd87535fc65420920d81eadc6b79401f65b7bf017759ff27515ecc89d960ea73be9e55ad57bcd4cdd032e08359a5046eb8d13d0042daed43382c0c4724a6aee3eec70fbaec9f28f3f35d8130e2cc738a04bd0e0bb49ebd470ecb7fbf8fa434f848e8aed33a1fd7cb65fc2415e3ad3bc57bb50a09747bdade1e58fd31e5fdb498b8f110bdfa390fa7651779521d1fdc9c3279ee0a68921330e26177d3e50ec20cc2efdcd3bc0a3fbf42e1f85eb184559d6674280ee849f04313627801561868e3228b1d3e5a020190131c914c9a02f9c60155fe0cfdadc634aa3f052555b2d9b0352d5710cdfea7d838104daa0d1b9467f06b070dcb39dfeff739f04f5224e9d4bcb820fe5c2f1771731bfe4b759140b7956fae0e7978415117745d1d57c5a944119c62b02321f549cb8d1a487078d3bfcb278907491c1808e65df0fa1e365f7d8a571fc6b43c8756093126af5454d4468c66f70dbff9a48615d7a41e4f32b4320b30df1c23d7f03c5632c40167a5ad8f293a150b56f91b38928d56132d78f0a28e1f01829dd27cf6cb3f75f00770c68df9fde10b3872e42581fa87ed4407c088feaf408d9933cd7e1ed859894df3c1de211c1408d151cfefff4295e50c46b4f7f7d3747bd5983a25a616ec095134663747079504bfe13d03791da920f5c23091338a07ef5970e76fd98e76f0f241d4aec484960523ce18f4a28f19c68699aa26d312e1a8407b7282fd5dd7ee4641a391eee7f20b5e693f4ae9cef7219688d70adb14bf582ad3ac36c9cd678e257c52aa27f3ba034c06408ec109986dc525c3fd0569f4c87af71e2e6998fd52a369cb7ca52eb10732dd4a18f1a76bf1a0216db6d3e89b5f29aa661570328708c84ede69aac65b0249544addcc3fb1898d33b9f87272f45a9b797f97880f77c03d16e84195041400c071c2d900cbbfb08a5ffd7183846726884e77a506321dd0c395271943a9dc08f4b5c8cf5872eb64d25c34d3830c4c45f7b1565647b985834be21a00fdd2dd0bb6116d0621d3eef312029dcd6e67941a0d13a3132c2db6f307eaaec88b5d6b0f8b9c1489ad9792006052c0305d03e1ca72d6e0bfd00254a7bdb0b5616c3b5f0bc70b8c47f6abf58511cb7d7474435f839812d0db83bb5152fc7c1bfdca2cc40d7db234280d0d07672e262533d9be4e7cb4fa38d266991f892c723d579404aa05306cbad97e179acb8ded8be33e87c3c1b7f0d13c63515170a5888135280c360b6554a5e57920a494728709709780766406988305edc2d3e70e36a335a2290908f749e424585f34db52bb23f266cfc21ed7fbc3d8c361c5c841ad52c05203e40d987e8c9f277efed398f9c02ba060a8b368794d9441f003b5485709680b5f1506758dece3a5456739e3a169f54f2a297d1f4b7179b996277f306db2fdbbe76e03041956fd992ea5a6282b81bd84b7fa17275bae847cbf6b7b0ce38b3b91548e02abd2b9c4d3ef42762a6fd8df382124e0bf149e3e9d4323d0142e898fba78830ca0344c27f1616ffb7f8353d0dac1cd7d8b172f8daa7128ed7f79770fa476ff06f23b93f506f4908ac698d7bda1351c54f97249be14b744a943de8a03999e630bb059302079a6c3f1a6d8e56e8a48b7f76e05dded7e538be30271c4d5b3bc880fbbca179510f742078390c7ec3a99a1e982775a035c071b653075f4e2a378f0016fa0dd3a0c9bed8ebde17c9d1d117b8a3aa9a4ede663fd4cd91314905df06b0a83ab5aff06ccb5e4569053161cb76e74be5749cc50c3fc26238b6245e580350cb966b1d375f2060409cb0b25620c4564b5736a12443d83c7b7720cc6e2cf600827f9a6fc1b221e59fa1e14c827dd1513b512dcdc743cafc375cdbda5bd788d0fa0b5faf78407eb59b77c34eef638e28e155b21cd6a744c0089792c54e0647a0459540d8c6c880aa75840e85a791de8a0cd107d6aa802584b6ff25025020b490b3a178c1ac3adbbd4d024efcac5e6f7507ac9c4955317492d53a6c1a5bb070f05aaeb989b5dfafbcd0b676b973057e7d683d1c891cbf5be02dcf1177caddebf0225bd57aaefaf5a1723887aedca0d8e55696222a81822995fc4ca91001c7dda0604603fd3275542b104240752c8cc50bd45453f606c0afa49cb627e302088c70a6f403e7ee6837a9b0c3d8ca404ca829b777f7d5eb3efc96481f05ececed3370fad98b4da97e7b8e5381e82ee4f3d07a1682a0405b68d7d8b46ccb2d106c1550ab4f31226e835e7ad9d5d63e17a57a6bb14358fbba10e6fba89fcb419e82112084f217121de144660f41447151067229610227ced84f8829cbcd7059c074cfc0f9f604b5b8ddc02f03a371d55167953557700fba084a7e7a6fa5046b905642e031366d52706175e0b09027adc84d6b799b305ab984e7b86c3def7d873d8798603655ec65a5ca477d64c9e5cdc589091d702a047b28d48f4bf97d748717af65f0eb7bb81fcbb1f6e56f3df0fb12f89f21df6b0127479f84b75f9f360bc76f4de0ccfefcace88864fedf39ca25ab28bdbdeffc63c9d8bc84a846f91ac9cf4ca7f0f5d4b48cdc30af64ca7ce116a5d7db2d02bec3897583d786a9956af7482cca70858604b9f6defc7c609670acdbd2923cf0baa48fc9c4e3512ef0de03597a095054069beb01b72bfba5803c427da8cf7a650fbc45be786c999e9982c35bb1b020f988496b7966daadb50c9182f0a96251b2139bce6d2157d257c113c0bd96893071757a8ff890aff9dc94cc152691b8487705855b61e21ee22a30fa1ac3b43ce08949226151c2b8d82de0073d0187c2580a53f0eb32147385829bcf4442901500378cfc3f49c16c4d7f226a0d3b7fc6098a5c7a5b510babf68795360b4402a1b09647bad1da714a07b8bfc08627543480b4cb96ec61a3de329c17d5f055159550839db697d633308a8aeb50f5099ea78bd484457ec9e2e81224365ac78adb1e605b76436fb16950e4e13e9d9b9308f0cdbddab1e8e68efc62084849cab91ca820eb787605021ce1322da05638501cbe914ed3ced9ba73115e4746f06f055c65102313765aea37ae8349f2b22715484ebb6d05461ea394a79f02398eb668aa1ae0bb7a3dda94881db130b704cd288641321a90681a54034165a43ddfbeb2cfa9f01669eef11b225bef91b379b88968bde6bc8a638a574a3e3300ac8143b13f5f902d5dcf331c168304a7668a783e21cda7ddf3286bdb88ab40d3630b1bf9527e708e05c02ba08fec5b4ae0771d173c97b81773c3ed55e900579d1f4abc01ca7d405cfd9d5b877f8b29a2c3eec59d652f5f2b16ca729fef248812e65a7d45a3a4c0f5b0c6233e9c2287956411be9075be3b05bd6610979eeb54683fd473e44efd509e52e64df862b1a7d5512b26d4fcae2f3ae4dccdca15603ff2c46a71be6a3e806aac3227c5479e067e410931975f5cab25bdd9e52ff3be6a99b94b6263f6ef60a8fd8ea935c62f6b11386ad8da37f288fb137d985c199d4f9a296573da418ca0ecd01b2418499e64c5244cd9c254decc386f8b6ae4e03d0ab17a78cbb2e89c702ce1e1c9b637e12079f655ce79dbbdeb45714ca40275606a80b9402c83ae71809600040018f6a0bf6fbc6788463acf5b25f37711abdcfe77cc2059d7d06af2b082dfd7409e505447cbdaebedaba426b7562485034a2a7472f934a41c2ca902d0c +generate_ring_signature 4b99442b09d30a94e40a661547b075704ff7076e432907efa34f28fbf8f98c3a 8c817e1ac218112b22b9f47fd1d7de82f36e48900926febdb32adcfc4e4503e1 2 3596cb2eedbe31599dd34e5577a038571b60800da674802f977182ddebf3cefb b290773d661bfaa5cc2d1db49b3633963e714e9c1f3cc8d843fb7f9e57e199a8 1452ad276c1cff34d8ed225e848e5f61080b131f2500d6bd06b7e85f85d08106 1 e2b501cd7d0686a838e640da652b738ad74ec10381cc0bbf43130ecbf202be05d0e2a4e1987d0b31cfc1e6736d6825b974545a03d23f58655ffef5304c0cda003f8b8126ae215bfcf0e6c3fd67e14684f4c77085f54d8807debcc7cdc1257909ea2b9e2d16949ce92f881f0cc0b373a28826c9d97d89d3fe7f7817f250943e07 +generate_ring_signature 308bdb322aa460f8c564d2134e23833f4e567150599cc3cb027083c5172a9ea4 6025c2ce5f391b371ceab7d0e0a0718eca1a660603056c887e0117f0c838737e 72 2c78fa1ff09188a0c4c26871aa125e9078025ac1e49d77593ca0dfa1989421a6 4dfd23fb69675f8836f93cda1dca1f2c55b68a16435d8ed3902c45709f11ce2f 9562994d83657b04fc8a26ec25ec00dd6c2650781d81e866a1e4ebf78e0ee80c f5d9c7e8ad13a830598f6f587c48910b3cd4b8d338c90a3194f3cc6b8c0a1a89 515aef296c04dc838810cd2dcfbf8f4f42ebfe4942679929cd3247f333a7734b 0f0f309eacd4b8ac55489aafba5cacd746a1643399130d56ddfc77e826feebaa 464b240b5fa690508441a556a6e7043451d269ba3fd38658a46dcd78d449b781 662cf10b9f6900077f61ef37bf3b21a4b42e0d65ecabfcd5c593ec23da6ef016 48095d922127ad39050e52130b408147dced83e557b81f237e67864e68e2103f 55916c66cb8086f42769ccf831cd4356d31eb6c3411db78d8b8a8e5d7e26849c b72e843b6a252e3d6cb4f98bccc29bc494779c107d385cb52fc9d90fdbbdd43d a9264eb06d803e5d7c9f5054678d649d87dbfc7a2c826a2346e6ac929b55fc48 df7e5cb855d1dd60f6efd88d00d9439c92fde3b71567cb46aef17c76fe53e51b 5c3b976629247f7f10f4c6d6c52846b9b4de1d7a258d84ee0811d0a417939e98 55a366725e671d0136fd0b711fdce650b77559adf56753f07c4130f18a36f024 c205931d4633f04b17c931fb00f2b47b8b7fe2c9de6def25496ebe5f61640bca c3baf879f8a858219e26c7983a5cc8618af19acaf1871fdecdaba355d6ed48d3 5fb47938e220e2eca446595c8396c75c28d0e180c2b3b6c3b4d9c9c1593d3ee5 ba26eded826bab30fba456ea53f5c34c97a1b5a985dccfaf6718d1231db6bf50 d5c01cedcbd2bdde6d37011766d8a864ca0f53943501cc0ab3933a86ac0a530f 434dd5d7e49617695b786c2ed3e851b1b76a5140369e2a390168d21cb99864e2 f87c29400d724f5c0c8e4e48049538035a4eec610a74d2730ed76176efbd84b1 ff55b2cd5d13d4d8da692ca9a0d1af104e1df3414e4b0b1821a56ccf676d3562 0cd6a4a67367d58f553ed01253582a17d8c85d8434b790fe0d79157e8b7a3fd4 27f75211c127aa1f68a3d22dcd83200fc3916cd1c7951e538600a31d19202599 3a23e03dc653bdc805cad9a038cdec71a0bb2bcb6de17e2db4467df3639ca7ee a1b76ca66834623b4a2445c7f369cedb1b2c0e884f938598bdadd245822ae52d 57e14ac6cf4de2ba8f4a4fa39131a40ff02398b2a2217a0747b4645c5eeb5703 0bd34d6cc73e4824f3b96cdc68b939fcbb3f3d64f85db0e2f404189d39e1e3e1 abc73c606cffe36f175a81afd69e3bdd7a6d534aa8da4541aebae0c5d78ff2a6 23eec063528ed18f12792077cda4ddcd7623b8ad7a8d9f194c0b25495d6d774f de751896b52bc0803437ec63efe0f68e6c25b17211373a6c083e9233cecd4c2f 88be6f30459ff0a926a4d7f57b9a71b55250e9387208b19b235f4e69511ce887 d64854d2a0eedab738b9155b41b30d425512d2165fdf68b60963e6586f0a75f8 29a1235869a8d737511ef31916570692eaadc9fc5a836419d91157959bc7408d 0cfe56ba540437788376011d248c5b0addffd76c8f183e1798a81a05995eca77 1d14bbdc17e017020d8c6696a1780ed8a92c1d25192da8ca83a0835ae6ceeecc 603114e5f9220594cd088b81a0cfe4b54fd73e0ef821e478fa1b3ce7dceb32ac a0fc34ac08c99f31ed89e5e310ff55208bc3fa2b53a750383992d460c2a12dde 83ed317fa3bcfcd2d5119593bd6e24db04b15669af5384d08d1bf32ca220a60a edb90d89c6618a866613546e755fb50ae469b95fc7148baddae72f531090e1c9 d29ec006b1a758c0cc9c6d3d9f2493295c574f90d6c6ee53840ad28c629fba2a 1e8eaaca2c4a8ff4f4dc13490237a0a97f061741c0b06dc5ce2a6acff153bf94 68df50cb1ef38e0ae678b4d449ead853d4845db7c1d41fad198cf2aecc594d13 e01e97370bb7b5364dff8611ae6da78f1a38102bd5e03c5e24eebf24f4ec00cb 5553e29cbf778f6b48e8c5e2a68c9bf3d81442e1ed81f25ec700a9e23a662466 23024f3db8eaf17fb8e0e05330a1c9760245afcadf2efcc400df7c7d17a2f767 a827106be8428f79cea92551f543d37393685ea2a286c66cae92f8182cd39605 0b08a56085462887c422032a20128ae61ed97dbf5e74fd8d93cdb1b6e19461e3 3b2c211dbd384da555d95b648a128b56ab6643b216cd0ef0b3efd2383428be80 ef5b4344e2685e2cd651042f3f03dec4e83d3fae8e19f7390756aac1252e8a2c 6874856045a450c60b61ff651b69e34e2c834ccc28f7f483485e43f2b7e619df 397019b0b5f8892c9cd5578270342cc060f29908a62afc7833f2132ddd890492 56cab4e804d55cfb63ce4827aad3984945bf52a980afc5fe65fe9724217bc007 516eeb63ea2cbd880702aef419a1a7d6b300523168bb60c1c2f2176cf1d3f0d9 0e9ae60794a835ce5d1988d7c461eb18f25ec62fdabce76be9ba7937a9f69217 0cb857ba94a45956dbe3c9634d5c0cfd5bf97ad640af92d0b99fb0544d7693bb 2095075d719d82294627cabd25b7dfaf7158c62ad211c771d3644c29a5e8bc7f d45ef2f378c76e52ff85f3586e3fab64411eb387e3a68d54834160e9d4f75195 28e2f30d2847b093194a3effa41f01428cdad1f2d20cfba6eb5139f9e0163a5f 65bbcc93001475bf68096f7cae42be2412e3ab74a60ee2f4926fe5c6aef83f0d a61e3661ee0c48cd01ada4fd2be409d2b461acddb708ec548cc1bb0f7eae6f97 d26de17c60cdb8043f11c087f8cbdf014dd27c7ca80a08fe8aeef96d0a1745d5 1a493350e3b85b91b47d8f1f52effbe30f2927f6f86b73d13a1bba57eeb6c67d abd5c231d76667750f9d4a234631bf3f72d067f1b88a4b0926dca81004b3d0cc 1a3e09ab6047738e738872c6cb46d5157a0978ba84af4e4f4436fcec61ab6e6b 29231906cfdd684e0f4f21ff0b0ab42e9969bf6d6064b38b735f90428296105a b4f5a4ed8d4169c040d9a4c387eed3e2a2282668f95e5d59cda02b9282432bb3 321e8195768e71c1859bc220da29d8b7315671615da6f7a1c70a65c08a1c435d b8b37d590b0147ab67db2bd9cade249369b44bca21e7027d56b61f375e643a7c 4f8253bd0d973bd1079b792ce8e714edfafc5b30625babd7c6c2a9f04a25f074 466cd2e93630e51c245a31c4d6cd4c6918374d45324eb23022b87214a23d4fd4 990b405d17c5c8583ba5b4967781da5456316385111c52d4c1761d44062eff05 19 47b66bb3d477a5b04351efc2172d60e25d7de574c6a9092d9dad8c760bbcb60816898fca364efc2d7b09d4021b2bd01d5b2cddde228aecb8ba95b0f42e31d50618fc5ec26512bf8286d0b6df805b2cecac7d721822138e6cd628e7823b221b079569d5acb33d57c44c3c95b473c81f9e6c508ae6d47ca4549df79ee1a4ae4005493383663459c326666e639f292946a242c6652ed0d804a0bdd9496a6b4f29050202ca0bd475463aa1894cb91ffb176e466df39c0e11d34ce231bd247484cb08559c28ee8fd0bdb4e94db9d995e3b6dee8ebdf263fc9515d99a555cbbf0bc806fa115a233832d759195a91fcb37d2aee13ba510e5c10dde8a4d33296d97b730a0fd97ea4073ae4cf8e0bb8ebda36bb58baf695d888cd3076d7eb5ee42ca28a0448c3663455792061b2b95556d3d6b087f20902aea04a2327704a8aaf07abce0269343896743263ee03f888c2c538de426e5d96e33de2d478f0949e1324fcee0df6ede249618b5a567d430d9b6a2a2f51c0d887b55ef4f9364c69b7f29284d7081ec2290ebda8dfcfb61c9dc988db38035afbf437ab63b4b3abba168a58bfc702cbcff10e2d992f1f3caeaba8376de5b8e0a73b20b2fae09f396a9960a95fa00248a774092660bd99afeb6b0907d372be6973a97646a4f7866eadda42bf5a40097ed147f36a3c8238f2ca734a153e2e52def8dd344dfafebdfd35bc78569cc706134cb21608532df9083c4e2df48c74f6e731558e6a244cd50b221ce298d3d1066f46091248718fcd9c45a6534b37a37cae75ea2a4591323263020d076f6efa00643b4a4463fca29efcddd070ee4816713eec42b57f059b7f4c556ae65991b709a1e7566b7b493d70d40c6b7813bfe2c127bb75165e31ddbd108f4dd9af0a5e06ef4b1fe7a9dabbd69e38b5816090dc63eaee84362a759a171c6ba361d17fc907cb5fc186931c35085b4a3879d54b4046b20e943dcc03dfdf764ed5ddf31db40eb912568d855607502149716b3209638e4827fe6ffe17322217df4b915d9adf03e2b87a48bbba16e3f430dcd02c83b453a9c18d98a00097a8b76ad7eace043c09d3148435b866df326835049461c52a0a027b7c1986a425608f98325e9b3f27067f96b197442a48120993cd96f8df852b343b1961a72397728e4bd398d623c30b5e9cd054d1bd6bb93f9208529e7ecd4474d250b811132a3f0f0d67fd40fc07013feea269de72c83f7bac8c7578807457e199c56b6352d3cbe1ace5ba28e35704b94137bee9c728c07265bdcb8db835f35ddf935b6d6bc03a1535f6df38a2f60fb5a703e07f4c013c9c2cda116effd81e06f2d39bb698653c716d1108c6e47c0c1eda383a446e6811a28c07af7b42b8f5a43f91874ea2f5358f0c12f245bbbb003eb9d288b53632b31114a332b26abd81593258fdc4eb6c31f51e36eb8cf98c00850e45e356eb37afa936140ad2f4a7ba0bed11433c8d798ef9d4b06b3aa038010eafcaeef2b942d5a5ef611d6b8e8739105035f4c3ef069b3692209e66c06e00fa07bb8b0e2b5578a396202d2e3117add3c9aeca2c95c742a1f161c3abac3f07a5d6cf09323a2ab782873af2efe5276a25c08d401c5ce1d9a2c7590f1b848f0bef3c40837da48d04ba3005efb74f7a6de2c689a210d46124f32691829443a3066c10190929c43912e0f042ecebb33f0a2b384a07d8757f62371845e5eac8d6090fe785f4bbd390a293de6a2ac8b57a14de60b014afa8f17de6e20059879aae0a956fbac23f2deac2c0cda6614d52837f71210f5b0448415071f69cbd751dec00a002f17a6c4adb748657dfb832d43a0c412376cb44bb363bf0cd0ef205c66d05c722d2ea1780617b156e531ee0fca7845a6a8b66ed0af7ba056a6d230e5f23012d3df0a011fb91f74f1d14487eaf3e77b0c2eceb7533c6ebe2f2a3dc6d655e0118b11fb316f432a72b5c3a28faaea2a14bfbcd3e5e391c3b625b7ecbda2cf802744996d081263741d9821dc1533ddf2ebe767dd8bf697ff023ae7f63c5b3630ea71e254a5eac99d0c172324b8f06824890222833c4a3a6d3f7127aed56480007702d4644b67c18d92906dbc980efcf693ec31dfea9ce1f9061ed3808578d7b07889f27633c4bebf62154fef766bb523effd4aeabad0d950b86095e91ebbcce0b99fbbd5ffae06c4a2afaa187cb9bf488b95a7bd32d5f1f9d08e04eb80852510df4c6da68c332a727f927a31fefc1b1f0c63344218c2a4f416cfa2fd7acf90d0c3a0289e0631060516e08910abb896325cb39e2a9293339b7fd450da494f14c0cc6282ddea0cc43ff2955f1437cce7c72617d85a3853f88b9814daa33039c940e2dc9de37dd680039e18754161b906f0cda88bd8a4f4753a802a90663cf10e80ad1229306fac3408bfc346a6f53a9a32487119980e8db237b5f9cf206d32016054c6278eb767f894f546ffd94c6d391c85c8d9e00dbec79ada19fc151a2e6610a39f60c64b7a73e24595856caaa1d0d5a2dc693038289a4369ee0b8bd6514910598e10cf6f68e4bd551466d0aa4640981b05739e4ec2d52bd545e3082775bdd0d2eadcc3db115a0fe7a2de0c583a4c769a3631f55d0ca147a17d3c4ef2d866c0f31bab53e282a113a55b7548db5ec963e4dc7ce58fe0ed0aae72902d8ca358c0f62b1202e22c1155ba86a7683a2ebedaebf5547cc574265c4452a3bf83664f5022c61662fa38267b1a36df9fac97cac0b8d39737aa86c9ec3703d4469851794066784f7243d37261b24952f5bb5a8ff0add82b0e017e247db0073e2482e00ef0e0b1127618077fd0bbfb20ef0eea0699ee9f9c9b84bb100120b76fe27fd89a6088e9ee3cef1a8efca20c6928c0cb498d387515c65c1b77de63a93381a1f642b07902d2533502d99b112422ae5147bc3cb014b77077a25f56586e215756dcf540e726f0c16f688faa65092d3c319767ac5c15f932d6af960e4a288de367731210a42fdd27b6e17dd7989b7fc208c597a8464eaabca174de591024a83d6313f250e9f30cab3a53e963e32cb7feb40dc1fbd5cfac3cbc03e6ffe816025798c63ca0d388747a2308c5a4506e27da110a1994df51f9dff4c3078ec266df02358e96506e32587a317ae37dafc381b18dee2440aa58ebed74ed9982cb003fb679cd7f60a60b87d161a706128cc8c3bd934749659170a9d682a500de195abda568e96350da68c2a3af5b120807775a44837406f4f2548a8f05897f196ad156611ce2d2a0ea7fd03b5e19df312eb8688c7d86730bc9cbcdb307936633eb4b2d000047e660e19733502e2e9de1b4474ab78de43caa7069b5e8a87b9fe942c14fb0d495d55045799df8f285a28770e44e27a20e7ca80c06c764136c4d6f7e21c7fb11303fc0f508703113e8e3bda762379065e243807d8f04a1ee82a5bfdd10ee3cbe7ae3f0c0377f69c949717afd959f7a7dd38f964d9b18759645f2efcdcb32a22c808ba0d13c679743b13d056b0b405cdad60b222d2cbf6f8c926e5055c1af6110b0e860a90a83ff0a852923ce25e3d41511c5c0c36aeffb7b26cd05f24804e82efc4f605434c53a239d12fb53df1a997d68d8cfc7604f86aaed1f6d7a6031db005a552094f390c4cf98475007b2c319681fa9f1b0564cdbd3be881a596167cdc8d76f70a11680bd2a47ea2daeb85d4dac3ebaafe5c190ca369501cbe21623e207e3ede0a6f0adc0d89d967c6a7fbffc4f257649d8d1d6e1b6623a18549147207040fe30bbc6c05f5a8c8df8d59e9de86a2228562378243d79aba40d79d7ca1e50f78670662f447e6561d2ff8493b8e66b9b24c4685ed5669b721c744534dd04e3916a208a02ef829dbf6a1ff4b4c538f39984b34828c0f1b38445e7faa071c16fe8ea50256904880545091d19695c58a9302de787eed93dd68689276222745e0d6b66f0855f58f712298de9c45593df3356842d9a5a15e9da10a2c6d59d759d64e1c0a01c401a226dd32222bfec5e6804f2f2bfda6e5cfdfe1341c27729ed480fac36505d0aa2725dae3346b63f56fea6b929faf34bb144bcdf66daa426cb3ebe2a8d905372f364be7424754755d9d2e4d384c8af4f046a082a3eeafa0ba68bb9aa6d705827f7eff085c961805eac36882cf54fa46f65138115fd99fd0424caefbc599078cd6ff873dac15902655cd0890953ac2d0147b6c65a2663a589df32123a8b4091c1baa35d69d6f99cf70512e62b1b27c872122d0d5444342dd57b63ae0992d0621095bb7dcc56899ebf6d47d536b8341eb28db0806cb5c4b480e97c56a6195033de18ec030acb08b6453b562e8ddad0bbcab5b4bf5e3a2a91f367dfbab0951031d219a2423dafea825e61e118259f5bcc1f46e51133b217b102ec8b3ce15920232fddc9717026b8d78c18c6f565bce766356f91a1dc7f89420f379b8abba3203c205b3a610dbf6cfbc4e379bc2269f4d7389cd8c6cf2e0bf1c8fecb4f1551a0b56c6b87307a788205800d9c371bfed642968eac96d323c530e49f92a7a43bb0ad23bed8e642670ea2ac1b70e74fac28f5f4e8a9c8356a259b1992a44f7e6860ca2022c7731c6d34640857ccb54ec53c85e051c67d49e87fc5ba6a35bb268e10f9e917958c72c091a170f222bd8eace45113b78e12edee412883f6d59c8b021033e9f00e6d569311d549ae95edbe49768651f16c36791faa6dcf346f223a31b0496d6d39a82b8495960aa56958f423cecb69a3dc5229ef2da50d002a57567bd0e411ef01549190a9dea6bc2849e143784a8469e928dda2db82a5dcce15bf02a0800a6c55186a68f6e94329dd05b8a4621f1a8ef0f87ac79a4d8f3e3462833ac05eaba87aba0ae563789d17823ca6d3395e42fd8d704110f8f0014b0119645500f0956463f5f19ff420bfaf94574297304a86e943958567f6f2398911e4ea3530fa0de3bad6c83de2c3a9838e3852ec17941c1a907905ffe3dc9ae6cf05116d409211d397bda1831ae225402f07e8ac3ac910493f7fc5f4c13adc64e17ba6f5b0cceaacb9344dc593c57bb5ed14123e75c8930a2470c240f079d6c002eb973a60ab868bd8ba803f66c889b64a2935f3044500a5aa39d65a6521289d3327a10bd01235316b8f29897b372f7911fc4ea88edc38ec2aa3678a3890f2bb38da4e253016af8b5c9b1764d4c7d40dec931326696b353c706ffabccc8051795e332efdb0f85d50fff295938fbc91fde7a2c2f8d8e13ed9bb05eddfc83aa8ef48c17366609150686186cf718416602f3ab5714a3217be8c101488689bbd42e57825905fd05d4f02e53a21b7aea582c2349e750370406e97b3ce971d561691876c0c992950db16cd05c786618cffaa8e9f3a63b9ced6782cc69f8425274a5c236513eaf830fd370b4460e5b0ccdd066e50ddce13779391d210cf4bf524152e92569d8ce830c1f0f29a1c5c9ea885301fd0abe33ac10858cd6849181f9982fe2c8f169da90047d61c3cef15c70784541d5275ac99402dea98a8471955f974d09761f55e4bc0150bc778c4889bd89842d8b563313d597a1c9f8f89ce41205be2257a45217f7079007efe31adeec3e1cdc479780cb46a8a61cbf88de3394edf91c0c368f424904f8e7748274b801e514ef3a9df2bd74b60c2d4553ede7acc29a6e18734e182906722e39106b6b406c5928286a94340b70661d1055d0ad9205a2e71839981f330ea0d62f0f80c155b9e20606e180119bcc506c98cd5e06867b3a8acdbf8c3ea0091cb0aa362ca0f716d473c18239a38bba97624b04d537b95710b260b12dcbb1010030b4537d1f7e4996b14ec7991fb0d9e5a9263863c2c1d72fcc4e38777ebf0f86bec748b36497b57459eb23bbeca2fbcea0ab111ceb0994462c7e74e54fd106eb725875c68ac9647d543d6c9421bfd12ff9d5538bfc26f4fd234bedbb1a0701916b9385013a0dbb26087b32a74df8ba2fcbb706844d1c308675f742bdee1c040affdf6bf4b2ca84d0ffc8cc8753f4f3142557161a02c43d38124c04a9c2c50325cc9bf144cd479c764b44781e441702cefea42c6977aca1410a003ba9685e008240546f3fc9b3956ae459856803a27193179e8e108bcabccd2c38bc25a66d00f0ac9f5191b7b215d695f3b0c43f8af577b26e4f9c1426a43051d22e5a4cbe0912b27395b6d9785c822060d710b2e2cb87b41ebd70cf1bcb8ac66928452f030dab293cbf2c1ac966d311a79711a9f1c9df8e6848dcf5c60424cf4f7aad0265002555686be44257961baeb71087560aee3d842b84970bfd5125010d5891f85a0065471bedc4db39ba93ea8b729e62a72c02fa78d4fcb23c106ac4431e6e9adf08122717cdbd02c1be09b34a45f0314756109e0849ea60102e39599376e7f57c0b5bbf2c0e71fccc029139ab6bea7ceceb47935c3ef3f844c48a204f476d99ee0b19589c40beab8fa1bc474b5a85fb8177e42665ffcc21cf614f320f2212fc370451e9cc7cbfed55774cc19ba8e4eeba40b4fcdfff8027721e25ff1ab11ea6fc06 +generate_ring_signature 19b59e689992b33510cb3a4641a0487214eb16b6c4ec84f17a725a64d241eb02 35404cf6de872e69cc675a0c374d23085e607a63b198b243575c3aaccba5110f 55 96bfedb655e4ad5ae088e1170a9f8b58daafeddb84a3d8922c7a695e82e66963 b57b0dd3ab098d2e5a1cb9df3b9ecdc5e05a5a79a40c818eaddeb80ef3502be5 679c0ec82de8ea3d0fbbd4f41f61cfe30d486e8d2807a7b2da091c4e79cd7efe 1859dc973eb9798fbf6c7cd07db81acce29d707577dd72eee64a3d5373103ff6 4749c0e2a2595df0cc7608c1893942e2bf72f1ceb3e4609d0a12737f3268378f 34980ffb911d8f163de0a1f2da648b989d2b38f41e9525b4fe2e1f5647a15263 8bb5ae4cda78148b7b0333f2ea45fb88eaf413880cce4254f6e84dcea809ce23 e02027de37ca5fa4a7490a35a52f26d3e1bc1a462f8357c3ed9db7f7ec1bf6e2 dc30251df7d13fb90afb8a6360bab069a15a7f18b2db02a80da3a3906b96b479 e72e6290ad812eb649b4a722ffa13919d2e4fd49ff4a44d5e1f8d80976553fb3 70caa5ef3d0766d48c6d9a6728d0d539290b4a68452876c9b8554590429da46c f57560f08f8f42d053aad27a4387fb09c0c914d921b518f1d158dfdf16a4e17e 4655b554d751125b469451635239474a94238a5e59aa3ed0766083e11ebfb9a7 259012fdbf5f5453f7aa98d6b021c4da9214f34f297b11e32f1effed4bb13909 81b66e8895b7411a2c7b8390c0b20673b1ae12dffcbda94d5561646a6d461dee c62b504e655ebf52bf1d72cf50cfe4b4e25a67cf6e968c939e8baa09fe05ada9 139ed705b0a5f440be69de7d7d3ad3a2996d36f8f809f02bfd42398b02e91dc5 392f1fe7ff4620ae2a3757acdae3b4ab011418e843153628c3193db731eca520 33c7d48405a1112a28fe51de2e6a9a9fc35669d246f23b66b88fd65480c23a80 2ecd2cff507a023c57776b03ceb8e75536c3ba9d734bb08672b7aca0a81dff95 4608c674e9421be71e557739fc5b2b868c8e46717d893c499a575d6d1f6af1a4 5e89a1023aca7015745d6e134222ae6d36574605626c816396755f0be2d68bd8 004f0204e19cfaad23af654dc4ac284fa89229c2ef95b9bbd5aec1e0e106fe02 20087f0163ca1244c5d52b7d9c343f4859f0fb8151f0ca44fd58e9fc28622cd3 d061d7f431bfcafe091420eb0bf49476ee690e680f63d2cd8e57ee04ec315d43 17107bccfcef930b408cbaefb05256f430fea8dc5fd4250ac9dd5f4ed3fc2547 ca339d35f0fe605eb0587ec378bf0f3bdb44e11438f999c1bb1c3c5637b7e2f5 2f89eddb111d0b6d8ef5a6a5c265c2ffc491d0e9d09d0017f494df2dbc360f0f 88719f6e415b781c843652d420fd554d291f2c0e8cbebd6d7e0fad80c04c809a 6e0b292ee1b4bf0767a7163b61c7d9cb7d4cd45276a78913b07b73f1c3c1d64a 4eabea46bd0266491d0228719fc8073a404fd25a4e837accd5251a61cc87c02b 2017496e7854f8fc95f17df21bfae2c0afeb43aa28c245ffa1b5047dc149e89d 24589b406b4a7698016ffb8aeaf5185993b8502e46d46e3bf37bb078623496c9 e8e32089aa49dfe331db6fc798abbd5c3f2daf7e1c98263cc7869e66dc7adacb 05b2f33d72ae54e95169937adc6420774768db8a9e3d7236edea05e4455e18b0 6a4b66dae9632c12f4daab911b0d8b9945fb75c25360549c23291cd63744c3ac 8f423495dbf74ee63563293a66074bf15713e1d3f3a16ee4d5f33d414bff6fa8 3f53e929272cc0ec8e023cc81acdfd50852273ee9274732acc069ff56e8644f0 f0a84ced9df533d2a34935af3d0bd07d544b2e84ada7c3143df14a529302f09c 39100e3dee3d1985e04694de6a49a082df90f3f14e07837dfd96298ee8019252 895b06215ade6268bddf5c5accbada81af003ff3a8326701c7c4b2e4af1f3f66 a99ddc831aed70624f0e17f8e1ccfcf5add0d30ff448ce7bdc08d6464fce9e1a 60ce654405b62dfea39278dfa49bf1cd1cb640f60d3bf18fe3dab964832bb7b3 0f755692573303969da6af7969a990f4fb4d0d5a22c1c446338c7aa394b8ea55 10da39906513a827a213081f345f6253bf93cbe5717b8add690d76499580ddf7 b363a3561c0abb151066a5a39b623c9c1b7745333dc16e3ff858b227588b525c 6436c0ca822aa4068aa89054638665f1936cd2a00ef7c68c18a200d5731748f6 310c66d3a506da4d1cab6649b14f875c07df63139e6c4424791fdd7ededca9eb 5137254f8aacf7b9b331927b27c932f68a248df87954633c08bcd84cf7bdffb7 1616d9caa4875622e3802ec550aea5d449b648ebf710a77d3c22eeecbbee6f76 56441b786f910a220f80a7312b1f58175bea544c2831d29af67671af474cc5f3 dbb337b8152363bdca9c7c7b15992fbf2797f113c6e035310becf222ab12b970 36fd79a1708fe543dd785cb28caa54f1d2224b6959821294d8bad2ba982ad4ab 745e169e91122070e4320302d73305504072395f02a6eff85884be79abce0c07 870a32a647e2db50d03544860d834c1c4666eba0e351ff13c0198bbfcfc89c46 f708b34270bb6c3817c74e5227e43d13c13ce3dce8162b8ac558da1c2e81be01 54 96aeab993b2ba87dffa35c2620687f1081c79fc15b8964fe58636102c6225c04538ba9df4b089bde96357f1b7529167f606f1d22a191fdb698b6688cffe0aa0761174141368451014097f5488c7e03181d0853f54d02e24e93267f54ca9bcd0d2298af2f543c5ef6e9850549e739f5e0ab0ad11392db95443f0dda8d3a94850df8f0198d55ee345c55a2db1c85320390e2154ecc7df786efd7f483b96b3a170de973d0ad93a6cce8c6642261b08a1f99da721d8d0b1aeeaaec4f26ce2a54db00cca418c80877ff5c5d391b38bc71f70755d197322c10c72536cb69c5d1d21b00e3df4614b4443d28db24d6e8a03e95cdf2467cd6741f017d76ce34f46585c30fc0ba9a74fcc1f3a4330a8945e848465f74a52beec2a4445773f4e651bd1f140ad655ffe8ed4115ee277e974881aa42076731b4198c9c32a5673b86d4a3574103ea525dc4e406d75869d30a3803c5d2b0c86c5be4236ff534a84e5aad8162240d35b62e36979566e75d6793b21aa4602a8e8b2173624e62989f409a914b97b50ac872b72eebbe0aacd4d044fdfc0961c9d79644cd13d6b87ba4b8cb3452e9a90d0f779527631294f7a6f630f9c6065090f59eef2f5d6f65ed1545c78c7107e601f8538cff77718952c0b4d14d222604c649b238fa9dc3201487fabc4dc768130d22ba9103b06c5584c3bfde2ebc8b9c71f339e99c33ca0f45d4b0ee69a3797603d9a8009c7e4d29c7ea7ff3b21d46e8d64cb40c59dabd2e72e48d282f5b6acd08f307760cad0f368e9ee84404acc11ef5384ecabf20dc1b244f99754a63b2fc06b18fe28e7c26b2cba2ca8adf264598c2ee67857ffde4d1ed34080bbb36a54801bd3cc4c04016950a8768a4649e05c941b17cc790b9615d04bd69a8a05540420a16561d7010e9f1baf7cb230b30c07bfc93d7fcc7a6c0be20cf62c4ff3ac12d01537a9ba21209f62ef0d5f26373f3ee755a4a7d7ed51f4cbbc0433c0950ed930bcf65fc192a962cf9c5dfcf8d62969adc89045750a6a1010f0ec41f9027ffc40a7019a9ec3cacc52bfcdc3f0ffa16c27074295a6464c4bd2c49c075e15b0052099499a34575d4b7445b023d7802f010d081662ad83ca601599f15ed6c8140fd02ca088b91b9aa0135dd250a7e412b7a59cee80d9c1c9adb96c8c6177d17a64407d718317c0c411199df2fb6716d8e870c582e299072021ed25769a60e3774f40a4f240fe027b405d6958241118d69224e408c29c6c538b4268125ee56e818f70f20dfec82fdc6879fa3e699ed77537ddefe769101dc6e9228cac80c7607bcf50ed1aa7c216f2adddadc28def5243441deed5f5a18bd64fad42d52d000d9a5cd07c462be3389c4f8ae83c0f6cc1f614437547811a29abcc49f9ce5a6132b781e04d2c2fd70e4d109d76e235bd755759ec32d2b858bbad926f7d389dd64ed971b0d478f0da009f31c34be549e50c12304ee6a7e9cdd49cb80aa0a191fc2c76dc501fcfec231b79083ae656e0eed52934f0e16f078e32b222e745df0c29cf5dfd205146c72d81ef244508d263053f8e9d5d78d9fabc095c970ec8901764f4b44590ce37a3d763ac1dbad2adc2b03f7cf2e71366648e9f33a83bc83145fb0ce145c0d5deff855c81d8d6db6fafa8d8d0dfd41a4cc90eccc66c0a78be5f2f7ced8fe0f1bc84892f4a08a764e2b240fcadec69dcded216d15ac6e006436d7474b0b1a007ff1f544c52886bf6592c3457753a60c7767a983876579ab60e02460a6c217062efe9cfa3cb22e648c370c7182b779241316033b7421bb25377af4e81465d80a7b4a5d151428bd4a6c4284df63d3f5faad79c310e26f31ddc8ac02c998eb7a086dd6e13ccda11d5d90961ac81859be8143ffd28224ce332460d134f1d702cf0c4d64014eecdf7a881522ec826decc97ac32eddb4edc8ce0f57b3e0c1d6a14d0f76eac2f17711261f9a820b200cadd522b11e4bc19a31e443a884a4edece40e0c658c9be300378b1108fa6ca8c178e65eae8c4e8881486d802705cb10f81bac01d60e58499a2e62435c653a41a1892addc2638756977196050ca196c64fe7980d36408819b9c71fbc83b1e3b753b155e2c4376738567da7f31fb75f26fdccb503edc4bd88f3701b6cf4e0503bffc578c5d112c6151bcc51da22e186380470900b95685260a428d08978cadf34b8c0c6499bae140b5a9bc5ab29dfd59ded9eae0cd3f1e02ae5a54143af1e23e12d8786febc69ae01730b1771947b0f4f280c5b0025363a6f88bbe38a2a13dab3ac92ff1b95002a81fe1c60a4272fd4ab22bbcb033dfd63cda389f41f75d968502efe105fd20d80676061835331ac556ab47bd6071188ac78b4cbd545c11f4d6a9ddb4df28382677e5da30301bca9fb95ee68a70b4059bdfd8a0e2739c22032b92d6912f1617387027d3b677df2ad8ae660ae0c0dfacbe6bfdef0ee9fc15b73f3b462652b9a28a46c9ebd1df5c5e8d386a43b59003c808fa828c76f556d5fca6a30e06f518194f87edeeb7cd6a8b91d437121da04a7147e686741cfe5dc9f49711d3227b12395bafce9def7b98319888ca1b85d04ccf772ac7af47434d132f15d201b95508a57cf531a69643d03edcc115444c3031ec3d5a5482d1b2943e433a5d3213287ddc363f64203c920049e6b98c433bd0b0f818535d7f72d565c5e474f4450087f490db02366339963859d937f85782502bc24604022f7a3380e707644219bf7216d42f85f9d10db65d44ad53d37bc430a336f5546c949e67aaf8c55d74d165f7bb3096123702872f96dd5935d8e147709f3b9934a9f54a2562d151eb58f603f0cc86afbc69358175a38ea8ca3a38f8706317bf73b80204c0e010eeaac2f076259ef551b1bbe8a6dce11aff53072aa2e08a25348b750163413ee37eb5653b53b255c8ab6f9c024d8b07dbcbf31e835740fd5ee986edc0638378198aab5eb3096571ffd9bb5c38422fad73eb96767ef980a47f0b27e1caab297ba2df2e4901f6e509f8cc583c74394b6e15fde83816e2e013baa1cca3e88572af1e1a2d627bc0046ba83614ed490bd3133ec82458445fa0d366928a1fe6246745361f2090e329ac938e171108ed3816e9eeb3d2a247fa80e539911235a92d83c2cf52409dde29bf433e6471439470db6a1dbe2513ad9db0b4d185865a3db9b23b42ac3b85dbc26b70260f6eb677571fcc7cc0e7def9c3107d223a558aad17d12b6a38b8b2d2fafa1751606a0524b0e5e4b78324298c2d100316c352d472a7d692a0bb3c47aa02ea6a1981d150adb3926cb4fddce5ae030019ca77e496fe4ae604295d158a2ef4d1acbf51c991e1fefa33502c0434aac6d0a4610f3791e03249cdcc28f52f23473dfe7bf651c53de381a64ba8c17592de408794478970be2bb5fa5e77f2aa3e2080e6bec490e5d2ba96d4e24c207adc4b30cbc7d9c6956c132dcf6123fffff763b9c7487ed53732b028b5b771650ba857e0f1a739d7343ab7ef4fb55a530450fd11d04eaa9ebd533815fa4a8e8210ad4800641337172b7f34c356ce0c7ef0fea248d2f5b92ecbad795faf3f6e30e4eba270cf0bf21e571523fa346935f0966ac8c1cf8b870ae7dffd6f8cbde8cf4fd126a07c91c38d7de3aea2f5246b6c6e2d45baeb3dfbaf6caaab1271c57b5c0097eeb0e6523d34601abf5bad92957419365dfa53dd33c1694f91a255ee5a6451fd3470b3498d34eacc0132518f74ade75deadb89c2c0e92004ce7349bdf0ef4077ea90a70630cdfae50efb715a95739c01735c1ef461fa66f41f337450db30abcd75105d3cc27692f04d44d9b02b387e55e61ed7a936221c0ca532310dbf9dac51b2f006cf2249aa834c5e2d59b264e077dadb62367851b8e9cf8f67e1905f01ad98501f03220a4fbc719d29e963b2f4bb0c08441c68b30f6aba6eb8a946bbfedbf3c05471adb66caac242c0bc6763e09cc61458d50af2a71edf7a86d055f72f5b870029aad9f7320ff3124119ebfd9cc8f5c1cb3d9d7a77b8903dc03b4e68c5dd9e20ffef22824cac4e54d3cd025d472686bb3314373266f54ce21ba4ad4ffcf3fc50c86434358faf8671fe1039260497fb9d2b83b5519d6bcd235b9e44305c472f1028b6fcfc369349d2bef66664e6f66d5ddb5786cd1f436c5d2307300815481c603c63a95c24ea4ac6909ee1276459466758e52a74a0d6dbe374047e2ef0ec88c025e18e97152dfa259dc82b43525584a37f0f9984d31c287c9c4194ef1adbadb07f8e9e5525f40dc1fc2eb66c982b851c5c18f1673d9f50b4fa6b3178802ca62030aeb6a71a17dfa87d2ca5d0fd3994a404ad861d0e7f7c275c4d24fb555da4901820b849c9ba5eb3ea9d3cc7cbe945e22739bff621b23fbeec57cccc1486760043d92b3c66b3f97988de4477128b596af6115533d319bde580786202a0d5a2b0151cb97530bc5a5482818f88938825065e19d5c17a3d644f8f97d035c6618410b7a4ff378477a10c53a9a7b1268a5f8387a038bd4a7e9f51fc14daaf7ef8c830cec7e033717b9f8fe567754813ab4d253db95dd3b756d63491c5d6ecb4d203a0b24e629b047f0ecd9085457ddafba19230f8957005c2c35c8aec41145cd812b01887e37ff98d3191246d9918ffd8b6e1912e285d5650d0b2a38a3bda948502a033ea9a70098548621c098d7e055bc4199066bb0ff476358612388c9406d964200a9c1879892bb6c4196d9c8f51c66bdb0f465cb4c4dfbe3db874694104fe0c0089c568dde8721f75516688093312523400955ebc2d3f51d1485035555b131890f831edcf7fceaa7c628ce50bec8d3d83b9109b64398b633f6bdf1a9f20b552003b9c0adcd5860bab47e8961d5c4ae05d02819c8a761bdbe761e57bfadfe61a509d4fadecb7bd14580284ae6f4e99cc8ef3cf9012483111b2203f3ea9b4f7f080d0f085cfff344e0b28b4d842ddf55fd8cf560d2f83e7892a4d111facbcd50db06 +generate_ring_signature 4672f905fb42dc0475983eb5a48f658a06268290809009c1c1b1d2c76fa8b2e7 1e95cfc5d2d71497ba502d461fcb7ea9f9768e229b9c268e3f8341a4f254990c 1 3d62ad8a5671f7bb33e660429e1a8d65f454ca409bded90f89d659e10284641f 303c8f50c931bd0703582650568fd4d3382117c521de689e1630abaae8a4440e 0 b63eeaac17ff2b3250f2551988df8db96ac6344c28047fc752de0a86d6953d0dfe32fa395601e090350d595c98a61693417075355e72fa94aaaf95a068802108 +generate_ring_signature 3c562fe4a9bf99c509f2e8d4032d42128a7e92b168392d9538b06fe8f57ffe7b bbf7fab81426cd9d7f448815014c590fdfdf9438021002f067664326bf4c972d 21 f766397c6872286d33fd553796dbfaa31e7b3d1fca071ec0a4460dbf140a9f1a a6a61c5ffe6b77455c458b3cb3e48ec99c70963f3d0b7476f2f3fd49d798bdbe 23ef9c123a46aba32944da02867fe6dbb83456fd81e57ac37c04fb398d748e2e fb3870eee10bc2a23e5cf82e0023c95d7de15ac76adc77a78e4a4c15a743b52d b433b1ea0f29c61a863315d234cfe7abf86106226da82d4de2a59efee82d4a97 90c7cc26068024a6a5ea19303358a9768440254c5fd395e60770dd114003b12f ebdfdeffcad6e468659e5e55c0702cebf169e0b6505bb802101acfa7936b5971 b558862930e7fded4b64474777d7e762f252d7e5ef32cc0862f259ffa0cc074a d10554549af45f28017be503d7f9d838d8c20afa543b0da9cd28f45089401038 939c678793f7619c5985f478b78f90ffc9b3de93a2aaa1674ea9a3b3f5b4f369 e639f8e4a4196ea5094fe047663e959e329ed2a9c5b205d4192213c5f3c45201 1b4d75055248504ac3c2821e2cab34035c4c28941960bbd177208e10499f0bc6 1b6e7a5ea11b4e4a042e2e197f06dbeba08163e681566fd83737e209ecb69678 209c34fe577f2dd7cc74706175211a65b87489553fb104c9da0ff1cf5e5c402d b1ce728ee6de1e258471179dd350967dcba276f7faf60a9659756e1d545811fe 8b8d736c76157dcc516cef83ac9318921e9d6adfc0e4a40cf686fe71504d79a5 96331d76ac46063e2d60b4bd809c91e77b893c8d9e2c53d27a1c378c20ab40be be0d16a05eaa11ec4ef8283d5f1b3ad1cae73883c059d855c7d0355c1249dd23 ae7000fefca7f670daeed94a5f3ad56697fef12313821842c0981271f2142d77 8c5fcddd3f4b3855f1d17487883f33fc05eaec1c7949aa9effcf1e17eccf8895 8900e46d54a0a2ead552e74ba2753b5c8d2b15e381daaeb48569029433d6e8f1 edbafd6cb274f37b1783450c83485489e72afb99e07afebd2853f9997418980d 19 998c597fadafa011815319d24942132c3bdcc700d4ab3d6af8d249a567f6bd0062e5a8af71825f21b31cc2e343c8fbd927f212b9769cbdd202fe8de4e6eeb705c8fded12288c91aedc16249ff6737a32cc97b3190659649601f12e6044d56f02a01687e4a9df3afbbc3ea3f6c154a4d847a14aad1c856f8d119b38ae408c0a0028a4103a17afd564556db21fe908591b7dd6af25760089fc249da1e4e2cefd00989137f09498c2ee3a5b56638b7796511367a6f114f3893b843bcff886e4ad030975073c6a76e3e38f0753ed2960b5f7f85ece867f584df5d164cd86150b8805e3667802880c08f55681c311fcad9fd2fea62d2193590354e0aa451312fe4708acfad1a9a8e492cf3a03418f7613220f662fd56d4889936eaf6e0e2e3c401f0d88812640f02576d42f00235c72bf7a5fa6fc051e58b75d5bd07e98ed501d210598567534918f09ee5a554df4958b7a0979054905c0b378f61709977f5b2bbc089b503abf4e563414238704dc7027c02e116c6880f42513268ea30673efbadb07021964f0f109e9d3d50b4c9ec2033e0b3bfec3bb6e7d4c5bd943105087755507556460633cc8f4b4876f2d1b7a37ce5da9787e09b842f2f2f461846840439a0fbd7491c068b496fd622d9bb3e4d64161d944c60322f3db0ddecb6b459707260000155b55012d89eb61c830a798e591bdfd7ff132302023bd89d77896a4ccf30017d244663479f9bcf7b38261fd8bd2888560997efe5bf6a5ae665b9dc803590b35c3e36d2b24f398ff7c75607088095d86fe956d26ce9aee10749c734c83b40abf88a1ab1ece77643c87851229645cad07b04a029c01b977a203ea9841bb1c077ba9a319937d126de3828e1fd3a43d33f7f925a4ee2487f81dfe4bb6d7e2a5092fb00260d1b6d95a26596edff3da72011a923132b3b8e7cb743947750e1a29061dd223b2432bb5dc6a91a3efb5a887907c201d2402f5479267a56bb356bbf50b8906c1550f06534c2ded19c9991b12dde68594d8787085b37c51e70728ac300754c61a0e658d279a102a6b9ae398d8804be338c518e9da6a9a641b998aefcb074213a1e45a946c4a9cb136d27e02c72be54be5468da5f522e73f52b43266f20e6845b6b2a2594f5239abf145b596b68455d8430a90785c940ee91b06dcee0a071c2870d3a2df188daf811a19ae31342efa8f1d5b7ffb8f72e056d5dfbe52430ea818cef5a80c33b21d34d1c4e353f930320dd1beb465e141e7d45f122cd8d1033212a928fa682499556f8b39497f31244ee070f128bcd0016cd8eb35d97bc10d9d117d43edf0748ca7c1f7d7c6cbfb43fa47b216aa166a5b2e8c6ab4da27df0190e0faff082ec92633eca71f1f53b61d4dec4828502841588eb1bd3d31c5560b563297de39119549e7d71a5656e24c80ebdd2380a7d435fe4e55b136345065001de98a6d10ca53244f09be66917bd3c9d89f682faef90652981274fb4912c309474f72d315ac5ac95591ed34d4c78c7ffb9c74de7e299d3c9515bb5bf2ce7a0832168d20fc064ce000f715560de40a22c3ecf058be09fd7d56d5a0dfe7df43076b9f5161d34119f4d3300b8e9a670d810681eae268b8c88f2f70b2d4d428b2074b60718054e8e943ee4db73faf8b5794175140242b890298ddc98e5ba9032405e1a2b6be0449f24468121c5c152a44bc3ab199bacf1eac52487db43ee73bed0ada3878ae39b6d179ecbb70d55039fc3a643496893b9114067ede6f5cc17caf0c4932e6f2265f815a1ee52721a3709b32cf6cf3d1ce37be9b094a5c813d4491054d719fa5e4b8971b942d584033b5d67af1357be6a835ae3f06bc26455ba1b60e6b891d71de1f5336c9b92747585de5c9dd005113cd54687d19eb71c1b5eb360b +generate_ring_signature f429996a2c98e465ed627943859394523fd3edaf7ab2a919b0d190789e34546a cb0dcebde38589ac7c0859ec7885c7e6952debb5cd69e6b0922e73eb31ba5068 14 7d8cf927a4b89584ee99103b0203a51636d454aeac07e163065565f37a22449f e5390b19dc3e87c8f2e8adf3fae09348f3abd92f936d3a4c1ea8700b29e31dd0 af63ef0b26af519197bc1c456474274d00301c68111a6552a781de6b5989d86e ad8a74a8276ce918dcfe1f633b4212d61782b38396082709eec6547be24cbba4 dddfd48e4e8599d65a87c7ffd853402dd1dd945df61402d6f5d1ee5997bc6f04 f5ed6a7ca50fa4352ef8b230f8f043d2109bdc4d914492233a24bd51571c7bb3 e9c6b1dbb79c841d2f39407336f0536b30466d76dd96c30634acfb4132718d99 06d9d6ab1381c4b6e3f914ca3273965aacb4479a575468aa84dcc088f675009c 4bca1da62da88b5c0194f893878425959abba8f1b59f70f8fc35e90fcf04607c 537de67dcb5b0515251c02ceefe5edebf29f96c3dfdc4cbdd80f677aa7a86feb 81979c19b915ed61382c40953dc698d9d2ab5487d8cf9de2958dae69d165f8ec 939b33105fbc1114c67916681405b0dffd88c6b9d265023f1889a0ded6b6f715 1da95472cf9ea935e2225531660b0340f21d8db50b8d7f9ef889ea69386884b5 bc01a282651fb0e13e062be2ae48dab4533bac2bfa7a069d281db33ece3bbef8 9f70dd260c97b39abfe0fd3a4773ea3e31f4d4a440c3f998f68482a6a580910e 6 49f32c1a4ea7f629cbd25ce611e62f3b1f15afb097b741be253699e7af38b208f4ceecdcaf0859bc947bfd19bca13eb0c4a79edbe179dadfe60ce6925365ac041554d97ca72204756d8094425d7fcc47ecb56159b9ba6ac3013f56c9ad7ff50e28abb30e9fb96eef331d2c30cf01a2e01228743fd427c161e4c406c952979d03683d5a04f98be9270e2d72a7e7508d47393d59cc245a4e4375836f5ebb50220fde69ddc39b6620e6fb367220226a779f9d48c43c2341aea8f494335ea6fdee0228b59bac066663f519221be8243748e672d8869ed6c4854bb55c285d984889028b4f3922d68ea29a4a78d70c7cf6fc5e0870595d6a1b637a0a0282574146bf0e5936ebd37c845bd38b24de7e811148c4056b75a073afa81a5b300ec67430940cc3d689e46669af53d9005e5ded7d4540e98d52ac6f8b94d8ca8b748de3b8bd06132f3b781124bdfbf03f3a15723c07b834c5e8e2e9781b9b130f4a284e850b073ed20dd04344d534a3f034cbb5071e827412c0dcf88af33319352fc128f0ee03ca8e85afee01e5c427e0232f46827da409dd01ac03c9c90ebefde041ebf8fd02147bb163966a12a64c353c52667967a986813bed287eeda8e0ff40a43f57f30c717d735093ed42396a08f82d55681f35968bd891c3a02460a46e6a090260500bdc01337b050806ee9565f9cfcb1321da58c19fded8de7dbfac35c15f7287b90ee07e538d31607c23bd31a9c53972dcbc824deab45342d41b5c3fc28dfe731f0dd161b6440fd69ab03d6a7dcf8dc47ffea48cf0624f8160a1c817d0b4d2054a087275210884126315d5b537490a67f851da27834a40d0289593c5cccfff3fdb0510721ae833298b06b1be4b23d77dc510255d7cd6284d5d67e585d0071ee3090ebcad8c7deb1ff30e3a04c4f0455f559351277b977c2d6d926a0c7b9c55bcbc067d4664557df0bb5f48e30873f29005b53a1ba5e36ce7541aad33712e92642505b5454547505d08a28b3f944721f4db925328997e45d25757e6b1afc18454270e71aebdbc5497ea2ca6e11da434c39954828fbcb0cfebc32662af2b37faa74a02c4e313a0ea1b35a94e77ed6fe0123a39516d7d7317937c1b1e6f2cc3895d6c0f93a983f7c0ba910d408aeaf7d4e9c0b775d57cc2a81af7cc1aec102fc786580b7a75238b2bd01a3f164ac617e58b9673280131b43240e850ace213c2c08ed6094bc7fd379452ba684f1e2ed6184aef38751c53d70f3b11180e1452a91ac24b05 +generate_ring_signature 8d14132e5e17124e6b09f1e79ae09c053856be240e8c7382f8d6c420f7904300 96eff4358f28c028507c8e7f0972dedb4330b822653b2b313f4730d46839464d 6 c616538cd58a741d5b8a384cdbb2d17de994f61c90f280f7977f1a485c2cd43f bcce7c9de27adf25424520a824e71ed3e00652f7a08dbab59ef38b690bc64d3a d2ac5e67b93192d3714c302e2c933c9a303d484ce2c69c242fba97913dddb0dc ea7b1655ef60438b22f1e7d37324d92b4b548aeb12d7b870f2cfceb28d0977c0 2cb5bfe3c0d79f7fb25ef00316e250bec871a4ad7f103b6a9d0c0ac7cea10375 0d5bbb4a6990c3e1c33f173354732dceb862bf580770829ee361569a8d77b44c 1196427b5966709ba0a1ca8b6cfbf60f91fd6a2c7c7e7a6609a63ee0f801300b 0 9f13881215597742d89133c1b5ac0eec371bcce401d3f8d4e5df9511db9e4b0a6d4240f0f7c5f3f51172f2f2712bcf11ffc49b7b18afe178235279042c432e08537b092b97f25e9617f54b31357f009265eae4bfd2a8dd2d8586b0517470d301456358a1b1ef9ac19879b6c781ab2ed5931760b1da7c6045cbfa8ac216a4130fef6f678681b13a8b38a5c34aafc4bd70fcfe996d29d00b7754c4b4a014abab0e037536370b390b09838efea5d673f1ae4dd5dfc832fdcf51303259ecfe7a6a0dbbebdac8e4e06ff5cfaebd35934c4ea095c6a7d9301c99a4db0f335daecfba0b42fc4b4274879e19aa1a612d84867fde040706d398d80b07e096a32f6c77ba0b281b3440a6552f990db9c07409b5813d1b15169b771d50a6e68077f3eddd6f08454a80f3321a3e77a56d091ec57547e95509cfbf41fd3ae364387073549162056ac4bfb296d50dc52ab4a73e32cba3f99d1bfa92fd0dba27fd3f7fa25089fc0b9f4233b85c338e10e96a0c755ad6d8f5073e19882753ddc2eff46d1eb2950606 +generate_ring_signature 05b70ec90350d0aacf2b9be53ea0a7e0912c456b4e0fe7f3efa8c7fabb1de215 db3f8c06eef1a728af728ce5e028068dbca392543d43b30e3408e70a5301e4bc 19 fd5ead415e17536a9a92d5d8063001eeeaf376a4408d3e4d3f3351200f2fcb0e 05fcda741a631937b631b745ef0451150d9442798aa28288c6a2f7c02d12b08b 04bca31da70f07d8e34aa70087d78dc097c478267db53798b1a4003ca02d9e1b 8001d9b613fc98a7a3fbeaa7382d71a02f566f02e1281abfb83888e3c83c6825 36b1a72a97fe92fff3d983c734cdbd2187f34763c98e85e9bd42f90541aae212 11d585e09e6cf199be4e13c2bac0b996a4addaad8b1451e5b0afe18b53cd941a 766650cb51e01a611c1724099e9999c60634e1a3cb36295c58ed35d4d8ce6860 8151bbc0a9b80790b44e6ce4542749de5e5b91171f0240b2a029d27e51c56836 a84a16dfad85aae7dc4f86f3576d396cf2867961b129eefba1c40a9b87895f0e eeb46b381844257d051435756aeb6e235b92545095136c1db72d84edb64fef7f 78ca044b9a1ef85d91d558d71046768bb5ca5e4cf1a97a693a51e25c3590d514 953c11498b63f8cbcd143d0515aba347c20c61fe47168e7c862466ebe875908d f11936b0f1dc77561fda254ce5ea7998bb1c00f4e3e69e6c63b0795d99961d93 5de09f7a41ebe987ea155d42edd7283ce1548191db18aede9bf0ade990936baf 26d83642ee3546f8334921be1fe4c766739b3e92dd8a29894e1e348bb6fb428b 48a98894200a4b25f10741c42195b8700bcb390b790ba0e01000d3477b36549a 4bf26521935253ad92ba75a2cc88b931d82b3c79fef8e5971f228e34168ada07 6a5cd2c7a32cecd50514eb46046ad996bcb2d1eda668a7f82ea105756cac2b08 63b28fc379bba0a51da8d41d1d89ab5cd2d5e514fd7ce0830963bbadbbbb236a 15af32d28b4350f74faba181075479bb3c5527f1ed7172c00ac28b703127b307 8 61339564afe2b84312162545e634784144063d993112f8ed45f4084d24affa083c5b72117d290735121aec439f4626046066e9dc30106c8b682aedce0e2c410d552e53a83e5aead11edfc421e2778e09500bae95687fac02b68facc7c5dcde02d996fcfb0c95c49b627d1bd3493c5648135ba09ae031a8303b87ed82ae746f01eb396ad97b5c6e971e63f425581d4d41ddf58f2d5d834e7c7c6229dd7f01fa0df7cec1c568d6b1efec96bd5267a061291b1bd4b85196f77232a235bc94fefb0cc4b53a739609476371765654b03bc91086ce943cb6e41ada1200237034cbbe08982830f70395059a92f1fccc3152c3fa18573c0936f7bf927907046f71a1260ff9fe34d659ebc980e5680ee03ef8228c76beacf36013427e6e21a6eafca14101d7fe079ff665a57ee84780411543981440343c4a9860a34a30d057eb98d44a0de3f58f1ff763ceac4f192de936719ea5015e604680db535f8299939aa1a03c0866fdb1d6b276babc2589bd7c009bf0f46c96dba0e24d0e2113046fa39c55aa003f61275c78362fafc8f1f880622f1f638f03bc176e4c20892b9a6829b43d9b0d791c5b1f356a31e2a7b727d38fd61fbe7a2ec653c3ce835fa8ced27381bff604663d86e9c855285d9b35213cda849efab1fba58f162a3db7ef7f7b7e95a90803afbb23f999bb012b0b9ac0728032fd6e35c55eb23856075eeb520ad9f71d710c9acd5c2ffa4e90a66e6cd02cb1e02de8e47431934802a042895f5dbcaed83e0608d6f44145caf24db0ea09648beae153d53aaa356643b0b972bd64d530692f029295984afdd6ed2dae90a055443106586a2c97736c60de180b1030ef84d90f0522d47943d6c8673425e1acba41b062c97277d69efc842e4e46690a69e1a4020f5acc288c518416192370aac4da4e984ca9fb8ebb1cf23fac7412daed53a2060272b2124f671fd583527c2c52de057718264fe3aed450e3050a62a488dc7c6301630c13e084d3f15362a4618f3f6393d888664d4865f32bb63834c71d74c9fa07bec0314d8b6f1d925094c4a7cf98ac84464bad2793009c723128e07fab983b0e5d2b3bad4a8e28b23de778e32d5e3fff8c813097ecfe8d1ee777c10753481f0d2053e080463c670ea38112dd3494f8a6d9c01d6bcc10a20a6665a99ebcc3100559a0fd1e43e29b7b4a922dcf8ed35ceaa7e04f3cf144e90db0d758cc5107600c86b289b70b54f87dca3cd81437f5e9f45d06a332cbb107d8ec01f3d1b3b74801eaffa35f18bb3fcd209f55d5f255ca4275c39deec9dc29dfd7a9831884acb000506b821849582959ab0279ae00513476088d532960b778e627a8a79c7d0dd60ba676bafea5c6e6260b2f38953d847f560b4661c0cb91f8b5bd113c297d35800e0a6ffcf2ceb318a25b93d7acc2057d5edfc955e5856fd5555683b5c181f9290b4da2ac34e6fca822dcf8e406b769cb85615f02c82baacebfb7c24bf868e5dc0fc8f8f9c5f371b2060b7a1707aba634581626fe4566874e6a5dbb5b34f85ac300f3ea63612aab3df71c36cabcecde86dc36d241d84e102871a4060018bb825e0bcd5ab727c7adb2c2e5042a41a7b5e7fc32033405534fcce627f67d9a61853000a09dcc2a6f7ca3b885ccde29fd51160ae7e778f42581a62d806c71ffea308c03d475b6e100379fa04a22f1887c94c71856a9eb2f1f0cb0529d10bf3a25afdf0b +generate_ring_signature 8e7b0209eaf2cb145700a9519b958e08189855775468f752b72dd90602caebf8 e128395e02c5766dfe8e16fa00f544a0f7e6cb5e698dc00de3683278a33a9d11 1 702d51626defaab76f436ecbbefb7a1bcd953735c1b585149a4e6ae7c87ddfec 9caeec6bceb4ad72ca57dbf1562d53ea0a93feb6a69236f8166f061d93f2c901 0 b0136b3f767f4575c4a02bc641328b2fc1843fd29e11636827fb5a7b04866b088fb2301818e9bc4a100ed08af0931a7738368b8761a48322267b6f24c0c1ac04 +generate_ring_signature 8bd72f709b787215cd41c9f7953b0287d08f4e6a73780b558ccd03c3934b5918 8d3904d2c864c2c47ded0b25d5e6b125a902b075738deceafb17fe51df3357bf 2 ad75d42030a6426aeab0303392ce21c936df265c5557d800604aa7c3c0e390e7 7d31f2eea57234b7d6d6f09d4ff58cc4bb7d4be04d9c40fa925706466bb82f0f 807012275c28d219e1b973c1ffc62aaf43bda1b49fe3ff094879f9676253b102 1 1e58a96cfdb229ff1067364ab778527299238252c2f5b6b54ce52b404a58b00d3f116e0a92e439730e43afb362be35141f8ebe7e836104938d345ac9bdeea2047932553bb0ee58017aa60a69a3ffdc6c52d5e2d918bb1553abcc0792423c51010f9a7409b61649aecdd855f500a37683e43772e09451e1627829219f49da7306 +generate_ring_signature 21a6e77355023a1ca3e620126ee0d7f247f9a814e40125e2586bc9c6773928cb e2814a98af57ee26de7edb7da847473008e128902c90ae796a9980863d8e1df6 11 c81a625349ab970763589667eb533b4fcc76f764e525ed10673e5bae74bf508d b2143c5206e143dd966e38b3d6e785a87b631bc5dab23df88c2e357ce6753e35 44f17cfc3e461746a121975377a05cf87c6bd41ec84a480fffbf045a4b17c0d1 737dd0a57c5bbb2c5038fc3d084871aa281d087040d47b5cc36c381fb25e0620 8bd71c4407f1a7e9048cecb474fdbb527f15964bcd2f9a06b540050b372bc542 f31ddccd182e3b9530ac13eec95786b47013c9566499c20fcb8b31d6b3dd91cb 074096dcf159f4a52337d58bca2b695a95f087125676381832a4547ae6161d4b 8536caa0bb2488682c0c5eff2a2cc7723dbe1741e3ee28194228235a363e7f6a 3ad9404f37be6a07cc9de38bdc1893c172c9674cb186070af90ca0cba027035c 9cbf183bc430c397a8eea5cf733dbf92f7ce3a0365319b979dd32cfcc3285cfb 817989a15ed0e35bbaf2d62d94a2a6bb3025c795f830753679c3773a8dcf6cdb f84b370bd4c5aee8c383a1cde66c5ccdb497c2266a6b15e53d4bb6cdc0535305 0 28d4938434009cf81d06ce9e86550a97cab92ba094d1c16c61f3285081061605a969fb42eea4b09bf670b1fbdd6a5ed130e6781a5b328e99dbce4acb1b77fe092b1550a9d385743ba10ce6c1c01e222334a61bd6a6eb03df16bd3941e61fe601632191d08465fd08baaf22f7b0f9741c2043c805f58a1481db63931d5b12ae0838c8911d1a3fad7d6721b7bc02ffb0af8f5a163bafc414f7acc54d145e4f430a58abdb5b50a7d944a0f154e50869438526d10c987577134689ac8d2a98b90c01a9d705db5056137206bf5d2854631769970e979efd3d23154f7be752ec3cec009d4a6284e4852830837f908fcd21fe7b8f2d7b9675841d14e91e35e98320bd0339c36f4423cdfc798d01950e5ccb547dc5b45ee27f578e4d22f808ffb37039092b188f0fd24eea22ef91cb20a57d8ef85db92591929af96e47641ee8c478140b148f56e466b5ad3ace2e200a8d900c77bca53936944de83f1e99e4eab716440ff391d234ce1f0505750548df78274166b79a7430d0d3d79a589e9f9f71bcd507c75a0b61bcae847371a9d38a21e129eef8ce0ca16bd069e142e88cbfe829ab080cc2a7d2e8fdc80d3fe0a9026183c2e1c87c4a47dbdd82a92be831b4117fda028701ef5c1103fdba5ef685de1c36750bb7324b12e1816457b329027bdfb1ca05ae46072de09885fd53a7c56f03b596988014d0797724e3404216daa4bdc8970715a79457ff653aeb5f8eada9a45bdb12ce7077d1b8bc4f8b00a83e08198c290022b242d7b1f2325acb8cd457774ac9f54fd12233a8006171175e588af6e75d090c8f4c9d44ee19d95e418d6801841e5a74d16ceb29313e8d6413caa28ec23905c05f3022e84c1ff7c14603540f56bec6f2c21c90f5af510dfbc8c6a55045850671536c95a00fa68817b05f2828342a862b5cdf318869bd45ee4146324ba4c50850fcdf100b7459fb95e7047090e824922b2ad57a8a0539a7a2e2ce9d87f88506 +generate_ring_signature 267687c04961afdc4dd6108ce46e82c9c5a60a6db9b613cb071a86dea4069053 1f8b194a5dcd594d315df03b43f6e6c6d8ae58b51fcc06678c93ef532a7c2ace 1 6bddec7e07d75ea37fd6fcb93b7094d1e73a56715a904647d5b0d572a6d2413a 83e12ef8c17ad0741746e1edde68f34fc3a241e9d612df464f282e449ecc600e 0 e6ab686ee1f55c5c814478789c9af8cc419dc6aad4a6f8efebb0355d14c2b80b9788f7a429b0dc6ee13d5f0744cec9ec9c69e691e9ac94665052bfe58d8f2b03 +generate_ring_signature 9310255e1d6492094731550312eec4fed0f54ea5d706cb2a611434e94de2f67b a6b30c2ab8c81f1e2b826258e3800503430825f7f111a5643034ddaefb4a9fe6 40 41d5a10bee397e496d882d7dd820e3854bb222240d4fde670ec2340931bd16d4 dad6b13bc8910f762c3badfbef8a686fb787b6511eb20e7f6fbe390036b2d3a6 100588d38cca1dc9c852d2f89a1f11a246fdb955ec2f73b8d5f8ff3e915beac1 b8b3ed919d7d75574bad2a81c5057b596f62796d96acea022784cb653bd4ad0e a0e37438540bd74fca28cddf345ef304b579ca4fb60457b02a9438dab0cf09f5 e5971444bb60d3b5cf42a088f0e318d2b3c22e356f97457dbfb7362d91f866e5 a159413d63e3466dc7cf257d0e5c094d0d5f87003c9bca4a00e0898fb137a364 38af861b033f1aef2c996952cd44a0120bc4d04d179e74b2ad9d21596a9b8730 bd0a348d3280f6b986c74e6d3703729374158bf1a069701f4aba16f73254c0aa c857108270e21b98d602fef841b56f0411c12305f38ff38d14372c8718f7d2eb aecae0afc964ab34c2f5564c220cda5182896dbca21635ac81026d3c29d0feb0 7bbdfc571e773e399b569f4ff2830e8d9b518d0b5d113c66c78b7f4286a97848 d4e44123919ce55ef7753229180e82335c43c064d515bbfdd16af5459e6fce9a e9454bb7137d5562ce7fd1a5d387441792a1d252af2e7f8249da3c143e7ca5f9 f8ba4561c33a80e40c7ca29743aaa5f9ff45831ea41af4e1835af4843311b5e7 562e1b85aca51bfe7fc455fdbc0060a3107836ccb8e72f197ebbd4cae6c38240 314d5ca7078b9cf66cc9a127a84346dc7a07934357ebff1f03ebb46a71c3b29e 15899bb4e5e5f5ac515e9202d3be847d7bc4a5d2f8b10606a5c25d8deea117fd b088006581f476a6f974389367a8d751e3e77db254b44c37007512fceaaabea2 ce728b492bc3de4934263add7ec0e8b8e8a9befcbf6d303804e8d543bd00935a b18e46b791bf0f12877386efa7dbbadf980d2f090fa2e59826a8eca0417cbb8b b1367e0a7711b395a74cbce696bb16102f8bfcb550ada075fdd28ae6f91ee820 f9d2d946be5236f38240e7750defb8a2e6f9c938dabc5773e8fdd8debc50d6fd 4423c9426972d73a83f73448e6bca19ed2edf3230ea018ea9cad3b1f1d76c4bf 97522c269b46cf817c95aa9fd186b9c09f9ab2da7af3ab55bd562d3cf3b51ed4 9f9380bdfa737fa00f31bfaa185c89ddb4a431e18f4a17c12afc3233e236483f e9544769948638dd359c9fee61317f0c93e4d476c457238c7f8741d4fdd20ffe 033aede3e0d40659cef397ad6250db441c97dea5c26ae4771a9f4dbb1941cc87 a436a016cfcddaf1b1f1fd9dbbfacd6979269f0772526309cbbbf7f40fe45920 d40b3ec269a31e412505088853b395405ab22a54d2fc0b7cf8f36d3746ba3e22 5127aeb4f206ecb11b191437e41ae3ce2e8734066ac559921a470bd656d56c24 89b0e22e57e9182a1a7d02ed51a5e6811f3ac915de5164664b296033124ec6ee 5937d8236f189d12fd6cd9a2ba68df4d5ba2bf77e72ed9449bce95cc0105c974 bccdc7108b06aae759d5b4d0e7e03ae74d5dbdacb6c5ae6935289e231a1bcb7b 15b5ddbcf9014f7f31170a82731321e9cbaa1d3b1d374ac8e9baba8e301fa1fc ece21182404939af7ec56ffbc46d8a72ae114945aa53baf864c7dfb925506b47 fec8930653c39e756c9ead78a921a02bbf3cd46e48053766a3ea05efce314417 bc04ea71c8a2d6774c477819f1e2be43fb4e8990b064e6c752756cd8fecd2ded 373a4b62cacd09fd6ed9e3b8354445b8316d15c11dcf334b28c2bb7e3458e848 c81605bf680bc260a7090ccb84ffc13e1f1e78fd94c2320642a3786f3fb2bcbc 299563329c5dc60a5689618c81e1e4edbfbfe96dd877c27c30e96258ed287405 10 35dcf7c756aee357a62bd3f7118f36e1ad541635cd4c3746c7f11fc4ee9a9b01ba4fe13dc2997be0114fc7c69aa617bb39c6ef84a1f1839b3210e3f128de1e0f8d4a4520ce53fc5eceb402d1de6230ccb68b2de43071137aa36c592a6bfe62056556ba6d14b5cc4f73d844cbf9f8ef77536334ca7984a7bd0947bde09361870fa25a6ce41f7ab117188545b5cf311b1d88415f2f053443b6c6285a3def1df603ce3d0bda6f4c05f9daf2214858b8b39d2f28df1949941b651b77e1128c0e89099440b8b0e843f0a29bacbe0118749993f1fb801f72c22280b4dcfce350c93b012f9867af2a83abe72d9e6ab2ad0e597c7b9e45d2b46f46588eda644ccab2a8020f6ac108ffd076f6c0516bcd0d550fff5da3b14aabd29f4c27335b0ab4932d00478058792e7e8da0cd843c3798a3521f8c270f0ec3bad76b20c7918622c18b0ef7f7c65665b2fc39192bdfdd372d7377019215546fd7d5499d67095c2f094909c77238e7301ea963097ee3f11fe8f4615c72db8f0fd8a550bd406a4500383b06eb1bf39d35d136bcc60c8e6b836234b79a86c72fac01b1b4013b3c6d81358c01c95fcb809430c91c9bfffa3c61e2bb89c06a80a9055303e59316191a6751d00f99465e18f23680454247d470366084811d7e1f464ca01912bc94f2cc624e0909346e8948f995d9e59f7d30dc5b6e40f3ae2c60668d5a79c91b1cd66b97d9800b438ca0e5ce3ba16da904fe02cfce462ceb9e8aab7a90e7bdc045204e6e84d60c6c17b4a3b647b6e446f19c8fb0c6e24a74e03ea8deb9e57999a532fddc9b8a044f0097570277084dd23fe2ab782a19dd0934a4b0f7378af215e345c7da9cd90a6d17b682725c1f50ebe09f2674368f725d6bdb7612f5a154f4c1fc2c97545a032290d1dadb3b21be2b9fef3aaaebef163db4f9f116ddfe602f5a39fdfb89e10d357bc61236d775748e49e6bfd51efe16f98b26ea130b48b5eb346d5591b2110650bb26f85eae2548b791cee8e606a99827ba4cba107de05c84afdd24b522fc02771afc8c206fc57833b8bbc5888d8cbc484525575afb66e70ab5021323743309ef0b032c1e16b039ead8d87b411f24a4e0be3fa47c21f610840736a7c7c32f071e062cb729d351b92311c2cd074ecc7179c374a0015b5ee84b493e897cff9d0a53b69db8049c710282db431a3bb00ff4fc04e30d4d745248812cdd4de9baaf07af2384d96b8c48e3254c65e646a2b9ae57476ba0c7eb75730a471ca6e997eb0d289e832fc38f939fe53162f7f39f22ae71c6b26a13baa1baf00f1519220ba004bfe9c21488408f71b1e9b05c9d69ecda5152a6400815b12d8b4db63b90375a0ba7bce7dd7ee54ae6f80d7e501ab6d6ba32b61e4062d0f3cd73b22b8fe9734e02403e509f00431f8cad1a39e140abc67236fa1ee226b62c54c8b729e0ac434b03e209d78244595bb90950056f0ed002ed9b7910f5ee350de05443ba6beab04e087706dfeb36744085f5202772d91dc13704390e887f7cd416048ff1151342090d6b665088d1c17286cdb3fc21430f00c06a1852b201c9e8f892f76a5c2a68740a57cd673d402e34bff1f2b6ba4332230175a4ebd0a497b022a14736d5d6d73203c9bcdcaf79cd226fb84c88295176715e7f717ae8fb623f9921eea1a30620070ba4db3b822878b6105d31818f8188993d31716893327e1ae9159b3efad6b0f70242b3525825e62f31faeaf23f455fea0478e64bd1e94de2a7e9344aee5e9e490e3ab9d2e49bda51a70174cc2460099856c820b9772b92f80ec7278be4ceaf670084f233618bd9f0105c19f44f67f8ae9e0f2284745935266a0a13f0be535efd09b2ba718e4fb68e05db1b9f1080411f3cbaf0e70cd0e3ec29242fba231c91f804fb9e3b21b18825fe552b55c8ae3e5f0f6f532724dd3df9606470327875f3250f5291d72160354ac92566794736330d61a2fb53dc2836ef05ccffa11fbfa11a0c29cf06602914637d6703a422bf2cc012267eaf10f9c0f1e5a2f29200c5ffc1031d2ea4acd4e94d6085a370a6291b2d6c03e85ec7b53b37f33dddf5bdee99fe008442fb0bf43c8510c46135a14e7692c986875187c18970822168c4636d4f4b0f7b27ad184bfc24d0a439c3914d4c4f1654b0d785fead82fe14dd65faf3e7550de49dbfb75ab8f61e7deaaee7aecfd8987e3331036b64d846f6c181547a68ff09498e8f3072b3dcc9cdc75e0dab3d53e0ec4ff9b9b8cbcc8f13b955dac8cceb082f235a784fa6f15912094941b823f6420667c83d663518558957908f617dee0e785b718f95cccb7a4e5a57cb9f6f93e1790b122a39cefd67d5e35455f62bb5031a44232614a697ee369bf5a2b17a287d7b2c112399b98f536b59aa0c1aa2ec0ed6f3e6cf419d072485fe69bebcd5987cc8240b40ef0d4b0c289ac5c27aa291018553cb7d3b29b866e1210fe18888fd30f9a750532f7c5fa040ad69d3f2327f09497b6670dd35e3ce93001795a469e97fed68e7382791c9f38be295aca11c5b06b6169e068b723faab33e667d09aed5a11ddd397b5ff36a7630bf7a3c4e6d4509773876dd25089c008c2f7050c063541bda43a85f9a3c0f6dbe057f28917cfc07b8727f8e56dde493df3d34a701f3d85bd480a57bd0f764bd7d3e9351e20e900b12b66263897e006da183828805d9a32ef7458651bdad8e30f0896398d5d36d0a88257c70b4921e21a9d87611c2a4bdad9980ca7c07f44d7cfb605e85d4d8540944a8890690ba252fb02466ad3d78aa12d69ab0c0da23b04f5bd1caa3d91e5c0529de3830893598202484452cfde5416fef5741780b43fbd1a0dce7abe3540a0a8ae162cc27d3dadc7d24b4a2eddd9946b360cf9b50de985789479206d5129308f8dbe1c001fd74d6489feb42f632aa4bf10c84c1b403c32f1f7595839bdca209a611945f3813b3a46f951f6fceb9026118a47677e8fd6d4b7979cc014ea3e40abefb3822d551cb89570772fa1e508191c0a7e43018b61d5c2e0fe30ab028d40671e982212222c3df945172c35b3986608e78fd187a774fb661dcfb18923c67091872163646b8fc9f7020f981b840781946d0b1c4f900e5fbf965ce0f1c5df800f308c5d94340fa52139cfbb6dfb0c459e050bd6869a7b51efd0fe2ba27f9e801464d0eab476d705c01819d0e61cc2af785bf5529c2275f6e163fb261c650250d5119e7418f44032be271a5ca6174454040427b6562bac5518d0004bc9a493c0c286f5445e0612188606243acb2d8220bc77dab3e8475813c9490d7e7c98b3208775b850b39580dfd45981a761b12408afff711907df44c2286bd18fa0a64d6081393638431da4888aadb3547285c544bd54b27a9d9b5f5136e28476fc789a30bffefcbd9d67e9963e47f8f711a9af038e992699dc63fa1b3a895b6eaa84478036b6908e55abc08f1bc2afe6bcf6f4b11e68d0ee5eac4c8017897f9226e27a60ead22365f569e8bd15fb85985ee8e51e0e8bb6dd70a4c5f0b87484ac37e4e6b0bb80b579ce4df91ffc56c3479b9151159e92f56316918499e1a950ce596539c0e3ecd955894e288a9c23e14ba741dad99ccf57d5bd03e5b6411bc64a1c4475603 +generate_ring_signature 657d817c2d4d9a393b1d9eda25141349f6daf11cc4d6fd7b8a1f279c12187e46 e8c62926823b84157dcb6145174f85084425a5c705ed0d37842430352451a394 1 6ca123601f7636c3372154cbba220818162efea295240761066b059d12148b96 eeb5e6852d87a410c402eae49f12242b35702470b648824c817adb253d119704 0 cee9b14166a77a6373b3d4077f1550d8a72f54a823a0aece5382269481e8a00b6d5032386581077ac954bbe3e4958dd777885fffb5588cdbf94f124b469f8d04 +generate_ring_signature d2ee8055b5e9fd86cd9e9cdeb95450590ea7a48e976337e8af699593d15d6232 cd23327ec509965ac07ac7f31509833e47fc56b96dda646223d917c02c292ba6 1 ae47e38a341217de4e0562f9b146299c0f7cdb971d314ace7522fd4fa3e20fb0 8c4730239e8f96e53ec2e30b856645506887527e7b6ea24fbff166a727a74007 0 6b65eacd2f7b2b882b7583dd6bb6345388951c77c1e00058c0bd01bbecc2ff0e33a4e0e0498ac03a17e70b0edc107710ff8a9d83d93fc05a3a833970c2ab0903 +generate_ring_signature 5f6aaa96065ebdadb91cfc017ad7e81c1052623a75dc979d052c43d025572d46 e96c5739f208695639dd5120c89672abd6148f185164bbf7e17c643f2441ed18 1 71b0a7ac6902754f376699e4526f3c9882787ef2ec7477dc2cca2b5d6791af28 0dacdc8c2bab9aa153b91ff96e7dcdd6736e8829632e92506efa068500ab4b04 0 bf9418ae6d50d252d0b743b10663324f0a0fad3ade6fd6e07b54e3f3e360af073f1110dfe23d7b3d6cd85f42d2f70bd577428ff3085ddbb3527a5c7a77c05307 +generate_ring_signature 210c782e19ba6264cb91745cb5cda5ce13b234ed030561310e88f6dc2722b483 b439238aacc9b192a9d7d1b73f845d24f574997d88c949d54d372549419baa7f 225 385d632938755662d282c477ad5bfb19bb1d153e5815077216a4c4e2b394e211 5e180c966833b87b3003a824abf2c6966441760258c201f7002e823306a9c2c3 5d040395bd315a7bde66937473a7d96ea72829d2f2f868258edb4cb265aec30d b5f068ea2e517abcd21e927c7ea996c5974cb5c681805ceeb348244594a342b3 327d212984e2e84026317ccd150142a955be2dfe34d19e194a4357bdd221a9bc 752baf4d17686fa71a2534a6864477b608ded4d869f847586fc6ff4e4d266255 05c39810cc10183513ab20ae2caab4448a0429f50673b239286c2843b43a9aa8 f60a2de08e5a77b7e56777d6ef84a5b6b546e6934cfc3a635378c9af0b7e2f1b 9986783276b0dd845c3b0c56ecda1b29b28bd7a3ea155ae6fab4376012a40180 0b3b7b391bb34851c1720cea7925614177f278deb37b16d52197fe8aa45118be 9d41a41006737c046947fc176183ee7baf4c37114479616e89d7517b9d039a56 d057fc6060c31d7629edc250791ea60c9f49bc5ca3872c6146030bc08ceec217 64bfe3083a136ae58ba33130ffd58a60b338245e368db66335d4844bbb0e25a4 4b7b5ae9dc755559da17de7325ad60d80b4f442bf508e304d2dfdfd9a3853a5e 2a5d589b910884a9267458678333e8a3818a71a45005a7aa3a9e088ebfcb88b3 eebb3f88b3f7951e243d7d8db9003edca9f0d9001244476efd46074fbd904fd7 a9ae02114f76adcf94ad23a9079337f1d1edfd74fff2438ddb47d0cabc1f5af3 db9edc21464cb1d6a8001bbb2933214b5ed3a2824935d63c05a55bc5b681953b 5afb033005752c35a5e1fcc95d2341640f532d11107b3a54c212b610845db203 8ec392866d432fa11d21b4293ad1ebd421fa859ef7c5311eec1acf73da95f366 2d372298a9b62e508013d83cbf0d95ffd72fc8fc6fb0d623db98a47e30640eb9 7d13621c703d7bbe44a196819e192422a9211e53a8a6ee138d99f541c7c45bbb 00a26e1fbae064a1c2c800ee32ff1bec58ea68e6bc3d9319d0e6f7d9d4858943 b3e674609b98360bbb85e1f9b80b779453ae635c894adda35bd100e7c6b1879c b9fd8cbae04c271e2e59702508d371c15f180310b6b5b16bf78f92c30cdf3451 c7d7b20e3bac8ac1a104ce0015b29d6ca9f66e4b921f3b7760d14b0d0ef806bd 50771023cdca6100850cdd2f60cabab98e9e42e6de099a1f399b898efc79321a 736e41ab6937e217c5ecaaf37eb7bb7136e8f9e29089c8be9567e8ec9bc76002 52cddd12cdf1c506e78e29591392fc24e7be5b9c5b6136ac2ecff15a94fe0768 132e70b68a7ef9c5f401b812458745e73c3b1143fbd6d548266d446e8ed21800 a5c2587a5b911408527f894750bc2fd3ad05d88fea96eb3fc521ce217cea614b c36d3589279a9754ed703a49e284243064bb8201823bb1e6c65ce1f1cefeb3a1 f352384e3ee128d4d9f5e53677e193699a4a0b32b87b494abbe9affe461c8224 623cb978cbe7068f3f5573abdbf77acb50f1fa21456d6c426ee7d14d925cee2c fe4eaf7646a7d7edc86ffc7bb3e02ba1bddac47517eb676a73c8cba574ac7583 74564c30fe7b0bed8908ed7550c7a1c0e81dcf9972c5588223a5c31f7c5f24f4 3957b9c95219d16df5af553548404c6ed013feaaaf3311ccc19767979f81afac 52e16912389caae211a4ca91048df9d62541505cae921b1adecd1c92a5e3106f 9aeb3d3ba84f8209795c9fe000a920d7b58e361c2ad09856e4b385cf1d3fabcf 52e159e9ed060899121461c71906a83f47c1337db4d487590c1d7b742e23e29d f4042896920a3546282ce109658cf0f1535d98aec685a54bb653bbb52f71f560 81581d762fda656949b3c072d157a16cbc96e27f73410fed70cc2d816ab76632 01d2d48e6e5599d24534f3482427eb4501508b8c1068583f7354074462aacbfb 323f4706c986053428d87e580cb45426eb38eee25fb956345607f71f76e2af64 c48ae1b3e86ab129abbf82123f987b773cda8a621313d5e3e12a82504aa4b4a2 764a1d46d895c15e2de0d8953d4a349c11ce203bd8fd5853c7acb497040e818e e59cb3843b69071cc6a876a73433694f55ad77999c1f2d9840cad6f4d46cddf0 f0c7fa7f51616a2b8b3d3c66fcd1b999ce236223d899a24d21d7cf51c7f8320b cf103bbd9665692f9ebb884207be5d48260d16cc118184ddeebaf8db303372d5 718247883127bebcf88b3c0aa856d2c82f205f0ad882cb40fc308f011d4c6ed9 12d01937773a71a29ddc4fcbe01005e7f3736b25414dbeb69f1f15504d65cd2b 8570f352e0cce7b04f9a415cbf78ea49b22c6dd1e199d2bf2a36eb7863b52633 72f0a2aaa9927f98bb985d97aec6cc91caa36f06ae37fd49e717f3c465943bf6 955bf1291296b84cc7f1a01a9d38145a747cac1f901739405cc358c1a791543c fdc3c3ca79c46bbd3226776dc9ed47e1ff3f3a7748860b9894812132f0290cce 5277b50bcf94ad33066eaa0d94885009bb6a2ccecc8748ab5d2b053d1c4bc8de c82775ef48623297b0ff928b04c03b1502645d58ed9c111478bb9c158a6245d7 88cd50b04d60e049714273798db1af00d5106280849efed1a9c1e37ffd97e253 d77f732b38cb637343ec20a66da97c5b39c9c3e8ff4b84a444e3d32164612791 cab9c3f461b91e213008d896bba641ad24993f98d1c7c872626c00a8b7c001ca 4c62c3d7c18c0b5f240c56b6f20e1ca4eca30b7d8806f66aa43f5df8a8d382ac a70644c6f3c2cabee8ad982a2994c09e81962abdc94f0fe48a5de7b155941de9 3ca3119e94ef5a78db0e52495b37ee42d3d87917cdee4cbf8ff53989939b626e 0df77c493d22bd419c3db7ee8f464acaeb6d83d607a7bd5a6083a62c1b318f94 011fdb5a7173b0924e03210b8ce3d0bf52a5982574faf3b110739d0288e5da3b 3361b6da469b5eacd5a5699ec7474c764d1426ae5caab18133252b5d186c1f41 eb68f27174f1dd2d342ea6be42eb3635fdd597031142086cef689f143e149faf 1fd91c95a7eb4552eec9f60fa61d3f904ae3c8799fa1a80926dea02cb8b69fcc 2fdce58215cfcd09250e3830dd355ef53090b5f8ea6c65ea7db805d720ead1a2 b1678ccc6288b290116e3d8341eb32e9bdc0e7c4bf1872afee58eb9430f82c45 48eb663ff455d22f43f0b6cb27f7d5eb0943d4b60b683717198002d7b76bf4c3 2423895ba50880058a75cbbdf5a535ce176ec31a14ab32d9de4dc3855156b8b9 06043986a58cfa6a7fa0668668b39f297b00dc2aa7470bc9608021165442b15a b881c686c1b423b1c36eca6246e5bd5d0dbf2f1bcba8462a008ca7dad7b8ce9d 1222668bc17ed0be0cd5f58172d8c058cf3869d26b201b850e46e2d1ba6528d1 1bfa0e56cb48d57372b35238c12746ee52345d52526ebabc79bdc28ac559f1ac 0dbd0538a75a12d385837b24a7948bfb3e7b9ec2e10b05516396041628e18a30 99910ae1888e67a0de161a454574c19c1f008f1c8559204e575db88dc80f1a94 1f000d2ec665610f784536730adf1b3e5b9b9db13a42165d9544acb881b0b92c d4970792197ccc02b043555225c505be8495eaf84b6a70d55139dda4c63b20fb e4601ad66a30cd9bf6e8a7291282a91731c303b459843c73d6b3538bfc3656ef 256a7723b4f9b7d7f652579beb53a1de9e7bee8e75833cf73b259452783f3251 613669fc70b3044796ae69b20a3fb3ad32f2e0e64d789a00dd880ec974e6afe3 0a9baa39c17da06593fefecc921b5a56b08e41dfae48234f0fc23d8f6a376ac3 8fed0f0e6b4d42105fff2fa2f5d17c1a29959a58edb4fa15f137f0676c6ea7be 630f0d0f9a93c58f56d9cd2f6ffd88e32588b5a2ceb0e1888429d3a38db25ce0 7e12a83617a864086ecf6b8c440f108ecbffc96bcbc8df5f3eb3c72cbd5b4e12 d267956f1075d0755c7f76b498b63e7f51c6e1c4902067d3be637346db102f1d d2e828db06faf7dd49e7e5223c21231a3b4cc4c3342cd71a3a92f06d727214de ce057552eb966328262a82f0106561632761a1872a5348c00e2fea537a946a4a e44b42268cb4127b29e47f5cc6499e67b618afd48462eaccc13477423abebca8 f9f0712fed976ab4091ac0dc3c15b3c2dee4bd54eda3e08c6dd2f9a4e4acdaa1 0e65a84589f21ec3b9ab6aee1d24ac1be5cbf7734450bef6f523b1052601341a 12fee1a3b1c49e1bea073a7bfb93c8879582febb9c28354ed5ce0268077fd176 4bc6cd65e4451fae3817ae3210d8a59303520d0eb1fe4c5d5e43d8466dd784d5 b96717f2b1ff0ae2dde66343dc8e00268366d0e8688337f6ab7d18508968ac8e e095f6be7cfbd4143c065b3f74abd9efac0552e91bd66c50fafb075534539ecc 750c8f66ef049a2f66dc488f51a125873b97569c692b9c31040bd4da3759d5ad 7612ac603bee6381390e5a20fff937b2532f880a0e51671f2641b67d58135f60 ebf737d8044309797fb792a059b02311943c12876d3a5c1384166ee2b717820f 80849e5e6d4f4415d77a52dcd6fa289881920aee19735d03fcf5979c2b001993 c97589e7bce4464b1f1921265e8ec6af29040970f09f2c3475487e7ecf1d489d 9c407322aac4ed8983ca8b8dad19b211f0c0770195119157fe44f1bfda932ed9 31fbe13706f2716dc25b149e77da52c1ecc55b70053e41e8bd92221b1b385783 b6ff14454cad1917030df735d2c9220b40880d7fb0ed08841e9e705eedf9036a 06afd0020bd36ca861f8202223febfb21eb5c7e338c907a4ce654e457cb2a315 be6f0907e14047eb8fbfdd1f878550770c6fc2630832b81948927b6f80617372 163f776a0f464f59a6fa6bbbd7e93aca17ab04b04b7ce06d34226bb7e900d7e7 f1550dd1d473f38ec080a6dc9872442350179aa80ebca3afe3286c61b1ab732a 38610d484a664c9cced181b42bb7a96a076921437de67f6be1c922e07ca7ca25 5990aa4aa00146e517e50f3eb04336597f2f8deab18ca1439ee5a90863d4a72a 50d7699edcd2730a75ba37516553f0548e2abb5044ea89cac8851026aa73221f aac4a77bad6ea4d5dab8b739c0a01c9fe78dac1fec94fdfb1168bd5cd59798a9 95022a31b8a904b2ad50d8f60e7779fbed16e47c07b1ea65c19a13ad8ada2291 439ad6b57a6dc6b7dbe15edaf44d7582f0d6b316ff823d41cf85ab381d35b860 38bfbeae4653dd4124de9a1a3e53bfa3066d4e5961412d6c1de12cf77d27fbbe 55992a2b6cb34982966bd89eae451b39c701e79de02d7f3440a8c493a51d9d9c 4b44ea04e75b073a4d597f4d1fdd414179bb7bc5b8ee9001a3c3313eb7a81028 3bd990d568b7f23824d36b441004334bc7d88d6552e1b1b9a6f772ccd02af8cc c6c13ff416fe823ac2ebdd4d8c96c9dcdeb678ce832218eaecaac26d227aeb93 384e2915e3003d1e5a32fe42c165eb31ce1cf2f7972ab7080ff9dc95146cb44c fa79bb9d556b1063924ff37f150aecc8590ebf234550f4c90135fd301010d020 a06676590cf910a41cbe07a80e3711ecea47d43343eeefb106027aeb81f11fc1 359028b96cf085cdd4156f29c48aacfcab8974c51e31d1920648d844c48503b9 f4fe5c34eef748c3c9a3dde8eb3a7cf88f894f7f29275ea11ad9d21c2da7487a 7af8f1af63e71d95ea3c26f57a9a3818945cde9f1f70c77678e501a6ed2c5b53 df5347afdd221d101381ca39489ae4a40c79e60dfe914640b7c9a15695bde31d 9d68f3e4e218ed9d94181094ec24d78d8e5e74462edf23a04e0f5c309a488ea4 f69e67c58bf7d1f54daff66bcb9f967f4a800bedde5c1e8e3388594d2b6162e1 4e522383f361282994b5df8bec583c9076915963c387cd8fb79c5a143b77b20f 649473a9ea78b524cd53b328a181eb40c0cc67f93545baed9fb9d013a218829e d3dc41dd315b7390789ae65cef880d5b87f4b6f1806877f4828313e8842fcd02 2f14539f942ee6e9b0f10fcedffc6c2928737cdefb8ec12d1100296ceb74d56b 9dcb4fea73d041a83daf21a9cf9cecc054749ce61b55161cea8533d0ffcfd66a 8683d13dab4da5823053c4623a860b0f30990ee197a528780a276f81c70b639b 700509bd43a4d2b1cbe027b952587f67bf07617ef194f1d182718bb1761ea6d2 41a1e9fda04748ddd5fec36d1178167776ad5fad58ca7ae08c83ca4fba14897f add8a8ea5b584b8eab15afe856925109b90a73f21700ebe6be92064968ba8766 e6db06fece8a3c69c8345ebcd3a5448348e3ba7b3fd15a05f2eea0ee45783d5d d4c104b6d17052fc549dcc764684c848cd7aa470cd478752b814010cfe8c31d4 e36fc9e9c235d35b997678b41b2702b931498e32d22911d3c47aea55503b0033 c46fd9dc260f6d25ceed78280b2d5bbc08657efa5f7b962e30e22ed3893f768f eeb7a4cde0a142ea63cb9580b0938366b3b95324d18b17da2be4d2249106d3ff e4987600c30314a65d4ba41c132d4aaa6df9bfdf43c9f6edb4aee71afb3b1ca9 26c450378bd5de84dace9354dc11164c0df30a66c2623ffc9821e9ba96573474 3768619c48bbe89a1929c7d18f71dc321ae9147c29e1e9823e15389af035f1fb 65c1b6c65fab69cf2026b344b5b0cf7e312d8554c8ca9fae97b87edcdea5f945 79be69b0f629b18930c38f588b76f86a2c91dd5f8ff77265c58004639539767c f5c4443cae1a272c3ea900331bc349b72f7537e4ebc4ec9995a5388ef922731c b71e544eaa0ba427d75dfa2bbbe03694da48ef2be247833d143f81812c7b5a47 b7b2457939c77d5ea8ffd71ea933e4efe986ca8b455e53e0ae148b4ebc4d48e0 10fca1960f5ef371ad93ba33f223b7d2851ec0c9c76f70b2d629d6581368f90d 988f89d0727350eb720066e198c14229bd9c6b30a4313419df9ed31503ddb02c 2f81e060a1c1d035ff2cd062ff432329b6b16dd5e5ccd3ba1e3c64907ca41351 335def56d9aadb5e550a9a87df2c4171ddedc3257cebd96a88ba2ed0b13f7301 1bfe59272dc577a9821aa956551cb1f045c67d4b9228a04e0b4449a8b462ff0f 444cb383eb3f041882c5ece5a3ba472f6cc4586f101c63050d8ca8e4ccd0106f 39c3b97d1ffcca4f7d0560f49f5d913f67a3903789743d29b49b902b897774c2 fae6d7e20a850723d9764e97e3856dd3863329e66705d8d0ee2b9889aa70c327 1a095ec877ccfd6ad7c098273feb65d098e1f573626e57de6ae4dc61784c9042 d562adedb07334daa8ef9cbbefaf98d64b24c9741dd72445a9e120e183a63c6d e80223440a373cda0d6adad064bc2148b320113d7406e1eada98f1de93472b0e d15f5a5ad17f8a3ba30847d4e85424da0114eb96dd2eff269cccb4b3cc364591 48707fd6560c4cf9cc8222491d45810fe75673ebe2a7de749e9dac85242c1104 8963b85abad1a6a24eed9b63dd46902766eb8b0dc13d02b9e80e3ed5dbfe6511 49d0fe1c91a4037348cd6f6b8c2bddb8da46214c33e8c39c0630483b83305ab6 f803ac0ae722d368b352c47dfae3852904bdfe9bc7d4642e1126a482d5e9eb72 1084f0fbc396d6ac455df3ca4fdc22a24b8d1e00b3f904ffcb6d2fab8729590e 4314465a59abb470b1e05dd90925da4035ba6aa20ea102c56b6365865cd05a9e bdb6292729ed47f044cdc357e7081b249a275ba904149ce9719b57dcc144269b 033d157a3b91aead40b8a4f029b61f2a40d39b86ff38e8e57b52c03c426dd10f 138d03ed71356e08e6ea8d29c57dd8b2802054587a058c80773a75bf74bfe371 397cfd01c6fe89a34b41873338fbb70b4dfbe8a954d895e88cc87ab2cbcd7686 292e2bc17fee6d9bc8f09c710411605ba57f8534063ee3608ef39bdf73ec4db7 8150e00b1e5b68586aff8612515be21fc803344d3d0329badb0cdc99b80a1362 c92a75c221dde7e996ae5f8ea43a5760563a0532dcc6360a60dcb0c7abc13f75 49bebc69b650aeb8ad9b8382ecdd65a74d640153ee047060db71536e5166e58d c03564454188c4a7c29c94b4d128246140bad2a621e400137c7c8566c3ed98a3 75ccedb4415bd43430e80668831b2f667bcb086778ca43805d9b67ebceb0138e 47a95d5a7fa6e64dfd42fc92f103795e8bb26d8548cda156075d528f4ea79c37 ddb756c6e856bf99b3cda839a95509f50a9b18b87c8d9e07395eb1f37f351004 d813a9adfed2a92f4d2a386acba181fd12d26e8cd139e67e39bb1a45775b3f8f 36c6a368a17becb5b42b43855f1a1ec9b958077077a9ae8b2b6186854f2d7b96 5698732e696ef2306c582231ff9d48f919f4f5d59bf11df3e2bc7a9511a8555f e0f7dd725ddf4ca25c9bc32f1d6b33a562325573cb3bd9f9bdd03eb15a568bc7 6ecd766abcb6ddf79fbc0b7b510d7be17bf5497fe57e54fbab5f44b59522eff9 29ccff9bd63867d4bdd3d99c79bc54b9503d3312ce4792f280da7435e22c773f cee276d9b36560b5078d93780431258456ad73e47b98741c4d885b7e917a620d c465fbc35b063492301a77e868a24728a238335f1eaee5e7c94173fd1d5c2bbd b8a805da943c09244669e79bb62d6a58e80e0cdf069445905b332e0ff2e0a4b9 081c277c8956e574ce8315e55a4d1bc6a1f7084e3ef79c565c5c920e09ccf7d7 62a0c62da5b16a818926f5568bdb97370612c34391831bb8fe60ddef00333e80 e98bb0d8e342ff5ec1041a49101a848ca973b3143373404be9a6c2045244abc6 4667b53e81243320173ad6bc9564fc5249fc89410c70b915602a8c164529342c 26c435b82bd68d0b7ce15e81bc391b2b60b554be5867e72a9566a101e27ea9e1 9587ffe6e571e2e36fe80649da50c303ce81f852ccaf2b2e9b7b4664260c00c9 3b28089761856fa94678ff62ef7f5ff7bbdeba5ab033f2bd71ba5850bccdea9a 5d92b8261d4196ba81ffef7859087b51a05ae5121554181cc687a4a9490699d6 297f21664140a48c0cb3fd6e6581ff9acafceb152c76fc4bc9d31d946e2aa54d 87d4a74090844bd351a393e7c9fcd7fdde317947281a8680ef950e5b71cbf8f7 1435d4678846561678bf9d5600b2a787b6339d4ea3b8187a47ab5c6541817741 a8ddb8e7b2ad56a9d46dfda69e17472e610b68af71a3f7ea908c9bd69d68ac62 d8fb6e4733f9fb707f77994667f77e4e3ca818bf8a6af8120e50ddb02ee65f83 8c34552da56de382f71218a665199132e665a1aacf11cdfbc77e5ffaee59c61e 098d1aceb9cc5ef48ba98dd731a3440ee771b9ba272a2c3f07cccdd192f76661 eec48d281e1ced0a49fe3259dd5b4834e22da58794495cb12b14c50f42f352ad 01bf24b8be84788a89206619db1cce831a71cdc4fe0b51c61d0d93144557c19f 53559b4a639f13b09851a396796fc6ca68211f3ecc2a00a687dc3a526bc1efcc efd8b29f481a043c230077c3eb0afa01d99835eb6c5432bfb855b4edfe57b43d 69cc325592eee89f8c1219ae28e35e515f167491b1a1a99987fc1091e3d7ec8f ee3e949a0957a440e8dcf6401c7483a97e78c90248ebb05bf3fa2259fa3b767d bda559c3325659ebeb39078896a9076f66ebdb69b95d21477d422bf892c81c0c 435a8f4867263036f7af465acc3c052161c75a6e983e8ada56e5a88fe04b4a6d 2fdfe818559614cbdc8428e814aef218181d888820c66e8e92b3d0e038b7d971 9f7a9fb7d041cb442104b1042a85085541b4e19332f837a6bf8200ecce1e4e70 97b37dd1b11cbd70c90f0c80fc8c4ae8ee7dffb1ddb1a9b89fbbf94a53152516 238255be5a20745de3541e9e269b10bdc886707eb44e6b7677ad29f57c2e5380 18203e567a0bc5b74c253fe7f3f55632d245f17e1acdd0efbd806129e2c57ce1 183149a1e04a061ccd380488268820b73083007d21f9f09ed559d49b2ed9cbe5 2dc198fb09f54ff3988d9c90d94238ddc96197722d71cb1793164eb352ff4a5d 45cbacaea04505e21430785d1b66aa47eddb172a078a578fb976b018b3902743 98938e4ccb446cfe999b091234c844e2a6648de95ea4b7408320b1b37a0157a9 d6b18d00da6c1f1585aaecfb15f8913fa65cf12466dd5ebc55fa84b28cf39e2a 56fda2b69037627ee3958547c0eafc7f376c0941ee1dbb2dd5885b63727392c0 b3538a6ac0908d7f225b58f2185a1735e2f0013d66a5fc5c7371158fe97b1005 4f4ea02eb4ab62fdfd94367557069356661e2d2032e9ad94b04e9ff3a7c44a01 204 16e419538ca50ed94680f3a13ecea37c21e8cdaa4fec621163935c2218035a0abb8be68930c603159fd635f62592c22c842742eb2d845a2a0f00a2da754f2d05babc0534b9b2c908de5138142cfddb036796d4a7c057ef56de4200294f886f09cbeff39b1a0f2b98c59d33b9709095aa7b26245c8d7b28fce123677db92f0c04db68a76cd0477edffff397b7e8ad1b7a91008ed41d8c1ad2a5f32d32ead9800ad8327c39a72f33cbe342a0b610e6b5f66a0f083ecee31b2d40b9951a9333910f0a7f5a91a5ace137adacdfe969fd95fa101acb192ed6f1873b2764c39c85570679075806bcf626ddf4c9966e103379ac6d5ce8d8b1b00d97b6d4494fe917c706a8aa2265fd1c60e9302a903a1e7b198e1e879d7e828da04ca9b914cc8751980e2e60ecd022a942ae3b0db4832c3530259c3e4c9aece72992070e5c925d6f73020b0f42a08162e91ae14fc0d66217c9178b008925ad08cb4aa956ce8de60ad50e954c1e7fdac604e1783c138a6ad51f295b8d85ae91c1f5c4b35028e1a372da00f84137eea8de263dcbca6935810e7d69e714f778db75701bc03f99246f24d009dc56f481cf8ae2c2ef03c8143acb6fdc8b3e70348b5efc205ab8e407b76cd30251b53ad2cc7b2a18aaa5e40647b4c1dea3cd9634747e006f8d3c7def6645e100e34347b44b1a5383f014faa710d4f7a361e8868b76939858df4d51b2ce1623045fd3065dec39648af3973f551c40638a61268f4ad4fae0303a26ebe12d554406afa92575ac82e732965c1352e3ba0ddd7e5512f8fffe614a3014cd095f2c5405b9689ba74d1811762645ddb3fe09b3d8bcc5ce45cc63b945adc45c2e6a6e8e0a2dfb265ab77590c4636f2975b5c9eedef807c7dd50915caa2896931ff9e43d000136cf77bb49e4214490a19ba0b883e93ca056f108f3b0c7b3f83c9b0291dd05aed430c3fbcfeac1fb2a0356759d1fd18e08f2eba1f9527d5d8ba2235f648b0ebb636b067b469fb9474bd9a180fbe1d132fee3563cb10771faaa4ae8eaa76f0ba555d2f723903bd0390fb518e724060b6877840a523932fc3349e739dfba7805e26d5aaacac5b0bcf57ab48971bcaedbd3553c474e809bde1adf9b2e24d254000f87d3bbf4c15c31b886a5e94694e3f3b737670e7dacdad2a257cf3ce7a1a609ff0a1316ab8e5942b2acfbf93bfa7853c5396e301d126927a16e283eaf35ab04fef2a1a9fee695a3d503d9c27e677b2282768a7a9717ff1fdada9fd2399a41060d93e02b05c28a19f8223a2bc4e9c24732252b79154656191b2baf8abc61980920c7cc5e425339f24d3ee8440208b0d9018289cf7816996ddf40dbd17b15d3093832f025b9e2a828a5f8e68ab0090e896d8b8d132da4972fa06bb77d6f146202e41d09f2dfb81bceeb916a5adee209f2e7486d7b53c9f07bba4273166cd1af061d8ed64260353e65ddf8c04e47968d2deb6ac37113eba1066d5ba933c3b5e50c253e5cdcbfa765c33b079fb5a21abb809c14d4f8ec3154e1066e2ecc6a56f10c18f2c71af5f566239552628c37ebc1b472e1350fa9609ef7ae1f57de662251024913a9de56b950c37909c31b58595634cf69a377243620a8b3e7f566ad6e500a9c8bf63b1b1a935bc7762b2e56826bd8e14c2f4376fff3ca49a9542dfd6e6b0e6e15ec91fa4d1b2ed4c282e65e333aac5446408ceacceb14bfded640f760720c37851b04d6618da04172b0c001e2f21f16d9c94d56494ffbdce8881223e5c302a14febd128995b8dd1d3c367d82bd4c348bd0817ada2d50e92b3107d688bca08ec1c9edd4e6a64506ea264b7378b3a87251bf39aa53342fba1d680dd5bff270d5d04535e058a3378598f2f6c23c571da0ca0686821ef9d3627a08d561cc82b078612bd5a0abebb9213f319f2cd0584bd1780a14643e90b22f2cc2e3e41b0a30040ac76fad246d6a21db47d97f5dbacd06610bb088f2db94256c129a38f6b960145185dd690b8493eeb55f859d2232ca1fbaf02e939b78151cfe75b59f99af701d8090c4f96743f581de28b9bb821cea6708573663111f34365203ba7bb08b2094c08d93f664d2c735778f384d51a87787f17ed27ea8bf6fa4d26408d03050705c7444ab7f1ecd2b5358aa70770c53f1dd90089b0aa442e0e6cc01045b12c2b07397211ec466f29a556dcb75623a2982c57d5925ebd9a69f4dde7b4842f26d30db278f6c4bf53699a75aaae3663e6ba25ab2c5bdb3179018311d128818ffac3078a394e13e5328a3c6e517adbfbbfff80bc4b9455dd86f6ff59cc5713f0f3bc042c6d4b02a0263e296b2877c1808a7ee2635f762c07019c490618dbdeef741105ae62e6fb6ece1b60518eb1d66ed80bbc085ac0f354a74705b3fa325dec07b907d6d4caded00e9aae8aca23f722fc6070120711f9e20b97ea5aa8d2d2f119b9054a710b6432a940f208ea1cdb44690246cef0516d811ad544afbbc4e0b067160d2f814b5e6ee49492142c6fce17e855f256982f919cc492b50fe57349c156db0baac808c51f0c9b54be9978eae208506fdf04f01233e8af1074f7becae18a620c7a8065d9b9648c28186247c0371df92c8c5a5e5f6b19d80016864bfd61efb608af98f7a7eae861aa6d7212f2586dffc8443f4103a9f0d8ad2f8dcda496a5690275b6759e25f8b41d5f5c5112007860cbe40f5608a507aa86ed8a1078224e34041c974ca360398fadb5d4b0581e1af384e6fd86c1a68815044a86ea731c2564078c17e1a257270d1c84157d1025bfc807a022e6b54ce9bead496ddced6debf00177708c1e6fcb221442c417c27bf8e7cb3ffdbd48e0ae023695498bed036808091df16463fdf882bf8377ad04b4141de04dcdd242c932b6a0932116385a9275062fb5835a8c1e0d85d5da4772b7c73a43c6b540f099c0cfecfaa08d12c521b0043c51f707e8873c6701eb49b613601dd8dbc5b3ce3a9c5423747d840b2fcd190d4aaad085d3e99eaa28d37e1597b8e3e0414dddcc5da32a98839e68cc572745047655b5938b6993adf0f0b1d5f7c870e56b375a2be5185e7ed119e9e0c1e19703da6d7a4b36633220c311546e8952c71834ee7def771346f2f09bad8141cd4c02e40da9d005566d44a514dc8cebc4f70d9b410b6cc13830d2b2c3558f1dacec0256c1e492aa3a72026a08012658f29ee26e0c4605b4b768283b373336066f3f01062ab4cd91a941f84abf7f20680261b22893b0ffd524afaa9abf35cdc66a880a83c4b60c404a7e398ac61899b9210df8a4606d0dbdd3619cda46af803a03500428cdff39b4af0435cfeef4c21246b7e017810255b36d19bdaf09a500165dd702cf05d9a51e916a88f148206cccfc4a57337fc7ef3f3542381fdc6299a56d4a02a21dc849cbfa65c7dd2f79ae66f85bed57ea1baee35f5e3821faa5c6b6837a0afc686b2e3345dbe76a055c8adc1a40612fdb090759f0666cf9dd707405e08005fe933768e0eeec889f8d74596463cadee4f4be1ea919cad166953f9ccb8ca90ae4ae642f224e7b93a7221b44324cd31268c5e48aba977aad1149f543edfb490edeef8d06d1629d30fc4608019d29d71508422d0ca83e49aaff40428707e1670e4a4fac31834ebbd978f4f243ee7097b064ec400a486e5c897a0855980764e806923857cc350a97feec97b9108821d4b12e9f60f6e16e433b89c4bc59f33479039ae9390e27d395c014fb68d603acd915a0ddff2dc9a2d3e7021ce08d363efc032e9a83eea905b02b5cdc10e76645c8ce41cc99893351d707eda9f782d50d12087886d5ca78408ac81785a60f2bdc2e6079ce07ca4c1d921b6bee6fa1913b430511672278a08ee394d6f39eb13281a2b8fe6ecc6118b4fd3617827c911363130990fef13c790f6f82f3f0607e9abffa36918c7ac64dc4982788aff25ac85fea029e4c8f6c7600ccc74fd4da2b2840774327604582f5f80c0767a0caf6cd02660efd6a8927774bb9e423c3e6fc1994f921bc77d48b4cdef75c4cfd2c5bb2be2d0615c8917b0df429844f796b2c56b05e4c19be253cea6ed8e30495dcb65c78410f94b1e102e05945643e3c20a729e040f35388aa0c18dade36068436a9268e980def3dee9143b1616c035b591f762cb208239fcaae1165feb808bd9ae739458409e882a944f826ee5f18469a21357ccaebd0a935d25c954ae969890edce7dad40eb3a716a957a339944d8f5e99ebc3ad4eafd992e1759071f3c0307fb33a595400c08099b22cfc8f4133cb18d3f4743ea56bb989cf1b1d603970895ffd54817f0b04cb00b6589a6475a35d42316c9bceba6d64f76d242ec8951e900cf86141d70bca9d1558b29144724f97117571a7528a291322a2ac29ab4a4927631510b0c20473280fc992cc3b0593e8316118e3ba44975a082840fb7aa5d5528c372ea5430e7a391f6ca74fecda3dfd6f7d1755f66c1a5b91cb39b5f18f7d842cc02aabe206cc75ce799e3aa5cf603b5c226c4e139c84512221c35609e6b97a0e0a54792806f4b37740f15b3e185dcfc89a4a1849c272a3507f0044440dfbeaa4a46921d50822f69a3a6feb27c6f9f1dadefec8f967d92fe0031152b220199822320072ad0ce836b6d24ccef0af6db6a7a6b1a1f4fa2d1350207a50d84db55688a4e4f0b50ae35035298490fe503f3ca00b04feed7f3bf008f4e49eea7078cb81fd9c4e6201dfb2713f2f57913d36fa6f2a95fa2ac072432383135c62ae31d97e3ff0352407504cab906bb799179b5af1809f82904b5531a02c5b5f7a02cfcc1a07ad9509015d29949c67d4588999ce67c403aeb48b289157486e83632571a737bcd9abe80298d8e140de8ffd0268d8cd2725cdf2ee78d854869b67758609a59e544e35790f036a3b1ad729a0372bdf1d6d001073f9cc702bc9d009e4fe66f9b61389ce920b6d3ca0bcf2569b98f0171bcaa81f6114e9600a5c59e808c5b8ace5c750fa610a56fd72c8304607a2a9fc664cc1a63a49f26196222286258661edc07af5ec5c0461497fa3216e66589607eff192818d6fb27616ffc69f91b4b57b46d37564e305d6d39875648d9537064dc19af6ff1fab0df15a898efed71fddf35dd853621d03b198a832f91eaf8967af31de0e3b04c5057ad3e33ab68f7bc80358971602290718825ce1dd00a6d71f6557bab44e983ae84728f2c817cb269e486c061043de08f31a676e5a20d53dd661568f75442cf4c9ad103467a3004f2938155efb883b03e9e5d96539468dfd90bd0364179f101f338f49d2455eb7c10830fa65d28f5007404f56e864763a6217e54b5b8e915a84026f83c596be8c6d03cc4f9790c54d02e98f44e7e069679157101c0cd7f4898e7497ed566f95b9d9373a5ce3e0535d031e4d733c5cac601da4b6ce79d092eacddc7814dd10e8cccd2f18f2c0f3ec2b066ed3f2b39659ea8b3a5d948b01b506b618befb251e6f2b664892629b235aa60d6dc9bb820018ec47a97b5766ccc4276bba11aa66cb1b77d014916557d43d1507cd61e671d7034943b5b8dba7543645cf8dc6df3268625c312b70d42303d137019be8d84be1bc63af870ce559417eddcfe146854bf2d771508d2399534629100a648f9879e698ae4ae238b24e30b749384b2510b96d3bcd5abc00dcd39d885001e010f8d9ab5cc12c1a1e90dfb6c93c76e799e10f340cc907a053bfc30465020905ad33ca6b9680d5ea7592808b8a47f7d0554347cd0cfb4a2dd10d9916853202ca75b6d3a8d6eeda543bbf14ba9b71c7c0193f3d7b615aff09fba7fc4c21e30a1bdb20ff5348185c7d153af260a7e9ab1fa61f44078c0a0d02a28e58e6b91309779a1eaa221a031182c59af732ec9409a760b525ff4677829a80fb761eae8d0ef70e0aa96c6329814b8ed3c42ae6c027406bd0bf4ef1a3ab5ab52fc1d98695000292597d4f4d4dc054a0ca200c03f38fc4163d1ca9b442e707411c12907d9f0effb4ce25fbfb2253fe45a575d9a2ee41e3427a84dce1c32a50cde3d2a853ff0071f0314b2916526c034e23452b64123f11199b771ee2b1af0e486c083a801f0096a0dc342c13f29c1a53af8b4d732f18e1c4adfe04c83c3694023b0a0949ef0b35ca14f33d67090c0c01d516110f26151ee2fd405335ccfd8dfb8561507dc3000717094149c49aa7e28be60c99b5f7d9a66a09a91d0098c081ff825a28c270048db1c81ace94678cb22e908bc69451b77aeedeb2f9890571049fdd39d9ad280eb63751f0b4963666afb016c990707252900715688790c642026fca2f086b6e0573e4c42cd654a9c8f606cfd80d9199301b8440644db32a411f9c9ace3b55e1084b65af44e1ae299f808d0c21453e99491b439bf5759b10723ff74d22ec9680063ce4e984a7b2f630ca95303dfff9729ae09113af350c36ad2aa53e3ad5641908972949bddbf204e3138c42dff2a4fc32d16b54a0831fc5241c89be25d8efda0c069884d2f1fe5f5690e65e111a713e4ae6340f4d7387c3975bf7f09318f6b10cb1b77ab274697410c3b4ea5ced839a0c642cd89e0d0a9da6bb1a958a93d5d70951363f4555cc34e2645279ba3e20352a0d87ef386279649ea44dd2cd9d758c0a8d8de625cd4b227c4bc29fb6b0b36b186f375c4ca65af35ff85c3039501a050e85c72fdbe25dd3bdc67ffbddfcb3b83c4231d6f1781dae5fe365b2062ef4330850063729202e507f2680f2a0019d06125d24bd0c728ef8b73f36858892c53101926f6c1c068a356be4d67a73131d6e4ad7c34db092a873f6adc20bec57e308012a22b6734f091ab85850990e75148eb913cb2a2d14cf843bb3b5705cc431de0ed9fbe3e1cd15a57a22a63fbd69e3e380b8c00bfe3ba89a4838123b2bff9b7701f2e8a65ab158a7d37ef52fdafe2cf69afc13ff79694fe320b6f81642f20d8108b7e54ea1da597e65539f39b505f67b053e701ae6f978e839030df1e0e4da4108163ac5425ef2ca7d15f8d65aaca655f35465561c3078b27e6b401a9841e79c05700d65fe2d4af9ac60b44940cd193c79e115a583dbd2cb48bed93d9ec859f10f742c06317eb859e5f3f0ea5ddada506232216d1857f94c0e88b952ed47a6910430ab3d73a770c5331fbf93950d202fac53ee7be8eecd6ec3eb1ce7ca60321c0e21dca91a3f95e7626bbe8cc99bb6f32e00a5c4cb021108f0665dcddf6e51e70de5d6773c5653196e53065c0a55db5150db641795eba6f4dc6a75d9e262646803f3b42dfc878ab46ca75c5c972240a55e09a5213bd818b002125b2187379620002625592b59e844c46cbe065caea8435306cdec8fd75a06297555df76bd22bd09bf86cf07f53e7d65b63dfdde8f850e704a96e19e6eb8bbd883070f00a5f1af03e2cba2b7d4ec4b79b00c26fd1ade75cae58db490a969873fe04fd8495aa6820b34d48a59d16d0b62c6542109b5d0f028febeaaff7be514b2a11bc9295eefa200fc1eb14982cf794bc8356f140925c3ccd6d56a3df7aed7abd5990a2b448fd40f35c8f88073099f8a4bacd22a4b6842ea6c1ce5009a16b66e0ca62532b5ca0f09f8250cc4ec1c0d17cfab374af2641252df977b6266aebe6a1f8e4873ef20bb00a2115674c94d3ad83e39a7ecdfacc69b6f41f982926b83e45fcc6cc84713050a67a28576ee08599431bd7bed74a451ff793aff50ff17338405f3d8d114521806a28c8b27c64871b1baa08cea4a7386d36d3a8d27e21e9ae541a1becb55504203bbbf53a14c550d5842a7d83c304493e9d2a194b4bfd25996761b634f669a120e0155fe83c0f9c5abcd68c235cbe6d7a1142c9aac98c87fd6a0beceb5e4e39c095ea2784e03cea3ab9d0349f33f07aec785838cd41fe6d9979a3b36b02b1b1406892939af622a34a2ef570ebb7a4b2bd8289e9560592ed2fe324e844a5be1190c12b6cddafe21b50f2fbe21c31cfaac26675c5d1a1cf76baf9feec4f1f1ce0708b98c321d6e881024ff6afec0f6a3251167e91817d904fad589039dd8db14be0817986f8191143abca5dffdd49f09a4cd9e9d5455c23a47d35f84d0569b379e09580f0346aa8d8837e7aed4f0fec7cf67b80fd3f5f2e2df48125ca6b98c518101b5d8d187ab91bbbd1accea50ba1bb92de1665325dde6d7ff89165d882ccc78025dbcfbb5795d83b797899206ff8ee04821ce86cbbf53077fc1441f9184ab440925ba058387019eb286594c2a38152548e027e8c2ee9109da30c60d8b2c42a60d961e09657811c6df334d66aef12fc5cf88e273569cf44cb9406258496044e808af22dd00a1561b4ef75343dd2052735ca8d4a8c81c27447cb0be59447da28108ce08d22221e90162c875dec610c5710b40375928af94d7d59296754f52050107d0ba13af9e404b0b055a76149e9cba9d2826c24a5ea34886b015abbfed3fe603fdc0002c6a2df1f274f0766ff9dc5f5579aa59290948163146ee04855285f90d24e07459daa71a6b1ecf7b1be2147696655f18a23e7c70bab4b8c63368aef10d37459811086cfbd940f11fa2f140847148851eb77c08c295118c490d8f94c30a08880489a007c1ddf1c37aa56f23d0f80bf2b1d012f4d6fa0eded13178240503753627914817389eb02480e1bea1997ad0e0684f1a319b0e3760e91b392ea5041679da09728a98ecd59742fef00d6cdb70ea08a2a2535d35c092937445551f09ea87b4410d08ef6a2c9441c8b32e410edd3a9e2317ab32e8d3b6a2374e00420f64c01ec37ec2ecdd5aa92b48628c11dd30209092d9f9a26c4d42a4e414b34f0f0005a349f8c7b6946313ec69ba63bae3c2013442c64d27b31c4763eed7f24709a6103a39fa57d94446263735995cadfb4275d3ba4f069b414dae108a4ac8860ce01251b6569e3a005c2fbd2973bba835869a4677624e5df55da1d991c0edc50a1594d733958ffe22a8a35a5c9230ac051b7267873f62e2ff056103c2730bf50821a36d8cb15978ec745a936a357326e66b324db4815cf3e775837ea458860109b0f07beb8a52512b6c3d2c2983956f808a1f3eb161b1674f48e27f97ebdef60f2aaeba48671e01b83808096d3d3cf5c8ad96dab79090b2a55a219d868fa45c05d8ed0b962a61af84132aa99cb9fd028149d458c0426fdc8da30147c9e39c1002a02730a489224bf62e20d431dd92350c1fa9df17b21588cd3e1eb7b238e07c0d9b9c834008a434112d33db8b1bf23c0920dc450c10b9f219dd42ca3541025406f62b3fe1e5a7bbb24d97febf1241311300631ff4a31c33871c0063d37339780e8669563ebfeb03027c736ef3e4140b3c3c86bf3f7228586e71f43612200f090875abcbba5cee99ee79d3ac7a68c91393e1e63eba4bd2f33846b21263a84bfc0f8f7da3a5adb861e4b0e4642bc14c269eed41312cfab11a8bb449d84e81cae90199e589e375e8732dae771246923a64e92baffe199abed788577f4fd525c7e40aab1fb215d64fbc7e4c3c4f01ea4376de6e31f6a8a010cc658420d009d36e55089e7f0d30a81d737cdca49a0a1008ff69fde6ffd1cf2e611859ca1ef81819ae0ebc557327f33e38f2750b6d66ffcde96ae11e625dd3b3380a8dc199f68a063c0380e8cf746b154b0bb32e13746498492cfea4f64e08225f1b48fcd49bb570be02ddc692fbc8525e38ffdf80ee1cd3e206349542e04597273f7263816959b7970e31eb189c37c67dbf4b840b984497102c88d0400de78ed3a39b52aaada390d20bed22fed65744eafaf541661470a0be697eda136be7ddec44bde772810e147f0791b2096138a56c0db00acf93054f91ac6bc0e3657e203410ccaf656cec06480820791ba583c3c89fe6ed44d39cab0fcb871ac47007b942bd974ab5b80e8f9304350f5cdc608bcd4e9f65c6f8ca48bdd3d17eb196971729627183f4a485b872032adb03ef61807b494560353a65b3091d41baa37955cc7cd4756505c38f4671085a84845566bc7b1d19ea1c23a08d9ff0f2db5cc6d6858e9e1e969075dc941106f3027c7e26cc38c8b8df447d6bd209ec9cbf963b2b60e2030440d0801c4e110358f7c55801369b23f8e4559af84ccc9d87aa14e9f00a1fe65ab1c85d0c070e05b2f2d7f553e032af8ad700937d5830f650880c51033c2c1a8b89e0659f2f3c0a7b937bda0acd91d2c1cfa3ec862bc2bd49666464a4b71b86d0321daa0139be093bd15251aad8f6af5e026abe6a3063f7fdf76453429265d136fa0eb62acff00a08fa64f9e63377f34644e8ff27db06357cd0e5fa31b3d1da7f02091abefeee075e0850908296d9daf74f360c9af16474572c38d928aeb7e7e3b1d299a0ec570fc63989ca6ce05c42df06e6409e37c3fa263ce92d361ea315940dee71267d9b0a09bb21390b2e37d7fc0e2ff5b93f3dc93997cf0dbafd27bbe0a999a56bd71906e618f93df4f53e309147b58b3978a1b7af931d884cc2df5fe18a2f5f6256640e2498eeed0874cd59217c7a5a2e2572006a2c3eafb08da01f31291ab69cd4b80a0d46f6cbb5b05f15bbeb931567e3c1d47f57902cbbdd9708cd8f9311e6fec00f266989a39d73ed9450205aeb878c05af1314c8d062060355d241af56145c590fa24335ee26b5b010b3494b93a37bee088c0185bc1751c6e01f4ca5d233fc2c056a08ce6cc681323982281d32804b50f510191c6147517775135c118415e4d40463f5bfb4f36152d050f908fabf5d714441d08474f5facb7432972fefa599ca0496f314d1737d48dbbba0ea092930261298e51f98dd2bf2eb0c3c2e9f84f0c308197a7ea2738129dd2773d1309e3871b01f9bec883508fb69ce6fe5a162fcb80ce851e11aca3f4cc918367e40a10bc1bab765c4f8872eb60f34ff5d282fe00f0bfd5d3e5168ae90fb4200851850d5a88aaf650bb3c0f356490c7c8cb5dcdc18026645d891455c98be8b0abe0067d0f1e9aaa6930fc8e3c3155fd0d8748c79170b5cf8e73c8ef4bd03851a485ee0e8c5e4ddaf24555673a06debc8c1a47818520b088becbd7099212c1c4ff64382c63d9ed1f2cf25f3ffeb7f31b9bd589e7010024e3f99eb151c294198dc98f04d08596da3460659f82ba21369a62c0304371102e903fd4bd2d78d06f97564307566642b50147fe28934cf197ecc8400bc4eac0065f1d2e53672e229ec0bbdd45e26e3598c523f4c1e7255329fa501e64d70580ef7efac180f95770585486b8c620cf724edd86d367519f7273deda6efb33ddb071df3cee79d69811fd4f8075bc03f8619b21c276fce3bfc45a92e0baef02e240b8be54dddd9d8ca16a49d7894823b6b22e61a0194210c5bf5f643e946421c40067c2c3614a2e7d6abaf14de09f720a8ed5e038eafffb178910f66f2a275588e0a1df2abc2014d2db73259eaf18ac722d94ddde567805370d1f860baf274fc9d0e2b0763d44270c11f5873e9f8109a4f83eb83b2e4e7681923e8f30467c6ca9803fdc5905d54b66383e3cfc1b7c5e7de5db9985eb335c997fe2e920da250b7af00fc82f3ec771a90a6cb9762e7421e39632c8beeb223a46a9391b34f1214c1050bb377874ed8e6a08b0634da87ec1fa1afc76460111bb7b607dcca77b3e7d9fe0d791be4d72409292ff9bad34e67cb21cf5d6ba6b220d77d768546b977f4c02800c502fff761a4384d1fda556dbcf16ed0fce9213eb1ae4ecc11cb50a16f60db09f0705eebe70056716413da666920d7ab0fdde72f90fa49774449d4c2701c9e054573968cc5719a63bc4b7ad50b7ca9efd6b1d3b19a1f135c6128aa42c6a0230f1f398b45b88f69c9c148c8bfd072dade1a7789fe25f64a4525b4e6d75761c50be2e8f0ad01d966ac7599b409a101c0959f22870db5f8d330cf2fdec26b34be0ea54801cc0d685f89904924cba6af5cbd7bf2c5865caa6c893886f3f1d6d8200619fb7b86e762f94d35081e6512252f3055216bba4460b96fba0e75ece44bba0705f5b413a6b88a2fde10630675aa49cb926b48ad1ac79452937f49cece871c050b39d3a54fe0a8504984e68f751524120b3aded08ea867d8faac8c688e6f880a5accad604206897b8a8b067e15a41aa053aae606ca5963ffa6e538093ad8280913e56105e7c11e9a3335033fd03472d76f50082cdabe05fae23b1c8a5196a10b8ec387b1dc87902010f25caf6e0af83fe5299396c85c866f436b5fd9cf20390bfc10c7a0056bb78d6537b918a826d2f8b80520bb8f61d3186fa6b57685e2a2061f1e2dd90687c2317a5fdbc1c5d88c99eeeb4d03ff66096803ca14669e2bcf03ccb4722719db4928c9fbe97298999a21abb195cbc6fa331c851faab9b6f84c029dc9702d89dfeb143bc11c80a89929f10d4dab8c7fe04bce0defa51c3f815306c75497a6ac01116e1110d500a855243a8c932625969aa74f504571619e8f7f00e5269170c817c605aadd7ea60dcf5bd2bda783e7bc763832d0b158bfa4ad9c057cdcd027d39817ea1d9451f7e35a469b5808c909423818656e6e49ab7df8590cbf6a39883e365e33cee6f69f9d7389459a1a27e9a959b534ee5d9734d03bb30d9300842d31a83f480f481927f9aac0b45addc03eaec44043bc3ec45d19f5e3043cac7f300058406bc9acbace495c8682c7bdb74c7bdf07359f84c378bd21b905a08d7507cb76385a0c8d15926e3cb29cfac7d02cb52e2ecb0681c960f6de3c0ae985f5e5447f204d3d8a1b53e607029dace2bc74c42deba777a34239b99c080a61648582e6e068b153542c3cd5af2f5adbcf29dfea5b16e28a1d6be47cc82b0c616acbb4c79b149df5c052d80e4a7ac4add12a48a22edcccebe2657cca5a2f000ac7300f55ca56bdede30cb3283756a0c11c01a607d9c762f5826862beb83b039366c5504e2c70ae0f5b18c1b27e49033b0f30dcbeaedf2afd6e9425f90b46065dfcc0f351fc6064406514ffc2b3a93174d80ac67161531a4b39538e6e2fbb0e0d1e2049d8d11d58f0893a8a913857baa7afe9dfa0c4c615439e654283b37d01aa0cc42f90b06903e4fc4e18ad8fd003b19709e5fbd8f643a86b8d083d4e07045002fb6a8aa4f414ac973ef3741fcc28437a6ad79605beef16bcec0f9807580b77566ee36360e7e3ddbf7163435bb1e98d2ada225777f05ca84226f770fffd042b12e0ae3594fc3e8ed3a290322c223f553d334211c0f381d43836752042480f8926171845044a4abd09e895bfe3ad2c9d01574ea3d008a228ff827c5cf25e017e8b969f07b04d0eee2edd6f2e80d5a7395021156fcfc7227d4f4ea224ef05089dcb07f8e0ef02136cfacafad31cdb783dc31502809b9486963a1bb24eb7e60c2d43b048080affa018b7913a847432318fe68e6247942975f7ced72039e860043bcfc9a2bf30b3214b1240ea03959071729984df735129eb9b1d16c956e30e0a34098c4fdc8c22817843e2c08af864a261319877e35f06b54542c4d80ada710fee11d3030ba1df9d61c572b58154ae658a052ff4cfb7ac3bc5939d4f27deeb053417eb3c93927f8fd7dc5e20961a09a5d544c10efc076e5a25507406bd29ed0f5963fba069d4dadd406b143e8b361f535ea3697aeed4b8973c63834b50293e0340e2bc94f77eb38647ec0ca02122dab1a3205a538b12bc5911448e9e4da9950fb3457dd06d06b3c284e3ea6dba3f183d2be52d9263f1103fe009613637930307dff5b15208a7cb76aabd1434210cba0e734ef02a3f8ce36764f593acf23ff50cf237540a827b14744f3482cae9ff2928a5de660920655fe42c87e9e22631d10f5e16190481dce7cb7599481c27d9b1ce20046b99e3697992f12e599695d9bb0c8149a988e7c93eaa91fb3228b151cd26cf1dc7667817364c53d8460d307a930e028cd29407453b8ae839f0989171dcf70ff519e5e9e4fa15e6ea3b6b0b41dc07d45cc09a08a09da72561f891e79492130f00b2d3faf1d2e5f220f1dec531800100d1d82016385933fe98190f32c81422ed36a26391f966f10fcaf91f762cc8047f9ce379c2f2962c9d1a38308d6e10c95d540699b1310bc041d1f888b4229e02c19c760b77b37d9985459eeecb12273a423ae0d5605c39a7abb0ce71841eca08a006b9579c1c08d2a29bf8f2bd14f51abbe0e8ac9e8755951377171b13164c091998712b360b8742d0102e67372fa53f9da9220e09c8881f9926fbf7e6cba608235130abb062e209a5891733e8202e8058f78370bca5ced0e4aa0185b63edb03b54c372a90f21f75613585a4fcc20f08e2c00aa5ff0da55dc4955c5e7361de0aa561488997704865724a8581a010a9ffe74560c24880b2b4a0c8996920c29d0b4460cbe6da64df6c538eb828f16bb3f82438ee3b594a9cf4bbfe13e5bd297d0c7cc4920319ccb0efbe3afceaed02787515d82d99ef73093aff2079e8db8ed20f75fa33dc24a823c6665a249d4dc661516a4128fdad663f0ee72d092bd41a1b0ba5ee01abe290f1d86ede37ceb70933d790ff53669f9b4e5117ae03172cca9b06afcca3a72786e5d5b19851935ae54e5659540adbdf7c9c0a9d38952cd9f5f10da48a4b0930a5dc83887af02f56438334a77c8db3a683358a558e715c0e56c402c78a7d09a78e765033a9d42e5f20ef551d980fc7770ec0643cbb7762d9504505fa41eeae028b235fc636d46041d772d2c180df0ced81559a8bcfac842999850f203aacd834622c2260ecd19d825951ef1ee1aa34a2a31f4ec23287de0eb74104efbc3ed8ca4c5033f0d95f427ef3a483417edff1fcbb29271d0edcf154765f0fec9bfe606453d3c77b23905cfab13cb8044b2776c4c5d13bc73c60b025b48b047f35fca842ae3d3316ae2a0aa61eca434fc79507124081505404b8b32ad4070f635717d289cef521b4d6e1a0e3801c3ad12c5204776cd4f9ede04f45265bc5063c7bb74da601392c99199097c21dd43a1c39fdf9966f65ccca7f3e866981060468f423d4896043bdf4dbe69e656dec113e86a0af6dd8acf8030482c527d3d501f430b0e1cce73c91a626a5209b74e79a508d8836870fc85c28e4e4dae58a5900f3fb4b28223aef935f783b2aa1be5670cfed32dfcfbec9fe4956ee9a5742710164340b658a2d51760a3f6b992ac0d611566ff537fff9c85568432298abe6900001adf3372e0bc5c8adf10877c652ddf6c0c7b3b7e1a55330cc61990101868b0819b60ae89ce1ca35d0f91c88737b29cff8cfff5fefd7fbf6760aa66c0373a704d9ee152dd3ac8a45b1feef865f60269d71fdcdb5e64312bad7ed57f09cb9fa029319eb67a1860dcc6dc9293f649030bc0b72723b6719e1d8ee1a16ba8bbcae013e6b209056ec2c0d71e72a29679f2e09b0e9d96c5a73c113304fb2041c15af0a719c570209fc35b1cd2e4045395cf71126068f850b2ff54bfed7d7df10537c0beea4a69b798139b9d7091da9735b8a026bb0ca2d75cc2a2417ffd14ac3ba430e628666c91d88d920cf5e1d672cc732c8e0180fc0653bd48d344e67abbf56e8044f1ebc12e05cc48dfb0a0614eec0fb4a4369fe1f764a28979e43c6c0bcb1a30b654861cd62197b0035a40da110f4239b07339d06d8b9cf3d918ec7fd905a7708173d5fa3addcfad525ea1301e08e8a2e4e3ab5c84b33a8a9bc861443eab6ba00596719ff66e957915f0fdde36c96ca0127ae10468de11ed30e0d9b3245626404aa958e3214f8c2a577ae9a66a4156d0180e1c955b36f9d32e046cb9d33553803215e93ceac4afe88d91caef2efb0768bf3cefc109c1e9a12073d22d46e2e520336ded892066f76566383550f04089f2a3fc23594bcb903b580aff57b8fa1d104241b3e18b2441eccbf09710c15f59f41c1e606622ebd803560cc2f0b22a5e80d8322bda71d6d5562dec57f57961e7ba2186f39812f391e08fd21fe6115836f07722a88e2d2f06d16ccaa65f1d9406711d6e8f9cf1f9cf28e9b65e3eda094a50c96a9dfef4dbd0ef94cb5327694d071b17a17fdd86f81c55a25e2268f3af4670a32e8873e35c7320aaa8b793f060011fc8e71dd3594f106f340d86f3990d2b00b3d016f8778025b2f2c241507179ed165b8bce6001157368a7e5ea50f7475e60e91f4cb2b41d23fecf2835993295f2c0e871be5d8dddf7cb2552c61afade985055d95a8e6dc6f5e69ed49c9b05f4a0cf54e969c9b10cca60da19158f5812c6c04cead56dfee889193ab86e803dac1c5ef2e187c6edb3dc88ab775c1e422ddd3090fb7933dbe284baeb1de3f10e999a9a745cce17da2d7c0a360bc69564f92860a6fce947c3d284b6d4139fe0c7bfaa26f75492e25d7a132154719e5319e6a0107bc81424c0c2d0590ec5e2332c7168a1b260ef2a95f2820148541c343d52cf809638581ce07c9f12efaceec80548043fd7d47f4d25e57dbf80ba55054b6890201fbb4ec8d061ba553d9b099999875a6fdce15af3335db0651c98c30e68295de03c31d8dfc059e94a24ee022951981a11ab491d99f01d2fbbfc21e5ea2babd290ebd0107cc4d770dac328077e71f7aab62001184301dd86ba403e0dd66a9fdbf07509b6600fc7a32cb6f36013b77987599b0adbdd507786c55729b284a83bf4d027bbcf3ca4315634ea40863ba60439a0933440178f052288610b22a73220ce9012ef08b538b46fb24f06c7895ca84901e14bf0797494b3ca0c3363b078ae9960b0ec81b0bc89762e50acd775cbc1f27f5f1199792b937287d5ee8d2e33bd1e6003969b29a36333b16931c5c409258833440e662301533c678b4f2bdb63345070b0512dff61ab32e0957d69a87a5d2eac76d2d045040a73db8bd1515122fe4d601611014aaf1235d5611371d9e8a4e5200c7806f591c48168fc4a86d37adac2809ce0bbcb537a5b469e131e79a08f8ef9cf8348b11ee2ce2e938d76f1bbc59220d459d4bf756ed9867fa30fbcd369c9aabe2e99482d39b340823fa7ea24a63470379a61e196366c5cac51d8b1bbab1a93b0804edca1879a7a3631d6e110597e70756b7e2ea006e4a17542bf1593fda7dd0cd84eebcc497425f355c217cc222ce015f29bd08cc1d5cfa95481bf2029320ac9fb99c2e5334eba5f1699115c28a89066f5307f2735c48604281d0df91a43144baba41ffe1d6660cb24f6373ed1afa0b928892a95b235b78d4eebdba762b96c688436216fb48e5e34eba10281254c20dd37cf56aa24f97fe7c55bed85714e5a0307018262885191f8481d5a0bf8abc0d92978c85a8587e855e86bd5a92b8b96ee44add6754f181fe1bf129109cb283018c34846fc01c2b681befeec173e0ebbbbd9262ab19a9c1395a8a363018b47a0799edbc9472040266ffe8af19c1e0402c290eb718a86ef0387e397c945f44e800935b4137144d742899d3111bbcb06b548a8b41425474148da36d9ce21feaac0abf7260624331a4b6d97cc26cf652b9c51f94f23d933ad8acf2854b579bb6ef00dd709624c761ea2d706fd817faf74c88058ba08e568741b599937d14c2183107bbb012884340ab18c6a17ecacea91826ec7a32f282912126eb0e78c468723f0c4525a6749d3618908fe941203ed085122563bd69b49e10f4c390469802a3240a14485da825b22e44d685ee6e3fe170c554b8cf2960101b6be98b3534fa00c4038839667928405113ea086fff8818f0bf9a45ab0ef3b7dab8f8bab618af7df704d19f569168ef56e22a0e203d9e693a65c28e0e5445dcc90f4691e397ffceb30ef2e3c57bcc069902f708f2e9980c2a06c7bec2c0153785864288010f539bff0cd801ca46e1d153b2474b4a92815d32eaa47c3d07d359b1d71bd2e1b5459dea08fd3c78385cf7abbad28030e9b2673636a108ba2ac1e98ab85d1632292399d80fdb0b60208bded3a97ca63fff65cf13d6d482f0d8cef1cfb588d677664cafee064ab4a739c5cf99cb51bf7fe4e0d4fc36670605b2c59dacf9f5164a66ac119c0f4038ba38ea06b8843c3040a3a2d9e836b4eac9929d60ca82ff81f339f80b7a0aa81dc77eca85b503203956106c545c062f3634d017f3860e870352aa0ca03f046e1f9d609a155633bc25f9515212a3f4349cc9da21b8b2fc3a4c57537c30800b4bb5c597ed3d7530dc29b6381627ccb0e8145b8391d277800b5ee5386fd8ab092b6d5098bed8341a7f062d58e5381e610792828ebb3973b00c7b941361ab2b0759a21dcb35c31aa15833bdbb3abdb6e1a24a28c0b78a42cc2fe113665130f3028210f60c294910a3711d3bf5f060ce1ef07b9947f9ed243ba94f4f97414c060f5a805c1ed5fff948823615638384054bd322fb128967e8e77ac1e8e0b589e908b086434b474c628656cdaa2fff803d50f688eb4f2de41da0679401d81253a1028e1b826083b0cfaf4bb527d38051e4214dd492d3fcd93c0296aea777c0d3690efebe7ee1104a6ca67e109b35d21ddd615a4e4f4db02ba694d254158413be0605b57dc9d2157741fb603ae5839c1f4f67fb4bac8575dbee59287310d625462f049c5b5cc5e9c9f762cd20972bc6c8991fbfe5a21ef183801f53f1c2802e752e022716d7a4bfe64eb808f582276c1cce06377b5e7b61688b896bc66882bada5d0824727ea47c3a45f1666bbb7913b15d8a92e49303d12c9e811b8d1f895bddbf04ec2d3b87fb2e70cb87a0431d5d7a887116c45968fb79a870dfe3e297a00439063dac09f303f07bcb73665552a4342490cb60c30aff178319406cb9d97de7aa0c3e5e5254171a11e813bdf5494e13e65d2c08d81371815b9abf1f31031c185f0717874be10ed6e8735c298f0f1696608168247fd2c070bdc670019880ecfc7803e45303ec93afc4941e2a6a90cdbc9b463114eb5f4eb9ef3b4c24909eb96b510dba51f59eca3351844cb30be921d1ee3e2520418322e68751c7e6aa7c9cb8ea0e3e551da1008be9ff608b80a3e7ca60512a03dbc46946f29fc83be4c681f7200a9b80e6721aaa118a2810fc9459114ee073eaf6601d3d88b40806b9ba4edfca02f63c8e69182b0feaab2f55dfec2847e0dceb5e8c52802c43edc34824190a6c0417f52008748cbf0c252a7436d62eb974bd2b0a33a15456373d7c743b952a680e23f3897f22904cef840efcbe6abe093b900ebaac721194b20ba3666eeaeeb8084cdb8b2c180d7d0a6a9191a2a3eae9fc1eb8d386caea56277c1eefc8cdee7605efe28cd8d32d53c4a98f6459979683fac109a73a52c878a029775bca072fff0e2840b88533ad7b08ce0ccf3676673d794d76f16cfab5346634f0b6548e32c5051bae29a08c2a0bc46dd7232dd60b69bfa66ddb1afa271371ba83ce725ee5700de1f66da0234dd99b8ca21b7760734910879051f46bbe2ac46d4afad0150c360470938abf0eecb5cd0a0619c8bd65b47594d6de315671a26df314fc7f10a7cb04939c89117a4815f2fa4373793462a8c71607044e567bfee8b619816cd204d80d72e9155da7a82671400d1a2cbe26bf87756951dc57a893aff7a1569951c7c90f4971a8e5231e2ff5a28b9368ec4f0bab8d270c6f00f8896deca5347b91fe4a075a7af8387414cd330967b59238e872b218f6de2a0329d8fcb38e5005554fd40b211a91870557d6d647cf244344f4878ea1fd4f5d56b21f9bd2616e83bf587b08d3c868014c78c5be6c4070b28a864966311976b4f6705787861f53e5379f840d1d96ed4b7b2aea312b1f34214ac5663a8f243f288152023c85ddcda052660f0b3bdd3e48a69e7703d53d579fc5225e7ea0ab9c716ff8a693494ee85e46ba9b0a2383b2dcb87aebd42f300971e57c03c963b20f8b59c0ce68dca2892da8d8780272253bb9f3960a1d18e10a5cc272deeb2458e7fa3293cb4356e8559e2cf64d023441b0a116a4b3822b086bc560e1f4670ab2993ba57aa4b2db1a55bcd25fd40e34f4661cba161df71ac414fb46e3f9ec4660bf11d207afea16eedf9cf9ab4702da879313767d399577c103c1559452e8087690df929694b1b417d4a5b367e70852d731399b7765897567418f9f52cc41ab6af07487c0538a5a080a81d9bbe80b089f98c52bfc59ffc3977c0550a832d670b695c5a7eaf880b6f951568365e508889cae5e25da80836ba5da46112b7bc90f23ae0be21ad84c303e19c450d54c081fa3b220460418281559d0eb7c6b6ca0129bdd16b2299cac74c27ad4f02f7904a3d39e398903e19ccccbff8f07db4d0eaaca8568baa49c056bb6139b594b8c02f9e4972c86e5ac463e0ade613585c2e546c6c3ae9e293ef9124143c19c97aa030f93af932fccca44286143691d9d9f6e1b0c7f9ee73822104dd0e4251b7b400a0ab3bdae3a10605db30b88244fb252405767da5035ccf292cffcf09fdde12809e94b96f541aa6324260333c6914afc72ca1bff2a3dd5a057ffb20b94d008850e +generate_ring_signature c94de04cde5cb97d00fc58347c9d43c5e2ff19eea4318f3ce8eeede86e590181 dd0cd265cb990195247bcff884ae711d6d6e27a552da53c3871f9f1ef313119a 41 fa1a0c591987eca8d52794fdfcb34be618b911c96bf95dac8383b875200f160b 5df51ed1944ca217cb3aabdf05f9e84481cbfba603863402a3b0bc3032345285 aab3f16311418e85c428006e09f6ab4cb938193554381b480c2ef44782ab092d fa2d0b52bf1b930a1f0f482813498bd527ce090daa3f4285477aab43cd1c45c0 954e9aeee405d40869ae3c3ac2ffddae7c00e08574f8e28528d9efc994885f51 1ff131463c64c2c3a1b7af41435e737718ceb11fc26df0e48f192cc78b49606a 05defdd1653a375ee7b758c3d7bc997beff91c486ef9acd1f7407599ef9eb509 4db7cf256f884cbde45e0e580207029b4f5fbaa85ad7464b20e36100b70083a1 581797147df7936051e989bb4881f330344d5733220ab185292371438a9a7660 606177bdf0e2889440bc7c26fcbd0f17c5a204d1c20205dce16be9eb561361b1 94a10e3d908cac01a162fa1087d08145fb3db70cacf955bb70d51bd0c0cc2915 a40e979b15e9b750a010c4f924a6da214bd0a0356aec01f392e676c9f9d42a4b e4e6ba7de1cb51d81bf38ad8e9bc3ba7e3059b170566e6188e5dcdc197f424cc 0bb2355397a31022e06f6edbea574aa43c63a063f8287fe3de58a438193cddbc 1994a036f18f48016570cd7ce9c56458df51f532738e2fd45fc8e09dd53ef228 0ab9a2f03402f2b2d2d7e850d682cbdf1a82b34566059897b754188b09f84477 338c19bd0bcbabee027bddd1b819ad49d5c37fc77c7cb6d6ad64862e23ef632c e91daf7d75e20f04693b719c473fc61a15d3076e1e372068316e0ef056be00a3 f547d83a8f49db95529bfb04880da8d1856c39a35ae73070dc84b3694e49c82d a3f131eff367e0c26660772e1598e71a430daa4d7ff274e3c8da018a40b7f765 205875d30d48dbfd44277116bb0d80b478d1f8a0a83ab789ac0404fdbc03617a 92c1fe822b784d0dbfbb2ec60c8c8e9e9a797b807648554bb5105b951a3d9d6d ff72f514b8adfc2861faf09820604be178b7daf92202088a85d2d0e66bd57edb 223c5ca852fabf656ceb333b5bf01d7f1f37f2566b54b7c4c29a86f18d1b8ae8 14a1430cd78fd2039ac5d7df2e3069b24cb36b12f2576771da0c1911f49a273b 69df315a3694a8a96963810c498a765f95eb4f84478bcb38a8775797de768e86 e94d3267a6fbec3acddc3ef82a82bab6ee8b0fd4a7defb1332b936a5f1013fb8 ccf5a0bbf7e6addeed9cdfc3599a5a94e47c5faa02a3ba4671a4c008bd403f49 45f5cd60d7af60fcade4173bc0ed1a36091f206dc28e13844a16c675422ee1af b1907d4d139c71715303888acb530cf952f70e777b3801a0f402f2f0585d6930 ca8744c0d9e716417589570bf615e2f97e01a83992b36558af37d7ff28714523 fc201c54527f971e930f3d35bdc20ced6a666ac12d851398a7fa9f26ca509f53 6efe0cfea913636b65c218d9eb260c3b9ac83018e18623135af56184a418b565 e35ed29a37ee3eaf9d9c8133a0ffe4d0b7b4b0358d5889ac0b3dce4bf4bf12b7 4f336bedd6f2044ffb6866bf87bd50c3bc3f0d814b18be816f51beec2f29f1b0 eb40611822d44136ae6779a5586246a2ded4d005c83334cbeda1aed834b7f888 64b8c042003d59c3e9e1c4ae0d2ef6c1d151e02c4c5e7c36c7aa5b59b7a74357 e7ebbdc1ca40f25c085c1d573b4951e9d0e15cb0c0ad0026492cb6b815ec2595 2148ddd29c481bb590a19dc5091847a0a86d1a1f6cde6a8df6f594be8dea66c8 96c16cd72fb2150c2371c2313d1d24520bd660b248c5cf3ee368f95d7257cc0d 9cf1735d67ccbd6c29457094d09bbe4b18c3824e97168d9fad6c3b97a177f9c4 25cd26b4ae877a3fc7251903d65736fd36329f066ef5043d715032b1244cc803 0 c9ec6588ac6cc707a200c7d8d3706c74be5aced3efd7d2c124c9061fef31540681728c1c84e7ab3ccbd43e21aaacd4471b4cf605ed42e05919b8202323481d036d7343303dd58f09d36e2985707e0f41768613efc107e1ce3f911372b6dfe603921aebba2bb8c0cf6febd785b6ebfb1ed5a1452f2067ddff8bfabb38c9427b0db0b64ef0702e53c995fe7084407a681e692b689b43b900c6c7980ac934a5bc017f0fcea68b71c2f398911ad990faf998746ecb3a7139074a4edb72f0da158b06ac869e7dfbce2c0bbb59392dec43df6ca1005fe55070641cb4e2f9a098449c0d18db46278dd1dd0687d8c8df5e9b4c8f2b28cd304b32b60fbe144b9655ad1508391655bae6fbbc044aca2768605dd2fd29950eadb9d2595b0c85348eebf80001a975eb9cd8c1d7280d8eb4fff5a8583e85ec03b864211d518e038c28b04df70e08e3e5847c10a6c389ae0fac0bb738948c702df6643509541cbc6d72b0512705c026cbf1958a90f5a5c2afa868f4977aa0654af18efd20f6c23eba86a8cd3207bf8f599c70dcfa6c62822bc97a84f3e2c555027c444e63448f7e390bf9d9400023df81a5f7286047cf7ef6f599331360559011e6d223ebd7494e3f685bda7e03d527abda260db3e74c8d3dda5dec5f6fc86d0e06b689df5d5f33fa47b49ad40ab62897bd66b9dff75cdb66477a23b99e6e6fce13329a25e4f8963c5482efeb06f3f211a5ca7e3197c6959e56256444ca318b52b25547161a750b754663a0df073922158d2201a3c238fd558cad934d7dfb8b5841d2b08ceaf24f6d3f3f8d1d0235053c2dbb961661cd49ab3ea072c7d5323b73b9742d0a413296fafbc54f860fd6041859f72d9ac9335529aa0f120a55f56c41df4573ecc017c3f293a4219f09ad36ca72d7c9565730dbd4ec15c3493166bb8bd24ecd05ba50e19baae305c405688eb9c2b5ae213a998658aef5ecf98344d9ce29d1a4ba79a58aa118929c830b4a0331b6dd837dbb8820b33e3417e3479225dccdb72898e80b2a0b0f02a241097db0a9848553024adfd5a6ae0dded46db0bf8664e3119da60523b707481a830e60bbccdaf4fec18e87e5594c4e725d9c86b30f42d33a6871da9592c49284a10d39c97e208850b25043dcf5fb57c85e024e55361c163b0a0d31374ac92e40db08d8a149f7f7ecfde9b39afbbac78ef9a2f62015ce7e0f7c8d1dde4549fa896f0dadad0f8b2868fd06e7c5dc20fa1e8877eeab6f65a4c73edcce23921c989e0a029ba93869de4f2574823f1e1de909c5e1f773a2dbe5102223e61fa84ff70d120ce4a46a450a62eb338f2fad947ee40b66d3586c0fc9b760677cb0a64a6c128a074f5e5138f2cb639a12269f9326ac484334879a31d1f11fb7f7a6b6ee46c6a7053d2d30f871231942d5660c3d3f114406c683126b64918c19d5f90f951710f6024708e2f569ceaf166845ca952c093ce5b138ae6c2f33d4f307b22c156cdea00063404304e0bfb586a5d4b88f70a3a271e47de6c0455f330e0c7b94ac93d35c0309e9cda85387821c6ba956bd39b1c2721aee2ca0fc1251339f2f0a45d0e38c02faee3b54e3ccf5e6770560b80308395d10355dfc3caa120abe0004ae628c1e09c88f6e10d0431b1a3e90762d8f138ff7e54e97561a0f31ee9420a13f17cda1044efd7bfb956c812bc6ef656077557b7fea2d5d0d47bf18e2faa5fe3f393458096271979951f4307cc8a69d1f2eca3e0e386f159c9b43cd28729900c0646b5204f9959387c4d54bf4ae40b3bccfe312673530d2aac16ebbd4455f675bbd660403a6a8cb841afc7c01f28536762bc1c3bc57f0fde5ea248822a88034acf68c3f0ea42d352e5bcf51c5ef62c17bf0ade7321dee5de65a95c0161751c08b2444f00e9039b031eb277fbc08e45b8c232e87bc668b0493bdcf0d7b1498552c82fa6f050772eb6e20a111d6bf6caba0eac1958e084dd819396f08056e5141ea74cc2b08ffcbb6f7b2f19d8aba02fe0b09f6befacbd2308fb3fa91115975b6923c006e02dcbaf76b7a4c2efada4eb81d37c6a2be4b3f271eed710fd13e4170dcb9fab80af5f2bfe6cab56d5ed2528edea9256b34620c363f4ca9f064e4f937e26a801d08866b616762d65445b4d11be160928afae8559bff299222dc532486e8bc20a7008928b10428fa49b2b6d03a9914f9bf0021f649bfd62b32b4bbb8a85acf68390997440d34c1bc8b7a3bad0c145433ddfad62bb5dc5a79ec7990ba1ff36024a60988c05ac4cdc7830ec062934b4e4c60b1664006f1a82e75da8c87a8f92eebd10dceeca0df6af81a48656e888b98b5e18108e5d5d9424f448089dd4feb9a3b91025687f6584d810484620302c7cd27e1b17cfb3675de2f5977887307a0e8ff73021e663c816ce008cf979eedc9eefc8a252d577bd187c96883dcd50da771e09f02a4e9d6a7df61958926d317afdccf83f32d92155320d718f6b2f3230249ed73018308ca28a0a5d0c517ca50682310cca727252f82bb852c1fb79c883f4ceab20a2b3b8c3421b94125c16ba95a9e551e9e688a263023435b2c66db2446046a5404bad0e48ab943e93d754013ee4de57a38dee3a9777367dada4158ecbea6b4200431a81c33fee4bfef863a9335b086af8872e06dfc691c04e10cc15b1727344709e2b77db6f6ef04dd4aefaae96cf568b56dad2ef0ee91ba79a8800d8caacadd07f8ddb4c72519a34b5688eae4d6c99517489a0acd3094ff34466664664ab0060f92e76bcb678b52ba78309b89b5b27e082a02745f60adc650fa104b66fc2f5205b2b87f5cb966a73ac301dffa8c50723b433cf222bcdddf4c62bafee78c3cd30866216d56570464d76f2c45ad4d262363489887589cdf787dc66f405bc6a66c0b85c9dd5d3418d79ac8ba6e6ba49cc2a45807a4fbd78183cc3538b2a93ca9e50d964e451f9ff097c9ba6ecde12ba25d18ee0917fb5a337278d085fbfd17466f01aa5e0a018f804e6cd08e22835b48a11ab8d08a10dbae6a511f4e1c5ee7c87e015755356f9fac2f75cfea0012ff04fdfeb4853ff200d34ccdc7ee62ef3b39f50471ecbdcc0cbf1fcbf7233de10b141a0b4fa167932a22e7c94712ad0ca9b00f08f5cb26e11765c1075a0608afd6e10671dd944cad6eacf44834ed32bd1dc5e9039cdf55334debbf5af0b5d7716afe8885c4e447538a051e8f355c3768af32a50821cdb3dd16c6cf865e278883281ab0813c8184f7bb373b09a8a416bfa9dfe8052f1c2391a0152c43c0b16fde0e74a0c8f574457128a9a311421fe485621fd80969e4d2b8886de5954dbb5ee85c8d212c74e8da355dca69405d8c3e006d509c030fda69ca35bbdc55f8466aa5d91ec91602afc111c2896e9758240df684cad008a7aa75bad2414cbbde2a0b1203fa27eeb7e121e726db0d5116d67a87ba12a90c8405a3fcac5e66e154fe1e4df99f2cfb5ab1356f005310739049f47181d4fd06e1f7987587327d506f10de7931e5c08954d80848b53c4f5986e501d3ab59760ffecd7189f0063699e6dba205d9a90b7a91be2badf2e693b2f77f4314bca2160b1633d4f09375d36925856556cebf3353a477d6085e2f3aecd4381773bbbba70735480e0dbd1ef69760c627e2eb9dd0a772111c4d2c9cab14e53a41d832443d03f3f6c3a5a6ec67c3286b230e406820d245dc4490a9007ae7a92455ea4c80b900 +generate_ring_signature d6ebf139a45ef1fa4b4be6a9def628430e9a0ece08b67e60646b44d7581f62fd 009fd2354de45de4f50f4171609d01f3af1da8c4a966e6613656ab7ba547bb4d 2 6f791430dc6089fe5726dcba4999c2f277ed2515275152c3615756c85ca7fce3 fb5ed1804e5b50fd5e0faea33284e7b91d9d881a69067cc49560ca97e1828d7b 689799e5ac8fae88a1fa82a03c0dec56a2ffa69f983d929bfcda6ea30e735f00 0 1f3a9646c1e9c1a54114bfe70605b849d0001331d297da070a524f20782777096d5cbe0511d7d855884e5ff5b1facbd0158ceba7deee8ebd5dda35cece103b0c416ecd95a60c405dfbc05331d385ae1b53cc06fff212db6b55f57a7e3ea5ca04fd0be79230ba3b22bda44012df65f57183a9718aff784cabacd38184884c3009 +generate_ring_signature 0ec44a65dc63613b9be298c6039f1c13f58071e1b8f642259fad92b82e70ea6b 9c1b7841db55bf2d432d77363bf8f7871946d1028ac185176bd270b9b1c8245a 22 0ac06d71a291bc9cb8a65166f6e267aa94f67345780fcc3a88990a1fa5ddbbae f4dc2655b78a75a5c02d1da6fadc0e7df8d10024cd2acdfa75d6b926d9df26f2 b898a4bc2437e9c7aca7ae42b40b9f55801251be6a22e06b35cfdb9b519fbb07 5fad8c5d05bbf6e210b426f1c85cdf65c610189659bc1dee66cb48e155b4848e 06c86b3de6e5b17a6b18bcc3bea96bba1f0e53011abdfc8d983e7128686aa565 1981523626d0ebc509c4975cfe7855f5935518d53bfbd8020103875bb04c54cf 71c3b087b4ac47bbf525fbe96f827dbb8ff8cf477ec15e188df1f7468f12319d 76ab284bb2b33394e49a7b9b6ab4eb0fa9175f8cc0549099afef7a425cfdee73 035ae37614df557e2879632c43dcb2a6e731f45ed71d4c24813a2fd0b33e27fe 4619e99967c91eddc5bbb3326d8bafb2d91a69e99120dd426dfdbf0fece87b2f 6c25581aef5948e25e70cacf2143b8aa5d09c4f0f71d6aa72b8f97acf504c9a1 035708993aa26e4a03188068a9053b2c4daaf5e7fb6fb5b63e4c5b3d5fced851 7046c37e9f70f71033fd835f432553e867df9ce1f42bf67e883fd1eaaf6fcd91 fc9c1fa387d94e42b000d5196cd36b4d9ae84e1a4cf2de91dd15a8e9c655995c 80c9ac968a6a22f994705609c7f829f80e9c56786ea24c1048814c9bd8ae39a8 4d7eee539bdc72685a3e4239f78c67756fe3972e1b88ec61f5ce7de6e90cbca2 9f080a14a10352a412dc2067d03db1e23a55fc00e1c90ebe2e3edf4c9067f8ea f124e82150067aef668390263f418cf8bd5ce9a01fb2907e6847231178f4cedc d28e3033f536023ea168cc178fd5c9a9e3ed795f8598f7cfbb973122a860e43c b501e956550a6e563a89978557633f9d61ede3fdcb4f5f73b10fd98961369c19 a7c9d688441935ef112dd091d6f934bd055fdc508e39f78035da02849b5e374e 5585b13645eb85ee005065fdebdd45914a3afbefcfa4d5aac0b2807c21bfe0d5 ef411cbe80bc6bdf3cf2c9ac140669cccc0c9355fbd42d32c545a67b3bc96a02 0 aca697bb06144d55b3906d30d0d450341c71864b99d9f1452798777cc484b8025fb67b8d7d930239fdcdabc762d1994b89a91c541ba2b0ff77c3d19e7fe71b0cd4e45a89681a1b412eb5d4f7a4ed374ce365282cc3856898961fa6427029040e451a45d6508293158dda5d7aef9be79f430e126d35efda48a96c2d5187e677097527728328fce97d5699090ac1deee665a9f0bdc73d5fbb2f68db89c35e0120beb46178a1098248a484f2a8bec5d839953069258ea50db97bbcd347f206e1d04c92421b273f6a14cb45804b6cc406cfb16460c96d29d070d1e77a30cff01020b4845641c2cd5a4289f86dba16dacddfc05fb087454199a68d9c4ad448aad9f00ad13aac6955b148ca394237ca8d607a168ab1e9788357f6b8c71abcb5afab009ea2573a4fffdd9c77bdc93847645bf39fc08f02936efdd737c6ef9f9fa697505f42530421a12759843125ab0ce55f491780690a697bef1078afe63a7ae782d023a310c941fe87d9df0a4da465403b88d10e228cc540578247c7dee57fb598e0f5482a1b00f1f071ed43e5a9b5bcd165a8af1ce8fdbde6dbc2b90d2c8c97ce00da2cb537811b79827ecee21ebeb477c1f0fd0301817f28774ca21c812e6f3140c0b651f08439eb7e5d68fc6aa52fe5c3466103d65f65f2e60a303b004ed8a6f0b3e2bbc4e9c43ce1873a0c6f15898f9b1bd465c06ce483763f862c45a7122c100d37196d463d4a511d3323490961b9aa6a98379b5c4fd2de367229e800c45e201a08778d829f529d38bc23e1af382f8cb8b5b4b2f8f9e2b1b954ce99c4ec2ee07536f04960af4ca938700b84e8bf3663dd80f4e6eb4e5543535e417c0b690330ab17413468dfb6258f2d6b8f6c5ae26ab902655e92ca3c795745b5799a0436d0a0a67e9f8a786f608669b7f2f3ada944e9db979512b3f89b64842533545f611094f319e0f8c434de546649b8213b241630335201e350cc9c49374c4127b96dc07a1a86c5fa76dde1863292eb7bf7fa33d9f254c63865afcbb0060c6189553980b247168b2cab577006578ee58b1cc93c53f31a8197de6b908734a84be32a17c09dc585f7a441016186ddc8b4026b89ed255a48c5df17a5257d83c75799b69660e64873f3576357d10656e0f399d334a5ce77dd7633498535ddb91c399050f63082bef1f63d923bf9fe6fe2ec7767aaa8dc1d01c9d396228e2bd318db4c1b4490e91d767ab0f89af08a85ec0b1a5e5f5b664ecc53f7a67bbc9861f4cacbab7b40ab93ce0780b42ade40849e8f51560becb47aaa2c5c97c30f9defea8d4e86d110e62603bc1ae24d41171c1a63af6f18edfa7949af088d12971059156975be9c40e00933b9cf66f8c691d29045070532d0a75d3c155cc6c5c89c811158fe439250038ada81184e0f15ec57bdd814b10915ac3dae65f57a47ddd875e8d9ea35c80094b89a38907ebd6b5c28a2aa87a64e4994a4799b1ad48206c64c9ed62393d510fbb102a4060b0e25f64956b9b3883b21df88500577b2632f3b8cf53aec2f6ce0f576aebc66d943b71deaac22d455136e024d318f6c6aff081a091ff6dea200406098006c1359b4d8613cb8ccdd87a6da93d7b5ef7031d54ddc8afab77d134260cd7f8292836dd49b8f0d44acbc7ccf829a229577b928adafe8331010e0b90e70017dc056496d6b1caa56929300911f9ca8ed42544fd7a51f48f777573e54e060370f005a3801734c8a3e4cd783f55a6204573a282ddd0c3957d49bc30d71342098899eae59702de18275d2a6ca2e9d6e2e0c9102b87ba2bf8c09cc57ed18c43092940725f044a0d73c7dfd04ae5c5c12efa99c47ed0942df7dfee0e811e4a8906015a70b6d9b4f94b6fa3f35468433d1117ee04937575a6cdd1e9be0a11303c0e95dc7ff3f792cef5922a19d35b90228d2c020830ca174a38772648d4ee11f800a8464ce9505e48de3c5653bfbac11fc405c20354d34e745f90738bb5bb200b0c +generate_ring_signature 93493d23fb44416ee09be43865923d0e1dc1066d56f5a3d0de81813af3ba0dbd 7748c0a5f3f87b8261d37a2fcf576f3641db82845d8c9baab1255a025e947fa2 27 2d160aedc903d73ca9290d7a6ab88c0e9f34d8d1eb85ff0c2123e1e619aab827 7bb969822d0b461e0f71f8582559be4532c9f9ba4b98a3bd8037f3ae2cc99086 3973974ed9fab1a8bfa2bd46370a22e6aa9ee366333379802af5647a360dd8c1 d6b7ed18d47a17ddb488bbf95b3af8435f2beecd767e1cef3bc7fc0a4ef7c4a2 0b30df77534bf83ade55d67c5525d900a8a7a4e45d22af5d3fa8036ab3ec3a28 7fbc57d8de03940bc6ee8cf3f5598cf04672d58cc53793c1f62e0f193a2cf264 300807e7fa48d0b41448f7307d2e53d080ca6da2dbe0b49856acbfb5ed7e126b b8caad285713bf9beda3f580194fd57152f67aec0e70a93c28928f5f486f59c6 a07af8e32900154e837d3b7b74d5eed2eea6b5fa431e09f4927256459a381021 73aa4e3f354b1059b14d1cd48f52502231c181cd06d3e377e93ff7d7e4493348 ce6d6cd79f36aa44b40bd05e6a087cde2fda0d82d4cdaa6e2b0cd36b0b3803a2 038030ecee860ada3ba07323cfef9534ea0a90d2b5d30f2af5cbadc1bef72e60 dea728c4b55cd4db70c5c69438fd75ea5180675a5b9d66c02fe1a7199e9bdb69 f27326d480de4e3b79274bd526dd79d24f11ebd046b9a8c5b376c44061d23a9f 136ba04808b1ddb0227d549f18e8322002c8f9e75600a3abd5c73559ce0260ea 601eed6733db13d4cb81bc73faf4f9f5af74ce56405f4bca0a048333bbc439e9 3c0c6f776ae4ee29c1c691d0b57309ed56b96a31a945079f41d3ea89e80f32d0 7249c2ae5af899b9283262f07f522602c6d1bf3d88ed92e0c1b9996d76f0447c 9518aee525652a3dcfa5a37d3844e7b2bc051db5335ea438673b94b01a04f8c9 2843e804e929b6da93b04026f44ee744eb364f20610f686d5a73fbc43b92663d 6b5281b6cd170d9cc7ba7bb56af507aa3dc21e816f0acd1659a908ea9a416366 ffe9b0f145fbc6ea4dce6273b27e9287654cfaf82accf33885fae7b152fca9ce d1bb3f05ab3be0fdee933d8492e1c5cc24e7488e44c4258dec75b4b650a9892d 8203602357f52070b4bcfc3c5240704ed87ecb93a0b4812512e3db7d9d6ed061 1568a18b7fc569e1cef7a211eabd5da6fd118cff94cd726d1b4d988598a51117 0080b6bfcc6e68ab3bd176987ec4f0ebfa08e2d5bd6901cbf24586bed9ecb4b6 6b608f086d9d4c67f3f1daf684659a389c5c9140c7b6e5ca71ab5b107cefdd79 dd1d64ade0adf36ab84cb537861c99b79a6d012ae1f994be411baa76db298b03 0 bea6d698f5433c70952cc7caeaf17ddcb69f63d0d7a4f67265ddc34142723e0965c0df9366b2c4e68dbdb9ea20baeb077e7817f05c7177f1be1f6ddf8452be0bde569a5578ea72513528aba714f00676622847818800c503e370e41b8150be056269f26db3dd87047b8a482963712f15089f2ba6c1aad59b44947fe4e3f25604a9e448fc52f5a50f47c0b00991addfe9c2b1e41ae2eb813065c181a54148ba02695efa2cb55dc8d84d1f574d03d9e0295229e6dcb3b9eed50f130e91065daf047b4c8350a02077a31d59c5cf73cb70b8d65a5efd6b66beea277032a4482ed60f946d7986e1e1793a5cfe16723af882b0e467d2d8d8c68bacae688dc5b5ff2a031c40f15a610b84b55b443ae6f7bdc463e839688724f9ca5764b5822f2b4ded0ec9ae74df3ae9b855d8da7c0af915023f81155bd602c55ccd76643b79ee6ee804f51c0dff6a13b4396a551ddddf56f87a908a356aa2084c241dab30ad1f5cfa0a5932eb1e3f268d29e8b70c2d67d3a100800e602818ed1582378b7494e5226b0d439ef4bf56d18abb171730090fe6a8e2077baca9588eb8b4568c4acdeb09cc0b9ba9da84092772b73a02c10d68904e1df5211e2314787cef870eaa08590c010c28cbf16fdd74e52d987b67b580a3b756c9bfe9f972874814ff00d9123c774c0a1499ad079044c423a4063a746c221b7196e46d0ee3cd727dbbb2b54a98def806ad3b71731873ff59f9322d2aa05f8d3f4bcab6b4274253f3892375941c72c00de58e2c2b80002ba3c8bba8bc240d9b9f2c28a9467406011604037c2609cd830cd4edd516946affbbb55050c4e263654d1d766d4f391f2ca76eafb48cdc3e5f0666f058e007d9c96558e3837991d42d69528900f804780d8a7ccdae4772cfc90bfc13ffe902b0312568990758a5e4526f4a623449b1c51aca4f98dbd21fbd740a665ca4c724bfac62d510bf0fee0daebb042cbe407b7c9d4bb45f82b17a849803090209a422ae6b579dd14e015ad5b4730fad267b0490165398ca0538d638d70988527e1fc8967c07bfb39ea958836c5df6854cad6f478e468ebdccd9c7abdd0cdb41fb600c43750a50d369c502ed069954e1f3eac8379079d2f73846b6464e0f7711c2df43adfa9a4b5e72d39bf1c965963748e489bf0ec0827ea2a43c74530492c34d31c156eda9fe249cf663d391d977f93d07577b5ee9102c9b6718ab0e0c91fb16c6e2be9986fe301f09e163eb13e583bfb7c660f31017d2e5b67337910b18955c54d4368a441cb3832bdcc064804129a587d2e1f5f1aa3fc48c1877e305793fc5a7b6e1b3c46a5b6dd2e1a3cef701e08d06d6620009ff17d9f5f8bd310a3915fc9620a214b44ca4330ee16bb3547d21c9071afb83007181ff63ba16cf00451edb71ed22be3347ebdc53a1f01880c7d3fb4a57df73bcdf213cb557b41a007e1a65e5aa0f627124158d05d41afaa1c6045519b6bdae349a41d06709827507ef59db9cffd08bd51e635879e6ae1bb66cd53995bd84181e52b3041afb4a170d8882369e2aae3edaf892e6e12cafde26862447a91e518d4b1ae26dd68d5c6a0060af9e33e4e890dea8a4a791ccb27bc575b6661b57f70434e070c9c410757102d62a4222d814429914e376099387c35a613b2b84f5b88bc68a10d00b88cb3805af4eec30920b5361d68fe562109d816f08a3ae2f48292f0c37f72eae0a9c610c91182ce834cb8640e4ad8658fdb0ab0c5607e93eabcfbf6c44997ed09d004f07afe14f05f5b8defabe47244bcc50a0db18ca66db64deb8f6672a5047bb76060ab1d0ac9aa708743f36936433f98c18dbac3545d36f4cfddb0455951ccd0db1090fc8adb82b12308243fadb52961b2068669e12b73d931a34d7f9b1b95d22c00e6243c01d061dd6bb1f9b49b59decabed3fc2fe9c643d61e8b90f839a4949e90f35032aab34dca4c06e4e3832186a8dfe05551a7c85eaecdf4a0388a69efd2204787609978f997649e6938c1572ba589c551d2f63f65e9132053fa5039216280c59210de7fe150f42777ba6453fe65ee80c9082e8079a202d90d2090278dab6032be9732402408d8498f471ede447e4a08bf543500e0850b8946f949450a2230d882ab915e7b45730af26f843949f22f2f42ffb28ee075358c3bac90ea2f9710d99e913986f3bd7302a8c843dbfadd16352df4aea22da55e79379457fce72fb04b2898ea995c9b73d03a152f5c4c41940ac13fb049600ef29445cd812d8196d023f24143e72586d4cfdd1273cc8d73d9f829df19938a81bf5c3b5cb5d1266910b1d0c65d258e7f23e0742a53d470cd859d82ed831c32c946ba7df9e8a956707031e3a5480623301c3f92bb429e5aa8b25b24125c2526b17afab1b74493353c90d3616d12f19a38cb37db8e0bc5b55b89617b85b3343c8d805f3e102b5366bcc04 +generate_ring_signature 47a17eef9aa4f45e349474251fa7db40f4dc3b2d9cb74a0f63628730e4f087fa 67206377b4229f14b901ee725025ad9067500f80708603d343cde543f6220070 16 b9d6ae2e4275913b39ded0e04f23f415e21d0755e6f5f15398ce24db187b80be 7443760d12012330c27c0d958be3f5bf3c543ea087b2e710f1df3aea40a3597d d58c7950dfa2cd1e5d5958b03665844c4ec07d3266238af73c026b2df9c9d749 22c2ac91231c9618673ee8d8cd01a3fbfc915f6da971019e5ed1c04cb848eaf6 bf376c083d6e0298d8e0bee38fc8a296ff9db39661ff0acac51a7cae47a528ba 4d84063c54fc35ef611c69679d6bf8252aff307262372e127ceb6ce28a4eb57a 191d335866139d9a9d70172dcb623d9d0d589bcfaf29d639db9408105350a6e7 16d1388ac89e6e7be13d79c597bab902ae51c727f4003e0e32d6a9944155f5eb 7161b56af7340827dceb9215c1ee38f21479d7deb8ca9aad4cadad12dd190762 eadf42743e1b0f76badbde7f1b5600c599eff2855e4af3ba1e832b56c745121f 0113294d12228749802c3502457a95df6875abd7dd63631232e319cda4ea7176 517b3628d5b1f6a781dc2a65084e515d45f745a55acf671b478d2553006ae2fe d1a16b1897c1379411ea2bad7ff86ac19c519141174182de83c372da21e04676 9c6e206bf2d21ec5f36fdc489e51f9748d748a8064e5ec0ed21e7210b6075d71 fa2fe7158dfba5f1083ff8db88ca7510fa17a08da7da486f3194297ed4ed0afd d467bdfb269e9f5e8c72cf6c27b5c7eb4c2beccacd7c0fa37d22cdbe2d15807b 02584ea85f70c3ef4912ee658341e6b6dba3f2b9e3cef23d99ced95aa50d680b 8 a40c1041eaefc3bb058d049d1d27265f5010428ff77c469fb1540e8c616a380993741a55f853fffe8f63159741fe6472bdefcbffdff49467008c3b45f45fda024a4cdd813b7f9a83328746863f0acb9dfad4f0a79e493166ba0e81f95ea98f040ab85093b2a1f18d58ba5d3388a50a7be32ce5e13b9087b9c1c1cab2be98f6056ac7bc41b7795ab46858ce7250ce69cf567d17acf4e0d885e898a26bc0d8600ed21c79a9642e20db569f02a5e5762ad5fa7dbc1de01cab8c5dad06e0c327e4065ed4063b87d629759d18e5bff5eb23b184e118e06326f0ebc015aa8c082e30032da8a9f908689b28b4d68a2faeca724b8a3fa4a19400064e88120aaa001c780ac0290d023b71be7a6164e50bf3b40b048514cf780ceeb1b959d0c5419e017008be397a37e7125e3e0fe28dceef3d0530ecda6853ff8e8c9ae8be5aba50a48f01cf4050224dcf77ce8c641bbd98b61ce2034753619ca96b99ab206474932ff2091ca4b0ee5efb1e722e22efab6392bccc03436434ee6f554ffbef847d841fe701bbb7b4fd8b3f669c2c463f290f492cb406d05651d0d7ad5d5d0c59f21d07510a0ac2f7d8aa9f17868a69aa9c27f0aec811de5126ef073737d1b3d9e202842b009ec25bea5b571ddbcf5ae407ba4b5fbae1b25323b17bc58fc17ea4bb439e6800a661f99fbbc3a4b8f0b5c7df581e59ae1a48fc5bf973548af75da1891d05a800a4d54d33e57944d29795c26d25e36e7f64fe959d23f1a01c794bb7e7eb3a210fb4b280905833d866ba86ff5ed90248ae3920e338bfe6ee173fd59b3160b84a09de48bef6ed248ef54510ed8063158cabcb1cd1d831e24371e1d8dae3ee25380d068e61dab85ad6e96c412941806ba9f3cac029f3f4877c904636443063c716077073c657eef097f972a81cd7e663dcc05538b6a3dffb5f17c10478603e06b60a2dd44dc881914bd8ee321e3dbef5005724abcbde7bbc013244f32463b1edce02eaf0d30a408b981ed8f58abc6e3e353737e7e9d4ce242e6c3b9caeeea9bbac0efaca348789ac733b1cf0009a18518f7bbd9cdb14a29cf7400387b395e0512f0be046d4e16b3f796f938016a263535c43d973e6c687405b52370627af9a11a304990e02474b54a2d999aeb53a10ae7f0b167ba9aab3064d8f5c89384f71296803cd71aaaa0d408ac449ad47f424fc15b6c63eab1211a1e3949e1a1c575047e604590299ce3e4ccd17484998e3dd92570d9487abd52213eba890b810ce1f16a60763df8d0229fe69d9f44a4e789ea00f84dc23256de7075f989a9dbeac670334072e1ecf44994057baed72f5c5920de737ec3b23b37397a1df52dd22e2c0ddbd04bbb10a48b14fc757eb4b95f72fd4a6f6191a950a3d2bf83aabeca10f7b8fb80a461d1d663d78e579e24123448beaf5dbe636fbc5de1876234e84eada0fb2290a +generate_ring_signature 9a7086a8ef73356a7a113ae91f791ea57a32bf63cb15766556218bcb2f7582fe 9eb46385239b056e240bc6a1180c51a025b0837da355c5d60055da138d05cf09 75 d716b1fb66fc9eb0d801d13e48a4795600b8afd004cb31bd7bb69ac7bf26a5b8 facaa9a563716b9d22e3e8204e8c38ab90e4329812d9bf33dcf49322105ef8bb 092504da28d4089cf7fe33cbd6cdcfa5ebf9ece75d42805975564bdcccf34497 893aac484591284d041d6cab92ad3a0e187834520d90b3a6686276070ac380a7 4e8670484c0779ba9975ac8bf55da8ae29d278a0334b23e670fac6eff5bc133a 7fe32c5fc50564dd44faae596d56dcb817232c1e50385f248e6dabcf382d0d20 5560a7525b10a96fb989ffffaf9c2a20291d0d28be117be81cf7292344c26a89 7abc90f8cf53e7b31349994334ab3388d209b38b0a1747d656b064255933b80d 4b5931de92f69f1272ace927b49149cc942276296ae99a42e52d7d83de3fd414 a2e67069563571d1eb1f16694869da1a9582511ea81b005eecaf1f63adcd779d 23c1f904a58e6ca0d91c4caa3dbd7cd5baae653299b339f9859ce870200db57c 19d779d89ba045aa643e717f548b247156a41d35e7be773d5775eb452587d44d 68e999d5a013b3d945434a46adacd5a2ef18ced42ee0259d949aaea9f06479d4 f8c85c1538d967eb627ec666563cfa8203b978a8f88a236f4fbe179fa791c596 19909e7839625dbff33ff26ed186110acd7b34179b0e55fafc84a71b7a3f7e31 53312455ea259a3056ef247f4b25e63a807fd2fb463b0ffe1417cff46ff4dfdc 3380930deba9c016b036d71ca525f349ddbf582ecc80ed127da3272f6aeea4ac dbde3d341c05dd02ad333c64a9bf35866b1f44da659a07135875cc65b4d65439 06577af36ac3f0ac371d8a2167f637c741b9d18a79afedafed8e943a081733d7 944ef7b3bab2fd90c5a46437b068c23d4a5074ccf0d0a6fcb960ed9763c0b880 01cd86208d37c5512d16d459a4581904d13eb862fbf9e34c24e8a1c3233da0eb 8a10901b4799674e91e6c496deca1f904b720dca6c3a81364bc2ff03a7e57897 339b3dac7330ec98d76e59a6e414629a1cd465d14402c6404c9655036cb5a010 c0f2a7cacf16277a4c84b115d21db048e8180a15b013af3f295739cb0e5e8c2b 9bb9566b6ebb7e992a2e6138c810873127f3406860cec5d3e867f676f23cbb73 3a31153daf08deaee78252f49b658c875d195343bae2a187c16465b4615b64a8 5047c33140837bc18466fc3d1e178179768860faeb7ff166f65450d53c7dd290 bc66bc18c1d6601746fdbf34d4f9c4e7213079d15eb054cf570edbb66ad70591 fdfdd237e667d70a530fc4f387af0473f99a1f2988dc480ac1606df7798798d3 d982e4a92446e08a2e9dd91b155e580f3d181bde1d5f5e69a6cf5e82d35607c5 f8b566a67779c1ece46c432d20b280c5cb8da1184f7eb242c33759434d7fcbc1 6fcc2b37559dd4cfc22abb8b5fa07eedca9d56ade5372f2e4a038ad1d0b92c0b 15ba3fe0679958f07c6deb5369d356b48b668ec67d4832ad33ff37b0abcc629c bc70e3eae472a511ab3e547f91aefd53e40dccb0b030fe7f0f5c382f41337be6 e74bbca25c9180dbf84ee1feacbb9e6d0514cb6acadfc7fe9269d8f1441c4004 0a681694b9181b8b0eee8388c9714f2920c63b46ddc955643d9ec4781c2a820b 377e5e05a7d1fda80259beb3d2d4067cf955e08089a74a45b460799745a52532 198981c0b8d166af3bff21e762bd85cfaa43cd7241d7fa611905b4fcc67d6236 1c533cc6c0bb0f4eeb97e36be210351f39b1a4ab10b7a339e92aa187a6e08679 955803e9b32dbf08ea6a31dcb5bfa460976d058cd5d35fa2cdbd446dd112b3b6 b6eff72cacb508187b555d67f22abb6e73ef2528404ddbf17411ac4510c44fa6 f55361c4f5dda43876eca6af215d10fab43d1fa4f029a370a08996e08fe5610c 38d3a3e8c7d263cf018d96a9c81737c34aea0b2d3092b63d79ca96a2289046e7 5a6424e37427e45b539c65f46272da6b578868ba83a76e6a0ac240c3004ccba0 80d9e5a24fa55306544acb007c88c87a5c65b6f3f814d3138b56d914a7c1f046 7635087c2fbf829094a693322f3f3ade9c719ffb418e5265c0186f4604a294f9 00ce84cfb403560546340864755a5e399e5b2c061645bb7c12a7c99b6098ae82 dcf0053ad01d6cb871a35dd4f255956e50f487ed81b0135862982eb4843df3e1 ae49f8be1790b697b4e8dadcfdcb5786ae07e2623798d494c18f22181ee1d4f2 36cf774f577b9ed47efc0b0b8d38dffc78e6372ff1c3326e336b71600789f487 e30e26abc6a8adc3d0e8a226fee82efdb0462b698b98a6a1878c640cb1cbed61 11ecb444c49b4a7571625fd56f8e30c9008cdb5ca45acbb8d8b716480ca9bd6a b89f7406af985457210d4e406755e995cc07515a414c0c6f751c4269ecf03eaf 7456e8c3d729f835db509c26a5b20a9349d73cd5a59c2f09978df89650f7db88 378ad7b8655c6acb0309dee66ac1cbf99454869528c5cd45b17cae0386db60c8 86cd4f6a7bdd988a848508a368343b0fadc1193111fa561687db3cac67e2696e b62cc61b3a0e5cc7b99bf1107caf5d810d95ddd4102ed701d862426639bc3735 01b43550d9efc0413fededc10d91ae556a36c29151bf36e94c391f162ca325ca 75c92f49af4ba741ad911d40d2c4e021927009b71602601d9432468069caaed0 4952e76de4f8e470060e655a18c697523d7ae0563ed61dc70a54cc3830e3c988 562ef593d5dd96f709bb82c1b59ae43ea34b365a05026a7344010abd6c419fbd 4699941b91092f0cf560e7f9f35db0295f952f11c5821635676538495f663201 ea0d5276b2de554255c56fc0589e3795fcc9f39234b354ad8cecc554b7b8f6bd 685df7e6f427618f22fd206f70ae5b44d71f408de837502005777fdd92bbb4bf f364ed496716cd88da9cd2b9b404f620868e8195de8d2f367f2c4c80e708a16a c7c368adbe6409674f1a2fc603ae4fdc1a7cb31ccead9b6e1443a6bd90fb35be 248ec8d63f6c5cf526819521d866ab1bbdb6cfa84b0e87dd8a7ad7d1a47a841a 4dc9ca992f062b7a011c372b3e99f87d54de6883627d42cc97bc7a576a551d1b 3dcfa728ad76ab77e634add3524207937374f3eeee11a566b5babddedb647fd7 77fb0e227ad84997ef3a5f33c800b7ad6033aeb0f3eca8d688abc8008dba62c8 b8990b5dfe159a6db927724d9572270071593aadbed7d4e9b9acc4a1458afb70 72886ce644da16d21a4eab8b7d00dd65f639a40a5dea3579ad7923cede155007 6ce176ac239a4afd1d9f61f7cea9286ee6b5a6c3f5d8c76c02a3e6c59600deaf bb67aec3e0b94a73aea72380edf585f9c47232f471b81ba1725beb05d6352cdd bd4034fd4c3d280c619ab29786e48bcf7bcc8e839941cac09d57eee9a67f0cf1 2b9967f5aca1eb4e0601d393b1989438c1934a0b0dbdf515637c2fc7c9709a02 17 f87b7b46f6e5064ac532e94bfa9383a8081623314eb3a47cc7bba298674a740fd8060cdb214b0ae414845662fc04687cd879a6d38d42b47ec048898145e68002a54a79ac4362ce06f7d2277648302a21e34382b7cc2c0e023bc0df5bf8e1dd07f4dde27a71085f9168c403c8f939a304c5f38e527cdcdcf7012eb4d64a7d11078277dfa8c57763271caff630e4bb2879c50f4e6bcd09619624b1372ae8f074044f2504c4d7d74449e37497cd23aa1961685d41e8ae35e8d4768c95749f1f9308fd35da60b895712f72e4fcfb4a0e5d53864513183afcd5c43d4ac9807d56d80da444c730e68ddcf048a78a8f162a268d872e84b053771ea6257a94549efdc000d1dc52f005443989cc609443e4fee4a2afbf420b8c5b0b44bc0da731cdc1d80358a4c1b47c9b4351752d760b45025aa4721b934a407f1ea096f88a6693932304f4514001a93582899773a68e6c63cb844ec4dbbcf2f5492d31ce3daccbc42b0aaa4ad238e08cb446c88839ab5889bd1ad120142393e8c55f04532f3f38060f0c40356f9e0d2769564eba415012d1aa74391dd2c54a45ea3bc995968608a0960568ccac49e52825d585a07c288c628ff1d943f30cb7aa5b652bedd37c4499040f49d83f8b3aa4d4f6c65356212d0b26ed2c6a16a2d786e4be3afadf70bdebd407d615b41c41a80c9d339580a17daae33501fb42b5e3b695f579c2bb3527818305592976625bcdb79d2965fb5cb240c46ba8a40506151fd9bbff4c472ff421c60408df1984e029617398296eb1e9678e7416d0209084d3301e1d45487d51f74b0b316d6bf5afc988d7402bcf14549c740f477799cd6a0dbfc67de22fdf8e9a02077e81bdf026f0c71a366b6c903030a72d60dfdc7b442b53e57cd60ab912ba6305becd9dc1e5207b68db934f1c0bb6ef7be553e94c517423214d9811048c97db0f7a58392b53522c938ec81016d4ae7735b6d9de1cdc049b57edb67855ce8e7c0f475f2bd16fe3da64f040e3037be77c1243f13b340b98b7791ac8a763136ca40cba66f9735c20627728c2a9129efca42bab42a2bf9edb62f43d3bcda433fe47026731c310d0196990d47fd2391e387f0fafd71385bcc098fafe8c01a415b8150e981a0bec2238bca76abe80f07d4752f50d3d7f5b9c7c9dae58281445f8aeb30dc84de88c4f138eae11bdf4feb9b4c3296319e774034c9ebadf14e25175a6a709b00b10fed2d75a40293b2777dcabbbcec7d713d4ce2db9a64b929dd65189010544cfe03c8f3f3979b30a3d5ce17815e36f03f6e7b9552f96cf98dab4ed0c8306d840e78dc0ffbc9467cacc058a87e3948b371cc93def309d57899e26d1191a022a3495a39b7a15a425675260595af6deef4bfa8695cda4f42ec444d4d4868b09749f06d7d2acf0f537ded7d32bccc4f17f6c34aede942b8fb9be350abab5ab0a6c328515db6a0cf897f59839d85be6506e0a4197ceb3ceac9c6043e6e9593708b2d2257072c46d0846e46848d28f7c99b0361a76e0fd22ef7642ba95f8dd1804c6e62ab836d4e06df27c2182f6b7fe5fb8d83432d37a656dffe62d8630b2180a89bf89ee905016b54b01c03f40985646a3a5338bf9e7675df2ef9c5249987d0358044a274a9edafcc314d902c680fa885e308d17f254d07cb95a41961df7be0bd7d10c1eab703e50c3fada009bff2d054524315132b27d660a97aa600ab4710dbc7c1a7f94420ddeca4cc070b45402fa5f267016749fa471f457f87e647058009ef22567f7be3d81bd72f6db6aa065eda0cb7a2fdc46d2d82830c16f13fa8b0a2e213790d8f0e403e93d1e8ae047dcbf51486f647e73da8ff397c72e53c2060c3eb05c88b60997835f4143f82f750881ac768230af688d267bc200338f4ad903faa91a93d60f5206a6cfbfcd8597419d0a87923fcbb66bbbdc139bc6495a710297514767c4ff3e2fe52dfd0b0570873bcba18c2cc36990253817d388056e79096b1691ce2473d4ac32445449a8e686b62b9a373aea9cc44d5a46c2ff5adb960cc218a24e1a85526a751d7dbf273f28ac238998e253d35c537f665482658d460b6faf6bc51c0012e301af13c403429077009257765df26178c4b1ad69698a7101a77df2fa2682ac01269a0fc4eef569cc277b1d13b4c1b04efb86823b2d8d1e0d58ecca65966c492929667efb801256d8a26996c8193aeaa54e8c4c026b7b990af04889d455f84a4fe96ea86e2a96ea38e44871414b53d126391183ebf9f06509e0709044435b885b1b866047f6cbad74e0fc269064c974a98bc591e4d7272f04a48ad884e29c877d5a478914e7eb5fa1afc14315e2794e9ddd9001d29156ec0dced39d640528f2898daf4c71ee72f96a424cb060688b1a1809d06d41dc76af07f7be406d259f320e56643780865cbee72021d9627d6cd0d034af82a52233eb0ed1a0d0c8fd052a443bc924639d729baf64357455012142f5ef279b72122ca30be5697b1115d24eb30fe7190484ecbf1eb8e313f491947c82a337af83a15a910df11d9bfe4d9c454630a9d834f5f2c96b01e905217a305e2ce48b2531b75a7f0bd8747543eeee305ff059fa87d151b8570ca7cb5f2e4aa8c13bfc77567d9c190cbb4aa17b661ae327db5c069b93c156876fdf2cb3deddce4b1f5788cf5c8c6d0cb93a5ffccb8cea902cf2df27d1802bf8ee44efb0a57eb167f433228c84d9bc008a1540fc4a2aa84b6f62cb958ef83d2258cc091f19228ec36ca7795b0b5ca40efacea614ae0bbb12c8b97da05cc4993372828e745ee902ff8deb05a2c7652a0e851c75fb96f8f70c67e2d165a22c96e50f3d57900833d372f5fa0db94a0d82075b9e9f2be1a03a45409bcb580c138eb322b1e5e2bdb87a908b363fc9f3debc0d5ef894eaf3b5415eb418c627a85a4a04dbaf209aa73218213e6eaffecb144e096d3cbe84cd6c48777a0872ff4f77ec3576037c8a7ba87cf795e263afbee65d06a58726459942930171d204d554e3673f9711763e13b0e05d586e5e144663f10900e52b6de41be11d57a840c602df5b3eac5cbc2dcb63328ef6d58768a6ba370b971265d07ba7969e3ded0dedd059400459b61dc14bdd60968741453eb74dca0decdf36a2037496588c818296d4b3733d5230cb5db82cf5c3c52d32f194696c0888a30efae07630542add79fe1761f41a72e7bf4044d930cf8d94b4650f8cae0e6c80aba538396988092503a7b53e1baa96e3b520ae1295acb358f5bcd855c501f67365790f7b568c85b5fa8b360f9dcef14871a659b5fad199f3f4a265d30004b278e109980eaa8f6caaf471348b3815517722064e8e31db372717c9dd76110fd13883bb23ee54c145ad851177d7eda3aa79bb5d72e85124618044a1945475038805f65c66cbe58d5db81870d62382580345a4e2c0c4c72d9aae34779f29340e5324b085ab8f20c022a1878c568bbee8e36ef0407759ea4471e1927e7eb80e0f68d15657ecf4cdde557117d4274b5e027f6164afe349637a52a437adc8bf630a9f0019817b317c275a55d4095793bd27b6b08eae54357eeecfd28887c8857c0ea717f35f69df80d3db21b68aaf15a4133e76b8d898c12d84464efbc2b3525903dbd971b570c4f9a45aa21a1dbc37ef54411ad261a377f2c450d590c4dd630b0e96e581eacafb7dbf115038ed36015aa1829b8460042b781b4114d84e88b7940dde26decb1d75b6ee4a1db215b4a75503947c7cc98bd04a367b0342b7877d960b2be7ef1facd457e6e1a93d58dd4b96e3c2faf684498284bb2f2cd7d499e1cf080bf975859169e7b4dbb1c20b26b807cb65547facc7dbf3d1f15b50f143639604a615b5460e4c4705a6a222657d83080b07e331a5cacd71e1fa96afee87ace3081788db28b2f3f35e75ce4ff4e87fdbb3b3a457abd4fb9497c29663164d6218069e65d4032de080a624053e9ebf7279f5fade0af4fad8750a44d825be50fe6e06dfc73597178491e4fce8e7a70114eab887cfecf620586d519bd383e3975b2107466bbb16301686e958f38a71a3ce774b4fe1498afd0816e4c23c2a638e308500463ce694f3ba203669a149282b09f2e5f1289eff160e598953f6f8ab9f7dc30e147a1e18ba1418f379f59705610ad814a507878cbd31ef0ad99cbbafe3c83800ac952d05194e52410678e3b5a5fba1edabc351afedf1e3a1eaad92ad6f4eef0eaf7cad976cf571b9a176c27cdc0e461c3a2afdcad5f7140761037bada6fe1208a53c485241f9f3118e0f8f6d93128e4965a1fddb2a08fde668b321dcb889dc0a955c88cbf796ad42a8273faafe8d89f9fb4f8c12c900802d303b9376b82513078a63de112d845f7eb3d832de837bf9be5ff110d8eb80fb4b01a03b2d3d82b20c83f128bc61116b00274a8cf1eca764a54e73d7ea64459ffabf94b810c9eef80da761dd781ab8bd551cadc6de9b855fa4897dc790e15d04defb4609397d5e3401424b60672ad99f626b66abf6db7116f79d979099f4160ed85fb53e6b8d659d099455e0cd6a79a31b95861c7c3d99d226297870d08d8cc3dd06988fb3086dd3052b91bfd3691f1adbf3901d7dec727d1cf4e1542440695bdecb589c8b2c75ce047280c0a085ba396c34b30d16d4b3af5c413b333a1842fa0a0678984168c0d40f28c4fb8361b59e9b616871e82c280b7c17addd59487987ec023303e2a962e1061059a97043e3914ad1fe31423d9d0a30b052eb9672332128c8ee5e74ee2b990b8ba0de941f522131225e11739a26ba0a494831e19fbf2e6289d21c3815202c029c99b2fc9a7a492066d722b43e2e3fffa9b8c711d0b9368d2ba19b87226385042f8869a00538995b5d028981c0faee06cdca8b4c6308a1d56efa7fd772ed4807f28ed6bf6fe2d61056055b7587ddfdf37bd31ede1d31e78b3a1dae0b415b6a0aa3d4c996e95cd9e3af4ae4f527cf5df69d654580bd7a54f4afa946a262db1a06718e075a0fecb603a3da413d9da00020e8db194408447d173503a67655dddf03f8f87cef2d55d4518cf4be3d6fbb224d08c74750f5c6ceafe7eb44e4e70c34012a7d1ffb9f929562c234c39f5300a41e9fc5abc95455cb8c14e1238ccb107b0a720e9be6307b844e9e8226b9a5a6de44c87faf7f1bb1279e97c8a63e4124f903e1b811655f719835dab14596612590344f854fff143f27e57bbc588f945fe30f6890abfd567cb38ebbdc01da0d393ada5cbb98f753374f33792843758298d001268973b9c4d71679d7969d4453e7e2f35b824ae86a0f94c7f6663f7052b8e40ba36495e9a3b5d5ac05ce1eaf11b766052b13b03bfa7faadfb3b0714e652ca005da1aaff1d9ab4ab18e213980e015ba6fb176e4fb281ba0645e229b4b30897003e47d5da289311db7677a754a1b6672087b54adc902b537c6366ef7b06fbcab02f50d9ef2cc95db03f5a46375d92cfb01fa364f6364a1c2305b2d4197ae5175074379c17d0e2b0c624487044896ac762afd7293d4b6dc02ac78d41e23dfd192056288522512eca4c1b7390e15592159b1781ec042b136a5f62b674222f52e2f02b0d151e275b3e4560a7eef14bfbdac4728e4e66f85ffe9f88dc765212413910e262d430180bfced320cd67d7a3458eb53f6d289bec1b6b2e19c70de043a8f70253d3594d4ae3cf974f16f40563d27bc35b31f343dd2dc5437357197b4e4456015d309d774d032f219f205ddb140949432420a4936cd0956fe9b94aac518b180e6019acfa875a441fa41ee165a6dcaa0189c314ea519ad916d5579387a9e22a07e62fe791688f540c2b41646ee634529803da6e1879fd4832c2f8d6d02ead0c007dd46f925a8dca5f3326f100cd72a41d1bc250d008a9a443b8ba968e59ce440be6217b354b3f00dc97848bbe5d60a03c42127056f2a7993e58185bc75d755d05b9e9301d0a8e7dc7fcf030e5784a06da6ad16afd03eab8286ccd73e299a61f0fa37bb6584934cb6fe79202892743083dfe59bc1a739a33ff9f3703cecc690e05f0841f210613d5c62393d39fc76f57a581202f5b57aa5e48fc20c5b85061f3091f4e75699944dd669e8a465766afe1a8c3dd40509ca18bb9149c4f9d48572a021a85053b9004a114e270bc49c7c8f95ce9f5488dbc0b038860a857013617270e58950c202879c830b131c3c7b74e3a5dbde2989cd9c980150c7bfc4da920660f3695f1c8852ca4e8b1f8be6e1fca07ad9e06a88683a1d65f23fad09ea52f6c00b1696494519fdedaffbc464ccecede3f32eb1d4aa3d2be87766b678b46c1430dd0a178d47bdbdfdbbc9cb5b84864f4294b1a8ed13366dde262c771cd222c6c05916c1524299d1585e8747f1fcac2e87f52743291c3cdc4fe1204c59eaef1090887d3882bc8b7c49fbb388e505e03dc48527da85ebd8da0f293cc28f32ab2cd0419baa20598b2bf4d4381ddd69b65f24fd73a1ac075c9be73995e69a08b35fb0808fba65c0e09c1c678eb8dac6aeba3008dd83e2031a3a22102b85c56b0c98a0cbea48333548a44cbdacb81af646c4692d3a2fafaecf1d557ae38d44c1e5f4106afe4715b5f6e5ad018cf2cfac81b996c0641a94101cc2bce4b2cee501bb3fd0bf908ad106a6550cc254f4a62691202555a3835f2f0708db355d5229a9b2033098756a5114a3c9770306fd51b710b14e1c0b695502673417dc87ca34c7f74d8057e39b2e738f74812729a4a5f0fc6b1e17a40bb20cd1034647eba953139267e0c0a9ac35f04fe82dc4b770dfb1e20b594688ab9e837856249bbc094b67ac2220b +generate_ring_signature 9d3b5964da0a0dafb5a05244d09c44cc052535bf41794549aec9b85b7a238998 b7d7b25d39766c953ee51d4057e3746b82c88731470657ffce7f90bba173c870 1 a0dd3438cf05d1a81fcb179ce77f0a61b8a47525d2faf17808662ae3d2127d4a 9046bd3b0ee4f3b9f6874301485110cc7a5af1806eb93acef62718598a837d08 0 21fa54f297c87388cdb9f180d3ebf5da753b708afe3247661116982a6b348202455a29020e8de76f7419a3255688ace36be49037ee17457c5bc8019762df1108 +generate_ring_signature 36c2cbcbb47f9040cb04a7e8e1bd128dd73240ae49d6a91e793c624f9a1b5680 faec603891f4e9da1cdd3e6d205a4173aa202867a1eb5959cbc9e93f353f36ed 2 2d266206059c398811fc3ec5e99c37dc51af758c47f094bcc5354884fcf0e83b b76f2f51b0e270cebb200499466d003705c19d8589e62d6f2abce941455aad68 5b96fa1f454618fba2f17fffe6a26238269fbc0cc08b9361aa03c451d1fed50f 0 8fc197b0d5b14d3456f532345c260f455a0b2d39ad78d0aee7dc840b04eddf04f038549bd94d79fe582b6293830455e977abf32abe7a99deffe14edce2fd8a07c905f794e8d5182505868ddd36061fdb019451c2bff2a3160c095efeeb5d5903fa4217e5a11f414b678737b3844ebc995a6e58715b9f44db071ac3e041766f0d +generate_ring_signature 8ec53a5456f968a3c524f9096eaefd8df6bac7b84fa3a94d0bea97696520c66c e55faa7e53c815159484d92d413bcda25408c57907fb97debcc43b82bb72454c 81 4e6ec9a062da749b4c7ce55c1771c5be74038853f1ec913878dc89b65a98ff82 76f3b513e21d14585ad5489f7e7614a99d0f565ba1d9c74d7d3ac6f4de6cd0be ba18df4320a46e06f5092c242c34b1e5780ed295992b8988d8b58fb4f4af8fb6 038270478eba4e0041656da105b78146c1934c9e8c43e54e37e4103dd9b2d84a 1d88989753b94a019df5b0d526a04355b5caca57eb89dafc2ddfd166cc2d9b16 a18d580f24cbc8814533279e50c1b76c62a3f84c8149a5f28985330342a5510c 8c97df0e0ed3cd10d4546368038ac04a493fdb4f776b71f2883da82cfca39ba0 d51124bf2d485048464f1292f68ac4f2f2d381ee4d776f442df04470341e5dc4 76e1c7489de49f7c029256219e12049f8868c0326dcd9fe7030d4cac30567dfa 0481ffdc653eb171f9f868759e310680c494d060876e40ff8259129ea672e213 c086abc24ec1e5d6c8f1a88fc25aa1b8ded0665a092d57678eeefc7a8fed692e 63068e1ba561aa0b5b7e3338583da9dfae07af9198024cc19636f2ab864ca457 2523398a630cd53e849a151ace8438fddefc758d65a0983556ebe4849cec0e69 05715bfef2fb4e3d04fcd6caf880db87df20232d9e6c22f3e536a051833eb97f 79afae0b5d5f0340259f26c0ae49f2d7b2efcf451f721386bcbd3db0cc516cc2 32dc102cb33a1dffc0551aa87f896bfa4ca364d7e473855053ddcedad08f6192 28772c16ef4767cc67a4802d334e62b3cc366320ddcac5db6ed6da4a3a518e48 ae62ab17972d879f26be0dc8a4e0e72cfabbeb48a1f6c862a566744c73f0d5b3 95757dc182b24d6e76f770585632e8e2293ecb0b2e068b1415c5aa6b8844bbf4 a9a01f9794d39e2135c734007c792c0170211b2f31d22ec855de6c2c9bb099ce 809e8daa0bdbf2845adfbff43ed329bfad793cb1d50964b76b0d2dde834cafba ded1d37ef497ee99ab7f47ba850d3d1592469939b25fa0dcccfeffeb6e487256 5f0c974156a91fda39cf74c75e8418f5dd735a30c3e1be7fccc6e66ebaf51bb3 df40904f087cdb5c2d375fd9e9cc1b8fccbaddd8f8b366bfd6c4c73d548c89ca 86786f17d9de29de6a6f3a96c1f15efd1b795d97f316b2945ea60497416c4d22 571c315699e3f94b0b49c31c9603c952c1cb7b4f501ebc975e7cb9aa76d22486 6b37e86d97644021c4aa8a7570dc5c75697d8a5f48c3d1dc40c5d3bc534ab466 37bbbb806e9bb6515f1b506b554a53590b078f3fcdcce8accb1503d83b4fef11 ad7c90bfb7c44aa341d1e52e1b2ca429e29ba4db54dfa33b061cf6ea1eb00f73 a6562d8af16e8877bc51ff84af564dc25a03f7d0d7535bd2c5bbd130b3e762fc 15baabaae493637cc08130ebdcc165d6250f863613d137e986c3318eb1e1d53d 30bdc4dc44932cda6b13dd13848dbde684a136dc36ddf053c021a789875fa5e7 486d213c4c340c96fab9ceeeb238077f728c3ace8b01862fceda8d0552b285d0 31b5688c7137a1f2caa7356b494db41e82e22dd6a8fbbc1d6f7f2c95bede6e04 df368afc6547d8757f35c2f1e925b2401a976e3dd656790c29df95da30aa7d41 5b457edbef079804a73b9b2c07ddf6795cba1dc7b2b378efd4e090206556c0e5 1de5856c9fbcc5b9320f75715d93718fc5ef2e131c202c109ec452576ad8c0af 57efee62fcef10bf49fa000f0859bbb6bd87601ddb2fb92c8d5b7155ac088d37 a66b2987ed02312dd641783d5e0667e1962271ff175d92a9cabd9f8ee5d7efe6 1d295ca90c2ca4e776946f589090f8b29ed75cfd4912b24335d1d3af6904a165 5836d1b2feb67fed61162fc0547a2989cff2b8223a54e304731ddb1777a53ce1 cf6c69da62ae9770b7cb9258bb6e8b14ecfc4d48157742c2795dbbc757bd9730 15d960a1be78b06fef10a1d748f4b7102e16e7b9b515b85e4ed56366bbf4d347 6f4084468f6082c7a3a09ba66ece708020d294c17718c8a15ecd4c84cea2e269 db08f8b2fd5a814a77a829c3d81da8aba108bb0a30d0ec79095f6272fabb29e9 bc4658cb9532f7dd50cbbe9cc3b69c42c684f1baed27c68f7bc61a3d0ac6fbfc 70118d671fc682d4ba0975f6db97a1e946ae1523a1602e980be873b3bdf7e262 100e6a645828abdf759c25a993f3176bfd861ec8b14fee0bf2257c6942f3cfe8 dd4a958e1f2f175763bc424c37138f7f6ace5505b4730c1474f13e29e9fee09e 981f550230c2ecc818356c5978e09c7bdaba2bfa50a18cb65861c757fd2a92b9 c4591c778f2af35031f0b8c10a9ff2076ebf4743dfcc683b135af85c68692734 ae21b69401e1dd790b5d0ab2a9f302f9596d399233c527fb03a3c04c174e9deb 1a479edea2a50f19902e1a6f7222d5dca50220170d3031d932aabbb2a2c60a50 ae222e075a61554b2931b48ffcced2deeb724b0931e57a7fa42c83dffea81be4 e09cffe50c23b6b501416c062abb82c4350142d512fa7718a1a7b4bb0c9c89b2 140813e40064d016f31c79e98732ff2488d8f5203755206ae3ff8b43ae29c2b8 755adafd8cbfe400bc7f0d6c7e9a6745f805094fa56411511cf71a842dcf5e95 d2f4bd013ff2a5614a0f46d8f8291a727974291ef07d6304cf43a3cdfc8020cb 77b077c9ab0974d0f7feda41526b5f3c49aa93128c71c20a32210320e1f64b76 5658127bad4fe2d7f6dd4dbc60a73717d42208a526b6d0cc42a0cfc72dc5de47 d990e1f4737f9389eb33c7ffa8b5ace778993c3e7843fd8eff9e5716d7dc3823 258ea018c125c4f407fff137b02e0b3f5746d2ea37e5b3f0ca6adcafd4d9fc36 15df78d82effc9eacf8420cc9e11aacac8d5e909cc54acc9c5bd7c4c871451d4 dcae2deb75447b6ea9fe455cb32c80b6d9e756547da0645f13d1089069238913 fe5566a38dbd13b5a0a18c0e6b39c5774a98c588d2fb2fbf062a6a313dca8e15 852091726bb4ee8baa253c406c0faf71cfc6214aaaede4a378465c6b8bac7d21 927934f87ed1e0f415b63e60145baa00c5974030a1c5eb8f24b7188f10fea32c c7db3aecfd44d679199341764a02c3be4a87466677263b8b7d05acdb694c124e 3afb101244955d7befd723e91c81a286ac7f651be08f9895d2273e2d0c49a300 2500f40884d6d748b93f852b6ee47039fec6db5d3377d28569d0fe76455ea246 e1b1e24c5a4eb749ae2e2cab6fcf3b86634b426c3743aa9ccb25f50d99ead1b0 c344cab976b25c1785abeca1946186a1b4d5c1ff5bbab59d8dfb6ad5db01a8af fe2baccb1e96d61f5be322ff3e39d5b72d5cc799a758de1313d4793b5a723fc6 83779c08a1567d1ead921064d6637784b8b85a2d18bb3facbb48e530cd5830fe 5560502b0c56672bcea621eb22343c543696ff0f25b230409984d89b083e399e f78636ef78c66c68d74165c4635fae714db6ddd9ad3b7ad51c7c45f125189b37 f344bdedb278b1a78067d1effad38e71921c10c7ee69b0f597e4a481b4a9b014 164d7bf3bfec8c94798fe21c02efb54b58665b26d7796d0ffa3679b5cfa02b31 a7e4fc8d4ec066b75bbe98d9cbcc648ea13bf6b32646dcb58e5590edec327232 03220e9496c7b992044f1d2f3dd65b6d0e8dbd4bf2f5fa7036508d9fcb28a147 6610c3995d2a266a69d4205027ac22196f9817d66da97a547a2d9a41ae582d22 e95249c7c9018c9c5e46e59b620e8e412eab445e832cc46cc99b4c9178100504 68 dc051e01a95bf5090b0c6a81a5c30ee2230bd6c946ae9cc01cc5b4d4e494be0ca5be00c329853f08d4e520acfd6cb627b307dc20600eca8a0d916d5cab538b0c851b7de41f13a230d6b174cbe94f6f45481d2f9a40171d6eb51a1c0603a6fe02d7a3d24f008b91c428bdbe4398a2aeb93997ac4cb0d8ecd3d6608f194894990892fc48cee0729b6c31813e360b7bdbba134ea93ffb24fe41b075ae8687ce400ed2524d8623d36e8312998eb41dba8ca73cf605f3df6be3bc0ceccd673dd57b0c4fb9efcc44e7d099aeafa41c03905fe212c26a90d0877d64ab9cc0790871f602cca960f5852539df513f152de3ffd5827978dc351288c5e0138b54e697ad0d03a5cfebb4d71cfa6d49dc358f4309a3d47d19a8d0d38908795a470350604eb20c57ab4d0b9896945f4a88dd69bf135aee3ccd44c777dac24379adc149f3915a0a12bb4c02b908c5e932397d6030aca3df38937a37d8f4883081662b762707e000693c2709362f1134d960efd0c121be47b599a3117244b207ca673bf6f777be0b8cd811a6b50498ca92a92a19d141a8ed0e5343fb87c41e99b3444d103c679d0aade2b9501ce663decacd5ae8478999631bb00803da5a40e81c8ec7ad896b4d0b90924d4b1987a19ca15bb9c5f5b10b79f3260d6d64cc7c928ad759098d69550c3ac2df95fc5659a6a61e1ee20cb54f9fb72ec19f85bf8a056e2b0732b42db702ef76cdf48529074860baf484958e088636eed5c2b6dd0abcc0e506c778dd710a4026f766ab1c8081b98ace78f5e53a147869c761c5bf759fbf820f40b7f1d40663219feec371b348319f4d674a50be827987373d8563f4c9e36d35cfef442c05caf61e094ee2c07729ebbec7a2b95e38a13018e1be9b22fde45acdabbbda55026e7fc9ee1cf27b424364e5411aff1f6ce1b0bcd404145498fc7acaabd15ad30e2e4fb61d4acb3a6ee0ffaf1939a0be06677978fa7aba04d973e4687d7ac2fa02c0930e5e874b48c6f7d8714898ebaf642597d5249851b5bf85b9cf5ebc247d0d82d1143abb5c3706c629c1b9739f7814366ec1fd406bbabfa25e52a30ca9fe06c3358826e931d82e30e12d2eaba19bc5d82c53cce0ca3548793ab16128a8650863df1ed605e009122eb2ca75c962bb5c4108961ce12ee957ecc212bba620ab046f96502dfc1693325ed41f38331c83ffe959cad53734f5c20bb9a9953bc7c6092c6ec30294c4caa14aa1a41e0b4d7d68c41f0cddfb2350c07a78908af8edab050b6f2c46859002f874ba8f8aad578ff72b472129b4bbedad565ec4294961a60fda096986f5863aec6fd0ecb0174058f52d0aa1574cec66547db47ba81d5302028cb9767c904744b20cf80a8af5fd60de771c4df2dea19d648289300e2d87d907fadcc3a4f482720b1e0b7b21e4e392dc8032719e12bfaf19aea01bdbc1f7d006e1cc33ce14cde0d3823504d6326fee43b0fc44ae7e7fd38fa6a38b60fceec301d2e9b824c501556fb498abcd38cc1c9afd68b0bf1c01e11d380c985f45ac9408a5e9466e9ef02cb335052f2b1073215b1a16f8ccbc46aa01ec42b1defad4d800a22582ba59e41451bc28f3a8632fe3761926fd2d82dd56374616dae1a8cc120e8efde44aa4f379e92dd694586e52004daf0f35c10fb41ae10751adca30c61d0a8ab0382ce938ee6e457925abf489d4291bd43c4314c6cf30ddbc5a16a2e75b0713d49666c219e12e0c52d1bee6e74f0723eeb16e8d17f95d91cdace8ee6cc00199d0c661f39021ca96be4d3053f566ccd11451749bd77ef5654d53c96177180a5b62159b0b111e8bdc29bb5cf3a866f70636d341de3a2b15562bf8139cb9c301c937cd6e0a5b29c471c9c22ee2395170eb4946f45f6c7bcf03f0e69ccbc5280b4d632f1b7e8aeb92e6dcc6f4c59adc99c7711531daa387e6a5197b11b35b2f0d9a1abbe7a2be58f15d3648de884a855e8be284da30078167e4f41ecf561943044083642de348abb049f2e0c7ae8ae5edeac583574aee572c9692e2e089fd9501622cf6346acb8bc59aed648ebcc19248f0399a286ba21cff0232808d39ded00bccc01d8cfea2777629ab002462bb54712cf0dbd7f7d5679676104a77c7b2890af00796c798a78a3a4e9e1ff9e3433231c02efe3056304f8b02ef93b85873110c66ffab4ff083839e7f5b7d2daf7db0a9c35c7f0aefa3b6b230a5f5aab4a6a0036005746dd22e1a4134075d1d9ee24c72aa7c9843c1c9562611644a9aea236303b1191478faf4e4f7a99c3741cd294c1d9e61d18fc9ffb16853640f402d3884016668b76f1d0ea844212f43b649c016a4e30a33b471da161cfeffab4ab2a59b0f9e66692763f3797b64037adb5a57396ed94a3f5373a0c5b782f1eea94006d10ceaf552c3c68b7068b2bd223abca4977ec6a6ee180ed881c9c09c0114b9ad820b66042feeb500eee8b7f5b57205b5e4be05d2e2ac9973b74e733df1cf3aace509f3c752e828f3af4d9d94e054540c5bb96887ac025a4802285e72733b64d4120e1c23aa5f9cd78a3d6165ec6b463c2f9fc67f3887f1394d7a8ff7b0a779483d0f602b51c512f29d16726f4c0dbda42b69bb909eb85c8d3680cad54d6d322d4304a127370f548f0096187c90a867f7fada233464a571419a4a370ec7476f78f80b04f45468810eb03f0305054b9c771748595686d9ca26af4562ba93490f993608eded0bcb2d17354bf8b08a62bd8313b3dc1d6befe8ba9931bdc337520a36160cc1d45749ef5d55e30df5e551c9dd84af1ecfbf1bca8bd7f05a70440f76f55203fcdb87dd42aee80293b1e89e3182ce4e1d5f1b7c5cf5cbfdbb4f4bc7af8a900ca562c974e870a3c2a05e8d15316c3f83edb7b9043e2503373c12765363bedf01c01f138f6700c9021da1d362d0dd000cc7514e78a0146bdfed847c8f64edbd01280a10add0f573bc2278154db5078ef688cd91df0eb1bc82bc6616c7207574012fd09b489ac09ad2544e7aa922328f67e1e2b0bfade3e03cf09656d7658a0402f14c10de89893b007b15c52e8bea8ea89c8e15c47067eee237079a08c0adba05391a806b5e329dfacba975e6c6f0c639845c72ec2a8f11872a5ac3007f09980cc730aa193cd66ef63bf52f9bad55d68366cb07421f2403d71db514556e2fe900af988b561771ad0c71af192b18969de454111ce873f62145370aa9bb7dfd7709f6591f4843a2539a91b0cece37f5185c8b3718fc49b076f05bccdcb3d475d807e602ab60a086453f204622a9ac975c263e048b3f3210dff5908aa3dfd46d770f3a2780ddf4ef203225bddd898024b05123aae2779887fcf4289c53b35fcf3701a625ee97863203f076147f221e7184273a30e99929aaf28d2ca6ba90b6f3330dfaba4fcae3072b388cf57b32cca39b06d634dcbf24f14971d954f96472081908f7be2c7122e71c77c39c89af70b7f93ee6fd833be132d8621fe4d92f79a6bd000aa9262c492cea68922ceccbcaa0829e62a61ecf1a83d95cac3e08f4dbc7a209f295530b04cdbb0eb8448c3679b0e0a2a8c8a1d99de043919c1f7430aa85670aa0dee47db68e89349d8b3d3cc889e5852c55e17209a71c1ae598e7167fe2bf02493ac42f91afc58588343a20c1482421488686ff456f1aa74cda2a103f38220d3a6448890cc050abad4dafbb17c0e1fb0db9443fee995b1d574287046ec3570d2208e6cc6ebd0c864fe47ac692a06eec096f4e0f9fd469355001cb864262510a46e15ef5f45f3370a9521f402369ba5692e081678ca073c7e89c0f5f840b32039a6a13ada04107cc722c36b1f66e66e3d9443f228e186c407a67a958fc19110b576585e343613674b3f63c1082e9f928f7eeb4aabab3a09d115936f7dc2fd80a70e8d45db877ae0a62ae2067bd2b9bf2c94a3e8eb32831e22c591054c804f50dbe2f59a7d8c62420c2eb8f3b73df5d328d9a87cae2dfee1eb88299769ab6700907cbe9b7274b904fa727b886487a6cc8006e419fe53c20cc858f9b59f5b67d00fe80d668e875704c86f1c59b2c228d057b627ca54e346bf79f5a56f1d623310dd187731be8b0803022d94898a7cad048dca68256976969650c9633f4aa09330b7cd80871c0f15ccf3311f83ae7b00af38cecab64a04319fb5a4aaa9377e5c502f2d64e1db85603b1241666998caa97026f71d57b891afbb4474331477d17540519d23ae42ec8181efb0a1441e48bc0aea6037c8b77d0b0933e06b20d9d36d70dc13e95f1cbaa6518e59c1a91c42f81ce38b518d526b02dd8dc7daea6db0e9003e8efaaf5c8f7fc780e5010069670d7e5702748b4b7816a76dce6a3cdb4899e0ed29708444f7d200da63740fb7c6537f6f80feedc6aaa8d4d754c75e0fb30e50f6ed9e46cc0085ce15097d5bbe5573c157b067a0a9ac489e8dd0ccaf947ce7701eb39660c0ca3dd107f1771c5663edd23636590677f5e9be06152acbaf383690a640b14986093df332005f0f3f60a69905f19d1a9cbc5540542108a2c199f830f3a2d1c5e28c80e720185cd62d43ccd697f0ba0c95a770751b6091590709585032b0cc3a0a913db766c5ff09e036722a61f15b8648dcaa864fe361f69ae6a5d002fe49cdbdbaa26ee7982755f756f4faea78040447df2832f49e1f0957bb4ef02a1b6b560bb2d2b474b71e16c9e4cd2d5a1df41d2df68fcaa78ebdb70badf340ad9de775504910be3cae4fa2e2ba919617667e591fc6ca7c6d3de32aaec3cca07b5b9637b95adc97dc6499641a1a31780b4dff64ac5aa8f9a7d02db722c577707a4ad72d6e5e4adb18325cceb4fd6cc492510e0323def59e62d968a870c6575090666f02b6df7baae501833f8822f785190d4a4231abb04a83ba8f39bfb7b7205d9a981bb8fb491f8d37f8ff4314d5c1bcf6245c296c9fd4a9d754573bd5d5e0d2bbca164ad824025f644588b431985d18a35f0cec6bf7ca6631ce825e670b3068943b553c4891fa50a9af200122bc97a262a1e6d7366c2329284cacd8387ac0dd196d9d8f996630d2ad1ef99bf5d171317caa0a0fb94c86115162c5b9e76750e4f5a081df272eedabec854c4dc9efc2aa3acc03e5cbd52980ea8be434830730098b71bf670b5ca1c50e7228f09ee5760e9fcaca30e80568e5a2d560c5a49b704353cd3982be53a945d9bd28db2e2628e47c3eeb3e2b07781fb98ac01958ed90a77de31823c9cc5b7aa1f0481edb215cdee65bf4f5c9e0f1310870b4204706001b5b9bcceaf34d56abc9a111125ec04a542938af9f39b20c8a2a7d9c78dfbbe0c4a41e595be489c4dbe92d040817144e9d49b4e32e125f7609ed031763150e80c29244ebcf8833f2c9afc31385504c302cde0101519e823c8000725ed290091094f9ea1152c164a25e6785958e304a6b72a986672ceef6996261ca7595cdb4d005d5fdc609d88b949f4d628b53c8d3c5a3ca33dd8b7f742e4f5f32e82a63f130485e9924b86256a14474d87a1a670b672daf38f5d7c7ef1f7b9c45d67354f1604528528ddd60c139ca0833ec2853c3b847cdcfb1195b0929a8be690e7d627f701980cd6fe9250a84a92790222193217deb7c423256b9dc795e1a61cdbeccd780d3be9d2c0117f47cd278f87e9683673f6dbcb6c412e5921060c72f691c61540049b96068ffb5129c7038d7498d14ee6be5ae2db581399a3e5d5b97fdabdfb770ad5623fb28b4e5dd1012af0edf9f3bdf8ee2ac859db2cc56bad939c2b237e4a05d08a44534277230f4e144a462738e6bba05fa08ffc42335e3cba52431d62450cfd734241def81f5301a1a360d3bd8fb361267e76b7921d554a0a18be8ea13d0b2f37f7167b7047cba35e810975e574e63fa109ddbc304aaf543a7f250c4cf10b4ef39fbd2125e53aed3bebc387d98b82b31b786cfd8a489254aa6a4c29ac5f05063f77bb3d8f5130d2750129eb1fd1beaf7c9700a8814662d79cf0ce6440bf08c1b5312711bf66bdfa6f7e31b6f3146302a5bc13f876fd4dac1ddb50a462eb05a292b799f071a599c8f509f14357ab9e2d64f0bb4ff963a3f0bea411c24e5b07880a720d52e1a9fbba609a86e57149dc1f4cb9a9e98c7e42936f1eac1c63930a9c9602e214e422408c6620069839fa1f2490df1eb01d57085baf334cba8d790597d487cc1e46a8eda4d5c250e0e6a23f0a4c2c0a0467b689a7afe4850126cf099c9488d1c2f62015161d5a9197fd48d057af2ff3c99b918189d8ad56ea56200afe2ac392b3fcffbbb69a3cf91a5c7e611e5dcc0822b6bf453fb9303bf96bd5011cce1be35dce1f2837c80df1374800811bcc9f0170588b3f8cf012bc7151eb0a0e22dddc7d5f0508afa8a4352fb218295d78f021bd54c704d9de4874f258ec0a451e83ed9ccd023c0d35ddca5acb2a91de6586c617d942e97a34cdd2f9533f0bf9d319a8965e7ba75cdee818b4c0c1f5bbf2f46e97808ed3b498f79a6857fc016d35d1b126fd1f1fdc6e9afcda7a184eebac4d82aeb9ca3106777d5e98d3460a23ac4da841430f2936337645611bba2d65ba1481be6959ab29d0901ea471780f8d15a5e151d55015c22576856bf054bc3d69755e253a4a34051dffd6dd45650c3e2154f5bae965173ca2f360c44eda8dcc07771266aa132db5bd6ee67ad27e099b2dda62d907ea9d35d3eb24da2e11373093d69c261d3e72d70484e085c0900ce4e35cfcbe7e60026b4590f2fc098f80fe3b410bd050e957f27ceb56ce187a0bc0ebcff5d60750ac1daef3458a9ba47a52738e87d585073975078b0144d06d06546f15f137b3919b5d5ecec0e0b9dd5b892a25b3dfb429462c9cf4d1ca005c0eb21a2d9a298940ed8a9f46d13bcd36908946e563034e21ce01eddaa8ed112f09e293e5e7cf56f2853849f16502e76a124e2ff78573dfa8a999468c1b67b8e501970df04b863aae3f558331f4f953bb357d8392ab66fa82b528fbf897da00f2094879e82a71ed2baf5f8ba0b8f616cb50b435ed56a7c9aeb6fe9d3d06e01cee0ba16650b6b53f852cc229d46a7dcab1f6a9bf0475ec8d98acaed8a916d061340c8fce4968583717b7673046ad6c2b402687e5f0cf86832d05db989da49431b00663f599718e68040631d86af65ebc2455ecb13db4678bc4db94e8c07ad07f0703096298328c559f8ad15896177b9ae22ac5300df758c5f5c6e0470071ce7e2403e7b9acddfa7a8b26dfe474b09367d55432de66b45203f75bda06c58f5eafac07ede66027b54bd81ae0b376523bebad5b392bfee557512abfe94dfd9a916ea4005b76627163172cdc7874b118f219cef35f8fb9e24f7280e5d72d4c0fd1adfe01 +generate_ring_signature 713c4c8803fc00e35bf91007f00baba57486e0d70d332c9608d09b6682d5a554 794244933962c44feef2118a18c784f069ef776f495f075dcc22870eef06fe25 4 d6ed7494656d90bc14042ae12e0dbbd7b9f890d2347417b21830785a32fb3b25 dacf20780d08fb684de5ac429259951b81deaf781a6e31dbcda6ad07f10ff0d2 d0e455ae02f0be142f67090ae09f49ef67d65c5ca67a2edd0e612bff1b04245f 61b716ef95d86ea4d50645d4ecd717cce494c2d4d42aa30fd13a45e3370a7f34 da2cc4f0ebc2d7f6020c9dfe8ede0c1b216075c6c2eda1a9e10969a69a35d10a 1 d6a43c90b85f2acd859bb242fb1f9ab325d50092f755df8a37092da21bd8d701776267c1a9f824b3bb52edb31a55346c1b036fd9fbfc441e1cf1896d9a52330b18cc5127a5d74b4955c36ef99e8d8535701a8d0dc4c449beabddd6e6d9bb4b07f05eee58a06413ae45e2fea73d83413112fcf3a05c2375f89cd3309f590ceb03dc52ac61f6c8cdca14e30c49570bfc7b983f6a86636b451ec9317d5630174e055e72e58852998b0022d72bfd9bb7bb924cc4189f33f8178873735e4e6190160c7d43d52e958605e89fbd223c62a75c8b9190a2e07775511b2983e65423cc4000e458bca4cf0713aeeb81a73ba918dbab9631e1cbf5914ce74af2c62975885904 +generate_ring_signature c24d45d5956e2ff05c5900e0e3cc833784a44af630d7aad0beaa57fd879825eb 359095816f878d15ab2b0a2186ffbf5d95c777c21ca2cb72d2266d083a4a5809 6 bb3e5f6ae5df3c4a43310d86dad1560abc1f6626a09d5a76eddfb78d652654d5 7a4ac8d3528abde1baf73df38ad03a857739fa446862d1e28c4ac3c83edfd15a ecc674b53bb166ac2be28a7d51e66edfc9f95a324277a2d9d1b908e0f65c5e9c 5841a26c6b246992664b8d8b58769db51a4f7a194fadf2c7a15046a0e2770023 75ee9e843ab7975fbbdd99bb16392452a10f7e7b38144f29b9d706bfa22cfd65 3de3c211b808c4235fba01b016862f8305f0deaab1fb58823f1be27f151acbc8 9b8031370c48fb1cbb36f400453a00108841cfc20f4ef9d9d206f33159cef60c 4 6f1b8ebe057c8ccdab02361c0b26ce0e5d625d19741e8b5300423aa0eea6e20ec67f938ed8c01a4ac90c61578ec6519e85f87e7419124fa2284c84be6ccd860b7f519abf1b744b8571038bc38589f17fb3770e224ff19617c4f710a5a5f6280b5e8cbf78a2e7fc08027582b163891e642a82a38672f5b7ce688a36fc20e0d50ee4e38c85afb5bdd0e9ac8def5c39a8849aa7f66753f1112616f768e1026ec40a490c71beb31686b6df5c3e51ccb38cf41ef403fc0725cf625ea6094ea0a74b00b874d65bddfc11d9034f5d4a273c870bd75cd07bd421f4f915095b6aea434b064088ae3e2265d345d4a7564c35a19e5c9e3b5a3fe6932687695197b419c4140bbc7d73d569b62f20155c97278c440c3b871290195e40b58be6c7b08767acaa0692e48b4a8ef22a9d78dcc5db4ea50d1e601df7c59e000d6042a4b310692cc405e40e12215380a43ee0e23e586666eec332bab42e5baf42c3c300b1547df5f902fb47e77e7a0b0675d58fa7cdf28d7efd405075587dde50f1ee1a4e4d08b8c001 +generate_ring_signature 9b9e3fcd3f946f6741e6186234eccbf58f97379d2138b6ff985ede15f22f113d c3b4076a7f669ba7d594c52e660da73a565bde7416c9bb99e06e9259d325724c 31 c468ab3f7c3268f7a1839d256c30bed690689a39994e4afe3512c765f2b21ae9 9e8f235c70243e95b84ed1e454c1c86b10446b699f059f66c46e5a1b37dd396e 5742fde4cbfc7422341b8a5449fdd125d30e41357237e469bf0fe77246577442 5402efe6137d07e5b71f0a1192ff212ea8959e080d32b012f937fefd0edbf337 2331347eb9271235c789b6d2be8c6dc18713e878968d77f72b9d644b16f26dc6 0bd583c1d0d8b4b2f8b19609c8d928eb4f4196a491ca45fb1f7d27e9b1a92c5f e1841b87e3d529490cd95098d08b589f51f0c84321b656ba6e0820c41bff4574 aa142732d8b972888a704821a22d62e92eb09b620f0de227981a7d9612333591 d80b7da6a8b938599b5dbba5941781b1217e4ae5a90efd8fae4a65c49c30ca3d b6d8d385a96ab10a4cb6a82df537be9ed042c8f6c42ae261baa487f7e927a384 131a2aeaa5db3afb87093ce9ece283ff1c3c8f7ae83f016612a4d3c866035a54 23cd7cbfd909e99e696f371875e9d7d6fe1f99a0a091dec26dc79fbcec6c29fc e496229d63f8c8b61f17f330dfc30417adc1306275b55adebd8b2a2863f72c8d da2db6040913049257b6ced576f35e4bfa431f25ae137c6cdfb562118bde066f b658b88b06cf34f1897ed99a26a20b0eef67ab280913d42a688b3d862cba3d09 da1f11e2eccd81c0de8498f22659197dbb66bb0512188ec900d44e8036bed70b 9fbf6c6e3f494eede2ca88646a1f3c3e3c40cbc0216227e0c474f5ed7355275d 5246031ef62f9bb1399d8c444a9e3ef99b0da76ce5cd7ecc882dbe9847d1d62e 8e4c0645b710436a74749e8d5531ed8719df52a68293713e61fbc45d5fe8a68f 18a5567a6e61cf2d3bdd2fb0a58a39783afabe754ef59dbd81e370e35954f1f3 a4c53ef53fd1c7dc628b6cac4bf35a5b84ce3703c07040924d2db4ca523cdf73 7ade9695fadb65c56bdbeda04307b304a04b9d5876a1ebdc9dfe35f7484a3ea8 82c7f5919c40ac93fe9c30b9b50b6353dac39748634b5afb52eda0aca5c00c68 12ba3162a1d7ea4b0c110e4b2f7b6f83a3874ebc983698258e9bdb187efd2ec6 62f6e20381447fe7c19968f5eeaa00c04df07ad01f96cdbfed367b6d34fe7b33 f27a9baac0ee08596f753298a41c704066556ceec1afe5a3c300e339a709225e 6b15357d01194d299f5dac3ac8dd4ad6cd02588038c3d9a8fc4cf322cca7be66 bcda402e1596fcea31ed893a65251f5fd60cc2b3571c5fcb1c8418bb6ff390cb f04d671aa35a499ff64ac633e18ced492f00946d4e3bda70d6274350ad1c313e 6b11299411b8c35cf07ee188537faaec524cb4ab00b28055554673450183d288 cc0b067ef56c580d768f01f0efc8bf7293e408d0369d5c3f0666a2ee4c70c553 28c2450bf0e73521266c017a04fff88bd10933d316572e91a497ffe3e9a48f0f 15 f52078f936f4fc464275c8d7efdaaf204fae9f54859d771b611bb4f40726660d6ae2c5c5c817c1dba6aecb9c68ca7920f9419f01778a45d4a780016faf22040852e1bf7374585a6c2daf275bcf53bae45862f7fe0e7e06425f0c1580f3bc09042af1873c896a9e38d3b3b9a0bec1b5c2834b5a34d1768d1c72cf0f7407c99f03dec7fc38b807e0e71fe9130516a44319a94138ebe4c5454dbe2ae97ff84487099c7e4dde05266349d93a277490350f5344662695ee32e25d2bf81ba9810ec8065f6bf1042e41fcbf0d39243528fdf6dc2c49a75440f1d4f2d5ca5c63eeff400b2a5fb4a6d99e264ce6a3377ddb452fe77b6d4de1e311c35b776f1af20afbac0c005cfb8f3e85e7a4087627faf6f5d6e2dc23954395da31f6db131208587403096f83912d456a82c29cd8217a5104bd5b9f3c447c21cbc325a3069e93fdee3209e31576a78f0f255e6f22bf9d344f4deb823ac8ad341383d82a07b8bffe1dbc08628facdc6992579262679cf2248740f43714e830be162832a93d90cff1eaaa009f3c15f0d7bd2ce540f18b8715d85111b86cc79c857079d33e97f4bf1b502507bc2eedb85db6f4b28f3eb957a8d21fbff8234773e43e697b01dfb8f8c075d60cc7bf48cf9fcc106d5ccfd308e777130b151e23c2eeb5c1408e9783fb0e82aa0cab6755bdbc5a231defecec0c9f19ae0b552a941be878feb41abb94e0e3beac024e4b606268058a85049ff0096ea006bb62e912a1512dbabf8f7ccca41d8d960ceb3a7188b2ace696ae4827156dcc91f064e95eb254941805b77ae2650f00f70a78b4c200c690bc0a0dd392169dd3fd3453d91700e87a104b9dd051018850f7042e30f42cd278f1efb1d57ca40bb0c9be1fb959fa6205527cc325885862272c098357c3012fd27494789a50b4aafb9d490b7b8da00480add7c400c820477f0d03dc121832e5e9b2f4699cb8885556645f839d639547438199ca87f14b23b423041a8fc77436154d6633695aa4916bb0eeb77927d31fb7389ad042b5cf7da6f40b16da701a7c5228786c9aa5fe0c3b2b2abbc973404cf4bf0cf77c2ce992f88901ca7910708b537aad408351b23ee31e8a71f2b154f39ee7ec071e7d8617843e050c173fdaf5104de4ec725f98088fa8aaf6b2ad1b5e1cf535f5e45b303bac3a0e49f324fcfa06ca00b195f994e0d5187d5b07482d9bcaea296b272c506f99ea01dc6cad19632c034a01a546ecbe8d1be78a733653e8dbc61a721deb5eeab57f0115ccbf2e2a70e6cadefe5eb104b6809d78ecf8727519d1716588a792819d72085603969d35d2230ca826a8cc760414c7ca768b384dcffa708bb14abc3db107036b15a95b9ab54d3d3656258be0c84d8eb0427b9eb0aa7af6d74578ad8ada2503d8194b22b8ed704a88f181204eca01d10616b29dd4d7047c08d787e3a0016f00fcae305a8afe1e0b6e2c4d568b43ffe828d0c40927cd5353db9a4f13b8d14901be99ffba31fbf182cc90b9e3f1a9f1b501cdd26e47b28ab2154b27247339460759ef84a8b47fd2d856f190c384f0265a0058937e2220c3a9a10fc81547b77d0da8887f50b80d5a92185ef165a9eb170f477b13b95922debee1b43516ef599a07f54c880d5fed6a106e018792ef76be1abaab1467bd9f2d313b1189e63fb7a30a2fd0ea02746858d87111eb70680406c86f4027b03174c08e7cfa20b9e266aa02b258ada57a843ff0036cf1a881c4fba860dbff1d244eca76d114b6ab636d720574d0feaa8dc4fda1eabfc723de785b97d2c616508927f17b28e571d98a83ec0dcf4d22101ab265aca302b87dacbaed23387bf69ba86d5e5b5bd4ee97671da907db6240529de4708fe305d067720456dbc945a219f0c40a953d5a23be5cb1de0decf6e7947312214bc60666791e71fd801112c2352c9bcf3031ee5f821bd35008a58d8eef0d6221083fe92ab0481d7d0deec9800c923dfd04c1a8a0c152ee140ae56e7eea817a7d4a63e721833d72c9ffd3bc6babb949a5bdff4eec8c612e44038e3d59d95ff73bfec83024964f4456af52bea09c35a07774290ade9c77d7600c24e3638ba1896157bc0effea656ffb444ab75f36777ba94f158335f10ec9ea07576148d0d880a3f568ec361689e86aa9eb4b0ca1942f11e28fca9178c352ae09e0332d2cca0c9177f907a38ce644bcd499d3d0a6bf45b8fcf4a6ea90c98f01019df4cdfd3986023dedaf1f12a2d6c83e3eb223afb9b7577db81f12b0ed094901eb84118895d379330ba82914aa3bbf884f8ebf703c99a7bdddc9d65604b43f0fd1d54026db98106a6e2b5f05dc7213ac689ff0561f1940622f2b50fc5460e705cd15d45703bc316772cef60e91a7463f7ea85a68b77de5e387c2b592d708d800cf16d0150f33e8b535a2a66d37077a6fff7d4fe21a70e1a64589d22a3f7e680f5a0d323f53f3831d71127d98946e3ba4882913c8039c7999aa505e7285ea7c076a6dc4ee6639d14b2038d1332a7632283ac9116ea7fb823bc37ff901de5a020494f9b96618b9da1b7794407b91d97464690be79eec33480aa378f31bd2ccf002156493a41e1f852c89a2e57eb878b95c87cafe8a0401e2897856ea853acc9e0bb855e2a398128226aa239206fa5d638de4656f7507b4d226d20866bb889a040cb88e6805d0673d552e46fd9e436ff53334d3fb5423774b0a3f799fd83114e80dd392363e5095333117c49a962825fd195062e800bf70c56113bc43552e51860d238d88360ab32c01657adae7ef564af13f9fd4204866dd7da5f17853bf1a2300 +generate_ring_signature f31b094e55ac7802f86f73c3525a6cd8e769ee880a8c63aa2f5992c255149a75 b17df6621687c3f37b1a5ce5ac09d95546679313dff994083f079b5cb462c930 2 9085d0eae6ab0e2b4c9289ddd4b4405dd818dc5218188920df043336d491f918 5048e0efec6bc401a73f920934508f4d9b99ef5ecbb439f178b7e65c7d08f410 39467bd15083c5504b8e2a161aa69a6e54d336270f30814bae129f1a53972306 1 0415bbdcd6d894bc056d377cc4ab1f08d694bdf62c572f6c359a914c1351a50bd378f1467bcea70b154150670ff65547891b4857a5db01ad7b57ce416bef1603e4ac86a3a0b160f999338a977380ff22148995358a87227cee8de27c9b22750e8818ce2639b448b0abc1c4e6860443e426f5bf045bbb723db9e076bf8c72b30d +generate_ring_signature 2b1ed38fd7d880a817ba1a9e09166f4ebf6db200b718496e4b0e1611b699a71e 71dddaf7adc360a91390f506b386c69a1cfdd89e948d803863f742d9fb9c5453 11 7670293c88d3439719814fa939089f77b31911384f46863e903d78704c004a4d b0bf008c1c329ea891d807a1d7ef55316b480c19dd90a00233ed6027d566aad1 726dd2e15afbd42415d0bfa6886d84e1f3eb9442f71b4088b04c79747aeab11a df4a86c0db52362d3beb62ee1083fbb3c400553a99b793ffd20d4afd8cea6a14 ff9ca05329874655d5be3d882099ef375017081b3b16a0b7da8cac1ed3820988 88baad2c438cb5208973869e561254d3a38f4848039b445b9a4e8edd2522c325 7285ad0124a73272138c91e701daa44aa52cc647a73351f1f842bd7454efe5ca 0cd91d92f61536a7fe4f7c9a6dc04e2dd74dacc2f2f6a93af216dce3f69ebcec 0062e10dc722a08dbaa346d3c2070973ccfbd511dad2db35f77e8c31eb95b913 a9290ab629ce61f962650140314ba4e6e23ad47e1196460bc33f6ee92b18eeea 6cd25a8510ff4112e5f2c4d15801b7a006db3092e24e2db9cc216c7834fafcdc a5d6b5f4dcfc705c6f42f3ce3854458503a5b82742a2857903bf9a57f297e703 4 4e922754f84435d2a6bba08b1d08cb54b84cc3d64c36bd893039466aebb48b06cadf8468100b5e921eb05ba1ca821fa8e6754988db9033e3af252e66def1d308a5c106a7964734bab8c5678acba8f5b07a39bcd04126d2d05a1936e7e1ad570fa8b326e66fbcb73b86636b12e7cc728139e4d62197f428fece963074cff7b30de8ececaca971b44fc0a19944eb7b48348f8da3b0c9b4e7e9ce4f0abc618f4601a815be934cff18e9662771b0039a39168c29e2ab2b05baa3fd8659657f5f4c00c32894a8cc1754f7ab3c13d06dcc53276ef617c6382fd1b4bb4b601f688aee0c23890314d6f4509ec1b00bd8a3df2775a5032c54ebe6d1aa6c9f129f4214bb0f21e6fdce50834183a9347b3c894eca2c79a2be2738c282ee62f40409a849750b092838c541959024ee799f8f2ea07ad425d1c9fb1c1deb59105c854a613600099c1476b2027ef8f475e15da2a8df91929ca0f4ce687590d95ac9e6e2d0878a0ad8c38092dbc484a278b1a0ae2da6e9b1fd105f3454dbdabc11c39bccf3302000ec9ad809de0b247f86d4e38e644ff7b29d52cdc5dfb23567232955dc7171690219f164a52b7c37b2d59973e1e58cf76cfb38d02b59a206d212093492442f4705e8969b8450a74c0dc4d9681d43ecf433c4e72ae8041f2fa757c562715ee5470aaf2ba2c951a8c7d6bbf6fae29b2c6ffc193c0af2fd419c2e87d6703f47465008353fd035ba7d3e29b40642d905a47235045fac3bd0d65c186fc83543e82fd5059f02cf54a9998ba3ef2e3ce397fa72260c64ea56674e8e9ef96b34d3d05e1e01373c5ad927623a312281fad2b6d8d0c4292917304c147df8e1aa37e6517cca09fda6af031fa6ee63e0a6692c9cef71143458ad073e3bc956d27c3fac68358606b1efcaaf2118bf88255ffe8318c1472d7ab5f4175c1266514a06e0b8233387025c3f5e3c9d46429925800d464a5dbb306cf680b5a8c6fa047fd6cf299fca380f +generate_ring_signature c5093dd27848ac8566eaedc9052fcd6197c28bf93c33afebaa7ce2dda5876a00 323871a9a595066bce864c151cafd777f14afb4943761ad5a574eeadf5cccb1c 251 39937708a3b6828e2561d337b4bb62a05329a677891317ef1fbf021efde4a32a 7d853d909949ca86efe934596d599e2d77997f771aa4877b6f66c9ef477cf18e e983c352edee9ff1590f1b148b377e4a2d5acf31be2fe34df8f6a8d4c4e4ba05 7e1871074a0892900d96e11c48249bf88d429d7bbfc5f0a1029dd27ae601cce6 053b105d261dafab41652fd16d5f6fd87e2c3802d396097745891a1f41af5c99 3499b5ae6a1a148a140469d676a0f42c4422cb71202196215c5e35b35dda93b0 cb14c49d87d8a24cfb716a35bfc392ed5ae3695ce41a9c65c34834f3c820ab4e d4d25b34a483a582179cb1c171532f16f1672c5350950936984484e8f2256c1b e657b8c50f8c92b530cbb8d9e77f3db440326b397a54ba56e3cc6a0b123a092d 4dd6a6ef7f8d5bc9f80693c0a886b0c4432eeab776c17346ac19504f46d622b5 130e7e54142cad6b55cf6143df37f9d206ecab657e1fce433ae278806ca1d26e dc7113def3d74145205db0179dd574c8b00b0e708ae5eb7510c2624500274532 7335c7e6cbd4e8153b9c1ee028e3412f1681058440bf04e6613c49fe1f1bc75e ae3948dd1c2d07972a05cb741943e766fa304e2fea461cf909eea69de08d4e79 28408929a927aee09552e8a162937eed73757ad37ba996365672f1204ad92717 2fdfbeda21d8293c0594ba9671d0110421fc35888908603226cd7eb0995e1ac8 3cfad5a488402caf59a69746fb5cd739782d61d6e2cf71c57bc8c1127055fa19 cea1d4b9951267cdba6ebe4c6bcfb7ff7f465582a73f3b8d60726ee1ee4564ef 36049feba81aabdb2b44241538d15f60fb851dfbc19d13a6e4de240be745fac3 d4225c2a253f2cbd16bb65b500ff4369240284ef1a4cb1f77e6e8143300d0fae e8ce1f23cf3ec0f98eecdd9c744f9894a5c8b3dcb0b29431c4bda4658b4ae24f c1456aae2024890d5c914e9e0a2926e7f004c2e2532572ce815a9ab91f8e282c 959ff70881dbc4521285d80eb7941cd4ba94418da70f041ffcc7479c7c513ccc 7aeb49c0c8b4256f7a718665b575b13cc0688964a8c947edb78eddee1b32656c bc18deebf265b86a577800c425c3c194a390b295063a87aebdf816315e04a0d1 e3e9862b6b0ef2c15cd2058dc35e03c2aafed007e25ac3aa59cbe2654afc8d42 00d1abb0f3f487207b0d764a621ec24265e35ef7d5256647b7404976a8bee167 9db522d9789398ae5954197c166cbd9f876c342836c100364716a3d23a3cf885 30a63e8040c68e413514de31646a18847214d70363850e40bcf5e9f5c3d92ca3 2a78fde45151e821a1a924b5241a2403ccfc6ed62d2463b09e7070b7279c5a9d a26c48fc6e7e82af42fa3cfbad13812bb6395aded22dd250b79f47ee7dfb6581 a801330fb5930a20ac3e1d928ad94a70faa44e7d8319cd6c781d3d5e0b7ce0fc 7ced5508691face4c0630aeee6dd57f510911092109720982102ade5361833b9 2232855cb3aac2d84031685bbe847619d358070216d20f681104f55bd100c2e9 6e49a809ca9d63a6a21496ece8bc4bcf3768b8588456ec1d909c2925f60e995e d3a035827bad58e5a65284003b112160f3d2f45c103bbbc13f00a92f101b2e7b 3abf72f7ac3ffb7fc6113fc42e26031badc02e4d8fdb3f7f08b436514c3f5a31 2d3b155aa6fb6ea05e5a8367039d257fa0b334039d5ea0ef11fc56b8117c306d c7fb3b153d7b88597b92ae7d79f0e3185716dba81744ddd9ac1fcad2e0543fe8 1325b0d92f95fa8ff2cd42384cdef4d2976e61e76ff73072bbcac1369013789f d8cb35a8558bec532c929325f135119af32d6e46a2aa9931a7464c6d469cdaac 7a7c8d38b604d98886d467cd766c40abc920d751c2c94574fad91bbd6a8aef5d be6b00d3cb61d3032253e1a7347e346dca1bb51a92a3eb8b071af0c891665b37 0717cb474be340eaaf4a144b64dac510b6e8e63e3bf9e8bcbb3c46fed88b0f52 3542fe85a3882427abfe0b026c12ae8d195a3db1534254a8d1af81d25992489f ac694745614759040e5dd4608aa75f9b570f77d2e166364d6912cbb21fea0823 5cd8f7e533b3c94444c897c8c91212f0a228fc719ceb8bd55a5c033700dbeac2 872355c0b5f2347b4d2d7fca96f8df10e41b45b9ffb72043a531274409494385 ff1d9017025185587a0a4cdd7c884cc70cece019b26ebc15b09f716560dedeef 186ba15a08137c92659ef620170b35f6b1bc9ecf4c029732451394adfc999590 9c97f115d584768d386ce5204bd2d3629aa63ae97628146498ca37b61918a442 79f55796b89a671a06bfebdca61971130b23fb61eae4704d2fac27538ac499e7 b90a0e602d78353e01b476c3a2c35a5f0c7e5f415bf78d46ede20024a1d3c4d3 a670570bf89098e8eab1fd7afe3e00b01c07049c3722d66dff9b359fa895d70d fd465b0756c76e548d9ca89b6523c153bbfefb1ed9f32774f9e114b60a551d1f 936420d78379ac16945964a1dee95d71940e87c2e88c0a309f70ea710ef1a1f1 7821936f936122f4adfe274bbd32089d4cec99401014b67f0cffbdfd3253bc51 30d8216ea0f746b13f5e4a43f6c4d297d7e6db7e19445749d595eb028ab09ccf 7681954cc4cbb792670aa9a21e64b4723d86495e4d6d27b3cd606b00114c4397 e9663cfa339901c3282b9d2b4560015f5ebd7fbbd4827023b224e54109933369 dbe9eaee8efc256b605483f862a3dc3d5f70a4053ccf29fa4a52d008441decdc 633da88c3db244e95ef373d027a09cbb954ad208fe938f44c3dee2a5c9bd7a23 a1aebb8588a8f423fd40bf80763d0a0e5c4b1bfd3fbe76f14f9b307561ed03a6 9863117a21bc64aa4a8a301aa9798ff1fe6febaffb1e2958b259e1783fb5737f 2eec0edee3b2793b4c35d7759054e7b0c15d4d450b06a5025fb65d8a60f15372 8ca8ed9269c7c5c7ffa431228e9f688716fcc4133c8f4394513251ec26fe3159 f345a4005a7c36b6b78d97a2a030e84cc8157daf4a5a02d63675ede07529dde0 f7cfca247ffb2ea7cef102761986bb68b2e5032245960d800d5fc65d3947f9b4 59afcb1a6088b9530d8bb2435c23a885ee82c3aa0c2e685631a21a228cf07e16 1f4b5692aebadd8d08e8f4468b568a94b2eb59d5d4bf26d5343cabeb20f80f01 897ecba2d98f671188525193fe5dceed743d68886ab7fbaf5be3d09564b619e7 bad4d546d08f62c1e14500f7e340a2eeb8d39e5aa99b9f66d329dd0bce311a37 a6fedfca7f611f99b505df971c935955e2a5179efba1f0323340d67962c4d84a a30f18f05878d4815c755fbe7569d5c042331ef9d94571b0e506892355bf79b4 fd7bbe962d8a76822895dc0b08c9fd60cf1e66e7d91cbb4d88a32d876941463b 5cfd62b1a9c70f8805539e08134011fda0bd8512815c04a96ab1b79f84c6ca91 07477415ae11c9974543d373acceaa7ffac870868220a69e06c3b733939b383f 947545a4a8315cfe81d61a3caf96f5f1a41d7cf354d7095891525ba18d5edc7a ae3e36b290fa5663f46cb37b4d8785778b6e7a5b5da34f28f7a1771ccefdc084 54d701879193377dc59c6df056652fd546b4f3accb0fabc6b6019320cb5ea088 18e49c30704466549121a72ef2756c34a208791ffee54ba4838225c88bb8a8e6 e569ad0ca542a034679e69d502cb792d6a6f8c864c6abdee43efbd188008e9d2 54d46ad00303564a2ee8ff3360eef10952abe041485f2876016994dec2f7ac1b 612af9076950f88f166138e7bd7eba7ce342273816ce9f701b6a3ce6a385159c 527ad33d10c16c2164c4493cb5ac7e3322cb31282c6abc8fc60d5c19395c291f 85b25c37f30e9b1e6afac878fb0fdef3b5145443bd9ba551bca138ba379817d8 f1ecc0173843823f14104228e526537ba7d80aafa2a3f048cdacb2c0640001ae 1cde7b25a4c9b20d6915df9262e220cf42af1620dbe43fe677f97a1c5e021194 40ccb1739d05ddd2e883bfe67a4b4b00807aeaf758a68251b109049674eb627e 541beec1429c13d96d5daea9a75dda434c11c07f445326b1132582caae01d282 646f5b9cc6746bcdc22265a54b7c8043cbcbeebb1ae15b9dee7f6d7c7ec0ff2d 71471b752190bd2ced82c80b406ae39ccc88db4ccc597a24cc1cebfe24e42e63 8cc5a53f3eea773c29cd1a51706f768177f0f388b88d74b2b705fdca00d41cb2 dfe6ec108b198bf4ab586bf12f3f337d341c1a727348582b02e9649a2efd9ae7 02a64a9f14c464086daac1deae485eec3b15b57ba73a3cb6829a45942df6c885 7993899437d2e12076f3311006ec58cbdc36498810766a5825434b3cea5a9fd4 8a99015c83d9788fd6890af7711c4a3701a18cb6691d01c2911daf81a7036e52 6bc9edf373af0048417561db069c88110da2929002ea335ec61b7869501066ba 212d2fa1f059a592c9698bebfa9fde4e1a42a7e5cd3ebcc5dfe4cff5e61150f4 eb4d38583d7578738306c3d8d49bdf9536403765691d7397dfb870b056806878 5086416d7d2681eaef90d4767bc362717d1f75738fe63fdbf399fe987db43485 164a5a51776f41979c044ae85dbfe48a718dc637e7f77602ac4745da472cff14 93cd3e88854c1f515ecdb84622489175df9af084ad7f7072cda98f3b4ea76031 83a6a90b6cba9599afdded09ca949ff11c59f21d715dbfe1b7bb69e8cd30e39e 1411f05a4deebdcb245c7fc6e1260963e848d2d5093bf81567abcf9f84823b36 0d7a2530fb5a816088760d2ff8268e77dae532a31d162fe4538f75faf017560d 0fb071cf8e1fe1698f021a4817531138967476a396b75b3cc904856f2d973caf 030725321cc53ccb7f6131762f53c5e56e9267c4b43acf557be855e62ac1555e e552bf40c1ffaf28e1c467817ef78ef8364d939f5005f981e3c15af2a8e1f68f 7e11b3ad88eb6d844a993b9de6b85439e897ecec629a27e001482461dd40d0ea 16f0aa77b2720fc1c010682081d6cf1a4dc995ba28641cf5bb3ca92be1396083 0d74a5621aa39cd36575996f46f70cb2e17a65a48544ef8153fa3ebd092cd2ac b07a03f471b407eae66bbd77e5f32d7df9ba2bd53600644a6c43ccf2da37380e 2453ee3f5ebc0f2c92ec0b9e520fdc85a5bfd1094774e89dbcf7c47f6a3c8e44 d251b156ff019aae5893d61e0a97d790a6f906970232452c420c2accc10150c0 c5a56fd86a0fd7b6a17cc704b241840b377d47f47eeec7a46b966354655f8b11 9cc2e36eafb7a43d447577fd8ebd8daaed083338bce3c7d150a562e5c77c56c7 1f496612b8a0a887f9fe7ba917df15a26c2a0e5367528b7dc207bfc731bb0ccc 9e53a519f3f0743b9b02d1869a9acde5ebc122f764facec61fbb1b344d71c26f 090a467d9f5b6e63f1d891861ed4b07ca59c39957c94a58b61bc31b0a5fd7ae0 0ef42450c94af1bc08b782b5fd13d4def2d845f1340e911cb3cc1a99deef0987 f56cf062d1975b7d2325078fccf313b0ca0f9bf47fb81a1f9496c59a56c38c9e 72a867219b50935dfe77b3b1df2962f2693f35d1786e586c1106f275e2fd43d1 0ce8053f27a719156673574afa97f4d4472cc0e61508e5e46f34d60349d70a24 7ae67af34879c1a48f48338f3c6a43e28b7adea79209cfe307929d2c213d18bd e8bc556cf00960590987c6b8e92c034421699462da965550e042b0394ce5f3c4 006ee6f0266bc3d1ee4de3c4dbabfd237dad65f3d8d1883be6e8261800a3f504 8bef60f2f5d2a4b6f92113fc6e6644e25786cde40c5b5756f0f0c00660905ee4 d5ddc9afb5043d875d63729e1ff1454e33b625c4b8809d6cc78a722fbad332eb eb9e35797e0754a91ce08ea61798563703fe8209ff6689c88b36d6a30440cc23 32e5d527199ef20bcd81d4a66bdc2ec4e06cc46ef4fce8f047267313b26b7739 bcd9bd9ca2c136832d0469c6e38b8df493072badc35ed90bb6063f10f565ef95 587cf5785217edbd9406ee2210fee0b0963001048943eb4360da7a922e09a6ca 66faac7543d5e35abc41ff40e93d26dc3dbbc0225b283e6803a8586a25d8f83e 481a8dd416ea4cb95da47d4df364da485d7733bf9544956082673aa41655fcd8 87cfab1ca3d4e95aa3419c6c42fd3eba0ec55dbbb4c1dcb2f344a10e0a4c1fe5 66925ceed101bb8ba62b1023f59f4caf1f30ce11d98971e5d26ea31419849e5c d5b3eebf8aa62351c4b731531ef851d9fe093327fb7a4598a5e3b0fa11d1aefa 144b857e9e1fd993dc9bc2677345d5cb721171e42eb90749675e782664e6c002 b93b6b29b90149848c2452df74b309a91ba6b67db97f03d85f0337ebc8cc624d b7b8343623c8331428a87549b7b93cf932dc3008a94920a95987226883bcecb2 7b4538e82a894a23770c06c18aadd0806ba8a9d2a001c07cfaf4be39913c7d2f 925df872ca181459906b39330b8238642f0c2ee8455e79378077064b41615316 c474eda94a395af8db75357c336d7093c996d9d73572fb646bafd395c8ee14e7 0d73fbfc77b3fe3a5750280e8d5a5427417a5474180efd55342fdf464a286cd9 64be1caece30b05367337ba9e0cae59a8b5fca0f872a256a2ca529db87bb56f8 8be33680a41f61ac87e8834991bf5bb066d6761d6b8dc1ee3261ab31d9489e2d 181df403060aaa9e45775e7711244a3eee977179a1a8b265ca469ca37ea8b918 c75c38b856bcd1aba8f0b39da20e99454a829a64aeda962211501db1f44fea65 ade8b070d01fa4825714e9cff03d3888b8e4494aa106ece1b5f19ac93ad662a2 be8cb23501c41cc8da43ff11695b06f03a7bfcb556eebefb3ef756a4d3adf39e 635c8c8532405004c47ec4f598eaf557d0f190101f2dc9092cbfb5d2c194a632 9f0303816b3161b9a54ed048c58f2aebd4b50dd1fd74759b77668d55aeca6966 6913a5aa387f5fc1df608509e94efeabbe1e645a7a0b2ea1731f5c2b76c4487b 3439a5b52138d8489f8e4ea2b6eed403591ffe406c92517fd92a0b94699f4338 e37f7d0be2eda682c76b916dde8e1f59e49a3f728d0e6a384a70617d20d67b1d b608f6462485000a57441392ba0a6f3e080a5702a2e1c2f0ee076c89396e626e f45b075f8bacc04dc537a68e3152714f9ec771cca01e8dedbc8ca0556bae3e50 ab88abef0ba0c0f80019a69d3edf35bb3acaec3b4cabbb999c2811fe6c0e5201 4131146f23703cd3a999104ad4de0e6cbe70617a18db2f12fe205ac0145c9939 7533bddc26066cbd331ea83cd47bed0803dcddf53e44efc8d08bb25622008642 bd4128d7a66046ea22c24c4113bb19550e8190873148ae4f8f7c2ce5088813db 97b798c7b5bee39cfbdcdd05ee03cc41531902fbf2510e2173fdf65f77d33e4b dcf940ab3e938ebb84705381e9bd8fbbf28d38147333027c931969294a73c5fb 8847b8216ce4370e2d6af86d9078e9076dd0d1254d39fdf268fe77fd555ac582 1164bc0ceb2209a98e74b2c8b0208d87e7b22faddfb85264d193b9e635bbfa0c 6389eededbc5d132c379c19f329b97a17504c1d15a0d1394f0c5424d71afed2e 23a1015cccd6b3e32b86be91c5cc6e284728e7361365492b643695ca569bec6f 994c0fa0662667820fe339e742f682eadb7363c2f32bbedb3c0581eac6103130 a07f149f06097c6867f376efdae00a1eadd77df6dd962fef3ff384aa62dbd994 132ee638838203d2f73abf3d2d6565a2b66a49182fd1efcf4e74f337dcef8abf 6e88dc3ef36edcc0a0855b50c9b74918c2a97c143158015391435bcd51cd997c 2f667583cbb5396449332bbc7eadd753dbfddbd858aef3427c1a612d5816233c cea8feb61dce52b4289a8d5340b4309b1c5eb6faf13f1119cd4b2bede2a197d9 784b19bb39c04a0c64d8e03cfa02cccf2bc421b68641a909367d0328917b707c 75beda4e48f1475b63c4e878d44172bb4c4c8223af792ea94ff990bca6b3069b 7faf00a416bd08e6e4ab4ddbfdda1a081a00a64fa0392b95e9230eee21b652d0 2fb7d868c25ff5d388eac019245e59490a83650409c4e6e577df9b3c42f0bed2 b69baad2f76263308d1f073fbeb2f1e8c00bc4bd11191eb5def5a9fdcac60ff2 666f450028ac20329193551b9c2610d8b25dcb9fa8f5940f43e7a60e9c47b7f0 84a1f6033685833b55eac7e04c741af9f760a50d56f209d222b0d2b46d42c532 f227596d9f75206ce74f2f6078986571acf0eba96100e76e01333836cca5828f 787811688624da50922ef60b9fed8899a3734ed64a553ea174255c02b775059e 0c38b0514b4afa77993790fd35a2313f70e2a5c7f2e57504e86b15b5be965b92 33e697073eedc087320c51d1ecf61b54d5578e355c636418b217f98ad8fbdc53 4b64e1c35f9eb38ad17344a4165489e69ea3407101b409465ad17f8b47dec206 7d6d53cbf0c498461fe18a8e68a19ab04374d171951c61bebd95dc4564ce1c98 17066d4063e136e4ca567b7a2dd79aa7aca40634f87793656fb38fde733dd759 fb9ac5740f31fff598eee443097156f3790a54ddb14cfe02aa6cf2e5b3263f46 b7f18404b45acafc9bb8cb10a186a696ae7ffb84126e5458f50a2f1794e15469 929ec779dabe57da88b1281ce1792624c729187e4fdc0d50585ab8528702db2d 8cb2b46ffcad9dcf356bb089b8beb2a461e4ad8cbd5d2532780350e37e41d0b8 25125b917658538e1d422595b7c4a5917d81e47d397b74baa5d713d1babbd730 6ab9febd5a2f5836ad625968e702dacc6babc7fe52d428d7bbae17ea7c3ad61f 4874db513848b00dfa108c53669ebc17506166896af48e3c68148d4bf8b99939 89872f5084d9875e216519bf5a6c0b9211d3ae603683f411ecb84b381895e863 6cd5ae3853e0abe7ab6854b6d0b0926d92c144293ef4dd4f9972fef1ce5d6e37 ab3475fb2007c87278a0b7a48147d165640842d5f67169dc0f3ae2fd305db10b 1ff198d5440e28b78e48d730cb6c210adbe74d42862b41c5ed42515b967b3c68 9cb44a35a2a15d1aa9a94b71d06fcba5758f1f691b48d1817a063b4be7f2ac5e 3b1552d31a70d241c3c33b5ef0358cc846b2565427d7c66094f08fdf0dc553de 5ab0a9edd5f873c69035024f5ce6dcea1fcbae7c75a35ddd3212783d0c692957 ad1d6326f5f0f122e8702a8494a3cefda9c5ecacc61daa3b3fc1dd94fd58e5e1 f8545840aa70b10af0918892800f095aaa228d8cc94527e93b1e5040d1413c88 9209e8b69e44f10f57fd5b60ffd7ea319ee94679bda2321cce9251f1260486ae ef5b023e360229edfcb38c0357ccb791b337630d5296f505763d48ca6d332114 641bf6bd2b1b1356498ea723e0018eba1c4126bfaaf51cedcb144ab2d0362398 6f3e871c296ece9eddc5461072ef8566bb4ffb9071eb591eea8eedfd0fa8a97f 2487fa37a0778bcd6560c8a445d09d5c7fa8ea4f5682c04cc446cdaf1af9f579 7126fd6c1918ff3feb8cf821b586142a07680460d7dfe528aced2980dc403076 d4d6a5d8655a8119d0686d5a82f74606037da1a7a2996ed25e9f3fdf77ca391f 8cc8830b2213ec306c201584340f4b4f5cbd3b3fa3eb36f75786c822e22e594c 32d173fabfc93d005f3ac3ad2366703b38907a2672b5bc724241ed87289d48d3 231ea19cb8dfd1538ad9358c94f381884e5eb671ebee6de0032dfbcdb53740c8 81346e00b86ca5b96febb07161273ebe427f374522df2ebac148d83fb1c84b65 6622ad031e4a255114ffcc1b993d8d3e31c6d65e40a59574905b216a352a37dc d74f5f46d0a64f9dfa77600f3f30b38d2a0143a770e664119f9f77ab9d2f09c6 8e653adfaf5064ea5eeb09d51dd65dd8655fa7fcba239f4ec6ff41379d44768d dd71ac0050e36750b6e33c90efa389b856cc6d06b4a88a2ec732bc505886f114 005dbef9166eb9d661b8954120bc5d3a26f0019ce3f3ed9323c45319e1820551 f5b548cc9af5856e732b3d2d40d73ed801d809611be1b95d3a0a4578974b2d20 defc412b437cad2fdc7299390a68aacb97e78252f38a80d9edabbcd3a4abf3ce e0af56e16a967fb94821b853f6197c6d9994ee0aab2b081ec16ee21f228ec0fb 2853b9be2f74e4731ad0d77b05fae3297fa7087f6e9a6a61dc8295035ed081fd 4d6a31f13950296e764b25be58d3f379d79b3bbd6b38f01fb6d3e473a4f51712 623c7cf4765775246300ad80136a7362cf5c9d862708aa7a19f57f56cc06dcad 548e4d75e578610781844f271035461009b327954bb099a2c1e45ac9b08c6df5 191e63823ab830693cf02c68e19d49e9307c7a4f044b7698dd94d476df250862 e8fd2c2f0a999138fcb936569f3f470c4d683580d72cbc1bd1e4b36faea2d972 925689b1d36695f45765184f979dff2f2bc70efe57e37d9226319b50b90dd469 8fd4bb24b442796650c1f0d825f468b955a34c5b61189a1c44b6df47feb40d8d 0361ed2dcb2a570f577b35cc6d7eeac9b617d70b7967fd4d30f94da25ffffc39 d732a6674821514311651afe2142d14be9526596e9f45e9c8cb658cca6fe53bd fa76c7ea0f87084e9543939eba23cad3d3ea2a0df03733044e73fa25859b8cd5 c8ddeb6e648cfc07f8ebf7a677ff6a038e747e37923849c15c45342afbef8493 5c431e3a46089c0b41ba2136374f0e42ba6f9555b0019995b690e02d1800d814 93dbc3d9dd0820e634aeebdc808ccabfea052b77199ae04fda47859928a371c8 df9e4ec31e852616503784e26457faad8ecd94428884bda9ee6d81c575394867 f9707e1b484adfcde45bd506627735685e0b3663ef00a41c48bb1c11cbc266bb b123a4cb6366790bac198d2d3d400b201c75cc4af7a3430c8a546b9891ae05dc ef38093824a8b47c138e20844f923934f435dc18e84cce31a679a61fe68917c0 e81b5c08382f256377a7fde70b56421e7962d4b9ee90378b201666a0bf88c6ab 256e4f321a15a634355690e49bd7ad6b92cd51c8b1629f85c49260c0c3431f63 ca7b3fdca514a286c7e8f440e8353af4a030c31e5f27ecdbde302a1f8269c482 e6a199df5a488561924613397989d00c595ab63019c2bd1d8b5df37fb1ac7ada 4610c481631c285a6acc307c9439f8f8acfc306f47194e2343cd74efb7007e4f bb2d60b1aeeaba077329246a5a406c310a98f94f3c51ee6dbd102f9e6677198a 834ef843a1b0508f4862ed8013d6555cc21a23c52fb5609c2698c20db9912922 f624c48256355e29be4357a424d2d5d6d9d0d4d8ed3045fd28957d1b747d1867 6168d8666d802eab8552a1908420575c4cc9b5dcda2bbad9c10df93d7b665931 206b2c718e2f34a009b1f1ee7df74b9e813831f175ad5d06e520af8d0012d178 61981a77843ffebd3625d850e0419fbeaaa7a08688d92266f33100d7b6c40d0a 141 4e67a7ac6cd1300b79a1790f1fbc535ec33969dfd79f9b6311e93f8dd35da50ad98a60c96327e1d573de75d5197ac694ac66774fb4a5f011b3442462a9c658022d687fb6ea3dae9c33505085ba037046155851720940686e88085f18311f5b0a046782e297b0ec579aa52b0002c62cf368259710878f3b5661af152457f28b001e1e243964635c80b1721b4bf11e5515c972743b9b33906c36f9c89cfd7c1b056871b6ad76d71cedfb11fa2d29a5ba3d309c160f90c4f48f1718ffb2310ba7067d769e772a0f82734091062885b0f379af0dde6090751de1f179853e48752603b03671cded7081c873b4de5a61082e9bd1235f7abff43f052b5457cbfcb8fa074555384a9cc8722d3ce79599b56777238aa78c09009ce198a57a9160206bfd08bf4fd8a1d4f2488174c67c49b39fbeae0ee42f2323a232b0bf380514b120960212e1483a0db6dda991f2c8736b9df9e7d31c981d7c928daf12bce0b1fdd49a0728f9e00861dcdf5fd9b15455132a5240d5389ba594f15438bdac5e4136181806c422e629d74126f876a0a5d14b0f7544980a085ec1d119cb9fc8d6379577d40ca62b66182b2e1790f55dc0e10fdfb3a8cd7b28da07e78512d640a1668eca950c5fb61dc96ba0d777ef2287b042a10c8eb9c526a31927200dd1097bef7c25ca01eb78c3f71630b880ac4cd2625ad980c0d2169671c8efc42e016ee7e5508a9000ac015e392716b3b9e7151013725e06745076684abe6441cd18e554799d12f70d9f52ea2355df3546b3835c7f9705c6ff7c9ffb41ff3b3263a9ae32eb7039f70b9b4bb332647fb99d7d61d05d38132aa68261f94bb6ed18bf100ecf9646dba1017ccf1e9aabdb9ead1974eb1d1c1fc9a059ab9788c0b201d8e5de85d76642e30ea6f7353b99dc6dd80ab0a740c50e0ea1e7151e318007dbb6d51fe42409f5b40a7f6e01bfabc19fd3149b4248e25b016219b99d340acfb3d9bb716afd8731570dac6bdf299bdb7e4515d8e0eb1fd251506074e08db975a975e620a5a31f9d7b0aa6296ebcd0d3799784a3a151143349e977dc8bc09ed6d89d0d62b93d143af50f2b6800dd90aa984bf33da5e6881b8e2e541f3bc8d8fc2f789319314f8ce8850ed369009d20ffb10daf1634e68c3c011dc8d8d9d2817f759b6e9603d13f4b740f71280430425ede8c579f344964dd22cb449aa31f64537414a80633c1cb02010328a7b38b703bcc632979024c62e92715e8f2adada6dabc007b8c78675991d703b2b935754000274bbeeeece5afac04415974fa15434575878f3006ad4cd3890341574661d1968038702745bd86e3ab8e88ad387ac2983aa2b7955370a6e25705cf9571db8d53f7ec1b29a9a9bd79f5df1139b42e2a2584776d23d4b43ca23209a5ff9d6a7ed14b42885adb3c17f3da044059a23fabc7e7208de27365612cdd0bd18c042cdb282a3f7f3f16bbb79113df7b4d2182f1bcad7d695882cf0607410cbb3791961c3ca45b5ee8a18d8ab6d14f7aeff98294769cfc0e75b6c03ebe9102234c1eb380868c0f247f5abfedca6a071fab84bc88dbb249bad56fa54e3b5408453873905b0137da3d5e29d778f37a989d6480d13e4386fedbdfed3578426a0619393d81047fe2b099ff81b61f41ced2e97ff14477fa6f506e1aeb8fc9fbfc001d77fe4252427d74a8e231a95546861d0c1490923e6aa0458b24e2559d95e60c9ba5d9c8533713c8fc975f18c534283b95974571a78352d467ca2775f64e2308a62df53bef2da6a945bceec7b63b279edd97f22eacfacdfb68a6835ee183f6043f0232c819e33ef8d48265c98faf68826d89a1784641618ea56ed1a18ac98700f40fc9778cfba5f0520b2b6c19e88c67f63f36a1d2d3455b40051d095ecd360a0516cbb8a7c86ecf4bd9987c6bd981c3abed6db965815b0ec20571c90016380500bae8ee2cc3dfa744dcde36255aac1e5fb795617e1e67d0fc9e8d766ce26905dd1b6c455f095435ec412e7a93637c734e810eef1c862a51ab12370662fba80762117a0a65481b98d7f92528be2469bc9b7b67779ffdd06b7b953f29fa040807840913ee988b4974920bfa08e71a8a09d43c38c532fd9db09344390a610e3e01d8d27573f8d6f0618b79ba6cef66470c646b6bba1b8501b57127c7837b24c40ad3002e6740418aebf28bcb3d236c8fa312a0dced5fb5e30aa451a5179da783006c7801089b53a6539188bda423be7af7c42a56216cd8f74d9a2878246d7867026086e912efd959ef41b6974a413e6d5ec54fb39d0caf03e76d6e52f2ddc1b401737b847da8c1ea3a26ea79437875b582fd22c2a8c8a1982f9925c70a088fd00f5a60373e26344f28860ccf8e6ab361676e918cb73d73c3e1ff18f29574459b076522d6b6a7d7292fb7bbf143a8c651c14cf179ac582a532803749a465f8cce096064409c944aaac8933c344b2fb714645dd9dda31306ee2b17960929beeb13032e058aad5888297a1fca21041e51cdabb38157f157458d3a546480d1ebea0c0c218bae518c72ad1f096c1ae339a02933c110121f1d28f53c7d89729bdbf2e103d117eac5f5f7a0408bd9d1a702e25142fcd9ec521883c11f818ac58e54f00001093d6e0daa39a484336c4aca8c444a093794d9abc3c9bd3c4f89d46d64af040c8cbf27dec713ae9d8342a9c5979735e2726fe7668377ee37ac80cc7a605afa0704983e5ce71c0ebf5069575eda2f54396cd2d1100adfce24796c7ddb5632ae01f3cdffb60309e4748f4b5c8ec7eca298757c9bc9f1e73a843bf65477fedd0e0e5a61765bdcc15bf9cae2df7be445b91dd80855d7570ddadeb318c1786d9e4404ebc558369b09a0cda3939cd9e7ee6eaf4e22e4dc3336e4b7650f347c8642df06bf192fe5f4aa21abd106ac2e093d107b243d2ad48a2aab1b3b5832d8e472530845e573874cab57742cc31fb0b1194f7e6d77d491dd377b55fd2a780bf8e3cc0791b6529f5897038e98e2d33d3f4465800c67671c5cdedcb21c2e792ddfaa250aa4eea5e7f81b56e4ffb717bf0228679cae32ff073e0f0a7fc6a7799de52e49026738d6cd7d1598c840360cb5c168d98b86365817c50427f30d5e12d60280dd00c25bf5199c86a8f58c7681754e6dc737707a6f55347ffe409a04bed4b577bd06af96d8545c8c0cd7968f9be5f7c0bd9775aaeab7161604d7e36d0b0c848de30002807e8ee3a5045d591489e0b33169c5882ee700cbf2570a17ee7cf142ca13036d816b9aadd06fd77f43a9e0b40b7d229375a7eb5f6186453d6668fc18683609a28007d19f1d56a08630da9a7e4e3ad2e70fe9247ffce23eaaa6b74a33346503acec3558784ebfccf56114aa3ae7f69311fbbcb8d6d6b0bc5fbaa281e2abff078f6c12c4a289d0211eb036c8b036f306b9b6c32c88d3731f27c7e45ef27f2b0b1d975a5a614ab986d65586339115121dac6fef2dfe50431b6d1b023b50808002eb36beab4532eafdfb967675647e4b6d7310e902210d22d125173a817146e20145bdfad526ad1e57b57dd6bcc86fbc9e8b44051f5d0b69128aa3a965200d81036b26ce7f4dfa4994acb50003f8ed28913cff52a9bca774535ce8f78045688400169e42475e1174d5da6412e0879aad9fb77add817afcf783d9ab4ee83aadc50103f670cf0f7792d56112de68aad7c1fef98fa01ed39375d0e9855ce7c032010a902b51cafd56d37364eab9c3ab21f648b159e3cc082d652cc8bd21754e2cac040f838be62e832e995c50b0610e5e4b3e767f8521c1745a3e63791a36f8475d0181507f31220558f352c2acd8be5beb2bf71c0c20ecb2439b94d3db429797eb0058e4e7894d0aa8313688b5ec6df0823783bf606bcb14de2f040805c9231d7e020c66b902affc191fc9fc25bce65c42facf62f6a76892c054a5531b48b0d932057430e5f81cebcd9bf6c33161f21b5fdade5647792c9271bc592fd173a363a00a0460f0c19dbbf29ba107026cf0cf172b4c7ed9cee9e9890863c65d666f205302bb6af64a9698ec2b1dadb2eac59438d6d3c6a4e6fcf79ff765cc9b28efbd8d0bd099635bb30467bea5a53e6918f3d9a9616e26ae2553cb08c44c4332bae35a0cc151842be36c98515c3c08719740651784ec93d2e2a47c7be9cbd8c71473d90f21c803384233e07bbcf10964985b38d7d935f74749cad59251153f4a7d27ed0c4ec1276018522f6f2ce46a97ca1b25989ad5fe9b92bc6b1396112df79ffb220833d75be1a6b9d0e6d17314396d0320188ded49127b721e113d51d2eb3ca6fc02ca7cc525de7bfd0ae0ba54aae895fbbc260cd2fead7b5055e0664c2f0ded3501c78b6a530857967ba3d942fd0ed3d0796a11c03c1f86778b8195fcfd84d952057a533d04d748c77c0d365b77f1c0dc0dfc69cf461580562322567f8b0f46050498a29529b624df50c49a3ec4102e2db13848631a78777ed6df003afa0206d70a9e5d31e059dddd0206dac32bf083c8e51f26cd53006ff2aa45edddc588a8c103c611025b883f9597fd32e1a6c9d2b99db3ff6c43bee87c0b55194b0b927cd909da8475b2a19a24843a492df9d46dce549db892c5aad79653469b732b794d1a07b52f572b6f6abf3a1a9fc4489bcc30ef9f3444261b944a92d5fb482d50ce4d088a79c14cc07953072d778b0b9dc7f6d91b4aec1c1a1218d3700a8be986d4f20171e51855cbe709567090f2ac9f50b98b990a66b25ae90bf30c8260898c3ae10fa033c17f463ad520bf60d18d159c1017fd1ecbfc4159fd97f7dbe94ef5e63e009fb47b0844893954c5edfd40f4c11840117a275b76a19f484a0943422b6a8e09742bd7745bf312b40324ecf9cdde61c7b2b7da2dd461e5f585853eab3556870f81ed26b26954c4e70c4ada951c3a81913cb5e01816f554c9c3ddd9c50480e40382a826235cb02887af2c37c9f8f983788713b04f3f3b8bc8c1b3af3ae98afb07e07c19c47559130fd03356d8ff2e766a0af558ec090b3fd13c6e4cecf1be6a0e37d02e193613c3b4df12a4d7229bb6bba87caa16d21e0bb6a447cde94a5b210fe1bf41d1237318d7cf841c2c1e88afca135b46ac4d3e48df0972841434165106264494106387b6d693bb7579e71daf6e4d220d22961e7acd111255401300c4039a0ccaca5a4eaa7d2483f41df06cabb1d6e35d4ac321bd13f45a0360a3415506811ab32cc6c32430c426ac484d12e6125ae53160747d76a2620582c14410d30d4eabc1ca13e0966898282f9493457b1472fbbb57a7120ba8af7acf811426340f658a0ca5911e76974125cac2006093b71626d6883d8dc82c23ab38531f17d802000375368f07a5994a9b4c8c651cce873d4cb192328f6d7ffc3dc32c292d47048e6739a3faeec4a0d99c4ddabfd70442a47756ee6897ba86b0ee4bab30dcfc0422af7400a5bb9ed4a57062bae7b3e9f94194ffc76087f6e986252a68c2eb520d8b4d8fea463992312272ae6dc503053bd8bd58cafbfc6cf0e778a05636b93d04de19bbb8a764f2663d63247bb7cba7392170f097795ad3e20205479b9d6c3d0c1e3d1aadaabc580112dae35ecb5d57addc00dd52d547741ea58fd017ba4e5b09f8da838d473aa5aed7fe26b054b217982a7bc79b6f1177b930c36eb5a0c4360c58cffb85abfd55a9ac6f3a9fc521022f18a377a5f874c6fa01ebd6ac2202150fcfbd787aec86c540fc64a636ca92636fcd290cbb109e206deaefc4e65b0f2e00b0190fe0dda4353d4e2a929ed9e4efac45a9bc93dcca748970460355b9632307cca776d7c41d8a77dc84396c4bbf5a9772c40b007a0df203531d2884e83bb50343a39bea1e436b3717c29c5dd29a36c208c22293fe75ac1e0ced941cf2d96103739401ba57d1c7d908d7388dd8d4daf08c1c9b678e8393614496ce46a1bd870c64912cba2d64c528955ee84e35dd9f85476de412c22056c1546561cb1f113e0f511e0216f12e7751b661832a52586fd908fd42e2584a658edb5158514f13040a77010defb6bdb5db11067f12430c30a28d2518c01ffb670e439f730aeade4c073c6537abb4fad0d09c8cb41546cf8a03e1fbd0595e71f75d034de629c6a0600b3cf372cbfeb570f0d196bc51a938f92252e661ccc023ec062117b3d28d163e02d41a651b463a5dcece0669b9278e3132ea18402986aeeca50008f5dfe9eeee09a377ffbb4e897e0af1c7d5a48c2cf87c4a30b1e56dedb1d25687520152c2b108dc3d4a7f7105c2cd2816830f0b27c7795ef4d58a947bc6476b10e6e1d883cc007dbc3cd7dd0cdee2f4ea6b023d54fa01a5201268e80372d7279dae38d1b0060a5d364891627ae4972ef69051d8b80289dceeed2554f0eaeb7efe0fc75173dc0cdaad9c881b5cc5da881594eb3aa35439e80ebe3cab22c99a5e927ecafc0b7d050981d651ad100f90c81ad9fa1d8a3c9e55162253d80626712b5076c34cd82e02ff2cfa0a9b141bd1e60c83f390415aa89c6f45e543e1141d60ff7292a240b00cb2042dac5e2f90f800855b1f46ad67789bb2c15a413376202a7481a31bea9b064966cf5b5c8b88ef8149963cc3d486b8f2b67d568b3eae8dee0cfb88ada5470d0f7a843eb7aa0f241f6ec12cafc3605ec246c3f53e4e9eacb3fabc1e2669e20a756d5b85a68e09988d3ba58fb2891208fec425c32e143f0eacd1ab18c45c4b04ad6836757009e32b1e2aabc93ccbf920141645ecb84d8529b1e80ae4738ceb0c88e5edd988e61de07166ef5fa497f62246508e2390271f14603b2a5682c60b09fcb7053857cb24b29bcca1e9a1bbb372dc55436871199caf0c06ef087b08f405038395d459aa07594c3b18151ee930f4e608faa998739da5502ba8d10ab66a052f72ab36b4ebf52ce3aaf1cfecdd99001448c2b2705a53babcd9a4a303ffc8057cf9e6482be3c3bbb1225e1eec07412028f9d7e00cc59f8fd800c4623023f2051c2ffe5b0bc18048ed78196bd0b152269f379b23eff10e6cff2a367ec69bcc0e6df5880402e32cfef89622781316ab6a7d918ac84ba7c2849ee0a02fe1f9c40dd2a5af52b04426b3b7588469d11c2b9ea8d113025e4b2846dd998b9629ce4808c8dc582dd2af193f027e4b1207f71fc34ee81586813d950275dc33610b38aa03524cbd0e5fee5b3ad6f6ed4ff907233c7ae14b3849782e3ac2b5bc87dce5680ca5146536f323c0d7e213f15cda539b7ffaf6eb1707077edbb9e21112a615160d52df5b4866199da8303824879cf62bd300ee15362078d7996c14ba490e90ec0f42cc321dcd24b226a1312113bbd2b7e9044d20be8c3a505989f10ac9ed30190e8596691a8638d247700928d36db4d5d3b3fe2e5b39ace8c9263891bee7e43e0d172ed241411c3b52b4867601a27bae38d141c6f34dbfc4630fd6f523b7ff430c4567702560b222efdb6a4b091587937619248f016dd348d7bd364fd81cc9d80a83ed1ea01096f30dc3ed33b88a7ae6a88cad49e9885fc74501ff1325dbb02f05ce5070dd2854c66ec0a03b256d4598b26bc073f9161c62668f16fe25a641880d5b14de234b81eaf22a7a339e9cbc1525f160555014f1ad0ed92aa4f2ea70660f91e5d586c0e2b8055396813a707f4e336fc1f29ebf23bfd43e87af0930ae000080d19fda872b57d018fad4d217fe1b2f30104b9db0f7643c649ede742bb33e082cc736c78f5c52f8158c149e06496e58065aca611b9233398c03a4fd4de2ec041981a48729a575eefbcd9180bd2530cb4f554880031cbec9c250f4b736bac10888c97e0c0c673dea61e28b45cf86909937351d8791a49f389e05e612dee52f0b61b053206a9c88ff9e9bdec0c69c14b408268edaaf6d04a21917bf05848d2d0f0c3dd76a5067219510a382ef3414512994004bbc36c28098bbca81f7e798a903b04dba2e3c4a03a63d72d1220c6da3073303afe4f792b4416907b31d8697230672887657cd684132bacc87314a2ea693ff6f55f006175965793f10dff6e3960b60764dfdc141284bbe78d2ae1c2b1431a16470a1cd563992918b565980c6450c40d957ca80dd7a275c5069392a5f1de2094ff2e0b5154798afa64e199cae9107d34183134c6675041a3364fed3b7cceaa5253b9bc99f8d7c3be09bfdd130ea0c59582971248b038956be5aa44e9437c54f2342c6127d312e39281af5dcf20206e7c677353669c3a36a06316765ca18984062907b6a6bd2d9e183a0d9f16a260402736e71e54f4fbd27e02289817995f7ad15b50fc8a2a27be822acf7ecfb78003e829d6bf32e4aa8b64cbe3a66de7e76ec40f0666776e5e863c48d4115fbbd0d5ddb3a0adb8558054102c34a9e9b5c0a6b4721064c7d11aa00080bb9ec3d11040dd6d8ed85c0fa1633ee2c1a7688b8fab9a16ec6bfc42243c0a80c662d20b40e7ca9322bf94591cb392d2f1affb4ecb2b3f0512d94571088a775af791e4603051a4707901a550c3104de4741078ca43b456fc9acbcb67aebf46856f2a062440846450e6af6e635d6e91cdccd8b7d97b95ec5fe0a6fe0616d2192c27f43786f04df12ce29b07e57d97f354a2377a29fed4c63bbd9dc2cc8ece9b07d8c0501070b670df267a924d21173a389bfb1d0c37708cdbfa2951d907a2b878cd53ccb360f1ef154b28e8452c201db3f644282edb7d04330a2633c42917bc07bb1980b550d3398bbb2ad49070286db8f9a46f17f6ed2fc9fc1518156dc6c1395685a0609023a9f21b809008e97832399710ea9e0d1c12f4456a878b2c5086e915166c11c05eba04041b199c295583b3d5c47f5f3a4f37a5e6ddda05be78a1c0a7e3e3bd5006c59410330fbbeeaa2024d495e77d3dd82d7c629eeff41d06ecfb2f6f0bbbd0b538366e6a2c971abee38a12580f1c28a313aa08b80347edf8bc9ba97cc3dec09c9307b37fe6bd16b31b5aec3c432ddea0083c6250758b0ab710d550e797fbf02f563e3296231ac35a26a85bdc40d50f8a71d7b0a04fcac8d58e721eedb1c850e5014485ac3eba8a1f838aee68eda74474a3835424994098f369992aa8e31520beaf70a8ae3a29f6cfd38f3c3d692df3d03274efb0f2522e0234927bd327d470513c21e34f7d0797211f35b17a276796322201fec51c251b160ed49a833922b0e14f251616a6930236f3075d222c5c1fe7c5fe3c904eb4f2cda39cdbe4bf3b80f3d99e00b12fc167fdec761dad45d7878ce6191eb440fdf8d3b590e5a791a3d0902734b97c6acf63d4a03a3f4fa923f634cd31f6eb5043aa302466cdeb56179034aedd4195541e6aa0ce4b14ed48f494f9cac4d5900de8faea15958302f8aad0a159ed3316425aa5a8da34c30f798ecd5e0b8deae2dd89e62a50f2a093a33d80d246ad0e707fb290a05aae2299232860027aa27dcecea6b8cef8fbb46ec56f405fa85d97b631e3944bc29d606f4a897f53144c66bc6236a4ca0df3a996d2c900d9e7d05e2e4f6744b072c228a963642907e44dac5960c41b7da56354b9ae5490945796143ab74cdf9227dffddb856ce63dd1705d57802d7bf5cb9b85c6e2eea0c837730139446d602414c94f0798911d8ccbd3d1ea4a878f799d8aff09a49e00d160841e884251827f78dace87c09fd5e69754bd50e16a4a7246d808f0e6b640f097aed72e68d63408dbe6fbe687bae732d1223fabc1804fba9c1ddfe40d65d07328496fca25a94c4d255330f20b685600525c111147dd5064c0df65a0eb3dd0a2b1614251710381d1ce49d2f9e4c02d2dda4d9445c6bbe420552a7140ad16f07eb8b90901bd2f43fb72d575539bbd0f1079645d9dd7a410097eeba6868396708e131f344e3139bbb83b3acf692389d5ccf9933f5a4f07d972dabedb5142aea0924baab9baab4a16d6bac38589b43a9a8a631b7e84d0490ecd6550575062406065a3120d5586a65206e9d54c529532262556a6f0f4c41821d69532094ba2a2c0f2c1150e4d9c829cee1f48279d208a1aa18cce4fb0757e50cb7721b1b57f77406e8d96c180fe8d245d67246a51e0e3ce476644fac65857c6ac29e26af06b760006b90f1e5a0ad8e3d069aaf50b7cac0b71a8e336f989cf0633bc42bc1d2c9a9009ae9bfd5d540873a99af3a8bc06f35b31b69ba58d759fd6129bd3df27c85ae069e71be14e7e223b04809d097a3bb04a16612891b5dce672bf026480ffc23be03ec7cecaeda733e8d94709de1452711bb89bcf2064c5fbf54f8dbc42a09a72904904dc6f61cab975f5e6285872067a4b2dc81db55622cb349a952d0b8423c9604b2dd54d6d7170bb0debd886a6a50fb28ff77634c4f8e8b0c949a989b4f0a1d00b1df6169e54bb450ece8282b6fab5931e825d26cab9c75afc68cf58f03d2ae05de6df4f31a0df8a31b8b68261f2c5a28c10ab713040b99954917a75ad31e400bce87e455c3e39d6c5ca11bcdef5df0b0d066531561ca27558d346ec3ee57b7097e2811824cf7d441c42538fec40f6783c695f6cfb698361d21ccf30cac708602bb18a5e2a627d2609e9c5533efc346a2a6bae08b1ff1d340cb67264e0adc8401ee9c24454dbca98653c19ec92e7def47d9dd398fe2da283ab466f2ca5162b1027db2d7aa3a85ac5786d1869caaf84a3c715d108121abb7ebd91f1e127c0c880364aae15de3901be5e0b6fa1f6181f0586f9df59dab88db9af0aa289ada08ce0ee773cfb2917358248594de7b0fa9be0d0699f54c16ab3ec581c2c9fc7154d703fde9316c838244f1a9dd3c6009f4f6f7bdc4e2826a0d400ebe322758e218980cef3044b754a86e113023cf973b55649e482e2ab18182bd34b38ca33e544e72023fbe2f23ebca510c1d257dcbf2a68d38a1fe1f9bfd0af64f8c8a5ae6afb3a60ecb3f8a43f0a6f589f0e178110bc6f2fb9e46ef810ffaebf9e1956a1a8f82c601a73e6f4d6b05740a1a8941ad4a53465041a8cb0082e1104d26e992842178940bff69d3bd914133a7afd4be25d20b0d8585ca5852b4f5caa14c1099a4430e4901668c040437235625422b6a3ce0ccddb961f3002bff5a7e95e69ca9f208ad950f2d96d5de67fe4a3efa801431a12a274eebb021816ed9a32f40245471e34c5d0cf127148d3867c8f98a223d8c30589a0a55d3ce32a0b07d5bbde6453d77d62c04e7c881a4ab2f2f8eec40df0fbab2e7f223f21da20ca329fc78bd1a8b082b5e0aeaaa110225d0bb065e40b269beb295b16126c04c095b58be9d4621f6995e7103b3fddf74f9e26ad5a5a1bf7884be7573d29a04baa7e52452717502314725050017d4e6f91e8822704460820f700bb33f1f51464f4cdbe9081612e7e5c907790c481acb9c159636920132508dee063e943d3df139dd4b5db4fb07dc82394cef00ba923a769ebfd0b8752c3c7b8e0265e0506b410dbb886f3c99071a15cd192a030a41b11ac3ac39a8eb39c0e7a5d326c590d33a3038f97ba28a0c61c787047e04c0815a502e89fa1d7a3cbb0b5b233c1fada19c6799991a4f922309f9201d7106ddeb0bbddd8310085f7c7d5e948621e2b3283379b3a9cfadb94834f9fb73760edce404bc17cd91c29e7f0ad88af434fdea071660c59e5c46c028f232844bac03964aee4678eaffacc944b06db61b66d716c3749077164af13c539ee41be4450d974522ea59219c73064da294b70f3f6bcd08d00c49840f28c31aea56afd25b0d772cef073acf5bf95d23e678203446583093621840d854cb02c8cd1a52ced50e2390465b5e521d937f6bd58d43ac6731241bd84c085c3e29d7335765d28e3106648be285aeb932d5b069ffe21341c1845cf964a74044a6bde2842a63e7a5ae0f8c36f434f1e7c525fb3ea5b3f3033dd54e25d179c9d524cd5e7c3ccffcc694050a719728aa8f54e1a3a889663569c0e65d603d7331a82ec3fa97814ed8a08e056a5b3867f45759823ba8dbe86d4b60102d026605d1ab8ff9eb8cf81ba561cc0aebd01b5fa5b046adf96ba75949fda75da1a3ff3d091307f721ad4b4fdfb62b06a69b6f6bb6fb32311484835290e384c17f58f1c36e235d41aecd8aab78503c0750746e3fe4dc9a9ce5e7bb6ce59df759554a9b057dcb3f9af95be4178c34640e7ee8c1b32bd50ce661528a209c3b4967dc0abecde33701c9b7f806b31dd91d0f6f4e76dabbfef290721bf93e0c3e9a2250edfe5fcbead26e0cc7804900945707eb7dabdba53b56fd428e52c01612a04f48c8077a0fff421aee3baf2c23cc400c129f98f17900c53560520570a59b5a3ae121cabd2edb7e19cf482daeb30e7d08f57afefe104efd55ab162d2ed7e9c171f78f25a54272cebfe57cb2a20e2d4306b27c0e2690ac0000e8da87a7ee66d8cf7ab7e324837f9c8cba33c6e404edda010735f5d925fa66d849a4cd2d7cd56f5ac1228951244a27e542ffc6b0161e9b044fee3e4c4b22bd328a4e6e74add87f77b1c9d69531e5d43380f7dad20ff3eb073ecb0a8227ea7fbcb85c314497700277105e705f8daa79b3d4fe7323c3dcda09753b9f99208f3622fdbb826165bdcc4508df12e5cab71ce52b1fcdcb218f1c0c6123ce1c73032a6d63b0847cf75ae949d8f9ebb14c2ca1b9a9d832a8cc1e770950a726e16a2bd5778d27ec46ecf4274757649301194cf8c486328b705e1d6e01f195a1d4660e78f5c9facdfd09606835fd764f7e21e6d48ace420eed20dc9e0787b6a8bff2cc9aff530e5b63396db5c0c479330577f3780529a2e7890366670cc2ff7f3466955b86b47a72300df87bf43f2c9cd2be23163151d041765f1b2103563f82bb2d964365e6faa7dd7705247d418ed6f553c67b121423d76f292cf20a483331bcd42eae8b63376e303b2890cbc4a7df739a4c0c81d7640f94d0b97c0262107620b2bf856b5e76c8792d86e362eeb7c033082c24c7ea4e745452c4c209bf5a053e576c4f2fc07417352903c345be0d167022ab65ced737904e2e2c010a9bfe31f13120de87534497a80e8a822f422dc69a38e246e329ef1f935d15840c065f0d9e6d1426b40a0d04936f1f484427486b6488d9c649d9c91d6919bc550699d213ed84011f2b749885ce2be1b4c6999cf3dd11ce329fb6672e614a82910906c0e65ce0a49194badf352d05d505fac38cc9ae4eb8f246d245328467459f0e405a98b2cde688114de7e73f2cd6b26359b30b185af35b9fe33ddbaeb355870a5ae0a86d3deb581df03c0a26f8d95327c9e10f35aad9b811a05a3829b81fbc02bd36781af9873dc983a8618e27790665619d9d0dd2efb6b838588ace940852005f615367bdcb7e3a6025c49470395a674bbc113728a0ef35583ebc5bdc8191008a6adfd195a2741ede9a6a9a0e114f66a695124c5b1e28125df3a98d4407c40d2f0439d43ea5110ad5a0e9ce0dcbe747906f83e6b020b63f65be447c30081c0a29a66b074825584c67f3eab551fb82d2cd16e1977fe60e7ba4554ef1d986530774d005fe259ab622fdec78fffb379e8ca7b16c538b1185cd81390fc297125006208af0e06e8bb93d70f2aaf6b3102453ed4c73cf0842a598825f7a3d4d3d040d2ccb60ce93c393392d11f60da7b06d786db98ba632726f2cc73654073fbbf10aace74a3ae6ff693b3325b7efe95b5f057b6b05629eb4e230faa1a776a01abd09adea1a20e7e1e2ca29b497cab9c752406619a1d4e0f67e1166be08a1b57a7f0dbba02cee84e977cafd3005a8360a4f9f3807dfcd973bcb0f60c6f6aafbd6e802772fb78e769a009c627b665059ad3c7734504e391f7e091a64ac8abbeeba840a871f7510529a982f8ed0ef0bf5e2211f19284d900b894c39379d72c0e54bc8018d7dcb9216681bfd8ed84842125041efca0add66d7dfe5c720c0d7a4b01c1c0c9087309b98c3213897d0159864d53c70b326d5d786f17fc62e2431c8060db901018315b20162ca127d03f5fdf689a57c7a542f05add01f500a25a0450ab922096c71e4c631d27d2ca09049dc81bcbf61009ab17ffe167fb8f10023352997850f3ee3a87f8fa039e9a1b22449f08d531cd0e248e41774653d56d99156b41b5609ff9f481d2e3b7e968f4107068b36ff4a5da44ed60a88f77b7b70f55a690b590c749fb6a4a3180db6a127f548b96171bdd8fdd124766d5fc22a5ddfd48dc07c086eaa9034a7a74f62a648b7a4f8d09ab2fd59fb428540f0ff1ddad1313f415506114e23f574ca17ac664e0c09fae3091489f8426be2b983044a5c7b922b47750a64a4ccb21000e31c85d92b335a419086ba5e256acb7335901e467c52f6e7a70db25ad7cb48d7ff1981a3b22264b3d67090d656599a46d80e05a886fdcea140069df7eed27c380cad9464b071f61c51e54eb35d977743bb49188745b8c8628c0174ef53502575389133f4d28828ada9b76cbbc82f419217f6395f88063763ec09f9e2d04ff0162e45b264b263e591f07580305f8f2bda16eaba552ed6212e9406ed4f6e57aee271e9599d98176f5c6b3fcc2e3fe6ceeebe48a38e3b8a6104540109af1233a481e61c1d8f7190c69b49c7d18f39bc3811398323bc664c57573309f5bac76a5b51b63cd2c284287884c2857dd9f2ab87d306e67c8a637adc085b06812f76f41dc1527101783a2b942e88d694dbbf5df525b22d5793ae7f7d7e8807113350e14bf7b58b7a4e11a052d8c2a9bd01d320ebbc50c94fce8d859af908081857d11c50ea4f97f7b357fefbf64a9365004ff3abf7ca7bdfcc4bbc2aaabf097d068434d843dbfaaddcffa3c183731f5484060467a732b61a0f15192800020f15e9807d6ea828ef8595b182aab943f04d65f267cd61a50244ca27a03c4c4f0bc4d69be239988132591adbdd8dd76dcd85647d768cfdcd5842ebe8cca88d5b04f20a465d4bdad27813d1c00ca527b2cd3fcdafa14e21d9423da6cc7c04c87f07a1454eb92c178d7ace7c1ba90156d2c550a49843ec9d13132a817065f8c5a3030067d24e1299a0fced20b8e62fa4811c2e2dd2dbc1d7a87f91939a057a570405ac9df5afe55691f94c79afece38c030f6f355ce4ed4784bead56a3d21c6f76074e3299a8cf37ed66c9f76e055fea6dc08d02d8a4051df482907f6425cb478c06433553a8d1da6b3e35f395403dfc1ff6302109e1b6395030934f32dfae4ce20536892f029cdfab6227b5a3e527cd0be1e7339ba449a900f1529fd654f8ae3d04f209afbf4b1b1c519852a6296ead7d150525389761baedf44ac6071f878d71050beaed1e520f631a0bde169a230038011f07f2f990ad52f07625021a254b4e018a4ef75b6014042aef20a9225d3617838ff1b37b18afe43aa449e3b49f9e2f05bd89598a4eac23c70b3145b1cd1577d8dc2bec5551165956f7eccfa5a507da03a65f6ace76a6057cb4064998eb3f960a5f91f4bfcb40a9a1e0806d22781d2d05da58e2aa1b183b5782e65442511b5ce6dd776e3f4a2ce95e8a3cf900df1783041477cc1dd735ead1425381e8a80d123c979b1a835e0928cb2b84984adb60f40861b5ff393a7558baa72dffd3c6ad39f7dc023509004a12722353db205e431606c9d85a7265c08c38744ed948c8d526aa252a7bb8971498faf318fc9652955802aac1a9b15cb8da39c54e367a3f3ea782c2b52f532f04fe2bc4c6e81bdcae0108d3e3393b7a7179da8e6fa02d219050d13ed3968a392200abf8df9b2139f5b50640d7f253f8ceca1c7296cd353559f850c5e5d5403eaf13192d3715f9c6db0d076a2bf0fa3228a45a3fa3bc4bee114dfda6565d38e9a70d975a0bf649dd4bdd0261bac65cca7b569ada19ddfefa126517ac5e2ac3441ea014b3f84ec7ce69a104bae63da0ec377e02bc209fe08262aeb2a3182aadaf87337937d93cf0265f880ec4fa6937027da17ba90de29893fdf65d0705e4c1c3c3ae497a6b8f702fb3df0dc2cd00e48ceddbd668f4e4bc5eec6d332500515bea352e8d897ef7d49e76390a5e298ec66a838f2216d3f1f0547199f40c4c4a453c8cf014c09a0a1b25e96c064f6367eab9c1173194917c15fa15b3b7a7be00dcaceff71c265f2ece50e67e0550689f939b8792e202a490c0a6060a0a452185097974c96c4e990ea59d0a140b23ed8bb2422b96cb27a662bef936f5e2f7ff3ab4b019a4d6f6051e334f4fe30f5c932fa98971075c6483fe6a2ce29810318ce787c101d4b1ba15a457dca7cd02bb2f4f7678d41bd32114202f7492548957f6c711754aa348d02143681a5f4905b82ef496b1535f848bcd4ecdccc4c8427bf2403d3dc0a681767d0f7ed7491b002d11c85ba1b8352887f981f5247d6fb62e23d034c22b202618512408638b1b03339c7867ec8ea70d7694b5ead1372b657595fb3530a9738a9908fb0e24fcd306370324f7993150b96215ff3a8fec8f1158f91712ce37086dc7a59d4bddb588054f22284152d2b3b1e591c6b27b1b6af1f92ec615cf5ef9076fb90089541da5026f651189cdc770cfb0cf6abbcdc9af8e36128173ccbd3a35afe1829f9cacdb0b6a20ec5035a4a4cffc2e85aa284e4dc6829e553906f8d39b3a3142289e1c080b5da4e5c489185ca64ade170750f0c3c872b899a6e89b62b644713c56e035b804f90d95a071ec6e1f8805edd90f8c57e8d886b90ca362d6416fb34063cf928609b5fb77b6d87135e2a5eb160fa79f24761aaac4e7bb740bbbf03e5a3f42a6ea0a432d348c279af085217c0bf2fc05bce508dbf1408a211d025ea73adae34cfb0968c223974583cd36b178aa748751209e29a0aa9602f20a2d7cc2cc9411fce3032a742deab670b9550cb7be44d47b8c5cde9362a3642baa92d48bb3be29defb0000d4ab583104e1ace4703894a88c9283d6e1340682377f3d7414a6a002a3e705a8aacf50be94c1c56b699aa95f7e6fd92d8cbd4397490e3751605a62f97c1909fb71ff9c5115abadad7e79cc844c4891df22a869e47febdab14da164c7715c077ce0d312b79887f8dd7a6606557409c654bba0929a98815bacdf90df9c42a202d10bd228f4634f8a5ada3e9c147cdcb65c509594db62959b5c891aa4f363b40f58d6b8c547a85b527bff20d067a12eb2571f7dd98e857e0861f62fdfee5540020d590a06d9397c973a289e3d9a7cb95bb3f807db306158c931f2c30872471d0c33c49e15b70bfcd3c238bf2594e222a33676c0259ac6637668704546e142ae0961cde5a4ee3a56c584bf8c86f232aacf5b6758d2f15c36b7ac7c9f05e8f7bd04e4913b31ab651e05efeb7052dd8277765ff5c23bde4ca362f603a2463a6417053bb56338e1ee29bba920fcb86526a3fb849f57b5622ba1bfd8223f21f947d80198c0dc70e966a36d60c97c3081e84a495fa9c184515162d7fb0379293b192c0a2e9ac7ab23089ade0e4daebf4eca54c658ac16e79f0562f206e1bc8f927b190c79f87d86dbca24d116c086fd3eb0a2247aaba0ba1d3982bf92ec30841fb12a0706c1c22266bc4a9ec7aa5bdaa65d3c78d0ef04d4e7d31bf4f0df889a57bb18002b3e7004e9f7f67d39a89b72f44141bfb7beb00018536c5c06b6e2ebac45d50a3ef547cac7806d02ef06a328fe818ee70a906d4c0a462267406eb280c8b1be0bc734ced8874e46c5eaedcb4eda3f5065fe668e58bfdec19dc44099739303f708bb311e72e172c8500c093e336afcca4228e12d14e7e669b578fb9e03898abf0599a079a0cf3c8bf5bcfde3feed2744edf02918c1712c6b39961a27162311350cff9289e670d576c142498b8f9b3bade0c250d6511fdcf2ac65398845bcd308043315ffc67ae233a28178f1de692281cb749c8867de7ba9115c04ac1e67709e0e137eb48d3b09ae04b0e8692c344ba6e8b6ae3306a1918d778845d3264d6e660c10442576b87edd5e1ed80115a39d5c66510b4287af5bfc2b329239e8a0ffeb0a518977958b11b0376329e59ce00fe2689bc4d2b9b67b98e0ead436ebcb94fb065db636235c8b94a11a249c14c4f26c4d137af658cbc19ab0ca250961fa3d1d05b87bf1047bee2a882a3c8b14c8ba50f32742081a1c135b3c19ebdb5a445b730ac123613196b99b058962108e5ed86356c103bfc4de82f16c18580b7d9a27bb00ac153402cd742a1259a8db7e88bdc367119c81b204187204944c2e42601637019c9c50fd7ec020d1451809b687f3f068db50b4e8a39475f8662c6c76c86ba607ca94b40b3e1079fd3f321f4cac7ae196d0301327c7dbe6dae5dad63d4d16390b5a66ede93167fb71b1bff91d17d19baed6a5df2694375856c99eb1e7a7b0d1022cc27072ef7179bc10ab3ec2db193de33447f3ad92263db9bf0a5582ee2bc50aa947768c343d11eeadd55a5b258dd1b8209d2a40441e61a82dc80be68958af008059a3aee9c2313983975c472bf015831f0d3a9a6c0b8e75e6b9002421bf9c0d7d4a3ad73c95fc6ef174066adf6da2dc78edb5646e5fa4f4b54ff330014e7401947ec253ac9c2965ea0114ec9970a8678e137a31ecc79d230820f7d66a926c06bcde6d63da5fbb76ea477d86c5f9442b6fad3bc6c04be7552845618b709a350a1005be0514567e4b48d8cc1937f32f962fa8450541b1a6b97cf7a8197bb604077a0fc5e75da43f626d866fc72bc3c045b0e728eae2b5cd7c25c6171504e070081b591420d8251683d91b69ef95eeb24efcd2ea4888eca0dcd1e1606c35575504776e54e00e3682c416a25bb0e650889e75c395b5d3745fd76b003cceb78b100574f900ba6b907313b8e1a3e3911ee034323efe369cb42fb4f10dc9ddefd3e70dc2d7675edec4a31783422069eda787f42735733bd5e885bf5cc91598385b2f0cda66e63e0ba6b0b079e0f9eef7b281df74514de287853e3183b8926b8ab0050a4d1eda80b80bf7a8663eef022cdef9b49b5359cbbbba4bec5a8e755fb7a8e304d2ea4b9285fb762e8de825ef8989b6c79d80f9ccf627992d3bebe30911abed07562c3ed0df19d8078196705dc2e530d6168872697fe9ec82318f494d81b7dd00e89510f37fb753e6e919836f12619ac5f42922292bf68d04d0f669fc74570605ef1952e3c7d7cb4b5f230af4e4098e42c3fd1d0cbbff9c3d66aed4d385601f04912a00071e446191f8a576dc54b4a1843634de988cb5518c64e2c484eac5b409214f3aec60478be5655297390fe4ce4c569822c0d349cfb818148a5d3ad0930808f40d8faddb86bbee303eeecd980d70a6d326bfdaca3234f24cd1d5e94ea80ad49888b14ec6768dc5f1f2666dc1d0da9da22099bc33b2b64c5d757292399d057ef6cab0e919bcc72806b6be6c2de83f4a83f52f5bf390e69bc7c279c53ca20ba002ae8b978751d01388b2454a1027730b62e0f50b1052edecb9d4bfef2ea6075a93d40b3b241d26d26221e4c6ac078d3faa5e20267c5b90735fa2bc6a036200561bd27e254bc28a15e05ab65686e2ba2c87141f90606ed2085e8794e59a730eab983538ddec3eca8182d44d93212aa0385b7212a48d34199b9657a39cb6f9030e476432d2acd77834b896fa2667a0517e2cbb90804d3da16647255738e06c000c44f606d9cde9c614f02c8b39bd3de77239fbf71fd26abbf65aa459a20c340ac807fa8efbc36c879611fc9448c7b288be230156478b6a5fe9612297932dfb006d2258b39e6347e7a560d29c9aed2a9cab6ed1c4c02730f411be183e8746cc08badde9636618d17541656c2d3b29adb73be7a1d946372a385605581f7089930dd44d04a4b4630320d67b8300ad2abf82a09c49ce1dde83f5021a5bef2ef069079d019caf190f32e9b9ba45abd4819bfc0a2a088fa11f77bb34a051a041bb4004d082cd3a4a1ae2669eed564c0a5e1d1421617ee62ba61e97cea0f24f19572e021bd3283d07627d59673b929bf5a706bcdb54901d238c8a8871fb5deb842de60218d891f5250e89d63c78932d72c9996f017b528cb0a0659d95df403e12b88a0d11f2b408054408c6089f2618cfbec6358e8b50e321d41651993841cf11994407f09608a97ebeb79a1a02c4e92b8313c24e70a0e9ba5ccbedf26bd18d9d8152091e905ce9baba0dfd43c13d28bcac56cb659dcd5f853d465b9e065bd6d9f2420bbe12fc595918933a8cc6f0f02a13777c777ecb8732f8977abd928f4688764b0f4490153fda77c4802e00bc720b85ab986936bedac95e3ae1bc5c72fb83d8ee02b18b774c56d1b5e8a0137d153bdf16e39f239c31141a79962c079a664a47650e2a5a48e23e2a20a82f958714ba4b6ebefa43edf8284a66b7e9d950f9a38d5d0bed183ba5f6b3f328bc50c8a995ce513cf922a8ea65b9d62109cc35955f82de03bc498deaa2f0f013b64841c317dae3f2797eb0b5033f7c591de9c001fde03a055946124caab7773263d9d499b71d924e3cccfb713865fc12f88d7bbcc1d1a50bd01b25055b53a67fe44d11ab7809ca6c9841026b05e282c9ffb28c02335a6c0b33267fdb96528a4313a7a19a54ab6af0de42950b908236367b30180cbccb830f8445a270ba8696f91c51b3f99ab2b37a48c36bacc8b60e168ef7a348ede5ba0c6a7b0c86d53de5d3af4919d40e7fdc9a7214bbdd97abbe4eb5eccad9d758fa0c5fec0f076dcd067a1d699e7d5f71581ef1cfc9b810ec1ecee98a3542e8c949088419f3c95b2c73252d7ed2fbd13aaa3adbb10a6ed9f9e9fecee741f3ebb3890d8cbf83eb83b5351bbfa4787793e75c11d51b80ef8afb608d4857b1d6faebe4080fb1b8d69b094203f78cc2312aacee53986cdf23c96d3181ca6b108c10dfde062ae92258e0929d0ee3038ab0fbb70b0df02f78fe203249b86a4e483a54929f07a1088a5d4a400efd984742efa00cda5d0a5e396076e1d0a4c7a906fdfbaf820623f29772932b9f26c63e3e78aae4fa79cf4b569f4ece90658c82eb9aa0659b0f839c86032aaf72ee5e834277308162f19985729af0fc21f96a3ecfc91a1dd10dfa511155eca952b071550c0e067d15d75a19775ac744ebb4ce7e913532ffde020d37bd305c5f0a63cc7ad2f5bb714a3f73fb6c18946e49272abdd3513babfe03c9fad33965b53dcb506dfc7dbf325c308cd83e360b3361cb156998dc79900a02fa852db74398d27038e6a805a7942dea87c529bf7c97857b61c81d02b638480b897d8b8ee5d6b06e1e0aaa3375f28b9b11998f9644f973345d6f0931d26c410e8b2d06ccb2d7be1f80ff41a1aeef00255c9511b731a86e845eb05164d3046802636d2d7f59c2b909328d574e8f84238db71010d421ea03d79b353f0c5ad32a069b271d4ae78beb72e3afdf4403f376c197fb3814a18c135e22e4cb7032166d0c6034d6aa3b26049375eac910719e377d9c73cb91fa0dc72fb3c2936daa74260e1d81b82ab1bf9261be6ee42f1039fa785071cb474b540771953048feabfc700782f355e06a5e57fa4cf64679b53cf3f917c58c0d3e7c9b7dfd23624baf9784027b53b77f3183510393ab91b8820eea9523dba15c9e6ced65e586c878ee379a0d7f1b58ea05e54a386f40f3621430d0d39454e120f7fd426e32a69a94c5e77f0e2dc1a019eaead12bd794e4eba898d203bb264c3e8af1b5ceee4872eab16e7a0c7d4f1ae1c1a1e97ab4d2b2e2b9f9862810fffc6c406baa895372c7bf252c1e09bac4f20651131dc487fd60489dab1aecb3a1a1fc628e5777aebc4d33ae6f70068c27f5e80c3c0cdac9f9a3d7d5dd423a23dae655222b37e00aecc00d39e40f06db09f83590612bc8a0c23ace281018fcb84125229d05849ce22fa24266852108109ebde425b24807c09a4be7889f9f6bfd151a82ccad7fcc71a73fa6f7b22b0fb2ae4d7930ab842ce89c6286f72ca3edb2e5517f8a2b10740df2aa4ff500510afc4e7b75c1f6211896b68aaf84f83f371b04b440bc0030c4df498b2ea56193035800bb9d69c677ac42400fac66b716b40672337786cd61ffef2f854a1b2e6a030688ff39c18ea36e684034fcd31037eb9d7ce15f23e9b750a6dcf7bdcf6158061a5ae95770ee24b4631f5856b019db3ced63ecbd912ac4f4418c3275631b9a0fada3885ffbc3e77d411831ed026fb4acda588f776ea5425930c5a347e8906804ed18dc5967f9c02ec6211df275518215909b3500327c298a34e3278b94ea5d0c92d47f8a911ff48af2bc8840803bf86f8723f496f59595adbdea5517fca80308581e5283f0fbd1bbe2b781e7921fc8fb528ad5907ba7d4c47cdbd4c67dddb90c29cc743d57a5f6ccf5386026b46670ee547e8a5a572706050aaee31cd7307f0426af789bc4583bb52a30be1983bbcaf0477a9ff96d4bc4bd6574a2ed4dd17c0da471b0ad1a2fba6c4ed923ddae0e548aafa53228e298b6186008c0ea61dc850e29894f98dc0c244d7535873b4303d9cbda241380f2079b056f914447a946e405d0bb72d1286dccbfebc5231cfe4c60f86cf20aeed368d1b589d4758c5743990e100f0f00b23da52a24917fecaae9780ebf8968df5d2394e74763da1a4892ee033c9c73be5e52a9d77f12ec675216c33b58f29289978ba8d8c0074c26f11a1e0844f2eda5a7ee8d3913d3a22dfafe5d150791d3cfc8f5f5b8592a1c644bbfce012382d59f1f5cebd915c9e04e1304e2f083e45d42c5b67f00fb40d99bcdb41109e698966696873e672b60425d5cac9d8a8e5d77ae5476950afe5c635dc08c2905049761c0596aa2c5f320555e77bb411c835373aed3a8d64f988ce5d216e8c50e02111a005a1a7776bf2a76a11c9e47c1969cc670646745e7bad5cfed1d42430c +generate_ring_signature 590a993f96a147077c4f5cc32f91a7c802e654a5c8586e9598d0b6e4fde6f48c cb9194bc59756cdca58afcda4960aefc51304bc4f6ed04c6c2440488f42b6a40 2 87fde03c5506336f088a68e7bdbaca0f4f0e8a9bc143808305a7047194242664 8b7ef8a586c5feec0dbb2e72d8036764ff5da197557bf179192e31aad1d5beff ec722447735f098024e362a928af39663703309bdb68b3a5e7e42ef7601c720d 0 6cdb051db188493325b1e132abdd978ddee23a79fcf20b0464c21664888e31035fde14ea9c26dd22745afaf7cdd50fea33056e5f68bf7e16167bdfa2f3635009306bbedb2716fb5503048ec1f3182b65f1919a42dad2a46d7de705998b0d8c00b4dcc465764a0782f41c636643bf767bf325039517be584d7a65f12553844705 +generate_ring_signature 0f36247a48d15d7ef13a065c7b1a1ee40149510d7297677108020d7b2a36375f b0226919b26683f35354c80532460ae47ac0c70b2f18a7155f9582904058976a 1 24c4f1e4a4d51bab40ff04be8bed03b4337518300c1a0073bd59011b3a0fdbc2 3dba1bb1ca997825b955163be13e92cb7f823c13db9de96570a1b539c4c1f607 0 fbe547a84c9ee1b2f58e589ca65a0a454ee43bdc8c886a377daa9d2aa19f7c0a569df96c7da5b83c53bcf8c7d839700fc3fad6f162d59267a4a6d7415d911909 +generate_ring_signature a1831c2903f8c324b1693adecbe806b81454c15fef50af931c62204b3e135bc1 261a8904b68f5185ce1657f266f83cbbb7cda81229e313d568a15cc55cc86521 40 9f6fcf297cc2dee2ae973a51eeaa6e940388bbb75065504f449cca9b25267d00 98e264b0c531599a8a579074b2d927f85e0c8b62c437f6372267a275c4e10169 42549d197b01ea3544e172e32b102414187e707f3bcabb6b175a48e280ffbdad 5cbc67dad405316a18ff8863ff2bc32fbe25e08f8c291e790733e39e83c42947 dfab77a0540c2ae33cc85efc014987cdc19cc435463a61152c70336fbaae0715 3cfd622fd739f3eb972eaac07aebf78208c6558bfad030afe4b6b7af38ac5581 2625cf12b9544cbbb8cfb6c43d176cc0722487328d7ea20a7ea435c7beeaa4d7 aaf3c52ed69792553ce48eb77242d350a8cd10ca9cfef151c4634a284338c15c abfb16ed19fe8aba99218c2734a87c62966e7450f66592a47199615d8f08073f bddb2d703ab7e9b711ae268e900ce8ffc7ea314ba16f69a839c8784a22dc1cde a4a71e5b7225ca67a6ca194f5f650cee3ab4aad608f27e05c2a72d5ab1417399 397f57af3d7105156e93c47c7e75205ce4b199d3d668e701414f62bed9f30a35 aa89fc94e7ab1e9c47e2be820e4b852e2841333375c7181534c1d9f6338b2b22 12574a1031709e503b7b55dca440095b5ae32e7e49960df3f13af0e3545856da 2cd8ad3c4c1479a755671fc233c1ca15da6d1319d58ee7fb46634cf5e6d2c2b3 f72d700961cde8dd195b2bd34b8b33c27cd38ead4ff52cd65f32597d9f845c25 dc50f2aeb860353b9c32c9163d2f371cc9b1966ea4099b783db95615d1b347db 02f9a85d776b326a14e3c116f3e34706f831e81ccfdc4323500d28e525c22fde e46577f6631c36676b6cc24d154902c03d8cdc427b8eaa9a16bdf1600dbacc5c f61fb173a89280c2c853240f345f1f4acba37c73fab295c6153d5b0c84ff5f29 78d202b46b95461b1f0850005668ddc15e38757853e48bbe6f531deeffb3c5b7 b4fbd260b5c4d963f1f1467eaeebdf072c22141d53d5b87d4b8b98a162977021 496e1bc0694a985482d5e4deb35a8f5b048d3f6af8d0d12a8c7e8d1425c16cec 5ac968233f0006c4849db8c0134ff928fc34212a6955d2fbe6c4741f29ddd1a1 4d29eb966788a97031479b226bbc1fa8b8124ecda89c039add1f48095bbbc2ef 3c721246302983cfc7fc5505fe05ba883e54bb4b50e87eebe01a44322e356cba 634e670cd777a9df99da4be3ab467801b4d87a42d93e057fa798e08fb239c91e 7f972510a0eefdc9ceb8ad6bc4051d8743642c8899ad90f81882a972042aa120 431c662db985b69fdb1492a243237c0b6575a137065ffd665be0df48773453cd f0c20daf7e7d96d1c1282bfc919af7cee54795c544bac82adc854040ce5bb142 b7429f8a8bda10eb7e6993550f7e967b69a4ae04bcf1be65bdb252f0bde3f097 ed293f21c6a1269a56ca8335e5c492d520fbde3f70b877f22a6a780eb1109bea 0a6b31f4ca18c8cd4e1b6860664e445c32a314b3cfab312f7485b35701b8d033 ede2afc010140fb9b7e89924c1a3696d1883d3255da5aecb64f64ba3d471a90e 1dba5ccec546590f8a9ea6e91170f0d0c505ddfc6793b150feaf64637ab6dc2e 9398df9e62ad755ce37bf7d03b555316e7a3a54714031fc3630e997e8ca226c5 39cd3807d39ae3de4b43789511593e57e3ca4f44d3ab87de4dc50fe8c79fbf79 678358781b39ff78d276740e2c8c2cc482c5bad694edaa12b194bbe9dba7f78d f965175ebd6af2efde584f6bec3180fe5da01682603e7b9a54a610da393fda68 793abfdb3fffc1c2669b18d68e3239c0662766c5745a0f715fe9af5530fa6d2e 07a99a07a14dcffe0a3dbd5e14332410dbb5a035cce4a0171ed6540eb09b1505 2 5b8f2db32b8f2739071b6c4839e41001423ddf62c2cdec572de0e94865058c06c8b7a4e79c37c20e21a880642c9164aa532b95ece108f018a39ded89e48ab500b832b4ad82163649b2bda47002ac8054e36831a8da997ea155860697ed2c2f02e91d69f842ffeed056a28be2156a542d458d59edb1346b95ed1d78e7c04d7a017a1715710122ad02921df682089c2b7e9b61b3cebf68d5312585ea7cc758dd0a38c80238489d00dd2d200a7d789014d9424cc53c671560f89d499dcf37aa0d08ea9ae5b3dabceb342afd551006a735a2dd1ca5e4cbb34918c52f20a5629dae05b3af34d91d73878dc794e19927e7ad88c70e3512d6e6874f4620acbd150ca70c3f8581df9f682aa7b4ae93db7a6b2a194a7ebde275e04c13702c26df94ee7308fc006408eebf1c39968a0cd7dc6df99933b0ca7724e9dfc7130cfb26f758120e9cd51382c2a509927845f7807fd06b5d5b0f6c2baefcd032971ecf225a4be00a55d4cda55ee6ef81e77cbb78c4246b30b794351980f591e71fe98f8355b08902a9c93fece9febe0488b9d1d0ad3b46c32e1b11d02bb83a45f56f1fb8b1089a0f8558c1e58ab09760751a63a9f1a34eb1265edeca6516bd35983f8deaa3c14309300a1b6d671a73261fc260963e438cd174b3b10883097c371a6c5b534a6c890f0107111e98069aed13e5dcd8ff3e8033a22b20a58356eaa173e3b4c5629b54048faa8c4564ebdec1a86a22e8efec9a7aaccbdf30b5ceb73dc19a913e4157aa07be61fb36087ca3d5b3c4338254ae693b9be72125579e4be6887a675deb034e0fa0f93342171f10a6b301d12591347138a4452f08310d511d77b2c86db8d3b50ba8bc1022f59b3f0debb920dcfa6ad32829f1874e9440aa3eab0f49bdb7a1260b645e56fc93792ce94a830a2f2455161fffc7434bf6415a30311e1aeb2f908108f3886d6dd8fee7f359b241cefab4276258ef836503d7077e7de9595b474c500b1703ec7dd1bac6b0aebb57a5b7fa75270706d21b955685b4c50e36a2da0f290263b76de4df0c17114a3e909b8e4fbf2a37cb0560ff3a76ca8df82972582e790f1d67a5f7399d4cbe094d2dc252de3163fee75bd9ab17a838d6992bc3ca0e9405f81050902f13feb35d50de7d1e3ea9ff282b78df49a32c70410849465a91d00c2b86fde9714474081f9a37327faf111240ff7ff81dc5bdcd3d800348325e0d0d281385d2a07936cc43f3ee625e977005f82ab07cb404156902dc8de21f5871052210f5a15bdb2b8f6224d7cc88980631bec59f756427a180242becabe1dff400b838df84724e49938facd892481f18384aff124d065f15a4a16ef3eac27b630e3a76cf606855bbd0e2eb097ac4360bd004ec2feb7ef9e12cb7047ceaee19a70106fad75757b02894f017dfb2328fdc72fc2c64a1feee065cb781f4aa5a549208c1081f6a903abb309db7dde9a1fb4be5db81b80e93885499cfcf20e263813800ea2c9efdda98b3323a6eee42676b4b7577f4eef37cc8b06fbd7c855472f2e305b5fb29e247ceca85b4198e563310093713f6101bb1b2f03b49181f8d857a350fd197d6f216250b3775ba694794d122a6277f0e1b91cb28d853e9af4edb7f6e0950c6d97a4d8646baf4667c78827caf90e9f948a131e051b95dc31a1479eba70b3e9664e415344fdf746e8fc115261777209f8208a0892167e6e47383776c110aa66e892ba130e61b3744e719cf4b503f81920537169b8a9cb9797516d80bec0ab00c7813e985a970815ced3cf3d154426870a2e1f9f1a1c5151f25f54474080c81ce850ebb25accc0a9d456f5cc5577469b8653a1ab5f2625d906f1ff88a410332c99e06696f5037068f8db6c96efd4d8d54df5b1096b06ec2f85ba1b1101500391ef6956d235c8b5c37aba56f7446f67bce0a370492ecf0883c9949cac27c021230ec9bb86491716fb65fa7aea9c6e2deecdc85d9074b7c875839f359288f037a9e339186e2e690298ad57c95662e2e2899e7f33ec32f06b74ea2c3fda22801f773f2ce3070338ace22c3feffa8cdb198c844fdf4e2bdc7ccb546f0405e6f0308f7ccc5facc037727078d9645a6c8193cde45dfadc2ebfe661b030d76bbf905a2c1ace08199c5dbc5ca671f9b7026b0f6804f4aa77c87efa62fe61eafb49a0ae78e136cd1b24d73c7e94d872134165f6cea6582fe278d425bd2df9050b8c802c2e713cc06358da730fdbba92dcbdfaa0f72154cfe48979f5d5e1afd3b64d7042ef74e7f63fe2b2b25a039e1351caaa91512d827c763f7445a8610b900a5890e70bfb237d7b5341090da9a954cf9010e26031bb8248327173b15b82cf87bb708e446461c1e7d1198f02dc08ee0408c7dbe648eded6846960745d619c0b2264091ba71ca5760deffa13cf5b07cd93960a07ffad6f0dac9c6595c92115f2740b0a6d177f8747b61e33ddcf107edb858be6e143c2a72d987a3d75a2e37ed750b2046a054dd740c021aa622816b2546e565f05f78fdd7b295db143a81a038403ac08158cb03c7f741e32235316141a659e06a0f7da5ce18f6055e9f2fcd109735b09b3fdfbfffc6c634c7d76bd0be666466cba7b35113e561c0cc445ebc016c37901062ae0e1a5db644250e32b5817e4cbfb9cce8e5995af01905a95b23808b9da088f22ed2488cf7f64779ed7dfc1bc2f4fcca4bba8934bd9f17be1d26898f54803be0317bda7e600adeb9b4a2971c828b379243676a23947fca75580c3c8667400e922a8ff1356832ab1563287495bfe1242a4b2f5de56f02b9e2af1173e59570bcf4a2677469a88b8de816253633f344ba7239a8a628754821998322fb01ae30e2957a883295145ad4b7e3651cc008e6473d00f0af5ecbea249db9e7b10622505dad2eeaedd3481852521cfb0d61dad9ef797697f918e8744d2ecf37948a2a003e18b4fb6d0dd1e5c6c45b058178ef75d4a6467ea2013bdf7d5a903366f382906482bef4bc476b834ed11a854307d3a09d0d06937d07f47a11fb38a5120f6c00ea507eb0593162071fc570e1210769acdf2d19e4e7864c188286de498336c4503600beecbb9669fc81176c471900c0508c63980690f623035b380b942db27bf0da5e1c34d5a2386b9f585191ff59d95d5f2ab94ab453e71a94d79074eb9bfa00c448c4fd19738c9b4eaea1e1cca372b10fe7de4bb7ab6d9c14bf58f1ac47a3509f7968fdc46d86ee9d49bc132e9c7c6d09330209cb2751b9e9838e0451d581a0b4bcb4dd8d264e5cb021f604155df24f375f693e406e88f4e9e3fa935eebcac0cd5c2a37cc33c741bb494676c710484f00d5513828da123360e1d4b6427294900f746e74e783a85650b27957a0f1fa6ec828e13b5b702eea4372655ed569b0d0f0282f2d11eafb5783b01a0be4b591a1ad7c24b62274502ab446d896fb59ac60ba5c2101e9d0cf5854cf942a41cce01e2863a492b750c773d6a71e589201eb80d2e0141c0e5eda8f65c5a56b2c3c44fe2eeee59cdd8a2440c69a1f9340f98fb09c3b380fd7acb101c204a652d001d7329d154eb37ec91cc481b94db779f21eb047a74c1e49016f06b0d3bd5a622c8e097dda9ddab02f6da4c650c499f8d1c820f +generate_ring_signature b9ab0b10afa5860d279a36ca738754958805e5bd3290604096442b8a4dcf9730 425b3eda70237941990ea68b9889230fb9badbbccf08a74b7c4a59ce552adb78 1 a15898bb31142d00cea651b265aa4ee7e73189b383c616eb0f6e37ed23ef7eff 84c6db89df73d478f2725b0ce36218d81fa68d1cd5ceaa8a1f8f2f10892c3005 0 fd75f1cfbca2b0c17988fb391db4fcace441e58ec3610c04fbbf4e3ad7c2370d43398ba08055cfe30bb7210ae3d1e26d580013ce15e9a7446b2c7c1815e25700 +generate_ring_signature 77f35661d7757d7d03c765f11350f4df7bd28708a37163e7520bd8c8fc622503 2b129b323db1ce4fdb915ebf2ddd4be3ac0798b4f9fc3e7188846d9993764fd8 10 ab241334d6e35fd6340511d9c72d5a18159c125336c76cc06eeda30ab23ca126 80832676389cb95756b34c8df992e88936b79d89114dfcc8da1e6eccf6320239 c5316c4465366ab16e1922a835d9a8b2f2ac89aa43e2f6c254c703b7cdc88519 74ea52737c3db895c2b38faaa7d1b47496998d4defde5cc72d070a15d622908d b1d7742def18ea0edf1a0bb3931b3777d85902d969b9a2a4ac30d997773de9b5 8f6ed97932d63ac5c74dcd60385536f4d3905e5697e6c27ee1fbe15edb0bf7e8 be4a83371f1d524c26abdfa898e92049d4f3471acbd5fe9b03cdce08d7fb3f4d 3d1d9750e65c8d279fde02c4c08a86c781880d3747b0d1a9ee063bd978c0921a e9d2250439b623b02e4915ba6f3eceb0a74bb7af6bd8ca70ba49e477f69f5cbe 09aef8291d5f369067a350126fb37112cd0550cf6534ce110100066ba0a8a7e6 7d6228970998328079db9ffb11b5340bd972a39cb5fd090f400fc2f4d9261008 4 2c3d6a3451c7ce0426a5704f312990c040758d0ff1309ae349208d4e412cba047145b28c3fc4a1219031df5b4f265215c2bea50861760319b34c0f3b73c78b0c89826b18249320d04cdad4fff35841220e9c5c26ec03ce569e9cc796bec87d01dc2abb4de17cb29d7f1c09a6796949e8a752a660b3c723bf709afabc9df91e04babf5af074d528775d583e0b247943be9c582251c902e973fa7297b4de0ce002a0f7561f9de2ac6941281dbb0be67b4dde21383b79588c6994f0912e1ada5c0571bef6138c8db1fa6741de3dacff01d93093e3acb8f2fd218104c5fd461b750d80bfa0fc47d230bb2e0dc16de4a585d64e8210dde3bfbf4c57748f9bac2e2609e97a20deacf6cc3b96add1478622cf5b57bbee9f718f856eb645a2909437f7022e8f2f09f3b3245e574decd65e55ea716dbfd69c10baff663ef9d1d8b9dbd70aef65990acc1a94470a719b3db9f37eb8bbfb87a0cec86bc827c16c606abad10a813caf397627ffa3d70ec8729e7b8f194c8e79cb63e72a628204a7dd96db0b0fbe815ca9dfd827e343c845b6aadbc2e99a7567bb22f3c408b8fe33722dba3209b04a80d481492ca510e76afa5b83f5d826abb5b16ed1e286e5d3d9dacb6b250d7ec4ab7b65ff6d7a84635b325c7e2b5bd5d579186395746b289abe3e5d933d0bcc87ef949cdacfda5be3b111ecaf02725c1c901f807bbba49257b4b8d1e35800f34241a9f56106971af83837b87c6f95dfcf7cd2e55ba0d644d41d614a0d5f0647246a1617529e4af6adc449c7d3fd0b599d23fd0a84ff680090a51eb821ad0c61b2cddef7e79ec969b4c4802e709b4bde35d392d3557cbf6e6de2502156b90f627082ece6744258443f92e5a055577754bea0b6a0c0b44bf2c4d7464882f80a +generate_ring_signature fe0e906842bbf5445baa4ecabe98dd6ef27226ecc2b5ad9834e08c046d0f578a 0e4320a4732e89ca8a53cea98b44492a77168316f9e8ce95fbdb5197e9d14d62 30 b7258c82c9b3e0adfadc33a81ca09cd307e70ba065c16144f46f552414a9a629 54d94e93bfa71f1ea7c1d64902bd8df7602d7687e43b983ec3ff6ab88b4765f4 637d164f669b1111c41a5533ec52a4b05a515ee5f46f74ab8e50cc481157edc7 66fe4748dc0ea965eee04b152c0dcbe2a5fea11333951d568491103296730f02 acb9c2c966da9415c9b09bdce3492e7962d10ac1c2a5e46938a6651e09fb5516 c2d7dfd2f498249427aa0eea3b192f4802a3d212f7fda58c6961e231ec0ddad8 c830fcb8aa75343dd3722955964dcdb9864c7922b872c1a209f477e923944379 bb23e3cba803bb4750739171050a55f31d479ec0ba27f4c0fd5e8a207257fd29 a6afa3958567c006ac5a61fe4d40b5e34aa0f796aea13c2ed3866a1f12fb3e0a ad6765229bf2c6c88971c254e49acbe6cfe0310049673e1cdf346cf09da3f56d 0edae6331033f401c42e320be461975a86552f196f495b3a4e3b0f547617e963 a920e55b3c7f3a4a8653d75ed1cc731e31ac880e175959f6e267fcfcecd22955 b4d509383bdcfd434c407c5070f125c46a46733da20a3f0fe9979df94c31d5bc 25daaf9f93a6284f702e02395babcc7598031c6940e7914a93a8e9ec722e7fc1 2f1c7e264b1262b445f4178156119b3fecc1f458f7044cc4de63304e14c97587 a487d33950e0d251e92691a34a583e13aee472b4c841843ba5fec5b3c7656ccd 48971038b01b0fbb7ed269349420385f1231bca51ce21913572ddb33162c9619 f222a85417740af8ea34ac66322a4f494ca309d7722ffca6713400e4be4fef81 696f40d88004d1a539495a61f35483afbde4fb225483f1bbf7c2f82c7e59dfde 9a3b1cf51901567f6e504c0c8b08a503b8cd633d6f542c9020cc4d238e6c00f5 3fc2d5cd18e5788a96f016cbff76554765afc0428cb260219b81dcead3edc978 ab62d587fb8948401a9a3cf874a024457791d3f2532803e39e00b7e522cac759 3dedef9bd6efc320fa1bc5dbf0b0313244efaec9a7fedc18667bed47a1e97af7 b0342dc39ce8e3f1b5053747f6fd3a450319d7568779dbad042e76b31dc816f3 8b3365279c15fbb8c7d2758fd0de17503c48d87aa9802593c22ad59a27d845d4 4566ca51354a7da4bcaf7f457bf803c6e32ee109a939a69c342ad61534831c44 da8e0ff478e7c6b7fe83467d5a5cf90fc769db0d8230eeab2e79424c262229a3 2cea0bf3ddc9e0ad18757bd6efc37a6e5923dbbb76d22a06463f25177223f3cd ebdecc636abbbd1fa2b166bbfd742ade21ad6a22dfc49fd667cc807b8f51db2e 5293d8afca91f70430a90bb0d05bcc4e6062b20578ff2cae721c767b6f929fd8 33c6805f3997d25bc420affd33636816459bc9b8cbc2f2c65fe2ebca22879b0d 8 9eb3443b52a8d541fc0dd9b4f1d0807b9bc5e9cf6c79672f3a53960fd84ca10445788f3acaa4df522b14b7bbab50e7b0581e1c297bcd0f7f06d43712be225c0fa86af386e1117928a5df4e73746cebc724db20ba3a1a835fd96419ecb851b100645980ab312a193b5c480a684edc9febf18000bc7306b02f0a2671db4bfdb400aefa3d9e221e000cc0c121daf7977005286baedf84fe482faa6276f9816e1300ed0c574792c6539bbb812e0e9dd9414ec9fe7e752b1ed20f4e8e886e29514406ef65cb982a9e2bd4227779549121d5c1863bea2eb2877e656199315cefbe6305c2689dec309efa27d6bbdb5b9bde168e1b747b7606215bdcfba9950f31a6850ad45d4b623b30b09a350cb710adb7b7df91fd7ef369c0f5ff8103d1d645e9c60c2920bf74cfc36d58fafe73077cbfa74c2fa20361a8b24b44835f0f6d2426e90beee9356d310392f802c1a34ab52ccfb3995504546eb3a9d8b6c5b8f977f3180800b001e292a25cf9de46b3462dab637e9a296d16d034d5ed8d6b5d039ea2670c071982eafd2ae5e692b0b1a66ecf016193e2a7b5b7ef114cf2524a4623251c06acf4780972215c5c375a2a2e4bbe637f9ce7858a6fe084451389be7e4c6f58081ba550c3287f36974dccb9433379067e7626b7ffe231f2469f299dc645185404dd04adb1228d303c459ab917d1237d0838d2171b7b26161b8d7188ff18d8bb0bd82441ee7ccb2cd324320c7d72e0e5aeb3c32aeb807600154300868bf3afda0300b49e37e244040351c6f6d2be8212c1717b05a2606f4aed304f18155fbe7e0faf684f2ef03ec401f9f0029a52dbcfe37bc846eec343eed97ae8862a3d385c0937594af033128137aef41f56d15ce6f35fd0749dec9d8d7fea8693911234e30121b83759262d5e3a12489d405b2775e072d59a47fa5c7d63fdb9731a77438a07298e3093753353ac9223827fca0599a725cad6c8a1f90630fbf50dc480211100240c4706354ccaa03d4d750caa9785276ef60115331dd1818bad57b45e7461015d3e97ea37e852dfd8202f17b5d9b4ebe72c5d3bf75080536407786a042a1c026a4032e5d1b95f849bef992006147750295329c7892cde5c8938992f0835c40164722b07b4f22823d2fca62826430024f05a251e38cd26dd201e9aaecffe2508440ca1d438be15b7d3187d4dc7e8cb8c6798a0086a81d8129d13b8be9087ef0e8ad5db0979ba37249f8bbe11b22a7ab8320550cd7713c94d36c8520dd8758305797ae9cb593219b6fc8ebbaeb3a1f3860d041e79b9f733756f463cd8c8adb10e8d7a1b9ca71fd87c0294d7f69bf2167356e10c5b5c2f490b4ec1d68449a0b80438b1e5d00f74c31d342aead66ac2a17578493674b9507f76a234ba886666ad029355355fb95489b89682e35e9ad288de2beaf33701e7bd078ec8e9e0061fd20b2e730218958e2479f256030c68c1b97b8deb2a3e2d77bccac3cdd07d8fbcef09eb980a87aca6032ebb34dec70846acf258dbc147c0071def9da0aeb3920ce90ea268ecd38514f199b34574f1af45ab62279d70e15a9b2007fec23c4ce336440c965fe7e2728bb8657ecfa8a13751c8bb611c01d11e9023695e87a199e862270b58b536ee8d2f801a9dca33d7f48854fb432cad6923bdf95af59d4d3fb35b24018a181b272c6206044168b0719da3f473d05c2a7fe2c0254cbb3a0ac51990700fb2d2633f7bcd569ae7146c0f1ecc9db593c4c37cd2954357a469967e393d8b0059d0f147a4b628a606d96323f4784e9b1e923478741d06d3cd2a13a709755501e6eeb698a242e14693bf8a7450f7a8774d7aa957f7cbf01660c636141c657d06c19dbe686b547ba6d62dc3c59ae3f5f1d967218aec23fe6b494def21115d5a05bd1301a86442a380152c30bae6d623eb85f0dd33634e231de178738a5eab10024f03f7700efb88122be88b7e23e5bae2d7d9ac36cac0bf308612892c2a09fc066c396ddee1752737b7a820bcd35a681ec7759a3ccc9c0b70f47249fb9bf3640870abfd5345feb1f034cc276f3d3c8b250905f1931bcc0703a66bf6018787520d6894b58beeff41777a509076b68216c09cd6f4a5a8fbe9045500ecf3487f1a003779919555001e5c1749736bb73095a1277ac1acb9859ad4031ead3c7c16b30bfe9e82574e5736a0baacf56cbd6dfd3fcdc2aa3a0d4010baf1d22fe4bfcb4b04dfae9b5e7e49ef88ddd959c23327ded194035f07406a21830f34049db6fcdf0047236fbf76defeca1573450e8bb6f519937d105520b854825a04d150301033009a17f81175d24703c3e5da295be2ef589a46c1adcf3dd0e493af7900241ed805b9f4f8ddb3250abd1c698b769f00624e85c48f95dd5fe0726f19e2c1f0515f08aa59068ffe6eec5f3862536f41e69bd7bd53a5cbf6fd67f5dbbfbeb7e790e3040dc97c7cbc0e3f2d574ddb27628de755d6f7af504f0fb9ce7b2dbb7b6b45cb04ee948cd0faabcea3e2d23a119c4625abaa4231814c25ed15e16d893076e2530a5fc5b2daf9a492dffdc587555068c8efa9c36d64476872e70be513b06638600fef12a4a8682b371ee60ca22b60669e462371d04ef987b4d2092cf44f15126c03775a837f4028bad9749b4ac9eacdd99bd4b7996d953b5dc21ac15e6566665d0e7e90b7de500076c68ec74ca34305f6156e7e34017145320cca59c686eb52cf05 +generate_ring_signature d44366265cc5e6e8a13f3d2f55c79baae89befbc5ac99d063e6e0dec1d5f8665 345925032111e3150664a24dc32937232ef1bb457d164c57c24ac1160fb7d6bf 1 0875f733705d14f8dc398a529ca0bb633914572d8849d084dde057b82a946fa3 f51168e99b11a987b49c956969326d5ab4bdcaa1313fa7ad146b24932208d607 0 c5f770668602662d20226301fca255f0df8c7b89e26ba282ebc79b94364fd0035638e021b0ff2f2f6e92eba40cd775aa6f7958d5d9187180d37c090ca9cc8c0d +generate_ring_signature 9b43013f26543ae86b8194bef3e6999d7a787ac7d9e69375e117de5f6a963a83 ff20e8a898dfc4e8c3ee2864418a757fc9c52a5a6941bab0c745d23846a4a0f6 5 551ababef74f99377fd5d68b2ad3a2bfe14ec6b4afee4c39686508184f87cc8e 25d84441eef268e987eb34f24749b4a13044e341e6399a5cd6b155a87e6ff0b6 c3850ee99faa03c06b6574ec52d2916e32b399aa82286fabf4936b39a916e431 42dd11fe00b24c64342341eeb6643d229ad1673a51f09192b3f9ab4b93256cbc af4d559997026bb67abd8ee6e469dd35577dd529ced3a657726d0c02edc3d5d6 b34b5b99dbb26140e778ba6fb61449721e4c7c8c1bda6e70eddbb6c0672e9e0a 3 4865be89e5c547bb98da5822bcdb92a85c98a2c9aa2eff5bf21f212f8cfeed09c1633b725947aa069c4d0842ed007f9aa8f3fcd3df6a232fd20dbad713f47604fa2b387df7262aa30456394ddd3d4972d7901ba8c54d830f005a87daf3988b00733bedaa0a03868b7cff21b9f7a7b2c5eff79fe08c249f7a119868236874510ea0f2e7e0383c97a7e78f43555d4ed5f1a81191fda0533c0d0215bb225886af097d07312ee870127d57dba00a4bd7e5ec165a53f7c916430e6e4ea1f715a2e3011a46c2d425cafe22688f639e55ae94c591c94056d102a771a57cb855b306fb0af181599841542b2548ab2b14a3ceefdfc123c0d23ff51debfe131e79713cd70c650c7acb0249389c70087564d0340eb8cd8341920ac71ef7101c5d05b76a0d04d6c9e3b7c4a609a6066c408127693181288fe3740758bf4c12c0d7802188a40d +generate_ring_signature 3c53431eba5c16dbdd1fb2f98d6388541b7b85cd5dceac9238d6aa7dd5e8ea8d e9c7b96e13b6225b03c9c774e90d2e3f71b7be553dfbd22cf1767888c4f83b45 16 70b95fdf50d68749f55586737433d11e0506886dfcfe08a82b5b01c173dea87f 9a8bf41dbb914dc402d80b6f1510e0318fb908181d765b578776b034d6359ceb 87a438d3d769aef03c070d3853f17c4e470a4a7ec5e4a118a8f00bb0966490e3 0ff0242f7714946d5b872f9fe6873d237f1f4fe501b88119795f64f744e8428b be4c9bd9231f25e1743419a2858c014ced12708662761fb36083aa29710d1120 94b62bad7ce972fc7ee236c909cf19203dcd46fa48552d86f85a2dd6379e4e6f af00070c55a10d7ea2ddd2953e593fd5cfb29eab6c6b676472d029152120fd95 e839d97bfcee72acce077bbf20856232fc8e31d27fd71da70a6f0600f85dc9a6 f35bd0d64ca12db2c9548255626727e57b4eaf37b42caeb5c4160e5053b54903 7b23cd36d3ef9cb17d8afa4346fa949be91492241c2eec7d02329f4ef37be3cc 7f5580e1ca9c5ad57941951ed0de16dce9a031761dc476b0a3f4733bfa76eb75 6bc5897d154e23c56cb1216534610d1d75241d7462aa11b8a80d6191dfed1354 99884db56a229765f038e573fb018af4e2c4e75e9750908a6dfcab2d4ec7a821 656458506acc81d90dfe82decfbfbb6b5cc2462b6e18ec798b486add559ae372 0d165d983a91a7228b44dd25f84ef8246fca9530a6f7e15924c102d4f4b9c038 83169f90ec014494f2e8dda969dc1dd5fb60f47e3a4386945b787928e56817d4 580db78b04c538d490ba0e0cabd646e357f6bbbf3752f0ea350165b3fae41a0f 15 8c8a00287044c0b083c3137365ab72cd2611abec4b7a7b800cb473250ab92e0da194616bf7f76bf5848b0f64b800bc320cd05a8273cb58c0f06dc1a117f39c032114dfb4ba47c7fae3bc8fe012a9fe0876322330aaf5f60068ad44c4beac2b0d34da67ae15b691339c51bf8fc0ab06eb480de096ba989ec346b98ca5b7cdd101d0371834480b2ab2aa759fbcaba7c21c44983d51998fd76782e4c3bacab6a409a9ad50b32f69987e9cb1e273117d2f1c64f540f93477c346135a7cc8256446073126e28ff988849a5127405768b21128399f0d4bf018e3db08508393a1ed8705378e795153c9fc6e99cdc69f0cad6efa1b932fa7a317e6737e9d63e17b87c1063577dfeef974e82a74cafbfa84f1c67b0b1055ae2fa613659ff34a088990ae035345fe0285439137c03c72e234872875bb31baaa249df56f14ca0bf0b9066a0b1af02147f735bfe562d207ff3b2a88e91f8f11dc5ee6c0fd769f2b736159bb0fe36805a6780ec62a4dfd121fad7a6ef5f4303302f61cd421bc54b58dc986010c550ad6ff49a1a4b818547f5550e1b132ad887f856a60b913aaeb866e22cbc60f31649c227a5597dcb65a7e391fd2996e87c72affe6d5693a3de90ec5f8d64c0167ea55430816f1f85ad67274c235bd428b0f1b3b76185167c4b7b314f39b3c0160b63afaf1d130495e677b1de3865bdcdc06d22dd14612a764ce8dc71cfe8f0ba4026efd5f13da7368d45a8879a4c0f06c2a290ce3eff4fb79465d3a0bce0d0591abeec4615e0005edf47bd6f73ed4935478d8a52974665429f0d464c610270bcd1485a8b67f7141723e9d4605fe047925e8374d6306fc977c41c1473250230b25a480cb1a318be8cec94783d0d9e8c5c1ddebc1ce78b84728fb36488ff2020d1d4d08554d757b7595fa336d0cde8191f50e471d219e08df5c33293cccdd680eb34f81b5da93e1a450f0a3059f238e4643ec59ea19513d733800acd83dcd0302259cda4a13932e4123550795d63770af8a6533568c78d288ac95626d77d4060e0bdee47ae8db7a3d2336b495f9a3611d69659d5ca7e83c3e40dd1286e6cd040388a27da7674891c4ba96d02186c6ff01564c927c691e37e0056d45523ea1db06765f8b6900b11aa51fc24ee8580496a5718ff21ae9f5169575808d917bb0b10a2076f2f5ea019cf7602051e9bba0c0c4fd18a5ac79b1741fd5f823f6de57ab0e5d8356634c1202c3c1f8ca50fa8a7bb64a286a46f8e322c92fa64c93c739080942f261516a1c7d6c97037d1dcb5f7744a6fd68776309e23fcc4a8d354a658d0d67c88778ad32280f0681db5101493cfe2b73b9e12160b73ed918012cd076ab0d16ca85c95fb3cc9bc8a06ed2aecf0740fb17793fb62682dc8d05591a47ff460746ce19bdf90049732e63575b18cea1efb698ceafbdd73d5758973bd271cb0c08 +generate_ring_signature a2848f3b36302284378d0f599c7fc0b7911a94281e33d4eb4d47f7554971f7c4 dbfbd2c763cc046137ae025d4fb22914e8e7182f3cc727d2177e5670ae95aaae 2 184816f5034af3d627d6d835e5c2913953f985f597b11370c779ca34048fbce2 85172db3a5e5418276b0b57bab2962b26cb6bb159f3608bd31d5eb6fd507b799 d598ef43cbb2381ebc6a1585786dc1afd6f3b2fab6ec7c7c020b934bd44f8405 1 e5e814fd7e2aa476364a67c34b32928e97fb3184e1bffa18327a6f3f087de000ba044964f187570aaaae6ecab02dd2155a4b35d9b45afe2d228fa6b69d72e50e83c8578ce076eee06b29cd26dbf14527aa4ab28224e1affc226fb73e1b5a550a4ac4261c5c1a4ef49a071912e67b2aa0566b417073dc3d09fe09bff37d109302 +generate_ring_signature 3bfa708865ea752237a4797383b6ae2d4d3c41482ab53d42ac8feba9f80b62a2 2e4d774207848de566e97eea6262bb6ea87dddf04f5dcf911ce9914e9c97f528 91 38f7acdbe90c284805550b51f1e9929022e7d533bfe75bea0dc33f717a523e4b 817262805accdb4febe4c20a8037f50aa1fa7a00592836fcd3d213fcaf022f45 13c55f83cf89d5272dd1d638e2b3048653167468c11a8bebd335cdd72e7158fc 71d23b3dd7bcc81b2fe24b32e9d3e57d638c21ebd4039a73c15bb2d6455f3b8a 2d30c73750bf6f92c3bb59771cdddc7d0d6e0dee4d4ef60b443224b418b5689a 5c0eea3463693e350aea35b9b75c918ab6782971cd4aa830180fff60f6084ea4 72d4ba8604da472325d32ddee00e21d844275c3896911c26f94038f30a59b305 2d09540ad3704fb6791fe07161f12cf7c634b173b4348dc2fa2b2d91b5716d6e 6cdd2f19ce8037aa1d0a1b933e2c619c65947fc732acd11f115c62d40e77ea41 db94bbbe5548ba971a53006889f253361d986258b9a641737b88d6d1c1a83aba c23eff960fa6ce574cfe1f74248c29b10843be110f8321641c2147baeccdda1a 47b9f3ad5306af76c7045c93b914dd0a90370ff9e3888129421ed8b78d5b6725 b749de5034ccf7c0c1bcc7a76fc3b71a90b395f8ce74189101c8b78dd08f176a 9c5c2afa18b46a710f5c2bc387d61eec6eba8c59a00781486a5504fb35b8e46d ea7a0f8cc1e7896b4de7ad84a8f2223c1f4cc320e466ddf80112b01e9f7383e4 8408d1626f6123a3c60271334ab9ce1568f4361c9e8961a9813b2f82947dd0bd 5ffb6207a836af8db9df4bde8a1fe896f864adb6d4a1699cebdd134250d3627b c719c4908860d4a4204a100951cd6f6a5f523fb1c25af8f4e1a7d91566042583 1588e45a7cd74dbe403112737070ea504804a951b592d86308aadca05e19bf97 2bc329ac65a06e56aa65e146f5ca37b15c707a1a856681f1b6438b876a257a62 6cb6add63061cb03876eee0de9733db3cfd194b529b92638d74ad1e3197ca55e 869f1663036ae025ed0696a551fd6153fd0eab16437e4f5ebb740975530cb4ea 565c6df71b17757170cfd147fc7ef7ef5442d60f420779de08537e4854242034 ee1361f2ed36794b34210f0084ed1e853d79403287ac7e422a9e2acef5d43171 396a2402d29083eb8514421bfb8b841f699d607565131c44aa72680c47ae6cd4 bf3e71778ff37c494d0d588e4c240a7a1fa28dec561e5fc95ce52d0795d30b0f 0b1c1afd2a06b982d99df49358e06aba690af29ddc2f1c6d5003e6b44b670eb9 0967624c84307d50ff5823e51dde55c2a0f4dd3e54c478bebfc74567bd7a1b27 12a0cf3cca9791f9d9188e21f8d5c4c5632ea737e4a548a29c6116009b1f583a c26300d73a72480a4553d325bdacc91f0c3b10b7dd53d490cb3d9a69ceab15da f91ce4b28de23f339c6ffdd175ece69c2718e2a3d9b22a80961f6aebd990f09b a5b04ed97ddd4a071ad49063c159c6e1e5bd53be621c2103efa9944a504c430f dd1ce2e7ad25135322058928f7e0176d77097063eb384130b19e30357b4d1a31 2987d815b4fa2b7214a0eca3d95b3dc8bb79ba433ac66aad97f3e5ecf8a67575 dec31d9c26307ed50d5ad50c5698b88d561b3732303d0ab09f2ceb106b2f0af5 7ca0b73fd6dc919d8380b22294516103ac26831cacfb46e0907223290d5ecab8 aa97e38b8e804aecb8e2c1da77776e3d0582341b2d39be6a45f8c513c99cd5f7 cb32604020edae3f1ac43f49a50364a09e9277ad9481f1632c7e446ff24d376c dc66cd2e4cbdd497703f1bd0953033cfb6550f46412250c4b62a8e44f20ff7e5 d713e361ad8a2b8715bc3a8bd0a64cdf4fe19d6f4bf2c20970572a5aed81c14a 1952d47e81a3e2a02d59f47090da2972b89d8952d498bd95d24256efbcf56fa8 33ffff80dbb6a235d0d6c0cc52dc0dcacb4a692cf09cce398423eb8742080853 1e328b3b594f3748288881bf6df6c2400fbad42e1dc53099f98945d0776357e6 e45396f2fc37f54c9ca3af36970ca0ddf908dbec065b232678d5fdf9cdc115cc 369cf4a82d27c83b295dce181970028fe53d6265380cf87c01819e36d1563ce4 7a68d3887599907fc20337d5f7b7fc4cb4f88dfc6759a0a35bf7a2a077659f20 0e579c35139219430eb86bc0da2d37f269893ffb6d10834a6d04f0a7f283dab4 52101d8a151e0a535bd2f5fe4db6d1826c47e6ccbd8a06d1ea8e05537fbf421d 3cc96ec853bf5d99eb69530adecd6aafbe9a95ba14ad506d33772c9de1cfe1f4 0ebb479ce8e81559493318897f9829cbf14499fb639f99c5a6432900b955befb da8e0e7f63a5f460d64b71a38263100afca62d6ddfeea1ac0db0a657f1296af3 119a595ecfa1514529f07f7a90cd78d4b7a98544fc4ed0141ce1c7fe2a9acee5 d9faf2641c4726c711f0d4136ac280e2972425b5ae7a3c27bf5b1171b378909a e807cdc919a38d3c286abd67ae7f0dd3376ac8e9ac55716b52b5c44fca5cb5c1 59c7d6c794047bcc905097d7fd8569a5e4888aa751065b66de32d8d00890bc79 39a5852394a61ea8ef293b244f4a2e3251939dd6dc383940e4b0e5f49b963c10 7fec3deb4d91e02f59d75bd1e7ac2e92b5f8030d696bf88463288e7a12bd4ede 9b1364077d41d4c6fc13b5e4a77e52c27b2fd1dc7765c1696ebde46a07c4abb0 2f6956d8fa81990810a2ed1f01e04b4484c0fd3a76cd84ddd2a9619e332e1d88 5e135779ff608a35cf5da0e30656090fe76c2cac427604b732fdc826b7265198 8c27f69dc560a8e8a68310264d9e65e7ac88beb237a9abbb7708266844057ce0 efcd93dca818910bceecf477ea4334af40922e56c9f4db357cdfc3321abb0f68 fdba4d25f7845c2858ca4e6c1b077a2b7f0f54bfba930670c255f254c277ac2e 04a7e77ee772a997a8954b709cfe1f660576adccc0bc828042a6bf9978594618 917d17c135e20aea7caea1dd22cc6752e4fd5ce05098d2836a146c793eac61a8 b45cb5ba80a17e6c0646adaf9cbea1f7dc5f3799a826c9f088722a5cd527b82d 29a200178549a18673e0eb92e8bd384dfaa695e130c54dab6cc44e826e7c3112 c8303040ae302f47eb64d676166d16b6e38b8996a88e49a0cf1a679da3dc40de d0dd2eb2093b1bb607c5b53a9df148a7af9ab8eaf50e56a9a19c4366a4b873a2 54107acc6285e280bebd6c5314d9ada8311dce901bc812d700a3e938fa628ee9 5b6748e854d70e02a91b3642e600fd32898279ece0f10729127de0bafc7db0ed ecd46f5a0d0719d60a71739ea93af27400770994db68ebe7fa98f9cbb97f5e86 9a5cdd5c941a30e5840598daf4e0e4dc646775ed5080ddc03bc88e74e57dd7b3 b76f4e14bc66012de09bd3fb98724759e0fb3cfe68b17ee2f236ca35fcbc058a fb0f82776ca7899d5cd705d784cb0a30d9a705fb5817497d66c048709392fd8c 6ced641a3d4c402a7bdf2de0d000f9512d6245cd946971429ce8abbf13ff56a8 fef493c6555d43b0f2303815f086b7de4c13eca9fad0bc7cdb75a118bd433392 3ab468a92ec76d5acd3837aa83b7ca933984be9c167dda2a43e86f5b5a70cd01 155bd8eb9c7efba4b2e45083e6e70539f68b0cc02a23aa1ef40d94424b0cf257 e51320a48a75b74d223cbda29a41403195300d6da467c8d636bc909bba0d36b8 3fffc66702b2144fdf6fcc3be9545f7f5ee034e399e4db1c44f7d7960cf0580b cee707102ff886386a1afc90ac11eb95ff796df190068216737f85da4a99a85d 27a33317c49c01e292d23a085ebc2af814f406665c43f9f8e064234bb9022a08 de53a7f01a8b11731298523138bc022a5def631ada76f4c513a71e5398674225 585c96a449f6712c701e231b526202e28a7e7878d8c09c79f103f2c734b106ac 55dc8746379b0c79871bb5c3fdcd4d07f16567e4ae20fb7ec2e794d27785facb e9a7d69695afde442c58da9339bb8d00fe3a90a74f813f00880f28784797a309 6ba12954a1b735fd842fd5534d28304d9819625ac0425b128850b5369e2c88f4 d41537e1b86925fd180c1dc27f7c6491813f3344d4b9e7bf26062f3c93a4ca24 d04d80299b1f40f97eef90c85799b317154baea13dee98f721fba3d6736e99ff 9629084c2f4e36d15a963af5ae25a8ae70a20d6d4029b19f313cecae8c810163 5eb7b30d7c80f24c1cd46f488db56a4b930cf1c595878682025f0315d1595401 43 f336d6453c3a30ec8367a796c44102f82869fb1d8cd15a83878925b976db46082ce45feceb7e5ff50cc41f78318bfcdfde684c1e5529ecd390b15bb3b2e6b402a14552344ed57b0d80d60b82a61b6a32de5683e84ece310ec4ae54c3576d5b089e80d45da5d6378508cc8dc70a6a332abba95313f3b19d245e5087f51bc4af08542317f9339a58e606aa2333f20faeb15ed86e20afc3044889b1a09904939c099525771d94d84068ed1527f387fa7171d84a83d4ef28aa6a035ea5062e942c0805a161f960a680cbe7fae0411b82699c4787ab597daf6b280cab430921d1970dd17f87fc1626c904d558f2cfbc74e8d9686ff3a0d05d8fbe693934f26b9aa000fb5ef204151bea5a8b39cbdc9150f3551d3d2cd9de389d4fd4db7e7aed3e8d063f461d8162d15bbbe55909cb9da4e4702413471cbc1d957357e6b29748a10608d021f14dcb7ac9761740e169b9aadfcd4727b4c99ccf66c00b41f6c189ac4a06eb96d0f2f3f97825811f42ce789c07c9bc02b24e554aa471559f15f87dbfd601753fa7cce2dc4403e22e365c9bfabb11ad394d041d11c4fda96031199c83160bdef56df1066ccf8bd41cfe5eea2affbd596ff975dc1eb55d9524f0417efebe007a15ef47e71e9a8abbace6b33cb89c40e532219d8f76f51092e9005ff5332f0ada57076e7d0900234eaa853c5fff469e14cad4c404a721900f8d9095c2cb2b00c6e0574787237836ebd0ce53d898d55a967015cc3625d5ff8e76662593264a0bd5b3b0889e1f949569adac819f53044c912827e9eabbd2ccba473532aab815065fce040587740f7f56f79aaf4cd31bd12d692aebcd2a6a9ee9ad5407e65eb50d722d9091d301b49f0eefbdfa3ae7ba7e6e516e940970e125e8a963e3bfd44e0b0b2ef05799af592610f66866f375da52e0fbf9f9bd75d979d6ab7e95e9a71706469fa2e5a12ab5d788fba235a2ecf8f6cd7d3ca4d4b99aaecc73ddedd60afc0932a62d81433e4f91f9fe6c197749334f37fba39fef3bcbc4ba72c036639dc309fd48db5a8a41b4fa128929ff9685a9a0a6036b816590ae7358eb10af8ecb9e05e0a311397c03b0bc5594a6f1a94fc2ab3cec20ea4c7cbf2dc462aeb6372e9903c7718317e03a76cee16b341f769d3666c438184bbc22387f6273e2d7329b460de2a40c00b52d11dd30058a9d49aa70b84eabc97cfe8a6c4c7424b52a4cec8309d8e11866f197c0b36cf097a5dd1cba984074194b2859fde7afd8bcfb8e757c04a203ea3dc101a6aad375bb151eac1b36461b7d9ed359e47e462d1083b4eed709fe77aa12c6e98be0368fab0fd09b0c7e8c4690ce708a9dbe60f2fe6ec10532025f48755e1c8696373602b7918f54c954e1dc8ba0ab1aa3cf78e7f87906d47705d8c4e378a3bcdd3216c944c0d4fc7631a31bc54103481955d59f20bfffd882014bf07b8a5600ffa46baf94023413edde9ae24b2e2b92fcc67714795230405f001ac0c9cf6b52d63b0e4dc2322ff9dd4c17f300737279bdf178f4a9bea330c30dc7f6b3470d81b338fbf73d53585612128adb9d86df73703dc8b57b6def5fdc0395b4a8861604f7b927fd6f19c7f54b3801c62ea0e282c25901cdd447fc2b790612a1ad62947946ce9aa32cdc4906557a1d88610a078635b3d7cd88df2589880aecebd228623fbcaa4a5b8dd86c1b4b54cb6107e1408b2493c9eeda4b89ddde0e96f832972cbb1a69998799514dfe2d21ae7a00de1cf19c6f19b30652857d4102937914e5536175e3430c9cde0e742439313a6ff6acfc8a3e399899261291a80537c1a8296128ff523fbef6f67f0a0388d43839b17bc711fd19cfee42d8d2fb02890f8f617fff8c0c74c7edd7c9f1757588038f0d7f2320fa65cc02c5eb184400495a978df82c24ab7374b0cb95fc2cdc22efe70744472340dc650d7ba81fa90ea0fe27c6d86aac4ba162f80c81dd51a5b957db08b0ccb2147077e5fc39f5c805db08788241aeec6ba497a8b09416395b327343ad86a4e186a811adae66dd970ac3e9323f2f639b5d9fa9ac20beeb3c2e4596ec5855c5fa0ec13db230ff51a900f4bd76b44541c835a95d52a1d97732773a735b343450a013333e068fe8853b073bae8c01bd3e89782df7d5da58774f07c919c27ac21dc7727eac824d2491a00451f48f2d62ab8a3d383cdc7eff66a83eb88d3865a037f5b6416a63f6cba554061e5f076f31e448933e066821069ffeac74a8b1dcd20dcc4e095f4ec29ffaf20d3ee9ede95ff2ae4bd9dc8a615334ca5f27655c455655776147baa73f58ac15099dcb17d6c3d178450f4f8369cec8d2e41584cb9396a95e2cacedca6cdf45da0fb37d8ac92775c798407d588661cb3e1848da4196d29d6a1e39c7bdbb1fcfd20f20e648a526f184c339e5ba2fcb97ccce3746d45bdde60f98ec978c5dbb81fe03a91e1e7ce570c555f270c390767b8680eef6171c1d247728936d1d407699ea0296eb0b6d2f1d11e0004fd9b918efcbe13a16bc5c0ccb9a1b4468eccba6a7760e8f3b87c88dc4adca7f8b85eff3e3d64bbd2278c92b513e89f269598ac170440730ab0f83a0975a57bbf932d596e72233156255bb8f75aac45c173168ba29e40230c5003896cf9b2a1a1b1e594ba2742435c9cedfe576714d248e995cbcdb13069391bacef5055fb0562796f076c46ca59124ef5cfc5f68a720233296b3991b038232555fd81cbd09423c05f7cc34e2a802e5767cfa62d4d134391aac04629c053ea0a240c5fa0e08bb31f222d841cffaf0dd98ea58ff9b9f98924c84e2664d07120ab9995ed6972b6199343cb7bcd6ed511d5f051e4d9ac07885ff57d9ce6602fc2de5447578204e2802c4b08ee2d70a756be0e1500a2aa0b0d25d72eceb780383d9840d4332ca8f56aa43e08e31ed5fcbf982f6fadc4b75e2d7bb8fd3c8ea0bd261d27c8edc81bfe94cb431e3a56646d7c73158312b058f1928b165c75d2a055e56621219f274c79e48fdab342c9a4efd2e50414d2deaeb62cc7b57facb9906d7df14f39d7f0c9e5cbb966be6907fb04826875fbb5e12d18fb6bee5cf37ba02f259769ee50bc6597d95bd5b7d79f20d31196717698d920778f83c3dec8195033b7099d88cea6f0478164ae6c9001511989ae3acd759b34082de19c0dd28390d68ca78138739dd60cb5284c095f50504a470a6239689b3882864d6cc042331099a2a1669e55046b106df947a28091601618525b34f3f383c9549f9e74b0a5407cbb875f7a1fae649c93cf48528f25bb5713f79372e636d827037f343496202033e5a4aabadf8f5bdb2f1bac693b51b558db8eb6cd933515c0675f90676b6ad073e05b8b4ee373a098547391b31ad1dea62b001a9432df65ee2328a3ba8637b07b22a4086b14ebdb70937f51ec7ee20f85e9b2935d8e4791c8aac31dcb5d4a1077a995c779baae86691a9b89517c14044d9e84ef9b2770363569ee6c0520ae30136f6c6940e6a089d74d2a91a7c5bb7016e986f538109540256da1aa436002006e3e5ed41a940863253c4eacb25faa05a0170d987913b2a51ca8f29a4fdd99e023479c0cb0478b11c5ee4b9fd63963117715d9efde2de988812542f301a56880c5e40ab8a9175a7e91f53ece9b991cc6913642217332afed0217a57b40fe62d0eb511378d1cfe76a316f8c36339a647aa0bc71fc916e5b9d334bf0cc1bf7adf0e7bc27c7f9ef44603eae38eae83d420e9ddf9fb5bb5dda168fc4af47cbab1370d0900a1ee0ba632f8889d6cdcdf44df62c3162c9de25340d2b406330a472c7e00c598783110bcc6cb69bb606005bbeef6b043cb7ba652fed4c012e987ef26120bbc025592cf475bf37e8fe615282cace19588a8f396f1dc88a02b29db0d74fe0b1ed4dccc404594d9d2e54b62ef9e04b1e6836c2bf151d4f8a9b94c16a190b10ca3d109acd57bf9732b4586cec825e2db4acbd6f5a35c4b47df229ee9a1f68b08e096e63a2b5115eb72985e9c798571272952bd9276a4b242fa5d23c83e0ed20afe4d88cc69c7de6145d4963d87711ada259b3cde77df8db5c0a3679c0be1e608e68e09bfb25f17a82864d44a5b01394bb2bd47e9bf0c4d5bb06ba6854937490d51eeb4952a307e1484a285a24a9d83426395bd991c9c2d1704f41ff355b298060454a052881c0f6a133cc9d38146512f6b1a8218a3bc7b32b062e41451f8c70d8d6dea558bf1c8dbfaa1842758a5f05f0377f35b8ed567b7bf48da58e9dc540180efd88fab346c66b87f14955c8c12dfdc63c9069353a3f731ab91daa0831103813f6ac0c1c8f74f86366afd925913c08b017306499d507c72472d3d10ecbf0e72cee2a2425d35170221e056aecedd3081a155377fbb51cd901f0d2f02b8df0c898207071f47ece88b1dd2c10a3f5f0ac904fed7fc48563474eeda726b84b20e79428b1204d475d74eb519bb6598d976455bcc90634afbaf0d965cbbd42fca0650d2abb6e6a4443e052bbd787efcbea78a1e3ad85f0fd841e2152f23f45cc5047fc8488359b43b4aa998e41d44ca2ca1125d1f4d63d7766cf73104d4915d8f0db8befe72f1f64871081577787f8f1db19ff0067459513742d1c6e4c5d08e3d0bb938ebbae33ede5946d1e40f985a7f452fb76a614c8c1bd5556cf6f8d712ae005ac78ce150c16538e5914c85a852da119cc0388f6168f894b9530b6440fcc905fd9419e0c5bf1ed2673b41dc533a80b6462eefd42aebda44226bc58325539c08f9306b5bd00952bd77075afbfafd124999cd637bab85f3c63976618c7b356800310e888a66535dcb16bd5c790ae1178d17bbc16790d1e685574419aa3626bf0e29ddc5b573a48ca3f85d10e920eb24b944c0e15ae88add3e1e81ed7c6361290464f8dac9ccedc60746000107a47699a5473dfdf53700381751957e02a07b0308405223a6afcf95e949ce29612aa7ddb67b5ca47edf272728420100c2ac5bc2059fafd5d5dabdfcb1d31bb71b56de855c1d8402dcf38ba04cf6a8841849622806be820a42e871d70b840ae945945df131eeeee530f290b8f89e5fb518d107ea016265ef77bb910c3cbda66bd7381bd60801c70549386cc06498411081cc891409a7e99ce434e2481d5824e9ec0f7c4e93732695710bb852de917a62be9ffb040b0ceef880075d3318cb028b47f59363ecd9925b27fd987c256eca83f868426b0e8a5d3b408940d94195201b9e3676ebbdedeaf30b4fe3a35abf36d4e0f2a1dd07d4cf8cd7df4a68b5c2de1f65db90d8ced5039ed98c6d5b54cab8e26e739e7608a2c0061a8ac18a8c06da340a6cbd2868fddabdfa3e0c709ce304e651e8b08e08d38f570f572a681e0452cfce934cc4be342cd030ac2db872581945d5d0b446029610934d35e9ebb44629d42b14e1d227b3cc717cebf39b1c382b63d11e930603734922d22d03e0d72c2060259e1b71d2e35712feb498a2b4919314d5aa467103144494cfdffaf66bcc705937fab5a477bae868d0671017cbabb4e7fdd861990c29afa675c3fbfafed07c5e6df79ba67b6932f82bd5d266833acd4505166dbc03e325fa2c51b420a6281c0c17ac57970b5a3b2407b4287f8490fc47bc50dd430b544b4e78a670bdde495896ea2a5ac55f5b16d28bd00bc847e618c015719df90c22e0347e83b937bae5cab1411f54ee6a88b81749a84659c338a6d3dbfc51bc0613390a5c44f3155f20c4c8b7d7ee8723fbad528b0374d00274592d9d1cb4f0042356010188bbd30e40f5b63068871de6aee6ca4ef164443257e0539e9322b00b182cce663aaa350ab818b7dfbf23ce4b373b004cf28011818bc1592dd9047505719b94a330fe25192d4e566df7ac855108fd15695eb0625895d679a370ef390d718a7da5ecf43449d3284c5fa53af73efb2caeccbfea0268339c03645dc22305f4dce933d41184afe2a2d8fd8cfa18483c7a90e188d8274f4d89cf796d70b108c57411d249f1607847595566a20b430b36011bf74f0801863ca1028dd0489702569a85e98c6e12ddc382f3d097519d0551116d94757666c638f478038ac0f5002158da5d14b8bd9349e6aa9f24e6f9f68166bce65d61dbede0fccc153011cd0f583e3f0af65e0aa84b1426fd7c20cc95a72acdfab8a09a5ab1ec1275981ce7045170fe75a50a7b3c1de6ce10a017ed9d11d675c4d9bd86615131beb646b944076ce8557fdbb9614b40ed8b63dfcae03fc2f3ac6247c6a5f967be9f4b23f59f0b68849d1434be889efae22768e7f3c3b572a9afa58d1f824079c4689212b26f0af5dbe233618c5a46f7b575dd7fcb22b51c2c668fe35d4d9b124a64a0929c05053faa00c2ad98e97e27805aff9d3c23c66b74e342ccf48d5a44a2c25f2b791403dff4c04d40b30be1cb10b607a399ceac6a1d567c7201b5cb1d68fcffcca31c0519a9487e723dbb104c165e2ef621b1dab018564fbcac873c81b5997543d63206505e1fc04c4ec67942039f399c5dd9ecdd99c1cad6d85a978813d1b2c824180b752f4e9ba6951a89a5ada46bd08fa901ad06beb5fdd841d9012eaea30a00d20be0ac0cdf675a023c94f0361f11464dae390be8ec77da865fd4fead09e29da70bb41a4cb873c4ca9a1226c6bd825dac965f705a9f2bfdf91791d6b49d5f9e4401ae5f56f1b39ebec11e0bc52bd19adab9f33d4350ef32eda32697ee371a5f8905a17370a57b9ddb10c3a59d0f33105dcab393b615549aaf791d1cbe92e920a80b020970969a06b3afce3de607e4118db0169e988f542e43d76a03c5ed9ecd2a0a20c66da2407dcbbd069b179b800f29c482126ebe32e7174cdccf063beb6d190efde1df5884020e4c30c4d8fa83807bffa5199314f05b2b1fb8d3842fd15c4e047f2dac875263b2de232dff225f24573f7ec0994f7cbfbe3e50cfbebbc61a5f01ba5a8056d4721bed705564935fd080114c8b37873742e416c04c6d897b6aaa0f638e0d96c809f8e46b3ab671e37e8cbc73ada9f4ceaabfebf7c32c50a415e60f309c0605b88875e377e3d6af881e703823b36133c2a1d173e64bc998102df70005f648583b0024d9c56a45689a711f89590f7d7e3e1f7cc906c776ba3a278304402c66967ff338497897cef8f4756b533acfd2614afde3d8dc7677411484590cae3e747189445add323b37794eab1b61ddf952ae72c0d7e47f81560da4a9c40221f4e5848cabbfcfd67fa623954481857436131ae689b1c61831bbb9bf17770f7627a53926097f618ae0e6e7106209abdf0164e51264dcf198b5ef6a08f4c10faa9d8ac222f6fda070612a9e8dd051c20d1258142c67d3ae50d4101bf58a4a0a9d1ee2a14c48ee45369f2a10688d7da8cd331d5765a7f5567cde91188875b500ce6c9bbe42be21af3c550215b24d08cac0e09a259faf427d3b8cf2a990b18d04aaba48404a896bc92c5aeafd067fc325a2d5460b50dc92d26069af2e57cac40b4a8d2dfff81716189ef2a8febb6f06ac428edf6d1a0cc342b7d3389d40544505d96c0942a02804e8c7300e8d094bcfde46a64c12d87c62552ca5df1730c3b0066c98067fc7c50a810f819b16ace8e841ed6aee6091b1977028a992df92ee5200ec9ff4919e8106fd9815e7f9ceb91b1cef4f37dca1962ce297f43f2fd5ebc90cdfe99c8de010085ff020fd2f53e5ff9c5a693aa856c9913b2d6266f0f74d4500e5139b5e5076c94c66d7ed42260fb5d4f93e0910b6ce736d28ae1baef6c5ef098cc3fe587d87a211ed80067354a31aa33eac4e6eecc58e7aecb385fa4d03090873a9b28baa5fd69810e0e7dbdf1880c1e9faba696bc17d355babca58b0635a0d2eb6bbc0b544dddb849f42b92ac6f02b5d94df207f4d68e584918ac1f85d800152f7a8a21af8382749d23a78375d30bdf760ecde8e77bce87a0f640f41778b07f8cac4ae1d0d1247ea8cd675ab94ec057666dd5325b6c8f898553e8ab5a88f0ca3ed47ed793c289e7a198c5b115f6075c1cd170a0717f31d4a14150532c601070caee6aae8f7f756ccee18b28afb327f0902800c5648a23551358bf3e661630a5ed926ca2fac1a2b872cae900e19eec41e565a52a9243e6bae3852e263ae2703689c2aa49f520ae01616e2adc70aee9bd62a78ad63545e1a268661aa9485ea0179e99ade674e0e698da1c8015b284cedf8bcc6cab49e964c34c84713537bb50e495cf0e0476664ce5542a660133495c993ab363f39bd9eb288825b764e309e02 +generate_ring_signature d9e4680311d1cb9dfb01254b08fe8cd0dcb7b3c503d6859c59dc47e25615024c b8f8617d742f1a96ab3b79e141b52c76545b39f00fd00729ed4325c2e8e839ee 35 c67863dc9547745b0a1835bd2bdd3c7f866df86486e79b5b23c60dbf330dafbd e837d13ecbb2a5a0bd620f59334475bdb332d44f44ae4220e5b30f4134416a87 c00aa3422f0a2f3a5a89edb8eb7c99d642bb33d2b5d4ae42a228ef3dcf990ade a132684aae1af1909b7d32035c7853359deec9fd5ad14e5548cc2e241223988d 39b05e702beb2ef5e6f02b67d46fe13923ba2629cf6dd4d813f92b88ce94e049 d3f8732f7598b1e7d51388a77e409698b1490ba56ff02df6c9091fc3723ace03 54929c78aa0ac34a796a230b66ae375a443a067a07954cdfdf4f34dee0a08a23 81ef7d3eb1c810cc7fdec70dae0a75ca8084651cd9a446005f7af0a37ff68c8c 06081276051edd0d6f829948a77a1ac5c8eae9e492880cc52c3a3b7e9442ea86 6b8762b54c942dfc5a701842fa7cb5ecb20ead13d5e72ed2475e00ad78f292a6 031c76372a7bb536cac58af77c11daa08cfb59f990cbc8c2eeaefc4ac1a598ee 660ca8b174d65ee995bceaa95e922dbb701ddb6c4df76e7cb46a75fc19b13eeb 81822888504f072acfca4da1c30575bf5efd17a769aac9cd0ba165003de67927 ffb833d7a1d148e158172ee880a850d83304eef3c6746427a1ef1370fe147474 23234646c19c1022929d56a62e494ec2d45e7188863f375cc2ffe681701112d0 227bed9c4db50f0e54a70edcdd4be63ea2f6e7b3563af10e01c9a9c5997fdba1 d5aff7375658c886005af953d831d6e4e2898b1490cad7d44a1b428e87e8c2fe 546e5da5f5599dfd4cf7139f92c80d70e6361249ddfcc36a64a4a6a4579ed6f3 4617768b70f157ad344866c8f0d9575c7a8029b2f693ebd75202fe238c9f334d c505334dbf2103cb24205e0ae4a660a592921c5132906cd7f634b78ebe0ced01 688761e17fbe6024f8e51c6faf00112256440cc4a2c7d37888ed4f5692e26b71 fc48bb2d96d7ade8b51a189f4ea1a1a427669fff066da86108899e2e069c37f4 e80d7e7298e64f4f273548bf0e2bdfe3cf8ff76f86f1291d28249052605dbf46 23345ed9e65ee1d2f9f0df21ec20f4fc506f837f54dd024ef770a81b12588302 b572cec572366f8fbaf5ad8ca07cd1cce301e56ada7e15fe2ceb03d6f7c615b0 ad65e69ee23a2d216d437521cbc41a946f1a0e2e350eb1c242e53d845e664a19 9d652c7154706b781272ea4ff1a5b3b84d83b2ab86389e927b6b0bbe611a207e 05bd82e4dd30ef4ae5041ba77652e173ed932b90ae8f26f0c7a1c924dbc16bbf 95f6523d233a5180cad52fa70fea8ba013a1f565f9fa5d56e6099b99018a5948 e376bffabf575bcc111d39638e0559a8d7e6055e594f5f43fefbc5add7904697 022b90003abcdffe3444bf899962aa0173489163c45d6f3a1c023fd8f089cd6f fbacd6c6ba0fe19d9d48ecb207c66a7578648a77e9c7e4475721510afbaab99e 88f5947ac68fa3b16c1c768a9bc70ebf76ccd2800e6acba428ef6d8be78b8b34 c4a86247b999e7c8460f3784603080ad902ffe2e71c986278c7a26db99c32447 1b83e2998db2167cb4f9133ec411f62fcb2246a84ec1ea4acaed25ba5fd60d52 a3480165d43b66938e0169faeb99f7d09b4c918620775b48e699b9e3e78dfe02 29 4f3672b263968374fa854ed0e6fdeb25a11a81943cff7d96308b68a8834dbe088f5e3bfe4a6b1477d9248d89eedfff7d71805bd1cdf81c18475e3a6255532806c14bfed5be4f67fcbb24fda0298dbcb1d12a3ac8e64c582dfd2a359d8c64fc0d5193a40ca88f0f760a28205950655c2cb6dd3e3445926c274b7bfb92f6923203d49b53ba5e591169998293ac19f23c1bb579ba937f5c843ba7f5124cfe12cf005d1ee3005f897c0d03091144207e3fb353c37c6855f12c96f60dc4b895480f06e84876bf871e1b0a08e6075bf0c2387e8f9210abef5e51b76e1c236cad84da0d5e63e03d36b55771ab22c0537003128bdd9ce9ff6ad2605aebc9f8952509bf0caa108acce225571004155a51f8f3ea552af7a10647d4a8b3563e4f6b95329d08fc091404508d87aba2246088504539c113fdc815e78f41b5edbe18523a944d087213bbca7ffb46514164e849bf0837b04838809d5e4bf96b06487c560f074b0ef9222f8f8e2a1889bf8b49053d23581376ac4abe4c45934b6aa93fee1d6e2e01e1db0f08cfb5cccbfa4face2b012daa3cd84903f7cb107f44938385554dfbe024e9ce2e3d9d000f53affae715e90279a78435e33127377bd7fbaa9054be6d00c7097737056e7565fca7792edbc2984740099575ea3c1f663d3ce237ac6e18e017aa614fbe5bce8c90ef9a5d58d4d5bd3084c7d4c290a8f4ddbd9add898732e02ed16945294cfbd77500ec361e148f3124213d8f8476a43b803bc377919880d0f16cc6cdee8f4512136b030c0335acb862fafacce525b977b3484d591b2c93f0396a45f559f0bcdd53a3d98bcdab020baf7917363e5ae4ece87d159e5d49aca015bb7df31a82a8ff485e3f41ed1a4dd45fd0c023b3ebe67ef5886dd73168089066f253aa431556dff8d113cbf16d8fed89d6db3baf83099b77083901bee90dc037e49e74d2a66f3e2d93db8607ccb946423f4c6b49833e814ad384bdd2ec6a9020369cd7c1717ed59c7a526e60d01cc166ad8f3b000095b79ee8f90cc4c272600d3aaafca34afcf7131975e0517ffa74f1e0ef8e51db38d0fc0aa90a1662d880ab31a000deade65f92768c6a7419f59ff25ab952f375f42ea249184075aecf50ea6ca7a1cb9e374d1d7acc7d7be78344876bbd8cb87fca86b2709145098764f0a0e823e57d69ace750ea58bf6f0db549531a655f23a85d2a05334dbbb55d6a00a1c3a1dd6472017f97436898075557af1dbe64e01e5ad22bf81be33ff0e2ec007342f3d6f586a14bd0cac0ed841098bd9104d99519b290dab5a8d90d3174ace0fd2e870cf2b903b73d614d4195e7f8dccf4308d3f4268349c13b6d02093429802665e2111af809831d8acdfd81aae3fe4ff196f33a4d0cbc93eb32335979cb40efa5f14a8120c102c979e98fd02d12d9a064a93d0c9bde6bc09d4ef2a22258c0b93c772e3fcbca3545d533c88d58f83b1e015b00526ef92b76f24d3061c4c7c0a106f248383955392e8f45fbee1503ea8e6a7c0e40dd1f451fe700ca88501a007280f337d5eab54185287ad30b921c454dd20b351d606ce7688cece3b3016a30db7c2a3b7e28aadca9cb50864fdabb82a7b4c14f072cf0ebbc90ae94b9299db0d490e612214bddd78c57459d55be8f40dc815c6a23f12f9e3172e9482d2f07209eeebd59d5d8c2aa1ebd5455541f26246b5a85212bcf309c51d8cc571811e290ac2def5cca70ce7be0259f2d1e3cfe5657df05e776c24910ccf04b589cc6f1105f06f200ce863e672703e6040e1aa61143ebf30b4f8d06bb951b8037241c21309aa6e5f7e5a505b817c965fb5230a8cc8db94bae06c282225eae54b8fa3bf6402a4ed950351406a7b1fd6ff2ea14de9bdb5530deb1bbf88bf0a72ec0e31ab3d09c937c367f3e97d7f20ef150c6be10f03acb0c89e63d46d64f73c31508eff5e059e29380453d02c27c28c51949ee208a8433ab61bd1e776996835511f0937e4014fbf2354935e1af783c20c2789d01e480b3255dc9342bd76065a28e904e0780caff08eff84bc92e0cfdcd324870a227c9008fd7396274b33679aaced513a670e5b6cbfb0b480eda0d3a1001e0936534ee564e63160c413524280a9345b66d90a9b542d786457984d97bcfb82f0e7ace032598b9ceb2eed153ba1a599c7b452014c509d49f481ec1774a896890723100352c78275c4b4a053a50ee0440e93770156065bbc1c2d9e14ef6a021816db11bf6b60cb95dcb0d11891815cccdf034e012a8c0e4425aa83c2206ec7d7792522bd394d176ddd859316f5f5e9ba51f6cb052be14312239919f1abf04fd8d993330f59b6bc202379695f0950aece7cb3110b1f6e540fc240b9011d9ffefc208018993d5df994986c3374e2bfbeecca834d0ff3f7f2013c9a70b5a609c9c0960ca43c42e8c4327748d33aae982850da4f690fe788cc121d73eb71ea878d2f5f746174c46b49a486ed1f0e02ec5c7c17f9bb0f51c31a90e16362e4327211e8f33804fa36803f298d8196c0e708d699ceab7304bf62b94dd15a657949fe1f67d10ef6d2fd710aee911ef0570aa599317d45f40660bb1a8cfa5b25f51a93d746660d8095070f1310ce72ac7ca0c0d282991b080acc94adfbb8c7658cf13a75d086d7de4c27e5427baf82f92c864317857e9b5802310dd0eb170a744c10397e10dc1dd9e105575b78b5e4b729db87cea1b221410d42d06ae16bc94209d0d3e317b4f06330bf7a5050dd79b631c3ef5404b9608d09a95ba6e87b631150a661b63a82bb514f201c0b0fcd670ced919023c7c15fb2062d2deaf0ff3a6c5c003bdd29c0322497322c1e126f3693dd9b5348f032d4200b68d984be379698bdbac416c8c3c91cb7cdf5d816320888ac4f6668b4faaee30ff5689195a227306035e6291af75514012a3b21667128821f6a28e26a7528800c1d83be7e41d78340bb4fe140b5df70f37d62b551185965fb8afde3d986a1dd0374f942d95b77eb9b56e3af94e93996951ccc64ec67c27388ef6c035d055c1202741f59f8b8e0b8703992e086a3bba8adaa3f916ce0ad5e6750bdcff2518dc807373c468728183e05f46bcdea1470d8d798ae7cbc2955671bb8cede79fd764209736717295f7f10ce1e7db42e1928d8f0f4711537b19702078d2579f9ff316907 +generate_ring_signature baf768d9e42fae2c59729a1d2b8c1c90345ba669b954316a5674d1de6220a76e 4d7d7d439f8fea0f37accc6bb4b1d9b3d2c6a8f1de4c4080d058aa9aaddbdbb0 1 782b450055215a1e6bc07b6003187d9560cee16e6f5262521df26ece593c89b4 388fafcce2f0aa9c54a2cdeabc47075bc4c6dcfafcf8bac5a99cda75c3e61b01 0 e7d04a6d4629a8a42dc237f45a0a0cba213ae340fefe70e4730812ae012fd40c50690523910dbddc97aca9daf6f2408f799d68c27e46c13bb8b126f1b82be70b +generate_ring_signature 01916e70c1a168a97075a7d873849f7ac03e65094ee16f44ae66ad4a3e0a9a61 19388d755023e1604e7a357953c2692ebf537afcd3c5b175b18b4480c1770ed4 53 242cfc91401e49fbe717545536ff1288d9929ecb3fc5fcf431ed7e457bbc4b39 5720bfc3064c22d0bcb46686feb470b9483cdec909c4b70c63a8144c603de1c5 3144d1064b96500b9fa8ee6ac4f95292f6d92e7c6ca34b5974511c380807739b 90aa403c95a6a5ea6866ce56913201a0aa37d5c09e3e0c0150b8f23e6a8f7074 29a6167a49da7587f4a4a05e534e194f7fb0858c8b187e681104939611678220 47a0530a95b6e548ed923ddaa08d8cec94bb9afd43a716602c73f7a563485426 d7792a71e94ddef9dfc4300d460fa547d0318c04a0f43513e98fec2389fd03a1 84ef4f5e3c509f8110ea7239b531431da9420ef98bef880020ef1565915a9a10 1a32deba9900d23de6dca4cfab511391e4bd2ecc9d153f2cec3a4ce4fa4c54df 371d5595b2a49bf72d7824711067e3248f2429e086383d77de2035a5da111c25 68bf85ebcae2c713975dcafc9200acca99d4d740f05bca766d7eb82391767f66 837dad3d55f3fafc1717b116c0f7e7121ce70b4cfb542b5452a08f92dc59ad55 d66ab88fe75167e16bc30dc727e2862128b938292e5bc60a5eb03390edf0b133 32f67f886be12e0bd1c98eeeb1690acae74e7c6e321ad14bf6dfe7bd843176a6 572c46495fc92467f1bf1142893e0edf08ae501f806402ed1b8ba58cc767f8ec b845884ca93520147dbfcc37a28b5d899faed2f821235f2b055ff34d2ce0ca2e 00617cc79d54d01e4bf16be365e673c3cc6bec51a5c337fb736a885ba0acd573 6a8278978430c6e99cb857f4e4512e8278aef7fe70e4b2cbdb0aea6d5baaf14f b0177887294e8717c7c4300e3daf4e5d35721a54fb85aefb99954ad1cf15228e 5d0a7d23e13f32762fe5e66ab9be9aa73336f9c385772269046132a11795caad b229f7653c5630d1b2ac5605a5b2419d7c8ad9879d6d296eddd14a1ed788eda4 97e01654bef78ba3a81c4bcb36b07c6dfd723a867bb2d1e1009574ca5a4a4444 05a16ea1786cec3520ded847a0addf9ac3dfe96c3315e785927d75834f73b5a5 995498a9ea1ba686429c39efe8fc92cc2bfe7a4a3da23891b302d56b18c61075 e4f2da569effe699c788939b58b7271eb5b63347382d7b9fa650e0c16caf7dbd 62b2acfa240fdd2888dcd3ae4b3b60d9aacf48d627ad4909f0ee3614d2a1d8bd 6d52a488709aa858e528c36acdd35cac6cc7a1e0d612ea4dc68a4393ef095a56 da6aceaf9e86d64910f2f90e7e7d30a3459260c9dfbcf427b0704f5d848ddac3 84c11c1ddf0fbe484e80c375f0bbe985133c72d3be646e4a62691b3ddf02c93b 132529a4a7796849c67315835700cacd444e85a11b9e71afb5ca41fa81dc8373 69d1d9c94d25a965f96c89bdfe92d5bd5a10a145312a46d990d8747d9b54ce72 a77f3f1c05b8b70a0203e373031adf0596eed28b5a434cdf0abaa7c2221c5a93 a611e3a402a42a33cd8f832dd46c794c6adc9df2bc32747686ac7614a72d0c2a e8f60d367002f5fda4a024992dba1b161fe1802e680977b2e2f55a9c3deb27d4 e887fde0d2c22351ec22b8aa8889a80c0917613ab397c978b2871af576c76fbf 9a90e74a7e602b11e1085681ef5e08a59827b7976ecd8e14f5c7a81312a9b84f e35b60b7564725d9a7dd34a6ff0dfb5d488e3cebd79f8260a7057b2b2089c233 71a3cdd1e3f0840ce0cbbc9049efc87f376938c4a6fefda0e4364684176e6bac 6dabcb53034fa276be2e73851d9a39aadd74b3ac7ecb0eec92b43608cbf4b02d 13477a1e0cfb62bdeb1a30889489446cdb3e37bed0e868906ee91da6ee4de0aa 1bdb6348c889278979c6064c256adb5e3bc76b61e332b2981a49fbd79a14dfe0 e3c6ad547fcb3afd9dbce9593931897d8f736b5044f543475757c7775a58168d 379dfebd4d07db29982a708e4a03ef03a583e869e704f36dd62da8fc41867cb2 4249977ebd5483286439256595142ce68bf2a88d40fa2a0e204bb8918b2ab1f3 08710fec6ed8004733e622281bf186975b7c7ec6e2a85d9cd7fd146f9386908c 0ea6d13d9634d24f6aa872f341b2bfaba220045e6dfd752f0746cc1b5e9732b5 201ac295b1b5a10a4317748a29f94ea0e5b6b069a8759eccf5bff9067bc1c829 623ea94a4d6de8a3db5e0c19026fa2623559fa878030c98495111ee9b2951247 f1382fca2d626d971af139e3873405119843d0b0237b48d08287d893e7f003b9 5e536ecd5570c8ae9165916d9f14688dbc8c82130e1c589e23ac5ecfa8a39435 f18095254b9f272c5e5f10322cbee3cb62cb40e75a900bfb69b1557c254060e5 d1c0c862d42a95b7008215dcd1cf6c4516df9830ea11c217042d0f3df720814e 4eed5da8eda2ca0a8436e337346fc54f3dd38b90748bd4023580e7fbfe271dba 8b58a9e402a6a320edb50825a07279ffdb434d5bba8c2b93bdf568144e098a01 0 609aa6ed537a983497297b9dae67a3d740f9fe31b632506e2e4a4c051dc5500a8d5100f9c8de1b796dca38fea0d48ee5d7c24996969d2875d0757f558240b70b98d8cea11b261dea4996a426bf66eb63820706bae1ad65e864dd117156e71202c4a51bccaa7c50d4651b2a6a18ce1f73aa5d443f531066f494e950f8771e90071c6c2552edb05d797ec2e275cb3653d0089eadfff5a1822c94c39b331bba7f0bf5b6b92904c9fc6ba8029bd61b18c540aa2f6fda19d9dba901a11fa43033730aa8f9976f1b307299007eca72e21a0b9f1cdc559081321bd18acfac996ce0150795611f1ea27d57db1eb548b9b83e9875dd38f1481d6c14cc19ac880d7d2508059d2889b0c4a4932be1a6aadf7999b300e8fb5aac17354b57ba74fc5b39f5860ab5ea394af859c4008674311a478ad64ccdd6b9f240ac896ba5e1431c45c2e905de4e9f4fc6b6b22804a817ae2f65c71f2739236b0f452ce622bb1db320b2690e6513f1c3cf8382d264b01a9fe8175b9f22f4752fd7934d37c8d203ef3af7fa0415759656ec966e46e8661f6d80a05205ea095fba0a5e1183ff625e116d9f4c0a3e3112bd160f7fb0a42df58c96664a0fde979e1c1a6cba1643f1f48895d4a607a92d715b31b68bd5ce1c2a8bee3f34b6dbcebc56bac2e480f6caa66b816348096602e3c9f49f2a43a526c7c252513c6b6289731c6fe2e9f29f3f428d608db0040d182278959804dab293cb5d201d49fd032557a54188c555b70dae8a6b36ea08700155ec01b8407a6ec7f40ee1add38ba72dba2f971c95a06c257df268545d0ee7420822d8f62bafc20d3c41852a22784408caf29d87b1e8743a43d146fd450143435f388d53d027bec2f3ff634aea41b98d3fd0a91fdc5843f5aec3ea4fc607986bf868c4839d767efdcb4cd47fcb9854a0c8a464aae9a569c54efd413e8c08de1136a11a22e37c9638f8bc144b5e477cf7aa521c4bd2c3c4200cea51796e03c54c9f30baa7b1530a5d0f19b96f9f731cef39469ca82e8fb943f7b72289db057bd851012bd066f97037ec08c16a5e5bdd3ed6d58e06d6cf62d14e304253f0067df2bcbfe93d5768a80b5647341a7e377be710eb154a5d7cf1f836d3e2948401c38df5776f04bac6bf7ef95c4ea4afaa066602fc2bfd2ec7966f215fa576590f2b12ed60a54017e42d4df0874c7ca833e4c74c6b104439ec14f6c0889f713f0676fed40e35319ff57b391d5cb1c707307b614eaeff8141e44232bccd4ea7140853185f2384ad78fc0ecd4262137d4a4520639e3d8475facbb0e8540f7aff7705fea17f5dc23a132d96d949dec90b9094296826ec34b4e915baaf3b6610df5a0d8fd3334b810f00e1e06e63618adb3bfd8a9e7e1478d1c3b1b2e4d5388248bc0ff2bc6b13a057effc9ac818f3e0b4bcc46f9bd36f3a913e28b76a31aeb33fc50839420d269f799394d86efa1006a61581f464b099e1cb3810993cd789adffbd09087504bca594d90ebaf0d18648de5f80cda5d8a64018cd8919b5e021a3d5e005c00da6b225b8315e1b6961f0b2fab2aced687df2a3c0dd14463579703fefa90cd45313ae0ec87915ee9a2fd71e47c155c0efd00efb49c88e1b5a193946bd8c0d086046114c15739c423919beb362923e4386139aeb1f889146a79fee23de0f01d4176198988ced521653793347cf193de33b42a904aef79e6bbd02bb485ab30eb4da829d4139bc32636d2e39a865f058ad516777a96171d567d07cc245041501f4f82bff8674c47f5b21c9fbb9a4974ff1516c005c02b94a4a27a50536926609bc0ae6633d3a7ff01ddb28b0a2b687617801b9d3ff42d709f7bc009d9b4f9e06f0fa7f66ed13a8f49e4d97dc6fcc8b95c7d7a88f6af9741d7c6c3542ecb84a090e4bd9f5496c11b669bb3be377223909d374af355cc579b9a129b5108477bd02c8369ac5f22a9fc44899890e55f8b59bb42a98c3ab61ab260d5da7f3eb98ef0f8cf10688274b7d6f81a89679ff0e6556d8aeda589ecee73b3e5a62e494a92f0af36e805eed96c63b69c6c1e139a11b98c330711075bc90d55ad592fa8fb6eb0c96c2277fc1cb2390cc3b2cf48b9251af71a9c81465517cc650b1b9c69fc2e60d6c20c17da0cff1ae874af430a43ec8f697af597ff731221dcaf7fceb974dc702900f58393d6101ece747af3a341d0c3ca4c9b75193f037623b86d231acfa43083eb7f24ee11db68345ad5c9ed7f5365038a1d5df9c304ffe9f5163a296492601c75ae6284c268dc1ca4f858ad7617fda4d8d509df0671fd2ede7faee5f68510767af721bf06ab3ae1c4bcc512bb8b3cdbb721a86295620c8ee21d096a5734e04862c1a054a708241aea5ed4bdf4f7a45094692397b5971db4566a26dbe42a10bdedf116f64ff38cf29e5d773955c87374bcec4e45b3fa12ee8dca9a201415b00b562e112cffb04c20e10da981c2ed18c33b1fb111b64f077d1d8db8b865e5e06c391f60a44c16186fd1473d2e7d684e0138859e927076802777107fa5a345b02bfd7cc4c97bc236a7db2931c3ad4581ac7d7e17f5cdcefd78956002354696a008c17c6a4f316dbf6b9a42ee397b434458097ab9043507e1941359c9c59d96306c53449864e6d6e58caed20f3f86cbbe254952b2bd1c697b1a117851e5efea80535bcc44db680809aee50f100b75fee39c0d6806c5c2562c605f125163618b70f4068859c266336476aa69d13bbf13e352f6ec58751f59226550c3e229eec9200df314c83e93d70ab8675ce0210f7d05b85da4574d55d293c47ce0e4b8c07a60020d00fd6ed703803d28ba3d86392d615449dd5db7a6ef8ba5fd39ff24cc453036306583174a75bf83a08fb6c1a3637e4c26551a9cdd28ed61226c6c8555de103373e3ce46c71a4a3fc6fb0aa806970844c7616b03c88f6306a4abd48d7f3d9062d8bb2228e968c3ef155f78ae43ba48350b957efd15e117ca1fa022b4481890eb66afaa914ed9da87b14ea4e4188decf45e1af8f78dc082d72f2b3fb3ba6410105cb789f8fd317b65396ba8b34d742d2581917b6de1bfba41c33d35a947ba008acfc83bde74e9a37cbda481a8c3930b0c6d369540bfbdc72c244e09b9996d00cb6886e6c07266c24cdea522b68fdde31f458e7d2990b848c448b69dec0ce450c339752b261c678415850f0f5e19b95aa2c126cc65db8feaa78cdbe80c31ced0418f436fd805bbc202f28e11ed0538df15698dbd98d2d44ec3e8001819c398a0d6c239b9c3b46497659fe5ecf96f5b33ca09f709c5f61b4b8800d3ee1e1c7980cdebb148b0b3808362e8599ba1fdd2666735ab8a9fcd5be987cbdbb5b6f863e0f82fecb7435a67c29a6e5a20310fdc85fec8989f6220575ef6e2834d58989da07b144bf7199498823325701ef1dcb166b1875b0dbf1f92f8d4b108696f71a660d84a1b6315269f8d6bd0eb2c9a161305a3f1f4269cfb5e85d4a332e6b99e7c4081bd128376494bc1cf308d5835f8251f72acd55228761997fa2ab38fc0e02e305d20a8d2181f121d4af43547f5fd324c147bbce81a371d643e93c66dd98196406ed9b7f9711d057f9f2d967e2d96c04abaf0ffeb56769a66763955b9933ff7e002191a2ad9851a6cd5cb7ebf52c166f271552f283344f2c70a5ff7403f72d1504fe6d8a6d41d16027c15d2661654ce416e5ac0936a4a95a011b1221a5b38bb6044733c983ea0d7c11c488dfb577ef1379a625f594461b9ba24783f062d96f7e0b084af26ad7f8d28b87c85bdb7ca6c1eb35dae7510f6f5d0ab9680eaf2c1f0804e63abbd55b87acdc4aaa1cc70a08c214a37b95098414298672853b8859cc24017fd21045fc2412fd78f1b69af0f08bb1e4be003dbd785ceeb843f651ddd0a906b17d52f06a635cf6d692f4b331fa850687d0e541baa7ee2fbc7669becfd3d3027a218a18b6c0814f04143a64d9a4d37391a006c79eb148b3f4d12e70876b010827fb7d1dcdc006dd3d55876f3fce3a90f62b7bc5c9ccd84aa3d28a94dce3350b76f0cb5a5d4048198a506dcdc1ed301609c04a011b4b7b89535a72ba207eed01594380d224173317d071e9fd4d3d6f72cd2e5918624bb4a9b10c60f6c30b9d0d7d5944eb974216c48bbdaf4a7f69ef92b20e03ad824ea78f41bd7ceeae73c00373139435c7841cb737304ec5add479f140fc06e28f67cf92de2044f4c23c0400c39ad413285f51e76c087ba5d927a300d44a7e0317e35be94d41cb89f66af40c6994d0df763b00428f7e94af71aa00c71227853f1a1f5878d02c85108bcf8803ff9e8c036495bb118eb35979fded8c3f8dc8c331278d0f3942d1752a2b79ac0aeb3d994f8c4204fa96787571924379697db8ebe0fb1688b15c6853f85c20110dd972052874cafd60b866bd163e83a975fa19f1eaf5daaa1c84de7acb867a880cc64aac0160eca5641070631d5f85e0845430f1fe3349fdb95e27dfd1e095330fd8e531d764605587b36e7481708978bec8d6a0f5efc188e729f6475ef9170c05b2510dde7fc1dca90eea757593daa442cc156a7bc09ffc40837e9395ffcc640350057acce214e5c3ecfc7d5834a9ef1029b32d84ac1f93784c279cc72bbd8204fd9b5af30c739d5d0ef8b5121394ea1a9fac2d1972366291a47213173e26d2010ed400128c775e733af9ab3fcf090707eed20b3b477fa3753a3455444333c805ac46e2c235c79fa20c71beb420c3ed022561076d8fa8c5a96e480d09562951021009389888f42b29f8dced9208ea91f9c5444ae02a261efe1c907e55ad90a209 +generate_ring_signature 0fdd09cc2513c1d346346db21cfda3bf3da4d65bf66a91dd87c83fda9b9d407b f96cf77c4abc31236ec66ab0e3c56d25860cb2027897a5fae40622a7908ab147 16 a5f687daffa9505005c73221669b1a728930db51d4d40b78ae6a821e9b319440 07a6ddc48dfad0383024bd700f46f56c88129345439bda5e7bb7f356ce11c8f5 585bb3d7f53e474d75cead5d1ebd12c68757bfe231091adacb877b09e34cf3d3 bd06689f501f10bb2f4d5e3cdda3954a6d55ad18c3ad485fa98a4e83b163fa15 0908f88df8ad262a40203ba72994f9bc919d79acf8f3847c5ae72205f8ec29e7 6ada309079d68bab1123ec762dd8ac04166d8ea824acd9408f502731dda08a8d 80d9f89d04bdf2c205d6955cd4f285790a4282a6d75bb555b1342138b42c9364 77e56ccbd4d1ad228e32d2be7837e3871495df21e94dd1328428251d7bc3729e 6387f7309b7005b5073e718b24b2b7fef3fd84749fa98e91dd1f83912487cf56 6b7e693dd187ad5c38f73079c040366abbf2990dd30eee6f685f0360ba22eeaa 819f739bedd0589653cba095292e436678a1fd38767d4a6e9449f3d4246f12c8 5b0d6f09b5ad89d7cd8ae24e8ba3c2c4440cb8858c2483ff1363037491953fd5 e3a579a3dcaf3ba92fb5f57cc4469d1ac00cf048f0f3950819875b1805e43a46 0b650b9de62dd063d88549fb717d3ebb8fc15b3bf7338457c53b7cb4c2b1350e e8ce03b5b89b392e9d2de32cd39416b7c189f5ba81954c37bf64586dbe707ea6 feb0d23239826ed5813f0d76ebc0934a4ad72186f9e43a7895c54e5e03c55b32 e01f9713a2feb7d4f9a06fc9b89f1f438d990269f8650286146ccb2df251e309 13 ecb57980bdbb5ef293359718719bba53ece5c16e9e91037d5500c599c5836c08473940755b3f465558847f606c162022f061e9daeb0742876661b5bd730285040676f5bcff5da1c3c13049dc3faec02267424e7bbac0fa645b5c5dc6b06d1f0034a89cae995d08ac91aa59b7e81e476f6b250b1c14e6c42f4603e50c8dcb5e08d6d054b4b5c93ffa94c1889a20bdfa7893c4550c1547ca7ad21067a06d1ed9081d2a97e21c46e453719e4f4ab4c55d3844f4c50d1a63bfa4783b9bd1f6a20c0ded2d41ee2218e45e74daaeaa11838e518cdf4c7f95e94727a15a6e5ec21a3108ae5a098b3988da0fd6bd4601fb84434d5d110eaffbf5fdb3fff485f8936a6805ef5b397667e8877c878ffe2991390de1b23e075b39d85eefa1d6948390b033064c6f966d30df6e2270794a671b3185b23180e01b48288c12c1cb56aec222a7032c1840c0b0a8a93702996416103907888902b9c41f470b70cd309ac8802e5a0357968ccf1f821e109543af1d90e9675d07feb78a5c39d7f1007f5c8c1d9fa9090ed07cf5eacb033f2375fc6364c3bc30744ca66e20386109b28842ad0994170e0423fb67e47ec48e0407f3627ffecea725494de2dfd8e2e061240f4578310a042c39d7b3a61b969a3645f2fadb91f06c2da7b717e74ae8d48e8355360aed24009413e4917e37f291c842ae6158b62b06c6582fb37bbdf6537bf40a58b3ea5d0fa81f0be7c04e0d8d7a9813caa3f64e08d72c3e8093fbd4edc536297b5388c10729346d4ca87bcc2eef0710ce23a884ddde40efd7ecee8efa34ad191184eb6c01d95e852d866e4c531db1a7a63c8291353fec37f838037a9bf4c4b21e97b87e088698f4352edde777b66f41c7bb1cb0711704e7f6a85e173fac67bf4c19c73b09e8f2369999f12b625e3c5f85d62c9e2336216b693606879b1cda7e015abf470c8d754c8f129ed8919bfa9ce0e9c4f7baa02666a352be858a0a789021ea1aef0f04d78e33db565b093ab0bd5ec9e73ed57d9274f851af5d90605a2727986be006821a1c54b0918fc1256fec86bb33bb9ee767cbe01bccf04868625908bd71ae0cb18711fac846b4c9e3740513373e910025facbe7c9633b09ec51d5e12c9aa30da1189c040c5b3d0a0b91befd7e1fae61e9157cb9487e2d99251922f65443a80a290ffeea5e13c10609301c7525d9f2cbc4cbee4a17d8d98ce68863411ae0ce0b2ace143a3ed6af756e6dff9ec2a365b63cd61b6f04bc471d128507befa12f903703a1f34594a0de03e2b18b6d61c449d742b4ec98228ec23638da3788e2be401ce7501b18a656fc5192a6dd2eb05345a89d2fa0872e470539840f2f10d9c5d02ee60b0fcfcbb8b4459d73a094ecf2407e19deb71f58325b13d037dd3dc74430141b8f2ecc3cdeedcacb087c659d186f36de2e9c643d8f8283daf4cd95631170b +generate_ring_signature e3495fc2ad438c8128a655249d4d6b69b6c17ee1dbf96ed1acea6380d07fb724 2adc1d2a97ca897fe0342c2165b8ac198eceb0374a7320da794d1bd38a45c962 5 dada0d56d195390308e9eb9304b877ed930a1c15de8c0a28ab04066cb0cb379b 85e9284587832bd7e59fbd7d3fb9b37b4b63e801522f187fd5bf45cc4f5454b9 ca11ed3ebfa24d358807b3ccdcf4c623af574b8c07c825b4b4b6f68cfbe9bef9 f77887b1191a51859ab5bfba228a360ce889059a60aaa740a5de8e9d0a7a1e57 c445962d9a4260693d6307b3305fcfd65807ffaeaab251349e02ec4dcb4f2d02 5d0db6d9854ffe9d26d8df64f9a0f5ac01aaa30ff2819e6377724f76d5c8d802 2 7325ae6c41730b79e34d127212f2834a6c62b1dfc56fdc676fca7f6b1b42630d92dd1e5eeaec7c20840ff55d6de5bb7cd1cf0b6f1b941011aec0fe842902d504149136b3398da5144fe1cc8c71e9cb0d6794bac16e3abcea9792915fc21a060267a8c0b46b7c60e5d9ffe12ca4fc69625488c723e96ea2faf0af2b430fc828055bb77a895c15e14f6cdab67db820c58ff5c4f34ac9891a1c4b98c37f343580047c5e2157ec077bf2ecf987f775e0853a68d803902b664aa6e98efc3421e7710e08eb5fef74e1aa83c93d60c289dfa174cac28778dc0f5e50ca8193c18f33f5049fd2d5982a2c5d3007625159d873a71ca0f1012d3e4200a6ae396bb12c41f00d4b95d8cbc1e4b0d887c14084b5d795d528211efd24d8b0909669a32083d6a7004f572503d0dafe8190c4f5960e151fa549f5363f6fdccc6939dbd597cf5db60e +generate_ring_signature 923a11626de7b0b613c07f95a600a1ae1b62ef2b0bff338120f4aca08a518f0f f12c207e70b2dd9bbfc6988a0ebe5f07f915b5bf2b1f1a8ba23c5db2475e8d26 4 eab9a7d80ad2d7e9060076bda332b1e2b31860426710e7071e5f34afeccd51e8 8d48bb5e57d8c7b0950384093d6187ccd03b0c535a0edd6da35a6f015f928cc6 079e64c24eee1da696543bfb274ead55ee33e3633b9601d9e3512371194dd4f9 6348488a0884315336961ace563bf106bef9f2c5e745e5204531a3e00695d5cf 2d4e461db0a3b63a7c05d4699b1c790f86351dc1fc96d1f8365ebace89d9db0e 3 c50d0ebe8a92436ef20456bfbd2b91f21df5f061757e6297cbc1cbeaced3cb0753dda12de97ee3bcda38122ac08ef4465e52313835f4e5ac60a2d5c84a21140d48bd38427129f7319fd2d37d8fb0d0ad65d319fb11e36d13b8c3b1fe79156f0cfea6d38a94ec9ed6d301584220ab9fb1210ee635af52b726618c3f1b0ff42b08045bd637569c4e3d8a32c67cce796175805d472778cd43a278993c94f7af7f0435bc0a9ca41bb6cb123e7d652d2a70d3e8dca0db564247ea617d29c25810d20c395bcc885554b26c751a77311310f487f2e5030989d02042985285a93a18550dd203770e98cd3a0e5874b27129e6f880aada3a1359ff8c95b62287e8e0ee0f0c +generate_ring_signature f7eeba3fcbdc35c01d69774f6d1e4ca270680e87e1af40b2773dac9e9b0a56a0 6f1f04eabe8e9213c7c5d4c4a4df1b5564e63b054d3e09ad81eb6c4d930ef44f 7 ecbc3d0e415e2c5360b40747cf604fcd6e4751e125ce2ed8f7922a317bebe282 60887705484f05a205b5e6c98ec1be1df8cdd4115f0f3edf9b545370d81b2283 4310521f1828d60b124567720d381d203d81d07b8c66434011e29bda436bf7d9 4280fb5b19e595916a02527cb77814868af83620174dcb21bc8da271ef680004 f2f8b898e39ad301c969cc191c11b162995a5e1f813dd63aae200a718c7cdb86 867556a39a4dbde3c7fabd5180eb0e7ce275ea211db2fe90e13c4c040c0cb28d f6c1111d0bdc89c508faad4ef2015ea98e1f5416324def724136e030ae647603 f01ce7ed8d9dca5629b5666899c8aff6ce0b58ad2fe04b77ae75c2ed6b9de30f 4 7a49dc7b8bf73358eb0bcb5024db650e740f79bb3b62de674715c29ba2887a0a1e341701d54bdb791d96252bbf7f2125ae29d52549cbd7800e72ff25b0014e0f1bec3dac65f1d1324aa604ad53260f2ee3a8d6ede607394e5df1a3a80b8cdc075f39840ef73502adb13e607f61945dab75a0274cd07d22d41e3b534294d073027b9e3829affd4b8e0a7814a64ac8ed6957dd377aeded35987c412046e6b22601344648c9f776501cca0f7055af58c9b553862731e05d2d6951b1e10acafde109be17aeac17d7b7be8fa4620b93a633df9b0b087fc89a927d280e39bfeed79f0ba6c6b08a540fd5926cccff9184cb0e39258bc531c06011cead0479d19a9bf107785030699ce1b31915d50d27716eae11c90f47a03260d371678daf8e53a6080a624d76522da2495401b211afce50b17ad336c2b89b610c5f000da1d328957502da737613ee8e1dbccc0d977a1512b9177fb04553c1891223fc352c91951b140e9bfb3b1847aa182ef05adbebb105a769160e2bd63cb2335d4db6947876d8b50f0e68fde6d3c687e114bf72cebbb8ce98c9def1c85bcc579294a119410c3c58018a55ceb9f0e00dd9d5a9106057529fdacf61750413e3f538e82fa367f19e5b04 +generate_ring_signature c4dee30ab0373ada5dee59e1362d805d6a2eb83f1a878dc9e82e1e9dda566d60 66250b52e8ea438a69f00cec532367c889ac73727ec2b41f696bfcec68b5788c 169 f2f2b60d7675c181f68eabb0946ecb76d12c2ae176c8d7f2b2f5cd7e5390b732 743abfcb7c102fcde3281f0989bdf8aa23a38aa28370e9caca172f5befcd9fb1 6925c7c3b98df3427d346866a2063fe956404bf6dd718ad32176af5f55fa4932 e13d9ea26cfc623476efdc90ae5e746d5c01ece1c662c1b93e9cf19a70830b7d 05d454706821b18b611d100393ec3307138bc84b0c0958cea0bc4eb1e9341236 cce2645b7ce500aa4fb89088e6ba8b90d488e7adc3403850b6fd762500c3317d df18cc2a78fbe16f092a779a7f0503cd26e45a8258379e4535d97a01e4f3af7e a9d9437498986268c6e2594b4932f8ac8798254bbbb9f17e9fc5be12aa35beda 9a8f7b61754085f10a5ddb5a7bc293ad28e8365f8b51e94a18e974b7b536a883 3295ada3db3830f1a2887c4f1403eee920a836108a42a748414da663e7fe9108 80545b9db676fd424f717135f3978b39415e045225c2aac5f340af7169e68b06 3d9d86d009b0be89558db01f5ab33c2bfa32242167d6a7afe06ac195f6c500a0 37662037db5136dbe094d1eac43c18c9239969f9d3e0039df9679cfdb4f3d18a 3bea446ef619e281d5fc24c358629581d78385b0556d72fb244c66d97e2c6483 a28b168bb36453c5fc8ac19c55ed117077e0f450eab8972815682e1fbad7327f 6e28882488bb1a06b52d716767a61584235e7351b839b84fb2d340230dbb75c7 52a7690a81457f57c9e79fcea48a019f3bd405bb6b66e61a6f63d9151d719a7f 9a80b4b0f25383a2c2f70d94ae5a4da7b15857d3242d05669e84f02d66e92edc a801c63fcd5ae02c607cc766f3ed531cf1e94316ef2d2989ebc611b200a300a8 34e4278c824bf5d404204de86aaa1a806ce70fd062eac373faf51353e9fa6202 4b6ff12fb5ee85425a5ef473502e22bb78aaadcf63ad4d93ed327bfa691d5a40 27dfd189f1ba9b2fc2b0d48bc6086b1a4f242130ceeead85acefa3a92ef86ce1 37b68e0abc377dc3c967591e5615c78e49ebb08f1b77952b5bbc8d9696f2aa7f 6bdc04cd1004485d5c703f63811fda02ed60d3e90e0dcbbc4e870027807f7db5 e106081b39c6bb35c5f5607e57b1eeedfba6a6060f21ed360faa2a8ca5af8d1d fbfb46345f0bd2e1711359b292ebf8bd6a7e39e531f986591954979fb09fbb2c 37b5b8821be52009c9e93c3ff1010474644f5b2872a8736650134e21b387ab31 c83fe1124f489b72f050473d52932fd5d68f2dfaa2dcc21136599cb918b135b1 cecc1ee7fdfb34c04581d11c61b300decf57a42e280edbe70320993be72af60a f2b51106e9a9d3d144df1c152a80a090331df2efc91e310403f0a05b9eee87be e17858155d4549918dea501437c1148710a310d0f90620fea070e83e02e53d12 46b090083bd9288b6acc4812e363601ddc83c03ea67b482eb55d2a790b77e4f8 bbfbcc1daff1f526118950dd3e2709c845523e19026123c5369a13c4d59939cf e075c3c1ea09cc5b80799103ec90cb60576c33c3b93a73c24b0d1ceb45d295f6 af65af5d55b0d0d15fcba45eaaf58ab174f985bd4d41c2d20b7a4526247c9714 80bb8136d6e99bd8016b594c39e7b137326ddb543eac6fedd100dff6d6b4b5b1 5339bc37bfce89a3dde58c7fa4db847a4158508bb50bafcacf80255382ac7671 c81b42947cc5f69eaa5c203eae4df3b3d020dd3a0c5018f8a9f5e34348624d0b 6d510f05dbe39d83d6640cac35d0a60068331cad220b17c520f23fa4552fcc87 d25684244293b811b3551f6dab2a5050fe9dc2bce52c15740a2467009222ad5b 9d87b136332a86b9c5bedc40467ccfb345ca8baeea24b84ca338928ec5eda14c f2e3f7adad9febf75773a3c857d62d3263120100e2022ae9b77c58b3c7b894c4 d7d5c94f7e248cbf878428a2b144af620e4681da88e766ae43b1d6dea2169967 30efeb1e0e068b2c359373c7add7676425c35d7c7fbf3bdb90b82dd824008ddf a761d5d202b96868e878551709c2b5ab877d7fe56995504bc9052ae0f0200bcf 55aa7d97269864c26740e4be038efc3b0c5befd21ee78be97639826f5f0f28ba beaa56cb576bc41b36f5751c3c7251a70398e4db0a3c09ca35791e8981ea1f91 83d7a2343fbe5165a375540f07a124513fcdc269f32c397a138f8af02a44ded7 26efe640c840ea1a9322378e8e90f84a17627e792190d3753a4d87bd730b18b4 f1a97f521feecc1049496d25dd3648443c601de11949d575b0c550d8baba51c0 d3a56987efaf33ad73e78557598c79892562eccd7a36207e1b4a8f2580b77bcb 5d74020c3d7c93bbb99d658b8fcf1cb18497208ad1c44bcfbd8a8b956646051c 0a0c3a31f1d7cdacaebb20388dab4a8ee07765177f8cbb039719985a5a942e74 e8fa5aa4fc7d0821c6c013cbafeea5d28eb6a24c9db8779e0be56f87383a3acf 78c803ef1f3df340b6cdd5855289dfde75746ae4fb6e6f79b902da3e6edcb510 5bde60d09e8518e581b09e441d9e9ee10fe68d206984d8da451be2969dc6351b 9f0d4380fe0bfb2639c08219c7c83b8d0dd4f8a37b9e7f956315cbcc91df1fb3 ee57237455930f2d6a13257fdbc4010bd95502b156592117befc744e68b6a4bc 05845a51848c70ac4ca85654d54962644005fab916c14b572137bfd21c90e893 cefc22e0ae4f7bdb5981e9af5226769bbfb4117a75bbfe9d0c0aaead403ad02f 79df1350f974a43e500894a2ef4d069347a199579a198bfbfa7309818f11cd3d 0043576d4b1652fe8a3006e223fe2907221a22c73ae9dfffb4af218b970c422b 178c74199c65363956e4c567def65f8ae5fc0ddb7aa7cf04873db552a18631bd ec64d4014cdce524f52ebaec746e4cb7985db07efee7f230f7a45ba0ebaf2667 61b2796c0984c0d366679762172eceaf6cacea13c448f10c01d9b95a3c0bd932 78b5e5e15304a5d7deaa7fb92d1dca0db3e8caf4a621596fb5efc9e45b56763a 8307fa27a58136ddaab152cb540dd912a5f9cc467f87529fccca7be31e3360b1 bce231185b0032910b1983e47b427164c87735004579b80ff136b48dca7dcaa8 14b20e79d48470cadb4c806df5af9b034d8df8097a3be1f2d7928fe34d2b3cef 4247fdc58e6f242735df1b4fe9ea9fa87c0f93ec02b7a1f57314325ffb849cad 45c59346b2e561a5ce513d69d53d60b0513d0c34fca33d1a82147187c05da5f9 5f7f797554373b48cc0b9fc19fe98fdf53fd7dac6895a3f7a7e773f616501c35 aa2bbb46678024ccfc9a9087a282316f6af2bb04c3757ef7228831d9833dbb83 dc399f899363da744d9d4153065b6b684bd658c393efd3590ecdff3d9c2ed52b ef6d0d6e6176b560256198b8e349dee4afaba86decd2bcce727dbe95e2b70636 d32ebfc10103684af4d1d40e94d595541aa541f4580b1f772655c653a02051f6 7eba6d64022bd3abc8a5c86a8895bc2997d77dc9a1945ce761b65b15da53c6f4 cff49af65ffe5745dca4cb015ff2c06015337e90d3eee87ed64de0490989c684 4c63ffd66a6dadb52cdf67ddb33d710dbccb0207c1e5566ac61f22a21bdb129a ce65e5281d0168a505605fdb278f1898e30d0a941f8b0f9764f13179b3401aae dd903cde97be0cbe8286e4fcb959f165ade330f86c365bc5373cc5d6921082f4 38346157f200df62a9caa5b51c0b39266e67e8c8b22f31876a0737ecb3924c64 7a59f714f0e320e8decd66d486b6aed8285ae48f77cdbf8db8496e43cf327dc6 43549791da8e97a5b368cde89d57dde61b4c7b1826d2bdca03c98547d6c1f210 5a8087729bcba594809cf4a31b6d2a66713d11f44282fe19fee4555fa38f33a7 80de3bf86da79e5ae3c839ab0a723291d3495dd3903c8f64a1aec3c388e774d9 2953ca5866e7d6eda4551ca518fdd1d8b88f5621483cbb9ecaf29097ef6876d3 bb9814f0e3957bd5dc7ce3fadea884545594cc240806159fe497ba5888164654 335a5909bb1f4401db26d7e55f726b209d154f632e713e1bda691e1346fb1fcf 34865ced6ca72c9e1ccd2635f6ff2a3689d3b968bdca016b9775e4df7e58377e cdf0adceffeb97350dd41a3b6ace99484a4f4f7980ba08c3c22bad35b2d25622 1095dd8c0c3555876d83821ef96927bcdeb3c33f5785b7eff6b23012c84e6bc7 e04fa71c25a0e3484b479825afbaed78bcca81d1ec16d0e02b0ed26732ab70db 383dd6af2e2638681fb025379fe1a58438a5b71ccfb44f62082fb711937dd982 3dbe16c9dacc2a7c3d4f2a7aadd7323208de7e81cc92dd4f8958768574ba8d0e 1b724b508e6f688287fc9adb44f097748eebc537b8a9ef2e5ffcd5eb0db256e6 a8ed0d0d34c15f7b9bd8539eb4f0db1a73703f7b1a684590737870417889d75c a1ab8437038769c0677d621cf9bcf25c02fd4ea19386e18e7a1cc4e27ac591e3 d8263c35b3be8a8054c7bae2c394b6c6dbbab44c0a3a8ecf0d705dd1fc8cba16 b903646b44e144d83b2a5338054fb318a0cbbaf2d4ae28f4e683241c72a45645 f66868f006d85e3cbea999cc1de9aab9dab8ed420e887eb6d8b8bd5cab1094e7 f062cc8d8b539657ee47edd97f11ff9b863157b951af13a736b8d16c82a84363 8c9fcc3758b9ab2b89d1ee8457b0d666087169ac25bbac716414ba5587047695 bfd1f74391c25ee492570d813899533797a11e321ea9e3461bec229061206ab0 455a4eba5a915e05782377566b72dab647d9c633dac408aaea6f8ba97581f044 e0c54ce9fe9341fafc56791db8d5ea68e8cbbaf222ff855120afd8dec9f62695 377c0338298c3f0f80e509182a3fd5a40c81f40d15b46b762c0e5598eed8ca41 1c107e944813fe33970f7d5cf7e1b12539e36364181450a0f60631c3b10f9813 8997bf02ac05b9491cd822642d487c72084752b5e4150b0c703e032418d754ca df6b6f5942e2fd0d674c709dc04297c935b33fb597edc3a5734af6196318a617 ab7f3b2f0558840dc839c5f281a190d8277534848c9f64534d9b47f887a54809 78ea46342cb94abffecc038e4e00ea9a5699dc34f89dcb625b35c09c31c20090 209130802e9d6511db53dd050c8c20963375343d5e47823fa970c40ffc553c3a a7b4251d7524597f481c5a5308d1bd3b3c0254d352a9a5b78f18336eb123acbc 7dc00f08f1299dbf52bb029f19685213291c8cc9508a1b2143d5e7b7b18b1882 ea3bc724e789117a5c484b5b84441b515b758399062f2bd98209706bc9a8c3fb cb0f6539b7b8dc5dc22b2301428b9e9a2f6ab08787853b664f6845ea9e85d9fa 315c2d8496df14859b7901592b800da7ae4db9f8c92653dc6d0c108e8b4e0eb2 980a0e7e3ee524d557a0bd194d2b81c5c305aba1c8c195691e4d4ddb4d257f52 41763dbab5729980d1bc0f26643e657852421493e1bdde3b0ecc756780fb6c57 3ba8348fe5bfb2577e997df67623e132d5a290c2eb381144bd42470baaeed8c4 6d9b9089ee7f98aae119f6c6b622bdba6955abe4e874a085245635b8f2b94393 4c82127d23c46e3053c6490ddb8aca8eb567ce3bb1c3972e26a4d47dde728567 5a9a20efb7bf87a0e7c91eeffd12efbbf72d7ae4c0a8c25b9d9c3a4ff8411c67 1d186b6ce7892fa7a1c3f4f00a8b1fdafb82d97e9e7aeebe18750f45d114069b f367f4b4179fa6d22d6d22a828c33de8ed0298cde5fb5d17863509f9417af0a7 2811d5b3ed14736137126bfbf65204cfe201c5298f70aba9d4d3d0470d729ae5 cf4b99cc377fcff0b7ddc95b4f4b523c2310394ca5bf1a17e802afdeb906cf32 e6a2fe52632d2367d577c8092a66c377ad37ec2cb209c7786886e5704bdbbb25 4ced786fa5fe2666af4caf36d267efc92906c327c06d449b601005fe4ecf2ba3 c742a00349add70c374c25e31f1e192e97705e210bcbe81294380e8df3802b51 6bca8c87b78e364890984fddbccc8c407e512a5e691f8c9e4e04f526eb27db32 33f41b7270fcceab849b60835b0c5b8f3d0a3362db18f6428ac8d6cc629a52e9 f6ccb30dba421f16c87224c1fc6c910c28d928947aa80a4aadebe14b5472e3a5 b75d3c6321f144f78bd014df235649645bf6eb8d15a0b412c8791f97dba6ce96 1ee09cd0a0c0ffe069acb5feba91b0d1eb78b810977acc83131b03415a3fa2cc aa4181c9559f8e935047ce91591939a372dac2b5daee47209472f919c6035a02 147b4551c883e364fe17ec3d1e9a40592cc27f8052e1fc7d17a6c5ec24e2494e c9e70b872854e07077e80d0f66a062e15c46c3e108d7cf656a5acbb5097d38cf 32e3e959f6dfb3e9e3bf1b1fb3c822bd8bb584966fa11dc1e444d2ce4cade8c8 842e0eee7a3fe30185d75956878c418aaf33dbadf2d95223031edcd9334a14fb ce6acedb380e0506c71df56f08e37c9cf6a6a7419a0a84d67421f8c56524ce82 c4dffae7b32ae71007f42fefa57835500094a53a7e59b4003da57be6d752a1df 7c3a885c38d04251b942ae97223b302ea5f38383fd01eab497709c42d5798a6c cb5531c9cae36b968f165726c3918cb6f0904779851d8ed46fd9504a90a39776 7229ff35e6048fb93a2ddf96abfd5642442d91ed4ee2928e2dd2d9819604b79b b79d0f869bab272133ec14603e03fe4cbbe5fafcd859b8751d5cffa2f13067b1 49759392147b1f835d36bb592ab1b387a433067c4ff3ef27c65046c868028b51 466ad6d9a84e0662e61b8a8abb0bf5df7beda276d68909d944a28ffdeaa8dbd1 a3b15d7dc0194fb2f1aab6a358d2b113f8e82a5effc3640a89671c76e6a70dce 43b84023745566f7628a0c30be507cb86dfd35b80dc8eba994cd1d2e0af685f7 b8fdd27e4e758747e0d303623d0eb13a255037f5154e0d869fcd2912436d0036 1070a72d4d5efdbef3183f5a0ac7699ca4f91f3d17883f44bf7fbc51b1fae1cb d4506ec0ae0bb3ebcb7d481bf5ca8a68e19125b15df4c15d955dbee943294e55 858799a67150b2fedf3544b0f1e6e3c3d26f7f886926f57f53dbca535c8f6cd5 3c2938a328956c51e42ba52e7bbbbdd2d85cd9ab1807ca2451822ae8002a9f47 2a1e89c0299dee6608a8652416d21ee18b71282dd0772c65d6d2af8174de8890 c710afe34c83d62d6fa2713f8ecd695ba6f7c3556f7a84f9f9e817abde3fcd14 9195d5d5b1a96ddb0f8366a2ff9e0ba783767d0bccc52449136ed07c05eb1729 8324d8cfbceba7901fb55976c5fa6acb13005fbd838dbb42e77495c867adbffd 5dc1c7243b65a5a6de9bfe9493ee49c369548a1b0f7a707c50b49fd0f9273e5b 8cd9443a7faa14d017876ac6116a4de3633059124c9f55e71687317ec4a4d4c9 0749cce6e8c6eb0b30cc9599b9d1e2e8f536af0a52e3883c9b1a0eaf60bc61ec 1900841d34f0df254a39a2d586c69ea435e108c903e9dada712075752da3c470 72fba31c4645db0e3fdfd6c4bd4bec66dccda0e3b1f39ca0963dd6ca237eed10 00de416b0503948f1eedb67da56b1b6b529f648c69f43bcf1a91bfe88a8c0e15 40b401518efa872876a64431ac7463a297acce71697edbf95bd5a4584385a91f 6300f0d82da691b8d88cfe2e2766f7547cf1f60ffbe70ab984cf1b8946334fd5 fa3eafab921b0a825cbf4841a68094cdae5734aee61725ae8c598ed4f66d43ad 45f89106066fb315ab9004ae5031b1f914599c3b0d9f59cd27ae15c9289dee0d 51 550f2cfe247143d73b8ca8c94230aea263cbdea6941240298a96522a699ef30295e4ac9fa151405a5e545c475d6f6d864d4722d248b5e9fad5500c0c46e8b8085305395183db1f246fae4c0ea40e76be4491e0e5a888415a0b15b2dcbf17b80fb3a3248300cb1d61c8418929ee52227e8dc819d4f5a332db61956e17ad428b0cff00324b1ac30ca7993b73942f9e9dc1b3775414071f066a531ae765e49b250a6c21fa9ca1f051603e0eb0c49d2b1b637a022db29f19cfa983474bd62a0fe901aee7329c23f522f01473f5d764a78589b97f24f06e82acfd38c07d368ba02607e0d0653463b71d8d9d228946999b6487bc271b0f285876705cd02b2032b501086a45fce6019dfd7e8c43db56ef03f298ed1843d8af0b8da285231c65a8e1900dc8a3a2bc427c23b03e1efaba87938fbc06d549b12ed7072cbf9f8c10edcc9c04748044f3a359c9842357ad9150b225764d1296060b68cbe961fbf7e8768ed106efe61d4d2e7703bf1b26ee9d01aef6e0889d5a0a587c82f736e49ec248e8c505abcade97aa5a018b78d3fea96c17f79f6db7e0b9a729646fec2752eb5e9e460f1d540c660f483f26ccf2afb61cca612f85533a0834b523931802fba1bb5bd80635a85a5988bc00ee6b59368bf520703cb69112d61d957e0c044b48228ed06a028b3ffc9e4aad4ec9a02295581c5b6859b6e1c6398cbcdb00b48608be1ce546035d2b98386520fb3fb03bdf36f58ae11423821cb80e18f9073ec7f1ae14f98b0e016d2fb7d6fc2be583fc4271eb65456630b5665d913f5334456d65ffa5425709aa68bcda0225092eae513213c35390aaff684cbd0c2646c3a20415800ebb3f060ef700a8b17e146adfeaae163489f3b738166518c5aa0555c3a2cfbc2a1a020553836ae870433f1c3c0e507d12ee7d80a2d9ff7b2dd0efd78a0a1ae7f788170ccb8b65cccb581c3a0a9391b7a52a2faf7536c3ef816e40ba7c4ea75b688b240a14cc3ab6dbb2209bc81182df55b68623c28293e8888ff25a790ba8daad713a0ad47e978ad935d2be4b07bd281c0ab2a252038d227cf9bcc2d64f4193cfd3b601b0ead5123b91e69b2afc0c85539f7083ab4cbe0a99fe7a414723bcbb7063430687a18661b7fa620de67e978454f87f634cf14a28c684756cef67135d60982706082007a751f92b55a693a77c0e6199cdbbb85e0827348951f9da840d07cc6001c95cf68230c8c6115668e682cc483e76a3af70bc770eb464d3627bc041daa9016ed9ddaa9205295e272bb3b3ef3172b52ce39338929f7f944e5258798249a30b57a3397b680d70ba5ef5d6f30d1a5c829a82d7119a7f80a578a64e1bfb5f85081b82124e8ea116d5bccb8ab41379a52ab317e51ce76506db98f224c138a84508ac7ac7056cc9820db1f5991de300159dc2975ed101345e8e715230b27ee8ae085af51523e604926cb124bb70529238da91a8094ce8ab8d12480e75290b5f4d01c959b8bb60bdacc774764deb71d000e5fcd2bf47f888519e10b5524d15df38090655c1ec3a231268f8ef87929fa92e2362d33e566f7c5046b78518232304bc04909b52c267d5e41e36f998254074c97a56ea2008631156dae267581e93da140d17c03b20f2f53919d896a0b52454bb8e19adf1314f5a9d9ae5aeaa8349d7580fd4c08c1da9209cb9f3a17583af0cffb89c75df70080f6653ddcb20b4b326d308c337636d587f90fd2c0eb13060617a4e16d9ed033bceab9f22bf2697dea10a0ff1a186837eff11209cbc4de5b035274f907ef87d5b8e6f1b3adc9eb8fdf8200aa04a2c0d28000e0feadff43a9ad985f0b8e16a124e4993254ee22869ded70f0044e26562145d155ef60f77aced604aad361b8eb573f7c504398b83ac25b25c0d6ec98c238860462d488e8369ab2a737835d476dc3e7b7f811d04858a9f346c0ded28c9ab941d91f81abf983fccc22841db7c0171e40b8aa59d41b3cc4f618108d7c91b213f6eb2d5c8aebbc41d5c3c2571b3b155cd1f2d85d0c961e7e6d51d0a10a393ad2198fc4407b86de23ded44f86b607f094cba653aed28f6ea4137eb04ab06a5b215940fb4b0547b4e1e416d6925a1d94b58d76b46c31ea3ee082f400c312b0b62b731f6b0ea7880e3f157da73b8f642e34380f064ef776fa49e9fd90972b948a54818660920bcd731cb00880dd6daf963b51b708bb02eede2336f7f07ba366256992311d522ec0c9e5e86c8ec1bc48278e33490670c90daad6970db0177e66a4d09af016837044adfdd519f85393731a9a2e9a570dcee585ebd1b3f002ee2dd1fe11afca75e34f980ef12a9b69c39a5046dc15febb1e53cda653fbc0c0660affe8cc0e6dbce8a1c5f08b1a9ad15b0c25e635c4ad1a59a37120300b60522527f5b4907cbbef7a739cac0edd54cfba16b42ac201fc22b94b9e2848e790ff15b0613a0382ada2cfe671f6929b7bfb032ffec6a4e6f389ebdf8c1167dab0baa5397e4725165e21644189c43e61f95768a1a4cd72cd4a299b6036f5325ba043fb0be698ea3a76126e222f5f656f858bd1c5c3a4fcef55d55a81839072af5044529efb2b8fe57875d426615768435b89da774045cfa7c43ab48256282547b0afbca3cd889d897b42daf38680b5e505bbea6975fd83a5f92c6ceca2c2ad48d04b5be5f14882c82f255d8265238237899a22c4e8d2fbd94ff7e8ec7f7ca12af03c13c28a3a5e94979e158482cf922259c27c96c7d0e3113af8b699a74960ecc09a62cd9c86c313000cbdee1411d69c3d28e12e067ccfbc78514277294a80cc7084680a24893e6ca23c9c43e31c7d3d3817933a029bb236230de0e0b918a44a603069c7cf48ae8432f23a44c75c38161168723e302e9b22c9d7157b7f00ac7970be3eb599d09df2313c0c0bdbcb6bc77e8de48246d7a4598ace9df80e055e96e09b4b6a4a1c826b5876c5965873647b7daa63665058c45dd5ba651a4b518b0270f018c5d10476e7ced2b9f6eebd132b37bf3d2a13a55d338d7c5dcacc72f4dd2087d39621969d2183ba266f22b145758c4d82df8b54c7a2c82135021a8dbfc4c036d4b51f51011b1a51a5a0cebd6a511fce1838d330288d02d0f5149adec4e060ad664500ea6eca23f74fd4063a41ce356c287345a04de842bebe17658de4c33049fbe98bd5bd68ad9d744dba870066d96658384e21c5a27284055c74b40463b0faf29c0dda1106b762c2d4633ac7fe6d55783f563c51807fc1b725d8d5d92450ef05c97a28f49dadd28d260696fc8e3bb4cac4217a5665de8041c0efcda79420f9f58b372437efc4a96afe6a2dcce3e642648a270cae90fa4117e75c605189e027fc84bffbcf3cb6c0304fbdab524f50afde3d0e760a15f82e35eabfff4dcef0595384c3dd73815be1902860070a92f9582c372440e45c4061fae081f1738dc0679214a61bae33a47a1cf4ab43049d271c142b02c4db231d6407d7f047d5c120a099c81deadabf1bbb59ddf3a7b30bca9be16b98c92e28390c5212c188d7196074e00a084bf47f983e4d01cc390e3f0f33d464310e57cbb813a4ce0716b07510b7a319df55fcac89d55f28bef3f33da75314ef9d782c617502694b7d517d7530361aed5ef08203e4ac01aba6fcb8c1404d41f52f8e746a0c91fd8d89f00df810b9ee016de75a61ef80111752a39b413ec940266a9fb120be4512f30a3a622a80d0d8a266c9be3fe8b742ed8d8fe2c54e8eaef06677a754798d6ed807516f9940306264a77c824b064f0ab1a47bbe197bbebfc5e0277dcb54c4415e35338d21e0c933080097a69ba5329e52f9c927c1c143c68e0eac2a3f139b37ce8cfc761be0aafa2a799b8bef98c2aff2de4d30606d031e490851894d9d74b984127734b19024d0058c98d86a530e49c8beeb714ed3197eb09eae8bd8b9fcfc0d6cf74b11300a1d043ba357927ff94392bf01e60e021a540f962afb1141902858efb36877c0c7eb33b1e0851b354f5cfbcc0d561ebc9cc53a443ba6240cb15fd7d56ade3130032f5038d8529d014d10eebf3e3cd57e23a0da2d672b3556b10fd89c1cc37ef0c6b6181e3dd4fea0d276084744e53bba3e68b81f851d1d9e5bd7130d3579faa0d977c914c6aeaff3354a6a197057b6737c757533ecd9b7f0142e097304c014a04ffdce71633d8f8bc0547a3ead1cfd5e16e23767079cf80b12fde1348b586430d6b207f71b66bf200a9dd7f4c9d16293500c8a967242beb8952a2932f2baef20772b02152f6baf2cfb847e1d311bfdde2da511d9e27f84ce338fe73e70f16fe030893af010bc2b1449b316f28e0f7918f5946d561188fe264a131b06faed5750f90aa4ff1d6bcf0ce5ff9347ef0b4fb53d8df66bd4cbbc0072fce0b9193b2560908077e7b9ee0a18011947ee0a1143b87ec52c87d418b909a7ce5f11e0d3e410e8104337d687c3b53d3eb821d6c7065cca45f358739a7adb4f8b99fb4d40b6c0c833581c60fdf04bd10a03dfeecd68511da41456941672a447ffacd7c75651d020553f976558cb338977e0aa2a1b26a722357dc86fe7381b77cdda3cb164cce0aaff5d0d9e21c46fd188b378b2a9f460cb3db1d934c18fb75302effe56c8b570eaaa445869dcf7a8c87a38ebceab762648cc3c933ef80ac10d3b1d6317dcad4014858c3a9ff75f2f7528fe81829605845998750e91b9afa5e3476766742eae7093428ad317749322b4fac899e4fac6e1e36ddc42083579bbd7db80b750a94e0054fb39cfef6b40b1ad03fae31f385170143c684c3925d4c25c6e8d0cf2ad8a9012733018daa75a0dfe6dba31d2d5af36833dd33521219c0e5fd921ce6b728af096284cdaff9e192b9ad120192f0a628f8f399438ef6275542a30681622eddbd0f19d08b98ee131d1303ec5637996f5b83f0e3748b84fdf5bbaf7d3f911a60fd06f64db90d5228769ecec72c4301a7b1f68ef14e0f6533d8e2f4e460d2cbb79a0a8512b7a6ce9991355b53fedc05b1c696a10bb0c69fa99a287de4243e0ec8490322c214baccd20bc146fbf5f8e5ee85db2afc2d8b834bd2b086e28632988356058790993c85d636c00f1512a80eab31eaf1bf0fd481ed8a9b8f5945b04289860a42f561b24ce53793d69934a0fbe93a837e18f4041ec6e38b620db9f7dbe4ea0a408d4187eb198a3eb31cfcf7d20f1446f78a412538ebdc74c9174ffe99f2180eb179a255caa1fcd072bcc938a17024e799be4276fb9964e60aa9d4e465a0e20b58eee935f84c3e2abf731bd6968a39bba4af52fb30f8b4129f187bfcbe9a4107986c5acf2327597eedcab62924bea6087aaa162807b77f73f1271f338571f703baece5d2c84b9ce9effa23f40c6562933a9e63d4aefa71bac4b201f1229ce80905ea987f725f3818cf3112cd7ccd835059932337fb688feec3e24fe9160cee0360122164528078155d3473196d63da43da105a3edb8dd56126f5c00407290e0600d511250ee64f9308ff23d92bbf720e16cb8099cc1b414293625ec06edc6309c4a519ad35ac88bd3dbc3e704614d8582d0fb0bef4533cfc15127d8cda67590155cf69ed41aaf9a71dbf83545991766cd7d68e47368f7e20af227106f1bf2b0216a8e199f9f0fa3067d9387e0c5399f0bb6e6bec51749d489a6489d8de4e360df1cc51722fcd8fa18775e05b9a5793961fe0db7b5826278aa91ddfbe15ecfc0864cb0b50dd8991b554bbfb029c72b0fabce898943c603d50eda6270bbdab7d0bbc2cadea95f67911aab0e151cef5de98ef9f0e3696b52926f26b24e2fa9be40cc1cb5e8b2114b38421e53d083fa62d7bc9bdb8be7b17bd8323685628caa53005c93d4f4a0daff10d166df97bcb632b9ba2a234df630792002aace248971eba0144275a3e15b7fb41ef51365a635231291e4b6ff2ade0b4123c99555f644351003ba557cd4d0b47cf8a1bb91378e0fe2c90c680d812816b46aaf6f8ca7456bb0599f4a76cfefc5cc954534fb0eb1a2bfb6ccc719099c669c49bf02ce660e0b90ffef7308ee778a7a4acd9f561e983417123ab7eb62f97f4b89d48f6150420850eebe38a49dbace14c90e531d837d994697023678c5d6ebcc49e1715cf03dfbd0bedb26f477e970356e58d4b5ac533ea479b0668d19c4e6a39b9fdcc709063e60e1b16a9604a70fc71afe4b33177c372085663a941a6c6f6077e5e521be105fb09d5fe92b1650cb1d8cad7ebd2df6a5993fb236a080d38b6398f82e36ab01c28005392a00ff0423fceca664a23b529de58994426b98eb259e49aa85435b392f50c2ca96a3142fe083696ba54979a168cac9a9e89a254c8ac610c938bb897050e0a5c7808e94dd1867a34d2222e6797f053b1ba32c4157c0f040ee2b46b19c9d80f5bdc8788bb88c3ddd5975ace0881d39fb23ded2300c62b0bf2793a36fd92e300adbe3b7958dc71bb07a18efd4a939ee3b65d16129777bab2db1f9575d126a707b398971b3afe8fb5d8e4aa830bf7ebd7348155fdd7ea62d0c4e1d21ad810950eac247cd29ee894e2061cbd3113465d2ae91052b61499cbfbc75622157b9d200487aee91a452d767553fd1eedb11027c612e2494b23a1a52d52e91fc4b8fd8d0492c0dac6c0b1d67dcb28385a8cd72e85b5919d1728293ee29e711d60bfc7c400ce7c2a0cb0ae9e4093d11955007dafd6ce1eadeb1a24377c830bce37036636069d93925aa38f2a6d8b6f1f48f999842010f5e01a395726c9e5734866e71ae004d444a58acabca849265f18f749a967b7fc17e56a49e46dd3253f5bb16cd3bd0ca6fe23661851068f0f4fe15f998e2ba86857bbb451caac8387f77effffb22a0caed422b23792a5bdd681bd97283c2b59df097b7fa4ceb89df1ff0ea7a1de080d1ac6122ea8130cf9d5b4ee15cf3ac689fd019ae9a0500cd6d7b17ef8d8215d00b0a40d2d85288f0aa3c94caba8deb695c4dbd6066daf8eeeff9081e262e3640eaa5aab602329b4c7a42aabf380eb804fd56dc475d58cf9f6eb6106582569c80719ae734362b450fbd0d51740fe2c44f871cb7221b9a9b6a7a4f281a2a28fc60345e9850557bdac3a93fab7f302106ad2ed2cd0e4717c79456b69865959f98b094331860bbf05393e18d43038446c185b728480035e1ad29652eb35dda43b330b361b63f34847448c5db957bede36c009115bbe7151b8ce5c7b7a72bea3b9a2042e0a907d9137581f3341b77a92b4089b01031097f40b1e23db8818686e1d6a0c827e44ed159fffdc44b65cf56509cb539bef77ffd8bedbbb0fa8f2cc846385001ef1adc1bc5fe6cd7f31b7409b3b07b40ee67b02f2c90d8de7c4dba372065104df9a1666c09d7bfd08b14a21f7cfb758137e296386ea1ed73eee68fd359f1e03cf94507002645e17c60182ae3462d44a9d9aa522156d7c2173d6731bcdcd3e0a17ef4c8ceabb347fd59001815fd3cec24005cb9aeca0a24bcf16b5b799c14d0747aa7485aab69c7914d40e765b7296556b470feea7e497b32e26387a6134fb01f170aa9899aa178219359b0d182e14cff5f7fdd3810b8f17fdc4c88a15364a0db47dba15577acf0e1d01e9f45029364d224bb671a616398f9c101855991a4604547649a901b4006a34f5b46321a2f6ee5f2df205c3f957414e39604b9272380760f39f1a44f9beafc65a88c853c0c9ec755dd9dd0eadb572d00e994a48e95a04d95caf2b6416bb39dbeb4ca5165d6b7a6578d96f13d922c1a3801c03cfc5e30cea4c8371ce829a4ca30966653469bd488f890e297a3c50087f07e9532a8eda0cd2957b1f99f00cd63dc7eeed770a51912350d65c58013d159125bc2f350413047bd081ef106e72c2f19538c9b7bb0202800bf3302d9cc095da9021d5836eb20ae09678c6286e175d47fafa72f5d04fd5833415510d8994a41037084e99f7eb01942be52177cbe5938cca162d353d5531c612be695f341b69458f04e6f277360cda48b874dd0f216161c87af81f450c42d3118f9734a61c30c0f73fa572ddfd099feb99360e6b20c8ff24609393b6b825132b67df31cc042e0f7290ee727f4802ef838bbeac63fad050ae612dc8d7f5ee20a0dae75dc43cba61d88f19c6e1fa0eda384f429d11996ef2a7e669ebe126593d4efd86e83d1591a3fddaa42544f2025d0f33e31240ccd925cf1985abc614ba50ec49430e89daff56640ac587f3420686af1ced9944741c869ba7ea50cd6bf22ac3a1647f015d471aee150fdfc05604d726cf0acd5e84a59b168b20ba2a7994b778fe5affca0322ae2a6111bca20201dec905b7d3317b33319cbe00d4718c64db3a11d6f365ef311816936ca3052d0716b57c354fee3c06d90eed59577bee10758538e4ce2a323a09446811dbe02f0c9aaa76595555977fb03073f1d30df66429472749ba11366497ff6a658715f504abe5f5ff5845117efe1057a6c6321c8f851ceae6af9f4641c0e965cdd5970005d32a920722b2da3b8ae5d178f5d3fdc5697543934d7be637d52aee1e06ddbc0a6aa682c560baae8de3ed965a5ab9d8dd337f60c9e577db9bba0f1e6f7a87a204cdb74687b04c7dddbdf079d29b92cb3d21182bff1d83fbb72b54ae7a598d44023e25e1abbedac7a2dc20c303b29d931ac18d80bed543803d65dd469febd9900a12ca20491c6c1c6925f70accfc25481f1ab619ca358a2ff8aeb0ddd7c465890244ab0eeb0b4b6ff35c0a198d25b679dc8000e15a098d02ad71dfa42b39839607213b35cd7d28f47b0d15eec12c1d1d6e0cffadad7ea636834f244e954079860985b1cb9aaf8465729623250c669c481737411bf4ae4c9eadb78355c6f976ba0d4d5cc6d696186780bc7f53941d7fc6c50715c4c975ba2d414ed065db028fbd0e136dc631d7a2f9419be922277cfa8a99444ec946f071899dd973340954612b08ba99d140aa2b3b0ebffe0e0858157b33fa8aa635d1120cd5b4f091b2f7d4d70d752d34ea69b42f9c24f8e20db8f8b48d6018fa3c1f68e82945c3d2d7c75c4001ac6070e9f37649995d18f823f2a387910959c7e4b45b52f20960f42ff335f40f6b29eca81894b8327dc340d2e349f3b89ac3d81b899f4b3c871f8dedf0d32103eb819e999fb0d90a75d71b9dc4fbc7fd9c8091dfd0dd9ac9e0f26e39dc9b280ff60fd6702c6dd246e21c7c0bf8edc4c2875cc3d0fbe5cb880ed1f4a95b8c3401a16b811bda412dab1d3a0ebac89c0c63b2e686ff274e3ed11fb81e5a8c817e0536e5088246a51c1888ab3396623933240e1e51b332d8162cd39c3414fddc3509ae1d46107d16e66db727d4e6833c9bc37b41518131ce274a20b2919c36ba0007dad9f262c3915c174d286808d9d69df6db283fbd916b4984d986ecef6cc24708893ae279bfb5346570e0903df8893b08d81e6c08072d6fcd1b5e1201c652fe027302b5121880483d2dc87365af6ebcd76096419116c79ef3323b43176913450fbb1260896e75a0be1d17801a8bdd6d8bcbbe0d2bbae720e384b728aac4f2970cfd590820118787936bbdcceaed78df2e8736628545804baf900780de46c09b0e66b035d6f5b5e6d185ddaf2919963447c18ac502837d5b4f51c6ed68bbd3bd0799589499c41e62779817ea25f19561e02082754e88c931c50c24f61869e5e101ea4b9ff34272e508cab3c9c07cd7b800883c33a065f2ce490bdf12ff3342400efd68d22c3e83a43303daa633b9b6500ebf8e42d3d58d1723bf72bd98294310077514f10278cca3f2caa7a8d4d2451502e598f37332da1c9bf86805f422d5bd08d97c2a12c7a72a5516962d966ff44a5b45c1b12bbf6da474813504d1edc693062fd1551b84bf4ad7e7b2ce18892905b5fdec551d38b0837f55caa56910c5cb0327188e15872b48970d75661c5b76f7d0f07dfce3fae5a7d28417d0648d383d097bdf15b78995995f581cfbe9fabd1fd7097a27c2d15e980d1e2bf6d70ccc5b089ad000145a4c0f78a1667258e695cc3b46f82c596f9807f81d1343d787001f0c3343dfed759eb42e66164dc0bb494d99b55a8748e1886cef1e47b15af0a6ed0b69aee2ad1511f8b1ef1d688fea65238939dcbb397c8f2fef42c896ffcef95907b5e101af3cca741d52a6f2807266ebef644b9b7396652c2cd6ae773795c4ec0a852ec1b9be565a37bba45219f3b06e8a0b00a8cf0503a25297068e9840c4f509d21c4ef0b03c08f3f8117f4fbf703a60b8be2a75736cf387f33dc4655bb571094007359e8ba6d1e3c0d8053086587e01a735339b7d2dfcf466177faa64c19e0410eea74d493684b06c376e695ecbf7e04ea14f5f0c046307d874228259254a047bb1263e30ea1fa1f8d89b1178e83941e192350f8284aa75b38c3541ba0097094ef1d1e002adffe4e4fe29cd2f56845e6cd3c11e4ecdeaa96effb41852de400f1bd2e9a712e7cfc601cf62051ecac58d86dada2acfe7e789b511e840883e55064e2bb20c115e6a5166f9e879e83ff1f16161ecdc82c233f9f9e93c1d3463010b2154a51d4cbfbbeeef4c0eedca5c73379adb1d743c5ba66b28dfd7d6f8691405581650bea1b09f5e9eb95484eb2358392d43e0afeb1b8cab2422e8212f09700a84fafdc7bb6c33557620356d4890c925470e30f31e7cc431fa72e5f39e03f604cb21362b3a19e9e4b85700323fbdbdbe2111aed6cdab45d805ceb8c715c35507b141bb3e093c69f9de6212822dbb5318e02eb40a171cbbf949e31407b5c9ee04d1a483e85fbda27a92287d85a52490c3eb86718f139837c060f802a3c4b807064c25bf40a9e047f826d1f4af5fff76d04bfa99a54ced5d80cf8d28c73a181c0ba3f4aa59a28a38b387ebb46e2b7b94e8c960446891445b54aa52857c29c47905f41d6c00b711ea92d667e4ee6abf0295f4104527440e4df9b9e238196988c00eae5517d7f41723ac9eac1647d02b500db7e7eb60d5612e2e89173562133cd80959314f95ade28704cb9cc63c8967354789581142611b8a63421a56bece799705b929bdf292b065015b4f6b6e582a29a4f175a13ad69ef897ea2012fe2b7c3e02009be466bb14c8a39af33edfd6ca5907a2888c0b8d8e5e1b99857060fd4e74053375370318927df8608de41b6f37416ad920e8813f4dcb47f01339557f986402451207edb5b442248aa34ca5c4946bbbd1ff93bf51b9001f0d735a0c4857880deec9b59ff810cc193b8ed503f8b8d5697f2d736030e61cd40cd1cba00aee4102eaffecaf7f49e224a3425ca9cd850c3fb71a1dafcd52a948eb0e3dc57bd0510c1ed101d7d6dc0ee5116ce9ae3ec0734826cfa6f9ff8271248c72acb807072906f90ce457871f922b8d74c95ae7cc6ac98ed610f65fa986b5d49523e4f31e1b019cef1ca0f8219ebaea071b116fbf8c1d38c239de5341fe9f899420d777183309020d86a19d9dfd2fb0caddb266c8e7d9f3207267405e554df1b33dfd519ae400d05c70332278841d8d62d554ab9e43db5bf634c0aba75047fdf8f11bb40443026107eb3c7e401ebb07d3b298e8d335e5fa84f8d835495521f044fb2927a625068216ba33efe82b36f6bdc29b1acef4d7b999887cd5d944d639cbbc3b38eacc01508e8bccf0be4687ff8b58098a87ae2b1978960378ba23e6a0052fd105f0f403ce969dc2be880f589bcaef738cc90722883c2b0c48556f14b85b9a14542f97081cd63c3545928a261ebb19757b0a57cf4b3667c2702b96649eb2d9d18e8c8209e96a4c23263c3b51033f733f540d4da59c9ea30e1f9724c745b7ab421d63a10dac08488ae51975071cb8c4ae6df0553fa349d62e5a7eba57912979f4efc3050f33ba087decab2a48d22a1f26bb48467fdf54eb986fab3e74270c0d89bcae0502c32cf0a88992ab7bd11184b638de5b1dec393d0fe0121f31216fc26ffadd17050d76fcdb239d02b2e70e5954cb19ecbb84f422e583a81868c63a2b13b782d7064c506f463adc2809de72e51cd718fe686e17e30fedb13481318d739d19fd260173a5cd8a3d00742e3653150bcf677800bed17ee6b2e1a63903ec7959b5a1f30b9b9ce010dce7908da3ebbc3c1ec8c4a1003bf4e21eb779a42f5d03e04b934704d1e9eb5ee8dcf795cc8a13376c96fc4294d8647af67f590abc4a18955e5c71078d918556d56b9b07a00e435b9647021a9264bfa93092fd76857529772f87a307fb93efa2190e897f97880d259697c22e8cb8a5880f1408a299ccf3368854b709e9118c03384911690a5a0bfb49364b2401fd306eab29e0804e6b28e03852f408c9247325ef760ecbb6e2cf29f4cdd1112c7188ce37803cd9cde6e1ba3c26850a7c11ff91063972535830d78907bc5d8ee2f1ccd53179cab41870cf1959861d03c1a76b3b7106e8232f842327f7836e52bd59c45f260a81576f67cb875e5b1601822a2927fe05f67c48d029d01fec72ff9ea3709c7c117659d8b168d22abf0600133bcf37195ceff135d7503da655409949cff3f3b6c80e3c73fb533d618dd30c15cec98d5abade4d78b4761efc4b608f1db13d243d94431abc8988269d3a28047a4b33e8f7fb7cf3b038208d986743ba37eaaa41e3ad9c22c59998f2b23abe0f5f741dce25dfde2e8fb69a3f11d51efe4f3217586b42eff8e1a8969e473a6f09ed6090ebc709ad61783d512c80d51d08e1e701b26aa2e152a55020518ba7330653d5893d0b43c18bff7ad05fe3655c8895c5c9d3184d5ab221699b81fa875209b3cacc6685ac2aab99df5fa3f53fb439fd294a2599b2a12a8c4a6548bbba8801835671a3a21d39dfb6323d8af9cbb3c44bbc7559f9946e7f4103d7b0a9605a051e76976d328ab3dc2ce4a5a1350e80db20826b285f2dfae02f969c020b59a60771a85eeae68b12e65a7c1bb9647c34587145143fc3a5b505d140261f48de2f07910f8fb905b22379217907253ddb8632299b57aba1197c910b47cc4551b13c063df05e049efde3f7516f100f441003159cc23d0deb5b79b456d07a4a80652d08f05db319131f5ba0f54f9c083e445ade70fd3785ebf31b3e651af7aebf9f1e020e88d404416c567e1345dbe50ce213ae900719dfb0a4737999432a01fe8c9f0e12227668445d56215fbe2521780a07c2e33a16adaa6a77c13c74c5cfcc4a42005697d8aacec53ffb0b85ecac45fce1330bd8bc4899fa91cc88e1a1caa223b2031fac5120d7bc8aed9c1e59d52ae28239aca69d6cd52c3950715e27695db8980ab38afe5ab51a4252b157c3ffe7c6bd2b75b8c93aa80c2c733920b2ca6d52320248483f109d327f79d4e78d5b1d6952efe147979c5e73a15af1bc6789fbf4b80c9cc7c364013dbe522cc37f8e86df62dec8e4a12d80b5ea89cf8f45569a54c50e385651561acd3eddafa2db494d257b66bce2be2feeb5ef0cbd74aa0c550ca70f1d51e324c4987c8895ec75046756eb8cf7d59f2d8ed32a247edaf432ac7d5e03baeb9ddd615057e6b863d453b00c1a617bcd4d9340c196b496bf39506ff7b602586b6466a92ea148e4910f7a106c002277da66f3c2e2e88cd816bbecc6aab20274f248fc73f02d3dd2efcafdf2309c24132f231949ef3555049be4f08dbafb0eaed210c99cdd6648ebe093a93ad20cab93dadb8246b096f32eb2516f6a89ab091595cfdcb3f252108e1271d8f6ebbe0ea5277047a9c4f666599fa31a1e8f3104ebe636221d96653821f54b1d745a1e448cf61f6acf62bf22592f6523eb0c900e46b0547622d76d8481585cf909c4e04c2e2850f3915ca7946984a21b180cae0c1402d001350e4c08d67e239b8b3515dcb1bdda1e1df691f38e6132cb9e80fc0bf81dcc709c7e29b01b4a1ee43ad9f24c11cc804f26c133bfda04f1eafb16c60ce7f9ea345fbea54f7397466cdea5814fa843dd372d30ac49393b9cc74d3f3b0ecef4bd90217b8c231ad0462c29387b039529033130ba34279986de7e5489640354ba38d1bea2f146684424bce21e4006be1c1cbad51e889a94ff5c37992f7b03c0a898f1f2c0976b16359339b27e0ad286d3da23337c49d79d8305bea06ff40b56c250391317c0ad3dea1f3be23be5bac9ed6ade9e8c75df7659a3a00b1c1d0f7b013fb839d66ef102174096fec84c50af0362ad372e4423915d36f3d812e808a2467598e952e516a29e16e92f1cd6ec18e57523fc2d4b70a0552d1f46cfe70e403517d598eb3d1a20b87c03a909bbe9ff5f233c88c0124e7ad7a4d2ff7e4005590cb064f53e5f892504049716f98d846fdb1bcbef0c61f83732f7820f7f21048f6ae1a3a4450b897397569222ffc2893d4fa280d45d23e2027c8f6314b0080d733dd234e58e67b4a31b5324d7033672a19620eee270e85d6a0eae3b921f0d01582151e29a844d0cdf6c1da9023c4941b189ca2c049a69ceb8a868837cbf5c0cc90361e66c6c1b0ef7df5fdef097d5a447c0e471f145d29b43bfeaf66f0509049ce9619ec2ff8b7b457f6af9ee79a9cb07033bb6ca1da962c2d6b678f7065b051e0147e5ef60e9aaf51b8cee66580daf2c69c67a9744741fe6e96c19195b0e0c99d0f070a45dbd0af6bfa3bbd210e50d2382d858f9144104942272321bdc5a0ee42e5d9315ebe848c16ffd85dc464194bdfc0f09278aed72beac0e8ec1940505cfd3d874594f4ac636fbe6729014f239e43b471a3ae3012debfc944a8fc0780142daeac8f8cbab98058f94d97a5f000634c7d77d797c4fbb23eec8747b1d8e04cec159026526333304c9a869c5360bfa1532f4b2b658a269cb56da17da943204c43e3583fc9c6785b4fdce469c32ff433da659f91ecff57c9c207151bf36fe0a06de682d18630614b99c15c3ea1347c293b8f183f7ae3e009e88d2fda0109f0f54b80b9a2df089c1d0c6a5494e6044c5003fee21ef217c8d802e1b6310cf6601611d8ffed3ec3a29c786b5bce3bc8e4d3d975dc674fa1f17c15180f5148b6a0121082e9d9b30aeb18a4058c2ecefce3f3e9631d5e93099c9d47f87f374630d0b29526cc2a15c76fbbe774d26f0ed70b86e7ff67463e3204ec8a482ae4dbe2309cdd5efc4fd6284c76481b8e7365a4438b0d9463b8163411962a12ee301d9020d5b31d8d17a3252d70fd56159cbbe3cf5145c1e7e4659098ec58ad38fbfc1770173db35636560e3f7fb12b6785f62289c3f2aeeb81937823cdff2abd377c9540ed38c1e0983cafe1c81314f3ed8445aea70d354127b17fd6ca0598ea50808b907c0ee41e6bc76831017fcea66103f153666aaabffc496084a43417b0c3a84630f3b97ed8847d94f5b8053dc3c00acffa1544aad18b4fd56c78b3c9949990bf201 +generate_ring_signature 23427a37b63e16df40a94a569475e086585f88042e3c6933cf1c7c089b041a45 a680bcc8da17f610deaae851e49a1153a41cf2a0e7b4df452b0cb0e06fed4fc5 27 76ef12c44e822d190d8591a0606a6246e3e339c3c0d09a3e4ef9b9f3e537c783 dfd8e238c9d58b0b26de9d6d577cb9ee7fb353d5e624577d8a7089dc7ba75c2c 75d01aa0ad629f58acb073ef222e20cf3ed9cc497ca0c0faed5328da617201eb 039651d4977f68975edd0044b8c69aef742b79b553550bbb89d03996940f6a65 2e20014f619c3253a8ef76ada8eb6b6a90b48d80a0442afb5c49bc677ca30633 b6172170bbe84ae154fc5815d4736578dd9d30e1e0436501dd36b09da27bfa7e 8cb4fd174df4dc7db427d121a19bf938b4e8aaad7830e9e638f653ef0011ab54 049bb382150f20b7205ee71165393d59fd4cc0d1a690a088387f8039867f5b08 a3ea8ea364079b4a02debc3fdf1b6d7a3791981efb1688a876b97008d3aa27a9 ffa5fa37dceb7570f33e6c757291119270ac62a9caf79c61a1f73347996b42cd f3d0898f92b979728369d0d9abe73aa690292800f3e4efb66de9544e95fd11b1 29291a457166cc60ec27368846b57c40876d1f573ec9af3f0adf44e478c1d561 195ea8899ba4e879cbbdcb3946d8158b0db04d9f147a07342e73089a7aaf5c53 0cfc96a760e8bba3e0a54bd8f64868fb7230f025603bc1d7517d5238f309223e 0beb14a39881918f54137e99f016277f66d022faf43dd0a4312de3611ef62167 87e660091dfe3e75636c7dd731a7aceef1aa6509a4ed0a5d9dd70b5ff02fbba5 4e1dac7838a601b8bf319f75d333a2488feb652d3f2c1610fb24bb66b5dbd727 6e2959ddd5b086f020527fb71362b3d902b030a27a873654c22794471312ee44 2c75319035311b2f58593503dc6f6f0f5c9d6a5b414586818f48452f5e7846e5 f204e336a2710b43975568558f6845fc099637b05cc8921e0557ef5121672e06 83210f8e7869415f49bb722f02ecca7b1a39aa5cb90ee5f62015c26b5aa7e23a 3ccbee824b4d6ba42ed29e238af0d36fb4213986ce70582e2b52e8e519b23c1c e42edb639429562afb90c78af73c0966e4a38f892a13e29c778af7febd30267f 1bb904406811cbe96212e0b34bea451f8fc119a7559e7bfe57eca21b43eb55c1 8414918482c2104b258f9095e099492c5da1aab97173d21dac8c083206ab739d 65bc67958d05c81c80d48855041e321e1e74aec3006c2cbdf0d9c6adc0549271 12fd3cd5de5c0cea07d92c01871687c5dabee74bd383fe58a0721e5c0658127b e3bdd2c25f7b73bd7570bb5c6d3dc6469dac206a4483a6d8dc173bd2b23a6f05 2 3f529b5044beb9d23a55096ed9d7865435c1024abd2ca87dc213e1dc7807af01193f09b542b30aa3f4edf5bcca516f05c8ae00943a413bf8c2c5845a9dd5a70d16576f2c229c8e8d8a7a38e317b72adc83c1d53bb9c39af06addcf33b297ff02756b9e69cdebaf6a035d79a9f06370aa8c86d549109c642b418245f097e20b04a8b61b81fe20f7449bf4b10832219df14ec26a958c0171e67ca6613ed9bfa70eeb807c0910459e5d971dcb852004fc62609b47833922fcc411bc46bedea61804ee4ae28ac9c73f16e24e95d3cc2ad92e79fe02be99a7b66ac7c19fa8f4873000e2591eda2565967f65f23cfc082d5c26a1ae09927aea39467ed67e53200bef0318792d9432d39360f2b267422a9dd6f2bb38a971d905ecd0c548647a532ffe0c147a0cbeb3b7109b3df7fb487636e58826dfea0b4f100cea7b8a7b797eb6e204800445d108e4b2cb3d750294989de215a9f801db9e13141bdb7066178de30b00f54c66e912f66fe472a06c106f1d9d581edb74b02d51a780fcf4ffed6daae0055d3944393edca64e8ea3afe95b640957398153348bf55c94c03ab1c6105f950a6028cf6d69ebd747f4ad241ad908d54eb83544276a0a8a3143803b89bd2cc80a00959bda8845088916105ba8dd1b7627601d43533bb3890bf85e41463055230829a5b5a1245a0b1e944adedc436e254be2366ec5e76d2d4b9318a86b5f7f490da0bc882d1d13477d11d17c91f7b63cebb1fb10bc92967014bd16c8a5965e5f08dc5f2369ce95b34d6c8d2d2d25b2f7f75b03c41fb002307a906d29610931ac03c4b800978d38de16cc23ab92d4caa800b2add89cb119d0a7194d50d1ef5287003c4fccbdcdeb8176dbbc1e964e8d6bf9a9be4401c1cf0072c2de2e9515d6f700a78d470677986c418e43dfc6f0f08245d3b8c301a28044c418625fb06ca4bd0472bd0fd4b01137217866b2f4d5273444ffa94b1c43e156add2dc3bc22546f903cd80d6a9e05548c4672e70a93a1a797c6015358efeebb6807d283ff856204705f428a3a06a011e07c8daeccd0fd63373b437611e457094f7b2d13172374ac70a8161f389fbb6421bea26b092a4bf8a3b9b888eb47ba82ef780358efac8503307a7834cc6e376a78c35ab7133f1e482b4c4d4effae5facf4a9838d89c072d7e00f4b7effda8e257ac2eeb46ea82dc6396d52ed9607504ff6b852432f318a83e0bcde26e122971c7dd9d92a1690ce70b5aa7b947645a49dd3262bd2c1d988c3f092f61bf555795d4dbb539ef593b3c2f5ccae19087a9f0cad6a47d70fbed6d5a0fec7f88d6652c529835167095c69be53ad2224e3b28369bbe3ab42045398fa70738b9f0756d9b0beede437616e33d65b9cc09f306d6983737d5e62491e0a1090ac852e6c83f13dca5d4a9e08c76d82f774760352ada589ee0a9bbcb756ee71701a02dc3df690218e664ebfeec6b782fd06376edc57d10513676b4054cfbd08801135356067624bb2030cd2f644616e5526bef25df44b8e3b573b0c2603b514d063e7d5fb0170fae65aa37be832ed397206f333e76094a44e7be4ae4f14a175a02163fbb4363f672b2e47628d86355363660a02c02a0d1f7ffcd496126451c0d017cafd06bdda34dc028d834d77c6e7ea508f1720b360c86fa296f37330548920b48482c0594a8cc8f0db2d250d1d1a95c11f27ab5a61099b7a949490db45fd90f677bc646723f405f29076ddc98c3a8f40e1c82f85d3ca5565de539baff9cd50f0c9c0cc6c764dc1657defcfb6ff11b86e483ab4a46507f08af763dd40fb4bf0aea5ca36c9750a32814f5d3df4a27e6b14e8155675e593785f25efb2d4db44b0e71a2f888b1c0d2808db436ed38b9d8d564e8bcfa2c7274dba60a02f495c9c101294d74260d8b51569d7ec2c49903e8c6904078b324dded75463a1e285a95510c56d30bfafad9b6a184ef93147cece263b21542f048415f8867a580d8a02b1300b9c1fe225cf0ca30b89d0c03d118e46f91f513356976a89df306a988aa96cc055d1a221eaf3a00fe25989d14dc40ebdf6cfa48bc50fe8c676a9380147d7ea70387287c180bddb6ced712597189b093966ed73589fd9de75c8872069deeedf000e7e68164c5d5dc4ef5c8e052e86617e198c9d90eb775d7a91978a035c2873c0c1a5f94708028ff566de96fe461dc7ed5e537eda47e8365ae8245a6a2bc4fcc08492f2dc757db6b2dd42bb80550371202d9a293d9ea1d966efadc35d2d4f7370e33c9d6685c207a5e11cba02bfd5b45970d76fdaed73ef87e7e52575e17693f09d0fa58d03cb64798a267b6ca4fe130c967674224a2ef5af6db8e2fbb8cb0470c2e7e8d73ecdbbcb32f187272690e238b48c5607639fd05f0f44e2a7f59adab0e9ed38ad638b8cd5161a7acae3e9c4876d8b00750664a663d1df480538e1d160f +generate_ring_signature 4a79e4858cf84189b1a787a39042ea6be11532c2bc4c1e87937546cf467d05d1 e6e7e9b18970c931eda06ce590197f03131062d3d1b5a6ad71ca1e5abcd18e3d 12 2fb5a4364780ab0fac68569071652025fd8b48e74aa242c27adef75f375d94e4 3c46616f02f3b6d9fcc70f74f9319547b6114a4b2af80da69a665b9edaf6a4d9 1c4d1f6b0f21a6b2a975e2a76e7c4c735bcad9430ae693d69b99db3623944c1c 2be94122483ecf69df0dd4920519ea1ce71af7036f03e27466ba7272421deaac 8eb99f028055ca92189f78726acd9b93ab452227084fe718ac4dab9e8f4389dc 226f4f356c64efa694b8311ad07d4ed444d940d98a54920d2d1f9fa687cf20a8 4bbfe2a4a7b779fa5336295fff9be27b165d9a01920c3fc3ff28d4861997c3a9 3c50a93bcc8b0ac7cb7904cb370d850c81750e2de67ac9d4a769f551bd28727c fa5880974ca82b96b3b64d419674cc7efba182815d23f8424e7e354cc3e63dc4 710410134c629f3565f3706878f34c8b4b049625d520c4efe0b8d0ff9ee02fe7 b0d02c0f6de3ec3774101beb260500d636a2a48c5be06472d17723377f82e0c9 7fc89baf0a1f51aec3e249259b8f22194c406956d27410ce5ffebca081fdca89 1d8156cb422b95597ac3b4ae844934b8e729b09dc95d288c4826fb2b3b8cc90f 4 613856b2ca411b309d246c66c23a061fdd5a61694a4d7e3151c5289c77d51b0a49dd2b84dca274f08d870e857b375837b6c00e269d73c73e20393bc45fb5fa058edeeaf8585e261c854675d89852599914862f451867ea48852c5bf87757b40b955eb8a953b2a9180795f69485e0eedc9ef69d534d7aee0d38878f69be585404defa08da1eb84e80ceb58623ac4ca7a6de61a786080162ae2a8e134c942b5b0f3a79729276cd16a02aa859369b5cf79911d123067b456247ae1c1cbe06bb990424a406104bf2557d86f6238dae70a5b6096fd81581bb7727df228adbfe885605b2f03ed1ff9b3847c77ba6b73eb273751c0e01975d52b04d3b5f05d965cd410df83232ccbc1db1b5b318d533ce38084c8ddf4de67cd594a9e3ea0ddf997979063839c830af8632ad08e652b180e15cdcaa5e115f12337a3e00802ac8bb12720f4eff8ee200f61047ca8575dfa779f6facc4f74ad75d1efaf171f9c004afb460740ebb29de09dceacbce81b590061e213e62d2dfe4761bf97ed9d7ccc2391d803ccc58e5aa54cb8641209ed899b1913d9b692142d7f45632915000ce507874d0b3bf38e7e7034c8e60975fb6bcfa2e0882c8681e62859acdd5c339dd27f664003b33d730635b7a262e5c97dabdaf00753925ee57920f83582e93fefd1d5073205e610d05cf7cb3c32af73feaece4a84596feafe08c714d8c24bd7bc1e436d710492790bb7320a56df42f165e47891fbd10a804a9278266c260e1c348cfa5747098c0f37d780402e08638c7fd2a0e0cced7ab579cf7f935d91db49dcd20499ef0a61e9962b7c454e9e8308e9a4ebc0187eb60b00a2fd007ff4ae11c53c29804c013148a67e294e754b2765bc8611df29d95c3fb468e6844f724136dc47bb082d0a453e6a8ee62bcfee6d9602766ffa50497cc4c278e7207d3ed115a132a4999d00139b6725e139131f4bb276f823bb823dfe6be7902caa8f2811fd136362589b00ae178b3160c8256cc7c74100a5c9bd14cd7081862c85cf68f4ad664b6183200516a6f1a8df7d004710a22e2f28816d0ec76a7f8e76639f702690a3bd87653408 +generate_ring_signature b3a73b35af9a583cec3e5ae026c0974460a27f514a1e57863ab5b18303b49bfd e40c3300a1834002a79553eedb269f35f9d7041cb646e3e6db9fca5f153dda0d 10 ebcb749e504392105e194230671d48c40a2122d199dc6ae4e0ba73efba82bdad 7859f6fd20abfa37a3c77079adacf378ca5e510c878c37c588a9646e93fe8388 427f5fbb4eab74be396e86079a6f7efa6cf50d058e9f050686421abf936cb4c1 0d6be5c57e41a58ef7f994a0face6c73c93aede764b54cb3666453d57c2ff4ca 7d0a29ac592eccd5310a9f6ef5cc6ebcfd854b437325c38254981fa83a35e333 67d601df6e64415b9113817e05b525ecde13d8f59020680a700768f9df9ce0ab ca677493bf0e87103de142670672bab6afe534f83fbf59bb7efbead953f2ae79 0146d12aa7dbdf94c4c6c9fba9ed250f5edb2042dc0a6b6d143155f2f5352452 2dbac320da8dc26a2e4483bcd01eae0ad7748c28e4bb6793f182f976f80108ed 87db7f074e179a7998b44b864409980beee78a7ea3befcc7fd1fa697735562bf 35b98eef6f934ddf49deebc1786ae2a7c69d7c061a108ea63be86644aa65510c 1 c134abdd131a057ffeb9bd9138f65ef2aac6f1df3245841c3e253c7744d7ec05e81d092d0aad6d215c4495729db9db88cb41dc8a2ebe480ae5d102a97b90a10ddc7d0e029c4cb4b48960afff2aece493752a03307cda81cd79ae059bd3b2e409b92d68a4142de43b55605df424f59f29c8698e0b8a72f90535a904a163085a038cd6bdcc824ca94bf647b0a37d801bd9bdd35cca242f5a6dcbf4d7df3024d30756cb694ec36c8bdc928e634a6a8ce4121debeb7c97db6dd7641b5d404e5c9e0a0300d423011fcf8367eca32333eca36931efe6f15cb8cb6ce9e2c352189cd50c6b2a1474c267f5f7abf4f49e2284d03c019006a9615bbe11949743eb64bf710f0ac80c7219ed024a9297c882be1087ff02ffbac14b4b2da87ec9ce583ada0e0018a1db64a022e9669b537a4b30ccdcbab69705dc7ae85cbe81009279796fa901cc4b3fc14b24ce61c8c8e7e0921f7ae468611a0dbcaa5c68cbd30a2df1e39b0ce8594d2a8fc7ba0ed48c09550682c648cbe6ca8302a2d16617a6a9bb30bcc201b123a72aef2c024cb24a8ed16a0d839b801b0432c21d6ae0db155f045ad812099355d3a3afe5002a6098010220f31343a446361aadb2788e1b7b49aff354fa0ccede8d1e5d53e011df1242eec4d74c03c5f5eb4685822cbe6a497ba52da04000e9f88ed91565f4fc65b3fbfd8351f82fe4d960e22f71942755cf2d3641271407fa5921ffd803e00d3068bc1fc0941eaaa6685f08552bcd83b8482ba63379ac07087893e4888f32d8f1f618aee2e3c35ea84b5940e5ca3e580d12793981955b0f9975ee6006450b5681c94afbf08be38f518c296f4df53b70e338fb358c1a7302590162dac7eeb32c4eacc6d6c4f4f7749d9c9687c020f8a9ae13a84b77805003 +generate_ring_signature ba2cf253cfce5ac728cc5bdfa9e02621cac271d7d640ef00c7bad3612f7ea07f ebfa9411cd1911793cd8c3c3751fefb1a4f26ca477020409303e36d624ba31f3 5 a8dd3253a8e5d1aed79cd6b19d6f8f006a6f755c8da0b20e2c9187fe203f6a18 f7cadf8be983339ac7aa073402ff1d5152c7db7a2b1ab927aff355d4ccb97538 beb5217ff6e8217cfa07a89e132171eaae685ffc7155e1349bc2ccb6289970da 6f7165dcfd5f3310abed2c531905a5c69b118b2d708ade5721f0a452248e458d 04201b3245cbf0b7225718ecf5aa71db813eb5ea54b21423f93fc9ba202399f0 d5d5570a52159caf96724aafa56bb402e9340bc605d665b4fb283c87f3404306 4 89af7f6a3e191273e7722963de426fc974226463a5e34af4d73a64a258d5490f929ff430f05f9e01197cdb143fb85ed5461fcb8be21eec1872f5bde0fd192c0cddd2c4c12711f5b3968e72e5cd309a22f492eeb13dd8db744bd764a9cf6d10027691aba258214eea546dc949e9c372c30b17ad10a9a56b755915036d09d8e2021f4cba99574d24cd898c297362ed91389cc5b148965934330720809b50cb010c022f36cb9eff3868f43acd5b3af37945c5bc51c61a95c0532fb2cbd59bbe310da48d59c57f620c43eb9f7e2d9e278903978813c3f5b6582eeb7d7790d0a81602b8e30b363626d50390b9afcec243a348535a3515ee78a9d3b81720c93fb75c0a0c26f6d88baf87b10899cb147691d159f731ff3aa72d2d5e7f648231b8caa90668e5a5dfadbf6ca81168d2d5e81fc3f632da23988fa3463feaff17f588c4db0e +generate_ring_signature 07d546720ad955c6ac191f0ea16e5f7d843651501ec655bb7352f0cbfc540cb7 9c58ecdc8162a18d21f31152b55b98c21529b1b918752e05d6440062b320e36b 34 c5de097b79f066a3bae592e76e4f681f9f9cbddf2d1bb9819d8988f12e657b28 3f2e00f51eaf8c766ea0a750a72aa798c3323c630bc6bbf66796d9eb1dcd7814 0108036469ee5180b39ab609a7cb4a4ba3f4c6ee1399f5a5475cd7abf56f6562 c607fb7f081e02725de17473c3358cc3863eebc8306403fc097d8a8cdf56bc5a 358cc0d47937f554bdfd5cad829d6d4c04bd7b4bdeb8138387409f51f5bbd233 c585912932e94cedab2246ef0f35ba86de9ac8816c72f39fac1b74ec849513fa b967097a873c451309dcc10c8a7716802451bcb666e7aef43c23c4886e958f59 edf0437f0789c2f1280f946e027b9e9d3cd7a7450b77c0c29cc2d1a3a4048e59 6e96fcc4fc42ec8113e21380f6e34dbe8a1fc39c8ec605413a6e4ebcef95a5ee 3540744c779e95429aedc594ca3e7f144fb36f2354c82a215ee3921eee58a687 eb13aa3f8418b863153d0ffa0a185060ea881d0bf26ec385511efd7d5af9a9f2 7f6f7a45c10655b51e31b6d8a8d718eb55be8263be321b959bd31d8175067833 403cf1c69bd4293df454439957b0b82b4fbc10de841225dff9bfe9f09d0b1e00 8e79d62b1b537f5eb4f56e876c25c5419aaffe859b9a281792009937d39e3a18 2c69ecd10e42299b2775570761c7f256084b0ae90d49e77a84f1f728e1ed9eff a15aa70042d5321cb041d4bd2204a7b3cda423ecbadc33aa57c4bbdf22e9464c 8a01554c542f2df7d9ca32dbad3ee49473c064baa299d3b659f30fc0cbf19b9d f2a1a19157f813c81ba6633b903d7c63ef3aafdbd1001206e1b6b1334260eeef 577dc9dae2487db1bb743f56d1a016e8352d2eef75b8439f7a0b06d2484e6329 c405f1ded4cad4af0084231859177e971c5525f1bb1854113ad7c5bc62e21dda db561e3f680279ff441e82c2a7251ac1e763ca0624ea03eb64fa2a6ac72b7b93 a38531b78756f9ba162cff58ca174efc1d776e9c3375d43a366c5da19e854a6a b12e648ca8bec1098b935a509295024ca47bb4f2f0f458cc371afd286ded5bfb e0e9d1e673bbfb6785f45e855cacbfc081725ee4f9572b152976c70166ff6f92 be23ab7ffbe883d989c97c708ce49e6b7847a4b42a78d7a2a122de8e7ed05aa0 d5a6a40407c2aff52cb6d80fe6fcee2cb265517744e655dfcaa45e9b5b159e3f 90e4110de219aa004caa3aed8b67108ff8888aa4857c54ce6d7191e8b11365cb 9dff22c48301e57a059f72497b5b04695bc6f69ed27d12650b8b5e48c9f8f8ad d056b92a46fb72170ff6d95012156b3c75abc6b19a70969ec132f93e628ccc4f c6bfa573278be6a28702532d85b56cf3ecac307a53474f1599bf35ce0c39f692 cbd889739f84e045de92495e182e47d1b87f2979de5eda0e24eb15078756ef4a b56c32d9e333345df49bcf546e4b7555e5820429035314341146e2eb555b736e 1fabeea644179badca2d5a7660fe89f61ef1e1975046490dcc8ab54a35b2ea12 8576859fe085697bb50b74c16b8f8d75e00ec1f6bb6207ce506df6b1bf8dd8ef 85c20f8768a15b7819a3885ecf1c285cfb1b5609a93007fde3e1964d014f5405 13 4c2af572af2615bccfe88571dcd9aa4df64b6218f2c083889407513625761300e8bcbef06be6bc034510979835818ba827e9336dd2670a5be31c6031a05c1005a45de60e7811890278f6c7c744f8ba59cc98b24b3b4ab69b0d8c9801780a9003623259e3fcb8a0df87be2d5035081cf934a97982927a2489b6283969f24ce8080f3b07076f61cf859b2dc96d76fd39e85b16f891059100ae9072be37d7a9c404d1eead8761e72e927a85728606da45c876ae03fa2d0ee79eecbe70d8450a41089b735448461c9be0f6b5a1b7b156e718f7520a06720b4ae337d6f6d4bf4d18097c9517da14d2a782374d94626fb388a2a8aa13969d0a59e067704617b010b503e6a008855d9e5c54700ca8852cf2f424ebb0f795466afe212f9c7e11fed252010e4c983c216964c2e08ca015b4d0f61c338f6663450576cb583ee542a1b1830d938a8febab8d207d6d680caa72226cc0a6d23f29fe67b61db2602da3fdca4b096af3a7693aa5bf1b7064ffa0fd762a670d79f577cc9eb551625df85f647434039afcd243d85920e39f59197fae25ae38f1655e8111e4b3204ee96c9af96db80cf2e7f470dbc808bf6c3b7138a2d902ab02331cb8696f5cdf42df15f001a33c09068f93d56df4a57f5a35ca0aaeacf4e5044f07799a47aba5741c81982142b60d33d6afe1e8cc4ab0500b98a4abc3d26f70fda407df8d606918dafdb089dc80085e37e658ba320d15e9692f022aaf7b6e2e282dc6d192cb409c5d1085099b130cfe71f476300be43345f81005dc3d7b651f6080253835837c428a9e702d63d805e64c67d5ace65cbfb6255f8ea6f7f19fbb3644a3dd92a3b7e6fb6ab3c78156027b471ad6a218ca5fce83c3ccc671f7b2518a9dfa230cee43489b35fac8ba7108a2363460caaa18bf63c7a1a8acc6e2773598a308ec4a349546580ac1ee363205c5473eb275878ec49f2cd30064ab14e7aaf69e7bb690fb11c2e17327c04edf0236090edbf9506aaadc3c793c97f36b8d1125ff24b872371e4586d75b1276cc02abb2cdc21e1dc603f9506f501d369875eb982ac684769b90a42eea5797230d0818de88160ba2415c5bdc5da9b15fe698dcd0358fac44a62fa3a6bed7c3dc9001cf122a1eb07c9842d4edce098ab99c8c1511070dcce0c3f0aabc854876d7100a42bdf0a278523c784a26f5e800c4425ced7fe7d04e16409271bd42ea8819200540735e9eee26e081aca217be30f893c46ab7063c8687de439b713551327297096cc51c2d26938e32ed3e7fe2f6dec46e76401f76548a541612cab694c6dc510d655e36951521f74488f73f8c748b97225642a6eee956e6217fb561e9f20bf606778a2ea44f9365ceac5d1a2df14ddb71f455fd52233817a942db0c9aaab9aa0e563490341af8381e7c74a6d7c5824b3d2d1975fea8231aafcba1a2ef5265eb0636e6cb6fd842042e496355b13dc4fcf1ff6706b18c63b30c374e2c5bb3110c059c5325f9cc53074351b3805ee1135d015d8f1e9e272c7b9cd663815b27ccf200c66f3db1b7f037ef149082e1157df89338b987456e8c31459e6a62967c58600c886400b24e5ed151eb1d02da908381b569963c1c30b3e24f99a19bc8482d300481d17ac2310bb527bfaa80f502b317d63da6b0b5d12cbc28d690f1a239af20049cbaf8276a36ddf2dc5713b86cb89fd80c24797a3442b495f6238389f466c30250179e19a7a30a5557c6c34e3d4be18eb7be5774cf0dcf528970676caf8f3c0b8dc2e8b2b459d978366af8da0206126142271ca3065e591b7953808ba039360c981dfeea37cc01a88326c80cb579ea64de423b3bd9f37fe60e5be036f76b2a0c63207787a8585ac866756d347e52fb7dc329cc96ed47c5756815d0ee698571048890df269e9eecb7923d93f62e8ab3d019542ce074587ad5f8f5055aeee5780407fd50320de0c3f43f3c4efcb2ce606c14d68e95854d241a5b275181a2ce760bf904803119422dd9d1a39a4fc8770498ebb652338e23cb93ce7889d9809a0f0d0f78b08f8dc593bd107a1d7e185932f9640f66814fd0948b8719444c9a317f090778b29287f2b1a9787191f90d46590e28ebd40d83db74de4c08ff6a1f437006d1514db193ff0124393df6191b56463a083a7afdc3726c0cb3f5382ca4bf2000367500d770f5e6e9805ddbdcf58745905463230fb8d6020a6c169a34f5609a0cebf78f364216dbe1a85144a5bc0e08ab21e22dfc88c78eada90c27b97e8d1e0847fa7c2be13be1e41caf3e2ac357f3a5abe6747c724a79411356226f22c88e058dcad1523dcac5399446fb24387cb5d1480a8652c5ce87ccc10cc4dd0c412c05da55f3ff8b591188eb5c3c6364b75ac7752ba1c4df5359e7a7695eedd50e80052d65574b6ce59b0e07d30255a1119cfc5e43d5f0e97eb92a9a2253e391d9a105e42c41debca573a6e0421037d451bf13473b80a06d2be6212650d0c244db59071542e9477a14cd35ab47744ea265f45d9d103e82c2828baa471ed9376a4bf10d5745a1caa0f634f3326552f56edfb4b44c6a4317642b6c1ab39cc19f7012f705ebd6f5afe9fa18c64d7a5b5748a5c39429fa9c53c310d74643cbc516f237b203fa66ac08c73c2b8c8a0c4ae8792c73e2ed2850af287268abe8080da8da91b0028c598da48d0e8544eb3449d1de52b7b83dd6a6f86acfd42bbc99d680a2569d06412e0de04d11509efe404aee94d466df845258599c70e6cd2c5a621c2404c00efac5594132ac56b4fd04f35e2b0c0178c571de1059dd4bf5642c7bf0416c820b9f5a2eba303de9e8c2c2c6e894155640713c80be86310441812cfe262b389009fdb8bd46e9fefdc1dbe2f6560b56f29cd706879ed5ae794bdc79ecaf57a7450a9c15235e55cc262c02b2edcc2ebf795ec9a37f039741ec3435ec5e9ca49fe00a8facb37d6d69565ff7bb35aaa7a1933b988f6e5fcb3e44f73df4d6a31e3d650c1e999bf28cd34556770d0086f9897c9f07e755ee7eb73f5a4d18a6114f651b0ed07afd371cea2d8d831cfc62228a38bd1762aec43bc018fff038449126e81f07 +generate_ring_signature 0b1546a77f70112073985f8a806f191e83e9d3cb9f19e41f15a07671a7d7d173 0ac40cb07f49b58cc8d7c8f9eda97937c5cfb83fdaa0e897e4aa8507cf55f2dc 56 fb504b3a12b0e8237788b91816e395f566762e9d85e276592b9f42eb46b932b2 2de49b9fed2a1379e27ff9df1387c5704728bd81a61381ce8da4d0190e310cba 4b42cb7ed8b175b0d82c6f6577b3c2ab65bf0e0cbec21bf00a6d0a532d508383 6996b0dc98d6d30c76d3424d16591495301a6981e8a4afe7b0b7b0f1682c0c8e 8c3d881ee3a14b52541c96ea7ba2200a17a9ae2123c15774efff022603808f16 c7bc7b4e978d13bc12a50ce744db1c964ea624d53d57ed21aedd761fa9e5dc56 84230301ac02a11ef46e5e7dd0e2e73faf19b28e3bb579374a805a3a07feb800 0f5062ed757ea9f3a1d9680003733cd69ac3077de75e715da4c72774fddbcb78 da0d99811f920053e451849dcb82c6f9c5f6f2e1e0435a52265dfa695289bacd aaaf445a2d1bcf716480c3f552efb0335d76633b4ab6b71ee6bd9460fc24a1ec 1f7884f7dbce7ede89ebec68fbd6765b2cc92ffa7f8c819b1a98c86c0166f142 5e1592fd36141e29de682b90ffd13d68aea02e67c8c75e872ba66690187fa6a4 78f92af4fc0e7fe8c117f1dec9d225cc1a690abe427f626466ad5768a1578932 5f34d7ff989f08ff0839d1df276a0905c9ddee265d652e2be032f59b86686d95 7525ee1aba8272b610df2377c0c275202fe06cc0589b9213e70a127a849e198b 43170ab7baf0ffdd0fe6bd391dbdb7e2dcd1b89707bb00dca9bf12e5025b0fda 8b84df55edbb4cef851c9674e435639a0b147456333b55f2ef083e572313e347 fe394491438a5a7860e525035b70555caced54a2ee0d0d7b23a85abb6b8c6a49 682a71ed5b369424431725db66925de5175d081d65f6a4fe049a5748836737fe 45ad3881f548cedcef87954b2c02363e0a4571bba5c5462d16bf5ae4f2dae423 8c3947dd8a8959059543c20250f6e83bbe841ea641759cf6f6cf0da204dc3fc4 11f235ed2bf9246af8ba9fc067374693fd3150529e05b138dd6b2942b7b8ef0c 57a1abfacc42c7ec4f8933bd93b59fe01f0e49690621379993bbb03ac100f036 0ed848133ef3457e15f0977f10a76d7e535b26eebf9dbdf97264a964214a05d2 fe282180e425a1bd0a8b7479be259067de45fc6e89243f0d22c7565148acd310 81857e68f244e6d5bbb6cde0c7ca8e5b8662870472f78cda46ee11f21ed85b48 545ec00ff569c1ed27563608e09719ec282f47e8517ed95bd1398ccc0f7f0404 015730048da5e50a112a705cd3110fa035634991efda22bcedc9ca6eb6940979 0e3c5354e7e30d619cf5f89a31817892507e64ef94dd9ddc36296da893c60389 9f16a094d0358ef39bbfae78c967677fcb3b701245f66de2faac17fbd9a50b3e 3ea3265959f6857f1b3de569fe573268e3ccd4f4fbb3fafe0b63524c13df0375 1b510080c98faffca1796b5937611e55d2d91ce491789a65df068ba7a06e2fdf 2d1f63f2b1f54755b4a3d328552f79708a3fa6db59dd970eb068845d8536c432 d9f5b0a6f2f92e8be14dd2ce300333d20579d3fb6f8719ef8a8d5ba5a3e72cfa e4d7b759782849314e443316bf297f0ea63370eacb54839ad9a377a54f72be32 be3341838a0f2bc12009e688ca22178361db99df8060bb520740138ddf5645f8 11d8b6d32bc7a35768648d631e4d320ea8b56e932f1a2cbff0d40095fc29f80a af23e51d53106147d060673d7fb4f2a47bb2ab72b2cc255797fcd818f5135220 3ae4c97b960f65673fd5e01d2c92b3220f6b285b748a546027a8b1bf59e8ac6e 76d0434adcad2173c4259116139ad0fde9b2ae309c1c6e332571794cb588d152 08d4b2c32fca4c8879102396427058c720babe1e39b90279bf202931a0f621ae e8e3cc79d0e05cfe951707e1c903524084e4aac4c30f9179c0be50b7beefd7ee bc9c3ed0a2c25b1e59d70d738dd9da4ae3fbf7d535fe58846d523faf47bc42a5 127d126506b2c410e2c8ac9ce472b1d261932e3fe361dd90880864d003812be3 c894d10d2ee5c17280e3e53c026084229c4e9e875cff20d608a1ef71e4573320 02b412846cfa7350e024294beb8fdc1e2587557e8a7b4452d4e9e9eed14c9702 c5fea97e5de8cdfe09a77f6f78a302e9db64df2137117a848c46c79079b6cdcf d7d0505214530ea4cd905270f664d9ffa08585a7ad28123156f52bed892f5055 fc78f270e04a04f38ecc4b754ed926fab86d7fd5cdb959e91b9ad15ab5677b6c 6fa027b87aca6842606c8f8bc4159221eb056c00d56553ff60884ade17445fae bf5e94eef26a03762c215cee6dd95140ef2ec454c2faa05d2964095992a47705 29440b94f3701df4472ac2647af0de415c2fd191c23dbb8b8f096d3f7ff490ff 6d6ef06df5e113380e27e8ea590b8548c81a715de4b54dc9a5d35d1dc45dfee5 c8ead1f8291072758ec6bf2c68a6d31e5f18a9906b3f22b9de6541f4d09f4068 179c75dd86d37ae7ad1ba856d31f36d6a0089ee1a363c540f0676e8dc727be9d ff186cd81fc7dffae43c919c60cc0da74e722d269741c3694c8ea925ead329e5 901ab283171d866625e3b45fd7fe13b6b829a44224a37795a61683e6addd1704 25 ea7f6a21a379e80b5d22a00bff43605a5c641ec986b955d75cd6983a03e5a40a15ce6134535179a72be5ba5c83502a052abf50cd6da4aacfa25ee7ba1361930821c2a9224299c1c3268131866e4f9f304bd406276e04a369818dd4924445ac085b5c6fa5174fc4b659b57102548835432cc67cc9c182b853b0c235c4ff93cb07bbde23d68de1117a7983807ab4362bee70f01eb91d90ca5f788e196df69c32041d2e0a8e37006bea18932b84ca68a744ebdb6aa6c077ef32d5f38b8aa91b5203280d0dc71dea4cd0ce59dc3df4239706a24ec314f08c64dc3a9ad70d4102a90b0a0445306210dc1a7e2e13b8acfc87a5783810e8e1114c3653e57a8c6fd92a0ff00861a19b6c95e8488e5b941c734c04b3ee61a6c8d8bd96b256989eb9aab506c0cc8f425c9b4b6b50c28497d7e451974643148f79eea014ed832e359729820649a6b0ea41d60015b39185d4e73839963cf7c928787f045a393ef4520d423a0268d6ee70e252b2dbf7621662a0ff8a4b50f65972f3401145103f3ef39020c109445ffe882b116927ec96214d4eaed46118840473f03e657fac317417168da50ab497f018501f319826c069349a509da85e91a14af7ceb39bbb03e9076bea4104a25c119e34b58e1f06bfbe89137e74337dd379be370fbd71502a44e28abf9704b174c466b962ea73d91faaf09a520ace560918d3db0ae4f596817195b552f405cf375ab3cdb82edce5cdabfc4f718a0e33fab7d735616c6f83ffa4271cad66019d33be509eba4363766bf685a64938b21666d786f36ae8303b8cfe82c3296a044e9a33293d814e7cfe7f324d10bbd55e4b3a4f8e7ac5f3b42e019e485f3eb9073694a88531baafe2aa47621519a2396a48b5d38b6695f5a99eacc3a627e66205fac696ddfee0f963f1435f29f0c59d8099db7b25e61c44d5aeb3f6751b675c0ad4a0e77ed524a5e8d30c5131aedaae113bbfc2d9e10c777f46708af2ebc9eb0cfa26efb6834499ea1ace959b09d00a20d99b928b607e9b38c748e1a1e3bbed0f78beb707e00602e7e2a78fdff44d2785f665864c752e9baa744b0b717d4f3908cc2a1182faf69702d5d0eb2a3ac119ec6fd00ad6bcaefd2c1a975b00fb5004068afc023901f8a968248deb99cec8a1d00d457eb5232f7cc58d4432da1c6c930a4def75a5b6850c1eb02b133a6c74dc07d81995c45120ef6cb09cc4842fdbaf0d4043e912a7e9c6d8d8aaa44358efdb8f5ef0d440599b02271cf8259d518e490a178aa3fea33f6487f8bf19b8754d28d9188c104a0b537d546ecd2970a9a70805ebf353a68c761acaa7a7382abd45675d8ee1e3ea2ef18410cb690a94067ba804a2f752adef8c7887bb10128755eda3ee220fbeb54f46dd17c0b0a4d22067d7024393fc9af0ae196f660d9bb91b2235b88606a8d2663d5c2393f785de1dec5204d6dfd45c9bf67a3bb8f768caaa66755103e7a14ea32ca2ad47449e01ba98c6030834a5ea66ac038aa6f97f1af2978c9c6e167d99ceecf6d03960eb16040b910eb83e84062eefbf3983d775eddad5fc7cd18d60cc146c7439c86f186d950b060e83fa8f838cc0f37cdc964e364bfc78b73492fbc6e599033a745558a27ffd2b061bb9dca3fd448bc00e37df84b03548bbb774c0b4fbf877a3c3b7cec2c001380401a14d4bb61696da5d5e4e6f5557cf6f0049ef4af152477d630226da11dd0101f736e1a7507fe045e5cf71b4ab749da4b9a88982716441f33f2a1b2ad286640a2acfa1e05244be97251e9e01da118edd4258a1d57e35b06ec83a02b7293e4a0c6d3a8e7b1191a5786ad83c068a3996c937d5ebfa236b9bb75b0a5408a61e160edab56a2ea40069d07fcf009fb8f38e88159ed2565e0e5d71d43faf359b0cec08b25d6cd86fe8556607f55e4f63292b5e63de758094ddbd4e119cb1baff0776034b0e8ca5ee48b4dabd9c06d89ca726382868180b0960a96b974c4acdfec55e0e3a895d58eb158543b5f70c30a07f4cf1933e498edd6f1594bcc7f269cd2b670526f5cad7cda88db8dce8629d8b03059f44946aca76a2b8aad3ff50547f97aa0301ebb366b542e75cac85550a01edc5d9f124b87b65a26928b7237529c270b809b93d85eeea3393abef724784b8307d8f71e5d11d5d57c1fe67f65f507eccaa063bd1c7509f618d6dbeaf3b697188402db41b353736b0761d08d88d883a2f0b090bf2d6f121f9d28b4d616d6ac91e28c4edb6877f5fc5557294056784a798230fefb467137c63d1147a7108f53c3d829d04a54b351ae5ebf6dbba15c52887aa06073a9c95f68e7745a2bd3edb31de58e11ad25018b545039853ba836203944e074c390ab3aa66299a3714fa0b8a91901ed65c701580398186d7ce24ae1354290c8f397c93a409beab93e6091dd842f454ee3e62cbdfcc29492bb503eccb2a1c0c885bf070d23238ec149c2daf3c5be9a9991a9ba255ee891799743f394f93520b2d20498495f8c3b5069d904914429b35c416057b71ffa2bade0b6f81e3beb40bb54a716ccf419c65d20fdf51eee71415e68ef134d1b7315b2ee62dc770229408d3db59c0e6c2c9efb19da96bd7f84c7fcc703388835911e2083e29accb9c8305ba311334ee6761e3b92a399dbdc882af86497e5e9e9d80bf00f6bcf53ea21208f7dc2ddfe35d3fdd46517cf64dbf149f5ef2d02cac8bbc5f43b8fb3cde77c20553732322ed3db3e4686b1c57a1b900a7e7a3e3af6c4c0e907f2274df6343b80a7f6988670bc7a0a8f0675eb99a89d861d8834c6bdf28abf44ef694d549e239041ea31c10ba19b0369a11f61fb839f328a39b9fd833f64d740e74241f4db4850f22382cdc8654fa088d55ee6b62ed8023dc363aeac53e95653d6fb9434e27ef0f275db1298d0d11fd767ca64b9919273b075dd4e15a109465ed413ea59a6cdc0948c46562055cfd7e6acd121b63fea957186f778c8ec1eb0bc12ca80ec318330ee996963b34a4646e82a38437af659a18d0c19cfaaaf5e119cb1dc50f21f3b700822986d743b03cbb34a36ce09178976ab9cfe8c12b26e2f0336f07edf471de00d25d772c234c55be41166c12d83a4b51aaab4b3ab77b54ddeeff4b68b003fc08337395a34260b92f7656e6208524b1217bff180f25e8eabb136f0b1e51d4e605d298558b39c34ea22f04654a158527ef85889d7a9d41551d8639ec5eafae5e08796345c40c31b7c28da1b765be18d5599137915fecef65eaca9c7c4c78c50b077836cecb6c522209d310a0c07f170b09c642f951325adb402d595e53d73f5e0748ba180db3b070039fa090302f73271b2dfe937180327ed3b1405ac9df198104dda7f8c8ea6f31b3e92eee292451a9a5dfe3af2703580707221a9ff8291a220854b3fe4deddc77b9fbe331d4e74e2e89b3fdec28ab411de71f87bd41af8adf008a6a7dbf27512441a5999d362b628e634fd3febc7fc5eb862122aeb1ac6bfa080ea30c00eb18d5c047656bc3a796ced004759f1ad04f1973901b10cf4ef4490f58001d1592f13fa33037e4bc22ecf72a2970db98d1e8d82722f1d443c2501f0e4f423eb7767c41cf4e1bd34bf17a1440d6ac28053b8b0df157aba27547b94d02e0b288abf2efa8b4348025fb47e1fcff1fda0cab0d6eee78607b7fc25ee93d0fa96a93fe64890827b8c31d267027aedcb2a9747285aa83eef4d557ddc1ba93011ad0823ce50a6ab1906b1c2797fc7cf576736b3ee7b918544535c0679c656c0d5aab8ae696617ec546e90227d95cfb956fcd15b46c0742e75f9da9f31ae30807b132af9651833f08264a742dda198646c69fce8c133a70c1846bb1a3f8e10803a07b25e8ef629b1216aa3e2e188daaf8a7a00e7375b4c12aa8607d73222272025a1facc8bc5e1112e42f836c8c9306e2d90f98ade556f4f321a7774dbf68620b6d0c72b20732f83ec7a355a4510508ff5ac223a87c193ae2598f53eceb0eef077ee2ea781164ca42da4c9995fa956f566b93db3fdedcc368164f49b5523f66035b20c6787939d349627a821fb78aa4e51f4858434c15d9d2e82fdaba90bf820eea738ceaf81093afd3ad7d1ca9a9d7e940741c8497b5f174c8c28e490987970580842bcb73c62e0b98695d43d6c877ba243cc8e13be27224f46b457d187705085ce1e4c48ec8c425fd377331ee7c495585ccfa993d598da0639d597194aba207891ca46b7f5994f5c4a6a067c76bad2f3ace342a3de56c99a58218699e41580ce9bcb060b69561c29bdb39ac0a32c2ac38547b36c1605380cad8e584c28d0b054c1e110d0c9ed87410151da57a5865806828386ea8401f84f1e864ca80d2d60767f6fd35d9965b2e5498a282606b90e0f07305a94753a919fabc1e12376ff204b155f53c6b4aa36fdf67ad2c306b1e1e7c109f9c521f25545ac6af4e1336ee091e7ce65b3f281c57294a76caee5280283c98429ba6191e6dcc3cc7b0870b9f0ad76748877be793830ae2a96c8ea1d4a2ae74556e0d692498b296533d4cd27404fd67a698fc96c43eb7d40eaa3787f3897466945ce1ab3a825a9c45e40a783003a5cdd368a6879e842982403b9988881c3b1b0201def49e6699f98d9be55e4105c2c879c99f93da90caf1f004d30f20fff6af22273ccf4f03f9aabec9042db802e0774ce4695d25c33e1b4ba73dc91bae60fa77dbf524c25904bdfafff210a20e08a1ec6194fbb8a8943bc1c3dc626689a7df4cc7f404e29273a1c617903911095fda7276bffe02f353d71d6362085e3d537b8bc068d8d6857d2b3cdbe0feb9036c97fd04a9047927e652a7759ba47e518c527ad8660ba9a5a3deab787e1a6a0022765a4a0645d610b6d7b7ffbc165c39189f92d160345b2250b657ebd5d7810dba07b24e98aa686c455d96b35d7df3883bb4befbe54e8d512600052f7062fd014e0f925af398c80d8ab546c8c144f40d8f6e4225ff13609d0d95f1ab7db8580f6785f5562ac2c50d8fa5153c3e151c46952adf2027fdee5bbc378642efd0790f403292110b08702f9495037099dfacf8378301df774ae05b16e29c37a606d20d +generate_ring_signature 246b0d03ba218cdcb8142ceb5233708fdc1f453f1a492cd962e0dd9b191ea00b a58a14933f07ddd1da220d588cd863ac95d4745b46b0bc581c8b608b8aa48f94 198 0db161f672914d04b3da1ef8fffcdb431771826fc0c197e8825f2b16f0e1ff2b 6a77d52d3688f88f6b8bdaafba20b78065cd9a9fbd05213a96a125ca224b8eb9 e2ea7c4d07361af255042d42a83fa1ed8c49a9f454c7a8f6953ac4a25efd7803 fd944b028ccf14871fa10383afe9e30660a86635d20503c3001960c37de2e69a 61410433de0ad6345b5991355caf4432ae0f6d68de7b8b92fbee59a8ef3461e6 b2a13b81e18cce2587150264dcc9a4b7d5db29199601ce267381f912f3950b7a 54e61f2d4c10373f8ea74c8c262df8130e8d9b92529d9f4010822561ddf431f4 243e9f19ea8dfe283cb4cd1532744223a9530328854c771da9815e411c3ca262 f7c880c639b2cdb9f5bedc069e18dd5891428036391b3872de804d2347e06bfc 7899ec95851d38036fa9305b0d06221227ed571c0f488f99357e74d0e97f2f41 b55d7ec0e21c1b1c397775e2877af9d50328d9bc1d1d9b41075b2c82e59c60d1 86da65cf84ffa0efc5d851f16927ae1ce5cfe37f22ca1ba6bcb0b0d0eb64e5f3 a0baec146642142849fc2bbe21b3fb8dd36c5bbf4c4c8400a9a718732be93895 18e7f44ad2e9dbae18937a04deaf986794c4d1de98423a0ffe9e838505218ea2 085fed1c67c7a5446f05ca2258b1dfaf302d2c357c5626db2f6ff1472be6b789 9a7a89bb811e02200372ebe05f263dfea069011f51616bfeaeeef4d06b471123 c7da7fabfc31f636d3357b0153cfb6dffd2ea29dbcaf79a51b0c8d2b9ee141cc b49528744efb738fbd350f830e7c94d14fcf929490b1a7a7ebef9cd3c0506bec d392408898105d4fa4ef7a113c5e51fc9ec12e73d3a3b681895c5d1f8d64f9ec 14c14d0a7083d434ee67caee6b865733aad7e1c6b53260adc4a67ae429001630 d0a66f5a0b9f256542b2f47d20d9ef0c9f0ce902762e794cb6bfd0223bb03309 53640b7ae4b07158cad551f8b87080c9665cad08ffae960cd538eeb202566880 333c1d4edb0dfa89b137577862e6280720ba32d5a8985cba281e9708a350cdc1 9c85633679a683c2c00a653dd983fd6e6939e9a5b3fb1a91857226ec720de3e1 bb1f5067b77b9f97c17f4a94e9f23c83ce86290fa675f0af3a88bdde6a41c8c6 0237b12838ac6dbae1e8129a75907939d6995c2823fad75c981dcd983ada76d9 5222c496e1219b242efb961b9061080da46d7184ea84ba3f443e5f56e6f8e301 1a73f7cfd54d0c215775175106a40c23a418861b0ecb10d9aac208b5cb917dfc b2ff34e86f46a9e541444950f04822c87a9f3fed09eda2e354ee00a25adaf5cc a37896dbb89f13cbab665ebe009de96110741380b6ba0fa3303d025eb2328c60 2e123842381b4b15f6c08d5dce5b7cd375414fd0309fef0f0234da74f928545a e594b714f1b8cb58d6c1784233d1889c036ac446f4a109db65ad3e504759d3b8 44e1e6f6481996fdccbc53330ec84315b23f2e6177a04a838557bdb2bd967ede 21c095ad1a318b4cb437bc85018015dd88f7be33d7a8dd9866c1748e68586c5d 91c5ccad9252630c2ea99586a87420e8af58c3348ee379c2d1ddf0a221b8a6bd 406ec1214dd65890ee515b98612c06de1fb192b5a9c79a62a30119622a63fc92 b97aebdab39ded19b5151cde77e0dba3bf884b20d76e989f998555379e279d1b 2ce46bb4e29bf4020c8f89041a0127191c91609c94d10167a8bfd0a05436c7af 38dcc4af14c5ef185e7eb728ebd9856666ff74e289176bde4e13f2e89c916b64 22858147faef84673a869220da42dd7ffa011b3b344c2e78d5f060207d030e34 efada635606131016da8e1cae06032667d020a4647fb543730168e424a5b8e3b 267cc134a2557c011b156d1b9d5f9d12d8cc1ceebf5e3071fa96878aefb28980 ec9eaa5c03b8773659309ddabdaa3cdcd7007935175ea3a6f5b3b929b4b45994 1832d72a8c8092c0d1edff25076be448986ffb145a7eac5f4bc99665bc4e636d 76ec16c44937efe375a1923cb4cea0d42a394233d00ed4e77461c0bb19591f46 387cce667b9796139aeffe1f79997a0d019299a8129b946a849f5f537cdbdf6b 79106c954375fcd054ee0e558d8959a6e8b100c1b2475c1f2a30e12e936125e6 7bd06529c0c944410e0027d4b418105022bbd479d2791066aea07763a2beca9d dba510ea42aa855ccdebbbb71fcadeffece08803f3c2760c1822a1e5f4fa9663 c98f55e44c5213fd51b52e869c7096ea5fe9620f8b183d484d447ff8a7779778 1dad1ca1319f93621fba40e4b7ffa7c939cb43525421df2b096361be1b8b1ddd 4ba49cac2da0d71749bd8a99f60bc231bfdc4304ba7d33323a362ba045003af8 823f604a64a454fae850acd9267ab3a7eb96deec9476d2ac69c2f013023ff2b3 afcc56fb9d925c8c7172178f518f795dd100013a35a22469aa01850d5a2b8933 3b02bee1a2346707050462002b2e0624a7f3beb0f61199e8082503ef7bbeea13 a5bf9d17c3fcdd0678cd6e02a3a44cb04127c3cbe0905dee1ec3e2f207e8247b 167cf06f2b5cb320d5d8e82a25312c768d176c1a8d7c2a978b0a6b355d9291a5 121f682795848f0cf6881bfaf24cd161e1d48ec64861a2d3d6f5bd5bdd8d1c19 1c9bc3a726a992f1e7afff27ee6cccfcea8f103dc018161d0bc3cd3367bb8424 4a5293fdea6a697bc9a561326b2ecb6a364a38d28d5b51ac517f8ac36a598633 fdb2f1a88848466354fb68e56ce89e17026ca60d049e063a67d85f6a29848aa2 8056d4516ab1042b2f40fe62a3e1df9eebc350f4cd39d7c60744a25f127f9f24 43039a60e66e80cd341f0d915608485413cf54abd00d6f74ae7d6b400740d82f efaccf178d84c5bffbe97a3f0af3f982049f3b4f7ae981e80b5caa19d887b606 532b117cf74e4766c38d3bc22de22b831a81496e04eb9f30cad2cf1a08c2e371 517fa25d585f74a45202d4583f084d656ea1854b0d03219792f4f3a28f23d58c f472a57780e1b0043e1c06079f64664ae911603ea663becd80d6ee23009000b0 eb6d7f2c9d42f5e9c5341ae6df2213ca5aff31308b8e56b6d9600fa2b3283a29 224239609d5215fe464286cedd2a04cf5be2b4865f3867df83543b1b9405ff32 54ce5094e3d07710882a35a255de37cdf2cd31d9b9c121b3bf91ab7bdf65c887 a0665b64ee617e93e6b2656ec248f99b568dce3cdb93d0cde77a59a63153f467 7a87b59a3b81eed644cce872635415ac56b14e8ce1d57758e3b17d208f802ae9 cd266d34ce426739a6817e41b47da687da15d0fccb44bcb63afe3e6d21808304 91d6a6f62da7e9086bb77ef0fa1652678f4112bf8c58966141812a41bde21014 7f11b0dce98b3b5b753547cad841a49bc70c50979cb7e26931188b2805562c11 6ebddd1fa58696e991018d0bd21507c9fac9670dfc09b83460831d0f99d6dbc9 d2a836ad7b06d482f0473cfcca188e2667241e2fac3c61446616d85e2873209a 0787949bbb01720189c8c681c73df597e5604d268f8ce00990f2e3d5f21e2cbd f7597c8aa7d943292de1f422b9ab12ae9fa26e6b029a8001fd6af739b8c5d16e 3938d0588802af284691bacc09e8924d5339abcc01664cfbec99510906fb8f06 0655a083cfc8ced5a2428b8ac6d90fac5492cb59d492b50c82ec706a9ad621f6 e67d8a5f62c1d7284daaf885e438f79fdd3f473cbbbcd7a12955ef107ee85c17 fe075fc6115e419d5e51a05bea69b864323bb3223ffd2cecef6e0c34e5b4e1fa 231b075c7044e047993781d57fa4d637f5df0cee973f033702ba468fd11a1e6e 63be8069804734a71417e6c6492aac78377a94411dc38f6ca94c1687fc0fa574 a71d86fb97b927e7a2c37baa95b6be30bff078fcdea7cee9ffb2ae3032631867 2868f924fa5a7633a1465a19064f6fcf49236cc893570256b5ff83def889dfee 9327dcd74a80d8e531c1355dab619058fd5cdc4fe01a06f037818a975a7fe74e 2499435cfc2c9432b3888b7a2807f192ba19b62b45d7d273d7f5b0a204d3202b ed5ddcc23a3f887ce3fdb9bb25d758312e7909f9535f9a3ff1624cd86f903a3a c32b0e534cbe8a00ebb0576d80b80dfb4b75f5b17f8f38c9c84e3fe49d13bb2a 9fca90b55ba9b6c5f2c050d2b2f1dbb3bd9e5e51e7b8476cf1355421b0f59b7c e7f9c9aef7db8e8dd1078201a3abe2293b43236ab521c8b0792d56bdd3008246 c09f4b85470610997c9946ed24c314d56689990756c11f8544420cc87826e683 38323e53c2f779eb1622324be8a5fb006af48ea5ef5e3ec00148a147b00f6542 1a06eb4466f0ceb6a73be5b1d799835e86f6ff309a8397a7738351b9f5cd1a00 f1ce554ee1a5e1bb952ae702018ad3ca23eb11ddcd1bfa11b0c1c55256b1547d ddacdb96a51e0a4627d47459904ac22d64a220a1d1bdbffe16f5aed08820fec8 0022e83f981f666993ce52fef3d82566be91bfc1954673b07b7ca3ff5fc58e49 4fb2031fc2197a88d496854b3935daf08373093ccce1f3e69486e4b35c35961a 12f5aad6c8f040e99b9426b95229f2498481a69f4555fc2a593768b68d8df433 a356628599009e89718a1d1d6966f28da025984e3b7dbb310de1e39945606db8 35a5213725ccf4d076a71e87981b63097228bc1043506e1c2ec429f13355ad88 1ec779087cfb19cc36ade2d26f64f9bda01057e81ff21fba482aeeed4ac12335 a913b76c8eac995496954feb8bcc402d6d6f4e800789ba28bdeff1025d818bff a75d75ee55b992b952ca5cd226383b3964322796819dba518628f2b4ffb2fd84 e2ea18fbd119c70d15f4223d594d40202614979a08ca9177ed2c8c76862b2e22 a89ec97b2fe0a79fe66416bc90f396a54b020381e2c7942fede9d56c8b2a36e1 1d27f8fda1fb4ebb4ac24e91716e636398c702214188591e68243123e126186c 02cb80704c2f7e0b1776e82a7e16258f0a8a4c3ebdfac82423e3f72b7ee56d4d 9a524da42a15229a1c21aa734d09e4a71c0ee202f5334ba972c59e00b0871fd3 04cb489a417d31f07b8aef25651e7ecd6ca857605c720860839562f66bdf19d0 d543251d5e015dddb344028dc9325456e3a65d33c5de7d3ffa0bc4f77bfac4fd 70f3c9efd0122620789d9b67d84eba1218f40df0ee75fc6e82566e83e888b990 643ac4025e417233bdbc3b1de190bbb1a0cd3c2dc8de7b9ced271bf3a067e391 1f86b62e7e9a8373f99508c94eae94e160610de9a2885dc75a4704f05d569b37 a329f36a793abf7b70440a1b88da55f88de436e4b65e75cee79957eccc454d94 20493dd3fc860598e2953e9fd29ef927ecfc661443285e7f44b55aa2853b8b40 dec1cc91cbbead300e627e6aff37467273a6deccfd077b4f016bb2ac7f54e591 49bf801c8c676cfe1313e692b56aebedd91546d3e3037d72cbb957253777313b de0168b980e3e80133c7dfa74f76a8eaf7a6e39c4c6f5e2bb9cdf9bc8ad94055 26385c685bb1bd0e809fc3732de977a3fdaf31e8547a78e18225b83120bc6e69 90dae46f81b1994f6952fe1639f741006af0c02c44c72aada46d6fe9aab0b879 cab19752deb880f776f0fe03f32025fd7cf1258638a487698a90302727eb80ce 90a85fe21ccee828f07c00b4c85f6d54516cd4bafd16a8b6a6ae81b2b5466785 430fc038dd9548be476dba2c31c6748c7a354e3c67966d3250900dd39c809e28 49a2d2e4c02a5b94b5ce54609f7dac32aca0a4b2051b784ade638db8df4eecac 4d6ae1194d575bfea52a4e09266ba41676b7a552346100db98908e927ebf18f4 1b0bb0578845f9c1f7c568ba36e7936729fa907f6c1a8c1899229cedaa1aa306 10d3aa986969f9c45562adacff4f74cf399294ae3d6a56c6ae72135f51ea90ed ac69f664f096b6a8684c5f014a8b984f1eb9ebc2cd7edd2b28b37ef25e3147e7 d348cffc84741e63d73f26e953e1a0c6dd8349008edaa22da7612600561a5ff6 d8513404ebf2c16f6f7775d3586391168e0846a973f2229d569a12a06429eb93 fdb70b83761ac9e5f0efaa7af09e44edb8ffa0183b75ce52ecb5f6dbd0abbede 595b8d61b840a5659888c9bc06ab291df02f4cf5199157e0a5b509c382198067 54cf1293b535ff5aacc99099974dd99a1261249472edd6e6b4086fc1cba8c90d 97c875ce3d7224207c85371c7b1def7eeeb5b4690762b719f9f04bcfb866c479 c348eb0c65094a6127d7fc807507c8302a1c69895a97364a2f01ca5482a20cd7 b1308b60a84a9941dd55462ede7f9a54be15fbbb3cc28d7d3479252d0aff9563 c9517163ac2088e0387a0cc36e85345d134fa4104cce020658fd8f542e836edc f64f3d2a5ee687e1f8dfdafc7b06b3b62f1829d4510ae5c41d1e637141b45f80 44a63864b9473c34d0eaf8ae941eb127d8413c6629b8c36fb08b0ca4dc214df9 93a1bdf9e791ee990bf5508b6bf513409cbfb4ebcb16852a38fcb493810ae898 fc94f570800fb6581c4eb3d277be0a34891423b22e1f3391fdae809f9e75729b 12122063caa42018d7beb28e347bf7c0831da5a492b9faa318f822ba6b543008 0a6544c33ea85db30ab5ef3944aa87b5243c6e44a3001f64ef98b86edceb9520 64b6729a7871fc0f35ec31ec51dc6e444b2a5afd02c508e336305f46d5eaee60 bfb50061c92ceda81088d5698f56c4ec4c554354060a38711460332d38e21a33 d77091a9d6d90d9e8d68d1070ae328ce59f7a6a6599068e7520af35fee82fc94 d09449e1b37600ad3a249b93f2decb928c3ab1916c38cca9b56c38e93ef4d987 bd1feb8de1340e0ea9a15ba6ea5bc4f897f0b3d86ab4b882743620cf7133754f 00ced965f3ad93b796fc644f608b94f5851bb8be1559a5ef9a072f07437cfadd 610ac68246075026e34fe339cfd31c413a4fdb201bd7cfe8773875ec5f08bb79 10ef02726da9fa5618dbabbe19e3670ee4167b7c9228f0c9367a53cd8c5cdd95 0137deb5bc0cc9a05e0f12f261da6584942c556c0598db0bbaba1704362a8fc0 f69de7e6f5116310708c3395a3e4e95e4e58aee0cf39670a69d167cb308daa8a 17a8dc951c11526dc54a5b877c78de0267a21841a3fb137f0037666f996795ed 28d84bbb485761c0177ff4b35cf8982f98f7092339e8d2fc448aefc8845c0876 540ffb7709f0e719fb5125e443b79c3bfcdac655671d33187b750bb3a27cdd59 b7e545642adf2ca59cbb67c5161f4fa5c82d42307201b55b55dced00107de1d1 bbabd53bb8454036ad63cddf866f7e6942a5e65d740efdbaf15393a46cc2a5b2 6a19d37e31a1d58fb394814334fd5a85f9fcd1bfc3b816990292f8d81eb2519d 98393707ff0ee3143d5c0e397dad545f7edce40c334f791df327ea542d31cd75 1f71b20fe2cd0064eb2688696006fcb45d5ad0b722891bd7c8077d9717914e01 3751f297c85b2064882e0afab06492cf1b103824cce5c111e2d841d8e8f459b6 ca24b137032418a3d5805e53f04ed21bf57524caaa5cabfcfa19d48061f5f612 b89270edc915387df517efe1ef3d13561001ac485733775779affb438e71d8cd 051a33c231cdddc05ee22d707ee5e8eff9d1ea12dcee617f7dad29210e08f6c4 9119b076330d06caee1dada7957dbea09e03144a122ab049bc0564818823ccaf 04e925699f33291b1ec77269f0f3a02ac1013939697ef6e1ed1c040ab59d213e 9ffcdfcbc96482ce62bcfff69719e9a6ced6e3cf172d0cd234ed9c7e8e5586da 9c35c55dd07ce906697dd1b1951fe1149ebd04cd7f31a0261804db2990cb6274 359f282831d45f2d2f915dffe9daeb8acfb4d99c5e2f1be17ff3671b378a80ec 1c10d7e23136bbdc3b829338ec8a11bca411d5faed84fce332ce88f7426c43d4 6a60603aeef94a7bacc061cb070fda7db41fbdcbb654a4999637af158acdf1c1 97ba1f621d285ca0222a2e31ad7685dc7aa40921f8e9dc0925bd84854d05dce8 6ac558b2d25d64f6b3ec05a6f1515f50474071883e3212cca0f1a84deb99ecd0 e490ef89f3ae5442e0e7afe3bab29cc80801fd82f32b65237208a5644ba942f2 7a904de44a30db4455676a6cea297a0bf420f5e9cfb9fdfae8aa833995fd7c65 58f12241a8a1ba839dad5b602c6ba45cde6113125e59f9052da4c832d14a66ca adef9f1a7a83deb20af284ab72e4fff3c573bd3dbb991106d40f4f725f289c8c bc3930af2f43c34e6d904ee625bd2beebd315d583eb7523c18f6958526bb131d 86e0d0697f039e5271ef13abaa28ff801f90e6bbb79ea905df55801bd0462286 c239f19f3a2be47fcd0a23cf95ec09547157010d8e6efc3315651f23e2655176 99e7300e40160c9b8dea5c066c1578aedbb8c9e35eebcc8ca77f2b2ea2b02dff a1a55f538026ddafba163ba90b5f348df85a214e35420cb11477d8391f62031d a298dec7818c28ffb74b359efcc38eec4b32c1e73e0e8881febd3bd04da66040 b2bf08059fff6b7ac09b835bcfebf106c4386ffab05295dddf769653be295a54 6a3938a46207a8cc52ff35a881a7ff5fee19b918c8d354ec074a2943636508c6 4e96f1e895e106ca0ee4fb9ccee77b63ebbb14c1d514269affa917a0249413b7 4f1e9be08fcee2d1ad61a1fd15c8f0c9a1aae378a763e1a953c96254f38b99de 8321c7ce4d12a3ea5003bb741646c771079875712d53d9ad50068e88b7de9fb1 d0a5e457c374b7d188834afff3ab9aae45c86f7db697b26b21b7a8220563c46f 4565759b70e9741178085228485c81e1bcfd8a8ed97b9e80850b7052c3bc7ee8 133d0f3f9a55c3e714eecc0d582a6efef92f78eb607e03bd505970d631714a63 83a5bcbd0ffae9cb2f11def4f599cbec5bfb02705415ab68dde796c121523a06 c9028b0ade18d0b941882b9ad366b079ff9b1074c0dc2b9b1286f2e3b8e6d15d 3fdeb6bfffd1afb9ebfd745e369eb82c8a328985803ae385f265e7c7c5417370 e24eb8851842daeacb0f9710304506f90d32cf3c49ea8a8463779a8ebb7add0d 25 91699a92fe43e1ea98614ba322db6376e183cd772112f41bfc616da7b5a8ae0345487ba93cbd8cd70e4991486634eb4033e06769be384223ce423f2dfabcb8046b6526a18d3fad7866bd596a389ffaa41859f65f92f73f882c77b3037aaa16070aa6a4532d034356090206306fe901645c5d779ca039645f7ab61908be7d9d082bac632faf9d769705c528d02af6b67ee1a36b15f12c5778a49c37735614b00039d8e5e29b4c411dff1ae7d0a92a60fe069e84ae4583a5099103768b82c80f02e264ed40e4d965cdaa9af657a8b5be12975c453cd9db34f0aaf434fdc170ae03c73df3fa30ce5847ca5c44b5d67997e3deaf0fb1ba9fc5acc0b1acb685600d0b1dd56bf8bff45af73fee3540a57a55830e3e7aa63f450b11f9b5421ffbf0af0d62e5f7904e585485f939f8fd99fbc93ba3ea0555ee2880b5379741f166cd800f7d4cb49847f030c49b0876f04003ce065442df33f687cc6fbdf82310e6d08a0fea04dac85fa056c299547df83280d996309ee2420fe199510c346a3850e79b01f6ee83abd38802bcdd6a7e6ae198946fc6d706cab83598f3f84299becfa1e00daafc7f4d96701b59a97645858c802b54256abca1ca596d22f18c9f440fd5d10e542ec3066c2412b7aebd229a9693c2751070d55ff1e248f11966c03ef3c6ee043456f3593f6eb82d1b99c21a73c826ca0570e8a93790c5250cb3935b6b62e00075c09978b2a287b93f30211ff1dc8a06e4d44b240d3676ebe738fb7c498b2a06e61bce59d9c6aa2e90be4cf1e62de6a3d4e329b978eae0284a7bf36228aee903884cb456c866b6ba662d621f153b30fc1fab7527bc8d3f12336f9d4afb6c3e0eae495ed06f2ac43ab52eb144e148ffedaabd544e16bd99d10951172f1b573c0b08d5fa8afca8e3c5a4ee46ca1a9b8f3107a1326e09cca06e83590e63e4b9e70d94d629b1203c54bae9f329105e9eeb447aed1f9ee2466b918bb36039e69d52075a0fe42273b217acfcd403af135c49b585e88bf3a8a984ae22ef4567878aab0e9fbf012e019f54ab863da4430b62ef2c924f2c1fe6be41d86303462e24e2bf092b3638c9d65968d158122b9d6a964a94d4d93e1a4b066f24341c95c5a8a9540b73ccf7b95826840f3dbce8cdbfed75e3ecb3857fbdd3ee13f69a110a96ac280bb52e6fd2500f35e6d619ba24f47a50346efa87a3ee50e97fda50c6003252e10101bea9ed9723b3d5faf71d1a573c5f1f588ea06ee149519d68ab52d8b70124055d056221d9c5c72e8c08375ab3c960ae6820e47ec0cab360b9b98049b8c7cc07a599802a7658fe6ed6527619040db407226db299ee03733be5d92037690cc202f0a828e072a47936ee3ea94b5fdd515e7ad58980b85c79c0338a44cbd52ace018984eda632b3e182c2baa576243ec05f772d07b3623dc54611ed9062f352cd0357df021827ceb55bdd7c11321f6663264acd85856ed7dda036241e6db096a30ac7d7b0ace5df2e568f9fd56d2d2071ddde4805d2cfc005ecf74d99c5e5ef5b0175a54799f9368c2f4ac6d22352dd6b0ef765f19dbddac7e1864173f70a19680891fe7d839c70444320d0004c0de49f69eb0e19095355d1add6768e0a6ab8bb07d8f9e8c83bb088a182906fd0f0a5fb211f0ff5e578181f0d2a5241a8aab4e30141226e5afe62f7c524df3b447ebd2cd0512eee099da450382c9d46266755cc06ebe0e10980f777c7204b50628b09dbb44d04d2a5066cef4c03830178b2b3f40923fe5a13c683dca11086152475e9e3c78320ca0baef3b20742a15225522fdb004d8e0d330d35e128ef26897f39f49f8b33cfe292209ad6409e6f0bad0a30bf06b5ebf734a0774fd388611125b9e74c2943b5bc33409356c9af1965bbb4ed5008a0232979193615dd2ad14065515f34b77890c28de49cdb2e8642fbf7ac251605438ec69cd43f0161f913de67729f2e5390d84d6606164dd30917ca54b9a84c0efc9bb6254cd6aea48fec1efc9a2335bf6cb232b406e925df5853630fe8d7ef06a75bfc6b9417f76e4d22052655da4b0f177df410310efa14ddd089f904aa8c0c459c0b20d106a92902828c2fa62dd8f44a03de236870f6b2c7c649713607b104d9d83fc4624c793c933a806a5fbdcc49d26094025e09fa63970f436d56bad60f4af3a66c5729237f613fa7947521e66425d4ade98222514c6a373bfef59a0b098a0fb50c7f72116ef28d4d488e4ddc92a66ce2e508954364f2f1107a6ccc2b03abab11440ce824f3aaea307294baa0fb59376246c40f3fe42a251c39f7fa6a0e200d7717a9b5585dab5e45ade7a1b9a5f27569b9f78988daa69bf81f993c4706af089ed739c7482f430981a29ba377b7734465f767c8773bdf5719856c241c00d229d4e42b4f5f780b74412e712de26c63a5c1b88bf09a631fb47aa6e802470d7d3f81375c0e0c8f8635d2a8bed3c93c0828b6b59a0c426a22e1889744e96f0a1a799dfcb662c9bd87e7b51eed8fc0fa28719328843e443ec2f5187ed5e58102dc22a0e0b41e98ce363fda693cd1c0ba1a2aa00b513260ad8943efc6f566cc085ad495aa0dc142edda93a60a7f0eed7e62c46dd0d0f8598f8c14e6f7d3e1a005832b30d50a8dc2cd075aaf50af96a494c077462fdd283e2a8ae7462582ff1f0eccd12217f9d4bff5e99be831d4d21e0a5629ab5b12238d9cb8600f4b101ad80dc4d0d62047fb5986ce9f714a831f39a32b9b90d9aa74276658b5eca31b894905af0c026d4ff041bd351cda99703ac9bcf057a3ed5c4a7c6de834c8f72e60ca0291f8e60efc5db2bd0bc02c47cdb5dca7a0a14d6f5d24650e78dd885dcd4b5c0fa478327ad6e358d54b4e5f6eaa8e7f0ce2d9922e7a1a15b221f6bb9f3174ec0b91e81ecf00d141212b3e51e3b2e8fde71c1e77c3208f2b3681841bb6b87b8a0fb903333460153249e52af97a82270ced5e2f3dea8718d1665ce9e611130faa0c0cc166cf79ce59616237806eff24a9bda63bf2ac5e6d48979623cc736d227608ac09742106f5bc93a9cce7d166260b313da04e19b31bde9c3887a3180206660e6ce6d1c3fc94117196c4c890ac73c6694f9b414a337cfd264aa864d6571aad04ba86c688d497372732be3b6b6e80a1600e9bc70fef1dfb1ad72bc66c077ed60ea9a03b83160cb86e6d4cdb41c3c3e82f163d2507b182aa8191777eb9c79eac038350babc394bbd84de52f0ab1cb5f0fbaa30680e304fd470fb7c6a2144a8310433f9f7972fe04b84acf7bd7bdb112115a9abb0a357692c7731d8490faed54a09ef095859ec566a698dd88ea4412bf3d62059acbc2a694f639436dd60aaba4f0fd6a180c5ef697a299af6e6a18461bbae187fae4762307ff28c29df48ed647b0f7b486e6966565f4cb03b0f988d520e2bc9465daa7f97b914e0678cd064751b00aca3dba380883c0353195bd7f261d67331708c4f602e2edfc43ca987b47b60056ce116ffb54ce23c2c767b261fa90d1821bb3efc63af74e9ee21ef793fcaf506303d3afd833f825ffbb8117047e75370441703085d86a4a64e654d9d261d0a063645166d6ad995240f0c1830b3fc3a485410da57403b9cdc591d9db2283d000bfd47c4d17cc2e024a77d45a897c779473c7e225cae0436527b70bc782db5000609fcab48484c8ac9a513e86aba1295d7dd7b2bd97cac26b192211386c5d97a040f2bc0b48ef85c5021c348b98b35b69cbd6a2eb8feb54949b7c70a7158e16e078ff54dbbb6abc552f0559746754e4d5feabffc5e6ba3affef36cc01c78def10ea4ce6de40fa498f21c22d434494eed600df26622d06a54bcb5c8cecb425edb069b4111dc66939082ff71822086fd4e5fc6bc4d1fa6ec15575385be72e084490ddc1be5fb2d581d5d909ca46a0796461b6effe82963d0d571a62e9086278e8307d94f0e9d952413a08b4a0733f704e4ef56486e8971ca01967305e9f9b6f32b0f7cff615d1f584cf5a72d99aba4c401f609a6e495891200e6163b9fe38ecfdd06e69258f73492a51f5ead29c642de50a4853839c78e5bcd9987f02d43ea860501c5932f115f12738783db8ec19ae959a8825698def6fc56b1424dddfb781e4201176973e8c028ccff37f00c8466f7443bf24f0443f39b02b8310d55d7d3f22e0615a097e8968450698eaf33978b6abaf7309a8b3ef120db57b25db0d60a32d40c98224cdaefc5fd58c28fe63810303b3bd856d511da92cebb5de3f5967463280d4d8b57539813f692e3566d1981e35e85d891c41430f4b73c0bc58ce2290a480c26fba04bf8b1a34ac21a0976b9d7741e5b6de64c40949a3f3cf27cc10807ef01063f22490855680a800a0642e982ba524ab62d972960d90fcc14a798268500041bba9fdc494310f11411b5c6114ba48f8d796395075b7faee63712ff463f010dc5ed204f68692373dae4d5da1399aa47d4dd5b5395defbfbdde24786bbb28b066b46e351b6155761543790ce065804e5264f0ccbd6fb952a032df317b0d9620cc78530ed46ac12c9162a4c840b8dcee4958c8a334820f6070db308e811174d01915d4bdb08ce81dc320ff528d0ba126f3ec28cf95c8d83dcca655cb225edfd000a939089c26eb8b8968d3064d3af109b4a2ad71a05cd167720f280a2a8777b09a909c1d571a79e045ef7db071134d26aa241483c205e667e77a4c406a5d3f1038b0895da9052b2c2fa00b26772529e7487069a48c277e294bf0ec4db923ef000bda2f96f9df6bbf3eb15fe17b077a8c96db63db56af40745a6902520ee797f0fab36b30a38754e47a88e379299af6ef2510bed802b110ddc9872d52513c2ac0486ce5c202dcfeaa326a67ae6c1195531eb828f7f605fb5be826ecd4ebe1b4c067e0e8ec0d4a8c2429a4314d58dbe983b78e0bc88bec77bd585dfeecd6753b807f280f75aba2c23201e555173e9d19e081b23fc18f561567d1fab358c9be7d508f96b353c5fd1bd9aa31283b4a2157ee947f095eafac073aceb2239ea96fd590d2bedecac821958188dd1ed47c7a5aa9ff74ae83d7e00e60b4f618f0647e6980d20c42b9e8765c19f739c505ca33158596e4445efe28298c9ddea8bfe174bc30a18432db20369124bf7f24c78110e7ff04b35a2c8fb76c992f7d820d526d04400ae9ec601545269d6ea40c6ffe90be99a4605b91f1f2f2c6da4b2eb1856bd030944a278e887c80d63446c74aeb34c3209ae9bd165ce39e2b5097b4f2d0b175b067512b7373c4431ff39e448675481311888ea483e4f4363f28776ebb8fae743035bcd5d91a985d0c4372b14af50f9c26b92cee26887c30d20a822a8404c99db0728170d61d523f20bdd125f3c4903301f4ba2f957f2b11e487cc73b81ac28d609589975c0c87cda5c8bfa9b0ef1ea60a8f5dcf2b8c892018a419f5596a80438053884ec38be277792183667eaddcf424645b12684ff816bd1d4482abfc49d480f2f038784dffdb22b7d60d6b15449b44d4b2ab4443bee8de5a5c22694f695ce0abbffa1d34e3aa8427b91d21fb4bbb43deee1d6fef21f0136575dd7c7484b670d24726ff39b10dcb41798923b2fc1489d22a2e5a95e1a394b8c89be2beb1985073056ceb10c9900ee4a23b3dba2658db70cea0f2a14576a17a3d4c46dcf63c902ce1b74ebf3fee12c55ba9e61c88b58303d89e918d12b96842f9cd686a70bb20ceef9cbbfb0d7f20c0ce6e95e93d2ca69b382132252b5c76c5c588ac07b98f7014324a11c1de768084f4c11c09ecaafb84fe934055fe0c455d81e0429fa74510d31f9fefd65f25cf573c588baa13a36ade0c005931802243b8e45895fb5f733022f1c3039bf0e353d63c790787b32e963a5b64a6a055abb7fd65a02dbd862eb07a3c25bf473bd7b0c6a319da7b36d7a24b280610bf26439f860c6915c1d72d5033930162f0a344bebae233bc3440159a5517a8f3eea84b5911d2099d20c099b092fd75579b2cd71c0221e31d55cae5afaeb221cf80bf24029e10496f1bea42307cfcd880562756bc12138edef7f8f256ef7906068832e4db67c1c71684d0dea05d6e8441102a9fae0e1e1e01854354a00b2e510c677e37f7928657b28f900ed080fc8120d61fb0ab548d74ad1ceb030b271ac6a6fbc4957b7c74b326ffed88306625f47d9307327492a2f89e05ed78422d300eece87787bc09fbb159462bc250b25b472c34dec48a8a5e6fc563e535295843cd6940c28f8fa58c5b4a0874e0f0268f81aa54f62969d9fef3f50608ed0c1d9266516b2d459e7730590d9fca7b70e2cd630d0b43e216264eac09d2e53eb7bb27b9b708b8e4f20d9b5f2b92917c90e1c13d02ff1c6af4fb468848844e1479997816a71c57f617b7a699170409a580f54d0b139b858daecf9bc75dd77baf10cb699e29e982b8c8deab2075388cde601739751a4e4e44084cd919b3bc0a45dc32c9d5734c688e0d9a6e693b7d12fbf02756a38aed80dc085ed934ca0755785751f4d3859f06375dda170b6a088e5ec08de6a7c4683d356c58a385b20efe7d3f2bbde5c7c9c7ed795db106cecd680070fed9afb5162564594c40d2f398b2734b41d24274023d11efe08fa540166e4a303af233f140a410f77f53beed9e8bbb971cdf2fa15bd1fc1029664f7592b25b20f20f08ce5750fd8ff3efb2016ec90a2461faaefc8ca4ca38bcc5f47f39d11340beb19222c43d0aafb2ade825e7125a967c2e9907432816db7c3bcfc418f577e042eb8d7a8d449a23a821efa8c5421b20895e97b34985c13f18d185ea359061b03e3ac695997ec614f82fe8d59a99dec3ed2984df4286f0e5b39e7998c29a8cf0b8a863ef5c6607c57d2d1996a7311a6aa6102342d3951243b8fd5262da46fb80241b55647a2e01dd6aa1de77b929d9fb0d51ed6c862d39c282d1832c0f8c11b06fca7cc3822478816988c6cc5c9dfdb009a642cdd1de78c243fa39c6df308b7085da84a1a17996bcf1a47586e76b9d149c32bbebf618fcf5be646ec708a64b406c57c90a490a0e5c58a41c48312db76fcd1a16e3528293770749cc40c2bb0b90fa9522183881a324a971c112b3dad96c3cb6052ae2adaa03d7496a1caacea7a0aab8031919839edb89e7e8c749a654cba13f8cb8344659859a379aa6a832f700667e12099ea769a1e8ea8e7108cdb07bed50ad60601513791216b4c2382135b06cb44ce9f5d4a6f36e668145d9788495c310217423d859ece5c014e9de01c430e89e3689272ea2d7badb629cb77dae0eac2fd6b695aa4d7ed7a0e3c7659096b0df7717c27cbbd2a7bb777440d3f1b29a3b2eb2aca6f6acc1991af0cbb1458d806b1753696c05755befb92d3f23c5d42aac1975242dc2049c4d4504b462e65410707623ce04282c159cd1dc0eb2776d797e40691526052f717ea0ffec3353fa405e95fb65467eaab4fb248497508c41d0ea71554fb026634fc76960e78ae0f0b0432e799657f175738a62f8f08afe00c43e442c93fbf643d4c2633831a4f5fbf099f3149fe42194f5aa19168b4b5f1640d0a4e9a484464f365914a6251d1720b018579ae58b287f58f8961922c015417790ccc5f3196b81a0140f2a9f2b3a12a037f6730eef1b164d9331ac13ad44ff1db00dab5564d2b57729184312d0808910c38c6247da6dd22c1926d957ee1607527ec835042b47b078628e2c0e154fbc3092a8bd2b26ee18f3be850b06f74754ec40a1a4f0a9683c626eb541d2edbab130521b1c980bf71acc5e3360b90bf62507b9cfbc2e668b2e0b95ba229fd6f7dd9095e8899b6bb49866d68a7d6475c6c305997fd823e265106b3622bda831b24630e5fb4d95d66cdb8e5624f09df6e95ede68934779e663da92d646691108df27b04ad9ce0e62f411613fd0f25602db2337ada3be3194dfed48168db6ff5aadf0608b878e66008ba56da671c1d58515fbd5b48d2fe120dd0745f6c64115848e0ab061c9f9f8ee13d13614331f628c3f5730bba0af989d86912e31f80c2eb8ade5e08043c58345d18f20cc71393414f8e052c987d3421519b665e328146bfe115ce0cc1daec25fec9bf801417ddd038c9f01bd37f38ddc35189f61d8b97aecc5356002394aa1175b5b234208ec7f639c5891c2dcad8c773cb4e65cd9c75ba9599290158c7bca37c78ccaf3e4fb04955d7afefd7a8b17d13b1fde83dbfd7e8d747e402f2bb6f5c271e44787e787d5be836a83f699341c07e1d515c4d22cccd9bce2f0781d35ffbb0e35ca9aaa5d8ab6d27c566b42f74ceff822a200a96f17a216b240ba0da2a917a34d256ed6cb2af3e8c4172eee2263cd85220e9d677af879d31da0991965eee5e81fb7bc547c4735201d798a3bc3e8dd2851102a7138ffec5892d0c2aca6d8610104621f0f54c78e68750f2e873f20831ff4e982d5efff258decf0b298e4538d1c55a6bb163985b207156d492617add7381c7b387a6f01adf576e09d36d62e80bc62d77f8ab56b6fca813b24e9a0ebad9a44723eb8657bff211c50c0b7e6ac8d63ea9cbf971160e857dfff9835e49c47bdb930a4920958e6abfa70c12d5290f2e7307942b22db66b6673a5cb3b96109b43ea5ce59acdab85ba6e30525800e98d5cfa6c5f5070bdb0109b37fe14c68c21310659beec8ee27f62aa50c31d0a0aa2d21ee33f4723870633ff89f776f40ae7ff9f1425ee32516dec4f200613837fbe1f00179b58cd6a678b53560362c5e07b770211f1bd297033b22d40698e78bcd093dbaed71f67fe0b1f8057ec3a0daea31fe76be6387f3f66d4f7506bf30d809b9fc3c234587f6b31c8d2c07b3d88b7f8fc232259de33be47463510cfb87f3afa13b67e44e650c26610672c5f067740cd54201984b07bc8cee2f0e02277b0a72c46b8042da256e57d1c3eb66b124335bf97dd06ee20f91cb83e48f02d163b34b853c800b507da8505be1a7e558ae5e8f8c7e7858d4403ab8311c3208de79c79f235612f6137730ea4df4c4e0e518578e3bb6b24c84d5f56ff05ba3055ee38f93d578422ca60e6ee9d893a95b378096ee8a164b6c7e403cd5e754df03075fc826b87553a67f33e93e6dfe51e4d2f242f4222cb8bb456119ab5ecef600a2745642e57ae63e62c2544720d3658b9534f6ffa010cf4032f5b3f16a7c4108bb7ff357b070c8b83554fc06d1ba2881278d28be137471c72a4e6af08c4afd0b91fbf9395ebc06572c7875b43d06ab3e4d9da771c1fa3e5ab78962d27d93b50713fae3be2a4a59675eda0104dd1b84cb42aba8c501b05c489f10a744acf02f0250bc3d744c76e474d5f2ca2d49cc87589e8ebfb3611a9d370227d41e65df200f729d4e28f0f6d07612ba9de438fee323e61e1ba275b4fbe2cb24940feed4240ce05e7a5c6a00b8ee8bee51fedbac4ddecff70c4c9eb061d724bf4353c7ac68099ac8dc498e8bc9cad3c3baf1bc36cf10dae1bd63f91e705df6a28b945969ab06831e4e6a4e7ef4a486c2253810870b1b6281a7e5191c4efb5fbb3e0d4c3f16016a06c9cc206c116496e4ddd85c7302599c1345dd7a01c5f5aea3adee1cc88005e343b5c5f0856b2908f6b7176cbef56123e07d666cb61b407ac310cb41cca8041a11515f6b66ec23110ba4126d96ecf2822fae8bfa98ba130e7d467ed93f280bbc20802a528bce488e9e48893597f20377076f0d134effc68e343584c404f703a846c5adcb5db29e56f4b6d58e74296f6c38ca4e113bc6f175994666b3f57a01a99f216f4465a29c195fd991165fa0a50e3dc5840999d214792d4d368c15150f5142626b85461ce3f0e6b4829b2e121f64c733a60226530244fad41014774e020f6939a396182bc6f638cbb64814d4bdf6742a2798f4da39bc9472cfc6755c0a911504078a3544e44d602087654928e84d85aa7e4087f9e831c05737bf29a108012c93376641f9e78a3059bb38d9e12c11a69e4a87ed0255869c94b02bacc70e7622d1c84df9ea82c4ac6d31de599b2af9f7ca9fd5b3c345b350b9b01c21dd0c8c101bc11725cac963e593d3bec607cfb393e09695300453574b23e01fdc490838ad57e08dc6b852d02abb7e4588dc708ec9d5fb21db2d70a64b4d19437a5e0439d95da018acff669c5fe760591541eb0dce5d7104920202718501cd69bcba0fefd7e9ad7857bfa1b004f41ae97e1352fd9c8f5833660dc677a78bd15243a90225d48df8c28bd5faf7a8cd221f95b6d38103ea4c3520cc4ebb8156f5450b680c1e4ab0c38e9de2d871ec17bd7916eac380f4900f965ed4e6e4a76c2ff7f3ee013eb5bcff371183857c03f62ab73a6cebafd705cf2d476a9ec82689d898181f0e52896642ec8b96ff482b9d1dad4f311feb5fd9755dff1f26ec9f755e910737008d00817779f5ad405700f00d2337e38dd5793d0578f7fb26ef62f8363c86a604c9258905bbe52992be611ef36d7814296aaa2fb124411dffd2084e88a46632046554604dc57863e7e6c02ad8ebfa9b7b58f24ed0e7495eaee3eaa23f60decc03a5d9870aaaf3168e2c4c5b28169e8d34fa8ccbac6932a277db6dc628051de20c54289e5b922debd3029cfa453232b592e182b8429ad33504ec30021a48c5da04779395912260137868a57d6359f63be0c4f7c80c742ad73e2a7518d337f4b60c2c6a5371852d8d3db84093354c7d82f6e7192da0bd27c383ec20be13fa90140d132215ad5160a3c245ae9d9272aac87454de860e2fdb08bdf9a6961021c90a01b8b0a7c2b27674d231e5766f9f206284a2ebc4c72e792d8fee29caa5eca92e04bfcdd858480311131ba4ad3305e5b4e2f8370eb3f1dfd3d14a49f8eb783492028343860414a5ab3cf645da149b623a9c25a5ac2eb16f6aa761e2436426b003024818656bf12dac5a2dccf67be874458e3f28764ab48861c13c03efd1f79ff202fc362b2911f386d0f7ab062599b881399a7405eb0ffcc3ae90d611bd92e8bf04f5062c399cd6008b46c7f5a72e608acf21cbe708ed72d7eb82889ce8edce1203add0f76ae8c2920cca192afdcf8dc2cfa027b3954a6b135be14c7916da25e70e8502306fd3c50a9e0477ba7d8b91730e77c643182ad3aa4a48f9725f28ec330b6b8f2d0cc4a571729dca34099d4cf199a0661244c17a02ae4d9e552d13faad09710a3b6b42ccbea9f615b001f09b02dbd2b9ddff9a923dbff04aa61954a9be0ff1b8500c9e0796a17b36f447191f5a5159a03e2670cd0e8cee6f4f13d6591a080f84dc2b10c5d663d90133f86caf52f7f751efd7c46806bf006acdc21a1b07073aacc92f5db917564312fc8d068e5dc4225cb1230454bf989d3c3c9b87ace00c11d019878d3cf1c267965e4ac2e381f8d3bc33bf6a31b684b9e4d3c5491bbe0e5d368965739cba92380d58f7eed8703eaed23cd0a2fe7f6656e05449604ef505923f50e2dd5050c1b9691344a6bd2b63312cdae32385a4153c72890baacee102da41085079ea544bf09e228f186d8b94d6fa7615980459c9ceaf0a933e4ff700f5156103452b7466d8d3c9d0d686ea0fb5e39feee4511abe3c73fbefb0612506140e9cde797fc0cbcee9fe01396a294064a11188fba3d8c749e4909623840607eccf527978073b6206db80253333e427b65c03075fda4c9901bf9df87387510ce7f4abcdac44d4d2b240a859ed839bda0dc6abaf175b30de9fedb3c99097910ecc96b4bbd92023af202f886a350b6d128599bdbba366af266582c92cd661630b939fa1604c47882585ade45814f94d717e6d67a52f5fe13aa9d69a78fe41a00669bade2d2ec8ed0197606ca09ea291881c061b3ce8b4f2b1f5088d839787730f4f232402962f89d359776f81e82fdb8d90d25c0f396a70a572760e69165d2201671b0b19f69a641bbfa7e420348347301397ee1206e6da5bf67642c7c830a50676813e6251feffab2e587956de1cdf619efc0b0dfe85bc2653e335ce1e7d010b15adc24eb4ba2a064ec2b78724bb586524788386a1b0ada4f8dcf24303993f0a4b0049d7385396d764d62d87de60b2f20e7bfeb771b4f42fadbb975770f4630ea1cc86ed9825950a78e9a5c27e5e7983c61182fcda36e75423d87ac9041d320abeda411ab96dc4bedded92752b0ca4ba8860bfb8279fd4d17374c606d4e09d0bc85ba45eed1dd4f869af265f032cf89058adc0a8538ce8d3243f96123a430c0309f8a5ccc588dc9d7cf08824d789aeaa0a405e687ba7b97b711da0912e35fc0f9c946ab6cebb64b0133e441d8d02bbf061fb77cb36ca452be312e0ae699eaa0425ed7d62876d0c945545ae1a3c9a1a4f7cd5b3764bbeeece90afc6a629c8f90f5097b9e767a4486d4f8607ddf40dd25c2df16c961b0ad1f75764df8a72aa6907f97e1fc0c11c5627b95f68378ea65d726f56708780cbf0f076009783f6031a06f3711c43044c89e9076f1d9c57c5aaee2d921a6b32e2e88ed91b7d800229b60d26abdab5ada93edefb924150504c95ca0162c49d45e234c1f4e26a2a96d7ac01e0e529d97f78e0aa1985b31c3750e4c7810e99416d7a09660732f5671848e909bc42fb8784dce3728579d04a09615c6a09fd49e1a4f985009672037e34722b0c77c53229832750befd88f80aeffa6f1475d873613695181a7c9fe64e9e8f3c002aa73e0eb6a21a85a512ccabb93ac0e1b089b2ad900f717bd60841008e97400968ee123a39eadf024077247a56cba05f8ea1e4a8b0d616498709041d3971960d068487d9a6d3386c56574c6ae9b1fee0aa09c33b24b206c3ffc8e6739ceb910dd7c12d235ccc5007e1c91b91e9100040c75eaf3bcd41e5f5e8c62e84809a050effcd505448b3adcc70db588b9782c09dab732db476aa483366927f76515f9502e3012971da123c8ea5e5901382da58cd269bba30a9682737bbdef4bf901a0903224e938adf0ea5b6927456fc08132f6d52411f53f5d41df9eac9f109f8d39207439b510a791516de6986a5d008364f2e006313af6ef60438ed42415d791bb70b090d06eb3dc7f597f9b582426277de4b7eabb260e647366adbefa72f45d50f047f0be10fc5e9064885c66ed598e460311140db05615192e5e059c55403777a062878628b75db81fff138611fda011a062ada194cb80e2bb635e657d4c5773f03b973fed6428fd0932f21a7ac026b8b2c94dde1cd3182d525211982a75867360b36f819f2359665c774e72bd24091fd7c4d9160c62fd92367eefd9115ccf57c07c5a61ad23406fbc369ae3e1f23f635a9126fcf89a525d7f28e1df60a83d0f9063d0c7b57befa8b2ab425b43301d639f7579af638eff5ac1cebfb0f1dc053240dd018221c868ecf390722d013b346f21a755592049eb55cc066bdf9a613ac18013c95771c8e97d38cc2591a8e6d053145d3a4c15b71effb726bdac189af1b7f0f0e4eefe9f006c78ceb33f4f48fdbd3567dcd9a28ac763beb1a3e2e74edf43e02b6a0e08599d58ea64f8732a8fd2cb39e76ec80ced1212ed5e35cc7d18c2d770eb4d7e24db9e651bfcfc9e5e22c329c846dbc42712a3009ac257cb6cef04d760bd9c23a51f09ed2080f72b3e34f71bab5a601458f2106d3e6e6d6d7e6c03e0b0997daf163144ed852c2200e2450be39258ececd5c117becc500ac5b79e2d52707f43b12090c31e2bf9abcc692263047243c61dac20ba4212079d5a2bc364f1001966a85595fb413102a7aa056cfa886363d55cbd9ab0f58a365e36e7a219cf10b45ced70c0b5f81793fc5639d7dba3bbb06a53a81d54e9a33a26b8b005a82fc083a84478e289b3fd24d3dbbb5e5730bf9cba3dac4ca5dbc362935b971af50920c9eb46475734f7d42f176e78c8a226baf097f74fab4a82ba0dd721c50b8e01809718cab67b368ecc007ea05783cf80c464d7acce98a44a6eb2053393d3acaea04c7c88c0ddc657b807ca68ea0953823186097c0c8b304ca516a1a39cb35d4c20fb2072fbd0acce7b174f4f81726a45e4b032b50c5fc4d50340766c941c5d8460aea6f068a4dc113e8c6e412b14e347c073643f347daf88cacfb668a309462600d257fe62504693dd813d55bd5e5b6d75c6f24dec3d10fa1ca8b062deef38a910611399ef2ba65ce299a088ce2f6760861000703c9a8b63819f8cebdd87e0a56062956224d5f08319ec945f50f57fd42fc98af19a02075e0ec217a747dfb3fd70e80a57b31812e9995f4780870d81457ad3723a0ff1b065f3606482f2b64d8090e3231f373245376f49e25a2a925f058b632c699c32e4d85f28db31bdef4786f0e1432c8af25e8a66fd924feb1bc49ee1c418b0dfae82991edbf9ed677c82bef09456942f258efcd9b203a140a0352cc84fbf276c94323d37410a474130d4ecb0f76814c9830408ab5764f1bcbb6a7048a4bbf515826bfe8282697a4cfbe7eaa0c65895648d1378164a9bc787d3e6ccfd3dabd7d87d9053b53f8cc6fe88279f80c2df6eff62c7bdfe626507a67ee295100fca8b1df2e6887516177e727d08d5a0332dc8be98862c587d2c44cffd94cefc7aaea5cf195643be9b0f855747e44c50b45fa6057d9e3dec3a7af9e63b8650f598504243d1a7439b5af646692f1de92008e23015b9f5057f78ff5aee33d946cb38bef508caccf03965d1e16042223950209de0d44c16488482a4bca2c4e882952d57f22f0d58ad488e5fa25395a4d6309ffc01afdd5d134a9423f102254304d5339a2d0db63711bf27e00622373e11c0d35161da5f4915e520aeff3e0d644437335e18d9145c38078714f964fe9774702ea31907cb1e0443338640bdd07bfba58edbb8b3f3d4a0c2d87a503360c78b20890dd8457408b712fe95b93c8eeb81224ce7373ff394857d4c0b220ab8b10ca086a3e33782e7b6b1fa84ea82b97061f430980be5595d0493e80f3c20f2cd10b0539f97352b592d42e00091fc312860d6506609f68e682eea30c972b50a53ae70ee812265cdeb9e136f953e95930d367989c2fdf7a1a8c01aa07fea12ce29339062c1d01eb0997b7e606b9112c48556e7254cc986cace4980c2f285d492383d30eb452e83cc6c4a1f04d469d86d8618c180bc92b722ff0fc66aa4db222c0bfa00c36d9e980a6558d3aa912c3c198e30ba06610a2cf53421565974678b4035d4805973c939dfcdb7a35d325b4f22449d1e83308b98c4be1351c4647dad678563d0e471f2fa769a615ee51be5bd56810f54e964eda3a4a02593e1dfe729a0925a00c3ae32a9513eb8577d59c9ca6ec8dc574cb5e6ced89e376453480dcad563f350cc5079a8f55c252c8f5982ffb66fac1bee2988a0ef6f14fc22a0c7a5f2e2a2107f7f5d0209f0bbbf09da9e844b1a3f59c4c12655664b146051d557ecfb3f12905a7bf8d0bdd033edb4ea70b35d1df5bf723f970a8717d83df9cd4db3760832c0218a302de4a350540914ff78ad63cfd5e311a6edd172084d15fe7cc30a3b59005cfa82e2a744966b85244b494b51d913d3e3a461d72da466d2e44092d451eac0614e3c3f981e56a488dd7c2655048e283197a3cb4223d6b38df3262e20add570aeed51819e04b645a538e476442782310782df1e971dd61d7b77014a464198f04711e3a7c9bd6f62b37048446db74080b6637ce264bfb901a583ecdb3f01ef404c2ecb491e10a299eb20be84fbbd929d833674177915b88338a80aa4654114d08a8390ccc731fc443454b005c2fb964a51b220cca7e6442df825b812369e6960d434f9debfeb29a574595e8126795bb75babb700f79d0813e91a144b2fe33d90a6b80d03374c62edf0a6b3b56d7b577b6a8e515e1fb0c079012cde87a8b6bae0e493b7649d9c76f48a7cb30a477c77eac6689e5d7db9512e3275378fa9a6bc2079aa5d456da9a504906428de84f9b34e0df23c264b789d79aaea6c16db4f81c041bc90fb45facb255dfbb0e5d2521ae74fe4dbcc7b056ff6a2e19085455cc9c0dd18a86e5d1ab695905dc95f39f653d6db128a281d3d08af9ba20691aadc4170a6e4a3ea298fc04c45fa7a2f5b8007ae277847128180a2e7375b9498fd517130baa931b6826a0a20faae159a1087c4e2ba26034652c0b38a53b0aeb96e9ca0b0f700ce71e41249cb4182997dd04393d81131d579902335abb5a177c138865c20d0663ad8395afdb154c78578c58e4428907bd3569b9d7cf58c0e1c53f3e57e305d2d79d0f023944cd466d16212f4501e2ba289883b151dd553908141c28f3cf00cf4894d8e3bd4f0bd4ad4403a72fce4822ca9eadd4acb82cc086c164735131035b46c652f905f48bf2509b2a78150860f4ab8a8d65dc610dab2d453f12bb69036119f0746eadcf8a4a03b1de0f8c22f0801884334a4b027a0a47d3c4bbe0460938a3f002061ffa01dabda2f96a786a4a19fcd0d0099d1a498c8b76004732ac0f5f518d07a775fed793d45508a063c3fe77135cf68e512d2093bea41bf7d9ad0e67466088f4f5bb5614b7f3822993b66e88fd118b3c48f9e3452314db4f884208e7622c37caf11e219f09cbb503a00f7f2ea0d7d1b0147ea225411ac039b88a0b4989287f23603a1a32a19823f0e238c7418e3002d7c8a5ce212b25108e738101db82fe2d88d5ef70ed231804e1716333e3cd21f8976c556e2af79643ae21100755ac97b7a5cf7e660ec162a4dc7e6661d31a804a51abe6178f7ff2b4a5cb0f0716401d87bca54f1d10b3ca6a44457469fb4052cf008cbf4ea6a0292de65b7b085e630d2f9f57e0917ba530095a5f512c236e4888077c64eb3fa43b70974eb6041e71305259839f7cf236d1e66e488204c108c9f60da7369562bd9439f9d1a10bb3dfaea30e7b1eacdaabba3696a4ab5df990fd7332c58776b4a87eef1f98ce0efd45bad2d47965c955aa7a2e4a93a6cde28e67ef7d6be89ca917b77c4d7ac30d42fb9bd5f1e5e42bbb6ea6efa84ee8a0e119a751f6be839882eaabda76b2ce0c8bb85b16b5a77cd0849040b48e63a96551a1416e712e09713acaf1f6bddd1a063870aab5ac79b04243ff3b1a23d265c8bc6c151d9ba5ce38175a736a38b63001aa96f91071849fb26e1f4293419874c887b0541ba0a84706f56f6793d1119f067a410d679e909e93fdb99b04a42fc7d1bbe873f0a6ddd23f511992c81de5d50a43d105d6db074c03cafbc6905323a0ec9109501c41231e913a9cb5ecb01efc0357c6a24a6286d8833857e19195cb37f5e18148852757b19b3ea136d2b544560513e8094a108ad8b50136d7ea1a5689f748830d234e7d6b4e4b9fd505af622a024ea25c55dcfe2503b095e085957a30b9ee55e79023c85f7a82fb1be5260f9908d63305e4267448fea8efab8c2c6c124b3a1e763b28685280c37d1adb002f680fc17a7e3632dbe18c430f4b799c3a4660faf039ad7e67d724bbb2fb9b10e5dc0ef671094a78aa561b3b55a7f48d64975d9e9affbe91004b50d9d94e2244434702613f71f6a4d2ff96c5ec14b35f04e7d6634aa8df18f7cb354e3410b9273eee0a215bdfca3db61b3961e6efc29e6036a8dcee279d82a32008081cfa4c8b7cab076f34223f8563f8f4406f0cb88c8b5f9e30ff3f37c48eb80814682828dfb77c0129c1ceafcc2da0bf24133b80bade09fb6651fbd68f2872a44d4fb47be30db0006670be7bc4f0f6d3b0cdb31276fe0deb50cd17bdab908d3f931cfb0870dadd0ef87fc22eb156412208e4296f221bab9bd2b9f628207ac5b5e2daa40257892d05d7a32e8b8cfd84db9a2c048b417b474f859ad2110ec0d7cc99a1431224acdf0c102423cd4cae34298f6d5bbe999359bf1641d9b184fc4c65ad363cb3ef8f410c5a696eae469985d0bfbbc0aab3ff609c8e88e6defc0c1a2f73d2b98e93a5ea081890eee8cb87780032722203f865fc149f197f69a597409535b11986a9fb9a06116e557210f7b19b3bd48428b6b5a0bf560e27c196c5c3caa0b2017bd6278807 +generate_ring_signature adcdb7399ebe573a2e88f555775d8df55ceef28b0f51d1256cae8f109668a24c 447ef2e9196b56579e07bf5411b980ed2bff6414024f79852d9d4af70a449870 10 9152fedaec98aba200129eab484df0a6366db49094419265a78ddc2b57380e30 b79be97e80cfdd3ab77c6855acab29c655e3d1a07a6e2b53462aa7b9ef83bcbd 69864e7c079c36ee8698ad1b54f66bfd101bf1c835670beca641f9ed37736f99 ca0520d1c7e07f43082c5a46ad0dbb4aacb97e88d7246a60a8ba3667f2458c9e 053d4ad64725e121493158de54d28826417808669ce746b807d65b1f11839a6f c299d75d2531a842aad42ff488b2b76112caf12bbd500d503f43dfd9708746bc 04dc77ff1925242237f287d9ccee87c27ebd75ac6e3a45c357d9bceecd937f63 ca3895a0d1560e67451efdfd8cafdc838c7f1fbc22a4504323ee47e7436ee49b 3a8185cf5f418483607610c29acc87d2b5b0eb5705a25252dc151d28168f65a5 7ccbafce5134beffb22c2906ab4008a62e784b46bd74ffe5bcb2386a990adcb9 f00cac90c0731f2c2eda3e61a2bbfa99255c22990d697a1008ce4a53d42a8600 3 c34e5cc292e6a45929650b358c9e4e06cdf8720eefcc1652e6e05f7d10fbdd06ab7fe0cf50b4de1cee7995ae250da3af0256ecde371d60098ed174437a2776064fe895c89b8e1cef0964745eb829dde215303abf16bd38d6152b195c89764f06a9443387b5a5a993d8a92afa46f8ad2dd6ecc9c71b0379ac7fe21e6a20163e0ee949ce04e30101f2a4776df1dc04af4a6d4665ad9469a54f2b9fcb948b696a024b6b6e0f1b60e7424b74b0b735e9232678c87274fd931ac18f7fc0064ad82a0c19a469a91a4cca00a4412dd423687ff6bd23410f4c9c2da14101cd249ce28c0467fdf5d519d4e5467e10346430d1d56bf7ff798b26e0810145428ae7621e0604994bea862bae4298b8fcf1bc25cf5f5501d6dd6f1d9aff1a9a642dbe001e45029f0248687977fc591ed17a30e572a019c5c939a892a76cc54be41d9bbf3781061ec3ffa5a58109a1d28896c4f5b2dfcfcebb48ac26452deee2102248ba46e1088ec88ba7a5a015c08d2f9f67b89b43e7f19fb1a6608d86e3094c48196d479d06653e53cfd9e2b415c0e2c39fb7b60ab1f72b6d682aa85981ff6bec05e36e8a0334c707b7c92e0b42aba055030938cb1e60c9c61e24a3ee2568b394da6ef1af0ac20735443c6033777ae285d8e239ec9009c0c1c18412889ea5a064c30359a5032701a66d6cdca2756438765e401205d833e397aab5b68da09ec63d3958994407116e2f18371d2493ee497c8add0e58e3c2a08389b0ff709eb6790a01c16ec40e4f93c285b621db15f45be3acdaa81eb61afa47b4b95017c303f7b1122f38630eb0c5d5694630f5533c8bd88d33c0aaa299f67e7c30ee766565208c428f51560fe55c533ea708f10a98fce6d7924e0c95ad8ab7f32975f2e36aa2bc69cd0ee200 +generate_ring_signature 8603bb45eb1799e1bc1fe6038b19a52bfb9ad7cc9cf44446f164acc3ca40da1d eea6a1e1451c63be7dc890f76883f56b1fe5b41921b4e9a5f7757b92df2b6a49 10 278124100668fd040c4da5aa9dcc1961b3e51c0c1c427757e00938d4584b9a72 1e7124d6beceb0fc37efdfb8e2098b24518f431bf8c0b1d5ebe553402c025b99 73f76c9a7b6cb8d8bc3996ce6fdaa00ef47e3c4c0af2fa6cbc794153d4ee0fc5 59bcb04b3649b9d1c6a697ef8465128d7ffa20ad00d4bc2f3651ea8893e75e17 8a996eeddd61f604a74434deba30fdc66efe9a92ec938471b11f88ded7365510 124691caca477ab72bd7b69ab0e991ed6f6d30e910f34997a8c826e5f9c3e502 e80acfb48cd74ae789a6591ec70de50049fdb9319c8b4cbd814e463113e97c23 4be725c440060881b1dfc58d506b5c16bdfdd539bfcf02ad5dea50748ca0350b 57f42b791c1ae5f5dbf20e303be722ea94fd993241ef302e94da5cb017802ba9 dcb72d6d16b55946296733c9638c60dff04002c51c55234878846bc1b6dd1864 f0dacee794abc5fbf38c017120a19c7e1c65637c65b4103fcb8c148af9aad201 4 f8b64affdb7ece73942627162170aded7a030ce093d1c90f986786156bda4b0a698140b00b2c50b8aa0b7fd5a8aaa2e3aaccb227093cc68d2ac59eceb0ce4d080744629f0b40bc96ddff73b312512b3e3886462fdc2c4efcfde9e74e0eff8f05b0a3ed6de5ec750cb68bef1a99c4dffd2bc3fa90a76496c3e890ec1fb54a1d043481b7d5961827c355e4e27dd79b67f03ce7d92075a368a0716dff52a35bec0bb54f00fcf2f317a7cf26243ec4764a90277d1396a9bf175e4503bad41d34900a954bd362cf87d97282c5662ec5c2c34db7ef9a2707cee69477956ea7ac99c408772cd1f86a4546d3716b7c0019a12398c06422da7f743c51d45bbde7f500af078f56333249798082fd5ba7178a6eb8f42f2a92b3ff781a4821425b96622e21033bcbb343d70594bdf46f722614b615fcd62ae9112ed56078a07d7f4719f1f306c049144c88327388bbe0088fb8af619213eee94c43b79c0a6cc6a330eba89a090a17a2a79a01313e701fc5d89bc5820462baf7233403117644bb167567ca930dce25f0a4dd3fd82f31bb60942cd73e9e49e17fb8e200acf57fec304b7509080f74bfea3a21626125b4b784610900713739222f7e73a2a547b6e26c991dbdfd0622f96f5e516a97a909d983ae811ad66a9a95134295d4765f83860ade5866b409098d5ceebfbf682dc288417b7aa06018f87fcf84bc9657af0289a1f4c99e240822b07d9ece9dd6134e026de189194270547847d85a323e48106aaba573230d0e6305e5674707af1ecd706d61fb3ec9d00adb297c70a7e49c1df9f36702c38b04956a41b2403a02c455691495ac183640746df4b3034182ddbf92c874fcc063011213dc8ad9a4bfd250103e98c73053de4d014ba146e8b92dfdcb0d2c7b49a500 +generate_ring_signature 393bd6d7e3d18366b43c7f64f94c7f3e803c05a357fd4c9ccab41f235545ee23 ad175b4b3efde3db1a37d5860d51d8c1e71bbdef25a0d88f29fdd6ed07309665 4 beb4f6cb5a63cfd76a7e66a78ed35d788947613b90e7bdc25ada761b87ea430a 0e37fe8e05c17b8ff67e4fb79a6849eae9631a41fccecca28618418746d0a9ea 087f54a5745a7fd38e45534fa0a878b582fd7d16e6705e0bd195d9c11ffe9638 3c91a5cb9c704c6f70c36cf483ff5dfb14b45958ee50b244de2f8c60e3931a82 c022e13c7fad4dc5e547e92919763f8276ba20a839ff4790f7423bfdbf60eb03 2 3a72a16440938e6dfb3fd9eb25859abc57b5dad2128a7d835bdfc78b131d840d968bc131d314a50a92cd05748666dce92f899bda29d8d80dd4590d792b42b00277cf912bd58034b9ee25d633da6958e3fbb312d129fccac46602aac8e7a1c202aedfb4a68eccc527813534ac26e1cc4df49281949e7dd8887cde8b4474ce340917cdfe0c21e51218202206c77a30b3a05583767605894b6cae1f21210800700d43fd3160d8140da1812f7a6b1e68c294d915ee30039c4f8c0cd4838812be760923bb6f5db6d4d89a2d8f78395b6961f972a47daf5af9bbfebe75f233e64ae106dd0199b0e92ef32e9b2a8d34e932000f06045674bd3c9a5f48b85ccf894e050d +generate_ring_signature e565abb91e365168b01e67f4a3c568d32789fdc1e6dab2f448e9f38d091a3d4e 9408b7dd8b3cad102645c2011cbfb0edf8b012cfde69948fa41745d9b2e0d375 1 353fd42821e9933571099b5e6cf214b0f20f3b6d012e753818641c618d20defa cbbbb2095f92ef10834a02629b43f7cdb9c44d638c6972b9030d793472a63207 0 090ccce3fb90269f460bf7fd200d55dbdd287c81fbb2e40c8403564e54736c0627389f1840c1ed4e9c063f2c9edadb6fab94ad7180df670f4d0ca30b9dc18402 +generate_ring_signature 7ca2998d7e7f0adc4ce35e5b474e47699d5460663aa16ab449609fbb00d500f8 7fc9c544c3e934782785e9f73fe032d96973111f922034ca48bc825cc27736c7 64 483168d096ddf7c014a148e53be2d680079bdf94ddccd86b087f8b33d357b7fd 86bdeea8a6d0fb1d4c7d5a173d5468a55f6787f612cc997039307ad56f2ec6f3 c60a76b900e4670551e578ad687fba402b25416433d72af5291ed69e630337ea caaacc734c97093c58dd64102a4246b2a5840c1c813c4e338394bbac7dbebc75 1ef3e4e217cce535fc694fd8c6d3e113914f0a519038690ce7324d06e4d1f8be 916111041980474203cd2afa9043cf5d9c3d636fec3498e1f6b06115b668bd1f a09a5afac52945be31fc7a2e7046862c8d4806649359fbf8b5f7c69628b4f167 6b5d1fdc1a962a295746d49938647583ea088de7f8e241497fc6b41bc9121e95 0400eb6d12f71290e0bf2e6af6ea14c37198a7e686b63e0569f6741844f0db28 235285f1e1e1a1bb6daf5820e3f9db2f5ebcf8a3c1dff2a988953d60d77c2c90 847ebd598b288b0052a82a35c58fee0e477556cad2bbf15dc60208ed14712b08 a8a54162f2a6e4fd177b3c727dcd2034643d6a43970f2bb9804cbb5f24366b5c 37fcdd2a3b035e5f5f4951d3ea120dabbdc219d9a6c385d03643a7037eb26af4 c3c68afaa4e7cc9c7cda7569cd3b0e880fc2b39e418383c4f7d6aede362acaee a14729c01c6f8c78c68f4863e384162a863d304e780cf1d87835369d6c0f4c42 ba8de594a2ebd411901147db78dce4c6b5dd35dd2c85ae6ce2e2f48f3e47c122 f9d0351c1c41aedcc6899d3c963c410b3e33510f747c1a3cce8ab1823a3414ac d8f4ac38ce7be2c45d3b74880338bba84e256042ddcdf30ff25089726edbcee9 4b33b840fe9c767bc2fc9b8f989aed3d8012edc993776b1d2476f6542ae15cdf a7b39b9014bde6702c0c7baf60a3245a70f8db5abac0fad30bf98c36d8b201a6 3905a5aa52ca2d61f49e770c53c6663687c2b6565736b51527b8332730d283b5 e9932651c0ae63cf3738fe067908dd46644f1d2235972e027beec99724d013f0 804f17dc20b27060756c20a2e3b1bcbc8fa01db1672c7aed51c20a868b0360bc 138c0ac27cb68eac5dd51b8f75d1c31a215f26b14445bcb3642fd00a176a6639 0f283696b934fbcf7ce30f78f8664d8f28c986a202a0c340b2d7d5782cc7bcee 36415f082045b4c3b1b0f805d2e6ade990723d39db0a53cdd723d10a255d41d6 16a9cec53d35cc2861126070e57d228c20f3350b93ea4b46d8b62d01f3209f20 a5c9112fefba9ad954476e87f670af6dd626898b3953a31cf3ffb57886f10d11 ca10bbb819ce71f840e69d53750dd43adf4cdc2c785e517c7edde54034a3baba b1f8df73868c6f39af5d6a71b16fae7c882cfa335115f62f0b093873c308d06d 557f9033e27f5ffa3268c4a3f3fc94dc73c2e3bc70908888c3644143503796b3 f2cad5e736f9839d99cf515859fb8464fece1f85450d5bd5558ce750e60e2942 9becaea439d210fd173a374e51586d125508c458ffb0eea0257416ae62813a81 a56164a43e06f4da3d2cfec1db2c1fbc2641b9f86dc4869e06e55f0a9ffcf751 c8a1abd87b2837247515c9736804424e235d61d6c41f90e748e72859446633e6 210074f11031e5ce0d2ac8cf51e9fc7981155fd1011745ef1e74b342672b453b 8fae668e0c5e30c77823534bb06c660867896f016907fa8b05f5a18452c7714f 0e1a32d6619800d7c2fd5873d124c917752f058318d43b66b2512aa1667c5151 6803d84d7b5a3574328f3403b2f4a79b5262aa31e6f1c971c179c4ec2c8560ab 44c61ae79c631193c330a66d64dab906514cbfec06c8bdf57d5cdb1844b2f789 4000fa60a283326fdf215ceffc849f6f58cd74b60bf19798cd6e54b6e3d8d06a a78e6293da0c64e26b788e55a0afbd8e4273a226a514cab6e3b4beb8d214c9ea 12980cd87170fc759380afa60290915389ff1929ab5de05f1e8549f014fb2b44 a1b83d3f280f162cc37feafd51e90cf6e1da6c82d25ca9ae12b4acbee46a949f 6cded174a9447758824a50352936a539d97c0977ecde4eb9df83c0244a44b89c e6a9d7f60b9d6f367befcfb0578605aefffe810cd55081232176f23cd4901ba1 13fa490eadd0b74699af3be95e1f5b2e488826a104779877768d5e10a506bdd5 5ee53f2482db49cd3373a9195fe2067e018a081189e9e0ba2500357fee9f11d7 0cec68b56dbdd3f61c5b5f242928af02ac0f1bdffc5461b5855fe58b582f54cc 6c17365304dfcf711494cfeeed75cf5e121ee6b59cd67219edaae7ea07db4250 6e801ef00a31b59ad896ab91c68c67ec24aac24b03436c7013c18df945666875 8326bacb4818132db5ca61d73897c1ec0769d9833b9ab677c42bfb9b177e0f09 2b460dbd272c99ec3ee39938db0555df0f23e8090da8e115a912e15d51aa913e c3cb48550117623033617d274569087b44d1da2dbc0257bbf45f35e3581b536d 110d6abeaf7b20668c3891fc61240bb2c3d67736f5e80361217156a55e142f5b 79e8696c0f50252f81b5f218eac91894ea15a95a48ca68594de8aafcae37d7e1 7308404a054e1005bdf9eddda7a5d0ac0b395efdeb81a655f4b8e8819bdc583a 38239bcf14fe4fdcb14d6943c464ca8cf401cdc46d3d212b4f1ee52161e3b4be 3a8bf4e796f2cd92a78240df064c3520f4966257b89e49ffa2ac6b4f70322a97 b4310494a59ac8bc4ccf06b17f70ddb789b387215fe2b73d180b3dc1779350ed 51275e0ac22a92d506d12e7969e28f060ee7fcdcaf100134af046d622ef7c8f9 d509aa1c71eaf0259e74c41d404c25ed03fbf75f249214b7525570eee1eefda7 388f6ce354823892c1cbe49d7fd79d8c62627308a48291af34e16bca292a6ac9 7831cb8fc935ac1eea198f48f6632475f8feebf686406eb00d43f4054299f8f3 d62eb32ea87d2f0efc686cf9d79d111ee668a65a48c3ae2606bee28d8dc54202 15 a333632662b109c834144757de67974f272b3aba9d1859bc2ef8c1134c63a502760c7379af1bafb7040420eabebbbd57d62386df763ed154f8083607473aac03dc69a24901d3a18d451fee65fe0f7a8164824e50405ee161280a6df87e817b054fabc2dbefed3afe6bed3115e43a7e5df306505b5ada2f1ab5b3187117ef990c83ff703ecb34cdbcb4699e39b286789762ed6d9999715df080a46ee815d9210997a83ef9369fecbef4daa6edc93fee2312b0282f3e31f4d91631dd74341f410a00b4b9900f54f9d7c232a56f2c5f15882b7cc9825d1804b104b34f1bf93810046d3f5e5500220d0b01824b4f849b3a7322ccf25596bd35e245d2d77f0595310f064bc5ad7e380fb7a22f43d2fe0c5361e5bd1ab523c488ac85b293cacbedcf0e21c988ffff0cd27c4c752473d51513364d741f43f3b2d4a6127be86099346f01c37cb274e95f79ab87c4e352e74aeccf91aed2b9eb2d2299fd70fe909f793104f60d22470d0705e2b698ee2185e1109606a0e5ed99c31d25ce85b7024179b50dd6a9d328f3dada191e642697d342337cfbea7d75d36a2bcd2d33aac7d7c35d0c6e82bd855cd1aca16e28a2512d14bc88f70386a215c3864be3949052fb93eb0e9ea6ddab0b83fb4233dde93d5829481e8f5d46fc3c16632eea27b14b5fe5bc0785e6d14be9490af7d2f4aca4de8364995c7c409c10d0a01659773d1b5221bf06386f77f50c1d2c76356c2b726dbba329099f0ae838a96cd5d464fe4a5b4ca901e7e0da04584bf2d484afd1599d4f9b0b568fac7c1a75210dbfc7e060d1c16e0c337d1b66b22a3a6c3f57a437b3cea7e8e5eaf9baf1812b0d771383796ca85f0ba136d301e4607f016eba758272d386c2f5276cb4268763210d1a4c31783fe30f624c783c8d22ad77ffe57ab95cbabc6dc3a3adffbcecc7fd847b37f4ea855b0c8c7da3c84f95e5ddcc6d0ae9443aa6427cf4fdd0d14e4b5c40b44b94145e9a0fa5e4b38b3532d2e1d85d9132ac9be44589f202d879455af8ef5b13e3f1ecb700f619bb90554201c6c11c1526304384a2120f8d4d54e8eb76c20a729ad261930fadad049fdd619f5f17521da59c45516cebddc632d3ac694fe9ced9af2266f20e55711593671b8dc1b26431dfc24ee1fc90fa4cb5b60d35c8ae06f7005af2fc037a6d458b86622353525fdbe333f05c7b760773d28eeee8abe96aeecf51b0e906b4989a676bc0b969da9a84ac6375c28d352a651ffa93dc068591bac94635e50862181ef497cb0df2140ca34294d19f91f3d2718c7869997ee00463d6a6a2f4031543d42df3eda9e21691b16ae19ac1c74a61da42eb8e59d5d642a7b471c5be029a0df5034099acc6da6ed818f28bf6aca8c75cd5fb755f54d557bd2d5757f20e934de129941549b7ba8b2a862d12d47f760b8009dae3eb23ed360194f540fc0be64f74cce7fbec163a29b34f0f08d4919b2f44ef9ae9aacf6c5d302a94fc440983ef2f059d0522caa3687373db330eab29cc72b82d72a4de2f2c593adce83502558b38226979c21e02b089b811f6622b72e379b3f7dd4546649fe8389b830b0736a2f9622422d62a5ae128162e05f62db8f38a33012d4c74481c02a0bb15610a3b7fb827bec033d8876dbb9d7ff72de78cad1c748008aeb1337e595eae13c20d26435bba69cfd5db7c25c18434dd251d2a27116d992022069385aa22584d47059b3bb38580429fc06ad2149ba9cbfb7d863b0d6657a1256c4c87e157443eec0d3ceb35d3e10666b4a18547fbdba0dc9365f584c364678c0207d32895f8903a0636a195be0d094a41d5d3507f11f1c815ca1cef7ce1a36100a8a8a9471d02f605e51c46fd906e4e76b207d71c3719660122f71d9ec1cfeb4960f139cd9a08f1011081b8cfeebcd41b353b9c08cf41065bc237a1f1232197926c09eef344a45c0d38910b056b455c149fad535e7c59e1389626c34174debd7f2db12ac45a93ce01eb8ded0a128780323f3054e2f1f2192a8ce55b447e5b9e1617c4b15148a05203ffefec27fb7cbe277e5632bff7a4f0437ff13c458fabd92e9cfdc01bddddc909a5d15510fda8a9224663746e1c4c366a7a270afaa3c54c090fa7e60e988ec70665fd9d8a09547c16ae57c688924add0840e6ac44128c08923fd5d57c3769f80923114ee0dff44b4b388402d581cb11af854053c667784465cbfb9f56133c4109824daf9338127f0152d659e61aeec034eb2c0247c486acaddecac0136979b30f7188637d847995cae9a41398c2c8986e8552b02841136d043b40d6828503440645a89b54af132ebb3c35e77289a8efc81c45bb54cef7366ba08e20f6db1fcd0871008bd048a0c311f104f350a1be8cf926164550b54de6cd9297eddae6172e018dac19ffb240e415a98d106544f2f1fe39ad079df179f79b4f93fbe644062c062593b29506cba09e5499fde8fe7de847c2b07f218086674f157d9fb52e7313046f9d6c8991150a8d1ccf7a77021a84c877566096261fad8f616a411684be9002b38070b0d557dfc107b728294b4d30b8e83bb907b50628c8b4282cbde0314f0f15ac98e382257c5eaf2086e588725d27f0ccc6956d46710ecd425274276c360281a6ed410a83ad45584ca08285a6ce01196d8e8a745d0ffa084a2c3a64490308fb8761db21acf32596291fc4c88c68604542d3b0b3863a53bc5327631c5af10887d68802ec663ef019538437c3a460ccbb17ddcabc9ab16bef28079e39c94b0b00295b9ebbea216810fc2b81c0a508294ced8e679270645e01c7acea8c528f0373fe48957e5e1c1ee6c29b255d36d8a70a9947c4bf5856889287f93af6b6bb0b70f65a31e00f547f218dc194690fee18c6487badc8adc9b29bb87d951a194a09e7b281b0f644b20b1ce8603ab8e7b2a9a08ddbeb9d7029c0922d3984875e3a03b4a654a21cb4670ad5417fdfabe083a6c514d2543604fae8cf0805f3025f73088225315c5a55202c1538a420509134caa7ca8b8c1efca3f9c7120a7ca675210a3e7aa7dde18781f1f749a1ec811aa4e9161faca2a0841f63b3eb3f1ee653000916f287ee81c0398b62ae7adef993e8c5041ef803b1ca8549bfbcc1246c6b000b9e6b4ce81c128f04a95f7e13069b576a62cef7056b0c493e8bed5a6ae9491304c7bbb98886a9043564f07ca460f7928a4105223e89d45ba5818246c752803e044b9802c16b6998b582d56c34f783673958d8cc918a5aa9c5c5915aa30c647801dd523b0fbe07e1b5c7d53a2f341e942c6a368eee64f6e2e0d4d3f7c75893bf04c4093d346155491f6e8c8b89eb55caf64a076cc6ea8085f9c7e7a5ed6c419609d22b80e81a3a2a943319cc58e32e27906c2dcaff6040730338d558d4aedcd509ff25243e4b54527a130553b959acf977c6eb9b389d4233203d67a0a12d1ca10fb54823621ebba093cff9d029916d9c4ba6fa1eded597725708428d7b12586e0f1fa030222224dca5380356cb7ca4c290c13f3f1c779734310164f393e50121036cb61b53e2d32a5bd21a2574c19a1cda83db8c940ba7e1753035afd7e1f7900667948b6abfbffe8a33d3a542aac78c651a1e59665bdbaf32a53f39e136de3b0107dd52481b55eca80a102c57e8a7df29e2f07dfd16eb079e9c2970e1df447204110efa43bccfce4b95dff75e00df256c112ff419a99355774e3efc7b48eea400fba35a8555c0da7486bed9069b0aea8d9e1aaf9130af2588255d5f8c88480d08052f4577645d83f1e845055d2a9cf7c84ce40fe2fe6ac1dc34ed323b3d185403f84de40cb52a01813be3b3783662ee51c07c7e49829e8d15f553c2d56a960f06478cfd28f06379f02111dcf2167073b20f88a4b80c4fbe24844f4e4748e70b0816639950a6c55e50e17b8ee0f507f15bb4ec5c7a7eb81cf40bee7e248fd8d30b5f7480e9ad4b5010d129995706e427b62030dd41dfcbdb256e7043aefa04f5052335b5c2cd550c4ab1dcd9b263768052f1f2a72fc2f5286f1299861278744e0afc41fb83d88fc713b1837ad301da8a0b8f812e0ee2320d278fc42273ab8b7906909f79d7b3cbadd9bfd7537c154a5db6ee8c50f95b6fe85eca0a9dbeaf50850fd86c21abc867919520deb226bb4f3f07ba445cbcb1e0796fe674ecae3c54c5032c8b5c34154bfd064aed8e986c9a94f8aabb5ae6d9ce2fff397a61dbccc52107e7690c850c50bd04017afb9c3abb073ed9a6dc557b3bc132a0ff03f7161fef06939a11e827c067e5a75fe793b3706ce75bb08c94d116ad8a74777ff4200a7f019b1439dad0fb9cb761393e919084dc1feeab33fc3567366f295c1ac295710f0b435b78a13b79b0b2cbb7c76803a445393671f1d5611fe5deba74afb262bcd80e63c779f2a35685e8d3e716761a27b53730664ae360bacc0243826315fd6ef807826bde8620f6529f8c603f981f8bde830f5865ad7cfe1b437124607a1d2eda018b6edabcb9ca579d6c47cb69837bbb7c84e57118434fe4ab78147b683686fd08fd9607303565e8b3b9354999917ef1ff0863f73212e8fb93ab70514aa521ae0dc659c43aa1bcb919680a149b5644067e403a699831474be553837228348f5f0ea65c06f4a305701962fa34167218a1b615045997b4572c6428a3eb86a1a3580629e100f493883ba229718447a3ee62b13948c0963458acfe95daa9f7ae78580fc496151c936d9549074bf049d6caa62e34130dae14af4799c844d3eb58a97007854ce2825cadfb50c5b46117fb305747af7a2bff971aeca767aee869558f55012b46a8c332aad6410320e09202e3f77a7548a3a896f3a8f9b08af406015b7b0fb7a4d2bd5cf904326c03650e9d4b6658fb8f9eb048cd6b64e462c4c51fa4ae003ab6c7a3a9c2f1bf8b4682f589890ace121c6d245eedad0e52330924e4b73404567949fb220dac572da95359022b0c0f4462a3b979d5e95ea8359a45cd40dd04d5d83155c1891333e6fdaa65da29ff050c4c3321d2ba039300e2ed5e77cd230aeb7eb38d4c6b8448ee303601a85f6179c5cb166fb668057073982c33cebb0d0b9f8fcd338abaec4bc0969727b4707a92b105fef3ce1135616bbf1b02b70eda06fad46d17522dff516de9ab3aec2341ffec1cb1920d75e168e13db8b8c12e3109561f6c4d3daa129bb3f63d0a0b14fb29d4354ceb039e5f81175161572d3a5b099d744a8808bab5ef03bfc77cf7ccc28b0a6082794d43b2f7166ab55fe060a6013b5b4c3fd50e374c96344245141520861e8925f782691565ad55b4a12546d20dcf026a39a9550d41d8293103068bd890c9c379ff780591b18d48c90091f05e07eb783b7216f08dc6e3069fc5396b3e4dede2b2c8e6f5e2293033ab011f672e0f11bf67f011d7ab70ba2bc2e404d69d924d7ec4a2efd01040c46fb62484ce04000316cb029af783d17f4fba05c762562d6eaab4f239ccd2971c9fc14d19fdf307ff92183dcb74caf6a7a8332dd851e602a2aa0d32bf9ba0048a265da63c1e7d0331fc0d5a30fd339ed82fe677706d886641935399922fd7fe721c3f29c955e10b3a3cbd728b15e5ebb6e61c69c79e228db75b84fbfadc91c73eba6f81a2b734031774979b2d72e7c0f41d22340e370d7ae24e2e8cc893688122d5f6305b485b0161adca3722e75560941c848b03eb96d14c4c5a3782c8cf9a361cdf02b73d070fc5e3465ce8067c827b4738e8c2ec7bd328a5117c5a917db0e8eff06c3d5af905048b9551ce371d26d48af1d8f7577377984948822e6fbb212165cef59568c607 +generate_ring_signature 4e6823185a8771ea63ca39e1aa4dbb7578a6fba3da566cea485c82b3c0efc115 0f0ffd85c0de4993b1c69e5b0c21a3811b49f6c0f4097a81e33448c1149afaef 2 cad1c5a19a24b36c12ba2812652309ba4234f04ca20812ef2d76405d7740afe2 4ff3063f234d5d7b4717b2c2b9e4c09966f69f1c1083099420e37baf84d8c9ba 071ae9ca27b1072d58abfc39b4bf143d61845202748c25189525172e1d081b0e 0 a99e4e3910315f33c42dd531f1951d5a36776284f55bec8714a6c6535bb28a07093aaf0ad3fc8d9c9d090e00e51edbafddead4f56f9b6ccc19b477b91daea306846c639bb01eb2bd1355339693445a147ebb57dfa9a8f3bb17e00201c1a8f70a80762b98f6e108ffff98800aec6d9f3cc1ca98d9af0e4615d37387b3bd2b6708 +generate_ring_signature 91cbc9e360956a6ea91cb542dc70d6cec885b8e2a1b763a3e352389041be1aa4 077849a2880ed750d786a1f1f0a17cc01c256f896373c465779e5154b3f7d955 113 1f4bdae2817d67f27dc0ceaf976717df975fb8353daf7fa01fff17ae37770146 fdd656b891d9e758069f81b2b2daf433fc84b3dc8eeefd2d295b7f5ab9d75c92 0859b4fd8c367923f1061c416b7e8e9ebb99e3829e50d8d21fa659ca4078330f 7e2da8ad81daf87ae03a1f42ccad8d8f3c8e1d4f75dd89416119b1c71e8a2416 c32fdedfa2464148a62c7630da8f598ff39f13628b221142f7f2d5b61fd5efaf 76737534abb4dfdc6ba72c40f3b406f9cec3a51fba44b56284fc62db05394e79 e4e6d4b71a8e963a256a300e4ecd265dadfc6ffb1fb7c46a68b9badf18e3e4d0 b4ce84dd04dc6ccc4b85eee0925825b4436237ec68b324f66f5f9684de4f66ba b1c379e91b9b72abdf75c4d25174bf029926cfb3498beb075586c9ae3cc105e7 2267492b2a3131012bdf68b84d681f03824aafbba96b2b9735343abbd74b8cb6 00ae5b1f0c470c254facb1b12ba8ac6a31191cdd01709d2ee6b37c1caee74cd5 bd859849cad2bb97e2da3908dd8b90578455599705674fc0016ff46c3549fea7 bb21060aae8d8503dd930512d7a080a6828223b5fcabda0c161ec3ecaae3fefc 66bb5f6b1c081e0393d2224ea522cf385115dc03b1970c2e7e3a5c72d8e7bc50 33cc10d158229e8603dc7895a81a8b6c55edecf6f43a506625074261d402fdc9 43a36814c2c72f6a273fac7094f9b00124e0bbd332b9ee61ca41ea231b018816 1d7f55694bfd332d08164d331fd276e56d365f2a7c5fcd3824c77a6809b65385 c2eefbf1779a3dc88c493b6512ac8d2393b2a65f195681baa41bb05e2e58f689 981ed644db662419628670953465ebe2ac3997ebb4d8292b24d2ddff64fdcd86 123dd138e0f62fad60aa9656d484aec34ad87fa623b69ca4f77f36b5b903d71d 29976e6ae7245518b0565907725a15e1445bd1c0730b2c98433c47e13f34e4ad 541da01c43ea8445433cf70817e7fccefa13245c1d6ff34ada5324ed8dad3d04 82a982aae68c079dedcd3455d266bcef67e4d39028afa0df883dfdb4b36ce979 7561dd51d46d617281a314a4e112228a6abc1ec8ae26f59f5d691cccbd96663e afa6f7640f981e3d4ed2b85b74151061aed19ff5390c0ab3791cccc1a21ede59 54c84b57a8749841027cdb4555f8a8c03dd9763957d801efe96096ce6a58a1ee d4186b3f8ab4c4fb9256161f632c9fbae63912909c22636357b001ff34486de3 e845c35b7b319a99099f122f0acea3e39e82bf536c9dc0a7fe5875d6b0739c74 faf9769154330257db19465d7ae0f2f211f730e4c83220c48d7648596e91aba5 3bd9d7b85fc7ae7d7bece942d0ead33b882d0c35d96d491eaebcd6fce5f345da 1cdbd85712849004e3e4069802d7387ef46a4816ffb84fc626d5c1b9b7a60a23 d284ec26e2df785de3542990946152a736f729a787913dafe58f8745b0d263db d23bd5efd82b47c8d982ed9e1b4dedd39a3ee3badfee703dcc33d3c32b2075d0 f1eb248b21f1a5abe42327d1f203121a0a113fb1cfd61281e639aca1a15798a7 dc124febbf2b8b75ad19408ba905ec2660f8641608048d4053b137c18118285f ff0a4193b301b36562d8c1ffab7bbb9c62834b0116a48b992a85c1ebea6b0a81 8ebd75cea0d1f657d506b3cbdb855a9db21f87d8e0cbef42849383f093b7eaa0 9e254d83820a33fd0971a3c7efe2ca64c35a3d5d50ac3884bbdf7d6f77d7dd5f d2a69bb8e79306446c6b2cda1ad9c3d1bb571720b43e55491abb505b6aed4f6d b60e0fb3e1013aa0c1d196cb9d2fce8c1f201c0a013e6634fde756f6023c36a7 262e4efc7730fa981014d1567de9d429eda860bfee22ede1c9941ef94aaf2159 8a40d305e04999c66638cfb169b2a991c0995c814ad978eb9f1e951bb3465c40 088739da61d37ad97d467ec8bd7ca4139b311f7f8b6cea6469aa4e8a4972ba51 6874ee85e75171ed81a59ee5030a8f5af72373719a8faec2680c00af75fbe0ee 0ec37865399810603eeb33048982fd83b25e9e1fcbeaa91c0ed06fccc47c3f80 5ea898f527a48bf7a4fddf87f26d13637557b6214da4eb1b0e4e0495111f9f36 b1dae8a393bf959ccf33a86ce48214f4250458661892fd8bcbcbed768f792548 25a26575e7b663bc898396ed9ccb961e9bcb00d107f0662edba72d2697a78d4d 927ac8a2052c2635eabe2602f67d1e024ff0f04dda5a09a1abe3475cff9b4f78 ef6c5437357e0b31c6a3ddc3474ce246f49a669ce06acc00232a4cb170e0b7ac fa6ec2d86e6f12a008a2415f6bb6d62d8f4530b7ee264e6335421b464033b6f1 77027c9a7b41d51ec9851dd80e52221d541330c4aa65ae9604bb924874dce56b 6229ab07ca58513e9a5fc1db57e4c1bef34e4007206e440b26707594b3cf88c7 137f3bea2550d2307d8a733cd7ccc1c6f80fd9b4f307b75583193c826805588d 48f0e380c36fa89f755054572914efcc6ccb415d77348416a33d5512f8376380 4f5e4429027959682771f088b7101e1cb07d0183052627e44191ab65a4d11e98 d40ec8d941cc48b7edf706b778d736183e05856d03fd929adac4eebf3268cb68 8bfda414414ad854211292ba745ee96f200c80e3623506fb8525f20fb2271fc3 1a615b1e5534445656037b118b8b7bd13ae02ae23436b4ee338d92d37c3d4896 9bc163d094d4f87154cf3593c1edb359a903dfe7509524d9335623a29c656663 eac712841f6694776756f6a89d043bb062861a4378cfb13af76d301c71cbc6ed 7ef33bfc75e86390bc080acbe35bbbad5ddc51da165009bd0daa10db962ba996 054e6574ef55c9f9e94207ec107352a0dccc240ef20c033dd6d1182f7f62909b c32880fa3ce928700ac24530ead3571072e12b03a49b26ef6b3d35d0f4c59d53 b98d0748298b80029c901eb4ec4cd8a436e83c7e01f440c786fd16e15107ac49 ebb0e6eacbc9c5f35484faf27ecacac6cc694ef4b09d807029ae6f510efdcda0 b490c1cb481c3ae08a73f63a1102b316d286d93852adf5648cb0dcb6818126d0 d4f4da3f84eec9c39a4c7cfe27a3bcd39cb9f291005614f1487e0f45a4f1ec00 aebf45b322432696c7b67d3cfc0aee50ff8e40c51ce8cae8a626812449d3a54d f606c7fa4e43f558fdcd80fe7a73dcbb249056b368f5a44f3dbcb9324b503c34 106f85af18b3197ad3351fa8fd8425d260e7aa0c05918af757bbd3361bfd69d8 32256ba58423e1ea10dd4f501a5d238f31499624872406fe500f498e66d008b4 d292d6bd8bf7432aaf9c61d7dc96c73f0dae8132673e85fd3aac5fac1a93431e 52b24f1118795f20e46b90c1b047ee5ee2d3652ab2bce842a9cec609fa62aefa 449fe393def04380cea7cc4c3db08cebf0482f2ff57127ffcd987d92c3fa6163 7a8d3c4d6eb1516f2fe284e496574b3f189b5015a489fc50e54a9f414dcc1b0b b39077b6c733c1de5db7ff8fb3873f024b40fca72f9cac1fc08af28b18e1b932 0b203cae9dada2034024effca44f99b52a36ed00a2f4b67f03a4e0ef5b7c5dcb bb56bf108fb574114fc98c31e713ddc445cbbeab6b01d4e568dff3bd169fbe2a 059819ee8457141574c6dda4edeecda5e62993b800bb2148b16529bf6c0a7d13 79688125c0e6b72e56ea7872416735714947f586635bea64120945fa37cc59e4 5adb1c6cc2ad2431222dd58e8a6f3571063b3ef20bb123e3a1071fcc60ecc9b1 cc3dc8f1ffec0e5f45293b94a5a7bf3ae58a4d4f572a9f5ba8ec3b4f34138369 3999f2af3d71f38b14e98dc84e2530f037e83e7394bced0b6d681ac73029e563 67652807e4ed16e014757a70974b4f8db9e6f6bfae39fd0614ad1f06e525c081 d3813e43348dd6866a69fe1755162f250e8e3de00b7ff0060cf6475d2445836d 438dc90ded371c003c1038b170b925cecb796c3074ccbebcb98e3168af4d39c9 740cdd1999f72c2a29922d6ea744085a4d9f104636ea931643943ffe673fb15e 585fbe2386bebe0fa46eca5ac524a3cf4665498c01929b98854634b0a0d113c3 de3706dcad8c44c755bd56ec422c99168fb5f8683866a13d7cf8c2145a49ba38 da4157d8f82d2a54744daed564c987c996120211b30d482fc0bed011e8f94fd9 721a665b9b6194d077108c0797031347aa6b2d6183812e890ead8f7ede04fa99 67cd0f8158601c13c2f85d32fca57f324985a20c4766cca265693c77d87d5143 9e8803213f74d6e5869fb505e8309245889a807a48327769b8476f16992a8198 eb535a0a39ee12899ad81b8327bb17194757e760e134a0bd34a38ca32b7fc9cd 1a94815e92a49195b30d5c6d08c6011f615a876d9f84562d13e20c8796bbf944 ac695022107503164727535604598ba50cfe2c8d2555345948521d94851caf8c 4819c71ab8e12d613df31c3e4302178dafdc21ae33baf2ee3a9f2b161aadd640 c5b8a211af4dc95ca5ddb7554270215cbdec87c34dde54c484e1bf8ed7a5500e 375f63727c1dedae8a51f8424156f97f9ee3b54301423c8be049492638476587 cbe6d761149db8e9231283d7d3e27b6bb107068127e49ab0ea318ae47a52d941 d78379ac13a4e4e05a8ff9cf451d22fcc16d17bf4b06f66c55f8da8584b08fab 6edfaf19c5dc75df69facf72eb82bc2c016b2650f32c4afb731f7f3fdcdadaff 44d3fe29429f69fec91ae3e2e33b227fdc00a8ab78e3510b2bf03322f20683a1 a859389fd668d9b8a8a5fcbd3ad6c49c751032078462fdf6aac4e5600b8bd37e 6808fb004f96d638abd7d164167fff230265214723a7b65344011667814ef5c1 6c3654047c65a6312b5f785a9246e064f1d223913b7e11db00bba27b8d6e728f f1383ffe364151b77625632904f34bda76a33df6561c30d591c30cc492b0fc41 6cabd4505bd1a98ff5f7c1c75d7d4e328d691510f4a23ab74098c3b54a08ac03 c4818a93d8e531cd4534694bf915b7bd5d65baf66976fea183e3df1ba69b65ed 4ed680ad71299f0ee843625af25736e020ed7bcf82ee266c0e34537b1cbbecdb c8a92d1bf5112f1781f2a7a528b196a3bf4ad426a48c30174be6ded892be8be5 6ad5557af5108ef9b99735168c733031ca356c49e9a4cd26bc6f3a332d9ec219 5c0ecde6ffe8646985d4552fc134f6d013b984474f29e62d6626dc941c23cb0a 31 6385a2b5033b9b43d35d5efb16e3b6840fc969452b509aae79a4a3e943bfbc0b7f1b4a1568a8f259400fc897ad6e4a2784d709a394d44e0920a0c44956330f03aad95900d0e6a4a847b2fe2f0bcfd1691ea0dd1e9c07f951e0327789e0ebaf076c4d206e1a46cf10afc929d4a69e7869ee1520b5e5eec5ac1eb23393615bff0ddca0780b25ca9562a6bf8bfebe04210a365afb51189c397fe83d63ccb4d2860ba6b43534eff3cb34daa96981c0ac86644bc9290ea47032c7b560f5f390acdc0dfc7d81dcc4658febe025e5cf95f5f2dbb8d7399279189346a75ba5bf4d6334010883701d18c54fe5e01338e2d6711ba9c29b4e146f69012f5200f88ebbf7840dca4f1b7bfd71f7e666ad17152b46e5cde06f929c5abe28db8cd2204d648e8800dd4a0c2a67cbca4ba6a61e25f97bee017460fb7f47c738bece161b9eae3a960ca05638b0ac6b4e746c2ac043c3499d6d523a1aebe5177b0018f54140cb8cda0d6d83b9220e2e809b1d8f9cbf5d5553a84de9ae86078614a0f574cbbc009b100a9794309b25dd619c98874f1ff133d8e6ca66bf09608c827b10fd6e42b94e4d022ed1bcf20be54f06f90e9906c1a2e0f1c22a4155d40a1adfb6caad724a9a24013ffb86711c3a195ad78f1127c7681b74b77ce99e66cf275536f4dc844a46590209f51d62433521f949cf143cac1b8148be44eca20e52b7c92a26d664af842c04e201cb7cb30c07b219853f75ecb47b579bfa7ae3f94c7550eac063e928db070f8370f06de67b5fabb73096ddf3da41d0c6f9b961797f3c791214c3fa234b5905c36488531b0ca89912a16f1f283f431789ec12691f409e06d862a5b8c47e2e03cc8dbd70b23379531a87cee403a6181789062912c6c302333c477644d987b20221768c39beb0e6453dff36875f26baa630ccac287bbe4a1fe86491b43d5a7b093e42003ecd73751c39e1a0aa4eb34a40cb635c4a6596783abf109ebc0680f10f83126ad88122f44223ffc737238f5040d6eb7d739919be413d7e2f82fc6e7e0abe29291ba715a2fd0c258275b77e050adb14f4e0585b8ed0c7e0359ce85c9805d604d03521dca95faed1b123fadbd90fb2c8d9007b8de02e1f519758aedc9c08ff5cf64251d6c7f6e7264288294ba4313d081152979a54fe5d1493295222430bd20133342177957d7626014b57ebeab7300faf53a10731e9de7a4e0372b1860446a26d53ce5a0a04bf18870dabb872d4df5ad06149a0f50917e2113f4adb520f6adc81be64c0e06dc615dd28e2afaf50a8f99bd4ad23025d065998ee4dfbe20f428c3109d77a5dbbac52f4c609c8c9a6da00ab052bbd8a4b901acc990426160ecbba9cd03a35da8d39649722acc5e9049c06ae93e81a6cfee7410dca76ffbf0ecac5fb11654925292d7d339618ee4e6783d3cdc16566f0f8c1d15f9ae00ec20d9b97e252f785c71929c5c9258a0ffcad21bfcd6cbddb5157dce90acbc50b2d07c0ea7700280055e65d1fb56edc5e6615d31cb0ef56f103a417d4025d225a8f056defc8a5ab783a47266607de5e78604ac3baaa88d89cc8ff05d0763b85bd8b0f177917e985c14b51f6e33aeb97cce5828250b4834d5df42fab0b3ad5fa3b7706140c130e40f26e8f0b07448ccdfa2c7d012d3c0dd267f0da542ab9413f614c011bacd85de612ed8d475fd0c45d40dbbb6ba123f20402933e5c61d83c07fd5104bf08de016c5239eddcb1cc728e2d685ff28ddcdcd0ebd7eb1c2e69ecf6736a0c653a044a8505cbbb42fab5ae6911bd42af399abbb9b77e3554bd2dc2df08930b0a3d835eba3770038e5f2931b003429646a14b97648a2a951e430a7ee4a0fa032663abc7746927e9a14a5605cf7f883f409c7dcefcea97d45204a1c5e8bb010e1817e4faca2d6519b9316836379747267d840a750eed44ce1e377440500c4505b5e08756ebcb79c22b3617a05cf278f8bee3c06b10741ceb29689c079844c0038565c6a96e5a9572afca53fa0ae82af872d901ece8e51c394a39fa7eb9806e0607c670426bb1474ac60bd1ffc6c841a00b2530dcb7aefe58ac4bd84953772f0adaf7a65f0bd15bd6ce9a94085174fd75c6a7c58787fe33132b52ae46174bd4007a65e14fa380b8ef26f5afbce7b3464625515fc72d7084d44046388bbe91a2036ec51078daabbbd7eb8c34ad4ffe772e1560f1ecf498b5198a594d7924d313089f3735fa05ffbd72e1113cb5ff1fb5e7d534677eaf624b1de444ddf8d969f60dd98af763bcf7f001dbae6d81a01895d7a51d097982bf4b4ea70ea5d60bccae0ebb7b1e9aa423dbe0387cae5fc23d56975e1a095e956004ac195257b0ac7b6c0c72ed294963552aed3d8ee825cc1ee1b14fbb367ad5d74c14f170bd59dcfcd107f1f97cd7f9830f8ff29ef0f7c473af62a650997a18ae31570756d7a3f1f02b09505e23944772c0c211807a2f0aea7d7b1e6bbc48e124a4de74b7dfe52d41680af7ddd077ecb1211d28385c9f439cd0eedb3c1d22b4ad2e9aea3fa0a6e46fa80978068aebf2544a24f8a8d7d42b60d40963af6c711984811b9ea15121c0c0e10e81b67134c7892b2b9ff906578f4d9b15e548751591d2900b6972d691aa027402a4ecbfcef7d92d97c25eebbf613bd7d488afa4fb86ad1a52df12408651aaf8088a5dd7fa8dffdc830378fa77a5f0b2494ff14ff715b1662d2c2d2b3e41ff75021e4534a8ee6eed9ce7bd4316264aee34586883227a3e49f8615efdce701b3a0c04358c5f7ff5edf26fb2aa906ad1785ab2e3acf3be54e23313995dc74a70350a76c4bc19df287dafe2d7f257c56ac3836e5ec3b27a1e9bf1520fc9cd634d2b08b1c2bf5a4511b6ee79d7f66ed7623627eb57929ddc507275bc6411a12dbc1308dfb48b8699ba0ed77313a30101bd66f732d8da39b6b7bd276ab6bcea9fe04306fe06d14152b8979775699990eed5bca3e5c95ce916dc22e279318568b9fdfc03e9abb7cb1a65b6ffa1c8fcdf03a264487df8e63efb32742c5e26a3eb66005c0834bd512c14f3e4898888070a065814595f19af5e5811c784dc78280c28d96a0a88aa65445d7675559d5ea59913f4f8f622bc8a15c2b4a1f74454b8df39f7540b4200397ec2d18a52bc22f78e80481044b037bd3c7d58f066f63e9144283a2905a65c57cdd8a3e3fe2581c7ec4a013e3c9bf44c2adfacd661a6fc2ce4b0c0940ffed569d437e4baee3e228484c28109bdd878a402964b226a446fe3d5fbf7af047ab52cd941a8230d73ec0d718117d6a5b7ee9ccf75e37f7e91f6ec218e66350e1d979a297ca386fde3edb6339e58c5ca546312012542857ddc2fd88c8c506906ea906f293f7c2491fe1f418b6f51d6b868e13e85f6d603cd48a79f1a27c4b402b337eb2a5820c54e7437f06facf1c4300d2ad086c4f84befc05e9d69a653710535078205a48ed49bd8bb063b5da10393fd501a6a1fe3d07e8b86ddf1fa11d00a17cca8c0d1775bfc56229e45fedc1723b9547cb2b296980b2dd42b58e1b39c09520064634c52ceec4e7e1ba80eb2ae8e3c4d51fc47785d9f382982347ede500ea2ea308c834bfab1068a7798810c29b5f2e5221e68160a6905424ebf4fb8b7037130eddf2d07014921b3d6d8db50f0868fba03fb4dbd724484994885e67c3a02e7145b0e55f57afde954094db28c9c6760009d629aa234e3ae0a7ff98579e30182513d104f668545bbab88c95832c2ae20f6b6aa7ccaa3cf99764ac3065afc0e588f4fd18582ea7252fce172cbc15b8153ddc538a4bd9692cbcba1f2f3eacc0e4103e4b16b7ab718b2f6e98368db669e9d9f36cdaa0c77fc78ba4885074b830318828d27c0d297023d4b4584b52055278d11cf4d4e948ef04bd4256ec7eacd09676d04ecf25a0d090a3b0bf930ab97b510013327b8b2612626648181ec2d5b0eeba57a690de924fd8c3a7a399a2c1c5fd6a8dd693e754e0e2e581deb2b92a708cec9a2afd01a110b2b22766d21a8fb3e7f7b751cd8e5e40a149895a89cf6920fe5d2c620a8eec7be1b57db4ff09c5ce1951dde409421ab713673d28439ba3e07bd8cfb78d21198edc9721b78d2adc7f566da02ed9595584c58dea46dc0f006066e30ac7ff056c7e2df7ac94929fcc05db096043d1ecafa6a45b5d8ded06d1605a8d16c8c27028d5ed28d0e0352291145eec9965da61da9acee89720962d5260ab1c08f8dce882b3168ba3f0928204899338d7228fa61b6faf2211243a186d90819edd992e132e347a81b396cfd302fed80c2d24ae7a06cdefc243d7435790a0ab972a25926cdf6a72f3e60631565aa9d347a64c6a0200b2550aa4e59f3b52d01f464aae7c1f2d3e93105cfb6d8beeeb384992a16894dadd5f003787a1ce11f0adc3ca244e4303d6b95fbd0ce0ae7f397b7c4b0110b3fecc50b687d08ce2d4c046383fb09144e9c9f171e2a17333cd6960ded59aa9325f199f953e5e8dd1c1e06f16ee645c56bc312ae0a348ab802eff03547a0292ec73aa45dc2583bca96ed0dc6f493fc33378d886c3dfafd314ec2000f8cfb657b706a0356f8957b6acf11055b9be40b2ddcc8ac81fd8a3fb440c2cbf67b61296a2428741c140736cc770704e96c3fabffa9aafd1d5d8540431c5312379859a839c23f429ef1122e283793033de9b75fee2e685590e4e4149f50040b736b049d07b3a8801545a41fd1b0140a12cf6c833ea3f6ca3065e9ef492a0960e54fa5ee4ab1cac89d26b406f2b2ba0ff2a34ca1dd3b9c16a7faacb534bfd06079ed992d8bf11cd9150eb5872ef5c40087f6de64cf9c491a9d7d96cb43f0fe96edee841cc30c163e2e1ac225faa9cf08c72f1769a780ef7a314055d2d93ac671c7281470d8517c9179e8ab4577844a05db273f000e91c8cd2df555159d6d1922caf25954a673c05262408fd4612f410a7d7e1e9a5c66b57869f612b28193ea1c95cff6b6dbd438581adda3f3f8b097015a6fd6642b0f1903dbf3bde8e901d432ea546ff95a2977441a221670a2f1e30466399271f64417efe20e1100a91f5982cdb91a2f4c43128ea12ebd541d9d1d051923905fc96c3e42aaa4b0b27779f9919a7966ec9122fe7dd4c5107b8b5e230491530628d82940be870f4267438d02417fade252023ceef3a605635316c239079ce256d52a662f0f581fdca98521bb99dbe83082b7f60cd73877eb13538ef80bad30de72c4c58f7105b9c19947e4db119fa0481bc82e5bf068f152a74270830f5ca7afd1534990f6a3bd920b1ac84c4b24ae08ac57000ce59a7d4d3dedd1310e77bf6afb2338563039be41bfc237e645fb6d22d62db7b37334aa1c291e876b0cd5b8288e339552c5591554c0aaf45301d99fbbf8bbb1c5cb97e2ab361468390d9eafd01110e51a1e22bf72c64513c985a59e36e614b3a1bd18672e4307b6260b4d79538e20dac5d50c40617608ff0f5e205e1035707af5f55236d8afff0cdd0b0061023637570926a2e58b4fd08bbe0fe32b6c403f837d5affe5d995644b1209c71939fff7e2f89f6cb8443403d7a3cf46b683498f6cc7c216609caf627f7307127c816036cb239c856f5e33c9ea8ebd4a40fd5b1c2d4bc6ef8c1c334ae54702002e5cf28a5c64819bdf516eed479adcba165abd00ee032df3be280aa19bfc0c515c9e6e17709d2955fffffdbb1c0a17dd5f952517222f2f6cf6fa5f804e2603137f674e2e6261d6d9a9d57f2d1270dc49967a3718774fe8d65c564aa0ba8c05b4b579bff9c9181ae9d03f7604d31e87914cdc4bc9029acd943300fb4330bc027b25491669aa99250578874fc4159bf839a2e1d5bbe19047032a8e40902b110f3de1f863fe026b7c38f8442eb29f72f151ea6e1d4e885d3640c2333c731764089c17838fd51a9a8c6124ece27e55983937a3ed94d9c4446b6b2fdc5d17621a03ae2d322b3509b7df40bab4afe9103ef4a8a73345b2b959f0d54bfeb1fef66f06878cce546ede1938cc65d43d697694b05c6bc299fc247374cb202e2f75157e0b635ba2e003d48faeb4f8a981c21b93180a05bac8362089b9c3baab1c021b4e0029c2250b79edc82255cdec523dd63d18789da4d1287298b7d33c84f5f81c0d0f1f0a24462d1b254f20e6e3586ff0f7278e0cda790e6fe5208753f0f10b1ce50666b7e28cf0e6e99924f366737ea7e3d0bddb5d6bfdf8cb85d2ab78d8eb72fb078b8d7518f20d345b45d8f61143e3c15694a9abb02f213a80fc9c2e1628c06b061ffcfc52678d707ffbaf3aa91b9f06687ae14fabb9ac62ce89a9cf470b982607dde67839485e77b57ed5b62ffed5da32f66498eb1310a43954f0b36629e7f1064b159d1c274f68169f93260f19ce102861534f2a9d3495d8df69f6847db4d70795da715367cbfd6ae7d1656e0bc8ade046ae4930d18b6d7e6ce39c8877838400156c1b2856adc94882520e4c58e7187966a3c55fcdbeba01fd38775f64a82e097223b0e0637b690748f2ce412687815e6ffc7909c1ad146a52bb793186897206274e4f84666bae653a0bfa2e22c3463e51fd116a040325372af6a57a638a670f55e0f581303beafa5f0a3dc4fc020e416ed7498c7d48abb9d1e0c3dfe078100a6c2d76285631a5b3b3b6769a3066b9a8310c5775c84456d7e88ddbf55ce91902150703f951d27ab3eb1d07d1640430e773ddc6352b34ff28998f8b688614cd079a78af0edb42eee1d8fc734f02d09297c347aecdde4ad7aa68ccd144856ede0d23290f15a817359a1bd5425a7f140796379f026b5b8d737282eb9c6330ee69043d2d88c240495720c2c1bc7bb181b13b0ac1d4b18ad75fc7ede8ee6da741830b639fca1809e51cbd5c6bafacbe10e3802d71f09467f9e6b255213fa67f02bc0be4ca2491929fc90dd1119463703a1fbc234ccb92f79f442d02ee5b409ebe450db8ebd1f2fe8b46e94aec94ef0932a6203a61a220419cedfa64a5f34148382b0eaf4bae04f66078c35b925e615c9a90b5ae2c39fc27ac00686e6c1df84caf0600c8bcd83a73f162c17f4b89b50ed5fcf9d5c90e3ecdbc54fb1eb97964b3189e068265e3934c6ae35455e2153d0ddd3414a2faa734c42e706c9a27f4dc66067a0bdb6945f8abcd81d88759f63041326c4881d70c3cee69ce087b3d03342b5e0d09dad6680d60894edd2c541bbed393b756d601c1b583636acd20875edae631f903782b7499515db9b1169dc73b771855f45857afea2ad034c458521bc9d6c4b00f00d6d3026a6a9ad19b75429c03197f05caaeba344def20bc79d63b587cdec50fbbceacd128cf4c97479f2a4b2459296beb6fd06e6f86aad288389483925ee60e8dfebd5d83ea6bff1a70fb230da92adc9866721bbbb2260b06fc83c72553870fcd63ff51281888782dd82b5b539835b1788c36f33ab82ec33b8848c8dde6800e66bf2005e3f195bbb300969c1512ba70611a15a99c58b5f7f59e38e8294f7e0a7ffad0c97de4c7ac8e2cea73ee7872971ea49ffd40c578f8dc3c3478d1699b0d8f39643b1941c47d0c116c80b51b9b02b4f738ed5b9e8251b2348f628c373708bcb529c2f3740f1e402be0d4bf634437054ffbad80eb59a273040495751b13046617bd3dd0cb9a7fb0df5a02c42bba95e6013b7b48cf5c646e9058a6c053ec05042affc6f1ccccde0ef89547a18bd0b0053e2424d9440e972bfc7e5d42956102f14e9a78fe4a018f88cbd007dd03e29739a1cdf6b5063f070befec0245987406ce9a0f6bd005c8e28bff749df87ebe1bd35fdfd5b2ddc843d97a212252b28200f3afa238422cf35ccdccc8dc477d0458ca5121ab3331ca43cc37f108e80b9900c3b11396d2b7e4fb7ab684f26168176fa326a25a632ddcf7a350b611b8d8c400e6e27f40896b5d45ad192a1fb9cb71ca858d3db543902d4beb72395c592bcd0be8c9492ad3b48bea50a91242710afb5bacc5a3cfe4c835e719d0d443b0be140b4f5af5fd4f9c34ff6fb49b75469c28b2bc4cf05998c9f76bb66ad283d4431205d730d816cd10dde0c30e706eebe1a0c174bab51924895b98e4bb2b4f5297870067d3f21cbca98b0001037ba4e897d288813bf873c05c9df06b18f864555d8003bff35d8f92a69e5428b63f978395c1c466aa43051325a4bf14c5d7b335e39d01ca1a872d67b7dcf6c73b0c566539c146b331f75d0056045721b81bcae7d1ec061d9bd2f57c64e09260e0f61c31e439f65ff73b2a92174e19c26c8dab42d77403b2f98e19417e62c2c65edd2563ad20d7576a358a89dab0220c054c48334f940c455a411137a66659d18d5a5ecc6954b59278a6ced59eb0d69f8360754e29b50104ef6f08f40d0d973762a7445c3298f30ee1733e17da37e9e062e1c247cdcc0da9ab3e8e8fdaccded2c633d348b2e38f9ca96b31f79a503d3b435730f36b3c06e683dadc4d671152fe94256897031427f02e45f149b6048261ae6b45a39f23087b921cb5438514344e101908abc2daae2e1d56410d5fdc938379aa41a162860d2239a6b7ad6a397bc3b7e95d0009fa676233532e5d9ebeed2baf10a1991e560eb2a7ccecf6ef1a5cdee3266283670afd1313e50e3bb66e7dd8f6d23e7a4bfd002da637ed1d1c6e0902289813fbec0874c6a71ab9e594ed2d8da4edb0b514180cd02bd629191da55700014c621bc174f6e4ab5d09ee4e2179aa6fc481d3bb98029d15f74776784cf3a67cb1b243133332aa17fd040a02ddb0255f8fbc693995026e106cbb6b2e92894a63c9da19d53807c464a8822814be812b56de6b678efa024369be06e561fbfcfc1b67b3953f25025e5dfc2a70d2a2cc265244724ebd8b0ea6f3d8c83cda7db16d50d265f11e7c5d65f179c9264c307ba9fcc0ac53bb6d0a4f2dd1e2190a934f866d6502252fd4449e7f48b4d7875e771e0d91c266867908dc6e3dc4ce9770054a9d32370e3dcb1dd770b8e0c9786a06463f722fcb8aa40ab34940f621830709cf3d5c2429d14772fbc79dfc637bf73ca7fcd7641b10730c35ae044f5dfad5407f71c017d17c311a2d37c0d982fc05e464439f724f3f050e00267ab187e7966bfdfd16cb385938e32aa0399a68af799cfae4bf4cb00f4c0e0ff0d8725ccf6e8336bdb9e773efd4881aa2d6bec997de5c7bf006a1b5066307082746846460b321b2d2bbcefb439ab20ae1ebdc5149a85b0e8c476fdb74360c2ff768d80276c04bc70dbe73345de2330bfa8d1b6d6f3abba7b7037e71ee1400cc9ca23f8634aa50056e33b262ebabd83ed5680bf650acedea1c96bbef0f460e311870b56dd5090ef60aa400aa0e51ad52df6b2ae69de4652dfd73726187390c0fc169c93e355cd9c449c53ed46da672b88cbae1bbe34cbc464a175bcd20710721df6bcd3968a4c7f0f2df2837a7f56c029d72d5d1d3f23b447922283bb8a705427100acfa2eb8f96c0d5b5880dfa0f8b0d02a2d2212e441b44fd088b956b30babf1c7252d1df7775d4fed2768aca06b664ee4daae142bd7ce874472ed64e40bcc04fc20a0e0cb585fc1cefb04aba3daf59977db2ec3893fb9ede441d577c50dc7189100475976179a0de1fca535a102b2a8b7a5484be7bc024793fe240ed70b77549c3bc22b439b5bc32ade784233529800b8300d06ffb24556c3f7669b7b0d8372ead3214b4ded23eb67300ce836cb76893018d98739f6552870acc3e8f5067f4d1f4c1dad5ce6da2c660b7463c7a4318d7aaf9acc358e6ebca5ce2579b80fc2c9988ac09bb5833c13bfcc1179a31fb496e1f86bc9f968d82b06aad0aaec081ea25afb44d6432084706727949b731df04df9b64adf236f8e84e8123bf17e0b6f90bee496e84079cef86c4a76e01e550e17ca1e32df56d1b42cf2df53b55f0e41e2a538daa9c499a2f8435105c86ac0da28c288bf145ae53c1d4695d0ce8e053be37f770734f8dcda81b66fd3ea98b87beb65fcaaa9be8bd41847631c82d00b6cedc0e13f6f6859fe63c6eaf97a5ee538ecbb1edeb737f1ae4640e60ad68d0292a549fea041ca35f037cffb46919da818e8ad732a6e4ed405e351ad35495c0b9e78b752e484634224a1d5e348c7dffe9e81d66c166442f8a5a1f5b4852fcc01663429c9cf8aea5abfba61e4139dd50e79e108fa29dfb367f5cfe6a6620a4100aa234d568f2746461dbabd0b9e02b9405513af4d5da32476a66babe276778705b3584ea525dd25046fc9374c27244fe107eae13b7233eaf10ac0fa84b3fccf04 +generate_ring_signature 5354b346c632fb825e3d4facd91c90176cef0f333295379b68ae2f3d646796ab b232e87202790b0f139b5abf06b5ed9dea9fa10b45137abb748bcedec49957d4 2 1d18a97357d5c6f3660f9cbaf1d05bec77fc60225b96c643af06888b41d560f3 e4943e8f3d243545a50e1fece9b992cbee891844f6920144321f5026d51c010f f0967d0fca4c4f72d2cbf40eeffac2483d32aec3c862f83c0e938e8788e95602 0 3bf521e80f707e0a2212329b98afcc721af4e80c0af3b3b4b52d3844b8d84d0276d32c21a4e81f57c428b76e25cf3e635e19d6715af3fa547855487f12f2cc098216c97a7071295606498ba068dc4d0be56eabfbb6764c50b12eb95b5be6540fded08135a06b5a444c4dee477d683597f5a9e95035df813b6b025ba22347a903 +generate_ring_signature a3e9f19a6821d12b0cae5d619f939c5d9beaeb9a361f9d7bf374b3814ff23ac3 5be7e561234f7b6bdefeebaf9a512ac4d97b77dff058f832100634dae4b222f3 4 1385acd49f2edcd1d00c3e69d899ff286d76ba1ee5e7684e480d914d31f57a58 5c43220a60eec00974abc48b6dd95e001d4b1e362aaeda5b712d8b6aca644304 02365a93fcab00824d25ce4e4bb58a49b9f68ff7b1748d1f03f8e5edd6fff416 a1abd0f71d7c05bc675f911d3c4e54c1b8618c603951a1de691eb38f030199ea dd3b77701253a786191b9d59b556f2b96336027fe8b0de80bd2b059acf610d0a 2 0615431114ffb77968c2a151e88ac4f7e9c76d0111d51532902385643bb19b0dca9538f3b1dce56e87719c5c9381ee912a3da53d70a24b1767ee83d7a6f0140f411c77ba4d43f724dc9a0734e14d151d760e660a7f6a563586978fd9af1b7d065251d60ad97b5f5459dc57c939a57161c2b8fe8c953c96a3ee9123ae2087880d89de70aebe162bd22db65536f5daa17b252581acba7c708fe0ca99f115b38d06e8f470bd37c8253a791e98b576b1c61fbaa33d127652dfbfa04d216638fbdb05a35daa5bcec656bacf32cf740402c6d611734427075266b7d163cea31c1e050f705aa16ef4aba7d720232540c56033922c07d10d3d2a61e0b337f2e9be95780c +generate_ring_signature 095549371963a6086a8533f8182dac9a9288863a9816d457484aae549c10c454 034fecfec4129e575a8f79e4a2f329ed0d6e804db3c6b30178a298acce98562a 2 80e4e87a2b4f800d02c125ad32c11b8d667705547775533c48d73f12f39e1c41 c404cb7233f19eac34d072f1f24f9edef448ab74024a8ddd8d72639079be844c 47b6e374cfb1b6ec4adcd3183f7b9f54812d7bb6d87adab41e1713b89a918c0a 1 9ebc27fbeb7d11d32db4957e4dd7377662b418712c824b453e76336bf64a53002365ad9e783df0164ab73d780cda0a05149847ba135a7f3f905c7aeba9e3e4048a993bcbbd6fc5b99b28ce2f11f420842402d3d33d19a26bd0e56419ec694301286953bf27efcecc2ce3a6f99a1e8effb8063046fd3183f3b7862a5849aea302 +generate_ring_signature 765d32e597159be85cd75d6f4a26b6afaf0eb7618a10bdbd64c3629d5f37829c e5729996355a7f45566941b16bbbdc3e9645df09d11c7a4fe0297d9046d80876 4 ab4b00cd5d57edf83bb7e34a17d61fa41fd2bb00eeb8b182e03e05fed0d63047 1f121a1695b39b438807592a0364e537459d9ecaedd7be6ecdb71b9534ef74a9 02849420f9ae2c064385bf53eb6a49ef1ced6fa4b2a386ccfbbc279633f3a4d7 4ae5357926a9a9e9204bc6f2c8d3c05bceffaded63d44c2e52792fbe87964b89 fbb4fa26dd9caef9daa40f9f451d01747fb79eb9bef92b680df1b0f52588fa04 0 523b25238b4edc3b55f35b6f3e58175c3f63b41268444c73d646405564b982095d30a67853047027f2713b68c2145ac132e004877cb1d4ec6018c98e9c3feb0a98d9e880919772e856a38194afba119a698a176578dfb545a67b311e8a54c10088a7de0b11d2fb5518222cd683488bb8cd9e3930d8d66d0dd976af3076f4b10bc62837fc63311ac9f613b24976917e833d3ef1039743aff5d36b142bbcbf9504b5dd7bce1365a93b0e9a245aeff2e8782a880bc5ed6c35bab42c377e0ff1700d361a48fa64b7e529edd291f83fe02e01464a509ce62b6472e02baeed9cf2160dbd545244dcbc322bf3629f756cdd2a80459d89988012cc21931350fbfab17004 +generate_ring_signature 5607d2ce8ec511c95bb7a1ea452f4574df04bd5bdbaeea20ac0e508f13feea37 66293e624f791235be8fcd3944938a213b1449de9b64b840f46ab25c244e2eb8 15 252083243400c45f44300a90a5a72cf124586df2fdd358c3f2d8a10a9d3b9f84 dbfc0f9a60359bdb13b1eeec597ce25a39019303f53e214106c1b6efa039f41b 1799c271c54721cb9b9aff31e770a27db2016d3462e8f7cdf97e58f5d957d1ea 0f3e0b58254ed1f16875206a5fa71d8b2bd3051cadd875a85c064fd43fe972a6 db61f4aaef31bec7a76142570228880bc1ba95aa3e130efb815c392daf1f648d 69a59d101b46108998f7147556e1a489d50a8de2822cc4b616fb316ba6308890 3216e6c0b89b0e9dc558f22b0646340901d4bff8dd6d0d37b428de10e1fb3d8f efad5c615373f47a5391c88bd7fd426915d61f772f8813fa1998007ee605b032 aad7a20871ffcf814a70a4964aadea64a2ca043ea3ed90c04bc0c581ace03197 ea57e5ceb849204391f7896c7cc93ac01645bb955a35e7295a6201d3888072b7 ef11e7753f6973b1f3c06b2b7dbc596db6f16120715339b17824086c12a18686 8a8442aa38bc171b5fede9ed5892c4c6f8b7834f6d84d6818134a2f61125f86a 12fded8c7ee0e7511d3f37d7fecb900ee662c3f7dc36756d64fd7bd5221ff607 97e073bf305e6e79f257fdeb5d5b126f12f818edbf3502a129a71948afe94933 71827fa61bc9bdf166378ae6e479ab50eff55f8498804bf26e92fce047f1a76e e1c93e67a747dff61c24a89817f62f6f84ebe585721cbb46d0139ff7d410250a 10 812b2d04456b45b6818b807fbc30a5638aadde2466818ff15281a09be39a5a00a61ea4ea3604a53dec3deea1c7df7ae2e023554ca5f2a3ff4c53cc8b3d83940e9fe5673b3138ebb57e77c7cc9c3d2e1a263d308266761abb58f6175b6a5b8d0193554bcf59025d8c967af773552b72fae6b8243606dfab55d081b4b640a27a0542f79afdf02186c46f053285b7a1d636494e2a10d8ae11cb833d46f24fa49f0ca10cf87572538f21baef377f6a4df585d6cf4fdfac6bea8fadff9eada00fa8009d35812b2bc13dcf81f82edff22e1399fbdcbe2c92f93aa769fa457da766d90c930aebf85cb20d9bea893cf4c4f04514a904836fb9a3545e9f11c6e9cc97110a086e89386835eb5e25774a05358d85604540fa68aa9424bc3d801f53d2394c075b9df7fbf58dc1340f2daa5f004ec67b4e4aa5b8b426026ada481762851f4c03632e5e62771546c253a94b507f7dceeee5cabe8f09eef8f944f16f2296a93d0be350e229edb889ccd3ddb22c984d33a83e94e9d7840932946d164fe8e1bd6c099c5c27a237c01d8042d49d089b129bc2cc142f12ddf77a856f817ec2758574023ba29e804de155a7f3dbfc7fa77a5f366eae53f7c4e962e01168c3355ffd3d08d33a709fc72f167758ec133fe90f2513c5f912ca82a56e5f17ca5cd6865faf07d3d5a1ecc34f48979e6f1e67604a44e749c33ba48b971b2d65920aa29699fa087a0dd99766049560d4b0942f7e3085d26ef1b44e88be567bf042c9979a3ea10c5105f85c6852b1e67beef8a3e0739b9668ee46a2b0dbf1e3cca85935744f10077dc31186c7225362c3ff5f5aee4fa7f27977b6a81b10d22c09505dc8e957ab0513e52d25b6d22a5af1a4ce02048749ddaa74f671f9dbbfc44cfce38c533cd1011460c5e5d18b26afc0687a55b313b790a3f6eb458d7bed7b076afadc63284301900ce9669e3f430879f6375f44e218af5607bbab9c9e7ce0ce12c1daa55a510895e3eabd3d91ea0be0511f2ecd6a313bfe74171ce6141f49fdbb06442334350c555b069e9f809ec0ff6fbcb19f873a7577d752c5a133e69490b28b7958d4c80473a41311b95b90f122b9a2e52c21e18c43b0f15f18cd76b308334db95bb06c06aadcb55c89c22e9793a99d3749568888be47e6c8abfaf5279aff0819bc8fb2045134c54d3cf4c44681b501fd8f32c57b1ab9a771c1dfe1bd122204b9c91e81046ed69f04959841cf300b8a766871107b1913198776f854217dd3e1de212b270d87f01dcc238f7dd4bf6938fc0871809279e14c7e2f5655c4147ef3908f22490350d973b2557ed2fd6aa6e253c8a2fcd3c84f2adc1f1d65e9715a47cc29384c08 +generate_ring_signature 781f7aae6916cb86442605ca1c1329f08692a28c5cdfe0cfa95d964ecbe6f9e7 dabd3b459a7ca4562fe5ca42191a402e51cdfe567079cffaf7ebe599459490c0 49 6d5bbeae1447773bb341d4f6253c84a563d776c81124bab9c82b8b55966ef9ee 5f3582036521d737a102ab0878f3482825882d869168795c8d29501d25741e71 52f9596059344eaf95f52e49d2ec46868c01a0612413866c50b351c5628fc8e6 57d139af17d71882f6a385f6a87caa657970517595baf226175248877f5ddff9 a9f4ebc8be21b9348b62361a712c90841c4ff4c595b36049efe2d101c4efd321 e8415eca3390ef846f46259c0e0fba6dd7427a7b5f1af4cbdae1cdd34abf7569 4f3e26a193a4cd0e338fe5ac6b4451217ff963c1ffc7ed2afb8c86d534b0bdcf 438597f124d1177200a93dfa9d8d0a9cbcceb2cbae2d93640147ff1eef7a2233 f5339ce166c0ceeb926acfe4a017911f9e1e3650e4eec6457e20cf52355ac4f4 4c59880049b15c24b6f54586a69b8935b5d8893a7dafa1550c5123cf0a253cdb 18a2d68bcf6db85d092a4362767cffbc973fdeb9eaff23d260c78777f8f57681 0d45035f8548a968acc0b0815518ea3024360446580b0e5878d75b0610911340 724763763a18220ca705b3221d8ee2231cce67235d0a75ddf076e8ad5523cf60 65236dfaaf133b1dce26fb478a81305bab5dd30e426ada4a399e29a7c02ba4ca eb7717bf4521a54e02db70fcb96c20d9d4d6673378f6b2c196bfc22a3d5dffe0 6752e1853091c6b4aa5f88df39696cdf2b7ab14d3e4b140d1c3905a6f86766a9 ffaf2190b741ec45ad0bb7070e66235764c44449ab408c6769eff87ee92df421 9a3fa68c3398397d68f7eac4a19e7235bd4256243bf8667082c4bf5bbc06b386 c0cbfc6b9c6b2e6c7c696cd7a89e4c79c7dcbb394199494981a6342fcffdea0e 661f99232ae0a6143a994c679146f2756cb4dcc6c465145b05d7bcf57aebaace bcd4b4ba71a309874300bbc35f9a9a541a7ee8055f798a4ca4dc546ab021f25a 8937c53d0262f927ef14ae7dda2cbd277363612e98669e58604547676a862af7 bf01ff591c0a495d7938ee4e3aefb0e20b121823451b1d902dfb8f2b460aabd4 6e0d51f94745295a30af485bbd46ef078fc52172f142f1f73e68d5d0d9ab1222 36fd64b91f05287e299200deb9fb4cb0e3de6f0240e2dbf274912579a8a75eac e72d9f05f370aac2cc5e0777bad931d6e4e193179e822583f02983f8c6bb54b7 d589025c70a128d8fd4b6364ada77612879a039ff49a61b710ea5ba856eb4cde 78c4e48f85675999752f7e6114460354ef4615e3ec05d2f6f96b85a0feb2093e 50f02e55a8df0e9bf0c44c66403ac989c630d21c2389ae73f3413f1d07f8fd2f 9d4879da832b2027ebd9dfe0d5d361a4dbf32158d039d6951d20d353d0129c6d 9e5f72821c4ac472b040e19d85234f46aca39613013ac66788c3143eff365d85 9d1d9d4be6eb21f591cef27b387545a22dfa1882a7dcb30fd5f2141b398fd2e9 35147ca542ae0c529dc9818dd8e4bcdc300a867254b9dd0317bf74a860a66817 17c55d7febc20cff8ad649ba7ae32318cac1750da6b4ec47c31c0f49ee1643f1 f24f5e7d6a9a0e9c457a0ebec586b4bb0a3a5564559cbf377138f64562df2484 952e0f4633631f3eb0135819fdb7aae7742ac5b2024c5f15ca1cc65e1cd08f24 7b9adc5c33bcd0feca677012599cd9a7fad17de30a30efc0981029e15c4fafbf 11e437c3869f6f8d822ee753c68b860560480a37eb57706082d076e0f601e54d 7b89c9df6a8a9f261d298a996c3a1947a6f676c2c7be4ae7643c2ac5d8e89c62 ff3e6f71a711fb28a24a2ea10170ec29d4fc3f3e2f2de6a8698783b0664d941a 919436ecb0dd620ea94686ac3fa07bc17a777d48f9c0224dde1703d7a2ebac63 7088fc87666f3a284b325d1a50ef179e47ce44f546bee688bbee3377b5462e3b c4bf7d3009908f391b0a36d2768394e429a4afb69d789d64bbf1377402f39b01 5626037572ef6d5c06636c8d2c7a56e19e4eaf2bfd6229204d4a60157b7c3d9e 94c3d100bdf06065c3d6c21e4d743d925065888271afb52777926c1bb49698d1 45591bae02bab2a3b21a83305a149c0f1ad63d16f23501ac471fc5195eb4c934 fe5634f879e4007ac0ffe322a23d96d428ad09df6eafd123e67009c77d3826a7 e131ff9519e778753cbb08ea67fad0bf1349f8bcdd3e2a9e30556d11ead0dd5b eaccdcec9531361e1b4559e4a9ee176075cc22a6770864c21abab1a28fa062e9 b6b0a5cb6c1acf7b032fe3401997b9be29ae854fa7d6c1dff8228a85426e8e05 35 d8fe93012fec5d202fea478b51dc5af3c87e5b412c3fb08bf2c8bed541fdf20d7a3ec008c2f0d60942b91e669108575e40bb33ba976fe62fefb9a79de3c0890d273bf195b698a43821a8161bc9f80920edade208cb80b29a56196793554c8d0804e8bae8036d3c9a4fb4af31dd43184f8337a6338950d32af747bbc2adeac705c9077b246ec19549a57c5bf1aa94037ce32ba7a9099351cc1316d9328c6155076b6e82c6d098597445b96e319023e8b46ae190567616d2336c91595135b7710dd58723d73fb870f178a5f224482d2c2d9f55c1ab612c2606139312f5164c5e0bc8ffd8a991d926e3a266347e2c004aec31f4bd4f98db32d39818e1aa7dab87008517faa1424239a44079b4c7161943d6b8cdf779374f0bbf2012fc73666fdb09fb18858d96b4aa594102f414fb2c0aad7e49b520513ca6c62d64fb843fec4a08bd2285dd7f572e7199d149a83da6464fa93643a88851a018ad6fa6ef201bd2044b272d5387d6ef576117e1707a2752be17dbb21f5822bc6938cf388900796f044c43f557f4c4b2a24ccab892f6cebb774ca186568e1a80f0ae7ba5073d3b3b01e1151b4f538859bbea8c7c682bd99c424d6426553663ed1201f077ff9808db0c76dc6e351020a0dd0f122df4f5ff545381313992c3470a6d1905ba87b000870ac9513dfff24f1a1bf3c5c26e0fc35f488d67467bb21fde9ff88621a91e32de052303150b9c06bf289539bb254db0f03b7a8a2c0376076d355c9047368bbe010032c0eabbef36d528ce939f5c81f67eb265d980e42d5ae529cb3093210397600038441cf34b914e2b8d315a05a3ff2e0215b4d9efa8a000b96bec4064d7654101c6c880205947bdcd7303d1ad34c848538d0b24662b9185ef3da41c7223dace03581374907a5c9785b9135f0199c774207c7d7b0ecd7b8ba464bc3df4830b780ce885ad3fb2387ebfc34b0e3c5df093ddab9d036d970c399ec4bf28a71cd7ef0baa5d353cc78326c42b6ccd49e762cf4c8642cc1c59b536356d3c6fbf44ff0d0e094c977a10cbe6bf26f367b6e9f9c02f7f469789d6b562d1cbf1a36f07f7f70a3a8fda9c53f03e721ffa7887e2c6b8965410932d888115ac62ebfa92ecd78d033724546da824872791287c8d6a534268e2abdbeafa488d8128c6d26ebe63f20667f461104a0c8b1d061da0f3b28b77379d3258cf58847774c2f888c68db8420e19bd67ef3ea2a47deeed384de335996079bf2c703586d7d66d95c6527f33af07af69dd5be66b65404f70f3e9821939b51aa5de51765a94e5d1307252b05a4c01e77adc9905cfecaa29fc8122b13f7aa999dac6cfb50fa2abc16460429827cc0f8f24d22784e5d7bdfdc6e55f31be66ee1d7c9a3a5a7e2d92b7bd1501414feb070b1dc12e110ce7d80f7e1dd67849b3855672008fbb3f34c42f5b7c0cce9a920f9a601d1bfa583a304017f1f600bab311d41f855c669c0f8c4284825f7b0b030c1358b710a4405c2cc3281714ed036eafdffcffc65625f42ed2386c01af4d7601bb7c2a89ad69b05bbf41e3d1deef6d63c3928925beff4291f729610b505c1d063752f43914559fde2632839a4418c505f5ed91558ff3b21ec145d5578001360dd4c711b46a1bb705d2c64a0c7df1480395cd65aa38117ee204f427e55a7260042c29b971490cbf1d542b737d5227917c7080cbb29c4b599c211efcf8de5287013688244d1566963693798ae8a0268365dcb049480c2ce85f31a0b13b9aee02030145a674a3e700150ee60af5520213c46c7c3480027ddac5df0a90c4bf1ef406fca63614e7bdbe19ecab77bbff6b6daf355729573ca9b3a409aa9e9b6ac57a093ccb1be1c819badf96c0270a7e033aceb54157fcf16a36373d0746080fa0e405e3c882a36fb846b82666eb815af683265c32da54e42ee0942ed1c215a48f7504a96f59f79760b0c8269eb875189629f9170cf3d50cf6903380e9e251245ee10c0e1a594c041a6155f1137b3ed40766842d19b8d48990d74319328caa3b380a01df9d09f76b7dcd156ca8d6e76010e42cfd93e5b779835f39ed88feea44ea310b689d5a19e7da79ab595135c35889edfe44f47a4e698a859cecc471bb54543307e198b1efee4c03c8eb53a4a404e31eafa05dc5c9a70219769fbd197eb3a91d0dc9b5fe08bf44c47ba431582490efe1a870370c71acc30be548a683024202e50ff14f8cfef3e3716f60ba95d1a1cf6ab1550dbc204ed86246a2c5a681329600066d8c3d3109fe58dc2f53c682fdfa0756e15f752e12cad949f37c8a38e2e9590d0c55c25c69b692d1857a1c42f31cba94b1fd96db9c0558999d34aaef5633c10ac69f56d70784fdc58505c187c354c07bbebd39188e5ede3e121b1e96001c450ff488108ac4415edf332b9fad54da22e67a092502b3816b291ef4dbbaff0dd100c3f5134be3dce80dec29f7b521431c85d3f036ef92bded0308657446548cc9088d8364b984cda1bb925225ebed08b8f5e02a8d38a81ff9c6a2fd629a94680306312a0f1f3e01794b44a905eacdec9799baa2986d258dd63bd701c30364bbc40029861ad1ec428d9ede1f7dde3196e8506178f87f944dd3ebd0d656e4a275600b29d3e5c1a6210e3db235d996c04073db321871ca6d9afe9c493cfcd987514508befbaa7276f8eb67e500285f9122d62eff6982a44461e035eea4d92730fc4f0df353c5b0c2a0af6156dd88f6bed1d7e54fc3dcb4dda2425d6be769f9b8cb020a50b36ec318ecc6dba966eaf88bbfe06aec868609e64b8ee7293f8d240c7ea4039722442ac539c0d22942c7869919d18d8d10ccf1280edec595eea82fb3b7ba0e81e54540c8767c168392204d78d4e6df6c5e48228a817032806c65ac0bc1d505b92e208fd7d27f9e0bc2700b110879d2ab802ee2eb10b35e0521540aa0b0c808eff9b720d0cefd4e8739a4848e0b8def2b2c88c0996c2c6613407b435ebe7403e806b2351cc60ff776c6b3de87c741f48b07fe45de41102d2b55f667178c9c0797c7d50a08cda2b20073fb3b381ae40a625c8f74ca1ffd5124e1c342e51d900cd386ccda7a5433cdd88a2aa31d1484bf8c90cb8791090f6b0096ef1d84d4750145d7393919cbb29ec50fd8bf47f85c38990cbf76ecc4fbd6ff98b77c8b206d0c0af0f5661b33a8933dcc535c916eb49d6e4c7e335bc36f04174c67903339fa00f5925cf4e975f7e0102c12a82778293140520217c18d56e37fa00e424940c70d7785a4b043f5d0324ce3785ffda5835f36506620ba1740aa368c7f1aa6b2cd097b8c606b8e03efc987e716ce0c2a6c8bff784690d15c1260aed6551ad2645d049853aceede4a02c36ca06b4accc3f66c2c80ae41415b6a0ea0e56ceb51c45c0483fd17c124575fd0c08e86cae2b471dfabbc0ad8a3fda70906f78b42d6093e0d5f0893134b5c4c28f1227779a8459f5f6b1d9c6eb52c60f08123245ba695570ce89942bd9e679a8554eabf42450a71458995f66bb276e256c3ada9abe0aecf0d96aa0bf1650080feba619ab005612f60151ca6e16818f5b910c350c6d0735407da32644a33f5e367d35cabcfa862f2d826d5f67f3ebea6441d124260eb0cd20f131e09dbb16a2854dc974552e0a299dfe9c5eb43ad91cf427b3181bf856f870b740ebe7c935bb0cbd3128383aff964d5d57c16f9249ae8173515bed3af0bc00c59464b1113188e5029dbe8e0cc3d8fa8eb2d272164215f60799637cd183d9b02d56e985d9f15a3b50c519d47b6a7b6c1369deffcbb59b18b60109a8e0b23140beedf4b17c862c238adcbff74d2871dadbc48b810936af86067a68d4b95c1c200a0ef7f8eff402fba8454a59712758db0cde2be748ef886e94919a9ee0bf3170af3a699c40f2143cd8afd078b4b2a1b7b10f6e4d398a32b77b1576a16dc71db03a44f027c22fc8b3fbb2b54aa4c309a8dfa39fc1bc12adf5058da648e44d44804c1366319c7c50f0ddef4a719c3e660be546c223f0403a7f418a28ffcf2951402e2443742b0581e13545eb945072c5eab773a0dc19f5527cdac61176249ee2307e45a41d9a0662dc14ece168ac4da94dd652e743b1365180a9390bec3d246c50b622c0eed9cced72b762a4244e26e89048ab6c8cb7dcafb23dca9d4f6d00c850894c53bd8d3c4db9f1cc1944130c2813a84fb6e013d3a73cabfd4473801cbe3096ba631e8bfe65fba0f04ae32c6fc7ea44e2e8ee17c4c04f4cbf140248f66f3016952e867339f1b3d7853b25c8180a452dc30e126f0942789997c0c5b6a812a021f9cc3369fb058d46302b5862968df852d9387aba6e0f72ba131958ee0e35d0a6f8015225713d6e457be74fb6d63de9a01d7d6a6d4cb103713064cc297a7980712e62d7406b48015f8110c3723dd528ddb9a308e7e7ac6e61db847a27efc7907 +generate_ring_signature 0ca6be8442f73c39adb0992700397888777437a7de0da8c8f80ac7397c51f46b c2483ecf2cb22c97aed88f73d621ddeb6f26c3417ed263dc17a486ff77274806 1 03b86aade5f2eb2b8868d951161eda03d4a47efe945a9dff7d630de9089a94f2 ef2190e473cce3732bb33ea3a62a4d8172a78336453c32f4419d1437ad3e950e 0 e70de0228fdafc3555d07ac61e61a4368fabcdab228cafbf0ec4aa2c8a59260229d579203c9f5ceff024ba0fc2eb41aaeace5ab5ecef65be0c2be53844a85f01 +generate_ring_signature 79c5aa9e030b432d9269f7e35bb8c1ed8eca493372526a02ea1d6ebcfa52b385 9b9a27939cd35b70d56eefed6bf3473df1bf5e1cf3ebaf7de8ea58ef212170fc 16 1e6c15fded661246e31217f773daa1c73c6b5a535dd39cdeece6347aa89981ed a2dac02c4e2ecf3f06cf95a267f7e1db2bc014bfde9b8d5e4698279429848a43 ca579ff489a24164edf6a84c9bdc87374b47efd80c3f994184b770fcce575296 2e247431d695436c7107a280e966eaaaa4d90440d824f93d1c96ae49bc16d4d9 f2609d17a8633257d1cdfbae1690892339e49f9a46eefa9e7b97d5b0570f9cf1 bdca7e43816c3c6fd0013557b1e0f2dce14ead44b6ceaa381986cf6c3055adbe de1b17fe16b510a67e0fc049a5cf4f801a9d9550e7ab8a5e4f74ada85a4057b4 2e9281c688bb6d020dd8a4079acab780a40329c6c54e1947f0e0e7d90b614aea 51dc71ffbdba2913ec8e578384e4d25fd2bed760dda9649155d4ef3f49898c63 88d6f98dd9e6c2a990aa0120152a372da00c455c97b22767803121f55b9d3433 6ce4fc262a6b4837e5f7baea0a512112a2d72dc6d36570cae857511197eee9d0 ddf3cf1ccbed6da657be10b878abde58598a07eb6be65cb870978429bd6550f3 5469427cb812861380add098f93b02d766d7b9de7b5c34d17bf8b2105bb99cc8 ae7c7772243d1b1a0939390bf562946634fb8f3a3df045c15f622aec4a19b05c 62f43f2a86d3cc9a59a82ce2c087c9442c8f0de3d648e319793e8c7ae4158735 1fa26c1a7685ce10b3734969ee68b9d8f441b0ff1beeda2a6a5ce41d7857727a 7f846dc0a1378202cd26c66f6d80322192979c9191c9bad913accbe8f3b4030f 7 0bbe1868d32efb1f473ee1f659d41e42734649005026e1ac279d5a4f517c6c0e5e346ef16de3810d7d88d57a2d9f2f6fb447ecacb08c84e9bd4e4a72aca50808e03d2c29c27a831b25786821a8cac11d96d11e5bf72116719b0816d642135701a1273adee1d7d1d6ea3141c1df8d4e24abc0f48a3f3223f1b230f8294f5e1209bf5493093162ac098f74abb5ecde6772c57378e3987fec98084ae4ef2311ff01d2055405ab8ca3803feae7beb18f3b30c06c38fb3f7e31aa2ddafdb5f2e5860f8b7a8c95779f997002a36fc230048a3ce226bf2a2dfb29ece8e835189301960ffc7745eb3ea67a64982eb099d59974868b8c86721706600b3e5675658c8944046664f52af1d85774107cc40b326496c07710797c3c475addf9c4932b0fb03b0c1ef1d853253c0c89a6805c99e7c610bc81ea1f39559e5fc3f7893da4d27b0b029ee580fe3ae9b8abd48810746be3b7e5374dd57751e3593024f69194b498b804e4ef37bd44f2d0d59ee4cbb6f6a114103e1cefc8b58077b6c152221ce75d0b0091bfa7db1dfdaf80a122d3d92b60279802970a5d5f03ca33c51812582d724b0b01abf07180a7cac8118aa5ecac6a516bbe9a2a62c72b59379bda7d93d0a37107fd3f78067cc47207911727df5529a4d4615f96f047094e51d2f316cc3d280603c922f432be163c96c277b3899f3b5bec9787398c91e3b0b5a8f25d8e7ebb0f01423e4b9cdf28651fb7dee1ead55d02bbc5b02722a1c5d14588a9f27b8cae640d9b7ef746083f33f0873455d0e3ffe469fd3383e510bb9184f52209fc5fe3a20f7b01928698bfbd1f33201c3b2a6147a6bfb78cb2aec79312a2271d41c03ffb064014c7ad2e736cec5bdd313fe08d8e44953276e3b9f9150432cb0de79183290750a91785fe2a5f7052b56dd4a96af3f41760984a032171615c65a5eec24966049ae0fca22086de48ca7222ebe52d03e5bcefdb174b45b8c96801f8cafe19880b60e07801447e325a06aab7ef883c275f49298b7795063b015eae1ff9d6e88909fa967f53e74e7a50a5e2399d8ecba2b7714bcbcce137acaa3e778d767f6fa106c43bb83ce741c0e7a9f5b30e1b52106aa63a4a03d0ed39bbeff96116e3c9900f44c6b560835c311721ec3cf5d22d2a023dbe4cfb904cce5921ebe6ca88b7ce07922806026209e364cc8a5e29aa60b546bd56fc37fceb6322d7a6e92d7dfada02e2e72c22e3fde3c1d7cd93e93621b544e4a72fe82f0f259a19bf7fa5294adc0ca42df2ceed1a1256142e86ec8cbcac099213b2352be87c941468d9ec1c149f022469523a31842723324589f45f4567028bca50e6d238812521b717b0cd8cd40050e17ff8812ae27f515a2ca37b261dfbc50be24b973ab2d843f0cda2e7917703ce7dc6d30ea5a15d51caa3e2cdf08f3459f0768f9df8eff5d8b2dfc0380ae30d +generate_ring_signature 80e43999bc762b25fa9d83c6fd111032ce5582d573d6f0f3e82e0f989fb6b8cb 1437aa8974c08719cc58a5334921c3201e9f45a5290f2581b26345a659b86475 17 40584e3d53899817ece3a763d73fbd5d5a09cd75ff713d3e06c3eba2fc853319 e4b87de4b1b936bf74e0bf1aef738e47297e65f2df50c566a9d5177c75917534 98449fead3b69ec5ab2d9f524ce8aa5e6d84f14917b3b03575aad4d4c291a634 23619e368d268b186bef7a5fa0165c31ad8a3bf4b0220bc262f484067d9ad4c7 06268863803442caa2e603b4791a097a8f33c45683ef55befb3c0d7e3055c36a 61ae5b158be8ad2e489dd7b0b78da17166a97f09629f96342e8712788c1dfc0b 4852fd68ec5cd1231f48b79a8bca21277e347d39af3349bce848312452874d59 5446a777840c6f3f75aaf437d1f84addbd22b5d5c848ffc3b7fe16e1157e4819 29984210fb9b7462a3cc598bfcfaed8a0d704d02851f1ea7d3e7e3cb8d73d3fe 93d4243ca3db959b7f3d7af72a0bc85e27226d1c716dcbe1c5b3743ef5c289ff 7efab8e221de5b67335eb804564b60b5be37ea20497329bceae89822d9e530fa fa084a1bf2c317822a31681302722b4a8001fa2aa9a0190e45b68346298b9aa7 bc15f7855be9e63f35567ed80b629c270a1cb1de56ed670714645261edd0acb9 17749387d62c8c3f725f1acd5b44c955bd4b35ece33bb5b3fb121ae52dd07750 7b83783748d179d74bf5f8a177a8e400ff4783ba1a24b7966d99b587f6e4210f 235b414cf437b43c611267566496a834045ba01b3f16c7b83a838aedb35e5863 0e326a30399705ca5c7fc10ba5f072fe06656388cbbf8f2417032c6d3e1f1519 0be7d9b1884397738bd4495a62ef77233f1b262363539f6bc3834b3430c84408 2 eb94166938408577a45808552cd51352f6ffcc40f03f596ffacfde3e9ac7b50024ddcc93c7be70deb202a923a924d41a66cbb1a4ef9ab6ab660c3b8f224fe1040d2e79825aedd60df00ee292be12da0cdafc1f893f51b37356e5d68551fba30db8f8ee0e2a033fda20d1005c49464527589bf9eff8bf5e14e1dffc5e81adf407a93c8ab8a007fa20e963df974238bbaece93f95470d9cc8415ca7b8b3d32400681f82b8cece5e152fd54312a9c481c124ebd94fea24ef74c4fde837e97e31e0ffea03a6ed5b7e864b1db520897487d6758f91872f5f2f2e922068ff3ec1ed4000302457a48b33868ae02e1ecef336183708f43935e26f77261a8a7130e9e0507ca91997751576260ed3566ce29b424705a14072c3a8753bd0350e26daeac1007535d6a441ddbc8c68a74d770ae1cd23105322015967d2366e103769dec98a80859f995202d2194affae7bae42965ca2dd820f0073ac34f32c9bb4d9e6ee3070f4053bd90e8dbc64aa724766cfb0f00e0e06ee07241e748f216e9749630a4290dd5c610fd6603022398be9d0db9bd6dbe83cfeb3ccc7d5c5b86acc226415e7f0957b52baff307d52c144de8c34ef36480046a04cda7b3221c3d9535b74375fd03da2491e036c7de424c9f2aeaae40aea5b546c57e12b08d170ab5fad3ee773a0b1e1bc4d7086d4794bcfcd605d39eb33494826c67cf5b3e989082eda1d61e950a6e7878783bd3a087f61e041c25926e207aa2adb686b124ee7d7e1d1641d8c20d380e986cec276bd823bf8ac174341074f568e85b865f466d2834bf66bdc8b80dd7e8a531ae71353f78ceff67154bfec3c5a5474a20ebf517ad2d68a8433a2e04de3e501cdd60e9d03ac21712a3c24de4421facb07e164ca68025ec71c47ffa00a68766dc2efb0a9b0c85ced09a48fc7d7dd169b9b8a64d916afea303540de4017feb4f5be92aa6159d0801f3bb8ee8aefdbeb309f6d6c639f5c25eae0fe92a01b5cc58a4c21300afd5d165c0900fd156374dd03482fa376333d1b168d7ee120308d8809a35a78c0434057efc2b51ebf6f7d3f2fc771277950197d3e13c5b6a0396385539bd059ffa3c12eb42ebc995ab020a5d2d026ae3bed525397026ee000e8c700fb7e2704a9dd41ec8a895a610619e87591e19fcf7c5957365e6306e580420f0c09f2a6a4e9c6853a44de2773d302c9d9fbb24472c2cad4303edae9c1c01bcd95d9e2ea2050245fae50d6e36347647dbbcbd6306f67ae9383c63799bbc093a0351597e0d56cb8fb43c2927ebdc832ce401a98a16082b56bb8ec10a986209423583e1cc2002a3ed6f307cb7abc97b270c61da2ae01d9d4ed927a97f30e106a8e1a42f2238bc8c54fea101d53a1752885b0f4404fcaf285e555a18c9dfdb0e9c0a1d9cc57c296f91c7346adf51945b0c31456fb3758ffd4a395a40250d020763b726a95b2168ca644ba999d40d68697e8d9d2f5970c8641b9a53a61c2bac05be5684c7cb5397315acee2e1e63f08cb7922469f532a97d2aebd5195b2fe0602 +generate_ring_signature a1cae1149c356e23100ecd1987c21504dfd7eb93e828bd3e67010b2618b67d12 41ef904648e97ded7bb6cdb675c0eb7c3e265920e0ed3a43f6d9d52b9cac4950 4 8af0cd069e8ed1cd5b5210d4f3b1234fba3701a51f71223a642afc1ab7c2ccf8 68a106230168fb6b767c70b9bc50adae625609a4deaa23607eb431a125d62acd 7f77b78b984a927a28d64f19feb63bab35f545c419003dc6804fcbbd0a6a7d89 02e2ec9fbcd3da1d36c4426320a0ae5dded7b03e360e801c842d6da1479119fe 9bc45f2111dce6e47ce84bce74bdb15374994ccfdcc33c497c8c6d02621cc402 2 60ded68241c357732f79290b35f199840589922d852fb66f6d5534199617fe05c15610a6a3821bc2d263be5dfe32b5b59bd5cfcec4ae1216fc61204047d92f00c900344a42ef57b8a104e93cf7b945d5e715dd39d86a5aec83d20d6674a1210665f31cebcb661ba213245498a30c716dfb459494aa7f7fd12cb5e2e95ad82901dbebe82723d550d6a8628447ca27997aa720a58e1bc4745b2c75e1263567c108d5bcc19ef6f64fdb392b2a52505a7255d5544d9026037e6586a81b238ef8ad083d9a46873035d309debf6bd4ddca8a292a3d03eb94800c11007af0c25444db03445ee7df8dabf763c5fc83151f3d09da5781c734d966e4a2a31925e36aaddd04 +generate_ring_signature a4a40107ca42f136d1a311b9dba01b70ab6e9b488c9d4bb1f82137a8de045b0a afaa82207be08f51be6d9e58d2029c812fca1215e9a4c9ce7614fa9b1ead0e33 1 21eb68d3bc8a11f481c4dd1aa33a7eb137bc0266faa99ad02035de1940e96ba9 4fbd33282caac0fb0207af80c006ae9da1993618bf7f2eff764426fd0fe7f204 0 976d64ee5b223688bcf5fd91721d245cad10601776ee516d040b0ad824cff1001c4b6005796b989a09dec70073942f48b38934d412a2521d1b57addfa751eb0a +generate_ring_signature 856142e92819b975d12e0fe26c75ae88da078d345246b985f3d2ee7b2c2f7c5f a7869efbf3517f7b3217cc10c8d34aa79dffcbcc332863aeb9dab30c14f2c216 4 c2fe2e312d4d1b483105304f0790bd832bc2bb194157b215d265ff8f1fe5688b b6db213bdac6431e7a58bcd7bcad322ac7dc04a2ac609b2e2c1942aaa2acbe7b a9a365b5196b89e4ead8efe752dec4900446c49a93fcaf75c6e01d6155adaab7 ecbc8dbe05d49b8c9ab8538a959747d3c965abe9bcdb090829e109aa9af410bc 4ab503c0deb81e5867cb8acfe683c713eecdeb27bb51b5cabe78fc3d287ad507 3 c4c26506f6d9797347707dab7b7faa9def8e26e05ecdd90c95cfd0704de6e406e64cceebeba4114144d568cbc334aacf1637f198d872b3b9ef24ce091f64bb08994ce6c06b12670dd7790e1f233da881b966679d21d8803d8563004431c45e08afa14f7c60f0580fa2406542194a468a9b634ac519dd099780b7f7930edfc004ee20ec07d92a8e74f130ff63699824d78716ceb89c745dc745f6669c17e855038655b1f968c43a4037a4b2cc590b0243157bf8fb185d98d798ed0ee8be82b608c5d6965dd7b46fe833fef0e99167ab4ecf154f540055567d87778bf1820c5303b57cb7ff52f507c3f6dafc0dbefb37f1f57bf8074fb1f38e3c2e5b85db989a03 +generate_ring_signature d43a0443d5c3325225fa5986febb44adbda0422c3198fefc5c6f004c12eac890 48a03abbc676e7c1e6595e7871d3e47c65756783745b6c83beb3966c83b24426 7 54c73df736d858d956c6ecc78f71bfa621f1668529798aae794a2ebcc0cfe8a3 48f1b704c8549362e4bd701890d49bbc96f029f5b51740cd9af23be3c8e330f8 34d8123aa5765ff6fab0fc962b525c8ae0bce152a59c8336155f62b5f8261ca3 82f6e242df725fa43241d94c88c371eff160c9bca40e27c1ccdfd5465f8d1195 75ea08cb3c88c0dff75d744fe587524a5b3cb012c2f4e7299673dd24a07cd85d 8a3935e33e50f5f9ff79a529f18ed5c88340893bb688622625bf7007e15d5fe5 3261bc8eac8e3f468997a2bcded49c910fdf5b97056bdb5e81535665a23cd13f 55cd53ed388daa9179037a611a241494ad182883ba0fa337a9d6608a6f554309 2 f539a11cb38fa78190c72dd5f4dfa3250b89e2b3af919f8fe91ffa94fd5e7c0cbf0b2e9af4474c4e952d0235c131923050a04bab8414789a4c167ee3446d340404dd270ff04df4da5a7e301e9764ef56b816563559cb4cd2aff94e4566defc05de40d8a2e4ce90b661bf5a96b85ec8c7366b562966d7d3c4cc50963656074503eba2b2fb49139643dde2e53641cd91d53a95d373a4e5c6283a9d050b6379010c7ff82ee48198c40728c4a9293251a1c586c179196159533fe1d5b95341ef97014d0fd482ab810f60aab332adddced38b17f257fdb81788482a3cf02a064b2b0d5551f8a94daf972e7cdcfe747c8b7700f10c4294ef4cebf4696a939870d87e01ac8d9f55d19b9e1618a2ac7894c21672284758b3c285c20ee0254ffee04f550c5c083ee98a440821eb3776f15f7f96c337e3291c25f8c1527007a78ba03fe70e94c6d30c8fa0c7a331c793ae2921a53aed25b369f71e3e1c2964f5be379dcc0f49743115b9202da8862a013496964782283bd78a22d500767dc3a84feca7e10f80746423dc53a08f99db9f9eeb840c5c4379883a42df2a4543e670d55c23ad0119a2c04849ba4ebd15152c5236a3a61714106ec8f0d9ba6c8b908850c5c3b60b +generate_ring_signature b2a6d43314f32e184b6224f1d237368213d205e110a06eab248d8ce14dc66b35 589f05748c0e2fe74f2d93e8bf91c6dd062456acad1a8c440151142a36e37366 5 d1015e1ee08e69977d25d7e23692136b840a702afaa2ca04c7a1ff3d844ebcd8 bbde63dda3a37b486f0f12757b2e5a560d47a041f4bbba732ecf55846ef97288 839a000be768e6908d46ee701b19eebc912e346f98cc523c2d8ae63caf9c6c57 10191593db747ecf070bf88ee12a5f8c16f8ef506c6f5caac4fc0e0d0dd2b6d3 446c82636100dabcd3fd93bd36eecd42dc10ded9be9b37fd1c868ded993a8bd0 c188a0b57f2c52f5ab7ad0e98f85d05d317a631d20a45f2ff8d65e4dd9d00f09 4 020aaad06adf2a007b3aafcbf35d2cbcb49ff9be7f0e969e4deb02278406a80fded3157393258f5c7ee083ff744b34f887e8e729bbcf3c13336b6a1fcaf29009e8cbcf24af6ad6659ed9f5a142349d343ffc8b3573f055206823cd80f627430038591e3616bac855c3007806a6573083f609d32f3a3e84f5633a7465d0cb2d0195f562dfc750a95a5e0066bd112820959a64c139f6dc821d8f5533a6541b7e0d41d0105dfba7c3579da79d20052c022f3873199f49f046d350d90ff8b3659f03d8e2307a338ba175fef90ae978498bf74a6c6e0bcdd47e6b172825f04801300f09bd758eb4f229fc412951a87acb062d0e0c085362cbd7ff90ff8077867397095d7e9a10f2b8950ed4404dc98fe3547c14ea46ef33c69763f7c8a33cd09f7f083d2d14d277b5ed0918052d527ca9c484a16016f8d8885b57d4d14488bd8ace08 +generate_ring_signature 2cc6d7e8e09924db18a5cb8454d9d4651e68b7c40c10f2124c4178819c166731 8a63ba236ee52e329eb0cec80e8281fec17a4553aacee39d386e7023e8c49f1f 211 402a41ca99cd815d081c2481ea92da40524a3c0d1115380d080643fa3d9b0261 c643929caa547433cd00d1e0cd02b77e37f5de0bd28fd21018040431e47ceac9 989a1efbfa052314e3f222b0d6a1870ad23a7759208790c4fe0dfe84f9a682dd 28478f7385c01c31b55f667e04f71eaafc7615d65f818155e4da35a160cc4d85 94a4beeecbef6b573e9fd9b3e959fe53b47de4d0da7b9030066e8ffbb31357da 090eb96dfb16bdeed9a02046eff14c0b3da54c8d93d4708d336ad86355ba116f 7b77baa8eac66b27d8c3a7b0156fcd94d958be2551351438d075b91b63c33a53 a58ed775d98bc67e07945599536d3d2b4cc06cc37e4f230396218bfae0fb1c44 29320ad589c514098a8ec72b9349f0940737f2bfa36e9330f2d42c00a0120386 3a9a27201c7bb4bccef5dc6806d040c30a8230d34eec0d685a6c4e40751f49a0 13f2730c4bf1c2b5a9d35cbefc51d4f74de8ca3271033c7c4a9d0944dde08c1f 73872a978fb3ec59c4f7f86f5c008cd9ee9f54736b180ec968930983176bb464 a657bf78310f970b68ee552e2bda5ebe5da601b5245d2454a30c0c276ec18efc 5ec07e85ab34bcac5d5172a700868c29b9ccbfaad9858276df0f2eb75e142996 9af36317b0c92e94bf9f88b3cb32eac5e2022d67c5843fd761328eb8d2f85783 65631374959a3b4b0fa5538d510a13edd1f616c1a7b304f1916004695dea5eb2 3ec33d71054ad6db9e4c10680d2dd4dccaa408e8796ad3f04f63e6fb6c9436c5 eb7fcde5107a6e168a6bc7bfd51604c60aacf0ffbc41c4ee9f004eec92a441d5 4d6264610c118362726519f09cc02b1dadc926ad5d3a1d334d3203c42a7eaa9f 0053001c4a77bbf3aebd3956aac9b8420bc67d84947128c32d651d78634f2890 98c403100bc3d1135388a8899718c67ae72d86fbe32bf4ad6430eb0e5a871f59 1f0f066060620f72fc27072fb04d111d7890db94da780753c565114b28fc61d6 c7bc71f7658a142c8c2f32112d5f2fa2bda5140632b577c4ded16dc0e0324bc0 e1d43f558301785c73f060139a114b59ba8260645d3a42256fa1554b50d7d1e2 92b26634220d7b331cf050d947fc8610d91f129412b01c29969001e7eaa9b770 a6db9435005617130ddcfbacd5f432cd688054c7bbd5d6c74e35a6b87b36fcbb 8fab60e3f2b22ee77a896e2f244f683e0215baa6c22124227548473e8226ea42 231600af4603f92dceee9d17187e0218993f6f60a91205a361a63a1edd23f31d 41065d0f4a7c7bc1e0e1921ae8db77c6e8bc58ed6eea0cfcbcd8ed75b7a657e9 83db2a59e355af551f2385866b1479993bbf3297877032f14a53893db3c0eacc 1f286383aa01569fb3cee63a35dd32f41c575261cb68afbbb832b3c4eb9135d8 14157b52262d16b5b2b03e0bfb051211b111776d2a777872dfae2a1f3dfbf780 8b499e0ded8c18e93b012b857352ca03f7c53d19a7b662b45bd6f0ce0fea230f be7c42646fbf9d17dc2c09924595162813a1cd92b41ca029465240f105ff498d 5bcc54cfc168b31f99d15c2e23cbbb33889107430b86dbedae47393121c7e575 9115043833cbe33d775d09472a193354f00cca070b86e2f2c55a3c587ecdefaf 62dae9a21077417d38636f9766d2b23e8f31c11d371b941600a68123a0e5852b 2f880dd8a7785fb4f6584ca858b7a9df171393eb1dbb997e049c3c74bd5c28ad 36f51e55611ed2b0ad671bc3b5a67dc32e9424baa52b7dcfa886fd16e32a4a92 cb88c24731f1c01341bb16711cccf0262711c5d538b5ee3d91e618a0aa94ae57 774ee3b25c731b1563ab1a4023808d1083f4d019438eae312a071e0963320b73 80f340dcd9f0edfa672adf1028fbe5e7933a5f412609062951c9c13984477c1f 846e5bb742c7d2c5e540a41b112dc6743c145a76dac105de39dfec0c03804934 6c3731202f8f8da0d540616f32fb1261187b0d5dbb8f4f86e36324adb50379c6 304c36bc24065978e078b24b16bf8811bece463f53926d8bf4cf14141a8accae 6e39e3ac724c929ed1fe8d2a1bba08b81b757f32ee119a07f8feb66c990cb484 2fe3718912375419fde8d55acc1e9f7ae8f3e8e4f7e8c253f180967ce9f6e277 1422514f9fd55ec628162935e2736205f7b19562e10f09a24b671fd3d363e807 ba7c20056d4c14ffd0f93dda7c39233f2b9f005dcff265a9ec774d54a16f21c6 4e024cfd9598522249b0940e0a532f8a2e460a06db24702c8503a7b88d8ba9a4 8bb41c4698f0c4e1afe52d5110012d704e063471ae568e4ec407fd6a7f97f179 1e5665d9af4551c32f5b41cb376f6ec70dea9a3b7c8568a8ce24c525ab1cc65b bd2af664fa7374d46ed3a2d6de4295b2b6d6d241af3df086abe5b9ee7765c8bf b598e6304c34c0a3158998db4877c8025e27de6ea53305f445f774a7235c60b2 7817004add781de4c2bb6afdcbcc88165a8620994a27e96e822c8b599cf9181d 402afa1c572a3e19f68ec8dd5c3f2ad4af9ec3dd0685019da41b208fe624dfd4 e1ff43e5787e2832904d4f55ccc3f24a896a673b5841cfb82590ca8889074818 2b8afb146c7f80094e3201010ff0f6c0503d4c916219e7f08523b6eaabac0627 671fac59f003840250f4070a05f89d73abbfefa4f105bfd8f85f377db6409f70 bd10a4554ebcf405fad8a7371ea2aff0589d0c554e83dda524a04663a7d3d694 81c03045a55f1c812b2280e0cb4fb6e122466d47ec60e10975eca58ab9a13d1b 1b3dae6b495af9875c3705bf1ea6695e7bdaf39c20947e061eaeb820026e9920 d89c734cb3226910d0ce056da4cae31648f5f7689f4879cb440afe2b783cc92d dfe58bd956769ab6129bc2966895f3eecf98d596a15b90dce4d41fda89f51908 90b7690e81a72de5b64b94be37faf4cf2e053c0560786cd3832ac0a887b6c2a0 97f5440f4fd3fe8928b1da92089fa2f2a2cfa22ee836e84bfe6f0f0ac2c0b639 78733374ee6577eb4aa1fe1dbe30fc49a26ca6709515f9fb9ca30ecec256e525 0cc4bceca6c04f76c8cb0579cf41869b523ed6dcbd0a7cca939040b113c5ecb1 a77eb08d2f61991f1c5d2d5c17ecff62b6f355f2c184586458aaf6f8fe1b0c47 e904d9ccc3b5d0727786b623997e85477572f374a157dea7a2ec15ac0b8cbd5b 529a15c0030b5ef91a2b172d050ee093b6a008726a5f30b4ada8e51272c89e61 e484b6e14d164bde5bdf5e11ea0360f4b3751add6dcb86e0609060903db9508c 678782c7d1fec628538b8bb22e04867e1cd37b82fc3bd27af6376049edd52027 7b75224203be14b0d9648feb66944b90a538a38977dc9def8515fae3d3b65446 471cfb7f41e6716e40f1e43fd3bf60619bb61cfe9647bae7bdcf159c6c011b6f 9a9abab7e9ba931b3e07d8b5f2a6b367e83b7fd27b876efdc15ab64c56bb3e99 3907d8e20f2cbdcb064d0f0b5a9cc39869aca703fea645e487ae7569fca87229 46a2823791014ddc54c9d56e45f469871e4f14f33443f7dc337d5910beb1aa19 b30317411d74a37484df3768877d0e92b5522e5622cdeb19db037688633a764c d63f7433d699967d4dea7f0353aa6d83a1db6688bcf05eac535d806f2861136b 05e06df4e9ab60de679307e3b12231ef37c0a7e8bdcdbeb792a140e0236486ad 543583fa163484a44a43267e2ba34db90e9ed2cc570b7a25c0e45b22b9ca1eac ca42a5c54f83e796076e644a436af8565132ddc1d075b95ed355c7f2b0b556b3 f3f5efe8d35449192c8795d2cb1059b720d0e1044a3ca69b1f57962fc819d56a 89bfa2c9258aa739d4fdbc65fd3b6f0a544d9cf4b22de25be25b036e9d9ab2a2 092f55c0c3bc3a275c4ac457c0b15d676c8d5b2a072472b624f1874fbef6c372 11595707e8ea5783c592db350724d7463f8cc141fcbc60a9863ff890d783131e a381570de73bc10fb7f4b4ea3876d21bde840c0cd5aaed2c2521cebbff77d258 c629d6bfd1ea0dcdd46444c5a560a4f386735c7e1dfba644e119da62637e6edf 4605ed8bc6ad9c8734a4f9fa61964edb5b73f1746b568a3525b088b7e4e28738 d2bd09313210ff4eb285b2c1171af27b1f7d3a14cdd78da5d36d5df33821097e ddecb9ee20cc01e3e00884943e5b284088589501ac73c033648430b6403bb0bd f983fa409d58a694b709658f79f488051a5a3627a1f5d3d8ab4f29b88ec85591 11b78bd153835d415546c093859342a069c2f9179fd77487ddd724441f8a1d00 a1be919083bb6e1f0e9ec941588f3ff0cc80bef966f32c1e57fe51f9d740ece1 c5c5a8d4f9a3c212c8ab2cb9bea0fe4a2d1b28ef78d0d92948ae2bc58d879924 2ba7ee31adfff772224a6915cc1026733d9750ed78b0a6003090d01b5e4800c7 f31856d475a6d9e542a8c52940e65ae76aaf239c58c7af445011118479ba2e97 d8ee080399b781b97e9e320be1efed8826ab3332ccdac22c412b96b587e49f3e 046b156f6016b3e8c4e28ec46e6a894a14bd085fb98d272f954646aa6a68cd8f 67da4100ca141d0b335ce9aa673c8ccb1c241d54e8e2d33cefd70fceb5b1d25f 93bf5b1c7a6ea142589637ede192df057328a7ff99325e8d143f5933e0e8d094 2013b7002841ce5b4c162f543ce204e55ca37c290241703cd444638b7e1956e6 e1fb9f7f35c2ce2f064db2652a494fc4132d67f1435d2c422651282e2d5936be 6b84444a3fc1f4ed71f07fe9807aeedc3d765e86a308cbddb549e41a07637540 6922d129dffb6f2e7e05e25d67a675deacf4a3eb7591cb8f411b4bb02fb352fd 807fa83b273eca0d0cdc181b9839f806164eac0da439354f27bd4c999fa3e112 ddea38959723583da1265d5bbbc23fd21d95912ab44ab7bdd388b8f816d7c950 d31e177c298fe21d24cda414ce09ab970fe07a16247afc1931831858f682913f bf212d78a9d4b8141c0e1e03b553c0be1bdfeb5b17cf2b9b109a1dd2f08fdc19 07bbe8caa6552571cd4fd46f165a89c5eca829e04ababadc29fc862625ef3415 43339e682fea6b57b40f7c199386f392488077429cb491f0380d649a87324411 46c1cd36c1efec4e065c8e64700fa8e225fdee7e7b5d0455184acb459785e924 09ea83b1d7d5a7b580b54e8e95f3204374bae8ed92e1271a8573d9d72baab4a1 8a7700e99533fcbbf456c9b22d519a25f1f5bc3099c71aabefee801277d0c19d 909b328ce25f2e5f270ed63fe45b43d242c03c3f74f3ff6001978ebeb418cc61 f8e3cf614e7bbee6985e1d5604dba2374630bbdb9651e91dcdad5ab53409630d d6052da08cf10157fb2a2e3b44c85cf76df325fc919c588086972c6bbfe193af 9a3b931fd12fd1f0d68fd76d68e44ac2de9f0d82fa476f50f0dacafcbc38dbf6 f07f3e41a4f7f79500cebf1b351ac5cf8346b068ab1cbef602fd60136b20cb6b 15f00a7af5e20b918db78299f63ab9ff993ffd9b9c3e50d32b89d8d68f1cb6ea 0402a24ffea45621cb1916265651c80c570a55e8ef7aa8577afbe2b15f3560ab 0ca97cc9867b2edec0d46d9d0fd14071c4c904025fd0c5ceaa5b13391a7303c9 755f312086e70719cf72de5564ccdb28288ac1fea0f41611f5437754cbbb360c 4f865ab94b9747b5219e5b62f06c8acd434b8ba71f9a49656bafef74e0fbe000 eacabe372936f28985b08db8fb4c2f8c9b4a48c261d698e77ecae3c3232c69ba 66e7c6a7231b1d3fa2771babe3dbbd42f1d32cf5f80e8112dc65ac08ea630c05 826d230e93e779a6e5d9ba481cead54668b91f7934e5a47f6d2067356d88a432 38d8c90a333e211e35d18433fc8e97a2912a4481a31007dcc3cfd7d02d58e445 b89ce70d879d1748dc572e179124da2922b52a9561c4e5d4ef787e65c3092bc3 dcd03083e2edc956b86afac0b6b72eccf61a96c0b9b1d8c49ed5fead44c22164 0b3b6bc0b8dd2aa42ab1909e31585da82da55ace9c06a9075120bb1584c4e65a bd7ded926e2626afadb1ba4c6a6484b02fbb328965bee31607f24c1f11cf35bb 1b822414e321327b717a8b73a68759528b22454ae4f1efbbea4acf27553bc7b7 6fd47667514ba98fb485a526bfd3eabb680eec1e6f6ee9b0ea242f051dfcb62f 10eaafcf82fb0ed3f7ac45f994eebacf1794bc7b484c3bcf1d4385534a7ac8cd 2f874b03e03e634e4afd81c541ec6061d956fdffa59940360b6be44ba08baa39 415a29f704999c6a1d57de28f50b6c8a28aa3a45e0f090971f0350169cf10442 65cc454756fe9f0afcacd7ebf5ecef668966b8d0a689ee9f8a80a0d672662d93 ae853a35374a5f088e4811ce400ca24b7292209f5591fb286a840f811a1a3a4c 13fce6e2bbc79ebe247e21edb3221333aea417be828cbf7dec641711cd009c0e d0d1c7e0df37e6dff7fca30ed034b174b6395667162b7ec2f81922433700006b 11700c2375553b574770775e7ecf74d356024c6cdb73a12040f899b072febab7 9f2045dfcf371ec181d645514a7200019f6917be67e5b29b27a690fdf434720e 9211009b28ae0d38739fda0cbc46242b9c091dc864e3849463f7d5cb6eb4224e acf85b5927f99c616e7f0119c2635f9e56689f1cf771e94c29d2bc6af30e3b73 fd8889f59b8fdd47277bea541ef26a7861a890898a15fa55f19df6eaac438054 dc0db4e6550ec7cd5ce876416f54c3bc59447b83c02415fae918adea6df7a4f1 29f5752cd58eb91887c95c38cd3691a9b404c99c5022e3e644947c58a62405a5 90d99d090249b1fb30482224f3c6d3dbc285c8a2f35a417a1a4559479a5af592 81c3b1b54dce4e4bea68f2cf57d5d10c6eaa396eb66880590a9d68409ee96e3e 8e78d814062170b9da0f2df53876e50d658e8abf5175be44f1a847b25456adc8 5c8e9a46ff66124cb418455a16ae3b78eded90d8d0e6b87f69e0d2dae684553b bd3bd4c32d490375d0652524597c89eb2a8dcda40278e9e17eaa0895fcf3cd63 37d269d95abc4010b6dbb4a1f13b0081701a625fb496a554ef50fa790503a0ae 069716673161003f4f4e03e91e61e4c35307be3285e9b9f4f96fc87d9256a112 3bf47bfecfcb4f13b54c8a5388623355fff92f4e991b1439ef4bd2c04c25abd6 8322ec655d08bd2297a67c7d218f22caa679139679f03a0bab4c0c07e74f5a59 4c7ffce48eeb64c719286644bd4dc84a6ca8a09976cec4365ed8a9f1e52020c3 1e66c12636713e73ad0661b537ec450f53bef1f34c814f53c780068c3cee944b 3606237da7504cdb79140ae14dac5e9970763a3c9f4a7d74a3721c93c7e1431f 1fa3ee9449761613dd39dd33e274e6d86709e83d747edc926b115659fab77121 14c7b97c2462aa3bec7a691242367004906e2b2daeed42dd2ea8c0aac75dbed4 70c011309fac84d7b2e71fa0702b49b21fbfd75204605c5bacc0c74dc5e8dc33 189ee51670246ce6697a111207191553afd5580f11cdf6859746993754e7ff80 1131c9a3307f39ca82c6232e5852e4461d0ef37afdecfa26b5022a93a7e5abff 9b130da9fae53bc05605bd8b9b7a015713699c5be1f02227a9c74da3db31a018 2ae654a588871677c065d03dd1d1d4232e1fb67b191a32573926c62d70ac7b34 a1a6f173a6aebf9df3f322eeb425a596e71bfbd8b75148232328b593059397b9 deb8c6156bb9b1cdaddcdcd83b0286e746c59d2888dbfefcf77e5fe1cfcd72e3 d007cf856dad279f734fb20b0e5c97839ac2ed18a607f8581b43fc5b0dd076c8 990073943718cc401ddb885dd082049c0168da46cb6568a56a4b287c9d959fa1 dd5c9a93ea17ec7174a091e41d91f3a03666ebd3411e5ea74938ca33b05dc162 c182c5d4f1d8be0d7a5f1b6ee8d126b397fc5d7fb52dd58b0f42f9b1d65e5d55 7adaeb0744f933f647464c92a39bf738067f6e1b4b996b4f2919289f2fa5ce9a f9adbfd726b517b55912e28c83f19d182b8df969ad4b12347131fbd7e0a8a2ee 0073fe5086e6319f79cb9243f359519f00da4d3be8855fa79f4233f8a50c5874 a6c4da072b6da74e25de82ee19976195b8483fa6a054856792d89b1e1f68ee44 d5f638f1b8eaea5e2da06bb90bdd5466830f036e6093df3bf23d03145d315529 1483358755a29f66d238dde5e62a125d50f6d22ce820553e207172859000a96c 72e77707ee4d52fd0ccfa34c57ed5d2339a69f39e6aa6f371080a3d414e25505 b7e9e15b52f4402ca9fe210563a213e1dba8bd02d0da57440a03a98980550a80 4b1480c24c531e5bee169e7ce570d89ecb0f0c7b3d041fd93f04abd1ca8bdae5 682b6bfc350ad26c92b229abbf92aa82a3d1c789f124c63c166555f4f960f592 94e2a267bcc92476200aa4d5fd0b3d6fca8f2c6a2c69623a22931b8e9242ea40 8d09501afab7b3142a44a531971cdefe348be311412347c0ed1ff2be5bae723f c6a8695fdc9043bf1a698ee3e2df46bc211ece66e0a5215bc16c8db3c676ae26 fece9504781b2b510ce438143395d3ce8f6ed69dd1179b9615680746b4260b56 7e43c14f5b5b2dbe7e3b37f9726c32a04244507d09cc1056084afbe29b97ea99 feff4768795d67539c95ebd9a89f8482775f595d8ef3852a988ab14fe95f5227 e88d342f5edbee919220c56ce616a548f801da5186ecf00a3fae3ce4eb163fe8 bf8e39152682f122fdf93ec892eda4246911194534a15293badb5e09e3c110a3 6654a6a6b4a0f3111b9c1bc051403c3d0a519a5721718e370190d833f63a8b88 a05d4624f0b50a2d8cbd071ad862012b3b01e98b69969f9e5d17b7660396eb4c 4bc3d8c6921685728332e49f6f4ceecb659504966c1d3bc49e57a79422c1e35e 3c497afa0fefe3026683a39fb701024d7ba204a2077ec0daf03d6bb1ba9ec005 3034c04416c57bedd19c75e57f79e9330bca3456c43418e5a6bf002d969b425c 5aab8831da8f0c192081f901a62c8768ea530c96a3b249aa9cd0cc83d404eaf6 ec54beb7e480aa22d6fd6f0c785e45c7f2a191bafdfab0bb07d5b48f092d9c5f 9839537dc7da3a8141b41c159b91c769d0cf2ba6e68b94217c602ebdbfc6a599 ed86db43e4a4868efa115d887acf04af9da75527a4fcd3c6244b6c4d61be22cb 8c507a0484046c536423e8d9af4e35f74ab7d30e0f3d41378b95da880febd57e ffd1a7b87836064b4247e77e6d4e88e944ab3aeea5c6e069161227e0d540482a 038230489f2a54958eba36e8609ead2529e3932f8b89c4204fc21ad19920adc1 ef2b4e6713f76a01478aa2d01d3caa45467de753c810df2f9b61caf0a526b699 abbdf138313206132f30d3c3376318e3c2a1754d95ea64e14b22e60d8dd8af54 5d3dae9e4dd24dff168fcbe2b21ff0313df82a1180b7d7aa0cd4aa1d9584bb5f 1df861a9b7b185f0eaff1c005d7c2bb89fed2ebe0f32f478d2f345bd678b49b5 829a61bcd2a3ad8a8d419c5c8b7fc26f2d13428fd97b7c54d6f888255640762e e6fe97cc23d26246af4db30b1860072a4841915bea8e575ddc5d7739a6b96954 7e5dde760460e9b910e216540728b4ccfd67182c070c6ec2afff9d1bcd0674c9 cce6c582a6b4d913727ed464544472bf199cabdd81769de5009814887650660d 170 ea82e4ba658f34410739c4febecc7c08078e0b62a6fc4597509894fa651a54089f3044db749f92a8ccff2aebd6063c91ec6a1f83de8e0808b177b70bd7035a08023fb3d69dc9940498d6cf2949daffd3171934aab529b7866f9628cc9b0eae04918e5dace8731a07508f66e4bcf9775bdef966ce4cd393c8fb71a25437d53209cd3b7521aeb7ad211a8c2daab389f2c263cf57999278d9b86e67eafb3c2623001c62aa6ba5e6d685bc8c579580fd3c6326ca0b267a5c53fb11eee3b7dd9cf5009106dfbb0e9388a1474258baa3344b41bf28d0deeeec9b643e974d92f6291b0bf6688b42199aaf9aa0897dd9e6a9d683e9d2601512cccd6fab39c4b93b275a0ce319c00e1a6ec063db3059312d7ffc149c24adaa59c42e590fcd332371fd4200da0011ac335c02ad7531056a58ace93ccc6479b5d6cdc838148c03bb01047e08da80a00977a41e2e139f6b8b899f8ac26fde88d7a84b6e5fd1e61833e4edf605d76b9c21006d29ace962274303672379e9a089bbede499f65b3e2d909273e40dbf3787444cc0eb17554d0fb392d57f437fea6a79042705eff3de784fbf60740e9b435b73a1d249eefd988647f3a165e859209183c36a58d60f4c1540329ad7078ae62561a9877795dde84995587a087653dfd767d2f176053bc4b373b62d7e0a054e93fb84d8066f0aa86c9ff4e31f4cce6835e3f95b3830a2b447610d36bd0354c98ee84bc7c352617822d9aff35fea5627c2231dd169672697981118cb3804afd33269e443c2ae2a8f1776c606028e4c6a04926d03b9661484f491f9a3b708908c658abeddb582b959c7eb022941f8e62e6cb7dcbd036bbc3f5ba8b7a2220054ecd2798166942946b837e1a7ca4f362a5b8aff3f183b155b571eca80880a0518b6340d2a77dced510845165cff01440f68397c53f61b409cb715b146714204c95c85a9c955a15df3565ab322eabb01632a4422b3fd83faef6ffdab3a2eb602098b926a0e0f70b838bb57d40f3b84012b1c0a5f8116b652da3d66c335d9cc0ebdb250bb55b877ad4e56a7b40b5907761f3485c0b56c6ae15892032165b14606fba3426836f8f3e7329379514584a6da97841f77efc48d86f9cd96c6e37beb0b1b22df1225abae4c98529c6e87a43a99bd5326c9aede7b1e664e0796e6ddad00e20c0a23a6d2fd0b257676bf7a56b7ed86b1b7b7bd36186742e538be5b809c0baf17056ead2c67b8da3a7cab6b508ff3ecd2b3b69ef7cae476e66369c76136065007b911a3e0993a156113ae5947962a5729d455e555f70546bd61821910a80f7081ddaf56841863c970ee7bd52429133eee999db65017867f5e5f2a3b7be3059f9835ee8ba1edf7578cc00cfed61278e8938809f685868acb94d6f919f1230ff5c34e89a25c04cdd64b892ae9a58bf75f5f9c6bb701b7d533a53207be0b930ef088241b033a967c4943d7338c71bafaf7bee5a0e58acb7d772b310ece706f0102bf10278c0171ea744e663b04accbafbc2328bf401aa1d5836408bddff5850dc3cab068a3059c4a68934df5aa446e3371829718357095570cac8d82e4132509850d421cbae0f0be02d919888c5e3cd32dcc1f4e883885c12d2ced8d740539071653045621f6f5fd88b7f41677ad932e4341899e6d5c0d390c769dff982b940278b2635d69f3f8bc4dea693058de53686bb6f7771d2f993cd0fd4eaf8520410231b66569fc70bc8a3b28de7e793e5dbae35a12d9c8b01fa484c4e5c2fbef4e0659eff7c948fa96fd9a3eaa8c89162916ae13c32654b6cd13c4d6cc1fb79cb70b5933434543f90e05cacd051abb6035344347334c254a1a940cf769b079a68901fa07b639ca3110f56c9f1ed5d6ab34bce27d9eb0fa5d5adb9d2b3d54424ba70a6f64b710ce76f4f515d23eddd937fdbdb4fdb35069bb2fc065df2f5a488f2f0f595ac58ba79ae6926bbd037dfc213c57a4693cfce1a22182ef5a703b07bcb80a93dfccd673acdf5deb9c266f775c9f726127bcacee35e0067f90749d994527093f89199b67b7aebd8c76cf68c769b64696d51fba4a8efdc7440df33de2214a05aaa1038d5608d57fe65af625528612fb46d52e3b64f47cd0e3d5211c8291d6057570ed14e632d34e0c67948e5fd9c97c7b582c70186e5bd1103b93357fbc2b08633f5581ae94ddea01f51b46b5ecb450aeae04e48e1c8bda8944143bec41260fa5bf3b24c9582632a60fc6d9921d64a69114ca1c5135cb55f94cf3c74a0fce0ccfa960df7e7f6a393fbd4b8ff87886b9f91d13385fa20a9df466d82b11ed54027004d239b763be04fd871018695af7701fc91562f89c485227638309b0d5750f0fce584bbb68d47dd659ab095e168bd524516ed76c99704d9b34a84962358a092f72f623fda9b89a414991030ffeabad1525ee40c5b651ef10f564405372840f5548955932df0305be158029800af45b36421743dff718a385b620efaa911f0697930d84096ebb3e0dd2fc60350e210532b7bde44d83746eb1be631c555d8e0ee4c37684906af04102cf63ac460938bfa90302f734f5b60c283a2f885ebb0e08ab5aeaafb2d33cd9ab3363094329674ccbccf7c36cec4e158a059d7c338f9703caf25c88f2bde6105fc530609f123955934a98b11faca11335b3bbe0705e8902175a8d746200450bc84f4d05b0308c9331d29d9be14caa5a2edc0878be424906e4194a94d7f8e14b692285c73da0fc5697782edcf85fe648d691ee6132cda8053464008153251c2efc5ad3c295930a1b6a33cd62def4da8b2aadfc88e69e4709b7afbc4ed1495cf3adcaf0333f22f11e6a59906605cb7bd5951461f7915c6d0d7d1710786973745ad7920568de3f1f266bd502ec31c9ca177d3159f921168306243b5363814c6b068dc7c96206aad242c6dd3dec185964be40d8ce7faf267d0566b3ea1f3017e5b12769483541b35d349d7908d16db98d39ac22a3b6b72ed1069cc1844ffbf2dc5fe2fc75e7f43a8447ffe1d08835d8a261303c246dd55b8102daa70651c81d4d31b2e2f9575aba18aea66b277d394a8d4c78c22863ea2c2a03d379ee502535345a68805668c7b99d3df69c74e5ba7959836646572c7705610f1ac3e2468e373deb7c134d1e979d5d2c05ce4be03987d264af8829068639490494cd984d7cbb6d24ccb5d900aca12928df14633acdec01bc4332a70ca195ae025f88a63240253cb37ba8864fb5194f898a32331714af54ff44cef61a500ffb0ea0aeac16c521cf92b344a13407e6dc3b266b5ee1610321b7de87ba4dcbded309eff15f61146ec007a1da5b07d28b0e32a81a76e800bf8c9771478c2a614a080b03d3daf34488df7510485a63d47e5d03c852817a924bbfa02582846fae6ac908e506581317266b3404885faa619b6e2907438b7e1cc388c56b30cbc44e93af031425c55f84316d1a4033eccc1f514ad544ac9e84b80e0fecb96f56af70a17a0d3d2c03f640d4856ee4410ba5a689c9ef97cac5ea9e91af40a786861fe61b81013b8b6b3fbe4906fcef61d38e6163238d141be7e80ae1d438c5b9f19fbd4543056b8aa7ff5e4f256a1cf1705ea41a100ca4ed369017ec71f3766c75d4bab01201806c87789ec0f1a8f9339da9699ad530cc3cd558d66f4fd3397fad7d91fc8c0c0fc8069473ecfc66ea6713698a9d9f21d55e09439eea1ede9d0b7b503c7b8703a242e0b0d14acf2e616272af6286de322d8aa06e49fbadbb16c1e245413fa40b19a25937b6768d5f6022fed900e125d27415454a6f0d3297af1ae58f41ead50a4309eab598976b5b4f31c4170dde86396f7519dad2841e059d0deabb311c1d07764988a77e1d51b54f79222fc0c6762b8066393faa7ed9af08de137dbeeb3d0c8cac5151a57655dc21e603eb32dbf32e586d7556d8795830e3cbc0712a693a01db7932a4ba68960814e94f11d0e66e6ec480a951e6f71eb728817d1ef9aed90cc1fe35ce64a578316ca373a229b783f88e7fe52605fbf02009f34935bca7410a7955037fabd3de7023342b430bc36b50a8ae68fbe346898bd685958aa8dfd50f160346c7d7ece3a09685051e8e59293933b64ea7f5c4656e17dd39df19b24303a3f8a001956f1c6bf2ebc9bfc6dcd14ba1b3cd78a3236bd317b5002d1189860902409a36b36c818b5e98cbfc324eb85d71bfebe59eb2cdf4defb40f197c2fc09085313f3d580a2b0b31fa62c8aead58bc2490ef88a1cbc6b8b344e3d2cf29006acdeec7ed75053b2a713b3ed4bde58e12290fd3bc49b44a9d196e89958e0d80836476e3de556445a6eba672f1ed2d41517f6e920b1797cd5b1e9075278db180bf8adbb24027fca68a5b9e536a22023ad09b1b3881e31cda17ae419ebc3d5a7019fd7211250fecf4af524771abbafaf813f8f8c479a763d1556bf3f1ec140a806c3e93c1194f8194ef338dc73c71288377d6fef28a6a2e02a01daac2a4c541e04eec3befe0b95e0078dc38c5605793dae820cd99446c19f5074e9d19222eaef069550de74671969287a8effd874089acf1f2184b01f0d08322e957b188090de01b26c09c842b0f2beed7c491a8e621ce897b13612ac2c6360d19758c79292b207e2d3556d75fd53719078ad3a112b49e86d80171e3cc97ce7fad27c16c65ff105c9515d4a4f17a1a1fc439b053cfb8cb2117f2847387cd26c719a912818c67e070bb80992dff1f4dd64e902508b7ff33fbf841d2427022fbf51dd4aaeaf3d6c0292a7c59e6a7bacefa10d5da2df89f79a443f840c7b661e142e996be43f5eed04ebd89587df6e2b917d45ac6d9f4d275ade6d0824e199b4e4cdfd76463648380c4638de95def2bcaf1cea53fd876e3698716ac20029adbed1f5905b400b31f40eccb6505e6000e12887f8ec10950e7d44178036da57d0a298527dda5257ba1402ac2d6676bd8ae155e4beafefff3fc7955edacd2f31d688d5e23c170dc08d300be162f3337fbdee3266b72b3dd9d0db781996176baa16524bf1cd90421bd6dd01ddc9ce76f6d0c9bd2918cdfa202ba49b18a815473263e1e2f6e23c54390c4601f4cc49762c2c3733abeecb4b0bf00f9e540e2aee6b45320a755e7ba68183480bba619c4bf6750650668678294da95c5a170a00827d62199a73884d2fff3ba6027586973b62f6c4a2bac364ddcf57e6c3c61968d6efd6933b1b81d3a1d93963093c4fc335fc0f0353713d4b1c8b25134c6f800f1b5628ea1361edbb7077819607619b2156b5e161426f7a3fc97531a655632071d843f573ce9aa2efd14e5d7102577a07c306685b0e2a3214f01c7d125270c167a3caba9f7dc0c6bd37512c67061318180f4b5e470c42ad4428f8460ef8d1399cd9e3a90a24c8b366465a8b7c0fa3d5dad8b7ee3a4b177d4400b3d84772c535d0b694762b6b47475e81dc91910e188f4c5cadd7fc51c217eb99c176aa78852f3c45c24c1efcb46ebf38fa53ab018dca8c99bcef24c46009c1694756c70e88750ab8813297ff55f1914f51b7690351d86402a2117ec62963ad602938c6f05db866f9667c9a4445cd86b1bd518c07f54fa73ac39967a88066092d369f2102584cc09683860a6713f3612b87e65f0238684a953f79167b78ebcbda155b8d6ae0e61b01a03a15ff2c966936e34efb079831ca0b7b9c92bfc8cbdb583245a8f6a6770acb465f6c35353d11d43de9ba0b0a45b1016cf76d0e6035b121a864e803939c85ea3ff7007c4b8c3c204da17b07d82b2c6b85d2cb097649b777d48d8bc32e67c88bbfb6998dc222e90a6d8f0500eb8ecc4de7adc6ffad76b2c864b714d5b30242fb8680c49ecbd97abf20e08d05d5c80d607e847c8b8f08119bb58d0aa1558a4c9f40256b9fc6a41f3e1f394e082fb8c740b095a5809348562218f8bd8409014d7f5ebfaf53840efb0138d4650bda7e9f0d768d70e4f133c9a9ddb1bea36e67df24d9de249bf058e53a151860091e3f98160840ec50174f45cc3d7ff2de1f38c22e5a9afad25db629a7066fb8035fa6938a8d0b07f9653f07517d9b9add7aef85de5bc7378c42d80cb517546c08fb77c47567ea0d6921a16307f1fcc67b2a022a04638d9de5c4b23f338a67680c372794dd0d2625802d623a53a228828139f126d1cc2db6fd1e13bd8d414c5109e99a096d4f91f2d84151f8b9d18f9213445fcb21a5016a530a8f61074c67d904a12b923aecff801afbacbc6f40d70df9fe568e182fd12e1122b9c87164509b0e86ed0c2fc1bf3865d44ecdb861e4564e34f95ae61117031d7054ea9d7cd1a90c949302dc61928379c496c3edabb7096aaf68996451c4c022ef143843156d6e0929f0fd47ae8e20e072f35e4c28b6e208686d7b45c0f45345b01b4f3421d0ee07037f56bc0446ace25fcc393aebbd984ab9b1a07a699a2ed18ab01aec144b930068383e648893ee195136515563b427a94d402ea6cb0b3a5c184e2c19945fe109927a7fe2b2e73fda2084d77b34e6972000eb161ba6ffd63f84af801b81f799091d9cc65d4307e2b1d307c70e4c72c38f6306505fcc66b8dbc6d176f9da92db0cf82b8f13050a1c60964bd38bd63391c7bfc58980c41438e75b17515dd338250018f039d0a1f317dbb3cffb1cc60f35986fcfb7e815b8ea93def314457b58f3087c0cfef3a6793527bcb11af4979557df291307cba7b423773e6484ffec83d908690fa62df2b0e24312004abfc2994eea1a65a4ead06018da44cb5faf5597ed0abfbf0ea5fe176070399c41fd7d72d4dfd8060d0027db4743a4f9cdd5e445320d60679d56bfcd0a2bbcf4bbdbb25a97f1ba3f858476dae8ca56e89a7f3dabff0151fa98c7731e3f7124251ee86b61fa24c883bbe593ebc1dedb906a612bbd810ae90b978ff0083f1ac026f7c0dd9bcce74637bf3ab2a34367964aa0ec2e9cb603f5dcd16b3b4031af45fd01be83037ee6f213c8cdb9ebffa986b190f571886f03bcc5f533ed810015d84603e0f33a13d5f759d34875429539c75548a6e2c2740eeb790e35a90f4d4192c7a3ed18dfbbad5112b54817ce3deb05f622f683c70a08386b384674f84588abe318d7031bff03e8c6f7908397d06c15f863b42de3270d38486faa9549bf55ab14d6b9c1249f1ded1b0a02eeec5046c45fcaead486ea07387ed579309ee7baa2611d2a659a42de78164b008ca2652d86073080dd4ce7021c11c666fe1649d4234952e7d30a9eeb280c4e878ae2cfaf5d226bc9bce0b9098e26aaec993bad328f8db4b24a0a77447604d623a67927e186236b0cd2c1f10f7f28d1ed128dca96f6575846bf1435fa0f1d6cbca9ecf0eac2288b540ff26c01d3cc88fcd1ba73cc5f43d9de3abe308cffd736c1935acc62ff7da34610c2f9002c509d32eff88e593cc663d94b1b0504a00a9578573ce1617794994c32213b0601e782d5da1a630c3f79c54d1e9360fda61b0af5db28ee79b2170639d67d2a0ba3bd4fbcd14875353faab2a1b8c251563220c066f386e6c56477378044f58e0275e4cfe864d6eef19f34b7da898806430d461fe6df89448ec9dbd7c8b8837f09b72f4fe32f0afb9eeae48f411752e140f0f27f37856a27d2791642afa96d0a064a18017d1c2f751e92515431bc0972eafeaf4a785a728c557ca711d3958cf50aa4bc5c088f738d05bf2d3d874f8fec8f0b08709386792c6df3ba6c90deae5a076205f194ed9c89757694fb62abfcbf49199169d29dee298053c066e3b04c6a05e5cfee89cd749cdf860bdcdbca95d2cb9560aa0a7f877ad40ecad7a098944a006989be2f14582077764c54991119a2c8fc026de98da44a04af8630aafe28f3041730337a2f9b2854f58493ea965a713453ab56a5d6cec461fa3e5673a9ed6903fe6c77c2449257b2c2a4e6fa5bbdf0cfa7f2e3cec0626471aa2fa00c842bc60d0365a1b1515fe36a53026a9ac6da01c508c50196bf6869eca5f3d2fd4328f308aa21b3d4e736dac9827ecc5eafc34df0d5d56ce1774916c44b5ef5e6eefa7b012479b8762877f381b556b9d22dff3fb16013e573d957b6bee4347e11f8cd340d7384c733ac852a6c41eb5d6bb314d0d5cb0a9b03903a734008e31f73d6532e008bfcd392595df1c73e49642e11be863227a941788b115ca7155d1783f2d44606a6d78fe1a7380b4f0307e392726a5ae2f34a57066e8a21a63d4ac0fddaee53031c6f98afe310e0b79afd64b01dfe669bf8c2a932b3c4db36f760a459b101a805ab6ffc9a717bd8613ce14d0b17ae78ab119403a154c9c79c26618f60f73e730d99305da08c4038adc745f7f4d2fbeeba4736a7c7c9f170f1cf9d31e8cb16db0acadb13ff9c9a373572a45b05551a3a18cfeeb365f77f0709fd95a39e144b4a055d5b0a207bb93994acb3deb6b1b8132508c3dec36e04d30a029291a7a9a47b0872b5108073c140cb4f3d69ef34096558671e9d312b6f769c9c022e76bba119069fa4fb636bad85f73b1988091c2444ad31e69a15d1779a9b13d661513537de02a10c1529992ece48dd9ee96a20d80006397a61fe6753af5b904c39cd5b3a850fdfafe46902859eebf2719e0498605590752c66f08b575ceeac2d3cd3baa35100b5ecb868b30adf6188049e0acf553779599804427e74cfa6451b782d420db50696e441326d39d3b3b52ddaeba5bd499eec4e7ed35174e87a87b66316ac06750416a43dd86fa575200b1ce704926ab53da40ae77c63dc87200bb8ab2a90ac8709f9aa861f8bac52bd77f8795753dac43e5030cb4d9575f694e87b9ee0179ebb002c2f4172e893c267fc11645ee3cca1d138494a7b87be31509782f2f9e1e05f056e47b72b06ad3d33eba0b7e37e8ae875d5babad59825de9d478673cadce8f704e1724fe0c47ec64a6f1c255babff74d212a95f261ef4a86febe1e22cd8ed6b071742ae32c3b2c15d1a4f4be8f452977c2c7290c64e4e9e59feda172071c30e06927b87a8f077181a78e088ff18a1b1564e65deb3042c88aab6b4752eaffd9b0298da07fbe75c5b3f64d6c8b0850064f7e7634cd0c3b74be6ca4c58d2cb5d8e006d317d4ea6d908229d00b5c2287bd9425ff34d73ba37fc3fd5907e559886770ddd93a9bdf3406fdfa139df1a5e8615db82ccbc53718d5b5f95dd8e906386b50bc09c03eaaed033b2fa78455282c83bc7e351c1d79a83ea148c1a3a43ab9dc30c53e3c83d8d877b677967b7e66f05b1d52bd31da93b789645d8d575461ab22f087d0aaa449fa2feda216185ddfe77bd3e4c9bf497527872830ac737ad0ba9bc08d3076440f4bf138b9484ad88368272005dc697228a9d1ebe5e4327dd2d5d2000d73c52abd3ec4989357ef79b1ee995ac39f6a3cd5437cb31983d39ee4328a00eafd708a321fadab321472844b13e8182a55faeb4f628c47bd6c90c959d43f8003e8f28ca6e18cdcf7fb0ab397071c3cedcc607cb8b56066210fa9fc96702110c565eb058e4311604640f15fcdeae2231dfe90dc8ae089895f2943b902625c7051a59db64358d791a3ecb47b4a7077398d315266e9c77688222df9c6c97d9b003a168a0f5221ba3fa362dd07e015c108ca61de0fae55a7b1d197e336f0d6e740fe3ab219d13d478cfb9eb8a21c2ffde8851f1f9f49fe63bb47dab567ce7e30f0ca82acce07a50099f904e4c2d5dbd3efa5d8242a010d84d8e85f1b758f186820f3632d051654478bf5f107a90eac5122cc0c77b01eccbf54cb4868b7ef78dfa0c2661f6372c71cdc8f878035268f8d1cfb4831cdc2b24fc6a3f3fbd2a0d30230328f434088d51533f3bee58b1a777100555ad68b42cb64e4b901f99c020f96601de78bc69c2e6304bec0794cbf8578fc6bbcb2fe899355943b251a27296907208008497cb97f2886021609adc12442689fd770dff2034ce32d992abd5319c090b8fe62771b4f0f40f338e8b9c911bab369c7a7cf853ffa242438b9467011eee09d568706749cfd61211500ae8ca593809aa02336db24bb1fe3fdec111608cb90c9ad0c802934b83de3c628674121199674464a90578eab26005ec51cafbf3990bcdf1e6f9840161fffb8cbe2c2fa34801ce7740e6c2666071103bb82b58ebb805f2d9e7b782f7382a7f9b976737220f8990e6076a1109d08d0bef610709918803b795c4b159f2d74d67219f03e61d148e16d8fce8972ce588b6f22dcc57a85d007d07a76b7f6ba929570ab067b89d58c285fc5e9e9233305178a2412f6fb1bb0d9d95ba369fcc75e9a4b70aacd44de01e87518f5cb960ed55375d192ad27f2f0faec8b8dfa0b15de459ea61357a22a4fb13b3167d06db99e0bba66621cf84390a27cac81c8f93ce9a2684ef8fe6b170c93fef9ac11a4af074132406df027e060de58dbcd39f18473ebec4a3393e34f297eb606b587e37bdd7463ec4d173bb5907c03dc919ee453c71772fb7420e1d392091cde9a37f445ebe482ce9fe8da89008a8dd9300f2c041e8e05c9d7de844ce06542936d3625d47444a4eb9b0f0b61706cb539eed9c93e90caf7ca70bd9ef30f8b46b342a286c358ddc4e35069b473c0b5be0be97ac44d7f96af292d0c46eb76bdae7c8f171078f7d724e5da56b0a0b01bddc6b6af7e1a314a5129593f916e4eb5a17d61131cf3f4b42a4870e2c856c07145d3c065bca131e4c2655f415b9a5567851145fff7f66c010c77a0417513f05ed3373e6a09e735999ca2e7ebdcc2b18a1ba9cca0fa9b3dfa4bf9f234e50240bcc5969fe08085cacdda15a1285283f800323889a3413f9278348041228aa5a022449c23cc347b2014330f46b983bbc4794f7090ecb28f31a7a44a017d3e4c803d4dde349b0c38cad74627dde575ddd03dc7c99c5cd05b2ef0be90c10dde1180a8e86b63556d5e835db8159fef524cb185313cada56cf45aec57623fef76316051858968767f813b7ffb7b947ffe6f50f897bfb792bcd9312c1ae447012b9490e6b751e1ad0aa8b3489ca2f70f606acb0cc1472a466c6adba7ae5a60a9a8fc0049eb53a6169bb998a13278f4a2e7ec1d8440f17574cfee884ad6d92635f4834082ba6ea8980de0868ee97317e3c6219f76e88ad2cdee30c59150d41830cbb8d0cd2fc3014c744bc8167be03cf94fb007d8e4571a3c4c0373a080348d6c03cd105c7e56b1565d46fac84292708e3d8df455226d0932c508bb0052e356057e48d05e01ac1a8f0e8ca56972807e88f50df601c043e77521299426500d2c62addd5012e23b3a32485e6748e61124b01897b1ef8e02496fb7b0a27b62e8885b4906e0fd8510c8f9811825dbdab47c9934b49dadcc52d87f0e485af5222c6b1a8ba5904a1bdc8d6d3b0d68f1047c290c3fcf4e6bde3fdf238284d66940b86b9f6842009963a0bb126a22a1fb050292aa8382279f7c6fe381933596839324284bb9ba50b19d13535fcd4380ad7dfd5850f876f56bf2147fff15b7d70e06aecefd8fddd0ef1a5e985c4ba9f41749209e705e1f2a31dc392ed4ec7d058165aafa5ac33d70aa36bab7ef7c388024aec4d6d8c5b3e08e6200a59e13b835b6667f379986f230740aac74ae49e3a4d0b9c8f84af043a1edbc5717b907098c870d5863760d2200938a5bf7df814a745ba5f712d9a48d3ee092b6c26602356a889c3e781e6385604973772884df951b47a0d5f0ce1bc427dd13abdee9d1689b07c0f7c90117e510ea3b3ff29e08a93b22ce332f59007db999d35b30da16b71a4ec08a87a80d63f075a47d380a8a135abfd11dd86dcabf94885c4d48bd8ba2d8209bfa1ed9283c501de35214a1a7e498d003fddd25010e1521521f8a9f85333d6b2964d1522026304308d3d59b0ea32194930a325b0b95a621c75b4c7aad6e849472290decaf47a0f1a1563f8fd732b771eb8fea64a0bbf06eae14000d1f9604824046a5366d2bb08b8f517d5b54352eee63a7c5be168b7b0e331a497469b15219dcbc098e35b2000069f258c8668c2636bae42435d2a6d30a53b771e1a71a555329524ec0922ec0a461e36906d8ac66a9d0845b29e86e97ce369d1d46d657d20d0ba253117a9700ba8b8e48a606aea466cc63c0cc5ef934e5af0632a37a1893c05abb787b19e3300d078350d0c68dd41ab34c07af29473647db4ef20c27026a86be8553c62dae809eaff1ad84c7cbdf2c6203cc90685a8507adabb27f64b368d10fe7069f962af04977547d385fe8ca54dd0d2c2969464bfa45d7e859c57b8f195978f430933c50d7cda0935feb7dbe8992e9871479af819492112c6cabf0831db8cd7ff9f54720e7f5da5036e07c2019558817b8603b4c4b8f75c08d2a6d011f43d6ac27ad83605a7c8206d23811191c283690498172dad0b21a8b45ef12c06e29318013aa0d40c2a16ade5ba3f04deca705a58d79e87b9bde8ee1b3b8c1aba716f23c8b5d9b80547e6bcfb763df7ea03286aabcfba25edca236acf954c73d92cf3e93af5f537063d39224cbe7675a0ac7b3b705d1a340226ac43abd1435a1045fbdb40330bc80c7cc83801510157fb53cc9ff5013cf1f408b5f815041274d6165a538a59a03908244002b713c10bb308e47bd14ede806c8cbc82bd668d024de0ef85ad247c4d0fde988bc4d59a9254ecd0a8d6cde887f7f3e832aa7bf911183ab8c82f70c21500f5f59b7a6f5900d3753f849b6289501236e2e8a73765cade626ed7b91c6d210a73640f612123a01f4e8dbdfe8a013376e3096c3382bb6767887b35d515d8ff0c3d1719ce14cb924af6b4ecccb076c679045f7e8cad08287d7e15aeeb68dfbe096a14dfbc1e4e9b8937773c88264ed04803a4b2374c6cb688b17bb367a3b22900ae8039d0fd667ce2f87e722a76d42b33e3af454ec79a8502f83ec5d628f7c80a2c0d197c739b192bad58f6d51cee3d231172462fbaf2bec8380568d04b46ed098b0f64ae0c65db3a6eb669d975b5af2205932e9c90ffa572e41087d2712c430b7b511ad2f3f65feced24fa0058e8741ea2739c9f6bec0ec60c87d58dca0b510a6feedd0ecdcf9e5e9965b467e4444ee744875e989c5d0898370939b9095391084248a2dd3a1702e8ada9bfbd460fab92822ba94e7537a1fd3b6503f051476a0db0e4cb7119d6b16059df4c8407510840fa07f320461881f831bbc28c52fbbc097f33cf11a214857dfd574745e3d4d38ffe77bb0e5ba063c2edc40ba8c5721b003d9a5f67ad09adc4352f3ba6e8d88ad97584035f632db9c49584801f5dd85b0180fbc7bc477d1d60e86676f6b01752305b233bd9ad7b0e76f58353826a481906d909b232652897c683d390f64bb124a59d3048d817b72ca11480770ff79553081f427bf781dbde2f276b280035e67f9abd0a8a68badd4bc7863052c4c115b201d331b21c91795c3490803e61618c67dcf8a7e7bd3c4a49b71ed480c3eceba1030bdb168ca1d26ab967cd029006e64a799bd9921beb00bc8a44f5f9e7baa2d40d1082ede87f5ab758107dea2fcfad9c0741ea5558d9ef1135ecce8fcfb01b240cc6bb0810ed22c097de84354df1645876a73a7653d4360424defa369ff6b2570050b3ae1f16f4d92974e8f6164bead83891df791826a7edf605ee91290cc2e8080fc3923e16541612485405a0f04e16c360e114059fe0b1a9a2ee37fb6dfeb3087746679dde11e49a663e4f2672dc2d6e0b71b588ea1702a94476f0b3a497cf086851c738ff397d00712f126844cf1f96bcf6258d76383f19b4118d7175769e0706b810a573f7f324e04d6d900a9e61a8988900699459c09053b07cc0753d39045c1e03d8ffdeecbd9ab3f20a25bc0e0e26385f8ce9a245859b35a3deeb055a0bf44a5dec70c627beadcec4671c3db85b5068131884c5ad81d98d5a560cdcf503a6433f6f6f1cdd4b8afc95209d08b2cedaa0178fa02b93cc1c9517ff00e9970d7809a95abbda9d065e9af67822b94de4be4e89cb3449fa068cc4e03b9e5ba10372e2a01bdf54cc87aa108343b6c333c7d11bb682c977454febb4bf7ec57cd90a68c0041fd3b271ca5cbb474b71767fd603816570c6520e2f9e714595a22e730b56c3a55158cac51fe8865f9b7a70723124a20f7517ca236f1e3f68c6abc8ab02bd681ec97c68663f62c68733c44654c98fbab22d1e45334035954b0cdf05b505004a4162899fcc06f3070235e8edf4192c581046e19c7087047b1829c3425c0fd3a16d8760d15a4fea7ecaea1f6eba43ec054af18ba848f76c9fd52be591f30b322627d555f7a4987ed128c318943d16abf2fa47cc6de262397e189570318d0aa3ac54afc4c09b4ce0db6c87284d16f15b18347abf8160d7b5d383dfbc182e08e8d6cf08eddc7fab9554ba0357326db4095bced4732282cda21407ed6eb39e0cbcc9deae090f632b47b81bdf4e947dd16e6a826da60e7c8976566da79bcda00b0b1805148c5e44422a6ae5e04746ff500b72f1210b2f379765704195dd6b8d06facd1964f05afa6d92bd65e0ac7f55b0a23ea64bf900f48b18b6d7b5d062a00a2910eb7dae2f8fc88596a3f585f1f3238b030278a77391de9c55ba858182e2064f493aad8baf78a6c1797c0425a2a9e7b31fd055e1e36e50ee19b1cc86b7e1014a87120e074b5d4b556aec3cc1e2372edb2dd0f93b94b941924c7bad46af780b24329b1dfaa5773f5a512b855e6d4882c4bb4954a0a99f8c5258445202a729067e5c88daf1367ad23e6a5d621d77571ef37054eb29a1fc763530da23750aba0ef98c4b91de987d756f47f4c58b532966a001b9e2544b8889b82dedb5b2a8070ab31ffbf3be38d6b387b378771d79832c7a9919b5c7222ee9c54e56c4a9e91f0e17f6b161676f19c0f2d4ead33b9de2c02d19d032f22d27b614addd30e10ccb07301ba09e07a8b9ec318557791ba3fb99a326345c44dc8eef0181c157c2365a07286fb992fd85e103a76a83d4004c611b21a8369967f106b156530b4f0e0e680992243ea896649a8db6d843bfc615eb7f22d8d1e391f9dd6372b5c234a1710302dc7be655c72ec5baaf1ef7f9aed62f90c264c7edd740e379ae2f8a8cc27b3708165de70dfb304e59b48078ac23453e0950c853230fad4c21332adf6188226a0ea635e5d190d2a8f6f2a35ac3b6a94ee3c418b8df2458e8a305750c439c187a03962d02698708fdbd5335b94ba1fba7e676f5bf77fbc563b0b49bc880dec1490fd4eefbcdd476316728e71ab2f7da533adabfdbd073d8c8766c23147c3a107403e52d144ae5ed4ee6048a8171be14d1d13af34ca1f3c90583f468d8ec6540bf0b022be81957614cf3ece7dbb69a5c435836379e4a480d73a491fee7ee0297450c313237999166df052b995f9f0fa7efd1d803b6c05225fc1ae894c4b46605fa0cc3c22f925c2a7977d2b6ea1d89c34aabf2a4f1c20503c974421e6f182e362e04d8f5f85fedeff501deeb8f99d863e640811a99f3c80724561f58950cb432b9069e271082b1f33910b4f148882a83176da4fc862b03c91b9d9ee885c2495a17085ebfeba5959dad04fa9c8975fcd80338e4b387bb3dbf78f8e2d3bab98229d40fecd8049fa13b8d946c71b308d2401d95cc7ff23e94b1a82bd152692d64c014096bc8f3f157a01ed7ffc5f9e6e8e701a7f60f7fd19f4b92f7474dea18b474140cf8844b51d3a0f572b82088e41cc7d47e2668dc23db7b85ca8d8364ce7b65f20214ff226387ac618a345ecc95b0a88b14b1c9b1ae1759c2633128bf0a3cb3fe0dad463f4664dd4dc9c03c0c389427fb598a1b440ebccbcdd2b7e89c0ddf0338040f16159de1924ab8877a6d95c8173a278beb7cdec33c26e084785b4215afbc0624fbe9bc92f7d716a4b98c1c7a3c9e28f2c5385490f3959cd23f63b4375eb7078435e064d9ff5f27b09e1ce17d02741bb5098def2d2d3db4311317f9cbc99403e0ea0f5d395de36baf1b1ddeb56100812e4354dfefb8a09f7f48bdca5bf22a04bb4926dc4c3caad3bfc4da5bf62907cd3e0004e3cc8cb6e8d9846af8a0985b0f2bcbe26a35445426c79bc35c796b3c6fb4ca76b9d3f455d4e6816ec13a344006d5b7e952a0c9c940596280609205f77ed9c15bab5dc5d8d0b88e3408cfb96f05b372415201d78d85a4b3501c742f22f712af44f2c89fdd1c81356a3532567506ee4b2c5752cbbb0c9935f872f4fcfc17d313efed0844902200e50a5a14a3d403c27eb43dec67b5818fd28ef0eeaa5fc40f33fdaf0a444dd612402d79ae52070cdb296c1b75cac20fd1ce1e73d0f99801c5824f3e0bdb48e4064878eea42dc20ae481f821910acbba3f17f212a611ed765295810c69b56bad736b11f7a1de7c0a4025f3088ec49ee57bdafb549b9724ddcfff01716fe155424fda1c76a18d180170cb99ef30338b10170e1bcc757885df008769aee7285e567b7de24788d85c04ff49ce862e929205c2bba38dfe97c1fc3ae3653e55973a048a01150a4c94fe0777883db93969f9224488e100966e3c407dad2a5254245319e830c7c2eb3e9f085acec121b2dd5ec4e157e0b79504eea5d9f9d2d6c92640c08e4100c2338f650e051776113af50a15928571f7e41ea7fd648947947d51b0634686316272af69029dec60afda07ce978ee1dfc65eded7979065a3672e1782ef28f183ca3102730f868b749d37f86de5e46c321ca20c7a203aa482741ef19bdce61df3c6cf6c89021b1d9369b2d4268bdbf0571a13d5225ea2dcf7936fe22e2e00fd00f005b2c20522cf0013790df6781f2f22cceac0559134f8fcdc4dd58ec25158416eba21750c913190ad6a90cbe6be3918cd13c919f9cf98ad89bd9a19dc5883bc23be838e088963da6ca14012fa74e3ee7dd406ab9fc09cf0efeba819b0b8f98355b35209064a21a1549a11cbc2e6f37caf69fef5fd42cf5195e7f9c9b0a2463096a356370b67fc61e307d6414ae2b20e5fe3aefc0ff65d6f4664916899278dc628e0380b034b45321613e620db6a75a811adc81ef79dc230f6c4242cb163f497d560d799039a92e52ee101884d6babb8cdfd033a46056174c4ff3b2faf4adc1bf07406db0193b2959776e1c3a7608f62ad9d4dae764390a9bb703ca661faa7742621db33054aee8a2039edd648ac74fb71454f6adc79ed4f69531c56d078c76c90836a9b08569377cdde7c7dbe1471fe1a4629031e53e6bce924b4985823dc89e962ae9a013a101e37e9f36b2bb267ed6bd88c7dc5336b8681e21075fb50f0357c8e063f0eedd46706205aa375571c00f39879570db3615eed51fa45809262206e02b0f9023280f450ae383c904825b3a556d973a0a267604b0e72e5a67db849d5a4fcfc0d8bceaa04927476003bf5a05df280d926bdf3abdbe652b2fb9d2a876041df6e018c8d96b038eba0feaf802ebf99c19cd12a90ca479b2ffd40a4b7b0e573a35e069ad3da0020f171003dab365c268182eec954301206bb67eb87260f2d28872703498ead8f0bf43d75ba473d296402c6d916df49082fdcbc99a3890336a01c41000f519521b0d75036523ebeb79dc7b279737f9dae384e3f106c757d68f0629d0a9a5b3cc60e419cc4104b17ebeae2df82a4dddf58643c1ff9bf38fefce8d3e80fbe1376955cf928fd954b03071f93ae1c22fdc2f7dcd99efa191aef2d17885c0c1f3f682ec81eb15fe1911439f5bbc29c4d76fc48c4dd841f121154adf2e0e1089ebc07ad453cbf13b67af52e098bf67e9906fd61e4209e6171fd6c32dc20eb0e29ae16d0a12f5ce48c9cdde55a78ce1e101a7be97bee2c05167c03f8f79f46073b9d7381fbdcbe3c3187bf4febb2bae7de420ecc193aab1ce77f303a08a5bc07cc18ba74606c05adce43fb4564238758569651de4820cf5a9d61022ccc3f260786ff9debf7b52f3817f76aea519bdcf8d1d2171f4bdfddcbbc87563ce42ba606d490eafbd0eb73af9e2e46dd1ab768d687220cfe70b0a5fd2dd397b3acb73f0153dbe436142adc8b20f0a7fef52571b6e2a724000621d550a2325b87c4e4d703f12c2ea561798d102bd77a720305956a59fd19c7a01ffd1b46c0ec314e674c0156b9f5125a838f4dbd55e29b46ee421bef326155b061209feb1409b44776f40182ddfd1a1bb8ce600741c871a5015298ce23978702f07b5064a1b324a917f304f0f03f051bca246b6d3465368cb19caffcba95646a82e169cd25adeff8493c0f57794ca31d17c6bf2116b9999f1a205c2ac5cc818e74f228852e5c0ffca6d407d665837997a50e8bf39ad9d1aa9a122ae3e8b3df3a96f8cc4f2d5aee62e1ed098deff6123fe0234b755fb9ea3d6f7501972a80ecd5f5c9c59b84bbcd5b078e034763e53e6c2455be9145e5f5e7ae6a1779bc07d988936dd6ddf549ccaa15b80eadc33e1afd1582d8d13e301e55e77a04bac08cc3b6f1489b57e76165d99e7c006e54902507b846db97e4e1a374e80a016e3c0f2eb31f6d831c52591cc6ccb202fcf043f325e61ee50d656a47d2447921ab1069edcda80d3e82549975099b0e0200a8f0e7f4afb4087ef95cecc2deded038b8aff3c4147b9a1c04bdf39ebc8f00d34dc9072bf5f2d77cdfa3347b00b13d56edf25d5cf62e956a04ecf1e65f8a089eab496c191ea906bc608d351b22bcf28c9a2133595e76828765c8d7961b6f0b7b61d770f58c57e8f7d36a00f3306e986f3fc2bd3fba35dc798c111dbc2e6b007dce9e9ec63e2835096580e1f91297a7e626c49d4cc3d642eb08b1e230866905d51bcde350a91efc4ca6d54bbe0ba7f55177cf9798a537f24f0e80e0d51bd80d53b5215eeb7be7587847c0ba7c9bc73f06c029c0cb660bdac0b153ee5862940e2edbfada8fcc4ef04b3bc3153a81d5f8568b291d17ed0463985eb0889279cf0db29777ef278929ea327b5aa1728719876b0732e25b735b09fb78d76326e8690ea316bbd5dae90f6e69fd549e66f05434334b682e0cc8d147fbacd25b2a7f480adf76b97d34e5947b88726afc5c40a8ff0ce0559a9be52a23d2a607094f86b80676f21c05f7f9b3889ac4bccf8c780b51874ccc39fbbce7bc4b2083cc1ac1480c2fef8fc480f423be32e217c2061343396a95b63ab4d6a8b947eedb094bb7120c6118018eaa626fec20c8525da789fd50f427dfc3dcc246b77f1fc944dc518100 +generate_ring_signature 68d6b1b9d874d51227c9f0a28cda04d8172e7cae79e06b58aca4cb5fdabf36c0 3438eed5f743a6715d57581bf005efe4fc4cce9447e6d296bd602250d8c16fa2 32 97f5294c86120cfa379c931f3616ae5853e5650dccf93877dd3d22a949deae47 20456187ac5551528a6c1e1e6e6ae511a8835a1e17c2d8376fb0d3d68f8ff749 0b8117871c4b8a996719c93ec132408c440b9f0a73d36ce619e4ea5099f46f3d b258d44bd5ecceec9ea3b4fc8e95496d997234a63c7b6e5128b39bbefbbc4bed 7c68933b4898deb06963efe87110b3a446466a13b18660dd8f550622629d0c0c c2a30c8b5167c28eb95b91920dc363bb6f933bcd64986c3434ed89fc077536ee 3c98ea89ace2a89cee7bc6e06f63cf5ba9297273b9044d4c6ae81b9e411bb440 cfbad8a65a06a22fe90f2bc11ffc003601392504fb1b3f85177f769e22481b8d 8085897e5683d6b33672d6e2e7532337eaf1a6c47d6eab0832460f5460f9f495 a4545f4596f607250e5de364cc2e0e070e37d1c330a6ac6eabb3d03af415209a 9febfeb7e7bbcfc9ed9d663b81671638f03555fdbad469150cb649f765d60d6b b7240cef5233c3eaf7ea031d7ed769d65a7362c90448afe2fb5fba997ceb6d45 0343b7459f99c7ac11c3d9f829f5615c5bc60e35569dfd97b78e6ae2e00f5d8b da9d89d8f350d85ce47516472c0e73dbe1114aa92de1e847d7c6a1903651671e 3bc5140c5ca0e3df7a69f91bdb268dcfa28550c35d64da809ba9940c03dc842c 85eb184236053a9e30b23e189e446a46c11be28359c0b4f70fa696e88a425b39 d08857473602cf81bbc7b1b17766fc09a7fd97c3994f8bc4de94c6aee03f3e99 7edfc9671443ccc734800a1479d6e7840689872b8652be4e97c9ea7aa9971e3f fe172c59587584facc28059d30713ecbdb2f38d75e967899cce7043592ad3e02 d073e626d083698553eb8c7d60e5a526e06f9e288f2c0216cc71535b443a3528 6621abc1d1d77e0e65d363b1dbea5a4651a810fead6d57fb116791f83824d668 fdffaf67600740168caec34bfd37b0fbb16275b155de1eb1bd7b39f1150d0c88 dfddcde4c8ac2861c78bc7df7cf627f7bf8d115c825a10d470ee665ab72bbb22 4d93013a13fb22587ebfdb63e9d8ccb1f7c04bd51c199346461fcf3314094887 f008c557a5edfa69389b32f0da30d88b2e6b91a4e37600c14772ffd578a67930 d1ee1579b8fe43e9faf76280b8c012985465491c20bfbc29d9b2f2e69083e16c 5056cb0d29f7e3fff91d6f9ddacb545f2266163f049ae76407df41c25101658f 80b56590de9d2826db4bc73ca7f18407fd0e5c5c03dd9841c9ded9b8dafbbea6 62571c909782c70567e9968ded1c05a4226a3e04a07ae9db48e0153a56b2a468 fa3243902d44e63e78a23d70a6706efb6e453d3ed42d087cabc7655fb83ca3e4 976c32ea460a47aa3596d836d70bb40a84e6f3d9ff1e2e01a516cfeec938a2f1 04d573623956db0dd22344c139dc7170d8ddf6296ccf558cc2bacf230485aa05 041c83987a208dcb9b7658b567e4baa29c0fd278a85a7ed885d26e4356250908 11 192c8007d03ce9c87ca63deabedc7e06dce9dfeb418fdc73e07662528829a40102cc1c120e9468234ec81ffa53d8c68330be6332a9805cdcd0429390cb303d0ac85cc6abbbdd73d3e1d0913a29c205e769b0da64f0a3d0f3e65c9b31d741090f99c23a484d3452207fe3309a6384db4454cccc9e9820dc03204b4b7e09f28f09ae7a76427627d23d4c308c243284b8277116cf656a6d69f79270238ce318d204caeaf4842ab1b2983a117763fa56cad0156678256a8890686da86d8a94360707648a9468b1c5c32675ba598f6e00f5af06fba34cc65434f884dc935acb3c100a3812ec0dfec3816b2f489d8815c59f37636130e4dc68c8ca4399d479dffa1f06ac3b16dac1b2ffceed29c58763697aed9fa9efda4176f18a7d3d571e3d2eaf0e4647e6c8d29daa1de4c1ed633401e4445e5ade6e293ff678049da2b4213e5807e4d337d7f86659197efe7d7d7c7142d102a186fe7821e4f409e839136c20cb0fe4490c8597959f324cb18c4ff3a0ba84dd5040f5097a218eeaa452410962c90a58a20e7eb44dd3859961dc35fbec539891907bd7d399ad145dcc5997a2d0cd0eedefe61b426468a6c0f6157b7aaa2a1f6d3ca1d065a8fe9e8fa784fa723eaf075fa3a5382f4022643c755b2394e3906336997e7889384ab97efb6a4eeb17320457797b825c187076a1f2f0ab0d26b92ec99041efe94de8284d57da2b03afb70d1277c95989150936f48f4574bdb97733d816a1b9f93be80f9a22749c0551eb0806e336b2f70f1a7ee42980d3f1f18779734fade9545ed72c5604f79596719507ab555fcd213f6e7b340a0af18ece2040f757e452293f5f3060ff513139e52a050eaf85a5a8e4b062a0bd62ad0cdf3aa797e9498cbb3d99b3763aacd85caddf0b7823f65051f03e7f08fe69eb79a1c6138a5ff0deab2f30fc4db152fd2064e0048203e7cd3701664d112c2550174e0d55cbf7e63be9501f86356e0c55dd3a0201a1df11a40d0f94e9cb82e2ee5272da83d55423d755be9330fb903be6de1dff01938b1a852c033978e704cd2e64271a9e96e0756a6655699ab71fe66a2ae5d507083a1abdaf2f092628c27cc8788c5768058d019f8e3a92ef526d06966c7e720f490aefac705994e51c7165dcb78bc68172856869603e4807241c446967f05b09e1870731faf6dfe3473e490581ed18df25dabb4e0c56a95f9ca75582ebbb670604dceaaca2f5ece824061f567648b9771abafc155745a2e18eae96324f5d390135da99bccc159140fcfa7456717bb40411a0ba1692c8b1fd77d0f4833e83a5043327474ee9fcab8e2d1db3236930a802e6f776b511e712c164d23767281b4108b750c3caa8f8da5058cb55e2ecfe348bb3664d24eca47b5eda3d656630f66f0ade294f9e140e2d0141648033f37a354322c5db452149a9b0f2210c77158b72020f28b12c43ba796ea82d55a52743e2486a649a39bd3bee12620c4fa04ac6900e4094aeee014e110646ec5e2d8bc1d3a661f3a5803368e9983bfabffcffc56500a84480160fbb0f651992bc03d74e295bc2677616f43c1378468c6a4339dd780b4bdf691e127f71b1cdd6e6eddc7e16c8e215bbe12aa25ee78f0e09eab4645a03917d016dd7d2f3893f185b78ea0c979acdc57fa7863a44dd896d89da6d8a650a806b34ca3a00b55d776fb234e8996f57e9658b36a453794bae74e8e862f0ea02e31cd0cab539dce0440c00633f656cbb164d17d96f9fbd4723e0de5a60b4e10b69a2e9b4f9a42cfe39c1d6832622f1bfc3ce1c2c0e7112bbac6c28b9e31c7b0c7ada715c5433be35eaf0c8f0d80cc9c4e865092dc98fb94da7a16b80be9e840ec67a1636590d40883ffbd31211546c32fdf518035e5fd0bbaf889c9485f7be08e3ca350e15e22c02bda292f7c8802856339319139d2185c55e68a057541746038c4a60f02e965cbc3a36147c3fadf2ff60de9190bd7c3de92b817e98a89dc902c3ab90db51aad66ed26f02d5ba86154f420891129e1a789ef1f9a51c72d98c05e847d8396e3fbf7934c5082928a78cee65e080232c28f3cf53a360a3a333250391dd7bab5a67b9020bd9bc7cefb84b6f7cfc2eb36a51055a96f09b98e6372d0fbbcfb4f9c42fdbc544180b572c2d38c3e40c23b11e0e5d58edb8b9ff55a3cd0a73224f90088c08b11a9b6c5fbbc0190e1780e8c1b832c561ae51b30b2de050026204540792cb734d8ca309d9963ef3659d9585195752396df0a2c0c46ae5950fdd809e10446768cbc25898d186ca5c6e3bc24bb752d7f1e22fcb6f46a99a6008f5417af0e2dc04ac032be3a136c0941d4c9f88d27f6a58c78cf4e17673cbc9006b1856eb776fe0860e53b03dfb691b084e2632267fcb2b8011e780f96ff4eb0997d73ed67c58219add3b97fe8550a8cd4f7ab013b85d5505109b2a752d5dd40b7bb39a9e7fa605587942c9a27d8eceaecaea5c18f98ac3698e3f2c7209168c0321a4c233a0010873cff7862fcbb8f8f75ccd1eb180ec77ebc4b97aa224bbe60bad2b389d190f50d4d7b483cd18fa5fc39ecea58aab034d6b9ace25899fa7920d36389001ac4a50b6a7ab12a94793a3e4dff3c814b33c44d6a8c749fea3a2e200ec58f4afd85b7eb9d07fefc9386d5129b89cb84d874f053f2b7ffdee1670d60bdec3d96010313576c0dc8d8cdb252f95b8dde6699b25e2f2a40e000ea8a0560f3d476f94f7a9034c33dceea828ed48d5bf6588f72e349218cd9c56e502de560c96e2bdf6f4f9859ff9c13af523794e630d68c3a5f62eb8dfedb14108cc0e300c7d0587ff5ce77b478061f126609224e47e8cb591816c038f90b2f1c2854ec407db4cb3e76229c3d3201610f8f21b85f0a70876247ccbd47e1533f85a76cb2f00 check_ring_signature 1eb5a959cfc7a40b661728d352c6287e9dc310fc65e4c0a918c20139d7607d08 7184fb63c48a07960785cb450cc4515b97e7b659c762758189a4cbdfd15ecbb9 200 543feea2b11f4c1999fd15f1968c57a31d76969ec3c84c25573b3cf2bf9b5311 f7acd82c5348165412cbff4df40ddd3ed317575cf11e2b47e2ac6b5b7934008b 2442c5df338c960d7c9dc1b3bc18c6a3a532617265d74eb65bc3d2f8c6bedf3c ec2e1890adc73628519c41299c4056b5a517b614401ca8eb9d1e891f48805adf d49719b604a8c4e7733a9a8581956224e5e9e814562df70c5c25a0a7b5dca7ce 7805c4fb95bc45642f87d5abb0c407f519b5664010294cf57ac09f59ac28867c dbed4e7268bbdc7dad3f866d8ad59b98ba855ab32834e6df09ca0f9710067d4e 3eb3cb8fe7eeea2ff5ba051e32fa3a2e3b7c711b828d8a76165cdf7b2cc176f9 94df9911db6f6d46b49115349ed48e8fa4fe32fed831c9b31ebb0c85faca116c 992c65174fa348e589d5f550a95d83ca76514fecc90d0f351d92d9a6c0df320e 225712dad2f04909e6d79d01ce8fb7d33047f972375a0ef54e8a5fcf3d875f7f a9a86880f9e0078a4947421c95987187dfc52d96adf363c502f30edc6fcb7aed 504fc767e317ee865214245e1410a400133f52b83badd645c3d2f64e48568bb4 a820199ede446a1c1b7ba39c3158128193d1e6285a1c546d7b8dd9bcca63e17a 8c9badfaca415bc7848f951f197edfd7e4da91cb0fd14d177b26b82c94063d37 eb82e30bf22d75c12cb67589bdcd56f9c2ee3486a1818e54e7b0e64bc23e00ea 489cfb0a793daeaa0bd62951309ea439d363bc1ab2ae5f08aaf8d6a0db866d87 db74a07a7f44f6262e709b945c4d8a21ab57201e3bf0e0571c022b8d7c927226 f347a3bf74560dee12fb32bce67c02e2ef61dcade637fb7666567d8e8f689376 0422c442ed56f7210722910a5138b93f885928562641477ceec412f895a6e48e f725928a066f1205d0a3c7c06124b1060d782dbcd5faa053ebb4c3fa376d83be eb0ebdf89be52235d34584264893dfed22d15019d0d0e6046f8bf35afa1a0f90 1578e3bb1eebd60e77205a9cc7ddce1d8cd994d8c0a57c683444622b895c6c42 d5e7af099509b7e97265f92f9a232c2c14183886209f9e5e6a9f04369cf87136 849b1e0918fc19c02871083ac8637df704b1179f03ae7fe0fb535bff7aff3d9e d258100a0f9aa386969c161ec81f8ba8eb5c33db0a2b6318dfdaf1e212652c0c 6f4e276c39fe940712737352d9aac0d872a14bfb0e20e8adb912ea9820a772af 1ebe7ba5c7d9787b4d448ee29f0b5a7df81886be38817a75cf099c1cffc8e806 a68e5c9414aff70b56a736ee21682beef2c8a446773abd14fa65ff4d9939efc8 76a2a0ba74d5786f9fd32f0e0e4ca5ec9c2d915878f629564b967bead19d03a5 e5d25bcb19a15506ffb8ebda3168a847cb1a4c3e1c883593963d0815926601ca b189a35b6584635e2ca225b99d10f935e5e2d9aacad94deb7159cf41c8057ddc e6af85777638abd5e11c416ea03972b4a874358e2b3d64dca64ddd456535d128 7c9ec2eed774675459197aaf4e2180bf8e9bf80ec7a0442d5f5659c71fa2c897 49b298e3e80b9c0003008b62cd6c5a3cc7f4229850300fd5c48fd2e24dd81650 f95d9552653ba4d6f3bff5859d6ad269cd7291b2892001080888bc619dc4a046 3680c7fefe69716f04f4314f8b409677dfa82557e994d65693dc7d7f8ae5614c 6e506622814f5d90ec809cf2302d5cdc275f29665009656fb1f941754c649b3a 667ae2afbe462c765f0b8cf5d2e9bb5b68560aa570efecdad57784585a1c7720 2890a34891ecbd6788bb47aa0681962cabe54f06ebc27b019408d7bdcc6ba637 87bb518c7d847f9fc2aca39317dab6b1a0c75c091b9f22a7f1848364b34509d6 0c838af55d313fa455f90656f7c7d97a1ef503417a97a74f395dce0524d789d5 6ec977027721921b53909973a702f24a8d366a45f0436a0113d7cce6993f53ad 71ba0e015bb7be397b27dd1789c94c4c017f1ab681a0d3846f0353a85d057293 64190fa9576c454a48d898256f23ae30fe7a500d09c68634366b5544a5ecbdcb a32298df698677db8fc50477a0043ff3bb022d27d63fce6de0c913fcb14ac637 98a1529478e55bf403fed1a009503bcaa71391dfa83e5bd0d1c3c74783c0208d ea14c8daecea208f07aee92f4edc7e00fc5397bdaea6c5a486036c517099cd9c 7dafd59d7f155aa634a26971c4106a24c172a30cbae48bf6ea1b1d72b6dba1fa bc7967c001f400998e18fbc53da60ba187a1480b731af2e09762979f7c4362d7 6ac5b82e87191fce298281d4b3520025b0eba743aeecc42ac9d218b30a781d91 e8c482ed4b62e158ae0dcdddcb794110c4f73c5abe540ec3fb7ccf8a9dab3a74 f7d52dcf00ebb2810331d1278b4ba03b0384f47c0f89f92e8e037ddfa03054f3 f123cca20321b7e73faa51c8255ffa7bd8266b1a39991bbbf8115570c667a306 ed8980dcd37a86d7f7d1e720548fb6ed8bdf33da32d8e33f681a93f6f5459883 c85766a5858537a13822419b9af7136875a760e4dcafdfe13176755499e5fbdf a3a94fb862d0e1c1867d46b6ec614a9bd6ac5164e8de19e29b8fb38e2a6436da b1fd45e36bc57e1c8672f4076dbffcf2bf09035dc5e4c47f14a6c01eaf3caba2 ce5e1669abc0c7f060ea303f41aa1197b90facb7a2fa2204726e7c57ebde9665 cfd271968a4967ffec462606fd7b195b03f2af58de867bc5278657a33f710d06 e3d31e9be390c1164c32441ac1fcfebf65db22c2d3b7b29f4e054894d854b217 809148d336d7f80f701a80e5eacab1de50f1165d03e5e352a7113d6f1efc6a2a 971ed240b6a0298187f27d4dec358f401040830673b07839f74e5ce7a7c82ff6 29618c8aaa9af5c68825a87aa8be826876e1814e854c86b7d76933b449d2d279 26353026e1c4758ebc3c432643a7f611590ab32bd7710529f2947d47f98cb072 0cc7ca72f7c934e5334af028989c07a49229fb00e4096c070dd24b87c2e2f690 c814ab0bd6e63cc3b2d7a6695dcb30b7710f7f2a7421ab2871ea2a4b5a9d5f8e a0b3bad37c0e765c22ffb2a526d0c2b8111f16e59c7cc3be7e6a1b07e8de767e 3902bb51c23ee908ab925e56fe904b5e661fd795b4d0ae83ee111dbbca78a819 10546ef941398f600a29d7eb6036bf371b11e4ebbc953b9c5329b219edad3b68 7323fcd88ba69cd500283550e1fc13813d73c6c8927e3705eccc63866bfce833 5bf49f286f1292c986bed0f67522965665a461c38852786845ba46ed654268a1 97d85e168d5987d1fc79b0e887aa7611bc298143795e9bc346b235bed1f08f46 ab25f12c7063b2483515c5b907dcd34fa0db8e94ab1b06bca82a0a4d7a21f166 b3f3794b37a43820d44a689b2b7b1a8df269777a21957f91b4308d2cee66e45f 1117043a0689cabd1960d1ef742bad37c510731992d024a7ebe7a2d63667e7ed d5525971a5cc476083846c310e77ceb540ef037fe181acd00afa81e29b67ae14 5db681990d100c409663fbf6e24b487bd7e4e915704fd0d841508630a613f097 56d7cceae4ef1712eb95a782dcbcdff8d959861b9a5ff56d395f0b2d34f4f12a 017ae0cc1fedfdd445b4fef15938cae99abdcd0583a188e07557fe2a65842233 4860520043dbe07024a476c4091dfbb17e8398ef825f665aaa11ed63a62a4b34 99913d4fa4a13a2a3732215ce2adef2951d4c4fa31191c62cc8cfb5555e27d23 d2d029ee0272ef1ea773fedbd8c464a1858bb4238198d2d4121393e6c8326b3c 87fde213731057da37cf5701dbdde968af42a328079e52157e56a4b2b6c95cf3 7b416abb2b0081c4205a614f1de524752cb5729e4431f28086ca888d7cc87860 38244112fd3bdbd05733434fd81a649419f413c0a8c14bb52263a589f0a73af7 63f4dfb6c5fe738cdf13cd432954f590bf00b4e81893ccdd5b1696e0049c8d58 723e7e3418302ea27987ccdf280ec556ad72cde3cb2a596f3349098f27a794c4 05e2cac03c8b1b049c9bc473f0894b8ba9b34aa506831e06723b416140308133 59d4102257ee505cf877a216aa22c840b7214764532b920bf8260a7f58a5dfca c4f3578faef7a4ff279ef30a71a06f56b037e0231c3fbe5ba778c53d524b101c 73822d96d4f8c8dfb37e8eed6aad9f19e9466b0871c917303957330d900dcbc6 ab3220323c42fac9e304170080e2f77315dc5ac77d46892308fc76a721c5773e 8ebf74406a07554db8abcc8713a23b3031c99dd6d232d478ccc5ab9fa54e4a53 59681c4bd7a5cb32ed92a2b1d8cc6156199921a4f7696d03eee11d3dd64b86cc 2a5e0eae43611e232226409667f02d74412cf4ff1f4b7135458040d935462a31 0bcd81359754353596e7e67369dd00812a607c48d6e4db6141ea84a70b9e9d30 14331aeb9f5a2f2128d40fc738cca6346ce2cb2df0df94d068c5ba0623cf7c65 fd07c4ae1e3fc78b95d7ac73a73ba18f6c1df405d28e3661ba860b3030a835a4 39f3ef8784ff04275a988836c1fe13467a3b2c5c538e34cab88ab03745531cc9 7144f8f4e2ac33178eecec7ff144123605c1f1303773d049dbda8b2aa34f163f c40805c5886287f2d2d7246128b5f64eb4f3d1428d3605a06ecdfb7ef66c43ef 059c6721140ae041d768b7384689981a13f998f5e19c4bbc03df95f8d4fa866c 9e995343b7855a18d916a9913aa7598b7b108ffd35e00d2510f17e842e71f45a 7d8a69025c207109fc0529fd604b76c4523ab439b00907445e2424ffffa98f46 6c568bc59db99d2ae9f78813e483de0039137628715e481870a42215502e241b 9972c9e517db11d6ea09e48c7db0222e64da5e7df3f78cebae553d4f8a0d05f1 3238ce4c1788fe041a12cae1fece1b6e6854f9a8499b3c09a4a08365dd89ab74 1eb6ebd1d5fca4b4caa9a1e08734c15057daf9aa135cd9b5c81b6b9838b690e1 5f84f99c4afa11c9d03111be7077f386a34869930f1c620619b296f297928728 ce6d586bbba966c28d152af4644e21f519d51a074e9d51d14aadeb500b4171f8 979a86122b24dbf7787836445630666d28710f38622b2372687724884de8feab 2d31c50d507f0340f5428678b5339f43e677c4a46dd9c3981a1a96cbaa251895 528d682b3782df90300fbccc325f62f11997f660a168444d69718ccfdfa616fb a819364888e956816954687dd809ccd3a1aecc32000e7a699a6c24771dfd6cc7 6a720408f24f4a5c0bfa9865513f41fd9e55c5bac0d3e7cdac06a7ee66fb50d0 4fce289cb8c7b82f183ba968834165040eec796434551de337d05b5f44bf0e7f 027cc53385b6d116160036a9ecddb3434b2562efd45abd0823839b70508912a2 636e066a5802d9504b86f1022b5a8b93474918be38a2962de9660d358e0094e4 c509514ef85df5dd5a66f619c963b5684878d1290ec2976e1702963eb97c794e f11e96dcb3820fc332652e6e973399f6f0a990ee88ea90b1e3710161bffe6797 c5e990f41806973d30789a085ec52291431d3f66e926c9a7b0fa50bf7b234f87 281463828afa2de23dbb757568330562b49c3b290aea675e47363fa3918b45fe cca71fda3caf010902660bdcbf6dc15faf08b04b345bfb6d4414f3e019a7bcee bf57436908bee12116fc88573ef538ae22699e66fd7d10304c13f5c83c2f0a1a ddc2e241acac4159fc35e49a8937afcda1c85e92ab47e1f659e7df2053ee006e dfa9a0dbeaebfc6948f685d905180e3dbe629df2973c961e49224cf1a104c351 846ca19fbf02abad29916e23ccfa80d7a89ede3f441ee77becd2a67bef045094 4849b1f596c629fa04976f04f1d1b797da43f6ea4130aeecc07bab8b37ff047a a3d2d94b33f852cd770fb595ca4c6b1fea367dd5c0fc1553f9168b94a5220fbb 7add63f8fadbeb00c6be6be1c36605d026c141cf4c3cd38bf83bfc296d99db48 6ee0daf17d82d17d4d53bc0b54c255e746592caf7ecec281bf0a44dcb2b5fc49 3bc952eda5f4687969715739a6892d53975a0f6dbffa12f8adc2eab46ee29d6d e5fdcde88c9d6175f3ba2c3e9a499033ab1a5419a70f3b6ed2d017c56da92f36 e309c4d435010678c8750dc2b3d8d4e250ba9fed2a2d7f46668878e31282853b 391f19c85ff9bd2e38d97998a41446207b044c6c5293f50a7e47e4f11edc2cbb f1f226812c18c243bb7caa6420e56b85378842dda40b358566f6dfed0dd0d48f 1c38d642cd012bbb3fcfc6e3ae33f0d3a9ea0f5dd6f7d9f55b6b51dde70d3379 0e7ac1cabce9bbb6012f866b5457ac146ecf181df31a0adfed70cc551f33ad3a ce6d87fa0d789679f90f97bc0ba84b149907f73e90d31af191cd666b008851b9 d70c3fa70104ac187ff11e87c88c3d439c32e1bd545e9fd03f0f623b3de40742 7a3b502186e37eecabcd2f59c5245787f4c9b9eee5fa5d41a1d9685bfdfba5cf 29ab3109d98ae17b91b533884a50a6ce720ff3559b94991d9a47542a7898253b 6401b466925bd5e24859cb228f22e0c2d3d1cede7a595daec5b53a1abeef7c80 3d084b70ca1190fbf49fb87a7256fa81af5dccdda264bd0fc4e44e9652c2144e 8739ad8e09f754b81cd511d55fdc96f09ffe2d036c7389014eec318a4090e7ff 1d0b13a09908544823ef82bcc0ee0702f771677396887243e9b855ae10fa0d3f a0dabc3212ac005f328af1b67f3bc40bfd568a3bfe23ff1d17724a2fc0a9b3f7 47ab08ea0a5d1ece88d24e91608cb486d48e78b428eef7d5af5b70b804458124 532dde7062bb3ac560c7341edad20cc4b3d1e55ed5446189ac3ff1bfd6e1b375 c26be2d6baadc86a620565d30fdb3e3875c04679f5cc462fc090c2ad1e9993d6 3f24a8cec1584ddcf6a36f82e21f5d5d3d14bf5fa6693d770bed98206227b5a5 8c44348acb609a7de70b5b3967fcc350f2a6e6fbcc3bc8e5b3c0d3d85668299a b0fc060e8f9c61cf0a65d96e40fc1eff32f878e8efdf2b9aee1802900c9bb812 a7f1cc91a33b8236620adb78493b4986f6baa36a9fc0ca39de708aee8b03a9d3 d9f2debb03723b5a5fb3f1eaca7d757cc53e6a1add37fbe97a44c6c995697d43 6c8b6bc336ed423fd2b8c688355252219f100ae0b3f7dd49715d2fa54f4d2dc4 0a5a6089033274496bac02a0be66293bc08a6323a7a9197cb5ee8b881b79b91a c6c7e200df00a5c8bb5cebd4819272446669b1a55dec6b46f9dc395ee5122518 317183a7f4bfebf4b8eb9d45abaae34053d561a29d4a18fffe4ac424aadf9a1c ed2719e979b9f9120a9fb07010dc60d4801a2a7d0988a7f2cb185693ff6dae22 93bf867715511b241f220390ea0ddc19bb430ebcf0197a23bcc866174c9ea270 f811165233275b8883df148c4e0aea9d344c557c965592546fb35057b73c9941 6413323625835409b033e73cecb47c414b7313d74c0cea45b231b9fe4371095a 4b3494d57d4a869dd7ada0b5c02b52c6eef052a60160b6a61d05872914569732 b8f46e5c2b7e79fdcc92aae87d8fd60c8178dd6e771ff15094a9da652f8269f9 195b6edada34c6068401ccb49b9f9caabcd9b51d59877ea6a7987253a7c570a4 944627fc5a0a7390208233ef15712c2e4f4fb0fa4f13f356e6d5d226a4b028c0 12e4921b4627591f5d4b9c3a704271175ae915d0ae8d9e350a225ca2ddb6902e b0614048fe6f4850dc31eb26140d577f367d022ad4fc13681245152ae1775deb 55406f7f4b2325cf39a34f916591155d671605c4519e4329a0f8e468805820ff 9e9a6827fffc2b03b4dfb4a018616c99e3bfd904857fa557316c2a16190ee91f 827fe24e9360186380c8373f428be2974709770be3040afb6f165f55cde6cd98 035b5472508d2b6599df3f46484bd5e2b798b8f2407074c2b3c5deb7ef7d1bab 36b3c7034a6a0bce5fffb24e89f5b34f52ec666b6593566d2bb019f12dd1d5e3 bd3fb3283305397254bdff0a0b7eea8edb3b8eb624a59a05d379d7b9a75d9482 5e3646050305256601ffdd954d32c7aa26bd1ce5cd64bca3ab158392d6f39813 3ba181568063a0920047f366be38f8e73eee814cb48b90a01f9496ad8bc63bbd 572777dffa2d63a4078122cc97fba3e8f9eaf0da391d436aa82d93374a6fafed d3d57c5790d18da0df8b9f8fb2be00eb3ee2adac775e5117d86c0084f3ea7787 73b4509bf34adcc3e4bbf20f60eb771c173f906c4a4cf5e59a1f74c29d4054ae 822e5fd7ecb36e2280d3fb300f9e2d4221385f421231937bfd634c5873892457 0ec5b38fbccdb87a07423ee0d7e787ab152e61cd4ce85820c22706f6d899a63c a5b289d4e5a8afcbc5fe71209e9af3b0922e1a1dea7fab4029932d1e2359fd34 dca27c0e7c2a877dc3ef1e000b0bd9d74eeb9defb48fe33ea19b185eaa551fbf ab288d412f5928b81ea2d7c772cc91414280a9e4ec5c7c3c8bed0ad0aa446fb9 e6399d35535d7fb50125496965791e751a906159d92920e21c147ff920a7f33f 265c6d002d46892c66a22a3ccc496f74983690e9d7a00096274ca4be1ec7356a 0d0d5998b3c2bcd15787e24d2c7325aa7c76155632d68dbf57461665b61253b9 0c3691ae2daabbdcb784ea3c0f6f68560ab548a12ad28e2bd252910ba78ea4d8 344bee7a3fa92be3e138402eb69ced23c34a94d7a60b6bbec466b156f286a013 9c76424430af08b8f5ac377c84660321dac7c87b02f55dd69f4151142ce7aca2 197ea5230ad5b754b94de2da099a842892266157d403b498a542c93a642d38e5 42ceb7329b551a2ecf1c3377ca2490dae242e8d92359f6b5d619002bacce37b1 7d24dab492d677ce092a87be26a245482b7e89b79205dbf76c8ed491a5c66518 5465ff982b133a9e1281c0ea318f7801915ee2da70f269735a0e17c7259b641c 3e31784aca01ea6728de1110ac04eaa9099348d0bfee82f920fbc0d53e1a587b 54dc85b634b92b67b53a63b955f5560c600c0921e7a788a826f0109e4db5013b ef2117c227b0f301500939f2d255bc805f066fed8117f16431ec7865015b3502 6bdbdde97c4c53684f7733531bd5ff5a0e6a0b5bb786fa193d1b81714ca99470 6574c7ea90add532ab17e8218f992a4be754579159c77e1b1e0c025c90e6c607d79db51a387cc6d1cbbba32f914ec953317c8915da7a28ebdb88169d99f8e70c388bf3d7d4fad9c11dd2c46f3944f378ab3d2bc0e1db0393b2a611f74de32e0b7a7198a0165d43e0fffb81c2034e5a032a9214c90197d25a021054498727ca07dac763ba6c2b47a30ac3e221521ec6864226cea23a054367fd67daa2b3fad901d98a68284aae665c19f9342c8293da2a1b2efafdc071032e2eb88dcbe587a8015125eadeb0fad1a0d3f8b5e71c477c96f0d2c97a56bd819f96748514b97205041eb159a2e4c861ecbc012ee6ba2c30ac02e7759ac2d932076a2d9713ec049e041eac3039f8a2965860515e700c9cbd71d72ff8665dfaa41e676e319783d9a9094bd0b6a4e560247d344dd5b006f091da0fe91bbb672295d82cedb8750ac3610dcc704382a3b118d1846af03cf3b699c9b240d6e8f9efe1a32cb088032d15580ce8c4d5813e6008cb3742e9fcc76223b966f2a1a64792cefcaa361e5b691c05024d0ed6bb607d67fb22d853ae72d9bdefdc2804683042a9dc35af5972f015870c36cf7cdc43228d717e79deef034cad0b8a30aba230dd5497ba18f533f101890ae402d83420ee249fd9f988649e2f58ec5843f245e6dfb229893fee2fff4e260395fbe236ccf24bc01b937efdd49b1adda3a86d9a8a8c34b00c29f679ec65950a47d23b47a80d42c49fc0af826c6f0d549ffe1971017848fd8148008b060048077ade0b66788c3dcef70ca09a6731f97ccce3207a9637f1ac5befd5bd34a546087966d4a800cb5ee22a92a6749cbb941e2bdcd078857dc003e33a1a51c0986b0d527efdc308a03b3a0ed935b329750f093759df3cbb5fc680d61a1198ab61a30cf3a7a1b6778498e8e74d42e5b1f138dfdc50069ccd566a42966955fb4abda302d611addf86710e8e1d7acb0216250658cb22634cd0a6caadf3a700f042c93b0364c9ef395cd3da91ab3074771f0b781498e78db9dfb875c6b371f88fae58ed00a6907684aa6475bc07809ad920ede6c548a36dd02ade6b69bef5679ee60ec00634b403e5b14020e4b271328de7eefe5aa442381343b871282c19cf4a90c9fa0ee15a8a53c96741363d05f736f9bb56185759c9f0728134a3789265eee027cc04b2bef44210f7ccc2850661e1e54f60c097949acd0e834e79efe7fb380983b205d90dab9d57e6a0dae8b7b8e639612473d5f8712c45a6854bf685412d6e08e00304e57c661176367151002b4bf745ed242a0e0b8d8201669db82534449138ef020c760c9555438f48abc355f070c9c746054cb0391508197525268667365fa807105ac420f6dffd08d7bcd2c3e6cd4ce2be13b5235e977f23e51a02fa9ac56b0b8e6695b1568e83575b4ce420486b6ef3b4bdfab73afc75bed1cf03ba895ac302eed9dd63db96745a44efda268188e96ae9a124fa24ca4a4aa66f8faea80d820336976be54bdd1002d5f42548fe8bc7f127c4b059a6744467aafe95266814840ed2ec7fc9aea084f1b4b48d1305b15fd0bbed6d4a6cd7bf7441a8bbfcb5942c07ce43b6cddf00ca0724faf4fae8fa2f959d3d63867531526f90bb5afcb3638c042d308a1700deaf49ab0bf0344633b860afdfe47b41102925e8a0eff12bf9ec02380b16a5c8b6b7bae6dba854829bab8651e41753e0800eafdc3466a08244710ed8b2c3dbc5592244d3a88d09d8124b85b39627ba45ff1c35568b2c27ae4939050a6472203e93318550d792c43e92aca7e5eca4439d617a49c430bf224126f30818b76536524d119c81823f5907c370deeab707eecccf22dfb7d90657c5ba530493c224f6500eb65b0b02585a1ddff1ca326bad311b768fef98de94d1b8241e07cc5e901331c3e085b3803335236aac9a312d347adc82aebd41e66391ebdd670d438303bf8efa18391d1cac0ebbe0303417acefa53870e4d7d56928a79f5b18022a42bafd1cd66e0bfa621652d599f0d55747d16eac71a914ffc978df21ccce01758fea0311913ab3c8e2ba9f22f860cd7132e2c43c6975874a3c187f2eda39034d63ee0baa78894f4d24714699eac0039ac7a50e6ad215b26b9ff029c6b320028b7e45d6dce6574a2ea91ec4f1fa5c6af9fcb8dbadb0592f7f1b99b522d34207c53b55ac96003831b79a768cdbb1b976be4c955cbf65f1bfc7fe94f65af2be0e85ed5c627428ec93aa4b0742960ff34c915c9c5dee8056e5698720abfc316108b272fd538f2c25e25e16608ac6747c9d21d077fd0196e81ad002ef9d9a87380ee1297c7f0b3b875fcabcbbdf1dbc8774dc12855c1e750a08077b3df6a87b5608680ca8af2ee893ca260213ab79be2722c8c5cf409410bb170c62cb4a7421c008c890c27a318c2ae8f2b62f1c0ccb0bbdf839672776026df3a79bced8a4f0f20ec84e1d47bf32e7aca64e43c2ff8349d00af3f2d61a8ce8bead082d4cd2fa750b80ee56bf43bb16701b50f3544b77dcb0ea88d631c3009b952bd099574848a70dd98cf7ed7dbc893621be5bd97e9701ba50f0e157b862fe9f7e4b5fb4098f7302ffdf7ccb310c42b05328ace52cee383c8d1fa4c921492bf005699537c40c8307bb3cc354268589e7d226ae2db76dfa1381539e66e072cc00fb3d7ed6fa78ff0e05f566103f3789c8b338603d5bd7ba193f35b8a4f58011c8631bf8ed7717460a985c0083eeb41939d3d19f286e0e424802f4763e1ce8bb0630cd04ab5d001101523f7e5b1ac7390483afe3c5d89053d84a2f48e2f34fbae7f7b7bf441c21fa0c6079f6becad400b6009b718aa2d335e3baad5d9e5ae22282cebb702f18bd5b0cdaced8d888d5b06db79fbeca7ff596034782c19c8f269cfd9c456bbe67b9fd05b0228415623dafe4bab86d378e42e60c713a26819ae2067817eab824e9ddd2039730c10fe7ad67896ca8336273ee601b46f3f4f5315df0f10207c45b1e0a1408a4d99d26d2e57682320a085dc673b15c863239f3dc17602988add7a3eb05ee0f148917ca318a6bfd6e926bd7d72235bb8b7923d71c28cc50a05ad04471adc20181e8d718c211125307e32cfeb604e668fbd16c36fe3238ae93ea9bafe1a49b0058b7179b1264155bd3efd5a3f66b357831662ba9fd703f52ad87af43ce03f9051442c8965674bf02fe138d3c7a18b39a6d174ecdb858547131e974702985e20f26b95a51bfbe529a0b3ab1f95a0099a284c8a8c60a2b710f18fb8f3d1d689b042e9f4a1fa9d3728971178fed2b71130a1d8299061307e1431723b68278339c01cee39b08b9d1b543d7a8c69ef725208578ff893475b967b862c04d63e8400c040b4b2ac25fc44464e312d86c004f68c1ff83fae64c694b3699cf8be0041a380ef42edf932377186d2499d4b07e37fd61321164a9b89c7d4d7f9a453f736ff60b5b9274fb356fa7eace6c21da9028228d5f6e0f61cf91fe2de19a3235fe28ff05fc077223df51d769a1acc61bca6a36e06c6968d85f0240858b7e54a8ef42aa0bb0b547783eb805256bf2a8b6e0bacc0e8881eb20abb630488d1168bc76ba460b0c20d82cd57326b419c066b0d316f8f8d88b73a864d3db84384e4950299e6e076b3e5c48410274e82e39d4b7eed1a00f8bc8501b5ffdd93169ac6e5fae9a860196c7b843de2f086e627bd40a775ce42d76b108faf1a066b010263c10c173430148e4f97394be97bcdf5c5029cc8d70bc39629812e7766ff73c37652c10af360677a9b8ee5550788d46a02150d965a78c1b9095da18283d732635715f78c9690df6879f44ee2a8145b71a8306d1bc27acebc8cdade42013c072777683d8b7d00617663009d1178aba6d544c2dbf741b4f6f2dca51b171d906e5ff09920c21090fa43bdab26e65c4a8e8aa6d053f14008a6d50709bedac1a28c33157877620410917d5760480e0d2007c9e4871b947349cbcae81bf5bf1f66829304fa5013af90e48b698fe10d1b9f4974ec20c78eb86d15129a70f879b54ff91cf4bf424f9b2040985daf3493898006790036ad7cc033c73490826faa507a2407f3ef9b994ed0cb13ee39397da2ece753b5e82cc79b9199154e0361c0a026ebfa66efc7f8c5909f35bf4296dd75a5c37c194521d2a268f95ded22535c51d04c6da379afaab9604bb17be7d4e5ab4e81ce51a19f397c6230c0c9d6404ca9472e4842bce00bccc0af4cc8a6a20bab6a551db552c0592838660db7dc4e9e85093f3c0cd90a6ad6109b65f99958e002873b7a163685738d9edddcde3f9a412b31510730ad6673d46014fc3a3f5927482e9d3cc1fee75a782465a09837dfff95baed79bea641dc31f09629c50efb1705cdf2919451b2434579136c6d456f72d0d40f5c9e7035a9ba20aa09ab5c6634539480e7d92fc630406bffc031298b93ecae9f1b89b72a6979a09318bf4c00f30533ee6737dfa86d998fef55a2f83a7bbd67d3371086ec94d150a4222f7f26f895a8a9ee8aeaee9a5267e825d0081f1e5c9883a720cd13a40680cf8015c0f317f6ea10d0b08c558706697b6feec3f48990ba5ff837116b1cefa04d3994fe4739f42769801532a616e7a50412feefd93c459b2acec97499c07ce0f8b47b89191e729f27e30ceafe883d1f436a181e1699a0ce9cc18777b601fe008e0ff102a75a6ec2c5003a2583fee0c5e56987d3f4c4f8bb5b5177729b9bad002351016c31db3ebe62b151664fe5fc2e5fa4d4c3116e8ef4f77108025986240079aedd3f12f49c0658216671ce28424e644168eebdbf2408e6ed94f640a7b7d0a48e981e9030fc1de0409026cb59a19a82854f0eefa2d280d4b60a46e358d680f331ae69c5191053d94dd92c6f851aa9e04428e615adaea0c00b2e6d375d79b0e43b8bfdab96deb1c354a702f8e0383dacd3e941fa921dd035e1dbbb91283440dc060ca229a49601e45c933dc1ca82a26ed2f6ebb7f4069ebfaa142ebe26cc30dbd10ed88d8704da012a7bff2beaa177bdbbe65aecc14317c5fd4f30b215d630a05893c062aa7592ef0a1924ea74fb108c65789067d1349a0399df6c1dd802a01c4392e48f72f1c10b4ef35c1bb66f2f3369f1168f23d425a8babde13e9e86400f2c31b9fa07aad174139153e07c23a43820fb1f1b621e9820bc9bc3d4bd5be09dbc0870c73715ba44158cb1589fafcfaa8bbe23f1e0216c658054965adb6e60ab92521e01f558d577067134015beada31dd6898f24419508d59bb8c09190080a9cfeec60827d6025bdd30be33eef8cadf55bd5499d93bc697fa4a0cb3faa260528d3be04734a3a772877a42ca56838a5d9b3f848a311daeeb373d958f37e63078a30c18cbb0430a5c1655b8202bf613b72a62472d8798b85babcc20b5ddde0067e78c74b85fc7224617b354771e5eaab6f4fd4c94d189d2bac244d4e7eec6a04522c6cc1a4e5cbeab8b0ac70f843c43ffd73e35be4a7ef0d971612e89a760605e2b4cd237933e4692db1642747d38541fc77d8ffaaad2bca53c7ad7fcdd39e075e39208d9a431863a3ebaa112d567bec95fc14dd96b553c71907a7f086a7210fb3968719ac7d86f9e1c6625399ce0388fed307df64caf5c4600aaa5956165b0702e3cc1ab0532b39e43fbe1961a2de0968d947244dd7ee990f247ea0d3730c006f670649bbd4d5cf38be5799375a922443dd81fbff8ee615d55deb58d56c00062688a9a67a6520c49bfe2a441351a57918c6ac9b8dc6ab3687a8563e6106630e32f07186c57e2d9160ac42afdd875a4ef45fce978551e7cab19f328cf99ebc09d69e1b70349d9ea9c22aded1bcdea723cafbb8e3f143ceeb1204ffcebc6e6b092009cc4720a3be9ac89387c4e73b4057089d021d7cfdc07a15ed1b8f71dc160356332520174dd9990ab8d34161292dde08e6f3c77961108c20d09dfe3b52c60875bc437be1db6822e0102bad0d908f7ebfb172c724ff5cd1559e4bde8e22d505f251a919a44618480fc6dbac82c3fdb340fa042e010d9dc6dc59344bc379a909881face2d2a544eecf1b7f1f0c6f267d6003b93a41e3ba21e85525b031b28f0572295bd7ce62fe348524e2136a542ce60ee7e81a666822b3dabb6cfac498b70dede6fc207764d734a64d0f7b68c9c5722651f917990cb447915332b9cc9c71076065f580cedc3010a4b2e4bdc0593718edc01bd6aa1396698f634051166dcd02cdec8627e61f211ef1d377a00a7f846fe999fd26134cf3f566d9aae5211fdc072e768ef12202809056ff599abbafd4c5b7a25a592fc0c16a6a50e4e737bbd3036400564f08050e1aca147b33ce3c30de48bea1881b471b311a7776bce851d500f9248afb156f3871644de712d40d901334ae2d3c043a740a5c21717cd6afbc08c712f544f2884f4d82af848a15761ccc259380589063526a93b2fedc4543ef0f89993a7f5adcae5b6e1202047b482c9c61646924c70c8464575158963eb3cc0956ec2ed8878b2c838de760dc3863fffdc9b2058087ff4838fcbbf50ac49e4405f16cc0659638797432ff56f010d8dbbc3dab0149f424f01a29a95a2a6fcf6c04864c45496b0fb865512cee810296a5525a085aa05f4e241f9d0e132e0f3692093dfbb5cb614a0c4fc4cbf009ea0b99438b4f4f3c1f617bbe58a4c6b578b9f9049656e5548f58e2613d75cc01715c14bf4e7dd28c48df6a7ffc726bcca360d50d68314ad6f5120cc3f5ec2cb578e4993627908513fa061f9b6faf3f018c596b08d5586693b2e5c5754582875714975e9aff44613c0f462d56feefd246eb703408c3ce415c5f244cbf1b1d12e7c6c585a01bdbfcac1c386c1b17829dffe13aa80fd3e357f44195de100e9d3f695b349ddd6477e25cfbc7e6aa3dded90f2137eb0e9b839c049353ee34152e70313212e007e688c15c72029f09b6a4b41d46295209f0c2432a0e762b3dd266565ad4d6d15b27e50ec831882ec85f64d6524e7ec10586a00d662160071535e89168dc248cf4ae7cc31f18e21487b6a03c90e738520f2a577a52d5b89af200a22ac083879437bb510ba626320a912a6425e087171c0df98ed7436a027f9482370440340a67b22d519676e5b181ff644e909a14c30904afc293a9f5374c67a93160c0cb9ff5ba0476d7d915b47e4d453edca6357a340d0050cdd26b45d8d81c453a079c8db3d3b6e5287269031ea01d4523276d142f005d1c0f78fc5c44bd546bf0ce0a8ffc514f0e7a942733e32b514b49bfd60a770aa7658a6627b81aefa120f574f51fbef466d85fbbcfb060f75e9ed8419a55700cdd948e9a791fd92eeda363137dfb4a46290b7827e0bea07aa5b93b431375b804261273bdd2a843fb62c110fccf9395aea73f6cdc6c4ec74411df35b4fa7289024adf4a719dae9a539569a4db6599d787e02cbded7b61205e0f4c17c23bb85e0f24394ab01eb41f1660c00662754e35c3c4f64bb9cf225ceb448c135051243809fd469571f4f155a880094e6dab6f79cfffc375c2ee2edbac1b4081ebf833ad0de996708d7b0c8d312c1781081cad3791def4bc6d61c1e9ca554e2483ed907804b457e1301a912279625ac841ada755c1b8652f65f8a229c8c695402d6414e5046159ba45de88a3fb0399e5bab9d46fbc97e283de4a9cb18d353a7d1df57a3b0a37c45d5985c930ae9a61d9e3c3308574b8db6ae781385543bbf212545c9b0a08af7a613378033f86caf2a598f4a8185e5a724fa6d6a1c49e3672179698e85f01d4f31b0041b96c59c1a7fe185c43f6c7f79ea943e402d0013f49dda188108b0ca140335892e9e67ae887a97700ee7ea2384540cdac804f9eba866fa4e7344a09c771167e0810c7d240ca11dc83c30f65354160b2bf67eb2d585fe5088f60a90b523bda2922281dbf1073a91746624d50ca04ba5fb753714fc724cb9f08201508214f86d04290578ef0ffa9bcf386a74de796f7adfca6a91389a15a9db0fdc40ba54d9d546a5da435d9aed5ba17d3aa0527210dbeadaa9771b7537a1880eb24013176c17d0f347b0603f4ceaa58d31538e4a2570d79efe90f3de72b520fd96601c549452372bbde8c8ed5754fb4b1b4fee09bb9270f7526de9cccb204abd8d10369375db73e85860bf4daee9ea65d58307c01ec44dd71ef2c90decbcf22c5430aae2c215755da71a8c36356730f08a312ea949144b42dcc494ffb37f426d749045910c212189f8be0cf63b842ef8379f440cfd4e13eb1a34426859a85469d4c00fe826d071e54c81afb90f13326057be51ebfd7fa2f72273a77a551fc580f210fc01feecfec5c226062b5ab043984852381bf5642cf3f0568132c87b358ee3a02cb28a3b2fa0b0d90cdcb22380888943e3d8744c0c8f1104d4831035937af9f0b2c010065e23090e4f96f6d662ccf4b2d23e6a3994e41da69f0b6d7403dc44f007d4dc1b3ea89d5142263391da3935a801a3b666f74bf7733335cd55b7f98c7007aa493d5364e0e330e93c195f6eb79fda9aee1840f6fdefa2ad9409910617b0842442dce288f46d04ad4e72929c1374d0025cbb971e59ec20e5b06abde36550ba333026c323c840f877596e0965924dfcd184ed799d2882c4bdc23a880776000274e28a5c8599ea62e91f1caab43e35d69676f6e513d5df265ad4d6a53980701d46e1b49bbf875602580ef3ffc38311d0d6942154fba52a1cfd888aea545700fc55d791f9da456e26a0665b5e9fa37d165fce814d1bbe7c3bd4b17ca08bc460f9bcc8f479ab8776379f43b4e182015cdb269a193434dac91f5353de7c90e7e0d49f2075b6c4c39a9e268be24338b5c6a45d3dbc178b80834e788e32079d1810d015804bca3566f28f4362da9df42986e15ca892ff1a0bb9c03cd3e6021e74c03f88548103c7a4afb6cb4df706f26ad412c5e35eb6b83e7e479cd37aab750b409eb04f68b746b722fc18b3b971c40553d9af49f61360a8fb0ca36751a726c6f0a7bc20208e9d2bc31d130d5ad2cf8213d140ba0ea095215fcadb33d2f2c209a0702600305ab97405225caff0c7ddd93bf04b3c36c37bfb0309b3c050b8e471100cb4d52cd59f022e0276b28c2d61a0952121d24f54d8e5fabbd00295f51fbf30d03523784a0dc69fb7e5af7e763eb58081c4ea583745bbc552ef56e0be040b80ffd384198b84d28f81ba72cc18abd2c7ec1931200c08062c1c2806242b5d8f2012c1ac370ca74498e79a4765cee7a29de977fa6012301d6da16f4f499d6f0b509923d4b8b09aea483ff261635696a1e3ce7948ea8865377bf78dd27f01f7cbd0e215f5f837a1ab10ae26d50b3764bfbb29e5ce93b9658ce7c569eccf13a16050c6e811df9731add88dc5b5bda5fd17d6dcc334838eb9e3945eeff8e36826192052528f032a00ba355f13bfac34cee4b84146940bef6c23c63963488ae620b42070430b50a7b03eac908f19259c428b08d10d8b4f5e67144949c1be6549fa282092c9153a399e6ec749df58466da26c95707b48d2801046c5c7aa1c7af31e85e0f56f41cfdb983b6080b612bf408a4176abc4dae9385654c7a7975ef5440b3a107268495c6d79d99a1334e9029a5e316838ff5055f4323d29abae6e42ba0f484095f5559c5317904b04e6d0ffe1f7805dc75b987476582354f642f79e124017b0f9d28f8f6fe0a610779a9ae25a53eb5cdcbbbc1085eedc3313847a41b15e1a9077ba6045bde9ab6a9fb576d985ac0dbdf54d2bd4549954c63bcd1f6d07c0c7f0add9fb398a7a5b72f57e4e831e4aed867db3953adf7518208e7fb0ba60ff9cd06b7df3eda50db9e0f4d8ca467ae6381cefa34ac0cba0bdd554dee41fb4ac69207430ac7a574894281088aef716227b2493065a062b937546bb2ffb2c43e52aa0ef05cbae45bf64b454ca6ab57c7ed808d479356477c903fe8c8b3d099037254099cf6776315a05682d86ebe596a345e40ae3d22d275d8074dae2633dd8460ef0f86e91f62ca6e98952f6f00e09d2d1a6036d01db043a5033bcd846081b625970e64fb9b188fc289ee61c2b4696ff3b4119e9f188dd595e9060e735ecf77d4a30d345820d1991debcb1823a9f0eb3c9a7bb257493fb6cd7262a13eb2fd747dc50f6fc6bc5a336ad0f5040f67890ffd9a7e0362825a0aa80f8eaea6352678873c00c379d4f2f3f805ff643d7426fbe1e19fac34254e75513690d343c57bfdfd7b015de9e262b25ce8e3c47136c17d90fe1cf959645f834fc2fa65c7727f0dbd4e0616d01eb262d97e5776e7dd3e4a7e783ef3263c2a9e59e73d41dd160c36adbb01836dde27aca1d62741f183599b85f6f7ade0557b554f03b14f71a8e5979e200275c2a81dc80b64f3f53e7769cfcbdfd4c9ad1b8dfee5f174f3fa4d7ee25a900ca8364125ba7141093077305afa3842b41c6ee0fef32b91c9b655f855e9dcc501c5e77e0e1b38c8a9b71dc8a03f389253d9a97193a3a5472257ec6305aa1e700aca4a7e23237d77f0b919edf0e380d8a3e9d7c03042abfed4fc4595443df6470f5db6410ef1a46bb45319f00171314d73c5565c3c85dc66d0eed7a52c3ec7d00d9c4eaab76640c384e8cc65fa21278b89c7da0cbee25f9667baf8be1a5fabdf037364ca7d4f347d890415bdfc151fb430c40d8a4aa228ef3f020ab5e384739f0c294fa36e411ac08f72fce3b3c2e9a5cb2f5a8c5fdb6d5898f4e87d85eac5370b129e1aa7039ba99ef81ea34b7b9549d5d2546ed70dc64f19a5fb26f7eaf8840e5ea513bbf9aca8462d0db78119baba8ad995f1978ffec53dd4edc93f5b18330214e4cd6b34d91f9201b9f457df457422c8b444bcd45d52107f32093797e0c4007d26cfbc521458e6601356a45554d456c66f0f399c75a7dc84a49d2f113ed1032e1bf7b7a30394fca199ab7c16ab7e3e78890d32c66f665ad89c4f033fe66a01456e8958e347a28dfd8995d1c5bf2568d01893a21b4e71dd85a8ba6f5f924d01a084a5ed00a25e2366d0e8c4a8aa934fcc73e2ee7d1228c716c47b67660c3b00168123c4e63ad3234e8e281274d8bc7e8a0be2c525d86a9639d21d852910210d4455d0e001b2da13869131c38972076403bb3574fe270ecfd47b60d385bc2009441bea4710edd0ca90b671f89513db5b2d46b2900ce42e95c9d8367e50d2e6035342e3e702b714862a212948a82de93f56e5bd2c3fe0b4bb9e853e7af3fac605e4ea52a275eef49866183f533eb8b3403eaa675cf262d46b88690bafd18d930038bcf4fad829e014929f4934481eacf943eb077374553ec5b5624be3a0ebbb06ec222e6833d4d6dca6eaf90ee766b0cda5fd040f5c77f9c4d594bd53f8bcaf0b0e01475dfadb034c0d9b9721b77f38e9513037eebc09d158616e80089d169105737daeaa26366b89ea7b9ec31407cc23ae312d2001ea5937377e49661d8aa2027c78c2f236db0d42460d273e4edf5a7b45c3a6f60313f655ae458c90973d5e01bac1c3d47d8db627f7f1f85f1bda702969165df95d2f66d6758147c0552c3600324e841e30ca18bace5e0c01313402a12603e95064eaeccd6fa870490c690e01355f79a532fec13cdd09187169aafe4c623102cf4cc39e747ab60db88252710d0881de3dc70026dbee1dc3c785332c15418ef65af21c6fb17e26f9c9a875bc09795db2488677347d7ba2ff7c0c738a95171a0b5f62308a249b8bec203cb44409a4faa09a0ab7262d864d4b15d967901c147c989a7640d3f6d2cbbb24de6c7f0cdca451ae4732c326be5beaaba2a5ffd471f1f024c5c09548a58f13f3818dcb0678b8139f96c7320947784beec04ad9b24e72146b58f70d56d71d5233e344d80f57cf44049eb3801625d09e538b1957dec8a233ae63ab30c138204cdaba0fd90513955109554d538266f0a1c3d24bf9bac6762c5aa51823af8054356a17cd0b04550211e34f39114c051c784222e00dc2141d7add8480f80f5698c68690311c0f0017465b91da26079b1f17aa6372a8bdf8d67932f3baad1f64b6fcb0a57f990e443f0cb35cb85073312baab8312066e3ee6d14644b42f9b8d06fb32e850b920d54920ec420a9fddc519c43e8d1704b1bd72e1f407022c09abc2103f43327250b5ca0cd69cbdb8536a1402e846cc0deb8ae23432d4c9f9c75a474ae3819b56405ab236d541be5582294894a3c691234fdf5289cc721c608801094a2b4181ec6026060a68da102f1f60e1120a0e0f74eb95cac771d87b5c23b1ab20a621cfa93033d738973ce692f6fca0d6f3b891c790574dc8add1efb6c5726afbb2b756feb0d8b3b87d1b7a452dfd18f0d648b210085f43be2b5673db4ec1ed077fb7a818e04a748cd211de5f551c10343f00e19ad53ec95b8267d1169319b4b82104f40ec00c6b5b51f08411600c56ba3c9968394d22535071f3d9964b6d9687f572cfb9b094b39adfff37cb229494564df2bff906b735db59d247278f5cdc0b06aede2ac0f677b34d8f2f63ee9a5b4ed7968d02b62396a1b6211e0ddb0cbc43fec41954b0beb1d5bce7c9e5806bda84130e5b97e2bccbc6ed68861ab42cbbd3e2e3af5e3061b44ff05520dd1baf283ec9695fb847d8db8ccbf536c6fc9eddc145f28e61d020a74d6bfb726e7c6f6e4c3160cc6f144d7d05fc6e99d20d92a369eb8f207b20efc4ad28230ca2711faf4ccc75937d9553c46987c2f9d0656e439d206165cef0b5555a13003dc93921e28555a66b2e0bb74e7ba8330ad10957581b9e57049b0038b2aea15eed52d716068c93bcc5241dc0c360e6d8c3f6818d36738939789750e455a10bdd1b2853a61d295db2d61b61103b56b3dbb7a9410feb4c565280f60008c410fc9d261e25af33902ceeb694c0e902ec5970159a57cdafec01e4c46270486b938592ce986333121315fe7ab5bbd6cbe5427e59328679bb7c01f3f9b620bddb31cb73b49fd8ea3b66e683c1bc96fc440b7973616bf21cb5f3d6aeeb1540143cffcf2ac64abd10d2141e7f3e89f16f0d2a5668ebdcee8f19d23d4d9b1800be112f4af82c92ff48e7b65b86aa07afaa0986c77b39602188084957bd25d5c0711418b73b54e1a7a3183816a979cb88dff90317f015d3e2c6e27cb08baed8e03135a480d87ccc31d2a9627c1baa2e53dd8f38c1782a119cedf877576b1162f066b75bfd854709a57b9f8b12466cb16d72635e71bfe38802614d74d4583ed72003273a48143211fb3be169415775efacaf0f9b3aa65fb866fd423a1014fc38b0f50dbc61fd603519c61dc4efd6d367e4bd8e5b05abf4fc594167aad6bb82a510d66017b8b1f5cc625f0b37aa75b6b413e5239ebc70235481addf60bc01957880d70edb5076e84a69b6ef16b06c473b51e98fa013657835a977fca84f737289f069635c0f029bf697abcdb0d5354608868edea778a5d33adf255651ac158ce7303457c2aae48d6d75df7552058e76870254e8c14c8abe3bf96160fdede8175ad00f77d9523b10b338934f79d3baee5c5814cdef1b764f296e9745bb591504de50a3af63516059aefb2af8d64f0211406007ebf6a88c4819768d777d629356c7e0b6c9c8ec227b3a418fb9dd6da12253bd9bed14880f35f76e3564be69de44ef501ed9e28af287005436778dff8893f1a2c1007fccaa05461e6ec7b4ac53dcdf30e2b43bfc4019f66fa27f9ca3eb1b715a383f5fb7102473ae44b459c0c563cc306780d3374f9c52e476dfa372602c4a8e88aaa71280679cd3a2d6e2b380071640e81d32b8dc5d3134ad8bb5f22898318c40e485fb758ac2b49fd647d306d305200ebab9f03f96fc52d4f2425806c4b47c3f44b36ef5fc564b0191e3274ccc93b0bca96d799dad028ba32ce93f0a38e08816830b8a10ad029daace6d825a450390b0dd302b6f4b58f261189bb11cdeae8e269778ad120e3d1f1e4adfda1386bb00cb8efb97b4737b05fb9e6b83725cabf5d6fdce10613db96055ebd23467066d80ba53e15ba5b821857808318820b959b943b24ce5eccd642fef8749a1fe0c88d012e9a0044a05223c0566094d2e7557ee273380b12c78610bbf840b313223c1a08961c2b3056e58ec2901629dccfe070b01dba0edb0d0492c14705dd37b242e207e4941ea4d4f7b99641d1c1d7c0dd825f4ddddc4ae90d5adb684985ca63490f04d83a7de60069a69674e34e9190b21dc2edf29d301aac1420e52006e19cdc4b04a2084335d9b4ede83a0ec2b508cddd131078a0eb345113e75216b793293aa9006be87039c822621e37906147efbf3ac4668f6d5cd7f8523b7cd387f036e73006ad5c935ba8d75696941a11506b86f3cb4713cda59e20660bf1f0008c65dac804d99e60db0369bb9530f9179d0f1e340b99cb50d7a23374fe00bff47d7a94840bb54d81bb6501307f8ff9d1cceec3a7e79eeb849d90b6448b3a04bf5790aab9090d63db0350ae9dba81b6e69a359d314d97cfe4f4fa5f7dbeaa3c22b5b28cf50507f3731146c60e3ee0a2bd894e2180f30b82e6b7006d1e9867dc531bd5370a0d79da1a1fb334e39e37a9ad33d5d9de3c501c96a3c6f6e3659665b8c664c08107703e25913bc36054d0bd4d2831ee876315c45bb58a47feb849ec50fa359d71024520e326b8a97729bb6083659e9a6c934bcb3d256c72f508f0457b1afd14b405d1d9618d80f008b65ee4477490b60902e33928a42bbebdc8ab7fb8e7c7b3f70c8a9c9dbbef93eeacf0304fba3e1f7e86ff73d12ce4961f264dbfc28d69be2e006aa96993296cb3edd72b84a66baab093660a38dec8fbc578021853228e4fdc0e0c7792c82bd52f57bdddc2ce177af2a325c2f225884b7fd02ba80e38bfc485037f2e1307c012442668b1ff7c73af07fec7be794a9460c03085adfa04ead6b60847f7ec625322dadf787ea430d04d88f591c66d2310b3f8cd7aefc4e983dfa301b26b88ff3459f2dafca3237be06c09dd61a9557c4aa316360d2ea0b7a3fead0107b41709c93841f164f649912f1276cd055f9e20031666ffa0a5efdffd5f2800830248963e7e2fe330db56aab8944d352cea075ddf16d8cc3fdc4a4bdd49390435ae91ddd2d5e9e7c338748a68cec114175992563b809877e658ad8a5dc61004d71f84ae6819dd0cfd62c6b5e8f8c72db877faab2b24dc84f269d0fad372ef0f795806dff9f95790d1ea4c0b5efb1ae79df105af676b77774aa7dcde40940d014dfa7f9e8545a34fabec7df6088e44657d9e28ca5f67fe966cb299f3ae9fbc0ddcdd263ae5910210378c2a4e312e58c7ac4cc4c747ec01d3c474fc998651d107e798498bcfc2cf97b647beb5f133ca878bebeab876a6d8d65ec28374254d5b04a086d230dfc448b9d635cfa59f4ffcaab74c324c88bd1fa0b96b1cf14787a60266a4b3c15ea0828f0bd6794cce450b368b01cb1da066b40f66b6a5e9529eec05b06b3e218a611dcd2a0338fdb7db2ffaace2fa31ac5f40bba888aac1203121026ce685a6888de03d5452ee603e8113beefc30dc14cbf25958a81a8a4a840240cdfe1ec9042309ed91235799be2f902003ce3b4976c18b8ed73b18678794dea0f4dc0d8b0ff2938a4d29ec756e1306fb018c24b47ac8ae1da1a9fd547769cad08d57a5451fb6b275e718db32c44427d9b0a7fcd0a50dd5807cc817b9f9f1f0d0e4ad827bffc12d8a4eb60dceb98ff5a01550485ac4ebb60b0a7ca661e1dbbd30871bb1df8ee106a6e3893e11ec46d9d188ff9f0359922c70ea794a665e7f2a50f2128d3e63d9e5173ac7e9d83638c53e9de0d0ddb50daad2cc303cdd6489ddf0d7fd8e1a8d59a8e5d70a909dfd24d2c9d742de5df0c1f390011f65906a189370d0e9f6405a716ea316006a2526a78cba0bfed5de0b30ce20e4264c8a4b94e7705388a445a24b84c8bb89da7c59bc5ee0a0edebd87be980dc1f0871b1a7e069b01cbd8ebc3923b1a4a15f487ce47415f5276ae2dd3417609c2325f334330308d0a95e529dcdd6bca69a5e6286668e078c2a6e624c870dd7de23afdcacbe6074408db145c0e6b20a878e713a5476b2aa009ade4efb006983567694e55f2f695e3034d372dcbd7ed79e7a0d78274786d2505e59d7383ba0879d0f1934fa87241ac03a66b57accc64db643108ac2093ab18ee76234e262c73585b40f148db7115c707de5cb98d48b7769a4ef62262bf72a953446a3a79c2fbbc3f26a72070cdb1190ec6cce473a90450c794b91434e6e78f9b47cf54810300672753ede88e98c8dd0a3d9ce938da4cab3a3228cfc9e9a8d253906e7ab45ab1cad794da355b197cc00b0aebc5a1bf6defd40683f1caec954eb81e889bdbbdfe724fbd3beeb5bf372301611791f51dfd3cdf2b93e2d8a72ac63a459961b017778827e49e5c0e26b62806641629d374055b4c9ca9b4786ebcebf3012f792ad4181d4747e59ae6fcd027014f66cd168735947c1572c3deb712efcabca9955eedc877f3e580f4373e4a6f06c62a28262ef8d0c88d4cbcc2dbcb1bc8a867001ee63274371475ccef161db00c9c4e19636f611ff203be5bdf0d2366a4ffb659acfa67940bba39b016fd92ac01e5da21a1ca7dfd457db0a2ad2838aae11107ff4b6d32e5de364f300cf1014409f6aae39fc36ea8cf70d34ed1e11f3433f52796f42336c80d32b195b273e0e200ed6217e30e2585580600d3ad67869361346010cd23dceec4d38c3eb2b759200963cb4452dc9fed21f6f60d209a0176358f049583c85e9354e55cce488d9cdb0ce45abc53b6a9333df888e2d3e7c63ededd44f57257c6e502f22c30e3a29dc40fcff1bebdc3e672746dc325136fec244649d5b144ab0b90220d842ff9bf8d940bd2025cf7b0ab9c0f4f0575eb2085b3cdb7a037fa9cbf171090ca58d3cb5e520b7d519ff9d24de471d2a1a880957b5c89e3d581148a28fcf403e125b1bcd0390f22b974b9938a6e987c096aaffda697d2f50b487097a05cd447110f1c24474f0ab007d30ecf352f078741584f2e53c2bc1eb01204ee416b76120858c8c33d7b04d16858a989432e7d5af36b2ecdc07535f4f895fa078d929fc971cf39627edd0f9c1b76a0ff81b0e872eada750d0a9e32a6b0f8ab8d71e4e913b122cf9f9c090686dcb3a2b162ada6f92a1a5e4dca935e734c0b7ea62deaf190f64cf80cee5b0726acfb18ae407ea619e78c5195a46ca8059efc586d618780982a87ab5d0c88034c59f947f8f92e69145fe788838e574ab159edd184e0706e804e050d1f3ec7087a9e4f4d0ab12c0066f2829ebdd87503ddca3090b15f37ec3ba9c0c5036579063427f51743df6bdaac5468dafc87e02983ad981600198e67797649ac56598d08099f6299dfb68bf9891c966eba1a9ec6e0ed33c0f012a4e49de28ce45b92b80a987ba942328772dfd0267b7a10401c53a8204773136c6008cff98ab79c75990ebbb7bd00709bde6fcaf8e45d509a8305f1fb1f447997194ab3933b1cb88ee00baa5e31cf5f7838fb62e2191a94e35f5618be7d42d02f645f071408ed13fa780f44de54d83862bec967cf3c1f00f6a69afa2851965b2570a7fc6557ab331ca00bfe3794286f279e92d2844ace81c8bbceb89ac2b1ddb6d824dfa17274193e0a038cd6b514fa84a19594f772ff72f6c4a488df37801d2eb9d29f9e13bfdf0260053a6f937f07413fd607adec071ca80f8625569f3953fa6f4c05eb092f910aa40378aa0ae264c4b0f471a83a64898f77e1719ddde3d930210013d9403d2ed8a60fddfaa4eb6841e4f75622a5576b6f16a962344e325cd8f5ddfeb879169fb1180fc9bc9f81a4b9dd06848107497c6d4b478148a3f8f915d1c014a9b7d09d92c30619cdf4c62407a3bd1aa4d68e4984b3b54fb8b70575977440b70d2705341293038cb06cd66c7a01c074cd22786a032303d7040ac15a1bbe2940409fdbdeede3042558ee40c033aed008fd7c6d6b6ddf36569699e2f5fd0921921105aaef3e3e0b578bb2c8a141fa52c6b395ff1e0ec247e78760317089e491abb282a7dae0b30e396761d9be46c452b5f3506e069f1cf5e633855e845ab5e706af6d00cdade40c7588b30d8086039cfbaf7a7e341bba0c0f39c24ab5b7ef8c7b6c3134d0f5c201591f285672d937a284b71abac38ef299866c4976798b82958e6bca727377db03 true check_ring_signature c70652ca5f06255dc529bc0924491754f5fad28552f4c9cd7e396f1582cecdca 89d2e649616ccdf1680e0a3f316dcbd59f0c7f20eba96e86500aa68f123f9ecd 1 9cc7f48f7a41d634397102d46b71dd46e6accd6465b903cb83e1c2cd0c41744e 3e292a748b8814564f4f393b6c4bd2eaaface741b37fd7ac39c06ab41f1b700db548462601351a1226e8247fea67df6f49ea8f7d952a66b9ec9456a99ce7b90b true check_ring_signature 3da5300a7aca651dc3a85016824b0620a19973eae4af8910cc177faee499358d 8d39d8d877d74cccbd5fad872b8297eec3f4b3f5187486f8c98a2ff27f994800 16 7af6983daaecec1bff70b05c7369fe1636270f8dd606d39eba974b8c1d5f6091 78c2676d12505b7d5a63ce29f736124b48a02dc78bd0ccd1e9901344811bbd3d 957251408e9a8b255adcabc52bf15cb8a05501e2892d7cdda22ce672adc0cd12 73e433a1668d056d9b651aead47658476cf23c39cfeb4fd23625ed94af439677 f58dd3a46e0a07000ef6e978cff87604c32cd3df487acd220b53b5ccc46d0bd5 3480f2437e01073133ee9cad9651665277b09bc0a46618f975a746500f9f34bf 0ed796ebd217fcae58272ed7c4c0b058f558961f95a68ea52f59e4e6f0374d73 729e5938a034b2f50b583d4ada5541a12de09aa1776653a821da7d6c6b057716 1a9855ca5dc19cf11f49a87b182695451e83952f2df4ee9009bf0f72d6e25194 c609386fb270c7af8dc4ee102fd33ed3c836e7ea493dc79655c9feb1cfb9c869 94ce9f9d3fbe4848aeb59231d749c6c9f7bfaaf99c31e4317c2b5b1b335f20f9 9331dabe4d6f230a7c45417dbbb28b0808b4b6bdcba83b774583d784413ddb4b 437652a047fe0872b264c094440625cb5cca5cb8d10cda950138c97dd09e5943 5c470fa7fc2684336917a626e2dd4a09d68b4ef9499857c11e8ef0aff77a9262 df3ccf869b0262bd33952ace2b1320f809e227949ce6a2e89a245ce5da75e250 4ec480b685a07d091057e73954c8bd5e4646b2bd0bb0a46a38f74cc44e0c940b 4e7ee4fb45708e2bd97b2ee4e134224714c2fe1c0b679b7de838d715e281f10e4da8a6455bf4683a91e6e56be119fde36ccd61f35aa4867e9725dda7c18bb40afa097778cdcec51fe2d205db00ab9a00dd5f6f0a2a3b8393a7ae2bc03d6ff5073b25f72be8166bdf2e21a4745841bbff68a54d6fa3c77577a6b5c0f0b7ce0a120d07d704e9a30f306a63d5b137534ac4b60953abeb420d526f4b501008bd0a01790d806d86cf96391953e8b630833a3d6a01f023da720e26015287dee5c9b9060f43fa3574626a3673628b6b82c4b0251b5251173b59177fdc8f79974205090da7a3d93d1d274ed39654c348870da21ac66ee7a0e072e504aa01ece4051c3f0c93eb6065ae0e4d05e49144b986b33ee741c406860d39c953062531f2e993140c43aeeef6b144cc42bd13c2aafb10402edddebdfe0b426730163fd530f7b2c3009d4b98d761131c5ccbdcb737b4a1e9fc72c1313fb45eef13ce4d31c40ec94c0466cfcba1771229b0d2e014d716c9a2c10f6191eb67c1bb05e72f291d1d98290c2b4542d196df41410992428e36c62965a2d68f1fb99d6d4058b7e0360dae2c0c56d7980a546f547d2944c3bb6672fdb7d40378d47b0bcd9ca9ce55abce22ee0d94a7906ff1e31d37578a919dd92b4615ee0930fb33cb494548e13b731b33320193bc5b88e5864fe2d5d9a1b2bfb343177ff10d647337205f22a37b5cb385090bd2a050d055cfb9fbe01cc88ffa3fad76eb5b7baa50cf5f379ac6cf2636da8308c945c859cae02e772e432b177238a6e3d75b5cc6e676d1f3989d78493d86440cbb5552586755101dc4dd43bf51cab188fcc919012f7d9b19db5189fad02362059c8b3cacc8f80413e9a43f41cbe154ad55acac98287edef98240b6d8a04fcd0a020c8481164041bd7165a6b90b8560593c8848d29995634392dd22f3e993bd05580e41349091cb125eb2048c53dd230808019482f4b22c83ca678d9b01931b03f84ff3b4a78c138fbe36d013c9a423e0233962be5f278b535b20f05c829624043641b8386f7cd01a77c4077a5a6da0eec912042c60d9b6bcb56cac9713dc7002ab69ee15e0013204f68d6093b158c21f3a09e205b15e627b3fa33d9a0cd0300d9287b5d75ab7dd914ed95c8ed3db0bf05f68792be1492c41854a2e36c123d30427ff0fdf706d5a01f6cf6b1c02e2140f7b165b2259904528f12b527a7de76a0d8f90c3c027bb2f71cb1b345fafee0628b3f15049cb577b780890b9b96c53160b9ec99cea512304c94325b2c894b6dbc926c9b2b8f497a765a180f70425174802033d83980e2b28cfaf767d6a4da871d6adee474fb093e89df7eab67255b1b70821e64bee166892d1e5ea9ff2dae7b65231427e01c63164b5f3f84626511d2f077de2741fb75e3d940646480b595338c6043cdda23f0fb48d875498675e686eaf false From 3e914ad831194759a7b1dd96860a454913e6f32c Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 7 Aug 2018 12:48:04 +0000 Subject: [PATCH 0226/1404] simplewallet: add a warning and prompt on rescan_blockchain Many people are using this as a "let's see what this does" command when something doesn't work as they thought it should, and thus destroying info that they might still need. --- src/simplewallet/simplewallet.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 775b7c3598e..c2d2c771949 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -2342,7 +2342,7 @@ simple_wallet::simple_wallet() tr("Show the unspent outputs of a specified address within an optional amount range.")); m_cmd_binder.set_handler("rescan_bc", boost::bind(&simple_wallet::rescan_blockchain, this, _1), - tr("Rescan the blockchain from scratch.")); + tr("Rescan the blockchain from scratch, losing any information which can not be recovered from the blockchain itself.")); m_cmd_binder.set_handler("set_tx_note", boost::bind(&simple_wallet::set_tx_note, this, _1), tr("set_tx_note [free text note]"), @@ -6594,6 +6594,14 @@ bool simple_wallet::unspent_outputs(const std::vector &args_) //---------------------------------------------------------------------------------------------------- bool simple_wallet::rescan_blockchain(const std::vector &args_) { + message_writer() << tr("Warning: this will lose any information which can not be recovered from the blockchain."); + message_writer() << tr("This includes destination addresses, tx secret keys, tx notes, etc"); + std::string confirm = input_line(tr("Rescan anyway ? (Y/Yes/N/No): ")); + if(!std::cin.eof()) + { + if (!command_line::is_yes(confirm)) + return true; + } return refresh_main(0, true); } //---------------------------------------------------------------------------------------------------- From 9e6c7784ea8227ce371f89ff3f069692d26ef5b8 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 7 Aug 2018 13:05:52 +0000 Subject: [PATCH 0227/1404] CMakeLists.txt: add -mmitigate-rop to security flags if found This is GCC/x86/x86_64 only --- CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index cab85358180..346b2592e19 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -572,6 +572,9 @@ else() add_c_flag_if_supported(-fstack-protector-strong C_SECURITY_FLAGS) add_cxx_flag_if_supported(-fstack-protector-strong CXX_SECURITY_FLAGS) + add_c_flag_if_supported(-mmitigate-rop C_SECURITY_FLAGS) + add_cxx_flag_if_supported(-mmitigate-rop CXX_SECURITY_FLAGS) + # linker if (NOT WIN32) # Windows binaries die on startup with PIE From bcab579864801e765333e8766b714adc22e47b8b Mon Sep 17 00:00:00 2001 From: stoffu Date: Fri, 6 Jul 2018 15:42:08 +0900 Subject: [PATCH 0228/1404] wallet: allow adjusting number of rounds for the key derivation function --- src/crypto/chacha.h | 12 ++++++++---- src/device/device.hpp | 2 +- src/device/device_default.cpp | 4 ++-- src/device/device_default.hpp | 2 +- src/device/device_ledger.cpp | 6 +++--- src/device/device_ledger.hpp | 2 +- src/wallet/api/wallet.cpp | 4 ++-- src/wallet/api/wallet.h | 2 +- src/wallet/api/wallet2_api.h | 20 ++++++++++++++------ src/wallet/api/wallet_manager.cpp | 27 +++++++++++++++------------ src/wallet/api/wallet_manager.h | 15 +++++++++------ src/wallet/wallet2.cpp | 25 +++++++++++++++---------- src/wallet/wallet2.h | 5 +++-- tests/unit_tests/serialization.cpp | 2 +- 14 files changed, 76 insertions(+), 52 deletions(-) diff --git a/src/crypto/chacha.h b/src/crypto/chacha.h index 2b3ed804391..1dc270faf68 100644 --- a/src/crypto/chacha.h +++ b/src/crypto/chacha.h @@ -69,22 +69,26 @@ namespace crypto { chacha20(data, length, key.data(), reinterpret_cast(&iv), cipher); } - inline void generate_chacha_key(const void *data, size_t size, chacha_key& key) { + inline void generate_chacha_key(const void *data, size_t size, chacha_key& key, uint64_t kdf_rounds) { static_assert(sizeof(chacha_key) <= sizeof(hash), "Size of hash must be at least that of chacha_key"); tools::scrubbed_arr pwd_hash; crypto::cn_slow_hash(data, size, pwd_hash.data(), 0/*variant*/, 0/*prehashed*/); + for (uint64_t n = 1; n < kdf_rounds; ++n) + crypto::cn_slow_hash(pwd_hash.data(), pwd_hash.size(), pwd_hash.data(), 0/*variant*/, 0/*prehashed*/); memcpy(&unwrap(key), pwd_hash.data(), sizeof(key)); } - inline void generate_chacha_key_prehashed(const void *data, size_t size, chacha_key& key) { + inline void generate_chacha_key_prehashed(const void *data, size_t size, chacha_key& key, uint64_t kdf_rounds) { static_assert(sizeof(chacha_key) <= sizeof(hash), "Size of hash must be at least that of chacha_key"); tools::scrubbed_arr pwd_hash; crypto::cn_slow_hash(data, size, pwd_hash.data(), 0/*variant*/, 1/*prehashed*/); + for (uint64_t n = 1; n < kdf_rounds; ++n) + crypto::cn_slow_hash(pwd_hash.data(), pwd_hash.size(), pwd_hash.data(), 0/*variant*/, 0/*prehashed*/); memcpy(&unwrap(key), pwd_hash.data(), sizeof(key)); } - inline void generate_chacha_key(std::string password, chacha_key& key) { - return generate_chacha_key(password.data(), password.size(), key); + inline void generate_chacha_key(std::string password, chacha_key& key, uint64_t kdf_rounds) { + return generate_chacha_key(password.data(), password.size(), key, kdf_rounds); } } diff --git a/src/device/device.hpp b/src/device/device.hpp index 9df0cb39d54..c21456daf08 100644 --- a/src/device/device.hpp +++ b/src/device/device.hpp @@ -125,7 +125,7 @@ namespace hw { /* ======================================================================= */ virtual bool get_public_address(cryptonote::account_public_address &pubkey) = 0; virtual bool get_secret_keys(crypto::secret_key &viewkey , crypto::secret_key &spendkey) = 0; - virtual bool generate_chacha_key(const cryptonote::account_keys &keys, crypto::chacha_key &key) = 0; + virtual bool generate_chacha_key(const cryptonote::account_keys &keys, crypto::chacha_key &key, uint64_t kdf_rounds) = 0; /* ======================================================================= */ /* SUB ADDRESS */ diff --git a/src/device/device_default.cpp b/src/device/device_default.cpp index 0071f7d4f1b..bf14813eaa4 100644 --- a/src/device/device_default.cpp +++ b/src/device/device_default.cpp @@ -100,14 +100,14 @@ namespace hw { /* WALLET & ADDRESS */ /* ======================================================================= */ - bool device_default::generate_chacha_key(const cryptonote::account_keys &keys, crypto::chacha_key &key) { + bool device_default::generate_chacha_key(const cryptonote::account_keys &keys, crypto::chacha_key &key, uint64_t kdf_rounds) { const crypto::secret_key &view_key = keys.m_view_secret_key; const crypto::secret_key &spend_key = keys.m_spend_secret_key; tools::scrubbed_arr data; memcpy(data.data(), &view_key, sizeof(view_key)); memcpy(data.data() + sizeof(view_key), &spend_key, sizeof(spend_key)); data[sizeof(data) - 1] = CHACHA8_KEY_TAIL; - crypto::generate_chacha_key(data.data(), sizeof(data), key); + crypto::generate_chacha_key(data.data(), sizeof(data), key, kdf_rounds); return true; } bool device_default::get_public_address(cryptonote::account_public_address &pubkey) { diff --git a/src/device/device_default.hpp b/src/device/device_default.hpp index 771fbba7215..8d841d9de3b 100644 --- a/src/device/device_default.hpp +++ b/src/device/device_default.hpp @@ -73,7 +73,7 @@ namespace hw { /* ======================================================================= */ bool get_public_address(cryptonote::account_public_address &pubkey) override; bool get_secret_keys(crypto::secret_key &viewkey , crypto::secret_key &spendkey) override; - bool generate_chacha_key(const cryptonote::account_keys &keys, crypto::chacha_key &key) override; + bool generate_chacha_key(const cryptonote::account_keys &keys, crypto::chacha_key &key, uint64_t kdf_rounds) override; /* ======================================================================= */ /* SUB ADDRESS */ diff --git a/src/device/device_ledger.cpp b/src/device/device_ledger.cpp index f7bf58531b5..7a34dad5e76 100644 --- a/src/device/device_ledger.cpp +++ b/src/device/device_ledger.cpp @@ -531,20 +531,20 @@ namespace hw { return true; } - bool device_ledger::generate_chacha_key(const cryptonote::account_keys &keys, crypto::chacha_key &key) { + bool device_ledger::generate_chacha_key(const cryptonote::account_keys &keys, crypto::chacha_key &key, uint64_t kdf_rounds) { AUTO_LOCK_CMD(); #ifdef DEBUG_HWDEVICE crypto::chacha_key key_x; cryptonote::account_keys keys_x = hw::ledger::decrypt(keys); - this->controle_device->generate_chacha_key(keys_x, key_x); + this->controle_device->generate_chacha_key(keys_x, key_x, kdf_rounds); #endif send_simple(INS_GET_CHACHA8_PREKEY); char prekey[200]; memmove(prekey, &this->buffer_recv[0], 200); - crypto::generate_chacha_key_prehashed(&prekey[0], sizeof(prekey), key); + crypto::generate_chacha_key_prehashed(&prekey[0], sizeof(prekey), key, kdf_rounds); #ifdef DEBUG_HWDEVICE hw::ledger::check32("generate_chacha_key_prehashed", "key", (char*)key_x.data(), (char*)key.data()); diff --git a/src/device/device_ledger.hpp b/src/device/device_ledger.hpp index c30a38aca23..e6c6e5b5261 100644 --- a/src/device/device_ledger.hpp +++ b/src/device/device_ledger.hpp @@ -156,7 +156,7 @@ namespace hw { /* ======================================================================= */ bool get_public_address(cryptonote::account_public_address &pubkey) override; bool get_secret_keys(crypto::secret_key &viewkey , crypto::secret_key &spendkey) override; - bool generate_chacha_key(const cryptonote::account_keys &keys, crypto::chacha_key &key) override; + bool generate_chacha_key(const cryptonote::account_keys &keys, crypto::chacha_key &key, uint64_t kdf_rounds) override; /* ======================================================================= */ diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index 680da26ce5d..f7c074b5afa 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -366,7 +366,7 @@ void Wallet::error(const std::string &category, const std::string &str) { } ///////////////////////// WalletImpl implementation //////////////////////// -WalletImpl::WalletImpl(NetworkType nettype) +WalletImpl::WalletImpl(NetworkType nettype, bool restricted, uint64_t kdf_rounds) :m_wallet(nullptr) , m_status(Wallet::Status_Ok) , m_trustedDaemon(false) @@ -377,7 +377,7 @@ WalletImpl::WalletImpl(NetworkType nettype) , m_rebuildWalletCache(false) , m_is_connected(false) { - m_wallet = new tools::wallet2(static_cast(nettype)); + m_wallet = new tools::wallet2(static_cast(nettype), restricted, kdf_rounds); m_history = new TransactionHistoryImpl(this); m_wallet2Callback = new Wallet2CallbackImpl(this); m_wallet->callback(m_wallet2Callback); diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h index 58be686fcd2..28b73423d44 100644 --- a/src/wallet/api/wallet.h +++ b/src/wallet/api/wallet.h @@ -52,7 +52,7 @@ struct Wallet2CallbackImpl; class WalletImpl : public Wallet { public: - WalletImpl(NetworkType nettype = MAINNET); + WalletImpl(NetworkType nettype = MAINNET, bool restricted = false, uint64_t kdf_rounds = 1); ~WalletImpl(); bool create(const std::string &path, const std::string &password, const std::string &language); diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h index 0cd0ff5cf1a..5a52c6b175b 100644 --- a/src/wallet/api/wallet2_api.h +++ b/src/wallet/api/wallet2_api.h @@ -920,9 +920,10 @@ struct WalletManager * \param password Password of wallet file * \param language Language to be used to generate electrum seed mnemonic * \param nettype Network type + * \param kdf_rounds Number of rounds for key derivation function * \return Wallet instance (Wallet::status() needs to be called to check if created successfully) */ - virtual Wallet * createWallet(const std::string &path, const std::string &password, const std::string &language, NetworkType nettype) = 0; + virtual Wallet * createWallet(const std::string &path, const std::string &password, const std::string &language, NetworkType nettype, uint64_t kdf_rounds = 1) = 0; Wallet * createWallet(const std::string &path, const std::string &password, const std::string &language, bool testnet = false) // deprecated { return createWallet(path, password, language, testnet ? TESTNET : MAINNET); @@ -933,9 +934,10 @@ struct WalletManager * \param path Name of wallet file * \param password Password of wallet file * \param nettype Network type + * \param kdf_rounds Number of rounds for key derivation function * \return Wallet instance (Wallet::status() needs to be called to check if opened successfully) */ - virtual Wallet * openWallet(const std::string &path, const std::string &password, NetworkType nettype) = 0; + virtual Wallet * openWallet(const std::string &path, const std::string &password, NetworkType nettype, uint64_t kdf_rounds = 1) = 0; Wallet * openWallet(const std::string &path, const std::string &password, bool testnet = false) // deprecated { return openWallet(path, password, testnet ? TESTNET : MAINNET); @@ -948,10 +950,11 @@ struct WalletManager * \param mnemonic mnemonic (25 words electrum seed) * \param nettype Network type * \param restoreHeight restore from start height + * \param kdf_rounds Number of rounds for key derivation function * \return Wallet instance (Wallet::status() needs to be called to check if recovered successfully) */ virtual Wallet * recoveryWallet(const std::string &path, const std::string &password, const std::string &mnemonic, - NetworkType nettype = MAINNET, uint64_t restoreHeight = 0) = 0; + NetworkType nettype = MAINNET, uint64_t restoreHeight = 0, uint64_t kdf_rounds = 1) = 0; Wallet * recoveryWallet(const std::string &path, const std::string &password, const std::string &mnemonic, bool testnet = false, uint64_t restoreHeight = 0) // deprecated { @@ -983,6 +986,7 @@ struct WalletManager * \param addressString public address * \param viewKeyString view key * \param spendKeyString spend key (optional) + * \param kdf_rounds Number of rounds for key derivation function * \return Wallet instance (Wallet::status() needs to be called to check if recovered successfully) */ virtual Wallet * createWalletFromKeys(const std::string &path, @@ -992,7 +996,8 @@ struct WalletManager uint64_t restoreHeight, const std::string &addressString, const std::string &viewKeyString, - const std::string &spendKeyString = "") = 0; + const std::string &spendKeyString = "", + uint64_t kdf_rounds = 1) = 0; Wallet * createWalletFromKeys(const std::string &path, const std::string &password, const std::string &language, @@ -1043,6 +1048,7 @@ struct WalletManager * \param deviceName Device name * \param restoreHeight restore from start height (0 sets to current height) * \param subaddressLookahead Size of subaddress lookahead (empty sets to some default low value) + * \param kdf_rounds Number of rounds for key derivation function * \return Wallet instance (Wallet::status() needs to be called to check if recovered successfully) */ virtual Wallet * createWalletFromDevice(const std::string &path, @@ -1050,7 +1056,8 @@ struct WalletManager NetworkType nettype, const std::string &deviceName, uint64_t restoreHeight = 0, - const std::string &subaddressLookahead = "") = 0; + const std::string &subaddressLookahead = "", + uint64_t kdf_rounds = 1) = 0; /*! * \brief Closes wallet. In case operation succeeded, wallet object deleted. in case operation failed, wallet object not deleted @@ -1075,13 +1082,14 @@ struct WalletManager * @param keys_file_name - location of keys file * @param password - password to verify * @param no_spend_key - verify only view keys? + * @param kdf_rounds - number of rounds for key derivation function * @return - true if password is correct * * @note * This function will fail when the wallet keys file is opened because the wallet program locks the keys file. * In this case, Wallet::unlockKeysFile() and Wallet::lockKeysFile() need to be called before and after the call to this function, respectively. */ - virtual bool verifyWalletPassword(const std::string &keys_file_name, const std::string &password, bool no_spend_key) const = 0; + virtual bool verifyWalletPassword(const std::string &keys_file_name, const std::string &password, bool no_spend_key, uint64_t kdf_rounds = 1) const = 0; /*! * \brief findWallets - searches for the wallet files by given path name recursively diff --git a/src/wallet/api/wallet_manager.cpp b/src/wallet/api/wallet_manager.cpp index 99eadc82fc5..5daf11ec0d3 100644 --- a/src/wallet/api/wallet_manager.cpp +++ b/src/wallet/api/wallet_manager.cpp @@ -50,16 +50,16 @@ namespace epee { namespace Monero { Wallet *WalletManagerImpl::createWallet(const std::string &path, const std::string &password, - const std::string &language, NetworkType nettype) + const std::string &language, NetworkType nettype, uint64_t kdf_rounds) { - WalletImpl * wallet = new WalletImpl(nettype); + WalletImpl * wallet = new WalletImpl(nettype, false, kdf_rounds); wallet->create(path, password, language); return wallet; } -Wallet *WalletManagerImpl::openWallet(const std::string &path, const std::string &password, NetworkType nettype) +Wallet *WalletManagerImpl::openWallet(const std::string &path, const std::string &password, NetworkType nettype, uint64_t kdf_rounds) { - WalletImpl * wallet = new WalletImpl(nettype); + WalletImpl * wallet = new WalletImpl(nettype, false, kdf_rounds); wallet->open(path, password); //Refresh addressBook wallet->addressBook()->refresh(); @@ -87,9 +87,10 @@ Wallet *WalletManagerImpl::recoveryWallet(const std::string &path, const std::string &password, const std::string &mnemonic, NetworkType nettype, - uint64_t restoreHeight) + uint64_t restoreHeight, + uint64_t kdf_rounds) { - WalletImpl * wallet = new WalletImpl(nettype); + WalletImpl * wallet = new WalletImpl(nettype, false, kdf_rounds); if(restoreHeight > 0){ wallet->setRefreshFromBlockHeight(restoreHeight); } @@ -104,9 +105,10 @@ Wallet *WalletManagerImpl::createWalletFromKeys(const std::string &path, uint64_t restoreHeight, const std::string &addressString, const std::string &viewKeyString, - const std::string &spendKeyString) + const std::string &spendKeyString, + uint64_t kdf_rounds) { - WalletImpl * wallet = new WalletImpl(nettype); + WalletImpl * wallet = new WalletImpl(nettype, false, kdf_rounds); if(restoreHeight > 0){ wallet->setRefreshFromBlockHeight(restoreHeight); } @@ -119,9 +121,10 @@ Wallet *WalletManagerImpl::createWalletFromDevice(const std::string &path, NetworkType nettype, const std::string &deviceName, uint64_t restoreHeight, - const std::string &subaddressLookahead) + const std::string &subaddressLookahead, + uint64_t kdf_rounds) { - WalletImpl * wallet = new WalletImpl(nettype); + WalletImpl * wallet = new WalletImpl(nettype, false, kdf_rounds); if(restoreHeight > 0){ wallet->setRefreshFromBlockHeight(restoreHeight); } @@ -159,9 +162,9 @@ bool WalletManagerImpl::walletExists(const std::string &path) return false; } -bool WalletManagerImpl::verifyWalletPassword(const std::string &keys_file_name, const std::string &password, bool no_spend_key) const +bool WalletManagerImpl::verifyWalletPassword(const std::string &keys_file_name, const std::string &password, bool no_spend_key, uint64_t kdf_rounds) const { - return tools::wallet2::verify_password(keys_file_name, password, no_spend_key, hw::get_device("default")); + return tools::wallet2::verify_password(keys_file_name, password, no_spend_key, hw::get_device("default"), kdf_rounds); } std::vector WalletManagerImpl::findWallets(const std::string &path) diff --git a/src/wallet/api/wallet_manager.h b/src/wallet/api/wallet_manager.h index 656a7142cec..8b1c8be7f78 100644 --- a/src/wallet/api/wallet_manager.h +++ b/src/wallet/api/wallet_manager.h @@ -39,13 +39,14 @@ class WalletManagerImpl : public WalletManager { public: Wallet * createWallet(const std::string &path, const std::string &password, - const std::string &language, NetworkType nettype) override; - Wallet * openWallet(const std::string &path, const std::string &password, NetworkType nettype) override; + const std::string &language, NetworkType nettype, uint64_t kdf_rounds = 1) override; + Wallet * openWallet(const std::string &path, const std::string &password, NetworkType nettype, uint64_t kdf_rounds = 1) override; virtual Wallet * recoveryWallet(const std::string &path, const std::string &password, const std::string &mnemonic, NetworkType nettype, - uint64_t restoreHeight) override; + uint64_t restoreHeight, + uint64_t kdf_rounds = 1) override; virtual Wallet * createWalletFromKeys(const std::string &path, const std::string &password, const std::string &language, @@ -53,7 +54,8 @@ class WalletManagerImpl : public WalletManager uint64_t restoreHeight, const std::string &addressString, const std::string &viewKeyString, - const std::string &spendKeyString = "") override; + const std::string &spendKeyString = "", + uint64_t kdf_rounds = 1) override; // next two methods are deprecated - use the above version which allow setting of a password virtual Wallet * recoveryWallet(const std::string &path, const std::string &mnemonic, NetworkType nettype, uint64_t restoreHeight) override; // deprecated: use createWalletFromKeys(..., password, ...) instead @@ -69,10 +71,11 @@ class WalletManagerImpl : public WalletManager NetworkType nettype, const std::string &deviceName, uint64_t restoreHeight = 0, - const std::string &subaddressLookahead = "") override; + const std::string &subaddressLookahead = "", + uint64_t kdf_rounds = 1) override; virtual bool closeWallet(Wallet *wallet, bool store = true) override; bool walletExists(const std::string &path) override; - bool verifyWalletPassword(const std::string &keys_file_name, const std::string &password, bool no_spend_key) const override; + bool verifyWalletPassword(const std::string &keys_file_name, const std::string &password, bool no_spend_key, uint64_t kdf_rounds = 1) const override; std::vector findWallets(const std::string &path) override; std::string errorString() const override; void setDaemonAddress(const std::string &address) override; diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 90807803af2..439bacd1713 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -160,6 +160,7 @@ struct options { return val; } }; + const command_line::arg_descriptor kdf_rounds = {"kdf-rounds", tools::wallet2::tr("Number of rounds for the key derivation function"), 1}; }; void do_prepare_file_names(const std::string& file_path, std::string& keys_file, std::string& wallet_file) @@ -203,6 +204,8 @@ std::unique_ptr make_basic(const boost::program_options::variabl const bool stagenet = command_line::get_arg(vm, opts.stagenet); const network_type nettype = testnet ? TESTNET : stagenet ? STAGENET : MAINNET; const bool restricted = command_line::get_arg(vm, opts.restricted); + const uint64_t kdf_rounds = command_line::get_arg(vm, opts.kdf_rounds); + THROW_WALLET_EXCEPTION_IF(kdf_rounds == 0, tools::error::wallet_internal_error, "KDF rounds must not be 0"); auto daemon_address = command_line::get_arg(vm, opts.daemon_address); auto daemon_host = command_line::get_arg(vm, opts.daemon_host); @@ -236,7 +239,7 @@ std::unique_ptr make_basic(const boost::program_options::variabl if (daemon_address.empty()) daemon_address = std::string("http://") + daemon_host + ":" + std::to_string(daemon_port); - std::unique_ptr wallet(new tools::wallet2(nettype, restricted)); + std::unique_ptr wallet(new tools::wallet2(nettype, restricted, kdf_rounds)); wallet->init(std::move(daemon_address), std::move(login)); boost::filesystem::path ringdb_path = command_line::get_arg(vm, opts.shared_ringdb_dir); wallet->set_ring_database(ringdb_path.string()); @@ -647,7 +650,7 @@ const size_t MAX_SPLIT_ATTEMPTS = 30; constexpr const std::chrono::seconds wallet2::rpc_timeout; const char* wallet2::tr(const char* str) { return i18n_translate(str, "tools::wallet2"); } -wallet2::wallet2(network_type nettype, bool restricted): +wallet2::wallet2(network_type nettype, bool restricted, uint64_t kdf_rounds): m_multisig_rescan_info(NULL), m_multisig_rescan_k(NULL), m_run(true), @@ -679,6 +682,7 @@ wallet2::wallet2(network_type nettype, bool restricted): m_ignore_fractional_outputs(true), m_is_initialized(false), m_restricted(restricted), + m_kdf_rounds(kdf_rounds), is_old_file_format(false), m_node_rpc_proxy(m_http_client, m_daemon_rpc_mutex), m_subaddress_lookahead_major(SUBADDRESS_LOOKAHEAD_MAJOR), @@ -723,6 +727,7 @@ void wallet2::init_options(boost::program_options::options_description& desc_par command_line::add_arg(desc_params, opts.stagenet); command_line::add_arg(desc_params, opts.restricted); command_line::add_arg(desc_params, opts.shared_ringdb_dir); + command_line::add_arg(desc_params, opts.kdf_rounds); } std::unique_ptr wallet2::make_from_json(const boost::program_options::variables_map& vm, const std::string& json_file, const std::function(const char *, bool)> &password_prompter) @@ -2844,7 +2849,7 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable // Encrypt the entire JSON object. crypto::chacha_key key; - crypto::generate_chacha_key(password.data(), password.size(), key); + crypto::generate_chacha_key(password.data(), password.size(), key, m_kdf_rounds); std::string cipher; cipher.resize(account_data.size()); keys_file_data.iv = crypto::rand(); @@ -2878,7 +2883,7 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_ r = ::serialization::parse_binary(buf, keys_file_data); THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "internal error: failed to deserialize \"" + keys_file_name + '\"'); crypto::chacha_key key; - crypto::generate_chacha_key(password.data(), password.size(), key); + crypto::generate_chacha_key(password.data(), password.size(), key, m_kdf_rounds); std::string account_data; account_data.resize(keys_file_data.account_data.size()); crypto::chacha20(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]); @@ -3084,7 +3089,7 @@ bool wallet2::verify_password(const epee::wipeable_string& password) { // this temporary unlocking is necessary for Windows (otherwise the file couldn't be loaded). unlock_keys_file(); - bool r = verify_password(m_keys_file, password, m_watch_only || m_multisig, m_account.get_device()); + bool r = verify_password(m_keys_file, password, m_watch_only || m_multisig, m_account.get_device(), m_kdf_rounds); lock_keys_file(); return r; } @@ -3102,7 +3107,7 @@ bool wallet2::verify_password(const epee::wipeable_string& password) * can be used prior to rewriting wallet keys file, to ensure user has entered the correct password * */ -bool wallet2::verify_password(const std::string& keys_file_name, const epee::wipeable_string& password, bool no_spend_key, hw::device &hwdev) +bool wallet2::verify_password(const std::string& keys_file_name, const epee::wipeable_string& password, bool no_spend_key, hw::device &hwdev, uint64_t kdf_rounds) { rapidjson::Document json; wallet2::keys_file_data keys_file_data; @@ -3114,7 +3119,7 @@ bool wallet2::verify_password(const std::string& keys_file_name, const epee::wip r = ::serialization::parse_binary(buf, keys_file_data); THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "internal error: failed to deserialize \"" + keys_file_name + '\"'); crypto::chacha_key key; - crypto::generate_chacha_key(password.data(), password.size(), key); + crypto::generate_chacha_key(password.data(), password.size(), key, kdf_rounds); std::string account_data; account_data.resize(keys_file_data.account_data.size()); crypto::chacha20(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]); @@ -3982,7 +3987,7 @@ bool wallet2::check_connection(uint32_t *version, uint32_t timeout) bool wallet2::generate_chacha_key_from_secret_keys(crypto::chacha_key &key) const { hw::device &hwdev = m_account.get_device(); - return hwdev.generate_chacha_key(m_account.get_keys(), key); + return hwdev.generate_chacha_key(m_account.get_keys(), key, m_kdf_rounds); } //---------------------------------------------------------------------------------------------------- void wallet2::load(const std::string& wallet_, const epee::wipeable_string& password) @@ -10534,7 +10539,7 @@ size_t wallet2::import_multisig(std::vector blobs) std::string wallet2::encrypt(const std::string &plaintext, const crypto::secret_key &skey, bool authenticated) const { crypto::chacha_key key; - crypto::generate_chacha_key(&skey, sizeof(skey), key); + crypto::generate_chacha_key(&skey, sizeof(skey), key, m_kdf_rounds); std::string ciphertext; crypto::chacha_iv iv = crypto::rand(); ciphertext.resize(plaintext.size() + sizeof(iv) + (authenticated ? sizeof(crypto::signature) : 0)); @@ -10564,7 +10569,7 @@ std::string wallet2::decrypt(const std::string &ciphertext, const crypto::secret error::wallet_internal_error, "Unexpected ciphertext size"); crypto::chacha_key key; - crypto::generate_chacha_key(&skey, sizeof(skey), key); + crypto::generate_chacha_key(&skey, sizeof(skey), key, m_kdf_rounds); const crypto::chacha_iv &iv = *(const crypto::chacha_iv*)&ciphertext[0]; std::string plaintext; plaintext.resize(ciphertext.size() - prefix_size); diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 220eca8a8ac..e2c19208494 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -165,9 +165,9 @@ namespace tools //! Just parses variables. static std::unique_ptr make_dummy(const boost::program_options::variables_map& vm, const std::function(const char *, bool)> &password_prompter); - static bool verify_password(const std::string& keys_file_name, const epee::wipeable_string& password, bool no_spend_key, hw::device &hwdev); + static bool verify_password(const std::string& keys_file_name, const epee::wipeable_string& password, bool no_spend_key, hw::device &hwdev, uint64_t kdf_rounds); - wallet2(cryptonote::network_type nettype = cryptonote::MAINNET, bool restricted = false); + wallet2(cryptonote::network_type nettype = cryptonote::MAINNET, bool restricted = false, uint64_t kdf_rounds = 1); ~wallet2(); struct multisig_info @@ -1258,6 +1258,7 @@ namespace tools bool m_key_on_device; cryptonote::network_type m_nettype; bool m_restricted; + uint64_t m_kdf_rounds; std::string seed_language; /*!< Language of the mnemonics (seed). */ bool is_old_file_format; /*!< Whether the wallet file is of an old file format */ bool m_watch_only; /*!< no spend key */ diff --git a/tests/unit_tests/serialization.cpp b/tests/unit_tests/serialization.cpp index 5a21140279f..40616059a2f 100644 --- a/tests/unit_tests/serialization.cpp +++ b/tests/unit_tests/serialization.cpp @@ -810,7 +810,7 @@ TEST(Serialization, portability_outputs) if(ciphertext.size() < prefix_size) return {}; crypto::chacha_key key; - crypto::generate_chacha_key(&skey, sizeof(skey), key); + crypto::generate_chacha_key(&skey, sizeof(skey), key, 1); const crypto::chacha_iv &iv = *(const crypto::chacha_iv*)&ciphertext[0]; std::string plaintext; plaintext.resize(ciphertext.size() - prefix_size); From 5dd03846b656af95a3f187a82b22710b9b373815 Mon Sep 17 00:00:00 2001 From: Jkat Date: Tue, 7 Aug 2018 17:07:58 -0400 Subject: [PATCH 0229/1404] Updating Qt download link --- README.i18n.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.i18n.md b/README.i18n.md index b0379bfb3a3..504fc9e752c 100644 --- a/README.i18n.md +++ b/README.i18n.md @@ -7,7 +7,7 @@ In order to use the same translation workflow as the [Monero Core GUI](https://g ### Tools for translators -In order to create, update or build translations files, you need to have Qt tools installed. For translating, you need either the **Qt Linguist GUI** ([part of QT Creator](https://www.qt.io/download-open-source/#allDownloadsDiv-9) or a [3rd-party standalone version](https://github.com/lelegard/qtlinguist-installers/releases)), or another tool that supports Qt ts files, such as Transifex. The files are XML, so they can be edited in any plain text editor if needed. +In order to create, update or build translations files, you need to have Qt tools installed. For translating, you need either the **Qt Linguist GUI** ([part of Qt Creator](https://www.qt.io/download) or a [3rd-party standalone version](https://github.com/lelegard/qtlinguist-installers/releases)), or another tool that supports Qt ts files, such as Transifex. The files are XML, so they can be edited in any plain text editor if needed. ### Creating / modifying translations From 289880d82d3cb206a2cf4ae67d2deacdab43d4f4 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 10 Jul 2018 19:56:05 +0100 Subject: [PATCH 0230/1404] blockchain_depth: get the average min depth of a set of txes --- src/blockchain_utilities/CMakeLists.txt | 32 ++ src/blockchain_utilities/blockchain_depth.cpp | 347 ++++++++++++++++++ 2 files changed, 379 insertions(+) create mode 100644 src/blockchain_utilities/blockchain_depth.cpp diff --git a/src/blockchain_utilities/CMakeLists.txt b/src/blockchain_utilities/CMakeLists.txt index 0ff768143f6..fe57895e176 100644 --- a/src/blockchain_utilities/CMakeLists.txt +++ b/src/blockchain_utilities/CMakeLists.txt @@ -102,6 +102,17 @@ monero_private_headers(blockchain_ancestry +set(blockchain_depth_sources + blockchain_depth.cpp + ) + +set(blockchain_depth_private_headers) + +monero_private_headers(blockchain_depth + ${blockchain_depth_private_headers}) + + + monero_add_executable(blockchain_import ${blockchain_import_sources} ${blockchain_import_private_headers} @@ -215,3 +226,24 @@ set_property(TARGET blockchain_ancestry OUTPUT_NAME "monero-blockchain-ancestry") install(TARGETS blockchain_ancestry DESTINATION bin) +monero_add_executable(blockchain_depth + ${blockchain_depth_sources} + ${blockchain_depth_private_headers}) + +target_link_libraries(blockchain_depth + PRIVATE + cryptonote_core + blockchain_db + version + epee + ${Boost_FILESYSTEM_LIBRARY} + ${Boost_SYSTEM_LIBRARY} + ${Boost_THREAD_LIBRARY} + ${CMAKE_THREAD_LIBS_INIT} + ${EXTRA_LIBRARIES}) + +set_property(TARGET blockchain_depth + PROPERTY + OUTPUT_NAME "monero-blockchain-depth") +install(TARGETS blockchain_depth DESTINATION bin) + diff --git a/src/blockchain_utilities/blockchain_depth.cpp b/src/blockchain_utilities/blockchain_depth.cpp new file mode 100644 index 00000000000..89fe6d03f1e --- /dev/null +++ b/src/blockchain_utilities/blockchain_depth.cpp @@ -0,0 +1,347 @@ +// Copyright (c) 2014-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include +#include +#include "common/command_line.h" +#include "common/varint.h" +#include "cryptonote_core/tx_pool.h" +#include "cryptonote_core/cryptonote_core.h" +#include "cryptonote_core/blockchain.h" +#include "blockchain_db/blockchain_db.h" +#include "blockchain_db/db_types.h" +#include "version.h" + +#undef MONERO_DEFAULT_LOG_CATEGORY +#define MONERO_DEFAULT_LOG_CATEGORY "bcutil" + +namespace po = boost::program_options; +using namespace epee; +using namespace cryptonote; + +int main(int argc, char* argv[]) +{ + TRY_ENTRY(); + + epee::string_tools::set_module_name_and_folder(argv[0]); + + std::string default_db_type = "lmdb"; + + std::string available_dbs = cryptonote::blockchain_db_types(", "); + available_dbs = "available: " + available_dbs; + + uint32_t log_level = 0; + + tools::on_startup(); + + boost::filesystem::path output_file_path; + + po::options_description desc_cmd_only("Command line options"); + po::options_description desc_cmd_sett("Command line options and settings options"); + const command_line::arg_descriptor arg_log_level = {"log-level", "0-4 or categories", ""}; + const command_line::arg_descriptor arg_database = { + "database", available_dbs.c_str(), default_db_type + }; + const command_line::arg_descriptor arg_txid = {"txid", "Get min depth for this txid", ""}; + const command_line::arg_descriptor arg_height = {"height", "Get min depth for all txes at this height", 0}; + const command_line::arg_descriptor arg_include_coinbase = {"include-coinbase", "Include coinbase in the average", false}; + + command_line::add_arg(desc_cmd_sett, cryptonote::arg_data_dir); + command_line::add_arg(desc_cmd_sett, cryptonote::arg_testnet_on); + command_line::add_arg(desc_cmd_sett, cryptonote::arg_stagenet_on); + command_line::add_arg(desc_cmd_sett, arg_log_level); + command_line::add_arg(desc_cmd_sett, arg_database); + command_line::add_arg(desc_cmd_sett, arg_txid); + command_line::add_arg(desc_cmd_sett, arg_height); + command_line::add_arg(desc_cmd_sett, arg_include_coinbase); + command_line::add_arg(desc_cmd_only, command_line::arg_help); + + po::options_description desc_options("Allowed options"); + desc_options.add(desc_cmd_only).add(desc_cmd_sett); + + po::variables_map vm; + bool r = command_line::handle_error_helper(desc_options, [&]() + { + auto parser = po::command_line_parser(argc, argv).options(desc_options); + po::store(parser.run(), vm); + po::notify(vm); + return true; + }); + if (! r) + return 1; + + if (command_line::get_arg(vm, command_line::arg_help)) + { + std::cout << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")" << ENDL << ENDL; + std::cout << desc_options << std::endl; + return 1; + } + + mlog_configure(mlog_get_default_log_path("monero-blockchain-depth.log"), true); + if (!command_line::is_arg_defaulted(vm, arg_log_level)) + mlog_set_log(command_line::get_arg(vm, arg_log_level).c_str()); + else + mlog_set_log(std::string(std::to_string(log_level) + ",bcutil:INFO").c_str()); + + LOG_PRINT_L0("Starting..."); + + std::string opt_data_dir = command_line::get_arg(vm, cryptonote::arg_data_dir); + bool opt_testnet = command_line::get_arg(vm, cryptonote::arg_testnet_on); + bool opt_stagenet = command_line::get_arg(vm, cryptonote::arg_stagenet_on); + network_type net_type = opt_testnet ? TESTNET : opt_stagenet ? STAGENET : MAINNET; + std::string opt_txid_string = command_line::get_arg(vm, arg_txid); + uint64_t opt_height = command_line::get_arg(vm, arg_height); + bool opt_include_coinbase = command_line::get_arg(vm, arg_include_coinbase); + + if (!opt_txid_string.empty() && opt_height) + { + std::cerr << "txid and height cannot be given at the same time" << std::endl; + return 1; + } + crypto::hash opt_txid = crypto::null_hash; + if (!opt_txid_string.empty()) + { + if (!epee::string_tools::hex_to_pod(opt_txid_string, opt_txid)) + { + std::cerr << "Invalid txid" << std::endl; + return 1; + } + } + + std::string db_type = command_line::get_arg(vm, arg_database); + if (!cryptonote::blockchain_valid_db_type(db_type)) + { + std::cerr << "Invalid database type: " << db_type << std::endl; + return 1; + } + + // If we wanted to use the memory pool, we would set up a fake_core. + + // Use Blockchain instead of lower-level BlockchainDB for two reasons: + // 1. Blockchain has the init() method for easy setup + // 2. exporter needs to use get_current_blockchain_height(), get_block_id_by_height(), get_block_by_hash() + // + // cannot match blockchain_storage setup above with just one line, + // e.g. + // Blockchain* core_storage = new Blockchain(NULL); + // because unlike blockchain_storage constructor, which takes a pointer to + // tx_memory_pool, Blockchain's constructor takes tx_memory_pool object. + LOG_PRINT_L0("Initializing source blockchain (BlockchainDB)"); + std::unique_ptr core_storage; + tx_memory_pool m_mempool(*core_storage); + core_storage.reset(new Blockchain(m_mempool)); + BlockchainDB *db = new_db(db_type); + if (db == NULL) + { + LOG_ERROR("Attempted to use non-existent database type: " << db_type); + throw std::runtime_error("Attempting to use non-existent database type"); + } + LOG_PRINT_L0("database: " << db_type); + + const std::string filename = (boost::filesystem::path(opt_data_dir) / db->get_db_name()).string(); + LOG_PRINT_L0("Loading blockchain from folder " << filename << " ..."); + + try + { + db->open(filename, DBF_RDONLY); + } + catch (const std::exception& e) + { + LOG_PRINT_L0("Error opening database: " << e.what()); + return 1; + } + r = core_storage->init(db, net_type); + + CHECK_AND_ASSERT_MES(r, 1, "Failed to initialize source blockchain storage"); + LOG_PRINT_L0("Source blockchain storage initialized OK"); + + std::vector start_txids; + if (!opt_txid_string.empty()) + { + start_txids.push_back(opt_txid); + } + else + { + const crypto::hash block_hash = db->get_block_hash_from_height(opt_height); + const cryptonote::blobdata bd = db->get_block_blob(block_hash); + cryptonote::block b; + if (!cryptonote::parse_and_validate_block_from_blob(bd, b)) + { + LOG_PRINT_L0("Bad block from db"); + return 1; + } + for (const crypto::hash &txid: b.tx_hashes) + start_txids.push_back(txid); + if (opt_include_coinbase) + start_txids.push_back(cryptonote::get_transaction_hash(b.miner_tx)); + } + + if (start_txids.empty()) + { + LOG_PRINT_L0("No transaction(s) to check"); + return 1; + } + + double cumulative_depth = 0.0; + for (const crypto::hash &start_txid: start_txids) + { + uint64_t depth = 0; + bool coinbase = false; + + LOG_PRINT_L0("Checking depth for txid " << start_txid); + std::vector txids(1, start_txid); + while (!coinbase) + { + LOG_PRINT_L0("Considering "<< txids.size() << " transaction(s) at depth " << depth); + std::vector new_txids; + for (const crypto::hash &txid: txids) + { + cryptonote::blobdata bd; + if (!db->get_pruned_tx_blob(txid, bd)) + { + LOG_PRINT_L0("Failed to get txid " << txid << " from db"); + return 1; + } + cryptonote::transaction tx; + if (!cryptonote::parse_and_validate_tx_base_from_blob(bd, tx)) + { + LOG_PRINT_L0("Bad tx: " << txid); + return 1; + } + for (size_t ring = 0; ring < tx.vin.size(); ++ring) + { + if (tx.vin[ring].type() == typeid(cryptonote::txin_gen)) + { + MDEBUG(txid << " is a coinbase transaction"); + coinbase = true; + goto done; + } + if (tx.vin[ring].type() == typeid(cryptonote::txin_to_key)) + { + const cryptonote::txin_to_key &txin = boost::get(tx.vin[ring]); + const uint64_t amount = txin.amount; + auto absolute_offsets = cryptonote::relative_output_offsets_to_absolute(txin.key_offsets); + for (uint64_t offset: absolute_offsets) + { + const output_data_t od = db->get_output_key(amount, offset); + const crypto::hash block_hash = db->get_block_hash_from_height(od.height); + bd = db->get_block_blob(block_hash); + cryptonote::block b; + if (!cryptonote::parse_and_validate_block_from_blob(bd, b)) + { + LOG_PRINT_L0("Bad block from db"); + return 1; + } + // find the tx which created this output + bool found = false; + for (size_t out = 0; out < b.miner_tx.vout.size(); ++out) + { + if (b.miner_tx.vout[out].target.type() == typeid(cryptonote::txout_to_key)) + { + const auto &txout = boost::get(b.miner_tx.vout[out].target); + if (txout.key == od.pubkey) + { + found = true; + new_txids.push_back(cryptonote::get_transaction_hash(b.miner_tx)); + MDEBUG("adding txid: " << cryptonote::get_transaction_hash(b.miner_tx)); + break; + } + } + else + { + LOG_PRINT_L0("Bad vout type in txid " << cryptonote::get_transaction_hash(b.miner_tx)); + return 1; + } + } + for (const crypto::hash &block_txid: b.tx_hashes) + { + if (found) + break; + if (!db->get_pruned_tx_blob(block_txid, bd)) + { + LOG_PRINT_L0("Failed to get txid " << block_txid << " from db"); + return 1; + } + cryptonote::transaction tx2; + if (!cryptonote::parse_and_validate_tx_base_from_blob(bd, tx2)) + { + LOG_PRINT_L0("Bad tx: " << block_txid); + return 1; + } + for (size_t out = 0; out < tx2.vout.size(); ++out) + { + if (tx2.vout[out].target.type() == typeid(cryptonote::txout_to_key)) + { + const auto &txout = boost::get(tx2.vout[out].target); + if (txout.key == od.pubkey) + { + found = true; + new_txids.push_back(block_txid); + MDEBUG("adding txid: " << block_txid); + break; + } + } + else + { + LOG_PRINT_L0("Bad vout type in txid " << block_txid); + return 1; + } + } + } + if (!found) + { + LOG_PRINT_L0("Output originating transaction not found"); + return 1; + } + } + } + else + { + LOG_PRINT_L0("Bad vin type in txid " << txid); + return 1; + } + } + } + if (!coinbase) + { + std::swap(txids, new_txids); + ++depth; + } + } +done: + LOG_PRINT_L0("Min depth for txid " << start_txid << ": " << depth); + cumulative_depth += depth; + } + + LOG_PRINT_L0("Average min depth for " << start_txids.size() << " transaction(s): " << cumulative_depth/start_txids.size()); + + core_storage->deinit(); + return 0; + + CATCH_ENTRY("Depth query error", 1); +} From a4d2d8420987f6e05a49936ed7fc854065dfa0c3 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 7 Aug 2018 20:03:48 +0000 Subject: [PATCH 0231/1404] blockchain_depth: add average min depth --- src/blockchain_utilities/blockchain_depth.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/blockchain_utilities/blockchain_depth.cpp b/src/blockchain_utilities/blockchain_depth.cpp index 89fe6d03f1e..dd2387e5b96 100644 --- a/src/blockchain_utilities/blockchain_depth.cpp +++ b/src/blockchain_utilities/blockchain_depth.cpp @@ -207,7 +207,7 @@ int main(int argc, char* argv[]) return 1; } - double cumulative_depth = 0.0; + std::vector depths; for (const crypto::hash &start_txid: start_txids) { uint64_t depth = 0; @@ -335,10 +335,14 @@ int main(int argc, char* argv[]) } done: LOG_PRINT_L0("Min depth for txid " << start_txid << ": " << depth); - cumulative_depth += depth; + depths.push_back(depth); } - LOG_PRINT_L0("Average min depth for " << start_txids.size() << " transaction(s): " << cumulative_depth/start_txids.size()); + uint64_t cumulative_depth = 0; + for (uint64_t depth: depths) + cumulative_depth += depth; + LOG_PRINT_L0("Average min depth for " << start_txids.size() << " transaction(s): " << cumulative_depth/(float)depths.size()); + LOG_PRINT_L0("Median min depth for " << start_txids.size() << " transaction(s): " << epee::misc_utils::median(depths)); core_storage->deinit(); return 0; From 13a43fcf0b4433f5aca1efd98f0928bf41c76492 Mon Sep 17 00:00:00 2001 From: HomDx Date: Tue, 7 Aug 2018 22:02:10 +0300 Subject: [PATCH 0232/1404] Added Codefresh.yml pipeline --- contrib/codefresh/codefresh.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 contrib/codefresh/codefresh.yml diff --git a/contrib/codefresh/codefresh.yml b/contrib/codefresh/codefresh.yml new file mode 100644 index 00000000000..a22debfa1ac --- /dev/null +++ b/contrib/codefresh/codefresh.yml @@ -0,0 +1,18 @@ +version: '1.0' +steps: + init_submodules: + title: Init Submodules + commands: + - git submodule update --init --recursive + image: codefreshio/git-image:latest + working_directory: ${{main_clone}} + + BuildingDockerImage: + title: Building Docker Image + type: build + image_name: monero + working_directory: ./ + tag: '${{CF_BRANCH_TAG_NORMALIZED}}' + dockerfile: Dockerfile + build_arguments: + - NPROC=1 From ce9457a379496b6c6bc64c830c693ac7ca26eec1 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 1 Aug 2018 13:56:37 +0000 Subject: [PATCH 0233/1404] cryptonote_protocol: don't serialize an unneeded new block message Non fluffy block nodes should now be very rare --- .../cryptonote_protocol_handler.inl | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl index 56aa1dc065f..73c4076d637 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl @@ -1671,11 +1671,6 @@ skip: fluffy_arg.b = arg.b; fluffy_arg.b.txs = fluffy_txs; - // pre-serialize them - std::string fullBlob, fluffyBlob; - epee::serialization::store_t_to_binary(arg, fullBlob); - epee::serialization::store_t_to_binary(fluffy_arg, fluffyBlob); - // sort peers between fluffy ones and others std::list fullConnections, fluffyConnections; m_p2p->for_each_connection([this, &exclude_context, &fullConnections, &fluffyConnections](connection_context& context, nodetool::peerid_type peer_id, uint32_t support_flags) @@ -1697,8 +1692,18 @@ skip: }); // send fluffy ones first, we want to encourage people to run that - m_p2p->relay_notify_to_list(NOTIFY_NEW_FLUFFY_BLOCK::ID, fluffyBlob, fluffyConnections); - m_p2p->relay_notify_to_list(NOTIFY_NEW_BLOCK::ID, fullBlob, fullConnections); + if (!fluffyConnections.empty()) + { + std::string fluffyBlob; + epee::serialization::store_t_to_binary(fluffy_arg, fluffyBlob); + m_p2p->relay_notify_to_list(NOTIFY_NEW_FLUFFY_BLOCK::ID, fluffyBlob, fluffyConnections); + } + if (!fullConnections.empty()) + { + std::string fullBlob; + epee::serialization::store_t_to_binary(arg, fullBlob); + m_p2p->relay_notify_to_list(NOTIFY_NEW_BLOCK::ID, fullBlob, fullConnections); + } return true; } From 361513ac810d9b32e61638f46c6497b3d6e33a8b Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 30 Jul 2018 17:22:01 +0100 Subject: [PATCH 0234/1404] blockchain_blackball: use a bit less memory --- src/blockchain_utilities/blockchain_blackball.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/blockchain_utilities/blockchain_blackball.cpp b/src/blockchain_utilities/blockchain_blackball.cpp index 52d2938cd85..ce41342b768 100644 --- a/src/blockchain_utilities/blockchain_blackball.cpp +++ b/src/blockchain_utilities/blockchain_blackball.cpp @@ -436,7 +436,6 @@ int main(int argc, char* argv[]) MINFO("Blackballing output " << pkey << ", due to being used in a 1-ring"); ringdb.blackball(pkey); newly_spent.insert(output_data(txin.amount, absolute[0])); - state.spent.insert(output_data(txin.amount, absolute[0])); } else if (state.ring_instances[new_ring] == new_ring.size()) { @@ -446,7 +445,6 @@ int main(int argc, char* argv[]) MINFO("Blackballing output " << pkey << ", due to being used in " << new_ring.size() << " identical " << new_ring.size() << "-rings"); ringdb.blackball(pkey); newly_spent.insert(output_data(txin.amount, absolute[o])); - state.spent.insert(output_data(txin.amount, absolute[o])); } } else if (state.relative_rings.find(txin.k_image) != state.relative_rings.end()) @@ -475,7 +473,6 @@ int main(int argc, char* argv[]) MINFO("Blackballing output " << pkey << ", due to being used in rings with a single common element"); ringdb.blackball(pkey); newly_spent.insert(output_data(txin.amount, common[0])); - state.spent.insert(output_data(txin.amount, common[0])); } else { @@ -500,6 +497,9 @@ int main(int argc, char* argv[]) std::unordered_set work_spent = std::move(newly_spent); newly_spent.clear(); + for (const auto &e: work_spent) + state.spent.insert(e); + for (const output_data &od: work_spent) { for (const crypto::key_image &ki: state.outputs[od]) @@ -522,7 +522,6 @@ int main(int argc, char* argv[]) absolute.size() << "-ring where all other outputs are known to be spent"); ringdb.blackball(pkey); newly_spent.insert(output_data(od.amount, last_unknown)); - state.spent.insert(output_data(od.amount, last_unknown)); } } } From 83f5587167708ab1b764ebae042832dcefecd256 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 9 Aug 2018 16:44:26 +0000 Subject: [PATCH 0235/1404] blockchain: use uint64_t for height, not size_t --- src/cryptonote_core/blockchain.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index dad30906ec9..c2445bba59b 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -2294,7 +2294,7 @@ bool Blockchain::find_blockchain_supplement(const uint64_t req_start_block, cons total_height = get_current_blockchain_height(); size_t count = 0, size = 0; blocks.reserve(std::min(std::min(max_count, (size_t)10000), (size_t)(total_height - start_height))); - for(size_t i = start_height; i < total_height && count < max_count && (size < FIND_BLOCKCHAIN_SUPPLEMENT_MAX_SIZE || count < 3); i++, count++) + for(uint64_t i = start_height; i < total_height && count < max_count && (size < FIND_BLOCKCHAIN_SUPPLEMENT_MAX_SIZE || count < 3); i++, count++) { blocks.resize(blocks.size()+1); blocks.back().first.first = m_db->get_block_blob_from_height(i); From 95766fe878420294eded752ce6e14f32ee1eede0 Mon Sep 17 00:00:00 2001 From: stoffu Date: Fri, 10 Aug 2018 18:16:26 +0900 Subject: [PATCH 0236/1404] README: add dependency on libpgm-dev --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 98c300a9d24..eb1271ba4a6 100644 --- a/README.md +++ b/README.md @@ -137,6 +137,7 @@ library archives (`.a`). | Boost | 1.58 | NO | `libboost-all-dev` | `boost` | `boost-devel` | NO | C++ libraries | | OpenSSL | basically any | NO | `libssl-dev` | `openssl` | `openssl-devel` | NO | sha256 sum | | libzmq | 3.0.0 | NO | `libzmq3-dev` | `zeromq` | `cppzmq-devel` | NO | ZeroMQ library | +| OpenPGM | ? | NO | `libpgm-dev` | `libpgm` | `openpgm-devel` | NO | For ZeroMQ | | libunbound | 1.4.16 | YES | `libunbound-dev` | `unbound` | `unbound-devel` | NO | DNS resolver | | libsodium | ? | NO | `libsodium-dev` | ? | `libsodium-devel` | NO | libsodium | | libunwind | any | NO | `libunwind8-dev` | `libunwind` | `libunwind-devel` | YES | Stack traces | @@ -154,7 +155,7 @@ library archives (`.a`). build the library binary manually. This can be done with the following command ```sudo apt-get install libgtest-dev && cd /usr/src/gtest && sudo cmake . && sudo make && sudo mv libg* /usr/lib/ ``` Debian / Ubuntu one liner for all dependencies -``` sudo apt update && sudo apt install build-essential cmake pkg-config libboost-all-dev libssl-dev libzmq3-dev libunbound-dev libsodium-dev libunwind8-dev liblzma-dev libreadline6-dev libldns-dev libexpat1-dev doxygen graphviz libpcsclite-dev ``` +``` sudo apt update && sudo apt install build-essential cmake pkg-config libboost-all-dev libssl-dev libzmq3-dev libunbound-dev libsodium-dev libunwind8-dev liblzma-dev libreadline6-dev libldns-dev libexpat1-dev doxygen graphviz libpcsclite-dev libpgm-dev``` ### Cloning the repository From b278b83860da5eaaa122c219ad8c63f641e41cd3 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 1 Aug 2018 10:09:35 +0000 Subject: [PATCH 0237/1404] core: sync database based on bytes added, not blocks added Blocks have a very wide range, whereas actual size is the relevant quantity to consider when syncing --- src/blockchain_db/blockchain_db.cpp | 4 ++-- src/cryptonote_core/blockchain.cpp | 12 +++++++---- src/cryptonote_core/blockchain.h | 9 +++++--- src/cryptonote_core/cryptonote_core.cpp | 28 ++++++++++++++++++------- 4 files changed, 37 insertions(+), 16 deletions(-) diff --git a/src/blockchain_db/blockchain_db.cpp b/src/blockchain_db/blockchain_db.cpp index 8544cc3f0db..78d63fdcfab 100644 --- a/src/blockchain_db/blockchain_db.cpp +++ b/src/blockchain_db/blockchain_db.cpp @@ -87,8 +87,8 @@ const command_line::arg_descriptor arg_db_type = { }; const command_line::arg_descriptor arg_db_sync_mode = { "db-sync-mode" -, "Specify sync option, using format [safe|fast|fastest]:[sync|async]:[nblocks_per_sync]." -, "fast:async:1000" +, "Specify sync option, using format [safe|fast|fastest]:[sync|async]:[[blocks]|[bytes]]." +, "fast:async:250000000bytes" }; const command_line::arg_descriptor arg_db_salvage = { "db-salvage" diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index dad30906ec9..f9766f643d3 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -156,7 +156,7 @@ static const struct { //------------------------------------------------------------------ Blockchain::Blockchain(tx_memory_pool& tx_pool) : m_db(), m_tx_pool(tx_pool), m_hardfork(NULL), m_timestamps_and_difficulties_height(0), m_current_block_cumul_sz_limit(0), m_current_block_cumul_sz_median(0), - m_enforce_dns_checkpoints(false), m_max_prepare_blocks_threads(4), m_db_blocks_per_sync(1), m_db_sync_mode(db_async), m_db_default_sync(false), m_fast_sync(true), m_show_time_stats(false), m_sync_counter(0), m_cancel(false), + m_enforce_dns_checkpoints(false), m_max_prepare_blocks_threads(4), m_db_sync_on_blocks(true), m_db_sync_threshold(1), m_db_sync_mode(db_async), m_db_default_sync(false), m_fast_sync(true), m_show_time_stats(false), m_sync_counter(0), m_bytes_to_sync(0), m_cancel(false), m_difficulty_for_next_block_top_hash(crypto::null_hash), m_difficulty_for_next_block(1) { @@ -3877,11 +3877,13 @@ bool Blockchain::cleanup_handle_incoming_blocks(bool force_sync) store_blockchain(); m_sync_counter = 0; } - else if (m_db_blocks_per_sync && m_sync_counter >= m_db_blocks_per_sync) + else if (m_db_sync_threshold && ((m_db_sync_on_blocks && m_sync_counter >= m_db_sync_threshold) || (!m_db_sync_on_blocks && m_bytes_to_sync >= m_db_sync_threshold))) { + MDEBUG("Sync threshold met, syncing"); if(m_db_sync_mode == db_async) { m_sync_counter = 0; + m_bytes_to_sync = 0; m_async_service.dispatch(boost::bind(&Blockchain::store_blockchain, this)); } else if(m_db_sync_mode == db_sync) @@ -4071,6 +4073,7 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vectorbatch_start(blocks_entry.size(), bytes))) { m_blockchain_lock.unlock(); m_tx_pool.unlock(); @@ -4419,7 +4422,7 @@ bool Blockchain::for_all_txpool_txes(std::functionfor_all_txpool_txes(f, include_blob, include_unrelayed_txes); } -void Blockchain::set_user_options(uint64_t maxthreads, uint64_t blocks_per_sync, blockchain_db_sync_mode sync_mode, bool fast_sync) +void Blockchain::set_user_options(uint64_t maxthreads, bool sync_on_blocks, uint64_t sync_threshold, blockchain_db_sync_mode sync_mode, bool fast_sync) { if (sync_mode == db_defaultsync) { @@ -4428,7 +4431,8 @@ void Blockchain::set_user_options(uint64_t maxthreads, uint64_t blocks_per_sync, } m_db_sync_mode = sync_mode; m_fast_sync = fast_sync; - m_db_blocks_per_sync = blocks_per_sync; + m_db_sync_on_blocks = sync_on_blocks; + m_db_sync_threshold = sync_threshold; m_max_prepare_blocks_threads = maxthreads; } diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index d95c8ed15f7..b1f8bb8298d 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -729,11 +729,12 @@ namespace cryptonote * @brief sets various performance options * * @param maxthreads max number of threads when preparing blocks for addition - * @param blocks_per_sync number of blocks to cache before syncing to database + * @param sync_on_blocks whether to sync based on blocks or bytes + * @param sync_threshold number of blocks/bytes to cache before syncing to database * @param sync_mode the ::blockchain_db_sync_mode to use * @param fast_sync sync using built-in block hashes as trusted */ - void set_user_options(uint64_t maxthreads, uint64_t blocks_per_sync, + void set_user_options(uint64_t maxthreads, bool sync_on_blocks, uint64_t sync_threshold, blockchain_db_sync_mode sync_mode, bool fast_sync); /** @@ -1017,11 +1018,13 @@ namespace cryptonote bool m_fast_sync; bool m_show_time_stats; bool m_db_default_sync; - uint64_t m_db_blocks_per_sync; + bool m_db_sync_on_blocks; + uint64_t m_db_sync_threshold; uint64_t m_max_prepare_blocks_threads; uint64_t m_fake_pow_calc_time; uint64_t m_fake_scan_time; uint64_t m_sync_counter; + uint64_t m_bytes_to_sync; std::vector m_timestamps; std::vector m_difficulties; uint64_t m_timestamps_and_difficulties_height; diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 18490c65e82..d0db387995f 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -439,9 +439,10 @@ namespace cryptonote MGINFO("Loading blockchain from folder " << folder.string() << " ..."); const std::string filename = folder.string(); - // default to fast:async:1 + // default to fast:async:1 if overridden blockchain_db_sync_mode sync_mode = db_defaultsync; - uint64_t blocks_per_sync = 1; + bool sync_on_blocks = true; + uint64_t sync_threshold = 1; if (m_nettype == FAKECHAIN) { @@ -491,7 +492,7 @@ namespace cryptonote else if(options[0] == "fastest") { db_flags = DBF_FASTEST; - blocks_per_sync = 1000; // default to fastest:async:1000 + sync_threshold = 1000; // default to fastest:async:1000 sync_mode = db_sync_mode_is_default ? db_defaultsync : db_async; } else @@ -509,9 +510,22 @@ namespace cryptonote if(options.size() >= 3 && !safemode) { char *endptr; - uint64_t bps = strtoull(options[2].c_str(), &endptr, 0); - if (*endptr == '\0') - blocks_per_sync = bps; + uint64_t threshold = strtoull(options[2].c_str(), &endptr, 0); + if (*endptr == '\0' || !strcmp(endptr, "blocks")) + { + sync_on_blocks = true; + sync_threshold = threshold; + } + else if (!strcmp(endptr, "bytes")) + { + sync_on_blocks = false; + sync_threshold = threshold; + } + else + { + LOG_ERROR("Invalid db sync mode: " << options[2]); + return false; + } } if (db_salvage) @@ -528,7 +542,7 @@ namespace cryptonote } m_blockchain_storage.set_user_options(blocks_threads, - blocks_per_sync, sync_mode, fast_sync); + sync_on_blocks, sync_threshold, sync_mode, fast_sync); const std::pair regtest_hard_forks[3] = {std::make_pair(1, 0), std::make_pair(Blockchain::get_hard_fork_heights(MAINNET).back().version, 1), std::make_pair(0, 0)}; const cryptonote::test_options regtest_test_options = { From bc8cbdb25d397a568ac39982a348ad2abab82508 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 15 Jul 2018 12:58:52 +0100 Subject: [PATCH 0238/1404] stack_trace: print stack traces on stdout if the logger isn't live --- src/common/stack_trace.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/common/stack_trace.cpp b/src/common/stack_trace.cpp index 9c2bf4b5379..55159783321 100644 --- a/src/common/stack_trace.cpp +++ b/src/common/stack_trace.cpp @@ -49,7 +49,16 @@ #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "stacktrace" -#define ST_LOG(x) CINFO(el::base::Writer,el::base::DispatchAction::FileOnlyLog,MONERO_DEFAULT_LOG_CATEGORY) << x +#define ST_LOG(x) \ + do { \ + auto elpp = ELPP; \ + if (elpp) { \ + CINFO(el::base::Writer,el::base::DispatchAction::FileOnlyLog,MONERO_DEFAULT_LOG_CATEGORY) << x; \ + } \ + else { \ + std::cout << x << std::endl; \ + } \ + } while(0) // from http://stackoverflow.com/questions/11665829/how-can-i-print-stack-trace-for-caught-exceptions-in-c-code-injection-in-c From 43074891479b4883645b06c4cc5341469d9cd9cd Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 15 Jul 2018 13:20:36 +0100 Subject: [PATCH 0239/1404] wallet: disable core dumps on startup in release mode --- src/common/util.cpp | 16 ++++++++++++++++ src/common/util.h | 2 ++ src/wallet/api/utils.cpp | 3 +++ src/wallet/wallet_args.cpp | 3 +++ 4 files changed, 24 insertions(+) diff --git a/src/common/util.cpp b/src/common/util.cpp index 7d9d7b408ea..be49c77c393 100644 --- a/src/common/util.cpp +++ b/src/common/util.cpp @@ -37,6 +37,7 @@ #ifdef __GLIBC__ #include #include +#include #include #include #include @@ -682,6 +683,21 @@ std::string get_nix_version_display_string() static void setup_crash_dump() {} #endif + bool disable_core_dumps() + { +#ifdef __GLIBC__ + // disable core dumps in release mode + struct rlimit rlimit; + rlimit.rlim_cur = rlimit.rlim_max = 0; + if (setrlimit(RLIMIT_CORE, &rlimit)) + { + MWARNING("Failed to disable core dumps"); + return false; + } +#endif + return true; + } + bool on_startup() { mlog_configure("", true); diff --git a/src/common/util.h b/src/common/util.h index a57a85fee65..b98a83bf6c5 100644 --- a/src/common/util.h +++ b/src/common/util.h @@ -149,6 +149,8 @@ namespace tools bool sanitize_locale(); + bool disable_core_dumps(); + bool on_startup(); /*! \brief Defines a signal handler for win32 and *nix diff --git a/src/wallet/api/utils.cpp b/src/wallet/api/utils.cpp index aebe41e5914..86fe5656473 100644 --- a/src/wallet/api/utils.cpp +++ b/src/wallet/api/utils.cpp @@ -51,6 +51,9 @@ bool isAddressLocal(const std::string &address) void onStartup() { tools::on_startup(); +#ifdef NDEBUG + tools::disable_core_dumps(); +#endif } } diff --git a/src/wallet/wallet_args.cpp b/src/wallet/wallet_args.cpp index a629eb5062b..95a4e0ad663 100644 --- a/src/wallet/wallet_args.cpp +++ b/src/wallet/wallet_args.cpp @@ -109,6 +109,9 @@ namespace wallet_args std::string lang = i18n_get_language(); tools::on_startup(); +#ifdef NDEBUG + tools::disable_core_dumps(); +#endif tools::set_strict_default_file_permissions(true); epee::string_tools::set_module_name_and_folder(argv[0]); From ac09cfa6be7d804f8d5fe0843b0a57046bc55ff6 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 13 Aug 2018 13:16:50 +0000 Subject: [PATCH 0240/1404] wallet2: remove obsolete pruned/unpruned case All daemons will not support pruned blocks --- src/wallet/wallet2.cpp | 29 +---------------------------- 1 file changed, 1 insertion(+), 28 deletions(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 90807803af2..4013c89a5a6 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -1753,34 +1753,7 @@ void wallet2::pull_blocks(uint64_t start_height, uint64_t &blocks_start_height, cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::response res = AUTO_VAL_INIT(res); req.block_ids = short_chain_history; - uint32_t rpc_version; - boost::optional result = m_node_rpc_proxy.get_rpc_version(rpc_version); - // no error - if (!!result) - { - // empty string -> not connection - THROW_WALLET_EXCEPTION_IF(result->empty(), tools::error::no_connection_to_daemon, "getversion"); - THROW_WALLET_EXCEPTION_IF(*result == CORE_RPC_STATUS_BUSY, tools::error::daemon_busy, "getversion"); - if (*result != CORE_RPC_STATUS_OK) - { - MDEBUG("Cannot determine daemon RPC version, not asking for pruned blocks"); - req.prune = false; // old daemon - } - } - else - { - if (rpc_version >= MAKE_CORE_RPC_VERSION(1, 7)) - { - MDEBUG("Daemon is recent enough, asking for pruned blocks"); - req.prune = true; - } - else - { - MDEBUG("Daemon is too old, not asking for pruned blocks"); - req.prune = false; - } - } - + req.prune = true; req.start_height = start_height; req.no_miner_tx = m_refresh_type == RefreshNoCoinbase; m_daemon_rpc_mutex.lock(); From 77655b0a0e46c6bccece889e5d98cc98b98fb5e3 Mon Sep 17 00:00:00 2001 From: rbrunner7 Date: Sun, 12 Aug 2018 14:49:32 +0200 Subject: [PATCH 0241/1404] simplewallet: Simplify LOCK_IDLE_SCOPE macro --- src/simplewallet/simplewallet.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 775b7c3598e..0523ce97e65 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -95,13 +95,8 @@ typedef cryptonote::simple_wallet sw; m_auto_refresh_enabled.store(false, std::memory_order_relaxed); \ /* stop any background refresh, and take over */ \ m_wallet->stop(); \ - m_idle_mutex.lock(); \ - while (m_auto_refresh_refreshing) \ - m_idle_cond.notify_one(); \ - m_idle_mutex.unlock(); \ -/* if (auto_refresh_run)*/ \ - /*m_auto_refresh_thread.join();*/ \ boost::unique_lock lock(m_idle_mutex); \ + m_idle_cond.notify_all(); \ epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){ \ m_auto_refresh_enabled.store(auto_refresh_enabled, std::memory_order_relaxed); \ }) From 10475ab23fe212998b02840e7b480915e8a30ee4 Mon Sep 17 00:00:00 2001 From: fireice-uk Date: Fri, 10 Aug 2018 16:49:18 +0200 Subject: [PATCH 0242/1404] node_rpc_proxy: fix fork earliest height caching [RYO backport] xref https://github.com/ryo-currency/ryo-currency/pull/86 --- src/wallet/node_rpc_proxy.cpp | 2 +- src/wallet/wallet2.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/wallet/node_rpc_proxy.cpp b/src/wallet/node_rpc_proxy.cpp index 401ada61b34..2072495cde1 100644 --- a/src/wallet/node_rpc_proxy.cpp +++ b/src/wallet/node_rpc_proxy.cpp @@ -146,7 +146,7 @@ boost::optional NodeRPCProxy::get_earliest_height(uint8_t version, CHECK_AND_ASSERT_MES(r, std::string(), "Failed to connect to daemon"); CHECK_AND_ASSERT_MES(resp_t.status != CORE_RPC_STATUS_BUSY, resp_t.status, "Failed to connect to daemon"); CHECK_AND_ASSERT_MES(resp_t.status == CORE_RPC_STATUS_OK, resp_t.status, "Failed to get hard fork status"); - m_earliest_height[version] = resp_t.enabled ? resp_t.earliest_height : std::numeric_limits::max(); + m_earliest_height[version] = resp_t.earliest_height; } earliest_height = m_earliest_height[version]; diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 90807803af2..3ad763f469a 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -8428,7 +8428,7 @@ bool wallet2::use_fork_rules(uint8_t version, int64_t early_blocks) const result = m_node_rpc_proxy.get_earliest_height(version, earliest_height); throw_on_rpc_response_error(result, "get_hard_fork_info"); - bool close_enough = height >= earliest_height - early_blocks && earliest_height != std::numeric_limits::max(); // start using the rules that many blocks beforehand + bool close_enough = height >= earliest_height - early_blocks; // start using the rules that many blocks beforehand if (close_enough) LOG_PRINT_L2("Using v" << (unsigned)version << " rules"); else From de905d4b48269a5bbc069da5e7476ffd798b7edb Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 13 Aug 2018 22:13:11 +0000 Subject: [PATCH 0243/1404] fuzz_tests: use __AFL_INIT when available alleged to speed things up --- tests/fuzz/fuzzer.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/fuzz/fuzzer.cpp b/tests/fuzz/fuzzer.cpp index b81bf80fe52..032ff049aaf 100644 --- a/tests/fuzz/fuzzer.cpp +++ b/tests/fuzz/fuzzer.cpp @@ -52,6 +52,10 @@ int run_fuzzer(int argc, const char **argv, Fuzzer &fuzzer) return 1; } +#ifdef __AFL_HAVE_MANUAL_CONTROL + __AFL_INIT(); +#endif + int ret = fuzzer.init(); if (ret) return ret; From d6fc870d041ab2fe28a6b8b525840962a218e91a Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 25 May 2018 11:59:59 +0100 Subject: [PATCH 0244/1404] db_lmdb: resize blockchain database when 90% filled instead of a random ratio from 60% to 90%. --- src/blockchain_db/lmdb/db_lmdb.cpp | 8 ++------ src/blockchain_db/lmdb/db_lmdb.h | 2 +- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp index a6eb3d880f7..f45b9eff1b6 100644 --- a/src/blockchain_db/lmdb/db_lmdb.cpp +++ b/src/blockchain_db/lmdb/db_lmdb.cpp @@ -545,8 +545,8 @@ bool BlockchainLMDB::need_resize(uint64_t threshold_size) const LOG_PRINT_L1("Space used: " << size_used); LOG_PRINT_L1("Space remaining: " << mei.me_mapsize - size_used); LOG_PRINT_L1("Size threshold: " << threshold_size); - float resize_percent_old = RESIZE_PERCENT; - LOG_PRINT_L1(boost::format("Percent used: %.04f Percent threshold: %.04f") % ((double)size_used/mei.me_mapsize) % resize_percent_old); + float resize_percent = RESIZE_PERCENT; + LOG_PRINT_L1(boost::format("Percent used: %.04f Percent threshold: %.04f") % ((double)size_used/mei.me_mapsize) % resize_percent); if (threshold_size > 0) { @@ -559,10 +559,6 @@ bool BlockchainLMDB::need_resize(uint64_t threshold_size) const return false; } - std::mt19937 engine(std::random_device{}()); - std::uniform_real_distribution fdis(0.6, 0.9); - double resize_percent = fdis(engine); - if ((double)size_used / mei.me_mapsize > resize_percent) { LOG_PRINT_L1("Threshold met (percent-based)"); diff --git a/src/blockchain_db/lmdb/db_lmdb.h b/src/blockchain_db/lmdb/db_lmdb.h index 8b214d2dfb6..5e3b3cfa595 100644 --- a/src/blockchain_db/lmdb/db_lmdb.h +++ b/src/blockchain_db/lmdb/db_lmdb.h @@ -450,7 +450,7 @@ class BlockchainLMDB : public BlockchainDB #endif #endif - constexpr static float RESIZE_PERCENT = 0.8f; + constexpr static float RESIZE_PERCENT = 0.9f; }; } // namespace cryptonote From b21fdaa87459e8898a023a7cd956fbcee4cf7eb4 Mon Sep 17 00:00:00 2001 From: stoffu Date: Tue, 14 Aug 2018 20:17:51 +0900 Subject: [PATCH 0245/1404] fuzz: set address properly --- tests/fuzz/signature.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/fuzz/signature.cpp b/tests/fuzz/signature.cpp index 7f22757b2f2..6dadf960dfa 100644 --- a/tests/fuzz/signature.cpp +++ b/tests/fuzz/signature.cpp @@ -64,6 +64,7 @@ int SignatureFuzzer::init() std::cerr << "failed to parse address" << std::endl; return 1; } + address = info.address; } catch (const std::exception &e) { From c3c14f30831f2414ce76cfecf4e4f8888791a4a2 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 15 Aug 2018 12:15:39 +0000 Subject: [PATCH 0246/1404] simplewallet: allow named priority levels for default-priority to match those used by the various transfer functions --- src/simplewallet/simplewallet.cpp | 46 +++++++++++++++++++++++++------ 1 file changed, 37 insertions(+), 9 deletions(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 775b7c3598e..0f922289ee2 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -527,6 +527,18 @@ bool parse_priority(const std::string& arg, uint32_t& priority) return false; } +std::string join_priority_strings(const char *delimiter) +{ + std::string s; + for (size_t n = 0; n < allowed_priority_strings.size(); ++n) + { + if (!s.empty()) + s += delimiter; + s += allowed_priority_strings[n]; + } + return s; +} + std::string simple_wallet::get_commands_str() { std::stringstream ss; @@ -1777,12 +1789,12 @@ bool simple_wallet::set_default_ring_size(const std::vector &args/* bool simple_wallet::set_default_priority(const std::vector &args/* = std::vector()*/) { - int priority = 0; + uint32_t priority = 0; try { if (strchr(args[1].c_str(), '-')) { - fail_msg_writer() << tr("priority must be 0, 1, 2, 3, or 4 "); + fail_msg_writer() << tr("priority must be either 0, 1, 2, 3, or 4, or one of: ") << join_priority_strings(", "); return true; } if (args[1] == "0") @@ -1791,11 +1803,23 @@ bool simple_wallet::set_default_priority(const std::vector &args/* } else { - priority = boost::lexical_cast(args[1]); - if (priority < 1 || priority > 4) + bool found = false; + for (size_t n = 0; n < allowed_priority_strings.size(); ++n) { - fail_msg_writer() << tr("priority must be 0, 1, 2, 3, or 4"); - return true; + if (allowed_priority_strings[n] == args[1]) + { + found = true; + priority = n; + } + } + if (!found) + { + priority = boost::lexical_cast(args[1]); + if (priority < 1 || priority > 4) + { + fail_msg_writer() << tr("priority must be either 0, 1, 2, 3, or 4, or one of: ") << join_priority_strings(", "); + return true; + } } } @@ -1809,7 +1833,7 @@ bool simple_wallet::set_default_priority(const std::vector &args/* } catch(const boost::bad_lexical_cast &) { - fail_msg_writer() << tr("priority must be 0, 1, 2, 3, or 4"); + fail_msg_writer() << tr("priority must be either 0, 1, 2, 3, or 4, or one of: ") << join_priority_strings(", "); return true; } catch(...) @@ -2471,6 +2495,10 @@ bool simple_wallet::set_variable(const std::vector &args) std::string seed_language = m_wallet->get_seed_language(); if (m_use_english_language_names) seed_language = crypto::ElectrumWords::get_english_name_for(seed_language); + std::string priority_string = "invalid"; + uint32_t priority = m_wallet->get_default_priority(); + if (priority < allowed_priority_strings.size()) + priority_string = allowed_priority_strings[priority]; success_msg_writer() << "seed = " << seed_language; success_msg_writer() << "always-confirm-transfers = " << m_wallet->always_confirm_transfers(); success_msg_writer() << "print-ring-members = " << m_wallet->print_ring_members(); @@ -2478,7 +2506,7 @@ bool simple_wallet::set_variable(const std::vector &args) success_msg_writer() << "default-ring-size = " << (m_wallet->default_mixin() ? m_wallet->default_mixin() + 1 : 0); success_msg_writer() << "auto-refresh = " << m_wallet->auto_refresh(); success_msg_writer() << "refresh-type = " << get_refresh_type_name(m_wallet->get_refresh_type()); - success_msg_writer() << "priority = " << m_wallet->get_default_priority(); + success_msg_writer() << "priority = " << priority<< " (" << priority_string << ")"; success_msg_writer() << "confirm-missing-payment-id = " << m_wallet->confirm_missing_payment_id(); success_msg_writer() << "ask-password = " << m_wallet->ask_password(); success_msg_writer() << "unit = " << cryptonote::get_unit(cryptonote::get_default_decimal_point()); @@ -2534,7 +2562,7 @@ bool simple_wallet::set_variable(const std::vector &args) CHECK_SIMPLE_VARIABLE("default-ring-size", set_default_ring_size, tr("integer >= ") << MIN_RING_SIZE); CHECK_SIMPLE_VARIABLE("auto-refresh", set_auto_refresh, tr("0 or 1")); CHECK_SIMPLE_VARIABLE("refresh-type", set_refresh_type, tr("full (slowest, no assumptions); optimize-coinbase (fast, assumes the whole coinbase is paid to a single address); no-coinbase (fastest, assumes we receive no coinbase transaction), default (same as optimize-coinbase)")); - CHECK_SIMPLE_VARIABLE("priority", set_default_priority, tr("0, 1, 2, 3, or 4")); + CHECK_SIMPLE_VARIABLE("priority", set_default_priority, tr("0, 1, 2, 3, or 4, or one of ") << join_priority_strings(", ")); CHECK_SIMPLE_VARIABLE("confirm-missing-payment-id", set_confirm_missing_payment_id, tr("0 or 1")); CHECK_SIMPLE_VARIABLE("ask-password", set_ask_password, tr("0 or 1")); CHECK_SIMPLE_VARIABLE("unit", set_unit, tr("monero, millinero, micronero, nanonero, piconero")); From 5ed6669130a154c3c154567014a7e56f4679c265 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 15 Aug 2018 12:47:33 +0000 Subject: [PATCH 0247/1404] wallet_rpc_server: remove unused amount_keys field in transfer RPC --- src/wallet/wallet_rpc_server_commands_defs.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h index 1bd572adda8..0e12378c3c1 100644 --- a/src/wallet/wallet_rpc_server_commands_defs.h +++ b/src/wallet/wallet_rpc_server_commands_defs.h @@ -424,7 +424,6 @@ namespace wallet_rpc { std::string tx_hash; std::string tx_key; - std::list amount_keys; uint64_t amount; uint64_t fee; std::string tx_blob; @@ -435,7 +434,6 @@ namespace wallet_rpc BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(tx_hash) KV_SERIALIZE(tx_key) - KV_SERIALIZE(amount_keys) KV_SERIALIZE(amount) KV_SERIALIZE(fee) KV_SERIALIZE(tx_blob) From 0c8d8f6b2e3b924c396babfa44b699ce09c91fc6 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 15 Aug 2018 13:09:14 +0000 Subject: [PATCH 0248/1404] unit_tests: remove std::move in return statement This actually prevents copy elision --- tests/unit_tests/serialization.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit_tests/serialization.cpp b/tests/unit_tests/serialization.cpp index 5a21140279f..07e15e61eb7 100644 --- a/tests/unit_tests/serialization.cpp +++ b/tests/unit_tests/serialization.cpp @@ -825,7 +825,7 @@ TEST(Serialization, portability_outputs) return {}; } crypto::chacha8(ciphertext.data() + sizeof(iv), ciphertext.size() - prefix_size, key, iv, &plaintext[0]); - return std::move(plaintext); + return plaintext; }; crypto::secret_key view_secret_key; epee::string_tools::hex_to_pod("339673bb1187e2f73ba7841ab6841c5553f96e9f13f8fe6612e69318db4e9d0a", view_secret_key); From 4e081001c007178d9d970219c14ae78afd9a6935 Mon Sep 17 00:00:00 2001 From: Dusan Klinec Date: Tue, 14 Aug 2018 23:23:00 +0200 Subject: [PATCH 0249/1404] Incremental Keccak API added - needed for TREZOR integration --- src/crypto/keccak.c | 74 +++++++++++++++++++++++++++++++++++++++++++++ src/crypto/keccak.h | 14 +++++++++ 2 files changed, 88 insertions(+) diff --git a/src/crypto/keccak.c b/src/crypto/keccak.c index de8e2a5b320..8fcd2138ea7 100644 --- a/src/crypto/keccak.c +++ b/src/crypto/keccak.c @@ -132,3 +132,77 @@ void keccak1600(const uint8_t *in, size_t inlen, uint8_t *md) { keccak(in, inlen, md, sizeof(state_t)); } + +#define KECCAK_FINALIZED 0x80000000 +#define KECCAK_BLOCKLEN 136 +#define KECCAK_WORDS 17 +#define KECCAK_DIGESTSIZE 32 +#define IS_ALIGNED_64(p) (0 == (7 & ((const char*)(p) - (const char*)0))) +#define KECCAK_PROCESS_BLOCK(st, block) { \ + for (int i_ = 0; i_ < KECCAK_WORDS; i_++){ \ + ((st))[i_] ^= ((block))[i_]; \ + }; \ + keccakf(st, KECCAK_ROUNDS); } + + +void keccak_init(KECCAK_CTX * ctx){ + memset(ctx, 0, sizeof(KECCAK_CTX)); +} + +void keccak_update(KECCAK_CTX * ctx, const uint8_t *in, size_t inlen){ + if (ctx->rest & KECCAK_FINALIZED) { + local_abort("Bad keccak use"); + } + + const size_t idx = ctx->rest; + ctx->rest = (ctx->rest + inlen) % KECCAK_BLOCKLEN; + + // fill partial block + if (idx) { + size_t left = KECCAK_BLOCKLEN - idx; + memcpy((char*)ctx->message + idx, in, (inlen < left ? inlen : left)); + if (inlen < left) return; + + KECCAK_PROCESS_BLOCK(ctx->hash, ctx->message); + + in += left; + inlen -= left; + } + + const bool is_aligned = IS_ALIGNED_64(in); + while (inlen >= KECCAK_BLOCKLEN) { + const uint64_t* aligned_message_block; + if (is_aligned) { + aligned_message_block = (uint64_t*)in; + } else { + memcpy(ctx->message, in, KECCAK_BLOCKLEN); + aligned_message_block = ctx->message; + } + + KECCAK_PROCESS_BLOCK(ctx->hash, aligned_message_block); + in += KECCAK_BLOCKLEN; + inlen -= KECCAK_BLOCKLEN; + } + if (inlen) { + memcpy(ctx->message, in, inlen); + } +} + +void keccak_finish(KECCAK_CTX * ctx, uint8_t *md){ + if (!(ctx->rest & KECCAK_FINALIZED)) + { + // clear the rest of the data queue + memset((char*)ctx->message + ctx->rest, 0, KECCAK_BLOCKLEN - ctx->rest); + ((char*)ctx->message)[ctx->rest] |= 0x01; + ((char*)ctx->message)[KECCAK_BLOCKLEN - 1] |= 0x80; + + // process final block + KECCAK_PROCESS_BLOCK(ctx->hash, ctx->message); + ctx->rest = KECCAK_FINALIZED; // mark context as finalized + } + + static_assert(KECCAK_BLOCKLEN > KECCAK_DIGESTSIZE, ""); + if (md) { + memcpy(md, ctx->hash, KECCAK_DIGESTSIZE); + } +} diff --git a/src/crypto/keccak.h b/src/crypto/keccak.h index fb9d8bd0430..9123c7a3b58 100644 --- a/src/crypto/keccak.h +++ b/src/crypto/keccak.h @@ -15,6 +15,17 @@ #define ROTL64(x, y) (((x) << (y)) | ((x) >> (64 - (y)))) #endif +// SHA3 Algorithm context. +typedef struct KECCAK_CTX +{ + // 1600 bits algorithm hashing state + uint64_t hash[25]; + // 1088-bit buffer for leftovers, block size = 136 B for 256-bit keccak + uint64_t message[17]; + // count of bytes in the message[] buffer + size_t rest; +} KECCAK_CTX; + // compute a keccak hash (md) of given byte length from "in" void keccak(const uint8_t *in, size_t inlen, uint8_t *md, int mdlen); @@ -23,4 +34,7 @@ void keccakf(uint64_t st[25], int norounds); void keccak1600(const uint8_t *in, size_t inlen, uint8_t *md); +void keccak_init(KECCAK_CTX * ctx); +void keccak_update(KECCAK_CTX * ctx, const uint8_t *in, size_t inlen); +void keccak_finish(KECCAK_CTX * ctx, uint8_t *md); #endif From a64f57fe42292720415a507feb4f543ef3c3adbe Mon Sep 17 00:00:00 2001 From: stoffu Date: Mon, 30 Jul 2018 14:38:55 +0900 Subject: [PATCH 0250/1404] wallet2: make --restricted-rpc available for wallet RPC only --- src/wallet/api/wallet.cpp | 4 +- src/wallet/api/wallet.h | 2 +- src/wallet/api/wallet_manager.cpp | 10 ++--- src/wallet/wallet2.cpp | 8 +--- src/wallet/wallet2.h | 4 +- src/wallet/wallet_rpc_server.cpp | 67 ++++++++++++++++-------------- src/wallet/wallet_rpc_server.h | 1 + tests/unit_tests/serialization.cpp | 3 +- 8 files changed, 48 insertions(+), 51 deletions(-) diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index f7c074b5afa..b48bf07e0d1 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -366,7 +366,7 @@ void Wallet::error(const std::string &category, const std::string &str) { } ///////////////////////// WalletImpl implementation //////////////////////// -WalletImpl::WalletImpl(NetworkType nettype, bool restricted, uint64_t kdf_rounds) +WalletImpl::WalletImpl(NetworkType nettype, uint64_t kdf_rounds) :m_wallet(nullptr) , m_status(Wallet::Status_Ok) , m_trustedDaemon(false) @@ -377,7 +377,7 @@ WalletImpl::WalletImpl(NetworkType nettype, bool restricted, uint64_t kdf_rounds , m_rebuildWalletCache(false) , m_is_connected(false) { - m_wallet = new tools::wallet2(static_cast(nettype), restricted, kdf_rounds); + m_wallet = new tools::wallet2(static_cast(nettype), kdf_rounds); m_history = new TransactionHistoryImpl(this); m_wallet2Callback = new Wallet2CallbackImpl(this); m_wallet->callback(m_wallet2Callback); diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h index 28b73423d44..9218d3ad51f 100644 --- a/src/wallet/api/wallet.h +++ b/src/wallet/api/wallet.h @@ -52,7 +52,7 @@ struct Wallet2CallbackImpl; class WalletImpl : public Wallet { public: - WalletImpl(NetworkType nettype = MAINNET, bool restricted = false, uint64_t kdf_rounds = 1); + WalletImpl(NetworkType nettype = MAINNET, uint64_t kdf_rounds = 1); ~WalletImpl(); bool create(const std::string &path, const std::string &password, const std::string &language); diff --git a/src/wallet/api/wallet_manager.cpp b/src/wallet/api/wallet_manager.cpp index 5daf11ec0d3..3851ca9cc81 100644 --- a/src/wallet/api/wallet_manager.cpp +++ b/src/wallet/api/wallet_manager.cpp @@ -52,14 +52,14 @@ namespace Monero { Wallet *WalletManagerImpl::createWallet(const std::string &path, const std::string &password, const std::string &language, NetworkType nettype, uint64_t kdf_rounds) { - WalletImpl * wallet = new WalletImpl(nettype, false, kdf_rounds); + WalletImpl * wallet = new WalletImpl(nettype, kdf_rounds); wallet->create(path, password, language); return wallet; } Wallet *WalletManagerImpl::openWallet(const std::string &path, const std::string &password, NetworkType nettype, uint64_t kdf_rounds) { - WalletImpl * wallet = new WalletImpl(nettype, false, kdf_rounds); + WalletImpl * wallet = new WalletImpl(nettype, kdf_rounds); wallet->open(path, password); //Refresh addressBook wallet->addressBook()->refresh(); @@ -90,7 +90,7 @@ Wallet *WalletManagerImpl::recoveryWallet(const std::string &path, uint64_t restoreHeight, uint64_t kdf_rounds) { - WalletImpl * wallet = new WalletImpl(nettype, false, kdf_rounds); + WalletImpl * wallet = new WalletImpl(nettype, kdf_rounds); if(restoreHeight > 0){ wallet->setRefreshFromBlockHeight(restoreHeight); } @@ -108,7 +108,7 @@ Wallet *WalletManagerImpl::createWalletFromKeys(const std::string &path, const std::string &spendKeyString, uint64_t kdf_rounds) { - WalletImpl * wallet = new WalletImpl(nettype, false, kdf_rounds); + WalletImpl * wallet = new WalletImpl(nettype, kdf_rounds); if(restoreHeight > 0){ wallet->setRefreshFromBlockHeight(restoreHeight); } @@ -124,7 +124,7 @@ Wallet *WalletManagerImpl::createWalletFromDevice(const std::string &path, const std::string &subaddressLookahead, uint64_t kdf_rounds) { - WalletImpl * wallet = new WalletImpl(nettype, false, kdf_rounds); + WalletImpl * wallet = new WalletImpl(nettype, kdf_rounds); if(restoreHeight > 0){ wallet->setRefreshFromBlockHeight(restoreHeight); } diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 98412d08e27..9deaad09b27 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -147,7 +147,6 @@ struct options { const command_line::arg_descriptor daemon_login = {"daemon-login", tools::wallet2::tr("Specify username[:password] for daemon RPC client"), "", true}; const command_line::arg_descriptor testnet = {"testnet", tools::wallet2::tr("For testnet. Daemon must also be launched with --testnet flag"), false}; const command_line::arg_descriptor stagenet = {"stagenet", tools::wallet2::tr("For stagenet. Daemon must also be launched with --stagenet flag"), false}; - const command_line::arg_descriptor restricted = {"restricted-rpc", tools::wallet2::tr("Restricts to view-only commands"), false}; const command_line::arg_descriptor shared_ringdb_dir = { "shared-ringdb-dir", tools::wallet2::tr("Set shared ring database path"), get_default_ringdb_path(), @@ -203,7 +202,6 @@ std::unique_ptr make_basic(const boost::program_options::variabl const bool testnet = command_line::get_arg(vm, opts.testnet); const bool stagenet = command_line::get_arg(vm, opts.stagenet); const network_type nettype = testnet ? TESTNET : stagenet ? STAGENET : MAINNET; - const bool restricted = command_line::get_arg(vm, opts.restricted); const uint64_t kdf_rounds = command_line::get_arg(vm, opts.kdf_rounds); THROW_WALLET_EXCEPTION_IF(kdf_rounds == 0, tools::error::wallet_internal_error, "KDF rounds must not be 0"); @@ -239,7 +237,7 @@ std::unique_ptr make_basic(const boost::program_options::variabl if (daemon_address.empty()) daemon_address = std::string("http://") + daemon_host + ":" + std::to_string(daemon_port); - std::unique_ptr wallet(new tools::wallet2(nettype, restricted, kdf_rounds)); + std::unique_ptr wallet(new tools::wallet2(nettype, kdf_rounds)); wallet->init(std::move(daemon_address), std::move(login)); boost::filesystem::path ringdb_path = command_line::get_arg(vm, opts.shared_ringdb_dir); wallet->set_ring_database(ringdb_path.string()); @@ -650,7 +648,7 @@ const size_t MAX_SPLIT_ATTEMPTS = 30; constexpr const std::chrono::seconds wallet2::rpc_timeout; const char* wallet2::tr(const char* str) { return i18n_translate(str, "tools::wallet2"); } -wallet2::wallet2(network_type nettype, bool restricted, uint64_t kdf_rounds): +wallet2::wallet2(network_type nettype, uint64_t kdf_rounds): m_multisig_rescan_info(NULL), m_multisig_rescan_k(NULL), m_run(true), @@ -681,7 +679,6 @@ wallet2::wallet2(network_type nettype, bool restricted, uint64_t kdf_rounds): m_segregation_height(0), m_ignore_fractional_outputs(true), m_is_initialized(false), - m_restricted(restricted), m_kdf_rounds(kdf_rounds), is_old_file_format(false), m_node_rpc_proxy(m_http_client, m_daemon_rpc_mutex), @@ -725,7 +722,6 @@ void wallet2::init_options(boost::program_options::options_description& desc_par command_line::add_arg(desc_params, opts.daemon_login); command_line::add_arg(desc_params, opts.testnet); command_line::add_arg(desc_params, opts.stagenet); - command_line::add_arg(desc_params, opts.restricted); command_line::add_arg(desc_params, opts.shared_ringdb_dir); command_line::add_arg(desc_params, opts.kdf_rounds); } diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 6e6c1a6ee7d..d04156461f5 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -167,7 +167,7 @@ namespace tools static bool verify_password(const std::string& keys_file_name, const epee::wipeable_string& password, bool no_spend_key, hw::device &hwdev, uint64_t kdf_rounds); - wallet2(cryptonote::network_type nettype = cryptonote::MAINNET, bool restricted = false, uint64_t kdf_rounds = 1); + wallet2(cryptonote::network_type nettype = cryptonote::MAINNET, uint64_t kdf_rounds = 1); ~wallet2(); struct multisig_info @@ -687,7 +687,6 @@ namespace tools RefreshType get_refresh_type() const { return m_refresh_type; } cryptonote::network_type nettype() const { return m_nettype; } - bool restricted() const { return m_restricted; } bool watch_only() const { return m_watch_only; } bool multisig(bool *ready = NULL, uint32_t *threshold = NULL, uint32_t *total = NULL) const; bool has_multisig_partial_key_images() const; @@ -1259,7 +1258,6 @@ namespace tools i_wallet2_callback* m_callback; bool m_key_on_device; cryptonote::network_type m_nettype; - bool m_restricted; uint64_t m_kdf_rounds; std::string seed_language; /*!< Language of the mnemonics (seed). */ bool is_old_file_format; /*!< Whether the wallet file is of an old file format */ diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index c6a81d886ff..510cb3e5813 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -60,6 +60,7 @@ namespace const command_line::arg_descriptor arg_rpc_bind_port = {"rpc-bind-port", "Sets bind port for server"}; const command_line::arg_descriptor arg_disable_rpc_login = {"disable-rpc-login", "Disable HTTP authentication for RPC connections served by this process"}; const command_line::arg_descriptor arg_trusted_daemon = {"trusted-daemon", "Enable commands which rely on a trusted daemon", false}; + const command_line::arg_descriptor arg_restricted = {"restricted-rpc", "Restricts to view-only commands", false}; const command_line::arg_descriptor arg_wallet_dir = {"wallet-dir", "Directory for newly created wallets"}; const command_line::arg_descriptor arg_prompt_for_password = {"prompt-for-password", "Prompts for password when not provided", false}; @@ -99,7 +100,7 @@ namespace tools } //------------------------------------------------------------------------------------------------------------------------------ - wallet_rpc_server::wallet_rpc_server():m_wallet(NULL), rpc_login_file(), m_stop(false), m_trusted_daemon(false), m_vm(NULL) + wallet_rpc_server::wallet_rpc_server():m_wallet(NULL), rpc_login_file(), m_stop(false), m_trusted_daemon(false), m_restricted(false), m_vm(NULL) { } //------------------------------------------------------------------------------------------------------------------------------ @@ -177,6 +178,7 @@ namespace tools m_trusted_daemon = true; } } + m_restricted = command_line::get_arg(*m_vm, arg_restricted); if (command_line::has_arg(*m_vm, arg_wallet_dir)) { m_wallet_dir = command_line::get_arg(*m_vm, arg_wallet_dir); @@ -830,7 +832,7 @@ namespace tools LOG_PRINT_L3("on_transfer starts"); if (!m_wallet) return not_open(er); - if (m_wallet->restricted()) + if (m_restricted) { er.code = WALLET_RPC_ERROR_CODE_DENIED; er.message = "Command unavailable in restricted mode."; @@ -890,7 +892,7 @@ namespace tools std::vector extra; if (!m_wallet) return not_open(er); - if (m_wallet->restricted()) + if (m_restricted) { er.code = WALLET_RPC_ERROR_CODE_DENIED; er.message = "Command unavailable in restricted mode."; @@ -933,7 +935,7 @@ namespace tools bool wallet_rpc_server::on_sign_transfer(const wallet_rpc::COMMAND_RPC_SIGN_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_SIGN_TRANSFER::response& res, epee::json_rpc::error& er) { if (!m_wallet) return not_open(er); - if (m_wallet->restricted()) + if (m_restricted) { er.code = WALLET_RPC_ERROR_CODE_DENIED; er.message = "Command unavailable in restricted mode."; @@ -1008,7 +1010,7 @@ namespace tools bool wallet_rpc_server::on_submit_transfer(const wallet_rpc::COMMAND_RPC_SUBMIT_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_SUBMIT_TRANSFER::response& res, epee::json_rpc::error& er) { if (!m_wallet) return not_open(er); - if (m_wallet->restricted()) + if (m_restricted) { er.code = WALLET_RPC_ERROR_CODE_DENIED; er.message = "Command unavailable in restricted mode."; @@ -1068,7 +1070,7 @@ namespace tools bool wallet_rpc_server::on_sweep_dust(const wallet_rpc::COMMAND_RPC_SWEEP_DUST::request& req, wallet_rpc::COMMAND_RPC_SWEEP_DUST::response& res, epee::json_rpc::error& er) { if (!m_wallet) return not_open(er); - if (m_wallet->restricted()) + if (m_restricted) { er.code = WALLET_RPC_ERROR_CODE_DENIED; er.message = "Command unavailable in restricted mode."; @@ -1096,7 +1098,7 @@ namespace tools std::vector extra; if (!m_wallet) return not_open(er); - if (m_wallet->restricted()) + if (m_restricted) { er.code = WALLET_RPC_ERROR_CODE_DENIED; er.message = "Command unavailable in restricted mode."; @@ -1144,7 +1146,7 @@ namespace tools std::vector extra; if (!m_wallet) return not_open(er); - if (m_wallet->restricted()) + if (m_restricted) { er.code = WALLET_RPC_ERROR_CODE_DENIED; er.message = "Command unavailable in restricted mode."; @@ -1360,7 +1362,7 @@ namespace tools bool wallet_rpc_server::on_store(const wallet_rpc::COMMAND_RPC_STORE::request& req, wallet_rpc::COMMAND_RPC_STORE::response& res, epee::json_rpc::error& er) { if (!m_wallet) return not_open(er); - if (m_wallet->restricted()) + if (m_restricted) { er.code = WALLET_RPC_ERROR_CODE_DENIED; er.message = "Command unavailable in restricted mode."; @@ -1563,7 +1565,7 @@ namespace tools bool wallet_rpc_server::on_query_key(const wallet_rpc::COMMAND_RPC_QUERY_KEY::request& req, wallet_rpc::COMMAND_RPC_QUERY_KEY::response& res, epee::json_rpc::error& er) { if (!m_wallet) return not_open(er); - if (m_wallet->restricted()) + if (m_restricted) { er.code = WALLET_RPC_ERROR_CODE_DENIED; er.message = "Command unavailable in restricted mode."; @@ -1598,7 +1600,7 @@ namespace tools bool wallet_rpc_server::on_rescan_blockchain(const wallet_rpc::COMMAND_RPC_RESCAN_BLOCKCHAIN::request& req, wallet_rpc::COMMAND_RPC_RESCAN_BLOCKCHAIN::response& res, epee::json_rpc::error& er) { if (!m_wallet) return not_open(er); - if (m_wallet->restricted()) + if (m_restricted) { er.code = WALLET_RPC_ERROR_CODE_DENIED; er.message = "Command unavailable in restricted mode."; @@ -1620,7 +1622,7 @@ namespace tools bool wallet_rpc_server::on_sign(const wallet_rpc::COMMAND_RPC_SIGN::request& req, wallet_rpc::COMMAND_RPC_SIGN::response& res, epee::json_rpc::error& er) { if (!m_wallet) return not_open(er); - if (m_wallet->restricted()) + if (m_restricted) { er.code = WALLET_RPC_ERROR_CODE_DENIED; er.message = "Command unavailable in restricted mode."; @@ -1634,7 +1636,7 @@ namespace tools bool wallet_rpc_server::on_verify(const wallet_rpc::COMMAND_RPC_VERIFY::request& req, wallet_rpc::COMMAND_RPC_VERIFY::response& res, epee::json_rpc::error& er) { if (!m_wallet) return not_open(er); - if (m_wallet->restricted()) + if (m_restricted) { er.code = WALLET_RPC_ERROR_CODE_DENIED; er.message = "Command unavailable in restricted mode."; @@ -1669,7 +1671,7 @@ namespace tools bool wallet_rpc_server::on_stop_wallet(const wallet_rpc::COMMAND_RPC_STOP_WALLET::request& req, wallet_rpc::COMMAND_RPC_STOP_WALLET::response& res, epee::json_rpc::error& er) { if (!m_wallet) return not_open(er); - if (m_wallet->restricted()) + if (m_restricted) { er.code = WALLET_RPC_ERROR_CODE_DENIED; er.message = "Command unavailable in restricted mode."; @@ -1692,7 +1694,7 @@ namespace tools bool wallet_rpc_server::on_set_tx_notes(const wallet_rpc::COMMAND_RPC_SET_TX_NOTES::request& req, wallet_rpc::COMMAND_RPC_SET_TX_NOTES::response& res, epee::json_rpc::error& er) { if (!m_wallet) return not_open(er); - if (m_wallet->restricted()) + if (m_restricted) { er.code = WALLET_RPC_ERROR_CODE_DENIED; er.message = "Command unavailable in restricted mode."; @@ -1764,7 +1766,7 @@ namespace tools bool wallet_rpc_server::on_set_attribute(const wallet_rpc::COMMAND_RPC_SET_ATTRIBUTE::request& req, wallet_rpc::COMMAND_RPC_SET_ATTRIBUTE::response& res, epee::json_rpc::error& er) { if (!m_wallet) return not_open(er); - if (m_wallet->restricted()) + if (m_restricted) { er.code = WALLET_RPC_ERROR_CODE_DENIED; er.message = "Command unavailable in restricted mode."; @@ -1779,7 +1781,7 @@ namespace tools bool wallet_rpc_server::on_get_attribute(const wallet_rpc::COMMAND_RPC_GET_ATTRIBUTE::request& req, wallet_rpc::COMMAND_RPC_GET_ATTRIBUTE::response& res, epee::json_rpc::error& er) { if (!m_wallet) return not_open(er); - if (m_wallet->restricted()) + if (m_restricted) { er.code = WALLET_RPC_ERROR_CODE_DENIED; er.message = "Command unavailable in restricted mode."; @@ -2055,7 +2057,7 @@ namespace tools bool wallet_rpc_server::on_get_transfers(const wallet_rpc::COMMAND_RPC_GET_TRANSFERS::request& req, wallet_rpc::COMMAND_RPC_GET_TRANSFERS::response& res, epee::json_rpc::error& er) { if (!m_wallet) return not_open(er); - if (m_wallet->restricted()) + if (m_restricted) { er.code = WALLET_RPC_ERROR_CODE_DENIED; er.message = "Command unavailable in restricted mode."; @@ -2121,7 +2123,7 @@ namespace tools bool wallet_rpc_server::on_get_transfer_by_txid(const wallet_rpc::COMMAND_RPC_GET_TRANSFER_BY_TXID::request& req, wallet_rpc::COMMAND_RPC_GET_TRANSFER_BY_TXID::response& res, epee::json_rpc::error& er) { if (!m_wallet) return not_open(er); - if (m_wallet->restricted()) + if (m_restricted) { er.code = WALLET_RPC_ERROR_CODE_DENIED; er.message = "Command unavailable in restricted mode."; @@ -2205,7 +2207,7 @@ namespace tools bool wallet_rpc_server::on_export_outputs(const wallet_rpc::COMMAND_RPC_EXPORT_OUTPUTS::request& req, wallet_rpc::COMMAND_RPC_EXPORT_OUTPUTS::response& res, epee::json_rpc::error& er) { if (!m_wallet) return not_open(er); - if (m_wallet->restricted()) + if (m_restricted) { er.code = WALLET_RPC_ERROR_CODE_DENIED; er.message = "Command unavailable in restricted mode."; @@ -2234,7 +2236,7 @@ namespace tools bool wallet_rpc_server::on_import_outputs(const wallet_rpc::COMMAND_RPC_IMPORT_OUTPUTS::request& req, wallet_rpc::COMMAND_RPC_IMPORT_OUTPUTS::response& res, epee::json_rpc::error& er) { if (!m_wallet) return not_open(er); - if (m_wallet->restricted()) + if (m_restricted) { er.code = WALLET_RPC_ERROR_CODE_DENIED; er.message = "Command unavailable in restricted mode."; @@ -2294,7 +2296,7 @@ namespace tools bool wallet_rpc_server::on_import_key_images(const wallet_rpc::COMMAND_RPC_IMPORT_KEY_IMAGES::request& req, wallet_rpc::COMMAND_RPC_IMPORT_KEY_IMAGES::response& res, epee::json_rpc::error& er) { if (!m_wallet) return not_open(er); - if (m_wallet->restricted()) + if (m_restricted) { er.code = WALLET_RPC_ERROR_CODE_DENIED; er.message = "Command unavailable in restricted mode."; @@ -2405,7 +2407,7 @@ namespace tools bool wallet_rpc_server::on_add_address_book(const wallet_rpc::COMMAND_RPC_ADD_ADDRESS_BOOK_ENTRY::request& req, wallet_rpc::COMMAND_RPC_ADD_ADDRESS_BOOK_ENTRY::response& res, epee::json_rpc::error& er) { if (!m_wallet) return not_open(er); - if (m_wallet->restricted()) + if (m_restricted) { er.code = WALLET_RPC_ERROR_CODE_DENIED; er.message = "Command unavailable in restricted mode."; @@ -2480,7 +2482,7 @@ namespace tools bool wallet_rpc_server::on_delete_address_book(const wallet_rpc::COMMAND_RPC_DELETE_ADDRESS_BOOK_ENTRY::request& req, wallet_rpc::COMMAND_RPC_DELETE_ADDRESS_BOOK_ENTRY::response& res, epee::json_rpc::error& er) { if (!m_wallet) return not_open(er); - if (m_wallet->restricted()) + if (m_restricted) { er.code = WALLET_RPC_ERROR_CODE_DENIED; er.message = "Command unavailable in restricted mode."; @@ -2506,7 +2508,7 @@ namespace tools bool wallet_rpc_server::on_rescan_spent(const wallet_rpc::COMMAND_RPC_RESCAN_SPENT::request& req, wallet_rpc::COMMAND_RPC_RESCAN_SPENT::response& res, epee::json_rpc::error& er) { if (!m_wallet) return not_open(er); - if (m_wallet->restricted()) + if (m_restricted) { er.code = WALLET_RPC_ERROR_CODE_DENIED; er.message = "Command unavailable in restricted mode."; @@ -2813,7 +2815,7 @@ namespace tools bool wallet_rpc_server::on_prepare_multisig(const wallet_rpc::COMMAND_RPC_PREPARE_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_PREPARE_MULTISIG::response& res, epee::json_rpc::error& er) { if (!m_wallet) return not_open(er); - if (m_wallet->restricted()) + if (m_restricted) { er.code = WALLET_RPC_ERROR_CODE_DENIED; er.message = "Command unavailable in restricted mode."; @@ -2839,7 +2841,7 @@ namespace tools bool wallet_rpc_server::on_make_multisig(const wallet_rpc::COMMAND_RPC_MAKE_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_MAKE_MULTISIG::response& res, epee::json_rpc::error& er) { if (!m_wallet) return not_open(er); - if (m_wallet->restricted()) + if (m_restricted) { er.code = WALLET_RPC_ERROR_CODE_DENIED; er.message = "Command unavailable in restricted mode."; @@ -2876,7 +2878,7 @@ namespace tools bool wallet_rpc_server::on_export_multisig(const wallet_rpc::COMMAND_RPC_EXPORT_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_EXPORT_MULTISIG::response& res, epee::json_rpc::error& er) { if (!m_wallet) return not_open(er); - if (m_wallet->restricted()) + if (m_restricted) { er.code = WALLET_RPC_ERROR_CODE_DENIED; er.message = "Command unavailable in restricted mode."; @@ -2916,7 +2918,7 @@ namespace tools bool wallet_rpc_server::on_import_multisig(const wallet_rpc::COMMAND_RPC_IMPORT_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_IMPORT_MULTISIG::response& res, epee::json_rpc::error& er) { if (!m_wallet) return not_open(er); - if (m_wallet->restricted()) + if (m_restricted) { er.code = WALLET_RPC_ERROR_CODE_DENIED; er.message = "Command unavailable in restricted mode."; @@ -2989,7 +2991,7 @@ namespace tools bool wallet_rpc_server::on_finalize_multisig(const wallet_rpc::COMMAND_RPC_FINALIZE_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_FINALIZE_MULTISIG::response& res, epee::json_rpc::error& er) { if (!m_wallet) return not_open(er); - if (m_wallet->restricted()) + if (m_restricted) { er.code = WALLET_RPC_ERROR_CODE_DENIED; er.message = "Command unavailable in restricted mode."; @@ -3040,7 +3042,7 @@ namespace tools bool wallet_rpc_server::on_sign_multisig(const wallet_rpc::COMMAND_RPC_SIGN_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_SIGN_MULTISIG::response& res, epee::json_rpc::error& er) { if (!m_wallet) return not_open(er); - if (m_wallet->restricted()) + if (m_restricted) { er.code = WALLET_RPC_ERROR_CODE_DENIED; er.message = "Command unavailable in restricted mode."; @@ -3109,7 +3111,7 @@ namespace tools bool wallet_rpc_server::on_submit_multisig(const wallet_rpc::COMMAND_RPC_SUBMIT_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_SUBMIT_MULTISIG::response& res, epee::json_rpc::error& er) { if (!m_wallet) return not_open(er); - if (m_wallet->restricted()) + if (m_restricted) { er.code = WALLET_RPC_ERROR_CODE_DENIED; er.message = "Command unavailable in restricted mode."; @@ -3191,6 +3193,7 @@ int main(int argc, char** argv) { command_line::add_arg(desc_params, arg_rpc_bind_port); command_line::add_arg(desc_params, arg_disable_rpc_login); command_line::add_arg(desc_params, arg_trusted_daemon); + command_line::add_arg(desc_params, arg_restricted); cryptonote::rpc_args::init_options(desc_params); command_line::add_arg(desc_params, arg_wallet_file); command_line::add_arg(desc_params, arg_from_json); diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h index b7e545c53f3..25eb01ba950 100644 --- a/src/wallet/wallet_rpc_server.h +++ b/src/wallet/wallet_rpc_server.h @@ -237,6 +237,7 @@ namespace tools tools::private_file rpc_login_file; std::atomic m_stop; bool m_trusted_daemon; + bool m_restricted; const boost::program_options::variables_map *m_vm; }; } diff --git a/tests/unit_tests/serialization.cpp b/tests/unit_tests/serialization.cpp index 40616059a2f..5bec280b13c 100644 --- a/tests/unit_tests/serialization.cpp +++ b/tests/unit_tests/serialization.cpp @@ -671,8 +671,7 @@ TEST(Serialization, serializes_ringct_types) TEST(Serialization, portability_wallet) { const cryptonote::network_type nettype = cryptonote::TESTNET; - const bool restricted = false; - tools::wallet2 w(nettype, restricted); + tools::wallet2 w(nettype); const boost::filesystem::path wallet_file = unit_test::data_dir / "wallet_9svHk1"; string password = "test"; bool r = false; From ea37614efe518ff8f363ddf2465301687e04d977 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 7 Jul 2018 00:03:15 +0100 Subject: [PATCH 0251/1404] wallet: wipe seed from memory where appropriate --- contrib/epee/include/fnv1.h | 45 ++++++ contrib/epee/include/hex.h | 5 + contrib/epee/include/wipeable_string.h | 28 +++- contrib/epee/src/hex.cpp | 10 +- contrib/epee/src/wipeable_string.cpp | 112 +++++++++++++- src/common/password.cpp | 22 +-- src/common/password.h | 2 +- src/mnemonics/electrum-words.cpp | 120 ++++++++------- src/mnemonics/electrum-words.h | 12 +- src/mnemonics/language_base.h | 29 ++-- src/simplewallet/simplewallet.cpp | 105 ++++++++++--- src/simplewallet/simplewallet.h | 7 +- src/wallet/api/wallet.cpp | 4 +- src/wallet/wallet2.cpp | 44 ++++-- src/wallet/wallet2.h | 11 +- src/wallet/wallet_rpc_server.cpp | 4 +- tests/unit_tests/CMakeLists.txt | 3 +- tests/unit_tests/mnemonics.cpp | 30 +++- tests/unit_tests/wipeable_string.cpp | 204 +++++++++++++++++++++++++ 19 files changed, 653 insertions(+), 144 deletions(-) create mode 100644 contrib/epee/include/fnv1.h create mode 100644 tests/unit_tests/wipeable_string.cpp diff --git a/contrib/epee/include/fnv1.h b/contrib/epee/include/fnv1.h new file mode 100644 index 00000000000..c04389bca5c --- /dev/null +++ b/contrib/epee/include/fnv1.h @@ -0,0 +1,45 @@ +// Copyright (c) 2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#pragma once + +namespace epee +{ + +namespace fnv +{ + inline uint64_t FNV1a(const char *ptr, size_t sz) + { + uint64_t h = 0xcbf29ce484222325; + for (size_t i = 0; i < sz; ++i) + h = (h ^ *(const uint8_t*)ptr++) * 0x100000001b3; + return h; + } +} + +} diff --git a/contrib/epee/include/hex.h b/contrib/epee/include/hex.h index e960da1d2c4..02600c32062 100644 --- a/contrib/epee/include/hex.h +++ b/contrib/epee/include/hex.h @@ -33,6 +33,7 @@ #include #include +#include "wipeable_string.h" #include "span.h" namespace epee @@ -41,6 +42,8 @@ namespace epee { //! \return A std::string containing hex of `src`. static std::string string(const span src); + //! \return A epee::wipeable_string containing hex of `src`. + static epee::wipeable_string wipeable_string(const span src); //! \return An array containing hex of `src`. template @@ -59,6 +62,8 @@ namespace epee static void formatted(std::ostream& out, const span src); private: + template T static convert(const span src); + //! Write `src` bytes as hex to `out`. `out` must be twice the length static void buffer_unchecked(char* out, const span src) noexcept; }; diff --git a/contrib/epee/include/wipeable_string.h b/contrib/epee/include/wipeable_string.h index 70d1a958642..4cebe5fdfe9 100644 --- a/contrib/epee/include/wipeable_string.h +++ b/contrib/epee/include/wipeable_string.h @@ -28,28 +28,43 @@ #pragma once +#include #include #include #include +#include "fnv1.h" namespace epee { class wipeable_string { public: + typedef char value_type; + wipeable_string() {} wipeable_string(const wipeable_string &other); wipeable_string(wipeable_string &&other); wipeable_string(const std::string &other); wipeable_string(std::string &&other); wipeable_string(const char *s); + wipeable_string(const char *s, size_t len); ~wipeable_string(); void wipe(); void push_back(char c); - void pop_back(); + void operator+=(char c); + void operator+=(const std::string &s); + void operator+=(const epee::wipeable_string &s); + void operator+=(const char *s); + void append(const char *ptr, size_t len); + char pop_back(); const char *data() const noexcept { return buffer.data(); } + char *data() noexcept { return buffer.data(); } size_t size() const noexcept { return buffer.size(); } + size_t length() const noexcept { return buffer.size(); } bool empty() const noexcept { return buffer.empty(); } + void trim(); + void split(std::vector &fields) const; + boost::optional parse_hexstr() const; void resize(size_t sz); void reserve(size_t sz); void clear(); @@ -65,3 +80,14 @@ namespace epee std::vector buffer; }; } + +namespace std +{ + template<> struct hash + { + size_t operator()(const epee::wipeable_string &s) const + { + return epee::fnv::FNV1a(s.data(), s.size()); + } + }; +} diff --git a/contrib/epee/src/hex.cpp b/contrib/epee/src/hex.cpp index c143b2dc206..5c8acc8be68 100644 --- a/contrib/epee/src/hex.cpp +++ b/contrib/epee/src/hex.cpp @@ -52,17 +52,21 @@ namespace epee } } - std::string to_hex::string(const span src) + template + T to_hex::convert(const span src) { if (std::numeric_limits::max() / 2 < src.size()) throw std::range_error("hex_view::to_string exceeded maximum size"); - std::string out{}; + T out{}; out.resize(src.size() * 2); - buffer_unchecked(std::addressof(out[0]), src); + to_hex::buffer_unchecked((char*)out.data(), src); // can't see the non const version in wipeable_string?? return out; } + std::string to_hex::string(const span src) { return convert(src); } + epee::wipeable_string to_hex::wipeable_string(const span src) { return convert(src); } + void to_hex::buffer(std::ostream& out, const span src) { write_hex(std::ostreambuf_iterator{out}, src); diff --git a/contrib/epee/src/wipeable_string.cpp b/contrib/epee/src/wipeable_string.cpp index 6ed4ee8a222..7c972276503 100644 --- a/contrib/epee/src/wipeable_string.cpp +++ b/contrib/epee/src/wipeable_string.cpp @@ -26,11 +26,22 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#include #include #include "memwipe.h" #include "misc_log_ex.h" #include "wipeable_string.h" +namespace +{ + int atolower(int c) + { + if (c >= 'A' && c <= 'Z') + c |= 32; + return c; + } +} + namespace epee { @@ -69,6 +80,12 @@ wipeable_string::wipeable_string(const char *s) memcpy(buffer.data(), s, size()); } +wipeable_string::wipeable_string(const char *s, size_t len) +{ + grow(len); + memcpy(buffer.data(), s, len); +} + wipeable_string::~wipeable_string() { wipe(); @@ -109,9 +126,100 @@ void wipeable_string::push_back(char c) buffer.back() = c; } -void wipeable_string::pop_back() +void wipeable_string::operator+=(char c) +{ + push_back(c); +} + +void wipeable_string::append(const char *ptr, size_t len) +{ + const size_t orgsz = size(); + CHECK_AND_ASSERT_THROW_MES(orgsz < std::numeric_limits::max() - len, "Appended data too large"); + grow(orgsz + len); + if (len > 0) + memcpy(data() + orgsz, ptr, len); +} + +void wipeable_string::operator+=(const char *s) +{ + append(s, strlen(s)); +} + +void wipeable_string::operator+=(const epee::wipeable_string &s) +{ + append(s.data(), s.size()); +} + +void wipeable_string::operator+=(const std::string &s) +{ + append(s.c_str(), s.size()); +} + +void wipeable_string::trim() +{ + size_t prefix = 0; + while (prefix < size() && data()[prefix] == ' ') + ++prefix; + if (prefix > 0) + memmove(buffer.data(), buffer.data() + prefix, size() - prefix); + + size_t suffix = 0; + while (suffix < size()-prefix && data()[size() - 1 - prefix - suffix] == ' ') + ++suffix; + + resize(size() - prefix - suffix); +} + +void wipeable_string::split(std::vector &fields) const +{ + fields.clear(); + size_t len = size(); + const char *ptr = data(); + bool space = true; + while (len--) + { + const char c = *ptr++; + if (c != ' ') + { + if (space) + fields.push_back({}); + fields.back().push_back(c); + } + space = c == ' '; + } +} + +boost::optional wipeable_string::parse_hexstr() const +{ + if (size() % 2 != 0) + return boost::none; + boost::optional res = epee::wipeable_string(""); + const size_t len = size(); + const char *d = data(); + res->grow(0, len / 2); + static constexpr const char hex[] = u8"0123456789abcdef"; + for (size_t i = 0; i < len; i += 2) + { + char c = atolower(d[i]); + const char *ptr0 = strchr(hex, c); + if (!ptr0) + return boost::none; + c = atolower(d[i+1]); + const char *ptr1 = strchr(hex, c); + if (!ptr1) + return boost::none; + res->push_back(((ptr0-hex)<<4) | (ptr1-hex)); + } + return res; +} + +char wipeable_string::pop_back() { - resize(size() - 1); + const size_t sz = size(); + CHECK_AND_ASSERT_THROW_MES(sz > 0, "Popping from an empty string"); + const char c = buffer.back(); + resize(sz - 1); + return c; } void wipeable_string::resize(size_t sz) diff --git a/src/common/password.cpp b/src/common/password.cpp index 3ce2ba42a34..5671c4a4e9c 100644 --- a/src/common/password.cpp +++ b/src/common/password.cpp @@ -54,7 +54,7 @@ namespace return 0 != _isatty(_fileno(stdin)); } - bool read_from_tty(epee::wipeable_string& pass) + bool read_from_tty(epee::wipeable_string& pass, bool hide_input) { static constexpr const char BACKSPACE = 8; @@ -62,7 +62,7 @@ namespace DWORD mode_old; ::GetConsoleMode(h_cin, &mode_old); - DWORD mode_new = mode_old & ~(ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT); + DWORD mode_new = mode_old & ~((hide_input ? ENABLE_ECHO_INPUT : 0) | ENABLE_LINE_INPUT); ::SetConsoleMode(h_cin, mode_new); bool r = true; @@ -107,14 +107,14 @@ namespace return 0 != isatty(fileno(stdin)); } - int getch() noexcept + int getch(bool hide_input) noexcept { struct termios tty_old; tcgetattr(STDIN_FILENO, &tty_old); struct termios tty_new; tty_new = tty_old; - tty_new.c_lflag &= ~(ICANON | ECHO); + tty_new.c_lflag &= ~(ICANON | (hide_input ? ECHO : 0)); tcsetattr(STDIN_FILENO, TCSANOW, &tty_new); int ch = getchar(); @@ -124,14 +124,14 @@ namespace return ch; } - bool read_from_tty(epee::wipeable_string& aPass) + bool read_from_tty(epee::wipeable_string& aPass, bool hide_input) { static constexpr const char BACKSPACE = 127; aPass.reserve(tools::password_container::max_password_size); while (aPass.size() < tools::password_container::max_password_size) { - int ch = getch(); + int ch = getch(hide_input); if (EOF == ch || ch == EOT) { return false; @@ -159,18 +159,18 @@ namespace #endif // end !WIN32 - bool read_from_tty(const bool verify, const char *message, epee::wipeable_string& pass1, epee::wipeable_string& pass2) + bool read_from_tty(const bool verify, const char *message, bool hide_input, epee::wipeable_string& pass1, epee::wipeable_string& pass2) { while (true) { if (message) std::cout << message <<": " << std::flush; - if (!read_from_tty(pass1)) + if (!read_from_tty(pass1, hide_input)) return false; if (verify) { std::cout << "Confirm password: "; - if (!read_from_tty(pass2)) + if (!read_from_tty(pass2, hide_input)) return false; if(pass1!=pass2) { @@ -229,12 +229,12 @@ namespace tools std::atomic password_container::is_prompting(false); - boost::optional password_container::prompt(const bool verify, const char *message) + boost::optional password_container::prompt(const bool verify, const char *message, bool hide_input) { is_prompting = true; password_container pass1{}; password_container pass2{}; - if (is_cin_tty() ? read_from_tty(verify, message, pass1.m_password, pass2.m_password) : read_from_file(pass1.m_password)) + if (is_cin_tty() ? read_from_tty(verify, message, hide_input, pass1.m_password, pass2.m_password) : read_from_file(pass1.m_password)) { is_prompting = false; return {std::move(pass1)}; diff --git a/src/common/password.h b/src/common/password.h index 61937b93a75..529881e4044 100644 --- a/src/common/password.h +++ b/src/common/password.h @@ -49,7 +49,7 @@ namespace tools password_container(std::string&& password) noexcept; //! \return A password from stdin TTY prompt or `std::cin` pipe. - static boost::optional prompt(bool verify, const char *mesage = "Password"); + static boost::optional prompt(bool verify, const char *mesage = "Password", bool hide_input = true); static std::atomic is_prompting; password_container(const password_container&) = delete; diff --git a/src/mnemonics/electrum-words.cpp b/src/mnemonics/electrum-words.cpp index 19a9c26bbca..290f2cb9382 100644 --- a/src/mnemonics/electrum-words.cpp +++ b/src/mnemonics/electrum-words.cpp @@ -43,6 +43,8 @@ #include #include #include +#include "wipeable_string.h" +#include "misc_language.h" #include "crypto/crypto.h" // for declaration of crypto::secret_key #include #include "mnemonics/electrum-words.h" @@ -80,9 +82,9 @@ namespace crypto namespace { - uint32_t create_checksum_index(const std::vector &word_list, + uint32_t create_checksum_index(const std::vector &word_list, uint32_t unique_prefix_length); - bool checksum_test(std::vector seed, uint32_t unique_prefix_length); + bool checksum_test(std::vector seed, uint32_t unique_prefix_length); /*! * \brief Finds the word list that contains the seed words and puts the indices @@ -93,7 +95,7 @@ namespace * \param language Language instance pointer to write to after it is found. * \return true if all the words were present in some language false if not. */ - bool find_seed_language(const std::vector &seed, + bool find_seed_language(const std::vector &seed, bool has_checksum, std::vector &matched_indices, Language::Base **language) { // If there's a new language added, add an instance of it here. @@ -114,17 +116,19 @@ namespace }); Language::Base *fallback = NULL; + std::vector::const_iterator it2; + matched_indices.reserve(seed.size()); + // Iterate through all the languages and find a match for (std::vector::iterator it1 = language_instances.begin(); it1 != language_instances.end(); it1++) { - const std::unordered_map &word_map = (*it1)->get_word_map(); - const std::unordered_map &trimmed_word_map = (*it1)->get_trimmed_word_map(); + const std::unordered_map &word_map = (*it1)->get_word_map(); + const std::unordered_map &trimmed_word_map = (*it1)->get_trimmed_word_map(); // To iterate through seed words - std::vector::const_iterator it2; bool full_match = true; - std::string trimmed_word; + epee::wipeable_string trimmed_word; // Iterate through all the words and see if they're all present for (it2 = seed.begin(); it2 != seed.end(); it2++) { @@ -167,6 +171,7 @@ namespace return true; } // Some didn't match. Clear the index array. + memwipe(matched_indices.data(), matched_indices.size() * sizeof(matched_indices[0])); matched_indices.clear(); } @@ -181,6 +186,7 @@ namespace } MINFO("No match found"); + memwipe(matched_indices.data(), matched_indices.size() * sizeof(matched_indices[0])); return false; } @@ -190,12 +196,12 @@ namespace * \param unique_prefix_length the prefix length of each word to use for checksum * \return Checksum index */ - uint32_t create_checksum_index(const std::vector &word_list, + uint32_t create_checksum_index(const std::vector &word_list, uint32_t unique_prefix_length) { - std::string trimmed_words = ""; + epee::wipeable_string trimmed_words = ""; - for (std::vector::const_iterator it = word_list.begin(); it != word_list.end(); it++) + for (std::vector::const_iterator it = word_list.begin(); it != word_list.end(); it++) { if (it->length() > unique_prefix_length) { @@ -217,22 +223,22 @@ namespace * \param unique_prefix_length the prefix length of each word to use for checksum * \return True if the test passed false if not. */ - bool checksum_test(std::vector seed, uint32_t unique_prefix_length) + bool checksum_test(std::vector seed, uint32_t unique_prefix_length) { if (seed.empty()) return false; // The last word is the checksum. - std::string last_word = seed.back(); + epee::wipeable_string last_word = seed.back(); seed.pop_back(); - std::string checksum = seed[create_checksum_index(seed, unique_prefix_length)]; + epee::wipeable_string checksum = seed[create_checksum_index(seed, unique_prefix_length)]; - std::string trimmed_checksum = checksum.length() > unique_prefix_length ? Language::utf8prefix(checksum, unique_prefix_length) : + epee::wipeable_string trimmed_checksum = checksum.length() > unique_prefix_length ? Language::utf8prefix(checksum, unique_prefix_length) : checksum; - std::string trimmed_last_word = last_word.length() > unique_prefix_length ? Language::utf8prefix(last_word, unique_prefix_length) : + epee::wipeable_string trimmed_last_word = last_word.length() > unique_prefix_length ? Language::utf8prefix(last_word, unique_prefix_length) : last_word; bool ret = trimmed_checksum == trimmed_last_word; - MINFO("Checksum is %s" << (ret ? "valid" : "invalid")); + MINFO("Checksum is " << (ret ? "valid" : "invalid")); return ret; } } @@ -260,13 +266,12 @@ namespace crypto * \param language_name Language of the seed as found gets written here. * \return false if not a multiple of 3 words, or if word is not in the words list */ - bool words_to_bytes(std::string words, std::string& dst, size_t len, bool duplicate, + bool words_to_bytes(const epee::wipeable_string &words, epee::wipeable_string& dst, size_t len, bool duplicate, std::string &language_name) { - std::vector seed; + std::vector seed; - boost::algorithm::trim(words); - boost::split(seed, words, boost::is_any_of(" "), boost::token_compress_on); + words.split(seed); if (len % 4) { @@ -291,6 +296,7 @@ namespace crypto } std::vector matched_indices; + auto wiper = epee::misc_utils::create_scope_leave_handler([&](){memwipe(matched_indices.data(), matched_indices.size() * sizeof(matched_indices[0]));}); Language::Base *language; if (!find_seed_language(seed, has_checksum, matched_indices, &language)) { @@ -313,33 +319,33 @@ namespace crypto for (unsigned int i=0; i < seed.size() / 3; i++) { - uint32_t val; - uint32_t w1, w2, w3; - w1 = matched_indices[i*3]; - w2 = matched_indices[i*3 + 1]; - w3 = matched_indices[i*3 + 2]; + uint32_t w[4]; + w[1] = matched_indices[i*3]; + w[2] = matched_indices[i*3 + 1]; + w[3] = matched_indices[i*3 + 2]; - val = w1 + word_list_length * (((word_list_length - w1) + w2) % word_list_length) + - word_list_length * word_list_length * (((word_list_length - w2) + w3) % word_list_length); + w[0]= w[1] + word_list_length * (((word_list_length - w[1]) + w[2]) % word_list_length) + + word_list_length * word_list_length * (((word_list_length - w[2]) + w[3]) % word_list_length); - if (!(val % word_list_length == w1)) + if (!(w[0]% word_list_length == w[1])) { + memwipe(w, sizeof(w)); MERROR("Invalid seed: mumble mumble"); return false; } - dst.append((const char*)&val, 4); // copy 4 bytes to position + dst.append((const char*)&w[0], 4); // copy 4 bytes to position + memwipe(w, sizeof(w)); } if (len > 0 && duplicate) { const size_t expected = len * 3 / 32; - std::string wlist_copy = words; if (seed.size() == expected/2) { - dst.append(dst); // if electrum 12-word seed, duplicate - wlist_copy += ' '; - wlist_copy += words; + dst += ' '; // if electrum 12-word seed, duplicate + dst += dst; // if electrum 12-word seed, duplicate + dst.pop_back(); // trailing space } } @@ -353,10 +359,10 @@ namespace crypto * \param language_name Language of the seed as found gets written here. * \return false if not a multiple of 3 words, or if word is not in the words list */ - bool words_to_bytes(std::string words, crypto::secret_key& dst, + bool words_to_bytes(const epee::wipeable_string &words, crypto::secret_key& dst, std::string &language_name) { - std::string s; + epee::wipeable_string s; if (!words_to_bytes(words, s, sizeof(dst), true, language_name)) { MERROR("Invalid seed: failed to convert words to bytes"); @@ -378,7 +384,7 @@ namespace crypto * \param language_name Seed language name * \return true if successful false if not. Unsuccessful if wrong key size. */ - bool bytes_to_words(const char *src, size_t len, std::string& words, + bool bytes_to_words(const char *src, size_t len, epee::wipeable_string& words, const std::string &language_name) { @@ -397,39 +403,38 @@ namespace crypto } const std::vector &word_list = language->get_word_list(); // To store the words for random access to add the checksum word later. - std::vector words_store; + std::vector words_store; uint32_t word_list_length = word_list.size(); // 4 bytes -> 3 words. 8 digits base 16 -> 3 digits base 1626 - for (unsigned int i=0; i < len/4; i++, words += ' ') + for (unsigned int i=0; i < len/4; i++, words.push_back(' ')) { - uint32_t w1, w2, w3; - - uint32_t val; + uint32_t w[4]; - memcpy(&val, src + (i * 4), 4); + memcpy(&w[0], src + (i * 4), 4); - w1 = val % word_list_length; - w2 = ((val / word_list_length) + w1) % word_list_length; - w3 = (((val / word_list_length) / word_list_length) + w2) % word_list_length; + w[1] = w[0] % word_list_length; + w[2] = ((w[0] / word_list_length) + w[1]) % word_list_length; + w[3] = (((w[0] / word_list_length) / word_list_length) + w[2]) % word_list_length; - words += word_list[w1]; + words += word_list[w[1]]; words += ' '; - words += word_list[w2]; + words += word_list[w[2]]; words += ' '; - words += word_list[w3]; + words += word_list[w[3]]; + + words_store.push_back(word_list[w[1]]); + words_store.push_back(word_list[w[2]]); + words_store.push_back(word_list[w[3]]); - words_store.push_back(word_list[w1]); - words_store.push_back(word_list[w2]); - words_store.push_back(word_list[w3]); + memwipe(w, sizeof(w)); } - words.pop_back(); - words += (' ' + words_store[create_checksum_index(words_store, language->get_unique_prefix_length())]); + words += words_store[create_checksum_index(words_store, language->get_unique_prefix_length())]; return true; } - bool bytes_to_words(const crypto::secret_key& src, std::string& words, + bool bytes_to_words(const crypto::secret_key& src, epee::wipeable_string& words, const std::string &language_name) { return bytes_to_words(src.data, sizeof(src), words, language_name); @@ -473,11 +478,10 @@ namespace crypto * \param seed The seed to check (a space delimited concatenated word list) * \return true if the seed passed is a old style seed false if not. */ - bool get_is_old_style_seed(std::string seed) + bool get_is_old_style_seed(const epee::wipeable_string &seed) { - std::vector word_list; - boost::algorithm::trim(seed); - boost::split(word_list, seed, boost::is_any_of(" "), boost::token_compress_on); + std::vector word_list; + seed.split(word_list); return word_list.size() != (seed_length + 1); } diff --git a/src/mnemonics/electrum-words.h b/src/mnemonics/electrum-words.h index 856edb92a41..5401b977993 100644 --- a/src/mnemonics/electrum-words.h +++ b/src/mnemonics/electrum-words.h @@ -44,6 +44,8 @@ #include #include "crypto/crypto.h" // for declaration of crypto::secret_key +namespace epee { class wipeable_string; } + /*! * \namespace crypto * @@ -70,7 +72,7 @@ namespace crypto * \param language_name Language of the seed as found gets written here. * \return false if not a multiple of 3 words, or if word is not in the words list */ - bool words_to_bytes(std::string words, std::string& dst, size_t len, bool duplicate, + bool words_to_bytes(const epee::wipeable_string &words, epee::wipeable_string& dst, size_t len, bool duplicate, std::string &language_name); /*! * \brief Converts seed words to bytes (secret key). @@ -79,7 +81,7 @@ namespace crypto * \param language_name Language of the seed as found gets written here. * \return false if not a multiple of 3 words, or if word is not in the words list */ - bool words_to_bytes(std::string words, crypto::secret_key& dst, + bool words_to_bytes(const epee::wipeable_string &words, crypto::secret_key& dst, std::string &language_name); /*! @@ -90,7 +92,7 @@ namespace crypto * \param language_name Seed language name * \return true if successful false if not. Unsuccessful if wrong key size. */ - bool bytes_to_words(const char *src, size_t len, std::string& words, + bool bytes_to_words(const char *src, size_t len, epee::wipeable_string& words, const std::string &language_name); /*! @@ -100,7 +102,7 @@ namespace crypto * \param language_name Seed language name * \return true if successful false if not. Unsuccessful if wrong key size. */ - bool bytes_to_words(const crypto::secret_key& src, std::string& words, + bool bytes_to_words(const crypto::secret_key& src, epee::wipeable_string& words, const std::string &language_name); /*! @@ -115,7 +117,7 @@ namespace crypto * \param seed The seed to check (a space delimited concatenated word list) * \return true if the seed passed is a old style seed false if not. */ - bool get_is_old_style_seed(std::string seed); + bool get_is_old_style_seed(const epee::wipeable_string &seed); /*! * \brief Returns the name of a language in English diff --git a/src/mnemonics/language_base.h b/src/mnemonics/language_base.h index 2b0c37c6bc1..cf518ab2a62 100644 --- a/src/mnemonics/language_base.h +++ b/src/mnemonics/language_base.h @@ -53,15 +53,20 @@ namespace Language * \param count How many characters to return. * \return A string consisting of the first count characters in s. */ - inline std::string utf8prefix(const std::string &s, size_t count) + template + inline T utf8prefix(const T &s, size_t count) { - std::string prefix = ""; - const char *ptr = s.c_str(); - while (count-- && *ptr) + T prefix = ""; + size_t avail = s.size(); + const char *ptr = s.data(); + while (count-- && avail--) { prefix += *ptr++; - while (((*ptr) & 0xc0) == 0x80) + while (avail && ((*ptr) & 0xc0) == 0x80) + { prefix += *ptr++; + --avail; + } } return prefix; } @@ -79,8 +84,8 @@ namespace Language ALLOW_DUPLICATE_PREFIXES = 1<<1, }; const std::vector word_list; /*!< A pointer to the array of words */ - std::unordered_map word_map; /*!< hash table to find word's index */ - std::unordered_map trimmed_word_map; /*!< hash table to find word's trimmed index */ + std::unordered_map word_map; /*!< hash table to find word's index */ + std::unordered_map trimmed_word_map; /*!< hash table to find word's trimmed index */ std::string language_name; /*!< Name of language */ std::string english_language_name; /*!< Name of language */ uint32_t unique_prefix_length; /*!< Number of unique starting characters to trim the wordlist to when matching */ @@ -103,7 +108,7 @@ namespace Language else throw std::runtime_error("Too short word in " + language_name + " word list: " + *it); } - std::string trimmed; + epee::wipeable_string trimmed; if (it->length() > unique_prefix_length) { trimmed = utf8prefix(*it, unique_prefix_length); @@ -115,9 +120,9 @@ namespace Language if (trimmed_word_map.find(trimmed) != trimmed_word_map.end()) { if (flags & ALLOW_DUPLICATE_PREFIXES) - MWARNING("Duplicate prefix in " << language_name << " word list: " << trimmed); + MWARNING("Duplicate prefix in " << language_name << " word list: " << std::string(trimmed.data(), trimmed.size())); else - throw std::runtime_error("Duplicate prefix in " + language_name + " word list: " + trimmed); + throw std::runtime_error("Duplicate prefix in " + language_name + " word list: " + std::string(trimmed.data(), trimmed.size())); } trimmed_word_map[trimmed] = ii; } @@ -145,7 +150,7 @@ namespace Language * \brief Returns a pointer to the word map. * \return A pointer to the word map. */ - const std::unordered_map& get_word_map() const + const std::unordered_map& get_word_map() const { return word_map; } @@ -153,7 +158,7 @@ namespace Language * \brief Returns a pointer to the trimmed word map. * \return A pointer to the trimmed word map. */ - const std::unordered_map& get_trimmed_word_map() const + const std::unordered_map& get_trimmed_word_map() const { return trimmed_word_map; } diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index e07c7e49ba1..f87c2936d12 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -152,12 +152,31 @@ namespace // // Note also that input for passwords is NOT translated, to remain compatible with any // passwords containing special characters that predate this switch to UTF-8 support. - static std::string cp850_to_utf8(const std::string &cp850_str) + template + static T cp850_to_utf8(const T &cp850_str) { boost::locale::generator gen; gen.locale_cache_enabled(true); std::locale loc = gen("en_US.CP850"); - return boost::locale::conv::to_utf(cp850_str, loc); + const boost::locale::conv::method_type how = boost::locale::conv::default_method; + T result; + const char *begin = cp850_str.data(); + const char *end = begin + cp850_str.size(); + result.reserve(end-begin); + typedef std::back_insert_iterator inserter_type; + inserter_type inserter(result); + boost::locale::utf::code_point c; + while(begin!=end) { + c=boost::locale::utf::utf_traits::template decode(begin,end); + if(c==boost::locale::utf::illegal || c==boost::locale::utf::incomplete) { + if(how==boost::locale::conv::stop) + throw boost::locale::conv::conversion_error(); + } + else { + boost::locale::utf::utf_traits::template encode(c,inserter); + } + } + return result; } #endif @@ -177,6 +196,28 @@ namespace return epee::string_tools::trim(buf); } + epee::wipeable_string input_secure_line(const std::string& prompt) + { +#ifdef HAVE_READLINE + rdln::suspend_readline pause_readline; +#endif + auto pwd_container = tools::password_container::prompt(false, prompt.c_str(), false); + if (!pwd_container) + { + MERROR("Failed to read secure line"); + return ""; + } + + epee::wipeable_string buf = pwd_container->password(); + +#ifdef WIN32 + buf = cp850_to_utf8(buf); +#endif + + buf.trim(); + return buf; + } + boost::optional password_prompter(const char *prompt, bool verify) { #ifdef HAVE_READLINE @@ -595,7 +636,7 @@ bool simple_wallet::spendkey(const std::vector &args/* = std::vecto bool simple_wallet::print_seed(bool encrypted) { bool success = false; - std::string seed; + epee::wipeable_string seed; bool ready, multisig; if (m_wallet->key_on_device()) @@ -2679,28 +2720,45 @@ bool simple_wallet::ask_wallet_create_if_needed() * \brief Prints the seed with a nice message * \param seed seed to print */ -void simple_wallet::print_seed(std::string seed) +void simple_wallet::print_seed(const epee::wipeable_string &seed) { success_msg_writer(true) << "\n" << tr("NOTE: the following 25 words can be used to recover access to your wallet. " "Write them down and store them somewhere safe and secure. Please do not store them in " "your email or on file storage services outside of your immediate control.\n"); - boost::replace_nth(seed, " ", 15, "\n"); - boost::replace_nth(seed, " ", 7, "\n"); // don't log - std::cout << seed << std::endl; + int space_index = 0; + size_t len = seed.size(); + for (const char *ptr = seed.data(); len--; ++ptr) + { + if (*ptr == ' ') + { + if (space_index == 15 || space_index == 7) + putchar('\n'); + else + putchar(*ptr); + ++space_index; + } + else + putchar(*ptr); + } + putchar('\n'); + fflush(stdout); } //---------------------------------------------------------------------------------------------------- -static bool might_be_partial_seed(std::string words) +static bool might_be_partial_seed(const epee::wipeable_string &words) { - std::vector seed; + std::vector seed; - boost::algorithm::trim(words); - boost::split(seed, words, boost::is_any_of(" "), boost::token_compress_on); + words.split(seed); return seed.size() < 24; } //---------------------------------------------------------------------------------------------------- bool simple_wallet::init(const boost::program_options::variables_map& vm) { + epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){ + m_electrum_seed.wipe(); + }); + const bool testnet = tools::wallet2::has_testnet_option(vm); const bool stagenet = tools::wallet2::has_stagenet_option(vm); if (testnet && stagenet) @@ -2710,7 +2768,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) } const network_type nettype = testnet ? TESTNET : stagenet ? STAGENET : MAINNET; - std::string multisig_keys; + epee::wipeable_string multisig_keys; if (!handle_command_line(vm)) return false; @@ -2752,8 +2810,8 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) { if (m_restore_multisig_wallet) { - const char *prompt = "Specify multisig seed: "; - m_electrum_seed = input_line(prompt); + const char *prompt = "Specify multisig seed"; + m_electrum_seed = input_secure_line(prompt); if (std::cin.eof()) return false; if (m_electrum_seed.empty()) @@ -2767,8 +2825,8 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) m_electrum_seed = ""; do { - const char *prompt = m_electrum_seed.empty() ? "Specify Electrum seed: " : "Electrum seed continued: "; - std::string electrum_seed = input_line(prompt); + const char *prompt = m_electrum_seed.empty() ? "Specify Electrum seed" : "Electrum seed continued"; + epee::wipeable_string electrum_seed = input_secure_line(prompt); if (std::cin.eof()) return false; if (electrum_seed.empty()) @@ -2776,18 +2834,21 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) fail_msg_writer() << tr("specify a recovery parameter with the --electrum-seed=\"words list here\""); return false; } - m_electrum_seed += electrum_seed + " "; + m_electrum_seed += electrum_seed; + m_electrum_seed += ' '; } while (might_be_partial_seed(m_electrum_seed)); } } if (m_restore_multisig_wallet) { - if (!epee::string_tools::parse_hexstr_to_binbuff(m_electrum_seed, multisig_keys)) + const boost::optional parsed = m_electrum_seed.parse_hexstr(); + if (!parsed) { fail_msg_writer() << tr("Multisig seed failed verification"); return false; } + multisig_keys = *parsed; } else { @@ -2809,7 +2870,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) crypto::secret_key key; crypto::cn_slow_hash(seed_pass.data(), seed_pass.size(), (crypto::hash&)key); sc_reduce32((unsigned char*)key.data); - multisig_keys = m_wallet->decrypt(multisig_keys, key, true); + multisig_keys = m_wallet->decrypt(std::string(multisig_keys.data(), multisig_keys.size()), key, true); } else m_recovery_key = cryptonote::decrypt_key(m_recovery_key, seed_pass); @@ -3478,7 +3539,7 @@ boost::optional simple_wallet::new_wallet(const boost::pr } // convert rng value to electrum-style word list - std::string electrum_words; + epee::wipeable_string electrum_words; crypto::ElectrumWords::bytes_to_words(recovery_val, electrum_words, mnemonic_language); @@ -3586,7 +3647,7 @@ boost::optional simple_wallet::new_wallet(const boost::pr } //---------------------------------------------------------------------------------------------------- boost::optional simple_wallet::new_wallet(const boost::program_options::variables_map& vm, - const std::string &multisig_keys, const std::string &old_language) + const epee::wipeable_string &multisig_keys, const std::string &old_language) { auto rc = tools::wallet2::make_new(vm, password_prompter); m_wallet = std::move(rc.first); @@ -3697,7 +3758,7 @@ bool simple_wallet::open_wallet(const boost::program_options::variables_map& vm) m_wallet->rewrite(m_wallet_file, password); // Display the seed - std::string seed; + epee::wipeable_string seed; m_wallet->get_seed(seed); print_seed(seed); } diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index 472ad0c0033..c8e17ea6e55 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -44,6 +44,7 @@ #include "cryptonote_basic/cryptonote_basic_impl.h" #include "wallet/wallet2.h" #include "console_handler.h" +#include "wipeable_string.h" #include "common/i18n.h" #include "common/password.h" #include "crypto/crypto.h" // for definition of crypto::secret_key @@ -96,7 +97,7 @@ namespace cryptonote boost::optional new_wallet(const boost::program_options::variables_map& vm, const cryptonote::account_public_address& address, const boost::optional& spendkey, const crypto::secret_key& viewkey); boost::optional new_wallet(const boost::program_options::variables_map& vm, - const std::string &multisig_keys, const std::string &old_language); + const epee::wipeable_string &multisig_keys, const std::string &old_language); boost::optional new_wallet(const boost::program_options::variables_map& vm, const std::string& device_name); bool open_wallet(const boost::program_options::variables_map& vm); bool close_wallet(); @@ -238,7 +239,7 @@ namespace cryptonote * \brief Prints the seed with a nice message * \param seed seed to print */ - void print_seed(std::string seed); + void print_seed(const epee::wipeable_string &seed); /*! * \brief Gets the word seed language from the user. @@ -329,7 +330,7 @@ namespace cryptonote std::string m_import_path; std::string m_subaddress_lookahead; - std::string m_electrum_seed; // electrum-style seed parameter + epee::wipeable_string m_electrum_seed; // electrum-style seed parameter crypto::secret_key m_recovery_key; // recovery key (used as random for wallet gen) bool m_restore_deterministic_wallet; // recover flag diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index b48bf07e0d1..e75bee5c919 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -733,10 +733,10 @@ bool WalletImpl::close(bool store) std::string WalletImpl::seed() const { - std::string seed; + epee::wipeable_string seed; if (m_wallet) m_wallet->get_seed(seed); - return seed; + return std::string(seed.data(), seed.size()); // TODO } std::string WalletImpl::getSeedLanguage() const diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 9deaad09b27..e61e437c05e 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -789,7 +789,7 @@ bool wallet2::is_deterministic() const return keys_deterministic; } //---------------------------------------------------------------------------------------------------- -bool wallet2::get_seed(std::string& electrum_words, const epee::wipeable_string &passphrase) const +bool wallet2::get_seed(epee::wipeable_string& electrum_words, const epee::wipeable_string &passphrase) const { bool keys_deterministic = is_deterministic(); if (!keys_deterministic) @@ -815,7 +815,7 @@ bool wallet2::get_seed(std::string& electrum_words, const epee::wipeable_string return true; } //---------------------------------------------------------------------------------------------------- -bool wallet2::get_multisig_seed(std::string& seed, const epee::wipeable_string &passphrase, bool raw) const +bool wallet2::get_multisig_seed(epee::wipeable_string& seed, const epee::wipeable_string &passphrase, bool raw) const { bool ready; uint32_t threshold, total; @@ -838,7 +838,7 @@ bool wallet2::get_multisig_seed(std::string& seed, const epee::wipeable_string & crypto::secret_key skey; crypto::public_key pkey; const account_keys &keys = get_account().get_keys(); - std::string data; + epee::wipeable_string data; data.append((const char*)&threshold, sizeof(uint32_t)); data.append((const char*)&total, sizeof(uint32_t)); skey = keys.m_spend_secret_key; @@ -864,7 +864,7 @@ bool wallet2::get_multisig_seed(std::string& seed, const epee::wipeable_string & if (raw) { - seed = epee::string_tools::buff_to_hex_nodelimer(data); + seed = epee::to_hex::wipeable_string({(const unsigned char*)data.data(), data.size()}); } else { @@ -3161,7 +3161,7 @@ bool wallet2::verify_password(const std::string& keys_file_name, const epee::wip * \param create_address_file Whether to create an address file */ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string& password, - const std::string& multisig_data, bool create_address_file) + const epee::wipeable_string& multisig_data, bool create_address_file) { clear(); prepare_file_names(wallet_); @@ -10671,14 +10671,14 @@ size_t wallet2::import_multisig(std::vector blobs) return n_outputs; } //---------------------------------------------------------------------------------------------------- -std::string wallet2::encrypt(const std::string &plaintext, const crypto::secret_key &skey, bool authenticated) const +std::string wallet2::encrypt(const char *plaintext, size_t len, const crypto::secret_key &skey, bool authenticated) const { crypto::chacha_key key; crypto::generate_chacha_key(&skey, sizeof(skey), key, m_kdf_rounds); std::string ciphertext; crypto::chacha_iv iv = crypto::rand(); - ciphertext.resize(plaintext.size() + sizeof(iv) + (authenticated ? sizeof(crypto::signature) : 0)); - crypto::chacha20(plaintext.data(), plaintext.size(), key, iv, &ciphertext[sizeof(iv)]); + ciphertext.resize(len + sizeof(iv) + (authenticated ? sizeof(crypto::signature) : 0)); + crypto::chacha20(plaintext, len, key, iv, &ciphertext[sizeof(iv)]); memcpy(&ciphertext[0], &iv, sizeof(iv)); if (authenticated) { @@ -10692,12 +10692,28 @@ std::string wallet2::encrypt(const std::string &plaintext, const crypto::secret_ return ciphertext; } //---------------------------------------------------------------------------------------------------- +std::string wallet2::encrypt(const epee::span &plaintext, const crypto::secret_key &skey, bool authenticated) const +{ + return encrypt(plaintext.data(), plaintext.size(), skey, authenticated); +} +//---------------------------------------------------------------------------------------------------- +std::string wallet2::encrypt(const std::string &plaintext, const crypto::secret_key &skey, bool authenticated) const +{ + return encrypt(plaintext.data(), plaintext.size(), skey, authenticated); +} +//---------------------------------------------------------------------------------------------------- +std::string wallet2::encrypt(const epee::wipeable_string &plaintext, const crypto::secret_key &skey, bool authenticated) const +{ + return encrypt(plaintext.data(), plaintext.size(), skey, authenticated); +} +//---------------------------------------------------------------------------------------------------- std::string wallet2::encrypt_with_view_secret_key(const std::string &plaintext, bool authenticated) const { return encrypt(plaintext, get_account().get_keys().m_view_secret_key, authenticated); } //---------------------------------------------------------------------------------------------------- -std::string wallet2::decrypt(const std::string &ciphertext, const crypto::secret_key &skey, bool authenticated) const +template +T wallet2::decrypt(const std::string &ciphertext, const crypto::secret_key &skey, bool authenticated) const { const size_t prefix_size = sizeof(chacha_iv) + (authenticated ? sizeof(crypto::signature) : 0); THROW_WALLET_EXCEPTION_IF(ciphertext.size() < prefix_size, @@ -10706,8 +10722,6 @@ std::string wallet2::decrypt(const std::string &ciphertext, const crypto::secret crypto::chacha_key key; crypto::generate_chacha_key(&skey, sizeof(skey), key, m_kdf_rounds); const crypto::chacha_iv &iv = *(const crypto::chacha_iv*)&ciphertext[0]; - std::string plaintext; - plaintext.resize(ciphertext.size() - prefix_size); if (authenticated) { crypto::hash hash; @@ -10718,10 +10732,14 @@ std::string wallet2::decrypt(const std::string &ciphertext, const crypto::secret THROW_WALLET_EXCEPTION_IF(!crypto::check_signature(hash, pkey, signature), error::wallet_internal_error, "Failed to authenticate ciphertext"); } - crypto::chacha20(ciphertext.data() + sizeof(iv), ciphertext.size() - prefix_size, key, iv, &plaintext[0]); - return plaintext; + std::unique_ptr buffer{new char[ciphertext.size() - prefix_size]}; + auto wiper = epee::misc_utils::create_scope_leave_handler([&]() { memwipe(buffer.get(), ciphertext.size() - prefix_size); }); + crypto::chacha20(ciphertext.data() + sizeof(iv), ciphertext.size() - prefix_size, key, iv, buffer.get()); + return T(buffer.get(), ciphertext.size() - prefix_size); } //---------------------------------------------------------------------------------------------------- +template epee::wipeable_string wallet2::decrypt(const std::string &ciphertext, const crypto::secret_key &skey, bool authenticated) const; +//---------------------------------------------------------------------------------------------------- std::string wallet2::decrypt_with_view_secret_key(const std::string &ciphertext, bool authenticated) const { return decrypt(ciphertext, get_account().get_keys().m_view_secret_key, authenticated); diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index d04156461f5..86fee0da73b 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -495,7 +495,7 @@ namespace tools * \param create_address_file Whether to create an address file */ void generate(const std::string& wallet_, const epee::wipeable_string& password, - const std::string& multisig_data, bool create_address_file = false); + const epee::wipeable_string& multisig_data, bool create_address_file = false); /*! * \brief Generates a wallet or restores one. @@ -637,7 +637,7 @@ namespace tools * \brief Checks if deterministic wallet */ bool is_deterministic() const; - bool get_seed(std::string& electrum_words, const epee::wipeable_string &passphrase = epee::wipeable_string()) const; + bool get_seed(epee::wipeable_string& electrum_words, const epee::wipeable_string &passphrase = epee::wipeable_string()) const; /*! * \brief Checks if light wallet. A light wallet sends view key to a server where the blockchain is scanned. @@ -691,7 +691,7 @@ namespace tools bool multisig(bool *ready = NULL, uint32_t *threshold = NULL, uint32_t *total = NULL) const; bool has_multisig_partial_key_images() const; bool has_unknown_key_images() const; - bool get_multisig_seed(std::string& seed, const epee::wipeable_string &passphrase = std::string(), bool raw = true) const; + bool get_multisig_seed(epee::wipeable_string& seed, const epee::wipeable_string &passphrase = std::string(), bool raw = true) const; bool key_on_device() const { return m_key_on_device; } // locked & unlocked balance of given or current subaddress account @@ -1054,9 +1054,12 @@ namespace tools void update_pool_state(bool refreshed = false); void remove_obsolete_pool_txs(const std::vector &tx_hashes); + std::string encrypt(const char *plaintext, size_t len, const crypto::secret_key &skey, bool authenticated = true) const; + std::string encrypt(const epee::span &span, const crypto::secret_key &skey, bool authenticated = true) const; std::string encrypt(const std::string &plaintext, const crypto::secret_key &skey, bool authenticated = true) const; + std::string encrypt(const epee::wipeable_string &plaintext, const crypto::secret_key &skey, bool authenticated = true) const; std::string encrypt_with_view_secret_key(const std::string &plaintext, bool authenticated = true) const; - std::string decrypt(const std::string &ciphertext, const crypto::secret_key &skey, bool authenticated = true) const; + template T decrypt(const std::string &ciphertext, const crypto::secret_key &skey, bool authenticated = true) const; std::string decrypt_with_view_secret_key(const std::string &ciphertext, bool authenticated = true) const; std::string make_uri(const std::string &address, const std::string &payment_id, uint64_t amount, const std::string &tx_description, const std::string &recipient_name, std::string &error) const; diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index 510cb3e5813..136cd0e2dc0 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -1574,11 +1574,13 @@ namespace tools if (req.key_type.compare("mnemonic") == 0) { - if (!m_wallet->get_seed(res.key)) + epee::wipeable_string seed; + if (!m_wallet->get_seed(seed)) { er.message = "The wallet is non-deterministic. Cannot display seed."; return false; } + res.key = std::string(seed.data(), seed.size()); // send to the network, then wipe RAM :D } else if(req.key_type.compare("view_key") == 0) { diff --git a/tests/unit_tests/CMakeLists.txt b/tests/unit_tests/CMakeLists.txt index 3c7414640ba..f47192ea918 100644 --- a/tests/unit_tests/CMakeLists.txt +++ b/tests/unit_tests/CMakeLists.txt @@ -72,7 +72,8 @@ set(unit_tests_sources ringct.cpp output_selection.cpp vercmp.cpp - ringdb.cpp) + ringdb.cpp + wipeable_string.cpp) set(unit_tests_headers unit_tests_utils.h) diff --git a/tests/unit_tests/mnemonics.cpp b/tests/unit_tests/mnemonics.cpp index 8fa3192b9bc..0b74a6b94aa 100644 --- a/tests/unit_tests/mnemonics.cpp +++ b/tests/unit_tests/mnemonics.cpp @@ -27,6 +27,8 @@ // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "gtest/gtest.h" +#include "wipeable_string.h" +#include "mnemonics/language_base.h" #include "mnemonics/electrum-words.h" #include "crypto/crypto.h" #include @@ -74,14 +76,16 @@ namespace void test_language(const Language::Base &language) { const std::vector &word_list = language.get_word_list(); - std::string seed = "", return_seed = ""; + epee::wipeable_string w_seed = "", w_return_seed = ""; + std::string seed, return_seed; // Generate a random seed without checksum crypto::secret_key randkey; for (size_t ii = 0; ii < sizeof(randkey); ++ii) { randkey.data[ii] = rand(); } - crypto::ElectrumWords::bytes_to_words(randkey, seed, language.get_language_name()); + crypto::ElectrumWords::bytes_to_words(randkey, w_seed, language.get_language_name()); + seed = std::string(w_seed.data(), w_seed.size()); // remove the checksum word const char *space = strrchr(seed.c_str(), ' '); ASSERT_TRUE(space != NULL); @@ -103,7 +107,8 @@ namespace ASSERT_STREQ(language.get_language_name().c_str(), language_name.c_str()); // Convert the secret key back to seed - crypto::ElectrumWords::bytes_to_words(key, return_seed, language.get_language_name()); + crypto::ElectrumWords::bytes_to_words(key, w_return_seed, language.get_language_name()); + return_seed = std::string(w_return_seed.data(), w_return_seed.size()); ASSERT_EQ(true, res); std::cout << "Returned seed:\n"; std::cout << return_seed << std::endl; @@ -126,8 +131,9 @@ namespace std::cout << "Detected language: " << language_name << std::endl; ASSERT_STREQ(language.get_language_name().c_str(), language_name.c_str()); - return_seed = ""; - crypto::ElectrumWords::bytes_to_words(key, return_seed, language.get_language_name()); + w_return_seed = ""; + crypto::ElectrumWords::bytes_to_words(key, w_return_seed, language.get_language_name()); + return_seed = std::string(w_return_seed.data(), w_return_seed.size()); ASSERT_EQ(true, res); std::cout << "Returned seed:\n"; std::cout << return_seed << std::endl; @@ -202,3 +208,17 @@ TEST(mnemonics, language_detection_with_bad_checksum) ASSERT_EQ(true, res); ASSERT_STREQ(language_name.c_str(), "Português"); } + +TEST(mnemonics, utf8prefix) +{ + ASSERT_TRUE(Language::utf8prefix(epee::wipeable_string("foo"), 0) == ""); + ASSERT_TRUE(Language::utf8prefix(epee::wipeable_string("foo"), 1) == "f"); + ASSERT_TRUE(Language::utf8prefix(epee::wipeable_string("foo"), 2) == "fo"); + ASSERT_TRUE(Language::utf8prefix(epee::wipeable_string("foo"), 3) == "foo"); + ASSERT_TRUE(Language::utf8prefix(epee::wipeable_string("foo"), 4) == "foo"); + ASSERT_TRUE(Language::utf8prefix(epee::wipeable_string("æon"), 0) == ""); + ASSERT_TRUE(Language::utf8prefix(epee::wipeable_string("æon"), 1) == "æ"); + ASSERT_TRUE(Language::utf8prefix(epee::wipeable_string("æon"), 2) == "æo"); + ASSERT_TRUE(Language::utf8prefix(epee::wipeable_string("æon"), 3) == "æon"); + ASSERT_TRUE(Language::utf8prefix(epee::wipeable_string("æon"), 4) == "æon"); +} diff --git a/tests/unit_tests/wipeable_string.cpp b/tests/unit_tests/wipeable_string.cpp new file mode 100644 index 00000000000..5ea1c1729b2 --- /dev/null +++ b/tests/unit_tests/wipeable_string.cpp @@ -0,0 +1,204 @@ +// Copyright (c) 2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include +#include +#include "gtest/gtest.h" + +#include "misc_log_ex.h" +#include "wipeable_string.h" + +TEST(wipeable_string, ctor) +{ + epee::wipeable_string s0; + ASSERT_EQ(s0.size(), 0); + + epee::wipeable_string s1(std::string("foo")); + ASSERT_EQ(s1.size(), 3); + ASSERT_TRUE(!memcmp(s1.data(), "foo", s1.size())); + + epee::wipeable_string s2(std::string("bar")); + ASSERT_EQ(s2.size(), 3); + ASSERT_TRUE(!memcmp(s2.data(), "bar", s2.size())); + + epee::wipeable_string s3(std::string("quux")); + ASSERT_EQ(s3.size(), 4); + ASSERT_TRUE(!memcmp(s3.data(), "quux", s3.size())); +} + +TEST(wipeable_string, wipe) +{ + epee::wipeable_string s0(std::string("foo")); + ASSERT_EQ(s0.size(), 3); + s0.wipe(); + ASSERT_EQ(s0.size(), 3); + ASSERT_TRUE(!memcmp(s0.data(), "\0\0\0", 3)); +} + +TEST(wipeable_string, clear) +{ + epee::wipeable_string s0(std::string("foo")); + ASSERT_EQ(s0.size(), 3); + s0.clear(); + ASSERT_EQ(s0.size(), 0); +} + +TEST(wipeable_string, push_back) +{ + epee::wipeable_string s0(std::string("fo")); + ASSERT_EQ(s0.size(), 2); + s0.push_back('o'); + ASSERT_EQ(s0.size(), 3); + ASSERT_TRUE(!memcmp(s0.data(), "foo", s0.size())); +} + +TEST(wipeable_string, append_char) +{ + epee::wipeable_string s0(std::string("fo")); + ASSERT_EQ(s0.size(), 2); + s0 += 'o'; + ASSERT_EQ(s0.size(), 3); + ASSERT_TRUE(!memcmp(s0.data(), "foo", s0.size())); +} + +TEST(wipeable_string, append_string) +{ + epee::wipeable_string s0(std::string("foo")); + ASSERT_EQ(s0.size(), 3); + s0 += "bar"; + ASSERT_EQ(s0.size(), 6); + ASSERT_TRUE(!memcmp(s0.data(), "foobar", s0.size())); +} + +TEST(wipeable_string, empty) +{ + epee::wipeable_string s0; + ASSERT_TRUE(s0.empty()); + s0.push_back(' '); + ASSERT_FALSE(s0.empty()); + ASSERT_EQ(s0.pop_back(), ' '); + ASSERT_TRUE(s0.empty()); +} + +TEST(wipeable_string, pop_back) +{ + epee::wipeable_string s = "test"; + ASSERT_EQ(s.size(), 4); + ASSERT_EQ(s.pop_back(), 't'); + ASSERT_EQ(s.size(), 3); + ASSERT_TRUE(!memcmp(s.data(), "tes", s.size())); +} + +TEST(wipeable_string, equal) +{ + epee::wipeable_string s0 = "foo"; + epee::wipeable_string s1 = "bar"; + epee::wipeable_string s0_2 = "foo"; + ASSERT_TRUE(s0 == s0); + ASSERT_TRUE(s0 == s0_2); + ASSERT_TRUE(s1 == s1); + ASSERT_FALSE(s1 == s0); + ASSERT_FALSE(s1 == s0_2); +} + +TEST(wipeable_string, not_equal) +{ + epee::wipeable_string s0 = "foo"; + epee::wipeable_string s1 = "bar"; + epee::wipeable_string s0_2 = "foo"; + ASSERT_FALSE(s0 != s0); + ASSERT_FALSE(s0 != s0_2); + ASSERT_FALSE(s1 != s1); + ASSERT_TRUE(s1 != s0); + ASSERT_TRUE(s1 != s0_2); +} + +static epee::wipeable_string trimmed(const char *s) +{ + epee::wipeable_string str(s); + str.trim(); + return str; +} + +TEST(wipeable_string, trim) +{ + ASSERT_TRUE(trimmed("") == ""); + ASSERT_TRUE(trimmed(" ") == ""); + ASSERT_TRUE(trimmed(" ") == ""); + ASSERT_TRUE(trimmed("a") == "a"); + ASSERT_TRUE(trimmed(" a") == "a"); + ASSERT_TRUE(trimmed(" a") == "a"); + ASSERT_TRUE(trimmed("a ") == "a"); + ASSERT_TRUE(trimmed("a ") == "a"); + ASSERT_TRUE(trimmed(" a ") == "a"); + ASSERT_TRUE(trimmed(" a ") == "a"); + ASSERT_TRUE(trimmed(" ab ") == "ab"); + ASSERT_TRUE(trimmed(" a b ") == "a b"); + ASSERT_TRUE(trimmed(" a b ") == "a b"); +} + +static bool check_split(const char *s, const std::vector &v) +{ + epee::wipeable_string str(s); + std::vector fields; + str.split(fields); + return v == fields; +} + +TEST(wipeable_string, split) +{ + ASSERT_TRUE(check_split("", {})); + ASSERT_TRUE(check_split("foo", {"foo"})); + ASSERT_TRUE(check_split(" foo ", {"foo"})); + ASSERT_TRUE(check_split("foo bar", {"foo", "bar"})); + ASSERT_TRUE(check_split("foo bar", {"foo", "bar"})); + ASSERT_TRUE(check_split("foo bar baz", {"foo", "bar", "baz"})); + ASSERT_TRUE(check_split(" foo bar baz ", {"foo", "bar", "baz"})); + ASSERT_TRUE(check_split(" foo bar baz", {"foo", "bar", "baz"})); + ASSERT_TRUE(check_split("foo bar baz ", {"foo", "bar", "baz"})); +} + +TEST(wipeable_string, parse_hexstr) +{ + boost::optional s; + + ASSERT_EQ(boost::none, epee::wipeable_string("x").parse_hexstr()); + ASSERT_EQ(boost::none, epee::wipeable_string("x0000000000000000").parse_hexstr()); + ASSERT_EQ(boost::none, epee::wipeable_string("0000000000000000x").parse_hexstr()); + ASSERT_EQ(boost::none, epee::wipeable_string("0").parse_hexstr()); + ASSERT_EQ(boost::none, epee::wipeable_string("000").parse_hexstr()); + + ASSERT_TRUE((s = epee::wipeable_string("").parse_hexstr())); + ASSERT_EQ(*s, ""); + ASSERT_TRUE((s = epee::wipeable_string("00").parse_hexstr())); + ASSERT_EQ(*s, epee::wipeable_string("", 1)); + ASSERT_TRUE((s = epee::wipeable_string("41").parse_hexstr())); + ASSERT_EQ(*s, epee::wipeable_string("A")); + ASSERT_TRUE((s = epee::wipeable_string("414243").parse_hexstr())); + ASSERT_EQ(*s, epee::wipeable_string("ABC")); +} From e9ffa91257b672009e8b8c84027378f3893a6d01 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 8 Jul 2018 21:12:33 +0100 Subject: [PATCH 0252/1404] store secret keys encrypted where possible The secret spend key is kept encrypted in memory, and decrypted on the fly when needed. Both spend and view secret keys are kept encrypted in a JSON field in the keys file. This avoids leaving the keys in memory due to being manipulated by the JSON I/O API. --- .../serialization/keyvalue_serialization.h | 9 + src/cryptonote_basic/account.cpp | 66 ++++ src/cryptonote_basic/account.h | 16 + src/gen_multisig/gen_multisig.cpp | 4 +- src/simplewallet/simplewallet.cpp | 237 ++++++++----- src/simplewallet/simplewallet.h | 1 + src/wallet/api/wallet.cpp | 3 +- src/wallet/wallet2.cpp | 312 +++++++++++++++--- src/wallet/wallet2.h | 57 +++- src/wallet/wallet_errors.h | 9 + src/wallet/wallet_rpc_server.cpp | 10 +- .../transactions_flow_test.cpp | 4 +- tests/unit_tests/CMakeLists.txt | 1 + tests/unit_tests/account.cpp | 71 ++++ tests/unit_tests/multisig.cpp | 15 +- tests/unit_tests/ringdb.cpp | 2 +- 16 files changed, 659 insertions(+), 158 deletions(-) create mode 100644 tests/unit_tests/account.cpp diff --git a/contrib/epee/include/serialization/keyvalue_serialization.h b/contrib/epee/include/serialization/keyvalue_serialization.h index 5791e199828..fc5a21851cd 100644 --- a/contrib/epee/include/serialization/keyvalue_serialization.h +++ b/contrib/epee/include/serialization/keyvalue_serialization.h @@ -85,6 +85,14 @@ public: \ static_assert(std::is_pod::value, "t_type must be a POD type."); \ KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE_N(varialble, val_name) +#define KV_SERIALIZE_VAL_POD_AS_BLOB_OPT_N(varialble, val_name, default_value) \ + do { \ + static_assert(std::is_pod::value, "t_type must be a POD type."); \ + bool ret = KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE_N(varialble, val_name); \ + if (!ret) \ + epee::serialize_default(this_ref.varialble, default_value); \ + } while(0); + #define KV_SERIALIZE_CONTAINER_POD_AS_BLOB_N(varialble, val_name) \ epee::serialization::selector::serialize_stl_container_pod_val_as_blob(this_ref.varialble, stg, hparent_section, val_name); @@ -92,6 +100,7 @@ public: \ #define KV_SERIALIZE(varialble) KV_SERIALIZE_N(varialble, #varialble) #define KV_SERIALIZE_VAL_POD_AS_BLOB(varialble) KV_SERIALIZE_VAL_POD_AS_BLOB_N(varialble, #varialble) +#define KV_SERIALIZE_VAL_POD_AS_BLOB_OPT(varialble, def) KV_SERIALIZE_VAL_POD_AS_BLOB_OPT_N(varialble, #varialble, def) #define KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(varialble) KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE_N(varialble, #varialble) //skip is_pod compile time check #define KV_SERIALIZE_CONTAINER_POD_AS_BLOB(varialble) KV_SERIALIZE_CONTAINER_POD_AS_BLOB_N(varialble, #varialble) #define KV_SERIALIZE_OPT(variable,default_value) KV_SERIALIZE_OPT_N(variable, #variable, default_value) diff --git a/src/cryptonote_basic/account.cpp b/src/cryptonote_basic/account.cpp index aac6ec22b6e..0aebf92232b 100644 --- a/src/cryptonote_basic/account.cpp +++ b/src/cryptonote_basic/account.cpp @@ -44,6 +44,9 @@ extern "C" #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "account" +#define KEYS_ENCRYPTION_SALT 'k' + + using namespace std; DISABLE_VS_WARNINGS(4244 4345) @@ -60,7 +63,70 @@ DISABLE_VS_WARNINGS(4244 4345) m_device = &hwdev; MCDEBUG("device", "account_keys::set_device device type: "< data; + memcpy(data.data(), &base_key, sizeof(base_key)); + data[sizeof(base_key)] = KEYS_ENCRYPTION_SALT; + crypto::generate_chacha_key(data.data(), sizeof(data), key, 1); + } + //----------------------------------------------------------------- + static epee::wipeable_string get_key_stream(const crypto::chacha_key &base_key, const crypto::chacha_iv &iv, size_t bytes) + { + // derive a new key + crypto::chacha_key key; + derive_key(base_key, key); + // chacha + epee::wipeable_string buffer0(std::string(bytes, '\0')); + epee::wipeable_string buffer1 = buffer0; + crypto::chacha20(buffer0.data(), buffer0.size(), key, iv, buffer1.data()); + return buffer1; + } + //----------------------------------------------------------------- + void account_keys::xor_with_key_stream(const crypto::chacha_key &key) + { + // encrypt a large enough byte stream with chacha20 + epee::wipeable_string key_stream = get_key_stream(key, m_encryption_iv, sizeof(crypto::secret_key) * (2 + m_multisig_keys.size())); + const char *ptr = key_stream.data(); + for (size_t i = 0; i < sizeof(crypto::secret_key); ++i) + m_spend_secret_key.data[i] ^= *ptr++; + for (size_t i = 0; i < sizeof(crypto::secret_key); ++i) + m_view_secret_key.data[i] ^= *ptr++; + for (crypto::secret_key &k: m_multisig_keys) + { + for (size_t i = 0; i < sizeof(crypto::secret_key); ++i) + k.data[i] ^= *ptr++; + } + } + //----------------------------------------------------------------- + void account_keys::encrypt(const crypto::chacha_key &key) + { + m_encryption_iv = crypto::rand(); + xor_with_key_stream(key); + } + //----------------------------------------------------------------- + void account_keys::decrypt(const crypto::chacha_key &key) + { + xor_with_key_stream(key); + } + //----------------------------------------------------------------- + void account_keys::encrypt_viewkey(const crypto::chacha_key &key) + { + // encrypt a large enough byte stream with chacha20 + epee::wipeable_string key_stream = get_key_stream(key, m_encryption_iv, sizeof(crypto::secret_key) * 2); + const char *ptr = key_stream.data(); + ptr += sizeof(crypto::secret_key); + for (size_t i = 0; i < sizeof(crypto::secret_key); ++i) + m_view_secret_key.data[i] ^= *ptr++; + } + //----------------------------------------------------------------- + void account_keys::decrypt_viewkey(const crypto::chacha_key &key) + { + encrypt_viewkey(key); + } //----------------------------------------------------------------- account_base::account_base() { diff --git a/src/cryptonote_basic/account.h b/src/cryptonote_basic/account.h index b5d119c46de..dac66ff1a13 100644 --- a/src/cryptonote_basic/account.h +++ b/src/cryptonote_basic/account.h @@ -44,18 +44,29 @@ namespace cryptonote crypto::secret_key m_view_secret_key; std::vector m_multisig_keys; hw::device *m_device = &hw::get_device("default"); + crypto::chacha_iv m_encryption_iv; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(m_account_address) KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(m_spend_secret_key) KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(m_view_secret_key) KV_SERIALIZE_CONTAINER_POD_AS_BLOB(m_multisig_keys) + const crypto::chacha_iv default_iv{{0, 0, 0, 0, 0, 0, 0, 0}}; + KV_SERIALIZE_VAL_POD_AS_BLOB_OPT(m_encryption_iv, default_iv) END_KV_SERIALIZE_MAP() account_keys& operator=(account_keys const&) = default; + void encrypt(const crypto::chacha_key &key); + void decrypt(const crypto::chacha_key &key); + void encrypt_viewkey(const crypto::chacha_key &key); + void decrypt_viewkey(const crypto::chacha_key &key); + hw::device& get_device() const ; void set_device( hw::device &hwdev) ; + + private: + void xor_with_key_stream(const crypto::chacha_key &key); }; /************************************************************************/ @@ -87,6 +98,11 @@ namespace cryptonote void forget_spend_key(); const std::vector &get_multisig_keys() const { return m_keys.m_multisig_keys; } + void encrypt_keys(const crypto::chacha_key &key) { m_keys.encrypt(key); } + void decrypt_keys(const crypto::chacha_key &key) { m_keys.decrypt(key); } + void encrypt_viewkey(const crypto::chacha_key &key) { m_keys.encrypt_viewkey(key); } + void decrypt_viewkey(const crypto::chacha_key &key) { m_keys.decrypt_viewkey(key); } + template inline void serialize(t_archive &a, const unsigned int /*ver*/) { diff --git a/src/gen_multisig/gen_multisig.cpp b/src/gen_multisig/gen_multisig.cpp index 03e0a79464d..e680a8157b7 100644 --- a/src/gen_multisig/gen_multisig.cpp +++ b/src/gen_multisig/gen_multisig.cpp @@ -92,7 +92,7 @@ static bool generate_multisig(uint32_t threshold, uint32_t total, const std::str { std::string name = basename + "-" + std::to_string(n + 1); wallets[n].reset(new tools::wallet2(nettype)); - wallets[n]->init(""); + wallets[n]->init(false, ""); wallets[n]->generate(name, pwd_container->password(), rct::rct2sk(rct::skGen()), false, false, create_address_file); } @@ -101,11 +101,13 @@ static bool generate_multisig(uint32_t threshold, uint32_t total, const std::str std::vector pk(total); for (size_t n = 0; n < total; ++n) { + wallets[n]->decrypt_keys(pwd_container->password()); if (!tools::wallet2::verify_multisig_info(wallets[n]->get_multisig_info(), sk[n], pk[n])) { tools::fail_msg_writer() << tr("Failed to verify multisig info"); return false; } + wallets[n]->encrypt_keys(pwd_container->password()); } // make the wallets multisig diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index f87c2936d12..03860572586 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -106,6 +106,12 @@ typedef cryptonote::simple_wallet sw; m_auto_refresh_enabled.store(auto_refresh_enabled, std::memory_order_relaxed); \ }) +#define SCOPED_WALLET_UNLOCK() \ + LOCK_IDLE_SCOPE(); \ + boost::optional pwd_container = boost::none; \ + if (m_wallet->ask_password() && !m_wallet->watch_only() && !(pwd_container = get_and_verify_password())) { return true; } \ + tools::wallet_keys_unlocker unlocker(*m_wallet, pwd_container); + enum TransferType { TransferOriginal, TransferNew, @@ -553,6 +559,18 @@ namespace } return true; } + + void print_secret_key(const crypto::secret_key &k) + { + static constexpr const char hex[] = u8"0123456789abcdef"; + const uint8_t *ptr = (const uint8_t*)k.data; + for (size_t i = 0, sz = sizeof(k); i < sz; ++i) + { + putchar(hex[*ptr >> 4]); + putchar(hex[*ptr & 15]); + ++ptr; + } + } } bool parse_priority(const std::string& arg, uint32_t& priority) @@ -602,12 +620,15 @@ std::string simple_wallet::get_command_usage(const std::vector &arg bool simple_wallet::viewkey(const std::vector &args/* = std::vector()*/) { - if (m_wallet->ask_password() && !get_and_verify_password()) { return true; } + SCOPED_WALLET_UNLOCK(); // don't log + PAUSE_READLINE(); if (m_wallet->key_on_device()) { std::cout << "secret: On device. Not available" << std::endl; } else { - std::cout << "secret: " << string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_view_secret_key) << std::endl; + printf("secret: "); + print_secret_key(m_wallet->get_account().get_keys().m_view_secret_key); + putchar('\n'); } std::cout << "public: " << string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_account_address.m_view_public_key) << std::endl; @@ -621,12 +642,15 @@ bool simple_wallet::spendkey(const std::vector &args/* = std::vecto fail_msg_writer() << tr("wallet is watch-only and has no spend key"); return true; } - if (m_wallet->ask_password() && !get_and_verify_password()) { return true; } + SCOPED_WALLET_UNLOCK(); // don't log + PAUSE_READLINE(); if (m_wallet->key_on_device()) { std::cout << "secret: On device. Not available" << std::endl; } else { - std::cout << "secret: " << string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_spend_secret_key) << std::endl; + printf("secret: "); + print_secret_key(m_wallet->get_account().get_keys().m_spend_secret_key); + putchar('\n'); } std::cout << "public: " << string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_account_address.m_spend_public_key) << std::endl; @@ -649,7 +673,8 @@ bool simple_wallet::print_seed(bool encrypted) fail_msg_writer() << tr("wallet is watch-only and has no seed"); return true; } - if (m_wallet->ask_password() && !get_and_verify_password()) { return true; } + + SCOPED_WALLET_UNLOCK(); multisig = m_wallet->multisig(&ready); if (multisig) @@ -718,22 +743,36 @@ bool simple_wallet::seed_set_language(const std::vector &args/* = s fail_msg_writer() << tr("wallet is watch-only and has no seed"); return true; } - if (!m_wallet->is_deterministic()) - { - fail_msg_writer() << tr("wallet is non-deterministic and has no seed"); - return true; - } - - const auto pwd_container = get_and_verify_password(); - if (pwd_container) + + epee::wipeable_string password; { - std::string mnemonic_language = get_mnemonic_language(); - if (mnemonic_language.empty()) + SCOPED_WALLET_UNLOCK(); + + if (!m_wallet->is_deterministic()) + { + fail_msg_writer() << tr("wallet is non-deterministic and has no seed"); return true; + } - m_wallet->set_seed_language(std::move(mnemonic_language)); - m_wallet->rewrite(m_wallet_file, pwd_container->password()); + // we need the password, even if ask-password is unset + if (!pwd_container) + { + pwd_container = get_and_verify_password(); + if (pwd_container == boost::none) + { + fail_msg_writer() << tr("Incorrect password"); + return true; + } + } + password = pwd_container->password(); } + + std::string mnemonic_language = get_mnemonic_language(); + if (mnemonic_language.empty()) + return true; + + m_wallet->set_seed_language(std::move(mnemonic_language)); + m_wallet->rewrite(m_wallet_file, password); return true; } @@ -754,8 +793,7 @@ bool simple_wallet::change_password(const std::vector &args) try { - m_wallet->rewrite(m_wallet_file, pwd_container->password()); - m_wallet->store(); + m_wallet->change_password(m_wallet_file, orig_pwd_container->password(), pwd_container->password()); } catch (const tools::error::wallet_logic_error& e) { @@ -858,12 +896,7 @@ bool simple_wallet::prepare_multisig(const std::vector &args) return true; } - const auto orig_pwd_container = get_and_verify_password(); - if(orig_pwd_container == boost::none) - { - fail_msg_writer() << tr("Your password is incorrect."); - return true; - } + SCOPED_WALLET_UNLOCK(); std::string multisig_info = m_wallet->get_multisig_info(); success_msg_writer() << multisig_info; @@ -896,13 +929,6 @@ bool simple_wallet::make_multisig(const std::vector &args) return true; } - const auto orig_pwd_container = get_and_verify_password(); - if(orig_pwd_container == boost::none) - { - fail_msg_writer() << tr("Your original password was incorrect."); - return true; - } - if (args.size() < 2) { fail_msg_writer() << tr("usage: make_multisig [...]"); @@ -917,6 +943,13 @@ bool simple_wallet::make_multisig(const std::vector &args) return true; } + const auto orig_pwd_container = get_and_verify_password(); + if(orig_pwd_container == boost::none) + { + fail_msg_writer() << tr("Your original password was incorrect."); + return true; + } + LOCK_IDLE_SCOPE(); try @@ -958,6 +991,14 @@ bool simple_wallet::finalize_multisig(const std::vector &args) fail_msg_writer() << tr("command not supported by HW wallet"); return true; } + + const auto pwd_container = get_and_verify_password(); + if(pwd_container == boost::none) + { + fail_msg_writer() << tr("Your original password was incorrect."); + return true; + } + if (!m_wallet->multisig(&ready)) { fail_msg_writer() << tr("This wallet is not multisig"); @@ -969,12 +1010,7 @@ bool simple_wallet::finalize_multisig(const std::vector &args) return true; } - const auto orig_pwd_container = get_and_verify_password(); - if(orig_pwd_container == boost::none) - { - fail_msg_writer() << tr("Your original password was incorrect."); - return true; - } + LOCK_IDLE_SCOPE(); if (args.size() < 2) { @@ -984,7 +1020,7 @@ bool simple_wallet::finalize_multisig(const std::vector &args) try { - if (!m_wallet->finalize_multisig(orig_pwd_container->password(), args)) + if (!m_wallet->finalize_multisig(pwd_container->password(), args)) { fail_msg_writer() << tr("Failed to finalize multisig"); return true; @@ -1022,8 +1058,8 @@ bool simple_wallet::export_multisig(const std::vector &args) fail_msg_writer() << tr("usage: export_multisig_info "); return true; } - if (m_wallet->ask_password() && !get_and_verify_password()) - return true; + + SCOPED_WALLET_UNLOCK(); const std::string filename = args[0]; if (m_wallet->confirm_export_overwrite() && !check_file_overwrite(filename)) @@ -1074,8 +1110,8 @@ bool simple_wallet::import_multisig(const std::vector &args) fail_msg_writer() << tr("usage: import_multisig_info [...] - one for each other participant"); return true; } - if (m_wallet->ask_password() && !get_and_verify_password()) - return true; + + SCOPED_WALLET_UNLOCK(); std::vector info; for (size_t n = 0; n < args.size(); ++n) @@ -1091,11 +1127,11 @@ bool simple_wallet::import_multisig(const std::vector &args) info.push_back(std::move(data)); } - LOCK_IDLE_SCOPE(); - // all read and parsed, actually import try { + m_in_manual_refresh.store(true, std::memory_order_relaxed); + epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){m_in_manual_refresh.store(false, std::memory_order_relaxed);}); size_t n_outputs = m_wallet->import_multisig(info); // Clear line "Height xxx of xxx" std::cout << "\r \r"; @@ -1153,7 +1189,8 @@ bool simple_wallet::sign_multisig(const std::vector &args) fail_msg_writer() << tr("usage: sign_multisig "); return true; } - if (m_wallet->ask_password() && !get_and_verify_password()) { return true; } + + SCOPED_WALLET_UNLOCK(); std::string filename = args[0]; std::vector txids; @@ -1226,7 +1263,8 @@ bool simple_wallet::submit_multisig(const std::vector &args) fail_msg_writer() << tr("usage: submit_multisig "); return true; } - if (m_wallet->ask_password() && !get_and_verify_password()) { return true; } + + SCOPED_WALLET_UNLOCK(); if (!try_connect_to_daemon()) return true; @@ -1293,7 +1331,8 @@ bool simple_wallet::export_raw_multisig(const std::vector &args) fail_msg_writer() << tr("usage: export_raw_multisig "); return true; } - if (m_wallet->ask_password() && !get_and_verify_password()) { return true; } + + SCOPED_WALLET_UNLOCK(); std::string filename = args[0]; if (m_wallet->confirm_export_overwrite() && !check_file_overwrite(filename)) @@ -1915,6 +1954,14 @@ bool simple_wallet::set_ask_password(const std::vector &args/* = st if (pwd_container) { parse_bool_and_use(args[1], [&](bool r) { + const bool cur_r = m_wallet->ask_password(); + if (!m_wallet->watch_only()) + { + if (cur_r && !r) + m_wallet->decrypt_keys(pwd_container->password()); + else if (!cur_r && r) + m_wallet->encrypt_keys(pwd_container->password()); + } m_wallet->ask_password(r); m_wallet->rewrite(m_wallet_file, pwd_container->password()); }); @@ -3173,7 +3220,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) m_wallet_file = m_generate_from_json; try { - m_wallet = tools::wallet2::make_from_json(vm, m_wallet_file, password_prompter); + m_wallet = tools::wallet2::make_from_json(vm, false, m_wallet_file, password_prompter); } catch (const std::exception &e) { @@ -3475,7 +3522,7 @@ boost::optional simple_wallet::get_and_verify_passwor boost::optional simple_wallet::new_wallet(const boost::program_options::variables_map& vm, const crypto::secret_key& recovery_key, bool recover, bool two_random, const std::string &old_language) { - auto rc = tools::wallet2::make_new(vm, password_prompter); + auto rc = tools::wallet2::make_new(vm, false, password_prompter); m_wallet = std::move(rc.first); if (!m_wallet) { @@ -3530,7 +3577,10 @@ boost::optional simple_wallet::new_wallet(const boost::pr recovery_val = m_wallet->generate(m_wallet_file, std::move(rc.second).password(), recovery_key, recover, two_random, create_address_file); message_writer(console_color_white, true) << tr("Generated new wallet: ") << m_wallet->get_account().get_public_address_str(m_wallet->nettype()); - std::cout << tr("View key: ") << string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_view_secret_key) << ENDL; + PAUSE_READLINE(); + std::cout << tr("View key: "); + print_secret_key(m_wallet->get_account().get_keys().m_view_secret_key); + putchar('\n'); } catch (const std::exception& e) { @@ -3567,7 +3617,7 @@ boost::optional simple_wallet::new_wallet(const boost::pr const cryptonote::account_public_address& address, const boost::optional& spendkey, const crypto::secret_key& viewkey) { - auto rc = tools::wallet2::make_new(vm, password_prompter); + auto rc = tools::wallet2::make_new(vm, false, password_prompter); m_wallet = std::move(rc.first); if (!m_wallet) { @@ -3613,7 +3663,7 @@ boost::optional simple_wallet::new_wallet(const boost::pr //---------------------------------------------------------------------------------------------------- boost::optional simple_wallet::new_wallet(const boost::program_options::variables_map& vm, const std::string &device_name) { - auto rc = tools::wallet2::make_new(vm, password_prompter); + auto rc = tools::wallet2::make_new(vm, false, password_prompter); m_wallet = std::move(rc.first); if (!m_wallet) { @@ -3649,7 +3699,7 @@ boost::optional simple_wallet::new_wallet(const boost::pr boost::optional simple_wallet::new_wallet(const boost::program_options::variables_map& vm, const epee::wipeable_string &multisig_keys, const std::string &old_language) { - auto rc = tools::wallet2::make_new(vm, password_prompter); + auto rc = tools::wallet2::make_new(vm, false, password_prompter); m_wallet = std::move(rc.first); if (!m_wallet) { @@ -3720,7 +3770,7 @@ bool simple_wallet::open_wallet(const boost::program_options::variables_map& vm) epee::wipeable_string password; try { - auto rc = tools::wallet2::make_from_file(vm, m_wallet_file, password_prompter); + auto rc = tools::wallet2::make_from_file(vm, false, m_wallet_file, password_prompter); m_wallet = std::move(rc.first); password = std::move(std::move(rc.second).password()); if (!m_wallet) @@ -3747,7 +3797,12 @@ bool simple_wallet::open_wallet(const boost::program_options::variables_map& vm) // NOTE: this is_deprecated() refers to the wallet file format before becoming JSON. It does not refer to the "old english" seed words form of "deprecated" used elsewhere. if (m_wallet->is_deprecated()) { - if (m_wallet->is_deterministic()) + bool is_deterministic; + { + SCOPED_WALLET_UNLOCK(); + is_deterministic = m_wallet->is_deterministic(); + } + if (is_deterministic) { message_writer(console_color_green, false) << "\n" << tr("You had been using " "a deprecated version of the wallet. Please proceed to upgrade your wallet.\n"); @@ -3985,7 +4040,7 @@ bool simple_wallet::set_daemon(const std::vector& args) daemon_url = args[0]; } LOCK_IDLE_SCOPE(); - m_wallet->init(daemon_url); + m_wallet->init(false, daemon_url); if (args.size() == 2) { @@ -4099,6 +4154,32 @@ void simple_wallet::on_skip_transaction(uint64_t height, const crypto::hash &txi { } //---------------------------------------------------------------------------------------------------- +boost::optional simple_wallet::on_get_password(const char *reason) +{ + // can't ask for password from a background thread + if (!m_in_manual_refresh.load(std::memory_order_relaxed)) + { + message_writer(console_color_red, false) << tr("Password needed - use the refresh command"); + m_cmd_binder.print_prompt(); + return boost::none; + } + +#ifdef HAVE_READLINE + rdln::suspend_readline pause_readline; +#endif + std::string msg = tr("Enter password"); + if (reason && *reason) + msg += std::string(" (") + reason + ")"; + auto pwd_container = tools::password_container::prompt(false, msg.c_str()); + if (!pwd_container) + { + MERROR("Failed to read password"); + return boost::none; + } + + return pwd_container->password(); +} +//---------------------------------------------------------------------------------------------------- bool simple_wallet::refresh_main(uint64_t start_height, bool reset, bool is_init) { if (!try_connect_to_daemon(is_init)) @@ -4574,11 +4655,10 @@ bool simple_wallet::print_ring_members(const std::vector &args_) { // "transfer [index=[,,...]] [] []
[]" - if (m_wallet->ask_password() && !get_and_verify_password()) { return true; } if (!try_connect_to_daemon()) return true; - LOCK_IDLE_SCOPE(); + SCOPED_WALLET_UNLOCK(); std::vector local_args = args_; @@ -4981,11 +5061,11 @@ bool simple_wallet::locked_sweep_all(const std::vector &args_) bool simple_wallet::sweep_unmixable(const std::vector &args_) { - if (m_wallet->ask_password() && !get_and_verify_password()) { return true; } if (!try_connect_to_daemon()) return true; - LOCK_IDLE_SCOPE(); + SCOPED_WALLET_UNLOCK(); + try { // figure out what tx will be necessary @@ -5098,7 +5178,6 @@ bool simple_wallet::sweep_main(uint64_t below, bool locked, const std::vectorask_password() && !get_and_verify_password()) { return true; } if (!try_connect_to_daemon()) return true; @@ -5262,7 +5341,7 @@ bool simple_wallet::sweep_main(uint64_t below, bool locked, const std::vector &args_) { - if (m_wallet->ask_password() && !get_and_verify_password()) { return true; } + SCOPED_WALLET_UNLOCK(); if (!try_connect_to_daemon()) return true; @@ -5790,7 +5869,8 @@ bool simple_wallet::sign_transfer(const std::vector &args_) fail_msg_writer() << tr("usage: sign_transfer [export_raw]"); return true; } - if (m_wallet->ask_password() && !get_and_verify_password()) { return true; } + + SCOPED_WALLET_UNLOCK(); const bool export_raw = args_.size() == 1; std::vector ptx; @@ -5879,7 +5959,6 @@ bool simple_wallet::get_tx_key(const std::vector &args_) fail_msg_writer() << tr("usage: get_tx_key "); return true; } - if (m_wallet->ask_password() && !get_and_verify_password()) { return true; } crypto::hash txid; if (!epee::string_tools::hex_to_pod(local_args[0], txid)) @@ -5888,7 +5967,7 @@ bool simple_wallet::get_tx_key(const std::vector &args_) return true; } - LOCK_IDLE_SCOPE(); + SCOPED_WALLET_UNLOCK(); crypto::secret_key tx_key; std::vector additional_tx_keys; @@ -5993,7 +6072,7 @@ bool simple_wallet::get_tx_proof(const std::vector &args) return true; } - if (m_wallet->ask_password() && !get_and_verify_password()) { return true; } + SCOPED_WALLET_UNLOCK(); try { @@ -6208,7 +6287,7 @@ bool simple_wallet::get_spend_proof(const std::vector &args) return true; } - if (m_wallet->ask_password() && !get_and_verify_password()) { return true; } + SCOPED_WALLET_UNLOCK(); try { @@ -6303,9 +6382,7 @@ bool simple_wallet::get_reserve_proof(const std::vector &args) return true; } - if (m_wallet->ask_password() && !get_and_verify_password()) { return true; } - - LOCK_IDLE_SCOPE(); + SCOPED_WALLET_UNLOCK(); try { @@ -6558,6 +6635,9 @@ bool simple_wallet::show_transfers(const std::vector &args_) if (pool) { try { + m_in_manual_refresh.store(true, std::memory_order_relaxed); + epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){m_in_manual_refresh.store(false, std::memory_order_relaxed);}); + m_wallet->update_pool_state(); std::list> payments; m_wallet->get_unconfirmed_payments(payments, m_current_subaddress_account, subaddr_indices); @@ -7406,7 +7486,8 @@ bool simple_wallet::sign(const std::vector &args) fail_msg_writer() << tr("This wallet is multisig and cannot sign"); return true; } - if (m_wallet->ask_password() && !get_and_verify_password()) { return true; } + + SCOPED_WALLET_UNLOCK(); std::string filename = args[0]; std::string data; bool r = epee::file_io_utils::load_file_to_string(filename, data); @@ -7475,14 +7556,14 @@ bool simple_wallet::export_key_images(const std::vector &args) fail_msg_writer() << tr("wallet is watch-only and cannot export key images"); return true; } - if (m_wallet->ask_password() && !get_and_verify_password()) { return true; } + + SCOPED_WALLET_UNLOCK(); std::string filename = args[0]; if (m_wallet->confirm_export_overwrite() && !check_file_overwrite(filename)) return true; try { - LOCK_IDLE_SCOPE(); if (!m_wallet->export_key_images(filename)) { fail_msg_writer() << tr("failed to save file ") << filename; @@ -7554,12 +7635,12 @@ bool simple_wallet::export_outputs(const std::vector &args) fail_msg_writer() << tr("usage: export_outputs "); return true; } - if (m_wallet->ask_password() && !get_and_verify_password()) { return true; } + + SCOPED_WALLET_UNLOCK(); std::string filename = args[0]; if (m_wallet->confirm_export_overwrite() && !check_file_overwrite(filename)) return true; - LOCK_IDLE_SCOPE(); try { std::string data = m_wallet->export_outputs_to_str(); @@ -7605,7 +7686,7 @@ bool simple_wallet::import_outputs(const std::vector &args) try { - LOCK_IDLE_SCOPE(); + SCOPED_WALLET_UNLOCK(); size_t n_outputs = m_wallet->import_outputs_from_str(data); success_msg_writer() << boost::lexical_cast(n_outputs) << " outputs imported"; } diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index c8e17ea6e55..99fc19c00e2 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -262,6 +262,7 @@ namespace cryptonote virtual void on_unconfirmed_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, const cryptonote::subaddress_index& subaddr_index); virtual void on_money_spent(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& in_tx, uint64_t amount, const cryptonote::transaction& spend_tx, const cryptonote::subaddress_index& subaddr_index); virtual void on_skip_transaction(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx); + virtual boost::optional on_get_password(const char *reason); //---------------------------------------------------------- friend class refresh_progress_reporter_t; diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index e75bee5c919..bfd0e4afffc 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -2032,7 +2032,8 @@ bool WalletImpl::isNewWallet() const bool WalletImpl::doInit(const string &daemon_address, uint64_t upper_transaction_size_limit, bool ssl) { - if (!m_wallet->init(daemon_address, m_daemon_login, upper_transaction_size_limit, ssl)) + // claim RPC so there's no in-memory encryption for now + if (!m_wallet->init(true, daemon_address, m_daemon_login, upper_transaction_size_limit, ssl)) return false; // in case new wallet, this will force fast-refresh (pulling hashes instead of blocks) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index e61e437c05e..ea3cca1ce3a 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -89,6 +89,7 @@ using namespace cryptonote; // arbitrary, used to generate different hashes from the same input #define CHACHA8_KEY_TAIL 0x8c +#define CACHE_KEY_TAIL 0x8d #define UNSIGNED_TX_PREFIX "Monero unsigned tx set\004" #define SIGNED_TX_PREFIX "Monero signed tx set\004" @@ -121,8 +122,6 @@ using namespace cryptonote; static const std::string MULTISIG_SIGNATURE_MAGIC = "SigMultisigPkV1"; -std::atomic tools::wallet2::key_ref::refs(0); - namespace { std::string get_default_ringdb_path() @@ -197,7 +196,7 @@ std::string get_size_string(const cryptonote::blobdata &tx) return get_size_string(tx.size()); } -std::unique_ptr make_basic(const boost::program_options::variables_map& vm, const options& opts, const std::function(const char *, bool)> &password_prompter) +std::unique_ptr make_basic(const boost::program_options::variables_map& vm, bool rpc, const options& opts, const std::function(const char *, bool)> &password_prompter) { const bool testnet = command_line::get_arg(vm, opts.testnet); const bool stagenet = command_line::get_arg(vm, opts.stagenet); @@ -238,7 +237,7 @@ std::unique_ptr make_basic(const boost::program_options::variabl daemon_address = std::string("http://") + daemon_host + ":" + std::to_string(daemon_port); std::unique_ptr wallet(new tools::wallet2(nettype, kdf_rounds)); - wallet->init(std::move(daemon_address), std::move(login)); + wallet->init(rpc, std::move(daemon_address), std::move(login)); boost::filesystem::path ringdb_path = command_line::get_arg(vm, opts.shared_ringdb_dir); wallet->set_ring_database(ringdb_path.string()); return wallet; @@ -273,7 +272,7 @@ boost::optional get_password(const boost::program_opt return password_prompter(verify ? tr("Enter a new password for the wallet") : tr("Wallet password"), verify); } -std::unique_ptr generate_from_json(const std::string& json_file, const boost::program_options::variables_map& vm, const options& opts, const std::function(const char *, bool)> &password_prompter) +std::unique_ptr generate_from_json(const std::string& json_file, const boost::program_options::variables_map& vm, bool rpc, const options& opts, const std::function(const char *, bool)> &password_prompter) { const bool testnet = command_line::get_arg(vm, opts.testnet); const bool stagenet = command_line::get_arg(vm, opts.stagenet); @@ -411,7 +410,7 @@ std::unique_ptr generate_from_json(const std::string& json_file, THROW_WALLET_EXCEPTION_IF(deprecated_wallet, tools::error::wallet_internal_error, tools::wallet2::tr("Cannot generate deprecated wallets from JSON")); - wallet.reset(make_basic(vm, opts, password_prompter).release()); + wallet.reset(make_basic(vm, rpc, opts, password_prompter).release()); wallet->set_refresh_from_block_height(field_scan_from_height); wallet->explicit_refresh_from_block_height(field_scan_from_height_found); @@ -648,6 +647,34 @@ const size_t MAX_SPLIT_ATTEMPTS = 30; constexpr const std::chrono::seconds wallet2::rpc_timeout; const char* wallet2::tr(const char* str) { return i18n_translate(str, "tools::wallet2"); } +wallet_keys_unlocker::wallet_keys_unlocker(wallet2 &w, const boost::optional &password): + w(w), + locked(password != boost::none) +{ + if (!locked || w.is_rpc()) + return; + const epee::wipeable_string pass = password->password(); + w.generate_chacha_key_from_password(pass, key); + w.decrypt_keys(key); +} + +wallet_keys_unlocker::wallet_keys_unlocker(wallet2 &w, bool locked, const epee::wipeable_string &password): + w(w), + locked(locked) +{ + if (!locked) + return; + w.generate_chacha_key_from_password(password, key); + w.decrypt_keys(key); +} + +wallet_keys_unlocker::~wallet_keys_unlocker() +{ + if (!locked) + return; + w.encrypt_keys(key); +} + wallet2::wallet2(network_type nettype, uint64_t kdf_rounds): m_multisig_rescan_info(NULL), m_multisig_rescan_k(NULL), @@ -693,7 +720,9 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds): m_key_on_device(false), m_ring_history_saved(false), m_ringdb(), - m_last_block_reward(0) + m_last_block_reward(0), + m_encrypt_keys_after_refresh(boost::none), + m_rpc(false) { } @@ -726,14 +755,14 @@ void wallet2::init_options(boost::program_options::options_description& desc_par command_line::add_arg(desc_params, opts.kdf_rounds); } -std::unique_ptr wallet2::make_from_json(const boost::program_options::variables_map& vm, const std::string& json_file, const std::function(const char *, bool)> &password_prompter) +std::unique_ptr wallet2::make_from_json(const boost::program_options::variables_map& vm, bool rpc, const std::string& json_file, const std::function(const char *, bool)> &password_prompter) { const options opts{}; - return generate_from_json(json_file, vm, opts, password_prompter); + return generate_from_json(json_file, vm, rpc, opts, password_prompter); } std::pair, password_container> wallet2::make_from_file( - const boost::program_options::variables_map& vm, const std::string& wallet_file, const std::function(const char *, bool)> &password_prompter) + const boost::program_options::variables_map& vm, bool rpc, const std::string& wallet_file, const std::function(const char *, bool)> &password_prompter) { const options opts{}; auto pwd = get_password(vm, opts, password_prompter, false); @@ -741,7 +770,7 @@ std::pair, password_container> wallet2::make_from_file( { return {nullptr, password_container{}}; } - auto wallet = make_basic(vm, opts, password_prompter); + auto wallet = make_basic(vm, rpc, opts, password_prompter); if (wallet) { wallet->load(wallet_file, pwd->password()); @@ -749,7 +778,7 @@ std::pair, password_container> wallet2::make_from_file( return {std::move(wallet), std::move(*pwd)}; } -std::pair, password_container> wallet2::make_new(const boost::program_options::variables_map& vm, const std::function(const char *, bool)> &password_prompter) +std::pair, password_container> wallet2::make_new(const boost::program_options::variables_map& vm, bool rpc, const std::function(const char *, bool)> &password_prompter) { const options opts{}; auto pwd = get_password(vm, opts, password_prompter, true); @@ -757,18 +786,19 @@ std::pair, password_container> wallet2::make_new(const { return {nullptr, password_container{}}; } - return {make_basic(vm, opts, password_prompter), std::move(*pwd)}; + return {make_basic(vm, rpc, opts, password_prompter), std::move(*pwd)}; } -std::unique_ptr wallet2::make_dummy(const boost::program_options::variables_map& vm, const std::function(const char *, bool)> &password_prompter) +std::unique_ptr wallet2::make_dummy(const boost::program_options::variables_map& vm, bool rpc, const std::function(const char *, bool)> &password_prompter) { const options opts{}; - return make_basic(vm, opts, password_prompter); + return make_basic(vm, rpc, opts, password_prompter); } //---------------------------------------------------------------------------------------------------- -bool wallet2::init(std::string daemon_address, boost::optional daemon_login, uint64_t upper_transaction_size_limit, bool ssl) +bool wallet2::init(bool rpc, std::string daemon_address, boost::optional daemon_login, uint64_t upper_transaction_size_limit, bool ssl) { + m_rpc = rpc; m_checkpoints.init_default_checkpoints(m_nettype); if(m_http_client.is_connected()) m_http_client.disconnect(); @@ -785,8 +815,7 @@ bool wallet2::is_deterministic() const crypto::secret_key second; keccak((uint8_t *)&get_account().get_keys().m_spend_secret_key, sizeof(crypto::secret_key), (uint8_t *)&second, sizeof(crypto::secret_key)); sc_reduce32((uint8_t *)&second); - bool keys_deterministic = memcmp(second.data,get_account().get_keys().m_view_secret_key.data, sizeof(crypto::secret_key)) == 0; - return keys_deterministic; + return memcmp(second.data,get_account().get_keys().m_view_secret_key.data, sizeof(crypto::secret_key)) == 0; } //---------------------------------------------------------------------------------------------------- bool wallet2::get_seed(epee::wipeable_string& electrum_words, const epee::wipeable_string &passphrase) const @@ -1103,9 +1132,25 @@ static uint64_t decodeRct(const rct::rctSig & rv, const crypto::key_derivation & } } //---------------------------------------------------------------------------------------------------- -void wallet2::scan_output(const cryptonote::transaction &tx, const crypto::public_key &tx_pub_key, size_t i, tx_scan_info_t &tx_scan_info, int &num_vouts_received, std::unordered_map &tx_money_got_in_outs, std::vector &outs) const +void wallet2::scan_output(const cryptonote::transaction &tx, const crypto::public_key &tx_pub_key, size_t i, tx_scan_info_t &tx_scan_info, int &num_vouts_received, std::unordered_map &tx_money_got_in_outs, std::vector &outs) { THROW_WALLET_EXCEPTION_IF(i >= tx.vout.size(), error::wallet_internal_error, "Invalid vout index"); + + // if keys are encrypted, ask for password + if (m_ask_password && !m_rpc && !m_watch_only && !m_multisig_rescan_k) + { + static critical_section password_lock; + CRITICAL_REGION_LOCAL(password_lock); + if (!m_encrypt_keys_after_refresh) + { + boost::optional pwd = m_callback->on_get_password("output received"); + THROW_WALLET_EXCEPTION_IF(!pwd, error::password_needed, tr("Password is needed to compute key image for incoming monero")); + THROW_WALLET_EXCEPTION_IF(!verify_password(*pwd), error::password_needed, tr("Invalid password: password is needed to compute key image for incoming monero")); + decrypt_keys(*pwd); + m_encrypt_keys_after_refresh = *pwd; + } + } + if (m_multisig) { tx_scan_info.in_ephemeral.pub = boost::get(tx.vout[i].target).key; @@ -2063,6 +2108,14 @@ void wallet2::update_pool_state(bool refreshed) { MDEBUG("update_pool_state start"); + auto keys_reencryptor = epee::misc_utils::create_scope_leave_handler([&, this]() { + if (m_encrypt_keys_after_refresh) + { + encrypt_keys(*m_encrypt_keys_after_refresh); + m_encrypt_keys_after_refresh = boost::none; + } + }); + // get the pool state cryptonote::COMMAND_RPC_GET_TRANSACTION_POOL_HASHES_BIN::request req; cryptonote::COMMAND_RPC_GET_TRANSACTION_POOL_HASHES_BIN::response res; @@ -2369,8 +2422,6 @@ bool wallet2::delete_address_book_row(std::size_t row_id) { //---------------------------------------------------------------------------------------------------- void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blocks_fetched, bool& received_money) { - key_ref kref(*this); - if(m_light_wallet) { // MyMonero get_address_info needs to be called occasionally to trigger wallet sync. @@ -2438,6 +2489,14 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo // subsequent pulls in this refresh. start_height = 0; + auto keys_reencryptor = epee::misc_utils::create_scope_leave_handler([&, this]() { + if (m_encrypt_keys_after_refresh) + { + encrypt_keys(*m_encrypt_keys_after_refresh); + m_encrypt_keys_after_refresh = boost::none; + } + }); + bool first = true; while(m_run.load(std::memory_order_relaxed)) { @@ -2507,6 +2566,12 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo throw std::runtime_error("proxy exception in refresh thread"); } } + catch (const tools::error::password_needed&) + { + blocks_fetched += added_blocks; + waiter.wait(&tpool); + throw; + } catch (const std::exception&) { blocks_fetched += added_blocks; @@ -2728,8 +2793,20 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable std::string multisig_signers; cryptonote::account_base account = m_account; + crypto::chacha_key key; + crypto::generate_chacha_key(password.data(), password.size(), key, m_kdf_rounds); + + if (m_ask_password && !m_rpc && !m_watch_only) + { + account.encrypt_viewkey(key); + account.decrypt_keys(key); + } + if (watch_only) account.forget_spend_key(); + + account.encrypt_keys(key); + bool r = epee::serialization::store_t_to_binary(account, account_data); CHECK_AND_ASSERT_MES(r, false, "failed to serialize wallet keys"); wallet2::keys_file_data keys_file_data = boost::value_initialized(); @@ -2846,6 +2923,9 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable value2.SetUint(m_subaddress_lookahead_minor); json.AddMember("subaddress_lookahead_minor", value2, json.GetAllocator()); + value2.SetUint(1); + json.AddMember("encrypted_secret_keys", value2, json.GetAllocator()); + // Serialize the JSON object rapidjson::StringBuffer buffer; rapidjson::Writer writer(buffer); @@ -2853,7 +2933,6 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable account_data = buffer.GetString(); // Encrypt the entire JSON object. - crypto::chacha_key key; crypto::generate_chacha_key(password.data(), password.size(), key, m_kdf_rounds); std::string cipher; cipher.resize(account_data.size()); @@ -2871,6 +2950,35 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable return true; } //---------------------------------------------------------------------------------------------------- +void wallet2::setup_keys(const epee::wipeable_string &password) +{ + crypto::chacha_key key; + crypto::generate_chacha_key(password.data(), password.size(), key, m_kdf_rounds); + + // re-encrypt, but keep viewkey unencrypted + if (m_ask_password && !m_rpc && !m_watch_only) + { + m_account.encrypt_keys(key); + m_account.decrypt_viewkey(key); + } + + static_assert(HASH_SIZE == sizeof(crypto::chacha_key), "Mismatched sizes of hash and chacha key"); + tools::scrubbed_arr cache_key_data; + memcpy(cache_key_data.data(), &key, HASH_SIZE); + cache_key_data[HASH_SIZE] = CACHE_KEY_TAIL; + cn_fast_hash(cache_key_data.data(), HASH_SIZE+1, (crypto::hash&)m_cache_key); + get_ringdb_key(); +} +//---------------------------------------------------------------------------------------------------- +void wallet2::change_password(const std::string &filename, const epee::wipeable_string &original_password, const epee::wipeable_string &new_password) +{ + if (m_ask_password && !m_rpc && !m_watch_only) + decrypt_keys(original_password); + setup_keys(new_password); + rewrite(filename, new_password); + store(); +} +//---------------------------------------------------------------------------------------------------- /*! * \brief Load wallet information from wallet file. * \param keys_file_name Name of wallet file @@ -2881,6 +2989,7 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_ rapidjson::Document json; wallet2::keys_file_data keys_file_data; std::string buf; + bool encrypted_secret_keys = false; bool r = epee::file_io_utils::load_file_to_string(keys_file_name, buf); THROW_WALLET_EXCEPTION_IF(!r, error::file_read_error, keys_file_name); @@ -2926,6 +3035,7 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_ m_subaddress_lookahead_major = SUBADDRESS_LOOKAHEAD_MAJOR; m_subaddress_lookahead_minor = SUBADDRESS_LOOKAHEAD_MINOR; m_key_on_device = false; + encrypted_secret_keys = false; } else if(json.IsObject()) { @@ -3055,6 +3165,8 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_ m_subaddress_lookahead_major = field_subaddress_lookahead_major; GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, subaddress_lookahead_minor, uint32_t, Uint, false, SUBADDRESS_LOOKAHEAD_MINOR); m_subaddress_lookahead_minor = field_subaddress_lookahead_minor; + GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, encrypted_secret_keys, uint32_t, Uint, false, false); + encrypted_secret_keys = field_encrypted_secret_keys; } else { @@ -3071,12 +3183,39 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_ m_account.set_device(hwdev); LOG_PRINT_L0("Device inited..."); } + + if (r) + { + if (encrypted_secret_keys) + { + m_account.decrypt_keys(key); + } + else + { + // rewrite with encrypted keys, ignore errors + if (m_ask_password && !m_rpc && !m_watch_only) + encrypt_keys(key); + bool saved_ret = store_keys(keys_file_name, password, m_watch_only); + if (!saved_ret) + { + // just moan a bit, but not fatal + MERROR("Error saving keys file with encrypted keys, not fatal"); + } + if (m_ask_password && !m_rpc && !m_watch_only) + decrypt_keys(key); + m_keys_file_locker.reset(); + } + } const cryptonote::account_keys& keys = m_account.get_keys(); hw::device &hwdev = m_account.get_device(); r = r && hwdev.verify_keys(keys.m_view_secret_key, keys.m_account_address.m_view_public_key); if(!m_watch_only && !m_multisig) r = r && hwdev.verify_keys(keys.m_spend_secret_key, keys.m_account_address.m_spend_public_key); THROW_WALLET_EXCEPTION_IF(!r, error::invalid_password); + + if (r) + setup_keys(password); + return true; } @@ -3117,6 +3256,7 @@ bool wallet2::verify_password(const std::string& keys_file_name, const epee::wip rapidjson::Document json; wallet2::keys_file_data keys_file_data; std::string buf; + bool encrypted_secret_keys = false; bool r = epee::file_io_utils::load_file_to_string(keys_file_name, buf); THROW_WALLET_EXCEPTION_IF(!r, error::file_read_error, keys_file_name); @@ -3140,19 +3280,50 @@ bool wallet2::verify_password(const std::string& keys_file_name, const epee::wip { account_data = std::string(json["key_data"].GetString(), json["key_data"].GetString() + json["key_data"].GetStringLength()); + GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, encrypted_secret_keys, uint32_t, Uint, false, false); + encrypted_secret_keys = field_encrypted_secret_keys; } cryptonote::account_base account_data_check; r = epee::serialization::load_t_from_binary(account_data_check, account_data); - const cryptonote::account_keys& keys = account_data_check.get_keys(); + if (encrypted_secret_keys) + account_data_check.decrypt_keys(key); + + const cryptonote::account_keys& keys = account_data_check.get_keys(); r = r && hwdev.verify_keys(keys.m_view_secret_key, keys.m_account_address.m_view_public_key); if(!no_spend_key) r = r && hwdev.verify_keys(keys.m_spend_secret_key, keys.m_account_address.m_spend_public_key); return r; } +void wallet2::encrypt_keys(const crypto::chacha_key &key) +{ + m_account.encrypt_keys(key); + m_account.decrypt_viewkey(key); +} + +void wallet2::decrypt_keys(const crypto::chacha_key &key) +{ + m_account.encrypt_viewkey(key); + m_account.decrypt_keys(key); +} + +void wallet2::encrypt_keys(const epee::wipeable_string &password) +{ + crypto::chacha_key key; + crypto::generate_chacha_key(password.data(), password.size(), key, m_kdf_rounds); + encrypt_keys(key); +} + +void wallet2::decrypt_keys(const epee::wipeable_string &password) +{ + crypto::chacha_key key; + crypto::generate_chacha_key(password.data(), password.size(), key, m_kdf_rounds); + decrypt_keys(key); +} + /*! * \brief Generates a wallet or restores one. * \param wallet_ Name of wallet file @@ -3227,6 +3398,7 @@ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string& m_multisig_threshold = threshold; m_multisig_signers = multisig_signers; m_key_on_device = false; + setup_keys(password); if (!wallet_.empty()) { @@ -3281,6 +3453,7 @@ crypto::secret_key wallet2::generate(const std::string& wallet_, const epee::wip m_multisig_threshold = 0; m_multisig_signers.clear(); m_key_on_device = false; + setup_keys(password); // calculate a starting refresh height if(m_refresh_from_block_height == 0 && !recover){ @@ -3382,6 +3555,7 @@ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string& m_multisig_threshold = 0; m_multisig_signers.clear(); m_key_on_device = false; + setup_keys(password); if (!wallet_.empty()) { @@ -3435,6 +3609,7 @@ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string& m_multisig_threshold = 0; m_multisig_signers.clear(); m_key_on_device = false; + setup_keys(password); if (!wallet_.empty()) { @@ -3481,6 +3656,7 @@ void wallet2::restore(const std::string& wallet_, const epee::wipeable_string& p m_multisig = false; m_multisig_threshold = 0; m_multisig_signers.clear(); + setup_keys(password); if (!wallet_.empty()) { bool r = store_keys(m_keys_file, password, false); @@ -3520,6 +3696,17 @@ std::string wallet2::make_multisig(const epee::wipeable_string &password, clear(); + // decrypt keys + epee::misc_utils::auto_scope_leave_caller keys_reencryptor; + if (m_ask_password && !m_rpc && !m_watch_only) + { + crypto::chacha_key chacha_key; + crypto::generate_chacha_key(password.data(), password.size(), chacha_key, m_kdf_rounds); + m_account.encrypt_viewkey(chacha_key); + m_account.decrypt_keys(chacha_key); + keys_reencryptor = epee::misc_utils::create_scope_leave_handler([&, this, chacha_key]() { m_account.encrypt_keys(chacha_key); m_account.decrypt_viewkey(chacha_key); }); + } + MINFO("Creating spend key..."); std::vector multisig_keys; rct::key spend_pkey, spend_skey; @@ -3580,6 +3767,9 @@ std::string wallet2::make_multisig(const epee::wipeable_string &password, m_multisig_signers = std::vector(spend_keys.size() + 1, crypto::null_pkey); } + // re-encrypt keys + keys_reencryptor = epee::misc_utils::auto_scope_leave_caller(); + if (!m_wallet_file.empty()) { bool r = store_keys(m_keys_file, password, false); @@ -3662,6 +3852,17 @@ bool wallet2::finalize_multisig(const epee::wipeable_string &password, std::unor { CHECK_AND_ASSERT_THROW_MES(!pkeys.empty(), "empty pkeys"); + // keys are decrypted + epee::misc_utils::auto_scope_leave_caller keys_reencryptor; + if (m_ask_password && !m_rpc && !m_watch_only) + { + crypto::chacha_key chacha_key; + crypto::generate_chacha_key(password.data(), password.size(), chacha_key, m_kdf_rounds); + m_account.encrypt_viewkey(chacha_key); + m_account.decrypt_keys(chacha_key); + keys_reencryptor = epee::misc_utils::create_scope_leave_handler([&, this, chacha_key]() { m_account.encrypt_keys(chacha_key); m_account.decrypt_viewkey(chacha_key); }); + } + // add ours if not included crypto::public_key local_signer; CHECK_AND_ASSERT_THROW_MES(crypto::secret_key_to_public_key(get_account().get_keys().m_spend_secret_key, local_signer), @@ -3684,6 +3885,9 @@ bool wallet2::finalize_multisig(const epee::wipeable_string &password, std::unor m_multisig_signers = signers; std::sort(m_multisig_signers.begin(), m_multisig_signers.end(), [](const crypto::public_key &e0, const crypto::public_key &e1){ return memcmp(&e0, &e1, sizeof(e0)); }); + // keys are encrypted again + keys_reencryptor = epee::misc_utils::auto_scope_leave_caller(); + if (!m_wallet_file.empty()) { bool r = store_keys(m_keys_file, password, false); @@ -3995,6 +4199,11 @@ bool wallet2::generate_chacha_key_from_secret_keys(crypto::chacha_key &key) cons return hwdev.generate_chacha_key(m_account.get_keys(), key, m_kdf_rounds); } //---------------------------------------------------------------------------------------------------- +void wallet2::generate_chacha_key_from_password(const epee::wipeable_string &pass, crypto::chacha_key &key) const +{ + crypto::generate_chacha_key(pass.data(), pass.size(), key, m_kdf_rounds); +} +//---------------------------------------------------------------------------------------------------- void wallet2::load(const std::string& wallet_, const epee::wipeable_string& password) { clear(); @@ -4015,6 +4224,8 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass LOG_PRINT_L0("Loaded wallet keys file, with public address: " << m_account.get_public_address_str(m_nettype)); lock_keys_file(); + wallet_keys_unlocker unlocker(*this, m_ask_password && !m_rpc && !m_watch_only, password); + //keys loaded ok! //try to load wallet file. but even if we failed, it is not big problem if(!boost::filesystem::exists(m_wallet_file, e) || e) @@ -4036,11 +4247,9 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass r = ::serialization::parse_binary(buf, cache_file_data); THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "internal error: failed to deserialize \"" + m_wallet_file + '\"'); - crypto::chacha_key key; - generate_chacha_key_from_secret_keys(key); std::string cache_data; cache_data.resize(cache_file_data.cache_data.size()); - crypto::chacha20(cache_file_data.cache_data.data(), cache_file_data.cache_data.size(), key, cache_file_data.iv, &cache_data[0]); + crypto::chacha20(cache_file_data.cache_data.data(), cache_file_data.cache_data.size(), m_cache_key, cache_file_data.iv, &cache_data[0]); try { std::stringstream iss; @@ -4048,11 +4257,13 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass boost::archive::portable_binary_iarchive ar(iss); ar >> *this; } - catch (...) + catch(...) { - crypto::chacha8(cache_file_data.cache_data.data(), cache_file_data.cache_data.size(), key, cache_file_data.iv, &cache_data[0]); - try - { + // try with previous scheme: direct from keys + crypto::chacha_key key; + generate_chacha_key_from_secret_keys(key); + crypto::chacha20(cache_file_data.cache_data.data(), cache_file_data.cache_data.size(), key, cache_file_data.iv, &cache_data[0]); + try { std::stringstream iss; iss << cache_data; boost::archive::portable_binary_iarchive ar(iss); @@ -4060,13 +4271,24 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass } catch (...) { - LOG_PRINT_L0("Failed to open portable binary, trying unportable"); - boost::filesystem::copy_file(m_wallet_file, m_wallet_file + ".unportable", boost::filesystem::copy_option::overwrite_if_exists); - std::stringstream iss; - iss.str(""); - iss << cache_data; - boost::archive::binary_iarchive ar(iss); - ar >> *this; + crypto::chacha8(cache_file_data.cache_data.data(), cache_file_data.cache_data.size(), key, cache_file_data.iv, &cache_data[0]); + try + { + std::stringstream iss; + iss << cache_data; + boost::archive::portable_binary_iarchive ar(iss); + ar >> *this; + } + catch (...) + { + LOG_PRINT_L0("Failed to open portable binary, trying unportable"); + boost::filesystem::copy_file(m_wallet_file, m_wallet_file + ".unportable", boost::filesystem::copy_option::overwrite_if_exists); + std::stringstream iss; + iss.str(""); + iss << cache_data; + boost::archive::binary_iarchive ar(iss); + ar >> *this; + } } } } @@ -4218,12 +4440,10 @@ void wallet2::store_to(const std::string &path, const epee::wipeable_string &pas wallet2::cache_file_data cache_file_data = boost::value_initialized(); cache_file_data.cache_data = oss.str(); - crypto::chacha_key key; - generate_chacha_key_from_secret_keys(key); std::string cipher; cipher.resize(cache_file_data.cache_data.size()); cache_file_data.iv = crypto::rand(); - crypto::chacha20(cache_file_data.cache_data.data(), cache_file_data.cache_data.size(), key, cache_file_data.iv, &cipher[0]); + crypto::chacha20(cache_file_data.cache_data.data(), cache_file_data.cache_data.size(), m_cache_key, cache_file_data.iv, &cipher[0]); cache_file_data.cache_data = cipher; const std::string new_file = same_file ? m_wallet_file + ".new" : path; @@ -5861,12 +6081,6 @@ crypto::chacha_key wallet2::get_ringdb_key() return *m_ringdb_key; } -void wallet2::clear_ringdb_key() -{ - MINFO("clearing ringdb key"); - m_ringdb_key = boost::none; -} - bool wallet2::add_rings(const crypto::chacha_key &key, const cryptonote::transaction_prefix &tx) { if (!m_ringdb) @@ -5877,7 +6091,6 @@ bool wallet2::add_rings(const crypto::chacha_key &key, const cryptonote::transac bool wallet2::add_rings(const cryptonote::transaction_prefix &tx) { - key_ref kref(*this); try { return add_rings(get_ringdb_key(), tx); } catch (const std::exception &e) { return false; } } @@ -5886,7 +6099,6 @@ bool wallet2::remove_rings(const cryptonote::transaction_prefix &tx) { if (!m_ringdb) return false; - key_ref kref(*this); try { return m_ringdb->remove_rings(get_ringdb_key(), tx); } catch (const std::exception &e) { return false; } } @@ -5924,7 +6136,6 @@ bool wallet2::get_rings(const crypto::hash &txid, std::vector &outs) { - key_ref kref(*this); try { return get_ring(get_ringdb_key(), key_image, outs); } catch (const std::exception &e) { return false; } } @@ -5934,7 +6145,6 @@ bool wallet2::set_ring(const crypto::key_image &key_image, const std::vectorset_ring(get_ringdb_key(), key_image, outs, relative); } catch (const std::exception &e) { return false; } } @@ -5946,7 +6156,6 @@ bool wallet2::find_and_save_rings(bool force) if (!m_ringdb) return false; - key_ref kref(*this); COMMAND_RPC_GET_TRANSACTIONS::request req = AUTO_VAL_INIT(req); COMMAND_RPC_GET_TRANSACTIONS::response res = AUTO_VAL_INIT(res); @@ -10662,6 +10871,7 @@ size_t wallet2::import_multisig(std::vector blobs) m_multisig_rescan_info = &info; try { + refresh(false); } catch (...) {} diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 86fee0da73b..2d45f4e3e8a 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -67,6 +67,19 @@ class Serialization_portability_wallet_Test; namespace tools { class ringdb; + class wallet2; + + class wallet_keys_unlocker + { + public: + wallet_keys_unlocker(wallet2 &w, const boost::optional &password); + wallet_keys_unlocker(wallet2 &w, bool locked, const epee::wipeable_string &password); + ~wallet_keys_unlocker(); + private: + wallet2 &w; + bool locked; + crypto::chacha_key key; + }; class i_wallet2_callback { @@ -77,6 +90,7 @@ namespace tools virtual void on_unconfirmed_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, const cryptonote::subaddress_index& subaddr_index) {} virtual void on_money_spent(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& in_tx, uint64_t amount, const cryptonote::transaction& spend_tx, const cryptonote::subaddress_index& subaddr_index) {} virtual void on_skip_transaction(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx) {} + virtual boost::optional on_get_password(const char *reason) { return boost::none; } // Light wallet callbacks virtual void on_lw_new_block(uint64_t height) {} virtual void on_lw_money_received(uint64_t height, const crypto::hash &txid, uint64_t amount) {} @@ -133,9 +147,11 @@ namespace tools std::deque m_blockchain; }; + class wallet_keys_unlocker; class wallet2 { friend class ::Serialization_portability_wallet_Test; + friend class wallet_keys_unlocker; public: static constexpr const std::chrono::seconds rpc_timeout = std::chrono::minutes(3) + std::chrono::seconds(30); @@ -153,17 +169,17 @@ namespace tools static void init_options(boost::program_options::options_description& desc_params); //! Uses stdin and stdout. Returns a wallet2 if no errors. - static std::unique_ptr make_from_json(const boost::program_options::variables_map& vm, const std::string& json_file, const std::function(const char *, bool)> &password_prompter); + static std::unique_ptr make_from_json(const boost::program_options::variables_map& vm, bool rpc, const std::string& json_file, const std::function(const char *, bool)> &password_prompter); //! Uses stdin and stdout. Returns a wallet2 and password for `wallet_file` if no errors. static std::pair, password_container> - make_from_file(const boost::program_options::variables_map& vm, const std::string& wallet_file, const std::function(const char *, bool)> &password_prompter); + make_from_file(const boost::program_options::variables_map& vm, bool rpc, const std::string& wallet_file, const std::function(const char *, bool)> &password_prompter); //! Uses stdin and stdout. Returns a wallet2 and password for wallet with no file if no errors. - static std::pair, password_container> make_new(const boost::program_options::variables_map& vm, const std::function(const char *, bool)> &password_prompter); + static std::pair, password_container> make_new(const boost::program_options::variables_map& vm, bool rpc, const std::function(const char *, bool)> &password_prompter); //! Just parses variables. - static std::unique_ptr make_dummy(const boost::program_options::variables_map& vm, const std::function(const char *, bool)> &password_prompter); + static std::unique_ptr make_dummy(const boost::program_options::variables_map& vm, bool rpc, const std::function(const char *, bool)> &password_prompter); static bool verify_password(const std::string& keys_file_name, const epee::wipeable_string& password, bool no_spend_key, hw::device &hwdev, uint64_t kdf_rounds); @@ -477,16 +493,6 @@ namespace tools std::vector additional; }; - struct key_ref - { - key_ref(tools::wallet2 &w): wallet(w) { ++refs; } - ~key_ref() { if (!--refs) wallet.clear_ringdb_key(); } - - private: - tools::wallet2 &wallet; - static std::atomic refs; - }; - /*! * \brief Generates a wallet or restores one. * \param wallet_ Name of wallet file @@ -613,6 +619,11 @@ namespace tools cryptonote::account_base& get_account(){return m_account;} const cryptonote::account_base& get_account()const{return m_account;} + void encrypt_keys(const crypto::chacha_key &key); + void encrypt_keys(const epee::wipeable_string &password); + void decrypt_keys(const crypto::chacha_key &key); + void decrypt_keys(const epee::wipeable_string &password); + void set_refresh_from_block_height(uint64_t height) {m_refresh_from_block_height = height;} uint64_t get_refresh_from_block_height() const {return m_refresh_from_block_height;} @@ -625,7 +636,7 @@ namespace tools // into account the current median block size rather than // the minimum block size. bool deinit(); - bool init(std::string daemon_address = "http://localhost:8080", + bool init(bool rpc, std::string daemon_address = "http://localhost:8080", boost::optional daemon_login = boost::none, uint64_t upper_transaction_size_limit = 0, bool ssl = false); void stop() { m_run.store(false, std::memory_order_relaxed); } @@ -1077,6 +1088,8 @@ namespace tools uint64_t adjust_mixin(uint64_t mixin) const; uint32_t adjust_priority(uint32_t priority); + bool is_rpc() const { return m_rpc; } + // Light wallet specific functions // fetch unspent outs from lw node and store in m_transfers void light_wallet_get_unspent_outs(); @@ -1153,6 +1166,9 @@ namespace tools bool lock_keys_file(); bool unlock_keys_file(); bool is_keys_file_locked() const; + + void change_password(const std::string &filename, const epee::wipeable_string &original_password, const epee::wipeable_string &new_password); + private: /*! * \brief Stores wallet information to wallet file. @@ -1187,6 +1203,7 @@ namespace tools void generate_genesis(cryptonote::block& b) const; void check_genesis(const crypto::hash& genesis_hash) const; //throws bool generate_chacha_key_from_secret_keys(crypto::chacha_key &key) const; + void generate_chacha_key_from_password(const epee::wipeable_string &pass, crypto::chacha_key &key) const; crypto::hash get_payment_id(const pending_tx &ptx) const; void check_acc_out_precomp(const cryptonote::tx_out &o, const crypto::key_derivation &derivation, const std::vector &additional_derivations, size_t i, tx_scan_info_t &tx_scan_info) const; void check_acc_out_precomp(const cryptonote::tx_out &o, const crypto::key_derivation &derivation, const std::vector &additional_derivations, size_t i, const is_out_data *is_out_data, tx_scan_info_t &tx_scan_info) const; @@ -1204,7 +1221,7 @@ namespace tools crypto::public_key get_tx_pub_key_from_received_outs(const tools::wallet2::transfer_details &td) const; bool should_pick_a_second_output(bool use_rct, size_t n_transfers, const std::vector &unused_transfers_indices, const std::vector &unused_dust_indices) const; std::vector get_only_rct(const std::vector &unused_dust_indices, const std::vector &unused_transfers_indices) const; - void scan_output(const cryptonote::transaction &tx, const crypto::public_key &tx_pub_key, size_t i, tx_scan_info_t &tx_scan_info, int &num_vouts_received, std::unordered_map &tx_money_got_in_outs, std::vector &outs) const; + void scan_output(const cryptonote::transaction &tx, const crypto::public_key &tx_pub_key, size_t i, tx_scan_info_t &tx_scan_info, int &num_vouts_received, std::unordered_map &tx_money_got_in_outs, std::vector &outs); void trim_hashchain(); crypto::key_image get_multisig_composite_key_image(size_t n) const; rct::multisig_kLRki get_multisig_composite_kLRki(size_t n, const crypto::public_key &ignore, std::unordered_set &used_L, std::unordered_set &new_used_L) const; @@ -1216,8 +1233,7 @@ namespace tools bool remove_rings(const cryptonote::transaction_prefix &tx); bool get_ring(const crypto::chacha_key &key, const crypto::key_image &key_image, std::vector &outs); crypto::chacha_key get_ringdb_key(); - void cache_ringdb_key(); - void clear_ringdb_key(); + void setup_keys(const epee::wipeable_string &password); bool get_rct_distribution(uint64_t &start_height, std::vector &distribution); @@ -1320,6 +1336,11 @@ namespace tools uint64_t m_last_block_reward; std::unique_ptr m_keys_file_locker; + + crypto::chacha_key m_cache_key; + boost::optional m_encrypt_keys_after_refresh; + + bool m_rpc; }; } BOOST_CLASS_VERSION(tools::wallet2, 25) diff --git a/src/wallet/wallet_errors.h b/src/wallet/wallet_errors.h index e80652750d4..2439532804b 100644 --- a/src/wallet/wallet_errors.h +++ b/src/wallet/wallet_errors.h @@ -53,6 +53,7 @@ namespace tools // wallet_not_initialized // multisig_export_needed // multisig_import_needed + // password_needed // std::logic_error // wallet_logic_error * // file_exists @@ -209,6 +210,14 @@ namespace tools } }; //---------------------------------------------------------------------------------------------------- + struct password_needed : public wallet_runtime_error + { + explicit password_needed(std::string&& loc, const std::string &msg = "Password needed") + : wallet_runtime_error(std::move(loc), msg) + { + } + }; + //---------------------------------------------------------------------------------------------------- const char* const file_error_messages[] = { "file already exists", "file not found", diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index 136cd0e2dc0..780f74d7fa0 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -163,7 +163,7 @@ namespace tools walvars = m_wallet; else { - tmpwal = tools::wallet2::make_dummy(*m_vm, password_prompter); + tmpwal = tools::wallet2::make_dummy(*m_vm, true, password_prompter); walvars = tmpwal.get(); } boost::optional http_login{}; @@ -2638,7 +2638,7 @@ namespace tools command_line::add_arg(desc, arg_password); po::store(po::parse_command_line(argc, argv, desc), vm2); } - std::unique_ptr wal = tools::wallet2::make_new(vm2, nullptr).first; + std::unique_ptr wal = tools::wallet2::make_new(vm2, true, nullptr).first; if (!wal) { er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; @@ -2712,7 +2712,7 @@ namespace tools } std::unique_ptr wal = nullptr; try { - wal = tools::wallet2::make_from_file(vm2, wallet_file, nullptr).first; + wal = tools::wallet2::make_from_file(vm2, true, wallet_file, nullptr).first; } catch (const std::exception& e) { @@ -3261,13 +3261,13 @@ int main(int argc, char** argv) { LOG_PRINT_L0(tools::wallet_rpc_server::tr("Loading wallet...")); if(!wallet_file.empty()) { - wal = tools::wallet2::make_from_file(*vm, wallet_file, password_prompt).first; + wal = tools::wallet2::make_from_file(*vm, true, wallet_file, password_prompt).first; } else { try { - wal = tools::wallet2::make_from_json(*vm, from_json, password_prompt); + wal = tools::wallet2::make_from_json(*vm, true, from_json, password_prompt); } catch (const std::exception &e) { diff --git a/tests/functional_tests/transactions_flow_test.cpp b/tests/functional_tests/transactions_flow_test.cpp index c36c53b89ae..03bfc0a9b55 100644 --- a/tests/functional_tests/transactions_flow_test.cpp +++ b/tests/functional_tests/transactions_flow_test.cpp @@ -138,7 +138,7 @@ bool transactions_flow_test(std::string& working_folder, return false; } - w1.init(daemon_addr_a); + w1.init(true, daemon_addr_a); uint64_t blocks_fetched = 0; bool received_money; @@ -149,7 +149,7 @@ bool transactions_flow_test(std::string& working_folder, return false; } - w2.init(daemon_addr_b); + w2.init(true, daemon_addr_b); MGINFO_GREEN("Using wallets: " << ENDL << "Source: " << w1.get_account().get_public_address_str(MAINNET) << ENDL << "Path: " << working_folder + "/" + path_source_wallet << ENDL diff --git a/tests/unit_tests/CMakeLists.txt b/tests/unit_tests/CMakeLists.txt index f47192ea918..6d4ebe47d54 100644 --- a/tests/unit_tests/CMakeLists.txt +++ b/tests/unit_tests/CMakeLists.txt @@ -27,6 +27,7 @@ # THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. set(unit_tests_sources + account.cpp apply_permutation.cpp address_from_url.cpp ban.cpp diff --git a/tests/unit_tests/account.cpp b/tests/unit_tests/account.cpp new file mode 100644 index 00000000000..113622b5ed1 --- /dev/null +++ b/tests/unit_tests/account.cpp @@ -0,0 +1,71 @@ +// Copyright (c) 2014-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "gtest/gtest.h" + +#include "cryptonote_basic/account.h" + +TEST(account, encrypt_keys) +{ + cryptonote::keypair recovery_key = cryptonote::keypair::generate(hw::get_device("default")); + cryptonote::account_base account; + crypto::secret_key key = account.generate(recovery_key.sec); + const cryptonote::account_keys keys = account.get_keys(); + + ASSERT_EQ(account.get_keys().m_account_address, keys.m_account_address); + ASSERT_EQ(account.get_keys().m_spend_secret_key, keys.m_spend_secret_key); + ASSERT_EQ(account.get_keys().m_view_secret_key, keys.m_view_secret_key); + ASSERT_EQ(account.get_keys().m_multisig_keys, keys.m_multisig_keys); + + crypto::chacha_key chacha_key; + crypto::generate_chacha_key(&recovery_key, sizeof(recovery_key), chacha_key, 1); + + account.encrypt_keys(chacha_key); + + ASSERT_EQ(account.get_keys().m_account_address, keys.m_account_address); + ASSERT_NE(account.get_keys().m_spend_secret_key, keys.m_spend_secret_key); + ASSERT_NE(account.get_keys().m_view_secret_key, keys.m_view_secret_key); + + account.decrypt_viewkey(chacha_key); + + ASSERT_EQ(account.get_keys().m_account_address, keys.m_account_address); + ASSERT_NE(account.get_keys().m_spend_secret_key, keys.m_spend_secret_key); + ASSERT_EQ(account.get_keys().m_view_secret_key, keys.m_view_secret_key); + + account.encrypt_viewkey(chacha_key); + + ASSERT_EQ(account.get_keys().m_account_address, keys.m_account_address); + ASSERT_NE(account.get_keys().m_spend_secret_key, keys.m_spend_secret_key); + ASSERT_NE(account.get_keys().m_view_secret_key, keys.m_view_secret_key); + + account.decrypt_keys(chacha_key); + + ASSERT_EQ(account.get_keys().m_account_address, keys.m_account_address); + ASSERT_EQ(account.get_keys().m_spend_secret_key, keys.m_spend_secret_key); + ASSERT_EQ(account.get_keys().m_view_secret_key, keys.m_view_secret_key); +} diff --git a/tests/unit_tests/multisig.cpp b/tests/unit_tests/multisig.cpp index 92229933383..eb453b960db 100644 --- a/tests/unit_tests/multisig.cpp +++ b/tests/unit_tests/multisig.cpp @@ -61,10 +61,13 @@ static void make_wallet(unsigned int idx, tools::wallet2 &wallet) try { - wallet.init(""); + wallet.init(false, ""); wallet.set_subaddress_lookahead(1, 1); wallet.generate("", "", spendkey, true, false); ASSERT_TRUE(test_addresses[idx].address == wallet.get_account().get_public_address_str(cryptonote::TESTNET)); + wallet.decrypt_keys(""); + ASSERT_TRUE(test_addresses[idx].spendkey == epee::string_tools::pod_to_hex(wallet.get_account().get_keys().m_spend_secret_key)); + wallet.encrypt_keys(""); } catch (const std::exception &e) { @@ -83,8 +86,12 @@ static void make_M_2_wallet(tools::wallet2 &wallet0, tools::wallet2 &wallet1, un std::vector sk0(1), sk1(1); std::vector pk0(1), pk1(1); + wallet0.decrypt_keys(""); std::string mi0 = wallet0.get_multisig_info(); + wallet0.encrypt_keys(""); + wallet1.decrypt_keys(""); std::string mi1 = wallet1.get_multisig_info(); + wallet1.encrypt_keys(""); ASSERT_TRUE(tools::wallet2::verify_multisig_info(mi1, sk0[0], pk0[0])); ASSERT_TRUE(tools::wallet2::verify_multisig_info(mi0, sk1[0], pk1[0])); @@ -118,9 +125,15 @@ static void make_M_3_wallet(tools::wallet2 &wallet0, tools::wallet2 &wallet1, to std::vector sk0(2), sk1(2), sk2(2); std::vector pk0(2), pk1(2), pk2(2); + wallet0.decrypt_keys(""); std::string mi0 = wallet0.get_multisig_info(); + wallet0.encrypt_keys(""); + wallet1.decrypt_keys(""); std::string mi1 = wallet1.get_multisig_info(); + wallet1.encrypt_keys(""); + wallet2.decrypt_keys(""); std::string mi2 = wallet2.get_multisig_info(); + wallet2.encrypt_keys(""); ASSERT_TRUE(tools::wallet2::verify_multisig_info(mi1, sk0[0], pk0[0])); ASSERT_TRUE(tools::wallet2::verify_multisig_info(mi2, sk0[1], pk0[1])); diff --git a/tests/unit_tests/ringdb.cpp b/tests/unit_tests/ringdb.cpp index 8b0ea10d416..9b842569a8f 100644 --- a/tests/unit_tests/ringdb.cpp +++ b/tests/unit_tests/ringdb.cpp @@ -47,7 +47,7 @@ static crypto::chacha_key generate_chacha_key() { crypto::chacha_key chacha_key; uint64_t password = crypto::rand(); - crypto::generate_chacha_key(std::string((const char*)&password, sizeof(password)), chacha_key); + crypto::generate_chacha_key(std::string((const char*)&password, sizeof(password)), chacha_key, 1); return chacha_key; } From 70271fa7888f44bb6ec11058853256fc3055037f Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 2 Aug 2018 11:44:31 +0000 Subject: [PATCH 0253/1404] common: add a class to safely wrap mlock/munlock This class will allow mlocking small objects, of which there may be several per page. It adds refcounting so pages are only munlocked when the last object on that page munlocks. --- contrib/epee/include/mlocker.h | 87 +++++++++++++ contrib/epee/include/string_tools.h | 7 ++ contrib/epee/src/CMakeLists.txt | 2 +- contrib/epee/src/mlocker.cpp | 182 +++++++++++++++++++++++++++ tests/unit_tests/CMakeLists.txt | 1 + tests/unit_tests/mlocker.cpp | 186 ++++++++++++++++++++++++++++ 6 files changed, 464 insertions(+), 1 deletion(-) create mode 100644 contrib/epee/include/mlocker.h create mode 100644 contrib/epee/src/mlocker.cpp create mode 100644 tests/unit_tests/mlocker.cpp diff --git a/contrib/epee/include/mlocker.h b/contrib/epee/include/mlocker.h new file mode 100644 index 00000000000..d2fc2ed5825 --- /dev/null +++ b/contrib/epee/include/mlocker.h @@ -0,0 +1,87 @@ +// Copyright (c) 2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#pragma once + +#include +#include + +namespace epee +{ + class mlocker + { + public: + mlocker(void *ptr, size_t len); + ~mlocker(); + + static size_t get_page_size(); + static size_t get_num_locked_pages(); + static size_t get_num_locked_objects(); + + static void lock(void *ptr, size_t len); + static void unlock(void *ptr, size_t len); + + private: + static size_t page_size; + static size_t num_locked_objects; + + static boost::mutex &mutex(); + static std::map &map(); + static void lock_page(size_t page); + static void unlock_page(size_t page); + + void *ptr; + size_t len; + }; + + /// Locks memory while in scope + /// + /// Primarily useful for making sure that private keys don't get swapped out + // to disk + template + struct mlocked : public T { + using type = T; + + mlocked(): T() { mlocker::lock(this, sizeof(T)); } + mlocked(const T &t): T(t) { mlocker::lock(this, sizeof(T)); } + mlocked(const mlocked &mt): T(mt) { mlocker::lock(this, sizeof(T)); } + mlocked(const T &&t): T(t) { mlocker::lock(this, sizeof(T)); } + mlocked(const mlocked &&mt): T(mt) { mlocker::lock(this, sizeof(T)); } + mlocked &operator=(const mlocked &mt) { T::operator=(mt); return *this; } + ~mlocked() { mlocker::unlock(this, sizeof(T)); } + }; + + template + T& unwrap(mlocked& src) { return src; } + + template + const T& unwrap(mlocked const& src) { return src; } + + template + using mlocked_arr = mlocked>; +} diff --git a/contrib/epee/include/string_tools.h b/contrib/epee/include/string_tools.h index 8d8603076dd..aba065cc79c 100644 --- a/contrib/epee/include/string_tools.h +++ b/contrib/epee/include/string_tools.h @@ -46,6 +46,7 @@ #include #include "hex.h" #include "memwipe.h" +#include "mlocker.h" #include "span.h" #include "warnings.h" @@ -358,6 +359,12 @@ POP_WARNINGS return hex_to_pod(hex_str, unwrap(s)); } //---------------------------------------------------------------------------- + template + bool hex_to_pod(const std::string& hex_str, epee::mlocked& s) + { + return hex_to_pod(hex_str, unwrap(s)); + } + //---------------------------------------------------------------------------- bool validate_hex(uint64_t length, const std::string& str); //---------------------------------------------------------------------------- inline std::string get_extension(const std::string& str) diff --git a/contrib/epee/src/CMakeLists.txt b/contrib/epee/src/CMakeLists.txt index c4750cea037..0b5e7ae6ca4 100644 --- a/contrib/epee/src/CMakeLists.txt +++ b/contrib/epee/src/CMakeLists.txt @@ -27,7 +27,7 @@ # THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. add_library(epee STATIC hex.cpp http_auth.cpp mlog.cpp net_utils_base.cpp string_tools.cpp wipeable_string.cpp memwipe.c - connection_basic.cpp network_throttle.cpp network_throttle-detail.cpp) + connection_basic.cpp network_throttle.cpp network_throttle-detail.cpp mlocker.cpp) if (USE_READLINE AND GNU_READLINE_FOUND) add_library(epee_readline STATIC readline_buffer.cpp) endif() diff --git a/contrib/epee/src/mlocker.cpp b/contrib/epee/src/mlocker.cpp new file mode 100644 index 00000000000..5573d591a17 --- /dev/null +++ b/contrib/epee/src/mlocker.cpp @@ -0,0 +1,182 @@ +// Copyright (c) 2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#if defined __GNUC__ && !defined _WIN32 +#define HAVE_MLOCK 1 +#endif + +#include +#if defined HAVE_MLOCK +#include +#endif +#include "misc_log_ex.h" +#include "syncobj.h" +#include "mlocker.h" + +static size_t query_page_size() +{ +#if defined HAVE_MLOCK + long ret = sysconf(_SC_PAGESIZE); + if (ret <= 0) + { + MERROR("Failed to determine page size"); + return 0; + } + MINFO("Page size: " << ret); + return ret; +#else +#warning Missing query_page_size implementation +#endif + return 0; +} + +static void do_lock(void *ptr, size_t len) +{ +#if defined HAVE_MLOCK + int ret = mlock(ptr, len); + if (ret < 0) + MERROR("Error locking page at " << ptr << ": " << strerror(errno)); +#else +#warning Missing do_lock implementation +#endif +} + +static void do_unlock(void *ptr, size_t len) +{ +#if defined HAVE_MLOCK + int ret = munlock(ptr, len); + if (ret < 0) + MERROR("Error unlocking page at " << ptr << ": " << strerror(errno)); +#else +#warning Missing implementation of page size detection +#endif +} + +namespace epee +{ + size_t mlocker::page_size = 0; + size_t mlocker::num_locked_objects = 0; + + boost::mutex &mlocker::mutex() + { + static boost::mutex vmutex; + return vmutex; + } + std::map &mlocker::map() + { + static std::map vmap; + return vmap; + } + + size_t mlocker::get_page_size() + { + CRITICAL_REGION_LOCAL(mutex()); + if (page_size == 0) + page_size = query_page_size(); + return page_size; + } + + mlocker::mlocker(void *ptr, size_t len): ptr(ptr), len(len) + { + lock(ptr, len); + } + + mlocker::~mlocker() + { + unlock(ptr, len); + } + + void mlocker::lock(void *ptr, size_t len) + { + size_t page_size = get_page_size(); + if (page_size == 0) + return; + + CRITICAL_REGION_LOCAL(mutex()); + const size_t first = ((uintptr_t)ptr) / page_size; + const size_t last = (((uintptr_t)ptr) + len - 1) / page_size; + for (size_t page = first; page <= last; ++page) + lock_page(page); + ++num_locked_objects; + } + + void mlocker::unlock(void *ptr, size_t len) + { + size_t page_size = get_page_size(); + if (page_size == 0) + return; + CRITICAL_REGION_LOCAL(mutex()); + const size_t first = ((uintptr_t)ptr) / page_size; + const size_t last = (((uintptr_t)ptr) + len - 1) / page_size; + for (size_t page = first; page <= last; ++page) + unlock_page(page); + --num_locked_objects; + } + + size_t mlocker::get_num_locked_pages() + { + CRITICAL_REGION_LOCAL(mutex()); + return map().size(); + } + + size_t mlocker::get_num_locked_objects() + { + CRITICAL_REGION_LOCAL(mutex()); + return num_locked_objects; + } + + void mlocker::lock_page(size_t page) + { + std::pair::iterator, bool> p = map().insert(std::make_pair(page, 1)); + if (p.second) + { + do_lock((void*)(page * page_size), page_size); + } + else + { + ++p.first->second; + } + } + + void mlocker::unlock_page(size_t page) + { + std::map::iterator i = map().find(page); + if (i == map().end()) + { + MERROR("Attempt to unlock unlocked page at " << (void*)(page * page_size)); + } + else + { + if (!--i->second) + { + map().erase(i); + do_unlock((void*)(page * page_size), page_size); + } + } + } +} diff --git a/tests/unit_tests/CMakeLists.txt b/tests/unit_tests/CMakeLists.txt index 6d4ebe47d54..4b4870c159b 100644 --- a/tests/unit_tests/CMakeLists.txt +++ b/tests/unit_tests/CMakeLists.txt @@ -53,6 +53,7 @@ set(unit_tests_sources http.cpp main.cpp memwipe.cpp + mlocker.cpp mnemonics.cpp mul_div.cpp multisig.cpp diff --git a/tests/unit_tests/mlocker.cpp b/tests/unit_tests/mlocker.cpp new file mode 100644 index 00000000000..6e6048c6cd7 --- /dev/null +++ b/tests/unit_tests/mlocker.cpp @@ -0,0 +1,186 @@ +// Copyright (c) 2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "gtest/gtest.h" + +#include "misc_log_ex.h" +#include "mlocker.h" + +#define BASE(data) (char*)(((uintptr_t)(data.get() + page_size - 1)) / page_size * page_size) + +TEST(mlocker, distinct_1) +{ + const size_t page_size = epee::mlocker::get_page_size(); + ASSERT_TRUE(page_size > 0); + const size_t base_pages = epee::mlocker::get_num_locked_pages(); + const size_t base_objects = epee::mlocker::get_num_locked_objects(); + std::unique_ptr data{new char[8 * page_size]}; + epee::mlocker *m0 = new epee::mlocker(BASE(data), 1); + epee::mlocker *m1 = new epee::mlocker(BASE(data) + 2 * page_size, 1); + epee::mlocker *m2 = new epee::mlocker(BASE(data) + 3 * page_size, 1); + ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 3); + ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 3); + delete m0; + delete m1; + delete m2; + ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 0); + ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 0); +} + +TEST(mlocker, distinct_full_page) +{ + const size_t page_size = epee::mlocker::get_page_size(); + ASSERT_TRUE(page_size > 0); + const size_t base_pages = epee::mlocker::get_num_locked_pages(); + const size_t base_objects = epee::mlocker::get_num_locked_objects(); + std::unique_ptr data{new char[8 * page_size]}; + epee::mlocker *m0 = new epee::mlocker(BASE(data), page_size); + epee::mlocker *m1 = new epee::mlocker(BASE(data) + 2 * page_size, page_size); + epee::mlocker *m2 = new epee::mlocker(BASE(data) + 3 * page_size, page_size); + ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 3); + ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 3); + delete m0; + delete m1; + delete m2; + ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 0); + ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 0); +} + +TEST(mlocker, identical) +{ + const size_t page_size = epee::mlocker::get_page_size(); + ASSERT_TRUE(page_size >= 32); + const size_t base_pages = epee::mlocker::get_num_locked_pages(); + const size_t base_objects = epee::mlocker::get_num_locked_objects(); + std::unique_ptr data{new char[8 * page_size]}; + epee::mlocker *m0 = new epee::mlocker(BASE(data) + page_size, 32); + epee::mlocker *m1 = new epee::mlocker(BASE(data) + page_size, 32); + epee::mlocker *m2 = new epee::mlocker(BASE(data) + page_size, 32); + ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 1); + ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 3); + delete m1; + ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 1); + ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 2); + delete m0; + delete m2; + ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 0); + ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 0); +} + +TEST(mlocker, overlapping_small) +{ + const size_t page_size = epee::mlocker::get_page_size(); + ASSERT_TRUE(page_size >= 64); + const size_t base_pages = epee::mlocker::get_num_locked_pages(); + const size_t base_objects = epee::mlocker::get_num_locked_objects(); + std::unique_ptr data{new char[8 * page_size]}; + epee::mlocker *m0 = new epee::mlocker(BASE(data), 32); + epee::mlocker *m1 = new epee::mlocker(BASE(data) + 16, 32); + epee::mlocker *m2 = new epee::mlocker(BASE(data) + 8, 32); + ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 1); + ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 3); + delete m1; + ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 1); + ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 2); + delete m2; + delete m0; + ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 0); + ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 0); +} + +TEST(mlocker, multi_page) +{ + const size_t page_size = epee::mlocker::get_page_size(); + ASSERT_TRUE(page_size > 0); + const size_t base_pages = epee::mlocker::get_num_locked_pages(); + const size_t base_objects = epee::mlocker::get_num_locked_objects(); + std::unique_ptr data{new char[8 * page_size]}; + epee::mlocker *m0 = new epee::mlocker(BASE(data) + page_size, page_size * 3); + ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 3); + ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 1); + epee::mlocker *m1 = new epee::mlocker(BASE(data) + page_size * 7, page_size); + ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 4); + ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 2); + delete m0; + ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 1); + ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 1); + delete m1; + ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 0); + ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 0); +} + +TEST(mlocker, cross_page) +{ + const size_t page_size = epee::mlocker::get_page_size(); + ASSERT_TRUE(page_size > 32); + const size_t base_pages = epee::mlocker::get_num_locked_pages(); + const size_t base_objects = epee::mlocker::get_num_locked_objects(); + std::unique_ptr data{new char[2 * page_size]}; + epee::mlocker *m0 = new epee::mlocker(BASE(data) + page_size - 1, 2); + ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 2); + ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 1); + delete m0; + ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 0); + ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 0); +} + +TEST(mlocker, redundant) +{ + const size_t page_size = epee::mlocker::get_page_size(); + const size_t base_pages = epee::mlocker::get_num_locked_pages(); + const size_t base_objects = epee::mlocker::get_num_locked_objects(); + std::unique_ptr data{new char[2 * page_size]}; + ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 0); + ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 0); + epee::mlocker *m0 = new epee::mlocker(BASE(data), 32); + ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 1); + ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 1); + epee::mlocker *m1 = new epee::mlocker(BASE(data), 32); + ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 1); + ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 2); + delete m1; + ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 1); + ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 1); + delete m0; + ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 0); + ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 0); +} + +TEST(mlocker, mlocked) +{ + const size_t base_pages = epee::mlocker::get_num_locked_pages(); + const size_t base_objects = epee::mlocker::get_num_locked_objects(); + { + struct Foo { uint64_t u; }; + epee::mlocked l; + ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 1); + ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 1); + } + ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 0); + ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 0); +} From ab74dc277aac3922a266ccae30fa2c30d9222790 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 15 Jul 2018 14:33:48 +0100 Subject: [PATCH 0254/1404] crypto: make secret_key automatically mlock --- src/crypto/chacha.h | 11 ++++++----- src/crypto/crypto.h | 3 ++- src/cryptonote_basic/account.cpp | 4 ++-- src/device/device_default.cpp | 2 +- src/wallet/wallet2.cpp | 2 +- 5 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/crypto/chacha.h b/src/crypto/chacha.h index 1dc270faf68..6e85ad0e9e4 100644 --- a/src/crypto/chacha.h +++ b/src/crypto/chacha.h @@ -40,6 +40,7 @@ #include #include "memwipe.h" +#include "mlocker.h" #include "hash.h" namespace crypto { @@ -50,7 +51,7 @@ namespace crypto { #if defined(__cplusplus) } - using chacha_key = tools::scrubbed_arr; + using chacha_key = epee::mlocked>; #pragma pack(push, 1) // MS VC 2012 doesn't interpret `class chacha_iv` as POD in spite of [9.0.10], so it is a struct @@ -71,20 +72,20 @@ namespace crypto { inline void generate_chacha_key(const void *data, size_t size, chacha_key& key, uint64_t kdf_rounds) { static_assert(sizeof(chacha_key) <= sizeof(hash), "Size of hash must be at least that of chacha_key"); - tools::scrubbed_arr pwd_hash; + epee::mlocked> pwd_hash; crypto::cn_slow_hash(data, size, pwd_hash.data(), 0/*variant*/, 0/*prehashed*/); for (uint64_t n = 1; n < kdf_rounds; ++n) crypto::cn_slow_hash(pwd_hash.data(), pwd_hash.size(), pwd_hash.data(), 0/*variant*/, 0/*prehashed*/); - memcpy(&unwrap(key), pwd_hash.data(), sizeof(key)); + memcpy(&unwrap(unwrap(key)), pwd_hash.data(), sizeof(key)); } inline void generate_chacha_key_prehashed(const void *data, size_t size, chacha_key& key, uint64_t kdf_rounds) { static_assert(sizeof(chacha_key) <= sizeof(hash), "Size of hash must be at least that of chacha_key"); - tools::scrubbed_arr pwd_hash; + epee::mlocked> pwd_hash; crypto::cn_slow_hash(data, size, pwd_hash.data(), 0/*variant*/, 1/*prehashed*/); for (uint64_t n = 1; n < kdf_rounds; ++n) crypto::cn_slow_hash(pwd_hash.data(), pwd_hash.size(), pwd_hash.data(), 0/*variant*/, 0/*prehashed*/); - memcpy(&unwrap(key), pwd_hash.data(), sizeof(key)); + memcpy(&unwrap(unwrap(key)), pwd_hash.data(), sizeof(key)); } inline void generate_chacha_key(std::string password, chacha_key& key, uint64_t kdf_rounds) { diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h index a2d61b04ef8..c1576a218b6 100644 --- a/src/crypto/crypto.h +++ b/src/crypto/crypto.h @@ -41,6 +41,7 @@ #include "common/pod-class.h" #include "common/util.h" #include "memwipe.h" +#include "mlocker.h" #include "generic-ops.h" #include "hex.h" #include "span.h" @@ -65,7 +66,7 @@ namespace crypto { friend class crypto_ops; }; - using secret_key = tools::scrubbed; + using secret_key = epee::mlocked>; POD_CLASS public_keyV { std::vector keys; diff --git a/src/cryptonote_basic/account.cpp b/src/cryptonote_basic/account.cpp index 0aebf92232b..4cbfa814282 100644 --- a/src/cryptonote_basic/account.cpp +++ b/src/cryptonote_basic/account.cpp @@ -67,7 +67,7 @@ DISABLE_VS_WARNINGS(4244 4345) static void derive_key(const crypto::chacha_key &base_key, crypto::chacha_key &key) { static_assert(sizeof(base_key) == sizeof(crypto::hash), "chacha key and hash should be the same size"); - tools::scrubbed_arr data; + epee::mlocked> data; memcpy(data.data(), &base_key, sizeof(base_key)); data[sizeof(base_key)] = KEYS_ENCRYPTION_SALT; crypto::generate_chacha_key(data.data(), sizeof(data), key, 1); @@ -223,7 +223,7 @@ DISABLE_VS_WARNINGS(4244 4345) void account_base::create_from_viewkey(const cryptonote::account_public_address& address, const crypto::secret_key& viewkey) { crypto::secret_key fake; - memset(&unwrap(fake), 0, sizeof(fake)); + memset(&unwrap(unwrap(fake)), 0, sizeof(fake)); create_from_keys(address, fake, viewkey); } //----------------------------------------------------------------- diff --git a/src/device/device_default.cpp b/src/device/device_default.cpp index bf14813eaa4..a4f40e04137 100644 --- a/src/device/device_default.cpp +++ b/src/device/device_default.cpp @@ -103,7 +103,7 @@ namespace hw { bool device_default::generate_chacha_key(const cryptonote::account_keys &keys, crypto::chacha_key &key, uint64_t kdf_rounds) { const crypto::secret_key &view_key = keys.m_view_secret_key; const crypto::secret_key &spend_key = keys.m_spend_secret_key; - tools::scrubbed_arr data; + epee::mlocked> data; memcpy(data.data(), &view_key, sizeof(view_key)); memcpy(data.data() + sizeof(view_key), &spend_key, sizeof(spend_key)); data[sizeof(data) - 1] = CHACHA8_KEY_TAIL; diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index ea3cca1ce3a..115438351a7 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -2963,7 +2963,7 @@ void wallet2::setup_keys(const epee::wipeable_string &password) } static_assert(HASH_SIZE == sizeof(crypto::chacha_key), "Mismatched sizes of hash and chacha key"); - tools::scrubbed_arr cache_key_data; + epee::mlocked> cache_key_data; memcpy(cache_key_data.data(), &key, HASH_SIZE); cache_key_data[HASH_SIZE] = CACHE_KEY_TAIL; cn_fast_hash(cache_key_data.data(), HASH_SIZE+1, (crypto::hash&)m_cache_key); From 82d1b74500aa7ffab47f428f9ca26bb43cc2d8af Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 17 Apr 2016 11:04:01 +0100 Subject: [PATCH 0255/1404] core: cache block template where possible This avoids constant rechecking of the same things each time a miner asks for the block template. The tx pool maintains a cookie to allow users to detect when the pool state changed, which means the block template needs rebuilding. --- src/cryptonote_core/blockchain.cpp | 44 +++++++++++++++++++++++++++++- src/cryptonote_core/blockchain.h | 21 ++++++++++++++ src/cryptonote_core/tx_pool.cpp | 22 ++++++++++++++- src/cryptonote_core/tx_pool.h | 9 ++++++ 4 files changed, 94 insertions(+), 2 deletions(-) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 87ef47c11e7..4f1b028a127 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -158,7 +158,8 @@ Blockchain::Blockchain(tx_memory_pool& tx_pool) : m_db(), m_tx_pool(tx_pool), m_hardfork(NULL), m_timestamps_and_difficulties_height(0), m_current_block_cumul_sz_limit(0), m_current_block_cumul_sz_median(0), m_enforce_dns_checkpoints(false), m_max_prepare_blocks_threads(4), m_db_blocks_per_sync(1), m_db_sync_mode(db_async), m_db_default_sync(false), m_fast_sync(true), m_show_time_stats(false), m_sync_counter(0), m_cancel(false), m_difficulty_for_next_block_top_hash(crypto::null_hash), - m_difficulty_for_next_block(1) + m_difficulty_for_next_block(1), + m_btc_valid(false) { LOG_PRINT_L3("Blockchain::" << __func__); } @@ -632,6 +633,7 @@ block Blockchain::pop_block_from_blockchain() update_next_cumulative_size_limit(); m_tx_pool.on_blockchain_dec(m_db->height()-1, get_tail_id()); + invalidate_block_template_cache(); return popped_block; } @@ -642,6 +644,7 @@ bool Blockchain::reset_and_set_genesis_block(const block& b) CRITICAL_REGION_LOCAL(m_blockchain_lock); m_timestamps_and_difficulties_height = 0; m_alternative_chains.clear(); + invalidate_block_template_cache(); m_db->reset(); m_hardfork->init(); @@ -1212,9 +1215,26 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m LOG_PRINT_L3("Blockchain::" << __func__); size_t median_size; uint64_t already_generated_coins; + uint64_t pool_cookie; CRITICAL_REGION_BEGIN(m_blockchain_lock); height = m_db->height(); + if (m_btc_valid) { + // The pool cookie is atomic. The lack of locking is OK, as if it changes + // just as we compare it, we'll just use a slightly old template, but + // this would be the case anyway if we'd lock, and the change happened + // just after the block template was created + if (!memcmp(&miner_address, &m_btc_address, sizeof(cryptonote::account_public_address)) && m_btc_nonce == ex_nonce && m_btc_pool_cookie == m_tx_pool.cookie()) { + MDEBUG("Using cached template"); + m_btc.timestamp = time(NULL); // update timestamp unconditionally + b = m_btc; + diffic = m_btc_difficulty; + expected_reward = m_btc_expected_reward; + return true; + } + MDEBUG("Not using cached template: address " << (!memcmp(&miner_address, &m_btc_address, sizeof(cryptonote::account_public_address))) << ", nonce " << (m_btc_nonce == ex_nonce) << ", cookie " << (m_btc_pool_cookie == m_tx_pool.cookie())); + invalidate_block_template_cache(); + } b.major_version = m_hardfork->get_current_version(); b.minor_version = m_hardfork->get_ideal_version(); @@ -1241,6 +1261,7 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m { return false; } + pool_cookie = m_tx_pool.cookie(); #if defined(DEBUG_CREATE_BLOCK_TEMPLATE) size_t real_txs_size = 0; uint64_t real_fee = 0; @@ -1355,6 +1376,8 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m MDEBUG("Creating block template: miner tx size " << coinbase_blob_size << ", cumulative size " << cumulative_size << " is now good"); #endif + + cache_block_template(b, miner_address, ex_nonce, diffic, expected_reward, pool_cookie); return true; } LOG_ERROR("Failed to create_block_template with " << 10 << " tries"); @@ -3697,6 +3720,7 @@ bool Blockchain::handle_block_to_main_chain(const block& bl, const crypto::hash& // appears to be a NOP *and* is called elsewhere. wat? m_tx_pool.on_blockchain_inc(new_height, id); get_difficulty_for_next_block(); // just to cache it + invalidate_block_template_cache(); return true; } @@ -4666,6 +4690,24 @@ bool Blockchain::for_all_outputs(uint64_t amount, std::functionfor_all_outputs(amount, f);; } +void Blockchain::invalidate_block_template_cache() +{ + MDEBUG("Invalidating block template cache"); + m_btc_valid = false; +} + +void Blockchain::cache_block_template(const block &b, const cryptonote::account_public_address &address, const blobdata &nonce, const difficulty_type &diff, uint64_t expected_reward, uint64_t pool_cookie) +{ + MDEBUG("Setting block template cache"); + m_btc = b; + m_btc_address = address; + m_btc_nonce = nonce; + m_btc_difficulty = diff; + m_btc_expected_reward = expected_reward; + m_btc_pool_cookie = pool_cookie; + m_btc_valid = true; +} + namespace cryptonote { template bool Blockchain::get_transactions(const std::vector&, std::vector&, std::vector&) const; template bool Blockchain::get_transactions_blobs(const std::vector&, std::vector&, std::vector&, bool) const; diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index d95c8ed15f7..238867c18f7 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -1052,6 +1052,15 @@ namespace cryptonote std::atomic m_cancel; + // block template cache + block m_btc; + account_public_address m_btc_address; + blobdata m_btc_nonce; + difficulty_type m_btc_difficulty; + uint64_t m_btc_pool_cookie; + uint64_t m_btc_expected_reward; + bool m_btc_valid; + /** * @brief collects the keys for all outputs being "spent" as an input * @@ -1407,5 +1416,17 @@ namespace cryptonote * that implicit data. */ bool expand_transaction_2(transaction &tx, const crypto::hash &tx_prefix_hash, const std::vector> &pubkeys); + + /** + * @brief invalidates any cached block template + */ + void invalidate_block_template_cache(); + + /** + * @brief stores a new cached block template + * + * At some point, may be used to push an update to miners + */ + void cache_block_template(const block &b, const cryptonote::account_public_address &address, const blobdata &nonce, const difficulty_type &diff, uint64_t expected_reward, uint64_t pool_cookie); }; } // namespace cryptonote diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index eac0f1f579a..5807867d95c 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -102,7 +102,7 @@ namespace cryptonote } //--------------------------------------------------------------------------------- //--------------------------------------------------------------------------------- - tx_memory_pool::tx_memory_pool(Blockchain& bchs): m_blockchain(bchs), m_txpool_max_size(DEFAULT_TXPOOL_MAX_SIZE), m_txpool_size(0) + tx_memory_pool::tx_memory_pool(Blockchain& bchs): m_blockchain(bchs), m_txpool_max_size(DEFAULT_TXPOOL_MAX_SIZE), m_txpool_size(0), m_cookie(0) { } @@ -306,6 +306,8 @@ namespace cryptonote tvc.m_verifivation_failed = false; m_txpool_size += blob_size; + ++m_cookie; + MINFO("Transaction added to pool: txid " << id << " bytes: " << blob_size << " fee/byte: " << (fee / (double)blob_size)); prune(m_txpool_max_size); @@ -341,6 +343,7 @@ namespace cryptonote bytes = m_txpool_max_size; CRITICAL_REGION_LOCAL1(m_blockchain); LockedTXN lock(m_blockchain); + bool changed = false; // this will never remove the first one, but we don't care auto it = --m_txs_by_fee_and_receive_time.end(); @@ -377,6 +380,7 @@ namespace cryptonote remove_transaction_keyimages(tx); MINFO("Pruned tx " << txid << " from txpool: size: " << it->first.second << ", fee/byte: " << it->first.first); m_txs_by_fee_and_receive_time.erase(it--); + changed = true; } catch (const std::exception &e) { @@ -384,6 +388,8 @@ namespace cryptonote return; } } + if (changed) + ++m_cookie; if (m_txpool_size > bytes) MINFO("Pool size after pruning is larger than limit: " << m_txpool_size << "/" << bytes); } @@ -401,6 +407,7 @@ namespace cryptonote auto ins_res = kei_image_set.insert(id); CHECK_AND_ASSERT_MES(ins_res.second, false, "internal error: try to insert duplicate iterator in key_image set"); } + ++m_cookie; return true; } //--------------------------------------------------------------------------------- @@ -435,6 +442,7 @@ namespace cryptonote } } + ++m_cookie; return true; } //--------------------------------------------------------------------------------- @@ -480,6 +488,7 @@ namespace cryptonote } m_txs_by_fee_and_receive_time.erase(sorted_it); + ++m_cookie; return true; } //--------------------------------------------------------------------------------- @@ -553,6 +562,7 @@ namespace cryptonote // ignore error } } + ++m_cookie; } return true; } @@ -1051,6 +1061,7 @@ namespace cryptonote { CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL1(m_blockchain); + bool changed = false; LockedTXN lock(m_blockchain); for(size_t i = 0; i!= tx.vin.size(); i++) { @@ -1071,6 +1082,7 @@ namespace cryptonote { MDEBUG("Marking " << txid << " as double spending " << itk.k_image); meta.double_spend_seen = true; + changed = true; try { m_blockchain.update_txpool_tx(txid, meta); @@ -1084,6 +1096,8 @@ namespace cryptonote } } } + if (changed) + ++m_cookie; } //--------------------------------------------------------------------------------- std::string tx_memory_pool::print_pool(bool short_format) const @@ -1305,6 +1319,8 @@ namespace cryptonote } } } + if (n_removed > 0) + ++m_cookie; return n_removed; } //--------------------------------------------------------------------------------- @@ -1361,6 +1377,10 @@ namespace cryptonote } } } + + m_cookie = 0; + + // Ignore deserialization error return true; } diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h index 4ade7ddbef3..4abfef85cb1 100644 --- a/src/cryptonote_core/tx_pool.h +++ b/src/cryptonote_core/tx_pool.h @@ -362,6 +362,13 @@ namespace cryptonote */ size_t validate(uint8_t version); + /** + * @brief return the cookie + * + * @return the cookie + */ + uint64_t cookie() const { return m_cookie; } + /** * @brief get the cumulative txpool size in bytes * @@ -549,6 +556,8 @@ namespace cryptonote //!< container for transactions organized by fee per size and receive time sorted_tx_container m_txs_by_fee_and_receive_time; + std::atomic m_cookie; //!< incremented at each change + /** * @brief get an iterator to a transaction in the sorted container * From 1f2409e9e2c15e1b96c2bcb3d7bfd77091b2a504 Mon Sep 17 00:00:00 2001 From: stoffu Date: Thu, 16 Aug 2018 22:08:58 +0900 Subject: [PATCH 0256/1404] Do memwipe for critical secret keys copied to rct::key --- src/cryptonote_core/cryptonote_tx_utils.cpp | 3 +++ src/multisig/multisig.cpp | 5 ++++- src/ringct/rctSigs.cpp | 8 ++++++-- src/wallet/wallet2.cpp | 2 ++ 4 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/cryptonote_core/cryptonote_tx_utils.cpp b/src/cryptonote_core/cryptonote_tx_utils.cpp index 071ce591eef..1581f30886f 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.cpp +++ b/src/cryptonote_core/cryptonote_tx_utils.cpp @@ -516,6 +516,7 @@ namespace cryptonote uint64_t amount_in = 0, amount_out = 0; rct::ctkeyV inSk; + inSk.reserve(sources.size()); // mixRing indexing is done the other way round for simple rct::ctkeyM mixRing(use_simple_rct ? sources.size() : n_total_outs); rct::keyV destinations; @@ -532,6 +533,7 @@ namespace cryptonote ctkey.dest = rct::sk2rct(in_contexts[i].in_ephemeral.sec); ctkey.mask = sources[i].mask; inSk.push_back(ctkey); + memwipe(&ctkey, sizeof(rct::ctkey)); // inPk: (public key, commitment) // will be done when filling in mixRing if (msout) @@ -590,6 +592,7 @@ namespace cryptonote tx.rct_signatures = rct::genRctSimple(rct::hash2rct(tx_prefix_hash), inSk, destinations, inamounts, outamounts, amount_in - amount_out, mixRing, amount_keys, msout ? &kLRki : NULL, msout, index, outSk, bulletproof, hwdev); else tx.rct_signatures = rct::genRct(rct::hash2rct(tx_prefix_hash), inSk, destinations, outamounts, mixRing, amount_keys, msout ? &kLRki[0] : NULL, msout, sources[0].real_output, outSk, bulletproof, hwdev); // same index assumption + memwipe(inSk.data(), inSk.size() * sizeof(rct::ctkey)); CHECK_AND_ASSERT_MES(tx.vout.size() == outSk.size(), false, "outSk size does not match vout"); diff --git a/src/multisig/multisig.cpp b/src/multisig/multisig.cpp index d85c477720d..a0a788b7d18 100644 --- a/src/multisig/multisig.cpp +++ b/src/multisig/multisig.cpp @@ -47,9 +47,12 @@ namespace cryptonote crypto::secret_key get_multisig_blinded_secret_key(const crypto::secret_key &key) { rct::keyV data; + data.reserve(2); data.push_back(rct::sk2rct(key)); data.push_back(multisig_salt); - return rct::rct2sk(rct::hash_to_scalar(data)); + crypto::secret_key result = rct::rct2sk(rct::hash_to_scalar(data)); + memwipe(&data[0], sizeof(rct::key)); + return result; } //----------------------------------------------------------------- void generate_multisig_N_N(const account_keys &keys, const std::vector &spend_keys, std::vector &multisig_keys, rct::key &spend_skey, rct::key &spend_pkey) diff --git a/src/ringct/rctSigs.cpp b/src/ringct/rctSigs.cpp index f74216ed400..cc966c44b07 100644 --- a/src/ringct/rctSigs.cpp +++ b/src/ringct/rctSigs.cpp @@ -492,7 +492,9 @@ namespace rct { for (size_t j = 0; j < outPk.size(); j++) { sc_sub(sk[rows].bytes, sk[rows].bytes, outSk[j].mask.bytes); //subtract output masks in last row.. } - return MLSAG_Gen(message, M, sk, kLRki, mscout, index, rows, hwdev); + mgSig result = MLSAG_Gen(message, M, sk, kLRki, mscout, index, rows, hwdev); + memwipe(sk.data(), sk.size() * sizeof(key)); + return result; } @@ -521,7 +523,9 @@ namespace rct { M[i][0] = pubs[i].dest; subKeys(M[i][1], pubs[i].mask, Cout); } - return MLSAG_Gen(message, M, sk, kLRki, mscout, index, rows, hwdev); + mgSig result = MLSAG_Gen(message, M, sk, kLRki, mscout, index, rows, hwdev); + memwipe(&sk[0], sizeof(key)); + return result; } diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 9deaad09b27..6b6e9865f6c 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -3217,6 +3217,7 @@ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string& for (const auto &msk: multisig_keys) sc_add(skey.bytes, skey.bytes, rct::sk2rct(msk).bytes); THROW_WALLET_EXCEPTION_IF(!(rct::rct2sk(skey) == spend_secret_key), error::invalid_multisig_seed); + memwipe(&skey, sizeof(rct::key)); m_account.make_multisig(view_secret_key, spend_secret_key, spend_public_key, multisig_keys); m_account.finalize_multisig(spend_public_key); @@ -3563,6 +3564,7 @@ std::string wallet2::make_multisig(const epee::wipeable_string &password, MINFO("Creating multisig address..."); CHECK_AND_ASSERT_THROW_MES(m_account.make_multisig(view_skey, rct::rct2sk(spend_skey), rct::rct2pk(spend_pkey), multisig_keys), "Failed to create multisig wallet due to bad keys"); + memwipe(&spend_skey, sizeof(rct::key)); m_account_public_address = m_account.get_keys().m_account_address; m_watch_only = false; From 29dea030916f38348f4c382848fa021afcc84a9f Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 16 Aug 2018 18:15:07 +0000 Subject: [PATCH 0257/1404] epee: resize vectors where possible in serialization to avoid unnecessary repeated reallocation --- .../serialization/keyvalue_serialization_overloads.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/contrib/epee/include/serialization/keyvalue_serialization_overloads.h b/contrib/epee/include/serialization/keyvalue_serialization_overloads.h index 09087f78534..15c95f07ae4 100644 --- a/contrib/epee/include/serialization/keyvalue_serialization_overloads.h +++ b/contrib/epee/include/serialization/keyvalue_serialization_overloads.h @@ -35,6 +35,11 @@ namespace epee { + namespace + { + template void hint_resize(C &container, size_t size) {} + template void hint_resize(std::vector &container, size_t size) { container.reserve(size); } + } namespace serialization { @@ -158,6 +163,7 @@ namespace epee false, "size in blob " << loaded_size << " not have not zero modulo for sizeof(value_type) = " << sizeof(typename stl_container::value_type) << ", type " << typeid(typename stl_container::value_type).name()); size_t count = (loaded_size/sizeof(typename stl_container::value_type)); + hint_resize(container, count); for(size_t i = 0; i < count; i++) container.insert(container.end(), *(pelem++)); } From 3d5abbe86b7bc109be1ea4b946d454ff29a9a600 Mon Sep 17 00:00:00 2001 From: artyomsol <2406155+artyomsol@users.noreply.github.com> Date: Thu, 16 Aug 2018 20:24:50 +0300 Subject: [PATCH 0258/1404] [#4027] add change_wallet_password wallet rpc command --- src/wallet/wallet_rpc_server.cpp | 32 ++++++++++++++++++++ src/wallet/wallet_rpc_server.h | 2 ++ src/wallet/wallet_rpc_server_commands_defs.h | 21 ++++++++++++- 3 files changed, 54 insertions(+), 1 deletion(-) diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index 510cb3e5813..b372b1a8596 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -2728,6 +2728,38 @@ namespace tools return true; } //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::on_change_wallet_password(const wallet_rpc::COMMAND_RPC_CHANGE_WALLET_PASSWORD::request& req, wallet_rpc::COMMAND_RPC_CHANGE_WALLET_PASSWORD::response& res, epee::json_rpc::error& er) + { + if (!m_wallet) return not_open(er); + if (m_restricted) + { + er.code = WALLET_RPC_ERROR_CODE_DENIED; + er.message = "Command unavailable in restricted mode."; + return false; + } + if (m_wallet->verify_password(req.old_password)) + { + try + { + m_wallet->rewrite(m_wallet->get_wallet_file(), req.new_password); + m_wallet->store(); + LOG_PRINT_L0("Wallet password changed."); + } + catch (const std::exception& e) + { + handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR); + return false; + } + } + else + { + er.code = WALLET_RPC_ERROR_CODE_INVALID_PASSWORD; + er.message = "Invalid original password."; + return false; + } + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ void wallet_rpc_server::handle_rpc_exception(const std::exception_ptr& e, epee::json_rpc::error& er, int default_error_code) { try { diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h index 25eb01ba950..fec99f73986 100644 --- a/src/wallet/wallet_rpc_server.h +++ b/src/wallet/wallet_rpc_server.h @@ -132,6 +132,7 @@ namespace tools MAP_JON_RPC_WE("get_languages", on_get_languages, wallet_rpc::COMMAND_RPC_GET_LANGUAGES) MAP_JON_RPC_WE("create_wallet", on_create_wallet, wallet_rpc::COMMAND_RPC_CREATE_WALLET) MAP_JON_RPC_WE("open_wallet", on_open_wallet, wallet_rpc::COMMAND_RPC_OPEN_WALLET) + MAP_JON_RPC_WE("change_wallet_password", on_change_wallet_password, wallet_rpc::COMMAND_RPC_CHANGE_WALLET_PASSWORD) MAP_JON_RPC_WE("is_multisig", on_is_multisig, wallet_rpc::COMMAND_RPC_IS_MULTISIG) MAP_JON_RPC_WE("prepare_multisig", on_prepare_multisig, wallet_rpc::COMMAND_RPC_PREPARE_MULTISIG) MAP_JON_RPC_WE("make_multisig", on_make_multisig, wallet_rpc::COMMAND_RPC_MAKE_MULTISIG) @@ -206,6 +207,7 @@ namespace tools bool on_get_languages(const wallet_rpc::COMMAND_RPC_GET_LANGUAGES::request& req, wallet_rpc::COMMAND_RPC_GET_LANGUAGES::response& res, epee::json_rpc::error& er); bool on_create_wallet(const wallet_rpc::COMMAND_RPC_CREATE_WALLET::request& req, wallet_rpc::COMMAND_RPC_CREATE_WALLET::response& res, epee::json_rpc::error& er); bool on_open_wallet(const wallet_rpc::COMMAND_RPC_OPEN_WALLET::request& req, wallet_rpc::COMMAND_RPC_OPEN_WALLET::response& res, epee::json_rpc::error& er); + bool on_change_wallet_password(const wallet_rpc::COMMAND_RPC_CHANGE_WALLET_PASSWORD::request& req, wallet_rpc::COMMAND_RPC_CHANGE_WALLET_PASSWORD::response& res, epee::json_rpc::error& er); bool on_is_multisig(const wallet_rpc::COMMAND_RPC_IS_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_IS_MULTISIG::response& res, epee::json_rpc::error& er); bool on_prepare_multisig(const wallet_rpc::COMMAND_RPC_PREPARE_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_PREPARE_MULTISIG::response& res, epee::json_rpc::error& er); bool on_make_multisig(const wallet_rpc::COMMAND_RPC_MAKE_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_MAKE_MULTISIG::response& res, epee::json_rpc::error& er); diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h index 48d881c4c3c..54c2554508b 100644 --- a/src/wallet/wallet_rpc_server_commands_defs.h +++ b/src/wallet/wallet_rpc_server_commands_defs.h @@ -47,7 +47,7 @@ // advance which version they will stop working with // Don't go over 32767 for any of these #define WALLET_RPC_VERSION_MAJOR 1 -#define WALLET_RPC_VERSION_MINOR 2 +#define WALLET_RPC_VERSION_MINOR 3 #define MAKE_WALLET_RPC_VERSION(major,minor) (((major)<<16)|(minor)) #define WALLET_RPC_VERSION MAKE_WALLET_RPC_VERSION(WALLET_RPC_VERSION_MAJOR, WALLET_RPC_VERSION_MINOR) namespace tools @@ -1808,6 +1808,25 @@ namespace wallet_rpc }; }; + struct COMMAND_RPC_CHANGE_WALLET_PASSWORD + { + struct request + { + std::string old_password; + std::string new_password; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(old_password) + KV_SERIALIZE(new_password) + END_KV_SERIALIZE_MAP() + }; + struct response + { + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; + }; + struct COMMAND_RPC_IS_MULTISIG { struct request From 11c67188659c9e5d0d9832fd4edc6860c9ae2bb9 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 17 Aug 2018 13:01:20 +0000 Subject: [PATCH 0259/1404] util: remove unused It's obsolete and removed from at least Arch Linux 8.2 Reported by moneroexamples --- src/common/util.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/common/util.cpp b/src/common/util.cpp index 5e0d2726e83..25312bddaa4 100644 --- a/src/common/util.cpp +++ b/src/common/util.cpp @@ -38,7 +38,6 @@ #include #include #include -#include #include #include #include From 76affd941b1b32dccecbe4001415a43a2edeffa3 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 16 Aug 2018 18:15:47 +0000 Subject: [PATCH 0260/1404] epee: some speedup in parsing --- contrib/epee/include/storages/parserse_base_utils.h | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/contrib/epee/include/storages/parserse_base_utils.h b/contrib/epee/include/storages/parserse_base_utils.h index c809392f47b..8c6c1a64d39 100644 --- a/contrib/epee/include/storages/parserse_base_utils.h +++ b/contrib/epee/include/storages/parserse_base_utils.h @@ -28,6 +28,8 @@ #pragma once +#include + namespace epee { namespace misc_utils @@ -36,8 +38,12 @@ namespace misc_utils { inline std::string transform_to_escape_sequence(const std::string& src) { - //std::stringstream res; + static const char escaped[] = "\b\f\n\r\t\v\"\\/"; + if (std::find_first_of(src.begin(), src.end(), escaped, escaped + sizeof(escaped)) == src.end()) + return src; + std::string res; + res.reserve(2 * src.size()); for(std::string::const_iterator it = src.begin(); it!=src.end(); ++it) { switch(*it) @@ -84,6 +90,7 @@ namespace misc_utils inline void match_string2(std::string::const_iterator& star_end_string, std::string::const_iterator buf_end, std::string& val) { val.clear(); + val.reserve(std::distance(star_end_string, buf_end)); bool escape_mode = false; std::string::const_iterator it = star_end_string; ++it; From dc6c0696fd0b9cd28c8195fc6d90390f5aeaf08f Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 16 Aug 2018 18:16:36 +0000 Subject: [PATCH 0261/1404] db_lmdb: speedup the get_output_distribution common case --- src/blockchain_db/lmdb/db_lmdb.cpp | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp index 627038ca766..1e817bb0770 100644 --- a/src/blockchain_db/lmdb/db_lmdb.cpp +++ b/src/blockchain_db/lmdb/db_lmdb.cpp @@ -1971,14 +1971,25 @@ std::vector BlockchainLMDB::get_block_cumulative_rct_outputs(const std MDB_val v; + uint64_t prev_height = heights[0]; for (uint64_t height: heights) { - MDB_val_set(v, height); - result = mdb_cursor_get(m_cur_block_info, (MDB_val *)&zerokval, &v, MDB_GET_BOTH); + if (height == prev_height + 1) + { + MDB_val k2; + result = mdb_cursor_get(m_cur_block_info, &k2, &v, MDB_NEXT); + } + else + { + v.mv_size = sizeof(uint64_t); + v.mv_data = (void*)&height; + result = mdb_cursor_get(m_cur_block_info, (MDB_val *)&zerokval, &v, MDB_GET_BOTH); + } if (result) throw0(DB_ERROR(lmdb_error("Error attempting to retrieve rct distribution from the db: ", result).c_str())); const mdb_block_info *bi = (const mdb_block_info *)v.mv_data; res.push_back(bi->bi_cum_rct); + prev_height = height; } TXN_POSTFIX_RDONLY(); From 76ac5a8fbe65f8d4d0c1494829f8793123542a5f Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 16 Aug 2018 18:17:59 +0000 Subject: [PATCH 0262/1404] wallet2: ask for a binary output distribution, for speed --- src/wallet/wallet2.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 9deaad09b27..d143cac878a 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -2591,6 +2591,7 @@ bool wallet2::get_rct_distribution(uint64_t &start_height, std::vector req.amounts.push_back(0); req.from_height = 0; req.cumulative = true; + req.binary = true; m_daemon_rpc_mutex.lock(); bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_output_distribution", req, res, m_http_client, rpc_timeout); m_daemon_rpc_mutex.unlock(); @@ -6267,6 +6268,7 @@ void wallet2::get_outs(std::vector> req_t.from_height = std::max(segregation_fork_height, RECENT_OUTPUT_BLOCKS) - RECENT_OUTPUT_BLOCKS; req_t.to_height = segregation_fork_height + 1; req_t.cumulative = true; + req_t.binary = true; m_daemon_rpc_mutex.lock(); bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_output_distribution", req_t, resp_t, m_http_client, rpc_timeout * 1000); m_daemon_rpc_mutex.unlock(); From 48a7bc4280b7aef7c9ef1179c5a3f9027abb243a Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 17 Aug 2018 17:24:24 +0000 Subject: [PATCH 0263/1404] tx_pool: fix infinite loop when failing to find a meta record --- src/cryptonote_core/tx_pool.cpp | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index eac0f1f579a..3b8dd81071e 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -1140,7 +1140,7 @@ namespace cryptonote LockedTXN lock(m_blockchain); auto sorted_it = m_txs_by_fee_and_receive_time.begin(); - while (sorted_it != m_txs_by_fee_and_receive_time.end()) + for (; sorted_it != m_txs_by_fee_and_receive_time.end(); ++sorted_it) { txpool_tx_meta_t meta; if (!m_blockchain.get_txpool_tx_meta(sorted_it->second, meta)) @@ -1154,7 +1154,6 @@ namespace cryptonote if (max_total_size < total_size + meta.blob_size) { LOG_PRINT_L2(" would exceed maximum block size"); - sorted_it++; continue; } @@ -1167,14 +1166,12 @@ namespace cryptonote if(!get_block_reward(median_size, total_size + meta.blob_size, already_generated_coins, block_reward, version)) { LOG_PRINT_L2(" would exceed maximum block size"); - sorted_it++; continue; } coinbase = block_reward + fee + meta.fee; if (coinbase < template_accept_threshold(best_coinbase)) { LOG_PRINT_L2(" would decrease coinbase to " << print_money(coinbase)); - sorted_it++; continue; } } @@ -1221,13 +1218,11 @@ namespace cryptonote if (!ready) { LOG_PRINT_L2(" not ready to go"); - sorted_it++; continue; } if (have_key_images(k_images, tx)) { LOG_PRINT_L2(" key images already seen"); - sorted_it++; continue; } @@ -1236,7 +1231,6 @@ namespace cryptonote fee += meta.fee; best_coinbase = coinbase; append_key_images(k_images, tx); - sorted_it++; LOG_PRINT_L2(" added, new block size " << total_size << "/" << max_total_size << ", coinbase " << print_money(best_coinbase)); } From 2a100fd81f712a312e5353acde116d73d836786b Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 17 Aug 2018 16:47:28 +0000 Subject: [PATCH 0264/1404] unit_tests: add tests for incremental keccak --- tests/unit_tests/CMakeLists.txt | 1 + tests/unit_tests/keccak.cpp | 150 ++++++++++++++++++++++++++++++++ 2 files changed, 151 insertions(+) create mode 100644 tests/unit_tests/keccak.cpp diff --git a/tests/unit_tests/CMakeLists.txt b/tests/unit_tests/CMakeLists.txt index 3c7414640ba..90feaf4a671 100644 --- a/tests/unit_tests/CMakeLists.txt +++ b/tests/unit_tests/CMakeLists.txt @@ -50,6 +50,7 @@ set(unit_tests_sources get_xtype_from_string.cpp hashchain.cpp http.cpp + keccak.cpp main.cpp memwipe.cpp mnemonics.cpp diff --git a/tests/unit_tests/keccak.cpp b/tests/unit_tests/keccak.cpp new file mode 100644 index 00000000000..4276b0e1d84 --- /dev/null +++ b/tests/unit_tests/keccak.cpp @@ -0,0 +1,150 @@ +// Copyright (c) 2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "gtest/gtest.h" + +extern "C" { +#include "crypto/keccak.h" +} + +#define KECCAK_BLOCKLEN 136 + +#define TEST_KECCAK(sz, chunks) \ + std::string data; \ + data.resize(sz); \ + for (size_t i = 0; i < sz; ++i) \ + data[i] = i * 17; \ + uint8_t md0[32], md1[32]; \ + keccak((const uint8_t*)data.data(), data.size(), md0, 32); \ + KECCAK_CTX ctx; \ + keccak_init(&ctx); \ + size_t offset = 0; \ + for (size_t i = 0; i < sizeof(chunks) / sizeof(chunks[0]); ++i) \ + { \ + ASSERT_TRUE(offset + chunks[i] <= data.size()); \ + keccak_update(&ctx, (const uint8_t*)data.data() + offset, chunks[i]); \ + offset += chunks[i]; \ + } \ + ASSERT_TRUE(offset == data.size()); \ + keccak_finish(&ctx, md1); \ + ASSERT_EQ(memcmp(md0, md1, 32), 0); + +TEST(keccak, ) +{ +} + +TEST(keccak, 0_and_0) +{ + static const size_t chunks[] = {0}; + TEST_KECCAK(0, chunks); +} + +TEST(keccak, 1_and_1) +{ + static const size_t chunks[] = {1}; + TEST_KECCAK(1, chunks); +} + +TEST(keccak, 1_and_0_1_0) +{ + static const size_t chunks[] = {0, 1, 0}; + TEST_KECCAK(1, chunks); +} + +TEST(keccak, 2_and_1_1) +{ + static const size_t chunks[] = {1, 1}; + TEST_KECCAK(2, chunks); +} + +TEST(keccak, 4_and_0_0_1_0_2_1_0) +{ + static const size_t chunks[] = {0, 0, 1, 0, 2, 1, 0}; + TEST_KECCAK(4, chunks); +} + +TEST(keccak, 15_and_1_14) +{ + static const size_t chunks[] = {1, 14}; + TEST_KECCAK(15, chunks); +} + +TEST(keccak, 135_and_134_1) +{ + static const size_t chunks[] = {134, 1}; + TEST_KECCAK(135, chunks); +} + +TEST(keccak, 135_and_135_0) +{ + static const size_t chunks[] = {135, 0}; + TEST_KECCAK(135, chunks); +} + +TEST(keccak, 135_and_0_135) +{ + static const size_t chunks[] = {0, 135}; + TEST_KECCAK(135, chunks); +} + +TEST(keccak, 136_and_135_1) +{ + static const size_t chunks[] = {135, 1}; + TEST_KECCAK(136, chunks); +} + +TEST(keccak, 136_and_136_0) +{ + static const size_t chunks[] = {136, 0}; + TEST_KECCAK(136, chunks); +} + +TEST(keccak, 136_and_0_136) +{ + static const size_t chunks[] = {0, 136}; + TEST_KECCAK(136, chunks); +} + +TEST(keccak, 136_and_136) +{ + static const size_t chunks[] = {136}; + TEST_KECCAK(136, chunks); +} + +TEST(keccak, 137_and_136_1) +{ + static const size_t chunks[] = {136, 1}; + TEST_KECCAK(137, chunks); +} + +TEST(keccak, 137_and_1_136) +{ + static const size_t chunks[] = {1, 136}; + TEST_KECCAK(137, chunks); +} + From 2bc977bab9b9a2fa97e034357606270804a3e0df Mon Sep 17 00:00:00 2001 From: iDunk5400 Date: Tue, 21 Aug 2018 22:40:03 +0000 Subject: [PATCH 0265/1404] CMakeLists.txt: disable -fstack-protector* on windows Current GCC produces broken binaries with these options --- CMakeLists.txt | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index cab85358180..56d7d5009d4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -567,10 +567,12 @@ else() add_cxx_flag_if_supported(-Wformat-security CXX_SECURITY_FLAGS) # -fstack-protector - add_c_flag_if_supported(-fstack-protector C_SECURITY_FLAGS) - add_cxx_flag_if_supported(-fstack-protector CXX_SECURITY_FLAGS) - add_c_flag_if_supported(-fstack-protector-strong C_SECURITY_FLAGS) - add_cxx_flag_if_supported(-fstack-protector-strong CXX_SECURITY_FLAGS) + if (NOT WIN32) + add_c_flag_if_supported(-fstack-protector C_SECURITY_FLAGS) + add_cxx_flag_if_supported(-fstack-protector CXX_SECURITY_FLAGS) + add_c_flag_if_supported(-fstack-protector-strong C_SECURITY_FLAGS) + add_cxx_flag_if_supported(-fstack-protector-strong CXX_SECURITY_FLAGS) + endif() # linker if (NOT WIN32) From 620105ecaf74d2f5271403ce5d4ffd26fc9d9e85 Mon Sep 17 00:00:00 2001 From: MoroccanMalinois Date: Tue, 21 Aug 2018 23:22:33 +0000 Subject: [PATCH 0266/1404] Translations: handle cross compiling --- CMakeLists.txt | 6 +----- translations/CMakeLists.txt | 17 ++++++++++++++++- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index cab85358180..0834e801b94 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -436,11 +436,7 @@ add_definition_if_function_found(strptime HAVE_STRPTIME) add_definitions(-DAUTO_INITIALIZE_EASYLOGGINGPP) # Generate header for embedded translations -include(ExternalProject) -ExternalProject_Add(generate_translations_header - SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/translations" - BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/translations" - INSTALL_COMMAND cmake -E echo "") +add_subdirectory(translations) include_directories("${CMAKE_CURRENT_BINARY_DIR}/translations") add_subdirectory(external) diff --git a/translations/CMakeLists.txt b/translations/CMakeLists.txt index db60190b19a..fdd5014a740 100644 --- a/translations/CMakeLists.txt +++ b/translations/CMakeLists.txt @@ -30,7 +30,17 @@ cmake_minimum_required(VERSION 2.8.7) project(translations) -add_executable(generate_translations_header generate_translations_header.c) +# when crosscompiling import the executable targets from a file +IF(CMAKE_CROSSCOMPILING) + message(WARNING "CrossCompiling") + SET(IMPORT_EXECUTABLES "${CMAKE_CURRENT_BINARY_DIR}/ImportExecutables.cmake" CACHE FILEPATH "Point it to the export file from a native build") + INCLUDE(${IMPORT_EXECUTABLES}) +ENDIF(CMAKE_CROSSCOMPILING) + +# only build the generator if not crosscompiling +IF(NOT CMAKE_CROSSCOMPILING) + add_executable(generate_translations_header generate_translations_header.c) +ENDIF(NOT CMAKE_CROSSCOMPILING) find_program(LRELEASE lrelease) if(LRELEASE STREQUAL "LRELEASE-NOTFOUND") @@ -61,3 +71,8 @@ add_custom_command(TARGET generate_translations_header COMMAND generate_translations_header ${qm_files} WORKING_DIRECTORY "${CMAKE_CURRENT_BIN_DIR}" COMMENT "Generating embedded translations header") + +# export the generator target to a file, so it can be imported (see above) by another build +IF(NOT CMAKE_CROSSCOMPILING) + EXPORT(TARGETS generate_translations_header FILE ${CMAKE_CURRENT_BINARY_DIR}/ImportExecutables.cmake ) +ENDIF(NOT CMAKE_CROSSCOMPILING) From fa814af96935f914a7dc14929f1713d2ebf86640 Mon Sep 17 00:00:00 2001 From: MoroccanMalinois Date: Tue, 21 Aug 2018 23:25:14 +0000 Subject: [PATCH 0267/1404] Build: Fix target release-static-android --- CMakeLists.txt | 1 + Makefile | 7 +++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0834e801b94..0834cea7504 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -843,6 +843,7 @@ endif() if(ANDROID) set(ATOMIC libatomic.a) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-error=user-defined-warnings") endif() if(CMAKE_C_COMPILER_ID STREQUAL "Clang" AND ARCH_WIDTH EQUAL "32" AND NOT IOS AND NOT FREEBSD) find_library(ATOMIC atomic) diff --git a/Makefile b/Makefile index 41e8805d4cc..9f328434c5e 100644 --- a/Makefile +++ b/Makefile @@ -26,6 +26,8 @@ # 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. +ANDROID_STANDALONE_TOOLCHAIN_PATH ?= /usr/local/toolchain + all: release-all cmake-debug: @@ -91,8 +93,9 @@ release-static-linux-armv7: cd build/release && cmake -D BUILD_TESTS=OFF -D ARCH="armv7-a" -D STATIC=ON -D BUILD_64=OFF -D CMAKE_BUILD_TYPE=release -D BUILD_TAG="linux-armv7" ../.. && $(MAKE) release-static-android: - mkdir -p build/release - cd build/release && cmake -D BUILD_TESTS=OFF -D ARCH="armv7-a" -D STATIC=ON -D BUILD_64=OFF -D CMAKE_BUILD_TYPE=release -D ANDROID=true -D INSTALL_VENDORED_LIBUNBOUND=ON -D BUILD_TAG="android" ../.. && $(MAKE) + mkdir -p build/release/translations + cd build/release/translations && cmake ../../../translations && $(MAKE) + cd build/release && CC=arm-linux-androideabi-clang CXX=arm-linux-androideabi-clang++ cmake -D BUILD_TESTS=OFF -D ARCH="armv7-a" -D STATIC=ON -D BUILD_64=OFF -D CMAKE_BUILD_TYPE=release -D ANDROID=true -D INSTALL_VENDORED_LIBUNBOUND=ON -D BUILD_TAG="android" -D CMAKE_SYSTEM_NAME="Android" -D CMAKE_ANDROID_STANDALONE_TOOLCHAIN="${ANDROID_STANDALONE_TOOLCHAIN_PATH}" -D CMAKE_ANDROID_ARM_MODE=ON -D CMAKE_ANDROID_ARCH_ABI="armeabi-v7a" ../.. && $(MAKE) release-static-linux-armv8: mkdir -p build/release From 5b6bcca32a8b6e5a926888131af5c42203af055f Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 4 Aug 2018 12:37:39 +0000 Subject: [PATCH 0268/1404] wallet2: fix checking the wrong vector when adding hashes The two vectors should be the same size anyway, so add an assert to catch any case where they aren't --- src/wallet/wallet2.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 9deaad09b27..7ef49c27d69 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -1976,9 +1976,11 @@ void wallet2::pull_and_parse_next_blocks(uint64_t start_height, uint64_t &blocks { drop_from_short_history(short_chain_history, 3); + THROW_WALLET_EXCEPTION_IF(prev_blocks.size() != prev_parsed_blocks.size(), error::wallet_internal_error, "size mismatch"); + // prepend the last 3 blocks, should be enough to guard against a block or two's reorg std::vector::const_reverse_iterator i = prev_parsed_blocks.rbegin(); - for (size_t n = 0; n < std::min((size_t)3, prev_blocks.size()); ++n) + for (size_t n = 0; n < std::min((size_t)3, prev_parsed_blocks.size()); ++n) { short_chain_history.push_front(i->hash); ++i; From b219c24c3a2576161b7eb58120123b278c5b1be7 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 4 Aug 2018 12:38:55 +0000 Subject: [PATCH 0269/1404] wallet2: trim hash chain after fast refresh of hashes This ensures it can't end up filled with the actual placeholders --- src/wallet/wallet2.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 7ef49c27d69..8da62c76d9f 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -2292,6 +2292,7 @@ void wallet2::fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height, while (missing_blocks-- > 0) m_blockchain.push_back(crypto::null_hash); // maybe a bit suboptimal, but deque won't do huge reallocs like vector m_blockchain.push_back(m_checkpoints.get_points().at(checkpoint_height)); + m_blockchain.trim(checkpoint_height); short_chain_history.clear(); get_short_chain_history(short_chain_history); } From 62511df622262ed1fcb73c3092f0c072ed93cc00 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 4 Aug 2018 12:41:15 +0000 Subject: [PATCH 0270/1404] wallet2: fix refresh retry when a block/tx fails to parse It would switch to a new set of blocks and fail, getting out of sync with the hash chain in the process --- src/wallet/wallet2.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 8da62c76d9f..f4b6708fcfd 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -2498,10 +2498,6 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo break; } - // switch to the new blocks from the daemon - blocks_start_height = next_blocks_start_height; - blocks = std::move(next_blocks); - parsed_blocks = std::move(next_parsed_blocks); first = false; // handle error from async fetching thread @@ -2509,6 +2505,11 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo { throw std::runtime_error("proxy exception in refresh thread"); } + + // switch to the new blocks from the daemon + blocks_start_height = next_blocks_start_height; + blocks = std::move(next_blocks); + parsed_blocks = std::move(next_parsed_blocks); } catch (const std::exception&) { From 6fcdc9e0b22c66353bdb58e339999651c195666a Mon Sep 17 00:00:00 2001 From: Dusan Klinec Date: Thu, 10 May 2018 00:03:52 +0200 Subject: [PATCH 0271/1404] rpc-wallet: refresh command added --- src/wallet/wallet_rpc_server.cpp | 22 +++++++++++++++++++ src/wallet/wallet_rpc_server.h | 2 ++ src/wallet/wallet_rpc_server_commands_defs.h | 23 ++++++++++++++++++++ 3 files changed, 47 insertions(+) diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index 510cb3e5813..c8a211d9873 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -2505,6 +2505,28 @@ namespace tools return true; } //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::on_refresh(const wallet_rpc::COMMAND_RPC_REFRESH::request& req, wallet_rpc::COMMAND_RPC_REFRESH::response& res, epee::json_rpc::error& er) + { + if (!m_wallet) return not_open(er); + if (m_restricted) + { + er.code = WALLET_RPC_ERROR_CODE_DENIED; + er.message = "Command unavailable in restricted mode."; + return false; + } + try + { + m_wallet->refresh(m_trusted_daemon, req.start_height, res.blocks_fetched, res.received_money); + return true; + } + catch (const std::exception& e) + { + handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR); + return false; + } + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_rescan_spent(const wallet_rpc::COMMAND_RPC_RESCAN_SPENT::request& req, wallet_rpc::COMMAND_RPC_RESCAN_SPENT::response& res, epee::json_rpc::error& er) { if (!m_wallet) return not_open(er); diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h index 25eb01ba950..bfe49a4591b 100644 --- a/src/wallet/wallet_rpc_server.h +++ b/src/wallet/wallet_rpc_server.h @@ -126,6 +126,7 @@ namespace tools MAP_JON_RPC_WE("get_address_book", on_get_address_book, wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY) MAP_JON_RPC_WE("add_address_book", on_add_address_book, wallet_rpc::COMMAND_RPC_ADD_ADDRESS_BOOK_ENTRY) MAP_JON_RPC_WE("delete_address_book",on_delete_address_book,wallet_rpc::COMMAND_RPC_DELETE_ADDRESS_BOOK_ENTRY) + MAP_JON_RPC_WE("refresh", on_refresh, wallet_rpc::COMMAND_RPC_REFRESH) MAP_JON_RPC_WE("rescan_spent", on_rescan_spent, wallet_rpc::COMMAND_RPC_RESCAN_SPENT) MAP_JON_RPC_WE("start_mining", on_start_mining, wallet_rpc::COMMAND_RPC_START_MINING) MAP_JON_RPC_WE("stop_mining", on_stop_mining, wallet_rpc::COMMAND_RPC_STOP_MINING) @@ -200,6 +201,7 @@ namespace tools bool on_get_address_book(const wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::request& req, wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::response& res, epee::json_rpc::error& er); bool on_add_address_book(const wallet_rpc::COMMAND_RPC_ADD_ADDRESS_BOOK_ENTRY::request& req, wallet_rpc::COMMAND_RPC_ADD_ADDRESS_BOOK_ENTRY::response& res, epee::json_rpc::error& er); bool on_delete_address_book(const wallet_rpc::COMMAND_RPC_DELETE_ADDRESS_BOOK_ENTRY::request& req, wallet_rpc::COMMAND_RPC_DELETE_ADDRESS_BOOK_ENTRY::response& res, epee::json_rpc::error& er); + bool on_refresh(const wallet_rpc::COMMAND_RPC_REFRESH::request& req, wallet_rpc::COMMAND_RPC_REFRESH::response& res, epee::json_rpc::error& er); bool on_rescan_spent(const wallet_rpc::COMMAND_RPC_RESCAN_SPENT::request& req, wallet_rpc::COMMAND_RPC_RESCAN_SPENT::response& res, epee::json_rpc::error& er); bool on_start_mining(const wallet_rpc::COMMAND_RPC_START_MINING::request& req, wallet_rpc::COMMAND_RPC_START_MINING::response& res, epee::json_rpc::error& er); bool on_stop_mining(const wallet_rpc::COMMAND_RPC_STOP_MINING::request& req, wallet_rpc::COMMAND_RPC_STOP_MINING::response& res, epee::json_rpc::error& er); diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h index 48d881c4c3c..3f946eaed7f 100644 --- a/src/wallet/wallet_rpc_server_commands_defs.h +++ b/src/wallet/wallet_rpc_server_commands_defs.h @@ -1714,6 +1714,29 @@ namespace wallet_rpc }; }; + struct COMMAND_RPC_REFRESH + { + struct request + { + uint64_t start_height; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_OPT(start_height, (uint64_t) 0) + END_KV_SERIALIZE_MAP() + }; + + struct response + { + uint64_t blocks_fetched; + bool received_money; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(blocks_fetched); + KV_SERIALIZE(received_money); + END_KV_SERIALIZE_MAP() + }; + }; + struct COMMAND_RPC_START_MINING { struct request From 339a23a85e027a6a189fd272866ce98c942ec471 Mon Sep 17 00:00:00 2001 From: MoroccanMalinois Date: Wed, 22 Aug 2018 21:21:34 +0000 Subject: [PATCH 0272/1404] Docker: Fix android build --- README.md | 4 +- utils/build_scripts/android32.Dockerfile | 119 +++++++++++++++-------- utils/build_scripts/android64.Dockerfile | 95 ------------------ 3 files changed, 79 insertions(+), 139 deletions(-) delete mode 100644 utils/build_scripts/android64.Dockerfile diff --git a/README.md b/README.md index 98c300a9d24..ce1f4437f4a 100644 --- a/README.md +++ b/README.md @@ -469,8 +469,8 @@ Then you can run make as usual. ### On Linux for Android (using docker): - # Build image (select android64.Dockerfile for aarch64) - cd utils/build_scripts/ && docker build -f android32.Dockerfile -t monero-android . + # Build image + docker build -f utils/build_scripts/android32.Dockerfile -t monero-android . # Create container docker create -it --name monero-android monero-android bash # Get binaries diff --git a/utils/build_scripts/android32.Dockerfile b/utils/build_scripts/android32.Dockerfile index d0d25aa3d01..e56fade50c3 100644 --- a/utils/build_scripts/android32.Dockerfile +++ b/utils/build_scripts/android32.Dockerfile @@ -1,65 +1,93 @@ -FROM debian:jessie +FROM debian:stable RUN apt-get update && apt-get install -y unzip automake build-essential curl file pkg-config git python libtool +ARG NPROC=1 + WORKDIR /opt/android ## INSTALL ANDROID SDK -RUN curl -s -O https://dl.google.com/android/android-sdk_r24.4.1-linux.tgz \ - && tar --no-same-owner -xzf android-sdk_r24.4.1-linux.tgz \ - && rm -f android-sdk_r24.4.1-linux.tgz +ENV ANDROID_SDK_REVISION 4333796 +ENV ANDROID_SDK_HASH 92ffee5a1d98d856634e8b71132e8a95d96c83a63fde1099be3d86df3106def9 +RUN curl -s -O https://dl.google.com/android/repository/sdk-tools-linux-${ANDROID_SDK_REVISION}.zip \ + && echo "${ANDROID_SDK_HASH} sdk-tools-linux-${ANDROID_SDK_REVISION}.zip" | sha256sum -c \ + && unzip sdk-tools-linux-${ANDROID_SDK_REVISION}.zip \ + && rm -f sdk-tools-linux-${ANDROID_SDK_REVISION}.zip ## INSTALL ANDROID NDK -ENV ANDROID_NDK_REVISION 14 +ENV ANDROID_NDK_REVISION 17b +ENV ANDROID_NDK_HASH 5dfbbdc2d3ba859fed90d0e978af87c71a91a5be1f6e1c40ba697503d48ccecd RUN curl -s -O https://dl.google.com/android/repository/android-ndk-r${ANDROID_NDK_REVISION}-linux-x86_64.zip \ + && echo "${ANDROID_NDK_HASH} android-ndk-r${ANDROID_NDK_REVISION}-linux-x86_64.zip" | sha256sum -c \ && unzip android-ndk-r${ANDROID_NDK_REVISION}-linux-x86_64.zip \ && rm -f android-ndk-r${ANDROID_NDK_REVISION}-linux-x86_64.zip ENV WORKDIR /opt/android -ENV ANDROID_SDK_ROOT ${WORKDIR}/android-sdk-linux +ENV ANDROID_SDK_ROOT ${WORKDIR}/tools ENV ANDROID_NDK_ROOT ${WORKDIR}/android-ndk-r${ANDROID_NDK_REVISION} -## INSTALL BOOST -ENV BOOST_VERSION 1_62_0 -ENV BOOST_VERSION_DOT 1.62.0 -RUN curl -s -L -o boost_${BOOST_VERSION}.tar.bz2 https://sourceforge.net/projects/boost/files/boost/${BOOST_VERSION_DOT}/boost_${BOOST_VERSION}.tar.bz2/download \ - && tar -xvf boost_${BOOST_VERSION}.tar.bz2 \ - && rm -f /usr/boost_${BOOST_VERSION}.tar.bz2 \ - && cd boost_${BOOST_VERSION} \ - && ./bootstrap.sh - ENV TOOLCHAIN_DIR ${WORKDIR}/toolchain-arm RUN ${ANDROID_NDK_ROOT}/build/tools/make_standalone_toolchain.py \ --arch arm \ --api 21 \ - --install-dir $TOOLCHAIN_DIR \ + --install-dir ${TOOLCHAIN_DIR} \ --stl=libc++ -ENV PATH $TOOLCHAIN_DIR/arm-linux-androideabi/bin:$TOOLCHAIN_DIR/bin:$PATH -## Build BOOST -RUN cd boost_${BOOST_VERSION} \ - && ./b2 --build-type=minimal link=static runtime-link=static --with-chrono --with-date_time --with-filesystem --with-program_options --with-regex --with-serialization --with-system --with-thread --build-dir=android32 --stagedir=android32 toolset=clang threading=multi threadapi=pthread target-os=android stage - -#INSTALL cmake (avoid 3.7 : https://github.com/android-ndk/ndk/issues/254) -ENV CMAKE_VERSION 3.6.3 +#INSTALL cmake +ENV CMAKE_VERSION 3.12.1 RUN cd /usr \ - && curl -s -O https://cmake.org/files/v3.6/cmake-${CMAKE_VERSION}-Linux-x86_64.tar.gz \ + && curl -s -O https://cmake.org/files/v3.12/cmake-${CMAKE_VERSION}-Linux-x86_64.tar.gz \ && tar -xzf /usr/cmake-${CMAKE_VERSION}-Linux-x86_64.tar.gz \ && rm -f /usr/cmake-${CMAKE_VERSION}-Linux-x86_64.tar.gz ENV PATH /usr/cmake-${CMAKE_VERSION}-Linux-x86_64/bin:$PATH +## Boost +ARG BOOST_VERSION=1_68_0 +ARG BOOST_VERSION_DOT=1.68.0 +ARG BOOST_HASH=7f6130bc3cf65f56a618888ce9d5ea704fa10b462be126ad053e80e553d6d8b7 +RUN set -ex \ + && curl -s -L -o boost_${BOOST_VERSION}.tar.bz2 https://dl.bintray.com/boostorg/release/${BOOST_VERSION_DOT}/source/boost_${BOOST_VERSION}.tar.bz2 \ + && echo "${BOOST_HASH} boost_${BOOST_VERSION}.tar.bz2" | sha256sum -c \ + && tar -xvf boost_${BOOST_VERSION}.tar.bz2 \ + && rm -f boost_${BOOST_VERSION}.tar.bz2 \ + && cd boost_${BOOST_VERSION} \ + && ./bootstrap.sh + +ENV HOST_PATH $PATH +ENV PATH $TOOLCHAIN_DIR/arm-linux-androideabi/bin:$TOOLCHAIN_DIR/bin:$PATH + +# Build iconv for lib boost locale +ENV ICONV_VERSION 1.15 +ENV ICONV_HASH ccf536620a45458d26ba83887a983b96827001e92a13847b45e4925cc8913178 +RUN curl -s -O http://ftp.gnu.org/pub/gnu/libiconv/libiconv-${ICONV_VERSION}.tar.gz \ + && echo "${ICONV_HASH} libiconv-${ICONV_VERSION}.tar.gz" | sha256sum -c \ + && tar -xzf libiconv-${ICONV_VERSION}.tar.gz \ + && rm -f libiconv-${ICONV_VERSION}.tar.gz \ + && cd libiconv-${ICONV_VERSION} \ + && CC=arm-linux-androideabi-clang CXX=arm-linux-androideabi-clang++ ./configure --build=x86_64-linux-gnu --host=arm-eabi --prefix=${WORKDIR}/libiconv --disable-rpath \ + && make -j${NPROC} && make install + +## Build BOOST +RUN cd boost_${BOOST_VERSION} \ + && ./b2 --build-type=minimal link=static runtime-link=static --with-chrono --with-date_time --with-filesystem --with-program_options --with-regex --with-serialization --with-system --with-thread --with-locale --build-dir=android32 --stagedir=android32 toolset=clang threading=multi threadapi=pthread target-os=android -sICONV_PATH=${WORKDIR}/libiconv stage -j${NPROC} + #Note : we build openssl because the default lacks DSA1 # download, configure and make Zlib ENV ZLIB_VERSION 1.2.11 +ENV ZLIB_HASH c3e5e9fdd5004dcb542feda5ee4f0ff0744628baf8ed2dd5d66f8ca1197cb1a1 RUN curl -s -O https://zlib.net/zlib-${ZLIB_VERSION}.tar.gz \ + && echo "${ZLIB_HASH} zlib-${ZLIB_VERSION}.tar.gz" | sha256sum -c \ && tar -xzf zlib-${ZLIB_VERSION}.tar.gz \ && rm zlib-${ZLIB_VERSION}.tar.gz \ && mv zlib-${ZLIB_VERSION} zlib \ && cd zlib && CC=clang CXX=clang++ ./configure --static \ - && make + && make -j${NPROC} + # open ssl -ENV OPENSSL_VERSION 1.0.2j +ARG OPENSSL_VERSION=1.0.2p +ARG OPENSSL_HASH=50a98e07b1a89eb8f6a99477f262df71c6fa7bef77df4dc83025a2845c827d00 RUN curl -s -O https://www.openssl.org/source/openssl-${OPENSSL_VERSION}.tar.gz \ + && echo "${OPENSSL_HASH} openssl-${OPENSSL_VERSION}.tar.gz" | sha256sum -c \ && tar -xzf openssl-${OPENSSL_VERSION}.tar.gz \ && rm openssl-${OPENSSL_VERSION}.tar.gz \ && cd openssl-${OPENSSL_VERSION} \ @@ -69,26 +97,33 @@ RUN curl -s -O https://www.openssl.org/source/openssl-${OPENSSL_VERSION}.tar.gz no-asm \ no-shared --static \ --with-zlib-include=${WORKDIR}/zlib/include --with-zlib-lib=${WORKDIR}/zlib/lib \ - && make build_crypto build_ssl \ + && make -j${NPROC} \ && cd .. && mv openssl-${OPENSSL_VERSION} openssl # ZMQ -RUN git clone https://github.com/zeromq/zeromq4-1.git \ - && git clone https://github.com/zeromq/cppzmq.git \ - && cd zeromq4-1 \ +ARG ZMQ_VERSION=v4.2.5 +ARG ZMQ_HASH=d062edd8c142384792955796329baf1e5a3377cd +RUN git clone https://github.com/zeromq/libzmq.git -b ${ZMQ_VERSION} \ + && cd libzmq \ + && test `git rev-parse HEAD` = ${ZMQ_HASH} || exit 1 \ && ./autogen.sh \ - && CC=clang CXX=clang++ ./configure --host=arm-none-linux-gnueabi \ - && make + && CC=clang CXX=clang++ ./configure --prefix=${PWD}/prebuilt --host=arm-linux-androideabi --enable-static --disable-shared \ + && make -j${NPROC} \ + && make install -RUN ln -s /opt/android/openssl/libcrypto.a /opt/android/openssl/libssl.a ${TOOLCHAIN_DIR}/arm-linux-androideabi/lib/armv7-a +# zmq.hpp +ARG CPPZMQ_VERSION=v4.2.3 +ARG CPPZMQ_HASH=6aa3ab686e916cb0e62df7fa7d12e0b13ae9fae6 +RUN git clone https://github.com/zeromq/cppzmq.git -b ${CPPZMQ_VERSION} \ + && cd cppzmq \ + && test `git rev-parse HEAD` = ${CPPZMQ_HASH} || exit 1 -RUN git clone https://github.com/monero-project/monero.git \ - && cd monero \ - && mkdir -p build/release \ - && CC=clang CXX=clang++ \ - BOOST_ROOT=${WORKDIR}/boost_${BOOST_VERSION} BOOST_LIBRARYDIR=${WORKDIR}/boost_${BOOST_VERSION}/android32/lib/ \ +ADD . /src +RUN cd /src \ + && BOOST_ROOT=${WORKDIR}/boost_${BOOST_VERSION} BOOST_LIBRARYDIR=${WORKDIR}/boost_${BOOST_VERSION}/android32/lib/ \ OPENSSL_ROOT_DIR=${WORKDIR}/openssl/ \ - CMAKE_INCLUDE_PATH=${WORKDIR}/cppzmq/ \ - CMAKE_LIBRARY_PATH=${WORKDIR}/zeromq4-1/.libs \ - CXXFLAGS="-I ${WORKDIR}/zeromq4-1/include/" \ - make release-static-android + CMAKE_INCLUDE_PATH="${WORKDIR}/cppzmq:${WORKDIR}/libzmq/prebuilt/include" \ + CMAKE_LIBRARY_PATH=${WORKDIR}/libzmq/prebuilt/lib \ + ANDROID_STANDALONE_TOOLCHAIN_PATH=${TOOLCHAIN_DIR} \ + CXXFLAGS="-I ${WORKDIR}/libzmq/prebuilt/include/" \ + PATH=${HOST_PATH} make release-static-android -j${NPROC} diff --git a/utils/build_scripts/android64.Dockerfile b/utils/build_scripts/android64.Dockerfile deleted file mode 100644 index a9504fd2c4d..00000000000 --- a/utils/build_scripts/android64.Dockerfile +++ /dev/null @@ -1,95 +0,0 @@ -FROM debian:jessie - -RUN apt-get update && apt-get install -y unzip automake build-essential curl file pkg-config git python libtool - -WORKDIR /opt/android -## INSTALL ANDROID SDK -RUN curl -s -O https://dl.google.com/android/android-sdk_r24.4.1-linux.tgz \ - && tar --no-same-owner -xzf android-sdk_r24.4.1-linux.tgz \ - && rm -f android-sdk_r24.4.1-linux.tgz - -## INSTALL ANDROID NDK -ENV ANDROID_NDK_REVISION 14 -RUN curl -s -O https://dl.google.com/android/repository/android-ndk-r${ANDROID_NDK_REVISION}-linux-x86_64.zip \ - && unzip android-ndk-r${ANDROID_NDK_REVISION}-linux-x86_64.zip \ - && rm -f android-ndk-r${ANDROID_NDK_REVISION}-linux-x86_64.zip - -ENV WORKDIR /opt/android -ENV ANDROID_SDK_ROOT ${WORKDIR}/android-sdk-linux -ENV ANDROID_NDK_ROOT ${WORKDIR}/android-ndk-r${ANDROID_NDK_REVISION} - -## INSTALL BOOST -ENV BOOST_VERSION 1_62_0 -ENV BOOST_VERSION_DOT 1.62.0 -RUN curl -s -L -o boost_${BOOST_VERSION}.tar.bz2 https://sourceforge.net/projects/boost/files/boost/${BOOST_VERSION_DOT}/boost_${BOOST_VERSION}.tar.bz2/download \ - && tar -xvf boost_${BOOST_VERSION}.tar.bz2 \ - && rm -f /usr/boost_${BOOST_VERSION}.tar.bz2 \ - && cd boost_${BOOST_VERSION} \ - && ./bootstrap.sh - -ENV TOOLCHAIN_DIR ${WORKDIR}/toolchain-arm -RUN ${ANDROID_NDK_ROOT}/build/tools/make_standalone_toolchain.py \ - --arch arm64 \ - --api 21 \ - --install-dir $TOOLCHAIN_DIR \ - --stl=libc++ -ENV PATH $TOOLCHAIN_DIR/aarch64-linux-android/bin:$TOOLCHAIN_DIR/bin:$PATH - -## Build BOOST -RUN cd boost_${BOOST_VERSION} \ - && ./b2 --build-type=minimal link=static runtime-link=static --with-chrono --with-date_time --with-filesystem --with-program_options --with-regex --with-serialization --with-system --with-thread --build-dir=android64 --stagedir=android64 toolset=clang threading=multi threadapi=pthread target-os=android stage - -#INSTALL cmake (avoid 3.7 : https://github.com/android-ndk/ndk/issues/254) -ENV CMAKE_VERSION 3.6.3 -RUN cd /usr \ - && curl -s -O https://cmake.org/files/v3.6/cmake-${CMAKE_VERSION}-Linux-x86_64.tar.gz \ - && tar -xzf /usr/cmake-${CMAKE_VERSION}-Linux-x86_64.tar.gz \ - && rm -f /usr/cmake-${CMAKE_VERSION}-Linux-x86_64.tar.gz -ENV PATH /usr/cmake-${CMAKE_VERSION}-Linux-x86_64/bin:$PATH - -#Note : we build openssl because the default lacks DSA1 - -# download, configure and make Zlib -ENV ZLIB_VERSION 1.2.11 -RUN curl -s -O https://zlib.net/zlib-${ZLIB_VERSION}.tar.gz \ - && tar -xzf zlib-${ZLIB_VERSION}.tar.gz \ - && rm zlib-${ZLIB_VERSION}.tar.gz \ - && mv zlib-${ZLIB_VERSION} zlib \ - && cd zlib && CC=clang CXX=clang++ ./configure --static \ - && make -# open ssl -ENV OPENSSL_VERSION 1.0.2j -RUN curl -s -O https://www.openssl.org/source/openssl-${OPENSSL_VERSION}.tar.gz \ - && tar -xzf openssl-${OPENSSL_VERSION}.tar.gz \ - && rm openssl-${OPENSSL_VERSION}.tar.gz \ - && cd openssl-${OPENSSL_VERSION} \ - && sed -i -e "s/mandroid/target\ aarch64\-none\-linux\-android/" Configure \ - && CC=clang CXX=clang++ \ - ./Configure android \ - no-asm \ - no-shared --static \ - --with-zlib-include=${WORKDIR}/zlib/include --with-zlib-lib=${WORKDIR}/zlib/lib \ - && make build_crypto build_ssl \ - && cd .. && mv openssl-${OPENSSL_VERSION} openssl - -RUN git clone https://github.com/zeromq/zeromq4-1.git \ - && git clone https://github.com/zeromq/cppzmq.git \ - && cd zeromq4-1 \ - && ./autogen.sh \ - && CC=clang CXX=clang++ ./configure --host=aarch64-linux-android \ - && make - -RUN ln -s /opt/android/openssl/libcrypto.a /opt/android/openssl/libssl.a ${TOOLCHAIN_DIR}/aarch64-linux-android/lib - -RUN git clone https://github.com/monero-project/monero.git \ - && cd monero \ - && mkdir -p build/release \ - && cd build/release \ - && CC=clang CXX=clang++ \ - BOOST_ROOT=${WORKDIR}/boost_${BOOST_VERSION} BOOST_LIBRARYDIR=${WORKDIR}/boost_${BOOST_VERSION}/android64/lib/ \ - OPENSSL_ROOT_DIR=${WORKDIR}/openssl/ \ - CMAKE_INCLUDE_PATH=${WORKDIR}/cppzmq/ \ - CMAKE_LIBRARY_PATH=${WORKDIR}/zeromq4-1/.libs \ - CXXFLAGS="-I ${WORKDIR}/zeromq4-1/include/" \ - cmake -D BUILD_TESTS=OFF -D ARCH="armv8-a" -D STATIC=ON -D BUILD_64=ON -D CMAKE_BUILD_TYPE=release -D ANDROID=true -D INSTALL_VENDORED_LIBUNBOUND=ON -D BUILD_TAG="android" ../.. \ - && make -j3 From 8ca1215f25ad48a87c5445ffeed8896122343ad1 Mon Sep 17 00:00:00 2001 From: stoffu Date: Fri, 10 Aug 2018 21:15:40 +0900 Subject: [PATCH 0273/1404] wallet: store trusted-daemon flag in wallet2 --- src/simplewallet/simplewallet.cpp | 75 ++++++----------- src/simplewallet/simplewallet.h | 2 - src/wallet/api/wallet.cpp | 11 ++- src/wallet/api/wallet.h | 1 - src/wallet/wallet2.cpp | 81 ++++++++++++------- src/wallet/wallet2.h | 44 +++++----- src/wallet/wallet_rpc_server.cpp | 35 +++----- src/wallet/wallet_rpc_server.h | 1 - .../transactions_flow_test.cpp | 2 +- 9 files changed, 122 insertions(+), 130 deletions(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index bdf4212ce13..d08ab82451d 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -129,8 +129,6 @@ namespace const command_line::arg_descriptor arg_restore_deterministic_wallet = {"restore-deterministic-wallet", sw::tr("Recover wallet using Electrum-style mnemonic seed"), false}; const command_line::arg_descriptor arg_restore_multisig_wallet = {"restore-multisig-wallet", sw::tr("Recover multisig wallet using Electrum-style mnemonic seed"), false}; const command_line::arg_descriptor arg_non_deterministic = {"non-deterministic", sw::tr("Generate non-deterministic view and spend keys"), false}; - const command_line::arg_descriptor arg_trusted_daemon = {"trusted-daemon", sw::tr("Enable commands which rely on a trusted daemon"), false}; - const command_line::arg_descriptor arg_untrusted_daemon = {"untrusted-daemon", sw::tr("Disable commands which rely on a trusted daemon"), false}; const command_line::arg_descriptor arg_allow_mismatched_daemon_version = {"allow-mismatched-daemon-version", sw::tr("Allow communicating with a daemon that uses a different RPC version"), false}; const command_line::arg_descriptor arg_restore_height = {"restore-height", sw::tr("Restore from specific blockchain height"), 0}; const command_line::arg_descriptor arg_do_not_relay = {"do-not-relay", sw::tr("The newly created transaction will not be relayed to the monero network"), false}; @@ -1137,7 +1135,7 @@ bool simple_wallet::import_multisig(const std::vector &args) fail_msg_writer() << tr("Failed to import multisig info: ") << e.what(); return true; } - if (is_daemon_trusted()) + if (m_wallet->is_trusted_daemon()) { try { @@ -1291,7 +1289,7 @@ bool simple_wallet::submit_multisig(const std::vector &args) } catch (const std::exception &e) { - handle_transfer_exception(std::current_exception(), is_daemon_trusted()); + handle_transfer_exception(std::current_exception(), m_wallet->is_trusted_daemon()); } catch (...) { @@ -3363,22 +3361,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) return false; } - // set --trusted-daemon if local and not overridden - if (!m_trusted_daemon) - { - try - { - m_trusted_daemon = false; - if (tools::is_local_address(m_wallet->get_daemon_address())) - { - MINFO(tr("Daemon is local, assuming trusted")); - m_trusted_daemon = true; - } - } - catch (const std::exception &e) { } - } - - if (!is_daemon_trusted()) + if (!m_wallet->is_trusted_daemon()) message_writer() << (boost::format(tr("Warning: using an untrusted daemon at %s, privacy will be lessened")) % m_wallet->get_daemon_address()).str(); if (m_wallet->get_ring_database().empty()) @@ -3412,10 +3395,6 @@ bool simple_wallet::handle_command_line(const boost::program_options::variables_ m_restore_deterministic_wallet = command_line::get_arg(vm, arg_restore_deterministic_wallet); m_restore_multisig_wallet = command_line::get_arg(vm, arg_restore_multisig_wallet); m_non_deterministic = command_line::get_arg(vm, arg_non_deterministic); - if (!command_line::is_arg_defaulted(vm, arg_trusted_daemon) || !command_line::is_arg_defaulted(vm, arg_untrusted_daemon)) - m_trusted_daemon = command_line::get_arg(vm, arg_trusted_daemon) && !command_line::get_arg(vm, arg_untrusted_daemon); - if (!command_line::is_arg_defaulted(vm, arg_trusted_daemon) && !command_line::is_arg_defaulted(vm, arg_untrusted_daemon)) - message_writer() << tr("--trusted-daemon and --untrusted-daemon are both seen, assuming untrusted"); m_allow_mismatched_daemon_version = command_line::get_arg(vm, arg_allow_mismatched_daemon_version); m_restore_height = command_line::get_arg(vm, arg_restore_height); m_do_not_relay = command_line::get_arg(vm, arg_do_not_relay); @@ -3927,7 +3906,7 @@ bool simple_wallet::save_watch_only(const std::vector &args/* = std //---------------------------------------------------------------------------------------------------- bool simple_wallet::start_mining(const std::vector& args) { - if (!is_daemon_trusted()) + if (!m_wallet->is_trusted_daemon()) { fail_msg_writer() << tr("this command requires a trusted daemon. Enable with --trusted-daemon"); return true; @@ -4040,29 +4019,29 @@ bool simple_wallet::set_daemon(const std::vector& args) if (args.size() == 2) { if (args[1] == "trusted") - m_trusted_daemon = true; + m_wallet->set_trusted_daemon(true); else if (args[1] == "untrusted") - m_trusted_daemon = false; + m_wallet->set_trusted_daemon(false); else { fail_msg_writer() << tr("Expected trusted or untrusted, got ") << args[1] << ": assuming untrusted"; - m_trusted_daemon = false; + m_wallet->set_trusted_daemon(false); } } else { - m_trusted_daemon = false; + m_wallet->set_trusted_daemon(false); try { if (tools::is_local_address(m_wallet->get_daemon_address())) { MINFO(tr("Daemon is local, assuming trusted")); - m_trusted_daemon = true; + m_wallet->set_trusted_daemon(true); } } catch (const std::exception &e) { } } - success_msg_writer() << boost::format("Daemon set to %s, %s") % daemon_url % (*m_trusted_daemon ? tr("trusted") : tr("untrusted")); + success_msg_writer() << boost::format("Daemon set to %s, %s") % daemon_url % (m_wallet->is_trusted_daemon() ? tr("trusted") : tr("untrusted")); } else { fail_msg_writer() << tr("This does not seem to be a valid daemon URL."); } @@ -4198,7 +4177,7 @@ bool simple_wallet::refresh_main(uint64_t start_height, bool reset, bool is_init { m_in_manual_refresh.store(true, std::memory_order_relaxed); epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){m_in_manual_refresh.store(false, std::memory_order_relaxed);}); - m_wallet->refresh(is_daemon_trusted(), start_height, fetched_blocks); + m_wallet->refresh(m_wallet->is_trusted_daemon(), start_height, fetched_blocks); ok = true; // Clear line "Height xxx of xxx" std::cout << "\r \r"; @@ -4489,7 +4468,7 @@ bool simple_wallet::show_blockchain_height(const std::vector& args) //---------------------------------------------------------------------------------------------------- bool simple_wallet::rescan_spent(const std::vector &args) { - if (!is_daemon_trusted()) + if (!m_wallet->is_trusted_daemon()) { fail_msg_writer() << tr("this command requires a trusted daemon. Enable with --trusted-daemon"); return true; @@ -4837,16 +4816,16 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vectorcreate_transactions_2(dsts, fake_outs_count, unlock_block /* unlock_time */, priority, extra, m_current_subaddress_account, subaddr_indices, is_daemon_trusted()); + ptx_vector = m_wallet->create_transactions_2(dsts, fake_outs_count, unlock_block /* unlock_time */, priority, extra, m_current_subaddress_account, subaddr_indices); break; case TransferNew: - ptx_vector = m_wallet->create_transactions_2(dsts, fake_outs_count, 0 /* unlock_time */, priority, extra, m_current_subaddress_account, subaddr_indices, is_daemon_trusted()); + ptx_vector = m_wallet->create_transactions_2(dsts, fake_outs_count, 0 /* unlock_time */, priority, extra, m_current_subaddress_account, subaddr_indices); break; default: LOG_ERROR("Unknown transfer method, using original"); /* FALLTHRU */ case TransferOriginal: - ptx_vector = m_wallet->create_transactions(dsts, fake_outs_count, 0 /* unlock_time */, priority, extra, is_daemon_trusted()); + ptx_vector = m_wallet->create_transactions(dsts, fake_outs_count, 0 /* unlock_time */, priority, extra); break; } @@ -5022,7 +5001,7 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vectoris_trusted_daemon()); } catch (...) { @@ -5064,7 +5043,7 @@ bool simple_wallet::sweep_unmixable(const std::vector &args_) try { // figure out what tx will be necessary - auto ptx_vector = m_wallet->create_unmixable_sweep_transactions(is_daemon_trusted()); + auto ptx_vector = m_wallet->create_unmixable_sweep_transactions(); if (ptx_vector.empty()) { @@ -5143,13 +5122,13 @@ bool simple_wallet::sweep_unmixable(const std::vector &args_) { try { - m_wallet->discard_unmixable_outputs(is_daemon_trusted()); + m_wallet->discard_unmixable_outputs(); } catch (...) {} } } catch (const std::exception &e) { - handle_transfer_exception(std::current_exception(), is_daemon_trusted()); + handle_transfer_exception(std::current_exception(), m_wallet->is_trusted_daemon()); } catch (...) { @@ -5341,7 +5320,7 @@ bool simple_wallet::sweep_main(uint64_t below, bool locked, const std::vectorcreate_transactions_all(below, info.address, info.is_subaddress, fake_outs_count, unlock_block /* unlock_time */, priority, extra, m_current_subaddress_account, subaddr_indices, is_daemon_trusted()); + auto ptx_vector = m_wallet->create_transactions_all(below, info.address, info.is_subaddress, fake_outs_count, unlock_block /* unlock_time */, priority, extra, m_current_subaddress_account, subaddr_indices); if (ptx_vector.empty()) { @@ -5425,7 +5404,7 @@ bool simple_wallet::sweep_main(uint64_t below, bool locked, const std::vectoris_trusted_daemon()); } catch (...) { @@ -5554,7 +5533,7 @@ bool simple_wallet::sweep_single(const std::vector &args_) try { // figure out what tx will be necessary - auto ptx_vector = m_wallet->create_transactions_single(ki, info.address, info.is_subaddress, fake_outs_count, 0 /* unlock_time */, priority, extra, is_daemon_trusted()); + auto ptx_vector = m_wallet->create_transactions_single(ki, info.address, info.is_subaddress, fake_outs_count, 0 /* unlock_time */, priority, extra); if (ptx_vector.empty()) { @@ -5624,7 +5603,7 @@ bool simple_wallet::sweep_single(const std::vector &args_) } catch (const std::exception& e) { - handle_transfer_exception(std::current_exception(), is_daemon_trusted()); + handle_transfer_exception(std::current_exception(), m_wallet->is_trusted_daemon()); } catch (...) { @@ -5930,7 +5909,7 @@ bool simple_wallet::submit_transfer(const std::vector &args_) } catch (const std::exception& e) { - handle_transfer_exception(std::current_exception(), is_daemon_trusted()); + handle_transfer_exception(std::current_exception(), m_wallet->is_trusted_daemon()); } catch (...) { @@ -6843,7 +6822,7 @@ void simple_wallet::wallet_idle_thread() { uint64_t fetched_blocks; if (try_connect_to_daemon(true)) - m_wallet->refresh(is_daemon_trusted(), 0, fetched_blocks); + m_wallet->refresh(m_wallet->is_trusted_daemon(), 0, fetched_blocks); } catch(...) {} m_auto_refresh_refreshing = false; @@ -7591,7 +7570,7 @@ bool simple_wallet::import_key_images(const std::vector &args) fail_msg_writer() << tr("command not supported by HW wallet"); return true; } - if (!is_daemon_trusted()) + if (!m_wallet->is_trusted_daemon()) { fail_msg_writer() << tr("this command requires a trusted daemon. Enable with --trusted-daemon"); return true; @@ -7922,8 +7901,6 @@ int main(int argc, char* argv[]) command_line::add_arg(desc_params, arg_restore_multisig_wallet ); command_line::add_arg(desc_params, arg_non_deterministic ); command_line::add_arg(desc_params, arg_electrum_seed ); - command_line::add_arg(desc_params, arg_trusted_daemon); - command_line::add_arg(desc_params, arg_untrusted_daemon); command_line::add_arg(desc_params, arg_allow_mismatched_daemon_version); command_line::add_arg(desc_params, arg_restore_height); command_line::add_arg(desc_params, arg_do_not_relay); diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index 99fc19c00e2..bfbe633aca1 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -233,7 +233,6 @@ namespace cryptonote bool print_ring_members(const std::vector& ptx_vector, std::ostream& ostr); std::string get_prompt() const; bool print_seed(bool encrypted); - bool is_daemon_trusted() const { return *m_trusted_daemon; } /*! * \brief Prints the seed with a nice message @@ -337,7 +336,6 @@ namespace cryptonote bool m_restore_deterministic_wallet; // recover flag bool m_restore_multisig_wallet; // recover flag bool m_non_deterministic; // old 2-random generation - boost::optional m_trusted_daemon; bool m_allow_mismatched_daemon_version; bool m_restoring; // are we restoring, by whatever method? uint64_t m_restore_height; // optional diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index bfd0e4afffc..7b4ad27e4f9 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -369,7 +369,6 @@ void Wallet::error(const std::string &category, const std::string &str) { WalletImpl::WalletImpl(NetworkType nettype, uint64_t kdf_rounds) :m_wallet(nullptr) , m_status(Wallet::Status_Ok) - , m_trustedDaemon(false) , m_wallet2Callback(nullptr) , m_recoveringFromSeed(false) , m_recoveringFromDevice(false) @@ -1358,7 +1357,7 @@ PendingTransaction *WalletImpl::createTransaction(const string &dst_addr, const dsts.push_back(de); transaction->m_pending_tx = m_wallet->create_transactions_2(dsts, fake_outs_count, 0 /* unlock_time */, adjusted_priority, - extra, subaddr_account, subaddr_indices, m_trustedDaemon); + extra, subaddr_account, subaddr_indices); } else { // for the GUI, sweep_all (i.e. amount set as "(all)") will always sweep all the funds in all the addresses if (subaddr_indices.empty()) @@ -1368,7 +1367,7 @@ PendingTransaction *WalletImpl::createTransaction(const string &dst_addr, const } transaction->m_pending_tx = m_wallet->create_transactions_all(0, info.address, info.is_subaddress, fake_outs_count, 0 /* unlock_time */, adjusted_priority, - extra, subaddr_account, subaddr_indices, m_trustedDaemon); + extra, subaddr_account, subaddr_indices); } if (multisig().isMultisig) { @@ -1454,7 +1453,7 @@ PendingTransaction *WalletImpl::createSweepUnmixableTransaction() do { try { - transaction->m_pending_tx = m_wallet->create_unmixable_sweep_transactions(m_trustedDaemon); + transaction->m_pending_tx = m_wallet->create_unmixable_sweep_transactions(); } catch (const tools::error::daemon_busy&) { // TODO: make it translatable with "tr"? @@ -1891,12 +1890,12 @@ Wallet::ConnectionStatus WalletImpl::connected() const void WalletImpl::setTrustedDaemon(bool arg) { - m_trustedDaemon = arg; + m_wallet->set_trusted_daemon(arg); } bool WalletImpl::trustedDaemon() const { - return m_trustedDaemon; + return m_wallet->is_trusted_daemon(); } bool WalletImpl::watchOnly() const diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h index 9218d3ad51f..0f3b1ce040c 100644 --- a/src/wallet/api/wallet.h +++ b/src/wallet/api/wallet.h @@ -219,7 +219,6 @@ class WalletImpl : public Wallet mutable std::string m_errorString; std::string m_password; TransactionHistoryImpl * m_history; - bool m_trustedDaemon; Wallet2CallbackImpl * m_wallet2Callback; AddressBookImpl * m_addressBook; SubaddressImpl * m_subaddress; diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 96b77a7a6ec..5e764579c73 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -140,6 +140,8 @@ namespace struct options { const command_line::arg_descriptor daemon_address = {"daemon-address", tools::wallet2::tr("Use daemon instance at :"), ""}; const command_line::arg_descriptor daemon_host = {"daemon-host", tools::wallet2::tr("Use daemon instance at host instead of localhost"), ""}; + const command_line::arg_descriptor trusted_daemon = {"trusted-daemon", tools::wallet2::tr("Enable commands which rely on a trusted daemon"), false}; + const command_line::arg_descriptor untrusted_daemon = {"untrusted-daemon", tools::wallet2::tr("Disable commands which rely on a trusted daemon"), false}; const command_line::arg_descriptor password = {"password", tools::wallet2::tr("Wallet password (escape/quote as needed)"), "", true}; const command_line::arg_descriptor password_file = {"password-file", tools::wallet2::tr("Wallet password file"), "", true}; const command_line::arg_descriptor daemon_port = {"daemon-port", tools::wallet2::tr("Use daemon instance at port instead of 18081"), 0}; @@ -236,8 +238,29 @@ std::unique_ptr make_basic(const boost::program_options::variabl if (daemon_address.empty()) daemon_address = std::string("http://") + daemon_host + ":" + std::to_string(daemon_port); + boost::optional trusted_daemon; + if (!command_line::is_arg_defaulted(vm, opts.trusted_daemon) || !command_line::is_arg_defaulted(vm, opts.untrusted_daemon)) + trusted_daemon = command_line::get_arg(vm, opts.trusted_daemon) && !command_line::get_arg(vm, opts.untrusted_daemon); + THROW_WALLET_EXCEPTION_IF(!command_line::is_arg_defaulted(vm, opts.trusted_daemon) && !command_line::is_arg_defaulted(vm, opts.untrusted_daemon), + tools::error::wallet_internal_error, tools::wallet2::tr("--trusted-daemon and --untrusted-daemon are both seen, assuming untrusted")); + + // set --trusted-daemon if local and not overridden + if (!trusted_daemon) + { + try + { + trusted_daemon = false; + if (tools::is_local_address(daemon_address)) + { + MINFO(tr("Daemon is local, assuming trusted")); + trusted_daemon = true; + } + } + catch (const std::exception &e) { } + } + std::unique_ptr wallet(new tools::wallet2(nettype, kdf_rounds)); - wallet->init(rpc, std::move(daemon_address), std::move(login)); + wallet->init(rpc, std::move(daemon_address), std::move(login), 0, false, *trusted_daemon); boost::filesystem::path ringdb_path = command_line::get_arg(vm, opts.shared_ringdb_dir); wallet->set_ring_database(ringdb_path.string()); return wallet; @@ -680,6 +703,7 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds): m_multisig_rescan_k(NULL), m_run(true), m_callback(0), + m_trusted_daemon(false), m_nettype(nettype), m_always_confirm_transfers(true), m_print_ring_members(false), @@ -745,6 +769,8 @@ void wallet2::init_options(boost::program_options::options_description& desc_par const options opts{}; command_line::add_arg(desc_params, opts.daemon_address); command_line::add_arg(desc_params, opts.daemon_host); + command_line::add_arg(desc_params, opts.trusted_daemon); + command_line::add_arg(desc_params, opts.untrusted_daemon); command_line::add_arg(desc_params, opts.password); command_line::add_arg(desc_params, opts.password_file); command_line::add_arg(desc_params, opts.daemon_port); @@ -796,7 +822,7 @@ std::unique_ptr wallet2::make_dummy(const boost::program_options::varia } //---------------------------------------------------------------------------------------------------- -bool wallet2::init(bool rpc, std::string daemon_address, boost::optional daemon_login, uint64_t upper_transaction_size_limit, bool ssl) +bool wallet2::init(bool rpc, std::string daemon_address, boost::optional daemon_login, uint64_t upper_transaction_size_limit, bool ssl, bool trusted_daemon) { m_rpc = rpc; m_checkpoints.init_default_checkpoints(m_nettype); @@ -806,6 +832,7 @@ bool wallet2::init(bool rpc, std::string daemon_address, boost::optional &unused_indices, const std::v // returns: // direct return: amount of money found // modified reference: selected_transfers, a list of iterators/indices of input sources -uint64_t wallet2::select_transfers(uint64_t needed_money, std::vector unused_transfers_indices, std::vector& selected_transfers, bool trusted_daemon) const +uint64_t wallet2::select_transfers(uint64_t needed_money, std::vector unused_transfers_indices, std::vector& selected_transfers) const { uint64_t found_money = 0; selected_transfers.reserve(unused_transfers_indices.size()); @@ -4945,17 +4972,17 @@ void wallet2::add_unconfirmed_tx(const cryptonote::transaction& tx, uint64_t amo //---------------------------------------------------------------------------------------------------- void wallet2::transfer(const std::vector& dsts, const size_t fake_outs_count, const std::vector &unused_transfers_indices, - uint64_t unlock_time, uint64_t fee, const std::vector& extra, cryptonote::transaction& tx, pending_tx& ptx, bool trusted_daemon) + uint64_t unlock_time, uint64_t fee, const std::vector& extra, cryptonote::transaction& tx, pending_tx& ptx) { - transfer(dsts, fake_outs_count, unused_transfers_indices, unlock_time, fee, extra, detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), tx, ptx, trusted_daemon); + transfer(dsts, fake_outs_count, unused_transfers_indices, unlock_time, fee, extra, detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), tx, ptx); } //---------------------------------------------------------------------------------------------------- void wallet2::transfer(const std::vector& dsts, const size_t fake_outs_count, const std::vector &unused_transfers_indices, - uint64_t unlock_time, uint64_t fee, const std::vector& extra, bool trusted_daemon) + uint64_t unlock_time, uint64_t fee, const std::vector& extra) { cryptonote::transaction tx; pending_tx ptx; - transfer(dsts, fake_outs_count, unused_transfers_indices, unlock_time, fee, extra, tx, ptx, trusted_daemon); + transfer(dsts, fake_outs_count, unused_transfers_indices, unlock_time, fee, extra, tx, ptx); } namespace { @@ -5941,9 +5968,9 @@ uint32_t wallet2::adjust_priority(uint32_t priority) // // this function will make multiple calls to wallet2::transfer if multiple // transactions will be required -std::vector wallet2::create_transactions(std::vector dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector& extra, bool trusted_daemon) +std::vector wallet2::create_transactions(std::vector dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector& extra) { - const std::vector unused_transfers_indices = select_available_outputs_from_histogram(fake_outs_count + 1, true, true, true, trusted_daemon); + const std::vector unused_transfers_indices = select_available_outputs_from_histogram(fake_outs_count + 1, true, true, true); const uint64_t fee_per_kb = get_per_kb_fee(); const uint64_t fee_multiplier = get_fee_multiplier(priority, get_fee_algorithm()); @@ -5977,7 +6004,7 @@ std::vector wallet2::create_transactions(std::vector wallet2::create_transactions_2(std::vector dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector& extra, uint32_t subaddr_account, std::set subaddr_indices, bool trusted_daemon) +std::vector wallet2::create_transactions_2(std::vector dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector& extra, uint32_t subaddr_account, std::set subaddr_indices) { //ensure device is let in NONE mode in any case hw::device &hwdev = m_account.get_device(); @@ -8476,7 +8503,7 @@ std::vector wallet2::create_transactions_2(std::vector wallet2::create_transactions_all(uint64_t below, const cryptonote::account_public_address &address, bool is_subaddress, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector& extra, uint32_t subaddr_account, std::set subaddr_indices, bool trusted_daemon) +std::vector wallet2::create_transactions_all(uint64_t below, const cryptonote::account_public_address &address, bool is_subaddress, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector& extra, uint32_t subaddr_account, std::set subaddr_indices) { std::vector unused_transfers_indices; std::vector unused_dust_indices; @@ -8527,10 +8554,10 @@ std::vector wallet2::create_transactions_all(uint64_t below } } - return create_transactions_from(address, is_subaddress, unused_transfers_indices, unused_dust_indices, fake_outs_count, unlock_time, priority, extra, trusted_daemon); + return create_transactions_from(address, is_subaddress, unused_transfers_indices, unused_dust_indices, fake_outs_count, unlock_time, priority, extra); } -std::vector wallet2::create_transactions_single(const crypto::key_image &ki, const cryptonote::account_public_address &address, bool is_subaddress, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector& extra, bool trusted_daemon) +std::vector wallet2::create_transactions_single(const crypto::key_image &ki, const cryptonote::account_public_address &address, bool is_subaddress, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector& extra) { std::vector unused_transfers_indices; std::vector unused_dust_indices; @@ -8548,10 +8575,10 @@ std::vector wallet2::create_transactions_single(const crypt break; } } - return create_transactions_from(address, is_subaddress, unused_transfers_indices, unused_dust_indices, fake_outs_count, unlock_time, priority, extra, trusted_daemon); + return create_transactions_from(address, is_subaddress, unused_transfers_indices, unused_dust_indices, fake_outs_count, unlock_time, priority, extra); } -std::vector wallet2::create_transactions_from(const cryptonote::account_public_address &address, bool is_subaddress, std::vector unused_transfers_indices, std::vector unused_dust_indices, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector& extra, bool trusted_daemon) +std::vector wallet2::create_transactions_from(const cryptonote::account_public_address &address, bool is_subaddress, std::vector unused_transfers_indices, std::vector unused_dust_indices, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector& extra) { //ensure device is let in NONE mode in any case hw::device &hwdev = m_account.get_device(); @@ -8785,12 +8812,12 @@ std::vector wallet2::get_unspent_amounts_vector() const return vector; } //---------------------------------------------------------------------------------------------------- -std::vector wallet2::select_available_outputs_from_histogram(uint64_t count, bool atleast, bool unlocked, bool allow_rct, bool trusted_daemon) +std::vector wallet2::select_available_outputs_from_histogram(uint64_t count, bool atleast, bool unlocked, bool allow_rct) { cryptonote::COMMAND_RPC_GET_OUTPUT_HISTOGRAM::request req_t = AUTO_VAL_INIT(req_t); cryptonote::COMMAND_RPC_GET_OUTPUT_HISTOGRAM::response resp_t = AUTO_VAL_INIT(resp_t); m_daemon_rpc_mutex.lock(); - if (trusted_daemon) + if (is_trusted_daemon()) req_t.amounts = get_unspent_amounts_vector(); req_t.min_count = count; req_t.max_count = 0; @@ -8851,21 +8878,21 @@ const wallet2::transfer_details &wallet2::get_transfer_details(size_t idx) const return m_transfers[idx]; } //---------------------------------------------------------------------------------------------------- -std::vector wallet2::select_available_unmixable_outputs(bool trusted_daemon) +std::vector wallet2::select_available_unmixable_outputs() { // request all outputs with less than 3 instances const size_t min_mixin = use_fork_rules(7, 10) ? 6 : use_fork_rules(6, 10) ? 4 : 2; // v6 increases min mixin from 2 to 4, v7 to 6 - return select_available_outputs_from_histogram(min_mixin + 1, false, true, false, trusted_daemon); + return select_available_outputs_from_histogram(min_mixin + 1, false, true, false); } //---------------------------------------------------------------------------------------------------- -std::vector wallet2::select_available_mixable_outputs(bool trusted_daemon) +std::vector wallet2::select_available_mixable_outputs() { // request all outputs with at least 3 instances, so we can use mixin 2 with const size_t min_mixin = use_fork_rules(7, 10) ? 6 : use_fork_rules(6, 10) ? 4 : 2; // v6 increases min mixin from 2 to 4, v7 to 6 - return select_available_outputs_from_histogram(min_mixin + 1, true, true, true, trusted_daemon); + return select_available_outputs_from_histogram(min_mixin + 1, true, true, true); } //---------------------------------------------------------------------------------------------------- -std::vector wallet2::create_unmixable_sweep_transactions(bool trusted_daemon) +std::vector wallet2::create_unmixable_sweep_transactions() { // From hard fork 1, we don't consider small amounts to be dust anymore const bool hf1_rules = use_fork_rules(2, 10); // first hard fork has version 2 @@ -8874,7 +8901,7 @@ std::vector wallet2::create_unmixable_sweep_transactions(bo const uint64_t fee_per_kb = get_per_kb_fee(); // may throw - std::vector unmixable_outputs = select_available_unmixable_outputs(trusted_daemon); + std::vector unmixable_outputs = select_available_unmixable_outputs(); size_t num_dust_outputs = unmixable_outputs.size(); if (num_dust_outputs == 0) @@ -8892,13 +8919,13 @@ std::vector wallet2::create_unmixable_sweep_transactions(bo unmixable_transfer_outputs.push_back(n); } - return create_transactions_from(m_account_public_address, false, unmixable_transfer_outputs, unmixable_dust_outputs, 0 /*fake_outs_count */, 0 /* unlock_time */, 1 /*priority */, std::vector(), trusted_daemon); + return create_transactions_from(m_account_public_address, false, unmixable_transfer_outputs, unmixable_dust_outputs, 0 /*fake_outs_count */, 0 /* unlock_time */, 1 /*priority */, std::vector()); } //---------------------------------------------------------------------------------------------------- -void wallet2::discard_unmixable_outputs(bool trusted_daemon) +void wallet2::discard_unmixable_outputs() { // may throw - std::vector unmixable_outputs = select_available_unmixable_outputs(trusted_daemon); + std::vector unmixable_outputs = select_available_unmixable_outputs(); for (size_t idx : unmixable_outputs) { m_transfers[idx].m_spent = true; diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 2d45f4e3e8a..556679f511d 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -637,13 +637,16 @@ namespace tools // the minimum block size. bool deinit(); bool init(bool rpc, std::string daemon_address = "http://localhost:8080", - boost::optional daemon_login = boost::none, uint64_t upper_transaction_size_limit = 0, bool ssl = false); + boost::optional daemon_login = boost::none, uint64_t upper_transaction_size_limit = 0, bool ssl = false, bool trusted_daemon = false); void stop() { m_run.store(false, std::memory_order_relaxed); } i_wallet2_callback* callback() const { return m_callback; } void callback(i_wallet2_callback* callback) { m_callback = callback; } + bool is_trusted_daemon() const { return m_trusted_daemon; } + void set_trusted_daemon(bool trusted) { m_trusted_daemon = trusted; } + /*! * \brief Checks if deterministic wallet */ @@ -715,11 +718,11 @@ namespace tools uint64_t balance_all() const; uint64_t unlocked_balance_all() const; template - void transfer(const std::vector& dsts, const size_t fake_outputs_count, const std::vector &unused_transfers_indices, uint64_t unlock_time, uint64_t fee, const std::vector& extra, T destination_split_strategy, const tx_dust_policy& dust_policy, bool trusted_daemon); + void transfer(const std::vector& dsts, const size_t fake_outputs_count, const std::vector &unused_transfers_indices, uint64_t unlock_time, uint64_t fee, const std::vector& extra, T destination_split_strategy, const tx_dust_policy& dust_policy); template - void transfer(const std::vector& dsts, const size_t fake_outputs_count, const std::vector &unused_transfers_indices, uint64_t unlock_time, uint64_t fee, const std::vector& extra, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction& tx, pending_tx& ptx, bool trusted_daemon); - void transfer(const std::vector& dsts, const size_t fake_outputs_count, const std::vector &unused_transfers_indices, uint64_t unlock_time, uint64_t fee, const std::vector& extra, bool trusted_daemon); - void transfer(const std::vector& dsts, const size_t fake_outputs_count, const std::vector &unused_transfers_indices, uint64_t unlock_time, uint64_t fee, const std::vector& extra, cryptonote::transaction& tx, pending_tx& ptx, bool trusted_daemon); + void transfer(const std::vector& dsts, const size_t fake_outputs_count, const std::vector &unused_transfers_indices, uint64_t unlock_time, uint64_t fee, const std::vector& extra, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction& tx, pending_tx& ptx); + void transfer(const std::vector& dsts, const size_t fake_outputs_count, const std::vector &unused_transfers_indices, uint64_t unlock_time, uint64_t fee, const std::vector& extra); + void transfer(const std::vector& dsts, const size_t fake_outputs_count, const std::vector &unused_transfers_indices, uint64_t unlock_time, uint64_t fee, const std::vector& extra, cryptonote::transaction& tx, pending_tx& ptx); template void transfer_selected(const std::vector& dsts, const std::vector& selected_transfers, size_t fake_outputs_count, std::vector> &outs, @@ -748,18 +751,18 @@ namespace tools bool parse_unsigned_tx_from_str(const std::string &unsigned_tx_st, unsigned_tx_set &exported_txs) const; bool load_tx(const std::string &signed_filename, std::vector &ptx, std::function accept_func = NULL); bool parse_tx_from_str(const std::string &signed_tx_st, std::vector &ptx, std::function accept_func); - std::vector create_transactions(std::vector dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector& extra, bool trusted_daemon); - std::vector create_transactions_2(std::vector dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector& extra, uint32_t subaddr_account, std::set subaddr_indices, bool trusted_daemon); // pass subaddr_indices by value on purpose - std::vector create_transactions_all(uint64_t below, const cryptonote::account_public_address &address, bool is_subaddress, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector& extra, uint32_t subaddr_account, std::set subaddr_indices, bool trusted_daemon); - std::vector create_transactions_single(const crypto::key_image &ki, const cryptonote::account_public_address &address, bool is_subaddress, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector& extra, bool trusted_daemon); - std::vector create_transactions_from(const cryptonote::account_public_address &address, bool is_subaddress, std::vector unused_transfers_indices, std::vector unused_dust_indices, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector& extra, bool trusted_daemon); + std::vector create_transactions(std::vector dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector& extra); + std::vector create_transactions_2(std::vector dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector& extra, uint32_t subaddr_account, std::set subaddr_indices); // pass subaddr_indices by value on purpose + std::vector create_transactions_all(uint64_t below, const cryptonote::account_public_address &address, bool is_subaddress, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector& extra, uint32_t subaddr_account, std::set subaddr_indices); + std::vector create_transactions_single(const crypto::key_image &ki, const cryptonote::account_public_address &address, bool is_subaddress, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector& extra); + std::vector create_transactions_from(const cryptonote::account_public_address &address, bool is_subaddress, std::vector unused_transfers_indices, std::vector unused_dust_indices, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector& extra); bool load_multisig_tx(cryptonote::blobdata blob, multisig_tx_set &exported_txs, std::function accept_func = NULL); bool load_multisig_tx_from_file(const std::string &filename, multisig_tx_set &exported_txs, std::function accept_func = NULL); bool sign_multisig_tx_from_file(const std::string &filename, std::vector &txids, std::function accept_func); bool sign_multisig_tx(multisig_tx_set &exported_txs, std::vector &txids); bool sign_multisig_tx_to_file(multisig_tx_set &exported_txs, const std::string &filename, std::vector &txids); - std::vector create_unmixable_sweep_transactions(bool trusted_daemon); - void discard_unmixable_outputs(bool trusted_daemon); + std::vector create_unmixable_sweep_transactions(); + void discard_unmixable_outputs(); bool check_connection(uint32_t *version = NULL, uint32_t timeout = 200000); void get_transfers(wallet2::transfer_container& incoming_transfers) const; void get_payments(const crypto::hash& payment_id, std::list& payments, uint64_t min_height = 0, const boost::optional& subaddr_account = boost::none, const std::set& subaddr_indices = {}) const; @@ -996,10 +999,10 @@ namespace tools */ uint64_t get_approximate_blockchain_height() const; uint64_t estimate_blockchain_height(); - std::vector select_available_outputs_from_histogram(uint64_t count, bool atleast, bool unlocked, bool allow_rct, bool trusted_daemon); + std::vector select_available_outputs_from_histogram(uint64_t count, bool atleast, bool unlocked, bool allow_rct); std::vector select_available_outputs(const std::function &f) const; - std::vector select_available_unmixable_outputs(bool trusted_daemon); - std::vector select_available_mixable_outputs(bool trusted_daemon); + std::vector select_available_unmixable_outputs(); + std::vector select_available_mixable_outputs(); size_t pop_best_value_from(const transfer_container &transfers, std::vector &unused_dust_indices, const std::vector& selected_transfers, bool smallest = false) const; size_t pop_best_value(std::vector &unused_dust_indices, const std::vector& selected_transfers, bool smallest = false) const; @@ -1195,7 +1198,7 @@ namespace tools void fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height, std::list &short_chain_history, bool force = false); void pull_and_parse_next_blocks(uint64_t start_height, uint64_t &blocks_start_height, std::list &short_chain_history, const std::vector &prev_blocks, const std::vector &prev_parsed_blocks, std::vector &blocks, std::vector &parsed_blocks, bool &error); void process_parsed_blocks(uint64_t start_height, const std::vector &blocks, const std::vector &parsed_blocks, uint64_t& blocks_added); - uint64_t select_transfers(uint64_t needed_money, std::vector unused_transfers_indices, std::vector& selected_transfers, bool trusted_daemon) const; + uint64_t select_transfers(uint64_t needed_money, std::vector unused_transfers_indices, std::vector& selected_transfers) const; bool prepare_file_names(const std::string& file_path); void process_unconfirmed(const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t height); void process_outgoing(const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t height, uint64_t ts, uint64_t spent, uint64_t received, uint32_t subaddr_account, const std::set& subaddr_indices); @@ -1274,6 +1277,7 @@ namespace tools boost::mutex m_daemon_rpc_mutex; + bool m_trusted_daemon; i_wallet2_callback* m_callback; bool m_key_on_device; cryptonote::network_type m_nettype; @@ -1817,16 +1821,16 @@ namespace tools //---------------------------------------------------------------------------------------------------- template void wallet2::transfer(const std::vector& dsts, const size_t fake_outs_count, const std::vector &unused_transfers_indices, - uint64_t unlock_time, uint64_t fee, const std::vector& extra, T destination_split_strategy, const tx_dust_policy& dust_policy, bool trusted_daemon) + uint64_t unlock_time, uint64_t fee, const std::vector& extra, T destination_split_strategy, const tx_dust_policy& dust_policy) { pending_tx ptx; cryptonote::transaction tx; - transfer(dsts, fake_outs_count, unused_transfers_indices, unlock_time, fee, extra, destination_split_strategy, dust_policy, tx, ptx, trusted_daemon); + transfer(dsts, fake_outs_count, unused_transfers_indices, unlock_time, fee, extra, destination_split_strategy, dust_policy, tx, ptx); } template void wallet2::transfer(const std::vector& dsts, const size_t fake_outputs_count, const std::vector &unused_transfers_indices, - uint64_t unlock_time, uint64_t fee, const std::vector& extra, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction& tx, pending_tx &ptx, bool trusted_daemon) + uint64_t unlock_time, uint64_t fee, const std::vector& extra, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction& tx, pending_tx &ptx) { using namespace cryptonote; // throw if attempting a transaction with no destinations @@ -1849,7 +1853,7 @@ namespace tools // randomly select inputs for transaction // throw if requested send amount is greater than (unlocked) amount available to send std::vector selected_transfers; - uint64_t found_money = select_transfers(needed_money, unused_transfers_indices, selected_transfers, trusted_daemon); + uint64_t found_money = select_transfers(needed_money, unused_transfers_indices, selected_transfers); THROW_WALLET_EXCEPTION_IF(found_money < needed_money, error::not_enough_unlocked_money, found_money, needed_money - fee, fee); uint32_t subaddr_account = m_transfers[*selected_transfers.begin()].m_subaddr_index.major; diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index 2cddea25d17..72b8cd98f01 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -59,7 +59,6 @@ namespace { const command_line::arg_descriptor arg_rpc_bind_port = {"rpc-bind-port", "Sets bind port for server"}; const command_line::arg_descriptor arg_disable_rpc_login = {"disable-rpc-login", "Disable HTTP authentication for RPC connections served by this process"}; - const command_line::arg_descriptor arg_trusted_daemon = {"trusted-daemon", "Enable commands which rely on a trusted daemon", false}; const command_line::arg_descriptor arg_restricted = {"restricted-rpc", "Restricts to view-only commands", false}; const command_line::arg_descriptor arg_wallet_dir = {"wallet-dir", "Directory for newly created wallets"}; const command_line::arg_descriptor arg_prompt_for_password = {"prompt-for-password", "Prompts for password when not provided", false}; @@ -100,7 +99,7 @@ namespace tools } //------------------------------------------------------------------------------------------------------------------------------ - wallet_rpc_server::wallet_rpc_server():m_wallet(NULL), rpc_login_file(), m_stop(false), m_trusted_daemon(false), m_restricted(false), m_vm(NULL) + wallet_rpc_server::wallet_rpc_server():m_wallet(NULL), rpc_login_file(), m_stop(false), m_restricted(false), m_vm(NULL) { } //------------------------------------------------------------------------------------------------------------------------------ @@ -120,7 +119,7 @@ namespace tools m_stop = false; m_net_server.add_idle_handler([this](){ try { - if (m_wallet) m_wallet->refresh(m_trusted_daemon); + if (m_wallet) m_wallet->refresh(m_wallet->is_trusted_daemon()); } catch (const std::exception& ex) { LOG_ERROR("Exception at while refreshing, what=" << ex.what()); } @@ -169,15 +168,6 @@ namespace tools boost::optional http_login{}; std::string bind_port = command_line::get_arg(*m_vm, arg_rpc_bind_port); const bool disable_auth = command_line::get_arg(*m_vm, arg_disable_rpc_login); - m_trusted_daemon = command_line::get_arg(*m_vm, arg_trusted_daemon); - if (!command_line::has_arg(*m_vm, arg_trusted_daemon)) - { - if (tools::is_local_address(walvars->get_daemon_address())) - { - MINFO(tr("Daemon is local, assuming trusted")); - m_trusted_daemon = true; - } - } m_restricted = command_line::get_arg(*m_vm, arg_restricted); if (command_line::has_arg(*m_vm, arg_wallet_dir)) { @@ -857,7 +847,7 @@ namespace tools mixin = m_wallet->adjust_mixin(req.mixin); } uint32_t priority = m_wallet->adjust_priority(req.priority); - std::vector ptx_vector = m_wallet->create_transactions_2(dsts, mixin, req.unlock_time, priority, extra, req.account_index, req.subaddr_indices, m_trusted_daemon); + std::vector ptx_vector = m_wallet->create_transactions_2(dsts, mixin, req.unlock_time, priority, extra, req.account_index, req.subaddr_indices); if (ptx_vector.empty()) { @@ -918,7 +908,7 @@ namespace tools } uint32_t priority = m_wallet->adjust_priority(req.priority); LOG_PRINT_L2("on_transfer_split calling create_transactions_2"); - std::vector ptx_vector = m_wallet->create_transactions_2(dsts, mixin, req.unlock_time, priority, extra, req.account_index, req.subaddr_indices, m_trusted_daemon); + std::vector ptx_vector = m_wallet->create_transactions_2(dsts, mixin, req.unlock_time, priority, extra, req.account_index, req.subaddr_indices); LOG_PRINT_L2("on_transfer_split called create_transactions_2"); return fill_response(ptx_vector, req.get_tx_keys, res.tx_key_list, res.amount_list, res.fee_list, res.multisig_txset, res.unsigned_txset, req.do_not_relay, @@ -1079,7 +1069,7 @@ namespace tools try { - std::vector ptx_vector = m_wallet->create_unmixable_sweep_transactions(m_trusted_daemon); + std::vector ptx_vector = m_wallet->create_unmixable_sweep_transactions(); return fill_response(ptx_vector, req.get_tx_keys, res.tx_key_list, res.amount_list, res.fee_list, res.multisig_txset, res.unsigned_txset, req.do_not_relay, res.tx_hash_list, req.get_tx_hex, res.tx_blob_list, req.get_tx_metadata, res.tx_metadata_list, er); @@ -1127,7 +1117,7 @@ namespace tools mixin = m_wallet->adjust_mixin(req.mixin); } uint32_t priority = m_wallet->adjust_priority(req.priority); - std::vector ptx_vector = m_wallet->create_transactions_all(req.below_amount, dsts[0].addr, dsts[0].is_subaddress, mixin, req.unlock_time, priority, extra, req.account_index, req.subaddr_indices, m_trusted_daemon); + std::vector ptx_vector = m_wallet->create_transactions_all(req.below_amount, dsts[0].addr, dsts[0].is_subaddress, mixin, req.unlock_time, priority, extra, req.account_index, req.subaddr_indices); return fill_response(ptx_vector, req.get_tx_keys, res.tx_key_list, res.amount_list, res.fee_list, res.multisig_txset, res.unsigned_txset, req.do_not_relay, res.tx_hash_list, req.get_tx_hex, res.tx_blob_list, req.get_tx_metadata, res.tx_metadata_list, er); @@ -1183,7 +1173,7 @@ namespace tools mixin = m_wallet->adjust_mixin(req.mixin); } uint32_t priority = m_wallet->adjust_priority(req.priority); - std::vector ptx_vector = m_wallet->create_transactions_single(ki, dsts[0].addr, dsts[0].is_subaddress, mixin, req.unlock_time, priority, extra, m_trusted_daemon); + std::vector ptx_vector = m_wallet->create_transactions_single(ki, dsts[0].addr, dsts[0].is_subaddress, mixin, req.unlock_time, priority, extra); if (ptx_vector.empty()) { @@ -2304,7 +2294,7 @@ namespace tools er.message = "Command unavailable in restricted mode."; return false; } - if (!m_trusted_daemon) + if (!m_wallet->is_trusted_daemon()) { er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; er.message = "This command requires a trusted daemon."; @@ -2518,7 +2508,7 @@ namespace tools } try { - m_wallet->refresh(m_trusted_daemon, req.start_height, res.blocks_fetched, res.received_money); + m_wallet->refresh(m_wallet->is_trusted_daemon(), req.start_height, res.blocks_fetched, res.received_money); return true; } catch (const std::exception& e) @@ -2554,7 +2544,7 @@ namespace tools bool wallet_rpc_server::on_start_mining(const wallet_rpc::COMMAND_RPC_START_MINING::request& req, wallet_rpc::COMMAND_RPC_START_MINING::response& res, epee::json_rpc::error& er) { if (!m_wallet) return not_open(er); - if (!m_trusted_daemon) + if (!m_wallet->is_trusted_daemon()) { er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; er.message = "This command requires a trusted daemon."; @@ -2993,7 +2983,7 @@ namespace tools return false; } - if (m_trusted_daemon) + if (m_wallet->is_trusted_daemon()) { try { @@ -3216,7 +3206,6 @@ int main(int argc, char** argv) { tools::wallet2::init_options(desc_params); command_line::add_arg(desc_params, arg_rpc_bind_port); command_line::add_arg(desc_params, arg_disable_rpc_login); - command_line::add_arg(desc_params, arg_trusted_daemon); command_line::add_arg(desc_params, arg_restricted); cryptonote::rpc_args::init_options(desc_params); command_line::add_arg(desc_params, arg_wallet_file); @@ -3309,7 +3298,7 @@ int main(int argc, char** argv) { wal->stop(); }); - wal->refresh(command_line::get_arg(*vm, arg_trusted_daemon)); + wal->refresh(wal->is_trusted_daemon()); // if we ^C during potentially length load/refresh, there's no server loop yet if (quit) { diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h index bfe49a4591b..d8c293fa078 100644 --- a/src/wallet/wallet_rpc_server.h +++ b/src/wallet/wallet_rpc_server.h @@ -238,7 +238,6 @@ namespace tools std::string m_wallet_dir; tools::private_file rpc_login_file; std::atomic m_stop; - bool m_trusted_daemon; bool m_restricted; const boost::program_options::variables_map *m_vm; }; diff --git a/tests/functional_tests/transactions_flow_test.cpp b/tests/functional_tests/transactions_flow_test.cpp index 03bfc0a9b55..7248efade1e 100644 --- a/tests/functional_tests/transactions_flow_test.cpp +++ b/tests/functional_tests/transactions_flow_test.cpp @@ -86,7 +86,7 @@ bool do_send_money(tools::wallet2& w1, tools::wallet2& w2, size_t mix_in_factor, { tools::wallet2::pending_tx ptx; std::vector indices = w1.select_available_outputs([](const tools::wallet2::transfer_details&) { return true; }); - w1.transfer(dsts, mix_in_factor, indices, 0, TEST_FEE, std::vector(), tools::detail::null_split_strategy, tools::tx_dust_policy(TEST_DUST_THRESHOLD), tx, ptx, true); + w1.transfer(dsts, mix_in_factor, indices, 0, TEST_FEE, std::vector(), tools::detail::null_split_strategy, tools::tx_dust_policy(TEST_DUST_THRESHOLD), tx, ptx); w1.commit_tx(ptx); return true; } From d2e26c23f3caa7928c46c3a6bded85fdc1a68cb3 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 13 Jun 2018 18:23:06 +0100 Subject: [PATCH 0274/1404] add and use constant time 32 byte equality function --- README.md | 2 +- src/crypto/CMakeLists.txt | 1 + src/crypto/crypto.h | 2 +- src/crypto/generic-ops.h | 28 ++++++++++-- src/device/device_ledger.cpp | 3 +- src/ringct/rctTypes.h | 15 ++++--- tests/performance_tests/equality.h | 72 ++++++++++++++++++++++++++++++ tests/performance_tests/main.cpp | 6 +++ tests/unit_tests/crypto.cpp | 15 +++++++ 9 files changed, 130 insertions(+), 14 deletions(-) create mode 100644 tests/performance_tests/equality.h diff --git a/README.md b/README.md index f4e034d0cb7..ee7c112e783 100644 --- a/README.md +++ b/README.md @@ -139,7 +139,7 @@ library archives (`.a`). | libzmq | 3.0.0 | NO | `libzmq3-dev` | `zeromq` | `cppzmq-devel` | NO | ZeroMQ library | | OpenPGM | ? | NO | `libpgm-dev` | `libpgm` | `openpgm-devel` | NO | For ZeroMQ | | libunbound | 1.4.16 | YES | `libunbound-dev` | `unbound` | `unbound-devel` | NO | DNS resolver | -| libsodium | ? | NO | `libsodium-dev` | ? | `libsodium-devel` | NO | libsodium | +| libsodium | ? | NO | `libsodium-dev` | ? | `libsodium-devel` | NO | cryptography | | libunwind | any | NO | `libunwind8-dev` | `libunwind` | `libunwind-devel` | YES | Stack traces | | liblzma | any | NO | `liblzma-dev` | `xz` | `xz-devel` | YES | For libunwind | | libreadline | 6.3.0 | NO | `libreadline6-dev` | `readline` | `readline-devel` | YES | Input editing | diff --git a/src/crypto/CMakeLists.txt b/src/crypto/CMakeLists.txt index 71dcedcab32..0c635e7cba8 100644 --- a/src/crypto/CMakeLists.txt +++ b/src/crypto/CMakeLists.txt @@ -78,6 +78,7 @@ target_link_libraries(cncrypto PUBLIC epee ${Boost_SYSTEM_LIBRARY} + ${SODIUM_LIBRARY} PRIVATE ${EXTRA_LIBRARIES}) diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h index c1576a218b6..33cc0a25a3b 100644 --- a/src/crypto/crypto.h +++ b/src/crypto/crypto.h @@ -283,6 +283,6 @@ namespace crypto { } CRYPTO_MAKE_HASHABLE(public_key) -CRYPTO_MAKE_HASHABLE(secret_key) +CRYPTO_MAKE_HASHABLE_CONSTANT_TIME(secret_key) CRYPTO_MAKE_HASHABLE(key_image) CRYPTO_MAKE_COMPARABLE(signature) diff --git a/src/crypto/generic-ops.h b/src/crypto/generic-ops.h index 62bc758c9d7..42b98706e9a 100644 --- a/src/crypto/generic-ops.h +++ b/src/crypto/generic-ops.h @@ -33,19 +33,30 @@ #include #include #include +#include #define CRYPTO_MAKE_COMPARABLE(type) \ namespace crypto { \ inline bool operator==(const type &_v1, const type &_v2) { \ - return std::memcmp(&_v1, &_v2, sizeof(type)) == 0; \ + return !memcmp(&_v1, &_v2, sizeof(_v1)); \ } \ inline bool operator!=(const type &_v1, const type &_v2) { \ - return std::memcmp(&_v1, &_v2, sizeof(type)) != 0; \ + return !operator==(_v1, _v2); \ } \ } -#define CRYPTO_MAKE_HASHABLE(type) \ -CRYPTO_MAKE_COMPARABLE(type) \ +#define CRYPTO_MAKE_COMPARABLE_CONSTANT_TIME(type) \ +namespace crypto { \ + inline bool operator==(const type &_v1, const type &_v2) { \ + static_assert(sizeof(_v1) == 32, "constant time comparison is only implenmted for 32 bytes"); \ + return crypto_verify_32((const unsigned char*)&_v1, (const unsigned char*)&_v2) == 0; \ + } \ + inline bool operator!=(const type &_v1, const type &_v2) { \ + return !operator==(_v1, _v2); \ + } \ +} + +#define CRYPTO_DEFINE_HASH_FUNCTIONS(type) \ namespace crypto { \ static_assert(sizeof(std::size_t) <= sizeof(type), "Size of " #type " must be at least that of size_t"); \ inline std::size_t hash_value(const type &_v) { \ @@ -60,3 +71,12 @@ namespace std { \ } \ }; \ } + +#define CRYPTO_MAKE_HASHABLE(type) \ +CRYPTO_MAKE_COMPARABLE(type) \ +CRYPTO_DEFINE_HASH_FUNCTIONS(type) + +#define CRYPTO_MAKE_HASHABLE_CONSTANT_TIME(type) \ +CRYPTO_MAKE_COMPARABLE_CONSTANT_TIME(type) \ +CRYPTO_DEFINE_HASH_FUNCTIONS(type) + diff --git a/src/device/device_ledger.cpp b/src/device/device_ledger.cpp index 7a34dad5e76..c4e9e40b7a3 100644 --- a/src/device/device_ledger.cpp +++ b/src/device/device_ledger.cpp @@ -136,7 +136,8 @@ namespace hw { } bool operator==(const crypto::key_derivation &d0, const crypto::key_derivation &d1) { - return !memcmp(&d0, &d1, sizeof(d0)); + static_assert(sizeof(crypto::key_derivation) == 32, "key_derivation must be 32 bytes"); + return !crypto_verify_32((const unsigned char*)&d0, (const unsigned char*)&d1); } /* ===================================================================== */ diff --git a/src/ringct/rctTypes.h b/src/ringct/rctTypes.h index a3ccf2e8594..452a68eb281 100644 --- a/src/ringct/rctTypes.h +++ b/src/ringct/rctTypes.h @@ -36,6 +36,7 @@ #include #include #include +#include extern "C" { #include "crypto/crypto-ops.h" @@ -81,7 +82,7 @@ namespace rct { unsigned char operator[](int i) const { return bytes[i]; } - bool operator==(const key &k) const { return !memcmp(bytes, k.bytes, sizeof(bytes)); } + bool operator==(const key &k) const { return !crypto_verify_32(bytes, k.bytes); } unsigned char bytes[32]; }; typedef std::vector keyV; //vector of keys @@ -524,16 +525,16 @@ namespace rct { static inline const crypto::secret_key rct2sk(const rct::key &k) { return (const crypto::secret_key&)k; } static inline const crypto::key_image rct2ki(const rct::key &k) { return (const crypto::key_image&)k; } static inline const crypto::hash rct2hash(const rct::key &k) { return (const crypto::hash&)k; } - static inline bool operator==(const rct::key &k0, const crypto::public_key &k1) { return !memcmp(&k0, &k1, 32); } - static inline bool operator!=(const rct::key &k0, const crypto::public_key &k1) { return memcmp(&k0, &k1, 32); } + static inline bool operator==(const rct::key &k0, const crypto::public_key &k1) { return !crypto_verify_32(k0.bytes, (const unsigned char*)&k1); } + static inline bool operator!=(const rct::key &k0, const crypto::public_key &k1) { return crypto_verify_32(k0.bytes, (const unsigned char*)&k1); } } namespace cryptonote { - static inline bool operator==(const crypto::public_key &k0, const rct::key &k1) { return !memcmp(&k0, &k1, 32); } - static inline bool operator!=(const crypto::public_key &k0, const rct::key &k1) { return memcmp(&k0, &k1, 32); } - static inline bool operator==(const crypto::secret_key &k0, const rct::key &k1) { return !memcmp(&k0, &k1, 32); } - static inline bool operator!=(const crypto::secret_key &k0, const rct::key &k1) { return memcmp(&k0, &k1, 32); } + static inline bool operator==(const crypto::public_key &k0, const rct::key &k1) { return !crypto_verify_32((const unsigned char*)&k0, k1.bytes); } + static inline bool operator!=(const crypto::public_key &k0, const rct::key &k1) { return crypto_verify_32((const unsigned char*)&k0, k1.bytes); } + static inline bool operator==(const crypto::secret_key &k0, const rct::key &k1) { return !crypto_verify_32((const unsigned char*)&k0, k1.bytes); } + static inline bool operator!=(const crypto::secret_key &k0, const rct::key &k1) { return crypto_verify_32((const unsigned char*)&k0, k1.bytes); } } namespace rct { diff --git a/tests/performance_tests/equality.h b/tests/performance_tests/equality.h new file mode 100644 index 00000000000..8d24d7da727 --- /dev/null +++ b/tests/performance_tests/equality.h @@ -0,0 +1,72 @@ +// Copyright (c) 2014-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers + +#pragma once + +#include +#include + +struct memcmp32 +{ + static const size_t loop_count = 1000000000; + static int call(const unsigned char *k0, const unsigned char *k1){ return memcmp(k0, k1, 32); } +}; + +struct verify32 +{ + static const size_t loop_count = 10000000; + static int call(const unsigned char *k0, const unsigned char *k1){ return crypto_verify_32(k0, k1); } +}; + +template +class test_equality +{ +public: + static const size_t loop_count = f::loop_count; + + bool init() + { + for (int n = 0; n < 32; ++n) + k0[n] = n; + for (int n = 0; n < 32; ++n) + k1[n] = equal ? n : n + 1; + return true; + } + + bool test() + { + return equal == !f::call(k0, k1); + } + +private: + unsigned char k0[32]; + unsigned char k1[32]; +}; + diff --git a/tests/performance_tests/main.cpp b/tests/performance_tests/main.cpp index bc3622ea8d5..1733e3409b8 100644 --- a/tests/performance_tests/main.cpp +++ b/tests/performance_tests/main.cpp @@ -51,6 +51,7 @@ #include "sc_reduce32.h" #include "cn_fast_hash.h" #include "rct_mlsag.h" +#include "equality.h" namespace po = boost::program_options; @@ -151,6 +152,11 @@ int main(int argc, char** argv) TEST_PERFORMANCE3(filter, test_ringct_mlsag, 1, 10, true); TEST_PERFORMANCE3(filter, test_ringct_mlsag, 1, 100, true); + TEST_PERFORMANCE2(filter, test_equality, memcmp32, true); + TEST_PERFORMANCE2(filter, test_equality, memcmp32, false); + TEST_PERFORMANCE2(filter, test_equality, verify32, false); + TEST_PERFORMANCE2(filter, test_equality, verify32, false); + std::cout << "Tests finished. Elapsed time: " << timer.elapsed_ms() / 1000 << " sec" << std::endl; return 0; diff --git a/tests/unit_tests/crypto.cpp b/tests/unit_tests/crypto.cpp index 9e168056841..29fa88f9dad 100644 --- a/tests/unit_tests/crypto.cpp +++ b/tests/unit_tests/crypto.cpp @@ -81,3 +81,18 @@ TEST(Crypto, null_keys) ASSERT_EQ(memcmp(crypto::null_skey.data, zero, 32), 0); ASSERT_EQ(memcmp(crypto::null_pkey.data, zero, 32), 0); } + +TEST(Crypto, verify_32) +{ + // all bytes are treated the same, so we can brute force just one byte + unsigned char k0[32] = {0}, k1[32] = {0}; + for (unsigned int i0 = 0; i0 < 256; ++i0) + { + k0[0] = i0; + for (unsigned int i1 = 0; i1 < 256; ++i1) + { + k1[0] = i1; + ASSERT_EQ(!crypto_verify_32(k0, k1), i0 == i1); + } + } +} From c955257c4ad4118c30ee757ffc00599379ad3f7c Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 4 Aug 2018 16:20:44 +0000 Subject: [PATCH 0275/1404] electrum-words: fix bytes_to_words on big endian Reported by cslashm --- src/mnemonics/electrum-words.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/mnemonics/electrum-words.cpp b/src/mnemonics/electrum-words.cpp index 290f2cb9382..3d6338856d9 100644 --- a/src/mnemonics/electrum-words.cpp +++ b/src/mnemonics/electrum-words.cpp @@ -47,6 +47,7 @@ #include "misc_language.h" #include "crypto/crypto.h" // for declaration of crypto::secret_key #include +#include "common/int-util.h" #include "mnemonics/electrum-words.h" #include #include @@ -411,7 +412,7 @@ namespace crypto { uint32_t w[4]; - memcpy(&w[0], src + (i * 4), 4); + w[0] = SWAP32LE(*(const uint32_t*)(src + (i * 4))); w[1] = w[0] % word_list_length; w[2] = ((w[0] / word_list_length) + w[1]) % word_list_length; From ce63d5634ec33f64b6e039f76b69ec9ad34f9c8e Mon Sep 17 00:00:00 2001 From: iDunk5400 Date: Thu, 23 Aug 2018 11:17:42 +0200 Subject: [PATCH 0276/1404] Windows: fix undefined references after unbound submodule update --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index cab85358180..b3ec03f547a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -814,7 +814,7 @@ endif() include_directories(SYSTEM ${Boost_INCLUDE_DIRS}) if(MINGW) set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wa,-mbig-obj") - set(EXTRA_LIBRARIES mswsock;ws2_32;iphlpapi) + set(EXTRA_LIBRARIES mswsock;ws2_32;iphlpapi;crypt32) set(ICU_LIBRARIES ${Boost_LOCALE_LIBRARY} icuio icuin icuuc icudt icutu iconv) elseif(APPLE OR OPENBSD OR ANDROID) set(EXTRA_LIBRARIES "") From 67666b14bad4929dfff7e182353132d5163b6669 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 23 Aug 2018 21:58:26 +0000 Subject: [PATCH 0277/1404] unit_tests: disable mlocker tests on windows (no implementation) --- tests/unit_tests/mlocker.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/unit_tests/mlocker.cpp b/tests/unit_tests/mlocker.cpp index 6e6048c6cd7..4809403744e 100644 --- a/tests/unit_tests/mlocker.cpp +++ b/tests/unit_tests/mlocker.cpp @@ -31,6 +31,12 @@ #include "misc_log_ex.h" #include "mlocker.h" +#if defined __GNUC__ && !defined _WIN32 +#define HAVE_MLOCK 1 +#endif + +#ifdef HAVE_MLOCK + #define BASE(data) (char*)(((uintptr_t)(data.get() + page_size - 1)) / page_size * page_size) TEST(mlocker, distinct_1) @@ -184,3 +190,5 @@ TEST(mlocker, mlocked) ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 0); ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 0); } + +#endif From 9d6539923e615014189c9281a52fad080f09a64e Mon Sep 17 00:00:00 2001 From: p8p Date: Sat, 25 Aug 2018 04:16:01 -0700 Subject: [PATCH 0278/1404] is_hdd update --- src/blockchain_db/lmdb/db_lmdb.cpp | 8 ++- src/common/util.cpp | 86 +++++++++++++----------------- src/common/util.h | 2 +- tests/unit_tests/CMakeLists.txt | 3 +- tests/unit_tests/is_hdd.cpp | 17 ++++++ 5 files changed, 62 insertions(+), 54 deletions(-) create mode 100644 tests/unit_tests/is_hdd.cpp diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp index 983a0168a8f..230fc1d7c31 100644 --- a/src/blockchain_db/lmdb/db_lmdb.cpp +++ b/src/blockchain_db/lmdb/db_lmdb.cpp @@ -1200,8 +1200,12 @@ void BlockchainLMDB::open(const std::string& filename, const int db_flags) throw DB_ERROR("Database could not be opened"); } - if (tools::is_hdd(filename.c_str())) - MCLOG_RED(el::Level::Warning, "global", "The blockchain is on a rotating drive: this will be very slow, use a SSD if possible"); + boost::optional is_hdd_result = tools::is_hdd(filename.c_str()); + if (is_hdd_result) + { + if (is_hdd_result.value()) + MCLOG_RED(el::Level::Warning, "global", "The blockchain is on a rotating drive: this will be very slow, use a SSD if possible"); + } m_folder = filename; diff --git a/src/common/util.cpp b/src/common/util.cpp index 5e0d2726e83..151e2b615c4 100644 --- a/src/common/util.cpp +++ b/src/common/util.cpp @@ -46,6 +46,13 @@ #include #endif +//tools::is_hdd +#ifdef __GLIBC__ + #include + #include + #include +#endif + #include "unbound.h" #include "include_base_utils.h" @@ -733,62 +740,41 @@ std::string get_nix_version_display_string() #endif } - bool is_hdd(const char *path) + boost::optional is_hdd(const char *file_path) { #ifdef __GLIBC__ - std::string device = ""; - struct stat st, dst; - if (stat(path, &st) < 0) - return 0; - - DIR *dir = opendir("/dev/block"); - if (!dir) - return 0; - struct dirent *de; - while ((de = readdir(dir))) - { - if (strcmp(de->d_name, ".") && strcmp(de->d_name, "..")) + struct stat st; + std::string prefix; + if(stat(file_path, &st) == 0) + { + std::ostringstream s; + s << "/sys/dev/block/" << major(st.st_dev) << ":" << minor(st.st_dev); + prefix = s.str(); + } + else + { + return boost::none; + } + std::string attr_path = prefix + "/queue/rotational"; + std::ifstream f(attr_path, std::ios_base::in); + if(not f.is_open()) + { + attr_path = prefix + "/../queue/rotational"; + f.open(attr_path, std::ios_base::in); + if(not f.is_open()) { - std::string dev_path = std::string("/dev/block/") + de->d_name; - char resolved[PATH_MAX]; - if (realpath(dev_path.c_str(), resolved) && !strncmp(resolved, "/dev/", 5)) - { - if (stat(resolved, &dst) == 0) - { - if (dst.st_rdev == st.st_dev) - { - // take out trailing digits (eg, sda1 -> sda) - char *ptr = resolved; - while (*ptr) - ++ptr; - while (ptr > resolved && isdigit(*--ptr)) - *ptr = 0; - device = resolved + 5; - break; - } - } - } + return boost::none; } } - closedir(dir); - - if (device.empty()) - return 0; - - std::string sys_path = "/sys/block/" + device + "/queue/rotational"; - FILE *f = fopen(sys_path.c_str(), "r"); - if (!f) - return false; - char s[8]; - char *ptr = fgets(s, sizeof(s), f); - fclose(f); - if (!ptr) - return 0; - s[sizeof(s) - 1] = 0; - int n = atoi(s); // returns 0 on parse error - return n == 1; + unsigned short val = 0xdead; + f >> val; + if(not f.fail()) + { + return (val == 1); + } + return boost::none; #else - return 0; + return boost::none; #endif } diff --git a/src/common/util.h b/src/common/util.h index 8815232e28d..0e0b50520d5 100644 --- a/src/common/util.h +++ b/src/common/util.h @@ -230,7 +230,7 @@ namespace tools bool sha256sum(const uint8_t *data, size_t len, crypto::hash &hash); bool sha256sum(const std::string &filename, crypto::hash &hash); - bool is_hdd(const char *path); + boost::optional is_hdd(const char *path); boost::optional> parse_subaddress_lookahead(const std::string& str); diff --git a/tests/unit_tests/CMakeLists.txt b/tests/unit_tests/CMakeLists.txt index 145e3820e12..7cbf38424fa 100644 --- a/tests/unit_tests/CMakeLists.txt +++ b/tests/unit_tests/CMakeLists.txt @@ -76,7 +76,8 @@ set(unit_tests_sources output_selection.cpp vercmp.cpp ringdb.cpp - wipeable_string.cpp) + wipeable_string.cpp + is_hdd.cpp) set(unit_tests_headers unit_tests_utils.h) diff --git a/tests/unit_tests/is_hdd.cpp b/tests/unit_tests/is_hdd.cpp new file mode 100644 index 00000000000..1be670e5e1f --- /dev/null +++ b/tests/unit_tests/is_hdd.cpp @@ -0,0 +1,17 @@ +#include "common/util.h" +#include +#include + +#if defined(__GLIBC__) +TEST(is_hdd, linux_os_root) +{ + std::string path = "/"; + EXPECT_TRUE(tools::is_hdd(path.c_str())); +} +#else +TEST(is_hdd, unknown_os) +{ + std::string path = ""; + EXPECT_FALSE(tools::is_hdd(path.c_str())); +} +#endif From 2af1ec3af7acd1dfd887cebeccbdaa8fd08360ed Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 19 Jul 2018 11:31:23 +0100 Subject: [PATCH 0279/1404] cn_deserialize: support pruned transactions --- src/debug_utilities/cn_deserialize.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/debug_utilities/cn_deserialize.cpp b/src/debug_utilities/cn_deserialize.cpp index 6c09b0f1835..af33510bee9 100644 --- a/src/debug_utilities/cn_deserialize.cpp +++ b/src/debug_utilities/cn_deserialize.cpp @@ -168,9 +168,12 @@ int main(int argc, char* argv[]) std::cout << "Parsed block:" << std::endl; std::cout << cryptonote::obj_to_json_str(block) << std::endl; } - else if (cryptonote::parse_and_validate_tx_from_blob(blob, tx)) + else if (cryptonote::parse_and_validate_tx_from_blob(blob, tx) || cryptonote::parse_and_validate_tx_base_from_blob(blob, tx)) { - std::cout << "Parsed transaction:" << std::endl; + if (tx.pruned) + std::cout << "Parsed pruned transaction:" << std::endl; + else + std::cout << "Parsed transaction:" << std::endl; std::cout << cryptonote::obj_to_json_str(tx) << std::endl; bool parsed = cryptonote::parse_tx_extra(tx.extra, fields); From 5019852adc6133c5842ec9dd71ca63d648f282e8 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 19 Jul 2018 11:31:40 +0100 Subject: [PATCH 0280/1404] cn_deserialize: extract payment ids from extra nonce --- src/debug_utilities/cn_deserialize.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/debug_utilities/cn_deserialize.cpp b/src/debug_utilities/cn_deserialize.cpp index af33510bee9..bf228fe142c 100644 --- a/src/debug_utilities/cn_deserialize.cpp +++ b/src/debug_utilities/cn_deserialize.cpp @@ -43,6 +43,15 @@ using namespace epee; using namespace cryptonote; +static std::string extra_nonce_to_string(const cryptonote::tx_extra_nonce &extra_nonce) +{ + if (extra_nonce.nonce.size() == 9 && extra_nonce.nonce[0] == TX_EXTRA_NONCE_ENCRYPTED_PAYMENT_ID) + return "encrypted payment ID: " + epee::string_tools::buff_to_hex_nodelimer(extra_nonce.nonce.substr(1)); + if (extra_nonce.nonce.size() == 33 && extra_nonce.nonce[0] == TX_EXTRA_NONCE_PAYMENT_ID) + return "plaintext payment ID: " + epee::string_tools::buff_to_hex_nodelimer(extra_nonce.nonce.substr(1)); + return epee::string_tools::buff_to_hex_nodelimer(extra_nonce.nonce); +} + static void print_extra_fields(const std::vector &fields) { std::cout << "tx_extra has " << fields.size() << " field(s)" << std::endl; @@ -51,7 +60,7 @@ static void print_extra_fields(const std::vector &fi std::cout << "field " << n << ": "; if (typeid(cryptonote::tx_extra_padding) == fields[n].type()) std::cout << "extra padding: " << boost::get(fields[n]).size << " bytes"; else if (typeid(cryptonote::tx_extra_pub_key) == fields[n].type()) std::cout << "extra pub key: " << boost::get(fields[n]).pub_key; - else if (typeid(cryptonote::tx_extra_nonce) == fields[n].type()) std::cout << "extra nonce: " << epee::string_tools::buff_to_hex_nodelimer(boost::get(fields[n]).nonce); + else if (typeid(cryptonote::tx_extra_nonce) == fields[n].type()) std::cout << "extra nonce: " << extra_nonce_to_string(boost::get(fields[n])); else if (typeid(cryptonote::tx_extra_merge_mining_tag) == fields[n].type()) std::cout << "extra merge mining tag: depth " << boost::get(fields[n]).depth << ", merkle root " << boost::get(fields[n]).merkle_root; else if (typeid(cryptonote::tx_extra_additional_pub_keys) == fields[n].type()) std::cout << "additional tx pubkeys: " << boost::join(boost::get(fields[n]).data | boost::adaptors::transformed([](const crypto::public_key &key){ return epee::string_tools::pod_to_hex(key); }), ", " ); else if (typeid(cryptonote::tx_extra_mysterious_minergate) == fields[n].type()) std::cout << "extra minergate custom: " << epee::string_tools::buff_to_hex_nodelimer(boost::get(fields[n]).data); From 8eab6147f414a47d12e00d6c67ca8c466f8a583b Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 27 Aug 2018 16:29:29 +0000 Subject: [PATCH 0281/1404] epee: use the socket::bind variant which does not throw When this throws in a loop, stack trace generation can take a significant amount of CPU --- .../epee/include/net/abstract_tcp_server2.inl | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/contrib/epee/include/net/abstract_tcp_server2.inl b/contrib/epee/include/net/abstract_tcp_server2.inl index 59a1261638c..d837208d381 100644 --- a/contrib/epee/include/net/abstract_tcp_server2.inl +++ b/contrib/epee/include/net/abstract_tcp_server2.inl @@ -1110,7 +1110,15 @@ POP_WARNINGS if(bind_ip != "0.0.0.0" && bind_ip != "0" && bind_ip != "" ) { boost::asio::ip::tcp::endpoint local_endpoint(boost::asio::ip::address::from_string(adr.c_str()), 0); - sock_.bind(local_endpoint); + boost::system::error_code ec; + sock_.bind(local_endpoint, ec); + if (ec) + { + MERROR("Error binding to " << adr << ": " << ec.message()); + if (sock_.is_open()) + sock_.close(); + return false; + } } /* @@ -1216,7 +1224,15 @@ POP_WARNINGS if(bind_ip != "0.0.0.0" && bind_ip != "0" && bind_ip != "" ) { boost::asio::ip::tcp::endpoint local_endpoint(boost::asio::ip::address::from_string(adr.c_str()), 0); - sock_.bind(local_endpoint); + boost::system::error_code ec; + sock_.bind(local_endpoint, ec); + if (ec) + { + MERROR("Error binding to " << adr << ": " << ec.message()); + if (sock_.is_open()) + sock_.close(); + return false; + } } boost::shared_ptr sh_deadline(new boost::asio::deadline_timer(io_service_)); From 5083614ffa84109fccd754ee5509b25030bec9a6 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 21 Aug 2018 17:31:42 +0000 Subject: [PATCH 0282/1404] dns_util: add new DNSSEC trust anchor for rollover It should be useful from the 11th of october 2018. The old key is still trusted for now. https://www.icann.org/resources/pages/ksk-rollover --- src/common/dns_utils.cpp | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/common/dns_utils.cpp b/src/common/dns_utils.cpp index 33f60bc3c80..3f2bde620bf 100644 --- a/src/common/dns_utils.cpp +++ b/src/common/dns_utils.cpp @@ -97,11 +97,16 @@ get_builtin_cert(void) */ /** return the built in root DS trust anchor */ -static const char* +static const char* const* get_builtin_ds(void) { - return -". IN DS 19036 8 2 49AAC11D7B6F6446702E54A1607371607A1A41855200FD2CE1CDDE32F24E8FB5\n"; + static const char * const ds[] = + { + ". IN DS 19036 8 2 49AAC11D7B6F6446702E54A1607371607A1A41855200FD2CE1CDDE32F24E8FB5\n", + ". IN DS 20326 8 2 E06D44B80B8F1D39A95C0B0D7C65D08458E880409BBC683457104237C7F8EC8D\n", + NULL + }; + return ds; } /************************************************************ @@ -240,7 +245,12 @@ DNSResolver::DNSResolver() : m_data(new DNSResolverData()) ub_ctx_hosts(m_data->m_ub_context, NULL); } - ub_ctx_add_ta(m_data->m_ub_context, string_copy(::get_builtin_ds())); + const char * const *ds = ::get_builtin_ds(); + while (*ds) + { + MINFO("adding trust anchor: " << *ds); + ub_ctx_add_ta(m_data->m_ub_context, string_copy(*ds++)); + } } DNSResolver::~DNSResolver() From 2c636e45f208b0b13f213895e4ab4f37d2ac849b Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 1 Aug 2018 16:01:36 +0000 Subject: [PATCH 0283/1404] simplewallet: handle transfers using a monero: URI --- src/simplewallet/simplewallet.cpp | 112 +++++++++++++++++++++--------- 1 file changed, 80 insertions(+), 32 deletions(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 775b7c3598e..8bc9902e18f 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -2160,15 +2160,15 @@ simple_wallet::simple_wallet() tr("Show the blockchain height.")); m_cmd_binder.set_handler("transfer_original", boost::bind(&simple_wallet::transfer, this, _1), - tr("transfer_original [index=[,,...]] [] []
[]"), - tr("Transfer to
using an older transaction building algorithm. If the parameter \"index=[,,...]\" is specified, the wallet uses outputs received by addresses of those indices. If omitted, the wallet randomly chooses address indices to be used. In any case, it tries its best not to combine outputs across multiple addresses. is the priority of the transaction. The higher the priority, the higher the transaction fee. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command \"set priority\") is used. is the number of inputs to include for untraceability. Multiple payments can be made at once by adding etcetera (before the payment ID, if it's included)")); + tr("transfer_original [index=[,,...]] [] [] ( |
) []"), + tr("Transfer to
using an older transaction building algorithm. If the parameter \"index=[,,...]\" is specified, the wallet uses outputs received by addresses of those indices. If omitted, the wallet randomly chooses address indices to be used. In any case, it tries its best not to combine outputs across multiple addresses. is the priority of the transaction. The higher the priority, the higher the transaction fee. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command \"set priority\") is used. is the number of inputs to include for untraceability. Multiple payments can be made at once by adding URI_2 or etcetera (before the payment ID, if it's included)")); m_cmd_binder.set_handler("transfer", boost::bind(&simple_wallet::transfer_new, this, _1), - tr("transfer [index=[,,...]] [] []
[]"), - tr("Transfer to
. If the parameter \"index=[,,...]\" is specified, the wallet uses outputs received by addresses of those indices. If omitted, the wallet randomly chooses address indices to be used. In any case, it tries its best not to combine outputs across multiple addresses. is the priority of the transaction. The higher the priority, the higher the transaction fee. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command \"set priority\") is used. is the number of inputs to include for untraceability. Multiple payments can be made at once by adding etcetera (before the payment ID, if it's included)")); + tr("transfer [index=[,,...]] [] [] ( |
) []"), + tr("Transfer to
. If the parameter \"index=[,,...]\" is specified, the wallet uses outputs received by addresses of those indices. If omitted, the wallet randomly chooses address indices to be used. In any case, it tries its best not to combine outputs across multiple addresses. is the priority of the transaction. The higher the priority, the higher the transaction fee. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command \"set priority\") is used. is the number of inputs to include for untraceability. Multiple payments can be made at once by adding URI_2 or etcetera (before the payment ID, if it's included)")); m_cmd_binder.set_handler("locked_transfer", boost::bind(&simple_wallet::locked_transfer, this, _1), - tr("locked_transfer [index=[,,...]] [] [] []"), - tr("Transfer to
and lock it for (max. 1000000). If the parameter \"index=[,,...]\" is specified, the wallet uses outputs received by addresses of those indices. If omitted, the wallet randomly chooses address indices to be used. In any case, it tries its best not to combine outputs across multiple addresses. is the priority of the transaction. The higher the priority, the higher the transaction fee. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command \"set priority\") is used. is the number of inputs to include for untraceability. Multiple payments can be made at once by adding etcetera (before the payment ID, if it's included)")); + tr("locked_transfer [index=[,,...]] [] [] ( | ) []"), + tr("Transfer to
and lock it for (max. 1000000). If the parameter \"index=[,,...]\" is specified, the wallet uses outputs received by addresses of those indices. If omitted, the wallet randomly chooses address indices to be used. In any case, it tries its best not to combine outputs across multiple addresses. is the priority of the transaction. The higher the priority, the higher the transaction fee. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command \"set priority\") is used. is the number of inputs to include for untraceability. Multiple payments can be made at once by adding URI_2 or etcetera (before the payment ID, if it's included)")); m_cmd_binder.set_handler("locked_sweep_all", boost::bind(&simple_wallet::locked_sweep_all, this, _1), tr("locked_sweep_all [index=[,,...]] [] []
[]"), @@ -4543,7 +4543,7 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector extra; bool payment_id_seen = false; - bool expect_even = (transfer_type == TransferLocked); - if ((expect_even ? 0 : 1) == local_args.size() % 2) + if (!local_args.empty()) { std::string payment_id_str = local_args.back(); - local_args.pop_back(); - crypto::hash payment_id; - bool r = tools::wallet2::parse_long_payment_id(payment_id_str, payment_id); - if(r) + bool r = true; + if (tools::wallet2::parse_long_payment_id(payment_id_str, payment_id)) { std::string extra_nonce; set_payment_id_to_tx_extra_nonce(extra_nonce, payment_id); r = add_extra_nonce_to_tx_extra(extra, extra_nonce); + local_args.pop_back(); + payment_id_seen = true; + message_writer() << tr("Unencrypted payment IDs are bad for privacy: ask the recipient to use subaddresses instead"); } else { crypto::hash8 payment_id8; - r = tools::wallet2::parse_short_payment_id(payment_id_str, payment_id8); - if(r) + if (tools::wallet2::parse_short_payment_id(payment_id_str, payment_id8)) { std::string extra_nonce; set_encrypted_payment_id_to_tx_extra_nonce(extra_nonce, payment_id8); r = add_extra_nonce_to_tx_extra(extra, extra_nonce); + local_args.pop_back(); + payment_id_seen = true; } } if(!r) { - fail_msg_writer() << tr("payment id has invalid format, expected 16 or 64 character hex string: ") << payment_id_str; + fail_msg_writer() << tr("payment id failed to encode"); return true; } - payment_id_seen = true; - message_writer() << tr("Unencrypted payment IDs are bad for privacy: ask the recipient to use subaddresses instead"); } uint64_t locked_blocks = 0; @@ -4609,11 +4608,54 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector dsts; size_t num_subaddresses = 0; - for (size_t i = 0; i < local_args.size(); i += 2) + for (size_t i = 0; i < local_args.size(); ) { - cryptonote::address_parse_info info; cryptonote::tx_destination_entry de; - if (!cryptonote::get_account_address_from_str_or_url(info, m_wallet->nettype(), local_args[i], oa_prompter)) + cryptonote::address_parse_info info; + bool r = true; + + // check for a URI + std::string address_uri, payment_id_uri, tx_description, recipient_name, error; + std::vector unknown_parameters; + uint64_t amount = 0; + bool has_uri = m_wallet->parse_uri(local_args[i], address_uri, payment_id_uri, amount, tx_description, recipient_name, unknown_parameters, error); + if (has_uri) + { + r = cryptonote::get_account_address_from_str_or_url(info, m_wallet->nettype(), address_uri, oa_prompter); + if (payment_id_uri.size() == 16) + { + if (!tools::wallet2::parse_short_payment_id(payment_id_uri, info.payment_id)) + { + fail_msg_writer() << tr("failed to parse short payment ID from URI"); + return true; + } + info.has_payment_id = true; + } + de.amount = amount; + ++i; + } + else if (i + 1 < local_args.size()) + { + r = cryptonote::get_account_address_from_str_or_url(info, m_wallet->nettype(), local_args[i], oa_prompter); + bool ok = cryptonote::parse_amount(de.amount, local_args[i + 1]); + if(!ok || 0 == de.amount) + { + fail_msg_writer() << tr("amount is wrong: ") << local_args[i] << ' ' << local_args[i + 1] << + ", " << tr("expected number from 0 to ") << print_money(std::numeric_limits::max()); + return true; + } + i += 2; + } + else + { + if (boost::starts_with(local_args[i], "monero:")) + fail_msg_writer() << tr("Invalid last argument: ") << local_args.back() << ": " << error; + else + fail_msg_writer() << tr("Invalid last argument: ") << local_args.back(); + return true; + } + + if (!r) { fail_msg_writer() << tr("failed to parse address"); return true; @@ -4622,16 +4664,30 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector::max()); - return true; - } - dsts.push_back(de); } From f0bc684ccdc7bc66010254c9136f960467e6a3db Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 29 Aug 2018 09:42:23 +0000 Subject: [PATCH 0284/1404] mnemonics: fix outrageous compile time with CLANG in release from several minutes to 10-15 seconds --- src/mnemonics/chinese_simplified.h | 9 ++++++--- src/mnemonics/dutch.h | 9 ++++++--- src/mnemonics/english.h | 9 ++++++--- src/mnemonics/english_old.h | 9 ++++++--- src/mnemonics/esperanto.h | 9 ++++++--- src/mnemonics/french.h | 9 ++++++--- src/mnemonics/german.h | 9 ++++++--- src/mnemonics/italian.h | 9 ++++++--- src/mnemonics/japanese.h | 9 ++++++--- src/mnemonics/language_base.h | 13 +++++++++++-- src/mnemonics/lojban.h | 9 ++++++--- src/mnemonics/portuguese.h | 9 ++++++--- src/mnemonics/russian.h | 9 ++++++--- src/mnemonics/spanish.h | 9 ++++++--- 14 files changed, 89 insertions(+), 41 deletions(-) diff --git a/src/mnemonics/chinese_simplified.h b/src/mnemonics/chinese_simplified.h index 1ae8c89d6a7..0566b10793d 100644 --- a/src/mnemonics/chinese_simplified.h +++ b/src/mnemonics/chinese_simplified.h @@ -72,7 +72,10 @@ namespace Language class Chinese_Simplified: public Base { public: - Chinese_Simplified(): Base("简体中文 (中国)", "Chinese (simplified)", std::vector({ + Chinese_Simplified(): Base("简体中文 (中国)", "Chinese (simplified)", {}, 1) + { + static constexpr const char * const words[NWORDS] = + { "的", "一", "是", @@ -1699,8 +1702,8 @@ namespace Language "秒", "浙", "貌" - }), 1) - { + }; + set_words(words); populate_maps(); } }; diff --git a/src/mnemonics/dutch.h b/src/mnemonics/dutch.h index c9806f4509e..801caf986b2 100644 --- a/src/mnemonics/dutch.h +++ b/src/mnemonics/dutch.h @@ -49,7 +49,10 @@ namespace Language class Dutch: public Base { public: - Dutch(): Base("Nederlands", "Dutch", std::vector({ + Dutch(): Base("Nederlands", "Dutch", {}, 4) + { + static constexpr const char * const words[NWORDS] = + { "aalglad", "aalscholver", "aambeeld", @@ -1676,8 +1679,8 @@ namespace Language "zwiep", "zwijmel", "zworen" - }), 4) - { + }; + set_words(words); populate_maps(); } }; diff --git a/src/mnemonics/english.h b/src/mnemonics/english.h index ee087674de3..d5c5594ef42 100644 --- a/src/mnemonics/english.h +++ b/src/mnemonics/english.h @@ -49,7 +49,10 @@ namespace Language class English: public Base { public: - English(): Base("English", "English", std::vector({ + English(): Base("English", "English", {}, 3) + { + static constexpr const char * const words[NWORDS] = + { "abbey", "abducts", "ability", @@ -1676,8 +1679,8 @@ namespace Language "zombie", "zones", "zoom" - }), 3) - { + }; + set_words(words); populate_maps(); } }; diff --git a/src/mnemonics/english_old.h b/src/mnemonics/english_old.h index b3149164607..e35b907df2b 100644 --- a/src/mnemonics/english_old.h +++ b/src/mnemonics/english_old.h @@ -51,7 +51,10 @@ namespace Language class EnglishOld: public Base { public: - EnglishOld(): Base("EnglishOld", "English (old)", std::vector({ + EnglishOld(): Base("EnglishOld", "English (old)", {}, 4) + { + static constexpr const char * const words[NWORDS] = + { "like", "just", "love", @@ -1678,8 +1681,8 @@ namespace Language "unseen", "weapon", "weary" - }), 4) - { + }; + set_words(words); populate_maps(ALLOW_DUPLICATE_PREFIXES | ALLOW_SHORT_WORDS); } }; diff --git a/src/mnemonics/esperanto.h b/src/mnemonics/esperanto.h index a1d1a3f30ee..b0be235ed1f 100644 --- a/src/mnemonics/esperanto.h +++ b/src/mnemonics/esperanto.h @@ -58,7 +58,10 @@ namespace Language class Esperanto: public Base { public: - Esperanto(): Base("Esperanto", "Esperanto", std::vector({ + Esperanto(): Base("Esperanto", "Esperanto", {}, 4) + { + static constexpr const char * const words[NWORDS] = + { "abako", "abdiki", "abelo", @@ -1685,8 +1688,8 @@ namespace Language "zorgi", "zukino", "zumilo", - }), 4) - { + }; + set_words(words); populate_maps(); } }; diff --git a/src/mnemonics/french.h b/src/mnemonics/french.h index 7eaf456501b..48ec46f7813 100644 --- a/src/mnemonics/french.h +++ b/src/mnemonics/french.h @@ -49,7 +49,10 @@ namespace Language class French: public Base { public: - French(): Base("Français", "French", std::vector({ + French(): Base("Français", "French", {}, 4) + { + static constexpr const char * const words[NWORDS] = + { "abandon", "abattre", "aboi", @@ -1676,8 +1679,8 @@ namespace Language "zinc", "zone", "zoom" - }), 4) - { + }; + set_words(words); populate_maps(); } }; diff --git a/src/mnemonics/german.h b/src/mnemonics/german.h index 8eff435233c..883a173a37f 100644 --- a/src/mnemonics/german.h +++ b/src/mnemonics/german.h @@ -51,7 +51,10 @@ namespace Language class German: public Base { public: - German(): Base("Deutsch", "German", std::vector({ + German(): Base("Deutsch", "German", {}, 4) + { + static constexpr const char * const words[NWORDS] = + { "Abakus", "Abart", "abbilden", @@ -1678,8 +1681,8 @@ namespace Language "Zündung", "Zweck", "Zyklop" - }), 4) - { + }; + set_words(words); populate_maps(); } }; diff --git a/src/mnemonics/italian.h b/src/mnemonics/italian.h index d5ecb74f465..57cdfa25e67 100644 --- a/src/mnemonics/italian.h +++ b/src/mnemonics/italian.h @@ -51,7 +51,10 @@ namespace Language class Italian: public Base { public: - Italian(): Base("Italiano", "Italian", std::vector({ + Italian(): Base("Italiano", "Italian", {}, 4) + { + static constexpr const char * const words[NWORDS] = + { "abbinare", "abbonato", "abisso", @@ -1678,8 +1681,8 @@ namespace Language "zolfo", "zombie", "zucchero" - }), 4) - { + }; + set_words(words); populate_maps(); } }; diff --git a/src/mnemonics/japanese.h b/src/mnemonics/japanese.h index f3b3e4924a3..5baabedf2b6 100644 --- a/src/mnemonics/japanese.h +++ b/src/mnemonics/japanese.h @@ -71,7 +71,10 @@ namespace Language class Japanese: public Base { public: - Japanese(): Base("日本語", "Japanese", std::vector({ + Japanese(): Base("日本語", "Japanese", {}, 3) + { + static constexpr const char * const words[NWORDS] = + { "あいこくしん", "あいさつ", "あいだ", @@ -1698,8 +1701,8 @@ namespace Language "ひさん", "びじゅつかん", "ひしょ" - }), 3) - { + }; + set_words(words); populate_maps(); } }; diff --git a/src/mnemonics/language_base.h b/src/mnemonics/language_base.h index cf518ab2a62..52e784cef44 100644 --- a/src/mnemonics/language_base.h +++ b/src/mnemonics/language_base.h @@ -83,7 +83,10 @@ namespace Language ALLOW_SHORT_WORDS = 1<<0, ALLOW_DUPLICATE_PREFIXES = 1<<1, }; - const std::vector word_list; /*!< A pointer to the array of words */ + enum { + NWORDS = 1626 + }; + std::vector word_list; /*!< A pointer to the array of words */ std::unordered_map word_map; /*!< hash table to find word's index */ std::unordered_map trimmed_word_map; /*!< hash table to find word's trimmed index */ std::string language_name; /*!< Name of language */ @@ -96,7 +99,7 @@ namespace Language { int ii; std::vector::const_iterator it; - if (word_list.size () != 1626) + if (word_list.size () != NWORDS) throw std::runtime_error("Wrong word list length for " + language_name); for (it = word_list.begin(), ii = 0; it != word_list.end(); it++, ii++) { @@ -138,6 +141,12 @@ namespace Language virtual ~Base() { } + void set_words(const char * const words[]) + { + word_list.resize(NWORDS); + for (size_t i = 0; i < NWORDS; ++i) + word_list[i] = words[i]; + } /*! * \brief Returns a pointer to the word list. * \return A pointer to the word list. diff --git a/src/mnemonics/lojban.h b/src/mnemonics/lojban.h index 0966a1169b9..5162a8ec968 100644 --- a/src/mnemonics/lojban.h +++ b/src/mnemonics/lojban.h @@ -56,7 +56,10 @@ namespace Language class Lojban: public Base { public: - Lojban(): Base("Lojban", "Lojban", std::vector({ + Lojban(): Base("Lojban", "Lojban", {}, 4) + { + static constexpr const char * const words[NWORDS] = + { "backi", "bacru", "badna", @@ -1683,8 +1686,8 @@ namespace Language "noltruti'u", "samtci", "snaxa'a", - }), 4) - { + }; + set_words(words); populate_maps(); } }; diff --git a/src/mnemonics/portuguese.h b/src/mnemonics/portuguese.h index 0195389ceb2..af04f89c24d 100644 --- a/src/mnemonics/portuguese.h +++ b/src/mnemonics/portuguese.h @@ -72,7 +72,10 @@ namespace Language class Portuguese: public Base { public: - Portuguese(): Base("Português", "Portuguese", std::vector({ + Portuguese(): Base("Português", "Portuguese", {}, 4) + { + static constexpr const char * const words[NWORDS] = + { "abaular", "abdominal", "abeto", @@ -1699,8 +1702,8 @@ namespace Language "zeloso", "zenite", "zumbi" - }), 4) - { + }; + set_words(words); populate_maps(); } }; diff --git a/src/mnemonics/russian.h b/src/mnemonics/russian.h index d5dd556ef97..f3e70ede6e9 100644 --- a/src/mnemonics/russian.h +++ b/src/mnemonics/russian.h @@ -51,7 +51,10 @@ namespace Language class Russian: public Base { public: - Russian(): Base("русский язык", "Russian", std::vector({ + Russian(): Base("русский язык", "Russian", {}, 4) + { + static constexpr const char * const words[NWORDS] = + { "абажур", "абзац", "абонент", @@ -1678,8 +1681,8 @@ namespace Language "яхта", "ячейка", "ящик" - }), 4) - { + }; + set_words(words); populate_maps(); } }; diff --git a/src/mnemonics/spanish.h b/src/mnemonics/spanish.h index 51f38fede7a..4d7a896a66a 100644 --- a/src/mnemonics/spanish.h +++ b/src/mnemonics/spanish.h @@ -72,7 +72,10 @@ namespace Language class Spanish: public Base { public: - Spanish(): Base("Español", "Spanish", std::vector({ + Spanish(): Base("Español", "Spanish", {}, 4) + { + static constexpr const char * const words[NWORDS] = + { "ábaco", "abdomen", "abeja", @@ -1699,8 +1702,8 @@ namespace Language "risa", "ritmo", "rito" - }), 4) - { + }; + set_words(words); populate_maps(ALLOW_SHORT_WORDS); } }; From 76f95f052e4ab7f6074464f11b436a7374017b52 Mon Sep 17 00:00:00 2001 From: Dusan Klinec Date: Thu, 30 Aug 2018 13:35:05 +0200 Subject: [PATCH 0285/1404] rpc: allow to pass RPC login via RPC_LOGIN env var - passing by parameter is insecure as it is shown in the process list --- src/daemon/main.cpp | 7 +++++-- src/rpc/rpc_args.cpp | 14 ++++++++++---- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/daemon/main.cpp b/src/daemon/main.cpp index 82ece62a9cd..f483ba6c92c 100644 --- a/src/daemon/main.cpp +++ b/src/daemon/main.cpp @@ -239,11 +239,14 @@ int main(int argc, char const * argv[]) return 1; } + const char *env_rpc_login = nullptr; + const bool has_rpc_arg = command_line::has_arg(vm, arg.rpc_login); + const bool use_rpc_env = !has_rpc_arg && (env_rpc_login = getenv("RPC_LOGIN")) != nullptr && strlen(env_rpc_login) > 0; boost::optional login{}; - if (command_line::has_arg(vm, arg.rpc_login)) + if (has_rpc_arg || use_rpc_env) { login = tools::login::parse( - command_line::get_arg(vm, arg.rpc_login), false, [](bool verify) { + has_rpc_arg ? command_line::get_arg(vm, arg.rpc_login) : std::string(env_rpc_login), false, [](bool verify) { #ifdef HAVE_READLINE rdln::suspend_readline pause_readline; #endif diff --git a/src/rpc/rpc_args.cpp b/src/rpc/rpc_args.cpp index d4044d11b7e..60c78480a20 100644 --- a/src/rpc/rpc_args.cpp +++ b/src/rpc/rpc_args.cpp @@ -82,11 +82,17 @@ namespace cryptonote } } - if (command_line::has_arg(vm, arg.rpc_login)) + const char *env_rpc_login = nullptr; + const bool has_rpc_arg = command_line::has_arg(vm, arg.rpc_login); + const bool use_rpc_env = !has_rpc_arg && (env_rpc_login = getenv("RPC_LOGIN")) != nullptr && strlen(env_rpc_login) > 0; + boost::optional login{}; + if (has_rpc_arg || use_rpc_env) { - config.login = tools::login::parse(command_line::get_arg(vm, arg.rpc_login), true, [](bool verify) { - return tools::password_container::prompt(verify, "RPC server password"); - }); + config.login = tools::login::parse( + has_rpc_arg ? command_line::get_arg(vm, arg.rpc_login) : std::string(env_rpc_login), true, [](bool verify) { + return tools::password_container::prompt(verify, "RPC server password"); + }); + if (!config.login) return boost::none; From 9a66d9f48bf564394f179beb1806c21cf98f6b98 Mon Sep 17 00:00:00 2001 From: HomDx Date: Sat, 1 Sep 2018 15:31:55 +0300 Subject: [PATCH 0286/1404] Docker: Updated dependencies cmake 3.12.1 and boost 1.68 --- Dockerfile | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Dockerfile b/Dockerfile index 5fde5a477c4..96526e45020 100644 --- a/Dockerfile +++ b/Dockerfile @@ -23,9 +23,9 @@ RUN set -ex && \ WORKDIR /usr/local #Cmake -ARG CMAKE_VERSION=3.12.0 +ARG CMAKE_VERSION=3.12.1 ARG CMAKE_VERSION_DOT=v3.12 -ARG CMAKE_HASH=d0781a90f6cdb9049d104ac16a150f9350b693498b9dea8a0331e799db6b9d69 +ARG CMAKE_HASH=c53d5c2ce81d7a957ee83e3e635c8cda5dfe20c9d501a4828ee28e1615e57ab2 RUN set -ex \ && curl -s -O https://cmake.org/files/${CMAKE_VERSION_DOT}/cmake-${CMAKE_VERSION}.tar.gz \ && echo "${CMAKE_HASH} cmake-${CMAKE_VERSION}.tar.gz" | sha256sum -c \ @@ -36,9 +36,9 @@ RUN set -ex \ && make install ## Boost -ARG BOOST_VERSION=1_67_0 -ARG BOOST_VERSION_DOT=1.67.0 -ARG BOOST_HASH=2684c972994ee57fc5632e03bf044746f6eb45d4920c343937a465fd67a5adba +ARG BOOST_VERSION=1_68_0 +ARG BOOST_VERSION_DOT=1.68.0 +ARG BOOST_HASH=7f6130bc3cf65f56a618888ce9d5ea704fa10b462be126ad053e80e553d6d8b7 RUN set -ex \ && curl -s -L -o boost_${BOOST_VERSION}.tar.bz2 https://dl.bintray.com/boostorg/release/${BOOST_VERSION_DOT}/source/boost_${BOOST_VERSION}.tar.bz2 \ && echo "${BOOST_HASH} boost_${BOOST_VERSION}.tar.bz2" | sha256sum -c \ From 4e1e9a607e45c365522d0859a019a1376a059317 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 2 Sep 2018 12:02:35 +0000 Subject: [PATCH 0287/1404] blockchain: add mainnet v8 height targetting 18 october and v9 a day later --- src/cryptonote_core/blockchain.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index e96dc6bb6d7..23aa895038a 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -109,6 +109,12 @@ static const struct { // version 7 starts from block 1546000, which is on or around the 6th of April, 2018. Fork time finalised on 2018-03-17. { 7, 1546000, 0, 1521303150 }, + + // version 8 starts from block 1685555, which is on or around the 18th of October, 2018. Fork time finalised on 2018-09-02. + { 8, 1685555, 0, 1535889547 }, + + // version 9 starts from block 1686275, which is on or around the 19th of October, 2018. Fork time finalised on 2018-09-02. + { 9, 1686275, 0, 1535889548 }, }; static const uint64_t mainnet_hard_fork_version_1_till = 1009826; From 45c85c89deea1de48229aa4a31154a8774a651fc Mon Sep 17 00:00:00 2001 From: el00ruobuob Date: Sun, 2 Sep 2018 23:50:31 +0200 Subject: [PATCH 0288/1404] remove unused fields from relay_tx RPC --- src/wallet/wallet_rpc_server_commands_defs.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h index 3f946eaed7f..4a2e7bca461 100644 --- a/src/wallet/wallet_rpc_server_commands_defs.h +++ b/src/wallet/wallet_rpc_server_commands_defs.h @@ -768,15 +768,9 @@ namespace wallet_rpc struct response { std::string tx_hash; - std::string tx_key; - uint64_t fee; - std::string tx_blob; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(tx_hash) - KV_SERIALIZE(tx_key) - KV_SERIALIZE(fee) - KV_SERIALIZE(tx_blob) END_KV_SERIALIZE_MAP() }; }; From 93e7627d5a3d071418141a03b587d728e90dcd98 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 24 Aug 2018 09:50:34 +0000 Subject: [PATCH 0289/1404] cryptonote_format_utils: do not early out on invalid tx pubkeys Another such pubkey might be valid --- src/cryptonote_basic/cryptonote_format_utils.cpp | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/cryptonote_basic/cryptonote_format_utils.cpp b/src/cryptonote_basic/cryptonote_format_utils.cpp index 428be1c9cc1..e9a9fba0363 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.cpp +++ b/src/cryptonote_basic/cryptonote_format_utils.cpp @@ -201,15 +201,25 @@ namespace cryptonote { crypto::key_derivation recv_derivation = AUTO_VAL_INIT(recv_derivation); bool r = hwdev.generate_key_derivation(tx_public_key, ack.m_view_secret_key, recv_derivation); - CHECK_AND_ASSERT_MES(r, false, "key image helper: failed to generate_key_derivation(" << tx_public_key << ", " << ack.m_view_secret_key << ")"); + if (!r) + { + MWARNING("key image helper: failed to generate_key_derivation(" << tx_public_key << ", " << ack.m_view_secret_key << ")"); + memcpy(&recv_derivation, rct::identity().bytes, sizeof(recv_derivation)); + } std::vector additional_recv_derivations; for (size_t i = 0; i < additional_tx_public_keys.size(); ++i) { crypto::key_derivation additional_recv_derivation = AUTO_VAL_INIT(additional_recv_derivation); r = hwdev.generate_key_derivation(additional_tx_public_keys[i], ack.m_view_secret_key, additional_recv_derivation); - CHECK_AND_ASSERT_MES(r, false, "key image helper: failed to generate_key_derivation(" << additional_tx_public_keys[i] << ", " << ack.m_view_secret_key << ")"); - additional_recv_derivations.push_back(additional_recv_derivation); + if (!r) + { + MWARNING("key image helper: failed to generate_key_derivation(" << additional_tx_public_keys[i] << ", " << ack.m_view_secret_key << ")"); + } + else + { + additional_recv_derivations.push_back(additional_recv_derivation); + } } boost::optional subaddr_recv_info = is_out_to_acc_precomp(subaddresses, out_key, recv_derivation, additional_recv_derivations, real_output_index,hwdev); From 88fbc4a567c71b3339801a99a4d31b483f81036c Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 3 Sep 2018 11:22:38 +0000 Subject: [PATCH 0290/1404] wallet2: fill in v2 height for stagenet --- src/wallet/wallet2.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 96b77a7a6ec..f800eb97d52 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -4751,7 +4751,7 @@ bool wallet2::is_tx_spendtime_unlocked(uint64_t unlock_time, uint64_t block_heig uint64_t current_time = static_cast(time(NULL)); // XXX: this needs to be fast, so we'd need to get the starting heights // from the daemon to be correct once voting kicks in - uint64_t v2height = m_nettype == TESTNET ? 624634 : m_nettype == STAGENET ? (uint64_t)-1/*TODO*/ : 1009827; + uint64_t v2height = m_nettype == TESTNET ? 624634 : m_nettype == STAGENET ? 32000 : 1009827; uint64_t leeway = block_height < v2height ? CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS_V1 : CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS_V2; if(current_time + leeway >= unlock_time) return true; From be6acfd5be44c07e72982db34d0386423deacdb0 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 3 Sep 2018 10:48:28 +0000 Subject: [PATCH 0291/1404] wallet2: factor new blockchain setup --- src/wallet/wallet2.cpp | 52 +++++++++++++----------------------------- src/wallet/wallet2.h | 2 ++ 2 files changed, 18 insertions(+), 36 deletions(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 96b77a7a6ec..6130506a50f 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -3324,6 +3324,15 @@ void wallet2::decrypt_keys(const epee::wipeable_string &password) decrypt_keys(key); } +void wallet2::setup_new_blockchain() +{ + cryptonote::block b; + generate_genesis(b); + m_blockchain.push_back(get_block_hash(b)); + m_last_block_reward = cryptonote::get_outs_money_amount(b.miner_tx); + add_subaddress_account(tr("Primary account")); +} + /*! * \brief Generates a wallet or restores one. * \param wallet_ Name of wallet file @@ -3412,11 +3421,7 @@ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string& } } - cryptonote::block b; - generate_genesis(b); - m_blockchain.push_back(get_block_hash(b)); - m_last_block_reward = cryptonote::get_outs_money_amount(b.miner_tx); - add_subaddress_account(tr("Primary account")); + setup_new_blockchain(); if (!wallet_.empty()) store(); @@ -3472,11 +3477,7 @@ crypto::secret_key wallet2::generate(const std::string& wallet_, const epee::wip } } - cryptonote::block b; - generate_genesis(b); - m_blockchain.push_back(get_block_hash(b)); - m_last_block_reward = cryptonote::get_outs_money_amount(b.miner_tx); - add_subaddress_account(tr("Primary account")); + setup_new_blockchain(); if (!wallet_.empty()) store(); @@ -3569,11 +3570,7 @@ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string& } } - cryptonote::block b; - generate_genesis(b); - m_blockchain.push_back(get_block_hash(b)); - m_last_block_reward = cryptonote::get_outs_money_amount(b.miner_tx); - add_subaddress_account(tr("Primary account")); + setup_new_blockchain(); if (!wallet_.empty()) store(); @@ -3623,11 +3620,7 @@ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string& } } - cryptonote::block b; - generate_genesis(b); - m_blockchain.push_back(get_block_hash(b)); - m_last_block_reward = cryptonote::get_outs_money_amount(b.miner_tx); - add_subaddress_account(tr("Primary account")); + setup_new_blockchain(); if (!wallet_.empty()) store(); @@ -3665,17 +3658,13 @@ void wallet2::restore(const std::string& wallet_, const epee::wipeable_string& p r = file_io_utils::save_string_to_file(m_wallet_file + ".address.txt", m_account.get_public_address_str(m_nettype)); if(!r) MERROR("String with address text not saved"); } - cryptonote::block b; - generate_genesis(b); - m_blockchain.push_back(get_block_hash(b)); if (m_subaddress_lookahead_major == SUBADDRESS_LOOKAHEAD_MAJOR && m_subaddress_lookahead_minor == SUBADDRESS_LOOKAHEAD_MINOR) { // the default lookahead setting (50:200) is clearly too much for hardware wallet m_subaddress_lookahead_major = 5; m_subaddress_lookahead_minor = 20; } - m_last_block_reward = cryptonote::get_outs_money_amount(b.miner_tx); - add_subaddress_account(tr("Primary account")); + setup_new_blockchain(); if (!wallet_.empty()) { store(); } @@ -3782,11 +3771,7 @@ std::string wallet2::make_multisig(const epee::wipeable_string &password, } } - cryptonote::block b; - generate_genesis(b); - m_blockchain.push_back(get_block_hash(b)); - m_last_block_reward = cryptonote::get_outs_money_amount(b.miner_tx); - add_subaddress_account(tr("Primary account")); + setup_new_blockchain(); if (!m_wallet_file.empty()) store(); @@ -4709,12 +4694,7 @@ void wallet2::rescan_blockchain(bool refresh) { clear(); - cryptonote::block genesis; - generate_genesis(genesis); - crypto::hash genesis_hash = get_block_hash(genesis); - m_blockchain.push_back(genesis_hash); - m_last_block_reward = cryptonote::get_outs_money_amount(genesis.miner_tx); - add_subaddress_account(tr("Primary account")); + setup_new_blockchain(); if (refresh) this->refresh(false); diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 2d45f4e3e8a..9d74cbf157d 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -1241,6 +1241,8 @@ namespace tools void cache_tx_data(const cryptonote::transaction& tx, const crypto::hash &txid, tx_cache_data &tx_cache_data) const; + void setup_new_blockchain(); + cryptonote::account_base m_account; boost::optional m_daemon_login; std::string m_daemon_address; From 20171746de19a9036a2a87e1929632433796925e Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 3 Sep 2018 11:13:23 +0000 Subject: [PATCH 0292/1404] wallet2: factor the creation of a new wallet keys file --- src/simplewallet/simplewallet.cpp | 3 +- src/wallet/wallet2.cpp | 99 ++++++++----------------------- src/wallet/wallet2.h | 4 +- 3 files changed, 29 insertions(+), 77 deletions(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index bdf4212ce13..09b965e7432 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -3678,7 +3678,8 @@ boost::optional simple_wallet::new_wallet(const boost::pr try { - m_wallet->restore(m_wallet_file, std::move(rc.second).password(), device_name); + bool create_address_file = command_line::get_arg(vm, arg_create_address_file); + m_wallet->restore(m_wallet_file, std::move(rc.second).password(), device_name, create_address_file); message_writer(console_color_white, true) << tr("Generated new wallet on hw device: ") << m_wallet->get_account().get_public_address_str(m_wallet->nettype()); } diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 6130506a50f..d7fbcee42b2 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -3333,6 +3333,22 @@ void wallet2::setup_new_blockchain() add_subaddress_account(tr("Primary account")); } +void wallet2::create_keys_file(const std::string &wallet_, bool watch_only, const epee::wipeable_string &password, bool create_address_file) +{ + if (!wallet_.empty()) + { + bool r = store_keys(m_keys_file, password, watch_only); + THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, m_keys_file); + + if (create_address_file) + { + r = file_io_utils::save_string_to_file(m_wallet_file + ".address.txt", m_account.get_public_address_str(m_nettype)); + if(!r) MERROR("String with address text not saved"); + } + } +} + + /*! * \brief Generates a wallet or restores one. * \param wallet_ Name of wallet file @@ -3409,18 +3425,7 @@ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string& m_key_on_device = false; setup_keys(password); - if (!wallet_.empty()) - { - bool r = store_keys(m_keys_file, password, false); - THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, m_keys_file); - - if (m_nettype != MAINNET || create_address_file) - { - r = file_io_utils::save_string_to_file(m_wallet_file + ".address.txt", m_account.get_public_address_str(m_nettype)); - if(!r) MERROR("String with address text not saved"); - } - } - + create_keys_file(wallet_, false, password, m_nettype != MAINNET || create_address_file); setup_new_blockchain(); if (!wallet_.empty()) @@ -3465,17 +3470,7 @@ crypto::secret_key wallet2::generate(const std::string& wallet_, const epee::wip m_refresh_from_block_height = estimate_blockchain_height(); } - if (!wallet_.empty()) - { - bool r = store_keys(m_keys_file, password, false); - THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, m_keys_file); - - if (m_nettype != MAINNET || create_address_file) - { - r = file_io_utils::save_string_to_file(m_wallet_file + ".address.txt", m_account.get_public_address_str(m_nettype)); - if(!r) MERROR("String with address text not saved"); - } - } + create_keys_file(wallet_, false, password, m_nettype != MAINNET || create_address_file); setup_new_blockchain(); @@ -3558,17 +3553,7 @@ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string& m_key_on_device = false; setup_keys(password); - if (!wallet_.empty()) - { - bool r = store_keys(m_keys_file, password, true); - THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, m_keys_file); - - if (m_nettype != MAINNET || create_address_file) - { - r = file_io_utils::save_string_to_file(m_wallet_file + ".address.txt", m_account.get_public_address_str(m_nettype)); - if(!r) MERROR("String with address text not saved"); - } - } + create_keys_file(wallet_, true, password, m_nettype != MAINNET || create_address_file); setup_new_blockchain(); @@ -3608,17 +3593,7 @@ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string& m_key_on_device = false; setup_keys(password); - if (!wallet_.empty()) - { - bool r = store_keys(m_keys_file, password, false); - THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, m_keys_file); - - if (m_nettype != MAINNET || create_address_file) - { - r = file_io_utils::save_string_to_file(m_wallet_file + ".address.txt", m_account.get_public_address_str(m_nettype)); - if(!r) MERROR("String with address text not saved"); - } - } + create_keys_file(wallet_, false, password, create_address_file); setup_new_blockchain(); @@ -3632,7 +3607,7 @@ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string& * \param password Password of wallet file * \param device_name device string address */ -void wallet2::restore(const std::string& wallet_, const epee::wipeable_string& password, const std::string &device_name) +void wallet2::restore(const std::string& wallet_, const epee::wipeable_string& password, const std::string &device_name, bool create_address_file) { clear(); prepare_file_names(wallet_); @@ -3651,13 +3626,7 @@ void wallet2::restore(const std::string& wallet_, const epee::wipeable_string& p m_multisig_signers.clear(); setup_keys(password); - if (!wallet_.empty()) { - bool r = store_keys(m_keys_file, password, false); - THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, m_keys_file); - - r = file_io_utils::save_string_to_file(m_wallet_file + ".address.txt", m_account.get_public_address_str(m_nettype)); - if(!r) MERROR("String with address text not saved"); - } + create_keys_file(wallet_, false, password, m_nettype != MAINNET || create_address_file); if (m_subaddress_lookahead_major == SUBADDRESS_LOOKAHEAD_MAJOR && m_subaddress_lookahead_minor == SUBADDRESS_LOOKAHEAD_MINOR) { // the default lookahead setting (50:200) is clearly too much for hardware wallet @@ -3759,17 +3728,7 @@ std::string wallet2::make_multisig(const epee::wipeable_string &password, // re-encrypt keys keys_reencryptor = epee::misc_utils::auto_scope_leave_caller(); - if (!m_wallet_file.empty()) - { - bool r = store_keys(m_keys_file, password, false); - THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, m_keys_file); - - if (boost::filesystem::exists(m_wallet_file + ".address.txt")) - { - r = file_io_utils::save_string_to_file(m_wallet_file + ".address.txt", m_account.get_public_address_str(m_nettype)); - if(!r) MERROR("String with address text not saved"); - } - } + create_keys_file(m_wallet_file, false, password, boost::filesystem::exists(m_wallet_file + ".address.txt")); setup_new_blockchain(); @@ -3873,17 +3832,7 @@ bool wallet2::finalize_multisig(const epee::wipeable_string &password, std::unor // keys are encrypted again keys_reencryptor = epee::misc_utils::auto_scope_leave_caller(); - if (!m_wallet_file.empty()) - { - bool r = store_keys(m_keys_file, password, false); - THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, m_keys_file); - - if (boost::filesystem::exists(m_wallet_file + ".address.txt")) - { - r = file_io_utils::save_string_to_file(m_wallet_file + ".address.txt", m_account.get_public_address_str(m_nettype)); - if(!r) MERROR("String with address text not saved"); - } - } + create_keys_file(m_wallet_file, false, password, boost::filesystem::exists(m_wallet_file + ".address.txt")); m_subaddresses.clear(); m_subaddress_labels.clear(); diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 9d74cbf157d..84d7475f175 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -544,8 +544,9 @@ namespace tools * \param wallet_ Name of wallet file * \param password Password of wallet file * \param device_name name of HW to use + * \param create_address_file Whether to create an address file */ - void restore(const std::string& wallet_, const epee::wipeable_string& password, const std::string &device_name); + void restore(const std::string& wallet_, const epee::wipeable_string& password, const std::string &device_name, bool create_address_file); /*! * \brief Creates a multisig wallet @@ -1242,6 +1243,7 @@ namespace tools void cache_tx_data(const cryptonote::transaction& tx, const crypto::hash &txid, tx_cache_data &tx_cache_data) const; void setup_new_blockchain(); + void create_keys_file(const std::string &wallet_, bool watch_only, const epee::wipeable_string &password, bool create_address_file); cryptonote::account_base m_account; boost::optional m_daemon_login; From 5f8f56315ce55103fc56c12b2b5178084ccf314c Mon Sep 17 00:00:00 2001 From: stoffu Date: Tue, 4 Sep 2018 13:00:38 +0900 Subject: [PATCH 0293/1404] wallet2.get_reserve_proof: throw when specified amount is zero --- src/wallet/wallet2.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 96b77a7a6ec..d207f35e5f2 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -9566,6 +9566,7 @@ std::string wallet2::get_reserve_proof(const boost::optionalsecond == 0, error::wallet_internal_error, "Proved amount must be greater than 0"); // minimize the number of outputs included in the proof, by only picking the N largest outputs that can cover the requested min reserve amount std::sort(selected_transfers.begin(), selected_transfers.end(), [&](const size_t a, const size_t b) { return m_transfers[a].amount() > m_transfers[b].amount(); }); From 7c8f95d3e22a63c5c990f518c4a90897d4adf4ce Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 16 Aug 2018 22:29:22 +0000 Subject: [PATCH 0294/1404] ringct: make conversion functions return const refs This might avoid unnecessary copies. Reported by stoffu --- src/ringct/rctTypes.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/ringct/rctTypes.h b/src/ringct/rctTypes.h index 452a68eb281..f6987d3f37b 100644 --- a/src/ringct/rctTypes.h +++ b/src/ringct/rctTypes.h @@ -517,14 +517,14 @@ namespace rct { //int[64] to uint long long xmr_amount b2d(bits amountb); - static inline const rct::key pk2rct(const crypto::public_key &pk) { return (const rct::key&)pk; } - static inline const rct::key sk2rct(const crypto::secret_key &sk) { return (const rct::key&)sk; } - static inline const rct::key ki2rct(const crypto::key_image &ki) { return (const rct::key&)ki; } - static inline const rct::key hash2rct(const crypto::hash &h) { return (const rct::key&)h; } - static inline const crypto::public_key rct2pk(const rct::key &k) { return (const crypto::public_key&)k; } - static inline const crypto::secret_key rct2sk(const rct::key &k) { return (const crypto::secret_key&)k; } - static inline const crypto::key_image rct2ki(const rct::key &k) { return (const crypto::key_image&)k; } - static inline const crypto::hash rct2hash(const rct::key &k) { return (const crypto::hash&)k; } + static inline const rct::key &pk2rct(const crypto::public_key &pk) { return (const rct::key&)pk; } + static inline const rct::key &sk2rct(const crypto::secret_key &sk) { return (const rct::key&)sk; } + static inline const rct::key &ki2rct(const crypto::key_image &ki) { return (const rct::key&)ki; } + static inline const rct::key &hash2rct(const crypto::hash &h) { return (const rct::key&)h; } + static inline const crypto::public_key &rct2pk(const rct::key &k) { return (const crypto::public_key&)k; } + static inline const crypto::secret_key &rct2sk(const rct::key &k) { return (const crypto::secret_key&)k; } + static inline const crypto::key_image &rct2ki(const rct::key &k) { return (const crypto::key_image&)k; } + static inline const crypto::hash &rct2hash(const rct::key &k) { return (const crypto::hash&)k; } static inline bool operator==(const rct::key &k0, const crypto::public_key &k1) { return !crypto_verify_32(k0.bytes, (const unsigned char*)&k1); } static inline bool operator!=(const rct::key &k0, const crypto::public_key &k1) { return crypto_verify_32(k0.bytes, (const unsigned char*)&k1); } } From e6117282284032cd759783a3dee2cf22225588d0 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 5 Sep 2018 09:41:07 +0000 Subject: [PATCH 0295/1404] daemon: request no PoW hashes we don't need when asking for blocks This fixes the horrendous slowdown in bc_dyn_stats --- src/daemon/rpc_command_executor.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp index 45ba81e1687..a3946ab7537 100644 --- a/src/daemon/rpc_command_executor.cpp +++ b/src/daemon/rpc_command_executor.cpp @@ -532,6 +532,7 @@ bool t_rpc_command_executor::print_blockchain_info(uint64_t start_block_index, u req.start_height = start_block_index; req.end_height = end_block_index; + req.fill_pow_hash = false; std::string fail_message = "Unsuccessful"; @@ -1743,6 +1744,7 @@ bool t_rpc_command_executor::print_blockchain_dynamic_stats(uint64_t nblocks) bhreq.start_height = ires.height - nblocks; bhreq.end_height = ires.height - 1; + bhreq.fill_pow_hash = false; if (m_is_rpc) { if (!m_rpc_client->json_rpc_request(bhreq, bhres, "getblockheadersrange", fail_message.c_str())) From 54b859bea5ad2b98d9db0b4e1e26e69a7bc7130c Mon Sep 17 00:00:00 2001 From: Guillaume LE VAILLANT Date: Wed, 5 Sep 2018 16:38:26 +0200 Subject: [PATCH 0296/1404] wallet rpc: Add close_wallet RPC And close the current wallet automatically if necessary when opening another wallet. --- src/wallet/wallet_rpc_server.cpp | 42 ++++++++++++++++++++ src/wallet/wallet_rpc_server.h | 2 + src/wallet/wallet_rpc_server_commands_defs.h | 15 +++++++ 3 files changed, 59 insertions(+) diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index 67f26c7a789..96ad23e604f 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -2678,8 +2678,20 @@ namespace tools er.message = "Failed to generate wallet"; return false; } + if (m_wallet) + { + try + { + m_wallet->store(); + } + catch (const std::exception& e) + { + handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR); + return false; + } delete m_wallet; + } m_wallet = wal.release(); return true; } @@ -2736,12 +2748,42 @@ namespace tools er.message = "Failed to open wallet"; return false; } + if (m_wallet) + { + try + { + m_wallet->store(); + } + catch (const std::exception& e) + { + handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR); + return false; + } delete m_wallet; + } m_wallet = wal.release(); return true; } //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::on_close_wallet(const wallet_rpc::COMMAND_RPC_CLOSE_WALLET::request& req, wallet_rpc::COMMAND_RPC_CLOSE_WALLET::response& res, epee::json_rpc::error& er) + { + if (!m_wallet) return not_open(er); + + try + { + m_wallet->store(); + } + catch (const std::exception& e) + { + handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR); + return false; + } + delete m_wallet; + m_wallet = NULL; + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_change_wallet_password(const wallet_rpc::COMMAND_RPC_CHANGE_WALLET_PASSWORD::request& req, wallet_rpc::COMMAND_RPC_CHANGE_WALLET_PASSWORD::response& res, epee::json_rpc::error& er) { if (!m_wallet) return not_open(er); diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h index 7525b9dd088..ab7917a7810 100644 --- a/src/wallet/wallet_rpc_server.h +++ b/src/wallet/wallet_rpc_server.h @@ -133,6 +133,7 @@ namespace tools MAP_JON_RPC_WE("get_languages", on_get_languages, wallet_rpc::COMMAND_RPC_GET_LANGUAGES) MAP_JON_RPC_WE("create_wallet", on_create_wallet, wallet_rpc::COMMAND_RPC_CREATE_WALLET) MAP_JON_RPC_WE("open_wallet", on_open_wallet, wallet_rpc::COMMAND_RPC_OPEN_WALLET) + MAP_JON_RPC_WE("close_wallet", on_close_wallet, wallet_rpc::COMMAND_RPC_CLOSE_WALLET) MAP_JON_RPC_WE("change_wallet_password", on_change_wallet_password, wallet_rpc::COMMAND_RPC_CHANGE_WALLET_PASSWORD) MAP_JON_RPC_WE("is_multisig", on_is_multisig, wallet_rpc::COMMAND_RPC_IS_MULTISIG) MAP_JON_RPC_WE("prepare_multisig", on_prepare_multisig, wallet_rpc::COMMAND_RPC_PREPARE_MULTISIG) @@ -209,6 +210,7 @@ namespace tools bool on_get_languages(const wallet_rpc::COMMAND_RPC_GET_LANGUAGES::request& req, wallet_rpc::COMMAND_RPC_GET_LANGUAGES::response& res, epee::json_rpc::error& er); bool on_create_wallet(const wallet_rpc::COMMAND_RPC_CREATE_WALLET::request& req, wallet_rpc::COMMAND_RPC_CREATE_WALLET::response& res, epee::json_rpc::error& er); bool on_open_wallet(const wallet_rpc::COMMAND_RPC_OPEN_WALLET::request& req, wallet_rpc::COMMAND_RPC_OPEN_WALLET::response& res, epee::json_rpc::error& er); + bool on_close_wallet(const wallet_rpc::COMMAND_RPC_CLOSE_WALLET::request& req, wallet_rpc::COMMAND_RPC_CLOSE_WALLET::response& res, epee::json_rpc::error& er); bool on_change_wallet_password(const wallet_rpc::COMMAND_RPC_CHANGE_WALLET_PASSWORD::request& req, wallet_rpc::COMMAND_RPC_CHANGE_WALLET_PASSWORD::response& res, epee::json_rpc::error& er); bool on_is_multisig(const wallet_rpc::COMMAND_RPC_IS_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_IS_MULTISIG::response& res, epee::json_rpc::error& er); bool on_prepare_multisig(const wallet_rpc::COMMAND_RPC_PREPARE_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_PREPARE_MULTISIG::response& res, epee::json_rpc::error& er); diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h index 62fb98ebb45..2d0f8c65906 100644 --- a/src/wallet/wallet_rpc_server_commands_defs.h +++ b/src/wallet/wallet_rpc_server_commands_defs.h @@ -1829,6 +1829,21 @@ namespace wallet_rpc }; }; + struct COMMAND_RPC_CLOSE_WALLET + { + struct request + { + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; + + struct response + { + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; + }; + struct COMMAND_RPC_CHANGE_WALLET_PASSWORD { struct request From 6929b524266d77b661d0e19a50e73ad0c9a05903 Mon Sep 17 00:00:00 2001 From: iDunk5400 Date: Thu, 6 Sep 2018 10:15:51 +0200 Subject: [PATCH 0297/1404] Windows: don't preset CMAKE_SYSTEM_NAME Let it be autodiscovered instead to avoid cmake thinking it's crosscompiling builds where host = target. This resolves a cmake configure error in MSYS2 caused by CMAKE_CROSSCOMPILING checks in #4294. --- cmake/32-bit-toolchain.cmake | 4 +++- cmake/64-bit-toolchain.cmake | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/cmake/32-bit-toolchain.cmake b/cmake/32-bit-toolchain.cmake index fe9aa483f5c..253523e6636 100644 --- a/cmake/32-bit-toolchain.cmake +++ b/cmake/32-bit-toolchain.cmake @@ -26,7 +26,9 @@ # 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. -set (CMAKE_SYSTEM_NAME Windows) +if (NOT CMAKE_HOST_WIN32) + set (CMAKE_SYSTEM_NAME Windows) +endif() set (GCC_PREFIX i686-w64-mingw32) set (CMAKE_C_COMPILER ${GCC_PREFIX}-gcc) diff --git a/cmake/64-bit-toolchain.cmake b/cmake/64-bit-toolchain.cmake index 20b568c71c8..b4b52834759 100644 --- a/cmake/64-bit-toolchain.cmake +++ b/cmake/64-bit-toolchain.cmake @@ -26,7 +26,9 @@ # 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. -set (CMAKE_SYSTEM_NAME Windows) +if (NOT CMAKE_HOST_WIN32) + set (CMAKE_SYSTEM_NAME Windows) +endif() set (GCC_PREFIX x86_64-w64-mingw32) set (CMAKE_C_COMPILER ${GCC_PREFIX}-gcc) From cd647612b06dc84a9d24433d383f7ed3d3fc0b40 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 7 Sep 2018 13:28:06 +0000 Subject: [PATCH 0298/1404] rpc: don't include start time if restricted --- src/rpc/core_rpc_server.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index 0a6daf8f04d..3bc68c2cc1b 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -198,7 +198,7 @@ namespace cryptonote res.block_size_limit = m_core.get_blockchain_storage().get_current_cumulative_blocksize_limit(); res.block_size_median = m_core.get_blockchain_storage().get_current_cumulative_blocksize_median(); res.status = CORE_RPC_STATUS_OK; - res.start_time = (uint64_t)m_core.get_start_time(); + res.start_time = m_restricted ? 0 : (uint64_t)m_core.get_start_time(); res.free_space = m_restricted ? std::numeric_limits::max() : m_core.get_free_space(); res.offline = m_core.offline(); res.bootstrap_daemon_address = m_bootstrap_daemon_address; From 97764bae3a3caaeeb4d96a063d1cc0ff1b3489c1 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 4 Sep 2018 09:18:46 +0000 Subject: [PATCH 0299/1404] wallet_rpc_server: error out if wallet-file and wallet-dir are both used --- src/wallet/wallet_rpc_server.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index 2cddea25d17..45bc0a1f307 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -179,8 +179,13 @@ namespace tools } } m_restricted = command_line::get_arg(*m_vm, arg_restricted); - if (command_line::has_arg(*m_vm, arg_wallet_dir)) + if (!command_line::is_arg_defaulted(*m_vm, arg_wallet_dir)) { + if (!command_line::is_arg_defaulted(*m_vm, wallet_args::arg_wallet_file())) + { + MERROR(arg_wallet_dir.name << " and " << wallet_args::arg_wallet_file().name << " are incompatible, use only one of them"); + return false; + } m_wallet_dir = command_line::get_arg(*m_vm, arg_wallet_dir); #ifdef _WIN32 #define MKDIR(path, mode) mkdir(path) From df0e7c2feb65c83fdaf2262cb653be568fc8b58c Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 8 Sep 2018 23:11:16 +0000 Subject: [PATCH 0300/1404] wallet2: fix secondary partially signed multisig txes --- src/wallet/wallet2.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index a2e36706eef..ac4f67b7447 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -7296,7 +7296,7 @@ void wallet2::transfer_selected_rct(std::vector Date: Sun, 9 Sep 2018 10:46:58 +0000 Subject: [PATCH 0301/1404] abstract_tcp_server2: fix binding to the wrong IP --- contrib/epee/include/net/abstract_tcp_server2.inl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/contrib/epee/include/net/abstract_tcp_server2.inl b/contrib/epee/include/net/abstract_tcp_server2.inl index d837208d381..3a5c830179d 100644 --- a/contrib/epee/include/net/abstract_tcp_server2.inl +++ b/contrib/epee/include/net/abstract_tcp_server2.inl @@ -1109,12 +1109,12 @@ POP_WARNINGS sock_.open(remote_endpoint.protocol()); if(bind_ip != "0.0.0.0" && bind_ip != "0" && bind_ip != "" ) { - boost::asio::ip::tcp::endpoint local_endpoint(boost::asio::ip::address::from_string(adr.c_str()), 0); + boost::asio::ip::tcp::endpoint local_endpoint(boost::asio::ip::address::from_string(bind_ip.c_str()), 0); boost::system::error_code ec; sock_.bind(local_endpoint, ec); if (ec) { - MERROR("Error binding to " << adr << ": " << ec.message()); + MERROR("Error binding to " << bind_ip << ": " << ec.message()); if (sock_.is_open()) sock_.close(); return false; @@ -1223,12 +1223,12 @@ POP_WARNINGS sock_.open(remote_endpoint.protocol()); if(bind_ip != "0.0.0.0" && bind_ip != "0" && bind_ip != "" ) { - boost::asio::ip::tcp::endpoint local_endpoint(boost::asio::ip::address::from_string(adr.c_str()), 0); + boost::asio::ip::tcp::endpoint local_endpoint(boost::asio::ip::address::from_string(bind_ip.c_str()), 0); boost::system::error_code ec; sock_.bind(local_endpoint, ec); if (ec) { - MERROR("Error binding to " << adr << ": " << ec.message()); + MERROR("Error binding to " << bind_ip << ": " << ec.message()); if (sock_.is_open()) sock_.close(); return false; From 7418aa60055464031c9814718027793765f45198 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 9 Sep 2018 10:03:39 +0000 Subject: [PATCH 0302/1404] README: bump cmake requirement to 3.5, as required by miniupnpc --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 637be8ed720..96774851efd 100644 --- a/README.md +++ b/README.md @@ -132,7 +132,7 @@ library archives (`.a`). | Dep | Min. version | Vendored | Debian/Ubuntu pkg | Arch pkg | Fedora | Optional | Purpose | | ------------ | ------------- | -------- | ------------------ | ------------ | ----------------- | -------- | -------------- | | GCC | 4.7.3 | NO | `build-essential` | `base-devel` | `gcc` | NO | | -| CMake | 3.0.0 | NO | `cmake` | `cmake` | `cmake` | NO | | +| CMake | 3.5 | NO | `cmake` | `cmake` | `cmake` | NO | | | pkg-config | any | NO | `pkg-config` | `base-devel` | `pkgconf` | NO | | | Boost | 1.58 | NO | `libboost-all-dev` | `boost` | `boost-devel` | NO | C++ libraries | | OpenSSL | basically any | NO | `libssl-dev` | `openssl` | `openssl-devel` | NO | sha256 sum | From 347bba9dd15fff187d23830940cc317aa58519d3 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 26 Jul 2018 14:07:04 +0100 Subject: [PATCH 0303/1404] CMakeLists.txt: detect -fcf-protection=full and -fstack-clash-protection Introduced with GCC 8.2 --- CMakeLists.txt | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index a02f62e624b..e4b6bfe019b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -570,6 +570,14 @@ else() add_cxx_flag_if_supported(-fstack-protector-strong CXX_SECURITY_FLAGS) endif() + # New in GCC 8.2 + if (NOT WIN32) + add_c_flag_if_supported(-fcf-protection=full C_SECURITY_FLAGS) + add_cxx_flag_if_supported(-fcf-protection=full CXX_SECURITY_FLAGS) + add_c_flag_if_supported(-fstack-clash-protection C_SECURITY_FLAGS) + add_cxx_flag_if_supported(-fstack-clash-protection CXX_SECURITY_FLAGS) + endif() + # linker if (NOT WIN32) # Windows binaries die on startup with PIE From 50cb370d5babcef5caa9f90981c475ca5fe3f887 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 12 Aug 2018 21:41:44 +0000 Subject: [PATCH 0304/1404] ringdb: allow blackballing many outputs at once It cuts down on txn commits, and speeds up blackballing substantially --- .../blockchain_blackball.cpp | 21 +++- src/wallet/ringdb.cpp | 95 +++++++++++-------- src/wallet/ringdb.h | 3 +- src/wallet/wallet2.cpp | 3 +- 4 files changed, 77 insertions(+), 45 deletions(-) diff --git a/src/blockchain_utilities/blockchain_blackball.cpp b/src/blockchain_utilities/blockchain_blackball.cpp index 1653910fcbe..04a894fe45f 100644 --- a/src/blockchain_utilities/blockchain_blackball.cpp +++ b/src/blockchain_utilities/blockchain_blackball.cpp @@ -417,6 +417,7 @@ int main(int argc, char* argv[]) if (it != state.processed_heights.end()) start_idx = it->second; LOG_PRINT_L0("Reading blockchain from " << inputs[n] << " from " << start_idx); + std::vector blackballs; for_all_transactions(inputs[n], start_idx, [&](const cryptonote::transaction_prefix &tx)->bool { for (const auto &in: tx.vin) @@ -439,7 +440,7 @@ int main(int argc, char* argv[]) { const crypto::public_key pkey = core_storage[n]->get_output_key(txin.amount, absolute[0]); MINFO("Blackballing output " << pkey << ", due to being used in a 1-ring"); - ringdb.blackball(pkey); + blackballs.push_back(pkey); newly_spent.insert(output_data(txin.amount, absolute[0])); } else if (state.ring_instances[new_ring] == new_ring.size()) @@ -448,7 +449,7 @@ int main(int argc, char* argv[]) { const crypto::public_key pkey = core_storage[n]->get_output_key(txin.amount, absolute[o]); MINFO("Blackballing output " << pkey << ", due to being used in " << new_ring.size() << " identical " << new_ring.size() << "-rings"); - ringdb.blackball(pkey); + blackballs.push_back(pkey); newly_spent.insert(output_data(txin.amount, absolute[o])); } } @@ -476,7 +477,7 @@ int main(int argc, char* argv[]) { const crypto::public_key pkey = core_storage[n]->get_output_key(txin.amount, common[0]); MINFO("Blackballing output " << pkey << ", due to being used in rings with a single common element"); - ringdb.blackball(pkey); + blackballs.push_back(pkey); newly_spent.insert(output_data(txin.amount, common[0])); } else @@ -490,6 +491,11 @@ int main(int argc, char* argv[]) } } state.relative_rings[txin.k_image] = new_ring; + if (!blackballs.empty()) + { + ringdb.blackball(blackballs); + blackballs.clear(); + } } if (stop_requested) { @@ -513,6 +519,7 @@ int main(int argc, char* argv[]) for (const auto &e: work_spent) state.spent.insert(e); + std::vector blackballs; for (const output_data &od: work_spent) { for (const crypto::key_image &ki: state.outputs[od]) @@ -533,13 +540,19 @@ int main(int argc, char* argv[]) const crypto::public_key pkey = core_storage[0]->get_output_key(od.amount, last_unknown); MINFO("Blackballing output " << pkey << ", due to being used in a " << absolute.size() << "-ring where all other outputs are known to be spent"); - ringdb.blackball(pkey); + blackballs.push_back(pkey); newly_spent.insert(output_data(od.amount, last_unknown)); } } } } + if (!blackballs.empty()) + { + ringdb.blackball(blackballs); + blackballs.clear(); + } + LOG_PRINT_L0("Saving state data to " << state_file_path); std::ofstream state_data_out; state_data_out.open(state_file_path, std::ios_base::binary | std::ios_base::out | std::ios::trunc); diff --git a/src/wallet/ringdb.cpp b/src/wallet/ringdb.cpp index 3f2634c8b1e..bf5478f5ee3 100644 --- a/src/wallet/ringdb.cpp +++ b/src/wallet/ringdb.cpp @@ -146,7 +146,7 @@ static int resize_env(MDB_env *env, const char *db_path, size_t needed) MDB_stat mst; int ret; - needed = std::max(needed, (size_t)(2ul * 1024 * 1024)); // at least 2 MB + needed = std::max(needed, (size_t)(100ul * 1024 * 1024)); // at least 100 MB ret = mdb_env_info(env, &mei); if (ret) @@ -374,7 +374,7 @@ bool ringdb::set_ring(const crypto::chacha_key &chacha_key, const crypto::key_im return true; } -bool ringdb::blackball_worker(const crypto::public_key &output, int op) +bool ringdb::blackball_worker(const std::vector &outputs, int op) { MDB_txn *txn; MDB_cursor *cursor; @@ -382,7 +382,9 @@ bool ringdb::blackball_worker(const crypto::public_key &output, int op) bool tx_active = false; bool ret = true; - dbr = resize_env(env, filename.c_str(), 32 * 2); // a pubkey, and some slack + THROW_WALLET_EXCEPTION_IF(outputs.size() > 1 && op == BLACKBALL_QUERY, tools::error::wallet_internal_error, "Blackball query only makes sense for a single output"); + + dbr = resize_env(env, filename.c_str(), 32 * 2 * outputs.size()); // a pubkey, and some slack THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to set env map size: " + std::string(mdb_strerror(dbr))); dbr = mdb_txn_begin(env, NULL, 0, &txn); THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to create LMDB transaction: " + std::string(mdb_strerror(dbr))); @@ -391,40 +393,49 @@ bool ringdb::blackball_worker(const crypto::public_key &output, int op) MDB_val key = zerokeyval; MDB_val data; - data.mv_data = (void*)&output; - data.mv_size = sizeof(output); - switch (op) + for (const crypto::public_key &output: outputs) + { + data.mv_data = (void*)&output; + data.mv_size = sizeof(output); + + switch (op) + { + case BLACKBALL_BLACKBALL: + MDEBUG("Blackballing output " << output); + dbr = mdb_put(txn, dbi_blackballs, &key, &data, MDB_NODUPDATA); + if (dbr == MDB_KEYEXIST) + dbr = 0; + break; + case BLACKBALL_UNBLACKBALL: + MDEBUG("Unblackballing output " << output); + dbr = mdb_del(txn, dbi_blackballs, &key, &data); + if (dbr == MDB_NOTFOUND) + dbr = 0; + break; + case BLACKBALL_QUERY: + dbr = mdb_cursor_open(txn, dbi_blackballs, &cursor); + THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to create cursor for blackballs table: " + std::string(mdb_strerror(dbr))); + dbr = mdb_cursor_get(cursor, &key, &data, MDB_GET_BOTH); + THROW_WALLET_EXCEPTION_IF(dbr && dbr != MDB_NOTFOUND, tools::error::wallet_internal_error, "Failed to lookup in blackballs table: " + std::string(mdb_strerror(dbr))); + ret = dbr != MDB_NOTFOUND; + if (dbr == MDB_NOTFOUND) + dbr = 0; + mdb_cursor_close(cursor); + break; + case BLACKBALL_CLEAR: + break; + default: + THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, "Invalid blackball op"); + } + THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to query blackballs table: " + std::string(mdb_strerror(dbr))); + } + + if (op == BLACKBALL_CLEAR) { - case BLACKBALL_BLACKBALL: - MDEBUG("Blackballing output " << output); - dbr = mdb_put(txn, dbi_blackballs, &key, &data, MDB_NODUPDATA); - if (dbr == MDB_KEYEXIST) - dbr = 0; - break; - case BLACKBALL_UNBLACKBALL: - MDEBUG("Unblackballing output " << output); - dbr = mdb_del(txn, dbi_blackballs, &key, &data); - if (dbr == MDB_NOTFOUND) - dbr = 0; - break; - case BLACKBALL_QUERY: - dbr = mdb_cursor_open(txn, dbi_blackballs, &cursor); - THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to create cursor for blackballs table: " + std::string(mdb_strerror(dbr))); - dbr = mdb_cursor_get(cursor, &key, &data, MDB_GET_BOTH); - THROW_WALLET_EXCEPTION_IF(dbr && dbr != MDB_NOTFOUND, tools::error::wallet_internal_error, "Failed to lookup in blackballs table: " + std::string(mdb_strerror(dbr))); - ret = dbr != MDB_NOTFOUND; - if (dbr == MDB_NOTFOUND) - dbr = 0; - mdb_cursor_close(cursor); - break; - case BLACKBALL_CLEAR: - dbr = mdb_drop(txn, dbi_blackballs, 0); - break; - default: - THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, "Invalid blackball op"); + dbr = mdb_drop(txn, dbi_blackballs, 0); + THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to clear blackballs table: " + std::string(mdb_strerror(dbr))); } - THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to query blackballs table: " + std::string(mdb_strerror(dbr))); dbr = mdb_txn_commit(txn); THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to commit txn blackballing output to database: " + std::string(mdb_strerror(dbr))); @@ -432,24 +443,32 @@ bool ringdb::blackball_worker(const crypto::public_key &output, int op) return ret; } +bool ringdb::blackball(const std::vector &outputs) +{ + return blackball_worker(outputs, BLACKBALL_BLACKBALL); +} + bool ringdb::blackball(const crypto::public_key &output) { - return blackball_worker(output, BLACKBALL_BLACKBALL); + std::vector outputs(1, output); + return blackball_worker(outputs, BLACKBALL_BLACKBALL); } bool ringdb::unblackball(const crypto::public_key &output) { - return blackball_worker(output, BLACKBALL_UNBLACKBALL); + std::vector outputs(1, output); + return blackball_worker(outputs, BLACKBALL_UNBLACKBALL); } bool ringdb::blackballed(const crypto::public_key &output) { - return blackball_worker(output, BLACKBALL_QUERY); + std::vector outputs(1, output); + return blackball_worker(outputs, BLACKBALL_QUERY); } bool ringdb::clear_blackballs() { - return blackball_worker(crypto::public_key(), BLACKBALL_CLEAR); + return blackball_worker(std::vector(), BLACKBALL_CLEAR); } } diff --git a/src/wallet/ringdb.h b/src/wallet/ringdb.h index 6b4bce12450..4a78f61f88e 100644 --- a/src/wallet/ringdb.h +++ b/src/wallet/ringdb.h @@ -50,12 +50,13 @@ namespace tools bool set_ring(const crypto::chacha_key &chacha_key, const crypto::key_image &key_image, const std::vector &outs, bool relative); bool blackball(const crypto::public_key &output); + bool blackball(const std::vector &outputs); bool unblackball(const crypto::public_key &output); bool blackballed(const crypto::public_key &output); bool clear_blackballs(); private: - bool blackball_worker(const crypto::public_key &output, int op); + bool blackball_worker(const std::vector &outputs, int op); private: std::string filename; diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index a2e36706eef..ffd7131cde7 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -6238,8 +6238,7 @@ bool wallet2::set_blackballed_outputs(const std::vector &out bool ret = true; if (!add) ret &= m_ringdb->clear_blackballs(); - for (const auto &output: outputs) - ret &= m_ringdb->blackball(output); + ret &= m_ringdb->blackball(outputs); return ret; } catch (const std::exception &e) { return false; } From daa6cc7d738c80a558b3ef1beb0c51a06ac9def3 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 12 Aug 2018 21:43:33 +0000 Subject: [PATCH 0305/1404] blockchain_blackball: use LMDB for the cache This uses less memory and makes it faster to load/save, though makes it slower to run (which is actually faster since it would previously start swapping anyway). --- .../blockchain_blackball.cpp | 698 ++++++++++++++---- 1 file changed, 561 insertions(+), 137 deletions(-) diff --git a/src/blockchain_utilities/blockchain_blackball.cpp b/src/blockchain_utilities/blockchain_blackball.cpp index 04a894fe45f..b1369f4c0e6 100644 --- a/src/blockchain_utilities/blockchain_blackball.cpp +++ b/src/blockchain_utilities/blockchain_blackball.cpp @@ -50,6 +50,19 @@ namespace po = boost::program_options; using namespace epee; using namespace cryptonote; +static const char zerokey[8] = {0}; +static const MDB_val zerokval = { sizeof(zerokey), (void *)zerokey }; + +static uint64_t records_per_sync = 200; +static uint64_t db_flags = 0; +static MDB_dbi dbi_relative_rings; +static MDB_dbi dbi_outputs; +static MDB_dbi dbi_processed_txidx; +static MDB_dbi dbi_spent; +static MDB_dbi dbi_ring_instances; +static MDB_dbi dbi_newly_spent; +static MDB_env *env = NULL; + struct output_data { uint64_t amount; @@ -57,57 +70,66 @@ struct output_data output_data(): amount(0), index(0) {} output_data(uint64_t a, uint64_t i): amount(a), index(i) {} bool operator==(const output_data &other) const { return other.amount == amount && other.index == index; } - template void serialize(t_archive &a, const unsigned int ver) - { - a & amount; - a & index; - } }; -BOOST_CLASS_VERSION(output_data, 0) -namespace std +// +// relative_rings: key_image -> vector +// outputs: 128 bits -> set of key images +// processed_txidx: string -> uint64_t +// spent: 128 bits, zerokval +// ring_instances: vector -> uint64_t +// newly_spent: 128 bits, zerokval +// + +static bool parse_db_sync_mode(std::string db_sync_mode) { - template<> struct hash + std::vector options; + boost::trim(db_sync_mode); + boost::split(options, db_sync_mode, boost::is_any_of(" :")); + + for(const auto &option : options) + MDEBUG("option: " << option); + + // default to fast:async:1 + uint64_t DEFAULT_FLAGS = DBF_FAST; + + if(options.size() == 0) { - size_t operator()(const output_data &od) const + // default to fast:async:1 + db_flags = DEFAULT_FLAGS; + } + + bool safemode = false; + if(options.size() >= 1) + { + if(options[0] == "safe") { - const uint64_t data[2] = {od.amount, od.index}; - crypto::hash h; - crypto::cn_fast_hash(data, 2 * sizeof(uint64_t), h); - return reinterpret_cast(h); + safemode = true; + db_flags = DBF_SAFE; } - }; - template<> struct hash> - { - size_t operator()(const std::vector &v) const + else if(options[0] == "fast") { - crypto::hash h; - crypto::cn_fast_hash(v.data(), v.size() * sizeof(uint64_t), h); - return reinterpret_cast(h); + db_flags = DBF_FAST; } - }; -} - -struct blackball_state_t -{ - std::unordered_map> relative_rings; - std::unordered_map> outputs; - std::unordered_map processed_heights; - std::unordered_set spent; - std::unordered_map, size_t> ring_instances; + else if(options[0] == "fastest") + { + db_flags = DBF_FASTEST; + records_per_sync = 1000; // default to fastest:async:1000 + } + else + db_flags = DEFAULT_FLAGS; + } - template void serialize(t_archive &a, const unsigned int ver) + if(options.size() >= 2 && !safemode) { - a & relative_rings; - a & outputs; - a & processed_heights; - a & spent; - if (ver < 1) - return; - a & ring_instances; + char *endptr; + uint64_t bps = strtoull(options[1].c_str(), &endptr, 0); + if (*endptr == '\0') + records_per_sync = bps; } -}; -BOOST_CLASS_VERSION(blackball_state_t, 1) + + return true; +} static std::string get_default_db_path() { @@ -118,6 +140,174 @@ static std::string get_default_db_path() return dir.string(); } +static std::string get_cache_filename(boost::filesystem::path filename) +{ + if (!boost::filesystem::is_directory(filename)) + filename.remove_filename(); + return filename.string(); +} + +static int compare_hash32(const MDB_val *a, const MDB_val *b) +{ + const uint32_t *va = (const uint32_t*) a->mv_data; + const uint32_t *vb = (const uint32_t*) b->mv_data; + for (int n = 7; n >= 0; n--) + { + if (va[n] == vb[n]) + continue; + return va[n] < vb[n] ? -1 : 1; + } + + return 0; +} + +static int compare_double64(const MDB_val *a, const MDB_val *b) +{ + const uint64_t va = *(const uint64_t*) a->mv_data; + const uint64_t vb = *(const uint64_t*) b->mv_data; + if (va == vb) + { + const uint64_t va = ((const uint64_t*) a->mv_data)[1]; + const uint64_t vb = ((const uint64_t*) b->mv_data)[1]; + return va < vb ? -1 : va > vb; + } + return va < vb ? -1 : va > vb; +} + +static int resize_env(const char *db_path) +{ + MDB_envinfo mei; + MDB_stat mst; + int ret; + + size_t needed = 1000ul * 1024 * 1024; // at least 1000 MB + + ret = mdb_env_info(env, &mei); + if (ret) + return ret; + ret = mdb_env_stat(env, &mst); + if (ret) + return ret; + uint64_t size_used = mst.ms_psize * mei.me_last_pgno; + uint64_t mapsize = mei.me_mapsize; + if (size_used + needed > mei.me_mapsize) + { + try + { + boost::filesystem::path path(db_path); + boost::filesystem::space_info si = boost::filesystem::space(path); + if(si.available < needed) + { + MERROR("!! WARNING: Insufficient free space to extend database !!: " << (si.available >> 20L) << " MB available"); + return ENOSPC; + } + } + catch(...) + { + // print something but proceed. + MWARNING("Unable to query free disk space."); + } + + mapsize += needed; + } + return mdb_env_set_mapsize(env, mapsize); +} + +static void init(std::string cache_filename) +{ + MDB_txn *txn; + bool tx_active = false; + int dbr; + + MINFO("Creating blackball cache in " << cache_filename); + + tools::create_directories_if_necessary(cache_filename); + + int flags = 0; + if (db_flags & DBF_FAST) + flags |= MDB_NOSYNC; + if (db_flags & DBF_FASTEST) + flags |= MDB_NOSYNC | MDB_WRITEMAP | MDB_MAPASYNC; + + dbr = mdb_env_create(&env); + CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to create LDMB environment: " + std::string(mdb_strerror(dbr))); + dbr = mdb_env_set_maxdbs(env, 6); + CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to set max env dbs: " + std::string(mdb_strerror(dbr))); + const std::string actual_filename = get_cache_filename(cache_filename); + dbr = mdb_env_open(env, actual_filename.c_str(), flags, 0664); + CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to open rings database file '" + + actual_filename + "': " + std::string(mdb_strerror(dbr))); + + dbr = mdb_txn_begin(env, NULL, 0, &txn); + CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to create LMDB transaction: " + std::string(mdb_strerror(dbr))); + epee::misc_utils::auto_scope_leave_caller txn_dtor = epee::misc_utils::create_scope_leave_handler([&](){if (tx_active) mdb_txn_abort(txn);}); + tx_active = true; + + dbr = mdb_dbi_open(txn, "relative_rings", MDB_CREATE | MDB_INTEGERKEY, &dbi_relative_rings); + CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to open LMDB dbi: " + std::string(mdb_strerror(dbr))); + mdb_set_compare(txn, dbi_relative_rings, compare_hash32); + + dbr = mdb_dbi_open(txn, "outputs", MDB_CREATE | MDB_INTEGERKEY, &dbi_outputs); + CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to open LMDB dbi: " + std::string(mdb_strerror(dbr))); + mdb_set_compare(txn, dbi_outputs, compare_double64); + + dbr = mdb_dbi_open(txn, "processed_txidx", MDB_CREATE, &dbi_processed_txidx); + CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to open LMDB dbi: " + std::string(mdb_strerror(dbr))); + + dbr = mdb_dbi_open(txn, "spent", MDB_CREATE | MDB_INTEGERKEY | MDB_DUPSORT | MDB_DUPFIXED, &dbi_spent); + CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to open LMDB dbi: " + std::string(mdb_strerror(dbr))); + mdb_set_dupsort(txn, dbi_spent, compare_double64); + + dbr = mdb_dbi_open(txn, "ring_instances", MDB_CREATE, &dbi_ring_instances); + CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to open LMDB dbi: " + std::string(mdb_strerror(dbr))); + + dbr = mdb_dbi_open(txn, "newly_spent", MDB_CREATE | MDB_INTEGERKEY | MDB_DUPSORT | MDB_DUPFIXED, &dbi_newly_spent); + CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to open LMDB dbi: " + std::string(mdb_strerror(dbr))); + mdb_set_dupsort(txn, dbi_newly_spent, compare_double64); + + dbr = mdb_txn_commit(txn); + CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to commit txn creating/opening database: " + std::string(mdb_strerror(dbr))); + tx_active = false; +} + +static void close() +{ + if (env) + { + mdb_dbi_close(env, dbi_relative_rings); + mdb_dbi_close(env, dbi_outputs); + mdb_dbi_close(env, dbi_processed_txidx); + mdb_dbi_close(env, dbi_spent); + mdb_dbi_close(env, dbi_ring_instances); + mdb_dbi_close(env, dbi_newly_spent); + mdb_env_close(env); + env = NULL; + } +} + +static std::string compress_ring(const std::vector &ring) +{ + std::string s; + for (uint64_t out: ring) + s += tools::get_varint_data(out); + return s; +} + +static std::vector decompress_ring(const std::string &s) +{ + std::vector ring; + int read = 0; + for (std::string::const_iterator i = s.begin(); i != s.cend(); std::advance(i, read)) + { + uint64_t out; + std::string tmp(i, s.cend()); + read = tools::read_varint(tmp.begin(), tmp.end(), out); + CHECK_AND_ASSERT_THROW_MES(read > 0 && read <= 256, "Internal error decompressing ring"); + ring.push_back(out); + } + return ring; +} + static bool for_all_transactions(const std::string &filename, uint64_t &start_idx, const std::function &f) { MDB_env *env; @@ -212,6 +402,234 @@ static std::vector canonicalize(const std::vector &v) return c; } +static uint64_t get_num_spent_outputs(bool newly) +{ + MDB_txn *txn; + bool tx_active = false; + + int dbr = mdb_txn_begin(env, NULL, MDB_RDONLY, &txn); + CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to create LMDB transaction: " + std::string(mdb_strerror(dbr))); + epee::misc_utils::auto_scope_leave_caller txn_dtor = epee::misc_utils::create_scope_leave_handler([&](){if (tx_active) mdb_txn_abort(txn);}); + tx_active = true; + + MDB_cursor *cur; + dbr = mdb_cursor_open(txn, newly ? dbi_newly_spent : dbi_spent, &cur); + CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to open cursor for spent outputs: " + std::string(mdb_strerror(dbr))); + MDB_val k, v; + mdb_size_t count = 0; + dbr = mdb_cursor_get(cur, &k, &v, MDB_FIRST); + if (dbr != MDB_NOTFOUND) + { + CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to get first spent output: " + std::string(mdb_strerror(dbr))); + dbr = mdb_cursor_count(cur, &count); + CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to count entries: " + std::string(mdb_strerror(dbr))); + } + + mdb_cursor_close(cur); + dbr = mdb_txn_commit(txn); + CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to commit txn: " + std::string(mdb_strerror(dbr))); + tx_active = false; + + return count; +} + +static void add_spent_output(MDB_txn *txn, const output_data &od, bool newly) +{ + MDB_cursor *cur; + int dbr = mdb_cursor_open(txn, newly ? dbi_newly_spent : dbi_spent, &cur); + CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to open cursor for spent outputs: " + std::string(mdb_strerror(dbr))); + MDB_val v = {sizeof(od), (void*)&od}; + dbr = mdb_cursor_put(cur, (MDB_val *)&zerokval, &v, MDB_NODUPDATA); + CHECK_AND_ASSERT_THROW_MES(!dbr || dbr == MDB_KEYEXIST, "Failed to add spent output: " + std::string(mdb_strerror(dbr))); + mdb_cursor_close(cur); +} + +static bool is_output_spent(MDB_txn *txn, const output_data &od, bool newly) +{ + MDB_cursor *cur; + int dbr = mdb_cursor_open(txn, newly ? dbi_newly_spent : dbi_spent, &cur); + CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to open cursor for spent outputs: " + std::string(mdb_strerror(dbr))); + MDB_val v = {sizeof(od), (void*)&od}; + dbr = mdb_cursor_get(cur, (MDB_val *)&zerokval, &v, MDB_GET_BOTH); + CHECK_AND_ASSERT_THROW_MES(!dbr || dbr == MDB_NOTFOUND, "Failed to get spent output: " + std::string(mdb_strerror(dbr))); + bool spent = dbr == 0; + mdb_cursor_close(cur); + return spent; +} + +static std::vector get_spent_outputs(MDB_txn *txn, bool newly) +{ + MDB_cursor *cur; + int dbr = mdb_cursor_open(txn, newly ? dbi_newly_spent : dbi_spent, &cur); + CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to open cursor for spent outputs: " + std::string(mdb_strerror(dbr))); + MDB_val k, v; + uint64_t count = 0; + dbr = mdb_cursor_get(cur, &k, &v, MDB_FIRST); + if (dbr != MDB_NOTFOUND) + { + CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to get first spent output: " + std::string(mdb_strerror(dbr))); + dbr = mdb_cursor_count(cur, &count); + CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to count entries: " + std::string(mdb_strerror(dbr))); + } + std::vector outs; + outs.reserve(count); + while (1) + { + const output_data *od = (const output_data*)v.mv_data; + outs.push_back(*od); + dbr = mdb_cursor_get(cur, &k, &v, MDB_NEXT); + if (dbr == MDB_NOTFOUND) + break; + CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to get next spent output: " + std::string(mdb_strerror(dbr))); + } + mdb_cursor_close(cur); + return outs; +} + +static void clear_spent_outputs(MDB_txn *txn, bool newly) +{ + int dbr = mdb_drop(txn, newly ? dbi_newly_spent : dbi_spent, 0); + CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to clear spent outputs: " + std::string(mdb_strerror(dbr))); +} + +static uint64_t get_processed_txidx(const std::string &name) +{ + MDB_txn *txn; + bool tx_active = false; + + int dbr = mdb_txn_begin(env, NULL, MDB_RDONLY, &txn); + CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to create LMDB transaction: " + std::string(mdb_strerror(dbr))); + epee::misc_utils::auto_scope_leave_caller txn_dtor = epee::misc_utils::create_scope_leave_handler([&](){if (tx_active) mdb_txn_abort(txn);}); + tx_active = true; + + uint64_t height = 0; + MDB_val k, v; + k.mv_data = (void*)name.c_str(); + k.mv_size = name.size(); + dbr = mdb_get(txn, dbi_processed_txidx, &k, &v); + if (dbr != MDB_NOTFOUND) + { + CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to get processed height: " + std::string(mdb_strerror(dbr))); + height = *(const uint64_t*)v.mv_data; + } + + dbr = mdb_txn_commit(txn); + CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to commit txn: " + std::string(mdb_strerror(dbr))); + tx_active = false; + + return height; +} + +static void set_processed_txidx(MDB_txn *txn, const std::string &name, uint64_t height) +{ + MDB_val k, v; + k.mv_data = (void*)name.c_str(); + k.mv_size = name.size(); + v.mv_data = (void*)&height; + v.mv_size = sizeof(height); + int dbr = mdb_put(txn, dbi_processed_txidx, &k, &v, 0); + CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to set processed height: " + std::string(mdb_strerror(dbr))); +} + +static bool get_relative_ring(MDB_txn *txn, const crypto::key_image &ki, std::vector &ring) +{ + const std::string sring = compress_ring(ring); + MDB_val k, v; + k.mv_data = (void*)&ki; + k.mv_size = sizeof(ki); + int dbr = mdb_get(txn, dbi_relative_rings, &k, &v); + if (dbr == MDB_NOTFOUND) + return false; + CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to get relative ring: " + std::string(mdb_strerror(dbr))); + ring = decompress_ring(std::string((const char*)v.mv_data, v.mv_size)); + return true; +} + +static void set_relative_ring(MDB_txn *txn, const crypto::key_image &ki, const std::vector &ring) +{ + const std::string sring = compress_ring(ring); + MDB_val k, v; + k.mv_data = (void*)&ki; + k.mv_size = sizeof(ki); + v.mv_data = (void*)sring.c_str(); + v.mv_size = sring.size(); + int dbr = mdb_put(txn, dbi_relative_rings, &k, &v, 0); + CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to set relative ring: " + std::string(mdb_strerror(dbr))); +} + +static std::string keep_under_511(const std::string &s) +{ + if (s.size() <= 511) + return s; + crypto::hash hash; + crypto::cn_fast_hash(s.data(), s.size(), hash); + return std::string((const char*)&hash, 32); +} + +static uint64_t get_ring_instances(MDB_txn *txn, const std::vector &ring) +{ + const std::string sring = keep_under_511(compress_ring(ring)); + MDB_val k, v; + k.mv_data = (void*)sring.data(); + k.mv_size = sring.size(); + int dbr = mdb_get(txn, dbi_ring_instances, &k, &v); + if (dbr == MDB_NOTFOUND) + return 0; + CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to get ring instances: " + std::string(mdb_strerror(dbr))); + return *(const uint64_t*)v.mv_data; +} + +static void set_ring_instances(MDB_txn *txn, const std::vector &ring, uint64_t count) +{ + const std::string sring = keep_under_511(compress_ring(ring)); + MDB_val k, v; + k.mv_data = (void*)sring.data(); + k.mv_size = sring.size(); + v.mv_data = &count; + v.mv_size = sizeof(count); + int dbr = mdb_put(txn, dbi_ring_instances, &k, &v, 0); + CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to get ring instances: " + std::string(mdb_strerror(dbr))); +} + +static std::vector get_key_images(MDB_txn *txn, const output_data &od) +{ + MDB_val k, v; + k.mv_data = (void*)&od; + k.mv_size = sizeof(od); + int dbr = mdb_get(txn, dbi_outputs, &k, &v); + CHECK_AND_ASSERT_THROW_MES(!dbr || dbr == MDB_NOTFOUND, "Failed to get output: " + std::string(mdb_strerror(dbr))); + if (dbr == MDB_NOTFOUND) + return {}; + CHECK_AND_ASSERT_THROW_MES(v.mv_size % 32 == 0, "Unexpected record size"); + std::vector key_images; + key_images.reserve(v.mv_size / 32); + const crypto::key_image *ki = (const crypto::key_image*)v.mv_data; + for (size_t n = 0; n < v.mv_size / 32; ++n) + key_images.push_back(*ki++); + return key_images; +} + +static void add_key_image(MDB_txn *txn, const output_data &od, const crypto::key_image &ki) +{ + MDB_val k, v; + k.mv_data = (void*)&od; + k.mv_size = sizeof(od); + int dbr = mdb_get(txn, dbi_outputs, &k, &v); + CHECK_AND_ASSERT_THROW_MES(!dbr || dbr == MDB_NOTFOUND, "Failed to get output"); + std::string data; + if (!dbr) + { + CHECK_AND_ASSERT_THROW_MES(v.mv_size % 32 == 0, "Unexpected record size"); + data = std::string((const char*)v.mv_data, v.mv_size); + } + data += std::string((const char*)&ki, sizeof(ki)); + + v.mv_data = (void*)data.data(); + v.mv_size = data.size(); + dbr = mdb_put(txn, dbi_outputs, &k, &v, 0); + CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to set outputs: " + std::string(mdb_strerror(dbr))); +} + int main(int argc, char* argv[]) { TRY_ENTRY(); @@ -249,6 +667,11 @@ int main(int argc, char* argv[]) }; const command_line::arg_descriptor arg_rct_only = {"rct-only", "Only work on ringCT outputs", false}; const command_line::arg_descriptor > arg_inputs = {"inputs", "Path to Monero DB, and path to any fork DBs"}; + const command_line::arg_descriptor arg_db_sync_mode = { + "db-sync-mode" + , "Specify sync option, using format [safe|fast|fastest]:[nrecords_per_sync]." + , "fast:1000" + }; command_line::add_arg(desc_cmd_sett, arg_blackball_db_dir); command_line::add_arg(desc_cmd_sett, cryptonote::arg_testnet_on); @@ -256,6 +679,7 @@ int main(int argc, char* argv[]) command_line::add_arg(desc_cmd_sett, arg_log_level); command_line::add_arg(desc_cmd_sett, arg_database); command_line::add_arg(desc_cmd_sett, arg_rct_only); + command_line::add_arg(desc_cmd_sett, arg_db_sync_mode); command_line::add_arg(desc_cmd_sett, arg_inputs); command_line::add_arg(desc_cmd_only, command_line::arg_help); @@ -304,6 +728,13 @@ int main(int argc, char* argv[]) return 1; } + std::string db_sync_mode = command_line::get_arg(vm, arg_db_sync_mode); + if (!parse_db_sync_mode(db_sync_mode)) + { + MERROR("Invalid db sync mode: " << db_sync_mode); + return 1; + } + // If we wanted to use the memory pool, we would set up a fake_core. // Use Blockchain instead of lower-level BlockchainDB for two reasons: @@ -337,7 +768,7 @@ int main(int argc, char* argv[]) } LOG_PRINT_L0("database: " << db_type); - std::string filename = inputs[n]; + std::string filename = (boost::filesystem::path(inputs[n]) / db->get_db_name()).string(); while (boost::ends_with(filename, "/") || boost::ends_with(filename, "\\")) filename.pop_back(); LOG_PRINT_L0("Loading blockchain from folder " << filename << " ..."); @@ -357,49 +788,14 @@ int main(int argc, char* argv[]) LOG_PRINT_L0("Source blockchain storage initialized OK"); } - boost::filesystem::path direc(output_file_path.string()); - if (boost::filesystem::exists(direc)) - { - if (!boost::filesystem::is_directory(direc)) - { - MERROR("LMDB needs a directory path, but a file was passed: " << output_file_path.string()); - return 1; - } - } - else - { - if (!boost::filesystem::create_directories(direc)) - { - MERROR("Failed to create directory: " << output_file_path.string()); - return 1; - } - } + const std::string cache_dir = (output_file_path / "blackball-cache").string(); + init(cache_dir); LOG_PRINT_L0("Scanning for blackballable outputs..."); size_t done = 0; - blackball_state_t state; - std::unordered_set newly_spent; - const std::string state_file_path = (boost::filesystem::path(output_file_path) / "blackball-state.bin").string(); - - LOG_PRINT_L0("Loading state data from " << state_file_path); - std::ifstream state_data_in; - state_data_in.open(state_file_path, std::ios_base::binary | std::ios_base::in); - if (!state_data_in.fail()) - { - try - { - boost::archive::portable_binary_iarchive a(state_data_in); - a >> state; - } - catch (const std::exception &e) - { - MERROR("Failed to load state data from " << state_file_path << ", restarting from scratch"); - state = blackball_state_t(); - } - state_data_in.close(); - } - uint64_t start_blackballed_outputs = state.spent.size(); + + const uint64_t start_blackballed_outputs = get_num_spent_outputs(false); cryptonote::block b = core_storage[0]->get_db().get_block_from_height(0); tools::ringdb ringdb(output_file_path.string(), epee::string_tools::pod_to_hex(get_block_hash(b))); @@ -409,17 +805,24 @@ int main(int argc, char* argv[]) stop_requested = true; }); + int dbr = resize_env(cache_dir.c_str()); + CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to resize LMDB database: " + std::string(mdb_strerror(dbr))); + for (size_t n = 0; n < inputs.size(); ++n) { const std::string canonical = boost::filesystem::canonical(inputs[n]).string(); - uint64_t start_idx = 0; - auto it = state.processed_heights.find(canonical); - if (it != state.processed_heights.end()) - start_idx = it->second; + uint64_t start_idx = get_processed_txidx(canonical); LOG_PRINT_L0("Reading blockchain from " << inputs[n] << " from " << start_idx); + MDB_txn *txn; + int dbr = mdb_txn_begin(env, NULL, 0, &txn); + CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to create LMDB transaction: " + std::string(mdb_strerror(dbr))); + size_t records = 0; + const uint64_t n_txes = core_storage[n]->get_db().get_tx_count(); + const std::string filename = (boost::filesystem::path(inputs[n]) / core_storage[n]->get_db().get_db_name()).string(); std::vector blackballs; - for_all_transactions(inputs[n], start_idx, [&](const cryptonote::transaction_prefix &tx)->bool + for_all_transactions(filename, start_idx, [&](const cryptonote::transaction_prefix &tx)->bool { + std::cout << "\r" << start_idx << "/" << n_txes << " \r" << std::flush; for (const auto &in: tx.vin) { if (in.type() != typeid(txin_to_key)) @@ -431,37 +834,44 @@ int main(int argc, char* argv[]) const std::vector absolute = cryptonote::relative_output_offsets_to_absolute(txin.key_offsets); if (n == 0) for (uint64_t out: absolute) - state.outputs[output_data(txin.amount, out)].insert(txin.k_image); + add_key_image(txn, output_data(txin.amount, out), txin.k_image); + std::vector relative_ring; std::vector new_ring = canonicalize(txin.key_offsets); const uint32_t ring_size = txin.key_offsets.size(); - state.ring_instances[new_ring] += 1; + uint64_t instances = get_ring_instances(txn, new_ring); + ++instances; + set_ring_instances(txn, new_ring, instances); if (ring_size == 1) { const crypto::public_key pkey = core_storage[n]->get_output_key(txin.amount, absolute[0]); MINFO("Blackballing output " << pkey << ", due to being used in a 1-ring"); + std::cout << "\r" << start_idx << "/" << n_txes << " \r" << std::flush; blackballs.push_back(pkey); - newly_spent.insert(output_data(txin.amount, absolute[0])); + add_spent_output(txn, output_data(txin.amount, absolute[0]), true); } - else if (state.ring_instances[new_ring] == new_ring.size()) + else if (instances == new_ring.size()) { for (size_t o = 0; o < new_ring.size(); ++o) { const crypto::public_key pkey = core_storage[n]->get_output_key(txin.amount, absolute[o]); MINFO("Blackballing output " << pkey << ", due to being used in " << new_ring.size() << " identical " << new_ring.size() << "-rings"); + std::cout << "\r" << start_idx << "/" << n_txes << " \r" << std::flush; blackballs.push_back(pkey); - newly_spent.insert(output_data(txin.amount, absolute[o])); + add_spent_output(txn, output_data(txin.amount, absolute[o]), true); } } - else if (state.relative_rings.find(txin.k_image) != state.relative_rings.end()) + else if (get_relative_ring(txn, txin.k_image, relative_ring)) { MINFO("Key image " << txin.k_image << " already seen: rings " << - boost::join(state.relative_rings[txin.k_image] | boost::adaptors::transformed([](uint64_t out){return std::to_string(out);}), " ") << + boost::join(relative_ring | boost::adaptors::transformed([](uint64_t out){return std::to_string(out);}), " ") << ", " << boost::join(txin.key_offsets | boost::adaptors::transformed([](uint64_t out){return std::to_string(out);}), " ")); - if (state.relative_rings[txin.k_image] != txin.key_offsets) + std::cout << "\r" << start_idx << "/" << n_txes << " \r" << std::flush; + if (relative_ring != txin.key_offsets) { MINFO("Rings are different"); - const std::vector r0 = cryptonote::relative_output_offsets_to_absolute(state.relative_rings[txin.k_image]); + std::cout << "\r" << start_idx << "/" << n_txes << " \r" << std::flush; + const std::vector r0 = cryptonote::relative_output_offsets_to_absolute(relative_ring); const std::vector r1 = cryptonote::relative_output_offsets_to_absolute(txin.key_offsets); std::vector common; for (uint64_t out: r0) @@ -472,17 +882,20 @@ int main(int argc, char* argv[]) if (common.empty()) { MERROR("Rings for the same key image are disjoint"); + std::cout << "\r" << start_idx << "/" << n_txes << " \r" << std::flush; } else if (common.size() == 1) { const crypto::public_key pkey = core_storage[n]->get_output_key(txin.amount, common[0]); MINFO("Blackballing output " << pkey << ", due to being used in rings with a single common element"); + std::cout << "\r" << start_idx << "/" << n_txes << " \r" << std::flush; blackballs.push_back(pkey); - newly_spent.insert(output_data(txin.amount, common[0])); + add_spent_output(txn, output_data(txin.amount, common[0]), true); } else { MINFO("The intersection has more than one element, it's still ok"); + std::cout << "\r" << start_idx << "/" << n_txes << " \r" << std::flush; for (const auto &out: r0) if (std::find(common.begin(), common.end(), out) != common.end()) new_ring.push_back(out); @@ -490,13 +903,27 @@ int main(int argc, char* argv[]) } } } - state.relative_rings[txin.k_image] = new_ring; - if (!blackballs.empty()) - { - ringdb.blackball(blackballs); - blackballs.clear(); - } + set_relative_ring(txn, txin.k_image, new_ring); + } + if (!blackballs.empty()) + { + ringdb.blackball(blackballs); + blackballs.clear(); + } + set_processed_txidx(txn, canonical, start_idx); + + ++records; + if (records >= records_per_sync) + { + dbr = mdb_txn_commit(txn); + CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to commit txn creating/opening database: " + std::string(mdb_strerror(dbr))); + int dbr = resize_env(cache_dir.c_str()); + CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to resize LMDB database: " + std::string(mdb_strerror(dbr))); + dbr = mdb_txn_begin(env, NULL, 0, &txn); + CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to create LMDB transaction: " + std::string(mdb_strerror(dbr))); + records = 0; } + if (stop_requested) { MINFO("Stopping scan, secondary passes will still happen..."); @@ -504,33 +931,45 @@ int main(int argc, char* argv[]) } return true; }); - LOG_PRINT_L0("blockchain from " << inputs[n] << " processed still height " << start_idx); - state.processed_heights[canonical] = start_idx; + dbr = mdb_txn_commit(txn); + CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to commit txn creating/opening database: " + std::string(mdb_strerror(dbr))); + LOG_PRINT_L0("blockchain from " << inputs[n] << " processed till tx idx " << start_idx); if (stop_requested) break; } - while (!newly_spent.empty()) + while (get_num_spent_outputs(true) != 0) { - LOG_PRINT_L0("Secondary pass due to " << newly_spent.size() << " newly found spent outputs"); - std::unordered_set work_spent = std::move(newly_spent); - newly_spent.clear(); + LOG_PRINT_L0("Secondary pass due to " << get_num_spent_outputs(true) << " newly found spent outputs"); + + int dbr = resize_env(cache_dir.c_str()); + CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to resize LMDB database: " + std::string(mdb_strerror(dbr))); + + MDB_txn *txn; + dbr = mdb_txn_begin(env, NULL, 0, &txn); + CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to create LMDB transaction: " + std::string(mdb_strerror(dbr))); + + std::vector work_spent = get_spent_outputs(txn, true); + clear_spent_outputs(txn, true); - for (const auto &e: work_spent) - state.spent.insert(e); + for (const auto &od: work_spent) + add_spent_output(txn, od, false); std::vector blackballs; for (const output_data &od: work_spent) { - for (const crypto::key_image &ki: state.outputs[od]) + std::vector key_images = get_key_images(txn, od); + for (const crypto::key_image &ki: key_images) { - std::vector absolute = cryptonote::relative_output_offsets_to_absolute(state.relative_rings[ki]); + std::vector relative_ring; + CHECK_AND_ASSERT_THROW_MES(get_relative_ring(txn, ki, relative_ring), "Relative ring not found"); + std::vector absolute = cryptonote::relative_output_offsets_to_absolute(relative_ring); size_t known = 0; uint64_t last_unknown = 0; for (uint64_t out: absolute) { output_data new_od(od.amount, out); - if (state.spent.find(new_od) != state.spent.end()) + if (is_output_spent(txn, new_od, false)) ++known; else last_unknown = out; @@ -541,39 +980,24 @@ int main(int argc, char* argv[]) MINFO("Blackballing output " << pkey << ", due to being used in a " << absolute.size() << "-ring where all other outputs are known to be spent"); blackballs.push_back(pkey); - newly_spent.insert(output_data(od.amount, last_unknown)); + add_spent_output(txn, output_data(od.amount, last_unknown), true); } } } - } - - if (!blackballs.empty()) - { - ringdb.blackball(blackballs); - blackballs.clear(); - } - - LOG_PRINT_L0("Saving state data to " << state_file_path); - std::ofstream state_data_out; - state_data_out.open(state_file_path, std::ios_base::binary | std::ios_base::out | std::ios::trunc); - if (!state_data_out.fail()) - { - try - { - boost::archive::portable_binary_oarchive a(state_data_out); - a << state; - } - catch (const std::exception &e) + if (!blackballs.empty()) { - MERROR("Failed to save state data to " << state_file_path); + ringdb.blackball(blackballs); + blackballs.clear(); } - state_data_out.close(); + dbr = mdb_txn_commit(txn); + CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to commit txn creating/opening database: " + std::string(mdb_strerror(dbr))); } - uint64_t diff = state.spent.size() - start_blackballed_outputs; - LOG_PRINT_L0(std::to_string(diff) << " new outputs blackballed, " << state.spent.size() << " total outputs blackballed"); + uint64_t diff = get_num_spent_outputs(false) - start_blackballed_outputs; + LOG_PRINT_L0(std::to_string(diff) << " new outputs blackballed, " << get_num_spent_outputs(false) << " total outputs blackballed"); LOG_PRINT_L0("Blockchain blackball data exported OK"); + close(); return 0; - CATCH_ENTRY("Export error", 1); + CATCH_ENTRY("Error", 1); } From 846190fd18a6b16ba2406db56b533b5bb87b1f3e Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 12 Aug 2018 05:54:38 +0000 Subject: [PATCH 0306/1404] blockchain_blackball: support pre-v2 databases --- .../blockchain_blackball.cpp | 202 +++++++++++------- 1 file changed, 123 insertions(+), 79 deletions(-) diff --git a/src/blockchain_utilities/blockchain_blackball.cpp b/src/blockchain_utilities/blockchain_blackball.cpp index b1369f4c0e6..f034acb2436 100644 --- a/src/blockchain_utilities/blockchain_blackball.cpp +++ b/src/blockchain_utilities/blockchain_blackball.cpp @@ -161,6 +161,13 @@ static int compare_hash32(const MDB_val *a, const MDB_val *b) return 0; } +int compare_uint64(const MDB_val *a, const MDB_val *b) +{ + const uint64_t va = *(const uint64_t *)a->mv_data; + const uint64_t vb = *(const uint64_t *)b->mv_data; + return (va < vb) ? -1 : va > vb; +} + static int compare_double64(const MDB_val *a, const MDB_val *b) { const uint64_t va = *(const uint64_t*) a->mv_data; @@ -308,7 +315,7 @@ static std::vector decompress_ring(const std::string &s) return ring; } -static bool for_all_transactions(const std::string &filename, uint64_t &start_idx, const std::function &f) +static bool for_all_transactions(const std::string &filename, uint64_t &start_idx, uint64_t &n_txes, const std::function &f) { MDB_env *env; MDB_dbi dbi; @@ -316,6 +323,8 @@ static bool for_all_transactions(const std::string &filename, uint64_t &start_id MDB_cursor *cur; int dbr; bool tx_active = false; + MDB_val k; + MDB_val v; dbr = mdb_env_create(&env); if (dbr) throw std::runtime_error("Failed to create LDMB environment: " + std::string(mdb_strerror(dbr))); @@ -337,9 +346,11 @@ static bool for_all_transactions(const std::string &filename, uint64_t &start_id if (dbr) throw std::runtime_error("Failed to open LMDB dbi: " + std::string(mdb_strerror(dbr))); dbr = mdb_cursor_open(txn, dbi, &cur); if (dbr) throw std::runtime_error("Failed to create LMDB cursor: " + std::string(mdb_strerror(dbr))); + MDB_stat stat; + dbr = mdb_stat(txn, dbi, &stat); + if (dbr) throw std::runtime_error("Failed to query m_block_info: " + std::string(mdb_strerror(dbr))); + n_txes = stat.ms_entries; - MDB_val k; - MDB_val v; bool fret = true; k.mv_size = sizeof(uint64_t); @@ -630,6 +641,90 @@ static void add_key_image(MDB_txn *txn, const output_data &od, const crypto::key CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to set outputs: " + std::string(mdb_strerror(dbr))); } +static void open_db(const std::string &filename, MDB_env **env, MDB_txn **txn, MDB_cursor **cur, MDB_dbi *dbi) +{ + tools::create_directories_if_necessary(filename); + + int flags = MDB_RDONLY; + if (db_flags & DBF_FAST) + flags |= MDB_NOSYNC; + if (db_flags & DBF_FASTEST) + flags |= MDB_NOSYNC | MDB_WRITEMAP | MDB_MAPASYNC; + + int dbr = mdb_env_create(env); + CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to create LDMB environment: " + std::string(mdb_strerror(dbr))); + dbr = mdb_env_set_maxdbs(*env, 1); + CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to set max env dbs: " + std::string(mdb_strerror(dbr))); + const std::string actual_filename = filename; + MINFO("Opening monero blockchain at " << actual_filename); + dbr = mdb_env_open(*env, actual_filename.c_str(), flags, 0664); + CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to open rings database file '" + + actual_filename + "': " + std::string(mdb_strerror(dbr))); + + dbr = mdb_txn_begin(*env, NULL, MDB_RDONLY, txn); + CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to create LMDB transaction: " + std::string(mdb_strerror(dbr))); + + dbr = mdb_dbi_open(*txn, "output_amounts", MDB_CREATE | MDB_INTEGERKEY | MDB_DUPSORT | MDB_DUPFIXED, dbi); + CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to open LMDB dbi: " + std::string(mdb_strerror(dbr))); + mdb_set_dupsort(*txn, *dbi, compare_uint64); + + dbr = mdb_cursor_open(*txn, *dbi, cur); + CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to create LMDB cursor: " + std::string(mdb_strerror(dbr))); +} + +static void close_db(MDB_env *env, MDB_txn *txn, MDB_cursor *cur, MDB_dbi dbi) +{ + mdb_txn_abort(txn); + mdb_cursor_close(cur); + mdb_dbi_close(env, dbi); + mdb_env_close(env); +} + +static crypto::public_key get_output_key(MDB_cursor *cur, uint64_t amount, uint64_t offset) +{ + MDB_val k = { sizeof(amount), (void*)&amount }, v = { sizeof(offset), (void*)&offset }; + int dbr = mdb_cursor_get(cur, &k, &v, MDB_GET_BOTH); + if (dbr) throw std::runtime_error("Output key not found: " + std::string(mdb_strerror(dbr))); + return *(const crypto::public_key*)(((const char*)v.mv_data) + sizeof(uint64_t) * 2); +} + +static crypto::hash get_genesis_block_hash(const std::string &filename) +{ + MDB_env *env; + MDB_dbi dbi; + MDB_txn *txn; + int dbr; + bool tx_active = false; + + dbr = mdb_env_create(&env); + if (dbr) throw std::runtime_error("Failed to create LDMB environment: " + std::string(mdb_strerror(dbr))); + dbr = mdb_env_set_maxdbs(env, 1); + if (dbr) throw std::runtime_error("Failed to set max env dbs: " + std::string(mdb_strerror(dbr))); + const std::string actual_filename = filename; + dbr = mdb_env_open(env, actual_filename.c_str(), 0, 0664); + if (dbr) throw std::runtime_error("Failed to open rings database file '" + + actual_filename + "': " + std::string(mdb_strerror(dbr))); + + dbr = mdb_txn_begin(env, NULL, MDB_RDONLY, &txn); + if (dbr) throw std::runtime_error("Failed to create LMDB transaction: " + std::string(mdb_strerror(dbr))); + epee::misc_utils::auto_scope_leave_caller txn_dtor = epee::misc_utils::create_scope_leave_handler([&](){if (tx_active) mdb_txn_abort(txn);}); + tx_active = true; + + dbr = mdb_dbi_open(txn, "block_info", MDB_INTEGERKEY | MDB_DUPSORT | MDB_DUPFIXED, &dbi); + mdb_set_dupsort(txn, dbi, compare_uint64); + if (dbr) throw std::runtime_error("Failed to open LMDB dbi: " + std::string(mdb_strerror(dbr))); + uint64_t zero = 0; + MDB_val k = { sizeof(uint64_t), (void*)&zero}, v; + dbr = mdb_get(txn, dbi, &k, &v); + if (dbr) throw std::runtime_error("Failed to retrieve genesis block: " + std::string(mdb_strerror(dbr))); + crypto::hash genesis_block_hash = *(const crypto::hash*)(((const uint64_t*)v.mv_data) + 5); + mdb_dbi_close(env, dbi); + mdb_txn_abort(txn); + mdb_env_close(env); + tx_active = false; + return genesis_block_hash; +} + int main(int argc, char* argv[]) { TRY_ENTRY(); @@ -649,17 +744,9 @@ int main(int argc, char* argv[]) po::options_description desc_cmd_only("Command line options"); po::options_description desc_cmd_sett("Command line options and settings options"); - const command_line::arg_descriptor arg_blackball_db_dir = { + const command_line::arg_descriptor arg_blackball_db_dir = { "blackball-db-dir", "Specify blackball database directory", get_default_db_path(), - {{ &arg_testnet_on, &arg_stagenet_on }}, - [](std::array testnet_stagenet, bool defaulted, std::string val)->std::string { - if (testnet_stagenet[0]) - return (boost::filesystem::path(val) / "testnet").string(); - else if (testnet_stagenet[1]) - return (boost::filesystem::path(val) / "stagenet").string(); - return val; - } }; const command_line::arg_descriptor arg_log_level = {"log-level", "0-4 or categories", ""}; const command_line::arg_descriptor arg_database = { @@ -674,8 +761,6 @@ int main(int argc, char* argv[]) }; command_line::add_arg(desc_cmd_sett, arg_blackball_db_dir); - command_line::add_arg(desc_cmd_sett, cryptonote::arg_testnet_on); - command_line::add_arg(desc_cmd_sett, cryptonote::arg_stagenet_on); command_line::add_arg(desc_cmd_sett, arg_log_level); command_line::add_arg(desc_cmd_sett, arg_database); command_line::add_arg(desc_cmd_sett, arg_rct_only); @@ -715,9 +800,6 @@ int main(int argc, char* argv[]) LOG_PRINT_L0("Starting..."); - bool opt_testnet = command_line::get_arg(vm, cryptonote::arg_testnet_on); - bool opt_stagenet = command_line::get_arg(vm, cryptonote::arg_stagenet_on); - network_type net_type = opt_testnet ? TESTNET : opt_stagenet ? STAGENET : MAINNET; output_file_path = command_line::get_arg(vm, arg_blackball_db_dir); bool opt_rct_only = command_line::get_arg(vm, arg_rct_only); @@ -735,58 +817,12 @@ int main(int argc, char* argv[]) return 1; } - // If we wanted to use the memory pool, we would set up a fake_core. - - // Use Blockchain instead of lower-level BlockchainDB for two reasons: - // 1. Blockchain has the init() method for easy setup - // 2. exporter needs to use get_current_blockchain_height(), get_block_id_by_height(), get_block_by_hash() - // - // cannot match blockchain_storage setup above with just one line, - // e.g. - // Blockchain* core_storage = new Blockchain(NULL); - // because unlike blockchain_storage constructor, which takes a pointer to - // tx_memory_pool, Blockchain's constructor takes tx_memory_pool object. - LOG_PRINT_L0("Initializing source blockchain (BlockchainDB)"); const std::vector inputs = command_line::get_arg(vm, arg_inputs); if (inputs.empty()) { LOG_PRINT_L0("No inputs given"); return 1; } - std::vector> core_storage(inputs.size()); - Blockchain *blockchain = NULL; - tx_memory_pool m_mempool(*blockchain); - for (size_t n = 0; n < inputs.size(); ++n) - { - core_storage[n].reset(new Blockchain(m_mempool)); - - BlockchainDB* db = new_db(db_type); - if (db == NULL) - { - LOG_ERROR("Attempted to use non-existent database type: " << db_type); - throw std::runtime_error("Attempting to use non-existent database type"); - } - LOG_PRINT_L0("database: " << db_type); - - std::string filename = (boost::filesystem::path(inputs[n]) / db->get_db_name()).string(); - while (boost::ends_with(filename, "/") || boost::ends_with(filename, "\\")) - filename.pop_back(); - LOG_PRINT_L0("Loading blockchain from folder " << filename << " ..."); - - try - { - db->open(filename, DBF_RDONLY); - } - catch (const std::exception& e) - { - LOG_PRINT_L0("Error opening database: " << e.what()); - return 1; - } - r = core_storage[n]->init(db, net_type); - - CHECK_AND_ASSERT_MES(r, 1, "Failed to initialize source blockchain storage"); - LOG_PRINT_L0("Source blockchain storage initialized OK"); - } const std::string cache_dir = (output_file_path / "blackball-cache").string(); init(cache_dir); @@ -797,8 +833,7 @@ int main(int argc, char* argv[]) const uint64_t start_blackballed_outputs = get_num_spent_outputs(false); - cryptonote::block b = core_storage[0]->get_db().get_block_from_height(0); - tools::ringdb ringdb(output_file_path.string(), epee::string_tools::pod_to_hex(get_block_hash(b))); + tools::ringdb ringdb(output_file_path.string(), epee::string_tools::pod_to_hex(get_genesis_block_hash(inputs[0]))); bool stop_requested = false; tools::signal_handler::install([&stop_requested](int type) { @@ -808,6 +843,13 @@ int main(int argc, char* argv[]) int dbr = resize_env(cache_dir.c_str()); CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to resize LMDB database: " + std::string(mdb_strerror(dbr))); + // open first db + MDB_env *env0; + MDB_txn *txn0; + MDB_dbi dbi0; + MDB_cursor *cur0; + open_db(inputs[0], &env0, &txn0, &cur0, &dbi0); + for (size_t n = 0; n < inputs.size(); ++n) { const std::string canonical = boost::filesystem::canonical(inputs[n]).string(); @@ -817,10 +859,10 @@ int main(int argc, char* argv[]) int dbr = mdb_txn_begin(env, NULL, 0, &txn); CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to create LMDB transaction: " + std::string(mdb_strerror(dbr))); size_t records = 0; - const uint64_t n_txes = core_storage[n]->get_db().get_tx_count(); - const std::string filename = (boost::filesystem::path(inputs[n]) / core_storage[n]->get_db().get_db_name()).string(); + const std::string filename = inputs[n]; std::vector blackballs; - for_all_transactions(filename, start_idx, [&](const cryptonote::transaction_prefix &tx)->bool + uint64_t n_txes; + for_all_transactions(filename, start_idx, n_txes, [&](const cryptonote::transaction_prefix &tx)->bool { std::cout << "\r" << start_idx << "/" << n_txes << " \r" << std::flush; for (const auto &in: tx.vin) @@ -842,34 +884,34 @@ int main(int argc, char* argv[]) uint64_t instances = get_ring_instances(txn, new_ring); ++instances; set_ring_instances(txn, new_ring, instances); - if (ring_size == 1) + if (n == 0 && ring_size == 1) { - const crypto::public_key pkey = core_storage[n]->get_output_key(txin.amount, absolute[0]); + const crypto::public_key pkey = get_output_key(cur0, txin.amount, absolute[0]); MINFO("Blackballing output " << pkey << ", due to being used in a 1-ring"); std::cout << "\r" << start_idx << "/" << n_txes << " \r" << std::flush; blackballs.push_back(pkey); add_spent_output(txn, output_data(txin.amount, absolute[0]), true); } - else if (instances == new_ring.size()) + else if (n == 0 && instances == new_ring.size()) { for (size_t o = 0; o < new_ring.size(); ++o) { - const crypto::public_key pkey = core_storage[n]->get_output_key(txin.amount, absolute[o]); + const crypto::public_key pkey = get_output_key(cur0, txin.amount, absolute[o]); MINFO("Blackballing output " << pkey << ", due to being used in " << new_ring.size() << " identical " << new_ring.size() << "-rings"); std::cout << "\r" << start_idx << "/" << n_txes << " \r" << std::flush; blackballs.push_back(pkey); add_spent_output(txn, output_data(txin.amount, absolute[o]), true); } } - else if (get_relative_ring(txn, txin.k_image, relative_ring)) + else if (n > 0 && get_relative_ring(txn, txin.k_image, relative_ring)) { - MINFO("Key image " << txin.k_image << " already seen: rings " << + MDEBUG("Key image " << txin.k_image << " already seen: rings " << boost::join(relative_ring | boost::adaptors::transformed([](uint64_t out){return std::to_string(out);}), " ") << ", " << boost::join(txin.key_offsets | boost::adaptors::transformed([](uint64_t out){return std::to_string(out);}), " ")); std::cout << "\r" << start_idx << "/" << n_txes << " \r" << std::flush; if (relative_ring != txin.key_offsets) { - MINFO("Rings are different"); + MDEBUG("Rings are different"); std::cout << "\r" << start_idx << "/" << n_txes << " \r" << std::flush; const std::vector r0 = cryptonote::relative_output_offsets_to_absolute(relative_ring); const std::vector r1 = cryptonote::relative_output_offsets_to_absolute(txin.key_offsets); @@ -886,7 +928,7 @@ int main(int argc, char* argv[]) } else if (common.size() == 1) { - const crypto::public_key pkey = core_storage[n]->get_output_key(txin.amount, common[0]); + const crypto::public_key pkey = get_output_key(cur0, txin.amount, common[0]); MINFO("Blackballing output " << pkey << ", due to being used in rings with a single common element"); std::cout << "\r" << start_idx << "/" << n_txes << " \r" << std::flush; blackballs.push_back(pkey); @@ -894,7 +936,7 @@ int main(int argc, char* argv[]) } else { - MINFO("The intersection has more than one element, it's still ok"); + MDEBUG("The intersection has more than one element, it's still ok"); std::cout << "\r" << start_idx << "/" << n_txes << " \r" << std::flush; for (const auto &out: r0) if (std::find(common.begin(), common.end(), out) != common.end()) @@ -903,7 +945,8 @@ int main(int argc, char* argv[]) } } } - set_relative_ring(txn, txin.k_image, new_ring); + if (n == 0) + set_relative_ring(txn, txin.k_image, new_ring); } if (!blackballs.empty()) { @@ -976,7 +1019,7 @@ int main(int argc, char* argv[]) } if (known == absolute.size() - 1) { - const crypto::public_key pkey = core_storage[0]->get_output_key(od.amount, last_unknown); + const crypto::public_key pkey = get_output_key(cur0, od.amount, last_unknown); MINFO("Blackballing output " << pkey << ", due to being used in a " << absolute.size() << "-ring where all other outputs are known to be spent"); blackballs.push_back(pkey); @@ -996,6 +1039,7 @@ int main(int argc, char* argv[]) uint64_t diff = get_num_spent_outputs(false) - start_blackballed_outputs; LOG_PRINT_L0(std::to_string(diff) << " new outputs blackballed, " << get_num_spent_outputs(false) << " total outputs blackballed"); LOG_PRINT_L0("Blockchain blackball data exported OK"); + close_db(env0, txn0, cur0, dbi0); close(); return 0; From 4801d6b514777ba32ab012620a5786281cb57295 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 12 Aug 2018 09:47:03 +0000 Subject: [PATCH 0307/1404] blockchain_blackball: add stats --- .../blockchain_blackball.cpp | 96 ++++++++++++++++++- 1 file changed, 94 insertions(+), 2 deletions(-) diff --git a/src/blockchain_utilities/blockchain_blackball.cpp b/src/blockchain_utilities/blockchain_blackball.cpp index f034acb2436..7f80eecefa2 100644 --- a/src/blockchain_utilities/blockchain_blackball.cpp +++ b/src/blockchain_utilities/blockchain_blackball.cpp @@ -61,6 +61,7 @@ static MDB_dbi dbi_processed_txidx; static MDB_dbi dbi_spent; static MDB_dbi dbi_ring_instances; static MDB_dbi dbi_newly_spent; +static MDB_dbi dbi_stats; static MDB_env *env = NULL; struct output_data @@ -79,6 +80,7 @@ struct output_data // spent: 128 bits, zerokval // ring_instances: vector -> uint64_t // newly_spent: 128 bits, zerokval +// stats: string -> arbitrary // static bool parse_db_sync_mode(std::string db_sync_mode) @@ -238,7 +240,7 @@ static void init(std::string cache_filename) dbr = mdb_env_create(&env); CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to create LDMB environment: " + std::string(mdb_strerror(dbr))); - dbr = mdb_env_set_maxdbs(env, 6); + dbr = mdb_env_set_maxdbs(env, 7); CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to set max env dbs: " + std::string(mdb_strerror(dbr))); const std::string actual_filename = get_cache_filename(cache_filename); dbr = mdb_env_open(env, actual_filename.c_str(), flags, 0664); @@ -272,6 +274,9 @@ static void init(std::string cache_filename) CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to open LMDB dbi: " + std::string(mdb_strerror(dbr))); mdb_set_dupsort(txn, dbi_newly_spent, compare_double64); + dbr = mdb_dbi_open(txn, "stats", MDB_CREATE, &dbi_stats); + CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to open LMDB dbi: " + std::string(mdb_strerror(dbr))); + dbr = mdb_txn_commit(txn); CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to commit txn creating/opening database: " + std::string(mdb_strerror(dbr))); tx_active = false; @@ -287,6 +292,7 @@ static void close() mdb_dbi_close(env, dbi_spent); mdb_dbi_close(env, dbi_ring_instances); mdb_dbi_close(env, dbi_newly_spent); + mdb_dbi_close(env, dbi_stats); mdb_env_close(env); env = NULL; } @@ -641,6 +647,40 @@ static void add_key_image(MDB_txn *txn, const output_data &od, const crypto::key CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to set outputs: " + std::string(mdb_strerror(dbr))); } +static bool get_stat(MDB_txn *txn, const char *key, uint64_t &data) +{ + MDB_val k, v; + k.mv_data = (void*)key; + k.mv_size = strlen(key); + int dbr = mdb_get(txn, dbi_stats, &k, &v); + if (dbr == MDB_NOTFOUND) + return false; + CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to get stat record"); + CHECK_AND_ASSERT_THROW_MES(v.mv_size == sizeof(uint64_t), "Unexpected record size"); + data = *(const uint64_t*)v.mv_data; + return true; +} + +static void set_stat(MDB_txn *txn, const char *key, uint64_t data) +{ + MDB_val k, v; + k.mv_data = (void*)key; + k.mv_size = strlen(key); + v.mv_data = (void*)&data; + v.mv_size = sizeof(uint64_t); + int dbr = mdb_put(txn, dbi_stats, &k, &v, 0); + CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to set stat record"); +} + +static void inc_stat(MDB_txn *txn, const char *key) +{ + uint64_t data; + if (!get_stat(txn, key, data)) + data = 0; + ++data; + set_stat(txn, key, data); +} + static void open_db(const std::string &filename, MDB_env **env, MDB_txn **txn, MDB_cursor **cur, MDB_dbi *dbi) { tools::create_directories_if_necessary(filename); @@ -688,6 +728,30 @@ static crypto::public_key get_output_key(MDB_cursor *cur, uint64_t amount, uint6 return *(const crypto::public_key*)(((const char*)v.mv_data) + sizeof(uint64_t) * 2); } +static void get_num_outputs(MDB_txn *txn, MDB_cursor *cur, MDB_dbi dbi, uint64_t &pre_rct, uint64_t &rct) +{ + uint64_t amount = 0; + MDB_val k = { sizeof(amount), (void*)&amount }, v; + int dbr = mdb_cursor_get(cur, &k, &v, MDB_SET); + if (dbr == MDB_NOTFOUND) + { + rct = 0; + } + else + { + if (dbr) throw std::runtime_error("Record 0 not found: " + std::string(mdb_strerror(dbr))); + mdb_size_t count = 0; + dbr = mdb_cursor_count(cur, &count); + if (dbr) throw std::runtime_error("Failed to count records: " + std::string(mdb_strerror(dbr))); + rct = count; + } + MDB_stat s; + dbr = mdb_stat(txn, dbi, &s); + if (dbr) throw std::runtime_error("Failed to count records: " + std::string(mdb_strerror(dbr))); + if (s.ms_entries < rct) throw std::runtime_error("Inconsistent records: " + std::string(mdb_strerror(dbr))); + pre_rct = s.ms_entries - rct; +} + static crypto::hash get_genesis_block_hash(const std::string &filename) { MDB_env *env; @@ -891,6 +955,7 @@ int main(int argc, char* argv[]) std::cout << "\r" << start_idx << "/" << n_txes << " \r" << std::flush; blackballs.push_back(pkey); add_spent_output(txn, output_data(txin.amount, absolute[0]), true); + inc_stat(txn, txin.amount ? "pre-rct-ring-size-1" : "rct-ring-size-1"); } else if (n == 0 && instances == new_ring.size()) { @@ -901,6 +966,7 @@ int main(int argc, char* argv[]) std::cout << "\r" << start_idx << "/" << n_txes << " \r" << std::flush; blackballs.push_back(pkey); add_spent_output(txn, output_data(txin.amount, absolute[o]), true); + inc_stat(txn, txin.amount ? "pre-rct-duplicate-rings" : "rct-duplicate-rings"); } } else if (n > 0 && get_relative_ring(txn, txin.k_image, relative_ring)) @@ -933,6 +999,7 @@ int main(int argc, char* argv[]) std::cout << "\r" << start_idx << "/" << n_txes << " \r" << std::flush; blackballs.push_back(pkey); add_spent_output(txn, output_data(txin.amount, common[0]), true); + inc_stat(txn, txin.amount ? "pre-rct-key-image-attack" : "rct-key-image-attack"); } else { @@ -1017,13 +1084,14 @@ int main(int argc, char* argv[]) else last_unknown = out; } - if (known == absolute.size() - 1) + if (known == absolute.size() - 1 && !is_output_spent(txn, output_data(od.amount, last_unknown), false) && !is_output_spent(txn, output_data(od.amount, last_unknown), true)) { const crypto::public_key pkey = get_output_key(cur0, od.amount, last_unknown); MINFO("Blackballing output " << pkey << ", due to being used in a " << absolute.size() << "-ring where all other outputs are known to be spent"); blackballs.push_back(pkey); add_spent_output(txn, output_data(od.amount, last_unknown), true); + inc_stat(txn, od.amount ? "pre-rct-chain-reaction" : "rct-chain-reaction"); } } } @@ -1038,6 +1106,30 @@ int main(int argc, char* argv[]) uint64_t diff = get_num_spent_outputs(false) - start_blackballed_outputs; LOG_PRINT_L0(std::to_string(diff) << " new outputs blackballed, " << get_num_spent_outputs(false) << " total outputs blackballed"); + + MDB_txn *txn; + dbr = mdb_txn_begin(env, NULL, MDB_RDONLY, &txn); + CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to create LMDB transaction: " + std::string(mdb_strerror(dbr))); + uint64_t pre_rct = 0, rct = 0; + get_num_outputs(txn0, cur0, dbi0, pre_rct, rct); + MINFO("Total pre-rct outputs: " << pre_rct); + MINFO("Total rct outputs: " << rct); + static const struct { const char *key; uint64_t base; } stat_keys[] = { + { "pre-rct-ring-size-1", pre_rct }, { "rct-ring-size-1", rct }, + { "pre-rct-duplicate-rings", pre_rct }, { "rct-duplicate-rings", rct }, + { "pre-rct-key-image-attack", pre_rct }, { "rct-key-image-attack", rct }, + { "pre-rct-chain-reaction", pre_rct }, { "rct-chain-reaction", rct }, + }; + for (const auto &key: stat_keys) + { + uint64_t data; + if (!get_stat(txn, key.key, data)) + data = 0; + float percent = key.base ? 100.0f * data / key.base : 0.0f; + MINFO(key.key << ": " << data << " (" << percent << "%)"); + } + mdb_txn_abort(txn); + LOG_PRINT_L0("Blockchain blackball data exported OK"); close_db(env0, txn0, cur0, dbi0); close(); From 80e4fef3c6bc423bec147d936685a827a4300c31 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 12 Aug 2018 09:50:48 +0000 Subject: [PATCH 0308/1404] blockchain_blackball: set transaction looping txn to read only --- src/blockchain_utilities/blockchain_blackball.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/blockchain_utilities/blockchain_blackball.cpp b/src/blockchain_utilities/blockchain_blackball.cpp index 7f80eecefa2..833a19a0b7c 100644 --- a/src/blockchain_utilities/blockchain_blackball.cpp +++ b/src/blockchain_utilities/blockchain_blackball.cpp @@ -341,7 +341,7 @@ static bool for_all_transactions(const std::string &filename, uint64_t &start_id if (dbr) throw std::runtime_error("Failed to open rings database file '" + actual_filename + "': " + std::string(mdb_strerror(dbr))); - dbr = mdb_txn_begin(env, NULL, 0, &txn); + dbr = mdb_txn_begin(env, NULL, MDB_RDONLY, &txn); if (dbr) throw std::runtime_error("Failed to create LMDB transaction: " + std::string(mdb_strerror(dbr))); epee::misc_utils::auto_scope_leave_caller txn_dtor = epee::misc_utils::create_scope_leave_handler([&](){if (tx_active) mdb_txn_abort(txn);}); tx_active = true; From 2b2a681b017683153ed747f693b21f67049a2b27 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 12 Aug 2018 21:45:10 +0000 Subject: [PATCH 0309/1404] blockchain_blackball: avoid false positives for different amounts Identical offset based rings may not actually be identical rings since they represent different outputs --- .../blockchain_blackball.cpp | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/blockchain_utilities/blockchain_blackball.cpp b/src/blockchain_utilities/blockchain_blackball.cpp index 833a19a0b7c..42e04f19515 100644 --- a/src/blockchain_utilities/blockchain_blackball.cpp +++ b/src/blockchain_utilities/blockchain_blackball.cpp @@ -298,14 +298,18 @@ static void close() } } -static std::string compress_ring(const std::vector &ring) +static std::string compress_ring(const std::vector &ring, std::string s = "") { - std::string s; for (uint64_t out: ring) s += tools::get_varint_data(out); return s; } +static std::string compress_ring(uint64_t amount, const std::vector &ring) +{ + return compress_ring(ring, tools::get_varint_data(amount)); +} + static std::vector decompress_ring(const std::string &s) { std::vector ring; @@ -583,9 +587,9 @@ static std::string keep_under_511(const std::string &s) return std::string((const char*)&hash, 32); } -static uint64_t get_ring_instances(MDB_txn *txn, const std::vector &ring) +static uint64_t get_ring_instances(MDB_txn *txn, uint64_t amount, const std::vector &ring) { - const std::string sring = keep_under_511(compress_ring(ring)); + const std::string sring = keep_under_511(compress_ring(amount, ring)); MDB_val k, v; k.mv_data = (void*)sring.data(); k.mv_size = sring.size(); @@ -596,9 +600,9 @@ static uint64_t get_ring_instances(MDB_txn *txn, const std::vector &ri return *(const uint64_t*)v.mv_data; } -static void set_ring_instances(MDB_txn *txn, const std::vector &ring, uint64_t count) +static void set_ring_instances(MDB_txn *txn, uint64_t amount, const std::vector &ring, uint64_t count) { - const std::string sring = keep_under_511(compress_ring(ring)); + const std::string sring = keep_under_511(compress_ring(amount, ring)); MDB_val k, v; k.mv_data = (void*)sring.data(); k.mv_size = sring.size(); @@ -945,9 +949,9 @@ int main(int argc, char* argv[]) std::vector relative_ring; std::vector new_ring = canonicalize(txin.key_offsets); const uint32_t ring_size = txin.key_offsets.size(); - uint64_t instances = get_ring_instances(txn, new_ring); + uint64_t instances = get_ring_instances(txn, txin.amount, new_ring); ++instances; - set_ring_instances(txn, new_ring, instances); + set_ring_instances(txn, txin.amount, new_ring, instances); if (n == 0 && ring_size == 1) { const crypto::public_key pkey = get_output_key(cur0, txin.amount, absolute[0]); From d6d276c6045a2ad8659e646eb03e06bffd62765c Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 12 Aug 2018 21:46:02 +0000 Subject: [PATCH 0310/1404] blockchain_blackball: fix chain reaction phase in incremental mode It makes it a lot slower, unfortunately --- .../blockchain_blackball.cpp | 76 +++++++++---------- 1 file changed, 35 insertions(+), 41 deletions(-) diff --git a/src/blockchain_utilities/blockchain_blackball.cpp b/src/blockchain_utilities/blockchain_blackball.cpp index 42e04f19515..feafdc7402e 100644 --- a/src/blockchain_utilities/blockchain_blackball.cpp +++ b/src/blockchain_utilities/blockchain_blackball.cpp @@ -60,7 +60,6 @@ static MDB_dbi dbi_outputs; static MDB_dbi dbi_processed_txidx; static MDB_dbi dbi_spent; static MDB_dbi dbi_ring_instances; -static MDB_dbi dbi_newly_spent; static MDB_dbi dbi_stats; static MDB_env *env = NULL; @@ -79,7 +78,6 @@ struct output_data // processed_txidx: string -> uint64_t // spent: 128 bits, zerokval // ring_instances: vector -> uint64_t -// newly_spent: 128 bits, zerokval // stats: string -> arbitrary // @@ -240,7 +238,7 @@ static void init(std::string cache_filename) dbr = mdb_env_create(&env); CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to create LDMB environment: " + std::string(mdb_strerror(dbr))); - dbr = mdb_env_set_maxdbs(env, 7); + dbr = mdb_env_set_maxdbs(env, 6); CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to set max env dbs: " + std::string(mdb_strerror(dbr))); const std::string actual_filename = get_cache_filename(cache_filename); dbr = mdb_env_open(env, actual_filename.c_str(), flags, 0664); @@ -270,10 +268,6 @@ static void init(std::string cache_filename) dbr = mdb_dbi_open(txn, "ring_instances", MDB_CREATE, &dbi_ring_instances); CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to open LMDB dbi: " + std::string(mdb_strerror(dbr))); - dbr = mdb_dbi_open(txn, "newly_spent", MDB_CREATE | MDB_INTEGERKEY | MDB_DUPSORT | MDB_DUPFIXED, &dbi_newly_spent); - CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to open LMDB dbi: " + std::string(mdb_strerror(dbr))); - mdb_set_dupsort(txn, dbi_newly_spent, compare_double64); - dbr = mdb_dbi_open(txn, "stats", MDB_CREATE, &dbi_stats); CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to open LMDB dbi: " + std::string(mdb_strerror(dbr))); @@ -291,7 +285,6 @@ static void close() mdb_dbi_close(env, dbi_processed_txidx); mdb_dbi_close(env, dbi_spent); mdb_dbi_close(env, dbi_ring_instances); - mdb_dbi_close(env, dbi_newly_spent); mdb_dbi_close(env, dbi_stats); mdb_env_close(env); env = NULL; @@ -423,7 +416,7 @@ static std::vector canonicalize(const std::vector &v) return c; } -static uint64_t get_num_spent_outputs(bool newly) +static uint64_t get_num_spent_outputs() { MDB_txn *txn; bool tx_active = false; @@ -434,7 +427,7 @@ static uint64_t get_num_spent_outputs(bool newly) tx_active = true; MDB_cursor *cur; - dbr = mdb_cursor_open(txn, newly ? dbi_newly_spent : dbi_spent, &cur); + dbr = mdb_cursor_open(txn, dbi_spent, &cur); CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to open cursor for spent outputs: " + std::string(mdb_strerror(dbr))); MDB_val k, v; mdb_size_t count = 0; @@ -454,10 +447,10 @@ static uint64_t get_num_spent_outputs(bool newly) return count; } -static void add_spent_output(MDB_txn *txn, const output_data &od, bool newly) +static void add_spent_output(MDB_txn *txn, const output_data &od) { MDB_cursor *cur; - int dbr = mdb_cursor_open(txn, newly ? dbi_newly_spent : dbi_spent, &cur); + int dbr = mdb_cursor_open(txn, dbi_spent, &cur); CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to open cursor for spent outputs: " + std::string(mdb_strerror(dbr))); MDB_val v = {sizeof(od), (void*)&od}; dbr = mdb_cursor_put(cur, (MDB_val *)&zerokval, &v, MDB_NODUPDATA); @@ -465,10 +458,10 @@ static void add_spent_output(MDB_txn *txn, const output_data &od, bool newly) mdb_cursor_close(cur); } -static bool is_output_spent(MDB_txn *txn, const output_data &od, bool newly) +static bool is_output_spent(MDB_txn *txn, const output_data &od) { MDB_cursor *cur; - int dbr = mdb_cursor_open(txn, newly ? dbi_newly_spent : dbi_spent, &cur); + int dbr = mdb_cursor_open(txn, dbi_spent, &cur); CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to open cursor for spent outputs: " + std::string(mdb_strerror(dbr))); MDB_val v = {sizeof(od), (void*)&od}; dbr = mdb_cursor_get(cur, (MDB_val *)&zerokval, &v, MDB_GET_BOTH); @@ -478,10 +471,10 @@ static bool is_output_spent(MDB_txn *txn, const output_data &od, bool newly) return spent; } -static std::vector get_spent_outputs(MDB_txn *txn, bool newly) +static std::vector get_spent_outputs(MDB_txn *txn) { MDB_cursor *cur; - int dbr = mdb_cursor_open(txn, newly ? dbi_newly_spent : dbi_spent, &cur); + int dbr = mdb_cursor_open(txn, dbi_spent, &cur); CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to open cursor for spent outputs: " + std::string(mdb_strerror(dbr))); MDB_val k, v; uint64_t count = 0; @@ -507,12 +500,6 @@ static std::vector get_spent_outputs(MDB_txn *txn, bool newly) return outs; } -static void clear_spent_outputs(MDB_txn *txn, bool newly) -{ - int dbr = mdb_drop(txn, newly ? dbi_newly_spent : dbi_spent, 0); - CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to clear spent outputs: " + std::string(mdb_strerror(dbr))); -} - static uint64_t get_processed_txidx(const std::string &name) { MDB_txn *txn; @@ -899,7 +886,7 @@ int main(int argc, char* argv[]) size_t done = 0; - const uint64_t start_blackballed_outputs = get_num_spent_outputs(false); + const uint64_t start_blackballed_outputs = get_num_spent_outputs(); tools::ringdb ringdb(output_file_path.string(), epee::string_tools::pod_to_hex(get_genesis_block_hash(inputs[0]))); @@ -958,7 +945,7 @@ int main(int argc, char* argv[]) MINFO("Blackballing output " << pkey << ", due to being used in a 1-ring"); std::cout << "\r" << start_idx << "/" << n_txes << " \r" << std::flush; blackballs.push_back(pkey); - add_spent_output(txn, output_data(txin.amount, absolute[0]), true); + add_spent_output(txn, output_data(txin.amount, absolute[0])); inc_stat(txn, txin.amount ? "pre-rct-ring-size-1" : "rct-ring-size-1"); } else if (n == 0 && instances == new_ring.size()) @@ -969,7 +956,7 @@ int main(int argc, char* argv[]) MINFO("Blackballing output " << pkey << ", due to being used in " << new_ring.size() << " identical " << new_ring.size() << "-rings"); std::cout << "\r" << start_idx << "/" << n_txes << " \r" << std::flush; blackballs.push_back(pkey); - add_spent_output(txn, output_data(txin.amount, absolute[o]), true); + add_spent_output(txn, output_data(txin.amount, absolute[o])); inc_stat(txn, txin.amount ? "pre-rct-duplicate-rings" : "rct-duplicate-rings"); } } @@ -1002,7 +989,7 @@ int main(int argc, char* argv[]) MINFO("Blackballing output " << pkey << ", due to being used in rings with a single common element"); std::cout << "\r" << start_idx << "/" << n_txes << " \r" << std::flush; blackballs.push_back(pkey); - add_spent_output(txn, output_data(txin.amount, common[0]), true); + add_spent_output(txn, output_data(txin.amount, common[0])); inc_stat(txn, txin.amount ? "pre-rct-key-image-attack" : "rct-key-image-attack"); } else @@ -1024,7 +1011,7 @@ int main(int argc, char* argv[]) ringdb.blackball(blackballs); blackballs.clear(); } - set_processed_txidx(txn, canonical, start_idx); + set_processed_txidx(txn, canonical, start_idx+1); ++records; if (records >= records_per_sync) @@ -1052,9 +1039,19 @@ int main(int argc, char* argv[]) break; } - while (get_num_spent_outputs(true) != 0) + std::vector work_spent; + if (get_num_spent_outputs() > start_blackballed_outputs) + { + MDB_txn *txn; + dbr = mdb_txn_begin(env, NULL, MDB_RDONLY, &txn); + CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to create LMDB transaction: " + std::string(mdb_strerror(dbr))); + work_spent = get_spent_outputs(txn); + mdb_txn_abort(txn); + } + + while (!work_spent.empty()) { - LOG_PRINT_L0("Secondary pass due to " << get_num_spent_outputs(true) << " newly found spent outputs"); + LOG_PRINT_L0("Secondary pass on " << work_spent.size() << " spent outputs"); int dbr = resize_env(cache_dir.c_str()); CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to resize LMDB database: " + std::string(mdb_strerror(dbr))); @@ -1063,14 +1060,10 @@ int main(int argc, char* argv[]) dbr = mdb_txn_begin(env, NULL, 0, &txn); CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to create LMDB transaction: " + std::string(mdb_strerror(dbr))); - std::vector work_spent = get_spent_outputs(txn, true); - clear_spent_outputs(txn, true); - - for (const auto &od: work_spent) - add_spent_output(txn, od, false); - std::vector blackballs; - for (const output_data &od: work_spent) + std::vector scan_spent = std::move(work_spent); + work_spent.clear(); + for (const output_data &od: scan_spent) { std::vector key_images = get_key_images(txn, od); for (const crypto::key_image &ki: key_images) @@ -1083,18 +1076,19 @@ int main(int argc, char* argv[]) for (uint64_t out: absolute) { output_data new_od(od.amount, out); - if (is_output_spent(txn, new_od, false)) + if (is_output_spent(txn, new_od)) ++known; else last_unknown = out; } - if (known == absolute.size() - 1 && !is_output_spent(txn, output_data(od.amount, last_unknown), false) && !is_output_spent(txn, output_data(od.amount, last_unknown), true)) + if (known == absolute.size() - 1 && !is_output_spent(txn, output_data(od.amount, last_unknown))) { const crypto::public_key pkey = get_output_key(cur0, od.amount, last_unknown); MINFO("Blackballing output " << pkey << ", due to being used in a " << absolute.size() << "-ring where all other outputs are known to be spent"); blackballs.push_back(pkey); - add_spent_output(txn, output_data(od.amount, last_unknown), true); + add_spent_output(txn, output_data(od.amount, last_unknown)); + work_spent.push_back(output_data(od.amount, last_unknown)); inc_stat(txn, od.amount ? "pre-rct-chain-reaction" : "rct-chain-reaction"); } } @@ -1108,8 +1102,8 @@ int main(int argc, char* argv[]) CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to commit txn creating/opening database: " + std::string(mdb_strerror(dbr))); } - uint64_t diff = get_num_spent_outputs(false) - start_blackballed_outputs; - LOG_PRINT_L0(std::to_string(diff) << " new outputs blackballed, " << get_num_spent_outputs(false) << " total outputs blackballed"); + uint64_t diff = get_num_spent_outputs() - start_blackballed_outputs; + LOG_PRINT_L0(std::to_string(diff) << " new outputs blackballed, " << get_num_spent_outputs() << " total outputs blackballed"); MDB_txn *txn; dbr = mdb_txn_begin(env, NULL, MDB_RDONLY, &txn); From eb8a51be68f9a32b85a5fcfccb5ab3baa461639e Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 14 Aug 2018 10:57:39 +0000 Subject: [PATCH 0311/1404] blockchain_blackball: detect spent outputs by partial ring reuse --- .../blockchain_blackball.cpp | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/blockchain_utilities/blockchain_blackball.cpp b/src/blockchain_utilities/blockchain_blackball.cpp index feafdc7402e..df1d14eafa7 100644 --- a/src/blockchain_utilities/blockchain_blackball.cpp +++ b/src/blockchain_utilities/blockchain_blackball.cpp @@ -587,6 +587,25 @@ static uint64_t get_ring_instances(MDB_txn *txn, uint64_t amount, const std::vec return *(const uint64_t*)v.mv_data; } +static uint64_t get_ring_subset_instances(MDB_txn *txn, uint64_t amount, const std::vector &ring) +{ + uint64_t instances = get_ring_instances(txn, amount, ring); + if (ring.size() > 11) + return instances; + + uint64_t extra = 0; + for (uint64_t mask = 1; mask < (1u << ring.size()) - 1; ++mask) + { + std::vector subset; + subset.reserve(ring.size()); + for (size_t i = 0; i < ring.size(); ++i) + if ((mask >> i) & 1) + subset.push_back(ring[i]); + extra += get_ring_instances(txn, amount, subset); + } + return instances + extra; +} + static void set_ring_instances(MDB_txn *txn, uint64_t amount, const std::vector &ring, uint64_t count) { const std::string sring = keep_under_511(compress_ring(amount, ring)); @@ -808,6 +827,7 @@ int main(int argc, char* argv[]) "database", available_dbs.c_str(), default_db_type }; const command_line::arg_descriptor arg_rct_only = {"rct-only", "Only work on ringCT outputs", false}; + const command_line::arg_descriptor arg_check_subsets = {"check-subsets", "Check ring subsets (very expensive)", false}; const command_line::arg_descriptor > arg_inputs = {"inputs", "Path to Monero DB, and path to any fork DBs"}; const command_line::arg_descriptor arg_db_sync_mode = { "db-sync-mode" @@ -819,6 +839,7 @@ int main(int argc, char* argv[]) command_line::add_arg(desc_cmd_sett, arg_log_level); command_line::add_arg(desc_cmd_sett, arg_database); command_line::add_arg(desc_cmd_sett, arg_rct_only); + command_line::add_arg(desc_cmd_sett, arg_check_subsets); command_line::add_arg(desc_cmd_sett, arg_db_sync_mode); command_line::add_arg(desc_cmd_sett, arg_inputs); command_line::add_arg(desc_cmd_only, command_line::arg_help); @@ -857,6 +878,7 @@ int main(int argc, char* argv[]) output_file_path = command_line::get_arg(vm, arg_blackball_db_dir); bool opt_rct_only = command_line::get_arg(vm, arg_rct_only); + bool opt_check_subsets = command_line::get_arg(vm, arg_check_subsets); std::string db_type = command_line::get_arg(vm, arg_database); if (!cryptonote::blockchain_valid_db_type(db_type)) @@ -960,6 +982,18 @@ int main(int argc, char* argv[]) inc_stat(txn, txin.amount ? "pre-rct-duplicate-rings" : "rct-duplicate-rings"); } } + else if (n == 0 && opt_check_subsets && get_ring_subset_instances(txn, txin.amount, new_ring) >= new_ring.size()) + { + for (size_t o = 0; o < new_ring.size(); ++o) + { + const crypto::public_key pkey = get_output_key(cur0, txin.amount, absolute[o]); + MINFO("Blackballing output " << pkey << ", due to being used in " << new_ring.size() << " subsets of " << new_ring.size() << "-rings"); + std::cout << "\r" << start_idx << "/" << n_txes << " \r" << std::flush; + blackballs.push_back(pkey); + add_spent_output(txn, output_data(txin.amount, absolute[o])); + inc_stat(txn, txin.amount ? "pre-rct-subset-rings" : "rct-subset-rings"); + } + } else if (n > 0 && get_relative_ring(txn, txin.k_image, relative_ring)) { MDEBUG("Key image " << txin.k_image << " already seen: rings " << @@ -1115,6 +1149,7 @@ int main(int argc, char* argv[]) static const struct { const char *key; uint64_t base; } stat_keys[] = { { "pre-rct-ring-size-1", pre_rct }, { "rct-ring-size-1", rct }, { "pre-rct-duplicate-rings", pre_rct }, { "rct-duplicate-rings", rct }, + { "pre-rct-subset-rings", pre_rct }, { "rct-subset-rings", rct }, { "pre-rct-key-image-attack", pre_rct }, { "rct-key-image-attack", rct }, { "pre-rct-chain-reaction", pre_rct }, { "rct-chain-reaction", rct }, }; From 639a3c019cf6ad45743a8432ca37bd5c162c4d35 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 14 Aug 2018 15:06:16 +0000 Subject: [PATCH 0312/1404] blockchain_blackball: make it clear secondary passes are not incremental yet --- src/blockchain_utilities/blockchain_blackball.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/blockchain_utilities/blockchain_blackball.cpp b/src/blockchain_utilities/blockchain_blackball.cpp index df1d14eafa7..8a578a56dde 100644 --- a/src/blockchain_utilities/blockchain_blackball.cpp +++ b/src/blockchain_utilities/blockchain_blackball.cpp @@ -1061,7 +1061,7 @@ int main(int argc, char* argv[]) if (stop_requested) { - MINFO("Stopping scan, secondary passes will still happen..."); + MINFO("Stopping scan..."); return false; } return true; @@ -1074,6 +1074,10 @@ int main(int argc, char* argv[]) } std::vector work_spent; + + if (stop_requested) + goto skip_secondary_passes; + if (get_num_spent_outputs() > start_blackballed_outputs) { MDB_txn *txn; @@ -1126,6 +1130,12 @@ int main(int argc, char* argv[]) inc_stat(txn, od.amount ? "pre-rct-chain-reaction" : "rct-chain-reaction"); } } + + if (stop_requested) + { + MINFO("Stopping secondary passes. Secondary passes are not incremental, they will re-run fully."); + return false; + } } if (!blackballs.empty()) { @@ -1136,6 +1146,7 @@ int main(int argc, char* argv[]) CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to commit txn creating/opening database: " + std::string(mdb_strerror(dbr))); } +skip_secondary_passes: uint64_t diff = get_num_spent_outputs() - start_blackballed_outputs; LOG_PRINT_L0(std::to_string(diff) << " new outputs blackballed, " << get_num_spent_outputs() << " total outputs blackballed"); From b66ba78306913f28fd73831e8667ca5e966a9f07 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 18 Aug 2018 12:40:26 +0000 Subject: [PATCH 0313/1404] blockchain_blackball: do not process duplicate blockchains parts --- .../blockchain_blackball.cpp | 76 +++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/src/blockchain_utilities/blockchain_blackball.cpp b/src/blockchain_utilities/blockchain_blackball.cpp index 8a578a56dde..de71632083a 100644 --- a/src/blockchain_utilities/blockchain_blackball.cpp +++ b/src/blockchain_utilities/blockchain_blackball.cpp @@ -398,6 +398,77 @@ static bool for_all_transactions(const std::string &filename, uint64_t &start_id return fret; } +static uint64_t find_first_diverging_transaction(const std::string &first_filename, const std::string &second_filename) +{ + MDB_env *env[2]; + MDB_dbi dbi[2]; + MDB_txn *txn[2]; + MDB_cursor *cur[2]; + int dbr; + bool tx_active[2] = { false, false }; + uint64_t n_txes[2]; + MDB_val k; + MDB_val v[2]; + + epee::misc_utils::auto_scope_leave_caller txn_dtor[2]; + for (int i = 0; i < 2; ++i) + { + dbr = mdb_env_create(&env[i]); + if (dbr) throw std::runtime_error("Failed to create LDMB environment: " + std::string(mdb_strerror(dbr))); + dbr = mdb_env_set_maxdbs(env[i], 2); + if (dbr) throw std::runtime_error("Failed to set max env dbs: " + std::string(mdb_strerror(dbr))); + const std::string actual_filename = i ? second_filename : first_filename; + dbr = mdb_env_open(env[i], actual_filename.c_str(), 0, 0664); + if (dbr) throw std::runtime_error("Failed to open rings database file '" + + actual_filename + "': " + std::string(mdb_strerror(dbr))); + + dbr = mdb_txn_begin(env[i], NULL, MDB_RDONLY, &txn[i]); + if (dbr) throw std::runtime_error("Failed to create LMDB transaction: " + std::string(mdb_strerror(dbr))); + txn_dtor[i] = epee::misc_utils::create_scope_leave_handler([&, i](){if (tx_active[i]) mdb_txn_abort(txn[i]);}); + tx_active[i] = true; + + dbr = mdb_dbi_open(txn[i], "txs_pruned", MDB_INTEGERKEY, &dbi[i]); + if (dbr) + dbr = mdb_dbi_open(txn[i], "txs", MDB_INTEGERKEY, &dbi[i]); + if (dbr) throw std::runtime_error("Failed to open LMDB dbi: " + std::string(mdb_strerror(dbr))); + dbr = mdb_cursor_open(txn[i], dbi[i], &cur[i]); + if (dbr) throw std::runtime_error("Failed to create LMDB cursor: " + std::string(mdb_strerror(dbr))); + MDB_stat stat; + dbr = mdb_stat(txn[i], dbi[i], &stat); + if (dbr) throw std::runtime_error("Failed to query m_block_info: " + std::string(mdb_strerror(dbr))); + n_txes[i] = stat.ms_entries; + } + + if (n_txes[0] == 0 || n_txes[1] == 0) + throw std::runtime_error("No transaction in the database"); + uint64_t lo = 0, hi = std::min(n_txes[0], n_txes[1]) - 1; + while (lo <= hi) + { + uint64_t mid = (lo + hi) / 2; + + k.mv_size = sizeof(uint64_t); + k.mv_data = (void*)∣ + dbr = mdb_cursor_get(cur[0], &k, &v[0], MDB_SET); + if (dbr) throw std::runtime_error("Failed to query transaction: " + std::string(mdb_strerror(dbr))); + dbr = mdb_cursor_get(cur[1], &k, &v[1], MDB_SET); + if (dbr) throw std::runtime_error("Failed to query transaction: " + std::string(mdb_strerror(dbr))); + if (v[0].mv_size == v[1].mv_size && !memcmp(v[0].mv_data, v[1].mv_data, v[0].mv_size)) + lo = mid + 1; + else + hi = mid - 1; + } + + for (int i = 0; i < 2; ++i) + { + mdb_cursor_close(cur[i]); + mdb_txn_commit(txn[i]); + tx_active[i] = false; + mdb_dbi_close(env[i], dbi[i]); + mdb_env_close(env[i]); + } + return hi; +} + static std::vector canonicalize(const std::vector &v) { std::vector c; @@ -931,6 +1002,11 @@ int main(int argc, char* argv[]) { const std::string canonical = boost::filesystem::canonical(inputs[n]).string(); uint64_t start_idx = get_processed_txidx(canonical); + if (n > 0 && start_idx == 0) + { + start_idx = find_first_diverging_transaction(inputs[0], inputs[n]); + LOG_PRINT_L0("First diverging transaction at " << start_idx); + } LOG_PRINT_L0("Reading blockchain from " << inputs[n] << " from " << start_idx); MDB_txn *txn; int dbr = mdb_txn_begin(env, NULL, 0, &txn); From 4bce935b4038046ed47db63ddd63fc4feab566cd Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 19 Aug 2018 16:52:56 +0000 Subject: [PATCH 0314/1404] blockchain_blackball: more optimizations --- .../blockchain_blackball.cpp | 131 ++++++++++++------ 1 file changed, 86 insertions(+), 45 deletions(-) diff --git a/src/blockchain_utilities/blockchain_blackball.cpp b/src/blockchain_utilities/blockchain_blackball.cpp index de71632083a..e68de9dc4b5 100644 --- a/src/blockchain_utilities/blockchain_blackball.cpp +++ b/src/blockchain_utilities/blockchain_blackball.cpp @@ -293,14 +293,24 @@ static void close() static std::string compress_ring(const std::vector &ring, std::string s = "") { + const size_t sz = s.size(); + s.resize(s.size() + 12 * ring.size()); + char *ptr = (char*)s.data() + sz; for (uint64_t out: ring) - s += tools::get_varint_data(out); + tools::write_varint(ptr, out); + if (ptr > s.data() + sz + 12 * ring.size()) + throw std::runtime_error("varint output overflow"); + s.resize(ptr - s.data()); return s; } static std::string compress_ring(uint64_t amount, const std::vector &ring) { - return compress_ring(ring, tools::get_varint_data(amount)); + char s[12], *ptr = s; + tools::write_varint(ptr, amount); + if (ptr > s + sizeof(s)) + throw std::runtime_error("varint output overflow"); + return compress_ring(ring, std::string(s, ptr-s)); } static std::vector decompress_ring(const std::string &s) @@ -518,27 +528,19 @@ static uint64_t get_num_spent_outputs() return count; } -static void add_spent_output(MDB_txn *txn, const output_data &od) +static void add_spent_output(MDB_cursor *cur, const output_data &od) { - MDB_cursor *cur; - int dbr = mdb_cursor_open(txn, dbi_spent, &cur); - CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to open cursor for spent outputs: " + std::string(mdb_strerror(dbr))); MDB_val v = {sizeof(od), (void*)&od}; - dbr = mdb_cursor_put(cur, (MDB_val *)&zerokval, &v, MDB_NODUPDATA); + int dbr = mdb_cursor_put(cur, (MDB_val *)&zerokval, &v, MDB_NODUPDATA); CHECK_AND_ASSERT_THROW_MES(!dbr || dbr == MDB_KEYEXIST, "Failed to add spent output: " + std::string(mdb_strerror(dbr))); - mdb_cursor_close(cur); } -static bool is_output_spent(MDB_txn *txn, const output_data &od) +static bool is_output_spent(MDB_cursor *cur, const output_data &od) { - MDB_cursor *cur; - int dbr = mdb_cursor_open(txn, dbi_spent, &cur); - CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to open cursor for spent outputs: " + std::string(mdb_strerror(dbr))); MDB_val v = {sizeof(od), (void*)&od}; - dbr = mdb_cursor_get(cur, (MDB_val *)&zerokval, &v, MDB_GET_BOTH); + int dbr = mdb_cursor_get(cur, (MDB_val *)&zerokval, &v, MDB_GET_BOTH); CHECK_AND_ASSERT_THROW_MES(!dbr || dbr == MDB_NOTFOUND, "Failed to get spent output: " + std::string(mdb_strerror(dbr))); bool spent = dbr == 0; - mdb_cursor_close(cur); return spent; } @@ -612,7 +614,6 @@ static void set_processed_txidx(MDB_txn *txn, const std::string &name, uint64_t static bool get_relative_ring(MDB_txn *txn, const crypto::key_image &ki, std::vector &ring) { - const std::string sring = compress_ring(ring); MDB_val k, v; k.mv_data = (void*)&ki; k.mv_size = sizeof(ki); @@ -665,10 +666,11 @@ static uint64_t get_ring_subset_instances(MDB_txn *txn, uint64_t amount, const s return instances; uint64_t extra = 0; + std::vector subset; + subset.reserve(ring.size()); for (uint64_t mask = 1; mask < (1u << ring.size()) - 1; ++mask) { - std::vector subset; - subset.reserve(ring.size()); + subset.resize(0); for (size_t i = 0; i < ring.size(); ++i) if ((mask >> i) & 1) subset.push_back(ring[i]); @@ -677,16 +679,28 @@ static uint64_t get_ring_subset_instances(MDB_txn *txn, uint64_t amount, const s return instances + extra; } -static void set_ring_instances(MDB_txn *txn, uint64_t amount, const std::vector &ring, uint64_t count) +static uint64_t inc_ring_instances(MDB_txn *txn, uint64_t amount, const std::vector &ring) { const std::string sring = keep_under_511(compress_ring(amount, ring)); MDB_val k, v; k.mv_data = (void*)sring.data(); k.mv_size = sring.size(); + + int dbr = mdb_get(txn, dbi_ring_instances, &k, &v); + CHECK_AND_ASSERT_THROW_MES(!dbr || dbr == MDB_NOTFOUND, "Failed to get ring instances: " + std::string(mdb_strerror(dbr))); + + uint64_t count; + if (dbr == MDB_NOTFOUND) + count = 1; + else + count = 1 + *(const uint64_t*)v.mv_data; + v.mv_data = &count; v.mv_size = sizeof(count); - int dbr = mdb_put(txn, dbi_ring_instances, &k, &v, 0); - CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to get ring instances: " + std::string(mdb_strerror(dbr))); + dbr = mdb_put(txn, dbi_ring_instances, &k, &v, 0); + CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to set ring instances: " + std::string(mdb_strerror(dbr))); + + return count; } static std::vector get_key_images(MDB_txn *txn, const output_data &od) @@ -899,6 +913,7 @@ int main(int argc, char* argv[]) }; const command_line::arg_descriptor arg_rct_only = {"rct-only", "Only work on ringCT outputs", false}; const command_line::arg_descriptor arg_check_subsets = {"check-subsets", "Check ring subsets (very expensive)", false}; + const command_line::arg_descriptor arg_verbose = {"verbose", "Verbose output)", false}; const command_line::arg_descriptor > arg_inputs = {"inputs", "Path to Monero DB, and path to any fork DBs"}; const command_line::arg_descriptor arg_db_sync_mode = { "db-sync-mode" @@ -911,6 +926,7 @@ int main(int argc, char* argv[]) command_line::add_arg(desc_cmd_sett, arg_database); command_line::add_arg(desc_cmd_sett, arg_rct_only); command_line::add_arg(desc_cmd_sett, arg_check_subsets); + command_line::add_arg(desc_cmd_sett, arg_verbose); command_line::add_arg(desc_cmd_sett, arg_db_sync_mode); command_line::add_arg(desc_cmd_sett, arg_inputs); command_line::add_arg(desc_cmd_only, command_line::arg_help); @@ -950,6 +966,7 @@ int main(int argc, char* argv[]) output_file_path = command_line::get_arg(vm, arg_blackball_db_dir); bool opt_rct_only = command_line::get_arg(vm, arg_rct_only); bool opt_check_subsets = command_line::get_arg(vm, arg_check_subsets); + bool opt_verbose = command_line::get_arg(vm, arg_verbose); std::string db_type = command_line::get_arg(vm, arg_database); if (!cryptonote::blockchain_valid_db_type(db_type)) @@ -1011,6 +1028,9 @@ int main(int argc, char* argv[]) MDB_txn *txn; int dbr = mdb_txn_begin(env, NULL, 0, &txn); CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to create LMDB transaction: " + std::string(mdb_strerror(dbr))); + MDB_cursor *cur; + dbr = mdb_cursor_open(txn, dbi_spent, &cur); + CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to open LMDB cursor: " + std::string(mdb_strerror(dbr))); size_t records = 0; const std::string filename = inputs[n]; std::vector blackballs; @@ -1034,16 +1054,17 @@ int main(int argc, char* argv[]) std::vector relative_ring; std::vector new_ring = canonicalize(txin.key_offsets); const uint32_t ring_size = txin.key_offsets.size(); - uint64_t instances = get_ring_instances(txn, txin.amount, new_ring); - ++instances; - set_ring_instances(txn, txin.amount, new_ring, instances); + const uint64_t instances = inc_ring_instances(txn, txin.amount, new_ring); if (n == 0 && ring_size == 1) { const crypto::public_key pkey = get_output_key(cur0, txin.amount, absolute[0]); - MINFO("Blackballing output " << pkey << ", due to being used in a 1-ring"); - std::cout << "\r" << start_idx << "/" << n_txes << " \r" << std::flush; + if (opt_verbose) + { + MINFO("Blackballing output " << pkey << ", due to being used in a 1-ring"); + std::cout << "\r" << start_idx << "/" << n_txes << " \r" << std::flush; + } blackballs.push_back(pkey); - add_spent_output(txn, output_data(txin.amount, absolute[0])); + add_spent_output(cur, output_data(txin.amount, absolute[0])); inc_stat(txn, txin.amount ? "pre-rct-ring-size-1" : "rct-ring-size-1"); } else if (n == 0 && instances == new_ring.size()) @@ -1051,10 +1072,13 @@ int main(int argc, char* argv[]) for (size_t o = 0; o < new_ring.size(); ++o) { const crypto::public_key pkey = get_output_key(cur0, txin.amount, absolute[o]); - MINFO("Blackballing output " << pkey << ", due to being used in " << new_ring.size() << " identical " << new_ring.size() << "-rings"); - std::cout << "\r" << start_idx << "/" << n_txes << " \r" << std::flush; + if (opt_verbose) + { + MINFO("Blackballing output " << pkey << ", due to being used in " << new_ring.size() << " identical " << new_ring.size() << "-rings"); + std::cout << "\r" << start_idx << "/" << n_txes << " \r" << std::flush; + } blackballs.push_back(pkey); - add_spent_output(txn, output_data(txin.amount, absolute[o])); + add_spent_output(cur, output_data(txin.amount, absolute[o])); inc_stat(txn, txin.amount ? "pre-rct-duplicate-rings" : "rct-duplicate-rings"); } } @@ -1063,10 +1087,13 @@ int main(int argc, char* argv[]) for (size_t o = 0; o < new_ring.size(); ++o) { const crypto::public_key pkey = get_output_key(cur0, txin.amount, absolute[o]); - MINFO("Blackballing output " << pkey << ", due to being used in " << new_ring.size() << " subsets of " << new_ring.size() << "-rings"); - std::cout << "\r" << start_idx << "/" << n_txes << " \r" << std::flush; + if (opt_verbose) + { + MINFO("Blackballing output " << pkey << ", due to being used in " << new_ring.size() << " subsets of " << new_ring.size() << "-rings"); + std::cout << "\r" << start_idx << "/" << n_txes << " \r" << std::flush; + } blackballs.push_back(pkey); - add_spent_output(txn, output_data(txin.amount, absolute[o])); + add_spent_output(cur, output_data(txin.amount, absolute[o])); inc_stat(txn, txin.amount ? "pre-rct-subset-rings" : "rct-subset-rings"); } } @@ -1096,10 +1123,13 @@ int main(int argc, char* argv[]) else if (common.size() == 1) { const crypto::public_key pkey = get_output_key(cur0, txin.amount, common[0]); - MINFO("Blackballing output " << pkey << ", due to being used in rings with a single common element"); - std::cout << "\r" << start_idx << "/" << n_txes << " \r" << std::flush; + if (opt_verbose) + { + MINFO("Blackballing output " << pkey << ", due to being used in rings with a single common element"); + std::cout << "\r" << start_idx << "/" << n_txes << " \r" << std::flush; + } blackballs.push_back(pkey); - add_spent_output(txn, output_data(txin.amount, common[0])); + add_spent_output(cur, output_data(txin.amount, common[0])); inc_stat(txn, txin.amount ? "pre-rct-key-image-attack" : "rct-key-image-attack"); } else @@ -1116,22 +1146,25 @@ int main(int argc, char* argv[]) if (n == 0) set_relative_ring(txn, txin.k_image, new_ring); } - if (!blackballs.empty()) - { - ringdb.blackball(blackballs); - blackballs.clear(); - } set_processed_txidx(txn, canonical, start_idx+1); ++records; if (records >= records_per_sync) { + if (!blackballs.empty()) + { + ringdb.blackball(blackballs); + blackballs.clear(); + } + mdb_cursor_close(cur); dbr = mdb_txn_commit(txn); CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to commit txn creating/opening database: " + std::string(mdb_strerror(dbr))); int dbr = resize_env(cache_dir.c_str()); CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to resize LMDB database: " + std::string(mdb_strerror(dbr))); dbr = mdb_txn_begin(env, NULL, 0, &txn); CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to create LMDB transaction: " + std::string(mdb_strerror(dbr))); + dbr = mdb_cursor_open(txn, dbi_spent, &cur); + CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to open LMDB cursor: " + std::string(mdb_strerror(dbr))); records = 0; } @@ -1142,6 +1175,7 @@ int main(int argc, char* argv[]) } return true; }); + mdb_cursor_close(cur); dbr = mdb_txn_commit(txn); CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to commit txn creating/opening database: " + std::string(mdb_strerror(dbr))); LOG_PRINT_L0("blockchain from " << inputs[n] << " processed till tx idx " << start_idx); @@ -1173,6 +1207,9 @@ int main(int argc, char* argv[]) MDB_txn *txn; dbr = mdb_txn_begin(env, NULL, 0, &txn); CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to create LMDB transaction: " + std::string(mdb_strerror(dbr))); + MDB_cursor *cur; + dbr = mdb_cursor_open(txn, dbi_spent, &cur); + CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to open LMDB cursor: " + std::string(mdb_strerror(dbr))); std::vector blackballs; std::vector scan_spent = std::move(work_spent); @@ -1190,18 +1227,21 @@ int main(int argc, char* argv[]) for (uint64_t out: absolute) { output_data new_od(od.amount, out); - if (is_output_spent(txn, new_od)) + if (is_output_spent(cur, new_od)) ++known; else last_unknown = out; } - if (known == absolute.size() - 1 && !is_output_spent(txn, output_data(od.amount, last_unknown))) + if (known == absolute.size() - 1) { const crypto::public_key pkey = get_output_key(cur0, od.amount, last_unknown); - MINFO("Blackballing output " << pkey << ", due to being used in a " << - absolute.size() << "-ring where all other outputs are known to be spent"); + if (opt_verbose) + { + MINFO("Blackballing output " << pkey << ", due to being used in a " << + absolute.size() << "-ring where all other outputs are known to be spent"); + } blackballs.push_back(pkey); - add_spent_output(txn, output_data(od.amount, last_unknown)); + add_spent_output(cur, output_data(od.amount, last_unknown)); work_spent.push_back(output_data(od.amount, last_unknown)); inc_stat(txn, od.amount ? "pre-rct-chain-reaction" : "rct-chain-reaction"); } @@ -1218,6 +1258,7 @@ int main(int argc, char* argv[]) ringdb.blackball(blackballs); blackballs.clear(); } + mdb_cursor_close(cur); dbr = mdb_txn_commit(txn); CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to commit txn creating/opening database: " + std::string(mdb_strerror(dbr))); } From 0664a9842172daff09d051ea0f080f1888252171 Mon Sep 17 00:00:00 2001 From: moneroexamples Date: Sun, 9 Sep 2018 12:35:47 +0000 Subject: [PATCH 0315/1404] wallet_rpc_server: remove some unused code --- src/wallet/wallet_rpc_server.cpp | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index 67f26c7a789..9a749cfa25c 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -155,16 +155,7 @@ namespace tools return false; m_vm = vm; - tools::wallet2 *walvars; - std::unique_ptr tmpwal; - if (m_wallet) - walvars = m_wallet; - else - { - tmpwal = tools::wallet2::make_dummy(*m_vm, true, password_prompter); - walvars = tmpwal.get(); - } boost::optional http_login{}; std::string bind_port = command_line::get_arg(*m_vm, arg_rpc_bind_port); const bool disable_auth = command_line::get_arg(*m_vm, arg_disable_rpc_login); From 03c07b167d2637836c0a5c7dd2994ca158057157 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 9 Sep 2018 15:26:50 +0000 Subject: [PATCH 0316/1404] rpc: return "already mining" in start_mining if already mining --- src/rpc/core_rpc_server.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index 0a6daf8f04d..d0021a6ef73 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -850,7 +850,13 @@ namespace cryptonote boost::thread::attributes attrs; attrs.set_stack_size(THREAD_STACK_SIZE); - if(!m_core.get_miner().start(info.address, static_cast(req.threads_count), attrs, req.do_background_mining, req.ignore_battery)) + cryptonote::miner &miner= m_core.get_miner(); + if (miner.is_mining()) + { + res.status = "Already mining"; + return true; + } + if(!miner.start(info.address, static_cast(req.threads_count), attrs, req.do_background_mining, req.ignore_battery)) { res.status = "Failed, mining not started"; LOG_PRINT_L0(res.status); From 8501b8dffbe64280b9cb0be33c79dd85f6b19bb6 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 9 Sep 2018 15:56:21 +0000 Subject: [PATCH 0317/1404] crypto_ops_builder: link to EdDSA/Ed25519 draft RFC instead of including the text, as it's not fully free to redistribute --- src/crypto/crypto_ops_builder/ietf.txt | 1404 +----------------------- 1 file changed, 3 insertions(+), 1401 deletions(-) diff --git a/src/crypto/crypto_ops_builder/ietf.txt b/src/crypto/crypto_ops_builder/ietf.txt index 0736f71ec06..609f5e75a55 100644 --- a/src/crypto/crypto_ops_builder/ietf.txt +++ b/src/crypto/crypto_ops_builder/ietf.txt @@ -1,1402 +1,4 @@ - +https://tools.ietf.org/id/draft-josefsson-eddsa-ed25519-02.txt -[Docs] [txt|pdf] [Tracker] [Email] [Diff1] [Diff2] [Nits] - -Versions: 00 01 02 - -Network Working Group S. Josefsson -Internet-Draft SJD AB -Intended status: Informational N. Moeller -Expires: August 26, 2015 - February 22, 2015 - - - EdDSA and Ed25519 - - draft-josefsson-eddsa-ed25519-02 - - -Abstract - - The elliptic curve signature scheme EdDSA and one instance of it - called Ed25519 is described. An example implementation and test - vectors are provided. - -Status of This Memo - - This Internet-Draft is submitted in full conformance with the - provisions of BCP 78 and BCP 79. - - Internet-Drafts are working documents of the Internet Engineering - Task Force (IETF). Note that other groups may also distribute - working documents as Internet-Drafts. The list of current Internet- - Drafts is at http://datatracker.ietf.org/drafts/current/. - - Internet-Drafts are draft documents valid for a maximum of six months - and may be updated, replaced, or obsoleted by other documents at any - time. It is inappropriate to use Internet-Drafts as reference - material or to cite them other than as "work in progress." - - This Internet-Draft will expire on August 26, 2015. - -Copyright Notice - - Copyright (c) 2015 IETF Trust and the persons identified as the - document authors. All rights reserved. - - This document is subject to BCP 78 and the IETF Trust's Legal - Provisions Relating to IETF Documents - (http://trustee.ietf.org/license-info) in effect on the date of - publication of this document. Please review these documents - carefully, as they describe your rights and restrictions with respect - to this document. Code Components extracted from this document must - include Simplified BSD License text as described in Section 4.e of - the Trust Legal Provisions and are provided without warranty as - described in the Simplified BSD License. - - - - -Josefsson & Moeller Expires August 26, 2015 [Page 1] - - -Internet-Draft EdDSA & Ed25519 February 2015 - - -Table of Contents - - 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . 2 - 2. Notation . . . . . . . . . . . . . . . . . . . . . . . . . . 3 - 3. Background . . . . . . . . . . . . . . . . . . . . . . . . . 3 - 4. EdDSA . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 - 4.1. Encoding . . . . . . . . . . . . . . . . . . . . . . . . 4 - 4.2. Keys . . . . . . . . . . . . . . . . . . . . . . . . . . 5 - 4.3. Sign . . . . . . . . . . . . . . . . . . . . . . . . . . 5 - 4.4. Verify . . . . . . . . . . . . . . . . . . . . . . . . . 5 - 5. Ed25519 . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 - 5.1. Modular arithmetic . . . . . . . . . . . . . . . . . . . 6 - 5.2. Encoding . . . . . . . . . . . . . . . . . . . . . . . . 6 - 5.3. Decoding . . . . . . . . . . . . . . . . . . . . . . . . 6 - 5.4. Point addition . . . . . . . . . . . . . . . . . . . . . 7 - 5.5. Key Generation . . . . . . . . . . . . . . . . . . . . . 8 - 5.6. Sign . . . . . . . . . . . . . . . . . . . . . . . . . . 8 - 5.7. Verify . . . . . . . . . . . . . . . . . . . . . . . . . 9 - 5.8. Python illustration . . . . . . . . . . . . . . . . . . . 9 - 6. Test Vectors for Ed25519 . . . . . . . . . . . . . . . . . . 14 - 7. Acknowledgements . . . . . . . . . . . . . . . . . . . . . . 17 - 8. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 18 - 9. Security Considerations . . . . . . . . . . . . . . . . . . . 18 - 9.1. Side-channel leaks . . . . . . . . . . . . . . . . . . . 18 - 10. References . . . . . . . . . . . . . . . . . . . . . . . . . 18 - 10.1. Normative References . . . . . . . . . . . . . . . . . . 18 - 10.2. Informative References . . . . . . . . . . . . . . . . . 18 - Appendix A. Ed25519 Python Library . . . . . . . . . . . . . . . 19 - Appendix B. Library driver . . . . . . . . . . . . . . . . . . . 23 - Authors' Addresses . . . . . . . . . . . . . . . . . . . . . . . 24 - -1. Introduction - - - The Edwards-curve Digital Signature Algorithm (EdDSA) is a variant of - Schnorr's signature system with Twisted Edwards curves. EdDSA needs - to be instantiated with certain parameters and this document describe - Ed25519 - an instantiation of EdDSA in a curve over GF(2^255-19). To - facilitate adoption in the Internet community of Ed25519, this - document describe the signature scheme in an implementation-oriented - way, and we provide sample code and test vectors. - - The advantages with EdDSA and Ed25519 include: - - 1. High-performance on a variety of platforms. - - 2. Does not require the use of a unique random number for each - signature. - - - - -Josefsson & Moeller Expires August 26, 2015 [Page 2] - - -Internet-Draft EdDSA & Ed25519 February 2015 - - - 3. More resilient to side-channel attacks. - - 4. Small public keys (32 bytes) and signatures (64 bytes). - - 5. The formulas are "strongly unified", i.e., they are valid for all - points on the curve, with no exceptions. This obviates the need - for EdDSA to perform expensive point validation on untrusted - public values. - - 6. Collision resilience, meaning that hash-function collisions do - not break this system. - - For further background, see the original EdDSA paper [EDDSA]. - -2. Notation - - - The following notation is used throughout the document: - - GF(p) finite field with p elements - - x^y x multiplied by itself y times - - B generator of the group or subgroup of interest - - n B B added to itself n times. - - h_i the i'th bit of h - - a || b (bit-)string a concatenated with (bit-)string b - -3. Background - - - EdDSA is defined using an elliptic curve over GF(p) of the form - - -x^2 + y^2 = 1 + d x^2 y^2 - - In general, p could be a prime power, but it is usually chosen as a - prime number. It is required that p = 1 modulo 4 (which implies that - -1 is a square modulo p) and that d is a non-square modulo p. For - Ed25519, the curve used is equivalent to Curve25519 [CURVE25519], - under a change of coordinates, which means that the difficulty of the - discrete logarithm problem is the same as for Curve25519. - - Points on this curve form a group under addition, (x3, y3) = (x1, y1) - + (x2, y2), with the formulas - - - - - - -Josefsson & Moeller Expires August 26, 2015 [Page 3] - - -Internet-Draft EdDSA & Ed25519 February 2015 - - - x1 y2 + x2 y1 y1 y2 + x1 x2 - x3 = -------------------, y3 = ------------------- - 1 + d x1 x2 y1 y2 1 - d x1 x2 y1 y2 - - The neutral element in the group is (0, 1). - - Unlike manyy other curves used for cryptographic applications, these - formulas are "strongly unified": they are valid for all points on the - curve, with no exceptions. In particular, the denominators are non- - zero for all input points. - - There are more efficient formulas, which are still strongly unified, - which use homogeneous coordinates to avoid the expensive modulo p - inversions. See [Faster-ECC] and [Edwards-revisited]. - -4. EdDSA - - - EdDSA is a digital signature system with several parameters. The - generic EdDSA digital signature system is normally not implemented - directly, but instead a particular instance of EdDSA (like Ed25519) - is implemented. A precise explanation of the generic EdDSA is thus - not particulary useful for implementers, but for background and - completeness, a succint description of the generic EdDSA algorithm is - given here. - - EdDSA has seven parameters: - - 1. an integer b >= 10. - - 2. a cryptographic hash function H producing 2b-bit outputs. - - 3. a prime power p congruent to 1 modulo 4. - - 4. a (b-1)-bit encoding of elements of the finite field GF(p). - - 5. a non-square element d of GF(p) - - 6. an element B != (0,1) of the set E = { (x,y) is a member of GF(p) - x GF(p) such that -x^2 + y^2 = 1 + dx^2y^2 }. - - 7. a prime q, of size b-3 bits, such that qB = (0, 1), i.e., q is - the order of B or a multiple thereof. - -4.1. Encoding - - - An element (x,y) of E is encoded as a b-bit string called ENC(x,y) - which is the (b-1)-bit encoding of y concatenated with one bit that - is 1 if x is negative and 0 if x is not negative. Negative elements - - - -Josefsson & Moeller Expires August 26, 2015 [Page 4] - - -Internet-Draft EdDSA & Ed25519 February 2015 - - - of GF(q) are those x which the (b-1)-bit encoding of x is - lexicographically larger than the (b-1)-bit encoding of -x. - -4.2. Keys - - - An EdDSA secret key is a b-bit string k. Let the hash H(k) = (h_0, - h_1, ..., h_(2b-1)) determine an integer a which is 2^(b-2) plus the - sum of m = 2^i * h_i for all i equal or larger than 3 and equal to or - less than b-3 such that m is a member of the set { 2^(b-2), 2^(b-2) + - 8, ..., 2^(b-1) - 8 }. The EdDSA public key is ENC(A) = ENC(aB). - The bits h_b, ..., h_(2b-1) is used below during signing. - -4.3. Sign - - - The signature of a message M under a secret key k is the 2b-bit - string ENC(R) || ENC'(S), where ENC'(S) is defined as the b-bit - little-endian encoding of S. R and S are derived as follows. First - define r = H(h_b, ... h_(2b-1)), M) interpreting 2b-bit strings in - little-endian form as integers in {0, 1, ..., 2^(2b)-1}. Let R=rB - and S=(r+H(ENC(R) || ENC(A) || M)a) mod l. - -4.4. Verify - - - To verify a signature ENC(R) || ENC'(S) on a message M under a public - key ENC(A), proceed as follows. Parse the inputs so that A and R is - an element of E, and S is a member of the set {0, 1, ..., l-1 }. - Compute H' = H(ENC(R) || ENC(A) || M) and check the group equation - 8SB = 8R + 8H'A in E. Verification is rejected if parsing fails or - the group equation does not hold. - -5. Ed25519 - - - Theoretically, Ed25519 is EdDSA instantiated with b=256, H being - SHA-512 [RFC4634], p is the prime 2^255-19, the 255-bit encoding of - GF(2^255-19) being the little-endian encoding of {0, 1, ..., - 2^255-20}, q is the prime 2^252 + 0x14def9dea2f79cd65812631a5cf5d3ed, - d = -121665/121666 which is a member of GF(p), and B is the unique - point (x, 4/5) in E for which x is "positive", which with the - encoding used simply means that the least significant bit of x is 0. - The curve p, prime q, d and B follows from [I-D.irtf-cfrg-curves]. - - Written out explicitly, B is the point (15112221349535400772501151409 - 588531511454012693041857206046113283949847762202, 4631683569492647816 - 9428394003475163141307993866256225615783033603165251855960). - - - - - - - -Josefsson & Moeller Expires August 26, 2015 [Page 5] - - -Internet-Draft EdDSA & Ed25519 February 2015 - - -5.1. Modular arithmetic - - - For advise on how to implement arithmetic modulo p = 2^255 - 1 - efficiently and securely, see Curve25519 [CURVE25519]. For inversion - modulo p, it is recommended to use the identity x^-1 = x^(p-2) (mod - p). - - For point decoding or "decompression", square roots modulo p are - needed. They can be computed using the Tonelli-Shanks algorithm, or - the special case for p = 5 (mod 8). To find a square root of a, - first compute the candidate root x = a^((p+3)/8) (mod p). Then there - are three cases: - - x^2 = a (mod p). Then x is a square root. - - x^2 = -a (mod p). Then 2^((p-1)/4) x is a square root. - - a is not a square modulo p. - -5.2. Encoding - - - All values are coded as octet strings, and integers are coded using - little endian convention. I.e., a 32-octet string h h[0],...h[31] - represents the integer h[0] + 2^8 h[1] + ... + 2^248 h[31]. - - A curve point (x,y), with coordiantes in the range 0 <= x,y < p, is - coded as follows. First encode the y-coordinate as a little-endian - string of 32 octets. The most significant bit of the final octet is - always zero. To form the encoding of the point, copy the least - significant bit of the x-coordinate to the most significant bit of - the final octet. - -5.3. Decoding - - - Decoding a point, given as a 32-octet string, is a little more - complicated. - - 1. First interpret the string as an integer in little-endian - representation. Bit 255 of this number is the least significant - bit of the x-coordinate, and denote this value x_0. The - y-coordinate is recovered simply by clearing this bit. If the - resulting value is >= p, decoding fails. - - 2. To recover the x coordinate, the curve equation implies x^2 = - (y^2 - 1) / (d y^2 + 1) (mod p). Since d is a non-square and -1 - is a square, the numerator, (d y^2 + 1), is always invertible - modulo p. Let u = y^2 - 1 and v = d y^2 + 1. To compute the - square root of (u/v), the first step is to compute the candidate - - - -Josefsson & Moeller Expires August 26, 2015 [Page 6] - - -Internet-Draft EdDSA & Ed25519 February 2015 - - - root x = (u/v)^((p+3)/8). This can be done using the following - trick, to use a single modular powering for both the inversion of - v and the square root: - - (p+3)/8 3 (p-5)/8 - x = (u/v) = u v (u v^7) (mod p) - - 3. Again, there are three cases: - - 1. If v x^2 = u (mod p), x is a square root. - - 2. If v x^2 = -u (mod p), set x <-- x 2^((p-1)/4), which is a - square root. - - 3. Otherwise, no square root exists modulo p, and decoding - fails. - - 4. Finally, use the x_0 bit to select the right square root. If x = - 0, and x_0 = 1, decoding fails. Otherwise, if x_0 != x mod 2, - set x <-- p - x. Return the decoded point (x,y). - -5.4. Point addition - - - For point addition, the following method is recommended. A point - (x,y) is represented in extended homogeneous coordinates (X, Y, Z, - T), with x = X/Z, y = Y/Z, x y = T/Z. - - The following formulas for adding two points, (x3,y3) = - (x1,y1)+(x2,y2) are described in [Edwards-revisited], section 3.1. - They are strongly unified, i.e., they work for any pair of valid - input points. - - A = (Y1-X1)*(Y2-X2) - B = (Y1+X1)*(Y2+X2) - C = T1*2*d*T2 - D = Z1*2*Z2 - E = B-A - F = D-C - G = D+C - H = B+A - X3 = E*F - Y3 = G*H - T3 = E*H - Z3 = F*G - - - - - - - -Josefsson & Moeller Expires August 26, 2015 [Page 7] - - -Internet-Draft EdDSA & Ed25519 February 2015 - - -5.5. Key Generation - - - The secret is 32 octets (256 bits, corresponding to b) of - cryptographically-secure random data. See [RFC4086] for a discussion - about randomness. - - The 32-byte public key is generated by the following steps. - - 1. Hash the 32-byte secret using SHA-512, storing the digest in a - 64-octet large buffer, denoted h. Only the lower 32 bytes are - used for generating the public key. - - 2. Prune the buffer. In C terminology: - - h[0] &= ~0x07; - h[31] &= 0x7F; - h[31] |= 0x40; - - 3. Interpret the buffer as the little-endian integer, forming a - secret scalar a. Perform a known-base-point scalar - multiplication a B. - - 4. The public key A is the encoding of the point aB. First encode - the y coordinate (in the range 0 <= y < p) as a little-endian - string of 32 octets. The most significant bit of the final octet - is always zero. To form the encoding of the point aB, copy the - least significant bit of the x coordinate to the most significant - bit of the final octet. The result is the public key. - -5.6. Sign - - - The imputs to the signing procedure is the secret key, a 32-octet - string, and a message M of arbitrary size. - - 1. Hash the secret key, 32-octets, using SHA-512. Let h denote the - resulting digest. Construct the secret scalar a from the first - half of the digest, and the corresponding public key A, as - described in the previous section. Let prefix denote the second - half of the hash digest, h[32],...,h[63]. - - 2. Compute SHA-512(prefix || M), where M is the message to be - signed. Interpret the 64-octet digest as a little-endian integer - r. - - 3. Compute the point rB. For efficiency, do this by first reducing - r modulo q, the group order of B. Let the string R be the - encoding of this point. - - - - -Josefsson & Moeller Expires August 26, 2015 [Page 8] - - -Internet-Draft EdDSA & Ed25519 February 2015 - - - 4. Compute SHA512(R || A || M), and interpret the 64-octet digest as - a little-endian integer k. - - 5. Compute s = (r + k a) mod q. For efficiency, again reduce k - modulo q first. - - 6. Form the signature of the concatenation of R (32 octets) and the - little-endian encoding of s (32 octets, three most significant - bits of the final octets always zero). - -5.7. Verify - - - 1. To verify a signature on a message M, first split the signature - into two 32-octet halves. Decode the first half as a point R, - and the second half as an integer s, in the range 0 <= s < q. If - the decoding fails, the signature is invalid. - - 2. Compute SHA512(R || A || M), and interpret the 64-octet digest as - a little-endian integer k. - - 3. Check the group equation 8s B = 8 R + 8k A. It's sufficient, but - not required, to instead check s B = R + k A. - -5.8. Python illustration - - - The rest of this section describes how Ed25519 can be implemented in - Python (version 3.2 or later) for illustration. See appendix A for - the complete implementation and appendix B for a test-driver to run - it through some test vectors. - - First some preliminaries that will be needed. - - - - - - - - - - - - - - - - - - - - -Josefsson & Moeller Expires August 26, 2015 [Page 9] - - -Internet-Draft EdDSA & Ed25519 February 2015 - - - import hashlib - - def sha512(s): - return hashlib.sha512(s).digest() - - # Base field Z_p - p = 2**255 - 19 - - def modp_inv(x): - return pow(x, p-2, p) - - # Curve constant - d = -121665 * modp_inv(121666) % p - - # Group order - q = 2**252 + 27742317777372353535851937790883648493 - - def sha512_modq(s): - return int.from_bytes(sha512(s), "little") % q - - Then follows functions to perform point operations. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Josefsson & Moeller Expires August 26, 2015 [Page 10] - - -Internet-Draft EdDSA & Ed25519 February 2015 - - -# Points are represented as tuples (X, Y, Z, T) of extended coordinates, -# with x = X/Z, y = Y/Z, x*y = T/Z - -def point_add(P, Q): - A = (P[1]-P[0])*(Q[1]-Q[0]) % p - B = (P[1]+P[0])*(Q[1]+Q[0]) % p - C = 2 * P[3] * Q[3] * d % p - D = 2 * P[2] * Q[2] % p - E = B-A - F = D-C - G = D+C - H = B+A - return (E*F, G*H, F*G, E*H) - -# Computes Q = s * Q -def point_mul(s, P): - Q = (0, 1, 1, 0) # Neutral element - while s > 0: - # Is there any bit-set predicate? - if s & 1: - Q = point_add(Q, P) - P = point_add(P, P) - s >>= 1 - return Q - -def point_equal(P, Q): - # x1 / z1 == x2 / z2 <==> x1 * z2 == x2 * z1 - if (P[0] * Q[2] - Q[0] * P[2]) % p != 0: - return False - if (P[1] * Q[2] - Q[1] * P[2]) % p != 0: - return False - return True - - Now follows functions for point compression. - - - - - - - - - - - - - - - - - -Josefsson & Moeller Expires August 26, 2015 [Page 11] - - -Internet-Draft EdDSA & Ed25519 February 2015 - - -# Square root of -1 -modp_sqrt_m1 = pow(2, (p-1) // 4, p) - -# Compute corresponding x coordinate, with low bit corresponding to sign, -# or return None on failure -def recover_x(y, sign): - x2 = (y*y-1) * modp_inv(d*y*y+1) - if x2 == 0: - if sign: - return None - else: - return 0 - - # Compute square root of x2 - x = pow(x2, (p+3) // 8, p) - if (x*x - x2) % p != 0: - x = x * modp_sqrt_m1 % p - if (x*x - x2) % p != 0: - return None - - if (x & 1) != sign: - x = p - x - return x - -# Base point -g_y = 4 * modp_inv(5) % p -g_x = recover_x(g_y, 0) -G = (g_x, g_y, 1, g_x * g_y % p) - -def point_compress(P): - zinv = modp_inv(P[2]) - x = P[0] * zinv % p - y = P[1] * zinv % p - return int.to_bytes(y | ((x & 1) << 255), 32, "little") - -def point_decompress(s): - if len(s) != 32: - raise Exception("Invalid input length for decompression") - y = int.from_bytes(s, "little") - sign = y >> 255 - y &= (1 << 255) - 1 - - x = recover_x(y, sign) - if x is None: - return None - else: - return (x, y, 1, x*y % p) - - - - -Josefsson & Moeller Expires August 26, 2015 [Page 12] - - -Internet-Draft EdDSA & Ed25519 February 2015 - - - These are functions for manipulating the secret. - - def secret_expand(secret): - if len(secret) != 32: - raise Exception("Bad size of private key") - h = sha512(secret) - a = int.from_bytes(h[:32], "little") - a &= (1 << 254) - 8 - a |= (1 << 254) - return (a, h[32:]) - - def secret_to_public(secret): - (a, dummy) = secret_expand(secret) - return point_compress(point_mul(a, G)) - - The signature function works as below. - - def sign(secret, msg): - a, prefix = secret_expand(secret) - A = point_compress(point_mul(a, G)) - r = sha512_modq(prefix + msg) - R = point_mul(r, G) - Rs = point_compress(R) - h = sha512_modq(Rs + A + msg) - s = (r + h * a) % q - return Rs + int.to_bytes(s, 32, "little") - - And finally the verification function. - - def verify(public, msg, signature): - if len(public) != 32: - raise Exception("Bad public-key length") - if len(signature) != 64: - Exception("Bad signature length") - A = point_decompress(public) - if not A: - return False - Rs = signature[:32] - R = point_decompress(Rs) - if not R: - return False - s = int.from_bytes(signature[32:], "little") - h = sha512_modq(Rs + public + msg) - sB = point_mul(s, G) - hA = point_mul(h, A) - return point_equal(sB, point_add(R, hA)) - - - - - -Josefsson & Moeller Expires August 26, 2015 [Page 13] - - -Internet-Draft EdDSA & Ed25519 February 2015 - - -6. Test Vectors for Ed25519 - - - Below is a sequence of octets with test vectors for the the Ed25519 - signature algorithm. The octets are hex encoded and whitespace is - inserted for readability. Private keys are 64 bytes, public keys 32 - bytes, message of arbitrary length, and signatures are 64 bytes. The - test vectors are taken from [ED25519-TEST-VECTORS] (but we removed - the public key as a suffix of the secret key, and removed the message - from the signature) and [ED25519-LIBGCRYPT-TEST-VECTORS]. - - -----TEST 1 - SECRET KEY: - 9d61b19deffd5a60ba844af492ec2cc4 - 4449c5697b326919703bac031cae7f60 - - PUBLIC KEY: - d75a980182b10ab7d54bfed3c964073a - 0ee172f3daa62325af021a68f707511a - - MESSAGE (length 0 bytes): - - SIGNATURE: - e5564300c360ac729086e2cc806e828a - 84877f1eb8e5d974d873e06522490155 - 5fb8821590a33bacc61e39701cf9b46b - d25bf5f0595bbe24655141438e7a100b - - -----TEST 2 - SECRET KEY: - 4ccd089b28ff96da9db6c346ec114e0f - 5b8a319f35aba624da8cf6ed4fb8a6fb - - PUBLIC KEY: - 3d4017c3e843895a92b70aa74d1b7ebc - 9c982ccf2ec4968cc0cd55f12af4660c - - MESSAGE (length 1 byte): - 72 - - SIGNATURE: - 92a009a9f0d4cab8720e820b5f642540 - a2b27b5416503f8fb3762223ebdb69da - 085ac1e43e15996e458f3613d0f11d8c - 387b2eaeb4302aeeb00d291612bb0c00 - - -----TEST 3 - SECRET KEY: - c5aa8df43f9f837bedb7442f31dcb7b1 - - - -Josefsson & Moeller Expires August 26, 2015 [Page 14] - - -Internet-Draft EdDSA & Ed25519 February 2015 - - - 66d38535076f094b85ce3a2e0b4458f7 - - PUBLIC KEY: - fc51cd8e6218a1a38da47ed00230f058 - 0816ed13ba3303ac5deb911548908025 - - MESSAGE (length 2 bytes): - af82 - - SIGNATURE: - 6291d657deec24024827e69c3abe01a3 - 0ce548a284743a445e3680d7db5ac3ac - 18ff9b538d16f290ae67f760984dc659 - 4a7c15e9716ed28dc027beceea1ec40a - - -----TEST 1024 - SECRET KEY: - f5e5767cf153319517630f226876b86c - 8160cc583bc013744c6bf255f5cc0ee5 - - PUBLIC KEY: - 278117fc144c72340f67d0f2316e8386 - ceffbf2b2428c9c51fef7c597f1d426e - - MESSAGE: - 08b8b2b733424243760fe426a4b54908 - 632110a66c2f6591eabd3345e3e4eb98 - fa6e264bf09efe12ee50f8f54e9f77b1 - e355f6c50544e23fb1433ddf73be84d8 - 79de7c0046dc4996d9e773f4bc9efe57 - 38829adb26c81b37c93a1b270b20329d - 658675fc6ea534e0810a4432826bf58c - 941efb65d57a338bbd2e26640f89ffbc - 1a858efcb8550ee3a5e1998bd177e93a - 7363c344fe6b199ee5d02e82d522c4fe - ba15452f80288a821a579116ec6dad2b - 3b310da903401aa62100ab5d1a36553e - 06203b33890cc9b832f79ef80560ccb9 - a39ce767967ed628c6ad573cb116dbef - efd75499da96bd68a8a97b928a8bbc10 - 3b6621fcde2beca1231d206be6cd9ec7 - aff6f6c94fcd7204ed3455c68c83f4a4 - 1da4af2b74ef5c53f1d8ac70bdcb7ed1 - 85ce81bd84359d44254d95629e9855a9 - 4a7c1958d1f8ada5d0532ed8a5aa3fb2 - d17ba70eb6248e594e1a2297acbbb39d - 502f1a8c6eb6f1ce22b3de1a1f40cc24 - 554119a831a9aad6079cad88425de6bd - - - -Josefsson & Moeller Expires August 26, 2015 [Page 15] - - -Internet-Draft EdDSA & Ed25519 February 2015 - - - e1a9187ebb6092cf67bf2b13fd65f270 - 88d78b7e883c8759d2c4f5c65adb7553 - 878ad575f9fad878e80a0c9ba63bcbcc - 2732e69485bbc9c90bfbd62481d9089b - eccf80cfe2df16a2cf65bd92dd597b07 - 07e0917af48bbb75fed413d238f5555a - 7a569d80c3414a8d0859dc65a46128ba - b27af87a71314f318c782b23ebfe808b - 82b0ce26401d2e22f04d83d1255dc51a - ddd3b75a2b1ae0784504df543af8969b - e3ea7082ff7fc9888c144da2af58429e - c96031dbcad3dad9af0dcbaaaf268cb8 - fcffead94f3c7ca495e056a9b47acdb7 - 51fb73e666c6c655ade8297297d07ad1 - ba5e43f1bca32301651339e22904cc8c - 42f58c30c04aafdb038dda0847dd988d - cda6f3bfd15c4b4c4525004aa06eeff8 - ca61783aacec57fb3d1f92b0fe2fd1a8 - 5f6724517b65e614ad6808d6f6ee34df - f7310fdc82aebfd904b01e1dc54b2927 - 094b2db68d6f903b68401adebf5a7e08 - d78ff4ef5d63653a65040cf9bfd4aca7 - 984a74d37145986780fc0b16ac451649 - de6188a7dbdf191f64b5fc5e2ab47b57 - f7f7276cd419c17a3ca8e1b939ae49e4 - 88acba6b965610b5480109c8b17b80e1 - b7b750dfc7598d5d5011fd2dcc5600a3 - 2ef5b52a1ecc820e308aa342721aac09 - 43bf6686b64b2579376504ccc493d97e - 6aed3fb0f9cd71a43dd497f01f17c0e2 - cb3797aa2a2f256656168e6c496afc5f - b93246f6b1116398a346f1a641f3b041 - e989f7914f90cc2c7fff357876e506b5 - 0d334ba77c225bc307ba537152f3f161 - 0e4eafe595f6d9d90d11faa933a15ef1 - 369546868a7f3a45a96768d40fd9d034 - 12c091c6315cf4fde7cb68606937380d - b2eaaa707b4c4185c32eddcdd306705e - 4dc1ffc872eeee475a64dfac86aba41c - 0618983f8741c5ef68d3a101e8a3b8ca - c60c905c15fc910840b94c00a0b9d0 - - SIGNATURE: - 0aab4c900501b3e24d7cdf4663326a3a - 87df5e4843b2cbdb67cbf6e460fec350 - aa5371b1508f9f4528ecea23c436d94b - 5e8fcd4f681e30a6ac00a9704a188a03 - - - - -Josefsson & Moeller Expires August 26, 2015 [Page 16] - - -Internet-Draft EdDSA & Ed25519 February 2015 - - - -----TEST 1A - -----An additional test with the data from test 1 but using an - -----uncompressed public key. - SECRET KEY: - 9d61b19deffd5a60ba844af492ec2cc4 - 4449c5697b326919703bac031cae7f60 - - PUBLIC KEY: - 0455d0e09a2b9d34292297e08d60d0f6 - 20c513d47253187c24b12786bd777645 - ce1a5107f7681a02af2523a6daf372e1 - 0e3a0764c9d3fe4bd5b70ab18201985a - d7 - - MSG (length 0 bytes): - - SIGNATURE: - e5564300c360ac729086e2cc806e828a - 84877f1eb8e5d974d873e06522490155 - 5fb8821590a33bacc61e39701cf9b46b - d25bf5f0595bbe24655141438e7a100b - - -----TEST 1B - -----An additional test with the data from test 1 but using an - -----compressed prefix. - SECRET KEY: - 9d61b19deffd5a60ba844af492ec2cc4 - 4449c5697b326919703bac031cae7f60 - - PUBLIC KEY: - 40d75a980182b10ab7d54bfed3c96407 - 3a0ee172f3daa62325af021a68f70751 - 1a - - MESSAGE (length 0 bytes): - - SIGNATURE: - e5564300c360ac729086e2cc806e828a - 84877f1eb8e5d974d873e06522490155 - 5fb8821590a33bacc61e39701cf9b46b - d25bf5f0595bbe24655141438e7a100b - ----- - -7. Acknowledgements - - - Feedback on this document was received from Werner Koch and Damien - Miller. - - - - -Josefsson & Moeller Expires August 26, 2015 [Page 17] - - -Internet-Draft EdDSA & Ed25519 February 2015 - - -8. IANA Considerations - - - None. - -9. Security Considerations - - -9.1. Side-channel leaks - - - For implementations performing signatures, secrecy of the key is - fundamental. It is possible to protect against some side-channel - attacks by ensuring that the implementation executes exactly the same - sequence of instructions and performs exactly the same memory - accesses, for any value of the secret key. - - To make an implementation side-channel silent in this way, the modulo - p arithmetic must not use any data-dependent branches, e.g., related - to carry propagation. Side channel-silent point addition is - straight-forward, thanks to the unified formulas. - - Scalar multiplication, multiplying a point by an integer, needs some - additional effort to implement in a side-channel silent manner. One - simple approach is to implement a side-channel silent conditional - assignment, and use together with the binary algorithm to examine one - bit of the integer at a time. - - Note that the example implementation in this document does not - attempt to be side-channel silent. - -10. References - - -10.1. Normative References - - - [RFC4634] Eastlake, D. and T. Hansen, "US Secure Hash Algorithms - (SHA and HMAC-SHA)", RFC 4634, July 2006. - - [I-D.irtf-cfrg-curves] - Langley, A., Salz, R., and S. Turner, "Elliptic Curves for - Security", draft-irtf-cfrg-curves-01 (work in progress), - January 2015. - -10.2. Informative References - - - [RFC4086] Eastlake, D., Schiller, J., and S. Crocker, "Randomness - Requirements for Security", BCP 106, RFC 4086, June 2005. - - - - - - - -Josefsson & Moeller Expires August 26, 2015 [Page 18] - - -Internet-Draft EdDSA & Ed25519 February 2015 - - - [EDDSA] Bernstein, D., Duif, N., Lange, T., Schwabe, P., and B. - Yang, "High-speed high-security signatures", WWW - http://ed25519.cr.yp.to/ed25519-20110926.pdf, September - 2011. - - [Faster-ECC] - Bernstein, D. and T. Lange, "Faster addition and doubling - on elliptic curves", WWW http://eprint.iacr.org/2007/286, - July 2007. - - [Edwards-revisited] - Hisil, H., Wong, K., Carter, G., and E. Dawson, "Twisted - Edwards Curves Revisited", WWW - http://eprint.iacr.org/2008/522, December 2008. - - [CURVE25519] - Bernstein, D., "Curve25519: new Diffie-Hellman speed - records", WWW http://cr.yp.to/ecdh.html, February 2006. - - [ED25519-TEST-VECTORS] - Bernstein, D., Duif, N., Lange, T., Schwabe, P., and B. - Yang, "Ed25519 test vectors", WWW - http://ed25519.cr.yp.to/python/sign.input, July 2011. - - [ED25519-LIBGCRYPT-TEST-VECTORS] - Koch, W., "Ed25519 Libgcrypt test vectors", WWW - http://git.gnupg.org/cgi- - bin/gitweb.cgi?p=libgcrypt.git;a=blob;f=tests/t-ed25519.in - p;h=e13566f826321eece65e02c593bc7d885b3dbe23;hb=refs/ - heads/master, July 2014. - -Appendix A. Ed25519 Python Library - - - Below is an example implementation of Ed25519 written in Python, - version 3.2 or higher is required. - -# Loosely based on the public domain code at -# http://ed25519.cr.yp.to/software.html -# -# Needs python-3.2 - -import hashlib - - -def sha512(s): - return hashlib.sha512(s).digest() - -# Base field Z_p - - - -Josefsson & Moeller Expires August 26, 2015 [Page 19] - - -Internet-Draft EdDSA & Ed25519 February 2015 - - -p = 2**255 - 19 - - -def modp_inv(x): - return pow(x, p-2, p) - -# Curve constant -d = -121665 * modp_inv(121666) % p - -# Group order -q = 2**252 + 27742317777372353535851937790883648493 - - -def sha512_modq(s): - return int.from_bytes(sha512(s), "little") % q - -# Points are represented as tuples (X, Y, Z, T) of extended coordinates, -# with x = X/Z, y = Y/Z, x*y = T/Z - - -def point_add(P, Q): - A = (P[1]-P[0])*(Q[1]-Q[0]) % p - B = (P[1]+P[0])*(Q[1]+Q[0]) % p - C = 2 * P[3] * Q[3] * d % p - D = 2 * P[2] * Q[2] % p - E = B-A - F = D-C - G = D+C - H = B+A - return (E*F, G*H, F*G, E*H) - - -# Computes Q = s * Q -def point_mul(s, P): - Q = (0, 1, 1, 0) # Neutral element - while s > 0: - # Is there any bit-set predicate? - if s & 1: - Q = point_add(Q, P) - P = point_add(P, P) - s >>= 1 - return Q - - -def point_equal(P, Q): - # x1 / z1 == x2 / z2 <==> x1 * z2 == x2 * z1 - if (P[0] * Q[2] - Q[0] * P[2]) % p != 0: - return False - - - -Josefsson & Moeller Expires August 26, 2015 [Page 20] - - -Internet-Draft EdDSA & Ed25519 February 2015 - - - if (P[1] * Q[2] - Q[1] * P[2]) % p != 0: - return False - return True - -# Square root of -1 -modp_sqrt_m1 = pow(2, (p-1) // 4, p) - - -# Compute corresponding x coordinate, with low bit corresponding to sign, -# or return None on failure -def recover_x(y, sign): - x2 = (y*y-1) * modp_inv(d*y*y+1) - if x2 == 0: - if sign: - return None - else: - return 0 - - # Compute square root of x2 - x = pow(x2, (p+3) // 8, p) - if (x*x - x2) % p != 0: - x = x * modp_sqrt_m1 % p - if (x*x - x2) % p != 0: - return None - - if (x & 1) != sign: - x = p - x - return x - -# Base point -g_y = 4 * modp_inv(5) % p -g_x = recover_x(g_y, 0) -G = (g_x, g_y, 1, g_x * g_y % p) - - -def point_compress(P): - zinv = modp_inv(P[2]) - x = P[0] * zinv % p - y = P[1] * zinv % p - return int.to_bytes(y | ((x & 1) << 255), 32, "little") - - -def point_decompress(s): - if len(s) != 32: - raise Exception("Invalid input length for decompression") - y = int.from_bytes(s, "little") - sign = y >> 255 - y &= (1 << 255) - 1 - - - -Josefsson & Moeller Expires August 26, 2015 [Page 21] - - -Internet-Draft EdDSA & Ed25519 February 2015 - - - x = recover_x(y, sign) - if x is None: - return None - else: - return (x, y, 1, x*y % p) - - -def secret_expand(secret): - if len(secret) != 32: - raise Exception("Bad size of private key") - h = sha512(secret) - a = int.from_bytes(h[:32], "little") - a &= (1 << 254) - 8 - a |= (1 << 254) - return (a, h[32:]) - - -def secret_to_public(secret): - (a, dummy) = secret_expand(secret) - return point_compress(point_mul(a, G)) - - -def sign(secret, msg): - a, prefix = secret_expand(secret) - A = point_compress(point_mul(a, G)) - r = sha512_modq(prefix + msg) - R = point_mul(r, G) - Rs = point_compress(R) - h = sha512_modq(Rs + A + msg) - s = (r + h * a) % q - return Rs + int.to_bytes(s, 32, "little") - - -def verify(public, msg, signature): - if len(public) != 32: - raise Exception("Bad public-key length") - if len(signature) != 64: - Exception("Bad signature length") - A = point_decompress(public) - if not A: - return False - Rs = signature[:32] - R = point_decompress(Rs) - if not R: - return False - s = int.from_bytes(signature[32:], "little") - h = sha512_modq(Rs + public + msg) - sB = point_mul(s, G) - - - -Josefsson & Moeller Expires August 26, 2015 [Page 22] - - -Internet-Draft EdDSA & Ed25519 February 2015 - - - hA = point_mul(h, A) - return point_equal(sB, point_add(R, hA)) - -Appendix B. Library driver - - - Below is a command-line tool that uses the library above to perform - computations, for interactive use or for self-checking. - - import sys - import binascii - - from ed25519 import * - - def point_valid(P): - zinv = modp_inv(P[2]) - x = P[0] * zinv % p - y = P[1] * zinv % p - assert (x*y - P[3]*zinv) % p == 0 - return (-x*x + y*y - 1 - d*x*x*y*y) % p == 0 - - assert point_valid(G) - Z = (0, 1, 1, 0) - assert point_valid(Z) - - assert point_equal(Z, point_add(Z, Z)) - assert point_equal(G, point_add(Z, G)) - assert point_equal(Z, point_mul(0, G)) - assert point_equal(G, point_mul(1, G)) - assert point_equal(point_add(G, G), point_mul(2, G)) - for i in range(0, 100): - assert point_valid(point_mul(i, G)) - assert point_equal(Z, point_mul(q, G)) - - def munge_string(s, pos, change): - return (s[:pos] + - int.to_bytes(s[pos] ^ change, 1, "little") + - s[pos+1:]) - - # Read a file in the format of - # http://ed25519.cr.yp.to/python/sign.input - lineno = 0 - while True: - line = sys.stdin.readline() - if not line: - break - lineno = lineno + 1 - print(lineno) - fields = line.split(":") - - - -Josefsson & Moeller Expires August 26, 2015 [Page 23] - - -Internet-Draft EdDSA & Ed25519 February 2015 - - - secret = (binascii.unhexlify(fields[0]))[:32] - public = binascii.unhexlify(fields[1]) - msg = binascii.unhexlify(fields[2]) - signature = binascii.unhexlify(fields[3])[:64] - - assert public == secret_to_public(secret) - assert signature == sign(secret, msg) - assert verify(public, msg, signature) - if len(msg) == 0: - bad_msg = b"x" - else: - bad_msg = munge_string(msg, len(msg) // 3, 4) - assert not verify(public, bad_msg, signature) - bad_signature = munge_string(signature, 20, 8) - assert not verify(public, msg, bad_signature) - bad_signature = munge_string(signature, 40, 16) - assert not verify(public, msg, bad_signature) - -Authors' Addresses - - Simon Josefsson - SJD AB - - Email: simon@josefsson.org - URI: http://josefsson.org/ - - - Niels Moeller - - Email: nisse@lysator.liu.se - - - - - - - - - - - - - - - - - - - - - -Josefsson & Moeller Expires August 26, 2015 [Page 24] - - - -Html markup produced by rfcmarkup 1.113, available from https://tools.ietf.org/tools/rfcmarkup/ +Note: This draft is now superseded by https://datatracker.ietf.org/doc/rfc8032/ +(review of the differences is left as an exercise for the reader) From 5fd83c13fbf8dc304909345e60a853c15b0de1e5 Mon Sep 17 00:00:00 2001 From: SChernykh Date: Fri, 3 Aug 2018 11:41:41 +0200 Subject: [PATCH 0318/1404] Cryptonight variant 2 Contains two modifications to improve ASIC resistance: shuffle and integer math. Shuffle makes use of the whole 64-byte cache line instead of 16 bytes only, making Cryptonight 4 times more demanding for memory bandwidth. Integer math adds 64:32 bit integer division followed by 64 bit integer square root, adding large and unavoidable computational latency to the main loop. More details and performance numbers: https://github.com/SChernykh/xmr-stak-cpu/blob/master/README.md --- src/crypto/slow-hash.c | 252 ++++++++++++++++++++++++++------- src/crypto/variant2_int_sqrt.h | 163 +++++++++++++++++++++ tests/hash/CMakeLists.txt | 6 +- tests/hash/main.cpp | 202 +++++++++++++++++++++++++- tests/hash/tests-slow-2.txt | 10 ++ 5 files changed, 577 insertions(+), 56 deletions(-) create mode 100644 src/crypto/variant2_int_sqrt.h create mode 100644 tests/hash/tests-slow-2.txt diff --git a/src/crypto/slow-hash.c b/src/crypto/slow-hash.c index 9d4fc0dfa12..a4d2b58de89 100644 --- a/src/crypto/slow-hash.c +++ b/src/crypto/slow-hash.c @@ -38,6 +38,7 @@ #include "common/int-util.h" #include "hash-ops.h" #include "oaes_lib.h" +#include "variant2_int_sqrt.h" #define MEMORY (1 << 21) // 2MB scratchpad #define ITER (1 << 20) @@ -50,7 +51,7 @@ extern int aesb_single_round(const uint8_t *in, uint8_t*out, const uint8_t *expa extern int aesb_pseudo_round(const uint8_t *in, uint8_t *out, const uint8_t *expandedKey); #define VARIANT1_1(p) \ - do if (variant > 0) \ + do if (variant == 1) \ { \ const uint8_t tmp = ((const uint8_t*)(p))[11]; \ static const uint32_t table = 0x75310; \ @@ -59,7 +60,7 @@ extern int aesb_pseudo_round(const uint8_t *in, uint8_t *out, const uint8_t *exp } while(0) #define VARIANT1_2(p) \ - do if (variant > 0) \ + do if (variant == 1) \ { \ xor64(p, tweak1_2); \ } while(0) @@ -67,7 +68,7 @@ extern int aesb_pseudo_round(const uint8_t *in, uint8_t *out, const uint8_t *exp #define VARIANT1_CHECK() \ do if (length < 43) \ { \ - fprintf(stderr, "Cryptonight variants need at least 43 bytes of data"); \ + fprintf(stderr, "Cryptonight variant 1 needs at least 43 bytes of data"); \ _exit(1); \ } while(0) @@ -75,7 +76,7 @@ extern int aesb_pseudo_round(const uint8_t *in, uint8_t *out, const uint8_t *exp #define VARIANT1_PORTABLE_INIT() \ uint8_t tweak1_2[8]; \ - do if (variant > 0) \ + do if (variant == 1) \ { \ VARIANT1_CHECK(); \ memcpy(&tweak1_2, &state.hs.b[192], sizeof(tweak1_2)); \ @@ -83,11 +84,119 @@ extern int aesb_pseudo_round(const uint8_t *in, uint8_t *out, const uint8_t *exp } while(0) #define VARIANT1_INIT64() \ - if (variant > 0) \ + if (variant == 1) \ { \ VARIANT1_CHECK(); \ } \ - const uint64_t tweak1_2 = variant > 0 ? (state.hs.w[24] ^ (*((const uint64_t*)NONCE_POINTER))) : 0 + const uint64_t tweak1_2 = (variant == 1) ? (state.hs.w[24] ^ (*((const uint64_t*)NONCE_POINTER))) : 0 + +#define VARIANT2_INIT64() \ + uint64_t division_result = 0; \ + uint64_t sqrt_result = 0; \ + do if (variant >= 2) \ + { \ + U64(b)[2] = state.hs.w[8] ^ state.hs.w[10]; \ + U64(b)[3] = state.hs.w[9] ^ state.hs.w[11]; \ + division_result = state.hs.w[12]; \ + sqrt_result = state.hs.w[13]; \ + } while (0) + +#define VARIANT2_PORTABLE_INIT() \ + uint64_t division_result = 0; \ + uint64_t sqrt_result = 0; \ + do if (variant >= 2) \ + { \ + memcpy(b + AES_BLOCK_SIZE, state.hs.b + 64, AES_BLOCK_SIZE); \ + xor64(b + AES_BLOCK_SIZE, state.hs.b + 80); \ + xor64(b + AES_BLOCK_SIZE + 8, state.hs.b + 88); \ + division_result = state.hs.w[12]; \ + sqrt_result = state.hs.w[13]; \ + } while (0) + +#define VARIANT2_SHUFFLE_ADD_SSE2(base_ptr, offset) \ + do if (variant >= 2) \ + { \ + const __m128i chunk1 = _mm_load_si128((__m128i *)((base_ptr) + ((offset) ^ 0x10))); \ + const __m128i chunk2 = _mm_load_si128((__m128i *)((base_ptr) + ((offset) ^ 0x20))); \ + const __m128i chunk3 = _mm_load_si128((__m128i *)((base_ptr) + ((offset) ^ 0x30))); \ + _mm_store_si128((__m128i *)((base_ptr) + ((offset) ^ 0x10)), _mm_add_epi64(chunk3, _b1)); \ + _mm_store_si128((__m128i *)((base_ptr) + ((offset) ^ 0x20)), _mm_add_epi64(chunk1, _b)); \ + _mm_store_si128((__m128i *)((base_ptr) + ((offset) ^ 0x30)), _mm_add_epi64(chunk2, _a)); \ + } while (0) + +#define VARIANT2_SHUFFLE_ADD_NEON(base_ptr, offset) \ + do if (variant >= 2) \ + { \ + const uint64x2_t chunk1 = vld1q_u64(U64((base_ptr) + ((offset) ^ 0x10))); \ + const uint64x2_t chunk2 = vld1q_u64(U64((base_ptr) + ((offset) ^ 0x20))); \ + const uint64x2_t chunk3 = vld1q_u64(U64((base_ptr) + ((offset) ^ 0x30))); \ + vst1q_u64(U64((base_ptr) + ((offset) ^ 0x10)), vaddq_u64(chunk3, vreinterpretq_u64_u8(_b1))); \ + vst1q_u64(U64((base_ptr) + ((offset) ^ 0x20)), vaddq_u64(chunk1, vreinterpretq_u64_u8(_b))); \ + vst1q_u64(U64((base_ptr) + ((offset) ^ 0x30)), vaddq_u64(chunk2, vreinterpretq_u64_u8(_a))); \ + } while (0) + +#define VARIANT2_PORTABLE_SHUFFLE_ADD(base_ptr, offset) \ + do if (variant >= 2) \ + { \ + uint64_t* chunk1 = U64((base_ptr) + ((offset) ^ 0x10)); \ + uint64_t* chunk2 = U64((base_ptr) + ((offset) ^ 0x20)); \ + uint64_t* chunk3 = U64((base_ptr) + ((offset) ^ 0x30)); \ + \ + const uint64_t chunk1_old[2] = { chunk1[0], chunk1[1] }; \ + \ + uint64_t b1[2]; \ + memcpy(b1, b + 16, 16); \ + chunk1[0] = chunk3[0] + b1[0]; \ + chunk1[1] = chunk3[1] + b1[1]; \ + \ + uint64_t a0[2]; \ + memcpy(a0, a, 16); \ + chunk3[0] = chunk2[0] + a0[0]; \ + chunk3[1] = chunk2[1] + a0[1]; \ + \ + uint64_t b0[2]; \ + memcpy(b0, b, 16); \ + chunk2[0] = chunk1_old[0] + b0[0]; \ + chunk2[1] = chunk1_old[1] + b0[1]; \ + } while (0) + +#define VARIANT2_INTEGER_MATH_DIVISION_STEP(b, ptr) \ + ((uint64_t*)(b))[0] ^= division_result ^ (sqrt_result << 32); \ + { \ + const uint64_t dividend = ((uint64_t*)(ptr))[1]; \ + const uint32_t divisor = (((uint64_t*)(ptr))[0] + (uint32_t)(sqrt_result << 1)) | 0x80000001UL; \ + division_result = ((uint32_t)(dividend / divisor)) + \ + (((uint64_t)(dividend % divisor)) << 32); \ + } \ + const uint64_t sqrt_input = ((uint64_t*)(ptr))[0] + division_result + +#define VARIANT2_INTEGER_MATH_SSE2(b, ptr) \ + do if (variant >= 2) \ + { \ + VARIANT2_INTEGER_MATH_DIVISION_STEP(b, ptr); \ + VARIANT2_INTEGER_MATH_SQRT_STEP_SSE2(); \ + VARIANT2_INTEGER_MATH_SQRT_FIXUP(sqrt_result); \ + } while(0) + +#if defined DBL_MANT_DIG && (DBL_MANT_DIG >= 50) + // double precision floating point type has enough bits of precision on current platform + #define VARIANT2_PORTABLE_INTEGER_MATH(b, ptr) \ + do if (variant >= 2) \ + { \ + VARIANT2_INTEGER_MATH_DIVISION_STEP(b, ptr); \ + VARIANT2_INTEGER_MATH_SQRT_STEP_FP64(); \ + VARIANT2_INTEGER_MATH_SQRT_FIXUP(sqrt_result); \ + } while (0) +#else + // double precision floating point type is not good enough on current platform + // fall back to the reference code (integer only) + #define VARIANT2_PORTABLE_INTEGER_MATH(b, ptr) \ + do if (variant >= 2) \ + { \ + VARIANT2_INTEGER_MATH_DIVISION_STEP(b, ptr); \ + VARIANT2_INTEGER_MATH_SQRT_STEP_REF(); \ + } while (0) +#endif #if !defined NO_AES && (defined(__x86_64__) || (defined(_MSC_VER) && defined(_WIN64))) // Optimised code below, uses x86-specific intrinsics, SSE2, AES-NI @@ -164,19 +273,22 @@ extern int aesb_pseudo_round(const uint8_t *in, uint8_t *out, const uint8_t *exp * This code is based upon an optimized implementation by dga. */ #define post_aes() \ + VARIANT2_SHUFFLE_ADD_SSE2(hp_state, j); \ _mm_store_si128(R128(c), _c); \ - _b = _mm_xor_si128(_b, _c); \ - _mm_store_si128(R128(&hp_state[j]), _b); \ + _mm_store_si128(R128(&hp_state[j]), _mm_xor_si128(_b, _c)); \ VARIANT1_1(&hp_state[j]); \ j = state_index(c); \ p = U64(&hp_state[j]); \ b[0] = p[0]; b[1] = p[1]; \ + VARIANT2_INTEGER_MATH_SSE2(b, c); \ __mul(); \ + VARIANT2_SHUFFLE_ADD_SSE2(hp_state, j); \ a[0] += hi; a[1] += lo; \ p = U64(&hp_state[j]); \ p[0] = a[0]; p[1] = a[1]; \ a[0] ^= b[0]; a[1] ^= b[1]; \ VARIANT1_2(p + 1); \ + _b1 = _b; \ _b = _c; \ #if defined(_MSC_VER) @@ -570,10 +682,10 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int uint8_t text[INIT_SIZE_BYTE]; RDATA_ALIGN16 uint64_t a[2]; - RDATA_ALIGN16 uint64_t b[2]; + RDATA_ALIGN16 uint64_t b[4]; RDATA_ALIGN16 uint64_t c[2]; union cn_slow_hash_state state; - __m128i _a, _b, _c; + __m128i _a, _b, _b1, _c; uint64_t hi, lo; size_t i, j; @@ -599,6 +711,7 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int memcpy(text, state.init, INIT_SIZE_BYTE); VARIANT1_INIT64(); + VARIANT2_INIT64(); /* CryptoNight Step 2: Iteratively encrypt the results from Keccak to fill * the 2MB large random access buffer. @@ -637,6 +750,7 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int */ _b = _mm_load_si128(R128(b)); + _b1 = _mm_load_si128(R128(b) + 1); // Two independent versions, one with AES, one without, to ensure that // the useAes test is only performed once, not every iteration. if(useAes) @@ -761,19 +875,22 @@ union cn_slow_hash_state _a = vld1q_u8((const uint8_t *)a); \ #define post_aes() \ + VARIANT2_SHUFFLE_ADD_NEON(hp_state, j); \ vst1q_u8((uint8_t *)c, _c); \ - _b = veorq_u8(_b, _c); \ - vst1q_u8(&hp_state[j], _b); \ + vst1q_u8(&hp_state[j], veorq_u8(_b, _c)); \ VARIANT1_1(&hp_state[j]); \ j = state_index(c); \ p = U64(&hp_state[j]); \ b[0] = p[0]; b[1] = p[1]; \ + VARIANT2_PORTABLE_INTEGER_MATH(b, c); \ __mul(); \ + VARIANT2_SHUFFLE_ADD_NEON(hp_state, j); \ a[0] += hi; a[1] += lo; \ p = U64(&hp_state[j]); \ p[0] = a[0]; p[1] = a[1]; \ a[0] ^= b[0]; a[1] ^= b[1]; \ VARIANT1_2(p + 1); \ + _b1 = _b; \ _b = _c; \ @@ -912,10 +1029,10 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int uint8_t text[INIT_SIZE_BYTE]; RDATA_ALIGN16 uint64_t a[2]; - RDATA_ALIGN16 uint64_t b[2]; + RDATA_ALIGN16 uint64_t b[4]; RDATA_ALIGN16 uint64_t c[2]; union cn_slow_hash_state state; - uint8x16_t _a, _b, _c, zero = {0}; + uint8x16_t _a, _b, _b1, _c, zero = {0}; uint64_t hi, lo; size_t i, j; @@ -936,6 +1053,7 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int memcpy(text, state.init, INIT_SIZE_BYTE); VARIANT1_INIT64(); + VARIANT2_INIT64(); /* CryptoNight Step 2: Iteratively encrypt the results from Keccak to fill * the 2MB large random access buffer. @@ -959,7 +1077,7 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int */ _b = vld1q_u8((const uint8_t *)b); - + _b1 = vld1q_u8(((const uint8_t *)b) + AES_BLOCK_SIZE); for(i = 0; i < ITER / 2; i++) { @@ -1075,6 +1193,11 @@ __asm__ __volatile__( #endif /* !aarch64 */ #endif // NO_OPTIMIZED_MULTIPLY_ON_ARM +STATIC INLINE void copy_block(uint8_t* dst, const uint8_t* src) +{ + memcpy(dst, src, AES_BLOCK_SIZE); +} + STATIC INLINE void sum_half_blocks(uint8_t* a, const uint8_t* b) { uint64_t a0, a1, b0, b1; @@ -1109,7 +1232,9 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int { uint8_t text[INIT_SIZE_BYTE]; uint8_t a[AES_BLOCK_SIZE]; - uint8_t b[AES_BLOCK_SIZE]; + uint8_t b[AES_BLOCK_SIZE * 2]; + uint8_t c[AES_BLOCK_SIZE]; + uint8_t c1[AES_BLOCK_SIZE]; uint8_t d[AES_BLOCK_SIZE]; uint8_t aes_key[AES_KEY_SIZE]; RDATA_ALIGN16 uint8_t expandedKey[256]; @@ -1138,11 +1263,12 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int } memcpy(text, state.init, INIT_SIZE_BYTE); - VARIANT1_INIT64(); - aes_ctx = (oaes_ctx *) oaes_alloc(); oaes_key_import_data(aes_ctx, state.hs.b, AES_KEY_SIZE); + VARIANT1_INIT64(); + VARIANT2_INIT64(); + // use aligned data memcpy(expandedKey, aes_ctx->key->exp_data, aes_ctx->key->exp_data_len); for(i = 0; i < MEMORY / INIT_SIZE_BYTE; i++) @@ -1163,23 +1289,33 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int #define state_index(x) ((*(uint32_t *) x) & MASK) // Iteration 1 - p = &long_state[state_index(a)]; + j = state_index(a); + p = &long_state[j]; aesb_single_round(p, p, a); + copy_block(c1, p); - xor_blocks(b, p); - swap_blocks(b, p); - swap_blocks(a, b); + VARIANT2_PORTABLE_SHUFFLE_ADD(long_state, j); + xor_blocks(p, b); VARIANT1_1(p); // Iteration 2 - p = &long_state[state_index(a)]; - - mul(a, p, d); - sum_half_blocks(b, d); - swap_blocks(b, p); - xor_blocks(b, p); - swap_blocks(a, b); - VARIANT1_2(U64(p) + 1); + j = state_index(c1); + p = &long_state[j]; + copy_block(c, p); + + VARIANT2_PORTABLE_INTEGER_MATH(c, c1); + mul(c1, c, d); + VARIANT2_PORTABLE_SHUFFLE_ADD(long_state, j); + sum_half_blocks(a, d); + swap_blocks(a, c); + xor_blocks(a, c); + VARIANT1_2(U64(c) + 1); + copy_block(p, c); + + if (variant >= 2) { + copy_block(b + AES_BLOCK_SIZE, b); + } + copy_block(b, c1); } memcpy(text, state.init, INIT_SIZE_BYTE); @@ -1298,8 +1434,9 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int union cn_slow_hash_state state; uint8_t text[INIT_SIZE_BYTE]; uint8_t a[AES_BLOCK_SIZE]; - uint8_t b[AES_BLOCK_SIZE]; - uint8_t c[AES_BLOCK_SIZE]; + uint8_t b[AES_BLOCK_SIZE * 2]; + uint8_t c1[AES_BLOCK_SIZE]; + uint8_t c2[AES_BLOCK_SIZE]; uint8_t d[AES_BLOCK_SIZE]; size_t i, j; uint8_t aes_key[AES_KEY_SIZE]; @@ -1315,6 +1452,7 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int aes_ctx = (oaes_ctx *) oaes_alloc(); VARIANT1_PORTABLE_INIT(); + VARIANT2_PORTABLE_INIT(); oaes_key_import_data(aes_ctx, aes_key, AES_KEY_SIZE); for (i = 0; i < MEMORY / INIT_SIZE_BYTE; i++) { @@ -1324,9 +1462,9 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int memcpy(&long_state[i * INIT_SIZE_BYTE], text, INIT_SIZE_BYTE); } - for (i = 0; i < 16; i++) { - a[i] = state.k[ i] ^ state.k[32 + i]; - b[i] = state.k[16 + i] ^ state.k[48 + i]; + for (i = 0; i < AES_BLOCK_SIZE; i++) { + a[i] = state.k[ i] ^ state.k[AES_BLOCK_SIZE * 2 + i]; + b[i] = state.k[AES_BLOCK_SIZE + i] ^ state.k[AES_BLOCK_SIZE * 3 + i]; } for (i = 0; i < ITER / 2; i++) { @@ -1335,26 +1473,32 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int * next address <-+ */ /* Iteration 1 */ - j = e2i(a, MEMORY / AES_BLOCK_SIZE); - copy_block(c, &long_state[j * AES_BLOCK_SIZE]); - aesb_single_round(c, c, a); - xor_blocks(b, c); - swap_blocks(b, c); - copy_block(&long_state[j * AES_BLOCK_SIZE], c); - assert(j == e2i(a, MEMORY / AES_BLOCK_SIZE)); - swap_blocks(a, b); - VARIANT1_1(&long_state[j * AES_BLOCK_SIZE]); + j = e2i(a, MEMORY / AES_BLOCK_SIZE) * AES_BLOCK_SIZE; + copy_block(c1, &long_state[j]); + aesb_single_round(c1, c1, a); + VARIANT2_PORTABLE_SHUFFLE_ADD(long_state, j); + copy_block(&long_state[j], c1); + xor_blocks(&long_state[j], b); + assert(j == e2i(a, MEMORY / AES_BLOCK_SIZE) * AES_BLOCK_SIZE); + VARIANT1_1(&long_state[j]); /* Iteration 2 */ - j = e2i(a, MEMORY / AES_BLOCK_SIZE); - copy_block(c, &long_state[j * AES_BLOCK_SIZE]); - mul(a, c, d); - sum_half_blocks(b, d); - swap_blocks(b, c); - xor_blocks(b, c); - VARIANT1_2(c + 8); - copy_block(&long_state[j * AES_BLOCK_SIZE], c); - assert(j == e2i(a, MEMORY / AES_BLOCK_SIZE)); - swap_blocks(a, b); + j = e2i(c1, MEMORY / AES_BLOCK_SIZE) * AES_BLOCK_SIZE; + copy_block(c2, &long_state[j]); + VARIANT2_PORTABLE_INTEGER_MATH(c2, c1); + mul(c1, c2, d); + VARIANT2_PORTABLE_SHUFFLE_ADD(long_state, j); + swap_blocks(a, c1); + sum_half_blocks(c1, d); + swap_blocks(c1, c2); + xor_blocks(c1, c2); + VARIANT1_2(c2 + 8); + copy_block(&long_state[j], c2); + assert(j == e2i(a, MEMORY / AES_BLOCK_SIZE) * AES_BLOCK_SIZE); + if (variant >= 2) { + copy_block(b + AES_BLOCK_SIZE, b); + } + copy_block(b, a); + copy_block(a, c1); } memcpy(text, state.init, INIT_SIZE_BYTE); diff --git a/src/crypto/variant2_int_sqrt.h b/src/crypto/variant2_int_sqrt.h new file mode 100644 index 00000000000..b405bb798dd --- /dev/null +++ b/src/crypto/variant2_int_sqrt.h @@ -0,0 +1,163 @@ +#ifndef VARIANT2_INT_SQRT_H +#define VARIANT2_INT_SQRT_H + +#include +#include + +#define VARIANT2_INTEGER_MATH_SQRT_STEP_SSE2() \ + do { \ + const __m128i exp_double_bias = _mm_set_epi64x(0, 1023ULL << 52); \ + __m128d x = _mm_castsi128_pd(_mm_add_epi64(_mm_cvtsi64_si128(sqrt_input >> 12), exp_double_bias)); \ + x = _mm_sqrt_sd(_mm_setzero_pd(), x); \ + sqrt_result = (uint64_t)(_mm_cvtsi128_si64(_mm_sub_epi64(_mm_castpd_si128(x), exp_double_bias))) >> 19; \ + } while(0) + +#define VARIANT2_INTEGER_MATH_SQRT_STEP_FP64() \ + do { \ + sqrt_result = sqrt(sqrt_input + 18446744073709551616.0) * 2.0 - 8589934592.0; \ + } while(0) + +#define VARIANT2_INTEGER_MATH_SQRT_STEP_REF() \ + sqrt_result = integer_square_root_v2(sqrt_input) + +// Reference implementation of the integer square root for Cryptonight variant 2 +// Computes integer part of "sqrt(2^64 + n) * 2 - 2^33" +// +// In other words, given 64-bit unsigned integer n: +// 1) Write it as x = 1.NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN000... in binary (1 <= x < 2, all 64 bits of n are used) +// 2) Calculate sqrt(x) = 1.0RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR... (1 <= sqrt(x) < sqrt(2), so it will always start with "1.0" in binary) +// 3) Take 32 bits that come after "1.0" and return them as a 32-bit unsigned integer, discard all remaining bits +// +// Some sample inputs and outputs: +// +// Input | Output | Exact value of "sqrt(2^64 + n) * 2 - 2^33" +// -----------------|------------|------------------------------------------- +// 0 | 0 | 0 +// 2^32 | 0 | 0.99999999994179233909330885695244... +// 2^32 + 1 | 1 | 1.0000000001746229827200734316305... +// 2^50 | 262140 | 262140.00012206565608606978175873... +// 2^55 + 20963331 | 8384515 | 8384515.9999999997673963974959744... +// 2^55 + 20963332 | 8384516 | 8384516 +// 2^62 + 26599786 | 1013904242 | 1013904242.9999999999479374853545... +// 2^62 + 26599787 | 1013904243 | 1013904243.0000000001561875439364... +// 2^64 - 1 | 3558067407 | 3558067407.9041987696409179931096... + +// The reference implementation as it is now uses only unsigned int64 arithmetic, so it can't have undefined behavior +// It was tested once for all edge cases and confirmed correct +static inline uint32_t integer_square_root_v2(uint64_t n) +{ + uint64_t r = 1ULL << 63; + + for (uint64_t bit = 1ULL << 60; bit; bit >>= 2) + { + const bool b = (n < r + bit); + const uint64_t n_next = n - (r + bit); + const uint64_t r_next = r + bit * 2; + n = b ? n : n_next; + r = b ? r : r_next; + r >>= 1; + } + + return r * 2 + ((n > r) ? 1 : 0); +} + +/* +VARIANT2_INTEGER_MATH_SQRT_FIXUP checks that "r" is an integer part of "sqrt(2^64 + sqrt_input) * 2 - 2^33" and adds or subtracts 1 if needed +It's hard to understand how it works, so here is a full calculation of formulas used in VARIANT2_INTEGER_MATH_SQRT_FIXUP + +The following inequalities must hold for r if it's an integer part of "sqrt(2^64 + sqrt_input) * 2 - 2^33": +1) r <= sqrt(2^64 + sqrt_input) * 2 - 2^33 +2) r + 1 > sqrt(2^64 + sqrt_input) * 2 - 2^33 + +We need to check them using only unsigned integer arithmetic to avoid rounding errors and undefined behavior + +First inequality: r <= sqrt(2^64 + sqrt_input) * 2 - 2^33 +----------------------------------------------------------------------------------- +r <= sqrt(2^64 + sqrt_input) * 2 - 2^33 +r + 2^33 <= sqrt(2^64 + sqrt_input) * 2 +r/2 + 2^32 <= sqrt(2^64 + sqrt_input) +(r/2 + 2^32)^2 <= 2^64 + sqrt_input + +Rewrite r as r = s * 2 + b (s = trunc(r/2), b is 0 or 1) + +((s*2+b)/2 + 2^32)^2 <= 2^64 + sqrt_input +(s*2+b)^2/4 + 2*2^32*(s*2+b)/2 + 2^64 <= 2^64 + sqrt_input +(s*2+b)^2/4 + 2*2^32*(s*2+b)/2 <= sqrt_input +(s*2+b)^2/4 + 2^32*r <= sqrt_input +(s^2*4+2*s*2*b+b^2)/4 + 2^32*r <= sqrt_input +s^2+s*b+b^2/4 + 2^32*r <= sqrt_input +s*(s+b) + b^2/4 + 2^32*r <= sqrt_input + +Let r2 = s*(s+b) + r*2^32 +r2 + b^2/4 <= sqrt_input + +If this inequality doesn't hold, then we must decrement r: IF "r2 + b^2/4 > sqrt_input" THEN r = r - 1 + +b can be 0 or 1 +If b is 0 then we need to compare "r2 > sqrt_input" +If b is 1 then b^2/4 = 0.25, so we need to compare "r2 + 0.25 > sqrt_input" +Since both r2 and sqrt_input are integers, we can safely replace it with "r2 + 1 > sqrt_input" +----------------------------------------------------------------------------------- +Both cases can be merged to a single expression "r2 + b > sqrt_input" +----------------------------------------------------------------------------------- +There will be no overflow when calculating "r2 + b", so it's safe to compare with sqrt_input: +r2 + b = s*(s+b) + r*2^32 + b +The largest value s, b and r can have is s = 1779033703, b = 1, r = 3558067407 when sqrt_input = 2^64 - 1 +r2 + b <= 1779033703*1779033704 + 3558067407*2^32 + 1 = 18446744068217447385 < 2^64 + +Second inequality: r + 1 > sqrt(2^64 + sqrt_input) * 2 - 2^33 +----------------------------------------------------------------------------------- +r + 1 > sqrt(2^64 + sqrt_input) * 2 - 2^33 +r + 1 + 2^33 > sqrt(2^64 + sqrt_input) * 2 +((r+1)/2 + 2^32)^2 > 2^64 + sqrt_input + +Rewrite r as r = s * 2 + b (s = trunc(r/2), b is 0 or 1) + +((s*2+b+1)/2 + 2^32)^2 > 2^64 + sqrt_input +(s*2+b+1)^2/4 + 2*(s*2+b+1)/2*2^32 + 2^64 > 2^64 + sqrt_input +(s*2+b+1)^2/4 + (s*2+b+1)*2^32 > sqrt_input +(s*2+b+1)^2/4 + (r+1)*2^32 > sqrt_input +(s*2+(b+1))^2/4 + r*2^32 + 2^32 > sqrt_input +(s^2*4+2*s*2*(b+1)+(b+1)^2)/4 + r*2^32 + 2^32 > sqrt_input +s^2+s*(b+1)+(b+1)^2/4 + r*2^32 + 2^32 > sqrt_input +s*(s+b) + s + (b+1)^2/4 + r*2^32 + 2^32 > sqrt_input + +Let r2 = s*(s+b) + r*2^32 + +r2 + s + (b+1)^2/4 + 2^32 > sqrt_input +r2 + 2^32 + (b+1)^2/4 > sqrt_input - s + +If this inequality doesn't hold, then we must decrement r: IF "r2 + 2^32 + (b+1)^2/4 <= sqrt_input - s" THEN r = r - 1 +b can be 0 or 1 +If b is 0 then we need to compare "r2 + 2^32 + 1/4 <= sqrt_input - s" which is equal to "r2 + 2^32 < sqrt_input - s" because all numbers here are integers +If b is 1 then (b+1)^2/4 = 1, so we need to compare "r2 + 2^32 + 1 <= sqrt_input - s" which is also equal to "r2 + 2^32 < sqrt_input - s" +----------------------------------------------------------------------------------- +Both cases can be merged to a single expression "r2 + 2^32 < sqrt_input - s" +----------------------------------------------------------------------------------- +There will be no overflow when calculating "r2 + 2^32": +r2 + 2^32 = s*(s+b) + r*2^32 + 2^32 = s*(s+b) + (r+1)*2^32 +The largest value s, b and r can have is s = 1779033703, b = 1, r = 3558067407 when sqrt_input = 2^64 - 1 +r2 + b <= 1779033703*1779033704 + 3558067408*2^32 = 18446744072512414680 < 2^64 + +There will be no integer overflow when calculating "sqrt_input - s", i.e. "sqrt_input >= s" at all times: +s = trunc(r/2) = trunc(sqrt(2^64 + sqrt_input) - 2^32) < sqrt(2^64 + sqrt_input) - 2^32 + 1 +sqrt_input > sqrt(2^64 + sqrt_input) - 2^32 + 1 +sqrt_input + 2^32 - 1 > sqrt(2^64 + sqrt_input) +(sqrt_input + 2^32 - 1)^2 > sqrt_input + 2^64 +sqrt_input^2 + 2*sqrt_input*(2^32 - 1) + (2^32-1)^2 > sqrt_input + 2^64 +sqrt_input^2 + sqrt_input*(2^33 - 2) + (2^32-1)^2 > sqrt_input + 2^64 +sqrt_input^2 + sqrt_input*(2^33 - 3) + (2^32-1)^2 > 2^64 +sqrt_input^2 + sqrt_input*(2^33 - 3) + 2^64-2^33+1 > 2^64 +sqrt_input^2 + sqrt_input*(2^33 - 3) - 2^33 + 1 > 0 +This inequality is true if sqrt_input > 1 and it's easy to check that s = 0 if sqrt_input is 0 or 1, so there will be no integer overflow +*/ + +#define VARIANT2_INTEGER_MATH_SQRT_FIXUP(r) \ + do { \ + const uint64_t s = r >> 1; \ + const uint64_t b = r & 1; \ + const uint64_t r2 = (uint64_t)(s) * (s + b) + (r << 32); \ + r += ((r2 + b > sqrt_input) ? -1 : 0) + ((r2 + (1ULL << 32) < sqrt_input - s) ? 1 : 0); \ + } while(0) + +#endif diff --git a/tests/hash/CMakeLists.txt b/tests/hash/CMakeLists.txt index 92abeca2040..433cf94e9a1 100644 --- a/tests/hash/CMakeLists.txt +++ b/tests/hash/CMakeLists.txt @@ -43,8 +43,12 @@ set_property(TARGET hash-tests PROPERTY FOLDER "tests") -foreach (hash IN ITEMS fast slow slow-1 tree extra-blake extra-groestl extra-jh extra-skein) +foreach (hash IN ITEMS fast slow slow-1 slow-2 tree extra-blake extra-groestl extra-jh extra-skein) add_test( NAME "hash-${hash}" COMMAND hash-tests "${hash}" "${CMAKE_CURRENT_SOURCE_DIR}/tests-${hash}.txt") endforeach () + +add_test( + NAME "hash-variant2-int-sqrt" + COMMAND hash-tests "variant2_int_sqrt") diff --git a/tests/hash/main.cpp b/tests/hash/main.cpp index cc5b9ba664b..7767d0d3bb4 100644 --- a/tests/hash/main.cpp +++ b/tests/hash/main.cpp @@ -33,9 +33,11 @@ #include #include #include +#include #include "warnings.h" #include "crypto/hash.h" +#include "crypto/variant2_int_sqrt.h" #include "../io.h" using namespace std; @@ -57,6 +59,9 @@ extern "C" { static void cn_slow_hash_1(const void *data, size_t length, char *hash) { return cn_slow_hash(data, length, hash, 1/*variant*/, 0/*prehashed*/); } + static void cn_slow_hash_2(const void *data, size_t length, char *hash) { + return cn_slow_hash(data, length, hash, 2/*variant*/, 0/*prehashed*/); + } } POP_WARNINGS @@ -67,7 +72,10 @@ struct hash_func { } hashes[] = {{"fast", cn_fast_hash}, {"slow", cn_slow_hash_0}, {"tree", hash_tree}, {"extra-blake", hash_extra_blake}, {"extra-groestl", hash_extra_groestl}, {"extra-jh", hash_extra_jh}, {"extra-skein", hash_extra_skein}, - {"slow-1", cn_slow_hash_1}}; + {"slow-1", cn_slow_hash_1}, {"slow-2", cn_slow_hash_2}}; + +int test_variant2_int_sqrt(); +int test_variant2_int_sqrt_ref(); int main(int argc, char *argv[]) { hash_f *f; @@ -78,6 +86,36 @@ int main(int argc, char *argv[]) { size_t test = 0; bool error = false; if (argc != 3) { + if ((argc == 2) && (strcmp(argv[1], "variant2_int_sqrt") == 0)) { + if (test_variant2_int_sqrt_ref() != 0) { + return 1; + } + const int round_modes[3] = { FE_DOWNWARD, FE_TONEAREST, FE_UPWARD }; + for (int i = 0; i < 3; ++i) { + std::fesetround(round_modes[i]); + const int result = test_variant2_int_sqrt(); + if (result != 0) { + cerr << "FPU round mode was set to "; + switch (round_modes[i]) { + case FE_DOWNWARD: + cerr << "FE_DOWNWARD"; + break; + case FE_TONEAREST: + cerr << "FE_TONEAREST"; + break; + case FE_UPWARD: + cerr << "FE_UPWARD"; + break; + default: + cerr << "unknown"; + break; + } + cerr << endl; + return result; + } + } + return 0; + } cerr << "Wrong number of arguments" << endl; return 1; } @@ -127,3 +165,165 @@ int main(int argc, char *argv[]) { } return error ? 1 : 0; } + +#if defined(__x86_64__) || (defined(_MSC_VER) && defined(_WIN64)) + +#include + +#if defined(_MSC_VER) || defined(__MINGW32__) + #include +#else + #include +#endif + +#endif + +static inline bool test_variant2_int_sqrt_sse(const uint64_t sqrt_input, const uint64_t correct_result) +{ +#if defined(__x86_64__) || (defined(_MSC_VER) && defined(_WIN64)) + uint64_t sqrt_result; + VARIANT2_INTEGER_MATH_SQRT_STEP_SSE2(); + VARIANT2_INTEGER_MATH_SQRT_FIXUP(sqrt_result); + if (sqrt_result != correct_result) { + cerr << "Integer sqrt (SSE2 version) returned incorrect result for N = " << sqrt_input << endl; + cerr << "Expected result: " << correct_result << endl; + cerr << "Returned result: " << sqrt_result << endl; + return false; + } +#endif + + return true; +} + +static inline bool test_variant2_int_sqrt_fp64(const uint64_t sqrt_input, const uint64_t correct_result) +{ +#if defined DBL_MANT_DIG && (DBL_MANT_DIG >= 50) + uint64_t sqrt_result; + VARIANT2_INTEGER_MATH_SQRT_STEP_FP64(); + VARIANT2_INTEGER_MATH_SQRT_FIXUP(sqrt_result); + if (sqrt_result != correct_result) { + cerr << "Integer sqrt (FP64 version) returned incorrect result for N = " << sqrt_input << endl; + cerr << "Expected result: " << correct_result << endl; + cerr << "Returned result: " << sqrt_result << endl; + return false; + } +#endif + + return true; +} + +static inline bool test_variant2_int_sqrt_ref(const uint64_t sqrt_input, const uint64_t correct_result) +{ + uint64_t sqrt_result; + VARIANT2_INTEGER_MATH_SQRT_STEP_REF(); + if (sqrt_result != correct_result) { + cerr << "Integer sqrt (reference version) returned incorrect result for N = " << sqrt_input << endl; + cerr << "Expected result: " << correct_result << endl; + cerr << "Returned result: " << sqrt_result << endl; + return false; + } + + return true; +} + +static inline bool test_variant2_int_sqrt(const uint64_t sqrt_input, const uint64_t correct_result) +{ + if (!test_variant2_int_sqrt_sse(sqrt_input, correct_result)) { + return false; + } + if (!test_variant2_int_sqrt_fp64(sqrt_input, correct_result)) { + return false; + } + + return true; +} + +int test_variant2_int_sqrt() +{ + if (!test_variant2_int_sqrt(0, 0)) { + return 1; + } + if (!test_variant2_int_sqrt(1ULL << 63, 1930543745UL)) { + return 1; + } + if (!test_variant2_int_sqrt(uint64_t(-1), 3558067407UL)) { + return 1; + } + + for (uint64_t i = 1; i <= 3558067407UL; ++i) { + // "i" is integer part of "sqrt(2^64 + n) * 2 - 2^33" + // n = (i/2 + 2^32)^2 - 2^64 + + const uint64_t i0 = i >> 1; + uint64_t n1; + if ((i & 1) == 0) { + // n = (i/2 + 2^32)^2 - 2^64 + // n = i^2/4 + 2*2^32*i/2 + 2^64 - 2^64 + // n = i^2/4 + 2^32*i + // i is even, so i^2 is divisible by 4: + // n = (i^2 >> 2) + (i << 32) + + // int_sqrt_v2(i^2/4 + 2^32*i - 1) must be equal to i - 1 + // int_sqrt_v2(i^2/4 + 2^32*i) must be equal to i + n1 = i0 * i0 + (i << 32) - 1; + } + else { + // n = (i/2 + 2^32)^2 - 2^64 + // n = i^2/4 + 2*2^32*i/2 + 2^64 - 2^64 + // n = i^2/4 + 2^32*i + // i is odd, so i = i0*2+1 (i0 = i >> 1) + // n = (i0*2+1)^2/4 + 2^32*i + // n = (i0^2*4+i0*4+1)/4 + 2^32*i + // n = i0^2+i0+1/4 + 2^32*i + // i0^2+i0 + 2^32*i < n < i0^2+i0+1 + 2^32*i + + // int_sqrt_v2(i0^2+i0 + 2^32*i) must be equal to i - 1 + // int_sqrt_v2(i0^2+i0+1 + 2^32*i) must be equal to i + n1 = i0 * i0 + i0 + (i << 32); + } + + if (!test_variant2_int_sqrt(n1, i - 1)) { + return 1; + } + if (!test_variant2_int_sqrt(n1 + 1, i)) { + return 1; + } + } + + return 0; +} + +int test_variant2_int_sqrt_ref() +{ + if (!test_variant2_int_sqrt_ref(0, 0)) { + return 1; + } + if (!test_variant2_int_sqrt_ref(1ULL << 63, 1930543745UL)) { + return 1; + } + if (!test_variant2_int_sqrt_ref(uint64_t(-1), 3558067407UL)) { + return 1; + } + + // Reference version is slow, so we test only every 83th edge case + // "i += 83" because 1 + 83 * 42868282 = 3558067407 + for (uint64_t i = 1; i <= 3558067407UL; i += 83) { + const uint64_t i0 = i >> 1; + uint64_t n1; + if ((i & 1) == 0) { + n1 = i0 * i0 + (i << 32) - 1; + } + else { + n1 = i0 * i0 + i0 + (i << 32); + } + + if (!test_variant2_int_sqrt_ref(n1, i - 1)) { + return 1; + } + if (!test_variant2_int_sqrt_ref(n1 + 1, i)) { + return 1; + } + } + + return 0; +} diff --git a/tests/hash/tests-slow-2.txt b/tests/hash/tests-slow-2.txt new file mode 100644 index 00000000000..8f90d05c9f9 --- /dev/null +++ b/tests/hash/tests-slow-2.txt @@ -0,0 +1,10 @@ +4cf1ff9ca46eb433b36cd9f70e02b14cc06bfd18ca77fa9ccaafd1fd96c674b0 5468697320697320612074657374205468697320697320612074657374205468697320697320612074657374 +7d292e43f4751714ec07dbcb0e4bbffe2a7afb6066420960684ff57d7474c871 4c6f72656d20697073756d20646f6c6f722073697420616d65742c20636f6e73656374657475722061646970697363696e67 +335563425256edebf1d92dc342369c2f4770ebb4112ba975659bd8a0f210abd0 656c69742c2073656420646f20656975736d6f642074656d706f7220696e6369646964756e74207574206c61626f7265 +47758e86d2f57210366cec36fff26f9464d89efd116fe6ef28b718b5da120801 657420646f6c6f7265206d61676e6120616c697175612e20557420656e696d206164206d696e696d2076656e69616d2c +48787b48d5c68f0c1dd825c32580af741cc0ee314f08133135c1e86d87a24a95 71756973206e6f737472756420657865726369746174696f6e20756c6c616d636f206c61626f726973206e697369 +93bdf47495854f7cfaaca1af8c0f39ef4a3024c10eb0dea23726b0e06ef29e84 757420616c697175697020657820656120636f6d6d6f646f20636f6e7365717561742e20447569732061757465 +a375a71d0541057ccc96719150dfe10b6e6f486b19cf4a0835e19605413a8417 697275726520646f6c6f7220696e20726570726568656e646572697420696e20766f6c7570746174652076656c6974 +163478a76f8f1432533fbdd1284d65c89f37479e54f20841c6ce4eba56c73854 657373652063696c6c756d20646f6c6f726520657520667567696174206e756c6c612070617269617475722e +356b0470c6eea75cad7a108179e232905b23bdaf03c2824c6e619d503ee93677 4578636570746575722073696e74206f6363616563617420637570696461746174206e6f6e2070726f6964656e742c +a47e2b007dc25bb279e197a1b91f67ecebe2ddd8791cd32dd2cb76dd21ed943f 73756e7420696e2063756c706120717569206f666669636961206465736572756e74206d6f6c6c697420616e696d20696420657374206c61626f72756d2e From 44439c32088dc684d686a392acf807f12ca0e0e9 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 29 Aug 2018 16:57:12 +0000 Subject: [PATCH 0319/1404] record blackballs as amount/offset, and add export ability --- .../blockchain_blackball.cpp | 241 +++++++++++++++--- src/simplewallet/simplewallet.cpp | 63 +++-- src/wallet/ringdb.cpp | 46 ++-- src/wallet/ringdb.h | 10 +- src/wallet/wallet2.cpp | 14 +- src/wallet/wallet2.h | 8 +- tests/unit_tests/ringdb.cpp | 8 +- 7 files changed, 287 insertions(+), 103 deletions(-) diff --git a/src/blockchain_utilities/blockchain_blackball.cpp b/src/blockchain_utilities/blockchain_blackball.cpp index e68de9dc4b5..9a31b95b30a 100644 --- a/src/blockchain_utilities/blockchain_blackball.cpp +++ b/src/blockchain_utilities/blockchain_blackball.cpp @@ -66,17 +66,17 @@ static MDB_env *env = NULL; struct output_data { uint64_t amount; - uint64_t index; - output_data(): amount(0), index(0) {} - output_data(uint64_t a, uint64_t i): amount(a), index(i) {} - bool operator==(const output_data &other) const { return other.amount == amount && other.index == index; } + uint64_t offset; + output_data(): amount(0), offset(0) {} + output_data(uint64_t a, uint64_t i): amount(a), offset(i) {} + bool operator==(const output_data &other) const { return other.amount == amount && other.offset == offset; } }; // // relative_rings: key_image -> vector // outputs: 128 bits -> set of key images // processed_txidx: string -> uint64_t -// spent: 128 bits, zerokval +// spent: amount -> offset // ring_instances: vector -> uint64_t // stats: string -> arbitrary // @@ -263,7 +263,7 @@ static void init(std::string cache_filename) dbr = mdb_dbi_open(txn, "spent", MDB_CREATE | MDB_INTEGERKEY | MDB_DUPSORT | MDB_DUPFIXED, &dbi_spent); CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to open LMDB dbi: " + std::string(mdb_strerror(dbr))); - mdb_set_dupsort(txn, dbi_spent, compare_double64); + mdb_set_dupsort(txn, dbi_spent, compare_uint64); dbr = mdb_dbi_open(txn, "ring_instances", MDB_CREATE, &dbi_ring_instances); CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to open LMDB dbi: " + std::string(mdb_strerror(dbr))); @@ -511,13 +511,19 @@ static uint64_t get_num_spent_outputs() dbr = mdb_cursor_open(txn, dbi_spent, &cur); CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to open cursor for spent outputs: " + std::string(mdb_strerror(dbr))); MDB_val k, v; - mdb_size_t count = 0; - dbr = mdb_cursor_get(cur, &k, &v, MDB_FIRST); - if (dbr != MDB_NOTFOUND) + mdb_size_t count = 0, tmp; + + MDB_cursor_op op = MDB_FIRST; + while (1) { - CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to get first spent output: " + std::string(mdb_strerror(dbr))); - dbr = mdb_cursor_count(cur, &count); + dbr = mdb_cursor_get(cur, &k, &v, op); + op = MDB_NEXT_NODUP; + if (dbr == MDB_NOTFOUND) + break; + CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to get first/next spent output: " + std::string(mdb_strerror(dbr))); + dbr = mdb_cursor_count(cur, &tmp); CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to count entries: " + std::string(mdb_strerror(dbr))); + count += tmp; } mdb_cursor_close(cur); @@ -530,15 +536,17 @@ static uint64_t get_num_spent_outputs() static void add_spent_output(MDB_cursor *cur, const output_data &od) { - MDB_val v = {sizeof(od), (void*)&od}; - int dbr = mdb_cursor_put(cur, (MDB_val *)&zerokval, &v, MDB_NODUPDATA); + MDB_val k = {sizeof(od.amount), (void*)&od.amount}; + MDB_val v = {sizeof(od.offset), (void*)&od.offset}; + int dbr = mdb_cursor_put(cur, &k, &v, 0); CHECK_AND_ASSERT_THROW_MES(!dbr || dbr == MDB_KEYEXIST, "Failed to add spent output: " + std::string(mdb_strerror(dbr))); } static bool is_output_spent(MDB_cursor *cur, const output_data &od) { - MDB_val v = {sizeof(od), (void*)&od}; - int dbr = mdb_cursor_get(cur, (MDB_val *)&zerokval, &v, MDB_GET_BOTH); + MDB_val k = {sizeof(od.amount), (void*)&od.amount}; + MDB_val v = {sizeof(od.offset), (void*)&od.offset}; + int dbr = mdb_cursor_get(cur, &k, &v, MDB_GET_BOTH); CHECK_AND_ASSERT_THROW_MES(!dbr || dbr == MDB_NOTFOUND, "Failed to get spent output: " + std::string(mdb_strerror(dbr))); bool spent = dbr == 0; return spent; @@ -562,8 +570,7 @@ static std::vector get_spent_outputs(MDB_txn *txn) outs.reserve(count); while (1) { - const output_data *od = (const output_data*)v.mv_data; - outs.push_back(*od); + outs.push_back({*(const uint64_t*)k.mv_data, *(const uint64_t*)v.mv_data}); dbr = mdb_cursor_get(cur, &k, &v, MDB_NEXT); if (dbr == MDB_NOTFOUND) break; @@ -815,14 +822,6 @@ static void close_db(MDB_env *env, MDB_txn *txn, MDB_cursor *cur, MDB_dbi dbi) mdb_env_close(env); } -static crypto::public_key get_output_key(MDB_cursor *cur, uint64_t amount, uint64_t offset) -{ - MDB_val k = { sizeof(amount), (void*)&amount }, v = { sizeof(offset), (void*)&offset }; - int dbr = mdb_cursor_get(cur, &k, &v, MDB_GET_BOTH); - if (dbr) throw std::runtime_error("Output key not found: " + std::string(mdb_strerror(dbr))); - return *(const crypto::public_key*)(((const char*)v.mv_data) + sizeof(uint64_t) * 2); -} - static void get_num_outputs(MDB_txn *txn, MDB_cursor *cur, MDB_dbi dbi, uint64_t &pre_rct, uint64_t &rct) { uint64_t amount = 0; @@ -884,6 +883,113 @@ static crypto::hash get_genesis_block_hash(const std::string &filename) return genesis_block_hash; } +static std::vector> load_outputs(const std::string &filename) +{ + std::vector> outputs; + uint64_t amount = std::numeric_limits::max(); + FILE *f; + + f = fopen(filename.c_str(), "r"); + if (!f) + { + MERROR("Failed to load outputs from " << filename << ": " << strerror(errno)); + return {}; + } + while (1) + { + char s[256]; + fgets(s, sizeof(s), f); + if (feof(f)) + break; + const size_t len = strlen(s); + if (len > 0 && s[len - 1] == '\n') + s[len - 1] = 0; + if (!s[0]) + continue; + std::pair output; + uint64_t offset, num_offsets; + if (sscanf(s, "@%" PRIu64, &amount) == 1) + { + continue; + } + if (amount == std::numeric_limits::max()) + { + MERROR("Bad format in " << filename); + continue; + } + if (sscanf(s, "%" PRIu64 "*%" PRIu64, &offset, &num_offsets) == 2 && num_offsets < std::numeric_limits::max() - offset) + { + while (num_offsets-- > 0) + outputs.push_back(std::make_pair(amount, offset++)); + } + else if (sscanf(s, "%" PRIu64, &offset) == 1) + { + outputs.push_back(std::make_pair(amount, offset)); + } + else + { + MERROR("Bad format in " << filename); + continue; + } + } + fclose(f); + return outputs; +} + +static bool export_spent_outputs(MDB_cursor *cur, const std::string &filename) +{ + FILE *f = fopen(filename.c_str(), "w"); + if (!f) + { + MERROR("Failed to open " << filename << ": " << strerror(errno)); + return false; + } + + uint64_t pending_amount = std::numeric_limits::max(); + std::vector pending_offsets; + MDB_val k, v; + MDB_cursor_op op = MDB_FIRST; + while (1) + { + int dbr = mdb_cursor_get(cur, &k, &v, op); + if (dbr == MDB_NOTFOUND) + break; + op = MDB_NEXT; + if (dbr) + { + fclose(f); + MERROR("Failed to enumerate spent outputs: " << mdb_strerror(dbr)); + return false; + } + const uint64_t amount = *(const uint64_t*)k.mv_data; + const uint64_t offset = *(const uint64_t*)v.mv_data; + if (!pending_offsets.empty() && (amount != pending_amount || pending_offsets.back()+1 != offset)) + { + if (pending_offsets.size() == 1) + fprintf(f, "%" PRIu64 "\n", pending_offsets.front()); + else + fprintf(f, "%" PRIu64 "*%" PRIu64 "\n", pending_offsets.front(), pending_offsets.size()); + pending_offsets.clear(); + } + if (pending_amount != amount) + { + fprintf(f, "@%" PRIu64 "\n", amount); + pending_amount = amount; + } + pending_offsets.push_back(offset); + } + if (!pending_offsets.empty()) + { + if (pending_offsets.size() == 1) + fprintf(f, "%" PRIu64 "\n", pending_offsets.front()); + else + fprintf(f, "%" PRIu64 "*%" PRIu64 "\n", pending_offsets.front(), pending_offsets.size()); + pending_offsets.clear(); + } + fclose(f); + return true; +} + int main(int argc, char* argv[]) { TRY_ENTRY(); @@ -920,6 +1026,8 @@ int main(int argc, char* argv[]) , "Specify sync option, using format [safe|fast|fastest]:[nrecords_per_sync]." , "fast:1000" }; + const command_line::arg_descriptor arg_extra_spent_list = {"extra-spent-list", "Optional list of known spent outputs",""}; + const command_line::arg_descriptor arg_export = {"export", "Filename to export the backball list to"}; command_line::add_arg(desc_cmd_sett, arg_blackball_db_dir); command_line::add_arg(desc_cmd_sett, arg_log_level); @@ -928,6 +1036,8 @@ int main(int argc, char* argv[]) command_line::add_arg(desc_cmd_sett, arg_check_subsets); command_line::add_arg(desc_cmd_sett, arg_verbose); command_line::add_arg(desc_cmd_sett, arg_db_sync_mode); + command_line::add_arg(desc_cmd_sett, arg_extra_spent_list); + command_line::add_arg(desc_cmd_sett, arg_export); command_line::add_arg(desc_cmd_sett, arg_inputs); command_line::add_arg(desc_cmd_only, command_line::arg_help); @@ -967,6 +1077,9 @@ int main(int argc, char* argv[]) bool opt_rct_only = command_line::get_arg(vm, arg_rct_only); bool opt_check_subsets = command_line::get_arg(vm, arg_check_subsets); bool opt_verbose = command_line::get_arg(vm, arg_verbose); + std::string opt_export = command_line::get_arg(vm, arg_export); + std::string extra_spent_list = command_line::get_arg(vm, arg_extra_spent_list); + std::vector> extra_spent_outputs = extra_spent_list.empty() ? std::vector>() : load_outputs(extra_spent_list); std::string db_type = command_line::get_arg(vm, arg_database); if (!cryptonote::blockchain_valid_db_type(db_type)) @@ -1015,6 +1128,36 @@ int main(int argc, char* argv[]) MDB_cursor *cur0; open_db(inputs[0], &env0, &txn0, &cur0, &dbi0); + if (!extra_spent_outputs.empty()) + { + MINFO("Adding " << extra_spent_outputs.size() << " extra spent outputs"); + MDB_txn *txn; + int dbr = mdb_txn_begin(env, NULL, 0, &txn); + CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to create LMDB transaction: " + std::string(mdb_strerror(dbr))); + MDB_cursor *cur; + dbr = mdb_cursor_open(txn, dbi_spent, &cur); + CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to open LMDB cursor: " + std::string(mdb_strerror(dbr))); + + std::vector> blackballs; + for (const std::pair &output: extra_spent_outputs) + { + if (!is_output_spent(cur, output_data(output.first, output.second))) + { + blackballs.push_back(output); + add_spent_output(cur, output_data(output.first, output.second)); + inc_stat(txn, output.first ? "pre-rct-extra" : "rct-ring-extra"); + } + } + if (!blackballs.empty()) + { + ringdb.blackball(blackballs); + blackballs.clear(); + } + mdb_cursor_close(cur); + dbr = mdb_txn_commit(txn); + CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to commit txn creating/opening database: " + std::string(mdb_strerror(dbr))); + } + for (size_t n = 0; n < inputs.size(); ++n) { const std::string canonical = boost::filesystem::canonical(inputs[n]).string(); @@ -1033,7 +1176,7 @@ int main(int argc, char* argv[]) CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to open LMDB cursor: " + std::string(mdb_strerror(dbr))); size_t records = 0; const std::string filename = inputs[n]; - std::vector blackballs; + std::vector> blackballs; uint64_t n_txes; for_all_transactions(filename, start_idx, n_txes, [&](const cryptonote::transaction_prefix &tx)->bool { @@ -1057,13 +1200,13 @@ int main(int argc, char* argv[]) const uint64_t instances = inc_ring_instances(txn, txin.amount, new_ring); if (n == 0 && ring_size == 1) { - const crypto::public_key pkey = get_output_key(cur0, txin.amount, absolute[0]); + const std::pair output = std::make_pair(txin.amount, absolute[0]); if (opt_verbose) { - MINFO("Blackballing output " << pkey << ", due to being used in a 1-ring"); + MINFO("Blackballing output " << output.first << "/" << output.second << ", due to being used in a 1-ring"); std::cout << "\r" << start_idx << "/" << n_txes << " \r" << std::flush; } - blackballs.push_back(pkey); + blackballs.push_back(output); add_spent_output(cur, output_data(txin.amount, absolute[0])); inc_stat(txn, txin.amount ? "pre-rct-ring-size-1" : "rct-ring-size-1"); } @@ -1071,13 +1214,13 @@ int main(int argc, char* argv[]) { for (size_t o = 0; o < new_ring.size(); ++o) { - const crypto::public_key pkey = get_output_key(cur0, txin.amount, absolute[o]); + const std::pair output = std::make_pair(txin.amount, absolute[o]); if (opt_verbose) { - MINFO("Blackballing output " << pkey << ", due to being used in " << new_ring.size() << " identical " << new_ring.size() << "-rings"); + MINFO("Blackballing output " << output.first << "/" << output.second << ", due to being used in " << new_ring.size() << " identical " << new_ring.size() << "-rings"); std::cout << "\r" << start_idx << "/" << n_txes << " \r" << std::flush; } - blackballs.push_back(pkey); + blackballs.push_back(output); add_spent_output(cur, output_data(txin.amount, absolute[o])); inc_stat(txn, txin.amount ? "pre-rct-duplicate-rings" : "rct-duplicate-rings"); } @@ -1086,13 +1229,13 @@ int main(int argc, char* argv[]) { for (size_t o = 0; o < new_ring.size(); ++o) { - const crypto::public_key pkey = get_output_key(cur0, txin.amount, absolute[o]); + const std::pair output = std::make_pair(txin.amount, absolute[o]); if (opt_verbose) { - MINFO("Blackballing output " << pkey << ", due to being used in " << new_ring.size() << " subsets of " << new_ring.size() << "-rings"); + MINFO("Blackballing output " << output.first << "/" << output.second << ", due to being used in " << new_ring.size() << " subsets of " << new_ring.size() << "-rings"); std::cout << "\r" << start_idx << "/" << n_txes << " \r" << std::flush; } - blackballs.push_back(pkey); + blackballs.push_back(output); add_spent_output(cur, output_data(txin.amount, absolute[o])); inc_stat(txn, txin.amount ? "pre-rct-subset-rings" : "rct-subset-rings"); } @@ -1122,13 +1265,13 @@ int main(int argc, char* argv[]) } else if (common.size() == 1) { - const crypto::public_key pkey = get_output_key(cur0, txin.amount, common[0]); + const std::pair output = std::make_pair(txin.amount, common[0]); if (opt_verbose) { - MINFO("Blackballing output " << pkey << ", due to being used in rings with a single common element"); + MINFO("Blackballing output " << output.first << "/" << output.second << ", due to being used in rings with a single common element"); std::cout << "\r" << start_idx << "/" << n_txes << " \r" << std::flush; } - blackballs.push_back(pkey); + blackballs.push_back(output); add_spent_output(cur, output_data(txin.amount, common[0])); inc_stat(txn, txin.amount ? "pre-rct-key-image-attack" : "rct-key-image-attack"); } @@ -1211,7 +1354,7 @@ int main(int argc, char* argv[]) dbr = mdb_cursor_open(txn, dbi_spent, &cur); CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to open LMDB cursor: " + std::string(mdb_strerror(dbr))); - std::vector blackballs; + std::vector> blackballs; std::vector scan_spent = std::move(work_spent); work_spent.clear(); for (const output_data &od: scan_spent) @@ -1234,13 +1377,13 @@ int main(int argc, char* argv[]) } if (known == absolute.size() - 1) { - const crypto::public_key pkey = get_output_key(cur0, od.amount, last_unknown); + const std::pair output = std::make_pair(od.amount, last_unknown); if (opt_verbose) { - MINFO("Blackballing output " << pkey << ", due to being used in a " << + MINFO("Blackballing output " << output.first << "/" << output.second << ", due to being used in a " << absolute.size() << "-ring where all other outputs are known to be spent"); } - blackballs.push_back(pkey); + blackballs.push_back(output); add_spent_output(cur, output_data(od.amount, last_unknown)); work_spent.push_back(output_data(od.amount, last_unknown)); inc_stat(txn, od.amount ? "pre-rct-chain-reaction" : "rct-chain-reaction"); @@ -1279,6 +1422,7 @@ int main(int argc, char* argv[]) { "pre-rct-duplicate-rings", pre_rct }, { "rct-duplicate-rings", rct }, { "pre-rct-subset-rings", pre_rct }, { "rct-subset-rings", rct }, { "pre-rct-key-image-attack", pre_rct }, { "rct-key-image-attack", rct }, + { "pre-rct-extra", pre_rct }, { "rct-ring-extra", rct }, { "pre-rct-chain-reaction", pre_rct }, { "rct-chain-reaction", rct }, }; for (const auto &key: stat_keys) @@ -1291,6 +1435,19 @@ int main(int argc, char* argv[]) } mdb_txn_abort(txn); + if (!opt_export.empty()) + { + MDB_txn *txn; + int dbr = mdb_txn_begin(env, NULL, 0, &txn); + CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to create LMDB transaction: " + std::string(mdb_strerror(dbr))); + MDB_cursor *cur; + dbr = mdb_cursor_open(txn, dbi_spent, &cur); + CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to open LMDB cursor: " + std::string(mdb_strerror(dbr))); + export_spent_outputs(cur, opt_export); + mdb_cursor_close(cur); + mdb_txn_abort(txn); + } + LOG_PRINT_L0("Blockchain blackball data exported OK"); close_db(env0, txn0, cur0, dbi0); close(); diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index f5aeabded1b..e0e0c441a5a 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -1624,23 +1624,23 @@ bool simple_wallet::set_ring(const std::vector &args) bool simple_wallet::blackball(const std::vector &args) { - crypto::public_key output; + uint64_t amount = std::numeric_limits::max(), offset, num_offsets; if (args.size() == 0) { - fail_msg_writer() << tr("usage: blackball | [add]"); + fail_msg_writer() << tr("usage: blackball / | [add]"); return true; } try { - if (epee::string_tools::hex_to_pod(args[0], output)) + if (sscanf(args[0].c_str(), "%" PRIu64 "/%" PRIu64, &amount, &offset) == 2) { - m_wallet->blackball_output(output); + m_wallet->blackball_output(std::make_pair(amount, offset)); } else if (epee::file_io_utils::is_file_exist(args[0])) { - std::vector outputs; - char str[65]; + std::vector> outputs; + char str[256]; std::unique_ptr f(fopen(args[0].c_str(), "r")); if (f) @@ -1654,10 +1654,27 @@ bool simple_wallet::blackball(const std::vector &args) str[len - 1] = 0; if (!str[0]) continue; - outputs.push_back(crypto::public_key()); - if (!epee::string_tools::hex_to_pod(str, outputs.back())) + if (sscanf(str, "@%" PRIu64, &amount) == 1) { - fail_msg_writer() << tr("Invalid public key: ") << str; + continue; + } + if (amount == std::numeric_limits::max()) + { + fail_msg_writer() << tr("First line is not an amount"); + return true; + } + if (sscanf(str, "%" PRIu64 "*%" PRIu64, &offset, &num_offsets) == 2 && num_offsets <= std::numeric_limits::max() - offset) + { + while (num_offsets--) + outputs.push_back(std::make_pair(amount, offset++)); + } + else if (sscanf(str, "%" PRIu64, &offset) == 1) + { + outputs.push_back(std::make_pair(amount, offset)); + } + else + { + fail_msg_writer() << tr("Invalid output: ") << str; return true; } } @@ -1682,7 +1699,7 @@ bool simple_wallet::blackball(const std::vector &args) } else { - fail_msg_writer() << tr("Invalid public key, and file doesn't exist"); + fail_msg_writer() << tr("Invalid output key, and file doesn't exist"); return true; } } @@ -1696,16 +1713,16 @@ bool simple_wallet::blackball(const std::vector &args) bool simple_wallet::unblackball(const std::vector &args) { - crypto::public_key output; + std::pair output; if (args.size() != 1) { - fail_msg_writer() << tr("usage: unblackball "); + fail_msg_writer() << tr("usage: unblackball /"); return true; } - if (!epee::string_tools::hex_to_pod(args[0], output)) + if (sscanf(args[0].c_str(), "%" PRIu64 "/%" PRIu64, &output.first, &output.second) != 2) { - fail_msg_writer() << tr("Invalid public key"); + fail_msg_writer() << tr("Invalid output"); return true; } @@ -1723,25 +1740,25 @@ bool simple_wallet::unblackball(const std::vector &args) bool simple_wallet::blackballed(const std::vector &args) { - crypto::public_key output; + std::pair output; if (args.size() != 1) { - fail_msg_writer() << tr("usage: blackballed "); + fail_msg_writer() << tr("usage: blackballed /"); return true; } - if (!epee::string_tools::hex_to_pod(args[0], output)) + if (sscanf(args[0].c_str(), "%" PRIu64 "/%" PRIu64, &output.first, &output.second) != 2) { - fail_msg_writer() << tr("Invalid public key"); + fail_msg_writer() << tr("Invalid output"); return true; } try { if (m_wallet->is_output_blackballed(output)) - message_writer() << tr("Blackballed: ") << output; + message_writer() << tr("Blackballed: ") << output.first << "/" << output.second; else - message_writer() << tr("not blackballed: ") << output; + message_writer() << tr("not blackballed: ") << output.first << "/" << output.second; } catch (const std::exception &e) { @@ -2554,15 +2571,15 @@ simple_wallet::simple_wallet() tr("Save known rings to the shared rings database")); m_cmd_binder.set_handler("blackball", boost::bind(&simple_wallet::blackball, this, _1), - tr("blackball | [add]"), + tr("blackball / | [add]"), tr("Blackball output(s) so they never get selected as fake outputs in a ring")); m_cmd_binder.set_handler("unblackball", boost::bind(&simple_wallet::unblackball, this, _1), - tr("unblackball "), + tr("unblackball /"), tr("Unblackballs an output so it may get selected as a fake output in a ring")); m_cmd_binder.set_handler("blackballed", boost::bind(&simple_wallet::blackballed, this, _1), - tr("blackballed "), + tr("blackballed /"), tr("Checks whether an output is blackballed")); m_cmd_binder.set_handler("version", boost::bind(&simple_wallet::version, this, _1), diff --git a/src/wallet/ringdb.cpp b/src/wallet/ringdb.cpp index bf5478f5ee3..e9fc6866da0 100644 --- a/src/wallet/ringdb.cpp +++ b/src/wallet/ringdb.cpp @@ -55,6 +55,13 @@ static int compare_hash32(const MDB_val *a, const MDB_val *b) return 0; } +static int compare_uint64(const MDB_val *a, const MDB_val *b) +{ + const uint64_t va = *(const uint64_t*) a->mv_data; + const uint64_t vb = *(const uint64_t*) b->mv_data; + return va < vb ? -1 : va > vb; +} + static std::string compress_ring(const std::vector &ring) { std::string s; @@ -217,9 +224,9 @@ ringdb::ringdb(std::string filename, const std::string &genesis): THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to open LMDB dbi: " + std::string(mdb_strerror(dbr))); mdb_set_compare(txn, dbi_rings, compare_hash32); - dbr = mdb_dbi_open(txn, ("blackballs-" + genesis).c_str(), MDB_CREATE | MDB_INTEGERKEY | MDB_DUPSORT | MDB_DUPFIXED, &dbi_blackballs); + dbr = mdb_dbi_open(txn, ("blackballs2-" + genesis).c_str(), MDB_CREATE | MDB_INTEGERKEY | MDB_DUPSORT | MDB_DUPFIXED, &dbi_blackballs); THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to open LMDB dbi: " + std::string(mdb_strerror(dbr))); - mdb_set_dupsort(txn, dbi_blackballs, compare_hash32); + mdb_set_dupsort(txn, dbi_blackballs, compare_uint64); dbr = mdb_txn_commit(txn); THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to commit txn creating/opening database: " + std::string(mdb_strerror(dbr))); @@ -374,7 +381,7 @@ bool ringdb::set_ring(const crypto::chacha_key &chacha_key, const crypto::key_im return true; } -bool ringdb::blackball_worker(const std::vector &outputs, int op) +bool ringdb::blackball_worker(const std::vector> &outputs, int op) { MDB_txn *txn; MDB_cursor *cursor; @@ -391,24 +398,25 @@ bool ringdb::blackball_worker(const std::vector &outputs, in epee::misc_utils::auto_scope_leave_caller txn_dtor = epee::misc_utils::create_scope_leave_handler([&](){if (tx_active) mdb_txn_abort(txn);}); tx_active = true; - MDB_val key = zerokeyval; - MDB_val data; - for (const crypto::public_key &output: outputs) + MDB_val key, data; + for (const std::pair &output: outputs) { - data.mv_data = (void*)&output; - data.mv_size = sizeof(output); + key.mv_data = (void*)&output.first; + key.mv_size = sizeof(output.first); + data.mv_data = (void*)&output.second; + data.mv_size = sizeof(output.second); switch (op) { case BLACKBALL_BLACKBALL: - MDEBUG("Blackballing output " << output); - dbr = mdb_put(txn, dbi_blackballs, &key, &data, MDB_NODUPDATA); + MDEBUG("Blackballing output " << output.first << "/" << output.second); + dbr = mdb_put(txn, dbi_blackballs, &key, &data, MDB_APPENDDUP); if (dbr == MDB_KEYEXIST) dbr = 0; break; case BLACKBALL_UNBLACKBALL: - MDEBUG("Unblackballing output " << output); + MDEBUG("Unblackballing output " << output.first << "/" << output.second); dbr = mdb_del(txn, dbi_blackballs, &key, &data); if (dbr == MDB_NOTFOUND) dbr = 0; @@ -443,32 +451,32 @@ bool ringdb::blackball_worker(const std::vector &outputs, in return ret; } -bool ringdb::blackball(const std::vector &outputs) +bool ringdb::blackball(const std::vector> &outputs) { return blackball_worker(outputs, BLACKBALL_BLACKBALL); } -bool ringdb::blackball(const crypto::public_key &output) +bool ringdb::blackball(const std::pair &output) { - std::vector outputs(1, output); + std::vector> outputs(1, output); return blackball_worker(outputs, BLACKBALL_BLACKBALL); } -bool ringdb::unblackball(const crypto::public_key &output) +bool ringdb::unblackball(const std::pair &output) { - std::vector outputs(1, output); + std::vector> outputs(1, output); return blackball_worker(outputs, BLACKBALL_UNBLACKBALL); } -bool ringdb::blackballed(const crypto::public_key &output) +bool ringdb::blackballed(const std::pair &output) { - std::vector outputs(1, output); + std::vector> outputs(1, output); return blackball_worker(outputs, BLACKBALL_QUERY); } bool ringdb::clear_blackballs() { - return blackball_worker(std::vector(), BLACKBALL_CLEAR); + return blackball_worker(std::vector>(), BLACKBALL_CLEAR); } } diff --git a/src/wallet/ringdb.h b/src/wallet/ringdb.h index 4a78f61f88e..7b448b0d7fd 100644 --- a/src/wallet/ringdb.h +++ b/src/wallet/ringdb.h @@ -49,14 +49,14 @@ namespace tools bool get_ring(const crypto::chacha_key &chacha_key, const crypto::key_image &key_image, std::vector &outs); bool set_ring(const crypto::chacha_key &chacha_key, const crypto::key_image &key_image, const std::vector &outs, bool relative); - bool blackball(const crypto::public_key &output); - bool blackball(const std::vector &outputs); - bool unblackball(const crypto::public_key &output); - bool blackballed(const crypto::public_key &output); + bool blackball(const std::pair &output); + bool blackball(const std::vector> &outputs); + bool unblackball(const std::pair &output); + bool blackballed(const std::pair &output); bool clear_blackballs(); private: - bool blackball_worker(const std::vector &outputs, int op); + bool blackball_worker(const std::vector> &outputs, int op); private: std::string filename; diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index ffd7131cde7..9866aa21530 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -6221,7 +6221,7 @@ bool wallet2::find_and_save_rings(bool force) return true; } -bool wallet2::blackball_output(const crypto::public_key &output) +bool wallet2::blackball_output(const std::pair &output) { if (!m_ringdb) return false; @@ -6229,7 +6229,7 @@ bool wallet2::blackball_output(const crypto::public_key &output) catch (const std::exception &e) { return false; } } -bool wallet2::set_blackballed_outputs(const std::vector &outputs, bool add) +bool wallet2::set_blackballed_outputs(const std::vector> &outputs, bool add) { if (!m_ringdb) return false; @@ -6244,7 +6244,7 @@ bool wallet2::set_blackballed_outputs(const std::vector &out catch (const std::exception &e) { return false; } } -bool wallet2::unblackball_output(const crypto::public_key &output) +bool wallet2::unblackball_output(const std::pair &output) { if (!m_ringdb) return false; @@ -6252,7 +6252,7 @@ bool wallet2::unblackball_output(const crypto::public_key &output) catch (const std::exception &e) { return false; } } -bool wallet2::is_output_blackballed(const crypto::public_key &output) const +bool wallet2::is_output_blackballed(const std::pair &output) const { if (!m_ringdb) return false; @@ -6297,8 +6297,8 @@ bool wallet2::tx_add_fake_output(std::vector> if (seen_indices.count(i)) continue; + if (is_output_blackballed(std::make_pair(amount, i))) // don't add blackballed outputs + continue; seen_indices.emplace(i); LOG_PRINT_L2("picking " << i << " as " << type); diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 556679f511d..f0ed1c0e8df 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -1161,10 +1161,10 @@ namespace tools bool set_ring(const crypto::key_image &key_image, const std::vector &outs, bool relative); bool find_and_save_rings(bool force = true); - bool blackball_output(const crypto::public_key &output); - bool set_blackballed_outputs(const std::vector &outputs, bool add = false); - bool unblackball_output(const crypto::public_key &output); - bool is_output_blackballed(const crypto::public_key &output) const; + bool blackball_output(const std::pair &output); + bool set_blackballed_outputs(const std::vector> &outputs, bool add = false); + bool unblackball_output(const std::pair &output); + bool is_output_blackballed(const std::pair &output) const; bool lock_keys_file(); bool unlock_keys_file(); diff --git a/tests/unit_tests/ringdb.cpp b/tests/unit_tests/ringdb.cpp index 9b842569a8f..416ae089091 100644 --- a/tests/unit_tests/ringdb.cpp +++ b/tests/unit_tests/ringdb.cpp @@ -59,17 +59,17 @@ static crypto::key_image generate_key_image() return key_image; } -static crypto::public_key generate_output() +static std::pair generate_output() { - return rct::rct2pk(rct::scalarmultBase(rct::skGen())); + return std::make_pair(rand(), rand()); } static const crypto::chacha_key KEY_1 = generate_chacha_key(); static const crypto::chacha_key KEY_2 = generate_chacha_key(); static const crypto::key_image KEY_IMAGE_1 = generate_key_image(); -static const crypto::public_key OUTPUT_1 = generate_output(); -static const crypto::public_key OUTPUT_2 = generate_output(); +static const std::pair OUTPUT_1 = generate_output(); +static const std::pair OUTPUT_2 = generate_output(); class RingDB: public tools::ringdb { From a54dbaee087a70df4be066d7d0f237fdfcba2300 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 30 Aug 2018 09:11:53 +0000 Subject: [PATCH 0320/1404] blockchain_blackball: add --force-chain-reaction-pass flag --- src/blockchain_utilities/blockchain_blackball.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/blockchain_utilities/blockchain_blackball.cpp b/src/blockchain_utilities/blockchain_blackball.cpp index 9a31b95b30a..d9a179f641c 100644 --- a/src/blockchain_utilities/blockchain_blackball.cpp +++ b/src/blockchain_utilities/blockchain_blackball.cpp @@ -1028,6 +1028,7 @@ int main(int argc, char* argv[]) }; const command_line::arg_descriptor arg_extra_spent_list = {"extra-spent-list", "Optional list of known spent outputs",""}; const command_line::arg_descriptor arg_export = {"export", "Filename to export the backball list to"}; + const command_line::arg_descriptor arg_force_chain_reaction_pass = {"force-chain-reaction-pass", "Run the chain reaction pass even if no new blockchain data was processed"}; command_line::add_arg(desc_cmd_sett, arg_blackball_db_dir); command_line::add_arg(desc_cmd_sett, arg_log_level); @@ -1038,6 +1039,7 @@ int main(int argc, char* argv[]) command_line::add_arg(desc_cmd_sett, arg_db_sync_mode); command_line::add_arg(desc_cmd_sett, arg_extra_spent_list); command_line::add_arg(desc_cmd_sett, arg_export); + command_line::add_arg(desc_cmd_sett, arg_force_chain_reaction_pass); command_line::add_arg(desc_cmd_sett, arg_inputs); command_line::add_arg(desc_cmd_only, command_line::arg_help); @@ -1077,6 +1079,7 @@ int main(int argc, char* argv[]) bool opt_rct_only = command_line::get_arg(vm, arg_rct_only); bool opt_check_subsets = command_line::get_arg(vm, arg_check_subsets); bool opt_verbose = command_line::get_arg(vm, arg_verbose); + bool opt_force_chain_reaction_pass = command_line::get_arg(vm, arg_force_chain_reaction_pass); std::string opt_export = command_line::get_arg(vm, arg_export); std::string extra_spent_list = command_line::get_arg(vm, arg_extra_spent_list); std::vector> extra_spent_outputs = extra_spent_list.empty() ? std::vector>() : load_outputs(extra_spent_list); @@ -1331,7 +1334,7 @@ int main(int argc, char* argv[]) if (stop_requested) goto skip_secondary_passes; - if (get_num_spent_outputs() > start_blackballed_outputs) + if (opt_force_chain_reaction_pass || get_num_spent_outputs() > start_blackballed_outputs) { MDB_txn *txn; dbr = mdb_txn_begin(env, NULL, MDB_RDONLY, &txn); From e774f2498a2c3b8ba7875e4a1d1887249946a4d0 Mon Sep 17 00:00:00 2001 From: MoroccanMalinois Date: Mon, 10 Sep 2018 10:11:58 -0400 Subject: [PATCH 0321/1404] Docker android: use common prefix --- utils/build_scripts/android32.Dockerfile | 30 ++++++++++++------------ 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/utils/build_scripts/android32.Dockerfile b/utils/build_scripts/android32.Dockerfile index e56fade50c3..7ad1363bb98 100644 --- a/utils/build_scripts/android32.Dockerfile +++ b/utils/build_scripts/android32.Dockerfile @@ -2,8 +2,6 @@ FROM debian:stable RUN apt-get update && apt-get install -y unzip automake build-essential curl file pkg-config git python libtool -ARG NPROC=1 - WORKDIR /opt/android ## INSTALL ANDROID SDK ENV ANDROID_SDK_REVISION 4333796 @@ -24,6 +22,7 @@ RUN curl -s -O https://dl.google.com/android/repository/android-ndk-r${ANDROID_N ENV WORKDIR /opt/android ENV ANDROID_SDK_ROOT ${WORKDIR}/tools ENV ANDROID_NDK_ROOT ${WORKDIR}/android-ndk-r${ANDROID_NDK_REVISION} +ENV PREFIX /opt/android/prefix ENV TOOLCHAIN_DIR ${WORKDIR}/toolchain-arm RUN ${ANDROID_NDK_ROOT}/build/tools/make_standalone_toolchain.py \ @@ -50,11 +49,13 @@ RUN set -ex \ && tar -xvf boost_${BOOST_VERSION}.tar.bz2 \ && rm -f boost_${BOOST_VERSION}.tar.bz2 \ && cd boost_${BOOST_VERSION} \ - && ./bootstrap.sh + && ./bootstrap.sh --prefix=${PREFIX} ENV HOST_PATH $PATH ENV PATH $TOOLCHAIN_DIR/arm-linux-androideabi/bin:$TOOLCHAIN_DIR/bin:$PATH +ARG NPROC=1 + # Build iconv for lib boost locale ENV ICONV_VERSION 1.15 ENV ICONV_HASH ccf536620a45458d26ba83887a983b96827001e92a13847b45e4925cc8913178 @@ -63,12 +64,12 @@ RUN curl -s -O http://ftp.gnu.org/pub/gnu/libiconv/libiconv-${ICONV_VERSION}.tar && tar -xzf libiconv-${ICONV_VERSION}.tar.gz \ && rm -f libiconv-${ICONV_VERSION}.tar.gz \ && cd libiconv-${ICONV_VERSION} \ - && CC=arm-linux-androideabi-clang CXX=arm-linux-androideabi-clang++ ./configure --build=x86_64-linux-gnu --host=arm-eabi --prefix=${WORKDIR}/libiconv --disable-rpath \ + && CC=arm-linux-androideabi-clang CXX=arm-linux-androideabi-clang++ ./configure --build=x86_64-linux-gnu --host=arm-eabi --prefix=${PREFIX} --disable-rpath \ && make -j${NPROC} && make install ## Build BOOST RUN cd boost_${BOOST_VERSION} \ - && ./b2 --build-type=minimal link=static runtime-link=static --with-chrono --with-date_time --with-filesystem --with-program_options --with-regex --with-serialization --with-system --with-thread --with-locale --build-dir=android32 --stagedir=android32 toolset=clang threading=multi threadapi=pthread target-os=android -sICONV_PATH=${WORKDIR}/libiconv stage -j${NPROC} + && ./b2 --build-type=minimal link=static runtime-link=static --with-chrono --with-date_time --with-filesystem --with-program_options --with-regex --with-serialization --with-system --with-thread --with-locale --build-dir=android --stagedir=android toolset=clang threading=multi threadapi=pthread target-os=android -sICONV_PATH=${PREFIX} install -j${NPROC} #Note : we build openssl because the default lacks DSA1 @@ -97,8 +98,9 @@ RUN curl -s -O https://www.openssl.org/source/openssl-${OPENSSL_VERSION}.tar.gz no-asm \ no-shared --static \ --with-zlib-include=${WORKDIR}/zlib/include --with-zlib-lib=${WORKDIR}/zlib/lib \ + --prefix=${PREFIX} --openssldir=${PREFIX} \ && make -j${NPROC} \ - && cd .. && mv openssl-${OPENSSL_VERSION} openssl + && make install # ZMQ ARG ZMQ_VERSION=v4.2.5 @@ -107,7 +109,7 @@ RUN git clone https://github.com/zeromq/libzmq.git -b ${ZMQ_VERSION} \ && cd libzmq \ && test `git rev-parse HEAD` = ${ZMQ_HASH} || exit 1 \ && ./autogen.sh \ - && CC=clang CXX=clang++ ./configure --prefix=${PWD}/prebuilt --host=arm-linux-androideabi --enable-static --disable-shared \ + && CC=clang CXX=clang++ ./configure --prefix=${PREFIX} --host=arm-linux-androideabi --enable-static --disable-shared \ && make -j${NPROC} \ && make install @@ -116,14 +118,12 @@ ARG CPPZMQ_VERSION=v4.2.3 ARG CPPZMQ_HASH=6aa3ab686e916cb0e62df7fa7d12e0b13ae9fae6 RUN git clone https://github.com/zeromq/cppzmq.git -b ${CPPZMQ_VERSION} \ && cd cppzmq \ - && test `git rev-parse HEAD` = ${CPPZMQ_HASH} || exit 1 + && test `git rev-parse HEAD` = ${CPPZMQ_HASH} || exit 1 \ + && cp *.hpp ${PREFIX}/include ADD . /src RUN cd /src \ - && BOOST_ROOT=${WORKDIR}/boost_${BOOST_VERSION} BOOST_LIBRARYDIR=${WORKDIR}/boost_${BOOST_VERSION}/android32/lib/ \ - OPENSSL_ROOT_DIR=${WORKDIR}/openssl/ \ - CMAKE_INCLUDE_PATH="${WORKDIR}/cppzmq:${WORKDIR}/libzmq/prebuilt/include" \ - CMAKE_LIBRARY_PATH=${WORKDIR}/libzmq/prebuilt/lib \ - ANDROID_STANDALONE_TOOLCHAIN_PATH=${TOOLCHAIN_DIR} \ - CXXFLAGS="-I ${WORKDIR}/libzmq/prebuilt/include/" \ - PATH=${HOST_PATH} make release-static-android -j${NPROC} + && CMAKE_INCLUDE_PATH="${PREFIX}/include" \ + CMAKE_LIBRARY_PATH="${PREFIX}/lib" \ + ANDROID_STANDALONE_TOOLCHAIN_PATH=${TOOLCHAIN_DIR} \ + PATH=${HOST_PATH} make release-static-android -j${NPROC} From 89202ce4626e781db9c1ed93885ddf6f9b2ac40c Mon Sep 17 00:00:00 2001 From: MoroccanMalinois Date: Mon, 10 Sep 2018 10:12:43 -0400 Subject: [PATCH 0322/1404] Docker android: add libsodium --- utils/build_scripts/android32.Dockerfile | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/utils/build_scripts/android32.Dockerfile b/utils/build_scripts/android32.Dockerfile index 7ad1363bb98..2738d8d5708 100644 --- a/utils/build_scripts/android32.Dockerfile +++ b/utils/build_scripts/android32.Dockerfile @@ -121,6 +121,18 @@ RUN git clone https://github.com/zeromq/cppzmq.git -b ${CPPZMQ_VERSION} \ && test `git rev-parse HEAD` = ${CPPZMQ_HASH} || exit 1 \ && cp *.hpp ${PREFIX}/include +# Sodium +ARG SODIUM_VERSION=1.0.16 +ARG SODIUM_HASH=675149b9b8b66ff44152553fb3ebf9858128363d +RUN set -ex \ + && git clone https://github.com/jedisct1/libsodium.git -b ${SODIUM_VERSION} \ + && cd libsodium \ + && test `git rev-parse HEAD` = ${SODIUM_HASH} || exit 1 \ + && ./autogen.sh \ + && CC=clang CXX=clang++ ./configure --prefix=${PREFIX} --host=arm-linux-androideabi --enable-static --disable-shared \ + && make -j${NPROC} \ + && make install + ADD . /src RUN cd /src \ && CMAKE_INCLUDE_PATH="${PREFIX}/include" \ From 7a76354c8bcbe85f6553914241da5ac1bb932b37 Mon Sep 17 00:00:00 2001 From: naughtyfox Date: Mon, 10 Sep 2018 18:45:37 +0300 Subject: [PATCH 0323/1404] wallet2: import_multisig forward refresh exception --- src/wallet/wallet2.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index a2e36706eef..f6dfd3b4531 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -10878,7 +10878,12 @@ size_t wallet2::import_multisig(std::vector blobs) refresh(false); } - catch (...) {} + catch (...) + { + m_multisig_rescan_info = NULL; + m_multisig_rescan_k = NULL; + throw; + } m_multisig_rescan_info = NULL; m_multisig_rescan_k = NULL; From 26971d46fc06b8be4a6ecc0309f2650f354ddc91 Mon Sep 17 00:00:00 2001 From: naughtyfox Date: Mon, 10 Sep 2018 19:18:25 +0300 Subject: [PATCH 0324/1404] WalletAPI: 'hasMultisigPartialKeyImages' function added --- src/wallet/api/wallet.cpp | 14 ++++++++++++++ src/wallet/api/wallet.h | 1 + src/wallet/api/wallet2_api.h | 5 +++++ 3 files changed, 20 insertions(+) diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index 7b4ad27e4f9..43a023d3c63 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -1244,6 +1244,20 @@ size_t WalletImpl::importMultisigImages(const vector& images) { return 0; } +bool WalletImpl::hasMultisigPartialKeyImages() const { + try { + clearStatus(); + checkMultisigWalletReady(m_wallet); + + return m_wallet->has_multisig_partial_key_images(); + } catch (const exception& e) { + LOG_ERROR("Error on checking for partial multisig key images: ") << e.what(); + setStatusError(string(tr("Failed to check for partial multisig key images: ")) + e.what()); + } + + return false; +} + PendingTransaction* WalletImpl::restoreMultisigTransaction(const string& signData) { try { clearStatus(); diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h index 0f3b1ce040c..1ae4afb9e41 100644 --- a/src/wallet/api/wallet.h +++ b/src/wallet/api/wallet.h @@ -139,6 +139,7 @@ class WalletImpl : public Wallet bool finalizeMultisig(const std::vector& extraMultisigInfo) override; bool exportMultisigImages(std::string& images) override; size_t importMultisigImages(const std::vector& images) override; + bool hasMultisigPartialKeyImages() const override; PendingTransaction* restoreMultisigTransaction(const std::string& signData) override; PendingTransaction * createTransaction(const std::string &dst_addr, const std::string &payment_id, diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h index 5a52c6b175b..5c53f712e45 100644 --- a/src/wallet/api/wallet2_api.h +++ b/src/wallet/api/wallet2_api.h @@ -720,6 +720,11 @@ struct Wallet * @return number of imported images */ virtual size_t importMultisigImages(const std::vector& images) = 0; + /** + * @brief hasMultisigPartialKeyImages - checks if wallet needs to import multisig key images from other participants + * @return true if there are partial key images + */ + virtual bool hasMultisigPartialKeyImages() const = 0; /** * @brief restoreMultisigTransaction creates PendingTransaction from signData From 0806a23a6e07834934fb2614bf27ee5d70bf2e2d Mon Sep 17 00:00:00 2001 From: TheCharlatan Date: Sun, 18 Mar 2018 23:01:56 +0100 Subject: [PATCH 0325/1404] Initial depends addition Depends cross compiles project dependencies for linux, mac and windows and multiple architectures. Depends is original work by Cory Fields and used in bitcoin and a wide range of bitcoin related projects. --- contrib/depends/.gitignore | 10 + contrib/depends/Makefile | 181 ++ contrib/depends/README.md | 59 + contrib/depends/builders/darwin.mk | 22 + contrib/depends/builders/default.mk | 20 + contrib/depends/builders/linux.mk | 2 + contrib/depends/config.guess | 1462 +++++++++++++ contrib/depends/config.site.in | 99 + contrib/depends/config.sub | 1828 +++++++++++++++++ contrib/depends/description.md | 53 + contrib/depends/funcs.mk | 245 +++ contrib/depends/hosts/darwin.mk | 17 + contrib/depends/hosts/default.mk | 26 + contrib/depends/hosts/linux.mk | 31 + contrib/depends/hosts/mingw32.mk | 10 + contrib/depends/packages.md | 147 ++ contrib/depends/packages/bdb.mk | 31 + contrib/depends/packages/boost.mk | 41 + contrib/depends/packages/curl.mk | 27 + contrib/depends/packages/dbus.mk | 23 + contrib/depends/packages/expat.mk | 21 + contrib/depends/packages/fontconfig.mk | 22 + contrib/depends/packages/freetype.mk | 22 + contrib/depends/packages/libICE.mk | 23 + contrib/depends/packages/libSM.mk | 23 + contrib/depends/packages/libX11.mk | 23 + contrib/depends/packages/libXau.mk | 23 + contrib/depends/packages/libXext.mk | 22 + contrib/depends/packages/libxcb.mk | 35 + contrib/depends/packages/miniupnpc.mk | 28 + contrib/depends/packages/native_biplist.mk | 20 + contrib/depends/packages/native_ccache.mk | 25 + contrib/depends/packages/native_cctools.mk | 65 + contrib/depends/packages/native_cdrkit.mk | 26 + contrib/depends/packages/native_ds_store.mk | 17 + .../depends/packages/native_libdmg-hfsplus.mk | 22 + contrib/depends/packages/native_mac_alias.mk | 21 + contrib/depends/packages/openssl.mk | 78 + contrib/depends/packages/packages.mk | 20 + contrib/depends/packages/qrencode.mk | 22 + contrib/depends/packages/qt.mk | 198 ++ contrib/depends/packages/xcb_proto.mk | 27 + contrib/depends/packages/xextproto.mk | 21 + contrib/depends/packages/xproto.mk | 21 + contrib/depends/packages/xtrans.mk | 22 + contrib/depends/packages/zlib.mk | 28 + .../patches/native_biplist/sorted_list.patch | 29 + .../native_cdrkit/cdrkit-deterministic.patch | 86 + .../patches/native_mac_alias/python3.patch | 72 + .../patches/qt/fix-xcb-include-order.patch | 49 + .../depends/patches/qt/fix_qt_pkgconfig.patch | 11 + contrib/depends/patches/qt/mac-qmake.conf | 25 + contrib/depends/patches/qt/mingw-uuidof.patch | 44 + .../depends/patches/qt/pidlist_absolute.patch | 37 + 54 files changed, 5562 insertions(+) create mode 100644 contrib/depends/.gitignore create mode 100644 contrib/depends/Makefile create mode 100644 contrib/depends/README.md create mode 100644 contrib/depends/builders/darwin.mk create mode 100644 contrib/depends/builders/default.mk create mode 100644 contrib/depends/builders/linux.mk create mode 100755 contrib/depends/config.guess create mode 100644 contrib/depends/config.site.in create mode 100755 contrib/depends/config.sub create mode 100644 contrib/depends/description.md create mode 100644 contrib/depends/funcs.mk create mode 100644 contrib/depends/hosts/darwin.mk create mode 100644 contrib/depends/hosts/default.mk create mode 100644 contrib/depends/hosts/linux.mk create mode 100644 contrib/depends/hosts/mingw32.mk create mode 100644 contrib/depends/packages.md create mode 100644 contrib/depends/packages/bdb.mk create mode 100644 contrib/depends/packages/boost.mk create mode 100644 contrib/depends/packages/curl.mk create mode 100644 contrib/depends/packages/dbus.mk create mode 100644 contrib/depends/packages/expat.mk create mode 100644 contrib/depends/packages/fontconfig.mk create mode 100644 contrib/depends/packages/freetype.mk create mode 100644 contrib/depends/packages/libICE.mk create mode 100644 contrib/depends/packages/libSM.mk create mode 100644 contrib/depends/packages/libX11.mk create mode 100644 contrib/depends/packages/libXau.mk create mode 100644 contrib/depends/packages/libXext.mk create mode 100644 contrib/depends/packages/libxcb.mk create mode 100644 contrib/depends/packages/miniupnpc.mk create mode 100644 contrib/depends/packages/native_biplist.mk create mode 100644 contrib/depends/packages/native_ccache.mk create mode 100644 contrib/depends/packages/native_cctools.mk create mode 100644 contrib/depends/packages/native_cdrkit.mk create mode 100644 contrib/depends/packages/native_ds_store.mk create mode 100644 contrib/depends/packages/native_libdmg-hfsplus.mk create mode 100644 contrib/depends/packages/native_mac_alias.mk create mode 100644 contrib/depends/packages/openssl.mk create mode 100644 contrib/depends/packages/packages.mk create mode 100644 contrib/depends/packages/qrencode.mk create mode 100644 contrib/depends/packages/qt.mk create mode 100644 contrib/depends/packages/xcb_proto.mk create mode 100644 contrib/depends/packages/xextproto.mk create mode 100644 contrib/depends/packages/xproto.mk create mode 100644 contrib/depends/packages/xtrans.mk create mode 100644 contrib/depends/packages/zlib.mk create mode 100644 contrib/depends/patches/native_biplist/sorted_list.patch create mode 100644 contrib/depends/patches/native_cdrkit/cdrkit-deterministic.patch create mode 100644 contrib/depends/patches/native_mac_alias/python3.patch create mode 100644 contrib/depends/patches/qt/fix-xcb-include-order.patch create mode 100644 contrib/depends/patches/qt/fix_qt_pkgconfig.patch create mode 100644 contrib/depends/patches/qt/mac-qmake.conf create mode 100644 contrib/depends/patches/qt/mingw-uuidof.patch create mode 100644 contrib/depends/patches/qt/pidlist_absolute.patch diff --git a/contrib/depends/.gitignore b/contrib/depends/.gitignore new file mode 100644 index 00000000000..3cb4b9ac155 --- /dev/null +++ b/contrib/depends/.gitignore @@ -0,0 +1,10 @@ +SDKs/ +work/ +built/ +sources/ +config.site +x86_64* +i686* +mips* +arm* +aarch64* diff --git a/contrib/depends/Makefile b/contrib/depends/Makefile new file mode 100644 index 00000000000..dedb0674cf1 --- /dev/null +++ b/contrib/depends/Makefile @@ -0,0 +1,181 @@ +.NOTPARALLEL : + +SOURCES_PATH ?= $(BASEDIR)/sources +BASE_CACHE ?= $(BASEDIR)/built +SDK_PATH ?= $(BASEDIR)/SDKs +NO_QT ?= +NO_WALLET ?= +NO_UPNP ?= +FALLBACK_DOWNLOAD_PATH ?= https://bitcoincore.org/depends-sources + +BUILD = $(shell ./config.guess) +HOST ?= $(BUILD) +PATCHES_PATH = $(BASEDIR)/patches +BASEDIR = $(CURDIR) +HASH_LENGTH:=11 +DOWNLOAD_CONNECT_TIMEOUT:=10 +DOWNLOAD_RETRIES:=3 +HOST_ID_SALT ?= salt +BUILD_ID_SALT ?= salt + +host:=$(BUILD) +ifneq ($(HOST),) +host:=$(HOST) +host_toolchain:=$(HOST)- +endif + +ifneq ($(DEBUG),) +release_type=debug +else +release_type=release +endif + +base_build_dir=$(BASEDIR)/work/build +base_staging_dir=$(BASEDIR)/work/staging +base_download_dir=$(BASEDIR)/work/download +canonical_host:=$(shell ./config.sub $(HOST)) +build:=$(shell ./config.sub $(BUILD)) + +build_arch =$(firstword $(subst -, ,$(build))) +build_vendor=$(word 2,$(subst -, ,$(build))) +full_build_os:=$(subst $(build_arch)-$(build_vendor)-,,$(build)) +build_os:=$(findstring linux,$(full_build_os)) +build_os+=$(findstring darwin,$(full_build_os)) +build_os:=$(strip $(build_os)) +ifeq ($(build_os),) +build_os=$(full_build_os) +endif + +host_arch=$(firstword $(subst -, ,$(canonical_host))) +host_vendor=$(word 2,$(subst -, ,$(canonical_host))) +full_host_os:=$(subst $(host_arch)-$(host_vendor)-,,$(canonical_host)) +host_os:=$(findstring linux,$(full_host_os)) +host_os+=$(findstring darwin,$(full_host_os)) +host_os+=$(findstring mingw32,$(full_host_os)) +host_os:=$(strip $(host_os)) +ifeq ($(host_os),) +host_os=$(full_host_os) +endif + +$(host_arch)_$(host_os)_prefix=$(BASEDIR)/$(host) +$(host_arch)_$(host_os)_host=$(host) +host_prefix=$($(host_arch)_$(host_os)_prefix) +build_prefix=$(host_prefix)/native +build_host=$(build) + +AT_$(V):= +AT_:=@ +AT:=$(AT_$(V)) + +all: install + +include hosts/$(host_os).mk +include hosts/default.mk +include builders/$(build_os).mk +include builders/default.mk +include packages/packages.mk + +build_id_string:=$(BUILD_ID_SALT) +build_id_string+=$(shell $(build_CC) --version 2>/dev/null) +build_id_string+=$(shell $(build_AR) --version 2>/dev/null) +build_id_string+=$(shell $(build_CXX) --version 2>/dev/null) +build_id_string+=$(shell $(build_RANLIB) --version 2>/dev/null) +build_id_string+=$(shell $(build_STRIP) --version 2>/dev/null) + +$(host_arch)_$(host_os)_id_string:=$(HOST_ID_SALT) +$(host_arch)_$(host_os)_id_string+=$(shell $(host_CC) --version 2>/dev/null) +$(host_arch)_$(host_os)_id_string+=$(shell $(host_AR) --version 2>/dev/null) +$(host_arch)_$(host_os)_id_string+=$(shell $(host_CXX) --version 2>/dev/null) +$(host_arch)_$(host_os)_id_string+=$(shell $(host_RANLIB) --version 2>/dev/null) +$(host_arch)_$(host_os)_id_string+=$(shell $(host_STRIP) --version 2>/dev/null) + +qt_packages_$(NO_QT) = $(qt_packages) $(qt_$(host_os)_packages) $(qt_$(host_arch)_$(host_os)_packages) +wallet_packages_$(NO_WALLET) = $(wallet_packages) +upnp_packages_$(NO_UPNP) = $(upnp_packages) + +packages += $($(host_arch)_$(host_os)_packages) $($(host_os)_packages) $(qt_packages_) $(wallet_packages_) $(upnp_packages_) +native_packages += $($(host_arch)_$(host_os)_native_packages) $($(host_os)_native_packages) + +ifneq ($(qt_packages_),) +native_packages += $(qt_native_packages) +endif + +all_packages = $(packages) $(native_packages) + +meta_depends = Makefile funcs.mk builders/default.mk hosts/default.mk hosts/$(host_os).mk builders/$(build_os).mk + +$(host_arch)_$(host_os)_native_toolchain?=$($(host_os)_native_toolchain) + +include funcs.mk + +toolchain_path=$($($(host_arch)_$(host_os)_native_toolchain)_prefixbin) +final_build_id_long+=$(shell $(build_SHA256SUM) config.site.in) +final_build_id+=$(shell echo -n "$(final_build_id_long)" | $(build_SHA256SUM) | cut -c-$(HASH_LENGTH)) +$(host_prefix)/.stamp_$(final_build_id): $(native_packages) $(packages) + $(AT)rm -rf $(@D) + $(AT)mkdir -p $(@D) + $(AT)echo copying packages: $^ + $(AT)echo to: $(@D) + $(AT)cd $(@D); $(foreach package,$^, tar xf $($(package)_cached); ) + $(AT)touch $@ + +$(host_prefix)/share/config.site : config.site.in $(host_prefix)/.stamp_$(final_build_id) + $(AT)@mkdir -p $(@D) + $(AT)sed -e 's|@HOST@|$(host)|' \ + -e 's|@CC@|$(toolchain_path)$(host_CC)|' \ + -e 's|@CXX@|$(toolchain_path)$(host_CXX)|' \ + -e 's|@AR@|$(toolchain_path)$(host_AR)|' \ + -e 's|@RANLIB@|$(toolchain_path)$(host_RANLIB)|' \ + -e 's|@NM@|$(toolchain_path)$(host_NM)|' \ + -e 's|@STRIP@|$(toolchain_path)$(host_STRIP)|' \ + -e 's|@build_os@|$(build_os)|' \ + -e 's|@host_os@|$(host_os)|' \ + -e 's|@CFLAGS@|$(strip $(host_CFLAGS) $(host_$(release_type)_CFLAGS))|' \ + -e 's|@CXXFLAGS@|$(strip $(host_CXXFLAGS) $(host_$(release_type)_CXXFLAGS))|' \ + -e 's|@CPPFLAGS@|$(strip $(host_CPPFLAGS) $(host_$(release_type)_CPPFLAGS))|' \ + -e 's|@LDFLAGS@|$(strip $(host_LDFLAGS) $(host_$(release_type)_LDFLAGS))|' \ + -e 's|@no_qt@|$(NO_QT)|' \ + -e 's|@no_wallet@|$(NO_WALLET)|' \ + -e 's|@no_upnp@|$(NO_UPNP)|' \ + -e 's|@debug@|$(DEBUG)|' \ + $< > $@ + $(AT)touch $@ + + +define check_or_remove_cached + mkdir -p $(BASE_CACHE)/$(host)/$(package) && cd $(BASE_CACHE)/$(host)/$(package); \ + $(build_SHA256SUM) -c $($(package)_cached_checksum) >/dev/null 2>/dev/null || \ + ( rm -f $($(package)_cached_checksum); \ + if test -f "$($(package)_cached)"; then echo "Checksum mismatch for $(package). Forcing rebuild.."; rm -f $($(package)_cached_checksum) $($(package)_cached); fi ) +endef + +define check_or_remove_sources + mkdir -p $($(package)_source_dir); cd $($(package)_source_dir); \ + test -f $($(package)_fetched) && ( $(build_SHA256SUM) -c $($(package)_fetched) >/dev/null 2>/dev/null || \ + ( echo "Checksum missing or mismatched for $(package) source. Forcing re-download."; \ + rm -f $($(package)_all_sources) $($(1)_fetched))) || true +endef + +check-packages: + @$(foreach package,$(all_packages),$(call check_or_remove_cached,$(package));) +check-sources: + @$(foreach package,$(all_packages),$(call check_or_remove_sources,$(package));) + +$(host_prefix)/share/config.site: check-packages + +check-packages: check-sources + +install: check-packages $(host_prefix)/share/config.site + + +download-one: check-sources $(all_sources) + +download-osx: + @$(MAKE) -s HOST=x86_64-apple-darwin11 download-one +download-linux: + @$(MAKE) -s HOST=x86_64-unknown-linux-gnu download-one +download-win: + @$(MAKE) -s HOST=x86_64-w64-mingw32 download-one +download: download-osx download-linux download-win + +.PHONY: install cached download-one download-osx download-linux download-win download check-packages check-sources diff --git a/contrib/depends/README.md b/contrib/depends/README.md new file mode 100644 index 00000000000..6053c531b46 --- /dev/null +++ b/contrib/depends/README.md @@ -0,0 +1,59 @@ +### Usage + +To build dependencies for the current arch+OS: + + make + +To build for another arch/OS: + + make HOST=host-platform-triplet + +For example: + + make HOST=x86_64-w64-mingw32 -j4 + +A prefix will be generated that's suitable for plugging into Bitcoin's +configure. In the above example, a dir named x86_64-w64-mingw32 will be +created. To use it for Bitcoin: + + ./configure --prefix=`pwd`/depends/x86_64-w64-mingw32 + +Common `host-platform-triplets` for cross compilation are: + +- `i686-w64-mingw32` for Win32 +- `x86_64-w64-mingw32` for Win64 +- `x86_64-apple-darwin11` for MacOSX +- `arm-linux-gnueabihf` for Linux ARM 32 bit +- `aarch64-linux-gnu` for Linux ARM 64 bit + +No other options are needed, the paths are automatically configured. + +Dependency Options: +The following can be set when running make: make FOO=bar + + SOURCES_PATH: downloaded sources will be placed here + BASE_CACHE: built packages will be placed here + SDK_PATH: Path where sdk's can be found (used by OSX) + FALLBACK_DOWNLOAD_PATH: If a source file can't be fetched, try here before giving up + NO_QT: Don't download/build/cache qt and its dependencies + 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 + HOST_ID_SALT: Optional salt to use when generating host package ids + BUILD_ID_SALT: Optional salt to use when generating build package ids + +If some packages are not built, for example `make NO_WALLET=1`, the appropriate +options will be passed to bitcoin's configure. In this case, `--disable-wallet`. + +Additional targets: + + download: run 'make download' to fetch all sources without building them + download-osx: run 'make download-osx' to fetch all sources needed for osx builds + download-win: run 'make download-win' to fetch all sources needed for win builds + download-linux: run 'make download-linux' to fetch all sources needed for linux builds + +### Other documentation + +- [description.md](description.md): General description of the depends system +- [packages.md](packages.md): Steps for adding packages + diff --git a/contrib/depends/builders/darwin.mk b/contrib/depends/builders/darwin.mk new file mode 100644 index 00000000000..27f550ab036 --- /dev/null +++ b/contrib/depends/builders/darwin.mk @@ -0,0 +1,22 @@ +build_darwin_CC: = $(shell xcrun -f clang) +build_darwin_CXX: = $(shell xcrun -f clang++) +build_darwin_AR: = $(shell xcrun -f ar) +build_darwin_RANLIB: = $(shell xcrun -f ranlib) +build_darwin_STRIP: = $(shell xcrun -f strip) +build_darwin_OTOOL: = $(shell xcrun -f otool) +build_darwin_NM: = $(shell xcrun -f nm) +build_darwin_INSTALL_NAME_TOOL:=$(shell xcrun -f install_name_tool) +build_darwin_SHA256SUM = shasum -a 256 +build_darwin_DOWNLOAD = curl --location --fail --connect-timeout $(DOWNLOAD_CONNECT_TIMEOUT) --retry $(DOWNLOAD_RETRIES) -o + +#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++ +darwin_AR:=$(shell xcrun -f ar) +darwin_RANLIB:=$(shell xcrun -f ranlib) +darwin_STRIP:=$(shell xcrun -f strip) +darwin_LIBTOOL:=$(shell xcrun -f libtool) +darwin_OTOOL:=$(shell xcrun -f otool) +darwin_NM:=$(shell xcrun -f nm) +darwin_INSTALL_NAME_TOOL:=$(shell xcrun -f install_name_tool) +darwin_native_toolchain= diff --git a/contrib/depends/builders/default.mk b/contrib/depends/builders/default.mk new file mode 100644 index 00000000000..f097db65d60 --- /dev/null +++ b/contrib/depends/builders/default.mk @@ -0,0 +1,20 @@ +default_build_CC = gcc +default_build_CXX = g++ +default_build_AR = ar +default_build_RANLIB = ranlib +default_build_STRIP = strip +default_build_NM = nm +default_build_OTOOL = otool +default_build_INSTALL_NAME_TOOL = install_name_tool + +define add_build_tool_func +build_$(build_os)_$1 ?= $$(default_build_$1) +build_$(build_arch)_$(build_os)_$1 ?= $$(build_$(build_os)_$1) +build_$1=$$(build_$(build_arch)_$(build_os)_$1) +endef +$(foreach var,CC CXX AR RANLIB NM STRIP SHA256SUM DOWNLOAD OTOOL INSTALL_NAME_TOOL,$(eval $(call add_build_tool_func,$(var)))) +define add_build_flags_func +build_$(build_arch)_$(build_os)_$1 += $(build_$(build_os)_$1) +build_$1=$$(build_$(build_arch)_$(build_os)_$1) +endef +$(foreach flags, CFLAGS CXXFLAGS LDFLAGS, $(eval $(call add_build_flags_func,$(flags)))) diff --git a/contrib/depends/builders/linux.mk b/contrib/depends/builders/linux.mk new file mode 100644 index 00000000000..b03f4240104 --- /dev/null +++ b/contrib/depends/builders/linux.mk @@ -0,0 +1,2 @@ +build_linux_SHA256SUM = sha256sum +build_linux_DOWNLOAD = curl --location --fail --connect-timeout $(DOWNLOAD_CONNECT_TIMEOUT) --retry $(DOWNLOAD_RETRIES) -o diff --git a/contrib/depends/config.guess b/contrib/depends/config.guess new file mode 100755 index 00000000000..bbd48b60e88 --- /dev/null +++ b/contrib/depends/config.guess @@ -0,0 +1,1462 @@ +#! /bin/sh +# Attempt to guess a canonical system name. +# Copyright 1992-2017 Free Software Foundation, Inc. + +timestamp='2017-01-01' + +# This file is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (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 Exception is an additional permission under section 7 +# of the GNU General Public License, version 3 ("GPLv3"). +# +# Originally written by Per Bothner; maintained since 2000 by Ben Elliston. +# +# You can get the latest version of this script from: +# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess +# +# Please send patches to . + + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] + +Output the configuration name of the system \`$me' is run on. + +Operation modes: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to ." + +version="\ +GNU config.guess ($timestamp) + +Originally written by Per Bothner. +Copyright 1992-2017 Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit ;; + --version | -v ) + echo "$version" ; exit ;; + --help | --h* | -h ) + echo "$usage"; exit ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" >&2 + exit 1 ;; + * ) + break ;; + esac +done + +if test $# != 0; then + echo "$me: too many arguments$help" >&2 + exit 1 +fi + +trap 'exit 1' 1 2 15 + +# CC_FOR_BUILD -- compiler used by this script. Note that the use of a +# compiler to aid in system detection is discouraged as it requires +# temporary files to be created and, as you can see below, it is a +# headache to deal with in a portable fashion. + +# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still +# use `HOST_CC' if defined, but it is deprecated. + +# Portable tmp directory creation inspired by the Autoconf team. + +set_cc_for_build=' +trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ; +trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ; +: ${TMPDIR=/tmp} ; + { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || + { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } || + { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } || + { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ; +dummy=$tmp/dummy ; +tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ; +case $CC_FOR_BUILD,$HOST_CC,$CC in + ,,) echo "int x;" > $dummy.c ; + for c in cc gcc c89 c99 ; do + if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then + CC_FOR_BUILD="$c"; break ; + fi ; + done ; + if test x"$CC_FOR_BUILD" = x ; then + CC_FOR_BUILD=no_compiler_found ; + fi + ;; + ,,*) CC_FOR_BUILD=$CC ;; + ,*,*) CC_FOR_BUILD=$HOST_CC ;; +esac ; set_cc_for_build= ;' + +# This is needed to find uname on a Pyramid OSx when run in the BSD universe. +# (ghazi@noc.rutgers.edu 1994-08-24) +if (test -f /.attbin/uname) >/dev/null 2>&1 ; then + PATH=$PATH:/.attbin ; export PATH +fi + +UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown +UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown +UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown +UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown + +case "${UNAME_SYSTEM}" in +Linux|GNU|GNU/*) + # If the system lacks a compiler, then just pick glibc. + # We could probably try harder. + LIBC=gnu + + eval $set_cc_for_build + cat <<-EOF > $dummy.c + #include + #if defined(__UCLIBC__) + LIBC=uclibc + #elif defined(__dietlibc__) + LIBC=dietlibc + #else + LIBC=gnu + #endif + EOF + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC' | sed 's, ,,g'` + ;; +esac + +# Note: order is significant - the case branches are not exclusive. + +case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in + *:NetBSD:*:*) + # NetBSD (nbsd) targets should (where applicable) match one or + # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*, + # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently + # switched to ELF, *-*-netbsd* would select the old + # object file format. This provides both forward + # compatibility and a consistent mechanism for selecting the + # object file format. + # + # Note: NetBSD doesn't particularly care about the vendor + # portion of the name. We always set it to "unknown". + sysctl="sysctl -n hw.machine_arch" + UNAME_MACHINE_ARCH=`(uname -p 2>/dev/null || \ + /sbin/$sysctl 2>/dev/null || \ + /usr/sbin/$sysctl 2>/dev/null || \ + echo unknown)` + case "${UNAME_MACHINE_ARCH}" in + armeb) machine=armeb-unknown ;; + arm*) machine=arm-unknown ;; + sh3el) machine=shl-unknown ;; + sh3eb) machine=sh-unknown ;; + sh5el) machine=sh5le-unknown ;; + earmv*) + arch=`echo ${UNAME_MACHINE_ARCH} | sed -e 's,^e\(armv[0-9]\).*$,\1,'` + endian=`echo ${UNAME_MACHINE_ARCH} | sed -ne 's,^.*\(eb\)$,\1,p'` + machine=${arch}${endian}-unknown + ;; + *) machine=${UNAME_MACHINE_ARCH}-unknown ;; + esac + # The Operating System including object format, if it has switched + # to ELF recently (or will in the future) and ABI. + case "${UNAME_MACHINE_ARCH}" in + earm*) + os=netbsdelf + ;; + arm*|i386|m68k|ns32k|sh3*|sparc|vax) + eval $set_cc_for_build + if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ELF__ + then + # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). + # Return netbsd for either. FIX? + os=netbsd + else + os=netbsdelf + fi + ;; + *) + os=netbsd + ;; + esac + # Determine ABI tags. + case "${UNAME_MACHINE_ARCH}" in + earm*) + expr='s/^earmv[0-9]/-eabi/;s/eb$//' + abi=`echo ${UNAME_MACHINE_ARCH} | sed -e "$expr"` + ;; + esac + # The OS release + # Debian GNU/NetBSD machines have a different userland, and + # thus, need a distinct triplet. However, they do not need + # kernel version information, so it can be replaced with a + # suitable tag, in the style of linux-gnu. + case "${UNAME_VERSION}" in + Debian*) + release='-gnu' + ;; + *) + release=`echo ${UNAME_RELEASE} | sed -e 's/[-_].*//' | cut -d. -f1,2` + ;; + esac + # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: + # contains redundant information, the shorter form: + # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. + echo "${machine}-${os}${release}${abi}" + exit ;; + *:Bitrig:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'` + echo ${UNAME_MACHINE_ARCH}-unknown-bitrig${UNAME_RELEASE} + exit ;; + *:OpenBSD:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` + echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE} + exit ;; + *:LibertyBSD:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/^.*BSD\.//'` + echo ${UNAME_MACHINE_ARCH}-unknown-libertybsd${UNAME_RELEASE} + exit ;; + *:ekkoBSD:*:*) + echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE} + exit ;; + *:SolidBSD:*:*) + echo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE} + exit ;; + macppc:MirBSD:*:*) + echo powerpc-unknown-mirbsd${UNAME_RELEASE} + exit ;; + *:MirBSD:*:*) + echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE} + exit ;; + *:Sortix:*:*) + echo ${UNAME_MACHINE}-unknown-sortix + exit ;; + alpha:OSF1:*:*) + case $UNAME_RELEASE in + *4.0) + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` + ;; + *5.*) + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` + ;; + esac + # According to Compaq, /usr/sbin/psrinfo has been available on + # OSF/1 and Tru64 systems produced since 1995. I hope that + # covers most systems running today. This code pipes the CPU + # types through head -n 1, so we only detect the type of CPU 0. + ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` + case "$ALPHA_CPU_TYPE" in + "EV4 (21064)") + UNAME_MACHINE=alpha ;; + "EV4.5 (21064)") + UNAME_MACHINE=alpha ;; + "LCA4 (21066/21068)") + UNAME_MACHINE=alpha ;; + "EV5 (21164)") + UNAME_MACHINE=alphaev5 ;; + "EV5.6 (21164A)") + UNAME_MACHINE=alphaev56 ;; + "EV5.6 (21164PC)") + UNAME_MACHINE=alphapca56 ;; + "EV5.7 (21164PC)") + UNAME_MACHINE=alphapca57 ;; + "EV6 (21264)") + UNAME_MACHINE=alphaev6 ;; + "EV6.7 (21264A)") + UNAME_MACHINE=alphaev67 ;; + "EV6.8CB (21264C)") + UNAME_MACHINE=alphaev68 ;; + "EV6.8AL (21264B)") + UNAME_MACHINE=alphaev68 ;; + "EV6.8CX (21264D)") + UNAME_MACHINE=alphaev68 ;; + "EV6.9A (21264/EV69A)") + UNAME_MACHINE=alphaev69 ;; + "EV7 (21364)") + UNAME_MACHINE=alphaev7 ;; + "EV7.9 (21364A)") + UNAME_MACHINE=alphaev79 ;; + esac + # A Pn.n version is a patched version. + # A Vn.n version is a released version. + # A Tn.n version is a released field test version. + # A Xn.n version is an unreleased experimental baselevel. + # 1.2 uses "1.2" for uname -r. + echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz` + # Reset EXIT trap before exiting to avoid spurious non-zero exit code. + exitcode=$? + trap '' 0 + exit $exitcode ;; + Alpha\ *:Windows_NT*:*) + # How do we know it's Interix rather than the generic POSIX subsystem? + # Should we change UNAME_MACHINE based on the output of uname instead + # of the specific Alpha model? + echo alpha-pc-interix + exit ;; + 21064:Windows_NT:50:3) + echo alpha-dec-winnt3.5 + exit ;; + Amiga*:UNIX_System_V:4.0:*) + echo m68k-unknown-sysv4 + exit ;; + *:[Aa]miga[Oo][Ss]:*:*) + echo ${UNAME_MACHINE}-unknown-amigaos + exit ;; + *:[Mm]orph[Oo][Ss]:*:*) + echo ${UNAME_MACHINE}-unknown-morphos + exit ;; + *:OS/390:*:*) + echo i370-ibm-openedition + exit ;; + *:z/VM:*:*) + echo s390-ibm-zvmoe + exit ;; + *:OS400:*:*) + echo powerpc-ibm-os400 + exit ;; + arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) + echo arm-acorn-riscix${UNAME_RELEASE} + exit ;; + arm*:riscos:*:*|arm*:RISCOS:*:*) + echo arm-unknown-riscos + exit ;; + SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) + echo hppa1.1-hitachi-hiuxmpp + exit ;; + Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) + # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. + if test "`(/bin/universe) 2>/dev/null`" = att ; then + echo pyramid-pyramid-sysv3 + else + echo pyramid-pyramid-bsd + fi + exit ;; + NILE*:*:*:dcosx) + echo pyramid-pyramid-svr4 + exit ;; + DRS?6000:unix:4.0:6*) + echo sparc-icl-nx6 + exit ;; + DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) + case `/usr/bin/uname -p` in + sparc) echo sparc-icl-nx7; exit ;; + esac ;; + s390x:SunOS:*:*) + echo ${UNAME_MACHINE}-ibm-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4H:SunOS:5.*:*) + echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) + echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*) + echo i386-pc-auroraux${UNAME_RELEASE} + exit ;; + i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) + eval $set_cc_for_build + SUN_ARCH=i386 + # If there is a compiler, see if it is configured for 64-bit objects. + # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. + # This test works for both compilers. + if [ "$CC_FOR_BUILD" != no_compiler_found ]; then + if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ + (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_64BIT_ARCH >/dev/null + then + SUN_ARCH=x86_64 + fi + fi + echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4*:SunOS:6*:*) + # According to config.sub, this is the proper way to canonicalize + # SunOS6. Hard to guess exactly what SunOS6 will be like, but + # it's likely to be more like Solaris than SunOS4. + echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4*:SunOS:*:*) + case "`/usr/bin/arch -k`" in + Series*|S4*) + UNAME_RELEASE=`uname -v` + ;; + esac + # Japanese Language versions have a version number like `4.1.3-JL'. + echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'` + exit ;; + sun3*:SunOS:*:*) + echo m68k-sun-sunos${UNAME_RELEASE} + exit ;; + sun*:*:4.2BSD:*) + UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` + test "x${UNAME_RELEASE}" = x && UNAME_RELEASE=3 + case "`/bin/arch`" in + sun3) + echo m68k-sun-sunos${UNAME_RELEASE} + ;; + sun4) + echo sparc-sun-sunos${UNAME_RELEASE} + ;; + esac + exit ;; + aushp:SunOS:*:*) + echo sparc-auspex-sunos${UNAME_RELEASE} + exit ;; + # The situation for MiNT is a little confusing. The machine name + # can be virtually everything (everything which is not + # "atarist" or "atariste" at least should have a processor + # > m68000). The system name ranges from "MiNT" over "FreeMiNT" + # to the lowercase version "mint" (or "freemint"). Finally + # the system name "TOS" denotes a system which is actually not + # MiNT. But MiNT is downward compatible to TOS, so this should + # be no problem. + atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit ;; + atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit ;; + *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit ;; + milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) + echo m68k-milan-mint${UNAME_RELEASE} + exit ;; + hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) + echo m68k-hades-mint${UNAME_RELEASE} + exit ;; + *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) + echo m68k-unknown-mint${UNAME_RELEASE} + exit ;; + m68k:machten:*:*) + echo m68k-apple-machten${UNAME_RELEASE} + exit ;; + powerpc:machten:*:*) + echo powerpc-apple-machten${UNAME_RELEASE} + exit ;; + RISC*:Mach:*:*) + echo mips-dec-mach_bsd4.3 + exit ;; + RISC*:ULTRIX:*:*) + echo mips-dec-ultrix${UNAME_RELEASE} + exit ;; + VAX*:ULTRIX*:*:*) + echo vax-dec-ultrix${UNAME_RELEASE} + exit ;; + 2020:CLIX:*:* | 2430:CLIX:*:*) + echo clipper-intergraph-clix${UNAME_RELEASE} + exit ;; + mips:*:*:UMIPS | mips:*:*:RISCos) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c +#ifdef __cplusplus +#include /* for printf() prototype */ + int main (int argc, char *argv[]) { +#else + int main (argc, argv) int argc; char *argv[]; { +#endif + #if defined (host_mips) && defined (MIPSEB) + #if defined (SYSTYPE_SYSV) + printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_SVR4) + printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) + printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0); + #endif + #endif + exit (-1); + } +EOF + $CC_FOR_BUILD -o $dummy $dummy.c && + dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` && + SYSTEM_NAME=`$dummy $dummyarg` && + { echo "$SYSTEM_NAME"; exit; } + echo mips-mips-riscos${UNAME_RELEASE} + exit ;; + Motorola:PowerMAX_OS:*:*) + echo powerpc-motorola-powermax + exit ;; + Motorola:*:4.3:PL8-*) + echo powerpc-harris-powermax + exit ;; + Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) + echo powerpc-harris-powermax + exit ;; + Night_Hawk:Power_UNIX:*:*) + echo powerpc-harris-powerunix + exit ;; + m88k:CX/UX:7*:*) + echo m88k-harris-cxux7 + exit ;; + m88k:*:4*:R4*) + echo m88k-motorola-sysv4 + exit ;; + m88k:*:3*:R3*) + echo m88k-motorola-sysv3 + exit ;; + AViiON:dgux:*:*) + # DG/UX returns AViiON for all architectures + UNAME_PROCESSOR=`/usr/bin/uname -p` + if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ] + then + if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \ + [ ${TARGET_BINARY_INTERFACE}x = x ] + then + echo m88k-dg-dgux${UNAME_RELEASE} + else + echo m88k-dg-dguxbcs${UNAME_RELEASE} + fi + else + echo i586-dg-dgux${UNAME_RELEASE} + fi + exit ;; + M88*:DolphinOS:*:*) # DolphinOS (SVR3) + echo m88k-dolphin-sysv3 + exit ;; + M88*:*:R3*:*) + # Delta 88k system running SVR3 + echo m88k-motorola-sysv3 + exit ;; + XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) + echo m88k-tektronix-sysv3 + exit ;; + Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) + echo m68k-tektronix-bsd + exit ;; + *:IRIX*:*:*) + echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` + exit ;; + ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. + echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id + exit ;; # Note that: echo "'`uname -s`'" gives 'AIX ' + i*86:AIX:*:*) + echo i386-ibm-aix + exit ;; + ia64:AIX:*:*) + if [ -x /usr/bin/oslevel ] ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + fi + echo ${UNAME_MACHINE}-ibm-aix${IBM_REV} + exit ;; + *:AIX:2:3) + if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include + + main() + { + if (!__power_pc()) + exit(1); + puts("powerpc-ibm-aix3.2.5"); + exit(0); + } +EOF + if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` + then + echo "$SYSTEM_NAME" + else + echo rs6000-ibm-aix3.2.5 + fi + elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then + echo rs6000-ibm-aix3.2.4 + else + echo rs6000-ibm-aix3.2 + fi + exit ;; + *:AIX:*:[4567]) + IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` + if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then + IBM_ARCH=rs6000 + else + IBM_ARCH=powerpc + fi + if [ -x /usr/bin/lslpp ] ; then + IBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc | + awk -F: '{ print $3 }' | sed s/[0-9]*$/0/` + else + IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + fi + echo ${IBM_ARCH}-ibm-aix${IBM_REV} + exit ;; + *:AIX:*:*) + echo rs6000-ibm-aix + exit ;; + ibmrt:4.4BSD:*|romp-ibm:BSD:*) + echo romp-ibm-bsd4.4 + exit ;; + ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and + echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to + exit ;; # report: romp-ibm BSD 4.3 + *:BOSX:*:*) + echo rs6000-bull-bosx + exit ;; + DPX/2?00:B.O.S.:*:*) + echo m68k-bull-sysv3 + exit ;; + 9000/[34]??:4.3bsd:1.*:*) + echo m68k-hp-bsd + exit ;; + hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) + echo m68k-hp-bsd4.4 + exit ;; + 9000/[34678]??:HP-UX:*:*) + HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` + case "${UNAME_MACHINE}" in + 9000/31? ) HP_ARCH=m68000 ;; + 9000/[34]?? ) HP_ARCH=m68k ;; + 9000/[678][0-9][0-9]) + if [ -x /usr/bin/getconf ]; then + sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` + sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` + case "${sc_cpu_version}" in + 523) HP_ARCH=hppa1.0 ;; # CPU_PA_RISC1_0 + 528) HP_ARCH=hppa1.1 ;; # CPU_PA_RISC1_1 + 532) # CPU_PA_RISC2_0 + case "${sc_kernel_bits}" in + 32) HP_ARCH=hppa2.0n ;; + 64) HP_ARCH=hppa2.0w ;; + '') HP_ARCH=hppa2.0 ;; # HP-UX 10.20 + esac ;; + esac + fi + if [ "${HP_ARCH}" = "" ]; then + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + + #define _HPUX_SOURCE + #include + #include + + int main () + { + #if defined(_SC_KERNEL_BITS) + long bits = sysconf(_SC_KERNEL_BITS); + #endif + long cpu = sysconf (_SC_CPU_VERSION); + + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1"); break; + case CPU_PA_RISC2_0: + #if defined(_SC_KERNEL_BITS) + switch (bits) + { + case 64: puts ("hppa2.0w"); break; + case 32: puts ("hppa2.0n"); break; + default: puts ("hppa2.0"); break; + } break; + #else /* !defined(_SC_KERNEL_BITS) */ + puts ("hppa2.0"); break; + #endif + default: puts ("hppa1.0"); break; + } + exit (0); + } +EOF + (CCOPTS="" $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy` + test -z "$HP_ARCH" && HP_ARCH=hppa + fi ;; + esac + if [ ${HP_ARCH} = hppa2.0w ] + then + eval $set_cc_for_build + + # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating + # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler + # generating 64-bit code. GNU and HP use different nomenclature: + # + # $ CC_FOR_BUILD=cc ./config.guess + # => hppa2.0w-hp-hpux11.23 + # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess + # => hppa64-hp-hpux11.23 + + if echo __LP64__ | (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | + grep -q __LP64__ + then + HP_ARCH=hppa2.0w + else + HP_ARCH=hppa64 + fi + fi + echo ${HP_ARCH}-hp-hpux${HPUX_REV} + exit ;; + ia64:HP-UX:*:*) + HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` + echo ia64-hp-hpux${HPUX_REV} + exit ;; + 3050*:HI-UX:*:*) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include + int + main () + { + long cpu = sysconf (_SC_CPU_VERSION); + /* The order matters, because CPU_IS_HP_MC68K erroneously returns + true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct + results, however. */ + if (CPU_IS_PA_RISC (cpu)) + { + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; + case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; + default: puts ("hppa-hitachi-hiuxwe2"); break; + } + } + else if (CPU_IS_HP_MC68K (cpu)) + puts ("m68k-hitachi-hiuxwe2"); + else puts ("unknown-hitachi-hiuxwe2"); + exit (0); + } +EOF + $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` && + { echo "$SYSTEM_NAME"; exit; } + echo unknown-hitachi-hiuxwe2 + exit ;; + 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) + echo hppa1.1-hp-bsd + exit ;; + 9000/8??:4.3bsd:*:*) + echo hppa1.0-hp-bsd + exit ;; + *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) + echo hppa1.0-hp-mpeix + exit ;; + hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) + echo hppa1.1-hp-osf + exit ;; + hp8??:OSF1:*:*) + echo hppa1.0-hp-osf + exit ;; + i*86:OSF1:*:*) + if [ -x /usr/sbin/sysversion ] ; then + echo ${UNAME_MACHINE}-unknown-osf1mk + else + echo ${UNAME_MACHINE}-unknown-osf1 + fi + exit ;; + parisc*:Lites*:*:*) + echo hppa1.1-hp-lites + exit ;; + C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) + echo c1-convex-bsd + exit ;; + C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit ;; + C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) + echo c34-convex-bsd + exit ;; + C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) + echo c38-convex-bsd + exit ;; + C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) + echo c4-convex-bsd + exit ;; + CRAY*Y-MP:*:*:*) + echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*[A-Z]90:*:*:*) + echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \ + | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ + -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ + -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*TS:*:*:*) + echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*T3E:*:*:*) + echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*SV1:*:*:*) + echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + *:UNICOS/mp:*:*) + echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) + FUJITSU_PROC=`uname -m | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz` + FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` + FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` + echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" + exit ;; + 5000:UNIX_System_V:4.*:*) + FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` + FUJITSU_REL=`echo ${UNAME_RELEASE} | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/ /_/'` + echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" + exit ;; + i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) + echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} + exit ;; + sparc*:BSD/OS:*:*) + echo sparc-unknown-bsdi${UNAME_RELEASE} + exit ;; + *:BSD/OS:*:*) + echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE} + exit ;; + *:FreeBSD:*:*) + UNAME_PROCESSOR=`/usr/bin/uname -p` + case ${UNAME_PROCESSOR} in + amd64) + echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; + *) + echo ${UNAME_PROCESSOR}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; + esac + exit ;; + i*:CYGWIN*:*) + echo ${UNAME_MACHINE}-pc-cygwin + exit ;; + *:MINGW64*:*) + echo ${UNAME_MACHINE}-pc-mingw64 + exit ;; + *:MINGW*:*) + echo ${UNAME_MACHINE}-pc-mingw32 + exit ;; + *:MSYS*:*) + echo ${UNAME_MACHINE}-pc-msys + exit ;; + i*:windows32*:*) + # uname -m includes "-pc" on this system. + echo ${UNAME_MACHINE}-mingw32 + exit ;; + i*:PW*:*) + echo ${UNAME_MACHINE}-pc-pw32 + exit ;; + *:Interix*:*) + case ${UNAME_MACHINE} in + x86) + echo i586-pc-interix${UNAME_RELEASE} + exit ;; + authenticamd | genuineintel | EM64T) + echo x86_64-unknown-interix${UNAME_RELEASE} + exit ;; + IA64) + echo ia64-unknown-interix${UNAME_RELEASE} + exit ;; + esac ;; + [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*) + echo i${UNAME_MACHINE}-pc-mks + exit ;; + 8664:Windows_NT:*) + echo x86_64-pc-mks + exit ;; + i*:Windows_NT*:* | Pentium*:Windows_NT*:*) + # How do we know it's Interix rather than the generic POSIX subsystem? + # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we + # UNAME_MACHINE based on the output of uname instead of i386? + echo i586-pc-interix + exit ;; + i*:UWIN*:*) + echo ${UNAME_MACHINE}-pc-uwin + exit ;; + amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) + echo x86_64-unknown-cygwin + exit ;; + p*:CYGWIN*:*) + echo powerpcle-unknown-cygwin + exit ;; + prep*:SunOS:5.*:*) + echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + *:GNU:*:*) + # the GNU system + echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-${LIBC}`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` + exit ;; + *:GNU/*:*:*) + # other systems with GNU libc and userland + echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]"``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-${LIBC} + exit ;; + i*86:Minix:*:*) + echo ${UNAME_MACHINE}-pc-minix + exit ;; + aarch64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + aarch64_be:Linux:*:*) + UNAME_MACHINE=aarch64_be + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + alpha:Linux:*:*) + case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in + EV5) UNAME_MACHINE=alphaev5 ;; + EV56) UNAME_MACHINE=alphaev56 ;; + PCA56) UNAME_MACHINE=alphapca56 ;; + PCA57) UNAME_MACHINE=alphapca56 ;; + EV6) UNAME_MACHINE=alphaev6 ;; + EV67) UNAME_MACHINE=alphaev67 ;; + EV68*) UNAME_MACHINE=alphaev68 ;; + esac + objdump --private-headers /bin/sh | grep -q ld.so.1 + if test "$?" = 0 ; then LIBC=gnulibc1 ; fi + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + arc:Linux:*:* | arceb:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + arm*:Linux:*:*) + eval $set_cc_for_build + if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ARM_EABI__ + then + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + else + if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ARM_PCS_VFP + then + echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabi + else + echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabihf + fi + fi + exit ;; + avr32*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + cris:Linux:*:*) + echo ${UNAME_MACHINE}-axis-linux-${LIBC} + exit ;; + crisv32:Linux:*:*) + echo ${UNAME_MACHINE}-axis-linux-${LIBC} + exit ;; + e2k:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + frv:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + hexagon:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + i*86:Linux:*:*) + echo ${UNAME_MACHINE}-pc-linux-${LIBC} + exit ;; + ia64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + k1om:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + m32r*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + m68*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + mips:Linux:*:* | mips64:Linux:*:*) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #undef CPU + #undef ${UNAME_MACHINE} + #undef ${UNAME_MACHINE}el + #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) + CPU=${UNAME_MACHINE}el + #else + #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) + CPU=${UNAME_MACHINE} + #else + CPU= + #endif + #endif +EOF + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'` + test x"${CPU}" != x && { echo "${CPU}-unknown-linux-${LIBC}"; exit; } + ;; + mips64el:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + openrisc*:Linux:*:*) + echo or1k-unknown-linux-${LIBC} + exit ;; + or32:Linux:*:* | or1k*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + padre:Linux:*:*) + echo sparc-unknown-linux-${LIBC} + exit ;; + parisc64:Linux:*:* | hppa64:Linux:*:*) + echo hppa64-unknown-linux-${LIBC} + exit ;; + parisc:Linux:*:* | hppa:Linux:*:*) + # Look for CPU level + case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in + PA7*) echo hppa1.1-unknown-linux-${LIBC} ;; + PA8*) echo hppa2.0-unknown-linux-${LIBC} ;; + *) echo hppa-unknown-linux-${LIBC} ;; + esac + exit ;; + ppc64:Linux:*:*) + echo powerpc64-unknown-linux-${LIBC} + exit ;; + ppc:Linux:*:*) + echo powerpc-unknown-linux-${LIBC} + exit ;; + ppc64le:Linux:*:*) + echo powerpc64le-unknown-linux-${LIBC} + exit ;; + ppcle:Linux:*:*) + echo powerpcle-unknown-linux-${LIBC} + exit ;; + riscv32:Linux:*:* | riscv64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + s390:Linux:*:* | s390x:Linux:*:*) + echo ${UNAME_MACHINE}-ibm-linux-${LIBC} + exit ;; + sh64*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + sh*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + sparc:Linux:*:* | sparc64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + tile*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + vax:Linux:*:*) + echo ${UNAME_MACHINE}-dec-linux-${LIBC} + exit ;; + x86_64:Linux:*:*) + echo ${UNAME_MACHINE}-pc-linux-${LIBC} + exit ;; + xtensa*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + i*86:DYNIX/ptx:4*:*) + # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. + # earlier versions are messed up and put the nodename in both + # sysname and nodename. + echo i386-sequent-sysv4 + exit ;; + i*86:UNIX_SV:4.2MP:2.*) + # Unixware is an offshoot of SVR4, but it has its own version + # number series starting with 2... + # I am not positive that other SVR4 systems won't match this, + # I just have to hope. -- rms. + # Use sysv4.2uw... so that sysv4* matches it. + echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION} + exit ;; + i*86:OS/2:*:*) + # If we were able to find `uname', then EMX Unix compatibility + # is probably installed. + echo ${UNAME_MACHINE}-pc-os2-emx + exit ;; + i*86:XTS-300:*:STOP) + echo ${UNAME_MACHINE}-unknown-stop + exit ;; + i*86:atheos:*:*) + echo ${UNAME_MACHINE}-unknown-atheos + exit ;; + i*86:syllable:*:*) + echo ${UNAME_MACHINE}-pc-syllable + exit ;; + i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*) + echo i386-unknown-lynxos${UNAME_RELEASE} + exit ;; + i*86:*DOS:*:*) + echo ${UNAME_MACHINE}-pc-msdosdjgpp + exit ;; + i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*) + UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'` + if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then + echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL} + else + echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL} + fi + exit ;; + i*86:*:5:[678]*) + # UnixWare 7.x, OpenUNIX and OpenServer 6. + case `/bin/uname -X | grep "^Machine"` in + *486*) UNAME_MACHINE=i486 ;; + *Pentium) UNAME_MACHINE=i586 ;; + *Pent*|*Celeron) UNAME_MACHINE=i686 ;; + esac + echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} + exit ;; + i*86:*:3.2:*) + if test -f /usr/options/cb.name; then + UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then + UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` + (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 + (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ + && UNAME_MACHINE=i586 + (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ + && UNAME_MACHINE=i686 + (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ + && UNAME_MACHINE=i686 + echo ${UNAME_MACHINE}-pc-sco$UNAME_REL + else + echo ${UNAME_MACHINE}-pc-sysv32 + fi + exit ;; + pc:*:*:*) + # Left here for compatibility: + # uname -m prints for DJGPP always 'pc', but it prints nothing about + # the processor, so we play safe by assuming i586. + # Note: whatever this is, it MUST be the same as what config.sub + # prints for the "djgpp" host, or else GDB configure will decide that + # this is a cross-build. + echo i586-pc-msdosdjgpp + exit ;; + Intel:Mach:3*:*) + echo i386-pc-mach3 + exit ;; + paragon:*:*:*) + echo i860-intel-osf1 + exit ;; + i860:*:4.*:*) # i860-SVR4 + if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then + echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 + else # Add other i860-SVR4 vendors below as they are discovered. + echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4 + fi + exit ;; + mini*:CTIX:SYS*5:*) + # "miniframe" + echo m68010-convergent-sysv + exit ;; + mc68k:UNIX:SYSTEM5:3.51m) + echo m68k-convergent-sysv + exit ;; + M680?0:D-NIX:5.3:*) + echo m68k-diab-dnix + exit ;; + M68*:*:R3V[5678]*:*) + test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;; + 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) + OS_REL='' + test -r /etc/.relid \ + && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4.3${OS_REL}; exit; } + /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ + && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; + 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4; exit; } ;; + NCR*:*:4.2:* | MPRAS*:*:4.2:*) + OS_REL='.3' + test -r /etc/.relid \ + && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4.3${OS_REL}; exit; } + /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ + && { echo i586-ncr-sysv4.3${OS_REL}; exit; } + /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \ + && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; + m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) + echo m68k-unknown-lynxos${UNAME_RELEASE} + exit ;; + mc68030:UNIX_System_V:4.*:*) + echo m68k-atari-sysv4 + exit ;; + TSUNAMI:LynxOS:2.*:*) + echo sparc-unknown-lynxos${UNAME_RELEASE} + exit ;; + rs6000:LynxOS:2.*:*) + echo rs6000-unknown-lynxos${UNAME_RELEASE} + exit ;; + PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*) + echo powerpc-unknown-lynxos${UNAME_RELEASE} + exit ;; + SM[BE]S:UNIX_SV:*:*) + echo mips-dde-sysv${UNAME_RELEASE} + exit ;; + RM*:ReliantUNIX-*:*:*) + echo mips-sni-sysv4 + exit ;; + RM*:SINIX-*:*:*) + echo mips-sni-sysv4 + exit ;; + *:SINIX-*:*:*) + if uname -p 2>/dev/null >/dev/null ; then + UNAME_MACHINE=`(uname -p) 2>/dev/null` + echo ${UNAME_MACHINE}-sni-sysv4 + else + echo ns32k-sni-sysv + fi + exit ;; + PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort + # says + echo i586-unisys-sysv4 + exit ;; + *:UNIX_System_V:4*:FTX*) + # From Gerald Hewes . + # How about differentiating between stratus architectures? -djm + echo hppa1.1-stratus-sysv4 + exit ;; + *:*:*:FTX*) + # From seanf@swdc.stratus.com. + echo i860-stratus-sysv4 + exit ;; + i*86:VOS:*:*) + # From Paul.Green@stratus.com. + echo ${UNAME_MACHINE}-stratus-vos + exit ;; + *:VOS:*:*) + # From Paul.Green@stratus.com. + echo hppa1.1-stratus-vos + exit ;; + mc68*:A/UX:*:*) + echo m68k-apple-aux${UNAME_RELEASE} + exit ;; + news*:NEWS-OS:6*:*) + echo mips-sony-newsos6 + exit ;; + R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) + if [ -d /usr/nec ]; then + echo mips-nec-sysv${UNAME_RELEASE} + else + echo mips-unknown-sysv${UNAME_RELEASE} + fi + exit ;; + BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. + echo powerpc-be-beos + exit ;; + BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. + echo powerpc-apple-beos + exit ;; + BePC:BeOS:*:*) # BeOS running on Intel PC compatible. + echo i586-pc-beos + exit ;; + BePC:Haiku:*:*) # Haiku running on Intel PC compatible. + echo i586-pc-haiku + exit ;; + x86_64:Haiku:*:*) + echo x86_64-unknown-haiku + exit ;; + SX-4:SUPER-UX:*:*) + echo sx4-nec-superux${UNAME_RELEASE} + exit ;; + SX-5:SUPER-UX:*:*) + echo sx5-nec-superux${UNAME_RELEASE} + exit ;; + SX-6:SUPER-UX:*:*) + echo sx6-nec-superux${UNAME_RELEASE} + exit ;; + SX-7:SUPER-UX:*:*) + echo sx7-nec-superux${UNAME_RELEASE} + exit ;; + SX-8:SUPER-UX:*:*) + echo sx8-nec-superux${UNAME_RELEASE} + exit ;; + SX-8R:SUPER-UX:*:*) + echo sx8r-nec-superux${UNAME_RELEASE} + exit ;; + SX-ACE:SUPER-UX:*:*) + echo sxace-nec-superux${UNAME_RELEASE} + exit ;; + Power*:Rhapsody:*:*) + echo powerpc-apple-rhapsody${UNAME_RELEASE} + exit ;; + *:Rhapsody:*:*) + echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} + exit ;; + *:Darwin:*:*) + UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown + eval $set_cc_for_build + if test "$UNAME_PROCESSOR" = unknown ; then + UNAME_PROCESSOR=powerpc + fi + if test `echo "$UNAME_RELEASE" | sed -e 's/\..*//'` -le 10 ; then + if [ "$CC_FOR_BUILD" != no_compiler_found ]; then + if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ + (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_64BIT_ARCH >/dev/null + then + case $UNAME_PROCESSOR in + i386) UNAME_PROCESSOR=x86_64 ;; + powerpc) UNAME_PROCESSOR=powerpc64 ;; + esac + fi + fi + elif test "$UNAME_PROCESSOR" = i386 ; then + # Avoid executing cc on OS X 10.9, as it ships with a stub + # that puts up a graphical alert prompting to install + # developer tools. Any system running Mac OS X 10.7 or + # later (Darwin 11 and later) is required to have a 64-bit + # processor. This is not true of the ARM version of Darwin + # that Apple uses in portable devices. + UNAME_PROCESSOR=x86_64 + fi + echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE} + exit ;; + *:procnto*:*:* | *:QNX:[0123456789]*:*) + UNAME_PROCESSOR=`uname -p` + if test "$UNAME_PROCESSOR" = x86; then + UNAME_PROCESSOR=i386 + UNAME_MACHINE=pc + fi + echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE} + exit ;; + *:QNX:*:4*) + echo i386-pc-qnx + exit ;; + NEO-?:NONSTOP_KERNEL:*:*) + echo neo-tandem-nsk${UNAME_RELEASE} + exit ;; + NSE-*:NONSTOP_KERNEL:*:*) + echo nse-tandem-nsk${UNAME_RELEASE} + exit ;; + NSR-?:NONSTOP_KERNEL:*:*) + echo nsr-tandem-nsk${UNAME_RELEASE} + exit ;; + *:NonStop-UX:*:*) + echo mips-compaq-nonstopux + exit ;; + BS2000:POSIX*:*:*) + echo bs2000-siemens-sysv + exit ;; + DS/*:UNIX_System_V:*:*) + echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE} + exit ;; + *:Plan9:*:*) + # "uname -m" is not consistent, so use $cputype instead. 386 + # is converted to i386 for consistency with other x86 + # operating systems. + if test "$cputype" = 386; then + UNAME_MACHINE=i386 + else + UNAME_MACHINE="$cputype" + fi + echo ${UNAME_MACHINE}-unknown-plan9 + exit ;; + *:TOPS-10:*:*) + echo pdp10-unknown-tops10 + exit ;; + *:TENEX:*:*) + echo pdp10-unknown-tenex + exit ;; + KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) + echo pdp10-dec-tops20 + exit ;; + XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) + echo pdp10-xkl-tops20 + exit ;; + *:TOPS-20:*:*) + echo pdp10-unknown-tops20 + exit ;; + *:ITS:*:*) + echo pdp10-unknown-its + exit ;; + SEI:*:*:SEIUX) + echo mips-sei-seiux${UNAME_RELEASE} + exit ;; + *:DragonFly:*:*) + echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` + exit ;; + *:*VMS:*:*) + UNAME_MACHINE=`(uname -p) 2>/dev/null` + case "${UNAME_MACHINE}" in + A*) echo alpha-dec-vms ; exit ;; + I*) echo ia64-dec-vms ; exit ;; + V*) echo vax-dec-vms ; exit ;; + esac ;; + *:XENIX:*:SysV) + echo i386-pc-xenix + exit ;; + i*86:skyos:*:*) + echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE} | sed -e 's/ .*$//'` + exit ;; + i*86:rdos:*:*) + echo ${UNAME_MACHINE}-pc-rdos + exit ;; + i*86:AROS:*:*) + echo ${UNAME_MACHINE}-pc-aros + exit ;; + x86_64:VMkernel:*:*) + echo ${UNAME_MACHINE}-unknown-esx + exit ;; + amd64:Isilon\ OneFS:*:*) + echo x86_64-unknown-onefs + exit ;; +esac + +cat >&2 </dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null` + +hostinfo = `(hostinfo) 2>/dev/null` +/bin/universe = `(/bin/universe) 2>/dev/null` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` +/bin/arch = `(/bin/arch) 2>/dev/null` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` + +UNAME_MACHINE = ${UNAME_MACHINE} +UNAME_RELEASE = ${UNAME_RELEASE} +UNAME_SYSTEM = ${UNAME_SYSTEM} +UNAME_VERSION = ${UNAME_VERSION} +EOF + +exit 1 + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/contrib/depends/config.site.in b/contrib/depends/config.site.in new file mode 100644 index 00000000000..3d7c9fd43c7 --- /dev/null +++ b/contrib/depends/config.site.in @@ -0,0 +1,99 @@ +depends_prefix="`dirname ${ac_site_file}`/.." + +cross_compiling=maybe +host_alias=@HOST@ +ac_tool_prefix=${host_alias}- + +if test -z $with_boost; then + with_boost=$depends_prefix +fi +if test -z $with_qt_plugindir; then + with_qt_plugindir=$depends_prefix/plugins +fi +if test -z $with_qt_translationdir; then + with_qt_translationdir=$depends_prefix/translations +fi +if test -z $with_qt_bindir; then + with_qt_bindir=$depends_prefix/native/bin +fi +if test -z $with_protoc_bindir; then + with_protoc_bindir=$depends_prefix/native/bin +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 -z $with_gui && test -n "@no_qt@"; then + with_gui=no +fi + +if test x@host_os@ = xdarwin; then + BREW=no + PORT=no +fi + +if test x@host_os@ = xmingw32; then + if test -z $with_qt_incdir; then + with_qt_incdir=$depends_prefix/include + fi + if test -z $with_qt_libdir; then + with_qt_libdir=$depends_prefix/lib + fi +fi + +PATH=$depends_prefix/native/bin:$PATH +PKG_CONFIG="`which pkg-config` --static" + +# These two need to remain exported because pkg-config does not see them +# otherwise. That means they must be unexported at the end of configure.ac to +# avoid ruining the cache. Sigh. + +export PKG_CONFIG_LIBDIR=$depends_prefix/lib/pkgconfig +export PKG_CONFIG_PATH=$depends_prefix/share/pkgconfig + +CPPFLAGS="-I$depends_prefix/include/ $CPPFLAGS" +LDFLAGS="-L$depends_prefix/lib $LDFLAGS" + +CC="@CC@" +CXX="@CXX@" +OBJC="${CC}" +CCACHE=$depends_prefix/native/bin/ccache +PYTHONPATH=$depends_prefix/native/lib/python/dist-packages:$PYTHONPATH + +if test -n "@AR@"; then + AR=@AR@ + ac_cv_path_ac_pt_AR=${AR} +fi + +if test -n "@RANLIB@"; then + RANLIB=@RANLIB@ + ac_cv_path_ac_pt_RANLIB=${RANLIB} +fi + +if test -n "@NM@"; then + NM=@NM@ + ac_cv_path_ac_pt_NM=${NM} +fi + +if test -n "@debug@"; then + enable_reduce_exports=no +fi + +if test -n "@CFLAGS@"; then + CFLAGS="@CFLAGS@ $CFLAGS" +fi +if test -n "@CXXFLAGS@"; then + CXXFLAGS="@CXXFLAGS@ $CXXFLAGS" +fi +if test -n "@CPPFLAGS@"; then + CPPFLAGS="@CPPFLAGS@ $CPPFLAGS" +fi +if test -n "@LDFLAGS@"; then + LDFLAGS="@LDFLAGS@ $LDFLAGS" +fi diff --git a/contrib/depends/config.sub b/contrib/depends/config.sub new file mode 100755 index 00000000000..7e792b4ae17 --- /dev/null +++ b/contrib/depends/config.sub @@ -0,0 +1,1828 @@ +#! /bin/sh +# Configuration validation subroutine script. +# Copyright 1992-2017 Free Software Foundation, Inc. + +timestamp='2017-01-01' + +# This file is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (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 Exception is an additional permission under section 7 +# of the GNU General Public License, version 3 ("GPLv3"). + + +# Please send patches to . +# +# Configuration subroutine to validate and canonicalize a configuration type. +# Supply the specified configuration type as an argument. +# If it is invalid, we print an error message on stderr and exit with code 1. +# Otherwise, we print the canonical config type on stdout and succeed. + +# You can get the latest version of this script from: +# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub + +# This file is supposed to be the same for all GNU packages +# and recognize all the CPU types, system types and aliases +# that are meaningful with *any* GNU software. +# Each package is responsible for reporting which valid configurations +# it does not support. The user should be able to distinguish +# a failure to support a valid configuration from a meaningless +# configuration. + +# The goal of this file is to map all the various variations of a given +# machine specification into a single specification in the form: +# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM +# or in some cases, the newer four-part form: +# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM +# It is wrong to echo any other type of specification. + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] CPU-MFR-OPSYS or ALIAS + +Canonicalize a configuration name. + +Operation modes: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to ." + +version="\ +GNU config.sub ($timestamp) + +Copyright 1992-2017 Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit ;; + --version | -v ) + echo "$version" ; exit ;; + --help | --h* | -h ) + echo "$usage"; exit ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" + exit 1 ;; + + *local*) + # First pass through any local machine types. + echo $1 + exit ;; + + * ) + break ;; + esac +done + +case $# in + 0) echo "$me: missing argument$help" >&2 + exit 1;; + 1) ;; + *) echo "$me: too many arguments$help" >&2 + exit 1;; +esac + +# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). +# Here we must recognize all the valid KERNEL-OS combinations. +maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` +case $maybe_os in + nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \ + linux-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \ + knetbsd*-gnu* | netbsd*-gnu* | netbsd*-eabi* | \ + kopensolaris*-gnu* | cloudabi*-eabi* | \ + storm-chaos* | os2-emx* | rtmk-nova*) + os=-$maybe_os + basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` + ;; + android-linux) + os=-linux-android + basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`-unknown + ;; + *) + basic_machine=`echo $1 | sed 's/-[^-]*$//'` + if [ $basic_machine != $1 ] + then os=`echo $1 | sed 's/.*-/-/'` + else os=; fi + ;; +esac + +### Let's recognize common machines as not being operating systems so +### that things like config.sub decstation-3100 work. We also +### recognize some manufacturers as not being operating systems, so we +### can provide default operating systems below. +case $os in + -sun*os*) + # Prevent following clause from handling this invalid input. + ;; + -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ + -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ + -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ + -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ + -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ + -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ + -apple | -axis | -knuth | -cray | -microblaze*) + os= + basic_machine=$1 + ;; + -bluegene*) + os=-cnk + ;; + -sim | -cisco | -oki | -wec | -winbond) + os= + basic_machine=$1 + ;; + -scout) + ;; + -wrs) + os=-vxworks + basic_machine=$1 + ;; + -chorusos*) + os=-chorusos + basic_machine=$1 + ;; + -chorusrdb) + os=-chorusrdb + basic_machine=$1 + ;; + -hiux*) + os=-hiuxwe2 + ;; + -sco6) + os=-sco5v6 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco5) + os=-sco3.2v5 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco4) + os=-sco3.2v4 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2.[4-9]*) + os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2v[4-9]*) + # Don't forget version if it is 3.2v4 or newer. + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco5v6*) + # Don't forget version if it is 3.2v4 or newer. + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco*) + os=-sco3.2v2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -udk*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -isc) + os=-isc2.2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -clix*) + basic_machine=clipper-intergraph + ;; + -isc*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -lynx*178) + os=-lynxos178 + ;; + -lynx*5) + os=-lynxos5 + ;; + -lynx*) + os=-lynxos + ;; + -ptx*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'` + ;; + -windowsnt*) + os=`echo $os | sed -e 's/windowsnt/winnt/'` + ;; + -psos*) + os=-psos + ;; + -mint | -mint[0-9]*) + basic_machine=m68k-atari + os=-mint + ;; +esac + +# Decode aliases for certain CPU-COMPANY combinations. +case $basic_machine in + # Recognize the basic CPU types without company name. + # Some are omitted here because they have special meanings below. + 1750a | 580 \ + | a29k \ + | aarch64 | aarch64_be \ + | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ + | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ + | am33_2.0 \ + | arc | arceb \ + | arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv7[arm] \ + | avr | avr32 \ + | ba \ + | be32 | be64 \ + | bfin \ + | c4x | c8051 | clipper \ + | d10v | d30v | dlx | dsp16xx \ + | e2k | epiphany \ + | fido | fr30 | frv | ft32 \ + | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ + | hexagon \ + | i370 | i860 | i960 | ia64 \ + | ip2k | iq2000 \ + | k1om \ + | le32 | le64 \ + | lm32 \ + | m32c | m32r | m32rle | m68000 | m68k | m88k \ + | maxq | mb | microblaze | microblazeel | mcore | mep | metag \ + | mips | mipsbe | mipseb | mipsel | mipsle \ + | mips16 \ + | mips64 | mips64el \ + | mips64octeon | mips64octeonel \ + | mips64orion | mips64orionel \ + | mips64r5900 | mips64r5900el \ + | mips64vr | mips64vrel \ + | mips64vr4100 | mips64vr4100el \ + | mips64vr4300 | mips64vr4300el \ + | mips64vr5000 | mips64vr5000el \ + | mips64vr5900 | mips64vr5900el \ + | mipsisa32 | mipsisa32el \ + | mipsisa32r2 | mipsisa32r2el \ + | mipsisa32r6 | mipsisa32r6el \ + | mipsisa64 | mipsisa64el \ + | mipsisa64r2 | mipsisa64r2el \ + | mipsisa64r6 | mipsisa64r6el \ + | mipsisa64sb1 | mipsisa64sb1el \ + | mipsisa64sr71k | mipsisa64sr71kel \ + | mipsr5900 | mipsr5900el \ + | mipstx39 | mipstx39el \ + | mn10200 | mn10300 \ + | moxie \ + | mt \ + | msp430 \ + | nds32 | nds32le | nds32be \ + | nios | nios2 | nios2eb | nios2el \ + | ns16k | ns32k \ + | open8 | or1k | or1knd | or32 \ + | pdp10 | pdp11 | pj | pjl \ + | powerpc | powerpc64 | powerpc64le | powerpcle \ + | pru \ + | pyramid \ + | riscv32 | riscv64 \ + | rl78 | rx \ + | score \ + | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[234]eb | sheb | shbe | shle | sh[1234]le | sh3ele \ + | sh64 | sh64le \ + | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \ + | sparcv8 | sparcv9 | sparcv9b | sparcv9v \ + | spu \ + | tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \ + | ubicom32 \ + | v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \ + | visium \ + | we32k \ + | x86 | xc16x | xstormy16 | xtensa \ + | z8k | z80) + basic_machine=$basic_machine-unknown + ;; + c54x) + basic_machine=tic54x-unknown + ;; + c55x) + basic_machine=tic55x-unknown + ;; + c6x) + basic_machine=tic6x-unknown + ;; + leon|leon[3-9]) + basic_machine=sparc-$basic_machine + ;; + m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | nvptx | picochip) + basic_machine=$basic_machine-unknown + os=-none + ;; + m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k) + ;; + ms1) + basic_machine=mt-unknown + ;; + + strongarm | thumb | xscale) + basic_machine=arm-unknown + ;; + xgate) + basic_machine=$basic_machine-unknown + os=-none + ;; + xscaleeb) + basic_machine=armeb-unknown + ;; + + xscaleel) + basic_machine=armel-unknown + ;; + + # We use `pc' rather than `unknown' + # because (1) that's what they normally are, and + # (2) the word "unknown" tends to confuse beginning users. + i*86 | x86_64) + basic_machine=$basic_machine-pc + ;; + # Object if more than one company name word. + *-*-*) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; + # Recognize the basic CPU types with company name. + 580-* \ + | a29k-* \ + | aarch64-* | aarch64_be-* \ + | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ + | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ + | alphapca5[67]-* | alpha64pca5[67]-* | arc-* | arceb-* \ + | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ + | avr-* | avr32-* \ + | ba-* \ + | be32-* | be64-* \ + | bfin-* | bs2000-* \ + | c[123]* | c30-* | [cjt]90-* | c4x-* \ + | c8051-* | clipper-* | craynv-* | cydra-* \ + | d10v-* | d30v-* | dlx-* \ + | e2k-* | elxsi-* \ + | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \ + | h8300-* | h8500-* \ + | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ + | hexagon-* \ + | i*86-* | i860-* | i960-* | ia64-* \ + | ip2k-* | iq2000-* \ + | k1om-* \ + | le32-* | le64-* \ + | lm32-* \ + | m32c-* | m32r-* | m32rle-* \ + | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ + | m88110-* | m88k-* | maxq-* | mcore-* | metag-* \ + | microblaze-* | microblazeel-* \ + | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \ + | mips16-* \ + | mips64-* | mips64el-* \ + | mips64octeon-* | mips64octeonel-* \ + | mips64orion-* | mips64orionel-* \ + | mips64r5900-* | mips64r5900el-* \ + | mips64vr-* | mips64vrel-* \ + | mips64vr4100-* | mips64vr4100el-* \ + | mips64vr4300-* | mips64vr4300el-* \ + | mips64vr5000-* | mips64vr5000el-* \ + | mips64vr5900-* | mips64vr5900el-* \ + | mipsisa32-* | mipsisa32el-* \ + | mipsisa32r2-* | mipsisa32r2el-* \ + | mipsisa32r6-* | mipsisa32r6el-* \ + | mipsisa64-* | mipsisa64el-* \ + | mipsisa64r2-* | mipsisa64r2el-* \ + | mipsisa64r6-* | mipsisa64r6el-* \ + | mipsisa64sb1-* | mipsisa64sb1el-* \ + | mipsisa64sr71k-* | mipsisa64sr71kel-* \ + | mipsr5900-* | mipsr5900el-* \ + | mipstx39-* | mipstx39el-* \ + | mmix-* \ + | mt-* \ + | msp430-* \ + | nds32-* | nds32le-* | nds32be-* \ + | nios-* | nios2-* | nios2eb-* | nios2el-* \ + | none-* | np1-* | ns16k-* | ns32k-* \ + | open8-* \ + | or1k*-* \ + | orion-* \ + | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ + | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \ + | pru-* \ + | pyramid-* \ + | riscv32-* | riscv64-* \ + | rl78-* | romp-* | rs6000-* | rx-* \ + | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \ + | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ + | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \ + | sparclite-* \ + | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx*-* \ + | tahoe-* \ + | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \ + | tile*-* \ + | tron-* \ + | ubicom32-* \ + | v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \ + | vax-* \ + | visium-* \ + | we32k-* \ + | x86-* | x86_64-* | xc16x-* | xps100-* \ + | xstormy16-* | xtensa*-* \ + | ymp-* \ + | z8k-* | z80-*) + ;; + # Recognize the basic CPU types without company name, with glob match. + xtensa*) + basic_machine=$basic_machine-unknown + ;; + # Recognize the various machine names and aliases which stand + # for a CPU type and a company and sometimes even an OS. + 386bsd) + basic_machine=i386-unknown + os=-bsd + ;; + 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) + basic_machine=m68000-att + ;; + 3b*) + basic_machine=we32k-att + ;; + a29khif) + basic_machine=a29k-amd + os=-udi + ;; + abacus) + basic_machine=abacus-unknown + ;; + adobe68k) + basic_machine=m68010-adobe + os=-scout + ;; + alliant | fx80) + basic_machine=fx80-alliant + ;; + altos | altos3068) + basic_machine=m68k-altos + ;; + am29k) + basic_machine=a29k-none + os=-bsd + ;; + amd64) + basic_machine=x86_64-pc + ;; + amd64-*) + basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + amdahl) + basic_machine=580-amdahl + os=-sysv + ;; + amiga | amiga-*) + basic_machine=m68k-unknown + ;; + amigaos | amigados) + basic_machine=m68k-unknown + os=-amigaos + ;; + amigaunix | amix) + basic_machine=m68k-unknown + os=-sysv4 + ;; + apollo68) + basic_machine=m68k-apollo + os=-sysv + ;; + apollo68bsd) + basic_machine=m68k-apollo + os=-bsd + ;; + aros) + basic_machine=i386-pc + os=-aros + ;; + asmjs) + basic_machine=asmjs-unknown + ;; + aux) + basic_machine=m68k-apple + os=-aux + ;; + balance) + basic_machine=ns32k-sequent + os=-dynix + ;; + blackfin) + basic_machine=bfin-unknown + os=-linux + ;; + blackfin-*) + basic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'` + os=-linux + ;; + bluegene*) + basic_machine=powerpc-ibm + os=-cnk + ;; + c54x-*) + basic_machine=tic54x-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + c55x-*) + basic_machine=tic55x-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + c6x-*) + basic_machine=tic6x-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + c90) + basic_machine=c90-cray + os=-unicos + ;; + cegcc) + basic_machine=arm-unknown + os=-cegcc + ;; + convex-c1) + basic_machine=c1-convex + os=-bsd + ;; + convex-c2) + basic_machine=c2-convex + os=-bsd + ;; + convex-c32) + basic_machine=c32-convex + os=-bsd + ;; + convex-c34) + basic_machine=c34-convex + os=-bsd + ;; + convex-c38) + basic_machine=c38-convex + os=-bsd + ;; + cray | j90) + basic_machine=j90-cray + os=-unicos + ;; + craynv) + basic_machine=craynv-cray + os=-unicosmp + ;; + cr16 | cr16-*) + basic_machine=cr16-unknown + os=-elf + ;; + crds | unos) + basic_machine=m68k-crds + ;; + crisv32 | crisv32-* | etraxfs*) + basic_machine=crisv32-axis + ;; + cris | cris-* | etrax*) + basic_machine=cris-axis + ;; + crx) + basic_machine=crx-unknown + os=-elf + ;; + da30 | da30-*) + basic_machine=m68k-da30 + ;; + decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) + basic_machine=mips-dec + ;; + decsystem10* | dec10*) + basic_machine=pdp10-dec + os=-tops10 + ;; + decsystem20* | dec20*) + basic_machine=pdp10-dec + os=-tops20 + ;; + delta | 3300 | motorola-3300 | motorola-delta \ + | 3300-motorola | delta-motorola) + basic_machine=m68k-motorola + ;; + delta88) + basic_machine=m88k-motorola + os=-sysv3 + ;; + dicos) + basic_machine=i686-pc + os=-dicos + ;; + djgpp) + basic_machine=i586-pc + os=-msdosdjgpp + ;; + dpx20 | dpx20-*) + basic_machine=rs6000-bull + os=-bosx + ;; + dpx2* | dpx2*-bull) + basic_machine=m68k-bull + os=-sysv3 + ;; + e500v[12]) + basic_machine=powerpc-unknown + os=$os"spe" + ;; + e500v[12]-*) + basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` + os=$os"spe" + ;; + ebmon29k) + basic_machine=a29k-amd + os=-ebmon + ;; + elxsi) + basic_machine=elxsi-elxsi + os=-bsd + ;; + encore | umax | mmax) + basic_machine=ns32k-encore + ;; + es1800 | OSE68k | ose68k | ose | OSE) + basic_machine=m68k-ericsson + os=-ose + ;; + fx2800) + basic_machine=i860-alliant + ;; + genix) + basic_machine=ns32k-ns + ;; + gmicro) + basic_machine=tron-gmicro + os=-sysv + ;; + go32) + basic_machine=i386-pc + os=-go32 + ;; + h3050r* | hiux*) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + h8300hms) + basic_machine=h8300-hitachi + os=-hms + ;; + h8300xray) + basic_machine=h8300-hitachi + os=-xray + ;; + h8500hms) + basic_machine=h8500-hitachi + os=-hms + ;; + harris) + basic_machine=m88k-harris + os=-sysv3 + ;; + hp300-*) + basic_machine=m68k-hp + ;; + hp300bsd) + basic_machine=m68k-hp + os=-bsd + ;; + hp300hpux) + basic_machine=m68k-hp + os=-hpux + ;; + hp3k9[0-9][0-9] | hp9[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hp9k2[0-9][0-9] | hp9k31[0-9]) + basic_machine=m68000-hp + ;; + hp9k3[2-9][0-9]) + basic_machine=m68k-hp + ;; + hp9k6[0-9][0-9] | hp6[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hp9k7[0-79][0-9] | hp7[0-79][0-9]) + basic_machine=hppa1.1-hp + ;; + hp9k78[0-9] | hp78[0-9]) + # FIXME: really hppa2.0-hp + basic_machine=hppa1.1-hp + ;; + hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) + # FIXME: really hppa2.0-hp + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][13679] | hp8[0-9][13679]) + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][0-9] | hp8[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hppa-next) + os=-nextstep3 + ;; + hppaosf) + basic_machine=hppa1.1-hp + os=-osf + ;; + hppro) + basic_machine=hppa1.1-hp + os=-proelf + ;; + i370-ibm* | ibm*) + basic_machine=i370-ibm + ;; + i*86v32) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv32 + ;; + i*86v4*) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv4 + ;; + i*86v) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv + ;; + i*86sol2) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-solaris2 + ;; + i386mach) + basic_machine=i386-mach + os=-mach + ;; + i386-vsta | vsta) + basic_machine=i386-unknown + os=-vsta + ;; + iris | iris4d) + basic_machine=mips-sgi + case $os in + -irix*) + ;; + *) + os=-irix4 + ;; + esac + ;; + isi68 | isi) + basic_machine=m68k-isi + os=-sysv + ;; + leon-*|leon[3-9]-*) + basic_machine=sparc-`echo $basic_machine | sed 's/-.*//'` + ;; + m68knommu) + basic_machine=m68k-unknown + os=-linux + ;; + m68knommu-*) + basic_machine=m68k-`echo $basic_machine | sed 's/^[^-]*-//'` + os=-linux + ;; + m88k-omron*) + basic_machine=m88k-omron + ;; + magnum | m3230) + basic_machine=mips-mips + os=-sysv + ;; + merlin) + basic_machine=ns32k-utek + os=-sysv + ;; + microblaze*) + basic_machine=microblaze-xilinx + ;; + mingw64) + basic_machine=x86_64-pc + os=-mingw64 + ;; + mingw32) + basic_machine=i686-pc + os=-mingw32 + ;; + mingw32ce) + basic_machine=arm-unknown + os=-mingw32ce + ;; + miniframe) + basic_machine=m68000-convergent + ;; + *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) + basic_machine=m68k-atari + os=-mint + ;; + mips3*-*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'` + ;; + mips3*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown + ;; + monitor) + basic_machine=m68k-rom68k + os=-coff + ;; + morphos) + basic_machine=powerpc-unknown + os=-morphos + ;; + moxiebox) + basic_machine=moxie-unknown + os=-moxiebox + ;; + msdos) + basic_machine=i386-pc + os=-msdos + ;; + ms1-*) + basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'` + ;; + msys) + basic_machine=i686-pc + os=-msys + ;; + mvs) + basic_machine=i370-ibm + os=-mvs + ;; + nacl) + basic_machine=le32-unknown + os=-nacl + ;; + ncr3000) + basic_machine=i486-ncr + os=-sysv4 + ;; + netbsd386) + basic_machine=i386-unknown + os=-netbsd + ;; + netwinder) + basic_machine=armv4l-rebel + os=-linux + ;; + news | news700 | news800 | news900) + basic_machine=m68k-sony + os=-newsos + ;; + news1000) + basic_machine=m68030-sony + os=-newsos + ;; + news-3600 | risc-news) + basic_machine=mips-sony + os=-newsos + ;; + necv70) + basic_machine=v70-nec + os=-sysv + ;; + next | m*-next ) + basic_machine=m68k-next + case $os in + -nextstep* ) + ;; + -ns2*) + os=-nextstep2 + ;; + *) + os=-nextstep3 + ;; + esac + ;; + nh3000) + basic_machine=m68k-harris + os=-cxux + ;; + nh[45]000) + basic_machine=m88k-harris + os=-cxux + ;; + nindy960) + basic_machine=i960-intel + os=-nindy + ;; + mon960) + basic_machine=i960-intel + os=-mon960 + ;; + nonstopux) + basic_machine=mips-compaq + os=-nonstopux + ;; + np1) + basic_machine=np1-gould + ;; + neo-tandem) + basic_machine=neo-tandem + ;; + nse-tandem) + basic_machine=nse-tandem + ;; + nsr-tandem) + basic_machine=nsr-tandem + ;; + op50n-* | op60c-*) + basic_machine=hppa1.1-oki + os=-proelf + ;; + openrisc | openrisc-*) + basic_machine=or32-unknown + ;; + os400) + basic_machine=powerpc-ibm + os=-os400 + ;; + OSE68000 | ose68000) + basic_machine=m68000-ericsson + os=-ose + ;; + os68k) + basic_machine=m68k-none + os=-os68k + ;; + pa-hitachi) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + paragon) + basic_machine=i860-intel + os=-osf + ;; + parisc) + basic_machine=hppa-unknown + os=-linux + ;; + parisc-*) + basic_machine=hppa-`echo $basic_machine | sed 's/^[^-]*-//'` + os=-linux + ;; + pbd) + basic_machine=sparc-tti + ;; + pbb) + basic_machine=m68k-tti + ;; + pc532 | pc532-*) + basic_machine=ns32k-pc532 + ;; + pc98) + basic_machine=i386-pc + ;; + pc98-*) + basic_machine=i386-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentium | p5 | k5 | k6 | nexgen | viac3) + basic_machine=i586-pc + ;; + pentiumpro | p6 | 6x86 | athlon | athlon_*) + basic_machine=i686-pc + ;; + pentiumii | pentium2 | pentiumiii | pentium3) + basic_machine=i686-pc + ;; + pentium4) + basic_machine=i786-pc + ;; + pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) + basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentiumpro-* | p6-* | 6x86-* | athlon-*) + basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*) + basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentium4-*) + basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pn) + basic_machine=pn-gould + ;; + power) basic_machine=power-ibm + ;; + ppc | ppcbe) basic_machine=powerpc-unknown + ;; + ppc-* | ppcbe-*) + basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppcle | powerpclittle) + basic_machine=powerpcle-unknown + ;; + ppcle-* | powerpclittle-*) + basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppc64) basic_machine=powerpc64-unknown + ;; + ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppc64le | powerpc64little) + basic_machine=powerpc64le-unknown + ;; + ppc64le-* | powerpc64little-*) + basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ps2) + basic_machine=i386-ibm + ;; + pw32) + basic_machine=i586-unknown + os=-pw32 + ;; + rdos | rdos64) + basic_machine=x86_64-pc + os=-rdos + ;; + rdos32) + basic_machine=i386-pc + os=-rdos + ;; + rom68k) + basic_machine=m68k-rom68k + os=-coff + ;; + rm[46]00) + basic_machine=mips-siemens + ;; + rtpc | rtpc-*) + basic_machine=romp-ibm + ;; + s390 | s390-*) + basic_machine=s390-ibm + ;; + s390x | s390x-*) + basic_machine=s390x-ibm + ;; + sa29200) + basic_machine=a29k-amd + os=-udi + ;; + sb1) + basic_machine=mipsisa64sb1-unknown + ;; + sb1el) + basic_machine=mipsisa64sb1el-unknown + ;; + sde) + basic_machine=mipsisa32-sde + os=-elf + ;; + sei) + basic_machine=mips-sei + os=-seiux + ;; + sequent) + basic_machine=i386-sequent + ;; + sh) + basic_machine=sh-hitachi + os=-hms + ;; + sh5el) + basic_machine=sh5le-unknown + ;; + sh64) + basic_machine=sh64-unknown + ;; + sparclite-wrs | simso-wrs) + basic_machine=sparclite-wrs + os=-vxworks + ;; + sps7) + basic_machine=m68k-bull + os=-sysv2 + ;; + spur) + basic_machine=spur-unknown + ;; + st2000) + basic_machine=m68k-tandem + ;; + stratus) + basic_machine=i860-stratus + os=-sysv4 + ;; + strongarm-* | thumb-*) + basic_machine=arm-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + sun2) + basic_machine=m68000-sun + ;; + sun2os3) + basic_machine=m68000-sun + os=-sunos3 + ;; + sun2os4) + basic_machine=m68000-sun + os=-sunos4 + ;; + sun3os3) + basic_machine=m68k-sun + os=-sunos3 + ;; + sun3os4) + basic_machine=m68k-sun + os=-sunos4 + ;; + sun4os3) + basic_machine=sparc-sun + os=-sunos3 + ;; + sun4os4) + basic_machine=sparc-sun + os=-sunos4 + ;; + sun4sol2) + basic_machine=sparc-sun + os=-solaris2 + ;; + sun3 | sun3-*) + basic_machine=m68k-sun + ;; + sun4) + basic_machine=sparc-sun + ;; + sun386 | sun386i | roadrunner) + basic_machine=i386-sun + ;; + sv1) + basic_machine=sv1-cray + os=-unicos + ;; + symmetry) + basic_machine=i386-sequent + os=-dynix + ;; + t3e) + basic_machine=alphaev5-cray + os=-unicos + ;; + t90) + basic_machine=t90-cray + os=-unicos + ;; + tile*) + basic_machine=$basic_machine-unknown + os=-linux-gnu + ;; + tx39) + basic_machine=mipstx39-unknown + ;; + tx39el) + basic_machine=mipstx39el-unknown + ;; + toad1) + basic_machine=pdp10-xkl + os=-tops20 + ;; + tower | tower-32) + basic_machine=m68k-ncr + ;; + tpf) + basic_machine=s390x-ibm + os=-tpf + ;; + udi29k) + basic_machine=a29k-amd + os=-udi + ;; + ultra3) + basic_machine=a29k-nyu + os=-sym1 + ;; + v810 | necv810) + basic_machine=v810-nec + os=-none + ;; + vaxv) + basic_machine=vax-dec + os=-sysv + ;; + vms) + basic_machine=vax-dec + os=-vms + ;; + vpp*|vx|vx-*) + basic_machine=f301-fujitsu + ;; + vxworks960) + basic_machine=i960-wrs + os=-vxworks + ;; + vxworks68) + basic_machine=m68k-wrs + os=-vxworks + ;; + vxworks29k) + basic_machine=a29k-wrs + os=-vxworks + ;; + w65*) + basic_machine=w65-wdc + os=-none + ;; + w89k-*) + basic_machine=hppa1.1-winbond + os=-proelf + ;; + xbox) + basic_machine=i686-pc + os=-mingw32 + ;; + xps | xps100) + basic_machine=xps100-honeywell + ;; + xscale-* | xscalee[bl]-*) + basic_machine=`echo $basic_machine | sed 's/^xscale/arm/'` + ;; + ymp) + basic_machine=ymp-cray + os=-unicos + ;; + z8k-*-coff) + basic_machine=z8k-unknown + os=-sim + ;; + z80-*-coff) + basic_machine=z80-unknown + os=-sim + ;; + none) + basic_machine=none-none + os=-none + ;; + +# Here we handle the default manufacturer of certain CPU types. It is in +# some cases the only manufacturer, in others, it is the most popular. + w89k) + basic_machine=hppa1.1-winbond + ;; + op50n) + basic_machine=hppa1.1-oki + ;; + op60c) + basic_machine=hppa1.1-oki + ;; + romp) + basic_machine=romp-ibm + ;; + mmix) + basic_machine=mmix-knuth + ;; + rs6000) + basic_machine=rs6000-ibm + ;; + vax) + basic_machine=vax-dec + ;; + pdp10) + # there are many clones, so DEC is not a safe bet + basic_machine=pdp10-unknown + ;; + pdp11) + basic_machine=pdp11-dec + ;; + we32k) + basic_machine=we32k-att + ;; + sh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele) + basic_machine=sh-unknown + ;; + sparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v) + basic_machine=sparc-sun + ;; + cydra) + basic_machine=cydra-cydrome + ;; + orion) + basic_machine=orion-highlevel + ;; + orion105) + basic_machine=clipper-highlevel + ;; + mac | mpw | mac-mpw) + basic_machine=m68k-apple + ;; + pmac | pmac-mpw) + basic_machine=powerpc-apple + ;; + *-unknown) + # Make sure to match an already-canonicalized machine name. + ;; + *) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; +esac + +# Here we canonicalize certain aliases for manufacturers. +case $basic_machine in + *-digital*) + basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'` + ;; + *-commodore*) + basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'` + ;; + *) + ;; +esac + +# Decode manufacturer-specific aliases for certain operating systems. + +if [ x"$os" != x"" ] +then +case $os in + # First match some system type aliases + # that might get confused with valid system types. + # -solaris* is a basic system type, with this one exception. + -auroraux) + os=-auroraux + ;; + -solaris1 | -solaris1.*) + os=`echo $os | sed -e 's|solaris1|sunos4|'` + ;; + -solaris) + os=-solaris2 + ;; + -svr4*) + os=-sysv4 + ;; + -unixware*) + os=-sysv4.2uw + ;; + -gnu/linux*) + os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` + ;; + # First accept the basic system types. + # The portable systems comes first. + # Each alternative MUST END IN A *, to match a version number. + # -sysv* is not here because it comes later, after sysvr4. + -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ + | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\ + | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \ + | -sym* | -kopensolaris* | -plan9* \ + | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ + | -aos* | -aros* | -cloudabi* | -sortix* \ + | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ + | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ + | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \ + | -bitrig* | -openbsd* | -solidbsd* | -libertybsd* \ + | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \ + | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ + | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ + | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ + | -chorusos* | -chorusrdb* | -cegcc* | -glidix* \ + | -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ + | -midipix* | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \ + | -linux-newlib* | -linux-musl* | -linux-uclibc* \ + | -uxpv* | -beos* | -mpeix* | -udk* | -moxiebox* \ + | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \ + | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ + | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ + | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ + | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \ + | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \ + | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es* \ + | -onefs* | -tirtos* | -phoenix* | -fuchsia* | -redox*) + # Remember, each alternative MUST END IN *, to match a version number. + ;; + -qnx*) + case $basic_machine in + x86-* | i*86-*) + ;; + *) + os=-nto$os + ;; + esac + ;; + -nto-qnx*) + ;; + -nto*) + os=`echo $os | sed -e 's|nto|nto-qnx|'` + ;; + -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \ + | -windows* | -osx | -abug | -netware* | -os9* | -beos* | -haiku* \ + | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*) + ;; + -mac*) + os=`echo $os | sed -e 's|mac|macos|'` + ;; + -linux-dietlibc) + os=-linux-dietlibc + ;; + -linux*) + os=`echo $os | sed -e 's|linux|linux-gnu|'` + ;; + -sunos5*) + os=`echo $os | sed -e 's|sunos5|solaris2|'` + ;; + -sunos6*) + os=`echo $os | sed -e 's|sunos6|solaris3|'` + ;; + -opened*) + os=-openedition + ;; + -os400*) + os=-os400 + ;; + -wince*) + os=-wince + ;; + -osfrose*) + os=-osfrose + ;; + -osf*) + os=-osf + ;; + -utek*) + os=-bsd + ;; + -dynix*) + os=-bsd + ;; + -acis*) + os=-aos + ;; + -atheos*) + os=-atheos + ;; + -syllable*) + os=-syllable + ;; + -386bsd) + os=-bsd + ;; + -ctix* | -uts*) + os=-sysv + ;; + -nova*) + os=-rtmk-nova + ;; + -ns2 ) + os=-nextstep2 + ;; + -nsk*) + os=-nsk + ;; + # Preserve the version number of sinix5. + -sinix5.*) + os=`echo $os | sed -e 's|sinix|sysv|'` + ;; + -sinix*) + os=-sysv4 + ;; + -tpf*) + os=-tpf + ;; + -triton*) + os=-sysv3 + ;; + -oss*) + os=-sysv3 + ;; + -svr4) + os=-sysv4 + ;; + -svr3) + os=-sysv3 + ;; + -sysvr4) + os=-sysv4 + ;; + # This must come after -sysvr4. + -sysv*) + ;; + -ose*) + os=-ose + ;; + -es1800*) + os=-ose + ;; + -xenix) + os=-xenix + ;; + -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + os=-mint + ;; + -aros*) + os=-aros + ;; + -zvmoe) + os=-zvmoe + ;; + -dicos*) + os=-dicos + ;; + -nacl*) + ;; + -ios) + ;; + -none) + ;; + *) + # Get rid of the `-' at the beginning of $os. + os=`echo $os | sed 's/[^-]*-//'` + echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2 + exit 1 + ;; +esac +else + +# Here we handle the default operating systems that come with various machines. +# The value should be what the vendor currently ships out the door with their +# machine or put another way, the most popular os provided with the machine. + +# Note that if you're going to try to match "-MANUFACTURER" here (say, +# "-sun"), then you have to tell the case statement up towards the top +# that MANUFACTURER isn't an operating system. Otherwise, code above +# will signal an error saying that MANUFACTURER isn't an operating +# system, and we'll never get to this point. + +case $basic_machine in + score-*) + os=-elf + ;; + spu-*) + os=-elf + ;; + *-acorn) + os=-riscix1.2 + ;; + arm*-rebel) + os=-linux + ;; + arm*-semi) + os=-aout + ;; + c4x-* | tic4x-*) + os=-coff + ;; + c8051-*) + os=-elf + ;; + hexagon-*) + os=-elf + ;; + tic54x-*) + os=-coff + ;; + tic55x-*) + os=-coff + ;; + tic6x-*) + os=-coff + ;; + # This must come before the *-dec entry. + pdp10-*) + os=-tops20 + ;; + pdp11-*) + os=-none + ;; + *-dec | vax-*) + os=-ultrix4.2 + ;; + m68*-apollo) + os=-domain + ;; + i386-sun) + os=-sunos4.0.2 + ;; + m68000-sun) + os=-sunos3 + ;; + m68*-cisco) + os=-aout + ;; + mep-*) + os=-elf + ;; + mips*-cisco) + os=-elf + ;; + mips*-*) + os=-elf + ;; + or32-*) + os=-coff + ;; + *-tti) # must be before sparc entry or we get the wrong os. + os=-sysv3 + ;; + sparc-* | *-sun) + os=-sunos4.1.1 + ;; + pru-*) + os=-elf + ;; + *-be) + os=-beos + ;; + *-haiku) + os=-haiku + ;; + *-ibm) + os=-aix + ;; + *-knuth) + os=-mmixware + ;; + *-wec) + os=-proelf + ;; + *-winbond) + os=-proelf + ;; + *-oki) + os=-proelf + ;; + *-hp) + os=-hpux + ;; + *-hitachi) + os=-hiux + ;; + i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) + os=-sysv + ;; + *-cbm) + os=-amigaos + ;; + *-dg) + os=-dgux + ;; + *-dolphin) + os=-sysv3 + ;; + m68k-ccur) + os=-rtu + ;; + m88k-omron*) + os=-luna + ;; + *-next ) + os=-nextstep + ;; + *-sequent) + os=-ptx + ;; + *-crds) + os=-unos + ;; + *-ns) + os=-genix + ;; + i370-*) + os=-mvs + ;; + *-next) + os=-nextstep3 + ;; + *-gould) + os=-sysv + ;; + *-highlevel) + os=-bsd + ;; + *-encore) + os=-bsd + ;; + *-sgi) + os=-irix + ;; + *-siemens) + os=-sysv4 + ;; + *-masscomp) + os=-rtu + ;; + f30[01]-fujitsu | f700-fujitsu) + os=-uxpv + ;; + *-rom68k) + os=-coff + ;; + *-*bug) + os=-coff + ;; + *-apple) + os=-macos + ;; + *-atari*) + os=-mint + ;; + *) + os=-none + ;; +esac +fi + +# Here we handle the case where we know the os, and the CPU type, but not the +# manufacturer. We pick the logical manufacturer. +vendor=unknown +case $basic_machine in + *-unknown) + case $os in + -riscix*) + vendor=acorn + ;; + -sunos*) + vendor=sun + ;; + -cnk*|-aix*) + vendor=ibm + ;; + -beos*) + vendor=be + ;; + -hpux*) + vendor=hp + ;; + -mpeix*) + vendor=hp + ;; + -hiux*) + vendor=hitachi + ;; + -unos*) + vendor=crds + ;; + -dgux*) + vendor=dg + ;; + -luna*) + vendor=omron + ;; + -genix*) + vendor=ns + ;; + -mvs* | -opened*) + vendor=ibm + ;; + -os400*) + vendor=ibm + ;; + -ptx*) + vendor=sequent + ;; + -tpf*) + vendor=ibm + ;; + -vxsim* | -vxworks* | -windiss*) + vendor=wrs + ;; + -aux*) + vendor=apple + ;; + -hms*) + vendor=hitachi + ;; + -mpw* | -macos*) + vendor=apple + ;; + -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + vendor=atari + ;; + -vos*) + vendor=stratus + ;; + esac + basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"` + ;; +esac + +echo $basic_machine$os +exit + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/contrib/depends/description.md b/contrib/depends/description.md new file mode 100644 index 00000000000..74f9ef3f205 --- /dev/null +++ b/contrib/depends/description.md @@ -0,0 +1,53 @@ +This is a system of building and caching dependencies necessary for building Bitcoin. +There are several features that make it different from most similar systems: + +### It is designed to be builder and host agnostic + +In theory, binaries for any target OS/architecture can be created, from a +builder running any OS/architecture. In practice, build-side tools must be +specified when the defaults don't fit, and packages must be amended to work +on new hosts. For now, a build architecture of x86_64 is assumed, either on +Linux or OSX. + +### No reliance on timestamps + +File presence is used to determine what needs to be built. This makes the +results distributable and easily digestable by automated builders. + +### Each build only has its specified dependencies available at build-time. + +For each build, the sysroot is wiped and the (recursive) dependencies are +installed. This makes each build deterministic, since there will never be any +unknown files available to cause side-effects. + +### Each package is cached and only rebuilt as needed. + +Before building, a unique build-id is generated for each package. This id +consists of a hash of all files used to build the package (Makefiles, packages, +etc), and as well as a hash of the same data for each recursive dependency. If +any portion of a package's build recipe changes, it will be rebuilt as well as +any other package that depends on it. If any of the main makefiles (Makefile, +funcs.mk, etc) are changed, all packages will be rebuilt. After building, the +results are cached into a tarball that can be re-used and distributed. + +### Package build results are (relatively) deterministic. + +Each package is configured and patched so that it will yield the same +build-results with each consequent build, within a reasonable set of +constraints. Some things like timestamp insertion are unavoidable, and are +beyond the scope of this system. Additionally, the toolchain itself must be +capable of deterministic results. When revisions are properly bumped, a cached +build should represent an exact single payload. + +### Sources are fetched and verified automatically + +Each package must define its source location and checksum. The build will fail +if the fetched source does not match. Sources may be pre-seeded and/or cached +as desired. + +### Self-cleaning + +Build and staging dirs are wiped after use, and any previous version of a +cached result is removed following a successful build. Automated builders +should be able to build each revision and store the results with no further +intervention. diff --git a/contrib/depends/funcs.mk b/contrib/depends/funcs.mk new file mode 100644 index 00000000000..15e404e42dc --- /dev/null +++ b/contrib/depends/funcs.mk @@ -0,0 +1,245 @@ +define int_vars +#Set defaults for vars which may be overridden per-package +$(1)_cc=$($($(1)_type)_CC) +$(1)_cxx=$($($(1)_type)_CXX) +$(1)_objc=$($($(1)_type)_OBJC) +$(1)_objcxx=$($($(1)_type)_OBJCXX) +$(1)_ar=$($($(1)_type)_AR) +$(1)_ranlib=$($($(1)_type)_RANLIB) +$(1)_libtool=$($($(1)_type)_LIBTOOL) +$(1)_nm=$($($(1)_type)_NM) +$(1)_cflags=$($($(1)_type)_CFLAGS) $($($(1)_type)_$(release_type)_CFLAGS) +$(1)_cxxflags=$($($(1)_type)_CXXFLAGS) $($($(1)_type)_$(release_type)_CXXFLAGS) +$(1)_ldflags=$($($(1)_type)_LDFLAGS) $($($(1)_type)_$(release_type)_LDFLAGS) -L$($($(1)_type)_prefix)/lib +$(1)_cppflags=$($($(1)_type)_CPPFLAGS) $($($(1)_type)_$(release_type)_CPPFLAGS) -I$($($(1)_type)_prefix)/include +$(1)_recipe_hash:= +endef + +define int_get_all_dependencies +$(sort $(foreach dep,$(2),$(2) $(call int_get_all_dependencies,$(1),$($(dep)_dependencies)))) +endef + +define fetch_file_inner + ( mkdir -p $$($(1)_download_dir) && echo Fetching $(3) from $(2) && \ + $(build_DOWNLOAD) "$$($(1)_download_dir)/$(4).temp" "$(2)/$(3)" && \ + echo "$(5) $$($(1)_download_dir)/$(4).temp" > $$($(1)_download_dir)/.$(4).hash && \ + $(build_SHA256SUM) -c $$($(1)_download_dir)/.$(4).hash && \ + mv $$($(1)_download_dir)/$(4).temp $$($(1)_source_dir)/$(4) && \ + rm -rf $$($(1)_download_dir) ) +endef + +define fetch_file + ( test -f $$($(1)_source_dir)/$(4) || \ + ( $(call fetch_file_inner,$(1),$(2),$(3),$(4),$(5)) || \ + $(call fetch_file_inner,$(1),$(FALLBACK_DOWNLOAD_PATH),$(3),$(4),$(5)))) +endef + +define int_get_build_recipe_hash +$(eval $(1)_all_file_checksums:=$(shell $(build_SHA256SUM) $(meta_depends) packages/$(1).mk $(addprefix $(PATCHES_PATH)/$(1)/,$($(1)_patches)) | cut -d" " -f1)) +$(eval $(1)_recipe_hash:=$(shell echo -n "$($(1)_all_file_checksums)" | $(build_SHA256SUM) | cut -d" " -f1)) +endef + +define int_get_build_id +$(eval $(1)_dependencies += $($(1)_$(host_arch)_$(host_os)_dependencies) $($(1)_$(host_os)_dependencies)) +$(eval $(1)_all_dependencies:=$(call int_get_all_dependencies,$(1),$($($(1)_type)_native_toolchain) $($(1)_dependencies))) +$(foreach dep,$($(1)_all_dependencies),$(eval $(1)_build_id_deps+=$(dep)-$($(dep)_version)-$($(dep)_recipe_hash))) +$(eval $(1)_build_id_long:=$(1)-$($(1)_version)-$($(1)_recipe_hash)-$(release_type) $($(1)_build_id_deps) $($($(1)_type)_id_string)) +$(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) + +#compute package-specific paths +$(1)_build_subdir?=. +$(1)_download_file?=$($(1)_file_name) +$(1)_source_dir:=$(SOURCES_PATH) +$(1)_source:=$$($(1)_source_dir)/$($(1)_file_name) +$(1)_staging_dir=$(base_staging_dir)/$(host)/$(1)/$($(1)_version)-$($(1)_build_id) +$(1)_staging_prefix_dir:=$$($(1)_staging_dir)$($($(1)_type)_prefix) +$(1)_extract_dir:=$(base_build_dir)/$(host)/$(1)/$($(1)_version)-$($(1)_build_id) +$(1)_download_dir:=$(base_download_dir)/$(1)-$($(1)_version) +$(1)_build_dir:=$$($(1)_extract_dir)/$$($(1)_build_subdir) +$(1)_cached_checksum:=$(BASE_CACHE)/$(host)/$(1)/$(1)-$($(1)_version)-$($(1)_build_id).tar.gz.hash +$(1)_patch_dir:=$(base_build_dir)/$(host)/$(1)/$($(1)_version)-$($(1)_build_id)/.patches-$($(1)_build_id) +$(1)_prefixbin:=$($($(1)_type)_prefix)/bin/ +$(1)_cached:=$(BASE_CACHE)/$(host)/$(1)/$(1)-$($(1)_version)-$($(1)_build_id).tar.gz +$(1)_all_sources=$($(1)_file_name) $($(1)_extra_sources) + +#stamps +$(1)_fetched=$(SOURCES_PATH)/download-stamps/.stamp_fetched-$(1)-$($(1)_file_name).hash +$(1)_extracted=$$($(1)_extract_dir)/.stamp_extracted +$(1)_preprocessed=$$($(1)_extract_dir)/.stamp_preprocessed +$(1)_cleaned=$$($(1)_extract_dir)/.stamp_cleaned +$(1)_built=$$($(1)_build_dir)/.stamp_built +$(1)_configured=$$($(1)_build_dir)/.stamp_configured +$(1)_staged=$$($(1)_staging_dir)/.stamp_staged +$(1)_postprocessed=$$($(1)_staging_prefix_dir)/.stamp_postprocessed +$(1)_download_path_fixed=$(subst :,\:,$$($(1)_download_path)) + + +#default commands +$(1)_fetch_cmds ?= $(call fetch_file,$(1),$(subst \:,:,$$($(1)_download_path_fixed)),$$($(1)_download_file),$($(1)_file_name),$($(1)_sha256_hash)) +$(1)_extract_cmds ?= mkdir -p $$($(1)_extract_dir) && echo "$$($(1)_sha256_hash) $$($(1)_source)" > $$($(1)_extract_dir)/.$$($(1)_file_name).hash && $(build_SHA256SUM) -c $$($(1)_extract_dir)/.$$($(1)_file_name).hash && tar --strip-components=1 -xf $$($(1)_source) +$(1)_preprocess_cmds ?= +$(1)_build_cmds ?= +$(1)_config_cmds ?= +$(1)_stage_cmds ?= +$(1)_set_vars ?= + + +all_sources+=$$($(1)_fetched) +endef +#$(foreach dep_target,$($(1)_all_dependencies),$(eval $(1)_dependency_targets=$($(dep_target)_cached))) + + +define int_config_attach_build_config +$(eval $(call $(1)_set_vars,$(1))) +$(1)_cflags+=$($(1)_cflags_$(release_type)) +$(1)_cflags+=$($(1)_cflags_$(host_arch)) $($(1)_cflags_$(host_arch)_$(release_type)) +$(1)_cflags+=$($(1)_cflags_$(host_os)) $($(1)_cflags_$(host_os)_$(release_type)) +$(1)_cflags+=$($(1)_cflags_$(host_arch)_$(host_os)) $($(1)_cflags_$(host_arch)_$(host_os)_$(release_type)) + +$(1)_cxxflags+=$($(1)_cxxflags_$(release_type)) +$(1)_cxxflags+=$($(1)_cxxflags_$(host_arch)) $($(1)_cxxflags_$(host_arch)_$(release_type)) +$(1)_cxxflags+=$($(1)_cxxflags_$(host_os)) $($(1)_cxxflags_$(host_os)_$(release_type)) +$(1)_cxxflags+=$($(1)_cxxflags_$(host_arch)_$(host_os)) $($(1)_cxxflags_$(host_arch)_$(host_os)_$(release_type)) + +$(1)_cppflags+=$($(1)_cppflags_$(release_type)) +$(1)_cppflags+=$($(1)_cppflags_$(host_arch)) $($(1)_cppflags_$(host_arch)_$(release_type)) +$(1)_cppflags+=$($(1)_cppflags_$(host_os)) $($(1)_cppflags_$(host_os)_$(release_type)) +$(1)_cppflags+=$($(1)_cppflags_$(host_arch)_$(host_os)) $($(1)_cppflags_$(host_arch)_$(host_os)_$(release_type)) + +$(1)_ldflags+=$($(1)_ldflags_$(release_type)) +$(1)_ldflags+=$($(1)_ldflags_$(host_arch)) $($(1)_ldflags_$(host_arch)_$(release_type)) +$(1)_ldflags+=$($(1)_ldflags_$(host_os)) $($(1)_ldflags_$(host_os)_$(release_type)) +$(1)_ldflags+=$($(1)_ldflags_$(host_arch)_$(host_os)) $($(1)_ldflags_$(host_arch)_$(host_os)_$(release_type)) + +$(1)_build_opts+=$$($(1)_build_opts_$(release_type)) +$(1)_build_opts+=$$($(1)_build_opts_$(host_arch)) $$($(1)_build_opts_$(host_arch)_$(release_type)) +$(1)_build_opts+=$$($(1)_build_opts_$(host_os)) $$($(1)_build_opts_$(host_os)_$(release_type)) +$(1)_build_opts+=$$($(1)_build_opts_$(host_arch)_$(host_os)) $$($(1)_build_opts_$(host_arch)_$(host_os)_$(release_type)) + +$(1)_config_opts+=$$($(1)_config_opts_$(release_type)) +$(1)_config_opts+=$$($(1)_config_opts_$(host_arch)) $$($(1)_config_opts_$(host_arch)_$(release_type)) +$(1)_config_opts+=$$($(1)_config_opts_$(host_os)) $$($(1)_config_opts_$(host_os)_$(release_type)) +$(1)_config_opts+=$$($(1)_config_opts_$(host_arch)_$(host_os)) $$($(1)_config_opts_$(host_arch)_$(host_os)_$(release_type)) + +$(1)_config_env+=$$($(1)_config_env_$(release_type)) +$(1)_config_env+=$($(1)_config_env_$(host_arch)) $($(1)_config_env_$(host_arch)_$(release_type)) +$(1)_config_env+=$($(1)_config_env_$(host_os)) $($(1)_config_env_$(host_os)_$(release_type)) +$(1)_config_env+=$($(1)_config_env_$(host_arch)_$(host_os)) $($(1)_config_env_$(host_arch)_$(host_os)_$(release_type)) + +$(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)_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),) +$(1)_autoconf += NM="$$($(1)_nm)" +endif +ifneq ($($(1)_ranlib),) +$(1)_autoconf += RANLIB="$$($(1)_ranlib)" +endif +ifneq ($($(1)_ar),) +$(1)_autoconf += AR="$$($(1)_ar)" +endif +ifneq ($($(1)_cflags),) +$(1)_autoconf += CFLAGS="$$($(1)_cflags)" +endif +ifneq ($($(1)_cxxflags),) +$(1)_autoconf += CXXFLAGS="$$($(1)_cxxflags)" +endif +ifneq ($($(1)_cppflags),) +$(1)_autoconf += CPPFLAGS="$$($(1)_cppflags)" +endif +ifneq ($($(1)_ldflags),) +$(1)_autoconf += LDFLAGS="$$($(1)_ldflags)" +endif +endef + +define int_add_cmds +$($(1)_fetched): + $(AT)mkdir -p $$(@D) $(SOURCES_PATH) + $(AT)rm -f $$@ + $(AT)touch $$@ + $(AT)cd $$(@D); $(call $(1)_fetch_cmds,$(1)) + $(AT)cd $($(1)_source_dir); $(foreach source,$($(1)_all_sources),$(build_SHA256SUM) $(source) >> $$(@);) + $(AT)touch $$@ +$($(1)_extracted): | $($(1)_fetched) + $(AT)echo Extracting $(1)... + $(AT)mkdir -p $$(@D) + $(AT)cd $$(@D); $(call $(1)_extract_cmds,$(1)) + $(AT)touch $$@ +$($(1)_preprocessed): | $($(1)_dependencies) $($(1)_extracted) + $(AT)echo Preprocessing $(1)... + $(AT)mkdir -p $$(@D) $($(1)_patch_dir) + $(AT)$(foreach patch,$($(1)_patches),cd $(PATCHES_PATH)/$(1); cp $(patch) $($(1)_patch_dir) ;) + $(AT)cd $$(@D); $(call $(1)_preprocess_cmds, $(1)) + $(AT)touch $$@ +$($(1)_configured): | $($(1)_preprocessed) + $(AT)echo Configuring $(1)... + $(AT)rm -rf $(host_prefix); mkdir -p $(host_prefix)/lib; cd $(host_prefix); $(foreach package,$($(1)_all_dependencies), tar xf $($(package)_cached); ) + $(AT)mkdir -p $$(@D) + $(AT)+cd $$(@D); $($(1)_config_env) $(call $(1)_config_cmds, $(1)) + $(AT)touch $$@ +$($(1)_built): | $($(1)_configured) + $(AT)echo Building $(1)... + $(AT)mkdir -p $$(@D) + $(AT)+cd $$(@D); $($(1)_build_env) $(call $(1)_build_cmds, $(1)) + $(AT)touch $$@ +$($(1)_staged): | $($(1)_built) + $(AT)echo Staging $(1)... + $(AT)mkdir -p $($(1)_staging_dir)/$(host_prefix) + $(AT)cd $($(1)_build_dir); $($(1)_stage_env) $(call $(1)_stage_cmds, $(1)) + $(AT)rm -rf $($(1)_extract_dir) + $(AT)touch $$@ +$($(1)_postprocessed): | $($(1)_staged) + $(AT)echo Postprocessing $(1)... + $(AT)cd $($(1)_staging_prefix_dir); $(call $(1)_postprocess_cmds) + $(AT)touch $$@ +$($(1)_cached): | $($(1)_dependencies) $($(1)_postprocessed) + $(AT)echo Caching $(1)... + $(AT)cd $$($(1)_staging_dir)/$(host_prefix); find . | sort | tar --no-recursion -czf $$($(1)_staging_dir)/$$(@F) -T - + $(AT)mkdir -p $$(@D) + $(AT)rm -rf $$(@D) && mkdir -p $$(@D) + $(AT)mv $$($(1)_staging_dir)/$$(@F) $$(@) + $(AT)rm -rf $($(1)_staging_dir) +$($(1)_cached_checksum): $($(1)_cached) + $(AT)cd $$(@D); $(build_SHA256SUM) $$( $$(@) + +.PHONY: $(1) +$(1): | $($(1)_cached_checksum) +.SECONDARY: $($(1)_cached) $($(1)_postprocessed) $($(1)_staged) $($(1)_built) $($(1)_configured) $($(1)_preprocessed) $($(1)_extracted) $($(1)_fetched) + +endef + +# These functions create the build targets for each package. They must be +# broken down into small steps so that each part is done for all packages +# before moving on to the next step. Otherwise, a package's info +# (build-id for example) would only be available to another package if it +# happened to be computed already. + +#set the type for host/build packages. +$(foreach native_package,$(native_packages),$(eval $(native_package)_type=build)) +$(foreach package,$(packages),$(eval $(package)_type=$(host_arch)_$(host_os))) + +#set overridable defaults +$(foreach package,$(all_packages),$(eval $(call int_vars,$(package)))) + +#include package files +$(foreach package,$(all_packages),$(eval include packages/$(package).mk)) + +#compute a hash of all files that comprise this package's build recipe +$(foreach package,$(all_packages),$(eval $(call int_get_build_recipe_hash,$(package)))) + +#generate a unique id for this package, incorporating its dependencies as well +$(foreach package,$(all_packages),$(eval $(call int_get_build_id,$(package)))) + +#compute final vars after reading package vars +$(foreach package,$(all_packages),$(eval $(call int_config_attach_build_config,$(package)))) + +#create build targets +$(foreach package,$(all_packages),$(eval $(call int_add_cmds,$(package)))) + +#special exception: if a toolchain package exists, all non-native packages depend on it +$(foreach package,$(packages),$(eval $($(package)_unpacked): |$($($(host_arch)_$(host_os)_native_toolchain)_cached) )) diff --git a/contrib/depends/hosts/darwin.mk b/contrib/depends/hosts/darwin.mk new file mode 100644 index 00000000000..4e58bec74e3 --- /dev/null +++ b/contrib/depends/hosts/darwin.mk @@ -0,0 +1,17 @@ +OSX_MIN_VERSION=10.8 +OSX_SDK_VERSION=10.11 +OSX_SDK=$(SDK_PATH)/MacOSX$(OSX_SDK_VERSION).sdk +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++ + +darwin_CFLAGS=-pipe +darwin_CXXFLAGS=$(darwin_CFLAGS) + +darwin_release_CFLAGS=-O2 +darwin_release_CXXFLAGS=$(darwin_release_CFLAGS) + +darwin_debug_CFLAGS=-O1 +darwin_debug_CXXFLAGS=$(darwin_debug_CFLAGS) + +darwin_native_toolchain=native_cctools diff --git a/contrib/depends/hosts/default.mk b/contrib/depends/hosts/default.mk new file mode 100644 index 00000000000..6f60d6b3fd0 --- /dev/null +++ b/contrib/depends/hosts/default.mk @@ -0,0 +1,26 @@ +default_host_CC = $(host_toolchain)gcc +default_host_CXX = $(host_toolchain)g++ +default_host_AR = $(host_toolchain)ar +default_host_RANLIB = $(host_toolchain)ranlib +default_host_STRIP = $(host_toolchain)strip +default_host_LIBTOOL = $(host_toolchain)libtool +default_host_INSTALL_NAME_TOOL = $(host_toolchain)install_name_tool +default_host_OTOOL = $(host_toolchain)otool +default_host_NM = $(host_toolchain)nm + +define add_host_tool_func +$(host_os)_$1?=$$(default_host_$1) +$(host_arch)_$(host_os)_$1?=$$($(host_os)_$1) +$(host_arch)_$(host_os)_$(release_type)_$1?=$$($(host_os)_$1) +host_$1=$$($(host_arch)_$(host_os)_$1) +endef + +define add_host_flags_func +$(host_arch)_$(host_os)_$1 += $($(host_os)_$1) +$(host_arch)_$(host_os)_$(release_type)_$1 += $($(host_os)_$(release_type)_$1) +host_$1 = $$($(host_arch)_$(host_os)_$1) +host_$(release_type)_$1 = $$($(host_arch)_$(host_os)_$(release_type)_$1) +endef + +$(foreach tool,CC CXX AR RANLIB STRIP NM LIBTOOL OTOOL INSTALL_NAME_TOOL,$(eval $(call add_host_tool_func,$(tool)))) +$(foreach flags,CFLAGS CXXFLAGS CPPFLAGS LDFLAGS, $(eval $(call add_host_flags_func,$(flags)))) diff --git a/contrib/depends/hosts/linux.mk b/contrib/depends/hosts/linux.mk new file mode 100644 index 00000000000..b13a0f1ad71 --- /dev/null +++ b/contrib/depends/hosts/linux.mk @@ -0,0 +1,31 @@ +linux_CFLAGS=-pipe +linux_CXXFLAGS=$(linux_CFLAGS) + +linux_release_CFLAGS=-O2 +linux_release_CXXFLAGS=$(linux_release_CFLAGS) + +linux_debug_CFLAGS=-O1 +linux_debug_CXXFLAGS=$(linux_debug_CFLAGS) + +linux_debug_CPPFLAGS=-D_GLIBCXX_DEBUG -D_GLIBCXX_DEBUG_PEDANTIC + +ifeq (86,$(findstring 86,$(build_arch))) +i686_linux_CC=gcc -m32 +i686_linux_CXX=g++ -m32 +i686_linux_AR=ar +i686_linux_RANLIB=ranlib +i686_linux_NM=nm +i686_linux_STRIP=strip + +x86_64_linux_CC=gcc -m64 +x86_64_linux_CXX=g++ -m64 +x86_64_linux_AR=ar +x86_64_linux_RANLIB=ranlib +x86_64_linux_NM=nm +x86_64_linux_STRIP=strip +else +i686_linux_CC=$(default_host_CC) -m32 +i686_linux_CXX=$(default_host_CXX) -m32 +x86_64_linux_CC=$(default_host_CC) -m64 +x86_64_linux_CXX=$(default_host_CXX) -m64 +endif diff --git a/contrib/depends/hosts/mingw32.mk b/contrib/depends/hosts/mingw32.mk new file mode 100644 index 00000000000..dbfb62fdcf9 --- /dev/null +++ b/contrib/depends/hosts/mingw32.mk @@ -0,0 +1,10 @@ +mingw32_CFLAGS=-pipe +mingw32_CXXFLAGS=$(mingw32_CFLAGS) + +mingw32_release_CFLAGS=-O2 +mingw32_release_CXXFLAGS=$(mingw32_release_CFLAGS) + +mingw32_debug_CFLAGS=-O1 +mingw32_debug_CXXFLAGS=$(mingw32_debug_CFLAGS) + +mingw32_debug_CPPFLAGS=-D_GLIBCXX_DEBUG -D_GLIBCXX_DEBUG_PEDANTIC diff --git a/contrib/depends/packages.md b/contrib/depends/packages.md new file mode 100644 index 00000000000..7c80362509e --- /dev/null +++ b/contrib/depends/packages.md @@ -0,0 +1,147 @@ +Each recipe consists of 3 main parts: defining identifiers, setting build +variables, and defining build commands. + +The package "mylib" will be used here as an example + +General tips: +- mylib_foo is written as $(package)_foo in order to make recipes more similar. + +## Identifiers +Each package is required to define at least these variables: + + $(package)_version: + Version of the upstream library or program. If there is no version, a + placeholder such as 1.0 can be used. + + $(package)_download_path: + Location of the upstream source, without the file-name. Usually http or + ftp. + + $(package)_file_name: + The upstream source filename available at the download path. + + $(package)_sha256_hash: + The sha256 hash of the upstream file + +These variables are optional: + + $(package)_build_subdir: + cd to this dir before running configure/build/stage commands. + + $(package)_download_file: + The file-name of the upstream source if it differs from how it should be + stored locally. This can be used to avoid storing file-names with strange + characters. + + $(package)_dependencies: + Names of any other packages that this one depends on. + + $(package)_patches: + Filenames of any patches needed to build the package + + $(package)_extra_sources: + Any extra files that will be fetched via $(package)_fetch_cmds. These are + specified so that they can be fetched and verified via 'make download'. + + +## Build Variables: +After defining the main identifiers, build variables may be added or customized +before running the build commands. They should be added to a function called +$(package)_set_vars. For example: + + define $(package)_set_vars + ... + endef + +Most variables can be prefixed with the host, architecture, or both, to make +the modifications specific to that case. For example: + + Universal: $(package)_cc=gcc + Linux only: $(package)_linux_cc=gcc + x86_64 only: $(package)_x86_64_cc = gcc + x86_64 linux only: $(package)_x86_64_linux_cc = gcc + +These variables may be set to override or append their default values. + + $(package)_cc + $(package)_cxx + $(package)_objc + $(package)_objcxx + $(package)_ar + $(package)_ranlib + $(package)_libtool + $(package)_nm + $(package)_cflags + $(package)_cxxflags + $(package)_ldflags + $(package)_cppflags + $(package)_config_env + $(package)_build_env + $(package)_stage_env + $(package)_build_opts + $(package)_config_opts + +The *_env variables are used to add environment variables to the respective +commands. + +Many variables respect a debug/release suffix as well, in order to use them for +only the appropriate build config. For example: + + $(package)_cflags_release = -O3 + $(package)_cflags_i686_debug = -g + $(package)_config_opts_release = --disable-debug + +These will be used in addition to the options that do not specify +debug/release. All builds are considered to be release unless DEBUG=1 is set by +the user. Other variables may be defined as needed. + +## Build commands: + + For each build, a unique build dir and staging dir are created. For example, + `work/build/mylib/1.0-1adac830f6e` and `work/staging/mylib/1.0-1adac830f6e`. + + The following build commands are available for each recipe: + + $(package)_fetch_cmds: + Runs from: build dir + Fetch the source file. If undefined, it will be fetched and verified + against its hash. + + $(package)_extract_cmds: + Runs from: build dir + Verify the source file against its hash and extract it. If undefined, the + source is assumed to be a tarball. + + $(package)_preprocess_cmds: + Runs from: build dir/$(package)_build_subdir + Preprocess the source as necessary. If undefined, does nothing. + + $(package)_config_cmds: + Runs from: build dir/$(package)_build_subdir + Configure the source. If undefined, does nothing. + + $(package)_build_cmds: + Runs from: build dir/$(package)_build_subdir + Build the source. If undefined, does nothing. + + $(package)_stage_cmds: + Runs from: build dir/$(package)_build_subdir + Stage the build results. If undefined, does nothing. + + The following variables are available for each recipe: + + $(1)_staging_dir: package's destination sysroot path + $(1)_staging_prefix_dir: prefix path inside of the package's staging dir + $(1)_extract_dir: path to the package's extracted sources + $(1)_build_dir: path where configure/build/stage commands will be run + $(1)_patch_dir: path where the package's patches (if any) are found + +Notes on build commands: + +For packages built with autotools, $($(package)_autoconf) can be used in the +configure step to (usually) correctly configure automatically. Any +$($(package)_config_opts) will be appended. + +Most autotools projects can be properly staged using: + + $(MAKE) DESTDIR=$($(package)_staging_dir) install diff --git a/contrib/depends/packages/bdb.mk b/contrib/depends/packages/bdb.mk new file mode 100644 index 00000000000..6c9876c2c7c --- /dev/null +++ b/contrib/depends/packages/bdb.mk @@ -0,0 +1,31 @@ +package=bdb +$(package)_version=4.8.30 +$(package)_download_path=http://download.oracle.com/berkeley-db +$(package)_file_name=db-$($(package)_version).NC.tar.gz +$(package)_sha256_hash=12edc0df75bf9abd7f82f821795bcee50f42cb2e5f76a6a281b85732798364ef +$(package)_build_subdir=build_unix + +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/' dbinc/atomic.h && \ + sed -i.old 's/atomic_init/atomic_init_db/' dbinc/atomic.h mp/mp_region.c mp/mp_mvcc.c mp/mp_fget.c mutex/mut_method.c mutex/mut_tas.c && \ + cp -f $(BASEDIR)/config.guess $(BASEDIR)/config.sub dist +endef + +define $(package)_config_cmds + ../dist/$($(package)_autoconf) +endef + +define $(package)_build_cmds + $(MAKE) libdb_cxx-4.8.a libdb-4.8.a +endef + +define $(package)_stage_cmds + $(MAKE) DESTDIR=$($(package)_staging_dir) install_lib install_include +endef diff --git a/contrib/depends/packages/boost.mk b/contrib/depends/packages/boost.mk new file mode 100644 index 00000000000..57d96e48212 --- /dev/null +++ b/contrib/depends/packages/boost.mk @@ -0,0 +1,41 @@ +package=boost +$(package)_version=1_63_0 +$(package)_download_path=https://sourceforge.net/projects/boost/files/boost/1.63.0 +$(package)_file_name=$(package)_$($(package)_version).tar.bz2 +$(package)_sha256_hash=beae2529f759f6b3bf3f4969a19c2e9d6f0c503edcb2de4a61d1428519fcb3b0 + +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_linux=threadapi=pthread runtime-link=shared +$(package)_config_opts_darwin=--toolset=darwin-4.2.1 runtime-link=shared +$(package)_config_opts_mingw32=binary-format=pe target-os=windows threadapi=win32 runtime-link=static +$(package)_config_opts_x86_64_mingw32=address-model=64 +$(package)_config_opts_i686_mingw32=address-model=32 +$(package)_config_opts_i686_linux=address-model=32 architecture=x86 +$(package)_toolset_$(host_os)=gcc +$(package)_archiver_$(host_os)=$($(package)_ar) +$(package)_toolset_darwin=darwin +$(package)_archiver_darwin=$($(package)_libtool) +$(package)_config_libraries=chrono,filesystem,program_options,system,thread,test +$(package)_cxxflags=-std=c++11 -fvisibility=hidden +$(package)_cxxflags_linux=-fPIC +endef + +define $(package)_preprocess_cmds + echo "using $(boost_toolset_$(host_os)) : : $($(package)_cxx) : \"$($(package)_cxxflags) $($(package)_cppflags)\" \"$($(package)_ldflags)\" \"$(boost_archiver_$(host_os))\" \"$(host_STRIP)\" \"$(host_RANLIB)\" \"$(host_WINDRES)\" : ;" > user-config.jam +endef + +define $(package)_config_cmds + ./bootstrap.sh --without-icu --with-libraries=$(boost_config_libraries) +endef + +define $(package)_build_cmds + ./b2 -d2 -j2 -d1 --prefix=$($(package)_staging_prefix_dir) $($(package)_config_opts) stage +endef + +define $(package)_stage_cmds + ./b2 -d0 -j4 --prefix=$($(package)_staging_prefix_dir) $($(package)_config_opts) install +endef diff --git a/contrib/depends/packages/curl.mk b/contrib/depends/packages/curl.mk new file mode 100644 index 00000000000..cb1390d26e8 --- /dev/null +++ b/contrib/depends/packages/curl.mk @@ -0,0 +1,27 @@ +package=curl +$(package)_version=7.55.0 +$(package)_download_path=https://curl.haxx.se/download/ +$(package)_file_name=$(package)-$($(package)_version).tar.gz +$(package)_sha256_hash=dae1b1be34f5983e8d46917f2bdbb2335aecd0e57f777f4c32213da6a8050a80 + +define $(package)_set_vars + $(package)_config_opts=--disable-shared + $(package)_config_opts+= --enable-static + $(package)_config_opts_release+=--disable-debug-mode + $(package)_config_opts_linux+=--with-pic +endef + +define $(package)_config_cmds + $($(package)_autoconf) +endef + +define $(package)_build_cmds + $(MAKE) +endef + +define $(package)_stage_cmds + $(MAKE) DESTDIR=$($(package)_staging_dir) install +endef + +define $(package)_postprocess_cmds +endef diff --git a/contrib/depends/packages/dbus.mk b/contrib/depends/packages/dbus.mk new file mode 100644 index 00000000000..90ddcb923f2 --- /dev/null +++ b/contrib/depends/packages/dbus.mk @@ -0,0 +1,23 @@ +package=dbus +$(package)_version=1.10.14 +$(package)_download_path=http://dbus.freedesktop.org/releases/dbus +$(package)_file_name=$(package)-$($(package)_version).tar.gz +$(package)_sha256_hash=23238f70353e38ce5ca183ebc9525c0d97ac00ef640ad29cf794782af6e6a083 +$(package)_dependencies=expat + +define $(package)_set_vars + $(package)_config_opts=--disable-tests --disable-doxygen-docs --disable-xml-docs --disable-static --without-x +endef + +define $(package)_config_cmds + $($(package)_autoconf) +endef + +define $(package)_build_cmds + $(MAKE) -C dbus libdbus-1.la +endef + +define $(package)_stage_cmds + $(MAKE) -C dbus DESTDIR=$($(package)_staging_dir) install-libLTLIBRARIES install-dbusincludeHEADERS install-nodist_dbusarchincludeHEADERS && \ + $(MAKE) DESTDIR=$($(package)_staging_dir) install-pkgconfigDATA +endef diff --git a/contrib/depends/packages/expat.mk b/contrib/depends/packages/expat.mk new file mode 100644 index 00000000000..7f484724a49 --- /dev/null +++ b/contrib/depends/packages/expat.mk @@ -0,0 +1,21 @@ +package=expat +$(package)_version=2.2.1 +$(package)_download_path=https://downloads.sourceforge.net/project/expat/expat/$($(package)_version) +$(package)_file_name=$(package)-$($(package)_version).tar.bz2 +$(package)_sha256_hash=1868cadae4c82a018e361e2b2091de103cd820aaacb0d6cfa49bd2cd83978885 + +define $(package)_set_vars +$(package)_config_opts=--disable-static +endef + +define $(package)_config_cmds + $($(package)_autoconf) +endef + +define $(package)_build_cmds + $(MAKE) +endef + +define $(package)_stage_cmds + $(MAKE) DESTDIR=$($(package)_staging_dir) install +endef diff --git a/contrib/depends/packages/fontconfig.mk b/contrib/depends/packages/fontconfig.mk new file mode 100644 index 00000000000..fb97e0b9ec1 --- /dev/null +++ b/contrib/depends/packages/fontconfig.mk @@ -0,0 +1,22 @@ +package=fontconfig +$(package)_version=2.12.1 +$(package)_download_path=http://www.freedesktop.org/software/fontconfig/release/ +$(package)_file_name=$(package)-$($(package)_version).tar.bz2 +$(package)_sha256_hash=b449a3e10c47e1d1c7a6ec6e2016cca73d3bd68fbbd4f0ae5cc6b573f7d6c7f3 +$(package)_dependencies=freetype expat + +define $(package)_set_vars + $(package)_config_opts=--disable-docs --disable-static +endef + +define $(package)_config_cmds + $($(package)_autoconf) +endef + +define $(package)_build_cmds + $(MAKE) +endef + +define $(package)_stage_cmds + $(MAKE) DESTDIR=$($(package)_staging_dir) install +endef diff --git a/contrib/depends/packages/freetype.mk b/contrib/depends/packages/freetype.mk new file mode 100644 index 00000000000..76b025c4631 --- /dev/null +++ b/contrib/depends/packages/freetype.mk @@ -0,0 +1,22 @@ +package=freetype +$(package)_version=2.7.1 +$(package)_download_path=http://download.savannah.gnu.org/releases/$(package) +$(package)_file_name=$(package)-$($(package)_version).tar.bz2 +$(package)_sha256_hash=3a3bb2c4e15ffb433f2032f50a5b5a92558206822e22bfe8cbe339af4aa82f88 + +define $(package)_set_vars + $(package)_config_opts=--without-zlib --without-png --disable-static + $(package)_config_opts_linux=--with-pic +endef + +define $(package)_config_cmds + $($(package)_autoconf) +endef + +define $(package)_build_cmds + $(MAKE) +endef + +define $(package)_stage_cmds + $(MAKE) DESTDIR=$($(package)_staging_dir) install +endef diff --git a/contrib/depends/packages/libICE.mk b/contrib/depends/packages/libICE.mk new file mode 100644 index 00000000000..fc60323b1c9 --- /dev/null +++ b/contrib/depends/packages/libICE.mk @@ -0,0 +1,23 @@ +package=libICE +$(package)_version=1.0.9 +$(package)_download_path=http://xorg.freedesktop.org/releases/individual/lib/ +$(package)_file_name=$(package)-$($(package)_version).tar.bz2 +$(package)_sha256_hash=8f7032f2c1c64352b5423f6b48a8ebdc339cc63064af34d66a6c9aa79759e202 +$(package)_dependencies=xtrans xproto + +define $(package)_set_vars + $(package)_config_opts=--disable-static --disable-docs --disable-specs --without-xsltproc + $(package)_config_opts_linux=--with-pic +endef + +define $(package)_config_cmds + $($(package)_autoconf) +endef + +define $(package)_build_cmds + $(MAKE) +endef + +define $(package)_stage_cmds + $(MAKE) DESTDIR=$($(package)_staging_dir) install +endef diff --git a/contrib/depends/packages/libSM.mk b/contrib/depends/packages/libSM.mk new file mode 100644 index 00000000000..0f9307ca76a --- /dev/null +++ b/contrib/depends/packages/libSM.mk @@ -0,0 +1,23 @@ +package=libSM +$(package)_version=1.2.2 +$(package)_download_path=http://xorg.freedesktop.org/releases/individual/lib/ +$(package)_file_name=$(package)-$($(package)_version).tar.bz2 +$(package)_sha256_hash=0baca8c9f5d934450a70896c4ad38d06475521255ca63b717a6510fdb6e287bd +$(package)_dependencies=xtrans xproto libICE + +define $(package)_set_vars + $(package)_config_opts=--without-libuuid --without-xsltproc --disable-docs --disable-static + $(package)_config_opts_linux=--with-pic +endef + +define $(package)_config_cmds + $($(package)_autoconf) +endef + +define $(package)_build_cmds + $(MAKE) +endef + +define $(package)_stage_cmds + $(MAKE) DESTDIR=$($(package)_staging_dir) install +endef diff --git a/contrib/depends/packages/libX11.mk b/contrib/depends/packages/libX11.mk new file mode 100644 index 00000000000..178d592ee63 --- /dev/null +++ b/contrib/depends/packages/libX11.mk @@ -0,0 +1,23 @@ +package=libX11 +$(package)_version=1.6.2 +$(package)_download_path=http://xorg.freedesktop.org/releases/individual/lib/ +$(package)_file_name=$(package)-$($(package)_version).tar.bz2 +$(package)_sha256_hash=2aa027e837231d2eeea90f3a4afe19948a6eb4c8b2bec0241eba7dbc8106bd16 +$(package)_dependencies=libxcb xtrans xextproto xproto + +define $(package)_set_vars +$(package)_config_opts=--disable-xkb --disable-static +$(package)_config_opts_linux=--with-pic +endef + +define $(package)_config_cmds + $($(package)_autoconf) +endef + +define $(package)_build_cmds + $(MAKE) +endef + +define $(package)_stage_cmds + $(MAKE) DESTDIR=$($(package)_staging_dir) install +endef diff --git a/contrib/depends/packages/libXau.mk b/contrib/depends/packages/libXau.mk new file mode 100644 index 00000000000..e87df2e4de6 --- /dev/null +++ b/contrib/depends/packages/libXau.mk @@ -0,0 +1,23 @@ +package=libXau +$(package)_version=1.0.8 +$(package)_download_path=http://xorg.freedesktop.org/releases/individual/lib/ +$(package)_file_name=$(package)-$($(package)_version).tar.bz2 +$(package)_sha256_hash=fdd477320aeb5cdd67272838722d6b7d544887dfe7de46e1e7cc0c27c2bea4f2 +$(package)_dependencies=xproto + +define $(package)_set_vars + $(package)_config_opts=--disable-shared + $(package)_config_opts_linux=--with-pic +endef + +define $(package)_config_cmds + $($(package)_autoconf) +endef + +define $(package)_build_cmds + $(MAKE) +endef + +define $(package)_stage_cmds + $(MAKE) DESTDIR=$($(package)_staging_dir) install +endef diff --git a/contrib/depends/packages/libXext.mk b/contrib/depends/packages/libXext.mk new file mode 100644 index 00000000000..4db836066f9 --- /dev/null +++ b/contrib/depends/packages/libXext.mk @@ -0,0 +1,22 @@ +package=libXext +$(package)_version=1.3.2 +$(package)_download_path=http://xorg.freedesktop.org/releases/individual/lib/ +$(package)_file_name=$(package)-$($(package)_version).tar.bz2 +$(package)_sha256_hash=f829075bc646cdc085fa25d98d5885d83b1759ceb355933127c257e8e50432e0 +$(package)_dependencies=xproto xextproto libX11 libXau + +define $(package)_set_vars + $(package)_config_opts=--disable-static +endef + +define $(package)_config_cmds + $($(package)_autoconf) +endef + +define $(package)_build_cmds + $(MAKE) +endef + +define $(package)_stage_cmds + $(MAKE) DESTDIR=$($(package)_staging_dir) install +endef diff --git a/contrib/depends/packages/libxcb.mk b/contrib/depends/packages/libxcb.mk new file mode 100644 index 00000000000..28f2bd6f253 --- /dev/null +++ b/contrib/depends/packages/libxcb.mk @@ -0,0 +1,35 @@ +package=libxcb +$(package)_version=1.10 +$(package)_download_path=http://xcb.freedesktop.org/dist +$(package)_file_name=$(package)-$($(package)_version).tar.bz2 +$(package)_sha256_hash=98d9ab05b636dd088603b64229dd1ab2d2cc02ab807892e107d674f9c3f2d5b5 +$(package)_dependencies=xcb_proto libXau xproto + +define $(package)_set_vars +$(package)_config_opts=--disable-static +endef + +define $(package)_preprocess_cmds + sed "s/pthread-stubs//" -i configure +endef + +# Don't install xcb headers to the default path in order to work around a qt +# build issue: https://bugreports.qt.io/browse/QTBUG-34748 +# When using qt's internal libxcb, it may end up finding the real headers in +# depends staging. Use a non-default path to avoid that. + +define $(package)_config_cmds + $($(package)_autoconf) --includedir=$(host_prefix)/include/xcb-shared +endef + +define $(package)_build_cmds + $(MAKE) +endef + +define $(package)_stage_cmds + $(MAKE) DESTDIR=$($(package)_staging_dir) install +endef + +define $(package)_postprocess_cmds + rm -rf share/man share/doc +endef diff --git a/contrib/depends/packages/miniupnpc.mk b/contrib/depends/packages/miniupnpc.mk new file mode 100644 index 00000000000..1bb8cb5d266 --- /dev/null +++ b/contrib/depends/packages/miniupnpc.mk @@ -0,0 +1,28 @@ +package=miniupnpc +$(package)_version=2.0.20170509 +$(package)_download_path=http://miniupnp.free.fr/files +$(package)_file_name=$(package)-$($(package)_version).tar.gz +$(package)_sha256_hash=d3c368627f5cdfb66d3ebd64ca39ba54d6ff14a61966dbecb8dd296b7039f16a + +define $(package)_set_vars +$(package)_build_opts=CC="$($(package)_cc)" +$(package)_build_opts_darwin=OS=Darwin LIBTOOL="$($(package)_libtool)" +$(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 +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/contrib/depends/packages/native_biplist.mk b/contrib/depends/packages/native_biplist.mk new file mode 100644 index 00000000000..3c6e8900f66 --- /dev/null +++ b/contrib/depends/packages/native_biplist.mk @@ -0,0 +1,20 @@ +package=native_biplist +$(package)_version=0.9 +$(package)_download_path=https://pypi.python.org/packages/source/b/biplist +$(package)_file_name=biplist-$($(package)_version).tar.gz +$(package)_sha256_hash=b57cadfd26e4754efdf89e9e37de87885f9b5c847b2615688ca04adfaf6ca604 +$(package)_install_libdir=$(build_prefix)/lib/python/dist-packages +$(package)_patches=sorted_list.patch + +define $(package)_preprocess_cmds + patch -p1 < $($(package)_patch_dir)/sorted_list.patch +endef + +define $(package)_build_cmds + python setup.py build +endef + +define $(package)_stage_cmds + mkdir -p $($(package)_install_libdir) && \ + python setup.py install --root=$($(package)_staging_dir) --prefix=$(build_prefix) --install-lib=$($(package)_install_libdir) +endef diff --git a/contrib/depends/packages/native_ccache.mk b/contrib/depends/packages/native_ccache.mk new file mode 100644 index 00000000000..4ed61a49e9c --- /dev/null +++ b/contrib/depends/packages/native_ccache.mk @@ -0,0 +1,25 @@ +package=native_ccache +$(package)_version=3.3.3 +$(package)_download_path=https://samba.org/ftp/ccache +$(package)_file_name=ccache-$($(package)_version).tar.bz2 +$(package)_sha256_hash=2985bc5e32ebe38d2958d508eb54ddcad39eed909489c0c2988035214597ca54 + +define $(package)_set_vars +$(package)_config_opts= +endef + +define $(package)_config_cmds + $($(package)_autoconf) +endef + +define $(package)_build_cmds + $(MAKE) +endef + +define $(package)_stage_cmds + $(MAKE) DESTDIR=$($(package)_staging_dir) install +endef + +define $(package)_postprocess_cmds + rm -rf lib include +endef diff --git a/contrib/depends/packages/native_cctools.mk b/contrib/depends/packages/native_cctools.mk new file mode 100644 index 00000000000..44d238cc4c2 --- /dev/null +++ b/contrib/depends/packages/native_cctools.mk @@ -0,0 +1,65 @@ +package=native_cctools +$(package)_version=807d6fd1be5d2224872e381870c0a75387fe05e6 +$(package)_download_path=https://github.com/theuni/cctools-port/archive +$(package)_file_name=$($(package)_version).tar.gz +$(package)_sha256_hash=a09c9ba4684670a0375e42d9d67e7f12c1f62581a27f28f7c825d6d7032ccc6a +$(package)_build_subdir=cctools +$(package)_clang_version=3.7.1 +$(package)_clang_download_path=http://llvm.org/releases/$($(package)_clang_version) +$(package)_clang_download_file=clang+llvm-$($(package)_clang_version)-x86_64-linux-gnu-ubuntu-14.04.tar.xz +$(package)_clang_file_name=clang-llvm-$($(package)_clang_version)-x86_64-linux-gnu-ubuntu-14.04.tar.xz +$(package)_clang_sha256_hash=99b28a6b48e793705228a390471991386daa33a9717cd9ca007fcdde69608fd9 +$(package)_extra_sources=$($(package)_clang_file_name) + +define $(package)_fetch_cmds +$(call fetch_file,$(package),$($(package)_download_path),$($(package)_download_file),$($(package)_file_name),$($(package)_sha256_hash)) && \ +$(call fetch_file,$(package),$($(package)_clang_download_path),$($(package)_clang_download_file),$($(package)_clang_file_name),$($(package)_clang_sha256_hash)) +endef + +define $(package)_extract_cmds + mkdir -p $($(package)_extract_dir) && \ + echo "$($(package)_sha256_hash) $($(package)_source)" > $($(package)_extract_dir)/.$($(package)_file_name).hash && \ + echo "$($(package)_clang_sha256_hash) $($(package)_source_dir)/$($(package)_clang_file_name)" >> $($(package)_extract_dir)/.$($(package)_file_name).hash && \ + $(build_SHA256SUM) -c $($(package)_extract_dir)/.$($(package)_file_name).hash && \ + mkdir -p toolchain/bin toolchain/lib/clang/3.5/include && \ + tar --strip-components=1 -C toolchain -xf $($(package)_source_dir)/$($(package)_clang_file_name) && \ + rm -f toolchain/lib/libc++abi.so* && \ + echo "#!/bin/sh" > toolchain/bin/$(host)-dsymutil && \ + echo "exit 0" >> toolchain/bin/$(host)-dsymutil && \ + chmod +x toolchain/bin/$(host)-dsymutil && \ + tar --strip-components=1 -xf $($(package)_source) +endef + +define $(package)_set_vars +$(package)_config_opts=--target=$(host) --disable-lto-support +$(package)_ldflags+=-Wl,-rpath=\\$$$$$$$$\$$$$$$$$ORIGIN/../lib +$(package)_cc=$($(package)_extract_dir)/toolchain/bin/clang +$(package)_cxx=$($(package)_extract_dir)/toolchain/bin/clang++ +endef + +define $(package)_preprocess_cmds + cd $($(package)_build_subdir); ./autogen.sh && \ + sed -i.old "/define HAVE_PTHREADS/d" ld64/src/ld/InputFiles.h +endef + +define $(package)_config_cmds + $($(package)_autoconf) +endef + +define $(package)_build_cmds + $(MAKE) +endef + +define $(package)_stage_cmds + $(MAKE) DESTDIR=$($(package)_staging_dir) install && \ + cd $($(package)_extract_dir)/toolchain && \ + mkdir -p $($(package)_staging_prefix_dir)/lib/clang/$($(package)_clang_version)/include && \ + mkdir -p $($(package)_staging_prefix_dir)/bin $($(package)_staging_prefix_dir)/include && \ + cp bin/clang $($(package)_staging_prefix_dir)/bin/ &&\ + cp -P bin/clang++ $($(package)_staging_prefix_dir)/bin/ &&\ + cp lib/libLTO.so $($(package)_staging_prefix_dir)/lib/ && \ + cp -rf lib/clang/$($(package)_clang_version)/include/* $($(package)_staging_prefix_dir)/lib/clang/$($(package)_clang_version)/include/ && \ + cp bin/llvm-dsymutil $($(package)_staging_prefix_dir)/bin/$(host)-dsymutil && \ + if `test -d include/c++/`; then cp -rf include/c++/ $($(package)_staging_prefix_dir)/include/; fi && \ + if `test -d lib/c++/`; then cp -rf lib/c++/ $($(package)_staging_prefix_dir)/lib/; fi +endef diff --git a/contrib/depends/packages/native_cdrkit.mk b/contrib/depends/packages/native_cdrkit.mk new file mode 100644 index 00000000000..cf694edb30e --- /dev/null +++ b/contrib/depends/packages/native_cdrkit.mk @@ -0,0 +1,26 @@ +package=native_cdrkit +$(package)_version=1.1.11 +$(package)_download_path=http://distro.ibiblio.org/fatdog/source/600/c +$(package)_file_name=cdrkit-$($(package)_version).tar.bz2 +$(package)_sha256_hash=b50d64c214a65b1a79afe3a964c691931a4233e2ba605d793eb85d0ac3652564 +$(package)_patches=cdrkit-deterministic.patch + +define $(package)_preprocess_cmds + patch -p1 < $($(package)_patch_dir)/cdrkit-deterministic.patch +endef + +define $(package)_config_cmds + cmake -DCMAKE_INSTALL_PREFIX=$(build_prefix) +endef + +define $(package)_build_cmds + $(MAKE) genisoimage +endef + +define $(package)_stage_cmds + $(MAKE) DESTDIR=$($(package)_staging_dir) -C genisoimage install +endef + +define $(package)_postprocess_cmds + rm bin/isovfy bin/isoinfo bin/isodump bin/isodebug bin/devdump +endef diff --git a/contrib/depends/packages/native_ds_store.mk b/contrib/depends/packages/native_ds_store.mk new file mode 100644 index 00000000000..49f5829ac1c --- /dev/null +++ b/contrib/depends/packages/native_ds_store.mk @@ -0,0 +1,17 @@ +package=native_ds_store +$(package)_version=1.1.0 +$(package)_download_path=https://bitbucket.org/al45tair/ds_store/get +$(package)_download_file=v$($(package)_version).tar.bz2 +$(package)_file_name=$(package)-$($(package)_version).tar.bz2 +$(package)_sha256_hash=921596764d71d1bbd3297a90ef6d286f718794d667e4f81d91d14053525d64c1 +$(package)_install_libdir=$(build_prefix)/lib/python/dist-packages +$(package)_dependencies=native_biplist + +define $(package)_build_cmds + python setup.py build +endef + +define $(package)_stage_cmds + mkdir -p $($(package)_install_libdir) && \ + python setup.py install --root=$($(package)_staging_dir) --prefix=$(build_prefix) --install-lib=$($(package)_install_libdir) +endef diff --git a/contrib/depends/packages/native_libdmg-hfsplus.mk b/contrib/depends/packages/native_libdmg-hfsplus.mk new file mode 100644 index 00000000000..a4ffb6046cc --- /dev/null +++ b/contrib/depends/packages/native_libdmg-hfsplus.mk @@ -0,0 +1,22 @@ +package=native_libdmg-hfsplus +$(package)_version=0.1 +$(package)_download_path=https://github.com/theuni/libdmg-hfsplus/archive +$(package)_file_name=libdmg-hfsplus-v$($(package)_version).tar.gz +$(package)_sha256_hash=6569a02eb31c2827080d7d59001869ea14484c281efab0ae7f2b86af5c3120b3 +$(package)_build_subdir=build + +define $(package)_preprocess_cmds + mkdir build +endef + +define $(package)_config_cmds + cmake -DCMAKE_INSTALL_PREFIX:PATH=$(build_prefix)/bin .. +endef + +define $(package)_build_cmds + $(MAKE) -C dmg +endef + +define $(package)_stage_cmds + $(MAKE) DESTDIR=$($(package)_staging_dir) -C dmg install +endef diff --git a/contrib/depends/packages/native_mac_alias.mk b/contrib/depends/packages/native_mac_alias.mk new file mode 100644 index 00000000000..85a8a402bf9 --- /dev/null +++ b/contrib/depends/packages/native_mac_alias.mk @@ -0,0 +1,21 @@ +package=native_mac_alias +$(package)_version=1.1.0 +$(package)_download_path=https://bitbucket.org/al45tair/mac_alias/get +$(package)_download_file=v$($(package)_version).tar.bz2 +$(package)_file_name=$(package)-$($(package)_version).tar.bz2 +$(package)_sha256_hash=87ad827e66790028361e43fc754f68ed041a9bdb214cca03c853f079b04fb120 +$(package)_install_libdir=$(build_prefix)/lib/python/dist-packages +$(package)_patches=python3.patch + +define $(package)_preprocess_cmds + patch -p1 < $($(package)_patch_dir)/python3.patch +endef + +define $(package)_build_cmds + python setup.py build +endef + +define $(package)_stage_cmds + mkdir -p $($(package)_install_libdir) && \ + python setup.py install --root=$($(package)_staging_dir) --prefix=$(build_prefix) --install-lib=$($(package)_install_libdir) +endef diff --git a/contrib/depends/packages/openssl.mk b/contrib/depends/packages/openssl.mk new file mode 100644 index 00000000000..5ee9f17a632 --- /dev/null +++ b/contrib/depends/packages/openssl.mk @@ -0,0 +1,78 @@ +package=openssl +$(package)_version=1.0.1k +$(package)_download_path=https://www.openssl.org/source +$(package)_file_name=$(package)-$($(package)_version).tar.gz +$(package)_sha256_hash=8f9faeaebad088e772f4ef5e38252d472be4d878c6b3a2718c10a4fcebe7a41c + +define $(package)_set_vars +$(package)_config_env=AR="$($(package)_ar)" RANLIB="$($(package)_ranlib)" CC="$($(package)_cc)" +$(package)_config_opts=--prefix=$(host_prefix) --openssldir=$(host_prefix)/etc/openssl +$(package)_config_opts+=no-camellia +$(package)_config_opts+=no-capieng +$(package)_config_opts+=no-cast +$(package)_config_opts+=no-comp +$(package)_config_opts+=no-dso +$(package)_config_opts+=no-dtls1 +$(package)_config_opts+=no-ec_nistp_64_gcc_128 +$(package)_config_opts+=no-gost +$(package)_config_opts+=no-gmp +$(package)_config_opts+=no-heartbeats +$(package)_config_opts+=no-idea +$(package)_config_opts+=no-jpake +$(package)_config_opts+=no-krb5 +$(package)_config_opts+=no-libunbound +$(package)_config_opts+=no-md2 +$(package)_config_opts+=no-mdc2 +$(package)_config_opts+=no-rc4 +$(package)_config_opts+=no-rc5 +$(package)_config_opts+=no-rdrand +$(package)_config_opts+=no-rfc3779 +$(package)_config_opts+=no-rsax +$(package)_config_opts+=no-sctp +$(package)_config_opts+=no-seed +$(package)_config_opts+=no-sha0 +$(package)_config_opts+=no-shared +$(package)_config_opts+=no-ssl-trace +$(package)_config_opts+=no-ssl2 +$(package)_config_opts+=no-ssl3 +$(package)_config_opts+=no-static_engine +$(package)_config_opts+=no-store +$(package)_config_opts+=no-unit-test +$(package)_config_opts+=no-weak-ssl-ciphers +$(package)_config_opts+=no-whirlpool +$(package)_config_opts+=no-zlib +$(package)_config_opts+=no-zlib-dynamic +$(package)_config_opts+=$($(package)_cflags) $($(package)_cppflags) +$(package)_config_opts_linux=-fPIC -Wa,--noexecstack +$(package)_config_opts_x86_64_linux=linux-x86_64 +$(package)_config_opts_i686_linux=linux-generic32 +$(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 +endef + +define $(package)_preprocess_cmds + sed -i.old "/define DATE/d" util/mkbuildinf.pl && \ + sed -i.old "s|engines apps test|engines|" Makefile.org +endef + +define $(package)_config_cmds + ./Configure $($(package)_config_opts) +endef + +define $(package)_build_cmds + $(MAKE) -j1 build_libs libcrypto.pc libssl.pc openssl.pc +endef + +define $(package)_stage_cmds + $(MAKE) INSTALL_PREFIX=$($(package)_staging_dir) -j1 install_sw +endef + +define $(package)_postprocess_cmds + rm -rf share bin etc +endef diff --git a/contrib/depends/packages/packages.mk b/contrib/depends/packages/packages.mk new file mode 100644 index 00000000000..c6aa02b38bb --- /dev/null +++ b/contrib/depends/packages/packages.mk @@ -0,0 +1,20 @@ +packages:=boost openssl curl +native_packages := native_ccache + +qt_packages = qrencode zlib + +qt_x86_64_linux_packages:=qt expat dbus libxcb xcb_proto libXau xproto freetype fontconfig libX11 xextproto libXext xtrans +qt_i686_linux_packages:=$(qt_x86_64_linux_packages) + +qt_darwin_packages=qt +qt_mingw32_packages=qt + +wallet_packages=bdb + +upnp_packages=miniupnpc + +darwin_native_packages = native_biplist native_ds_store native_mac_alias + +ifneq ($(build_os),darwin) +darwin_native_packages += native_cctools native_cdrkit native_libdmg-hfsplus +endif diff --git a/contrib/depends/packages/qrencode.mk b/contrib/depends/packages/qrencode.mk new file mode 100644 index 00000000000..7b21247133f --- /dev/null +++ b/contrib/depends/packages/qrencode.mk @@ -0,0 +1,22 @@ +package=qrencode +$(package)_version=3.4.4 +$(package)_download_path=https://fukuchi.org/works/qrencode/ +$(package)_file_name=qrencode-$(qrencode_version).tar.bz2 +$(package)_sha256_hash=efe5188b1ddbcbf98763b819b146be6a90481aac30cfc8d858ab78a19cde1fa5 + +define $(package)_set_vars +$(package)_config_opts=--disable-shared -without-tools --disable-sdltest +$(package)_config_opts_linux=--with-pic +endef + +define $(package)_config_cmds + $($(package)_autoconf) +endef + +define $(package)_build_cmds + $(MAKE) +endef + +define $(package)_stage_cmds + $(MAKE) DESTDIR=$($(package)_staging_dir) install +endef diff --git a/contrib/depends/packages/qt.mk b/contrib/depends/packages/qt.mk new file mode 100644 index 00000000000..f45355c492a --- /dev/null +++ b/contrib/depends/packages/qt.mk @@ -0,0 +1,198 @@ +PACKAGE=qt +$(package)_version=5.7.1 +$(package)_download_path=http://download.qt.io/official_releases/qt/5.7/$($(package)_version)/submodules +$(package)_suffix=opensource-src-$($(package)_version).tar.gz +$(package)_file_name=qtbase-$($(package)_suffix) +$(package)_sha256_hash=95f83e532d23b3ddbde7973f380ecae1bac13230340557276f75f2e37984e410 +$(package)_dependencies=openssl zlib +$(package)_linux_dependencies=freetype fontconfig libxcb libX11 xproto libXext +$(package)_build_subdir=qtbase +$(package)_qt_libs=corelib network widgets gui plugins testlib concurrent +$(package)_patches=mac-qmake.conf mingw-uuidof.patch pidlist_absolute.patch fix-xcb-include-order.patch fix_qt_pkgconfig.patch + +$(package)_qttranslations_file_name=qttranslations-$($(package)_suffix) +$(package)_qttranslations_sha256_hash=3a15aebd523c6d89fb97b2d3df866c94149653a26d27a00aac9b6d3020bc5a1d + +$(package)_qttools_file_name=qttools-$($(package)_suffix) +$(package)_qttools_sha256_hash=22d67de915cb8cd93e16fdd38fa006224ad9170bd217c2be1e53045a8dd02f0f + +$(package)_qtactiveqt_file_name=qtactiveqt-$($(package)_suffix) +$(package)_qtactiveqt_sha256_hash=57b39e9fe1d8d430da14d38d8c0de39bede1cd3ce1540f3d51c1fa0a2ef149cf +$(package)_qtactiveqt_libs=axcontainer + +$(package)_extra_sources = $($(package)_qttranslations_file_name) +$(package)_extra_sources += $($(package)_qttools_file_name) +$(package)_extra_sources += $($(package)_qtactiveqt_file_name) + +define $(package)_set_vars +$(package)_config_opts_release = -release +$(package)_config_opts_debug = -debug +$(package)_config_opts += -bindir $(build_prefix)/bin +$(package)_config_opts += -c++std c++11 +$(package)_config_opts += -confirm-license +$(package)_config_opts += -dbus-runtime +$(package)_config_opts += -hostprefix $(build_prefix) +$(package)_config_opts += -no-alsa +$(package)_config_opts += -no-audio-backend +$(package)_config_opts += -no-cups +$(package)_config_opts += -no-egl +$(package)_config_opts += -no-eglfs +$(package)_config_opts += -no-feature-style-windowsmobile +$(package)_config_opts += -no-feature-style-windowsce +$(package)_config_opts += -no-freetype +$(package)_config_opts += -no-gif +$(package)_config_opts += -no-glib +$(package)_config_opts += -no-gstreamer +$(package)_config_opts += -no-icu +$(package)_config_opts += -no-iconv +$(package)_config_opts += -no-kms +$(package)_config_opts += -no-linuxfb +$(package)_config_opts += -no-libudev +$(package)_config_opts += -no-mitshm +$(package)_config_opts += -no-mtdev +$(package)_config_opts += -no-pulseaudio +$(package)_config_opts += -no-openvg +$(package)_config_opts += -no-reduce-relocations +$(package)_config_opts += -no-qml-debug +$(package)_config_opts += -no-sql-db2 +$(package)_config_opts += -no-sql-ibase +$(package)_config_opts += -no-sql-oci +$(package)_config_opts += -no-sql-tds +$(package)_config_opts += -no-sql-mysql +$(package)_config_opts += -no-sql-odbc +$(package)_config_opts += -no-sql-psql +$(package)_config_opts += -no-sql-sqlite +$(package)_config_opts += -no-sql-sqlite2 +$(package)_config_opts += -no-use-gold-linker +$(package)_config_opts += -no-xinput2 +$(package)_config_opts += -no-xrender +$(package)_config_opts += -nomake examples +$(package)_config_opts += -nomake tests +$(package)_config_opts += -opensource +$(package)_config_opts += -openssl-linked +$(package)_config_opts += -optimized-qmake +$(package)_config_opts += -pch +$(package)_config_opts += -pkg-config +$(package)_config_opts += -prefix $(host_prefix) +$(package)_config_opts += -qt-libpng +$(package)_config_opts += -qt-libjpeg +$(package)_config_opts += -qt-pcre +$(package)_config_opts += -system-zlib +$(package)_config_opts += -reduce-exports +$(package)_config_opts += -static +$(package)_config_opts += -silent +$(package)_config_opts += -v +$(package)_config_opts += -no-feature-printer +$(package)_config_opts += -no-feature-printdialog + +ifneq ($(build_os),darwin) +$(package)_config_opts_darwin = -xplatform macx-clang-linux +$(package)_config_opts_darwin += -device-option MAC_SDK_PATH=$(OSX_SDK) +$(package)_config_opts_darwin += -device-option MAC_SDK_VERSION=$(OSX_SDK_VERSION) +$(package)_config_opts_darwin += -device-option CROSS_COMPILE="$(host)-" +$(package)_config_opts_darwin += -device-option MAC_MIN_VERSION=$(OSX_MIN_VERSION) +$(package)_config_opts_darwin += -device-option MAC_TARGET=$(host) +$(package)_config_opts_darwin += -device-option MAC_LD64_VERSION=$(LD64_VERSION) +endif + +$(package)_config_opts_linux = -qt-xkbcommon +$(package)_config_opts_linux += -qt-xcb +$(package)_config_opts_linux += -system-freetype +$(package)_config_opts_linux += -no-sm +$(package)_config_opts_linux += -fontconfig +$(package)_config_opts_linux += -no-opengl +$(package)_config_opts_arm_linux = -platform linux-g++ -xplatform $(host) +$(package)_config_opts_i686_linux = -xplatform linux-g++-32 +$(package)_config_opts_mingw32 = -no-opengl -xplatform win32-g++ -device-option CROSS_COMPILE="$(host)-" +$(package)_build_env = QT_RCC_TEST=1 +endef + +define $(package)_fetch_cmds +$(call fetch_file,$(package),$($(package)_download_path),$($(package)_download_file),$($(package)_file_name),$($(package)_sha256_hash)) && \ +$(call fetch_file,$(package),$($(package)_download_path),$($(package)_qttranslations_file_name),$($(package)_qttranslations_file_name),$($(package)_qttranslations_sha256_hash)) && \ +$(call fetch_file,$(package),$($(package)_download_path),$($(package)_qttools_file_name),$($(package)_qttools_file_name),$($(package)_qttools_sha256_hash)) && \ +$(call fetch_file,$(package),$($(package)_download_path),$($(package)_qtactiveqt_file_name),$($(package)_qtactiveqt_file_name),$($(package)_qtactiveqt_sha256_hash)) +endef + +define $(package)_extract_cmds + mkdir -p $($(package)_extract_dir) && \ + echo "$($(package)_sha256_hash) $($(package)_source)" > $($(package)_extract_dir)/.$($(package)_file_name).hash && \ + echo "$($(package)_qttranslations_sha256_hash) $($(package)_source_dir)/$($(package)_qttranslations_file_name)" >> $($(package)_extract_dir)/.$($(package)_file_name).hash && \ + echo "$($(package)_qttools_sha256_hash) $($(package)_source_dir)/$($(package)_qttools_file_name)" >> $($(package)_extract_dir)/.$($(package)_file_name).hash && \ + echo "$($(package)_qtactiveqt_sha256_hash) $($(package)_source_dir)/$($(package)_qtactiveqt_file_name)" >> $($(package)_extract_dir)/.$($(package)_file_name).hash && \ + $(build_SHA256SUM) -c $($(package)_extract_dir)/.$($(package)_file_name).hash && \ + mkdir qtbase && \ + tar --strip-components=1 -xf $($(package)_source) -C qtbase && \ + mkdir qttranslations && \ + tar --strip-components=1 -xf $($(package)_source_dir)/$($(package)_qttranslations_file_name) -C qttranslations && \ + mkdir qttools && \ + tar --strip-components=1 -xf $($(package)_source_dir)/$($(package)_qttools_file_name) -C qttools &&\ + mkdir qtactiveqt && \ + tar --strip-components=1 -xf $($(package)_source_dir)/$($(package)_qtactiveqt_file_name) -C qtactiveqt +endef + + +define $(package)_preprocess_cmds + sed -i.old "s|updateqm.commands = \$$$$\$$$$LRELEASE|updateqm.commands = $($(package)_extract_dir)/qttools/bin/lrelease|" qttranslations/translations/translations.pro && \ + sed -i.old "/updateqm.depends =/d" qttranslations/translations/translations.pro && \ + sed -i.old "s/src_plugins.depends = src_sql src_xml src_network/src_plugins.depends = src_xml src_network/" qtbase/src/src.pro && \ + sed -i.old "s|X11/extensions/XIproto.h|X11/X.h|" qtbase/src/plugins/platforms/xcb/qxcbxsettings.cpp && \ + sed -i.old 's/if \[ "$$$$XPLATFORM_MAC" = "yes" \]; then xspecvals=$$$$(macSDKify/if \[ "$$$$BUILD_ON_MAC" = "yes" \]; then xspecvals=$$$$(macSDKify/' qtbase/configure && \ + sed -i.old 's/CGEventCreateMouseEvent(0, kCGEventMouseMoved, pos, 0)/CGEventCreateMouseEvent(0, kCGEventMouseMoved, pos, kCGMouseButtonLeft)/' qtbase/src/plugins/platforms/cocoa/qcocoacursor.mm && \ + mkdir -p qtbase/mkspecs/macx-clang-linux &&\ + cp -f qtbase/mkspecs/macx-clang/Info.plist.lib qtbase/mkspecs/macx-clang-linux/ &&\ + cp -f qtbase/mkspecs/macx-clang/Info.plist.app qtbase/mkspecs/macx-clang-linux/ &&\ + cp -f qtbase/mkspecs/macx-clang/qplatformdefs.h qtbase/mkspecs/macx-clang-linux/ &&\ + cp -f $($(package)_patch_dir)/mac-qmake.conf qtbase/mkspecs/macx-clang-linux/qmake.conf && \ + patch -p1 < $($(package)_patch_dir)/mingw-uuidof.patch && \ + patch -p1 < $($(package)_patch_dir)/pidlist_absolute.patch && \ + patch -p1 < $($(package)_patch_dir)/fix-xcb-include-order.patch && \ + patch -p1 < $($(package)_patch_dir)/fix_qt_pkgconfig.patch && \ + echo "!host_build: QMAKE_CFLAGS += $($(package)_cflags) $($(package)_cppflags)" >> qtbase/mkspecs/common/gcc-base.conf && \ + echo "!host_build: QMAKE_CXXFLAGS += $($(package)_cxxflags) $($(package)_cppflags)" >> qtbase/mkspecs/common/gcc-base.conf && \ + echo "!host_build: QMAKE_LFLAGS += $($(package)_ldflags)" >> qtbase/mkspecs/common/gcc-base.conf && \ + sed -i.old "s|QMAKE_CFLAGS = |!host_build: QMAKE_CFLAGS = $($(package)_cflags) $($(package)_cppflags) |" qtbase/mkspecs/win32-g++/qmake.conf && \ + sed -i.old "s|QMAKE_LFLAGS = |!host_build: QMAKE_LFLAGS = $($(package)_ldflags) |" qtbase/mkspecs/win32-g++/qmake.conf && \ + sed -i.old "s|QMAKE_CXXFLAGS = |!host_build: QMAKE_CXXFLAGS = $($(package)_cxxflags) $($(package)_cppflags) |" qtbase/mkspecs/win32-g++/qmake.conf + +endef + +define $(package)_config_cmds + export PKG_CONFIG_SYSROOT_DIR=/ && \ + export PKG_CONFIG_LIBDIR=$(host_prefix)/lib/pkgconfig && \ + export PKG_CONFIG_PATH=$(host_prefix)/share/pkgconfig && \ + ./configure $($(package)_config_opts) && \ + echo "host_build: QT_CONFIG ~= s/system-zlib/zlib" >> mkspecs/qconfig.pri && \ + echo "CONFIG += force_bootstrap" >> mkspecs/qconfig.pri && \ + $(MAKE) sub-src-clean && \ + cd ../qttranslations && ../qtbase/bin/qmake qttranslations.pro -o Makefile && \ + cd translations && ../../qtbase/bin/qmake translations.pro -o Makefile && cd ../.. && \ + cd qttools/src/linguist/lrelease/ && ../../../../qtbase/bin/qmake lrelease.pro -o Makefile && cd ../../../.. && \ + cd qtactiveqt/src/activeqt && ../../../qtbase/bin/qmake activeqt.pro -o Makefile +endef + +define $(package)_build_cmds + $(MAKE) -C src $(addprefix sub-,$($(package)_qt_libs)) && \ + $(MAKE) -C ../qttools/src/linguist/lrelease && \ + $(MAKE) -C ../qtactiveqt/src/activeqt && \ + $(MAKE) -C ../qttranslations +endef + +# $(MAKE) -C qtactiveqt/src/activeqt/container INSTALL_ROOT=$($(package)_staging_dir) install_target && \ +# $(MAKE) -C qtactiveqt/src/activeqt/control INSTALL_ROOT=$($(package)_staging_dir) install_target && \ + + +define $(package)_stage_cmds + $(MAKE) -C src INSTALL_ROOT=$($(package)_staging_dir) $(addsuffix -install_subtargets,$(addprefix sub-,$($(package)_qt_libs))) && cd .. && \ + $(MAKE) -C qttools/src/linguist/lrelease INSTALL_ROOT=$($(package)_staging_dir) install_target && \ + $(MAKE) -C qtactiveqt/src/activeqt INSTALL_ROOT=$($(package)_staging_dir) install &&\ + $(MAKE) -C qttranslations INSTALL_ROOT=$($(package)_staging_dir) install_subtargets && \ + if `test -f qtbase/src/plugins/platforms/xcb/xcb-static/libxcb-static.a`; then \ + cp qtbase/src/plugins/platforms/xcb/xcb-static/libxcb-static.a $($(package)_staging_prefix_dir)/lib; \ + fi +endef + +define $(package)_postprocess_cmds + rm -rf native/mkspecs/ native/lib/ lib/cmake/ && \ + rm -f lib/lib*.la lib/*.prl plugins/*/*.prl +endef diff --git a/contrib/depends/packages/xcb_proto.mk b/contrib/depends/packages/xcb_proto.mk new file mode 100644 index 00000000000..0c7c958d62d --- /dev/null +++ b/contrib/depends/packages/xcb_proto.mk @@ -0,0 +1,27 @@ +package=xcb_proto +$(package)_version=1.10 +$(package)_download_path=http://xcb.freedesktop.org/dist +$(package)_file_name=xcb-proto-$($(package)_version).tar.bz2 +$(package)_sha256_hash=7ef40ddd855b750bc597d2a435da21e55e502a0fefa85b274f2c922800baaf05 + +define $(package)_set_vars + $(package)_config_opts=--disable-shared + $(package)_config_opts_linux=--with-pic +endef + +define $(package)_config_cmds + $($(package)_autoconf) +endef + +define $(package)_build_cmds + $(MAKE) +endef + +define $(package)_stage_cmds + $(MAKE) DESTDIR=$($(package)_staging_dir) install +endef + +define $(package)_postprocess_cmds + find -name "*.pyc" -delete && \ + find -name "*.pyo" -delete +endef diff --git a/contrib/depends/packages/xextproto.mk b/contrib/depends/packages/xextproto.mk new file mode 100644 index 00000000000..98a11eb4974 --- /dev/null +++ b/contrib/depends/packages/xextproto.mk @@ -0,0 +1,21 @@ +package=xextproto +$(package)_version=7.3.0 +$(package)_download_path=http://xorg.freedesktop.org/releases/individual/proto +$(package)_file_name=$(package)-$($(package)_version).tar.bz2 +$(package)_sha256_hash=f3f4b23ac8db9c3a9e0d8edb591713f3d70ef9c3b175970dd8823dfc92aa5bb0 + +define $(package)_set_vars +$(package)_config_opts=--disable-shared +endef + +define $(package)_config_cmds + $($(package)_autoconf) +endef + +define $(package)_build_cmds + $(MAKE) +endef + +define $(package)_stage_cmds + $(MAKE) DESTDIR=$($(package)_staging_dir) install +endef diff --git a/contrib/depends/packages/xproto.mk b/contrib/depends/packages/xproto.mk new file mode 100644 index 00000000000..50a90b26850 --- /dev/null +++ b/contrib/depends/packages/xproto.mk @@ -0,0 +1,21 @@ +package=xproto +$(package)_version=7.0.26 +$(package)_download_path=http://xorg.freedesktop.org/releases/individual/proto +$(package)_file_name=$(package)-$($(package)_version).tar.bz2 +$(package)_sha256_hash=636162c1759805a5a0114a369dffdeccb8af8c859ef6e1445f26a4e6e046514f + +define $(package)_set_vars +$(package)_config_opts=--disable-shared +endef + +define $(package)_config_cmds + $($(package)_autoconf) +endef + +define $(package)_build_cmds + $(MAKE) +endef + +define $(package)_stage_cmds + $(MAKE) DESTDIR=$($(package)_staging_dir) install +endef diff --git a/contrib/depends/packages/xtrans.mk b/contrib/depends/packages/xtrans.mk new file mode 100644 index 00000000000..99eefa6d5ea --- /dev/null +++ b/contrib/depends/packages/xtrans.mk @@ -0,0 +1,22 @@ +package=xtrans +$(package)_version=1.3.4 +$(package)_download_path=http://xorg.freedesktop.org/releases/individual/lib/ +$(package)_file_name=$(package)-$($(package)_version).tar.bz2 +$(package)_sha256_hash=054d4ee3efd52508c753e9f7bc655ef185a29bd2850dd9e2fc2ccc33544f583a +$(package)_dependencies= + +define $(package)_set_vars +$(package)_config_opts_linux=--with-pic --disable-static +endef + +define $(package)_config_cmds + $($(package)_autoconf) +endef + +define $(package)_build_cmds + $(MAKE) +endef + +define $(package)_stage_cmds + $(MAKE) DESTDIR=$($(package)_staging_dir) install +endef diff --git a/contrib/depends/packages/zlib.mk b/contrib/depends/packages/zlib.mk new file mode 100644 index 00000000000..7739c26a796 --- /dev/null +++ b/contrib/depends/packages/zlib.mk @@ -0,0 +1,28 @@ +package=zlib +$(package)_version=1.2.11 +$(package)_download_path=http://www.zlib.net +$(package)_file_name=$(package)-$($(package)_version).tar.gz +$(package)_sha256_hash=c3e5e9fdd5004dcb542feda5ee4f0ff0744628baf8ed2dd5d66f8ca1197cb1a1 + +define $(package)_set_vars +$(package)_build_opts= CC="$($(package)_cc)" +$(package)_build_opts+=CFLAGS="$($(package)_cflags) $($(package)_cppflags) -fPIC" +$(package)_build_opts+=RANLIB="$($(package)_ranlib)" +$(package)_build_opts+=AR="$($(package)_ar)" +$(package)_build_opts_darwin+=AR="$($(package)_libtool)" +$(package)_build_opts_darwin+=ARFLAGS="-o" +endef + +define $(package)_config_cmds + ./configure --static --prefix=$(host_prefix) +endef + +define $(package)_build_cmds + $(MAKE) $($(package)_build_opts) libz.a +endef + +define $(package)_stage_cmds + $(MAKE) DESTDIR=$($(package)_staging_dir) install $($(package)_build_opts) &&\ + $(MAKE) DESTDIR=$(host_prefix) install $($(package)_build_opts) +endef + diff --git a/contrib/depends/patches/native_biplist/sorted_list.patch b/contrib/depends/patches/native_biplist/sorted_list.patch new file mode 100644 index 00000000000..89abdb1b71f --- /dev/null +++ b/contrib/depends/patches/native_biplist/sorted_list.patch @@ -0,0 +1,29 @@ +--- a/biplist/__init__.py 2014-10-26 19:03:11.000000000 +0000 ++++ b/biplist/__init__.py 2016-07-19 19:30:17.663521999 +0000 +@@ -541,7 +541,7 @@ + return HashableWrapper(n) + elif isinstance(root, dict): + n = {} +- for key, value in iteritems(root): ++ for key, value in sorted(iteritems(root)): + n[self.wrapRoot(key)] = self.wrapRoot(value) + return HashableWrapper(n) + elif isinstance(root, list): +@@ -616,7 +616,7 @@ + elif isinstance(obj, dict): + size = proc_size(len(obj)) + self.incrementByteCount('dictBytes', incr=1+size) +- for key, value in iteritems(obj): ++ for key, value in sorted(iteritems(obj)): + check_key(key) + self.computeOffsets(key, asReference=True) + self.computeOffsets(value, asReference=True) +@@ -714,7 +714,7 @@ + keys = [] + values = [] + objectsToWrite = [] +- for key, value in iteritems(obj): ++ for key, value in sorted(iteritems(obj)): + keys.append(key) + values.append(value) + for key in keys: diff --git a/contrib/depends/patches/native_cdrkit/cdrkit-deterministic.patch b/contrib/depends/patches/native_cdrkit/cdrkit-deterministic.patch new file mode 100644 index 00000000000..8ab0993dc4d --- /dev/null +++ b/contrib/depends/patches/native_cdrkit/cdrkit-deterministic.patch @@ -0,0 +1,86 @@ +--- cdrkit-1.1.11.old/genisoimage/tree.c 2008-10-21 19:57:47.000000000 -0400 ++++ cdrkit-1.1.11/genisoimage/tree.c 2013-12-06 00:23:18.489622668 -0500 +@@ -1139,8 +1139,9 @@ + scan_directory_tree(struct directory *this_dir, char *path, + struct directory_entry *de) + { +- DIR *current_dir; ++ int current_file; + char whole_path[PATH_MAX]; ++ struct dirent **d_list; + struct dirent *d_entry; + struct directory *parent; + int dflag; +@@ -1164,7 +1165,8 @@ + this_dir->dir_flags |= DIR_WAS_SCANNED; + + errno = 0; /* Paranoia */ +- current_dir = opendir(path); ++ //current_dir = opendir(path); ++ current_file = scandir(path, &d_list, NULL, alphasort); + d_entry = NULL; + + /* +@@ -1173,12 +1175,12 @@ + */ + old_path = path; + +- if (current_dir) { ++ if (current_file >= 0) { + errno = 0; +- d_entry = readdir(current_dir); ++ d_entry = d_list[0]; + } + +- if (!current_dir || !d_entry) { ++ if (current_file < 0 || !d_entry) { + int ret = 1; + + #ifdef USE_LIBSCHILY +@@ -1191,8 +1193,8 @@ + de->isorec.flags[0] &= ~ISO_DIRECTORY; + ret = 0; + } +- if (current_dir) +- closedir(current_dir); ++ if(d_list) ++ free(d_list); + return (ret); + } + #ifdef ABORT_DEEP_ISO_ONLY +@@ -1208,7 +1210,7 @@ + errmsgno(EX_BAD, "use Rock Ridge extensions via -R or -r,\n"); + errmsgno(EX_BAD, "or allow deep ISO9660 directory nesting via -D.\n"); + } +- closedir(current_dir); ++ free(d_list); + return (1); + } + #endif +@@ -1250,13 +1252,13 @@ + * The first time through, skip this, since we already asked + * for the first entry when we opened the directory. + */ +- if (dflag) +- d_entry = readdir(current_dir); ++ if (dflag && current_file >= 0) ++ d_entry = d_list[current_file]; + dflag++; + +- if (!d_entry) ++ if (current_file < 0) + break; +- ++ current_file--; + /* OK, got a valid entry */ + + /* If we do not want all files, then pitch the backups. */ +@@ -1348,7 +1350,7 @@ + insert_file_entry(this_dir, whole_path, d_entry->d_name); + #endif /* APPLE_HYB */ + } +- closedir(current_dir); ++ free(d_list); + + #ifdef APPLE_HYB + /* diff --git a/contrib/depends/patches/native_mac_alias/python3.patch b/contrib/depends/patches/native_mac_alias/python3.patch new file mode 100644 index 00000000000..1a32340be5b --- /dev/null +++ b/contrib/depends/patches/native_mac_alias/python3.patch @@ -0,0 +1,72 @@ +diff -dur a/mac_alias/alias.py b/mac_alias/alias.py +--- a/mac_alias/alias.py 2015-10-19 12:12:48.000000000 +0200 ++++ b/mac_alias/alias.py 2016-04-03 12:13:12.037159417 +0200 +@@ -243,10 +243,10 @@ + alias = Alias() + alias.appinfo = appinfo + +- alias.volume = VolumeInfo (volname.replace('/',':'), ++ alias.volume = VolumeInfo (volname.decode().replace('/',':'), + voldate, fstype, disktype, + volattrs, volfsid) +- alias.target = TargetInfo (kind, filename.replace('/',':'), ++ alias.target = TargetInfo (kind, filename.decode().replace('/',':'), + folder_cnid, cnid, + crdate, creator_code, type_code) + alias.target.levels_from = levels_from +@@ -261,9 +261,9 @@ + b.read(1) + + if tag == TAG_CARBON_FOLDER_NAME: +- alias.target.folder_name = value.replace('/',':') ++ alias.target.folder_name = value.decode().replace('/',':') + elif tag == TAG_CNID_PATH: +- alias.target.cnid_path = struct.unpack(b'>%uI' % (length // 4), ++ alias.target.cnid_path = struct.unpack('>%uI' % (length // 4), + value) + elif tag == TAG_CARBON_PATH: + alias.target.carbon_path = value +@@ -298,9 +298,9 @@ + alias.target.creation_date \ + = mac_epoch + datetime.timedelta(seconds=seconds) + elif tag == TAG_POSIX_PATH: +- alias.target.posix_path = value ++ alias.target.posix_path = value.decode() + elif tag == TAG_POSIX_PATH_TO_MOUNTPOINT: +- alias.volume.posix_path = value ++ alias.volume.posix_path = value.decode() + elif tag == TAG_RECURSIVE_ALIAS_OF_DISK_IMAGE: + alias.volume.disk_image_alias = Alias.from_bytes(value) + elif tag == TAG_USER_HOME_LENGTH_PREFIX: +@@ -422,13 +422,13 @@ + # (so doing so is ridiculous, and nothing could rely on it). + b.write(struct.pack(b'>h28pI2shI64pII4s4shhI2s10s', + self.target.kind, +- carbon_volname, voldate, ++ carbon_volname, int(voldate), + self.volume.fs_type, + self.volume.disk_type, + self.target.folder_cnid, + carbon_filename, + self.target.cnid, +- crdate, ++ int(crdate), + self.target.creator_code, + self.target.type_code, + self.target.levels_from, +@@ -449,12 +449,12 @@ + + b.write(struct.pack(b'>hhQhhQ', + TAG_HIGH_RES_VOLUME_CREATION_DATE, +- 8, long(voldate * 65536), ++ 8, int(voldate * 65536), + TAG_HIGH_RES_CREATION_DATE, +- 8, long(crdate * 65536))) ++ 8, int(crdate * 65536))) + + if self.target.cnid_path: +- cnid_path = struct.pack(b'>%uI' % len(self.target.cnid_path), ++ cnid_path = struct.pack('>%uI' % len(self.target.cnid_path), + *self.target.cnid_path) + b.write(struct.pack(b'>hh', TAG_CNID_PATH, + len(cnid_path))) diff --git a/contrib/depends/patches/qt/fix-xcb-include-order.patch b/contrib/depends/patches/qt/fix-xcb-include-order.patch new file mode 100644 index 00000000000..ec2bc17d9bd --- /dev/null +++ b/contrib/depends/patches/qt/fix-xcb-include-order.patch @@ -0,0 +1,49 @@ +--- old/qtbase/src/plugins/platforms/xcb/xcb_qpa_lib.pro 2015-03-17 ++++ new/qtbase/src/plugins/platforms/xcb/xcb_qpa_lib.pro 2015-03-17 +@@ -76,8 +76,6 @@ + + DEFINES += $$QMAKE_DEFINES_XCB + LIBS += $$QMAKE_LIBS_XCB +-QMAKE_CXXFLAGS += $$QMAKE_CFLAGS_XCB +-QMAKE_CFLAGS += $$QMAKE_CFLAGS_XCB + + CONFIG += qpa/genericunixfontdatabase + +@@ -89,7 +87,8 @@ + contains(QT_CONFIG, xcb-qt) { + DEFINES += XCB_USE_RENDER + XCB_DIR = ../../../3rdparty/xcb +- INCLUDEPATH += $$XCB_DIR/include $$XCB_DIR/sysinclude ++ QMAKE_CFLAGS += -I$$XCB_DIR/include -I$$XCB_DIR/sysinclude $$QMAKE_CFLAGS_XCB ++ QMAKE_CXXFLAGS += -I$$XCB_DIR/include -I$$XCB_DIR/sysinclude $$QMAKE_CFLAGS_XCB + LIBS += -lxcb -L$$MODULE_BASE_OUTDIR/lib -lxcb-static$$qtPlatformTargetSuffix() + } else { + LIBS += -lxcb -lxcb-image -lxcb-icccm -lxcb-sync -lxcb-xfixes -lxcb-shm -lxcb-randr -lxcb-shape -lxcb-keysyms -lxcb-xinerama +--- old/qtbase/src/plugins/platforms/xcb/xcb-static/xcb-static.pro ++++ new/qtbase/src/plugins/platforms/xcb/xcb-static/xcb-static.pro +@@ -9,7 +9,8 @@ + + XCB_DIR = ../../../../3rdparty/xcb + +-INCLUDEPATH += $$XCB_DIR/include $$XCB_DIR/include/xcb $$XCB_DIR/sysinclude ++QMAKE_CFLAGS += -I$$XCB_DIR/include -I$$XCB_DIR/include/xcb -I$$XCB_DIR/sysinclude ++QMAKE_CXXFLAGS += -I$$XCB_DIR/include -I$$XCB_DIR/include/xcb -I$$XCB_DIR/sysinclude + + QMAKE_CXXFLAGS += $$QMAKE_CFLAGS_XCB + QMAKE_CFLAGS += $$QMAKE_CFLAGS_XCB +--- old/qtbase/src/plugins/platforms/xcb/xcb-plugin.pro ++++ new/qtbase/src/plugins/platforms/xcb/xcb-plugin.pro +@@ -6,6 +6,13 @@ + qxcbmain.cpp + OTHER_FILES += xcb.json README + ++contains(QT_CONFIG, xcb-qt) { ++ DEFINES += XCB_USE_RENDER ++ XCB_DIR = ../../../3rdparty/xcb ++ QMAKE_CFLAGS += -I$$XCB_DIR/include -I$$XCB_DIR/sysinclude $$QMAKE_CFLAGS_XCB ++ QMAKE_CXXFLAGS += -I$$XCB_DIR/include -I$$XCB_DIR/sysinclude $$QMAKE_CFLAGS_XCB ++} ++ + PLUGIN_TYPE = platforms + PLUGIN_CLASS_NAME = QXcbIntegrationPlugin + !equals(TARGET, $$QT_DEFAULT_QPA_PLUGIN): PLUGIN_EXTENDS = - diff --git a/contrib/depends/patches/qt/fix_qt_pkgconfig.patch b/contrib/depends/patches/qt/fix_qt_pkgconfig.patch new file mode 100644 index 00000000000..34302a9f2d2 --- /dev/null +++ b/contrib/depends/patches/qt/fix_qt_pkgconfig.patch @@ -0,0 +1,11 @@ +--- old/qtbase/mkspecs/features/qt_module.prf ++++ new/qtbase/mkspecs/features/qt_module.prf +@@ -245,7 +245,7 @@ + load(qt_targets) + + # this builds on top of qt_common +-!internal_module:!lib_bundle:if(unix|mingw) { ++unix|mingw { + CONFIG += create_pc + QMAKE_PKGCONFIG_DESTDIR = pkgconfig + host_build: \ diff --git a/contrib/depends/patches/qt/mac-qmake.conf b/contrib/depends/patches/qt/mac-qmake.conf new file mode 100644 index 00000000000..ca70d30b15e --- /dev/null +++ b/contrib/depends/patches/qt/mac-qmake.conf @@ -0,0 +1,25 @@ +MAKEFILE_GENERATOR = UNIX +CONFIG += app_bundle incremental global_init_link_order lib_version_first plugin_no_soname absolute_library_soname +QMAKE_INCREMENTAL_STYLE = sublib +include(../common/macx.conf) +include(../common/gcc-base-mac.conf) +include(../common/clang.conf) +include(../common/clang-mac.conf) +QMAKE_MAC_SDK_PATH=$${MAC_SDK_PATH} +QMAKE_XCODE_VERSION=4.3 +QMAKE_XCODE_DEVELOPER_PATH=/Developer +QMAKE_MACOSX_DEPLOYMENT_TARGET = $${MAC_MIN_VERSION} +QMAKE_MAC_SDK=macosx +QMAKE_MAC_SDK.macosx.Path = $${MAC_SDK_PATH} +QMAKE_MAC_SDK.macosx.platform_name = macosx +QMAKE_MAC_SDK.macosx.SDKVersion = $${MAC_SDK_VERSION} +QMAKE_MAC_SDK.macosx.PlatformPath = /phony +!host_build: QMAKE_CFLAGS += -target $${MAC_TARGET} +!host_build: QMAKE_OBJECTIVE_CFLAGS += $$QMAKE_CFLAGS +!host_build: QMAKE_CXXFLAGS += $$QMAKE_CFLAGS +!host_build: QMAKE_LFLAGS += -target $${MAC_TARGET} -mlinker-version=$${MAC_LD64_VERSION} +QMAKE_AR = $${CROSS_COMPILE}ar cq +QMAKE_RANLIB=$${CROSS_COMPILE}ranlib +QMAKE_LIBTOOL=$${CROSS_COMPILE}libtool +QMAKE_INSTALL_NAME_TOOL=$${CROSS_COMPILE}install_name_tool +load(qt_config) diff --git a/contrib/depends/patches/qt/mingw-uuidof.patch b/contrib/depends/patches/qt/mingw-uuidof.patch new file mode 100644 index 00000000000..fb21923c8c3 --- /dev/null +++ b/contrib/depends/patches/qt/mingw-uuidof.patch @@ -0,0 +1,44 @@ +--- old/qtbase/src/plugins/platforms/windows/qwindowscontext.cpp ++++ new/qtbase/src/plugins/platforms/windows/qwindowscontext.cpp +@@ -77,7 +77,7 @@ + #include + #include + #include +-#ifndef Q_OS_WINCE ++#if !defined(Q_OS_WINCE) && (!defined(USE___UUIDOF) || (defined(USE___UUIDOF) && USE___UUIDOF == 1)) + # include + #endif + +@@ -814,7 +814,7 @@ + HWND_MESSAGE, NULL, static_cast(GetModuleHandle(0)), NULL); + } + +-#ifndef Q_OS_WINCE ++#if !defined(Q_OS_WINCE) && (!defined(USE___UUIDOF) || (defined(USE___UUIDOF) && USE___UUIDOF == 1)) + // Re-engineered from the inline function _com_error::ErrorMessage(). + // We cannot use it directly since it uses swprintf_s(), which is not + // present in the MSVCRT.DLL found on Windows XP (QTBUG-35617). +@@ -833,7 +833,7 @@ + return QString::asprintf("IDispatch error #%u", uint(wCode)); + return QString::asprintf("Unknown error 0x0%x", uint(comError.Error())); + } +-#endif // !Q_OS_WINCE ++#endif // !defined(Q_OS_WINCE) && (!defined(USE___UUIDOF) || (defined(USE___UUIDOF) && USE___UUIDOF == 1)) + + /*! + \brief Common COM error strings. +@@ -901,12 +901,12 @@ + default: + break; + } +-#ifndef Q_OS_WINCE ++#if !defined(Q_OS_WINCE) && (!defined(USE___UUIDOF) || (defined(USE___UUIDOF) && USE___UUIDOF == 1)) + _com_error error(hr); + result += QByteArrayLiteral(" ("); + result += errorMessageFromComError(error); + result += ')'; +-#endif // !Q_OS_WINCE ++#endif // !defined(Q_OS_WINCE) && (!defined(USE___UUIDOF) || (defined(USE___UUIDOF) && USE___UUIDOF == 1)) + return result; + } + diff --git a/contrib/depends/patches/qt/pidlist_absolute.patch b/contrib/depends/patches/qt/pidlist_absolute.patch new file mode 100644 index 00000000000..c7928241790 --- /dev/null +++ b/contrib/depends/patches/qt/pidlist_absolute.patch @@ -0,0 +1,37 @@ +diff -dur old/qtbase/src/plugins/platforms/windows/qwindowscontext.h new/qtbase/src/plugins/platforms/windows/qwindowscontext.h +--- old/qtbase/src/plugins/platforms/windows/qwindowscontext.h ++++ new/qtbase/src/plugins/platforms/windows/qwindowscontext.h +@@ -136,10 +136,18 @@ + inline void init(); + + typedef HRESULT (WINAPI *SHCreateItemFromParsingName)(PCWSTR, IBindCtx *, const GUID&, void **); ++#if defined(Q_CC_MINGW) && (!defined(__MINGW64_VERSION_MAJOR) || __MINGW64_VERSION_MAJOR < 3) ++ typedef HRESULT (WINAPI *SHGetKnownFolderIDList)(const GUID &, DWORD, HANDLE, ITEMIDLIST **); ++#else + typedef HRESULT (WINAPI *SHGetKnownFolderIDList)(const GUID &, DWORD, HANDLE, PIDLIST_ABSOLUTE *); ++#endif + typedef HRESULT (WINAPI *SHGetStockIconInfo)(int , int , _SHSTOCKICONINFO *); + typedef HRESULT (WINAPI *SHGetImageList)(int, REFIID , void **); ++#if defined(Q_CC_MINGW) && (!defined(__MINGW64_VERSION_MAJOR) || __MINGW64_VERSION_MAJOR < 3) ++ typedef HRESULT (WINAPI *SHCreateItemFromIDList)(const ITEMIDLIST *, REFIID, void **); ++#else + typedef HRESULT (WINAPI *SHCreateItemFromIDList)(PCIDLIST_ABSOLUTE, REFIID, void **); ++#endif + + SHCreateItemFromParsingName sHCreateItemFromParsingName; + SHGetKnownFolderIDList sHGetKnownFolderIDList; +diff -dur old/qtbase/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp new/qtbase/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp +--- old/qtbase/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp ++++ new/qtbase/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp +@@ -1016,7 +1016,11 @@ + qWarning() << __FUNCTION__ << ": Invalid CLSID: " << url.path(); + return Q_NULLPTR; + } ++#if defined(Q_CC_MINGW) && (!defined(__MINGW64_VERSION_MAJOR) || __MINGW64_VERSION_MAJOR < 3) ++ ITEMIDLIST *idList; ++#else + PIDLIST_ABSOLUTE idList; ++#endif + HRESULT hr = QWindowsContext::shell32dll.sHGetKnownFolderIDList(uuid, 0, 0, &idList); + if (FAILED(hr)) { + qErrnoWarning("%s: SHGetKnownFolderIDList(%s)) failed", __FUNCTION__, qPrintable(url.toString())); From 8db3d5731bc999983ca6c776731a26a4e97d8bc8 Mon Sep 17 00:00:00 2001 From: TheCharlatan Date: Tue, 20 Mar 2018 01:26:35 +0100 Subject: [PATCH 0326/1404] Modify depends for monero's dependencies Add readline, ldns, graphviz, unbound to depends packages Add a cmake toolchain file to depends that is uniquely created for every build and placed in triple/share/toolchain.cmake This file is then passed to cmake with -DCMAKE_TOOLCHAIN_FILE=/path/to/triple/share/toolchain.cmake Add the boost locale package to depends In the depends cmake toolchain file, a DEPENDS flag is added to exclude, or change cmake checks done that are required for depends Link miniupnpc and unwind from depends and not external Add libiconv and icu4c to depends, required for mingw32 builds. Headers (winsock) need to be lower case in order to compile on unix systems. This should not affect building on windows. --- contrib/depends/Makefile | 48 ++++- contrib/depends/config.guess | 12 +- contrib/depends/config.site.in | 11 +- contrib/depends/config.sub | 14 +- contrib/depends/packages/boost.mk | 10 +- .../packages/cmake/conf/mxe-conf.cmake.in | 67 ++++++ .../packages/cmake/conf/target-cmake.in | 29 +++ .../packages/cmake/test/CMakeLists.txt | 18 ++ contrib/depends/packages/cppzmq.mk | 27 +++ contrib/depends/packages/curl.mk | 27 --- contrib/depends/packages/dbus.mk | 23 -- contrib/depends/packages/expat.mk | 9 +- contrib/depends/packages/fontconfig.mk | 22 -- contrib/depends/packages/freetype.mk | 22 -- contrib/depends/packages/graphviz.mk | 30 +++ contrib/depends/packages/icu4c.mk | 32 +++ contrib/depends/packages/ldns.mk | 28 +++ contrib/depends/packages/libX11.mk | 23 -- contrib/depends/packages/libXau.mk | 23 -- contrib/depends/packages/libXext.mk | 22 -- contrib/depends/packages/libevent.mk | 30 +++ contrib/depends/packages/libiconv.mk | 17 ++ contrib/depends/packages/libxcb.mk | 35 ---- contrib/depends/packages/native_ccache.mk | 4 +- contrib/depends/packages/packages.mk | 20 +- contrib/depends/packages/pcsc-lite.mk | 26 +++ contrib/depends/packages/protobuf.mk | 29 +++ contrib/depends/packages/qrencode.mk | 22 -- contrib/depends/packages/qt.mk | 198 ------------------ contrib/depends/packages/readline.mk | 33 +++ contrib/depends/packages/unbound.mk | 28 +++ contrib/depends/packages/unwind.mk | 20 ++ contrib/depends/packages/xcb_proto.mk | 27 --- contrib/depends/packages/xextproto.mk | 21 -- contrib/depends/packages/xtrans.mk | 22 -- contrib/depends/packages/zeromq.mk | 34 +++ contrib/depends/packages/zlib.mk | 3 +- .../depends/patches/cmake/cmake-1-fixes.patch | 67 ++++++ ...-001-dont-build-static-dynamic-twice.patch | 37 ++++ .../patches/qt/fix-xcb-include-order.patch | 49 ----- .../depends/patches/qt/fix_qt_pkgconfig.patch | 11 - contrib/depends/patches/qt/mac-qmake.conf | 25 --- contrib/depends/patches/qt/mingw-uuidof.patch | 44 ---- .../depends/patches/qt/pidlist_absolute.patch | 37 ---- .../depends/patches/readline/readline-1.patch | 187 +++++++++++++++++ ...d3957725acd34aa8b8d011585812f3369411.patch | 22 ++ ...45c12e0b100cd38acecc16ce7db02905e27c.patch | 22 ++ contrib/depends/protobuf.mk | 29 +++ contrib/depends/toolchain.cmake.in | 45 ++++ contrib/epee/include/misc_os_dependent.h | 2 +- external/CMakeLists.txt | 46 ++-- 51 files changed, 984 insertions(+), 705 deletions(-) create mode 100644 contrib/depends/packages/cmake/conf/mxe-conf.cmake.in create mode 100644 contrib/depends/packages/cmake/conf/target-cmake.in create mode 100644 contrib/depends/packages/cmake/test/CMakeLists.txt create mode 100644 contrib/depends/packages/cppzmq.mk delete mode 100644 contrib/depends/packages/curl.mk delete mode 100644 contrib/depends/packages/dbus.mk delete mode 100644 contrib/depends/packages/fontconfig.mk delete mode 100644 contrib/depends/packages/freetype.mk create mode 100644 contrib/depends/packages/graphviz.mk create mode 100644 contrib/depends/packages/icu4c.mk create mode 100644 contrib/depends/packages/ldns.mk delete mode 100644 contrib/depends/packages/libX11.mk delete mode 100644 contrib/depends/packages/libXau.mk delete mode 100644 contrib/depends/packages/libXext.mk create mode 100644 contrib/depends/packages/libevent.mk create mode 100644 contrib/depends/packages/libiconv.mk delete mode 100644 contrib/depends/packages/libxcb.mk create mode 100644 contrib/depends/packages/pcsc-lite.mk create mode 100644 contrib/depends/packages/protobuf.mk delete mode 100644 contrib/depends/packages/qrencode.mk delete mode 100644 contrib/depends/packages/qt.mk create mode 100644 contrib/depends/packages/readline.mk create mode 100644 contrib/depends/packages/unbound.mk create mode 100644 contrib/depends/packages/unwind.mk delete mode 100644 contrib/depends/packages/xcb_proto.mk delete mode 100644 contrib/depends/packages/xextproto.mk delete mode 100644 contrib/depends/packages/xtrans.mk create mode 100644 contrib/depends/packages/zeromq.mk create mode 100644 contrib/depends/patches/cmake/cmake-1-fixes.patch create mode 100644 contrib/depends/patches/icu4c/icu-001-dont-build-static-dynamic-twice.patch delete mode 100644 contrib/depends/patches/qt/fix-xcb-include-order.patch delete mode 100644 contrib/depends/patches/qt/fix_qt_pkgconfig.patch delete mode 100644 contrib/depends/patches/qt/mac-qmake.conf delete mode 100644 contrib/depends/patches/qt/mingw-uuidof.patch delete mode 100644 contrib/depends/patches/qt/pidlist_absolute.patch create mode 100644 contrib/depends/patches/readline/readline-1.patch create mode 100644 contrib/depends/patches/zeromq/9114d3957725acd34aa8b8d011585812f3369411.patch create mode 100644 contrib/depends/patches/zeromq/9e6745c12e0b100cd38acecc16ce7db02905e27c.patch create mode 100644 contrib/depends/protobuf.mk create mode 100644 contrib/depends/toolchain.cmake.in diff --git a/contrib/depends/Makefile b/contrib/depends/Makefile index dedb0674cf1..be81626b120 100644 --- a/contrib/depends/Makefile +++ b/contrib/depends/Makefile @@ -61,7 +61,15 @@ $(host_arch)_$(host_os)_prefix=$(BASEDIR)/$(host) $(host_arch)_$(host_os)_host=$(host) host_prefix=$($(host_arch)_$(host_os)_prefix) build_prefix=$(host_prefix)/native -build_host=$(build) +ifeq ($(host_os),mingw32) +host_cmake=Windows +endif +ifeq ($(host_os),linux) +host_cmake=Linux +endif +ifeq ($(host_os),darwin11) +host_cmake=Darwin +endif AT_$(V):= AT_:=@ @@ -108,8 +116,18 @@ $(host_arch)_$(host_os)_native_toolchain?=$($(host_os)_native_toolchain) include funcs.mk +CONF_PKGS := cmake-conf mxe-conf + +build-only-$(1)_$(3): CMAKE_RUNRESULT_FILE = $(PREFIX)/share/cmake/modules/TryRunResults.cmake +build-only-$(1)_$(3): CMAKE_TOOLCHAIN_FILE = $(PREFIX)/$(3)/share/cmake/mxe-conf.cmake +build-only-$(1)_$(3): CMAKE_TOOLCHAIN_DIR = $(PREFIX)/$(3)/share/cmake/mxe-conf.d +build-only-$(1)_$(3): CMAKE_STATIC_BOOL = $(if $(findstring shared,$(3)),OFF,ON) +build-only-$(1)_$(3): CMAKE_SHARED_BOOL = $(if $(findstring shared,$(3)),ON,OFF) + + toolchain_path=$($($(host_arch)_$(host_os)_native_toolchain)_prefixbin) final_build_id_long+=$(shell $(build_SHA256SUM) config.site.in) +final_build_id_long+=$(shell $(build_SHA256SUM) toolchain.cmake.in) final_build_id+=$(shell echo -n "$(final_build_id_long)" | $(build_SHA256SUM) | cut -c-$(HASH_LENGTH)) $(host_prefix)/.stamp_$(final_build_id): $(native_packages) $(packages) $(AT)rm -rf $(@D) @@ -134,6 +152,7 @@ $(host_prefix)/share/config.site : config.site.in $(host_prefix)/.stamp_$(final_ -e 's|@CXXFLAGS@|$(strip $(host_CXXFLAGS) $(host_$(release_type)_CXXFLAGS))|' \ -e 's|@CPPFLAGS@|$(strip $(host_CPPFLAGS) $(host_$(release_type)_CPPFLAGS))|' \ -e 's|@LDFLAGS@|$(strip $(host_LDFLAGS) $(host_$(release_type)_LDFLAGS))|' \ + -e 's|@allow_host_packages@|$(ALLOW_HOST_PACKAGES)|' \ -e 's|@no_qt@|$(NO_QT)|' \ -e 's|@no_wallet@|$(NO_WALLET)|' \ -e 's|@no_upnp@|$(NO_UPNP)|' \ @@ -141,6 +160,30 @@ $(host_prefix)/share/config.site : config.site.in $(host_prefix)/.stamp_$(final_ $< > $@ $(AT)touch $@ +$(host_prefix)/share/toolchain.cmake : toolchain.cmake.in $(host_prefix)/.stamp_$(final_build_id) + $(AT)@mkdir -p $(@D) + $(AT)sed -e 's|@HOST@|$(host)|' \ + -e 's|@CC@|$(toolchain_path)$(host_CC)|' \ + -e 's|@CXX@|$(toolchain_path)$(host_CXX)|' \ + -e 's|@AR@|$(toolchain_path)$(host_AR)|' \ + -e 's|@RANLIB@|$(toolchain_path)$(host_RANLIB)|' \ + -e 's|@NM@|$(toolchain_path)$(host_NM)|' \ + -e 's|@STRIP@|$(toolchain_path)$(host_STRIP)|' \ + -e 's|@build_os@|$(build_os)|' \ + -e 's|@host_os@|$(host_os)|' \ + -e 's|@CFLAGS@|$(strip $(host_CFLAGS) $(host_$(release_type)_CFLAGS))|' \ + -e 's|@CXXFLAGS@|$(strip $(host_CXXFLAGS) $(host_$(release_type)_CXXFLAGS))|' \ + -e 's|@CPPFLAGS@|$(strip $(host_CPPFLAGS) $(host_$(release_type)_CPPFLAGS))|' \ + -e 's|@LDFLAGS@|$(strip $(host_LDFLAGS) $(host_$(release_type)_LDFLAGS))|' \ + -e 's|@allow_host_packages@|$(ALLOW_HOST_PACKAGES)|' \ + -e 's|@no_qt@|$(NO_QT)|' \ + -e 's|@no_wallet@|$(NO_WALLET)|' \ + -e 's|@no_upnp@|$(NO_UPNP)|' \ + -e 's|@debug@|$(DEBUG)|' \ + -e 's|@depends@|$(host_cmake)|' \ + -e 's|@prefix@|$($(host_arch)_$(host_os)_prefix)|'\ + $< > $@ + $(AT)touch $@ define check_or_remove_cached mkdir -p $(BASE_CACHE)/$(host)/$(package) && cd $(BASE_CACHE)/$(host)/$(package); \ @@ -162,11 +205,12 @@ check-sources: @$(foreach package,$(all_packages),$(call check_or_remove_sources,$(package));) $(host_prefix)/share/config.site: check-packages +$(host_prefix)/share/toolchain.cmake: check-packages check-packages: check-sources install: check-packages $(host_prefix)/share/config.site - +install: check-packages $(host_prefix)/share/toolchain.cmake download-one: check-sources $(all_sources) diff --git a/contrib/depends/config.guess b/contrib/depends/config.guess index bbd48b60e88..69ed3e573bb 100755 --- a/contrib/depends/config.guess +++ b/contrib/depends/config.guess @@ -2,7 +2,7 @@ # Attempt to guess a canonical system name. # Copyright 1992-2017 Free Software Foundation, Inc. -timestamp='2017-01-01' +timestamp='2017-03-05' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by @@ -837,10 +837,11 @@ EOF UNAME_PROCESSOR=`/usr/bin/uname -p` case ${UNAME_PROCESSOR} in amd64) - echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; - *) - echo ${UNAME_PROCESSOR}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; + UNAME_PROCESSOR=x86_64 ;; + i386) + UNAME_PROCESSOR=i586 ;; esac + echo ${UNAME_PROCESSOR}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` exit ;; i*:CYGWIN*:*) echo ${UNAME_MACHINE}-pc-cygwin @@ -1343,6 +1344,9 @@ EOF NSR-?:NONSTOP_KERNEL:*:*) echo nsr-tandem-nsk${UNAME_RELEASE} exit ;; + NSX-?:NONSTOP_KERNEL:*:*) + echo nsx-tandem-nsk${UNAME_RELEASE} + exit ;; *:NonStop-UX:*:*) echo mips-compaq-nonstopux exit ;; diff --git a/contrib/depends/config.site.in b/contrib/depends/config.site.in index 3d7c9fd43c7..0a4a9c327e2 100644 --- a/contrib/depends/config.site.in +++ b/contrib/depends/config.site.in @@ -13,10 +13,10 @@ fi if test -z $with_qt_translationdir; then with_qt_translationdir=$depends_prefix/translations fi -if test -z $with_qt_bindir; then +if test -z $with_qt_bindir && test -z "@no_qt@"; then with_qt_bindir=$depends_prefix/native/bin fi -if test -z $with_protoc_bindir; then +if test -z $with_protoc_bindir && test -z "@no_qt@"; then with_protoc_bindir=$depends_prefix/native/bin fi @@ -53,9 +53,10 @@ PKG_CONFIG="`which pkg-config` --static" # These two need to remain exported because pkg-config does not see them # otherwise. That means they must be unexported at the end of configure.ac to # avoid ruining the cache. Sigh. - -export PKG_CONFIG_LIBDIR=$depends_prefix/lib/pkgconfig -export PKG_CONFIG_PATH=$depends_prefix/share/pkgconfig +export PKG_CONFIG_PATH=$depends_prefix/share/pkgconfig:$depends_prefix/lib/pkgconfig +if test -z "@allow_host_packages@"; then + export PKGCONFIG_LIBDIR= +fi CPPFLAGS="-I$depends_prefix/include/ $CPPFLAGS" LDFLAGS="-L$depends_prefix/lib $LDFLAGS" diff --git a/contrib/depends/config.sub b/contrib/depends/config.sub index 7e792b4ae17..40ea5dfe115 100755 --- a/contrib/depends/config.sub +++ b/contrib/depends/config.sub @@ -2,7 +2,7 @@ # Configuration validation subroutine script. # Copyright 1992-2017 Free Software Foundation, Inc. -timestamp='2017-01-01' +timestamp='2017-04-02' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by @@ -263,7 +263,7 @@ case $basic_machine in | fido | fr30 | frv | ft32 \ | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ | hexagon \ - | i370 | i860 | i960 | ia64 \ + | i370 | i860 | i960 | ia16 | ia64 \ | ip2k | iq2000 \ | k1om \ | le32 | le64 \ @@ -315,6 +315,7 @@ case $basic_machine in | ubicom32 \ | v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \ | visium \ + | wasm32 \ | we32k \ | x86 | xc16x | xstormy16 | xtensa \ | z8k | z80) @@ -388,7 +389,7 @@ case $basic_machine in | h8300-* | h8500-* \ | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ | hexagon-* \ - | i*86-* | i860-* | i960-* | ia64-* \ + | i*86-* | i860-* | i960-* | ia16-* | ia64-* \ | ip2k-* | iq2000-* \ | k1om-* \ | le32-* | le64-* \ @@ -446,6 +447,7 @@ case $basic_machine in | v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \ | vax-* \ | visium-* \ + | wasm32-* \ | we32k-* \ | x86-* | x86_64-* | xc16x-* | xps100-* \ | xstormy16-* | xtensa*-* \ @@ -948,6 +950,9 @@ case $basic_machine in nsr-tandem) basic_machine=nsr-tandem ;; + nsx-tandem) + basic_machine=nsx-tandem + ;; op50n-* | op60c-*) basic_machine=hppa1.1-oki os=-proelf @@ -1243,6 +1248,9 @@ case $basic_machine in basic_machine=a29k-wrs os=-vxworks ;; + wasm32) + basic_machine=wasm32-unknown + ;; w65*) basic_machine=w65-wdc os=-none diff --git a/contrib/depends/packages/boost.mk b/contrib/depends/packages/boost.mk index 57d96e48212..9d3dda74ac4 100644 --- a/contrib/depends/packages/boost.mk +++ b/contrib/depends/packages/boost.mk @@ -1,8 +1,8 @@ package=boost -$(package)_version=1_63_0 -$(package)_download_path=https://sourceforge.net/projects/boost/files/boost/1.63.0 -$(package)_file_name=$(package)_$($(package)_version).tar.bz2 -$(package)_sha256_hash=beae2529f759f6b3bf3f4969a19c2e9d6f0c503edcb2de4a61d1428519fcb3b0 +$(package)_version=1_58_0 +$(package)_download_path=https://sourceforge.net/projects/boost/files/boost/1.58.0 +$(package)_file_name=$(package)_$($(package)_version).tar.gz +$(package)_sha256_hash=a004d9b3fa95e956383693b86fce1b68805a6f71c2e68944fa813de0fb8c8102 define $(package)_set_vars $(package)_config_opts_release=variant=release @@ -19,7 +19,7 @@ $(package)_toolset_$(host_os)=gcc $(package)_archiver_$(host_os)=$($(package)_ar) $(package)_toolset_darwin=darwin $(package)_archiver_darwin=$($(package)_libtool) -$(package)_config_libraries=chrono,filesystem,program_options,system,thread,test +$(package)_config_libraries=system,filesystem,thread,date_time,chrono,regex,serialization,program_options,locale $(package)_cxxflags=-std=c++11 -fvisibility=hidden $(package)_cxxflags_linux=-fPIC endef diff --git a/contrib/depends/packages/cmake/conf/mxe-conf.cmake.in b/contrib/depends/packages/cmake/conf/mxe-conf.cmake.in new file mode 100644 index 00000000000..8bd4cf1f719 --- /dev/null +++ b/contrib/depends/packages/cmake/conf/mxe-conf.cmake.in @@ -0,0 +1,67 @@ +# This file is part of MXE. See LICENSE.md for licensing information. + +# https://cmake.org/cmake/help/latest + +# Can't set `cmake_minimum_required` or `cmake_policy` in toolchain +# since toolchain is read before CMakeLists.txt +# See `target-cmake.in` for CMAKE_POLICY_DEFAULT_CMPNNNN + +# Check if we are using mxe supplied version +# - toolchain is included multiple times so set a guard in +# environment to suppress duplicate messages +if(NOT ${CMAKE_COMMAND} STREQUAL @PREFIX@/@BUILD@/bin/cmake AND NOT DEFINED ENV{_MXE_CMAKE_TOOLCHAIN_INCLUDED}) + message(WARNING " +** Warning: direct use of toolchain file is deprecated +** Please use prefixed wrapper script instead: + @TARGET@-cmake [options] + - uses mxe supplied cmake version @CMAKE_VERSION@ + - loads toolchain + - loads common run results + - sets various policy defaults + ") + set(ENV{_MXE_CMAKE_TOOLCHAIN_INCLUDED} TRUE) +endif() + +## General configuration +set(CMAKE_SYSTEM_NAME Windows) +set(MSYS 1) +set(CMAKE_EXPORT_NO_PACKAGE_REGISTRY ON) +# Workaround for https://www.cmake.org/Bug/view.php?id=14075 +set(CMAKE_CROSS_COMPILING ON) + + +## Library config +set(BUILD_SHARED_LIBS @CMAKE_SHARED_BOOL@ CACHE BOOL "BUILD_SHARED_LIBS" FORCE) +set(BUILD_STATIC_LIBS @CMAKE_STATIC_BOOL@ CACHE BOOL "BUILD_STATIC_LIBS" FORCE) +set(BUILD_SHARED @CMAKE_SHARED_BOOL@ CACHE BOOL "BUILD_SHARED" FORCE) +set(BUILD_STATIC @CMAKE_STATIC_BOOL@ CACHE BOOL "BUILD_STATIC" FORCE) +set(LIBTYPE @LIBTYPE@) + + +## Paths etc. +set(CMAKE_FIND_ROOT_PATH @PREFIX@/@TARGET@) +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_PREFIX_PATH @PREFIX@/@TARGET@) +set(CMAKE_INSTALL_PREFIX @PREFIX@/@TARGET@ CACHE PATH "Installation Prefix") +# For custom mxe FindPackage scripts +set(CMAKE_MODULE_PATH "@PREFIX@/share/cmake/modules" ${CMAKE_MODULE_PATH}) + + +## Programs +set(CMAKE_C_COMPILER @PREFIX@/bin/@TARGET@-gcc) +set(CMAKE_CXX_COMPILER @PREFIX@/bin/@TARGET@-g++) +set(CMAKE_Fortran_COMPILER @PREFIX@/bin/@TARGET@-gfortran) +set(CMAKE_RC_COMPILER @PREFIX@/bin/@TARGET@-windres) +# CMAKE_RC_COMPILE_OBJECT is defined in: +# /share/cmake-X.Y/Modules/Platform/Windows-windres.cmake +set(CPACK_NSIS_EXECUTABLE @TARGET@-makensis) + +## Individual package configuration +file(GLOB mxe_cmake_files + "@CMAKE_TOOLCHAIN_DIR@/*.cmake" +) +foreach(mxe_cmake_file ${mxe_cmake_files}) + include(${mxe_cmake_file}) +endforeach() diff --git a/contrib/depends/packages/cmake/conf/target-cmake.in b/contrib/depends/packages/cmake/conf/target-cmake.in new file mode 100644 index 00000000000..a78bcf58d8a --- /dev/null +++ b/contrib/depends/packages/cmake/conf/target-cmake.in @@ -0,0 +1,29 @@ +#!/usr/bin/env bash +echo "== Using MXE wrapper: @PREFIX@/bin/@TARGET@-cmake" + +# https://cmake.org/cmake/help/latest/manual/cmake-policies.7.html +# https://cmake.org/cmake/help/latest/variable/CMAKE_POLICY_DEFAULT_CMPNNNN.html +POLICIES=(0017,0020) + +unset NO_MXE_TOOLCHAIN +if echo -- "$@" | grep -Ewq "(--build|-E|--system-information)" ; then + NO_MXE_TOOLCHAIN=1 +fi +if [[ "$NO_MXE_TOOLCHAIN" == "1" ]]; then + echo "== Skip using MXE toolchain: @CMAKE_TOOLCHAIN_FILE@" + # see https://github.com/mxe/mxe/issues/932 + exec "@PREFIX@/@BUILD@/bin/cmake" "$@" +else + echo " - cmake version @CMAKE_VERSION@" + echo " - warnings for unused CMAKE_POLICY_DEFAULT variables can be ignored" + echo "== Using MXE toolchain: @CMAKE_TOOLCHAIN_FILE@" + echo "== Using MXE runresult: @CMAKE_RUNRESULT_FILE@" + if ! ( echo "$@" | grep --silent "DCMAKE_BUILD_TYPE" ) ; then + echo '== Adding "-DCMAKE_BUILD_TYPE=Release"' + set -- "-DCMAKE_BUILD_TYPE=Release" "$@" + fi + exec "@PREFIX@/@BUILD@/bin/cmake" \ + -DCMAKE_TOOLCHAIN_FILE="@CMAKE_TOOLCHAIN_FILE@" \ + `eval echo -DCMAKE_POLICY_DEFAULT_CMP{$POLICIES}=NEW` \ + -C"@CMAKE_RUNRESULT_FILE@" "$@" +fi diff --git a/contrib/depends/packages/cmake/test/CMakeLists.txt b/contrib/depends/packages/cmake/test/CMakeLists.txt new file mode 100644 index 00000000000..6075a8f5a7d --- /dev/null +++ b/contrib/depends/packages/cmake/test/CMakeLists.txt @@ -0,0 +1,18 @@ +# This file is part of MXE. See LICENSE.md for licensing information. + +# 2.8.9 is Debian Wheezy version +cmake_minimum_required(VERSION 2.8.9) + +# use default C and CXX languages +project(mxe) + +# see cmake --help-policy for details +cmake_policy(SET CMP0017 NEW) +if (POLICY CMP0020) + cmake_policy(SET CMP0020 NEW) +endif() + +# so we can find pkg-test.cmake files to include +set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/../.. ${CMAKE_MODULE_PATH}) + +include(${PKG}-test) diff --git a/contrib/depends/packages/cppzmq.mk b/contrib/depends/packages/cppzmq.mk new file mode 100644 index 00000000000..9ff5a5ab042 --- /dev/null +++ b/contrib/depends/packages/cppzmq.mk @@ -0,0 +1,27 @@ +package=cppzmq +$(package)_version=4.2.3 +$(package)_download_path=https://github.com/zeromq/cppzmq/archive/ +$(package)_file_name=v$($(package)_version).tar.gz +$(package)_sha256_hash=3e6b57bf49115f4ae893b1ff7848ead7267013087dc7be1ab27636a97144d373 +$(package)_dependencies=zeromq + +define $(package)_config_cmds + echo $(build_pefix) &&\ + cmake -DCMAKE_INSTALL_PREFIX=$(build_prefix)/../ -DCMAKE_PREFIX_PATH=$(build_prefix)/../include -DZeroMQ_STATIC_LIBRARY=$(build_prefix)/lib -DPC_LIBZMQ_LIBDIR=$(build_prefix)/lib -DPC_LIBZMQ_LIBRARY_DIRS=$(build_prefix)/lib CMakeLists.txt +endef + +define $(package)_build_cmds + $(MAKE) +endef + +define $(package)_stage_cmds + $(MAKE) DESTDIR=$($(package)_staging_dir) &&\ + echo $(host_prefix)/include/ &&\ + echo $($(package)_staging_prefix_dir) &&\ + mkdir $($(package)_staging_prefix_dir)/include &&\ + cp zmq.hpp $($(package)_staging_prefix_dir)/include +endef + +define $(package)_postprocess_cmds + rm -rf bin share +endef diff --git a/contrib/depends/packages/curl.mk b/contrib/depends/packages/curl.mk deleted file mode 100644 index cb1390d26e8..00000000000 --- a/contrib/depends/packages/curl.mk +++ /dev/null @@ -1,27 +0,0 @@ -package=curl -$(package)_version=7.55.0 -$(package)_download_path=https://curl.haxx.se/download/ -$(package)_file_name=$(package)-$($(package)_version).tar.gz -$(package)_sha256_hash=dae1b1be34f5983e8d46917f2bdbb2335aecd0e57f777f4c32213da6a8050a80 - -define $(package)_set_vars - $(package)_config_opts=--disable-shared - $(package)_config_opts+= --enable-static - $(package)_config_opts_release+=--disable-debug-mode - $(package)_config_opts_linux+=--with-pic -endef - -define $(package)_config_cmds - $($(package)_autoconf) -endef - -define $(package)_build_cmds - $(MAKE) -endef - -define $(package)_stage_cmds - $(MAKE) DESTDIR=$($(package)_staging_dir) install -endef - -define $(package)_postprocess_cmds -endef diff --git a/contrib/depends/packages/dbus.mk b/contrib/depends/packages/dbus.mk deleted file mode 100644 index 90ddcb923f2..00000000000 --- a/contrib/depends/packages/dbus.mk +++ /dev/null @@ -1,23 +0,0 @@ -package=dbus -$(package)_version=1.10.14 -$(package)_download_path=http://dbus.freedesktop.org/releases/dbus -$(package)_file_name=$(package)-$($(package)_version).tar.gz -$(package)_sha256_hash=23238f70353e38ce5ca183ebc9525c0d97ac00ef640ad29cf794782af6e6a083 -$(package)_dependencies=expat - -define $(package)_set_vars - $(package)_config_opts=--disable-tests --disable-doxygen-docs --disable-xml-docs --disable-static --without-x -endef - -define $(package)_config_cmds - $($(package)_autoconf) -endef - -define $(package)_build_cmds - $(MAKE) -C dbus libdbus-1.la -endef - -define $(package)_stage_cmds - $(MAKE) -C dbus DESTDIR=$($(package)_staging_dir) install-libLTLIBRARIES install-dbusincludeHEADERS install-nodist_dbusarchincludeHEADERS && \ - $(MAKE) DESTDIR=$($(package)_staging_dir) install-pkgconfigDATA -endef diff --git a/contrib/depends/packages/expat.mk b/contrib/depends/packages/expat.mk index 7f484724a49..bd2cea1b6b7 100644 --- a/contrib/depends/packages/expat.mk +++ b/contrib/depends/packages/expat.mk @@ -1,15 +1,16 @@ package=expat -$(package)_version=2.2.1 +$(package)_version=2.2.4 $(package)_download_path=https://downloads.sourceforge.net/project/expat/expat/$($(package)_version) $(package)_file_name=$(package)-$($(package)_version).tar.bz2 -$(package)_sha256_hash=1868cadae4c82a018e361e2b2091de103cd820aaacb0d6cfa49bd2cd83978885 +$(package)_sha256_hash=03ad85db965f8ab2d27328abcf0bc5571af6ec0a414874b2066ee3fdd372019e define $(package)_set_vars -$(package)_config_opts=--disable-static +$(package)_config_opts=--enable-static +$(package)_config_opts+=--prefix=$(host_prefix) endef define $(package)_config_cmds - $($(package)_autoconf) + $($(package)_autoconf) $($(package)_config_opts) endef define $(package)_build_cmds diff --git a/contrib/depends/packages/fontconfig.mk b/contrib/depends/packages/fontconfig.mk deleted file mode 100644 index fb97e0b9ec1..00000000000 --- a/contrib/depends/packages/fontconfig.mk +++ /dev/null @@ -1,22 +0,0 @@ -package=fontconfig -$(package)_version=2.12.1 -$(package)_download_path=http://www.freedesktop.org/software/fontconfig/release/ -$(package)_file_name=$(package)-$($(package)_version).tar.bz2 -$(package)_sha256_hash=b449a3e10c47e1d1c7a6ec6e2016cca73d3bd68fbbd4f0ae5cc6b573f7d6c7f3 -$(package)_dependencies=freetype expat - -define $(package)_set_vars - $(package)_config_opts=--disable-docs --disable-static -endef - -define $(package)_config_cmds - $($(package)_autoconf) -endef - -define $(package)_build_cmds - $(MAKE) -endef - -define $(package)_stage_cmds - $(MAKE) DESTDIR=$($(package)_staging_dir) install -endef diff --git a/contrib/depends/packages/freetype.mk b/contrib/depends/packages/freetype.mk deleted file mode 100644 index 76b025c4631..00000000000 --- a/contrib/depends/packages/freetype.mk +++ /dev/null @@ -1,22 +0,0 @@ -package=freetype -$(package)_version=2.7.1 -$(package)_download_path=http://download.savannah.gnu.org/releases/$(package) -$(package)_file_name=$(package)-$($(package)_version).tar.bz2 -$(package)_sha256_hash=3a3bb2c4e15ffb433f2032f50a5b5a92558206822e22bfe8cbe339af4aa82f88 - -define $(package)_set_vars - $(package)_config_opts=--without-zlib --without-png --disable-static - $(package)_config_opts_linux=--with-pic -endef - -define $(package)_config_cmds - $($(package)_autoconf) -endef - -define $(package)_build_cmds - $(MAKE) -endef - -define $(package)_stage_cmds - $(MAKE) DESTDIR=$($(package)_staging_dir) install -endef diff --git a/contrib/depends/packages/graphviz.mk b/contrib/depends/packages/graphviz.mk new file mode 100644 index 00000000000..1c4bc1b7160 --- /dev/null +++ b/contrib/depends/packages/graphviz.mk @@ -0,0 +1,30 @@ +package=graphviz +$(package)_version=2.40.1 +$(package)_download_path=www.graphviz.org/pub/graphviz/stable/SOURCES/ +$(package)_file_name=$(package)-$($(package)_version).tar.gz +$(package)_sha256_hash=ca5218fade0204d59947126c38439f432853543b0818d9d728c589dfe7f3a421 + +define $(package)_preprocess_cmds + ./autogen.sh +endef + +define $(package)_set_vars + $(package)_config_opts=--disable-shared --enable-multibye --without-purify --without-curses + $(package)_config_opts_release=--disable-debug-mode + $(package)_config_opts_linux=--with-pic +endef + +define $(package)_config_cmds + $($(package)_autoconf) +endef + +define $(package)_build_cmds + $(MAKE) +endef + +define $(package)_stage_cmds + $(MAKE) DESTDIR=$($(package)_staging_dir) install +endef + +define $(package)_postprocess_cmds +endef diff --git a/contrib/depends/packages/icu4c.mk b/contrib/depends/packages/icu4c.mk new file mode 100644 index 00000000000..eedf22da04d --- /dev/null +++ b/contrib/depends/packages/icu4c.mk @@ -0,0 +1,32 @@ +package=icu4c +$(package)_version=55.1 +$(package)_download_path=https://github.com/TheCharlatan/icu4c/archive +$(package)_file_name=55.1.tar.gz +$(package)_sha256_hash=1f912c54035533fb4268809701d65c7468d00e292efbc31e6444908450cc46ef +$(package)_patches=icu-001-dont-build-static-dynamic-twice.patch + +define $(package)_set_vars + $(package)_build_opts=CFLAGS="$($(package)_cflags) $($(package)_cppflags) -DU_USING_ICU_NAMESPACE=0 --std=gnu++0x -DU_STATIC_IMPLEMENTATION -DU_COMBINED_IMPLEMENTATION -fPIC" +endef + +define $(package)_config_cmds + patch -p1 < $($(package)_patch_dir)/icu-001-dont-build-static-dynamic-twice.patch &&\ + mkdir builda &&\ + mkdir buildb &&\ + cd builda &&\ + sh ../source/runConfigureICU Linux &&\ + make &&\ + cd ../buildb &&\ + sh ../source/$($(package)_autoconf) --enable-static=yes --enable-shared=yes --prefix=$(host_prefix) --with-cross-build=`pwd`/../builda &&\ + $(MAKE) $($(package)_build_opts) +endef + +#define $(package)_build_cmds +# cd source &&\ + $(MAKE) $($((package)_build_opts) `nproc` +#endef + +define $(package)_stage_cmds + cd buildb &&\ + $(MAKE) $($(package)_build_opts) DESTDIR=$($(package)_staging_dir) install lib/* +endef diff --git a/contrib/depends/packages/ldns.mk b/contrib/depends/packages/ldns.mk new file mode 100644 index 00000000000..a9565a58129 --- /dev/null +++ b/contrib/depends/packages/ldns.mk @@ -0,0 +1,28 @@ +package=ldns +$(package)_version=1.6.17 +$(package)_download_path=http://www.nlnetlabs.nl/downloads/ldns/ +$(package)_file_name=$(package)-$($(package)_version).tar.gz +$(package)_sha256_hash=8b88e059452118e8949a2752a55ce59bc71fa5bc414103e17f5b6b06f9bcc8cd +$(package)_dependencies=openssl + +define $(package)_set_vars + $(package)_config_opts=--disable-shared --enable-static --disable-dane-ta-usage --with-drill + $(package)_config_opts=--with-ssl=$(host_prefix) + $(package)_config_opts_release=--disable-debug-mode + $(package)_config_opts_linux=--with-pic +endef + +define $(package)_config_cmds + $($(package)_autoconf) +endef + +define $(package)_build_cmds + $(MAKE) +endef + +define $(package)_stage_cmds + $(MAKE) DESTDIR=$($(package)_staging_dir) install-h install-lib +endef + +define $(package)_postprocess_cmds +endef diff --git a/contrib/depends/packages/libX11.mk b/contrib/depends/packages/libX11.mk deleted file mode 100644 index 178d592ee63..00000000000 --- a/contrib/depends/packages/libX11.mk +++ /dev/null @@ -1,23 +0,0 @@ -package=libX11 -$(package)_version=1.6.2 -$(package)_download_path=http://xorg.freedesktop.org/releases/individual/lib/ -$(package)_file_name=$(package)-$($(package)_version).tar.bz2 -$(package)_sha256_hash=2aa027e837231d2eeea90f3a4afe19948a6eb4c8b2bec0241eba7dbc8106bd16 -$(package)_dependencies=libxcb xtrans xextproto xproto - -define $(package)_set_vars -$(package)_config_opts=--disable-xkb --disable-static -$(package)_config_opts_linux=--with-pic -endef - -define $(package)_config_cmds - $($(package)_autoconf) -endef - -define $(package)_build_cmds - $(MAKE) -endef - -define $(package)_stage_cmds - $(MAKE) DESTDIR=$($(package)_staging_dir) install -endef diff --git a/contrib/depends/packages/libXau.mk b/contrib/depends/packages/libXau.mk deleted file mode 100644 index e87df2e4de6..00000000000 --- a/contrib/depends/packages/libXau.mk +++ /dev/null @@ -1,23 +0,0 @@ -package=libXau -$(package)_version=1.0.8 -$(package)_download_path=http://xorg.freedesktop.org/releases/individual/lib/ -$(package)_file_name=$(package)-$($(package)_version).tar.bz2 -$(package)_sha256_hash=fdd477320aeb5cdd67272838722d6b7d544887dfe7de46e1e7cc0c27c2bea4f2 -$(package)_dependencies=xproto - -define $(package)_set_vars - $(package)_config_opts=--disable-shared - $(package)_config_opts_linux=--with-pic -endef - -define $(package)_config_cmds - $($(package)_autoconf) -endef - -define $(package)_build_cmds - $(MAKE) -endef - -define $(package)_stage_cmds - $(MAKE) DESTDIR=$($(package)_staging_dir) install -endef diff --git a/contrib/depends/packages/libXext.mk b/contrib/depends/packages/libXext.mk deleted file mode 100644 index 4db836066f9..00000000000 --- a/contrib/depends/packages/libXext.mk +++ /dev/null @@ -1,22 +0,0 @@ -package=libXext -$(package)_version=1.3.2 -$(package)_download_path=http://xorg.freedesktop.org/releases/individual/lib/ -$(package)_file_name=$(package)-$($(package)_version).tar.bz2 -$(package)_sha256_hash=f829075bc646cdc085fa25d98d5885d83b1759ceb355933127c257e8e50432e0 -$(package)_dependencies=xproto xextproto libX11 libXau - -define $(package)_set_vars - $(package)_config_opts=--disable-static -endef - -define $(package)_config_cmds - $($(package)_autoconf) -endef - -define $(package)_build_cmds - $(MAKE) -endef - -define $(package)_stage_cmds - $(MAKE) DESTDIR=$($(package)_staging_dir) install -endef diff --git a/contrib/depends/packages/libevent.mk b/contrib/depends/packages/libevent.mk new file mode 100644 index 00000000000..5f622f8e6e8 --- /dev/null +++ b/contrib/depends/packages/libevent.mk @@ -0,0 +1,30 @@ +package=libevent +$(package)_version=2.1.8-stable +$(package)_download_path=https://github.com/libevent/libevent/archive/ +$(package)_file_name=release-$($(package)_version).tar.gz +$(package)_sha256_hash=316ddb401745ac5d222d7c529ef1eada12f58f6376a66c1118eee803cb70f83d + +define $(package)_preprocess_cmds + ./autogen.sh +endef + +define $(package)_set_vars + $(package)_config_opts=--disable-shared --disable-openssl --disable-libevent-regress --disable-samples + $(package)_config_opts_release=--disable-debug-mode + $(package)_config_opts_linux=--with-pic +endef + +define $(package)_config_cmds + $($(package)_autoconf) +endef + +define $(package)_build_cmds + $(MAKE) +endef + +define $(package)_stage_cmds + $(MAKE) DESTDIR=$($(package)_staging_dir) install +endef + +define $(package)_postprocess_cmds +endef diff --git a/contrib/depends/packages/libiconv.mk b/contrib/depends/packages/libiconv.mk new file mode 100644 index 00000000000..87e30b208ad --- /dev/null +++ b/contrib/depends/packages/libiconv.mk @@ -0,0 +1,17 @@ +package=libiconv +$(package)_version=1.15 +$(package)_download_path=https://ftp.gnu.org/gnu/libiconv +$(package)_file_name=libiconv-$($(package)_version).tar.gz +$(package)_sha256_hash=ccf536620a45458d26ba83887a983b96827001e92a13847b45e4925cc8913178 + +define $(package)_config_cmds + $($(package)_autoconf) --disable-nls --enable-static --disable-shared +endef + +define $(package)_build_cmds + $(MAKE) +endef + +define $(package)_stage_cmds + $(MAKE) DESTDIR=$($(package)_staging_dir) install +endef diff --git a/contrib/depends/packages/libxcb.mk b/contrib/depends/packages/libxcb.mk deleted file mode 100644 index 28f2bd6f253..00000000000 --- a/contrib/depends/packages/libxcb.mk +++ /dev/null @@ -1,35 +0,0 @@ -package=libxcb -$(package)_version=1.10 -$(package)_download_path=http://xcb.freedesktop.org/dist -$(package)_file_name=$(package)-$($(package)_version).tar.bz2 -$(package)_sha256_hash=98d9ab05b636dd088603b64229dd1ab2d2cc02ab807892e107d674f9c3f2d5b5 -$(package)_dependencies=xcb_proto libXau xproto - -define $(package)_set_vars -$(package)_config_opts=--disable-static -endef - -define $(package)_preprocess_cmds - sed "s/pthread-stubs//" -i configure -endef - -# Don't install xcb headers to the default path in order to work around a qt -# build issue: https://bugreports.qt.io/browse/QTBUG-34748 -# When using qt's internal libxcb, it may end up finding the real headers in -# depends staging. Use a non-default path to avoid that. - -define $(package)_config_cmds - $($(package)_autoconf) --includedir=$(host_prefix)/include/xcb-shared -endef - -define $(package)_build_cmds - $(MAKE) -endef - -define $(package)_stage_cmds - $(MAKE) DESTDIR=$($(package)_staging_dir) install -endef - -define $(package)_postprocess_cmds - rm -rf share/man share/doc -endef diff --git a/contrib/depends/packages/native_ccache.mk b/contrib/depends/packages/native_ccache.mk index 4ed61a49e9c..966804ce8bb 100644 --- a/contrib/depends/packages/native_ccache.mk +++ b/contrib/depends/packages/native_ccache.mk @@ -1,8 +1,8 @@ package=native_ccache -$(package)_version=3.3.3 +$(package)_version=3.3.4 $(package)_download_path=https://samba.org/ftp/ccache $(package)_file_name=ccache-$($(package)_version).tar.bz2 -$(package)_sha256_hash=2985bc5e32ebe38d2958d508eb54ddcad39eed909489c0c2988035214597ca54 +$(package)_sha256_hash=fa9d7f38367431bc86b19ad107d709ca7ecf1574fdacca01698bdf0a47cd8567 define $(package)_set_vars $(package)_config_opts= diff --git a/contrib/depends/packages/packages.mk b/contrib/depends/packages/packages.mk index c6aa02b38bb..1aca0609665 100644 --- a/contrib/depends/packages/packages.mk +++ b/contrib/depends/packages/packages.mk @@ -1,20 +1,22 @@ -packages:=boost openssl curl +packages:=boost openssl libevent zeromq cppzmq zlib expat ldns unbound cppzmq readline libiconv icu4c native_packages := native_ccache -qt_packages = qrencode zlib - -qt_x86_64_linux_packages:=qt expat dbus libxcb xcb_proto libXau xproto freetype fontconfig libX11 xextproto libXext xtrans -qt_i686_linux_packages:=$(qt_x86_64_linux_packages) - -qt_darwin_packages=qt -qt_mingw32_packages=qt - wallet_packages=bdb upnp_packages=miniupnpc darwin_native_packages = native_biplist native_ds_store native_mac_alias +ifeq ($(host_os),linux) +packages += unwind +endif +ifeq ($(host_os),darwin11) +package += unwind +endif + ifneq ($(build_os),darwin) darwin_native_packages += native_cctools native_cdrkit native_libdmg-hfsplus +packages += readline endif + + diff --git a/contrib/depends/packages/pcsc-lite.mk b/contrib/depends/packages/pcsc-lite.mk new file mode 100644 index 00000000000..a24442eb99c --- /dev/null +++ b/contrib/depends/packages/pcsc-lite.mk @@ -0,0 +1,26 @@ +package=pcsc-lite +$(package)_version=1.8.23 +$(package)_download_path=https://alioth.debian.org/frs/download.php/file/4235 +$(package)_file_name=$(package)-$($(package)_version).tar.bz2 +$(package)_sha256_hash=5a27262586eff39cfd5c19aadc8891dd71c0818d3d629539bd631b958be689c9 + +define $(package)_set_vars + $(package)_build_opts=CC="$($(package)_cc)" + $(package)_config_env=AR="$($(package)_ar)" RANLIB="$($(package)_ranlib)" CC="$($(package)_cc)" + $(package)_config_opts=--prefix=$(host_prefix) + $(package)_config_opts_release=--disable-debug-mode --disable-libsystemd --enable-static + $(package)_build_opts=CFLAGS="$($(package)_cflags) $($(package)_cppflags) -fPIC" +endef + +define $(package)_config_cmds + ./bootstrap &&\ + $($(package)_autoconf) $($(package)_config_opts) +endef + +define $(package)_build_cmds + $(MAKE) $($(package)_build_opts) +endef + +define $(package)_stage_cmds + $(MAKE) DESTDIR=$($(package)_staging_dir) install +endef diff --git a/contrib/depends/packages/protobuf.mk b/contrib/depends/packages/protobuf.mk new file mode 100644 index 00000000000..54d3fd92451 --- /dev/null +++ b/contrib/depends/packages/protobuf.mk @@ -0,0 +1,29 @@ +package=protobuf +$(package)_version=$(native_$(package)_version) +$(package)_download_path=$(native_$(package)_download_path) +$(package)_file_name=$(native_$(package)_file_name) +$(package)_sha256_hash=$(native_$(package)_sha256_hash) +$(package)_dependencies=native_$(package) +$(package)_cxxflags=-std=c++11 + +define $(package)_set_vars + $(package)_config_opts=--disable-shared --with-protoc=$(build_prefix)/bin/protoc + $(package)_config_opts_linux=--with-pic +endef + +define $(package)_config_cmds + $($(package)_autoconf) +endef + +define $(package)_build_cmds + $(MAKE) -C src libprotobuf.la +endef + +define $(package)_stage_cmds + $(MAKE) DESTDIR=$($(package)_staging_dir) -C src install-libLTLIBRARIES install-nobase_includeHEADERS &&\ + $(MAKE) DESTDIR=$($(package)_staging_dir) install-pkgconfigDATA +endef + +define $(package)_postprocess_cmds + rm lib/libprotoc.a +endef diff --git a/contrib/depends/packages/qrencode.mk b/contrib/depends/packages/qrencode.mk deleted file mode 100644 index 7b21247133f..00000000000 --- a/contrib/depends/packages/qrencode.mk +++ /dev/null @@ -1,22 +0,0 @@ -package=qrencode -$(package)_version=3.4.4 -$(package)_download_path=https://fukuchi.org/works/qrencode/ -$(package)_file_name=qrencode-$(qrencode_version).tar.bz2 -$(package)_sha256_hash=efe5188b1ddbcbf98763b819b146be6a90481aac30cfc8d858ab78a19cde1fa5 - -define $(package)_set_vars -$(package)_config_opts=--disable-shared -without-tools --disable-sdltest -$(package)_config_opts_linux=--with-pic -endef - -define $(package)_config_cmds - $($(package)_autoconf) -endef - -define $(package)_build_cmds - $(MAKE) -endef - -define $(package)_stage_cmds - $(MAKE) DESTDIR=$($(package)_staging_dir) install -endef diff --git a/contrib/depends/packages/qt.mk b/contrib/depends/packages/qt.mk deleted file mode 100644 index f45355c492a..00000000000 --- a/contrib/depends/packages/qt.mk +++ /dev/null @@ -1,198 +0,0 @@ -PACKAGE=qt -$(package)_version=5.7.1 -$(package)_download_path=http://download.qt.io/official_releases/qt/5.7/$($(package)_version)/submodules -$(package)_suffix=opensource-src-$($(package)_version).tar.gz -$(package)_file_name=qtbase-$($(package)_suffix) -$(package)_sha256_hash=95f83e532d23b3ddbde7973f380ecae1bac13230340557276f75f2e37984e410 -$(package)_dependencies=openssl zlib -$(package)_linux_dependencies=freetype fontconfig libxcb libX11 xproto libXext -$(package)_build_subdir=qtbase -$(package)_qt_libs=corelib network widgets gui plugins testlib concurrent -$(package)_patches=mac-qmake.conf mingw-uuidof.patch pidlist_absolute.patch fix-xcb-include-order.patch fix_qt_pkgconfig.patch - -$(package)_qttranslations_file_name=qttranslations-$($(package)_suffix) -$(package)_qttranslations_sha256_hash=3a15aebd523c6d89fb97b2d3df866c94149653a26d27a00aac9b6d3020bc5a1d - -$(package)_qttools_file_name=qttools-$($(package)_suffix) -$(package)_qttools_sha256_hash=22d67de915cb8cd93e16fdd38fa006224ad9170bd217c2be1e53045a8dd02f0f - -$(package)_qtactiveqt_file_name=qtactiveqt-$($(package)_suffix) -$(package)_qtactiveqt_sha256_hash=57b39e9fe1d8d430da14d38d8c0de39bede1cd3ce1540f3d51c1fa0a2ef149cf -$(package)_qtactiveqt_libs=axcontainer - -$(package)_extra_sources = $($(package)_qttranslations_file_name) -$(package)_extra_sources += $($(package)_qttools_file_name) -$(package)_extra_sources += $($(package)_qtactiveqt_file_name) - -define $(package)_set_vars -$(package)_config_opts_release = -release -$(package)_config_opts_debug = -debug -$(package)_config_opts += -bindir $(build_prefix)/bin -$(package)_config_opts += -c++std c++11 -$(package)_config_opts += -confirm-license -$(package)_config_opts += -dbus-runtime -$(package)_config_opts += -hostprefix $(build_prefix) -$(package)_config_opts += -no-alsa -$(package)_config_opts += -no-audio-backend -$(package)_config_opts += -no-cups -$(package)_config_opts += -no-egl -$(package)_config_opts += -no-eglfs -$(package)_config_opts += -no-feature-style-windowsmobile -$(package)_config_opts += -no-feature-style-windowsce -$(package)_config_opts += -no-freetype -$(package)_config_opts += -no-gif -$(package)_config_opts += -no-glib -$(package)_config_opts += -no-gstreamer -$(package)_config_opts += -no-icu -$(package)_config_opts += -no-iconv -$(package)_config_opts += -no-kms -$(package)_config_opts += -no-linuxfb -$(package)_config_opts += -no-libudev -$(package)_config_opts += -no-mitshm -$(package)_config_opts += -no-mtdev -$(package)_config_opts += -no-pulseaudio -$(package)_config_opts += -no-openvg -$(package)_config_opts += -no-reduce-relocations -$(package)_config_opts += -no-qml-debug -$(package)_config_opts += -no-sql-db2 -$(package)_config_opts += -no-sql-ibase -$(package)_config_opts += -no-sql-oci -$(package)_config_opts += -no-sql-tds -$(package)_config_opts += -no-sql-mysql -$(package)_config_opts += -no-sql-odbc -$(package)_config_opts += -no-sql-psql -$(package)_config_opts += -no-sql-sqlite -$(package)_config_opts += -no-sql-sqlite2 -$(package)_config_opts += -no-use-gold-linker -$(package)_config_opts += -no-xinput2 -$(package)_config_opts += -no-xrender -$(package)_config_opts += -nomake examples -$(package)_config_opts += -nomake tests -$(package)_config_opts += -opensource -$(package)_config_opts += -openssl-linked -$(package)_config_opts += -optimized-qmake -$(package)_config_opts += -pch -$(package)_config_opts += -pkg-config -$(package)_config_opts += -prefix $(host_prefix) -$(package)_config_opts += -qt-libpng -$(package)_config_opts += -qt-libjpeg -$(package)_config_opts += -qt-pcre -$(package)_config_opts += -system-zlib -$(package)_config_opts += -reduce-exports -$(package)_config_opts += -static -$(package)_config_opts += -silent -$(package)_config_opts += -v -$(package)_config_opts += -no-feature-printer -$(package)_config_opts += -no-feature-printdialog - -ifneq ($(build_os),darwin) -$(package)_config_opts_darwin = -xplatform macx-clang-linux -$(package)_config_opts_darwin += -device-option MAC_SDK_PATH=$(OSX_SDK) -$(package)_config_opts_darwin += -device-option MAC_SDK_VERSION=$(OSX_SDK_VERSION) -$(package)_config_opts_darwin += -device-option CROSS_COMPILE="$(host)-" -$(package)_config_opts_darwin += -device-option MAC_MIN_VERSION=$(OSX_MIN_VERSION) -$(package)_config_opts_darwin += -device-option MAC_TARGET=$(host) -$(package)_config_opts_darwin += -device-option MAC_LD64_VERSION=$(LD64_VERSION) -endif - -$(package)_config_opts_linux = -qt-xkbcommon -$(package)_config_opts_linux += -qt-xcb -$(package)_config_opts_linux += -system-freetype -$(package)_config_opts_linux += -no-sm -$(package)_config_opts_linux += -fontconfig -$(package)_config_opts_linux += -no-opengl -$(package)_config_opts_arm_linux = -platform linux-g++ -xplatform $(host) -$(package)_config_opts_i686_linux = -xplatform linux-g++-32 -$(package)_config_opts_mingw32 = -no-opengl -xplatform win32-g++ -device-option CROSS_COMPILE="$(host)-" -$(package)_build_env = QT_RCC_TEST=1 -endef - -define $(package)_fetch_cmds -$(call fetch_file,$(package),$($(package)_download_path),$($(package)_download_file),$($(package)_file_name),$($(package)_sha256_hash)) && \ -$(call fetch_file,$(package),$($(package)_download_path),$($(package)_qttranslations_file_name),$($(package)_qttranslations_file_name),$($(package)_qttranslations_sha256_hash)) && \ -$(call fetch_file,$(package),$($(package)_download_path),$($(package)_qttools_file_name),$($(package)_qttools_file_name),$($(package)_qttools_sha256_hash)) && \ -$(call fetch_file,$(package),$($(package)_download_path),$($(package)_qtactiveqt_file_name),$($(package)_qtactiveqt_file_name),$($(package)_qtactiveqt_sha256_hash)) -endef - -define $(package)_extract_cmds - mkdir -p $($(package)_extract_dir) && \ - echo "$($(package)_sha256_hash) $($(package)_source)" > $($(package)_extract_dir)/.$($(package)_file_name).hash && \ - echo "$($(package)_qttranslations_sha256_hash) $($(package)_source_dir)/$($(package)_qttranslations_file_name)" >> $($(package)_extract_dir)/.$($(package)_file_name).hash && \ - echo "$($(package)_qttools_sha256_hash) $($(package)_source_dir)/$($(package)_qttools_file_name)" >> $($(package)_extract_dir)/.$($(package)_file_name).hash && \ - echo "$($(package)_qtactiveqt_sha256_hash) $($(package)_source_dir)/$($(package)_qtactiveqt_file_name)" >> $($(package)_extract_dir)/.$($(package)_file_name).hash && \ - $(build_SHA256SUM) -c $($(package)_extract_dir)/.$($(package)_file_name).hash && \ - mkdir qtbase && \ - tar --strip-components=1 -xf $($(package)_source) -C qtbase && \ - mkdir qttranslations && \ - tar --strip-components=1 -xf $($(package)_source_dir)/$($(package)_qttranslations_file_name) -C qttranslations && \ - mkdir qttools && \ - tar --strip-components=1 -xf $($(package)_source_dir)/$($(package)_qttools_file_name) -C qttools &&\ - mkdir qtactiveqt && \ - tar --strip-components=1 -xf $($(package)_source_dir)/$($(package)_qtactiveqt_file_name) -C qtactiveqt -endef - - -define $(package)_preprocess_cmds - sed -i.old "s|updateqm.commands = \$$$$\$$$$LRELEASE|updateqm.commands = $($(package)_extract_dir)/qttools/bin/lrelease|" qttranslations/translations/translations.pro && \ - sed -i.old "/updateqm.depends =/d" qttranslations/translations/translations.pro && \ - sed -i.old "s/src_plugins.depends = src_sql src_xml src_network/src_plugins.depends = src_xml src_network/" qtbase/src/src.pro && \ - sed -i.old "s|X11/extensions/XIproto.h|X11/X.h|" qtbase/src/plugins/platforms/xcb/qxcbxsettings.cpp && \ - sed -i.old 's/if \[ "$$$$XPLATFORM_MAC" = "yes" \]; then xspecvals=$$$$(macSDKify/if \[ "$$$$BUILD_ON_MAC" = "yes" \]; then xspecvals=$$$$(macSDKify/' qtbase/configure && \ - sed -i.old 's/CGEventCreateMouseEvent(0, kCGEventMouseMoved, pos, 0)/CGEventCreateMouseEvent(0, kCGEventMouseMoved, pos, kCGMouseButtonLeft)/' qtbase/src/plugins/platforms/cocoa/qcocoacursor.mm && \ - mkdir -p qtbase/mkspecs/macx-clang-linux &&\ - cp -f qtbase/mkspecs/macx-clang/Info.plist.lib qtbase/mkspecs/macx-clang-linux/ &&\ - cp -f qtbase/mkspecs/macx-clang/Info.plist.app qtbase/mkspecs/macx-clang-linux/ &&\ - cp -f qtbase/mkspecs/macx-clang/qplatformdefs.h qtbase/mkspecs/macx-clang-linux/ &&\ - cp -f $($(package)_patch_dir)/mac-qmake.conf qtbase/mkspecs/macx-clang-linux/qmake.conf && \ - patch -p1 < $($(package)_patch_dir)/mingw-uuidof.patch && \ - patch -p1 < $($(package)_patch_dir)/pidlist_absolute.patch && \ - patch -p1 < $($(package)_patch_dir)/fix-xcb-include-order.patch && \ - patch -p1 < $($(package)_patch_dir)/fix_qt_pkgconfig.patch && \ - echo "!host_build: QMAKE_CFLAGS += $($(package)_cflags) $($(package)_cppflags)" >> qtbase/mkspecs/common/gcc-base.conf && \ - echo "!host_build: QMAKE_CXXFLAGS += $($(package)_cxxflags) $($(package)_cppflags)" >> qtbase/mkspecs/common/gcc-base.conf && \ - echo "!host_build: QMAKE_LFLAGS += $($(package)_ldflags)" >> qtbase/mkspecs/common/gcc-base.conf && \ - sed -i.old "s|QMAKE_CFLAGS = |!host_build: QMAKE_CFLAGS = $($(package)_cflags) $($(package)_cppflags) |" qtbase/mkspecs/win32-g++/qmake.conf && \ - sed -i.old "s|QMAKE_LFLAGS = |!host_build: QMAKE_LFLAGS = $($(package)_ldflags) |" qtbase/mkspecs/win32-g++/qmake.conf && \ - sed -i.old "s|QMAKE_CXXFLAGS = |!host_build: QMAKE_CXXFLAGS = $($(package)_cxxflags) $($(package)_cppflags) |" qtbase/mkspecs/win32-g++/qmake.conf - -endef - -define $(package)_config_cmds - export PKG_CONFIG_SYSROOT_DIR=/ && \ - export PKG_CONFIG_LIBDIR=$(host_prefix)/lib/pkgconfig && \ - export PKG_CONFIG_PATH=$(host_prefix)/share/pkgconfig && \ - ./configure $($(package)_config_opts) && \ - echo "host_build: QT_CONFIG ~= s/system-zlib/zlib" >> mkspecs/qconfig.pri && \ - echo "CONFIG += force_bootstrap" >> mkspecs/qconfig.pri && \ - $(MAKE) sub-src-clean && \ - cd ../qttranslations && ../qtbase/bin/qmake qttranslations.pro -o Makefile && \ - cd translations && ../../qtbase/bin/qmake translations.pro -o Makefile && cd ../.. && \ - cd qttools/src/linguist/lrelease/ && ../../../../qtbase/bin/qmake lrelease.pro -o Makefile && cd ../../../.. && \ - cd qtactiveqt/src/activeqt && ../../../qtbase/bin/qmake activeqt.pro -o Makefile -endef - -define $(package)_build_cmds - $(MAKE) -C src $(addprefix sub-,$($(package)_qt_libs)) && \ - $(MAKE) -C ../qttools/src/linguist/lrelease && \ - $(MAKE) -C ../qtactiveqt/src/activeqt && \ - $(MAKE) -C ../qttranslations -endef - -# $(MAKE) -C qtactiveqt/src/activeqt/container INSTALL_ROOT=$($(package)_staging_dir) install_target && \ -# $(MAKE) -C qtactiveqt/src/activeqt/control INSTALL_ROOT=$($(package)_staging_dir) install_target && \ - - -define $(package)_stage_cmds - $(MAKE) -C src INSTALL_ROOT=$($(package)_staging_dir) $(addsuffix -install_subtargets,$(addprefix sub-,$($(package)_qt_libs))) && cd .. && \ - $(MAKE) -C qttools/src/linguist/lrelease INSTALL_ROOT=$($(package)_staging_dir) install_target && \ - $(MAKE) -C qtactiveqt/src/activeqt INSTALL_ROOT=$($(package)_staging_dir) install &&\ - $(MAKE) -C qttranslations INSTALL_ROOT=$($(package)_staging_dir) install_subtargets && \ - if `test -f qtbase/src/plugins/platforms/xcb/xcb-static/libxcb-static.a`; then \ - cp qtbase/src/plugins/platforms/xcb/xcb-static/libxcb-static.a $($(package)_staging_prefix_dir)/lib; \ - fi -endef - -define $(package)_postprocess_cmds - rm -rf native/mkspecs/ native/lib/ lib/cmake/ && \ - rm -f lib/lib*.la lib/*.prl plugins/*/*.prl -endef diff --git a/contrib/depends/packages/readline.mk b/contrib/depends/packages/readline.mk new file mode 100644 index 00000000000..826c1029e69 --- /dev/null +++ b/contrib/depends/packages/readline.mk @@ -0,0 +1,33 @@ +package=readline +$(package)_version=6.3 +$(package)_download_path=ftp://ftp.cwru.edu/pub/bash/ +$(package)_file_name=$(package)-$($(package)_version).tar.gz +$(package)_sha256_hash=56ba6071b9462f980c5a72ab0023893b65ba6debb4eeb475d7a563dc65cafd43 +$(package)_patches=readline-1.patch + +define $(package)_set_vars + $(package)_build_opts=CC="$($(package)_cc)" + $(package)_config_env=AR="$($(package)_ar)" RANLIB="$($(package)_ranlib)" CC="$($(package)_cc)" + $(package)_config_opts=--prefix=$(host_prefix) + $(package)_config_opts+=--disable-shared --enable-multibye --without-purify --without-curses + $(package)_config_opts_release=--disable-debug-mode + $(package)_build_opts=CFLAGS="$($(package)_cflags) $($(package)_cppflags) -fPIC" +endef + +define $(package)_config_cmds + patch -p1 < $($(package)_patch_dir)/readline-1.patch &&\ + export bash_cv_have_mbstate_t=yes &&\ + export bash_cv_wcwidth_broken=yes &&\ + ./configure $($(package)_config_opts) +endef + +define $(package)_build_cmds + $(MAKE) $($(package)_build_opts) +endef + +define $(package)_stage_cmds + $(MAKE) DESTDIR=$($(package)_staging_dir) install +endef + +define $(package)_postprocess_cmds +endef diff --git a/contrib/depends/packages/unbound.mk b/contrib/depends/packages/unbound.mk new file mode 100644 index 00000000000..beeeb54c170 --- /dev/null +++ b/contrib/depends/packages/unbound.mk @@ -0,0 +1,28 @@ +package=unbound +$(package)_version=1.6.8 +$(package)_download_path=http://www.unbound.net/downloads/ +$(package)_file_name=$(package)-$($(package)_version).tar.gz +$(package)_sha256_hash=e3b428e33f56a45417107448418865fe08d58e0e7fea199b855515f60884dd49 +$(package)_dependencies=openssl expat ldns + +define $(package)_set_vars + $(package)_config_opts=--disable-shared --enable-static --without-pyunbound --prefix=$(host_prefix) --with-libexpat=$(host_prefix) --with-ssl=$(host_prefix) --with-libevent=no --without-pythonmodule --disable-flto --with-pthreads + $(package)_config_opts_linux=--with-pic + $(package)_config_opts_w64=--enable-static-exe --sysconfdir=/etc --prefix=$(host_prefix) --target=$(host_prefix) + $(package)_build_opts_mingw32=LDFLAGS="$($(package)_ldflags) -lpthread" +endef + +define $(package)_config_cmds + $($(package)_autoconf) $($(package)_config_opts) +endef + +define $(package)_build_cmds + $(MAKE) $($(package)_build_opts) +endef + +define $(package)_stage_cmds + $(MAKE) DESTDIR=$($(package)_staging_dir) install +endef + +define $(package)_postprocess_cmds +endef diff --git a/contrib/depends/packages/unwind.mk b/contrib/depends/packages/unwind.mk new file mode 100644 index 00000000000..06e14211586 --- /dev/null +++ b/contrib/depends/packages/unwind.mk @@ -0,0 +1,20 @@ +package=unwind +$(package)_version=1.2 +$(package)_download_path=http://download.savannah.nongnu.org/releases/libunwind +$(package)_file_name=lib$(package)-$($(package)_version).tar.gz +$(package)_sha256_hash=1de38ffbdc88bd694d10081865871cd2bfbb02ad8ef9e1606aee18d65532b992 + +define $(package)_config_cmds + $($(package)_autoconf) --disable-shared --enable-static +endef + +define $(package)_build_cmds + $(MAKE) +endef + +define $(package)_stage_cmds + $(MAKE) DESTDIR=$($(package)_staging_dir) install +endef + +define $(package)_postprocess_cmds +endef diff --git a/contrib/depends/packages/xcb_proto.mk b/contrib/depends/packages/xcb_proto.mk deleted file mode 100644 index 0c7c958d62d..00000000000 --- a/contrib/depends/packages/xcb_proto.mk +++ /dev/null @@ -1,27 +0,0 @@ -package=xcb_proto -$(package)_version=1.10 -$(package)_download_path=http://xcb.freedesktop.org/dist -$(package)_file_name=xcb-proto-$($(package)_version).tar.bz2 -$(package)_sha256_hash=7ef40ddd855b750bc597d2a435da21e55e502a0fefa85b274f2c922800baaf05 - -define $(package)_set_vars - $(package)_config_opts=--disable-shared - $(package)_config_opts_linux=--with-pic -endef - -define $(package)_config_cmds - $($(package)_autoconf) -endef - -define $(package)_build_cmds - $(MAKE) -endef - -define $(package)_stage_cmds - $(MAKE) DESTDIR=$($(package)_staging_dir) install -endef - -define $(package)_postprocess_cmds - find -name "*.pyc" -delete && \ - find -name "*.pyo" -delete -endef diff --git a/contrib/depends/packages/xextproto.mk b/contrib/depends/packages/xextproto.mk deleted file mode 100644 index 98a11eb4974..00000000000 --- a/contrib/depends/packages/xextproto.mk +++ /dev/null @@ -1,21 +0,0 @@ -package=xextproto -$(package)_version=7.3.0 -$(package)_download_path=http://xorg.freedesktop.org/releases/individual/proto -$(package)_file_name=$(package)-$($(package)_version).tar.bz2 -$(package)_sha256_hash=f3f4b23ac8db9c3a9e0d8edb591713f3d70ef9c3b175970dd8823dfc92aa5bb0 - -define $(package)_set_vars -$(package)_config_opts=--disable-shared -endef - -define $(package)_config_cmds - $($(package)_autoconf) -endef - -define $(package)_build_cmds - $(MAKE) -endef - -define $(package)_stage_cmds - $(MAKE) DESTDIR=$($(package)_staging_dir) install -endef diff --git a/contrib/depends/packages/xtrans.mk b/contrib/depends/packages/xtrans.mk deleted file mode 100644 index 99eefa6d5ea..00000000000 --- a/contrib/depends/packages/xtrans.mk +++ /dev/null @@ -1,22 +0,0 @@ -package=xtrans -$(package)_version=1.3.4 -$(package)_download_path=http://xorg.freedesktop.org/releases/individual/lib/ -$(package)_file_name=$(package)-$($(package)_version).tar.bz2 -$(package)_sha256_hash=054d4ee3efd52508c753e9f7bc655ef185a29bd2850dd9e2fc2ccc33544f583a -$(package)_dependencies= - -define $(package)_set_vars -$(package)_config_opts_linux=--with-pic --disable-static -endef - -define $(package)_config_cmds - $($(package)_autoconf) -endef - -define $(package)_build_cmds - $(MAKE) -endef - -define $(package)_stage_cmds - $(MAKE) DESTDIR=$($(package)_staging_dir) install -endef diff --git a/contrib/depends/packages/zeromq.mk b/contrib/depends/packages/zeromq.mk new file mode 100644 index 00000000000..01146c26f69 --- /dev/null +++ b/contrib/depends/packages/zeromq.mk @@ -0,0 +1,34 @@ +package=zeromq +$(package)_version=4.1.5 +$(package)_download_path=https://github.com/zeromq/zeromq4-1/releases/download/v$($(package)_version)/ +$(package)_file_name=$(package)-$($(package)_version).tar.gz +$(package)_sha256_hash=04aac57f081ffa3a2ee5ed04887be9e205df3a7ddade0027460b8042432bdbcf +$(package)_patches=9114d3957725acd34aa8b8d011585812f3369411.patch 9e6745c12e0b100cd38acecc16ce7db02905e27c.patch + +define $(package)_set_vars + $(package)_config_opts=--without-documentation --disable-shared --without-libsodium --disable-curve + $(package)_config_opts_linux=--with-pic + $(package)_cxxflags=-std=c++11 +endef + +define $(package)_preprocess_cmds + patch -p1 < $($(package)_patch_dir)/9114d3957725acd34aa8b8d011585812f3369411.patch && \ + patch -p1 < $($(package)_patch_dir)/9e6745c12e0b100cd38acecc16ce7db02905e27c.patch && \ + ./autogen.sh +endef + +define $(package)_config_cmds + $($(package)_autoconf) +endef + +define $(package)_build_cmds + $(MAKE) libzmq.la +endef + +define $(package)_stage_cmds + $(MAKE) DESTDIR=$($(package)_staging_dir) install-libLTLIBRARIES install-includeHEADERS install-pkgconfigDATA +endef + +define $(package)_postprocess_cmds + rm -rf bin share +endef diff --git a/contrib/depends/packages/zlib.mk b/contrib/depends/packages/zlib.mk index 7739c26a796..589490800f8 100644 --- a/contrib/depends/packages/zlib.mk +++ b/contrib/depends/packages/zlib.mk @@ -22,7 +22,6 @@ define $(package)_build_cmds endef define $(package)_stage_cmds - $(MAKE) DESTDIR=$($(package)_staging_dir) install $($(package)_build_opts) &&\ - $(MAKE) DESTDIR=$(host_prefix) install $($(package)_build_opts) + $(MAKE) DESTDIR=$($(package)_staging_dir) install $($(package)_build_opts) endef diff --git a/contrib/depends/patches/cmake/cmake-1-fixes.patch b/contrib/depends/patches/cmake/cmake-1-fixes.patch new file mode 100644 index 00000000000..062c067676d --- /dev/null +++ b/contrib/depends/patches/cmake/cmake-1-fixes.patch @@ -0,0 +1,67 @@ +This file is part of MXE. See LICENSE.md for licensing information. + +Contains ad hoc patches for cross building. + +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Tony Theodore +Date: Fri, 12 Aug 2016 02:01:20 +1000 +Subject: [PATCH 1/3] fix windres invocation options + +windres doesn't recognise various gcc flags like -mms-bitfields, +-fopenmp, -mthreads etc. (basically not `-D` or `-I`) + +diff --git a/Modules/Platform/Windows-windres.cmake b/Modules/Platform/Windows-windres.cmake +index 1111111..2222222 100644 +--- a/Modules/Platform/Windows-windres.cmake ++++ b/Modules/Platform/Windows-windres.cmake +@@ -1 +1 @@ +-set(CMAKE_RC_COMPILE_OBJECT " -O coff ") ++set(CMAKE_RC_COMPILE_OBJECT " -O coff ") + +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Tony Theodore +Date: Tue, 25 Jul 2017 20:34:56 +1000 +Subject: [PATCH 2/3] add option to disable -isystem + +taken from (not accepted): +https://gitlab.kitware.com/cmake/cmake/merge_requests/895 + +see also: +https://gitlab.kitware.com/cmake/cmake/issues/16291 +https://gitlab.kitware.com/cmake/cmake/issues/16919 + +diff --git a/Modules/Compiler/GNU.cmake b/Modules/Compiler/GNU.cmake +index 1111111..2222222 100644 +--- a/Modules/Compiler/GNU.cmake ++++ b/Modules/Compiler/GNU.cmake +@@ -42,7 +42,7 @@ macro(__compiler_gnu lang) + string(APPEND CMAKE_${lang}_FLAGS_RELWITHDEBINFO_INIT " -O2 -g -DNDEBUG") + set(CMAKE_${lang}_CREATE_PREPROCESSED_SOURCE " -E > ") + set(CMAKE_${lang}_CREATE_ASSEMBLY_SOURCE " -S -o ") +- if(NOT APPLE OR NOT CMAKE_${lang}_COMPILER_VERSION VERSION_LESS 4) # work around #4462 ++ if(NOT APPLE OR NOT CMAKE_${lang}_COMPILER_VERSION VERSION_LESS 4 AND (NOT MXE_DISABLE_INCLUDE_SYSTEM_FLAG)) # work around #4462 + set(CMAKE_INCLUDE_SYSTEM_FLAG_${lang} "-isystem ") + endif() + endmacro() + +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Tony Theodore +Date: Tue, 15 Aug 2017 15:25:06 +1000 +Subject: [PATCH 3/3] add CPACK_NSIS_EXECUTABLE variable + + +diff --git a/Source/CPack/cmCPackNSISGenerator.cxx b/Source/CPack/cmCPackNSISGenerator.cxx +index 1111111..2222222 100644 +--- a/Source/CPack/cmCPackNSISGenerator.cxx ++++ b/Source/CPack/cmCPackNSISGenerator.cxx +@@ -384,7 +384,9 @@ int cmCPackNSISGenerator::InitializeInternal() + } + #endif + +- nsisPath = cmSystemTools::FindProgram("makensis", path, false); ++ this->SetOptionIfNotSet("CPACK_NSIS_EXECUTABLE", "makensis"); ++ nsisPath = cmSystemTools::FindProgram( ++ this->GetOption("CPACK_NSIS_EXECUTABLE"), path, false); + + if (nsisPath.empty()) { + cmCPackLogger( diff --git a/contrib/depends/patches/icu4c/icu-001-dont-build-static-dynamic-twice.patch b/contrib/depends/patches/icu4c/icu-001-dont-build-static-dynamic-twice.patch new file mode 100644 index 00000000000..bbd4e99e7f3 --- /dev/null +++ b/contrib/depends/patches/icu4c/icu-001-dont-build-static-dynamic-twice.patch @@ -0,0 +1,37 @@ +Don't build object files twice + +When passed --enable-static and --enable-shared, icu will generate +both a shared and a static version of its libraries. + +However, in order to do so, it builds each and every object file +twice: once with -fPIC (for the shared library), and once without +-fPIC (for the static library). While admittedly building -fPIC for a +static library generates a slightly suboptimal code, this is what all +the autotools-based project are doing. They build each object file +once, and they use it for both the static and shared libraries. + +icu builds the object files for the shared library as .o files, and +the object files for static library as .ao files. By simply changing +the suffix of object files used for static libraries to ".o", we tell +icu to use the ones built for the shared library (i.e, with -fPIC), +and avoid the double build of icu. + +On a fast build server, this brings the target icu build from +3m41.302s down to 1m43.926s (approximate numbers: some other builds +are running on the system at the same time). + +Signed-off-by: Thomas Petazzoni + +Index: b/source/config/mh-linux +=================================================================== +--- a/source/config/mh-linux ++++ b/source/config/mh-linux +@@ -38,7 +38,7 @@ + ## Shared object suffix + SO = so + ## Non-shared intermediate object suffix +-STATIC_O = ao ++STATIC_O = o + + ## Compilation rules + %.$(STATIC_O): $(srcdir)/%.c diff --git a/contrib/depends/patches/qt/fix-xcb-include-order.patch b/contrib/depends/patches/qt/fix-xcb-include-order.patch deleted file mode 100644 index ec2bc17d9bd..00000000000 --- a/contrib/depends/patches/qt/fix-xcb-include-order.patch +++ /dev/null @@ -1,49 +0,0 @@ ---- old/qtbase/src/plugins/platforms/xcb/xcb_qpa_lib.pro 2015-03-17 -+++ new/qtbase/src/plugins/platforms/xcb/xcb_qpa_lib.pro 2015-03-17 -@@ -76,8 +76,6 @@ - - DEFINES += $$QMAKE_DEFINES_XCB - LIBS += $$QMAKE_LIBS_XCB --QMAKE_CXXFLAGS += $$QMAKE_CFLAGS_XCB --QMAKE_CFLAGS += $$QMAKE_CFLAGS_XCB - - CONFIG += qpa/genericunixfontdatabase - -@@ -89,7 +87,8 @@ - contains(QT_CONFIG, xcb-qt) { - DEFINES += XCB_USE_RENDER - XCB_DIR = ../../../3rdparty/xcb -- INCLUDEPATH += $$XCB_DIR/include $$XCB_DIR/sysinclude -+ QMAKE_CFLAGS += -I$$XCB_DIR/include -I$$XCB_DIR/sysinclude $$QMAKE_CFLAGS_XCB -+ QMAKE_CXXFLAGS += -I$$XCB_DIR/include -I$$XCB_DIR/sysinclude $$QMAKE_CFLAGS_XCB - LIBS += -lxcb -L$$MODULE_BASE_OUTDIR/lib -lxcb-static$$qtPlatformTargetSuffix() - } else { - LIBS += -lxcb -lxcb-image -lxcb-icccm -lxcb-sync -lxcb-xfixes -lxcb-shm -lxcb-randr -lxcb-shape -lxcb-keysyms -lxcb-xinerama ---- old/qtbase/src/plugins/platforms/xcb/xcb-static/xcb-static.pro -+++ new/qtbase/src/plugins/platforms/xcb/xcb-static/xcb-static.pro -@@ -9,7 +9,8 @@ - - XCB_DIR = ../../../../3rdparty/xcb - --INCLUDEPATH += $$XCB_DIR/include $$XCB_DIR/include/xcb $$XCB_DIR/sysinclude -+QMAKE_CFLAGS += -I$$XCB_DIR/include -I$$XCB_DIR/include/xcb -I$$XCB_DIR/sysinclude -+QMAKE_CXXFLAGS += -I$$XCB_DIR/include -I$$XCB_DIR/include/xcb -I$$XCB_DIR/sysinclude - - QMAKE_CXXFLAGS += $$QMAKE_CFLAGS_XCB - QMAKE_CFLAGS += $$QMAKE_CFLAGS_XCB ---- old/qtbase/src/plugins/platforms/xcb/xcb-plugin.pro -+++ new/qtbase/src/plugins/platforms/xcb/xcb-plugin.pro -@@ -6,6 +6,13 @@ - qxcbmain.cpp - OTHER_FILES += xcb.json README - -+contains(QT_CONFIG, xcb-qt) { -+ DEFINES += XCB_USE_RENDER -+ XCB_DIR = ../../../3rdparty/xcb -+ QMAKE_CFLAGS += -I$$XCB_DIR/include -I$$XCB_DIR/sysinclude $$QMAKE_CFLAGS_XCB -+ QMAKE_CXXFLAGS += -I$$XCB_DIR/include -I$$XCB_DIR/sysinclude $$QMAKE_CFLAGS_XCB -+} -+ - PLUGIN_TYPE = platforms - PLUGIN_CLASS_NAME = QXcbIntegrationPlugin - !equals(TARGET, $$QT_DEFAULT_QPA_PLUGIN): PLUGIN_EXTENDS = - diff --git a/contrib/depends/patches/qt/fix_qt_pkgconfig.patch b/contrib/depends/patches/qt/fix_qt_pkgconfig.patch deleted file mode 100644 index 34302a9f2d2..00000000000 --- a/contrib/depends/patches/qt/fix_qt_pkgconfig.patch +++ /dev/null @@ -1,11 +0,0 @@ ---- old/qtbase/mkspecs/features/qt_module.prf -+++ new/qtbase/mkspecs/features/qt_module.prf -@@ -245,7 +245,7 @@ - load(qt_targets) - - # this builds on top of qt_common --!internal_module:!lib_bundle:if(unix|mingw) { -+unix|mingw { - CONFIG += create_pc - QMAKE_PKGCONFIG_DESTDIR = pkgconfig - host_build: \ diff --git a/contrib/depends/patches/qt/mac-qmake.conf b/contrib/depends/patches/qt/mac-qmake.conf deleted file mode 100644 index ca70d30b15e..00000000000 --- a/contrib/depends/patches/qt/mac-qmake.conf +++ /dev/null @@ -1,25 +0,0 @@ -MAKEFILE_GENERATOR = UNIX -CONFIG += app_bundle incremental global_init_link_order lib_version_first plugin_no_soname absolute_library_soname -QMAKE_INCREMENTAL_STYLE = sublib -include(../common/macx.conf) -include(../common/gcc-base-mac.conf) -include(../common/clang.conf) -include(../common/clang-mac.conf) -QMAKE_MAC_SDK_PATH=$${MAC_SDK_PATH} -QMAKE_XCODE_VERSION=4.3 -QMAKE_XCODE_DEVELOPER_PATH=/Developer -QMAKE_MACOSX_DEPLOYMENT_TARGET = $${MAC_MIN_VERSION} -QMAKE_MAC_SDK=macosx -QMAKE_MAC_SDK.macosx.Path = $${MAC_SDK_PATH} -QMAKE_MAC_SDK.macosx.platform_name = macosx -QMAKE_MAC_SDK.macosx.SDKVersion = $${MAC_SDK_VERSION} -QMAKE_MAC_SDK.macosx.PlatformPath = /phony -!host_build: QMAKE_CFLAGS += -target $${MAC_TARGET} -!host_build: QMAKE_OBJECTIVE_CFLAGS += $$QMAKE_CFLAGS -!host_build: QMAKE_CXXFLAGS += $$QMAKE_CFLAGS -!host_build: QMAKE_LFLAGS += -target $${MAC_TARGET} -mlinker-version=$${MAC_LD64_VERSION} -QMAKE_AR = $${CROSS_COMPILE}ar cq -QMAKE_RANLIB=$${CROSS_COMPILE}ranlib -QMAKE_LIBTOOL=$${CROSS_COMPILE}libtool -QMAKE_INSTALL_NAME_TOOL=$${CROSS_COMPILE}install_name_tool -load(qt_config) diff --git a/contrib/depends/patches/qt/mingw-uuidof.patch b/contrib/depends/patches/qt/mingw-uuidof.patch deleted file mode 100644 index fb21923c8c3..00000000000 --- a/contrib/depends/patches/qt/mingw-uuidof.patch +++ /dev/null @@ -1,44 +0,0 @@ ---- old/qtbase/src/plugins/platforms/windows/qwindowscontext.cpp -+++ new/qtbase/src/plugins/platforms/windows/qwindowscontext.cpp -@@ -77,7 +77,7 @@ - #include - #include - #include --#ifndef Q_OS_WINCE -+#if !defined(Q_OS_WINCE) && (!defined(USE___UUIDOF) || (defined(USE___UUIDOF) && USE___UUIDOF == 1)) - # include - #endif - -@@ -814,7 +814,7 @@ - HWND_MESSAGE, NULL, static_cast(GetModuleHandle(0)), NULL); - } - --#ifndef Q_OS_WINCE -+#if !defined(Q_OS_WINCE) && (!defined(USE___UUIDOF) || (defined(USE___UUIDOF) && USE___UUIDOF == 1)) - // Re-engineered from the inline function _com_error::ErrorMessage(). - // We cannot use it directly since it uses swprintf_s(), which is not - // present in the MSVCRT.DLL found on Windows XP (QTBUG-35617). -@@ -833,7 +833,7 @@ - return QString::asprintf("IDispatch error #%u", uint(wCode)); - return QString::asprintf("Unknown error 0x0%x", uint(comError.Error())); - } --#endif // !Q_OS_WINCE -+#endif // !defined(Q_OS_WINCE) && (!defined(USE___UUIDOF) || (defined(USE___UUIDOF) && USE___UUIDOF == 1)) - - /*! - \brief Common COM error strings. -@@ -901,12 +901,12 @@ - default: - break; - } --#ifndef Q_OS_WINCE -+#if !defined(Q_OS_WINCE) && (!defined(USE___UUIDOF) || (defined(USE___UUIDOF) && USE___UUIDOF == 1)) - _com_error error(hr); - result += QByteArrayLiteral(" ("); - result += errorMessageFromComError(error); - result += ')'; --#endif // !Q_OS_WINCE -+#endif // !defined(Q_OS_WINCE) && (!defined(USE___UUIDOF) || (defined(USE___UUIDOF) && USE___UUIDOF == 1)) - return result; - } - diff --git a/contrib/depends/patches/qt/pidlist_absolute.patch b/contrib/depends/patches/qt/pidlist_absolute.patch deleted file mode 100644 index c7928241790..00000000000 --- a/contrib/depends/patches/qt/pidlist_absolute.patch +++ /dev/null @@ -1,37 +0,0 @@ -diff -dur old/qtbase/src/plugins/platforms/windows/qwindowscontext.h new/qtbase/src/plugins/platforms/windows/qwindowscontext.h ---- old/qtbase/src/plugins/platforms/windows/qwindowscontext.h -+++ new/qtbase/src/plugins/platforms/windows/qwindowscontext.h -@@ -136,10 +136,18 @@ - inline void init(); - - typedef HRESULT (WINAPI *SHCreateItemFromParsingName)(PCWSTR, IBindCtx *, const GUID&, void **); -+#if defined(Q_CC_MINGW) && (!defined(__MINGW64_VERSION_MAJOR) || __MINGW64_VERSION_MAJOR < 3) -+ typedef HRESULT (WINAPI *SHGetKnownFolderIDList)(const GUID &, DWORD, HANDLE, ITEMIDLIST **); -+#else - typedef HRESULT (WINAPI *SHGetKnownFolderIDList)(const GUID &, DWORD, HANDLE, PIDLIST_ABSOLUTE *); -+#endif - typedef HRESULT (WINAPI *SHGetStockIconInfo)(int , int , _SHSTOCKICONINFO *); - typedef HRESULT (WINAPI *SHGetImageList)(int, REFIID , void **); -+#if defined(Q_CC_MINGW) && (!defined(__MINGW64_VERSION_MAJOR) || __MINGW64_VERSION_MAJOR < 3) -+ typedef HRESULT (WINAPI *SHCreateItemFromIDList)(const ITEMIDLIST *, REFIID, void **); -+#else - typedef HRESULT (WINAPI *SHCreateItemFromIDList)(PCIDLIST_ABSOLUTE, REFIID, void **); -+#endif - - SHCreateItemFromParsingName sHCreateItemFromParsingName; - SHGetKnownFolderIDList sHGetKnownFolderIDList; -diff -dur old/qtbase/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp new/qtbase/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp ---- old/qtbase/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp -+++ new/qtbase/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp -@@ -1016,7 +1016,11 @@ - qWarning() << __FUNCTION__ << ": Invalid CLSID: " << url.path(); - return Q_NULLPTR; - } -+#if defined(Q_CC_MINGW) && (!defined(__MINGW64_VERSION_MAJOR) || __MINGW64_VERSION_MAJOR < 3) -+ ITEMIDLIST *idList; -+#else - PIDLIST_ABSOLUTE idList; -+#endif - HRESULT hr = QWindowsContext::shell32dll.sHGetKnownFolderIDList(uuid, 0, 0, &idList); - if (FAILED(hr)) { - qErrnoWarning("%s: SHGetKnownFolderIDList(%s)) failed", __FUNCTION__, qPrintable(url.toString())); diff --git a/contrib/depends/patches/readline/readline-1.patch b/contrib/depends/patches/readline/readline-1.patch new file mode 100644 index 00000000000..7610a29ebea --- /dev/null +++ b/contrib/depends/patches/readline/readline-1.patch @@ -0,0 +1,187 @@ +From c0572cecbeadc8fe24c70c5c39d49210a39ac719 Mon Sep 17 00:00:00 2001 +From: Timothy Gu +Date: Tue, 30 Sep 2014 10:32:33 -0700 +Subject: [PATCH 1/2] signals: safeguard the remaining usage of frequently + missing signals + +diff --git a/input.c b/input.c +index 117dfe8..465f0b9 100644 +--- a/input.c ++++ b/input.c +@@ -532,9 +532,17 @@ rl_getc (stream) + Otherwise (not EINTR), some error occurred, also signifying EOF. */ + if (errno != EINTR) + return (RL_ISSTATE (RL_STATE_READCMD) ? READERR : EOF); +- else if (_rl_caught_signal == SIGHUP || _rl_caught_signal == SIGTERM) ++ else if (_rl_caught_signal == SIGTERM ++#if defined(SIGHUP) ++ || _rl_caught_signal == SIGHUP ++#endif ++ ) + return (RL_ISSTATE (RL_STATE_READCMD) ? READERR : EOF); +- else if (_rl_caught_signal == SIGINT || _rl_caught_signal == SIGQUIT) ++ else if (_rl_caught_signal == SIGINT ++#if defined(SIGQUIT) ++ || _rl_caught_signal == SIGQUIT ++#endif ++ ) + RL_CHECK_SIGNALS (); + + if (rl_signal_event_hook) +diff --git a/signals.c b/signals.c +index 61f02f9..7c921d6 100644 +--- a/signals.c ++++ b/signals.c +@@ -216,7 +216,9 @@ _rl_handle_signal (sig) + /* FALLTHROUGH */ + + case SIGTERM: ++#if defined (SIGHUP) + case SIGHUP: ++#endif + #if defined (SIGTSTP) + case SIGTSTP: + case SIGTTOU: +@@ -426,7 +428,9 @@ rl_set_signals () + + rl_maybe_set_sighandler (SIGINT, rl_signal_handler, &old_int); + rl_maybe_set_sighandler (SIGTERM, rl_signal_handler, &old_term); ++#if defined (SIGHUP) + rl_maybe_set_sighandler (SIGHUP, rl_signal_handler, &old_hup); ++#endif + #if defined (SIGQUIT) + rl_maybe_set_sighandler (SIGQUIT, rl_signal_handler, &old_quit); + #endif +@@ -491,7 +495,9 @@ rl_clear_signals () + overhead */ + rl_maybe_restore_sighandler (SIGINT, &old_int); + rl_maybe_restore_sighandler (SIGTERM, &old_term); ++#if defined (SIGHUP) + rl_maybe_restore_sighandler (SIGHUP, &old_hup); ++#endif + #if defined (SIGQUIT) + rl_maybe_restore_sighandler (SIGQUIT, &old_quit); + #endif +-- +1.8.3.2 + + +From 6896ffa4fc85bf0dfae58e69a860d2076c1d9fd2 Mon Sep 17 00:00:00 2001 +From: Timothy Gu +Date: Tue, 30 Sep 2014 17:16:32 -0700 +Subject: [PATCH 2/2] Handle missing S_IS* macros more gracefully + +diff --git a/colors.c b/colors.c +index 89d9035..ec19844 100644 +--- a/colors.c ++++ b/colors.c +@@ -152,14 +152,22 @@ _rl_print_color_indicator (char *f) + { + colored_filetype = C_FILE; + ++#if defined (S_ISUID) + if ((mode & S_ISUID) != 0 && is_colored (C_SETUID)) + colored_filetype = C_SETUID; +- else if ((mode & S_ISGID) != 0 && is_colored (C_SETGID)) ++ else ++#endif ++#if defined (S_ISGID) ++ if ((mode & S_ISGID) != 0 && is_colored (C_SETGID)) + colored_filetype = C_SETGID; +- else if (is_colored (C_CAP) && 0) //f->has_capability) ++ else ++#endif ++ if (is_colored (C_CAP) && 0) //f->has_capability) + colored_filetype = C_CAP; ++#if defined(S_IXUGO) + else if ((mode & S_IXUGO) != 0 && is_colored (C_EXEC)) + colored_filetype = C_EXEC; ++#endif + else if ((1 < astat.st_nlink) && is_colored (C_MULTIHARDLINK)) + colored_filetype = C_MULTIHARDLINK; + } +@@ -173,8 +181,10 @@ _rl_print_color_indicator (char *f) + colored_filetype = C_STICKY_OTHER_WRITABLE; + else + #endif ++#if defined (S_IWOTH) + if ((mode & S_IWOTH) != 0 && is_colored (C_OTHER_WRITABLE)) + colored_filetype = C_OTHER_WRITABLE; ++#endif + #if defined (S_ISVTX) + else if ((mode & S_ISVTX) != 0 && is_colored (C_STICKY)) + colored_filetype = C_STICKY; +diff --git a/colors.h b/colors.h +index fc926e5..e62edd0 100644 +--- a/colors.h ++++ b/colors.h +@@ -96,7 +96,7 @@ enum indicator_no + }; + + +-#if !S_IXUGO ++#if !S_IXUGO && defined(S_IXUSR) && defined(S_IXGRP) && defined(S_IXOTH) + # define S_IXUGO (S_IXUSR | S_IXGRP | S_IXOTH) + #endif + +diff --git a/posixstat.h b/posixstat.h +index 3eb7f29..854a2c9 100644 +--- a/posixstat.h ++++ b/posixstat.h +@@ -78,30 +78,44 @@ + + #if defined (S_IFBLK) && !defined (S_ISBLK) + #define S_ISBLK(m) (((m)&S_IFMT) == S_IFBLK) /* block device */ ++#elif !defined (S_IFBLK) ++#define S_ISBLK(m) 0 + #endif + + #if defined (S_IFCHR) && !defined (S_ISCHR) + #define S_ISCHR(m) (((m)&S_IFMT) == S_IFCHR) /* character device */ ++#elif !defined (S_IFCHR) ++#define S_ISCHR(m) 0 + #endif + + #if defined (S_IFDIR) && !defined (S_ISDIR) + #define S_ISDIR(m) (((m)&S_IFMT) == S_IFDIR) /* directory */ ++#elif !defined (S_IFDIR) ++#define S_ISDIR(m) 0 + #endif + + #if defined (S_IFREG) && !defined (S_ISREG) + #define S_ISREG(m) (((m)&S_IFMT) == S_IFREG) /* file */ ++#elif !defined (S_IFREG) ++#define S_ISREG(m) 0 + #endif + + #if defined (S_IFIFO) && !defined (S_ISFIFO) + #define S_ISFIFO(m) (((m)&S_IFMT) == S_IFIFO) /* fifo - named pipe */ ++#elif !defined (S_IFIFO) ++#define S_ISFIFO(m) 0 + #endif + + #if defined (S_IFLNK) && !defined (S_ISLNK) + #define S_ISLNK(m) (((m)&S_IFMT) == S_IFLNK) /* symbolic link */ ++#elif !defined (S_IFLNK) ++#define S_ISLNK(m) 0 + #endif + + #if defined (S_IFSOCK) && !defined (S_ISSOCK) + #define S_ISSOCK(m) (((m)&S_IFMT) == S_IFSOCK) /* socket */ ++#elif !defined (S_IFSOCK) ++#define S_ISSOCK(m) 0 + #endif + + /* +@@ -137,6 +151,8 @@ + /* These are non-standard, but are used in builtins.c$symbolic_umask() */ + #define S_IRUGO (S_IRUSR | S_IRGRP | S_IROTH) + #define S_IWUGO (S_IWUSR | S_IWGRP | S_IWOTH) ++#if defined(S_IXUSR) && defined(S_IXGRP) && defined(S_IXOTH) + #define S_IXUGO (S_IXUSR | S_IXGRP | S_IXOTH) ++#endif + + #endif /* _POSIXSTAT_H_ */ +-- +1.8.3.2 + diff --git a/contrib/depends/patches/zeromq/9114d3957725acd34aa8b8d011585812f3369411.patch b/contrib/depends/patches/zeromq/9114d3957725acd34aa8b8d011585812f3369411.patch new file mode 100644 index 00000000000..f704b3d94fd --- /dev/null +++ b/contrib/depends/patches/zeromq/9114d3957725acd34aa8b8d011585812f3369411.patch @@ -0,0 +1,22 @@ +From 9114d3957725acd34aa8b8d011585812f3369411 Mon Sep 17 00:00:00 2001 +From: Jeroen Ooms +Date: Tue, 20 Oct 2015 13:10:38 +0200 +Subject: [PATCH] enable static libraries on mingw + +--- + configure.ac | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/configure.ac b/configure.ac +index 393505b..e92131a 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -265,7 +265,7 @@ case "${host_os}" in + libzmq_dso_visibility="no" + + if test "x$enable_static" = "xyes"; then +- AC_MSG_ERROR([Building static libraries is not supported under MinGW32]) ++ CPPFLAGS="-DZMQ_STATIC" + fi + + # Set FD_SETSIZE to 1024 \ No newline at end of file diff --git a/contrib/depends/patches/zeromq/9e6745c12e0b100cd38acecc16ce7db02905e27c.patch b/contrib/depends/patches/zeromq/9e6745c12e0b100cd38acecc16ce7db02905e27c.patch new file mode 100644 index 00000000000..9aff2c179a1 --- /dev/null +++ b/contrib/depends/patches/zeromq/9e6745c12e0b100cd38acecc16ce7db02905e27c.patch @@ -0,0 +1,22 @@ +From 9e6745c12e0b100cd38acecc16ce7db02905e27c Mon Sep 17 00:00:00 2001 +From: David Millard +Date: Tue, 10 May 2016 13:53:53 -0700 +Subject: [PATCH] Fix autotools for static MinGW builds + +--- + configure.ac | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/configure.ac b/configure.ac +index 5a0fa14..def6ea7 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -259,7 +259,7 @@ case "${host_os}" in + libzmq_dso_visibility="no" + + if test "x$enable_static" = "xyes"; then +- CPPFLAGS="-DZMQ_STATIC" ++ CPPFLAGS="-DZMQ_STATIC $CPPFLAGS" + fi + + # Set FD_SETSIZE to 1024 \ No newline at end of file diff --git a/contrib/depends/protobuf.mk b/contrib/depends/protobuf.mk new file mode 100644 index 00000000000..54d3fd92451 --- /dev/null +++ b/contrib/depends/protobuf.mk @@ -0,0 +1,29 @@ +package=protobuf +$(package)_version=$(native_$(package)_version) +$(package)_download_path=$(native_$(package)_download_path) +$(package)_file_name=$(native_$(package)_file_name) +$(package)_sha256_hash=$(native_$(package)_sha256_hash) +$(package)_dependencies=native_$(package) +$(package)_cxxflags=-std=c++11 + +define $(package)_set_vars + $(package)_config_opts=--disable-shared --with-protoc=$(build_prefix)/bin/protoc + $(package)_config_opts_linux=--with-pic +endef + +define $(package)_config_cmds + $($(package)_autoconf) +endef + +define $(package)_build_cmds + $(MAKE) -C src libprotobuf.la +endef + +define $(package)_stage_cmds + $(MAKE) DESTDIR=$($(package)_staging_dir) -C src install-libLTLIBRARIES install-nobase_includeHEADERS &&\ + $(MAKE) DESTDIR=$($(package)_staging_dir) install-pkgconfigDATA +endef + +define $(package)_postprocess_cmds + rm lib/libprotoc.a +endef diff --git a/contrib/depends/toolchain.cmake.in b/contrib/depends/toolchain.cmake.in new file mode 100644 index 00000000000..eb4fcba04e2 --- /dev/null +++ b/contrib/depends/toolchain.cmake.in @@ -0,0 +1,45 @@ +# this one is important +SET(CMAKE_SYSTEM_NAME @depends@) + +#this one not so much +SET(CMAKE_SYSTEM_VERSION 1) + +# specify the cross compiler +SET(CMAKE_C_COMPILER @CC@) +SET(CMAKE_CXX_COMPILER @CXX@) + +SET(STATIC true) +SET(UNBOUND_STATIC true) + +# where is the target environment +SET(CMAKE_FIND_ROOT_PATH @prefix@ /usr) + +SET(THREADS_PTHREAD_ARG "2" CACHE STRING "Forcibly set by CMakeLists.txt." FORCE) +SET(Readline_ROOT_DIR @prefix@) + +SET(LIBUNWIND_INCLUDE_DIR @prefix@/include) +SET(LIBUNWIND_LIBRARIES @prefix@/lib/libunwind.a) +SET(LIBUNWIND_LIBRARY_DIRS @prefix@/lib) + +SET(UNBOUND_INCLUDE_DIR @prefix@/include) +SET(UNBOUND_LIBRARIES @prefix@/lib) +SET(UNBOUND_LIBRARY @prefix@/lib/libunbound.a) + +SET(ZMQ_INCLUDE_PATH @prefix@/include) +SET(ZMQ_LIB @prefix@/lib/libzmq.a) + +#SET(BOOST_IGNORE_SYSTEM_PATHS_DEFAULT ON) +#SET(BOOST_IGNORE_SYSTEM_PATH ON) +#SET(BOOST_ROOT @prefix@) +#SET(BOOST_LIBRARYDIR @prefix@/lib) + +# for libraries and headers in the target directories +set (CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) # Find programs on host +set (CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) # Find libs in target +set (CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) # Find includes in target + +#set (MINGW_FLAG "-m64") +#set (USE_LTO_DEFAULT false) + +#Create a new cmake flag that indicates building with depends +set (DEPENDS true) diff --git a/contrib/epee/include/misc_os_dependent.h b/contrib/epee/include/misc_os_dependent.h index ffe575501eb..0d09683d6e0 100644 --- a/contrib/epee/include/misc_os_dependent.h +++ b/contrib/epee/include/misc_os_dependent.h @@ -24,7 +24,7 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // #ifdef _WIN32 -#include +#include #endif #ifdef WIN32 diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt index 1fc4d64c1cb..4757b2a3b23 100644 --- a/external/CMakeLists.txt +++ b/external/CMakeLists.txt @@ -37,14 +37,18 @@ find_package(Miniupnpc REQUIRED) -message(STATUS "Using in-tree miniupnpc") - -add_subdirectory(miniupnp/miniupnpc) - -set_property(TARGET libminiupnpc-static PROPERTY FOLDER "external") +if(DEPENDS) + message(STATUS "Using static depends miniupnpc found at ${MINIUPNP_LIBRARIES}") + set(UPNP_STATIC true PARENT_SCOPE) + set(UPNP_LIBRARIES ${MINIUPNP_LIBRARY} PARENT_SCOPE) +else() + message(STATUS "Using in-tree miniupnpc") + add_subdirectory(miniupnp/miniupnpc) + set_property(TARGET libminiupnpc-static PROPERTY FOLDER "external") +endif() if(MSVC) set_property(TARGET libminiupnpc-static APPEND_STRING PROPERTY COMPILE_FLAGS " -wd4244 -wd4267") -elseif(NOT MSVC) +elseif(NOT MSVC AND NOT DEPENDS) set_property(TARGET libminiupnpc-static APPEND_STRING PROPERTY COMPILE_FLAGS " -Wno-undef -Wno-unused-result -Wno-unused-value") endif() @@ -52,7 +56,7 @@ set(UPNP_LIBRARIES "libminiupnpc-static" PARENT_SCOPE) find_package(Unbound) -if(NOT UNBOUND_INCLUDE_DIR OR STATIC) +if(NOT UNBOUND_INCLUDE_DIR OR STATIC AND NOT DEPENDS) # NOTE: If STATIC is true, CMAKE_FIND_LIBRARY_SUFFIXES has been reordered. # unbound has config tests which used OpenSSL libraries, so -ldl may need to # be set in this case. @@ -65,15 +69,27 @@ if(NOT UNBOUND_INCLUDE_DIR OR STATIC) set(UNBOUND_LIBRARY "unbound" PARENT_SCOPE) set(UNBOUND_LIBRARY_DIRS "${LIBEVENT2_LIBDIR}" PARENT_SCOPE) else() - message(STATUS "Found libunbound include (unbound.h) in ${UNBOUND_INCLUDE_DIR}") - if(UNBOUND_LIBRARIES) - message(STATUS "Found libunbound shared library") - set(UNBOUND_STATIC false PARENT_SCOPE) - set(UNBOUND_INCLUDE ${UNBOUND_INCLUDE_DIR} PARENT_SCOPE) - set(UNBOUND_LIBRARY ${UNBOUND_LIBRARIES} PARENT_SCOPE) - set(UNBOUND_LIBRARY_DIRS "" PARENT_SCOPE) + if(DEPENDS) + message(STATUS "Found libunbound include (unbound.h) in ${UNBOUND_INCLUDE_DIR}") + if(UNBOUND_LIBRARIES) + message(STATUS "Found libunbound library in ${UNBOUND_LIBRARIES}") + set(UNBOUND_STATIC true PARENT_SCOPE) + set(UNBOUND_LIBRARY ${UNBOUND_LIBRARIES} PARENTSCOPE) + set(UNBOUND_LIBRARY_DIRS "" PARENT_SCOPE) + else() + die("Found libunbound includes, but could not find libunbound library. Please make sure you have installed libunbound or libunbound-dev or the equivalent") + endif() else() - die("Found libunbound includes, but could not find libunbound library. Please make sure you have installed libunbound or libunbound-dev or the equivalent") + message(STATUS "Found libunbound include (unbound.h) in ${UNBOUND_INCLUDE_DIR}") + if(UNBOUND_LIBRARIES) + message(STATUS "Found libunbound shared library") + set(UNBOUND_STATIC false PARENT_SCOPE) + set(UNBOUND_INCLUDE ${UNBOUND_INCLUDE_DIR} PARENT_SCOPE) + set(UNBOUND_LIBRARY ${UNBOUND_LIBRARIES} PARENT_SCOPE) + set(UNBOUND_LIBRARY_DIRS "" PARENT_SCOPE) + else() + die("Found libunbound includes, but could not find libunbound library. Please make sure you have installed libunbound or libunbound-dev or the equivalent") + endif() endif() endif() From 29311fd140bf7e9983fa7500ad7c094974484a58 Mon Sep 17 00:00:00 2001 From: TheCharlatan Date: Tue, 20 Mar 2018 00:35:47 +0100 Subject: [PATCH 0327/1404] Disable stack unwinding for mingw32 depends build. --- CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index a02f62e624b..32100e57474 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -372,6 +372,9 @@ add_definitions("-DBLOCKCHAIN_DB=${BLOCKCHAIN_DB}") if (APPLE) set(DEFAULT_STACK_TRACE OFF) set(LIBUNWIND_LIBRARIES "") +elseif (DEPENDS AND NOT LINUX) + set(DEFAULT_STACK_TRACE OFF) + set(LIBUNWIND_LIBRARIES "") elseif(CMAKE_C_COMPILER_ID STREQUAL "GNU" AND NOT MINGW) set(DEFAULT_STACK_TRACE ON) set(STACK_TRACE_LIB "easylogging++") # for diag output only From 56b6e41ea7b7c17b513c24797d234e5cf9df8fa2 Mon Sep 17 00:00:00 2001 From: TheCharlatan Date: Tue, 20 Mar 2018 00:33:49 +0100 Subject: [PATCH 0328/1404] Add support for apple and arm building Add pcsc-lite to linux builds Fixup windows icu4c linking with depends, the static libraries have an 's' appended to them Compiling depends arm-linux-gnueabihf will allow you to compile armv6zk monero binaries --- CMakeLists.txt | 19 ++++++-- contrib/depends/Makefile | 4 +- contrib/depends/packages/boost.mk | 14 +++--- contrib/depends/packages/packages.mk | 5 +- contrib/depends/packages/unwind.mk | 4 +- contrib/depends/toolchain.cmake.in | 65 +++++++++++++++++++------ src/blockchain_utilities/CMakeLists.txt | 4 +- src/checkpoints/CMakeLists.txt | 10 ++-- src/cryptonote_basic/CMakeLists.txt | 10 ++-- src/daemon/CMakeLists.txt | 4 +- 10 files changed, 102 insertions(+), 37 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 32100e57474..b1fea6899f6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -413,6 +413,7 @@ if (CMAKE_SYSTEM_NAME MATCHES "(SunOS|Solaris)") endif () if (APPLE AND NOT IOS) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=x86-64 -fvisibility=default") if (NOT OpenSSL_DIR) EXECUTE_PROCESS(COMMAND brew --prefix openssl OUTPUT_VARIABLE OPENSSL_ROOT_DIR @@ -486,7 +487,9 @@ if(MSVC) include_directories(SYSTEM src/platform/msc) else() include(TestCXXAcceptsFlag) - set(ARCH native CACHE STRING "CPU to build for: -march value or 'default' to not pass -march at all") + if (NOT DEPENDS) + set(ARCH native CACHE STRING "CPU to build for: -march value or 'default' to not pass -march at all") + endif() message(STATUS "Building on ${CMAKE_SYSTEM_PROCESSOR} for ${ARCH}") if(ARCH STREQUAL "default") set(ARCH_FLAG "") @@ -671,6 +674,10 @@ else() message(STATUS "Selecting VFP for ARMv6") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mfpu=vfp") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mfpu=vfp") + if(DEPENDS) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -marm") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -marm") + endif() endif(ARM6) if(ARM7) @@ -725,7 +732,7 @@ else() endif() if(APPLE) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DGTEST_HAS_TR1_TUPLE=0") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden -DGTEST_HAS_TR1_TUPLE=0") endif() set(DEBUG_FLAGS "-g3") @@ -815,8 +822,12 @@ endif() include_directories(SYSTEM ${Boost_INCLUDE_DIRS}) if(MINGW) set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wa,-mbig-obj") - set(EXTRA_LIBRARIES mswsock;ws2_32;iphlpapi;crypt32) - set(ICU_LIBRARIES ${Boost_LOCALE_LIBRARY} icuio icuin icuuc icudt icutu iconv) + set(EXTRA_LIBRARIES mswsock;ws2_32;iphlpapi) + if(DEPENDS) + set(ICU_LIBRARIES ${Boost_LOCALE_LIBRARY} sicuio sicuin sicuuc sicudt sicutu iconv) + else() + set(ICU_LIBRARIES ${Boost_LOCALE_LIBRARY} icuio icuin icuuc icudt icutu iconv) + endif() elseif(APPLE OR OPENBSD OR ANDROID) set(EXTRA_LIBRARIES "") elseif(FREEBSD) diff --git a/contrib/depends/Makefile b/contrib/depends/Makefile index be81626b120..b0c032c09c8 100644 --- a/contrib/depends/Makefile +++ b/contrib/depends/Makefile @@ -67,7 +67,7 @@ endif ifeq ($(host_os),linux) host_cmake=Linux endif -ifeq ($(host_os),darwin11) +ifeq ($(host_os),darwin) host_cmake=Darwin endif @@ -182,6 +182,8 @@ $(host_prefix)/share/toolchain.cmake : toolchain.cmake.in $(host_prefix)/.stamp_ -e 's|@debug@|$(DEBUG)|' \ -e 's|@depends@|$(host_cmake)|' \ -e 's|@prefix@|$($(host_arch)_$(host_os)_prefix)|'\ + -e 's|@sdk@|$(SDK_PATH)|'\ + -e 's|@arch@|$(host_arch)|'\ $< > $@ $(AT)touch $@ diff --git a/contrib/depends/packages/boost.mk b/contrib/depends/packages/boost.mk index 9d3dda74ac4..89db5a41528 100644 --- a/contrib/depends/packages/boost.mk +++ b/contrib/depends/packages/boost.mk @@ -1,8 +1,8 @@ -package=boost -$(package)_version=1_58_0 -$(package)_download_path=https://sourceforge.net/projects/boost/files/boost/1.58.0 -$(package)_file_name=$(package)_$($(package)_version).tar.gz -$(package)_sha256_hash=a004d9b3fa95e956383693b86fce1b68805a6f71c2e68944fa813de0fb8c8102 +package=boost +$(package)_version=1_64_0 +$(package)_download_path=https://dl.bintray.com/boostorg/release/1.64.0/source/ +$(package)_file_name=$(package)_$($(package)_version).tar.bz2 +$(package)_sha256_hash=7bcc5caace97baa948931d712ea5f37038dbb1c5d89b43ad4def4ed7cb683332 define $(package)_set_vars $(package)_config_opts_release=variant=release @@ -19,13 +19,13 @@ $(package)_toolset_$(host_os)=gcc $(package)_archiver_$(host_os)=$($(package)_ar) $(package)_toolset_darwin=darwin $(package)_archiver_darwin=$($(package)_libtool) -$(package)_config_libraries=system,filesystem,thread,date_time,chrono,regex,serialization,program_options,locale +$(package)_config_libraries=chrono,filesystem,program_options,system,thread,test,date_time,regex,serialization,locale $(package)_cxxflags=-std=c++11 -fvisibility=hidden $(package)_cxxflags_linux=-fPIC endef define $(package)_preprocess_cmds - echo "using $(boost_toolset_$(host_os)) : : $($(package)_cxx) : \"$($(package)_cxxflags) $($(package)_cppflags)\" \"$($(package)_ldflags)\" \"$(boost_archiver_$(host_os))\" \"$(host_STRIP)\" \"$(host_RANLIB)\" \"$(host_WINDRES)\" : ;" > user-config.jam + echo "using $(boost_toolset_$(host_os)) : : $($(package)_cxx) : \"$($(package)_cxxflags) $($(package)_cppflags)\" \"$($(package)_ldflags)\" \"$(boost_archiver_$(host_os))\" \"$(host_STR IP)\" \"$(host_RANLIB)\" \"$(host_WINDRES)\" : ;" > user-config.jam endef define $(package)_config_cmds diff --git a/contrib/depends/packages/packages.mk b/contrib/depends/packages/packages.mk index 1aca0609665..af92191bb14 100644 --- a/contrib/depends/packages/packages.mk +++ b/contrib/depends/packages/packages.mk @@ -1,4 +1,4 @@ -packages:=boost openssl libevent zeromq cppzmq zlib expat ldns unbound cppzmq readline libiconv icu4c +packages:=boost openssl libevent zeromq cppzmq zlib expat ldns unbound cppzmq readline libiconv native_packages := native_ccache wallet_packages=bdb @@ -13,6 +13,9 @@ endif ifeq ($(host_os),darwin11) package += unwind endif +ifeq ($(host_os),mingw32) +packages += icu4c +endif ifneq ($(build_os),darwin) darwin_native_packages += native_cctools native_cdrkit native_libdmg-hfsplus diff --git a/contrib/depends/packages/unwind.mk b/contrib/depends/packages/unwind.mk index 06e14211586..e1bcb35b2d5 100644 --- a/contrib/depends/packages/unwind.mk +++ b/contrib/depends/packages/unwind.mk @@ -5,7 +5,9 @@ $(package)_file_name=lib$(package)-$($(package)_version).tar.gz $(package)_sha256_hash=1de38ffbdc88bd694d10081865871cd2bfbb02ad8ef9e1606aee18d65532b992 define $(package)_config_cmds - $($(package)_autoconf) --disable-shared --enable-static + cp -f $(BASEDIR)/config.guess config/config.guess &&\ + cp -f $(BASEDIR)/config.sub config/config.sub &&\ + $($(package)_autoconf) --disable-shared --enable-static endef define $(package)_build_cmds diff --git a/contrib/depends/toolchain.cmake.in b/contrib/depends/toolchain.cmake.in index eb4fcba04e2..06217363c5a 100644 --- a/contrib/depends/toolchain.cmake.in +++ b/contrib/depends/toolchain.cmake.in @@ -1,12 +1,6 @@ -# this one is important +# Set the system name, either Darwin, Linux, or Windows SET(CMAKE_SYSTEM_NAME @depends@) - -#this one not so much -SET(CMAKE_SYSTEM_VERSION 1) - -# specify the cross compiler -SET(CMAKE_C_COMPILER @CC@) -SET(CMAKE_CXX_COMPILER @CXX@) +SET(CMAKE_BUILD_TYPE release) SET(STATIC true) SET(UNBOUND_STATIC true) @@ -14,6 +8,8 @@ SET(UNBOUND_STATIC true) # where is the target environment SET(CMAKE_FIND_ROOT_PATH @prefix@ /usr) +SET(ENV{PKG_CONFIG_PATH} @prefix@/lib/pkgconfig) + SET(THREADS_PTHREAD_ARG "2" CACHE STRING "Forcibly set by CMakeLists.txt." FORCE) SET(Readline_ROOT_DIR @prefix@) @@ -28,18 +24,57 @@ SET(UNBOUND_LIBRARY @prefix@/lib/libunbound.a) SET(ZMQ_INCLUDE_PATH @prefix@/include) SET(ZMQ_LIB @prefix@/lib/libzmq.a) -#SET(BOOST_IGNORE_SYSTEM_PATHS_DEFAULT ON) -#SET(BOOST_IGNORE_SYSTEM_PATH ON) -#SET(BOOST_ROOT @prefix@) -#SET(BOOST_LIBRARYDIR @prefix@/lib) +SET(BOOST_IGNORE_SYSTEM_PATHS_DEFAULT ON) +SET(BOOST_IGNORE_SYSTEM_PATH ON) +SET(BOOST_ROOT @prefix@) +SET(BOOST_LIBRARYDIR @prefix@/lib) +SET(Boost_COMPILER "-clang") +SET(BOOST_IGNORE_SYSTEM_PATHS_DEFAULT OFF) +SET(Boost_NO_SYSTEM_PATHS TRUE) +SET(Boost_USE_STATIC_LIBS TRUE) +SET(Boost_USE_STATIC_RUNTIME TRUE) + +SET(OpenSSL_DIR @prefix@/lib) # for libraries and headers in the target directories set (CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) # Find programs on host set (CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) # Find libs in target set (CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) # Find includes in target -#set (MINGW_FLAG "-m64") -#set (USE_LTO_DEFAULT false) +# specify the cross compiler to be used. Darwin uses clang provided by the SDK. +if(CMAKE_SYSTEM_NAME STREQUAL "Darwin") + SET(CMAKE_C_COMPILER @prefix@/native/bin/clang) + SET(CMAKE_C_COMPILER_TARGET x86_64-apple-darwin11) + SET(CMAKE_CXX_COMPILER @prefix@/native/bin/clang++ -stdlib=libc++) + SET(CMAKE_CXX_COMPILER_TARGET x86_64-apple-darwin11) + SET(_CMAKE_TOOLCHAIN_PREFIX x86_64-apple-darwin11-) + SET(APPLE True) + SET(BUILD_TAG "mac-x64") + SET(BUILD_64 ON) + SET(ARCH "x86_64") + SET(BREW OFF) + SET(PORT OFF) + SET(CMAKE_OSX_SYSROOT "@sdk@/MacOSX10.11.sdk/") + SET(CMAKE_OSX_DEPLOYMENT_TARGET "10.08") + SET(CMAKE_CXX_STANDARD 11) + SET(CMAKE_OSX_ARCHITECTURES "x86_64") + SET(LLVM_ENABLE_PIC OFF) + SET(LLVM_ENABLE_PIE OFF) +else() + SET(CMAKE_C_COMPILER @CC@) + SET(CMAKE_CXX_COMPILER @CXX@) +endif() + +set(ARCH @arch@) + +if(ARCH STREQUAL "arm") + set(ARCH "armv6zk") + set(ARMID "armv6zk") + set(BUILD_64 OFF) + set(CMAKE_BUILD_TYPE release) + set(BUILD_TAG "linux-armv6") + set(ARM6) +endif() -#Create a new cmake flag that indicates building with depends +#Create a new global cmake flag that indicates building with depends set (DEPENDS true) diff --git a/src/blockchain_utilities/CMakeLists.txt b/src/blockchain_utilities/CMakeLists.txt index 338ec3e4b12..bc2ea6876be 100644 --- a/src/blockchain_utilities/CMakeLists.txt +++ b/src/blockchain_utilities/CMakeLists.txt @@ -28,7 +28,9 @@ set(blocksdat "") if(PER_BLOCK_CHECKPOINT) - if(APPLE) + if(APPLE AND DEPENDS) + add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && touch stub.c && ${CMAKE_C_COMPILER} --target=x86_64-apple-darwin11 -o stub.o -c stub.c COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} -r -sectcreate __DATA __blocks_dat ../blocks/checkpoints.dat -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o stub.o && rm -f stub.*) + elseif(APPLE AND NOT DEPENDS) add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && touch stub.c && ${CMAKE_C_COMPILER} -o stub.o -c stub.c COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} ${LD_RAW_FLAGS} -r -sectcreate __DATA __blocks_dat ../blocks/checkpoints.dat -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o stub.o && rm -f stub.*) else() add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && cp ../blocks/checkpoints.dat blocks.dat && ${CMAKE_LINKER} ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o blocks.dat && rm -f blocks.dat) diff --git a/src/checkpoints/CMakeLists.txt b/src/checkpoints/CMakeLists.txt index 02bb2891a32..71500652247 100644 --- a/src/checkpoints/CMakeLists.txt +++ b/src/checkpoints/CMakeLists.txt @@ -27,9 +27,13 @@ # THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. if(APPLE) - find_library(IOKIT_LIBRARY IOKit) - mark_as_advanced(IOKIT_LIBRARY) - list(APPEND EXTRA_LIBRARIES ${IOKIT_LIBRARY}) + if(DEPENDS) + list(APPEND EXTRA_LIBRARIES "-framework Foundation -framework ApplicationServices -framework AppKit -framework IOKit") + else() + find_library(IOKIT_LIBRARY IOKit) + mark_as_advanced(IOKIT_LIBRARY) + list(APPEND EXTRA_LIBRARIES ${IOKIT_LIBRARY}) + endif() endif() set(checkpoints_sources diff --git a/src/cryptonote_basic/CMakeLists.txt b/src/cryptonote_basic/CMakeLists.txt index d50a9df6790..21445959d9f 100644 --- a/src/cryptonote_basic/CMakeLists.txt +++ b/src/cryptonote_basic/CMakeLists.txt @@ -27,9 +27,13 @@ # THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. if(APPLE) - find_library(IOKIT_LIBRARY IOKit) - mark_as_advanced(IOKIT_LIBRARY) - list(APPEND EXTRA_LIBRARIES ${IOKIT_LIBRARY}) + if(DEPENDS) + list(APPEND EXTRA_LIBRARIES "-framework Foundation -framework ApplicationServices -framework AppKit -framework IOKit") + else() + find_library(IOKIT_LIBRARY IOKit) + mark_as_advanced(IOKIT_LIBRARY) + list(APPEND EXTRA_LIBRARIES ${IOKIT_LIBRARY}) + endif() endif() set(cryptonote_basic_sources diff --git a/src/daemon/CMakeLists.txt b/src/daemon/CMakeLists.txt index 84004c3c619..b1c4b711dc9 100644 --- a/src/daemon/CMakeLists.txt +++ b/src/daemon/CMakeLists.txt @@ -28,7 +28,9 @@ set(blocksdat "") if(PER_BLOCK_CHECKPOINT) - if(APPLE) + if(APPLE AND DEPENDS) + add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && touch stub.c && ${CMAKE_C_COMPILER} --target=x86_64-apple-darwin11 -o stub.o -c stub.c COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} -r -sectcreate __DATA __blocks_dat ../blocks/checkpoints.dat -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o stub.o && rm -f stub.*) + elseif(APPLE AND NOT DEPENDS) add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && touch stub.c && ${CMAKE_C_COMPILER} -o stub.o -c stub.c COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} ${LD_RAW_FLAGS} -r -sectcreate __DATA __blocks_dat ../blocks/checkpoints.dat -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o stub.o && rm -f stub.*) else() add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && cp ../blocks/checkpoints.dat blocks.dat && ${CMAKE_LINKER} ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o blocks.dat && rm -f blocks.dat) From d6b9bdd322f4b0318d8d5a2c54b3f1617e0dfe54 Mon Sep 17 00:00:00 2001 From: TheCharlatan Date: Sun, 18 Mar 2018 15:50:41 +0100 Subject: [PATCH 0329/1404] Update readmes to reflect the usage of depends Explain the role of the SDK in the darwin build. Add instructions to compile depends to the basic readme. --- README.md | 18 +++++++++++++++++- contrib/depends/README.md | 7 +++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 637be8ed720..2768d580d2f 100644 --- a/README.md +++ b/README.md @@ -477,7 +477,7 @@ Then you can run make as usual. # Get binaries docker cp monero-android:/opt/android/monero/build/release/bin . -### Building portable statically linked binaries +### Building portable statically linked binaries (Cross Compiling) By default, in either dynamically or statically linked builds, binaries target the specific host processor on which the build happens and are not portable to other processors. Portable binaries can be built using the following targets: @@ -532,6 +532,22 @@ Installing a snap is very quick. Snaps are secure. They are isolated with all of Packaging for your favorite distribution would be a welcome contribution! +You can also cross-compile binaries on linux for windows and macos with the depends system. Go to contrib/depends and type: + +* ```make HOST=x86_64-linux-gnu``` for 64-bit linux binaries. +* ```make HOST=x86_64-w64-mingw32``` for 64-bit windows binaries. Requires: python3 nsis g++-mingw-w64-x86-64 wine1.6 bc +* ```make HOST=x86_64-apple-darwin11``` for darwin binaries. Requires: cmake imagemagick libcap-dev librsvg2-bin libz-dev libbz2-dev libtiff-tools python-dev +* ```make HOST=i686-linux-gnu``` for 32-bit linux binaries. Requires: g++-multilib bc +* ```make HOST=i686-w64-mingw32``` for 32-bit windows binaries. Requires: python3 nsis g++-mingw-w64-i686 +* ```make HOST=arm-linux-gnueabihf``` for armv6 binaries. Requires: g++-arm-linux-gnueabihf + +The required packages are the names for each toolchain on apt. Depending on your distro, they may have different names. +Then go back to the source dir and type for example for windows 64bit: + +* ```cmake -DCMAKE_TOOLCHAIN_FILE=`pwd`/contrib/depends/x86_64-w64-mingw32``` + +Using depends might also be easier to compile monero on windows than using msys. Activate windows subsystem for linux (for example ubuntu) install the apt build-essentials and follow the depends steps as depicted above. + ## Running monerod The build places the binary in `bin/` sub-directory within the build directory diff --git a/contrib/depends/README.md b/contrib/depends/README.md index 6053c531b46..dd28245695a 100644 --- a/contrib/depends/README.md +++ b/contrib/depends/README.md @@ -52,6 +52,13 @@ Additional targets: download-win: run 'make download-win' to fetch all sources needed for win builds download-linux: run 'make download-linux' to fetch all sources needed for linux builds +#Darwin (macos) builds: + +To build with the x86_64-apple-darwin11 you require the mac os developer tools in MacOSX10.11.sdk. +Download it from apple, or search for it on github. Create a new directoty called SDKs in this +directory and place the entire MacOSX10.11.sdk folder in it. The depends build will then pick it up automatically +(without requiring SDK_PATH). + ### Other documentation - [description.md](description.md): General description of the depends system From 5f7da005a3897184e9395c054da2ad8bed3e56bb Mon Sep 17 00:00:00 2001 From: TheCharlatan Date: Tue, 20 Mar 2018 03:56:11 +0100 Subject: [PATCH 0330/1404] Unbound is now a submodule. Adapt depends for this. Fix builds for native linux and windows The architecture flag was set incorrectly. It needs to be set only when compiling arm6. --- CMakeLists.txt | 2 +- contrib/depends/toolchain.cmake.in | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b1fea6899f6..771b349495d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -487,7 +487,7 @@ if(MSVC) include_directories(SYSTEM src/platform/msc) else() include(TestCXXAcceptsFlag) - if (NOT DEPENDS) + if (NOT ARM6) set(ARCH native CACHE STRING "CPU to build for: -march value or 'default' to not pass -march at all") endif() message(STATUS "Building on ${CMAKE_SYSTEM_PROCESSOR} for ${ARCH}") diff --git a/contrib/depends/toolchain.cmake.in b/contrib/depends/toolchain.cmake.in index 06217363c5a..f76d7d1ea1e 100644 --- a/contrib/depends/toolchain.cmake.in +++ b/contrib/depends/toolchain.cmake.in @@ -65,8 +65,6 @@ else() SET(CMAKE_CXX_COMPILER @CXX@) endif() -set(ARCH @arch@) - if(ARCH STREQUAL "arm") set(ARCH "armv6zk") set(ARMID "armv6zk") From cfb3046222ba2bf42732bb9806906a78bc11a49c Mon Sep 17 00:00:00 2001 From: TheCharlatan Date: Wed, 25 Apr 2018 18:19:05 +0200 Subject: [PATCH 0331/1404] Add Miniupnp submodule Drop miniupnp and unbound depends builds. Make sure that build variables are propageted properly to unbound and miniupnp. Rebase to after the v0.12 release --- cmake/FindPCSC.cmake | 3 +- contrib/depends/packages/cppzmq.mk | 12 -------- contrib/depends/packages/packages.mk | 8 ++--- contrib/depends/packages/pcsc-lite.mk | 2 +- contrib/depends/toolchain.cmake.in | 7 ++--- external/CMakeLists.txt | 44 ++++++++------------------- 6 files changed, 21 insertions(+), 55 deletions(-) diff --git a/cmake/FindPCSC.cmake b/cmake/FindPCSC.cmake index b5e8420e664..0819196558c 100644 --- a/cmake/FindPCSC.cmake +++ b/cmake/FindPCSC.cmake @@ -46,8 +46,9 @@ FIND_PACKAGE_HANDLE_STANDARD_ARGS(PCSC DEFAULT_MSG PCSC_LIBRARY PCSC_INCLUDE_DIR IF(PCSC_FOUND) SET( PCSC_LIBRARIES ${PCSC_LIBRARY} ) + SET(PCSC_STATIC_LIBRARIES ${PCSC_STATIC_LIBRARY}) ELSE(PCSC_FOUND) SET( PCSC_LIBRARIES ) ENDIF(PCSC_FOUND) -MARK_AS_ADVANCED( PCSC_LIBRARY PCSC_INCLUDE_DIR ) +MARK_AS_ADVANCED( PCSC_LIBRARY PCSC_INCLUDE_DIR PCSC_STATIC_LIBRARY) diff --git a/contrib/depends/packages/cppzmq.mk b/contrib/depends/packages/cppzmq.mk index 9ff5a5ab042..6c0faf08432 100644 --- a/contrib/depends/packages/cppzmq.mk +++ b/contrib/depends/packages/cppzmq.mk @@ -5,19 +5,7 @@ $(package)_file_name=v$($(package)_version).tar.gz $(package)_sha256_hash=3e6b57bf49115f4ae893b1ff7848ead7267013087dc7be1ab27636a97144d373 $(package)_dependencies=zeromq -define $(package)_config_cmds - echo $(build_pefix) &&\ - cmake -DCMAKE_INSTALL_PREFIX=$(build_prefix)/../ -DCMAKE_PREFIX_PATH=$(build_prefix)/../include -DZeroMQ_STATIC_LIBRARY=$(build_prefix)/lib -DPC_LIBZMQ_LIBDIR=$(build_prefix)/lib -DPC_LIBZMQ_LIBRARY_DIRS=$(build_prefix)/lib CMakeLists.txt -endef - -define $(package)_build_cmds - $(MAKE) -endef - define $(package)_stage_cmds - $(MAKE) DESTDIR=$($(package)_staging_dir) &&\ - echo $(host_prefix)/include/ &&\ - echo $($(package)_staging_prefix_dir) &&\ mkdir $($(package)_staging_prefix_dir)/include &&\ cp zmq.hpp $($(package)_staging_prefix_dir)/include endef diff --git a/contrib/depends/packages/packages.mk b/contrib/depends/packages/packages.mk index af92191bb14..604b5017fb1 100644 --- a/contrib/depends/packages/packages.mk +++ b/contrib/depends/packages/packages.mk @@ -1,18 +1,14 @@ -packages:=boost openssl libevent zeromq cppzmq zlib expat ldns unbound cppzmq readline libiconv +packages:=boost openssl libevent zeromq cppzmq zlib expat ldns cppzmq readline libiconv native_packages := native_ccache wallet_packages=bdb -upnp_packages=miniupnpc - darwin_native_packages = native_biplist native_ds_store native_mac_alias ifeq ($(host_os),linux) +packages += pcsc-lite packages += unwind endif -ifeq ($(host_os),darwin11) -package += unwind -endif ifeq ($(host_os),mingw32) packages += icu4c endif diff --git a/contrib/depends/packages/pcsc-lite.mk b/contrib/depends/packages/pcsc-lite.mk index a24442eb99c..f568db773ec 100644 --- a/contrib/depends/packages/pcsc-lite.mk +++ b/contrib/depends/packages/pcsc-lite.mk @@ -8,7 +8,7 @@ define $(package)_set_vars $(package)_build_opts=CC="$($(package)_cc)" $(package)_config_env=AR="$($(package)_ar)" RANLIB="$($(package)_ranlib)" CC="$($(package)_cc)" $(package)_config_opts=--prefix=$(host_prefix) - $(package)_config_opts_release=--disable-debug-mode --disable-libsystemd --enable-static + $(package)_config_opts_release=--disable-debug-mode --disable-libsystemd --disable-libudev --enable-static --disable-shared --disable-libusb $(package)_build_opts=CFLAGS="$($(package)_cflags) $($(package)_cppflags) -fPIC" endef diff --git a/contrib/depends/toolchain.cmake.in b/contrib/depends/toolchain.cmake.in index f76d7d1ea1e..6c708f43980 100644 --- a/contrib/depends/toolchain.cmake.in +++ b/contrib/depends/toolchain.cmake.in @@ -17,9 +17,9 @@ SET(LIBUNWIND_INCLUDE_DIR @prefix@/include) SET(LIBUNWIND_LIBRARIES @prefix@/lib/libunwind.a) SET(LIBUNWIND_LIBRARY_DIRS @prefix@/lib) -SET(UNBOUND_INCLUDE_DIR @prefix@/include) -SET(UNBOUND_LIBRARIES @prefix@/lib) -SET(UNBOUND_LIBRARY @prefix@/lib/libunbound.a) +#SET(UNBOUND_INCLUDE_DIR @prefix@/include) +#SET(UNBOUND_LIBRARIES @prefix@/lib) +#SET(UNBOUND_LIBRARY @prefix@/lib/libunbound.a) SET(ZMQ_INCLUDE_PATH @prefix@/include) SET(ZMQ_LIB @prefix@/lib/libzmq.a) @@ -28,7 +28,6 @@ SET(BOOST_IGNORE_SYSTEM_PATHS_DEFAULT ON) SET(BOOST_IGNORE_SYSTEM_PATH ON) SET(BOOST_ROOT @prefix@) SET(BOOST_LIBRARYDIR @prefix@/lib) -SET(Boost_COMPILER "-clang") SET(BOOST_IGNORE_SYSTEM_PATHS_DEFAULT OFF) SET(Boost_NO_SYSTEM_PATHS TRUE) SET(Boost_USE_STATIC_LIBS TRUE) diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt index 4757b2a3b23..25021fe9e79 100644 --- a/external/CMakeLists.txt +++ b/external/CMakeLists.txt @@ -37,18 +37,12 @@ find_package(Miniupnpc REQUIRED) -if(DEPENDS) - message(STATUS "Using static depends miniupnpc found at ${MINIUPNP_LIBRARIES}") - set(UPNP_STATIC true PARENT_SCOPE) - set(UPNP_LIBRARIES ${MINIUPNP_LIBRARY} PARENT_SCOPE) -else() - message(STATUS "Using in-tree miniupnpc") - add_subdirectory(miniupnp/miniupnpc) - set_property(TARGET libminiupnpc-static PROPERTY FOLDER "external") -endif() +message(STATUS "Using in-tree miniupnpc") +add_subdirectory(miniupnp/miniupnpc) +set_property(TARGET libminiupnpc-static PROPERTY FOLDER "external") if(MSVC) set_property(TARGET libminiupnpc-static APPEND_STRING PROPERTY COMPILE_FLAGS " -wd4244 -wd4267") -elseif(NOT MSVC AND NOT DEPENDS) +elseif(NOT MSVC) set_property(TARGET libminiupnpc-static APPEND_STRING PROPERTY COMPILE_FLAGS " -Wno-undef -Wno-unused-result -Wno-unused-value") endif() @@ -56,7 +50,7 @@ set(UPNP_LIBRARIES "libminiupnpc-static" PARENT_SCOPE) find_package(Unbound) -if(NOT UNBOUND_INCLUDE_DIR OR STATIC AND NOT DEPENDS) +if(NOT UNBOUND_INCLUDE_DIR OR STATIC) # NOTE: If STATIC is true, CMAKE_FIND_LIBRARY_SUFFIXES has been reordered. # unbound has config tests which used OpenSSL libraries, so -ldl may need to # be set in this case. @@ -69,27 +63,15 @@ if(NOT UNBOUND_INCLUDE_DIR OR STATIC AND NOT DEPENDS) set(UNBOUND_LIBRARY "unbound" PARENT_SCOPE) set(UNBOUND_LIBRARY_DIRS "${LIBEVENT2_LIBDIR}" PARENT_SCOPE) else() - if(DEPENDS) - message(STATUS "Found libunbound include (unbound.h) in ${UNBOUND_INCLUDE_DIR}") - if(UNBOUND_LIBRARIES) - message(STATUS "Found libunbound library in ${UNBOUND_LIBRARIES}") - set(UNBOUND_STATIC true PARENT_SCOPE) - set(UNBOUND_LIBRARY ${UNBOUND_LIBRARIES} PARENTSCOPE) - set(UNBOUND_LIBRARY_DIRS "" PARENT_SCOPE) - else() - die("Found libunbound includes, but could not find libunbound library. Please make sure you have installed libunbound or libunbound-dev or the equivalent") - endif() + message(STATUS "Found libunbound include (unbound.h) in ${UNBOUND_INCLUDE_DIR}") + if(UNBOUND_LIBRARIES) + message(STATUS "Found libunbound shared library") + set(UNBOUND_STATIC false PARENT_SCOPE) + set(UNBOUND_INCLUDE ${UNBOUND_INCLUDE_DIR} PARENT_SCOPE) + set(UNBOUND_LIBRARY ${UNBOUND_LIBRARIES} PARENT_SCOPE) + set(UNBOUND_LIBRARY_DIRS "" PARENT_SCOPE) else() - message(STATUS "Found libunbound include (unbound.h) in ${UNBOUND_INCLUDE_DIR}") - if(UNBOUND_LIBRARIES) - message(STATUS "Found libunbound shared library") - set(UNBOUND_STATIC false PARENT_SCOPE) - set(UNBOUND_INCLUDE ${UNBOUND_INCLUDE_DIR} PARENT_SCOPE) - set(UNBOUND_LIBRARY ${UNBOUND_LIBRARIES} PARENT_SCOPE) - set(UNBOUND_LIBRARY_DIRS "" PARENT_SCOPE) - else() - die("Found libunbound includes, but could not find libunbound library. Please make sure you have installed libunbound or libunbound-dev or the equivalent") - endif() + die("Found libunbound includes, but could not find libunbound library. Please make sure you have installed libunbound or libunbound-dev or the equivalent") endif() endif() From f0ba19fde558b2ef8ac80132f163de3d0750869f Mon Sep 17 00:00:00 2001 From: TheCharlatan Date: Wed, 2 May 2018 17:11:02 +0200 Subject: [PATCH 0332/1404] Add lrelease to the depends This includes a minimal qt build without gui --- CMakeLists.txt | 12 +- contrib/depends/packages/packages.mk | 2 +- contrib/depends/packages/qt.mk | 150 ++++++++++++++++++ .../depends/patches/qt/fix_qt_pkgconfig.patch | 11 ++ .../depends/patches/qt/pidlist_absolute.patch | 37 +++++ .../depends/patches/qt/qfixed-coretext.patch | 34 ++++ contrib/depends/toolchain.cmake.in | 7 +- translations/CMakeLists.txt | 9 +- 8 files changed, 253 insertions(+), 9 deletions(-) create mode 100644 contrib/depends/packages/qt.mk create mode 100644 contrib/depends/patches/qt/fix_qt_pkgconfig.patch create mode 100644 contrib/depends/patches/qt/pidlist_absolute.patch create mode 100644 contrib/depends/patches/qt/qfixed-coretext.patch diff --git a/CMakeLists.txt b/CMakeLists.txt index 771b349495d..59584a93ee3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -441,8 +441,18 @@ add_definitions(-DAUTO_INITIALIZE_EASYLOGGINGPP) # Generate header for embedded translations add_subdirectory(translations) +#======= +## Generate header for embedded translations, use target toolchain if depends, otherwise use the +## lrelease and lupdate binaries from the host +#include(ExternalProject) +#ExternalProject_Add(generate_translations_header +# SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/translations" +# BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/translations" +# STAMP_DIR ${LRELEASE_PATH} +# CMAKE_ARGS -DLRELEASE_PATH=${LRELEASE_PATH} +# INSTALL_COMMAND cmake -E echo "") +#>>>>>>> b1f43170... Add lrelease to the depends include_directories("${CMAKE_CURRENT_BINARY_DIR}/translations") - add_subdirectory(external) # Final setup for libunbound diff --git a/contrib/depends/packages/packages.mk b/contrib/depends/packages/packages.mk index 604b5017fb1..d6d6b2d442c 100644 --- a/contrib/depends/packages/packages.mk +++ b/contrib/depends/packages/packages.mk @@ -1,4 +1,4 @@ -packages:=boost openssl libevent zeromq cppzmq zlib expat ldns cppzmq readline libiconv +packages:=boost openssl libevent zeromq cppzmq zlib expat ldns cppzmq readline libiconv qt native_packages := native_ccache wallet_packages=bdb diff --git a/contrib/depends/packages/qt.mk b/contrib/depends/packages/qt.mk new file mode 100644 index 00000000000..32ca4a84c5c --- /dev/null +++ b/contrib/depends/packages/qt.mk @@ -0,0 +1,150 @@ +PACKAGE=qt +$(package)_version=5.7.1 +$(package)_download_path=http://download.qt.io/official_releases/qt/5.7/$($(package)_version)/submodules +$(package)_suffix=opensource-src-$($(package)_version).tar.gz +$(package)_file_name=qtbase-$($(package)_suffix) +$(package)_sha256_hash=95f83e532d23b3ddbde7973f380ecae1bac13230340557276f75f2e37984e410 +$(package)_dependencies=openssl zlib +$(package)_build_subdir=qtbase +$(package)_qt_libs=corelib +$(package)_patches=pidlist_absolute.patch fix_qt_pkgconfig.patch qfixed-coretext.patch + +$(package)_qttranslations_file_name=qttranslations-$($(package)_suffix) +$(package)_qttranslations_sha256_hash=3a15aebd523c6d89fb97b2d3df866c94149653a26d27a00aac9b6d3020bc5a1d + +$(package)_qttools_file_name=qttools-$($(package)_suffix) +$(package)_qttools_sha256_hash=22d67de915cb8cd93e16fdd38fa006224ad9170bd217c2be1e53045a8dd02f0f + +$(package)_extra_sources = $($(package)_qttranslations_file_name) +$(package)_extra_sources += $($(package)_qttools_file_name) + +define $(package)_set_vars +$(package)_config_opts_release = -release +$(package)_config_opts_debug = -debug +$(package)_config_opts += -bindir $(build_prefix)/bin +$(package)_config_opts += -c++std c++11 +$(package)_config_opts += -confirm-license +$(package)_config_opts += -dbus-runtime +$(package)_config_opts += -no-alsa +$(package)_config_opts += -no-audio-backend +$(package)_config_opts += -no-cups +$(package)_config_opts += -no-egl +$(package)_config_opts += -no-eglfs +$(package)_config_opts += -no-feature-style-windowsmobile +$(package)_config_opts += -no-feature-style-windowsce +$(package)_config_opts += -no-freetype +$(package)_config_opts += -no-gif +$(package)_config_opts += -no-glib +$(package)_config_opts += -no-gstreamer +$(package)_config_opts += -no-icu +$(package)_config_opts += -no-iconv +$(package)_config_opts += -no-kms +$(package)_config_opts += -no-linuxfb +$(package)_config_opts += -no-libudev +$(package)_config_opts += -no-mitshm +$(package)_config_opts += -no-mtdev +$(package)_config_opts += -no-pulseaudio +$(package)_config_opts += -no-openvg +$(package)_config_opts += -no-reduce-relocations +$(package)_config_opts += -no-qml-debug +$(package)_config_opts += -no-sql-db2 +$(package)_config_opts += -no-sql-ibase +$(package)_config_opts += -no-sql-oci +$(package)_config_opts += -no-sql-tds +$(package)_config_opts += -no-sql-mysql +$(package)_config_opts += -no-sql-odbc +$(package)_config_opts += -no-sql-psql +$(package)_config_opts += -no-sql-sqlite +$(package)_config_opts += -no-sql-sqlite2 +$(package)_config_opts += -no-use-gold-linker +$(package)_config_opts += -no-xinput2 +$(package)_config_opts += -no-xrender +$(package)_config_opts += -nomake examples +$(package)_config_opts += -nomake tests +$(package)_config_opts += -opensource +$(package)_config_opts += -openssl-linked +$(package)_config_opts += -optimized-qmake +$(package)_config_opts += -pch +$(package)_config_opts += -pkg-config +$(package)_config_opts += -qt-libpng +$(package)_config_opts += -qt-libjpeg +$(package)_config_opts += -qt-pcre +$(package)_config_opts += -system-zlib +$(package)_config_opts += -reduce-exports +$(package)_config_opts += -static +$(package)_config_opts += -silent +$(package)_config_opts += -v +$(package)_config_opts += -no-feature-printer +$(package)_config_opts += -no-feature-printdialog +$(package)_config_opts += -no-gui +$(package)_config_opts += -no-freetype +$(package)_config_opts += -no-sm +$(package)_config_opts += -no-fontconfig +$(package)_config_opts += -no-opengl +$(package)_config_opts += -no-xkb +$(package)_config_opts += -no-xcb +$(package)_config_opts += -no-xshape +$(package)_build_env = QT_RCC_TEST=1 +endef + +define $(package)_fetch_cmds +$(call fetch_file,$(package),$($(package)_download_path),$($(package)_download_file),$($(package)_file_name),$($(package)_sha256_hash)) && \ +$(call fetch_file,$(package),$($(package)_download_path),$($(package)_qttranslations_file_name),$($(package)_qttranslations_file_name),$($(package)_qttranslations_sha256_hash)) && \ +$(call fetch_file,$(package),$($(package)_download_path),$($(package)_qttools_file_name),$($(package)_qttools_file_name),$($(package)_qttools_sha256_hash)) +endef + +define $(package)_extract_cmds + mkdir -p $($(package)_extract_dir) && \ + echo "$($(package)_sha256_hash) $($(package)_source)" > $($(package)_extract_dir)/.$($(package)_file_name).hash && \ + echo "$($(package)_qttranslations_sha256_hash) $($(package)_source_dir)/$($(package)_qttranslations_file_name)" >> $($(package)_extract_dir)/.$($(package)_file_name).hash && \ + echo "$($(package)_qttools_sha256_hash) $($(package)_source_dir)/$($(package)_qttools_file_name)" >> $($(package)_extract_dir)/.$($(package)_file_name).hash && \ + $(build_SHA256SUM) -c $($(package)_extract_dir)/.$($(package)_file_name).hash && \ + mkdir qtbase && \ + tar --strip-components=1 -xf $($(package)_source) -C qtbase && \ + mkdir qttranslations && \ + tar --strip-components=1 -xf $($(package)_source_dir)/$($(package)_qttranslations_file_name) -C qttranslations && \ + mkdir qttools && \ + tar --strip-components=1 -xf $($(package)_source_dir)/$($(package)_qttools_file_name) -C qttools +endef + + +define $(package)_preprocess_cmds + sed -i.old "s|updateqm.commands = \$$$$\$$$$LRELEASE|updateqm.commands = $($(package)_extract_dir)/qttools/bin/lrelease|" qttranslations/translations/translations.pro && \ + sed -i.old "/updateqm.depends =/d" qttranslations/translations/translations.pro && \ + patch -p1 < $($(package)_patch_dir)/pidlist_absolute.patch && \ + patch -p1 < $($(package)_patch_dir)/fix_qt_pkgconfig.patch && \ + patch -p1 < $($(package)_patch_dir)/qfixed-coretext.patch && \ + echo "!host_build: QMAKE_CFLAGS += $($(package)_cflags) $($(package)_cppflags)" >> qtbase/mkspecs/common/gcc-base.conf && \ + echo "!host_build: QMAKE_CXXFLAGS += $($(package)_cxxflags) $($(package)_cppflags)" >> qtbase/mkspecs/common/gcc-base.conf && \ + echo "!host_build: QMAKE_LFLAGS += $($(package)_ldflags)" >> qtbase/mkspecs/common/gcc-base.conf +endef + +define $(package)_config_cmds + export PKG_CONFIG_SYSROOT_DIR=/ && \ + export PKG_CONFIG_LIBDIR=$(host_prefix)/lib/pkgconfig && \ + export PKG_CONFIG_PATH=$(host_prefix)/share/pkgconfig && \ + ./configure $($(package)_config_opts) && \ + echo "host_build: QT_CONFIG ~= s/system-zlib/zlib" >> mkspecs/qconfig.pri && \ + echo "CONFIG += force_bootstrap" >> mkspecs/qconfig.pri && \ + $(MAKE) sub-src-clean && \ + cd ../qttranslations && ../qtbase/bin/qmake qttranslations.pro -o Makefile && \ + cd translations && ../../qtbase/bin/qmake translations.pro -o Makefile && cd ../.. &&\ + cd qttools/src/linguist/lrelease/ && ../../../../qtbase/bin/qmake lrelease.pro -o Makefile +endef + +define $(package)_build_cmds + $(MAKE) -C src $(addprefix sub-,$($(package)_qt_libs)) && \ + $(MAKE) -C ../qttools/src/linguist/lrelease && \ + $(MAKE) -C ../qttranslations +endef + +define $(package)_stage_cmds + $(MAKE) -C src INSTALL_ROOT=$($(package)_staging_dir) $(addsuffix -install_subtargets,$(addprefix sub-,$($(package)_qt_libs))) && cd .. &&\ + $(MAKE) -C qttools/src/linguist/lrelease INSTALL_ROOT=$($(package)_staging_dir) install_target && \ + $(MAKE) -C qttranslations INSTALL_ROOT=$($(package)_staging_dir) install_subtargets +endef + +define $(package)_postprocess_cmds + rm -rf native/mkspecs/ native/lib/ lib/cmake/ && \ + rm -f lib/lib*.la lib/*.prl plugins/*/*.prl +endef diff --git a/contrib/depends/patches/qt/fix_qt_pkgconfig.patch b/contrib/depends/patches/qt/fix_qt_pkgconfig.patch new file mode 100644 index 00000000000..34302a9f2d2 --- /dev/null +++ b/contrib/depends/patches/qt/fix_qt_pkgconfig.patch @@ -0,0 +1,11 @@ +--- old/qtbase/mkspecs/features/qt_module.prf ++++ new/qtbase/mkspecs/features/qt_module.prf +@@ -245,7 +245,7 @@ + load(qt_targets) + + # this builds on top of qt_common +-!internal_module:!lib_bundle:if(unix|mingw) { ++unix|mingw { + CONFIG += create_pc + QMAKE_PKGCONFIG_DESTDIR = pkgconfig + host_build: \ diff --git a/contrib/depends/patches/qt/pidlist_absolute.patch b/contrib/depends/patches/qt/pidlist_absolute.patch new file mode 100644 index 00000000000..c7928241790 --- /dev/null +++ b/contrib/depends/patches/qt/pidlist_absolute.patch @@ -0,0 +1,37 @@ +diff -dur old/qtbase/src/plugins/platforms/windows/qwindowscontext.h new/qtbase/src/plugins/platforms/windows/qwindowscontext.h +--- old/qtbase/src/plugins/platforms/windows/qwindowscontext.h ++++ new/qtbase/src/plugins/platforms/windows/qwindowscontext.h +@@ -136,10 +136,18 @@ + inline void init(); + + typedef HRESULT (WINAPI *SHCreateItemFromParsingName)(PCWSTR, IBindCtx *, const GUID&, void **); ++#if defined(Q_CC_MINGW) && (!defined(__MINGW64_VERSION_MAJOR) || __MINGW64_VERSION_MAJOR < 3) ++ typedef HRESULT (WINAPI *SHGetKnownFolderIDList)(const GUID &, DWORD, HANDLE, ITEMIDLIST **); ++#else + typedef HRESULT (WINAPI *SHGetKnownFolderIDList)(const GUID &, DWORD, HANDLE, PIDLIST_ABSOLUTE *); ++#endif + typedef HRESULT (WINAPI *SHGetStockIconInfo)(int , int , _SHSTOCKICONINFO *); + typedef HRESULT (WINAPI *SHGetImageList)(int, REFIID , void **); ++#if defined(Q_CC_MINGW) && (!defined(__MINGW64_VERSION_MAJOR) || __MINGW64_VERSION_MAJOR < 3) ++ typedef HRESULT (WINAPI *SHCreateItemFromIDList)(const ITEMIDLIST *, REFIID, void **); ++#else + typedef HRESULT (WINAPI *SHCreateItemFromIDList)(PCIDLIST_ABSOLUTE, REFIID, void **); ++#endif + + SHCreateItemFromParsingName sHCreateItemFromParsingName; + SHGetKnownFolderIDList sHGetKnownFolderIDList; +diff -dur old/qtbase/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp new/qtbase/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp +--- old/qtbase/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp ++++ new/qtbase/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp +@@ -1016,7 +1016,11 @@ + qWarning() << __FUNCTION__ << ": Invalid CLSID: " << url.path(); + return Q_NULLPTR; + } ++#if defined(Q_CC_MINGW) && (!defined(__MINGW64_VERSION_MAJOR) || __MINGW64_VERSION_MAJOR < 3) ++ ITEMIDLIST *idList; ++#else + PIDLIST_ABSOLUTE idList; ++#endif + HRESULT hr = QWindowsContext::shell32dll.sHGetKnownFolderIDList(uuid, 0, 0, &idList); + if (FAILED(hr)) { + qErrnoWarning("%s: SHGetKnownFolderIDList(%s)) failed", __FUNCTION__, qPrintable(url.toString())); diff --git a/contrib/depends/patches/qt/qfixed-coretext.patch b/contrib/depends/patches/qt/qfixed-coretext.patch new file mode 100644 index 00000000000..aa56f1e1de5 --- /dev/null +++ b/contrib/depends/patches/qt/qfixed-coretext.patch @@ -0,0 +1,34 @@ +From dbdd5f0ffbce52c8b789ed09f1aa3f1da6c02e23 Mon Sep 17 00:00:00 2001 +From: Gabriel de Dietrich +Date: Fri, 30 Mar 2018 11:58:16 -0700 +Subject: [PATCH] QCoreTextFontEngine: Fix build with Xcode 9.3 + +Apple LLVM version 9.1.0 (clang-902.0.39.1) + +Error message: + +.../qfontengine_coretext.mm:827:20: error: qualified reference to + 'QFixed' is a constructor name rather than a type in this context + return QFixed::QFixed(int(CTFontGetUnitsPerEm(ctfont))); + +Change-Id: Iebe26b3b087a16b10664208fc8851cbddb47f043 +Reviewed-by: Konstantin Ritt +--- + src/platformsupport/fontdatabases/mac/qfontengine_coretext.mm | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git old/qtbase/src/platformsupport/fontdatabases/mac/qfontengine_coretext.mm new/qtbase/src/platformsupport/fontdatabases/mac/qfontengine_coretext.mm +index 25ff69d877d..98b753eff96 100644 +--- old/qtbase/src/platformsupport/fontdatabases/mac/qfontengine_coretext.mm ++++ new/qtbase/src/platformsupport/fontdatabases/mac/qfontengine_coretext.mm +@@ -824,7 +824,7 @@ void QCoreTextFontEngine::getUnscaledGlyph(glyph_t glyph, QPainterPath *path, gl + + QFixed QCoreTextFontEngine::emSquareSize() const + { +- return QFixed::QFixed(int(CTFontGetUnitsPerEm(ctfont))); ++ return QFixed(int(CTFontGetUnitsPerEm(ctfont))); + } + + QFontEngine *QCoreTextFontEngine::cloneWithSize(qreal pixelSize) const +-- +2.16.3 \ No newline at end of file diff --git a/contrib/depends/toolchain.cmake.in b/contrib/depends/toolchain.cmake.in index 6c708f43980..5ab93b1c321 100644 --- a/contrib/depends/toolchain.cmake.in +++ b/contrib/depends/toolchain.cmake.in @@ -10,17 +10,14 @@ SET(CMAKE_FIND_ROOT_PATH @prefix@ /usr) SET(ENV{PKG_CONFIG_PATH} @prefix@/lib/pkgconfig) -SET(THREADS_PTHREAD_ARG "2" CACHE STRING "Forcibly set by CMakeLists.txt." FORCE) +SET(LRELEASE_PATH @prefix@/native/bin CACHE FILEPATH "path to lrelease" FORCE) + SET(Readline_ROOT_DIR @prefix@) SET(LIBUNWIND_INCLUDE_DIR @prefix@/include) SET(LIBUNWIND_LIBRARIES @prefix@/lib/libunwind.a) SET(LIBUNWIND_LIBRARY_DIRS @prefix@/lib) -#SET(UNBOUND_INCLUDE_DIR @prefix@/include) -#SET(UNBOUND_LIBRARIES @prefix@/lib) -#SET(UNBOUND_LIBRARY @prefix@/lib/libunbound.a) - SET(ZMQ_INCLUDE_PATH @prefix@/include) SET(ZMQ_LIB @prefix@/lib/libzmq.a) diff --git a/translations/CMakeLists.txt b/translations/CMakeLists.txt index fdd5014a740..dc7772c1d07 100644 --- a/translations/CMakeLists.txt +++ b/translations/CMakeLists.txt @@ -42,7 +42,12 @@ IF(NOT CMAKE_CROSSCOMPILING) add_executable(generate_translations_header generate_translations_header.c) ENDIF(NOT CMAKE_CROSSCOMPILING) -find_program(LRELEASE lrelease) +if(LRELEASE_PATH STREQUAL "") + find_program(LRELEASE lrelease) +else() + set(LRELEASE ${LRELEASE_PATH}/lrelease) +endif() + if(LRELEASE STREQUAL "LRELEASE-NOTFOUND") set(ts_files "") message(WARNING "lrelease program not found, translation files not built") @@ -68,7 +73,7 @@ string(REPLACE ".ts" ".qm" qm_files "${ts_files}") add_custom_command(TARGET generate_translations_header POST_BUILD - COMMAND generate_translations_header ${qm_files} + COMMAND ./generate_translations_header ${qm_files} WORKING_DIRECTORY "${CMAKE_CURRENT_BIN_DIR}" COMMENT "Generating embedded translations header") From db5715468afc50147c02fa7b7dd76746d3b00170 Mon Sep 17 00:00:00 2001 From: TheCharlatan Date: Mon, 10 Sep 2018 21:58:04 +0200 Subject: [PATCH 0333/1404] Updated pcsc url --- contrib/depends/packages/pcsc-lite.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/depends/packages/pcsc-lite.mk b/contrib/depends/packages/pcsc-lite.mk index f568db773ec..06be0e58589 100644 --- a/contrib/depends/packages/pcsc-lite.mk +++ b/contrib/depends/packages/pcsc-lite.mk @@ -1,6 +1,6 @@ package=pcsc-lite $(package)_version=1.8.23 -$(package)_download_path=https://alioth.debian.org/frs/download.php/file/4235 +$(package)_download_path=https://pcsclite.apdu.fr/files $(package)_file_name=$(package)-$($(package)_version).tar.bz2 $(package)_sha256_hash=5a27262586eff39cfd5c19aadc8891dd71c0818d3d629539bd631b958be689c9 From cbbf4d241b25166688ac3cf4a09c71f0383812f2 Mon Sep 17 00:00:00 2001 From: TheCharlatan Date: Mon, 10 Sep 2018 22:18:18 +0200 Subject: [PATCH 0334/1404] Adapt translations to upstream changes --- CMakeLists.txt | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 59584a93ee3..95b57e49d6f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -440,17 +440,17 @@ add_definition_if_function_found(strptime HAVE_STRPTIME) add_definitions(-DAUTO_INITIALIZE_EASYLOGGINGPP) # Generate header for embedded translations -add_subdirectory(translations) +#add_subdirectory(translations) #======= -## Generate header for embedded translations, use target toolchain if depends, otherwise use the -## lrelease and lupdate binaries from the host -#include(ExternalProject) -#ExternalProject_Add(generate_translations_header -# SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/translations" -# BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/translations" -# STAMP_DIR ${LRELEASE_PATH} -# CMAKE_ARGS -DLRELEASE_PATH=${LRELEASE_PATH} -# INSTALL_COMMAND cmake -E echo "") +# Generate header for embedded translations, use target toolchain if depends, otherwise use the +# lrelease and lupdate binaries from the host +include(ExternalProject) +ExternalProject_Add(generate_translations_header + SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/translations" + BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/translations" + STAMP_DIR ${LRELEASE_PATH} + CMAKE_ARGS -DLRELEASE_PATH=${LRELEASE_PATH} + INSTALL_COMMAND cmake -E echo "") #>>>>>>> b1f43170... Add lrelease to the depends include_directories("${CMAKE_CURRENT_BINARY_DIR}/translations") add_subdirectory(external) From cb1cc757ba5ff5eafe496062583d93e867e14e74 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 9 Jan 2018 13:57:01 +0000 Subject: [PATCH 0335/1404] performance_tests: don't override log level to 0 --- tests/performance_tests/main.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/performance_tests/main.cpp b/tests/performance_tests/main.cpp index 1733e3409b8..5fe8263b2eb 100644 --- a/tests/performance_tests/main.cpp +++ b/tests/performance_tests/main.cpp @@ -63,7 +63,6 @@ int main(int argc, char** argv) set_thread_high_priority(); mlog_configure(mlog_get_default_log_path("performance_tests.log"), true); - mlog_set_log_level(0); po::options_description desc_options("Command line options"); const command_line::arg_descriptor arg_filter = { "filter", "Regular expression filter for which tests to run" }; From aacfd6e3702810c43365658da61bb1afc7470fd5 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 3 Jan 2018 21:37:18 +0000 Subject: [PATCH 0336/1404] bulletproofs: multi-output bulletproofs --- src/ringct/bulletproofs.cc | 348 +++++++++++++++++++++++++++--- src/ringct/bulletproofs.h | 2 + src/ringct/rctTypes.h | 2 + tests/unit_tests/bulletproofs.cpp | 19 ++ 4 files changed, 339 insertions(+), 32 deletions(-) diff --git a/src/ringct/bulletproofs.cc b/src/ringct/bulletproofs.cc index fd15ffbc451..40d097f20c3 100644 --- a/src/ringct/bulletproofs.cc +++ b/src/ringct/bulletproofs.cc @@ -51,14 +51,16 @@ namespace rct { static rct::key vector_exponent(const rct::keyV &a, const rct::keyV &b); -static rct::keyV vector_powers(rct::key x, size_t n); +static rct::keyV vector_powers(const rct::key &x, size_t n); +static rct::keyV vector_dup(const rct::key &x, size_t n); static rct::key inner_product(const rct::keyV &a, const rct::keyV &b); static constexpr size_t maxN = 64; -static rct::key Hi[maxN], Gi[maxN]; -static ge_dsmp Gprecomp[64], Hprecomp[64]; +static constexpr size_t maxM = 16; +static rct::key Hi[maxN*maxM], Gi[maxN*maxM]; +static ge_dsmp Gprecomp[maxN*maxM], Hprecomp[maxN*maxM]; static const rct::key TWO = { {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 } }; -static const rct::keyV oneN = vector_powers(rct::identity(), maxN); +static const rct::keyV oneN = vector_dup(rct::identity(), maxN); static const rct::keyV twoN = vector_powers(TWO, maxN); static const rct::key ip12 = inner_product(oneN, twoN); static boost::mutex init_mutex; @@ -77,7 +79,7 @@ static void init_exponents() static bool init_done = false; if (init_done) return; - for (size_t i = 0; i < maxN; ++i) + for (size_t i = 0; i < maxN*maxM; ++i) { Hi[i] = get_exponent(rct::H, i * 2); rct::precomp(Hprecomp[i], Hi[i]); @@ -91,7 +93,7 @@ static void init_exponents() static rct::key vector_exponent(const rct::keyV &a, const rct::keyV &b) { CHECK_AND_ASSERT_THROW_MES(a.size() == b.size(), "Incompatible sizes of a and b"); - CHECK_AND_ASSERT_THROW_MES(a.size() <= maxN, "Incompatible sizes of a and maxN"); + CHECK_AND_ASSERT_THROW_MES(a.size() <= maxN*maxM, "Incompatible sizes of a and maxN"); rct::key res = rct::identity(); for (size_t i = 0; i < a.size(); ++i) { @@ -108,7 +110,7 @@ static rct::key vector_exponent_custom(const rct::keyV &A, const rct::keyV &B, c CHECK_AND_ASSERT_THROW_MES(A.size() == B.size(), "Incompatible sizes of A and B"); CHECK_AND_ASSERT_THROW_MES(a.size() == b.size(), "Incompatible sizes of a and b"); CHECK_AND_ASSERT_THROW_MES(a.size() == A.size(), "Incompatible sizes of a and A"); - CHECK_AND_ASSERT_THROW_MES(a.size() <= maxN, "Incompatible sizes of a and maxN"); + CHECK_AND_ASSERT_THROW_MES(a.size() <= maxN*maxM, "Incompatible sizes of a and maxN"); rct::key res = rct::identity(); for (size_t i = 0; i < a.size(); ++i) { @@ -145,7 +147,7 @@ static rct::key vector_exponent_custom(const rct::keyV &A, const rct::keyV &B, c } /* Given a scalar, construct a vector of powers */ -static rct::keyV vector_powers(rct::key x, size_t n) +static rct::keyV vector_powers(const rct::key &x, size_t n) { rct::keyV res(n); if (n == 0) @@ -232,6 +234,12 @@ static rct::keyV vector_scalar(const rct::keyV &a, const rct::key &x) return res; } +/* Create a vector from copies of a single value */ +static rct::keyV vector_dup(const rct::key &x, size_t N) +{ + return rct::keyV(N, x); +} + /* Exponentiate a curve vector by a scalar */ static rct::keyV vector_scalar2(const rct::keyV &a, const rct::key &x) { @@ -243,6 +251,17 @@ static rct::keyV vector_scalar2(const rct::keyV &a, const rct::key &x) return res; } +/* Get the sum of a vector's elements */ +static rct::key vector_sum(const rct::keyV &a) +{ + rct::key res = rct::zero(); + for (size_t i = 0; i < a.size(); ++i) + { + sc_add(res.bytes, res.bytes, a[i].bytes); + } + return res; +} + static rct::key switch_endianness(rct::key k) { std::reverse(k.bytes, k.bytes + sizeof(k)); @@ -405,7 +424,7 @@ Bulletproof bulletproof_PROVE(const rct::key &sv, const rct::key &gamma) const auto yN = vector_powers(y, N); - rct::key ip1y = inner_product(oneN, yN); + rct::key ip1y = vector_sum(yN); rct::key tmp; sc_muladd(t0.bytes, z.bytes, ip1y.bytes, t0.bytes); @@ -437,7 +456,7 @@ Bulletproof bulletproof_PROVE(const rct::key &sv, const rct::key &gamma) PERF_TIMER_START_BP(PROVE_step2); const auto HyNsR = hadamard(yN, sR); - const auto vpIz = vector_scalar(oneN, z); + const auto vpIz = vector_dup(z, N); const auto vp2zsq = vector_scalar(twoN, zsq); const auto aL_vpIz = vector_subtract(aL, vpIz); const auto aR_vpIz = vector_add(aR, vpIz); @@ -567,23 +586,284 @@ Bulletproof bulletproof_PROVE(uint64_t v, const rct::key &gamma) return bulletproof_PROVE(sv, gamma); } +/* Given a set of values v (0..2^N-1) and masks gamma, construct a range proof */ +Bulletproof bulletproof_PROVE(const rct::keyV &sv, const rct::keyV &gamma) +{ + CHECK_AND_ASSERT_THROW_MES(sv.size() == gamma.size(), "Incompatible sizes of sv and gamma"); + CHECK_AND_ASSERT_THROW_MES(!sv.empty(), "sv is empty"); + + init_exponents(); + + PERF_TIMER_UNIT(PROVE, 1000000); + + constexpr size_t logN = 6; // log2(64) + constexpr size_t N = 1< 0; ) + { + if (j >= sv.size()) + { + aL[j*N+i] = rct::zero(); + } + else if (sv[j][i/8] & (((uint64_t)1)<<(i%8))) + { + aL[j*N+i] = rct::identity(); + } + else + { + aL[j*N+i] = rct::zero(); + } + sc_sub(aR[j*N+i].bytes, aL[j*N+i].bytes, rct::identity().bytes); + } + } + PERF_TIMER_STOP(PROVE_aLaR); + + rct::key hash_cache = rct::hash_to_scalar(V); + + // DEBUG: Test to ensure this recovers the value +#ifdef DEBUG_BP + for (size_t j = 0; j < M; ++j) + { + uint64_t test_aL = 0, test_aR = 0; + for (size_t i = 0; i < N; ++i) + { + if (aL[j*N+i] == rct::identity()) + test_aL += ((uint64_t)1)<= (j-1)*N && i < j*N) + { + CHECK_AND_ASSERT_THROW_MES(1+j < zpow.size(), "invalid zpow index"); + CHECK_AND_ASSERT_THROW_MES(i-(j-1)*N < twoN.size(), "invalid twoN index"); + sc_muladd(zero_twos[i].bytes, zpow[1+j].bytes, twoN[i-(j-1)*N].bytes, zero_twos[i].bytes); + } + } + } + + rct::keyV r0 = vector_add(aR, zMN); + const auto yMN = vector_powers(y, MN); + r0 = hadamard(r0, yMN); + r0 = vector_add(r0, zero_twos); + rct::keyV r1 = hadamard(yMN, sR); + + // Polynomial construction before PAPER LINE 46 + rct::key t1_1 = inner_product(l0, r1); + rct::key t1_2 = inner_product(l1, r0); + rct::key t1; + sc_add(t1.bytes, t1_1.bytes, t1_2.bytes); + rct::key t2 = inner_product(l1, r1); + + PERF_TIMER_STOP(PROVE_step1); + + PERF_TIMER_START_BP(PROVE_step2); + // PAPER LINES 47-48 + rct::key tau1 = rct::skGen(), tau2 = rct::skGen(); + + rct::key T1 = rct::addKeys(rct::scalarmultKey(rct::H, t1), rct::scalarmultBase(tau1)); + rct::key T2 = rct::addKeys(rct::scalarmultKey(rct::H, t2), rct::scalarmultBase(tau2)); + + // PAPER LINES 49-51 + rct::key x = hash_cache_mash(hash_cache, z, T1, T2); + + // PAPER LINES 52-53 + rct::key taux; + sc_mul(taux.bytes, tau1.bytes, x.bytes); + rct::key xsq; + sc_mul(xsq.bytes, x.bytes, x.bytes); + sc_muladd(taux.bytes, tau2.bytes, xsq.bytes, taux.bytes); + for (size_t j = 1; j <= sv.size(); ++j) + { + CHECK_AND_ASSERT_THROW_MES(j+1 < zpow.size(), "invalid zpow index"); + sc_muladd(taux.bytes, zpow[j+1].bytes, gamma[j-1].bytes, taux.bytes); + } + rct::key mu; + sc_muladd(mu.bytes, x.bytes, rho.bytes, alpha.bytes); + + // PAPER LINES 54-57 + rct::keyV l = l0; + l = vector_add(l, vector_scalar(l1, x)); + rct::keyV r = r0; + r = vector_add(r, vector_scalar(r1, x)); + PERF_TIMER_STOP(PROVE_step2); + + PERF_TIMER_START_BP(PROVE_step3); + rct::key t = inner_product(l, r); + + // DEBUG: Test if the l and r vectors match the polynomial forms +#ifdef DEBUG_BP + rct::key test_t; + const rct::key t0 = inner_product(l0, r0); + sc_muladd(test_t.bytes, t1.bytes, x.bytes, t0.bytes); + sc_muladd(test_t.bytes, t2.bytes, xsq.bytes, test_t.bytes); + CHECK_AND_ASSERT_THROW_MES(test_t == t, "test_t check failed"); +#endif + + // PAPER LINES 32-33 + rct::key x_ip = hash_cache_mash(hash_cache, x, taux, mu, t); + + // These are used in the inner product rounds + size_t nprime = MN; + rct::keyV Gprime(MN); + rct::keyV Hprime(MN); + rct::keyV aprime(MN); + rct::keyV bprime(MN); + const rct::key yinv = invert(y); + rct::key yinvpow = rct::identity(); + for (size_t i = 0; i < MN; ++i) + { + Gprime[i] = Gi[i]; + Hprime[i] = scalarmultKey(Hi[i], yinvpow); + sc_mul(yinvpow.bytes, yinvpow.bytes, yinv.bytes); + aprime[i] = l[i]; + bprime[i] = r[i]; + } + rct::keyV L(logMN); + rct::keyV R(logMN); + int round = 0; + rct::keyV w(logMN); // this is the challenge x in the inner product protocol + PERF_TIMER_STOP(PROVE_step3); + + PERF_TIMER_START_BP(PROVE_step4); + // PAPER LINE 13 + while (nprime > 1) + { + // PAPER LINE 15 + nprime /= 2; + + // PAPER LINES 16-17 + rct::key cL = inner_product(slice(aprime, 0, nprime), slice(bprime, nprime, bprime.size())); + rct::key cR = inner_product(slice(aprime, nprime, aprime.size()), slice(bprime, 0, nprime)); + + // PAPER LINES 18-19 + L[round] = vector_exponent_custom(slice(Gprime, nprime, Gprime.size()), slice(Hprime, 0, nprime), slice(aprime, 0, nprime), slice(bprime, nprime, bprime.size())); + sc_mul(tmp.bytes, cL.bytes, x_ip.bytes); + rct::addKeys(L[round], L[round], rct::scalarmultKey(rct::H, tmp)); + R[round] = vector_exponent_custom(slice(Gprime, 0, nprime), slice(Hprime, nprime, Hprime.size()), slice(aprime, nprime, aprime.size()), slice(bprime, 0, nprime)); + sc_mul(tmp.bytes, cR.bytes, x_ip.bytes); + rct::addKeys(R[round], R[round], rct::scalarmultKey(rct::H, tmp)); + + // PAPER LINES 21-22 + w[round] = hash_cache_mash(hash_cache, L[round], R[round]); + + // PAPER LINES 24-25 + const rct::key winv = invert(w[round]); + Gprime = hadamard2(vector_scalar2(slice(Gprime, 0, nprime), winv), vector_scalar2(slice(Gprime, nprime, Gprime.size()), w[round])); + Hprime = hadamard2(vector_scalar2(slice(Hprime, 0, nprime), w[round]), vector_scalar2(slice(Hprime, nprime, Hprime.size()), winv)); + + // PAPER LINES 28-29 + aprime = vector_add(vector_scalar(slice(aprime, 0, nprime), w[round]), vector_scalar(slice(aprime, nprime, aprime.size()), winv)); + bprime = vector_add(vector_scalar(slice(bprime, 0, nprime), winv), vector_scalar(slice(bprime, nprime, bprime.size()), w[round])); + + ++round; + } + PERF_TIMER_STOP(PROVE_step4); + + // PAPER LINE 58 (with inclusions from PAPER LINE 8 and PAPER LINE 20) + return Bulletproof(V, A, S, T1, T2, taux, mu, L, R, aprime[0], bprime[0], t); +} + +Bulletproof bulletproof_PROVE(const std::vector &v, const rct::keyV &gamma) +{ + CHECK_AND_ASSERT_THROW_MES(v.size() == gamma.size(), "Incompatible sizes of v and gamma"); + + // vG + gammaH + PERF_TIMER_START_BP(PROVE_v); + rct::keyV sv(v.size()); + for (size_t i = 0; i < v.size(); ++i) + { + sv[i] = rct::zero(); + sv[i].bytes[0] = v[i] & 255; + sv[i].bytes[1] = (v[i] >> 8) & 255; + sv[i].bytes[2] = (v[i] >> 16) & 255; + sv[i].bytes[3] = (v[i] >> 24) & 255; + sv[i].bytes[4] = (v[i] >> 32) & 255; + sv[i].bytes[5] = (v[i] >> 40) & 255; + sv[i].bytes[6] = (v[i] >> 48) & 255; + sv[i].bytes[7] = (v[i] >> 56) & 255; + } + PERF_TIMER_STOP(PROVE_v); + return bulletproof_PROVE(sv, gamma); +} + /* Given a range proof, determine if it is valid */ bool bulletproof_VERIFY(const Bulletproof &proof) { init_exponents(); - CHECK_AND_ASSERT_MES(proof.V.size() == 1, false, "V does not have exactly one element"); + CHECK_AND_ASSERT_MES(proof.V.size() >= 1, false, "V does not have at least one element"); CHECK_AND_ASSERT_MES(proof.L.size() == proof.R.size(), false, "Mismatched L and R sizes"); CHECK_AND_ASSERT_MES(proof.L.size() > 0, false, "Empty proof"); - CHECK_AND_ASSERT_MES(proof.L.size() == 6, false, "Proof is not for 64 bits"); - const size_t logN = proof.L.size(); + const size_t logN = 6; const size_t N = 1 << logN; + rct::key tmp, tmp2; + + size_t M, logM; + for (logM = 0; (M = 1< 0, false, "Zero rounds"); PERF_TIMER_START_BP(VERIFY_line_21_22); @@ -666,7 +948,7 @@ bool bulletproof_VERIFY(const Bulletproof &proof) winv[i] = invert(w[i]); PERF_TIMER_STOP(VERIFY_line_24_25_invert); - for (size_t i = 0; i < N; ++i) + for (size_t i = 0; i < MN; ++i) { // Convert the index to binary IN REVERSE and construct the scalar exponent rct::key g_scalar = proof.a; @@ -691,7 +973,9 @@ bool bulletproof_VERIFY(const Bulletproof &proof) // Adjust the scalars using the exponents from PAPER LINE 62 sc_add(g_scalar.bytes, g_scalar.bytes, z.bytes); - sc_mul(tmp.bytes, zsq.bytes, twoN[i].bytes); + CHECK_AND_ASSERT_MES(2+i/N < zpow.size(), false, "invalid zpow index"); + CHECK_AND_ASSERT_MES(i%N < twoN.size(), false, "invalid twoN index"); + sc_mul(tmp.bytes, zpow[2+i/N].bytes, twoN[i%N].bytes); sc_muladd(tmp.bytes, z.bytes, ypow.bytes, tmp.bytes); sc_mulsub(h_scalar.bytes, tmp.bytes, yinvpow.bytes, h_scalar.bytes); @@ -700,7 +984,7 @@ bool bulletproof_VERIFY(const Bulletproof &proof) rct::addKeys3(tmp, g_scalar, Gprecomp[i], h_scalar, Hprecomp[i]); rct::addKeys(inner_prod, inner_prod, tmp); - if (i != N-1) + if (i != MN-1) { sc_mul(yinvpow.bytes, yinvpow.bytes, yinv.bytes); sc_mul(ypow.bytes, ypow.bytes, y.bytes); diff --git a/src/ringct/bulletproofs.h b/src/ringct/bulletproofs.h index 3061d272e29..3dfa38b1246 100644 --- a/src/ringct/bulletproofs.h +++ b/src/ringct/bulletproofs.h @@ -40,6 +40,8 @@ namespace rct Bulletproof bulletproof_PROVE(const rct::key &v, const rct::key &gamma); Bulletproof bulletproof_PROVE(uint64_t v, const rct::key &gamma); +Bulletproof bulletproof_PROVE(const rct::keyV &v, const rct::keyV &gamma); +Bulletproof bulletproof_PROVE(const std::vector &v, const rct::keyV &gamma); bool bulletproof_VERIFY(const Bulletproof &proof); } diff --git a/src/ringct/rctTypes.h b/src/ringct/rctTypes.h index f6987d3f37b..68f04b0f14f 100644 --- a/src/ringct/rctTypes.h +++ b/src/ringct/rctTypes.h @@ -190,6 +190,8 @@ namespace rct { Bulletproof() {} Bulletproof(const rct::key &V, const rct::key &A, const rct::key &S, const rct::key &T1, const rct::key &T2, const rct::key &taux, const rct::key &mu, const rct::keyV &L, const rct::keyV &R, const rct::key &a, const rct::key &b, const rct::key &t): V({V}), A(A), S(S), T1(T1), T2(T2), taux(taux), mu(mu), L(L), R(R), a(a), b(b), t(t) {} + Bulletproof(const rct::keyV &V, const rct::key &A, const rct::key &S, const rct::key &T1, const rct::key &T2, const rct::key &taux, const rct::key &mu, const rct::keyV &L, const rct::keyV &R, const rct::key &a, const rct::key &b, const rct::key &t): + V(V), A(A), S(S), T1(T1), T2(T2), taux(taux), mu(mu), L(L), R(R), a(a), b(b), t(t) {} BEGIN_SERIALIZE_OBJECT() // Commitments aren't saved, they're restored via outPk diff --git a/tests/unit_tests/bulletproofs.cpp b/tests/unit_tests/bulletproofs.cpp index 00595a4c7f8..d3b80453025 100644 --- a/tests/unit_tests/bulletproofs.cpp +++ b/tests/unit_tests/bulletproofs.cpp @@ -32,6 +32,7 @@ #include "ringct/rctOps.h" #include "ringct/bulletproofs.h" +#include "misc_log_ex.h" TEST(bulletproofs, valid_zero) { @@ -54,6 +55,24 @@ TEST(bulletproofs, valid_random) } } +TEST(bulletproofs, valid_multi_random) +{ + for (int n = 0; n < 8; ++n) + { + size_t outputs = 2 + n; + std::vector amounts; + rct::keyV gamma; + for (size_t i = 0; i < outputs; ++i) + { + amounts.push_back(crypto::rand()); + gamma.push_back(rct::skGen()); + } + rct::Bulletproof proof = bulletproof_PROVE(amounts, gamma); + ASSERT_TRUE(rct::bulletproof_VERIFY(proof)); + } +} + + TEST(bulletproofs, invalid_8) { rct::key invalid_amount = rct::zero(); From 1aa10c4364be4ba48df06f5ff582eef10b9288c3 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 5 Jan 2018 12:36:32 +0000 Subject: [PATCH 0337/1404] performance_tests: add (Borromean) range proofs --- tests/performance_tests/CMakeLists.txt | 1 + tests/performance_tests/main.cpp | 4 ++ tests/performance_tests/range_proof.h | 63 ++++++++++++++++++++++++++ 3 files changed, 68 insertions(+) create mode 100644 tests/performance_tests/range_proof.h diff --git a/tests/performance_tests/CMakeLists.txt b/tests/performance_tests/CMakeLists.txt index 8cbd71444a4..690489ab86f 100644 --- a/tests/performance_tests/CMakeLists.txt +++ b/tests/performance_tests/CMakeLists.txt @@ -42,6 +42,7 @@ set(performance_tests_headers generate_keypair.h is_out_to_acc.h subaddress_expand.h + range_proof.h multi_tx_test_base.h performance_tests.h performance_utils.h diff --git a/tests/performance_tests/main.cpp b/tests/performance_tests/main.cpp index 5fe8263b2eb..27fa56afbfd 100644 --- a/tests/performance_tests/main.cpp +++ b/tests/performance_tests/main.cpp @@ -52,6 +52,7 @@ #include "cn_fast_hash.h" #include "rct_mlsag.h" #include "equality.h" +#include "range_proof.h" namespace po = boost::program_options; @@ -156,6 +157,9 @@ int main(int argc, char** argv) TEST_PERFORMANCE2(filter, test_equality, verify32, false); TEST_PERFORMANCE2(filter, test_equality, verify32, false); + TEST_PERFORMANCE1(filter, test_range_proof, true); + TEST_PERFORMANCE1(filter, test_range_proof, false); + std::cout << "Tests finished. Elapsed time: " << timer.elapsed_ms() / 1000 << " sec" << std::endl; return 0; diff --git a/tests/performance_tests/range_proof.h b/tests/performance_tests/range_proof.h new file mode 100644 index 00000000000..0ce962cd940 --- /dev/null +++ b/tests/performance_tests/range_proof.h @@ -0,0 +1,63 @@ +// Copyright (c) 2014-2017, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers + +#pragma once + +#include "ringct/rctSigs.h" + +template +class test_range_proof +{ +public: + static const size_t loop_count = 50; + static const bool verify = a_verify; + + bool init() + { + rct::key mask; + sig = rct::proveRange(C, mask, 84932483243793); + return true; + } + + bool test() + { + bool ret = true; + rct::key mask; + if (verify) + ret = rct::verRange(C, sig); + else + rct::proveRange(C, mask, 84932483243793); + return ret; + } + +private: + rct::key C; + rct::rangeSig sig; +}; From 8f4ce989c2b586667d9fed4a342576472ace5ff4 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 31 Dec 2017 18:58:45 +0000 Subject: [PATCH 0338/1404] performance_tests: add RingCT MLSAG gen/ver tests --- tests/performance_tests/main.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/performance_tests/main.cpp b/tests/performance_tests/main.cpp index 27fa56afbfd..1025d9a4402 100644 --- a/tests/performance_tests/main.cpp +++ b/tests/performance_tests/main.cpp @@ -53,6 +53,7 @@ #include "rct_mlsag.h" #include "equality.h" #include "range_proof.h" +#include "rct_mlsag.h" namespace po = boost::program_options; @@ -160,6 +161,15 @@ int main(int argc, char** argv) TEST_PERFORMANCE1(filter, test_range_proof, true); TEST_PERFORMANCE1(filter, test_range_proof, false); + TEST_PERFORMANCE3(filter, test_ringct_mlsag, 1, 3, false); + TEST_PERFORMANCE3(filter, test_ringct_mlsag, 1, 5, false); + TEST_PERFORMANCE3(filter, test_ringct_mlsag, 1, 10, false); + TEST_PERFORMANCE3(filter, test_ringct_mlsag, 1, 100, false); + TEST_PERFORMANCE3(filter, test_ringct_mlsag, 1, 3, true); + TEST_PERFORMANCE3(filter, test_ringct_mlsag, 1, 5, true); + TEST_PERFORMANCE3(filter, test_ringct_mlsag, 1, 10, true); + TEST_PERFORMANCE3(filter, test_ringct_mlsag, 1, 100, true); + std::cout << "Tests finished. Elapsed time: " << timer.elapsed_ms() / 1000 << " sec" << std::endl; return 0; From f5d7b9933a68e70002ac74b65a9b6e3f4ccd2d24 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 5 Jan 2018 13:10:14 +0000 Subject: [PATCH 0339/1404] performance_tests: add bulletproofs --- tests/performance_tests/CMakeLists.txt | 1 + tests/performance_tests/bulletproof.h | 62 ++++++++++++++++++++++++++ tests/performance_tests/main.cpp | 10 +++++ 3 files changed, 73 insertions(+) create mode 100644 tests/performance_tests/bulletproof.h diff --git a/tests/performance_tests/CMakeLists.txt b/tests/performance_tests/CMakeLists.txt index 690489ab86f..a41359ca741 100644 --- a/tests/performance_tests/CMakeLists.txt +++ b/tests/performance_tests/CMakeLists.txt @@ -43,6 +43,7 @@ set(performance_tests_headers is_out_to_acc.h subaddress_expand.h range_proof.h + bulletproof.h multi_tx_test_base.h performance_tests.h performance_utils.h diff --git a/tests/performance_tests/bulletproof.h b/tests/performance_tests/bulletproof.h new file mode 100644 index 00000000000..b11c9fe97c1 --- /dev/null +++ b/tests/performance_tests/bulletproof.h @@ -0,0 +1,62 @@ +// Copyright (c) 2014-2017, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers + +#pragma once + +#include "ringct/rctSigs.h" +#include "ringct/bulletproofs.h" + +template +class test_bulletproof +{ +public: + static const size_t approx_loop_count = 100 / n_amounts; + static const size_t loop_count = (approx_loop_count >= 10 ? approx_loop_count : 10) / (a_verify ? 1 : 5); + static const bool verify = a_verify; + + bool init() + { + proof = rct::bulletproof_PROVE(std::vector(n_amounts, 749327532984), rct::skvGen(n_amounts)); + return true; + } + + bool test() + { + bool ret = true; + if (verify) + ret = rct::bulletproof_VERIFY(proof); + else + rct::bulletproof_PROVE(std::vector(n_amounts, 749327532984), rct::skvGen(n_amounts)); + return ret; + } + +private: + rct::Bulletproof proof; +}; diff --git a/tests/performance_tests/main.cpp b/tests/performance_tests/main.cpp index 1025d9a4402..7fd1873b51c 100644 --- a/tests/performance_tests/main.cpp +++ b/tests/performance_tests/main.cpp @@ -54,6 +54,7 @@ #include "equality.h" #include "range_proof.h" #include "rct_mlsag.h" +#include "bulletproof.h" namespace po = boost::program_options; @@ -161,6 +162,15 @@ int main(int argc, char** argv) TEST_PERFORMANCE1(filter, test_range_proof, true); TEST_PERFORMANCE1(filter, test_range_proof, false); + TEST_PERFORMANCE2(filter, test_bulletproof, true, 1); + TEST_PERFORMANCE2(filter, test_bulletproof, false, 1); + + TEST_PERFORMANCE2(filter, test_bulletproof, true, 2); + TEST_PERFORMANCE2(filter, test_bulletproof, false, 2); + + TEST_PERFORMANCE2(filter, test_bulletproof, true, 15); + TEST_PERFORMANCE2(filter, test_bulletproof, false, 15); + TEST_PERFORMANCE3(filter, test_ringct_mlsag, 1, 3, false); TEST_PERFORMANCE3(filter, test_ringct_mlsag, 1, 5, false); TEST_PERFORMANCE3(filter, test_ringct_mlsag, 1, 10, false); From 112f32f068e2e6d0b8e713ccf49090b73e542b5d Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 5 Jan 2018 16:30:24 +0000 Subject: [PATCH 0340/1404] performance_tests: add crypto ops --- tests/performance_tests/CMakeLists.txt | 1 + tests/performance_tests/crypto_ops.h | 116 +++++++++++++++++++++++++ tests/performance_tests/main.cpp | 16 ++++ 3 files changed, 133 insertions(+) create mode 100644 tests/performance_tests/crypto_ops.h diff --git a/tests/performance_tests/CMakeLists.txt b/tests/performance_tests/CMakeLists.txt index a41359ca741..47f441dda26 100644 --- a/tests/performance_tests/CMakeLists.txt +++ b/tests/performance_tests/CMakeLists.txt @@ -44,6 +44,7 @@ set(performance_tests_headers subaddress_expand.h range_proof.h bulletproof.h + crypto_ops.h multi_tx_test_base.h performance_tests.h performance_utils.h diff --git a/tests/performance_tests/crypto_ops.h b/tests/performance_tests/crypto_ops.h new file mode 100644 index 00000000000..852a6670d9b --- /dev/null +++ b/tests/performance_tests/crypto_ops.h @@ -0,0 +1,116 @@ +// Copyright (c) 2014-2017, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers + +#pragma once + +#include "crypto/crypto.h" +#include "ringct/rctOps.h" + +enum test_op +{ + op_sc_add, + op_sc_sub, + op_sc_mul, + op_ge_add_raw, + op_ge_add_p3_p3, + ops_fast, + + op_addKeys, + op_scalarmultBase, + op_scalarmultKey, + op_ge_double_scalarmult_base_vartime, + op_ge_double_scalarmult_precomp_vartime, + op_ge_double_scalarmult_precomp_vartime2, + op_addKeys2, + op_addKeys3, + op_addKeys3_2, +}; + +template +class test_crypto_ops +{ +public: + static const size_t loop_count = op < ops_fast ? 10000000 : 1000; + + bool init() + { + scalar0 = rct::skGen(); + scalar1 = rct::skGen(); + point0 = rct::scalarmultBase(rct::skGen()); + point1 = rct::scalarmultBase(rct::skGen()); + if (ge_frombytes_vartime(&p3_0, point0.bytes) != 0) + return false; + if (ge_frombytes_vartime(&p3_1, point1.bytes) != 0) + return false; + ge_p3_to_cached(&cached, &p3_0); + rct::precomp(precomp0, point0); + rct::precomp(precomp1, point1); + return true; + } + + bool test() + { + rct::key key; + ge_cached tmp_cached; + ge_p1p1 tmp_p1p1; + ge_p2 tmp_p2; + switch (op) + { + case op_sc_add: sc_add(key.bytes, scalar0.bytes, scalar1.bytes); break; + case op_sc_sub: sc_sub(key.bytes, scalar0.bytes, scalar1.bytes); break; + case op_sc_mul: sc_mul(key.bytes, scalar0.bytes, scalar1.bytes); break; + case op_ge_add_p3_p3: { + ge_p3_to_cached(&tmp_cached, &p3_0); + ge_add(&tmp_p1p1, &p3_1, &tmp_cached); + ge_p1p1_to_p3(&p3_1, &tmp_p1p1); + break; + } + case op_ge_add_raw: ge_add(&tmp_p1p1, &p3_1, &cached); break; + case op_addKeys: rct::addKeys(key, point0, point1); break; + case op_scalarmultBase: rct::scalarmultBase(scalar0); break; + case op_scalarmultKey: rct::scalarmultKey(point0, scalar0); break; + case op_ge_double_scalarmult_base_vartime: ge_double_scalarmult_base_vartime(&tmp_p2, scalar0.bytes, &p3_0, scalar1.bytes); break; + case op_ge_double_scalarmult_precomp_vartime: ge_double_scalarmult_precomp_vartime(&tmp_p2, scalar0.bytes, &p3_0, scalar1.bytes, precomp0); break; + case op_ge_double_scalarmult_precomp_vartime2: ge_double_scalarmult_precomp_vartime2(&tmp_p2, scalar0.bytes, precomp0, scalar1.bytes, precomp1); break; + case op_addKeys2: rct::addKeys2(key, scalar0, scalar1, point0); break; + case op_addKeys3: rct::addKeys3(key, scalar0, point0, scalar1, precomp1); break; + case op_addKeys3_2: rct::addKeys3(key, scalar0, precomp0, scalar1, precomp1); break; + default: return false; + } + return true; + } + +private: + rct::key scalar0, scalar1; + rct::key point0, point1; + ge_p3 p3_0, p3_1; + ge_cached cached; + ge_dsmp precomp0, precomp1; +}; diff --git a/tests/performance_tests/main.cpp b/tests/performance_tests/main.cpp index 7fd1873b51c..5b7a30f9623 100644 --- a/tests/performance_tests/main.cpp +++ b/tests/performance_tests/main.cpp @@ -55,6 +55,7 @@ #include "range_proof.h" #include "rct_mlsag.h" #include "bulletproof.h" +#include "crypto_ops.h" namespace po = boost::program_options; @@ -180,6 +181,21 @@ int main(int argc, char** argv) TEST_PERFORMANCE3(filter, test_ringct_mlsag, 1, 10, true); TEST_PERFORMANCE3(filter, test_ringct_mlsag, 1, 100, true); + TEST_PERFORMANCE1(filter, test_crypto_ops, op_sc_add); + TEST_PERFORMANCE1(filter, test_crypto_ops, op_sc_sub); + TEST_PERFORMANCE1(filter, test_crypto_ops, op_sc_mul); + TEST_PERFORMANCE1(filter, test_crypto_ops, op_ge_add_raw); + TEST_PERFORMANCE1(filter, test_crypto_ops, op_ge_add_p3_p3); + TEST_PERFORMANCE1(filter, test_crypto_ops, op_addKeys); + TEST_PERFORMANCE1(filter, test_crypto_ops, op_scalarmultBase); + TEST_PERFORMANCE1(filter, test_crypto_ops, op_scalarmultKey); + TEST_PERFORMANCE1(filter, test_crypto_ops, op_ge_double_scalarmult_base_vartime); + TEST_PERFORMANCE1(filter, test_crypto_ops, op_ge_double_scalarmult_precomp_vartime); + TEST_PERFORMANCE1(filter, test_crypto_ops, op_ge_double_scalarmult_precomp_vartime2); + TEST_PERFORMANCE1(filter, test_crypto_ops, op_addKeys2); + TEST_PERFORMANCE1(filter, test_crypto_ops, op_addKeys3); + TEST_PERFORMANCE1(filter, test_crypto_ops, op_addKeys3_2); + std::cout << "Tests finished. Elapsed time: " << timer.elapsed_ms() / 1000 << " sec" << std::endl; return 0; From e9164bb38ba6105810597c8fa0d5a9bdfa295b07 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 5 Jan 2018 22:39:59 +0000 Subject: [PATCH 0341/1404] bulletproofs: misc optimizations Use double mults where possible, avoid conversions, simplify --- src/ringct/bulletproofs.cc | 110 +++++++++++++++++++++++++++++-------- 1 file changed, 87 insertions(+), 23 deletions(-) diff --git a/src/ringct/bulletproofs.cc b/src/ringct/bulletproofs.cc index 40d097f20c3..aa56b058961 100644 --- a/src/ringct/bulletproofs.cc +++ b/src/ringct/bulletproofs.cc @@ -65,6 +65,33 @@ static const rct::keyV twoN = vector_powers(TWO, maxN); static const rct::key ip12 = inner_product(oneN, twoN); static boost::mutex init_mutex; +//addKeys3acc_p3 +//aAbB += a*A + b*B where a, b are scalars, A, B are curve points +//A and B must be input after applying "precomp" +static void addKeys3acc_p3(ge_p3 *aAbB, const key &a, const ge_dsmp A, const key &b, const ge_dsmp B) +{ + ge_p3 rv; + ge_p1p1 p1; + ge_p2 p2; + ge_double_scalarmult_precomp_vartime2_p3(&rv, a.bytes, A, b.bytes, B); + ge_cached cached; + ge_p3_to_cached(&cached, aAbB); + ge_add(&p1, &rv, &cached); + ge_p1p1_to_p3(aAbB, &p1); +} + +static void addKeys_acc_p3(ge_p3 *acc_p3, const rct::key &a, const rct::key &point) +{ + ge_p3 p3; + CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&p3, point.bytes) == 0, "ge_frombytes_vartime failed"); + ge_scalarmult_p3(&p3, a.bytes, &p3); + ge_cached cached; + ge_p3_to_cached(&cached, acc_p3); + ge_p1p1 p1; + ge_add(&p1, &p3, &cached); + ge_p1p1_to_p3(acc_p3, &p1); +} + static rct::key get_exponent(const rct::key &base, size_t idx) { static const std::string salt("bulletproof"); @@ -94,13 +121,13 @@ static rct::key vector_exponent(const rct::keyV &a, const rct::keyV &b) { CHECK_AND_ASSERT_THROW_MES(a.size() == b.size(), "Incompatible sizes of a and b"); CHECK_AND_ASSERT_THROW_MES(a.size() <= maxN*maxM, "Incompatible sizes of a and maxN"); - rct::key res = rct::identity(); + ge_p3 res_p3 = ge_p3_identity; for (size_t i = 0; i < a.size(); ++i) { - rct::key term; - rct::addKeys3(term, a[i], Gprecomp[i], b[i], Hprecomp[i]); - rct::addKeys(res, res, term); + rct::addKeys3acc_p3(&res_p3, a[i], Gprecomp[i], b[i], Hprecomp[i]); } + rct::key res; + ge_p3_tobytes(res.bytes, &res_p3); return res; } @@ -111,11 +138,11 @@ static rct::key vector_exponent_custom(const rct::keyV &A, const rct::keyV &B, c CHECK_AND_ASSERT_THROW_MES(a.size() == b.size(), "Incompatible sizes of a and b"); CHECK_AND_ASSERT_THROW_MES(a.size() == A.size(), "Incompatible sizes of a and A"); CHECK_AND_ASSERT_THROW_MES(a.size() <= maxN*maxM, "Incompatible sizes of a and maxN"); - rct::key res = rct::identity(); + ge_p3 res_p3 = ge_p3_identity; for (size_t i = 0; i < a.size(); ++i) { - rct::key term; #if 0 + rct::key term; // we happen to know where A and B might fall, so don't bother checking the rest ge_dsmp *Acache = NULL, *Bcache = NULL; ge_dsmp Acache_custom[1], Bcache_custom[1]; @@ -136,13 +163,16 @@ static rct::key vector_exponent_custom(const rct::keyV &A, const rct::keyV &B, c Bcache = Bcache_custom; } rct::addKeys3(term, a[i], *Acache, b[i], *Bcache); + rct::addKeys(res, res, term); #else ge_dsmp Acache, Bcache; rct::precomp(Bcache, B[i]); - rct::addKeys3(term, a[i], A[i], b[i], Bcache); + rct::precomp(Acache, A[i]); + addKeys3acc_p3(&res_p3, a[i], Acache, b[i], Bcache); #endif - rct::addKeys(res, res, term); } + rct::key res; + ge_p3_tobytes(res.bytes, &res_p3); return res; } @@ -163,6 +193,24 @@ static rct::keyV vector_powers(const rct::key &x, size_t n) return res; } +/* Given a scalar, return the sum of its powers from 0 to n-1 */ +static rct::key vector_power_sum(const rct::key &x, size_t n) +{ + if (n == 0) + return rct::zero(); + rct::key res = rct::identity(); + if (n == 1) + return res; + rct::key prev = x; + for (size_t i = 1; i < n; ++i) + { + if (i > 1) + sc_mul(prev.bytes, prev.bytes, x.bytes); + sc_add(res.bytes, res.bytes, prev.bytes); + } + return res; +} + /* Given two scalar arrays, construct the inner product */ static rct::key inner_product(const rct::keyV &a, const rct::keyV &b) { @@ -876,12 +924,13 @@ bool bulletproof_VERIFY(const Bulletproof &proof) PERF_TIMER_START_BP(VERIFY_line_61); // PAPER LINE 61 - rct::key L61Left = rct::addKeys(rct::scalarmultBase(proof.taux), rct::scalarmultKey(rct::H, proof.t)); + rct::key L61Left; + rct::addKeys2(L61Left, proof.taux, proof.t, rct::H); const rct::keyV zpow = vector_powers(z, M+3); rct::key k; - const rct::key ip1y = vector_sum(vector_powers(y, MN)); + const rct::key ip1y = vector_power_sum(y, MN); sc_mulsub(k.bytes, zpow[2].bytes, ip1y.bytes, rct::zero().bytes); for (size_t j = 1; j <= M; ++j) { @@ -893,20 +942,32 @@ bool bulletproof_VERIFY(const Bulletproof &proof) PERF_TIMER_START_BP(VERIFY_line_61rl); sc_muladd(tmp.bytes, z.bytes, ip1y.bytes, k.bytes); rct::key L61Right = rct::scalarmultKey(rct::H, tmp); - for (size_t j = 0; j < M; ++j) + ge_p3 L61Right_p3; + CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&L61Right_p3, L61Right.bytes) == 0, "ge_frombytes_vartime failed"); + for (size_t j = 0; j+1 < proof.V.size(); j += 2) + { + CHECK_AND_ASSERT_MES(j+2+1 < zpow.size(), false, "invalid zpow index"); + ge_dsmp precomp0, precomp1; + rct::precomp(precomp0, j < proof.V.size() ? proof.V[j] : rct::identity()); + rct::precomp(precomp1, j+1 < proof.V.size() ? proof.V[j+1] : rct::identity()); + rct::addKeys3acc_p3(&L61Right_p3, zpow[j+2], precomp0, zpow[j+2+1], precomp1); + } + for (size_t j = proof.V.size() & 0xfffffffe; j < M; j++) { CHECK_AND_ASSERT_MES(j+2 < zpow.size(), false, "invalid zpow index"); - tmp = rct::scalarmultKey(j < proof.V.size() ? proof.V[j] : rct::identity(), zpow[j+2]); - rct::addKeys(L61Right, L61Right, tmp); + // faster equivalent to: + // tmp = rct::scalarmultKey(j < proof.V.size() ? proof.V[j] : rct::identity(), zpow[j+2]); + // rct::addKeys(L61Right, L61Right, tmp); + if (j < proof.V.size()) + addKeys_acc_p3(&L61Right_p3, zpow[j+2], proof.V[j]); } - tmp = rct::scalarmultKey(proof.T1, x); - rct::addKeys(L61Right, L61Right, tmp); + addKeys_acc_p3(&L61Right_p3, x, proof.T1); rct::key xsq; sc_mul(xsq.bytes, x.bytes, x.bytes); - tmp = rct::scalarmultKey(proof.T2, xsq); - rct::addKeys(L61Right, L61Right, tmp); + addKeys_acc_p3(&L61Right_p3, xsq, proof.T2); + ge_p3_tobytes(L61Right.bytes, &L61Right_p3); PERF_TIMER_STOP(VERIFY_line_61rl); if (!(L61Right == L61Left)) @@ -937,7 +998,7 @@ bool bulletproof_VERIFY(const Bulletproof &proof) PERF_TIMER_START_BP(VERIFY_line_24_25); // Basically PAPER LINES 24-25 // Compute the curvepoints from G[i] and H[i] - rct::key inner_prod = rct::identity(); + ge_p3 inner_prod_p3 = ge_p3_identity; rct::key yinvpow = rct::identity(); rct::key ypow = rct::identity(); @@ -981,8 +1042,7 @@ bool bulletproof_VERIFY(const Bulletproof &proof) // Now compute the basepoint's scalar multiplication // Each of these could be written as a multiexp operation instead - rct::addKeys3(tmp, g_scalar, Gprecomp[i], h_scalar, Hprecomp[i]); - rct::addKeys(inner_prod, inner_prod, tmp); + addKeys3acc_p3(&inner_prod_p3, g_scalar, Gprecomp[i], h_scalar, Hprecomp[i]); if (i != MN-1) { @@ -990,6 +1050,8 @@ bool bulletproof_VERIFY(const Bulletproof &proof) sc_mul(ypow.bytes, ypow.bytes, y.bytes); } } + rct::key inner_prod; + ge_p3_tobytes(inner_prod.bytes, &inner_prod_p3); PERF_TIMER_STOP(VERIFY_line_24_25); PERF_TIMER_START_BP(VERIFY_line_26); @@ -997,6 +1059,8 @@ bool bulletproof_VERIFY(const Bulletproof &proof) rct::key pprime; sc_sub(tmp.bytes, rct::zero().bytes, proof.mu.bytes); rct::addKeys(pprime, P, rct::scalarmultBase(tmp)); + ge_p3 pprime_p3; + CHECK_AND_ASSERT_MES(ge_frombytes_vartime(&pprime_p3, pprime.bytes) == 0, false, "ge_frombytes_vartime failed"); for (size_t i = 0; i < rounds; ++i) { @@ -1006,15 +1070,15 @@ bool bulletproof_VERIFY(const Bulletproof &proof) ge_dsmp cacheL, cacheR; rct::precomp(cacheL, proof.L[i]); rct::precomp(cacheR, proof.R[i]); - rct::addKeys3(tmp, tmp, cacheL, tmp2, cacheR); - rct::addKeys(pprime, pprime, tmp); + addKeys3acc_p3(&pprime_p3, tmp, cacheL, tmp2, cacheR); #else rct::addKeys(pprime, pprime, rct::scalarmultKey(proof.L[i], tmp)); rct::addKeys(pprime, pprime, rct::scalarmultKey(proof.R[i], tmp2)); #endif } sc_mul(tmp.bytes, proof.t.bytes, x_ip.bytes); - rct::addKeys(pprime, pprime, rct::scalarmultKey(rct::H, tmp)); + addKeys_acc_p3(&pprime_p3, tmp, rct::H); + ge_p3_tobytes(pprime.bytes, &pprime_p3); PERF_TIMER_STOP(VERIFY_line_26); PERF_TIMER_START_BP(VERIFY_step2_check); From 9ff6e6a0a75d24fb3189e695def0b51b4fa67e92 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 9 Jan 2018 13:51:17 +0000 Subject: [PATCH 0342/1404] ringct: add bos coster multiexp --- src/ringct/CMakeLists.txt | 2 + src/ringct/bulletproofs.cc | 205 +++++++++++++++++++++++-------- src/ringct/multiexp.cc | 239 +++++++++++++++++++++++++++++++++++++ src/ringct/multiexp.h | 60 ++++++++++ 4 files changed, 455 insertions(+), 51 deletions(-) create mode 100644 src/ringct/multiexp.cc create mode 100644 src/ringct/multiexp.h diff --git a/src/ringct/CMakeLists.txt b/src/ringct/CMakeLists.txt index c8dcdca26a9..29f166a3bb4 100644 --- a/src/ringct/CMakeLists.txt +++ b/src/ringct/CMakeLists.txt @@ -30,11 +30,13 @@ set(ringct_basic_sources rctOps.cpp rctTypes.cpp rctCryptoOps.c + multiexp.cc bulletproofs.cc) set(ringct_basic_private_headers rctOps.h rctTypes.h + multiexp.h bulletproofs.h) monero_private_headers(ringct_basic diff --git a/src/ringct/bulletproofs.cc b/src/ringct/bulletproofs.cc index aa56b058961..057f190290b 100644 --- a/src/ringct/bulletproofs.cc +++ b/src/ringct/bulletproofs.cc @@ -38,6 +38,7 @@ extern "C" #include "crypto/crypto-ops.h" } #include "rctOps.h" +#include "multiexp.h" #include "bulletproofs.h" #undef MONERO_DEFAULT_LOG_CATEGORY @@ -58,6 +59,7 @@ static rct::key inner_product(const rct::keyV &a, const rct::keyV &b); static constexpr size_t maxN = 64; static constexpr size_t maxM = 16; static rct::key Hi[maxN*maxM], Gi[maxN*maxM]; +static ge_p3 Hi_p3[maxN*maxM], Gi_p3[maxN*maxM]; static ge_dsmp Gprecomp[maxN*maxM], Hprecomp[maxN*maxM]; static const rct::key TWO = { {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 } }; static const rct::keyV oneN = vector_dup(rct::identity(), maxN); @@ -110,9 +112,12 @@ static void init_exponents() { Hi[i] = get_exponent(rct::H, i * 2); rct::precomp(Hprecomp[i], Hi[i]); + CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&Hi_p3[i], Hi[i].bytes) == 0, "ge_frombytes_vartime failed"); Gi[i] = get_exponent(rct::H, i * 2 + 1); rct::precomp(Gprecomp[i], Gi[i]); + CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&Gi_p3[i], Gi[i].bytes) == 0, "ge_frombytes_vartime failed"); } + MINFO("cache size: " << (sizeof(Hi)+sizeof(Hprecomp)+sizeof(Hi_p3))*2/1024 << " kB"); init_done = true; } @@ -121,6 +126,26 @@ static rct::key vector_exponent(const rct::keyV &a, const rct::keyV &b) { CHECK_AND_ASSERT_THROW_MES(a.size() == b.size(), "Incompatible sizes of a and b"); CHECK_AND_ASSERT_THROW_MES(a.size() <= maxN*maxM, "Incompatible sizes of a and maxN"); +#if 1 + std::vector multiexp_data; + multiexp_data.reserve(a.size()*2); + for (size_t i = 0; i < a.size(); ++i) + { + if (!(a[i] == rct::zero())) + { + multiexp_data.resize(multiexp_data.size() + 1); + multiexp_data.back().scalar = a[i]; + multiexp_data.back().point = Gi_p3[i]; + } + if (!(b[i] == rct::zero())) + { + multiexp_data.resize(multiexp_data.size() + 1); + multiexp_data.back().scalar = b[i]; + multiexp_data.back().point = Hi_p3[i]; + } + } + return bos_coster_heap_conv_robust(multiexp_data); +#else ge_p3 res_p3 = ge_p3_identity; for (size_t i = 0; i < a.size(); ++i) { @@ -129,6 +154,7 @@ static rct::key vector_exponent(const rct::keyV &a, const rct::keyV &b) rct::key res; ge_p3_tobytes(res.bytes, &res_p3); return res; +#endif } /* Compute a custom vector-scalar commitment */ @@ -138,6 +164,26 @@ static rct::key vector_exponent_custom(const rct::keyV &A, const rct::keyV &B, c CHECK_AND_ASSERT_THROW_MES(a.size() == b.size(), "Incompatible sizes of a and b"); CHECK_AND_ASSERT_THROW_MES(a.size() == A.size(), "Incompatible sizes of a and A"); CHECK_AND_ASSERT_THROW_MES(a.size() <= maxN*maxM, "Incompatible sizes of a and maxN"); +#if 1 + std::vector multiexp_data; + multiexp_data.reserve(a.size()*2); + for (size_t i = 0; i < a.size(); ++i) + { + if (!(a[i] == rct::zero())) + { + multiexp_data.resize(multiexp_data.size() + 1); + multiexp_data.back().scalar = a[i]; + CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&multiexp_data.back().point, A[i].bytes) == 0, "ge_frombytes_vartime failed"); + } + if (!(b[i] == rct::zero())) + { + multiexp_data.resize(multiexp_data.size() + 1); + multiexp_data.back().scalar = b[i]; + CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&multiexp_data.back().point, B[i].bytes) == 0, "ge_frombytes_vartime failed"); + } + } + return bos_coster_heap_conv_robust(multiexp_data); +#else ge_p3 res_p3 = ge_p3_identity; for (size_t i = 0; i < a.size(); ++i) { @@ -174,6 +220,7 @@ static rct::key vector_exponent_custom(const rct::keyV &A, const rct::keyV &B, c rct::key res; ge_p3_tobytes(res.bytes, &res_p3); return res; +#endif } /* Given a scalar, construct a vector of powers */ @@ -924,7 +971,7 @@ bool bulletproof_VERIFY(const Bulletproof &proof) PERF_TIMER_START_BP(VERIFY_line_61); // PAPER LINE 61 - rct::key L61Left; + rct::key L61Left, L61Right; rct::addKeys2(L61Left, proof.taux, proof.t, rct::H); const rct::keyV zpow = vector_powers(z, M+3); @@ -939,36 +986,61 @@ bool bulletproof_VERIFY(const Bulletproof &proof) } PERF_TIMER_STOP(VERIFY_line_61); - PERF_TIMER_START_BP(VERIFY_line_61rl); - sc_muladd(tmp.bytes, z.bytes, ip1y.bytes, k.bytes); - rct::key L61Right = rct::scalarmultKey(rct::H, tmp); - ge_p3 L61Right_p3; - CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&L61Right_p3, L61Right.bytes) == 0, "ge_frombytes_vartime failed"); - for (size_t j = 0; j+1 < proof.V.size(); j += 2) + // multiexp is slower for small numbers of calcs + if (M >= 16) { - CHECK_AND_ASSERT_MES(j+2+1 < zpow.size(), false, "invalid zpow index"); - ge_dsmp precomp0, precomp1; - rct::precomp(precomp0, j < proof.V.size() ? proof.V[j] : rct::identity()); - rct::precomp(precomp1, j+1 < proof.V.size() ? proof.V[j+1] : rct::identity()); - rct::addKeys3acc_p3(&L61Right_p3, zpow[j+2], precomp0, zpow[j+2+1], precomp1); + PERF_TIMER_START_BP(VERIFY_line_61rl_new); + sc_muladd(tmp.bytes, z.bytes, ip1y.bytes, k.bytes); + std::vector multiexp_data; + multiexp_data.reserve(3+M); + multiexp_data.push_back({tmp, rct::H}); + for (size_t j = 0; j < M; j++) + { + if (!(zpow[j+2] == rct::zero())) + multiexp_data.push_back({zpow[j+2], j < proof.V.size() ? proof.V[j] : rct::identity()}); + } + if (!(x == rct::zero())) + multiexp_data.push_back({x, proof.T1}); + rct::key xsq; + sc_mul(xsq.bytes, x.bytes, x.bytes); + if (!(xsq == rct::zero())) + multiexp_data.push_back({xsq, proof.T2}); + L61Right = bos_coster_heap_conv_robust(multiexp_data); + PERF_TIMER_STOP(VERIFY_line_61rl_new); } - for (size_t j = proof.V.size() & 0xfffffffe; j < M; j++) + else { - CHECK_AND_ASSERT_MES(j+2 < zpow.size(), false, "invalid zpow index"); - // faster equivalent to: - // tmp = rct::scalarmultKey(j < proof.V.size() ? proof.V[j] : rct::identity(), zpow[j+2]); - // rct::addKeys(L61Right, L61Right, tmp); - if (j < proof.V.size()) - addKeys_acc_p3(&L61Right_p3, zpow[j+2], proof.V[j]); - } + PERF_TIMER_START_BP(VERIFY_line_61rl_old); + sc_muladd(tmp.bytes, z.bytes, ip1y.bytes, k.bytes); + L61Right = rct::scalarmultKey(rct::H, tmp); + ge_p3 L61Right_p3; + CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&L61Right_p3, L61Right.bytes) == 0, "ge_frombytes_vartime failed"); + for (size_t j = 0; j+1 < proof.V.size(); j += 2) + { + CHECK_AND_ASSERT_MES(j+2+1 < zpow.size(), false, "invalid zpow index"); + ge_dsmp precomp0, precomp1; + rct::precomp(precomp0, j < proof.V.size() ? proof.V[j] : rct::identity()); + rct::precomp(precomp1, j+1 < proof.V.size() ? proof.V[j+1] : rct::identity()); + rct::addKeys3acc_p3(&L61Right_p3, zpow[j+2], precomp0, zpow[j+2+1], precomp1); + } + for (size_t j = proof.V.size() & 0xfffffffe; j < M; j++) + { + CHECK_AND_ASSERT_MES(j+2 < zpow.size(), false, "invalid zpow index"); + // faster equivalent to: + // tmp = rct::scalarmultKey(j < proof.V.size() ? proof.V[j] : rct::identity(), zpow[j+2]); + // rct::addKeys(L61Right, L61Right, tmp); + if (j < proof.V.size()) + addKeys_acc_p3(&L61Right_p3, zpow[j+2], proof.V[j]); + } - addKeys_acc_p3(&L61Right_p3, x, proof.T1); + addKeys_acc_p3(&L61Right_p3, x, proof.T1); - rct::key xsq; - sc_mul(xsq.bytes, x.bytes, x.bytes); - addKeys_acc_p3(&L61Right_p3, xsq, proof.T2); - ge_p3_tobytes(L61Right.bytes, &L61Right_p3); - PERF_TIMER_STOP(VERIFY_line_61rl); + rct::key xsq; + sc_mul(xsq.bytes, x.bytes, x.bytes); + addKeys_acc_p3(&L61Right_p3, xsq, proof.T2); + ge_p3_tobytes(L61Right.bytes, &L61Right_p3); + PERF_TIMER_STOP(VERIFY_line_61rl_old); + } if (!(L61Right == L61Left)) { @@ -998,7 +1070,6 @@ bool bulletproof_VERIFY(const Bulletproof &proof) PERF_TIMER_START_BP(VERIFY_line_24_25); // Basically PAPER LINES 24-25 // Compute the curvepoints from G[i] and H[i] - ge_p3 inner_prod_p3 = ge_p3_identity; rct::key yinvpow = rct::identity(); rct::key ypow = rct::identity(); @@ -1009,6 +1080,9 @@ bool bulletproof_VERIFY(const Bulletproof &proof) winv[i] = invert(w[i]); PERF_TIMER_STOP(VERIFY_line_24_25_invert); + std::vector multiexp_data; + multiexp_data.clear(); + multiexp_data.reserve(MN*2); for (size_t i = 0; i < MN; ++i) { // Convert the index to binary IN REVERSE and construct the scalar exponent @@ -1040,9 +1114,10 @@ bool bulletproof_VERIFY(const Bulletproof &proof) sc_muladd(tmp.bytes, z.bytes, ypow.bytes, tmp.bytes); sc_mulsub(h_scalar.bytes, tmp.bytes, yinvpow.bytes, h_scalar.bytes); - // Now compute the basepoint's scalar multiplication - // Each of these could be written as a multiexp operation instead - addKeys3acc_p3(&inner_prod_p3, g_scalar, Gprecomp[i], h_scalar, Hprecomp[i]); + if (!(g_scalar == rct::zero())) + multiexp_data.push_back({g_scalar, Gi_p3[i]}); + if (!(h_scalar == rct::zero())) + multiexp_data.push_back({h_scalar, Hi_p3[i]}); if (i != MN-1) { @@ -1050,36 +1125,64 @@ bool bulletproof_VERIFY(const Bulletproof &proof) sc_mul(ypow.bytes, ypow.bytes, y.bytes); } } - rct::key inner_prod; - ge_p3_tobytes(inner_prod.bytes, &inner_prod_p3); + + rct::key inner_prod = bos_coster_heap_conv_robust(multiexp_data); PERF_TIMER_STOP(VERIFY_line_24_25); - PERF_TIMER_START_BP(VERIFY_line_26); - // PAPER LINE 26 rct::key pprime; - sc_sub(tmp.bytes, rct::zero().bytes, proof.mu.bytes); - rct::addKeys(pprime, P, rct::scalarmultBase(tmp)); - ge_p3 pprime_p3; - CHECK_AND_ASSERT_MES(ge_frombytes_vartime(&pprime_p3, pprime.bytes) == 0, false, "ge_frombytes_vartime failed"); + // multiexp does not seem to give any speedup here + if(0) + { + PERF_TIMER_START_BP(VERIFY_line_26_new); + // PAPER LINE 26 + std::vector multiexp_data; + multiexp_data.reserve(1+2*rounds); + + sc_sub(tmp.bytes, rct::zero().bytes, proof.mu.bytes); + rct::addKeys(pprime, P, rct::scalarmultBase(tmp)); + for (size_t i = 0; i < rounds; ++i) + { + sc_mul(tmp.bytes, w[i].bytes, w[i].bytes); + sc_mul(tmp2.bytes, winv[i].bytes, winv[i].bytes); + if (!(tmp == rct::zero())) + multiexp_data.push_back({tmp, proof.L[i]}); + if (!(tmp2 == rct::zero())) + multiexp_data.push_back({tmp2, proof.R[i]}); + } + sc_mul(tmp.bytes, proof.t.bytes, x_ip.bytes); + if (!(tmp == rct::zero())) + multiexp_data.push_back({tmp, rct::H}); + addKeys(pprime, pprime, bos_coster_heap_conv_robust(multiexp_data)); + PERF_TIMER_STOP(VERIFY_line_26_new); + } - for (size_t i = 0; i < rounds; ++i) { - sc_mul(tmp.bytes, w[i].bytes, w[i].bytes); - sc_mul(tmp2.bytes, winv[i].bytes, winv[i].bytes); + PERF_TIMER_START_BP(VERIFY_line_26_old); + // PAPER LINE 26 + sc_sub(tmp.bytes, rct::zero().bytes, proof.mu.bytes); + rct::addKeys(pprime, P, rct::scalarmultBase(tmp)); + ge_p3 pprime_p3; + CHECK_AND_ASSERT_MES(ge_frombytes_vartime(&pprime_p3, pprime.bytes) == 0, false, "ge_frombytes_vartime failed"); + + for (size_t i = 0; i < rounds; ++i) + { + sc_mul(tmp.bytes, w[i].bytes, w[i].bytes); + sc_mul(tmp2.bytes, winv[i].bytes, winv[i].bytes); #if 1 - ge_dsmp cacheL, cacheR; - rct::precomp(cacheL, proof.L[i]); - rct::precomp(cacheR, proof.R[i]); - addKeys3acc_p3(&pprime_p3, tmp, cacheL, tmp2, cacheR); + ge_dsmp cacheL, cacheR; + rct::precomp(cacheL, proof.L[i]); + rct::precomp(cacheR, proof.R[i]); + addKeys3acc_p3(&pprime_p3, tmp, cacheL, tmp2, cacheR); #else - rct::addKeys(pprime, pprime, rct::scalarmultKey(proof.L[i], tmp)); - rct::addKeys(pprime, pprime, rct::scalarmultKey(proof.R[i], tmp2)); + rct::addKeys(pprime, pprime, rct::scalarmultKey(proof.L[i], tmp)); + rct::addKeys(pprime, pprime, rct::scalarmultKey(proof.R[i], tmp2)); #endif + } + sc_mul(tmp.bytes, proof.t.bytes, x_ip.bytes); + addKeys_acc_p3(&pprime_p3, tmp, rct::H); + ge_p3_tobytes(pprime.bytes, &pprime_p3); + PERF_TIMER_STOP(VERIFY_line_26_old); } - sc_mul(tmp.bytes, proof.t.bytes, x_ip.bytes); - addKeys_acc_p3(&pprime_p3, tmp, rct::H); - ge_p3_tobytes(pprime.bytes, &pprime_p3); - PERF_TIMER_STOP(VERIFY_line_26); PERF_TIMER_START_BP(VERIFY_step2_check); sc_mul(tmp.bytes, proof.a.bytes, proof.b.bytes); diff --git a/src/ringct/multiexp.cc b/src/ringct/multiexp.cc new file mode 100644 index 00000000000..2545325ae23 --- /dev/null +++ b/src/ringct/multiexp.cc @@ -0,0 +1,239 @@ +// Copyright (c) 2017, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Adapted from Python code by Sarang Noether + +#include "misc_log_ex.h" +#include "common/perf_timer.h" +extern "C" +{ +#include "crypto/crypto-ops.h" +} +#include "rctOps.h" +#include "multiexp.h" + +#undef MONERO_DEFAULT_LOG_CATEGORY +#define MONERO_DEFAULT_LOG_CATEGORY "multiexp.boscoster" + +//#define MULTIEXP_PERF(x) x +#define MULTIEXP_PERF(x) + +namespace rct +{ + +static inline bool operator<(const rct::key &k0, const rct::key&k1) +{ + for (int n = 31; n >= 0; --n) + { + if (k0.bytes[n] < k1.bytes[n]) + return true; + if (k0.bytes[n] > k1.bytes[n]) + return false; + } + return false; +} + +static inline rct::key div2(const rct::key &k) +{ + rct::key res; + int carry = 0; + for (int n = 31; n >= 0; --n) + { + int new_carry = (k.bytes[n] & 1) << 7; + res.bytes[n] = k.bytes[n] / 2 + carry; + carry = new_carry; + } + return res; +} + +rct::key bos_coster_heap_conv(std::vector &data) +{ + MULTIEXP_PERF(PERF_TIMER_START_UNIT(bos_coster, 1000000)); + MULTIEXP_PERF(PERF_TIMER_START_UNIT(setup, 1000000)); + size_t points = data.size(); + CHECK_AND_ASSERT_THROW_MES(points > 1, "Not enough points"); + std::vector heap(points); + for (size_t n = 0; n < points; ++n) + heap[n] = n; + + auto Comp = [&](size_t e0, size_t e1) { return data[e0].scalar < data[e1].scalar; }; + std::make_heap(heap.begin(), heap.end(), Comp); + MULTIEXP_PERF(PERF_TIMER_STOP(setup)); + + MULTIEXP_PERF(PERF_TIMER_START_UNIT(loop, 1000000)); + MULTIEXP_PERF(PERF_TIMER_START_UNIT(pop, 1000000)); MULTIEXP_PERF(PERF_TIMER_PAUSE(pop)); + MULTIEXP_PERF(PERF_TIMER_START_UNIT(add, 1000000)); MULTIEXP_PERF(PERF_TIMER_PAUSE(add)); + MULTIEXP_PERF(PERF_TIMER_START_UNIT(sub, 1000000)); MULTIEXP_PERF(PERF_TIMER_PAUSE(sub)); + MULTIEXP_PERF(PERF_TIMER_START_UNIT(push, 1000000)); MULTIEXP_PERF(PERF_TIMER_PAUSE(push)); + while (heap.size() > 1) + { + MULTIEXP_PERF(PERF_TIMER_RESUME(pop)); + std::pop_heap(heap.begin(), heap.end(), Comp); + size_t index1 = heap.back(); + heap.pop_back(); + std::pop_heap(heap.begin(), heap.end(), Comp); + size_t index2 = heap.back(); + heap.pop_back(); + MULTIEXP_PERF(PERF_TIMER_PAUSE(pop)); + + MULTIEXP_PERF(PERF_TIMER_RESUME(add)); + ge_cached cached; + ge_p3_to_cached(&cached, &data[index1].point); + ge_p1p1 p1; + ge_add(&p1, &data[index2].point, &cached); + ge_p1p1_to_p3(&data[index2].point, &p1); + MULTIEXP_PERF(PERF_TIMER_PAUSE(add)); + + MULTIEXP_PERF(PERF_TIMER_RESUME(sub)); + sc_sub(data[index1].scalar.bytes, data[index1].scalar.bytes, data[index2].scalar.bytes); + MULTIEXP_PERF(PERF_TIMER_PAUSE(sub)); + + MULTIEXP_PERF(PERF_TIMER_RESUME(push)); + if (!(data[index1].scalar == rct::zero())) + { + heap.push_back(index1); + std::push_heap(heap.begin(), heap.end(), Comp); + } + + heap.push_back(index2); + std::push_heap(heap.begin(), heap.end(), Comp); + MULTIEXP_PERF(PERF_TIMER_PAUSE(push)); + } + MULTIEXP_PERF(PERF_TIMER_STOP(push)); + MULTIEXP_PERF(PERF_TIMER_STOP(sub)); + MULTIEXP_PERF(PERF_TIMER_STOP(add)); + MULTIEXP_PERF(PERF_TIMER_STOP(pop)); + MULTIEXP_PERF(PERF_TIMER_STOP(loop)); + + MULTIEXP_PERF(PERF_TIMER_START_UNIT(end, 1000000)); + //return rct::scalarmultKey(data[index1].point, data[index1].scalar); + std::pop_heap(heap.begin(), heap.end(), Comp); + size_t index1 = heap.back(); + heap.pop_back(); + ge_p2 p2; + ge_scalarmult(&p2, data[index1].scalar.bytes, &data[index1].point); + rct::key res; + ge_tobytes(res.bytes, &p2); + return res; +} + +rct::key bos_coster_heap_conv_robust(std::vector &data) +{ + MULTIEXP_PERF(PERF_TIMER_START_UNIT(bos_coster, 1000000)); + MULTIEXP_PERF(PERF_TIMER_START_UNIT(setup, 1000000)); + size_t points = data.size(); + CHECK_AND_ASSERT_THROW_MES(points > 1, "Not enough points"); + std::vector heap(points); + for (size_t n = 0; n < points; ++n) + heap[n] = n; + + auto Comp = [&](size_t e0, size_t e1) { return data[e0].scalar < data[e1].scalar; }; + std::make_heap(heap.begin(), heap.end(), Comp); + MULTIEXP_PERF(PERF_TIMER_STOP(setup)); + + MULTIEXP_PERF(PERF_TIMER_START_UNIT(loop, 1000000)); + MULTIEXP_PERF(PERF_TIMER_START_UNIT(pop, 1000000)); MULTIEXP_PERF(PERF_TIMER_PAUSE(pop)); + MULTIEXP_PERF(PERF_TIMER_START_UNIT(div, 1000000)); MULTIEXP_PERF(PERF_TIMER_PAUSE(div)); + MULTIEXP_PERF(PERF_TIMER_START_UNIT(add, 1000000)); MULTIEXP_PERF(PERF_TIMER_PAUSE(add)); + MULTIEXP_PERF(PERF_TIMER_START_UNIT(sub, 1000000)); MULTIEXP_PERF(PERF_TIMER_PAUSE(sub)); + MULTIEXP_PERF(PERF_TIMER_START_UNIT(push, 1000000)); MULTIEXP_PERF(PERF_TIMER_PAUSE(push)); + while (heap.size() > 1) + { + MULTIEXP_PERF(PERF_TIMER_RESUME(pop)); + std::pop_heap(heap.begin(), heap.end(), Comp); + size_t index1 = heap.back(); + heap.pop_back(); + std::pop_heap(heap.begin(), heap.end(), Comp); + size_t index2 = heap.back(); + heap.pop_back(); + MULTIEXP_PERF(PERF_TIMER_PAUSE(pop)); + + ge_cached cached; + ge_p1p1 p1; + + MULTIEXP_PERF(PERF_TIMER_RESUME(div)); + while (1) + { + rct::key s1_2 = div2(data[index1].scalar); + if (!(data[index2].scalar < s1_2)) + break; + if (data[index1].scalar.bytes[0] & 1) + { + data.resize(data.size()+1); + data.back().scalar = rct::identity(); + data.back().point = data[index1].point; + heap.push_back(data.size() - 1); + std::push_heap(heap.begin(), heap.end(), Comp); + } + data[index1].scalar = div2(data[index1].scalar); + ge_p3_to_cached(&cached, &data[index1].point); + ge_add(&p1, &data[index1].point, &cached); + ge_p1p1_to_p3(&data[index1].point, &p1); + } + MULTIEXP_PERF(PERF_TIMER_PAUSE(div)); + + MULTIEXP_PERF(PERF_TIMER_RESUME(add)); + ge_p3_to_cached(&cached, &data[index1].point); + ge_add(&p1, &data[index2].point, &cached); + ge_p1p1_to_p3(&data[index2].point, &p1); + MULTIEXP_PERF(PERF_TIMER_PAUSE(add)); + + MULTIEXP_PERF(PERF_TIMER_RESUME(sub)); + sc_sub(data[index1].scalar.bytes, data[index1].scalar.bytes, data[index2].scalar.bytes); + MULTIEXP_PERF(PERF_TIMER_PAUSE(sub)); + + MULTIEXP_PERF(PERF_TIMER_RESUME(push)); + if (!(data[index1].scalar == rct::zero())) + { + heap.push_back(index1); + std::push_heap(heap.begin(), heap.end(), Comp); + } + + heap.push_back(index2); + std::push_heap(heap.begin(), heap.end(), Comp); + MULTIEXP_PERF(PERF_TIMER_PAUSE(push)); + } + MULTIEXP_PERF(PERF_TIMER_STOP(push)); + MULTIEXP_PERF(PERF_TIMER_STOP(sub)); + MULTIEXP_PERF(PERF_TIMER_STOP(add)); + MULTIEXP_PERF(PERF_TIMER_STOP(pop)); + MULTIEXP_PERF(PERF_TIMER_STOP(loop)); + + MULTIEXP_PERF(PERF_TIMER_START_UNIT(end, 1000000)); + //return rct::scalarmultKey(data[index1].point, data[index1].scalar); + std::pop_heap(heap.begin(), heap.end(), Comp); + size_t index1 = heap.back(); + heap.pop_back(); + ge_p2 p2; + ge_scalarmult(&p2, data[index1].scalar.bytes, &data[index1].point); + rct::key res; + ge_tobytes(res.bytes, &p2); + return res; +} + +} diff --git a/src/ringct/multiexp.h b/src/ringct/multiexp.h new file mode 100644 index 00000000000..108db7c39ca --- /dev/null +++ b/src/ringct/multiexp.h @@ -0,0 +1,60 @@ +// Copyright (c) 2017, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Adapted from Python code by Sarang Noether + +#pragma once + +#ifndef MULTIEXP_H +#define MULTIEXP_H + +#include +#include "crypto/crypto.h" +#include "rctTypes.h" + +namespace rct +{ + +struct MultiexpData { + rct::key scalar; + ge_p3 point; + + MultiexpData() {} + MultiexpData(const rct::key &s, const ge_p3 &p): scalar(s), point(p) {} + MultiexpData(const rct::key &s, const rct::key &p): scalar(s) + { + CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&point, p.bytes) == 0, "ge_frombytes_vartime failed"); + } +}; + +rct::key bos_coster_heap_conv(std::vector &data); +rct::key bos_coster_heap_conv_robust(std::vector &data); + +} + +#endif From 939bc2233281c47427c9987fc5310cfb77b085f9 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 14 Jan 2018 23:06:55 +0000 Subject: [PATCH 0343/1404] add Straus multiexp --- src/ringct/bulletproofs.cc | 190 ++++++------------------- src/ringct/multiexp.cc | 137 +++++++++++++++++- src/ringct/multiexp.h | 5 +- tests/performance_tests/CMakeLists.txt | 1 + tests/performance_tests/main.cpp | 15 ++ tests/performance_tests/multiexp.h | 81 +++++++++++ 6 files changed, 279 insertions(+), 150 deletions(-) create mode 100644 tests/performance_tests/multiexp.h diff --git a/src/ringct/bulletproofs.cc b/src/ringct/bulletproofs.cc index 057f190290b..1c29b1b99cb 100644 --- a/src/ringct/bulletproofs.cc +++ b/src/ringct/bulletproofs.cc @@ -67,6 +67,14 @@ static const rct::keyV twoN = vector_powers(TWO, maxN); static const rct::key ip12 = inner_product(oneN, twoN); static boost::mutex init_mutex; +static inline rct::key multiexp(const std::vector &data, bool HiGi) +{ + if (HiGi || data.size() < 1000) + return straus(data, HiGi); + else + return bos_coster_heap_conv_robust(data); +} + //addKeys3acc_p3 //aAbB += a*A + b*B where a, b are scalars, A, B are curve points //A and B must be input after applying "precomp" @@ -126,35 +134,15 @@ static rct::key vector_exponent(const rct::keyV &a, const rct::keyV &b) { CHECK_AND_ASSERT_THROW_MES(a.size() == b.size(), "Incompatible sizes of a and b"); CHECK_AND_ASSERT_THROW_MES(a.size() <= maxN*maxM, "Incompatible sizes of a and maxN"); -#if 1 + std::vector multiexp_data; multiexp_data.reserve(a.size()*2); for (size_t i = 0; i < a.size(); ++i) { - if (!(a[i] == rct::zero())) - { - multiexp_data.resize(multiexp_data.size() + 1); - multiexp_data.back().scalar = a[i]; - multiexp_data.back().point = Gi_p3[i]; - } - if (!(b[i] == rct::zero())) - { - multiexp_data.resize(multiexp_data.size() + 1); - multiexp_data.back().scalar = b[i]; - multiexp_data.back().point = Hi_p3[i]; - } + multiexp_data.emplace_back(a[i], Gi_p3[i]); + multiexp_data.emplace_back(b[i], Hi_p3[i]); } - return bos_coster_heap_conv_robust(multiexp_data); -#else - ge_p3 res_p3 = ge_p3_identity; - for (size_t i = 0; i < a.size(); ++i) - { - rct::addKeys3acc_p3(&res_p3, a[i], Gprecomp[i], b[i], Hprecomp[i]); - } - rct::key res; - ge_p3_tobytes(res.bytes, &res_p3); - return res; -#endif + return multiexp(multiexp_data, true); } /* Compute a custom vector-scalar commitment */ @@ -164,63 +152,19 @@ static rct::key vector_exponent_custom(const rct::keyV &A, const rct::keyV &B, c CHECK_AND_ASSERT_THROW_MES(a.size() == b.size(), "Incompatible sizes of a and b"); CHECK_AND_ASSERT_THROW_MES(a.size() == A.size(), "Incompatible sizes of a and A"); CHECK_AND_ASSERT_THROW_MES(a.size() <= maxN*maxM, "Incompatible sizes of a and maxN"); -#if 1 + std::vector multiexp_data; multiexp_data.reserve(a.size()*2); for (size_t i = 0; i < a.size(); ++i) { - if (!(a[i] == rct::zero())) - { - multiexp_data.resize(multiexp_data.size() + 1); - multiexp_data.back().scalar = a[i]; - CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&multiexp_data.back().point, A[i].bytes) == 0, "ge_frombytes_vartime failed"); - } - if (!(b[i] == rct::zero())) - { - multiexp_data.resize(multiexp_data.size() + 1); - multiexp_data.back().scalar = b[i]; - CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&multiexp_data.back().point, B[i].bytes) == 0, "ge_frombytes_vartime failed"); - } - } - return bos_coster_heap_conv_robust(multiexp_data); -#else - ge_p3 res_p3 = ge_p3_identity; - for (size_t i = 0; i < a.size(); ++i) - { -#if 0 - rct::key term; - // we happen to know where A and B might fall, so don't bother checking the rest - ge_dsmp *Acache = NULL, *Bcache = NULL; - ge_dsmp Acache_custom[1], Bcache_custom[1]; - if (Gi[i] == A[i]) - Acache = Gprecomp + i; - else if (i<32 && Gi[i+32] == A[i]) - Acache = Gprecomp + i + 32; - else - { - rct::precomp(Acache_custom[0], A[i]); - Acache = Acache_custom; - } - if (i == 0 && B[i] == Hi[0]) - Bcache = Hprecomp; - else - { - rct::precomp(Bcache_custom[0], B[i]); - Bcache = Bcache_custom; - } - rct::addKeys3(term, a[i], *Acache, b[i], *Bcache); - rct::addKeys(res, res, term); -#else - ge_dsmp Acache, Bcache; - rct::precomp(Bcache, B[i]); - rct::precomp(Acache, A[i]); - addKeys3acc_p3(&res_p3, a[i], Acache, b[i], Bcache); -#endif + multiexp_data.resize(multiexp_data.size() + 1); + multiexp_data.back().scalar = a[i]; + CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&multiexp_data.back().point, A[i].bytes) == 0, "ge_frombytes_vartime failed"); + multiexp_data.resize(multiexp_data.size() + 1); + multiexp_data.back().scalar = b[i]; + CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&multiexp_data.back().point, B[i].bytes) == 0, "ge_frombytes_vartime failed"); } - rct::key res; - ge_p3_tobytes(res.bytes, &res_p3); - return res; -#endif + return multiexp(multiexp_data, false); } /* Given a scalar, construct a vector of powers */ @@ -986,26 +930,23 @@ bool bulletproof_VERIFY(const Bulletproof &proof) } PERF_TIMER_STOP(VERIFY_line_61); - // multiexp is slower for small numbers of calcs - if (M >= 16) + // bos coster is slower for small numbers of calcs, straus seems not + if (1) { PERF_TIMER_START_BP(VERIFY_line_61rl_new); sc_muladd(tmp.bytes, z.bytes, ip1y.bytes, k.bytes); std::vector multiexp_data; - multiexp_data.reserve(3+M); - multiexp_data.push_back({tmp, rct::H}); - for (size_t j = 0; j < M; j++) + multiexp_data.reserve(3+proof.V.size()); + multiexp_data.emplace_back(tmp, rct::H); + for (size_t j = 0; j < proof.V.size(); j++) { - if (!(zpow[j+2] == rct::zero())) - multiexp_data.push_back({zpow[j+2], j < proof.V.size() ? proof.V[j] : rct::identity()}); + multiexp_data.emplace_back(zpow[j+2], proof.V[j]); } - if (!(x == rct::zero())) - multiexp_data.push_back({x, proof.T1}); + multiexp_data.emplace_back(x, proof.T1); rct::key xsq; sc_mul(xsq.bytes, x.bytes, x.bytes); - if (!(xsq == rct::zero())) - multiexp_data.push_back({xsq, proof.T2}); - L61Right = bos_coster_heap_conv_robust(multiexp_data); + multiexp_data.emplace_back(xsq, proof.T2); + L61Right = multiexp(multiexp_data, false); PERF_TIMER_STOP(VERIFY_line_61rl_new); } else @@ -1114,10 +1055,8 @@ bool bulletproof_VERIFY(const Bulletproof &proof) sc_muladd(tmp.bytes, z.bytes, ypow.bytes, tmp.bytes); sc_mulsub(h_scalar.bytes, tmp.bytes, yinvpow.bytes, h_scalar.bytes); - if (!(g_scalar == rct::zero())) - multiexp_data.push_back({g_scalar, Gi_p3[i]}); - if (!(h_scalar == rct::zero())) - multiexp_data.push_back({h_scalar, Hi_p3[i]}); + multiexp_data.emplace_back(g_scalar, Gi_p3[i]); + multiexp_data.emplace_back(h_scalar, Hi_p3[i]); if (i != MN-1) { @@ -1126,63 +1065,28 @@ bool bulletproof_VERIFY(const Bulletproof &proof) } } - rct::key inner_prod = bos_coster_heap_conv_robust(multiexp_data); + rct::key inner_prod = multiexp(multiexp_data, true); PERF_TIMER_STOP(VERIFY_line_24_25); + // PAPER LINE 26 rct::key pprime; - // multiexp does not seem to give any speedup here - if(0) - { - PERF_TIMER_START_BP(VERIFY_line_26_new); - // PAPER LINE 26 - std::vector multiexp_data; - multiexp_data.reserve(1+2*rounds); - - sc_sub(tmp.bytes, rct::zero().bytes, proof.mu.bytes); - rct::addKeys(pprime, P, rct::scalarmultBase(tmp)); - for (size_t i = 0; i < rounds; ++i) - { - sc_mul(tmp.bytes, w[i].bytes, w[i].bytes); - sc_mul(tmp2.bytes, winv[i].bytes, winv[i].bytes); - if (!(tmp == rct::zero())) - multiexp_data.push_back({tmp, proof.L[i]}); - if (!(tmp2 == rct::zero())) - multiexp_data.push_back({tmp2, proof.R[i]}); - } - sc_mul(tmp.bytes, proof.t.bytes, x_ip.bytes); - if (!(tmp == rct::zero())) - multiexp_data.push_back({tmp, rct::H}); - addKeys(pprime, pprime, bos_coster_heap_conv_robust(multiexp_data)); - PERF_TIMER_STOP(VERIFY_line_26_new); - } + PERF_TIMER_START_BP(VERIFY_line_26_new); + multiexp_data.clear(); + multiexp_data.reserve(1+2*rounds); + sc_sub(tmp.bytes, rct::zero().bytes, proof.mu.bytes); + rct::addKeys(pprime, P, rct::scalarmultBase(tmp)); + for (size_t i = 0; i < rounds; ++i) { - PERF_TIMER_START_BP(VERIFY_line_26_old); - // PAPER LINE 26 - sc_sub(tmp.bytes, rct::zero().bytes, proof.mu.bytes); - rct::addKeys(pprime, P, rct::scalarmultBase(tmp)); - ge_p3 pprime_p3; - CHECK_AND_ASSERT_MES(ge_frombytes_vartime(&pprime_p3, pprime.bytes) == 0, false, "ge_frombytes_vartime failed"); - - for (size_t i = 0; i < rounds; ++i) - { - sc_mul(tmp.bytes, w[i].bytes, w[i].bytes); - sc_mul(tmp2.bytes, winv[i].bytes, winv[i].bytes); -#if 1 - ge_dsmp cacheL, cacheR; - rct::precomp(cacheL, proof.L[i]); - rct::precomp(cacheR, proof.R[i]); - addKeys3acc_p3(&pprime_p3, tmp, cacheL, tmp2, cacheR); -#else - rct::addKeys(pprime, pprime, rct::scalarmultKey(proof.L[i], tmp)); - rct::addKeys(pprime, pprime, rct::scalarmultKey(proof.R[i], tmp2)); -#endif - } - sc_mul(tmp.bytes, proof.t.bytes, x_ip.bytes); - addKeys_acc_p3(&pprime_p3, tmp, rct::H); - ge_p3_tobytes(pprime.bytes, &pprime_p3); - PERF_TIMER_STOP(VERIFY_line_26_old); + sc_mul(tmp.bytes, w[i].bytes, w[i].bytes); + sc_mul(tmp2.bytes, winv[i].bytes, winv[i].bytes); + multiexp_data.emplace_back(tmp, proof.L[i]); + multiexp_data.emplace_back(tmp2, proof.R[i]); } + sc_mul(tmp.bytes, proof.t.bytes, x_ip.bytes); + multiexp_data.emplace_back(tmp, rct::H); + addKeys(pprime, pprime, multiexp(multiexp_data, false)); + PERF_TIMER_STOP(VERIFY_line_26_new); PERF_TIMER_START_BP(VERIFY_step2_check); sc_mul(tmp.bytes, proof.a.bytes, proof.b.bytes); diff --git a/src/ringct/multiexp.cc b/src/ringct/multiexp.cc index 2545325ae23..b70d92d4672 100644 --- a/src/ringct/multiexp.cc +++ b/src/ringct/multiexp.cc @@ -38,7 +38,7 @@ extern "C" #include "multiexp.h" #undef MONERO_DEFAULT_LOG_CATEGORY -#define MONERO_DEFAULT_LOG_CATEGORY "multiexp.boscoster" +#define MONERO_DEFAULT_LOG_CATEGORY "multiexp" //#define MULTIEXP_PERF(x) x #define MULTIEXP_PERF(x) @@ -71,7 +71,15 @@ static inline rct::key div2(const rct::key &k) return res; } -rct::key bos_coster_heap_conv(std::vector &data) +static inline rct::key pow2(size_t n) +{ + CHECK_AND_ASSERT_THROW_MES(n < 256, "Invalid pow2 argument"); + rct::key res = rct::zero(); + res[n >> 3] |= 1<<(n&7); + return res; +} + +rct::key bos_coster_heap_conv(std::vector data) { MULTIEXP_PERF(PERF_TIMER_START_UNIT(bos_coster, 1000000)); MULTIEXP_PERF(PERF_TIMER_START_UNIT(setup, 1000000)); @@ -142,15 +150,20 @@ rct::key bos_coster_heap_conv(std::vector &data) return res; } -rct::key bos_coster_heap_conv_robust(std::vector &data) +rct::key bos_coster_heap_conv_robust(std::vector data) { MULTIEXP_PERF(PERF_TIMER_START_UNIT(bos_coster, 1000000)); MULTIEXP_PERF(PERF_TIMER_START_UNIT(setup, 1000000)); size_t points = data.size(); CHECK_AND_ASSERT_THROW_MES(points > 1, "Not enough points"); - std::vector heap(points); + std::vector heap; + heap.reserve(points); for (size_t n = 0; n < points; ++n) - heap[n] = n; + { + if (!(data[n].scalar == rct::zero()) && memcmp(&data[n].point, &ge_p3_identity, sizeof(ge_p3))) + heap.push_back(n); + } + points = heap.size(); auto Comp = [&](size_t e0, size_t e1) { return data[e0].scalar < data[e1].scalar; }; std::make_heap(heap.begin(), heap.end(), Comp); @@ -236,4 +249,118 @@ rct::key bos_coster_heap_conv_robust(std::vector &data) return res; } +rct::key straus(const std::vector &data, bool HiGi) +{ + MULTIEXP_PERF(PERF_TIMER_UNIT(straus, 1000000)); + + MULTIEXP_PERF(PERF_TIMER_START_UNIT(setup, 1000000)); + static constexpr unsigned int c = 4; + static constexpr unsigned int mask = (1<> HiGi_multiples; + std::vector> local_multiples, &multiples = HiGi ? HiGi_multiples : local_multiples; + ge_cached cached; + ge_p1p1 p1; + ge_p3 p3; + + std::vector skip(data.size()); + for (size_t i = 0; i < data.size(); ++i) + skip[i] = data[i].scalar == rct::zero() || !memcmp(&data[i].point, &ge_p3_identity, sizeof(ge_p3)); + + MULTIEXP_PERF(PERF_TIMER_START_UNIT(multiples, 1000000)); + multiples.resize(1<> digits; + digits.resize(data.size()); + for (size_t j = 0; j < data.size(); ++j) + { + digits[j].resize(256); + unsigned char bytes33[33]; + memcpy(bytes33, data[j].scalar.bytes, 32); + bytes33[32] = 0; +#if 1 + static_assert(c == 4, "optimized version needs c == 4"); + const unsigned char *bytes = bytes33; + unsigned int i; + for (i = 0; i < 256; i += 8, bytes++) + { + digits[j][i] = bytes[0] & 0xf; + digits[j][i+1] = (bytes[0] >> 1) & 0xf; + digits[j][i+2] = (bytes[0] >> 2) & 0xf; + digits[j][i+3] = (bytes[0] >> 3) & 0xf; + digits[j][i+4] = ((bytes[0] >> 4) | (bytes[1]<<4)) & 0xf; + digits[j][i+5] = ((bytes[0] >> 5) | (bytes[1]<<3)) & 0xf; + digits[j][i+6] = ((bytes[0] >> 6) | (bytes[1]<<2)) & 0xf; + digits[j][i+7] = ((bytes[0] >> 7) | (bytes[1]<<1)) & 0xf; + } +#elif 1 + for (size_t i = 0; i < 256; ++i) + digits[j][i] = ((bytes[i>>3] | (bytes[(i>>3)+1]<<8)) >> (i&7)) & mask; +#else + rct::key shifted = data[j].scalar; + for (size_t i = 0; i < 256; ++i) + { + digits[j][i] = shifted.bytes[0] & 0xf; + shifted = div2(shifted, (256-i)>>3); + } +#endif + } + MULTIEXP_PERF(PERF_TIMER_STOP(digits)); + + rct::key maxscalar = rct::zero(); + for (size_t i = 0; i < data.size(); ++i) + if (maxscalar < data[i].scalar) + maxscalar = data[i].scalar; + size_t i = 0; + while (i < 256 && !(maxscalar < pow2(i))) + i += c; + MULTIEXP_PERF(PERF_TIMER_STOP(setup)); + + ge_p3 res_p3 = ge_p3_identity; + if (!(i < c)) + goto skipfirst; + while (!(i < c)) + { + for (size_t j = 0; j < c; ++j) + { + ge_p3_to_cached(&cached, &res_p3); + ge_add(&p1, &res_p3, &cached); + ge_p1p1_to_p3(&res_p3, &p1); + } +skipfirst: + i -= c; + for (size_t j = 0; j < data.size(); ++j) + { + if (skip[j]) + continue; + int digit = digits[j][i]; + if (digit) + { + ge_add(&p1, &res_p3, &multiples[digit][j]); + ge_p1p1_to_p3(&res_p3, &p1); + } + } + } + + rct::key res; + ge_p3_tobytes(res.bytes, &res_p3); + return res; +} + } diff --git a/src/ringct/multiexp.h b/src/ringct/multiexp.h index 108db7c39ca..cc53e633ed3 100644 --- a/src/ringct/multiexp.h +++ b/src/ringct/multiexp.h @@ -52,8 +52,9 @@ struct MultiexpData { } }; -rct::key bos_coster_heap_conv(std::vector &data); -rct::key bos_coster_heap_conv_robust(std::vector &data); +rct::key bos_coster_heap_conv(std::vector data); +rct::key bos_coster_heap_conv_robust(std::vector data); +rct::key straus(const std::vector &data, bool HiGi = false); } diff --git a/tests/performance_tests/CMakeLists.txt b/tests/performance_tests/CMakeLists.txt index 47f441dda26..3ffd84aa602 100644 --- a/tests/performance_tests/CMakeLists.txt +++ b/tests/performance_tests/CMakeLists.txt @@ -45,6 +45,7 @@ set(performance_tests_headers range_proof.h bulletproof.h crypto_ops.h + multiexp.h multi_tx_test_base.h performance_tests.h performance_utils.h diff --git a/tests/performance_tests/main.cpp b/tests/performance_tests/main.cpp index 5b7a30f9623..739c6cc87e1 100644 --- a/tests/performance_tests/main.cpp +++ b/tests/performance_tests/main.cpp @@ -56,6 +56,7 @@ #include "rct_mlsag.h" #include "bulletproof.h" #include "crypto_ops.h" +#include "multiexp.h" namespace po = boost::program_options; @@ -196,6 +197,20 @@ int main(int argc, char** argv) TEST_PERFORMANCE1(filter, test_crypto_ops, op_addKeys3); TEST_PERFORMANCE1(filter, test_crypto_ops, op_addKeys3_2); + TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_bos_coster, 2); + TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_bos_coster, 8); + TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_bos_coster, 16); + TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_bos_coster, 256); + TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_bos_coster, 1024); + TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_bos_coster, 4096); + + TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_straus, 2); + TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_straus, 8); + TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_straus, 16); + TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_straus, 256); + TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_straus, 1024); + TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_straus, 4096); + std::cout << "Tests finished. Elapsed time: " << timer.elapsed_ms() / 1000 << " sec" << std::endl; return 0; diff --git a/tests/performance_tests/multiexp.h b/tests/performance_tests/multiexp.h new file mode 100644 index 00000000000..ac5f60fdf09 --- /dev/null +++ b/tests/performance_tests/multiexp.h @@ -0,0 +1,81 @@ +// Copyright (c) 2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers + +#pragma once + +#include +#include "ringct/rctOps.h" +#include "ringct/multiexp.h" + +enum test_multiexp_algorithm +{ + multiexp_bos_coster, + multiexp_straus, +}; + +template +class test_multiexp +{ +public: + static const size_t loop_count = npoints >= 1024 ? 10 : npoints < 256 ? 1000 : 100; + + bool init() + { + data.resize(npoints); + res = rct::identity(); + for (size_t n = 0; n < npoints; ++n) + { + data[n].scalar = rct::skGen(); + rct::key point = rct::scalarmultBase(rct::skGen()); + if (ge_frombytes_vartime(&data[n].point, point.bytes)) + return false; + rct::key kn = rct::scalarmultKey(point, data[n].scalar); + res = rct::addKeys(res, kn); + } + return true; + } + + bool test() + { + switch (algorithm) + { + case multiexp_bos_coster: + return res == bos_coster_heap_conv_robust(data); + case multiexp_straus: + return res == straus(data, false); + default: + return false; + } + } + +private: + std::vector data; + rct::key res; +}; From 0793184bd008b51cc6865738a86bcd10792d42e0 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 14 Jan 2018 23:02:34 +0000 Subject: [PATCH 0344/1404] performance_tests: add a --verbose flag, and default to terse --- tests/performance_tests/main.cpp | 221 ++++++++++---------- tests/performance_tests/performance_tests.h | 35 ++-- 2 files changed, 135 insertions(+), 121 deletions(-) diff --git a/tests/performance_tests/main.cpp b/tests/performance_tests/main.cpp index 739c6cc87e1..ba1640263d2 100644 --- a/tests/performance_tests/main.cpp +++ b/tests/performance_tests/main.cpp @@ -71,7 +71,9 @@ int main(int argc, char** argv) po::options_description desc_options("Command line options"); const command_line::arg_descriptor arg_filter = { "filter", "Regular expression filter for which tests to run" }; - command_line::add_arg(desc_options, arg_filter); + const command_line::arg_descriptor arg_verbose = { "verbose", "Verbose output", false }; + command_line::add_arg(desc_options, arg_filter, ""); + command_line::add_arg(desc_options, arg_verbose, ""); po::variables_map vm; bool r = command_line::handle_error_helper(desc_options, [&]() @@ -84,118 +86,119 @@ int main(int argc, char** argv) return 1; const std::string filter = tools::glob_to_regex(command_line::get_arg(vm, arg_filter)); + bool verbose = command_line::get_arg(vm, arg_verbose); performance_timer timer; timer.start(); - TEST_PERFORMANCE3(filter, test_construct_tx, 1, 1, false); - TEST_PERFORMANCE3(filter, test_construct_tx, 1, 2, false); - TEST_PERFORMANCE3(filter, test_construct_tx, 1, 10, false); - TEST_PERFORMANCE3(filter, test_construct_tx, 1, 100, false); - TEST_PERFORMANCE3(filter, test_construct_tx, 1, 1000, false); - - TEST_PERFORMANCE3(filter, test_construct_tx, 2, 1, false); - TEST_PERFORMANCE3(filter, test_construct_tx, 2, 2, false); - TEST_PERFORMANCE3(filter, test_construct_tx, 2, 10, false); - TEST_PERFORMANCE3(filter, test_construct_tx, 2, 100, false); - - TEST_PERFORMANCE3(filter, test_construct_tx, 10, 1, false); - TEST_PERFORMANCE3(filter, test_construct_tx, 10, 2, false); - TEST_PERFORMANCE3(filter, test_construct_tx, 10, 10, false); - TEST_PERFORMANCE3(filter, test_construct_tx, 10, 100, false); - - TEST_PERFORMANCE3(filter, test_construct_tx, 100, 1, false); - TEST_PERFORMANCE3(filter, test_construct_tx, 100, 2, false); - TEST_PERFORMANCE3(filter, test_construct_tx, 100, 10, false); - TEST_PERFORMANCE3(filter, test_construct_tx, 100, 100, false); - - TEST_PERFORMANCE3(filter, test_construct_tx, 2, 1, true); - TEST_PERFORMANCE3(filter, test_construct_tx, 2, 2, true); - TEST_PERFORMANCE3(filter, test_construct_tx, 2, 10, true); - - TEST_PERFORMANCE3(filter, test_construct_tx, 10, 1, true); - TEST_PERFORMANCE3(filter, test_construct_tx, 10, 2, true); - TEST_PERFORMANCE3(filter, test_construct_tx, 10, 10, true); - - TEST_PERFORMANCE3(filter, test_construct_tx, 100, 1, true); - TEST_PERFORMANCE3(filter, test_construct_tx, 100, 2, true); - TEST_PERFORMANCE3(filter, test_construct_tx, 100, 10, true); - - TEST_PERFORMANCE2(filter, test_check_tx_signature, 1, false); - TEST_PERFORMANCE2(filter, test_check_tx_signature, 2, false); - TEST_PERFORMANCE2(filter, test_check_tx_signature, 10, false); - TEST_PERFORMANCE2(filter, test_check_tx_signature, 100, false); - - TEST_PERFORMANCE2(filter, test_check_tx_signature, 2, true); - TEST_PERFORMANCE2(filter, test_check_tx_signature, 10, true); - TEST_PERFORMANCE2(filter, test_check_tx_signature, 100, true); - - TEST_PERFORMANCE0(filter, test_is_out_to_acc); - TEST_PERFORMANCE0(filter, test_is_out_to_acc_precomp); - TEST_PERFORMANCE0(filter, test_generate_key_image_helper); - TEST_PERFORMANCE0(filter, test_generate_key_derivation); - TEST_PERFORMANCE0(filter, test_generate_key_image); - TEST_PERFORMANCE0(filter, test_derive_public_key); - TEST_PERFORMANCE0(filter, test_derive_secret_key); - TEST_PERFORMANCE0(filter, test_ge_frombytes_vartime); - TEST_PERFORMANCE0(filter, test_generate_keypair); - TEST_PERFORMANCE0(filter, test_sc_reduce32); - - TEST_PERFORMANCE2(filter, test_wallet2_expand_subaddresses, 50, 200); - - TEST_PERFORMANCE0(filter, test_cn_slow_hash); - TEST_PERFORMANCE1(filter, test_cn_fast_hash, 32); - TEST_PERFORMANCE1(filter, test_cn_fast_hash, 16384); - - TEST_PERFORMANCE3(filter, test_ringct_mlsag, 1, 3, false); - TEST_PERFORMANCE3(filter, test_ringct_mlsag, 1, 5, false); - TEST_PERFORMANCE3(filter, test_ringct_mlsag, 1, 10, false); - TEST_PERFORMANCE3(filter, test_ringct_mlsag, 1, 100, false); - TEST_PERFORMANCE3(filter, test_ringct_mlsag, 1, 3, true); - TEST_PERFORMANCE3(filter, test_ringct_mlsag, 1, 5, true); - TEST_PERFORMANCE3(filter, test_ringct_mlsag, 1, 10, true); - TEST_PERFORMANCE3(filter, test_ringct_mlsag, 1, 100, true); - - TEST_PERFORMANCE2(filter, test_equality, memcmp32, true); - TEST_PERFORMANCE2(filter, test_equality, memcmp32, false); - TEST_PERFORMANCE2(filter, test_equality, verify32, false); - TEST_PERFORMANCE2(filter, test_equality, verify32, false); - - TEST_PERFORMANCE1(filter, test_range_proof, true); - TEST_PERFORMANCE1(filter, test_range_proof, false); - - TEST_PERFORMANCE2(filter, test_bulletproof, true, 1); - TEST_PERFORMANCE2(filter, test_bulletproof, false, 1); - - TEST_PERFORMANCE2(filter, test_bulletproof, true, 2); - TEST_PERFORMANCE2(filter, test_bulletproof, false, 2); - - TEST_PERFORMANCE2(filter, test_bulletproof, true, 15); - TEST_PERFORMANCE2(filter, test_bulletproof, false, 15); - - TEST_PERFORMANCE3(filter, test_ringct_mlsag, 1, 3, false); - TEST_PERFORMANCE3(filter, test_ringct_mlsag, 1, 5, false); - TEST_PERFORMANCE3(filter, test_ringct_mlsag, 1, 10, false); - TEST_PERFORMANCE3(filter, test_ringct_mlsag, 1, 100, false); - TEST_PERFORMANCE3(filter, test_ringct_mlsag, 1, 3, true); - TEST_PERFORMANCE3(filter, test_ringct_mlsag, 1, 5, true); - TEST_PERFORMANCE3(filter, test_ringct_mlsag, 1, 10, true); - TEST_PERFORMANCE3(filter, test_ringct_mlsag, 1, 100, true); - - TEST_PERFORMANCE1(filter, test_crypto_ops, op_sc_add); - TEST_PERFORMANCE1(filter, test_crypto_ops, op_sc_sub); - TEST_PERFORMANCE1(filter, test_crypto_ops, op_sc_mul); - TEST_PERFORMANCE1(filter, test_crypto_ops, op_ge_add_raw); - TEST_PERFORMANCE1(filter, test_crypto_ops, op_ge_add_p3_p3); - TEST_PERFORMANCE1(filter, test_crypto_ops, op_addKeys); - TEST_PERFORMANCE1(filter, test_crypto_ops, op_scalarmultBase); - TEST_PERFORMANCE1(filter, test_crypto_ops, op_scalarmultKey); - TEST_PERFORMANCE1(filter, test_crypto_ops, op_ge_double_scalarmult_base_vartime); - TEST_PERFORMANCE1(filter, test_crypto_ops, op_ge_double_scalarmult_precomp_vartime); - TEST_PERFORMANCE1(filter, test_crypto_ops, op_ge_double_scalarmult_precomp_vartime2); - TEST_PERFORMANCE1(filter, test_crypto_ops, op_addKeys2); - TEST_PERFORMANCE1(filter, test_crypto_ops, op_addKeys3); - TEST_PERFORMANCE1(filter, test_crypto_ops, op_addKeys3_2); + TEST_PERFORMANCE3(filter, verbose, test_construct_tx, 1, 1, false); + TEST_PERFORMANCE3(filter, verbose, test_construct_tx, 1, 2, false); + TEST_PERFORMANCE3(filter, verbose, test_construct_tx, 1, 10, false); + TEST_PERFORMANCE3(filter, verbose, test_construct_tx, 1, 100, false); + TEST_PERFORMANCE3(filter, verbose, test_construct_tx, 1, 1000, false); + + TEST_PERFORMANCE3(filter, verbose, test_construct_tx, 2, 1, false); + TEST_PERFORMANCE3(filter, verbose, test_construct_tx, 2, 2, false); + TEST_PERFORMANCE3(filter, verbose, test_construct_tx, 2, 10, false); + TEST_PERFORMANCE3(filter, verbose, test_construct_tx, 2, 100, false); + + TEST_PERFORMANCE3(filter, verbose, test_construct_tx, 10, 1, false); + TEST_PERFORMANCE3(filter, verbose, test_construct_tx, 10, 2, false); + TEST_PERFORMANCE3(filter, verbose, test_construct_tx, 10, 10, false); + TEST_PERFORMANCE3(filter, verbose, test_construct_tx, 10, 100, false); + + TEST_PERFORMANCE3(filter, verbose, test_construct_tx, 100, 1, false); + TEST_PERFORMANCE3(filter, verbose, test_construct_tx, 100, 2, false); + TEST_PERFORMANCE3(filter, verbose, test_construct_tx, 100, 10, false); + TEST_PERFORMANCE3(filter, verbose, test_construct_tx, 100, 100, false); + + TEST_PERFORMANCE3(filter, verbose, test_construct_tx, 2, 1, true); + TEST_PERFORMANCE3(filter, verbose, test_construct_tx, 2, 2, true); + TEST_PERFORMANCE3(filter, verbose, test_construct_tx, 2, 10, true); + + TEST_PERFORMANCE3(filter, verbose, test_construct_tx, 10, 1, true); + TEST_PERFORMANCE3(filter, verbose, test_construct_tx, 10, 2, true); + TEST_PERFORMANCE3(filter, verbose, test_construct_tx, 10, 10, true); + + TEST_PERFORMANCE3(filter, verbose, test_construct_tx, 100, 1, true); + TEST_PERFORMANCE3(filter, verbose, test_construct_tx, 100, 2, true); + TEST_PERFORMANCE3(filter, verbose, test_construct_tx, 100, 10, true); + + TEST_PERFORMANCE2(filter, verbose, test_check_tx_signature, 1, false); + TEST_PERFORMANCE2(filter, verbose, test_check_tx_signature, 2, false); + TEST_PERFORMANCE2(filter, verbose, test_check_tx_signature, 10, false); + TEST_PERFORMANCE2(filter, verbose, test_check_tx_signature, 100, false); + + TEST_PERFORMANCE2(filter, verbose, test_check_tx_signature, 2, true); + TEST_PERFORMANCE2(filter, verbose, test_check_tx_signature, 10, true); + TEST_PERFORMANCE2(filter, verbose, test_check_tx_signature, 100, true); + + TEST_PERFORMANCE0(filter, verbose, test_is_out_to_acc); + TEST_PERFORMANCE0(filter, verbose, test_is_out_to_acc_precomp); + TEST_PERFORMANCE0(filter, verbose, test_generate_key_image_helper); + TEST_PERFORMANCE0(filter, verbose, test_generate_key_derivation); + TEST_PERFORMANCE0(filter, verbose, test_generate_key_image); + TEST_PERFORMANCE0(filter, verbose, test_derive_public_key); + TEST_PERFORMANCE0(filter, verbose, test_derive_secret_key); + TEST_PERFORMANCE0(filter, verbose, test_ge_frombytes_vartime); + TEST_PERFORMANCE0(filter, verbose, test_generate_keypair); + TEST_PERFORMANCE0(filter, verbose, test_sc_reduce32); + + TEST_PERFORMANCE2(filter, verbose, test_wallet2_expand_subaddresses, 50, 200); + + TEST_PERFORMANCE0(filter, verbose, test_cn_slow_hash); + TEST_PERFORMANCE1(filter, verbose, test_cn_fast_hash, 32); + TEST_PERFORMANCE1(filter, verbose, test_cn_fast_hash, 16384); + + TEST_PERFORMANCE3(filter, verbose, test_ringct_mlsag, 1, 3, false); + TEST_PERFORMANCE3(filter, verbose, test_ringct_mlsag, 1, 5, false); + TEST_PERFORMANCE3(filter, verbose, test_ringct_mlsag, 1, 10, false); + TEST_PERFORMANCE3(filter, verbose, test_ringct_mlsag, 1, 100, false); + TEST_PERFORMANCE3(filter, verbose, test_ringct_mlsag, 1, 3, true); + TEST_PERFORMANCE3(filter, verbose, test_ringct_mlsag, 1, 5, true); + TEST_PERFORMANCE3(filter, verbose, test_ringct_mlsag, 1, 10, true); + TEST_PERFORMANCE3(filter, verbose, test_ringct_mlsag, 1, 100, true); + + TEST_PERFORMANCE2(filter, verbose, test_equality, memcmp32, true); + TEST_PERFORMANCE2(filter, verbose, test_equality, memcmp32, false); + TEST_PERFORMANCE2(filter, verbose, test_equality, verify32, false); + TEST_PERFORMANCE2(filter, verbose, test_equality, verify32, false); + + TEST_PERFORMANCE1(filter, verbose, test_range_proof, true); + TEST_PERFORMANCE1(filter, verbose, test_range_proof, false); + + TEST_PERFORMANCE2(filter, verbose, test_bulletproof, true, 1); + TEST_PERFORMANCE2(filter, verbose, test_bulletproof, false, 1); + + TEST_PERFORMANCE2(filter, verbose, test_bulletproof, true, 2); + TEST_PERFORMANCE2(filter, verbose, test_bulletproof, false, 2); + + TEST_PERFORMANCE2(filter, verbose, test_bulletproof, true, 15); + TEST_PERFORMANCE2(filter, verbose, test_bulletproof, false, 15); + + TEST_PERFORMANCE3(filter, verbose, test_ringct_mlsag, 1, 3, false); + TEST_PERFORMANCE3(filter, verbose, test_ringct_mlsag, 1, 5, false); + TEST_PERFORMANCE3(filter, verbose, test_ringct_mlsag, 1, 10, false); + TEST_PERFORMANCE3(filter, verbose, test_ringct_mlsag, 1, 100, false); + TEST_PERFORMANCE3(filter, verbose, test_ringct_mlsag, 1, 3, true); + TEST_PERFORMANCE3(filter, verbose, test_ringct_mlsag, 1, 5, true); + TEST_PERFORMANCE3(filter, verbose, test_ringct_mlsag, 1, 10, true); + TEST_PERFORMANCE3(filter, verbose, test_ringct_mlsag, 1, 100, true); + + TEST_PERFORMANCE1(filter, verbose, test_crypto_ops, op_sc_add); + TEST_PERFORMANCE1(filter, verbose, test_crypto_ops, op_sc_sub); + TEST_PERFORMANCE1(filter, verbose, test_crypto_ops, op_sc_mul); + TEST_PERFORMANCE1(filter, verbose, test_crypto_ops, op_ge_add_raw); + TEST_PERFORMANCE1(filter, verbose, test_crypto_ops, op_ge_add_p3_p3); + TEST_PERFORMANCE1(filter, verbose, test_crypto_ops, op_addKeys); + TEST_PERFORMANCE1(filter, verbose, test_crypto_ops, op_scalarmultBase); + TEST_PERFORMANCE1(filter, verbose, test_crypto_ops, op_scalarmultKey); + TEST_PERFORMANCE1(filter, verbose, test_crypto_ops, op_ge_double_scalarmult_base_vartime); + TEST_PERFORMANCE1(filter, verbose, test_crypto_ops, op_ge_double_scalarmult_precomp_vartime); + TEST_PERFORMANCE1(filter, verbose, test_crypto_ops, op_ge_double_scalarmult_precomp_vartime2); + TEST_PERFORMANCE1(filter, verbose, test_crypto_ops, op_addKeys2); + TEST_PERFORMANCE1(filter, verbose, test_crypto_ops, op_addKeys3); + TEST_PERFORMANCE1(filter, verbose, test_crypto_ops, op_addKeys3_2); TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_bos_coster, 2); TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_bos_coster, 8); diff --git a/tests/performance_tests/performance_tests.h b/tests/performance_tests/performance_tests.h index 29d296571fe..6f613e0a5f5 100644 --- a/tests/performance_tests/performance_tests.h +++ b/tests/performance_tests/performance_tests.h @@ -67,8 +67,9 @@ template class test_runner { public: - test_runner() + test_runner(bool verbose = true) : m_elapsed(0) + , m_verbose(verbose) { } @@ -81,7 +82,8 @@ class test_runner performance_timer timer; timer.start(); warm_up(); - std::cout << "Warm up: " << timer.elapsed_ms() << " ms" << std::endl; + if (m_verbose) + std::cout << "Warm up: " << timer.elapsed_ms() << " ms" << std::endl; timer.start(); for (size_t i = 0; i < T::loop_count; ++i) @@ -120,21 +122,29 @@ class test_runner private: volatile uint64_t m_warm_up; /// -void run_test(const std::string &filter, const char* test_name) +void run_test(const std::string &filter, bool verbose, const char* test_name) { boost::smatch match; if (!filter.empty() && !boost::regex_match(std::string(test_name), match, boost::regex(filter))) return; - test_runner runner; + test_runner runner(verbose); if (runner.run()) { - std::cout << test_name << " - OK:\n"; - std::cout << " loop count: " << T::loop_count << '\n'; - std::cout << " elapsed: " << runner.elapsed_time() << " ms\n"; + if (verbose) + { + std::cout << test_name << " - OK:\n"; + std::cout << " loop count: " << T::loop_count << '\n'; + std::cout << " elapsed: " << runner.elapsed_time() << " ms\n"; + } + else + { + std::cout << test_name << " (" << T::loop_count << " calls) - OK:"; + } const char *unit = "ms"; int time_per_call = runner.time_per_call(); if (time_per_call < 30000) { @@ -145,7 +155,7 @@ void run_test(const std::string &filter, const char* test_name) unit = "µs"; #endif } - std::cout << " time per call: " << time_per_call << " " << unit << "/call\n" << std::endl; + std::cout << (verbose ? " time per call: " : " ") << time_per_call << " " << unit << "/call" << (verbose ? "\n" : "") << std::endl; } else { @@ -154,7 +164,8 @@ void run_test(const std::string &filter, const char* test_name) } #define QUOTEME(x) #x -#define TEST_PERFORMANCE0(filter, test_class) run_test< test_class >(filter, QUOTEME(test_class)) -#define TEST_PERFORMANCE1(filter, test_class, a0) run_test< test_class >(filter, QUOTEME(test_class)) -#define TEST_PERFORMANCE2(filter, test_class, a0, a1) run_test< test_class >(filter, QUOTEME(test_class) "<" QUOTEME(a0) ", " QUOTEME(a1) ">") -#define TEST_PERFORMANCE3(filter, test_class, a0, a1, a2) run_test< test_class >(filter, QUOTEME(test_class) "<" QUOTEME(a0) ", " QUOTEME(a1) ", " QUOTEME(a2) ">") +#define TEST_PERFORMANCE0(filter, verbose, test_class) run_test< test_class >(filter, verbose, QUOTEME(test_class)) +#define TEST_PERFORMANCE1(filter, verbose, test_class, a0) run_test< test_class >(filter, verbose, QUOTEME(test_class)) +#define TEST_PERFORMANCE2(filter, verbose, test_class, a0, a1) run_test< test_class >(filter, verbose, QUOTEME(test_class) "<" QUOTEME(a0) ", " QUOTEME(a1) ">") +#define TEST_PERFORMANCE3(filter, verbose, test_class, a0, a1, a2) run_test< test_class >(filter, verbose, QUOTEME(test_class) "<" QUOTEME(a0) ", " QUOTEME(a1) ", " QUOTEME(a2) ">") +#define TEST_PERFORMANCE4(filter, verbose, test_class, a0, a1, a2, a3) run_test< test_class >(filter, verbose, QUOTEME(test_class) "<" QUOTEME(a0) ", " QUOTEME(a1) ", " QUOTEME(a2) ", " QUOTEME(a3) ">") From f34e2e20bdfbefa171e3bbb46615b2b0513ca394 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 14 Jan 2018 23:05:48 +0000 Subject: [PATCH 0345/1404] performance_tests: add tx checking tests with more than 2 outputs --- tests/performance_tests/check_tx_signature.h | 12 ++++++---- tests/performance_tests/main.cpp | 23 +++++++++++++------- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/tests/performance_tests/check_tx_signature.h b/tests/performance_tests/check_tx_signature.h index ec570e69d92..93a8304be62 100644 --- a/tests/performance_tests/check_tx_signature.h +++ b/tests/performance_tests/check_tx_signature.h @@ -40,15 +40,17 @@ #include "multi_tx_test_base.h" -template +template class test_check_tx_signature : private multi_tx_test_base { static_assert(0 < a_ring_size, "ring_size must be greater than 0"); public: - static const size_t loop_count = a_rct ? 10 : a_ring_size < 100 ? 100 : 10; + static const size_t loop_count = a_rct ? (a_ring_size <= 2 ? 50 : 10) : a_ring_size < 100 ? 100 : 10; static const size_t ring_size = a_ring_size; + static const size_t outputs = a_outputs; static const bool rct = a_rct; + static const bool bulletproof = a_bulletproof; typedef multi_tx_test_base base_class; @@ -62,13 +64,15 @@ class test_check_tx_signature : private multi_tx_test_base m_alice.generate(); std::vector destinations; - destinations.push_back(tx_destination_entry(this->m_source_amount, m_alice.get_keys().m_account_address, false)); + destinations.push_back(tx_destination_entry(this->m_source_amount - outputs + 1, m_alice.get_keys().m_account_address, false)); + for (size_t n = 1; n < outputs; ++n) + destinations.push_back(tx_destination_entry(1, m_alice.get_keys().m_account_address, false)); crypto::secret_key tx_key; std::vector additional_tx_keys; std::unordered_map subaddresses; subaddresses[this->m_miners[this->real_source_idx].get_keys().m_account_address.m_spend_public_key] = {0,0}; - if (!construct_tx_and_get_tx_key(this->m_miners[this->real_source_idx].get_keys(), subaddresses, this->m_sources, destinations, cryptonote::account_public_address{}, std::vector(), m_tx, 0, tx_key, additional_tx_keys, rct)) + if (!construct_tx_and_get_tx_key(this->m_miners[this->real_source_idx].get_keys(), subaddresses, this->m_sources, destinations, cryptonote::account_public_address{}, std::vector(), m_tx, 0, tx_key, additional_tx_keys, rct, bulletproof)) return false; get_transaction_prefix_hash(m_tx, m_tx_prefix_hash); diff --git a/tests/performance_tests/main.cpp b/tests/performance_tests/main.cpp index ba1640263d2..c18a653c898 100644 --- a/tests/performance_tests/main.cpp +++ b/tests/performance_tests/main.cpp @@ -124,14 +124,21 @@ int main(int argc, char** argv) TEST_PERFORMANCE3(filter, verbose, test_construct_tx, 100, 2, true); TEST_PERFORMANCE3(filter, verbose, test_construct_tx, 100, 10, true); - TEST_PERFORMANCE2(filter, verbose, test_check_tx_signature, 1, false); - TEST_PERFORMANCE2(filter, verbose, test_check_tx_signature, 2, false); - TEST_PERFORMANCE2(filter, verbose, test_check_tx_signature, 10, false); - TEST_PERFORMANCE2(filter, verbose, test_check_tx_signature, 100, false); - - TEST_PERFORMANCE2(filter, verbose, test_check_tx_signature, 2, true); - TEST_PERFORMANCE2(filter, verbose, test_check_tx_signature, 10, true); - TEST_PERFORMANCE2(filter, verbose, test_check_tx_signature, 100, true); + TEST_PERFORMANCE4(filter, verbose, test_check_tx_signature, 1, 2, false, false); + TEST_PERFORMANCE4(filter, verbose, test_check_tx_signature, 2, 2, false, false); + TEST_PERFORMANCE4(filter, verbose, test_check_tx_signature, 10, 2, false, false); + TEST_PERFORMANCE4(filter, verbose, test_check_tx_signature, 100, 2, false, false); + TEST_PERFORMANCE4(filter, verbose, test_check_tx_signature, 2, 10, false, false); + + TEST_PERFORMANCE4(filter, verbose, test_check_tx_signature, 2, 2, true, false); + TEST_PERFORMANCE4(filter, verbose, test_check_tx_signature, 10, 2, true, false); + TEST_PERFORMANCE4(filter, verbose, test_check_tx_signature, 100, 2, true, false); + TEST_PERFORMANCE4(filter, verbose, test_check_tx_signature, 2, 10, true, false); + + TEST_PERFORMANCE4(filter, verbose, test_check_tx_signature, 2, 2, true, true); + TEST_PERFORMANCE4(filter, verbose, test_check_tx_signature, 10, 2, true, true); + TEST_PERFORMANCE4(filter, verbose, test_check_tx_signature, 100, 2, true, true); + TEST_PERFORMANCE4(filter, verbose, test_check_tx_signature, 2, 10, true, true); TEST_PERFORMANCE0(filter, verbose, test_is_out_to_acc); TEST_PERFORMANCE0(filter, verbose, test_is_out_to_acc_precomp); From 9ce9f8caf6e290088711ce4d5d90c1507a837bf2 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 17 Jan 2018 21:50:03 +0000 Subject: [PATCH 0346/1404] bulletproofs: add multi output bulletproofs to rct --- src/cryptonote_core/blockchain.cpp | 16 +++ src/cryptonote_core/cryptonote_tx_utils.cpp | 12 +- src/cryptonote_core/cryptonote_tx_utils.h | 4 +- src/ringct/rctSigs.cpp | 143 ++++++++++--------- src/ringct/rctSigs.h | 4 +- src/ringct/rctTypes.cpp | 32 +++++ src/ringct/rctTypes.h | 18 ++- src/wallet/wallet2.cpp | 42 ++++-- src/wallet/wallet2.h | 4 +- tests/core_tests/multisig.cpp | 2 +- tests/performance_tests/check_tx_signature.h | 2 +- tests/unit_tests/bulletproofs.cpp | 63 ++++++++ tests/unit_tests/json_serialization.cpp | 2 +- 13 files changed, 247 insertions(+), 97 deletions(-) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index e96dc6bb6d7..c815b507e69 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -3059,6 +3059,22 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, MERROR_VER("Unsupported rct type: " << rv.type); return false; } + + // for bulletproofs, check they're only multi-output after v8 + if (rv.type == rct::RCTTypeFullBulletproof || rv.type == rct::RCTTypeSimpleBulletproof) + { + if (hf_version < 8) + { + for (const rct::Bulletproof &proof: rv.p.bulletproofs) + { + if (proof.V.size() > 1) + { + MERROR_VER("Multi output bulletproofs are invalid before v8"); + return false; + } + } + } + } } return true; } diff --git a/src/cryptonote_core/cryptonote_tx_utils.cpp b/src/cryptonote_core/cryptonote_tx_utils.cpp index 1581f30886f..32031e95087 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.cpp +++ b/src/cryptonote_core/cryptonote_tx_utils.cpp @@ -195,7 +195,7 @@ namespace cryptonote return addr.m_view_public_key; } //--------------------------------------------------------------- - bool construct_tx_with_tx_key(const account_keys& sender_account_keys, const std::unordered_map& subaddresses, std::vector& sources, std::vector& destinations, const boost::optional& change_addr, std::vector extra, transaction& tx, uint64_t unlock_time, const crypto::secret_key &tx_key, const std::vector &additional_tx_keys, bool rct, bool bulletproof, rct::multisig_out *msout, bool shuffle_outs) + bool construct_tx_with_tx_key(const account_keys& sender_account_keys, const std::unordered_map& subaddresses, std::vector& sources, std::vector& destinations, const boost::optional& change_addr, std::vector extra, transaction& tx, uint64_t unlock_time, const crypto::secret_key &tx_key, const std::vector &additional_tx_keys, bool rct, rct::RangeProofType range_proof_type, rct::multisig_out *msout, bool shuffle_outs) { hw::device &hwdev = sender_account_keys.get_device(); @@ -589,9 +589,9 @@ namespace cryptonote get_transaction_prefix_hash(tx, tx_prefix_hash); rct::ctkeyV outSk; if (use_simple_rct) - tx.rct_signatures = rct::genRctSimple(rct::hash2rct(tx_prefix_hash), inSk, destinations, inamounts, outamounts, amount_in - amount_out, mixRing, amount_keys, msout ? &kLRki : NULL, msout, index, outSk, bulletproof, hwdev); + tx.rct_signatures = rct::genRctSimple(rct::hash2rct(tx_prefix_hash), inSk, destinations, inamounts, outamounts, amount_in - amount_out, mixRing, amount_keys, msout ? &kLRki : NULL, msout, index, outSk, range_proof_type, hwdev); else - tx.rct_signatures = rct::genRct(rct::hash2rct(tx_prefix_hash), inSk, destinations, outamounts, mixRing, amount_keys, msout ? &kLRki[0] : NULL, msout, sources[0].real_output, outSk, bulletproof, hwdev); // same index assumption + tx.rct_signatures = rct::genRct(rct::hash2rct(tx_prefix_hash), inSk, destinations, outamounts, mixRing, amount_keys, msout ? &kLRki[0] : NULL, msout, sources[0].real_output, outSk, range_proof_type, hwdev); // same index assumption memwipe(inSk.data(), inSk.size() * sizeof(rct::ctkey)); CHECK_AND_ASSERT_MES(tx.vout.size() == outSk.size(), false, "outSk size does not match vout"); @@ -604,7 +604,7 @@ namespace cryptonote return true; } //--------------------------------------------------------------- - bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map& subaddresses, std::vector& sources, std::vector& destinations, const boost::optional& change_addr, std::vector extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector &additional_tx_keys, bool rct, bool bulletproof, rct::multisig_out *msout) + bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map& subaddresses, std::vector& sources, std::vector& destinations, const boost::optional& change_addr, std::vector extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector &additional_tx_keys, bool rct, rct::RangeProofType range_proof_type, rct::multisig_out *msout) { hw::device &hwdev = sender_account_keys.get_device(); hwdev.open_tx(tx_key); @@ -622,7 +622,7 @@ namespace cryptonote additional_tx_keys.push_back(keypair::generate(sender_account_keys.get_device()).sec); } - bool r = construct_tx_with_tx_key(sender_account_keys, subaddresses, sources, destinations, change_addr, extra, tx, unlock_time, tx_key, additional_tx_keys, rct, bulletproof, msout); + bool r = construct_tx_with_tx_key(sender_account_keys, subaddresses, sources, destinations, change_addr, extra, tx, unlock_time, tx_key, additional_tx_keys, rct, range_proof_type, msout); hwdev.close_tx(); return r; } @@ -634,7 +634,7 @@ namespace cryptonote crypto::secret_key tx_key; std::vector additional_tx_keys; std::vector destinations_copy = destinations; - return construct_tx_and_get_tx_key(sender_account_keys, subaddresses, sources, destinations_copy, change_addr, extra, tx, unlock_time, tx_key, additional_tx_keys, false, false, NULL); + return construct_tx_and_get_tx_key(sender_account_keys, subaddresses, sources, destinations_copy, change_addr, extra, tx, unlock_time, tx_key, additional_tx_keys, false, rct::RangeProofBorromean, NULL); } //--------------------------------------------------------------- bool generate_genesis_block( diff --git a/src/cryptonote_core/cryptonote_tx_utils.h b/src/cryptonote_core/cryptonote_tx_utils.h index a5d149fcad4..08e8725f75f 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.h +++ b/src/cryptonote_core/cryptonote_tx_utils.h @@ -90,8 +90,8 @@ namespace cryptonote //--------------------------------------------------------------- crypto::public_key get_destination_view_key_pub(const std::vector &destinations, const boost::optional& change_addr); bool construct_tx(const account_keys& sender_account_keys, std::vector &sources, const std::vector& destinations, const boost::optional& change_addr, std::vector extra, transaction& tx, uint64_t unlock_time); - bool construct_tx_with_tx_key(const account_keys& sender_account_keys, const std::unordered_map& subaddresses, std::vector& sources, std::vector& destinations, const boost::optional& change_addr, std::vector extra, transaction& tx, uint64_t unlock_time, const crypto::secret_key &tx_key, const std::vector &additional_tx_keys, bool rct = false, bool bulletproof = false, rct::multisig_out *msout = NULL, bool shuffle_outs = true); - bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map& subaddresses, std::vector& sources, std::vector& destinations, const boost::optional& change_addr, std::vector extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector &additional_tx_keys, bool rct = false, bool bulletproof = false, rct::multisig_out *msout = NULL); + bool construct_tx_with_tx_key(const account_keys& sender_account_keys, const std::unordered_map& subaddresses, std::vector& sources, std::vector& destinations, const boost::optional& change_addr, std::vector extra, transaction& tx, uint64_t unlock_time, const crypto::secret_key &tx_key, const std::vector &additional_tx_keys, bool rct = false, rct::RangeProofType range_proof_type = rct::RangeProofBorromean, rct::multisig_out *msout = NULL, bool shuffle_outs = true); + bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map& subaddresses, std::vector& sources, std::vector& destinations, const boost::optional& change_addr, std::vector extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector &additional_tx_keys, bool rct = false, rct::RangeProofType range_proof_type = rct::RangeProofBorromean, rct::multisig_out *msout = NULL); bool generate_genesis_block( block& bl diff --git a/src/ringct/rctSigs.cpp b/src/ringct/rctSigs.cpp index cc966c44b07..6dcb203bffa 100644 --- a/src/ringct/rctSigs.cpp +++ b/src/ringct/rctSigs.cpp @@ -45,30 +45,6 @@ using namespace std; #define CHECK_AND_ASSERT_MES_L1(expr, ret, message) {if(!(expr)) {MCERROR("verify", message); return ret;}} namespace rct { - bool is_simple(int type) - { - switch (type) - { - case RCTTypeSimple: - case RCTTypeSimpleBulletproof: - return true; - default: - return false; - } - } - - bool is_bulletproof(int type) - { - switch (type) - { - case RCTTypeSimpleBulletproof: - case RCTTypeFullBulletproof: - return true; - default: - return false; - } - } - Bulletproof proveRangeBulletproof(key &C, key &mask, uint64_t amount) { mask = rct::skGen(); @@ -78,6 +54,15 @@ namespace rct { return proof; } + Bulletproof proveRangeBulletproof(keyV &C, keyV &masks, const std::vector &amounts) + { + masks = rct::skvGen(amounts.size()); + Bulletproof proof = bulletproof_PROVE(amounts, masks); + CHECK_AND_ASSERT_THROW_MES(proof.V.size() == amounts.size(), "V does not have the expected size"); + C = proof.V; + return proof; + } + bool verBulletproof(const Bulletproof &proof) { try { return bulletproof_VERIFY(proof); } @@ -389,7 +374,7 @@ namespace rct { std::stringstream ss; binary_archive ba(ss); CHECK_AND_ASSERT_THROW_MES(!rv.mixRing.empty(), "Empty mixRing"); - const size_t inputs = is_simple(rv.type) ? rv.mixRing.size() : rv.mixRing[0].size(); + const size_t inputs = is_rct_simple(rv.type) ? rv.mixRing.size() : rv.mixRing[0].size(); const size_t outputs = rv.ecdhInfo.size(); key prehash; CHECK_AND_ASSERT_THROW_MES(const_cast(rv).serialize_rctsig_base(ba, inputs, outputs), @@ -659,7 +644,8 @@ namespace rct { // must know the destination private key to find the correct amount, else will return a random number // Note: For txn fees, the last index in the amounts vector should contain that // Thus the amounts vector will be "one" longer than the destinations vectort - rctSig genRct(const key &message, const ctkeyV & inSk, const keyV & destinations, const vector & amounts, const ctkeyM &mixRing, const keyV &amount_keys, const multisig_kLRki *kLRki, multisig_out *msout, unsigned int index, ctkeyV &outSk, bool bulletproof, hw::device &hwdev) { + rctSig genRct(const key &message, const ctkeyV & inSk, const keyV & destinations, const vector & amounts, const ctkeyM &mixRing, const keyV &amount_keys, const multisig_kLRki *kLRki, multisig_out *msout, unsigned int index, ctkeyV &outSk, RangeProofType range_proof_type, hw::device &hwdev) { + const bool bulletproof = range_proof_type != RangeProofBorromean; CHECK_AND_ASSERT_THROW_MES(amounts.size() == destinations.size() || amounts.size() == destinations.size() + 1, "Different number of amounts/destinations"); CHECK_AND_ASSERT_THROW_MES(amount_keys.size() == destinations.size(), "Different number of amount_keys/destinations"); CHECK_AND_ASSERT_THROW_MES(index < mixRing.size(), "Bad index into mixRing"); @@ -672,9 +658,7 @@ namespace rct { rv.type = bulletproof ? RCTTypeFullBulletproof : RCTTypeFull; rv.message = message; rv.outPk.resize(destinations.size()); - if (bulletproof) - rv.p.bulletproofs.resize(destinations.size()); - else + if (!bulletproof) rv.p.rangeSigs.resize(destinations.size()); rv.ecdhInfo.resize(destinations.size()); @@ -684,18 +668,46 @@ namespace rct { for (i = 0; i < destinations.size(); i++) { //add destination to sig rv.outPk[i].dest = copy(destinations[i]); - //compute range proof - if (bulletproof) - rv.p.bulletproofs[i] = proveRangeBulletproof(rv.outPk[i].mask, outSk[i].mask, amounts[i]); - else + //compute range proof (bulletproofs are done later) + if (!bulletproof) + { rv.p.rangeSigs[i] = proveRange(rv.outPk[i].mask, outSk[i].mask, amounts[i]); #ifdef DBG - if (bulletproof) - CHECK_AND_ASSERT_THROW_MES(verBulletproof(rv.p.bulletproofs[i]), "verBulletproof failed on newly created proof"); - else CHECK_AND_ASSERT_THROW_MES(verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]), "verRange failed on newly created proof"); #endif + } + } + + rv.p.bulletproofs.clear(); + if (bulletproof) + { + std::vector proof_amounts; + size_t amounts_proved = 0; + while (amounts_proved < amounts.size()) + { + size_t batch_size = 1; + if (range_proof_type == RangeProofMultiOutputBulletproof) + while (batch_size * 2 + amounts_proved <= amounts.size()) + batch_size *= 2; + rct::keyV C, masks; + std::vector batch_amounts(batch_size); + for (i = 0; i < batch_size; ++i) + batch_amounts[i] = amounts[i + amounts_proved]; + rv.p.bulletproofs.push_back(proveRangeBulletproof(C, masks, batch_amounts)); + #ifdef DBG + CHECK_AND_ASSERT_THROW_MES(verBulletproof(rv.p.bulletproofs.back()), "verBulletproof failed on newly created proof"); + #endif + for (i = 0; i < batch_size; ++i) + { + rv.outPk[i + amounts_proved].mask = C[i]; + outSk[i + amounts_proved].mask = masks[i]; + } + amounts_proved += batch_size; + } + } + for (i = 0; i < outSk.size(); ++i) + { //mask amount and mask rv.ecdhInfo[i].mask = copy(outSk[i].mask); rv.ecdhInfo[i].amount = d2h(amounts[i]); @@ -725,12 +737,13 @@ namespace rct { ctkeyM mixRing; ctkeyV outSk; tie(mixRing, index) = populateFromBlockchain(inPk, mixin); - return genRct(message, inSk, destinations, amounts, mixRing, amount_keys, kLRki, msout, index, outSk, false, hwdev); + return genRct(message, inSk, destinations, amounts, mixRing, amount_keys, kLRki, msout, index, outSk, RangeProofBorromean, hwdev); } //RCT simple //for post-rct only - rctSig genRctSimple(const key &message, const ctkeyV & inSk, const keyV & destinations, const vector &inamounts, const vector &outamounts, xmr_amount txnFee, const ctkeyM & mixRing, const keyV &amount_keys, const std::vector *kLRki, multisig_out *msout, const std::vector & index, ctkeyV &outSk, bool bulletproof, hw::device &hwdev) { + rctSig genRctSimple(const key &message, const ctkeyV & inSk, const keyV & destinations, const vector &inamounts, const vector &outamounts, xmr_amount txnFee, const ctkeyM & mixRing, const keyV &amount_keys, const std::vector *kLRki, multisig_out *msout, const std::vector & index, ctkeyV &outSk, RangeProofType range_proof_type, hw::device &hwdev) { + const bool bulletproof = range_proof_type != RangeProofBorromean; CHECK_AND_ASSERT_THROW_MES(inamounts.size() > 0, "Empty inamounts"); CHECK_AND_ASSERT_THROW_MES(inamounts.size() == inSk.size(), "Different number of inamounts/inSk"); CHECK_AND_ASSERT_THROW_MES(outamounts.size() == destinations.size(), "Different number of amounts/destinations"); @@ -822,7 +835,7 @@ namespace rct { mixRing[i].resize(mixin+1); index[i] = populateFromBlockchainSimple(mixRing[i], inPk[i], mixin); } - return genRctSimple(message, inSk, destinations, inamounts, outamounts, txnFee, mixRing, amount_keys, kLRki, msout, index, outSk, false, hwdev); + return genRctSimple(message, inSk, destinations, inamounts, outamounts, txnFee, mixRing, amount_keys, kLRki, msout, index, outSk, RangeProofBorromean, hwdev); } //RingCT protocol @@ -838,10 +851,11 @@ namespace rct { bool verRct(const rctSig & rv, bool semantics) { PERF_TIMER(verRct); CHECK_AND_ASSERT_MES(rv.type == RCTTypeFull || rv.type == RCTTypeFullBulletproof, false, "verRct called on non-full rctSig"); + const bool bulletproof = is_rct_bulletproof(rv.type); if (semantics) { - if (rv.type == RCTTypeFullBulletproof) - CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.p.bulletproofs.size(), false, "Mismatched sizes of outPk and rv.p.bulletproofs"); + if (bulletproof) + CHECK_AND_ASSERT_MES(rv.outPk.size() == n_bulletproof_amounts(rv.p.bulletproofs), false, "Mismatched sizes of outPk and bulletproofs"); else CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.p.rangeSigs.size(), false, "Mismatched sizes of outPk and rv.p.rangeSigs"); CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.ecdhInfo.size(), false, "Mismatched sizes of outPk and rv.ecdhInfo"); @@ -858,21 +872,19 @@ namespace rct { if (semantics) { tools::threadpool& tpool = tools::threadpool::getInstance(); tools::threadpool::waiter waiter; - std::deque results(rv.outPk.size(), false); + std::deque results(bulletproof ? rv.p.bulletproofs.size() : rv.outPk.size(), false); DP("range proofs verified?"); - for (size_t i = 0; i < rv.outPk.size(); i++) { - tpool.submit(&waiter, [&, i] { - if (rv.p.rangeSigs.empty()) - results[i] = verBulletproof(rv.p.bulletproofs[i]); - else - results[i] = verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]); - }, true); - } + if (bulletproof) + for (size_t i = 0; i < rv.p.bulletproofs.size(); i++) + tpool.submit(&waiter, [&, i] { results[i] = verBulletproof(rv.p.bulletproofs[i]); }); + else + for (size_t i = 0; i < rv.outPk.size(); i++) + tpool.submit(&waiter, [&, i] { results[i] = verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]); }); waiter.wait(&tpool); - for (size_t i = 0; i < rv.outPk.size(); ++i) { + for (size_t i = 0; i < results.size(); ++i) { if (!results[i]) { - LOG_PRINT_L1("Range proof verified failed for output " << i); + LOG_PRINT_L1("Range proof verified failed for proof " << i); return false; } } @@ -912,11 +924,12 @@ namespace rct { PERF_TIMER(verRctSimple); CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple || rv.type == RCTTypeSimpleBulletproof, false, "verRctSimple called on non simple rctSig"); + const bool bulletproof = is_rct_bulletproof(rv.type); if (semantics) { - if (rv.type == RCTTypeSimpleBulletproof) + if (bulletproof) { - CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.p.bulletproofs.size(), false, "Mismatched sizes of outPk and rv.p.bulletproofs"); + CHECK_AND_ASSERT_MES(rv.outPk.size() == n_bulletproof_amounts(rv.p.bulletproofs), false, "Mismatched sizes of outPk and bulletproofs"); CHECK_AND_ASSERT_MES(rv.p.pseudoOuts.size() == rv.p.MGs.size(), false, "Mismatched sizes of rv.p.pseudoOuts and rv.p.MGs"); CHECK_AND_ASSERT_MES(rv.pseudoOuts.empty(), false, "rv.pseudoOuts is not empty"); } @@ -931,7 +944,7 @@ namespace rct { else { // semantics check is early, and mixRing/MGs aren't resolved yet - if (rv.type == RCTTypeSimpleBulletproof) + if (bulletproof) CHECK_AND_ASSERT_MES(rv.p.pseudoOuts.size() == rv.mixRing.size(), false, "Mismatched sizes of rv.p.pseudoOuts and mixRing"); else CHECK_AND_ASSERT_MES(rv.pseudoOuts.size() == rv.mixRing.size(), false, "Mismatched sizes of rv.pseudoOuts and mixRing"); @@ -943,7 +956,7 @@ namespace rct { tools::threadpool& tpool = tools::threadpool::getInstance(); tools::threadpool::waiter waiter; - const keyV &pseudoOuts = is_bulletproof(rv.type) ? rv.p.pseudoOuts : rv.pseudoOuts; + const keyV &pseudoOuts = bulletproof ? rv.p.pseudoOuts : rv.pseudoOuts; if (semantics) { key sumOutpks = identity(); @@ -967,20 +980,18 @@ namespace rct { } results.clear(); - results.resize(rv.outPk.size()); - for (size_t i = 0; i < rv.outPk.size(); i++) { - tpool.submit(&waiter, [&, i] { - if (rv.p.rangeSigs.empty()) - results[i] = verBulletproof(rv.p.bulletproofs[i]); - else - results[i] = verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]); - }, true); - } + results.resize(bulletproof ? rv.p.bulletproofs.size() : rv.outPk.size()); + if (bulletproof) + for (size_t i = 0; i < rv.p.bulletproofs.size(); i++) + tpool.submit(&waiter, [&, i] { results[i] = verBulletproof(rv.p.bulletproofs[i]); }); + else + for (size_t i = 0; i < rv.p.rangeSigs.size(); i++) + tpool.submit(&waiter, [&, i] { results[i] = verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]); }); waiter.wait(&tpool); for (size_t i = 0; i < results.size(); ++i) { if (!results[i]) { - LOG_PRINT_L1("Range proof verified failed for output " << i); + LOG_PRINT_L1("Range proof verified failed for proof " << i); return false; } } diff --git a/src/ringct/rctSigs.h b/src/ringct/rctSigs.h index 5a9b2dd44fe..9a66310c709 100644 --- a/src/ringct/rctSigs.h +++ b/src/ringct/rctSigs.h @@ -119,10 +119,10 @@ namespace rct { //decodeRct: (c.f. https://eprint.iacr.org/2015/1098 section 5.1.1) // uses the attached ecdh info to find the amounts represented by each output commitment // must know the destination private key to find the correct amount, else will return a random number - rctSig genRct(const key &message, const ctkeyV & inSk, const keyV & destinations, const std::vector & amounts, const ctkeyM &mixRing, const keyV &amount_keys, const multisig_kLRki *kLRki, multisig_out *msout, unsigned int index, ctkeyV &outSk, bool bulletproof, hw::device &hwdev); + rctSig genRct(const key &message, const ctkeyV & inSk, const keyV & destinations, const std::vector & amounts, const ctkeyM &mixRing, const keyV &amount_keys, const multisig_kLRki *kLRki, multisig_out *msout, unsigned int index, ctkeyV &outSk, RangeProofType range_proof_type, hw::device &hwdev); rctSig genRct(const key &message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const std::vector & amounts, const keyV &amount_keys, const multisig_kLRki *kLRki, multisig_out *msout, const int mixin, hw::device &hwdev); rctSig genRctSimple(const key & message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const std::vector & inamounts, const std::vector & outamounts, const keyV &amount_keys, const std::vector *kLRki, multisig_out *msout, xmr_amount txnFee, unsigned int mixin, hw::device &hwdev); - rctSig genRctSimple(const key & message, const ctkeyV & inSk, const keyV & destinations, const std::vector & inamounts, const std::vector & outamounts, xmr_amount txnFee, const ctkeyM & mixRing, const keyV &amount_keys, const std::vector *kLRki, multisig_out *msout, const std::vector & index, ctkeyV &outSk, bool bulletproof, hw::device &hwdev); + rctSig genRctSimple(const key & message, const ctkeyV & inSk, const keyV & destinations, const std::vector & inamounts, const std::vector & outamounts, xmr_amount txnFee, const ctkeyM & mixRing, const keyV &amount_keys, const std::vector *kLRki, multisig_out *msout, const std::vector & index, ctkeyV &outSk, RangeProofType range_proof_type, hw::device &hwdev); bool verRct(const rctSig & rv, bool semantics); static inline bool verRct(const rctSig & rv) { return verRct(rv, true) && verRct(rv, false); } bool verRctSimple(const rctSig & rv, bool semantics); diff --git a/src/ringct/rctTypes.cpp b/src/ringct/rctTypes.cpp index 5650b3ba11f..72ef7578025 100644 --- a/src/ringct/rctTypes.cpp +++ b/src/ringct/rctTypes.cpp @@ -209,4 +209,36 @@ namespace rct { return vali; } + bool is_rct_simple(int type) + { + switch (type) + { + case RCTTypeSimple: + case RCTTypeSimpleBulletproof: + return true; + default: + return false; + } + } + + bool is_rct_bulletproof(int type) + { + switch (type) + { + case RCTTypeSimpleBulletproof: + case RCTTypeFullBulletproof: + return true; + default: + return false; + } + } + + size_t n_bulletproof_amounts(const std::vector &proofs) + { + size_t n = 0; + for (const Bulletproof &proof: proofs) + n += proof.V.size(); + return n; + } + } diff --git a/src/ringct/rctTypes.h b/src/ringct/rctTypes.h index 68f04b0f14f..5574c7784e4 100644 --- a/src/ringct/rctTypes.h +++ b/src/ringct/rctTypes.h @@ -213,6 +213,8 @@ namespace rct { END_SERIALIZE() }; + size_t n_bulletproof_amounts(const std::vector &proofs); + //A container to hold all signatures necessary for RingCT // rangeSigs holds all the rangeproof data of a transaction // MG holds the MLSAG signature of a transaction @@ -227,6 +229,7 @@ namespace rct { RCTTypeFullBulletproof = 3, RCTTypeSimpleBulletproof = 4, }; + enum RangeProofType { RangeProofBorromean, RangeProofBulletproof, RangeProofMultiOutputBulletproof }; struct rctSigBase { uint8_t type; key message; @@ -310,15 +313,19 @@ namespace rct { { ar.tag("bp"); ar.begin_array(); - PREPARE_CUSTOM_VECTOR_SERIALIZATION(outputs, bulletproofs); - if (bulletproofs.size() != outputs) + uint32_t nbp = bulletproofs.size(); + FIELD(nbp) + PREPARE_CUSTOM_VECTOR_SERIALIZATION(nbp, bulletproofs); + if (bulletproofs.size() > outputs) return false; - for (size_t i = 0; i < outputs; ++i) + for (size_t i = 0; i < nbp; ++i) { FIELDS(bulletproofs[i]) - if (outputs - i > 1) + if (nbp - i > 1) ar.delimit_array(); } + if (n_bulletproof_amounts(bulletproofs) != outputs) + return false; ar.end_array(); } else @@ -519,6 +526,9 @@ namespace rct { //int[64] to uint long long xmr_amount b2d(bits amountb); + bool is_rct_simple(int type); + bool is_rct_bulletproof(int type); + static inline const rct::key &pk2rct(const crypto::public_key &pk) { return (const rct::key&)pk; } static inline const rct::key &sk2rct(const crypto::secret_key &sk) { return (const rct::key&)sk; } static inline const rct::key &ki2rct(const crypto::key_image &ki) { return (const rct::key&)ki; } diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index a2e36706eef..fdba6d84a6e 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -5268,10 +5268,18 @@ bool wallet2::sign_tx(unsigned_tx_set &exported_txs, std::vector 1) + range_proof_type = rct::RangeProofMultiOutputBulletproof; + } crypto::secret_key tx_key; std::vector additional_tx_keys; rct::multisig_out msout; - bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sd.sources, sd.splitted_dsts, sd.change_dts.addr, sd.extra, ptx.tx, sd.unlock_time, tx_key, additional_tx_keys, sd.use_rct, sd.use_bulletproofs, m_multisig ? &msout : NULL); + bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sd.sources, sd.splitted_dsts, sd.change_dts.addr, sd.extra, ptx.tx, sd.unlock_time, tx_key, additional_tx_keys, sd.use_rct, range_proof_type, m_multisig ? &msout : NULL); THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sd.sources, sd.splitted_dsts, sd.unlock_time, m_nettype); // we don't test tx size, because we don't know the current limit, due to not having a blockchain, // and it's a bit pointless to fail there anyway, since it'd be a (good) guess only. We sign anyway, @@ -5684,7 +5692,15 @@ bool wallet2::sign_multisig_tx(multisig_tx_set &exported_txs, std::vector 1) + range_proof_type = rct::RangeProofMultiOutputBulletproof; + } + bool r = cryptonote::construct_tx_with_tx_key(m_account.get_keys(), m_subaddresses, sources, sd.splitted_dsts, ptx.change_dts.addr, sd.extra, tx, sd.unlock_time, ptx.tx_key, ptx.additional_tx_keys, sd.use_rct, range_proof_type, &msout, false); THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sd.sources, sd.splitted_dsts, sd.unlock_time, m_nettype); THROW_WALLET_EXCEPTION_IF(get_transaction_prefix_hash (tx) != get_transaction_prefix_hash(ptx.tx), @@ -7054,7 +7070,7 @@ void wallet2::transfer_selected(const std::vector additional_tx_keys; rct::multisig_out msout; LOG_PRINT_L2("constructing tx"); - bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sources, splitted_dsts, change_dts.addr, extra, tx, unlock_time, tx_key, additional_tx_keys, false, false, m_multisig ? &msout : NULL); + bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sources, splitted_dsts, change_dts.addr, extra, tx, unlock_time, tx_key, additional_tx_keys, false, rct::RangeProofBulletproof, m_multisig ? &msout : NULL); LOG_PRINT_L2("constructed tx, r="< dsts, const std::vector& selected_transfers, size_t fake_outputs_count, std::vector> &outs, - uint64_t unlock_time, uint64_t fee, const std::vector& extra, cryptonote::transaction& tx, pending_tx &ptx, bool bulletproof) + uint64_t unlock_time, uint64_t fee, const std::vector& extra, cryptonote::transaction& tx, pending_tx &ptx, rct::RangeProofType range_proof_type) { using namespace cryptonote; // throw if attempting a transaction with no destinations @@ -7259,7 +7275,7 @@ void wallet2::transfer_selected_rct(std::vector wallet2::create_transactions_2(std::vector test_ptx.fee) { if (use_rct) transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra, - test_tx, test_ptx, bulletproof); + test_tx, test_ptx, range_proof_type); else transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra, detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx); @@ -8442,7 +8459,7 @@ std::vector wallet2::create_transactions_2(std::vector& extra, */ test_tx, /* OUT cryptonote::transaction& tx, */ test_ptx, /* OUT cryptonote::transaction& tx, */ - bulletproof); + range_proof_type); } else { transfer_selected(tx.dsts, tx.selected_transfers, @@ -8581,6 +8598,7 @@ std::vector wallet2::create_transactions_from(const crypton const bool use_rct = fake_outs_count > 0 && use_fork_rules(4, 0); const bool bulletproof = use_fork_rules(get_bulletproof_fork(), 0); + const rct::RangeProofType range_proof_type = bulletproof ? rct::RangeProofBulletproof : rct::RangeProofBorromean; const uint64_t fee_per_kb = get_per_kb_fee(); const uint64_t fee_multiplier = get_fee_multiplier(priority, get_fee_algorithm()); @@ -8636,7 +8654,7 @@ std::vector wallet2::create_transactions_from(const crypton tx.selected_transfers.size() << " outputs"); if (use_rct) transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra, - test_tx, test_ptx, bulletproof); + test_tx, test_ptx, range_proof_type); else transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra, detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx); @@ -8653,7 +8671,7 @@ std::vector wallet2::create_transactions_from(const crypton tx.dsts[0].amount = available_for_fee - needed_fee; if (use_rct) transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra, - test_tx, test_ptx, bulletproof); + test_tx, test_ptx, range_proof_type); else transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra, detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx); @@ -8692,7 +8710,7 @@ std::vector wallet2::create_transactions_from(const crypton pending_tx test_ptx; if (use_rct) { transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, tx.outs, unlock_time, tx.needed_fee, extra, - test_tx, test_ptx, bulletproof); + test_tx, test_ptx, range_proof_type); } else { transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, tx.outs, unlock_time, tx.needed_fee, extra, detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx); diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 556679f511d..3ba51a27c1d 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -729,7 +729,7 @@ namespace tools uint64_t unlock_time, uint64_t fee, const std::vector& extra, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction& tx, pending_tx &ptx); void transfer_selected_rct(std::vector dsts, const std::vector& selected_transfers, size_t fake_outputs_count, std::vector> &outs, - uint64_t unlock_time, uint64_t fee, const std::vector& extra, cryptonote::transaction& tx, pending_tx &ptx, bool bulletproof); + uint64_t unlock_time, uint64_t fee, const std::vector& extra, cryptonote::transaction& tx, pending_tx &ptx, rct::RangeProofType range_proof_type); void commit_tx(pending_tx& ptx_vector); void commit_tx(std::vector& ptx_vector); @@ -1968,7 +1968,7 @@ namespace tools crypto::secret_key tx_key; std::vector additional_tx_keys; rct::multisig_out msout; - bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sources, splitted_dsts, change_dts.addr, extra, tx, unlock_time, tx_key, additional_tx_keys, false, false, m_multisig ? &msout : NULL); + bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sources, splitted_dsts, change_dts.addr, extra, tx, unlock_time, tx_key, additional_tx_keys, false, rct::RangeProofBorromean, m_multisig ? &msout : NULL); THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sources, splitted_dsts, unlock_time, m_nettype); THROW_WALLET_EXCEPTION_IF(upper_transaction_size_limit <= get_object_blobsize(tx), error::tx_too_big, tx, upper_transaction_size_limit); diff --git a/tests/core_tests/multisig.cpp b/tests/core_tests/multisig.cpp index 936a4b742bc..46cc0ff35b4 100644 --- a/tests/core_tests/multisig.cpp +++ b/tests/core_tests/multisig.cpp @@ -285,7 +285,7 @@ bool gen_multisig_tx_validation_base::generate_with(std::vector additional_tx_secret_keys; auto sources_copy = sources; - r = construct_tx_and_get_tx_key(miner_account[creator].get_keys(), subaddresses, sources, destinations, boost::none, std::vector(), tx, 0, tx_key, additional_tx_secret_keys, true, false, msoutp); + r = construct_tx_and_get_tx_key(miner_account[creator].get_keys(), subaddresses, sources, destinations, boost::none, std::vector(), tx, 0, tx_key, additional_tx_secret_keys, true, rct::RangeProofBorromean, msoutp); CHECK_AND_ASSERT_MES(r, false, "failed to construct transaction"); #ifndef NO_MULTISIG diff --git a/tests/performance_tests/check_tx_signature.h b/tests/performance_tests/check_tx_signature.h index 93a8304be62..693ea186634 100644 --- a/tests/performance_tests/check_tx_signature.h +++ b/tests/performance_tests/check_tx_signature.h @@ -72,7 +72,7 @@ class test_check_tx_signature : private multi_tx_test_base std::vector additional_tx_keys; std::unordered_map subaddresses; subaddresses[this->m_miners[this->real_source_idx].get_keys().m_account_address.m_spend_public_key] = {0,0}; - if (!construct_tx_and_get_tx_key(this->m_miners[this->real_source_idx].get_keys(), subaddresses, this->m_sources, destinations, cryptonote::account_public_address{}, std::vector(), m_tx, 0, tx_key, additional_tx_keys, rct, bulletproof)) + if (!construct_tx_and_get_tx_key(this->m_miners[this->real_source_idx].get_keys(), subaddresses, this->m_sources, destinations, cryptonote::account_public_address{}, std::vector(), m_tx, 0, tx_key, additional_tx_keys, rct, bulletproof ? rct::RangeProofMultiOutputBulletproof : rct::RangeProofBorromean)) return false; get_transaction_prefix_hash(m_tx, m_tx_prefix_hash); diff --git a/tests/unit_tests/bulletproofs.cpp b/tests/unit_tests/bulletproofs.cpp index d3b80453025..183bb51672e 100644 --- a/tests/unit_tests/bulletproofs.cpp +++ b/tests/unit_tests/bulletproofs.cpp @@ -31,7 +31,9 @@ #include "gtest/gtest.h" #include "ringct/rctOps.h" +#include "ringct/rctSigs.h" #include "ringct/bulletproofs.h" +#include "device/device.hpp" #include "misc_log_ex.h" TEST(bulletproofs, valid_zero) @@ -72,6 +74,67 @@ TEST(bulletproofs, valid_multi_random) } } +TEST(bulletproofs, multi_splitting) +{ + rct::ctkeyV sc, pc; + rct::ctkey sctmp, pctmp; + + std::tie(sctmp, pctmp) = rct::ctskpkGen(6000); + sc.push_back(sctmp); + pc.push_back(pctmp); + + std::tie(sctmp, pctmp) = rct::ctskpkGen(7000); + sc.push_back(sctmp); + pc.push_back(pctmp); + + const int mixin = 3, max_outputs = 16; + + for (int n_outputs = 1; n_outputs <= max_outputs; ++n_outputs) + { + std::vector amounts; + rct::keyV amount_keys; + rct::keyV destinations; + rct::key Sk, Pk; + uint64_t available = 6000 + 7000; + uint64_t amount; + rct::ctkeyM mixRing(mixin+1); + + //add output + for (size_t i = 0; i < n_outputs; ++i) + { + amount = rct::randXmrAmount(available); + amounts.push_back(amount); + amount_keys.push_back(rct::hash_to_scalar(rct::zero())); + rct::skpkGen(Sk, Pk); + destinations.push_back(Pk); + available -= amount; + } + if (!amounts.empty()) + amounts.back() += available; + + for (size_t j = 0; j <= mixin; ++j) + { + for (size_t i = 0; i < sc.size(); ++i) + { + if (j == 1) + mixRing[j].push_back(pc[i]); + else + mixRing[j].push_back({rct::scalarmultBase(rct::skGen()), rct::scalarmultBase(rct::skGen())}); + } + } + + rct::ctkeyV outSk; + rct::rctSig s = rct::genRct(rct::zero(), sc, destinations, amounts, mixRing, amount_keys, NULL, NULL, 1, outSk, rct::RangeProofMultiOutputBulletproof, hw::get_device("default")); + ASSERT_TRUE(rct::verRct(s)); + for (size_t i = 0; i < n_outputs; ++i) + { + rct::key mask; + rct::decodeRct(s, amount_keys[i], i, mask, hw::get_device("default")); + ASSERT_TRUE(mask == outSk[i].mask); + } + } +} + TEST(bulletproofs, invalid_8) { diff --git a/tests/unit_tests/json_serialization.cpp b/tests/unit_tests/json_serialization.cpp index ac6b60846cf..234cb2c33bd 100644 --- a/tests/unit_tests/json_serialization.cpp +++ b/tests/unit_tests/json_serialization.cpp @@ -75,7 +75,7 @@ namespace std::unordered_map subaddresses; subaddresses[from.m_account_address.m_spend_public_key] = {0,0}; - if (!cryptonote::construct_tx_and_get_tx_key(from, subaddresses, actual_sources, to, boost::none, {}, tx, 0, tx_key, extra_keys, rct, bulletproof)) + if (!cryptonote::construct_tx_and_get_tx_key(from, subaddresses, actual_sources, to, boost::none, {}, tx, 0, tx_key, extra_keys, rct, bulletproof ? rct::RangeProofBulletproof : rct::RangeProofBorromean)) throw std::runtime_error{"transaction construction error"}; return tx; From 7f48bf05d7229112fb16373fc58e540cdae3b0d0 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 18 Jan 2018 12:00:47 +0000 Subject: [PATCH 0347/1404] multiexp: bos coster now works for just one point --- src/ringct/multiexp.cc | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/ringct/multiexp.cc b/src/ringct/multiexp.cc index b70d92d4672..7ed9672f231 100644 --- a/src/ringct/multiexp.cc +++ b/src/ringct/multiexp.cc @@ -155,7 +155,7 @@ rct::key bos_coster_heap_conv_robust(std::vector data) MULTIEXP_PERF(PERF_TIMER_START_UNIT(bos_coster, 1000000)); MULTIEXP_PERF(PERF_TIMER_START_UNIT(setup, 1000000)); size_t points = data.size(); - CHECK_AND_ASSERT_THROW_MES(points > 1, "Not enough points"); + CHECK_AND_ASSERT_THROW_MES(points > 0, "Not enough points"); std::vector heap; heap.reserve(points); for (size_t n = 0; n < points; ++n) @@ -164,6 +164,16 @@ rct::key bos_coster_heap_conv_robust(std::vector data) heap.push_back(n); } points = heap.size(); + if (points == 0) + return rct::identity(); + if (points < 2) + { + ge_p2 p2; + ge_scalarmult(&p2, data[0].scalar.bytes, &data[0].point); + rct::key res; + ge_tobytes(res.bytes, &p2); + return res; + } auto Comp = [&](size_t e0, size_t e1) { return data[e0].scalar < data[e1].scalar; }; std::make_heap(heap.begin(), heap.end(), Comp); From e895c3def1aa6c037c3d9c2daca8dacbd62e74dd Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 18 Jan 2018 12:01:45 +0000 Subject: [PATCH 0348/1404] make straus cached mode thread safe, and add tests for it --- src/ringct/bulletproofs.cc | 11 ++- src/ringct/multiexp.cc | 80 ++++++++++------ src/ringct/multiexp.h | 7 +- tests/performance_tests/main.cpp | 7 ++ tests/performance_tests/multiexp.h | 7 +- tests/unit_tests/CMakeLists.txt | 1 + tests/unit_tests/multiexp.cpp | 149 +++++++++++++++++++++++++++++ 7 files changed, 230 insertions(+), 32 deletions(-) create mode 100644 tests/unit_tests/multiexp.cpp diff --git a/src/ringct/bulletproofs.cc b/src/ringct/bulletproofs.cc index 1c29b1b99cb..6ba984b0331 100644 --- a/src/ringct/bulletproofs.cc +++ b/src/ringct/bulletproofs.cc @@ -61,6 +61,7 @@ static constexpr size_t maxM = 16; static rct::key Hi[maxN*maxM], Gi[maxN*maxM]; static ge_p3 Hi_p3[maxN*maxM], Gi_p3[maxN*maxM]; static ge_dsmp Gprecomp[maxN*maxM], Hprecomp[maxN*maxM]; +static std::shared_ptr HiGi_cache; static const rct::key TWO = { {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 } }; static const rct::keyV oneN = vector_dup(rct::identity(), maxN); static const rct::keyV twoN = vector_powers(TWO, maxN); @@ -70,7 +71,7 @@ static boost::mutex init_mutex; static inline rct::key multiexp(const std::vector &data, bool HiGi) { if (HiGi || data.size() < 1000) - return straus(data, HiGi); + return straus(data, HiGi ? HiGi_cache: NULL); else return bos_coster_heap_conv_robust(data); } @@ -116,6 +117,7 @@ static void init_exponents() static bool init_done = false; if (init_done) return; + std::vector data; for (size_t i = 0; i < maxN*maxM; ++i) { Hi[i] = get_exponent(rct::H, i * 2); @@ -124,8 +126,13 @@ static void init_exponents() Gi[i] = get_exponent(rct::H, i * 2 + 1); rct::precomp(Gprecomp[i], Gi[i]); CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&Gi_p3[i], Gi[i].bytes) == 0, "ge_frombytes_vartime failed"); + + data.push_back({rct::zero(), Gi[i]}); + data.push_back({rct::zero(), Hi[i]}); } - MINFO("cache size: " << (sizeof(Hi)+sizeof(Hprecomp)+sizeof(Hi_p3))*2/1024 << " kB"); + HiGi_cache = straus_init_cache(data); + size_t cache_size = (sizeof(Hi)+sizeof(Hprecomp)+sizeof(Hi_p3))*2 + straus_get_cache_size(HiGi_cache); + MINFO("cache size: " << cache_size/1024 << " kB"); init_done = true; } diff --git a/src/ringct/multiexp.cc b/src/ringct/multiexp.cc index 7ed9672f231..4f16bd588f0 100644 --- a/src/ringct/multiexp.cc +++ b/src/ringct/multiexp.cc @@ -259,42 +259,66 @@ rct::key bos_coster_heap_conv_robust(std::vector data) return res; } -rct::key straus(const std::vector &data, bool HiGi) +struct straus_cached_data { - MULTIEXP_PERF(PERF_TIMER_UNIT(straus, 1000000)); + std::vector> multiples; +}; - MULTIEXP_PERF(PERF_TIMER_START_UNIT(setup, 1000000)); - static constexpr unsigned int c = 4; - static constexpr unsigned int mask = (1<> HiGi_multiples; - std::vector> local_multiples, &multiples = HiGi ? HiGi_multiples : local_multiples; +static constexpr unsigned int STRAUS_C = 4; + +std::shared_ptr straus_init_cache(const std::vector &data) +{ + MULTIEXP_PERF(PERF_TIMER_START_UNIT(multiples, 1000000)); ge_cached cached; ge_p1p1 p1; ge_p3 p3; + std::shared_ptr cache(new straus_cached_data()); - std::vector skip(data.size()); - for (size_t i = 0; i < data.size(); ++i) - skip[i] = data[i].scalar == rct::zero() || !memcmp(&data[i].point, &ge_p3_identity, sizeof(ge_p3)); - - MULTIEXP_PERF(PERF_TIMER_START_UNIT(multiples, 1000000)); - multiples.resize(1<multiples.resize(1<multiples[1].size(); + cache->multiples[1].resize(std::max(offset, data.size())); for (size_t i = offset; i < data.size(); ++i) - ge_p3_to_cached(&multiples[1][i], &data[i].point); - for (size_t i=2;i<1<multiples[1][i], &data[i].point); + for (size_t i=2;i<1<multiples[i].resize(std::max(offset, data.size())); for (size_t j=offset;jmultiples[i-1][j]); ge_p1p1_to_p3(&p3, &p1); - ge_p3_to_cached(&multiples[i][j], &p3); + ge_p3_to_cached(&cache->multiples[i][j], &p3); } } MULTIEXP_PERF(PERF_TIMER_STOP(multiples)); + return cache; +} + +size_t straus_get_cache_size(const std::shared_ptr &cache) +{ + size_t sz = 0; + for (const auto &e0: cache->multiples) + sz += e0.size() * sizeof(ge_p3); + return sz; +} + +rct::key straus(const std::vector &data, const std::shared_ptr &cache) +{ + MULTIEXP_PERF(PERF_TIMER_UNIT(straus, 1000000)); + bool HiGi = cache != NULL; + + MULTIEXP_PERF(PERF_TIMER_START_UNIT(setup, 1000000)); + static constexpr unsigned int mask = (1< local_cache = cache == NULL ? straus_init_cache(data) : cache; + ge_cached cached; + ge_p1p1 p1; + ge_p3 p3; + + std::vector skip(data.size()); + for (size_t i = 0; i < data.size(); ++i) + skip[i] = data[i].scalar == rct::zero() || !memcmp(&data[i].point, &ge_p3_identity, sizeof(ge_p3)); + MULTIEXP_PERF(PERF_TIMER_START_UNIT(digits, 1000000)); std::vector> digits; digits.resize(data.size()); @@ -305,7 +329,7 @@ rct::key straus(const std::vector &data, bool HiGi) memcpy(bytes33, data[j].scalar.bytes, 32); bytes33[32] = 0; #if 1 - static_assert(c == 4, "optimized version needs c == 4"); + static_assert(STRAUS_C == 4, "optimized version needs STRAUS_C == 4"); const unsigned char *bytes = bytes33; unsigned int i; for (i = 0; i < 256; i += 8, bytes++) @@ -339,22 +363,22 @@ rct::key straus(const std::vector &data, bool HiGi) maxscalar = data[i].scalar; size_t i = 0; while (i < 256 && !(maxscalar < pow2(i))) - i += c; + i += STRAUS_C; MULTIEXP_PERF(PERF_TIMER_STOP(setup)); ge_p3 res_p3 = ge_p3_identity; - if (!(i < c)) + if (!(i < STRAUS_C)) goto skipfirst; - while (!(i < c)) + while (!(i < STRAUS_C)) { - for (size_t j = 0; j < c; ++j) + for (size_t j = 0; j < STRAUS_C; ++j) { ge_p3_to_cached(&cached, &res_p3); ge_add(&p1, &res_p3, &cached); ge_p1p1_to_p3(&res_p3, &p1); } skipfirst: - i -= c; + i -= STRAUS_C; for (size_t j = 0; j < data.size(); ++j) { if (skip[j]) @@ -362,7 +386,7 @@ rct::key straus(const std::vector &data, bool HiGi) int digit = digits[j][i]; if (digit) { - ge_add(&p1, &res_p3, &multiples[digit][j]); + ge_add(&p1, &res_p3, &local_cache->multiples[digit][j]); ge_p1p1_to_p3(&res_p3, &p1); } } diff --git a/src/ringct/multiexp.h b/src/ringct/multiexp.h index cc53e633ed3..44998e2e0f9 100644 --- a/src/ringct/multiexp.h +++ b/src/ringct/multiexp.h @@ -36,6 +36,7 @@ #include #include "crypto/crypto.h" #include "rctTypes.h" +#include "misc_log_ex.h" namespace rct { @@ -52,9 +53,13 @@ struct MultiexpData { } }; +struct straus_cached_data; + rct::key bos_coster_heap_conv(std::vector data); rct::key bos_coster_heap_conv_robust(std::vector data); -rct::key straus(const std::vector &data, bool HiGi = false); +std::shared_ptr straus_init_cache(const std::vector &data); +size_t straus_get_cache_size(const std::shared_ptr &cache); +rct::key straus(const std::vector &data, const std::shared_ptr &cache = NULL); } diff --git a/tests/performance_tests/main.cpp b/tests/performance_tests/main.cpp index c18a653c898..a00f05ce77b 100644 --- a/tests/performance_tests/main.cpp +++ b/tests/performance_tests/main.cpp @@ -221,6 +221,13 @@ int main(int argc, char** argv) TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_straus, 1024); TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_straus, 4096); + TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_straus_cached, 2); + TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_straus_cached, 8); + TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_straus_cached, 16); + TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_straus_cached, 256); + TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_straus_cached, 1024); + TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_straus_cached, 4096); + std::cout << "Tests finished. Elapsed time: " << timer.elapsed_ms() / 1000 << " sec" << std::endl; return 0; diff --git a/tests/performance_tests/multiexp.h b/tests/performance_tests/multiexp.h index ac5f60fdf09..ab5af166b30 100644 --- a/tests/performance_tests/multiexp.h +++ b/tests/performance_tests/multiexp.h @@ -38,6 +38,7 @@ enum test_multiexp_algorithm { multiexp_bos_coster, multiexp_straus, + multiexp_straus_cached, }; template @@ -59,6 +60,7 @@ class test_multiexp rct::key kn = rct::scalarmultKey(point, data[n].scalar); res = rct::addKeys(res, kn); } + cache = rct::straus_init_cache(data); return true; } @@ -69,7 +71,9 @@ class test_multiexp case multiexp_bos_coster: return res == bos_coster_heap_conv_robust(data); case multiexp_straus: - return res == straus(data, false); + return res == straus(data); + case multiexp_straus_cached: + return res == straus(data, cache); default: return false; } @@ -77,5 +81,6 @@ class test_multiexp private: std::vector data; + std::shared_ptr cache; rct::key res; }; diff --git a/tests/unit_tests/CMakeLists.txt b/tests/unit_tests/CMakeLists.txt index 7366990adf3..cdb74169906 100644 --- a/tests/unit_tests/CMakeLists.txt +++ b/tests/unit_tests/CMakeLists.txt @@ -58,6 +58,7 @@ set(unit_tests_sources mlocker.cpp mnemonics.cpp mul_div.cpp + multiexp.cpp multisig.cpp parse_amount.cpp random.cpp diff --git a/tests/unit_tests/multiexp.cpp b/tests/unit_tests/multiexp.cpp new file mode 100644 index 00000000000..2dce5bb8094 --- /dev/null +++ b/tests/unit_tests/multiexp.cpp @@ -0,0 +1,149 @@ +// Copyright (c) 2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "gtest/gtest.h" + +#include "crypto/crypto.h" +#include "ringct/rctOps.h" +#include "ringct/multiexp.h" + +static const rct::key TESTSCALAR = rct::H; +static const rct::key TESTPOINT = rct::scalarmultBase(rct::H); + +static rct::key basic(const std::vector &data) +{ + ge_p3 res_p3 = ge_p3_identity; + for (const auto &d: data) + { + ge_cached cached; + ge_p3 p3; + ge_p1p1 p1; + ge_scalarmult_p3(&p3, d.scalar.bytes, &d.point); + ge_p3_to_cached(&cached, &p3); + ge_add(&p1, &res_p3, &cached); + ge_p1p1_to_p3(&res_p3, &p1); + } + rct::key res; + ge_p3_tobytes(res.bytes, &res_p3); + return res; +} + +static ge_p3 get_p3(const rct::key &point) +{ + ge_p3 p3; + EXPECT_TRUE(ge_frombytes_vartime(&p3, point.bytes) == 0); + return p3; +} + +TEST(multiexp, bos_coster_empty) +{ + std::vector data; + data.push_back({rct::zero(), get_p3(rct::identity())}); + ASSERT_TRUE(basic(data) == bos_coster_heap_conv_robust(data)); +} + +TEST(multiexp, straus_empty) +{ + std::vector data; + data.push_back({rct::zero(), get_p3(rct::identity())}); + ASSERT_TRUE(basic(data) == straus(data)); +} + +TEST(multiexp, bos_coster_only_zeroes) +{ + std::vector data; + for (int n = 0; n < 16; ++n) + data.push_back({rct::zero(), get_p3(TESTPOINT)}); + ASSERT_TRUE(basic(data) == bos_coster_heap_conv_robust(data)); +} + +TEST(multiexp, straus_only_zeroes) +{ + std::vector data; + for (int n = 0; n < 16; ++n) + data.push_back({rct::zero(), get_p3(TESTPOINT)}); + ASSERT_TRUE(basic(data) == straus(data)); +} + +TEST(multiexp, bos_coster_only_identities) +{ + std::vector data; + for (int n = 0; n < 16; ++n) + data.push_back({TESTSCALAR, get_p3(rct::identity())}); + ASSERT_TRUE(basic(data) == bos_coster_heap_conv_robust(data)); +} + +TEST(multiexp, straus_only_identities) +{ + std::vector data; + for (int n = 0; n < 16; ++n) + data.push_back({TESTSCALAR, get_p3(rct::identity())}); + ASSERT_TRUE(basic(data) == straus(data)); +} + +TEST(multiexp, bos_coster_random) +{ + std::vector data; + for (int n = 0; n < 32; ++n) + { + data.push_back({rct::skGen(), get_p3(rct::scalarmultBase(rct::skGen()))}); + ASSERT_TRUE(basic(data) == bos_coster_heap_conv_robust(data)); + } +} + +TEST(multiexp, straus_random) +{ + std::vector data; + for (int n = 0; n < 32; ++n) + { + data.push_back({rct::skGen(), get_p3(rct::scalarmultBase(rct::skGen()))}); + ASSERT_TRUE(basic(data) == straus(data)); + } +} + +TEST(multiexp, straus_cached) +{ + static constexpr size_t N = 256; + std::vector P(N); + for (size_t n = 0; n < N; ++n) + { + P[n].scalar = rct::zero(); + ASSERT_TRUE(ge_frombytes_vartime(&P[n].point, rct::scalarmultBase(rct::skGen()).bytes) == 0); + } + std::shared_ptr cache = rct::straus_init_cache(P); + for (size_t n = 0; n < N/16; ++n) + { + std::vector data; + size_t sz = 1 + crypto::rand() % (N-1); + for (size_t s = 0; s < sz; ++s) + { + data.push_back({rct::skGen(), P[s].point}); + } + ASSERT_TRUE(basic(data) == straus(data, cache)); + } +} From bacf0a1e2ff54ef1fc77e3f6ec92e87946084c1a Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 3 Feb 2018 14:36:29 +0000 Subject: [PATCH 0349/1404] bulletproofs: add aggregated verification Ported from sarang's java code --- src/cryptonote_core/blockchain.cpp | 2 +- src/cryptonote_core/cryptonote_core.cpp | 2 +- src/ringct/bulletproofs.cc | 394 +++++++++++--------- src/ringct/bulletproofs.h | 2 + src/ringct/rctSigs.cpp | 163 +++++--- src/ringct/rctSigs.h | 6 +- tests/performance_tests/bulletproof.h | 38 ++ tests/performance_tests/main.cpp | 11 + tests/performance_tests/performance_tests.h | 2 + tests/unit_tests/bulletproofs.cpp | 19 + tests/unit_tests/ringct.cpp | 17 + 11 files changed, 425 insertions(+), 231 deletions(-) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index c815b507e69..0800409b530 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -2988,7 +2988,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, } } - if (!rct::verRctSimple(rv, false)) + if (!rct::verRctNonSemanticsSimple(rv)) { MERROR_VER("Failed to check ringct signatures!"); return false; diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index d0db387995f..4928bb52849 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -896,7 +896,7 @@ namespace cryptonote return false; case rct::RCTTypeSimple: case rct::RCTTypeSimpleBulletproof: - if (!rct::verRctSimple(rv, true)) + if (!rct::verRctSemanticsSimple(rv)) { MERROR_VER("rct signature semantics check failed"); return false; diff --git a/src/ringct/bulletproofs.cc b/src/ringct/bulletproofs.cc index 6ba984b0331..e2540fb229f 100644 --- a/src/ringct/bulletproofs.cc +++ b/src/ringct/bulletproofs.cc @@ -889,219 +889,248 @@ Bulletproof bulletproof_PROVE(const std::vector &v, const rct::keyV &g } /* Given a range proof, determine if it is valid */ -bool bulletproof_VERIFY(const Bulletproof &proof) +bool bulletproof_VERIFY(const std::vector &proofs) { init_exponents(); - CHECK_AND_ASSERT_MES(proof.V.size() >= 1, false, "V does not have at least one element"); - CHECK_AND_ASSERT_MES(proof.L.size() == proof.R.size(), false, "Mismatched L and R sizes"); - CHECK_AND_ASSERT_MES(proof.L.size() > 0, false, "Empty proof"); - - const size_t logN = 6; - const size_t N = 1 << logN; - rct::key tmp, tmp2; - - size_t M, logM; - for (logM = 0; (M = 1<= 1, false, "V does not have at least one element"); + CHECK_AND_ASSERT_MES(proof.L.size() == proof.R.size(), false, "Mismatched L and R sizes"); + CHECK_AND_ASSERT_MES(proof.L.size() > 0, false, "Empty proof"); + + max_length = std::max(max_length, proof.L.size()); } - PERF_TIMER_STOP(VERIFY_line_61); + CHECK_AND_ASSERT_MES(max_length < 32, false, "At least one proof is too large"); + size_t maxMN = 1u << max_length; - // bos coster is slower for small numbers of calcs, straus seems not - if (1) + const size_t logN = 6; + const size_t N = 1 << logN; + rct::key tmp; + + // setup weighted aggregates + rct::key Z0 = rct::identity(); + rct::key z1 = rct::zero(); + rct::key Z2 = rct::identity(); + rct::key z3 = rct::zero(); + rct::keyV z4(maxMN, rct::zero()), z5(maxMN, rct::zero()); + for (const Bulletproof *p: proofs) { - PERF_TIMER_START_BP(VERIFY_line_61rl_new); - sc_muladd(tmp.bytes, z.bytes, ip1y.bytes, k.bytes); - std::vector multiexp_data; - multiexp_data.reserve(3+proof.V.size()); - multiexp_data.emplace_back(tmp, rct::H); - for (size_t j = 0; j < proof.V.size(); j++) + const Bulletproof &proof = *p; + + size_t M, logM; + for (logM = 0; (M = 1< multiexp_data; + multiexp_data.reserve(3+proof.V.size()); + multiexp_data.emplace_back(tmp, rct::H); + for (size_t j = 0; j < proof.V.size(); j++) + { + multiexp_data.emplace_back(zpow[j+2], proof.V[j]); + } + multiexp_data.emplace_back(x, proof.T1); + rct::key xsq; + sc_mul(xsq.bytes, x.bytes, x.bytes); + multiexp_data.emplace_back(xsq, proof.T2); + L61Right = multiexp(multiexp_data, false); + PERF_TIMER_STOP(VERIFY_line_61rl_new); } - for (size_t j = proof.V.size() & 0xfffffffe; j < M; j++) + else { - CHECK_AND_ASSERT_MES(j+2 < zpow.size(), false, "invalid zpow index"); - // faster equivalent to: - // tmp = rct::scalarmultKey(j < proof.V.size() ? proof.V[j] : rct::identity(), zpow[j+2]); - // rct::addKeys(L61Right, L61Right, tmp); - if (j < proof.V.size()) - addKeys_acc_p3(&L61Right_p3, zpow[j+2], proof.V[j]); - } - - addKeys_acc_p3(&L61Right_p3, x, proof.T1); - - rct::key xsq; - sc_mul(xsq.bytes, x.bytes, x.bytes); - addKeys_acc_p3(&L61Right_p3, xsq, proof.T2); - ge_p3_tobytes(L61Right.bytes, &L61Right_p3); - PERF_TIMER_STOP(VERIFY_line_61rl_old); - } - - if (!(L61Right == L61Left)) - { - MERROR("Verification failure at step 1"); - return false; - } - - PERF_TIMER_START_BP(VERIFY_line_62); - // PAPER LINE 62 - rct::key P = rct::addKeys(proof.A, rct::scalarmultKey(proof.S, x)); - PERF_TIMER_STOP(VERIFY_line_62); + PERF_TIMER_START_BP(VERIFY_line_61rl_old); + sc_muladd(tmp.bytes, z.bytes, ip1y.bytes, k.bytes); + L61Right = rct::scalarmultKey(rct::H, tmp); + ge_p3 L61Right_p3; + CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&L61Right_p3, L61Right.bytes) == 0, "ge_frombytes_vartime failed"); + for (size_t j = 0; j+1 < proof.V.size(); j += 2) + { + CHECK_AND_ASSERT_MES(j+2+1 < zpow.size(), false, "invalid zpow index"); + ge_dsmp precomp0, precomp1; + rct::precomp(precomp0, j < proof.V.size() ? proof.V[j] : rct::identity()); + rct::precomp(precomp1, j+1 < proof.V.size() ? proof.V[j+1] : rct::identity()); + rct::addKeys3acc_p3(&L61Right_p3, zpow[j+2], precomp0, zpow[j+2+1], precomp1); + } + for (size_t j = proof.V.size() & 0xfffffffe; j < M; j++) + { + CHECK_AND_ASSERT_MES(j+2 < zpow.size(), false, "invalid zpow index"); + // faster equivalent to: + // tmp = rct::scalarmultKey(j < proof.V.size() ? proof.V[j] : rct::identity(), zpow[j+2]); + // rct::addKeys(L61Right, L61Right, tmp); + if (j < proof.V.size()) + addKeys_acc_p3(&L61Right_p3, zpow[j+2], proof.V[j]); + } - // Compute the number of rounds for the inner product - const size_t rounds = logM+logN; - CHECK_AND_ASSERT_MES(rounds > 0, false, "Zero rounds"); + addKeys_acc_p3(&L61Right_p3, x, proof.T1); - PERF_TIMER_START_BP(VERIFY_line_21_22); - // PAPER LINES 21-22 - // The inner product challenges are computed per round - rct::keyV w(rounds); - for (size_t i = 0; i < rounds; ++i) - { - w[i] = hash_cache_mash(hash_cache, proof.L[i], proof.R[i]); - } - PERF_TIMER_STOP(VERIFY_line_21_22); + rct::key xsq; + sc_mul(xsq.bytes, x.bytes, x.bytes); + addKeys_acc_p3(&L61Right_p3, xsq, proof.T2); + ge_p3_tobytes(L61Right.bytes, &L61Right_p3); + PERF_TIMER_STOP(VERIFY_line_61rl_old); + } - PERF_TIMER_START_BP(VERIFY_line_24_25); - // Basically PAPER LINES 24-25 - // Compute the curvepoints from G[i] and H[i] - rct::key yinvpow = rct::identity(); - rct::key ypow = rct::identity(); + if (!(L61Right == L61Left)) + { + MERROR("Verification failure at step 1"); + return false; + } - PERF_TIMER_START_BP(VERIFY_line_24_25_invert); - const rct::key yinv = invert(y); - rct::keyV winv(rounds); - for (size_t i = 0; i < rounds; ++i) - winv[i] = invert(w[i]); - PERF_TIMER_STOP(VERIFY_line_24_25_invert); + PERF_TIMER_START_BP(VERIFY_line_62); + // PAPER LINE 62 + rct::addKeys(Z0, Z0, rct::scalarmultKey(rct::addKeys(proof.A, rct::scalarmultKey(proof.S, x)), weight)); + PERF_TIMER_STOP(VERIFY_line_62); - std::vector multiexp_data; - multiexp_data.clear(); - multiexp_data.reserve(MN*2); - for (size_t i = 0; i < MN; ++i) - { - // Convert the index to binary IN REVERSE and construct the scalar exponent - rct::key g_scalar = proof.a; - rct::key h_scalar; - sc_mul(h_scalar.bytes, proof.b.bytes, yinvpow.bytes); + // Compute the number of rounds for the inner product + const size_t rounds = logM+logN; + CHECK_AND_ASSERT_MES(rounds > 0, false, "Zero rounds"); - for (size_t j = rounds; j-- > 0; ) + PERF_TIMER_START_BP(VERIFY_line_21_22); + // PAPER LINES 21-22 + // The inner product challenges are computed per round + rct::keyV w(rounds); + for (size_t i = 0; i < rounds; ++i) + { + w[i] = hash_cache_mash(hash_cache, proof.L[i], proof.R[i]); + } + PERF_TIMER_STOP(VERIFY_line_21_22); + + PERF_TIMER_START_BP(VERIFY_line_24_25); + // Basically PAPER LINES 24-25 + // Compute the curvepoints from G[i] and H[i] + rct::key yinvpow = rct::identity(); + rct::key ypow = rct::identity(); + + PERF_TIMER_START_BP(VERIFY_line_24_25_invert); + const rct::key yinv = invert(y); + rct::keyV winv(rounds); + for (size_t i = 0; i < rounds; ++i) + winv[i] = invert(w[i]); + PERF_TIMER_STOP(VERIFY_line_24_25_invert); + + for (size_t i = 0; i < MN; ++i) { - size_t J = w.size() - j - 1; + // Convert the index to binary IN REVERSE and construct the scalar exponent + rct::key g_scalar = proof.a; + rct::key h_scalar; + sc_mul(h_scalar.bytes, proof.b.bytes, yinvpow.bytes); - if ((i & (((size_t)1)< 0; ) { - sc_mul(g_scalar.bytes, g_scalar.bytes, winv[J].bytes); - sc_mul(h_scalar.bytes, h_scalar.bytes, w[J].bytes); + size_t J = w.size() - j - 1; + + if ((i & (((size_t)1)< multiexp_data; + multiexp_data.reserve(2*rounds); - if (i != MN-1) + sc_muladd(z1.bytes, proof.mu.bytes, weight.bytes, z1.bytes); + for (size_t i = 0; i < rounds; ++i) { - sc_mul(yinvpow.bytes, yinvpow.bytes, yinv.bytes); - sc_mul(ypow.bytes, ypow.bytes, y.bytes); + sc_mul(tmp.bytes, w[i].bytes, w[i].bytes); + multiexp_data.emplace_back(tmp, proof.L[i]); + sc_mul(tmp.bytes, winv[i].bytes, winv[i].bytes); + multiexp_data.emplace_back(tmp, proof.R[i]); } + rct::key acc = multiexp(multiexp_data, false); + rct::addKeys(Z2, Z2, rct::scalarmultKey(acc, weight)); + sc_mulsub(tmp.bytes, proof.a.bytes, proof.b.bytes, proof.t.bytes); + sc_mul(tmp.bytes, tmp.bytes, x_ip.bytes); + sc_muladd(z3.bytes, tmp.bytes, weight.bytes, z3.bytes); + PERF_TIMER_STOP(VERIFY_line_26_new); } - rct::key inner_prod = multiexp(multiexp_data, true); - PERF_TIMER_STOP(VERIFY_line_24_25); - - // PAPER LINE 26 - rct::key pprime; - PERF_TIMER_START_BP(VERIFY_line_26_new); - multiexp_data.clear(); - multiexp_data.reserve(1+2*rounds); + // now check all proofs at once + PERF_TIMER_START_BP(VERIFY_step2_check); + rct::key Y = Z0; + sc_sub(tmp.bytes, rct::zero().bytes, z1.bytes); + rct::addKeys(Y, Y, rct::scalarmultBase(tmp)); + rct::addKeys(Y, Y, Z2); + rct::addKeys(Y, Y, rct::scalarmultKey(rct::H, z3)); - sc_sub(tmp.bytes, rct::zero().bytes, proof.mu.bytes); - rct::addKeys(pprime, P, rct::scalarmultBase(tmp)); - for (size_t i = 0; i < rounds; ++i) + std::vector multiexp_data; + multiexp_data.reserve(2 * maxMN); + for (size_t i = 0; i < maxMN; ++i) { - sc_mul(tmp.bytes, w[i].bytes, w[i].bytes); - sc_mul(tmp2.bytes, winv[i].bytes, winv[i].bytes); - multiexp_data.emplace_back(tmp, proof.L[i]); - multiexp_data.emplace_back(tmp2, proof.R[i]); + sc_sub(tmp.bytes, rct::zero().bytes, z4[i].bytes); + multiexp_data.emplace_back(tmp, Gi_p3[i]); + sc_sub(tmp.bytes, rct::zero().bytes, z5[i].bytes); + multiexp_data.emplace_back(tmp, Hi_p3[i]); } - sc_mul(tmp.bytes, proof.t.bytes, x_ip.bytes); - multiexp_data.emplace_back(tmp, rct::H); - addKeys(pprime, pprime, multiexp(multiexp_data, false)); - PERF_TIMER_STOP(VERIFY_line_26_new); - - PERF_TIMER_START_BP(VERIFY_step2_check); - sc_mul(tmp.bytes, proof.a.bytes, proof.b.bytes); - sc_mul(tmp.bytes, tmp.bytes, x_ip.bytes); - tmp = rct::scalarmultKey(rct::H, tmp); - rct::addKeys(tmp, tmp, inner_prod); + rct::addKeys(Y, Y, multiexp(multiexp_data, true)); PERF_TIMER_STOP(VERIFY_step2_check); - if (!(pprime == tmp)) + + if (!(Y == rct::identity())) { MERROR("Verification failure at step 2"); return false; @@ -1111,4 +1140,19 @@ bool bulletproof_VERIFY(const Bulletproof &proof) return true; } +bool bulletproof_VERIFY(const std::vector &proofs) +{ + std::vector proof_pointers; + for (const Bulletproof &proof: proofs) + proof_pointers.push_back(&proof); + return bulletproof_VERIFY(proof_pointers); +} + +bool bulletproof_VERIFY(const Bulletproof &proof) +{ + std::vector proofs; + proofs.push_back(&proof); + return bulletproof_VERIFY(proofs); +} + } diff --git a/src/ringct/bulletproofs.h b/src/ringct/bulletproofs.h index 3dfa38b1246..b86202cccbf 100644 --- a/src/ringct/bulletproofs.h +++ b/src/ringct/bulletproofs.h @@ -43,6 +43,8 @@ Bulletproof bulletproof_PROVE(uint64_t v, const rct::key &gamma); Bulletproof bulletproof_PROVE(const rct::keyV &v, const rct::keyV &gamma); Bulletproof bulletproof_PROVE(const std::vector &v, const rct::keyV &gamma); bool bulletproof_VERIFY(const Bulletproof &proof); +bool bulletproof_VERIFY(const std::vector &proofs); +bool bulletproof_VERIFY(const std::vector &proofs); } diff --git a/src/ringct/rctSigs.cpp b/src/ringct/rctSigs.cpp index 6dcb203bffa..2e2b07fcc1f 100644 --- a/src/ringct/rctSigs.cpp +++ b/src/ringct/rctSigs.cpp @@ -70,6 +70,13 @@ namespace rct { catch (...) { return false; } } + bool verBulletproof(const std::vector &proofs) + { + try { return bulletproof_VERIFY(proofs); } + // we can get deep throws from ge_frombytes_vartime if input isn't valid + catch (...) { return false; } + } + //Borromean (c.f. gmax/andytoshi's paper) boroSig genBorromean(const key64 x, const key64 P1, const key64 P2, const bits indices) { key64 L[2], alpha; @@ -918,15 +925,23 @@ namespace rct { //ver RingCT simple //assumes only post-rct style inputs (at least for max anonymity) - bool verRctSimple(const rctSig & rv, bool semantics) { + bool verRctSemanticsSimple(const std::vector & rvv) { try { - PERF_TIMER(verRctSimple); + PERF_TIMER(verRctSemanticsSimple); - CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple || rv.type == RCTTypeSimpleBulletproof, false, "verRctSimple called on non simple rctSig"); - const bool bulletproof = is_rct_bulletproof(rv.type); - if (semantics) + tools::threadpool& tpool = tools::threadpool::getInstance(); + tools::threadpool::waiter waiter; + std::deque results; + std::vector proofs; + size_t max_non_bp_proofs = 0, offset = 0; + + for (const rctSig *rvp: rvv) { + CHECK_AND_ASSERT_MES(rvp, false, "rctSig pointer is NULL"); + const rctSig &rv = *rvp; + CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple || rv.type == RCTTypeSimpleBulletproof, false, "verRctSemanticsSimple called on non simple rctSig"); + const bool bulletproof = is_rct_bulletproof(rv.type); if (bulletproof) { CHECK_AND_ASSERT_MES(rv.outPk.size() == n_bulletproof_amounts(rv.p.bulletproofs), false, "Mismatched sizes of outPk and bulletproofs"); @@ -940,28 +955,22 @@ namespace rct { CHECK_AND_ASSERT_MES(rv.p.pseudoOuts.empty(), false, "rv.p.pseudoOuts is not empty"); } CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.ecdhInfo.size(), false, "Mismatched sizes of outPk and rv.ecdhInfo"); - } - else - { - // semantics check is early, and mixRing/MGs aren't resolved yet - if (bulletproof) - CHECK_AND_ASSERT_MES(rv.p.pseudoOuts.size() == rv.mixRing.size(), false, "Mismatched sizes of rv.p.pseudoOuts and mixRing"); - else - CHECK_AND_ASSERT_MES(rv.pseudoOuts.size() == rv.mixRing.size(), false, "Mismatched sizes of rv.pseudoOuts and mixRing"); - } - const size_t threads = std::max(rv.outPk.size(), rv.mixRing.size()); + if (!bulletproof) + max_non_bp_proofs += rv.p.rangeSigs.size(); + } - std::deque results(threads); - tools::threadpool& tpool = tools::threadpool::getInstance(); - tools::threadpool::waiter waiter; + results.resize(max_non_bp_proofs); + for (const rctSig *rvp: rvv) + { + const rctSig &rv = *rvp; - const keyV &pseudoOuts = bulletproof ? rv.p.pseudoOuts : rv.pseudoOuts; + const bool bulletproof = is_rct_bulletproof(rv.type); + const keyV &pseudoOuts = bulletproof ? rv.p.pseudoOuts : rv.pseudoOuts; - if (semantics) { key sumOutpks = identity(); for (size_t i = 0; i < rv.outPk.size(); i++) { - addKeys(sumOutpks, sumOutpks, rv.outPk[i].mask); + addKeys(sumOutpks, sumOutpks, rv.outPk[i].mask); } DP(sumOutpks); key txnFeeKey = scalarmultH(d2h(rv.txnFee)); @@ -969,50 +978,100 @@ namespace rct { key sumPseudoOuts = identity(); for (size_t i = 0 ; i < pseudoOuts.size() ; i++) { - addKeys(sumPseudoOuts, sumPseudoOuts, pseudoOuts[i]); + addKeys(sumPseudoOuts, sumPseudoOuts, pseudoOuts[i]); } DP(sumPseudoOuts); //check pseudoOuts vs Outs.. if (!equalKeys(sumPseudoOuts, sumOutpks)) { - LOG_PRINT_L1("Sum check failed"); - return false; + LOG_PRINT_L1("Sum check failed"); + return false; } - results.clear(); - results.resize(bulletproof ? rv.p.bulletproofs.size() : rv.outPk.size()); if (bulletproof) + { for (size_t i = 0; i < rv.p.bulletproofs.size(); i++) - tpool.submit(&waiter, [&, i] { results[i] = verBulletproof(rv.p.bulletproofs[i]); }); + proofs.push_back(&rv.p.bulletproofs[i]); + } else + { for (size_t i = 0; i < rv.p.rangeSigs.size(); i++) - tpool.submit(&waiter, [&, i] { results[i] = verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]); }); - waiter.wait(&tpool); - - for (size_t i = 0; i < results.size(); ++i) { - if (!results[i]) { - LOG_PRINT_L1("Range proof verified failed for proof " << i); - return false; - } + tpool.submit(&waiter, [&, i, offset] { results[i+offset] = verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]); }); + offset += rv.p.rangeSigs.size(); } } - else { - const key message = get_pre_mlsag_hash(rv, hw::get_device("default")); - - results.clear(); - results.resize(rv.mixRing.size()); - for (size_t i = 0 ; i < rv.mixRing.size() ; i++) { - tpool.submit(&waiter, [&, i] { - results[i] = verRctMGSimple(message, rv.p.MGs[i], rv.mixRing[i], pseudoOuts[i]); - }, true); + if (!proofs.empty() && !verBulletproof(proofs)) + { + LOG_PRINT_L1("Aggregate range proof verified failed"); + return false; + } + + waiter.wait(&tpool); + for (size_t i = 0; i < results.size(); ++i) { + if (!results[i]) { + LOG_PRINT_L1("Range proof verified failed for proof " << i); + return false; } - waiter.wait(&tpool); + } - for (size_t i = 0; i < results.size(); ++i) { - if (!results[i]) { - LOG_PRINT_L1("verRctMGSimple failed for input " << i); - return false; - } + return true; + } + // we can get deep throws from ge_frombytes_vartime if input isn't valid + catch (const std::exception &e) + { + LOG_PRINT_L1("Error in verRctSemanticsSimple: " << e.what()); + return false; + } + catch (...) + { + LOG_PRINT_L1("Error in verRctSemanticsSimple, but not an actual exception"); + return false; + } + } + + bool verRctSemanticsSimple(const rctSig & rv) + { + return verRctSemanticsSimple(std::vector(1, &rv)); + } + + //ver RingCT simple + //assumes only post-rct style inputs (at least for max anonymity) + bool verRctNonSemanticsSimple(const rctSig & rv) { + try + { + PERF_TIMER(verRctNonSemanticsSimple); + + CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple || rv.type == RCTTypeSimpleBulletproof, false, "verRctNonSemanticsSimple called on non simple rctSig"); + const bool bulletproof = is_rct_bulletproof(rv.type); + // semantics check is early, and mixRing/MGs aren't resolved yet + if (bulletproof) + CHECK_AND_ASSERT_MES(rv.p.pseudoOuts.size() == rv.mixRing.size(), false, "Mismatched sizes of rv.p.pseudoOuts and mixRing"); + else + CHECK_AND_ASSERT_MES(rv.pseudoOuts.size() == rv.mixRing.size(), false, "Mismatched sizes of rv.pseudoOuts and mixRing"); + + const size_t threads = std::max(rv.outPk.size(), rv.mixRing.size()); + + std::deque results(threads); + tools::threadpool& tpool = tools::threadpool::getInstance(); + tools::threadpool::waiter waiter; + + const keyV &pseudoOuts = bulletproof ? rv.p.pseudoOuts : rv.pseudoOuts; + + const key message = get_pre_mlsag_hash(rv, hw::get_device("default")); + + results.clear(); + results.resize(rv.mixRing.size()); + for (size_t i = 0 ; i < rv.mixRing.size() ; i++) { + tpool.submit(&waiter, [&, i] { + results[i] = verRctMGSimple(message, rv.p.MGs[i], rv.mixRing[i], pseudoOuts[i]); + }); + } + waiter.wait(&tpool); + + for (size_t i = 0; i < results.size(); ++i) { + if (!results[i]) { + LOG_PRINT_L1("verRctMGSimple failed for input " << i); + return false; } } @@ -1021,12 +1080,12 @@ namespace rct { // we can get deep throws from ge_frombytes_vartime if input isn't valid catch (const std::exception &e) { - LOG_PRINT_L1("Error in verRct: " << e.what()); + LOG_PRINT_L1("Error in verRctNonSemanticsSimple: " << e.what()); return false; } catch (...) { - LOG_PRINT_L1("Error in verRct, but not an actual exception"); + LOG_PRINT_L1("Error in verRctNonSemanticsSimple, but not an actual exception"); return false; } } diff --git a/src/ringct/rctSigs.h b/src/ringct/rctSigs.h index 9a66310c709..d1090ca7792 100644 --- a/src/ringct/rctSigs.h +++ b/src/ringct/rctSigs.h @@ -125,8 +125,10 @@ namespace rct { rctSig genRctSimple(const key & message, const ctkeyV & inSk, const keyV & destinations, const std::vector & inamounts, const std::vector & outamounts, xmr_amount txnFee, const ctkeyM & mixRing, const keyV &amount_keys, const std::vector *kLRki, multisig_out *msout, const std::vector & index, ctkeyV &outSk, RangeProofType range_proof_type, hw::device &hwdev); bool verRct(const rctSig & rv, bool semantics); static inline bool verRct(const rctSig & rv) { return verRct(rv, true) && verRct(rv, false); } - bool verRctSimple(const rctSig & rv, bool semantics); - static inline bool verRctSimple(const rctSig & rv) { return verRctSimple(rv, true) && verRctSimple(rv, false); } + bool verRctSemanticsSimple(const rctSig & rv); + bool verRctSemanticsSimple(const std::vector & rv); + bool verRctNonSemanticsSimple(const rctSig & rv); + static inline bool verRctSimple(const rctSig & rv) { return verRctSemanticsSimple(rv) && verRctNonSemanticsSimple(rv); } xmr_amount decodeRct(const rctSig & rv, const key & sk, unsigned int i, key & mask, hw::device &hwdev); xmr_amount decodeRct(const rctSig & rv, const key & sk, unsigned int i, hw::device &hwdev); xmr_amount decodeRctSimple(const rctSig & rv, const key & sk, unsigned int i, key & mask, hw::device &hwdev); diff --git a/tests/performance_tests/bulletproof.h b/tests/performance_tests/bulletproof.h index b11c9fe97c1..7bb702c3d58 100644 --- a/tests/performance_tests/bulletproof.h +++ b/tests/performance_tests/bulletproof.h @@ -60,3 +60,41 @@ class test_bulletproof private: rct::Bulletproof proof; }; + +template +class test_aggregated_bulletproof +{ +public: + static const size_t loop_count = 500 / (N * repeat); + + bool init() + { + size_t o = start; + for (size_t n = 0; n < N; ++n) + { + //printf("adding %zu times %zu\n", repeat, o); + for (size_t i = 0; i < repeat; ++i) + proofs.push_back(rct::bulletproof_PROVE(std::vector(o, 749327532984), rct::skvGen(o))); + o = o * mul + add; + } + return true; + } + + bool test() + { + if (batch) + { + return rct::bulletproof_VERIFY(proofs); + } + else + { + for (const rct::Bulletproof &proof: proofs) + if (!rct::bulletproof_VERIFY(proof)) + return false; + return true; + } + } + +private: + std::vector proofs; +}; diff --git a/tests/performance_tests/main.cpp b/tests/performance_tests/main.cpp index a00f05ce77b..c125f90427e 100644 --- a/tests/performance_tests/main.cpp +++ b/tests/performance_tests/main.cpp @@ -183,6 +183,17 @@ int main(int argc, char** argv) TEST_PERFORMANCE2(filter, verbose, test_bulletproof, true, 15); TEST_PERFORMANCE2(filter, verbose, test_bulletproof, false, 15); + TEST_PERFORMANCE6(filter, verbose, test_aggregated_bulletproof, false, 2, 1, 1, 0, 4); + TEST_PERFORMANCE6(filter, verbose, test_aggregated_bulletproof, true, 2, 1, 1, 0, 4); + TEST_PERFORMANCE6(filter, verbose, test_aggregated_bulletproof, false, 8, 1, 1, 0, 4); + TEST_PERFORMANCE6(filter, verbose, test_aggregated_bulletproof, true, 8, 1, 1, 0, 4); + TEST_PERFORMANCE6(filter, verbose, test_aggregated_bulletproof, false, 1, 1, 2, 0, 4); + TEST_PERFORMANCE6(filter, verbose, test_aggregated_bulletproof, true, 1, 1, 2, 0, 4); + TEST_PERFORMANCE6(filter, verbose, test_aggregated_bulletproof, false, 1, 8, 1, 1, 4); + TEST_PERFORMANCE6(filter, verbose, test_aggregated_bulletproof, true, 1, 8, 1, 1, 4); + TEST_PERFORMANCE6(filter, verbose, test_aggregated_bulletproof, false, 2, 1, 1, 0, 64); + TEST_PERFORMANCE6(filter, verbose, test_aggregated_bulletproof, true, 2, 1, 1, 0, 64); + TEST_PERFORMANCE3(filter, verbose, test_ringct_mlsag, 1, 3, false); TEST_PERFORMANCE3(filter, verbose, test_ringct_mlsag, 1, 5, false); TEST_PERFORMANCE3(filter, verbose, test_ringct_mlsag, 1, 10, false); diff --git a/tests/performance_tests/performance_tests.h b/tests/performance_tests/performance_tests.h index 6f613e0a5f5..ce9baf61f79 100644 --- a/tests/performance_tests/performance_tests.h +++ b/tests/performance_tests/performance_tests.h @@ -169,3 +169,5 @@ void run_test(const std::string &filter, bool verbose, const char* test_name) #define TEST_PERFORMANCE2(filter, verbose, test_class, a0, a1) run_test< test_class >(filter, verbose, QUOTEME(test_class) "<" QUOTEME(a0) ", " QUOTEME(a1) ">") #define TEST_PERFORMANCE3(filter, verbose, test_class, a0, a1, a2) run_test< test_class >(filter, verbose, QUOTEME(test_class) "<" QUOTEME(a0) ", " QUOTEME(a1) ", " QUOTEME(a2) ">") #define TEST_PERFORMANCE4(filter, verbose, test_class, a0, a1, a2, a3) run_test< test_class >(filter, verbose, QUOTEME(test_class) "<" QUOTEME(a0) ", " QUOTEME(a1) ", " QUOTEME(a2) ", " QUOTEME(a3) ">") +#define TEST_PERFORMANCE5(filter, verbose, test_class, a0, a1, a2, a3, a4) run_test< test_class >(filter, verbose, QUOTEME(test_class) "<" QUOTEME(a0) ", " QUOTEME(a1) ", " QUOTEME(a2) ", " QUOTEME(a3) ", " QUOTEME(a4) ">") +#define TEST_PERFORMANCE6(filter, verbose, test_class, a0, a1, a2, a3, a4, a5) run_test< test_class >(filter, verbose, QUOTEME(test_class) "<" QUOTEME(a0) ", " QUOTEME(a1) ", " QUOTEME(a2) ", " QUOTEME(a3) ", " QUOTEME(a4) ", " QUOTEME(a5) ">") diff --git a/tests/unit_tests/bulletproofs.cpp b/tests/unit_tests/bulletproofs.cpp index 183bb51672e..db14c050a43 100644 --- a/tests/unit_tests/bulletproofs.cpp +++ b/tests/unit_tests/bulletproofs.cpp @@ -135,6 +135,25 @@ TEST(bulletproofs, multi_splitting) } } +TEST(bulletproofs, valid_aggregated) +{ + static const size_t N_PROOFS = 8; + std::vector proofs(N_PROOFS); + for (size_t n = 0; n < N_PROOFS; ++n) + { + size_t outputs = 2 + n; + std::vector amounts; + rct::keyV gamma; + for (size_t i = 0; i < outputs; ++i) + { + amounts.push_back(crypto::rand()); + gamma.push_back(rct::skGen()); + } + proofs[n] = bulletproof_PROVE(amounts, gamma); + } + ASSERT_TRUE(rct::bulletproof_VERIFY(proofs)); +} + TEST(bulletproofs, invalid_8) { diff --git a/tests/unit_tests/ringct.cpp b/tests/unit_tests/ringct.cpp index 6e3958f8a1d..d4e9421761a 100644 --- a/tests/unit_tests/ringct.cpp +++ b/tests/unit_tests/ringct.cpp @@ -1085,3 +1085,20 @@ TEST(ringct, zeroCommmit) const rct::key manual = rct::addKeys(a, b); ASSERT_EQ(z, manual); } + +TEST(ringct, aggregated) +{ + static const size_t N_PROOFS = 16; + std::vector s(N_PROOFS); + std::vector sp(N_PROOFS); + + for (size_t n = 0; n < N_PROOFS; ++n) + { + static const uint64_t inputs[] = {1000, 1000}; + static const uint64_t outputs[] = {500, 1500}; + s[n] = make_sample_simple_rct_sig(NELTS(inputs), inputs, NELTS(outputs), outputs, 0); + sp[n] = &s[n]; + } + + ASSERT_TRUE(verRctSemanticsSimple(sp)); +} From cb9ecab197b2a7f4594ba2208ce5b6386d65d287 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 16 Mar 2018 19:02:55 +0000 Subject: [PATCH 0350/1404] performance_tests: add signature generation/verification --- tests/performance_tests/CMakeLists.txt | 1 + tests/performance_tests/main.cpp | 3 ++ tests/performance_tests/signature.h | 68 ++++++++++++++++++++++++++ 3 files changed, 72 insertions(+) create mode 100644 tests/performance_tests/signature.h diff --git a/tests/performance_tests/CMakeLists.txt b/tests/performance_tests/CMakeLists.txt index 3ffd84aa602..837d39bd3d6 100644 --- a/tests/performance_tests/CMakeLists.txt +++ b/tests/performance_tests/CMakeLists.txt @@ -40,6 +40,7 @@ set(performance_tests_headers generate_key_image.h generate_key_image_helper.h generate_keypair.h + signature.h is_out_to_acc.h subaddress_expand.h range_proof.h diff --git a/tests/performance_tests/main.cpp b/tests/performance_tests/main.cpp index c125f90427e..ea23ec9eade 100644 --- a/tests/performance_tests/main.cpp +++ b/tests/performance_tests/main.cpp @@ -46,6 +46,7 @@ #include "generate_key_image.h" #include "generate_key_image_helper.h" #include "generate_keypair.h" +#include "signature.h" #include "is_out_to_acc.h" #include "subaddress_expand.h" #include "sc_reduce32.h" @@ -150,6 +151,8 @@ int main(int argc, char** argv) TEST_PERFORMANCE0(filter, verbose, test_ge_frombytes_vartime); TEST_PERFORMANCE0(filter, verbose, test_generate_keypair); TEST_PERFORMANCE0(filter, verbose, test_sc_reduce32); + TEST_PERFORMANCE1(filter, verbose, test_signature, false); + TEST_PERFORMANCE1(filter, verbose, test_signature, true); TEST_PERFORMANCE2(filter, verbose, test_wallet2_expand_subaddresses, 50, 200); diff --git a/tests/performance_tests/signature.h b/tests/performance_tests/signature.h new file mode 100644 index 00000000000..21110b5257b --- /dev/null +++ b/tests/performance_tests/signature.h @@ -0,0 +1,68 @@ +// Copyright (c) 2014-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers + +#pragma once + +#include "crypto/crypto.h" +#include "cryptonote_basic/cryptonote_basic.h" + +#include "single_tx_test_base.h" + +template +class test_signature : public single_tx_test_base +{ +public: + static const size_t loop_count = 10000; + + bool init() + { + if (!single_tx_test_base::init()) + return false; + + message = crypto::rand(); + keys = cryptonote::keypair::generate(hw::get_device("default")); + crypto::generate_signature(message, keys.pub, keys.sec, m_signature); + + return true; + } + + bool test() + { + if (verify) + return crypto::check_signature(message, keys.pub, m_signature); + crypto::generate_signature(message, keys.pub, keys.sec, m_signature); + return true; + } + +private: + cryptonote::keypair keys; + crypto::hash message; + crypto::signature m_signature; +}; From 71d67bda7418603ad3a34c1c9e8e9374a7607b90 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 27 Mar 2018 12:35:27 +0100 Subject: [PATCH 0351/1404] aligned: aligned memory alloc/realloc/free --- src/common/CMakeLists.txt | 6 +- src/common/aligned.c | 139 ++++++++++++++++++++++++++++++++ src/common/aligned.h | 41 ++++++++++ tests/unit_tests/CMakeLists.txt | 3 +- tests/unit_tests/aligned.cpp | 86 ++++++++++++++++++++ 5 files changed, 272 insertions(+), 3 deletions(-) create mode 100644 src/common/aligned.c create mode 100644 src/common/aligned.h create mode 100644 tests/unit_tests/aligned.cpp diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index f0df05b0d27..c6bac21998f 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -38,7 +38,8 @@ set(common_sources password.cpp perf_timer.cpp threadpool.cpp - updates.cpp) + updates.cpp + aligned.c) if (STACK_TRACE) list(APPEND common_sources stack_trace.cpp) @@ -67,7 +68,8 @@ set(common_private_headers perf_timer.h stack_trace.h threadpool.h - updates.h) + updates.h + aligned.h) monero_private_headers(common ${common_private_headers}) diff --git a/src/common/aligned.c b/src/common/aligned.c new file mode 100644 index 00000000000..763dfd0e7df --- /dev/null +++ b/src/common/aligned.c @@ -0,0 +1,139 @@ +// Copyright (c) 2017-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include +#include +#include +#include +#include +#include "aligned.h" + +static inline int is_power_of_2(size_t n) { return n && (n & (n-1)) == 0; } + +#define MAGIC 0xaa0817161500ff81 +#define MAGIC_FREED 0xaa0817161500ff82 + +static void local_abort(const char *msg) +{ + fprintf(stderr, "%s\n", msg); +#ifdef NDEBUG + _exit(1); +#else + abort(); +#endif +} + +typedef struct +{ + uint64_t magic; + void *raw; + size_t bytes; + size_t align; +} control; + +void *aligned_malloc(size_t bytes, size_t align) +{ + void *raw, *ptr; + control *ctrl; + + if (!is_power_of_2(align)) + return NULL; + if (bytes > (size_t)-1 - align) + return NULL; + if (bytes + align > (size_t)-1 - sizeof(control)) + return NULL; + + raw = malloc(bytes + sizeof(control) + align); + if (!raw) + return NULL; + ptr = (void*)(((uintptr_t)raw + align + sizeof(control) - 1) & ~(align-1)); + ctrl = ((control*)ptr) - 1; + ctrl->magic = MAGIC; + ctrl->raw = raw; + ctrl->bytes = bytes; + ctrl->align = align; + return ptr; +} + +void *aligned_realloc(void *ptr, size_t bytes, size_t align) +{ + void *raw, *ptr2; + control *ctrl, *ctrl2; + + if (!ptr) + return aligned_malloc(bytes, align); + if (!bytes) + { + aligned_free(ptr); + return NULL; + } + if (!is_power_of_2(align)) + return NULL; + + ctrl = ((control*)ptr) - 1; + if (ctrl->magic == MAGIC_FREED) + local_abort("Double free detected"); + if (ctrl->magic != MAGIC) + local_abort("Freeing unallocated memory"); + if (ctrl->align != align) + return NULL; + if (ctrl->bytes >= bytes) + return ptr; + + if (ctrl->bytes > (size_t)-1 - ctrl->align) + return NULL; + if (ctrl->bytes + ctrl->align > (size_t)-1 - sizeof(control)) + return NULL; + + raw = malloc(bytes + sizeof(control) + ctrl->align); + if (!raw) + return NULL; + ptr2 = (void*)(((uintptr_t)raw + ctrl->align + sizeof(control) - 1) & ~(ctrl->align-1)); + memcpy(ptr2, ptr, ctrl->bytes); + ctrl2 = ((control*)ptr2) - 1; + ctrl2->magic = MAGIC; + ctrl2->raw = raw; + ctrl2->bytes = bytes; + ctrl2->align = ctrl->align; + ctrl->magic = MAGIC_FREED; + free(ctrl->raw); + return ptr2; +} + +void aligned_free(void *ptr) +{ + if (!ptr) + return; + control *ctrl = ((control*)ptr) - 1; + if (ctrl->magic == MAGIC_FREED) + local_abort("Double free detected"); + if (ctrl->magic != MAGIC) + local_abort("Freeing unallocated memory"); + ctrl->magic = MAGIC_FREED; + free(ctrl->raw); +} diff --git a/src/common/aligned.h b/src/common/aligned.h new file mode 100644 index 00000000000..fed3ccb36ac --- /dev/null +++ b/src/common/aligned.h @@ -0,0 +1,41 @@ +// Copyright (c) 2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +void *aligned_malloc(size_t bytes, size_t align); +void *aligned_realloc(void *ptr, size_t bytes, size_t align); +void aligned_free(void *ptr); + +#ifdef __cplusplus +} +#endif diff --git a/tests/unit_tests/CMakeLists.txt b/tests/unit_tests/CMakeLists.txt index cdb74169906..741bb188279 100644 --- a/tests/unit_tests/CMakeLists.txt +++ b/tests/unit_tests/CMakeLists.txt @@ -79,7 +79,8 @@ set(unit_tests_sources vercmp.cpp ringdb.cpp wipeable_string.cpp - is_hdd.cpp) + is_hdd.cpp + aligned.cpp) set(unit_tests_headers unit_tests_utils.h) diff --git a/tests/unit_tests/aligned.cpp b/tests/unit_tests/aligned.cpp new file mode 100644 index 00000000000..ad4837becde --- /dev/null +++ b/tests/unit_tests/aligned.cpp @@ -0,0 +1,86 @@ +// Copyright (c) 2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "gtest/gtest.h" + +#include "common/aligned.h" + +TEST(aligned, large_null) { ASSERT_TRUE(aligned_malloc((size_t)-1, 1) == NULL); } +TEST(aligned, free_null) { aligned_free(NULL); } +TEST(aligned, zero) { void *ptr = aligned_malloc(0, 1); ASSERT_TRUE(ptr); aligned_free(ptr); } +TEST(aligned, aligned1) { void *ptr = aligned_malloc(1, 1); ASSERT_TRUE(ptr); aligned_free(ptr); } +TEST(aligned, aligned4096) { void *ptr = aligned_malloc(1, 4096); ASSERT_TRUE(ptr && ((uintptr_t)ptr & 4095) == 0); aligned_free(ptr); } +TEST(aligned, aligned8) { void *ptr = aligned_malloc(1, 8); ASSERT_TRUE(ptr && ((uintptr_t)ptr & 7) == 0); aligned_free(ptr); } +TEST(aligned, realloc_null) { void *ptr = aligned_realloc(NULL, 1, 4096); ASSERT_TRUE(ptr && ((uintptr_t)ptr & 4095) == 0); aligned_free(ptr); } +TEST(aligned, realloc_diff_align) { void *ptr = aligned_malloc(1, 4096); ASSERT_TRUE(!aligned_realloc(ptr, 1, 2048)); aligned_free(ptr); } +TEST(aligned, realloc_same) { void *ptr = aligned_malloc(1, 4096), *ptr2 = aligned_realloc(ptr, 1, 4096); ASSERT_TRUE(ptr == ptr2); aligned_free(ptr2); } +TEST(aligned, realloc_larger) { void *ptr = aligned_malloc(1, 4096), *ptr2 = aligned_realloc(ptr, 2, 4096); ASSERT_TRUE(ptr != ptr2); aligned_free(ptr2); } +TEST(aligned, realloc_zero) { void *ptr = aligned_malloc(1, 4096), *ptr2 = aligned_realloc(ptr, 0, 4096); ASSERT_TRUE(ptr && !ptr2); } + +TEST(aligned, contents_larger) +{ + unsigned char *ptr = (unsigned char*)aligned_malloc(50, 256); + ASSERT_TRUE(ptr); + for (int n = 0; n < 50; ++n) + ptr[n] = n; + unsigned char *ptr2 = (unsigned char*)aligned_realloc(ptr, 51, 256); + for (int n = 0; n < 50; ++n) + { + ASSERT_TRUE(ptr2[n] == n); + } + aligned_free(ptr2); +} + +TEST(aligned, contents_same) +{ + unsigned char *ptr = (unsigned char*)aligned_malloc(50, 256); + ASSERT_TRUE(ptr); + for (int n = 0; n < 50; ++n) + ptr[n] = n; + unsigned char *ptr2 = (unsigned char*)aligned_realloc(ptr, 50, 256); + for (int n = 0; n < 50; ++n) + { + ASSERT_TRUE(ptr2[n] == n); + } + aligned_free(ptr2); +} + +TEST(aligned, contents_smaller) +{ + unsigned char *ptr = (unsigned char*)aligned_malloc(50, 256); + ASSERT_TRUE(ptr); + for (int n = 0; n < 50; ++n) + ptr[n] = n; + unsigned char *ptr2 = (unsigned char*)aligned_realloc(ptr, 49, 256); + for (int n = 0; n < 49; ++n) + { + ASSERT_TRUE(ptr2[n] == n); + } + aligned_free(ptr2); +} + From 126196b017cd93ff399212b7315f9053511afb07 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 25 Mar 2018 12:17:37 +0100 Subject: [PATCH 0352/1404] multiexp: some speedups - use a raw memory block to store cache - use aligned memory - use doubling API where appropriate - calculate straus in bands --- src/ringct/bulletproofs.cc | 3 +- src/ringct/multiexp.cc | 144 ++++++++++++++++++++++++++++++------- src/ringct/multiexp.h | 2 +- 3 files changed, 121 insertions(+), 28 deletions(-) diff --git a/src/ringct/bulletproofs.cc b/src/ringct/bulletproofs.cc index e2540fb229f..94be0e54594 100644 --- a/src/ringct/bulletproofs.cc +++ b/src/ringct/bulletproofs.cc @@ -70,8 +70,9 @@ static boost::mutex init_mutex; static inline rct::key multiexp(const std::vector &data, bool HiGi) { + static const size_t STEP = getenv("STRAUS_STEP") ? atoi(getenv("STRAUS_STEP")) : 0; if (HiGi || data.size() < 1000) - return straus(data, HiGi ? HiGi_cache: NULL); + return straus(data, HiGi ? HiGi_cache: NULL, STEP); else return bos_coster_heap_conv_robust(data); } diff --git a/src/ringct/multiexp.cc b/src/ringct/multiexp.cc index 4f16bd588f0..99bef25f32e 100644 --- a/src/ringct/multiexp.cc +++ b/src/ringct/multiexp.cc @@ -34,6 +34,7 @@ extern "C" { #include "crypto/crypto-ops.h" } +#include "common/aligned.h" #include "rctOps.h" #include "multiexp.h" @@ -43,6 +44,17 @@ extern "C" //#define MULTIEXP_PERF(x) x #define MULTIEXP_PERF(x) +#define RAW_MEMORY_BLOCK +//#define ALTERNATE_LAYOUT +//#define TRACK_STRAUS_ZERO_IDENTITY + +// per points us for N/B points (B point bands) +// raw alt 128/192 4096/192 4096/4096 +// 0 0 52.6 71 71.2 +// 0 1 53.2 72.2 72.4 +// 1 0 52.7 67 67.1 +// 1 1 52.8 70.4 70.2 + namespace rct { @@ -198,6 +210,7 @@ rct::key bos_coster_heap_conv_robust(std::vector data) ge_cached cached; ge_p1p1 p1; + ge_p2 p2; MULTIEXP_PERF(PERF_TIMER_RESUME(div)); while (1) @@ -214,8 +227,8 @@ rct::key bos_coster_heap_conv_robust(std::vector data) std::push_heap(heap.begin(), heap.end(), Comp); } data[index1].scalar = div2(data[index1].scalar); - ge_p3_to_cached(&cached, &data[index1].point); - ge_add(&p1, &data[index1].point, &cached); + ge_p3_to_p2(&p2, &data[index1].point); + ge_p2_dbl(&p1, &p2); ge_p1p1_to_p3(&data[index1].point, &p1); } MULTIEXP_PERF(PERF_TIMER_PAUSE(div)); @@ -259,12 +272,32 @@ rct::key bos_coster_heap_conv_robust(std::vector data) return res; } +static constexpr unsigned int STRAUS_C = 4; + struct straus_cached_data { +#ifdef RAW_MEMORY_BLOCK + size_t size; + ge_cached *multiples; + straus_cached_data(): size(0), multiples(NULL) {} + ~straus_cached_data() { aligned_free(multiples); } +#else std::vector> multiples; +#endif }; - -static constexpr unsigned int STRAUS_C = 4; +#ifdef RAW_MEMORY_BLOCK +#ifdef ALTERNATE_LAYOUT +#define CACHE_OFFSET(cache,point,digit) cache->multiples[(point)*((1<multiples[(point)+cache->size*((digit)-1)] +#endif +#else +#ifdef ALTERNATE_LAYOUT +#define CACHE_OFFSET(cache,point,digit) local_cache->multiples[j][digit-1] +#else +#define CACHE_OFFSET(cache,point,digit) local_cache->multiples[digit][j] +#endif +#endif std::shared_ptr straus_init_cache(const std::vector &data) { @@ -274,6 +307,36 @@ std::shared_ptr straus_init_cache(const std::vector cache(new straus_cached_data()); +#ifdef RAW_MEMORY_BLOCK + const size_t offset = cache->size; + cache->multiples = (ge_cached*)aligned_realloc(cache->multiples, sizeof(ge_cached) * ((1<size = data.size(); + for (size_t j=offset;jmultiples.size(); + cache->multiples.resize(std::max(offset, data.size())); + for (size_t i = offset; i < data.size(); ++i) + { + cache->multiples[i].resize((1<multiples[i][0], &data[i].point); + for (size_t j=2;j<1<multiples[i][j-2]); + ge_p1p1_to_p3(&p3, &p1); + ge_p3_to_cached(&cache->multiples[i][j-1], &p3); + } + } +#else cache->multiples.resize(1<multiples[1].size(); cache->multiples[1].resize(std::max(offset, data.size())); @@ -290,6 +353,8 @@ std::shared_ptr straus_init_cache(const std::vectormultiples[i][j], &p3); } } +#endif +#endif MULTIEXP_PERF(PERF_TIMER_STOP(multiples)); return cache; @@ -298,15 +363,20 @@ std::shared_ptr straus_init_cache(const std::vector &cache) { size_t sz = 0; +#ifdef RAW_MEMORY_BLOCK + sz += cache->size * sizeof(ge_cached) * ((1<multiples) - sz += e0.size() * sizeof(ge_p3); + sz += e0.size() * sizeof(ge_cached); +#endif return sz; } -rct::key straus(const std::vector &data, const std::shared_ptr &cache) +rct::key straus(const std::vector &data, const std::shared_ptr &cache, size_t STEP) { MULTIEXP_PERF(PERF_TIMER_UNIT(straus, 1000000)); bool HiGi = cache != NULL; + STEP = STEP ? STEP : 192; MULTIEXP_PERF(PERF_TIMER_START_UNIT(setup, 1000000)); static constexpr unsigned int mask = (1< &data, const std::shared_ptr skip(data.size()); for (size_t i = 0; i < data.size(); ++i) skip[i] = data[i].scalar == rct::zero() || !memcmp(&data[i].point, &ge_p3_identity, sizeof(ge_p3)); + MULTIEXP_PERF(PERF_TIMER_STOP(skip)); +#endif MULTIEXP_PERF(PERF_TIMER_START_UNIT(digits, 1000000)); std::vector> digits; @@ -361,35 +435,53 @@ rct::key straus(const std::vector &data, const std::shared_ptrmultiples[digit][j]); - ge_p1p1_to_p3(&res_p3, &p1); +#ifdef TRACK_STRAUS_ZERO_IDENTITY + if (skip[j]) + continue; +#endif + const uint8_t digit = digits[j][i]; + if (digit) + { + ge_add(&p1, &band_p3, &CACHE_OFFSET(local_cache, j, digit)); + ge_p1p1_to_p3(&band_p3, &p1); + } } } + + ge_p3_to_cached(&cached, &band_p3); + ge_add(&p1, &res_p3, &cached); + ge_p1p1_to_p3(&res_p3, &p1); } rct::key res; diff --git a/src/ringct/multiexp.h b/src/ringct/multiexp.h index 44998e2e0f9..c08c70858e3 100644 --- a/src/ringct/multiexp.h +++ b/src/ringct/multiexp.h @@ -59,7 +59,7 @@ rct::key bos_coster_heap_conv(std::vector data); rct::key bos_coster_heap_conv_robust(std::vector data); std::shared_ptr straus_init_cache(const std::vector &data); size_t straus_get_cache_size(const std::shared_ptr &cache); -rct::key straus(const std::vector &data, const std::shared_ptr &cache = NULL); +rct::key straus(const std::vector &data, const std::shared_ptr &cache = NULL, size_t STEP = 0); } From 2a8fcb421bc41eb254f95379dd73238915dd509d Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 30 Mar 2018 20:29:42 +0100 Subject: [PATCH 0353/1404] Bulletproof aggregated verification and tests Also constrains bulletproofs to simple rct, for simplicity --- .../cryptonote_boost_serialization.h | 6 +- .../cryptonote_format_utils.cpp | 18 +- src/cryptonote_core/blockchain.cpp | 15 +- src/cryptonote_core/cryptonote_core.cpp | 178 ++++++--- src/cryptonote_core/cryptonote_core.h | 3 + src/cryptonote_core/cryptonote_tx_utils.cpp | 4 +- src/device/device_ledger.cpp | 2 +- src/ringct/rctSigs.cpp | 133 +++---- src/ringct/rctSigs.h | 2 +- src/ringct/rctTypes.cpp | 19 +- src/ringct/rctTypes.h | 20 +- src/wallet/wallet2.cpp | 3 +- tests/core_tests/CMakeLists.txt | 7 +- tests/core_tests/bulletproofs.cpp | 339 ++++++++++++++++++ tests/core_tests/bulletproofs.h | 170 +++++++++ tests/core_tests/chaingen.h | 46 ++- tests/core_tests/chaingen_main.cpp | 12 + tests/core_tests/chaingen_tests_list.h | 1 + tests/core_tests/rct.cpp | 2 +- tests/performance_tests/check_tx_signature.h | 2 +- tests/unit_tests/bulletproofs.cpp | 28 +- 21 files changed, 840 insertions(+), 170 deletions(-) create mode 100644 tests/core_tests/bulletproofs.cpp create mode 100644 tests/core_tests/bulletproofs.h diff --git a/src/cryptonote_basic/cryptonote_boost_serialization.h b/src/cryptonote_basic/cryptonote_boost_serialization.h index 143133163e1..0725a2bb8f9 100644 --- a/src/cryptonote_basic/cryptonote_boost_serialization.h +++ b/src/cryptonote_basic/cryptonote_boost_serialization.h @@ -295,7 +295,7 @@ namespace boost a & x.type; if (x.type == rct::RCTTypeNull) return; - if (x.type != rct::RCTTypeFull && x.type != rct::RCTTypeFullBulletproof && x.type != rct::RCTTypeSimple && x.type != rct::RCTTypeSimpleBulletproof) + if (x.type != rct::RCTTypeFull && x.type != rct::RCTTypeSimple && x.type != rct::RCTTypeBulletproof) throw boost::archive::archive_exception(boost::archive::archive_exception::other_exception, "Unsupported rct type"); // a & x.message; message is not serialized, as it can be reconstructed from the tx data // a & x.mixRing; mixRing is not serialized, as it can be reconstructed from the offsets @@ -323,7 +323,7 @@ namespace boost a & x.type; if (x.type == rct::RCTTypeNull) return; - if (x.type != rct::RCTTypeFull && x.type != rct::RCTTypeFullBulletproof && x.type != rct::RCTTypeSimple && x.type != rct::RCTTypeSimpleBulletproof) + if (x.type != rct::RCTTypeFull && x.type != rct::RCTTypeSimple && x.type != rct::RCTTypeBulletproof) throw boost::archive::archive_exception(boost::archive::archive_exception::other_exception, "Unsupported rct type"); // a & x.message; message is not serialized, as it can be reconstructed from the tx data // a & x.mixRing; mixRing is not serialized, as it can be reconstructed from the offsets @@ -337,7 +337,7 @@ namespace boost if (x.p.rangeSigs.empty()) a & x.p.bulletproofs; a & x.p.MGs; - if (x.type == rct::RCTTypeSimpleBulletproof) + if (x.type == rct::RCTTypeBulletproof) a & x.p.pseudoOuts; } } diff --git a/src/cryptonote_basic/cryptonote_format_utils.cpp b/src/cryptonote_basic/cryptonote_format_utils.cpp index 428be1c9cc1..524cbf7aee2 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.cpp +++ b/src/cryptonote_basic/cryptonote_format_utils.cpp @@ -139,18 +139,26 @@ namespace cryptonote if (!base_only) { - const bool bulletproof = rv.type == rct::RCTTypeFullBulletproof || rv.type == rct::RCTTypeSimpleBulletproof; + const bool bulletproof = rct::is_rct_bulletproof(rv.type); if (bulletproof) { - if (rv.p.bulletproofs.size() != tx.vout.size()) + if (rct::n_bulletproof_amounts(rv.p.bulletproofs) != tx.vout.size()) { LOG_PRINT_L1("Failed to parse transaction from blob, bad bulletproofs size in tx " << get_transaction_hash(tx)); return false; } - for (size_t n = 0; n < rv.outPk.size(); ++n) + size_t idx = 0; + for (size_t n = 0; n < rv.p.bulletproofs.size(); ++n) { - rv.p.bulletproofs[n].V.resize(1); - rv.p.bulletproofs[n].V[0] = rv.outPk[n].mask; + //rv.p.bulletproofs[n].V.resize(1); + //rv.p.bulletproofs[n].V[0] = rv.outPk[n].mask; + CHECK_AND_ASSERT_MES(rv.p.bulletproofs[n].L.size() >= 6, false, "Bad bulletproofs L size"); // at least 64 bits + const size_t n_amounts = rct::n_bulletproof_amounts(rv.p.bulletproofs[n]); + CHECK_AND_ASSERT_MES(idx + n_amounts <= rv.outPk.size(), false, "Internal error filling out V"); + rv.p.bulletproofs[n].V.resize(n_amounts); + rv.p.bulletproofs[n].V.clear(); + for (size_t i = 0; i < n_amounts; ++i) + rv.p.bulletproofs[n].V[i] = rv.outPk[idx++].mask; } } } diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 0800409b530..9beb28fbdd1 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -2597,7 +2597,7 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context // from v8, allow bulletproofs if (hf_version < 8) { - const bool bulletproof = tx.rct_signatures.type == rct::RCTTypeFullBulletproof || tx.rct_signatures.type == rct::RCTTypeSimpleBulletproof; + const bool bulletproof = rct::is_rct_bulletproof(tx.rct_signatures.type); if (bulletproof || !tx.rct_signatures.p.bulletproofs.empty()) { MERROR("Bulletproofs are not allowed before v8"); @@ -2631,7 +2631,7 @@ bool Blockchain::expand_transaction_2(transaction &tx, const crypto::hash &tx_pr rv.message = rct::hash2rct(tx_prefix_hash); // mixRing - full and simple store it in opposite ways - if (rv.type == rct::RCTTypeFull || rv.type == rct::RCTTypeFullBulletproof) + if (rv.type == rct::RCTTypeFull) { CHECK_AND_ASSERT_MES(!pubkeys.empty() && !pubkeys[0].empty(), false, "empty pubkeys"); rv.mixRing.resize(pubkeys[0].size()); @@ -2646,7 +2646,7 @@ bool Blockchain::expand_transaction_2(transaction &tx, const crypto::hash &tx_pr } } } - else if (rv.type == rct::RCTTypeSimple || rv.type == rct::RCTTypeSimpleBulletproof) + else if (rv.type == rct::RCTTypeSimple || rv.type == rct::RCTTypeBulletproof) { CHECK_AND_ASSERT_MES(!pubkeys.empty() && !pubkeys[0].empty(), false, "empty pubkeys"); rv.mixRing.resize(pubkeys.size()); @@ -2665,14 +2665,14 @@ bool Blockchain::expand_transaction_2(transaction &tx, const crypto::hash &tx_pr } // II - if (rv.type == rct::RCTTypeFull || rv.type == rct::RCTTypeFullBulletproof) + if (rv.type == rct::RCTTypeFull) { rv.p.MGs.resize(1); rv.p.MGs[0].II.resize(tx.vin.size()); for (size_t n = 0; n < tx.vin.size(); ++n) rv.p.MGs[0].II[n] = rct::ki2rct(boost::get(tx.vin[n]).k_image); } - else if (rv.type == rct::RCTTypeSimple || rv.type == rct::RCTTypeSimpleBulletproof) + else if (rv.type == rct::RCTTypeSimple || rv.type == rct::RCTTypeBulletproof) { CHECK_AND_ASSERT_MES(rv.p.MGs.size() == tx.vin.size(), false, "Bad MGs size"); for (size_t n = 0; n < tx.vin.size(); ++n) @@ -2938,7 +2938,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, return false; } case rct::RCTTypeSimple: - case rct::RCTTypeSimpleBulletproof: + case rct::RCTTypeBulletproof: { // check all this, either reconstructed (so should really pass), or not { @@ -2996,7 +2996,6 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, break; } case rct::RCTTypeFull: - case rct::RCTTypeFullBulletproof: { // check all this, either reconstructed (so should really pass), or not { @@ -3061,7 +3060,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, } // for bulletproofs, check they're only multi-output after v8 - if (rv.type == rct::RCTTypeFullBulletproof || rv.type == rct::RCTTypeSimpleBulletproof) + if (rct::is_rct_bulletproof(rv.type)) { if (hf_version < 8) { diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 4928bb52849..f1a666814ca 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -692,26 +692,142 @@ namespace cryptonote return false; } + // resolve outPk references in rct txes + // outPk aren't the only thing that need resolving for a fully resolved tx, + // but outPk (1) are needed now to check range proof semantics, and + // (2) do not need access to the blockchain to find data + if (tx.version >= 2) + { + rct::rctSig &rv = tx.rct_signatures; + if (rv.outPk.size() != tx.vout.size()) + { + LOG_PRINT_L1("WRONG TRANSACTION BLOB, Bad outPk size in tx " << tx_hash << ", rejected"); + tvc.m_verifivation_failed = true; + return false; + } + for (size_t n = 0; n < tx.rct_signatures.outPk.size(); ++n) + rv.outPk[n].dest = rct::pk2rct(boost::get(tx.vout[n].target).key); + + const bool bulletproof = rct::is_rct_bulletproof(rv.type); + if (bulletproof) + { + if (rct::n_bulletproof_amounts(rv.p.bulletproofs) != tx.vout.size()) + { + LOG_PRINT_L1("WRONG TRANSACTION BLOB, Bad bulletproofs size in tx " << tx_hash << ", rejected"); + tvc.m_verifivation_failed = true; + return false; + } + size_t idx = 0; + for (size_t n = 0; n < rv.p.bulletproofs.size(); ++n) + { + CHECK_AND_ASSERT_MES(rv.p.bulletproofs[n].L.size() >= 6, false, "Bad bulletproofs L size"); // at least 64 bits + const size_t n_amounts = rct::n_bulletproof_amounts(rv.p.bulletproofs[n]); + CHECK_AND_ASSERT_MES(idx + n_amounts <= rv.outPk.size(), false, "Internal error filling out V"); + rv.p.bulletproofs[n].V.clear(); + for (size_t i = 0; i < n_amounts; ++i) + rv.p.bulletproofs[n].V.push_back(rv.outPk[idx++].mask); + } + } + } + return true; + } + //----------------------------------------------------------------------------------------------- + void core::set_semantics_failed(const crypto::hash &tx_hash) + { + LOG_PRINT_L1("WRONG TRANSACTION BLOB, Failed to check tx " << tx_hash << " semantic, rejected"); + bad_semantics_txes_lock.lock(); + bad_semantics_txes[0].insert(tx_hash); + if (bad_semantics_txes[0].size() >= BAD_SEMANTICS_TXES_MAX_SIZE) + { + std::swap(bad_semantics_txes[0], bad_semantics_txes[1]); + bad_semantics_txes[0].clear(); + } + bad_semantics_txes_lock.unlock(); + } + //----------------------------------------------------------------------------------------------- + bool core::handle_incoming_tx_accumulated_batch(std::vector &tx_info, bool keeped_by_block) + { + bool ret = true; if (keeped_by_block && get_blockchain_storage().is_within_compiled_block_hash_area()) { MTRACE("Skipping semantics check for tx kept by block in embedded hash area"); + return true; } - else if(!check_tx_semantic(tx, keeped_by_block)) + + std::vector rvv; + for (size_t n = 0; n < tx_info.size(); ++n) { - LOG_PRINT_L1("WRONG TRANSACTION BLOB, Failed to check tx " << tx_hash << " semantic, rejected"); - tvc.m_verifivation_failed = true; - bad_semantics_txes_lock.lock(); - bad_semantics_txes[0].insert(tx_hash); - if (bad_semantics_txes[0].size() >= BAD_SEMANTICS_TXES_MAX_SIZE) + if (!check_tx_semantic(*tx_info[n].tx, keeped_by_block)) { - std::swap(bad_semantics_txes[0], bad_semantics_txes[1]); - bad_semantics_txes[0].clear(); + set_semantics_failed(tx_info[n].tx_hash); + tx_info[n].tvc.m_verifivation_failed = true; + tx_info[n].result = false; + continue; + } + + if (tx_info[n].tx->version < 2) + continue; + const rct::rctSig &rv = tx_info[n].tx->rct_signatures; + switch (rv.type) { + case rct::RCTTypeNull: + // coinbase should not come here, so we reject for all other types + MERROR_VER("Unexpected Null rctSig type"); + set_semantics_failed(tx_info[n].tx_hash); + tx_info[n].tvc.m_verifivation_failed = true; + tx_info[n].result = false; + break; + case rct::RCTTypeSimple: + if (!rct::verRctSemanticsSimple(rv)) + { + MERROR_VER("rct signature semantics check failed"); + set_semantics_failed(tx_info[n].tx_hash); + tx_info[n].tvc.m_verifivation_failed = true; + tx_info[n].result = false; + break; + } + break; + case rct::RCTTypeFull: + if (!rct::verRct(rv, true)) + { + MERROR_VER("rct signature semantics check failed"); + set_semantics_failed(tx_info[n].tx_hash); + tx_info[n].tvc.m_verifivation_failed = true; + tx_info[n].result = false; + break; + } + break; + case rct::RCTTypeBulletproof: + rvv.push_back(&rv); // delayed batch verification + break; + default: + MERROR_VER("Unknown rct type: " << rv.type); + set_semantics_failed(tx_info[n].tx_hash); + tx_info[n].tvc.m_verifivation_failed = true; + tx_info[n].result = false; + break; + } + } + if (!rvv.empty() && !rct::verRctSemanticsSimple(rvv)) + { + LOG_PRINT_L1("One transaction among this group has bad semantics, verifying one at a time"); + ret = false; + const bool assumed_bad = rvv.size() == 1; // if there's only one tx, it must be the bad one + for (size_t n = 0; n < tx_info.size(); ++n) + { + if (!tx_info[n].result) + continue; + if (tx_info[n].tx->rct_signatures.type != rct::RCTTypeBulletproof) + continue; + if (assumed_bad || !rct::verRctSemanticsSimple(tx_info[n].tx->rct_signatures)) + { + set_semantics_failed(tx_info[n].tx_hash); + tx_info[n].tvc.m_verifivation_failed = true; + tx_info[n].result = false; + } } - bad_semantics_txes_lock.unlock(); - return false; } - return true; + return ret; } //----------------------------------------------------------------------------------------------- bool core::handle_incoming_txs(const std::vector& tx_blobs, std::vector& tvc, bool keeped_by_block, bool relayed, bool do_not_relay) @@ -769,6 +885,16 @@ namespace cryptonote } waiter.wait(&tpool); + std::vector tx_info; + tx_info.reserve(tx_blobs.size()); + for (size_t i = 0; i < tx_blobs.size(); i++) { + if (!results[i].res) + continue; + tx_info.push_back({&results[i].tx, results[i].hash, tvc[i], results[i].res}); + } + if (!tx_info.empty()) + handle_incoming_tx_accumulated_batch(tx_info, keeped_by_block); + bool ok = true; it = tx_blobs.begin(); for (size_t i = 0; i < tx_blobs.size(); i++, ++it) { @@ -886,36 +1012,6 @@ namespace cryptonote return false; } - if (tx.version >= 2) - { - const rct::rctSig &rv = tx.rct_signatures; - switch (rv.type) { - case rct::RCTTypeNull: - // coinbase should not come here, so we reject for all other types - MERROR_VER("Unexpected Null rctSig type"); - return false; - case rct::RCTTypeSimple: - case rct::RCTTypeSimpleBulletproof: - if (!rct::verRctSemanticsSimple(rv)) - { - MERROR_VER("rct signature semantics check failed"); - return false; - } - break; - case rct::RCTTypeFull: - case rct::RCTTypeFullBulletproof: - if (!rct::verRct(rv, true)) - { - MERROR_VER("rct signature semantics check failed"); - return false; - } - break; - default: - MERROR_VER("Unknown rct type: " << rv.type); - return false; - } - } - return true; } //----------------------------------------------------------------------------------------------- diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index 84e1bb91839..497b1621467 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -864,9 +864,12 @@ namespace cryptonote * @return true if all the checks pass, otherwise false */ bool check_tx_semantic(const transaction& tx, bool keeped_by_block) const; + void set_semantics_failed(const crypto::hash &tx_hash); bool handle_incoming_tx_pre(const blobdata& tx_blob, tx_verification_context& tvc, cryptonote::transaction &tx, crypto::hash &tx_hash, crypto::hash &tx_prefixt_hash, bool keeped_by_block, bool relayed, bool do_not_relay); bool handle_incoming_tx_post(const blobdata& tx_blob, tx_verification_context& tvc, cryptonote::transaction &tx, crypto::hash &tx_hash, crypto::hash &tx_prefixt_hash, bool keeped_by_block, bool relayed, bool do_not_relay); + struct tx_verification_batch_info { const cryptonote::transaction *tx; crypto::hash tx_hash; tx_verification_context &tvc; bool &result; }; + bool handle_incoming_tx_accumulated_batch(std::vector &tx_info, bool keeped_by_block); /** * @copydoc miner::on_block_chain_update diff --git a/src/cryptonote_core/cryptonote_tx_utils.cpp b/src/cryptonote_core/cryptonote_tx_utils.cpp index 32031e95087..52594507936 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.cpp +++ b/src/cryptonote_core/cryptonote_tx_utils.cpp @@ -491,7 +491,7 @@ namespace cryptonote // the non-simple version is slightly smaller, but assumes all real inputs // are on the same index, so can only be used if there just one ring. - bool use_simple_rct = sources.size() > 1; + bool use_simple_rct = sources.size() > 1 || range_proof_type == rct::RangeProofMultiOutputBulletproof || range_proof_type == rct::RangeProofBulletproof; if (!use_simple_rct) { @@ -591,7 +591,7 @@ namespace cryptonote if (use_simple_rct) tx.rct_signatures = rct::genRctSimple(rct::hash2rct(tx_prefix_hash), inSk, destinations, inamounts, outamounts, amount_in - amount_out, mixRing, amount_keys, msout ? &kLRki : NULL, msout, index, outSk, range_proof_type, hwdev); else - tx.rct_signatures = rct::genRct(rct::hash2rct(tx_prefix_hash), inSk, destinations, outamounts, mixRing, amount_keys, msout ? &kLRki[0] : NULL, msout, sources[0].real_output, outSk, range_proof_type, hwdev); // same index assumption + tx.rct_signatures = rct::genRct(rct::hash2rct(tx_prefix_hash), inSk, destinations, outamounts, mixRing, amount_keys, msout ? &kLRki[0] : NULL, msout, sources[0].real_output, outSk, hwdev); // same index assumption memwipe(inSk.data(), inSk.size() * sizeof(rct::ctkey)); CHECK_AND_ASSERT_MES(tx.vout.size() == outSk.size(), false, "outSk size does not match vout"); diff --git a/src/device/device_ledger.cpp b/src/device/device_ledger.cpp index c4e9e40b7a3..658b379e43f 100644 --- a/src/device/device_ledger.cpp +++ b/src/device/device_ledger.cpp @@ -1354,7 +1354,7 @@ namespace hw { this->exchange(); //pseudoOuts - if ((type == rct::RCTTypeSimple) || (type == rct::RCTTypeSimpleBulletproof)) { + if ((type == rct::RCTTypeSimple) || (type == rct::RCTTypeBulletproof)) { for ( i = 0; i < inputs_size; i++) { offset = set_command_header(INS_VALIDATE, 0x01, i+2); //options diff --git a/src/ringct/rctSigs.cpp b/src/ringct/rctSigs.cpp index 2e2b07fcc1f..2175d1659cc 100644 --- a/src/ringct/rctSigs.cpp +++ b/src/ringct/rctSigs.cpp @@ -390,7 +390,7 @@ namespace rct { hashes.push_back(hash2rct(h)); keyV kv; - if (rv.type == RCTTypeSimpleBulletproof || rv.type == RCTTypeFullBulletproof) + if (rv.type == RCTTypeBulletproof) { kv.reserve((6*2+9) * rv.p.bulletproofs.size()); for (const auto &p: rv.p.bulletproofs) @@ -651,8 +651,7 @@ namespace rct { // must know the destination private key to find the correct amount, else will return a random number // Note: For txn fees, the last index in the amounts vector should contain that // Thus the amounts vector will be "one" longer than the destinations vectort - rctSig genRct(const key &message, const ctkeyV & inSk, const keyV & destinations, const vector & amounts, const ctkeyM &mixRing, const keyV &amount_keys, const multisig_kLRki *kLRki, multisig_out *msout, unsigned int index, ctkeyV &outSk, RangeProofType range_proof_type, hw::device &hwdev) { - const bool bulletproof = range_proof_type != RangeProofBorromean; + rctSig genRct(const key &message, const ctkeyV & inSk, const keyV & destinations, const vector & amounts, const ctkeyM &mixRing, const keyV &amount_keys, const multisig_kLRki *kLRki, multisig_out *msout, unsigned int index, ctkeyV &outSk, hw::device &hwdev) { CHECK_AND_ASSERT_THROW_MES(amounts.size() == destinations.size() || amounts.size() == destinations.size() + 1, "Different number of amounts/destinations"); CHECK_AND_ASSERT_THROW_MES(amount_keys.size() == destinations.size(), "Different number of amount_keys/destinations"); CHECK_AND_ASSERT_THROW_MES(index < mixRing.size(), "Bad index into mixRing"); @@ -662,11 +661,10 @@ namespace rct { CHECK_AND_ASSERT_THROW_MES((kLRki && msout) || (!kLRki && !msout), "Only one of kLRki/msout is present"); rctSig rv; - rv.type = bulletproof ? RCTTypeFullBulletproof : RCTTypeFull; + rv.type = RCTTypeFull; rv.message = message; rv.outPk.resize(destinations.size()); - if (!bulletproof) - rv.p.rangeSigs.resize(destinations.size()); + rv.p.rangeSigs.resize(destinations.size()); rv.ecdhInfo.resize(destinations.size()); size_t i = 0; @@ -675,46 +673,11 @@ namespace rct { for (i = 0; i < destinations.size(); i++) { //add destination to sig rv.outPk[i].dest = copy(destinations[i]); - //compute range proof (bulletproofs are done later) - if (!bulletproof) - { - rv.p.rangeSigs[i] = proveRange(rv.outPk[i].mask, outSk[i].mask, amounts[i]); - #ifdef DBG - CHECK_AND_ASSERT_THROW_MES(verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]), "verRange failed on newly created proof"); - #endif - } - } - - rv.p.bulletproofs.clear(); - if (bulletproof) - { - std::vector proof_amounts; - size_t amounts_proved = 0; - while (amounts_proved < amounts.size()) - { - size_t batch_size = 1; - if (range_proof_type == RangeProofMultiOutputBulletproof) - while (batch_size * 2 + amounts_proved <= amounts.size()) - batch_size *= 2; - rct::keyV C, masks; - std::vector batch_amounts(batch_size); - for (i = 0; i < batch_size; ++i) - batch_amounts[i] = amounts[i + amounts_proved]; - rv.p.bulletproofs.push_back(proveRangeBulletproof(C, masks, batch_amounts)); + //compute range proof + rv.p.rangeSigs[i] = proveRange(rv.outPk[i].mask, outSk[i].mask, amounts[i]); #ifdef DBG - CHECK_AND_ASSERT_THROW_MES(verBulletproof(rv.p.bulletproofs.back()), "verBulletproof failed on newly created proof"); + CHECK_AND_ASSERT_THROW_MES(verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]), "verRange failed on newly created proof"); #endif - for (i = 0; i < batch_size; ++i) - { - rv.outPk[i + amounts_proved].mask = C[i]; - outSk[i + amounts_proved].mask = masks[i]; - } - amounts_proved += batch_size; - } - } - - for (i = 0; i < outSk.size(); ++i) - { //mask amount and mask rv.ecdhInfo[i].mask = copy(outSk[i].mask); rv.ecdhInfo[i].amount = d2h(amounts[i]); @@ -744,7 +707,7 @@ namespace rct { ctkeyM mixRing; ctkeyV outSk; tie(mixRing, index) = populateFromBlockchain(inPk, mixin); - return genRct(message, inSk, destinations, amounts, mixRing, amount_keys, kLRki, msout, index, outSk, RangeProofBorromean, hwdev); + return genRct(message, inSk, destinations, amounts, mixRing, amount_keys, kLRki, msout, index, outSk, hwdev); } //RCT simple @@ -766,35 +729,61 @@ namespace rct { } rctSig rv; - rv.type = bulletproof ? RCTTypeSimpleBulletproof : RCTTypeSimple; + rv.type = bulletproof ? RCTTypeBulletproof : RCTTypeSimple; rv.message = message; rv.outPk.resize(destinations.size()); - if (bulletproof) - rv.p.bulletproofs.resize(destinations.size()); - else + if (!bulletproof) rv.p.rangeSigs.resize(destinations.size()); rv.ecdhInfo.resize(destinations.size()); size_t i; keyV masks(destinations.size()); //sk mask.. outSk.resize(destinations.size()); - key sumout = zero(); for (i = 0; i < destinations.size(); i++) { //add destination to sig rv.outPk[i].dest = copy(destinations[i]); //compute range proof - if (bulletproof) - rv.p.bulletproofs[i] = proveRangeBulletproof(rv.outPk[i].mask, outSk[i].mask, outamounts[i]); - else + if (!bulletproof) rv.p.rangeSigs[i] = proveRange(rv.outPk[i].mask, outSk[i].mask, outamounts[i]); #ifdef DBG - if (bulletproof) - CHECK_AND_ASSERT_THROW_MES(verBulletproof(rv.p.bulletproofs[i]), "verBulletproof failed on newly created proof"); - else + if (!bulletproof) CHECK_AND_ASSERT_THROW_MES(verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]), "verRange failed on newly created proof"); #endif - + } + + rv.p.bulletproofs.clear(); + if (bulletproof) + { + std::vector proof_amounts; + size_t n_amounts = outamounts.size(); + size_t amounts_proved = 0; + while (amounts_proved < n_amounts) + { + size_t batch_size = 1; + if (range_proof_type == RangeProofMultiOutputBulletproof) + while (batch_size * 2 + amounts_proved <= n_amounts && batch_size * 2 <= 16) + batch_size *= 2; + rct::keyV C, masks; + std::vector batch_amounts(batch_size); + for (i = 0; i < batch_size; ++i) + batch_amounts[i] = outamounts[i + amounts_proved]; + rv.p.bulletproofs.push_back(proveRangeBulletproof(C, masks, batch_amounts)); + #ifdef DBG + CHECK_AND_ASSERT_THROW_MES(verBulletproof(rv.p.bulletproofs.back()), "verBulletproof failed on newly created proof"); + #endif + for (i = 0; i < batch_size; ++i) + { + rv.outPk[i + amounts_proved].mask = C[i]; + outSk[i + amounts_proved].mask = masks[i]; + } + amounts_proved += batch_size; + } + } + + key sumout = zero(); + for (i = 0; i < outSk.size(); ++i) + { sc_add(sumout.bytes, outSk[i].mask.bytes, sumout.bytes); //mask amount and mask @@ -857,14 +846,10 @@ namespace rct { // must know the destination private key to find the correct amount, else will return a random number bool verRct(const rctSig & rv, bool semantics) { PERF_TIMER(verRct); - CHECK_AND_ASSERT_MES(rv.type == RCTTypeFull || rv.type == RCTTypeFullBulletproof, false, "verRct called on non-full rctSig"); - const bool bulletproof = is_rct_bulletproof(rv.type); + CHECK_AND_ASSERT_MES(rv.type == RCTTypeFull, false, "verRct called on non-full rctSig"); if (semantics) { - if (bulletproof) - CHECK_AND_ASSERT_MES(rv.outPk.size() == n_bulletproof_amounts(rv.p.bulletproofs), false, "Mismatched sizes of outPk and bulletproofs"); - else - CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.p.rangeSigs.size(), false, "Mismatched sizes of outPk and rv.p.rangeSigs"); + CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.p.rangeSigs.size(), false, "Mismatched sizes of outPk and rv.p.rangeSigs"); CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.ecdhInfo.size(), false, "Mismatched sizes of outPk and rv.ecdhInfo"); CHECK_AND_ASSERT_MES(rv.p.MGs.size() == 1, false, "full rctSig has not one MG"); } @@ -879,14 +864,10 @@ namespace rct { if (semantics) { tools::threadpool& tpool = tools::threadpool::getInstance(); tools::threadpool::waiter waiter; - std::deque results(bulletproof ? rv.p.bulletproofs.size() : rv.outPk.size(), false); + std::deque results(rv.outPk.size(), false); DP("range proofs verified?"); - if (bulletproof) - for (size_t i = 0; i < rv.p.bulletproofs.size(); i++) - tpool.submit(&waiter, [&, i] { results[i] = verBulletproof(rv.p.bulletproofs[i]); }); - else - for (size_t i = 0; i < rv.outPk.size(); i++) - tpool.submit(&waiter, [&, i] { results[i] = verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]); }); + for (size_t i = 0; i < rv.outPk.size(); i++) + tpool.submit(&waiter, [&, i] { results[i] = verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]); }); waiter.wait(&tpool); for (size_t i = 0; i < results.size(); ++i) { @@ -940,7 +921,7 @@ namespace rct { { CHECK_AND_ASSERT_MES(rvp, false, "rctSig pointer is NULL"); const rctSig &rv = *rvp; - CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple || rv.type == RCTTypeSimpleBulletproof, false, "verRctSemanticsSimple called on non simple rctSig"); + CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple || rv.type == RCTTypeBulletproof, false, "verRctSemanticsSimple called on non simple rctSig"); const bool bulletproof = is_rct_bulletproof(rv.type); if (bulletproof) { @@ -1041,7 +1022,7 @@ namespace rct { { PERF_TIMER(verRctNonSemanticsSimple); - CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple || rv.type == RCTTypeSimpleBulletproof, false, "verRctNonSemanticsSimple called on non simple rctSig"); + CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple || rv.type == RCTTypeBulletproof, false, "verRctNonSemanticsSimple called on non simple rctSig"); const bool bulletproof = is_rct_bulletproof(rv.type); // semantics check is early, and mixRing/MGs aren't resolved yet if (bulletproof) @@ -1101,7 +1082,7 @@ namespace rct { // uses the attached ecdh info to find the amounts represented by each output commitment // must know the destination private key to find the correct amount, else will return a random number xmr_amount decodeRct(const rctSig & rv, const key & sk, unsigned int i, key & mask, hw::device &hwdev) { - CHECK_AND_ASSERT_MES(rv.type == RCTTypeFull || rv.type == RCTTypeFullBulletproof, false, "decodeRct called on non-full rctSig"); + CHECK_AND_ASSERT_MES(rv.type == RCTTypeFull, false, "decodeRct called on non-full rctSig"); CHECK_AND_ASSERT_THROW_MES(i < rv.ecdhInfo.size(), "Bad index"); CHECK_AND_ASSERT_THROW_MES(rv.outPk.size() == rv.ecdhInfo.size(), "Mismatched sizes of rv.outPk and rv.ecdhInfo"); @@ -1129,7 +1110,7 @@ namespace rct { } xmr_amount decodeRctSimple(const rctSig & rv, const key & sk, unsigned int i, key &mask, hw::device &hwdev) { - CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple || rv.type == RCTTypeSimpleBulletproof, false, "decodeRct called on non simple rctSig"); + CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple || rv.type == RCTTypeBulletproof, false, "decodeRct called on non simple rctSig"); CHECK_AND_ASSERT_THROW_MES(i < rv.ecdhInfo.size(), "Bad index"); CHECK_AND_ASSERT_THROW_MES(rv.outPk.size() == rv.ecdhInfo.size(), "Mismatched sizes of rv.outPk and rv.ecdhInfo"); @@ -1157,12 +1138,12 @@ namespace rct { } bool signMultisig(rctSig &rv, const std::vector &indices, const keyV &k, const multisig_out &msout, const key &secret_key) { - CHECK_AND_ASSERT_MES(rv.type == RCTTypeFull || rv.type == RCTTypeSimple || rv.type == RCTTypeFullBulletproof || rv.type == RCTTypeSimpleBulletproof, + CHECK_AND_ASSERT_MES(rv.type == RCTTypeFull || rv.type == RCTTypeSimple || rv.type == RCTTypeBulletproof, false, "unsupported rct type"); CHECK_AND_ASSERT_MES(indices.size() == k.size(), false, "Mismatched k/indices sizes"); CHECK_AND_ASSERT_MES(k.size() == rv.p.MGs.size(), false, "Mismatched k/MGs size"); CHECK_AND_ASSERT_MES(k.size() == msout.c.size(), false, "Mismatched k/msout.c size"); - if (rv.type == RCTTypeFull || rv.type == RCTTypeFullBulletproof) + if (rv.type == RCTTypeFull) { CHECK_AND_ASSERT_MES(rv.p.MGs.size() == 1, false, "MGs not a single element"); } diff --git a/src/ringct/rctSigs.h b/src/ringct/rctSigs.h index d1090ca7792..ae8bb91d76d 100644 --- a/src/ringct/rctSigs.h +++ b/src/ringct/rctSigs.h @@ -119,7 +119,7 @@ namespace rct { //decodeRct: (c.f. https://eprint.iacr.org/2015/1098 section 5.1.1) // uses the attached ecdh info to find the amounts represented by each output commitment // must know the destination private key to find the correct amount, else will return a random number - rctSig genRct(const key &message, const ctkeyV & inSk, const keyV & destinations, const std::vector & amounts, const ctkeyM &mixRing, const keyV &amount_keys, const multisig_kLRki *kLRki, multisig_out *msout, unsigned int index, ctkeyV &outSk, RangeProofType range_proof_type, hw::device &hwdev); + rctSig genRct(const key &message, const ctkeyV & inSk, const keyV & destinations, const std::vector & amounts, const ctkeyM &mixRing, const keyV &amount_keys, const multisig_kLRki *kLRki, multisig_out *msout, unsigned int index, ctkeyV &outSk, hw::device &hwdev); rctSig genRct(const key &message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const std::vector & amounts, const keyV &amount_keys, const multisig_kLRki *kLRki, multisig_out *msout, const int mixin, hw::device &hwdev); rctSig genRctSimple(const key & message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const std::vector & inamounts, const std::vector & outamounts, const keyV &amount_keys, const std::vector *kLRki, multisig_out *msout, xmr_amount txnFee, unsigned int mixin, hw::device &hwdev); rctSig genRctSimple(const key & message, const ctkeyV & inSk, const keyV & destinations, const std::vector & inamounts, const std::vector & outamounts, xmr_amount txnFee, const ctkeyM & mixRing, const keyV &amount_keys, const std::vector *kLRki, multisig_out *msout, const std::vector & index, ctkeyV &outSk, RangeProofType range_proof_type, hw::device &hwdev); diff --git a/src/ringct/rctTypes.cpp b/src/ringct/rctTypes.cpp index 72ef7578025..e67637af6cb 100644 --- a/src/ringct/rctTypes.cpp +++ b/src/ringct/rctTypes.cpp @@ -28,6 +28,7 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#include "misc_log_ex.h" #include "rctTypes.h" using namespace crypto; using namespace std; @@ -214,7 +215,7 @@ namespace rct { switch (type) { case RCTTypeSimple: - case RCTTypeSimpleBulletproof: + case RCTTypeBulletproof: return true; default: return false; @@ -225,19 +226,29 @@ namespace rct { { switch (type) { - case RCTTypeSimpleBulletproof: - case RCTTypeFullBulletproof: + case RCTTypeBulletproof: return true; default: return false; } } + size_t n_bulletproof_amounts(const Bulletproof &proof) + { + CHECK_AND_ASSERT_MES(proof.L.size() >= 6, 0, "Invalid bulletproof L size"); + return 1 << (proof.L.size() - 6); + } + size_t n_bulletproof_amounts(const std::vector &proofs) { size_t n = 0; for (const Bulletproof &proof: proofs) - n += proof.V.size(); + { + size_t n2 = n_bulletproof_amounts(proof); + if (n2 == 0) + return 0; + n += n2; + } return n; } diff --git a/src/ringct/rctTypes.h b/src/ringct/rctTypes.h index 5574c7784e4..894f747b880 100644 --- a/src/ringct/rctTypes.h +++ b/src/ringct/rctTypes.h @@ -213,6 +213,7 @@ namespace rct { END_SERIALIZE() }; + size_t n_bulletproof_amounts(const Bulletproof &proof); size_t n_bulletproof_amounts(const std::vector &proofs); //A container to hold all signatures necessary for RingCT @@ -226,8 +227,7 @@ namespace rct { RCTTypeNull = 0, RCTTypeFull = 1, RCTTypeSimple = 2, - RCTTypeFullBulletproof = 3, - RCTTypeSimpleBulletproof = 4, + RCTTypeBulletproof = 3, }; enum RangeProofType { RangeProofBorromean, RangeProofBulletproof, RangeProofMultiOutputBulletproof }; struct rctSigBase { @@ -246,7 +246,7 @@ namespace rct { FIELD(type) if (type == RCTTypeNull) return true; - if (type != RCTTypeFull && type != RCTTypeFullBulletproof && type != RCTTypeSimple && type != RCTTypeSimpleBulletproof) + if (type != RCTTypeFull && type != RCTTypeSimple && type != RCTTypeBulletproof) return false; VARINT_FIELD(txnFee) // inputs/outputs not saved, only here for serialization help @@ -307,9 +307,9 @@ namespace rct { { if (type == RCTTypeNull) return true; - if (type != RCTTypeFull && type != RCTTypeFullBulletproof && type != RCTTypeSimple && type != RCTTypeSimpleBulletproof) + if (type != RCTTypeFull && type != RCTTypeSimple && type != RCTTypeBulletproof) return false; - if (type == RCTTypeSimpleBulletproof || type == RCTTypeFullBulletproof) + if (type == RCTTypeBulletproof) { ar.tag("bp"); ar.begin_array(); @@ -348,7 +348,7 @@ namespace rct { ar.begin_array(); // we keep a byte for size of MGs, because we don't know whether this is // a simple or full rct signature, and it's starting to annoy the hell out of me - size_t mg_elements = (type == RCTTypeSimple || type == RCTTypeSimpleBulletproof) ? inputs : 1; + size_t mg_elements = (type == RCTTypeSimple || type == RCTTypeBulletproof) ? inputs : 1; PREPARE_CUSTOM_VECTOR_SERIALIZATION(mg_elements, MGs); if (MGs.size() != mg_elements) return false; @@ -366,7 +366,7 @@ namespace rct { for (size_t j = 0; j < mixin + 1; ++j) { ar.begin_array(); - size_t mg_ss2_elements = ((type == RCTTypeSimple || type == RCTTypeSimpleBulletproof) ? 1 : inputs) + 1; + size_t mg_ss2_elements = ((type == RCTTypeSimple || type == RCTTypeBulletproof) ? 1 : inputs) + 1; PREPARE_CUSTOM_VECTOR_SERIALIZATION(mg_ss2_elements, MGs[i].ss[j]); if (MGs[i].ss[j].size() != mg_ss2_elements) return false; @@ -392,7 +392,7 @@ namespace rct { ar.delimit_array(); } ar.end_array(); - if (type == RCTTypeSimpleBulletproof) + if (type == RCTTypeBulletproof) { ar.tag("pseudoOuts"); ar.begin_array(); @@ -416,12 +416,12 @@ namespace rct { keyV& get_pseudo_outs() { - return type == RCTTypeSimpleBulletproof ? p.pseudoOuts : pseudoOuts; + return type == RCTTypeBulletproof ? p.pseudoOuts : pseudoOuts; } keyV const& get_pseudo_outs() const { - return type == RCTTypeSimpleBulletproof ? p.pseudoOuts : pseudoOuts; + return type == RCTTypeBulletproof ? p.pseudoOuts : pseudoOuts; } }; diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index fdba6d84a6e..37340e22b75 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -1142,10 +1142,9 @@ static uint64_t decodeRct(const rct::rctSig & rv, const crypto::key_derivation & switch (rv.type) { case rct::RCTTypeSimple: - case rct::RCTTypeSimpleBulletproof: + case rct::RCTTypeBulletproof: return rct::decodeRctSimple(rv, rct::sk2rct(scalar1), i, mask, hwdev); case rct::RCTTypeFull: - case rct::RCTTypeFullBulletproof: return rct::decodeRct(rv, rct::sk2rct(scalar1), i, mask, hwdev); default: LOG_ERROR("Unsupported rct type: " << rv.type); diff --git a/tests/core_tests/CMakeLists.txt b/tests/core_tests/CMakeLists.txt index c8ee7d9db61..1ac0e7864f8 100644 --- a/tests/core_tests/CMakeLists.txt +++ b/tests/core_tests/CMakeLists.txt @@ -41,7 +41,8 @@ set(core_tests_sources transaction_tests.cpp tx_validation.cpp v2_tests.cpp - rct.cpp) + rct.cpp + bulletproofs.cpp) set(core_tests_headers block_reward.h @@ -58,7 +59,8 @@ set(core_tests_headers transaction_tests.h tx_validation.h v2_tests.h - rct.h) + rct.h + bulletproofs.h) add_executable(core_tests ${core_tests_sources} @@ -73,6 +75,7 @@ target_link_libraries(core_tests device ${CMAKE_THREAD_LIBS_INIT} ${EXTRA_LIBRARIES}) +enable_stack_trace(core_tests) set_property(TARGET core_tests PROPERTY FOLDER "tests") diff --git a/tests/core_tests/bulletproofs.cpp b/tests/core_tests/bulletproofs.cpp new file mode 100644 index 00000000000..3f2a2567cfe --- /dev/null +++ b/tests/core_tests/bulletproofs.cpp @@ -0,0 +1,339 @@ +// Copyright (c) 2014-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers + +#include "ringct/rctSigs.h" +#include "ringct/bulletproofs.h" +#include "chaingen.h" +#include "bulletproofs.h" +#include "device/device.hpp" + +using namespace epee; +using namespace crypto; +using namespace cryptonote; + +//---------------------------------------------------------------------------------------------------------------------- +// Tests + +bool gen_bp_tx_validation_base::generate_with(std::vector& events, + const int *out_idx, int mixin, size_t n_txes, const uint64_t *amounts_paid, bool valid, const bool *multi_out, + const std::function &sources, std::vector &destinations, size_t tx_idx)> &pre_tx, + const std::function &post_tx) const +{ + uint64_t ts_start = 1338224400; + + GENERATE_ACCOUNT(miner_account); + MAKE_GENESIS_BLOCK(events, blk_0, miner_account, ts_start); + + // create 8 miner accounts, and have them mine the next 8 blocks + cryptonote::account_base miner_accounts[8]; + const cryptonote::block *prev_block = &blk_0; + cryptonote::block blocks[8 + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW]; + for (size_t n = 0; n < 8; ++n) { + miner_accounts[n].generate(); + CHECK_AND_ASSERT_MES(generator.construct_block_manually(blocks[n], *prev_block, miner_accounts[n], + test_generator::bf_major_ver | test_generator::bf_minor_ver | test_generator::bf_timestamp | test_generator::bf_hf_version, + 2, 2, prev_block->timestamp + DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN * 2, // v2 has blocks twice as long + crypto::hash(), 0, transaction(), std::vector(), 0, 0, 2), + false, "Failed to generate block"); + events.push_back(blocks[n]); + prev_block = blocks + n; + LOG_PRINT_L0("Initial miner tx " << n << ": " << obj_to_json_str(blocks[n].miner_tx)); + } + + // rewind + cryptonote::block blk_r, blk_last; + { + blk_last = blocks[7]; + for (size_t i = 0; i < CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW; ++i) + { + CHECK_AND_ASSERT_MES(generator.construct_block_manually(blocks[8+i], blk_last, miner_account, + test_generator::bf_major_ver | test_generator::bf_minor_ver | test_generator::bf_timestamp | test_generator::bf_hf_version, + 2, 2, blk_last.timestamp + DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN * 2, // v2 has blocks twice as long + crypto::hash(), 0, transaction(), std::vector(), 0, 0, 2), + false, "Failed to generate block"); + events.push_back(blocks[8+i]); + blk_last = blocks[8+i]; + } + blk_r = blk_last; + } + + // create 4 txes from these miners in another block, to generate some rct outputs + std::vector rct_txes; + cryptonote::block blk_txes; + std::vector starting_rct_tx_hashes; + static const uint64_t input_amounts_available[] = {5000000000000, 30000000000000, 100000000000, 80000000000}; + for (size_t n = 0; n < n_txes; ++n) + { + std::vector sources; + + sources.resize(1); + tx_source_entry& src = sources.back(); + + const uint64_t needed_amount = input_amounts_available[n]; + src.amount = input_amounts_available[n]; + size_t real_index_in_tx = 0; + for (size_t m = 0; m < 7; ++m) { + size_t index_in_tx = 0; + for (size_t i = 0; i < blocks[m].miner_tx.vout.size(); ++i) + if (blocks[m].miner_tx.vout[i].amount == needed_amount) + index_in_tx = i; + CHECK_AND_ASSERT_MES(blocks[m].miner_tx.vout[index_in_tx].amount == needed_amount, false, "Expected amount not found"); + src.push_output(m, boost::get(blocks[m].miner_tx.vout[index_in_tx].target).key, src.amount); + if (m == n) + real_index_in_tx = index_in_tx; + } + src.real_out_tx_key = cryptonote::get_tx_pub_key_from_extra(blocks[n].miner_tx); + src.real_output = n; + src.real_output_in_tx_index = real_index_in_tx; + src.mask = rct::identity(); + src.rct = false; + + //fill outputs entry + tx_destination_entry td; + td.addr = miner_accounts[n].get_keys().m_account_address; + std::vector destinations; + for (int o = 0; amounts_paid[o] != (uint64_t)-1; ++o) + { + td.amount = amounts_paid[o]; + destinations.push_back(td); + } + + if (pre_tx && !pre_tx(sources, destinations, n)) + { + MDEBUG("pre_tx returned failure"); + return false; + } + + crypto::secret_key tx_key; + std::vector additional_tx_keys; + std::unordered_map subaddresses; + subaddresses[miner_accounts[n].get_keys().m_account_address.m_spend_public_key] = {0,0}; + rct_txes.resize(rct_txes.size() + 1); + bool r = construct_tx_and_get_tx_key(miner_accounts[n].get_keys(), subaddresses, sources, destinations, cryptonote::account_public_address{}, std::vector(), rct_txes.back(), 0, tx_key, additional_tx_keys, true, multi_out[n] ? rct::RangeProofMultiOutputBulletproof : rct::RangeProofBulletproof); + CHECK_AND_ASSERT_MES(r, false, "failed to construct transaction"); + + if (post_tx && !post_tx(rct_txes.back(), n)) + { + MDEBUG("post_tx returned failure"); + return false; + } + + //events.push_back(rct_txes.back()); + starting_rct_tx_hashes.push_back(get_transaction_hash(rct_txes.back())); + LOG_PRINT_L0("Test tx: " << obj_to_json_str(rct_txes.back())); + + for (int o = 0; amounts_paid[o] != (uint64_t)-1; ++o) + { + crypto::key_derivation derivation; + bool r = crypto::generate_key_derivation(destinations[o].addr.m_view_public_key, tx_key, derivation); + CHECK_AND_ASSERT_MES(r, false, "Failed to generate key derivation"); + crypto::secret_key amount_key; + crypto::derivation_to_scalar(derivation, o, amount_key); + rct::key rct_tx_mask; + if (rct_txes.back().rct_signatures.type == rct::RCTTypeSimple || rct_txes.back().rct_signatures.type == rct::RCTTypeBulletproof) + rct::decodeRctSimple(rct_txes.back().rct_signatures, rct::sk2rct(amount_key), o, rct_tx_mask, hw::get_device("default")); + else + rct::decodeRct(rct_txes.back().rct_signatures, rct::sk2rct(amount_key), o, rct_tx_mask, hw::get_device("default")); + } + + while (amounts_paid[0] != (size_t)-1) + ++amounts_paid; + ++amounts_paid; + } + if (!valid) + DO_CALLBACK(events, "mark_invalid_tx"); + events.push_back(rct_txes); + + CHECK_AND_ASSERT_MES(generator.construct_block_manually(blk_txes, blk_last, miner_account, + test_generator::bf_major_ver | test_generator::bf_minor_ver | test_generator::bf_timestamp | test_generator::bf_tx_hashes | test_generator::bf_hf_version | test_generator::bf_max_outs, + 8, 8, blk_last.timestamp + DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN * 2, // v2 has blocks twice as long + crypto::hash(), 0, transaction(), starting_rct_tx_hashes, 0, 6, 8), + false, "Failed to generate block"); + if (!valid) + DO_CALLBACK(events, "mark_invalid_block"); + events.push_back(blk_txes); + blk_last = blk_txes; + + return true; +} + +bool gen_bp_tx_validation_base::check_bp(const cryptonote::transaction &tx, size_t tx_idx, const size_t *sizes, const char *context) const +{ + DEFINE_TESTS_ERROR_CONTEXT(context); + CHECK_TEST_CONDITION(tx.version >= 2); + CHECK_TEST_CONDITION(rct::is_rct_bulletproof(tx.rct_signatures.type)); + size_t n_sizes = 0, n_amounts = 0; + for (size_t n = 0; n < tx_idx; ++n) + { + while (sizes[0] != (size_t)-1) + ++sizes; + ++sizes; + } + while (sizes[n_sizes] != (size_t)-1) + n_amounts += sizes[n_sizes++]; + CHECK_TEST_CONDITION(tx.rct_signatures.p.bulletproofs.size() == n_sizes); + CHECK_TEST_CONDITION(rct::n_bulletproof_amounts(tx.rct_signatures.p.bulletproofs) == n_amounts); + for (size_t n = 0; n < n_sizes; ++n) + CHECK_TEST_CONDITION(rct::n_bulletproof_amounts(tx.rct_signatures.p.bulletproofs[n]) == sizes[n]); + return true; +} + +bool gen_bp_tx_valid_1::generate(std::vector& events) const +{ + const int mixin = 6; + const int out_idx[] = {1, -1}; + const uint64_t amounts_paid[] = {10000, (uint64_t)-1}; + const size_t bp_sizes[] = {1, (size_t)-1}; + const bool multi_out[] = {false}; + return generate_with(events, out_idx, mixin, 1, amounts_paid, true, multi_out, NULL, [&](const cryptonote::transaction &tx, size_t tx_idx){ return check_bp(tx, tx_idx, bp_sizes, "gen_bp_tx_valid_1"); }); +} + +bool gen_bp_tx_valid_1_1::generate(std::vector& events) const +{ + const int mixin = 6; + const int out_idx[] = {1, -1}; + const uint64_t amounts_paid[] = {5000, 5000, (uint64_t)-1}; + const size_t bp_sizes[] = {1, 1, (size_t)-1}; + const bool multi_out[] = {false}; + return generate_with(events, out_idx, mixin, 1, amounts_paid, true, multi_out, NULL, [&](const cryptonote::transaction &tx, size_t tx_idx){ return check_bp(tx, tx_idx, bp_sizes, "gen_bp_tx_valid_1_1"); }); +} + +bool gen_bp_tx_valid_2::generate(std::vector& events) const +{ + const int mixin = 6; + const int out_idx[] = {1, -1}; + const uint64_t amounts_paid[] = {5000, 5000, (uint64_t)-1}; + const size_t bp_sizes[] = {2, (size_t)-1}; + const bool multi_out[] = {true}; + return generate_with(events, out_idx, mixin, 1, amounts_paid, true, multi_out, NULL, [&](const cryptonote::transaction &tx, size_t tx_idx){ return check_bp(tx, tx_idx, bp_sizes, "gen_bp_tx_valid_2"); }); +} + +bool gen_bp_tx_valid_4_2_1::generate(std::vector& events) const +{ + const int mixin = 6; + const int out_idx[] = {1, -1}; + const uint64_t amounts_paid[] = {1000, 1000, 1000, 1000, 1000, 1000, 1000, (uint64_t)-1}; + const size_t bp_sizes[] = {4, 2, 1, (size_t)-1}; + const bool multi_out[] = {true}; + return generate_with(events, out_idx, mixin, 1, amounts_paid, true, multi_out, NULL, [&](const cryptonote::transaction &tx, size_t tx_idx){ return check_bp(tx, tx_idx, bp_sizes, "gen_bp_tx_valid_4_2_1"); }); +} + +bool gen_bp_tx_valid_16_16::generate(std::vector& events) const +{ + const int mixin = 6; + const int out_idx[] = {1, -1}; + const uint64_t amounts_paid[] = {1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, (uint64_t)-1}; + const size_t bp_sizes[] = {16, 16, (size_t)-1}; + const bool multi_out[] = {true}; + return generate_with(events, out_idx, mixin, 1, amounts_paid, true, multi_out, NULL, [&](const cryptonote::transaction &tx, size_t tx_idx){ return check_bp(tx, tx_idx, bp_sizes, "gen_bp_tx_valid_16_16"); }); +} + +bool gen_bp_txs_valid_2_and_2::generate(std::vector& events) const +{ + const int mixin = 6; + const int out_idx[] = {1, -1}; + const uint64_t amounts_paid[] = {1000, 1000, (size_t)-1, 1000, 1000, (uint64_t)-1}; + const size_t bp_sizes[] = {2, (size_t)-1, 2, (size_t)-1}; + const bool multi_out[] = {true}; + return generate_with(events, out_idx, mixin, 2, amounts_paid, true, multi_out, NULL, [&](const cryptonote::transaction &tx, size_t tx_idx){ return check_bp(tx, tx_idx, bp_sizes, "gen_bp_txs_valid_2_2"); }); +} + +bool gen_bp_txs_valid_1_1_and_8_2_and_16_16_1::generate(std::vector& events) const +{ + const int mixin = 6; + const int out_idx[] = {1, -1}; + const uint64_t amounts_paid[] = {1000, 1000, (uint64_t)-1, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, (uint64_t)-1, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, (uint64_t)-1}; + const bool multi_out[] = {false, true, true}; + const size_t bp_sizes[] = {1, 1, (size_t)-1, 8, 2, (size_t)-1, 16, 16, 1, (size_t)-1}; + return generate_with(events, out_idx, mixin, 3, amounts_paid, true, multi_out, NULL, [&](const cryptonote::transaction &tx, size_t tx_idx){ return check_bp(tx, tx_idx, bp_sizes, "gen_bp_txs_valid_1_1_and_8_2_and_16_16_1"); }); +} + +bool gen_bp_tx_invalid_not_enough_proofs::generate(std::vector& events) const +{ + DEFINE_TESTS_ERROR_CONTEXT("gen_bp_tx_invalid_not_enough_proofs"); + const int mixin = 6; + const int out_idx[] = {1, -1}; + const uint64_t amounts_paid[] = {10000, (uint64_t)-1}; + const bool multi_out[] = {false}; + return generate_with(events, out_idx, mixin, 1, amounts_paid, false, multi_out, NULL, [&](cryptonote::transaction &tx, size_t idx){ + CHECK_TEST_CONDITION(tx.rct_signatures.type == rct::RCTTypeBulletproof); + CHECK_TEST_CONDITION(!tx.rct_signatures.p.bulletproofs.empty()); + tx.rct_signatures.p.bulletproofs.pop_back(); + return true; + }); +} + +bool gen_bp_tx_invalid_too_many_proofs::generate(std::vector& events) const +{ + DEFINE_TESTS_ERROR_CONTEXT("gen_bp_tx_invalid_too_many_proofs"); + const int mixin = 6; + const int out_idx[] = {1, -1}; + const uint64_t amounts_paid[] = {10000, (uint64_t)-1}; + const bool multi_out[] = {false}; + return generate_with(events, out_idx, mixin, 1, amounts_paid, false, multi_out, NULL, [&](cryptonote::transaction &tx, size_t idx){ + CHECK_TEST_CONDITION(tx.rct_signatures.type == rct::RCTTypeBulletproof); + CHECK_TEST_CONDITION(!tx.rct_signatures.p.bulletproofs.empty()); + tx.rct_signatures.p.bulletproofs.push_back(tx.rct_signatures.p.bulletproofs.back()); + return true; + }); +} + +bool gen_bp_tx_invalid_wrong_amount::generate(std::vector& events) const +{ + DEFINE_TESTS_ERROR_CONTEXT("gen_bp_tx_invalid_wrong_amount"); + const int mixin = 6; + const int out_idx[] = {1, -1}; + const uint64_t amounts_paid[] = {10000, (uint64_t)-1}; + const bool multi_out[] = {false}; + return generate_with(events, out_idx, mixin, 1, amounts_paid, false, multi_out, NULL, [&](cryptonote::transaction &tx, size_t idx){ + CHECK_TEST_CONDITION(tx.rct_signatures.type == rct::RCTTypeBulletproof); + CHECK_TEST_CONDITION(!tx.rct_signatures.p.bulletproofs.empty()); + tx.rct_signatures.p.bulletproofs.back() = rct::bulletproof_PROVE(1000, rct::skGen()); + return true; + }); +} + +bool gen_bp_tx_invalid_switched::generate(std::vector& events) const +{ + DEFINE_TESTS_ERROR_CONTEXT("gen_bp_tx_invalid_switched"); + const int mixin = 6; + const int out_idx[] = {1, -1}; + const uint64_t amounts_paid[] = {5000, 5000, (uint64_t)-1}; + const bool multi_out[] = {false}; + return generate_with(events, out_idx, mixin, 1, amounts_paid, false, multi_out, NULL, [&](cryptonote::transaction &tx, size_t tx_idx){ + CHECK_TEST_CONDITION(tx.rct_signatures.type == rct::RCTTypeBulletproof); + CHECK_TEST_CONDITION(tx.rct_signatures.p.bulletproofs.size() == 2); + rct::Bulletproof proof = tx.rct_signatures.p.bulletproofs[0]; + tx.rct_signatures.p.bulletproofs[0] = tx.rct_signatures.p.bulletproofs[1]; + tx.rct_signatures.p.bulletproofs[1] = proof; + return true; + }); +} + diff --git a/tests/core_tests/bulletproofs.h b/tests/core_tests/bulletproofs.h new file mode 100644 index 00000000000..25d1cbb8bff --- /dev/null +++ b/tests/core_tests/bulletproofs.h @@ -0,0 +1,170 @@ +// Copyright (c) 2014-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers + +#pragma once +#include "chaingen.h" + +struct gen_bp_tx_validation_base : public test_chain_unit_base +{ + gen_bp_tx_validation_base() + : m_invalid_tx_index(0) + , m_invalid_block_index(0) + { + REGISTER_CALLBACK_METHOD(gen_bp_tx_validation_base, mark_invalid_tx); + REGISTER_CALLBACK_METHOD(gen_bp_tx_validation_base, mark_invalid_block); + } + + bool check_tx_verification_context(const cryptonote::tx_verification_context& tvc, bool tx_added, size_t event_idx, const cryptonote::transaction& /*tx*/) + { + if (m_invalid_tx_index == event_idx) + return tvc.m_verifivation_failed; + else + return !tvc.m_verifivation_failed && tx_added; + } + + bool check_tx_verification_context(const std::vector& tvcs, size_t tx_added, size_t event_idx, const std::vector& /*txs*/) + { + size_t failed = 0; + for (const cryptonote::tx_verification_context &tvc: tvcs) + if (tvc.m_verifivation_failed) + ++failed; + if (m_invalid_tx_index == event_idx) + return failed > 0; + else + return failed == 0 && tx_added == tvcs.size(); + } + + bool check_block_verification_context(const cryptonote::block_verification_context& bvc, size_t event_idx, const cryptonote::block& /*block*/) + { + if (m_invalid_block_index == event_idx) + return bvc.m_verifivation_failed; + else + return !bvc.m_verifivation_failed; + } + + bool mark_invalid_block(cryptonote::core& /*c*/, size_t ev_index, const std::vector& /*events*/) + { + m_invalid_block_index = ev_index + 1; + return true; + } + + bool mark_invalid_tx(cryptonote::core& /*c*/, size_t ev_index, const std::vector& /*events*/) + { + m_invalid_tx_index = ev_index + 1; + return true; + } + + bool generate_with(std::vector& events, const int *out_idx, int mixin, + size_t n_txes, const uint64_t *amounts_paid, bool valid, const bool *multi_out, + const std::function &sources, std::vector &destinations, size_t)> &pre_tx, + const std::function &post_tx) const; + + bool check_bp(const cryptonote::transaction &tx, size_t tx_idx, const size_t *sizes, const char *context) const; + +private: + size_t m_invalid_tx_index; + size_t m_invalid_block_index; +}; + +template<> +struct get_test_options { + const std::pair hard_forks[4] = {std::make_pair(1, 0), std::make_pair(2, 1), std::make_pair(8, 69), std::make_pair(0, 0)}; + const cryptonote::test_options test_options = { + hard_forks + }; +}; + +// valid +struct gen_bp_tx_valid_1 : public gen_bp_tx_validation_base +{ + bool generate(std::vector& events) const; +}; +template<> struct get_test_options: public get_test_options {}; + +struct gen_bp_tx_valid_1_1 : public gen_bp_tx_validation_base +{ + bool generate(std::vector& events) const; +}; +template<> struct get_test_options: public get_test_options {}; + +struct gen_bp_tx_valid_2 : public gen_bp_tx_validation_base +{ + bool generate(std::vector& events) const; +}; +template<> struct get_test_options: public get_test_options {}; + +struct gen_bp_tx_valid_4_2_1 : public gen_bp_tx_validation_base +{ + bool generate(std::vector& events) const; +}; +template<> struct get_test_options: public get_test_options {}; + +struct gen_bp_tx_valid_16_16 : public gen_bp_tx_validation_base +{ + bool generate(std::vector& events) const; +}; +template<> struct get_test_options: public get_test_options {}; + +struct gen_bp_txs_valid_2_and_2 : public gen_bp_tx_validation_base +{ + bool generate(std::vector& events) const; +}; +template<> struct get_test_options: public get_test_options {}; + +struct gen_bp_txs_valid_1_1_and_8_2_and_16_16_1 : public gen_bp_tx_validation_base +{ + bool generate(std::vector& events) const; +}; +template<> struct get_test_options: public get_test_options {}; + +struct gen_bp_tx_invalid_not_enough_proofs : public gen_bp_tx_validation_base +{ + bool generate(std::vector& events) const; +}; +template<> struct get_test_options: public get_test_options {}; + +struct gen_bp_tx_invalid_too_many_proofs : public gen_bp_tx_validation_base +{ + bool generate(std::vector& events) const; +}; +template<> struct get_test_options: public get_test_options {}; + +struct gen_bp_tx_invalid_wrong_amount : public gen_bp_tx_validation_base +{ + bool generate(std::vector& events) const; +}; +template<> struct get_test_options: public get_test_options {}; + +struct gen_bp_tx_invalid_switched : public gen_bp_tx_validation_base +{ + bool generate(std::vector& events) const; +}; +template<> struct get_test_options: public get_test_options {}; + diff --git a/tests/core_tests/chaingen.h b/tests/core_tests/chaingen.h index bbc9edd1987..e44bcd37b42 100644 --- a/tests/core_tests/chaingen.h +++ b/tests/core_tests/chaingen.h @@ -135,7 +135,7 @@ VARIANT_TAG(binary_archive, serialized_block, 0xcd); VARIANT_TAG(binary_archive, serialized_transaction, 0xce); VARIANT_TAG(binary_archive, event_visitor_settings, 0xcf); -typedef boost::variant test_event_entry; +typedef boost::variant, cryptonote::account_base, callback_entry, serialized_block, serialized_transaction, event_visitor_settings> test_event_entry; typedef std::unordered_map map_hash2tx_t; class test_chain_unit_base @@ -263,6 +263,30 @@ bool check_tx_verification_context(const cryptonote::tx_verification_context& tv } //-------------------------------------------------------------------------- template +auto do_check_tx_verification_context(const std::vector& tvcs, size_t tx_added, size_t event_index, const std::vector& txs, t_test_class& validator, int) + -> decltype(validator.check_tx_verification_context(tvcs, tx_added, event_index, txs)) +{ + return validator.check_tx_verification_context(tvcs, tx_added, event_index, txs); +} +//-------------------------------------------------------------------------- +template +bool do_check_tx_verification_context(const std::vector& tvcs, size_t tx_added, size_t /*event_index*/, const std::vector& /*txs*/, t_test_class&, long) +{ + // Default block verification context check + for (const cryptonote::tx_verification_context &tvc: tvcs) + if (tvc.m_verifivation_failed) + throw std::runtime_error("Transaction verification failed"); + return true; +} +//-------------------------------------------------------------------------- +template +bool check_tx_verification_context(const std::vector& tvcs, size_t tx_added, size_t event_index, const std::vector& txs, t_test_class& validator) +{ + // SFINAE in action + return do_check_tx_verification_context(tvcs, tx_added, event_index, txs, validator, 0); +} +//-------------------------------------------------------------------------- +template auto do_check_block_verification_context(const cryptonote::block_verification_context& bvc, size_t event_index, const cryptonote::block& blk, t_test_class& validator, int) -> decltype(validator.check_block_verification_context(bvc, event_index, blk)) { @@ -339,6 +363,26 @@ struct push_core_event_visitor: public boost::static_visitor return true; } + bool operator()(const std::vector& txs) const + { + log_event("cryptonote::transaction"); + + std::vector tx_blobs; + std::vector tvcs; + cryptonote::tx_verification_context tvc0 = AUTO_VAL_INIT(tvc0); + for (const auto &tx: txs) + { + tx_blobs.push_back(t_serializable_object_to_blob(tx)); + tvcs.push_back(tvc0); + } + size_t pool_size = m_c.get_pool_transactions_count(); + m_c.handle_incoming_txs(tx_blobs, tvcs, m_txs_keeped_by_block, false, false); + size_t tx_added = m_c.get_pool_transactions_count() - pool_size; + bool r = check_tx_verification_context(tvcs, tx_added, m_ev_index, txs, m_validator); + CHECK_AND_NO_ASSERT_MES(r, false, "tx verification context check failed"); + return true; + } + bool operator()(const cryptonote::block& b) const { log_event("cryptonote::block"); diff --git a/tests/core_tests/chaingen_main.cpp b/tests/core_tests/chaingen_main.cpp index c3165507076..f662c68dfe6 100644 --- a/tests/core_tests/chaingen_main.cpp +++ b/tests/core_tests/chaingen_main.cpp @@ -224,6 +224,18 @@ int main(int argc, char* argv[]) GENERATE_AND_PLAY(gen_multisig_tx_invalid_33_1_2_no_threshold); GENERATE_AND_PLAY(gen_multisig_tx_invalid_33_1_3_no_threshold); + GENERATE_AND_PLAY(gen_bp_tx_valid_1); + GENERATE_AND_PLAY(gen_bp_tx_valid_1_1); + GENERATE_AND_PLAY(gen_bp_tx_valid_2); + GENERATE_AND_PLAY(gen_bp_tx_valid_4_2_1); + GENERATE_AND_PLAY(gen_bp_tx_valid_16_16); + GENERATE_AND_PLAY(gen_bp_txs_valid_2_and_2); + GENERATE_AND_PLAY(gen_bp_txs_valid_1_1_and_8_2_and_16_16_1); + GENERATE_AND_PLAY(gen_bp_tx_invalid_not_enough_proofs); + GENERATE_AND_PLAY(gen_bp_tx_invalid_too_many_proofs); + GENERATE_AND_PLAY(gen_bp_tx_invalid_wrong_amount); + GENERATE_AND_PLAY(gen_bp_tx_invalid_switched); + el::Level level = (failed_tests.empty() ? el::Level::Info : el::Level::Error); MLOG(level, "\nREPORT:"); MLOG(level, " Test run: " << tests_count); diff --git a/tests/core_tests/chaingen_tests_list.h b/tests/core_tests/chaingen_tests_list.h index 174610d5d40..c12e97f95f2 100644 --- a/tests/core_tests/chaingen_tests_list.h +++ b/tests/core_tests/chaingen_tests_list.h @@ -42,6 +42,7 @@ #include "v2_tests.h" #include "rct.h" #include "multisig.h" +#include "bulletproofs.h" /************************************************************************/ /* */ /************************************************************************/ diff --git a/tests/core_tests/rct.cpp b/tests/core_tests/rct.cpp index 55401c4cbff..342c3f1ee73 100644 --- a/tests/core_tests/rct.cpp +++ b/tests/core_tests/rct.cpp @@ -133,7 +133,7 @@ bool gen_rct_tx_validation_base::generate_with(std::vector& ev CHECK_AND_ASSERT_MES(r, false, "Failed to generate key derivation"); crypto::secret_key amount_key; crypto::derivation_to_scalar(derivation, o, amount_key); - if (rct_txes[n].rct_signatures.type == rct::RCTTypeSimple || rct_txes[n].rct_signatures.type == rct::RCTTypeSimpleBulletproof) + if (rct_txes[n].rct_signatures.type == rct::RCTTypeSimple || rct_txes[n].rct_signatures.type == rct::RCTTypeBulletproof) rct::decodeRctSimple(rct_txes[n].rct_signatures, rct::sk2rct(amount_key), o, rct_tx_masks[o+n*4], hw::get_device("default")); else rct::decodeRct(rct_txes[n].rct_signatures, rct::sk2rct(amount_key), o, rct_tx_masks[o+n*4], hw::get_device("default")); diff --git a/tests/performance_tests/check_tx_signature.h b/tests/performance_tests/check_tx_signature.h index 693ea186634..79132cceedb 100644 --- a/tests/performance_tests/check_tx_signature.h +++ b/tests/performance_tests/check_tx_signature.h @@ -84,7 +84,7 @@ class test_check_tx_signature : private multi_tx_test_base { if (rct) { - if (m_tx.rct_signatures.type == rct::RCTTypeFull || m_tx.rct_signatures.type == rct::RCTTypeFullBulletproof) + if (m_tx.rct_signatures.type == rct::RCTTypeFull) return rct::verRct(m_tx.rct_signatures); else return rct::verRctSimple(m_tx.rct_signatures); diff --git a/tests/unit_tests/bulletproofs.cpp b/tests/unit_tests/bulletproofs.cpp index db14c050a43..077e02d5f40 100644 --- a/tests/unit_tests/bulletproofs.cpp +++ b/tests/unit_tests/bulletproofs.cpp @@ -78,58 +78,62 @@ TEST(bulletproofs, multi_splitting) { rct::ctkeyV sc, pc; rct::ctkey sctmp, pctmp; + std::vector index; + std::vector inamounts, outamounts; std::tie(sctmp, pctmp) = rct::ctskpkGen(6000); sc.push_back(sctmp); pc.push_back(pctmp); + inamounts.push_back(6000); + index.push_back(1); std::tie(sctmp, pctmp) = rct::ctskpkGen(7000); sc.push_back(sctmp); pc.push_back(pctmp); + inamounts.push_back(7000); + index.push_back(1); const int mixin = 3, max_outputs = 16; for (int n_outputs = 1; n_outputs <= max_outputs; ++n_outputs) { - std::vector amounts; + std::vector outamounts; rct::keyV amount_keys; rct::keyV destinations; rct::key Sk, Pk; uint64_t available = 6000 + 7000; uint64_t amount; - rct::ctkeyM mixRing(mixin+1); + rct::ctkeyM mixRing(sc.size()); //add output for (size_t i = 0; i < n_outputs; ++i) { amount = rct::randXmrAmount(available); - amounts.push_back(amount); + outamounts.push_back(amount); amount_keys.push_back(rct::hash_to_scalar(rct::zero())); rct::skpkGen(Sk, Pk); destinations.push_back(Pk); available -= amount; } - if (!amounts.empty()) - amounts.back() += available; - for (size_t j = 0; j <= mixin; ++j) + for (size_t i = 0; i < sc.size(); ++i) { - for (size_t i = 0; i < sc.size(); ++i) + for (size_t j = 0; j <= mixin; ++j) { if (j == 1) - mixRing[j].push_back(pc[i]); + mixRing[i].push_back(pc[i]); else - mixRing[j].push_back({rct::scalarmultBase(rct::skGen()), rct::scalarmultBase(rct::skGen())}); + mixRing[i].push_back({rct::scalarmultBase(rct::skGen()), rct::scalarmultBase(rct::skGen())}); } } rct::ctkeyV outSk; - rct::rctSig s = rct::genRct(rct::zero(), sc, destinations, amounts, mixRing, amount_keys, NULL, NULL, 1, outSk, rct::RangeProofMultiOutputBulletproof, hw::get_device("default")); - ASSERT_TRUE(rct::verRct(s)); + rct::rctSig s = rct::genRctSimple(rct::zero(), sc, destinations, inamounts, outamounts, available, mixRing, amount_keys, NULL, NULL, index, outSk, rct::RangeProofMultiOutputBulletproof, hw::get_device("default")); + ASSERT_TRUE(rct::verRctSimple(s)); for (size_t i = 0; i < n_outputs; ++i) { rct::key mask; - rct::decodeRct(s, amount_keys[i], i, mask, hw::get_device("default")); + rct::decodeRctSimple(s, amount_keys[i], i, mask, hw::get_device("default")); ASSERT_TRUE(mask == outSk[i].mask); } } From 7e67c52fa2a4ee68437a5fd906d9848a2f0a7150 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 30 Mar 2018 20:44:51 +0100 Subject: [PATCH 0354/1404] Add a define for the max number of bulletproof multi-outputs --- src/cryptonote_config.h | 2 ++ src/ringct/bulletproofs.cc | 3 ++- src/ringct/rctSigs.cpp | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index a0dcf2df18a..00bc2f27574 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -141,6 +141,8 @@ #define DEFAULT_TXPOOL_MAX_SIZE 648000000ull // 3 days at 300000, in bytes +#define BULLETPROOF_MAX_OUTPUTS 16 + // New constants are intended to go here namespace config { diff --git a/src/ringct/bulletproofs.cc b/src/ringct/bulletproofs.cc index 94be0e54594..dc591873810 100644 --- a/src/ringct/bulletproofs.cc +++ b/src/ringct/bulletproofs.cc @@ -33,6 +33,7 @@ #include #include "misc_log_ex.h" #include "common/perf_timer.h" +#include "cryptonote_config.h" extern "C" { #include "crypto/crypto-ops.h" @@ -57,7 +58,7 @@ static rct::keyV vector_dup(const rct::key &x, size_t n); static rct::key inner_product(const rct::keyV &a, const rct::keyV &b); static constexpr size_t maxN = 64; -static constexpr size_t maxM = 16; +static constexpr size_t maxM = BULLETPROOF_MAX_OUTPUTS; static rct::key Hi[maxN*maxM], Gi[maxN*maxM]; static ge_p3 Hi_p3[maxN*maxM], Gi_p3[maxN*maxM]; static ge_dsmp Gprecomp[maxN*maxM], Hprecomp[maxN*maxM]; diff --git a/src/ringct/rctSigs.cpp b/src/ringct/rctSigs.cpp index 2175d1659cc..d2dc87e279d 100644 --- a/src/ringct/rctSigs.cpp +++ b/src/ringct/rctSigs.cpp @@ -762,7 +762,7 @@ namespace rct { { size_t batch_size = 1; if (range_proof_type == RangeProofMultiOutputBulletproof) - while (batch_size * 2 + amounts_proved <= n_amounts && batch_size * 2 <= 16) + while (batch_size * 2 + amounts_proved <= n_amounts && batch_size * 2 <= BULLETPROOF_MAX_OUTPUTS) batch_size *= 2; rct::keyV C, masks; std::vector batch_amounts(batch_size); From c444b1b22922b423256c6d15887a27e77d060d7f Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 31 Mar 2018 10:49:53 +0100 Subject: [PATCH 0355/1404] require canonical multi output bulletproof layout --- src/cryptonote_core/cryptonote_core.cpp | 29 +++++++++++++++++++++++++ tests/core_tests/bulletproofs.cpp | 14 ++++++------ tests/core_tests/bulletproofs.h | 8 +++---- tests/core_tests/chaingen_main.cpp | 4 ++-- 4 files changed, 42 insertions(+), 13 deletions(-) diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index f1a666814ca..f9e0b68d0b5 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -745,6 +745,25 @@ namespace cryptonote bad_semantics_txes_lock.unlock(); } //----------------------------------------------------------------------------------------------- + static bool is_canonical_bulletproof_layout(const std::vector &proofs) + { + size_t n_amounts = rct::n_bulletproof_amounts(proofs), amounts_proved = 0; + size_t n = 0; + while (amounts_proved < n_amounts) + { + if (n >= proofs.size()) + return false; + size_t batch_size = 1; + while (batch_size * 2 + amounts_proved <= n_amounts && batch_size * 2 <= BULLETPROOF_MAX_OUTPUTS) + batch_size *= 2; + if (rct::n_bulletproof_amounts(proofs[n]) != batch_size) + return false; + amounts_proved += batch_size; + ++n; + } + return true; + } + //----------------------------------------------------------------------------------------------- bool core::handle_incoming_tx_accumulated_batch(std::vector &tx_info, bool keeped_by_block) { bool ret = true; @@ -797,6 +816,16 @@ namespace cryptonote } break; case rct::RCTTypeBulletproof: + // in addition to valid bulletproofs, we want multi-out + // proofs to be in decreasing power of 2 constituents + if (!is_canonical_bulletproof_layout(rv.p.bulletproofs)) + { + MERROR_VER("Bulletproof does not use decreasing power of 2 rule"); + set_semantics_failed(tx_info[n].tx_hash); + tx_info[n].tvc.m_verifivation_failed = true; + tx_info[n].result = false; + break; + } rvv.push_back(&rv); // delayed batch verification break; default: diff --git a/tests/core_tests/bulletproofs.cpp b/tests/core_tests/bulletproofs.cpp index 3f2a2567cfe..a675b2c59a6 100644 --- a/tests/core_tests/bulletproofs.cpp +++ b/tests/core_tests/bulletproofs.cpp @@ -215,14 +215,14 @@ bool gen_bp_tx_valid_1::generate(std::vector& events) const return generate_with(events, out_idx, mixin, 1, amounts_paid, true, multi_out, NULL, [&](const cryptonote::transaction &tx, size_t tx_idx){ return check_bp(tx, tx_idx, bp_sizes, "gen_bp_tx_valid_1"); }); } -bool gen_bp_tx_valid_1_1::generate(std::vector& events) const +bool gen_bp_tx_invalid_1_1::generate(std::vector& events) const { const int mixin = 6; const int out_idx[] = {1, -1}; const uint64_t amounts_paid[] = {5000, 5000, (uint64_t)-1}; const size_t bp_sizes[] = {1, 1, (size_t)-1}; const bool multi_out[] = {false}; - return generate_with(events, out_idx, mixin, 1, amounts_paid, true, multi_out, NULL, [&](const cryptonote::transaction &tx, size_t tx_idx){ return check_bp(tx, tx_idx, bp_sizes, "gen_bp_tx_valid_1_1"); }); + return generate_with(events, out_idx, mixin, 1, amounts_paid, false, multi_out, NULL, [&](const cryptonote::transaction &tx, size_t tx_idx){ return check_bp(tx, tx_idx, bp_sizes, "gen_bp_tx_invalid_1_1"); }); } bool gen_bp_tx_valid_2::generate(std::vector& events) const @@ -265,13 +265,13 @@ bool gen_bp_txs_valid_2_and_2::generate(std::vector& events) c return generate_with(events, out_idx, mixin, 2, amounts_paid, true, multi_out, NULL, [&](const cryptonote::transaction &tx, size_t tx_idx){ return check_bp(tx, tx_idx, bp_sizes, "gen_bp_txs_valid_2_2"); }); } -bool gen_bp_txs_valid_1_1_and_8_2_and_16_16_1::generate(std::vector& events) const +bool gen_bp_txs_valid_2_and_8_2_and_16_16_1::generate(std::vector& events) const { const int mixin = 6; const int out_idx[] = {1, -1}; const uint64_t amounts_paid[] = {1000, 1000, (uint64_t)-1, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, (uint64_t)-1, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, (uint64_t)-1}; - const bool multi_out[] = {false, true, true}; - const size_t bp_sizes[] = {1, 1, (size_t)-1, 8, 2, (size_t)-1, 16, 16, 1, (size_t)-1}; + const bool multi_out[] = {true, true, true}; + const size_t bp_sizes[] = {2, (size_t)-1, 8, 2, (size_t)-1, 16, 16, 1, (size_t)-1}; return generate_with(events, out_idx, mixin, 3, amounts_paid, true, multi_out, NULL, [&](const cryptonote::transaction &tx, size_t tx_idx){ return check_bp(tx, tx_idx, bp_sizes, "gen_bp_txs_valid_1_1_and_8_2_and_16_16_1"); }); } @@ -325,8 +325,8 @@ bool gen_bp_tx_invalid_switched::generate(std::vector& events) DEFINE_TESTS_ERROR_CONTEXT("gen_bp_tx_invalid_switched"); const int mixin = 6; const int out_idx[] = {1, -1}; - const uint64_t amounts_paid[] = {5000, 5000, (uint64_t)-1}; - const bool multi_out[] = {false}; + const uint64_t amounts_paid[] = {1001, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, (uint64_t)-1}; + const bool multi_out[] = {true}; return generate_with(events, out_idx, mixin, 1, amounts_paid, false, multi_out, NULL, [&](cryptonote::transaction &tx, size_t tx_idx){ CHECK_TEST_CONDITION(tx.rct_signatures.type == rct::RCTTypeBulletproof); CHECK_TEST_CONDITION(tx.rct_signatures.p.bulletproofs.size() == 2); diff --git a/tests/core_tests/bulletproofs.h b/tests/core_tests/bulletproofs.h index 25d1cbb8bff..d3b65f5bd09 100644 --- a/tests/core_tests/bulletproofs.h +++ b/tests/core_tests/bulletproofs.h @@ -108,11 +108,11 @@ struct gen_bp_tx_valid_1 : public gen_bp_tx_validation_base }; template<> struct get_test_options: public get_test_options {}; -struct gen_bp_tx_valid_1_1 : public gen_bp_tx_validation_base +struct gen_bp_tx_invalid_1_1 : public gen_bp_tx_validation_base { bool generate(std::vector& events) const; }; -template<> struct get_test_options: public get_test_options {}; +template<> struct get_test_options: public get_test_options {}; struct gen_bp_tx_valid_2 : public gen_bp_tx_validation_base { @@ -138,11 +138,11 @@ struct gen_bp_txs_valid_2_and_2 : public gen_bp_tx_validation_base }; template<> struct get_test_options: public get_test_options {}; -struct gen_bp_txs_valid_1_1_and_8_2_and_16_16_1 : public gen_bp_tx_validation_base +struct gen_bp_txs_valid_2_and_8_2_and_16_16_1 : public gen_bp_tx_validation_base { bool generate(std::vector& events) const; }; -template<> struct get_test_options: public get_test_options {}; +template<> struct get_test_options: public get_test_options {}; struct gen_bp_tx_invalid_not_enough_proofs : public gen_bp_tx_validation_base { diff --git a/tests/core_tests/chaingen_main.cpp b/tests/core_tests/chaingen_main.cpp index f662c68dfe6..60de110fac0 100644 --- a/tests/core_tests/chaingen_main.cpp +++ b/tests/core_tests/chaingen_main.cpp @@ -225,12 +225,12 @@ int main(int argc, char* argv[]) GENERATE_AND_PLAY(gen_multisig_tx_invalid_33_1_3_no_threshold); GENERATE_AND_PLAY(gen_bp_tx_valid_1); - GENERATE_AND_PLAY(gen_bp_tx_valid_1_1); + GENERATE_AND_PLAY(gen_bp_tx_invalid_1_1); GENERATE_AND_PLAY(gen_bp_tx_valid_2); GENERATE_AND_PLAY(gen_bp_tx_valid_4_2_1); GENERATE_AND_PLAY(gen_bp_tx_valid_16_16); GENERATE_AND_PLAY(gen_bp_txs_valid_2_and_2); - GENERATE_AND_PLAY(gen_bp_txs_valid_1_1_and_8_2_and_16_16_1); + GENERATE_AND_PLAY(gen_bp_txs_valid_2_and_8_2_and_16_16_1); GENERATE_AND_PLAY(gen_bp_tx_invalid_not_enough_proofs); GENERATE_AND_PLAY(gen_bp_tx_invalid_too_many_proofs); GENERATE_AND_PLAY(gen_bp_tx_invalid_wrong_amount); From 3011178021690d31e031ed66ed56d5a7300005d2 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 2 Apr 2018 15:09:10 +0100 Subject: [PATCH 0356/1404] unit_tests: a couple more bulletproof unit tests for gamma --- tests/unit_tests/bulletproofs.cpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tests/unit_tests/bulletproofs.cpp b/tests/unit_tests/bulletproofs.cpp index 077e02d5f40..f163f7a2068 100644 --- a/tests/unit_tests/bulletproofs.cpp +++ b/tests/unit_tests/bulletproofs.cpp @@ -174,3 +174,22 @@ TEST(bulletproofs, invalid_31) rct::Bulletproof proof = bulletproof_PROVE(invalid_amount, rct::skGen()); ASSERT_FALSE(rct::bulletproof_VERIFY(proof)); } + +TEST(bulletproofs, invalid_gamma_0) +{ + rct::key invalid_amount = rct::zero(); + invalid_amount[8] = 1; + rct::key gamma = rct::zero(); + rct::Bulletproof proof = bulletproof_PROVE(invalid_amount, gamma); + ASSERT_FALSE(rct::bulletproof_VERIFY(proof)); +} + +TEST(bulletproofs, invalid_gamma_ff) +{ + rct::key invalid_amount = rct::zero(); + invalid_amount[8] = 1; + rct::key gamma = rct::zero(); + memset(&gamma, 0xff, sizeof(gamma)); + rct::Bulletproof proof = bulletproof_PROVE(invalid_amount, gamma); + ASSERT_FALSE(rct::bulletproof_VERIFY(proof)); +} From ef56529f9325ece4a67ac11969a585cbe5b75c4d Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 22 May 2018 00:48:09 +0100 Subject: [PATCH 0357/1404] performance_tests: document the tested bulletproof layouts --- tests/performance_tests/main.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/performance_tests/main.cpp b/tests/performance_tests/main.cpp index ea23ec9eade..912376595aa 100644 --- a/tests/performance_tests/main.cpp +++ b/tests/performance_tests/main.cpp @@ -177,25 +177,25 @@ int main(int argc, char** argv) TEST_PERFORMANCE1(filter, verbose, test_range_proof, true); TEST_PERFORMANCE1(filter, verbose, test_range_proof, false); - TEST_PERFORMANCE2(filter, verbose, test_bulletproof, true, 1); + TEST_PERFORMANCE2(filter, verbose, test_bulletproof, true, 1); // 1 bulletproof with 1 amount TEST_PERFORMANCE2(filter, verbose, test_bulletproof, false, 1); - TEST_PERFORMANCE2(filter, verbose, test_bulletproof, true, 2); + TEST_PERFORMANCE2(filter, verbose, test_bulletproof, true, 2); // 1 bulletproof with 2 amounts TEST_PERFORMANCE2(filter, verbose, test_bulletproof, false, 2); - TEST_PERFORMANCE2(filter, verbose, test_bulletproof, true, 15); + TEST_PERFORMANCE2(filter, verbose, test_bulletproof, true, 15); // 1 bulletproof with 15 amounts TEST_PERFORMANCE2(filter, verbose, test_bulletproof, false, 15); TEST_PERFORMANCE6(filter, verbose, test_aggregated_bulletproof, false, 2, 1, 1, 0, 4); - TEST_PERFORMANCE6(filter, verbose, test_aggregated_bulletproof, true, 2, 1, 1, 0, 4); + TEST_PERFORMANCE6(filter, verbose, test_aggregated_bulletproof, true, 2, 1, 1, 0, 4); // 4 proofs, each with 2 amounts TEST_PERFORMANCE6(filter, verbose, test_aggregated_bulletproof, false, 8, 1, 1, 0, 4); - TEST_PERFORMANCE6(filter, verbose, test_aggregated_bulletproof, true, 8, 1, 1, 0, 4); + TEST_PERFORMANCE6(filter, verbose, test_aggregated_bulletproof, true, 8, 1, 1, 0, 4); // 4 proofs, each with 8 amounts TEST_PERFORMANCE6(filter, verbose, test_aggregated_bulletproof, false, 1, 1, 2, 0, 4); - TEST_PERFORMANCE6(filter, verbose, test_aggregated_bulletproof, true, 1, 1, 2, 0, 4); + TEST_PERFORMANCE6(filter, verbose, test_aggregated_bulletproof, true, 1, 1, 2, 0, 4); // 4 proofs with 1, 2, 4, 8 amounts TEST_PERFORMANCE6(filter, verbose, test_aggregated_bulletproof, false, 1, 8, 1, 1, 4); - TEST_PERFORMANCE6(filter, verbose, test_aggregated_bulletproof, true, 1, 8, 1, 1, 4); + TEST_PERFORMANCE6(filter, verbose, test_aggregated_bulletproof, true, 1, 8, 1, 1, 4); // 32 proofs, with 1, 2, 3, 4 amounts, 8 of each TEST_PERFORMANCE6(filter, verbose, test_aggregated_bulletproof, false, 2, 1, 1, 0, 64); - TEST_PERFORMANCE6(filter, verbose, test_aggregated_bulletproof, true, 2, 1, 1, 0, 64); + TEST_PERFORMANCE6(filter, verbose, test_aggregated_bulletproof, true, 2, 1, 1, 0, 64); // 64 proof, each with 2 amounts TEST_PERFORMANCE3(filter, verbose, test_ringct_mlsag, 1, 3, false); TEST_PERFORMANCE3(filter, verbose, test_ringct_mlsag, 1, 5, false); From 1b867e7f4087378a04a0b94d720d3bed8505e245 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 24 May 2018 20:14:09 +0100 Subject: [PATCH 0358/1404] precalc the ge_p3 representation of H --- src/crypto/crypto-ops-data.c | 6 ++++++ src/crypto/crypto-ops.h | 1 + src/ringct/bulletproofs.cc | 22 +++++++++++----------- src/ringct/rctOps.cpp | 4 +--- tests/unit_tests/ringct.cpp | 7 +++++++ 5 files changed, 26 insertions(+), 14 deletions(-) diff --git a/src/crypto/crypto-ops-data.c b/src/crypto/crypto-ops-data.c index 4ff4310deec..1f77513cafb 100644 --- a/src/crypto/crypto-ops-data.c +++ b/src/crypto/crypto-ops-data.c @@ -871,3 +871,9 @@ const fe fe_fffb2 = {8166131, -6741800, -17040804, 3154616, 21461005, 1466302, - const fe fe_fffb3 = {-13620103, 14639558, 4532995, 7679154, 16815101, -15883539, -22863840, -14813421, 13716513, -6477756}; /* sqrt(-sqrt(-1) * A * (A + 2)) */ const fe fe_fffb4 = {-21786234, -12173074, 21573800, 4524538, -4645904, 16204591, 8012863, -8444712, 3212926, 6885324}; /* sqrt(sqrt(-1) * A * (A + 2)) */ const ge_p3 ge_p3_identity = { {0}, {1, 0}, {1, 0}, {0} }; +const ge_p3 ge_p3_H = { + {7329926, -15101362, 31411471, 7614783, 27996851, -3197071, -11157635, -6878293, 466949, -7986503}, + {5858699, 5096796, 21321203, -7536921, -5553480, -11439507, -5627669, 15045946, 19977121, 5275251}, + {1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {23443568, -5110398, -8776029, -4345135, 6889568, -14710814, 7474843, 3279062, 14550766, -7453428} +}; diff --git a/src/crypto/crypto-ops.h b/src/crypto/crypto-ops.h index dc3c60794bd..52854889f8e 100644 --- a/src/crypto/crypto-ops.h +++ b/src/crypto/crypto-ops.h @@ -140,6 +140,7 @@ extern const fe fe_fffb2; extern const fe fe_fffb3; extern const fe fe_fffb4; extern const ge_p3 ge_p3_identity; +extern const ge_p3 ge_p3_H; void ge_fromfe_frombytes_vartime(ge_p2 *, const unsigned char *); void sc_0(unsigned char *); void sc_reduce32(unsigned char *); diff --git a/src/ringct/bulletproofs.cc b/src/ringct/bulletproofs.cc index dc591873810..884c9953719 100644 --- a/src/ringct/bulletproofs.cc +++ b/src/ringct/bulletproofs.cc @@ -521,8 +521,8 @@ Bulletproof bulletproof_PROVE(const rct::key &sv, const rct::key &gamma) // PAPER LINES 47-48 rct::key tau1 = rct::skGen(), tau2 = rct::skGen(); - rct::key T1 = rct::addKeys(rct::scalarmultKey(rct::H, t1), rct::scalarmultBase(tau1)); - rct::key T2 = rct::addKeys(rct::scalarmultKey(rct::H, t2), rct::scalarmultBase(tau2)); + rct::key T1 = rct::addKeys(rct::scalarmultH(t1), rct::scalarmultBase(tau1)); + rct::key T2 = rct::addKeys(rct::scalarmultH(t2), rct::scalarmultBase(tau2)); // PAPER LINES 49-51 rct::key x = hash_cache_mash(hash_cache, z, T1, T2); @@ -592,10 +592,10 @@ Bulletproof bulletproof_PROVE(const rct::key &sv, const rct::key &gamma) // PAPER LINES 18-19 L[round] = vector_exponent_custom(slice(Gprime, nprime, Gprime.size()), slice(Hprime, 0, nprime), slice(aprime, 0, nprime), slice(bprime, nprime, bprime.size())); sc_mul(tmp.bytes, cL.bytes, x_ip.bytes); - rct::addKeys(L[round], L[round], rct::scalarmultKey(rct::H, tmp)); + rct::addKeys(L[round], L[round], rct::scalarmultH(tmp)); R[round] = vector_exponent_custom(slice(Gprime, 0, nprime), slice(Hprime, nprime, Hprime.size()), slice(aprime, nprime, aprime.size()), slice(bprime, 0, nprime)); sc_mul(tmp.bytes, cR.bytes, x_ip.bytes); - rct::addKeys(R[round], R[round], rct::scalarmultKey(rct::H, tmp)); + rct::addKeys(R[round], R[round], rct::scalarmultH(tmp)); // PAPER LINES 21-22 w[round] = hash_cache_mash(hash_cache, L[round], R[round]); @@ -764,8 +764,8 @@ Bulletproof bulletproof_PROVE(const rct::keyV &sv, const rct::keyV &gamma) // PAPER LINES 47-48 rct::key tau1 = rct::skGen(), tau2 = rct::skGen(); - rct::key T1 = rct::addKeys(rct::scalarmultKey(rct::H, t1), rct::scalarmultBase(tau1)); - rct::key T2 = rct::addKeys(rct::scalarmultKey(rct::H, t2), rct::scalarmultBase(tau2)); + rct::key T1 = rct::addKeys(rct::scalarmultH(t1), rct::scalarmultBase(tau1)); + rct::key T2 = rct::addKeys(rct::scalarmultH(t2), rct::scalarmultBase(tau2)); // PAPER LINES 49-51 rct::key x = hash_cache_mash(hash_cache, z, T1, T2); @@ -842,10 +842,10 @@ Bulletproof bulletproof_PROVE(const rct::keyV &sv, const rct::keyV &gamma) // PAPER LINES 18-19 L[round] = vector_exponent_custom(slice(Gprime, nprime, Gprime.size()), slice(Hprime, 0, nprime), slice(aprime, 0, nprime), slice(bprime, nprime, bprime.size())); sc_mul(tmp.bytes, cL.bytes, x_ip.bytes); - rct::addKeys(L[round], L[round], rct::scalarmultKey(rct::H, tmp)); + rct::addKeys(L[round], L[round], rct::scalarmultH(tmp)); R[round] = vector_exponent_custom(slice(Gprime, 0, nprime), slice(Hprime, nprime, Hprime.size()), slice(aprime, nprime, aprime.size()), slice(bprime, 0, nprime)); sc_mul(tmp.bytes, cR.bytes, x_ip.bytes); - rct::addKeys(R[round], R[round], rct::scalarmultKey(rct::H, tmp)); + rct::addKeys(R[round], R[round], rct::scalarmultH(tmp)); // PAPER LINES 21-22 w[round] = hash_cache_mash(hash_cache, L[round], R[round]); @@ -964,7 +964,7 @@ bool bulletproof_VERIFY(const std::vector &proofs) sc_muladd(tmp.bytes, z.bytes, ip1y.bytes, k.bytes); std::vector multiexp_data; multiexp_data.reserve(3+proof.V.size()); - multiexp_data.emplace_back(tmp, rct::H); + multiexp_data.emplace_back(tmp, ge_p3_H); for (size_t j = 0; j < proof.V.size(); j++) { multiexp_data.emplace_back(zpow[j+2], proof.V[j]); @@ -980,7 +980,7 @@ bool bulletproof_VERIFY(const std::vector &proofs) { PERF_TIMER_START_BP(VERIFY_line_61rl_old); sc_muladd(tmp.bytes, z.bytes, ip1y.bytes, k.bytes); - L61Right = rct::scalarmultKey(rct::H, tmp); + L61Right = rct::scalarmultH(tmp); ge_p3 L61Right_p3; CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&L61Right_p3, L61Right.bytes) == 0, "ge_frombytes_vartime failed"); for (size_t j = 0; j+1 < proof.V.size(); j += 2) @@ -1118,7 +1118,7 @@ bool bulletproof_VERIFY(const std::vector &proofs) sc_sub(tmp.bytes, rct::zero().bytes, z1.bytes); rct::addKeys(Y, Y, rct::scalarmultBase(tmp)); rct::addKeys(Y, Y, Z2); - rct::addKeys(Y, Y, rct::scalarmultKey(rct::H, z3)); + rct::addKeys(Y, Y, rct::scalarmultH(z3)); std::vector multiexp_data; multiexp_data.reserve(2 * maxMN); diff --git a/src/ringct/rctOps.cpp b/src/ringct/rctOps.cpp index 50693bad743..fe0ad87472c 100644 --- a/src/ringct/rctOps.cpp +++ b/src/ringct/rctOps.cpp @@ -193,10 +193,8 @@ namespace rct { //Computes aH where H= toPoint(cn_fast_hash(G)), G the basepoint key scalarmultH(const key & a) { - ge_p3 A; ge_p2 R; - CHECK_AND_ASSERT_THROW_MES_L1(ge_frombytes_vartime(&A, H.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast(__LINE__)); - ge_scalarmult(&R, a.bytes, &A); + ge_scalarmult(&R, a.bytes, &ge_p3_H); key aP; ge_tobytes(aP.bytes, &R); return aP; diff --git a/tests/unit_tests/ringct.cpp b/tests/unit_tests/ringct.cpp index d4e9421761a..360529b9e53 100644 --- a/tests/unit_tests/ringct.cpp +++ b/tests/unit_tests/ringct.cpp @@ -1086,6 +1086,13 @@ TEST(ringct, zeroCommmit) ASSERT_EQ(z, manual); } +TEST(ringct, H) +{ + ge_p3 p3; + ASSERT_EQ(ge_frombytes_vartime(&p3, rct::H.bytes), 0); + ASSERT_EQ(memcmp(&p3, &ge_p3_H, sizeof(ge_p3)), 0); +} + TEST(ringct, aggregated) { static const size_t N_PROOFS = 16; From 1ed0ed4de46dbb93dd1030a68f2239615fb80eb6 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 26 May 2018 23:19:21 +0100 Subject: [PATCH 0359/1404] multiexp: cut down on memory allocations --- src/ringct/multiexp.cc | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/src/ringct/multiexp.cc b/src/ringct/multiexp.cc index 99bef25f32e..52c6feb7101 100644 --- a/src/ringct/multiexp.cc +++ b/src/ringct/multiexp.cc @@ -394,37 +394,35 @@ rct::key straus(const std::vector &data, const std::shared_ptr> digits; - digits.resize(data.size()); + std::unique_ptr digits{new uint8_t[256 * data.size()]}; for (size_t j = 0; j < data.size(); ++j) { - digits[j].resize(256); unsigned char bytes33[33]; memcpy(bytes33, data[j].scalar.bytes, 32); bytes33[32] = 0; + const unsigned char *bytes = bytes33; #if 1 static_assert(STRAUS_C == 4, "optimized version needs STRAUS_C == 4"); - const unsigned char *bytes = bytes33; unsigned int i; for (i = 0; i < 256; i += 8, bytes++) { - digits[j][i] = bytes[0] & 0xf; - digits[j][i+1] = (bytes[0] >> 1) & 0xf; - digits[j][i+2] = (bytes[0] >> 2) & 0xf; - digits[j][i+3] = (bytes[0] >> 3) & 0xf; - digits[j][i+4] = ((bytes[0] >> 4) | (bytes[1]<<4)) & 0xf; - digits[j][i+5] = ((bytes[0] >> 5) | (bytes[1]<<3)) & 0xf; - digits[j][i+6] = ((bytes[0] >> 6) | (bytes[1]<<2)) & 0xf; - digits[j][i+7] = ((bytes[0] >> 7) | (bytes[1]<<1)) & 0xf; + digits[j*256+i] = bytes[0] & 0xf; + digits[j*256+i+1] = (bytes[0] >> 1) & 0xf; + digits[j*256+i+2] = (bytes[0] >> 2) & 0xf; + digits[j*256+i+3] = (bytes[0] >> 3) & 0xf; + digits[j*256+i+4] = ((bytes[0] >> 4) | (bytes[1]<<4)) & 0xf; + digits[j*256+i+5] = ((bytes[0] >> 5) | (bytes[1]<<3)) & 0xf; + digits[j*256+i+6] = ((bytes[0] >> 6) | (bytes[1]<<2)) & 0xf; + digits[j*256+i+7] = ((bytes[0] >> 7) | (bytes[1]<<1)) & 0xf; } #elif 1 for (size_t i = 0; i < 256; ++i) - digits[j][i] = ((bytes[i>>3] | (bytes[(i>>3)+1]<<8)) >> (i&7)) & mask; + digits[j*256+i] = ((bytes[i>>3] | (bytes[(i>>3)+1]<<8)) >> (i&7)) & mask; #else rct::key shifted = data[j].scalar; for (size_t i = 0; i < 256; ++i) { - digits[j][i] = shifted.bytes[0] & 0xf; + digits[j*256+i] = shifted.bytes[0] & 0xf; shifted = div2(shifted, (256-i)>>3); } #endif @@ -470,7 +468,7 @@ rct::key straus(const std::vector &data, const std::shared_ptr Date: Mon, 28 May 2018 00:27:54 +0100 Subject: [PATCH 0360/1404] Pippenger multiexp Based on sarang's python code --- src/ringct/bulletproofs.cc | 6 +- src/ringct/multiexp.cc | 133 +++++++++++++++++++++++++++ src/ringct/multiexp.h | 2 + tests/performance_tests/main.cpp | 142 +++++++++++++++++++++++++++++ tests/performance_tests/multiexp.h | 5 +- 5 files changed, 284 insertions(+), 4 deletions(-) diff --git a/src/ringct/bulletproofs.cc b/src/ringct/bulletproofs.cc index 884c9953719..83005eaa439 100644 --- a/src/ringct/bulletproofs.cc +++ b/src/ringct/bulletproofs.cc @@ -72,10 +72,10 @@ static boost::mutex init_mutex; static inline rct::key multiexp(const std::vector &data, bool HiGi) { static const size_t STEP = getenv("STRAUS_STEP") ? atoi(getenv("STRAUS_STEP")) : 0; - if (HiGi || data.size() < 1000) - return straus(data, HiGi ? HiGi_cache: NULL, STEP); + if (HiGi) + return data.size() <= 256 ? straus(data, HiGi_cache, STEP) : pippenger(data, get_pippenger_c(data.size())); else - return bos_coster_heap_conv_robust(data); + return data.size() <= 64 ? straus(data, NULL, STEP) : pippenger(data, get_pippenger_c(data.size())); } //addKeys3acc_p3 diff --git a/src/ringct/multiexp.cc b/src/ringct/multiexp.cc index 52c6feb7101..fd2d1cbd74a 100644 --- a/src/ringct/multiexp.cc +++ b/src/ringct/multiexp.cc @@ -55,6 +55,30 @@ extern "C" // 1 0 52.7 67 67.1 // 1 1 52.8 70.4 70.2 +// Pippenger: +// 1 2 3 4 5 6 7 8 9 bestN +// 2 555 598 621 804 1038 1733 2486 5020 8304 1 +// 4 783 747 800 1006 1428 2132 3285 5185 9806 2 +// 8 1174 1071 1095 1286 1640 2398 3869 6378 12080 2 +// 16 2279 1874 1745 1739 2144 2831 4209 6964 12007 4 +// 32 3910 3706 2588 2477 2782 3467 4856 7489 12618 4 +// 64 7184 5429 4710 4368 4010 4672 6027 8559 13684 5 +// 128 14097 10574 8452 7297 6841 6718 8615 10580 15641 6 +// 256 27715 20800 16000 13550 11875 11400 11505 14090 18460 6 +// 512 55100 41250 31740 26570 22030 19830 20760 21380 25215 6 +// 1024 111520 79000 61080 49720 43080 38320 37600 35040 36750 8 +// 2048 219480 162680 122120 102080 83760 70360 66600 63920 66160 8 +// 4096 453320 323080 247240 210200 180040 150240 132440 114920 110560 9 + +// 2 4 8 16 32 64 128 256 512 1024 2048 4096 +// Bos Coster 858 994 1316 1949 3183 5512 9865 17830 33485 63160 124280 246320 +// Straus 226 341 548 980 1870 3538 7039 14490 29020 57200 118640 233640 +// Straus/cached 226 315 485 785 1514 2858 5753 11065 22970 45120 98880 194840 +// Pippenger 555 747 1071 1739 2477 4010 6718 11400 19830 35040 63920 110560 + +// Best/cached Straus Straus Straus Straus Straus Straus Straus Straus Pip Pip Pip Pip +// Best/uncached Straus Straus Straus Straus Straus Straus Pip Pip Pip Pip Pip Pip + namespace rct { @@ -91,6 +115,26 @@ static inline rct::key pow2(size_t n) return res; } +static inline int test(const rct::key &k, size_t n) +{ + if (n >= 256) return 0; + return k[n >> 3] & (1 << (n & 7)); +} + +static inline void add(ge_p3 &p3, const ge_cached &other) +{ + ge_p1p1 p1; + ge_add(&p1, &p3, &other); + ge_p1p1_to_p3(&p3, &p1); +} + +static inline void add(ge_p3 &p3, const ge_p3 &other) +{ + ge_cached cached; + ge_p3_to_cached(&cached, &other); + add(p3, cached); +} + rct::key bos_coster_heap_conv(std::vector data) { MULTIEXP_PERF(PERF_TIMER_START_UNIT(bos_coster, 1000000)); @@ -487,4 +531,93 @@ rct::key straus(const std::vector &data, const std::shared_ptr &data, size_t c) +{ + CHECK_AND_ASSERT_THROW_MES(c <= 9, "c is too large"); + + ge_p3 result = ge_p3_identity; + std::unique_ptr buckets{new ge_p3[1< cached_points{new ge_cached[data.size()]}; + + for (size_t i = 0; i < data.size(); ++i) + { + ge_p3_to_cached(&cached_points[i], &data[i].point); + } + + rct::key maxscalar = rct::zero(); + for (size_t i = 0; i < data.size(); ++i) + if (maxscalar < data[i].scalar) + maxscalar = data[i].scalar; + size_t groups = 0; + while (groups < 256 && pow2(groups) < maxscalar) + ++groups; + groups = (groups + c - 1) / c; + + for (size_t k = groups; k-- > 0; ) + { + if (memcmp(&result, &ge_p3_identity, sizeof(ge_p3))) + { + ge_p2 p2; + ge_p3_to_p2(&p2, &result); + for (size_t i = 0; i < c; ++i) + { + ge_p1p1 p1; + ge_p2_dbl(&p1, &p2); + if (i == c - 1) + ge_p1p1_to_p3(&result, &p1); + else + ge_p1p1_to_p2(&p2, &p1); + } + } + for (size_t i = 0; i < (1u< 0; --i) + { + if (memcmp(&buckets[i], &ge_p3_identity, sizeof(ge_p3))) + add(pail, buckets[i]); + if (memcmp(&pail, &ge_p3_identity, sizeof(ge_p3))) + add(result, pail); + } + } + + rct::key res; + ge_p3_tobytes(res.bytes, &result); + return res; +} + } diff --git a/src/ringct/multiexp.h b/src/ringct/multiexp.h index c08c70858e3..2ed8db27995 100644 --- a/src/ringct/multiexp.h +++ b/src/ringct/multiexp.h @@ -60,6 +60,8 @@ rct::key bos_coster_heap_conv_robust(std::vector data); std::shared_ptr straus_init_cache(const std::vector &data); size_t straus_get_cache_size(const std::shared_ptr &cache); rct::key straus(const std::vector &data, const std::shared_ptr &cache = NULL, size_t STEP = 0); +size_t get_pippenger_c(size_t N); +rct::key pippenger(const std::vector &data, size_t c); } diff --git a/tests/performance_tests/main.cpp b/tests/performance_tests/main.cpp index 912376595aa..c3e1bb4e040 100644 --- a/tests/performance_tests/main.cpp +++ b/tests/performance_tests/main.cpp @@ -222,26 +222,168 @@ int main(int argc, char** argv) TEST_PERFORMANCE1(filter, verbose, test_crypto_ops, op_addKeys3_2); TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_bos_coster, 2); + TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_bos_coster, 4); TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_bos_coster, 8); TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_bos_coster, 16); + TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_bos_coster, 32); + TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_bos_coster, 64); + TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_bos_coster, 128); TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_bos_coster, 256); + TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_bos_coster, 512); TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_bos_coster, 1024); + TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_bos_coster, 2048); TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_bos_coster, 4096); TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_straus, 2); + TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_straus, 4); TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_straus, 8); TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_straus, 16); + TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_straus, 32); + TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_straus, 64); + TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_straus, 128); TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_straus, 256); + TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_straus, 512); TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_straus, 1024); + TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_straus, 2048); TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_straus, 4096); TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_straus_cached, 2); + TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_straus_cached, 4); TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_straus_cached, 8); TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_straus_cached, 16); + TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_straus_cached, 32); + TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_straus_cached, 64); + TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_straus_cached, 128); TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_straus_cached, 256); + TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_straus_cached, 512); TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_straus_cached, 1024); + TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_straus_cached, 2048); TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_straus_cached, 4096); +#if 1 + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 2, 1); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 4, 2); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 8, 2); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 16, 4); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 32, 4); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 64, 5); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 128, 6); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 256, 6); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 512, 6); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 1024, 8); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 2048, 8); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 4096, 9); +#else + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 2, 1); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 2, 2); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 2, 3); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 2, 4); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 2, 5); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 2, 6); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 2, 7); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 2, 8); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 2, 9); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 4, 1); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 4, 2); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 4, 3); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 4, 4); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 4, 5); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 4, 6); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 4, 7); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 4, 8); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 4, 9); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 8, 1); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 8, 2); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 8, 3); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 8, 4); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 8, 5); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 8, 6); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 8, 7); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 8, 8); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 8, 9); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 16, 1); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 16, 2); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 16, 3); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 16, 4); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 16, 5); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 16, 6); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 16, 7); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 16, 8); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 16, 9); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 32, 1); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 32, 2); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 32, 3); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 32, 4); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 32, 5); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 32, 6); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 32, 7); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 32, 8); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 32, 9); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 64, 1); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 64, 2); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 64, 3); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 64, 4); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 64, 5); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 64, 6); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 64, 7); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 64, 8); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 64, 9); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 128, 1); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 128, 2); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 128, 3); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 128, 4); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 128, 5); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 128, 6); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 128, 7); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 128, 8); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 128, 9); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 256, 1); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 256, 2); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 256, 3); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 256, 4); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 256, 5); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 256, 6); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 256, 7); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 256, 8); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 256, 9); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 512, 1); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 512, 2); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 512, 3); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 512, 4); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 512, 5); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 512, 6); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 512, 7); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 512, 8); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 512, 9); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 1024, 1); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 1024, 2); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 1024, 3); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 1024, 4); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 1024, 5); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 1024, 6); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 1024, 7); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 1024, 8); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 1024, 9); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 2048, 1); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 2048, 2); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 2048, 3); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 2048, 4); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 2048, 5); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 2048, 6); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 2048, 7); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 2048, 8); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 2048, 9); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 4096, 1); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 4096, 2); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 4096, 3); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 4096, 4); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 4096, 5); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 4096, 6); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 4096, 7); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 4096, 8); + TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 4096, 9); +#endif + std::cout << "Tests finished. Elapsed time: " << timer.elapsed_ms() / 1000 << " sec" << std::endl; return 0; diff --git a/tests/performance_tests/multiexp.h b/tests/performance_tests/multiexp.h index ab5af166b30..294e7411637 100644 --- a/tests/performance_tests/multiexp.h +++ b/tests/performance_tests/multiexp.h @@ -39,9 +39,10 @@ enum test_multiexp_algorithm multiexp_bos_coster, multiexp_straus, multiexp_straus_cached, + multiexp_pippenger, }; -template +template class test_multiexp { public: @@ -74,6 +75,8 @@ class test_multiexp return res == straus(data); case multiexp_straus_cached: return res == straus(data, cache); + case multiexp_pippenger: + return res == pippenger(data, c); default: return false; } From d126a02b5de6989d045f92cedf0408e251648f87 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 28 May 2018 12:54:11 +0100 Subject: [PATCH 0361/1404] performance_tests: add aggregated bulletproof tx verification --- tests/performance_tests/check_tx_signature.h | 71 ++++++++++++++++++++ tests/performance_tests/main.cpp | 10 +++ 2 files changed, 81 insertions(+) diff --git a/tests/performance_tests/check_tx_signature.h b/tests/performance_tests/check_tx_signature.h index 79132cceedb..bcd3b3dfe5d 100644 --- a/tests/performance_tests/check_tx_signature.h +++ b/tests/performance_tests/check_tx_signature.h @@ -101,3 +101,74 @@ class test_check_tx_signature : private multi_tx_test_base cryptonote::transaction m_tx; crypto::hash m_tx_prefix_hash; }; + +template +class test_check_tx_signature_aggregated_bulletproofs : private multi_tx_test_base +{ + static_assert(0 < a_ring_size, "ring_size must be greater than 0"); + +public: + static const size_t loop_count = a_ring_size <= 2 ? 50 : 10; + static const size_t ring_size = a_ring_size; + static const size_t outputs = a_outputs; + + typedef multi_tx_test_base base_class; + + bool init() + { + using namespace cryptonote; + + if (!base_class::init()) + return false; + + m_alice.generate(); + + std::vector destinations; + destinations.push_back(tx_destination_entry(this->m_source_amount - outputs + 1, m_alice.get_keys().m_account_address, false)); + for (size_t n = 1; n < outputs; ++n) + destinations.push_back(tx_destination_entry(1, m_alice.get_keys().m_account_address, false)); + + crypto::secret_key tx_key; + std::vector additional_tx_keys; + std::unordered_map subaddresses; + subaddresses[this->m_miners[this->real_source_idx].get_keys().m_account_address.m_spend_public_key] = {0,0}; + + m_txes.resize(a_num_txes + (extra_outs > 0 ? 1 : 0)); + for (size_t n = 0; n < a_num_txes; ++n) + { + if (!construct_tx_and_get_tx_key(this->m_miners[this->real_source_idx].get_keys(), subaddresses, this->m_sources, destinations, cryptonote::account_public_address{}, std::vector(), m_txes[n], 0, tx_key, additional_tx_keys, true, rct::RangeProofMultiOutputBulletproof)) + return false; + } + + if (extra_outs) + { + destinations.clear(); + destinations.push_back(tx_destination_entry(this->m_source_amount - extra_outs + 1, m_alice.get_keys().m_account_address, false)); + for (size_t n = 1; n < extra_outs; ++n) + destinations.push_back(tx_destination_entry(1, m_alice.get_keys().m_account_address, false)); + + if (!construct_tx_and_get_tx_key(this->m_miners[this->real_source_idx].get_keys(), subaddresses, this->m_sources, destinations, cryptonote::account_public_address{}, std::vector(), m_txes.back(), 0, tx_key, additional_tx_keys, true, rct::RangeProofMultiOutputBulletproof)) + return false; + } + + return true; + } + + bool test() + { + std::vector rvv; + rvv.reserve(m_txes.size()); + for (size_t n = 0; n < m_txes.size(); ++n) + { + const rct::rctSig &rv = m_txes[n].rct_signatures; + if (!rct::verRctNonSemanticsSimple(rv)) + return false; + rvv.push_back(&rv); + } + return rct::verRctSemanticsSimple(rvv); + } + +private: + cryptonote::account_base m_alice; + std::vector m_txes; +}; diff --git a/tests/performance_tests/main.cpp b/tests/performance_tests/main.cpp index c3e1bb4e040..abcc2a3d053 100644 --- a/tests/performance_tests/main.cpp +++ b/tests/performance_tests/main.cpp @@ -141,6 +141,16 @@ int main(int argc, char** argv) TEST_PERFORMANCE4(filter, verbose, test_check_tx_signature, 100, 2, true, true); TEST_PERFORMANCE4(filter, verbose, test_check_tx_signature, 2, 10, true, true); + TEST_PERFORMANCE3(filter, verbose, test_check_tx_signature_aggregated_bulletproofs, 2, 2, 64); + TEST_PERFORMANCE3(filter, verbose, test_check_tx_signature_aggregated_bulletproofs, 10, 2, 64); + TEST_PERFORMANCE3(filter, verbose, test_check_tx_signature_aggregated_bulletproofs, 100, 2, 64); + TEST_PERFORMANCE3(filter, verbose, test_check_tx_signature_aggregated_bulletproofs, 2, 10, 64); + + TEST_PERFORMANCE4(filter, verbose, test_check_tx_signature_aggregated_bulletproofs, 2, 2, 62, 4); + TEST_PERFORMANCE4(filter, verbose, test_check_tx_signature_aggregated_bulletproofs, 10, 2, 62, 4); + TEST_PERFORMANCE4(filter, verbose, test_check_tx_signature_aggregated_bulletproofs, 2, 2, 56, 16); + TEST_PERFORMANCE4(filter, verbose, test_check_tx_signature_aggregated_bulletproofs, 10, 2, 56, 16); + TEST_PERFORMANCE0(filter, verbose, test_is_out_to_acc); TEST_PERFORMANCE0(filter, verbose, test_is_out_to_acc_precomp); TEST_PERFORMANCE0(filter, verbose, test_generate_key_image_helper); From 7314d919e7a600545c10c8ebced8e44769c7f43f Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 28 May 2018 19:16:25 +0100 Subject: [PATCH 0362/1404] perf_timer: split timer class into a base one and a logging one --- src/common/perf_timer.cpp | 26 +++++++++++++++++++------- src/common/perf_timer.h | 30 +++++++++++++++++++++++------- 2 files changed, 42 insertions(+), 14 deletions(-) diff --git a/src/common/perf_timer.cpp b/src/common/perf_timer.cpp index 16abdfd991c..6910ebdd4f7 100644 --- a/src/common/perf_timer.cpp +++ b/src/common/perf_timer.cpp @@ -33,7 +33,7 @@ #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "perf" -namespace +namespace tools { uint64_t get_tick_count() { @@ -83,7 +83,7 @@ namespace tools el::Level performance_timer_log_level = el::Level::Debug; -static __thread std::vector *performance_timers = NULL; +static __thread std::vector *performance_timers = NULL; void set_performance_timer_log_level(el::Level level) { @@ -96,17 +96,24 @@ void set_performance_timer_log_level(el::Level level) performance_timer_log_level = level; } -PerformanceTimer::PerformanceTimer(const std::string &s, uint64_t unit, el::Level l): name(s), unit(unit), level(l), started(false), paused(false) +PerformanceTimer::PerformanceTimer(bool paused): started(true), paused(paused) +{ + if (paused) + ticks = 0; + else + ticks = get_tick_count(); +} + +LoggingPerformanceTimer::LoggingPerformanceTimer(const std::string &s, uint64_t unit, el::Level l): PerformanceTimer(), name(s), unit(unit), level(l) { - ticks = get_tick_count(); if (!performance_timers) { MLOG(level, "PERF ----------"); - performance_timers = new std::vector(); + performance_timers = new std::vector(); } else { - PerformanceTimer *pt = performance_timers->back(); + LoggingPerformanceTimer *pt = performance_timers->back(); if (!pt->started && !pt->paused) { size_t size = 0; for (const auto *tmp: *performance_timers) if (!tmp->paused) ++size; @@ -119,9 +126,14 @@ PerformanceTimer::PerformanceTimer(const std::string &s, uint64_t unit, el::Leve PerformanceTimer::~PerformanceTimer() { - performance_timers->pop_back(); if (!paused) ticks = get_tick_count() - ticks; +} + +LoggingPerformanceTimer::~LoggingPerformanceTimer() +{ + pause(); + performance_timers->pop_back(); char s[12]; snprintf(s, sizeof(s), "%8llu ", (unsigned long long)(ticks_to_ns(ticks) / (1000000000 / unit))); size_t size = 0; for (const auto *tmp: *performance_timers) if (!tmp->paused || tmp==this) ++size; diff --git a/src/common/perf_timer.h b/src/common/perf_timer.h index 0e910caf9f2..675d6234d5b 100644 --- a/src/common/perf_timer.h +++ b/src/common/perf_timer.h @@ -43,30 +43,46 @@ class PerformanceTimer; extern el::Level performance_timer_log_level; +uint64_t get_tick_count(); +uint64_t get_ticks_per_ns(); +uint64_t ticks_to_ns(uint64_t ticks); + class PerformanceTimer { public: - PerformanceTimer(const std::string &s, uint64_t unit, el::Level l = el::Level::Debug); + PerformanceTimer(bool paused = false); ~PerformanceTimer(); void pause(); void resume(); + uint64_t value() const { return ticks; } +void set(uint64_t v){ticks=v;} + +protected: + uint64_t ticks; + bool started; + bool paused; +}; + +class LoggingPerformanceTimer: public PerformanceTimer +{ +public: + LoggingPerformanceTimer(const std::string &s, uint64_t unit, el::Level l = el::Level::Debug); + ~LoggingPerformanceTimer(); + private: std::string name; uint64_t unit; el::Level level; - uint64_t ticks; - bool started; - bool paused; }; void set_performance_timer_log_level(el::Level level); -#define PERF_TIMER_UNIT(name, unit) tools::PerformanceTimer pt_##name(#name, unit, tools::performance_timer_log_level) -#define PERF_TIMER_UNIT_L(name, unit, l) tools::PerformanceTimer pt_##name(#name, unit, l) +#define PERF_TIMER_UNIT(name, unit) tools::LoggingPerformanceTimer pt_##name(#name, unit, tools::performance_timer_log_level) +#define PERF_TIMER_UNIT_L(name, unit, l) tools::LoggingPerformanceTimer pt_##name(#name, unit, l) #define PERF_TIMER(name) PERF_TIMER_UNIT(name, 1000) #define PERF_TIMER_L(name, l) PERF_TIMER_UNIT_L(name, 1000, l) -#define PERF_TIMER_START_UNIT(name, unit) std::unique_ptr pt_##name(new tools::PerformanceTimer(#name, unit, el::Level::Info)) +#define PERF_TIMER_START_UNIT(name, unit) std::unique_ptr pt_##name(new tools::LoggingPerformanceTimer(#name, unit, el::Level::Info)) #define PERF_TIMER_START(name) PERF_TIMER_START_UNIT(name, 1000) #define PERF_TIMER_STOP(name) do { pt_##name.reset(NULL); } while(0) #define PERF_TIMER_PAUSE(name) pt_##name->pause() From b17b8db3f557d6070fd8cf2da16b229259f9d986 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 28 May 2018 19:17:11 +0100 Subject: [PATCH 0363/1404] performance_tests: add stats and loop count multiplier options Stats are: min, median, standard deviation --- tests/performance_tests/main.cpp | 603 ++++++++++---------- tests/performance_tests/performance_tests.h | 123 +++- 2 files changed, 409 insertions(+), 317 deletions(-) diff --git a/tests/performance_tests/main.cpp b/tests/performance_tests/main.cpp index abcc2a3d053..de9f0e647a5 100644 --- a/tests/performance_tests/main.cpp +++ b/tests/performance_tests/main.cpp @@ -73,8 +73,12 @@ int main(int argc, char** argv) po::options_description desc_options("Command line options"); const command_line::arg_descriptor arg_filter = { "filter", "Regular expression filter for which tests to run" }; const command_line::arg_descriptor arg_verbose = { "verbose", "Verbose output", false }; + const command_line::arg_descriptor arg_stats = { "stats", "Including statistics (min/median)", false }; + const command_line::arg_descriptor arg_loop_multiplier = { "loop-multiplier", "Run for that many times more loops", 1 }; command_line::add_arg(desc_options, arg_filter, ""); command_line::add_arg(desc_options, arg_verbose, ""); + command_line::add_arg(desc_options, arg_stats, ""); + command_line::add_arg(desc_options, arg_loop_multiplier, ""); po::variables_map vm; bool r = command_line::handle_error_helper(desc_options, [&]() @@ -87,311 +91,314 @@ int main(int argc, char** argv) return 1; const std::string filter = tools::glob_to_regex(command_line::get_arg(vm, arg_filter)); - bool verbose = command_line::get_arg(vm, arg_verbose); + Params p; + p.verbose = command_line::get_arg(vm, arg_verbose); + p.stats = command_line::get_arg(vm, arg_stats); + p.loop_multiplier = command_line::get_arg(vm, arg_loop_multiplier); performance_timer timer; timer.start(); - TEST_PERFORMANCE3(filter, verbose, test_construct_tx, 1, 1, false); - TEST_PERFORMANCE3(filter, verbose, test_construct_tx, 1, 2, false); - TEST_PERFORMANCE3(filter, verbose, test_construct_tx, 1, 10, false); - TEST_PERFORMANCE3(filter, verbose, test_construct_tx, 1, 100, false); - TEST_PERFORMANCE3(filter, verbose, test_construct_tx, 1, 1000, false); - - TEST_PERFORMANCE3(filter, verbose, test_construct_tx, 2, 1, false); - TEST_PERFORMANCE3(filter, verbose, test_construct_tx, 2, 2, false); - TEST_PERFORMANCE3(filter, verbose, test_construct_tx, 2, 10, false); - TEST_PERFORMANCE3(filter, verbose, test_construct_tx, 2, 100, false); - - TEST_PERFORMANCE3(filter, verbose, test_construct_tx, 10, 1, false); - TEST_PERFORMANCE3(filter, verbose, test_construct_tx, 10, 2, false); - TEST_PERFORMANCE3(filter, verbose, test_construct_tx, 10, 10, false); - TEST_PERFORMANCE3(filter, verbose, test_construct_tx, 10, 100, false); - - TEST_PERFORMANCE3(filter, verbose, test_construct_tx, 100, 1, false); - TEST_PERFORMANCE3(filter, verbose, test_construct_tx, 100, 2, false); - TEST_PERFORMANCE3(filter, verbose, test_construct_tx, 100, 10, false); - TEST_PERFORMANCE3(filter, verbose, test_construct_tx, 100, 100, false); - - TEST_PERFORMANCE3(filter, verbose, test_construct_tx, 2, 1, true); - TEST_PERFORMANCE3(filter, verbose, test_construct_tx, 2, 2, true); - TEST_PERFORMANCE3(filter, verbose, test_construct_tx, 2, 10, true); - - TEST_PERFORMANCE3(filter, verbose, test_construct_tx, 10, 1, true); - TEST_PERFORMANCE3(filter, verbose, test_construct_tx, 10, 2, true); - TEST_PERFORMANCE3(filter, verbose, test_construct_tx, 10, 10, true); - - TEST_PERFORMANCE3(filter, verbose, test_construct_tx, 100, 1, true); - TEST_PERFORMANCE3(filter, verbose, test_construct_tx, 100, 2, true); - TEST_PERFORMANCE3(filter, verbose, test_construct_tx, 100, 10, true); - - TEST_PERFORMANCE4(filter, verbose, test_check_tx_signature, 1, 2, false, false); - TEST_PERFORMANCE4(filter, verbose, test_check_tx_signature, 2, 2, false, false); - TEST_PERFORMANCE4(filter, verbose, test_check_tx_signature, 10, 2, false, false); - TEST_PERFORMANCE4(filter, verbose, test_check_tx_signature, 100, 2, false, false); - TEST_PERFORMANCE4(filter, verbose, test_check_tx_signature, 2, 10, false, false); - - TEST_PERFORMANCE4(filter, verbose, test_check_tx_signature, 2, 2, true, false); - TEST_PERFORMANCE4(filter, verbose, test_check_tx_signature, 10, 2, true, false); - TEST_PERFORMANCE4(filter, verbose, test_check_tx_signature, 100, 2, true, false); - TEST_PERFORMANCE4(filter, verbose, test_check_tx_signature, 2, 10, true, false); - - TEST_PERFORMANCE4(filter, verbose, test_check_tx_signature, 2, 2, true, true); - TEST_PERFORMANCE4(filter, verbose, test_check_tx_signature, 10, 2, true, true); - TEST_PERFORMANCE4(filter, verbose, test_check_tx_signature, 100, 2, true, true); - TEST_PERFORMANCE4(filter, verbose, test_check_tx_signature, 2, 10, true, true); - - TEST_PERFORMANCE3(filter, verbose, test_check_tx_signature_aggregated_bulletproofs, 2, 2, 64); - TEST_PERFORMANCE3(filter, verbose, test_check_tx_signature_aggregated_bulletproofs, 10, 2, 64); - TEST_PERFORMANCE3(filter, verbose, test_check_tx_signature_aggregated_bulletproofs, 100, 2, 64); - TEST_PERFORMANCE3(filter, verbose, test_check_tx_signature_aggregated_bulletproofs, 2, 10, 64); - - TEST_PERFORMANCE4(filter, verbose, test_check_tx_signature_aggregated_bulletproofs, 2, 2, 62, 4); - TEST_PERFORMANCE4(filter, verbose, test_check_tx_signature_aggregated_bulletproofs, 10, 2, 62, 4); - TEST_PERFORMANCE4(filter, verbose, test_check_tx_signature_aggregated_bulletproofs, 2, 2, 56, 16); - TEST_PERFORMANCE4(filter, verbose, test_check_tx_signature_aggregated_bulletproofs, 10, 2, 56, 16); - - TEST_PERFORMANCE0(filter, verbose, test_is_out_to_acc); - TEST_PERFORMANCE0(filter, verbose, test_is_out_to_acc_precomp); - TEST_PERFORMANCE0(filter, verbose, test_generate_key_image_helper); - TEST_PERFORMANCE0(filter, verbose, test_generate_key_derivation); - TEST_PERFORMANCE0(filter, verbose, test_generate_key_image); - TEST_PERFORMANCE0(filter, verbose, test_derive_public_key); - TEST_PERFORMANCE0(filter, verbose, test_derive_secret_key); - TEST_PERFORMANCE0(filter, verbose, test_ge_frombytes_vartime); - TEST_PERFORMANCE0(filter, verbose, test_generate_keypair); - TEST_PERFORMANCE0(filter, verbose, test_sc_reduce32); - TEST_PERFORMANCE1(filter, verbose, test_signature, false); - TEST_PERFORMANCE1(filter, verbose, test_signature, true); - - TEST_PERFORMANCE2(filter, verbose, test_wallet2_expand_subaddresses, 50, 200); - - TEST_PERFORMANCE0(filter, verbose, test_cn_slow_hash); - TEST_PERFORMANCE1(filter, verbose, test_cn_fast_hash, 32); - TEST_PERFORMANCE1(filter, verbose, test_cn_fast_hash, 16384); - - TEST_PERFORMANCE3(filter, verbose, test_ringct_mlsag, 1, 3, false); - TEST_PERFORMANCE3(filter, verbose, test_ringct_mlsag, 1, 5, false); - TEST_PERFORMANCE3(filter, verbose, test_ringct_mlsag, 1, 10, false); - TEST_PERFORMANCE3(filter, verbose, test_ringct_mlsag, 1, 100, false); - TEST_PERFORMANCE3(filter, verbose, test_ringct_mlsag, 1, 3, true); - TEST_PERFORMANCE3(filter, verbose, test_ringct_mlsag, 1, 5, true); - TEST_PERFORMANCE3(filter, verbose, test_ringct_mlsag, 1, 10, true); - TEST_PERFORMANCE3(filter, verbose, test_ringct_mlsag, 1, 100, true); - - TEST_PERFORMANCE2(filter, verbose, test_equality, memcmp32, true); - TEST_PERFORMANCE2(filter, verbose, test_equality, memcmp32, false); - TEST_PERFORMANCE2(filter, verbose, test_equality, verify32, false); - TEST_PERFORMANCE2(filter, verbose, test_equality, verify32, false); - - TEST_PERFORMANCE1(filter, verbose, test_range_proof, true); - TEST_PERFORMANCE1(filter, verbose, test_range_proof, false); - - TEST_PERFORMANCE2(filter, verbose, test_bulletproof, true, 1); // 1 bulletproof with 1 amount - TEST_PERFORMANCE2(filter, verbose, test_bulletproof, false, 1); - - TEST_PERFORMANCE2(filter, verbose, test_bulletproof, true, 2); // 1 bulletproof with 2 amounts - TEST_PERFORMANCE2(filter, verbose, test_bulletproof, false, 2); - - TEST_PERFORMANCE2(filter, verbose, test_bulletproof, true, 15); // 1 bulletproof with 15 amounts - TEST_PERFORMANCE2(filter, verbose, test_bulletproof, false, 15); - - TEST_PERFORMANCE6(filter, verbose, test_aggregated_bulletproof, false, 2, 1, 1, 0, 4); - TEST_PERFORMANCE6(filter, verbose, test_aggregated_bulletproof, true, 2, 1, 1, 0, 4); // 4 proofs, each with 2 amounts - TEST_PERFORMANCE6(filter, verbose, test_aggregated_bulletproof, false, 8, 1, 1, 0, 4); - TEST_PERFORMANCE6(filter, verbose, test_aggregated_bulletproof, true, 8, 1, 1, 0, 4); // 4 proofs, each with 8 amounts - TEST_PERFORMANCE6(filter, verbose, test_aggregated_bulletproof, false, 1, 1, 2, 0, 4); - TEST_PERFORMANCE6(filter, verbose, test_aggregated_bulletproof, true, 1, 1, 2, 0, 4); // 4 proofs with 1, 2, 4, 8 amounts - TEST_PERFORMANCE6(filter, verbose, test_aggregated_bulletproof, false, 1, 8, 1, 1, 4); - TEST_PERFORMANCE6(filter, verbose, test_aggregated_bulletproof, true, 1, 8, 1, 1, 4); // 32 proofs, with 1, 2, 3, 4 amounts, 8 of each - TEST_PERFORMANCE6(filter, verbose, test_aggregated_bulletproof, false, 2, 1, 1, 0, 64); - TEST_PERFORMANCE6(filter, verbose, test_aggregated_bulletproof, true, 2, 1, 1, 0, 64); // 64 proof, each with 2 amounts - - TEST_PERFORMANCE3(filter, verbose, test_ringct_mlsag, 1, 3, false); - TEST_PERFORMANCE3(filter, verbose, test_ringct_mlsag, 1, 5, false); - TEST_PERFORMANCE3(filter, verbose, test_ringct_mlsag, 1, 10, false); - TEST_PERFORMANCE3(filter, verbose, test_ringct_mlsag, 1, 100, false); - TEST_PERFORMANCE3(filter, verbose, test_ringct_mlsag, 1, 3, true); - TEST_PERFORMANCE3(filter, verbose, test_ringct_mlsag, 1, 5, true); - TEST_PERFORMANCE3(filter, verbose, test_ringct_mlsag, 1, 10, true); - TEST_PERFORMANCE3(filter, verbose, test_ringct_mlsag, 1, 100, true); - - TEST_PERFORMANCE1(filter, verbose, test_crypto_ops, op_sc_add); - TEST_PERFORMANCE1(filter, verbose, test_crypto_ops, op_sc_sub); - TEST_PERFORMANCE1(filter, verbose, test_crypto_ops, op_sc_mul); - TEST_PERFORMANCE1(filter, verbose, test_crypto_ops, op_ge_add_raw); - TEST_PERFORMANCE1(filter, verbose, test_crypto_ops, op_ge_add_p3_p3); - TEST_PERFORMANCE1(filter, verbose, test_crypto_ops, op_addKeys); - TEST_PERFORMANCE1(filter, verbose, test_crypto_ops, op_scalarmultBase); - TEST_PERFORMANCE1(filter, verbose, test_crypto_ops, op_scalarmultKey); - TEST_PERFORMANCE1(filter, verbose, test_crypto_ops, op_ge_double_scalarmult_base_vartime); - TEST_PERFORMANCE1(filter, verbose, test_crypto_ops, op_ge_double_scalarmult_precomp_vartime); - TEST_PERFORMANCE1(filter, verbose, test_crypto_ops, op_ge_double_scalarmult_precomp_vartime2); - TEST_PERFORMANCE1(filter, verbose, test_crypto_ops, op_addKeys2); - TEST_PERFORMANCE1(filter, verbose, test_crypto_ops, op_addKeys3); - TEST_PERFORMANCE1(filter, verbose, test_crypto_ops, op_addKeys3_2); - - TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_bos_coster, 2); - TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_bos_coster, 4); - TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_bos_coster, 8); - TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_bos_coster, 16); - TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_bos_coster, 32); - TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_bos_coster, 64); - TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_bos_coster, 128); - TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_bos_coster, 256); - TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_bos_coster, 512); - TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_bos_coster, 1024); - TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_bos_coster, 2048); - TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_bos_coster, 4096); - - TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_straus, 2); - TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_straus, 4); - TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_straus, 8); - TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_straus, 16); - TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_straus, 32); - TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_straus, 64); - TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_straus, 128); - TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_straus, 256); - TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_straus, 512); - TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_straus, 1024); - TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_straus, 2048); - TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_straus, 4096); - - TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_straus_cached, 2); - TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_straus_cached, 4); - TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_straus_cached, 8); - TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_straus_cached, 16); - TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_straus_cached, 32); - TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_straus_cached, 64); - TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_straus_cached, 128); - TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_straus_cached, 256); - TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_straus_cached, 512); - TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_straus_cached, 1024); - TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_straus_cached, 2048); - TEST_PERFORMANCE2(filter, verbose, test_multiexp, multiexp_straus_cached, 4096); + TEST_PERFORMANCE3(filter, p, test_construct_tx, 1, 1, false); + TEST_PERFORMANCE3(filter, p, test_construct_tx, 1, 2, false); + TEST_PERFORMANCE3(filter, p, test_construct_tx, 1, 10, false); + TEST_PERFORMANCE3(filter, p, test_construct_tx, 1, 100, false); + TEST_PERFORMANCE3(filter, p, test_construct_tx, 1, 1000, false); + + TEST_PERFORMANCE3(filter, p, test_construct_tx, 2, 1, false); + TEST_PERFORMANCE3(filter, p, test_construct_tx, 2, 2, false); + TEST_PERFORMANCE3(filter, p, test_construct_tx, 2, 10, false); + TEST_PERFORMANCE3(filter, p, test_construct_tx, 2, 100, false); + + TEST_PERFORMANCE3(filter, p, test_construct_tx, 10, 1, false); + TEST_PERFORMANCE3(filter, p, test_construct_tx, 10, 2, false); + TEST_PERFORMANCE3(filter, p, test_construct_tx, 10, 10, false); + TEST_PERFORMANCE3(filter, p, test_construct_tx, 10, 100, false); + + TEST_PERFORMANCE3(filter, p, test_construct_tx, 100, 1, false); + TEST_PERFORMANCE3(filter, p, test_construct_tx, 100, 2, false); + TEST_PERFORMANCE3(filter, p, test_construct_tx, 100, 10, false); + TEST_PERFORMANCE3(filter, p, test_construct_tx, 100, 100, false); + + TEST_PERFORMANCE3(filter, p, test_construct_tx, 2, 1, true); + TEST_PERFORMANCE3(filter, p, test_construct_tx, 2, 2, true); + TEST_PERFORMANCE3(filter, p, test_construct_tx, 2, 10, true); + + TEST_PERFORMANCE3(filter, p, test_construct_tx, 10, 1, true); + TEST_PERFORMANCE3(filter, p, test_construct_tx, 10, 2, true); + TEST_PERFORMANCE3(filter, p, test_construct_tx, 10, 10, true); + + TEST_PERFORMANCE3(filter, p, test_construct_tx, 100, 1, true); + TEST_PERFORMANCE3(filter, p, test_construct_tx, 100, 2, true); + TEST_PERFORMANCE3(filter, p, test_construct_tx, 100, 10, true); + + TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 1, 2, false, false); + TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 2, 2, false, false); + TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 10, 2, false, false); + TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 100, 2, false, false); + TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 2, 10, false, false); + + TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 2, 2, true, false); + TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 10, 2, true, false); + TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 100, 2, true, false); + TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 2, 10, true, false); + + TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 2, 2, true, true); + TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 10, 2, true, true); + TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 100, 2, true, true); + TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 2, 10, true, true); + + TEST_PERFORMANCE3(filter, p, test_check_tx_signature_aggregated_bulletproofs, 2, 2, 64); + TEST_PERFORMANCE3(filter, p, test_check_tx_signature_aggregated_bulletproofs, 10, 2, 64); + TEST_PERFORMANCE3(filter, p, test_check_tx_signature_aggregated_bulletproofs, 100, 2, 64); + TEST_PERFORMANCE3(filter, p, test_check_tx_signature_aggregated_bulletproofs, 2, 10, 64); + + TEST_PERFORMANCE4(filter, p, test_check_tx_signature_aggregated_bulletproofs, 2, 2, 62, 4); + TEST_PERFORMANCE4(filter, p, test_check_tx_signature_aggregated_bulletproofs, 10, 2, 62, 4); + TEST_PERFORMANCE4(filter, p, test_check_tx_signature_aggregated_bulletproofs, 2, 2, 56, 16); + TEST_PERFORMANCE4(filter, p, test_check_tx_signature_aggregated_bulletproofs, 10, 2, 56, 16); + + TEST_PERFORMANCE0(filter, p, test_is_out_to_acc); + TEST_PERFORMANCE0(filter, p, test_is_out_to_acc_precomp); + TEST_PERFORMANCE0(filter, p, test_generate_key_image_helper); + TEST_PERFORMANCE0(filter, p, test_generate_key_derivation); + TEST_PERFORMANCE0(filter, p, test_generate_key_image); + TEST_PERFORMANCE0(filter, p, test_derive_public_key); + TEST_PERFORMANCE0(filter, p, test_derive_secret_key); + TEST_PERFORMANCE0(filter, p, test_ge_frombytes_vartime); + TEST_PERFORMANCE0(filter, p, test_generate_keypair); + TEST_PERFORMANCE0(filter, p, test_sc_reduce32); + TEST_PERFORMANCE1(filter, p, test_signature, false); + TEST_PERFORMANCE1(filter, p, test_signature, true); + + TEST_PERFORMANCE2(filter, p, test_wallet2_expand_subaddresses, 50, 200); + + TEST_PERFORMANCE0(filter, p, test_cn_slow_hash); + TEST_PERFORMANCE1(filter, p, test_cn_fast_hash, 32); + TEST_PERFORMANCE1(filter, p, test_cn_fast_hash, 16384); + + TEST_PERFORMANCE3(filter, p, test_ringct_mlsag, 1, 3, false); + TEST_PERFORMANCE3(filter, p, test_ringct_mlsag, 1, 5, false); + TEST_PERFORMANCE3(filter, p, test_ringct_mlsag, 1, 10, false); + TEST_PERFORMANCE3(filter, p, test_ringct_mlsag, 1, 100, false); + TEST_PERFORMANCE3(filter, p, test_ringct_mlsag, 1, 3, true); + TEST_PERFORMANCE3(filter, p, test_ringct_mlsag, 1, 5, true); + TEST_PERFORMANCE3(filter, p, test_ringct_mlsag, 1, 10, true); + TEST_PERFORMANCE3(filter, p, test_ringct_mlsag, 1, 100, true); + + TEST_PERFORMANCE2(filter, p, test_equality, memcmp32, true); + TEST_PERFORMANCE2(filter, p, test_equality, memcmp32, false); + TEST_PERFORMANCE2(filter, p, test_equality, verify32, false); + TEST_PERFORMANCE2(filter, p, test_equality, verify32, false); + + TEST_PERFORMANCE1(filter, p, test_range_proof, true); + TEST_PERFORMANCE1(filter, p, test_range_proof, false); + + TEST_PERFORMANCE2(filter, p, test_bulletproof, true, 1); // 1 bulletproof with 1 amount + TEST_PERFORMANCE2(filter, p, test_bulletproof, false, 1); + + TEST_PERFORMANCE2(filter, p, test_bulletproof, true, 2); // 1 bulletproof with 2 amounts + TEST_PERFORMANCE2(filter, p, test_bulletproof, false, 2); + + TEST_PERFORMANCE2(filter, p, test_bulletproof, true, 15); // 1 bulletproof with 15 amounts + TEST_PERFORMANCE2(filter, p, test_bulletproof, false, 15); + + TEST_PERFORMANCE6(filter, p, test_aggregated_bulletproof, false, 2, 1, 1, 0, 4); + TEST_PERFORMANCE6(filter, p, test_aggregated_bulletproof, true, 2, 1, 1, 0, 4); // 4 proofs, each with 2 amounts + TEST_PERFORMANCE6(filter, p, test_aggregated_bulletproof, false, 8, 1, 1, 0, 4); + TEST_PERFORMANCE6(filter, p, test_aggregated_bulletproof, true, 8, 1, 1, 0, 4); // 4 proofs, each with 8 amounts + TEST_PERFORMANCE6(filter, p, test_aggregated_bulletproof, false, 1, 1, 2, 0, 4); + TEST_PERFORMANCE6(filter, p, test_aggregated_bulletproof, true, 1, 1, 2, 0, 4); // 4 proofs with 1, 2, 4, 8 amounts + TEST_PERFORMANCE6(filter, p, test_aggregated_bulletproof, false, 1, 8, 1, 1, 4); + TEST_PERFORMANCE6(filter, p, test_aggregated_bulletproof, true, 1, 8, 1, 1, 4); // 32 proofs, with 1, 2, 3, 4 amounts, 8 of each + TEST_PERFORMANCE6(filter, p, test_aggregated_bulletproof, false, 2, 1, 1, 0, 64); + TEST_PERFORMANCE6(filter, p, test_aggregated_bulletproof, true, 2, 1, 1, 0, 64); // 64 proof, each with 2 amounts + + TEST_PERFORMANCE3(filter, p, test_ringct_mlsag, 1, 3, false); + TEST_PERFORMANCE3(filter, p, test_ringct_mlsag, 1, 5, false); + TEST_PERFORMANCE3(filter, p, test_ringct_mlsag, 1, 10, false); + TEST_PERFORMANCE3(filter, p, test_ringct_mlsag, 1, 100, false); + TEST_PERFORMANCE3(filter, p, test_ringct_mlsag, 1, 3, true); + TEST_PERFORMANCE3(filter, p, test_ringct_mlsag, 1, 5, true); + TEST_PERFORMANCE3(filter, p, test_ringct_mlsag, 1, 10, true); + TEST_PERFORMANCE3(filter, p, test_ringct_mlsag, 1, 100, true); + + TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_sc_add); + TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_sc_sub); + TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_sc_mul); + TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_ge_add_raw); + TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_ge_add_p3_p3); + TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_addKeys); + TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_scalarmultBase); + TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_scalarmultKey); + TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_ge_double_scalarmult_base_vartime); + TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_ge_double_scalarmult_precomp_vartime); + TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_ge_double_scalarmult_precomp_vartime2); + TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_addKeys2); + TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_addKeys3); + TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_addKeys3_2); + + TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_bos_coster, 2); + TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_bos_coster, 4); + TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_bos_coster, 8); + TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_bos_coster, 16); + TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_bos_coster, 32); + TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_bos_coster, 64); + TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_bos_coster, 128); + TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_bos_coster, 256); + TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_bos_coster, 512); + TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_bos_coster, 1024); + TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_bos_coster, 2048); + TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_bos_coster, 4096); + + TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_straus, 2); + TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_straus, 4); + TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_straus, 8); + TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_straus, 16); + TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_straus, 32); + TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_straus, 64); + TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_straus, 128); + TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_straus, 256); + TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_straus, 512); + TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_straus, 1024); + TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_straus, 2048); + TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_straus, 4096); + + TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_straus_cached, 2); + TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_straus_cached, 4); + TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_straus_cached, 8); + TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_straus_cached, 16); + TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_straus_cached, 32); + TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_straus_cached, 64); + TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_straus_cached, 128); + TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_straus_cached, 256); + TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_straus_cached, 512); + TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_straus_cached, 1024); + TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_straus_cached, 2048); + TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_straus_cached, 4096); #if 1 - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 2, 1); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 4, 2); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 8, 2); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 16, 4); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 32, 4); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 64, 5); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 128, 6); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 256, 6); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 512, 6); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 1024, 8); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 2048, 8); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 4096, 9); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 2, 1); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 4, 2); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 8, 2); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 16, 4); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 32, 4); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 64, 5); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 128, 6); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 256, 6); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 512, 6); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 1024, 8); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 2048, 8); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 4096, 9); #else - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 2, 1); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 2, 2); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 2, 3); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 2, 4); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 2, 5); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 2, 6); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 2, 7); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 2, 8); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 2, 9); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 4, 1); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 4, 2); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 4, 3); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 4, 4); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 4, 5); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 4, 6); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 4, 7); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 4, 8); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 4, 9); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 8, 1); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 8, 2); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 8, 3); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 8, 4); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 8, 5); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 8, 6); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 8, 7); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 8, 8); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 8, 9); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 16, 1); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 16, 2); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 16, 3); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 16, 4); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 16, 5); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 16, 6); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 16, 7); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 16, 8); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 16, 9); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 32, 1); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 32, 2); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 32, 3); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 32, 4); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 32, 5); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 32, 6); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 32, 7); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 32, 8); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 32, 9); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 64, 1); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 64, 2); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 64, 3); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 64, 4); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 64, 5); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 64, 6); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 64, 7); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 64, 8); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 64, 9); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 128, 1); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 128, 2); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 128, 3); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 128, 4); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 128, 5); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 128, 6); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 128, 7); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 128, 8); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 128, 9); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 256, 1); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 256, 2); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 256, 3); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 256, 4); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 256, 5); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 256, 6); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 256, 7); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 256, 8); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 256, 9); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 512, 1); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 512, 2); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 512, 3); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 512, 4); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 512, 5); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 512, 6); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 512, 7); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 512, 8); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 512, 9); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 1024, 1); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 1024, 2); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 1024, 3); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 1024, 4); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 1024, 5); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 1024, 6); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 1024, 7); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 1024, 8); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 1024, 9); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 2048, 1); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 2048, 2); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 2048, 3); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 2048, 4); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 2048, 5); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 2048, 6); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 2048, 7); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 2048, 8); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 2048, 9); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 4096, 1); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 4096, 2); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 4096, 3); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 4096, 4); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 4096, 5); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 4096, 6); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 4096, 7); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 4096, 8); - TEST_PERFORMANCE3(filter, verbose, test_multiexp, multiexp_pippenger, 4096, 9); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 2, 1); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 2, 2); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 2, 3); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 2, 4); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 2, 5); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 2, 6); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 2, 7); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 2, 8); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 2, 9); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 4, 1); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 4, 2); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 4, 3); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 4, 4); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 4, 5); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 4, 6); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 4, 7); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 4, 8); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 4, 9); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 8, 1); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 8, 2); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 8, 3); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 8, 4); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 8, 5); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 8, 6); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 8, 7); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 8, 8); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 8, 9); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 16, 1); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 16, 2); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 16, 3); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 16, 4); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 16, 5); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 16, 6); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 16, 7); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 16, 8); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 16, 9); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 32, 1); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 32, 2); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 32, 3); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 32, 4); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 32, 5); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 32, 6); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 32, 7); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 32, 8); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 32, 9); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 64, 1); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 64, 2); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 64, 3); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 64, 4); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 64, 5); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 64, 6); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 64, 7); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 64, 8); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 64, 9); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 128, 1); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 128, 2); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 128, 3); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 128, 4); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 128, 5); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 128, 6); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 128, 7); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 128, 8); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 128, 9); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 256, 1); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 256, 2); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 256, 3); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 256, 4); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 256, 5); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 256, 6); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 256, 7); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 256, 8); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 256, 9); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 512, 1); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 512, 2); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 512, 3); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 512, 4); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 512, 5); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 512, 6); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 512, 7); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 512, 8); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 512, 9); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 1024, 1); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 1024, 2); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 1024, 3); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 1024, 4); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 1024, 5); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 1024, 6); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 1024, 7); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 1024, 8); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 1024, 9); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 2048, 1); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 2048, 2); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 2048, 3); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 2048, 4); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 2048, 5); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 2048, 6); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 2048, 7); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 2048, 8); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 2048, 9); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 4096, 1); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 4096, 2); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 4096, 3); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 4096, 4); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 4096, 5); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 4096, 6); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 4096, 7); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 4096, 8); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 4096, 9); #endif std::cout << "Tests finished. Elapsed time: " << timer.elapsed_ms() / 1000 << " sec" << std::endl; diff --git a/tests/performance_tests/performance_tests.h b/tests/performance_tests/performance_tests.h index ce9baf61f79..d37dda72944 100644 --- a/tests/performance_tests/performance_tests.h +++ b/tests/performance_tests/performance_tests.h @@ -36,6 +36,9 @@ #include #include +#include "misc_language.h" +#include "common/perf_timer.h" + class performance_timer { public: @@ -62,14 +65,21 @@ class performance_timer clock::time_point m_start; }; +struct Params +{ + bool verbose; + bool stats; + unsigned loop_multiplier; +}; template class test_runner { public: - test_runner(bool verbose = true) + test_runner(const Params ¶ms) : m_elapsed(0) - , m_verbose(verbose) + , m_params(params) + , m_per_call_timers(T::loop_count * params.loop_multiplier, {true}) { } @@ -82,14 +92,18 @@ class test_runner performance_timer timer; timer.start(); warm_up(); - if (m_verbose) + if (m_params.verbose) std::cout << "Warm up: " << timer.elapsed_ms() << " ms" << std::endl; timer.start(); - for (size_t i = 0; i < T::loop_count; ++i) + for (size_t i = 0; i < T::loop_count * m_params.loop_multiplier; ++i) { + if (m_params.stats) + m_per_call_timers[i].resume(); if (!test.test()) return false; + if (m_params.stats) + m_per_call_timers[i].pause(); } m_elapsed = timer.elapsed_ms(); @@ -101,9 +115,62 @@ class test_runner int time_per_call(int scale = 1) const { static_assert(0 < T::loop_count, "T::loop_count must be greater than 0"); - return m_elapsed * scale / T::loop_count; + return m_elapsed * scale / (T::loop_count * m_params.loop_multiplier); + } + + uint64_t per_call_min() const + { + uint64_t v = std::numeric_limits::max(); + for (const auto &pt: m_per_call_timers) + v = std::min(v, pt.value()); + return v; + } + + uint64_t per_call_max() const + { + uint64_t v = std::numeric_limits::min(); + for (const auto &pt: m_per_call_timers) + v = std::max(v, pt.value()); + return v; + } + + uint64_t per_call_mean() const + { + uint64_t v = 0; + for (const auto &pt: m_per_call_timers) + v += pt.value(); + return v / m_per_call_timers.size(); } + uint64_t per_call_median() const + { + std::vector values; + values.reserve(m_per_call_timers.size()); + for (const auto &pt: m_per_call_timers) + values.push_back(pt.value()); + return epee::misc_utils::median(values); + } + + uint64_t per_call_stddev() const + { + if (m_per_call_timers.size() <= 1) + return 0; + const uint64_t mean = per_call_mean(); + uint64_t acc = 0; + for (const auto &pt: m_per_call_timers) + { + int64_t dv = pt.value() - mean; + acc += dv * dv; + } + acc /= m_per_call_timers.size () - 1; + return sqrt(acc); + } + + uint64_t min_time_ns() const { return tools::ticks_to_ns(per_call_min()); } + uint64_t max_time_ns() const { return tools::ticks_to_ns(per_call_max()); } + uint64_t median_time_ns() const { return tools::ticks_to_ns(per_call_median()); } + uint64_t standard_deviation_time_ns() const { return tools::ticks_to_ns(per_call_stddev()); } + private: /** * Warm up processor core, enabling turbo boost, etc. @@ -122,30 +189,39 @@ class test_runner private: volatile uint64_t m_warm_up; /// m_per_call_timers; }; template -void run_test(const std::string &filter, bool verbose, const char* test_name) +void run_test(const std::string &filter, const Params ¶ms, const char* test_name) { boost::smatch match; if (!filter.empty() && !boost::regex_match(std::string(test_name), match, boost::regex(filter))) return; - test_runner runner(verbose); + test_runner runner(params); if (runner.run()) { - if (verbose) + if (params.verbose) { std::cout << test_name << " - OK:\n"; - std::cout << " loop count: " << T::loop_count << '\n'; + std::cout << " loop count: " << T::loop_count * params.loop_multiplier << '\n'; std::cout << " elapsed: " << runner.elapsed_time() << " ms\n"; + if (params.stats) + { + std::cout << " min: " << runner.min_time_ns() << " ns\n"; + std::cout << " max: " << runner.max_time_ns() << " ns\n"; + std::cout << " median: " << runner.median_time_ns() << " ns\n"; + std::cout << " std dev: " << runner.standard_deviation_time_ns() << " ns\n"; + } } else { - std::cout << test_name << " (" << T::loop_count << " calls) - OK:"; + std::cout << test_name << " (" << T::loop_count * params.loop_multiplier << " calls) - OK:"; } const char *unit = "ms"; + uint64_t scale = 1000000; int time_per_call = runner.time_per_call(); if (time_per_call < 30000) { time_per_call = runner.time_per_call(1000); @@ -154,8 +230,17 @@ void run_test(const std::string &filter, bool verbose, const char* test_name) #else unit = "µs"; #endif + scale = 1000; + } + std::cout << (params.verbose ? " time per call: " : " ") << time_per_call << " " << unit << "/call" << (params.verbose ? "\n" : ""); + if (params.stats) + { + uint64_t min_ns = runner.min_time_ns() / scale; + uint64_t med_ns = runner.median_time_ns() / scale; + uint64_t stddev_ns = runner.standard_deviation_time_ns() / scale; + std::cout << " (min " << min_ns << " " << unit << ", median " << med_ns << " " << unit << ", std dev " << stddev_ns << " " << unit << ")"; } - std::cout << (verbose ? " time per call: " : " ") << time_per_call << " " << unit << "/call" << (verbose ? "\n" : "") << std::endl; + std::cout << std::endl; } else { @@ -164,10 +249,10 @@ void run_test(const std::string &filter, bool verbose, const char* test_name) } #define QUOTEME(x) #x -#define TEST_PERFORMANCE0(filter, verbose, test_class) run_test< test_class >(filter, verbose, QUOTEME(test_class)) -#define TEST_PERFORMANCE1(filter, verbose, test_class, a0) run_test< test_class >(filter, verbose, QUOTEME(test_class)) -#define TEST_PERFORMANCE2(filter, verbose, test_class, a0, a1) run_test< test_class >(filter, verbose, QUOTEME(test_class) "<" QUOTEME(a0) ", " QUOTEME(a1) ">") -#define TEST_PERFORMANCE3(filter, verbose, test_class, a0, a1, a2) run_test< test_class >(filter, verbose, QUOTEME(test_class) "<" QUOTEME(a0) ", " QUOTEME(a1) ", " QUOTEME(a2) ">") -#define TEST_PERFORMANCE4(filter, verbose, test_class, a0, a1, a2, a3) run_test< test_class >(filter, verbose, QUOTEME(test_class) "<" QUOTEME(a0) ", " QUOTEME(a1) ", " QUOTEME(a2) ", " QUOTEME(a3) ">") -#define TEST_PERFORMANCE5(filter, verbose, test_class, a0, a1, a2, a3, a4) run_test< test_class >(filter, verbose, QUOTEME(test_class) "<" QUOTEME(a0) ", " QUOTEME(a1) ", " QUOTEME(a2) ", " QUOTEME(a3) ", " QUOTEME(a4) ">") -#define TEST_PERFORMANCE6(filter, verbose, test_class, a0, a1, a2, a3, a4, a5) run_test< test_class >(filter, verbose, QUOTEME(test_class) "<" QUOTEME(a0) ", " QUOTEME(a1) ", " QUOTEME(a2) ", " QUOTEME(a3) ", " QUOTEME(a4) ", " QUOTEME(a5) ">") +#define TEST_PERFORMANCE0(filter, params, test_class) run_test< test_class >(filter, params, QUOTEME(test_class)) +#define TEST_PERFORMANCE1(filter, params, test_class, a0) run_test< test_class >(filter, params, QUOTEME(test_class)) +#define TEST_PERFORMANCE2(filter, params, test_class, a0, a1) run_test< test_class >(filter, params, QUOTEME(test_class) "<" QUOTEME(a0) ", " QUOTEME(a1) ">") +#define TEST_PERFORMANCE3(filter, params, test_class, a0, a1, a2) run_test< test_class >(filter, params, QUOTEME(test_class) "<" QUOTEME(a0) ", " QUOTEME(a1) ", " QUOTEME(a2) ">") +#define TEST_PERFORMANCE4(filter, params, test_class, a0, a1, a2, a3) run_test< test_class >(filter, params, QUOTEME(test_class) "<" QUOTEME(a0) ", " QUOTEME(a1) ", " QUOTEME(a2) ", " QUOTEME(a3) ">") +#define TEST_PERFORMANCE5(filter, params, test_class, a0, a1, a2, a3, a4) run_test< test_class >(filter, params, QUOTEME(test_class) "<" QUOTEME(a0) ", " QUOTEME(a1) ", " QUOTEME(a2) ", " QUOTEME(a3) ", " QUOTEME(a4) ">") +#define TEST_PERFORMANCE6(filter, params, test_class, a0, a1, a2, a3, a4, a5) run_test< test_class >(filter, params, QUOTEME(test_class) "<" QUOTEME(a0) ", " QUOTEME(a1) ", " QUOTEME(a2) ", " QUOTEME(a3) ", " QUOTEME(a4) ", " QUOTEME(a5) ">") From 51eb3bdcd6090ae42d4aca24c35772f0d6104c72 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 29 May 2018 12:38:21 +0100 Subject: [PATCH 0364/1404] add pippenger unit tests --- tests/unit_tests/multiexp.cpp | 55 +++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/tests/unit_tests/multiexp.cpp b/tests/unit_tests/multiexp.cpp index 2dce5bb8094..10303c392aa 100644 --- a/tests/unit_tests/multiexp.cpp +++ b/tests/unit_tests/multiexp.cpp @@ -74,6 +74,13 @@ TEST(multiexp, straus_empty) ASSERT_TRUE(basic(data) == straus(data)); } +TEST(multiexp, pippenger_empty) +{ + std::vector data; + data.push_back({rct::zero(), get_p3(rct::identity())}); + ASSERT_TRUE(basic(data) == pippenger(data)); +} + TEST(multiexp, bos_coster_only_zeroes) { std::vector data; @@ -90,6 +97,14 @@ TEST(multiexp, straus_only_zeroes) ASSERT_TRUE(basic(data) == straus(data)); } +TEST(multiexp, pippenger_only_zeroes) +{ + std::vector data; + for (int n = 0; n < 16; ++n) + data.push_back({rct::zero(), get_p3(TESTPOINT)}); + ASSERT_TRUE(basic(data) == pippenger(data)); +} + TEST(multiexp, bos_coster_only_identities) { std::vector data; @@ -106,6 +121,14 @@ TEST(multiexp, straus_only_identities) ASSERT_TRUE(basic(data) == straus(data)); } +TEST(multiexp, pippenger_only_identities) +{ + std::vector data; + for (int n = 0; n < 16; ++n) + data.push_back({TESTSCALAR, get_p3(rct::identity())}); + ASSERT_TRUE(basic(data) == pippenger(data)); +} + TEST(multiexp, bos_coster_random) { std::vector data; @@ -126,6 +149,16 @@ TEST(multiexp, straus_random) } } +TEST(multiexp, pippenger_random) +{ + std::vector data; + for (int n = 0; n < 32; ++n) + { + data.push_back({rct::skGen(), get_p3(rct::scalarmultBase(rct::skGen()))}); + ASSERT_TRUE(basic(data) == pippenger(data)); + } +} + TEST(multiexp, straus_cached) { static constexpr size_t N = 256; @@ -147,3 +180,25 @@ TEST(multiexp, straus_cached) ASSERT_TRUE(basic(data) == straus(data, cache)); } } + +TEST(multiexp, pippenger_cached) +{ + static constexpr size_t N = 256; + std::vector P(N); + for (size_t n = 0; n < N; ++n) + { + P[n].scalar = rct::zero(); + ASSERT_TRUE(ge_frombytes_vartime(&P[n].point, rct::scalarmultBase(rct::skGen()).bytes) == 0); + } + std::shared_ptr cache = rct::pippenger_init_cache(P); + for (size_t n = 0; n < N/16; ++n) + { + std::vector data; + size_t sz = 1 + crypto::rand() % (N-1); + for (size_t s = 0; s < sz; ++s) + { + data.push_back({rct::skGen(), P[s].point}); + } + ASSERT_TRUE(basic(data) == pippenger(data, cache)); + } +} From 0b05a0fa74512970ceba3c15ed2cbf027ef7b6da Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 29 May 2018 12:39:00 +0100 Subject: [PATCH 0365/1404] Add Pippenger cache and limit Straus cache size --- src/ringct/bulletproofs.cc | 29 +++++-- src/ringct/multiexp.cc | 76 ++++++++++++----- src/ringct/multiexp.h | 7 +- tests/performance_tests/main.cpp | 131 +++++++++++++++++++++++++++-- tests/performance_tests/multiexp.h | 13 ++- 5 files changed, 217 insertions(+), 39 deletions(-) diff --git a/src/ringct/bulletproofs.cc b/src/ringct/bulletproofs.cc index 83005eaa439..4eb6d6d5b9d 100644 --- a/src/ringct/bulletproofs.cc +++ b/src/ringct/bulletproofs.cc @@ -49,6 +49,9 @@ extern "C" #define PERF_TIMER_START_BP(x) PERF_TIMER_START_UNIT(x, 1000000) +#define STRAUS_SIZE_LIMIT 128 +#define PIPPENGER_SIZE_LIMIT 0 + namespace rct { @@ -61,8 +64,8 @@ static constexpr size_t maxN = 64; static constexpr size_t maxM = BULLETPROOF_MAX_OUTPUTS; static rct::key Hi[maxN*maxM], Gi[maxN*maxM]; static ge_p3 Hi_p3[maxN*maxM], Gi_p3[maxN*maxM]; -static ge_dsmp Gprecomp[maxN*maxM], Hprecomp[maxN*maxM]; -static std::shared_ptr HiGi_cache; +static std::shared_ptr straus_HiGi_cache; +static std::shared_ptr pippenger_HiGi_cache; static const rct::key TWO = { {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 } }; static const rct::keyV oneN = vector_dup(rct::identity(), maxN); static const rct::keyV twoN = vector_powers(TWO, maxN); @@ -73,9 +76,12 @@ static inline rct::key multiexp(const std::vector &data, bool HiGi { static const size_t STEP = getenv("STRAUS_STEP") ? atoi(getenv("STRAUS_STEP")) : 0; if (HiGi) - return data.size() <= 256 ? straus(data, HiGi_cache, STEP) : pippenger(data, get_pippenger_c(data.size())); + { + static_assert(128 <= STRAUS_SIZE_LIMIT, "Straus in precalc mode can only be calculated till STRAUS_SIZE_LIMIT"); + return data.size() <= 128 ? straus(data, straus_HiGi_cache, STEP) : pippenger(data, pippenger_HiGi_cache, get_pippenger_c(data.size())); + } else - return data.size() <= 64 ? straus(data, NULL, STEP) : pippenger(data, get_pippenger_c(data.size())); + return data.size() <= 64 ? straus(data, NULL, STEP) : pippenger(data, NULL, get_pippenger_c(data.size())); } //addKeys3acc_p3 @@ -123,18 +129,23 @@ static void init_exponents() for (size_t i = 0; i < maxN*maxM; ++i) { Hi[i] = get_exponent(rct::H, i * 2); - rct::precomp(Hprecomp[i], Hi[i]); CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&Hi_p3[i], Hi[i].bytes) == 0, "ge_frombytes_vartime failed"); Gi[i] = get_exponent(rct::H, i * 2 + 1); - rct::precomp(Gprecomp[i], Gi[i]); CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&Gi_p3[i], Gi[i].bytes) == 0, "ge_frombytes_vartime failed"); data.push_back({rct::zero(), Gi[i]}); data.push_back({rct::zero(), Hi[i]}); } - HiGi_cache = straus_init_cache(data); - size_t cache_size = (sizeof(Hi)+sizeof(Hprecomp)+sizeof(Hi_p3))*2 + straus_get_cache_size(HiGi_cache); - MINFO("cache size: " << cache_size/1024 << " kB"); + + straus_HiGi_cache = straus_init_cache(data, STRAUS_SIZE_LIMIT); + pippenger_HiGi_cache = pippenger_init_cache(data, PIPPENGER_SIZE_LIMIT); + + MINFO("Hi/Gi cache size: " << (sizeof(Hi)+sizeof(Gi))/1024 << " kB"); + MINFO("Hi_p3/Gi_p3 cache size: " << (sizeof(Hi_p3)+sizeof(Gi_p3))/1024 << " kB"); + MINFO("Straus cache size: " << straus_get_cache_size(straus_HiGi_cache)/1024 << " kB"); + MINFO("Pippenger cache size: " << pippenger_get_cache_size(pippenger_HiGi_cache)/1024 << " kB"); + size_t cache_size = (sizeof(Hi)+sizeof(Hi_p3))*2 + straus_get_cache_size(straus_HiGi_cache) + pippenger_get_cache_size(pippenger_HiGi_cache); + MINFO("Total cache size: " << cache_size/1024 << "kB"); init_done = true; } diff --git a/src/ringct/multiexp.cc b/src/ringct/multiexp.cc index fd2d1cbd74a..f9ef9e42268 100644 --- a/src/ringct/multiexp.cc +++ b/src/ringct/multiexp.cc @@ -343,9 +343,12 @@ struct straus_cached_data #endif #endif -std::shared_ptr straus_init_cache(const std::vector &data) +std::shared_ptr straus_init_cache(const std::vector &data, size_t N) { MULTIEXP_PERF(PERF_TIMER_START_UNIT(multiples, 1000000)); + if (N == 0) + N = data.size(); + CHECK_AND_ASSERT_THROW_MES(N <= data.size(), "Bad cache base data"); ge_cached cached; ge_p1p1 p1; ge_p3 p3; @@ -353,9 +356,10 @@ std::shared_ptr straus_init_cache(const std::vectorsize; - cache->multiples = (ge_cached*)aligned_realloc(cache->multiples, sizeof(ge_cached) * ((1<size = data.size(); - for (size_t j=offset;jmultiples = (ge_cached*)aligned_realloc(cache->multiples, sizeof(ge_cached) * ((1<multiples, "Out of memory"); + cache->size = N; + for (size_t j=offset;j straus_init_cache(const std::vectormultiples.size(); - cache->multiples.resize(std::max(offset, data.size())); - for (size_t i = offset; i < data.size(); ++i) + cache->multiples.resize(std::max(offset, N)); + for (size_t i = offset; i < N; ++i) { cache->multiples[i].resize((1<multiples[i][0], &data[i].point); @@ -383,12 +387,12 @@ std::shared_ptr straus_init_cache(const std::vectormultiples.resize(1<multiples[1].size(); - cache->multiples[1].resize(std::max(offset, data.size())); - for (size_t i = offset; i < data.size(); ++i) + cache->multiples[1].resize(std::max(offset, N)); + for (size_t i = offset; i < N; ++i) ge_p3_to_cached(&cache->multiples[1][i], &data[i].point); for (size_t i=2;i<1<multiples[i].resize(std::max(offset, data.size())); - for (size_t j=offset;jmultiples[i].resize(std::max(offset, N)); + for (size_t j=offset;j &cache) rct::key straus(const std::vector &data, const std::shared_ptr &cache, size_t STEP) { + CHECK_AND_ASSERT_THROW_MES(cache == NULL || cache->size >= data.size(), "Cache is too small"); MULTIEXP_PERF(PERF_TIMER_UNIT(straus, 1000000)); bool HiGi = cache != NULL; STEP = STEP ? STEP : 192; @@ -533,7 +538,8 @@ rct::key straus(const std::vector &data, const std::shared_ptr &data, size_t c) +struct pippenger_cached_data { + size_t size; + ge_cached *cached; + pippenger_cached_data(): size(0), cached(NULL) {} + ~pippenger_cached_data() { aligned_free(cached); } +}; + +std::shared_ptr pippenger_init_cache(const std::vector &data, size_t N) +{ + MULTIEXP_PERF(PERF_TIMER_START_UNIT(pippenger_init_cache, 1000000)); + if (N == 0) + N = data.size(); + CHECK_AND_ASSERT_THROW_MES(N <= data.size(), "Bad cache base data"); + ge_cached cached; + std::shared_ptr cache(new pippenger_cached_data()); + + cache->size = N; + cache->cached = (ge_cached*)aligned_realloc(cache->cached, N * sizeof(ge_cached), 4096); + CHECK_AND_ASSERT_THROW_MES(cache->cached, "Out of memory"); + for (size_t i = 0; i < N; ++i) + ge_p3_to_cached(&cache->cached[i], &data[i].point); + + MULTIEXP_PERF(PERF_TIMER_STOP(pippenger_init_cache)); + return cache; +} + +size_t pippenger_get_cache_size(const std::shared_ptr &cache) +{ + return cache->size * sizeof(*cache->cached); +} + +rct::key pippenger(const std::vector &data, const std::shared_ptr &cache, size_t c) +{ + CHECK_AND_ASSERT_THROW_MES(cache == NULL || cache->size >= data.size(), "Cache is too small"); + if (c == 0) + c = get_pippenger_c(data.size()); CHECK_AND_ASSERT_THROW_MES(c <= 9, "c is too large"); ge_p3 result = ge_p3_identity; std::unique_ptr buckets{new ge_p3[1< cached_points{new ge_cached[data.size()]}; - - for (size_t i = 0; i < data.size(); ++i) - { - ge_p3_to_cached(&cached_points[i], &data[i].point); - } + std::shared_ptr local_cache = cache == NULL ? pippenger_init_cache(data) : cache; rct::key maxscalar = rct::zero(); for (size_t i = 0; i < data.size(); ++i) + { if (maxscalar < data[i].scalar) maxscalar = data[i].scalar; + } size_t groups = 0; while (groups < 256 && pow2(groups) < maxscalar) ++groups; @@ -598,7 +636,7 @@ rct::key pippenger(const std::vector &data, size_t c) CHECK_AND_ASSERT_THROW_MES(bucket < (1u<cached[i]); } else buckets[bucket] = data[i].point; diff --git a/src/ringct/multiexp.h b/src/ringct/multiexp.h index 2ed8db27995..559ab664a94 100644 --- a/src/ringct/multiexp.h +++ b/src/ringct/multiexp.h @@ -54,14 +54,17 @@ struct MultiexpData { }; struct straus_cached_data; +struct pippenger_cached_data; rct::key bos_coster_heap_conv(std::vector data); rct::key bos_coster_heap_conv_robust(std::vector data); -std::shared_ptr straus_init_cache(const std::vector &data); +std::shared_ptr straus_init_cache(const std::vector &data, size_t N =0); size_t straus_get_cache_size(const std::shared_ptr &cache); rct::key straus(const std::vector &data, const std::shared_ptr &cache = NULL, size_t STEP = 0); +std::shared_ptr pippenger_init_cache(const std::vector &data, size_t N =0); +size_t pippenger_get_cache_size(const std::shared_ptr &cache); size_t get_pippenger_c(size_t N); -rct::key pippenger(const std::vector &data, size_t c); +rct::key pippenger(const std::vector &data, const std::shared_ptr &cache = NULL, size_t c = 0); } diff --git a/tests/performance_tests/main.cpp b/tests/performance_tests/main.cpp index de9f0e647a5..142e544dd5e 100644 --- a/tests/performance_tests/main.cpp +++ b/tests/performance_tests/main.cpp @@ -281,16 +281,137 @@ int main(int argc, char** argv) TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 2, 1); TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 4, 2); TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 8, 2); - TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 16, 4); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 16, 3); TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 32, 4); - TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 64, 5); - TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 128, 6); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 64, 4); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 128, 5); TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 256, 6); - TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 512, 6); - TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 1024, 8); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 512, 7); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 1024, 7); TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 2048, 8); TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 4096, 9); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 2, 1); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 4, 2); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 8, 2); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 16, 3); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 32, 4); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 64, 4); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 128, 5); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 256, 6); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 512, 7); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 1024, 7); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 2048, 8); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 4096, 9); #else + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 2, 1); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 2, 2); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 2, 3); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 2, 4); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 2, 5); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 2, 6); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 2, 7); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 2, 8); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 2, 9); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 4, 1); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 4, 2); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 4, 3); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 4, 4); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 4, 5); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 4, 6); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 4, 7); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 4, 8); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 4, 9); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 8, 1); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 8, 2); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 8, 3); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 8, 4); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 8, 5); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 8, 6); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 8, 7); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 8, 8); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 8, 9); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 16, 1); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 16, 2); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 16, 3); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 16, 4); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 16, 5); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 16, 6); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 16, 7); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 16, 8); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 16, 9); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 32, 1); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 32, 2); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 32, 3); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 32, 4); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 32, 5); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 32, 6); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 32, 7); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 32, 8); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 32, 9); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 64, 1); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 64, 2); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 64, 3); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 64, 4); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 64, 5); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 64, 6); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 64, 7); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 64, 8); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 64, 9); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 128, 1); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 128, 2); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 128, 3); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 128, 4); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 128, 5); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 128, 6); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 128, 7); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 128, 8); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 128, 9); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 256, 1); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 256, 2); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 256, 3); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 256, 4); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 256, 5); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 256, 6); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 256, 7); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 256, 8); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 256, 9); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 512, 1); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 512, 2); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 512, 3); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 512, 4); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 512, 5); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 512, 6); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 512, 7); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 512, 8); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 512, 9); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 1024, 1); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 1024, 2); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 1024, 3); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 1024, 4); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 1024, 5); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 1024, 6); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 1024, 7); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 1024, 8); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 1024, 9); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 2048, 1); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 2048, 2); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 2048, 3); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 2048, 4); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 2048, 5); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 2048, 6); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 2048, 7); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 2048, 8); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 2048, 9); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 4096, 1); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 4096, 2); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 4096, 3); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 4096, 4); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 4096, 5); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 4096, 6); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 4096, 7); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 4096, 8); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 4096, 9); + TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 2, 1); TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 2, 2); TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 2, 3); diff --git a/tests/performance_tests/multiexp.h b/tests/performance_tests/multiexp.h index 294e7411637..b8b87b3a68c 100644 --- a/tests/performance_tests/multiexp.h +++ b/tests/performance_tests/multiexp.h @@ -40,6 +40,7 @@ enum test_multiexp_algorithm multiexp_straus, multiexp_straus_cached, multiexp_pippenger, + multiexp_pippenger_cached, }; template @@ -61,7 +62,8 @@ class test_multiexp rct::key kn = rct::scalarmultKey(point, data[n].scalar); res = rct::addKeys(res, kn); } - cache = rct::straus_init_cache(data); + straus_cache = rct::straus_init_cache(data); + pippenger_cache = rct::pippenger_init_cache(data); return true; } @@ -74,9 +76,11 @@ class test_multiexp case multiexp_straus: return res == straus(data); case multiexp_straus_cached: - return res == straus(data, cache); + return res == straus(data, straus_cache); case multiexp_pippenger: - return res == pippenger(data, c); + return res == pippenger(data, NULL, c); + case multiexp_pippenger_cached: + return res == pippenger(data, pippenger_cache, c); default: return false; } @@ -84,6 +88,7 @@ class test_multiexp private: std::vector data; - std::shared_ptr cache; + std::shared_ptr straus_cache; + std::shared_ptr pippenger_cache; rct::key res; }; From 15697177183db06b31a73d45a4e8406eaaa4cf5b Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 20 Jun 2018 16:54:13 +0100 Subject: [PATCH 0366/1404] bulletproofs: speed up a few multiplies using existing Hi cache --- src/ringct/bulletproofs.cc | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/ringct/bulletproofs.cc b/src/ringct/bulletproofs.cc index 4eb6d6d5b9d..bc7e15f3572 100644 --- a/src/ringct/bulletproofs.cc +++ b/src/ringct/bulletproofs.cc @@ -111,6 +111,15 @@ static void addKeys_acc_p3(ge_p3 *acc_p3, const rct::key &a, const rct::key &poi ge_p1p1_to_p3(acc_p3, &p1); } +static rct::key scalarmultKey(const ge_p3 &P, const rct::key &a) +{ + ge_p2 R; + ge_scalarmult(&R, a.bytes, &P); + rct::key aP; + ge_tobytes(aP.bytes, &R); + return aP; +} + static rct::key get_exponent(const rct::key &base, size_t idx) { static const std::string salt("bulletproof"); @@ -578,7 +587,7 @@ Bulletproof bulletproof_PROVE(const rct::key &sv, const rct::key &gamma) for (size_t i = 0; i < N; ++i) { Gprime[i] = Gi[i]; - Hprime[i] = scalarmultKey(Hi[i], yinvpow); + Hprime[i] = scalarmultKey(Hi_p3[i], yinvpow); sc_mul(yinvpow.bytes, yinvpow.bytes, yinv.bytes); aprime[i] = l[i]; bprime[i] = r[i]; @@ -828,7 +837,7 @@ Bulletproof bulletproof_PROVE(const rct::keyV &sv, const rct::keyV &gamma) for (size_t i = 0; i < MN; ++i) { Gprime[i] = Gi[i]; - Hprime[i] = scalarmultKey(Hi[i], yinvpow); + Hprime[i] = scalarmultKey(Hi_p3[i], yinvpow); sc_mul(yinvpow.bytes, yinvpow.bytes, yinv.bytes); aprime[i] = l[i]; bprime[i] = r[i]; From c42917624849daeac0b4bc2fb1cd1f2539470b28 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 29 Jun 2018 15:03:00 +0100 Subject: [PATCH 0367/1404] bulletproofs: reject points not in the main subgroup --- src/ringct/bulletproofs.cc | 13 ++++++ src/ringct/rctOps.cpp | 17 ++++++++ src/ringct/rctOps.h | 3 ++ tests/performance_tests/crypto_ops.h | 2 + tests/performance_tests/main.cpp | 1 + tests/unit_tests/bulletproofs.cpp | 61 ++++++++++++++++++++++++++++ 6 files changed, 97 insertions(+) diff --git a/src/ringct/bulletproofs.cc b/src/ringct/bulletproofs.cc index bc7e15f3572..7ec87378bf7 100644 --- a/src/ringct/bulletproofs.cc +++ b/src/ringct/bulletproofs.cc @@ -922,6 +922,19 @@ bool bulletproof_VERIFY(const std::vector &proofs) for (const Bulletproof *p: proofs) { const Bulletproof &proof = *p; + + // check subgroup + for (const rct::key &k: proof.V) + CHECK_AND_ASSERT_MES(rct::isInMainSubgroup(k), false, "Input point not in subgroup"); + for (const rct::key &k: proof.L) + CHECK_AND_ASSERT_MES(rct::isInMainSubgroup(k), false, "Input point not in subgroup"); + for (const rct::key &k: proof.R) + CHECK_AND_ASSERT_MES(rct::isInMainSubgroup(k), false, "Input point not in subgroup"); + CHECK_AND_ASSERT_MES(rct::isInMainSubgroup(proof.A), false, "Input point not in subgroup"); + CHECK_AND_ASSERT_MES(rct::isInMainSubgroup(proof.S), false, "Input point not in subgroup"); + CHECK_AND_ASSERT_MES(rct::isInMainSubgroup(proof.T1), false, "Input point not in subgroup"); + CHECK_AND_ASSERT_MES(rct::isInMainSubgroup(proof.T2), false, "Input point not in subgroup"); + CHECK_AND_ASSERT_MES(proof.V.size() >= 1, false, "V does not have at least one element"); CHECK_AND_ASSERT_MES(proof.L.size() == proof.R.size(), false, "Mismatched L and R sizes"); CHECK_AND_ASSERT_MES(proof.L.size() > 0, false, "Empty proof"); diff --git a/src/ringct/rctOps.cpp b/src/ringct/rctOps.cpp index fe0ad87472c..df027f4b677 100644 --- a/src/ringct/rctOps.cpp +++ b/src/ringct/rctOps.cpp @@ -60,6 +60,17 @@ namespace rct { //Various key generation functions + bool toPointCheckOrder(ge_p3 *P, const unsigned char *data) + { + if (ge_frombytes_vartime(P, data)) + return false; + ge_p2 R; + ge_scalarmult(&R, curveOrder().bytes, P); + key tmp; + ge_tobytes(tmp.bytes, &R); + return tmp == identity(); + } + //generates a random scalar which can be used as a secret key or mask void skGen(key &sk) { random32_unbiased(sk.bytes); @@ -200,6 +211,12 @@ namespace rct { return aP; } + //Computes aL where L is the curve order + bool isInMainSubgroup(const key & a) { + ge_p3 p3; + return toPointCheckOrder(&p3, a.bytes); + } + //Curve addition / subtractions //for curve points: AB = A + B diff --git a/src/ringct/rctOps.h b/src/ringct/rctOps.h index f8889af5c7d..f0320f33329 100644 --- a/src/ringct/rctOps.h +++ b/src/ringct/rctOps.h @@ -83,6 +83,7 @@ namespace rct { keyM keyMInit(size_t rows, size_t cols); //Various key generation functions + bool toPointCheckOrder(ge_p3 *P, const unsigned char *data); //generates a random scalar which can be used as a secret key or mask key skGen(); @@ -119,6 +120,8 @@ namespace rct { key scalarmultKey(const key &P, const key &a); //Computes aH where H= toPoint(cn_fast_hash(G)), G the basepoint key scalarmultH(const key & a); + // checks a is in the main subgroup (ie, not a small one) + bool isInMainSubgroup(const key & a); //Curve addition / subtractions diff --git a/tests/performance_tests/crypto_ops.h b/tests/performance_tests/crypto_ops.h index 852a6670d9b..f52faa70866 100644 --- a/tests/performance_tests/crypto_ops.h +++ b/tests/performance_tests/crypto_ops.h @@ -51,6 +51,7 @@ enum test_op op_addKeys2, op_addKeys3, op_addKeys3_2, + op_isInMainSubgroup, }; template @@ -102,6 +103,7 @@ class test_crypto_ops case op_addKeys2: rct::addKeys2(key, scalar0, scalar1, point0); break; case op_addKeys3: rct::addKeys3(key, scalar0, point0, scalar1, precomp1); break; case op_addKeys3_2: rct::addKeys3(key, scalar0, precomp0, scalar1, precomp1); break; + case op_isInMainSubgroup: rct::isInMainSubgroup(point0); break; default: return false; } return true; diff --git a/tests/performance_tests/main.cpp b/tests/performance_tests/main.cpp index 142e544dd5e..19c2cb5e5e4 100644 --- a/tests/performance_tests/main.cpp +++ b/tests/performance_tests/main.cpp @@ -237,6 +237,7 @@ int main(int argc, char** argv) TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_addKeys2); TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_addKeys3); TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_addKeys3_2); + TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_isInMainSubgroup); TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_bos_coster, 2); TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_bos_coster, 4); diff --git a/tests/unit_tests/bulletproofs.cpp b/tests/unit_tests/bulletproofs.cpp index f163f7a2068..2358aa27a84 100644 --- a/tests/unit_tests/bulletproofs.cpp +++ b/tests/unit_tests/bulletproofs.cpp @@ -30,6 +30,7 @@ #include "gtest/gtest.h" +#include "string_tools.h" #include "ringct/rctOps.h" #include "ringct/rctSigs.h" #include "ringct/bulletproofs.h" @@ -193,3 +194,63 @@ TEST(bulletproofs, invalid_gamma_ff) rct::Bulletproof proof = bulletproof_PROVE(invalid_amount, gamma); ASSERT_FALSE(rct::bulletproof_VERIFY(proof)); } + +static const char * const torsion_elements[] = +{ + "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa", + "0000000000000000000000000000000000000000000000000000000000000000", + "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc85", + "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05", + "0000000000000000000000000000000000000000000000000000000000000080", + "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a", +}; + +TEST(bulletproofs, invalid_torsion) +{ + rct::Bulletproof proof = bulletproof_PROVE(7329838943733, rct::skGen()); + ASSERT_TRUE(rct::bulletproof_VERIFY(proof)); + for (const auto &xs: torsion_elements) + { + rct::key x; + ASSERT_TRUE(epee::string_tools::hex_to_pod(xs, x)); + ASSERT_FALSE(rct::isInMainSubgroup(x)); + for (auto &k: proof.V) + { + const rct::key org_k = k; + rct::addKeys(k, org_k, x); + ASSERT_FALSE(rct::bulletproof_VERIFY(proof)); + k = org_k; + } + for (auto &k: proof.L) + { + const rct::key org_k = k; + rct::addKeys(k, org_k, x); + ASSERT_FALSE(rct::bulletproof_VERIFY(proof)); + k = org_k; + } + for (auto &k: proof.R) + { + const rct::key org_k = k; + rct::addKeys(k, org_k, x); + ASSERT_FALSE(rct::bulletproof_VERIFY(proof)); + k = org_k; + } + const rct::key org_A = proof.A; + rct::addKeys(proof.A, org_A, x); + ASSERT_FALSE(rct::bulletproof_VERIFY(proof)); + proof.A = org_A; + const rct::key org_S = proof.S; + rct::addKeys(proof.S, org_S, x); + ASSERT_FALSE(rct::bulletproof_VERIFY(proof)); + proof.S = org_S; + const rct::key org_T1 = proof.T1; + rct::addKeys(proof.T1, org_T1, x); + ASSERT_FALSE(rct::bulletproof_VERIFY(proof)); + proof.T1 = org_T1; + const rct::key org_T2 = proof.T2; + rct::addKeys(proof.T2, org_T2, x); + ASSERT_FALSE(rct::bulletproof_VERIFY(proof)); + proof.T2 = org_T2; + } +} From 869b3bf824387ae50ab28be9bce66caae21bcae9 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 16 Jul 2018 14:40:51 +0100 Subject: [PATCH 0368/1404] bulletproofs: a few fixes from the Kudelski review - fix integer overflow in n_bulletproof_amounts - check input scalars are in range - remove use of environment variable to tweak straus performance - do not use implementation defined signed shift for signum --- src/crypto/crypto-ops.c | 3 +-- src/ringct/bulletproofs.cc | 23 ++++++++++++++++++++--- src/ringct/rctTypes.cpp | 2 ++ tests/unit_tests/bulletproofs.cpp | 10 ---------- 4 files changed, 23 insertions(+), 15 deletions(-) diff --git a/src/crypto/crypto-ops.c b/src/crypto/crypto-ops.c index 45d412ac690..c1fff1d442f 100644 --- a/src/crypto/crypto-ops.c +++ b/src/crypto/crypto-ops.c @@ -3707,9 +3707,8 @@ void sc_muladd(unsigned char *s, const unsigned char *a, const unsigned char *b, s[31] = s11 >> 17; } -/* Assumes that a != INT64_MIN */ static int64_t signum(int64_t a) { - return (a >> 63) - ((-a) >> 63); + return a > 0 ? 1 : a < 0 ? -1 : 0; } int sc_check(const unsigned char *s) { diff --git a/src/ringct/bulletproofs.cc b/src/ringct/bulletproofs.cc index 7ec87378bf7..d3822c454d0 100644 --- a/src/ringct/bulletproofs.cc +++ b/src/ringct/bulletproofs.cc @@ -74,14 +74,20 @@ static boost::mutex init_mutex; static inline rct::key multiexp(const std::vector &data, bool HiGi) { - static const size_t STEP = getenv("STRAUS_STEP") ? atoi(getenv("STRAUS_STEP")) : 0; if (HiGi) { static_assert(128 <= STRAUS_SIZE_LIMIT, "Straus in precalc mode can only be calculated till STRAUS_SIZE_LIMIT"); - return data.size() <= 128 ? straus(data, straus_HiGi_cache, STEP) : pippenger(data, pippenger_HiGi_cache, get_pippenger_c(data.size())); + return data.size() <= 128 ? straus(data, straus_HiGi_cache, 0) : pippenger(data, pippenger_HiGi_cache, get_pippenger_c(data.size())); } else - return data.size() <= 64 ? straus(data, NULL, STEP) : pippenger(data, NULL, get_pippenger_c(data.size())); + return data.size() <= 64 ? straus(data, NULL, 0) : pippenger(data, NULL, get_pippenger_c(data.size())); +} + +static bool is_reduced(const rct::key &scalar) +{ + rct::key reduced = scalar; + sc_reduce32(reduced.bytes); + return scalar == reduced; } //addKeys3acc_p3 @@ -659,6 +665,10 @@ Bulletproof bulletproof_PROVE(const rct::keyV &sv, const rct::keyV &gamma) { CHECK_AND_ASSERT_THROW_MES(sv.size() == gamma.size(), "Incompatible sizes of sv and gamma"); CHECK_AND_ASSERT_THROW_MES(!sv.empty(), "sv is empty"); + for (const rct::key &sve: sv) + CHECK_AND_ASSERT_THROW_MES(is_reduced(sve), "Invalid sv input"); + for (const rct::key &g: gamma) + CHECK_AND_ASSERT_THROW_MES(is_reduced(g), "Invalid gamma input"); init_exponents(); @@ -935,6 +945,13 @@ bool bulletproof_VERIFY(const std::vector &proofs) CHECK_AND_ASSERT_MES(rct::isInMainSubgroup(proof.T1), false, "Input point not in subgroup"); CHECK_AND_ASSERT_MES(rct::isInMainSubgroup(proof.T2), false, "Input point not in subgroup"); + // check scalar range + CHECK_AND_ASSERT_MES(is_reduced(proof.taux), false, "Input scalar not in range"); + CHECK_AND_ASSERT_MES(is_reduced(proof.mu), false, "Input scalar not in range"); + CHECK_AND_ASSERT_MES(is_reduced(proof.a), false, "Input scalar not in range"); + CHECK_AND_ASSERT_MES(is_reduced(proof.b), false, "Input scalar not in range"); + CHECK_AND_ASSERT_MES(is_reduced(proof.t), false, "Input scalar not in range"); + CHECK_AND_ASSERT_MES(proof.V.size() >= 1, false, "V does not have at least one element"); CHECK_AND_ASSERT_MES(proof.L.size() == proof.R.size(), false, "Mismatched L and R sizes"); CHECK_AND_ASSERT_MES(proof.L.size() > 0, false, "Empty proof"); diff --git a/src/ringct/rctTypes.cpp b/src/ringct/rctTypes.cpp index e67637af6cb..5dd59aec38a 100644 --- a/src/ringct/rctTypes.cpp +++ b/src/ringct/rctTypes.cpp @@ -236,6 +236,7 @@ namespace rct { size_t n_bulletproof_amounts(const Bulletproof &proof) { CHECK_AND_ASSERT_MES(proof.L.size() >= 6, 0, "Invalid bulletproof L size"); + CHECK_AND_ASSERT_MES(proof.L.size() <= 31, 0, "Insane bulletproof L size"); return 1 << (proof.L.size() - 6); } @@ -245,6 +246,7 @@ namespace rct { for (const Bulletproof &proof: proofs) { size_t n2 = n_bulletproof_amounts(proof); + CHECK_AND_ASSERT_MES(n2 < std::numeric_limits::max() - n, 0, "Invalid number of bulletproofs"); if (n2 == 0) return 0; n += n2; diff --git a/tests/unit_tests/bulletproofs.cpp b/tests/unit_tests/bulletproofs.cpp index 2358aa27a84..e3498207006 100644 --- a/tests/unit_tests/bulletproofs.cpp +++ b/tests/unit_tests/bulletproofs.cpp @@ -185,16 +185,6 @@ TEST(bulletproofs, invalid_gamma_0) ASSERT_FALSE(rct::bulletproof_VERIFY(proof)); } -TEST(bulletproofs, invalid_gamma_ff) -{ - rct::key invalid_amount = rct::zero(); - invalid_amount[8] = 1; - rct::key gamma = rct::zero(); - memset(&gamma, 0xff, sizeof(gamma)); - rct::Bulletproof proof = bulletproof_PROVE(invalid_amount, gamma); - ASSERT_FALSE(rct::bulletproof_VERIFY(proof)); -} - static const char * const torsion_elements[] = { "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa", From 5ffb2ff9b7c301eda5811a939c705f26627c4735 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 18 Jul 2018 22:24:53 +0100 Subject: [PATCH 0369/1404] v8: per byte fee, pad bulletproofs, fixed 11 ring size --- src/blockchain_db/berkeleydb/db_bdb.cpp | 10 +- src/blockchain_db/berkeleydb/db_bdb.h | 6 +- src/blockchain_db/blockchain_db.cpp | 4 +- src/blockchain_db/blockchain_db.h | 20 +- src/blockchain_db/lmdb/db_lmdb.cpp | 33 +- src/blockchain_db/lmdb/db_lmdb.h | 6 +- .../blockchain_import.cpp | 6 +- src/blockchain_utilities/bootstrap_file.cpp | 4 +- .../bootstrap_serialization.h | 4 +- .../cryptonote_basic_impl.cpp | 28 +- src/cryptonote_basic/cryptonote_basic_impl.h | 4 +- .../cryptonote_format_utils.cpp | 58 ++- .../cryptonote_format_utils.h | 2 + src/cryptonote_config.h | 6 +- src/cryptonote_core/blockchain.cpp | 297 +++++++------ src/cryptonote_core/blockchain.h | 77 ++-- src/cryptonote_core/cryptonote_core.cpp | 88 +--- src/cryptonote_core/cryptonote_core.h | 4 +- src/cryptonote_core/cryptonote_tx_utils.cpp | 6 +- src/cryptonote_core/cryptonote_tx_utils.h | 2 +- src/cryptonote_core/tx_pool.cpp | 163 +++---- src/cryptonote_core/tx_pool.h | 46 +- src/daemon/rpc_command_executor.cpp | 27 +- src/ringct/rctSigs.cpp | 15 +- src/ringct/rctTypes.cpp | 47 ++- src/ringct/rctTypes.h | 7 +- src/rpc/core_rpc_server.cpp | 19 +- src/rpc/core_rpc_server.h | 4 +- src/rpc/core_rpc_server_commands_defs.h | 18 +- src/rpc/daemon_handler.cpp | 5 +- src/rpc/message_data_structs.h | 4 + src/serialization/json_object.cpp | 8 + src/simplewallet/simplewallet.cpp | 47 ++- src/wallet/api/wallet.cpp | 1 + src/wallet/node_rpc_proxy.cpp | 66 ++- src/wallet/node_rpc_proxy.h | 14 +- src/wallet/wallet2.cpp | 396 ++++++++++++------ src/wallet/wallet2.h | 24 +- src/wallet/wallet_errors.h | 12 +- src/wallet/wallet_rpc_server.cpp | 1 - src/wallet/wallet_rpc_server_commands_defs.h | 2 - tests/core_tests/block_reward.cpp | 54 +-- tests/core_tests/block_validation.cpp | 4 +- tests/core_tests/bulletproofs.cpp | 146 +++---- tests/core_tests/bulletproofs.h | 35 +- tests/core_tests/chaingen.cpp | 76 ++-- tests/core_tests/chaingen.h | 14 +- tests/core_tests/chaingen_main.cpp | 10 +- tests/performance_tests/check_tx_signature.h | 7 +- tests/performance_tests/main.cpp | 34 +- tests/unit_tests/block_reward.cpp | 84 ++-- tests/unit_tests/blockchain_db.cpp | 2 +- tests/unit_tests/bulletproofs.cpp | 2 +- tests/unit_tests/fee.cpp | 54 +-- tests/unit_tests/hardfork.cpp | 4 +- 55 files changed, 1240 insertions(+), 877 deletions(-) diff --git a/src/blockchain_db/berkeleydb/db_bdb.cpp b/src/blockchain_db/berkeleydb/db_bdb.cpp index f827ab7c32a..6c79120e8ae 100644 --- a/src/blockchain_db/berkeleydb/db_bdb.cpp +++ b/src/blockchain_db/berkeleydb/db_bdb.cpp @@ -224,7 +224,7 @@ struct Dbt_safe : public Dbt namespace cryptonote { -void BlockchainBDB::add_block(const block& blk, const size_t& block_size, const difficulty_type& cumulative_difficulty, const uint64_t& coins_generated, const crypto::hash& blk_hash) +void BlockchainBDB::add_block(const block& blk, size_t block_weight, const difficulty_type& cumulative_difficulty, const uint64_t& coins_generated, const crypto::hash& blk_hash) { LOG_PRINT_L3("BlockchainBDB::" << __func__); check_open(); @@ -255,7 +255,7 @@ void BlockchainBDB::add_block(const block& blk, const size_t& block_size, const if (res) throw0(DB_ERROR("Failed to add block blob to db transaction.")); - Dbt_copy sz(block_size); + Dbt_copy sz(block_weight); if (m_block_sizes->put(DB_DEFAULT_TX, &key, &sz, 0)) throw0(DB_ERROR("Failed to add block size to db transaction.")); @@ -1353,7 +1353,7 @@ uint64_t BlockchainBDB::get_top_block_timestamp() const return get_block_timestamp(m_height - 1); } -size_t BlockchainBDB::get_block_size(const uint64_t& height) const +size_t BlockchainBDB::get_block_weight(const uint64_t& height) const { LOG_PRINT_L3("BlockchainBDB::" << __func__); check_open(); @@ -1861,7 +1861,7 @@ void BlockchainBDB::block_txn_abort() // TODO } -uint64_t BlockchainBDB::add_block(const block& blk, const size_t& block_size, const difficulty_type& cumulative_difficulty, const uint64_t& coins_generated, const std::vector& txs) +uint64_t BlockchainBDB::add_block(const block& blk, size_t block_weight, const difficulty_type& cumulative_difficulty, const uint64_t& coins_generated, const std::vector& txs) { LOG_PRINT_L3("BlockchainBDB::" << __func__); check_open(); @@ -1874,7 +1874,7 @@ uint64_t BlockchainBDB::add_block(const block& blk, const size_t& block_size, co uint64_t num_outputs = m_num_outputs; try { - BlockchainDB::add_block(blk, block_size, cumulative_difficulty, coins_generated, txs); + BlockchainDB::add_block(blk, block_weight, cumulative_difficulty, coins_generated, txs); m_write_txn = NULL; TIME_MEASURE_START(time1); diff --git a/src/blockchain_db/berkeleydb/db_bdb.h b/src/blockchain_db/berkeleydb/db_bdb.h index 8c99ab25582..76d0a05174a 100644 --- a/src/blockchain_db/berkeleydb/db_bdb.h +++ b/src/blockchain_db/berkeleydb/db_bdb.h @@ -266,7 +266,7 @@ class BlockchainBDB : public BlockchainDB virtual uint64_t get_top_block_timestamp() const; - virtual size_t get_block_size(const uint64_t& height) const; + virtual size_t get_block_weight(const uint64_t& height) const; virtual difficulty_type get_block_cumulative_difficulty(const uint64_t& height) const; @@ -318,7 +318,7 @@ class BlockchainBDB : public BlockchainDB virtual bool has_key_image(const crypto::key_image& img) const; virtual uint64_t add_block( const block& blk - , const size_t& block_size + , size_t block_weight , const difficulty_type& cumulative_difficulty , const uint64_t& coins_generated , const std::vector& txs @@ -353,7 +353,7 @@ class BlockchainBDB : public BlockchainDB private: virtual void add_block( const block& blk - , const size_t& block_size + , size_t block_weight , const difficulty_type& cumulative_difficulty , const uint64_t& coins_generated , const crypto::hash& block_hash diff --git a/src/blockchain_db/blockchain_db.cpp b/src/blockchain_db/blockchain_db.cpp index 78d63fdcfab..be0ffeac378 100644 --- a/src/blockchain_db/blockchain_db.cpp +++ b/src/blockchain_db/blockchain_db.cpp @@ -196,7 +196,7 @@ void BlockchainDB::add_transaction(const crypto::hash& blk_hash, const transacti } uint64_t BlockchainDB::add_block( const block& blk - , const size_t& block_size + , size_t block_weight , const difficulty_type& cumulative_difficulty , const uint64_t& coins_generated , const std::vector& txs @@ -241,7 +241,7 @@ uint64_t BlockchainDB::add_block( const block& blk // call out to subclass implementation to add the block & metadata time1 = epee::misc_utils::get_tick_count(); - add_block(blk, block_size, cumulative_difficulty, coins_generated, num_rct_outs, blk_hash); + add_block(blk, block_weight, cumulative_difficulty, coins_generated, num_rct_outs, blk_hash); TIME_MEASURE_FINISH(time1); time_add_block1 += time1; diff --git a/src/blockchain_db/blockchain_db.h b/src/blockchain_db/blockchain_db.h index 4431ca44cc0..396ae754476 100644 --- a/src/blockchain_db/blockchain_db.h +++ b/src/blockchain_db/blockchain_db.h @@ -137,7 +137,7 @@ struct txpool_tx_meta_t { crypto::hash max_used_block_id; crypto::hash last_failed_id; - uint64_t blob_size; + uint64_t weight; uint64_t fee; uint64_t max_used_block_height; uint64_t last_failed_height; @@ -358,13 +358,13 @@ class BlockchainDB * subclass of DB_EXCEPTION * * @param blk the block to be added - * @param block_size the size of the block (transactions and all) + * @param block_weight the weight of the block (transactions and all) * @param cumulative_difficulty the accumulated difficulty after this block * @param coins_generated the number of coins generated total after this block * @param blk_hash the hash of the block */ virtual void add_block( const block& blk - , const size_t& block_size + , size_t block_weight , const difficulty_type& cumulative_difficulty , const uint64_t& coins_generated , uint64_t num_rct_outs @@ -376,7 +376,7 @@ class BlockchainDB * * The subclass implementing this will remove the block data from the top * block in the chain. The data to be removed is that which was added in - * BlockchainDB::add_block(const block& blk, const size_t& block_size, const difficulty_type& cumulative_difficulty, const uint64_t& coins_generated, const crypto::hash& blk_hash) + * BlockchainDB::add_block(const block& blk, size_t block_weight, const difficulty_type& cumulative_difficulty, const uint64_t& coins_generated, const crypto::hash& blk_hash) * * If any of this cannot be done, the subclass should throw the corresponding * subclass of DB_EXCEPTION @@ -789,7 +789,7 @@ class BlockchainDB * subclass of DB_EXCEPTION * * @param blk the block to be added - * @param block_size the size of the block (transactions and all) + * @param block_weight the size of the block (transactions and all) * @param cumulative_difficulty the accumulated difficulty after this block * @param coins_generated the number of coins generated total after this block * @param txs the transactions in the block @@ -797,7 +797,7 @@ class BlockchainDB * @return the height of the chain post-addition */ virtual uint64_t add_block( const block& blk - , const size_t& block_size + , size_t block_weight , const difficulty_type& cumulative_difficulty , const uint64_t& coins_generated , const std::vector& txs @@ -930,18 +930,18 @@ class BlockchainDB virtual uint64_t get_top_block_timestamp() const = 0; /** - * @brief fetch a block's size + * @brief fetch a block's weight * - * The subclass should return the size of the block with the + * The subclass should return the weight of the block with the * given height. * * If the block does not exist, the subclass should throw BLOCK_DNE * * @param height the height requested * - * @return the size + * @return the weight */ - virtual size_t get_block_size(const uint64_t& height) const = 0; + virtual size_t get_block_weight(const uint64_t& height) const = 0; /** * @brief fetch a block's cumulative difficulty diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp index d854cbeaff3..9e22e2e4b07 100644 --- a/src/blockchain_db/lmdb/db_lmdb.cpp +++ b/src/blockchain_db/lmdb/db_lmdb.cpp @@ -256,7 +256,7 @@ typedef struct mdb_block_info_old uint64_t bi_height; uint64_t bi_timestamp; uint64_t bi_coins; - uint64_t bi_size; // a size_t really but we need 32-bit compat + uint64_t bi_weight; // a size_t really but we need 32-bit compat difficulty_type bi_diff; crypto::hash bi_hash; } mdb_block_info_old; @@ -266,7 +266,7 @@ typedef struct mdb_block_info uint64_t bi_height; uint64_t bi_timestamp; uint64_t bi_coins; - uint64_t bi_size; // a size_t really but we need 32-bit compat + uint64_t bi_weight; // a size_t really but we need 32-bit compat difficulty_type bi_diff; crypto::hash bi_hash; uint64_t bi_cum_rct; @@ -652,8 +652,11 @@ uint64_t BlockchainLMDB::get_estimated_batch_size(uint64_t batch_num_blocks, uin block_rtxn_start(&rtxn, &rcurs); for (uint64_t block_num = block_start; block_num <= block_stop; ++block_num) { - uint32_t block_size = get_block_size(block_num); - total_block_size += block_size; + // we have access to block weight, which will be greater or equal to block size, + // so use this as a proxy. If it's too much off, we might have to check actual size, + // which involves reading more data, so is not really wanted + size_t block_weight = get_block_weight(block_num); + total_block_size += block_weight; // Track number of blocks being totalled here instead of assuming, in case // some blocks were to be skipped for being outliers. ++num_blocks_used; @@ -674,7 +677,7 @@ uint64_t BlockchainLMDB::get_estimated_batch_size(uint64_t batch_num_blocks, uin return threshold_size; } -void BlockchainLMDB::add_block(const block& blk, const size_t& block_size, const difficulty_type& cumulative_difficulty, const uint64_t& coins_generated, +void BlockchainLMDB::add_block(const block& blk, size_t block_weight, const difficulty_type& cumulative_difficulty, const uint64_t& coins_generated, uint64_t num_rct_outs, const crypto::hash& blk_hash) { LOG_PRINT_L3("BlockchainLMDB::" << __func__); @@ -720,7 +723,7 @@ void BlockchainLMDB::add_block(const block& blk, const size_t& block_size, const bi.bi_height = m_height; bi.bi_timestamp = blk.timestamp; bi.bi_coins = coins_generated; - bi.bi_size = block_size; + bi.bi_weight = block_weight; bi.bi_diff = cumulative_difficulty; bi.bi_hash = blk_hash; bi.bi_cum_rct = num_rct_outs; @@ -743,7 +746,9 @@ void BlockchainLMDB::add_block(const block& blk, const size_t& block_size, const if (result) throw0(DB_ERROR(lmdb_error("Failed to add block height by hash to db transaction: ", result).c_str())); - m_cum_size += block_size; + // we use weight as a proxy for size, since we don't have size but weight is >= size + // and often actually equal + m_cum_size += block_weight; m_cum_count++; } @@ -2011,7 +2016,7 @@ uint64_t BlockchainLMDB::get_top_block_timestamp() const return get_block_timestamp(m_height - 1); } -size_t BlockchainLMDB::get_block_size(const uint64_t& height) const +size_t BlockchainLMDB::get_block_weight(const uint64_t& height) const { LOG_PRINT_L3("BlockchainLMDB::" << __func__); check_open(); @@ -2029,7 +2034,7 @@ size_t BlockchainLMDB::get_block_size(const uint64_t& height) const throw0(DB_ERROR("Error attempting to retrieve a block size from the db")); mdb_block_info *bi = (mdb_block_info *)result.mv_data; - size_t ret = bi->bi_size; + size_t ret = bi->bi_weight; TXN_POSTFIX_RDONLY(); return ret; } @@ -3090,7 +3095,7 @@ void BlockchainLMDB::block_txn_abort() } } -uint64_t BlockchainLMDB::add_block(const block& blk, const size_t& block_size, const difficulty_type& cumulative_difficulty, const uint64_t& coins_generated, +uint64_t BlockchainLMDB::add_block(const block& blk, size_t block_weight, const difficulty_type& cumulative_difficulty, const uint64_t& coins_generated, const std::vector& txs) { LOG_PRINT_L3("BlockchainLMDB::" << __func__); @@ -3109,7 +3114,7 @@ uint64_t BlockchainLMDB::add_block(const block& blk, const size_t& block_size, c try { - BlockchainDB::add_block(blk, block_size, cumulative_difficulty, coins_generated, txs); + BlockchainDB::add_block(blk, block_weight, cumulative_difficulty, coins_generated, txs); } catch (const DB_ERROR_TXN_START& e) { @@ -3698,9 +3703,9 @@ void BlockchainLMDB::migrate_0_1() if (result) throw0(DB_ERROR(lmdb_error("Failed to get a record from block_sizes: ", result).c_str())); if (v.mv_size == sizeof(uint32_t)) - bi.bi_size = *(uint32_t *)v.mv_data; + bi.bi_weight = *(uint32_t *)v.mv_data; else - bi.bi_size = *(uint64_t *)v.mv_data; // this is a 32/64 compat bug in version 0 + bi.bi_weight = *(uint64_t *)v.mv_data; // this is a 32/64 compat bug in version 0 result = mdb_cursor_get(c_timestamps, &k, &v, MDB_NEXT); if (result) throw0(DB_ERROR(lmdb_error("Failed to get a record from block_timestamps: ", result).c_str())); @@ -4259,7 +4264,7 @@ void BlockchainLMDB::migrate_2_3() bi.bi_height = bi_old->bi_height; bi.bi_timestamp = bi_old->bi_timestamp; bi.bi_coins = bi_old->bi_coins; - bi.bi_size = bi_old->bi_size; + bi.bi_weight = bi_old->bi_weight; bi.bi_diff = bi_old->bi_diff; bi.bi_hash = bi_old->bi_hash; if (bi_old->bi_height >= distribution.size()) diff --git a/src/blockchain_db/lmdb/db_lmdb.h b/src/blockchain_db/lmdb/db_lmdb.h index f700d3178f7..e1f748ed8d2 100644 --- a/src/blockchain_db/lmdb/db_lmdb.h +++ b/src/blockchain_db/lmdb/db_lmdb.h @@ -205,7 +205,7 @@ class BlockchainLMDB : public BlockchainDB virtual uint64_t get_top_block_timestamp() const; - virtual size_t get_block_size(const uint64_t& height) const; + virtual size_t get_block_weight(const uint64_t& height) const; virtual difficulty_type get_block_cumulative_difficulty(const uint64_t& height) const; @@ -273,7 +273,7 @@ class BlockchainLMDB : public BlockchainDB virtual bool for_all_outputs(uint64_t amount, const std::function &f) const; virtual uint64_t add_block( const block& blk - , const size_t& block_size + , size_t block_weight , const difficulty_type& cumulative_difficulty , const uint64_t& coins_generated , const std::vector& txs @@ -317,7 +317,7 @@ class BlockchainLMDB : public BlockchainDB uint64_t get_estimated_batch_size(uint64_t batch_num_blocks, uint64_t batch_bytes) const; virtual void add_block( const block& blk - , const size_t& block_size + , size_t block_weight , const difficulty_type& cumulative_difficulty , const uint64_t& coins_generated , uint64_t num_rct_outs diff --git a/src/blockchain_utilities/blockchain_import.cpp b/src/blockchain_utilities/blockchain_import.cpp index 7291dbd6843..9ec768d2604 100644 --- a/src/blockchain_utilities/blockchain_import.cpp +++ b/src/blockchain_utilities/blockchain_import.cpp @@ -474,17 +474,17 @@ int import_from_file(cryptonote::core& core, const std::string& import_file_path txs.push_back(tx); } - size_t block_size; + size_t block_weight; difficulty_type cumulative_difficulty; uint64_t coins_generated; - block_size = bp.block_size; + block_weight = bp.block_weight; cumulative_difficulty = bp.cumulative_difficulty; coins_generated = bp.coins_generated; try { - core.get_blockchain_storage().get_db().add_block(b, block_size, cumulative_difficulty, coins_generated, txs); + core.get_blockchain_storage().get_db().add_block(b, block_weight, cumulative_difficulty, coins_generated, txs); } catch (const std::exception& e) { diff --git a/src/blockchain_utilities/bootstrap_file.cpp b/src/blockchain_utilities/bootstrap_file.cpp index ba2697226d4..beaad2abc20 100644 --- a/src/blockchain_utilities/bootstrap_file.cpp +++ b/src/blockchain_utilities/bootstrap_file.cpp @@ -236,11 +236,11 @@ void BootstrapFile::write_block(block& block) bool include_extra_block_data = true; if (include_extra_block_data) { - size_t block_size = m_blockchain_storage->get_db().get_block_size(block_height); + size_t block_weight = m_blockchain_storage->get_db().get_block_weight(block_height); difficulty_type cumulative_difficulty = m_blockchain_storage->get_db().get_block_cumulative_difficulty(block_height); uint64_t coins_generated = m_blockchain_storage->get_db().get_block_already_generated_coins(block_height); - bp.block_size = block_size; + bp.block_weight = block_weight; bp.cumulative_difficulty = cumulative_difficulty; bp.coins_generated = coins_generated; } diff --git a/src/blockchain_utilities/bootstrap_serialization.h b/src/blockchain_utilities/bootstrap_serialization.h index 8755d3fe3e2..278a7b40f3b 100644 --- a/src/blockchain_utilities/bootstrap_serialization.h +++ b/src/blockchain_utilities/bootstrap_serialization.h @@ -70,14 +70,14 @@ namespace cryptonote { cryptonote::block block; std::vector txs; - size_t block_size; + size_t block_weight; difficulty_type cumulative_difficulty; uint64_t coins_generated; BEGIN_SERIALIZE() FIELD(block) FIELD(txs) - VARINT_FIELD(block_size) + VARINT_FIELD(block_weight) VARINT_FIELD(cumulative_difficulty) VARINT_FIELD(coins_generated) END_SERIALIZE() diff --git a/src/cryptonote_basic/cryptonote_basic_impl.cpp b/src/cryptonote_basic/cryptonote_basic_impl.cpp index cff23695f3b..b18ef1c5cbf 100644 --- a/src/cryptonote_basic/cryptonote_basic_impl.cpp +++ b/src/cryptonote_basic/cryptonote_basic_impl.cpp @@ -67,7 +67,7 @@ namespace cryptonote { /* Cryptonote helper functions */ /************************************************************************/ //----------------------------------------------------------------------------------------------- - size_t get_min_block_size(uint8_t version) + size_t get_min_block_weight(uint8_t version) { if (version < 2) return CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1; @@ -86,7 +86,7 @@ namespace cryptonote { return CRYPTONOTE_MAX_TX_SIZE; } //----------------------------------------------------------------------------------------------- - bool get_block_reward(size_t median_size, size_t current_block_size, uint64_t already_generated_coins, uint64_t &reward, uint8_t version) { + bool get_block_reward(size_t median_weight, size_t current_block_weight, uint64_t already_generated_coins, uint64_t &reward, uint8_t version) { static_assert(DIFFICULTY_TARGET_V2%60==0&&DIFFICULTY_TARGET_V1%60==0,"difficulty targets must be a multiple of 60"); const int target = version < 2 ? DIFFICULTY_TARGET_V1 : DIFFICULTY_TARGET_V2; const int target_minutes = target / 60; @@ -98,37 +98,37 @@ namespace cryptonote { base_reward = FINAL_SUBSIDY_PER_MINUTE*target_minutes; } - uint64_t full_reward_zone = get_min_block_size(version); + uint64_t full_reward_zone = get_min_block_weight(version); //make it soft - if (median_size < full_reward_zone) { - median_size = full_reward_zone; + if (median_weight < full_reward_zone) { + median_weight = full_reward_zone; } - if (current_block_size <= median_size) { + if (current_block_weight <= median_weight) { reward = base_reward; return true; } - if(current_block_size > 2 * median_size) { - MERROR("Block cumulative size is too big: " << current_block_size << ", expected less than " << 2 * median_size); + if(current_block_weight > 2 * median_weight) { + MERROR("Block cumulative weight is too big: " << current_block_weight << ", expected less than " << 2 * median_weight); return false; } - assert(median_size < std::numeric_limits::max()); - assert(current_block_size < std::numeric_limits::max()); + assert(median_weight < std::numeric_limits::max()); + assert(current_block_weight < std::numeric_limits::max()); uint64_t product_hi; // BUGFIX: 32-bit saturation bug (e.g. ARM7), the result was being // treated as 32-bit by default. - uint64_t multiplicand = 2 * median_size - current_block_size; - multiplicand *= current_block_size; + uint64_t multiplicand = 2 * median_weight - current_block_weight; + multiplicand *= current_block_weight; uint64_t product_lo = mul128(base_reward, multiplicand, &product_hi); uint64_t reward_hi; uint64_t reward_lo; - div128_32(product_hi, product_lo, static_cast(median_size), &reward_hi, &reward_lo); - div128_32(reward_hi, reward_lo, static_cast(median_size), &reward_hi, &reward_lo); + div128_32(product_hi, product_lo, static_cast(median_weight), &reward_hi, &reward_lo); + div128_32(reward_hi, reward_lo, static_cast(median_weight), &reward_hi, &reward_lo); assert(0 == reward_hi); assert(reward_lo < base_reward); diff --git a/src/cryptonote_basic/cryptonote_basic_impl.h b/src/cryptonote_basic/cryptonote_basic_impl.h index f597850214a..c804a88fa55 100644 --- a/src/cryptonote_basic/cryptonote_basic_impl.h +++ b/src/cryptonote_basic/cryptonote_basic_impl.h @@ -86,10 +86,10 @@ namespace cryptonote { /************************************************************************/ /* Cryptonote helper functions */ /************************************************************************/ - size_t get_min_block_size(uint8_t version); + size_t get_min_block_weight(uint8_t version); size_t get_max_block_size(); size_t get_max_tx_size(); - bool get_block_reward(size_t median_size, size_t current_block_size, uint64_t already_generated_coins, uint64_t &reward, uint8_t version); + bool get_block_reward(size_t median_weight, size_t current_block_weight, uint64_t already_generated_coins, uint64_t &reward, uint8_t version); uint8_t get_account_address_checksum(const public_address_outer_blob& bl); uint8_t get_account_integrated_address_checksum(const public_integrated_address_outer_blob& bl); diff --git a/src/cryptonote_basic/cryptonote_format_utils.cpp b/src/cryptonote_basic/cryptonote_format_utils.cpp index 524cbf7aee2..f1a5c4d942f 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.cpp +++ b/src/cryptonote_basic/cryptonote_format_utils.cpp @@ -142,24 +142,27 @@ namespace cryptonote const bool bulletproof = rct::is_rct_bulletproof(rv.type); if (bulletproof) { - if (rct::n_bulletproof_amounts(rv.p.bulletproofs) != tx.vout.size()) + if (rv.p.bulletproofs.size() != 1) { LOG_PRINT_L1("Failed to parse transaction from blob, bad bulletproofs size in tx " << get_transaction_hash(tx)); return false; } - size_t idx = 0; - for (size_t n = 0; n < rv.p.bulletproofs.size(); ++n) + if (rv.p.bulletproofs[0].L.size() < 6) { - //rv.p.bulletproofs[n].V.resize(1); - //rv.p.bulletproofs[n].V[0] = rv.outPk[n].mask; - CHECK_AND_ASSERT_MES(rv.p.bulletproofs[n].L.size() >= 6, false, "Bad bulletproofs L size"); // at least 64 bits - const size_t n_amounts = rct::n_bulletproof_amounts(rv.p.bulletproofs[n]); - CHECK_AND_ASSERT_MES(idx + n_amounts <= rv.outPk.size(), false, "Internal error filling out V"); - rv.p.bulletproofs[n].V.resize(n_amounts); - rv.p.bulletproofs[n].V.clear(); - for (size_t i = 0; i < n_amounts; ++i) - rv.p.bulletproofs[n].V[i] = rv.outPk[idx++].mask; + LOG_PRINT_L1("Failed to parse transaction from blob, bad bulletproofs L size in tx " << get_transaction_hash(tx)); + return false; + } + const size_t max_outputs = 1 << (rv.p.bulletproofs[0].L.size() - 6); + if (max_outputs < tx.vout.size()) + { + LOG_PRINT_L1("Failed to parse transaction from blob, bad bulletproofs max outputs in tx " << get_transaction_hash(tx)); + return false; } + const size_t n_amounts = tx.vout.size(); + CHECK_AND_ASSERT_MES(n_amounts == rv.outPk.size(), false, "Internal error filling out V"); + rv.p.bulletproofs[0].V.resize(n_amounts); + for (size_t i = 0; i < n_amounts; ++i) + rv.p.bulletproofs[0].V[i] = rv.outPk[i].mask; } } } @@ -326,6 +329,37 @@ namespace cryptonote return string_tools::get_xtype_from_string(amount, str_amount); } //--------------------------------------------------------------- + uint64_t get_transaction_weight(const transaction &tx, size_t blob_size) + { + if (tx.version < 2) + return blob_size; + const rct::rctSig &rv = tx.rct_signatures; + if (!rct::is_rct_bulletproof(rv.type)) + return blob_size; + const size_t n_outputs = tx.vout.size(); + if (n_outputs <= 2) + return blob_size; + const uint64_t bp_base = 368; + const size_t n_padded_outputs = rct::n_bulletproof_max_amounts(rv.p.bulletproofs); + size_t nlr = 0; + for (const auto &bp: rv.p.bulletproofs) + nlr += bp.L.size() * 2; + const size_t bp_size = 32 * (9 + nlr); + CHECK_AND_ASSERT_THROW_MES_L1(bp_base * n_padded_outputs >= bp_size, "Invalid bulletproof clawback"); + const uint64_t bp_clawback = (bp_base * n_padded_outputs - bp_size) * 4 / 5; + CHECK_AND_ASSERT_THROW_MES_L1(bp_clawback <= std::numeric_limits::max() - blob_size, "Weight overflow"); + return blob_size + bp_clawback; + } + //--------------------------------------------------------------- + uint64_t get_transaction_weight(const transaction &tx) + { + std::ostringstream s; + binary_archive a(s); + ::serialization::serialize(a, const_cast(tx)); + const cryptonote::blobdata blob = s.str(); + return get_transaction_weight(tx, blob.size()); + } + //--------------------------------------------------------------- bool get_tx_fee(const transaction& tx, uint64_t & fee) { if (tx.version > 1) diff --git a/src/cryptonote_basic/cryptonote_format_utils.h b/src/cryptonote_basic/cryptonote_format_utils.h index 8a5296d5bc5..bf71eb59189 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.h +++ b/src/cryptonote_basic/cryptonote_format_utils.h @@ -117,6 +117,8 @@ namespace cryptonote bool check_inputs_types_supported(const transaction& tx); bool check_outs_valid(const transaction& tx); bool parse_amount(uint64_t& amount, const std::string& str_amount); + uint64_t get_transaction_weight(const transaction &tx); + uint64_t get_transaction_weight(const transaction &tx, size_t blob_size); bool check_money_overflow(const transaction& tx); bool check_outs_overflow(const transaction& tx); diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index 00bc2f27574..a6858ce7cce 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -65,9 +65,11 @@ #define FEE_PER_KB_OLD ((uint64_t)10000000000) // pow(10, 10) #define FEE_PER_KB ((uint64_t)2000000000) // 2 * pow(10, 9) +#define FEE_PER_BYTE ((uint64_t)300000) #define DYNAMIC_FEE_PER_KB_BASE_FEE ((uint64_t)2000000000) // 2 * pow(10,9) #define DYNAMIC_FEE_PER_KB_BASE_BLOCK_REWARD ((uint64_t)10000000000000) // 10 * pow(10,12) #define DYNAMIC_FEE_PER_KB_BASE_FEE_V5 ((uint64_t)2000000000 * (uint64_t)CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 / CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5) +#define DYNAMIC_FEE_REFERENCE_TRANSACTION_WEIGHT ((uint64_t)3000) #define ORPHANED_BLOCKS_MAX_COUNT 100 @@ -133,13 +135,15 @@ #define HF_VERSION_DYNAMIC_FEE 4 #define HF_VERSION_MIN_MIXIN_4 6 #define HF_VERSION_MIN_MIXIN_6 7 +#define HF_VERSION_MIN_MIXIN_10 8 #define HF_VERSION_ENFORCE_RCT 6 +#define HF_VERSION_PER_BYTE_FEE 8 #define PER_KB_FEE_QUANTIZATION_DECIMALS 8 #define HASH_OF_HASHES_STEP 256 -#define DEFAULT_TXPOOL_MAX_SIZE 648000000ull // 3 days at 300000, in bytes +#define DEFAULT_TXPOOL_MAX_WEIGHT 648000000ull // 3 days at 300000, in bytes #define BULLETPROOF_MAX_OUTPUTS 16 diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 9beb28fbdd1..6fe4a287ff3 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -155,7 +155,7 @@ static const struct { //------------------------------------------------------------------ Blockchain::Blockchain(tx_memory_pool& tx_pool) : - m_db(), m_tx_pool(tx_pool), m_hardfork(NULL), m_timestamps_and_difficulties_height(0), m_current_block_cumul_sz_limit(0), m_current_block_cumul_sz_median(0), + m_db(), m_tx_pool(tx_pool), m_hardfork(NULL), m_timestamps_and_difficulties_height(0), m_current_block_cumul_weight_limit(0), m_current_block_cumul_weight_median(0), m_enforce_dns_checkpoints(false), m_max_prepare_blocks_threads(4), m_db_sync_on_blocks(true), m_db_sync_threshold(1), m_db_sync_mode(db_async), m_db_default_sync(false), m_fast_sync(true), m_show_time_stats(false), m_sync_counter(0), m_bytes_to_sync(0), m_cancel(false), m_difficulty_for_next_block_top_hash(crypto::null_hash), m_difficulty_for_next_block(1), @@ -482,7 +482,7 @@ bool Blockchain::init(BlockchainDB* db, const network_type nettype, bool offline m_tx_pool.on_blockchain_dec(m_db->height()-1, get_tail_id()); } - update_next_cumulative_size_limit(); + update_next_cumulative_weight_limit(); return true; } //------------------------------------------------------------------ @@ -631,7 +631,7 @@ block Blockchain::pop_block_from_blockchain() m_blocks_txs_check.clear(); m_check_txin_table.clear(); - update_next_cumulative_size_limit(); + update_next_cumulative_weight_limit(); m_tx_pool.on_blockchain_dec(m_db->height()-1, get_tail_id()); invalidate_block_template_cache(); @@ -650,7 +650,7 @@ bool Blockchain::reset_and_set_genesis_block(const block& b) block_verification_context bvc = boost::value_initialized(); add_new_block(b, bvc); - update_next_cumulative_size_limit(); + update_next_cumulative_weight_limit(); return bvc.m_added_to_main_chain && !bvc.m_verifivation_failed; } //------------------------------------------------------------------ @@ -1113,7 +1113,7 @@ bool Blockchain::prevalidate_miner_transaction(const block& b, uint64_t height) } //------------------------------------------------------------------ // This function validates the miner transaction reward -bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_block_size, uint64_t fee, uint64_t& base_reward, uint64_t already_generated_coins, bool &partial_block_reward, uint8_t version) +bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_block_weight, uint64_t fee, uint64_t& base_reward, uint64_t already_generated_coins, bool &partial_block_reward, uint8_t version) { LOG_PRINT_L3("Blockchain::" << __func__); //validate reward @@ -1131,11 +1131,11 @@ bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_bl } } - std::vector last_blocks_sizes; - get_last_n_blocks_sizes(last_blocks_sizes, CRYPTONOTE_REWARD_BLOCKS_WINDOW); - if (!get_block_reward(epee::misc_utils::median(last_blocks_sizes), cumulative_block_size, already_generated_coins, base_reward, version)) + std::vector last_blocks_weights; + get_last_n_blocks_weights(last_blocks_weights, CRYPTONOTE_REWARD_BLOCKS_WINDOW); + if (!get_block_reward(epee::misc_utils::median(last_blocks_weights), cumulative_block_weight, already_generated_coins, base_reward, version)) { - MERROR_VER("block size " << cumulative_block_size << " is bigger than allowed for this blockchain"); + MERROR_VER("block weight " << cumulative_block_weight << " is bigger than allowed for this blockchain"); return false; } if(base_reward + fee < money_in_use) @@ -1165,8 +1165,8 @@ bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_bl return true; } //------------------------------------------------------------------ -// get the block sizes of the last blocks, and return by reference . -void Blockchain::get_last_n_blocks_sizes(std::vector& sz, size_t count) const +// get the block weights of the last blocks, and return by reference . +void Blockchain::get_last_n_blocks_weights(std::vector& weights, size_t count) const { LOG_PRINT_L3("Blockchain::" << __func__); CRITICAL_REGION_LOCAL(m_blockchain_lock); @@ -1177,26 +1177,26 @@ void Blockchain::get_last_n_blocks_sizes(std::vector& sz, size_t count) return; m_db->block_txn_start(true); - // add size of last blocks to vector (or less, if blockchain size < count) + // add weight of last blocks to vector (or less, if blockchain size < count) size_t start_offset = h - std::min(h, count); - sz.reserve(sz.size() + h - start_offset); + weights.reserve(weights.size() + h - start_offset); for(size_t i = start_offset; i < h; i++) { - sz.push_back(m_db->get_block_size(i)); + weights.push_back(m_db->get_block_weight(i)); } m_db->block_txn_stop(); } //------------------------------------------------------------------ -uint64_t Blockchain::get_current_cumulative_blocksize_limit() const +uint64_t Blockchain::get_current_cumulative_block_weight_limit() const { LOG_PRINT_L3("Blockchain::" << __func__); - return m_current_block_cumul_sz_limit; + return m_current_block_cumul_weight_limit; } //------------------------------------------------------------------ -uint64_t Blockchain::get_current_cumulative_blocksize_median() const +uint64_t Blockchain::get_current_cumulative_block_weight_median() const { LOG_PRINT_L3("Blockchain::" << __func__); - return m_current_block_cumul_sz_median; + return m_current_block_cumul_weight_median; } //------------------------------------------------------------------ //TODO: This function only needed minor modification to work with BlockchainDB, @@ -1213,7 +1213,7 @@ uint64_t Blockchain::get_current_cumulative_blocksize_median() const bool Blockchain::create_block_template(block& b, const account_public_address& miner_address, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce) { LOG_PRINT_L3("Blockchain::" << __func__); - size_t median_size; + size_t median_weight; uint64_t already_generated_coins; uint64_t pool_cookie; @@ -1250,20 +1250,20 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m diffic = get_difficulty_for_next_block(); CHECK_AND_ASSERT_MES(diffic, false, "difficulty overhead."); - median_size = m_current_block_cumul_sz_limit / 2; + median_weight = m_current_block_cumul_weight_limit / 2; already_generated_coins = m_db->get_block_already_generated_coins(height - 1); CRITICAL_REGION_END(); - size_t txs_size; + size_t txs_weight; uint64_t fee; - if (!m_tx_pool.fill_block_template(b, median_size, already_generated_coins, txs_size, fee, expected_reward, m_hardfork->get_current_version())) + if (!m_tx_pool.fill_block_template(b, median_weight, already_generated_coins, txs_weight, fee, expected_reward, m_hardfork->get_current_version())) { return false; } pool_cookie = m_tx_pool.cookie(); #if defined(DEBUG_CREATE_BLOCK_TEMPLATE) - size_t real_txs_size = 0; + size_t real_txs_weight = 0; uint64_t real_fee = 0; CRITICAL_REGION_BEGIN(m_tx_pool.m_transactions_lock); for(crypto::hash &cur_hash: b.tx_hashes) @@ -1275,11 +1275,11 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m continue; } tx_memory_pool::tx_details &cur_tx = cur_res->second; - real_txs_size += cur_tx.blob_size; + real_txs_weight += cur_tx.weight; real_fee += cur_tx.fee; - if (cur_tx.blob_size != get_object_blobsize(cur_tx.tx)) + if (cur_tx.weight != get_transaction_weight(cur_tx.tx)) { - LOG_ERROR("Creating block template: error: invalid transaction size"); + LOG_ERROR("Creating block template: error: invalid transaction weight"); } if (cur_tx.tx.version == 1) { @@ -1301,9 +1301,9 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m } } } - if (txs_size != real_txs_size) + if (txs_weight != real_txs_weight) { - LOG_ERROR("Creating block template: error: wrongly calculated transaction size"); + LOG_ERROR("Creating block template: error: wrongly calculated transaction weight"); } if (fee != real_fee) { @@ -1311,70 +1311,70 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m } CRITICAL_REGION_END(); MDEBUG("Creating block template: height " << height << - ", median size " << median_size << + ", median weight " << median_weight << ", already generated coins " << already_generated_coins << - ", transaction size " << txs_size << + ", transaction weight " << txs_weight << ", fee " << fee); #endif /* - two-phase miner transaction generation: we don't know exact block size until we prepare block, but we don't know reward until we know - block size, so first miner transaction generated with fake amount of money, and with phase we know think we know expected block size + two-phase miner transaction generation: we don't know exact block weight until we prepare block, but we don't know reward until we know + block weight, so first miner transaction generated with fake amount of money, and with phase we know think we know expected block weight */ - //make blocks coin-base tx looks close to real coinbase tx to get truthful blob size + //make blocks coin-base tx looks close to real coinbase tx to get truthful blob weight uint8_t hf_version = m_hardfork->get_current_version(); size_t max_outs = hf_version >= 4 ? 1 : 11; - bool r = construct_miner_tx(height, median_size, already_generated_coins, txs_size, fee, miner_address, b.miner_tx, ex_nonce, max_outs, hf_version); + bool r = construct_miner_tx(height, median_weight, already_generated_coins, txs_weight, fee, miner_address, b.miner_tx, ex_nonce, max_outs, hf_version); CHECK_AND_ASSERT_MES(r, false, "Failed to construct miner tx, first chance"); - size_t cumulative_size = txs_size + get_object_blobsize(b.miner_tx); + size_t cumulative_weight = txs_weight + get_transaction_weight(b.miner_tx); #if defined(DEBUG_CREATE_BLOCK_TEMPLATE) - MDEBUG("Creating block template: miner tx size " << get_object_blobsize(b.miner_tx) << - ", cumulative size " << cumulative_size); + MDEBUG("Creating block template: miner tx weight " << get_transaction_weight(b.miner_tx) << + ", cumulative weight " << cumulative_weight); #endif for (size_t try_count = 0; try_count != 10; ++try_count) { - r = construct_miner_tx(height, median_size, already_generated_coins, cumulative_size, fee, miner_address, b.miner_tx, ex_nonce, max_outs, hf_version); + r = construct_miner_tx(height, median_weight, already_generated_coins, cumulative_weight, fee, miner_address, b.miner_tx, ex_nonce, max_outs, hf_version); CHECK_AND_ASSERT_MES(r, false, "Failed to construct miner tx, second chance"); - size_t coinbase_blob_size = get_object_blobsize(b.miner_tx); - if (coinbase_blob_size > cumulative_size - txs_size) + size_t coinbase_weight = get_transaction_weight(b.miner_tx); + if (coinbase_weight > cumulative_weight - txs_weight) { - cumulative_size = txs_size + coinbase_blob_size; + cumulative_weight = txs_weight + coinbase_weight; #if defined(DEBUG_CREATE_BLOCK_TEMPLATE) - MDEBUG("Creating block template: miner tx size " << coinbase_blob_size << - ", cumulative size " << cumulative_size << " is greater than before"); + MDEBUG("Creating block template: miner tx weight " << coinbase_weight << + ", cumulative weight " << cumulative_weight << " is greater than before"); #endif continue; } - if (coinbase_blob_size < cumulative_size - txs_size) + if (coinbase_weight < cumulative_weight - txs_weight) { - size_t delta = cumulative_size - txs_size - coinbase_blob_size; + size_t delta = cumulative_weight - txs_weight - coinbase_weight; #if defined(DEBUG_CREATE_BLOCK_TEMPLATE) - MDEBUG("Creating block template: miner tx size " << coinbase_blob_size << - ", cumulative size " << txs_size + coinbase_blob_size << + MDEBUG("Creating block template: miner tx weight " << coinbase_weight << + ", cumulative weight " << txs_weight + coinbase_weight << " is less than before, adding " << delta << " zero bytes"); #endif b.miner_tx.extra.insert(b.miner_tx.extra.end(), delta, 0); //here could be 1 byte difference, because of extra field counter is varint, and it can become from 1-byte len to 2-bytes len. - if (cumulative_size != txs_size + get_object_blobsize(b.miner_tx)) + if (cumulative_weight != txs_weight + get_transaction_weight(b.miner_tx)) { - CHECK_AND_ASSERT_MES(cumulative_size + 1 == txs_size + get_object_blobsize(b.miner_tx), false, "unexpected case: cumulative_size=" << cumulative_size << " + 1 is not equal txs_cumulative_size=" << txs_size << " + get_object_blobsize(b.miner_tx)=" << get_object_blobsize(b.miner_tx)); + CHECK_AND_ASSERT_MES(cumulative_weight + 1 == txs_weight + get_transaction_weight(b.miner_tx), false, "unexpected case: cumulative_weight=" << cumulative_weight << " + 1 is not equal txs_cumulative_weight=" << txs_weight << " + get_transaction_weight(b.miner_tx)=" << get_transaction_weight(b.miner_tx)); b.miner_tx.extra.resize(b.miner_tx.extra.size() - 1); - if (cumulative_size != txs_size + get_object_blobsize(b.miner_tx)) + if (cumulative_weight != txs_weight + get_transaction_weight(b.miner_tx)) { - //fuck, not lucky, -1 makes varint-counter size smaller, in that case we continue to grow with cumulative_size + //fuck, not lucky, -1 makes varint-counter size smaller, in that case we continue to grow with cumulative_weight MDEBUG("Miner tx creation has no luck with delta_extra size = " << delta << " and " << delta - 1); - cumulative_size += delta - 1; + cumulative_weight += delta - 1; continue; } MDEBUG("Setting extra for block: " << b.miner_tx.extra.size() << ", try_count=" << try_count); } } - CHECK_AND_ASSERT_MES(cumulative_size == txs_size + get_object_blobsize(b.miner_tx), false, "unexpected case: cumulative_size=" << cumulative_size << " is not equal txs_cumulative_size=" << txs_size << " + get_object_blobsize(b.miner_tx)=" << get_object_blobsize(b.miner_tx)); + CHECK_AND_ASSERT_MES(cumulative_weight == txs_weight + get_transaction_weight(b.miner_tx), false, "unexpected case: cumulative_weight=" << cumulative_weight << " is not equal txs_cumulative_weight=" << txs_weight << " + get_transaction_weight(b.miner_tx)=" << get_transaction_weight(b.miner_tx)); #if defined(DEBUG_CREATE_BLOCK_TEMPLATE) - MDEBUG("Creating block template: miner tx size " << coinbase_blob_size << - ", cumulative size " << cumulative_size << " is now good"); + MDEBUG("Creating block template: miner tx weight " << coinbase_weight << + ", cumulative weight " << cumulative_weight << " is now good"); #endif cache_block_template(b, miner_address, ex_nonce, diffic, expected_reward, pool_cookie); @@ -2540,7 +2540,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, uint64_t& max_used_block_heigh if(m_show_time_stats) { size_t ring_size = !tx.vin.empty() && tx.vin[0].type() == typeid(txin_to_key) ? boost::get(tx.vin[0]).key_offsets.size() : 0; - MINFO("HASH: " << get_transaction_hash(tx) << " I/M/O: " << tx.vin.size() << "/" << ring_size << "/" << tx.vout.size() << " H: " << max_used_block_height << " ms: " << a + m_fake_scan_time << " B: " << get_object_blobsize(tx)); + MINFO("HASH: " << get_transaction_hash(tx) << " I/M/O: " << tx.vin.size() << "/" << ring_size << "/" << tx.vout.size() << " H: " << max_used_block_height << " ms: " << a + m_fake_scan_time << " B: " << get_object_blobsize(tx) << " W: " << get_transaction_weight(tx)); } if (!res) return false; @@ -2597,12 +2597,27 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context // from v8, allow bulletproofs if (hf_version < 8) { - const bool bulletproof = rct::is_rct_bulletproof(tx.rct_signatures.type); - if (bulletproof || !tx.rct_signatures.p.bulletproofs.empty()) - { - MERROR("Bulletproofs are not allowed before v8"); - tvc.m_invalid_output = true; - return false; + if (tx.version >= 2) { + const bool bulletproof = rct::is_rct_bulletproof(tx.rct_signatures.type); + if (bulletproof || !tx.rct_signatures.p.bulletproofs.empty()) + { + MERROR("Bulletproofs are not allowed before v8"); + tvc.m_invalid_output = true; + return false; + } + } + } + + // from v9, forbid borromean range proofs + if (hf_version > 8) { + if (tx.version >= 2) { + const bool borromean = rct::is_rct_borromean(tx.rct_signatures.type); + if (borromean) + { + MERROR("Borromean range proofs are not allowed after v8"); + tvc.m_invalid_output = true; + return false; + } } } @@ -2714,7 +2729,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, { size_t n_unmixable = 0, n_mixable = 0; size_t mixin = std::numeric_limits::max(); - const size_t min_mixin = hf_version >= HF_VERSION_MIN_MIXIN_6 ? 6 : hf_version >= HF_VERSION_MIN_MIXIN_4 ? 4 : 2; + const size_t min_mixin = hf_version >= HF_VERSION_MIN_MIXIN_10 ? 10 : hf_version >= HF_VERSION_MIN_MIXIN_6 ? 6 : hf_version >= HF_VERSION_MIN_MIXIN_4 ? 4 : 2; for (const auto& txin : tx.vin) { // non txin_to_key inputs will be rejected below @@ -2743,6 +2758,13 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, } } + if (hf_version >= HF_VERSION_MIN_MIXIN_10 && mixin != 10) + { + MERROR_VER("Tx " << get_transaction_hash(tx) << " has invalid ring size (" << (mixin + 1) << "), it should be 11"); + tvc.m_low_mixin = true; + return false; + } + if (mixin < min_mixin) { if (n_unmixable == 0) @@ -3093,7 +3115,7 @@ void Blockchain::check_ring_signature(const crypto::hash &tx_prefix_hash, const } //------------------------------------------------------------------ -static uint64_t get_fee_quantization_mask() +uint64_t Blockchain::get_fee_quantization_mask() { static uint64_t mask = 0; if (mask == 0) @@ -3106,16 +3128,27 @@ static uint64_t get_fee_quantization_mask() } //------------------------------------------------------------------ -uint64_t Blockchain::get_dynamic_per_kb_fee(uint64_t block_reward, size_t median_block_size, uint8_t version) +uint64_t Blockchain::get_dynamic_base_fee(uint64_t block_reward, size_t median_block_weight, uint8_t version) { - const uint64_t min_block_size = get_min_block_size(version); - const uint64_t fee_per_kb_base = version >= 5 ? DYNAMIC_FEE_PER_KB_BASE_FEE_V5 : DYNAMIC_FEE_PER_KB_BASE_FEE; + const uint64_t min_block_weight = get_min_block_weight(version); + if (median_block_weight < min_block_weight) + median_block_weight = min_block_weight; + uint64_t hi, lo; + + if (version >= HF_VERSION_PER_BYTE_FEE) + { + lo = mul128(block_reward, DYNAMIC_FEE_REFERENCE_TRANSACTION_WEIGHT, &hi); + div128_32(hi, lo, min_block_weight, &hi, &lo); + div128_32(hi, lo, median_block_weight, &hi, &lo); + assert(hi == 0); + lo /= 5; + return lo; + } - if (median_block_size < min_block_size) - median_block_size = min_block_size; + const uint64_t fee_base = version >= 5 ? DYNAMIC_FEE_PER_KB_BASE_FEE_V5 : DYNAMIC_FEE_PER_KB_BASE_FEE; - uint64_t unscaled_fee_per_kb = (fee_per_kb_base * min_block_size / median_block_size); - uint64_t hi, lo = mul128(unscaled_fee_per_kb, block_reward, &hi); + uint64_t unscaled_fee_base = (fee_base * min_block_weight / median_block_weight); + lo = mul128(unscaled_fee_base, block_reward, &hi); static_assert(DYNAMIC_FEE_PER_KB_BASE_BLOCK_REWARD % 1000000 == 0, "DYNAMIC_FEE_PER_KB_BASE_BLOCK_REWARD must be divisible by 1000000"); static_assert(DYNAMIC_FEE_PER_KB_BASE_BLOCK_REWARD / 1000000 <= std::numeric_limits::max(), "DYNAMIC_FEE_PER_KB_BASE_BLOCK_REWARD is too large"); @@ -3133,29 +3166,48 @@ uint64_t Blockchain::get_dynamic_per_kb_fee(uint64_t block_reward, size_t median } //------------------------------------------------------------------ -bool Blockchain::check_fee(size_t blob_size, uint64_t fee) const +bool Blockchain::check_fee(size_t tx_weight, uint64_t fee) const { const uint8_t version = get_current_hard_fork_version(); - uint64_t fee_per_kb; - if (version < HF_VERSION_DYNAMIC_FEE) - { - fee_per_kb = FEE_PER_KB; - } - else + uint64_t median = 0; + uint64_t already_generated_coins = 0; + uint64_t base_reward = 0; + if (version >= HF_VERSION_DYNAMIC_FEE) { - uint64_t median = m_current_block_cumul_sz_limit / 2; - uint64_t already_generated_coins = m_db->height() ? m_db->get_block_already_generated_coins(m_db->height() - 1) : 0; - uint64_t base_reward; + median = m_current_block_cumul_weight_limit / 2; + already_generated_coins = m_db->height() ? m_db->get_block_already_generated_coins(m_db->height() - 1) : 0; if (!get_block_reward(median, 1, already_generated_coins, base_reward, version)) return false; - fee_per_kb = get_dynamic_per_kb_fee(base_reward, median, version); } - MDEBUG("Using " << print_money(fee_per_kb) << "/kB fee"); - uint64_t needed_fee = blob_size / 1024; - needed_fee += (blob_size % 1024) ? 1 : 0; - needed_fee *= fee_per_kb; + uint64_t needed_fee; + if (version >= HF_VERSION_PER_BYTE_FEE) + { + uint64_t fee_per_byte = get_dynamic_base_fee(base_reward, median, version); + MDEBUG("Using " << print_money(fee_per_byte) << "/byte fee"); + needed_fee = tx_weight * fee_per_byte; + // quantize fee up to 8 decimals + const uint64_t mask = get_fee_quantization_mask(); + needed_fee = (needed_fee + mask - 1) / mask * mask; + } + else + { + uint64_t fee_per_kb; + if (version < HF_VERSION_DYNAMIC_FEE) + { + fee_per_kb = FEE_PER_KB; + } + else + { + fee_per_kb = get_dynamic_base_fee(base_reward, median, version); + } + MDEBUG("Using " << print_money(fee_per_kb) << "/kB fee"); + + needed_fee = tx_weight / 1024; + needed_fee += (tx_weight % 1024) ? 1 : 0; + needed_fee *= fee_per_kb; + } if (fee < needed_fee - needed_fee / 50) // keep a little 2% buffer on acceptance - no integer overflow { @@ -3166,7 +3218,7 @@ bool Blockchain::check_fee(size_t blob_size, uint64_t fee) const } //------------------------------------------------------------------ -uint64_t Blockchain::get_dynamic_per_kb_fee_estimate(uint64_t grace_blocks) const +uint64_t Blockchain::get_dynamic_base_fee_estimate(uint64_t grace_blocks) const { const uint8_t version = get_current_hard_fork_version(); @@ -3176,16 +3228,16 @@ uint64_t Blockchain::get_dynamic_per_kb_fee_estimate(uint64_t grace_blocks) cons if (grace_blocks >= CRYPTONOTE_REWARD_BLOCKS_WINDOW) grace_blocks = CRYPTONOTE_REWARD_BLOCKS_WINDOW - 1; - const uint64_t min_block_size = get_min_block_size(version); - std::vector sz; - get_last_n_blocks_sizes(sz, CRYPTONOTE_REWARD_BLOCKS_WINDOW - grace_blocks); - sz.reserve(grace_blocks); + const uint64_t min_block_weight = get_min_block_weight(version); + std::vector weights; + get_last_n_blocks_weights(weights, CRYPTONOTE_REWARD_BLOCKS_WINDOW - grace_blocks); + weights.reserve(grace_blocks); for (size_t i = 0; i < grace_blocks; ++i) - sz.push_back(min_block_size); + weights.push_back(min_block_weight); - uint64_t median = epee::misc_utils::median(sz); - if(median <= min_block_size) - median = min_block_size; + uint64_t median = epee::misc_utils::median(weights); + if(median <= min_block_weight) + median = min_block_weight; uint64_t already_generated_coins = m_db->height() ? m_db->get_block_already_generated_coins(m_db->height() - 1) : 0; uint64_t base_reward; @@ -3195,8 +3247,9 @@ uint64_t Blockchain::get_dynamic_per_kb_fee_estimate(uint64_t grace_blocks) cons base_reward = BLOCK_REWARD_OVERESTIMATE; } - uint64_t fee = get_dynamic_per_kb_fee(base_reward, median, version); - MDEBUG("Estimating " << grace_blocks << "-block fee at " << print_money(fee) << "/kB"); + uint64_t fee = get_dynamic_base_fee(base_reward, median, version); + const bool per_byte = version < HF_VERSION_PER_BYTE_FEE; + MDEBUG("Estimating " << grace_blocks << "-block fee at " << print_money(fee) << "/" << (per_byte ? "byte" : "kB")); return fee; } @@ -3372,11 +3425,11 @@ bool Blockchain::flush_txes_from_pool(const std::vector &txids) for (const auto &txid: txids) { cryptonote::transaction tx; - size_t blob_size; + size_t tx_weight; uint64_t fee; bool relayed, do_not_relay, double_spend_seen; MINFO("Removing txid " << txid << " from the pool"); - if(m_tx_pool.have_tx(txid) && !m_tx_pool.take_tx(txid, tx, blob_size, fee, relayed, do_not_relay, double_spend_seen)) + if(m_tx_pool.have_tx(txid) && !m_tx_pool.take_tx(txid, tx, tx_weight, fee, relayed, do_not_relay, double_spend_seen)) { MERROR("Failed to remove txid " << txid << " from the pool"); res = false; @@ -3536,8 +3589,8 @@ bool Blockchain::handle_block_to_main_chain(const block& bl, const crypto::hash& goto leave; } - size_t coinbase_blob_size = get_object_blobsize(bl.miner_tx); - size_t cumulative_block_size = coinbase_blob_size; + size_t coinbase_weight = get_transaction_weight(bl.miner_tx); + size_t cumulative_block_weight = coinbase_weight; std::vector txs; key_images_container keys; @@ -3559,7 +3612,7 @@ bool Blockchain::handle_block_to_main_chain(const block& bl, const crypto::hash& for (const crypto::hash& tx_id : bl.tx_hashes) { transaction tx; - size_t blob_size = 0; + size_t tx_weight = 0; uint64_t fee = 0; bool relayed = false, do_not_relay = false, double_spend_seen = false; TIME_MEASURE_START(aa); @@ -3578,7 +3631,7 @@ bool Blockchain::handle_block_to_main_chain(const block& bl, const crypto::hash& TIME_MEASURE_START(bb); // get transaction with hash from tx_pool - if(!m_tx_pool.take_tx(tx_id, tx, blob_size, fee, relayed, do_not_relay, double_spend_seen)) + if(!m_tx_pool.take_tx(tx_id, tx, tx_weight, fee, relayed, do_not_relay, double_spend_seen)) { MERROR_VER("Block with id: " << id << " has at least one unknown transaction with id: " << tx_id); bvc.m_verifivation_failed = true; @@ -3649,7 +3702,7 @@ bool Blockchain::handle_block_to_main_chain(const block& bl, const crypto::hash& TIME_MEASURE_FINISH(cc); t_checktx += cc; fee_summary += fee; - cumulative_block_size += blob_size; + cumulative_block_weight += tx_weight; } m_blocks_txs_check.clear(); @@ -3657,7 +3710,7 @@ bool Blockchain::handle_block_to_main_chain(const block& bl, const crypto::hash& TIME_MEASURE_START(vmt); uint64_t base_reward = 0; uint64_t already_generated_coins = m_db->height() ? m_db->get_block_already_generated_coins(m_db->height() - 1) : 0; - if(!validate_miner_transaction(bl, cumulative_block_size, fee_summary, base_reward, already_generated_coins, bvc.m_partial_block_reward, m_hardfork->get_current_version())) + if(!validate_miner_transaction(bl, cumulative_block_weight, fee_summary, base_reward, already_generated_coins, bvc.m_partial_block_reward, m_hardfork->get_current_version())) { MERROR_VER("Block with id: " << id << " has incorrect miner transaction"); bvc.m_verifivation_failed = true; @@ -3666,11 +3719,11 @@ bool Blockchain::handle_block_to_main_chain(const block& bl, const crypto::hash& } TIME_MEASURE_FINISH(vmt); - size_t block_size; + size_t block_weight; difficulty_type cumulative_difficulty; // populate various metadata about the block to be stored alongside it. - block_size = cumulative_block_size; + block_weight = cumulative_block_weight; cumulative_difficulty = current_diffic; // In the "tail" state when the minimum subsidy (implemented in get_block_reward) is in effect, the number of // coins will eventually exceed MONEY_SUPPLY and overflow a uint64. To prevent overflow, cap already_generated_coins @@ -3691,7 +3744,7 @@ bool Blockchain::handle_block_to_main_chain(const block& bl, const crypto::hash& { try { - new_height = m_db->add_block(bl, block_size, cumulative_difficulty, already_generated_coins, txs); + new_height = m_db->add_block(bl, block_weight, cumulative_difficulty, already_generated_coins, txs); } catch (const KEY_IMAGE_EXISTS& e) { @@ -3716,14 +3769,14 @@ bool Blockchain::handle_block_to_main_chain(const block& bl, const crypto::hash& TIME_MEASURE_FINISH(addblock); - // do this after updating the hard fork state since the size limit may change due to fork - update_next_cumulative_size_limit(); + // do this after updating the hard fork state since the weight limit may change due to fork + update_next_cumulative_weight_limit(); - MINFO("+++++ BLOCK SUCCESSFULLY ADDED" << std::endl << "id:\t" << id << std::endl << "PoW:\t" << proof_of_work << std::endl << "HEIGHT " << new_height-1 << ", difficulty:\t" << current_diffic << std::endl << "block reward: " << print_money(fee_summary + base_reward) << "(" << print_money(base_reward) << " + " << print_money(fee_summary) << "), coinbase_blob_size: " << coinbase_blob_size << ", cumulative size: " << cumulative_block_size << ", " << block_processing_time << "(" << target_calculating_time << "/" << longhash_calculating_time << ")ms"); + MINFO("+++++ BLOCK SUCCESSFULLY ADDED" << std::endl << "id:\t" << id << std::endl << "PoW:\t" << proof_of_work << std::endl << "HEIGHT " << new_height-1 << ", difficulty:\t" << current_diffic << std::endl << "block reward: " << print_money(fee_summary + base_reward) << "(" << print_money(base_reward) << " + " << print_money(fee_summary) << "), coinbase_weight: " << coinbase_weight << ", cumulative weight: " << cumulative_block_weight << ", " << block_processing_time << "(" << target_calculating_time << "/" << longhash_calculating_time << ")ms"); if(m_show_time_stats) { - MINFO("Height: " << new_height << " blob: " << coinbase_blob_size << " cumm: " - << cumulative_block_size << " p/t: " << block_processing_time << " (" + MINFO("Height: " << new_height << " coinbase weight: " << coinbase_weight << " cumm: " + << cumulative_block_weight << " p/t: " << block_processing_time << " (" << target_calculating_time << "/" << longhash_calculating_time << "/" << t1 << "/" << t2 << "/" << t3 << "/" << t_exists << "/" << t_pool << "/" << t_checktx << "/" << t_dblspnd << "/" << vmt << "/" << addblock << ")ms"); @@ -3740,20 +3793,20 @@ bool Blockchain::handle_block_to_main_chain(const block& bl, const crypto::hash& return true; } //------------------------------------------------------------------ -bool Blockchain::update_next_cumulative_size_limit() +bool Blockchain::update_next_cumulative_weight_limit() { - uint64_t full_reward_zone = get_min_block_size(get_current_hard_fork_version()); + uint64_t full_reward_zone = get_min_block_weight(get_current_hard_fork_version()); LOG_PRINT_L3("Blockchain::" << __func__); - std::vector sz; - get_last_n_blocks_sizes(sz, CRYPTONOTE_REWARD_BLOCKS_WINDOW); + std::vector weights; + get_last_n_blocks_weights(weights, CRYPTONOTE_REWARD_BLOCKS_WINDOW); - uint64_t median = epee::misc_utils::median(sz); - m_current_block_cumul_sz_median = median; + uint64_t median = epee::misc_utils::median(weights); + m_current_block_cumul_weight_median = median; if(median <= full_reward_zone) median = full_reward_zone; - m_current_block_cumul_sz_limit = median*2; + m_current_block_cumul_weight_limit = median*2; return true; } //------------------------------------------------------------------ @@ -4650,14 +4703,14 @@ void Blockchain::load_compiled_in_block_hashes() std::vector txs; m_tx_pool.get_transactions(txs); - size_t blob_size; + size_t tx_weight; uint64_t fee; bool relayed, do_not_relay, double_spend_seen; transaction pool_tx; for(const transaction &tx : txs) { crypto::hash tx_hash = get_transaction_hash(tx); - m_tx_pool.take_tx(tx_hash, pool_tx, blob_size, fee, relayed, do_not_relay, double_spend_seen); + m_tx_pool.take_tx(tx_hash, pool_tx, tx_weight, fee, relayed, do_not_relay, double_spend_seen); } } } diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index 2292ffbf3c5..7e2ba7a39fa 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -95,7 +95,7 @@ namespace cryptonote { block bl; //!< the block uint64_t height; //!< the height of the block in the blockchain - size_t block_cumulative_size; //!< the size (in bytes) of the block + size_t block_cumulative_weight; //!< the weight of the block difficulty_type cumulative_difficulty; //!< the accumulated difficulty after that block uint64_t already_generated_coins; //!< the total coins minted after that block }; @@ -579,46 +579,57 @@ namespace cryptonote bool check_tx_inputs(transaction& tx, uint64_t& pmax_used_block_height, crypto::hash& max_used_block_id, tx_verification_context &tvc, bool kept_by_block = false); /** - * @brief get dynamic per kB fee for a given block size + * @brief get fee quantization mask * - * The dynamic fee is based on the block size in a past window, and - * the current block reward. It is expressed by kB. + * The dynamic fee may be quantized, to mask out the last decimal places + * + * @return the fee quantized mask + */ + static uint64_t get_fee_quantization_mask(); + + /** + * @brief get dynamic per kB or byte fee for a given block weight + * + * The dynamic fee is based on the block weight in a past window, and + * the current block reward. It is expressed by kB before v8, and + * per byte from v8. * * @param block_reward the current block reward - * @param median_block_size the median blob's size in the past window + * @param median_block_weight the median block weight in the past window * @param version hard fork version for rules and constants to use * - * @return the per kB fee + * @return the fee */ - static uint64_t get_dynamic_per_kb_fee(uint64_t block_reward, size_t median_block_size, uint8_t version); + static uint64_t get_dynamic_base_fee(uint64_t block_reward, size_t median_block_weight, uint8_t version); /** - * @brief get dynamic per kB fee estimate for the next few blocks + * @brief get dynamic per kB or byte fee estimate for the next few blocks * - * The dynamic fee is based on the block size in a past window, and - * the current block reward. It is expressed by kB. This function - * calculates an estimate for a dynamic fee which will be valid for - * the next grace_blocks + * The dynamic fee is based on the block weight in a past window, and + * the current block reward. It is expressed by kB before v8, and + * per byte from v8. + * This function calculates an estimate for a dynamic fee which will be + * valid for the next grace_blocks * * @param grace_blocks number of blocks we want the fee to be valid for * - * @return the per kB fee estimate + * @return the fee estimate */ - uint64_t get_dynamic_per_kb_fee_estimate(uint64_t grace_blocks) const; + uint64_t get_dynamic_base_fee_estimate(uint64_t grace_blocks) const; /** * @brief validate a transaction's fee * * This function validates the fee is enough for the transaction. - * This is based on the size of the transaction blob, and, after a - * height threshold, on the average size of transaction in a past window + * This is based on the weight of the transaction, and, after a + * height threshold, on the average weight of transaction in a past window * - * @param blob_size the transaction blob's size + * @param tx_weight the transaction weight * @param fee the fee * * @return true if the fee is enough, false otherwise */ - bool check_fee(size_t blob_size, uint64_t fee) const; + bool check_fee(size_t tx_weight, uint64_t fee) const; /** * @brief check that a transaction's outputs conform to current standards @@ -635,18 +646,18 @@ namespace cryptonote bool check_tx_outputs(const transaction& tx, tx_verification_context &tvc); /** - * @brief gets the blocksize limit based on recent blocks + * @brief gets the block weight limit based on recent blocks * * @return the limit */ - uint64_t get_current_cumulative_blocksize_limit() const; + uint64_t get_current_cumulative_block_weight_limit() const; /** - * @brief gets the blocksize median based on recent blocks (same window as for the limit) + * @brief gets the block weight median based on recent blocks (same window as for the limit) * * @return the median */ - uint64_t get_current_cumulative_blocksize_median() const; + uint64_t get_current_cumulative_block_weight_median() const; /** * @brief gets the difficulty of the block with a given height @@ -1001,8 +1012,8 @@ namespace cryptonote // main chain transactions_container m_transactions; - size_t m_current_block_cumul_sz_limit; - size_t m_current_block_cumul_sz_median; + size_t m_current_block_cumul_weight_limit; + size_t m_current_block_cumul_weight_median; // metadata containers std::unordered_map>> m_scan_table; @@ -1225,7 +1236,7 @@ namespace cryptonote * and that his miner transaction totals reward + fee. * * @param b the block containing the miner transaction to be validated - * @param cumulative_block_size the block's size + * @param cumulative_block_weight the block's weight * @param fee the total fees collected in the block * @param base_reward return-by-reference the new block's generated coins * @param already_generated_coins the amount of currency generated prior to this block @@ -1234,7 +1245,7 @@ namespace cryptonote * * @return false if anything is found wrong with the miner transaction, otherwise true */ - bool validate_miner_transaction(const block& b, size_t cumulative_block_size, uint64_t fee, uint64_t& base_reward, uint64_t already_generated_coins, bool &partial_block_reward, uint8_t version); + bool validate_miner_transaction(const block& b, size_t cumulative_block_weight, uint64_t fee, uint64_t& base_reward, uint64_t already_generated_coins, bool &partial_block_reward, uint8_t version); /** * @brief reverts the blockchain to its previous state following a failed switch @@ -1251,14 +1262,14 @@ namespace cryptonote bool rollback_blockchain_switching(std::list& original_chain, uint64_t rollback_height); /** - * @brief gets recent block sizes for median calculation + * @brief gets recent block weights for median calculation * - * get the block sizes of the last blocks, and return by reference . + * get the block weights of the last blocks, and return by reference . * - * @param sz return-by-reference the list of sizes - * @param count the number of blocks to get sizes for + * @param sz return-by-reference the list of weights + * @param count the number of blocks to get weights for */ - void get_last_n_blocks_sizes(std::vector& sz, size_t count) const; + void get_last_n_blocks_weights(std::vector& weights, size_t count) const; /** * @brief adds the given output to the requested set of random outputs @@ -1373,11 +1384,11 @@ namespace cryptonote bool complete_timestamps_vector(uint64_t start_height, std::vector& timestamps); /** - * @brief calculate the block size limit for the next block to be added + * @brief calculate the block weight limit for the next block to be added * * @return true */ - bool update_next_cumulative_size_limit(); + bool update_next_cumulative_weight_limit(); void return_tx_to_pool(std::vector &txs); /** diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index f9e0b68d0b5..c4eaa0cc443 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -162,10 +162,10 @@ namespace cryptonote , "Relay blocks as normal blocks" , false }; - static const command_line::arg_descriptor arg_max_txpool_size = { - "max-txpool-size" - , "Set maximum txpool size in bytes." - , DEFAULT_TXPOOL_MAX_SIZE + static const command_line::arg_descriptor arg_max_txpool_weight = { + "max-txpool-weight" + , "Set maximum txpool weight in bytes." + , DEFAULT_TXPOOL_MAX_WEIGHT }; //----------------------------------------------------------------------------------------------- @@ -274,7 +274,7 @@ namespace cryptonote command_line::add_arg(desc, arg_test_dbg_lock_sleep); command_line::add_arg(desc, arg_offline); command_line::add_arg(desc, arg_disable_dns_checkpoints); - command_line::add_arg(desc, arg_max_txpool_size); + command_line::add_arg(desc, arg_max_txpool_weight); miner::init_options(desc); BlockchainDB::init_options(desc); @@ -402,7 +402,7 @@ namespace cryptonote bool fast_sync = command_line::get_arg(vm, arg_fast_block_sync) != 0; uint64_t blocks_threads = command_line::get_arg(vm, arg_prep_blocks_threads); std::string check_updates_string = command_line::get_arg(vm, arg_check_updates); - size_t max_txpool_size = command_line::get_arg(vm, arg_max_txpool_size); + size_t max_txpool_weight = command_line::get_arg(vm, arg_max_txpool_weight); boost::filesystem::path folder(m_config_folder); if (m_nettype == FAKECHAIN) @@ -551,7 +551,7 @@ namespace cryptonote const difficulty_type fixed_difficulty = command_line::get_arg(vm, arg_fixed_difficulty); r = m_blockchain_storage.init(db.release(), m_nettype, m_offline, regtest ? ®test_test_options : test_options, fixed_difficulty); - r = m_mempool.init(max_txpool_size); + r = m_mempool.init(max_txpool_weight); CHECK_AND_ASSERT_MES(r, false, "Failed to initialize memory pool"); // now that we have a valid m_blockchain_storage, we can clean out any @@ -692,43 +692,6 @@ namespace cryptonote return false; } - // resolve outPk references in rct txes - // outPk aren't the only thing that need resolving for a fully resolved tx, - // but outPk (1) are needed now to check range proof semantics, and - // (2) do not need access to the blockchain to find data - if (tx.version >= 2) - { - rct::rctSig &rv = tx.rct_signatures; - if (rv.outPk.size() != tx.vout.size()) - { - LOG_PRINT_L1("WRONG TRANSACTION BLOB, Bad outPk size in tx " << tx_hash << ", rejected"); - tvc.m_verifivation_failed = true; - return false; - } - for (size_t n = 0; n < tx.rct_signatures.outPk.size(); ++n) - rv.outPk[n].dest = rct::pk2rct(boost::get(tx.vout[n].target).key); - - const bool bulletproof = rct::is_rct_bulletproof(rv.type); - if (bulletproof) - { - if (rct::n_bulletproof_amounts(rv.p.bulletproofs) != tx.vout.size()) - { - LOG_PRINT_L1("WRONG TRANSACTION BLOB, Bad bulletproofs size in tx " << tx_hash << ", rejected"); - tvc.m_verifivation_failed = true; - return false; - } - size_t idx = 0; - for (size_t n = 0; n < rv.p.bulletproofs.size(); ++n) - { - CHECK_AND_ASSERT_MES(rv.p.bulletproofs[n].L.size() >= 6, false, "Bad bulletproofs L size"); // at least 64 bits - const size_t n_amounts = rct::n_bulletproof_amounts(rv.p.bulletproofs[n]); - CHECK_AND_ASSERT_MES(idx + n_amounts <= rv.outPk.size(), false, "Internal error filling out V"); - rv.p.bulletproofs[n].V.clear(); - for (size_t i = 0; i < n_amounts; ++i) - rv.p.bulletproofs[n].V.push_back(rv.outPk[idx++].mask); - } - } - } return true; } //----------------------------------------------------------------------------------------------- @@ -747,20 +710,11 @@ namespace cryptonote //----------------------------------------------------------------------------------------------- static bool is_canonical_bulletproof_layout(const std::vector &proofs) { - size_t n_amounts = rct::n_bulletproof_amounts(proofs), amounts_proved = 0; - size_t n = 0; - while (amounts_proved < n_amounts) - { - if (n >= proofs.size()) - return false; - size_t batch_size = 1; - while (batch_size * 2 + amounts_proved <= n_amounts && batch_size * 2 <= BULLETPROOF_MAX_OUTPUTS) - batch_size *= 2; - if (rct::n_bulletproof_amounts(proofs[n]) != batch_size) - return false; - amounts_proved += batch_size; - ++n; - } + if (proofs.size() != 1) + return false; + const size_t sz = proofs[0].V.size(); + if (sz == 0 || sz > BULLETPROOF_MAX_OUTPUTS) + return false; return true; } //----------------------------------------------------------------------------------------------- @@ -816,11 +770,9 @@ namespace cryptonote } break; case rct::RCTTypeBulletproof: - // in addition to valid bulletproofs, we want multi-out - // proofs to be in decreasing power of 2 constituents if (!is_canonical_bulletproof_layout(rv.p.bulletproofs)) { - MERROR_VER("Bulletproof does not use decreasing power of 2 rule"); + MERROR_VER("Bulletproof does not have canonical form"); set_semantics_failed(tx_info[n].tx_hash); tx_info[n].tvc.m_verifivation_failed = true; tx_info[n].result = false; @@ -933,7 +885,8 @@ namespace cryptonote continue; } - ok &= add_new_tx(results[i].tx, results[i].hash, results[i].prefix_hash, it->size(), tvc[i], keeped_by_block, relayed, do_not_relay); + const size_t weight = get_transaction_weight(results[i].tx, it->size()); + ok &= add_new_tx(results[i].tx, results[i].hash, results[i].prefix_hash, weight, tvc[i], keeped_by_block, relayed, do_not_relay); if(tvc[i].m_verifivation_failed) {MERROR_VER("Transaction verification failed: " << results[i].hash);} else if(tvc[i].m_verifivation_impossible) @@ -1016,9 +969,9 @@ namespace cryptonote } // for version > 1, ringct signatures check verifies amounts match - if(!keeped_by_block && get_object_blobsize(tx) >= m_blockchain_storage.get_current_cumulative_blocksize_limit() - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE) + if(!keeped_by_block && get_transaction_weight(tx) >= m_blockchain_storage.get_current_cumulative_block_weight_limit() - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE) { - MERROR_VER("tx is too large " << get_object_blobsize(tx) << ", expected not bigger than " << m_blockchain_storage.get_current_cumulative_blocksize_limit() - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE); + MERROR_VER("tx is too large " << get_transaction_weight(tx) << ", expected not bigger than " << m_blockchain_storage.get_current_cumulative_block_weight_limit() - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE); return false; } @@ -1150,7 +1103,8 @@ namespace cryptonote crypto::hash tx_prefix_hash = get_transaction_prefix_hash(tx); blobdata bl; t_serializable_object_to_blob(tx, bl); - return add_new_tx(tx, tx_hash, tx_prefix_hash, bl.size(), tvc, keeped_by_block, relayed, do_not_relay); + size_t tx_weight = get_transaction_weight(tx, bl.size()); + return add_new_tx(tx, tx_hash, tx_prefix_hash, tx_weight, tvc, keeped_by_block, relayed, do_not_relay); } //----------------------------------------------------------------------------------------------- size_t core::get_blockchain_total_transactions() const @@ -1158,7 +1112,7 @@ namespace cryptonote return m_blockchain_storage.get_total_transactions(); } //----------------------------------------------------------------------------------------------- - bool core::add_new_tx(transaction& tx, const crypto::hash& tx_hash, const crypto::hash& tx_prefix_hash, size_t blob_size, tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay) + bool core::add_new_tx(transaction& tx, const crypto::hash& tx_hash, const crypto::hash& tx_prefix_hash, size_t tx_weight, tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay) { if (keeped_by_block) get_blockchain_storage().on_new_tx_from_block(tx); @@ -1176,7 +1130,7 @@ namespace cryptonote } uint8_t version = m_blockchain_storage.get_current_hard_fork_version(); - return m_mempool.add_tx(tx, tx_hash, blob_size, tvc, keeped_by_block, relayed, do_not_relay, version); + return m_mempool.add_tx(tx, tx_hash, tx_weight, tvc, keeped_by_block, relayed, do_not_relay, version); } //----------------------------------------------------------------------------------------------- bool core::relay_txpool_transactions() diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index 497b1621467..8b68f5e2b5b 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -788,12 +788,12 @@ namespace cryptonote * * @param tx_hash the transaction's hash * @param tx_prefix_hash the transaction prefix' hash - * @param blob_size the size of the transaction + * @param tx_weight the weight of the transaction * @param relayed whether or not the transaction was relayed to us * @param do_not_relay whether to prevent the transaction from being relayed * */ - bool add_new_tx(transaction& tx, const crypto::hash& tx_hash, const crypto::hash& tx_prefix_hash, size_t blob_size, tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay); + bool add_new_tx(transaction& tx, const crypto::hash& tx_hash, const crypto::hash& tx_prefix_hash, size_t tx_weight, tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay); /** * @brief add a new transaction to the transaction pool diff --git a/src/cryptonote_core/cryptonote_tx_utils.cpp b/src/cryptonote_core/cryptonote_tx_utils.cpp index 52594507936..fb2af9ceb45 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.cpp +++ b/src/cryptonote_core/cryptonote_tx_utils.cpp @@ -74,7 +74,7 @@ namespace cryptonote LOG_PRINT_L2("destinations include " << num_stdaddresses << " standard addresses and " << num_subaddresses << " subaddresses"); } //--------------------------------------------------------------- - bool construct_miner_tx(size_t height, size_t median_size, uint64_t already_generated_coins, size_t current_block_size, uint64_t fee, const account_public_address &miner_address, transaction& tx, const blobdata& extra_nonce, size_t max_outs, uint8_t hard_fork_version) { + bool construct_miner_tx(size_t height, size_t median_weight, uint64_t already_generated_coins, size_t current_block_weight, uint64_t fee, const account_public_address &miner_address, transaction& tx, const blobdata& extra_nonce, size_t max_outs, uint8_t hard_fork_version) { tx.vin.clear(); tx.vout.clear(); tx.extra.clear(); @@ -89,7 +89,7 @@ namespace cryptonote in.height = height; uint64_t block_reward; - if(!get_block_reward(median_size, current_block_size, already_generated_coins, block_reward, hard_fork_version)) + if(!get_block_reward(median_weight, current_block_weight, already_generated_coins, block_reward, hard_fork_version)) { LOG_PRINT_L0("Block is too big"); return false; @@ -491,7 +491,7 @@ namespace cryptonote // the non-simple version is slightly smaller, but assumes all real inputs // are on the same index, so can only be used if there just one ring. - bool use_simple_rct = sources.size() > 1 || range_proof_type == rct::RangeProofMultiOutputBulletproof || range_proof_type == rct::RangeProofBulletproof; + bool use_simple_rct = sources.size() > 1 || range_proof_type != rct::RangeProofBorromean; if (!use_simple_rct) { diff --git a/src/cryptonote_core/cryptonote_tx_utils.h b/src/cryptonote_core/cryptonote_tx_utils.h index 08e8725f75f..f2cf7b6ca52 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.h +++ b/src/cryptonote_core/cryptonote_tx_utils.h @@ -37,7 +37,7 @@ namespace cryptonote { //--------------------------------------------------------------- - bool construct_miner_tx(size_t height, size_t median_size, uint64_t already_generated_coins, size_t current_block_size, uint64_t fee, const account_public_address &miner_address, transaction& tx, const blobdata& extra_nonce = blobdata(), size_t max_outs = 999, uint8_t hard_fork_version = 1); + bool construct_miner_tx(size_t height, size_t median_weight, uint64_t already_generated_coins, size_t current_block_weight, uint64_t fee, const account_public_address &miner_address, transaction& tx, const blobdata& extra_nonce = blobdata(), size_t max_outs = 999, uint8_t hard_fork_version = 1); struct tx_source_entry { diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index 41c58fcb6fd..b12a329bb36 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -80,9 +80,13 @@ namespace cryptonote return amount * ACCEPT_THRESHOLD; } - uint64_t get_transaction_size_limit(uint8_t version) + uint64_t get_transaction_weight_limit(uint8_t version) { - return get_min_block_size(version) - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE; + // from v8, limit a tx to 50% of the minimum block weight + if (version >= 8) + return get_min_block_weight(version) / 2 - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE; + else + return get_min_block_weight(version) - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE; } // This class is meant to create a batch when none currently exists. @@ -102,12 +106,12 @@ namespace cryptonote } //--------------------------------------------------------------------------------- //--------------------------------------------------------------------------------- - tx_memory_pool::tx_memory_pool(Blockchain& bchs): m_blockchain(bchs), m_txpool_max_size(DEFAULT_TXPOOL_MAX_SIZE), m_txpool_size(0), m_cookie(0) + tx_memory_pool::tx_memory_pool(Blockchain& bchs): m_blockchain(bchs), m_txpool_max_weight(DEFAULT_TXPOOL_MAX_WEIGHT), m_txpool_weight(0), m_cookie(0) { } //--------------------------------------------------------------------------------- - bool tx_memory_pool::add_tx(transaction &tx, /*const crypto::hash& tx_prefix_hash,*/ const crypto::hash &id, size_t blob_size, tx_verification_context& tvc, bool kept_by_block, bool relayed, bool do_not_relay, uint8_t version) + bool tx_memory_pool::add_tx(transaction &tx, /*const crypto::hash& tx_prefix_hash,*/ const crypto::hash &id, size_t tx_weight, tx_verification_context& tvc, bool kept_by_block, bool relayed, bool do_not_relay, uint8_t version) { // this should already be called with that lock, but let's make it explicit for clarity CRITICAL_REGION_LOCAL(m_transactions_lock); @@ -173,17 +177,17 @@ namespace cryptonote fee = tx.rct_signatures.txnFee; } - if (!kept_by_block && !m_blockchain.check_fee(blob_size, fee)) + if (!kept_by_block && !m_blockchain.check_fee(tx_weight, fee)) { tvc.m_verifivation_failed = true; tvc.m_fee_too_low = true; return false; } - size_t tx_size_limit = get_transaction_size_limit(version); - if (!kept_by_block && blob_size > tx_size_limit) + size_t tx_weight_limit = get_transaction_weight_limit(version); + if (!kept_by_block && tx_weight > tx_weight_limit) { - LOG_PRINT_L1("transaction is too big: " << blob_size << " bytes, maximum size: " << tx_size_limit); + LOG_PRINT_L1("transaction is too heavy: " << tx_weight << " bytes, maximum weight: " << tx_weight_limit); tvc.m_verifivation_failed = true; tvc.m_too_big = true; return false; @@ -227,7 +231,7 @@ namespace cryptonote // may become valid again, so ignore the failed inputs check. if(kept_by_block) { - meta.blob_size = blob_size; + meta.weight = tx_weight; meta.fee = fee; meta.max_used_block_id = null_hash; meta.max_used_block_height = 0; @@ -248,7 +252,7 @@ namespace cryptonote m_blockchain.add_txpool_tx(tx, meta); if (!insert_key_images(tx, kept_by_block)) return false; - m_txs_by_fee_and_receive_time.emplace(std::pair(fee / (double)blob_size, receive_time), id); + m_txs_by_fee_and_receive_time.emplace(std::pair(fee / (double)tx_weight, receive_time), id); } catch (const std::exception &e) { @@ -267,7 +271,7 @@ namespace cryptonote }else { //update transactions container - meta.blob_size = blob_size; + meta.weight = tx_weight; meta.kept_by_block = kept_by_block; meta.fee = fee; meta.max_used_block_id = max_used_block_id; @@ -290,7 +294,7 @@ namespace cryptonote m_blockchain.add_txpool_tx(tx, meta); if (!insert_key_images(tx, kept_by_block)) return false; - m_txs_by_fee_and_receive_time.emplace(std::pair(fee / (double)blob_size, receive_time), id); + m_txs_by_fee_and_receive_time.emplace(std::pair(fee / (double)tx_weight, receive_time), id); } catch (const std::exception &e) { @@ -304,13 +308,13 @@ namespace cryptonote } tvc.m_verifivation_failed = false; - m_txpool_size += blob_size; + m_txpool_weight += tx_weight; ++m_cookie; - MINFO("Transaction added to pool: txid " << id << " bytes: " << blob_size << " fee/byte: " << (fee / (double)blob_size)); + MINFO("Transaction added to pool: txid " << id << " weight: " << tx_weight << " fee/byte: " << (fee / (double)tx_weight)); - prune(m_txpool_max_size); + prune(m_txpool_max_weight); return true; } @@ -321,26 +325,26 @@ namespace cryptonote size_t blob_size = 0; if (!get_transaction_hash(tx, h, blob_size) || blob_size == 0) return false; - return add_tx(tx, h, blob_size, tvc, keeped_by_block, relayed, do_not_relay, version); + return add_tx(tx, h, get_transaction_weight(tx, blob_size), tvc, keeped_by_block, relayed, do_not_relay, version); } //--------------------------------------------------------------------------------- - size_t tx_memory_pool::get_txpool_size() const + size_t tx_memory_pool::get_txpool_weight() const { CRITICAL_REGION_LOCAL(m_transactions_lock); - return m_txpool_size; + return m_txpool_weight; } //--------------------------------------------------------------------------------- - void tx_memory_pool::set_txpool_max_size(size_t bytes) + void tx_memory_pool::set_txpool_max_weight(size_t bytes) { CRITICAL_REGION_LOCAL(m_transactions_lock); - m_txpool_max_size = bytes; + m_txpool_max_weight = bytes; } //--------------------------------------------------------------------------------- void tx_memory_pool::prune(size_t bytes) { CRITICAL_REGION_LOCAL(m_transactions_lock); if (bytes == 0) - bytes = m_txpool_max_size; + bytes = m_txpool_max_weight; CRITICAL_REGION_LOCAL1(m_blockchain); LockedTXN lock(m_blockchain); bool changed = false; @@ -349,7 +353,7 @@ namespace cryptonote auto it = --m_txs_by_fee_and_receive_time.end(); while (it != m_txs_by_fee_and_receive_time.begin()) { - if (m_txpool_size <= bytes) + if (m_txpool_weight <= bytes) break; try { @@ -374,11 +378,11 @@ namespace cryptonote return; } // remove first, in case this throws, so key images aren't removed - MINFO("Pruning tx " << txid << " from txpool: size: " << it->first.second << ", fee/byte: " << it->first.first); + MINFO("Pruning tx " << txid << " from txpool: weight: " << it->first.second << ", fee/byte: " << it->first.first); m_blockchain.remove_txpool_tx(txid); - m_txpool_size -= txblob.size(); + m_txpool_weight -= it->first.second; remove_transaction_keyimages(tx); - MINFO("Pruned tx " << txid << " from txpool: size: " << it->first.second << ", fee/byte: " << it->first.first); + MINFO("Pruned tx " << txid << " from txpool: weight: " << it->first.second << ", fee/byte: " << it->first.first); m_txs_by_fee_and_receive_time.erase(it--); changed = true; } @@ -390,8 +394,8 @@ namespace cryptonote } if (changed) ++m_cookie; - if (m_txpool_size > bytes) - MINFO("Pool size after pruning is larger than limit: " << m_txpool_size << "/" << bytes); + if (m_txpool_weight > bytes) + MINFO("Pool weight after pruning is larger than limit: " << m_txpool_weight << "/" << bytes); } //--------------------------------------------------------------------------------- bool tx_memory_pool::insert_key_images(const transaction &tx, bool kept_by_block) @@ -446,7 +450,7 @@ namespace cryptonote return true; } //--------------------------------------------------------------------------------- - bool tx_memory_pool::take_tx(const crypto::hash &id, transaction &tx, size_t& blob_size, uint64_t& fee, bool &relayed, bool &do_not_relay, bool &double_spend_seen) + bool tx_memory_pool::take_tx(const crypto::hash &id, transaction &tx, size_t& tx_weight, uint64_t& fee, bool &relayed, bool &do_not_relay, bool &double_spend_seen) { CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL1(m_blockchain); @@ -470,7 +474,7 @@ namespace cryptonote MERROR("Failed to parse tx from txpool"); return false; } - blob_size = meta.blob_size; + tx_weight = meta.weight; fee = meta.fee; relayed = meta.relayed; do_not_relay = meta.do_not_relay; @@ -478,7 +482,7 @@ namespace cryptonote // remove first, in case this throws, so key images aren't removed m_blockchain.remove_txpool_tx(id); - m_txpool_size -= blob_size; + m_txpool_weight -= tx_weight; remove_transaction_keyimages(tx); } catch (const std::exception &e) @@ -552,7 +556,7 @@ namespace cryptonote { // remove first, so we only remove key images if the tx removal succeeds m_blockchain.remove_txpool_tx(txid); - m_txpool_size -= bd.size(); + m_txpool_weight -= get_transaction_weight(tx, bd.size()); remove_transaction_keyimages(tx); } } @@ -670,7 +674,7 @@ namespace cryptonote const uint64_t now = time(NULL); backlog.reserve(m_blockchain.get_txpool_tx_count(include_unrelayed_txes)); m_blockchain.for_all_txpool_txes([&backlog, now](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd){ - backlog.push_back({meta.blob_size, meta.fee, meta.receive_time - now}); + backlog.push_back({meta.weight, meta.fee, meta.receive_time - now}); return true; }, false, include_unrelayed_txes); } @@ -682,15 +686,15 @@ namespace cryptonote const uint64_t now = time(NULL); std::map agebytes; stats.txs_total = m_blockchain.get_txpool_tx_count(include_unrelayed_txes); - std::vector sizes; - sizes.reserve(stats.txs_total); - m_blockchain.for_all_txpool_txes([&stats, &sizes, now, &agebytes](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd){ - sizes.push_back(meta.blob_size); - stats.bytes_total += meta.blob_size; - if (!stats.bytes_min || meta.blob_size < stats.bytes_min) - stats.bytes_min = meta.blob_size; - if (meta.blob_size > stats.bytes_max) - stats.bytes_max = meta.blob_size; + std::vector weights; + weights.reserve(stats.txs_total); + m_blockchain.for_all_txpool_txes([&stats, &weights, now, &agebytes](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd){ + weights.push_back(meta.weight); + stats.bytes_total += meta.weight; + if (!stats.bytes_min || meta.weight < stats.bytes_min) + stats.bytes_min = meta.weight; + if (meta.weight > stats.bytes_max) + stats.bytes_max = meta.weight; if (!meta.relayed) stats.num_not_relayed++; stats.fee_total += meta.fee; @@ -702,12 +706,12 @@ namespace cryptonote stats.num_failing++; uint64_t age = now - meta.receive_time + (now == meta.receive_time); agebytes[age].txs++; - agebytes[age].bytes += meta.blob_size; + agebytes[age].bytes += meta.weight; if (meta.double_spend_seen) ++stats.num_double_spends; return true; }, false, include_unrelayed_txes); - stats.bytes_med = epee::misc_utils::median(sizes); + stats.bytes_med = epee::misc_utils::median(weights); if (stats.txs_total > 1) { /* looking for 98th percentile */ @@ -771,7 +775,8 @@ namespace cryptonote return true; } txi.tx_json = obj_to_json_str(tx); - txi.blob_size = meta.blob_size; + txi.blob_size = bd->size(); + txi.weight = meta.weight; txi.fee = meta.fee; txi.kept_by_block = meta.kept_by_block; txi.max_used_block_height = meta.max_used_block_height; @@ -842,7 +847,8 @@ namespace cryptonote return true; } txi.tx = tx; - txi.blob_size = meta.blob_size; + txi.blob_size = bd->size(); + txi.weight = meta.weight; txi.fee = meta.fee; txi.kept_by_block = meta.kept_by_block; txi.max_used_block_height = meta.max_used_block_height; @@ -1116,7 +1122,8 @@ namespace cryptonote } ss << obj_to_json_str(tx) << std::endl; } - ss << "blob_size: " << meta.blob_size << std::endl + ss << "blob_size: " << (short_format ? "-" : std::to_string(txblob->size())) << std::endl + << "weight: " << meta.weight << std::endl << "fee: " << print_money(meta.fee) << std::endl << "kept_by_block: " << (meta.kept_by_block ? 'T' : 'F') << std::endl << "double_spend_seen: " << (meta.double_spend_seen ? 'T' : 'F') << std::endl @@ -1131,25 +1138,25 @@ namespace cryptonote } //--------------------------------------------------------------------------------- //TODO: investigate whether boolean return is appropriate - bool tx_memory_pool::fill_block_template(block &bl, size_t median_size, uint64_t already_generated_coins, size_t &total_size, uint64_t &fee, uint64_t &expected_reward, uint8_t version) + bool tx_memory_pool::fill_block_template(block &bl, size_t median_weight, uint64_t already_generated_coins, size_t &total_weight, uint64_t &fee, uint64_t &expected_reward, uint8_t version) { CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL1(m_blockchain); uint64_t best_coinbase = 0, coinbase = 0; - total_size = 0; + total_weight = 0; fee = 0; //baseline empty block - get_block_reward(median_size, total_size, already_generated_coins, best_coinbase, version); + get_block_reward(median_weight, total_weight, already_generated_coins, best_coinbase, version); - size_t max_total_size_pre_v5 = (130 * median_size) / 100 - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE; - size_t max_total_size_v5 = 2 * median_size - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE; - size_t max_total_size = version >= 5 ? max_total_size_v5 : max_total_size_pre_v5; + size_t max_total_weight_pre_v5 = (130 * median_weight) / 100 - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE; + size_t max_total_weight_v5 = 2 * median_weight - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE; + size_t max_total_weight = version >= 5 ? max_total_weight_v5 : max_total_weight_pre_v5; std::unordered_set k_images; - LOG_PRINT_L2("Filling block template, median size " << median_size << ", " << m_txs_by_fee_and_receive_time.size() << " txes in the pool"); + LOG_PRINT_L2("Filling block template, median weight " << median_weight << ", " << m_txs_by_fee_and_receive_time.size() << " txes in the pool"); LockedTXN lock(m_blockchain); @@ -1162,12 +1169,12 @@ namespace cryptonote MERROR(" failed to find tx meta"); continue; } - LOG_PRINT_L2("Considering " << sorted_it->second << ", size " << meta.blob_size << ", current block size " << total_size << "/" << max_total_size << ", current coinbase " << print_money(best_coinbase)); + LOG_PRINT_L2("Considering " << sorted_it->second << ", weight " << meta.weight << ", current block weight " << total_weight << "/" << max_total_weight << ", current coinbase " << print_money(best_coinbase)); - // Can not exceed maximum block size - if (max_total_size < total_size + meta.blob_size) + // Can not exceed maximum block weight + if (max_total_weight < total_weight + meta.weight) { - LOG_PRINT_L2(" would exceed maximum block size"); + LOG_PRINT_L2(" would exceed maximum block weight"); continue; } @@ -1177,9 +1184,9 @@ namespace cryptonote // If we're getting lower coinbase tx, // stop including more tx uint64_t block_reward; - if(!get_block_reward(median_size, total_size + meta.blob_size, already_generated_coins, block_reward, version)) + if(!get_block_reward(median_weight, total_weight + meta.weight, already_generated_coins, block_reward, version)) { - LOG_PRINT_L2(" would exceed maximum block size"); + LOG_PRINT_L2(" would exceed maximum block weight"); continue; } coinbase = block_reward + fee + meta.fee; @@ -1191,11 +1198,11 @@ namespace cryptonote } else { - // If we've exceeded the penalty free size, + // If we've exceeded the penalty free weight, // stop including more tx - if (total_size > median_size) + if (total_weight > median_weight) { - LOG_PRINT_L2(" would exceed median block size"); + LOG_PRINT_L2(" would exceed median block weight"); break; } } @@ -1241,16 +1248,16 @@ namespace cryptonote } bl.tx_hashes.push_back(sorted_it->second); - total_size += meta.blob_size; + total_weight += meta.weight; fee += meta.fee; best_coinbase = coinbase; append_key_images(k_images, tx); - LOG_PRINT_L2(" added, new block size " << total_size << "/" << max_total_size << ", coinbase " << print_money(best_coinbase)); + LOG_PRINT_L2(" added, new block weight " << total_weight << "/" << max_total_weight << ", coinbase " << print_money(best_coinbase)); } expected_reward = best_coinbase; - LOG_PRINT_L2("Block template filled with " << bl.tx_hashes.size() << " txes, size " - << total_size << "/" << max_total_size << ", coinbase " << print_money(best_coinbase) + LOG_PRINT_L2("Block template filled with " << bl.tx_hashes.size() << " txes, weight " + << total_weight << "/" << max_total_weight << ", coinbase " << print_money(best_coinbase) << " (including " << print_money(fee) << " in fees)"); return true; } @@ -1259,14 +1266,14 @@ namespace cryptonote { CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL1(m_blockchain); - size_t tx_size_limit = get_transaction_size_limit(version); + size_t tx_weight_limit = get_transaction_weight_limit(version); std::unordered_set remove; - m_txpool_size = 0; - m_blockchain.for_all_txpool_txes([this, &remove, tx_size_limit](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata*) { - m_txpool_size += meta.blob_size; - if (meta.blob_size > tx_size_limit) { - LOG_PRINT_L1("Transaction " << txid << " is too big (" << meta.blob_size << " bytes), removing it from pool"); + m_txpool_weight = 0; + m_blockchain.for_all_txpool_txes([this, &remove, tx_weight_limit](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata*) { + m_txpool_weight += meta.weight; + if (meta.weight > tx_weight_limit) { + LOG_PRINT_L1("Transaction " << txid << " is too big (" << meta.weight << " bytes), removing it from pool"); remove.insert(txid); } else if (m_blockchain.have_tx(txid)) { @@ -1293,7 +1300,7 @@ namespace cryptonote } // remove tx from db first m_blockchain.remove_txpool_tx(txid); - m_txpool_size -= txblob.size(); + m_txpool_weight -= get_transaction_weight(tx, txblob.size()); remove_transaction_keyimages(tx); auto sorted_it = find_tx_in_sorted_container(txid); if (sorted_it == m_txs_by_fee_and_receive_time.end()) @@ -1318,15 +1325,15 @@ namespace cryptonote return n_removed; } //--------------------------------------------------------------------------------- - bool tx_memory_pool::init(size_t max_txpool_size) + bool tx_memory_pool::init(size_t max_txpool_weight) { CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL1(m_blockchain); - m_txpool_max_size = max_txpool_size ? max_txpool_size : DEFAULT_TXPOOL_MAX_SIZE; + m_txpool_max_weight = max_txpool_weight ? max_txpool_weight : DEFAULT_TXPOOL_MAX_WEIGHT; m_txs_by_fee_and_receive_time.clear(); m_spent_key_images.clear(); - m_txpool_size = 0; + m_txpool_weight = 0; std::vector remove; // first add the not kept by block, then the kept by block, @@ -1348,8 +1355,8 @@ namespace cryptonote MFATAL("Failed to insert key images from txpool tx"); return false; } - m_txs_by_fee_and_receive_time.emplace(std::pair(meta.fee / (double)meta.blob_size, meta.receive_time), txid); - m_txpool_size += meta.blob_size; + m_txs_by_fee_and_receive_time.emplace(std::pair(meta.fee / (double)meta.weight, meta.receive_time), txid); + m_txpool_weight += meta.weight; return true; }, true); if (!r) diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h index 4abfef85cb1..892cadc6916 100644 --- a/src/cryptonote_core/tx_pool.h +++ b/src/cryptonote_core/tx_pool.h @@ -84,7 +84,7 @@ namespace cryptonote * * This handling includes: * storing the transactions - * organizing the transactions by fee per size + * organizing the transactions by fee per weight unit * taking/giving transactions to and from various other components * saving the transactions to disk on shutdown * helping create a new block template by choosing transactions for it @@ -105,9 +105,9 @@ namespace cryptonote * @copydoc add_tx(transaction&, tx_verification_context&, bool, bool, uint8_t) * * @param id the transaction's hash - * @param blob_size the transaction's size + * @param tx_weight the transaction's weight */ - bool add_tx(transaction &tx, const crypto::hash &id, size_t blob_size, tx_verification_context& tvc, bool kept_by_block, bool relayed, bool do_not_relay, uint8_t version); + bool add_tx(transaction &tx, const crypto::hash &id, size_t tx_weight, tx_verification_context& tvc, bool kept_by_block, bool relayed, bool do_not_relay, uint8_t version); /** * @brief add a transaction to the transaction pool @@ -133,7 +133,7 @@ namespace cryptonote * * @param id the hash of the transaction * @param tx return-by-reference the transaction taken - * @param blob_size return-by-reference the transaction's size + * @param tx_weight return-by-reference the transaction's weight * @param fee the transaction fee * @param relayed return-by-reference was transaction relayed to us by the network? * @param do_not_relay return-by-reference is transaction not to be relayed to the network? @@ -141,7 +141,7 @@ namespace cryptonote * * @return true unless the transaction cannot be found in the pool */ - bool take_tx(const crypto::hash &id, transaction &tx, size_t& blob_size, uint64_t& fee, bool &relayed, bool &do_not_relay, bool &double_spend_seen); + bool take_tx(const crypto::hash &id, transaction &tx, size_t& tx_weight, uint64_t& fee, bool &relayed, bool &do_not_relay, bool &double_spend_seen); /** * @brief checks if the pool has a transaction with the given hash @@ -198,11 +198,11 @@ namespace cryptonote /** * @brief loads pool state (if any) from disk, and initializes pool * - * @param max_txpool_size the max size in bytes + * @param max_txpool_weight the max weight in bytes * * @return true */ - bool init(size_t max_txpool_size = 0); + bool init(size_t max_txpool_weight = 0); /** * @brief attempts to save the transaction pool state to disk @@ -219,16 +219,16 @@ namespace cryptonote * @brief Chooses transactions for a block to include * * @param bl return-by-reference the block to fill in with transactions - * @param median_size the current median block size + * @param median_weight the current median block weight * @param already_generated_coins the current total number of coins "minted" - * @param total_size return-by-reference the total size of the new block + * @param total_weight return-by-reference the total weight of the new block * @param fee return-by-reference the total of fees from the included transactions * @param expected_reward return-by-reference the total reward awarded to the miner finding this block, including transaction fees * @param version hard fork version to use for consensus rules * * @return true */ - bool fill_block_template(block &bl, size_t median_size, uint64_t already_generated_coins, size_t &total_size, uint64_t &fee, uint64_t &expected_reward, uint8_t version); + bool fill_block_template(block &bl, size_t median_weight, uint64_t already_generated_coins, size_t &total_weight, uint64_t &fee, uint64_t &expected_reward, uint8_t version); /** * @brief get a list of all transactions in the pool @@ -249,7 +249,7 @@ namespace cryptonote void get_transaction_hashes(std::vector& txs, bool include_unrelayed_txes = true) const; /** - * @brief get (size, fee, receive time) for all transaction in the pool + * @brief get (weight, fee, receive time) for all transaction in the pool * * @param txs return-by-reference that data * @param include_unrelayed_txes include unrelayed txes in the result @@ -370,21 +370,21 @@ namespace cryptonote uint64_t cookie() const { return m_cookie; } /** - * @brief get the cumulative txpool size in bytes + * @brief get the cumulative txpool weight in bytes * - * @return the cumulative txpool size in bytes + * @return the cumulative txpool weight in bytes */ - size_t get_txpool_size() const; + size_t get_txpool_weight() const; /** - * @brief set the max cumulative txpool size in bytes + * @brief set the max cumulative txpool weight in bytes * - * @param bytes the max cumulative txpool size in bytes + * @param bytes the max cumulative txpool weight in bytes */ - void set_txpool_max_size(size_t bytes); + void set_txpool_max_weight(size_t bytes); #define CURRENT_MEMPOOL_ARCHIVE_VER 11 -#define CURRENT_MEMPOOL_TX_DETAILS_ARCHIVE_VER 12 +#define CURRENT_MEMPOOL_TX_DETAILS_ARCHIVE_VER 13 /** * @brief information about a single transaction @@ -393,6 +393,7 @@ namespace cryptonote { transaction tx; //!< the transaction size_t blob_size; //!< the transaction's size + size_t weight; //!< the transaction's weight uint64_t fee; //!< the transaction's fee amount crypto::hash max_used_block_id; //!< the hash of the highest block referenced by an input uint64_t max_used_block_height; //!< the height of the highest block referenced by an input @@ -522,7 +523,7 @@ namespace cryptonote /** * @brief prune lowest fee/byte txes till we're not above bytes * - * if bytes is 0, use m_txpool_max_size + * if bytes is 0, use m_txpool_max_weight */ void prune(size_t bytes = 0); @@ -578,8 +579,8 @@ namespace cryptonote Blockchain& m_blockchain; //!< reference to the Blockchain object - size_t m_txpool_max_size; - size_t m_txpool_size; + size_t m_txpool_max_weight; + size_t m_txpool_weight; mutable std::unordered_map> m_input_cache; }; @@ -608,6 +609,9 @@ namespace boost if (version < 12) return; ar & td.do_not_relay; + if (version < 13) + return; + ar & td.weight; } } } diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp index 45ba81e1687..9ab1be246de 100644 --- a/src/daemon/rpc_command_executor.cpp +++ b/src/daemon/rpc_command_executor.cpp @@ -76,6 +76,7 @@ namespace { << "difficulty: " << boost::lexical_cast(header.difficulty) << std::endl << "POW hash: " << header.pow_hash << std::endl << "block size: " << header.block_size << std::endl + << "block weight: " << header.block_weight << std::endl << "num txes: " << header.num_txes << std::endl << "reward: " << cryptonote::print_money(header.reward); } @@ -558,7 +559,7 @@ bool t_rpc_command_executor::print_blockchain_info(uint64_t start_block_index, u std::cout << std::endl; std::cout << "height: " << header.height << ", timestamp: " << header.timestamp - << ", size: " << header.block_size << ", transactions: " << header.num_txes << std::endl + << ", size: " << header.block_size << ", weight: " << header.block_weight << ", transactions: " << header.num_txes << std::endl << "major version: " << (unsigned)header.major_version << ", minor version: " << (unsigned)header.minor_version << std::endl << "block id: " << header.hash << ", previous block id: " << header.prev_hash << std::endl << "difficulty: " << header.difficulty << ", nonce " << header.nonce << ", reward " << cryptonote::print_money(header.reward) << std::endl; @@ -857,8 +858,9 @@ bool t_rpc_command_executor::print_transaction_pool_long() { tools::msg_writer() << "id: " << tx_info.id_hash << std::endl << tx_info.tx_json << std::endl << "blob_size: " << tx_info.blob_size << std::endl + << "weight: " << tx_info.weight << std::endl << "fee: " << cryptonote::print_money(tx_info.fee) << std::endl - << "fee/byte: " << cryptonote::print_money(tx_info.fee / (double)tx_info.blob_size) << std::endl + << "fee/byte: " << cryptonote::print_money(tx_info.fee / (double)tx_info.weight) << std::endl << "receive_time: " << tx_info.receive_time << " (" << get_human_time_ago(tx_info.receive_time, now) << ")" << std::endl << "relayed: " << [&](const cryptonote::tx_info &tx_info)->std::string { if (!tx_info.relayed) return "no"; return boost::lexical_cast(tx_info.last_relayed_time) + " (" + get_human_time_ago(tx_info.last_relayed_time, now) + ")"; } (tx_info) << std::endl << "do_not_relay: " << (tx_info.do_not_relay ? 'T' : 'F') << std::endl @@ -940,8 +942,9 @@ bool t_rpc_command_executor::print_transaction_pool_short() { { tools::msg_writer() << "id: " << tx_info.id_hash << std::endl << "blob_size: " << tx_info.blob_size << std::endl + << "weight: " << tx_info.weight << std::endl << "fee: " << cryptonote::print_money(tx_info.fee) << std::endl - << "fee/byte: " << cryptonote::print_money(tx_info.fee / (double)tx_info.blob_size) << std::endl + << "fee/byte: " << cryptonote::print_money(tx_info.fee / (double)tx_info.weight) << std::endl << "receive_time: " << tx_info.receive_time << " (" << get_human_time_ago(tx_info.receive_time, now) << ")" << std::endl << "relayed: " << [&](const cryptonote::tx_info &tx_info)->std::string { if (!tx_info.relayed) return "no"; return boost::lexical_cast(tx_info.last_relayed_time) + " (" + get_human_time_ago(tx_info.last_relayed_time, now) + ")"; } (tx_info) << std::endl << "do_not_relay: " << (tx_info.do_not_relay ? 'T' : 'F') << std::endl @@ -996,7 +999,7 @@ bool t_rpc_command_executor::print_transaction_pool_stats() { size_t avg_bytes = n_transactions ? res.pool_stats.bytes_total / n_transactions : 0; std::string backlog_message; - const uint64_t full_reward_zone = ires.block_size_limit / 2; + const uint64_t full_reward_zone = ires.block_weight_limit / 2; if (res.pool_stats.bytes_total <= full_reward_zone) { backlog_message = "no backlog"; @@ -1701,8 +1704,8 @@ bool t_rpc_command_executor::print_blockchain_dynamic_stats(uint64_t nblocks) cryptonote::COMMAND_RPC_GET_INFO::response ires; cryptonote::COMMAND_RPC_GET_BLOCK_HEADERS_RANGE::request bhreq; cryptonote::COMMAND_RPC_GET_BLOCK_HEADERS_RANGE::response bhres; - cryptonote::COMMAND_RPC_GET_PER_KB_FEE_ESTIMATE::request fereq; - cryptonote::COMMAND_RPC_GET_PER_KB_FEE_ESTIMATE::response feres; + cryptonote::COMMAND_RPC_GET_BASE_FEE_ESTIMATE::request fereq; + cryptonote::COMMAND_RPC_GET_BASE_FEE_ESTIMATE::response feres; epee::json_rpc::error error_resp; std::string fail_message = "Problem fetching info"; @@ -1726,7 +1729,7 @@ bool t_rpc_command_executor::print_blockchain_dynamic_stats(uint64_t nblocks) tools::fail_msg_writer() << make_error(fail_message, ires.status); return true; } - if (!m_rpc_server->on_get_per_kb_fee_estimate(fereq, feres, error_resp) || feres.status != CORE_RPC_STATUS_OK) + if (!m_rpc_server->on_get_base_fee_estimate(fereq, feres, error_resp) || feres.status != CORE_RPC_STATUS_OK) { tools::fail_msg_writer() << make_error(fail_message, feres.status); return true; @@ -1762,8 +1765,8 @@ bool t_rpc_command_executor::print_blockchain_dynamic_stats(uint64_t nblocks) double avgdiff = 0; double avgnumtxes = 0; double avgreward = 0; - std::vector sizes; - sizes.reserve(nblocks); + std::vector weights; + weights.reserve(nblocks); uint64_t earliest = std::numeric_limits::max(), latest = 0; std::vector major_versions(256, 0), minor_versions(256, 0); for (const auto &bhr: bhres.headers) @@ -1771,7 +1774,7 @@ bool t_rpc_command_executor::print_blockchain_dynamic_stats(uint64_t nblocks) avgdiff += bhr.difficulty; avgnumtxes += bhr.num_txes; avgreward += bhr.reward; - sizes.push_back(bhr.block_size); + weights.push_back(bhr.block_weight); static_assert(sizeof(bhr.major_version) == 1, "major_version expected to be uint8_t"); static_assert(sizeof(bhr.minor_version) == 1, "major_version expected to be uint8_t"); major_versions[(unsigned)bhr.major_version]++; @@ -1782,9 +1785,9 @@ bool t_rpc_command_executor::print_blockchain_dynamic_stats(uint64_t nblocks) avgdiff /= nblocks; avgnumtxes /= nblocks; avgreward /= nblocks; - uint64_t median_block_size = epee::misc_utils::median(sizes); + uint64_t median_block_weight = epee::misc_utils::median(weights); tools::msg_writer() << "Last " << nblocks << ": avg. diff " << (uint64_t)avgdiff << ", " << (latest - earliest) / nblocks << " avg sec/block, avg num txes " << avgnumtxes - << ", avg. reward " << cryptonote::print_money(avgreward) << ", median block size " << median_block_size; + << ", avg. reward " << cryptonote::print_money(avgreward) << ", median block weight " << median_block_weight; unsigned int max_major = 256, max_minor = 256; while (max_major > 0 && !major_versions[--max_major]); diff --git a/src/ringct/rctSigs.cpp b/src/ringct/rctSigs.cpp index d2dc87e279d..dbbf29fd1d5 100644 --- a/src/ringct/rctSigs.cpp +++ b/src/ringct/rctSigs.cpp @@ -758,7 +758,20 @@ namespace rct { std::vector proof_amounts; size_t n_amounts = outamounts.size(); size_t amounts_proved = 0; - while (amounts_proved < n_amounts) + if (range_proof_type == RangeProofPaddedBulletproof) + { + rct::keyV C, masks; + rv.p.bulletproofs.push_back(proveRangeBulletproof(C, masks, outamounts)); + #ifdef DBG + CHECK_AND_ASSERT_THROW_MES(verBulletproof(rv.p.bulletproofs.back()), "verBulletproof failed on newly created proof"); + #endif + for (i = 0; i < outamounts.size(); ++i) + { + rv.outPk[i].mask = C[i]; + outSk[i].mask = masks[i]; + } + } + else while (amounts_proved < n_amounts) { size_t batch_size = 1; if (range_proof_type == RangeProofMultiOutputBulletproof) diff --git a/src/ringct/rctTypes.cpp b/src/ringct/rctTypes.cpp index 5dd59aec38a..90ed65df010 100644 --- a/src/ringct/rctTypes.cpp +++ b/src/ringct/rctTypes.cpp @@ -29,6 +29,7 @@ // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "misc_log_ex.h" +#include "cryptonote_config.h" #include "rctTypes.h" using namespace crypto; using namespace std; @@ -233,11 +234,29 @@ namespace rct { } } + bool is_rct_borromean(int type) + { + switch (type) + { + case RCTTypeSimple: + case RCTTypeFull: + return true; + default: + return false; + } + } + size_t n_bulletproof_amounts(const Bulletproof &proof) { CHECK_AND_ASSERT_MES(proof.L.size() >= 6, 0, "Invalid bulletproof L size"); - CHECK_AND_ASSERT_MES(proof.L.size() <= 31, 0, "Insane bulletproof L size"); - return 1 << (proof.L.size() - 6); + CHECK_AND_ASSERT_MES(proof.L.size() == proof.R.size(), 0, "Mismatched bulletproof L/R size"); + static const size_t extra_bits = 4; + static_assert((1 << extra_bits) == BULLETPROOF_MAX_OUTPUTS, "log2(BULLETPROOF_MAX_OUTPUTS) is out of date"); + CHECK_AND_ASSERT_MES(proof.L.size() <= 6 + extra_bits, 0, "Invalid bulletproof L size"); + CHECK_AND_ASSERT_MES(proof.V.size() <= (1u<<(proof.L.size()-6)), 0, "Invalid bulletproof V/L"); + CHECK_AND_ASSERT_MES(proof.V.size() * 2 > (1u<<(proof.L.size()-6)), 0, "Invalid bulletproof V/L"); + CHECK_AND_ASSERT_MES(proof.V.size() > 0, 0, "Empty bulletproof"); + return proof.V.size(); } size_t n_bulletproof_amounts(const std::vector &proofs) @@ -254,4 +273,28 @@ namespace rct { return n; } + size_t n_bulletproof_max_amounts(const Bulletproof &proof) + { + CHECK_AND_ASSERT_MES(proof.L.size() >= 6, 0, "Invalid bulletproof L size"); + CHECK_AND_ASSERT_MES(proof.L.size() == proof.R.size(), 0, "Mismatched bulletproof L/R size"); + static const size_t extra_bits = 4; + static_assert((1 << extra_bits) == BULLETPROOF_MAX_OUTPUTS, "log2(BULLETPROOF_MAX_OUTPUTS) is out of date"); + CHECK_AND_ASSERT_MES(proof.L.size() <= 6 + extra_bits, 0, "Invalid bulletproof L size"); + return 1 << (proof.L.size() - 6); + } + + size_t n_bulletproof_max_amounts(const std::vector &proofs) + { + size_t n = 0; + for (const Bulletproof &proof: proofs) + { + size_t n2 = n_bulletproof_max_amounts(proof); + CHECK_AND_ASSERT_MES(n2 < std::numeric_limits::max() - n, 0, "Invalid number of bulletproofs"); + if (n2 == 0) + return 0; + n += n2; + } + return n; + } + } diff --git a/src/ringct/rctTypes.h b/src/ringct/rctTypes.h index 894f747b880..5c02f92aa72 100644 --- a/src/ringct/rctTypes.h +++ b/src/ringct/rctTypes.h @@ -214,7 +214,9 @@ namespace rct { }; size_t n_bulletproof_amounts(const Bulletproof &proof); + size_t n_bulletproof_max_amounts(const Bulletproof &proof); size_t n_bulletproof_amounts(const std::vector &proofs); + size_t n_bulletproof_max_amounts(const std::vector &proofs); //A container to hold all signatures necessary for RingCT // rangeSigs holds all the rangeproof data of a transaction @@ -229,7 +231,7 @@ namespace rct { RCTTypeSimple = 2, RCTTypeBulletproof = 3, }; - enum RangeProofType { RangeProofBorromean, RangeProofBulletproof, RangeProofMultiOutputBulletproof }; + enum RangeProofType { RangeProofBorromean, RangeProofBulletproof, RangeProofMultiOutputBulletproof, RangeProofPaddedBulletproof }; struct rctSigBase { uint8_t type; key message; @@ -324,7 +326,7 @@ namespace rct { if (nbp - i > 1) ar.delimit_array(); } - if (n_bulletproof_amounts(bulletproofs) != outputs) + if (n_bulletproof_max_amounts(bulletproofs) < outputs) return false; ar.end_array(); } @@ -528,6 +530,7 @@ namespace rct { bool is_rct_simple(int type); bool is_rct_bulletproof(int type); + bool is_rct_borromean(int type); static inline const rct::key &pk2rct(const crypto::public_key &pk) { return (const rct::key&)pk; } static inline const rct::key &sk2rct(const crypto::secret_key &sk) { return (const rct::key&)sk; } diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index 0a6daf8f04d..4383ad190a9 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -195,8 +195,8 @@ namespace cryptonote res.stagenet = m_nettype == STAGENET; res.nettype = m_nettype == MAINNET ? "mainnet" : m_nettype == TESTNET ? "testnet" : m_nettype == STAGENET ? "stagenet" : "fakechain"; res.cumulative_difficulty = m_core.get_blockchain_storage().get_db().get_block_cumulative_difficulty(res.height - 1); - res.block_size_limit = m_core.get_blockchain_storage().get_current_cumulative_blocksize_limit(); - res.block_size_median = m_core.get_blockchain_storage().get_current_cumulative_blocksize_median(); + res.block_size_limit = res.block_weight_limit = m_core.get_blockchain_storage().get_current_cumulative_block_weight_limit(); + res.block_size_median = res.block_weight_median = m_core.get_blockchain_storage().get_current_cumulative_block_weight_median(); res.status = CORE_RPC_STATUS_OK; res.start_time = (uint64_t)m_core.get_start_time(); res.free_space = m_restricted ? std::numeric_limits::max() : m_core.get_free_space(); @@ -1313,7 +1313,7 @@ namespace cryptonote response.hash = string_tools::pod_to_hex(hash); response.difficulty = m_core.get_blockchain_storage().block_difficulty(height); response.reward = get_block_reward(blk); - response.block_size = m_core.get_blockchain_storage().get_db().get_block_size(height); + response.block_size = response.block_weight = m_core.get_blockchain_storage().get_db().get_block_weight(height); response.num_txes = blk.tx_hashes.size(); response.pow_hash = fill_pow_hash ? string_tools::pod_to_hex(get_block_longhash(blk, height)) : ""; return true; @@ -1646,8 +1646,8 @@ namespace cryptonote res.stagenet = m_nettype == STAGENET; res.nettype = m_nettype == MAINNET ? "mainnet" : m_nettype == TESTNET ? "testnet" : m_nettype == STAGENET ? "stagenet" : "fakechain"; res.cumulative_difficulty = m_core.get_blockchain_storage().get_db().get_block_cumulative_difficulty(res.height - 1); - res.block_size_limit = m_core.get_blockchain_storage().get_current_cumulative_blocksize_limit(); - res.block_size_median = m_core.get_blockchain_storage().get_current_cumulative_blocksize_median(); + res.block_size_limit = res.block_weight_limit = m_core.get_blockchain_storage().get_current_cumulative_block_weight_limit(); + res.block_size_median = res.block_weight_median = m_core.get_blockchain_storage().get_current_cumulative_block_weight_median(); res.status = CORE_RPC_STATUS_OK; res.start_time = (uint64_t)m_core.get_start_time(); res.free_space = m_restricted ? std::numeric_limits::max() : m_core.get_free_space(); @@ -1839,14 +1839,15 @@ namespace cryptonote return true; } //------------------------------------------------------------------------------------------------------------------------------ - bool core_rpc_server::on_get_per_kb_fee_estimate(const COMMAND_RPC_GET_PER_KB_FEE_ESTIMATE::request& req, COMMAND_RPC_GET_PER_KB_FEE_ESTIMATE::response& res, epee::json_rpc::error& error_resp) + bool core_rpc_server::on_get_base_fee_estimate(const COMMAND_RPC_GET_BASE_FEE_ESTIMATE::request& req, COMMAND_RPC_GET_BASE_FEE_ESTIMATE::response& res, epee::json_rpc::error& error_resp) { - PERF_TIMER(on_get_per_kb_fee_estimate); + PERF_TIMER(on_get_base_fee_estimate); bool r; - if (use_bootstrap_daemon_if_necessary(invoke_http_mode::JON_RPC, "get_fee_estimate", req, res, r)) + if (use_bootstrap_daemon_if_necessary(invoke_http_mode::JON_RPC, "get_fee_estimate", req, res, r)) return r; - res.fee = m_core.get_blockchain_storage().get_dynamic_per_kb_fee_estimate(req.grace_blocks); + res.fee = m_core.get_blockchain_storage().get_dynamic_base_fee_estimate(req.grace_blocks); + res.quantization_mask = Blockchain::get_fee_quantization_mask(); res.status = CORE_RPC_STATUS_OK; return true; } diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h index 166020d018d..5dbe44d247a 100644 --- a/src/rpc/core_rpc_server.h +++ b/src/rpc/core_rpc_server.h @@ -150,7 +150,7 @@ namespace cryptonote MAP_JON_RPC_WE("get_output_histogram", on_get_output_histogram, COMMAND_RPC_GET_OUTPUT_HISTOGRAM) MAP_JON_RPC_WE("get_version", on_get_version, COMMAND_RPC_GET_VERSION) MAP_JON_RPC_WE_IF("get_coinbase_tx_sum", on_get_coinbase_tx_sum, COMMAND_RPC_GET_COINBASE_TX_SUM, !m_restricted) - MAP_JON_RPC_WE("get_fee_estimate", on_get_per_kb_fee_estimate, COMMAND_RPC_GET_PER_KB_FEE_ESTIMATE) + MAP_JON_RPC_WE("get_fee_estimate", on_get_base_fee_estimate, COMMAND_RPC_GET_BASE_FEE_ESTIMATE) MAP_JON_RPC_WE_IF("get_alternate_chains",on_get_alternate_chains, COMMAND_RPC_GET_ALTERNATE_CHAINS, !m_restricted) MAP_JON_RPC_WE_IF("relay_tx", on_relay_tx, COMMAND_RPC_RELAY_TX, !m_restricted) MAP_JON_RPC_WE_IF("sync_info", on_sync_info, COMMAND_RPC_SYNC_INFO, !m_restricted) @@ -214,7 +214,7 @@ namespace cryptonote bool on_get_output_histogram(const COMMAND_RPC_GET_OUTPUT_HISTOGRAM::request& req, COMMAND_RPC_GET_OUTPUT_HISTOGRAM::response& res, epee::json_rpc::error& error_resp); bool on_get_version(const COMMAND_RPC_GET_VERSION::request& req, COMMAND_RPC_GET_VERSION::response& res, epee::json_rpc::error& error_resp); bool on_get_coinbase_tx_sum(const COMMAND_RPC_GET_COINBASE_TX_SUM::request& req, COMMAND_RPC_GET_COINBASE_TX_SUM::response& res, epee::json_rpc::error& error_resp); - bool on_get_per_kb_fee_estimate(const COMMAND_RPC_GET_PER_KB_FEE_ESTIMATE::request& req, COMMAND_RPC_GET_PER_KB_FEE_ESTIMATE::response& res, epee::json_rpc::error& error_resp); + bool on_get_base_fee_estimate(const COMMAND_RPC_GET_BASE_FEE_ESTIMATE::request& req, COMMAND_RPC_GET_BASE_FEE_ESTIMATE::response& res, epee::json_rpc::error& error_resp); bool on_get_alternate_chains(const COMMAND_RPC_GET_ALTERNATE_CHAINS::request& req, COMMAND_RPC_GET_ALTERNATE_CHAINS::response& res, epee::json_rpc::error& error_resp); bool on_relay_tx(const COMMAND_RPC_RELAY_TX::request& req, COMMAND_RPC_RELAY_TX::response& res, epee::json_rpc::error& error_resp); bool on_sync_info(const COMMAND_RPC_SYNC_INFO::request& req, COMMAND_RPC_SYNC_INFO::response& res, epee::json_rpc::error& error_resp); diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index e2d120cad90..da4c8b6cfa6 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -48,8 +48,8 @@ namespace cryptonote // whether they can talk to a given daemon without having to know in // advance which version they will stop working with // Don't go over 32767 for any of these -#define CORE_RPC_VERSION_MAJOR 1 -#define CORE_RPC_VERSION_MINOR 21 +#define CORE_RPC_VERSION_MAJOR 2 +#define CORE_RPC_VERSION_MINOR 0 #define MAKE_CORE_RPC_VERSION(major,minor) (((major)<<16)|(minor)) #define CORE_RPC_VERSION MAKE_CORE_RPC_VERSION(CORE_RPC_VERSION_MAJOR, CORE_RPC_VERSION_MINOR) @@ -959,7 +959,9 @@ namespace cryptonote std::string top_block_hash; uint64_t cumulative_difficulty; uint64_t block_size_limit; + uint64_t block_weight_limit; uint64_t block_size_median; + uint64_t block_weight_median; uint64_t start_time; uint64_t free_space; bool offline; @@ -990,7 +992,9 @@ namespace cryptonote KV_SERIALIZE(top_block_hash) KV_SERIALIZE(cumulative_difficulty) KV_SERIALIZE(block_size_limit) + KV_SERIALIZE_OPT(block_weight_limit, (uint64_t)0) KV_SERIALIZE(block_size_median) + KV_SERIALIZE_OPT(block_weight_median, (uint64_t)0) KV_SERIALIZE(start_time) KV_SERIALIZE(free_space) KV_SERIALIZE(offline) @@ -1195,6 +1199,7 @@ namespace cryptonote difficulty_type difficulty; uint64_t reward; uint64_t block_size; + uint64_t block_weight; uint64_t num_txes; std::string pow_hash; @@ -1211,6 +1216,7 @@ namespace cryptonote KV_SERIALIZE(difficulty) KV_SERIALIZE(reward) KV_SERIALIZE(block_size) + KV_SERIALIZE_OPT(block_weight, (uint64_t)0) KV_SERIALIZE(num_txes) KV_SERIALIZE(pow_hash) END_KV_SERIALIZE_MAP() @@ -1451,6 +1457,7 @@ namespace cryptonote std::string id_hash; std::string tx_json; // TODO - expose this data directly uint64_t blob_size; + uint64_t weight; uint64_t fee; std::string max_used_block_id_hash; uint64_t max_used_block_height; @@ -1468,6 +1475,7 @@ namespace cryptonote KV_SERIALIZE(id_hash) KV_SERIALIZE(tx_json) KV_SERIALIZE(blob_size) + KV_SERIALIZE_OPT(weight, (uint64_t)0) KV_SERIALIZE(fee) KV_SERIALIZE(max_used_block_id_hash) KV_SERIALIZE(max_used_block_height) @@ -1564,7 +1572,7 @@ namespace cryptonote struct tx_backlog_entry { - uint64_t blob_size; + uint64_t weight; uint64_t fee; uint64_t time_in_pool; }; @@ -2102,7 +2110,7 @@ namespace cryptonote }; }; - struct COMMAND_RPC_GET_PER_KB_FEE_ESTIMATE + struct COMMAND_RPC_GET_BASE_FEE_ESTIMATE { struct request { @@ -2117,11 +2125,13 @@ namespace cryptonote { std::string status; uint64_t fee; + uint64_t quantization_mask; bool untrusted; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(status) KV_SERIALIZE(fee) + KV_SERIALIZE_OPT(quantization_mask, (uint64_t)1) KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; diff --git a/src/rpc/daemon_handler.cpp b/src/rpc/daemon_handler.cpp index 25abe48250e..26f102a8b36 100644 --- a/src/rpc/daemon_handler.cpp +++ b/src/rpc/daemon_handler.cpp @@ -472,7 +472,8 @@ namespace rpc res.info.testnet = m_core.get_nettype() == TESTNET; res.info.stagenet = m_core.get_nettype() == STAGENET; res.info.cumulative_difficulty = m_core.get_blockchain_storage().get_db().get_block_cumulative_difficulty(res.info.height - 1); - res.info.block_size_limit = m_core.get_blockchain_storage().get_current_cumulative_blocksize_limit(); + res.info.block_size_limit = res.info.block_weight_limit = m_core.get_blockchain_storage().get_current_cumulative_block_weight_limit(); + res.info.block_size_median = res.info.block_weight_median = m_core.get_blockchain_storage().get_current_cumulative_block_weight_median(); res.info.start_time = (uint64_t)m_core.get_start_time(); res.status = Message::STATUS_OK; @@ -763,7 +764,7 @@ namespace rpc void DaemonHandler::handle(const GetPerKBFeeEstimate::Request& req, GetPerKBFeeEstimate::Response& res) { - res.estimated_fee_per_kb = m_core.get_blockchain_storage().get_dynamic_per_kb_fee_estimate(req.num_grace_blocks); + res.estimated_fee_per_kb = m_core.get_blockchain_storage().get_dynamic_base_fee_estimate(req.num_grace_blocks); res.status = Message::STATUS_OK; } diff --git a/src/rpc/message_data_structs.h b/src/rpc/message_data_structs.h index 20390aee84e..cf15ade1cf8 100644 --- a/src/rpc/message_data_structs.h +++ b/src/rpc/message_data_structs.h @@ -85,6 +85,7 @@ namespace rpc cryptonote::transaction tx; crypto::hash tx_hash; uint64_t blob_size; + uint64_t weight; uint64_t fee; crypto::hash max_used_block_hash; uint64_t max_used_block_height; @@ -185,6 +186,9 @@ namespace rpc crypto::hash top_block_hash; uint64_t cumulative_difficulty; uint64_t block_size_limit; + uint64_t block_weight_limit; + uint64_t block_size_median; + uint64_t block_weight_median; uint64_t start_time; }; diff --git a/src/serialization/json_object.cpp b/src/serialization/json_object.cpp index 67fe709dcef..89a1dbd2392 100644 --- a/src/serialization/json_object.cpp +++ b/src/serialization/json_object.cpp @@ -757,6 +757,7 @@ void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::tx_in_pool& tx INSERT_INTO_JSON_OBJECT(val, doc, tx, tx.tx); INSERT_INTO_JSON_OBJECT(val, doc, tx_hash, tx.tx_hash); INSERT_INTO_JSON_OBJECT(val, doc, blob_size, tx.blob_size); + INSERT_INTO_JSON_OBJECT(val, doc, weight, tx.weight); INSERT_INTO_JSON_OBJECT(val, doc, fee, tx.fee); INSERT_INTO_JSON_OBJECT(val, doc, max_used_block_hash, tx.max_used_block_hash); INSERT_INTO_JSON_OBJECT(val, doc, max_used_block_height, tx.max_used_block_height); @@ -780,6 +781,7 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::tx_in_pool& tx) GET_FROM_JSON_OBJECT(val, tx.tx, tx); GET_FROM_JSON_OBJECT(val, tx.blob_size, blob_size); + GET_FROM_JSON_OBJECT(val, tx.weight, weight); GET_FROM_JSON_OBJECT(val, tx.fee, fee); GET_FROM_JSON_OBJECT(val, tx.max_used_block_hash, max_used_block_hash); GET_FROM_JSON_OBJECT(val, tx.max_used_block_height, max_used_block_height); @@ -1195,6 +1197,9 @@ void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::DaemonInfo& in INSERT_INTO_JSON_OBJECT(val, doc, top_block_hash, info.top_block_hash); INSERT_INTO_JSON_OBJECT(val, doc, cumulative_difficulty, info.cumulative_difficulty); INSERT_INTO_JSON_OBJECT(val, doc, block_size_limit, info.block_size_limit); + INSERT_INTO_JSON_OBJECT(val, doc, block_weight_limit, info.block_weight_limit); + INSERT_INTO_JSON_OBJECT(val, doc, block_size_median, info.block_size_median); + INSERT_INTO_JSON_OBJECT(val, doc, block_weight_median, info.block_weight_median); INSERT_INTO_JSON_OBJECT(val, doc, start_time, info.start_time); } @@ -1221,6 +1226,9 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::DaemonInfo& inf GET_FROM_JSON_OBJECT(val, info.top_block_hash, top_block_hash); GET_FROM_JSON_OBJECT(val, info.cumulative_difficulty, cumulative_difficulty); GET_FROM_JSON_OBJECT(val, info.block_size_limit, block_size_limit); + GET_FROM_JSON_OBJECT(val, info.block_weight_limit, block_weight_limit); + GET_FROM_JSON_OBJECT(val, info.block_size_median, block_size_median); + GET_FROM_JSON_OBJECT(val, info.block_weight_median, block_weight_median); GET_FROM_JSON_OBJECT(val, info.start_time, start_time); } diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index f5aeabded1b..bcdf2a43f2f 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -86,9 +86,9 @@ typedef cryptonote::simple_wallet sw; #define EXTENDED_LOGS_FILE "wallet_details.log" -#define DEFAULT_MIX 6 +#define DEFAULT_MIX 10 -#define MIN_RING_SIZE 7 // Used to inform user about min ring size -- does not track actual protocol +#define MIN_RING_SIZE 11 // Used to inform user about min ring size -- does not track actual protocol #define LOCK_IDLE_SCOPE() \ bool auto_refresh_enabled = m_auto_refresh_enabled.load(std::memory_order_relaxed); \ @@ -829,21 +829,24 @@ bool simple_wallet::print_fee_info(const std::vector &args/* = std: fail_msg_writer() << tr("Cannot connect to daemon"); return true; } - const uint64_t per_kb_fee = m_wallet->get_per_kb_fee(); - const uint64_t typical_size_kb = 13; - message_writer() << (boost::format(tr("Current fee is %s %s per kB")) % print_money(per_kb_fee) % cryptonote::get_unit(cryptonote::get_default_decimal_point())).str(); + const bool per_byte = m_wallet->use_fork_rules(HF_VERSION_PER_BYTE_FEE); + const uint64_t base_fee = m_wallet->get_base_fee(); + const char *base = per_byte ? "byte" : "kB"; + const uint64_t typical_size = per_byte ? 2500 : 13; + const uint64_t size_granularity = per_byte ? 1 : 1024; + message_writer() << (boost::format(tr("Current fee is %s %s per %s")) % print_money(base_fee) % cryptonote::get_unit(cryptonote::get_default_decimal_point()) % base).str(); std::vector fees; for (uint32_t priority = 1; priority <= 4; ++priority) { uint64_t mult = m_wallet->get_fee_multiplier(priority); - fees.push_back(per_kb_fee * typical_size_kb * mult); + fees.push_back(base_fee * typical_size * mult); } std::vector> blocks; try { - uint64_t base_size = typical_size_kb * 1024; - blocks = m_wallet->estimate_backlog(base_size, base_size + 1023, fees); + uint64_t base_size = typical_size * size_granularity; + blocks = m_wallet->estimate_backlog(base_size, base_size + size_granularity - 1, fees); } catch (const std::exception &e) { @@ -1839,6 +1842,8 @@ bool simple_wallet::set_default_ring_size(const std::vector &args/* if (ring_size != 0 && ring_size != DEFAULT_MIX+1) message_writer() << tr("WARNING: this is a non default ring size, which may harm your privacy. Default is recommended."); + else if (ring_size == DEFAULT_MIX) + message_writer() << tr("WARNING: from v8, ring size will be fixed and this setting will be ignored."); const auto pwd_container = get_and_verify_password(); if (pwd_container) @@ -4704,6 +4709,11 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector &args_) if (fake_outs_count == 0) fake_outs_count = DEFAULT_MIX; } + else if (ring_size == 0) + { + fail_msg_writer() << tr("Ring size must not be 0"); + return true; + } else { fake_outs_count = ring_size - 1; local_args.erase(local_args.begin()); } } + uint64_t adjusted_fake_outs_count = m_wallet->adjust_mixin(fake_outs_count); + if (adjusted_fake_outs_count > fake_outs_count) + { + fail_msg_writer() << (boost::format(tr("ring size %u is too small, minimum is %u")) % (fake_outs_count+1) % (adjusted_fake_outs_count+1)).str(); + return true; + } + if (adjusted_fake_outs_count < fake_outs_count) + { + fail_msg_writer() << (boost::format(tr("ring size %u is too large, maximum is %u")) % (fake_outs_count+1) % (adjusted_fake_outs_count+1)).str(); + return true; + } std::vector extra; bool payment_id_seen = false; diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index 7b4ad27e4f9..3780d72716c 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -1297,6 +1297,7 @@ PendingTransaction *WalletImpl::createTransaction(const string &dst_addr, const size_t fake_outs_count = mixin_count > 0 ? mixin_count : m_wallet->default_mixin(); if (fake_outs_count == 0) fake_outs_count = DEFAULT_MIXIN; + fake_outs_count = m_wallet->adjust_mixin(fake_outs_count); uint32_t adjusted_priority = m_wallet->adjust_priority(static_cast(priority)); diff --git a/src/wallet/node_rpc_proxy.cpp b/src/wallet/node_rpc_proxy.cpp index 2072495cde1..21f75371b59 100644 --- a/src/wallet/node_rpc_proxy.cpp +++ b/src/wallet/node_rpc_proxy.cpp @@ -50,12 +50,13 @@ void NodeRPCProxy::invalidate() m_height = 0; for (size_t n = 0; n < 256; ++n) m_earliest_height[n] = 0; - m_dynamic_per_kb_fee_estimate = 0; - m_dynamic_per_kb_fee_estimate_cached_height = 0; - m_dynamic_per_kb_fee_estimate_grace_blocks = 0; + m_dynamic_base_fee_estimate = 0; + m_dynamic_base_fee_estimate_cached_height = 0; + m_dynamic_base_fee_estimate_grace_blocks = 0; + m_fee_quantization_mask = 1; m_rpc_version = 0; m_target_height = 0; - m_block_size_limit = 0; + m_block_weight_limit = 0; m_get_info_time = 0; } @@ -99,7 +100,7 @@ boost::optional NodeRPCProxy::get_info() const CHECK_AND_ASSERT_MES(resp_t.status == CORE_RPC_STATUS_OK, resp_t.status, "Failed to get target blockchain height"); m_height = resp_t.height; m_target_height = resp_t.target_height; - m_block_size_limit = resp_t.block_size_limit; + m_block_weight_limit = resp_t.block_weight_limit ? resp_t.block_weight_limit : resp_t.block_size_limit; m_get_info_time = now; } return boost::optional(); @@ -123,12 +124,12 @@ boost::optional NodeRPCProxy::get_target_height(uint64_t &height) c return boost::optional(); } -boost::optional NodeRPCProxy::get_block_size_limit(uint64_t &block_size_limit) const +boost::optional NodeRPCProxy::get_block_weight_limit(uint64_t &block_weight_limit) const { auto res = get_info(); if (res) return res; - block_size_limit = m_block_size_limit; + block_weight_limit = m_block_weight_limit; return boost::optional(); } @@ -153,7 +154,7 @@ boost::optional NodeRPCProxy::get_earliest_height(uint8_t version, return boost::optional(); } -boost::optional NodeRPCProxy::get_dynamic_per_kb_fee_estimate(uint64_t grace_blocks, uint64_t &fee) const +boost::optional NodeRPCProxy::get_dynamic_base_fee_estimate(uint64_t grace_blocks, uint64_t &fee) const { uint64_t height; @@ -161,10 +162,10 @@ boost::optional NodeRPCProxy::get_dynamic_per_kb_fee_estimate(uint6 if (result) return result; - if (m_dynamic_per_kb_fee_estimate_cached_height != height || m_dynamic_per_kb_fee_estimate_grace_blocks != grace_blocks) + if (m_dynamic_base_fee_estimate_cached_height != height || m_dynamic_base_fee_estimate_grace_blocks != grace_blocks) { - cryptonote::COMMAND_RPC_GET_PER_KB_FEE_ESTIMATE::request req_t = AUTO_VAL_INIT(req_t); - cryptonote::COMMAND_RPC_GET_PER_KB_FEE_ESTIMATE::response resp_t = AUTO_VAL_INIT(resp_t); + cryptonote::COMMAND_RPC_GET_BASE_FEE_ESTIMATE::request req_t = AUTO_VAL_INIT(req_t); + cryptonote::COMMAND_RPC_GET_BASE_FEE_ESTIMATE::response resp_t = AUTO_VAL_INIT(resp_t); m_daemon_rpc_mutex.lock(); req_t.grace_blocks = grace_blocks; @@ -173,12 +174,47 @@ boost::optional NodeRPCProxy::get_dynamic_per_kb_fee_estimate(uint6 CHECK_AND_ASSERT_MES(r, std::string(), "Failed to connect to daemon"); CHECK_AND_ASSERT_MES(resp_t.status != CORE_RPC_STATUS_BUSY, resp_t.status, "Failed to connect to daemon"); CHECK_AND_ASSERT_MES(resp_t.status == CORE_RPC_STATUS_OK, resp_t.status, "Failed to get fee estimate"); - m_dynamic_per_kb_fee_estimate = resp_t.fee; - m_dynamic_per_kb_fee_estimate_cached_height = height; - m_dynamic_per_kb_fee_estimate_grace_blocks = grace_blocks; + m_dynamic_base_fee_estimate = resp_t.fee; + m_dynamic_base_fee_estimate_cached_height = height; + m_dynamic_base_fee_estimate_grace_blocks = grace_blocks; + m_fee_quantization_mask = resp_t.quantization_mask; } - fee = m_dynamic_per_kb_fee_estimate; + fee = m_dynamic_base_fee_estimate; + return boost::optional(); +} + +boost::optional NodeRPCProxy::get_fee_quantization_mask(uint64_t &fee_quantization_mask) const +{ + uint64_t height; + + boost::optional result = get_height(height); + if (result) + return result; + + if (m_dynamic_base_fee_estimate_cached_height != height) + { + cryptonote::COMMAND_RPC_GET_BASE_FEE_ESTIMATE::request req_t = AUTO_VAL_INIT(req_t); + cryptonote::COMMAND_RPC_GET_BASE_FEE_ESTIMATE::response resp_t = AUTO_VAL_INIT(resp_t); + + m_daemon_rpc_mutex.lock(); + req_t.grace_blocks = m_dynamic_base_fee_estimate_grace_blocks; + bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_fee_estimate", req_t, resp_t, m_http_client, rpc_timeout); + m_daemon_rpc_mutex.unlock(); + CHECK_AND_ASSERT_MES(r, std::string(), "Failed to connect to daemon"); + CHECK_AND_ASSERT_MES(resp_t.status != CORE_RPC_STATUS_BUSY, resp_t.status, "Failed to connect to daemon"); + CHECK_AND_ASSERT_MES(resp_t.status == CORE_RPC_STATUS_OK, resp_t.status, "Failed to get fee estimate"); + m_dynamic_base_fee_estimate = resp_t.fee; + m_dynamic_base_fee_estimate_cached_height = height; + m_fee_quantization_mask = resp_t.quantization_mask; + } + + fee_quantization_mask = m_fee_quantization_mask; + if (fee_quantization_mask == 0) + { + MERROR("Fee quantization mask is 0, forcing to 1"); + fee_quantization_mask = 1; + } return boost::optional(); } diff --git a/src/wallet/node_rpc_proxy.h b/src/wallet/node_rpc_proxy.h index 8a65884f71d..65f13eaaae8 100644 --- a/src/wallet/node_rpc_proxy.h +++ b/src/wallet/node_rpc_proxy.h @@ -47,9 +47,10 @@ class NodeRPCProxy boost::optional get_height(uint64_t &height) const; void set_height(uint64_t h); boost::optional get_target_height(uint64_t &height) const; - boost::optional get_block_size_limit(uint64_t &block_size_limit) const; + boost::optional get_block_weight_limit(uint64_t &block_weight_limit) const; boost::optional get_earliest_height(uint8_t version, uint64_t &earliest_height) const; - boost::optional get_dynamic_per_kb_fee_estimate(uint64_t grace_blocks, uint64_t &fee) const; + boost::optional get_dynamic_base_fee_estimate(uint64_t grace_blocks, uint64_t &fee) const; + boost::optional get_fee_quantization_mask(uint64_t &fee_quantization_mask) const; private: boost::optional get_info() const; @@ -59,12 +60,13 @@ class NodeRPCProxy mutable uint64_t m_height; mutable uint64_t m_earliest_height[256]; - mutable uint64_t m_dynamic_per_kb_fee_estimate; - mutable uint64_t m_dynamic_per_kb_fee_estimate_cached_height; - mutable uint64_t m_dynamic_per_kb_fee_estimate_grace_blocks; + mutable uint64_t m_dynamic_base_fee_estimate; + mutable uint64_t m_dynamic_base_fee_estimate_cached_height; + mutable uint64_t m_dynamic_base_fee_estimate_grace_blocks; + mutable uint64_t m_fee_quantization_mask; mutable uint32_t m_rpc_version; mutable uint64_t m_target_height; - mutable uint64_t m_block_size_limit; + mutable uint64_t m_block_weight_limit; mutable time_t m_get_info_time; }; diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 37340e22b75..6c0aec4a408 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -84,8 +84,8 @@ using namespace cryptonote; // used to choose when to stop adding outputs to a tx #define APPROXIMATE_INPUT_BYTES 80 -// used to target a given block size (additional outputs may be added on top to build fee) -#define TX_SIZE_TARGET(bytes) (bytes*2/3) +// used to target a given block weight (additional outputs may be added on top to build fee) +#define TX_WEIGHT_TARGET(bytes) (bytes*2/3) // arbitrary, used to generate different hashes from the same input #define CHACHA8_KEY_TAIL 0x8c @@ -183,19 +183,21 @@ uint64_t calculate_fee(uint64_t fee_per_kb, size_t bytes, uint64_t fee_multiplie return kB * fee_per_kb * fee_multiplier; } -uint64_t calculate_fee(uint64_t fee_per_kb, const cryptonote::blobdata &blob, uint64_t fee_multiplier) +uint64_t calculate_fee_from_weight(uint64_t base_fee, uint64_t weight, uint64_t fee_multiplier, uint64_t fee_quantization_mask) { - return calculate_fee(fee_per_kb, blob.size(), fee_multiplier); + uint64_t fee = weight * base_fee * fee_multiplier; + fee = (fee + fee_quantization_mask - 1) / fee_quantization_mask * fee_quantization_mask; + return fee; } -std::string get_size_string(size_t sz) +std::string get_weight_string(size_t weight) { - return std::to_string(sz) + " bytes (" + std::to_string((sz + 1023) / 1024) + " kB)"; + return std::to_string(weight) + " weight"; } -std::string get_size_string(const cryptonote::blobdata &tx) +std::string get_weight_string(const cryptonote::transaction &tx, size_t blob_size) { - return get_size_string(tx.size()); + return get_weight_string(get_transaction_weight(tx, blob_size)); } std::unique_ptr make_basic(const boost::program_options::variables_map& vm, bool rpc, const options& opts, const std::function(const char *, bool)> &password_prompter) @@ -576,7 +578,12 @@ size_t estimate_rct_tx_size(int n_inputs, int mixin, int n_outputs, size_t extra // rangeSigs if (bulletproof) - size += ((2*6 + 4 + 5)*32 + 3) * n_outputs; + { + size_t log_padded_outputs = 0; + while ((1< 2) + { + const uint64_t bp_base = 368; + size_t log_padded_outputs = 2; + while ((1< wallet2::make_dummy(const boost::program_options::varia } //---------------------------------------------------------------------------------------------------- -bool wallet2::init(bool rpc, std::string daemon_address, boost::optional daemon_login, uint64_t upper_transaction_size_limit, bool ssl, bool trusted_daemon) +bool wallet2::init(bool rpc, std::string daemon_address, boost::optional daemon_login, uint64_t upper_transaction_weight_limit, bool ssl, bool trusted_daemon) { m_rpc = rpc; m_checkpoints.init_default_checkpoints(m_nettype); if(m_http_client.is_connected()) m_http_client.disconnect(); m_is_initialized = true; - m_upper_transaction_size_limit = upper_transaction_size_limit; + m_upper_transaction_weight_limit = upper_transaction_weight_limit; m_daemon_address = std::move(daemon_address); m_daemon_login = std::move(daemon_login); m_trusted_daemon = trusted_daemon; @@ -5273,7 +5320,7 @@ bool wallet2::sign_tx(unsigned_tx_set &exported_txs, std::vector 1) - range_proof_type = rct::RangeProofMultiOutputBulletproof; + range_proof_type = rct::RangeProofPaddedBulletproof; } crypto::secret_key tx_key; std::vector additional_tx_keys; @@ -5697,7 +5744,7 @@ bool wallet2::sign_multisig_tx(multisig_tx_set &exported_txs, std::vector 1) - range_proof_type = rct::RangeProofMultiOutputBulletproof; + range_proof_type = rct::RangeProofPaddedBulletproof; } bool r = cryptonote::construct_tx_with_tx_key(m_account.get_keys(), m_subaddresses, sources, sd.splitted_dsts, ptx.change_dts.addr, sd.extra, tx, sd.unlock_time, ptx.tx_key, ptx.additional_tx_keys, sd.use_rct, range_proof_type, &msout, false); THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sd.sources, sd.splitted_dsts, sd.unlock_time, m_nettype); @@ -5799,9 +5846,18 @@ bool wallet2::sign_multisig_tx_from_file(const std::string &filename, std::vecto //---------------------------------------------------------------------------------------------------- uint64_t wallet2::get_fee_multiplier(uint32_t priority, int fee_algorithm) const { - static const uint64_t old_multipliers[3] = {1, 2, 3}; - static const uint64_t new_multipliers[3] = {1, 20, 166}; - static const uint64_t newer_multipliers[4] = {1, 4, 20, 166}; + static const struct + { + size_t count; + uint64_t multipliers[4]; + } + multipliers[] = + { + { 3, {1, 2, 3} }, + { 3, {1, 20, 166} }, + { 4, {1, 4, 20, 166} }, + { 4, {1, 5, 25, 1000} }, + }; if (fee_algorithm == -1) fee_algorithm = get_fee_algorithm(); @@ -5817,47 +5873,68 @@ uint64_t wallet2::get_fee_multiplier(uint32_t priority, int fee_algorithm) const priority = 1; } + THROW_WALLET_EXCEPTION_IF(fee_algorithm < 0 || fee_algorithm > 3, error::invalid_priority); + // 1 to 3/4 are allowed as priorities - uint32_t max_priority = (fee_algorithm >= 2) ? 4 : 3; + const uint32_t max_priority = multipliers[fee_algorithm].count; if (priority >= 1 && priority <= max_priority) { - switch (fee_algorithm) - { - case 0: return old_multipliers[priority-1]; - case 1: return new_multipliers[priority-1]; - case 2: return newer_multipliers[priority-1]; - default: THROW_WALLET_EXCEPTION_IF (true, error::invalid_priority); - } + return multipliers[fee_algorithm].multipliers[priority-1]; } THROW_WALLET_EXCEPTION_IF (false, error::invalid_priority); return 1; } //---------------------------------------------------------------------------------------------------- -uint64_t wallet2::get_dynamic_per_kb_fee_estimate() const +uint64_t wallet2::get_dynamic_base_fee_estimate() const { uint64_t fee; - boost::optional result = m_node_rpc_proxy.get_dynamic_per_kb_fee_estimate(FEE_ESTIMATE_GRACE_BLOCKS, fee); + boost::optional result = m_node_rpc_proxy.get_dynamic_base_fee_estimate(FEE_ESTIMATE_GRACE_BLOCKS, fee); if (!result) return fee; - LOG_PRINT_L1("Failed to query per kB fee, using " << print_money(FEE_PER_KB)); - return FEE_PER_KB; + const uint64_t base_fee = use_fork_rules(HF_VERSION_PER_BYTE_FEE) ? FEE_PER_BYTE : FEE_PER_KB; + LOG_PRINT_L1("Failed to query base fee, using " << print_money(base_fee)); + return base_fee; } //---------------------------------------------------------------------------------------------------- -uint64_t wallet2::get_per_kb_fee() const +uint64_t wallet2::get_base_fee() const { if(m_light_wallet) - return m_light_wallet_per_kb_fee; + { + if (use_fork_rules(HF_VERSION_PER_BYTE_FEE)) + return m_light_wallet_per_kb_fee / 1024; + else + return m_light_wallet_per_kb_fee; + } bool use_dyn_fee = use_fork_rules(HF_VERSION_DYNAMIC_FEE, -720 * 1); if (!use_dyn_fee) return FEE_PER_KB; - return get_dynamic_per_kb_fee_estimate(); + return get_dynamic_base_fee_estimate(); +} +//---------------------------------------------------------------------------------------------------- +uint64_t wallet2::get_fee_quantization_mask() const +{ + if(m_light_wallet) + { + return 1; // TODO + } + bool use_per_byte_fee = use_fork_rules(HF_VERSION_PER_BYTE_FEE, 0); + if (!use_per_byte_fee) + return 1; + + uint64_t fee_quantization_mask; + boost::optional result = m_node_rpc_proxy.get_fee_quantization_mask(fee_quantization_mask); + if (result) + return 1; + return fee_quantization_mask; } //---------------------------------------------------------------------------------------------------- int wallet2::get_fee_algorithm() const { - // changes at v3 and v5 + // changes at v3, v5, v8 + if (use_fork_rules(HF_VERSION_PER_BYTE_FEE, 0)) + return 3; if (use_fork_rules(5, 0)) return 2; if (use_fork_rules(3, -720 * 14)) @@ -5865,19 +5942,39 @@ int wallet2::get_fee_algorithm() const return 0; } //------------------------------------------------------------------------------------------------------------------------------ +uint64_t wallet2::get_min_ring_size() const +{ + if (use_fork_rules(8, 10)) + return 11; + if (use_fork_rules(7, 10)) + return 7; + if (use_fork_rules(6, 10)) + return 5; + if (use_fork_rules(2, 10)) + return 3; + return 0; +} +//------------------------------------------------------------------------------------------------------------------------------ +uint64_t wallet2::get_max_ring_size() const +{ + if (use_fork_rules(8, 10)) + return 11; + return 0; +} +//------------------------------------------------------------------------------------------------------------------------------ uint64_t wallet2::adjust_mixin(uint64_t mixin) const { - if (mixin < 6 && use_fork_rules(7, 10)) { - MWARNING("Requested ring size " << (mixin + 1) << " too low for hard fork 7, using 7"); - mixin = 6; - } - else if (mixin < 4 && use_fork_rules(6, 10)) { - MWARNING("Requested ring size " << (mixin + 1) << " too low for hard fork 6, using 5"); - mixin = 4; + const uint64_t min_ring_size = get_min_ring_size(); + if (mixin + 1 < min_ring_size) + { + MWARNING("Requested ring size " << (mixin + 1) << " too low, using " << min_ring_size); + mixin = min_ring_size-1; } - else if (mixin < 2 && use_fork_rules(2, 10)) { - MWARNING("Requested ring size " << (mixin + 1) << " too low for hard fork 2, using 3"); - mixin = 2; + const uint64_t max_ring_size = get_max_ring_size(); + if (max_ring_size && mixin + 1 > max_ring_size) + { + MWARNING("Requested ring size " << (mixin + 1) << " too high, using " << max_ring_size); + mixin = max_ring_size-1; } return mixin; } @@ -5889,7 +5986,10 @@ uint32_t wallet2::adjust_priority(uint32_t priority) try { // check if there's a backlog in the tx pool - const double fee_level = get_fee_multiplier(1) * get_per_kb_fee() * (12/(double)13) / (double)1024; + const bool use_per_byte_fee = use_fork_rules(HF_VERSION_PER_BYTE_FEE, 0); + const uint64_t base_fee = get_base_fee(); + const uint64_t fee_multiplier = get_fee_multiplier(1); + const double fee_level = fee_multiplier * base_fee * (use_per_byte_fee ? 1 : (12/(double)13 / (double)1024)); const std::vector> blocks = estimate_backlog({std::make_pair(fee_level, fee_level)}); if (blocks.size() != 1) { @@ -5903,10 +6003,10 @@ uint32_t wallet2::adjust_priority(uint32_t priority) } // get the current full reward zone - uint64_t block_size_limit = 0; - const auto result = m_node_rpc_proxy.get_block_size_limit(block_size_limit); + uint64_t block_weight_limit = 0; + const auto result = m_node_rpc_proxy.get_block_weight_limit(block_weight_limit); throw_on_rpc_response_error(result, "get_info"); - const uint64_t full_reward_zone = block_size_limit / 2; + const uint64_t full_reward_zone = block_weight_limit / 2; // get the last N block headers and sum the block sizes const size_t N = 10; @@ -5930,14 +6030,14 @@ uint32_t wallet2::adjust_priority(uint32_t priority) MERROR("Bad blockheaders size"); return priority; } - size_t block_size_sum = 0; + size_t block_weight_sum = 0; for (const cryptonote::block_header_response &i : getbh_res.headers) { - block_size_sum += i.block_size; + block_weight_sum += i.block_weight; } // estimate how 'full' the last N blocks are - const size_t P = 100 * block_size_sum / (N * full_reward_zone); + const size_t P = 100 * block_weight_sum / (N * full_reward_zone); MINFO((boost::format("The last %d blocks fill roughly %d%% of the full reward zone.") % N % P).str()); if (P > 80) { @@ -5963,8 +6063,10 @@ std::vector wallet2::create_transactions(std::vector unused_transfers_indices = select_available_outputs_from_histogram(fake_outs_count + 1, true, true, true); - const uint64_t fee_per_kb = get_per_kb_fee(); + const uint64_t base_fee = get_base_fee(); const uint64_t fee_multiplier = get_fee_multiplier(priority, get_fee_algorithm()); + const bool use_per_byte_fee = use_fork_rules(HF_VERSION_PER_BYTE_FEE); + const uint64_t fee_quantization_mask = get_fee_quantization_mask(); // failsafe split attempt counter size_t attempt_count = 0; @@ -5991,13 +6093,12 @@ std::vector wallet2::create_transactions(std::vector(), msout}); @@ -7990,11 +8091,11 @@ std::vector wallet2::create_transactions_2(std::vector dsts; cryptonote::transaction tx; pending_tx ptx; - size_t bytes; + size_t weight; uint64_t needed_fee; std::vector> outs; - TX() : bytes(0), needed_fee(0) {} + TX() : weight(0), needed_fee(0) {} void add(const account_public_address &addr, bool is_subaddress, uint64_t amount, unsigned int original_output_index, bool merge_destinations) { if (merge_destinations) @@ -8022,13 +8123,15 @@ std::vector wallet2::create_transactions_2(std::vector txes; bool adding_fee; // true if new outputs go towards fee, rather than destinations uint64_t needed_fee, available_for_fee = 0; - uint64_t upper_transaction_size_limit = get_upper_transaction_size_limit(); + uint64_t upper_transaction_weight_limit = get_upper_transaction_weight_limit(); + const bool use_per_byte_fee = use_fork_rules(HF_VERSION_PER_BYTE_FEE, 0); const bool use_rct = use_fork_rules(4, 0); const bool bulletproof = use_fork_rules(get_bulletproof_fork(), 0); - const rct::RangeProofType range_proof_type = bulletproof ? rct::RangeProofMultiOutputBulletproof : rct::RangeProofBorromean; + const rct::RangeProofType range_proof_type = bulletproof ? rct::RangeProofPaddedBulletproof : rct::RangeProofBorromean; - const uint64_t fee_per_kb = get_per_kb_fee(); + const uint64_t base_fee = get_base_fee(); const uint64_t fee_multiplier = get_fee_multiplier(priority, get_fee_algorithm()); + const uint64_t fee_quantization_mask = get_fee_quantization_mask(); // throw if attempting a transaction with no destinations THROW_WALLET_EXCEPTION_IF(dsts.empty(), error::zero_destination); @@ -8059,7 +8162,7 @@ std::vector wallet2::create_transactions_2(std::vector wallet2::create_transactions_2(std::vector tx_size_two_rings, error::wallet_internal_error, "Estimated tx size with 1 input is larger than with 2 inputs!"); - const size_t tx_size_per_ring = tx_size_two_rings - tx_size_one_ring; - const uint64_t fractional_threshold = (fee_multiplier * fee_per_kb * tx_size_per_ring) / 1024; + const size_t tx_weight_one_ring = estimate_tx_weight(use_rct, 1, fake_outs_count, 2, 0, bulletproof); + const size_t tx_weight_two_rings = estimate_tx_weight(use_rct, 2, fake_outs_count, 2, 0, bulletproof); + THROW_WALLET_EXCEPTION_IF(tx_weight_one_ring > tx_weight_two_rings, error::wallet_internal_error, "Estimated tx weight with 1 input is larger than with 2 inputs!"); + const size_t tx_weight_per_ring = tx_weight_two_rings - tx_weight_one_ring; + const uint64_t fractional_threshold = (fee_multiplier * base_fee * tx_weight_per_ring) / (use_per_byte_fee ? 1 : 1024); // gather all dust and non-dust outputs belonging to specified subaddresses size_t num_nondust_outputs = 0; @@ -8174,7 +8277,7 @@ std::vector wallet2::create_transactions_2(std::vector wallet2::create_transactions_2(std::vector 0) || adding_fee || !preferred_inputs.empty() || should_pick_a_second_output(use_rct, txes.back().selected_transfers.size(), *unused_transfers_indices, *unused_dust_indices)) { TX &tx = txes.back(); - LOG_PRINT_L2("Start of loop with " << unused_transfers_indices->size() << " " << unused_dust_indices->size()); + LOG_PRINT_L2("Start of loop with " << unused_transfers_indices->size() << " " << unused_dust_indices->size() << ", tx.dsts.size() " << tx.dsts.size()); LOG_PRINT_L2("unused_transfers_indices: " << strjoin(*unused_transfers_indices, " ")); LOG_PRINT_L2("unused_dust_indices: " << strjoin(*unused_dust_indices, " ")); LOG_PRINT_L2("dsts size " << dsts.size() << ", first " << (dsts.empty() ? "-" : cryptonote::print_money(dsts[0].amount))); @@ -8279,7 +8382,7 @@ std::vector wallet2::create_transactions_2(std::vector wallet2::create_transactions_2(std::vector 0 && !dsts.empty() && estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size(), extra.size(), bulletproof) < TX_SIZE_TARGET(upper_transaction_size_limit)) { + if (available_amount > 0 && !dsts.empty() && estimate_tx_weight(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof) < TX_WEIGHT_TARGET(upper_transaction_weight_limit)) { // we can partially fill that destination LOG_PRINT_L2("We can partially pay " << get_account_address_as_str(m_nettype, dsts[0].is_subaddress, dsts[0].addr) << " for " << print_money(available_amount) << "/" << print_money(dsts[0].amount)); @@ -8303,7 +8406,7 @@ std::vector wallet2::create_transactions_2(std::vector wallet2::create_transactions_2(std::vector= TX_SIZE_TARGET(upper_transaction_size_limit)); + const size_t estimated_rct_tx_weight = estimate_tx_weight(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof); + try_tx = dsts.empty() || (estimated_rct_tx_weight >= TX_WEIGHT_TARGET(upper_transaction_weight_limit)); } } @@ -8324,8 +8427,7 @@ std::vector wallet2::create_transactions_2(std::vector wallet2::create_transactions_2(std::vector available_for_fee && !dsts.empty() && dsts[0].amount > 0) @@ -8390,17 +8492,17 @@ std::vector wallet2::create_transactions_2(std::vector wallet2::create_transactions_2(std::vector ptx_vector; @@ -8486,7 +8588,7 @@ std::vector wallet2::create_transactions_2(std::vector wallet2::create_transactions_from(const crypton std::vector dsts; cryptonote::transaction tx; pending_tx ptx; - size_t bytes; + size_t weight; uint64_t needed_fee; std::vector> outs; - TX() : bytes(0), needed_fee(0) {} + TX() : weight(0), needed_fee(0) {} }; std::vector txes; uint64_t needed_fee, available_for_fee = 0; - uint64_t upper_transaction_size_limit = get_upper_transaction_size_limit(); + uint64_t upper_transaction_weight_limit = get_upper_transaction_weight_limit(); std::vector> outs; + const bool use_per_byte_fee = use_fork_rules(HF_VERSION_PER_BYTE_FEE); const bool use_rct = fake_outs_count > 0 && use_fork_rules(4, 0); const bool bulletproof = use_fork_rules(get_bulletproof_fork(), 0); - const rct::RangeProofType range_proof_type = bulletproof ? rct::RangeProofBulletproof : rct::RangeProofBorromean; - const uint64_t fee_per_kb = get_per_kb_fee(); + const rct::RangeProofType range_proof_type = bulletproof ? rct::RangeProofPaddedBulletproof : rct::RangeProofBorromean; + const uint64_t base_fee = get_base_fee(); const uint64_t fee_multiplier = get_fee_multiplier(priority, get_fee_algorithm()); + const uint64_t fee_quantization_mask = get_fee_quantization_mask(); LOG_PRINT_L2("Starting with " << unused_transfers_indices.size() << " non-dust outputs and " << unused_dust_indices.size() << " dust outputs"); @@ -8621,7 +8725,25 @@ std::vector wallet2::create_transactions_from(const crypton // get a random unspent output and use it to pay next chunk. We try to alternate // dust and non dust to ensure we never get with only dust, from which we might // get a tx that can't pay for itself - size_t idx = unused_transfers_indices.empty() ? pop_best_value(unused_dust_indices, tx.selected_transfers) : unused_dust_indices.empty() ? pop_best_value(unused_transfers_indices, tx.selected_transfers) : ((tx.selected_transfers.size() & 1) || accumulated_outputs > fee_per_kb * fee_multiplier * (upper_transaction_size_limit + 1023) / 1024) ? pop_best_value(unused_dust_indices, tx.selected_transfers) : pop_best_value(unused_transfers_indices, tx.selected_transfers); + uint64_t fee_dust_threshold; + if (use_fork_rules(HF_VERSION_PER_BYTE_FEE)) + { + const uint64_t estimated_tx_weight_with_one_extra_output = estimate_tx_weight(use_rct, tx.selected_transfers.size() + 1, fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof); + fee_dust_threshold = calculate_fee_from_weight(base_fee, estimated_tx_weight_with_one_extra_output, fee_multiplier, fee_quantization_mask); + } + else + { + fee_dust_threshold = base_fee * fee_multiplier * (upper_transaction_weight_limit + 1023) / 1024; + } + + size_t idx = + unused_transfers_indices.empty() + ? pop_best_value(unused_dust_indices, tx.selected_transfers) + : unused_dust_indices.empty() + ? pop_best_value(unused_transfers_indices, tx.selected_transfers) + : ((tx.selected_transfers.size() & 1) || accumulated_outputs > fee_dust_threshold) + ? pop_best_value(unused_dust_indices, tx.selected_transfers) + : pop_best_value(unused_transfers_indices, tx.selected_transfers); const transfer_details &td = m_transfers[idx]; LOG_PRINT_L2("Picking output " << idx << ", amount " << print_money(td.amount())); @@ -8636,16 +8758,15 @@ std::vector wallet2::create_transactions_from(const crypton // here, check if we need to sent tx and start a new one LOG_PRINT_L2("Considering whether to create a tx now, " << tx.selected_transfers.size() << " inputs, tx limit " - << upper_transaction_size_limit); - const size_t estimated_rct_tx_size = estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size() + 1, extra.size(), bulletproof); - bool try_tx = (unused_dust_indices.empty() && unused_transfers_indices.empty()) || ( estimated_rct_tx_size >= TX_SIZE_TARGET(upper_transaction_size_limit)); + << upper_transaction_weight_limit); + const size_t estimated_rct_tx_weight = estimate_tx_weight(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size() + 2, extra.size(), bulletproof); + bool try_tx = (unused_dust_indices.empty() && unused_transfers_indices.empty()) || ( estimated_rct_tx_weight >= TX_WEIGHT_TARGET(upper_transaction_weight_limit)); if (try_tx) { cryptonote::transaction test_tx; pending_tx test_ptx; - const size_t estimated_tx_size = estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size(), extra.size(), bulletproof); - needed_fee = calculate_fee(fee_per_kb, estimated_tx_size, fee_multiplier); + needed_fee = estimate_fee(use_per_byte_fee, use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof, base_fee, fee_multiplier, fee_quantization_mask); tx.dsts.push_back(tx_destination_entry(1, address, is_subaddress)); @@ -8658,9 +8779,9 @@ std::vector wallet2::create_transactions_from(const crypton transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra, detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx); auto txBlob = t_serializable_object_to_blob(test_ptx.tx); - needed_fee = calculate_fee(fee_per_kb, txBlob, fee_multiplier); + needed_fee = calculate_fee(use_per_byte_fee, test_ptx.tx, txBlob.size(), base_fee, fee_multiplier, fee_quantization_mask); available_for_fee = test_ptx.fee + test_ptx.dests[0].amount + test_ptx.change_dts.amount; - LOG_PRINT_L2("Made a " << get_size_string(txBlob) << " tx, with " << print_money(available_for_fee) << " available for fee (" << + LOG_PRINT_L2("Made a " << get_weight_string(test_ptx.tx, txBlob.size()) << " tx, with " << print_money(available_for_fee) << " available for fee (" << print_money(needed_fee) << " needed)"); THROW_WALLET_EXCEPTION_IF(needed_fee > available_for_fee, error::wallet_internal_error, "Transaction cannot pay for itself"); @@ -8675,17 +8796,17 @@ std::vector wallet2::create_transactions_from(const crypton transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra, detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx); txBlob = t_serializable_object_to_blob(test_ptx.tx); - needed_fee = calculate_fee(fee_per_kb, txBlob, fee_multiplier); - LOG_PRINT_L2("Made an attempt at a final " << get_size_string(txBlob) << " tx, with " << print_money(test_ptx.fee) << + needed_fee = calculate_fee(use_per_byte_fee, test_ptx.tx, txBlob.size(), base_fee, fee_multiplier, fee_quantization_mask); + LOG_PRINT_L2("Made an attempt at a final " << get_weight_string(test_ptx.tx, txBlob.size()) << " tx, with " << print_money(test_ptx.fee) << " fee and " << print_money(test_ptx.change_dts.amount) << " change"); } while (needed_fee > test_ptx.fee); - LOG_PRINT_L2("Made a final " << get_size_string(txBlob) << " tx, with " << print_money(test_ptx.fee) << + LOG_PRINT_L2("Made a final " << get_weight_string(test_ptx.tx, txBlob.size()) << " tx, with " << print_money(test_ptx.fee) << " fee and " << print_money(test_ptx.change_dts.amount) << " change"); tx.tx = test_tx; tx.ptx = test_ptx; - tx.bytes = txBlob.size(); + tx.weight = get_transaction_weight(test_tx, txBlob.size()); tx.outs = outs; tx.needed_fee = needed_fee; accumulated_fee += test_ptx.fee; @@ -8717,7 +8838,7 @@ std::vector wallet2::create_transactions_from(const crypton auto txBlob = t_serializable_object_to_blob(test_ptx.tx); tx.tx = test_tx; tx.ptx = test_ptx; - tx.bytes = txBlob.size(); + tx.weight = get_transaction_weight(test_tx, txBlob.size()); } std::vector ptx_vector; @@ -8728,7 +8849,7 @@ std::vector wallet2::create_transactions_from(const crypton for (size_t idx: tx.selected_transfers) tx_money += m_transfers[idx].amount(); LOG_PRINT_L1(" Transaction " << (1+std::distance(txes.begin(), i)) << "/" << txes.size() << - " " << get_transaction_hash(tx.ptx.tx) << ": " << get_size_string(tx.bytes) << ", sending " << print_money(tx_money) << " in " << tx.selected_transfers.size() << + " " << get_transaction_hash(tx.ptx.tx) << ": " << get_weight_string(tx.weight) << ", sending " << print_money(tx_money) << " in " << tx.selected_transfers.size() << " outputs to " << tx.dsts.size() << " destination(s), including " << print_money(tx.ptx.fee) << " fee, " << print_money(tx.ptx.change_dts.amount) << " change"); ptx_vector.push_back(tx.ptx); @@ -8763,12 +8884,15 @@ bool wallet2::use_fork_rules(uint8_t version, int64_t early_blocks) const return close_enough; } //---------------------------------------------------------------------------------------------------- -uint64_t wallet2::get_upper_transaction_size_limit() const +uint64_t wallet2::get_upper_transaction_weight_limit() const { - if (m_upper_transaction_size_limit > 0) - return m_upper_transaction_size_limit; + if (m_upper_transaction_weight_limit > 0) + return m_upper_transaction_weight_limit; uint64_t full_reward_zone = use_fork_rules(5, 10) ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 : use_fork_rules(2, 10) ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 : CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1; - return full_reward_zone - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE; + if (use_fork_rules(8, 10)) + return full_reward_zone / 2 - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE; + else + return full_reward_zone - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE; } //---------------------------------------------------------------------------------------------------- std::vector wallet2::select_available_outputs(const std::function &f) const @@ -8874,16 +8998,14 @@ const wallet2::transfer_details &wallet2::get_transfer_details(size_t idx) const //---------------------------------------------------------------------------------------------------- std::vector wallet2::select_available_unmixable_outputs() { - // request all outputs with less than 3 instances - const size_t min_mixin = use_fork_rules(7, 10) ? 6 : use_fork_rules(6, 10) ? 4 : 2; // v6 increases min mixin from 2 to 4, v7 to 6 - return select_available_outputs_from_histogram(min_mixin + 1, false, true, false); + // request all outputs with less instances than the min ring size + return select_available_outputs_from_histogram(get_min_ring_size(), false, true, false); } //---------------------------------------------------------------------------------------------------- std::vector wallet2::select_available_mixable_outputs() { - // request all outputs with at least 3 instances, so we can use mixin 2 with - const size_t min_mixin = use_fork_rules(7, 10) ? 6 : use_fork_rules(6, 10) ? 4 : 2; // v6 increases min mixin from 2 to 4, v7 to 6 - return select_available_outputs_from_histogram(min_mixin + 1, true, true, true); + // request all outputs with at least as many instances as the min ring size + return select_available_outputs_from_histogram(get_min_ring_size(), true, true, true); } //---------------------------------------------------------------------------------------------------- std::vector wallet2::create_unmixable_sweep_transactions() @@ -8892,7 +9014,7 @@ std::vector wallet2::create_unmixable_sweep_transactions() const bool hf1_rules = use_fork_rules(2, 10); // first hard fork has version 2 tx_dust_policy dust_policy(hf1_rules ? 0 : ::config::DEFAULT_DUST_THRESHOLD); - const uint64_t fee_per_kb = get_per_kb_fee(); + const uint64_t base_fee = get_base_fee(); // may throw std::vector unmixable_outputs = select_available_unmixable_outputs(); @@ -8907,7 +9029,7 @@ std::vector wallet2::create_unmixable_sweep_transactions() std::vector unmixable_transfer_outputs, unmixable_dust_outputs; for (auto n: unmixable_outputs) { - if (m_transfers[n].amount() < fee_per_kb) + if (m_transfers[n].amount() < base_fee) unmixable_dust_outputs.push_back(n); else unmixable_transfer_outputs.push_back(n); @@ -11228,46 +11350,46 @@ std::vector> wallet2::estimate_backlog(const std:: THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_txpool_backlog"); THROW_WALLET_EXCEPTION_IF(res.status != CORE_RPC_STATUS_OK, error::get_tx_pool_error); - uint64_t block_size_limit = 0; - const auto result = m_node_rpc_proxy.get_block_size_limit(block_size_limit); + uint64_t block_weight_limit = 0; + const auto result = m_node_rpc_proxy.get_block_weight_limit(block_weight_limit); throw_on_rpc_response_error(result, "get_info"); - uint64_t full_reward_zone = block_size_limit / 2; - THROW_WALLET_EXCEPTION_IF(full_reward_zone == 0, error::wallet_internal_error, "Invalid block size limit from daemon"); + uint64_t full_reward_zone = block_weight_limit / 2; + THROW_WALLET_EXCEPTION_IF(full_reward_zone == 0, error::wallet_internal_error, "Invalid block weight limit from daemon"); std::vector> blocks; for (const auto &fee_level: fee_levels) { const double our_fee_byte_min = fee_level.first; const double our_fee_byte_max = fee_level.second; - uint64_t priority_size_min = 0, priority_size_max = 0; + uint64_t priority_weight_min = 0, priority_weight_max = 0; for (const auto &i: res.backlog) { - if (i.blob_size == 0) + if (i.weight == 0) { - MWARNING("Got 0 sized blob from txpool, ignored"); + MWARNING("Got 0 weight tx from txpool, ignored"); continue; } - double this_fee_byte = i.fee / (double)i.blob_size; + double this_fee_byte = i.fee / (double)i.weight; if (this_fee_byte >= our_fee_byte_min) - priority_size_min += i.blob_size; + priority_weight_min += i.weight; if (this_fee_byte >= our_fee_byte_max) - priority_size_max += i.blob_size; + priority_weight_max += i.weight; } - uint64_t nblocks_min = priority_size_min / full_reward_zone; - uint64_t nblocks_max = priority_size_max / full_reward_zone; - MDEBUG("estimate_backlog: priority_size " << priority_size_min << " - " << priority_size_max << " for " + uint64_t nblocks_min = priority_weight_min / full_reward_zone; + uint64_t nblocks_max = priority_weight_max / full_reward_zone; + MDEBUG("estimate_backlog: priority_weight " << priority_weight_min << " - " << priority_weight_max << " for " << our_fee_byte_min << " - " << our_fee_byte_max << " piconero byte fee, " - << nblocks_min << " - " << nblocks_max << " blocks at block size " << full_reward_zone); + << nblocks_min << " - " << nblocks_max << " blocks at block weight " << full_reward_zone); blocks.push_back(std::make_pair(nblocks_min, nblocks_max)); } return blocks; } //---------------------------------------------------------------------------------------------------- -std::vector> wallet2::estimate_backlog(uint64_t min_blob_size, uint64_t max_blob_size, const std::vector &fees) +std::vector> wallet2::estimate_backlog(uint64_t min_tx_weight, uint64_t max_tx_weight, const std::vector &fees) { - THROW_WALLET_EXCEPTION_IF(min_blob_size == 0, error::wallet_internal_error, "Invalid 0 fee"); - THROW_WALLET_EXCEPTION_IF(max_blob_size == 0, error::wallet_internal_error, "Invalid 0 fee"); + THROW_WALLET_EXCEPTION_IF(min_tx_weight == 0, error::wallet_internal_error, "Invalid 0 fee"); + THROW_WALLET_EXCEPTION_IF(max_tx_weight == 0, error::wallet_internal_error, "Invalid 0 fee"); for (uint64_t fee: fees) { THROW_WALLET_EXCEPTION_IF(fee == 0, error::wallet_internal_error, "Invalid 0 fee"); @@ -11275,7 +11397,7 @@ std::vector> wallet2::estimate_backlog(uint64_t mi std::vector> fee_levels; for (uint64_t fee: fees) { - double our_fee_byte_min = fee / (double)min_blob_size, our_fee_byte_max = fee / (double)max_blob_size; + double our_fee_byte_min = fee / (double)min_tx_weight, our_fee_byte_max = fee / (double)max_tx_weight; fee_levels.emplace_back(our_fee_byte_min, our_fee_byte_max); } return estimate_backlog(fee_levels); diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 3ba51a27c1d..f9b516bff84 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -630,14 +630,9 @@ namespace tools void explicit_refresh_from_block_height(bool expl) {m_explicit_refresh_from_block_height = expl;} bool explicit_refresh_from_block_height() const {return m_explicit_refresh_from_block_height;} - // upper_transaction_size_limit as defined below is set to - // approximately 125% of the fixed minimum allowable penalty - // free block size. TODO: fix this so that it actually takes - // into account the current median block size rather than - // the minimum block size. bool deinit(); bool init(bool rpc, std::string daemon_address = "http://localhost:8080", - boost::optional daemon_login = boost::none, uint64_t upper_transaction_size_limit = 0, bool ssl = false, bool trusted_daemon = false); + boost::optional daemon_login = boost::none, uint64_t upper_transaction_weight_limit = 0, bool ssl = false, bool trusted_daemon = false); void stop() { m_run.store(false, std::memory_order_relaxed); } @@ -1084,10 +1079,13 @@ namespace tools bool is_synced() const; std::vector> estimate_backlog(const std::vector> &fee_levels); - std::vector> estimate_backlog(uint64_t min_blob_size, uint64_t max_blob_size, const std::vector &fees); + std::vector> estimate_backlog(uint64_t min_tx_weight, uint64_t max_tx_weight, const std::vector &fees); uint64_t get_fee_multiplier(uint32_t priority, int fee_algorithm = -1) const; - uint64_t get_per_kb_fee() const; + uint64_t get_base_fee() const; + uint64_t get_fee_quantization_mask() const; + uint64_t get_min_ring_size() const; + uint64_t get_max_ring_size() const; uint64_t adjust_mixin(uint64_t mixin) const; uint32_t adjust_priority(uint32_t priority); @@ -1212,9 +1210,9 @@ namespace tools void check_acc_out_precomp(const cryptonote::tx_out &o, const crypto::key_derivation &derivation, const std::vector &additional_derivations, size_t i, const is_out_data *is_out_data, tx_scan_info_t &tx_scan_info) const; void check_acc_out_precomp_once(const cryptonote::tx_out &o, const crypto::key_derivation &derivation, const std::vector &additional_derivations, size_t i, const is_out_data *is_out_data, tx_scan_info_t &tx_scan_info, bool &already_seen) const; void parse_block_round(const cryptonote::blobdata &blob, cryptonote::block &bl, crypto::hash &bl_id, bool &error) const; - uint64_t get_upper_transaction_size_limit() const; + uint64_t get_upper_transaction_weight_limit() const; std::vector get_unspent_amounts_vector() const; - uint64_t get_dynamic_per_kb_fee_estimate() const; + uint64_t get_dynamic_base_fee_estimate() const; float get_output_relatedness(const transfer_details &td0, const transfer_details &td1) const; std::vector pick_preferred_rct_inputs(uint64_t needed_money, uint32_t subaddr_account, const std::set &subaddr_indices) const; void set_spent(size_t idx, uint64_t height); @@ -1269,7 +1267,7 @@ namespace tools std::unordered_map m_attributes; std::vector m_address_book; std::pair, std::vector> m_account_tags; - uint64_t m_upper_transaction_size_limit; //TODO: auto-calc this value or request from daemon, now use some fixed value + uint64_t m_upper_transaction_weight_limit; //TODO: auto-calc this value or request from daemon, now use some fixed value const std::vector> *m_multisig_rescan_info; const std::vector> *m_multisig_rescan_k; @@ -1838,7 +1836,7 @@ namespace tools THROW_WALLET_EXCEPTION_IF(m_multisig, error::wallet_internal_error, "Multisig wallets cannot spend non rct outputs"); - uint64_t upper_transaction_size_limit = get_upper_transaction_size_limit(); + uint64_t upper_transaction_weight_limit = get_upper_transaction_weight_limit(); uint64_t needed_money = fee; // calculate total amount being sent to all destinations @@ -1970,7 +1968,7 @@ namespace tools rct::multisig_out msout; bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sources, splitted_dsts, change_dts.addr, extra, tx, unlock_time, tx_key, additional_tx_keys, false, rct::RangeProofBorromean, m_multisig ? &msout : NULL); THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sources, splitted_dsts, unlock_time, m_nettype); - THROW_WALLET_EXCEPTION_IF(upper_transaction_size_limit <= get_object_blobsize(tx), error::tx_too_big, tx, upper_transaction_size_limit); + THROW_WALLET_EXCEPTION_IF(upper_transaction_weight_limit <= get_transaction_weight(tx), error::tx_too_big, tx, upper_transaction_weight_limit); std::string key_images; bool all_are_txin_to_key = std::all_of(tx.vin.begin(), tx.vin.end(), [&](const txin_v& s_e) -> bool diff --git a/src/wallet/wallet_errors.h b/src/wallet/wallet_errors.h index 2439532804b..a30e807b1c0 100644 --- a/src/wallet/wallet_errors.h +++ b/src/wallet/wallet_errors.h @@ -679,30 +679,30 @@ namespace tools //---------------------------------------------------------------------------------------------------- struct tx_too_big : public transfer_error { - explicit tx_too_big(std::string&& loc, const cryptonote::transaction& tx, uint64_t tx_size_limit) + explicit tx_too_big(std::string&& loc, const cryptonote::transaction& tx, uint64_t tx_weight_limit) : transfer_error(std::move(loc), "transaction is too big") , m_tx(tx) - , m_tx_size_limit(tx_size_limit) + , m_tx_weight_limit(tx_weight_limit) { } const cryptonote::transaction& tx() const { return m_tx; } - uint64_t tx_size_limit() const { return m_tx_size_limit; } + uint64_t tx_weight_limit() const { return m_tx_weight_limit; } std::string to_string() const { std::ostringstream ss; cryptonote::transaction tx = m_tx; ss << transfer_error::to_string() << - ", tx_size_limit = " << m_tx_size_limit << - ", tx size = " << get_object_blobsize(m_tx) << + ", tx_weight_limit = " << m_tx_weight_limit << + ", tx weight = " << get_transaction_weight(m_tx) << ", tx:\n" << cryptonote::obj_to_json_str(tx); return ss.str(); } private: cryptonote::transaction m_tx; - uint64_t m_tx_size_limit; + uint64_t m_tx_weight_limit; }; //---------------------------------------------------------------------------------------------------- struct zero_destination : public transfer_error diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index 96ad23e604f..a6fe4e244c3 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -1542,7 +1542,6 @@ namespace tools rpc_transfers.spent = td.m_spent; rpc_transfers.global_index = td.m_global_output_index; rpc_transfers.tx_hash = epee::string_tools::pod_to_hex(td.m_txid); - rpc_transfers.tx_size = txBlob.size(); rpc_transfers.subaddr_index = td.m_subaddr_index.minor; rpc_transfers.key_image = req.verbose && td.m_key_image_known ? epee::string_tools::pod_to_hex(td.m_key_image) : ""; res.transfers.push_back(rpc_transfers); diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h index 2d0f8c65906..ce10e2917c4 100644 --- a/src/wallet/wallet_rpc_server_commands_defs.h +++ b/src/wallet/wallet_rpc_server_commands_defs.h @@ -865,7 +865,6 @@ namespace wallet_rpc bool spent; uint64_t global_index; std::string tx_hash; - uint64_t tx_size; uint32_t subaddr_index; std::string key_image; @@ -874,7 +873,6 @@ namespace wallet_rpc KV_SERIALIZE(spent) KV_SERIALIZE(global_index) KV_SERIALIZE(tx_hash) - KV_SERIALIZE(tx_size) KV_SERIALIZE(subaddr_index) KV_SERIALIZE(key_image) END_KV_SERIALIZE_MAP() diff --git a/tests/core_tests/block_reward.cpp b/tests/core_tests/block_reward.cpp index 9b0e74911a6..e553784397f 100644 --- a/tests/core_tests/block_reward.cpp +++ b/tests/core_tests/block_reward.cpp @@ -36,24 +36,24 @@ using namespace cryptonote; namespace { - bool construct_miner_tx_by_size(transaction& miner_tx, uint64_t height, uint64_t already_generated_coins, - const account_public_address& miner_address, std::vector& block_sizes, size_t target_tx_size, - size_t target_block_size, uint64_t fee = 0) + bool construct_miner_tx_by_weight(transaction& miner_tx, uint64_t height, uint64_t already_generated_coins, + const account_public_address& miner_address, std::vector& block_weights, size_t target_tx_weight, + size_t target_block_weight, uint64_t fee = 0) { - if (!construct_miner_tx(height, misc_utils::median(block_sizes), already_generated_coins, target_block_size, fee, miner_address, miner_tx)) + if (!construct_miner_tx(height, misc_utils::median(block_weights), already_generated_coins, target_block_weight, fee, miner_address, miner_tx)) return false; - size_t current_size = get_object_blobsize(miner_tx); + size_t current_weight = get_transaction_weight(miner_tx); size_t try_count = 0; - while (target_tx_size != current_size) + while (target_tx_weight != current_weight) { ++try_count; if (10 < try_count) return false; - if (target_tx_size < current_size) + if (target_tx_weight < current_weight) { - size_t diff = current_size - target_tx_size; + size_t diff = current_weight - target_tx_weight; if (diff <= miner_tx.extra.size()) miner_tx.extra.resize(miner_tx.extra.size() - diff); else @@ -61,28 +61,28 @@ namespace } else { - size_t diff = target_tx_size - current_size; + size_t diff = target_tx_weight - current_weight; miner_tx.extra.resize(miner_tx.extra.size() + diff); } - current_size = get_object_blobsize(miner_tx); + current_weight = get_transaction_weight(miner_tx); } return true; } - bool construct_max_size_block(test_generator& generator, block& blk, const block& blk_prev, const account_base& miner_account, + bool construct_max_weight_block(test_generator& generator, block& blk, const block& blk_prev, const account_base& miner_account, size_t median_block_count = CRYPTONOTE_REWARD_BLOCKS_WINDOW) { - std::vector block_sizes; - generator.get_last_n_block_sizes(block_sizes, get_block_hash(blk_prev), median_block_count); + std::vector block_weights; + generator.get_last_n_block_weights(block_weights, get_block_hash(blk_prev), median_block_count); - size_t median = misc_utils::median(block_sizes); + size_t median = misc_utils::median(block_weights); median = std::max(median, static_cast(CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1)); transaction miner_tx; - bool r = construct_miner_tx_by_size(miner_tx, get_block_height(blk_prev) + 1, generator.get_already_generated_coins(blk_prev), - miner_account.get_keys().m_account_address, block_sizes, 2 * median, 2 * median); + bool r = construct_miner_tx_by_weight(miner_tx, get_block_height(blk_prev) + 1, generator.get_already_generated_coins(blk_prev), + miner_account.get_keys().m_account_address, block_weights, 2 * median, 2 * median); if (!r) return false; @@ -97,7 +97,7 @@ namespace for (size_t i = 0; i < block_count; ++i) { block blk_i; - if (!construct_max_size_block(generator, blk_i, blk, miner_account)) + if (!construct_max_weight_block(generator, blk_i, blk, miner_account)) return false; events.push_back(blk_i); @@ -141,18 +141,18 @@ bool gen_block_reward::generate(std::vector& events) const // Test: block reward is calculated using median of the latest CRYPTONOTE_REWARD_BLOCKS_WINDOW blocks DO_CALLBACK(events, "mark_invalid_block"); block blk_1_bad_1; - if (!construct_max_size_block(generator, blk_1_bad_1, blk_0r, miner_account, CRYPTONOTE_REWARD_BLOCKS_WINDOW + 1)) + if (!construct_max_weight_block(generator, blk_1_bad_1, blk_0r, miner_account, CRYPTONOTE_REWARD_BLOCKS_WINDOW + 1)) return false; events.push_back(blk_1_bad_1); DO_CALLBACK(events, "mark_invalid_block"); block blk_1_bad_2; - if (!construct_max_size_block(generator, blk_1_bad_2, blk_0r, miner_account, CRYPTONOTE_REWARD_BLOCKS_WINDOW - 1)) + if (!construct_max_weight_block(generator, blk_1_bad_2, blk_0r, miner_account, CRYPTONOTE_REWARD_BLOCKS_WINDOW - 1)) return false; events.push_back(blk_1_bad_2); block blk_1; - if (!construct_max_size_block(generator, blk_1, blk_0r, miner_account)) + if (!construct_max_weight_block(generator, blk_1, blk_0r, miner_account)) return false; events.push_back(blk_1); @@ -187,16 +187,16 @@ bool gen_block_reward::generate(std::vector& events) const { transaction tx_1 = construct_tx_with_fee(events, blk_5, miner_account, bob_account, MK_COINS(1), 11 * TESTS_DEFAULT_FEE); transaction tx_2 = construct_tx_with_fee(events, blk_5, miner_account, bob_account, MK_COINS(1), 13 * TESTS_DEFAULT_FEE); - size_t txs_1_size = get_object_blobsize(tx_1) + get_object_blobsize(tx_2); + size_t txs_1_weight = get_transaction_weight(tx_1) + get_transaction_weight(tx_2); uint64_t txs_fee = get_tx_fee(tx_1) + get_tx_fee(tx_2); - std::vector block_sizes; - generator.get_last_n_block_sizes(block_sizes, get_block_hash(blk_7), CRYPTONOTE_REWARD_BLOCKS_WINDOW); - size_t median = misc_utils::median(block_sizes); + std::vector block_weights; + generator.get_last_n_block_weights(block_weights, get_block_hash(blk_7), CRYPTONOTE_REWARD_BLOCKS_WINDOW); + size_t median = misc_utils::median(block_weights); transaction miner_tx; - bool r = construct_miner_tx_by_size(miner_tx, get_block_height(blk_7) + 1, generator.get_already_generated_coins(blk_7), - miner_account.get_keys().m_account_address, block_sizes, 2 * median - txs_1_size, 2 * median, txs_fee); + bool r = construct_miner_tx_by_weight(miner_tx, get_block_height(blk_7) + 1, generator.get_already_generated_coins(blk_7), + miner_account.get_keys().m_account_address, block_weights, 2 * median - txs_1_weight, 2 * median, txs_fee); if (!r) return false; @@ -206,7 +206,7 @@ bool gen_block_reward::generate(std::vector& events) const block blk_8; generator.construct_block_manually(blk_8, blk_7, miner_account, test_generator::bf_miner_tx | test_generator::bf_tx_hashes, - 0, 0, 0, crypto::hash(), 0, miner_tx, txs_1_hashes, txs_1_size); + 0, 0, 0, crypto::hash(), 0, miner_tx, txs_1_hashes, txs_1_weight); events.push_back(blk_8); DO_CALLBACK(events, "mark_checked_block"); diff --git a/tests/core_tests/block_validation.cpp b/tests/core_tests/block_validation.cpp index 598cd409801..760cc43284f 100644 --- a/tests/core_tests/block_validation.cpp +++ b/tests/core_tests/block_validation.cpp @@ -586,11 +586,11 @@ bool gen_block_invalid_binary_format::generate(std::vector& ev block blk_test; std::vector tx_hashes; tx_hashes.push_back(get_transaction_hash(tx_0)); - size_t txs_size = get_object_blobsize(tx_0); + size_t txs_weight = get_transaction_weight(tx_0); diffic = next_difficulty(timestamps, cummulative_difficulties,DIFFICULTY_TARGET_V1); if (!generator.construct_block_manually(blk_test, blk_last, miner_account, test_generator::bf_diffic | test_generator::bf_timestamp | test_generator::bf_tx_hashes, 0, 0, blk_last.timestamp, - crypto::hash(), diffic, transaction(), tx_hashes, txs_size)) + crypto::hash(), diffic, transaction(), tx_hashes, txs_weight)) return false; blobdata blob = t_serializable_object_to_blob(blk_test); diff --git a/tests/core_tests/bulletproofs.cpp b/tests/core_tests/bulletproofs.cpp index a675b2c59a6..3b3039a18db 100644 --- a/tests/core_tests/bulletproofs.cpp +++ b/tests/core_tests/bulletproofs.cpp @@ -42,7 +42,7 @@ using namespace cryptonote; // Tests bool gen_bp_tx_validation_base::generate_with(std::vector& events, - const int *out_idx, int mixin, size_t n_txes, const uint64_t *amounts_paid, bool valid, const bool *multi_out, + size_t mixin, size_t n_txes, const uint64_t *amounts_paid, bool valid, const rct::RangeProofType *range_proof_type, const std::function &sources, std::vector &destinations, size_t tx_idx)> &pre_tx, const std::function &post_tx) const { @@ -51,11 +51,11 @@ bool gen_bp_tx_validation_base::generate_with(std::vector& eve GENERATE_ACCOUNT(miner_account); MAKE_GENESIS_BLOCK(events, blk_0, miner_account, ts_start); - // create 8 miner accounts, and have them mine the next 8 blocks - cryptonote::account_base miner_accounts[8]; + // create 12 miner accounts, and have them mine the next 12 blocks + cryptonote::account_base miner_accounts[12]; const cryptonote::block *prev_block = &blk_0; - cryptonote::block blocks[8 + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW]; - for (size_t n = 0; n < 8; ++n) { + cryptonote::block blocks[12 + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW]; + for (size_t n = 0; n < 12; ++n) { miner_accounts[n].generate(); CHECK_AND_ASSERT_MES(generator.construct_block_manually(blocks[n], *prev_block, miner_accounts[n], test_generator::bf_major_ver | test_generator::bf_minor_ver | test_generator::bf_timestamp | test_generator::bf_hf_version, @@ -70,16 +70,16 @@ bool gen_bp_tx_validation_base::generate_with(std::vector& eve // rewind cryptonote::block blk_r, blk_last; { - blk_last = blocks[7]; + blk_last = blocks[11]; for (size_t i = 0; i < CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW; ++i) { - CHECK_AND_ASSERT_MES(generator.construct_block_manually(blocks[8+i], blk_last, miner_account, + CHECK_AND_ASSERT_MES(generator.construct_block_manually(blocks[12+i], blk_last, miner_account, test_generator::bf_major_ver | test_generator::bf_minor_ver | test_generator::bf_timestamp | test_generator::bf_hf_version, 2, 2, blk_last.timestamp + DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN * 2, // v2 has blocks twice as long crypto::hash(), 0, transaction(), std::vector(), 0, 0, 2), false, "Failed to generate block"); - events.push_back(blocks[8+i]); - blk_last = blocks[8+i]; + events.push_back(blocks[12+i]); + blk_last = blocks[12+i]; } blk_r = blk_last; } @@ -99,7 +99,7 @@ bool gen_bp_tx_validation_base::generate_with(std::vector& eve const uint64_t needed_amount = input_amounts_available[n]; src.amount = input_amounts_available[n]; size_t real_index_in_tx = 0; - for (size_t m = 0; m < 7; ++m) { + for (size_t m = 0; m <= mixin; ++m) { size_t index_in_tx = 0; for (size_t i = 0; i < blocks[m].miner_tx.vout.size(); ++i) if (blocks[m].miner_tx.vout[i].amount == needed_amount) @@ -136,7 +136,7 @@ bool gen_bp_tx_validation_base::generate_with(std::vector& eve std::unordered_map subaddresses; subaddresses[miner_accounts[n].get_keys().m_account_address.m_spend_public_key] = {0,0}; rct_txes.resize(rct_txes.size() + 1); - bool r = construct_tx_and_get_tx_key(miner_accounts[n].get_keys(), subaddresses, sources, destinations, cryptonote::account_public_address{}, std::vector(), rct_txes.back(), 0, tx_key, additional_tx_keys, true, multi_out[n] ? rct::RangeProofMultiOutputBulletproof : rct::RangeProofBulletproof); + bool r = construct_tx_and_get_tx_key(miner_accounts[n].get_keys(), subaddresses, sources, destinations, cryptonote::account_public_address{}, std::vector(), rct_txes.back(), 0, tx_key, additional_tx_keys, true, range_proof_type[n]); CHECK_AND_ASSERT_MES(r, false, "failed to construct transaction"); if (post_tx && !post_tx(rct_txes.back(), n)) @@ -199,93 +199,100 @@ bool gen_bp_tx_validation_base::check_bp(const cryptonote::transaction &tx, size while (sizes[n_sizes] != (size_t)-1) n_amounts += sizes[n_sizes++]; CHECK_TEST_CONDITION(tx.rct_signatures.p.bulletproofs.size() == n_sizes); - CHECK_TEST_CONDITION(rct::n_bulletproof_amounts(tx.rct_signatures.p.bulletproofs) == n_amounts); + CHECK_TEST_CONDITION(rct::n_bulletproof_max_amounts(tx.rct_signatures.p.bulletproofs) == n_amounts); for (size_t n = 0; n < n_sizes; ++n) - CHECK_TEST_CONDITION(rct::n_bulletproof_amounts(tx.rct_signatures.p.bulletproofs[n]) == sizes[n]); + CHECK_TEST_CONDITION(rct::n_bulletproof_max_amounts(tx.rct_signatures.p.bulletproofs[n]) == sizes[n]); return true; } bool gen_bp_tx_valid_1::generate(std::vector& events) const { - const int mixin = 6; - const int out_idx[] = {1, -1}; + const size_t mixin = 10; const uint64_t amounts_paid[] = {10000, (uint64_t)-1}; const size_t bp_sizes[] = {1, (size_t)-1}; - const bool multi_out[] = {false}; - return generate_with(events, out_idx, mixin, 1, amounts_paid, true, multi_out, NULL, [&](const cryptonote::transaction &tx, size_t tx_idx){ return check_bp(tx, tx_idx, bp_sizes, "gen_bp_tx_valid_1"); }); + const rct::RangeProofType range_proof_type[] = {rct::RangeProofPaddedBulletproof}; + return generate_with(events, mixin, 1, amounts_paid, true, range_proof_type, NULL, [&](const cryptonote::transaction &tx, size_t tx_idx){ return check_bp(tx, tx_idx, bp_sizes, "gen_bp_tx_valid_1"); }); } bool gen_bp_tx_invalid_1_1::generate(std::vector& events) const { - const int mixin = 6; - const int out_idx[] = {1, -1}; + const size_t mixin = 10; const uint64_t amounts_paid[] = {5000, 5000, (uint64_t)-1}; - const size_t bp_sizes[] = {1, 1, (size_t)-1}; - const bool multi_out[] = {false}; - return generate_with(events, out_idx, mixin, 1, amounts_paid, false, multi_out, NULL, [&](const cryptonote::transaction &tx, size_t tx_idx){ return check_bp(tx, tx_idx, bp_sizes, "gen_bp_tx_invalid_1_1"); }); + const rct::RangeProofType range_proof_type[] = { rct::RangeProofBulletproof }; + return generate_with(events, mixin, 1, amounts_paid, false, range_proof_type, NULL, NULL); } bool gen_bp_tx_valid_2::generate(std::vector& events) const { - const int mixin = 6; - const int out_idx[] = {1, -1}; + const size_t mixin = 10; const uint64_t amounts_paid[] = {5000, 5000, (uint64_t)-1}; const size_t bp_sizes[] = {2, (size_t)-1}; - const bool multi_out[] = {true}; - return generate_with(events, out_idx, mixin, 1, amounts_paid, true, multi_out, NULL, [&](const cryptonote::transaction &tx, size_t tx_idx){ return check_bp(tx, tx_idx, bp_sizes, "gen_bp_tx_valid_2"); }); + const rct::RangeProofType range_proof_type[] = {rct::RangeProofPaddedBulletproof}; + return generate_with(events, mixin, 1, amounts_paid, true, range_proof_type, NULL, [&](const cryptonote::transaction &tx, size_t tx_idx){ return check_bp(tx, tx_idx, bp_sizes, "gen_bp_tx_valid_2"); }); } -bool gen_bp_tx_valid_4_2_1::generate(std::vector& events) const +bool gen_bp_tx_valid_3::generate(std::vector& events) const { - const int mixin = 6; - const int out_idx[] = {1, -1}; + const size_t mixin = 10; + const uint64_t amounts_paid[] = {5000, 5000, 5000, (uint64_t)-1}; + const size_t bp_sizes[] = {4, (size_t)-1}; + const rct::RangeProofType range_proof_type[] = { rct::RangeProofPaddedBulletproof }; + return generate_with(events, mixin, 1, amounts_paid, true, range_proof_type, NULL, [&](const cryptonote::transaction &tx, size_t tx_idx){ return check_bp(tx, tx_idx, bp_sizes, "gen_bp_tx_valid_3"); }); +} + +bool gen_bp_tx_valid_16::generate(std::vector& events) const +{ + const size_t mixin = 10; + const uint64_t amounts_paid[] = {500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, (uint64_t)-1}; + const size_t bp_sizes[] = {16, (size_t)-1}; + const rct::RangeProofType range_proof_type[] = { rct::RangeProofPaddedBulletproof }; + return generate_with(events, mixin, 1, amounts_paid, true, range_proof_type, NULL, [&](const cryptonote::transaction &tx, size_t tx_idx){ return check_bp(tx, tx_idx, bp_sizes, "gen_bp_tx_valid_16"); }); +} + +bool gen_bp_tx_invalid_4_2_1::generate(std::vector& events) const +{ + const size_t mixin = 10; const uint64_t amounts_paid[] = {1000, 1000, 1000, 1000, 1000, 1000, 1000, (uint64_t)-1}; - const size_t bp_sizes[] = {4, 2, 1, (size_t)-1}; - const bool multi_out[] = {true}; - return generate_with(events, out_idx, mixin, 1, amounts_paid, true, multi_out, NULL, [&](const cryptonote::transaction &tx, size_t tx_idx){ return check_bp(tx, tx_idx, bp_sizes, "gen_bp_tx_valid_4_2_1"); }); + const rct::RangeProofType range_proof_type[] = { rct::RangeProofMultiOutputBulletproof }; + return generate_with(events, mixin, 1, amounts_paid, false, range_proof_type, NULL, NULL); } -bool gen_bp_tx_valid_16_16::generate(std::vector& events) const +bool gen_bp_tx_invalid_16_16::generate(std::vector& events) const { - const int mixin = 6; - const int out_idx[] = {1, -1}; + const size_t mixin = 10; const uint64_t amounts_paid[] = {1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, (uint64_t)-1}; - const size_t bp_sizes[] = {16, 16, (size_t)-1}; - const bool multi_out[] = {true}; - return generate_with(events, out_idx, mixin, 1, amounts_paid, true, multi_out, NULL, [&](const cryptonote::transaction &tx, size_t tx_idx){ return check_bp(tx, tx_idx, bp_sizes, "gen_bp_tx_valid_16_16"); }); + const rct::RangeProofType range_proof_type[] = { rct::RangeProofMultiOutputBulletproof }; + return generate_with(events, mixin, 1, amounts_paid, false, range_proof_type, NULL, NULL); } bool gen_bp_txs_valid_2_and_2::generate(std::vector& events) const { - const int mixin = 6; - const int out_idx[] = {1, -1}; + const size_t mixin = 10; const uint64_t amounts_paid[] = {1000, 1000, (size_t)-1, 1000, 1000, (uint64_t)-1}; const size_t bp_sizes[] = {2, (size_t)-1, 2, (size_t)-1}; - const bool multi_out[] = {true}; - return generate_with(events, out_idx, mixin, 2, amounts_paid, true, multi_out, NULL, [&](const cryptonote::transaction &tx, size_t tx_idx){ return check_bp(tx, tx_idx, bp_sizes, "gen_bp_txs_valid_2_2"); }); + const rct::RangeProofType range_proof_type[] = { rct::RangeProofPaddedBulletproof, rct::RangeProofPaddedBulletproof}; + return generate_with(events, mixin, 2, amounts_paid, true, range_proof_type, NULL, [&](const cryptonote::transaction &tx, size_t tx_idx){ return check_bp(tx, tx_idx, bp_sizes, "gen_bp_txs_valid_2_and_2"); }); } -bool gen_bp_txs_valid_2_and_8_2_and_16_16_1::generate(std::vector& events) const +bool gen_bp_txs_invalid_2_and_8_2_and_16_16_1::generate(std::vector& events) const { - const int mixin = 6; - const int out_idx[] = {1, -1}; + const size_t mixin = 10; const uint64_t amounts_paid[] = {1000, 1000, (uint64_t)-1, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, (uint64_t)-1, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, (uint64_t)-1}; - const bool multi_out[] = {true, true, true}; - const size_t bp_sizes[] = {2, (size_t)-1, 8, 2, (size_t)-1, 16, 16, 1, (size_t)-1}; - return generate_with(events, out_idx, mixin, 3, amounts_paid, true, multi_out, NULL, [&](const cryptonote::transaction &tx, size_t tx_idx){ return check_bp(tx, tx_idx, bp_sizes, "gen_bp_txs_valid_1_1_and_8_2_and_16_16_1"); }); + const rct::RangeProofType range_proof_type[] = {rct::RangeProofMultiOutputBulletproof, rct::RangeProofMultiOutputBulletproof, rct::RangeProofMultiOutputBulletproof}; + return generate_with(events, mixin, 3, amounts_paid, false, range_proof_type, NULL, NULL); } bool gen_bp_tx_invalid_not_enough_proofs::generate(std::vector& events) const { DEFINE_TESTS_ERROR_CONTEXT("gen_bp_tx_invalid_not_enough_proofs"); - const int mixin = 6; - const int out_idx[] = {1, -1}; - const uint64_t amounts_paid[] = {10000, (uint64_t)-1}; - const bool multi_out[] = {false}; - return generate_with(events, out_idx, mixin, 1, amounts_paid, false, multi_out, NULL, [&](cryptonote::transaction &tx, size_t idx){ + const size_t mixin = 10; + const uint64_t amounts_paid[] = {5000, 5000, (uint64_t)-1}; + const rct::RangeProofType range_proof_type[] = { rct::RangeProofBulletproof }; + return generate_with(events, mixin, 1, amounts_paid, false, range_proof_type, NULL, [&](cryptonote::transaction &tx, size_t idx){ CHECK_TEST_CONDITION(tx.rct_signatures.type == rct::RCTTypeBulletproof); CHECK_TEST_CONDITION(!tx.rct_signatures.p.bulletproofs.empty()); tx.rct_signatures.p.bulletproofs.pop_back(); + CHECK_TEST_CONDITION(!tx.rct_signatures.p.bulletproofs.empty()); return true; }); } @@ -293,11 +300,10 @@ bool gen_bp_tx_invalid_not_enough_proofs::generate(std::vector bool gen_bp_tx_invalid_too_many_proofs::generate(std::vector& events) const { DEFINE_TESTS_ERROR_CONTEXT("gen_bp_tx_invalid_too_many_proofs"); - const int mixin = 6; - const int out_idx[] = {1, -1}; + const size_t mixin = 10; const uint64_t amounts_paid[] = {10000, (uint64_t)-1}; - const bool multi_out[] = {false}; - return generate_with(events, out_idx, mixin, 1, amounts_paid, false, multi_out, NULL, [&](cryptonote::transaction &tx, size_t idx){ + const rct::RangeProofType range_proof_type[] = { rct::RangeProofBulletproof }; + return generate_with(events, mixin, 1, amounts_paid, false, range_proof_type, NULL, [&](cryptonote::transaction &tx, size_t idx){ CHECK_TEST_CONDITION(tx.rct_signatures.type == rct::RCTTypeBulletproof); CHECK_TEST_CONDITION(!tx.rct_signatures.p.bulletproofs.empty()); tx.rct_signatures.p.bulletproofs.push_back(tx.rct_signatures.p.bulletproofs.back()); @@ -308,11 +314,10 @@ bool gen_bp_tx_invalid_too_many_proofs::generate(std::vector& bool gen_bp_tx_invalid_wrong_amount::generate(std::vector& events) const { DEFINE_TESTS_ERROR_CONTEXT("gen_bp_tx_invalid_wrong_amount"); - const int mixin = 6; - const int out_idx[] = {1, -1}; + const size_t mixin = 10; const uint64_t amounts_paid[] = {10000, (uint64_t)-1}; - const bool multi_out[] = {false}; - return generate_with(events, out_idx, mixin, 1, amounts_paid, false, multi_out, NULL, [&](cryptonote::transaction &tx, size_t idx){ + const rct::RangeProofType range_proof_type[] = { rct::RangeProofBulletproof }; + return generate_with(events, mixin, 1, amounts_paid, false, range_proof_type, NULL, [&](cryptonote::transaction &tx, size_t idx){ CHECK_TEST_CONDITION(tx.rct_signatures.type == rct::RCTTypeBulletproof); CHECK_TEST_CONDITION(!tx.rct_signatures.p.bulletproofs.empty()); tx.rct_signatures.p.bulletproofs.back() = rct::bulletproof_PROVE(1000, rct::skGen()); @@ -320,20 +325,15 @@ bool gen_bp_tx_invalid_wrong_amount::generate(std::vector& eve }); } -bool gen_bp_tx_invalid_switched::generate(std::vector& events) const +bool gen_bp_tx_invalid_borromean_type::generate(std::vector& events) const { - DEFINE_TESTS_ERROR_CONTEXT("gen_bp_tx_invalid_switched"); - const int mixin = 6; - const int out_idx[] = {1, -1}; - const uint64_t amounts_paid[] = {1001, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, (uint64_t)-1}; - const bool multi_out[] = {true}; - return generate_with(events, out_idx, mixin, 1, amounts_paid, false, multi_out, NULL, [&](cryptonote::transaction &tx, size_t tx_idx){ + DEFINE_TESTS_ERROR_CONTEXT("gen_bp_tx_invalid_borromean_type"); + const size_t mixin = 10; + const uint64_t amounts_paid[] = {5000, 5000, (uint64_t)-1}; + const rct::RangeProofType range_proof_type[] = {rct::RangeProofPaddedBulletproof}; + return generate_with(events, mixin, 1, amounts_paid, false, range_proof_type, NULL, [&](cryptonote::transaction &tx, size_t tx_idx){ CHECK_TEST_CONDITION(tx.rct_signatures.type == rct::RCTTypeBulletproof); - CHECK_TEST_CONDITION(tx.rct_signatures.p.bulletproofs.size() == 2); - rct::Bulletproof proof = tx.rct_signatures.p.bulletproofs[0]; - tx.rct_signatures.p.bulletproofs[0] = tx.rct_signatures.p.bulletproofs[1]; - tx.rct_signatures.p.bulletproofs[1] = proof; + tx.rct_signatures.type = rct::RCTTypeSimple; return true; }); } - diff --git a/tests/core_tests/bulletproofs.h b/tests/core_tests/bulletproofs.h index d3b65f5bd09..13faa3493e1 100644 --- a/tests/core_tests/bulletproofs.h +++ b/tests/core_tests/bulletproofs.h @@ -81,8 +81,8 @@ struct gen_bp_tx_validation_base : public test_chain_unit_base return true; } - bool generate_with(std::vector& events, const int *out_idx, int mixin, - size_t n_txes, const uint64_t *amounts_paid, bool valid, const bool *multi_out, + bool generate_with(std::vector& events, size_t mixin, + size_t n_txes, const uint64_t *amounts_paid, bool valid, const rct::RangeProofType *range_proof_type, const std::function &sources, std::vector &destinations, size_t)> &pre_tx, const std::function &post_tx) const; @@ -95,7 +95,7 @@ struct gen_bp_tx_validation_base : public test_chain_unit_base template<> struct get_test_options { - const std::pair hard_forks[4] = {std::make_pair(1, 0), std::make_pair(2, 1), std::make_pair(8, 69), std::make_pair(0, 0)}; + const std::pair hard_forks[4] = {std::make_pair(1, 0), std::make_pair(2, 1), std::make_pair(8, 73), std::make_pair(0, 0)}; const cryptonote::test_options test_options = { hard_forks }; @@ -120,17 +120,29 @@ struct gen_bp_tx_valid_2 : public gen_bp_tx_validation_base }; template<> struct get_test_options: public get_test_options {}; -struct gen_bp_tx_valid_4_2_1 : public gen_bp_tx_validation_base +struct gen_bp_tx_valid_3 : public gen_bp_tx_validation_base { bool generate(std::vector& events) const; }; -template<> struct get_test_options: public get_test_options {}; +template<> struct get_test_options: public get_test_options {}; -struct gen_bp_tx_valid_16_16 : public gen_bp_tx_validation_base +struct gen_bp_tx_valid_16 : public gen_bp_tx_validation_base { bool generate(std::vector& events) const; }; -template<> struct get_test_options: public get_test_options {}; +template<> struct get_test_options: public get_test_options {}; + +struct gen_bp_tx_invalid_4_2_1 : public gen_bp_tx_validation_base +{ + bool generate(std::vector& events) const; +}; +template<> struct get_test_options: public get_test_options {}; + +struct gen_bp_tx_invalid_16_16 : public gen_bp_tx_validation_base +{ + bool generate(std::vector& events) const; +}; +template<> struct get_test_options: public get_test_options {}; struct gen_bp_txs_valid_2_and_2 : public gen_bp_tx_validation_base { @@ -138,11 +150,11 @@ struct gen_bp_txs_valid_2_and_2 : public gen_bp_tx_validation_base }; template<> struct get_test_options: public get_test_options {}; -struct gen_bp_txs_valid_2_and_8_2_and_16_16_1 : public gen_bp_tx_validation_base +struct gen_bp_txs_invalid_2_and_8_2_and_16_16_1 : public gen_bp_tx_validation_base { bool generate(std::vector& events) const; }; -template<> struct get_test_options: public get_test_options {}; +template<> struct get_test_options: public get_test_options {}; struct gen_bp_tx_invalid_not_enough_proofs : public gen_bp_tx_validation_base { @@ -162,9 +174,8 @@ struct gen_bp_tx_invalid_wrong_amount : public gen_bp_tx_validation_base }; template<> struct get_test_options: public get_test_options {}; -struct gen_bp_tx_invalid_switched : public gen_bp_tx_validation_base +struct gen_bp_tx_invalid_borromean_type : public gen_bp_tx_validation_base { bool generate(std::vector& events) const; }; -template<> struct get_test_options: public get_test_options {}; - +template<> struct get_test_options: public get_test_options {}; diff --git a/tests/core_tests/chaingen.cpp b/tests/core_tests/chaingen.cpp index 41256a4f199..d3cb52246c1 100644 --- a/tests/core_tests/chaingen.cpp +++ b/tests/core_tests/chaingen.cpp @@ -69,13 +69,13 @@ void test_generator::get_block_chain(std::vector& blockchain, const std::reverse(blockchain.begin(), blockchain.end()); } -void test_generator::get_last_n_block_sizes(std::vector& block_sizes, const crypto::hash& head, size_t n) const +void test_generator::get_last_n_block_weights(std::vector& block_weights, const crypto::hash& head, size_t n) const { std::vector blockchain; get_block_chain(blockchain, head, n); BOOST_FOREACH(auto& bi, blockchain) { - block_sizes.push_back(bi.block_size); + block_weights.push_back(bi.block_weight); } } @@ -95,17 +95,17 @@ uint64_t test_generator::get_already_generated_coins(const cryptonote::block& bl return get_already_generated_coins(blk_hash); } -void test_generator::add_block(const cryptonote::block& blk, size_t tsx_size, std::vector& block_sizes, uint64_t already_generated_coins, uint8_t hf_version) +void test_generator::add_block(const cryptonote::block& blk, size_t txs_weight, std::vector& block_weights, uint64_t already_generated_coins, uint8_t hf_version) { - const size_t block_size = tsx_size + get_object_blobsize(blk.miner_tx); + const size_t block_weight = txs_weight + get_transaction_weight(blk.miner_tx); uint64_t block_reward; - get_block_reward(misc_utils::median(block_sizes), block_size, already_generated_coins, block_reward, hf_version); - m_blocks_info[get_block_hash(blk)] = block_info(blk.prev_id, already_generated_coins + block_reward, block_size); + get_block_reward(misc_utils::median(block_weights), block_weight, already_generated_coins, block_reward, hf_version); + m_blocks_info[get_block_hash(blk)] = block_info(blk.prev_id, already_generated_coins + block_reward, block_weight); } bool test_generator::construct_block(cryptonote::block& blk, uint64_t height, const crypto::hash& prev_id, const cryptonote::account_base& miner_acc, uint64_t timestamp, uint64_t already_generated_coins, - std::vector& block_sizes, const std::list& tx_list) + std::vector& block_weights, const std::list& tx_list) { blk.major_version = CURRENT_BLOCK_MAJOR_VERSION; blk.minor_version = CURRENT_BLOCK_MINOR_VERSION; @@ -121,52 +121,52 @@ bool test_generator::construct_block(cryptonote::block& blk, uint64_t height, co } uint64_t total_fee = 0; - size_t txs_size = 0; + size_t txs_weight = 0; BOOST_FOREACH(auto& tx, tx_list) { uint64_t fee = 0; bool r = get_tx_fee(tx, fee); CHECK_AND_ASSERT_MES(r, false, "wrong transaction passed to construct_block"); total_fee += fee; - txs_size += get_object_blobsize(tx); + txs_weight += get_transaction_weight(tx); } blk.miner_tx = AUTO_VAL_INIT(blk.miner_tx); - size_t target_block_size = txs_size + get_object_blobsize(blk.miner_tx); + size_t target_block_weight = txs_weight + get_transaction_weight(blk.miner_tx); while (true) { - if (!construct_miner_tx(height, misc_utils::median(block_sizes), already_generated_coins, target_block_size, total_fee, miner_acc.get_keys().m_account_address, blk.miner_tx, blobdata(), 10)) + if (!construct_miner_tx(height, misc_utils::median(block_weights), already_generated_coins, target_block_weight, total_fee, miner_acc.get_keys().m_account_address, blk.miner_tx, blobdata(), 10)) return false; - size_t actual_block_size = txs_size + get_object_blobsize(blk.miner_tx); - if (target_block_size < actual_block_size) + size_t actual_block_weight = txs_weight + get_transaction_weight(blk.miner_tx); + if (target_block_weight < actual_block_weight) { - target_block_size = actual_block_size; + target_block_weight = actual_block_weight; } - else if (actual_block_size < target_block_size) + else if (actual_block_weight < target_block_weight) { - size_t delta = target_block_size - actual_block_size; + size_t delta = target_block_weight - actual_block_weight; blk.miner_tx.extra.resize(blk.miner_tx.extra.size() + delta, 0); - actual_block_size = txs_size + get_object_blobsize(blk.miner_tx); - if (actual_block_size == target_block_size) + actual_block_weight = txs_weight + get_transaction_weight(blk.miner_tx); + if (actual_block_weight == target_block_weight) { break; } else { - CHECK_AND_ASSERT_MES(target_block_size < actual_block_size, false, "Unexpected block size"); - delta = actual_block_size - target_block_size; + CHECK_AND_ASSERT_MES(target_block_weight < actual_block_weight, false, "Unexpected block size"); + delta = actual_block_weight - target_block_weight; blk.miner_tx.extra.resize(blk.miner_tx.extra.size() - delta); - actual_block_size = txs_size + get_object_blobsize(blk.miner_tx); - if (actual_block_size == target_block_size) + actual_block_weight = txs_weight + get_transaction_weight(blk.miner_tx); + if (actual_block_weight == target_block_weight) { break; } else { - CHECK_AND_ASSERT_MES(actual_block_size < target_block_size, false, "Unexpected block size"); + CHECK_AND_ASSERT_MES(actual_block_weight < target_block_weight, false, "Unexpected block size"); blk.miner_tx.extra.resize(blk.miner_tx.extra.size() + delta, 0); - target_block_size = txs_size + get_object_blobsize(blk.miner_tx); + target_block_weight = txs_weight + get_transaction_weight(blk.miner_tx); } } } @@ -183,16 +183,16 @@ bool test_generator::construct_block(cryptonote::block& blk, uint64_t height, co while (!miner::find_nonce_for_given_block(blk, get_test_difficulty(), height)) blk.timestamp++; - add_block(blk, txs_size, block_sizes, already_generated_coins); + add_block(blk, txs_weight, block_weights, already_generated_coins); return true; } bool test_generator::construct_block(cryptonote::block& blk, const cryptonote::account_base& miner_acc, uint64_t timestamp) { - std::vector block_sizes; + std::vector block_weights; std::list tx_list; - return construct_block(blk, 0, null_hash, miner_acc, timestamp, 0, block_sizes, tx_list); + return construct_block(blk, 0, null_hash, miner_acc, timestamp, 0, block_weights, tx_list); } bool test_generator::construct_block(cryptonote::block& blk, const cryptonote::block& blk_prev, @@ -204,10 +204,10 @@ bool test_generator::construct_block(cryptonote::block& blk, const cryptonote::b // Keep difficulty unchanged uint64_t timestamp = blk_prev.timestamp + DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN; uint64_t already_generated_coins = get_already_generated_coins(prev_id); - std::vector block_sizes; - get_last_n_block_sizes(block_sizes, prev_id, CRYPTONOTE_REWARD_BLOCKS_WINDOW); + std::vector block_weights; + get_last_n_block_weights(block_weights, prev_id, CRYPTONOTE_REWARD_BLOCKS_WINDOW); - return construct_block(blk, height, prev_id, miner_acc, timestamp, already_generated_coins, block_sizes, tx_list); + return construct_block(blk, height, prev_id, miner_acc, timestamp, already_generated_coins, block_weights, tx_list); } bool test_generator::construct_block_manually(block& blk, const block& prev_block, const account_base& miner_acc, @@ -216,7 +216,7 @@ bool test_generator::construct_block_manually(block& blk, const block& prev_bloc const crypto::hash& prev_id/* = crypto::hash()*/, const difficulty_type& diffic/* = 1*/, const transaction& miner_tx/* = transaction()*/, const std::vector& tx_hashes/* = std::vector()*/, - size_t txs_sizes/* = 0*/, size_t max_outs/* = 0*/, uint8_t hf_version/* = 1*/) + size_t txs_weight/* = 0*/, size_t max_outs/* = 0*/, uint8_t hf_version/* = 1*/) { blk.major_version = actual_params & bf_major_ver ? major_ver : CURRENT_BLOCK_MAJOR_VERSION; blk.minor_version = actual_params & bf_minor_ver ? minor_ver : CURRENT_BLOCK_MINOR_VERSION; @@ -228,17 +228,17 @@ bool test_generator::construct_block_manually(block& blk, const block& prev_bloc size_t height = get_block_height(prev_block) + 1; uint64_t already_generated_coins = get_already_generated_coins(prev_block); - std::vector block_sizes; - get_last_n_block_sizes(block_sizes, get_block_hash(prev_block), CRYPTONOTE_REWARD_BLOCKS_WINDOW); + std::vector block_weights; + get_last_n_block_weights(block_weights, get_block_hash(prev_block), CRYPTONOTE_REWARD_BLOCKS_WINDOW); if (actual_params & bf_miner_tx) { blk.miner_tx = miner_tx; } else { - size_t current_block_size = txs_sizes + get_object_blobsize(blk.miner_tx); + size_t current_block_weight = txs_weight + get_transaction_weight(blk.miner_tx); // TODO: This will work, until size of constructed block is less then CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE - if (!construct_miner_tx(height, misc_utils::median(block_sizes), already_generated_coins, current_block_size, 0, miner_acc.get_keys().m_account_address, blk.miner_tx, blobdata(), max_outs, hf_version)) + if (!construct_miner_tx(height, misc_utils::median(block_weights), already_generated_coins, current_block_weight, 0, miner_acc.get_keys().m_account_address, blk.miner_tx, blobdata(), max_outs, hf_version)) return false; } @@ -247,16 +247,16 @@ bool test_generator::construct_block_manually(block& blk, const block& prev_bloc difficulty_type a_diffic = actual_params & bf_diffic ? diffic : get_test_difficulty(); fill_nonce(blk, a_diffic, height); - add_block(blk, txs_sizes, block_sizes, already_generated_coins, hf_version); + add_block(blk, txs_weight, block_weights, already_generated_coins, hf_version); return true; } bool test_generator::construct_block_manually_tx(cryptonote::block& blk, const cryptonote::block& prev_block, const cryptonote::account_base& miner_acc, - const std::vector& tx_hashes, size_t txs_size) + const std::vector& tx_hashes, size_t txs_weight) { - return construct_block_manually(blk, prev_block, miner_acc, bf_tx_hashes, 0, 0, 0, crypto::hash(), 0, transaction(), tx_hashes, txs_size); + return construct_block_manually(blk, prev_block, miner_acc, bf_tx_hashes, 0, 0, 0, crypto::hash(), 0, transaction(), tx_hashes, txs_weight); } diff --git a/tests/core_tests/chaingen.h b/tests/core_tests/chaingen.h index e44bcd37b42..34b205ae58c 100644 --- a/tests/core_tests/chaingen.h +++ b/tests/core_tests/chaingen.h @@ -159,20 +159,20 @@ class test_generator block_info() : prev_id() , already_generated_coins(0) - , block_size(0) + , block_weight(0) { } - block_info(crypto::hash a_prev_id, uint64_t an_already_generated_coins, size_t a_block_size) + block_info(crypto::hash a_prev_id, uint64_t an_already_generated_coins, size_t a_block_weight) : prev_id(a_prev_id) , already_generated_coins(an_already_generated_coins) - , block_size(a_block_size) + , block_weight(a_block_weight) { } crypto::hash prev_id; uint64_t already_generated_coins; - size_t block_size; + size_t block_weight; }; enum block_fields @@ -190,15 +190,15 @@ class test_generator }; void get_block_chain(std::vector& blockchain, const crypto::hash& head, size_t n) const; - void get_last_n_block_sizes(std::vector& block_sizes, const crypto::hash& head, size_t n) const; + void get_last_n_block_weights(std::vector& block_weights, const crypto::hash& head, size_t n) const; uint64_t get_already_generated_coins(const crypto::hash& blk_id) const; uint64_t get_already_generated_coins(const cryptonote::block& blk) const; - void add_block(const cryptonote::block& blk, size_t tsx_size, std::vector& block_sizes, uint64_t already_generated_coins, + void add_block(const cryptonote::block& blk, size_t tsx_size, std::vector& block_weights, uint64_t already_generated_coins, uint8_t hf_version = 1); bool construct_block(cryptonote::block& blk, uint64_t height, const crypto::hash& prev_id, const cryptonote::account_base& miner_acc, uint64_t timestamp, uint64_t already_generated_coins, - std::vector& block_sizes, const std::list& tx_list); + std::vector& block_weights, const std::list& tx_list); bool construct_block(cryptonote::block& blk, const cryptonote::account_base& miner_acc, uint64_t timestamp); bool construct_block(cryptonote::block& blk, const cryptonote::block& blk_prev, const cryptonote::account_base& miner_acc, const std::list& tx_list = std::list()); diff --git a/tests/core_tests/chaingen_main.cpp b/tests/core_tests/chaingen_main.cpp index 60de110fac0..278ef81a62a 100644 --- a/tests/core_tests/chaingen_main.cpp +++ b/tests/core_tests/chaingen_main.cpp @@ -227,14 +227,16 @@ int main(int argc, char* argv[]) GENERATE_AND_PLAY(gen_bp_tx_valid_1); GENERATE_AND_PLAY(gen_bp_tx_invalid_1_1); GENERATE_AND_PLAY(gen_bp_tx_valid_2); - GENERATE_AND_PLAY(gen_bp_tx_valid_4_2_1); - GENERATE_AND_PLAY(gen_bp_tx_valid_16_16); + GENERATE_AND_PLAY(gen_bp_tx_valid_3); + GENERATE_AND_PLAY(gen_bp_tx_valid_16); + GENERATE_AND_PLAY(gen_bp_tx_invalid_4_2_1); + GENERATE_AND_PLAY(gen_bp_tx_invalid_16_16); GENERATE_AND_PLAY(gen_bp_txs_valid_2_and_2); - GENERATE_AND_PLAY(gen_bp_txs_valid_2_and_8_2_and_16_16_1); + GENERATE_AND_PLAY(gen_bp_txs_invalid_2_and_8_2_and_16_16_1); GENERATE_AND_PLAY(gen_bp_tx_invalid_not_enough_proofs); GENERATE_AND_PLAY(gen_bp_tx_invalid_too_many_proofs); GENERATE_AND_PLAY(gen_bp_tx_invalid_wrong_amount); - GENERATE_AND_PLAY(gen_bp_tx_invalid_switched); + GENERATE_AND_PLAY(gen_bp_tx_invalid_borromean_type); el::Level level = (failed_tests.empty() ? el::Level::Info : el::Level::Error); MLOG(level, "\nREPORT:"); diff --git a/tests/performance_tests/check_tx_signature.h b/tests/performance_tests/check_tx_signature.h index bcd3b3dfe5d..ee382d9ad7b 100644 --- a/tests/performance_tests/check_tx_signature.h +++ b/tests/performance_tests/check_tx_signature.h @@ -40,7 +40,7 @@ #include "multi_tx_test_base.h" -template +template class test_check_tx_signature : private multi_tx_test_base { static_assert(0 < a_ring_size, "ring_size must be greater than 0"); @@ -50,7 +50,6 @@ class test_check_tx_signature : private multi_tx_test_base static const size_t ring_size = a_ring_size; static const size_t outputs = a_outputs; static const bool rct = a_rct; - static const bool bulletproof = a_bulletproof; typedef multi_tx_test_base base_class; @@ -72,7 +71,7 @@ class test_check_tx_signature : private multi_tx_test_base std::vector additional_tx_keys; std::unordered_map subaddresses; subaddresses[this->m_miners[this->real_source_idx].get_keys().m_account_address.m_spend_public_key] = {0,0}; - if (!construct_tx_and_get_tx_key(this->m_miners[this->real_source_idx].get_keys(), subaddresses, this->m_sources, destinations, cryptonote::account_public_address{}, std::vector(), m_tx, 0, tx_key, additional_tx_keys, rct, bulletproof ? rct::RangeProofMultiOutputBulletproof : rct::RangeProofBorromean)) + if (!construct_tx_and_get_tx_key(this->m_miners[this->real_source_idx].get_keys(), subaddresses, this->m_sources, destinations, cryptonote::account_public_address{}, std::vector(), m_tx, 0, tx_key, additional_tx_keys, rct, range_proof_type)) return false; get_transaction_prefix_hash(m_tx, m_tx_prefix_hash); @@ -136,7 +135,7 @@ class test_check_tx_signature_aggregated_bulletproofs : private multi_tx_test_ba m_txes.resize(a_num_txes + (extra_outs > 0 ? 1 : 0)); for (size_t n = 0; n < a_num_txes; ++n) { - if (!construct_tx_and_get_tx_key(this->m_miners[this->real_source_idx].get_keys(), subaddresses, this->m_sources, destinations, cryptonote::account_public_address{}, std::vector(), m_txes[n], 0, tx_key, additional_tx_keys, true, rct::RangeProofMultiOutputBulletproof)) + if (!construct_tx_and_get_tx_key(this->m_miners[this->real_source_idx].get_keys(), subaddresses, this->m_sources, destinations, cryptonote::account_public_address{}, std::vector(), m_txes[n], 0, tx_key, additional_tx_keys, true, rct::RangeProofPaddedBulletproof)) return false; } diff --git a/tests/performance_tests/main.cpp b/tests/performance_tests/main.cpp index 19c2cb5e5e4..6007242bfb1 100644 --- a/tests/performance_tests/main.cpp +++ b/tests/performance_tests/main.cpp @@ -132,21 +132,25 @@ int main(int argc, char** argv) TEST_PERFORMANCE3(filter, p, test_construct_tx, 100, 2, true); TEST_PERFORMANCE3(filter, p, test_construct_tx, 100, 10, true); - TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 1, 2, false, false); - TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 2, 2, false, false); - TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 10, 2, false, false); - TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 100, 2, false, false); - TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 2, 10, false, false); - - TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 2, 2, true, false); - TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 10, 2, true, false); - TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 100, 2, true, false); - TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 2, 10, true, false); - - TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 2, 2, true, true); - TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 10, 2, true, true); - TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 100, 2, true, true); - TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 2, 10, true, true); + TEST_PERFORMANCE3(filter, p, test_check_tx_signature, 1, 2, false); + TEST_PERFORMANCE3(filter, p, test_check_tx_signature, 2, 2, false); + TEST_PERFORMANCE3(filter, p, test_check_tx_signature, 10, 2, false); + TEST_PERFORMANCE3(filter, p, test_check_tx_signature, 100, 2, false); + TEST_PERFORMANCE3(filter, p, test_check_tx_signature, 2, 10, false); + + TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 2, 2, true, rct::RangeProofBorromean); + TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 10, 2, true, rct::RangeProofBorromean); + TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 100, 2, true, rct::RangeProofBorromean); + TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 2, 10, true, rct::RangeProofBorromean); + + TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 2, 2, true, rct::RangeProofPaddedBulletproof); + TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 2, 2, true, rct::RangeProofMultiOutputBulletproof); + TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 10, 2, true, rct::RangeProofPaddedBulletproof); + TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 10, 2, true, rct::RangeProofMultiOutputBulletproof); + TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 100, 2, true, rct::RangeProofPaddedBulletproof); + TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 100, 2, true, rct::RangeProofMultiOutputBulletproof); + TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 2, 10, true, rct::RangeProofPaddedBulletproof); + TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 2, 10, true, rct::RangeProofMultiOutputBulletproof); TEST_PERFORMANCE3(filter, p, test_check_tx_signature_aggregated_bulletproofs, 2, 2, 64); TEST_PERFORMANCE3(filter, p, test_check_tx_signature_aggregated_bulletproofs, 10, 2, 64); diff --git a/tests/unit_tests/block_reward.cpp b/tests/unit_tests/block_reward.cpp index ca863ded919..a897e4140fc 100644 --- a/tests/unit_tests/block_reward.cpp +++ b/tests/unit_tests/block_reward.cpp @@ -40,14 +40,14 @@ namespace class block_reward_and_already_generated_coins : public ::testing::Test { protected: - static const size_t current_block_size = CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 / 2; + static const size_t current_block_weight = CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 / 2; bool m_block_not_too_big; uint64_t m_block_reward; }; #define TEST_ALREADY_GENERATED_COINS(already_generated_coins, expected_reward) \ - m_block_not_too_big = get_block_reward(0, current_block_size, already_generated_coins, m_block_reward,1); \ + m_block_not_too_big = get_block_reward(0, current_block_weight, already_generated_coins, m_block_reward,1); \ ASSERT_TRUE(m_block_not_too_big); \ ASSERT_EQ(m_block_reward, expected_reward); @@ -74,7 +74,7 @@ namespace } //-------------------------------------------------------------------------------------------------------------------- - class block_reward_and_current_block_size : public ::testing::Test + class block_reward_and_current_block_weight : public ::testing::Test { protected: virtual void SetUp() @@ -84,9 +84,9 @@ namespace ASSERT_LT(CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1, m_standard_block_reward); } - void do_test(size_t median_block_size, size_t current_block_size) + void do_test(size_t median_block_weight, size_t current_block_weight) { - m_block_not_too_big = get_block_reward(median_block_size, current_block_size, already_generated_coins, m_block_reward, 1); + m_block_not_too_big = get_block_reward(median_block_weight, current_block_weight, already_generated_coins, m_block_reward, 1); } static const uint64_t already_generated_coins = 0; @@ -96,28 +96,28 @@ namespace uint64_t m_standard_block_reward; }; - TEST_F(block_reward_and_current_block_size, handles_block_size_less_relevance_level) + TEST_F(block_reward_and_current_block_weight, handles_block_weight_less_relevance_level) { do_test(0, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 - 1); ASSERT_TRUE(m_block_not_too_big); ASSERT_EQ(m_block_reward, m_standard_block_reward); } - TEST_F(block_reward_and_current_block_size, handles_block_size_eq_relevance_level) + TEST_F(block_reward_and_current_block_weight, handles_block_weight_eq_relevance_level) { do_test(0, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1); ASSERT_TRUE(m_block_not_too_big); ASSERT_EQ(m_block_reward, m_standard_block_reward); } - TEST_F(block_reward_and_current_block_size, handles_block_size_gt_relevance_level) + TEST_F(block_reward_and_current_block_weight, handles_block_weight_gt_relevance_level) { do_test(0, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 + 1); ASSERT_TRUE(m_block_not_too_big); ASSERT_LT(m_block_reward, m_standard_block_reward); } - TEST_F(block_reward_and_current_block_size, handles_block_size_less_2_relevance_level) + TEST_F(block_reward_and_current_block_weight, handles_block_weight_less_2_relevance_level) { do_test(0, 2 * CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 - 1); ASSERT_TRUE(m_block_not_too_big); @@ -125,21 +125,21 @@ namespace ASSERT_LT(0, m_block_reward); } - TEST_F(block_reward_and_current_block_size, handles_block_size_eq_2_relevance_level) + TEST_F(block_reward_and_current_block_weight, handles_block_weight_eq_2_relevance_level) { do_test(0, 2 * CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1); ASSERT_TRUE(m_block_not_too_big); ASSERT_EQ(0, m_block_reward); } - TEST_F(block_reward_and_current_block_size, handles_block_size_gt_2_relevance_level) + TEST_F(block_reward_and_current_block_weight, handles_block_weight_gt_2_relevance_level) { do_test(0, 2 * CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 + 1); ASSERT_FALSE(m_block_not_too_big); } #ifdef __x86_64__ // For 64-bit systems only, because block size is limited to size_t. - TEST_F(block_reward_and_current_block_size, fails_on_huge_median_size) + TEST_F(block_reward_and_current_block_weight, fails_on_huge_median_size) { #if !defined(NDEBUG) size_t huge_size = std::numeric_limits::max() + UINT64_C(2); @@ -147,7 +147,7 @@ namespace #endif } - TEST_F(block_reward_and_current_block_size, fails_on_huge_block_size) + TEST_F(block_reward_and_current_block_weight, fails_on_huge_block_weight) { #if !defined(NDEBUG) size_t huge_size = std::numeric_limits::max() + UINT64_C(2); @@ -157,94 +157,94 @@ namespace #endif // __x86_64__ //-------------------------------------------------------------------------------------------------------------------- - class block_reward_and_last_block_sizes : public ::testing::Test + class block_reward_and_last_block_weights : public ::testing::Test { protected: virtual void SetUp() { - m_last_block_sizes.push_back(3 * CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1); - m_last_block_sizes.push_back(5 * CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1); - m_last_block_sizes.push_back(7 * CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1); - m_last_block_sizes.push_back(11 * CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1); - m_last_block_sizes.push_back(13 * CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1); + m_last_block_weights.push_back(3 * CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1); + m_last_block_weights.push_back(5 * CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1); + m_last_block_weights.push_back(7 * CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1); + m_last_block_weights.push_back(11 * CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1); + m_last_block_weights.push_back(13 * CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1); - m_last_block_sizes_median = 7 * CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1; + m_last_block_weights_median = 7 * CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1; - m_block_not_too_big = get_block_reward(epee::misc_utils::median(m_last_block_sizes), 0, already_generated_coins, m_standard_block_reward, 1); + m_block_not_too_big = get_block_reward(epee::misc_utils::median(m_last_block_weights), 0, already_generated_coins, m_standard_block_reward, 1); ASSERT_TRUE(m_block_not_too_big); ASSERT_LT(CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1, m_standard_block_reward); } - void do_test(size_t current_block_size) + void do_test(size_t current_block_weight) { - m_block_not_too_big = get_block_reward(epee::misc_utils::median(m_last_block_sizes), current_block_size, already_generated_coins, m_block_reward, 1); + m_block_not_too_big = get_block_reward(epee::misc_utils::median(m_last_block_weights), current_block_weight, already_generated_coins, m_block_reward, 1); } static const uint64_t already_generated_coins = 0; - std::vector m_last_block_sizes; - uint64_t m_last_block_sizes_median; + std::vector m_last_block_weights; + uint64_t m_last_block_weights_median; bool m_block_not_too_big; uint64_t m_block_reward; uint64_t m_standard_block_reward; }; - TEST_F(block_reward_and_last_block_sizes, handles_block_size_less_median) + TEST_F(block_reward_and_last_block_weights, handles_block_weight_less_median) { - do_test(m_last_block_sizes_median - 1); + do_test(m_last_block_weights_median - 1); ASSERT_TRUE(m_block_not_too_big); ASSERT_EQ(m_block_reward, m_standard_block_reward); } - TEST_F(block_reward_and_last_block_sizes, handles_block_size_eq_median) + TEST_F(block_reward_and_last_block_weights, handles_block_weight_eq_median) { - do_test(m_last_block_sizes_median); + do_test(m_last_block_weights_median); ASSERT_TRUE(m_block_not_too_big); ASSERT_EQ(m_block_reward, m_standard_block_reward); } - TEST_F(block_reward_and_last_block_sizes, handles_block_size_gt_median) + TEST_F(block_reward_and_last_block_weights, handles_block_weight_gt_median) { - do_test(m_last_block_sizes_median + 1); + do_test(m_last_block_weights_median + 1); ASSERT_TRUE(m_block_not_too_big); ASSERT_LT(m_block_reward, m_standard_block_reward); } - TEST_F(block_reward_and_last_block_sizes, handles_block_size_less_2_medians) + TEST_F(block_reward_and_last_block_weights, handles_block_weight_less_2_medians) { - do_test(2 * m_last_block_sizes_median - 1); + do_test(2 * m_last_block_weights_median - 1); ASSERT_TRUE(m_block_not_too_big); ASSERT_LT(m_block_reward, m_standard_block_reward); ASSERT_LT(0, m_block_reward); } - TEST_F(block_reward_and_last_block_sizes, handles_block_size_eq_2_medians) + TEST_F(block_reward_and_last_block_weights, handles_block_weight_eq_2_medians) { - do_test(2 * m_last_block_sizes_median); + do_test(2 * m_last_block_weights_median); ASSERT_TRUE(m_block_not_too_big); ASSERT_EQ(0, m_block_reward); } - TEST_F(block_reward_and_last_block_sizes, handles_block_size_gt_2_medians) + TEST_F(block_reward_and_last_block_weights, handles_block_weight_gt_2_medians) { - do_test(2 * m_last_block_sizes_median + 1); + do_test(2 * m_last_block_weights_median + 1); ASSERT_FALSE(m_block_not_too_big); } - TEST_F(block_reward_and_last_block_sizes, calculates_correctly) + TEST_F(block_reward_and_last_block_weights, calculates_correctly) { - ASSERT_EQ(0, m_last_block_sizes_median % 8); + ASSERT_EQ(0, m_last_block_weights_median % 8); - do_test(m_last_block_sizes_median * 9 / 8); + do_test(m_last_block_weights_median * 9 / 8); ASSERT_TRUE(m_block_not_too_big); ASSERT_EQ(m_block_reward, m_standard_block_reward * 63 / 64); // 3/2 = 12/8 - do_test(m_last_block_sizes_median * 3 / 2); + do_test(m_last_block_weights_median * 3 / 2); ASSERT_TRUE(m_block_not_too_big); ASSERT_EQ(m_block_reward, m_standard_block_reward * 3 / 4); - do_test(m_last_block_sizes_median * 15 / 8); + do_test(m_last_block_weights_median * 15 / 8); ASSERT_TRUE(m_block_not_too_big); ASSERT_EQ(m_block_reward, m_standard_block_reward * 15 / 64); } diff --git a/tests/unit_tests/blockchain_db.cpp b/tests/unit_tests/blockchain_db.cpp index 3a62fba697c..7e7ce9bf770 100644 --- a/tests/unit_tests/blockchain_db.cpp +++ b/tests/unit_tests/blockchain_db.cpp @@ -319,7 +319,7 @@ TYPED_TEST(BlockchainDBTest, RetrieveBlockData) ASSERT_NO_THROW(this->m_db->add_block(this->m_blocks[0], t_sizes[0], t_diffs[0], t_coins[0], this->m_txs[0])); - ASSERT_EQ(t_sizes[0], this->m_db->get_block_size(0)); + ASSERT_EQ(t_sizes[0], this->m_db->get_block_weight(0)); ASSERT_EQ(t_diffs[0], this->m_db->get_block_cumulative_difficulty(0)); ASSERT_EQ(t_diffs[0], this->m_db->get_block_difficulty(0)); ASSERT_EQ(t_coins[0], this->m_db->get_block_already_generated_coins(0)); diff --git a/tests/unit_tests/bulletproofs.cpp b/tests/unit_tests/bulletproofs.cpp index e3498207006..45075c63da3 100644 --- a/tests/unit_tests/bulletproofs.cpp +++ b/tests/unit_tests/bulletproofs.cpp @@ -129,7 +129,7 @@ TEST(bulletproofs, multi_splitting) } rct::ctkeyV outSk; - rct::rctSig s = rct::genRctSimple(rct::zero(), sc, destinations, inamounts, outamounts, available, mixRing, amount_keys, NULL, NULL, index, outSk, rct::RangeProofMultiOutputBulletproof, hw::get_device("default")); + rct::rctSig s = rct::genRctSimple(rct::zero(), sc, destinations, inamounts, outamounts, available, mixRing, amount_keys, NULL, NULL, index, outSk, rct::RangeProofPaddedBulletproof, hw::get_device("default")); ASSERT_TRUE(rct::verRctSimple(s)); for (size_t i = 0; i < n_outputs; ++i) { diff --git a/tests/unit_tests/fee.cpp b/tests/unit_tests/fee.cpp index c5589ab966b..8ccb38fc9fb 100644 --- a/tests/unit_tests/fee.cpp +++ b/tests/unit_tests/fee.cpp @@ -58,46 +58,46 @@ namespace TEST_F(fee, 10xmr) { // CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 and lower are clamped - ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(10000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2, 3), clamp_fee(2000000000)); - ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(10000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 / 2, 3), clamp_fee(2000000000)); - ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(10000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 / 100, 3), clamp_fee(2000000000)); - ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(10000000000000, 1, 3), 2000000000); + ASSERT_EQ(Blockchain::get_dynamic_base_fee(10000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2, 3), clamp_fee(2000000000)); + ASSERT_EQ(Blockchain::get_dynamic_base_fee(10000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 / 2, 3), clamp_fee(2000000000)); + ASSERT_EQ(Blockchain::get_dynamic_base_fee(10000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 / 100, 3), clamp_fee(2000000000)); + ASSERT_EQ(Blockchain::get_dynamic_base_fee(10000000000000, 1, 3), 2000000000); // higher is inverse proportional - ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(10000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 2, 3), clamp_fee(2000000000 / 2)); - ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(10000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 10, 3), clamp_fee(2000000000 / 10)); - ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(10000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 1000, 3), clamp_fee(2000000000 / 1000)); - ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(10000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 20000ull, 3), clamp_fee(2000000000 / 20000)); + ASSERT_EQ(Blockchain::get_dynamic_base_fee(10000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 2, 3), clamp_fee(2000000000 / 2)); + ASSERT_EQ(Blockchain::get_dynamic_base_fee(10000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 10, 3), clamp_fee(2000000000 / 10)); + ASSERT_EQ(Blockchain::get_dynamic_base_fee(10000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 1000, 3), clamp_fee(2000000000 / 1000)); + ASSERT_EQ(Blockchain::get_dynamic_base_fee(10000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 20000ull, 3), clamp_fee(2000000000 / 20000)); } TEST_F(fee, 1xmr) { // CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 and lower are clamped - ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(1000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2, 3), clamp_fee(200000000)); - ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(1000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 / 2, 3), clamp_fee(200000000)); - ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(1000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 / 100, 3), clamp_fee(200000000)); - ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(1000000000000, 1, 3), 200000000); + ASSERT_EQ(Blockchain::get_dynamic_base_fee(1000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2, 3), clamp_fee(200000000)); + ASSERT_EQ(Blockchain::get_dynamic_base_fee(1000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 / 2, 3), clamp_fee(200000000)); + ASSERT_EQ(Blockchain::get_dynamic_base_fee(1000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 / 100, 3), clamp_fee(200000000)); + ASSERT_EQ(Blockchain::get_dynamic_base_fee(1000000000000, 1, 3), 200000000); // higher is inverse proportional - ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(1000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 2, 3), clamp_fee(200000000 / 2)); - ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(1000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 10, 3), clamp_fee(200000000 / 10)); - ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(1000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 1000, 3), clamp_fee(200000000 / 1000)); - ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(1000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 20000ull, 3), clamp_fee(200000000 / 20000)); + ASSERT_EQ(Blockchain::get_dynamic_base_fee(1000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 2, 3), clamp_fee(200000000 / 2)); + ASSERT_EQ(Blockchain::get_dynamic_base_fee(1000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 10, 3), clamp_fee(200000000 / 10)); + ASSERT_EQ(Blockchain::get_dynamic_base_fee(1000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 1000, 3), clamp_fee(200000000 / 1000)); + ASSERT_EQ(Blockchain::get_dynamic_base_fee(1000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 20000ull, 3), clamp_fee(200000000 / 20000)); } TEST_F(fee, dot3xmr) { // CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 and lower are clamped - ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(300000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2, 3), clamp_fee(60000000)); - ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(300000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 / 2, 3), clamp_fee(60000000)); - ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(300000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 / 100, 3), clamp_fee(60000000)); - ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(300000000000, 1, 3), 60000000); + ASSERT_EQ(Blockchain::get_dynamic_base_fee(300000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2, 3), clamp_fee(60000000)); + ASSERT_EQ(Blockchain::get_dynamic_base_fee(300000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 / 2, 3), clamp_fee(60000000)); + ASSERT_EQ(Blockchain::get_dynamic_base_fee(300000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 / 100, 3), clamp_fee(60000000)); + ASSERT_EQ(Blockchain::get_dynamic_base_fee(300000000000, 1, 3), 60000000); // higher is inverse proportional - ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(300000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 2, 3), clamp_fee(60000000 / 2)); - ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(300000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 10, 3), clamp_fee(60000000 / 10)); - ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(300000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 1000, 3), clamp_fee(60000000 / 1000)); - ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(300000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 20000ull, 3), clamp_fee(60000000 / 20000)); + ASSERT_EQ(Blockchain::get_dynamic_base_fee(300000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 2, 3), clamp_fee(60000000 / 2)); + ASSERT_EQ(Blockchain::get_dynamic_base_fee(300000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 10, 3), clamp_fee(60000000 / 10)); + ASSERT_EQ(Blockchain::get_dynamic_base_fee(300000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 1000, 3), clamp_fee(60000000 / 1000)); + ASSERT_EQ(Blockchain::get_dynamic_base_fee(300000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 20000ull, 3), clamp_fee(60000000 / 20000)); } static bool is_more_or_less(double x, double y) @@ -116,7 +116,7 @@ namespace 600000000000ull, // .6 monero, minimum reward per block at 2min 300000000000ull, // .3 monero, minimum reward per block at 1min }; - static const uint64_t median_block_sizes[] = { + static const uint64_t median_block_weights[] = { CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 2, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 10, @@ -127,9 +127,9 @@ namespace for (uint64_t block_reward: block_rewards) { - for (uint64_t median_block_size: median_block_sizes) + for (uint64_t median_block_weight: median_block_weights) { - ASSERT_TRUE(is_more_or_less(Blockchain::get_dynamic_per_kb_fee(block_reward, median_block_size, 3) * (median_block_size / 1024.) * MAX_MULTIPLIER / (double)block_reward, 1.992 * 1000 / 1024)); + ASSERT_TRUE(is_more_or_less(Blockchain::get_dynamic_base_fee(block_reward, median_block_weight, 3) * (median_block_weight / 1024.) * MAX_MULTIPLIER / (double)block_reward, 1.992 * 1000 / 1024)); } } } diff --git a/tests/unit_tests/hardfork.cpp b/tests/unit_tests/hardfork.cpp index 8155c65f940..47177db1c6b 100644 --- a/tests/unit_tests/hardfork.cpp +++ b/tests/unit_tests/hardfork.cpp @@ -72,7 +72,7 @@ class TestDB: public BlockchainDB { virtual uint64_t get_block_timestamp(const uint64_t& height) const { return 0; } virtual std::vector get_block_cumulative_rct_outputs(const std::vector &heights) const { return {}; } virtual uint64_t get_top_block_timestamp() const { return 0; } - virtual size_t get_block_size(const uint64_t& height) const { return 128; } + virtual size_t get_block_weight(const uint64_t& height) const { return 128; } virtual difficulty_type get_block_cumulative_difficulty(const uint64_t& height) const { return 10; } virtual difficulty_type get_block_difficulty(const uint64_t& height) const { return 0; } virtual uint64_t get_block_already_generated_coins(const uint64_t& height) const { return 10000000000; } @@ -130,7 +130,7 @@ class TestDB: public BlockchainDB { virtual bool for_all_txpool_txes(std::function, bool include_blob = false, bool include_unrelayed_txes = false) const { return false; } virtual void add_block( const block& blk - , const size_t& block_size + , size_t block_weight , const difficulty_type& cumulative_difficulty , const uint64_t& coins_generated , uint64_t num_rct_outs From fe0fa3b9c5098ae61f9c60ddc9f1f3dd5b008ec6 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 24 Jul 2018 19:56:48 +0100 Subject: [PATCH 0370/1404] bulletproofs: reject x, y, z, or w[i] being zero Also try again when we're generate a proof with those characteristics Reported by QuarksLab. --- src/ringct/bulletproofs.cc | 65 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 63 insertions(+), 2 deletions(-) diff --git a/src/ringct/bulletproofs.cc b/src/ringct/bulletproofs.cc index d3822c454d0..3f4a6fd10b0 100644 --- a/src/ringct/bulletproofs.cc +++ b/src/ringct/bulletproofs.cc @@ -473,6 +473,7 @@ Bulletproof bulletproof_PROVE(const rct::key &sv, const rct::key &gamma) CHECK_AND_ASSERT_THROW_MES(test_aR == v_test, "test_aR failed"); #endif +try_again: PERF_TIMER_START_BP(PROVE_step1); // PAPER LINES 38-39 rct::key alpha = rct::skGen(); @@ -489,7 +490,19 @@ Bulletproof bulletproof_PROVE(const rct::key &sv, const rct::key &gamma) // PAPER LINES 43-45 rct::key y = hash_cache_mash(hash_cache, A, S); + if (y == rct::zero()) + { + PERF_TIMER_STOP(PROVE_step1); + MINFO("y is 0, trying again"); + goto try_again; + } rct::key z = hash_cache = rct::hash_to_scalar(y); + if (z == rct::zero()) + { + PERF_TIMER_STOP(PROVE_step1); + MINFO("z is 0, trying again"); + goto try_again; + } // Polynomial construction before PAPER LINE 46 rct::key t0 = rct::zero(); @@ -552,6 +565,12 @@ Bulletproof bulletproof_PROVE(const rct::key &sv, const rct::key &gamma) // PAPER LINES 49-51 rct::key x = hash_cache_mash(hash_cache, z, T1, T2); + if (x == rct::zero()) + { + PERF_TIMER_STOP(PROVE_step2); + MINFO("x is 0, trying again"); + goto try_again; + } // PAPER LINES 52-53 rct::key taux = rct::zero(); @@ -625,6 +644,12 @@ Bulletproof bulletproof_PROVE(const rct::key &sv, const rct::key &gamma) // PAPER LINES 21-22 w[round] = hash_cache_mash(hash_cache, L[round], R[round]); + if (w[round] == rct::zero()) + { + PERF_TIMER_STOP(PROVE_step4); + MINFO("w[round] is 0, trying again"); + goto try_again; + } // PAPER LINES 24-25 const rct::key winv = invert(w[round]); @@ -713,8 +738,6 @@ Bulletproof bulletproof_PROVE(const rct::keyV &sv, const rct::keyV &gamma) } PERF_TIMER_STOP(PROVE_aLaR); - rct::key hash_cache = rct::hash_to_scalar(V); - // DEBUG: Test to ensure this recovers the value #ifdef DEBUG_BP for (size_t j = 0; j < M; ++j) @@ -735,6 +758,9 @@ Bulletproof bulletproof_PROVE(const rct::keyV &sv, const rct::keyV &gamma) } #endif +try_again: + rct::key hash_cache = rct::hash_to_scalar(V); + PERF_TIMER_START_BP(PROVE_step1); // PAPER LINES 38-39 rct::key alpha = rct::skGen(); @@ -751,7 +777,19 @@ Bulletproof bulletproof_PROVE(const rct::keyV &sv, const rct::keyV &gamma) // PAPER LINES 43-45 rct::key y = hash_cache_mash(hash_cache, A, S); + if (y == rct::zero()) + { + PERF_TIMER_STOP(PROVE_step1); + MINFO("y is 0, trying again"); + goto try_again; + } rct::key z = hash_cache = rct::hash_to_scalar(y); + if (z == rct::zero()) + { + PERF_TIMER_STOP(PROVE_step1); + MINFO("z is 0, trying again"); + goto try_again; + } // Polynomial construction by coefficients const auto zMN = vector_dup(z, MN); @@ -799,6 +837,12 @@ Bulletproof bulletproof_PROVE(const rct::keyV &sv, const rct::keyV &gamma) // PAPER LINES 49-51 rct::key x = hash_cache_mash(hash_cache, z, T1, T2); + if (x == rct::zero()) + { + PERF_TIMER_STOP(PROVE_step2); + MINFO("x is 0, trying again"); + goto try_again; + } // PAPER LINES 52-53 rct::key taux; @@ -835,6 +879,12 @@ Bulletproof bulletproof_PROVE(const rct::keyV &sv, const rct::keyV &gamma) // PAPER LINES 32-33 rct::key x_ip = hash_cache_mash(hash_cache, x, taux, mu, t); + if (x_ip == rct::zero()) + { + PERF_TIMER_STOP(PROVE_step3); + MINFO("x_ip is 0, trying again"); + goto try_again; + } // These are used in the inner product rounds size_t nprime = MN; @@ -879,6 +929,12 @@ Bulletproof bulletproof_PROVE(const rct::keyV &sv, const rct::keyV &gamma) // PAPER LINES 21-22 w[round] = hash_cache_mash(hash_cache, L[round], R[round]); + if (w[round] == rct::zero()) + { + PERF_TIMER_STOP(PROVE_step4); + MINFO("w[round] is 0, trying again"); + goto try_again; + } // PAPER LINES 24-25 const rct::key winv = invert(w[round]); @@ -985,9 +1041,13 @@ bool bulletproof_VERIFY(const std::vector &proofs) PERF_TIMER_START_BP(VERIFY_start); rct::key hash_cache = rct::hash_to_scalar(proof.V); rct::key y = hash_cache_mash(hash_cache, proof.A, proof.S); + CHECK_AND_ASSERT_MES(!(y == rct::zero()), false, "y == 0"); rct::key z = hash_cache = rct::hash_to_scalar(y); + CHECK_AND_ASSERT_MES(!(z == rct::zero()), false, "z == 0"); rct::key x = hash_cache_mash(hash_cache, z, proof.T1, proof.T2); + CHECK_AND_ASSERT_MES(!(x == rct::zero()), false, "x == 0"); rct::key x_ip = hash_cache_mash(hash_cache, x, proof.taux, proof.mu, proof.t); + CHECK_AND_ASSERT_MES(!(x_ip == rct::zero()), false, "x_ip == 0"); PERF_TIMER_STOP(VERIFY_start); PERF_TIMER_START_BP(VERIFY_line_61); @@ -1082,6 +1142,7 @@ bool bulletproof_VERIFY(const std::vector &proofs) for (size_t i = 0; i < rounds; ++i) { w[i] = hash_cache_mash(hash_cache, proof.L[i], proof.R[i]); + CHECK_AND_ASSERT_MES(!(w[i] == rct::zero()), false, "w[i] == 0"); } PERF_TIMER_STOP(VERIFY_line_21_22); From a1359ad43c9263c919f8c39b54224ac9fc992d74 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 24 Jul 2018 20:17:32 +0100 Subject: [PATCH 0371/1404] Check inputs to addKeys are in range Reported by QuarksLab. --- src/ringct/rctSigs.cpp | 4 ++++ src/wallet/wallet2.cpp | 2 ++ 2 files changed, 6 insertions(+) diff --git a/src/ringct/rctSigs.cpp b/src/ringct/rctSigs.cpp index dbbf29fd1d5..ae58ad12c9e 100644 --- a/src/ringct/rctSigs.cpp +++ b/src/ringct/rctSigs.cpp @@ -1108,6 +1108,8 @@ namespace rct { DP("C"); DP(C); key Ctmp; + CHECK_AND_ASSERT_THROW_MES(sc_check(mask.bytes) == 0, "warning, bad ECDH mask"); + CHECK_AND_ASSERT_THROW_MES(sc_check(amount.bytes) == 0, "warning, bad ECDH amount"); addKeys2(Ctmp, mask, amount, H); DP("Ctmp"); DP(Ctmp); @@ -1136,6 +1138,8 @@ namespace rct { DP("C"); DP(C); key Ctmp; + CHECK_AND_ASSERT_THROW_MES(sc_check(mask.bytes) == 0, "warning, bad ECDH mask"); + CHECK_AND_ASSERT_THROW_MES(sc_check(amount.bytes) == 0, "warning, bad ECDH amount"); addKeys2(Ctmp, mask, amount, H); DP("Ctmp"); DP(Ctmp); diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 6c0aec4a408..9b977350eb9 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -9422,6 +9422,8 @@ void wallet2::check_tx_key_helper(const crypto::hash &txid, const crypto::key_de hwdev.ecdhDecode(ecdh_info, rct::sk2rct(scalar1)); const rct::key C = tx.rct_signatures.outPk[n].mask; rct::key Ctmp; + THROW_WALLET_EXCEPTION_IF(sc_check(ecdh_info.mask.bytes) != 0, error::wallet_internal_error, "Bad ECDH input mask"); + THROW_WALLET_EXCEPTION_IF(sc_check(ecdh_info.amount.bytes) != 0, error::wallet_internal_error, "Bad ECDH input amount"); rct::addKeys2(Ctmp, ecdh_info.mask, ecdh_info.amount, rct::H); if (rct::equalKeys(C, Ctmp)) amount = rct::h2d(ecdh_info.amount); From 0825e974362ee5e62f44e08fae08dbb10e48383c Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 24 Jul 2018 20:26:43 +0100 Subject: [PATCH 0372/1404] multiexp: fix wrong Bos-Coster result for 1 non trivial input Reported by QuarksLab. --- src/ringct/multiexp.cc | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/ringct/multiexp.cc b/src/ringct/multiexp.cc index f9ef9e42268..b0584032275 100644 --- a/src/ringct/multiexp.cc +++ b/src/ringct/multiexp.cc @@ -222,17 +222,21 @@ rct::key bos_coster_heap_conv_robust(std::vector data) points = heap.size(); if (points == 0) return rct::identity(); + + auto Comp = [&](size_t e0, size_t e1) { return data[e0].scalar < data[e1].scalar; }; + std::make_heap(heap.begin(), heap.end(), Comp); + if (points < 2) { + std::pop_heap(heap.begin(), heap.end(), Comp); + size_t index1 = heap.back(); ge_p2 p2; - ge_scalarmult(&p2, data[0].scalar.bytes, &data[0].point); + ge_scalarmult(&p2, data[index1].scalar.bytes, &data[index1].point); rct::key res; ge_tobytes(res.bytes, &p2); return res; } - auto Comp = [&](size_t e0, size_t e1) { return data[e0].scalar < data[e1].scalar; }; - std::make_heap(heap.begin(), heap.end(), Comp); MULTIEXP_PERF(PERF_TIMER_STOP(setup)); MULTIEXP_PERF(PERF_TIMER_START_UNIT(loop, 1000000)); From 7434df1cc6e96b5d98ea6e3ec095eaae4ac9a95c Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 24 Jul 2018 20:53:09 +0100 Subject: [PATCH 0373/1404] crypto: never return zero in random32_unbiased This avoids problems when the caller can't deal with a zero walue, which happens often enough that it's worth nipping the problem in the bud. --- src/crypto/crypto.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/crypto/crypto.cpp b/src/crypto/crypto.cpp index 4243c71fd52..d6e3b8e0e71 100644 --- a/src/crypto/crypto.cpp +++ b/src/crypto/crypto.cpp @@ -116,7 +116,7 @@ namespace crypto { do { generate_random_bytes_thread_safe(32, bytes); - } while (!less32(bytes, limit)); // should be good about 15/16 of the time + } while (!sc_isnonzero(bytes) && !less32(bytes, limit)); // should be good about 15/16 of the time sc_reduce32(bytes); } /* generate a random 32-byte (256-bit) integer and copy it to res */ From a4317e61b5f48c05bb094f8bfe1a8cc890885a6f Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 24 Jul 2018 21:12:57 +0100 Subject: [PATCH 0374/1404] crypto: some paranoid checks in generate_signature/check_signature Reported by QuarksLab. --- src/crypto/crypto.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/crypto/crypto.cpp b/src/crypto/crypto.cpp index d6e3b8e0e71..ad7721cf052 100644 --- a/src/crypto/crypto.cpp +++ b/src/crypto/crypto.cpp @@ -274,11 +274,18 @@ namespace crypto { #endif buf.h = prefix_hash; buf.key = pub; + try_again: random_scalar(k); + if (((const uint32_t*)(&k))[7] == 0) // we don't want tiny numbers here + goto try_again; ge_scalarmult_base(&tmp3, &k); ge_p3_tobytes(&buf.comm, &tmp3); hash_to_scalar(&buf, sizeof(s_comm), sig.c); + if (!sc_isnonzero((const unsigned char*)sig.c.data)) + goto try_again; sc_mulsub(&sig.r, &sig.c, &unwrap(sec), &k); + if (!sc_isnonzero((const unsigned char*)sig.r.data)) + goto try_again; } bool crypto_ops::check_signature(const hash &prefix_hash, const public_key &pub, const signature &sig) { @@ -292,11 +299,14 @@ namespace crypto { if (ge_frombytes_vartime(&tmp3, &pub) != 0) { return false; } - if (sc_check(&sig.c) != 0 || sc_check(&sig.r) != 0) { + if (sc_check(&sig.c) != 0 || sc_check(&sig.r) != 0 || !sc_isnonzero(&sig.c)) { return false; } ge_double_scalarmult_base_vartime(&tmp2, &sig.c, &tmp3, &sig.r); ge_tobytes(&buf.comm, &tmp2); + static const ec_point infinity = {{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}; + if (memcmp(&buf.comm, &infinity, 32) == 0) + return false; hash_to_scalar(&buf, sizeof(s_comm), c); sc_sub(&c, &c, &sig.c); return sc_isnonzero(&c) == 0; From 61632dc166c25d172681a928583836b6bc2e1562 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 24 Jul 2018 21:23:08 +0100 Subject: [PATCH 0375/1404] ringct: prevent a potential very large allocation Reported by QuarksLab. --- src/ringct/rctTypes.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ringct/rctTypes.h b/src/ringct/rctTypes.h index 5c02f92aa72..ffc4df3ed0b 100644 --- a/src/ringct/rctTypes.h +++ b/src/ringct/rctTypes.h @@ -317,9 +317,9 @@ namespace rct { ar.begin_array(); uint32_t nbp = bulletproofs.size(); FIELD(nbp) - PREPARE_CUSTOM_VECTOR_SERIALIZATION(nbp, bulletproofs); - if (bulletproofs.size() > outputs) + if (nbp > outputs) return false; + PREPARE_CUSTOM_VECTOR_SERIALIZATION(nbp, bulletproofs); for (size_t i = 0; i < nbp; ++i) { FIELDS(bulletproofs[i]) From d1591853bf0df9a278420e874b7d8dd03966b66d Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 24 Jul 2018 21:32:49 +0100 Subject: [PATCH 0376/1404] cryptonote_basic: check output type before using it Reported by QuarksLab. --- src/cryptonote_basic/cryptonote_format_utils.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/cryptonote_basic/cryptonote_format_utils.cpp b/src/cryptonote_basic/cryptonote_format_utils.cpp index f1a5c4d942f..9061bf1059a 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.cpp +++ b/src/cryptonote_basic/cryptonote_format_utils.cpp @@ -135,7 +135,14 @@ namespace cryptonote return false; } for (size_t n = 0; n < tx.rct_signatures.outPk.size(); ++n) + { + if (tx.vout[n].target.type() != typeid(txout_to_key)) + { + LOG_PRINT_L1("Unsupported output type in tx " << get_transaction_hash(tx)); + return false; + } rv.outPk[n].dest = rct::pk2rct(boost::get(tx.vout[n].target).key); + } if (!base_only) { From 7ed496cc780489f9bb8fe13c4d97885666e0dfaf Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 25 Jul 2018 10:10:46 +0100 Subject: [PATCH 0377/1404] ringct: error out when hashToPoint* returns the point at infinity Reported by QuarksLab. --- src/ringct/bulletproofs.cc | 4 +++- src/ringct/rctSigs.cpp | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/ringct/bulletproofs.cc b/src/ringct/bulletproofs.cc index 3f4a6fd10b0..2964fc4693c 100644 --- a/src/ringct/bulletproofs.cc +++ b/src/ringct/bulletproofs.cc @@ -130,7 +130,9 @@ static rct::key get_exponent(const rct::key &base, size_t idx) { static const std::string salt("bulletproof"); std::string hashed = std::string((const char*)base.bytes, sizeof(base)) + salt + tools::get_varint_data(idx); - return rct::hashToPoint(rct::hash2rct(crypto::cn_fast_hash(hashed.data(), hashed.size()))); + const rct::key e = rct::hashToPoint(rct::hash2rct(crypto::cn_fast_hash(hashed.data(), hashed.size()))); + CHECK_AND_ASSERT_THROW_MES(!(e == rct::identity()), "Exponent is point at infinity"); + return e; } static void init_exponents() diff --git a/src/ringct/rctSigs.cpp b/src/ringct/rctSigs.cpp index ae58ad12c9e..e98e6290343 100644 --- a/src/ringct/rctSigs.cpp +++ b/src/ringct/rctSigs.cpp @@ -277,6 +277,7 @@ namespace rct { for (j = 0; j < dsRows; j++) { addKeys2(L, rv.ss[i][j], c_old, pk[i][j]); hashToPoint(Hi, pk[i][j]); + CHECK_AND_ASSERT_MES(!(Hi == rct::identity()), false, "Data hashed to point at infinity"); addKeys3(R, rv.ss[i][j], Hi, c_old, Ip[j].k); toHash[3 * j + 1] = pk[i][j]; toHash[3 * j + 2] = L; From a129bbd944f717d00cff6ee099c47ecede4dbc3c Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 25 Jul 2018 21:56:39 +0100 Subject: [PATCH 0378/1404] multiexp: fix maxscalar off by one Reported by QuarksLab. --- src/ringct/multiexp.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ringct/multiexp.cc b/src/ringct/multiexp.cc index b0584032275..901b00eddae 100644 --- a/src/ringct/multiexp.cc +++ b/src/ringct/multiexp.cc @@ -605,7 +605,7 @@ rct::key pippenger(const std::vector &data, const std::shared_ptr< maxscalar = data[i].scalar; } size_t groups = 0; - while (groups < 256 && pow2(groups) < maxscalar) + while (groups < 256 && !(maxscalar < pow2(groups))) ++groups; groups = (groups + c - 1) / c; From 6526d87f17a153a6a7f9f1e81a53b113c20e5f3b Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 26 Jul 2018 10:21:28 +0100 Subject: [PATCH 0379/1404] core_tests: add a test for a tx with empty bulletproof --- tests/core_tests/bulletproofs.cpp | 13 +++++++++++++ tests/core_tests/bulletproofs.h | 6 ++++++ tests/core_tests/chaingen_main.cpp | 1 + 3 files changed, 20 insertions(+) diff --git a/tests/core_tests/bulletproofs.cpp b/tests/core_tests/bulletproofs.cpp index 3b3039a18db..aea502faa1c 100644 --- a/tests/core_tests/bulletproofs.cpp +++ b/tests/core_tests/bulletproofs.cpp @@ -297,6 +297,19 @@ bool gen_bp_tx_invalid_not_enough_proofs::generate(std::vector }); } +bool gen_bp_tx_invalid_empty_proofs::generate(std::vector& events) const +{ + DEFINE_TESTS_ERROR_CONTEXT("gen_bp_tx_invalid_empty_proofs"); + const size_t mixin = 10; + const uint64_t amounts_paid[] = {50000, 50000, (uint64_t)-1}; + const rct::RangeProofType range_proof_type[] = { rct::RangeProofBulletproof }; + return generate_with(events, mixin, 1, amounts_paid, false, range_proof_type, NULL, [&](cryptonote::transaction &tx, size_t idx){ + CHECK_TEST_CONDITION(tx.rct_signatures.type == rct::RCTTypeBulletproof); + tx.rct_signatures.p.bulletproofs.clear(); + return true; + }); +} + bool gen_bp_tx_invalid_too_many_proofs::generate(std::vector& events) const { DEFINE_TESTS_ERROR_CONTEXT("gen_bp_tx_invalid_too_many_proofs"); diff --git a/tests/core_tests/bulletproofs.h b/tests/core_tests/bulletproofs.h index 13faa3493e1..c3a7db2c70a 100644 --- a/tests/core_tests/bulletproofs.h +++ b/tests/core_tests/bulletproofs.h @@ -162,6 +162,12 @@ struct gen_bp_tx_invalid_not_enough_proofs : public gen_bp_tx_validation_base }; template<> struct get_test_options: public get_test_options {}; +struct gen_bp_tx_invalid_empty_proofs : public gen_bp_tx_validation_base +{ + bool generate(std::vector& events) const; +}; +template<> struct get_test_options: public get_test_options {}; + struct gen_bp_tx_invalid_too_many_proofs : public gen_bp_tx_validation_base { bool generate(std::vector& events) const; diff --git a/tests/core_tests/chaingen_main.cpp b/tests/core_tests/chaingen_main.cpp index 278ef81a62a..6f26e65320c 100644 --- a/tests/core_tests/chaingen_main.cpp +++ b/tests/core_tests/chaingen_main.cpp @@ -234,6 +234,7 @@ int main(int argc, char* argv[]) GENERATE_AND_PLAY(gen_bp_txs_valid_2_and_2); GENERATE_AND_PLAY(gen_bp_txs_invalid_2_and_8_2_and_16_16_1); GENERATE_AND_PLAY(gen_bp_tx_invalid_not_enough_proofs); + GENERATE_AND_PLAY(gen_bp_tx_invalid_empty_proofs); GENERATE_AND_PLAY(gen_bp_tx_invalid_too_many_proofs); GENERATE_AND_PLAY(gen_bp_tx_invalid_wrong_amount); GENERATE_AND_PLAY(gen_bp_tx_invalid_borromean_type); From d0a0565f7d2aa658b0632acd11b61ac705bd2294 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 26 Jul 2018 11:09:48 +0100 Subject: [PATCH 0380/1404] unit_tests: add a few more multiexp unit tests --- tests/unit_tests/multiexp.cpp | 54 +++++++++++++++++++++++++++++++++-- 1 file changed, 52 insertions(+), 2 deletions(-) diff --git a/tests/unit_tests/multiexp.cpp b/tests/unit_tests/multiexp.cpp index 10303c392aa..d8d79a7a207 100644 --- a/tests/unit_tests/multiexp.cpp +++ b/tests/unit_tests/multiexp.cpp @@ -32,8 +32,10 @@ #include "ringct/rctOps.h" #include "ringct/multiexp.h" -static const rct::key TESTSCALAR = rct::H; -static const rct::key TESTPOINT = rct::scalarmultBase(rct::H); +static const rct::key TESTSCALAR = rct::skGen(); +static const rct::key TESTPOW2SCALAR = {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}; +static const rct::key TESTSMALLSCALAR = {{5, 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}}; +static const rct::key TESTPOINT = rct::scalarmultBase(rct::skGen()); static rct::key basic(const std::vector &data) { @@ -81,6 +83,54 @@ TEST(multiexp, pippenger_empty) ASSERT_TRUE(basic(data) == pippenger(data)); } +TEST(multiexp, bos_coster_zero_and_non_zero) +{ + std::vector data; + data.push_back({rct::zero(), get_p3(TESTPOINT)}); + data.push_back({TESTSCALAR, get_p3(TESTPOINT)}); + ASSERT_TRUE(basic(data) == bos_coster_heap_conv_robust(data)); +} + +TEST(multiexp, straus_zero_and_non_zero) +{ + std::vector data; + data.push_back({rct::zero(), get_p3(TESTPOINT)}); + data.push_back({TESTSCALAR, get_p3(TESTPOINT)}); + ASSERT_TRUE(basic(data) == straus(data)); +} + +TEST(multiexp, pippenger_zero_and_non_zero) +{ + std::vector data; + data.push_back({rct::zero(), get_p3(TESTPOINT)}); + data.push_back({TESTSCALAR, get_p3(TESTPOINT)}); + ASSERT_TRUE(basic(data) == pippenger(data)); +} + +TEST(multiexp, bos_coster_pow2_scalar) +{ + std::vector data; + data.push_back({TESTPOW2SCALAR, get_p3(TESTPOINT)}); + data.push_back({TESTSMALLSCALAR, get_p3(TESTPOINT)}); + ASSERT_TRUE(basic(data) == bos_coster_heap_conv_robust(data)); +} + +TEST(multiexp, straus_pow2_scalar) +{ + std::vector data; + data.push_back({TESTPOW2SCALAR, get_p3(TESTPOINT)}); + data.push_back({TESTSMALLSCALAR, get_p3(TESTPOINT)}); + ASSERT_TRUE(basic(data) == straus(data)); +} + +TEST(multiexp, pippenger_pow2_scalar) +{ + std::vector data; + data.push_back({TESTPOW2SCALAR, get_p3(TESTPOINT)}); + data.push_back({TESTSMALLSCALAR, get_p3(TESTPOINT)}); + ASSERT_TRUE(basic(data) == pippenger(data)); +} + TEST(multiexp, bos_coster_only_zeroes) { std::vector data; From 463434d1f7159780f64b0fe2ca4d042cb49f1b2a Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 26 Jul 2018 18:03:10 +0100 Subject: [PATCH 0381/1404] more comprehensive test for ge_p3 comparison to identity/point at infinity Reported by QuarksLab. --- src/crypto/crypto-ops.c | 13 +++++++++++++ src/crypto/crypto-ops.h | 2 ++ src/ringct/multiexp.cc | 12 ++++++------ 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/src/crypto/crypto-ops.c b/src/crypto/crypto-ops.c index c1fff1d442f..09296d6f936 100644 --- a/src/crypto/crypto-ops.c +++ b/src/crypto/crypto-ops.c @@ -3729,3 +3729,16 @@ int sc_isnonzero(const unsigned char *s) { s[18] | s[19] | s[20] | s[21] | s[22] | s[23] | s[24] | s[25] | s[26] | s[27] | s[28] | s[29] | s[30] | s[31]) - 1) >> 8) + 1; } + +int ge_p3_is_point_at_infinity(const ge_p3 *p) { + // X = 0 and Y == Z + int n; + for (n = 0; n < 10; ++n) + { + if (p->X[n] | p->T[n]) + return 0; + if (p->Y[n] != p->Z[n]) + return 0; + } + return 1; +} diff --git a/src/crypto/crypto-ops.h b/src/crypto/crypto-ops.h index 52854889f8e..2910dafd46f 100644 --- a/src/crypto/crypto-ops.h +++ b/src/crypto/crypto-ops.h @@ -159,3 +159,5 @@ void ge_sub(ge_p1p1 *r, const ge_p3 *p, const ge_cached *q); void fe_add(fe h, const fe f, const fe g); void fe_tobytes(unsigned char *, const fe); void fe_invert(fe out, const fe z); + +int ge_p3_is_point_at_infinity(const ge_p3 *p); diff --git a/src/ringct/multiexp.cc b/src/ringct/multiexp.cc index 901b00eddae..21957b94ce5 100644 --- a/src/ringct/multiexp.cc +++ b/src/ringct/multiexp.cc @@ -216,7 +216,7 @@ rct::key bos_coster_heap_conv_robust(std::vector data) heap.reserve(points); for (size_t n = 0; n < points; ++n) { - if (!(data[n].scalar == rct::zero()) && memcmp(&data[n].point, &ge_p3_identity, sizeof(ge_p3))) + if (!(data[n].scalar == rct::zero()) && !ge_p3_is_point_at_infinity(&data[n].point)) heap.push_back(n); } points = heap.size(); @@ -442,7 +442,7 @@ rct::key straus(const std::vector &data, const std::shared_ptr skip(data.size()); for (size_t i = 0; i < data.size(); ++i) - skip[i] = data[i].scalar == rct::zero() || !memcmp(&data[i].point, &ge_p3_identity, sizeof(ge_p3)); + skip[i] = data[i].scalar == rct::zero() || ge_p3_is_point_at_infinity(&data[i].point); MULTIEXP_PERF(PERF_TIMER_STOP(skip)); #endif @@ -611,7 +611,7 @@ rct::key pippenger(const std::vector &data, const std::shared_ptr< for (size_t k = groups; k-- > 0; ) { - if (memcmp(&result, &ge_p3_identity, sizeof(ge_p3))) + if (!ge_p3_is_point_at_infinity(&result)) { ge_p2 p2; ge_p3_to_p2(&p2, &result); @@ -638,7 +638,7 @@ rct::key pippenger(const std::vector &data, const std::shared_ptr< if (bucket == 0) continue; CHECK_AND_ASSERT_THROW_MES(bucket < (1u<cached[i]); } @@ -650,9 +650,9 @@ rct::key pippenger(const std::vector &data, const std::shared_ptr< ge_p3 pail = ge_p3_identity; for (size_t i = (1< 0; --i) { - if (memcmp(&buckets[i], &ge_p3_identity, sizeof(ge_p3))) + if (!ge_p3_is_point_at_infinity(&buckets[i])) add(pail, buckets[i]); - if (memcmp(&pail, &ge_p3_identity, sizeof(ge_p3))) + if (!ge_p3_is_point_at_infinity(&pail)) add(result, pail); } } From 0e6ed559c674a6d945201c70372dde6e0d65d107 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 27 Jul 2018 17:00:48 +0100 Subject: [PATCH 0382/1404] fuzz_tests: add a bulletproof fuzz test --- contrib/fuzz_testing/fuzz.sh | 6 +-- tests/data/fuzz/bulletproof/BP0 | Bin 0 -> 674 bytes tests/fuzz/CMakeLists.txt | 15 +++++++ tests/fuzz/bulletproof.cpp | 70 ++++++++++++++++++++++++++++++++ 4 files changed, 88 insertions(+), 3 deletions(-) create mode 100644 tests/data/fuzz/bulletproof/BP0 create mode 100644 tests/fuzz/bulletproof.cpp diff --git a/contrib/fuzz_testing/fuzz.sh b/contrib/fuzz_testing/fuzz.sh index f1c4ff2025b..efd43c23146 100755 --- a/contrib/fuzz_testing/fuzz.sh +++ b/contrib/fuzz_testing/fuzz.sh @@ -10,12 +10,12 @@ fi type="$1" if test -z "$type" then - echo "usage: $0 block|transaction|signature|cold-outputs|cold-transaction|load-from-binary|load-from-json|base58|parse-url|http-client|levin" + echo "usage: $0 block|transaction|signature|cold-outputs|cold-transaction|load-from-binary|load-from-json|base58|parse-url|http-client|levin|bulletproof" exit 1 fi case "$type" in - block|transaction|signature|cold-outputs|cold-transaction|load-from-binary|load-from-json|base58|parse-url|http-client|levin) ;; - *) echo "usage: $0 block|transaction|signature|cold-outputs|cold-transaction|load-from-binary|load-from-json|base58|parse-url|http-client|levin"; exit 1 ;; + block|transaction|signature|cold-outputs|cold-transaction|load-from-binary|load-from-json|base58|parse-url|http-client|levin|bulletproof) ;; + *) echo "usage: $0 block|transaction|signature|cold-outputs|cold-transaction|load-from-binary|load-from-json|base58|parse-url|http-client|levin|bulletproof"; exit 1 ;; esac if test -d "fuzz-out/$type" diff --git a/tests/data/fuzz/bulletproof/BP0 b/tests/data/fuzz/bulletproof/BP0 new file mode 100644 index 0000000000000000000000000000000000000000..17590b770f6a74ccb1e5065fcbfc4b979033cfff GIT binary patch literal 674 zcmV;T0$u%NxPE*JzbSo!yl8;_<%@E7;r2giX!3#@nHF}2-yz+wfqp|7UR-Qdwj9+b zzCA1#U07b7lAA#mNq&?bjnQ%5NX~;-+@vW>_jHL#6B`fLDMRr~G4Z7+HOcdq{^g>cW0<5H-Txs zK4^_g#!JWQSe6SEjg1uh@>$ribRSaIIv4B`#$HHNU(+&)9-{sr$Z-P(gY>f%HczN- za1u>r^P-gfu0d}oXj61%803yc{grnI+ce515z8NmJGsZgqc*F{cSD9a4_JXrYCEgK z%fU~J??G4(LyRLT^RI)RLH>-2{RZt_&3b>tvmin9NbYfKg>2LXjkZ0h6G>=P`ET*& zIaow&d`@bEw*Y88(e}D&0nva?>R-g?jlXw>y_Coit+~vfKkQ}2lQ*1LLgnUTZlR%L zz&TUwpTi`WrrpG?6{ZANr-8Qo8*}(rEWbzx2GslX-cyLfzVka(l*7S~7FDVQhwsS9 z-tJ3}RYQVg(7>|!YlEm_LUn=EtnEqaT?pVzdl}L*H?^X9{p;_{h12KI`A%;1N_xfW zwtheafYmjZkv7ZvWIiU}7i9>2E_`jlZqsGpb`20yw_I(6-#3eqm-x7dHajrQ3eXdfwg5aH4heiKcz@yZ#LI_3x$jRo zP|8a34cKy3t|62EU5ceKO0?rk{rb{XE1$H1^ZDF0#KpzSwL9&|*G-GN4EBiR+{Y6= zxkyX^?uN1&+RM&m3p*0{1G$wM6rgk14vz;EWFS)LR+Nl#vm1~CqY1M!00p8zEI>r} II8_ht0=-5~!~g&Q literal 0 HcmV?d00001 diff --git a/tests/fuzz/CMakeLists.txt b/tests/fuzz/CMakeLists.txt index dfbbaeca63a..fdb74569997 100644 --- a/tests/fuzz/CMakeLists.txt +++ b/tests/fuzz/CMakeLists.txt @@ -173,3 +173,18 @@ set_property(TARGET levin_fuzz_tests PROPERTY FOLDER "tests") +add_executable(bulletproof_fuzz_tests bulletproof.cpp fuzzer.cpp) +target_link_libraries(bulletproof_fuzz_tests + PRIVATE + common + epee + ${Boost_THREAD_LIBRARY} + ${Boost_CHRONO_LIBRARY} + ${Boost_REGEX_LIBRARY} + ${Boost_PROGRAM_OPTIONS_LIBRARY} + ${CMAKE_THREAD_LIBS_INIT} + ${EXTRA_LIBRARIES}) +set_property(TARGET bulletproof_fuzz_tests + PROPERTY + FOLDER "tests") + diff --git a/tests/fuzz/bulletproof.cpp b/tests/fuzz/bulletproof.cpp new file mode 100644 index 00000000000..2f4dfd0eab8 --- /dev/null +++ b/tests/fuzz/bulletproof.cpp @@ -0,0 +1,70 @@ +// Copyright (c) 2017-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "include_base_utils.h" +#include "file_io_utils.h" +#include "cryptonote_basic/blobdatatype.h" +#include "cryptonote_basic/cryptonote_basic.h" +#include "cryptonote_basic/cryptonote_format_utils.h" +#include "fuzzer.h" + +class BulletproofFuzzer: public Fuzzer +{ +public: + virtual int run(const std::string &filename); + +private: +}; + +int BulletproofFuzzer::run(const std::string &filename) +{ + std::string s; + + if (!epee::file_io_utils::load_file_to_string(filename, s)) + { + std::cout << "Error: failed to load file " << filename << std::endl; + return 1; + } + std::stringstream ss; + ss << s; + binary_archive ba(ss); + rct::Bulletproof proof = AUTO_VAL_INIT(proof); + bool r = ::serialization::serialize(ba, proof); + if(!r) + { + std::cout << "Error: failed to parse bulletproof from file " << filename << std::endl; + return 1; + } + return 0; +} + +int main(int argc, const char **argv) +{ + BulletproofFuzzer fuzzer; + return run_fuzzer(argc, argv, fuzzer); +} From 1224e53b744f8338163998d06b139a3f7a006b08 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 29 Jul 2018 14:04:42 +0100 Subject: [PATCH 0383/1404] core_tests: add a test for 4-aggregated BP verification --- tests/core_tests/bulletproofs.cpp | 9 +++++++++ tests/core_tests/bulletproofs.h | 6 ++++++ tests/core_tests/chaingen_main.cpp | 1 + 3 files changed, 16 insertions(+) diff --git a/tests/core_tests/bulletproofs.cpp b/tests/core_tests/bulletproofs.cpp index aea502faa1c..db875d2a2e9 100644 --- a/tests/core_tests/bulletproofs.cpp +++ b/tests/core_tests/bulletproofs.cpp @@ -282,6 +282,15 @@ bool gen_bp_txs_invalid_2_and_8_2_and_16_16_1::generate(std::vector& events) const +{ + const size_t mixin = 10; + const uint64_t amounts_paid[] = {11111115000, 11111115000, (uint64_t)-1, 11111115000, 11111115000, 11111115001, (uint64_t)-1, 11111115000, 11111115002, (uint64_t)-1, 11111115000, 11111115000, 11111115000, 11111115003, (uint64_t)-1}; + const rct::RangeProofType range_proof_type[] = {rct::RangeProofPaddedBulletproof, rct::RangeProofPaddedBulletproof, rct::RangeProofPaddedBulletproof, rct::RangeProofPaddedBulletproof}; + const size_t bp_sizes[] = {2, (size_t)-1, 4, (size_t)-1, 2, (size_t)-1, 4, (size_t)-1}; + return generate_with(events, mixin, 4, amounts_paid, true, range_proof_type, NULL, [&](const cryptonote::transaction &tx, size_t tx_idx) { return check_bp(tx, tx_idx, bp_sizes, "gen_bp_txs_valid_2_and_3_and_2_and_4"); }); +} + bool gen_bp_tx_invalid_not_enough_proofs::generate(std::vector& events) const { DEFINE_TESTS_ERROR_CONTEXT("gen_bp_tx_invalid_not_enough_proofs"); diff --git a/tests/core_tests/bulletproofs.h b/tests/core_tests/bulletproofs.h index c3a7db2c70a..e29b3469014 100644 --- a/tests/core_tests/bulletproofs.h +++ b/tests/core_tests/bulletproofs.h @@ -156,6 +156,12 @@ struct gen_bp_txs_invalid_2_and_8_2_and_16_16_1 : public gen_bp_tx_validation_ba }; template<> struct get_test_options: public get_test_options {}; +struct gen_bp_txs_valid_2_and_3_and_2_and_4 : public gen_bp_tx_validation_base +{ + bool generate(std::vector& events) const; +}; +template<> struct get_test_options: public get_test_options {}; + struct gen_bp_tx_invalid_not_enough_proofs : public gen_bp_tx_validation_base { bool generate(std::vector& events) const; diff --git a/tests/core_tests/chaingen_main.cpp b/tests/core_tests/chaingen_main.cpp index 6f26e65320c..abc4123182a 100644 --- a/tests/core_tests/chaingen_main.cpp +++ b/tests/core_tests/chaingen_main.cpp @@ -233,6 +233,7 @@ int main(int argc, char* argv[]) GENERATE_AND_PLAY(gen_bp_tx_invalid_16_16); GENERATE_AND_PLAY(gen_bp_txs_valid_2_and_2); GENERATE_AND_PLAY(gen_bp_txs_invalid_2_and_8_2_and_16_16_1); + GENERATE_AND_PLAY(gen_bp_txs_valid_2_and_3_and_2_and_4); GENERATE_AND_PLAY(gen_bp_tx_invalid_not_enough_proofs); GENERATE_AND_PLAY(gen_bp_tx_invalid_empty_proofs); GENERATE_AND_PLAY(gen_bp_tx_invalid_too_many_proofs); From ce0c7432050fff7e6abcfa7a0bca294208d9b13f Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 3 Aug 2018 21:53:20 +0000 Subject: [PATCH 0384/1404] performance_tests: add padded bulletproof construction --- tests/performance_tests/construct_tx.h | 4 ++-- tests/performance_tests/main.cpp | 12 ++++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/tests/performance_tests/construct_tx.h b/tests/performance_tests/construct_tx.h index 99c7f1b1890..378065ddd0c 100644 --- a/tests/performance_tests/construct_tx.h +++ b/tests/performance_tests/construct_tx.h @@ -36,7 +36,7 @@ #include "multi_tx_test_base.h" -template +template class test_construct_tx : private multi_tx_test_base { static_assert(0 < a_in_count, "in_count must be greater than 0"); @@ -73,7 +73,7 @@ class test_construct_tx : private multi_tx_test_base std::vector additional_tx_keys; std::unordered_map subaddresses; subaddresses[this->m_miners[this->real_source_idx].get_keys().m_account_address.m_spend_public_key] = {0,0}; - return cryptonote::construct_tx_and_get_tx_key(this->m_miners[this->real_source_idx].get_keys(), subaddresses, this->m_sources, m_destinations, cryptonote::account_public_address{}, std::vector(), m_tx, 0, tx_key, additional_tx_keys, rct); + return cryptonote::construct_tx_and_get_tx_key(this->m_miners[this->real_source_idx].get_keys(), subaddresses, this->m_sources, m_destinations, cryptonote::account_public_address{}, std::vector(), m_tx, 0, tx_key, additional_tx_keys, rct, range_proof_type); } private: diff --git a/tests/performance_tests/main.cpp b/tests/performance_tests/main.cpp index 6007242bfb1..d8348629632 100644 --- a/tests/performance_tests/main.cpp +++ b/tests/performance_tests/main.cpp @@ -132,6 +132,18 @@ int main(int argc, char** argv) TEST_PERFORMANCE3(filter, p, test_construct_tx, 100, 2, true); TEST_PERFORMANCE3(filter, p, test_construct_tx, 100, 10, true); + TEST_PERFORMANCE4(filter, p, test_construct_tx, 2, 1, true, rct::RangeProofPaddedBulletproof); + TEST_PERFORMANCE4(filter, p, test_construct_tx, 2, 2, true, rct::RangeProofPaddedBulletproof); + TEST_PERFORMANCE4(filter, p, test_construct_tx, 2, 10, true, rct::RangeProofPaddedBulletproof); + + TEST_PERFORMANCE4(filter, p, test_construct_tx, 10, 1, true, rct::RangeProofPaddedBulletproof); + TEST_PERFORMANCE4(filter, p, test_construct_tx, 10, 2, true, rct::RangeProofPaddedBulletproof); + TEST_PERFORMANCE4(filter, p, test_construct_tx, 10, 10, true, rct::RangeProofPaddedBulletproof); + + TEST_PERFORMANCE4(filter, p, test_construct_tx, 100, 1, true, rct::RangeProofPaddedBulletproof); + TEST_PERFORMANCE4(filter, p, test_construct_tx, 100, 2, true, rct::RangeProofPaddedBulletproof); + TEST_PERFORMANCE4(filter, p, test_construct_tx, 100, 10, true, rct::RangeProofPaddedBulletproof); + TEST_PERFORMANCE3(filter, p, test_check_tx_signature, 1, 2, false); TEST_PERFORMANCE3(filter, p, test_check_tx_signature, 2, 2, false); TEST_PERFORMANCE3(filter, p, test_check_tx_signature, 10, 2, false); From c83012c47654d05ca6b045c3942d3e57da6ad199 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 6 Aug 2018 10:09:46 +0000 Subject: [PATCH 0385/1404] bulletproofs: match aggregated verification to sarang's latest prototype --- src/ringct/bulletproofs.cc | 103 ++++++++++++++----------------------- 1 file changed, 39 insertions(+), 64 deletions(-) diff --git a/src/ringct/bulletproofs.cc b/src/ringct/bulletproofs.cc index 2964fc4693c..acf0dc36e28 100644 --- a/src/ringct/bulletproofs.cc +++ b/src/ringct/bulletproofs.cc @@ -1029,6 +1029,8 @@ bool bulletproof_VERIFY(const std::vector &proofs) rct::key Z2 = rct::identity(); rct::key z3 = rct::zero(); rct::keyV z4(maxMN, rct::zero()), z5(maxMN, rct::zero()); + rct::key Y2 = rct::identity(), Y3 = rct::identity(), Y4 = rct::identity(); + rct::key y0 = rct::zero(), y1 = rct::zero(); for (const Bulletproof *p: proofs) { const Bulletproof &proof = *p; @@ -1054,8 +1056,7 @@ bool bulletproof_VERIFY(const std::vector &proofs) PERF_TIMER_START_BP(VERIFY_line_61); // PAPER LINE 61 - rct::key L61Left, L61Right; - rct::addKeys2(L61Left, proof.taux, proof.t, rct::H); + sc_muladd(y0.bytes, proof.taux.bytes, weight.bytes, y0.bytes); const rct::keyV zpow = vector_powers(z, M+3); @@ -1069,64 +1070,26 @@ bool bulletproof_VERIFY(const std::vector &proofs) } PERF_TIMER_STOP(VERIFY_line_61); - // bos coster is slower for small numbers of calcs, straus seems not - if (1) - { - PERF_TIMER_START_BP(VERIFY_line_61rl_new); - sc_muladd(tmp.bytes, z.bytes, ip1y.bytes, k.bytes); - std::vector multiexp_data; - multiexp_data.reserve(3+proof.V.size()); - multiexp_data.emplace_back(tmp, ge_p3_H); - for (size_t j = 0; j < proof.V.size(); j++) - { - multiexp_data.emplace_back(zpow[j+2], proof.V[j]); - } - multiexp_data.emplace_back(x, proof.T1); - rct::key xsq; - sc_mul(xsq.bytes, x.bytes, x.bytes); - multiexp_data.emplace_back(xsq, proof.T2); - L61Right = multiexp(multiexp_data, false); - PERF_TIMER_STOP(VERIFY_line_61rl_new); - } - else + PERF_TIMER_START_BP(VERIFY_line_61rl_new); + sc_muladd(tmp.bytes, z.bytes, ip1y.bytes, k.bytes); + std::vector multiexp_data; + multiexp_data.reserve(proof.V.size()); + sc_sub(tmp.bytes, proof.t.bytes, tmp.bytes); + sc_muladd(y1.bytes, tmp.bytes, weight.bytes, y1.bytes); + for (size_t j = 0; j < proof.V.size(); j++) { - PERF_TIMER_START_BP(VERIFY_line_61rl_old); - sc_muladd(tmp.bytes, z.bytes, ip1y.bytes, k.bytes); - L61Right = rct::scalarmultH(tmp); - ge_p3 L61Right_p3; - CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&L61Right_p3, L61Right.bytes) == 0, "ge_frombytes_vartime failed"); - for (size_t j = 0; j+1 < proof.V.size(); j += 2) - { - CHECK_AND_ASSERT_MES(j+2+1 < zpow.size(), false, "invalid zpow index"); - ge_dsmp precomp0, precomp1; - rct::precomp(precomp0, j < proof.V.size() ? proof.V[j] : rct::identity()); - rct::precomp(precomp1, j+1 < proof.V.size() ? proof.V[j+1] : rct::identity()); - rct::addKeys3acc_p3(&L61Right_p3, zpow[j+2], precomp0, zpow[j+2+1], precomp1); - } - for (size_t j = proof.V.size() & 0xfffffffe; j < M; j++) - { - CHECK_AND_ASSERT_MES(j+2 < zpow.size(), false, "invalid zpow index"); - // faster equivalent to: - // tmp = rct::scalarmultKey(j < proof.V.size() ? proof.V[j] : rct::identity(), zpow[j+2]); - // rct::addKeys(L61Right, L61Right, tmp); - if (j < proof.V.size()) - addKeys_acc_p3(&L61Right_p3, zpow[j+2], proof.V[j]); - } - - addKeys_acc_p3(&L61Right_p3, x, proof.T1); - - rct::key xsq; - sc_mul(xsq.bytes, x.bytes, x.bytes); - addKeys_acc_p3(&L61Right_p3, xsq, proof.T2); - ge_p3_tobytes(L61Right.bytes, &L61Right_p3); - PERF_TIMER_STOP(VERIFY_line_61rl_old); + multiexp_data.emplace_back(zpow[j+2], proof.V[j]); } + rct::key temp = multiexp(multiexp_data, false); - if (!(L61Right == L61Left)) - { - MERROR("Verification failure at step 1"); - return false; - } + rct::addKeys(Y2, Y2, rct::scalarmultKey(temp, weight)); + sc_mul(tmp.bytes, x.bytes, weight.bytes); + rct::addKeys(Y3, Y3, rct::scalarmultKey(proof.T1, tmp)); + rct::key xsq; + sc_mul(xsq.bytes, x.bytes, x.bytes); + sc_mul(tmp.bytes, xsq.bytes, weight.bytes); + rct::addKeys(Y4, Y4, rct::scalarmultKey(proof.T2, tmp)); + PERF_TIMER_STOP(VERIFY_line_61rl_new); PERF_TIMER_START_BP(VERIFY_line_62); // PAPER LINE 62 @@ -1206,7 +1169,7 @@ bool bulletproof_VERIFY(const std::vector &proofs) // PAPER LINE 26 PERF_TIMER_START_BP(VERIFY_line_26_new); - std::vector multiexp_data; + multiexp_data.clear(); multiexp_data.reserve(2*rounds); sc_muladd(z1.bytes, proof.mu.bytes, weight.bytes, z1.bytes); @@ -1227,11 +1190,23 @@ bool bulletproof_VERIFY(const std::vector &proofs) // now check all proofs at once PERF_TIMER_START_BP(VERIFY_step2_check); - rct::key Y = Z0; + rct::key check1 = rct::identity(); + rct::addKeys(check1, check1, rct::scalarmultBase(y0)); + rct::addKeys(check1, check1, rct::scalarmultH(y1)); + rct::subKeys(check1, check1, Y2); + rct::subKeys(check1, check1, Y3); + rct::subKeys(check1, check1, Y4); + if (!(check1 == rct::identity())) + { + MERROR("Verification failure at step 1"); + return false; + } + rct::key check2 = rct::identity(); + rct::addKeys(check2, check2, Z0); sc_sub(tmp.bytes, rct::zero().bytes, z1.bytes); - rct::addKeys(Y, Y, rct::scalarmultBase(tmp)); - rct::addKeys(Y, Y, Z2); - rct::addKeys(Y, Y, rct::scalarmultH(z3)); + rct::addKeys(check2, check2, rct::scalarmultBase(tmp)); + rct::addKeys(check2, check2, Z2); + rct::addKeys(check2, check2, rct::scalarmultH(z3)); std::vector multiexp_data; multiexp_data.reserve(2 * maxMN); @@ -1242,10 +1217,10 @@ bool bulletproof_VERIFY(const std::vector &proofs) sc_sub(tmp.bytes, rct::zero().bytes, z5[i].bytes); multiexp_data.emplace_back(tmp, Hi_p3[i]); } - rct::addKeys(Y, Y, multiexp(multiexp_data, true)); + rct::addKeys(check2, check2, multiexp(multiexp_data, true)); PERF_TIMER_STOP(VERIFY_step2_check); - if (!(Y == rct::identity())) + if (!(check2 == rct::identity())) { MERROR("Verification failure at step 2"); return false; From 044dff5a30deb05f5236f36ebae5e671d15ddb4e Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 6 Aug 2018 11:05:20 +0000 Subject: [PATCH 0386/1404] bulletproofs: scale points by 8 to ensure subgroup validity --- .../cryptonote_format_utils.cpp | 2 +- src/ringct/bulletproofs.cc | 44 ++++++++++++------- src/ringct/rctOps.h | 2 + src/ringct/rctSigs.cpp | 4 +- 4 files changed, 33 insertions(+), 19 deletions(-) diff --git a/src/cryptonote_basic/cryptonote_format_utils.cpp b/src/cryptonote_basic/cryptonote_format_utils.cpp index 9061bf1059a..7ea4718d2a3 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.cpp +++ b/src/cryptonote_basic/cryptonote_format_utils.cpp @@ -169,7 +169,7 @@ namespace cryptonote CHECK_AND_ASSERT_MES(n_amounts == rv.outPk.size(), false, "Internal error filling out V"); rv.p.bulletproofs[0].V.resize(n_amounts); for (size_t i = 0; i < n_amounts; ++i) - rv.p.bulletproofs[0].V[i] = rv.outPk[i].mask; + rv.p.bulletproofs[0].V[i] = rct::scalarmultKey(rv.outPk[i].mask, rct::INV_EIGHT); } } } diff --git a/src/ringct/bulletproofs.cc b/src/ringct/bulletproofs.cc index acf0dc36e28..5bcdd968dbe 100644 --- a/src/ringct/bulletproofs.cc +++ b/src/ringct/bulletproofs.cc @@ -67,6 +67,8 @@ static ge_p3 Hi_p3[maxN*maxM], Gi_p3[maxN*maxM]; static std::shared_ptr straus_HiGi_cache; static std::shared_ptr pippenger_HiGi_cache; static const rct::key TWO = { {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 } }; +static const rct::key MINUS_ONE = { { 0xec, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10 } }; +static const rct::key MINUS_INV_EIGHT = { { 0x74, 0xa4, 0x19, 0x7a, 0xf0, 0x7d, 0x0b, 0xf7, 0x05, 0xc2, 0xda, 0x25, 0x2b, 0x5c, 0x0b, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a } }; static const rct::keyV oneN = vector_dup(rct::identity(), maxN); static const rct::keyV twoN = vector_powers(TWO, maxN); static const rct::key ip12 = inner_product(oneN, twoN); @@ -440,6 +442,7 @@ Bulletproof bulletproof_PROVE(const rct::key &sv, const rct::key &gamma) PERF_TIMER_START_BP(PROVE_v); rct::addKeys2(V, gamma, sv, rct::H); + V = rct::scalarmultKey(V, INV_EIGHT); PERF_TIMER_STOP(PROVE_v); PERF_TIMER_START_BP(PROVE_aLaR); @@ -482,6 +485,7 @@ Bulletproof bulletproof_PROVE(const rct::key &sv, const rct::key &gamma) rct::key ve = vector_exponent(aL, aR); rct::key A; rct::addKeys(A, ve, rct::scalarmultBase(alpha)); + A = rct::scalarmultKey(A, INV_EIGHT); // PAPER LINES 40-42 rct::keyV sL = rct::skvGen(N), sR = rct::skvGen(N); @@ -489,6 +493,7 @@ Bulletproof bulletproof_PROVE(const rct::key &sv, const rct::key &gamma) ve = vector_exponent(sL, sR); rct::key S; rct::addKeys(S, ve, rct::scalarmultBase(rho)); + S = rct::scalarmultKey(S, INV_EIGHT); // PAPER LINES 43-45 rct::key y = hash_cache_mash(hash_cache, A, S); @@ -563,7 +568,9 @@ Bulletproof bulletproof_PROVE(const rct::key &sv, const rct::key &gamma) rct::key tau1 = rct::skGen(), tau2 = rct::skGen(); rct::key T1 = rct::addKeys(rct::scalarmultH(t1), rct::scalarmultBase(tau1)); + T1 = rct::scalarmultKey(T1, INV_EIGHT); rct::key T2 = rct::addKeys(rct::scalarmultH(t2), rct::scalarmultBase(tau2)); + T2 = rct::scalarmultKey(T2, INV_EIGHT); // PAPER LINES 49-51 rct::key x = hash_cache_mash(hash_cache, z, T1, T2); @@ -640,9 +647,11 @@ Bulletproof bulletproof_PROVE(const rct::key &sv, const rct::key &gamma) L[round] = vector_exponent_custom(slice(Gprime, nprime, Gprime.size()), slice(Hprime, 0, nprime), slice(aprime, 0, nprime), slice(bprime, nprime, bprime.size())); sc_mul(tmp.bytes, cL.bytes, x_ip.bytes); rct::addKeys(L[round], L[round], rct::scalarmultH(tmp)); + L[round] = rct::scalarmultKey(L[round], INV_EIGHT); R[round] = vector_exponent_custom(slice(Gprime, 0, nprime), slice(Hprime, nprime, Hprime.size()), slice(aprime, nprime, aprime.size()), slice(bprime, 0, nprime)); sc_mul(tmp.bytes, cR.bytes, x_ip.bytes); rct::addKeys(R[round], R[round], rct::scalarmultH(tmp)); + R[round] = rct::scalarmultKey(R[round], INV_EIGHT); // PAPER LINES 21-22 w[round] = hash_cache_mash(hash_cache, L[round], R[round]); @@ -715,7 +724,10 @@ Bulletproof bulletproof_PROVE(const rct::keyV &sv, const rct::keyV &gamma) PERF_TIMER_START_BP(PROVE_v); for (size_t i = 0; i < sv.size(); ++i) + { rct::addKeys2(V[i], gamma[i], sv[i], rct::H); + V[i] = rct::scalarmultKey(V[i], INV_EIGHT); + } PERF_TIMER_STOP(PROVE_v); PERF_TIMER_START_BP(PROVE_aLaR); @@ -769,6 +781,7 @@ Bulletproof bulletproof_PROVE(const rct::keyV &sv, const rct::keyV &gamma) rct::key ve = vector_exponent(aL, aR); rct::key A; rct::addKeys(A, ve, rct::scalarmultBase(alpha)); + A = rct::scalarmultKey(A, INV_EIGHT); // PAPER LINES 40-42 rct::keyV sL = rct::skvGen(MN), sR = rct::skvGen(MN); @@ -776,6 +789,7 @@ Bulletproof bulletproof_PROVE(const rct::keyV &sv, const rct::keyV &gamma) ve = vector_exponent(sL, sR); rct::key S; rct::addKeys(S, ve, rct::scalarmultBase(rho)); + S = rct::scalarmultKey(S, INV_EIGHT); // PAPER LINES 43-45 rct::key y = hash_cache_mash(hash_cache, A, S); @@ -835,7 +849,9 @@ Bulletproof bulletproof_PROVE(const rct::keyV &sv, const rct::keyV &gamma) rct::key tau1 = rct::skGen(), tau2 = rct::skGen(); rct::key T1 = rct::addKeys(rct::scalarmultH(t1), rct::scalarmultBase(tau1)); + T1 = rct::scalarmultKey(T1, INV_EIGHT); rct::key T2 = rct::addKeys(rct::scalarmultH(t2), rct::scalarmultBase(tau2)); + T2 = rct::scalarmultKey(T2, INV_EIGHT); // PAPER LINES 49-51 rct::key x = hash_cache_mash(hash_cache, z, T1, T2); @@ -925,9 +941,11 @@ Bulletproof bulletproof_PROVE(const rct::keyV &sv, const rct::keyV &gamma) L[round] = vector_exponent_custom(slice(Gprime, nprime, Gprime.size()), slice(Hprime, 0, nprime), slice(aprime, 0, nprime), slice(bprime, nprime, bprime.size())); sc_mul(tmp.bytes, cL.bytes, x_ip.bytes); rct::addKeys(L[round], L[round], rct::scalarmultH(tmp)); + L[round] = rct::scalarmultKey(L[round], INV_EIGHT); R[round] = vector_exponent_custom(slice(Gprime, 0, nprime), slice(Hprime, nprime, Hprime.size()), slice(aprime, nprime, aprime.size()), slice(bprime, 0, nprime)); sc_mul(tmp.bytes, cR.bytes, x_ip.bytes); rct::addKeys(R[round], R[round], rct::scalarmultH(tmp)); + R[round] = rct::scalarmultKey(R[round], INV_EIGHT); // PAPER LINES 21-22 w[round] = hash_cache_mash(hash_cache, L[round], R[round]); @@ -991,18 +1009,6 @@ bool bulletproof_VERIFY(const std::vector &proofs) { const Bulletproof &proof = *p; - // check subgroup - for (const rct::key &k: proof.V) - CHECK_AND_ASSERT_MES(rct::isInMainSubgroup(k), false, "Input point not in subgroup"); - for (const rct::key &k: proof.L) - CHECK_AND_ASSERT_MES(rct::isInMainSubgroup(k), false, "Input point not in subgroup"); - for (const rct::key &k: proof.R) - CHECK_AND_ASSERT_MES(rct::isInMainSubgroup(k), false, "Input point not in subgroup"); - CHECK_AND_ASSERT_MES(rct::isInMainSubgroup(proof.A), false, "Input point not in subgroup"); - CHECK_AND_ASSERT_MES(rct::isInMainSubgroup(proof.S), false, "Input point not in subgroup"); - CHECK_AND_ASSERT_MES(rct::isInMainSubgroup(proof.T1), false, "Input point not in subgroup"); - CHECK_AND_ASSERT_MES(rct::isInMainSubgroup(proof.T2), false, "Input point not in subgroup"); - // check scalar range CHECK_AND_ASSERT_MES(is_reduced(proof.taux), false, "Input scalar not in range"); CHECK_AND_ASSERT_MES(is_reduced(proof.mu), false, "Input scalar not in range"); @@ -1078,22 +1084,26 @@ bool bulletproof_VERIFY(const std::vector &proofs) sc_muladd(y1.bytes, tmp.bytes, weight.bytes, y1.bytes); for (size_t j = 0; j < proof.V.size(); j++) { - multiexp_data.emplace_back(zpow[j+2], proof.V[j]); + sc_mul(tmp.bytes, zpow[j+2].bytes, EIGHT.bytes); + multiexp_data.emplace_back(tmp, proof.V[j]); } rct::key temp = multiexp(multiexp_data, false); rct::addKeys(Y2, Y2, rct::scalarmultKey(temp, weight)); - sc_mul(tmp.bytes, x.bytes, weight.bytes); + rct::key weight8; + sc_mul(weight8.bytes, weight.bytes, EIGHT.bytes); + sc_mul(tmp.bytes, x.bytes, weight8.bytes); rct::addKeys(Y3, Y3, rct::scalarmultKey(proof.T1, tmp)); rct::key xsq; sc_mul(xsq.bytes, x.bytes, x.bytes); - sc_mul(tmp.bytes, xsq.bytes, weight.bytes); + sc_mul(tmp.bytes, xsq.bytes, weight8.bytes); rct::addKeys(Y4, Y4, rct::scalarmultKey(proof.T2, tmp)); PERF_TIMER_STOP(VERIFY_line_61rl_new); PERF_TIMER_START_BP(VERIFY_line_62); // PAPER LINE 62 - rct::addKeys(Z0, Z0, rct::scalarmultKey(rct::addKeys(proof.A, rct::scalarmultKey(proof.S, x)), weight)); + sc_mul(tmp.bytes, x.bytes, EIGHT.bytes); + rct::addKeys(Z0, Z0, rct::scalarmultKey(rct::addKeys(rct::scalarmultKey(proof.A, EIGHT), rct::scalarmultKey(proof.S, tmp)), weight)); PERF_TIMER_STOP(VERIFY_line_62); // Compute the number of rounds for the inner product @@ -1176,8 +1186,10 @@ bool bulletproof_VERIFY(const std::vector &proofs) for (size_t i = 0; i < rounds; ++i) { sc_mul(tmp.bytes, w[i].bytes, w[i].bytes); + sc_mul(tmp.bytes, tmp.bytes, EIGHT.bytes); multiexp_data.emplace_back(tmp, proof.L[i]); sc_mul(tmp.bytes, winv[i].bytes, winv[i].bytes); + sc_mul(tmp.bytes, tmp.bytes, EIGHT.bytes); multiexp_data.emplace_back(tmp, proof.R[i]); } rct::key acc = multiexp(multiexp_data, false); diff --git a/src/ringct/rctOps.h b/src/ringct/rctOps.h index f0320f33329..53c8e1d0154 100644 --- a/src/ringct/rctOps.h +++ b/src/ringct/rctOps.h @@ -63,6 +63,8 @@ namespace rct { static const key I = { {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 } }; static const key L = { {0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10 } }; static const key G = { {0x58, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66 } }; + static const key EIGHT = { {0x08, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 } }; + static const key INV_EIGHT = { { 0x79, 0x2f, 0xdc, 0xe2, 0x29, 0xe5, 0x06, 0x61, 0xd0, 0xda, 0x1c, 0x7d, 0xb3, 0x9d, 0xd3, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06 } }; //Creates a zero scalar inline key zero() { return Z; } diff --git a/src/ringct/rctSigs.cpp b/src/ringct/rctSigs.cpp index e98e6290343..29743c7900b 100644 --- a/src/ringct/rctSigs.cpp +++ b/src/ringct/rctSigs.cpp @@ -768,7 +768,7 @@ namespace rct { #endif for (i = 0; i < outamounts.size(); ++i) { - rv.outPk[i].mask = C[i]; + rv.outPk[i].mask = rct::scalarmultKey(C[i], EIGHT); outSk[i].mask = masks[i]; } } @@ -788,7 +788,7 @@ namespace rct { #endif for (i = 0; i < batch_size; ++i) { - rv.outPk[i + amounts_proved].mask = C[i]; + rv.outPk[i + amounts_proved].mask = rct::scalarmultKey(C[i], EIGHT); outSk[i + amounts_proved].mask = masks[i]; } amounts_proved += batch_size; From 2bf636503fa09346b02964b2903ca70579f94c6b Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 6 Aug 2018 15:21:07 +0000 Subject: [PATCH 0387/1404] bulletproofs: speed up the latest changes a bit --- src/ringct/bulletproofs.cc | 68 +++++++++++++++------------- src/ringct/rctOps.cpp | 14 ++++++ src/ringct/rctOps.h | 2 + src/ringct/rctSigs.cpp | 4 +- tests/performance_tests/crypto_ops.h | 4 ++ tests/performance_tests/main.cpp | 2 + tests/unit_tests/ringct.cpp | 7 +++ 7 files changed, 67 insertions(+), 34 deletions(-) diff --git a/src/ringct/bulletproofs.cc b/src/ringct/bulletproofs.cc index 5bcdd968dbe..165691bfe97 100644 --- a/src/ringct/bulletproofs.cc +++ b/src/ringct/bulletproofs.cc @@ -92,30 +92,37 @@ static bool is_reduced(const rct::key &scalar) return scalar == reduced; } -//addKeys3acc_p3 -//aAbB += a*A + b*B where a, b are scalars, A, B are curve points -//A and B must be input after applying "precomp" -static void addKeys3acc_p3(ge_p3 *aAbB, const key &a, const ge_dsmp A, const key &b, const ge_dsmp B) +static void addKeys_acc_p3(ge_p3 *acc_p3, const rct::key &a, const rct::key &point) { - ge_p3 rv; + ge_p3 p3; + CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&p3, point.bytes) == 0, "ge_frombytes_vartime failed"); + ge_scalarmult_p3(&p3, a.bytes, &p3); + ge_cached cached; + ge_p3_to_cached(&cached, acc_p3); ge_p1p1 p1; - ge_p2 p2; - ge_double_scalarmult_precomp_vartime2_p3(&rv, a.bytes, A, b.bytes, B); + ge_add(&p1, &p3, &cached); + ge_p1p1_to_p3(acc_p3, &p1); +} + +static void add_acc_p3(ge_p3 *acc_p3, const rct::key &point) +{ + ge_p3 p3; + CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&p3, point.bytes) == 0, "ge_frombytes_vartime failed"); ge_cached cached; - ge_p3_to_cached(&cached, aAbB); - ge_add(&p1, &rv, &cached); - ge_p1p1_to_p3(aAbB, &p1); + ge_p3_to_cached(&cached, &p3); + ge_p1p1 p1; + ge_add(&p1, acc_p3, &cached); + ge_p1p1_to_p3(acc_p3, &p1); } -static void addKeys_acc_p3(ge_p3 *acc_p3, const rct::key &a, const rct::key &point) +static void sub_acc_p3(ge_p3 *acc_p3, const rct::key &point) { ge_p3 p3; CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&p3, point.bytes) == 0, "ge_frombytes_vartime failed"); - ge_scalarmult_p3(&p3, a.bytes, &p3); ge_cached cached; - ge_p3_to_cached(&cached, acc_p3); + ge_p3_to_cached(&cached, &p3); ge_p1p1 p1; - ge_add(&p1, &p3, &cached); + ge_sub(&p1, acc_p3, &cached); ge_p1p1_to_p3(acc_p3, &p1); } @@ -1087,9 +1094,7 @@ bool bulletproof_VERIFY(const std::vector &proofs) sc_mul(tmp.bytes, zpow[j+2].bytes, EIGHT.bytes); multiexp_data.emplace_back(tmp, proof.V[j]); } - rct::key temp = multiexp(multiexp_data, false); - - rct::addKeys(Y2, Y2, rct::scalarmultKey(temp, weight)); + rct::addKeys(Y2, Y2, rct::scalarmultKey(multiexp(multiexp_data, false), weight)); rct::key weight8; sc_mul(weight8.bytes, weight.bytes, EIGHT.bytes); sc_mul(tmp.bytes, x.bytes, weight8.bytes); @@ -1103,7 +1108,7 @@ bool bulletproof_VERIFY(const std::vector &proofs) PERF_TIMER_START_BP(VERIFY_line_62); // PAPER LINE 62 sc_mul(tmp.bytes, x.bytes, EIGHT.bytes); - rct::addKeys(Z0, Z0, rct::scalarmultKey(rct::addKeys(rct::scalarmultKey(proof.A, EIGHT), rct::scalarmultKey(proof.S, tmp)), weight)); + rct::addKeys(Z0, Z0, rct::scalarmultKey(rct::addKeys(rct::scalarmult8(proof.A), rct::scalarmultKey(proof.S, tmp)), weight)); PERF_TIMER_STOP(VERIFY_line_62); // Compute the number of rounds for the inner product @@ -1202,23 +1207,22 @@ bool bulletproof_VERIFY(const std::vector &proofs) // now check all proofs at once PERF_TIMER_START_BP(VERIFY_step2_check); - rct::key check1 = rct::identity(); - rct::addKeys(check1, check1, rct::scalarmultBase(y0)); - rct::addKeys(check1, check1, rct::scalarmultH(y1)); - rct::subKeys(check1, check1, Y2); - rct::subKeys(check1, check1, Y3); - rct::subKeys(check1, check1, Y4); - if (!(check1 == rct::identity())) + ge_p3 check1; + ge_scalarmult_base(&check1, y0.bytes); + addKeys_acc_p3(&check1, y1, rct::H); + sub_acc_p3(&check1, Y2); + sub_acc_p3(&check1, Y3); + sub_acc_p3(&check1, Y4); + if (!ge_p3_is_point_at_infinity(&check1)) { MERROR("Verification failure at step 1"); return false; } - rct::key check2 = rct::identity(); - rct::addKeys(check2, check2, Z0); + ge_p3 check2; sc_sub(tmp.bytes, rct::zero().bytes, z1.bytes); - rct::addKeys(check2, check2, rct::scalarmultBase(tmp)); - rct::addKeys(check2, check2, Z2); - rct::addKeys(check2, check2, rct::scalarmultH(z3)); + ge_double_scalarmult_base_vartime_p3(&check2, z3.bytes, &ge_p3_H, tmp.bytes); + add_acc_p3(&check2, Z0); + add_acc_p3(&check2, Z2); std::vector multiexp_data; multiexp_data.reserve(2 * maxMN); @@ -1229,10 +1233,10 @@ bool bulletproof_VERIFY(const std::vector &proofs) sc_sub(tmp.bytes, rct::zero().bytes, z5[i].bytes); multiexp_data.emplace_back(tmp, Hi_p3[i]); } - rct::addKeys(check2, check2, multiexp(multiexp_data, true)); + add_acc_p3(&check2, multiexp(multiexp_data, true)); PERF_TIMER_STOP(VERIFY_step2_check); - if (!(check2 == rct::identity())) + if (!ge_p3_is_point_at_infinity(&check2)) { MERROR("Verification failure at step 2"); return false; diff --git a/src/ringct/rctOps.cpp b/src/ringct/rctOps.cpp index df027f4b677..6c3c4500e06 100644 --- a/src/ringct/rctOps.cpp +++ b/src/ringct/rctOps.cpp @@ -211,6 +211,20 @@ namespace rct { return aP; } + //Computes 8P + key scalarmult8(const key & P) { + ge_p3 p3; + CHECK_AND_ASSERT_THROW_MES_L1(ge_frombytes_vartime(&p3, P.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast(__LINE__)); + ge_p2 p2; + ge_p3_to_p2(&p2, &p3); + ge_p1p1 p1; + ge_mul8(&p1, &p2); + ge_p1p1_to_p2(&p2, &p1); + rct::key res; + ge_tobytes(res.bytes, &p2); + return res; + } + //Computes aL where L is the curve order bool isInMainSubgroup(const key & a) { ge_p3 p3; diff --git a/src/ringct/rctOps.h b/src/ringct/rctOps.h index 53c8e1d0154..50645821c48 100644 --- a/src/ringct/rctOps.h +++ b/src/ringct/rctOps.h @@ -122,6 +122,8 @@ namespace rct { key scalarmultKey(const key &P, const key &a); //Computes aH where H= toPoint(cn_fast_hash(G)), G the basepoint key scalarmultH(const key & a); + // multiplies a point by 8 + key scalarmult8(const key & P); // checks a is in the main subgroup (ie, not a small one) bool isInMainSubgroup(const key & a); diff --git a/src/ringct/rctSigs.cpp b/src/ringct/rctSigs.cpp index 29743c7900b..fe0cd9c5737 100644 --- a/src/ringct/rctSigs.cpp +++ b/src/ringct/rctSigs.cpp @@ -768,7 +768,7 @@ namespace rct { #endif for (i = 0; i < outamounts.size(); ++i) { - rv.outPk[i].mask = rct::scalarmultKey(C[i], EIGHT); + rv.outPk[i].mask = rct::scalarmult8(C[i]); outSk[i].mask = masks[i]; } } @@ -788,7 +788,7 @@ namespace rct { #endif for (i = 0; i < batch_size; ++i) { - rv.outPk[i + amounts_proved].mask = rct::scalarmultKey(C[i], EIGHT); + rv.outPk[i + amounts_proved].mask = rct::scalarmult8(C[i]); outSk[i + amounts_proved].mask = masks[i]; } amounts_proved += batch_size; diff --git a/tests/performance_tests/crypto_ops.h b/tests/performance_tests/crypto_ops.h index f52faa70866..3c68583c5a6 100644 --- a/tests/performance_tests/crypto_ops.h +++ b/tests/performance_tests/crypto_ops.h @@ -45,6 +45,8 @@ enum test_op op_addKeys, op_scalarmultBase, op_scalarmultKey, + op_scalarmultH, + op_scalarmult8, op_ge_double_scalarmult_base_vartime, op_ge_double_scalarmult_precomp_vartime, op_ge_double_scalarmult_precomp_vartime2, @@ -97,6 +99,8 @@ class test_crypto_ops case op_addKeys: rct::addKeys(key, point0, point1); break; case op_scalarmultBase: rct::scalarmultBase(scalar0); break; case op_scalarmultKey: rct::scalarmultKey(point0, scalar0); break; + case op_scalarmultH: rct::scalarmultH(scalar0); break; + case op_scalarmult8: rct::scalarmult8(point0); break; case op_ge_double_scalarmult_base_vartime: ge_double_scalarmult_base_vartime(&tmp_p2, scalar0.bytes, &p3_0, scalar1.bytes); break; case op_ge_double_scalarmult_precomp_vartime: ge_double_scalarmult_precomp_vartime(&tmp_p2, scalar0.bytes, &p3_0, scalar1.bytes, precomp0); break; case op_ge_double_scalarmult_precomp_vartime2: ge_double_scalarmult_precomp_vartime2(&tmp_p2, scalar0.bytes, precomp0, scalar1.bytes, precomp1); break; diff --git a/tests/performance_tests/main.cpp b/tests/performance_tests/main.cpp index d8348629632..7c5135c65cb 100644 --- a/tests/performance_tests/main.cpp +++ b/tests/performance_tests/main.cpp @@ -247,6 +247,8 @@ int main(int argc, char** argv) TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_addKeys); TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_scalarmultBase); TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_scalarmultKey); + TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_scalarmultH); + TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_scalarmult8); TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_ge_double_scalarmult_base_vartime); TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_ge_double_scalarmult_precomp_vartime); TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_ge_double_scalarmult_precomp_vartime2); diff --git a/tests/unit_tests/ringct.cpp b/tests/unit_tests/ringct.cpp index 360529b9e53..3877ef7859c 100644 --- a/tests/unit_tests/ringct.cpp +++ b/tests/unit_tests/ringct.cpp @@ -1093,6 +1093,13 @@ TEST(ringct, H) ASSERT_EQ(memcmp(&p3, &ge_p3_H, sizeof(ge_p3)), 0); } +TEST(ringct, mul8) +{ + ASSERT_EQ(rct::scalarmult8(rct::identity()), rct::identity()); + ASSERT_EQ(rct::scalarmult8(rct::H), rct::scalarmultKey(rct::H, rct::EIGHT)); + ASSERT_EQ(rct::scalarmultKey(rct::scalarmultKey(rct::H, rct::INV_EIGHT), rct::EIGHT), rct::H); +} + TEST(ringct, aggregated) { static const size_t N_PROOFS = 16; From 8f418a6db0953d7a1d0c1dfa24d79f59cef618b7 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 21 Aug 2018 15:28:06 +0000 Subject: [PATCH 0388/1404] bulletproofs: #include Apparently needed for openssl 1.1.x --- src/ringct/bulletproofs.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ringct/bulletproofs.cc b/src/ringct/bulletproofs.cc index 165691bfe97..abe4ef18dcb 100644 --- a/src/ringct/bulletproofs.cc +++ b/src/ringct/bulletproofs.cc @@ -30,6 +30,7 @@ #include #include +#include #include #include "misc_log_ex.h" #include "common/perf_timer.h" From ac4f71c200d24613ae9abdc35756e64672114cba Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 24 Aug 2018 11:05:03 +0000 Subject: [PATCH 0389/1404] wallet2: bump testnet rollback to account for coming reorg --- src/wallet/wallet2.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 9b977350eb9..a50a88b5946 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -9994,7 +9994,7 @@ uint64_t wallet2::get_approximate_blockchain_height() const // Calculated blockchain height uint64_t approx_blockchain_height = fork_block + (time(NULL) - fork_time)/seconds_per_block; // testnet got some huge rollbacks, so the estimation is way off - static const uint64_t approximate_testnet_rolled_back_blocks = 148540; + static const uint64_t approximate_testnet_rolled_back_blocks = 303967; if (m_nettype == TESTNET && approx_blockchain_height > approximate_testnet_rolled_back_blocks) approx_blockchain_height -= approximate_testnet_rolled_back_blocks; LOG_PRINT_L2("Calculated blockchain height: " << approx_blockchain_height); From 9137ad2c635daa63d1ff804a0646457a5238d8b1 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 21 Aug 2018 22:53:07 +0000 Subject: [PATCH 0390/1404] blockchain: add a testnet v9 a day after v8 So that bulletproofs become mandatory --- src/cryptonote_core/blockchain.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 6fe4a287ff3..b20fe986915 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -132,6 +132,7 @@ static const struct { { 6, 971400, 0, 1501709789 }, { 7, 1057027, 0, 1512211236 }, { 8, 1057058, 0, 1515967497 }, + { 9, 1057778, 0, 1515967498 }, }; static const uint64_t testnet_hard_fork_version_1_till = 624633; From bcda7adcd444cc72b5c0d02da65be6b5cd843fc2 Mon Sep 17 00:00:00 2001 From: thomasvaughan Date: Sun, 2 Sep 2018 14:51:23 +0000 Subject: [PATCH 0391/1404] NetBSD support --- CMakeLists.txt | 11 +++++++++++ external/CMakeLists.txt | 3 +++ external/db_drivers/CMakeLists.txt | 3 +++ external/easylogging++/easylogging++.h | 9 +++++++-- src/crypto/oaes_lib.c | 13 +++++++------ src/crypto/slow-hash.c | 2 +- src/crypto/tree-hash.c | 3 ++- 7 files changed, 34 insertions(+), 10 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index cab85358180..20af7e07a99 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -502,6 +502,17 @@ else() set(ARCH_FLAG "-march=armv8") else() set(ARCH_FLAG "-march=${ARCH}") + if(ARCH STREQUAL "native") + check_c_compiler_flag(-march=native CC_SUPPORTS_MARCH_NATIVE) + if (NOT CC_SUPPORTS_MARCH_NATIVE) + check_c_compiler_flag(-mtune=native CC_SUPPORTS_MTUNE_NATIVE) + if (CC_SUPPORTS_MTUNE_NATIVE) + set(ARCH_FLAG "-mtune=${ARCH}") + else() + set(ARCH_FLAG "") + endif() + endif() + endif() endif() set(WARNINGS "-Wall -Wextra -Wpointer-arith -Wundef -Wvla -Wwrite-strings -Wno-error=extra -Wno-error=deprecated-declarations -Wno-unused-parameter -Wno-unused-variable -Wno-error=unused-variable -Wno-error=undef -Wno-error=uninitialized") if(NOT MINGW) diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt index 1fc4d64c1cb..358d47a4fb9 100644 --- a/external/CMakeLists.txt +++ b/external/CMakeLists.txt @@ -47,6 +47,9 @@ if(MSVC) elseif(NOT MSVC) set_property(TARGET libminiupnpc-static APPEND_STRING PROPERTY COMPILE_FLAGS " -Wno-undef -Wno-unused-result -Wno-unused-value") endif() +if(CMAKE_SYSTEM_NAME MATCHES "NetBSD") + set_property(TARGET libminiupnpc-static APPEND_STRING PROPERTY COMPILE_FLAGS " -D_NETBSD_SOURCE") +endif() set(UPNP_LIBRARIES "libminiupnpc-static" PARENT_SCOPE) diff --git a/external/db_drivers/CMakeLists.txt b/external/db_drivers/CMakeLists.txt index 13f70aa40ef..d537ec02929 100644 --- a/external/db_drivers/CMakeLists.txt +++ b/external/db_drivers/CMakeLists.txt @@ -32,3 +32,6 @@ message(STATUS "Using ${ARCH_WIDTH}-bit LMDB from source tree") add_subdirectory(liblmdb) set(LMDB_INCLUDE "${CMAKE_CURRENT_SOURCE_DIR}/liblmdb" CACHE STRING "LMDB Include path") set(LMDB_LIBRARY "lmdb" CACHE STRING "LMDB Library name") +if(CMAKE_SYSTEM_NAME MATCHES "NetBSD") + set_property(TARGET lmdb APPEND_STRING PROPERTY COMPILE_FLAGS " -D_SEM_SEMUN_UNDEFINED") +endif() diff --git a/external/easylogging++/easylogging++.h b/external/easylogging++/easylogging++.h index 193f835b029..8e8b7094b19 100644 --- a/external/easylogging++/easylogging++.h +++ b/external/easylogging++/easylogging++.h @@ -104,6 +104,11 @@ #else # define ELPP_OS_OPENBSD 0 #endif +#if (defined(__NetBSD__)) +# define ELPP_OS_NETBSD 1 +#else +# define ELPP_OS_NETBSD 0 +#endif #if (defined(__sun)) # define ELPP_OS_SOLARIS 1 #else @@ -115,7 +120,7 @@ # define ELPP_OS_DRAGONFLY 0 #endif // Unix -#if ((ELPP_OS_LINUX || ELPP_OS_MAC || ELPP_OS_FREEBSD || ELPP_OS_SOLARIS || ELPP_OS_DRAGONFLY || ELPP_OS_OPENBSD) && (!ELPP_OS_WINDOWS)) +#if ((ELPP_OS_LINUX || ELPP_OS_MAC || ELPP_OS_FREEBSD || ELPP_OS_SOLARIS || ELPP_OS_DRAGONFLY || ELPP_OS_OPENBSD || ELPP_OS_NETBSD ) && (!ELPP_OS_WINDOWS)) # define ELPP_OS_UNIX 1 #else # define ELPP_OS_UNIX 0 @@ -200,7 +205,7 @@ ELPP_INTERNAL_DEBUGGING_OUT_INFO << ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStre # define ELPP_INTERNAL_INFO(lvl, msg) #endif // (defined(ELPP_DEBUG_INFO)) #if (defined(ELPP_FEATURE_ALL)) || (defined(ELPP_FEATURE_CRASH_LOG)) -# if (ELPP_COMPILER_GCC && !ELPP_MINGW && !ELPP_OS_OPENBSD) +# if (ELPP_COMPILER_GCC && !ELPP_MINGW && !ELPP_OS_OPENBSD && !ELPP_OS_NETBSD) # define ELPP_STACKTRACE 1 # else # define ELPP_STACKTRACE 0 diff --git a/src/crypto/oaes_lib.c b/src/crypto/oaes_lib.c index 9e31ebf4649..210f5d43a11 100644 --- a/src/crypto/oaes_lib.c +++ b/src/crypto/oaes_lib.c @@ -33,14 +33,15 @@ #include #include -// OS X, FreeBSD, and OpenBSD don't need malloc.h +// OS X, FreeBSD, OpenBSD and NetBSD don't need malloc.h #if !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__OpenBSD__) \ - && !defined(__DragonFly__) + && !defined(__DragonFly__) && !defined(__NetBSD__) #include #endif -// ANDROID, FreeBSD, and OpenBSD also don't need timeb.h -#if !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__ANDROID__) +// ANDROID, FreeBSD, OpenBSD and NetBSD also don't need timeb.h +#if !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__ANDROID__) \ + && !defined(__NetBSD__) #include #else #include @@ -473,7 +474,7 @@ OAES_RET oaes_sprintf( #ifdef OAES_HAVE_ISAAC static void oaes_get_seed( char buf[RANDSIZ + 1] ) { - #if !defined(__FreeBSD__) && !defined(__OpenBSD__) + #if !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__NetBSD__) struct timeb timer; struct tm *gmTimer; char * _test = NULL; @@ -505,7 +506,7 @@ static void oaes_get_seed( char buf[RANDSIZ + 1] ) #else static uint32_t oaes_get_seed(void) { - #if !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__ANDROID__) + #if !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__ANDROID__) && !defined(__NetBSD__) struct timeb timer; struct tm *gmTimer; char * _test = NULL; diff --git a/src/crypto/slow-hash.c b/src/crypto/slow-hash.c index 9d4fc0dfa12..e76fb1c0ab2 100644 --- a/src/crypto/slow-hash.c +++ b/src/crypto/slow-hash.c @@ -492,7 +492,7 @@ void slow_hash_allocate_state(void) MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); #else #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || \ - defined(__DragonFly__) + defined(__DragonFly__) || defined(__NetBSD__) hp_state = mmap(0, MEMORY, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, 0, 0); #else diff --git a/src/crypto/tree-hash.c b/src/crypto/tree-hash.c index 57c38b86bbd..b2dc3ffb21b 100644 --- a/src/crypto/tree-hash.c +++ b/src/crypto/tree-hash.c @@ -36,7 +36,8 @@ #ifdef _MSC_VER #include -#elif !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__DragonFly__) +#elif !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__DragonFly__) \ + && !defined(__NetBSD__) #include #else #include From 07ec748c8245b42de0ec9964b2f0062c0034f2f8 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 27 Aug 2018 08:43:56 +0000 Subject: [PATCH 0392/1404] wipeable_string: add hex_to_pod function --- contrib/epee/include/hex.h | 1 + contrib/epee/include/wipeable_string.h | 19 ++++++++++++++++++- contrib/epee/src/wipeable_string.cpp | 3 ++- tests/unit_tests/wipeable_string.cpp | 7 +++++++ 4 files changed, 28 insertions(+), 2 deletions(-) diff --git a/contrib/epee/include/hex.h b/contrib/epee/include/hex.h index 02600c32062..901c666a98d 100644 --- a/contrib/epee/include/hex.h +++ b/contrib/epee/include/hex.h @@ -44,6 +44,7 @@ namespace epee static std::string string(const span src); //! \return A epee::wipeable_string containing hex of `src`. static epee::wipeable_string wipeable_string(const span src); + template static epee::wipeable_string wipeable_string(const T &pod) { return wipeable_string(span((const uint8_t*)&pod, sizeof(pod))); } //! \return An array containing hex of `src`. template diff --git a/contrib/epee/include/wipeable_string.h b/contrib/epee/include/wipeable_string.h index 4cebe5fdfe9..31854fe2e1b 100644 --- a/contrib/epee/include/wipeable_string.h +++ b/contrib/epee/include/wipeable_string.h @@ -28,10 +28,11 @@ #pragma once -#include +#include #include #include #include +#include "memwipe.h" #include "fnv1.h" namespace epee @@ -65,6 +66,8 @@ namespace epee void trim(); void split(std::vector &fields) const; boost::optional parse_hexstr() const; + template inline bool hex_to_pod(T &pod) const; + template inline bool hex_to_pod(tools::scrubbed &pod) const { return hex_to_pod(unwrap(pod)); } void resize(size_t sz); void reserve(size_t sz); void clear(); @@ -79,6 +82,20 @@ namespace epee private: std::vector buffer; }; + + template inline bool wipeable_string::hex_to_pod(T &pod) const + { + static_assert(std::is_pod::value, "expected pod type"); + if (size() != sizeof(T) * 2) + return false; + boost::optional blob = parse_hexstr(); + if (!blob) + return false; + if (blob->size() != sizeof(T)) + return false; + pod = *(const T*)blob->data(); + return true; + } } namespace std diff --git a/contrib/epee/src/wipeable_string.cpp b/contrib/epee/src/wipeable_string.cpp index 7c972276503..69f92e1064d 100644 --- a/contrib/epee/src/wipeable_string.cpp +++ b/contrib/epee/src/wipeable_string.cpp @@ -32,6 +32,8 @@ #include "misc_log_ex.h" #include "wipeable_string.h" +static constexpr const char hex[] = u8"0123456789abcdef"; + namespace { int atolower(int c) @@ -197,7 +199,6 @@ boost::optional wipeable_string::parse_hexstr() const const size_t len = size(); const char *d = data(); res->grow(0, len / 2); - static constexpr const char hex[] = u8"0123456789abcdef"; for (size_t i = 0; i < len; i += 2) { char c = atolower(d[i]); diff --git a/tests/unit_tests/wipeable_string.cpp b/tests/unit_tests/wipeable_string.cpp index 5ea1c1729b2..65718fd4563 100644 --- a/tests/unit_tests/wipeable_string.cpp +++ b/tests/unit_tests/wipeable_string.cpp @@ -32,6 +32,7 @@ #include "misc_log_ex.h" #include "wipeable_string.h" +#include "hex.h" TEST(wipeable_string, ctor) { @@ -202,3 +203,9 @@ TEST(wipeable_string, parse_hexstr) ASSERT_TRUE((s = epee::wipeable_string("414243").parse_hexstr())); ASSERT_EQ(*s, epee::wipeable_string("ABC")); } + +TEST(wipeable_string, to_hex) +{ + ASSERT_TRUE(epee::to_hex::wipeable_string(epee::span((const uint8_t*)"", 0)) == epee::wipeable_string("")); + ASSERT_TRUE(epee::to_hex::wipeable_string(epee::span((const uint8_t*)"abc", 3)) == epee::wipeable_string("616263")); +} From 56b50faab20166fc77e67d12bcd9f424c621f875 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 27 Aug 2018 08:44:15 +0000 Subject: [PATCH 0393/1404] wallet: use wipeable_string in more places where a secret is used --- src/simplewallet/simplewallet.cpp | 39 ++++++++++++++----------------- src/wallet/wallet_rpc_server.cpp | 39 +++++++++++++++++++------------ 2 files changed, 41 insertions(+), 37 deletions(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index bdf4212ce13..f5d972ab40f 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -2943,20 +2943,19 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) } // parse view secret key - std::string viewkey_string = input_line("View key: "); + epee::wipeable_string viewkey_string = input_secure_line("Secret view key: "); if (std::cin.eof()) return false; if (viewkey_string.empty()) { fail_msg_writer() << tr("No data supplied, cancelled"); return false; } - cryptonote::blobdata viewkey_data; - if(!epee::string_tools::parse_hexstr_to_binbuff(viewkey_string, viewkey_data) || viewkey_data.size() != sizeof(crypto::secret_key)) + crypto::secret_key viewkey; + if (viewkey_string.hex_to_pod(unwrap(unwrap(viewkey)))) { fail_msg_writer() << tr("failed to parse view key secret key"); return false; } - crypto::secret_key viewkey = *reinterpret_cast(viewkey_data.data()); m_wallet_file=m_generate_from_view_key; @@ -2979,14 +2978,14 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) { m_wallet_file = m_generate_from_spend_key; // parse spend secret key - std::string spendkey_string = input_line("Secret spend key: "); + epee::wipeable_string spendkey_string = input_secure_line("Secret spend key: "); if (std::cin.eof()) return false; if (spendkey_string.empty()) { fail_msg_writer() << tr("No data supplied, cancelled"); return false; } - if (!epee::string_tools::hex_to_pod(spendkey_string, m_recovery_key)) + if (!spendkey_string.hex_to_pod(unwrap(unwrap(m_recovery_key)))) { fail_msg_writer() << tr("failed to parse spend key secret key"); return false; @@ -3019,36 +3018,34 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) } // parse spend secret key - std::string spendkey_string = input_line("Secret spend key: "); + epee::wipeable_string spendkey_string = input_secure_line("Secret spend key: "); if (std::cin.eof()) return false; if (spendkey_string.empty()) { fail_msg_writer() << tr("No data supplied, cancelled"); return false; } - cryptonote::blobdata spendkey_data; - if(!epee::string_tools::parse_hexstr_to_binbuff(spendkey_string, spendkey_data) || spendkey_data.size() != sizeof(crypto::secret_key)) + crypto::secret_key spendkey; + if (!spendkey_string.hex_to_pod(unwrap(unwrap(spendkey)))) { fail_msg_writer() << tr("failed to parse spend key secret key"); return false; } - crypto::secret_key spendkey = *reinterpret_cast(spendkey_data.data()); // parse view secret key - std::string viewkey_string = input_line("Secret view key: "); + epee::wipeable_string viewkey_string = input_secure_line("Secret view key: "); if (std::cin.eof()) return false; if (viewkey_string.empty()) { fail_msg_writer() << tr("No data supplied, cancelled"); return false; } - cryptonote::blobdata viewkey_data; - if(!epee::string_tools::parse_hexstr_to_binbuff(viewkey_string, viewkey_data) || viewkey_data.size() != sizeof(crypto::secret_key)) + crypto::secret_key viewkey; + if(!viewkey_string.hex_to_pod(unwrap(unwrap(viewkey)))) { fail_msg_writer() << tr("failed to parse view key secret key"); return false; } - crypto::secret_key viewkey = *reinterpret_cast(viewkey_data.data()); m_wallet_file=m_generate_from_keys; @@ -3124,7 +3121,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) } // parse secret view key - std::string viewkey_string = input_line("Secret view key: "); + epee::wipeable_string viewkey_string = input_secure_line("Secret view key: "); if (std::cin.eof()) return false; if (viewkey_string.empty()) @@ -3132,13 +3129,12 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) fail_msg_writer() << tr("No data supplied, cancelled"); return false; } - cryptonote::blobdata viewkey_data; - if(!epee::string_tools::parse_hexstr_to_binbuff(viewkey_string, viewkey_data) || viewkey_data.size() != sizeof(crypto::secret_key)) + crypto::secret_key viewkey; + if(!viewkey_string.hex_to_pod(unwrap(unwrap(viewkey)))) { fail_msg_writer() << tr("failed to parse secret view key"); return false; } - crypto::secret_key viewkey = *reinterpret_cast(viewkey_data.data()); // check that the view key matches the given address crypto::public_key pkey; @@ -3159,12 +3155,12 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) if(multisig_m == multisig_n) { std::vector multisig_secret_spendkeys(multisig_n); - std::string spendkey_string; + epee::wipeable_string spendkey_string; cryptonote::blobdata spendkey_data; // get N secret spend keys from user for(unsigned int i=0; i(spendkey_data.data()); } // sum the spend keys together to get the master spend key diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index 2cddea25d17..bb064f5a838 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -773,10 +773,10 @@ namespace tools { if (get_tx_key) { - std::string s = epee::string_tools::pod_to_hex(ptx.tx_key); + epee::wipeable_string s = epee::to_hex::wipeable_string(ptx.tx_key); for (const crypto::secret_key& additional_tx_key : ptx.additional_tx_keys) - s += epee::string_tools::pod_to_hex(additional_tx_key); - fill(tx_key, s); + s += epee::to_hex::wipeable_string(additional_tx_key); + fill(tx_key, std::string(s.data(), s.size())); } // Compute amount leaving wallet in tx. By convention dests does not include change outputs fill(amount, total_amount(ptx)); @@ -1584,11 +1584,13 @@ namespace tools } else if(req.key_type.compare("view_key") == 0) { - res.key = string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_view_secret_key); + epee::wipeable_string key = epee::to_hex::wipeable_string(m_wallet->get_account().get_keys().m_view_secret_key); + res.key = std::string(key.data(), key.size()); } else if(req.key_type.compare("spend_key") == 0) { - res.key = string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_spend_secret_key); + epee::wipeable_string key = epee::to_hex::wipeable_string(m_wallet->get_account().get_keys().m_spend_secret_key); + res.key = std::string(key.data(), key.size()); } else { @@ -1814,11 +1816,11 @@ namespace tools return false; } - std::ostringstream oss; - oss << epee::string_tools::pod_to_hex(tx_key); + epee::wipeable_string s; + s += epee::to_hex::wipeable_string(tx_key); for (size_t i = 0; i < additional_tx_keys.size(); ++i) - oss << epee::string_tools::pod_to_hex(additional_tx_keys[i]); - res.tx_key = oss.str(); + s += epee::to_hex::wipeable_string(additional_tx_keys[i]); + res.tx_key = std::string(s.data(), s.size()); return true; } //------------------------------------------------------------------------------------------------------------------------------ @@ -1834,26 +1836,33 @@ namespace tools return false; } - std::string tx_key_str = req.tx_key; + epee::wipeable_string tx_key_str = req.tx_key; + if (tx_key_str.size() < 64 || tx_key_str.size() % 64) + { + er.code = WALLET_RPC_ERROR_CODE_WRONG_KEY; + er.message = "Tx key has invalid format"; + return false; + } + const char *data = tx_key_str.data(); crypto::secret_key tx_key; - if (!epee::string_tools::hex_to_pod(tx_key_str.substr(0, 64), tx_key)) + if (!epee::wipeable_string(data, 64).hex_to_pod(unwrap(unwrap(tx_key)))) { er.code = WALLET_RPC_ERROR_CODE_WRONG_KEY; er.message = "Tx key has invalid format"; return false; } - tx_key_str = tx_key_str.substr(64); + size_t offset = 64; std::vector additional_tx_keys; - while (!tx_key_str.empty()) + while (offset < tx_key_str.size()) { additional_tx_keys.resize(additional_tx_keys.size() + 1); - if (!epee::string_tools::hex_to_pod(tx_key_str.substr(0, 64), additional_tx_keys.back())) + if (!epee::wipeable_string(data + offset, 64).hex_to_pod(unwrap(unwrap(additional_tx_keys.back())))) { er.code = WALLET_RPC_ERROR_CODE_WRONG_KEY; er.message = "Tx key has invalid format"; return false; } - tx_key_str = tx_key_str.substr(64); + offset += 64; } cryptonote::address_parse_info info; From 67a56a9f8b98c3d09616bffb569a320a392b447b Mon Sep 17 00:00:00 2001 From: stoffu Date: Wed, 12 Sep 2018 20:33:10 +0900 Subject: [PATCH 0394/1404] rctTypes: fix incorrect serialization --- src/ringct/rctTypes.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ringct/rctTypes.h b/src/ringct/rctTypes.h index ffc4df3ed0b..18290637bd2 100644 --- a/src/ringct/rctTypes.h +++ b/src/ringct/rctTypes.h @@ -313,10 +313,10 @@ namespace rct { return false; if (type == RCTTypeBulletproof) { - ar.tag("bp"); - ar.begin_array(); uint32_t nbp = bulletproofs.size(); FIELD(nbp) + ar.tag("bp"); + ar.begin_array(); if (nbp > outputs) return false; PREPARE_CUSTOM_VECTOR_SERIALIZATION(nbp, bulletproofs); From befdcbf4bef3a418a5fcd035b55dbadab2951d4b Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 12 Sep 2018 19:18:26 +0000 Subject: [PATCH 0395/1404] db_lmdb: do not use base for cumulative distribution it's confusing and needlessly complicated --- src/blockchain_db/lmdb/db_lmdb.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp index 9e22e2e4b07..b0f3ca5f009 100644 --- a/src/blockchain_db/lmdb/db_lmdb.cpp +++ b/src/blockchain_db/lmdb/db_lmdb.cpp @@ -3391,8 +3391,10 @@ bool BlockchainLMDB::get_output_distribution(uint64_t amount, uint64_t from_heig break; } + distribution[0] += base; for (size_t n = 1; n < distribution.size(); ++n) distribution[n] += distribution[n - 1]; + base = 0; TXN_POSTFIX_RDONLY(); From b2bb9312a75781e714acf3c406634b3d4cded418 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 12 Sep 2018 19:19:22 +0000 Subject: [PATCH 0396/1404] blockchain: simplify output distribution code --- src/cryptonote_core/blockchain.cpp | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index b20fe986915..578c951bee6 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -2054,15 +2054,10 @@ bool Blockchain::get_output_distribution(uint64_t amount, uint64_t from_height, { std::vector heights; heights.reserve(to_height + 1 - start_height); - uint64_t real_start_height = start_height > 0 ? start_height-1 : start_height; - for (uint64_t h = real_start_height; h <= to_height; ++h) + for (uint64_t h = start_height; h <= to_height; ++h) heights.push_back(h); distribution = m_db->get_block_cumulative_rct_outputs(heights); - if (start_height > 0) - { - base = distribution[0]; - distribution.erase(distribution.begin()); - } + base = 0; return true; } else From 55c7cd14582323b4403bed88bd02e2f62d2a3169 Mon Sep 17 00:00:00 2001 From: Lee Clagett Date: Mon, 10 Sep 2018 21:25:15 -0400 Subject: [PATCH 0397/1404] Adding expect - a value-or-error implementation --- src/common/CMakeLists.txt | 4 + src/common/error.cpp | 75 +++ src/common/error.h | 52 ++ src/common/expect.cpp | 70 +++ src/common/expect.h | 447 ++++++++++++++++ tests/unit_tests/CMakeLists.txt | 1 + tests/unit_tests/expect.cpp | 915 ++++++++++++++++++++++++++++++++ 7 files changed, 1564 insertions(+) create mode 100644 src/common/error.cpp create mode 100644 src/common/error.h create mode 100644 src/common/expect.cpp create mode 100644 src/common/expect.h create mode 100644 tests/unit_tests/expect.cpp diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index f0df05b0d27..606f6fc9a05 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -33,6 +33,8 @@ set(common_sources command_line.cpp dns_utils.cpp download.cpp + error.cpp + expect.cpp util.cpp i18n.cpp password.cpp @@ -54,6 +56,8 @@ set(common_private_headers common_fwd.h dns_utils.h download.h + error.h + expect.h http_connection.h int-util.h pod-class.h diff --git a/src/common/error.cpp b/src/common/error.cpp new file mode 100644 index 00000000000..e091e447813 --- /dev/null +++ b/src/common/error.cpp @@ -0,0 +1,75 @@ +// Copyright (c) 2018, The Monero Project +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#include "error.h" + +#include + +namespace +{ + struct category final : std::error_category + { + virtual const char* name() const noexcept override final + { + return "common_category()"; + } + + virtual std::string message(int value) const override final + { + switch (common_error(value)) + { + case common_error::kInvalidArgument: + return make_error_code(std::errc::invalid_argument).message(); + case common_error::kInvalidErrorCode: + return "expect was given an error value of zero"; + default: + break; + } + return "Unknown basic_category() value"; + } + + virtual std::error_condition default_error_condition(int value) const noexcept override final + { + // maps specific errors to generic `std::errc` cases. + switch (common_error(value)) + { + case common_error::kInvalidArgument: + case common_error::kInvalidErrorCode: + return std::errc::invalid_argument; + default: + break; + } + return std::error_condition{value, *this}; + } + }; +} + +std::error_category const& common_category() noexcept +{ + static const category instance{}; + return instance; +} + diff --git a/src/common/error.h b/src/common/error.h new file mode 100644 index 00000000000..6fef3eb4b3c --- /dev/null +++ b/src/common/error.h @@ -0,0 +1,52 @@ +// Copyright (c) 2018, The Monero Project +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#pragma once + +#include +#include + +enum class common_error : int +{ + // 0 is reserved for no error, as per expect + kInvalidArgument = 1, //!< A function argument is invalid + kInvalidErrorCode //!< Default `std::error_code` given to `expect` +}; + +std::error_category const& common_category() noexcept; + +inline std::error_code make_error_code(::common_error value) noexcept +{ + return std::error_code{int(value), common_category()}; +} + +namespace std +{ + template<> + struct is_error_code_enum<::common_error> + : true_type + {}; +} diff --git a/src/common/expect.cpp b/src/common/expect.cpp new file mode 100644 index 00000000000..c86e23e9514 --- /dev/null +++ b/src/common/expect.cpp @@ -0,0 +1,70 @@ +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "expect.h" + +#include +#include + +namespace detail +{ + namespace + { + std::string generate_error(const char* msg, const char* file, unsigned line) + { + std::string error_msg{}; + if (msg) + { + error_msg.append(msg); + if (file) + error_msg.append(" ("); + } + if (file) + { + error_msg.append("thrown at "); + + // remove path, get just filename + extension + char buff[256] = {0}; + el::base::utils::File::buildBaseFilename(file, buff, sizeof(buff) - 1); + error_msg.append(buff); + + error_msg.push_back(':'); + error_msg.append(std::to_string(line)); + } + if (msg && file) + error_msg.push_back(')'); + return error_msg; + } + } + + void expect::throw_(std::error_code ec, const char* msg, const char* file, unsigned line) + { + if (msg || file) + throw std::system_error{ec, generate_error(msg, file, line)}; + throw std::system_error{ec}; + } +} // detail diff --git a/src/common/expect.h b/src/common/expect.h new file mode 100644 index 00000000000..32624250244 --- /dev/null +++ b/src/common/expect.h @@ -0,0 +1,447 @@ +// Copyright (c) 2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#pragma once + +#include +#include +#include +#include + +#include "common/error.h" + +//! If precondition fails, return `::error::kInvalidArgument` in current scope. +#define MONERO_PRECOND(...) \ + do \ + { \ + if (!( __VA_ARGS__ )) \ + return {::common_error::kInvalidArgument}; \ + } while (0) + +//! Check `expect` and return errors in current scope. +#define MONERO_CHECK(...) \ + do \ + { \ + const ::expect result = __VA_ARGS__ ; \ + if (!result) \ + return result.error(); \ + } while (0) + +/*! Get `T` from `expect` by `std::move` as-if by function call. + `expect` returns nothing. + + \throw std::system_error with `expect::error()`, filename and line + number when `expect::has_error() == true`.*/ +#define MONERO_UNWRAP(...) \ + ::detail::expect::unwrap( __VA_ARGS__ , nullptr, __FILE__ , __LINE__ ) + +/* \throw std::system_error with `code` and `msg` as part of the details. The +filename and line number will automatically be injected into the explanation +string. `code` can be any enum convertible to `std::error_code`. */ +#define MONERO_THROW(code, msg) \ + ::detail::expect::throw_( code , msg , __FILE__ , __LINE__ ) + + +template class expect; + +namespace detail +{ + // Shortens the characters in the places that `enable_if` is used below. + template + using enable_if = typename std::enable_if::type; + + struct expect + { + //! \throw std::system_error with `ec`, optional `msg` and/or optional `file` + `line`. + static void throw_(std::error_code ec, const char* msg, const char* file, unsigned line); + + //! If `result.has_error()` call `throw_`. Otherwise, \return `*result` by move. + template + static T unwrap(::expect&& result, const char* error_msg, const char* file, unsigned line) + { + if (!result) + throw_(result.error(), error_msg, file, line); + return std::move(*result); + } + + //! If `result.has_error()` call `throw_`. + static void unwrap(::expect&& result, const char* error_msg, const char* file, unsigned line); + }; +} + +/*! + `expect` is a value or error implementation, similar to Rust std::result + or various C++ proposals (boost::expected, boost::outcome). This + implementation currently has a strict error type, `std::error_code`, and a + templated value type `T`. `expect` is implicitly convertible from `T` + or `std::error_code`, and one `expect` object type is implicitly + convertible to another `expect` object iff the destination value type + can be implicitly constructed from the source value type (i.e. + `struct U { ... U(T src) { ...} ... };`). + + `operator==` and `operator!=` are the only comparison operators provided; + comparison between different value types is allowed provided the two values + types have a `operator==` defined between them (i.e. + `assert(expect{100} == expect{100});`). Comparisons can also be + done against `std::error_code` objects or error code enums directly (i.e. + `assert(expect{make_error_code(common_error::kInvalidArgument)} == error::kInvalidArgument)`). + Comparison of default constructed `std::error_code` will always fail. + "Generic" comparisons can be done with `std::error_condition` via the `matches` + method only (i.e. + `assert(expect{make_error_code{common_error::kInvalidErrorCode}.matches(std::errc::invalid_argument))`), + `operator==` and `operator!=` will not work with `std::errc` or + `std::error_condition`. A comparison with `matches` is more expensive + because an equivalency between error categories is computed, but is + recommended when an error can be one of several categories (this is going + to be the case in nearly every situation when calling a function from + another C++ struct/class). + + `expect` is a special case with no stored value. It is used by + functions that can fail, but otherwise would return `void`. It is useful + for consistency; all macros, standalone functions, and comparison operators + work with `expect`. + + \note See `src/common/error.h` for creating a custom error enum. + */ +template +class expect +{ + static_assert(std::is_nothrow_destructible(), "T must have a nothrow destructor"); + + template + static constexpr bool is_convertible() noexcept + { + return std::is_constructible() && + std::is_convertible(); + } + + // MEMBERS + std::error_code code_; + typename std::aligned_storage::type storage_; + // MEMBERS + + T& get() noexcept + { + assert(has_value()); + return *reinterpret_cast(std::addressof(storage_)); + } + + T const& get() const noexcept + { + assert(has_value()); + return *reinterpret_cast(std::addressof(storage_)); + } + + template + void store(U&& value) noexcept(std::is_nothrow_constructible()) + { + new (std::addressof(storage_)) T{std::forward(value)}; + code_ = std::error_code{}; + } + + void maybe_throw() const + { + if (has_error()) + ::detail::expect::throw_(error(), nullptr, nullptr, 0); + } + +public: + using value_type = T; + using error_type = std::error_code; + + expect() = delete; + + /*! Store an error, `code`, in the `expect` object. If `code` creates a + `std::error_code` object whose `.value() == 0`, then `error()` will be set + to `::common_error::kInvalidErrorCode`. */ + expect(std::error_code const& code) noexcept + : code_(code), storage_() + { + if (!has_error()) + code_ = ::common_error::kInvalidErrorCode; + } + + //! Store a value, `val`, in the `expect` object. + expect(T val) noexcept(std::is_nothrow_move_constructible()) + : code_(), storage_() + { + store(std::move(val)); + } + + expect(expect const& src) noexcept(std::is_nothrow_copy_constructible()) + : code_(src.error()), storage_() + { + if (src.has_value()) + store(src.get()); + } + + //! Copy conversion from `U` to `T`. + template()>> + expect(expect const& src) noexcept(std::is_nothrow_constructible()) + : code_(src.error()), storage_() + { + if (src.has_value()) + store(*src); + } + + expect(expect&& src) noexcept(std::is_nothrow_move_constructible()) + : code_(src.error()), storage_() + { + if (src.has_value()) + store(std::move(src.get())); + } + + //! Move conversion from `U` to `T`. + template()>> + expect(expect&& src) noexcept(std::is_nothrow_constructible()) + : code_(src.error()), storage_() + { + if (src.has_value()) + store(std::move(*src)); + } + + ~expect() noexcept + { + if (has_value()) + get().~T(); + } + + expect& operator=(expect const& src) noexcept(std::is_nothrow_copy_constructible() && std::is_nothrow_copy_assignable()) + { + if (this != std::addressof(src)) + { + if (has_value() && src.has_value()) + get() = src.get(); + else if (has_value()) + get().~T(); + else if (src.has_value()) + store(src.get()); + code_ = src.error(); + } + return *this; + } + + /*! Move `src` into `this`. If `src.has_value() && addressof(src) != this` + then `src.value() will be in a "moved from state". */ + expect& operator=(expect&& src) noexcept(std::is_nothrow_move_constructible() && std::is_nothrow_move_assignable()) + { + if (this != std::addressof(src)) + { + if (has_value() && src.has_value()) + get() = std::move(src.get()); + else if (has_value()) + get().~T(); + else if (src.has_value()) + store(std::move(src.get())); + code_ = src.error(); + } + return *this; + } + + //! \return True if `this` is storing a value instead of an error. + explicit operator bool() const noexcept { return has_value(); } + + //! \return True if `this` is storing an error instead of a value. + bool has_error() const noexcept { return bool(code_); } + + //! \return True if `this` is storing a value instead of an error. + bool has_value() const noexcept { return !has_error(); } + + //! \return Error - always safe to call. Empty when `!has_error()`. + std::error_code error() const noexcept { return code_; } + + //! \return Value if `has_value()` otherwise \throw `std::system_error{error()}`. + T& value() & + { + maybe_throw(); + return get(); + } + + //! \return Value if `has_value()` otherwise \throw `std::system_error{error()}`. + T const& value() const & + { + maybe_throw(); + return get(); + } + + /*! Same as other overloads, but expressions such as `foo(bar().value())` + will automatically perform moves with no copies. */ + T&& value() && + { + maybe_throw(); + return std::move(get()); + } + + //! \return Value, \pre `has_value()`. + T* operator->() noexcept { return std::addressof(get()); } + //! \return Value, \pre `has_value()`. + T const* operator->() const noexcept { return std::addressof(get()); } + //! \return Value, \pre `has_value()`. + T& operator*() noexcept { return get(); } + //! \return Value, \pre `has_value()`. + T const& operator*() const noexcept { return get(); } + + /*! + \note This function is `noexcept` when `U == T` is `noexcept`. + \return True if `has_value() == rhs.has_value()` and if values or errors are equal. + */ + template + bool equal(expect const& rhs) const noexcept(noexcept(*std::declval>() == *rhs)) + { + return has_value() && rhs.has_value() ? + get() == *rhs : error() == rhs.error(); + } + + //! \return False if `has_value()`, otherwise `error() == rhs`. + bool equal(std::error_code const& rhs) const noexcept + { + return has_error() && error() == rhs; + } + + /*! + \note This function is `noexcept` when `U == T` is `noexcept`. + \return False if `has_error()`, otherwise `value() == rhs`. + */ + template::value>> + bool equal(U const& rhs) const noexcept(noexcept(*std::declval>() == rhs)) + { + return has_value() && get() == rhs; + } + + //! \return False if `has_value()`, otherwise `error() == rhs`. + bool matches(std::error_condition const& rhs) const noexcept + { + return has_error() && error() == rhs; + } +}; + +template<> +class expect +{ + std::error_code code_; + +public: + using value_type = void; + using error_type = std::error_code; + + //! Create a successful object. + expect() = default; + + expect(std::error_code const& code) noexcept + : code_(code) + { + if (!has_error()) + code_ = ::common_error::kInvalidErrorCode; + } + + expect(expect const&) = default; + ~expect() = default; + expect& operator=(expect const&) = default; + + //! \return True if `this` is storing a value instead of an error. + explicit operator bool() const noexcept { return !has_error(); } + + //! \return True if `this` is storing an error instead of a value. + bool has_error() const noexcept { return bool(code_); } + + //! \return Error - alway + std::error_code error() const noexcept { return code_; } + + //! \return `error() == rhs.error()`. + bool equal(expect const& rhs) const noexcept + { + return error() == rhs.error(); + } + + //! \return `has_error() && error() == rhs`. + bool equal(std::error_code const& rhs) const noexcept + { + return has_error() && error() == rhs; + } + + //! \return False if `has_value()`, otherwise `error() == rhs`. + bool matches(std::error_condition const& rhs) const noexcept + { + return has_error() && error() == rhs; + } +}; + +//! \return An `expect` object with `!has_error()`. +inline expect success() noexcept { return expect{}; } + +template +inline +bool operator==(expect const& lhs, expect const& rhs) noexcept(noexcept(lhs.equal(rhs))) +{ + return lhs.equal(rhs); +} + +template +inline +bool operator==(expect const& lhs, U const& rhs) noexcept(noexcept(lhs.equal(rhs))) +{ + return lhs.equal(rhs); +} + +template +inline +bool operator==(T const& lhs, expect const& rhs) noexcept(noexcept(rhs.equal(lhs))) +{ + return rhs.equal(lhs); +} + +template +inline +bool operator!=(expect const& lhs, expect const& rhs) noexcept(noexcept(lhs.equal(rhs))) +{ + return !lhs.equal(rhs); +} + +template +inline +bool operator!=(expect const& lhs, U const& rhs) noexcept(noexcept(lhs.equal(rhs))) +{ + return !lhs.equal(rhs); +} + +template +inline +bool operator!=(T const& lhs, expect const& rhs) noexcept(noexcept(rhs.equal(lhs))) +{ + return !rhs.equal(lhs); +} + +namespace detail +{ + inline void expect::unwrap(::expect&& result, const char* error_msg, const char* file, unsigned line) + { + if (!result) + throw_(result.error(), error_msg, file, line); + } +} + diff --git a/tests/unit_tests/CMakeLists.txt b/tests/unit_tests/CMakeLists.txt index 076a4a87395..0fa22b4e1d4 100644 --- a/tests/unit_tests/CMakeLists.txt +++ b/tests/unit_tests/CMakeLists.txt @@ -47,6 +47,7 @@ set(unit_tests_sources epee_boosted_tcp_server.cpp epee_levin_protocol_handler_async.cpp epee_utils.cpp + expect.cpp fee.cpp json_serialization.cpp get_xtype_from_string.cpp diff --git a/tests/unit_tests/expect.cpp b/tests/unit_tests/expect.cpp new file mode 100644 index 00000000000..efa843496e5 --- /dev/null +++ b/tests/unit_tests/expect.cpp @@ -0,0 +1,915 @@ +// Copyright (c) 2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include + +#include +#include +#include +#include +#include + +#include "common/expect.h" + +namespace +{ + struct move_only; + struct throw_construct; + struct throw_copies; + struct throw_moves; + + struct move_only + { + move_only() = default; + move_only(move_only const&) = delete; + move_only(move_only&&) = default; + ~move_only() = default; + move_only& operator=(move_only const&) = delete; + move_only& operator=(move_only&&) = default; + }; + + struct throw_construct + { + throw_construct() {} + throw_construct(int) {} + throw_construct(throw_construct const&) = default; + throw_construct(throw_construct&&) = default; + ~throw_construct() = default; + throw_construct& operator=(throw_construct const&) = default; + throw_construct& operator=(throw_construct&&) = default; + }; + + struct throw_copies + { + throw_copies() noexcept {} + throw_copies(throw_copies const&) {} + throw_copies(throw_copies&&) = default; + ~throw_copies() = default; + throw_copies& operator=(throw_copies const&) { return *this; } + throw_copies& operator=(throw_copies&&) = default; + bool operator==(throw_copies const&) noexcept { return true; } + bool operator==(throw_moves const&) noexcept { return true; } + }; + + struct throw_moves + { + throw_moves() noexcept {} + throw_moves(throw_moves const&) = default; + throw_moves(throw_moves&&) {} + ~throw_moves() = default; + throw_moves& operator=(throw_moves const&) = default; + throw_moves& operator=(throw_moves&&) { return *this; } + bool operator==(throw_moves const&) { return true; } + bool operator==(throw_copies const&) { return true; } + }; + + template + void construction_bench() + { + EXPECT_TRUE(std::is_copy_constructible>()); + EXPECT_TRUE(std::is_move_constructible>()); + EXPECT_TRUE(std::is_copy_assignable>()); + EXPECT_TRUE(std::is_move_assignable>()); + EXPECT_TRUE(std::is_destructible>()); + } + + template + void noexcept_bench() + { + EXPECT_TRUE(std::is_nothrow_copy_constructible>()); + EXPECT_TRUE(std::is_nothrow_move_constructible>()); + EXPECT_TRUE(std::is_nothrow_copy_assignable>()); + EXPECT_TRUE(std::is_nothrow_move_assignable>()); + EXPECT_TRUE(std::is_nothrow_destructible>()); + + EXPECT_TRUE(noexcept(bool(std::declval>()))); + EXPECT_TRUE(noexcept(std::declval>().has_error())); + EXPECT_TRUE(noexcept(std::declval>().error())); + EXPECT_TRUE(noexcept(std::declval>().equal(std::declval>()))); + EXPECT_TRUE(noexcept(std::declval>() == std::declval>())); + EXPECT_TRUE(noexcept(std::declval>() != std::declval>())); + } + + template + void conversion_bench() + { + EXPECT_TRUE((std::is_convertible>())); + EXPECT_TRUE((std::is_convertible>())); + EXPECT_TRUE((std::is_convertible>())); + EXPECT_TRUE((std::is_convertible>())); + + EXPECT_TRUE((std::is_constructible, std::error_code>())); + EXPECT_TRUE((std::is_constructible, std::error_code&&>())); + EXPECT_TRUE((std::is_constructible, std::error_code&>())); + EXPECT_TRUE((std::is_constructible, std::error_code const&>())); + } +} + + +TEST(Expect, Constructions) +{ + construction_bench(); + construction_bench(); + + EXPECT_TRUE(std::is_constructible>()); + + EXPECT_TRUE((std::is_constructible, expect>())); + + EXPECT_TRUE(std::is_move_constructible>()); + EXPECT_TRUE(std::is_move_assignable>()); +} + +TEST(Expect, Conversions) +{ + struct implicit { implicit(int) {} }; + struct explicit_only { explicit explicit_only(int) {} }; + + conversion_bench(); + conversion_bench(); + + EXPECT_TRUE((std::is_convertible>())); + EXPECT_TRUE((std::is_convertible>())); + EXPECT_TRUE((std::is_convertible>())); + EXPECT_TRUE((std::is_convertible>())); + EXPECT_TRUE((std::is_convertible, expect>())); + EXPECT_TRUE((std::is_convertible&&, expect>())); + EXPECT_TRUE((std::is_convertible&, expect>())); + EXPECT_TRUE((std::is_convertible const&, expect>())); + EXPECT_TRUE((std::is_convertible, expect>())); + EXPECT_TRUE((std::is_convertible&&, expect>())); + EXPECT_TRUE((std::is_convertible&, expect>())); + EXPECT_TRUE((std::is_convertible const&, expect>())); + EXPECT_TRUE(!(std::is_convertible, expect>())); + EXPECT_TRUE(!(std::is_convertible&&, expect>())); + EXPECT_TRUE(!(std::is_convertible&, expect>())); + EXPECT_TRUE(!(std::is_convertible const&, expect>())); + + EXPECT_TRUE((std::is_constructible, int>())); + EXPECT_TRUE((std::is_constructible, int&&>())); + EXPECT_TRUE((std::is_constructible, int&>())); + EXPECT_TRUE((std::is_constructible, int const&>())); + EXPECT_TRUE((std::is_constructible, expect>())); + EXPECT_TRUE((std::is_constructible, expect&&>())); + EXPECT_TRUE((std::is_constructible, expect&>())); + EXPECT_TRUE((std::is_constructible, expect const&>())); + EXPECT_TRUE((std::is_constructible, expect>())); + EXPECT_TRUE((std::is_constructible, expect&&>())); + EXPECT_TRUE((std::is_constructible, expect&>())); + EXPECT_TRUE((std::is_constructible, expect const&>())); + EXPECT_TRUE(!(std::is_constructible, expect>())); + EXPECT_TRUE(!(std::is_constructible, expect&&>())); + EXPECT_TRUE(!(std::is_constructible, expect&>())); + EXPECT_TRUE(!(std::is_constructible, expect const&>())); + + EXPECT_EQ(expect{expect{100}}.value(), 100); + + expect val1{std::string{}}; + expect val2{"foo"}; + + EXPECT_EQ(val1.value(), std::string{}); + EXPECT_EQ(val2.value(), std::string{"foo"}); + + const expect val3{val2}; + + EXPECT_EQ(val1.value(), std::string{}); + EXPECT_EQ(val2.value(), std::string{"foo"}); + EXPECT_EQ(val3.value(), std::string{"foo"}); + + val1 = val2; + + EXPECT_EQ(val1.value(), "foo"); + EXPECT_EQ(val2.value(), std::string{"foo"}); + EXPECT_EQ(val3.value(), "foo"); +} + +TEST(Expect, NoExcept) +{ + noexcept_bench(); + noexcept_bench(); + + EXPECT_TRUE(std::is_nothrow_constructible>()); + + EXPECT_TRUE((std::is_nothrow_constructible, int>())); + EXPECT_TRUE((std::is_nothrow_constructible, expect>())); + EXPECT_TRUE((std::is_nothrow_constructible, expect&&>())); + EXPECT_TRUE((std::is_nothrow_constructible, expect&>())); + EXPECT_TRUE((std::is_nothrow_constructible, expect const&>())); + + EXPECT_TRUE(noexcept(expect{std::declval&&>()})); + EXPECT_TRUE(noexcept(expect{std::declval const&>()})); + EXPECT_TRUE(noexcept(std::declval>().has_value())); + EXPECT_TRUE(noexcept(*std::declval>())); + EXPECT_TRUE(noexcept(std::declval>().equal(std::declval>()))); + EXPECT_TRUE(noexcept(std::declval>().equal(std::declval>()))); + EXPECT_TRUE(noexcept(std::declval>().equal(0))); + EXPECT_TRUE(noexcept(std::declval>() == std::declval>())); + EXPECT_TRUE(noexcept(std::declval>() == std::declval>())); + EXPECT_TRUE(noexcept(std::declval>() == 0)); + EXPECT_TRUE(noexcept(0 == std::declval>())); + EXPECT_TRUE(noexcept(std::declval>() != std::declval>())); + EXPECT_TRUE(noexcept(std::declval>() != std::declval>())); + EXPECT_TRUE(noexcept(std::declval>() != 0)); + EXPECT_TRUE(noexcept(0 != std::declval>())); + + EXPECT_TRUE((std::is_nothrow_constructible, std::error_code>())); + EXPECT_TRUE((std::is_nothrow_constructible, std::error_code&&>())); + EXPECT_TRUE((std::is_nothrow_constructible, std::error_code&>())); + EXPECT_TRUE((std::is_nothrow_constructible, std::error_code const&>())); + EXPECT_TRUE((std::is_nothrow_constructible, throw_construct>())); + EXPECT_TRUE((std::is_nothrow_constructible, throw_construct&&>())); + EXPECT_TRUE((std::is_nothrow_constructible, throw_construct&>())); + EXPECT_TRUE((std::is_nothrow_constructible, throw_construct const&>())); + EXPECT_TRUE(!(std::is_nothrow_constructible, expect>())); + EXPECT_TRUE(!(std::is_nothrow_constructible, expect&&>())); + EXPECT_TRUE(!(std::is_nothrow_constructible, expect&>())); + EXPECT_TRUE(!(std::is_nothrow_constructible, expect const&>())); + EXPECT_TRUE(std::is_nothrow_copy_constructible>()); + EXPECT_TRUE(std::is_nothrow_move_constructible>()); + EXPECT_TRUE(std::is_nothrow_copy_assignable>()); + EXPECT_TRUE(std::is_nothrow_move_assignable>()); + EXPECT_TRUE(std::is_nothrow_destructible>()); + + EXPECT_TRUE((std::is_nothrow_constructible, std::error_code>())); + EXPECT_TRUE((std::is_nothrow_constructible, std::error_code&&>())); + EXPECT_TRUE((std::is_nothrow_constructible, std::error_code&>())); + EXPECT_TRUE((std::is_nothrow_constructible, std::error_code const&>())); + EXPECT_TRUE((std::is_nothrow_constructible, throw_copies>())); + EXPECT_TRUE((std::is_nothrow_constructible, throw_copies&&>())); + EXPECT_TRUE(!(std::is_nothrow_constructible, throw_copies&>())); + EXPECT_TRUE(!(std::is_nothrow_constructible, throw_copies const&>())); + EXPECT_TRUE(!std::is_nothrow_copy_constructible>()); + EXPECT_TRUE(std::is_nothrow_move_constructible>()); + EXPECT_TRUE(!std::is_nothrow_copy_assignable>()); + EXPECT_TRUE(std::is_nothrow_move_assignable>()); + EXPECT_TRUE(std::is_nothrow_destructible>()); + EXPECT_TRUE(noexcept(std::declval>().equal(std::declval>()))); + EXPECT_TRUE(noexcept(std::declval>().equal(std::declval()))); + EXPECT_TRUE(noexcept(std::declval>() == std::declval>())); + EXPECT_TRUE(noexcept(std::declval>() == std::declval())); + EXPECT_TRUE(noexcept(std::declval() == std::declval>())); + EXPECT_TRUE(noexcept(std::declval>() != std::declval>())); + EXPECT_TRUE(noexcept(std::declval>() != std::declval())); + EXPECT_TRUE(noexcept(std::declval() != std::declval>())); + EXPECT_TRUE(noexcept(std::declval>().equal(std::declval>()))); + EXPECT_TRUE(noexcept(std::declval>().equal(std::declval()))); + EXPECT_TRUE(noexcept(std::declval>() == std::declval>())); + EXPECT_TRUE(noexcept(std::declval>() == std::declval())); + EXPECT_TRUE(noexcept(std::declval() == std::declval>())); + EXPECT_TRUE(noexcept(std::declval>() != std::declval>())); + EXPECT_TRUE(noexcept(std::declval>() != std::declval())); + EXPECT_TRUE(noexcept(std::declval() != std::declval>())); + + EXPECT_TRUE((std::is_nothrow_constructible, std::error_code>())); + EXPECT_TRUE((std::is_nothrow_constructible, std::error_code&&>())); + EXPECT_TRUE((std::is_nothrow_constructible, std::error_code&>())); + EXPECT_TRUE((std::is_nothrow_constructible, std::error_code const&>())); + EXPECT_TRUE(!(std::is_nothrow_constructible, throw_moves>())); + EXPECT_TRUE(!(std::is_nothrow_constructible, throw_moves&&>())); + EXPECT_TRUE(!(std::is_nothrow_constructible, throw_moves&>())); + EXPECT_TRUE(!(std::is_nothrow_constructible, throw_moves const&>())); + EXPECT_TRUE(std::is_nothrow_copy_constructible>()); + EXPECT_TRUE(!std::is_nothrow_move_constructible>()); + EXPECT_TRUE(std::is_nothrow_copy_assignable>()); + EXPECT_TRUE(!std::is_nothrow_move_assignable>()); + EXPECT_TRUE(std::is_nothrow_destructible>()); + EXPECT_TRUE(!noexcept(std::declval>().equal(std::declval>()))); + EXPECT_TRUE(!noexcept(std::declval>().equal(std::declval()))); + EXPECT_TRUE(!noexcept(std::declval>() == std::declval>())); + EXPECT_TRUE(!noexcept(std::declval>() == std::declval())); + EXPECT_TRUE(!noexcept(std::declval() == std::declval>())); + EXPECT_TRUE(!noexcept(std::declval>() != std::declval>())); + EXPECT_TRUE(!noexcept(std::declval>() != std::declval())); + EXPECT_TRUE(!noexcept(std::declval() != std::declval>())); + EXPECT_TRUE(!noexcept(std::declval>().equal(std::declval>()))); + EXPECT_TRUE(!noexcept(std::declval>().equal(std::declval()))); + EXPECT_TRUE(!noexcept(std::declval>() == std::declval>())); + EXPECT_TRUE(!noexcept(std::declval>() == std::declval())); + EXPECT_TRUE(!noexcept(std::declval() == std::declval>())); + EXPECT_TRUE(!noexcept(std::declval>() != std::declval>())); + EXPECT_TRUE(!noexcept(std::declval>() != std::declval())); + EXPECT_TRUE(!noexcept(std::declval() != std::declval>())); +} + +TEST(Expect, Trivial) +{ + EXPECT_TRUE(std::is_trivially_copy_constructible>()); + EXPECT_TRUE(std::is_trivially_move_constructible>()); + EXPECT_TRUE(std::is_trivially_destructible>()); +} + +TEST(Expect, Assignment) +{ + expect val1{std::string{}}; + expect val2{"foobar"}; + + ASSERT_TRUE(val1.has_value()); + ASSERT_TRUE(val2.has_value()); + EXPECT_TRUE(bool(val1)); + EXPECT_TRUE(bool(val2)); + EXPECT_TRUE(!val1.has_error()); + EXPECT_TRUE(!val2.has_error()); + EXPECT_EQ(val1.value(), std::string{}); + EXPECT_TRUE(*val1 == std::string{}); + EXPECT_TRUE(boost::equals(val1->c_str(), "")); + EXPECT_TRUE(val2.value() == "foobar"); + EXPECT_TRUE(*val2 == "foobar"); + EXPECT_TRUE(boost::equals(val2->c_str(), "foobar")); + EXPECT_EQ(val1.error(), std::error_code{}); + EXPECT_EQ(val2.error(), std::error_code{}); + EXPECT_TRUE(!val1.equal(std::error_code{})); + EXPECT_TRUE(!val2.equal(std::error_code{})); + EXPECT_TRUE(!(val1 == std::error_code{})); + EXPECT_TRUE(!(std::error_code{} == val1)); + EXPECT_TRUE(!(val2 == std::error_code{})); + EXPECT_TRUE(!(std::error_code{} == val2)); + EXPECT_TRUE(val1 != std::error_code{}); + EXPECT_TRUE(std::error_code{} != val1); + EXPECT_TRUE(val2 != std::error_code{}); + EXPECT_TRUE(std::error_code{} != val2); + EXPECT_TRUE(!val1.matches(std::error_condition{})); + EXPECT_TRUE(!val2.matches(std::error_condition{})); + + val1 = std::move(val2); + + ASSERT_TRUE(val1.has_value()); + ASSERT_TRUE(val2.has_value()); + EXPECT_TRUE(bool(val1)); + EXPECT_TRUE(bool(val2)); + EXPECT_TRUE(!val1.has_error()); + EXPECT_TRUE(!val2.has_error()); + EXPECT_EQ(val1.value(), "foobar"); + EXPECT_TRUE(*val1 == "foobar"); + EXPECT_TRUE(boost::equals(val1->c_str(), "foobar")); + EXPECT_EQ(val2.value(), std::string{}); + EXPECT_TRUE(*val2 == std::string{}); + EXPECT_TRUE(boost::equals(val2->c_str(), "")); + EXPECT_EQ(val1.error(), std::error_code{}); + EXPECT_EQ(val2.error(), std::error_code{}); + EXPECT_TRUE(!val1.equal(std::error_code{})); + EXPECT_TRUE(!val2.equal(std::error_code{})); + EXPECT_TRUE(!(val1 == std::error_code{})); + EXPECT_TRUE(!(std::error_code{} == val1)); + EXPECT_TRUE(!(val2 == std::error_code{})); + EXPECT_TRUE(!(std::error_code{} == val2)); + EXPECT_TRUE(val1 != std::error_code{}); + EXPECT_TRUE(std::error_code{} != val1); + EXPECT_TRUE(val2 != std::error_code{}); + EXPECT_TRUE(std::error_code{} != val2); + EXPECT_TRUE(!val1.matches(std::error_condition{})); + EXPECT_TRUE(!val2.matches(std::error_condition{})); + + val2 = val1; + + ASSERT_TRUE(val1.has_value()); + ASSERT_TRUE(val2.has_value()); + EXPECT_TRUE(bool(val1)); + EXPECT_TRUE(bool(val2)); + EXPECT_TRUE(!val1.has_error()); + EXPECT_TRUE(!val2.has_error()); + EXPECT_EQ(val1.value(), "foobar"); + EXPECT_TRUE(*val1 == "foobar"); + EXPECT_TRUE(boost::equals(val1->c_str(), "foobar")); + EXPECT_EQ(val2.value(), "foobar"); + EXPECT_TRUE(*val2 == "foobar"); + EXPECT_TRUE(boost::equals(val2->c_str(), "foobar")); + EXPECT_EQ(val1.error(), std::error_code{}); + EXPECT_EQ(val2.error(), std::error_code{}); + EXPECT_TRUE(!val1.equal(std::error_code{})); + EXPECT_TRUE(!val2.equal(std::error_code{})); + EXPECT_TRUE(!(val1 == std::error_code{})); + EXPECT_TRUE(!(std::error_code{} == val1)); + EXPECT_TRUE(!(val2 == std::error_code{})); + EXPECT_TRUE(!(std::error_code{} == val2)); + EXPECT_TRUE(val1 != std::error_code{}); + EXPECT_TRUE(std::error_code{} != val1); + EXPECT_TRUE(val2 != std::error_code{}); + EXPECT_TRUE(std::error_code{} != val2); + EXPECT_TRUE(!val1.matches(std::error_condition{})); + EXPECT_TRUE(!val2.matches(std::error_condition{})); + + val1 = make_error_code(common_error::kInvalidArgument); + + ASSERT_TRUE(val1.has_error()); + ASSERT_TRUE(val2.has_value()); + EXPECT_TRUE(!val1); + EXPECT_TRUE(bool(val2)); + EXPECT_TRUE(!val1.has_value()); + EXPECT_TRUE(!val2.has_error()); + EXPECT_EQ(val1.error(), common_error::kInvalidArgument); + EXPECT_TRUE(val1 == common_error::kInvalidArgument); + EXPECT_TRUE(common_error::kInvalidArgument == val1); + EXPECT_STREQ(val2.value().c_str(), "foobar"); + EXPECT_TRUE(*val2 == "foobar"); + EXPECT_TRUE(boost::equals(val2->c_str(), "foobar")); + EXPECT_NE(val1.error(), std::error_code{}); + EXPECT_EQ(val2.error(), std::error_code{}); + EXPECT_TRUE(val1.equal(common_error::kInvalidArgument)); + EXPECT_TRUE(!val2.equal(std::error_code{})); + EXPECT_TRUE(!(val1 == std::error_code{})); + EXPECT_TRUE(!(std::error_code{} == val1)); + EXPECT_TRUE(!(val2 == std::error_code{})); + EXPECT_TRUE(!(std::error_code{} == val2)); + EXPECT_TRUE(val1 != std::error_code{}); + EXPECT_TRUE(std::error_code{} != val1); + EXPECT_TRUE(val2 != std::error_code{}); + EXPECT_TRUE(std::error_code{} != val2); + EXPECT_TRUE(val1.matches(std::errc::invalid_argument)); + EXPECT_TRUE(!val1.matches(std::error_condition{})); + EXPECT_TRUE(!val2.matches(std::error_condition{})); + + val2 = val1; + + ASSERT_TRUE(val1.has_error()); + ASSERT_TRUE(val2.has_error()); + EXPECT_TRUE(!val1); + EXPECT_TRUE(!val2); + EXPECT_TRUE(!val1.has_value()); + EXPECT_TRUE(!val2.has_value()); + EXPECT_EQ(val1.error(), common_error::kInvalidArgument); + EXPECT_TRUE(val1 == common_error::kInvalidArgument); + EXPECT_TRUE(common_error::kInvalidArgument == val1); + EXPECT_EQ(val2.error(), common_error::kInvalidArgument); + EXPECT_TRUE(val2 == common_error::kInvalidArgument); + EXPECT_TRUE(common_error::kInvalidArgument == val2); + EXPECT_NE(val1.error(), std::error_code{}); + EXPECT_NE(val2.error(), std::error_code{}); + EXPECT_TRUE(val1.equal(common_error::kInvalidArgument)); + EXPECT_TRUE(val2.equal(common_error::kInvalidArgument)); + EXPECT_TRUE(!(val1 == std::error_code{})); + EXPECT_TRUE(!(std::error_code{} == val1)); + EXPECT_TRUE(!(val2 == std::error_code{})); + EXPECT_TRUE(!(std::error_code{} == val2)); + EXPECT_TRUE(val1 != std::error_code{}); + EXPECT_TRUE(std::error_code{} != val1); + EXPECT_TRUE(val2 != std::error_code{}); + EXPECT_TRUE(std::error_code{} != val2); + EXPECT_TRUE(val1.matches(std::errc::invalid_argument)); + EXPECT_TRUE(val2.matches(std::errc::invalid_argument)); + EXPECT_TRUE(!val1.matches(std::error_condition{})); + EXPECT_TRUE(!val2.matches(std::error_condition{})); + + val1 = std::string{"barfoo"}; + + ASSERT_TRUE(val1.has_value()); + ASSERT_TRUE(val2.has_error()); + EXPECT_TRUE(bool(val1)); + EXPECT_TRUE(!val2); + EXPECT_TRUE(!val1.has_error()); + EXPECT_TRUE(!val2.has_value()); + EXPECT_STREQ(val1.value().c_str(), "barfoo"); + EXPECT_TRUE(*val1 == "barfoo"); + EXPECT_TRUE(boost::equals(val1->c_str(), "barfoo")); + EXPECT_EQ(val2.error(), common_error::kInvalidArgument); + EXPECT_TRUE(val2 == common_error::kInvalidArgument); + EXPECT_TRUE(common_error::kInvalidArgument == val2); + EXPECT_EQ(val1.error(), std::error_code{}); + EXPECT_NE(val2.error(), std::error_code{}); + EXPECT_TRUE(!val1.equal(std::error_code{})); + EXPECT_TRUE(val2.equal(common_error::kInvalidArgument)); + EXPECT_TRUE(!(val1 == std::error_code{})); + EXPECT_TRUE(!(std::error_code{} == val1)); + EXPECT_TRUE(!(val2 == std::error_code{})); + EXPECT_TRUE(!(std::error_code{} == val2)); + EXPECT_TRUE(val1 != std::error_code{}); + EXPECT_TRUE(std::error_code{} != val1); + EXPECT_TRUE(val2 != std::error_code{}); + EXPECT_TRUE(std::error_code{} != val2); + EXPECT_TRUE(val2.matches(std::errc::invalid_argument)); + EXPECT_TRUE(!val1.matches(std::error_condition{})); + EXPECT_TRUE(!val2.matches(std::error_condition{})); + + val2 = val1; + + ASSERT_TRUE(val1.has_value()); + ASSERT_TRUE(val2.has_value()); + EXPECT_TRUE(bool(val1)); + EXPECT_TRUE(bool(val2)); + EXPECT_TRUE(!val1.has_error()); + EXPECT_TRUE(!val2.has_error()); + EXPECT_EQ(val1.value(), "barfoo"); + EXPECT_TRUE(*val1 == "barfoo"); + EXPECT_TRUE(boost::equals(val1->c_str(), "barfoo")); + EXPECT_EQ(val2.value(), "barfoo"); + EXPECT_TRUE(*val2 == "barfoo"); + EXPECT_TRUE(boost::equals(val2->c_str(), "barfoo")); + EXPECT_EQ(val1.error(), std::error_code{}); + EXPECT_EQ(val2.error(), std::error_code{}); + EXPECT_TRUE(!val1.equal(std::error_code{})); + EXPECT_TRUE(!val2.equal(std::error_code{})); + EXPECT_TRUE(!(val1 == std::error_code{})); + EXPECT_TRUE(!(std::error_code{} == val1)); + EXPECT_TRUE(!(val2 == std::error_code{})); + EXPECT_TRUE(!(std::error_code{} == val2)); + EXPECT_TRUE(val1 != std::error_code{}); + EXPECT_TRUE(std::error_code{} != val1); + EXPECT_TRUE(val2 != std::error_code{}); + EXPECT_TRUE(std::error_code{} != val2); + EXPECT_TRUE(!val1.matches(std::error_condition{})); + EXPECT_TRUE(!val2.matches(std::error_condition{})); +} + +TEST(Expect, AssignmentThrowsOnMove) +{ + struct construct_error {}; + struct assignment_error {}; + + struct throw_on_move { + std::string msg; + + throw_on_move(const char* msg) : msg(msg) {} + throw_on_move(throw_on_move&&) { + throw construct_error{}; + } + throw_on_move(throw_on_move const&) = default; + ~throw_on_move() = default; + throw_on_move& operator=(throw_on_move&&) { + throw assignment_error{}; + } + throw_on_move& operator=(throw_on_move const&) = default; + }; + + expect val1{expect{"foobar"}}; + expect val2{common_error::kInvalidArgument}; + + ASSERT_TRUE(val1.has_value()); + ASSERT_TRUE(val2.has_error()); + EXPECT_TRUE(!val1.has_error()); + EXPECT_TRUE(!val2.has_value()); + EXPECT_STREQ(val1->msg.c_str(), "foobar"); + EXPECT_EQ(val2.error(), common_error::kInvalidArgument); + + EXPECT_THROW(val2 = std::move(val1), construct_error); + + ASSERT_TRUE(val1.has_value()); + ASSERT_TRUE(val2.has_error()); + EXPECT_TRUE(!val1.has_error()); + EXPECT_TRUE(!val2.has_value()); + EXPECT_STREQ(val1->msg.c_str(), "foobar"); + EXPECT_EQ(val2.error(), common_error::kInvalidArgument); + + EXPECT_THROW(val1 = expect{"barfoo"}, assignment_error); + + ASSERT_TRUE(val1.has_value()); + ASSERT_TRUE(val2.has_error()); + EXPECT_TRUE(!val1.has_error()); + EXPECT_TRUE(!val2.has_value()); + EXPECT_STREQ(val1->msg.c_str(), "foobar"); + EXPECT_EQ(val2.error(), common_error::kInvalidArgument); + + EXPECT_NO_THROW(val2 = val1); + + ASSERT_TRUE(val1.has_value()); + ASSERT_TRUE(val2.has_value()); + EXPECT_TRUE(!val1.has_error()); + EXPECT_TRUE(!val2.has_error()); + EXPECT_STREQ(val1->msg.c_str(), "foobar"); + EXPECT_STREQ(val2->msg.c_str(), "foobar"); +} + +TEST(Expect, EqualWithStrings) +{ + expect val1{std::string{}}; + expect val2{"barfoo"}; + expect val3{boost::string_ref{}}; + + EXPECT_TRUE(!val1.equal(val2)); + EXPECT_TRUE(val1.equal(val3)); + EXPECT_TRUE(!val2.equal(val1)); + EXPECT_TRUE(!val2.equal(val3)); + EXPECT_TRUE(val3.equal(val1)); + EXPECT_TRUE(!val3.equal(val2)); + EXPECT_TRUE(!(val1 == val2)); + EXPECT_TRUE(!(val2 == val1)); + EXPECT_TRUE(val1 == val3); + EXPECT_TRUE(val3 == val1); + EXPECT_TRUE(!(val2 == val3)); + EXPECT_TRUE(!(val3 == val2)); + EXPECT_TRUE(val1 != val2); + EXPECT_TRUE(val2 != val1); + EXPECT_TRUE(!(val1 != val3)); + EXPECT_TRUE(!(val3 != val1)); + EXPECT_TRUE(val2 != val3); + EXPECT_TRUE(val3 != val2); + + EXPECT_TRUE(val1.equal("")); + EXPECT_TRUE(val2.equal("barfoo")); + EXPECT_TRUE(val3.equal("")); + EXPECT_TRUE(!val1.equal(std::error_code{})); + EXPECT_TRUE(!val2.equal(std::error_code{})); + EXPECT_TRUE(!val3.equal(std::error_code{})); + EXPECT_TRUE(val1 == ""); + EXPECT_TRUE("" == val1); + EXPECT_TRUE(val2 == "barfoo"); + EXPECT_TRUE("barfoo" == val2); + EXPECT_TRUE(val3 == ""); + EXPECT_TRUE("" == val3); + EXPECT_TRUE(!(val1 != "")); + EXPECT_TRUE(!("" != val1)); + EXPECT_TRUE(!(val2 != "barfoo")); + EXPECT_TRUE(!("barfoo" != val2)); + EXPECT_TRUE(!(val3 != "")); + EXPECT_TRUE(!("" != val3)); + EXPECT_TRUE(!(val1 == std::error_code{})); + EXPECT_TRUE(!(std::error_code{} == val1)); + EXPECT_TRUE(!(val2 == std::error_code{})); + EXPECT_TRUE(!(std::error_code{} == val2)); + EXPECT_TRUE(!(val3 == std::error_code{})); + EXPECT_TRUE(!(std::error_code{} == val3)); + EXPECT_TRUE(val1 != std::error_code{}); + EXPECT_TRUE(std::error_code{} != val1); + EXPECT_TRUE(val2 != std::error_code{}); + EXPECT_TRUE(std::error_code{} != val2); + EXPECT_TRUE(val3 != std::error_code{}); + EXPECT_TRUE(std::error_code{} != val3); + EXPECT_TRUE(!val1.matches(std::error_condition{})); + EXPECT_TRUE(!val2.matches(std::error_condition{})); + EXPECT_TRUE(!val3.matches(std::error_condition{})); + + val2 = make_error_code(common_error::kInvalidArgument); + + EXPECT_TRUE(!val1.equal(val2)); + EXPECT_TRUE(val1.equal(val3)); + EXPECT_TRUE(!val2.equal(val1)); + EXPECT_TRUE(!val2.equal(val3)); + EXPECT_TRUE(val3.equal(val1)); + EXPECT_TRUE(!val3.equal(val2)); + EXPECT_TRUE(!(val1 == val2)); + EXPECT_TRUE(!(val2 == val1)); + EXPECT_TRUE(val1 == val3); + EXPECT_TRUE(val3 == val1); + EXPECT_TRUE(!(val2 == val3)); + EXPECT_TRUE(!(val3 == val2)); + EXPECT_TRUE(val1 != val2); + EXPECT_TRUE(val2 != val1); + EXPECT_TRUE(!(val1 != val3)); + EXPECT_TRUE(!(val3 != val1)); + EXPECT_TRUE(val2 != val3); + EXPECT_TRUE(val3 != val2); + + EXPECT_TRUE(!val1.equal(common_error::kInvalidArgument)); + EXPECT_TRUE(val2.equal(common_error::kInvalidArgument)); + EXPECT_TRUE(!val3.equal(common_error::kInvalidArgument)); + EXPECT_TRUE(val2 == common_error::kInvalidArgument); + EXPECT_TRUE(common_error::kInvalidArgument == val2); + EXPECT_TRUE(!(val2 != common_error::kInvalidArgument)); + EXPECT_TRUE(!(common_error::kInvalidArgument != val2)); + EXPECT_TRUE(val2.matches(std::errc::invalid_argument)); + EXPECT_TRUE(!val2.matches(std::error_condition{})); + + val1 = expect{"barfoo"}; + + EXPECT_TRUE(!val1.equal(val2)); + EXPECT_TRUE(!val1.equal(val3)); + EXPECT_TRUE(!val2.equal(val1)); + EXPECT_TRUE(!val2.equal(val3)); + EXPECT_TRUE(!val3.equal(val1)); + EXPECT_TRUE(!val3.equal(val2)); + EXPECT_TRUE(!(val1 == val2)); + EXPECT_TRUE(!(val2 == val1)); + EXPECT_TRUE(!(val1 == val3)); + EXPECT_TRUE(!(val3 == val1)); + EXPECT_TRUE(!(val2 == val3)); + EXPECT_TRUE(!(val3 == val2)); + EXPECT_TRUE(val1 != val2); + EXPECT_TRUE(val2 != val1); + EXPECT_TRUE(val1 != val3); + EXPECT_TRUE(val3 != val1); + EXPECT_TRUE(val2 != val3); + EXPECT_TRUE(val3 != val2); + + EXPECT_TRUE(val1.equal("barfoo")); + EXPECT_TRUE(val1 == "barfoo"); + EXPECT_TRUE("barfoo" == val1); + EXPECT_TRUE(!(val1 != "barfoo")); + EXPECT_TRUE(!("barfoo" != val1)); + EXPECT_TRUE(!val1.equal(common_error::kInvalidArgument)); + EXPECT_TRUE(!(val1 == common_error::kInvalidArgument)); + EXPECT_TRUE(!(common_error::kInvalidArgument == val1)); + EXPECT_TRUE(!(val1 == std::error_code{})); + EXPECT_TRUE(!(std::error_code{} == val1)); + EXPECT_TRUE(!val1.matches(std::error_condition{})); + EXPECT_TRUE(!val1.matches(std::errc::invalid_argument)); +} + +TEST(Expect, EqualWithVoid) +{ + const expect val1; + expect val2; + + EXPECT_TRUE(val1.equal(val2)); + EXPECT_TRUE(val2.equal(val1)); + EXPECT_TRUE(!val1.equal(std::error_code{})); + EXPECT_TRUE(!val2.equal(std::error_code{})); + EXPECT_TRUE(val1 == val2); + EXPECT_TRUE(val2 == val1); + EXPECT_TRUE(!(val1 == std::error_code{})); + EXPECT_TRUE(!(std::error_code{} == val1)); + EXPECT_TRUE(!(val2 == std::error_code{})); + EXPECT_TRUE(!(std::error_code{} == val2)); + EXPECT_TRUE(!(val1 != val2)); + EXPECT_TRUE(!(val2 != val1)); + EXPECT_TRUE(val1 != std::error_code{}); + EXPECT_TRUE(std::error_code{} != val1); + EXPECT_TRUE(!(val2 == std::error_code{})); + EXPECT_TRUE(!(std::error_code{} == val2)); + + val2 = make_error_code(common_error::kInvalidArgument); + + EXPECT_TRUE(!val1.equal(val2)); + EXPECT_TRUE(!val2.equal(val1)); + EXPECT_TRUE(!val1.equal(common_error::kInvalidArgument)); + EXPECT_TRUE(val2.equal(common_error::kInvalidArgument)); + EXPECT_TRUE(!val2.equal(std::error_code{})); + EXPECT_TRUE(!(val1 == val2)); + EXPECT_TRUE(!(val2 == val1)); + EXPECT_TRUE(val2 == common_error::kInvalidArgument); + EXPECT_TRUE(common_error::kInvalidArgument == val2); + EXPECT_TRUE(!(val2 == std::error_code{})); + EXPECT_TRUE(!(std::error_code{} == val2)); + EXPECT_TRUE(val1 != val2); + EXPECT_TRUE(val2 != val1); + EXPECT_TRUE(!(val2 != common_error::kInvalidArgument)); + EXPECT_TRUE(!(common_error::kInvalidArgument != val2)); + EXPECT_TRUE(val2 != std::error_code{}); + EXPECT_TRUE(std::error_code{} != val2); +} + +TEST(Expect, EqualNoCopies) +{ + struct copy_error {}; + + struct throw_on_copy { + throw_on_copy() = default; + throw_on_copy(int) noexcept {} + throw_on_copy(throw_on_copy const&) { + throw copy_error{}; + } + ~throw_on_copy() = default; + throw_on_copy& operator=(throw_on_copy const&) { + throw copy_error{}; + } + + bool operator==(throw_on_copy const&) const noexcept { return true; } + }; + + expect val1{expect{0}}; + expect val2{expect{0}}; + + EXPECT_TRUE(val1.equal(val2)); + EXPECT_TRUE(val2.equal(val1)); + EXPECT_TRUE(val1 == val2); + EXPECT_TRUE(val2 == val1); + EXPECT_TRUE(!(val1 != val2)); + EXPECT_TRUE(!(val2 != val1)); + + EXPECT_TRUE(val1.equal(throw_on_copy{})); + EXPECT_TRUE(val1 == throw_on_copy{}); + EXPECT_TRUE(throw_on_copy{} == val1); + EXPECT_TRUE(!(val1 != throw_on_copy{})); + EXPECT_TRUE(!(throw_on_copy{} != val1)); + + throw_on_copy val3; + + EXPECT_TRUE(val1.equal(val3)); + EXPECT_TRUE(val1 == val3); + EXPECT_TRUE(val3 == val1); + EXPECT_TRUE(!(val1 != val3)); + EXPECT_TRUE(!(val3 != val1)); + + expect val4{common_error::kInvalidArgument}; + + EXPECT_TRUE(!val4.equal(throw_on_copy{})); + EXPECT_TRUE(!(val4 == throw_on_copy{})); + EXPECT_TRUE(!(throw_on_copy{} == val4)); + EXPECT_TRUE(val4 != throw_on_copy{}); + EXPECT_TRUE(throw_on_copy{} != val4); + EXPECT_TRUE(!val4.equal(val3)); + EXPECT_TRUE(!(val4 == val3)); + EXPECT_TRUE(!(val3 == val4)); + EXPECT_TRUE(val4 != val3); + EXPECT_TRUE(val3 != val4); +} + +TEST(Expect, Macros) { + EXPECT_TRUE( + [] () -> ::common_error { + MONERO_PRECOND(true); + return {common_error::kInvalidErrorCode}; + } () == common_error::kInvalidErrorCode + ); + EXPECT_TRUE( + [] () -> ::common_error { + MONERO_PRECOND(false); + return {common_error::kInvalidErrorCode}; + } () == common_error::kInvalidArgument + ); + EXPECT_TRUE( + [] () -> std::error_code { + MONERO_PRECOND(true); + return {common_error::kInvalidErrorCode}; + } () == common_error::kInvalidErrorCode + ); + EXPECT_TRUE( + [] () -> std::error_code { + MONERO_PRECOND(false); + return {common_error::kInvalidErrorCode}; + } () == common_error::kInvalidArgument + ); + EXPECT_TRUE( + [] () -> expect { + MONERO_PRECOND(true); + return {common_error::kInvalidErrorCode}; + } () == common_error::kInvalidErrorCode + ); + EXPECT_TRUE( + [] () -> expect { + MONERO_PRECOND(false); + return {common_error::kInvalidErrorCode}; + } () == common_error::kInvalidArgument + ); + EXPECT_TRUE( + [] () -> expect { + MONERO_PRECOND(true); + return {common_error::kInvalidErrorCode}; + } () == common_error::kInvalidErrorCode + ); + EXPECT_TRUE( + [] () -> expect { + MONERO_PRECOND(false); + return {common_error::kInvalidErrorCode}; + } () == common_error::kInvalidArgument + ); + + EXPECT_TRUE( + [] () -> std::error_code { + MONERO_CHECK(expect{}); + return {common_error::kInvalidErrorCode}; + } () == common_error::kInvalidErrorCode + ); + EXPECT_TRUE( + [] () -> std::error_code { + MONERO_CHECK(expect{common_error::kInvalidArgument}); + return {common_error::kInvalidErrorCode}; + } () == common_error::kInvalidArgument + ); + EXPECT_TRUE( + [] () -> expect { + MONERO_CHECK(expect{}); + return {common_error::kInvalidErrorCode}; + } () == common_error::kInvalidErrorCode + ); + EXPECT_TRUE( + [] () -> expect { + MONERO_CHECK(expect{common_error::kInvalidArgument}); + return {common_error::kInvalidErrorCode}; + } () == common_error::kInvalidArgument + ); + EXPECT_TRUE( + [] () -> expect { + MONERO_CHECK(expect{}); + return {common_error::kInvalidErrorCode}; + } () == common_error::kInvalidErrorCode + ); + EXPECT_TRUE( + [] () -> expect { + MONERO_CHECK(expect{common_error::kInvalidArgument}); + return {common_error::kInvalidErrorCode}; + } () == common_error::kInvalidArgument + ); + + EXPECT_NO_THROW(MONERO_UNWRAP(success())); + EXPECT_NO_THROW(MONERO_UNWRAP(expect{})); + EXPECT_NO_THROW(MONERO_UNWRAP(expect{0})); + EXPECT_THROW( + MONERO_UNWRAP(expect{common_error::kInvalidArgument}), std::system_error + ); + EXPECT_THROW( + MONERO_UNWRAP(expect{common_error::kInvalidArgument}), std::system_error + ); +} + From cd236aebca4251ef46cb0920e1f96ff018267829 Mon Sep 17 00:00:00 2001 From: cslashm Date: Mon, 30 Jul 2018 10:16:13 +0200 Subject: [PATCH 0398/1404] Add bulletproof support --- src/device/device_ledger.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/device/device_ledger.cpp b/src/device/device_ledger.cpp index 658b379e43f..9b5ea0fd751 100644 --- a/src/device/device_ledger.cpp +++ b/src/device/device_ledger.cpp @@ -1354,7 +1354,7 @@ namespace hw { this->exchange(); //pseudoOuts - if ((type == rct::RCTTypeSimple) || (type == rct::RCTTypeBulletproof)) { + if (type == rct::RCTTypeSimple) { for ( i = 0; i < inputs_size; i++) { offset = set_command_header(INS_VALIDATE, 0x01, i+2); //options From 1660b0e72c3facac887ab4ccc38a4c87851364c2 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 13 Sep 2018 09:01:05 +0000 Subject: [PATCH 0399/1404] tx_pool: make the max tx size a consensus rule from v8 --- src/cryptonote_core/tx_pool.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index b12a329bb36..a725eac6e0d 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -185,7 +185,7 @@ namespace cryptonote } size_t tx_weight_limit = get_transaction_weight_limit(version); - if (!kept_by_block && tx_weight > tx_weight_limit) + if ((!kept_by_block || version >= HF_VERSION_PER_BYTE_FEE) && tx_weight > tx_weight_limit) { LOG_PRINT_L1("transaction is too heavy: " << tx_weight << " bytes, maximum weight: " << tx_weight_limit); tvc.m_verifivation_failed = true; From 4f81cd3a3ce75a3a125ae7cb99b17683fd7e809f Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 13 Sep 2018 12:41:36 +0000 Subject: [PATCH 0400/1404] rpc: add cumulative difficulty in block header data --- src/rpc/core_rpc_server.cpp | 1 + src/rpc/core_rpc_server_commands_defs.h | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index 4383ad190a9..0e0f8d9fb50 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -1312,6 +1312,7 @@ namespace cryptonote response.depth = m_core.get_current_blockchain_height() - height - 1; response.hash = string_tools::pod_to_hex(hash); response.difficulty = m_core.get_blockchain_storage().block_difficulty(height); + response.cumulative_difficulty = response.block_weight = m_core.get_blockchain_storage().get_db().get_block_cumulative_difficulty(height); response.reward = get_block_reward(blk); response.block_size = response.block_weight = m_core.get_blockchain_storage().get_db().get_block_weight(height); response.num_txes = blk.tx_hashes.size(); diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index da4c8b6cfa6..8e2cec70600 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -1197,6 +1197,7 @@ namespace cryptonote uint64_t depth; std::string hash; difficulty_type difficulty; + difficulty_type cumulative_difficulty; uint64_t reward; uint64_t block_size; uint64_t block_weight; @@ -1214,6 +1215,7 @@ namespace cryptonote KV_SERIALIZE(depth) KV_SERIALIZE(hash) KV_SERIALIZE(difficulty) + KV_SERIALIZE(cumulative_difficulty) KV_SERIALIZE(reward) KV_SERIALIZE(block_size) KV_SERIALIZE_OPT(block_weight, (uint64_t)0) From b4679f37f40c45059dd65db90d52732b77f4488d Mon Sep 17 00:00:00 2001 From: cslashm Date: Mon, 20 Aug 2018 17:24:26 +0200 Subject: [PATCH 0401/1404] One build directory per branch/arch. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This proposal allows to perform multiple compilation from different branch/arch in separate directories. Example: build ├── GNU_Linux │   ├── multi-compilation │   │   └── release │   └── NanoS-USBHID │   └── release └── Msys └── NanoS-USBHID └── release Edit 1: Try to handle special char as : / \ . --data-dir in unit test not yet tested Edit 2: donot use param for uname. -o is not supported by MacOS. --- Makefile | 115 +++++++++++++++++++++++++++++++------------------------ 1 file changed, 66 insertions(+), 49 deletions(-) diff --git a/Makefile b/Makefile index 9f328434c5e..a0bf86c0e90 100644 --- a/Makefile +++ b/Makefile @@ -28,112 +28,129 @@ ANDROID_STANDALONE_TOOLCHAIN_PATH ?= /usr/local/toolchain +subbuilddir:=$(shell echo `uname | sed -e 's|[:/\\ \(\)]|_|g'`/`git branch | grep '\* ' | cut -f2- -d' '| sed -e 's|[:/\\ \(\)]|_|g'`) +ifeq ($(USE_SINGLE_BUILDDIR),) + builddir := build/"$(subbuilddir)" + topdir := ../../../.. + deldirs := $(builddir) +else + builddir := build + topdir := ../.. + deldirs := $(builddir)/debug $(builddir)/release $(builddir)/fuzz +endif + all: release-all cmake-debug: - mkdir -p build/debug - cd build/debug && cmake -D CMAKE_BUILD_TYPE=Debug ../.. + mkdir -p $(builddir)/debug + cd $(builddir)/debug && cmake -D CMAKE_BUILD_TYPE=Debug $(topdir) debug: cmake-debug - cd build/debug && $(MAKE) + cd $(builddir)/debug && $(MAKE) # Temporarily disable some tests: # * libwallet_api_tests fail (Issue #895) debug-test: - mkdir -p build/debug - cd build/debug && cmake -D BUILD_TESTS=ON -D CMAKE_BUILD_TYPE=Debug ../.. && $(MAKE) && $(MAKE) ARGS="-E libwallet_api_tests" test + mkdir -p $(builddir)/debug + cd $(builddir)/debug && cmake -D BUILD_TESTS=ON -D CMAKE_BUILD_TYPE=Debug $(topdir) && $(MAKE) && $(MAKE) ARGS="-E libwallet_api_tests" test debug-all: - mkdir -p build/debug - cd build/debug && cmake -D BUILD_TESTS=ON -D BUILD_SHARED_LIBS=OFF -D CMAKE_BUILD_TYPE=Debug ../.. && $(MAKE) + mkdir -p $(builddir)/debug + cd $(builddir)/debug && cmake -D BUILD_TESTS=ON -D BUILD_SHARED_LIBS=OFF -D CMAKE_BUILD_TYPE=Debug $(topdir) && $(MAKE) debug-static-all: - mkdir -p build/debug - cd build/debug && cmake -D BUILD_TESTS=ON -D STATIC=ON -D CMAKE_BUILD_TYPE=Debug ../.. && $(MAKE) + mkdir -p $(builddir)/debug + cd $(builddir)/debug && cmake -D BUILD_TESTS=ON -D STATIC=ON -D CMAKE_BUILD_TYPE=Debug $(topdir) && $(MAKE) debug-static-win64: - mkdir -p build/debug - cd build/debug && cmake -G "MSYS Makefiles" -D STATIC=ON -D ARCH="x86-64" -D BUILD_64=ON -D CMAKE_BUILD_TYPE=Debug -D BUILD_TAG="win-x64" -D CMAKE_TOOLCHAIN_FILE=../../cmake/64-bit-toolchain.cmake -D MSYS2_FOLDER=c:/msys64 ../.. && $(MAKE) + mkdir -p $(builddir)/debug + cd $(builddir)/debug && cmake -G "MSYS Makefiles" -D STATIC=ON -D ARCH="x86-64" -D BUILD_64=ON -D CMAKE_BUILD_TYPE=Debug -D BUILD_TAG="win-x64" -D CMAKE_TOOLCHAIN_FILE=$(topdir)/cmake/64-bit-toolchain.cmake -D MSYS2_FOLDER=c:/msys64 $(topdir) && $(MAKE) debug-static-win32: - mkdir -p build/debug - cd build/debug && cmake -G "MSYS Makefiles" -D STATIC=ON -D ARCH="i686" -D BUILD_64=OFF -D CMAKE_BUILD_TYPE=Debug -D BUILD_TAG="win-x32" -D CMAKE_TOOLCHAIN_FILE=../../cmake/32-bit-toolchain.cmake -D MSYS2_FOLDER=c:/msys32 ../.. && $(MAKE) + mkdir -p $(builddir)/debug + cd $(builddir)/debug && cmake -G "MSYS Makefiles" -D STATIC=ON -D ARCH="i686" -D BUILD_64=OFF -D CMAKE_BUILD_TYPE=Debug -D BUILD_TAG="win-x32" -D CMAKE_TOOLCHAIN_FILE=$(topdir)/cmake/32-bit-toolchain.cmake -D MSYS2_FOLDER=c:/msys32 $(topdir) && $(MAKE) cmake-release: - mkdir -p build/release - cd build/release && cmake -D CMAKE_BUILD_TYPE=Release ../.. + mkdir -p $(builddir)/release + cd $(builddir)/release && cmake -D CMAKE_BUILD_TYPE=Release $(topdir) release: cmake-release - cd build/release && $(MAKE) + cd $(builddir)/release && $(MAKE) release-test: - mkdir -p build/release - cd build/release && cmake -D BUILD_TESTS=ON -D CMAKE_BUILD_TYPE=release ../.. && $(MAKE) && $(MAKE) test + mkdir -p $(builddir)/release + cd $(builddir)/release && cmake -D BUILD_TESTS=ON -D CMAKE_BUILD_TYPE=release $(topdir) && $(MAKE) && $(MAKE) test release-all: - mkdir -p build/release - cd build/release && cmake -D BUILD_TESTS=ON -D CMAKE_BUILD_TYPE=release ../.. && $(MAKE) + mkdir -p $(builddir)/release + cd $(builddir)/release && cmake -D BUILD_TESTS=ON -D CMAKE_BUILD_TYPE=release $(topdir) && $(MAKE) release-static: - mkdir -p build/release - cd build/release && cmake -D STATIC=ON -D ARCH="x86-64" -D BUILD_64=ON -D CMAKE_BUILD_TYPE=release ../.. && $(MAKE) + mkdir -p $(builddir)/release + cd $(builddir)/release && cmake -D STATIC=ON -D ARCH="x86-64" -D BUILD_64=ON -D CMAKE_BUILD_TYPE=release $(topdir) && $(MAKE) coverage: - mkdir -p build/debug - cd build/debug && cmake -D BUILD_TESTS=ON -D CMAKE_BUILD_TYPE=Debug -D COVERAGE=ON ../.. && $(MAKE) && $(MAKE) test + mkdir -p $(builddir)/debug + cd $(builddir)/debug && cmake -D BUILD_TESTS=ON -D CMAKE_BUILD_TYPE=Debug -D COVERAGE=ON $(topdir) && $(MAKE) && $(MAKE) test # Targets for specific prebuilt builds which will be advertised for updates by their build tag release-static-linux-armv6: - mkdir -p build/release - cd build/release && cmake -D BUILD_TESTS=OFF -D ARCH="armv6zk" -D STATIC=ON -D BUILD_64=OFF -D CMAKE_BUILD_TYPE=release -D BUILD_TAG="linux-armv6" ../.. && $(MAKE) + mkdir -p $(builddir)/release + cd $(builddir)/release && cmake -D BUILD_TESTS=OFF -D ARCH="armv6zk" -D STATIC=ON -D BUILD_64=OFF -D CMAKE_BUILD_TYPE=release -D BUILD_TAG="linux-armv6" $(topdir) && $(MAKE) release-static-linux-armv7: - mkdir -p build/release - cd build/release && cmake -D BUILD_TESTS=OFF -D ARCH="armv7-a" -D STATIC=ON -D BUILD_64=OFF -D CMAKE_BUILD_TYPE=release -D BUILD_TAG="linux-armv7" ../.. && $(MAKE) + mkdir -p $(builddir)/release + cd $(builddir)/release && cmake -D BUILD_TESTS=OFF -D ARCH="armv7-a" -D STATIC=ON -D BUILD_64=OFF -D CMAKE_BUILD_TYPE=release -D BUILD_TAG="linux-armv7" $(topdir) && $(MAKE) release-static-android: - mkdir -p build/release/translations - cd build/release/translations && cmake ../../../translations && $(MAKE) - cd build/release && CC=arm-linux-androideabi-clang CXX=arm-linux-androideabi-clang++ cmake -D BUILD_TESTS=OFF -D ARCH="armv7-a" -D STATIC=ON -D BUILD_64=OFF -D CMAKE_BUILD_TYPE=release -D ANDROID=true -D INSTALL_VENDORED_LIBUNBOUND=ON -D BUILD_TAG="android" -D CMAKE_SYSTEM_NAME="Android" -D CMAKE_ANDROID_STANDALONE_TOOLCHAIN="${ANDROID_STANDALONE_TOOLCHAIN_PATH}" -D CMAKE_ANDROID_ARM_MODE=ON -D CMAKE_ANDROID_ARCH_ABI="armeabi-v7a" ../.. && $(MAKE) + mkdir -p $(builddir)/release/translations + cd $(builddir)/release/translations && cmake ../../../translations && $(MAKE) + cd $(builddir)/release && CC=arm-linux-androideabi-clang CXX=arm-linux-androideabi-clang++ cmake -D BUILD_TESTS=OFF -D ARCH="armv7-a" -D STATIC=ON -D BUILD_64=OFF -D CMAKE_BUILD_TYPE=release -D ANDROID=true -D INSTALL_VENDORED_LIBUNBOUND=ON -D BUILD_TAG="android" -D CMAKE_SYSTEM_NAME="Android" -D CMAKE_ANDROID_STANDALONE_TOOLCHAIN="${ANDROID_STANDALONE_TOOLCHAIN_PATH}" -D CMAKE_ANDROID_ARM_MODE=ON -D CMAKE_ANDROID_ARCH_ABI="armeabi-v7a" ../.. && $(MAKE) release-static-linux-armv8: - mkdir -p build/release - cd build/release && cmake -D BUILD_TESTS=OFF -D ARCH="armv8-a" -D STATIC=ON -D BUILD_64=ON -D CMAKE_BUILD_TYPE=release -D BUILD_TAG="linux-armv8" ../.. && $(MAKE) + mkdir -p $(builddir)/release + cd $(builddir)/release && cmake -D BUILD_TESTS=OFF -D ARCH="armv8-a" -D STATIC=ON -D BUILD_64=ON -D CMAKE_BUILD_TYPE=release -D BUILD_TAG="linux-armv8" $(topdir) && $(MAKE) release-static-linux-x86_64: - mkdir -p build/release - cd build/release && cmake -D STATIC=ON -D ARCH="x86-64" -D BUILD_64=ON -D CMAKE_BUILD_TYPE=release -D BUILD_TAG="linux-x64" ../.. && $(MAKE) + mkdir -p $(builddir)/release + cd $(builddir)/release && cmake -D STATIC=ON -D ARCH="x86-64" -D BUILD_64=ON -D CMAKE_BUILD_TYPE=release -D BUILD_TAG="linux-x64" $(topdir) && $(MAKE) release-static-freebsd-x86_64: - mkdir -p build/release - cd build/release && cmake -D STATIC=ON -D ARCH="x86-64" -D BUILD_64=ON -D CMAKE_BUILD_TYPE=release -D BUILD_TAG="freebsd-x64" ../.. && $(MAKE) + mkdir -p $(builddir)/release + cd $(builddir)/release && cmake -D STATIC=ON -D ARCH="x86-64" -D BUILD_64=ON -D CMAKE_BUILD_TYPE=release -D BUILD_TAG="freebsd-x64" $(topdir) && $(MAKE) release-static-mac-x86_64: - mkdir -p build/release - cd build/release && cmake -D STATIC=ON -D ARCH="x86-64" -D BUILD_64=ON -D CMAKE_BUILD_TYPE=release -D BUILD_TAG="mac-x64" ../.. && $(MAKE) + mkdir -p $(builddir)/release + cd $(builddir)/release && cmake -D STATIC=ON -D ARCH="x86-64" -D BUILD_64=ON -D CMAKE_BUILD_TYPE=release -D BUILD_TAG="mac-x64" $(topdir) && $(MAKE) release-static-linux-i686: - mkdir -p build/release - cd build/release && cmake -D STATIC=ON -D ARCH="i686" -D BUILD_64=OFF -D CMAKE_BUILD_TYPE=release -D BUILD_TAG="linux-x86" ../.. && $(MAKE) + mkdir -p $(builddir)/release + cd $(builddir)/release && cmake -D STATIC=ON -D ARCH="i686" -D BUILD_64=OFF -D CMAKE_BUILD_TYPE=release -D BUILD_TAG="linux-x86" $(topdir) && $(MAKE) release-static-win64: - mkdir -p build/release - cd build/release && cmake -G "MSYS Makefiles" -D STATIC=ON -D ARCH="x86-64" -D BUILD_64=ON -D CMAKE_BUILD_TYPE=Release -D BUILD_TAG="win-x64" -D CMAKE_TOOLCHAIN_FILE=../../cmake/64-bit-toolchain.cmake -D MSYS2_FOLDER=c:/msys64 ../.. && $(MAKE) + mkdir -p $(builddir)/release + cd $(builddir)/release && cmake -G "MSYS Makefiles" -D STATIC=ON -D ARCH="x86-64" -D BUILD_64=ON -D CMAKE_BUILD_TYPE=Release -D BUILD_TAG="win-x64" -D CMAKE_TOOLCHAIN_FILE=$(topdir)/cmake/64-bit-toolchain.cmake -D MSYS2_FOLDER=c:/msys64 $(topdir) && $(MAKE) release-static-win32: - mkdir -p build/release - cd build/release && cmake -G "MSYS Makefiles" -D STATIC=ON -D ARCH="i686" -D BUILD_64=OFF -D CMAKE_BUILD_TYPE=Release -D BUILD_TAG="win-x32" -D CMAKE_TOOLCHAIN_FILE=../../cmake/32-bit-toolchain.cmake -D MSYS2_FOLDER=c:/msys32 ../.. && $(MAKE) + mkdir -p $(builddir)/release + cd $(builddir)/release && cmake -G "MSYS Makefiles" -D STATIC=ON -D ARCH="i686" -D BUILD_64=OFF -D CMAKE_BUILD_TYPE=Release -D BUILD_TAG="win-x32" -D CMAKE_TOOLCHAIN_FILE=$(topdir)/cmake/32-bit-toolchain.cmake -D MSYS2_FOLDER=c:/msys32 $(topdir) && $(MAKE) fuzz: - mkdir -p build/fuzz - cd build/fuzz && cmake -D STATIC=ON -D SANITIZE=ON -D BUILD_TESTS=ON -D USE_LTO=OFF -D CMAKE_C_COMPILER=afl-gcc -D CMAKE_CXX_COMPILER=afl-g++ -D ARCH="x86-64" -D CMAKE_BUILD_TYPE=fuzz -D BUILD_TAG="linux-x64" ../.. && $(MAKE) + mkdir -p $(builddir)/fuzz + cd $(builddir)/fuzz && cmake -D STATIC=ON -D SANITIZE=ON -D BUILD_TESTS=ON -D USE_LTO=OFF -D CMAKE_C_COMPILER=afl-gcc -D CMAKE_CXX_COMPILER=afl-g++ -D ARCH="x86-64" -D CMAKE_BUILD_TYPE=fuzz -D BUILD_TAG="linux-x64" $(topdir) && $(MAKE) clean: + @echo "WARNING: Back-up your wallet if it exists within ./"$(deldirs)"!" ; \ + read -r -p "This will destroy the build directory, continue (y/N)?: " CONTINUE; \ + [ $$CONTINUE = "y" ] || [ $$CONTINUE = "Y" ] || (echo "Exiting."; exit 1;) + rm -rf $(deldirs) + +clean-all: @echo "WARNING: Back-up your wallet if it exists within ./build!" ; \ - read -r -p "This will destroy the build directory, continue (y/N)?: " CONTINUE; \ + read -r -p "This will destroy all build directories, continue (y/N)?: " CONTINUE; \ [ $$CONTINUE = "y" ] || [ $$CONTINUE = "Y" ] || (echo "Exiting."; exit 1;) - rm -rf build + rm -rf ./build tags: ctags -R --sort=1 --c++-kinds=+p --fields=+iaS --extra=+q --language-force=C++ src contrib tests/gtest From 2ace509a7802e286e98e931f57e676f58ff8a2e0 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 14 Sep 2018 08:26:05 +0000 Subject: [PATCH 0402/1404] simplewallet: print reason why refresh+password is needed --- src/simplewallet/simplewallet.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index bcdf2a43f2f..26a235dc2af 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -4166,7 +4166,7 @@ boost::optional simple_wallet::on_get_password(const char // can't ask for password from a background thread if (!m_in_manual_refresh.load(std::memory_order_relaxed)) { - message_writer(console_color_red, false) << tr("Password needed - use the refresh command"); + message_writer(console_color_red, false) << boost::format(tr("Password needed (%s) - use the refresh command")) % reason; m_cmd_binder.print_prompt(); return boost::none; } From 4d52ec0ca473fb9ca0868be1afb4c23d4aa06e9b Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 17 Jun 2018 16:58:42 +0100 Subject: [PATCH 0403/1404] mlog: do not display http errors by default They're controllable by potential attackers and would just spam --- contrib/epee/src/mlog.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/epee/src/mlog.cpp b/contrib/epee/src/mlog.cpp index 818fc0a69aa..cd9867ff57c 100644 --- a/contrib/epee/src/mlog.cpp +++ b/contrib/epee/src/mlog.cpp @@ -97,7 +97,7 @@ static const char *get_default_categories(int level) switch (level) { case 0: - categories = "*:WARNING,net:FATAL,net.p2p:FATAL,net.cn:FATAL,global:INFO,verify:FATAL,stacktrace:INFO,logging:INFO,msgwriter:INFO"; + categories = "*:WARNING,net:FATAL,net.http:FATAL,net.p2p:FATAL,net.cn:FATAL,global:INFO,verify:FATAL,stacktrace:INFO,logging:INFO,msgwriter:INFO"; break; case 1: categories = "*:INFO,global:INFO,stacktrace:INFO,logging:INFO,msgwriter:INFO"; From ac9346637a353851610f028239b2df7762341d35 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 18 Jun 2018 15:31:34 +0100 Subject: [PATCH 0404/1404] rpc: add a "is an update available" flag in get_info Make it easier for a user to be told when to update --- src/cryptonote_core/cryptonote_core.cpp | 7 ++++++- src/cryptonote_core/cryptonote_core.h | 12 ++++++++++++ src/rpc/core_rpc_server.cpp | 2 ++ src/rpc/core_rpc_server_commands_defs.h | 4 +++- 4 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index c4eaa0cc443..c73e7476ab7 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -181,7 +181,8 @@ namespace cryptonote m_last_json_checkpoints_update(0), m_disable_dns_checkpoints(false), m_update_download(0), - m_nettype(UNDEFINED) + m_nettype(UNDEFINED), + m_update_available(false) { m_checkpoints_updating.clear(); set_cryptonote_protocol(pprotocol); @@ -1551,10 +1552,14 @@ namespace cryptonote return false; if (tools::vercmp(version.c_str(), MONERO_VERSION) <= 0) + { + m_update_available = false; return true; + } std::string url = tools::get_update_url(software, subdir, buildtag, version, true); MCLOG_CYAN(el::Level::Info, "global", "Version " << version << " of " << software << " for " << buildtag << " is available: " << url << ", SHA256 hash " << hash); + m_update_available = true; if (check_updates_level == UPDATES_NOTIFY) return true; diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index 8b68f5e2b5b..306521748ee 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -753,6 +753,16 @@ namespace cryptonote */ network_type get_nettype() const { return m_nettype; }; + /** + * @brief check whether an update is known to be available or not + * + * This does not actually trigger a check, but returns the result + * of the last check + * + * @return whether an update is known to be available or not + */ + bool is_update_available() const { return m_update_available; } + /** * @brief get whether fluffy blocks are enabled * @@ -981,6 +991,8 @@ namespace cryptonote network_type m_nettype; //!< which network are we on? + std::atomic m_update_available; + std::string m_checkpoints_path; //!< path to json checkpoints file time_t m_last_dns_checkpoints_update; //!< time when dns checkpoints were last updated time_t m_last_json_checkpoints_update; //!< time when json checkpoints were last updated diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index 4383ad190a9..411e677a0da 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -208,6 +208,7 @@ namespace cryptonote res.was_bootstrap_ever_used = m_was_bootstrap_ever_used; } res.database_size = m_core.get_blockchain_storage().get_db().get_database_size(); + res.update_available = m_core.is_update_available(); return true; } //------------------------------------------------------------------------------------------------------------------------------ @@ -1659,6 +1660,7 @@ namespace cryptonote res.was_bootstrap_ever_used = m_was_bootstrap_ever_used; } res.database_size = m_core.get_blockchain_storage().get_db().get_database_size(); + res.update_available = m_core.is_update_available(); return true; } //------------------------------------------------------------------------------------------------------------------------------ diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index da4c8b6cfa6..06451df18d1 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -49,7 +49,7 @@ namespace cryptonote // advance which version they will stop working with // Don't go over 32767 for any of these #define CORE_RPC_VERSION_MAJOR 2 -#define CORE_RPC_VERSION_MINOR 0 +#define CORE_RPC_VERSION_MINOR 1 #define MAKE_CORE_RPC_VERSION(major,minor) (((major)<<16)|(minor)) #define CORE_RPC_VERSION MAKE_CORE_RPC_VERSION(CORE_RPC_VERSION_MAJOR, CORE_RPC_VERSION_MINOR) @@ -970,6 +970,7 @@ namespace cryptonote uint64_t height_without_bootstrap; bool was_bootstrap_ever_used; uint64_t database_size; + bool update_available; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(status) @@ -1003,6 +1004,7 @@ namespace cryptonote KV_SERIALIZE(height_without_bootstrap) KV_SERIALIZE(was_bootstrap_ever_used) KV_SERIALIZE(database_size) + KV_SERIALIZE(update_available) END_KV_SERIALIZE_MAP() }; }; From 607301bf6dd4531c43d8e2f15c7cd94a92693918 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 24 Jun 2018 21:59:49 +0100 Subject: [PATCH 0405/1404] rct: avoid repeated unnecessary conversions when accummulating --- src/ringct/rctOps.cpp | 19 +++++++++++++++++++ src/ringct/rctOps.h | 1 + src/ringct/rctSigs.cpp | 12 +++++------- 3 files changed, 25 insertions(+), 7 deletions(-) diff --git a/src/ringct/rctOps.cpp b/src/ringct/rctOps.cpp index 6c3c4500e06..41bbf6ca387 100644 --- a/src/ringct/rctOps.cpp +++ b/src/ringct/rctOps.cpp @@ -252,6 +252,25 @@ namespace rct { return k; } + rct::key addKeys(const keyV &A) { + if (A.empty()) + return rct::identity(); + ge_p3 p3, tmp; + CHECK_AND_ASSERT_THROW_MES_L1(ge_frombytes_vartime(&p3, A[0].bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast(__LINE__)); + for (size_t i = 1; i < A.size(); ++i) + { + CHECK_AND_ASSERT_THROW_MES_L1(ge_frombytes_vartime(&tmp, A[i].bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast(__LINE__)); + ge_cached p2; + ge_p3_to_cached(&p2, &tmp); + ge_p1p1 p1; + ge_add(&p1, &p3, &p2); + ge_p1p1_to_p3(&p3, &p1); + } + rct::key res; + ge_p3_tobytes(res.bytes, &p3); + return res; + } + //addKeys1 //aGB = aG + B where a is a scalar, G is the basepoint, and B is a point void addKeys1(key &aGB, const key &a, const key & B) { diff --git a/src/ringct/rctOps.h b/src/ringct/rctOps.h index 50645821c48..60e920b3ad7 100644 --- a/src/ringct/rctOps.h +++ b/src/ringct/rctOps.h @@ -132,6 +132,7 @@ namespace rct { //for curve points: AB = A + B void addKeys(key &AB, const key &A, const key &B); rct::key addKeys(const key &A, const key &B); + rct::key addKeys(const keyV &A); //aGB = aG + B where a is a scalar, G is the basepoint, and B is a point void addKeys1(key &aGB, const key &a, const key & B); //aGbB = aG + bB where a, b are scalars, G is the basepoint and B is a point diff --git a/src/ringct/rctSigs.cpp b/src/ringct/rctSigs.cpp index fe0cd9c5737..0d1789a3886 100644 --- a/src/ringct/rctSigs.cpp +++ b/src/ringct/rctSigs.cpp @@ -963,18 +963,16 @@ namespace rct { const bool bulletproof = is_rct_bulletproof(rv.type); const keyV &pseudoOuts = bulletproof ? rv.p.pseudoOuts : rv.pseudoOuts; - key sumOutpks = identity(); + rct::keyV masks(rv.outPk.size()); for (size_t i = 0; i < rv.outPk.size(); i++) { - addKeys(sumOutpks, sumOutpks, rv.outPk[i].mask); + masks[i] = rv.outPk[i].mask; } + key sumOutpks = addKeys(masks); DP(sumOutpks); - key txnFeeKey = scalarmultH(d2h(rv.txnFee)); + const key txnFeeKey = scalarmultH(d2h(rv.txnFee)); addKeys(sumOutpks, txnFeeKey, sumOutpks); - key sumPseudoOuts = identity(); - for (size_t i = 0 ; i < pseudoOuts.size() ; i++) { - addKeys(sumPseudoOuts, sumPseudoOuts, pseudoOuts[i]); - } + key sumPseudoOuts = addKeys(pseudoOuts); DP(sumPseudoOuts); //check pseudoOuts vs Outs.. From be001326d1baac0b00d79d2d0a7f42662dc1be1f Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 13 Aug 2018 16:18:32 +0000 Subject: [PATCH 0406/1404] remove obsolete daemon selection of fake outs and old tx construction --- src/cryptonote_core/blockchain.cpp | 242 ------------------ src/cryptonote_core/blockchain.h | 61 ----- src/cryptonote_core/cryptonote_core.cpp | 10 - src/cryptonote_core/cryptonote_core.h | 15 -- src/rpc/core_rpc_server.cpp | 71 ----- src/rpc/core_rpc_server.h | 6 - src/rpc/core_rpc_server_commands_defs.h | 77 ------ src/rpc/daemon_handler.cpp | 39 --- src/rpc/daemon_handler.h | 2 - src/rpc/daemon_messages.cpp | 37 --- src/simplewallet/simplewallet.cpp | 11 +- src/wallet/api/wallet.cpp | 8 +- src/wallet/wallet2.cpp | 175 +------------ src/wallet/wallet2.h | 201 --------------- src/wallet/wallet_errors.h | 6 +- .../transactions_flow_test.cpp | 12 +- 16 files changed, 18 insertions(+), 955 deletions(-) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index b20fe986915..c7e2f5a65f2 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -1717,17 +1717,6 @@ size_t Blockchain::get_alternative_blocks_count() const //------------------------------------------------------------------ // This function adds the output specified by to the result_outs container // unlocked and other such checks should be done by here. -void Blockchain::add_out_to_get_random_outs(COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& result_outs, uint64_t amount, size_t i) const -{ - LOG_PRINT_L3("Blockchain::" << __func__); - CRITICAL_REGION_LOCAL(m_blockchain_lock); - - COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry& oen = *result_outs.outs.insert(result_outs.outs.end(), COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry()); - oen.global_amount_index = i; - output_data_t data = m_db->get_output_key(amount, i); - oen.out_key = data.pubkey; -} - uint64_t Blockchain::get_num_mature_outputs(uint64_t amount) const { uint64_t num_outs = m_db->get_num_outputs(amount); @@ -1745,243 +1734,12 @@ uint64_t Blockchain::get_num_mature_outputs(uint64_t amount) const return num_outs; } -std::vector Blockchain::get_random_outputs(uint64_t amount, uint64_t count) const -{ - uint64_t num_outs = get_num_mature_outputs(amount); - - std::vector indices; - - std::unordered_set seen_indices; - - // if there aren't enough outputs to mix with (or just enough), - // use all of them. Eventually this should become impossible. - if (num_outs <= count) - { - for (uint64_t i = 0; i < num_outs; i++) - { - // get tx_hash, tx_out_index from DB - tx_out_index toi = m_db->get_output_tx_and_index(amount, i); - - // if tx is unlocked, add output to indices - if (is_tx_spendtime_unlocked(m_db->get_tx_unlock_time(toi.first))) - { - indices.push_back(i); - } - } - } - else - { - // while we still need more mixins - while (indices.size() < count) - { - // if we've gone through every possible output, we've gotten all we can - if (seen_indices.size() == num_outs) - { - break; - } - - // get a random output index from the DB. If we've already seen it, - // return to the top of the loop and try again, otherwise add it to the - // list of output indices we've seen. - - // triangular distribution over [a,b) with a=0, mode c=b=up_index_limit - uint64_t r = crypto::rand() % ((uint64_t)1 << 53); - double frac = std::sqrt((double)r / ((uint64_t)1 << 53)); - uint64_t i = (uint64_t)(frac*num_outs); - // just in case rounding up to 1 occurs after sqrt - if (i == num_outs) - --i; - - if (seen_indices.count(i)) - { - continue; - } - seen_indices.emplace(i); - - // get tx_hash, tx_out_index from DB - tx_out_index toi = m_db->get_output_tx_and_index(amount, i); - - // if the output's transaction is unlocked, add the output's index to - // our list. - if (is_tx_spendtime_unlocked(m_db->get_tx_unlock_time(toi.first))) - { - indices.push_back(i); - } - } - } - - return indices; -} - crypto::public_key Blockchain::get_output_key(uint64_t amount, uint64_t global_index) const { output_data_t data = m_db->get_output_key(amount, global_index); return data.pubkey; } -//------------------------------------------------------------------ -// This function takes an RPC request for mixins and creates an RPC response -// with the requested mixins. -// TODO: figure out why this returns boolean / if we should be returning false -// in some cases -bool Blockchain::get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res) const -{ - LOG_PRINT_L3("Blockchain::" << __func__); - CRITICAL_REGION_LOCAL(m_blockchain_lock); - - // for each amount that we need to get mixins for, get random outputs - // from BlockchainDB where is req.outs_count (number of mixins). - for (uint64_t amount : req.amounts) - { - // create outs_for_amount struct and populate amount field - COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& result_outs = *res.outs.insert(res.outs.end(), COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount()); - result_outs.amount = amount; - - std::vector indices = get_random_outputs(amount, req.outs_count); - - for (auto i : indices) - { - COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry& oe = *result_outs.outs.insert(result_outs.outs.end(), COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry()); - - oe.global_amount_index = i; - oe.out_key = get_output_key(amount, i); - } - } - return true; -} -//------------------------------------------------------------------ -// This function adds the ringct output at index i to the list -// unlocked and other such checks should be done by here. -void Blockchain::add_out_to_get_rct_random_outs(std::list& outs, uint64_t amount, size_t i) const -{ - LOG_PRINT_L3("Blockchain::" << __func__); - CRITICAL_REGION_LOCAL(m_blockchain_lock); - - COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::out_entry& oen = *outs.insert(outs.end(), COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::out_entry()); - oen.amount = amount; - oen.global_amount_index = i; - output_data_t data = m_db->get_output_key(amount, i); - oen.out_key = data.pubkey; - oen.commitment = data.commitment; -} -//------------------------------------------------------------------ -// This function takes an RPC request for mixins and creates an RPC response -// with the requested mixins. -// TODO: figure out why this returns boolean / if we should be returning false -// in some cases -bool Blockchain::get_random_rct_outs(const COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::request& req, COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::response& res) const -{ - LOG_PRINT_L3("Blockchain::" << __func__); - CRITICAL_REGION_LOCAL(m_blockchain_lock); - - // for each amount that we need to get mixins for, get random outputs - // from BlockchainDB where is req.outs_count (number of mixins). - auto num_outs = m_db->get_num_outputs(0); - // ensure we don't include outputs that aren't yet eligible to be used - // outpouts are sorted by height - while (num_outs > 0) - { - const tx_out_index toi = m_db->get_output_tx_and_index(0, num_outs - 1); - const uint64_t height = m_db->get_tx_block_height(toi.first); - if (height + CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE <= m_db->height()) - break; - --num_outs; - } - - std::unordered_set seen_indices; - - // if there aren't enough outputs to mix with (or just enough), - // use all of them. Eventually this should become impossible. - if (num_outs <= req.outs_count) - { - for (uint64_t i = 0; i < num_outs; i++) - { - // get tx_hash, tx_out_index from DB - tx_out_index toi = m_db->get_output_tx_and_index(0, i); - - // if tx is unlocked, add output to result_outs - if (is_tx_spendtime_unlocked(m_db->get_tx_unlock_time(toi.first))) - { - add_out_to_get_rct_random_outs(res.outs, 0, i); - } - } - } - else - { - // while we still need more mixins - while (res.outs.size() < req.outs_count) - { - // if we've gone through every possible output, we've gotten all we can - if (seen_indices.size() == num_outs) - { - break; - } - - // get a random output index from the DB. If we've already seen it, - // return to the top of the loop and try again, otherwise add it to the - // list of output indices we've seen. - - // triangular distribution over [a,b) with a=0, mode c=b=up_index_limit - uint64_t r = crypto::rand() % ((uint64_t)1 << 53); - double frac = std::sqrt((double)r / ((uint64_t)1 << 53)); - uint64_t i = (uint64_t)(frac*num_outs); - // just in case rounding up to 1 occurs after sqrt - if (i == num_outs) - --i; - - if (seen_indices.count(i)) - { - continue; - } - seen_indices.emplace(i); - - // get tx_hash, tx_out_index from DB - tx_out_index toi = m_db->get_output_tx_and_index(0, i); - - // if the output's transaction is unlocked, add the output's index to - // our list. - if (is_tx_spendtime_unlocked(m_db->get_tx_unlock_time(toi.first))) - { - add_out_to_get_rct_random_outs(res.outs, 0, i); - } - } - } - - if (res.outs.size() < req.outs_count) - return false; -#if 0 - // if we do not have enough RCT inputs, we can pick from the non RCT ones - // which will have a zero mask - if (res.outs.size() < req.outs_count) - { - LOG_PRINT_L0("Out of RCT inputs (" << res.outs.size() << "/" << req.outs_count << "), using regular ones"); - - // TODO: arbitrary selection, needs better - COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request req2 = AUTO_VAL_INIT(req2); - COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response res2 = AUTO_VAL_INIT(res2); - req2.outs_count = req.outs_count - res.outs.size(); - static const uint64_t amounts[] = {1, 10, 20, 50, 100, 200, 500, 1000, 10000}; - for (uint64_t a: amounts) - req2.amounts.push_back(a); - if (!get_random_outs_for_amounts(req2, res2)) - return false; - - // pick random ones from there - while (res.outs.size() < req.outs_count) - { - int list_idx = rand() % (sizeof(amounts)/sizeof(amounts[0])); - if (!res2.outs[list_idx].outs.empty()) - { - const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry oe = res2.outs[list_idx].outs.back(); - res2.outs[list_idx].outs.pop_back(); - add_out_to_get_rct_random_outs(res.outs, res2.outs[list_idx].amount, oe.global_amount_index); - } - } - } -#endif - - return true; -} //------------------------------------------------------------------ bool Blockchain::get_outs(const COMMAND_RPC_GET_OUTPUTS_BIN::request& req, COMMAND_RPC_GET_OUTPUTS_BIN::response& res) const { diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index 7e2ba7a39fa..50ceccd0f76 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -446,16 +446,6 @@ namespace cryptonote */ uint64_t get_num_mature_outputs(uint64_t amount) const; - /** - * @brief get random outputs (indices) for an amount - * - * @param amount the amount - * @param count the number of random outputs to choose - * - * @return the outputs' amount-global indices - */ - std::vector get_random_outputs(uint64_t amount, uint64_t count) const; - /** * @brief get the public key for an output * @@ -466,22 +456,6 @@ namespace cryptonote */ crypto::public_key get_output_key(uint64_t amount, uint64_t global_index) const; - /** - * @brief gets random outputs to mix with - * - * This function takes an RPC request for outputs to mix with - * and creates an RPC response with the resultant output indices. - * - * Outputs to mix with are randomly selected from the utxo set - * for each output amount in the request. - * - * @param req the output amounts and number of mixins to select - * @param res return-by-reference the resultant output indices - * - * @return true - */ - bool get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res) const; - /** * @brief gets specific outputs to mix with * @@ -508,23 +482,6 @@ namespace cryptonote */ void get_output_key_mask_unlocked(const uint64_t& amount, const uint64_t& index, crypto::public_key& key, rct::key& mask, bool& unlocked) const; - /** - * @brief gets random ringct outputs to mix with - * - * This function takes an RPC request for outputs to mix with - * and creates an RPC response with the resultant output indices - * and the matching keys. - * - * Outputs to mix with are randomly selected from the utxo set - * for each output amount in the request. - * - * @param req the output amounts and number of mixins to select - * @param res return-by-reference the resultant output indices - * - * @return true - */ - bool get_random_rct_outs(const COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::request& req, COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::response& res) const; - /** * @brief gets per block distribution of outputs of a given amount * @@ -1271,24 +1228,6 @@ namespace cryptonote */ void get_last_n_blocks_weights(std::vector& weights, size_t count) const; - /** - * @brief adds the given output to the requested set of random outputs - * - * @param result_outs return-by-reference the set the output is to be added to - * @param amount the output amount - * @param i the output index (indexed to amount) - */ - void add_out_to_get_random_outs(COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& result_outs, uint64_t amount, size_t i) const; - - /** - * @brief adds the given output to the requested set of random ringct outputs - * - * @param outs return-by-reference the set the output is to be added to - * @param amount the output amount (0 for rct inputs) - * @param i the rct output index - */ - void add_out_to_get_rct_random_outs(std::list& outs, uint64_t amount, size_t i) const; - /** * @brief checks if a transaction is unlocked (its outputs spendable) * diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index c4eaa0cc443..7cbf414b780 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -1181,21 +1181,11 @@ namespace cryptonote return m_blockchain_storage.find_blockchain_supplement(req_start_block, qblock_ids, blocks, total_height, start_height, pruned, get_miner_tx_hash, max_count); } //----------------------------------------------------------------------------------------------- - bool core::get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res) const - { - return m_blockchain_storage.get_random_outs_for_amounts(req, res); - } - //----------------------------------------------------------------------------------------------- bool core::get_outs(const COMMAND_RPC_GET_OUTPUTS_BIN::request& req, COMMAND_RPC_GET_OUTPUTS_BIN::response& res) const { return m_blockchain_storage.get_outs(req, res); } //----------------------------------------------------------------------------------------------- - bool core::get_random_rct_outs(const COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::request& req, COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::response& res) const - { - return m_blockchain_storage.get_random_rct_outs(req, res); - } - //----------------------------------------------------------------------------------------------- bool core::get_output_distribution(uint64_t amount, uint64_t from_height, uint64_t to_height, uint64_t &start_height, std::vector &distribution, uint64_t &base) const { return m_blockchain_storage.get_output_distribution(amount, from_height, to_height, start_height, distribution, base); diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index 8b68f5e2b5b..b40575ae92c 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -550,13 +550,6 @@ namespace cryptonote */ difficulty_type get_block_cumulative_difficulty(uint64_t height) const; - /** - * @copydoc Blockchain::get_random_outs_for_amounts - * - * @note see Blockchain::get_random_outs_for_amounts - */ - bool get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res) const; - /** * @copydoc Blockchain::get_outs * @@ -564,14 +557,6 @@ namespace cryptonote */ bool get_outs(const COMMAND_RPC_GET_OUTPUTS_BIN::request& req, COMMAND_RPC_GET_OUTPUTS_BIN::response& res) const; - /** - * - * @copydoc Blockchain::get_random_rct_outs - * - * @note see Blockchain::get_random_rct_outs - */ - bool get_random_rct_outs(const COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::request& req, COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::response& res) const; - /** * @copydoc Blockchain::get_output_distribution * diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index 4383ad190a9..db7f2dbaa01 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -364,49 +364,6 @@ namespace cryptonote return true; } //------------------------------------------------------------------------------------------------------------------------------ - bool core_rpc_server::on_get_random_outs(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res) - { - PERF_TIMER(on_get_random_outs); - bool r; - if (use_bootstrap_daemon_if_necessary(invoke_http_mode::BIN, "/getrandom_outs.bin", req, res, r)) - return r; - - res.status = "Failed"; - - if (m_restricted) - { - if (req.amounts.size() > 100 || req.outs_count > MAX_RESTRICTED_FAKE_OUTS_COUNT) - { - res.status = "Too many outs requested"; - return true; - } - } - - if(!m_core.get_random_outs_for_amounts(req, res)) - { - return true; - } - - res.status = CORE_RPC_STATUS_OK; - std::stringstream ss; - typedef COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount outs_for_amount; - typedef COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry out_entry; - std::for_each(res.outs.begin(), res.outs.end(), [&](outs_for_amount& ofa) - { - ss << "[" << ofa.amount << "]:"; - CHECK_AND_ASSERT_MES(ofa.outs.size(), ;, "internal error: ofa.outs.size() is empty for amount " << ofa.amount); - std::for_each(ofa.outs.begin(), ofa.outs.end(), [&](out_entry& oe) - { - ss << oe.global_amount_index << " "; - }); - ss << ENDL; - }); - std::string s = ss.str(); - LOG_PRINT_L2("COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS: " << ENDL << s); - res.status = CORE_RPC_STATUS_OK; - return true; - } - //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_get_outs_bin(const COMMAND_RPC_GET_OUTPUTS_BIN::request& req, COMMAND_RPC_GET_OUTPUTS_BIN::response& res) { PERF_TIMER(on_get_outs_bin); @@ -476,34 +433,6 @@ namespace cryptonote return true; } //------------------------------------------------------------------------------------------------------------------------------ - bool core_rpc_server::on_get_random_rct_outs(const COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::request& req, COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::response& res) - { - PERF_TIMER(on_get_random_rct_outs); - bool r; - if (use_bootstrap_daemon_if_necessary(invoke_http_mode::BIN, "/getrandom_rctouts.bin", req, res, r)) - return r; - - res.status = "Failed"; - if(!m_core.get_random_rct_outs(req, res)) - { - return true; - } - - res.status = CORE_RPC_STATUS_OK; - std::stringstream ss; - typedef COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::out_entry out_entry; - CHECK_AND_ASSERT_MES(res.outs.size(), true, "internal error: res.outs.size() is empty"); - std::for_each(res.outs.begin(), res.outs.end(), [&](out_entry& oe) - { - ss << oe.global_amount_index << " "; - }); - ss << ENDL; - std::string s = ss.str(); - LOG_PRINT_L2("COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS: " << ENDL << s); - res.status = CORE_RPC_STATUS_OK; - return true; - } - //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_get_indexes(const COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::request& req, COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::response& res) { PERF_TIMER(on_get_indexes); diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h index 5dbe44d247a..3ba882b2355 100644 --- a/src/rpc/core_rpc_server.h +++ b/src/rpc/core_rpc_server.h @@ -87,11 +87,7 @@ namespace cryptonote MAP_URI_AUTO_BIN2("/get_hashes.bin", on_get_hashes, COMMAND_RPC_GET_HASHES_FAST) MAP_URI_AUTO_BIN2("/gethashes.bin", on_get_hashes, COMMAND_RPC_GET_HASHES_FAST) MAP_URI_AUTO_BIN2("/get_o_indexes.bin", on_get_indexes, COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES) - MAP_URI_AUTO_BIN2("/get_random_outs.bin", on_get_random_outs, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS) - MAP_URI_AUTO_BIN2("/getrandom_outs.bin", on_get_random_outs, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS) MAP_URI_AUTO_BIN2("/get_outs.bin", on_get_outs_bin, COMMAND_RPC_GET_OUTPUTS_BIN) - MAP_URI_AUTO_BIN2("/get_random_rctouts.bin", on_get_random_rct_outs, COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS) - MAP_URI_AUTO_BIN2("/getrandom_rctouts.bin", on_get_random_rct_outs, COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS) MAP_URI_AUTO_JON2("/get_transactions", on_get_transactions, COMMAND_RPC_GET_TRANSACTIONS) MAP_URI_AUTO_JON2("/gettransactions", on_get_transactions, COMMAND_RPC_GET_TRANSACTIONS) MAP_URI_AUTO_JON2("/get_alt_blocks_hashes", on_get_alt_blocks_hashes, COMMAND_RPC_GET_ALT_BLOCKS_HASHES) @@ -171,10 +167,8 @@ namespace cryptonote bool on_start_mining(const COMMAND_RPC_START_MINING::request& req, COMMAND_RPC_START_MINING::response& res); bool on_stop_mining(const COMMAND_RPC_STOP_MINING::request& req, COMMAND_RPC_STOP_MINING::response& res); bool on_mining_status(const COMMAND_RPC_MINING_STATUS::request& req, COMMAND_RPC_MINING_STATUS::response& res); - bool on_get_random_outs(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res); bool on_get_outs_bin(const COMMAND_RPC_GET_OUTPUTS_BIN::request& req, COMMAND_RPC_GET_OUTPUTS_BIN::response& res); bool on_get_outs(const COMMAND_RPC_GET_OUTPUTS::request& req, COMMAND_RPC_GET_OUTPUTS::response& res); - bool on_get_random_rct_outs(const COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::request& req, COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::response& res); bool on_get_info(const COMMAND_RPC_GET_INFO::request& req, COMMAND_RPC_GET_INFO::response& res); bool on_save_bc(const COMMAND_RPC_SAVE_BC::request& req, COMMAND_RPC_SAVE_BC::response& res); bool on_get_peer_list(const COMMAND_RPC_GET_PEER_LIST::request& req, COMMAND_RPC_GET_PEER_LIST::response& res); diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index da4c8b6cfa6..1a84ee61433 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -680,50 +680,6 @@ namespace cryptonote }; }; //----------------------------------------------- - struct COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS - { - struct request - { - std::vector amounts; - uint64_t outs_count; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(amounts) - KV_SERIALIZE(outs_count) - END_KV_SERIALIZE_MAP() - }; - -#pragma pack (push, 1) - struct out_entry - { - uint64_t global_amount_index; - crypto::public_key out_key; - }; -#pragma pack(pop) - - struct outs_for_amount - { - uint64_t amount; - std::list outs; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(amount) - KV_SERIALIZE_CONTAINER_POD_AS_BLOB(outs) - END_KV_SERIALIZE_MAP() - }; - - struct response - { - std::vector outs; - std::string status; - bool untrusted; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(outs) - KV_SERIALIZE(status) - KV_SERIALIZE(untrusted) - END_KV_SERIALIZE_MAP() - }; - }; - //----------------------------------------------- struct get_outputs_out { uint64_t amount; @@ -818,39 +774,6 @@ namespace cryptonote END_KV_SERIALIZE_MAP() }; }; - - struct COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS - { - struct request - { - uint64_t outs_count; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(outs_count) - END_KV_SERIALIZE_MAP() - }; - -#pragma pack (push, 1) - struct out_entry - { - uint64_t amount; - uint64_t global_amount_index; - crypto::public_key out_key; - rct::key commitment; - }; -#pragma pack(pop) - - struct response - { - std::list outs; - std::string status; - bool untrusted; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE_CONTAINER_POD_AS_BLOB(outs) - KV_SERIALIZE(status) - KV_SERIALIZE(untrusted) - END_KV_SERIALIZE_MAP() - }; - }; //----------------------------------------------- struct COMMAND_RPC_SEND_RAW_TX { diff --git a/src/rpc/daemon_handler.cpp b/src/rpc/daemon_handler.cpp index 26f102a8b36..9d3b09b68b2 100644 --- a/src/rpc/daemon_handler.cpp +++ b/src/rpc/daemon_handler.cpp @@ -260,44 +260,6 @@ namespace rpc } - //TODO: handle "restricted" RPC - void DaemonHandler::handle(const GetRandomOutputsForAmounts::Request& req, GetRandomOutputsForAmounts::Response& res) - { - auto& chain = m_core.get_blockchain_storage(); - - try - { - for (const uint64_t& amount : req.amounts) - { - std::vector indices = chain.get_random_outputs(amount, req.count); - - outputs_for_amount ofa; - - ofa.resize(indices.size()); - - for (size_t i = 0; i < indices.size(); i++) - { - crypto::public_key key = chain.get_output_key(amount, indices[i]); - ofa[i].amount_index = indices[i]; - ofa[i].key = key; - } - - amount_with_random_outputs amt; - amt.amount = amount; - amt.outputs = ofa; - - res.amounts_with_outputs.push_back(amt); - } - - res.status = Message::STATUS_OK; - } - catch (const std::exception& e) - { - res.status = Message::STATUS_FAILED; - res.error_details = e.what(); - } - } - void DaemonHandler::handle(const SendRawTx::Request& req, SendRawTx::Response& res) { auto tx_blob = cryptonote::tx_to_blob(req.tx); @@ -824,7 +786,6 @@ namespace rpc REQ_RESP_TYPES_MACRO(request_type, GetTransactions, req_json, resp_message, handle); REQ_RESP_TYPES_MACRO(request_type, KeyImagesSpent, req_json, resp_message, handle); REQ_RESP_TYPES_MACRO(request_type, GetTxGlobalOutputIndices, req_json, resp_message, handle); - REQ_RESP_TYPES_MACRO(request_type, GetRandomOutputsForAmounts, req_json, resp_message, handle); REQ_RESP_TYPES_MACRO(request_type, SendRawTx, req_json, resp_message, handle); REQ_RESP_TYPES_MACRO(request_type, GetInfo, req_json, resp_message, handle); REQ_RESP_TYPES_MACRO(request_type, StartMining, req_json, resp_message, handle); diff --git a/src/rpc/daemon_handler.h b/src/rpc/daemon_handler.h index f4371164048..5f968751132 100644 --- a/src/rpc/daemon_handler.h +++ b/src/rpc/daemon_handler.h @@ -66,8 +66,6 @@ class DaemonHandler : public RpcHandler void handle(const GetTxGlobalOutputIndices::Request& req, GetTxGlobalOutputIndices::Response& res); - void handle(const GetRandomOutputsForAmounts::Request& req, GetRandomOutputsForAmounts::Response& res); - void handle(const SendRawTx::Request& req, SendRawTx::Response& res); void handle(const StartMining::Request& req, StartMining::Response& res); diff --git a/src/rpc/daemon_messages.cpp b/src/rpc/daemon_messages.cpp index e5fb9781c44..56f6f6a8c1d 100644 --- a/src/rpc/daemon_messages.cpp +++ b/src/rpc/daemon_messages.cpp @@ -41,7 +41,6 @@ const char* const GetHashesFast::name = "get_hashes_fast"; const char* const GetTransactions::name = "get_transactions"; const char* const KeyImagesSpent::name = "key_images_spent"; const char* const GetTxGlobalOutputIndices::name = "get_tx_global_output_indices"; -const char* const GetRandomOutputsForAmounts::name = "get_random_outputs_for_amounts"; const char* const SendRawTx::name = "send_raw_tx"; const char* const StartMining::name = "start_mining"; const char* const StopMining::name = "stop_mining"; @@ -273,42 +272,6 @@ void GetTxGlobalOutputIndices::Response::fromJson(rapidjson::Value& val) GET_FROM_JSON_OBJECT(val, output_indices, output_indices); } - -rapidjson::Value GetRandomOutputsForAmounts::Request::toJson(rapidjson::Document& doc) const -{ - auto val = Message::toJson(doc); - - auto& al = doc.GetAllocator(); - - INSERT_INTO_JSON_OBJECT(val, doc, amounts, amounts); - INSERT_INTO_JSON_OBJECT(val, doc, count, count); - - return val; -} - -void GetRandomOutputsForAmounts::Request::fromJson(rapidjson::Value& val) -{ - GET_FROM_JSON_OBJECT(val, amounts, amounts); - GET_FROM_JSON_OBJECT(val, count, count); -} - -rapidjson::Value GetRandomOutputsForAmounts::Response::toJson(rapidjson::Document& doc) const -{ - auto val = Message::toJson(doc); - - auto& al = doc.GetAllocator(); - - INSERT_INTO_JSON_OBJECT(val, doc, amounts_with_outputs, amounts_with_outputs); - - return val; -} - -void GetRandomOutputsForAmounts::Response::fromJson(rapidjson::Value& val) -{ - GET_FROM_JSON_OBJECT(val, amounts_with_outputs, amounts_with_outputs); -} - - rapidjson::Value SendRawTx::Request::toJson(rapidjson::Document& doc) const { auto val = Message::toJson(doc); diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index bcdf2a43f2f..98f17eb2444 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -445,7 +445,7 @@ namespace LOG_ERROR("RPC error: " << e.to_string()); fail_msg_writer() << tr("RPC error: ") << e.what(); } - catch (const tools::error::get_random_outs_error &e) + catch (const tools::error::get_outs_error &e) { fail_msg_writer() << tr("failed to get random outputs to mix: ") << e.what(); } @@ -4856,15 +4856,12 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vectorcreate_transactions_2(dsts, fake_outs_count, unlock_block /* unlock_time */, priority, extra, m_current_subaddress_account, subaddr_indices); break; + default: + LOG_ERROR("Unknown transfer method, using default"); + /* FALLTHRU */ case TransferNew: ptx_vector = m_wallet->create_transactions_2(dsts, fake_outs_count, 0 /* unlock_time */, priority, extra, m_current_subaddress_account, subaddr_indices); break; - default: - LOG_ERROR("Unknown transfer method, using original"); - /* FALLTHRU */ - case TransferOriginal: - ptx_vector = m_wallet->create_transactions(dsts, fake_outs_count, 0 /* unlock_time */, priority, extra); - break; } if (ptx_vector.empty()) diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index 3780d72716c..e9e9a4e6779 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -1381,8 +1381,8 @@ PendingTransaction *WalletImpl::createTransaction(const string &dst_addr, const setStatusError(tr("no connection to daemon. Please make sure daemon is running.")); } catch (const tools::error::wallet_rpc_error& e) { setStatusError(tr("RPC error: ") + e.to_string()); - } catch (const tools::error::get_random_outs_error &e) { - setStatusError((boost::format(tr("failed to get random outputs to mix: %s")) % e.what()).str()); + } catch (const tools::error::get_outs_error &e) { + setStatusError((boost::format(tr("failed to get outputs to mix: %s")) % e.what()).str()); } catch (const tools::error::not_enough_unlocked_money& e) { std::ostringstream writer; @@ -1463,8 +1463,8 @@ PendingTransaction *WalletImpl::createSweepUnmixableTransaction() setStatusError(tr("no connection to daemon. Please make sure daemon is running.")); } catch (const tools::error::wallet_rpc_error& e) { setStatusError(tr("RPC error: ") + e.to_string()); - } catch (const tools::error::get_random_outs_error&) { - setStatusError(tr("failed to get random outputs to mix")); + } catch (const tools::error::get_outs_error&) { + setStatusError(tr("failed to get outputs to mix")); } catch (const tools::error::not_enough_unlocked_money& e) { setStatusError(""); std::ostringstream writer; diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index a50a88b5946..3e0d02967f6 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -4992,69 +4992,6 @@ void wallet2::add_unconfirmed_tx(const cryptonote::transaction& tx, uint64_t amo } } -//---------------------------------------------------------------------------------------------------- -void wallet2::transfer(const std::vector& dsts, const size_t fake_outs_count, const std::vector &unused_transfers_indices, - uint64_t unlock_time, uint64_t fee, const std::vector& extra, cryptonote::transaction& tx, pending_tx& ptx) -{ - transfer(dsts, fake_outs_count, unused_transfers_indices, unlock_time, fee, extra, detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), tx, ptx); -} -//---------------------------------------------------------------------------------------------------- -void wallet2::transfer(const std::vector& dsts, const size_t fake_outs_count, const std::vector &unused_transfers_indices, - uint64_t unlock_time, uint64_t fee, const std::vector& extra) -{ - cryptonote::transaction tx; - pending_tx ptx; - transfer(dsts, fake_outs_count, unused_transfers_indices, unlock_time, fee, extra, tx, ptx); -} - -namespace { -// split_amounts(vector dsts, size_t num_splits) -// -// split amount for each dst in dsts into num_splits parts -// and make num_splits new vector instances to hold these new amounts -std::vector> split_amounts( - std::vector dsts, size_t num_splits) -{ - std::vector> retVal; - - if (num_splits <= 1) - { - retVal.push_back(dsts); - return retVal; - } - - // for each split required - for (size_t i=0; i < num_splits; i++) - { - std::vector new_dsts; - - // for each destination - for (size_t j=0; j < dsts.size(); j++) - { - cryptonote::tx_destination_entry de; - uint64_t amount; - - amount = dsts[j].amount; - amount = amount / num_splits; - - // if last split, add remainder - if (i + 1 == num_splits) - { - amount += dsts[j].amount % num_splits; - } - - de.addr = dsts[j].addr; - de.amount = amount; - - new_dsts.push_back(de); - } - - retVal.push_back(new_dsts); - } - - return retVal; -} -} // anonymous namespace //---------------------------------------------------------------------------------------------------- crypto::hash wallet2::get_payment_id(const pending_tx &ptx) const { @@ -6055,116 +5992,6 @@ uint32_t wallet2::adjust_priority(uint32_t priority) return priority; } //---------------------------------------------------------------------------------------------------- -// separated the call(s) to wallet2::transfer into their own function -// -// this function will make multiple calls to wallet2::transfer if multiple -// transactions will be required -std::vector wallet2::create_transactions(std::vector dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector& extra) -{ - const std::vector unused_transfers_indices = select_available_outputs_from_histogram(fake_outs_count + 1, true, true, true); - - const uint64_t base_fee = get_base_fee(); - const uint64_t fee_multiplier = get_fee_multiplier(priority, get_fee_algorithm()); - const bool use_per_byte_fee = use_fork_rules(HF_VERSION_PER_BYTE_FEE); - const uint64_t fee_quantization_mask = get_fee_quantization_mask(); - - // failsafe split attempt counter - size_t attempt_count = 0; - - for(attempt_count = 1; ;attempt_count++) - { - size_t num_tx = 0.5 + pow(1.7,attempt_count-1); - - auto split_values = split_amounts(dsts, num_tx); - - // Throw if split_amounts comes back with a vector of size different than it should - if (split_values.size() != num_tx) - { - throw std::runtime_error("Splitting transactions returned a number of potential tx not equal to what was requested"); - } - - std::vector ptx_vector; - try - { - // for each new destination vector (i.e. for each new tx) - for (auto & dst_vector : split_values) - { - cryptonote::transaction tx; - pending_tx ptx; - - // loop until fee is met without increasing tx size to next KB boundary. - uint64_t needed_fee = estimate_fee(use_per_byte_fee, false, unused_transfers_indices.size(), fake_outs_count, dst_vector.size()+1, extra.size(), false, base_fee, fee_multiplier, fee_quantization_mask); - do - { - transfer(dst_vector, fake_outs_count, unused_transfers_indices, unlock_time, needed_fee, extra, tx, ptx); - auto txBlob = t_serializable_object_to_blob(ptx.tx); - needed_fee = calculate_fee(use_per_byte_fee, ptx.tx, txBlob.size(), base_fee, fee_multiplier, fee_quantization_mask); - } while (ptx.fee < needed_fee); - - ptx_vector.push_back(ptx); - - // mark transfers to be used as "spent" - for(size_t idx: ptx.selected_transfers) - { - set_spent(idx, 0); - } - } - - // if we made it this far, we've selected our transactions. committing them will mark them spent, - // so this is a failsafe in case they don't go through - // unmark pending tx transfers as spent - for (auto & ptx : ptx_vector) - { - // mark transfers to be used as not spent - for(size_t idx2: ptx.selected_transfers) - { - set_unspent(idx2); - } - - } - - // if we made it this far, we're OK to actually send the transactions - return ptx_vector; - - } - // only catch this here, other exceptions need to pass through to the calling function - catch (const tools::error::tx_too_big& e) - { - - // unmark pending tx transfers as spent - for (auto & ptx : ptx_vector) - { - // mark transfers to be used as not spent - for(size_t idx2: ptx.selected_transfers) - { - set_unspent(idx2); - } - } - - if (attempt_count >= MAX_SPLIT_ATTEMPTS) - { - throw; - } - } - catch (...) - { - // in case of some other exception, make sure any tx in queue are marked unspent again - - // unmark pending tx transfers as spent - for (auto & ptx : ptx_vector) - { - // mark transfers to be used as not spent - for(size_t idx2: ptx.selected_transfers) - { - set_unspent(idx2); - } - } - - throw; - } - } -} - bool wallet2::set_ring_database(const std::string &filename) { m_ring_database = filename; @@ -6934,7 +6761,7 @@ void wallet2::get_outs(std::vector> m_daemon_rpc_mutex.unlock(); THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_outs.bin"); THROW_WALLET_EXCEPTION_IF(daemon_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_outs.bin"); - THROW_WALLET_EXCEPTION_IF(daemon_resp.status != CORE_RPC_STATUS_OK, error::get_random_outs_error, daemon_resp.status); + THROW_WALLET_EXCEPTION_IF(daemon_resp.status != CORE_RPC_STATUS_OK, error::get_outs_error, daemon_resp.status); THROW_WALLET_EXCEPTION_IF(daemon_resp.outs.size() != req.outputs.size(), error::wallet_internal_error, "daemon returned wrong response for get_outs.bin, wrong amounts count = " + std::to_string(daemon_resp.outs.size()) + ", expected " + std::to_string(req.outputs.size())); diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index f9b516bff84..7a63d933c5a 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -713,12 +713,6 @@ namespace tools uint64_t balance_all() const; uint64_t unlocked_balance_all() const; template - void transfer(const std::vector& dsts, const size_t fake_outputs_count, const std::vector &unused_transfers_indices, uint64_t unlock_time, uint64_t fee, const std::vector& extra, T destination_split_strategy, const tx_dust_policy& dust_policy); - template - void transfer(const std::vector& dsts, const size_t fake_outputs_count, const std::vector &unused_transfers_indices, uint64_t unlock_time, uint64_t fee, const std::vector& extra, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction& tx, pending_tx& ptx); - void transfer(const std::vector& dsts, const size_t fake_outputs_count, const std::vector &unused_transfers_indices, uint64_t unlock_time, uint64_t fee, const std::vector& extra); - void transfer(const std::vector& dsts, const size_t fake_outputs_count, const std::vector &unused_transfers_indices, uint64_t unlock_time, uint64_t fee, const std::vector& extra, cryptonote::transaction& tx, pending_tx& ptx); - template void transfer_selected(const std::vector& dsts, const std::vector& selected_transfers, size_t fake_outputs_count, std::vector> &outs, uint64_t unlock_time, uint64_t fee, const std::vector& extra, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction& tx, pending_tx &ptx); @@ -746,7 +740,6 @@ namespace tools bool parse_unsigned_tx_from_str(const std::string &unsigned_tx_st, unsigned_tx_set &exported_txs) const; bool load_tx(const std::string &signed_filename, std::vector &ptx, std::function accept_func = NULL); bool parse_tx_from_str(const std::string &signed_tx_st, std::vector &ptx, std::function accept_func); - std::vector create_transactions(std::vector dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector& extra); std::vector create_transactions_2(std::vector dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector& extra, uint32_t subaddr_account, std::set subaddr_indices); // pass subaddr_indices by value on purpose std::vector create_transactions_all(uint64_t below, const cryptonote::account_public_address &address, bool is_subaddress, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector& extra, uint32_t subaddr_account, std::set subaddr_indices); std::vector create_transactions_single(const crypto::key_image &ki, const cryptonote::account_public_address &address, bool is_subaddress, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector& extra); @@ -1817,198 +1810,4 @@ namespace tools //---------------------------------------------------------------------------------------------------- } //---------------------------------------------------------------------------------------------------- - template - void wallet2::transfer(const std::vector& dsts, const size_t fake_outs_count, const std::vector &unused_transfers_indices, - uint64_t unlock_time, uint64_t fee, const std::vector& extra, T destination_split_strategy, const tx_dust_policy& dust_policy) - { - pending_tx ptx; - cryptonote::transaction tx; - transfer(dsts, fake_outs_count, unused_transfers_indices, unlock_time, fee, extra, destination_split_strategy, dust_policy, tx, ptx); - } - - template - void wallet2::transfer(const std::vector& dsts, const size_t fake_outputs_count, const std::vector &unused_transfers_indices, - uint64_t unlock_time, uint64_t fee, const std::vector& extra, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction& tx, pending_tx &ptx) - { - using namespace cryptonote; - // throw if attempting a transaction with no destinations - THROW_WALLET_EXCEPTION_IF(dsts.empty(), error::zero_destination); - - THROW_WALLET_EXCEPTION_IF(m_multisig, error::wallet_internal_error, "Multisig wallets cannot spend non rct outputs"); - - uint64_t upper_transaction_weight_limit = get_upper_transaction_weight_limit(); - uint64_t needed_money = fee; - - // calculate total amount being sent to all destinations - // throw if total amount overflows uint64_t - for(auto& dt: dsts) - { - THROW_WALLET_EXCEPTION_IF(0 == dt.amount, error::zero_destination); - needed_money += dt.amount; - THROW_WALLET_EXCEPTION_IF(needed_money < dt.amount, error::tx_sum_overflow, dsts, fee, m_nettype); - } - - // randomly select inputs for transaction - // throw if requested send amount is greater than (unlocked) amount available to send - std::vector selected_transfers; - uint64_t found_money = select_transfers(needed_money, unused_transfers_indices, selected_transfers); - THROW_WALLET_EXCEPTION_IF(found_money < needed_money, error::not_enough_unlocked_money, found_money, needed_money - fee, fee); - - uint32_t subaddr_account = m_transfers[*selected_transfers.begin()].m_subaddr_index.major; - for (auto i = ++selected_transfers.begin(); i != selected_transfers.end(); ++i) - THROW_WALLET_EXCEPTION_IF(subaddr_account != *i, error::wallet_internal_error, "the tx uses funds from multiple accounts"); - - typedef COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry out_entry; - typedef cryptonote::tx_source_entry::output_entry tx_output_entry; - - COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response daemon_resp = AUTO_VAL_INIT(daemon_resp); - if(fake_outputs_count) - { - COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request req = AUTO_VAL_INIT(req); - req.outs_count = fake_outputs_count + 1;// add one to make possible (if need) to skip real output key - for(size_t idx: selected_transfers) - { - const transfer_container::const_iterator it = m_transfers.begin() + idx; - THROW_WALLET_EXCEPTION_IF(it->m_tx.vout.size() <= it->m_internal_output_index, error::wallet_internal_error, - "m_internal_output_index = " + std::to_string(it->m_internal_output_index) + - " is greater or equal to outputs count = " + std::to_string(it->m_tx.vout.size())); - req.amounts.push_back(it->amount()); - } - - m_daemon_rpc_mutex.lock(); - bool r = epee::net_utils::invoke_http_bin("/getrandom_outs.bin", req, daemon_resp, m_http_client, rpc_timeout); - m_daemon_rpc_mutex.unlock(); - THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "getrandom_outs.bin"); - THROW_WALLET_EXCEPTION_IF(daemon_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "getrandom_outs.bin"); - THROW_WALLET_EXCEPTION_IF(daemon_resp.status != CORE_RPC_STATUS_OK, error::get_random_outs_error, daemon_resp.status); - THROW_WALLET_EXCEPTION_IF(daemon_resp.outs.size() != selected_transfers.size(), error::wallet_internal_error, - "daemon returned wrong response for getrandom_outs.bin, wrong amounts count = " + - std::to_string(daemon_resp.outs.size()) + ", expected " + std::to_string(selected_transfers.size())); - - std::unordered_map scanty_outs; - for(COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& amount_outs: daemon_resp.outs) - { - if (amount_outs.outs.size() < fake_outputs_count) - { - scanty_outs[amount_outs.amount] = amount_outs.outs.size(); - } - } - THROW_WALLET_EXCEPTION_IF(!scanty_outs.empty(), error::not_enough_outs_to_mix, scanty_outs, fake_outputs_count); - } - - //prepare inputs - size_t i = 0; - std::vector sources; - for(size_t idx: selected_transfers) - { - sources.resize(sources.size()+1); - cryptonote::tx_source_entry& src = sources.back(); - const transfer_details& td = m_transfers[idx]; - src.amount = td.amount(); - src.rct = false; - //paste mixin transaction - if(daemon_resp.outs.size()) - { - daemon_resp.outs[i].outs.sort([](const out_entry& a, const out_entry& b){return a.global_amount_index < b.global_amount_index;}); - for(out_entry& daemon_oe: daemon_resp.outs[i].outs) - { - if(td.m_global_output_index == daemon_oe.global_amount_index) - continue; - tx_output_entry oe; - oe.first = daemon_oe.global_amount_index; - oe.second.dest = rct::pk2rct(daemon_oe.out_key); - oe.second.mask = rct::identity(); - src.outputs.push_back(oe); - if(src.outputs.size() >= fake_outputs_count) - break; - } - } - - //paste real transaction to the random index - auto it_to_insert = std::find_if(src.outputs.begin(), src.outputs.end(), [&](const tx_output_entry& a) - { - return a.first >= td.m_global_output_index; - }); - //size_t real_index = src.outputs.size() ? (rand() % src.outputs.size() ):0; - tx_output_entry real_oe; - real_oe.first = td.m_global_output_index; - real_oe.second.dest = rct::pk2rct(boost::get(td.m_tx.vout[td.m_internal_output_index].target).key); - real_oe.second.mask = rct::identity(); - auto interted_it = src.outputs.insert(it_to_insert, real_oe); - src.real_out_tx_key = get_tx_pub_key_from_extra(td.m_tx); - src.real_output = interted_it - src.outputs.begin(); - src.real_output_in_tx_index = td.m_internal_output_index; - src.multisig_kLRki = rct::multisig_kLRki({rct::zero(), rct::zero(), rct::zero(), rct::zero()}); - detail::print_source_entry(src); - ++i; - } - - cryptonote::tx_destination_entry change_dts = AUTO_VAL_INIT(change_dts); - if (needed_money < found_money) - { - change_dts.addr = get_subaddress({subaddr_account, 0}); - change_dts.amount = found_money - needed_money; - } - - std::vector splitted_dsts, dust_dsts; - uint64_t dust = 0; - destination_split_strategy(dsts, change_dts, dust_policy.dust_threshold, splitted_dsts, dust_dsts); - for(auto& d: dust_dsts) { - THROW_WALLET_EXCEPTION_IF(dust_policy.dust_threshold < d.amount, error::wallet_internal_error, "invalid dust value: dust = " + - std::to_string(d.amount) + ", dust_threshold = " + std::to_string(dust_policy.dust_threshold)); - } - for(auto& d: dust_dsts) { - if (!dust_policy.add_to_fee) - splitted_dsts.push_back(cryptonote::tx_destination_entry(d.amount, dust_policy.addr_for_dust, d.is_subaddress)); - dust += d.amount; - } - - crypto::secret_key tx_key; - std::vector additional_tx_keys; - rct::multisig_out msout; - bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sources, splitted_dsts, change_dts.addr, extra, tx, unlock_time, tx_key, additional_tx_keys, false, rct::RangeProofBorromean, m_multisig ? &msout : NULL); - THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sources, splitted_dsts, unlock_time, m_nettype); - THROW_WALLET_EXCEPTION_IF(upper_transaction_weight_limit <= get_transaction_weight(tx), error::tx_too_big, tx, upper_transaction_weight_limit); - - std::string key_images; - bool all_are_txin_to_key = std::all_of(tx.vin.begin(), tx.vin.end(), [&](const txin_v& s_e) -> bool - { - CHECKED_GET_SPECIFIC_VARIANT(s_e, const txin_to_key, in, false); - key_images += boost::to_string(in.k_image) + " "; - return true; - }); - THROW_WALLET_EXCEPTION_IF(!all_are_txin_to_key, error::unexpected_txin_type, tx); - - bool dust_sent_elsewhere = (dust_policy.addr_for_dust.m_view_public_key != change_dts.addr.m_view_public_key - || dust_policy.addr_for_dust.m_spend_public_key != change_dts.addr.m_spend_public_key); - - if (dust_policy.add_to_fee || dust_sent_elsewhere) change_dts.amount -= dust; - - ptx.key_images = key_images; - ptx.fee = (dust_policy.add_to_fee ? fee+dust : fee); - ptx.dust = ((dust_policy.add_to_fee || dust_sent_elsewhere) ? dust : 0); - ptx.dust_added_to_fee = dust_policy.add_to_fee; - ptx.tx = tx; - ptx.change_dts = change_dts; - ptx.selected_transfers = selected_transfers; - ptx.tx_key = tx_key; - ptx.additional_tx_keys = additional_tx_keys; - ptx.dests = dsts; - ptx.construction_data.sources = sources; - ptx.construction_data.change_dts = change_dts; - ptx.construction_data.splitted_dsts = splitted_dsts; - ptx.construction_data.selected_transfers = selected_transfers; - ptx.construction_data.extra = tx.extra; - ptx.construction_data.unlock_time = unlock_time; - ptx.construction_data.use_rct = false; - ptx.construction_data.use_bulletproofs = false; - ptx.construction_data.dests = dsts; - // record which subaddress indices are being used as inputs - ptx.construction_data.subaddr_account = subaddr_account; - ptx.construction_data.subaddr_indices.clear(); - for (size_t idx: selected_transfers) - ptx.construction_data.subaddr_indices.insert(m_transfers[idx].m_subaddr_index.minor); - } - - } diff --git a/src/wallet/wallet_errors.h b/src/wallet/wallet_errors.h index a30e807b1c0..bc518d04a13 100644 --- a/src/wallet/wallet_errors.h +++ b/src/wallet/wallet_errors.h @@ -73,7 +73,7 @@ namespace tools // get_tx_pool_error // out_of_hashchain_bounds_error // transfer_error * - // get_random_outs_general_error + // get_outs_general_error // not_enough_unlocked_money // not_enough_money // tx_not_possible @@ -128,7 +128,7 @@ namespace tools get_blocks_error_message_index, get_hashes_error_message_index, get_out_indices_error_message_index, - get_random_outs_error_message_index + get_outs_error_message_index }; template @@ -427,7 +427,7 @@ namespace tools } }; //---------------------------------------------------------------------------------------------------- - typedef failed_rpc_request get_random_outs_error; + typedef failed_rpc_request get_outs_error; //---------------------------------------------------------------------------------------------------- struct not_enough_unlocked_money : public transfer_error { diff --git a/tests/functional_tests/transactions_flow_test.cpp b/tests/functional_tests/transactions_flow_test.cpp index 7248efade1e..25dcc77bc09 100644 --- a/tests/functional_tests/transactions_flow_test.cpp +++ b/tests/functional_tests/transactions_flow_test.cpp @@ -84,10 +84,10 @@ bool do_send_money(tools::wallet2& w1, tools::wallet2& w2, size_t mix_in_factor, try { - tools::wallet2::pending_tx ptx; - std::vector indices = w1.select_available_outputs([](const tools::wallet2::transfer_details&) { return true; }); - w1.transfer(dsts, mix_in_factor, indices, 0, TEST_FEE, std::vector(), tools::detail::null_split_strategy, tools::tx_dust_policy(TEST_DUST_THRESHOLD), tx, ptx); - w1.commit_tx(ptx); + std::vector ptx; + ptx = w1.create_transactions_2(dsts, mix_in_factor, 0, 0, std::vector(), 0, {}); + for (auto &p: ptx) + w1.commit_tx(p); return true; } catch (const std::exception&) @@ -167,8 +167,8 @@ bool transactions_flow_test(std::string& working_folder, daemon_req.miner_address = w1.get_account().get_public_address_str(MAINNET); daemon_req.threads_count = 9; r = net_utils::invoke_http_json("/start_mining", daemon_req, daemon_rsp, http_client, std::chrono::seconds(10)); - CHECK_AND_ASSERT_MES(r, false, "failed to get getrandom_outs"); - CHECK_AND_ASSERT_MES(daemon_rsp.status == CORE_RPC_STATUS_OK, false, "failed to getrandom_outs.bin"); + CHECK_AND_ASSERT_MES(r, false, "failed to start mining getrandom_outs"); + CHECK_AND_ASSERT_MES(daemon_rsp.status == CORE_RPC_STATUS_OK, false, "failed to start mining"); //wait for money, until balance will have enough money w1.refresh(true, blocks_fetched, received_money, ok); From 53a99ca112c288fcaf57af4b0022a85bfbed1564 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 14 Sep 2018 10:55:50 +0000 Subject: [PATCH 0407/1404] cn_deserialize: comment out pruned tx case it's not merged yet --- src/debug_utilities/cn_deserialize.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/debug_utilities/cn_deserialize.cpp b/src/debug_utilities/cn_deserialize.cpp index bf228fe142c..3e25522300d 100644 --- a/src/debug_utilities/cn_deserialize.cpp +++ b/src/debug_utilities/cn_deserialize.cpp @@ -179,9 +179,11 @@ int main(int argc, char* argv[]) } else if (cryptonote::parse_and_validate_tx_from_blob(blob, tx) || cryptonote::parse_and_validate_tx_base_from_blob(blob, tx)) { +/* if (tx.pruned) std::cout << "Parsed pruned transaction:" << std::endl; else +*/ std::cout << "Parsed transaction:" << std::endl; std::cout << cryptonote::obj_to_json_str(tx) << std::endl; From 44259e560e484350204a4c11f978cd9a6f090494 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 2 Sep 2018 09:59:01 +0000 Subject: [PATCH 0408/1404] wallet: ask-password can now ask without encrypting the secret spend key --- src/common/json_util.h | 4 +- src/simplewallet/simplewallet.cpp | 48 ++++++++++++++++-------- src/wallet/wallet2.cpp | 61 ++++++++++++++++--------------- src/wallet/wallet2.h | 26 ++++++++----- 4 files changed, 83 insertions(+), 56 deletions(-) diff --git a/src/common/json_util.h b/src/common/json_util.h index 661022a6f0b..c320c3956a8 100644 --- a/src/common/json_util.h +++ b/src/common/json_util.h @@ -29,14 +29,14 @@ #pragma once #define GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, name, type, jtype, mandatory, def) \ - type field_##name = def; \ + type field_##name = static_cast(def); \ bool field_##name##_found = false; \ (void)field_##name##_found; \ do if (json.HasMember(#name)) \ { \ if (json[#name].Is##jtype()) \ { \ - field_##name = json[#name].Get##jtype(); \ + field_##name = static_cast(json[#name].Get##jtype()); \ field_##name##_found = true; \ } \ else \ diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 602cdfe44d6..e9aac52eeab 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -1992,18 +1992,29 @@ bool simple_wallet::set_ask_password(const std::vector &args/* = st const auto pwd_container = get_and_verify_password(); if (pwd_container) { - parse_bool_and_use(args[1], [&](bool r) { - const bool cur_r = m_wallet->ask_password(); - if (!m_wallet->watch_only()) - { - if (cur_r && !r) - m_wallet->decrypt_keys(pwd_container->password()); - else if (!cur_r && r) - m_wallet->encrypt_keys(pwd_container->password()); - } - m_wallet->ask_password(r); - m_wallet->rewrite(m_wallet_file, pwd_container->password()); - }); + tools::wallet2::AskPasswordType ask = tools::wallet2::AskPasswordToDecrypt; + if (args[1] == "never" || args[1] == "0") + ask = tools::wallet2::AskPasswordNever; + else if (args[1] == "action" || args[1] == "1") + ask = tools::wallet2::AskPasswordOnAction; + else if (args[1] == "encrypt" || args[1] == "decrypt" || args[1] == "2") + ask = tools::wallet2::AskPasswordToDecrypt; + else + { + fail_msg_writer() << tr("invalid argument: must be either 0/never, 1/action, or 2/encrypt/decrypt"); + return true; + } + + const tools::wallet2::AskPasswordType cur_ask = m_wallet->ask_password(); + if (!m_wallet->watch_only()) + { + if (cur_ask == tools::wallet2::AskPasswordToDecrypt && ask != tools::wallet2::AskPasswordToDecrypt) + m_wallet->decrypt_keys(pwd_container->password()); + else if (cur_ask != tools::wallet2::AskPasswordToDecrypt && ask == tools::wallet2::AskPasswordToDecrypt) + m_wallet->encrypt_keys(pwd_container->password()); + } + m_wallet->ask_password(ask); + m_wallet->rewrite(m_wallet_file, pwd_container->password()); } return true; } @@ -2394,7 +2405,7 @@ simple_wallet::simple_wallet() "priority [0|1|2|3|4]\n " " Set the fee to default/unimportant/normal/elevated/priority.\n " "confirm-missing-payment-id <1|0>\n " - "ask-password <1|0>\n " + "ask-password <0|1|2 (or never|action|decrypt)>\n " "unit \n " " Set the default monero (sub-)unit.\n " "min-outputs-count [n]\n " @@ -2607,6 +2618,13 @@ bool simple_wallet::set_variable(const std::vector &args) uint32_t priority = m_wallet->get_default_priority(); if (priority < allowed_priority_strings.size()) priority_string = allowed_priority_strings[priority]; + std::string ask_password_string = "invalid"; + switch (m_wallet->ask_password()) + { + case tools::wallet2::AskPasswordNever: ask_password_string = "never"; break; + case tools::wallet2::AskPasswordOnAction: ask_password_string = "action"; break; + case tools::wallet2::AskPasswordToDecrypt: ask_password_string = "decrypt"; break; + } success_msg_writer() << "seed = " << seed_language; success_msg_writer() << "always-confirm-transfers = " << m_wallet->always_confirm_transfers(); success_msg_writer() << "print-ring-members = " << m_wallet->print_ring_members(); @@ -2616,7 +2634,7 @@ bool simple_wallet::set_variable(const std::vector &args) success_msg_writer() << "refresh-type = " << get_refresh_type_name(m_wallet->get_refresh_type()); success_msg_writer() << "priority = " << priority<< " (" << priority_string << ")"; success_msg_writer() << "confirm-missing-payment-id = " << m_wallet->confirm_missing_payment_id(); - success_msg_writer() << "ask-password = " << m_wallet->ask_password(); + success_msg_writer() << "ask-password = " << m_wallet->ask_password() << " (" << ask_password_string << ")"; success_msg_writer() << "unit = " << cryptonote::get_unit(cryptonote::get_default_decimal_point()); success_msg_writer() << "min-outputs-count = " << m_wallet->get_min_output_count(); success_msg_writer() << "min-outputs-value = " << cryptonote::print_money(m_wallet->get_min_output_value()); @@ -2672,7 +2690,7 @@ bool simple_wallet::set_variable(const std::vector &args) CHECK_SIMPLE_VARIABLE("refresh-type", set_refresh_type, tr("full (slowest, no assumptions); optimize-coinbase (fast, assumes the whole coinbase is paid to a single address); no-coinbase (fastest, assumes we receive no coinbase transaction), default (same as optimize-coinbase)")); CHECK_SIMPLE_VARIABLE("priority", set_default_priority, tr("0, 1, 2, 3, or 4, or one of ") << join_priority_strings(", ")); CHECK_SIMPLE_VARIABLE("confirm-missing-payment-id", set_confirm_missing_payment_id, tr("0 or 1")); - CHECK_SIMPLE_VARIABLE("ask-password", set_ask_password, tr("0 or 1")); + CHECK_SIMPLE_VARIABLE("ask-password", set_ask_password, tr("0|1|2 (or never|action|decrypt)")); CHECK_SIMPLE_VARIABLE("unit", set_unit, tr("monero, millinero, micronero, nanonero, piconero")); CHECK_SIMPLE_VARIABLE("min-outputs-count", set_min_output_count, tr("unsigned integer")); CHECK_SIMPLE_VARIABLE("min-outputs-value", set_min_output_value, tr("amount")); diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index f670febbfa4..2a655208e51 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -200,7 +200,7 @@ std::string get_weight_string(const cryptonote::transaction &tx, size_t blob_siz return get_weight_string(get_transaction_weight(tx, blob_size)); } -std::unique_ptr make_basic(const boost::program_options::variables_map& vm, bool rpc, const options& opts, const std::function(const char *, bool)> &password_prompter) +std::unique_ptr make_basic(const boost::program_options::variables_map& vm, bool unattended, const options& opts, const std::function(const char *, bool)> &password_prompter) { const bool testnet = command_line::get_arg(vm, opts.testnet); const bool stagenet = command_line::get_arg(vm, opts.stagenet); @@ -262,7 +262,7 @@ std::unique_ptr make_basic(const boost::program_options::variabl } std::unique_ptr wallet(new tools::wallet2(nettype, kdf_rounds)); - wallet->init(rpc, std::move(daemon_address), std::move(login), 0, false, *trusted_daemon); + wallet->init(unattended, std::move(daemon_address), std::move(login), 0, false, *trusted_daemon); boost::filesystem::path ringdb_path = command_line::get_arg(vm, opts.shared_ringdb_dir); wallet->set_ring_database(ringdb_path.string()); return wallet; @@ -297,7 +297,7 @@ boost::optional get_password(const boost::program_opt return password_prompter(verify ? tr("Enter a new password for the wallet") : tr("Wallet password"), verify); } -std::unique_ptr generate_from_json(const std::string& json_file, const boost::program_options::variables_map& vm, bool rpc, const options& opts, const std::function(const char *, bool)> &password_prompter) +std::unique_ptr generate_from_json(const std::string& json_file, const boost::program_options::variables_map& vm, bool unattended, const options& opts, const std::function(const char *, bool)> &password_prompter) { const bool testnet = command_line::get_arg(vm, opts.testnet); const bool stagenet = command_line::get_arg(vm, opts.stagenet); @@ -435,7 +435,7 @@ std::unique_ptr generate_from_json(const std::string& json_file, THROW_WALLET_EXCEPTION_IF(deprecated_wallet, tools::error::wallet_internal_error, tools::wallet2::tr("Cannot generate deprecated wallets from JSON")); - wallet.reset(make_basic(vm, rpc, opts, password_prompter).release()); + wallet.reset(make_basic(vm, unattended, opts, password_prompter).release()); wallet->set_refresh_from_block_height(field_scan_from_height); wallet->explicit_refresh_from_block_height(field_scan_from_height_found); @@ -721,8 +721,11 @@ wallet_keys_unlocker::wallet_keys_unlocker(wallet2 &w, const boost::optionalpassword(); w.generate_chacha_key_from_password(pass, key); w.decrypt_keys(key); @@ -764,7 +767,7 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds): m_explicit_refresh_from_block_height(true), m_confirm_missing_payment_id(true), m_confirm_non_default_ring_size(true), - m_ask_password(true), + m_ask_password(AskPasswordToDecrypt), m_min_output_count(0), m_min_output_value(0), m_merge_destinations(false), @@ -793,7 +796,7 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds): m_ringdb(), m_last_block_reward(0), m_encrypt_keys_after_refresh(boost::none), - m_rpc(false) + m_unattended(false) { } @@ -828,14 +831,14 @@ void wallet2::init_options(boost::program_options::options_description& desc_par command_line::add_arg(desc_params, opts.kdf_rounds); } -std::unique_ptr wallet2::make_from_json(const boost::program_options::variables_map& vm, bool rpc, const std::string& json_file, const std::function(const char *, bool)> &password_prompter) +std::unique_ptr wallet2::make_from_json(const boost::program_options::variables_map& vm, bool unattended, const std::string& json_file, const std::function(const char *, bool)> &password_prompter) { const options opts{}; - return generate_from_json(json_file, vm, rpc, opts, password_prompter); + return generate_from_json(json_file, vm, unattended, opts, password_prompter); } std::pair, password_container> wallet2::make_from_file( - const boost::program_options::variables_map& vm, bool rpc, const std::string& wallet_file, const std::function(const char *, bool)> &password_prompter) + const boost::program_options::variables_map& vm, bool unattended, const std::string& wallet_file, const std::function(const char *, bool)> &password_prompter) { const options opts{}; auto pwd = get_password(vm, opts, password_prompter, false); @@ -843,7 +846,7 @@ std::pair, password_container> wallet2::make_from_file( { return {nullptr, password_container{}}; } - auto wallet = make_basic(vm, rpc, opts, password_prompter); + auto wallet = make_basic(vm, unattended, opts, password_prompter); if (wallet) { wallet->load(wallet_file, pwd->password()); @@ -851,7 +854,7 @@ std::pair, password_container> wallet2::make_from_file( return {std::move(wallet), std::move(*pwd)}; } -std::pair, password_container> wallet2::make_new(const boost::program_options::variables_map& vm, bool rpc, const std::function(const char *, bool)> &password_prompter) +std::pair, password_container> wallet2::make_new(const boost::program_options::variables_map& vm, bool unattended, const std::function(const char *, bool)> &password_prompter) { const options opts{}; auto pwd = get_password(vm, opts, password_prompter, true); @@ -859,19 +862,19 @@ std::pair, password_container> wallet2::make_new(const { return {nullptr, password_container{}}; } - return {make_basic(vm, rpc, opts, password_prompter), std::move(*pwd)}; + return {make_basic(vm, unattended, opts, password_prompter), std::move(*pwd)}; } -std::unique_ptr wallet2::make_dummy(const boost::program_options::variables_map& vm, bool rpc, const std::function(const char *, bool)> &password_prompter) +std::unique_ptr wallet2::make_dummy(const boost::program_options::variables_map& vm, bool unattended, const std::function(const char *, bool)> &password_prompter) { const options opts{}; - return make_basic(vm, rpc, opts, password_prompter); + return make_basic(vm, unattended, opts, password_prompter); } //---------------------------------------------------------------------------------------------------- -bool wallet2::init(bool rpc, std::string daemon_address, boost::optional daemon_login, uint64_t upper_transaction_weight_limit, bool ssl, bool trusted_daemon) +bool wallet2::init(bool unattended, std::string daemon_address, boost::optional daemon_login, uint64_t upper_transaction_weight_limit, bool ssl, bool trusted_daemon) { - m_rpc = rpc; + m_unattended = unattended; m_checkpoints.init_default_checkpoints(m_nettype); if(m_http_client.is_connected()) m_http_client.disconnect(); @@ -1210,7 +1213,7 @@ void wallet2::scan_output(const cryptonote::transaction &tx, const crypto::publi THROW_WALLET_EXCEPTION_IF(i >= tx.vout.size(), error::wallet_internal_error, "Invalid vout index"); // if keys are encrypted, ask for password - if (m_ask_password && !m_rpc && !m_watch_only && !m_multisig_rescan_k) + if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only && !m_multisig_rescan_k) { static critical_section password_lock; CRITICAL_REGION_LOCAL(password_lock); @@ -2847,7 +2850,7 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable crypto::chacha_key key; crypto::generate_chacha_key(password.data(), password.size(), key, m_kdf_rounds); - if (m_ask_password && !m_rpc && !m_watch_only) + if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only) { account.encrypt_viewkey(key); account.decrypt_keys(key); @@ -2926,7 +2929,7 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable value2.SetInt(m_confirm_non_default_ring_size ? 1 :0); json.AddMember("confirm_non_default_ring_size", value2, json.GetAllocator()); - value2.SetInt(m_ask_password ? 1 :0); + value2.SetInt(m_ask_password); json.AddMember("ask_password", value2, json.GetAllocator()); value2.SetUint(m_min_output_count); @@ -3007,7 +3010,7 @@ void wallet2::setup_keys(const epee::wipeable_string &password) crypto::generate_chacha_key(password.data(), password.size(), key, m_kdf_rounds); // re-encrypt, but keep viewkey unencrypted - if (m_ask_password && !m_rpc && !m_watch_only) + if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only) { m_account.encrypt_keys(key); m_account.decrypt_viewkey(key); @@ -3023,7 +3026,7 @@ void wallet2::setup_keys(const epee::wipeable_string &password) //---------------------------------------------------------------------------------------------------- void wallet2::change_password(const std::string &filename, const epee::wipeable_string &original_password, const epee::wipeable_string &new_password) { - if (m_ask_password && !m_rpc && !m_watch_only) + if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only) decrypt_keys(original_password); setup_keys(new_password); rewrite(filename, new_password); @@ -3071,7 +3074,7 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_ m_refresh_type = RefreshType::RefreshDefault; m_confirm_missing_payment_id = true; m_confirm_non_default_ring_size = true; - m_ask_password = true; + m_ask_password = AskPasswordToDecrypt; m_min_output_count = 0; m_min_output_value = 0; m_merge_destinations = false; @@ -3180,7 +3183,7 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_ m_confirm_missing_payment_id = field_confirm_missing_payment_id; GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, confirm_non_default_ring_size, int, Int, false, true); m_confirm_non_default_ring_size = field_confirm_non_default_ring_size; - GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, ask_password, int, Int, false, true); + GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, ask_password, AskPasswordType, Int, false, AskPasswordToDecrypt); m_ask_password = field_ask_password; GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, default_decimal_point, int, Int, false, CRYPTONOTE_DISPLAY_DECIMAL_POINT); cryptonote::set_default_decimal_point(field_default_decimal_point); @@ -3244,7 +3247,7 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_ else { // rewrite with encrypted keys, ignore errors - if (m_ask_password && !m_rpc && !m_watch_only) + if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only) encrypt_keys(key); bool saved_ret = store_keys(keys_file_name, password, m_watch_only); if (!saved_ret) @@ -3252,7 +3255,7 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_ // just moan a bit, but not fatal MERROR("Error saving keys file with encrypted keys, not fatal"); } - if (m_ask_password && !m_rpc && !m_watch_only) + if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only) decrypt_keys(key); m_keys_file_locker.reset(); } @@ -3750,7 +3753,7 @@ std::string wallet2::make_multisig(const epee::wipeable_string &password, // decrypt keys epee::misc_utils::auto_scope_leave_caller keys_reencryptor; - if (m_ask_password && !m_rpc && !m_watch_only) + if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only) { crypto::chacha_key chacha_key; crypto::generate_chacha_key(password.data(), password.size(), chacha_key, m_kdf_rounds); @@ -3907,7 +3910,7 @@ bool wallet2::finalize_multisig(const epee::wipeable_string &password, std::unor // keys are decrypted epee::misc_utils::auto_scope_leave_caller keys_reencryptor; - if (m_ask_password && !m_rpc && !m_watch_only) + if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only) { crypto::chacha_key chacha_key; crypto::generate_chacha_key(password.data(), password.size(), chacha_key, m_kdf_rounds); @@ -4277,7 +4280,7 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass LOG_PRINT_L0("Loaded wallet keys file, with public address: " << m_account.get_public_address_str(m_nettype)); lock_keys_file(); - wallet_keys_unlocker unlocker(*this, m_ask_password && !m_rpc && !m_watch_only, password); + wallet_keys_unlocker unlocker(*this, m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only, password); //keys loaded ok! //try to load wallet file. but even if we failed, it is not big problem diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 0347092c0d0..42046dee389 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -162,6 +162,12 @@ namespace tools RefreshDefault = RefreshOptimizeCoinbase, }; + enum AskPasswordType { + AskPasswordNever = 0, + AskPasswordOnAction = 1, + AskPasswordToDecrypt = 2, + }; + static const char* tr(const char* str); static bool has_testnet_option(const boost::program_options::variables_map& vm); @@ -169,17 +175,17 @@ namespace tools static void init_options(boost::program_options::options_description& desc_params); //! Uses stdin and stdout. Returns a wallet2 if no errors. - static std::unique_ptr make_from_json(const boost::program_options::variables_map& vm, bool rpc, const std::string& json_file, const std::function(const char *, bool)> &password_prompter); + static std::unique_ptr make_from_json(const boost::program_options::variables_map& vm, bool unattended, const std::string& json_file, const std::function(const char *, bool)> &password_prompter); //! Uses stdin and stdout. Returns a wallet2 and password for `wallet_file` if no errors. static std::pair, password_container> - make_from_file(const boost::program_options::variables_map& vm, bool rpc, const std::string& wallet_file, const std::function(const char *, bool)> &password_prompter); + make_from_file(const boost::program_options::variables_map& vm, bool unattended, const std::string& wallet_file, const std::function(const char *, bool)> &password_prompter); //! Uses stdin and stdout. Returns a wallet2 and password for wallet with no file if no errors. - static std::pair, password_container> make_new(const boost::program_options::variables_map& vm, bool rpc, const std::function(const char *, bool)> &password_prompter); + static std::pair, password_container> make_new(const boost::program_options::variables_map& vm, bool unattended, const std::function(const char *, bool)> &password_prompter); //! Just parses variables. - static std::unique_ptr make_dummy(const boost::program_options::variables_map& vm, bool rpc, const std::function(const char *, bool)> &password_prompter); + static std::unique_ptr make_dummy(const boost::program_options::variables_map& vm, bool unattended, const std::function(const char *, bool)> &password_prompter); static bool verify_password(const std::string& keys_file_name, const epee::wipeable_string& password, bool no_spend_key, hw::device &hwdev, uint64_t kdf_rounds); @@ -631,7 +637,7 @@ namespace tools bool explicit_refresh_from_block_height() const {return m_explicit_refresh_from_block_height;} bool deinit(); - bool init(bool rpc, std::string daemon_address = "http://localhost:8080", + bool init(bool unatteded, std::string daemon_address = "http://localhost:8080", boost::optional daemon_login = boost::none, uint64_t upper_transaction_weight_limit = 0, bool ssl = false, bool trusted_daemon = false); void stop() { m_run.store(false, std::memory_order_relaxed); } @@ -905,8 +911,8 @@ namespace tools void auto_refresh(bool r) { m_auto_refresh = r; } bool confirm_missing_payment_id() const { return m_confirm_missing_payment_id; } void confirm_missing_payment_id(bool always) { m_confirm_missing_payment_id = always; } - bool ask_password() const { return m_ask_password; } - void ask_password(bool always) { m_ask_password = always; } + AskPasswordType ask_password() const { return m_ask_password; } + void ask_password(AskPasswordType ask) { m_ask_password = ask; } void set_min_output_count(uint32_t count) { m_min_output_count = count; } uint32_t get_min_output_count() const { return m_min_output_count; } void set_min_output_value(uint64_t value) { m_min_output_value = value; } @@ -1082,7 +1088,7 @@ namespace tools uint64_t adjust_mixin(uint64_t mixin) const; uint32_t adjust_priority(uint32_t priority); - bool is_rpc() const { return m_rpc; } + bool is_unattended() const { return m_unattended; } // Light wallet specific functions // fetch unspent outs from lw node and store in m_transfers @@ -1293,7 +1299,7 @@ namespace tools bool m_explicit_refresh_from_block_height; bool m_confirm_missing_payment_id; bool m_confirm_non_default_ring_size; - bool m_ask_password; + AskPasswordType m_ask_password; uint32_t m_min_output_count; uint64_t m_min_output_value; bool m_merge_destinations; @@ -1335,7 +1341,7 @@ namespace tools crypto::chacha_key m_cache_key; boost::optional m_encrypt_keys_after_refresh; - bool m_rpc; + bool m_unattended; }; } BOOST_CLASS_VERSION(tools::wallet2, 25) From f9b22a7b01994f444185f38fe1f645593590c943 Mon Sep 17 00:00:00 2001 From: Dusan Klinec Date: Fri, 24 Aug 2018 00:50:31 +0200 Subject: [PATCH 0409/1404] hw_device: support for multiple devices added [for review] - device name is a new wallet property - full device name is now a bit more structured so we can address particular device vendor + device path. Example: 'Ledger', 'Trezor:udp', 'Trezor:udp:127.0.0.1:21324', 'Trezor:bridge:usb01'. The part before ':' identifies HW device implementation, the optional part after ':' is device path to look for. - new --hw-device parameter added to the wallet, can name the hardware device - device reconnect added --- src/cryptonote_basic/account.cpp | 8 +++- src/cryptonote_basic/account.h | 3 +- src/device/device.cpp | 70 +++++++++++++++++++++---------- src/device/device.hpp | 13 +++++- src/simplewallet/simplewallet.cpp | 39 +++++++++++++++-- src/simplewallet/simplewallet.h | 3 +- src/wallet/wallet2.cpp | 51 +++++++++++++++++++++- src/wallet/wallet2.h | 5 +++ 8 files changed, 160 insertions(+), 32 deletions(-) diff --git a/src/cryptonote_basic/account.cpp b/src/cryptonote_basic/account.cpp index 4cbfa814282..e891a748db2 100644 --- a/src/cryptonote_basic/account.cpp +++ b/src/cryptonote_basic/account.cpp @@ -197,10 +197,14 @@ DISABLE_VS_WARNINGS(4244 4345) //----------------------------------------------------------------- void account_base::create_from_device(const std::string &device_name) { - hw::device &hwdev = hw::get_device(device_name); - m_keys.set_device(hwdev); hwdev.set_name(device_name); + create_from_device(hwdev); + } + + void account_base::create_from_device(hw::device &hwdev) + { + m_keys.set_device(hwdev); MCDEBUG("ledger", "device type: "< &multisig_keys); diff --git a/src/device/device.cpp b/src/device/device.cpp index 983f59b60ea..8a8b4006133 100644 --- a/src/device/device.cpp +++ b/src/device/device.cpp @@ -39,32 +39,60 @@ namespace hw { /* ======================================================================= */ /* SETUP */ - /* ======================================================================= */ - device& get_device(const std::string device_descriptor) { - - struct s_devices { - std::map> registry; - s_devices() : registry() { - hw::core::register_all(registry); - #ifdef HAVE_PCSC - hw::ledger::register_all(registry); - #endif - }; - }; - - static const s_devices devices; + /* ======================================================================= */ + + static std::unique_ptr registry; + + device_registry::device_registry(){ + hw::core::register_all(registry); + #ifdef HAVE_PCSC + hw::ledger::register_all(registry); + #endif + } + + bool device_registry::register_device(const std::string & device_name, device * hw_device){ + auto search = registry.find(device_name); + if (search != registry.end()){ + return false; + } + + registry.insert(std::make_pair(device_name, std::unique_ptr(hw_device))); + return true; + } + + device& device_registry::get_device(const std::string & device_descriptor){ + // Device descriptor can contain further specs after first : + auto delim = device_descriptor.find(':'); + auto device_descriptor_lookup = device_descriptor; + if (delim != std::string::npos) { + device_descriptor_lookup = device_descriptor.substr(0, delim); + } - auto device = devices.registry.find(device_descriptor); - if (device == devices.registry.end()) { - MERROR("device not found in registry: '" << device_descriptor << "'\n" << - "known devices:"); - - for( const auto& sm_pair : devices.registry ) { + auto device = registry.find(device_descriptor_lookup); + if (device == registry.end()) { + MERROR("Device not found in registry: '" << device_descriptor << "'. Known devices: "); + for( const auto& sm_pair : registry ) { MERROR(" - " << sm_pair.first); } - throw std::runtime_error("device not found: "+ device_descriptor); + throw std::runtime_error("device not found: " + device_descriptor); } return *device->second; } + device& get_device(const std::string & device_descriptor) { + if (!registry){ + registry.reset(new device_registry()); + } + + return registry->get_device(device_descriptor); + } + + bool register_device(const std::string & device_name, device * hw_device){ + if (!registry){ + registry.reset(new device_registry()); + } + + return registry->register_device(device_name, hw_device); + } + } diff --git a/src/device/device.hpp b/src/device/device.hpp index c21456daf08..d14b8848ca7 100644 --- a/src/device/device.hpp +++ b/src/device/device.hpp @@ -202,6 +202,17 @@ namespace hw { ~reset_mode() { hwref.set_mode(hw::device::NONE);} }; - device& get_device(const std::string device_descriptor) ; + class device_registry { + private: + std::map> registry; + + public: + device_registry(); + bool register_device(const std::string & device_name, device * hw_device); + device& get_device(const std::string & device_descriptor); + }; + + device& get_device(const std::string & device_descriptor); + bool register_device(const std::string & device_name, device * hw_device); } diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index e535b6d2777..ad67b108e25 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -2523,6 +2523,10 @@ simple_wallet::simple_wallet() boost::bind(&simple_wallet::import_key_images, this, _1), tr("import_key_images "), tr("Import a signed key images list and verify their spent status.")); + m_cmd_binder.set_handler("hw_reconnect", + boost::bind(&simple_wallet::hw_reconnect, this, _1), + tr("hw_reconnect"), + tr("Attempts to reconnect HW wallet.")); m_cmd_binder.set_handler("export_outputs", boost::bind(&simple_wallet::export_outputs, this, _1), tr("export_outputs "), @@ -2650,6 +2654,7 @@ bool simple_wallet::set_variable(const std::vector &args) success_msg_writer() << "subaddress-lookahead = " << lookahead.first << ":" << lookahead.second; success_msg_writer() << "segregation-height = " << m_wallet->segregation_height(); success_msg_writer() << "ignore-fractional-outputs = " << m_wallet->ignore_fractional_outputs(); + success_msg_writer() << "device_name = " << m_wallet->device_name(); return true; } else @@ -3295,7 +3300,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) { m_wallet_file = m_generate_from_device; // create wallet - auto r = new_wallet(vm, "Ledger"); + auto r = new_wallet(vm); CHECK_AND_ASSERT_MES(r, false, tr("account creation failed")); password = *r; // if no block_height is specified, assume its a new account and start it "now" @@ -3703,8 +3708,8 @@ boost::optional simple_wallet::new_wallet(const boost::pr } //---------------------------------------------------------------------------------------------------- -boost::optional simple_wallet::new_wallet(const boost::program_options::variables_map& vm, - const std::string &device_name) { +boost::optional simple_wallet::new_wallet(const boost::program_options::variables_map& vm) +{ auto rc = tools::wallet2::make_new(vm, false, password_prompter); m_wallet = std::move(rc.first); if (!m_wallet) @@ -3723,10 +3728,11 @@ boost::optional simple_wallet::new_wallet(const boost::pr if (m_restore_height) m_wallet->set_refresh_from_block_height(m_restore_height); + auto device_desc = tools::wallet2::device_name_option(vm); try { bool create_address_file = command_line::get_arg(vm, arg_create_address_file); - m_wallet->restore(m_wallet_file, std::move(rc.second).password(), device_name, create_address_file); + m_wallet->restore(m_wallet_file, std::move(rc.second).password(), device_desc.empty() ? "Ledger" : device_desc, create_address_file); message_writer(console_color_white, true) << tr("Generated new wallet on hw device: ") << m_wallet->get_account().get_public_address_str(m_wallet->nettype()); } @@ -7745,6 +7751,31 @@ bool simple_wallet::import_key_images(const std::vector &args) return true; } //---------------------------------------------------------------------------------------------------- +bool simple_wallet::hw_reconnect(const std::vector &args) +{ + if (!m_wallet->key_on_device()) + { + fail_msg_writer() << tr("command only supported by HW wallet"); + return true; + } + + LOCK_IDLE_SCOPE(); + try + { + bool r = m_wallet->reconnect_device(); + if (!r){ + fail_msg_writer() << tr("Failed to reconnect device"); + } + } + catch (const std::exception &e) + { + fail_msg_writer() << tr("Failed to reconnect device: ") << tr(e.what()); + return true; + } + + return true; +} +//---------------------------------------------------------------------------------------------------- bool simple_wallet::export_outputs(const std::vector &args) { if (m_wallet->key_on_device()) diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index bfbe633aca1..d50e4ce0404 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -98,7 +98,7 @@ namespace cryptonote const boost::optional& spendkey, const crypto::secret_key& viewkey); boost::optional new_wallet(const boost::program_options::variables_map& vm, const epee::wipeable_string &multisig_keys, const std::string &old_language); - boost::optional new_wallet(const boost::program_options::variables_map& vm, const std::string& device_name); + boost::optional new_wallet(const boost::program_options::variables_map& vm); bool open_wallet(const boost::program_options::variables_map& vm); bool close_wallet(); @@ -201,6 +201,7 @@ namespace cryptonote bool verify(const std::vector &args); bool export_key_images(const std::vector &args); bool import_key_images(const std::vector &args); + bool hw_reconnect(const std::vector &args); bool export_outputs(const std::vector &args); bool import_outputs(const std::vector &args); bool show_transfer(const std::vector &args); diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index b266821d6c8..dc7ab668f7d 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -161,6 +161,7 @@ struct options { } }; const command_line::arg_descriptor kdf_rounds = {"kdf-rounds", tools::wallet2::tr("Number of rounds for the key derivation function"), 1}; + const command_line::arg_descriptor hw_device = {"hw-device", tools::wallet2::tr("HW device to use"), ""}; }; void do_prepare_file_names(const std::string& file_path, std::string& keys_file, std::string& wallet_file) @@ -211,6 +212,7 @@ std::unique_ptr make_basic(const boost::program_options::variabl auto daemon_address = command_line::get_arg(vm, opts.daemon_address); auto daemon_host = command_line::get_arg(vm, opts.daemon_host); auto daemon_port = command_line::get_arg(vm, opts.daemon_port); + auto device_name = command_line::get_arg(vm, opts.hw_device); THROW_WALLET_EXCEPTION_IF(!daemon_address.empty() && !daemon_host.empty() && 0 != daemon_port, tools::error::wallet_internal_error, tools::wallet2::tr("can't specify daemon host or port more than once")); @@ -265,6 +267,7 @@ std::unique_ptr make_basic(const boost::program_options::variabl wallet->init(unattended, std::move(daemon_address), std::move(login), 0, false, *trusted_daemon); boost::filesystem::path ringdb_path = command_line::get_arg(vm, opts.shared_ringdb_dir); wallet->set_ring_database(ringdb_path.string()); + wallet->device_name(device_name); return wallet; } @@ -814,6 +817,11 @@ bool wallet2::has_stagenet_option(const boost::program_options::variables_map& v return command_line::get_arg(vm, options().stagenet); } +std::string wallet2::device_name_option(const boost::program_options::variables_map& vm) +{ + return command_line::get_arg(vm, options().hw_device); +} + void wallet2::init_options(boost::program_options::options_description& desc_params) { const options opts{}; @@ -829,6 +837,7 @@ void wallet2::init_options(boost::program_options::options_description& desc_par command_line::add_arg(desc_params, opts.stagenet); command_line::add_arg(desc_params, opts.shared_ringdb_dir); command_line::add_arg(desc_params, opts.kdf_rounds); + command_line::add_arg(desc_params, opts.hw_device); } std::unique_ptr wallet2::make_from_json(const boost::program_options::variables_map& vm, bool unattended, const std::string& json_file, const std::function(const char *, bool)> &password_prompter) @@ -984,6 +993,27 @@ bool wallet2::get_multisig_seed(epee::wipeable_string& seed, const epee::wipeabl return true; } //---------------------------------------------------------------------------------------------------- +bool wallet2::reconnect_device() +{ + bool r = true; + hw::device &hwdev = hw::get_device(m_device_name); + hwdev.set_name(m_device_name); + r = hwdev.init(); + if (!r){ + LOG_PRINT_L2("Could not init device"); + return false; + } + + r = hwdev.connect(); + if (!r){ + LOG_PRINT_L2("Could not connect to the device"); + return false; + } + + m_account.set_device(hwdev); + return true; +} +//---------------------------------------------------------------------------------------------------- /*! * \brief Gets the seed language */ @@ -2980,6 +3010,9 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable value2.SetUint(1); json.AddMember("encrypted_secret_keys", value2, json.GetAllocator()); + value.SetString(m_device_name.c_str(), m_device_name.size()); + json.AddMember("device_name", value, json.GetAllocator()); + // Serialize the JSON object rapidjson::StringBuffer buffer; rapidjson::Writer writer(buffer); @@ -3088,6 +3121,7 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_ m_ignore_fractional_outputs = true; m_subaddress_lookahead_major = SUBADDRESS_LOOKAHEAD_MAJOR; m_subaddress_lookahead_minor = SUBADDRESS_LOOKAHEAD_MINOR; + m_device_name = ""; m_key_on_device = false; encrypted_secret_keys = false; } @@ -3219,8 +3253,15 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_ m_subaddress_lookahead_major = field_subaddress_lookahead_major; GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, subaddress_lookahead_minor, uint32_t, Uint, false, SUBADDRESS_LOOKAHEAD_MINOR); m_subaddress_lookahead_minor = field_subaddress_lookahead_minor; + GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, encrypted_secret_keys, uint32_t, Uint, false, false); encrypted_secret_keys = field_encrypted_secret_keys; + + GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, device_name, std::string, String, false, std::string()); + if (m_device_name.empty() && field_device_name_found) + { + m_device_name = field_device_name; + } } else { @@ -3231,7 +3272,8 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_ r = epee::serialization::load_t_from_binary(m_account, account_data); if (r && m_key_on_device) { LOG_PRINT_L0("Account on device. Initing device..."); - hw::device &hwdev = hw::get_device("Ledger"); + hw::device &hwdev = hw::get_device(m_device_name); + hwdev.set_name(m_device_name); hwdev.init(); hwdev.connect(); m_account.set_device(hwdev); @@ -3673,13 +3715,18 @@ void wallet2::restore(const std::string& wallet_, const epee::wipeable_string& p THROW_WALLET_EXCEPTION_IF(boost::filesystem::exists(m_keys_file, ignored_ec), error::file_exists, m_keys_file); } m_key_on_device = true; - m_account.create_from_device(device_name); + + auto &hwdev = hw::get_device(device_name); + hwdev.set_name(device_name); + + m_account.create_from_device(hwdev); m_account_public_address = m_account.get_keys().m_account_address; m_watch_only = false; m_multisig = false; m_multisig_threshold = 0; m_multisig_signers.clear(); setup_keys(password); + m_device_name = device_name; create_keys_file(wallet_, false, password, m_nettype != MAINNET || create_address_file); if (m_subaddress_lookahead_major == SUBADDRESS_LOOKAHEAD_MAJOR && m_subaddress_lookahead_minor == SUBADDRESS_LOOKAHEAD_MINOR) diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 402066b5088..2f833c679f3 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -172,6 +172,7 @@ namespace tools static bool has_testnet_option(const boost::program_options::variables_map& vm); static bool has_stagenet_option(const boost::program_options::variables_map& vm); + static std::string device_name_option(const boost::program_options::variables_map& vm); static void init_options(boost::program_options::options_description& desc_params); //! Uses stdin and stdout. Returns a wallet2 if no errors. @@ -709,6 +710,7 @@ namespace tools bool has_unknown_key_images() const; bool get_multisig_seed(epee::wipeable_string& seed, const epee::wipeable_string &passphrase = std::string(), bool raw = true) const; bool key_on_device() const { return m_key_on_device; } + bool reconnect_device(); // locked & unlocked balance of given or current subaddress account uint64_t balance(uint32_t subaddr_index_major) const; @@ -938,6 +940,8 @@ namespace tools void ignore_fractional_outputs(bool value) { m_ignore_fractional_outputs = value; } bool confirm_non_default_ring_size() const { return m_confirm_non_default_ring_size; } void confirm_non_default_ring_size(bool always) { m_confirm_non_default_ring_size = always; } + const std::string & device_name() const { return m_device_name; } + void device_name(const std::string & device_name) { m_device_name = device_name; } bool get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key, std::vector &additional_tx_keys) const; void set_tx_key(const crypto::hash &txid, const crypto::secret_key &tx_key, const std::vector &additional_tx_keys); @@ -1319,6 +1323,7 @@ namespace tools NodeRPCProxy m_node_rpc_proxy; std::unordered_set m_scanned_pool_txs[2]; size_t m_subaddress_lookahead_major, m_subaddress_lookahead_minor; + std::string m_device_name; // Light wallet bool m_light_wallet; /* sends view key to daemon for scanning */ From b1f05f589e4d879c14d3bf4fd294a355862c505a Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 14 Sep 2018 16:31:24 +0000 Subject: [PATCH 0410/1404] blockchain_blackball: error out on fgets error --- src/blockchain_utilities/blockchain_blackball.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/blockchain_utilities/blockchain_blackball.cpp b/src/blockchain_utilities/blockchain_blackball.cpp index d9a179f641c..c80dd586280 100644 --- a/src/blockchain_utilities/blockchain_blackball.cpp +++ b/src/blockchain_utilities/blockchain_blackball.cpp @@ -898,7 +898,11 @@ static std::vector> load_outputs(const std::string while (1) { char s[256]; - fgets(s, sizeof(s), f); + if (!fgets(s, sizeof(s), f)) + { + MERROR("Error reading from " << filename << ": " << strerror(errno)); + break; + } if (feof(f)) break; const size_t len = strlen(s); From db3db0930efff25932290912951ed8d5e27f24a5 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 14 Sep 2018 17:11:11 +0000 Subject: [PATCH 0411/1404] blockchain_blackball: use mdb_size_t where appropriate --- src/blockchain_utilities/blockchain_blackball.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/blockchain_utilities/blockchain_blackball.cpp b/src/blockchain_utilities/blockchain_blackball.cpp index c80dd586280..e4a51381fe2 100644 --- a/src/blockchain_utilities/blockchain_blackball.cpp +++ b/src/blockchain_utilities/blockchain_blackball.cpp @@ -558,7 +558,7 @@ static std::vector get_spent_outputs(MDB_txn *txn) int dbr = mdb_cursor_open(txn, dbi_spent, &cur); CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to open cursor for spent outputs: " + std::string(mdb_strerror(dbr))); MDB_val k, v; - uint64_t count = 0; + mdb_size_t count = 0; dbr = mdb_cursor_get(cur, &k, &v, MDB_FIRST); if (dbr != MDB_NOTFOUND) { From 9bdc9109fb0303b17afa2af615decf79eba3159e Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 14 Sep 2018 17:11:58 +0000 Subject: [PATCH 0412/1404] blockchain_blackball: use %zu for size_t --- src/blockchain_utilities/blockchain_blackball.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/blockchain_utilities/blockchain_blackball.cpp b/src/blockchain_utilities/blockchain_blackball.cpp index e4a51381fe2..3b3bc72b6f1 100644 --- a/src/blockchain_utilities/blockchain_blackball.cpp +++ b/src/blockchain_utilities/blockchain_blackball.cpp @@ -972,7 +972,7 @@ static bool export_spent_outputs(MDB_cursor *cur, const std::string &filename) if (pending_offsets.size() == 1) fprintf(f, "%" PRIu64 "\n", pending_offsets.front()); else - fprintf(f, "%" PRIu64 "*%" PRIu64 "\n", pending_offsets.front(), pending_offsets.size()); + fprintf(f, "%" PRIu64 "*%zu\n", pending_offsets.front(), pending_offsets.size()); pending_offsets.clear(); } if (pending_amount != amount) @@ -987,7 +987,7 @@ static bool export_spent_outputs(MDB_cursor *cur, const std::string &filename) if (pending_offsets.size() == 1) fprintf(f, "%" PRIu64 "\n", pending_offsets.front()); else - fprintf(f, "%" PRIu64 "*%" PRIu64 "\n", pending_offsets.front(), pending_offsets.size()); + fprintf(f, "%" PRIu64 "*%zu\n", pending_offsets.front(), pending_offsets.size()); pending_offsets.clear(); } fclose(f); From ff72200d0ab2c32dac61aac31cf70ce6c9de0077 Mon Sep 17 00:00:00 2001 From: Gingeropolous Date: Fri, 14 Sep 2018 15:59:27 -0400 Subject: [PATCH 0413/1404] update readme fork table with consensus rules and block heights --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 637be8ed720..7f8edf877c2 100644 --- a/README.md +++ b/README.md @@ -108,7 +108,9 @@ Dates are provided in the format YYYY-MM-DD. | 1288616 | 2017-04-15 | v5 | v0.10.3.0 | v0.10.3.1 | Adjusted minimum blocksize and fee algorithm | | 1400000 | 2017-09-16 | v6 | v0.11.0.0 | v0.11.0.0 | Allow only RingCT transactions, allow only >= ringsize 5 | | 1546000 | 2018-04-06 | v7 | v0.12.0.0 | v0.12.3.0 | Cryptonight variant 1, ringsize >= 7, sorted inputs -| XXXXXXX | 2018-10-XX | XX | XXXXXXXXX | XXXXXXXXX | X +| 1685555 | 2018-10-18 | v8 | v0.13.0.0 | v0.13.0.0 | max transaction size at half the penalty free block size, bulletproofs enabled, cryptonight variant 2, fixed ringsize [11](https://youtu.be/KOO5S4vxi0o) +| 1686275 | 2018-10-19 | v9 | v0.13.0.0 | v0.13.0.0 | bulletproofs required +| XXXXXXX | 2019-04-XX | XX | XXXXXXXXX | XXXXXXXXX | X X's indicate that these details have not been determined as of commit date. From 4cb1d879f6436f6bae96cc8f115ecd04398fdf35 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 14 Sep 2018 20:08:52 +0000 Subject: [PATCH 0414/1404] blockchain_blackball: s/return false;/return 0;/ --- src/blockchain_utilities/blockchain_blackball.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/blockchain_utilities/blockchain_blackball.cpp b/src/blockchain_utilities/blockchain_blackball.cpp index 3b3bc72b6f1..1c6e54d1086 100644 --- a/src/blockchain_utilities/blockchain_blackball.cpp +++ b/src/blockchain_utilities/blockchain_blackball.cpp @@ -1400,7 +1400,7 @@ int main(int argc, char* argv[]) if (stop_requested) { MINFO("Stopping secondary passes. Secondary passes are not incremental, they will re-run fully."); - return false; + return 0; } } if (!blackballs.empty()) From c336d0f217bbb8a8417ebcb362abc9359fcc5d43 Mon Sep 17 00:00:00 2001 From: jcktm Date: Sat, 15 Sep 2018 00:03:20 +1000 Subject: [PATCH 0415/1404] add daemonizer to rpc wallet --- src/wallet/CMakeLists.txt | 2 + src/wallet/wallet_rpc_server.cpp | 275 ++++++++++++++++++------------- 2 files changed, 166 insertions(+), 111 deletions(-) diff --git a/src/wallet/CMakeLists.txt b/src/wallet/CMakeLists.txt index a16f4fe1900..be10b9f6241 100644 --- a/src/wallet/CMakeLists.txt +++ b/src/wallet/CMakeLists.txt @@ -90,6 +90,8 @@ target_link_libraries(wallet_rpc_server cncrypto common version + daemonizer + ${EPEE_READLINE} ${Boost_CHRONO_LIBRARY} ${Boost_PROGRAM_OPTIONS_LIBRARY} ${Boost_FILESYSTEM_LIBRARY} diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index ee86587bc9c..121c675497f 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -51,6 +51,7 @@ using namespace epee; #include "mnemonics/electrum-words.h" #include "rpc/rpc_args.h" #include "rpc/core_rpc_server_commands_defs.h" +#include "daemonizer/daemonizer.h" #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "wallet.rpc" @@ -3265,12 +3266,172 @@ namespace tools //------------------------------------------------------------------------------------------------------------------------------ } +class t_daemon +{ +private: + const boost::program_options::variables_map& vm; + +public: + t_daemon(boost::program_options::variables_map const & _vm) + : vm(_vm) + { + } + + bool run() + { + std::unique_ptr wal; + try + { + const bool testnet = tools::wallet2::has_testnet_option(vm); + const bool stagenet = tools::wallet2::has_stagenet_option(vm); + if (testnet && stagenet) + { + MERROR(tools::wallet_rpc_server::tr("Can't specify more than one of --testnet and --stagenet")); + return false; + } + + const auto arg_wallet_file = wallet_args::arg_wallet_file(); + const auto arg_from_json = wallet_args::arg_generate_from_json(); + + const auto wallet_file = command_line::get_arg(vm, arg_wallet_file); + const auto from_json = command_line::get_arg(vm, arg_from_json); + const auto wallet_dir = command_line::get_arg(vm, arg_wallet_dir); + const auto prompt_for_password = command_line::get_arg(vm, arg_prompt_for_password); + const auto password_prompt = prompt_for_password ? password_prompter : nullptr; + + if(!wallet_file.empty() && !from_json.empty()) + { + LOG_ERROR(tools::wallet_rpc_server::tr("Can't specify more than one of --wallet-file and --generate-from-json")); + return false; + } + + if (!wallet_dir.empty()) + { + wal = NULL; + goto just_dir; + } + + if (wallet_file.empty() && from_json.empty()) + { + LOG_ERROR(tools::wallet_rpc_server::tr("Must specify --wallet-file or --generate-from-json or --wallet-dir")); + return false; + } + + LOG_PRINT_L0(tools::wallet_rpc_server::tr("Loading wallet...")); + if(!wallet_file.empty()) + { + wal = tools::wallet2::make_from_file(vm, true, wallet_file, password_prompt).first; + } + else + { + try + { + wal = tools::wallet2::make_from_json(vm, true, from_json, password_prompt); + } + catch (const std::exception &e) + { + MERROR("Error creating wallet: " << e.what()); + return false; + } + } + if (!wal) + { + return false; + } + + bool quit = false; + tools::signal_handler::install([&wal, &quit](int) { + assert(wal); + quit = true; + wal->stop(); + }); + + wal->refresh(wal->is_trusted_daemon()); + // if we ^C during potentially length load/refresh, there's no server loop yet + if (quit) + { + MINFO(tools::wallet_rpc_server::tr("Saving wallet...")); + wal->store(); + MINFO(tools::wallet_rpc_server::tr("Successfully saved")); + return false; + } + MINFO(tools::wallet_rpc_server::tr("Successfully loaded")); + } + catch (const std::exception& e) + { + LOG_ERROR(tools::wallet_rpc_server::tr("Wallet initialization failed: ") << e.what()); + return false; + } + just_dir: + tools::wallet_rpc_server wrpc; + if (wal) wrpc.set_wallet(wal.release()); + bool r = wrpc.init(&vm); + CHECK_AND_ASSERT_MES(r, false, tools::wallet_rpc_server::tr("Failed to initialize wallet RPC server")); + tools::signal_handler::install([&wrpc](int) { + wrpc.send_stop_signal(); + }); + LOG_PRINT_L0(tools::wallet_rpc_server::tr("Starting wallet RPC server")); + try + { + wrpc.run(); + } + catch (const std::exception &e) + { + LOG_ERROR(tools::wallet_rpc_server::tr("Failed to run wallet: ") << e.what()); + return false; + } + LOG_PRINT_L0(tools::wallet_rpc_server::tr("Stopped wallet RPC server")); + try + { + LOG_PRINT_L0(tools::wallet_rpc_server::tr("Saving wallet...")); + wrpc.stop(); + LOG_PRINT_L0(tools::wallet_rpc_server::tr("Successfully saved")); + } + catch (const std::exception& e) + { + LOG_ERROR(tools::wallet_rpc_server::tr("Failed to save wallet: ") << e.what()); + return false; + } + return true; + } +}; + +class t_executor final +{ +public: + static std::string const NAME; + + std::string const & name() + { + return NAME; + } + + t_daemon create_daemon(boost::program_options::variables_map const & vm) + { + return t_daemon(vm); + } + + bool run_non_interactive(boost::program_options::variables_map const & vm) + { + return t_daemon(vm).run(); + } + + bool run_interactive(boost::program_options::variables_map const & vm) + { + return t_daemon(vm).run(); + } +}; + +std::string const t_executor::NAME = "Wallet RPC Daemon"; + int main(int argc, char** argv) { namespace po = boost::program_options; const auto arg_wallet_file = wallet_args::arg_wallet_file(); const auto arg_from_json = wallet_args::arg_generate_from_json(); + po::options_description hidden_options("Hidden"); + po::options_description desc_params(wallet_args::tr("Wallet options")); tools::wallet2::init_options(desc_params); command_line::add_arg(desc_params, arg_rpc_bind_port); @@ -3282,6 +3443,8 @@ int main(int argc, char** argv) { command_line::add_arg(desc_params, arg_wallet_dir); command_line::add_arg(desc_params, arg_prompt_for_password); + daemonizer::init_options(hidden_options, desc_params); + boost::optional vm; bool should_terminate = false; std::tie(vm, should_terminate) = wallet_args::main( @@ -3303,115 +3466,5 @@ int main(int argc, char** argv) { return 0; } - std::unique_ptr wal; - try - { - const bool testnet = tools::wallet2::has_testnet_option(*vm); - const bool stagenet = tools::wallet2::has_stagenet_option(*vm); - if (testnet && stagenet) - { - MERROR(tools::wallet_rpc_server::tr("Can't specify more than one of --testnet and --stagenet")); - return 1; - } - - const auto wallet_file = command_line::get_arg(*vm, arg_wallet_file); - const auto from_json = command_line::get_arg(*vm, arg_from_json); - const auto wallet_dir = command_line::get_arg(*vm, arg_wallet_dir); - const auto prompt_for_password = command_line::get_arg(*vm, arg_prompt_for_password); - const auto password_prompt = prompt_for_password ? password_prompter : nullptr; - - if(!wallet_file.empty() && !from_json.empty()) - { - LOG_ERROR(tools::wallet_rpc_server::tr("Can't specify more than one of --wallet-file and --generate-from-json")); - return 1; - } - - if (!wallet_dir.empty()) - { - wal = NULL; - goto just_dir; - } - - if (wallet_file.empty() && from_json.empty()) - { - LOG_ERROR(tools::wallet_rpc_server::tr("Must specify --wallet-file or --generate-from-json or --wallet-dir")); - return 1; - } - - LOG_PRINT_L0(tools::wallet_rpc_server::tr("Loading wallet...")); - if(!wallet_file.empty()) - { - wal = tools::wallet2::make_from_file(*vm, true, wallet_file, password_prompt).first; - } - else - { - try - { - wal = tools::wallet2::make_from_json(*vm, true, from_json, password_prompt); - } - catch (const std::exception &e) - { - MERROR("Error creating wallet: " << e.what()); - return 1; - } - } - if (!wal) - { - return 1; - } - - bool quit = false; - tools::signal_handler::install([&wal, &quit](int) { - assert(wal); - quit = true; - wal->stop(); - }); - - wal->refresh(wal->is_trusted_daemon()); - // if we ^C during potentially length load/refresh, there's no server loop yet - if (quit) - { - MINFO(tools::wallet_rpc_server::tr("Saving wallet...")); - wal->store(); - MINFO(tools::wallet_rpc_server::tr("Successfully saved")); - return 1; - } - MINFO(tools::wallet_rpc_server::tr("Successfully loaded")); - } - catch (const std::exception& e) - { - LOG_ERROR(tools::wallet_rpc_server::tr("Wallet initialization failed: ") << e.what()); - return 1; - } -just_dir: - tools::wallet_rpc_server wrpc; - if (wal) wrpc.set_wallet(wal.release()); - bool r = wrpc.init(&(vm.get())); - CHECK_AND_ASSERT_MES(r, 1, tools::wallet_rpc_server::tr("Failed to initialize wallet RPC server")); - tools::signal_handler::install([&wrpc](int) { - wrpc.send_stop_signal(); - }); - LOG_PRINT_L0(tools::wallet_rpc_server::tr("Starting wallet RPC server")); - try - { - wrpc.run(); - } - catch (const std::exception &e) - { - LOG_ERROR(tools::wallet_rpc_server::tr("Failed to run wallet: ") << e.what()); - return 1; - } - LOG_PRINT_L0(tools::wallet_rpc_server::tr("Stopped wallet RPC server")); - try - { - LOG_PRINT_L0(tools::wallet_rpc_server::tr("Saving wallet...")); - wrpc.stop(); - LOG_PRINT_L0(tools::wallet_rpc_server::tr("Successfully saved")); - } - catch (const std::exception& e) - { - LOG_ERROR(tools::wallet_rpc_server::tr("Failed to save wallet: ") << e.what()); - return 1; - } - return 0; + return daemonizer::daemonize(argc, const_cast(argv), t_executor{}, *vm) ? 0 : 1; } From 27af46c9308b98b5f282ec50ae1d953cff025766 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 15 Sep 2018 09:25:17 +0000 Subject: [PATCH 0416/1404] tests: update test wallet keys file for encrypted keys --- tests/data/wallet_9svHk1.keys | Bin 759 -> 1271 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/tests/data/wallet_9svHk1.keys b/tests/data/wallet_9svHk1.keys index 3159e200b22224c51f4d7bec8f6cac4c4b6e5aae..945283f001cf277d40361865268e06249c9c3100 100644 GIT binary patch literal 1271 zcmV9~X49+L*35hlIcB16pvp{5{p}Zkg_#~O4UwOBChdsOJ zko3XT2j>Qpr9V=Puv4Lx*gYV~p13hMs;#aCC&vYU*HkO{!kW+9uvHui|` zCRSEB${KT$+e4Fwf$*k6Loh5mt9mH)nt%BOQ^FZ9tHZ+fz#vRS1hwb@>{IcJK{PP; zD9AQOPxg5(P1sl5IljA_W6^Z44Fb40<6DnNy#VG8&_m-D1d_jEj*MKg$j zA#i1T4{x836SYh=7!L91{_D&u+Ft=ffK*bV=m-LGz*qL8mC%%Ss=u-^Q80_lp8+FC ztxD!~Qx4m2FTQCRI+*N+>E88Ui-?^BBH79y_~QbW`HEa7wU!o2g%9w>zW_~Sf-C>kEvcW^~gDHVr1TK2}tsZP)T z|49#ZHAu0-?-6wLjU&G#q#Rxetx|?&Dc%SsB+9k6>@P$~!Pasw`2s`JoKBSJz|244{;T;j}G$mZZ)SL*K}eR zrP00T@a9>NV4v-ZZ0$H-Np?wey&F~^Yc40Hp<4v8h^wN>-I|m)i1QMb)1e=i^Ck^3 z6BF#++n1Q_0oiERK+%Z-X(>c)!=AEe1lk15*>bS``5DkPISr{r$@xvB(M9wuZ3br0 z+Sp@=uH#)jmxOZ(bXX^N5G}mebN>4Qj8~(#zn!(3(9BOQ1dK#^$Et!bIywc%rn5tL z-5ES}{bd3v4bh10Cg8;!Ic4DM*+Z6A->Kp(J)%D;-a;i~yP$)?IngG_cHdpGWiLk< zC8XTod?N?%q875}dhG(4qA*=!nAh(LL+IpOpsT(Rg^1Pvi(qdW{_*h~sJe&{%Jze- zw`QCCkpDlxEav4M*|q(Y%aR5bQ++`EPu2By$Xz4aV3>>dq^>H^Eaes|91$aziZD%Du6X_Z2SPue=Z$PeGzav<33&T>%a;s-E48tEr zs72m+LOk`h+DTNzVlUyNE;6~(d9@l_iqc1M}fot z-mcyt_v|DKuYkxjUDXa5R%mQ~=697bs{uNWac=b%G3ni)<9{`N8oRsMe+V$`G$dOQi-tX3|6mMYwUW66X6~ z=#`^%pl@VjI0|$dQ98Ox*oLSm1Va7J<-y@5D0y*^PTu%^G_WL>-2+h!qnDW_plgad zT7mMbFbjKfucN<|${&KKZMc-6-Zt!@NQ3S0%@gvJcZbL8mViT|6L(1BZ*!-efR~m5 zFCHgloM7$1yX}KA%_(%a`O0k`KMK^%gHs(^>J&g4h*yVP@K;$@jgCQDu+<~6j;%tl zDGl(D{6!6vYKUIXPZ7q#-?i{Nl8Ueg?CHo*-$8pXf}Cr%7_k56erK*T?3=5m(RYL; z#-6MJSNYUj6p|F504*A&sT?m0o!XobvpU>fbR7$ruhm_P(1~O>Zho`Te#gd>EGtR- z)3(bW*UkJHZKr!+*AAVh(Vc8aJp+b%5Ab7!#c!l#o|IVBT}XdXT7^gpw9er5qLkn= zzP6HM*@0@9q_Xu-eJ^YIUjr&={CsqYoZlhMaKh6)b)ZWngE*ie0gVomf1cUhGQfZ0 pq1Xu5O&NRpP{+83EGgxNUG`Ty-{ Date: Sat, 15 Sep 2018 16:11:14 +0000 Subject: [PATCH 0417/1404] unit_tests: sanity check on transaction weight --- tests/unit_tests/bulletproofs.cpp | 32 +++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/tests/unit_tests/bulletproofs.cpp b/tests/unit_tests/bulletproofs.cpp index 45075c63da3..ac6eaca0b66 100644 --- a/tests/unit_tests/bulletproofs.cpp +++ b/tests/unit_tests/bulletproofs.cpp @@ -34,6 +34,8 @@ #include "ringct/rctOps.h" #include "ringct/rctSigs.h" #include "ringct/bulletproofs.h" +#include "cryptonote_basic/blobdatatype.h" +#include "cryptonote_basic/cryptonote_format_utils.h" #include "device/device.hpp" #include "misc_log_ex.h" @@ -244,3 +246,33 @@ TEST(bulletproofs, invalid_torsion) proof.T2 = org_T2; } } + +TEST(bulletproof, weight_equal) +{ + static const char *tx_hex = "02000102000b849b08f2b70b9891019707a8081bc7040d9f0b55d3019669afc83528a6e18454cf13ca392a581098c067df30e66dee8aaddf14c61a8f020002775faa070d3b3ab1d9de66deb402f635aca2580191bce277c26fef7c00cb3f3500025c9c10a978bfe085d42a7b73980f53eab4cbfde73d8023e21978ec8a467375e22101a340cd8bc95636a0ba6ffe5ebfda5eb637d44ad73c32150a469008cb870d22aa03d0cca632f376c5417327569d497d42f09386c5dd4b5efecd9dd20719861ef5aed810e70d824e8e77189c35e6d79993eeeea77b219106df29dd9e77370e7f2fb5ead175064ba8a59397a3ce6804bde23b4d90039c5ad4d1282bc23f791221bc185d70b30d84dda556348a3b9af09513946a03c190b9c53fbeb970a286b1ff8d462630ef0a2737ff40f238461e8ed3eedb8f2a01492abcb96e116ae9d51c4b35e9ba2f3bbe78228618f17a5708c0e30a47b7ed15d4a20ded508f9daddd92e07c6e74167cdf0100000099c4e562de6abd309b4cc26ab41aac39eb0eb252468f79bc5369eae8ba7f94ef2d795fb6b61a0e69e6a95dd3e257615188e80bc1c90c5f571028bb9d2b99c13d41a1e1a770e592ae7a9cda9014f6d4f3233d30f062b774a7241b6e0bb0b83b4a3e36200234a288fcf65cf8a35dfd7710dc5ece5d7abb5ec58451f1cbd41513b1bb6190c609c25e2a2b94eadfe22e8a9eb28ea3d16fa49cb1eb4d7f5c3706b50e7ae60cedf6af2c3e8dc8f96113c029749ae2b266090cc2e6650cf0a869f6c20b0792987702834ff278516dccbd3cff94a6ff36361178a302b37a62c9134b50739228430306ff2bc6a6d282d4cfa9bf6b92486f0e0dd594f2334296e248514c28436b3e86f9d527a8b1ed9f6ed09fa48514364df41d50cb3d376b71b3585cad9de30c465302ae91818ce42eb77e26a31242b4f1255f455df49409197a6d0e468f2c2d781684bb697a785ac77d41950901e9b67a2a4d6a3ec05fffec9e3a0313c972120ac3f5e01f1bc595438d7e07ff6de4ede96915a8696bcbaf449fae978565eceaebe2c3bd2f8315c535ff25fa8924fc2d49e0cb7ecc1c3fd72ce821513fa113078fda233e1588022c6267ba2f78a8a4f9ac8c7ea2dc4dca464902f46fb92702db8d26afa628f2aa182c2b34768a2b0581e7196ce041e73924af51d713db75093bf292e4263be8fc08a0b2f531e1a10ce79b95ab1fab726478cea8e79e0313ffc895069938ecf7ed14a037577f4f461ae6cde9bae6ade8a1d9e46040321b250d7ff9f3612b278757717596040dc58e7f68687b72c1ba71f36daeeb7ebdcbfd77d3518dff7d0fee252887ee38db33dffd714924d5823c539288d581eba17053beb273a13ca6f43132da705308bdc53c80c45e347bffb5c1fae7907369598660ce2c70d34083fec197b914c3b77f50e57ec54d89d0031df92a1241d40f9ea3ed14008ecc339323118ad22adca5c56687f854bc5fd47a3223016eee46e7d94b31a101df22d87b1404bbceaaaab2a8bde72aa318d3364e8926119d792cad21e51faf0cbd5ea0bbe939c5bcfbaa489dfda38aa124f3fc007b9e58f55ad8acd25d17a40bd4c1c17e03610fecb789702b0b8a4aa3a79028a7292212c550dec72f2c356f02bc0f2a0513ae07892143b8aa5ab30e9f6d71eeb3df2ea64a839b5b857000db043bf506a26953a909116b10cdce03a27d549db2f51f9a341c721bb0e442b5d0034038fbb0cd2ef27fb48f5acbd6b4104af18a98a1692d10d59884fcd2eb4641000ac32df57b5dcf387c4c097e5e7e702b2f07cdb18a69d5c69a5f7e135a9f8e020670758a1e4d955878de2f93181adfddd8cff4d20365c4663e870ff09d6b15065bbd81555d6aeb92e07ebbeae426cd0ab982a03ffeec31627ae140cd1e78f60ab6a55811d9d4051d50050c9e920e0b11c526530e613e0d3f925271f90ef0990e3df2c46170153e553a0035c0e8e87d957f40f072fd6b1ff30ee7aca3af88c40f1c255b3546dba9d23f352c729a0466729918336560df233843734e7dad57960f8d5592a299f6b762efdbd37aa0ff5310c940d03622023146a042079c8097fe01606594ab3578d0c0a90f8088d5c93504896ed80e809d22bf9483bf62398feb06099904cc23480b27709845ef1e26059d4730aeb5c2bb34c2ff34bff3c1a1c10a5898584fac078225bd435541fd2f4244e14118c8a08af7a3027d41b7af62420d12ba05466f905fe49882db44994180a1a549acfec42549254feda65aa6ee0c0e35e5a7525ae373ea0053fd536d4b6605ee833a0fa85e863807c30f02b46fde0305864da7d10f60b44ec1c2944a45de27912a39cebdc0ae18034397e4f5cfaf0ebe9ea5b225e80075f1bf6ac2211b7512870cc556e685a2464bf91100b36e5d0ea64af85d92d2aa1c2625e5bcbe93352a92dec8d735e54a2e6dfba6a91cc7c40e5c883d932769ce2d57b21ba898a2437ae6a39cfda1f3adefab0241548ad88104cbf113df4d1a243a5ae639b75169ae60b2c0dd1091a994e2a4d6d3536e3f4405a723c50ba4e9f822a2de189fd8158b0aa94c4b6255e5d4b504f789e4036d4206e8afd25693198f7bb3b04c23a6dc83f09260ae7c83726d4d524e7f9f851c39f5"; + cryptonote::blobdata bd; + ASSERT_TRUE(epee::string_tools::parse_hexstr_to_binbuff(std::string(tx_hex), bd)); + cryptonote::transaction tx; + crypto::hash tx_hash, tx_prefix_hash; + ASSERT_TRUE(parse_and_validate_tx_from_blob(bd, tx, tx_hash, tx_prefix_hash)); + ASSERT_TRUE(tx.version == 2); + ASSERT_TRUE(rct::is_rct_bulletproof(tx.rct_signatures.type)); + const uint64_t tx_size = bd.size(); + const uint64_t tx_weight = cryptonote::get_transaction_weight(tx); + ASSERT_TRUE(tx_weight == tx_size); // it has two outputs, <= 2 makes weight == size +} + +TEST(bulletproof, weight_more) +{ + static const char *tx_hex = "02000102000be98714944aeb01c006c80cbd0aaa04e5023e9003fa089669afc83528a6e18454cf13ca392a581098c067df30e66dee8aaddf14c61a8f040002377a0483ad63d58e7667a8325349c89e41e9ad5dce5aef30204fd4a6dc8eb7a100022a518afc3d690a992150646c559a24add698c98e87e732244cf2855cb7d0cff10002485a8de9d099c96fce8f26ad320cd627a6bb188f719c380517861c031aa65011000218ebf7b40a5ba25fb98ad7c6543239c2a3343b9e7cfd5140280e587c8f930f0d2101b330267408724dcf7fc2902e7d74a7962995e7905cc5d043fa1c8c6379e0eccd03e089bb498d0fffca61afd0d61f4875e8b32fa63f8729c654bba5f167199b7b518433680242640504c7568d273b2a74fe2d204dbb97eea4724ee5a2cf19eb3349ec3a2602a244de2a33c62bbfaa0b4cb85bf36863f765b237138929e43462e4bf19684d0924fd73c30bee474f0e927e8eb84dd6cc987acaf41e19e2f1e07381bd95e0f504964c8d10793972e88d64683a4a3960a9645735a76cc62d99e7a87b62c3cb590ca9dc27f91e9103c1b55ece5d5a932a04c99bf019463455b5d78397bd2295be075af5ae9bf0e43e724e11f83f336ca3c1bd9601c7fd6642795e8618b5c5b9d0045a6766daf2118f994b418504c6939b94c72e875423989ea7069d73e8d02f7b0bd9c1c7eb2289eaeda5fabd8142ee0ebafcbe101c58e99034d0c9ca34a703180e1dd7000e9f11cb4bcbe11f0c0041a0cc30f5b8b3bd7f2ace0266dd0282aea17f088c88e98a22f764e32507d1c900ef50b1157b49dbfda2fd9a2ea3be5182fb10fa590b464907049d88ff9c33fbe6d8b05898abf196dd097e1009d9bd1a1997830100000006ccdcc8aa53e183578656540fa393e6d9f96c07497b9dd009b48e8f990a75be18f5f37a47ff07f3c4d6427626afc5d24897ad31a98d01cc44476fba41f6bc3dc3de91d2655130090393e3ebcb7f436470edefff5aa11fa1016fecccf1824a5cdd66ea51dcd8f0193a6e507309d6a13680605febb971c3df4cceac5078be996c0d686c72627696e6961447145143e23cae2c97686524c0587b6cce7b05851b087d658a795bd22de18c0f68e824e1673c47f4b7f4ba7d4bedd95f46ffc22d9409087a58a80088679d3775e46f75dc6dc48f485a2c35a8dd60e7dabe29f656cbaad8d25a5e01d165fa9df29acd6e2471c4880d3129fd110066788de9c979f03d9c2e46ab80bf0dbd24ac6aaba2db0d723e1ba3a002efd2aebf245a2fd53767fa490244396284826b64a4a3f069f21047486a27c5ebdf802c23d8d276b9c83fa2e329aefb953ad34f382975204706c14249a496791cd3d20f4bd98d8f6325f5e7d7aa2d1f8b23361434f74584136cf1ead94365a6ce134159141fa4c68660a99ad90caa8c711abe5411dffa7132f8ce71dba63619e1410380c56766c79ff8e433eb806f49bca6f1bcb0c66dfd61c3133e7c095a11abd068b6a5774a02a5825cbd82a408d5580ee4dbe9a4ba07282f5a764279ad27f7ac27e7cccb3c76b5dd64be7bbdf3ecc4abcc29bc561e81cbc502ed3a4ce277b567a6eb09dfd8454c4d4e8c038b9dd6042a0515b0d1dfbb45585e79ca5705a22fcb3c67bc0261cc0cec6998354448e83fa7ff8706178e14a482e73719df33c9d753757131f3560391be2dd6c40391e3e7882ea07bb23c4d2d157349965082e1447e94849fde224452f6c98efa44f6438b731859fac8f49761e4447e8d34275e7dd9ae01d8550dcc75284715e026d25e9c444265fb4fee3f783ff2a2a5c414714a57525738884bbdbd8d997dbbcafcafd8e283b524bef0ded141160f47ce352b2104257b312d12594de45a0241d9753ec19e2f8603b5fc8682d72bf1de51d7f4caa7026989a7e46b9dd41075ad480df9de6a952e8562e548e3576e9c9230cb2cd0ee7f955e2d29240d7fa55b8e0b0b6c92823d9636592c460af670bb0b8714ca626497c68403793fe8495a7542c60587d117e3adb3644e62053817fa600910e2dfad97b2a7492ac6fa13c0a9a03e0ce12c3d12a09a4e22b9d0d74b9d431d53252fcbd06cb119d128646042eef81002fc6e9ff5006e06247f40f0391ad095c0d50a78863c975edbf0498e58ba7e6e0505d5eaa4d8ba2aa3f40e728f1a6aef6b2f9f5705cc3d2591bb5b878c258b4107857dc6ee75d591ea7af7b16196cd6c979a0bf819db39658491889c83a41c2c0035116fcac23ac45144731592ea0e3a11c335a278b2a6798d46828c8590b92701468e9d9c3560ed58c3ab861995aca439ee49ecf4a5b0ad160a12bd23a437f90c383095e85a95bb441bcb8dc1752e805e85d1ee0f6967ce95dcd888efa7ac440813c78b11d56d2a1b870c59d36430b10cd28bc693c4f64e769acfbcff27d8e904e0ca7e73aab5439de571b66f91fe9c05264e070aa223b68e5de763d838985e0ec0e8ac0dd0b1f6eb1e145c4473f9edda5732b1f9d3627423b5c60e055377bd044ff30017d25b3d26b5590b53d8aeaf10ce73d86fe4c40fa14e6f710f72c7da0600e7fc495a75a875d1aeee246b70cf24a8a85fba2d31f96faa42ece112b9030987ce0e735ab51eb4222a48bc51ab69d644bda77fb2aa0cd3a0477a2a2d92510103dbd58ee1c28eb20cfb31f5268f4a70a431ff4aedbfdfc59ea6709283a51902202effba960da6170b1a25c26a52890da54757c93156d250540590266eed8c00647270cb302cff7cbe4a8ed27da21dbaa303d1aea0eb152e1f6fd24dbdfaea0c5b0f5d6acb6724cf711ec3194a94f52f8cce13e1e3d1d7758d3d7e3cd37fd1011265199eb4126975687ce958dd1a75b6a71cf397fb618003e85af842dc3ff50a134411bbe18a1dea4beeb1e8d1ca5ac67f7f6ce2bbdeb2efcf6dcfdef64b360d4fb1849947800a3595e0a8029b631a06508b5d9f4f6e6a1be110524e5584f209b9db1651ddc8571102a58e7823dcf026f89d59ca213b6c6e32088d6c4967b20b28ffe86aed6c11d6aa0072691ff133d7bbc6d013629faebadc087c0f4f84d106677013893be4ca55018fbafcc2cee8be4ad0bcf1ad8762ec0c285e8c414bb204"; + cryptonote::blobdata bd; + ASSERT_TRUE(epee::string_tools::parse_hexstr_to_binbuff(std::string(tx_hex), bd)); + cryptonote::transaction tx; + crypto::hash tx_hash, tx_prefix_hash; + ASSERT_TRUE(parse_and_validate_tx_from_blob(bd, tx, tx_hash, tx_prefix_hash)); + ASSERT_TRUE(tx.version == 2); + ASSERT_TRUE(rct::is_rct_bulletproof(tx.rct_signatures.type)); + const uint64_t tx_size = bd.size(); + const uint64_t tx_weight = cryptonote::get_transaction_weight(tx); + ASSERT_TRUE(tx_weight > tx_size); // it has four outputs, > 2 makes weight > size +} From 6e6ffc0650cbe6207ed704501e90d8667ee26465 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 16 Sep 2018 13:24:19 +0000 Subject: [PATCH 0418/1404] wallet2_api: bring up to latest wallet api --- src/wallet/api/wallet.cpp | 52 +++++++++++++++++++++++++----------- src/wallet/api/wallet.h | 2 +- src/wallet/api/wallet2_api.h | 2 +- 3 files changed, 38 insertions(+), 18 deletions(-) diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index e9e9a4e6779..f64a44243d0 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -629,7 +629,7 @@ bool WalletImpl::recoverFromDevice(const std::string &path, const std::string &p m_recoveringFromDevice = true; try { - m_wallet->restore(path, password, device_name); + m_wallet->restore(path, password, device_name, false); LOG_PRINT_L1("Generated new wallet from device: " + device_name); } catch (const std::exception& e) { @@ -2095,21 +2095,36 @@ bool WalletImpl::useForkRules(uint8_t version, int64_t early_blocks) const return m_wallet->use_fork_rules(version,early_blocks); } -bool WalletImpl::blackballOutputs(const std::vector &pubkeys, bool add) +bool WalletImpl::blackballOutputs(const std::vector &outputs, bool add) { - std::vector raw_pubkeys; - raw_pubkeys.reserve(pubkeys.size()); - for (const std::string &str: pubkeys) + std::vector> raw_outputs; + raw_outputs.reserve(outputs.size()); + uint64_t amount = std::numeric_limits::max(), offset, num_offsets; + for (const std::string &str: outputs) { - crypto::public_key pkey; - if (!epee::string_tools::hex_to_pod(str, pkey)) + if (sscanf(str.c_str(), "@%" PRIu64, &amount) == 1) + continue; + if (amount == std::numeric_limits::max()) { - setStatusError(tr("Failed to parse output public key")); - return false; + setStatusError("First line is not an amount"); + return true; + } + if (sscanf(str.c_str(), "%" PRIu64 "*%" PRIu64, &offset, &num_offsets) == 2 && num_offsets <= std::numeric_limits::max() - offset) + { + while (num_offsets--) + raw_outputs.push_back(std::make_pair(amount, offset++)); + } + else if (sscanf(str.c_str(), "%" PRIu64, &offset) == 1) + { + raw_outputs.push_back(std::make_pair(amount, offset)); + } + else + { + setStatusError(tr("Invalid output: ") + str); + return false; } - raw_pubkeys.push_back(pkey); } - bool ret = m_wallet->set_blackballed_outputs(raw_pubkeys, add); + bool ret = m_wallet->set_blackballed_outputs(raw_outputs, add); if (!ret) { setStatusError(tr("Failed to set blackballed outputs")); @@ -2118,15 +2133,20 @@ bool WalletImpl::blackballOutputs(const std::vector &pubkeys, bool return true; } -bool WalletImpl::unblackballOutput(const std::string &pubkey) +bool WalletImpl::unblackballOutput(const std::string &amount, const std::string &offset) { - crypto::public_key raw_pubkey; - if (!epee::string_tools::hex_to_pod(pubkey, raw_pubkey)) + uint64_t raw_amount, raw_offset; + if (!epee::string_tools::get_xtype_from_string(raw_amount, amount)) + { + setStatusError(tr("Failed to parse output amount")); + return false; + } + if (!epee::string_tools::get_xtype_from_string(raw_offset, offset)) { - setStatusError(tr("Failed to parse output public key")); + setStatusError(tr("Failed to parse output offset")); return false; } - bool ret = m_wallet->unblackball_output(raw_pubkey); + bool ret = m_wallet->unblackball_output(std::make_pair(raw_amount, raw_offset)); if (!ret) { setStatusError(tr("Failed to unblackball output")); diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h index 0f3b1ce040c..209040157af 100644 --- a/src/wallet/api/wallet.h +++ b/src/wallet/api/wallet.h @@ -181,7 +181,7 @@ class WalletImpl : public Wallet virtual bool lightWalletLogin(bool &isNewWallet) const override; virtual bool lightWalletImportWalletRequest(std::string &payment_id, uint64_t &fee, bool &new_request, bool &request_fulfilled, std::string &payment_address, std::string &status) override; virtual bool blackballOutputs(const std::vector &pubkeys, bool add) override; - virtual bool unblackballOutput(const std::string &pubkey) override; + virtual bool unblackballOutput(const std::string &amount, const std::string &offset) override; virtual bool getRing(const std::string &key_image, std::vector &ring) const override; virtual bool getRings(const std::string &txid, std::vector>> &rings) const override; virtual bool setRing(const std::string &key_image, const std::vector &ring, bool relative) override; diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h index 5a52c6b175b..594fcc6f14a 100644 --- a/src/wallet/api/wallet2_api.h +++ b/src/wallet/api/wallet2_api.h @@ -875,7 +875,7 @@ struct Wallet virtual bool blackballOutputs(const std::vector &pubkeys, bool add) = 0; //! unblackballs an output - virtual bool unblackballOutput(const std::string &pubkey) = 0; + virtual bool unblackballOutput(const std::string &amount, const std::string &offset) = 0; //! gets the ring used for a key image, if any virtual bool getRing(const std::string &key_image, std::vector &ring) const = 0; From 05edc969c09346e6dc2a11cf233e089e98d9b01d Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 16 Sep 2018 14:28:59 +0000 Subject: [PATCH 0419/1404] simplewallet: remove obsolete transfer_original command --- src/simplewallet/simplewallet.cpp | 20 +++++--------------- src/simplewallet/simplewallet.h | 1 - 2 files changed, 5 insertions(+), 16 deletions(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index e535b6d2777..1a5b2ee31f5 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -108,8 +108,7 @@ typedef cryptonote::simple_wallet sw; tools::wallet_keys_unlocker unlocker(*m_wallet, pwd_container); enum TransferType { - TransferOriginal, - TransferNew, + Transfer, TransferLocked, }; @@ -2297,11 +2296,7 @@ simple_wallet::simple_wallet() m_cmd_binder.set_handler("bc_height", boost::bind(&simple_wallet::show_blockchain_height, this, _1), tr("Show the blockchain height.")); - m_cmd_binder.set_handler("transfer_original", - boost::bind(&simple_wallet::transfer, this, _1), - tr("transfer_original [index=[,,...]] [] [] ( |
) []"), - tr("Transfer to
using an older transaction building algorithm. If the parameter \"index=[,,...]\" is specified, the wallet uses outputs received by addresses of those indices. If omitted, the wallet randomly chooses address indices to be used. In any case, it tries its best not to combine outputs across multiple addresses. is the priority of the transaction. The higher the priority, the higher the transaction fee. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command \"set priority\") is used. is the number of inputs to include for untraceability. Multiple payments can be made at once by adding URI_2 or etcetera (before the payment ID, if it's included)")); - m_cmd_binder.set_handler("transfer", boost::bind(&simple_wallet::transfer_new, this, _1), + m_cmd_binder.set_handler("transfer", boost::bind(&simple_wallet::transfer, this, _1), tr("transfer [index=[,,...]] [] [] ( |
) []"), tr("Transfer to
. If the parameter \"index=[,,...]\" is specified, the wallet uses outputs received by addresses of those indices. If omitted, the wallet randomly chooses address indices to be used. In any case, it tries its best not to combine outputs across multiple addresses. is the priority of the transaction. The higher the priority, the higher the transaction fee. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command \"set priority\") is used. is the number of inputs to include for untraceability. Multiple payments can be made at once by adding URI_2 or etcetera (before the payment ID, if it's included)")); m_cmd_binder.set_handler("locked_transfer", @@ -4943,7 +4938,7 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vectorcreate_transactions_2(dsts, fake_outs_count, 0 /* unlock_time */, priority, extra, m_current_subaddress_account, subaddr_indices); break; } @@ -5133,12 +5128,7 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector &args_) { - return transfer_main(TransferOriginal, args_); -} -//---------------------------------------------------------------------------------------------------- -bool simple_wallet::transfer_new(const std::vector &args_) -{ - return transfer_main(TransferNew, args_); + return transfer_main(Transfer, args_); } //---------------------------------------------------------------------------------------------------- bool simple_wallet::locked_transfer(const std::vector &args_) @@ -5809,7 +5799,7 @@ bool simple_wallet::donate(const std::vector &args_) if (!payment_id_str.empty()) local_args.push_back(payment_id_str); message_writer() << (boost::format(tr("Donating %s %s to The Monero Project (donate.getmonero.org or %s).")) % amount_str % cryptonote::get_unit(cryptonote::get_default_decimal_point()) % MONERO_DONATION_ADDR).str(); - transfer_new(local_args); + transfer(local_args); return true; } //---------------------------------------------------------------------------------------------------- diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index bfbe633aca1..774be644c3f 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -152,7 +152,6 @@ namespace cryptonote bool show_blockchain_height(const std::vector &args); bool transfer_main(int transfer_type, const std::vector &args); bool transfer(const std::vector &args); - bool transfer_new(const std::vector &args); bool locked_transfer(const std::vector &args); bool locked_sweep_all(const std::vector &args); bool sweep_main(uint64_t below, bool locked, const std::vector &args); From e9bce045a44a34884e917babb103318c589df024 Mon Sep 17 00:00:00 2001 From: Howard Chu Date: Sun, 16 Sep 2018 19:35:07 +0000 Subject: [PATCH 0420/1404] Fix missing std::runtime_error def in difficulty.cpp --- tests/difficulty/difficulty.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/difficulty/difficulty.cpp b/tests/difficulty/difficulty.cpp index 736a5823714..996913a192e 100644 --- a/tests/difficulty/difficulty.cpp +++ b/tests/difficulty/difficulty.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include "cryptonote_config.h" #include "cryptonote_basic/difficulty.h" From b7660149332c5857e5ff59e1375261d41b07a042 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 17 Sep 2018 11:02:02 +0000 Subject: [PATCH 0421/1404] node_rpc_proxy: return a non empty error string on connection failure This makes it easier to avoid bugs on the caller side if errors are represented by non empty strings. This fixes the refresh height setting in new wallets when no daemon is running. --- src/wallet/node_rpc_proxy.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/wallet/node_rpc_proxy.cpp b/src/wallet/node_rpc_proxy.cpp index 21f75371b59..346c052b576 100644 --- a/src/wallet/node_rpc_proxy.cpp +++ b/src/wallet/node_rpc_proxy.cpp @@ -69,7 +69,7 @@ boost::optional NodeRPCProxy::get_rpc_version(uint32_t &rpc_version m_daemon_rpc_mutex.lock(); bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_version", req_t, resp_t, m_http_client, rpc_timeout); m_daemon_rpc_mutex.unlock(); - CHECK_AND_ASSERT_MES(r, std::string(), "Failed to connect to daemon"); + CHECK_AND_ASSERT_MES(r, std::string("Failed to connect to daemon"), "Failed to connect to daemon"); CHECK_AND_ASSERT_MES(resp_t.status != CORE_RPC_STATUS_BUSY, resp_t.status, "Failed to connect to daemon"); CHECK_AND_ASSERT_MES(resp_t.status == CORE_RPC_STATUS_OK, resp_t.status, "Failed to get daemon RPC version"); m_rpc_version = resp_t.version; @@ -95,7 +95,7 @@ boost::optional NodeRPCProxy::get_info() const bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_info", req_t, resp_t, m_http_client, rpc_timeout); m_daemon_rpc_mutex.unlock(); - CHECK_AND_ASSERT_MES(r, std::string(), "Failed to connect to daemon"); + CHECK_AND_ASSERT_MES(r, std::string("Failed to connect to daemon"), "Failed to connect to daemon"); CHECK_AND_ASSERT_MES(resp_t.status != CORE_RPC_STATUS_BUSY, resp_t.status, "Failed to connect to daemon"); CHECK_AND_ASSERT_MES(resp_t.status == CORE_RPC_STATUS_OK, resp_t.status, "Failed to get target blockchain height"); m_height = resp_t.height; @@ -144,7 +144,7 @@ boost::optional NodeRPCProxy::get_earliest_height(uint8_t version, req_t.version = version; bool r = net_utils::invoke_http_json_rpc("/json_rpc", "hard_fork_info", req_t, resp_t, m_http_client, rpc_timeout); m_daemon_rpc_mutex.unlock(); - CHECK_AND_ASSERT_MES(r, std::string(), "Failed to connect to daemon"); + CHECK_AND_ASSERT_MES(r, std::string("Failed to connect to daemon"), "Failed to connect to daemon"); CHECK_AND_ASSERT_MES(resp_t.status != CORE_RPC_STATUS_BUSY, resp_t.status, "Failed to connect to daemon"); CHECK_AND_ASSERT_MES(resp_t.status == CORE_RPC_STATUS_OK, resp_t.status, "Failed to get hard fork status"); m_earliest_height[version] = resp_t.earliest_height; @@ -171,7 +171,7 @@ boost::optional NodeRPCProxy::get_dynamic_base_fee_estimate(uint64_ req_t.grace_blocks = grace_blocks; bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_fee_estimate", req_t, resp_t, m_http_client, rpc_timeout); m_daemon_rpc_mutex.unlock(); - CHECK_AND_ASSERT_MES(r, std::string(), "Failed to connect to daemon"); + CHECK_AND_ASSERT_MES(r, std::string("Failed to connect to daemon"), "Failed to connect to daemon"); CHECK_AND_ASSERT_MES(resp_t.status != CORE_RPC_STATUS_BUSY, resp_t.status, "Failed to connect to daemon"); CHECK_AND_ASSERT_MES(resp_t.status == CORE_RPC_STATUS_OK, resp_t.status, "Failed to get fee estimate"); m_dynamic_base_fee_estimate = resp_t.fee; @@ -201,7 +201,7 @@ boost::optional NodeRPCProxy::get_fee_quantization_mask(uint64_t &f req_t.grace_blocks = m_dynamic_base_fee_estimate_grace_blocks; bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_fee_estimate", req_t, resp_t, m_http_client, rpc_timeout); m_daemon_rpc_mutex.unlock(); - CHECK_AND_ASSERT_MES(r, std::string(), "Failed to connect to daemon"); + CHECK_AND_ASSERT_MES(r, std::string("Failed to connect to daemon"), "Failed to connect to daemon"); CHECK_AND_ASSERT_MES(resp_t.status != CORE_RPC_STATUS_BUSY, resp_t.status, "Failed to connect to daemon"); CHECK_AND_ASSERT_MES(resp_t.status == CORE_RPC_STATUS_OK, resp_t.status, "Failed to get fee estimate"); m_dynamic_base_fee_estimate = resp_t.fee; From ba8dd3479d56ff084fbc4b631548ff8cf05f60d1 Mon Sep 17 00:00:00 2001 From: stoffu Date: Sat, 25 Aug 2018 14:22:26 +0900 Subject: [PATCH 0422/1404] api/wallet: properly disable key encryption --- src/gen_multisig/gen_multisig.cpp | 4 ++-- src/simplewallet/simplewallet.cpp | 2 +- src/wallet/api/wallet.cpp | 4 ++-- src/wallet/wallet2.cpp | 11 +++++------ src/wallet/wallet2.h | 4 ++-- tests/functional_tests/transactions_flow_test.cpp | 4 ++-- tests/unit_tests/multisig.cpp | 2 +- 7 files changed, 15 insertions(+), 16 deletions(-) diff --git a/src/gen_multisig/gen_multisig.cpp b/src/gen_multisig/gen_multisig.cpp index e680a8157b7..f11f442bc7a 100644 --- a/src/gen_multisig/gen_multisig.cpp +++ b/src/gen_multisig/gen_multisig.cpp @@ -91,8 +91,8 @@ static bool generate_multisig(uint32_t threshold, uint32_t total, const std::str for (size_t n = 0; n < total; ++n) { std::string name = basename + "-" + std::to_string(n + 1); - wallets[n].reset(new tools::wallet2(nettype)); - wallets[n]->init(false, ""); + wallets[n].reset(new tools::wallet2(nettype, 1, false)); + wallets[n]->init(""); wallets[n]->generate(name, pwd_container->password(), rct::rct2sk(rct::skGen()), false, false, create_address_file); } diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index e535b6d2777..3305526497e 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -4083,7 +4083,7 @@ bool simple_wallet::set_daemon(const std::vector& args) daemon_url = args[0]; } LOCK_IDLE_SCOPE(); - m_wallet->init(false, daemon_url); + m_wallet->init(daemon_url); if (args.size() == 2) { diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index e9e9a4e6779..c9814d020f0 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -376,7 +376,7 @@ WalletImpl::WalletImpl(NetworkType nettype, uint64_t kdf_rounds) , m_rebuildWalletCache(false) , m_is_connected(false) { - m_wallet = new tools::wallet2(static_cast(nettype), kdf_rounds); + m_wallet = new tools::wallet2(static_cast(nettype), kdf_rounds, true); m_history = new TransactionHistoryImpl(this); m_wallet2Callback = new Wallet2CallbackImpl(this); m_wallet->callback(m_wallet2Callback); @@ -2033,7 +2033,7 @@ bool WalletImpl::isNewWallet() const bool WalletImpl::doInit(const string &daemon_address, uint64_t upper_transaction_size_limit, bool ssl) { // claim RPC so there's no in-memory encryption for now - if (!m_wallet->init(true, daemon_address, m_daemon_login, upper_transaction_size_limit, ssl)) + if (!m_wallet->init(daemon_address, m_daemon_login, upper_transaction_size_limit, ssl)) return false; // in case new wallet, this will force fast-refresh (pulling hashes instead of blocks) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index b266821d6c8..38509ed5a2d 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -261,8 +261,8 @@ std::unique_ptr make_basic(const boost::program_options::variabl catch (const std::exception &e) { } } - std::unique_ptr wallet(new tools::wallet2(nettype, kdf_rounds)); - wallet->init(unattended, std::move(daemon_address), std::move(login), 0, false, *trusted_daemon); + std::unique_ptr wallet(new tools::wallet2(nettype, kdf_rounds, unattended)); + wallet->init(std::move(daemon_address), std::move(login), 0, false, *trusted_daemon); boost::filesystem::path ringdb_path = command_line::get_arg(vm, opts.shared_ringdb_dir); wallet->set_ring_database(ringdb_path.string()); return wallet; @@ -748,7 +748,7 @@ wallet_keys_unlocker::~wallet_keys_unlocker() w.encrypt_keys(key); } -wallet2::wallet2(network_type nettype, uint64_t kdf_rounds): +wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended): m_multisig_rescan_info(NULL), m_multisig_rescan_k(NULL), m_run(true), @@ -796,7 +796,7 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds): m_ringdb(), m_last_block_reward(0), m_encrypt_keys_after_refresh(boost::none), - m_unattended(false) + m_unattended(unattended) { } @@ -872,9 +872,8 @@ std::unique_ptr wallet2::make_dummy(const boost::program_options::varia } //---------------------------------------------------------------------------------------------------- -bool wallet2::init(bool unattended, std::string daemon_address, boost::optional daemon_login, uint64_t upper_transaction_weight_limit, bool ssl, bool trusted_daemon) +bool wallet2::init(std::string daemon_address, boost::optional daemon_login, uint64_t upper_transaction_weight_limit, bool ssl, bool trusted_daemon) { - m_unattended = unattended; m_checkpoints.init_default_checkpoints(m_nettype); if(m_http_client.is_connected()) m_http_client.disconnect(); diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 402066b5088..fe39014bad4 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -189,7 +189,7 @@ namespace tools static bool verify_password(const std::string& keys_file_name, const epee::wipeable_string& password, bool no_spend_key, hw::device &hwdev, uint64_t kdf_rounds); - wallet2(cryptonote::network_type nettype = cryptonote::MAINNET, uint64_t kdf_rounds = 1); + wallet2(cryptonote::network_type nettype = cryptonote::MAINNET, uint64_t kdf_rounds = 1, bool unattended = false); ~wallet2(); struct multisig_info @@ -638,7 +638,7 @@ namespace tools bool explicit_refresh_from_block_height() const {return m_explicit_refresh_from_block_height;} bool deinit(); - bool init(bool unatteded, std::string daemon_address = "http://localhost:8080", + bool init(std::string daemon_address = "http://localhost:8080", boost::optional daemon_login = boost::none, uint64_t upper_transaction_weight_limit = 0, bool ssl = false, bool trusted_daemon = false); void stop() { m_run.store(false, std::memory_order_relaxed); } diff --git a/tests/functional_tests/transactions_flow_test.cpp b/tests/functional_tests/transactions_flow_test.cpp index 25dcc77bc09..ffe500d21b9 100644 --- a/tests/functional_tests/transactions_flow_test.cpp +++ b/tests/functional_tests/transactions_flow_test.cpp @@ -138,7 +138,7 @@ bool transactions_flow_test(std::string& working_folder, return false; } - w1.init(true, daemon_addr_a); + w1.init(daemon_addr_a); uint64_t blocks_fetched = 0; bool received_money; @@ -149,7 +149,7 @@ bool transactions_flow_test(std::string& working_folder, return false; } - w2.init(true, daemon_addr_b); + w2.init(daemon_addr_b); MGINFO_GREEN("Using wallets: " << ENDL << "Source: " << w1.get_account().get_public_address_str(MAINNET) << ENDL << "Path: " << working_folder + "/" + path_source_wallet << ENDL diff --git a/tests/unit_tests/multisig.cpp b/tests/unit_tests/multisig.cpp index eb453b960db..83924c7afc8 100644 --- a/tests/unit_tests/multisig.cpp +++ b/tests/unit_tests/multisig.cpp @@ -61,7 +61,7 @@ static void make_wallet(unsigned int idx, tools::wallet2 &wallet) try { - wallet.init(false, ""); + wallet.init(""); wallet.set_subaddress_lookahead(1, 1); wallet.generate("", "", spendkey, true, false); ASSERT_TRUE(test_addresses[idx].address == wallet.get_account().get_public_address_str(cryptonote::TESTNET)); From ecaf5b3feb2272ee8b9aec7f10d427dc20318d22 Mon Sep 17 00:00:00 2001 From: TheCharlatan Date: Mon, 10 Sep 2018 23:22:41 +0200 Subject: [PATCH 0423/1404] Add libsodium to the packages, the arm build was complaining about it. Fixup arm toolchain file. --- CMakeLists.txt | 3 --- contrib/depends/packages/packages.mk | 2 +- contrib/depends/packages/sodium.mk | 23 +++++++++++++++++++++++ contrib/depends/toolchain.cmake.in | 15 +++++++++++---- 4 files changed, 35 insertions(+), 8 deletions(-) create mode 100644 contrib/depends/packages/sodium.mk diff --git a/CMakeLists.txt b/CMakeLists.txt index 95b57e49d6f..6d4fe96e524 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -440,8 +440,6 @@ add_definition_if_function_found(strptime HAVE_STRPTIME) add_definitions(-DAUTO_INITIALIZE_EASYLOGGINGPP) # Generate header for embedded translations -#add_subdirectory(translations) -#======= # Generate header for embedded translations, use target toolchain if depends, otherwise use the # lrelease and lupdate binaries from the host include(ExternalProject) @@ -451,7 +449,6 @@ ExternalProject_Add(generate_translations_header STAMP_DIR ${LRELEASE_PATH} CMAKE_ARGS -DLRELEASE_PATH=${LRELEASE_PATH} INSTALL_COMMAND cmake -E echo "") -#>>>>>>> b1f43170... Add lrelease to the depends include_directories("${CMAKE_CURRENT_BINARY_DIR}/translations") add_subdirectory(external) diff --git a/contrib/depends/packages/packages.mk b/contrib/depends/packages/packages.mk index d6d6b2d442c..28eaf34c568 100644 --- a/contrib/depends/packages/packages.mk +++ b/contrib/depends/packages/packages.mk @@ -1,4 +1,4 @@ -packages:=boost openssl libevent zeromq cppzmq zlib expat ldns cppzmq readline libiconv qt +packages:=boost openssl libevent zeromq cppzmq zlib expat ldns cppzmq readline libiconv qt sodium native_packages := native_ccache wallet_packages=bdb diff --git a/contrib/depends/packages/sodium.mk b/contrib/depends/packages/sodium.mk new file mode 100644 index 00000000000..885b1ea1972 --- /dev/null +++ b/contrib/depends/packages/sodium.mk @@ -0,0 +1,23 @@ +package=sodium +$(package)_version=1.0.16 +$(package)_download_path=https://github.com/jedisct1/libsodium/archive +$(package)_file_name=$($(package)_version).tar.gz +$(package)_sha256_hash=0c14604bbeab2e82a803215d65c3b6e74bb28291aaee6236d65c699ccfe1a98c + +define $(package)_set_vars +$(package)_config_opts=--enable-static +$(package)_config_opts+=--prefix=$(host_prefix) +endef + +define $(package)_config_cmds + ./autogen.sh &&\ + $($(package)_autoconf) $($(package)_config_opts) +endef + +define $(package)_build_cmds + $(MAKE) +endef + +define $(package)_stage_cmds + $(MAKE) DESTDIR=$($(package)_staging_dir) install +endef diff --git a/contrib/depends/toolchain.cmake.in b/contrib/depends/toolchain.cmake.in index 5ab93b1c321..d2fdd42707e 100644 --- a/contrib/depends/toolchain.cmake.in +++ b/contrib/depends/toolchain.cmake.in @@ -62,12 +62,19 @@ else() endif() if(ARCH STREQUAL "arm") - set(ARCH "armv6zk") - set(ARMID "armv6zk") + set(ARCH "armv7-a") + set(ARM ON) + set(ARM_ID "armv7-a") set(BUILD_64 OFF) set(CMAKE_BUILD_TYPE release) - set(BUILD_TAG "linux-armv6") - set(ARM6) + set(BUILD_TAG "linux-armv7") + set(ARM7) +elif(ARCH STREQUAL "aarch64") + set(ARCH "armv8-a") + set(ARM ON) + set(ARM_ID "armv8-a") + set(BUILD_TAG "linux-armv8") + set(BUILD_64 ON) endif() #Create a new global cmake flag that indicates building with depends From a4065bf267faf3d1c802c94b95e921732e6c2a9b Mon Sep 17 00:00:00 2001 From: OPSXCQ Date: Mon, 17 Sep 2018 14:36:33 +0000 Subject: [PATCH 0424/1404] Fixes Dockerfile build problem #4396 --- Dockerfile | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 5fde5a477c4..4ff760a5667 100644 --- a/Dockerfile +++ b/Dockerfile @@ -128,8 +128,7 @@ RUN set -ex && \ apt-get --no-install-recommends --yes install ca-certificates && \ apt-get clean && \ rm -rf /var/lib/apt - -COPY --from=builder /src/build/release/bin/* /usr/local/bin/ +COPY --from=builder /src/build/Linux/master/release/* /usr/local/bin/ # Contains the blockchain VOLUME /root/.bitmonero From 9bf0105e251fdedb5bd2cc2f04d99339d640f0af Mon Sep 17 00:00:00 2001 From: Alexandro Sanchez Date: Mon, 17 Sep 2018 16:32:55 +0200 Subject: [PATCH 0425/1404] Fixed Keccak implementation on big-endian platforms --- src/crypto/keccak.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/crypto/keccak.c b/src/crypto/keccak.c index 8fcd2138ea7..b5946036ee6 100644 --- a/src/crypto/keccak.c +++ b/src/crypto/keccak.c @@ -5,6 +5,7 @@ #include #include #include +#include "common/int-util.h" #include "hash-ops.h" #include "keccak.h" @@ -105,7 +106,7 @@ void keccak(const uint8_t *in, size_t inlen, uint8_t *md, int mdlen) for ( ; inlen >= rsiz; inlen -= rsiz, in += rsiz) { for (i = 0; i < rsizw; i++) - st[i] ^= ((uint64_t *) in)[i]; + st[i] ^= swap64le(((uint64_t *) in)[i]); keccakf(st, KECCAK_ROUNDS); } @@ -121,11 +122,15 @@ void keccak(const uint8_t *in, size_t inlen, uint8_t *md, int mdlen) temp[rsiz - 1] |= 0x80; for (i = 0; i < rsizw; i++) - st[i] ^= ((uint64_t *) temp)[i]; + st[i] ^= swap64le(((uint64_t *) temp)[i]); keccakf(st, KECCAK_ROUNDS); - memcpy(md, st, mdlen); + if (((size_t)mdlen % sizeof(uint64_t)) != 0) + { + local_abort("Bad keccak use"); + } + memcpy_swap64le(md, st, mdlen/sizeof(uint64_t)); } void keccak1600(const uint8_t *in, size_t inlen, uint8_t *md) From a06d2581c3938006df9776185521db5216712629 Mon Sep 17 00:00:00 2001 From: TheCharlatan Date: Mon, 17 Sep 2018 23:59:37 +0200 Subject: [PATCH 0426/1404] Fix Windows build icu tex support is not required, so just disable it. Re-add mistakingly removed crypt32 lib. --- CMakeLists.txt | 2 +- contrib/depends/packages/icu4c.mk | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6d4fe96e524..0de82b436be 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -829,7 +829,7 @@ endif() include_directories(SYSTEM ${Boost_INCLUDE_DIRS}) if(MINGW) set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wa,-mbig-obj") - set(EXTRA_LIBRARIES mswsock;ws2_32;iphlpapi) + set(EXTRA_LIBRARIES mswsock;ws2_32;iphlpapi;crypt32) if(DEPENDS) set(ICU_LIBRARIES ${Boost_LOCALE_LIBRARY} sicuio sicuin sicuuc sicudt sicutu iconv) else() diff --git a/contrib/depends/packages/icu4c.mk b/contrib/depends/packages/icu4c.mk index eedf22da04d..7e092425e5b 100644 --- a/contrib/depends/packages/icu4c.mk +++ b/contrib/depends/packages/icu4c.mk @@ -17,7 +17,7 @@ define $(package)_config_cmds sh ../source/runConfigureICU Linux &&\ make &&\ cd ../buildb &&\ - sh ../source/$($(package)_autoconf) --enable-static=yes --enable-shared=yes --prefix=$(host_prefix) --with-cross-build=`pwd`/../builda &&\ + sh ../source/$($(package)_autoconf) --enable-static=yes --enable-shared=yes --disable-layoutex --prefix=$(host_prefix) --with-cross-build=`pwd`/../builda &&\ $(MAKE) $($(package)_build_opts) endef From f77ce675cbf9e269d6fae09fef1385296bf708de Mon Sep 17 00:00:00 2001 From: cryptochangements34 Date: Mon, 17 Sep 2018 20:38:40 -0400 Subject: [PATCH 0427/1404] rpc: allow pruning of json encoded txs --- src/rpc/core_rpc_server.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index db7f2dbaa01..492f85ba1c6 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -220,6 +220,15 @@ namespace cryptonote return ss.str(); } //------------------------------------------------------------------------------------------------------------------------------ + static cryptonote::blobdata get_pruned_tx_json(cryptonote::transaction &tx) + { + std::stringstream ss; + json_archive ar(ss); + bool r = tx.serialize_base(ar); + CHECK_AND_ASSERT_MES(r, cryptonote::blobdata(), "Failed to serialize rct signatures base"); + return ss.str(); + } + //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_get_blocks(const COMMAND_RPC_GET_BLOCKS_FAST::request& req, COMMAND_RPC_GET_BLOCKS_FAST::response& res) { PERF_TIMER(on_get_blocks); @@ -557,7 +566,7 @@ namespace cryptonote blobdata blob = req.prune ? get_pruned_tx_blob(tx) : t_serializable_object_to_blob(tx); e.as_hex = string_tools::buff_to_hex_nodelimer(blob); if (req.decode_as_json) - e.as_json = obj_to_json_str(tx); + e.as_json = req.prune ? get_pruned_tx_json(tx) : obj_to_json_str(tx); e.in_pool = pool_tx_hashes.find(tx_hash) != pool_tx_hashes.end(); if (e.in_pool) { From 423973596ba6ce645bc578bbc4774023caa335c6 Mon Sep 17 00:00:00 2001 From: TheCharlatan Date: Tue, 18 Sep 2018 02:52:57 +0200 Subject: [PATCH 0428/1404] Fixup 32bit arm build Set the architecture in the toolchain file correctly --- CMakeLists.txt | 4 +++- contrib/depends/toolchain.cmake.in | 5 +++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0de82b436be..e62179e184c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -495,7 +495,9 @@ if(MSVC) else() include(TestCXXAcceptsFlag) if (NOT ARM6) - set(ARCH native CACHE STRING "CPU to build for: -march value or 'default' to not pass -march at all") + if(NOT DEPENDS OR DEPENDS AND NOT ARM) + set(ARCH native CACHE STRING "CPU to build for: -march value or 'default' to not pass -march at all") + endif() endif() message(STATUS "Building on ${CMAKE_SYSTEM_PROCESSOR} for ${ARCH}") if(ARCH STREQUAL "default") diff --git a/contrib/depends/toolchain.cmake.in b/contrib/depends/toolchain.cmake.in index d2fdd42707e..b594ddd6254 100644 --- a/contrib/depends/toolchain.cmake.in +++ b/contrib/depends/toolchain.cmake.in @@ -31,6 +31,7 @@ SET(Boost_USE_STATIC_LIBS TRUE) SET(Boost_USE_STATIC_RUNTIME TRUE) SET(OpenSSL_DIR @prefix@/lib) +SET(ARCHITECTURE @arch@) # for libraries and headers in the target directories set (CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) # Find programs on host @@ -61,7 +62,7 @@ else() SET(CMAKE_CXX_COMPILER @CXX@) endif() -if(ARCH STREQUAL "arm") +if(ARCHITECTURE STREQUAL "arm") set(ARCH "armv7-a") set(ARM ON) set(ARM_ID "armv7-a") @@ -69,7 +70,7 @@ if(ARCH STREQUAL "arm") set(CMAKE_BUILD_TYPE release) set(BUILD_TAG "linux-armv7") set(ARM7) -elif(ARCH STREQUAL "aarch64") +elseif(ARCHITECTURE STREQUAL "aarch64") set(ARCH "armv8-a") set(ARM ON) set(ARM_ID "armv8-a") From 0c77523d61607ada26537f36651e41762bee7cfc Mon Sep 17 00:00:00 2001 From: phloatingman Date: Fri, 29 Dec 2017 20:02:50 -0800 Subject: [PATCH 0429/1404] README: fill in libsodium package name for Arch Rebased by moneromooo, with a sensible commit message --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 637be8ed720..f18bd90814f 100644 --- a/README.md +++ b/README.md @@ -139,7 +139,7 @@ library archives (`.a`). | libzmq | 3.0.0 | NO | `libzmq3-dev` | `zeromq` | `cppzmq-devel` | NO | ZeroMQ library | | OpenPGM | ? | NO | `libpgm-dev` | `libpgm` | `openpgm-devel` | NO | For ZeroMQ | | libunbound | 1.4.16 | YES | `libunbound-dev` | `unbound` | `unbound-devel` | NO | DNS resolver | -| libsodium | ? | NO | `libsodium-dev` | ? | `libsodium-devel` | NO | cryptography | +| libsodium | ? | NO | `libsodium-dev` | `libsodium` | `libsodium-devel` | NO | cryptography | | libunwind | any | NO | `libunwind8-dev` | `libunwind` | `libunwind-devel` | YES | Stack traces | | liblzma | any | NO | `liblzma-dev` | `xz` | `xz-devel` | YES | For libunwind | | libreadline | 6.3.0 | NO | `libreadline6-dev` | `readline` | `readline-devel` | YES | Input editing | From 52e19d695517fffedabd28424756175743f511bc Mon Sep 17 00:00:00 2001 From: Italocoin Project <12392580+italocoin-project@users.noreply.github.com> Date: Sun, 25 Mar 2018 18:36:25 +0200 Subject: [PATCH 0430/1404] README: Compile boost with cxxflags=-fPIC cflags=-fPIC Rebased and squashed by moneromooo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f18bd90814f..7f5dcf2907f 100644 --- a/README.md +++ b/README.md @@ -282,7 +282,7 @@ If you are using the older Raspbian Jessie image, compiling Monero is a bit more ``` * Wait ~8 hours ``` - sudo ./bjam install + sudo ./bjam cxxflags=-fPIC cflags=-fPIC -a install ``` * Wait ~4 hours From 24f52396935f7d0624afaf88c6a4b5f2bf5e6932 Mon Sep 17 00:00:00 2001 From: whythat Date: Sun, 6 May 2018 11:38:47 +0300 Subject: [PATCH 0431/1404] wallet: add 'outputs' option for sweep_* commands 'outputs' option allows to specify the number of separate outputs of smaller denomination that will be created by sweep operation. rebased by moneromooo --- src/simplewallet/simplewallet.cpp | 52 +++++++++++++++++--- src/wallet/wallet2.cpp | 12 ++--- src/wallet/wallet2.h | 6 +-- src/wallet/wallet_rpc_server.cpp | 18 ++++++- src/wallet/wallet_rpc_server_commands_defs.h | 4 ++ 5 files changed, 74 insertions(+), 18 deletions(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index e535b6d2777..24903721a1b 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -2316,15 +2316,15 @@ simple_wallet::simple_wallet() boost::bind(&simple_wallet::sweep_unmixable, this, _1), tr("Send all unmixable outputs to yourself with ring_size 1")); m_cmd_binder.set_handler("sweep_all", boost::bind(&simple_wallet::sweep_all, this, _1), - tr("sweep_all [index=[,,...]] [] []
[]"), - tr("Send all unlocked balance to an address. If the parameter \"index[,,...]\" is specified, the wallet sweeps outputs received by those address indices. If omitted, the wallet randomly chooses an address index to be used.")); + tr("sweep_all [index=[,,...]] [] [] [outputs=]
[]"), + tr("Send all unlocked balance to an address. If the parameter \"index[,,...]\" is specified, the wallet sweeps outputs received by those address indices. If omitted, the wallet randomly chooses an address index to be used. If the parameter \"outputs=\" is specified and N > 0, wallet splits the transaction into N even outputs.")); m_cmd_binder.set_handler("sweep_below", boost::bind(&simple_wallet::sweep_below, this, _1), tr("sweep_below [index=[,,...]] [] []
[]"), tr("Send all unlocked outputs below the threshold to an address.")); m_cmd_binder.set_handler("sweep_single", boost::bind(&simple_wallet::sweep_single, this, _1), - tr("sweep_single [] []
[]"), + tr("sweep_single [] [] [outputs=]
[]"), tr("Send a single output of the given key image to an address without change.")); m_cmd_binder.set_handler("donate", boost::bind(&simple_wallet::donate, this, _1), @@ -5262,7 +5262,7 @@ bool simple_wallet::sweep_main(uint64_t below, bool locked, const std::vector[,,...]] [] []
[]")) % (below ? "sweep_below" : "sweep_all"); + fail_msg_writer() << boost::format(tr("usage: %s [index=[,,...]] [] [] [outputs=]
[]")) % (below ? "sweep_below" : "sweep_all"); }; if (args_.size() == 0) { @@ -5360,6 +5360,25 @@ bool simple_wallet::sweep_main(uint64_t below, bool locked, const std::vector 0 && local_args[0].substr(0, 8) == "outputs=") + { + if (!epee::string_tools::get_xtype_from_string(outputs, local_args[0].substr(8))) + { + fail_msg_writer() << tr("Failed to parse number of outputs"); + return true; + } + else if (outputs < 1) + { + fail_msg_writer() << tr("Amount of outputs should be greater than 0"); + return true; + } + else + { + local_args.erase(local_args.begin()); + } + } + std::vector extra; bool payment_id_seen = false; if (local_args.size() >= 2) @@ -5444,7 +5463,7 @@ bool simple_wallet::sweep_main(uint64_t below, bool locked, const std::vectorcreate_transactions_all(below, info.address, info.is_subaddress, fake_outs_count, unlock_block /* unlock_time */, priority, extra, m_current_subaddress_account, subaddr_indices); + auto ptx_vector = m_wallet->create_transactions_all(below, info.address, info.is_subaddress, outputs, fake_outs_count, unlock_block /* unlock_time */, priority, extra, m_current_subaddress_account, subaddr_indices); if (ptx_vector.empty()) { @@ -5585,6 +5604,25 @@ bool simple_wallet::sweep_single(const std::vector &args_) return true; } + size_t outputs = 1; + if (local_args.size() > 0 && local_args[0].substr(0, 8) == "outputs=") + { + if (!epee::string_tools::get_xtype_from_string(outputs, local_args[0].substr(8))) + { + fail_msg_writer() << tr("Failed to parse number of outputs"); + return true; + } + else if (outputs < 1) + { + fail_msg_writer() << tr("Amount of outputs should be greater than 0"); + return true; + } + else + { + local_args.erase(local_args.begin()); + } + } + std::vector extra; bool payment_id_seen = false; if (local_args.size() == 3) @@ -5618,7 +5656,7 @@ bool simple_wallet::sweep_single(const std::vector &args_) if (local_args.size() != 2) { - fail_msg_writer() << tr("usage: sweep_single [] []
[]"); + fail_msg_writer() << tr("usage: sweep_single [] [] [outputs=]
[]"); return true; } @@ -5673,7 +5711,7 @@ bool simple_wallet::sweep_single(const std::vector &args_) try { // figure out what tx will be necessary - auto ptx_vector = m_wallet->create_transactions_single(ki, info.address, info.is_subaddress, fake_outs_count, 0 /* unlock_time */, priority, extra); + auto ptx_vector = m_wallet->create_transactions_single(ki, info.address, info.is_subaddress, outputs, fake_outs_count, 0 /* unlock_time */, priority, extra); if (ptx_vector.empty()) { diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index b266821d6c8..fad90403180 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -8362,7 +8362,7 @@ std::vector wallet2::create_transactions_2(std::vector wallet2::create_transactions_all(uint64_t below, const cryptonote::account_public_address &address, bool is_subaddress, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector& extra, uint32_t subaddr_account, std::set subaddr_indices) +std::vector wallet2::create_transactions_all(uint64_t below, const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector& extra, uint32_t subaddr_account, std::set subaddr_indices) { std::vector unused_transfers_indices; std::vector unused_dust_indices; @@ -8413,10 +8413,10 @@ std::vector wallet2::create_transactions_all(uint64_t below } } - return create_transactions_from(address, is_subaddress, unused_transfers_indices, unused_dust_indices, fake_outs_count, unlock_time, priority, extra); + return create_transactions_from(address, is_subaddress, outputs, unused_transfers_indices, unused_dust_indices, fake_outs_count, unlock_time, priority, extra); } -std::vector wallet2::create_transactions_single(const crypto::key_image &ki, const cryptonote::account_public_address &address, bool is_subaddress, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector& extra) +std::vector wallet2::create_transactions_single(const crypto::key_image &ki, const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector& extra) { std::vector unused_transfers_indices; std::vector unused_dust_indices; @@ -8434,10 +8434,10 @@ std::vector wallet2::create_transactions_single(const crypt break; } } - return create_transactions_from(address, is_subaddress, unused_transfers_indices, unused_dust_indices, fake_outs_count, unlock_time, priority, extra); + return create_transactions_from(address, is_subaddress, outputs, unused_transfers_indices, unused_dust_indices, fake_outs_count, unlock_time, priority, extra); } -std::vector wallet2::create_transactions_from(const cryptonote::account_public_address &address, bool is_subaddress, std::vector unused_transfers_indices, std::vector unused_dust_indices, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector& extra) +std::vector wallet2::create_transactions_from(const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, std::vector unused_transfers_indices, std::vector unused_dust_indices, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector& extra) { //ensure device is let in NONE mode in any case hw::device &hwdev = m_account.get_device(); @@ -8799,7 +8799,7 @@ std::vector wallet2::create_unmixable_sweep_transactions() unmixable_transfer_outputs.push_back(n); } - return create_transactions_from(m_account_public_address, false, unmixable_transfer_outputs, unmixable_dust_outputs, 0 /*fake_outs_count */, 0 /* unlock_time */, 1 /*priority */, std::vector()); + return create_transactions_from(m_account_public_address, false, 1, unmixable_transfer_outputs, unmixable_dust_outputs, 0 /*fake_outs_count */, 0 /* unlock_time */, 1 /*priority */, std::vector()); } //---------------------------------------------------------------------------------------------------- void wallet2::discard_unmixable_outputs() diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 402066b5088..62a72f461d0 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -748,9 +748,9 @@ namespace tools bool load_tx(const std::string &signed_filename, std::vector &ptx, std::function accept_func = NULL); bool parse_tx_from_str(const std::string &signed_tx_st, std::vector &ptx, std::function accept_func); std::vector create_transactions_2(std::vector dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector& extra, uint32_t subaddr_account, std::set subaddr_indices); // pass subaddr_indices by value on purpose - std::vector create_transactions_all(uint64_t below, const cryptonote::account_public_address &address, bool is_subaddress, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector& extra, uint32_t subaddr_account, std::set subaddr_indices); - std::vector create_transactions_single(const crypto::key_image &ki, const cryptonote::account_public_address &address, bool is_subaddress, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector& extra); - std::vector create_transactions_from(const cryptonote::account_public_address &address, bool is_subaddress, std::vector unused_transfers_indices, std::vector unused_dust_indices, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector& extra); + std::vector create_transactions_all(uint64_t below, const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector& extra, uint32_t subaddr_account, std::set subaddr_indices); + std::vector create_transactions_single(const crypto::key_image &ki, const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector& extra); + std::vector create_transactions_from(const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, std::vector unused_transfers_indices, std::vector unused_dust_indices, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector& extra); bool load_multisig_tx(cryptonote::blobdata blob, multisig_tx_set &exported_txs, std::function accept_func = NULL); bool load_multisig_tx_from_file(const std::string &filename, multisig_tx_set &exported_txs, std::function accept_func = NULL); bool sign_multisig_tx_from_file(const std::string &filename, std::vector &txids, std::function accept_func); diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index ee86587bc9c..a2cbbd391b5 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -1101,6 +1101,13 @@ namespace tools return false; } + if (req.outputs < 1) + { + er.code = WALLET_RPC_ERROR_CODE_TX_NOT_POSSIBLE; + er.message = "Amount of outputs should be greater than 0."; + return false; + } + try { uint64_t mixin; @@ -1113,7 +1120,7 @@ namespace tools mixin = m_wallet->adjust_mixin(req.mixin); } uint32_t priority = m_wallet->adjust_priority(req.priority); - std::vector ptx_vector = m_wallet->create_transactions_all(req.below_amount, dsts[0].addr, dsts[0].is_subaddress, mixin, req.unlock_time, priority, extra, req.account_index, req.subaddr_indices); + std::vector ptx_vector = m_wallet->create_transactions_all(req.below_amount, dsts[0].addr, dsts[0].is_subaddress, req.outputs, mixin, req.unlock_time, priority, extra, req.account_index, req.subaddr_indices); return fill_response(ptx_vector, req.get_tx_keys, res.tx_key_list, res.amount_list, res.fee_list, res.multisig_txset, res.unsigned_txset, req.do_not_relay, res.tx_hash_list, req.get_tx_hex, res.tx_blob_list, req.get_tx_metadata, res.tx_metadata_list, er); @@ -1139,6 +1146,13 @@ namespace tools return false; } + if (req.outputs < 1) + { + er.code = WALLET_RPC_ERROR_CODE_TX_NOT_POSSIBLE; + er.message = "Amount of outputs should be greater than 0."; + return false; + } + // validate the transfer requested and populate dsts & extra std::list destination; destination.push_back(wallet_rpc::transfer_destination()); @@ -1169,7 +1183,7 @@ namespace tools mixin = m_wallet->adjust_mixin(req.mixin); } uint32_t priority = m_wallet->adjust_priority(req.priority); - std::vector ptx_vector = m_wallet->create_transactions_single(ki, dsts[0].addr, dsts[0].is_subaddress, mixin, req.unlock_time, priority, extra); + std::vector ptx_vector = m_wallet->create_transactions_single(ki, dsts[0].addr, dsts[0].is_subaddress, req.outputs, mixin, req.unlock_time, priority, extra); if (ptx_vector.empty()) { diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h index 4501cf575e1..5d9c2297163 100644 --- a/src/wallet/wallet_rpc_server_commands_defs.h +++ b/src/wallet/wallet_rpc_server_commands_defs.h @@ -639,6 +639,7 @@ namespace wallet_rpc uint32_t priority; uint64_t mixin; uint64_t ring_size; + uint64_t outputs; uint64_t unlock_time; std::string payment_id; bool get_tx_keys; @@ -654,6 +655,7 @@ namespace wallet_rpc KV_SERIALIZE(priority) KV_SERIALIZE_OPT(mixin, (uint64_t)0) KV_SERIALIZE_OPT(ring_size, (uint64_t)0) + KV_SERIALIZE_OPT(outputs, (uint64_t)1) KV_SERIALIZE(unlock_time) KV_SERIALIZE(payment_id) KV_SERIALIZE(get_tx_keys) @@ -705,6 +707,7 @@ namespace wallet_rpc uint32_t priority; uint64_t mixin; uint64_t ring_size; + uint64_t outputs; uint64_t unlock_time; std::string payment_id; bool get_tx_key; @@ -718,6 +721,7 @@ namespace wallet_rpc KV_SERIALIZE(priority) KV_SERIALIZE_OPT(mixin, (uint64_t)0) KV_SERIALIZE_OPT(ring_size, (uint64_t)0) + KV_SERIALIZE_OPT(outputs, (uint64_t)1) KV_SERIALIZE(unlock_time) KV_SERIALIZE(payment_id) KV_SERIALIZE(get_tx_key) From 4ed30bab500dbe2ad94dcb44a0f198f94615836a Mon Sep 17 00:00:00 2001 From: whythat Date: Sun, 6 May 2018 11:45:04 +0300 Subject: [PATCH 0432/1404] wallet: implement coin splitting for sweep_* 'outputs' option Implemented strategy splits total amount into N equal parts, where N is a specified number of outputs. If N > 1, dummy change output is NOT created. rebased by moneromooo --- src/wallet/wallet2.cpp | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index fad90403180..0ab1035ddd5 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -8532,7 +8532,9 @@ std::vector wallet2::create_transactions_from(const crypton needed_fee = estimate_fee(use_per_byte_fee, use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof, base_fee, fee_multiplier, fee_quantization_mask); - tx.dsts.push_back(tx_destination_entry(1, address, is_subaddress)); + // add N - 1 outputs for correct initial fee estimation + for (size_t i = 0; i < ((outputs > 1) ? outputs - 1 : outputs); ++i) + tx.dsts.push_back(tx_destination_entry(1, address, is_subaddress)); LOG_PRINT_L2("Trying to create a tx now, with " << tx.dsts.size() << " destinations and " << tx.selected_transfers.size() << " outputs"); @@ -8544,15 +8546,35 @@ std::vector wallet2::create_transactions_from(const crypton detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx); auto txBlob = t_serializable_object_to_blob(test_ptx.tx); needed_fee = calculate_fee(use_per_byte_fee, test_ptx.tx, txBlob.size(), base_fee, fee_multiplier, fee_quantization_mask); - available_for_fee = test_ptx.fee + test_ptx.dests[0].amount + test_ptx.change_dts.amount; + available_for_fee = test_ptx.fee + test_ptx.change_dts.amount; + for (auto &dt: test_ptx.dests) + available_for_fee += dt.amount; LOG_PRINT_L2("Made a " << get_weight_string(test_ptx.tx, txBlob.size()) << " tx, with " << print_money(available_for_fee) << " available for fee (" << print_money(needed_fee) << " needed)"); + // add last output, missed for fee estimation + if (outputs > 1) + tx.dsts.push_back(tx_destination_entry(1, address, is_subaddress)); + THROW_WALLET_EXCEPTION_IF(needed_fee > available_for_fee, error::wallet_internal_error, "Transaction cannot pay for itself"); do { LOG_PRINT_L2("We made a tx, adjusting fee and saving it"); - tx.dsts[0].amount = available_for_fee - needed_fee; + // distribute total transferred amount between outputs + uint64_t amount_transferred = available_for_fee - needed_fee; + uint64_t dt_amount = amount_transferred / outputs; + // residue is distributed as one atomic unit per output until it reaches zero + uint64_t residue = amount_transferred % outputs; + for (auto &dt: tx.dsts) + { + uint64_t dt_residue = 0; + if (residue > 0) + { + dt_residue = 1; + residue -= 1; + } + dt.amount = dt_amount + dt_residue; + } if (use_rct) transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra, test_tx, test_ptx, range_proof_type); From 8cd9840859ed1128f266f6853c3caa9cb8ada8b0 Mon Sep 17 00:00:00 2001 From: "Tuan M. Hoang" Date: Tue, 1 May 2018 00:13:10 -0500 Subject: [PATCH 0433/1404] disable AES on s390x architecture rebased by moneromooo --- CMakeLists.txt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5e47edcca68..545acfee591 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -154,6 +154,10 @@ if(ARCH_ID STREQUAL "powerpc") set(PPC 1) endif() +if(ARCH_ID STREQUAL "s390x") + set(S390X 1) +endif() + if(WIN32 OR ARM OR PPC64LE OR PPC64 OR PPC) set(OPT_FLAGS_RELEASE "-O2") else() @@ -626,12 +630,14 @@ else() message(STATUS "AES support explicitly disabled") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DNO_AES") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DNO_AES") - elseif(NOT ARM AND NOT PPC64LE AND NOT PPC64 AND NOT PPC) + elseif(NOT ARM AND NOT PPC64LE AND NOT PPC64 AND NOT PPC AND NOT S390X) message(STATUS "AES support enabled") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -maes") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -maes") elseif(PPC64LE OR PPC64 OR PPC) message(STATUS "AES support not available on POWER") + elseif(S390X) + message(STATUS "AES support not available on s390x") elseif(ARM6) message(STATUS "AES support not available on ARMv6") elseif(ARM7) From 669019010e41247419a188be26df725068a0abc3 Mon Sep 17 00:00:00 2001 From: Andrea Date: Tue, 5 Jun 2018 20:45:48 -0500 Subject: [PATCH 0434/1404] README: harmonize command formatting inside README.md rebased by moneromooo --- README.md | 48 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 7f5dcf2907f..99c6cf3a9dc 100644 --- a/README.md +++ b/README.md @@ -116,6 +116,52 @@ X's indicate that these details have not been determined as of commit date. Approximately three months prior to a scheduled software upgrade, a branch from Master will be created with the new release version tag. Pull requests that address bugs should then be made to both Master and the new release branch. Pull requests that require extensive review and testing (generally, optimizations and new features) should *not* be made to the release branch. +<<<<<<< HEAD +======= +## Installing Monero from a package + +Packages are available for + +* Ubuntu and [snap supported](https://snapcraft.io/docs/core/install) systems, via a community contributed build. + + snap install monero --beta + +Installing a snap is very quick. Snaps are secure. They are isolated with all of their dependencies. Snaps also auto update when a new version is released. + +* Arch Linux (via [AUR](https://aur.archlinux.org/)): + - Stable release: [`monero`](https://aur.archlinux.org/packages/monero) + - Bleeding edge: [`monero-git`](https://aur.archlinux.org/packages/monero-git) + +* Void Linux: + + xbps-install -S monero + +* GuixSD + + guix package -i monero + +* OS X via [Homebrew](http://brew.sh) + + brew tap sammy007/cryptonight + brew install monero --build-from-source + +* Docker + + # Build using all available cores + docker build -t monero . + + # or build using a specific number of cores (reduce RAM requirement) + docker build --build-arg NPROC=1 -t monero . + + # either run in foreground + docker run -it -v /monero/chain:/root/.bitmonero -v /monero/wallet:/wallet -p 18080:18080 monero + + # or in background + docker run -it -d -v /monero/chain:/root/.bitmonero -v /monero/wallet:/wallet -p 18080:18080 monero + +Packaging for your favorite distribution would be a welcome contribution! + +>>>>>>> f6d62ab... Formating commands inside README.md ## Compiling Monero from source ### Dependencies @@ -497,7 +543,7 @@ Packages are available for * Ubuntu and [snap supported](https://snapcraft.io/docs/core/install) systems, via a community contributed build. - snap install monero --beta + snap install monero --beta Installing a snap is very quick. Snaps are secure. They are isolated with all of their dependencies. Snaps also auto update when a new version is released. From a2505c2fcc97f17944d47e68eeb20077d3082a2f Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 19 Sep 2018 07:50:04 +0000 Subject: [PATCH 0435/1404] gpg_keys: bump my key validity for two more years --- utils/gpg_keys/moneromooo.asc | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/utils/gpg_keys/moneromooo.asc b/utils/gpg_keys/moneromooo.asc index 86a4a21b4af..cce18093767 100644 --- a/utils/gpg_keys/moneromooo.asc +++ b/utils/gpg_keys/moneromooo.asc @@ -1,5 +1,4 @@ -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: GnuPG v1 mQINBFQym34BEADDKCspvziDW0f+T9i6iOewFO9m2XTWKXlQutCPgTkIlZZUrcTR K+ApsfPxk+PBWgucQDPv/nJVs0CNaSzqewxk7Swjsf8+YjvRmxSSg/NQEgsiBx/s @@ -14,18 +13,18 @@ p7gDvxXOGxzq0sqfPTWTBdCj1OPfunHbbeH8ypwBlNpwVG40fJdya+Dqjwu25qX6 Xh5vxLzeJTBmlawa97MCliPvzzJgW9qHRVCa9lLloGVYLiUOS0N+dZ/r/QARAQAB tD5tb25lcm9tb29vLW1vbmVybyA8bW9uZXJvbW9vby1tb25lcm9AdXNlcnMubm9y ZXBseS5naXRodWIuY29tPokCPwQTAQIAKQIbAwcLCQgHAwIBBhUIAgkKCwQWAgMB -Ah4BAheABQJX3tlPBQkHbqTNAAoJEGhvB0VNbO/DDnsP/R3lqXUkaglERu0mxHcR -CrqzO97nBRXiodTjOCuj2ITGBvDWTqCrxmyF0g/GQhUKPtBrfVJzyVa00G/5X8G4 -u+nFy3D49URhKBwn3n2fDs0sTbHKWqSzEzMMhrzbnO/44qvDQKeE25gPR/Rv+sOW -c+EcYLtfQgSH0S3KGAG6HVXRuNbTTI0cPzrPIhZBgyjQ5QZJaRKJcSsskAGYR26X -mT5W3x+dqySMtBpS8ovBjoSBGDAKpbksqqXundS1JjavAgc3c8ZdD+glUaceso1X -93N3RLrevZTyZY/xNFYrfQvjJgGgn8cnMOfR2atSAZgLJq8J1mfbpzUp5yC1r/ws -nqbJH2GJkBl3WjuwgHFIZ82RkUjuCJ4F5SA4aAJjVI1vw8ZuK/lFtzoLnGvJTaT4 -7SvnE/1zQx+h63kee50H4GAJgfJhwm5DjjwsSvOjOP7Hsb68RrYii43AS5fG6l5W -L/7B8lohMM9/e/4+G+F1zUeNbAFFemynDlYiyscJ6kqVfKuVW6oV1o+SkQmq7Sn1 -bMRfrNxu7sl5mkEhud1O3oJwUqLuk2rG5zSMiR4P7kNQbojOUkLXR/rXvCN7MfQi -HhShjFz9ViCx9/IdTuPQuKyKT24Y8qIe5SxRqoJ8feotSYRWxaj28t9Tmf0HkxB4 -IRiSzougGxTPIIRbnS7QlOdPuQINBFQym34BEADHtTHduZFdu76RAzqTjT94F92L +Ah4BAheABQJbof8TBQkLMcqRAAoJEGhvB0VNbO/DOrcP/1eG3gkIgq+lXKlv/lta +ZFl8jS5iQUwxTHv8s+O7pbL2mwKt5oM8QmXVMFLlaanENmH0y/DhWRYIYuKTifDN +tXOxENCZhxgjlVxAtnMdD2J8MJANzV2MYGLbGQeFAuZHIL2LMfvUENLYx/jJpe7f +93kXZoQio7rIolnlbM1QoJLxi/7HlOt/VsopJlV2wAmOmlpyWDnOZtUtZgiCGV6a +apFzTs2m3n2GP7+8PG/W01/jVyFqixGW8cWVZORBMhjro2JqrRvw7U+Ypk2o7Em4 +SAnAJyzqpTDh9zf0QWkQXN83YThB3dk1M1FXOyKtZ8G+YVNfs5Ldr7tHNbqKi2v8 +lPaPIDQ7UmxZhy9vraKss0/3AEXaiE2eSvLc4eCcLiS4yVQ2KJpK5TUvz3cP1D5C +USsjxUZvolUELBtNaRVAsxX6OAyA8FjwJ8AKCqyR0NHvrbYhj54N1Js8PWtb+qqk +5sBLKOwEPaWM1uG6SJabrY1xu2VNLOGdJA9bRfyyzfpXksD0lFgwLKki6ReqqoMS +QiVdymhcp55J4tFwsDkzJX7296d1x3r7GAIQMLNc7YizukWfNtvUkSUsAwp+RKUx +TTwkwL1ztzSRpt3hGMJ9SfULTaQD7YAYfz4kitMBNpm90CLFa4CeINu98gE9Ogmc +R+CGQOHLUC3rXubgRCiQg5wfuQINBFQym34BEADHtTHduZFdu76RAzqTjT94F92L xSSopLSk7/sdLWTc2ERmjDId7dKmqrL1Kh2kqAtHY3Rq8Y839LGmbJCzI1kJyOHF o9jkEI93sqXcztLjizPVukqClOZNt3NV/nvefH6JSdqWcnC4V1mQr2Ztl0j+51i+ NYVwGjlsOMlBER+LW/s7egRqAQonrcEB5vsSAzd8mOlNKjRAnDCV+C21GDKxzb80 @@ -48,5 +47,5 @@ IQOy33qWguR1xoEnwAp9ov/meS7HtUOoC0m9ROZqWT6ArN+1ONplFP4GTsuW91Qz pacQMl09F0KF1MacAdpauCOKj+wy9XPUW8v3kWZufUSgNtOLHS+kVumkqdJuJ0aB 7Ezc1yYY6DwRFOdlUpTJkRMozyPRvS15Lz/vPEhcbxMHRAg611CS2CqTEIbk2chJ QBlj77a45lDTXGQHFVYXlbXx/HWb5zOGpNji0QhY1hOABRo78DT5yda+ -=Vv2M +=ksQj -----END PGP PUBLIC KEY BLOCK----- From 579383c26b3cc8f29e09a55bc24446b9141f4f87 Mon Sep 17 00:00:00 2001 From: fireice-uk Date: Wed, 19 Sep 2018 13:39:01 +0100 Subject: [PATCH 0436/1404] simplewallet: Add Unicode input_line [Ryo backport] --- src/common/util.cpp | 28 +++++++++++++++++ src/common/util.h | 3 ++ src/simplewallet/simplewallet.cpp | 50 ++----------------------------- 3 files changed, 34 insertions(+), 47 deletions(-) diff --git a/src/common/util.cpp b/src/common/util.cpp index c56c7750545..2a1d49af0f1 100644 --- a/src/common/util.cpp +++ b/src/common/util.cpp @@ -939,4 +939,32 @@ std::string get_nix_version_display_string() } return newval; } + +#ifdef _WIN32 + std::string input_line_win() + { + HANDLE hConIn = CreateFileW(L"CONIN$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, nullptr); + DWORD oldMode; + + FlushConsoleInputBuffer(hConIn); + GetConsoleMode(hConIn, &oldMode); + SetConsoleMode(hConIn, ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT); + + wchar_t buffer[1024]; + DWORD read; + + ReadConsoleW(hConIn, buffer, sizeof(buffer)/sizeof(wchar_t)-1, &read, nullptr); + buffer[read] = 0; + + SetConsoleMode(hConIn, oldMode); + CloseHandle(hConIn); + + int size_needed = WideCharToMultiByte(CP_UTF8, 0, buffer, -1, NULL, 0, NULL, NULL); + std::string buf(size_needed, '\0'); + WideCharToMultiByte(CP_UTF8, 0, buffer, -1, &buf[0], size_needed, NULL, NULL); + buf.pop_back(); //size_needed includes null that we needed to have space for + return buf; + } +#endif + } diff --git a/src/common/util.h b/src/common/util.h index 0e0b50520d5..ce773bd386e 100644 --- a/src/common/util.h +++ b/src/common/util.h @@ -235,4 +235,7 @@ namespace tools boost::optional> parse_subaddress_lookahead(const std::string& str); std::string glob_to_regex(const std::string &val); +#ifdef _WIN32 + std::string input_line_win(); +#endif } diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index e535b6d2777..9b7d7694dac 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -138,47 +138,6 @@ namespace const command_line::arg_descriptor< std::vector > arg_command = {"command", ""}; -#ifdef WIN32 - // Translate from CP850 to UTF-8; - // std::getline for a Windows console returns a string in CP437 or CP850; as simplewallet, - // like all of Monero, is assumed to work internally with UTF-8 throughout, even on Windows - // (although only implemented partially), a translation to UTF-8 is needed for input. - // - // Note that if a program is started inside the MSYS2 shell somebody already translates - // console input to UTF-8, but it's not clear how one could detect that in order to avoid - // double-translation; this code here thus breaks UTF-8 input within a MSYS2 shell, - // unfortunately. - // - // Note also that input for passwords is NOT translated, to remain compatible with any - // passwords containing special characters that predate this switch to UTF-8 support. - template - static T cp850_to_utf8(const T &cp850_str) - { - boost::locale::generator gen; - gen.locale_cache_enabled(true); - std::locale loc = gen("en_US.CP850"); - const boost::locale::conv::method_type how = boost::locale::conv::default_method; - T result; - const char *begin = cp850_str.data(); - const char *end = begin + cp850_str.size(); - result.reserve(end-begin); - typedef std::back_insert_iterator inserter_type; - inserter_type inserter(result); - boost::locale::utf::code_point c; - while(begin!=end) { - c=boost::locale::utf::utf_traits::template decode(begin,end); - if(c==boost::locale::utf::illegal || c==boost::locale::utf::incomplete) { - if(how==boost::locale::conv::stop) - throw boost::locale::conv::conversion_error(); - } - else { - boost::locale::utf::utf_traits::template encode(c,inserter); - } - } - return result; - } -#endif - std::string input_line(const std::string& prompt) { #ifdef HAVE_READLINE @@ -187,9 +146,10 @@ namespace std::cout << prompt; std::string buf; +#ifdef _WIN32 + buf = tools::input_line_win(); +#else std::getline(std::cin, buf); -#ifdef WIN32 - buf = cp850_to_utf8(buf); #endif return epee::string_tools::trim(buf); @@ -209,10 +169,6 @@ namespace epee::wipeable_string buf = pwd_container->password(); -#ifdef WIN32 - buf = cp850_to_utf8(buf); -#endif - buf.trim(); return buf; } From bce474be7d9cfa80d9f0970047bfd508db60424f Mon Sep 17 00:00:00 2001 From: m2049r Date: Thu, 16 Aug 2018 10:31:48 +0200 Subject: [PATCH 0437/1404] query backing device --- src/device/device.hpp | 11 ++++- src/device/device_default.hpp | 2 + src/device/device_ledger.hpp | 4 +- src/wallet/api/wallet.cpp | 5 ++ src/wallet/api/wallet.h | 1 + src/wallet/api/wallet2_api.h | 22 +++++++++ src/wallet/api/wallet_manager.cpp | 8 ++++ src/wallet/api/wallet_manager.h | 1 + src/wallet/wallet2.cpp | 80 ++++++++++++++++++++++++++----- src/wallet/wallet2.h | 6 ++- 10 files changed, 123 insertions(+), 17 deletions(-) diff --git a/src/device/device.hpp b/src/device/device.hpp index d14b8848ca7..87f1430f4fc 100644 --- a/src/device/device.hpp +++ b/src/device/device.hpp @@ -78,7 +78,6 @@ namespace hw { return false; } - class device { protected: std::string name; @@ -96,6 +95,12 @@ namespace hw { TRANSACTION_CREATE_FAKE, TRANSACTION_PARSE }; + enum device_type + { + SOFTWARE = 0, + LEDGER = 1 + }; + /* ======================================================================= */ /* SETUP/TEARDOWN */ @@ -109,7 +114,9 @@ namespace hw { virtual bool connect(void) = 0; virtual bool disconnect(void) = 0; - virtual bool set_mode(device_mode mode) = 0; + virtual bool set_mode(device_mode mode) = 0; + + virtual device_type get_type() const = 0; /* ======================================================================= */ diff --git a/src/device/device_default.hpp b/src/device/device_default.hpp index 8d841d9de3b..b697e17752c 100644 --- a/src/device/device_default.hpp +++ b/src/device/device_default.hpp @@ -61,6 +61,8 @@ namespace hw { bool set_mode(device_mode mode) override; + device_type get_type() const {return device_type::SOFTWARE;}; + /* ======================================================================= */ /* LOCKER */ /* ======================================================================= */ diff --git a/src/device/device_ledger.hpp b/src/device/device_ledger.hpp index e6c6e5b5261..4a3625b2b89 100644 --- a/src/device/device_ledger.hpp +++ b/src/device/device_ledger.hpp @@ -142,7 +142,9 @@ namespace hw { bool connect(void) override; bool disconnect() override; - bool set_mode(device_mode mode) override; + bool set_mode(device_mode mode) override; + + device_type get_type() const {return device_type::LEDGER;}; /* ======================================================================= */ /* LOCKER */ diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index 4631852845c..8b25096a22b 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -639,6 +639,11 @@ bool WalletImpl::recoverFromDevice(const std::string &path, const std::string &p return true; } +Wallet::Device WalletImpl::getDeviceType() const +{ + return static_cast(m_wallet->get_device_type()); +} + bool WalletImpl::open(const std::string &path, const std::string &password) { clearStatus(); diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h index 2cbfa30d0ea..e3a300317e7 100644 --- a/src/wallet/api/wallet.h +++ b/src/wallet/api/wallet.h @@ -79,6 +79,7 @@ class WalletImpl : public Wallet bool recoverFromDevice(const std::string &path, const std::string &password, const std::string &device_name); + Device getDeviceType() const; bool close(bool store = true); std::string seed() const override; std::string getSeedLanguage() const override; diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h index 93391607662..e0d49170544 100644 --- a/src/wallet/api/wallet2_api.h +++ b/src/wallet/api/wallet2_api.h @@ -373,6 +373,10 @@ struct WalletListener */ struct Wallet { + enum Device { + Device_Software = 0, + Device_Ledger = 1 + }; enum Status { Status_Ok, @@ -911,6 +915,12 @@ struct Wallet virtual bool unlockKeysFile() = 0; //! returns true if the keys file is locked virtual bool isKeysFileLocked() = 0; + + /*! + * \brief Queries backing device for wallet keys + * \return Device they are on + */ + virtual Device getDeviceType() const = 0; }; /** @@ -1096,6 +1106,18 @@ struct WalletManager */ virtual bool verifyWalletPassword(const std::string &keys_file_name, const std::string &password, bool no_spend_key, uint64_t kdf_rounds = 1) const = 0; + /*! + * \brief determine the key storage for the specified wallet file + * \param device_type (OUT) wallet backend as enumerated in Wallet::Device + * \param keys_file_name Keys file to verify password for + * \param password Password to verify + * \return true if password correct, else false + * + * for verification only - determines key storage hardware + * + */ + virtual bool queryWalletDevice(Wallet::Device& device_type, const std::string &keys_file_name, const std::string &password, uint64_t kdf_rounds = 1) const = 0; + /*! * \brief findWallets - searches for the wallet files by given path name recursively * \param path - starting point to search diff --git a/src/wallet/api/wallet_manager.cpp b/src/wallet/api/wallet_manager.cpp index 3851ca9cc81..5b262f1b768 100644 --- a/src/wallet/api/wallet_manager.cpp +++ b/src/wallet/api/wallet_manager.cpp @@ -167,6 +167,14 @@ bool WalletManagerImpl::verifyWalletPassword(const std::string &keys_file_name, return tools::wallet2::verify_password(keys_file_name, password, no_spend_key, hw::get_device("default"), kdf_rounds); } +bool WalletManagerImpl::queryWalletDevice(Wallet::Device& device_type, const std::string &keys_file_name, const std::string &password, uint64_t kdf_rounds) const +{ + hw::device::device_type type; + bool r = tools::wallet2::query_device(type, keys_file_name, password, kdf_rounds); + device_type = static_cast(type); + return r; +} + std::vector WalletManagerImpl::findWallets(const std::string &path) { std::vector result; diff --git a/src/wallet/api/wallet_manager.h b/src/wallet/api/wallet_manager.h index 8b1c8be7f78..573e80d1a47 100644 --- a/src/wallet/api/wallet_manager.h +++ b/src/wallet/api/wallet_manager.h @@ -76,6 +76,7 @@ class WalletManagerImpl : public WalletManager virtual bool closeWallet(Wallet *wallet, bool store = true) override; bool walletExists(const std::string &path) override; bool verifyWalletPassword(const std::string &keys_file_name, const std::string &password, bool no_spend_key, uint64_t kdf_rounds = 1) const override; + bool queryWalletDevice(Wallet::Device& device_type, const std::string &keys_file_name, const std::string &password, uint64_t kdf_rounds = 1) const; std::vector findWallets(const std::string &path) override; std::string errorString() const override; void setDaemonAddress(const std::string &address) override; diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 6502463e2ae..4e93309ed8f 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -794,7 +794,7 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended): m_light_wallet_connected(false), m_light_wallet_balance(0), m_light_wallet_unlocked_balance(0), - m_key_on_device(false), + m_key_device_type(hw::device::device_type::SOFTWARE), m_ring_history_saved(false), m_ringdb(), m_last_block_reward(0), @@ -2908,7 +2908,7 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable rapidjson::Value value2(rapidjson::kNumberType); - value2.SetInt(m_key_on_device?1:0); + value2.SetInt(m_key_device_type); json.AddMember("key_on_device", value2, json.GetAllocator()); value2.SetInt(watch_only ? 1 :0); // WTF ? JSON has different true and false types, and not boolean ?? @@ -3121,7 +3121,7 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_ m_subaddress_lookahead_major = SUBADDRESS_LOOKAHEAD_MAJOR; m_subaddress_lookahead_minor = SUBADDRESS_LOOKAHEAD_MINOR; m_device_name = ""; - m_key_on_device = false; + m_key_device_type = hw::device::device_type::SOFTWARE; encrypted_secret_keys = false; } else if(json.IsObject()) @@ -3141,8 +3141,8 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_ if (json.HasMember("key_on_device")) { - GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, key_on_device, int, Int, false, false); - m_key_on_device = field_key_on_device; + GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, key_on_device, int, Int, false, hw::device::device_type::SOFTWARE); + m_key_device_type = static_cast(field_key_on_device); } GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, seed_language, std::string, String, false, std::string()); @@ -3269,7 +3269,8 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_ } r = epee::serialization::load_t_from_binary(m_account, account_data); - if (r && m_key_on_device) { + THROW_WALLET_EXCEPTION_IF(!r, error::invalid_password); + if (m_key_device_type == hw::device::device_type::LEDGER) { LOG_PRINT_L0("Account on device. Initing device..."); hw::device &hwdev = hw::get_device(m_device_name); hwdev.set_name(m_device_name); @@ -3277,6 +3278,8 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_ hwdev.connect(); m_account.set_device(hwdev); LOG_PRINT_L0("Device inited..."); + } else if (key_on_device()) { + THROW_WALLET_EXCEPTION(error::wallet_internal_error, "hardware device not supported"); } if (r) @@ -3444,6 +3447,59 @@ void wallet2::create_keys_file(const std::string &wallet_, bool watch_only, cons } +/*! + * \brief determine the key storage for the specified wallet file + * \param device_type (OUT) wallet backend as enumerated in hw::device::device_type + * \param keys_file_name Keys file to verify password for + * \param password Password to verify + * \return true if password correct, else false + * + * for verification only - determines key storage hardware + * + */ +bool wallet2::query_device(hw::device::device_type& device_type, const std::string& keys_file_name, const epee::wipeable_string& password, uint64_t kdf_rounds) +{ + rapidjson::Document json; + wallet2::keys_file_data keys_file_data; + std::string buf; + bool r = epee::file_io_utils::load_file_to_string(keys_file_name, buf); + THROW_WALLET_EXCEPTION_IF(!r, error::file_read_error, keys_file_name); + + // Decrypt the contents + r = ::serialization::parse_binary(buf, keys_file_data); + THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "internal error: failed to deserialize \"" + keys_file_name + '\"'); + crypto::chacha_key key; + crypto::generate_chacha_key(password.data(), password.size(), key, kdf_rounds); + std::string account_data; + account_data.resize(keys_file_data.account_data.size()); + crypto::chacha20(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]); + if (json.Parse(account_data.c_str()).HasParseError() || !json.IsObject()) + crypto::chacha8(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]); + + // The contents should be JSON if the wallet follows the new format. + if (json.Parse(account_data.c_str()).HasParseError()) + { + // old format before JSON wallet key file format + } + else + { + account_data = std::string(json["key_data"].GetString(), json["key_data"].GetString() + + json["key_data"].GetStringLength()); + + if (json.HasMember("key_on_device")) + { + GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, key_on_device, int, Int, false, hw::device::device_type::SOFTWARE); + device_type = static_cast(field_key_on_device); + } + } + + cryptonote::account_base account_data_check; + + r = epee::serialization::load_t_from_binary(account_data_check, account_data); + if (!r) return false; + return true; +} + /*! * \brief Generates a wallet or restores one. * \param wallet_ Name of wallet file @@ -3518,7 +3574,7 @@ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string& m_multisig = true; m_multisig_threshold = threshold; m_multisig_signers = multisig_signers; - m_key_on_device = false; + m_key_device_type = hw::device::device_type::SOFTWARE; setup_keys(password); create_keys_file(wallet_, false, password, m_nettype != MAINNET || create_address_file); @@ -3558,7 +3614,7 @@ crypto::secret_key wallet2::generate(const std::string& wallet_, const epee::wip m_multisig = false; m_multisig_threshold = 0; m_multisig_signers.clear(); - m_key_on_device = false; + m_key_device_type = hw::device::device_type::SOFTWARE; setup_keys(password); // calculate a starting refresh height @@ -3646,7 +3702,7 @@ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string& m_multisig = false; m_multisig_threshold = 0; m_multisig_signers.clear(); - m_key_on_device = false; + m_key_device_type = hw::device::device_type::SOFTWARE; setup_keys(password); create_keys_file(wallet_, true, password, m_nettype != MAINNET || create_address_file); @@ -3686,7 +3742,7 @@ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string& m_multisig = false; m_multisig_threshold = 0; m_multisig_signers.clear(); - m_key_on_device = false; + m_key_device_type = hw::device::device_type::SOFTWARE; setup_keys(password); create_keys_file(wallet_, false, password, create_address_file); @@ -3713,12 +3769,12 @@ void wallet2::restore(const std::string& wallet_, const epee::wipeable_string& p THROW_WALLET_EXCEPTION_IF(boost::filesystem::exists(m_wallet_file, ignored_ec), error::file_exists, m_wallet_file); THROW_WALLET_EXCEPTION_IF(boost::filesystem::exists(m_keys_file, ignored_ec), error::file_exists, m_keys_file); } - m_key_on_device = true; auto &hwdev = hw::get_device(device_name); hwdev.set_name(device_name); m_account.create_from_device(hwdev); + m_key_device_type = m_account.get_device().get_type(); m_account_public_address = m_account.get_keys().m_account_address; m_watch_only = false; m_multisig = false; @@ -3815,7 +3871,7 @@ std::string wallet2::make_multisig(const epee::wipeable_string &password, m_watch_only = false; m_multisig = true; m_multisig_threshold = threshold; - m_key_on_device = false; + m_key_device_type = hw::device::device_type::SOFTWARE; if (threshold == spend_keys.size() + 1) { diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index c30ca1d85b0..9eb9b04f206 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -189,6 +189,7 @@ namespace tools static std::unique_ptr make_dummy(const boost::program_options::variables_map& vm, bool unattended, const std::function(const char *, bool)> &password_prompter); static bool verify_password(const std::string& keys_file_name, const epee::wipeable_string& password, bool no_spend_key, hw::device &hwdev, uint64_t kdf_rounds); + static bool query_device(hw::device::device_type& device_type, const std::string& keys_file_name, const epee::wipeable_string& password, uint64_t kdf_rounds = 1); wallet2(cryptonote::network_type nettype = cryptonote::MAINNET, uint64_t kdf_rounds = 1, bool unattended = false); ~wallet2(); @@ -709,7 +710,8 @@ namespace tools bool has_multisig_partial_key_images() const; bool has_unknown_key_images() const; bool get_multisig_seed(epee::wipeable_string& seed, const epee::wipeable_string &passphrase = std::string(), bool raw = true) const; - bool key_on_device() const { return m_key_on_device; } + bool key_on_device() const { return get_device_type() != hw::device::device_type::SOFTWARE; } + hw::device::device_type get_device_type() const { return m_key_device_type; } bool reconnect_device(); // locked & unlocked balance of given or current subaddress account @@ -1284,7 +1286,7 @@ namespace tools bool m_trusted_daemon; i_wallet2_callback* m_callback; - bool m_key_on_device; + hw::device::device_type m_key_device_type; cryptonote::network_type m_nettype; uint64_t m_kdf_rounds; std::string seed_language; /*!< Language of the mnemonics (seed). */ From 8db2d8d4165921dfeee57903bb3e774f3bcd9ab3 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 19 Sep 2018 19:55:38 +0000 Subject: [PATCH 0438/1404] simplewallet: fix --generate-from-json setting wrong wallet filename --- src/simplewallet/simplewallet.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 391d1f03e11..01619393a63 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -3278,10 +3278,10 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) else if (!m_generate_from_json.empty()) { - m_wallet_file = m_generate_from_json; try { - m_wallet = tools::wallet2::make_from_json(vm, false, m_wallet_file, password_prompter); + m_wallet = tools::wallet2::make_from_json(vm, false, m_generate_from_json, password_prompter); + m_wallet_file = m_wallet->path(); } catch (const std::exception &e) { From 43a06350cf9a14d6a86bd08898bad15c19a0272d Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 20 Sep 2018 12:21:25 +0000 Subject: [PATCH 0439/1404] ringdb: use cursors to be a bit faster --- src/wallet/ringdb.cpp | 15 ++++++++------- tests/unit_tests/ringdb.cpp | 24 ++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/src/wallet/ringdb.cpp b/src/wallet/ringdb.cpp index e9fc6866da0..e5995e7fb50 100644 --- a/src/wallet/ringdb.cpp +++ b/src/wallet/ringdb.cpp @@ -398,6 +398,8 @@ bool ringdb::blackball_worker(const std::vector> & epee::misc_utils::auto_scope_leave_caller txn_dtor = epee::misc_utils::create_scope_leave_handler([&](){if (tx_active) mdb_txn_abort(txn);}); tx_active = true; + dbr = mdb_cursor_open(txn, dbi_blackballs, &cursor); + THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to create cursor for blackballs table: " + std::string(mdb_strerror(dbr))); MDB_val key, data; for (const std::pair &output: outputs) @@ -411,25 +413,22 @@ bool ringdb::blackball_worker(const std::vector> & { case BLACKBALL_BLACKBALL: MDEBUG("Blackballing output " << output.first << "/" << output.second); - dbr = mdb_put(txn, dbi_blackballs, &key, &data, MDB_APPENDDUP); + dbr = mdb_cursor_put(cursor, &key, &data, MDB_APPENDDUP); if (dbr == MDB_KEYEXIST) dbr = 0; break; case BLACKBALL_UNBLACKBALL: MDEBUG("Unblackballing output " << output.first << "/" << output.second); - dbr = mdb_del(txn, dbi_blackballs, &key, &data); - if (dbr == MDB_NOTFOUND) - dbr = 0; + dbr = mdb_cursor_get(cursor, &key, &data, MDB_GET_BOTH); + if (dbr == 0) + dbr = mdb_cursor_del(cursor, 0); break; case BLACKBALL_QUERY: - dbr = mdb_cursor_open(txn, dbi_blackballs, &cursor); - THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to create cursor for blackballs table: " + std::string(mdb_strerror(dbr))); dbr = mdb_cursor_get(cursor, &key, &data, MDB_GET_BOTH); THROW_WALLET_EXCEPTION_IF(dbr && dbr != MDB_NOTFOUND, tools::error::wallet_internal_error, "Failed to lookup in blackballs table: " + std::string(mdb_strerror(dbr))); ret = dbr != MDB_NOTFOUND; if (dbr == MDB_NOTFOUND) dbr = 0; - mdb_cursor_close(cursor); break; case BLACKBALL_CLEAR: break; @@ -439,6 +438,8 @@ bool ringdb::blackball_worker(const std::vector> & THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to query blackballs table: " + std::string(mdb_strerror(dbr))); } + mdb_cursor_close(cursor); + if (op == BLACKBALL_CLEAR) { dbr = mdb_drop(txn, dbi_blackballs, 0); diff --git a/tests/unit_tests/ringdb.cpp b/tests/unit_tests/ringdb.cpp index 416ae089091..0d92049ac91 100644 --- a/tests/unit_tests/ringdb.cpp +++ b/tests/unit_tests/ringdb.cpp @@ -150,6 +150,30 @@ TEST(blackball, found) ASSERT_TRUE(ringdb.blackballed(OUTPUT_1)); } +TEST(blackball, vector) +{ + RingDB ringdb; + std::vector> outputs; + outputs.push_back(std::make_pair(0, 1)); + outputs.push_back(std::make_pair(10, 3)); + outputs.push_back(std::make_pair(10, 4)); + outputs.push_back(std::make_pair(10, 8)); + outputs.push_back(std::make_pair(20, 0)); + outputs.push_back(std::make_pair(20, 1)); + outputs.push_back(std::make_pair(30, 5)); + ASSERT_TRUE(ringdb.blackball(outputs)); + ASSERT_TRUE(ringdb.blackballed(std::make_pair(0, 1))); + ASSERT_FALSE(ringdb.blackballed(std::make_pair(10, 2))); + ASSERT_TRUE(ringdb.blackballed(std::make_pair(10, 3))); + ASSERT_TRUE(ringdb.blackballed(std::make_pair(10, 4))); + ASSERT_FALSE(ringdb.blackballed(std::make_pair(10, 5))); + ASSERT_TRUE(ringdb.blackballed(std::make_pair(10, 8))); + ASSERT_TRUE(ringdb.blackballed(std::make_pair(20, 0))); + ASSERT_TRUE(ringdb.blackballed(std::make_pair(20, 1))); + ASSERT_FALSE(ringdb.blackballed(std::make_pair(20, 2))); + ASSERT_TRUE(ringdb.blackballed(std::make_pair(30, 5))); +} + TEST(blackball, unblackball) { RingDB ringdb; From 341b3931ed79dd0b16a04e8a1468179ba5dd22c2 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 20 Sep 2018 13:31:45 +0000 Subject: [PATCH 0440/1404] cryptonote_core: warn when the block rate deviates from expectations The warning threshold is set to allow a false positive every ten days on average. --- src/cryptonote_core/blockchain.cpp | 9 +++++ src/cryptonote_core/blockchain.h | 5 +++ src/cryptonote_core/cryptonote_core.cpp | 47 +++++++++++++++++++++++++ src/cryptonote_core/cryptonote_core.h | 8 +++++ 4 files changed, 69 insertions(+) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 3751d6473d6..e94024ae52e 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -880,6 +880,15 @@ difficulty_type Blockchain::get_difficulty_for_next_block() return diff; } //------------------------------------------------------------------ +std::vector Blockchain::get_last_block_timestamps(unsigned int blocks) const +{ + std::vector timestamps(blocks); + uint64_t height = m_db->height(); + while (blocks--) + timestamps[blocks] = m_db->get_block_timestamp(height - blocks - 1); + return timestamps; +} +//------------------------------------------------------------------ // This function removes blocks from the blockchain until it gets to the // position where the blockchain switch started and then re-adds the blocks // that had been removed. diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index 50ceccd0f76..f56068db8e5 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -943,6 +943,11 @@ namespace cryptonote */ void on_new_tx_from_block(const cryptonote::transaction &tx); + /** + * @brief returns the timestamps of the last N blocks + */ + std::vector get_last_block_timestamps(unsigned int blocks) const; + private: // TODO: evaluate whether or not each of these typedefs are left over from blockchain_storage diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 7cbf414b780..7f8c60f7342 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -1471,6 +1471,7 @@ namespace cryptonote m_txpool_auto_relayer.do_call(boost::bind(&core::relay_txpool_transactions, this)); m_check_updates_interval.do_call(boost::bind(&core::check_updates, this)); m_check_disk_space_interval.do_call(boost::bind(&core::check_disk_space, this)); + m_block_rate_interval.do_call(boost::bind(&core::check_block_rate, this)); m_miner.on_idle(); m_mempool.on_idle(); return true; @@ -1655,6 +1656,52 @@ namespace cryptonote return true; } //----------------------------------------------------------------------------------------------- + double factorial(unsigned int n) + { + if (n <= 1) + return 1.0; + double f = n; + while (n-- > 1) + f *= n; + return f; + } + //----------------------------------------------------------------------------------------------- + static double probability(unsigned int blocks, unsigned int expected) + { + // https://www.umass.edu/wsp/resources/poisson/#computing + return pow(expected, blocks) / (factorial(blocks) * exp(expected)); + } + //----------------------------------------------------------------------------------------------- + bool core::check_block_rate() + { + if (m_offline || m_target_blockchain_height > get_current_blockchain_height()) + { + MDEBUG("Not checking block rate, offline or syncing"); + return true; + } + + static constexpr double threshold = 1. / (864000 / DIFFICULTY_TARGET_V2); // one false positive every 10 days + + const time_t now = time(NULL); + const std::vector timestamps = m_blockchain_storage.get_last_block_timestamps(60); + + static const unsigned int seconds[] = { 5400, 1800, 600 }; + for (size_t n = 0; n < sizeof(seconds)/sizeof(seconds[0]); ++n) + { + unsigned int b = 0; + for (time_t ts: timestamps) b += ts >= now - seconds[n]; + const double p = probability(b, seconds[n] / DIFFICULTY_TARGET_V2); + MDEBUG("blocks in the last " << seconds[n] / 60 << " minutes: " << b << " (probability " << p << ")"); + if (p < threshold) + { + MWARNING("There were " << b << " blocks in the last " << seconds[n] / 60 << " minutes, there might be large hash rate changes, or we might be partitioned, cut off from the Monero network or under attack. Or it could be just sheer bad luck."); + break; // no need to look further + } + } + + return true; + } + //----------------------------------------------------------------------------------------------- void core::set_target_blockchain_height(uint64_t target_blockchain_height) { m_target_blockchain_height = target_blockchain_height; diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index b40575ae92c..b2be05bf45b 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -935,6 +935,13 @@ namespace cryptonote */ bool check_disk_space(); + /** + * @brief checks block rate, and warns if it's too slow + * + * @return true on success, false otherwise + */ + bool check_block_rate(); + bool m_test_drop_download = true; //!< whether or not to drop incoming blocks (for testing) uint64_t m_test_drop_download_height = 0; //!< height under which to drop incoming blocks, if doing so @@ -959,6 +966,7 @@ namespace cryptonote epee::math_helper::once_a_time_seconds<60*2, false> m_txpool_auto_relayer; //!< interval for checking re-relaying txpool transactions epee::math_helper::once_a_time_seconds<60*60*12, true> m_check_updates_interval; //!< interval for checking for new versions epee::math_helper::once_a_time_seconds<60*10, true> m_check_disk_space_interval; //!< interval for checking for disk space + epee::math_helper::once_a_time_seconds<90, false> m_block_rate_interval; //!< interval for checking block rate std::atomic m_starter_message_showed; //!< has the "daemon will sync now" message been shown? From 7a056f4425aa4fbe87494f77a4dc64e26f6000bf Mon Sep 17 00:00:00 2001 From: naughtyfox Date: Thu, 20 Sep 2018 17:12:35 +0300 Subject: [PATCH 0441/1404] WalletAPI: multisigSignData bug fixed --- src/wallet/api/pending_transaction.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/wallet/api/pending_transaction.cpp b/src/wallet/api/pending_transaction.cpp index 8d200220d05..913e3156fd0 100644 --- a/src/wallet/api/pending_transaction.cpp +++ b/src/wallet/api/pending_transaction.cpp @@ -200,7 +200,11 @@ std::string PendingTransactionImpl::multisigSignData() { throw std::runtime_error("wallet is not multisig"); } - auto cipher = m_wallet.m_wallet->save_multisig_tx(m_pending_tx); + tools::wallet2::multisig_tx_set txSet; + txSet.m_ptx = m_pending_tx; + txSet.m_signers = m_signers; + auto cipher = m_wallet.m_wallet->save_multisig_tx(txSet); + return epee::string_tools::buff_to_hex_nodelimer(cipher); } catch (const std::exception& e) { m_status = Status_Error; From e03402b0c7a0da9822ac17b079c3872fac511496 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 21 Sep 2018 18:49:05 +0000 Subject: [PATCH 0442/1404] wallet_rpc_server: fix build for windows Thanks iDunk for building patches on windows --- src/wallet/wallet_rpc_server.cpp | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index 6361a6cface..b9e685a110f 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -3294,9 +3294,12 @@ class t_daemon private: const boost::program_options::variables_map& vm; + std::unique_ptr wrpc; + public: t_daemon(boost::program_options::variables_map const & _vm) : vm(_vm) + , wrpc(new tools::wallet_rpc_server) { } @@ -3386,17 +3389,16 @@ class t_daemon return false; } just_dir: - tools::wallet_rpc_server wrpc; - if (wal) wrpc.set_wallet(wal.release()); - bool r = wrpc.init(&vm); + if (wal) wrpc->set_wallet(wal.release()); + bool r = wrpc->init(&vm); CHECK_AND_ASSERT_MES(r, false, tools::wallet_rpc_server::tr("Failed to initialize wallet RPC server")); - tools::signal_handler::install([&wrpc](int) { - wrpc.send_stop_signal(); + tools::signal_handler::install([this](int) { + wrpc->send_stop_signal(); }); LOG_PRINT_L0(tools::wallet_rpc_server::tr("Starting wallet RPC server")); try { - wrpc.run(); + wrpc->run(); } catch (const std::exception &e) { @@ -3407,7 +3409,7 @@ class t_daemon try { LOG_PRINT_L0(tools::wallet_rpc_server::tr("Saving wallet...")); - wrpc.stop(); + wrpc->stop(); LOG_PRINT_L0(tools::wallet_rpc_server::tr("Successfully saved")); } catch (const std::exception& e) @@ -3417,6 +3419,11 @@ class t_daemon } return true; } + + void stop() + { + wrpc->send_stop_signal(); + } }; class t_executor final @@ -3424,7 +3431,9 @@ class t_executor final public: static std::string const NAME; - std::string const & name() + typedef ::t_daemon t_daemon; + + std::string const & name() const { return NAME; } From 4d0a8db06d1bb457ef5130a4f95a02618dcf1c78 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 21 Sep 2018 19:32:47 +0000 Subject: [PATCH 0443/1404] device: fix warnings about overridden functions --- src/device/device_default.hpp | 2 +- src/device/device_ledger.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/device/device_default.hpp b/src/device/device_default.hpp index b697e17752c..5c59a906682 100644 --- a/src/device/device_default.hpp +++ b/src/device/device_default.hpp @@ -61,7 +61,7 @@ namespace hw { bool set_mode(device_mode mode) override; - device_type get_type() const {return device_type::SOFTWARE;}; + device_type get_type() const override {return device_type::SOFTWARE;}; /* ======================================================================= */ /* LOCKER */ diff --git a/src/device/device_ledger.hpp b/src/device/device_ledger.hpp index 4a3625b2b89..cd97d42a05c 100644 --- a/src/device/device_ledger.hpp +++ b/src/device/device_ledger.hpp @@ -144,7 +144,7 @@ namespace hw { bool set_mode(device_mode mode) override; - device_type get_type() const {return device_type::LEDGER;}; + device_type get_type() const override {return device_type::LEDGER;}; /* ======================================================================= */ /* LOCKER */ From bf1e1d979c5efd0d1025b218342b553582e096d5 Mon Sep 17 00:00:00 2001 From: TheCharlatan Date: Tue, 18 Sep 2018 17:07:54 +0200 Subject: [PATCH 0444/1404] A Travis to monero Add a Travis build script for Monero. This was blatantly copied from Bitcoin. It spawns jobs in docker containers running an ubuntu bionic image. This commit also a fixes a problem where librt was still linked, even when compiling statically. --- .travis.yml | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++ CMakeLists.txt | 2 +- 2 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000000..c87dceac9f8 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,63 @@ +sudo: required +dist: trusty +os: linux +language: minimal +cache: + directories: + - contrib/depends/built + - contrib/depends/sdk-sources + - $HOME/.ccache +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 + - CCACHE_DIR=$HOME/.ccache + - BASE_OUTDIR=$TRAVIS_BUILD_DIR/out + - SDK_URL=https://bitcoincore.org/depends-sources/sdks + - PYTHON_DEBUG=1 + - WINEDEBUG=fixme-all + - DOCKER_PACKAGES="build-essential libtool cmake autotools-dev automake pkg-config bsdmainutils curl git ca-certificates ccache" + matrix: +# ARM v7 + - HOST=arm-linux-gnueabihf PACKAGES="g++-arm-linux-gnueabihf" +# ARM v8 + - HOST=aarch64-linux-gnu PACKAGES="g++-aarch64-linux-gnu" +# i686 Linux + - HOST=i686-pc-linux-gnu PACKAGES="cmake g++-multilib bc python3-zmq" RUN_TESTS=true +# Win64 + - HOST=x86_64-w64-mingw32 PACKAGES="cmake python3 nsis g++-mingw-w64-x86-64 wine-binfmt wine64 bc" RUN_TESTS=true +# x86_64 Linux + - HOST=x86_64-unknown-linux-gnu PACKAGES="cmake python3-zmq protobuf-compiler libdbus-1-dev libharfbuzz-dev libprotobuf-dev" RUN_TESTS=true +# Cross-Mac + - HOST=x86_64-apple-darwin11 PACKAGES="cmake imagemagick libcap-dev librsvg2-bin libz-dev libbz2-dev libtiff-tools python-dev python3-setuptools-git" OSX_SDK=10.11 + +before_install: + - export PATH=$(echo $PATH | tr ':' "\n" | sed '/\/opt\/python/d' | tr "\n" ":" | sed "s|::|:|g") +install: + - env | grep -E '^(CCACHE_|WINEDEBUG|DISPLAY|BOOST_TEST_RANDOM|CONFIG_SHELL)' | tee /tmp/env + - if [[ $HOST = *-mingw32 ]]; then DOCKER_ADMIN="--cap-add SYS_ADMIN"; fi + - DOCKER_ID=$(docker run $DOCKER_ADMIN -idt --mount type=bind,src=$TRAVIS_BUILD_DIR,dst=$TRAVIS_BUILD_DIR --mount type=bind,src=$CCACHE_DIR,dst=$CCACHE_DIR -w $TRAVIS_BUILD_DIR --env-file /tmp/env ubuntu:18.04) + - DOCKER_EXEC="docker exec $DOCKER_ID" + - if [ -n "$DPKG_ADD_ARCH" ]; then $DOCKER_EXEC dpkg --add-architecture "$DPKG_ADD_ARCH" ; fi + - travis_retry $DOCKER_EXEC apt-get update + - travis_retry $DOCKER_EXEC apt-get install --no-install-recommends --no-upgrade -qq $PACKAGES $DOCKER_PACKAGES +before_script: + - mkdir -p contrib/depends/SDKs contrib/depends/sdk-sources + - if [ -n "$OSX_SDK" -a ! -f contrib/depends/sdk-sources/MacOSX${OSX_SDK}.sdk.tar.gz ]; then curl --location --fail $SDK_URL/MacOSX${OSX_SDK}.sdk.tar.gz -o contrib/depends/sdk-sources/MacOSX${OSX_SDK}.sdk.tar.gz; fi + - if [ -n "$OSX_SDK" -a -f contrib/depends/sdk-sources/MacOSX${OSX_SDK}.sdk.tar.gz ]; then tar -C contrib/depends/SDKs -xf contrib/depends/sdk-sources/MacOSX${OSX_SDK}.sdk.tar.gz; fi + - if [[ $HOST = *-mingw32 ]]; then $DOCKER_EXEC bash -c "update-alternatives --set $HOST-g++ \$(which $HOST-g++-posix)"; fi + - if [ -z "$NO_DEPENDS" ]; then $DOCKER_EXEC bash -c "CONFIG_SHELL= make $MAKEJOBS -C contrib/depends HOST=$HOST $DEP_OPTS"; fi +script: + - git submodule init && git submodule update + - export TRAVIS_COMMIT_LOG=`git log --format=fuller -1` + - OUTDIR=$BASE_OUTDIR/$TRAVIS_PULL_REQUEST/$TRAVIS_JOB_NUMBER-$HOST + - if [ -z "$NO_DEPENDS" ]; then $DOCKER_EXEC ccache --max-size=$CCACHE_SIZE; fi + - $DOCKER_EXEC bash -c "mkdir build && cd build && cmake -DCMAKE_TOOLCHAIN_FILE=$TRAVIS_BUILD_DIR/contrib/depends/$HOST/share/toolchain.cmake .. && make $MAKEJOBS" + - export LD_LIBRARY_PATH=$TRAVIS_BUILD_DIR/contrib/depends/$HOST/lib +after_script: + - echo $TRAVIS_COMMIT_RANGE + - echo $TRAVIS_COMMIT_LOG diff --git a/CMakeLists.txt b/CMakeLists.txt index a95e14b6583..a018600fbf4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -874,7 +874,7 @@ elseif(DRAGONFLY) set(EXTRA_LIBRARIES execinfo ${COMPAT}) elseif(CMAKE_SYSTEM_NAME MATCHES "(SunOS|Solaris)") set(EXTRA_LIBRARIES socket nsl resolv) -elseif(NOT MSVC) +elseif(NOT MSVC AND NOT DEPENDS) find_library(RT rt) set(EXTRA_LIBRARIES ${RT}) endif() From f3cd51a12b202875bd8191668aceb8a4f810ecd4 Mon Sep 17 00:00:00 2001 From: Lee Clagett Date: Wed, 19 Sep 2018 00:30:58 -0400 Subject: [PATCH 0445/1404] cryptonote tweak v2.2 --- src/crypto/slow-hash.c | 20 ++++++++++++++++++++ tests/hash/tests-slow-2.txt | 20 ++++++++++---------- 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/src/crypto/slow-hash.c b/src/crypto/slow-hash.c index 914ba6dc0ef..80bf4e06f15 100644 --- a/src/crypto/slow-hash.c +++ b/src/crypto/slow-hash.c @@ -198,6 +198,22 @@ extern int aesb_pseudo_round(const uint8_t *in, uint8_t *out, const uint8_t *exp } while (0) #endif +#define VARIANT2_2_PORTABLE() \ + if (variant >= 2) { \ + xor_blocks(long_state + (j ^ 0x10), d); \ + xor_blocks(d, long_state + (j ^ 0x20)); \ + } + +#define VARIANT2_2() \ + do if (variant >= 2) \ + { \ + *U64(hp_state + (j ^ 0x10)) ^= hi; \ + *(U64(hp_state + (j ^ 0x10)) + 1) ^= lo; \ + hi ^= *U64(hp_state + (j ^ 0x20)); \ + lo ^= *(U64(hp_state + (j ^ 0x20)) + 1); \ + } while (0) + + #if !defined NO_AES && (defined(__x86_64__) || (defined(_MSC_VER) && defined(_WIN64))) // Optimised code below, uses x86-specific intrinsics, SSE2, AES-NI // Fall back to more portable code is down at the bottom @@ -282,6 +298,7 @@ extern int aesb_pseudo_round(const uint8_t *in, uint8_t *out, const uint8_t *exp b[0] = p[0]; b[1] = p[1]; \ VARIANT2_INTEGER_MATH_SSE2(b, c); \ __mul(); \ + VARIANT2_2(); \ VARIANT2_SHUFFLE_ADD_SSE2(hp_state, j); \ a[0] += hi; a[1] += lo; \ p = U64(&hp_state[j]); \ @@ -884,6 +901,7 @@ union cn_slow_hash_state b[0] = p[0]; b[1] = p[1]; \ VARIANT2_PORTABLE_INTEGER_MATH(b, c); \ __mul(); \ + VARIANT2_2(); \ VARIANT2_SHUFFLE_ADD_NEON(hp_state, j); \ a[0] += hi; a[1] += lo; \ p = U64(&hp_state[j]); \ @@ -1305,6 +1323,7 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int VARIANT2_PORTABLE_INTEGER_MATH(c, c1); mul(c1, c, d); + VARIANT2_2_PORTABLE(); VARIANT2_PORTABLE_SHUFFLE_ADD(long_state, j); sum_half_blocks(a, d); swap_blocks(a, c); @@ -1486,6 +1505,7 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int copy_block(c2, &long_state[j]); VARIANT2_PORTABLE_INTEGER_MATH(c2, c1); mul(c1, c2, d); + VARIANT2_2_PORTABLE(); VARIANT2_PORTABLE_SHUFFLE_ADD(long_state, j); swap_blocks(a, c1); sum_half_blocks(c1, d); diff --git a/tests/hash/tests-slow-2.txt b/tests/hash/tests-slow-2.txt index 8f90d05c9f9..79afd2712f7 100644 --- a/tests/hash/tests-slow-2.txt +++ b/tests/hash/tests-slow-2.txt @@ -1,10 +1,10 @@ -4cf1ff9ca46eb433b36cd9f70e02b14cc06bfd18ca77fa9ccaafd1fd96c674b0 5468697320697320612074657374205468697320697320612074657374205468697320697320612074657374 -7d292e43f4751714ec07dbcb0e4bbffe2a7afb6066420960684ff57d7474c871 4c6f72656d20697073756d20646f6c6f722073697420616d65742c20636f6e73656374657475722061646970697363696e67 -335563425256edebf1d92dc342369c2f4770ebb4112ba975659bd8a0f210abd0 656c69742c2073656420646f20656975736d6f642074656d706f7220696e6369646964756e74207574206c61626f7265 -47758e86d2f57210366cec36fff26f9464d89efd116fe6ef28b718b5da120801 657420646f6c6f7265206d61676e6120616c697175612e20557420656e696d206164206d696e696d2076656e69616d2c -48787b48d5c68f0c1dd825c32580af741cc0ee314f08133135c1e86d87a24a95 71756973206e6f737472756420657865726369746174696f6e20756c6c616d636f206c61626f726973206e697369 -93bdf47495854f7cfaaca1af8c0f39ef4a3024c10eb0dea23726b0e06ef29e84 757420616c697175697020657820656120636f6d6d6f646f20636f6e7365717561742e20447569732061757465 -a375a71d0541057ccc96719150dfe10b6e6f486b19cf4a0835e19605413a8417 697275726520646f6c6f7220696e20726570726568656e646572697420696e20766f6c7570746174652076656c6974 -163478a76f8f1432533fbdd1284d65c89f37479e54f20841c6ce4eba56c73854 657373652063696c6c756d20646f6c6f726520657520667567696174206e756c6c612070617269617475722e -356b0470c6eea75cad7a108179e232905b23bdaf03c2824c6e619d503ee93677 4578636570746575722073696e74206f6363616563617420637570696461746174206e6f6e2070726f6964656e742c -a47e2b007dc25bb279e197a1b91f67ecebe2ddd8791cd32dd2cb76dd21ed943f 73756e7420696e2063756c706120717569206f666669636961206465736572756e74206d6f6c6c697420616e696d20696420657374206c61626f72756d2e +353fdc068fd47b03c04b9431e005e00b68c2168a3cc7335c8b9b308156591a4f 5468697320697320612074657374205468697320697320612074657374205468697320697320612074657374 +72f134fc50880c330fe65a2cb7896d59b2e708a0221c6a9da3f69b3a702d8682 4c6f72656d20697073756d20646f6c6f722073697420616d65742c20636f6e73656374657475722061646970697363696e67 +410919660ec540fc49d8695ff01f974226a2a28dbbac82949c12f541b9a62d2f 656c69742c2073656420646f20656975736d6f642074656d706f7220696e6369646964756e74207574206c61626f7265 +4472fecfeb371e8b7942ce0378c0ba5e6d0c6361b669c587807365c787ae652d 657420646f6c6f7265206d61676e6120616c697175612e20557420656e696d206164206d696e696d2076656e69616d2c +577568395203f1f1225f2982b637f7d5e61b47a0f546ba16d46020b471b74076 71756973206e6f737472756420657865726369746174696f6e20756c6c616d636f206c61626f726973206e697369 +f6fd7efe95a5c6c4bb46d9b429e3faf65b1ce439e116742d42b928e61de52385 757420616c697175697020657820656120636f6d6d6f646f20636f6e7365717561742e20447569732061757465 +422f8cfe8060cf6c3d9fd66f68e3c9977adb683aea2788029308bbe9bc50d728 697275726520646f6c6f7220696e20726570726568656e646572697420696e20766f6c7570746174652076656c6974 +512e62c8c8c833cfbd9d361442cb00d63c0a3fd8964cfd2fedc17c7c25ec2d4b 657373652063696c6c756d20646f6c6f726520657520667567696174206e756c6c612070617269617475722e +12a794c1aa13d561c9c6111cee631ca9d0a321718d67d3416add9de1693ba41e 4578636570746575722073696e74206f6363616563617420637570696461746174206e6f6e2070726f6964656e742c +2659ff95fc74b6215c1dc741e85b7a9710101b30620212f80eb59c3c55993f9d 73756e7420696e2063756c706120717569206f666669636961206465736572756e74206d6f6c6c697420616e696d20696420657374206c61626f72756d2e From 83907f88293584b3c38490cccbc31ef17713108c Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 22 Sep 2018 11:55:36 +0000 Subject: [PATCH 0446/1404] wallet_rpc_server: fix --run-as-service on Windows Thanks iDunk for the windows testing --- src/wallet/wallet_rpc_server.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index b9e685a110f..4dc579f868a 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -3476,6 +3476,7 @@ int main(int argc, char** argv) { command_line::add_arg(desc_params, arg_prompt_for_password); daemonizer::init_options(hidden_options, desc_params); + desc_params.add(hidden_options); boost::optional vm; bool should_terminate = false; From 7964d4f8ea9ef05a4d2dea54e1b5a55a4b2487e5 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 19 Sep 2018 20:20:58 +0000 Subject: [PATCH 0447/1404] wallet2: handle corner case in picking fake outputs If we originally think we have enough outputs on the blockchain to pick random fake outputs, we might end up with not enough of them if enough are actually blackballed. --- src/wallet/wallet2.cpp | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 6502463e2ae..6ca393cd42d 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -6642,11 +6642,23 @@ void wallet2::get_outs(std::vector> } // while we still need more mixins + uint64_t num_usable_outs = num_outs; + bool allow_blackballed = false; while (num_found < requested_outputs_count) { // if we've gone through every possible output, we've gotten all we can - if (seen_indices.size() == num_outs) - break; + if (seen_indices.size() == num_usable_outs) + { + // there is a first pass which rejects blackballed outputs, then a second pass + // which allows them if we don't have enough non blackballed outputs to reach + // the required amount of outputs (since consensus does not care about blackballed + // outputs, we still need to reach the minimum ring size) + if (allow_blackballed) + break; + MINFO("Not enough non blackballed outputs, we'll allow blackballed ones"); + allow_blackballed = true; + num_usable_outs = num_outs; + } // get a random output index from the DB. If we've already seen it, // return to the top of the loop and try again, otherwise add it to the @@ -6720,14 +6732,26 @@ void wallet2::get_outs(std::vector> if (seen_indices.count(i)) continue; - if (is_output_blackballed(std::make_pair(amount, i))) // don't add blackballed outputs + if (!allow_blackballed && is_output_blackballed(std::make_pair(amount, i))) // don't add blackballed outputs + { + --num_usable_outs; continue; + } seen_indices.emplace(i); LOG_PRINT_L2("picking " << i << " as " << type); req.outputs.push_back({amount, i}); ++num_found; } + + // if we had enough unusable outputs, we might fall off here and still + // have too few outputs, so we stuff with one to keep counts good, and + // we'll error out later + while (num_found < requested_outputs_count) + { + req.outputs.push_back({amount, 0}); + ++num_found; + } } // sort the subsection, to ensure the daemon doesn't know which output is ours From c05f98d4c369ccf00c848a7d517f3289c602e8d3 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 22 Sep 2018 19:07:48 +0000 Subject: [PATCH 0448/1404] README: remove overlooked conflict --- README.md | 46 ---------------------------------------------- 1 file changed, 46 deletions(-) diff --git a/README.md b/README.md index c9e8e7aa3a2..2ded4eb5147 100644 --- a/README.md +++ b/README.md @@ -118,52 +118,6 @@ X's indicate that these details have not been determined as of commit date. Approximately three months prior to a scheduled software upgrade, a branch from Master will be created with the new release version tag. Pull requests that address bugs should then be made to both Master and the new release branch. Pull requests that require extensive review and testing (generally, optimizations and new features) should *not* be made to the release branch. -<<<<<<< HEAD -======= -## Installing Monero from a package - -Packages are available for - -* Ubuntu and [snap supported](https://snapcraft.io/docs/core/install) systems, via a community contributed build. - - snap install monero --beta - -Installing a snap is very quick. Snaps are secure. They are isolated with all of their dependencies. Snaps also auto update when a new version is released. - -* Arch Linux (via [AUR](https://aur.archlinux.org/)): - - Stable release: [`monero`](https://aur.archlinux.org/packages/monero) - - Bleeding edge: [`monero-git`](https://aur.archlinux.org/packages/monero-git) - -* Void Linux: - - xbps-install -S monero - -* GuixSD - - guix package -i monero - -* OS X via [Homebrew](http://brew.sh) - - brew tap sammy007/cryptonight - brew install monero --build-from-source - -* Docker - - # Build using all available cores - docker build -t monero . - - # or build using a specific number of cores (reduce RAM requirement) - docker build --build-arg NPROC=1 -t monero . - - # either run in foreground - docker run -it -v /monero/chain:/root/.bitmonero -v /monero/wallet:/wallet -p 18080:18080 monero - - # or in background - docker run -it -d -v /monero/chain:/root/.bitmonero -v /monero/wallet:/wallet -p 18080:18080 monero - -Packaging for your favorite distribution would be a welcome contribution! - ->>>>>>> f6d62ab... Formating commands inside README.md ## Compiling Monero from source ### Dependencies From 6f153533e2a2eabe89cea9f7da07baa6a673d478 Mon Sep 17 00:00:00 2001 From: Riccardo Spagni Date: Sat, 22 Sep 2018 21:31:06 +0200 Subject: [PATCH 0449/1404] update readme with v13.0 --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 2ded4eb5147..ac40f08d39a 100644 --- a/README.md +++ b/README.md @@ -180,7 +180,7 @@ invokes cmake commands as needed. * Change to the root of the source code directory, change to the most recent release branch, and build: cd monero - git checkout v0.12.3.0 + git checkout v0.13.0.0 make *Optional*: If your machine has several cores and enough memory, enable @@ -242,7 +242,7 @@ Tested on a Raspberry Pi Zero with a clean install of minimal Raspbian Stretch ( ``` git clone https://github.com/monero-project/monero.git cd monero - git checkout tags/v0.12.3.0 + git checkout tags/v0.13.0.0 ``` * Build: ``` @@ -339,9 +339,9 @@ application. cd monero -* If you would like a specific [version/tag](https://github.com/monero-project/monero/tags), do a git checkout for that version. eg. 'v0.12.3.0'. If you dont care about the version and just want binaries from master, skip this step: +* If you would like a specific [version/tag](https://github.com/monero-project/monero/tags), do a git checkout for that version. eg. 'v0.13.0.0'. If you dont care about the version and just want binaries from master, skip this step: - git checkout v0.12.3.0 + git checkout v0.13.0.0 * If you are on a 64-bit system, run: From 6b1b4e83dd3c2f06fc670ed825f0563faa1c02dd Mon Sep 17 00:00:00 2001 From: Riccardo Spagni Date: Sat, 22 Sep 2018 21:36:11 +0200 Subject: [PATCH 0450/1404] update version to 13.0 --- src/version.cpp.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/version.cpp.in b/src/version.cpp.in index 55ba51f5013..ff24058111c 100644 --- a/src/version.cpp.in +++ b/src/version.cpp.in @@ -1,6 +1,6 @@ #define DEF_MONERO_VERSION_TAG "@VERSIONTAG@" -#define DEF_MONERO_VERSION "0.12.3.0-master" -#define DEF_MONERO_RELEASE_NAME "Lithium Luna" +#define DEF_MONERO_VERSION "0.13.0.0-master" +#define DEF_MONERO_RELEASE_NAME "Beryllium Bullet" #define DEF_MONERO_VERSION_FULL DEF_MONERO_VERSION "-" DEF_MONERO_VERSION_TAG #include "version.h" From 0f4f873bf33404dbfc016a90cd6930fb43b1165e Mon Sep 17 00:00:00 2001 From: TheCharlatan Date: Sat, 22 Sep 2018 20:07:33 +0200 Subject: [PATCH 0451/1404] Fix Darwin Sodium build Clang needs to get its cctools path passed. --- contrib/depends/hosts/darwin.mk | 6 +++--- contrib/depends/packages/packages.mk | 6 ++++-- contrib/depends/packages/sodium-darwin.mk | 24 +++++++++++++++++++++++ contrib/depends/packages/sodium.mk | 8 ++++---- contrib/depends/toolchain.cmake.in | 6 +++--- 5 files changed, 38 insertions(+), 12 deletions(-) create mode 100644 contrib/depends/packages/sodium-darwin.mk diff --git a/contrib/depends/hosts/darwin.mk b/contrib/depends/hosts/darwin.mk index 4e58bec74e3..6f8f2ffe225 100644 --- a/contrib/depends/hosts/darwin.mk +++ b/contrib/depends/hosts/darwin.mk @@ -2,13 +2,13 @@ OSX_MIN_VERSION=10.8 OSX_SDK_VERSION=10.11 OSX_SDK=$(SDK_PATH)/MacOSX$(OSX_SDK_VERSION).sdk 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++ +darwin_CC=clang -target $(host) -mmacosx-version-min=$(OSX_MIN_VERSION) --sysroot $(OSX_SDK) -mlinker-version=$(LD64_VERSION) -B $(host_prefix)/native/bin +darwin_CXX=clang++ -target $(host) -mmacosx-version-min=$(OSX_MIN_VERSION) --sysroot $(OSX_SDK) -mlinker-version=$(LD64_VERSION) -stdlib=libc++ -B $(host_prefix)/native/bin darwin_CFLAGS=-pipe darwin_CXXFLAGS=$(darwin_CFLAGS) -darwin_release_CFLAGS=-O2 +darwin_release_CFLAGS=-O1 darwin_release_CXXFLAGS=$(darwin_release_CFLAGS) darwin_debug_CFLAGS=-O1 diff --git a/contrib/depends/packages/packages.mk b/contrib/depends/packages/packages.mk index 28eaf34c568..114f8e9c019 100644 --- a/contrib/depends/packages/packages.mk +++ b/contrib/depends/packages/packages.mk @@ -1,16 +1,19 @@ -packages:=boost openssl libevent zeromq cppzmq zlib expat ldns cppzmq readline libiconv qt sodium +packages:=boost openssl libevent zeromq cppzmq zlib expat ldns cppzmq readline libiconv qt native_packages := native_ccache wallet_packages=bdb darwin_native_packages = native_biplist native_ds_store native_mac_alias +darwin_packages += sodium-darwin ifeq ($(host_os),linux) packages += pcsc-lite packages += unwind +packages += sodium endif ifeq ($(host_os),mingw32) packages += icu4c +packages += sodium endif ifneq ($(build_os),darwin) @@ -18,4 +21,3 @@ darwin_native_packages += native_cctools native_cdrkit native_libdmg-hfsplus packages += readline endif - diff --git a/contrib/depends/packages/sodium-darwin.mk b/contrib/depends/packages/sodium-darwin.mk new file mode 100644 index 00000000000..796bead1622 --- /dev/null +++ b/contrib/depends/packages/sodium-darwin.mk @@ -0,0 +1,24 @@ +package=sodium-darwin +$(package)_version=1.0.15 +$(package)_download_path=https://download.libsodium.org/libsodium/releases/ +$(package)_file_name=libsodium-$($(package)_version).tar.gz +$(package)_sha256_hash=fb6a9e879a2f674592e4328c5d9f79f082405ee4bb05cb6e679b90afe9e178f4 + +define $(package)_set_vars +$(package)_config_opts=--enable-static +$(package)_config_opts+=--prefix=$(host_prefix) +endef + +define $(package)_config_cmds + ./autogen.sh &&\ + $($(package)_autoconf) $($(package)_config_opts) RANLIB="$(host_prefix)/native/bin/x86_64-apple-darwin11-ranlib" AR="$(host_prefix)/native/bin/x86_64-apple-darwin11-ar" +endef + +define $(package)_build_cmds + echo "path is problematic here" &&\ + make +endef + +define $(package)_stage_cmds + $(MAKE) DESTDIR=$($(package)_staging_dir) install +endef diff --git a/contrib/depends/packages/sodium.mk b/contrib/depends/packages/sodium.mk index 885b1ea1972..0e6668062cc 100644 --- a/contrib/depends/packages/sodium.mk +++ b/contrib/depends/packages/sodium.mk @@ -1,8 +1,8 @@ package=sodium -$(package)_version=1.0.16 -$(package)_download_path=https://github.com/jedisct1/libsodium/archive -$(package)_file_name=$($(package)_version).tar.gz -$(package)_sha256_hash=0c14604bbeab2e82a803215d65c3b6e74bb28291aaee6236d65c699ccfe1a98c +$(package)_version=1.0.15 +$(package)_download_path=https://download.libsodium.org/libsodium/releases/ +$(package)_file_name=libsodium-$($(package)_version).tar.gz +$(package)_sha256_hash=fb6a9e879a2f674592e4328c5d9f79f082405ee4bb05cb6e679b90afe9e178f4 define $(package)_set_vars $(package)_config_opts=--enable-static diff --git a/contrib/depends/toolchain.cmake.in b/contrib/depends/toolchain.cmake.in index b594ddd6254..3755335571f 100644 --- a/contrib/depends/toolchain.cmake.in +++ b/contrib/depends/toolchain.cmake.in @@ -26,9 +26,9 @@ SET(BOOST_IGNORE_SYSTEM_PATH ON) SET(BOOST_ROOT @prefix@) SET(BOOST_LIBRARYDIR @prefix@/lib) SET(BOOST_IGNORE_SYSTEM_PATHS_DEFAULT OFF) -SET(Boost_NO_SYSTEM_PATHS TRUE) -SET(Boost_USE_STATIC_LIBS TRUE) -SET(Boost_USE_STATIC_RUNTIME TRUE) +SET(BOOST_NO_SYSTEM_PATHS TRUE) +SET(BOOST_USE_STATIC_LIBS TRUE) +SET(BOOST_USE_STATIC_RUNTIME TRUE) SET(OpenSSL_DIR @prefix@/lib) SET(ARCHITECTURE @arch@) From a21da9058bf33939ce07c2eef8ba9b2da508192b Mon Sep 17 00:00:00 2001 From: oneiric Date: Sat, 22 Sep 2018 22:03:36 +0000 Subject: [PATCH 0452/1404] Wallet: use unique_ptr for WalletImpl members Use unique_ptr to manage WalletImpl internals, rather than raw pointers. --- src/wallet/api/wallet.cpp | 26 ++++++++++---------------- src/wallet/api/wallet.h | 12 ++++++------ 2 files changed, 16 insertions(+), 22 deletions(-) diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index 8b25096a22b..b0e5fa37ff9 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -376,15 +376,15 @@ WalletImpl::WalletImpl(NetworkType nettype, uint64_t kdf_rounds) , m_rebuildWalletCache(false) , m_is_connected(false) { - m_wallet = new tools::wallet2(static_cast(nettype), kdf_rounds, true); - m_history = new TransactionHistoryImpl(this); - m_wallet2Callback = new Wallet2CallbackImpl(this); + m_wallet = std::make_unique(static_cast(nettype), kdf_rounds, true); + m_history = std::make_unique(this); + m_wallet2Callback = std::make_unique(this); m_wallet->callback(m_wallet2Callback); m_refreshThreadDone = false; m_refreshEnabled = false; - m_addressBook = new AddressBookImpl(this); - m_subaddress = new SubaddressImpl(this); - m_subaddressAccount = new SubaddressAccountImpl(this); + m_addressBook = std::make_unique(this); + m_subaddress = std::make_unique(this); + m_subaddressAccount = std::make_unique(this); m_refreshIntervalMillis = DEFAULT_REFRESH_INTERVAL_MILLIS; @@ -405,12 +405,6 @@ WalletImpl::~WalletImpl() close(false); // do not store wallet as part of the closing activities // Stop refresh thread stopRefresh(); - delete m_wallet2Callback; - delete m_history; - delete m_addressBook; - delete m_subaddress; - delete m_subaddressAccount; - delete m_wallet; LOG_PRINT_L1(__FUNCTION__ << " finished"); } @@ -1551,22 +1545,22 @@ void WalletImpl::disposeTransaction(PendingTransaction *t) TransactionHistory *WalletImpl::history() { - return m_history; + return m_history.get(); } AddressBook *WalletImpl::addressBook() { - return m_addressBook; + return m_addressBook.get(); } Subaddress *WalletImpl::subaddress() { - return m_subaddress; + return m_subaddress.get(); } SubaddressAccount *WalletImpl::subaddressAccount() { - return m_subaddressAccount; + return m_subaddressAccount.get(); } void WalletImpl::setListener(WalletListener *l) diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h index e3a300317e7..b2317b9ba50 100644 --- a/src/wallet/api/wallet.h +++ b/src/wallet/api/wallet.h @@ -215,16 +215,16 @@ class WalletImpl : public Wallet friend class SubaddressImpl; friend class SubaddressAccountImpl; - tools::wallet2 * m_wallet; + std::unique_ptr m_wallet; mutable boost::mutex m_statusMutex; mutable int m_status; mutable std::string m_errorString; std::string m_password; - TransactionHistoryImpl * m_history; - Wallet2CallbackImpl * m_wallet2Callback; - AddressBookImpl * m_addressBook; - SubaddressImpl * m_subaddress; - SubaddressAccountImpl * m_subaddressAccount; + std::unique_ptr m_history; + std::unique_ptr m_wallet2Callback; + std::unique_ptr m_addressBook; + std::unique_ptr m_subaddress; + std::unique_ptr m_subaddressAccount; // multi-threaded refresh stuff std::atomic m_refreshEnabled; From 6e270fbd29d3b13493ce9ba182b97d9ce8191403 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 22 Sep 2018 22:58:06 +0000 Subject: [PATCH 0453/1404] wallet2_api: fix for latest code changes --- src/wallet/api/wallet.cpp | 2 +- src/wallet/api/wallet.h | 2 +- src/wallet/api/wallet_manager.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index 8b25096a22b..5827e4d1a7c 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -1385,7 +1385,7 @@ PendingTransaction *WalletImpl::createTransaction(const string &dst_addr, const for (uint32_t index = 0; index < m_wallet->get_num_subaddresses(subaddr_account); ++index) subaddr_indices.insert(index); } - transaction->m_pending_tx = m_wallet->create_transactions_all(0, info.address, info.is_subaddress, fake_outs_count, 0 /* unlock_time */, + transaction->m_pending_tx = m_wallet->create_transactions_all(0, info.address, info.is_subaddress, 1, fake_outs_count, 0 /* unlock_time */, adjusted_priority, extra, subaddr_account, subaddr_indices); } diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h index e3a300317e7..64350cee763 100644 --- a/src/wallet/api/wallet.h +++ b/src/wallet/api/wallet.h @@ -79,7 +79,7 @@ class WalletImpl : public Wallet bool recoverFromDevice(const std::string &path, const std::string &password, const std::string &device_name); - Device getDeviceType() const; + Device getDeviceType() const override; bool close(bool store = true); std::string seed() const override; std::string getSeedLanguage() const override; diff --git a/src/wallet/api/wallet_manager.h b/src/wallet/api/wallet_manager.h index 573e80d1a47..b3c0d6c0018 100644 --- a/src/wallet/api/wallet_manager.h +++ b/src/wallet/api/wallet_manager.h @@ -76,7 +76,7 @@ class WalletManagerImpl : public WalletManager virtual bool closeWallet(Wallet *wallet, bool store = true) override; bool walletExists(const std::string &path) override; bool verifyWalletPassword(const std::string &keys_file_name, const std::string &password, bool no_spend_key, uint64_t kdf_rounds = 1) const override; - bool queryWalletDevice(Wallet::Device& device_type, const std::string &keys_file_name, const std::string &password, uint64_t kdf_rounds = 1) const; + bool queryWalletDevice(Wallet::Device& device_type, const std::string &keys_file_name, const std::string &password, uint64_t kdf_rounds = 1) const override; std::vector findWallets(const std::string &path) override; std::string errorString() const override; void setDaemonAddress(const std::string &address) override; From d9f666d78c841bb44513dfaa1a91d0527dc51363 Mon Sep 17 00:00:00 2001 From: Riccardo Spagni Date: Sun, 23 Sep 2018 09:51:15 +0200 Subject: [PATCH 0454/1404] update checkpoints.dat --- src/blocks/checkpoints.dat | Bin 198276 -> 208420 bytes src/cryptonote_core/blockchain.cpp | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/blocks/checkpoints.dat b/src/blocks/checkpoints.dat index 08555850414691beb9b4b841911b7a347e8cb90e..03e2cd2a81769168f39f2628dad7358217ad763c 100644 GIT binary patch delta 10246 zcmV+hDEZffjSQsR3<7Z(fdYjB0fhnrwE_cz27f6??wS^+knj5+|Lu2}KVimbbP`$a z;;>+*330#J;nTuuWHLUdsZ2hYS4v1r_(m>q;TTO1tKDjBc9>icj!?)iV;{-&&pp<@ zrVb{v@;-lRr$42?B}^!Odb<;6onm$_UCJ?S*j4cKq_R5oT!db&5;t6*E~f3Nsi^Wt z6@M)F%i)!SNd{TtuX)Vdp3sH=uem?LzeyM_&1-^W3ow4!wlzcwGU%q^=BL6+d#skZ zBtU&QgrOYTGC5w66WQH8_jBqzhkn~;>YZe!9MgRpiqD~RyTpUaf=+l1o`^+GI3zjZ z&BK7Brd6}thO+DpdJrKqaGBmd9I#OI;D7ajCi*eY(Qf?1bEcq?99`>F7cel1)BjtB zVaAA2dp?LY8%7xvgub5V_2&{jZ!fcrmJ-HT*>W*YiZyYvXu3I)?b{_{(W0Bzk4uI+ z1$KNJrw)xb@8gB6o9HXkvqM7w9wb?L1YME}3kJXIn8Da-%!fHDFEZJJUCBRloPX#8 zOrjaM*{5a}eceq8xOH{$Y_I(6V zNG9^?9dK>O10JfB+GcX~n&##;%-v1BvyB(NijXr-N9q52@|GS;+7DouLA1GHsDJ1r zm7lTG!TATor+D_zw>V2$~t52eP?(5S^&8IaUjSEK}%86+0;x# zeDges^K^Gg<+VmkD7>DM#_KvHzjQdq;;1Isl$OEOH(C>&?~BUKNdIw)Tz{9=sEQfu z3dsttWOyOfkLWdus)^%O_kLOQx>F$hRf`mewY{l}z(OU^HebyK33KSFnpNq`U2FFTaQvxeqmJb6 zxd=OB;IbL3NK=GR8RJ!UbpuSqO+wwWHn|f_s~U#y&M7YSUD-VaV30{XffgG899?kb z&H3@l@A#$r9*Y|}E~0c{Q715({sy+y5Il@Dq{PC$3luo)=o~riM}L4J_V@2$w9I7y z{14&}b>7`?fd$BeHDZ^>56F#IjTKlFZh~Mgen3np!javSJ2ebCw`&J9O?1yFg!a$D z?3Gj1O?lHREZ4=8V(nx2bkgJ?yduFkW+CCQ4~8(EmFmnYbMrdkqrwEO+qX&F$I|NF zd(xZ1{7aYp@_g~cEq~YoS)=y6?cphKd`z-w0;T!bj6+rHK6X_g?9SYdEaK52>sIv% zNmVM`AF0QWT}L*mfTQC9G*{9~(WOp|g%tk6!4=_V1@RIKE-;G=UhYN{C&hj}e_nDh zTkU^QIqdU~#8IH%oit#%>_jnndt~n3A(lXHed&@5E`M8+14`-#q*|!~gfcOO zRM6c11c}ndEKznAV=aEa{CkU0C!MJYnk|HpLr2)6{s2^L3d+)C!WT2#8>Co&pxa1K zNSHc|C8GyS1gR>VG*|a#@TtKT0<;-CMm39!Y{_uS#Jks;fyB!~@dVe5bQT%2*}Yn@wo)$186oj03CKcJbjO1jyET4P@l8A`4V`HEj=F3E z=Lw1n-Osc5FJ~oedq_z~# z!qP`I5`S_}+FZ3YWv4X(X0(-S84*=zSvPH+c{Xx!^uN<6IMN%9IcxCqj|u05WgCA% zqK-O^>3*&Og=){g_a`$+|1aye0X|kCM=5i=>r@=1@UGjexkR%Ro^gb2%rhBr@2b?NKUpI)lO5Y1)S{FY(B&nSX|i`U8iSLNtdTK;{8H6a(Y*OcS3(iCQrotj^---ojmnxx1cO{>A#7ddOoqRG8}SM zm+OwLQ7=xWdJ3)Q4GqxnHag0MKAk*j>N$n_?=cZiE*?mexVP8_5PjV)xje@?3Ajo6 zYk$}aERC@YZlm=f85mz@mvr%QzYHyZ$MC-CZfXX7B~(-|q3zt7oRXc$YppRu%Dy2Sl-Q2~>Zigri?AwYpMahLZShQBLffR4nR%6bJ7 zo2)W^rp+R6q*}?ubdj`TVwykNy0kRU34h4<#VnY`?ig^&lwmutQ?bFpG_O_gD04*| z;6!QSEBylHQSHF?Z5feBh> z&Q1+=2X}`C0F?ks8ISmwuvn~n|&qi^lNqu{YV zRjS<8I+80Y;|!HlV^@7LSFMvvq9GiUSqe$jT$3t#O(PW;Dm|O3n^Dw9Pi4K>T5L!` zhIxe9d!Sj1)Imo!@2xURX!kv?R)4pUx=Qo{!IT2=Hk}LTaks`;K)|#dL9BAs1UfZl z?;PcxN$E^TXAn=hC=Kf}-wkoUiwIqM zCn*s{k-n>sDK|E@wq9iKKln-&S#u>YXe~uH zNxCg97UAb`U~#rIm6RdiUZVbJMYm87oqp$H;N_2*BuS%qn}k^<8%jR_FJHAbQ<9O9)GBQj+?&*J(%7O zPj15zES!#!#5?oFdvj39TK&1L1)rPH{4g9@sC*6^_h2*QseFKa&~QsFBU)+lK>g%B zZ-wj&)9jCS?x3WYDIo5DXkb3(9||72VjkJu0eq*3tFj_qCskPoFt`ZKhR%w(T${lk z&unprgQ#klHDng&P6iT>bX33RqH&YZ;+|wIUs89pK+X%mi z#1K1gaitd|%zsyo{`AT}!8$K>{1_nqT4nKPlYV6{=|EDd%D`UWMPo9W4?-#40)~I9 z)*($f^2!r_hfQP4E%np|4jFUl3T7?AAHvY%cayu1cYO5urS*R5`sHy)*63FET>0Yk zg_#D=w##t1MDrglepl`FFEsPy(Z48}ftW}(eWsIn8h-_qZH0RkhD?uLuymEK2M4By zr1z%khT_;B8(KzpXuvq;eXh}>uS{#3IVf@$%WJgps2L(#1uCGKr~Z)Xn}{r!vI$R< z*PkJz0ghGmd0Vn!xtqF9|L%#|Sn0?rxPY@1W#l5=GAH5GpdfI|T-sU89;jBYadUfN zE-l>>2{zR!S+{${Hcf!)H3*_i^3! z9D$Ha`ab*H zv<1l5O3}cm3M;29d!po9Y@e0g zQbVwpoxe(=lGqKqwhWnB5ScAg%;C2nHDnw4czV6b zrdJM=myHfZL}6$Iv?J|8yS3&+R=xpSxqk@gnYzBr9LdztUFMhg*nV~#i8q>a5-TRV z?rLH@!)^S%BpFC2JM&JDnJ`vB-y9vu?}7$M8818ogmOTY-+Hvdkm+F}N4wHypHB_} z3x4tsRJx{~N#-px;@uvbb)Mpy@SdV8J)578>bh-1JheJcf$#NG?p7iLZ3N(T`hP$n z9n%uLvH=I8c2MkVufQLs@GQzFpFW_P-!)c!rInw=DGq2 z)0WZV1fyoRq-Q{*zS`>W@`?TJO4mWc#cuBf_dXPXD0jtWF<>_?@fdo6w0}$J%4*iQ z9zL$eNj@*1)|k#K4~5wuGOkB?^Z3`LneAO(=1Mr5NJcev7GLG)Hv6r6-#L$h>C2E} zdaEWEPQ)_u*Qd@{o!{L3Li%T8{ZqpKnkM2}jXBWDZ(SV{2wZMLuqqBK%H5Y!#y=XJ zfwsxI6IQ1w!<1}tw#vw2ReukpKiAQ6-*x1xkuBR-V;02)s+z-82*0o4u+&U)T<^FO zNf31^Oq$!_a+iX%47Q!dB#r2slDC*R&9weY^tueK8j6x6eoZ#eF6HsuKlOf6y=4Gz z>NdJ$JD!(z7G0rpubZf-U6xiS%vmrkGODJ9D~z+MX5JG#yKwF$7k@l$eqd6h?8_R?Jyeqk>)8F6Y11ya@+^k+}F#5F+mdR~R3JC{abhEH-ek&9%CN-SL@}d8um| ztpHlB0NwOZi4w&~1A|>28{W8rzTv4oUFEW#;-+LY@WT(}f^<05KL;U9*5(Lq!mi8mRgeK#_P&Txn{UM8sFG z8BneqBb{GcM#;O?WLFfQ)y(JQ!`pk;H@;kE7aOKa84I%!wtw2~MwGVBB;a6EX6GzA z3cw4c?Ql?8&O+81m~k23QTBP}i9N(3K0tK+Csy4P(-UfyuZF=-m&Hi|a4rfkzoQ%j zU6OMe7)w<*2wVk{v`qiSK4_B&f|YAKbEF6>BgiJali$9#i`C|h*q`cAvb7RdF!seG zFEV6&6JYR^o~?Gs$((lOca|FTP_W~v^ zmp`2=P=8|Kc))M$djIC!)}c42wA$*4(#^klKEXH^t&LYInf|iYeie9cSb@~h*pfK0 zqH!8;i;BnP{MAyEu@`t2+!<(It1m}3hMnzU9IOOv?bP^2BQ+S`rX;yKDeo{wry$W_ zl5FW>DwdznYEl7@MN9LEHB}jivsCpnCz$W3&VL3uLr5Q_s(ZxTeQ^ceuPKZKqSe1u zT2tdy_aP>$F{r$_(i0>QqRS=^b`4KebiI#2Roz7$(KD|#E|($BlI-P$OHg$vb#kZO z@z-io%K_{{lwNd0dHFJ&w67tBAO#MnXI(t47R-yC% zLA%yCbS_^d`7x0P;^dzjT)i#J%@Ve38Gk5_HV;Zy7PU~;P!Z?75J zzm9%Az^7I?^R{Y_j=kEE-mBLu)SUb%%fC5*3DT~H_v9Xia1F{6kIKdP0(`paB8rzw zp(lrQY*h&+E-4|CNQ8_wWB<&F|jTKg`M7f#fy zc?FX8OutFvf3mccA{Y3GuZYmj)lkYnmqxcb+0~+J>T)uTCIf!1F=GV#CzgbCB;4H9 zI5LacZy;SsfDX8w1NZ-0SilC5iJcuN;O2`0SS-}rG7j}D(M8XlwAC!R3m zF+B1z&8rly1j7CSoP6G?TCeutW2~9ft7d0ja1_4f3*i$pU%wPWvnlHs4nCYDSYDN8 zN^feQRwFtKe!E|ADSb@o_R4)-1gZeCi4K`+Um`beH&GbyNn2yZduy85Zhts@JQ_6e z;DKPS3u5$M0NFfED6KgO>oM?+u?muwHt-{(mZ=`Io(Qlf^ z(#a39BSuJJ2bUNEk4OZIQ_(oL>HUecYP5OM9=HG_uN<2Y87=v|18vVMDRGE;fBH~u z=d9PNw$p6JU?|c*an;oW)j$6QP~%Ftgjsou@S}^7U!Wg~H@QTr*MFSG7i-f?&AWyb zqa&aqyVVvUc6cZleMo0lm&v%66|8#0d@#1XQtKbmbuZUu?X!x2RUl1~QN&9O$O`tt z#ciUU((J4e2rVs0EBSkowjMhZXfgT%@R)LVF|WAc)e(!+H)RM-+G#}axOrFZ@0r6O z%Hb49EbMo?GNF2tnSXj~BcctN;D_qp@0pJ(HSE`+5@WUC~}Jl}IyPFCPOiZo~=v5`XTnzxQ;3?H2b^n$Z1x zV(F8;z7dupwFqM2&vOX>btR4U?zWkW%ir~lJ8@QQnf%@;dTx2RxcKO4h+2qt;NF@|GuDUcX6b15Og!`;oG(3d_Xqi8~TB}UF*n33r zU>?K-C+hcFLphtw;To=j(gT4rgNuKBUWHdQJ^eCwfq&_I!wtZ3RYLO)ETz)HLMHc? zI_u`AVVPwK(^;c9)eQgw6kp>Z-ziej5W8eIm@U7Z#87V?q-eaKyNkRxdS)tC{f&wQ zcG-Jwl%gK}gi=^V9byi5nxN-(*t4Ed)~_x39Yv9UdP7}}TXBnj7>z$Atgz|C)x2cG zzGtygvVXv&`hhu3h_${_z)a$NkB~t@WBmpME6eIiGn81Dsj`u6l~nZ$(sQh^-TWr@ zA|%@4#89^^9Ehj1wtA{K>{{n^qADU28hz}+veL9`@?@m&#)_MX-W1|01J{PIXy@r+ zcp%6;fh>Nrmz^R1zsd>JwtHgZeQ2$xMB&erMt|)XEW>VRo@9qy0I%j+S${2`&6DJs z_=0t-PD0-3t|FCh+v819eCKtue*QXPU^{Wh+sb73|C>aN5`o+@U6Ce3x7nbhAQbz? zt?0_#>#478Rp$6!oszsmvV%impdOmYtLsUZ@V|_C?v+AUr)ob-^)+Cr;wb=FLxqPi zTYr`yId6{X&%?Z*p~G2zA9ebITp96~iKC@KkVb)jrk@#yTfdzMS=DEU+SXfJHaG|%AJdcHg^5&^&JF@XdHm4BC5;1pqN7!Biu6^R)3;H zt|7tB$9W*MQdz;BN5z@gx4n#jF=6_;(vkH$40w{I>5`xyCm*zM)(=w4LE|Nr~0_rB?F`dl`n~2sQ_l>9-$n*sKa27R=Qe z%{CR>d9K2JR7<~ou__oSi!3GHvV%l(YVKsD(jTgPCsTE;T+_oa>XOc@QXPY|akd1o zh4;o=9Q;*@H}y}tV>V4Zvws8*RzwU0eyx}KaS#BA8E`z!EDJ%A^o(OA`C$%^lX>Dd@_v~B1VAh z+bn@O7u*#ynog8`Jp5B}^Y^5fRb|1j{VtCnX2+q=NH{TLR@S&rqOPp~{fsnpL=9Gl zL^D46>_^tEyLZJIc7Gxe9VpJmipvm!3>yG;1LehlZ^RbAz@+HCBcIwYM`gT){&)&c z>PozySq9}Z)f+>D(f|2|*L8wCPEhqpRFw6Ut9;#Z5FkRQxj_nPE-`bezY%VWKyBO= zT_A{N;Gy{5g|!vbP1uAgJS%)spXa4}?7%#HPTS~Y=Os5ZuYXz#^Xd!judS0eUh`|K zn{gUbxNDYwTpsUI@?KF^eBk9@N_RRg2|>8dr24Rku~CdLL%CMRKqL5;G+~yyQX12T zM+3mXB%bv+a{HS}X)08P;%k9811cFyilv$z%*cd6{-n95 zu^l;!IuM^%0cpVntjCB_bv(KgAN5oO&_sO4ofgFl_mvzmL-iJ;OX0;iy#a*}Y&mYW z=D&~0y2h_A^Y}tzzBZ-=f zC~xDIKqYyQOierP>cHP%3zuL7W#X2V2y1?D+_1J|e$azR2#FHD#%W3%8jvA{vxl#E zBUtS%6i5eqp%PA8eZx^ba2(_9I9&Tl;{eA50Q~_~syOS?2v3;&dof);a?#MFO|!VK z^}wJ^T7MkVuhJroQ@vz}s4=%%o*St+u3y8lZ}2-ZllQJa8p14-K~+^`^^@`jdW~2# z^$<>hgc`=rSF7S!7EKNaoxo}2S@a#f88Wq1Qt{$@XzzdG5S85U^|Tj~1CeZi6U*IN z2@e!wLVuT+1JEX`N)=|dZoP51d{U%HI3yRMzkgK!R)_1&OL3fhdCT%2Js`jS1}2$UycX-RJzF%jyJTSm_? zbo&U4Ztud+cEghwi<>$Jm$j5}+UX3Tn3&<2^c%lC6FNOLR(6U*N^-OpO|PO%bLtZo z)_(>F?dQ;8-R7qLv4r1nHJ!>Qvu^f*ejGq7Zvp|B(OD4dPu3HL)@uTU$W6H{yhx&Nd767zmB3jd25@P|6A}d!sD}tT8bR!vRE7i=K7kxFmkcCEWYnrTceO+w zAM&uXGoO~PrpzShuY#EJF-D&npmor4@AN#hj#OK1zHZ2}o{_sY%Y8=!%_~C)?|)vd zRUfsFce@O89XTcH0q5N4nBN0^i0a_}0Y(s1w=pcEMmKk(tAd}S|8eti`D83P)|W1`+sTrl`@w(c3K18uyyo+`7twV;tT^UIKqRbCu-zG zR%{@5t~Wr!gj(W^pqk%HiJ?eCz^?On1*T^Dh<~sR?ZSs8 z!B`&4irMuW|J1RG&VLTy$nvqmH=3Osh{-Q-k=uMYlTEx@ug9--K`vXAJ^GsHI_ISm ze$BNymzze@{-XdZw%Cz!sWhT>D%wnL25L#42?oNj2m3cl5B#0^A6S7*FYd2T-)%)r z^l1^x1_l?cY!UK7IeVh5mVej^){^IVGS?^fl`Hj=ENCza9%qG=vKj^7&n#$a)es%c zwl_H7IDBFjv(HVSIL>f87DA^#9TFb=@q$9P?AZ0g5e@ljN;@Kk&RLIcJViGiCT{yW z670(0YPEGi*TQV;fIIf9dow@I^PDDNNt`BndZ9^<*^T|Z5yD!J(|>vwO*juc$25QF zid#*xA&?XqiUUc2WTFc9P~3hj>C&LJijJ!IJZb~jeSF;+P2>c4+97k=L+j~9-{ind!I(J6rAxeDk^rdEr zwh+z+nSH74Bz&bpeGki-wlB)N6@LK)6a$+hk(nb~QYbv^ zg=Y8LTTP=i4+_!p6MNi|h(8+-`IFOow~I#R*-^%L{LcJSf8sQT6y=lzM#6a1ech%M zkP9lD$?B295(Yx(Bm7h?Iohg*s~La1;Ra3DrQ&&N5ib*w3+RU$>rkOph61$0yf7(DTR>*E`U=AUZ6m z;x$i||Efk$I7H5C`Bf6HaDITMKboK-+ro$Q(1EpcbWUmUi;C{PlB`Nh5!JpyKR2f<4SXbnf`3!KlQE)G#Cl|E6KX|o?%0@{Qw9TM zE@P%aua4xB`d>syu3|%r8U7JmQsre|fVD3TU0l~@{-`cl1;HQF`hB~Vo5fBl8hOYu zzvrRq&HpY6hI)OXP6=WleS*hHgx#kQgHJOxFrQH}WpByjth)W>xyaK1#UTo)+lC&V zWp4;6BSa=}15}bec;x6C5%XR;OP-1V>3?+A_yryNMGaU9-J^tLl`ar@PE@DA_^c2| zZG)~~=K#H5P&DV^XhJr3&XdZ$dYgksp652wEhN4o>$@NyK&G>yeO}l@XS=`My Date: Sun, 23 Sep 2018 13:47:30 +0000 Subject: [PATCH 0455/1404] unit_tests: fix build with GCC 5.4.0 on ubuntu --- tests/unit_tests/is_hdd.cpp | 4 ++-- tests/unit_tests/wipeable_string.cpp | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/unit_tests/is_hdd.cpp b/tests/unit_tests/is_hdd.cpp index 1be670e5e1f..040af4f47cc 100644 --- a/tests/unit_tests/is_hdd.cpp +++ b/tests/unit_tests/is_hdd.cpp @@ -6,12 +6,12 @@ TEST(is_hdd, linux_os_root) { std::string path = "/"; - EXPECT_TRUE(tools::is_hdd(path.c_str())); + EXPECT_TRUE(tools::is_hdd(path.c_str()) != boost::none); } #else TEST(is_hdd, unknown_os) { std::string path = ""; - EXPECT_FALSE(tools::is_hdd(path.c_str())); + EXPECT_FALSE(tools::is_hdd(path.c_str()) != boost::none); } #endif diff --git a/tests/unit_tests/wipeable_string.cpp b/tests/unit_tests/wipeable_string.cpp index 65718fd4563..44e050c5c81 100644 --- a/tests/unit_tests/wipeable_string.cpp +++ b/tests/unit_tests/wipeable_string.cpp @@ -194,13 +194,13 @@ TEST(wipeable_string, parse_hexstr) ASSERT_EQ(boost::none, epee::wipeable_string("0").parse_hexstr()); ASSERT_EQ(boost::none, epee::wipeable_string("000").parse_hexstr()); - ASSERT_TRUE((s = epee::wipeable_string("").parse_hexstr())); + ASSERT_TRUE((s = epee::wipeable_string("").parse_hexstr()) != boost::none); ASSERT_EQ(*s, ""); - ASSERT_TRUE((s = epee::wipeable_string("00").parse_hexstr())); + ASSERT_TRUE((s = epee::wipeable_string("00").parse_hexstr()) != boost::none); ASSERT_EQ(*s, epee::wipeable_string("", 1)); - ASSERT_TRUE((s = epee::wipeable_string("41").parse_hexstr())); + ASSERT_TRUE((s = epee::wipeable_string("41").parse_hexstr()) != boost::none); ASSERT_EQ(*s, epee::wipeable_string("A")); - ASSERT_TRUE((s = epee::wipeable_string("414243").parse_hexstr())); + ASSERT_TRUE((s = epee::wipeable_string("414243").parse_hexstr()) != boost::none); ASSERT_EQ(*s, epee::wipeable_string("ABC")); } From a69271faf78ce6071fd58e21c08f69ddc8c7814d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20K=C4=85kol?= Date: Sun, 23 Sep 2018 18:31:30 +0200 Subject: [PATCH 0456/1404] Fixed a typo --- cmake/CheckLinkerFlag.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/CheckLinkerFlag.cmake b/cmake/CheckLinkerFlag.cmake index a3879d0befa..2b507ab7144 100644 --- a/cmake/CheckLinkerFlag.cmake +++ b/cmake/CheckLinkerFlag.cmake @@ -39,7 +39,7 @@ macro(CHECK_LINKER_FLAG flag VARIABLE) endif() set(${VARIABLE} "" CACHE INTERNAL "Have linker flag ${flag}") file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log - "Determining if the ${flag} linker flag is suppored " + "Determining if the ${flag} linker flag is supported " "failed with the following output:\n" "${OUTPUT}\n\n") endif() From 1dd5c73f62edc9edc54f46bbff2f5eb43ee9a0e0 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 24 Sep 2018 14:01:33 +0000 Subject: [PATCH 0457/1404] bulletproofs: multiply points by 8 first thing in verification instead of merging that with other scalar multiplications where possible for speed, since this is not actually safe --- src/ringct/bulletproofs.cc | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/src/ringct/bulletproofs.cc b/src/ringct/bulletproofs.cc index abe4ef18dcb..381f5087211 100644 --- a/src/ringct/bulletproofs.cc +++ b/src/ringct/bulletproofs.cc @@ -1068,6 +1068,14 @@ bool bulletproof_VERIFY(const std::vector &proofs) CHECK_AND_ASSERT_MES(!(x_ip == rct::zero()), false, "x_ip == 0"); PERF_TIMER_STOP(VERIFY_start); + // pre-multiply some points by 8 + rct::keyV proof8_V = proof.V; for (rct::key &k: proof8_V) k = rct::scalarmult8(k); + rct::keyV proof8_L = proof.L; for (rct::key &k: proof8_L) k = rct::scalarmult8(k); + rct::keyV proof8_R = proof.R; for (rct::key &k: proof8_R) k = rct::scalarmult8(k); + rct::key proof8_T1 = rct::scalarmult8(proof.T1); + rct::key proof8_T2 = rct::scalarmult8(proof.T2); + rct::key proof8_S = rct::scalarmult8(proof.S); + PERF_TIMER_START_BP(VERIFY_line_61); // PAPER LINE 61 sc_muladd(y0.bytes, proof.taux.bytes, weight.bytes, y0.bytes); @@ -1090,26 +1098,22 @@ bool bulletproof_VERIFY(const std::vector &proofs) multiexp_data.reserve(proof.V.size()); sc_sub(tmp.bytes, proof.t.bytes, tmp.bytes); sc_muladd(y1.bytes, tmp.bytes, weight.bytes, y1.bytes); - for (size_t j = 0; j < proof.V.size(); j++) + for (size_t j = 0; j < proof8_V.size(); j++) { - sc_mul(tmp.bytes, zpow[j+2].bytes, EIGHT.bytes); - multiexp_data.emplace_back(tmp, proof.V[j]); + multiexp_data.emplace_back(zpow[j+2], proof8_V[j]); } rct::addKeys(Y2, Y2, rct::scalarmultKey(multiexp(multiexp_data, false), weight)); - rct::key weight8; - sc_mul(weight8.bytes, weight.bytes, EIGHT.bytes); - sc_mul(tmp.bytes, x.bytes, weight8.bytes); - rct::addKeys(Y3, Y3, rct::scalarmultKey(proof.T1, tmp)); + sc_mul(tmp.bytes, x.bytes, weight.bytes); + rct::addKeys(Y3, Y3, rct::scalarmultKey(proof8_T1, tmp)); rct::key xsq; sc_mul(xsq.bytes, x.bytes, x.bytes); - sc_mul(tmp.bytes, xsq.bytes, weight8.bytes); - rct::addKeys(Y4, Y4, rct::scalarmultKey(proof.T2, tmp)); + sc_mul(tmp.bytes, xsq.bytes, weight.bytes); + rct::addKeys(Y4, Y4, rct::scalarmultKey(proof8_T2, tmp)); PERF_TIMER_STOP(VERIFY_line_61rl_new); PERF_TIMER_START_BP(VERIFY_line_62); // PAPER LINE 62 - sc_mul(tmp.bytes, x.bytes, EIGHT.bytes); - rct::addKeys(Z0, Z0, rct::scalarmultKey(rct::addKeys(rct::scalarmult8(proof.A), rct::scalarmultKey(proof.S, tmp)), weight)); + rct::addKeys(Z0, Z0, rct::scalarmultKey(rct::addKeys(rct::scalarmult8(proof.A), rct::scalarmultKey(proof8_S, x)), weight)); PERF_TIMER_STOP(VERIFY_line_62); // Compute the number of rounds for the inner product @@ -1192,11 +1196,9 @@ bool bulletproof_VERIFY(const std::vector &proofs) for (size_t i = 0; i < rounds; ++i) { sc_mul(tmp.bytes, w[i].bytes, w[i].bytes); - sc_mul(tmp.bytes, tmp.bytes, EIGHT.bytes); - multiexp_data.emplace_back(tmp, proof.L[i]); + multiexp_data.emplace_back(tmp, proof8_L[i]); sc_mul(tmp.bytes, winv[i].bytes, winv[i].bytes); - sc_mul(tmp.bytes, tmp.bytes, EIGHT.bytes); - multiexp_data.emplace_back(tmp, proof.R[i]); + multiexp_data.emplace_back(tmp, proof8_R[i]); } rct::key acc = multiexp(multiexp_data, false); rct::addKeys(Z2, Z2, rct::scalarmultKey(acc, weight)); From b35beaa8d657aabce3e6567c2db6b52bc73a56a8 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 20 Sep 2018 13:03:21 +0000 Subject: [PATCH 0458/1404] wallet_rpc_server: include account index in incoming_transfers RPC --- src/wallet/wallet_rpc_server.cpp | 2 +- src/wallet/wallet_rpc_server_commands_defs.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index 86b46b1734a..752d7a68644 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -1539,7 +1539,7 @@ namespace tools rpc_transfers.spent = td.m_spent; rpc_transfers.global_index = td.m_global_output_index; rpc_transfers.tx_hash = epee::string_tools::pod_to_hex(td.m_txid); - rpc_transfers.subaddr_index = td.m_subaddr_index.minor; + rpc_transfers.subaddr_index = {td.m_subaddr_index.major, td.m_subaddr_index.minor}; rpc_transfers.key_image = req.verbose && td.m_key_image_known ? epee::string_tools::pod_to_hex(td.m_key_image) : ""; res.transfers.push_back(rpc_transfers); } diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h index 4501cf575e1..c3da3e7a8b9 100644 --- a/src/wallet/wallet_rpc_server_commands_defs.h +++ b/src/wallet/wallet_rpc_server_commands_defs.h @@ -859,7 +859,7 @@ namespace wallet_rpc bool spent; uint64_t global_index; std::string tx_hash; - uint32_t subaddr_index; + cryptonote::subaddress_index subaddr_index; std::string key_image; BEGIN_KV_SERIALIZE_MAP() From bb6e3bbc0f0a6c4ac126f56ba75b39d0dfac01a6 Mon Sep 17 00:00:00 2001 From: cslashm Date: Wed, 1 Aug 2018 09:24:53 +0200 Subject: [PATCH 0459/1404] Replace USB-CCID (smartcard) by USB-HID Remove PCSC dependencies which is a bit hard (not user friendly) to install on linux and Mac Split Ledger logic and device IO --- CMakeLists.txt | 30 ++-- cmake/FindHIDAPI.cmake | 46 ++++++ cmake/FindPCSC.cmake | 54 ------- src/device/CMakeLists.txt | 19 ++- src/device/device.cpp | 4 +- src/device/device.hpp | 3 +- src/device/device_io.hpp | 56 +++++++ src/device/device_io_hid.cpp | 288 +++++++++++++++++++++++++++++++++++ src/device/device_io_hid.hpp | 112 ++++++++++++++ src/device/device_ledger.cpp | 200 ++++++++---------------- src/device/device_ledger.hpp | 38 +++-- src/device/log.cpp | 41 ++--- src/device/log.hpp | 7 +- 13 files changed, 645 insertions(+), 253 deletions(-) create mode 100644 cmake/FindHIDAPI.cmake delete mode 100644 cmake/FindPCSC.cmake create mode 100644 src/device/device_io.hpp create mode 100644 src/device/device_io_hid.cpp create mode 100644 src/device/device_io_hid.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index a95e14b6583..2b5a3850919 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -435,7 +435,7 @@ if(STATIC AND NOT IOS) endif() endif() -find_package(PCSC) +find_package(HIDAPI) add_definition_if_library_exists(c memset_s "string.h" HAVE_MEMSET_S) add_definition_if_library_exists(c explicit_bzero "strings.h" HAVE_EXPLICIT_BZERO) @@ -476,14 +476,14 @@ endif() include_directories(${LIBUNWIND_INCLUDE}) link_directories(${LIBUNWIND_LIBRARY_DIRS}) -# Final setup for libpcsc -if (PCSC_FOUND) - message(STATUS "Using PCSC include dir at ${PCSC_INCLUDE_DIR}") - add_definitions(-DHAVE_PCSC) - include_directories(${PCSC_INCLUDE_DIR}) - link_directories(${LIBPCSC_LIBRARY_DIRS}) -else (PCSC_FOUND) - message(STATUS "Could not find PCSC") +# Final setup for hid +if (HIDAPI_FOUND) + message(STATUS "Using HIDAPI include dir at ${HIDAPI_INCLUDE_DIR}") + add_definitions(-DHAVE_HIDAPI) + include_directories(${HIDAPI_INCLUDE_DIR}) + link_directories(${LIBHIDAPI_LIBRARY_DIRS}) +else (HIDAPI_FOUND) + message(STATUS "Could not find HIDAPI") endif() if(MSVC) @@ -881,6 +881,18 @@ endif() list(APPEND EXTRA_LIBRARIES ${CMAKE_DL_LIBS}) +if (HIDAPI_FOUND) + if (APPLE) + find_library(COREFOUNDATION CoreFoundation) + find_library(IOKIT IOKit) + list(APPEND EXTRA_LIBRARIES ${IOKIT}) + list(APPEND EXTRA_LIBRARIES ${COREFOUNDATION}) + endif() + if (WIN32) + list(APPEND EXTRA_LIBRARIES setupapi) + endif() +endif() + option(USE_READLINE "Build with GNU readline support." ON) if(USE_READLINE) find_package(Readline) diff --git a/cmake/FindHIDAPI.cmake b/cmake/FindHIDAPI.cmake new file mode 100644 index 00000000000..a689fb4eb6b --- /dev/null +++ b/cmake/FindHIDAPI.cmake @@ -0,0 +1,46 @@ +# - try to find HIDAPI library +# from http://www.signal11.us/oss/hidapi/ +# +# Cache Variables: (probably not for direct use in your scripts) +# HIDAPI_INCLUDE_DIR +# HIDAPI_LIBRARY +# +# Non-cache variables you might use in your CMakeLists.txt: +# HIDAPI_FOUND +# HIDAPI_INCLUDE_DIRS +# HIDAPI_LIBRARIES +# +# Requires these CMake modules: +# FindPackageHandleStandardArgs (known included with CMake >=2.6.2) +# +# Original Author: +# 2009-2010 Ryan Pavlik +# http://academic.cleardefinition.com +# Iowa State University HCI Graduate Program/VRAC +# +# Copyright Iowa State University 2009-2010. +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) + +find_library(HIDAPI_LIBRARY + NAMES hidapi hidapi-libusb) + +find_path(HIDAPI_INCLUDE_DIR + NAMES hidapi.h + PATH_SUFFIXES + hidapi) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(HIDAPI + DEFAULT_MSG + HIDAPI_LIBRARY + HIDAPI_INCLUDE_DIR) + +if(HIDAPI_FOUND) + set(HIDAPI_LIBRARIES "${HIDAPI_LIBRARY}") + + set(HIDAPI_INCLUDE_DIRS "${HIDAPI_INCLUDE_DIR}") +endif() + +mark_as_advanced(HIDAPI_INCLUDE_DIR HIDAPI_LIBRARY) diff --git a/cmake/FindPCSC.cmake b/cmake/FindPCSC.cmake deleted file mode 100644 index 0819196558c..00000000000 --- a/cmake/FindPCSC.cmake +++ /dev/null @@ -1,54 +0,0 @@ -# - Find PCSC -# Find the native PCSC includes and library -# -# PCSC_INCLUDE_DIR - where to find winscard.h, wintypes.h, etc. -# PCSC_LIBRARIES - List of libraries when using PCSC. -# PCSC_FOUND - True if PCSC found. - - -IF (PCSC_INCLUDE_DIR AND PCSC_LIBRARIES) - # Already in cache, be silent - SET(PCSC_FIND_QUIETLY TRUE) -ENDIF (PCSC_INCLUDE_DIR AND PCSC_LIBRARIES) - -IF (NOT WIN32) - FIND_PACKAGE(PkgConfig) - PKG_CHECK_MODULES(PC_PCSC libpcsclite) - - FIND_PATH(PCSC_INCLUDE_DIR winscard.h - HINTS - /usr/include/PCSC - ${PC_PCSC_INCLUDEDIR} - ${PC_PCSC_INCLUDE_DIRS} - PATH_SUFFIXES PCSC - ) - - FIND_LIBRARY(PCSC_LIBRARY NAMES pcsclite libpcsclite PCSC - HINTS - ${PC_PCSC_LIBDIR} - ${PC_PCSC_LIBRARY_DIRS} - ) - -ELSE (NOT WIN32) - IF(BUILD_64 STREQUAL "ON") - set(PCSC_INCLUDE_DIR /mingw64/x86_64-w64-mingw32/include) - set(PCSC_LIBRARY /mingw64/x86_64-w64-mingw32/lib/libwinscard.a) - ELSE(BUILD_64 STREQUAL "ON") - set(PCSC_INCLUDE_DIR /mingw32/i686-w64-mingw32/include) - set(PCSC_LIBRARY /mingw32/i686-w64-mingw32/lib/libwinscard.a) - ENDIF(BUILD_64 STREQUAL "ON") -ENDIF (NOT WIN32) - -# handle the QUIETLY and REQUIRED arguments and set PCSC_FOUND to TRUE if -# all listed variables are TRUE -INCLUDE(FindPackageHandleStandardArgs) -FIND_PACKAGE_HANDLE_STANDARD_ARGS(PCSC DEFAULT_MSG PCSC_LIBRARY PCSC_INCLUDE_DIR) - -IF(PCSC_FOUND) - SET( PCSC_LIBRARIES ${PCSC_LIBRARY} ) - SET(PCSC_STATIC_LIBRARIES ${PCSC_STATIC_LIBRARY}) -ELSE(PCSC_FOUND) - SET( PCSC_LIBRARIES ) -ENDIF(PCSC_FOUND) - -MARK_AS_ADVANCED( PCSC_LIBRARY PCSC_INCLUDE_DIR PCSC_STATIC_LIBRARY) diff --git a/src/device/CMakeLists.txt b/src/device/CMakeLists.txt index cc2d20a54ed..8f446f42a24 100644 --- a/src/device/CMakeLists.txt +++ b/src/device/CMakeLists.txt @@ -32,18 +32,27 @@ set(device_sources log.cpp ) -if(PCSC_FOUND) - set(device_sources ${device_sources} device_ledger.cpp) +if(HIDAPI_FOUND) + set(device_sources + ${device_sources} + device_ledger.cpp + device_io_hid.cpp + ) endif() set(device_headers device.hpp + device_io.hpp device_default.hpp log.hpp ) -if(PCSC_FOUND) - set(device_headers ${device_headers} device_ledger.hpp) +if(HIDAPI_FOUND) + set(device_headers + ${device_headers} + device_ledger.hpp + device_io_hid.hpp + ) endif() set(device_private_headers) @@ -65,7 +74,7 @@ monero_add_library(device target_link_libraries(device PUBLIC - ${PCSC_LIBRARIES} + ${HIDAPI_LIBRARIES} cncrypto ringct_basic ${OPENSSL_CRYPTO_LIBRARIES} diff --git a/src/device/device.cpp b/src/device/device.cpp index 8a8b4006133..50041baef57 100644 --- a/src/device/device.cpp +++ b/src/device/device.cpp @@ -29,7 +29,7 @@ #include "device.hpp" #include "device_default.hpp" -#ifdef HAVE_PCSC +#ifdef WITH_DEVICE_LEDGER #include "device_ledger.hpp" #endif #include "misc_log_ex.h" @@ -45,7 +45,7 @@ namespace hw { device_registry::device_registry(){ hw::core::register_all(registry); - #ifdef HAVE_PCSC + #ifdef WITH_DEVICE_LEDGER hw::ledger::register_all(registry); #endif } diff --git a/src/device/device.hpp b/src/device/device.hpp index 87f1430f4fc..cb91176503a 100644 --- a/src/device/device.hpp +++ b/src/device/device.hpp @@ -48,11 +48,12 @@ #include "crypto/chacha.h" #include "ringct/rctTypes.h" + #ifndef USE_DEVICE_LEDGER #define USE_DEVICE_LEDGER 1 #endif -#if !defined(HAVE_PCSC) +#if !defined(HAVE_HIDAPI) #undef USE_DEVICE_LEDGER #define USE_DEVICE_LEDGER 0 #endif diff --git a/src/device/device_io.hpp b/src/device/device_io.hpp new file mode 100644 index 00000000000..96163a2113e --- /dev/null +++ b/src/device/device_io.hpp @@ -0,0 +1,56 @@ +// Copyright (c) 2017-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + + + + +#pragma once + + +namespace hw { + namespace io { + + class device_io { + + public: + + device_io() {}; + ~device_io() {}; + + virtual void init() = 0; + virtual void release() = 0; + + virtual void connect(void *parms) = 0; + virtual void disconnect() = 0; + virtual bool connected() const = 0; + + virtual int exchange(unsigned char *command, unsigned int cmd_len, unsigned char *response, unsigned int max_resp_len) = 0; + }; + }; +}; diff --git a/src/device/device_io_hid.cpp b/src/device/device_io_hid.cpp new file mode 100644 index 00000000000..562aca8b8c7 --- /dev/null +++ b/src/device/device_io_hid.cpp @@ -0,0 +1,288 @@ +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +#if defined(HAVE_HIDAPI) + +#include "log.hpp" +#include "device_io_hid.hpp" + +namespace hw { + namespace io { + + #undef MONERO_DEFAULT_LOG_CATEGORY + #define MONERO_DEFAULT_LOG_CATEGORY "device.io" + + #define ASSERT_X(exp,msg) CHECK_AND_ASSERT_THROW_MES(exp, msg); + + #define MAX_BLOCK 64 + + static std::string safe_hid_error(hid_device *hwdev) { + if (hwdev) { + return std::string((char*)hid_error(hwdev)); + } + return std::string("NULL device"); + } + + static std::string safe_hid_path(const hid_device_info *hwdev_info) { + if (hwdev_info && hwdev_info->path) { + return std::string(hwdev_info->path); + } + return std::string("NULL path"); + } + + device_io_hid::device_io_hid(unsigned short c, unsigned char t, unsigned int ps, unsigned int to) : + channel(c), + tag(t), + packet_size(ps), + timeout(to), + usb_vid(0), + usb_pid(0), + usb_device(NULL) { + } + + device_io_hid::device_io_hid() : device_io_hid(DEFAULT_CHANNEL, DEFAULT_TAG, DEFAULT_PACKET_SIZE, DEFAULT_TIMEOUT) { + } + + void device_io_hid::io_hid_log(int read, unsigned char* buffer, int block_len) { + if (hid_verbose) { + char strbuffer[1024]; + hw::buffer_to_str(strbuffer, sizeof(strbuffer), (char*)buffer, block_len); + MDEBUG( "HID " << (read?"<":">") <<" : "<=0, "Unable to init hidapi library. Error "+std::to_string(r)+": "+safe_hid_error(this->usb_device)); + } + + void device_io_hid::connect(void *params) { + hid_conn_params *p = (struct hid_conn_params*)params; + this->connect(p->vid, p->pid, p->interface_number, p->usage_page, p->interface_OR_page); + } + + void device_io_hid::connect(unsigned int vid, unsigned int pid, unsigned int interface_number, unsigned int usage_page, bool interface_OR_page ) { + hid_device_info *hwdev_info, *hwdev_info_list; + hid_device *hwdev; + + this->disconnect(); + + hwdev_info_list = hid_enumerate(vid, pid); + ASSERT_X(hwdev_info_list, "Unable to enumerate device "+std::to_string(vid)+":"+std::to_string(vid)+ ": "+ safe_hid_error(this->usb_device)); + hwdev = NULL; + hwdev_info = hwdev_info_list; + while (hwdev_info) { + if ((interface_OR_page && ((usage_page == 0xffa0) || (interface_number == 0))) || + ((usage_page == 0xffa0) && (interface_number == 0)) ) { + MDEBUG("HID Device found: " << safe_hid_path(hwdev_info)); + hwdev = hid_open_path(hwdev_info->path); + break; + } else { + MDEBUG("HID Device discard: " << safe_hid_path(hwdev_info) << "("+std::to_string(hwdev_info->usage_page) << "," << std::to_string(hwdev_info->interface_number) << ")"); + } + hwdev_info = hwdev_info->next; + } + hid_free_enumeration(hwdev_info_list); + ASSERT_X(hwdev, "Unable to open device "+std::to_string(pid)+":"+std::to_string(vid)); + this->usb_vid = vid; + this->usb_pid = pid; + this->usb_device = hwdev; + } + + + bool device_io_hid::connected() const { + return this->usb_device != NULL; + } + + int device_io_hid::exchange(unsigned char *command, unsigned int cmd_len, unsigned char *response, unsigned int max_resp_len) { + unsigned char buffer[400]; + unsigned char padding_buffer[MAX_BLOCK+1]; + unsigned int result; + int hid_ret; + unsigned int sw_offset; + unsigned int remaining; + unsigned int offset = 0; + + ASSERT_X(this->usb_device,"No device opened"); + + //Split command in several HID packet + memset(buffer, 0, sizeof(buffer)); + result = this->wrapCommand(command, cmd_len, buffer, sizeof(buffer)); + remaining = result; + + while (remaining > 0) { + int block_size = (remaining > MAX_BLOCK ? MAX_BLOCK : remaining); + memset(padding_buffer, 0, sizeof(padding_buffer)); + memcpy(padding_buffer+1, buffer + offset, block_size); + io_hid_log(0, padding_buffer, block_size+1); + hid_ret = hid_write(this->usb_device, padding_buffer, block_size+1); + ASSERT_X(hid_ret>=0, "Unable to send hidapi command. Error "+std::to_string(result)+": "+ safe_hid_error(this->usb_device)); + offset += block_size; + remaining -= block_size; + } + + //get first response + memset(buffer, 0, sizeof(buffer)); + hid_ret = hid_read_timeout(this->usb_device, buffer, MAX_BLOCK, this->timeout); + ASSERT_X(hid_ret>=0, "Unable to read hidapi response. Error "+std::to_string(result)+": "+ safe_hid_error(this->usb_device)); + result = (unsigned int)hid_ret; + io_hid_log(1, buffer, result); + offset = MAX_BLOCK; + //parse first response and get others if any + for (;;) { + result = this->unwrapReponse(buffer, offset, response, max_resp_len); + if (result != 0) { + break; + } + hid_ret = hid_read_timeout(this->usb_device, buffer + offset, MAX_BLOCK, this->timeout); + ASSERT_X(hid_ret>=0, "Unable to receive hidapi response. Error "+std::to_string(result)+": "+ safe_hid_error(this->usb_device)); + result = (unsigned int)hid_ret; + io_hid_log(1, buffer + offset, result); + offset += MAX_BLOCK; + } + return result; + } + + void device_io_hid::disconnect(void) { + if (this->usb_device) { + hid_close(this->usb_device); + } + this->usb_vid = 0; + this->usb_pid = 0; + this->usb_device = NULL; + } + + void device_io_hid::release() { + /* Do not exit, as the lib context is global*/ + //hid_exit(); + } + + unsigned int device_io_hid::wrapCommand(const unsigned char *command, size_t command_len, unsigned char *out, size_t out_len) { + unsigned int sequence_idx = 0; + unsigned int offset = 0; + unsigned int offset_out = 0; + unsigned int block_size; + + ASSERT_X(this->packet_size >= 3, "Invalid Packet size: "+std::to_string(this->packet_size)) ; + ASSERT_X(out_len >= 7, "out_len too short: "+std::to_string(out_len)); + + out_len -= 7; + out[offset_out++] = ((this->channel >> 8) & 0xff); + out[offset_out++] = (this->channel & 0xff); + out[offset_out++] = this->tag; + out[offset_out++] = ((sequence_idx >> 8) & 0xff); + out[offset_out++] = (sequence_idx & 0xff); + sequence_idx++; + out[offset_out++] = ((command_len >> 8) & 0xff); + out[offset_out++] = (command_len & 0xff); + block_size = (command_len > this->packet_size - 7 ? this->packet_size - 7 : command_len); + ASSERT_X(out_len >= block_size, "out_len too short: "+std::to_string(out_len)); + out_len -= block_size; + memcpy(out + offset_out, command + offset, block_size); + offset_out += block_size; + offset += block_size; + while (offset != command_len) { + ASSERT_X(out_len >= 5, "out_len too short: "+std::to_string(out_len)); + out_len -= 5; + out[offset_out++] = ((this->channel >> 8) & 0xff); + out[offset_out++] = (this->channel & 0xff); + out[offset_out++] = this->tag; + out[offset_out++] = ((sequence_idx >> 8) & 0xff); + out[offset_out++] = (sequence_idx & 0xff); + sequence_idx++; + block_size = ((command_len - offset) > this->packet_size - 5 ? this->packet_size - 5 : command_len - offset); + ASSERT_X(out_len >= block_size, "out_len too short: "+std::to_string(out_len)); + out_len -= block_size; + memcpy(out + offset_out, command + offset, block_size); + offset_out += block_size; + offset += block_size; + } + while ((offset_out % this->packet_size) != 0) { + ASSERT_X(out_len >= 1, "out_len too short: "+std::to_string(out_len)); + out_len--; + out[offset_out++] = 0; + } + return offset_out; + } + + /* + * return 0 if more data are needed + * >0 if response is fully available + */ + unsigned int device_io_hid::unwrapReponse(const unsigned char *data, size_t data_len, unsigned char *out, size_t out_len) { + unsigned int sequence_idx = 0; + unsigned int offset = 0; + unsigned int offset_out = 0; + unsigned int response_len; + unsigned int block_size; + unsigned int val; + + //end? + if ((data == NULL) || (data_len < 7 + 5)) { + return 0; + } + + //check hid header + val = (data[offset]<<8) + data[offset+1]; + offset += 2; + ASSERT_X(val == this->channel, "Wrong Channel"); + val = data[offset]; + offset++; + ASSERT_X(val == this->tag, "Wrong TAG"); + val = (data[offset]<<8) + data[offset+1]; + offset += 2; + ASSERT_X(val == sequence_idx, "Wrong sequence_idx"); + + //fetch + response_len = (data[offset++] << 8); + response_len |= data[offset++]; + ASSERT_X(out_len >= response_len, "Out Buffer too short"); + if (data_len < (7 + response_len)) { + return 0; + } + block_size = (response_len > (this->packet_size - 7) ? this->packet_size - 7 : response_len); + memcpy(out + offset_out, data + offset, block_size); + offset += block_size; + offset_out += block_size; + while (offset_out != response_len) { + sequence_idx++; + if (offset == data_len) { + return 0; + } + val = (data[offset]<<8) + data[offset+1]; + offset += 2; + ASSERT_X(val == this->channel, "Wrong Channel"); + val = data[offset]; + offset++; + ASSERT_X(val == this->tag, "Wrong TAG"); + val = (data[offset]<<8) + data[offset+1]; + offset += 2; + ASSERT_X(val == sequence_idx, "Wrong sequence_idx"); + + block_size = ((response_len - offset_out) > this->packet_size - 5 ? this->packet_size - 5 : response_len - offset_out); + if (block_size > (data_len - offset)) { + return 0; + } + memcpy(out + offset_out, data + offset, block_size); + offset += block_size; + offset_out += block_size; + } + return offset_out; + } + + + } +} + +#endif //#if defined(HAVE_HIDAPI) diff --git a/src/device/device_io_hid.hpp b/src/device/device_io_hid.hpp new file mode 100644 index 00000000000..560208c77d7 --- /dev/null +++ b/src/device/device_io_hid.hpp @@ -0,0 +1,112 @@ +// Copyright (c) 2017-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +#if defined(HAVE_HIDAPI) + +#include +#include "device_io.hpp" + +#pragma once + +namespace hw { + namespace io { + + + + /** HID class base. Commands are formated as follow: + * + * |----------------------------------------------------------| + * | 2 bytes | 1 byte | 2 bytes | 2 bytes | len bytes | + * |-----------|----------|-----------|----------|------------| + * | channel | tag | sequence | len | payload | + * |----------------------------------------------------------| + */ + + + struct hid_conn_params { + unsigned int vid; + unsigned int pid; + unsigned int interface_number; + unsigned int usage_page; + bool interface_OR_page ; + }; + + + class device_io_hid: device_io { + + + private: + + + unsigned short channel; + unsigned char tag; + unsigned int packet_size; + unsigned int timeout; + + unsigned int usb_vid; + unsigned int usb_pid; + hid_device *usb_device; + + void io_hid_log(int read, unsigned char* buf, int buf_len); + void io_hid_init(); + void io_hid_exit() ; + void io_hid_open(int vid, int pid, int mode); + void io_hid_close (void); + + unsigned int wrapCommand(const unsigned char *command, size_t command_len, unsigned char *out, size_t out_len); + unsigned int unwrapReponse(const unsigned char *data, size_t data_len, unsigned char *out, size_t out_len); + + + public: + bool hid_verbose = false; + + const unsigned int OR_SELECT = 1; + const unsigned int AND_SELECT = 2; + + const unsigned char DEFAULT_CHANNEL = 0x0001; + const unsigned char DEFAULT_TAG = 0x01; + const unsigned int DEFAULT_PACKET_SIZE = 64; + const unsigned int DEFAULT_TIMEOUT = 120000; + + device_io_hid(unsigned short channel, unsigned char tag, unsigned int packet_zize, unsigned int timeout); + device_io_hid(); + ~device_io_hid() {}; + + void init(); + void connect(void *params); + void connect(unsigned int vid, unsigned int pid, unsigned int interface_number, unsigned int usage_page, bool interface_OR_page ); + bool connected() const; + int exchange(unsigned char *command, unsigned int cmd_len, unsigned char *response, unsigned int max_resp_len); + void disconnect(); + void release(); + }; + }; +}; + +#endif //#if defined(HAVE_HIDAPI) diff --git a/src/device/device_ledger.cpp b/src/device/device_ledger.cpp index 9b5ea0fd751..456eda739f1 100644 --- a/src/device/device_ledger.cpp +++ b/src/device/device_ledger.cpp @@ -185,41 +185,8 @@ namespace hw { #define INS_GET_RESPONSE 0xc0 - void device_ledger::logCMD() { - if (apdu_verbose) { - char strbuffer[1024]; - snprintf(strbuffer, sizeof(strbuffer), "%.02x %.02x %.02x %.02x %.02x ", - this->buffer_send[0], - this->buffer_send[1], - this->buffer_send[2], - this->buffer_send[3], - this->buffer_send[4] - ); - const size_t len = strlen(strbuffer); - buffer_to_str(strbuffer+len, sizeof(strbuffer)-len, (char*)(this->buffer_send+5), this->length_send-5); - MDEBUG( "CMD :" << strbuffer); - } - } - - void device_ledger::logRESP() { - if (apdu_verbose) { - char strbuffer[1024]; - snprintf(strbuffer, sizeof(strbuffer), "%.02x%.02x ", - this->buffer_recv[this->length_recv-2], - this->buffer_recv[this->length_recv-1] - ); - const size_t len = strlen(strbuffer); - buffer_to_str(strbuffer+len, sizeof(strbuffer)-len, (char*)(this->buffer_recv), this->length_recv-2); - MDEBUG( "RESP :" << strbuffer); - - } - } - - /* -------------------------------------------------------------- */ - device_ledger::device_ledger() { + device_ledger::device_ledger(): hw_device(0x0101, 0x05, 64, 10000) { this->id = device_id++; - this->hCard = 0; - this->hContext = 0; this->reset_buffer(); this->mode = NONE; this->has_view_key = false; @@ -272,10 +239,39 @@ namespace hw { MDEBUG( "Device "<name << " UNLOCKed"); } + /* ======================================================================= */ - /* MISC */ + /* IO */ /* ======================================================================= */ - int device_ledger::set_command_header(BYTE ins, BYTE p1, BYTE p2) { + + void device_ledger::logCMD() { + if (apdu_verbose) { + char strbuffer[1024]; + snprintf(strbuffer, sizeof(strbuffer), "%.02x %.02x %.02x %.02x %.02x ", + this->buffer_send[0], + this->buffer_send[1], + this->buffer_send[2], + this->buffer_send[3], + this->buffer_send[4] + ); + const size_t len = strlen(strbuffer); + buffer_to_str(strbuffer+len, sizeof(strbuffer)-len, (char*)(this->buffer_send+5), this->length_send-5); + MDEBUG( "CMD : " << strbuffer); + } + } + + void device_ledger::logRESP() { + if (apdu_verbose) { + char strbuffer[1024]; + snprintf(strbuffer, sizeof(strbuffer), "%.04x ", this->sw); + const size_t len = strlen(strbuffer); + buffer_to_str(strbuffer+len, sizeof(strbuffer)-len, (char*)(this->buffer_recv), this->length_recv); + MDEBUG( "RESP : " << strbuffer); + + } + } + + int device_ledger::set_command_header(unsigned char ins, unsigned char p1, unsigned char p2) { reset_buffer(); int offset = 0; this->buffer_send[0] = 0x00; @@ -286,7 +282,7 @@ namespace hw { return 5; } - int device_ledger::set_command_header_noopt(BYTE ins, BYTE p1, BYTE p2) { + int device_ledger::set_command_header_noopt(unsigned char ins, unsigned char p1, unsigned char p2) { int offset = set_command_header(ins, p1, p2); //options this->buffer_send[offset++] = 0; @@ -294,7 +290,7 @@ namespace hw { return offset; } - void device_ledger::send_simple(BYTE ins, BYTE p1) { + void device_ledger::send_simple(unsigned char ins, unsigned char p1) { this->length_send = set_command_header_noopt(ins, p1); this->exchange(); } @@ -305,23 +301,17 @@ namespace hw { } unsigned int device_ledger::exchange(unsigned int ok, unsigned int mask) { - LONG rv; - unsigned int sw; - - ASSERT_T0(this->length_send <= BUFFER_SEND_SIZE); logCMD(); - this->length_recv = BUFFER_RECV_SIZE; - rv = SCardTransmit(this->hCard, - SCARD_PCI_T0, this->buffer_send, this->length_send, - NULL, this->buffer_recv, &this->length_recv); - ASSERT_RV(rv); - ASSERT_T0(this->length_recv >= 2); - ASSERT_T0(this->length_recv <= BUFFER_RECV_SIZE); - logRESP(); - sw = (this->buffer_recv[this->length_recv-2]<<8) | this->buffer_recv[this->length_recv-1]; - ASSERT_SW(sw,ok,msk); - return sw; + this->length_recv = hw_device.exchange(this->buffer_send, this->length_send, this->buffer_recv, BUFFER_SEND_SIZE); + ASSERT_X(this->length_recv>=2, "Communication error, less than tow bytes received"); + + this->length_recv -= 2; + this->sw = (this->buffer_recv[length_recv]<<8) | this->buffer_recv[length_recv+1]; + ASSERT_SW(this->sw,ok,msk); + + logRESP(); + return this->sw; } void device_ledger::reset_buffer() { @@ -341,101 +331,25 @@ namespace hw { } const std::string device_ledger::get_name() const { - if (this->full_name.empty() || (this->hCard == 0)) { + if (this->full_name.empty() || !this->connected()) { return std::string("name).append(">"); } - return this->full_name; + return this->name; } bool device_ledger::init(void) { #ifdef DEBUG_HWDEVICE this->controle_device = &hw::get_device("default"); #endif - LONG rv; this->release(); - rv = SCardEstablishContext(SCARD_SCOPE_SYSTEM,0,0, &this->hContext); - ASSERT_RV(rv); - MDEBUG( "Device "<id <<" SCardContext created: hContext="<hContext); - this->hCard = 0; - return true; - } - - bool device_ledger::release() { - this->disconnect(); - if (this->hContext) { - SCardReleaseContext(this->hContext); - MDEBUG( "Device "<id <<" SCardContext released: hContext="<hContext); - this->hContext = 0; - this->full_name.clear(); - } + hw_device.init(); + MDEBUG( "Device "<id <<" HIDUSB inited"); return true; } bool device_ledger::connect(void) { - BYTE pbAtr[MAX_ATR_SIZE]; - LPSTR mszReaders; - DWORD dwReaders; - LONG rv; - DWORD dwState, dwProtocol, dwAtrLen, dwReaderLen; - this->disconnect(); -#ifdef SCARD_AUTOALLOCATE - dwReaders = SCARD_AUTOALLOCATE; - rv = SCardListReaders(this->hContext, NULL, (LPSTR)&mszReaders, &dwReaders); -#else - dwReaders = 0; - rv = SCardListReaders(this->hContext, NULL, NULL, &dwReaders); - if (rv != SCARD_S_SUCCESS) - return false; - mszReaders = (LPSTR)calloc(dwReaders, sizeof(char)); - rv = SCardListReaders(this->hContext, NULL, mszReaders, &dwReaders); -#endif - if (rv == SCARD_S_SUCCESS) { - char* p; - const char* prefix = this->name.c_str(); - - p = mszReaders; - MDEBUG( "Looking for " << std::string(prefix)); - while (*p) { - MDEBUG( "Device Found: " << std::string(p)); - if ((strncmp(prefix, p, strlen(prefix))==0)) { - MDEBUG( "Device Match: " << std::string(p)); - if ((rv = SCardConnect(this->hContext, - p, SCARD_SHARE_EXCLUSIVE, SCARD_PROTOCOL_T0, - &this->hCard, &dwProtocol))!=SCARD_S_SUCCESS) { - break; - } - MDEBUG( "Device "<id <<" Connected: hCard="<hCard); - dwAtrLen = sizeof(pbAtr); - if ((rv = SCardStatus(this->hCard, NULL, &dwReaderLen, &dwState, &dwProtocol, pbAtr, &dwAtrLen))!=SCARD_S_SUCCESS) { - break; - } - MDEBUG( "Device "<id <<" Status OK"); - rv = SCARD_S_SUCCESS ; - this->full_name = std::string(p); - break; - } - p += strlen(p) +1; - } - } - - if (rv == SCARD_S_SUCCESS && mszReaders) { - #ifdef SCARD_AUTOALLOCATE - SCardFreeMemory(this->hContext, mszReaders); - #else - free(mszReaders); - #endif - mszReaders = NULL; - } - if (rv != SCARD_S_SUCCESS) { - if ( hCard) { - SCardDisconnect(this->hCard, SCARD_UNPOWER_CARD); - MDEBUG( "Device "<id <<" disconnected: hCard="<hCard); - this->hCard = 0; - } - } - ASSERT_RV(rv); - + hw_device.connect(0x2c97,0x0001, 0, 0xffa0, hw_device.OR_SELECT); this->reset(); #ifdef DEBUG_HWDEVICE cryptonote::account_public_address pubkey; @@ -445,15 +359,21 @@ namespace hw { crypto::secret_key skey; this->get_secret_keys(vkey,skey); - return rv==SCARD_S_SUCCESS; + return true; + } + + bool device_ledger::connected(void) const { + return hw_device.connected(); } bool device_ledger::disconnect() { - if (this->hCard) { - SCardDisconnect(this->hCard, SCARD_UNPOWER_CARD); - MDEBUG( "Device "<id <<" disconnected: hCard="<hCard); - this->hCard = 0; - } + hw_device.disconnect(); + return true; + } + + bool device_ledger::release() { + this->disconnect(); + hw_device.release(); return true; } diff --git a/src/device/device_ledger.hpp b/src/device/device_ledger.hpp index 4a3625b2b89..87b65996d6d 100644 --- a/src/device/device_ledger.hpp +++ b/src/device/device_ledger.hpp @@ -33,13 +33,7 @@ #include #include #include "device.hpp" -#ifdef WIN32 -#include -#define MAX_ATR_SIZE 33 -#else -#include -#include -#endif +#include "device_io_hid.hpp" #include #include @@ -89,22 +83,23 @@ namespace hw { mutable boost::recursive_mutex device_locker; mutable boost::mutex command_locker; - //PCSC management - std::string full_name; - SCARDCONTEXT hContext; - SCARDHANDLE hCard; - DWORD length_send; - BYTE buffer_send[BUFFER_SEND_SIZE]; - DWORD length_recv; - BYTE buffer_recv[BUFFER_RECV_SIZE]; - unsigned int id; + //IO + hw::io::device_io_hid hw_device; + std::string full_name; + unsigned int length_send; + unsigned char buffer_send[BUFFER_SEND_SIZE]; + unsigned int length_recv; + unsigned char buffer_recv[BUFFER_RECV_SIZE]; + unsigned int sw; + unsigned int id; void logCMD(void); void logRESP(void); - unsigned int exchange(unsigned int ok=0x9000, unsigned int mask=0xFFFF); + unsigned int exchange(unsigned int ok=0x9000, unsigned int mask=0xFFFF); void reset_buffer(void); - int set_command_header(BYTE ins, BYTE p1 = 0x00, BYTE p2 = 0x00); - int set_command_header_noopt(BYTE ins, BYTE p1 = 0x00, BYTE p2 = 0x00); - void send_simple(BYTE ins, BYTE p1 = 0x00); + int set_command_header(unsigned char ins, unsigned char p1 = 0x00, unsigned char p2 = 0x00); + int set_command_header_noopt(unsigned char ins, unsigned char p1 = 0x00, unsigned char p2 = 0x00); + void send_simple(unsigned char ins, unsigned char p1 = 0x00); + // hw running mode device_mode mode; @@ -127,7 +122,7 @@ namespace hw { device_ledger(const device_ledger &device) = delete ; device_ledger& operator=(const device_ledger &device) = delete; - explicit operator bool() const override {return this->hContext != 0;} + explicit operator bool() const override {return this->connected(); } bool reset(void); @@ -141,6 +136,7 @@ namespace hw { bool release() override; bool connect(void) override; bool disconnect() override; + bool connected(void) const; bool set_mode(device_mode mode) override; diff --git a/src/device/log.cpp b/src/device/log.cpp index 1707524fb76..c9d3b551b0e 100644 --- a/src/device/log.cpp +++ b/src/device/log.cpp @@ -32,29 +32,34 @@ namespace hw { - #ifdef WITH_DEVICE_LEDGER - namespace ledger { - - #undef MONERO_DEFAULT_LOG_CATEGORY - #define MONERO_DEFAULT_LOG_CATEGORY "device.ledger" + #undef MONERO_DEFAULT_LOG_CATEGORY + #define MONERO_DEFAULT_LOG_CATEGORY "device" - void buffer_to_str(char *to_buff, size_t to_len, const char *buff, size_t len) { - CHECK_AND_ASSERT_THROW_MES(to_len > (len*2), "destination buffer too short. At least" << (len*2+1) << " bytes required"); - for (size_t i=0; i (len*2), "destination buffer too short. At least" << (len*2+1) << " bytes required"); + for (size_t i=0; i Date: Mon, 24 Sep 2018 20:43:06 +0000 Subject: [PATCH 0460/1404] blockchain: add stagenet v8 and v9, two weeks before mainnet --- src/cryptonote_core/blockchain.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 0dc3f3bb4ad..6f1d26c5a1f 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -158,6 +158,8 @@ static const struct { { 5, 35000, 0, 1521360000 }, { 6, 36000, 0, 1521480000 }, { 7, 37000, 0, 1521600000 }, + { 8, 176456, 0, 1537821770 }, + { 9, 177176, 0, 1537821771 }, }; //------------------------------------------------------------------ From 936e22a9b5bfca9d2073791a5c272901414bc45c Mon Sep 17 00:00:00 2001 From: MoroccanMalinois Date: Tue, 25 Sep 2018 00:14:12 +0200 Subject: [PATCH 0461/1404] Dockerfile: use single build dir --- Dockerfile | 3 ++- utils/build_scripts/android32.Dockerfile | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index c518b5d0308..40ba81d9c5b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -112,6 +112,7 @@ RUN set -ex \ WORKDIR /src COPY . . +ENV USE_SINGLE_BUILDDIR=1 ARG NPROC RUN set -ex && \ rm -rf build && \ @@ -128,7 +129,7 @@ RUN set -ex && \ apt-get --no-install-recommends --yes install ca-certificates && \ apt-get clean && \ rm -rf /var/lib/apt -COPY --from=builder /src/build/Linux/master/release/* /usr/local/bin/ +COPY --from=builder /src/build/release/bin /usr/local/bin/ # Contains the blockchain VOLUME /root/.bitmonero diff --git a/utils/build_scripts/android32.Dockerfile b/utils/build_scripts/android32.Dockerfile index 2738d8d5708..8647341cb7c 100644 --- a/utils/build_scripts/android32.Dockerfile +++ b/utils/build_scripts/android32.Dockerfile @@ -138,4 +138,5 @@ RUN cd /src \ && CMAKE_INCLUDE_PATH="${PREFIX}/include" \ CMAKE_LIBRARY_PATH="${PREFIX}/lib" \ ANDROID_STANDALONE_TOOLCHAIN_PATH=${TOOLCHAIN_DIR} \ + USE_SINGLE_BUILDDIR=1 \ PATH=${HOST_PATH} make release-static-android -j${NPROC} From 357441a2fa8ad410a51d37a55f0ade89086489d4 Mon Sep 17 00:00:00 2001 From: Riccardo Spagni Date: Tue, 25 Sep 2018 10:02:05 +0200 Subject: [PATCH 0462/1404] add checkpoints for testnet and mainnet --- src/checkpoints/checkpoints.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/checkpoints/checkpoints.cpp b/src/checkpoints/checkpoints.cpp index ef1ee171d18..6251fcc91a3 100644 --- a/src/checkpoints/checkpoints.cpp +++ b/src/checkpoints/checkpoints.cpp @@ -165,6 +165,7 @@ namespace cryptonote { ADD_CHECKPOINT(0, "48ca7cd3c8de5b6a4d53d2861fbdaedca141553559f9be9520068053cda8430b"); ADD_CHECKPOINT(1000000, "46b690b710a07ea051bc4a6b6842ac37be691089c0f7758cfeec4d5fc0b4a258"); + ADD_CHECKPOINT(1058600, "12904f6b4d9e60fd875674e07147d2c83d6716253f046af7b894c3e81da7e1bd"); return true; } if (nettype == STAGENET) @@ -208,7 +209,7 @@ namespace cryptonote ADD_CHECKPOINT(1450000, "ac94e8860093bc7c83e4e91215cba1d663421ecf4067a0ae609c3a8b52bcfac2"); ADD_CHECKPOINT(1530000, "01759bce497ec38e63c78b1038892169203bb78f87e488172f6b854fcd63ba7e"); ADD_CHECKPOINT(1579000, "7d0d7a2346373afd41ed1e744a939fc5d474a7dbaa257be5c6fff4009e789241"); - + ADD_CHECKPOINT(1668900, "ac2dcaf3d2f58ffcf8391639f0f1ebafcb8eac43c49479c7c37f611868d07568"); return true; } From 89288863a4d3f0c87845a8651ab590e348fe5fe6 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 25 Sep 2018 09:41:41 +0000 Subject: [PATCH 0463/1404] README: mention ASAN usage alongside valgrind --- README.md | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2ded4eb5147..c1abe625ddc 100644 --- a/README.md +++ b/README.md @@ -666,9 +666,19 @@ Type `run` to run monerod ### Analysing memory corruption -We use the tool `valgrind` for this. +There are two tools available: -Run with `valgrind /path/to/monerod`. It will be slow. +* ASAN + +Configure Monero with the -D SANITIZE=ON cmake flag, eg: + + cd build/debug && cmake -D SANITIZE=ON -D CMAKE_BUILD_TYPE=Debug ../.. + +You can then run the monero tools normally. Performance will typically halve. + +* valgrind + +Install valgrind and run as `valgrind /path/to/monerod`. It will be very slow. ### LMDB From c39ad0cd7293d1f9fa56e5d646e82ee8908ece2f Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 25 Sep 2018 09:57:24 +0000 Subject: [PATCH 0464/1404] tx_pool: fix tx removal at startup keeping references --- src/cryptonote_core/tx_pool.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index a725eac6e0d..c31a2073be9 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -1349,6 +1349,7 @@ namespace cryptonote { MWARNING("Failed to parse tx from txpool, removing"); remove.push_back(txid); + return true; } if (!insert_key_images(tx, meta.kept_by_block)) { From 5c234cbdcbd7289539b2df4281ee20fbd12dbea0 Mon Sep 17 00:00:00 2001 From: TheCharlatan Date: Sat, 22 Sep 2018 20:07:33 +0200 Subject: [PATCH 0465/1404] Add hidapi to depends Clang needs to get its cctools path passed directly for the hid build to succeed. Make gperf a permanent external dependency. Remove pcsc from depends. --- .travis.yml | 8 +++--- contrib/depends/packages/eudev.mk | 25 ++++++++++++++++++ contrib/depends/packages/hidapi-darwin.mk | 23 +++++++++++++++++ contrib/depends/packages/hidapi-linux.mk | 28 ++++++++++++++++++++ contrib/depends/packages/hidapi.mk | 23 +++++++++++++++++ contrib/depends/packages/libusb.mk | 31 +++++++++++++++++++++++ contrib/depends/packages/packages.mk | 6 +++-- contrib/depends/packages/pcsc-lite.mk | 26 ------------------- 8 files changed, 138 insertions(+), 32 deletions(-) create mode 100644 contrib/depends/packages/eudev.mk create mode 100644 contrib/depends/packages/hidapi-darwin.mk create mode 100644 contrib/depends/packages/hidapi-linux.mk create mode 100644 contrib/depends/packages/hidapi.mk create mode 100644 contrib/depends/packages/libusb.mk delete mode 100644 contrib/depends/packages/pcsc-lite.mk diff --git a/.travis.yml b/.travis.yml index c87dceac9f8..9368c3ff29e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,15 +23,15 @@ env: - DOCKER_PACKAGES="build-essential libtool cmake autotools-dev automake pkg-config bsdmainutils curl git ca-certificates ccache" matrix: # ARM v7 - - HOST=arm-linux-gnueabihf PACKAGES="g++-arm-linux-gnueabihf" + - HOST=arm-linux-gnueabihf PACKAGES="gperf g++-arm-linux-gnueabihf" # ARM v8 - - HOST=aarch64-linux-gnu PACKAGES="g++-aarch64-linux-gnu" + - HOST=aarch64-linux-gnu PACKAGES="gperf g++-aarch64-linux-gnu" # i686 Linux - - HOST=i686-pc-linux-gnu PACKAGES="cmake g++-multilib bc python3-zmq" RUN_TESTS=true + - HOST=i686-pc-linux-gnu PACKAGES="gperf cmake g++-multilib bc python3-zmq" RUN_TESTS=true # Win64 - HOST=x86_64-w64-mingw32 PACKAGES="cmake python3 nsis g++-mingw-w64-x86-64 wine-binfmt wine64 bc" RUN_TESTS=true # x86_64 Linux - - HOST=x86_64-unknown-linux-gnu PACKAGES="cmake python3-zmq protobuf-compiler libdbus-1-dev libharfbuzz-dev libprotobuf-dev" RUN_TESTS=true + - HOST=x86_64-unknown-linux-gnu PACKAGES="gperf cmake python3-zmq protobuf-compiler libdbus-1-dev libharfbuzz-dev libprotobuf-dev" RUN_TESTS=true # Cross-Mac - HOST=x86_64-apple-darwin11 PACKAGES="cmake imagemagick libcap-dev librsvg2-bin libz-dev libbz2-dev libtiff-tools python-dev python3-setuptools-git" OSX_SDK=10.11 diff --git a/contrib/depends/packages/eudev.mk b/contrib/depends/packages/eudev.mk new file mode 100644 index 00000000000..08752909fe3 --- /dev/null +++ b/contrib/depends/packages/eudev.mk @@ -0,0 +1,25 @@ +package=eudev +$(package)_version=v3.2.6 +$(package)_download_path=https://github.com/gentoo/eudev/archive/ +$(package)_file_name=$($(package)_version).tar.gz +$(package)_sha256_hash=a96ecb8637667897b8bd4dee4c22c7c5f08b327be45186e912ce6bc768385852 + +define $(package)_set_vars + $(package)_config_opts=--disable-gudev --disable-introspection --disable-hwdb --disable-manpages +endef + +define $(package)_config_cmds + $($(package)_autoconf) +endef + +define $(package)_build_cmd + $(MAKE) +endef + +define $(package)_preprocess_cmds + cd $($(package)_build_subdir); autoreconf -f -i +endef + +define $(package)_stage_cmds + $(MAKE) DESTDIR=$($(package)_staging_dir) install +endef diff --git a/contrib/depends/packages/hidapi-darwin.mk b/contrib/depends/packages/hidapi-darwin.mk new file mode 100644 index 00000000000..1fbbb59e06c --- /dev/null +++ b/contrib/depends/packages/hidapi-darwin.mk @@ -0,0 +1,23 @@ +package=hidapi-darwin +$(package)_version=0.8.0-rc1 +$(package)_download_path=https://github.com/signal11/hidapi/archive +$(package)_file_name=hidapi-$($(package)_version).tar.gz +$(package)_sha256_hash=3c147200bf48a04c1e927cd81589c5ddceff61e6dac137a605f6ac9793f4af61 + +define $(package)_set_vars +$(package)_config_opts=--enable-static +$(package)_config_opts+=--prefix=$(host_prefix) +endef + +define $(package)_config_cmds + ./bootstrap &&\ + $($(package)_autoconf) $($(package)_config_opts) RANLIB="$(host_prefix)/native/bin/x86_64-apple-darwin11-ranlib" AR="$(host_prefix)/native/bin/x86_64-apple-darwin11-ar" +endef + +define $(package)_build_cmds + $(MAKE) +endef + +define $(package)_stage_cmds + $(MAKE) DESTDIR=$($(package)_staging_dir) install +endef diff --git a/contrib/depends/packages/hidapi-linux.mk b/contrib/depends/packages/hidapi-linux.mk new file mode 100644 index 00000000000..b2a49991550 --- /dev/null +++ b/contrib/depends/packages/hidapi-linux.mk @@ -0,0 +1,28 @@ +package=hidapi-linux +$(package)_version=0.8.0-rc1 +$(package)_download_path=https://github.com/signal11/hidapi/archive +$(package)_file_name=hidapi-$($(package)_version).tar.gz +$(package)_sha256_hash=3c147200bf48a04c1e927cd81589c5ddceff61e6dac137a605f6ac9793f4af61 +$(package)_dependencies=libusb eudev + +define $(package)_set_vars +$(package)_config_opts=--enable-static +$(package)_config_opts+=--prefix=$(host_prefix) +$(package)_config_opts+=libudev_LIBS="-L$(host_prefix)/lib -ludev" +$(package)_config_opts+=libudev_CFLAGS=-I$(host_prefix)/include +$(package)_config_opts+=libusb_LIBS="-L$(host_prefix)/lib -lusb-1.0" +$(package)_config_opts+=libusb_CFLAGS=-I$(host_prefix)/include/libusb-1.0 +endef + +define $(package)_config_cmds + ./bootstrap &&\ + $($(package)_autoconf) $($(package)_config_opts) +endef + +define $(package)_build_cmds + $(MAKE) +endef + +define $(package)_stage_cmds + $(MAKE) DESTDIR=$($(package)_staging_dir) install +endef diff --git a/contrib/depends/packages/hidapi.mk b/contrib/depends/packages/hidapi.mk new file mode 100644 index 00000000000..b6b228643cf --- /dev/null +++ b/contrib/depends/packages/hidapi.mk @@ -0,0 +1,23 @@ +package=hidapi +$(package)_version=0.8.0-rc1 +$(package)_download_path=https://github.com/signal11/hidapi/archive +$(package)_file_name=$(package)-$($(package)_version).tar.gz +$(package)_sha256_hash=3c147200bf48a04c1e927cd81589c5ddceff61e6dac137a605f6ac9793f4af61 + +define $(package)_set_vars +$(package)_config_opts=--enable-static +$(package)_config_opts+=--prefix=$(host_prefix) +endef + +define $(package)_config_cmds + ./bootstrap &&\ + $($(package)_autoconf) $($(package)_config_opts) +endef + +define $(package)_build_cmds + $(MAKE) +endef + +define $(package)_stage_cmds + $(MAKE) DESTDIR=$($(package)_staging_dir) install +endef diff --git a/contrib/depends/packages/libusb.mk b/contrib/depends/packages/libusb.mk new file mode 100644 index 00000000000..47f8b3cbc59 --- /dev/null +++ b/contrib/depends/packages/libusb.mk @@ -0,0 +1,31 @@ +package=libusb +$(package)_version=1.0.9 +$(package)_download_path=http://sourceforge.net/projects/libusb/files/libusb-1.0/libusb-1.0.9/ +$(package)_file_name=$(package)-$($(package)_version).tar.bz2 +$(package)_sha256_hash=e920eedc2d06b09606611c99ec7304413c6784cba6e33928e78243d323195f9b + +define $(package)_preprocess_cmds + autoreconf -i +endef + +define $(package)_set_vars + $(package)_config_opts=--disable-shared + $(package)_config_opts_linux=--with-pic +endef + +define $(package)_config_cmds + cp -f $(BASEDIR)/config.guess config.guess &&\ + cp -f $(BASEDIR)/config.sub config.sub &&\ + $($(package)_autoconf) +endef + +define $(package)_build_cmd + $(MAKE) +endef + +define $(package)_stage_cmds + $(MAKE) DESTDIR=$($(package)_staging_dir) install +endef + +define $(package)_postprocess_cmds cp -f lib/libusb-1.0.a lib/libusb.a +endef diff --git a/contrib/depends/packages/packages.mk b/contrib/depends/packages/packages.mk index 114f8e9c019..13ba3762855 100644 --- a/contrib/depends/packages/packages.mk +++ b/contrib/depends/packages/packages.mk @@ -4,16 +4,18 @@ native_packages := native_ccache wallet_packages=bdb darwin_native_packages = native_biplist native_ds_store native_mac_alias -darwin_packages += sodium-darwin +darwin_packages += sodium-darwin hidapi-darwin + +linux_packages = eudev libusb hidapi-linux ifeq ($(host_os),linux) -packages += pcsc-lite packages += unwind packages += sodium endif ifeq ($(host_os),mingw32) packages += icu4c packages += sodium +packages += hidapi endif ifneq ($(build_os),darwin) diff --git a/contrib/depends/packages/pcsc-lite.mk b/contrib/depends/packages/pcsc-lite.mk deleted file mode 100644 index 06be0e58589..00000000000 --- a/contrib/depends/packages/pcsc-lite.mk +++ /dev/null @@ -1,26 +0,0 @@ -package=pcsc-lite -$(package)_version=1.8.23 -$(package)_download_path=https://pcsclite.apdu.fr/files -$(package)_file_name=$(package)-$($(package)_version).tar.bz2 -$(package)_sha256_hash=5a27262586eff39cfd5c19aadc8891dd71c0818d3d629539bd631b958be689c9 - -define $(package)_set_vars - $(package)_build_opts=CC="$($(package)_cc)" - $(package)_config_env=AR="$($(package)_ar)" RANLIB="$($(package)_ranlib)" CC="$($(package)_cc)" - $(package)_config_opts=--prefix=$(host_prefix) - $(package)_config_opts_release=--disable-debug-mode --disable-libsystemd --disable-libudev --enable-static --disable-shared --disable-libusb - $(package)_build_opts=CFLAGS="$($(package)_cflags) $($(package)_cppflags) -fPIC" -endef - -define $(package)_config_cmds - ./bootstrap &&\ - $($(package)_autoconf) $($(package)_config_opts) -endef - -define $(package)_build_cmds - $(MAKE) $($(package)_build_opts) -endef - -define $(package)_stage_cmds - $(MAKE) DESTDIR=$($(package)_staging_dir) install -endef From 45683ee02c39f6e57f1f5e1065f4295292a0d8cf Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 2 Aug 2018 16:17:22 +0000 Subject: [PATCH 0466/1404] epee: fix invalid memory write reading an array entry Reported by Lilith Wyatt at Talos. Since this is not needed in normal operation, I just let this error out. --- contrib/epee/include/storages/portable_storage_from_bin.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/contrib/epee/include/storages/portable_storage_from_bin.h b/contrib/epee/include/storages/portable_storage_from_bin.h index 44a80cb2175..f9cc22d2707 100644 --- a/contrib/epee/include/storages/portable_storage_from_bin.h +++ b/contrib/epee/include/storages/portable_storage_from_bin.h @@ -59,6 +59,7 @@ namespace epee storage_entry load_storage_entry(); void read(section& sec); void read(std::string& str); + void read(array_entry &ae); private: struct recursuion_limitation_guard { @@ -114,6 +115,7 @@ namespace epee void throwable_buffer_reader::read(t_pod_type& pod_val) { RECURSION_LIMITATION(); + static_assert(std::is_pod::value, "POD type expected"); read(&pod_val, sizeof(pod_val)); } @@ -277,5 +279,11 @@ namespace epee m_ptr+=len; m_count -= len; } + inline + void throwable_buffer_reader::read(array_entry &ae) + { + RECURSION_LIMITATION(); + CHECK_AND_ASSERT_THROW_MES(false, "Reading array entry is not supported"); + } } } From bf9a0f4c656ab989c388017387267e8df77949a1 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 5 Aug 2018 08:42:52 +0000 Subject: [PATCH 0467/1404] epee: fix stack overflow on crafted input --- .../include/storages/portable_storage_from_json.h | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/contrib/epee/include/storages/portable_storage_from_json.h b/contrib/epee/include/storages/portable_storage_from_json.h index 727f3655251..5b2eafa9a1b 100644 --- a/contrib/epee/include/storages/portable_storage_from_json.h +++ b/contrib/epee/include/storages/portable_storage_from_json.h @@ -30,6 +30,8 @@ #include "parserse_base_utils.h" #include "file_io_utils.h" +#define EPEE_JSON_RECURSION_LIMIT_INTERNAL 100 + namespace epee { using namespace misc_utils::parse; @@ -44,8 +46,9 @@ namespace epee ASSERT_MES_AND_THROW("json parse error"); }*/ template - inline void run_handler(typename t_storage::hsection current_section, std::string::const_iterator& sec_buf_begin, std::string::const_iterator buf_end, t_storage& stg) + inline void run_handler(typename t_storage::hsection current_section, std::string::const_iterator& sec_buf_begin, std::string::const_iterator buf_end, t_storage& stg, unsigned int recursion) { + CHECK_AND_ASSERT_THROW_MES(recursion < EPEE_JSON_RECURSION_LIMIT_INTERNAL, "Wrong JSON data: recursion limitation (" << EPEE_JSON_RECURSION_LIMIT_INTERNAL << ") exceeded"); std::string::const_iterator sub_element_start; std::string name; @@ -157,7 +160,7 @@ namespace epee //sub section here typename t_storage::hsection new_sec = stg.open_section(name, current_section, true); CHECK_AND_ASSERT_THROW_MES(new_sec, "Failed to insert new section in json: " << std::string(it, buf_end)); - run_handler(new_sec, it, buf_end, stg); + run_handler(new_sec, it, buf_end, stg, recursion + 1); state = match_state_wonder_after_value; }else if(*it == '[') {//array of something @@ -186,7 +189,7 @@ namespace epee typename t_storage::hsection new_sec = nullptr; h_array = stg.insert_first_section(name, new_sec, current_section); CHECK_AND_ASSERT_THROW_MES(h_array&&new_sec, "failed to create new section"); - run_handler(new_sec, it, buf_end, stg); + run_handler(new_sec, it, buf_end, stg, recursion + 1); state = match_state_array_after_value; array_md = array_mode_sections; }else if(*it == '"') @@ -260,7 +263,7 @@ namespace epee typename t_storage::hsection new_sec = NULL; bool res = stg.insert_next_section(h_array, new_sec); CHECK_AND_ASSERT_THROW_MES(res&&new_sec, "failed to insert next section"); - run_handler(new_sec, it, buf_end, stg); + run_handler(new_sec, it, buf_end, stg, recursion + 1); state = match_state_array_after_value; }else CHECK_ISSPACE(); break; @@ -362,7 +365,7 @@ namespace epee std::string::const_iterator sec_buf_begin = buff_json.begin(); try { - run_handler(nullptr, sec_buf_begin, buff_json.end(), stg); + run_handler(nullptr, sec_buf_begin, buff_json.end(), stg, 0); return true; } catch(const std::exception& ex) From e350cc5ad557eba67790551f96bbbcfc97173480 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 19 Sep 2018 09:08:02 +0000 Subject: [PATCH 0468/1404] wallet2: fix duplicate output making it to the RPC --- src/wallet/wallet2.cpp | 41 +++++++++++++++++++++++++++++++++-------- 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 00b40fef8b4..75178845a88 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -1344,6 +1344,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote size_t pk_index = 0; std::vector tx_scan_info(tx.vout.size()); std::deque output_found(tx.vout.size(), false); + uint64_t total_received_1 = 0; while (!tx.vout.empty()) { // if tx.vout is not empty, we loop through all tx pubkeys @@ -1518,6 +1519,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote + ", m_transfers.size() is " + boost::lexical_cast(m_transfers.size())); if (kit == m_pub_keys.end()) { + uint64_t amount = tx.vout[o].amount ? tx.vout[o].amount : tx_scan_info[o].amount; if (!pool) { m_transfers.push_back(boost::value_initialized()); @@ -1530,14 +1532,13 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote td.m_key_image = tx_scan_info[o].ki; td.m_key_image_known = !m_watch_only && !m_multisig; td.m_key_image_partial = m_multisig; - td.m_amount = tx.vout[o].amount; + td.m_amount = amount; td.m_pk_index = pk_index - 1; td.m_subaddr_index = tx_scan_info[o].received->index; expand_subaddresses(tx_scan_info[o].received->index); - if (td.m_amount == 0) + if (tx.vout[o].amount == 0) { td.m_mask = tx_scan_info[o].mask; - td.m_amount = tx_scan_info[o].amount; td.m_rct = true; } else if (miner_tx && tx.version == 2) @@ -1565,6 +1566,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote if (0 != m_callback) m_callback->on_money_received(height, txid, tx, td.m_amount, td.m_subaddr_index); } + total_received_1 += amount; } else if (m_transfers[kit->second].m_spent || m_transfers[kit->second].amount() >= tx_scan_info[o].amount) { @@ -1572,6 +1574,9 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote << " from received " << print_money(tx_scan_info[o].amount) << " output already exists with " << (m_transfers[kit->second].m_spent ? "spent" : "unspent") << " " << print_money(m_transfers[kit->second].amount()) << " in tx " << m_transfers[kit->second].m_txid << ", received output ignored"); + THROW_WALLET_EXCEPTION_IF(tx_money_got_in_outs[tx_scan_info[o].received->index] < tx_scan_info[o].amount, + error::wallet_internal_error, "Unexpected values of new and old outputs"); + tx_money_got_in_outs[tx_scan_info[o].received->index] -= tx_scan_info[o].amount; } else { @@ -1579,8 +1584,14 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote << " from received " << print_money(tx_scan_info[o].amount) << " output already exists with " << print_money(m_transfers[kit->second].amount()) << ", replacing with new output"); // The new larger output replaced a previous smaller one - tx_money_got_in_outs[tx_scan_info[o].received->index] -= tx_scan_info[o].amount; - + THROW_WALLET_EXCEPTION_IF(tx_money_got_in_outs[tx_scan_info[o].received->index] < tx_scan_info[o].amount, + error::wallet_internal_error, "Unexpected values of new and old outputs"); + THROW_WALLET_EXCEPTION_IF(m_transfers[kit->second].amount() > tx_scan_info[o].amount, + error::wallet_internal_error, "Unexpected values of new and old outputs"); + tx_money_got_in_outs[tx_scan_info[o].received->index] -= m_transfers[kit->second].amount(); + + uint64_t amount = tx.vout[o].amount ? tx.vout[o].amount : tx_scan_info[o].amount; + uint64_t extra_amount = amount - m_transfers[kit->second].amount(); if (!pool) { transfer_details &td = m_transfers[kit->second]; @@ -1589,14 +1600,13 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote td.m_global_output_index = o_indices[o]; td.m_tx = (const cryptonote::transaction_prefix&)tx; td.m_txid = txid; - td.m_amount = tx.vout[o].amount; + td.m_amount = amount; td.m_pk_index = pk_index - 1; td.m_subaddr_index = tx_scan_info[o].received->index; expand_subaddresses(tx_scan_info[o].received->index); - if (td.m_amount == 0) + if (tx.vout[o].amount == 0) { td.m_mask = tx_scan_info[o].mask; - td.m_amount = tx_scan_info[o].amount; td.m_rct = true; } else if (miner_tx && tx.version == 2) @@ -1623,6 +1633,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote if (0 != m_callback) m_callback->on_money_received(height, txid, tx, td.m_amount, td.m_subaddr_index); } + total_received_1 += extra_amount; } } } @@ -1744,6 +1755,20 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote } } + uint64_t total_received_2 = 0; + for (const auto& i : tx_money_got_in_outs) + total_received_2 += i.second; + if (total_received_1 != total_received_2) + { + const el::Level level = el::Level::Warning; + MCLOG_RED(level, "global", "**********************************************************************"); + MCLOG_RED(level, "global", "Consistency failure in amounts received"); + MCLOG_RED(level, "global", "Check transaction " << txid); + MCLOG_RED(level, "global", "**********************************************************************"); + exit(1); + return; + } + for (const auto& i : tx_money_got_in_outs) { payment_details payment; From 174f31bf7d53b032523f89cfe3ac78e9df2f784e Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 25 Sep 2018 12:33:39 +0000 Subject: [PATCH 0469/1404] simplewallet: don't complain about payment id on pool mined blocks Those use the extra nonce without a payment id --- src/simplewallet/simplewallet.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 302d2a99915..06655ed69d1 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -4157,10 +4157,11 @@ void simple_wallet::on_money_received(uint64_t height, const crypto::hash &txid, if (find_tx_extra_field_by_type(tx_extra_fields, extra_nonce)) { crypto::hash8 payment_id8 = crypto::null_hash8; + crypto::hash payment_id = crypto::null_hash; if (get_encrypted_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id8)) message_writer() << tr("NOTE: this transaction uses an encrypted payment ID: consider using subaddresses instead"); - else + else if (get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id)) message_writer(console_color_red, false) << tr("WARNING: this transaction uses an unencrypted payment ID: consider using subaddresses instead"); } From cf470bf3d6d94d925a95e6bca66d84169e3ebaca Mon Sep 17 00:00:00 2001 From: Riccardo Spagni Date: Tue, 25 Sep 2018 15:07:28 +0200 Subject: [PATCH 0470/1404] switch from master to rc --- src/version.cpp.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/version.cpp.in b/src/version.cpp.in index ff24058111c..257e7b5d1cf 100644 --- a/src/version.cpp.in +++ b/src/version.cpp.in @@ -1,5 +1,5 @@ #define DEF_MONERO_VERSION_TAG "@VERSIONTAG@" -#define DEF_MONERO_VERSION "0.13.0.0-master" +#define DEF_MONERO_VERSION "0.13.0.0-rc" #define DEF_MONERO_RELEASE_NAME "Beryllium Bullet" #define DEF_MONERO_VERSION_FULL DEF_MONERO_VERSION "-" DEF_MONERO_VERSION_TAG From 6f5360b3c6497da785da367a2b54f6760e3cd8bd Mon Sep 17 00:00:00 2001 From: Riccardo Spagni Date: Tue, 25 Sep 2018 15:48:56 +0200 Subject: [PATCH 0471/1404] bump version to 0.13.0.1 --- src/version.cpp.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/version.cpp.in b/src/version.cpp.in index 257e7b5d1cf..55075a06405 100644 --- a/src/version.cpp.in +++ b/src/version.cpp.in @@ -1,5 +1,5 @@ #define DEF_MONERO_VERSION_TAG "@VERSIONTAG@" -#define DEF_MONERO_VERSION "0.13.0.0-rc" +#define DEF_MONERO_VERSION "0.13.0.1-rc" #define DEF_MONERO_RELEASE_NAME "Beryllium Bullet" #define DEF_MONERO_VERSION_FULL DEF_MONERO_VERSION "-" DEF_MONERO_VERSION_TAG From fe1256471a2de22e0ccaf27e7c62d6861bec197f Mon Sep 17 00:00:00 2001 From: Howard Chu Date: Tue, 25 Sep 2018 20:59:37 +0100 Subject: [PATCH 0472/1404] Fixup RENAME_DB() macro Make sure target DB's record is on a writable page --- src/blockchain_db/lmdb/db_lmdb.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp index b0f3ca5f009..d8f7df5f73d 100644 --- a/src/blockchain_db/lmdb/db_lmdb.cpp +++ b/src/blockchain_db/lmdb/db_lmdb.cpp @@ -3490,7 +3490,17 @@ void BlockchainLMDB::fixup() BlockchainDB::fixup(); } -#define RENAME_DB(name) \ +#define RENAME_DB(name) do { \ + char n2[] = name; \ + MDB_dbi tdbi; \ + n2[sizeof(n2)-2]--; \ + /* play some games to put (name) on a writable page */ \ + result = mdb_dbi_open(txn, n2, MDB_CREATE, &tdbi); \ + if (result) \ + throw0(DB_ERROR(lmdb_error("Failed to create " + std::string(n2) + ": ", result).c_str())); \ + result = mdb_drop(txn, tdbi, 1); \ + if (result) \ + throw0(DB_ERROR(lmdb_error("Failed to delete " + std::string(n2) + ": ", result).c_str())); \ k.mv_data = (void *)name; \ k.mv_size = sizeof(name)-1; \ result = mdb_cursor_open(txn, 1, &c_cur); \ @@ -3500,7 +3510,7 @@ void BlockchainLMDB::fixup() if (result) \ throw0(DB_ERROR(lmdb_error("Failed to get DB record for " name ": ", result).c_str())); \ ptr = (char *)k.mv_data; \ - ptr[sizeof(name)-2]++ + ptr[sizeof(name)-2]++; } while(0) #define LOGIF(y) if (ELPP->vRegistry()->allowed(y, "global")) From 69da14e105c96140a0fbf4c40f2bf1672965910f Mon Sep 17 00:00:00 2001 From: Dusan Klinec Date: Tue, 25 Sep 2018 22:44:00 +0200 Subject: [PATCH 0473/1404] fixes make debug compilation on OSX --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 994d4769111..a7746371e23 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -769,7 +769,7 @@ else() endif() if(APPLE) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden -DGTEST_HAS_TR1_TUPLE=0") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=default -DGTEST_HAS_TR1_TUPLE=0") endif() set(DEBUG_FLAGS "-g3") From 6a78140863cbfc11dd4388901c913717f8ca4d17 Mon Sep 17 00:00:00 2001 From: TheCharlatan Date: Tue, 25 Sep 2018 19:54:41 +0000 Subject: [PATCH 0474/1404] Make depends use self built clang for darwin The configure script in hidapi and libsodium tried to find clang in /usr/bin, even though the correct prefix was passed in. This sets the correct CC flag. This was previously undetected, because clang and the sdk where installed in the global environment. This also fixes a subsequent error, where IOKIT and CoreFoundation are not found, again for the reason stated above. --- CMakeLists.txt | 12 ++++++++---- contrib/depends/packages/hidapi-darwin.mk | 2 +- contrib/depends/packages/packages.mk | 2 +- contrib/depends/packages/sodium-darwin.mk | 3 ++- 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 994d4769111..75d8ba9c32d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -883,10 +883,14 @@ list(APPEND EXTRA_LIBRARIES ${CMAKE_DL_LIBS}) if (HIDAPI_FOUND) if (APPLE) - find_library(COREFOUNDATION CoreFoundation) - find_library(IOKIT IOKit) - list(APPEND EXTRA_LIBRARIES ${IOKIT}) - list(APPEND EXTRA_LIBRARIES ${COREFOUNDATION}) + if(DEPENDS) + list(APPEND EXTRA_LIBRARIES "-framework Foundation -framework IOKit") + else() + find_library(COREFOUNDATION CoreFoundation) + find_library(IOKIT IOKit) + list(APPEND EXTRA_LIBRARIES ${IOKIT}) + list(APPEND EXTRA_LIBRARIES ${COREFOUNDATION}) + endif() endif() if (WIN32) list(APPEND EXTRA_LIBRARIES setupapi) diff --git a/contrib/depends/packages/hidapi-darwin.mk b/contrib/depends/packages/hidapi-darwin.mk index 1fbbb59e06c..014aba5787b 100644 --- a/contrib/depends/packages/hidapi-darwin.mk +++ b/contrib/depends/packages/hidapi-darwin.mk @@ -11,7 +11,7 @@ endef define $(package)_config_cmds ./bootstrap &&\ - $($(package)_autoconf) $($(package)_config_opts) RANLIB="$(host_prefix)/native/bin/x86_64-apple-darwin11-ranlib" AR="$(host_prefix)/native/bin/x86_64-apple-darwin11-ar" + $($(package)_autoconf) $($(package)_config_opts) RANLIB="$(host_prefix)/native/bin/x86_64-apple-darwin11-ranlib" AR="$(host_prefix)/native/bin/x86_64-apple-darwin11-ar" CC="$(host_prefix)/native/bin/$($(package)_cc)" endef define $(package)_build_cmds diff --git a/contrib/depends/packages/packages.mk b/contrib/depends/packages/packages.mk index 13ba3762855..9bfbff7d9d1 100644 --- a/contrib/depends/packages/packages.mk +++ b/contrib/depends/packages/packages.mk @@ -4,7 +4,7 @@ native_packages := native_ccache wallet_packages=bdb darwin_native_packages = native_biplist native_ds_store native_mac_alias -darwin_packages += sodium-darwin hidapi-darwin +darwin_packages = sodium-darwin hidapi-darwin linux_packages = eudev libusb hidapi-linux diff --git a/contrib/depends/packages/sodium-darwin.mk b/contrib/depends/packages/sodium-darwin.mk index 796bead1622..59c6d1ec67d 100644 --- a/contrib/depends/packages/sodium-darwin.mk +++ b/contrib/depends/packages/sodium-darwin.mk @@ -5,13 +5,14 @@ $(package)_file_name=libsodium-$($(package)_version).tar.gz $(package)_sha256_hash=fb6a9e879a2f674592e4328c5d9f79f082405ee4bb05cb6e679b90afe9e178f4 define $(package)_set_vars +$(package)_build_opts_darwin=OS=Darwin LIBTOOL="$($(package)_libtool)" $(package)_config_opts=--enable-static $(package)_config_opts+=--prefix=$(host_prefix) endef define $(package)_config_cmds ./autogen.sh &&\ - $($(package)_autoconf) $($(package)_config_opts) RANLIB="$(host_prefix)/native/bin/x86_64-apple-darwin11-ranlib" AR="$(host_prefix)/native/bin/x86_64-apple-darwin11-ar" + $($(package)_autoconf) $($(package)_config_opts) RANLIB="$(host_prefix)/native/bin/x86_64-apple-darwin11-ranlib" AR="$(host_prefix)/native/bin/x86_64-apple-darwin11-ar" CC="$(host_prefix)/native/bin/$($(package)_cc)" endef define $(package)_build_cmds From 17142ec9bbb99c8e6ddea51965f2edeeaea8e769 Mon Sep 17 00:00:00 2001 From: m2049r Date: Fri, 30 Mar 2018 13:01:41 +0200 Subject: [PATCH 0475/1404] malloc scratchpad for all supported android archs --- src/crypto/slow-hash.c | 43 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/src/crypto/slow-hash.c b/src/crypto/slow-hash.c index 80bf4e06f15..40cfb0461e5 100644 --- a/src/crypto/slow-hash.c +++ b/src/crypto/slow-hash.c @@ -1040,10 +1040,37 @@ STATIC INLINE void aes_pseudo_round_xor(const uint8_t *in, uint8_t *out, const u } } +#ifdef FORCE_USE_HEAP +STATIC INLINE void* aligned_malloc(size_t size, size_t align) +{ + void *result; +#ifdef _MSC_VER + result = _aligned_malloc(size, align); +#else + if (posix_memalign(&result, align, size)) result = NULL; +#endif + return result; +} + +STATIC INLINE void aligned_free(void *ptr) +{ +#ifdef _MSC_VER + _aligned_free(ptr); +#else + free(ptr); +#endif +} +#endif /* FORCE_USE_HEAP */ + void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int prehashed) { RDATA_ALIGN16 uint8_t expandedKey[240]; + +#ifndef FORCE_USE_HEAP RDATA_ALIGN16 uint8_t hp_state[MEMORY]; +#else + uint8_t *hp_state = (uint8_t *)aligned_malloc(MEMORY,16); +#endif uint8_t text[INIT_SIZE_BYTE]; RDATA_ALIGN16 uint64_t a[2]; @@ -1129,6 +1156,10 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int memcpy(state.init, text, INIT_SIZE_BYTE); hash_permutation(&state.hs); extra_hashes[state.hs.b[0] & 3](&state, 200, hash); + +#ifdef FORCE_USE_HEAP + aligned_free(hp_state); +#endif } #else /* aarch64 && crypto */ @@ -1270,8 +1301,7 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int #ifndef FORCE_USE_HEAP uint8_t long_state[MEMORY]; #else - uint8_t *long_state = NULL; - long_state = (uint8_t *)malloc(MEMORY); + uint8_t *long_state = (uint8_t *)malloc(MEMORY); #endif if (prehashed) { @@ -1449,7 +1479,12 @@ union cn_slow_hash_state { #pragma pack(pop) void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int prehashed) { +#ifndef FORCE_USE_HEAP uint8_t long_state[MEMORY]; +#else + uint8_t *long_state = (uint8_t *)malloc(MEMORY); +#endif + union cn_slow_hash_state state; uint8_t text[INIT_SIZE_BYTE]; uint8_t a[AES_BLOCK_SIZE]; @@ -1534,6 +1569,10 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int /*memcpy(hash, &state, 32);*/ extra_hashes[state.hs.b[0] & 3](&state, 200, hash); oaes_free((OAES_CTX **) &aes_ctx); + +#ifdef FORCE_USE_HEAP + free(long_state); +#endif } #endif From b2972927ea0bff9d72594ed23f97e302a0022440 Mon Sep 17 00:00:00 2001 From: Dusan Klinec Date: Tue, 25 Sep 2018 19:30:22 +0200 Subject: [PATCH 0476/1404] osx compilation fix: missing boost libs added --- contrib/epee/src/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/contrib/epee/src/CMakeLists.txt b/contrib/epee/src/CMakeLists.txt index 0b5e7ae6ca4..bc437deb903 100644 --- a/contrib/epee/src/CMakeLists.txt +++ b/contrib/epee/src/CMakeLists.txt @@ -54,7 +54,9 @@ endif() target_link_libraries(epee PUBLIC easylogging + ${Boost_CHRONO_LIBRARY} ${Boost_FILESYSTEM_LIBRARY} + ${Boost_THREAD_LIBRARY} PRIVATE ${OPENSSL_LIBRARIES} ${EXTRA_LIBRARIES}) From 8110bea34bc36827e31288f1e89d6c7efa0c6e5b Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 28 Sep 2018 15:22:20 +0000 Subject: [PATCH 0477/1404] dns_utils: refresh list of usable DNSSEC servers A few of them are now returning invalid replies. --- src/common/dns_utils.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/common/dns_utils.cpp b/src/common/dns_utils.cpp index 3f2bde620bf..f2b27098132 100644 --- a/src/common/dns_utils.cpp +++ b/src/common/dns_utils.cpp @@ -46,10 +46,11 @@ namespace bf = boost::filesystem; static const char *DEFAULT_DNS_PUBLIC_ADDR[] = { "194.150.168.168", // CCC (Germany) - "81.3.27.54", // Lightning Wire Labs (Germany) - "31.3.135.232", // OpenNIC (Switzerland) "80.67.169.40", // FDN (France) - "209.58.179.186", // Cyberghost (Singapore) + "89.233.43.71", // http://censurfridns.dk (Denmark) + "109.69.8.51", // punCAT (Spain) + "77.109.148.137", // Xiala.net (Switzerland) + "193.58.251.251", // SkyDNS (Russia) }; static boost::mutex instance_lock; From bef1750f650b0c7d62c8306dc1a74855a2a4d6a0 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 28 Sep 2018 15:27:46 +0000 Subject: [PATCH 0478/1404] unit_tests: fix longstanding DNS related unit test --- tests/unit_tests/address_from_url.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit_tests/address_from_url.cpp b/tests/unit_tests/address_from_url.cpp index f174738fd43..f6c0ad105a5 100644 --- a/tests/unit_tests/address_from_url.cpp +++ b/tests/unit_tests/address_from_url.cpp @@ -109,7 +109,7 @@ TEST(AddressFromURL, Failure) { bool dnssec_result = false; - std::vector addresses = tools::dns_utils::addresses_from_url("example.invalid", dnssec_result); + std::vector addresses = tools::dns_utils::addresses_from_url("example.veryinvalid", dnssec_result); // for a non-existing domain such as "example.invalid", the non-existence is proved with NSEC records ASSERT_TRUE(dnssec_result); From 83debef99e207fd5761141d2d2630f77dc1b00ed Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 24 Sep 2018 09:57:28 +0000 Subject: [PATCH 0479/1404] wallet_rpc_server: remove verbose field in incoming_transfers query The key image (which was only supplied if verbose was true) is readily available, not a secret key, and it was only modelled after the CLI command because it's a bit spammy for the CLI. --- src/wallet/wallet_rpc_server.cpp | 2 +- src/wallet/wallet_rpc_server_commands_defs.h | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index e6eb64d12b6..27631187c84 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -1554,7 +1554,7 @@ namespace tools rpc_transfers.global_index = td.m_global_output_index; rpc_transfers.tx_hash = epee::string_tools::pod_to_hex(td.m_txid); rpc_transfers.subaddr_index = {td.m_subaddr_index.major, td.m_subaddr_index.minor}; - rpc_transfers.key_image = req.verbose && td.m_key_image_known ? epee::string_tools::pod_to_hex(td.m_key_image) : ""; + rpc_transfers.key_image = td.m_key_image_known ? epee::string_tools::pod_to_hex(td.m_key_image) : ""; res.transfers.push_back(rpc_transfers); } } diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h index 2168e0f7124..81ea2292891 100644 --- a/src/wallet/wallet_rpc_server_commands_defs.h +++ b/src/wallet/wallet_rpc_server_commands_defs.h @@ -883,13 +883,11 @@ namespace wallet_rpc std::string transfer_type; uint32_t account_index; std::set subaddr_indices; - bool verbose; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(transfer_type) KV_SERIALIZE(account_index) KV_SERIALIZE(subaddr_indices) - KV_SERIALIZE(verbose) END_KV_SERIALIZE_MAP() }; From fa942ef610ef9fa32a3d6b8a8d6106e141f418c8 Mon Sep 17 00:00:00 2001 From: iDunk5400 Date: Sat, 29 Sep 2018 14:42:10 +0200 Subject: [PATCH 0480/1404] daemon: silence daemon update warnings on testnet --- src/cryptonote_core/blockchain.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 0e175cdbe7c..2c703223825 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -137,8 +137,8 @@ static const struct { { 6, 971400, 0, 1501709789 }, { 7, 1057027, 0, 1512211236 }, - { 8, 1057058, 0, 1515967497 }, - { 9, 1057778, 0, 1515967498 }, + { 8, 1057058, 0, 1533211200 }, + { 9, 1057778, 0, 1533297600 }, }; static const uint64_t testnet_hard_fork_version_1_till = 624633; From 2a44e13658aff63692094eb30fef9376b1ba0c10 Mon Sep 17 00:00:00 2001 From: Riccardo Spagni Date: Tue, 25 Sep 2018 15:54:01 +0200 Subject: [PATCH 0481/1404] Merge pull request #4441 6f5360b3 bump version to 0.13.0.1 (Riccardo Spagni) From ff0d7a767894a6c71ceb5d1e0d4cb740b407d8d1 Mon Sep 17 00:00:00 2001 From: Riccardo Spagni Date: Sat, 29 Sep 2018 21:51:50 +0200 Subject: [PATCH 0482/1404] Merge pull request #4406 7964d4f8 wallet2: handle corner case in picking fake outputs (moneromooo-monero) --- src/wallet/wallet2.cpp | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 75178845a88..33699cb79bf 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -6723,11 +6723,23 @@ void wallet2::get_outs(std::vector> } // while we still need more mixins + uint64_t num_usable_outs = num_outs; + bool allow_blackballed = false; while (num_found < requested_outputs_count) { // if we've gone through every possible output, we've gotten all we can - if (seen_indices.size() == num_outs) - break; + if (seen_indices.size() == num_usable_outs) + { + // there is a first pass which rejects blackballed outputs, then a second pass + // which allows them if we don't have enough non blackballed outputs to reach + // the required amount of outputs (since consensus does not care about blackballed + // outputs, we still need to reach the minimum ring size) + if (allow_blackballed) + break; + MINFO("Not enough non blackballed outputs, we'll allow blackballed ones"); + allow_blackballed = true; + num_usable_outs = num_outs; + } // get a random output index from the DB. If we've already seen it, // return to the top of the loop and try again, otherwise add it to the @@ -6801,14 +6813,26 @@ void wallet2::get_outs(std::vector> if (seen_indices.count(i)) continue; - if (is_output_blackballed(std::make_pair(amount, i))) // don't add blackballed outputs + if (!allow_blackballed && is_output_blackballed(std::make_pair(amount, i))) // don't add blackballed outputs + { + --num_usable_outs; continue; + } seen_indices.emplace(i); LOG_PRINT_L2("picking " << i << " as " << type); req.outputs.push_back({amount, i}); ++num_found; } + + // if we had enough unusable outputs, we might fall off here and still + // have too few outputs, so we stuff with one to keep counts good, and + // we'll error out later + while (num_found < requested_outputs_count) + { + req.outputs.push_back({amount, 0}); + ++num_found; + } } // sort the subsection, to ensure the daemon doesn't know which output is ours From 2fbf38ee918894a92f2f058c5551d81d34e215c9 Mon Sep 17 00:00:00 2001 From: TheCharlatan Date: Wed, 26 Sep 2018 00:36:34 +0200 Subject: [PATCH 0483/1404] Fix 32bit depends builds Add architecture flags when cmake invokes gcc manually. Add 32bit to Travis. --- .travis.yml | 2 ++ contrib/depends/toolchain.cmake.in | 4 ++++ src/blockchain_utilities/CMakeLists.txt | 2 ++ src/blocks/CMakeLists.txt | 12 +++++++++--- src/daemon/CMakeLists.txt | 2 ++ 5 files changed, 19 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9368c3ff29e..ca3a24ae82b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -26,6 +26,8 @@ env: - HOST=arm-linux-gnueabihf PACKAGES="gperf g++-arm-linux-gnueabihf" # ARM v8 - HOST=aarch64-linux-gnu PACKAGES="gperf g++-aarch64-linux-gnu" +# i686 Win + - HOST=i686-w64-mingw32 PACKAGES="python3 nsis g++-mingw-w64-i686" # i686 Linux - HOST=i686-pc-linux-gnu PACKAGES="gperf cmake g++-multilib bc python3-zmq" RUN_TESTS=true # Win64 diff --git a/contrib/depends/toolchain.cmake.in b/contrib/depends/toolchain.cmake.in index 3755335571f..bde7497b3e7 100644 --- a/contrib/depends/toolchain.cmake.in +++ b/contrib/depends/toolchain.cmake.in @@ -78,5 +78,9 @@ elseif(ARCHITECTURE STREQUAL "aarch64") set(BUILD_64 ON) endif() +if(ARCHITECTURE STREQUAL "i686" AND CMAKE_SYSTEM_NAME STREQUAL "Linux") + SET(LINUX_32 ON) +endif() + #Create a new global cmake flag that indicates building with depends set (DEPENDS true) diff --git a/src/blockchain_utilities/CMakeLists.txt b/src/blockchain_utilities/CMakeLists.txt index 37bca671fa4..ecd7b754c91 100644 --- a/src/blockchain_utilities/CMakeLists.txt +++ b/src/blockchain_utilities/CMakeLists.txt @@ -32,6 +32,8 @@ if(PER_BLOCK_CHECKPOINT) add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && touch stub.c && ${CMAKE_C_COMPILER} --target=x86_64-apple-darwin11 -o stub.o -c stub.c COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} -r -sectcreate __DATA __blocks_dat ../blocks/checkpoints.dat -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o stub.o && rm -f stub.*) elseif(APPLE AND NOT DEPENDS) add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && touch stub.c && ${CMAKE_C_COMPILER} -o stub.o -c stub.c COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} ${LD_RAW_FLAGS} -r -sectcreate __DATA __blocks_dat ../blocks/checkpoints.dat -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o stub.o && rm -f stub.*) + elseif(LINUX_32) + add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && cp ../blocks/checkpoints.dat blocks.dat && ${CMAKE_LINKER} -m elf_i386 ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o blocks.dat && rm -f blocks.dat) else() add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && cp ../blocks/checkpoints.dat blocks.dat && ${CMAKE_LINKER} ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o blocks.dat && rm -f blocks.dat) endif() diff --git a/src/blocks/CMakeLists.txt b/src/blocks/CMakeLists.txt index cf3f0b35480..ebb5408cc2c 100644 --- a/src/blocks/CMakeLists.txt +++ b/src/blocks/CMakeLists.txt @@ -30,9 +30,15 @@ if(APPLE) add_library(blocks STATIC blockexports.c) set_target_properties(blocks PROPERTIES LINKER_LANGUAGE C) else() - add_custom_command(OUTPUT blocks.o MAIN_DEPENDENCY blocks.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/blocks.o blocks.dat) - add_custom_command(OUTPUT testnet_blocks.o MAIN_DEPENDENCY testnet_blocks.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/testnet_blocks.o testnet_blocks.dat) - add_custom_command(OUTPUT stagenet_blocks.o MAIN_DEPENDENCY stagenet_blocks.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/stagenet_blocks.o stagenet_blocks.dat) + if(LINUX_32) + add_custom_command(OUTPUT blocks.o MAIN_DEPENDENCY blocks.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} -m elf_i386 ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/blocks.o blocks.dat) + add_custom_command(OUTPUT testnet_blocks.o MAIN_DEPENDENCY testnet_blocks.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} -m elf_i386 ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/testnet_blocks.o testnet_blocks.dat) + add_custom_command(OUTPUT stagenet_blocks.o MAIN_DEPENDENCY stagenet_blocks.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} -m elf_i386 ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/stagenet_blocks.o stagenet_blocks.dat) + else() + add_custom_command(OUTPUT blocks.o MAIN_DEPENDENCY blocks.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/blocks.o blocks.dat) + add_custom_command(OUTPUT testnet_blocks.o MAIN_DEPENDENCY testnet_blocks.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/testnet_blocks.o testnet_blocks.dat) + add_custom_command(OUTPUT stagenet_blocks.o MAIN_DEPENDENCY stagenet_blocks.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/stagenet_blocks.o stagenet_blocks.dat) + endif() add_library(blocks STATIC blocks.o testnet_blocks.o stagenet_blocks.o blockexports.c) set_target_properties(blocks PROPERTIES LINKER_LANGUAGE C) endif() diff --git a/src/daemon/CMakeLists.txt b/src/daemon/CMakeLists.txt index b1c4b711dc9..f645836a4b2 100644 --- a/src/daemon/CMakeLists.txt +++ b/src/daemon/CMakeLists.txt @@ -32,6 +32,8 @@ if(PER_BLOCK_CHECKPOINT) add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && touch stub.c && ${CMAKE_C_COMPILER} --target=x86_64-apple-darwin11 -o stub.o -c stub.c COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} -r -sectcreate __DATA __blocks_dat ../blocks/checkpoints.dat -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o stub.o && rm -f stub.*) elseif(APPLE AND NOT DEPENDS) add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && touch stub.c && ${CMAKE_C_COMPILER} -o stub.o -c stub.c COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} ${LD_RAW_FLAGS} -r -sectcreate __DATA __blocks_dat ../blocks/checkpoints.dat -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o stub.o && rm -f stub.*) + elseif(LINUX_32) + add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && cp ../blocks/checkpoints.dat blocks.dat && ${CMAKE_LINKER} -m elf_i386 ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o blocks.dat && rm -f blocks.dat) else() add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && cp ../blocks/checkpoints.dat blocks.dat && ${CMAKE_LINKER} ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o blocks.dat && rm -f blocks.dat) endif() From 402349760f699fe512e9ba97328a5f8827f0c003 Mon Sep 17 00:00:00 2001 From: Riccardo Spagni Date: Sat, 29 Sep 2018 22:10:48 +0200 Subject: [PATCH 0484/1404] Merge pull request #4407 43a06350 ringdb: use cursors to be a bit faster (moneromooo-monero) --- src/wallet/ringdb.cpp | 15 ++++++++------- tests/unit_tests/ringdb.cpp | 24 ++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/src/wallet/ringdb.cpp b/src/wallet/ringdb.cpp index e9fc6866da0..e5995e7fb50 100644 --- a/src/wallet/ringdb.cpp +++ b/src/wallet/ringdb.cpp @@ -398,6 +398,8 @@ bool ringdb::blackball_worker(const std::vector> & epee::misc_utils::auto_scope_leave_caller txn_dtor = epee::misc_utils::create_scope_leave_handler([&](){if (tx_active) mdb_txn_abort(txn);}); tx_active = true; + dbr = mdb_cursor_open(txn, dbi_blackballs, &cursor); + THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to create cursor for blackballs table: " + std::string(mdb_strerror(dbr))); MDB_val key, data; for (const std::pair &output: outputs) @@ -411,25 +413,22 @@ bool ringdb::blackball_worker(const std::vector> & { case BLACKBALL_BLACKBALL: MDEBUG("Blackballing output " << output.first << "/" << output.second); - dbr = mdb_put(txn, dbi_blackballs, &key, &data, MDB_APPENDDUP); + dbr = mdb_cursor_put(cursor, &key, &data, MDB_APPENDDUP); if (dbr == MDB_KEYEXIST) dbr = 0; break; case BLACKBALL_UNBLACKBALL: MDEBUG("Unblackballing output " << output.first << "/" << output.second); - dbr = mdb_del(txn, dbi_blackballs, &key, &data); - if (dbr == MDB_NOTFOUND) - dbr = 0; + dbr = mdb_cursor_get(cursor, &key, &data, MDB_GET_BOTH); + if (dbr == 0) + dbr = mdb_cursor_del(cursor, 0); break; case BLACKBALL_QUERY: - dbr = mdb_cursor_open(txn, dbi_blackballs, &cursor); - THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to create cursor for blackballs table: " + std::string(mdb_strerror(dbr))); dbr = mdb_cursor_get(cursor, &key, &data, MDB_GET_BOTH); THROW_WALLET_EXCEPTION_IF(dbr && dbr != MDB_NOTFOUND, tools::error::wallet_internal_error, "Failed to lookup in blackballs table: " + std::string(mdb_strerror(dbr))); ret = dbr != MDB_NOTFOUND; if (dbr == MDB_NOTFOUND) dbr = 0; - mdb_cursor_close(cursor); break; case BLACKBALL_CLEAR: break; @@ -439,6 +438,8 @@ bool ringdb::blackball_worker(const std::vector> & THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to query blackballs table: " + std::string(mdb_strerror(dbr))); } + mdb_cursor_close(cursor); + if (op == BLACKBALL_CLEAR) { dbr = mdb_drop(txn, dbi_blackballs, 0); diff --git a/tests/unit_tests/ringdb.cpp b/tests/unit_tests/ringdb.cpp index 416ae089091..0d92049ac91 100644 --- a/tests/unit_tests/ringdb.cpp +++ b/tests/unit_tests/ringdb.cpp @@ -150,6 +150,30 @@ TEST(blackball, found) ASSERT_TRUE(ringdb.blackballed(OUTPUT_1)); } +TEST(blackball, vector) +{ + RingDB ringdb; + std::vector> outputs; + outputs.push_back(std::make_pair(0, 1)); + outputs.push_back(std::make_pair(10, 3)); + outputs.push_back(std::make_pair(10, 4)); + outputs.push_back(std::make_pair(10, 8)); + outputs.push_back(std::make_pair(20, 0)); + outputs.push_back(std::make_pair(20, 1)); + outputs.push_back(std::make_pair(30, 5)); + ASSERT_TRUE(ringdb.blackball(outputs)); + ASSERT_TRUE(ringdb.blackballed(std::make_pair(0, 1))); + ASSERT_FALSE(ringdb.blackballed(std::make_pair(10, 2))); + ASSERT_TRUE(ringdb.blackballed(std::make_pair(10, 3))); + ASSERT_TRUE(ringdb.blackballed(std::make_pair(10, 4))); + ASSERT_FALSE(ringdb.blackballed(std::make_pair(10, 5))); + ASSERT_TRUE(ringdb.blackballed(std::make_pair(10, 8))); + ASSERT_TRUE(ringdb.blackballed(std::make_pair(20, 0))); + ASSERT_TRUE(ringdb.blackballed(std::make_pair(20, 1))); + ASSERT_FALSE(ringdb.blackballed(std::make_pair(20, 2))); + ASSERT_TRUE(ringdb.blackballed(std::make_pair(30, 5))); +} + TEST(blackball, unblackball) { RingDB ringdb; From e4130e6ae6314b7364e0411edd84f3c0fdc0b5e5 Mon Sep 17 00:00:00 2001 From: Riccardo Spagni Date: Sat, 29 Sep 2018 22:11:13 +0200 Subject: [PATCH 0485/1404] Merge pull request #4409 7a056f44 WalletAPI: multisigSignData bug fixed (naughtyfox) --- src/wallet/api/pending_transaction.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/wallet/api/pending_transaction.cpp b/src/wallet/api/pending_transaction.cpp index 8d200220d05..913e3156fd0 100644 --- a/src/wallet/api/pending_transaction.cpp +++ b/src/wallet/api/pending_transaction.cpp @@ -200,7 +200,11 @@ std::string PendingTransactionImpl::multisigSignData() { throw std::runtime_error("wallet is not multisig"); } - auto cipher = m_wallet.m_wallet->save_multisig_tx(m_pending_tx); + tools::wallet2::multisig_tx_set txSet; + txSet.m_ptx = m_pending_tx; + txSet.m_signers = m_signers; + auto cipher = m_wallet.m_wallet->save_multisig_tx(txSet); + return epee::string_tools::buff_to_hex_nodelimer(cipher); } catch (const std::exception& e) { m_status = Status_Error; From cd5638f894954a8424af6bd22f4386b121e5dc8f Mon Sep 17 00:00:00 2001 From: Riccardo Spagni Date: Sat, 29 Sep 2018 22:11:32 +0200 Subject: [PATCH 0486/1404] Merge pull request #4417 a21da905 Wallet: use unique_ptr for WalletImpl members (oneiric) --- src/wallet/api/wallet.cpp | 26 ++++++++++---------------- src/wallet/api/wallet.h | 12 ++++++------ 2 files changed, 16 insertions(+), 22 deletions(-) diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index 5827e4d1a7c..96e5c862933 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -376,15 +376,15 @@ WalletImpl::WalletImpl(NetworkType nettype, uint64_t kdf_rounds) , m_rebuildWalletCache(false) , m_is_connected(false) { - m_wallet = new tools::wallet2(static_cast(nettype), kdf_rounds, true); - m_history = new TransactionHistoryImpl(this); - m_wallet2Callback = new Wallet2CallbackImpl(this); + m_wallet = std::make_unique(static_cast(nettype), kdf_rounds, true); + m_history = std::make_unique(this); + m_wallet2Callback = std::make_unique(this); m_wallet->callback(m_wallet2Callback); m_refreshThreadDone = false; m_refreshEnabled = false; - m_addressBook = new AddressBookImpl(this); - m_subaddress = new SubaddressImpl(this); - m_subaddressAccount = new SubaddressAccountImpl(this); + m_addressBook = std::make_unique(this); + m_subaddress = std::make_unique(this); + m_subaddressAccount = std::make_unique(this); m_refreshIntervalMillis = DEFAULT_REFRESH_INTERVAL_MILLIS; @@ -405,12 +405,6 @@ WalletImpl::~WalletImpl() close(false); // do not store wallet as part of the closing activities // Stop refresh thread stopRefresh(); - delete m_wallet2Callback; - delete m_history; - delete m_addressBook; - delete m_subaddress; - delete m_subaddressAccount; - delete m_wallet; LOG_PRINT_L1(__FUNCTION__ << " finished"); } @@ -1551,22 +1545,22 @@ void WalletImpl::disposeTransaction(PendingTransaction *t) TransactionHistory *WalletImpl::history() { - return m_history; + return m_history.get(); } AddressBook *WalletImpl::addressBook() { - return m_addressBook; + return m_addressBook.get(); } Subaddress *WalletImpl::subaddress() { - return m_subaddress; + return m_subaddress.get(); } SubaddressAccount *WalletImpl::subaddressAccount() { - return m_subaddressAccount; + return m_subaddressAccount.get(); } void WalletImpl::setListener(WalletListener *l) diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h index 64350cee763..5963a760765 100644 --- a/src/wallet/api/wallet.h +++ b/src/wallet/api/wallet.h @@ -215,16 +215,16 @@ class WalletImpl : public Wallet friend class SubaddressImpl; friend class SubaddressAccountImpl; - tools::wallet2 * m_wallet; + std::unique_ptr m_wallet; mutable boost::mutex m_statusMutex; mutable int m_status; mutable std::string m_errorString; std::string m_password; - TransactionHistoryImpl * m_history; - Wallet2CallbackImpl * m_wallet2Callback; - AddressBookImpl * m_addressBook; - SubaddressImpl * m_subaddress; - SubaddressAccountImpl * m_subaddressAccount; + std::unique_ptr m_history; + std::unique_ptr m_wallet2Callback; + std::unique_ptr m_addressBook; + std::unique_ptr m_subaddress; + std::unique_ptr m_subaddressAccount; // multi-threaded refresh stuff std::atomic m_refreshEnabled; From 876282fd6956ee6a39447f189b5ab82c1f388bac Mon Sep 17 00:00:00 2001 From: Riccardo Spagni Date: Sat, 29 Sep 2018 22:11:50 +0200 Subject: [PATCH 0487/1404] Merge pull request #4424 92d1da28 unit_tests: fix build with GCC 5.4.0 on ubuntu (moneromooo-monero) --- tests/unit_tests/is_hdd.cpp | 4 ++-- tests/unit_tests/wipeable_string.cpp | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/unit_tests/is_hdd.cpp b/tests/unit_tests/is_hdd.cpp index 1be670e5e1f..040af4f47cc 100644 --- a/tests/unit_tests/is_hdd.cpp +++ b/tests/unit_tests/is_hdd.cpp @@ -6,12 +6,12 @@ TEST(is_hdd, linux_os_root) { std::string path = "/"; - EXPECT_TRUE(tools::is_hdd(path.c_str())); + EXPECT_TRUE(tools::is_hdd(path.c_str()) != boost::none); } #else TEST(is_hdd, unknown_os) { std::string path = ""; - EXPECT_FALSE(tools::is_hdd(path.c_str())); + EXPECT_FALSE(tools::is_hdd(path.c_str()) != boost::none); } #endif diff --git a/tests/unit_tests/wipeable_string.cpp b/tests/unit_tests/wipeable_string.cpp index 65718fd4563..44e050c5c81 100644 --- a/tests/unit_tests/wipeable_string.cpp +++ b/tests/unit_tests/wipeable_string.cpp @@ -194,13 +194,13 @@ TEST(wipeable_string, parse_hexstr) ASSERT_EQ(boost::none, epee::wipeable_string("0").parse_hexstr()); ASSERT_EQ(boost::none, epee::wipeable_string("000").parse_hexstr()); - ASSERT_TRUE((s = epee::wipeable_string("").parse_hexstr())); + ASSERT_TRUE((s = epee::wipeable_string("").parse_hexstr()) != boost::none); ASSERT_EQ(*s, ""); - ASSERT_TRUE((s = epee::wipeable_string("00").parse_hexstr())); + ASSERT_TRUE((s = epee::wipeable_string("00").parse_hexstr()) != boost::none); ASSERT_EQ(*s, epee::wipeable_string("", 1)); - ASSERT_TRUE((s = epee::wipeable_string("41").parse_hexstr())); + ASSERT_TRUE((s = epee::wipeable_string("41").parse_hexstr()) != boost::none); ASSERT_EQ(*s, epee::wipeable_string("A")); - ASSERT_TRUE((s = epee::wipeable_string("414243").parse_hexstr())); + ASSERT_TRUE((s = epee::wipeable_string("414243").parse_hexstr()) != boost::none); ASSERT_EQ(*s, epee::wipeable_string("ABC")); } From 8e07e26555acdf1d6e9feb11f3f77475adf22cc9 Mon Sep 17 00:00:00 2001 From: Riccardo Spagni Date: Sat, 29 Sep 2018 22:12:14 +0200 Subject: [PATCH 0488/1404] Merge pull request #4425 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit a69271fa Fixed a typo (Piotr Kąkol) --- cmake/CheckLinkerFlag.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/CheckLinkerFlag.cmake b/cmake/CheckLinkerFlag.cmake index a3879d0befa..2b507ab7144 100644 --- a/cmake/CheckLinkerFlag.cmake +++ b/cmake/CheckLinkerFlag.cmake @@ -39,7 +39,7 @@ macro(CHECK_LINKER_FLAG flag VARIABLE) endif() set(${VARIABLE} "" CACHE INTERNAL "Have linker flag ${flag}") file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log - "Determining if the ${flag} linker flag is suppored " + "Determining if the ${flag} linker flag is supported " "failed with the following output:\n" "${OUTPUT}\n\n") endif() From 54d883d826fd345e3636bf2b781b91e6d07782b1 Mon Sep 17 00:00:00 2001 From: Riccardo Spagni Date: Sat, 29 Sep 2018 22:12:31 +0200 Subject: [PATCH 0489/1404] Merge pull request #4427 83debef9 wallet_rpc_server: remove verbose field in incoming_transfers query (moneromooo-monero) --- src/wallet/wallet_rpc_server.cpp | 2 +- src/wallet/wallet_rpc_server_commands_defs.h | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index e6eb64d12b6..27631187c84 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -1554,7 +1554,7 @@ namespace tools rpc_transfers.global_index = td.m_global_output_index; rpc_transfers.tx_hash = epee::string_tools::pod_to_hex(td.m_txid); rpc_transfers.subaddr_index = {td.m_subaddr_index.major, td.m_subaddr_index.minor}; - rpc_transfers.key_image = req.verbose && td.m_key_image_known ? epee::string_tools::pod_to_hex(td.m_key_image) : ""; + rpc_transfers.key_image = td.m_key_image_known ? epee::string_tools::pod_to_hex(td.m_key_image) : ""; res.transfers.push_back(rpc_transfers); } } diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h index 2168e0f7124..81ea2292891 100644 --- a/src/wallet/wallet_rpc_server_commands_defs.h +++ b/src/wallet/wallet_rpc_server_commands_defs.h @@ -883,13 +883,11 @@ namespace wallet_rpc std::string transfer_type; uint32_t account_index; std::set subaddr_indices; - bool verbose; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(transfer_type) KV_SERIALIZE(account_index) KV_SERIALIZE(subaddr_indices) - KV_SERIALIZE(verbose) END_KV_SERIALIZE_MAP() }; From 365a9d0c09684e13da7dd36e1258ecd65e964c4c Mon Sep 17 00:00:00 2001 From: Riccardo Spagni Date: Sat, 29 Sep 2018 22:12:56 +0200 Subject: [PATCH 0490/1404] Merge pull request #4434 89288863 README: mention ASAN usage alongside valgrind (moneromooo-monero) --- README.md | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ac40f08d39a..d16b215289e 100644 --- a/README.md +++ b/README.md @@ -666,9 +666,19 @@ Type `run` to run monerod ### Analysing memory corruption -We use the tool `valgrind` for this. +There are two tools available: -Run with `valgrind /path/to/monerod`. It will be slow. +* ASAN + +Configure Monero with the -D SANITIZE=ON cmake flag, eg: + + cd build/debug && cmake -D SANITIZE=ON -D CMAKE_BUILD_TYPE=Debug ../.. + +You can then run the monero tools normally. Performance will typically halve. + +* valgrind + +Install valgrind and run as `valgrind /path/to/monerod`. It will be very slow. ### LMDB From b710d780268de63e77cd0d0a3efaf733819f4897 Mon Sep 17 00:00:00 2001 From: Riccardo Spagni Date: Sat, 29 Sep 2018 22:13:16 +0200 Subject: [PATCH 0491/1404] Merge pull request #4439 174f31bf simplewallet: don't complain about payment id on pool mined blocks (moneromooo-monero) --- src/simplewallet/simplewallet.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 302d2a99915..06655ed69d1 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -4157,10 +4157,11 @@ void simple_wallet::on_money_received(uint64_t height, const crypto::hash &txid, if (find_tx_extra_field_by_type(tx_extra_fields, extra_nonce)) { crypto::hash8 payment_id8 = crypto::null_hash8; + crypto::hash payment_id = crypto::null_hash; if (get_encrypted_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id8)) message_writer() << tr("NOTE: this transaction uses an encrypted payment ID: consider using subaddresses instead"); - else + else if (get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id)) message_writer(console_color_red, false) << tr("WARNING: this transaction uses an unencrypted payment ID: consider using subaddresses instead"); } From b4ec67b2f7a9a9ca25783dec8598422de0d89149 Mon Sep 17 00:00:00 2001 From: Riccardo Spagni Date: Sat, 29 Sep 2018 22:13:34 +0200 Subject: [PATCH 0492/1404] Merge pull request #4443 b2972927 osx compilation fix: missing boost libs added (Dusan Klinec) --- contrib/epee/src/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/contrib/epee/src/CMakeLists.txt b/contrib/epee/src/CMakeLists.txt index 0b5e7ae6ca4..bc437deb903 100644 --- a/contrib/epee/src/CMakeLists.txt +++ b/contrib/epee/src/CMakeLists.txt @@ -54,7 +54,9 @@ endif() target_link_libraries(epee PUBLIC easylogging + ${Boost_CHRONO_LIBRARY} ${Boost_FILESYSTEM_LIBRARY} + ${Boost_THREAD_LIBRARY} PRIVATE ${OPENSSL_LIBRARIES} ${EXTRA_LIBRARIES}) From 09fb2bb5f895410a401f699f3d621d52fe12bee3 Mon Sep 17 00:00:00 2001 From: Riccardo Spagni Date: Sat, 29 Sep 2018 22:14:09 +0200 Subject: [PATCH 0493/1404] Merge pull request #4445 fe125647 Fixup RENAME_DB() macro (Howard Chu) --- src/blockchain_db/lmdb/db_lmdb.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp index b0f3ca5f009..d8f7df5f73d 100644 --- a/src/blockchain_db/lmdb/db_lmdb.cpp +++ b/src/blockchain_db/lmdb/db_lmdb.cpp @@ -3490,7 +3490,17 @@ void BlockchainLMDB::fixup() BlockchainDB::fixup(); } -#define RENAME_DB(name) \ +#define RENAME_DB(name) do { \ + char n2[] = name; \ + MDB_dbi tdbi; \ + n2[sizeof(n2)-2]--; \ + /* play some games to put (name) on a writable page */ \ + result = mdb_dbi_open(txn, n2, MDB_CREATE, &tdbi); \ + if (result) \ + throw0(DB_ERROR(lmdb_error("Failed to create " + std::string(n2) + ": ", result).c_str())); \ + result = mdb_drop(txn, tdbi, 1); \ + if (result) \ + throw0(DB_ERROR(lmdb_error("Failed to delete " + std::string(n2) + ": ", result).c_str())); \ k.mv_data = (void *)name; \ k.mv_size = sizeof(name)-1; \ result = mdb_cursor_open(txn, 1, &c_cur); \ @@ -3500,7 +3510,7 @@ void BlockchainLMDB::fixup() if (result) \ throw0(DB_ERROR(lmdb_error("Failed to get DB record for " name ": ", result).c_str())); \ ptr = (char *)k.mv_data; \ - ptr[sizeof(name)-2]++ + ptr[sizeof(name)-2]++; } while(0) #define LOGIF(y) if (ELPP->vRegistry()->allowed(y, "global")) From 5bcead236fc1c9c095403d96d2aa5273f5443815 Mon Sep 17 00:00:00 2001 From: Riccardo Spagni Date: Sat, 29 Sep 2018 22:14:28 +0200 Subject: [PATCH 0494/1404] Merge pull request #4446 69da14e1 fixes make debug compilation on OSX (Dusan Klinec) --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 994d4769111..a7746371e23 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -769,7 +769,7 @@ else() endif() if(APPLE) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden -DGTEST_HAS_TR1_TUPLE=0") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=default -DGTEST_HAS_TR1_TUPLE=0") endif() set(DEBUG_FLAGS "-g3") From 8e98ed8c713ec1a12b81665d924d13571493f346 Mon Sep 17 00:00:00 2001 From: Riccardo Spagni Date: Sat, 29 Sep 2018 22:14:56 +0200 Subject: [PATCH 0495/1404] Merge pull request #4448 6a781408 Make depends use self built clang for darwin (TheCharlatan) --- CMakeLists.txt | 12 ++++++++---- contrib/depends/packages/hidapi-darwin.mk | 2 +- contrib/depends/packages/packages.mk | 2 +- contrib/depends/packages/sodium-darwin.mk | 3 ++- 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a7746371e23..fc0ecb14b6b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -883,10 +883,14 @@ list(APPEND EXTRA_LIBRARIES ${CMAKE_DL_LIBS}) if (HIDAPI_FOUND) if (APPLE) - find_library(COREFOUNDATION CoreFoundation) - find_library(IOKIT IOKit) - list(APPEND EXTRA_LIBRARIES ${IOKIT}) - list(APPEND EXTRA_LIBRARIES ${COREFOUNDATION}) + if(DEPENDS) + list(APPEND EXTRA_LIBRARIES "-framework Foundation -framework IOKit") + else() + find_library(COREFOUNDATION CoreFoundation) + find_library(IOKIT IOKit) + list(APPEND EXTRA_LIBRARIES ${IOKIT}) + list(APPEND EXTRA_LIBRARIES ${COREFOUNDATION}) + endif() endif() if (WIN32) list(APPEND EXTRA_LIBRARIES setupapi) diff --git a/contrib/depends/packages/hidapi-darwin.mk b/contrib/depends/packages/hidapi-darwin.mk index 1fbbb59e06c..014aba5787b 100644 --- a/contrib/depends/packages/hidapi-darwin.mk +++ b/contrib/depends/packages/hidapi-darwin.mk @@ -11,7 +11,7 @@ endef define $(package)_config_cmds ./bootstrap &&\ - $($(package)_autoconf) $($(package)_config_opts) RANLIB="$(host_prefix)/native/bin/x86_64-apple-darwin11-ranlib" AR="$(host_prefix)/native/bin/x86_64-apple-darwin11-ar" + $($(package)_autoconf) $($(package)_config_opts) RANLIB="$(host_prefix)/native/bin/x86_64-apple-darwin11-ranlib" AR="$(host_prefix)/native/bin/x86_64-apple-darwin11-ar" CC="$(host_prefix)/native/bin/$($(package)_cc)" endef define $(package)_build_cmds diff --git a/contrib/depends/packages/packages.mk b/contrib/depends/packages/packages.mk index 13ba3762855..9bfbff7d9d1 100644 --- a/contrib/depends/packages/packages.mk +++ b/contrib/depends/packages/packages.mk @@ -4,7 +4,7 @@ native_packages := native_ccache wallet_packages=bdb darwin_native_packages = native_biplist native_ds_store native_mac_alias -darwin_packages += sodium-darwin hidapi-darwin +darwin_packages = sodium-darwin hidapi-darwin linux_packages = eudev libusb hidapi-linux diff --git a/contrib/depends/packages/sodium-darwin.mk b/contrib/depends/packages/sodium-darwin.mk index 796bead1622..59c6d1ec67d 100644 --- a/contrib/depends/packages/sodium-darwin.mk +++ b/contrib/depends/packages/sodium-darwin.mk @@ -5,13 +5,14 @@ $(package)_file_name=libsodium-$($(package)_version).tar.gz $(package)_sha256_hash=fb6a9e879a2f674592e4328c5d9f79f082405ee4bb05cb6e679b90afe9e178f4 define $(package)_set_vars +$(package)_build_opts_darwin=OS=Darwin LIBTOOL="$($(package)_libtool)" $(package)_config_opts=--enable-static $(package)_config_opts+=--prefix=$(host_prefix) endef define $(package)_config_cmds ./autogen.sh &&\ - $($(package)_autoconf) $($(package)_config_opts) RANLIB="$(host_prefix)/native/bin/x86_64-apple-darwin11-ranlib" AR="$(host_prefix)/native/bin/x86_64-apple-darwin11-ar" + $($(package)_autoconf) $($(package)_config_opts) RANLIB="$(host_prefix)/native/bin/x86_64-apple-darwin11-ranlib" AR="$(host_prefix)/native/bin/x86_64-apple-darwin11-ar" CC="$(host_prefix)/native/bin/$($(package)_cc)" endef define $(package)_build_cmds From 2ec0d780c497d6ef68314fbda7d32cba41a58bf1 Mon Sep 17 00:00:00 2001 From: Riccardo Spagni Date: Sat, 29 Sep 2018 22:15:31 +0200 Subject: [PATCH 0496/1404] Merge pull request #4449 17142ec9 malloc scratchpad for all supported android archs (m2049r) --- src/crypto/slow-hash.c | 43 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/src/crypto/slow-hash.c b/src/crypto/slow-hash.c index 80bf4e06f15..40cfb0461e5 100644 --- a/src/crypto/slow-hash.c +++ b/src/crypto/slow-hash.c @@ -1040,10 +1040,37 @@ STATIC INLINE void aes_pseudo_round_xor(const uint8_t *in, uint8_t *out, const u } } +#ifdef FORCE_USE_HEAP +STATIC INLINE void* aligned_malloc(size_t size, size_t align) +{ + void *result; +#ifdef _MSC_VER + result = _aligned_malloc(size, align); +#else + if (posix_memalign(&result, align, size)) result = NULL; +#endif + return result; +} + +STATIC INLINE void aligned_free(void *ptr) +{ +#ifdef _MSC_VER + _aligned_free(ptr); +#else + free(ptr); +#endif +} +#endif /* FORCE_USE_HEAP */ + void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int prehashed) { RDATA_ALIGN16 uint8_t expandedKey[240]; + +#ifndef FORCE_USE_HEAP RDATA_ALIGN16 uint8_t hp_state[MEMORY]; +#else + uint8_t *hp_state = (uint8_t *)aligned_malloc(MEMORY,16); +#endif uint8_t text[INIT_SIZE_BYTE]; RDATA_ALIGN16 uint64_t a[2]; @@ -1129,6 +1156,10 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int memcpy(state.init, text, INIT_SIZE_BYTE); hash_permutation(&state.hs); extra_hashes[state.hs.b[0] & 3](&state, 200, hash); + +#ifdef FORCE_USE_HEAP + aligned_free(hp_state); +#endif } #else /* aarch64 && crypto */ @@ -1270,8 +1301,7 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int #ifndef FORCE_USE_HEAP uint8_t long_state[MEMORY]; #else - uint8_t *long_state = NULL; - long_state = (uint8_t *)malloc(MEMORY); + uint8_t *long_state = (uint8_t *)malloc(MEMORY); #endif if (prehashed) { @@ -1449,7 +1479,12 @@ union cn_slow_hash_state { #pragma pack(pop) void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int prehashed) { +#ifndef FORCE_USE_HEAP uint8_t long_state[MEMORY]; +#else + uint8_t *long_state = (uint8_t *)malloc(MEMORY); +#endif + union cn_slow_hash_state state; uint8_t text[INIT_SIZE_BYTE]; uint8_t a[AES_BLOCK_SIZE]; @@ -1534,6 +1569,10 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int /*memcpy(hash, &state, 32);*/ extra_hashes[state.hs.b[0] & 3](&state, 200, hash); oaes_free((OAES_CTX **) &aes_ctx); + +#ifdef FORCE_USE_HEAP + free(long_state); +#endif } #endif From e9512585430b84e2ee47d4069a3e5903c6e47961 Mon Sep 17 00:00:00 2001 From: Riccardo Spagni Date: Sat, 29 Sep 2018 22:15:52 +0200 Subject: [PATCH 0497/1404] Merge pull request #4456 06d05c21 device: set device name correctly if key_on_device is set (Dusan Klinec) --- src/wallet/wallet2.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 33699cb79bf..95474ce34ad 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -3282,9 +3282,16 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_ encrypted_secret_keys = field_encrypted_secret_keys; GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, device_name, std::string, String, false, std::string()); - if (m_device_name.empty() && field_device_name_found) + if (m_device_name.empty()) { - m_device_name = field_device_name; + if (field_device_name_found) + { + m_device_name = field_device_name; + } + else + { + m_device_name = m_key_device_type == hw::device::device_type::LEDGER ? "Ledger" : "default"; + } } } else From 4e3bff92ab4517d7eaa8a03fb1731283ce231342 Mon Sep 17 00:00:00 2001 From: Riccardo Spagni Date: Sat, 29 Sep 2018 22:16:19 +0200 Subject: [PATCH 0498/1404] Merge pull request #4457 85318e78 build: set ARCH_FLAG before compiler/linker flag checks (xiphon) --- CMakeLists.txt | 68 ++++++++++++++++++++++++++------------------------ 1 file changed, 36 insertions(+), 32 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index fc0ecb14b6b..c31c14350c9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -529,6 +529,40 @@ else() endif() endif() endif() + + option(NO_AES "Explicitly disable AES support" ${NO_AES}) + + if(NO_AES) + message(STATUS "AES support explicitly disabled") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DNO_AES") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DNO_AES") + elseif(NOT ARM AND NOT PPC64LE AND NOT PPC64 AND NOT PPC AND NOT S390X) + message(STATUS "AES support enabled") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -maes") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -maes") + elseif(PPC64LE OR PPC64 OR PPC) + message(STATUS "AES support not available on POWER") + elseif(S390X) + message(STATUS "AES support not available on s390x") + elseif(ARM6) + message(STATUS "AES support not available on ARMv6") + elseif(ARM7) + message(STATUS "AES support not available on ARMv7") + elseif(ARM8) + CHECK_CXX_ACCEPTS_FLAG("-march=${ARCH}+crypto" ARCH_PLUS_CRYPTO) + if(ARCH_PLUS_CRYPTO) + message(STATUS "Crypto extensions enabled for ARMv8") + set(ARCH_FLAG "-march=${ARCH}+crypto") + else() + message(STATUS "Crypto extensions unavailable on your ARMv8 device") + endif() + else() + message(STATUS "AES support disabled") + endif() + + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${ARCH_FLAG}") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${ARCH_FLAG}") + set(WARNINGS "-Wall -Wextra -Wpointer-arith -Wundef -Wvla -Wwrite-strings -Wno-error=extra -Wno-error=deprecated-declarations -Wno-unused-parameter -Wno-unused-variable -Wno-error=unused-variable -Wno-error=undef -Wno-error=uninitialized") if(NOT MINGW) set(WARNINGS_AS_ERRORS_FLAG "-Werror") @@ -639,38 +673,8 @@ else() message(STATUS "Using C++ security hardening flags: ${CXX_SECURITY_FLAGS}") message(STATUS "Using linker security hardening flags: ${LD_SECURITY_FLAGS}") - option(NO_AES "Explicitly disable AES support" ${NO_AES}) - - if(NO_AES) - message(STATUS "AES support explicitly disabled") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DNO_AES") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DNO_AES") - elseif(NOT ARM AND NOT PPC64LE AND NOT PPC64 AND NOT PPC AND NOT S390X) - message(STATUS "AES support enabled") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -maes") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -maes") - elseif(PPC64LE OR PPC64 OR PPC) - message(STATUS "AES support not available on POWER") - elseif(S390X) - message(STATUS "AES support not available on s390x") - elseif(ARM6) - message(STATUS "AES support not available on ARMv6") - elseif(ARM7) - message(STATUS "AES support not available on ARMv7") - elseif(ARM8) - CHECK_CXX_ACCEPTS_FLAG("-march=${ARCH}+crypto" ARCH_PLUS_CRYPTO) - if(ARCH_PLUS_CRYPTO) - message(STATUS "Crypto extensions enabled for ARMv8") - set(ARCH_FLAG "-march=${ARCH}+crypto") - else() - message(STATUS "Crypto extensions unavailable on your ARMv8 device") - endif() - else() - message(STATUS "AES support disabled") - endif() - - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c11 -D_GNU_SOURCE ${MINGW_FLAG} ${STATIC_ASSERT_FLAG} ${WARNINGS} ${C_WARNINGS} ${ARCH_FLAG} ${COVERAGE_FLAGS} ${PIC_FLAG} ${C_SECURITY_FLAGS}") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -D_GNU_SOURCE ${MINGW_FLAG} ${STATIC_ASSERT_CPP_FLAG} ${WARNINGS} ${CXX_WARNINGS} ${ARCH_FLAG} ${COVERAGE_FLAGS} ${PIC_FLAG} ${CXX_SECURITY_FLAGS}") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c11 -D_GNU_SOURCE ${MINGW_FLAG} ${STATIC_ASSERT_FLAG} ${WARNINGS} ${C_WARNINGS} ${COVERAGE_FLAGS} ${PIC_FLAG} ${C_SECURITY_FLAGS}") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -D_GNU_SOURCE ${MINGW_FLAG} ${STATIC_ASSERT_CPP_FLAG} ${WARNINGS} ${CXX_WARNINGS} ${COVERAGE_FLAGS} ${PIC_FLAG} ${CXX_SECURITY_FLAGS}") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${LD_SECURITY_FLAGS}") # With GCC 6.1.1 the compiled binary malfunctions due to aliasing. Until that From 9a54d0033e1104a264ddf05cc7e00bbbac12fba5 Mon Sep 17 00:00:00 2001 From: Riccardo Spagni Date: Sat, 29 Sep 2018 22:16:40 +0200 Subject: [PATCH 0499/1404] Merge pull request #4458 921b0fb1 use default create_address_file argument (m2049r) --- src/wallet/api/wallet.cpp | 4 ++-- src/wallet/wallet2.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index 96e5c862933..3d4e981ea03 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -603,7 +603,7 @@ bool WalletImpl::recoverFromKeysWithPassword(const std::string &path, LOG_PRINT_L1("Generated new view only wallet from keys"); } if(has_spendkey && !has_viewkey) { - m_wallet->generate(path, password, spendkey, true, false, false); + m_wallet->generate(path, password, spendkey, true, false); setSeedLanguage(language); LOG_PRINT_L1("Generated deterministic wallet from spend key with seed language: " + language); } @@ -623,7 +623,7 @@ bool WalletImpl::recoverFromDevice(const std::string &path, const std::string &p m_recoveringFromDevice = true; try { - m_wallet->restore(path, password, device_name, false); + m_wallet->restore(path, password, device_name); LOG_PRINT_L1("Generated new wallet from device: " + device_name); } catch (const std::exception& e) { diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index acbc09c3622..db1ac7e429b 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -554,7 +554,7 @@ namespace tools * \param device_name name of HW to use * \param create_address_file Whether to create an address file */ - void restore(const std::string& wallet_, const epee::wipeable_string& password, const std::string &device_name, bool create_address_file); + void restore(const std::string& wallet_, const epee::wipeable_string& password, const std::string &device_name, bool create_address_file = false); /*! * \brief Creates a multisig wallet From effcbf2060a48b01779a713fe66c82714a7fb7f2 Mon Sep 17 00:00:00 2001 From: Riccardo Spagni Date: Sat, 29 Sep 2018 22:17:00 +0200 Subject: [PATCH 0500/1404] Merge pull request #4459 bcf3f6af fuzz_tests: catch unhandled exceptions (moneromooo-monero) 3ebd05d4 miner: restore stream flags after changing them (moneromooo-monero) a093092e levin_protocol_handler_async: do not propagate exception through dtor (moneromooo-monero) 1eebb82b net_helper: do not propagate exceptions through dtor (moneromooo-monero) fb6a3630 miner: do not propagate exceptions through dtor (moneromooo-monero) 2e2139ff epee: do not propagate exception through dtor (moneromooo-monero) 0749a8bd db_lmdb: do not propagate exceptions in dtor (moneromooo-monero) 1b0afeeb wallet_rpc_server: exit cleanly on unhandled exceptions (moneromooo-monero) 418a9936 unit_tests: catch unhandled exceptions (moneromooo-monero) ea7f9543 threadpool: do not propagate exceptions through the dtor (moneromooo-monero) 6e855422 gen_multisig: nice exit on unhandled exception (moneromooo-monero) 53df2deb db_lmdb: catch error in mdb_stat calls during migration (moneromooo-monero) e67016dd blockchain_blackball: catch failure to commit db transaction (moneromooo-monero) 661439f4 mlog: don't remove old logs if we failed to rename the current file (moneromooo-monero) 5fdcda50 easylogging++: test for NULL before dereference (moneromooo-monero) 7ece1550 performance_test: fix bad last argument calling add_arg (moneromooo-monero) a085da32 unit_tests: add check for page size > 0 before dividing (moneromooo-monero) d8b1ec8b unit_tests: use std::shared_ptr to shut coverity up about leaks (moneromooo-monero) 02563bf4 simplewallet: top level exception catcher to print nicer messages (moneromooo-monero) c57a65b2 blockchain_blackball: fix shift range for 32 bit archs (moneromooo-monero) --- contrib/epee/include/console_handler.h | 3 +- .../net/levin_protocol_handler_async.h | 6 ++ contrib/epee/include/net/net_helper.h | 3 +- contrib/epee/src/mlog.cpp | 7 +- external/easylogging++/easylogging++.cc | 4 +- src/blockchain_db/lmdb/db_lmdb.cpp | 21 ++++-- .../blockchain_blackball.cpp | 8 ++- src/common/threadpool.cpp | 3 +- src/cryptonote_basic/miner.cpp | 6 +- src/gen_multisig/gen_multisig.cpp | 4 +- src/simplewallet/simplewallet.cpp | 4 +- src/wallet/wallet_rpc_server.cpp | 3 + tests/fuzz/fuzzer.cpp | 4 ++ tests/performance_tests/main.cpp | 8 +-- tests/unit_tests/main.cpp | 4 ++ tests/unit_tests/mlocker.cpp | 69 ++++++++++--------- 16 files changed, 101 insertions(+), 56 deletions(-) diff --git a/contrib/epee/include/console_handler.h b/contrib/epee/include/console_handler.h index 0e22a971c27..2ccf5b095a0 100644 --- a/contrib/epee/include/console_handler.h +++ b/contrib/epee/include/console_handler.h @@ -63,7 +63,8 @@ namespace epee ~async_stdin_reader() { - stop(); + try { stop(); } + catch (...) { /* ignore */ } } #ifdef HAVE_READLINE diff --git a/contrib/epee/include/net/levin_protocol_handler_async.h b/contrib/epee/include/net/levin_protocol_handler_async.h index 0b1fe05fa1d..e9853ee265e 100644 --- a/contrib/epee/include/net/levin_protocol_handler_async.h +++ b/contrib/epee/include/net/levin_protocol_handler_async.h @@ -275,6 +275,9 @@ class async_protocol_handler } virtual ~async_protocol_handler() { + try + { + m_deletion_initiated = true; if(m_connection_initialized) { @@ -288,6 +291,9 @@ class async_protocol_handler CHECK_AND_ASSERT_MES_NO_RET(0 == boost::interprocess::ipcdetail::atomic_read32(&m_wait_count), "Failed to wait for operation completion. m_wait_count = " << m_wait_count); MTRACE(m_connection_context << "~async_protocol_handler()"); + + } + catch (...) { /* ignore */ } } bool start_outer_call() diff --git a/contrib/epee/include/net/net_helper.h b/contrib/epee/include/net/net_helper.h index 2c2efcd82df..94744ac21a4 100644 --- a/contrib/epee/include/net/net_helper.h +++ b/contrib/epee/include/net/net_helper.h @@ -106,7 +106,8 @@ namespace net_utils ~blocked_mode_client() { //profile_tools::local_coast lc("~blocked_mode_client()", 3); - shutdown(); + try { shutdown(); } + catch(...) { /* ignore */ } } inline diff --git a/contrib/epee/src/mlog.cpp b/contrib/epee/src/mlog.cpp index cd9867ff57c..61d853ef484 100644 --- a/contrib/epee/src/mlog.cpp +++ b/contrib/epee/src/mlog.cpp @@ -137,7 +137,12 @@ void mlog_configure(const std::string &filename_base, bool console, const std::s el::Loggers::addFlag(el::LoggingFlag::StrictLogFileSizeCheck); el::Helpers::installPreRollOutCallback([filename_base, max_log_files](const char *name, size_t){ std::string rname = generate_log_filename(filename_base.c_str()); - rename(name, rname.c_str()); + int ret = rename(name, rname.c_str()); + if (ret < 0) + { + // can't log a failure, but don't do the file removal below + return; + } if (max_log_files != 0) { std::vector found_files; diff --git a/external/easylogging++/easylogging++.cc b/external/easylogging++/easylogging++.cc index b438fa543a7..a4bdad4cf73 100644 --- a/external/easylogging++/easylogging++.cc +++ b/external/easylogging++/easylogging++.cc @@ -1975,11 +1975,11 @@ void VRegistry::setCategories(const char* categories, bool clear) { m_cached_allowed_categories.clear(); m_categoriesString.clear(); } + if (!categories) + return; if (!m_categoriesString.empty()) m_categoriesString += ","; m_categoriesString += categories; - if (!categories) - return; bool isCat = true; bool isLevel = false; diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp index d8f7df5f73d..824598f8ca4 100644 --- a/src/blockchain_db/lmdb/db_lmdb.cpp +++ b/src/blockchain_db/lmdb/db_lmdb.cpp @@ -1145,7 +1145,10 @@ BlockchainLMDB::~BlockchainLMDB() // batch transaction shouldn't be active at this point. If it is, consider it aborted. if (m_batch_active) - batch_abort(); + { + try { batch_abort(); } + catch (...) { /* ignore */ } + } if (m_open) close(); } @@ -3589,7 +3592,9 @@ void BlockchainLMDB::migrate_0_1() throw0(DB_ERROR(lmdb_error("Failed to open a cursor for block_heights: ", result).c_str())); if (!i) { MDB_stat ms; - mdb_stat(txn, m_block_heights, &ms); + result = mdb_stat(txn, m_block_heights, &ms); + if (result) + throw0(DB_ERROR(lmdb_error("Failed to query block_heights table: ", result).c_str())); i = ms.ms_entries; } } @@ -3692,7 +3697,9 @@ void BlockchainLMDB::migrate_0_1() throw0(DB_ERROR(lmdb_error("Failed to open a cursor for block_timestamps: ", result).c_str())); if (!i) { MDB_stat ms; - mdb_stat(txn, m_block_info, &ms); + result = mdb_stat(txn, m_block_info, &ms); + if (result) + throw0(DB_ERROR(lmdb_error("Failed to query block_info table: ", result).c_str())); i = ms.ms_entries; } } @@ -3812,7 +3819,9 @@ void BlockchainLMDB::migrate_0_1() throw0(DB_ERROR(lmdb_error("Failed to open a cursor for spent_keys: ", result).c_str())); if (!i) { MDB_stat ms; - mdb_stat(txn, m_hf_versions, &ms); + result = mdb_stat(txn, m_hf_versions, &ms); + if (result) + throw0(DB_ERROR(lmdb_error("Failed to query hf_versions table: ", result).c_str())); i = ms.ms_entries; } } @@ -3967,7 +3976,9 @@ void BlockchainLMDB::migrate_0_1() throw0(DB_ERROR(lmdb_error("Failed to open a cursor for txs: ", result).c_str())); if (!i) { MDB_stat ms; - mdb_stat(txn, m_txs, &ms); + result = mdb_stat(txn, m_txs, &ms); + if (result) + throw0(DB_ERROR(lmdb_error("Failed to query txs table: ", result).c_str())); i = ms.ms_entries; if (i) { MDB_val_set(pk, "txblk"); diff --git a/src/blockchain_utilities/blockchain_blackball.cpp b/src/blockchain_utilities/blockchain_blackball.cpp index 1c6e54d1086..5eb2acc79c1 100644 --- a/src/blockchain_utilities/blockchain_blackball.cpp +++ b/src/blockchain_utilities/blockchain_blackball.cpp @@ -401,7 +401,8 @@ static bool for_all_transactions(const std::string &filename, uint64_t &start_id } mdb_cursor_close(cur); - mdb_txn_commit(txn); + dbr = mdb_txn_commit(txn); + if (dbr) throw std::runtime_error("Failed to commit db transaction: " + std::string(mdb_strerror(dbr))); tx_active = false; mdb_dbi_close(env, dbi); mdb_env_close(env); @@ -471,7 +472,8 @@ static uint64_t find_first_diverging_transaction(const std::string &first_filena for (int i = 0; i < 2; ++i) { mdb_cursor_close(cur[i]); - mdb_txn_commit(txn[i]); + dbr = mdb_txn_commit(txn[i]); + if (dbr) throw std::runtime_error("Failed to query transaction: " + std::string(mdb_strerror(dbr))); tx_active[i] = false; mdb_dbi_close(env[i], dbi[i]); mdb_env_close(env[i]); @@ -675,7 +677,7 @@ static uint64_t get_ring_subset_instances(MDB_txn *txn, uint64_t amount, const s uint64_t extra = 0; std::vector subset; subset.reserve(ring.size()); - for (uint64_t mask = 1; mask < (1u << ring.size()) - 1; ++mask) + for (uint64_t mask = 1; mask < (((uint64_t)1) << ring.size()) - 1; ++mask) { subset.resize(0); for (size_t i = 0; i < ring.size(); ++i) diff --git a/src/common/threadpool.cpp b/src/common/threadpool.cpp index 6b69e2a12c9..5ea04a3531d 100644 --- a/src/common/threadpool.cpp +++ b/src/common/threadpool.cpp @@ -57,7 +57,8 @@ threadpool::~threadpool() { has_work.notify_all(); } for (size_t i = 0; i(total_hr)/static_cast(m_last_hash_rates.size()); + const auto flags = std::cout.flags(); const auto precision = std::cout.precision(); - std::cout << "hashrate: " << std::setprecision(4) << std::fixed << hr << precision << ENDL; + std::cout << "hashrate: " << std::setprecision(4) << std::fixed << hr << flags << precision << ENDL; } } m_last_hr_merge_time = misc_utils::get_tick_count(); diff --git a/src/gen_multisig/gen_multisig.cpp b/src/gen_multisig/gen_multisig.cpp index f11f442bc7a..69be70e1b21 100644 --- a/src/gen_multisig/gen_multisig.cpp +++ b/src/gen_multisig/gen_multisig.cpp @@ -167,6 +167,8 @@ static bool generate_multisig(uint32_t threshold, uint32_t total, const std::str int main(int argc, char* argv[]) { + TRY_ENTRY(); + po::options_description desc_params(wallet_args::tr("Wallet options")); command_line::add_arg(desc_params, arg_filename_base); command_line::add_arg(desc_params, arg_scheme); @@ -254,5 +256,5 @@ int main(int argc, char* argv[]) return 1; return 0; - //CATCH_ENTRY_L0("main", 1); + CATCH_ENTRY_L0("main", 1); } diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 06655ed69d1..7c68de3f316 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -8073,6 +8073,8 @@ void simple_wallet::commit_or_save(std::vector& ptx_ //---------------------------------------------------------------------------------------------------- int main(int argc, char* argv[]) { + TRY_ENTRY(); + #ifdef WIN32 // Activate UTF-8 support for Boost filesystem classes on Windows std::locale::global(boost::locale::generator().generate("")); @@ -8167,5 +8169,5 @@ int main(int argc, char* argv[]) w.deinit(); } return 0; - //CATCH_ENTRY_L0("main", 1); + CATCH_ENTRY_L0("main", 1); } diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index 27631187c84..5991e0cc250 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -3457,6 +3457,8 @@ class t_executor final std::string const t_executor::NAME = "Wallet RPC Daemon"; int main(int argc, char** argv) { + TRY_ENTRY(); + namespace po = boost::program_options; const auto arg_wallet_file = wallet_args::arg_wallet_file(); @@ -3500,4 +3502,5 @@ int main(int argc, char** argv) { } return daemonizer::daemonize(argc, const_cast(argv), t_executor{}, *vm) ? 0 : 1; + CATCH_ENTRY_L0("main", 1); } diff --git a/tests/fuzz/fuzzer.cpp b/tests/fuzz/fuzzer.cpp index 032ff049aaf..ab14e2b7921 100644 --- a/tests/fuzz/fuzzer.cpp +++ b/tests/fuzz/fuzzer.cpp @@ -46,6 +46,8 @@ static int __AFL_LOOP(int) int run_fuzzer(int argc, const char **argv, Fuzzer &fuzzer) { + TRY_ENTRY(); + if (argc < 2) { std::cout << "usage: " << argv[0] << " " << "" << std::endl; @@ -69,4 +71,6 @@ int run_fuzzer(int argc, const char **argv, Fuzzer &fuzzer) } return 0; + + CATCH_ENTRY_L0("run_fuzzer", 1); } diff --git a/tests/performance_tests/main.cpp b/tests/performance_tests/main.cpp index 7c5135c65cb..87a1573c2c8 100644 --- a/tests/performance_tests/main.cpp +++ b/tests/performance_tests/main.cpp @@ -75,10 +75,10 @@ int main(int argc, char** argv) const command_line::arg_descriptor arg_verbose = { "verbose", "Verbose output", false }; const command_line::arg_descriptor arg_stats = { "stats", "Including statistics (min/median)", false }; const command_line::arg_descriptor arg_loop_multiplier = { "loop-multiplier", "Run for that many times more loops", 1 }; - command_line::add_arg(desc_options, arg_filter, ""); - command_line::add_arg(desc_options, arg_verbose, ""); - command_line::add_arg(desc_options, arg_stats, ""); - command_line::add_arg(desc_options, arg_loop_multiplier, ""); + command_line::add_arg(desc_options, arg_filter); + command_line::add_arg(desc_options, arg_verbose); + command_line::add_arg(desc_options, arg_stats); + command_line::add_arg(desc_options, arg_loop_multiplier); po::variables_map vm; bool r = command_line::handle_error_helper(desc_options, [&]() diff --git a/tests/unit_tests/main.cpp b/tests/unit_tests/main.cpp index 13b62cbb438..f7251a09e41 100644 --- a/tests/unit_tests/main.cpp +++ b/tests/unit_tests/main.cpp @@ -53,6 +53,8 @@ namespace cryptonote { template class t_cryptonote_protocol_handler data{new char[8 * page_size]}; - epee::mlocker *m0 = new epee::mlocker(BASE(data), 1); - epee::mlocker *m1 = new epee::mlocker(BASE(data) + 2 * page_size, 1); - epee::mlocker *m2 = new epee::mlocker(BASE(data) + 3 * page_size, 1); + std::shared_ptr m0{new epee::mlocker(BASE(data), 1)}; + std::shared_ptr m1{new epee::mlocker(BASE(data) + 2 * page_size, 1)}; + std::shared_ptr m2{new epee::mlocker(BASE(data) + 3 * page_size, 1)}; ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 3); ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 3); - delete m0; - delete m1; - delete m2; + m0 = NULL; + m1 = NULL; + m2 = NULL; ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 0); ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 0); } @@ -65,14 +65,14 @@ TEST(mlocker, distinct_full_page) const size_t base_pages = epee::mlocker::get_num_locked_pages(); const size_t base_objects = epee::mlocker::get_num_locked_objects(); std::unique_ptr data{new char[8 * page_size]}; - epee::mlocker *m0 = new epee::mlocker(BASE(data), page_size); - epee::mlocker *m1 = new epee::mlocker(BASE(data) + 2 * page_size, page_size); - epee::mlocker *m2 = new epee::mlocker(BASE(data) + 3 * page_size, page_size); + std::shared_ptr m0{new epee::mlocker(BASE(data), page_size)}; + std::shared_ptr m1{new epee::mlocker(BASE(data) + 2 * page_size, page_size)}; + std::shared_ptr m2{new epee::mlocker(BASE(data) + 3 * page_size, page_size)}; ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 3); ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 3); - delete m0; - delete m1; - delete m2; + m0 = NULL; + m1 = NULL; + m2 = NULL; ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 0); ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 0); } @@ -84,16 +84,16 @@ TEST(mlocker, identical) const size_t base_pages = epee::mlocker::get_num_locked_pages(); const size_t base_objects = epee::mlocker::get_num_locked_objects(); std::unique_ptr data{new char[8 * page_size]}; - epee::mlocker *m0 = new epee::mlocker(BASE(data) + page_size, 32); - epee::mlocker *m1 = new epee::mlocker(BASE(data) + page_size, 32); - epee::mlocker *m2 = new epee::mlocker(BASE(data) + page_size, 32); + std::shared_ptr m0{new epee::mlocker(BASE(data) + page_size, 32)}; + std::shared_ptr m1{new epee::mlocker(BASE(data) + page_size, 32)}; + std::shared_ptr m2{new epee::mlocker(BASE(data) + page_size, 32)}; ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 1); ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 3); - delete m1; + m1 = NULL; ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 1); ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 2); - delete m0; - delete m2; + m0 = NULL; + m2 = NULL; ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 0); ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 0); } @@ -105,16 +105,16 @@ TEST(mlocker, overlapping_small) const size_t base_pages = epee::mlocker::get_num_locked_pages(); const size_t base_objects = epee::mlocker::get_num_locked_objects(); std::unique_ptr data{new char[8 * page_size]}; - epee::mlocker *m0 = new epee::mlocker(BASE(data), 32); - epee::mlocker *m1 = new epee::mlocker(BASE(data) + 16, 32); - epee::mlocker *m2 = new epee::mlocker(BASE(data) + 8, 32); + std::shared_ptr m0{new epee::mlocker(BASE(data), 32)}; + std::shared_ptr m1{new epee::mlocker(BASE(data) + 16, 32)}; + std::shared_ptr m2{new epee::mlocker(BASE(data) + 8, 32)}; ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 1); ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 3); - delete m1; + m1 = NULL; ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 1); ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 2); - delete m2; - delete m0; + m2 = NULL; + m0 = NULL; ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 0); ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 0); } @@ -126,16 +126,16 @@ TEST(mlocker, multi_page) const size_t base_pages = epee::mlocker::get_num_locked_pages(); const size_t base_objects = epee::mlocker::get_num_locked_objects(); std::unique_ptr data{new char[8 * page_size]}; - epee::mlocker *m0 = new epee::mlocker(BASE(data) + page_size, page_size * 3); + std::shared_ptr m0{new epee::mlocker(BASE(data) + page_size, page_size * 3)}; ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 3); ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 1); - epee::mlocker *m1 = new epee::mlocker(BASE(data) + page_size * 7, page_size); + std::shared_ptr m1{new epee::mlocker(BASE(data) + page_size * 7, page_size)}; ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 4); ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 2); - delete m0; + m0 = NULL; ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 1); ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 1); - delete m1; + m1 = NULL; ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 0); ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 0); } @@ -147,10 +147,10 @@ TEST(mlocker, cross_page) const size_t base_pages = epee::mlocker::get_num_locked_pages(); const size_t base_objects = epee::mlocker::get_num_locked_objects(); std::unique_ptr data{new char[2 * page_size]}; - epee::mlocker *m0 = new epee::mlocker(BASE(data) + page_size - 1, 2); + std::shared_ptr m0{new epee::mlocker(BASE(data) + page_size - 1, 2)}; ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 2); ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 1); - delete m0; + m0 = NULL; ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 0); ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 0); } @@ -158,21 +158,22 @@ TEST(mlocker, cross_page) TEST(mlocker, redundant) { const size_t page_size = epee::mlocker::get_page_size(); + ASSERT_TRUE(page_size > 0); const size_t base_pages = epee::mlocker::get_num_locked_pages(); const size_t base_objects = epee::mlocker::get_num_locked_objects(); std::unique_ptr data{new char[2 * page_size]}; ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 0); ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 0); - epee::mlocker *m0 = new epee::mlocker(BASE(data), 32); + std::shared_ptr m0{new epee::mlocker(BASE(data), 32)}; ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 1); ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 1); - epee::mlocker *m1 = new epee::mlocker(BASE(data), 32); + std::shared_ptr m1{new epee::mlocker(BASE(data), 32)}; ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 1); ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 2); - delete m1; + m1 = NULL; ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 1); ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 1); - delete m0; + m0 = NULL; ASSERT_EQ(epee::mlocker::get_num_locked_pages(), base_pages + 0); ASSERT_EQ(epee::mlocker::get_num_locked_objects(), base_objects + 0); } From 4a2664f743bcbfe47b15e20c22c966a54f91090e Mon Sep 17 00:00:00 2001 From: Riccardo Spagni Date: Sat, 29 Sep 2018 22:17:21 +0200 Subject: [PATCH 0501/1404] Merge pull request #4461 7dd11711 wallet2: fix transfers between subaddresses hitting the sanity check (moneromooo-monero) --- src/wallet/wallet2.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 95474ce34ad..a60874af948 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -1707,10 +1707,14 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote } // remove change sent to the spending subaddress account from the list of received funds + uint64_t sub_change = 0; for (auto i = tx_money_got_in_outs.begin(); i != tx_money_got_in_outs.end();) { if (subaddr_account && i->first.major == *subaddr_account) + { + sub_change += i->second; i = tx_money_got_in_outs.erase(i); + } else ++i; } @@ -1755,7 +1759,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote } } - uint64_t total_received_2 = 0; + uint64_t total_received_2 = sub_change; for (const auto& i : tx_money_got_in_outs) total_received_2 += i.second; if (total_received_1 != total_received_2) From 83d8f03c2338e865ce72f4e2d46d43a48530ab10 Mon Sep 17 00:00:00 2001 From: Riccardo Spagni Date: Sat, 29 Sep 2018 22:18:08 +0200 Subject: [PATCH 0502/1404] Merge pull request #4333 73403004 add --block-notify to monerod and --tx-notify to monero-wallet-{cli,rpc} (moneromooo-monero) --- src/common/CMakeLists.txt | 4 ++ src/common/exec.cpp | 88 +++++++++++++++++++++++++ src/common/exec.h | 36 ++++++++++ src/common/notify.cpp | 61 +++++++++++++++++ src/common/notify.h | 49 ++++++++++++++ src/cryptonote_core/blockchain.cpp | 5 ++ src/cryptonote_core/blockchain.h | 11 ++++ src/cryptonote_core/cryptonote_core.cpp | 17 +++++ src/wallet/wallet2.cpp | 24 +++++++ src/wallet/wallet2.h | 5 ++ 10 files changed, 300 insertions(+) create mode 100644 src/common/exec.cpp create mode 100644 src/common/exec.h create mode 100644 src/common/notify.cpp create mode 100644 src/common/notify.h diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index e89dbbc2476..9b83f149fcc 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -34,9 +34,11 @@ set(common_sources dns_utils.cpp download.cpp error.cpp + exec.cpp expect.cpp util.cpp i18n.cpp + notify.cpp password.cpp perf_timer.cpp threadpool.cpp @@ -58,9 +60,11 @@ set(common_private_headers dns_utils.h download.h error.h + exec.h expect.h http_connection.h int-util.h + notify.h pod-class.h rpc_client.h scoped_message_writer.h diff --git a/src/common/exec.cpp b/src/common/exec.cpp new file mode 100644 index 00000000000..41b8f1378a1 --- /dev/null +++ b/src/common/exec.cpp @@ -0,0 +1,88 @@ +// Copyright (c) 2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include +#include +#include +#include + +#include "misc_log_ex.h" +#include "exec.h" + +namespace tools +{ + +int exec(const char *filename, char * const argv[], bool wait) +{ + pid_t pid = fork(); + if (pid < 0) + { + MERROR("Error forking: " << strerror(errno)); + return -1; + } + + // child + if (pid == 0) + { + char *envp[] = {NULL}; + execve(filename, argv, envp); + MERROR("Failed to execve: " << strerror(errno)); + return -1; + } + + // parent + if (pid > 0) + { + if (!wait) + return 0; + + while (1) + { + int wstatus = 0; + pid_t w = waitpid(pid, &wstatus, WUNTRACED | WCONTINUED); + if (w < 0) { + MERROR("Error waiting for child: " << strerror(errno)); + return -1; + } + if (WIFEXITED(wstatus)) + { + MINFO("Child exited with " << WEXITSTATUS(wstatus)); + return WEXITSTATUS(wstatus); + } + if (WIFSIGNALED(wstatus)) + { + MINFO("Child killed by " << WEXITSTATUS(wstatus)); + return WEXITSTATUS(wstatus); + } + } + } + MERROR("Secret passage found"); + return -1; +} + +} diff --git a/src/common/exec.h b/src/common/exec.h new file mode 100644 index 00000000000..4d2037798dd --- /dev/null +++ b/src/common/exec.h @@ -0,0 +1,36 @@ +// Copyright (c) 2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#pragma once + +namespace tools +{ + +int exec(const char *filename, char * const argv[], bool wait); + +} diff --git a/src/common/notify.cpp b/src/common/notify.cpp new file mode 100644 index 00000000000..b7869ad841b --- /dev/null +++ b/src/common/notify.cpp @@ -0,0 +1,61 @@ +// Copyright (c) 2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include +#include "misc_log_ex.h" +#include "file_io_utils.h" +#include "exec.h" +#include "notify.h" + +namespace tools +{ + +Notify::Notify(const char *spec) +{ + CHECK_AND_ASSERT_THROW_MES(spec, "Null spec"); + + boost::split(args, spec, boost::is_any_of(" ")); + CHECK_AND_ASSERT_THROW_MES(args.size() > 0, "Failed to parse spec"); + filename = args[0]; + CHECK_AND_ASSERT_THROW_MES(epee::file_io_utils::is_file_exist(filename), "File not found: " << filename); +} + +int Notify::notify(const char *parameter) +{ + std::vector margs = args; + for (std::string &s: margs) + boost::replace_all(s, "%s", parameter); + + char **cargs = (char**)alloca(sizeof(char*) * (margs.size() + 1)); + for (size_t n = 0; n < margs.size(); ++n) + cargs[n] = (char*)margs[n].c_str(); + cargs[margs.size()] = NULL; + return tools::exec(filename.c_str(), cargs, false); +} + +} diff --git a/src/common/notify.h b/src/common/notify.h new file mode 100644 index 00000000000..81aacebb068 --- /dev/null +++ b/src/common/notify.h @@ -0,0 +1,49 @@ +// Copyright (c) 2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#pragma once + +#include +#include + +namespace tools +{ + +class Notify +{ +public: + Notify(const char *spec); + + int notify(const char *parameter); + +private: + std::string filename; + std::vector args; +}; + +} diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 0e175cdbe7c..fb4dcef7cff 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -52,6 +52,7 @@ #include "cryptonote_core.h" #include "ringct/rctSigs.h" #include "common/perf_timer.h" +#include "common/notify.h" #if defined(PER_BLOCK_CHECKPOINT) #include "blocks/blocks.h" #endif @@ -3552,6 +3553,10 @@ bool Blockchain::handle_block_to_main_chain(const block& bl, const crypto::hash& get_difficulty_for_next_block(); // just to cache it invalidate_block_template_cache(); + std::shared_ptr block_notify = m_block_notify; + if (block_notify) + block_notify->notify(epee::string_tools::pod_to_hex(id).c_str()); + return true; } //------------------------------------------------------------------ diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index 50ceccd0f76..ab66fac8b8e 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -55,6 +55,8 @@ #include "cryptonote_basic/hardfork.h" #include "blockchain_db/blockchain_db.h" +namespace tools { class Notify; } + namespace cryptonote { class tx_memory_pool; @@ -705,6 +707,13 @@ namespace cryptonote void set_user_options(uint64_t maxthreads, bool sync_on_blocks, uint64_t sync_threshold, blockchain_db_sync_mode sync_mode, bool fast_sync); + /** + * @brief sets a block notify object to call for every new block + * + * @param notify the notify object to cal at every new block + */ + void set_block_notify(const std::shared_ptr ¬ify) { m_block_notify = notify; } + /** * @brief Put DB in safe sync mode */ @@ -1032,6 +1041,8 @@ namespace cryptonote uint64_t m_btc_expected_reward; bool m_btc_valid; + std::shared_ptr m_block_notify; + /** * @brief collects the keys for all outputs being "spent" as an input * diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 3e3d3c19ce3..69e3c708b7a 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -53,6 +53,7 @@ using namespace epee; #include "ringct/rctTypes.h" #include "blockchain_db/blockchain_db.h" #include "ringct/rctSigs.h" +#include "common/notify.h" #include "version.h" #undef MONERO_DEFAULT_LOG_CATEGORY @@ -167,6 +168,11 @@ namespace cryptonote , "Set maximum txpool weight in bytes." , DEFAULT_TXPOOL_MAX_WEIGHT }; + static const command_line::arg_descriptor arg_block_notify = { + "block-notify" + , "Run a program for each new block, '%s' will be replaced by the block hash" + , "" + }; //----------------------------------------------------------------------------------------------- core::core(i_cryptonote_protocol* pprotocol): @@ -276,6 +282,7 @@ namespace cryptonote command_line::add_arg(desc, arg_offline); command_line::add_arg(desc, arg_disable_dns_checkpoints); command_line::add_arg(desc, arg_max_txpool_weight); + command_line::add_arg(desc, arg_block_notify); miner::init_options(desc); BlockchainDB::init_options(desc); @@ -545,6 +552,16 @@ namespace cryptonote m_blockchain_storage.set_user_options(blocks_threads, sync_on_blocks, sync_threshold, sync_mode, fast_sync); + try + { + if (!command_line::is_arg_defaulted(vm, arg_block_notify)) + m_blockchain_storage.set_block_notify(std::shared_ptr(new tools::Notify(command_line::get_arg(vm, arg_block_notify).c_str()))); + } + catch (const std::exception &e) + { + MERROR("Failed to parse block notify spec"); + } + const std::pair regtest_hard_forks[3] = {std::make_pair(1, 0), std::make_pair(Blockchain::get_hard_fork_heights(MAINNET).back().version, 1), std::make_pair(0, 0)}; const cryptonote::test_options regtest_test_options = { regtest_hard_forks diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index a60874af948..299b4afeb4b 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -66,6 +66,7 @@ using namespace epee; #include "memwipe.h" #include "common/base58.h" #include "common/dns_utils.h" +#include "common/notify.h" #include "ringct/rctSigs.h" #include "ringdb.h" @@ -162,6 +163,7 @@ struct options { }; const command_line::arg_descriptor kdf_rounds = {"kdf-rounds", tools::wallet2::tr("Number of rounds for the key derivation function"), 1}; const command_line::arg_descriptor hw_device = {"hw-device", tools::wallet2::tr("HW device to use"), ""}; + const command_line::arg_descriptor tx_notify = { "tx-notify" , "Run a program for each new incoming transaction, '%s' will be replaced by the transaction hash" , "" }; }; void do_prepare_file_names(const std::string& file_path, std::string& keys_file, std::string& wallet_file) @@ -268,6 +270,17 @@ std::unique_ptr make_basic(const boost::program_options::variabl boost::filesystem::path ringdb_path = command_line::get_arg(vm, opts.shared_ringdb_dir); wallet->set_ring_database(ringdb_path.string()); wallet->device_name(device_name); + + try + { + if (!command_line::is_arg_defaulted(vm, opts.tx_notify)) + wallet->set_tx_notify(std::shared_ptr(new tools::Notify(command_line::get_arg(vm, opts.tx_notify).c_str()))); + } + catch (const std::exception &e) + { + MERROR("Failed to parse tx notify spec"); + } + return wallet; } @@ -838,6 +851,7 @@ void wallet2::init_options(boost::program_options::options_description& desc_par command_line::add_arg(desc_params, opts.shared_ringdb_dir); command_line::add_arg(desc_params, opts.kdf_rounds); command_line::add_arg(desc_params, opts.hw_device); + command_line::add_arg(desc_params, opts.tx_notify); } std::unique_ptr wallet2::make_from_json(const boost::program_options::variables_map& vm, bool unattended, const std::string& json_file, const std::function(const char *, bool)> &password_prompter) @@ -1328,6 +1342,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote std::vector outs; std::unordered_map tx_money_got_in_outs; // per receiving subaddress index crypto::public_key tx_pub_key = null_pkey; + bool notify = false; std::vector local_tx_extra_fields; if (tx_cache_data.tx_extra_fields.empty()) @@ -1567,6 +1582,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote m_callback->on_money_received(height, txid, tx, td.m_amount, td.m_subaddr_index); } total_received_1 += amount; + notify = true; } else if (m_transfers[kit->second].m_spent || m_transfers[kit->second].amount() >= tx_scan_info[o].amount) { @@ -1634,6 +1650,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote m_callback->on_money_received(height, txid, tx, td.m_amount, td.m_subaddr_index); } total_received_1 += extra_amount; + notify = true; } } } @@ -1794,6 +1811,13 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote LOG_PRINT_L2("Payment found in " << (pool ? "pool" : "block") << ": " << payment_id << " / " << payment.m_tx_hash << " / " << payment.m_amount); } } + + if (notify) + { + std::shared_ptr tx_notify = m_tx_notify; + if (tx_notify) + tx_notify->notify(epee::string_tools::pod_to_hex(txid).c_str()); + } } //---------------------------------------------------------------------------------------------------- void wallet2::process_unconfirmed(const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t height) diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index db1ac7e429b..497dd486f25 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -68,6 +68,7 @@ namespace tools { class ringdb; class wallet2; + class Notify; class wallet_keys_unlocker { @@ -1176,6 +1177,8 @@ namespace tools void change_password(const std::string &filename, const epee::wipeable_string &original_password, const epee::wipeable_string &new_password); + void set_tx_notify(const std::shared_ptr ¬ify) { m_tx_notify = notify; } + private: /*! * \brief Stores wallet information to wallet file. @@ -1353,6 +1356,8 @@ namespace tools boost::optional m_encrypt_keys_after_refresh; bool m_unattended; + + std::shared_ptr m_tx_notify; }; } BOOST_CLASS_VERSION(tools::wallet2, 25) From 31559e6ad251437f8c1eb2c5624bd19e972d9534 Mon Sep 17 00:00:00 2001 From: iDunk5400 Date: Sat, 29 Sep 2018 23:11:59 +0200 Subject: [PATCH 0503/1404] Update unbound submodule to 7f23967 Fixes building in MSYS2 with openssl 1.1.1 --- external/unbound | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/external/unbound b/external/unbound index d3724dfa553..7f239679547 160000 --- a/external/unbound +++ b/external/unbound @@ -1 +1 @@ -Subproject commit d3724dfa553429d368c27aef160f02f5e8b8075f +Subproject commit 7f23967954736dcaa366806b9eaba7e2bdfede11 From 6da36ea05745f2ca8821d3b1d3e185e0ad0c9c60 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 30 Sep 2018 08:10:30 +0000 Subject: [PATCH 0504/1404] wallet2_api: blackball/unblackball now take two parameters amount and offset (instead of pubkey) --- src/wallet/api/wallet.cpp | 22 ++++++++++++++++++++++ src/wallet/api/wallet.h | 3 ++- src/wallet/api/wallet2_api.h | 5 ++++- 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index 3d4e981ea03..23692834829 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -2146,6 +2146,28 @@ bool WalletImpl::blackballOutputs(const std::vector &outputs, bool return true; } +bool WalletImpl::blackballOutput(const std::string &amount, const std::string &offset) +{ + uint64_t raw_amount, raw_offset; + if (!epee::string_tools::get_xtype_from_string(raw_amount, amount)) + { + setStatusError(tr("Failed to parse output amount")); + return false; + } + if (!epee::string_tools::get_xtype_from_string(raw_offset, offset)) + { + setStatusError(tr("Failed to parse output offset")); + return false; + } + bool ret = m_wallet->blackball_output(std::make_pair(raw_amount, raw_offset)); + if (!ret) + { + setStatusError(tr("Failed to blackball output")); + return false; + } + return true; +} + bool WalletImpl::unblackballOutput(const std::string &amount, const std::string &offset) { uint64_t raw_amount, raw_offset; diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h index 5963a760765..8e2af347dce 100644 --- a/src/wallet/api/wallet.h +++ b/src/wallet/api/wallet.h @@ -182,7 +182,8 @@ class WalletImpl : public Wallet virtual std::string getDefaultDataDir() const override; virtual bool lightWalletLogin(bool &isNewWallet) const override; virtual bool lightWalletImportWalletRequest(std::string &payment_id, uint64_t &fee, bool &new_request, bool &request_fulfilled, std::string &payment_address, std::string &status) override; - virtual bool blackballOutputs(const std::vector &pubkeys, bool add) override; + virtual bool blackballOutputs(const std::vector &outputs, bool add) override; + virtual bool blackballOutput(const std::string &amount, const std::string &offset) override; virtual bool unblackballOutput(const std::string &amount, const std::string &offset) override; virtual bool getRing(const std::string &key_image, std::vector &ring) const override; virtual bool getRings(const std::string &txid, std::vector>> &rings) const override; diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h index e0d49170544..68ea26262f7 100644 --- a/src/wallet/api/wallet2_api.h +++ b/src/wallet/api/wallet2_api.h @@ -881,7 +881,10 @@ struct Wallet virtual bool rescanSpent() = 0; //! blackballs a set of outputs - virtual bool blackballOutputs(const std::vector &pubkeys, bool add) = 0; + virtual bool blackballOutputs(const std::vector &outputs, bool add) = 0; + + //! blackballs an output + virtual bool blackballOutput(const std::string &amount, const std::string &offset) = 0; //! unblackballs an output virtual bool unblackballOutput(const std::string &amount, const std::string &offset) = 0; From fa9e54b6eec065ece76312cc3f1abff2424fdc42 Mon Sep 17 00:00:00 2001 From: xiphon Date: Sun, 30 Sep 2018 02:42:22 +0000 Subject: [PATCH 0505/1404] build: fix gcc false positive 'stringop-overflow' warning --- src/cryptonote_core/cryptonote_tx_utils.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cryptonote_core/cryptonote_tx_utils.cpp b/src/cryptonote_core/cryptonote_tx_utils.cpp index fb2af9ceb45..4bc33b56b0d 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.cpp +++ b/src/cryptonote_core/cryptonote_tx_utils.cpp @@ -127,7 +127,7 @@ namespace cryptonote out_amounts[1] += out_amounts[0]; for (size_t n = 1; n < out_amounts.size(); ++n) out_amounts[n - 1] = out_amounts[n]; - out_amounts.resize(out_amounts.size() - 1); + out_amounts.pop_back(); } } else From 98c922582346850faa315d8d1f95a0a9257ccbc5 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 1 Oct 2018 09:13:22 +0000 Subject: [PATCH 0506/1404] unit_tests: add a notifier test --- tests/unit_tests/CMakeLists.txt | 7 +++- tests/unit_tests/notify.cpp | 57 ++++++++++++++++++++++++++++++ tests/unit_tests/test_notifier.cpp | 54 ++++++++++++++++++++++++++++ 3 files changed, 117 insertions(+), 1 deletion(-) create mode 100644 tests/unit_tests/notify.cpp create mode 100644 tests/unit_tests/test_notifier.cpp diff --git a/tests/unit_tests/CMakeLists.txt b/tests/unit_tests/CMakeLists.txt index d6bcbde4658..e248ed96510 100644 --- a/tests/unit_tests/CMakeLists.txt +++ b/tests/unit_tests/CMakeLists.txt @@ -61,6 +61,7 @@ set(unit_tests_sources mul_div.cpp multiexp.cpp multisig.cpp + notify.cpp parse_amount.cpp random.cpp serialization.cpp @@ -122,4 +123,8 @@ SET_PROPERTY(SOURCE memwipe.cpp PROPERTY COMPILE_FLAGS -Ofast) add_test( NAME unit_tests - COMMAND unit_tests --data-dir "${TEST_DATA_DIR}") + COMMAND unit_tests --data-dir "${TEST_DATA_DIR} --binary-dir ${CMAKE_BINARY_DIR}") + +add_executable(test_notifier test_notifier.cpp) +target_link_libraries(test_notifier ${EXTRA_LIBRARIES}) +set_property(TARGET test_notifier PROPERTY FOLDER "tests") diff --git a/tests/unit_tests/notify.cpp b/tests/unit_tests/notify.cpp new file mode 100644 index 00000000000..d6811c6bd5d --- /dev/null +++ b/tests/unit_tests/notify.cpp @@ -0,0 +1,57 @@ +// Copyright (c) 2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "gtest/gtest.h" + +#include + +#include "misc_language.h" +#include "string_tools.h" +#include "file_io_utils.h" +#include "common/notify.h" + +TEST(notify, works) +{ + char name_template[] = "/tmp/monero-notify-unit-test-XXXXXX"; + int fd = mkstemp(name_template); + ASSERT_TRUE(fd >= 0); + close(fd); + + const std::string spec = epee::string_tools::get_current_module_folder() + "/test_notifier " + name_template + " %s"; + + tools::Notify notify(spec.c_str()); + notify.notify("1111111111111111111111111111111111111111111111111111111111111111"); + + epee::misc_utils::sleep_no_w(100); + + std::string s; + ASSERT_TRUE(epee::file_io_utils::load_file_to_string(name_template, s)); + ASSERT_TRUE(s == "1111111111111111111111111111111111111111111111111111111111111111"); + + boost::filesystem::remove(name_template); +} diff --git a/tests/unit_tests/test_notifier.cpp b/tests/unit_tests/test_notifier.cpp new file mode 100644 index 00000000000..7fd9809c59c --- /dev/null +++ b/tests/unit_tests/test_notifier.cpp @@ -0,0 +1,54 @@ +// Copyright (c) 2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include +#include +#include +#include + +int main(int argc, char **argv) +{ + if (argc < 3) + { + fprintf(stderr, "usage: %s \n", argv[0]); + return 1; + } + const char *filename = argv[1]; + const char *hash = argv[2]; + + FILE *f = fopen(filename, "a+"); + if (!f) + { + fprintf(stderr, "error opening file %s: %s\n", filename, strerror(errno)); + return 1; + } + fprintf(f, "%s", hash); + fclose(f); + + return 0; +} From 25e5890d37a1b243d4b8aadb45743d715bd4d0f9 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 1 Oct 2018 11:18:50 +0000 Subject: [PATCH 0507/1404] wallet: fix --generate-from-json using wrong password --- src/common/password.cpp | 4 ++++ src/common/password.h | 1 + src/simplewallet/simplewallet.cpp | 4 +++- src/wallet/wallet2.cpp | 13 +++++++++---- src/wallet/wallet2.h | 2 +- src/wallet/wallet_rpc_server.cpp | 3 ++- 6 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/common/password.cpp b/src/common/password.cpp index 5671c4a4e9c..b32bedae2aa 100644 --- a/src/common/password.cpp +++ b/src/common/password.cpp @@ -221,6 +221,10 @@ namespace tools : m_password(std::move(password)) { } + password_container::password_container(const epee::wipeable_string& password) noexcept + : m_password(password) + { + } password_container::~password_container() noexcept { diff --git a/src/common/password.h b/src/common/password.h index 529881e4044..beb98283bbf 100644 --- a/src/common/password.h +++ b/src/common/password.h @@ -47,6 +47,7 @@ namespace tools //! `password` is used as password password_container(std::string&& password) noexcept; + password_container(const epee::wipeable_string& password) noexcept; //! \return A password from stdin TTY prompt or `std::cin` pipe. static boost::optional prompt(bool verify, const char *mesage = "Password", bool hide_input = true); diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 7c68de3f316..90f8535d76f 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -3275,7 +3275,9 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) { try { - m_wallet = tools::wallet2::make_from_json(vm, false, m_generate_from_json, password_prompter); + auto rc = tools::wallet2::make_from_json(vm, false, m_generate_from_json, password_prompter); + m_wallet = std::move(rc.first); + password = rc.second.password(); m_wallet_file = m_wallet->path(); } catch (const std::exception &e) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 299b4afeb4b..b657fcd9f2b 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -313,7 +313,7 @@ boost::optional get_password(const boost::program_opt return password_prompter(verify ? tr("Enter a new password for the wallet") : tr("Wallet password"), verify); } -std::unique_ptr generate_from_json(const std::string& json_file, const boost::program_options::variables_map& vm, bool unattended, const options& opts, const std::function(const char *, bool)> &password_prompter) +std::pair, tools::password_container> generate_from_json(const std::string& json_file, const boost::program_options::variables_map& vm, bool unattended, const options& opts, const std::function(const char *, bool)> &password_prompter) { const bool testnet = command_line::get_arg(vm, opts.testnet); const bool stagenet = command_line::get_arg(vm, opts.stagenet); @@ -323,6 +323,7 @@ std::unique_ptr generate_from_json(const std::string& json_file, false. Gcc will coerce this into unique_ptr(nullptr), but clang correctly fails. This large wrapper is for the use of that macro */ std::unique_ptr wallet; + epee::wipeable_string password; const auto do_generate = [&]() -> bool { std::string buf; if (!epee::file_io_utils::load_file_to_string(json_file, buf)) { @@ -460,10 +461,12 @@ std::unique_ptr generate_from_json(const std::string& json_file, if (!field_seed.empty()) { wallet->generate(field_filename, field_password, recovery_key, recover, false, create_address_file); + password = field_password; } else if (field_viewkey.empty() && !field_spendkey.empty()) { wallet->generate(field_filename, field_password, spendkey, recover, false, create_address_file); + password = field_password; } else { @@ -490,6 +493,7 @@ std::unique_ptr generate_from_json(const std::string& json_file, THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("Address must be specified in order to create watch-only wallet")); } wallet->generate(field_filename, field_password, address, viewkey, create_address_file); + password = field_password; } else { @@ -497,6 +501,7 @@ std::unique_ptr generate_from_json(const std::string& json_file, THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("failed to verify spend key secret key")); } wallet->generate(field_filename, field_password, address, spendkey, viewkey, create_address_file); + password = field_password; } } } @@ -509,9 +514,9 @@ std::unique_ptr generate_from_json(const std::string& json_file, if (do_generate()) { - return wallet; + return {std::move(wallet), tools::password_container(password)}; } - return nullptr; + return {nullptr, tools::password_container{}}; } static void throw_on_rpc_response_error(const boost::optional &status, const char *method) @@ -854,7 +859,7 @@ void wallet2::init_options(boost::program_options::options_description& desc_par command_line::add_arg(desc_params, opts.tx_notify); } -std::unique_ptr wallet2::make_from_json(const boost::program_options::variables_map& vm, bool unattended, const std::string& json_file, const std::function(const char *, bool)> &password_prompter) +std::pair, tools::password_container> wallet2::make_from_json(const boost::program_options::variables_map& vm, bool unattended, const std::string& json_file, const std::function(const char *, bool)> &password_prompter) { const options opts{}; return generate_from_json(json_file, vm, unattended, opts, password_prompter); diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 497dd486f25..7857f36f1ff 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -177,7 +177,7 @@ namespace tools static void init_options(boost::program_options::options_description& desc_params); //! Uses stdin and stdout. Returns a wallet2 if no errors. - static std::unique_ptr make_from_json(const boost::program_options::variables_map& vm, bool unattended, const std::string& json_file, const std::function(const char *, bool)> &password_prompter); + static std::pair, password_container> make_from_json(const boost::program_options::variables_map& vm, bool unattended, const std::string& json_file, const std::function(const char *, bool)> &password_prompter); //! Uses stdin and stdout. Returns a wallet2 and password for `wallet_file` if no errors. static std::pair, password_container> diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index 5991e0cc250..8b15359ccf1 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -3352,7 +3352,8 @@ class t_daemon { try { - wal = tools::wallet2::make_from_json(vm, true, from_json, password_prompt); + auto rc = tools::wallet2::make_from_json(vm, true, from_json, password_prompt); + wal = std::move(rc.first); } catch (const std::exception &e) { From 2c74b1a1c42d2763ce9ad773f96309546c9dbbe1 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 1 Oct 2018 14:46:32 +0000 Subject: [PATCH 0508/1404] wallet_rpc_server: include all transfer records for a txid Since subaddresses were added, a tx can now create more than one payment --- src/wallet/wallet_rpc_server.cpp | 22 +++++++++++++------- src/wallet/wallet_rpc_server_commands_defs.h | 4 +++- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index 5991e0cc250..cd519a8159f 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -2173,8 +2173,8 @@ namespace tools for (std::list>::const_iterator i = payments.begin(); i != payments.end(); ++i) { if (i->second.m_tx_hash == txid) { - fill_transfer_entry(res.transfer, i->second.m_tx_hash, i->first, i->second); - return true; + res.transfers.resize(res.transfers.size() + 1); + fill_transfer_entry(res.transfers.back(), i->second.m_tx_hash, i->first, i->second); } } @@ -2183,8 +2183,8 @@ namespace tools for (std::list>::const_iterator i = payments_out.begin(); i != payments_out.end(); ++i) { if (i->first == txid) { - fill_transfer_entry(res.transfer, i->first, i->second); - return true; + res.transfers.resize(res.transfers.size() + 1); + fill_transfer_entry(res.transfers.back(), i->first, i->second); } } @@ -2193,8 +2193,8 @@ namespace tools for (std::list>::const_iterator i = upayments.begin(); i != upayments.end(); ++i) { if (i->first == txid) { - fill_transfer_entry(res.transfer, i->first, i->second); - return true; + res.transfers.resize(res.transfers.size() + 1); + fill_transfer_entry(res.transfers.back(), i->first, i->second); } } @@ -2205,11 +2205,17 @@ namespace tools for (std::list>::const_iterator i = pool_payments.begin(); i != pool_payments.end(); ++i) { if (i->second.m_pd.m_tx_hash == txid) { - fill_transfer_entry(res.transfer, i->first, i->second); - return true; + res.transfers.resize(res.transfers.size() + 1); + fill_transfer_entry(res.transfers.back(), i->first, i->second); } } + if (!res.transfers.empty()) + { + res.transfer = res.transfers.front(); // backward compat + return true; + } + er.code = WALLET_RPC_ERROR_CODE_WRONG_TXID; er.message = "Transaction not found."; return false; diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h index 81ea2292891..e46745339d7 100644 --- a/src/wallet/wallet_rpc_server_commands_defs.h +++ b/src/wallet/wallet_rpc_server_commands_defs.h @@ -47,7 +47,7 @@ // advance which version they will stop working with // Don't go over 32767 for any of these #define WALLET_RPC_VERSION_MAJOR 1 -#define WALLET_RPC_VERSION_MINOR 3 +#define WALLET_RPC_VERSION_MINOR 4 #define MAKE_WALLET_RPC_VERSION(major,minor) (((major)<<16)|(minor)) #define WALLET_RPC_VERSION MAKE_WALLET_RPC_VERSION(WALLET_RPC_VERSION_MAJOR, WALLET_RPC_VERSION_MINOR) namespace tools @@ -1399,9 +1399,11 @@ namespace wallet_rpc struct response { transfer_entry transfer; + std::list transfers; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(transfer); + KV_SERIALIZE(transfers); END_KV_SERIALIZE_MAP() }; }; From 9f3963e8235826704b7bc6ef9e3b90613a72e16c Mon Sep 17 00:00:00 2001 From: naughtyfox Date: Thu, 12 Jul 2018 12:55:52 +0300 Subject: [PATCH 0509/1404] Arbitrary M/N multisig schemes: * support in wallet2 * support in monero-wallet-cli * support in monero-wallet-rpc * support in wallet api * support in monero-gen-trusted-multisig * unit tests for multisig wallets creation --- src/gen_multisig/gen_multisig.cpp | 15 +- src/multisig/multisig.cpp | 44 +- src/multisig/multisig.h | 24 +- src/simplewallet/simplewallet.cpp | 61 ++- src/simplewallet/simplewallet.h | 1 + src/wallet/api/wallet.cpp | 14 + src/wallet/api/wallet.h | 1 + src/wallet/api/wallet2_api.h | 6 + src/wallet/wallet2.cpp | 402 ++++++++++++++----- src/wallet/wallet2.h | 17 + src/wallet/wallet_rpc_server.cpp | 51 ++- src/wallet/wallet_rpc_server.h | 2 + src/wallet/wallet_rpc_server_commands_defs.h | 25 ++ tests/core_tests/chaingen.h | 2 +- tests/unit_tests/multisig.cpp | 179 ++++----- 15 files changed, 625 insertions(+), 219 deletions(-) diff --git a/src/gen_multisig/gen_multisig.cpp b/src/gen_multisig/gen_multisig.cpp index f11f442bc7a..9749ba13fe1 100644 --- a/src/gen_multisig/gen_multisig.cpp +++ b/src/gen_multisig/gen_multisig.cpp @@ -130,8 +130,8 @@ static bool generate_multisig(uint32_t threshold, uint32_t total, const std::str ss << " " << name << std::endl; } - // finalize step if needed - if (!extra_info[0].empty()) + //exchange keys unless exchange_multisig_keys returns no extra info + while (!extra_info[0].empty()) { std::unordered_set pkeys; std::vector signers(total); @@ -145,11 +145,7 @@ static bool generate_multisig(uint32_t threshold, uint32_t total, const std::str } for (size_t n = 0; n < total; ++n) { - if (!wallets[n]->finalize_multisig(pwd_container->password(), pkeys, signers)) - { - tools::fail_msg_writer() << genms::tr("Error finalizing multisig"); - return false; - } + extra_info[n] = wallets[n]->exchange_multisig_keys(pwd_container->password(), pkeys, signers); } } @@ -244,11 +240,6 @@ int main(int argc, char* argv[]) return 1; } - if (threshold != total-1 && threshold != total) - { - tools::fail_msg_writer() << genms::tr("Error: unsupported scheme: only N/N and N-1/N are supported"); - return 1; - } bool create_address_file = command_line::get_arg(*vm, arg_create_address_file); if (!generate_multisig(threshold, total, basename, testnet ? TESTNET : stagenet ? STAGENET : MAINNET, create_address_file)) return 1; diff --git a/src/multisig/multisig.cpp b/src/multisig/multisig.cpp index a0a788b7d18..33d0a312fe9 100644 --- a/src/multisig/multisig.cpp +++ b/src/multisig/multisig.cpp @@ -84,6 +84,43 @@ namespace cryptonote } } //----------------------------------------------------------------- + std::vector generate_multisig_derivations(const account_keys &keys, const std::vector &derivations) + { + std::vector multisig_keys; + crypto::secret_key blinded_skey = get_multisig_blinded_secret_key(keys.m_spend_secret_key); + for (const auto &k: derivations) + { + rct::key d = rct::scalarmultKey(rct::pk2rct(k), rct::sk2rct(blinded_skey)); + multisig_keys.push_back(rct::rct2pk(d)); + } + + return multisig_keys; + } + //----------------------------------------------------------------- + crypto::secret_key calculate_multisig_signer_key(const std::vector& multisig_keys) + { + rct::key secret_key = rct::zero(); + for (const auto &k: multisig_keys) + { + sc_add(secret_key.bytes, secret_key.bytes, (const unsigned char*)k.data); + } + + return rct::rct2sk(secret_key); + } + //----------------------------------------------------------------- + std::vector calculate_multisig_keys(const std::vector& derivations) + { + std::vector multisig_keys; + multisig_keys.reserve(derivations.size()); + + for (const auto &k: derivations) + { + multisig_keys.emplace_back(get_multisig_blinded_secret_key(rct::rct2sk(rct::pk2rct(k)))); + } + + return multisig_keys; + } + //----------------------------------------------------------------- crypto::secret_key generate_multisig_view_secret_key(const crypto::secret_key &skey, const std::vector &skeys) { rct::key view_skey = rct::sk2rct(get_multisig_blinded_secret_key(skey)); @@ -92,7 +129,7 @@ namespace cryptonote return rct::rct2sk(view_skey); } //----------------------------------------------------------------- - crypto::public_key generate_multisig_N1_N_spend_public_key(const std::vector &pkeys) + crypto::public_key generate_multisig_M_N_spend_public_key(const std::vector &pkeys) { rct::key spend_public_key = rct::identity(); for (const auto &pk: pkeys) @@ -141,4 +178,9 @@ namespace cryptonote return true; } //----------------------------------------------------------------- + uint32_t multisig_rounds_required(uint32_t participants, uint32_t threshold) + { + CHECK_AND_ASSERT_THROW_MES(participants >= threshold, "participants must be greater or equal than threshold"); + return participants - threshold + 1; + } } diff --git a/src/multisig/multisig.h b/src/multisig/multisig.h index f95611441b1..93a75681282 100644 --- a/src/multisig/multisig.h +++ b/src/multisig/multisig.h @@ -41,9 +41,31 @@ namespace cryptonote crypto::secret_key get_multisig_blinded_secret_key(const crypto::secret_key &key); void generate_multisig_N_N(const account_keys &keys, const std::vector &spend_keys, std::vector &multisig_keys, rct::key &spend_skey, rct::key &spend_pkey); void generate_multisig_N1_N(const account_keys &keys, const std::vector &spend_keys, std::vector &multisig_keys, rct::key &spend_skey, rct::key &spend_pkey); + /** + * @brief generate_multisig_derivations performs common DH key derivation. + * Each middle round in M/N scheme is DH exchange of public multisig keys of other participants multiplied by secret spend key of current participant. + * this functions does the following: new multisig key = secret spend * public multisig key + * @param keys - current wallet's keys + * @param derivations - public multisig keys of other participants + * @return new public multisig keys derived from previous round. This data needs to be exchange with other participants + */ + std::vector generate_multisig_derivations(const account_keys &keys, const std::vector &derivations); + crypto::secret_key calculate_multisig_signer_key(const std::vector& derivations); + /** + * @brief calculate_multisig_keys. Calculates secret multisig keys from others' participants ones as follows: mi = H(Mi) + * @param derivations - others' participants public multisig keys. + * @return vector of current wallet's multisig secret keys + */ + std::vector calculate_multisig_keys(const std::vector& derivations); crypto::secret_key generate_multisig_view_secret_key(const crypto::secret_key &skey, const std::vector &skeys); - crypto::public_key generate_multisig_N1_N_spend_public_key(const std::vector &pkeys); + /** + * @brief generate_multisig_M_N_spend_public_key calculates multisig wallet's spend public key by summing all of public multisig keys + * @param pkeys unique public multisig keys + * @return multisig wallet's spend public key + */ + crypto::public_key generate_multisig_M_N_spend_public_key(const std::vector &pkeys); bool generate_multisig_key_image(const account_keys &keys, size_t multisig_key_index, const crypto::public_key& out_key, crypto::key_image& ki); void generate_multisig_LR(const crypto::public_key pkey, const crypto::secret_key &k, crypto::public_key &L, crypto::public_key &R); bool generate_multisig_composite_key_image(const account_keys &keys, const std::unordered_map& subaddresses, const crypto::public_key& out_key, const crypto::public_key &tx_public_key, const std::vector& additional_tx_public_keys, size_t real_output_index, const std::vector &pkis, crypto::key_image &ki); + uint32_t multisig_rounds_required(uint32_t participants, uint32_t threshold); } diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 302d2a99915..cf6f64173b9 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -968,7 +968,7 @@ bool simple_wallet::make_multisig(const std::vector &args) { success_msg_writer() << tr("Another step is needed"); success_msg_writer() << multisig_extra_info; - success_msg_writer() << tr("Send this multisig info to all other participants, then use finalize_multisig [...] with others' multisig info"); + success_msg_writer() << tr("Send this multisig info to all other participants, then use exchange_multisig_keys [...] with others' multisig info"); return true; } } @@ -1042,6 +1042,61 @@ bool simple_wallet::finalize_multisig(const std::vector &args) return true; } +bool simple_wallet::exchange_multisig_keys(const std::vector &args) { + bool ready; + if (m_wallet->key_on_device()) + { + fail_msg_writer() << tr("command not supported by HW wallet"); + return true; + } + if (!m_wallet->multisig(&ready)) + { + fail_msg_writer() << tr("This wallet is not multisig"); + return true; + } + if (ready) + { + fail_msg_writer() << tr("This wallet is already finalized"); + return true; + } + + const auto orig_pwd_container = get_and_verify_password(); + if(orig_pwd_container == boost::none) + { + fail_msg_writer() << tr("Your original password was incorrect."); + return true; + } + + if (args.size() < 2) + { + fail_msg_writer() << tr("usage: exchange_multisig_keys [...]"); + return true; + } + + try + { + std::string multisig_extra_info = m_wallet->exchange_multisig_keys(orig_pwd_container->password(), args); + if (!multisig_extra_info.empty()) + { + message_writer() << tr("Another step is needed"); + message_writer() << multisig_extra_info; + message_writer() << tr("Send this multisig info to all other participants, then use exchange_multisig_keys [...] with others' multisig info"); + return true; + } else { + uint32_t threshold, total; + m_wallet->multisig(NULL, &threshold, &total); + success_msg_writer() << tr("Multisig wallet has been successfully created. Current wallet type: ") << threshold << "/" << total; + } + } + catch (const std::exception &e) + { + fail_msg_writer() << tr("Failed to perform multisig keys exchange: ") << e.what(); + return true; + } + + return true; +} + bool simple_wallet::export_multisig(const std::vector &args) { bool ready; @@ -2552,6 +2607,10 @@ simple_wallet::simple_wallet() boost::bind(&simple_wallet::finalize_multisig, this, _1), tr("finalize_multisig [...]"), tr("Turn this wallet into a multisig wallet, extra step for N-1/N wallets")); + m_cmd_binder.set_handler("exchange_multisig_keys", + boost::bind(&simple_wallet::exchange_multisig_keys, this, _1), + tr("exchange_multisig_keys [...]"), + tr("Performs extra multisig keys exchange rounds. Needed for arbitrary M/N multisig wallets")); m_cmd_binder.set_handler("export_multisig_info", boost::bind(&simple_wallet::export_multisig, this, _1), tr("export_multisig_info "), diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index 2d23d6248f9..39b715b7306 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -210,6 +210,7 @@ namespace cryptonote bool prepare_multisig(const std::vector& args); bool make_multisig(const std::vector& args); bool finalize_multisig(const std::vector &args); + bool exchange_multisig_keys(const std::vector &args); bool export_multisig(const std::vector& args); bool import_multisig(const std::vector& args); bool accept_loaded_tx(const tools::wallet2::multisig_tx_set &txs); diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index 5827e4d1a7c..a7fc3dcd72d 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -1187,6 +1187,20 @@ string WalletImpl::makeMultisig(const vector& info, uint32_t threshold) return string(); } +std::string WalletImpl::exchangeMultisigKeys(const std::vector &info) { + try { + clearStatus(); + checkMultisigWalletNotReady(m_wallet); + + return m_wallet->exchange_multisig_keys(epee::wipeable_string(m_password), info); + } catch (const exception& e) { + LOG_ERROR("Error on exchanging multisig keys: ") << e.what(); + setStatusError(string(tr("Failed to make multisig: ")) + e.what()); + } + + return string(); +} + bool WalletImpl::finalizeMultisig(const vector& extraMultisigInfo) { try { clearStatus(); diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h index 64350cee763..8c20af34709 100644 --- a/src/wallet/api/wallet.h +++ b/src/wallet/api/wallet.h @@ -137,6 +137,7 @@ class WalletImpl : public Wallet MultisigState multisig() const override; std::string getMultisigInfo() const override; std::string makeMultisig(const std::vector& info, uint32_t threshold) override; + std::string exchangeMultisigKeys(const std::vector &info) override; bool finalizeMultisig(const std::vector& extraMultisigInfo) override; bool exportMultisigImages(std::string& images) override; size_t importMultisigImages(const std::vector& images) override; diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h index e0d49170544..184c0aa7355 100644 --- a/src/wallet/api/wallet2_api.h +++ b/src/wallet/api/wallet2_api.h @@ -706,6 +706,12 @@ struct Wallet * @return in case of N / N wallets returns empty string since no more key exchanges needed. For N - 1 / N wallets returns base58 encoded extra multisig info */ virtual std::string makeMultisig(const std::vector& info, uint32_t threshold) = 0; + /** + * @brief exchange_multisig_keys - provides additional key exchange round for arbitrary multisig schemes (like N-1/N, M/N) + * @param info - base58 encoded key derivations returned by makeMultisig or exchangeMultisigKeys function call + * @return new info string if more rounds required or an empty string if wallet creation is done + */ + virtual std::string exchangeMultisigKeys(const std::vector &info) = 0; /** * @brief finalizeMultisig - finalizes N - 1 / N multisig wallets creation * @param extraMultisigInfo - wallet participants' extra multisig info obtained with makeMultisig call diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 75178845a88..841c5c2ab2d 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -121,6 +121,7 @@ using namespace cryptonote; #define FIRST_REFRESH_GRANULARITY 1024 static const std::string MULTISIG_SIGNATURE_MAGIC = "SigMultisigPkV1"; +static const std::string MULTISIG_EXTRA_INFO_MAGIC = "MultisigxV1"; namespace { @@ -132,6 +133,42 @@ namespace dir /= ".shared-ringdb"; return dir.string(); } + + std::string pack_multisignature_keys(const std::string& prefix, const std::vector& keys, const crypto::secret_key& signer_secret_key) + { + std::string data; + crypto::public_key signer; + CHECK_AND_ASSERT_THROW_MES(crypto::secret_key_to_public_key(signer_secret_key, signer), "Failed to derive public spend key"); + data += std::string((const char *)&signer, sizeof(crypto::public_key)); + + for (const auto &key: keys) + { + data += std::string((const char *)&key, sizeof(crypto::public_key)); + } + + data.resize(data.size() + sizeof(crypto::signature)); + + crypto::hash hash; + crypto::cn_fast_hash(data.data(), data.size() - sizeof(crypto::signature), hash); + crypto::signature &signature = *(crypto::signature*)&data[data.size() - sizeof(crypto::signature)]; + crypto::generate_signature(hash, signer, signer_secret_key, signature); + + return MULTISIG_EXTRA_INFO_MAGIC + tools::base58::encode(data); + } + + std::vector secret_keys_to_public_keys(const std::vector& keys) + { + std::vector public_keys; + public_keys.reserve(keys.size()); + + std::transform(keys.begin(), keys.end(), std::back_inserter(public_keys), [] (const crypto::secret_key& k) -> crypto::public_key { + crypto::public_key p; + CHECK_AND_ASSERT_THROW_MES(crypto::secret_key_to_public_key(k, p), "Failed to derive public spend key"); + return p; + }); + + return public_keys; + } } namespace @@ -758,6 +795,7 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended): m_callback(0), m_trusted_daemon(false), m_nettype(nettype), + m_multisig_rounds_passed(0), m_always_confirm_transfers(true), m_print_ring_members(false), m_store_tx_info(true), @@ -2885,6 +2923,7 @@ bool wallet2::clear() m_address_book.clear(); m_subaddresses.clear(); m_subaddress_labels.clear(); + m_multisig_rounds_passed = 0; return true; } @@ -2899,6 +2938,7 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable { std::string account_data; std::string multisig_signers; + std::string multisig_derivations; cryptonote::account_base account = m_account; crypto::chacha_key key; @@ -2951,6 +2991,14 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable CHECK_AND_ASSERT_MES(r, false, "failed to serialize wallet multisig signers"); value.SetString(multisig_signers.c_str(), multisig_signers.length()); json.AddMember("multisig_signers", value, json.GetAllocator()); + + r = ::serialization::dump_binary(m_multisig_derivations, multisig_derivations); + CHECK_AND_ASSERT_MES(r, false, "failed to serialize wallet multisig derivations"); + value.SetString(multisig_derivations.c_str(), multisig_derivations.length()); + json.AddMember("multisig_derivations", value, json.GetAllocator()); + + value2.SetUint(m_multisig_rounds_passed); + json.AddMember("multisig_rounds_passed", value2, json.GetAllocator()); } value2.SetInt(m_always_confirm_transfers ? 1 :0); @@ -3123,6 +3171,8 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_ m_multisig = false; m_multisig_threshold = 0; m_multisig_signers.clear(); + m_multisig_rounds_passed = 0; + m_multisig_derivations.clear(); m_always_confirm_transfers = false; m_print_ring_members = false; m_default_mixin = 0; @@ -3181,6 +3231,8 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_ m_multisig = field_multisig; GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, multisig_threshold, unsigned int, Uint, m_multisig, 0); m_multisig_threshold = field_multisig_threshold; + GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, multisig_rounds_passed, unsigned int, Uint, false, 0); + m_multisig_rounds_passed = field_multisig_rounds_passed; if (m_multisig) { if (!json.HasMember("multisig_signers")) @@ -3201,6 +3253,24 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_ LOG_ERROR("Field multisig_signers found in JSON, but failed to parse"); return false; } + + //previous version of multisig does not have this field + if (json.HasMember("multisig_derivations")) + { + if (!json["multisig_derivations"].IsString()) + { + LOG_ERROR("Field multisig_derivations found in JSON, but not String"); + return false; + } + const char *field_multisig_derivations = json["multisig_derivations"].GetString(); + std::string multisig_derivations = std::string(field_multisig_derivations, field_multisig_derivations + json["multisig_derivations"].GetStringLength()); + r = ::serialization::parse_binary(multisig_derivations, m_multisig_derivations); + if (!r) + { + LOG_ERROR("Field multisig_derivations found in JSON, but failed to parse"); + return false; + } + } } GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, always_confirm_transfers, int, Int, false, true); m_always_confirm_transfers = field_always_confirm_transfers; @@ -3829,12 +3899,12 @@ std::string wallet2::make_multisig(const epee::wipeable_string &password, CHECK_AND_ASSERT_THROW_MES(!view_keys.empty(), "empty view keys"); CHECK_AND_ASSERT_THROW_MES(view_keys.size() == spend_keys.size(), "Mismatched view/spend key sizes"); CHECK_AND_ASSERT_THROW_MES(threshold > 1 && threshold <= spend_keys.size() + 1, "Invalid threshold"); - CHECK_AND_ASSERT_THROW_MES(threshold == spend_keys.size() || threshold == spend_keys.size() + 1, "Unsupported threshold case"); std::string extra_multisig_info; - crypto::hash hash; - - clear(); + std::vector multisig_keys; + rct::key spend_pkey = rct::identity(); + rct::key spend_skey; + std::vector multisig_signers; // decrypt keys epee::misc_utils::auto_scope_leave_caller keys_reencryptor; @@ -3847,43 +3917,78 @@ std::string wallet2::make_multisig(const epee::wipeable_string &password, keys_reencryptor = epee::misc_utils::create_scope_leave_handler([&, this, chacha_key]() { m_account.encrypt_keys(chacha_key); m_account.decrypt_viewkey(chacha_key); }); } - MINFO("Creating spend key..."); - std::vector multisig_keys; - rct::key spend_pkey, spend_skey; + // In common multisig scheme there are 4 types of key exchange rounds: + // 1. First round is exchange of view secret keys and public spend keys. + // 2. Middle round is exchange of derivations: Ki = b * Mj, where b - spend secret key, + // M - public multisig key (in first round it equals to public spend key), K - new public multisig key. + // 3. Secret spend establishment round sets your secret multisig keys as follows: kl = H(Ml), where M - is *your* public multisig key, + // k - secret multisig key used to sign transactions. k and M are sets of keys, of course. + // And secret spend key as the sum of all participant's secret multisig keys + // 4. Last round establishes multisig wallet's public spend key. Participants exchange their public multisig keys + // and calculate common spend public key as sum of all unique participants' public multisig keys. + // Note that N/N scheme has only first round. N-1/N has 2 rounds: first and last. Common M/N has all 4 rounds. + + // IMPORTANT: wallet's public spend key is not equal to secret_spend_key * G! + // Wallet's public spend key is the sum of unique public multisig keys of all participants. + // secret_spend_key * G = public signer key + if (threshold == spend_keys.size() + 1) { + // In N / N case we only need to do one round and calculate secret multisig keys and new secret spend key + MINFO("Creating spend key..."); + + // Calculates all multisig keys and spend key cryptonote::generate_multisig_N_N(get_account().get_keys(), spend_keys, multisig_keys, spend_skey, spend_pkey); + + // Our signer key is b * G, where b is secret spend key. + multisig_signers = spend_keys; + multisig_signers.push_back(get_multisig_signer_public_key(get_account().get_keys().m_spend_secret_key)); } - else if (threshold == spend_keys.size()) + else { - cryptonote::generate_multisig_N1_N(get_account().get_keys(), spend_keys, multisig_keys, spend_skey, spend_pkey); + // We just got public spend keys of all participants and deriving multisig keys (set of Mi = b * Bi). + // note that derivations are public keys as DH exchange suppose it to be + auto derivations = cryptonote::generate_multisig_derivations(get_account().get_keys(), spend_keys); - // We need an extra step, so we package all the composite public keys - // we know about, and make a signed string out of them - std::string data; - crypto::public_key signer; - CHECK_AND_ASSERT_THROW_MES(crypto::secret_key_to_public_key(rct::rct2sk(spend_skey), signer), "Failed to derive public spend key"); - data += std::string((const char *)&signer, sizeof(crypto::public_key)); + spend_pkey = rct::identity(); + multisig_signers = std::vector(spend_keys.size() + 1, crypto::null_pkey); - for (const auto &msk: multisig_keys) + if (threshold == spend_keys.size()) { - rct::key pmsk = rct::scalarmultBase(rct::sk2rct(msk)); - data += std::string((const char *)&pmsk, sizeof(crypto::public_key)); + // N - 1 / N case + + // We need an extra step, so we package all the composite public keys + // we know about, and make a signed string out of them + MINFO("Creating spend key..."); + + // Calculating set of our secret multisig keys as follows: mi = H(Mi), + // where mi - secret multisig key, Mi - others' participants public multisig key + multisig_keys = cryptonote::calculate_multisig_keys(derivations); + + // calculating current participant's spend secret key as sum of all secret multisig keys for current participant. + // IMPORTANT: participant's secret spend key is not an entire wallet's secret spend! + // Entire wallet's secret spend is sum of all unique secret multisig keys + // among all of participants and is not held by anyone! + spend_skey = rct::sk2rct(cryptonote::calculate_multisig_signer_key(multisig_keys)); + + // Preparing data for the last round to calculate common public spend key. The data contains public multisig keys. + extra_multisig_info = pack_multisignature_keys(MULTISIG_EXTRA_INFO_MAGIC, secret_keys_to_public_keys(multisig_keys), rct::rct2sk(spend_skey)); } + else + { + // M / N case + MINFO("Preparing keys for next exchange round..."); - data.resize(data.size() + sizeof(crypto::signature)); - crypto::cn_fast_hash(data.data(), data.size() - sizeof(signature), hash); - crypto::signature &signature = *(crypto::signature*)&data[data.size() - sizeof(crypto::signature)]; - crypto::generate_signature(hash, signer, rct::rct2sk(spend_skey), signature); + // Preparing data for middle round - packing new public multisig keys to exchage with others. + extra_multisig_info = pack_multisignature_keys(MULTISIG_EXTRA_INFO_MAGIC, derivations, m_account.get_keys().m_spend_secret_key); + spend_skey = rct::sk2rct(m_account.get_keys().m_spend_secret_key); - extra_multisig_info = std::string("MultisigxV1") + tools::base58::encode(data); - } - else - { - CHECK_AND_ASSERT_THROW_MES(false, "Unsupported threshold case"); + // Need to store middle keys to be able to proceed in case of wallet shutdown. + m_multisig_derivations = derivations; + } } - // the multisig view key is shared by all, make one all can derive + clear(); MINFO("Creating view key..."); crypto::secret_key view_skey = cryptonote::generate_multisig_view_secret_key(get_account().get_keys().m_view_secret_key, view_keys); @@ -3895,18 +4000,10 @@ std::string wallet2::make_multisig(const epee::wipeable_string &password, m_account_public_address = m_account.get_keys().m_account_address; m_watch_only = false; m_multisig = true; - m_multisig_threshold = threshold; m_key_device_type = hw::device::device_type::SOFTWARE; - - if (threshold == spend_keys.size() + 1) - { - m_multisig_signers = spend_keys; - m_multisig_signers.push_back(get_multisig_signer_public_key()); - } - else - { - m_multisig_signers = std::vector(spend_keys.size() + 1, crypto::null_pkey); - } + m_multisig_threshold = threshold; + m_multisig_signers = multisig_signers; + ++m_multisig_rounds_passed; // re-encrypt keys keys_reencryptor = epee::misc_utils::auto_scope_leave_caller(); @@ -3921,13 +4018,147 @@ std::string wallet2::make_multisig(const epee::wipeable_string &password, return extra_multisig_info; } -std::string wallet2::make_multisig(const epee::wipeable_string &password, - const std::vector &info, - uint32_t threshold) +std::string wallet2::exchange_multisig_keys(const epee::wipeable_string &password, + const std::vector &info) +{ + THROW_WALLET_EXCEPTION_IF(info.empty(), + error::wallet_internal_error, "Empty multisig info"); + + if (info[0].substr(0, MULTISIG_EXTRA_INFO_MAGIC.size()) != MULTISIG_EXTRA_INFO_MAGIC) + { + THROW_WALLET_EXCEPTION_IF(false, + error::wallet_internal_error, "Unsupported info string"); + } + + std::vector signers; + std::unordered_set pkeys; + + THROW_WALLET_EXCEPTION_IF(!unpack_extra_multisig_info(info, signers, pkeys), + error::wallet_internal_error, "Bad extra multisig info"); + + return exchange_multisig_keys(password, pkeys, signers); +} + +std::string wallet2::exchange_multisig_keys(const epee::wipeable_string &password, + std::unordered_set derivations, + std::vector signers) +{ + CHECK_AND_ASSERT_THROW_MES(!derivations.empty(), "empty pkeys"); + CHECK_AND_ASSERT_THROW_MES(!signers.empty(), "empty signers"); + + bool ready = false; + CHECK_AND_ASSERT_THROW_MES(multisig(&ready), "The wallet is not multisig"); + CHECK_AND_ASSERT_THROW_MES(!ready, "Multisig wallet creation process has already been finished"); + + // keys are decrypted + epee::misc_utils::auto_scope_leave_caller keys_reencryptor; + if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only) + { + crypto::chacha_key chacha_key; + crypto::generate_chacha_key(password.data(), password.size(), chacha_key, m_kdf_rounds); + m_account.encrypt_viewkey(chacha_key); + m_account.decrypt_keys(chacha_key); + keys_reencryptor = epee::misc_utils::create_scope_leave_handler([&, this, chacha_key]() { m_account.encrypt_keys(chacha_key); m_account.decrypt_viewkey(chacha_key); }); + } + + if (m_multisig_rounds_passed == multisig_rounds_required(m_multisig_signers.size(), m_multisig_threshold) - 1) + { + // the last round is passed and we have to calculate spend public key + // add ours if not included + crypto::public_key local_signer = get_multisig_signer_public_key(); + + if (std::find(signers.begin(), signers.end(), local_signer) == signers.end()) + { + signers.push_back(local_signer); + for (const auto &msk: get_account().get_multisig_keys()) + { + derivations.insert(rct::rct2pk(rct::scalarmultBase(rct::sk2rct(msk)))); + } + } + + CHECK_AND_ASSERT_THROW_MES(signers.size() == m_multisig_signers.size(), "Bad signers size"); + + // Summing all of unique public multisig keys to calculate common public spend key + crypto::public_key spend_public_key = cryptonote::generate_multisig_M_N_spend_public_key(std::vector(derivations.begin(), derivations.end())); + m_account_public_address.m_spend_public_key = spend_public_key; + m_account.finalize_multisig(spend_public_key); + + m_multisig_signers = signers; + std::sort(m_multisig_signers.begin(), m_multisig_signers.end(), [](const crypto::public_key &e0, const crypto::public_key &e1){ return memcmp(&e0, &e1, sizeof(e0)); }); + + ++m_multisig_rounds_passed; + m_multisig_derivations.clear(); + + // keys are encrypted again + keys_reencryptor = epee::misc_utils::auto_scope_leave_caller(); + + if (!m_wallet_file.empty()) + { + bool r = store_keys(m_keys_file, password, false); + THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, m_keys_file); + + if (boost::filesystem::exists(m_wallet_file + ".address.txt")) + { + r = file_io_utils::save_string_to_file(m_wallet_file + ".address.txt", m_account.get_public_address_str(m_nettype)); + if(!r) MERROR("String with address text not saved"); + } + } + + m_subaddresses.clear(); + m_subaddress_labels.clear(); + add_subaddress_account(tr("Primary account")); + + if (!m_wallet_file.empty()) + store(); + + return {}; + } + + // Below are either middle or secret spend key establishment rounds + + for (const auto& key: m_multisig_derivations) + derivations.erase(key); + + // Deriving multisig keys (set of Mi = b * Bi) according to DH from other participants' multisig keys. + auto new_derivations = cryptonote::generate_multisig_derivations(get_account().get_keys(), std::vector(derivations.begin(), derivations.end())); + + std::string extra_multisig_info; + if (m_multisig_rounds_passed == multisig_rounds_required(m_multisig_signers.size(), m_multisig_threshold) - 2) // next round is last + { + // Next round is last therefore we are performing secret spend establishment round as described above. + MINFO("Creating spend key..."); + + // Calculating our secret multisig keys by hashing our public multisig keys. + auto multisig_keys = cryptonote::calculate_multisig_keys(std::vector(new_derivations.begin(), new_derivations.end())); + // And summing it to get personal secret spend key + crypto::secret_key spend_skey = cryptonote::calculate_multisig_signer_key(multisig_keys); + + m_account.make_multisig(m_account.get_keys().m_view_secret_key, spend_skey, rct::rct2pk(rct::identity()), multisig_keys); + + // Packing public multisig keys to exchange with others and calculate common public spend key in the last round + extra_multisig_info = pack_multisignature_keys(MULTISIG_EXTRA_INFO_MAGIC, secret_keys_to_public_keys(multisig_keys), spend_skey); + } + else + { + // This is just middle round + MINFO("Preparing keys for next exchange round..."); + extra_multisig_info = pack_multisignature_keys(MULTISIG_EXTRA_INFO_MAGIC, new_derivations, m_account.get_keys().m_spend_secret_key); + m_multisig_derivations = new_derivations; + } + + ++m_multisig_rounds_passed; + + create_keys_file(m_wallet_file, false, password, boost::filesystem::exists(m_wallet_file + ".address.txt")); + return extra_multisig_info; +} + +void wallet2::unpack_multisig_info(const std::vector& info, + std::vector &public_keys, + std::vector &secret_keys) const { // parse all multisig info - std::vector secret_keys(info.size()); - std::vector public_keys(info.size()); + public_keys.resize(info.size()); + secret_keys.resize(info.size()); for (size_t i = 0; i < info.size(); ++i) { THROW_WALLET_EXCEPTION_IF(!verify_multisig_info(info[i], secret_keys[i], public_keys[i]), @@ -3971,75 +4202,51 @@ std::string wallet2::make_multisig(const epee::wipeable_string &password, "Found local spend public key, but not local view secret key - something very weird"); } } +} +std::string wallet2::make_multisig(const epee::wipeable_string &password, + const std::vector &info, + uint32_t threshold) +{ + std::vector secret_keys(info.size()); + std::vector public_keys(info.size()); + unpack_multisig_info(info, public_keys, secret_keys); return make_multisig(password, secret_keys, public_keys, threshold); } bool wallet2::finalize_multisig(const epee::wipeable_string &password, std::unordered_set pkeys, std::vector signers) { - CHECK_AND_ASSERT_THROW_MES(!pkeys.empty(), "empty pkeys"); - - // keys are decrypted - epee::misc_utils::auto_scope_leave_caller keys_reencryptor; - if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only) - { - crypto::chacha_key chacha_key; - crypto::generate_chacha_key(password.data(), password.size(), chacha_key, m_kdf_rounds); - m_account.encrypt_viewkey(chacha_key); - m_account.decrypt_keys(chacha_key); - keys_reencryptor = epee::misc_utils::create_scope_leave_handler([&, this, chacha_key]() { m_account.encrypt_keys(chacha_key); m_account.decrypt_viewkey(chacha_key); }); - } + exchange_multisig_keys(password, pkeys, signers); + return true; +} - // add ours if not included - crypto::public_key local_signer; - CHECK_AND_ASSERT_THROW_MES(crypto::secret_key_to_public_key(get_account().get_keys().m_spend_secret_key, local_signer), - "Failed to derive public spend key"); - if (std::find(signers.begin(), signers.end(), local_signer) == signers.end()) +bool wallet2::unpack_extra_multisig_info(const std::vector& info, + std::vector &signers, + std::unordered_set &pkeys) const +{ + // parse all multisig info + signers.resize(info.size(), crypto::null_pkey); + for (size_t i = 0; i < info.size(); ++i) { - signers.push_back(local_signer); - for (const auto &msk: get_account().get_multisig_keys()) - { - pkeys.insert(rct::rct2pk(rct::scalarmultBase(rct::sk2rct(msk)))); - } + if (!verify_extra_multisig_info(info[i], pkeys, signers[i])) + { + return false; + } } - CHECK_AND_ASSERT_THROW_MES(signers.size() == m_multisig_signers.size(), "Bad signers size"); - - crypto::public_key spend_public_key = cryptonote::generate_multisig_N1_N_spend_public_key(std::vector(pkeys.begin(), pkeys.end())); - m_account_public_address.m_spend_public_key = spend_public_key; - m_account.finalize_multisig(spend_public_key); - - m_multisig_signers = signers; - std::sort(m_multisig_signers.begin(), m_multisig_signers.end(), [](const crypto::public_key &e0, const crypto::public_key &e1){ return memcmp(&e0, &e1, sizeof(e0)); }); - - // keys are encrypted again - keys_reencryptor = epee::misc_utils::auto_scope_leave_caller(); - - create_keys_file(m_wallet_file, false, password, boost::filesystem::exists(m_wallet_file + ".address.txt")); - - m_subaddresses.clear(); - m_subaddress_labels.clear(); - add_subaddress_account(tr("Primary account")); - - if (!m_wallet_file.empty()) - store(); - return true; } bool wallet2::finalize_multisig(const epee::wipeable_string &password, const std::vector &info) { - // parse all multisig info std::unordered_set public_keys; - std::vector signers(info.size(), crypto::null_pkey); - for (size_t i = 0; i < info.size(); ++i) + std::vector signers; + if (!unpack_extra_multisig_info(info, signers, public_keys)) { - if (!verify_extra_multisig_info(info[i], public_keys, signers[i])) - { - MERROR("Bad multisig info"); - return false; - } + MERROR("Bad multisig info"); + return false; } + return finalize_multisig(password, public_keys, signers); } @@ -4102,14 +4309,13 @@ bool wallet2::verify_multisig_info(const std::string &data, crypto::secret_key & bool wallet2::verify_extra_multisig_info(const std::string &data, std::unordered_set &pkeys, crypto::public_key &signer) { - const size_t header_len = strlen("MultisigxV1"); - if (data.size() < header_len || data.substr(0, header_len) != "MultisigxV1") + if (data.size() < MULTISIG_EXTRA_INFO_MAGIC.size() || data.substr(0, MULTISIG_EXTRA_INFO_MAGIC.size()) != MULTISIG_EXTRA_INFO_MAGIC) { MERROR("Multisig info header check error"); return false; } std::string decoded; - if (!tools::base58::decode(data.substr(header_len), decoded)) + if (!tools::base58::decode(data.substr(MULTISIG_EXTRA_INFO_MAGIC.size()), decoded)) { MERROR("Multisig info decoding error"); return false; diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index acbc09c3622..440ac709bc8 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -573,6 +573,14 @@ namespace tools const std::vector &view_keys, const std::vector &spend_keys, uint32_t threshold); + std::string exchange_multisig_keys(const epee::wipeable_string &password, + const std::vector &info); + /*! + * \brief Any but first round of keys exchange + */ + std::string exchange_multisig_keys(const epee::wipeable_string &password, + std::unordered_set pkeys, + std::vector signers); /*! * \brief Finalizes creation of a multisig wallet */ @@ -1245,6 +1253,12 @@ namespace tools bool get_rct_distribution(uint64_t &start_height, std::vector &distribution); uint64_t get_segregation_fork_height() const; + void unpack_multisig_info(const std::vector& info, + std::vector &public_keys, + std::vector &secret_keys) const; + bool unpack_extra_multisig_info(const std::vector& info, + std::vector &signers, + std::unordered_set &pkeys) const; void cache_tx_data(const cryptonote::transaction& tx, const crypto::hash &txid, tx_cache_data &tx_cache_data) const; @@ -1295,6 +1309,9 @@ namespace tools bool m_multisig; /*!< if > 1 spend secret key will not match spend public key */ uint32_t m_multisig_threshold; std::vector m_multisig_signers; + //in case of general M/N multisig wallet we should perform N - M + 1 key exchange rounds and remember how many rounds are passed. + uint32_t m_multisig_rounds_passed; + std::vector m_multisig_derivations; bool m_always_confirm_transfers; bool m_print_ring_members; bool m_store_tx_info; /*!< request txkey to be returned in RPC, and store in the wallet cache file */ diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index e6eb64d12b6..b8379448d47 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -3119,7 +3119,7 @@ namespace tools return false; } - if (req.multisig_info.size() < threshold - 1) + if (req.multisig_info.size() < 1 || req.multisig_info.size() > total) { er.code = WALLET_RPC_ERROR_CODE_THRESHOLD_NOT_REACHED; er.message = "Needs multisig info from more participants"; @@ -3146,6 +3146,55 @@ namespace tools return true; } //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::on_exchange_multisig_keys(const wallet_rpc::COMMAND_RPC_EXCHANGE_MULTISIG_KEYS::request& req, wallet_rpc::COMMAND_RPC_EXCHANGE_MULTISIG_KEYS::response& res, epee::json_rpc::error& er) + { + if (!m_wallet) return not_open(er); + if (m_restricted) + { + er.code = WALLET_RPC_ERROR_CODE_DENIED; + er.message = "Command unavailable in restricted mode."; + return false; + } + bool ready; + uint32_t threshold, total; + if (!m_wallet->multisig(&ready, &threshold, &total)) + { + er.code = WALLET_RPC_ERROR_CODE_NOT_MULTISIG; + er.message = "This wallet is not multisig"; + return false; + } + + if (ready) + { + er.code = WALLET_RPC_ERROR_CODE_ALREADY_MULTISIG; + er.message = "This wallet is multisig, and already finalized"; + return false; + } + + if (req.multisig_info.size() < 1 || req.multisig_info.size() > total) + { + er.code = WALLET_RPC_ERROR_CODE_THRESHOLD_NOT_REACHED; + er.message = "Needs multisig info from more participants"; + return false; + } + + try + { + res.multisig_info = m_wallet->exchange_multisig_keys(req.password, req.multisig_info); + if (res.multisig_info.empty()) + { + res.address = m_wallet->get_account().get_public_address_str(m_wallet->nettype()); + } + } + catch (const std::exception &e) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = std::string("Error calling exchange_multisig_info: ") + e.what(); + return false; + } + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_sign_multisig(const wallet_rpc::COMMAND_RPC_SIGN_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_SIGN_MULTISIG::response& res, epee::json_rpc::error& er) { if (!m_wallet) return not_open(er); diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h index ab7917a7810..ab896aa3ba3 100644 --- a/src/wallet/wallet_rpc_server.h +++ b/src/wallet/wallet_rpc_server.h @@ -141,6 +141,7 @@ namespace tools MAP_JON_RPC_WE("export_multisig_info", on_export_multisig, wallet_rpc::COMMAND_RPC_EXPORT_MULTISIG) MAP_JON_RPC_WE("import_multisig_info", on_import_multisig, wallet_rpc::COMMAND_RPC_IMPORT_MULTISIG) MAP_JON_RPC_WE("finalize_multisig", on_finalize_multisig, wallet_rpc::COMMAND_RPC_FINALIZE_MULTISIG) + MAP_JON_RPC_WE("exchange_multisig_keys", on_exchange_multisig_keys, wallet_rpc::COMMAND_RPC_EXCHANGE_MULTISIG_KEYS) MAP_JON_RPC_WE("sign_multisig", on_sign_multisig, wallet_rpc::COMMAND_RPC_SIGN_MULTISIG) MAP_JON_RPC_WE("submit_multisig", on_submit_multisig, wallet_rpc::COMMAND_RPC_SUBMIT_MULTISIG) MAP_JON_RPC_WE("get_version", on_get_version, wallet_rpc::COMMAND_RPC_GET_VERSION) @@ -218,6 +219,7 @@ namespace tools bool on_export_multisig(const wallet_rpc::COMMAND_RPC_EXPORT_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_EXPORT_MULTISIG::response& res, epee::json_rpc::error& er); bool on_import_multisig(const wallet_rpc::COMMAND_RPC_IMPORT_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_IMPORT_MULTISIG::response& res, epee::json_rpc::error& er); bool on_finalize_multisig(const wallet_rpc::COMMAND_RPC_FINALIZE_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_FINALIZE_MULTISIG::response& res, epee::json_rpc::error& er); + bool on_exchange_multisig_keys(const wallet_rpc::COMMAND_RPC_EXCHANGE_MULTISIG_KEYS::request& req, wallet_rpc::COMMAND_RPC_EXCHANGE_MULTISIG_KEYS::response& res, epee::json_rpc::error& er); bool on_sign_multisig(const wallet_rpc::COMMAND_RPC_SIGN_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_SIGN_MULTISIG::response& res, epee::json_rpc::error& er); bool on_submit_multisig(const wallet_rpc::COMMAND_RPC_SUBMIT_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_SUBMIT_MULTISIG::response& res, epee::json_rpc::error& er); bool on_get_version(const wallet_rpc::COMMAND_RPC_GET_VERSION::request& req, wallet_rpc::COMMAND_RPC_GET_VERSION::response& res, epee::json_rpc::error& er); diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h index 2168e0f7124..c769929f570 100644 --- a/src/wallet/wallet_rpc_server_commands_defs.h +++ b/src/wallet/wallet_rpc_server_commands_defs.h @@ -1990,6 +1990,31 @@ namespace wallet_rpc }; }; + struct COMMAND_RPC_EXCHANGE_MULTISIG_KEYS + { + struct request + { + std::string password; + std::vector multisig_info; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(password) + KV_SERIALIZE(multisig_info) + END_KV_SERIALIZE_MAP() + }; + + struct response + { + std::string address; + std::string multisig_info; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(address) + KV_SERIALIZE(multisig_info) + END_KV_SERIALIZE_MAP() + }; + }; + struct COMMAND_RPC_SIGN_MULTISIG { struct request diff --git a/tests/core_tests/chaingen.h b/tests/core_tests/chaingen.h index 34b205ae58c..c9e1640aa62 100644 --- a/tests/core_tests/chaingen.h +++ b/tests/core_tests/chaingen.h @@ -593,7 +593,7 @@ inline bool do_replay_file(const std::string& filename) std::vector spend_public_keys; \ for (const auto &k: all_multisig_keys) \ spend_public_keys.push_back(k); \ - crypto::public_key spend_pkey = cryptonote::generate_multisig_N1_N_spend_public_key(spend_public_keys); \ + crypto::public_key spend_pkey = cryptonote::generate_multisig_M_N_spend_public_key(spend_public_keys); \ for (size_t msidx = 0; msidx < total; ++msidx) \ account[msidx].finalize_multisig(spend_pkey); \ } \ diff --git a/tests/unit_tests/multisig.cpp b/tests/unit_tests/multisig.cpp index 83924c7afc8..7268f2690dd 100644 --- a/tests/unit_tests/multisig.cpp +++ b/tests/unit_tests/multisig.cpp @@ -49,9 +49,19 @@ static const struct { "9t6Hn946u3eah5cuncH1hB5hGzsTUoevtf4SY7MHN5NgJZh2SFWsyVt3vUhuHyRKyrCQvr71Lfc1AevG3BXE11PQFoXDtD8", "bbd3175ef9fd9f5eefdc43035f882f74ad14c4cf1799d8b6f9001bc197175d02" + }, + { + "9zmAWoNyNPbgnYSm3nJNpAKHm6fCcs3MR94gBWxp9MCDUiMUhyYFfyQETUDLPF7DP6ZsmNo6LRxwPP9VmhHNxKrER9oGigT", + "f2efae45bef1917a7430cda8fcffc4ee010e3178761aa41d4628e23b1fe2d501" + }, + { + "9ue8NJMg3WzKxTtmjeXzWYF5KmU6dC7LHEt9wvYdPn2qMmoFUa8hJJHhSHvJ46UEwpDyy5jSboNMRaDBKwU54NT42YcNUp5", + "a4cef54ed3fd61cd78a2ceb82ecf85a903ad2db9a86fb77ff56c35c56016280a" } }; +static const size_t KEYS_COUNT = 5; + static void make_wallet(unsigned int idx, tools::wallet2 &wallet) { ASSERT_TRUE(idx < sizeof(test_addresses) / sizeof(test_addresses[0])); @@ -76,126 +86,87 @@ static void make_wallet(unsigned int idx, tools::wallet2 &wallet) } } -static void make_M_2_wallet(tools::wallet2 &wallet0, tools::wallet2 &wallet1, unsigned int M) +static std::vector exchange_round(std::vector& wallets, const std::vector& mis) { - ASSERT_TRUE(M <= 2); - - make_wallet(0, wallet0); - make_wallet(1, wallet1); - - std::vector sk0(1), sk1(1); - std::vector pk0(1), pk1(1); - - wallet0.decrypt_keys(""); - std::string mi0 = wallet0.get_multisig_info(); - wallet0.encrypt_keys(""); - wallet1.decrypt_keys(""); - std::string mi1 = wallet1.get_multisig_info(); - wallet1.encrypt_keys(""); - - ASSERT_TRUE(tools::wallet2::verify_multisig_info(mi1, sk0[0], pk0[0])); - ASSERT_TRUE(tools::wallet2::verify_multisig_info(mi0, sk1[0], pk1[0])); - - ASSERT_FALSE(wallet0.multisig() || wallet1.multisig()); - wallet0.make_multisig("", sk0, pk0, M); - wallet1.make_multisig("", sk1, pk1, M); - - ASSERT_TRUE(wallet0.get_account().get_public_address_str(cryptonote::TESTNET) == wallet1.get_account().get_public_address_str(cryptonote::TESTNET)); - - bool ready; - uint32_t threshold, total; - ASSERT_TRUE(wallet0.multisig(&ready, &threshold, &total)); - ASSERT_TRUE(ready); - ASSERT_TRUE(threshold == M); - ASSERT_TRUE(total == 2); - ASSERT_TRUE(wallet1.multisig(&ready, &threshold, &total)); - ASSERT_TRUE(ready); - ASSERT_TRUE(threshold == M); - ASSERT_TRUE(total == 2); + std::vector new_infos; + for (size_t i = 0; i < wallets.size(); ++i) { + new_infos.push_back(wallets[i].exchange_multisig_keys("", mis)); + } + + return new_infos; } -static void make_M_3_wallet(tools::wallet2 &wallet0, tools::wallet2 &wallet1, tools::wallet2 &wallet2, unsigned int M) +static void make_wallets(std::vector& wallets, unsigned int M) { - ASSERT_TRUE(M <= 3); - - make_wallet(0, wallet0); - make_wallet(1, wallet1); - make_wallet(2, wallet2); - - std::vector sk0(2), sk1(2), sk2(2); - std::vector pk0(2), pk1(2), pk2(2); - - wallet0.decrypt_keys(""); - std::string mi0 = wallet0.get_multisig_info(); - wallet0.encrypt_keys(""); - wallet1.decrypt_keys(""); - std::string mi1 = wallet1.get_multisig_info(); - wallet1.encrypt_keys(""); - wallet2.decrypt_keys(""); - std::string mi2 = wallet2.get_multisig_info(); - wallet2.encrypt_keys(""); - - ASSERT_TRUE(tools::wallet2::verify_multisig_info(mi1, sk0[0], pk0[0])); - ASSERT_TRUE(tools::wallet2::verify_multisig_info(mi2, sk0[1], pk0[1])); - ASSERT_TRUE(tools::wallet2::verify_multisig_info(mi0, sk1[0], pk1[0])); - ASSERT_TRUE(tools::wallet2::verify_multisig_info(mi2, sk1[1], pk1[1])); - ASSERT_TRUE(tools::wallet2::verify_multisig_info(mi0, sk2[0], pk2[0])); - ASSERT_TRUE(tools::wallet2::verify_multisig_info(mi1, sk2[1], pk2[1])); - - ASSERT_FALSE(wallet0.multisig() || wallet1.multisig() || wallet2.multisig()); - std::string mxi0 = wallet0.make_multisig("", sk0, pk0, M); - std::string mxi1 = wallet1.make_multisig("", sk1, pk1, M); - std::string mxi2 = wallet2.make_multisig("", sk2, pk2, M); - - const size_t nset = !mxi0.empty() + !mxi1.empty() + !mxi2.empty(); - ASSERT_TRUE((M < 3 && nset == 3) || (M == 3 && nset == 0)); - - if (nset > 0) - { - std::unordered_set pkeys; - std::vector signers(3, crypto::null_pkey); - ASSERT_TRUE(tools::wallet2::verify_extra_multisig_info(mxi0, pkeys, signers[0])); - ASSERT_TRUE(tools::wallet2::verify_extra_multisig_info(mxi1, pkeys, signers[1])); - ASSERT_TRUE(tools::wallet2::verify_extra_multisig_info(mxi2, pkeys, signers[2])); - ASSERT_TRUE(pkeys.size() == 3); - ASSERT_TRUE(wallet0.finalize_multisig("", pkeys, signers)); - ASSERT_TRUE(wallet1.finalize_multisig("", pkeys, signers)); - ASSERT_TRUE(wallet2.finalize_multisig("", pkeys, signers)); + ASSERT_TRUE(wallets.size() > 1 && wallets.size() <= KEYS_COUNT); + ASSERT_TRUE(M <= wallets.size()); + + std::vector mis(wallets.size()); + + for (size_t i = 0; i < wallets.size(); ++i) { + make_wallet(i, wallets[i]); + + wallets[i].decrypt_keys(""); + mis[i] = wallets[i].get_multisig_info(); + wallets[i].encrypt_keys(""); + } + + for (auto& wallet: wallets) { + ASSERT_FALSE(wallet.multisig() || wallet.multisig() || wallet.multisig()); + } + + std::vector mxis; + for (size_t i = 0; i < wallets.size(); ++i) { + // it's ok to put all of multisig keys in this function. it throws in case of error + mxis.push_back(wallets[i].make_multisig("", mis, M)); } - ASSERT_TRUE(wallet0.get_account().get_public_address_str(cryptonote::TESTNET) == wallet1.get_account().get_public_address_str(cryptonote::TESTNET)); - ASSERT_TRUE(wallet0.get_account().get_public_address_str(cryptonote::TESTNET) == wallet2.get_account().get_public_address_str(cryptonote::TESTNET)); - - bool ready; - uint32_t threshold, total; - ASSERT_TRUE(wallet0.multisig(&ready, &threshold, &total)); - ASSERT_TRUE(ready); - ASSERT_TRUE(threshold == M); - ASSERT_TRUE(total == 3); - ASSERT_TRUE(wallet1.multisig(&ready, &threshold, &total)); - ASSERT_TRUE(ready); - ASSERT_TRUE(threshold == M); - ASSERT_TRUE(total == 3); - ASSERT_TRUE(wallet2.multisig(&ready, &threshold, &total)); - ASSERT_TRUE(ready); - ASSERT_TRUE(threshold == M); - ASSERT_TRUE(total == 3); + while (!mxis[0].empty()) { + mxis = exchange_round(wallets, mxis); + } + + for (size_t i = 0; i < wallets.size(); ++i) { + ASSERT_TRUE(mxis[i].empty()); + bool ready; + uint32_t threshold, total; + ASSERT_TRUE(wallets[i].multisig(&ready, &threshold, &total)); + ASSERT_TRUE(ready); + ASSERT_TRUE(threshold == M); + ASSERT_TRUE(total == wallets.size()); + + if (i != 0) { + // "equals" is transitive relation so we need only to compare first wallet's address to each others' addresses. no need to compare 0's address with itself. + ASSERT_TRUE(wallets[0].get_account().get_public_address_str(cryptonote::TESTNET) == wallets[i].get_account().get_public_address_str(cryptonote::TESTNET)); + } + } } TEST(multisig, make_2_2) { - tools::wallet2 wallet0, wallet1; - make_M_2_wallet(wallet0, wallet1, 2); + std::vector wallets(2); + make_wallets(wallets, 2); } TEST(multisig, make_3_3) { - tools::wallet2 wallet0, wallet1, wallet2; - make_M_3_wallet(wallet0, wallet1, wallet2, 3); + std::vector wallets(3); + make_wallets(wallets, 3); } TEST(multisig, make_2_3) { - tools::wallet2 wallet0, wallet1, wallet2; - make_M_3_wallet(wallet0, wallet1, wallet2, 2); + std::vector wallets(3); + make_wallets(wallets, 2); +} + +TEST(multisig, make_2_4) +{ + std::vector wallets(4); + make_wallets(wallets, 2); +} + +TEST(multisig, make_2_5) +{ + std::vector wallets(5); + make_wallets(wallets, 2); } From 9acf42d3719a3237b99bc66089b49c86fbb9b467 Mon Sep 17 00:00:00 2001 From: naughtyfox Date: Tue, 17 Jul 2018 12:44:46 +0300 Subject: [PATCH 0510/1404] Multisig M/N functionality core tests added --- tests/core_tests/chaingen.h | 43 +------- tests/core_tests/chaingen_main.cpp | 10 ++ tests/core_tests/multisig.cpp | 152 ++++++++++++++++++++++++++++- tests/core_tests/multisig.h | 60 ++++++++++++ 4 files changed, 223 insertions(+), 42 deletions(-) diff --git a/tests/core_tests/chaingen.h b/tests/core_tests/chaingen.h index c9e1640aa62..6b9277a307f 100644 --- a/tests/core_tests/chaingen.h +++ b/tests/core_tests/chaingen.h @@ -544,6 +544,7 @@ inline bool do_replay_file(const std::string& filename) } return do_replay_events(events); } + //-------------------------------------------------------------------------- #define GENERATE_ACCOUNT(account) \ cryptonote::account_base account; \ @@ -556,47 +557,7 @@ inline bool do_replay_file(const std::string& filename) { \ for (size_t msidx = 0; msidx < total; ++msidx) \ account[msidx].generate(); \ - std::unordered_set all_multisig_keys; \ - std::vector> view_keys(total); \ - std::vector> spend_keys(total); \ - for (size_t msidx = 0; msidx < total; ++msidx) \ - { \ - for (size_t msidx_inner = 0; msidx_inner < total; ++msidx_inner) \ - { \ - if (msidx_inner != msidx) \ - { \ - crypto::secret_key vkh = cryptonote::get_multisig_blinded_secret_key(account[msidx_inner].get_keys().m_view_secret_key); \ - view_keys[msidx].push_back(vkh); \ - crypto::secret_key skh = cryptonote::get_multisig_blinded_secret_key(account[msidx_inner].get_keys().m_spend_secret_key); \ - crypto::public_key pskh; \ - crypto::secret_key_to_public_key(skh, pskh); \ - spend_keys[msidx].push_back(pskh); \ - } \ - } \ - } \ - for (size_t msidx = 0; msidx < total; ++msidx) \ - { \ - std::vector multisig_keys; \ - crypto::secret_key spend_skey; \ - crypto::public_key spend_pkey; \ - if (threshold == total) \ - cryptonote::generate_multisig_N_N(account[msidx].get_keys(), spend_keys[msidx], multisig_keys, (rct::key&)spend_skey, (rct::key&)spend_pkey); \ - else \ - cryptonote::generate_multisig_N1_N(account[msidx].get_keys(), spend_keys[msidx], multisig_keys, (rct::key&)spend_skey, (rct::key&)spend_pkey); \ - crypto::secret_key view_skey = cryptonote::generate_multisig_view_secret_key(account[msidx].get_keys().m_view_secret_key, view_keys[msidx]); \ - account[msidx].make_multisig(view_skey, spend_skey, spend_pkey, multisig_keys); \ - for (const auto &k: multisig_keys) \ - all_multisig_keys.insert(rct::rct2pk(rct::scalarmultBase(rct::sk2rct(k)))); \ - } \ - if (threshold < total) \ - { \ - std::vector spend_public_keys; \ - for (const auto &k: all_multisig_keys) \ - spend_public_keys.push_back(k); \ - crypto::public_key spend_pkey = cryptonote::generate_multisig_M_N_spend_public_key(spend_public_keys); \ - for (size_t msidx = 0; msidx < total; ++msidx) \ - account[msidx].finalize_multisig(spend_pkey); \ - } \ + make_multisig_accounts(account, threshold); \ } while(0) #define MAKE_ACCOUNT(VEC_EVENTS, account) \ diff --git a/tests/core_tests/chaingen_main.cpp b/tests/core_tests/chaingen_main.cpp index abc4123182a..84254cfdb95 100644 --- a/tests/core_tests/chaingen_main.cpp +++ b/tests/core_tests/chaingen_main.cpp @@ -223,6 +223,16 @@ int main(int argc, char* argv[]) GENERATE_AND_PLAY(gen_multisig_tx_invalid_33_1__no_threshold); GENERATE_AND_PLAY(gen_multisig_tx_invalid_33_1_2_no_threshold); GENERATE_AND_PLAY(gen_multisig_tx_invalid_33_1_3_no_threshold); + GENERATE_AND_PLAY(gen_multisig_tx_valid_24_1_2); + GENERATE_AND_PLAY(gen_multisig_tx_valid_24_1_2_many_inputs); + GENERATE_AND_PLAY(gen_multisig_tx_valid_25_1_2); + GENERATE_AND_PLAY(gen_multisig_tx_valid_25_1_2_many_inputs); + GENERATE_AND_PLAY(gen_multisig_tx_valid_48_1_234); + GENERATE_AND_PLAY(gen_multisig_tx_valid_48_1_234_many_inputs); + GENERATE_AND_PLAY(gen_multisig_tx_valid_24_1_no_signers); + GENERATE_AND_PLAY(gen_multisig_tx_valid_25_1_no_signers); + GENERATE_AND_PLAY(gen_multisig_tx_valid_48_1_no_signers); + GENERATE_AND_PLAY(gen_multisig_tx_valid_48_1_23_no_threshold); GENERATE_AND_PLAY(gen_bp_tx_valid_1); GENERATE_AND_PLAY(gen_bp_tx_invalid_1_1); diff --git a/tests/core_tests/multisig.cpp b/tests/core_tests/multisig.cpp index 46cc0ff35b4..fe5feb942a3 100644 --- a/tests/core_tests/multisig.cpp +++ b/tests/core_tests/multisig.cpp @@ -41,6 +41,87 @@ using namespace cryptonote; //#define NO_MULTISIG +void make_multisig_accounts(std::vector& account, uint32_t threshold) +{ + std::vector all_view_keys; + std::vector> derivations(account.size()); + //storage for all set of multisig derivations and spend public key (in first round) + std::unordered_set exchanging_keys; + + for (size_t msidx = 0; msidx < account.size(); ++msidx) + { + crypto::secret_key vkh = cryptonote::get_multisig_blinded_secret_key(account[msidx].get_keys().m_view_secret_key); + all_view_keys.push_back(vkh); + + crypto::secret_key skh = cryptonote::get_multisig_blinded_secret_key(account[msidx].get_keys().m_spend_secret_key); + crypto::public_key pskh; + crypto::secret_key_to_public_key(skh, pskh); + + derivations[msidx].push_back(pskh); + exchanging_keys.insert(pskh); + } + + uint32_t roundsTotal = 1; + if (threshold < account.size()) + roundsTotal = account.size() - threshold; + + //secret multisig keys of every account + std::vector> multisig_keys(account.size()); + std::vector spend_skey(account.size()); + std::vector spend_pkey(account.size()); + for (uint32_t round = 0; round < roundsTotal; ++round) + { + std::unordered_set roundKeys; + for (size_t msidx = 0; msidx < account.size(); ++msidx) + { + // subtracting one's keys from set of all unique keys is the same as key exchange + auto myKeys = exchanging_keys; + for (const auto& d: derivations[msidx]) + myKeys.erase(d); + + if (threshold == account.size()) + { + cryptonote::generate_multisig_N_N(account[msidx].get_keys(), std::vector(myKeys.begin(), myKeys.end()), multisig_keys[msidx], (rct::key&)spend_skey[msidx], (rct::key&)spend_pkey[msidx]); + } + else + { + derivations[msidx] = cryptonote::generate_multisig_derivations(account[msidx].get_keys(), std::vector(myKeys.begin(), myKeys.end())); + roundKeys.insert(derivations[msidx].begin(), derivations[msidx].end()); + } + } + + exchanging_keys = roundKeys; + roundKeys.clear(); + } + + std::unordered_set all_multisig_keys; + for (size_t msidx = 0; msidx < account.size(); ++msidx) + { + std::unordered_set view_keys(all_view_keys.begin(), all_view_keys.end()); + view_keys.erase(all_view_keys[msidx]); + + crypto::secret_key view_skey = cryptonote::generate_multisig_view_secret_key(account[msidx].get_keys().m_view_secret_key, std::vector(view_keys.begin(), view_keys.end())); + if (threshold < account.size()) + { + multisig_keys[msidx] = cryptonote::calculate_multisig_keys(derivations[msidx]); + spend_skey[msidx] = cryptonote::calculate_multisig_signer_key(multisig_keys[msidx]); + } + account[msidx].make_multisig(view_skey, spend_skey[msidx], spend_pkey[msidx], multisig_keys[msidx]); + for (const auto &k: multisig_keys[msidx]) { + all_multisig_keys.insert(rct::rct2pk(rct::scalarmultBase(rct::sk2rct(k)))); + } + } + + if (threshold < account.size()) + { + std::vector public_keys(std::vector(all_multisig_keys.begin(), all_multisig_keys.end())); + crypto::public_key spend_pkey = cryptonote::generate_multisig_M_N_spend_public_key(public_keys); + + for (size_t msidx = 0; msidx < account.size(); ++msidx) + account[msidx].finalize_multisig(spend_pkey); + } +} + //---------------------------------------------------------------------------------------------------------------------- // Tests @@ -55,7 +136,6 @@ bool gen_multisig_tx_validation_base::generate_with(std::vector= 2, false, "Bad scheme"); CHECK_AND_ASSERT_MES(threshold <= total, false, "Bad scheme"); - CHECK_AND_ASSERT_MES(threshold >= total - 1, false, "Unsupported scheme"); #ifdef NO_MULTISIG CHECK_AND_ASSERT_MES(total <= 5, false, "Unsupported scheme"); #endif @@ -480,6 +560,48 @@ bool gen_multisig_tx_valid_89_3_1245789::generate(std::vector& return generate_with(events, 2, mixin, amount_paid, true, 8, 9, 3, {1, 2, 4, 5, 7, 8, 9}, NULL, NULL); } +bool gen_multisig_tx_valid_24_1_2::generate(std::vector& events) const +{ + const size_t mixin = 4; + const uint64_t amount_paid = 10000; + return generate_with(events, 2, mixin, amount_paid, true, 2, 4, 1, {2}, NULL, NULL); +} + +bool gen_multisig_tx_valid_24_1_2_many_inputs::generate(std::vector& events) const +{ + const size_t mixin = 4; + const uint64_t amount_paid = 10000; + return generate_with(events, 4, mixin, amount_paid, true, 2, 4, 1, {2}, NULL, NULL); +} + +bool gen_multisig_tx_valid_25_1_2::generate(std::vector& events) const +{ + const size_t mixin = 4; + const uint64_t amount_paid = 10000; + return generate_with(events, 2, mixin, amount_paid, true, 2, 5, 1, {2}, NULL, NULL); +} + +bool gen_multisig_tx_valid_25_1_2_many_inputs::generate(std::vector& events) const +{ + const size_t mixin = 4; + const uint64_t amount_paid = 10000; + return generate_with(events, 4, mixin, amount_paid, true, 2, 5, 1, {2}, NULL, NULL); +} + +bool gen_multisig_tx_valid_48_1_234::generate(std::vector& events) const +{ + const size_t mixin = 4; + const uint64_t amount_paid = 10000; + return generate_with(events, 2, mixin, amount_paid, true, 4, 8, 1, {2, 3, 4}, NULL, NULL); +} + +bool gen_multisig_tx_valid_48_1_234_many_inputs::generate(std::vector& events) const +{ + const size_t mixin = 4; + const uint64_t amount_paid = 10000; + return generate_with(events, 4, mixin, amount_paid, true, 4, 8, 1, {2, 3, 4}, NULL, NULL); +} + bool gen_multisig_tx_invalid_22_1__no_threshold::generate(std::vector& events) const { const size_t mixin = 4; @@ -521,3 +643,31 @@ bool gen_multisig_tx_invalid_45_5_23_no_threshold::generate(std::vector& events) const +{ + const size_t mixin = 4; + const uint64_t amount_paid = 10000; + return generate_with(events, 2, mixin, amount_paid, false, 2, 4, 1, {}, NULL, NULL); +} + +bool gen_multisig_tx_valid_25_1_no_signers::generate(std::vector& events) const +{ + const size_t mixin = 4; + const uint64_t amount_paid = 10000; + return generate_with(events, 2, mixin, amount_paid, false, 2, 5, 1, {}, NULL, NULL); +} + +bool gen_multisig_tx_valid_48_1_no_signers::generate(std::vector& events) const +{ + const size_t mixin = 4; + const uint64_t amount_paid = 10000; + return generate_with(events, 2, mixin, amount_paid, false, 4, 8, 1, {}, NULL, NULL); +} + +bool gen_multisig_tx_valid_48_1_23_no_threshold::generate(std::vector& events) const +{ + const size_t mixin = 4; + const uint64_t amount_paid = 10000; + return generate_with(events, 2, mixin, amount_paid, false, 4, 8, 1, {2, 3}, NULL, NULL); +} diff --git a/tests/core_tests/multisig.h b/tests/core_tests/multisig.h index f0157a6d12f..9e4cb3a23d6 100644 --- a/tests/core_tests/multisig.h +++ b/tests/core_tests/multisig.h @@ -161,6 +161,42 @@ struct gen_multisig_tx_valid_89_3_1245789: public gen_multisig_tx_validation_bas }; template<> struct get_test_options: public get_test_options {}; +struct gen_multisig_tx_valid_24_1_2: public gen_multisig_tx_validation_base +{ + bool generate(std::vector& events) const; +}; +template<> struct get_test_options: public get_test_options {}; + +struct gen_multisig_tx_valid_24_1_2_many_inputs: public gen_multisig_tx_validation_base +{ + bool generate(std::vector& events) const; +}; +template<> struct get_test_options: public get_test_options {}; + +struct gen_multisig_tx_valid_25_1_2: public gen_multisig_tx_validation_base +{ + bool generate(std::vector& events) const; +}; +template<> struct get_test_options: public get_test_options {}; + +struct gen_multisig_tx_valid_25_1_2_many_inputs: public gen_multisig_tx_validation_base +{ + bool generate(std::vector& events) const; +}; +template<> struct get_test_options: public get_test_options {}; + +struct gen_multisig_tx_valid_48_1_234: public gen_multisig_tx_validation_base +{ + bool generate(std::vector& events) const; +}; +template<> struct get_test_options: public get_test_options {}; + +struct gen_multisig_tx_valid_48_1_234_many_inputs: public gen_multisig_tx_validation_base +{ + bool generate(std::vector& events) const; +}; +template<> struct get_test_options: public get_test_options {}; + // invalid struct gen_multisig_tx_invalid_22_1__no_threshold: public gen_multisig_tx_validation_base { @@ -197,3 +233,27 @@ struct gen_multisig_tx_invalid_45_5_23_no_threshold: public gen_multisig_tx_vali bool generate(std::vector& events) const; }; template<> struct get_test_options: public get_test_options {}; + +struct gen_multisig_tx_valid_24_1_no_signers: public gen_multisig_tx_validation_base +{ + bool generate(std::vector& events) const; +}; +template<> struct get_test_options: public get_test_options {}; + +struct gen_multisig_tx_valid_25_1_no_signers: public gen_multisig_tx_validation_base +{ + bool generate(std::vector& events) const; +}; +template<> struct get_test_options: public get_test_options {}; + +struct gen_multisig_tx_valid_48_1_no_signers: public gen_multisig_tx_validation_base +{ + bool generate(std::vector& events) const; +}; +template<> struct get_test_options: public get_test_options {}; + +struct gen_multisig_tx_valid_48_1_23_no_threshold: public gen_multisig_tx_validation_base +{ + bool generate(std::vector& events) const; +}; +template<> struct get_test_options: public get_test_options {}; From d5541e44feaa2be10c6a4ffb2746d59bb01a2b26 Mon Sep 17 00:00:00 2001 From: xiphon Date: Mon, 1 Oct 2018 06:39:37 +0000 Subject: [PATCH 0511/1404] common: Windows 'spawn' support for tx and block notifications --- src/common/CMakeLists.txt | 4 +-- src/common/notify.cpp | 13 +++---- src/common/{exec.cpp => spawn.cpp} | 57 ++++++++++++++++++++++++++++-- src/common/{exec.h => spawn.h} | 2 +- 4 files changed, 65 insertions(+), 11 deletions(-) rename src/common/{exec.cpp => spawn.cpp} (64%) rename src/common/{exec.h => spawn.h} (95%) diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 9b83f149fcc..aed9bfee73e 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -34,13 +34,13 @@ set(common_sources dns_utils.cpp download.cpp error.cpp - exec.cpp expect.cpp util.cpp i18n.cpp notify.cpp password.cpp perf_timer.cpp + spawn.cpp threadpool.cpp updates.cpp aligned.c) @@ -60,7 +60,6 @@ set(common_private_headers dns_utils.h download.h error.h - exec.h expect.h http_connection.h int-util.h @@ -74,6 +73,7 @@ set(common_private_headers i18n.h password.h perf_timer.h + spawn.h stack_trace.h threadpool.h updates.h diff --git a/src/common/notify.cpp b/src/common/notify.cpp index b7869ad841b..cadc68ea777 100644 --- a/src/common/notify.cpp +++ b/src/common/notify.cpp @@ -29,12 +29,17 @@ #include #include "misc_log_ex.h" #include "file_io_utils.h" -#include "exec.h" +#include "spawn.h" #include "notify.h" namespace tools { +/* + TODO: + - Improve tokenization to handle paths containing whitespaces, quotes, etc. + - Windows unicode support (implies implementing unicode command line parsing code) +*/ Notify::Notify(const char *spec) { CHECK_AND_ASSERT_THROW_MES(spec, "Null spec"); @@ -51,11 +56,7 @@ int Notify::notify(const char *parameter) for (std::string &s: margs) boost::replace_all(s, "%s", parameter); - char **cargs = (char**)alloca(sizeof(char*) * (margs.size() + 1)); - for (size_t n = 0; n < margs.size(); ++n) - cargs[n] = (char*)margs[n].c_str(); - cargs[margs.size()] = NULL; - return tools::exec(filename.c_str(), cargs, false); + return tools::spawn(filename.c_str(), margs, false); } } diff --git a/src/common/exec.cpp b/src/common/spawn.cpp similarity index 64% rename from src/common/exec.cpp rename to src/common/spawn.cpp index 41b8f1378a1..59f11675cc5 100644 --- a/src/common/exec.cpp +++ b/src/common/spawn.cpp @@ -29,16 +29,68 @@ #include #include #include +#ifdef _WIN32 +#include +#include +#include +#else #include +#endif #include "misc_log_ex.h" -#include "exec.h" +#include "spawn.h" namespace tools { -int exec(const char *filename, char * const argv[], bool wait) +int spawn(const char *filename, const std::vector& args, bool wait) { +#ifdef _WIN32 + std::string joined = boost::algorithm::join(args, " "); + char *commandLine = !joined.empty() ? &joined[0] : nullptr; + STARTUPINFOA si = {}; + si.cb = sizeof(si); + PROCESS_INFORMATION pi; + if (!CreateProcessA(filename, commandLine, nullptr, nullptr, false, 0, nullptr, nullptr, &si, &pi)) + { + MERROR("CreateProcess failed. Error code " << GetLastError()); + return -1; + } + + BOOST_SCOPE_EXIT(&pi) + { + CloseHandle(pi.hThread); + CloseHandle(pi.hProcess); + } + BOOST_SCOPE_EXIT_END + + if (!wait) + { + return 0; + } + + DWORD result = WaitForSingleObject(pi.hProcess, INFINITE); + if (result != WAIT_OBJECT_0) + { + MERROR("WaitForSingleObject failed. Result " << result << ", error code " << GetLastError()); + return -1; + } + + DWORD exitCode; + if (!GetExitCodeProcess(pi.hProcess, &exitCode)) + { + MERROR("GetExitCodeProcess failed. Error code " << GetLastError()); + return -1; + } + + MINFO("Child exited with " << exitCode); + return static_cast(exitCode); +#else + char **argv = (char**)alloca(sizeof(char*) * (args.size() + 1)); + for (size_t n = 0; n < args.size(); ++n) + argv[n] = (char*)args[n].c_str(); + argv[args.size()] = NULL; + pid_t pid = fork(); if (pid < 0) { @@ -83,6 +135,7 @@ int exec(const char *filename, char * const argv[], bool wait) } MERROR("Secret passage found"); return -1; +#endif } } diff --git a/src/common/exec.h b/src/common/spawn.h similarity index 95% rename from src/common/exec.h rename to src/common/spawn.h index 4d2037798dd..c90a0f790b3 100644 --- a/src/common/exec.h +++ b/src/common/spawn.h @@ -31,6 +31,6 @@ namespace tools { -int exec(const char *filename, char * const argv[], bool wait); +int spawn(const char *filename, const std::vector& args, bool wait); } From 5ec929fb3a01c3dfa7409debe1f585e2aff229a3 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 1 Oct 2018 21:25:56 +0000 Subject: [PATCH 0512/1404] daemon: do not display uptime when not known --- src/daemon/rpc_command_executor.cpp | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp index 6b6c889078e..6464d372fcd 100644 --- a/src/daemon/rpc_command_executor.cpp +++ b/src/daemon/rpc_command_executor.cpp @@ -442,7 +442,8 @@ bool t_rpc_command_executor::show_status() { } } - tools::success_msg_writer() << boost::format("Height: %llu/%llu (%.1f%%) on %s%s, %s, net hash %s, v%u%s, %s, %u(out)+%u(in) connections, uptime %ud %uh %um %us") + std::stringstream str; + str << boost::format("Height: %llu/%llu (%.1f%%) on %s%s, %s, net hash %s, v%u%s, %s, %u(out)+%u(in) connections") % (unsigned long long)ires.height % (unsigned long long)net_height % get_sync_percentage(ires) @@ -455,12 +456,21 @@ bool t_rpc_command_executor::show_status() { % (hfres.state == cryptonote::HardFork::Ready ? "up to date" : hfres.state == cryptonote::HardFork::UpdateNeeded ? "update needed" : "out of date, likely forked") % (unsigned)ires.outgoing_connections_count % (unsigned)ires.incoming_connections_count - % (unsigned int)floor(uptime / 60.0 / 60.0 / 24.0) - % (unsigned int)floor(fmod((uptime / 60.0 / 60.0), 24.0)) - % (unsigned int)floor(fmod((uptime / 60.0), 60.0)) - % (unsigned int)fmod(uptime, 60.0) ; + // restricted RPC does not disclose start time + if (ires.start_time) + { + str << boost::format(", uptime %ud %uh %um %us") + % (unsigned int)floor(uptime / 60.0 / 60.0 / 24.0) + % (unsigned int)floor(fmod((uptime / 60.0 / 60.0), 24.0)) + % (unsigned int)floor(fmod((uptime / 60.0), 60.0)) + % (unsigned int)fmod(uptime, 60.0) + ; + } + + tools::success_msg_writer() << str.str(); + return true; } From bccd88ddf5f09a7bf774b3437a6396ac9953aad5 Mon Sep 17 00:00:00 2001 From: doy-lee Date: Wed, 26 Sep 2018 12:38:39 +0000 Subject: [PATCH 0513/1404] wallet2: clear found out for every tx key Avoids triggering the sanity check --- src/wallet/wallet2.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 299b4afeb4b..14077bc9bbc 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -1339,7 +1339,6 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote // (that is, the prunable stuff may or may not be included) if (!miner_tx && !pool) process_unconfirmed(txid, tx, height); - std::vector outs; std::unordered_map tx_money_got_in_outs; // per receiving subaddress index crypto::public_key tx_pub_key = null_pkey; bool notify = false; @@ -1362,6 +1361,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote uint64_t total_received_1 = 0; while (!tx.vout.empty()) { + std::vector outs; // if tx.vout is not empty, we loop through all tx pubkeys tx_extra_pub_key pub_key_field; From 7c790f11f207ffad5e8ba857a1d1640cae815cee Mon Sep 17 00:00:00 2001 From: Howard Chu Date: Tue, 2 Oct 2018 16:14:20 +0100 Subject: [PATCH 0514/1404] Fix rtxn usage in BlockchainLMDB::get_estimated_batch_size Should only stop the rtxn if we actually started it Fixes Coverity 184960 --- src/blockchain_db/lmdb/db_lmdb.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp index 824598f8ca4..bd91f308ac5 100644 --- a/src/blockchain_db/lmdb/db_lmdb.cpp +++ b/src/blockchain_db/lmdb/db_lmdb.cpp @@ -649,7 +649,7 @@ uint64_t BlockchainLMDB::get_estimated_batch_size(uint64_t batch_num_blocks, uin { MDB_txn *rtxn; mdb_txn_cursors *rcurs; - block_rtxn_start(&rtxn, &rcurs); + bool my_rtxn = block_rtxn_start(&rtxn, &rcurs); for (uint64_t block_num = block_start; block_num <= block_stop; ++block_num) { // we have access to block weight, which will be greater or equal to block size, @@ -661,7 +661,7 @@ uint64_t BlockchainLMDB::get_estimated_batch_size(uint64_t batch_num_blocks, uin // some blocks were to be skipped for being outliers. ++num_blocks_used; } - block_rtxn_stop(); + if (my_rtxn) block_rtxn_stop(); avg_block_size = total_block_size / num_blocks_used; MDEBUG("average block size across recent " << num_blocks_used << " blocks: " << avg_block_size); } From e5108a294a7d4fd923665e059a6eb90b0ab5a337 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 2 Oct 2018 08:39:47 +0000 Subject: [PATCH 0515/1404] Catch more exceptions in dtors Misc coverity reports --- contrib/epee/include/misc_language.h | 3 ++- src/common/threadpool.cpp | 10 ++++++++++ tests/crypto/main.cpp | 3 +++ tests/fuzz/base58.cpp | 2 ++ tests/fuzz/block.cpp | 2 ++ tests/fuzz/bulletproof.cpp | 2 ++ tests/fuzz/cold-outputs.cpp | 2 ++ tests/fuzz/cold-transaction.cpp | 2 ++ tests/fuzz/http-client.cpp | 2 ++ tests/fuzz/levin.cpp | 2 ++ tests/fuzz/load_from_binary.cpp | 2 ++ tests/fuzz/load_from_json.cpp | 2 ++ tests/fuzz/parse_url.cpp | 2 ++ tests/fuzz/signature.cpp | 2 ++ tests/fuzz/transaction.cpp | 2 ++ tests/net_load_tests/clt.cpp | 2 ++ tests/net_load_tests/srv.cpp | 2 ++ 17 files changed, 43 insertions(+), 1 deletion(-) diff --git a/contrib/epee/include/misc_language.h b/contrib/epee/include/misc_language.h index d5157365c8f..7e4eb337db2 100644 --- a/contrib/epee/include/misc_language.h +++ b/contrib/epee/include/misc_language.h @@ -147,7 +147,8 @@ namespace misc_utils {} ~call_befor_die() { - m_func(); + try { m_func(); } + catch (...) { /* ignore */ } } }; diff --git a/src/common/threadpool.cpp b/src/common/threadpool.cpp index 5ea04a3531d..37825e31d07 100644 --- a/src/common/threadpool.cpp +++ b/src/common/threadpool.cpp @@ -51,11 +51,19 @@ threadpool::threadpool(unsigned int max_threads) : running(true), active(0) { } threadpool::~threadpool() { + try { const boost::unique_lock lock(mutex); running = false; has_work.notify_all(); } + catch (...) + { + // if the lock throws, we're just do it without a lock and hope, + // since the alternative is terminate + running = false; + has_work.notify_all(); + } for (size_t i = 0; i lock(mt); if (num) MERROR("wait should have been called before waiter dtor - waiting now"); } + catch (...) { /* ignore */ } try { wait(NULL); diff --git a/tests/crypto/main.cpp b/tests/crypto/main.cpp index e5406f77142..cc3b53b83cf 100644 --- a/tests/crypto/main.cpp +++ b/tests/crypto/main.cpp @@ -35,6 +35,7 @@ #include #include "warnings.h" +#include "misc_log_ex.h" #include "crypto/crypto.h" #include "crypto/hash.h" #include "crypto-tests.h" @@ -59,6 +60,7 @@ bool operator !=(const key_derivation &a, const key_derivation &b) { DISABLE_GCC_WARNING(maybe-uninitialized) int main(int argc, char *argv[]) { + TRY_ENTRY(); fstream input; string cmd; size_t test = 0; @@ -266,4 +268,5 @@ int main(int argc, char *argv[]) { error = true; } return error ? 1 : 0; + CATCH_ENTRY_L0("main", 1); } diff --git a/tests/fuzz/base58.cpp b/tests/fuzz/base58.cpp index 49516dd8305..a4857bdd12a 100644 --- a/tests/fuzz/base58.cpp +++ b/tests/fuzz/base58.cpp @@ -68,7 +68,9 @@ int Base58Fuzzer::run(const std::string &filename) int main(int argc, const char **argv) { + TRY_ENTRY(); Base58Fuzzer fuzzer; return run_fuzzer(argc, argv, fuzzer); + CATCH_ENTRY_L0("main", 1); } diff --git a/tests/fuzz/block.cpp b/tests/fuzz/block.cpp index 2df77b046f9..eed3b94b20a 100644 --- a/tests/fuzz/block.cpp +++ b/tests/fuzz/block.cpp @@ -61,6 +61,8 @@ int BlockFuzzer::run(const std::string &filename) int main(int argc, const char **argv) { + TRY_ENTRY(); BlockFuzzer fuzzer; return run_fuzzer(argc, argv, fuzzer); + CATCH_ENTRY_L0("main", 1); } diff --git a/tests/fuzz/bulletproof.cpp b/tests/fuzz/bulletproof.cpp index 2f4dfd0eab8..2f3a2f8d12f 100644 --- a/tests/fuzz/bulletproof.cpp +++ b/tests/fuzz/bulletproof.cpp @@ -65,6 +65,8 @@ int BulletproofFuzzer::run(const std::string &filename) int main(int argc, const char **argv) { + TRY_ENTRY(); BulletproofFuzzer fuzzer; return run_fuzzer(argc, argv, fuzzer); + CATCH_ENTRY_L0("main", 1); } diff --git a/tests/fuzz/cold-outputs.cpp b/tests/fuzz/cold-outputs.cpp index 59b59810c91..488a3b93118 100644 --- a/tests/fuzz/cold-outputs.cpp +++ b/tests/fuzz/cold-outputs.cpp @@ -95,7 +95,9 @@ int ColdOutputsFuzzer::run(const std::string &filename) int main(int argc, const char **argv) { + TRY_ENTRY(); ColdOutputsFuzzer fuzzer; return run_fuzzer(argc, argv, fuzzer); + CATCH_ENTRY_L0("main", 1); } diff --git a/tests/fuzz/cold-transaction.cpp b/tests/fuzz/cold-transaction.cpp index da33dc31804..fa3041ba318 100644 --- a/tests/fuzz/cold-transaction.cpp +++ b/tests/fuzz/cold-transaction.cpp @@ -97,6 +97,8 @@ int ColdTransactionFuzzer::run(const std::string &filename) int main(int argc, const char **argv) { + TRY_ENTRY(); ColdTransactionFuzzer fuzzer; return run_fuzzer(argc, argv, fuzzer); + CATCH_ENTRY_L0("main", 1); } diff --git a/tests/fuzz/http-client.cpp b/tests/fuzz/http-client.cpp index cd52643d9fb..909325832dd 100644 --- a/tests/fuzz/http-client.cpp +++ b/tests/fuzz/http-client.cpp @@ -92,7 +92,9 @@ int HTTPClientFuzzer::run(const std::string &filename) int main(int argc, const char **argv) { + TRY_ENTRY(); HTTPClientFuzzer fuzzer; return run_fuzzer(argc, argv, fuzzer); + CATCH_ENTRY_L0("main", 1); } diff --git a/tests/fuzz/levin.cpp b/tests/fuzz/levin.cpp index 4ced1837f78..d0c5803f503 100644 --- a/tests/fuzz/levin.cpp +++ b/tests/fuzz/levin.cpp @@ -341,7 +341,9 @@ int LevinFuzzer::run(const std::string &filename) int main(int argc, const char **argv) { + TRY_ENTRY(); LevinFuzzer fuzzer; return run_fuzzer(argc, argv, fuzzer); + CATCH_ENTRY_L0("main", 1); } diff --git a/tests/fuzz/load_from_binary.cpp b/tests/fuzz/load_from_binary.cpp index 8f96c454fe8..89f122902a3 100644 --- a/tests/fuzz/load_from_binary.cpp +++ b/tests/fuzz/load_from_binary.cpp @@ -70,7 +70,9 @@ int PortableStorageFuzzer::run(const std::string &filename) int main(int argc, const char **argv) { + TRY_ENTRY(); PortableStorageFuzzer fuzzer; return run_fuzzer(argc, argv, fuzzer); + CATCH_ENTRY_L0("main", 1); } diff --git a/tests/fuzz/load_from_json.cpp b/tests/fuzz/load_from_json.cpp index b0c1a9bf361..083555f7ebc 100644 --- a/tests/fuzz/load_from_json.cpp +++ b/tests/fuzz/load_from_json.cpp @@ -70,7 +70,9 @@ int PortableStorageFuzzer::run(const std::string &filename) int main(int argc, const char **argv) { + TRY_ENTRY(); PortableStorageFuzzer fuzzer; return run_fuzzer(argc, argv, fuzzer); + CATCH_ENTRY_L0("main", 1); } diff --git a/tests/fuzz/parse_url.cpp b/tests/fuzz/parse_url.cpp index 8812cf9c20e..fb5754a705f 100644 --- a/tests/fuzz/parse_url.cpp +++ b/tests/fuzz/parse_url.cpp @@ -68,7 +68,9 @@ int ParseURLFuzzer::run(const std::string &filename) int main(int argc, const char **argv) { + TRY_ENTRY(); ParseURLFuzzer fuzzer; return run_fuzzer(argc, argv, fuzzer); + CATCH_ENTRY_L0("main", 1); } diff --git a/tests/fuzz/signature.cpp b/tests/fuzz/signature.cpp index 6dadf960dfa..f82ada8b489 100644 --- a/tests/fuzz/signature.cpp +++ b/tests/fuzz/signature.cpp @@ -92,6 +92,8 @@ int SignatureFuzzer::run(const std::string &filename) int main(int argc, const char **argv) { + TRY_ENTRY(); SignatureFuzzer fuzzer; return run_fuzzer(argc, argv, fuzzer); + CATCH_ENTRY_L0("main", 1); } diff --git a/tests/fuzz/transaction.cpp b/tests/fuzz/transaction.cpp index b3349c383fb..934bd4685c8 100644 --- a/tests/fuzz/transaction.cpp +++ b/tests/fuzz/transaction.cpp @@ -61,6 +61,8 @@ int TransactionFuzzer::run(const std::string &filename) int main(int argc, const char **argv) { + TRY_ENTRY(); TransactionFuzzer fuzzer; return run_fuzzer(argc, argv, fuzzer); + CATCH_ENTRY_L0("main", 1); } diff --git a/tests/net_load_tests/clt.cpp b/tests/net_load_tests/clt.cpp index a65e02cab2e..4ae50d064bd 100644 --- a/tests/net_load_tests/clt.cpp +++ b/tests/net_load_tests/clt.cpp @@ -628,6 +628,7 @@ TEST_F(net_load_test_clt, permament_open_and_close_and_connections_closed_by_ser int main(int argc, char** argv) { + TRY_ENTRY(); tools::on_startup(); epee::debug::get_set_enable_assert(true, false); //set up logging options @@ -635,4 +636,5 @@ int main(int argc, char** argv) ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); + CATCH_ENTRY_L0("main", 1); } diff --git a/tests/net_load_tests/srv.cpp b/tests/net_load_tests/srv.cpp index 6518d911705..dc3772127e5 100644 --- a/tests/net_load_tests/srv.cpp +++ b/tests/net_load_tests/srv.cpp @@ -215,6 +215,7 @@ namespace int main(int argc, char** argv) { + TRY_ENTRY(); tools::on_startup(); //set up logging options mlog_configure(mlog_get_default_log_path("net_load_tests_srv.log"), true); @@ -233,4 +234,5 @@ int main(int argc, char** argv) if (!tcp_server.run_server(thread_count, true)) return 2; return 0; + CATCH_ENTRY_L0("main", 1); } From 758d7684863b8ff001758c4360ed7087245eac03 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 2 Oct 2018 09:54:38 +0000 Subject: [PATCH 0516/1404] connection_basic: remove unused floating time start time --- contrib/epee/include/net/connection_basic.hpp | 3 --- contrib/epee/src/connection_basic.cpp | 7 ------- 2 files changed, 10 deletions(-) diff --git a/contrib/epee/include/net/connection_basic.hpp b/contrib/epee/include/net/connection_basic.hpp index 095e747a5ab..7e875004705 100644 --- a/contrib/epee/include/net/connection_basic.hpp +++ b/contrib/epee/include/net/connection_basic.hpp @@ -92,7 +92,6 @@ class connection_basic { // not-templated base class for rapid developmet of som critical_section m_send_que_lock; std::list m_send_que; volatile bool m_is_multithreaded; - double m_start_time; /// Strand to ensure the connection's handlers are not called concurrently. boost::asio::io_service::strand strand_; /// Socket for the connection. @@ -112,8 +111,6 @@ class connection_basic { // not-templated base class for rapid developmet of som void logger_handle_net_write(size_t size); // network data written void logger_handle_net_read(size_t size); // network data read - void set_start_time(); - // config for rate limit static void set_rate_up_limit(uint64_t limit); diff --git a/contrib/epee/src/connection_basic.cpp b/contrib/epee/src/connection_basic.cpp index dea1928a7e7..9ab4858392f 100644 --- a/contrib/epee/src/connection_basic.cpp +++ b/contrib/epee/src/connection_basic.cpp @@ -250,22 +250,15 @@ void connection_basic::sleep_before_packet(size_t packet_size, int phase, int q } } -void connection_basic::set_start_time() { - CRITICAL_REGION_LOCAL( network_throttle_manager::m_lock_get_global_throttle_out ); - m_start_time = network_throttle_manager::get_global_throttle_out().get_time_seconds(); -} void connection_basic::do_send_handler_write(const void* ptr , size_t cb ) { // No sleeping here; sleeping is done once and for all in connection::handle_write MTRACE("handler_write (direct) - before ASIO write, for packet="<::handle_write MTRACE("handler_write (after write, from queue="< Date: Tue, 2 Oct 2018 12:13:57 +0000 Subject: [PATCH 0517/1404] abstract_tcp_server2: move m_period to subclass This is where it is actually used, and initialized --- contrib/epee/include/net/abstract_tcp_server2.h | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/contrib/epee/include/net/abstract_tcp_server2.h b/contrib/epee/include/net/abstract_tcp_server2.h index b2c05ebb0b0..3f726a35272 100644 --- a/contrib/epee/include/net/abstract_tcp_server2.h +++ b/contrib/epee/include/net/abstract_tcp_server2.h @@ -246,7 +246,6 @@ namespace net_utils m_timer(io_serice) {} boost::asio::deadline_timer m_timer; - uint64_t m_period; }; template @@ -262,25 +261,27 @@ namespace net_utils { return m_handler(); } + uint64_t m_period; }; template bool add_idle_handler(t_handler t_callback, uint64_t timeout_ms) { - boost::shared_ptr ptr(new idle_callback_conext(io_service_, t_callback, timeout_ms)); + boost::shared_ptr> ptr(new idle_callback_conext(io_service_, t_callback, timeout_ms)); //needed call handler here ?... ptr->m_timer.expires_from_now(boost::posix_time::milliseconds(ptr->m_period)); - ptr->m_timer.async_wait(boost::bind(&boosted_tcp_server::global_timer_handler, this, ptr)); + ptr->m_timer.async_wait(boost::bind(&boosted_tcp_server::global_timer_handler, this, ptr)); return true; } - bool global_timer_handler(/*const boost::system::error_code& err, */boost::shared_ptr ptr) + template + bool global_timer_handler(/*const boost::system::error_code& err, */boost::shared_ptr> ptr) { //if handler return false - he don't want to be called anymore if(!ptr->call_handler()) return true; ptr->m_timer.expires_from_now(boost::posix_time::milliseconds(ptr->m_period)); - ptr->m_timer.async_wait(boost::bind(&boosted_tcp_server::global_timer_handler, this, ptr)); + ptr->m_timer.async_wait(boost::bind(&boosted_tcp_server::global_timer_handler, this, ptr)); return true; } From 00901e9c938dc801e963f98825c2ab5976fa8cc5 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 2 Oct 2018 12:14:44 +0000 Subject: [PATCH 0518/1404] epee: initialize a few data members where it seems to be appropriate --- contrib/epee/include/net/levin_protocol_handler_async.h | 4 +++- contrib/epee/src/network_throttle-detail.cpp | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/contrib/epee/include/net/levin_protocol_handler_async.h b/contrib/epee/include/net/levin_protocol_handler_async.h index e9853ee265e..08aa1d468b8 100644 --- a/contrib/epee/include/net/levin_protocol_handler_async.h +++ b/contrib/epee/include/net/levin_protocol_handler_async.h @@ -99,7 +99,7 @@ class async_protocol_handler_config size_t get_connections_count(); void set_handler(levin_commands_handler* handler, void (*destroy)(levin_commands_handler*) = NULL); - async_protocol_handler_config():m_pcommands_handler(NULL), m_pcommands_handler_destroy(NULL), m_max_packet_size(LEVIN_DEFAULT_MAX_PACKET_SIZE) + async_protocol_handler_config():m_pcommands_handler(NULL), m_pcommands_handler_destroy(NULL), m_max_packet_size(LEVIN_DEFAULT_MAX_PACKET_SIZE), m_invoke_timeout(LEVIN_DEFAULT_TIMEOUT_PRECONFIGURED) {} ~async_protocol_handler_config() { set_handler(NULL, NULL); } void del_out_connections(size_t count); @@ -272,6 +272,8 @@ class async_protocol_handler m_wait_count = 0; m_oponent_protocol_ver = 0; m_connection_initialized = false; + m_invoke_buf_ready = 0; + m_invoke_result_code = LEVIN_ERROR_CONNECTION; } virtual ~async_protocol_handler() { diff --git a/contrib/epee/src/network_throttle-detail.cpp b/contrib/epee/src/network_throttle-detail.cpp index 7eeade3a190..28c85bb7823 100644 --- a/contrib/epee/src/network_throttle-detail.cpp +++ b/contrib/epee/src/network_throttle-detail.cpp @@ -146,6 +146,7 @@ network_throttle::network_throttle(const std::string &nameshort, const std::stri m_network_add_cost = 128; m_network_minimal_segment = 256; m_network_max_segment = 1024*1024; + m_start_time = 0; m_any_packet_yet = false; m_slot_size = 1.0; // hard coded in few places m_target_speed = 16 * 1024; // other defaults are probably defined in the command-line parsing code when this class is used e.g. as main global throttle From 7f2ad1a768cd89c3e873c8d1750fe72f4ec47003 Mon Sep 17 00:00:00 2001 From: iDunk5400 Date: Wed, 3 Oct 2018 01:06:03 +0200 Subject: [PATCH 0519/1404] functional_tests: fix linking on Windows --- tests/functional_tests/CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/functional_tests/CMakeLists.txt b/tests/functional_tests/CMakeLists.txt index 7a2a7fbd18a..4b21b945cd4 100644 --- a/tests/functional_tests/CMakeLists.txt +++ b/tests/functional_tests/CMakeLists.txt @@ -26,6 +26,10 @@ # 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. +if(WIN32) + set(EXTRA_LIBRARIES "${EXTRA_LIBRARIES};bcrypt") +endif() + set(functional_tests_sources main.cpp transactions_flow_test.cpp From 0a9bd1b3c2ed5b6be91e2bd11c5d9219d414b52a Mon Sep 17 00:00:00 2001 From: K3v1n Kur14k053 Date: Wed, 3 Oct 2018 18:41:28 +0530 Subject: [PATCH 0520/1404] Move cross compiling steps into proper heading As per #3430 --- README.md | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index d16b215289e..45954680ef2 100644 --- a/README.md +++ b/README.md @@ -479,7 +479,7 @@ Then you can run make as usual. # Get binaries docker cp monero-android:/opt/android/monero/build/release/bin . -### Building portable statically linked binaries (Cross Compiling) +### Building portable statically linked binaries By default, in either dynamically or statically linked builds, binaries target the specific host processor on which the build happens and are not portable to other processors. Portable binaries can be built using the following targets: @@ -491,6 +491,25 @@ By default, in either dynamically or statically linked builds, binaries target t * ```make release-static-win64``` builds binaries on 64-bit Windows portable across 64-bit Windows systems * ```make release-static-win32``` builds binaries on 64-bit or 32-bit Windows portable across 32-bit Windows systems +### Cross Compiling + +You can also cross-compile static binaries on Linux for Windows and macOS with the `depends` system. Go to `contrib/depends` and type: + +* ```make HOST=x86_64-linux-gnu``` for 64-bit linux binaries. +* ```make HOST=x86_64-w64-mingw32``` for 64-bit windows binaries. Requires: python3 nsis g++-mingw-w64-x86-64 wine1.6 bc +* ```make HOST=x86_64-apple-darwin11``` for darwin binaries. Requires: cmake imagemagick libcap-dev librsvg2-bin libz-dev libbz2-dev libtiff-tools python-dev +* ```make HOST=i686-linux-gnu``` for 32-bit linux binaries. Requires: g++-multilib bc +* ```make HOST=i686-w64-mingw32``` for 32-bit windows binaries. Requires: python3 nsis g++-mingw-w64-i686 +* ```make HOST=arm-linux-gnueabihf``` for armv6 binaries. Requires: g++-arm-linux-gnueabihf + +The required packages are the names for each toolchain on apt. Depending on your distro, they may have different names. +Then go back to the source dir and type: + +* ```cmake -DCMAKE_TOOLCHAIN_FILE=`pwd`/contrib/depends//share/toolchain.cmake``` +Where is one of the above mentioned targets. + +Using `depends` might also be easier to compile Monero on Windows than using MSys. Activate Windows Subsystem for Linux (WSL) with a distro (for example Ubuntu), install the apt build-essentials and follow the `depends` steps as depicted above. + ## Installing Monero from a package **DISCLAIMER: These packages are not part of this repository or maintained by this project's contributors, and as such, do not go through the same review process to ensure their trustworthiness and security.** @@ -534,22 +553,6 @@ Installing a snap is very quick. Snaps are secure. They are isolated with all of Packaging for your favorite distribution would be a welcome contribution! -You can also cross-compile binaries on linux for windows and macos with the depends system. Go to contrib/depends and type: - -* ```make HOST=x86_64-linux-gnu``` for 64-bit linux binaries. -* ```make HOST=x86_64-w64-mingw32``` for 64-bit windows binaries. Requires: python3 nsis g++-mingw-w64-x86-64 wine1.6 bc -* ```make HOST=x86_64-apple-darwin11``` for darwin binaries. Requires: cmake imagemagick libcap-dev librsvg2-bin libz-dev libbz2-dev libtiff-tools python-dev -* ```make HOST=i686-linux-gnu``` for 32-bit linux binaries. Requires: g++-multilib bc -* ```make HOST=i686-w64-mingw32``` for 32-bit windows binaries. Requires: python3 nsis g++-mingw-w64-i686 -* ```make HOST=arm-linux-gnueabihf``` for armv6 binaries. Requires: g++-arm-linux-gnueabihf - -The required packages are the names for each toolchain on apt. Depending on your distro, they may have different names. -Then go back to the source dir and type for example for windows 64bit: - -* ```cmake -DCMAKE_TOOLCHAIN_FILE=`pwd`/contrib/depends/x86_64-w64-mingw32``` - -Using depends might also be easier to compile monero on windows than using msys. Activate windows subsystem for linux (for example ubuntu) install the apt build-essentials and follow the depends steps as depicted above. - ## Running monerod The build places the binary in `bin/` sub-directory within the build directory From 34a85e0cc395a9e8995ab46100fe5c6e5f9f1fd8 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 3 Oct 2018 20:43:46 +0000 Subject: [PATCH 0521/1404] wallet2: disable height based segregation It can still be enable via DNS if a key reusing fork pops up --- src/wallet/wallet2.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index e6ab107563c..5bdc75b1360 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -114,9 +114,9 @@ using namespace cryptonote; #define OUTPUT_EXPORT_FILE_MAGIC "Monero output export\003" -#define SEGREGATION_FORK_HEIGHT 1546000 -#define TESTNET_SEGREGATION_FORK_HEIGHT 1000000 -#define STAGENET_SEGREGATION_FORK_HEIGHT 1000000 +#define SEGREGATION_FORK_HEIGHT 99999999 +#define TESTNET_SEGREGATION_FORK_HEIGHT 99999999 +#define STAGENET_SEGREGATION_FORK_HEIGHT 99999999 #define SEGREGATION_FORK_VICINITY 1500 /* blocks */ #define FIRST_REFRESH_GRANULARITY 1024 From 02d3ef7bdae7d1dd7a9a6f4fee24631cf9403278 Mon Sep 17 00:00:00 2001 From: xiphon Date: Thu, 4 Oct 2018 00:01:09 +0000 Subject: [PATCH 0522/1404] blocks: use auto-generated .c files instead of 'LD -r -b binary' --- CMakeLists.txt | 5 +- src/blockchain_utilities/CMakeLists.txt | 20 +---- .../blockchain_import.cpp | 8 +- src/blocks/CMakeLists.txt | 35 ++++---- src/blocks/blockexports.c | 87 ------------------- src/blocks/blocks.cpp | 31 +++++++ src/blocks/blocks.dat | 0 src/blocks/blocks.h | 14 ++- src/cryptonote_config.h | 1 + src/cryptonote_core/CMakeLists.txt | 7 -- src/cryptonote_core/blockchain.cpp | 29 +++---- src/cryptonote_core/blockchain.h | 18 +++- src/cryptonote_core/cryptonote_core.cpp | 4 +- src/cryptonote_core/cryptonote_core.h | 3 +- src/daemon/CMakeLists.txt | 21 +---- src/daemon/core.h | 8 +- src/device/CMakeLists.txt | 7 -- 17 files changed, 113 insertions(+), 185 deletions(-) delete mode 100644 src/blocks/blockexports.c create mode 100644 src/blocks/blocks.cpp delete mode 100644 src/blocks/blocks.dat diff --git a/CMakeLists.txt b/CMakeLists.txt index c31c14350c9..829a7e96c13 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -180,6 +180,9 @@ set(PER_BLOCK_CHECKPOINT 1) if(PER_BLOCK_CHECKPOINT) add_definitions("-DPER_BLOCK_CHECKPOINT") + set(Blocks "blocks") +else() + set(Blocks "") endif() list(INSERT CMAKE_MODULE_PATH 0 @@ -655,12 +658,10 @@ else() add_linker_flag_if_supported(-Wl,-z,noexecstack noexecstack_SUPPORTED) if (noexecstack_SUPPORTED) set(LD_SECURITY_FLAGS "${LD_SECURITY_FLAGS} -Wl,-z,noexecstack") - set(LD_RAW_FLAGS ${LD_RAW_FLAGS} -z noexecstack) endif() add_linker_flag_if_supported(-Wl,-z,noexecheap noexecheap_SUPPORTED) if (noexecheap_SUPPORTED) set(LD_SECURITY_FLAGS "${LD_SECURITY_FLAGS} -Wl,-z,noexecheap") - set(LD_RAW_FLAGS ${LD_RAW_FLAGS} -z noexecheap) endif() # some windows linker bits diff --git a/src/blockchain_utilities/CMakeLists.txt b/src/blockchain_utilities/CMakeLists.txt index ecd7b754c91..873bf552cd2 100644 --- a/src/blockchain_utilities/CMakeLists.txt +++ b/src/blockchain_utilities/CMakeLists.txt @@ -26,20 +26,6 @@ # 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. -set(blocksdat "") -if(PER_BLOCK_CHECKPOINT) - if(APPLE AND DEPENDS) - add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && touch stub.c && ${CMAKE_C_COMPILER} --target=x86_64-apple-darwin11 -o stub.o -c stub.c COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} -r -sectcreate __DATA __blocks_dat ../blocks/checkpoints.dat -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o stub.o && rm -f stub.*) - elseif(APPLE AND NOT DEPENDS) - add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && touch stub.c && ${CMAKE_C_COMPILER} -o stub.o -c stub.c COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} ${LD_RAW_FLAGS} -r -sectcreate __DATA __blocks_dat ../blocks/checkpoints.dat -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o stub.o && rm -f stub.*) - elseif(LINUX_32) - add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && cp ../blocks/checkpoints.dat blocks.dat && ${CMAKE_LINKER} -m elf_i386 ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o blocks.dat && rm -f blocks.dat) - else() - add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && cp ../blocks/checkpoints.dat blocks.dat && ${CMAKE_LINKER} ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o blocks.dat && rm -f blocks.dat) - endif() - set(blocksdat "blocksdat.o") -endif() - set(blockchain_import_sources blockchain_import.cpp bootstrap_file.cpp @@ -119,8 +105,7 @@ monero_private_headers(blockchain_depth monero_add_executable(blockchain_import ${blockchain_import_sources} - ${blockchain_import_private_headers} - ${blocksdat}) + ${blockchain_import_private_headers}) target_link_libraries(blockchain_import PRIVATE @@ -132,7 +117,8 @@ target_link_libraries(blockchain_import ${Boost_SYSTEM_LIBRARY} ${Boost_THREAD_LIBRARY} ${CMAKE_THREAD_LIBS_INIT} - ${EXTRA_LIBRARIES}) + ${EXTRA_LIBRARIES} + ${Blocks}) if(ARCH_WIDTH) target_compile_definitions(blockchain_import diff --git a/src/blockchain_utilities/blockchain_import.cpp b/src/blockchain_utilities/blockchain_import.cpp index 9ec768d2604..134969dc46e 100644 --- a/src/blockchain_utilities/blockchain_import.cpp +++ b/src/blockchain_utilities/blockchain_import.cpp @@ -37,6 +37,7 @@ #include "misc_log_ex.h" #include "bootstrap_file.h" #include "bootstrap_serialization.h" +#include "blocks/blocks.h" #include "cryptonote_basic/cryptonote_format_utils.h" #include "serialization/binary_utils.h" // dump_binary(), parse_binary() #include "serialization/json_utils.h" // dump_json() @@ -758,7 +759,12 @@ int main(int argc, char* argv[]) { core.disable_dns_checkpoints(true); - if (!core.init(vm, NULL)) +#if defined(PER_BLOCK_CHECKPOINT) + GetCheckpointsCallback get_checkpoints = blocks::GetCheckpointsData; +#else + GetCheckpointsCallback get_checkpoints = nullptr; +#endif + if (!core.init(vm, nullptr, nullptr, get_checkpoints)) { std::cerr << "Failed to initialize core" << ENDL; return 1; diff --git a/src/blocks/CMakeLists.txt b/src/blocks/CMakeLists.txt index ebb5408cc2c..bedda3b880b 100644 --- a/src/blocks/CMakeLists.txt +++ b/src/blocks/CMakeLists.txt @@ -26,20 +26,23 @@ # 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. -if(APPLE) - add_library(blocks STATIC blockexports.c) - set_target_properties(blocks PROPERTIES LINKER_LANGUAGE C) -else() - if(LINUX_32) - add_custom_command(OUTPUT blocks.o MAIN_DEPENDENCY blocks.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} -m elf_i386 ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/blocks.o blocks.dat) - add_custom_command(OUTPUT testnet_blocks.o MAIN_DEPENDENCY testnet_blocks.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} -m elf_i386 ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/testnet_blocks.o testnet_blocks.dat) - add_custom_command(OUTPUT stagenet_blocks.o MAIN_DEPENDENCY stagenet_blocks.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} -m elf_i386 ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/stagenet_blocks.o stagenet_blocks.dat) - else() - add_custom_command(OUTPUT blocks.o MAIN_DEPENDENCY blocks.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/blocks.o blocks.dat) - add_custom_command(OUTPUT testnet_blocks.o MAIN_DEPENDENCY testnet_blocks.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/testnet_blocks.o testnet_blocks.dat) - add_custom_command(OUTPUT stagenet_blocks.o MAIN_DEPENDENCY stagenet_blocks.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/stagenet_blocks.o stagenet_blocks.dat) - endif() - add_library(blocks STATIC blocks.o testnet_blocks.o stagenet_blocks.o blockexports.c) - set_target_properties(blocks PROPERTIES LINKER_LANGUAGE C) -endif() +set(GENERATED_SOURCES "") +foreach(BLOB_NAME checkpoints testnet_blocks stagenet_blocks) + set(OUTPUT_C_SOURCE "generated_${BLOB_NAME}.c") + list(APPEND GENERATED_SOURCES ${OUTPUT_C_SOURCE}) + set(INPUT_DAT_FILE "${BLOB_NAME}.dat") + add_custom_command( + OUTPUT ${OUTPUT_C_SOURCE} + MAIN_DEPENDENCY ${INPUT_DAT_FILE} + COMMAND + cd ${CMAKE_CURRENT_BINARY_DIR} && + echo "'#include\t'" > ${OUTPUT_C_SOURCE} && + echo -n "'const\tunsigned\tchar\t${BLOB_NAME}[]={'" >> ${OUTPUT_C_SOURCE} && + od -v -An -w1 -tu1 ${CMAKE_CURRENT_SOURCE_DIR}/${INPUT_DAT_FILE} | sed -e "':a;N;$$!ba;s/\\n/,/g'" >> ${OUTPUT_C_SOURCE} && + echo "'};'" >> ${OUTPUT_C_SOURCE} && + echo "'const\tsize_t\t${BLOB_NAME}_len\t=\tsizeof(${BLOB_NAME});'" >> ${OUTPUT_C_SOURCE} + ) +endforeach() + +add_library(blocks STATIC blocks.cpp ${GENERATED_SOURCES}) diff --git a/src/blocks/blockexports.c b/src/blocks/blockexports.c deleted file mode 100644 index 0154b041351..00000000000 --- a/src/blocks/blockexports.c +++ /dev/null @@ -1,87 +0,0 @@ -#include - -#if defined(__APPLE__) -#include -#ifdef BUILD_SHARED_LIBS -#if !defined(__LP64__) -const struct mach_header _mh_execute_header; -#else -const struct mach_header_64 _mh_execute_header; -#endif -#else -#if !defined(__LP64__) -extern const struct mach_header _mh_execute_header; -#else -extern const struct mach_header_64 _mh_execute_header; -#endif -#endif - -const unsigned char *get_blocks_dat_start(int testnet, int stagenet) -{ - size_t size; - if (testnet) - return getsectiondata(&_mh_execute_header, "__DATA", "__testnet_blocks_dat", &size); - else if (stagenet) - return getsectiondata(&_mh_execute_header, "__DATA", "__stagenet_blocks_dat", &size); - else - return getsectiondata(&_mh_execute_header, "__DATA", "__blocks_dat", &size); -} - -size_t get_blocks_dat_size(int testnet, int stagenet) -{ - size_t size; - if (testnet) - getsectiondata(&_mh_execute_header, "__DATA", "__testnet_blocks_dat", &size); - else if (stagenet) - getsectiondata(&_mh_execute_header, "__DATA", "__stagenet_blocks_dat", &size); - else - getsectiondata(&_mh_execute_header, "__DATA", "__blocks_dat", &size); - return size; -} - -#else - -#if defined(_WIN32) && !defined(_WIN64) -#define _binary_blocks_start binary_blocks_dat_start -#define _binary_blocks_end binary_blocks_dat_end -#define _binary_testnet_blocks_start binary_testnet_blocks_dat_start -#define _binary_testnet_blocks_end binary_testnet_blocks_dat_end -#define _binary_stagenet_blocks_start binary_stagenet_blocks_dat_start -#define _binary_stagenet_blocks_end binary_stagenet_blocks_dat_end -#else -#define _binary_blocks_start _binary_blocks_dat_start -#define _binary_blocks_end _binary_blocks_dat_end -#define _binary_testnet_blocks_start _binary_testnet_blocks_dat_start -#define _binary_testnet_blocks_end _binary_testnet_blocks_dat_end -#define _binary_stagenet_blocks_start _binary_stagenet_blocks_dat_start -#define _binary_stagenet_blocks_end _binary_stagenet_blocks_dat_end -#endif - -extern const unsigned char _binary_blocks_start[]; -extern const unsigned char _binary_blocks_end[]; -extern const unsigned char _binary_testnet_blocks_start[]; -extern const unsigned char _binary_testnet_blocks_end[]; -extern const unsigned char _binary_stagenet_blocks_start[]; -extern const unsigned char _binary_stagenet_blocks_end[]; - -const unsigned char *get_blocks_dat_start(int testnet, int stagenet) -{ - if (testnet) - return _binary_testnet_blocks_start; - else if (stagenet) - return _binary_stagenet_blocks_start; - else - return _binary_blocks_start; -} - -size_t get_blocks_dat_size(int testnet, int stagenet) -{ - if (testnet) - return (size_t) (_binary_testnet_blocks_end - _binary_testnet_blocks_start); - else if (stagenet) - return (size_t) (_binary_stagenet_blocks_end - _binary_stagenet_blocks_start); - else - return (size_t) (_binary_blocks_end - _binary_blocks_start); -} - -#endif diff --git a/src/blocks/blocks.cpp b/src/blocks/blocks.cpp new file mode 100644 index 00000000000..0661f8448bd --- /dev/null +++ b/src/blocks/blocks.cpp @@ -0,0 +1,31 @@ +#include "blocks.h" + +#include + +extern const unsigned char checkpoints[]; +extern const size_t checkpoints_len; +extern const unsigned char stagenet_blocks[]; +extern const size_t stagenet_blocks_len; +extern const unsigned char testnet_blocks[]; +extern const size_t testnet_blocks_len; + +namespace blocks +{ + + const std::unordered_map, std::hash> CheckpointsByNetwork = { + {cryptonote::network_type::MAINNET, {checkpoints, checkpoints_len}}, + {cryptonote::network_type::STAGENET, {stagenet_blocks, stagenet_blocks_len}}, + {cryptonote::network_type::TESTNET, {testnet_blocks, testnet_blocks_len}} + }; + + const epee::span GetCheckpointsData(cryptonote::network_type network) + { + const auto it = CheckpointsByNetwork.find(network); + if (it != CheckpointsByNetwork.end()) + { + return it->second; + } + return nullptr; + } + +} diff --git a/src/blocks/blocks.dat b/src/blocks/blocks.dat deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/src/blocks/blocks.h b/src/blocks/blocks.h index ec683f47ef6..14e391319f6 100644 --- a/src/blocks/blocks.h +++ b/src/blocks/blocks.h @@ -1,16 +1,12 @@ #ifndef SRC_BLOCKS_BLOCKS_H_ #define SRC_BLOCKS_BLOCKS_H_ -#ifdef __cplusplus -extern "C" { -#endif +#include "cryptonote_config.h" +#include "span.h" -const unsigned char *get_blocks_dat_start(int testnet, int stagenet); -size_t get_blocks_dat_size(int testnet, int stagenet); - -#ifdef __cplusplus +namespace blocks +{ + const epee::span GetCheckpointsData(cryptonote::network_type network); } -#endif - #endif /* SRC_BLOCKS_BLOCKS_H_ */ diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index a6858ce7cce..c62eeb73828 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -30,6 +30,7 @@ #pragma once +#include #include #include diff --git a/src/cryptonote_core/CMakeLists.txt b/src/cryptonote_core/CMakeLists.txt index 72844db66e0..231489fdbc5 100644 --- a/src/cryptonote_core/CMakeLists.txt +++ b/src/cryptonote_core/CMakeLists.txt @@ -41,12 +41,6 @@ set(cryptonote_core_private_headers tx_pool.h cryptonote_tx_utils.h) -if(PER_BLOCK_CHECKPOINT) - set(Blocks "blocks") -else() - set(Blocks "") -endif() - monero_private_headers(cryptonote_core ${cryptonote_core_private_headers}) monero_add_library(cryptonote_core @@ -69,5 +63,4 @@ target_link_libraries(cryptonote_core ${Boost_SYSTEM_LIBRARY} ${Boost_THREAD_LIBRARY} PRIVATE - ${Blocks} ${EXTRA_LIBRARIES}) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index eb869b79561..6c6e024e4ad 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -53,9 +53,6 @@ #include "ringct/rctSigs.h" #include "common/perf_timer.h" #include "common/notify.h" -#if defined(PER_BLOCK_CHECKPOINT) -#include "blocks/blocks.h" -#endif #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "blockchain" @@ -341,7 +338,7 @@ uint64_t Blockchain::get_current_blockchain_height() const //------------------------------------------------------------------ //FIXME: possibly move this into the constructor, to avoid accidentally // dereferencing a null BlockchainDB pointer -bool Blockchain::init(BlockchainDB* db, const network_type nettype, bool offline, const cryptonote::test_options *test_options, difficulty_type fixed_difficulty) +bool Blockchain::init(BlockchainDB* db, const network_type nettype, bool offline, const cryptonote::test_options *test_options, difficulty_type fixed_difficulty, const GetCheckpointsCallback get_checkpoints/* = nullptr*/) { LOG_PRINT_L3("Blockchain::" << __func__); CRITICAL_REGION_LOCAL(m_tx_pool); @@ -439,7 +436,7 @@ bool Blockchain::init(BlockchainDB* db, const network_type nettype, bool offline #if defined(PER_BLOCK_CHECKPOINT) if (m_nettype != FAKECHAIN) - load_compiled_in_block_hashes(); + load_compiled_in_block_hashes(get_checkpoints); #endif MINFO("Blockchain initialized. last block: " << m_db->height() - 1 << ", " << epee::misc_utils::get_time_interval_string(timestamp_diff) << " time ago, current difficulty: " << get_difficulty_for_next_block()); @@ -4404,19 +4401,21 @@ void Blockchain::cancel() #if defined(PER_BLOCK_CHECKPOINT) static const char expected_block_hashes_hash[] = "954cb2bbfa2fe6f74b2cdd22a1a4c767aea249ad47ad4f7c9445f0f03260f511"; -void Blockchain::load_compiled_in_block_hashes() +void Blockchain::load_compiled_in_block_hashes(const GetCheckpointsCallback get_checkpoints) { - const bool testnet = m_nettype == TESTNET; - const bool stagenet = m_nettype == STAGENET; - if (m_fast_sync && get_blocks_dat_start(testnet, stagenet) != nullptr && get_blocks_dat_size(testnet, stagenet) > 0) + if (get_checkpoints == nullptr || !m_fast_sync) { - MINFO("Loading precomputed blocks (" << get_blocks_dat_size(testnet, stagenet) << " bytes)"); - + return; + } + const epee::span checkpoints = get_checkpoints(m_nettype); + if (!checkpoints.empty()) + { + MINFO("Loading precomputed blocks (" << checkpoints.size() << " bytes)"); if (m_nettype == MAINNET) { // first check hash crypto::hash hash; - if (!tools::sha256sum(get_blocks_dat_start(testnet, stagenet), get_blocks_dat_size(testnet, stagenet), hash)) + if (!tools::sha256sum(checkpoints.data(), checkpoints.size(), hash)) { MERROR("Failed to hash precomputed blocks data"); return; @@ -4436,9 +4435,9 @@ void Blockchain::load_compiled_in_block_hashes() } } - if (get_blocks_dat_size(testnet, stagenet) > 4) + if (checkpoints.size() > 4) { - const unsigned char *p = get_blocks_dat_start(testnet, stagenet); + const unsigned char *p = checkpoints.data(); const uint32_t nblocks = *p | ((*(p+1))<<8) | ((*(p+2))<<16) | ((*(p+3))<<24); if (nblocks > (std::numeric_limits::max() - 4) / sizeof(hash)) { @@ -4446,7 +4445,7 @@ void Blockchain::load_compiled_in_block_hashes() return; } const size_t size_needed = 4 + nblocks * sizeof(crypto::hash); - if(nblocks > 0 && nblocks > (m_db->height() + HASH_OF_HASHES_STEP - 1) / HASH_OF_HASHES_STEP && get_blocks_dat_size(testnet, stagenet) >= size_needed) + if(nblocks > 0 && nblocks > (m_db->height() + HASH_OF_HASHES_STEP - 1) / HASH_OF_HASHES_STEP && checkpoints.size() >= size_needed) { p += sizeof(uint32_t); m_blocks_hash_of_hashes.reserve(nblocks); diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index ab66fac8b8e..9b67765b5ab 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -38,9 +38,11 @@ #include #include #include +#include #include #include +#include "span.h" #include "syncobj.h" #include "string_tools.h" #include "cryptonote_basic/cryptonote_basic.h" @@ -73,6 +75,15 @@ namespace cryptonote db_nosync //!< Leave syncing up to the backing db (safest, but slowest because of disk I/O) }; + /** + * @brief Callback routine that returns checkpoints data for specific network type + * + * @param network network type + * + * @return checkpoints data, empty span if there ain't any checkpoints for specific network type + */ + typedef std::function(cryptonote::network_type network)> GetCheckpointsCallback; + /************************************************************************/ /* */ /************************************************************************/ @@ -117,10 +128,11 @@ namespace cryptonote * @param offline true if running offline, else false * @param test_options test parameters * @param fixed_difficulty fixed difficulty for testing purposes; 0 means disabled + * @param get_checkpoints if set, will be called to get checkpoints data * * @return true on success, false if any initialization steps fail */ - bool init(BlockchainDB* db, const network_type nettype = MAINNET, bool offline = false, const cryptonote::test_options *test_options = NULL, difficulty_type fixed_difficulty = 0); + bool init(BlockchainDB* db, const network_type nettype = MAINNET, bool offline = false, const cryptonote::test_options *test_options = NULL, difficulty_type fixed_difficulty = 0, const GetCheckpointsCallback get_checkpoints = nullptr); /** * @brief Initialize the Blockchain state @@ -1369,8 +1381,10 @@ namespace cryptonote * A (possibly empty) set of block hashes can be compiled into the * monero daemon binary. This function loads those hashes into * a useful state. + * + * @param get_checkpoints if set, will be called to get checkpoints data */ - void load_compiled_in_block_hashes(); + void load_compiled_in_block_hashes(const GetCheckpointsCallback get_checkpoints); /** * @brief expands v2 transaction data from blockchain diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 69e3c708b7a..3882a14ae62 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -389,7 +389,7 @@ namespace cryptonote return m_blockchain_storage.get_alternative_blocks_count(); } //----------------------------------------------------------------------------------------------- - bool core::init(const boost::program_options::variables_map& vm, const char *config_subdir, const cryptonote::test_options *test_options) + bool core::init(const boost::program_options::variables_map& vm, const char *config_subdir, const cryptonote::test_options *test_options, const GetCheckpointsCallback get_checkpoints/* = nullptr */) { start_time = std::time(nullptr); @@ -567,7 +567,7 @@ namespace cryptonote regtest_hard_forks }; const difficulty_type fixed_difficulty = command_line::get_arg(vm, arg_fixed_difficulty); - r = m_blockchain_storage.init(db.release(), m_nettype, m_offline, regtest ? ®test_test_options : test_options, fixed_difficulty); + r = m_blockchain_storage.init(db.release(), m_nettype, m_offline, regtest ? ®test_test_options : test_options, fixed_difficulty, get_checkpoints); r = m_mempool.init(max_txpool_weight); CHECK_AND_ASSERT_MES(r, false, "Failed to initialize memory pool"); diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index 58fe5b7b56e..88256833000 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -244,10 +244,11 @@ namespace cryptonote * @param vm command line parameters * @param config_subdir subdirectory for config storage * @param test_options configuration options for testing + * @param get_checkpoints if set, will be called to get checkpoints data, must return checkpoints data pointer and size or nullptr if there ain't any checkpoints for specific network type * * @return false if one of the init steps fails, otherwise true */ - bool init(const boost::program_options::variables_map& vm, const char *config_subdir = NULL, const test_options *test_options = NULL); + bool init(const boost::program_options::variables_map& vm, const char *config_subdir = NULL, const test_options *test_options = NULL, const GetCheckpointsCallback get_checkpoints = nullptr); /** * @copydoc Blockchain::reset_and_set_genesis_block diff --git a/src/daemon/CMakeLists.txt b/src/daemon/CMakeLists.txt index f645836a4b2..1177904557d 100644 --- a/src/daemon/CMakeLists.txt +++ b/src/daemon/CMakeLists.txt @@ -26,20 +26,6 @@ # 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. -set(blocksdat "") -if(PER_BLOCK_CHECKPOINT) - if(APPLE AND DEPENDS) - add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && touch stub.c && ${CMAKE_C_COMPILER} --target=x86_64-apple-darwin11 -o stub.o -c stub.c COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} -r -sectcreate __DATA __blocks_dat ../blocks/checkpoints.dat -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o stub.o && rm -f stub.*) - elseif(APPLE AND NOT DEPENDS) - add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && touch stub.c && ${CMAKE_C_COMPILER} -o stub.o -c stub.c COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} ${LD_RAW_FLAGS} -r -sectcreate __DATA __blocks_dat ../blocks/checkpoints.dat -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o stub.o && rm -f stub.*) - elseif(LINUX_32) - add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && cp ../blocks/checkpoints.dat blocks.dat && ${CMAKE_LINKER} -m elf_i386 ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o blocks.dat && rm -f blocks.dat) - else() - add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && cp ../blocks/checkpoints.dat blocks.dat && ${CMAKE_LINKER} ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o blocks.dat && rm -f blocks.dat) - endif() - set(blocksdat "blocksdat.o") -endif() - set(daemon_sources command_parser_executor.cpp command_server.cpp @@ -81,9 +67,7 @@ monero_private_headers(daemon monero_add_executable(daemon ${daemon_sources} ${daemon_headers} - ${daemon_private_headers} - ${blocksdat} -) + ${daemon_private_headers}) target_link_libraries(daemon PRIVATE rpc @@ -106,7 +90,8 @@ target_link_libraries(daemon ${CMAKE_THREAD_LIBS_INIT} ${ZMQ_LIB} ${GNU_READLINE_LIBRARY} - ${EXTRA_LIBRARIES}) + ${EXTRA_LIBRARIES} + ${Blocks}) set_property(TARGET daemon PROPERTY OUTPUT_NAME "monerod") diff --git a/src/daemon/core.h b/src/daemon/core.h index 475f418d650..eda8894a6fc 100644 --- a/src/daemon/core.h +++ b/src/daemon/core.h @@ -28,6 +28,7 @@ #pragma once +#include "blocks/blocks.h" #include "cryptonote_core/cryptonote_core.h" #include "cryptonote_protocol/cryptonote_protocol_handler.h" #include "misc_log_ex.h" @@ -85,7 +86,12 @@ class t_core final //initialize core here MGINFO("Initializing core..."); std::string config_subdir = get_config_subdir(); - if (!m_core.init(m_vm_HACK, config_subdir.empty() ? NULL : config_subdir.c_str())) +#if defined(PER_BLOCK_CHECKPOINT) + cryptonote::GetCheckpointsCallback get_checkpoints = blocks::GetCheckpointsData; +#else + cryptonote::GetCheckpointsCallback get_checkpoints = nullptr; +#endif + if (!m_core.init(m_vm_HACK, config_subdir.empty() ? NULL : config_subdir.c_str(), nullptr, get_checkpoints)) { return false; } diff --git a/src/device/CMakeLists.txt b/src/device/CMakeLists.txt index 8f446f42a24..727134f75e0 100644 --- a/src/device/CMakeLists.txt +++ b/src/device/CMakeLists.txt @@ -58,12 +58,6 @@ endif() set(device_private_headers) -if(PER_BLOCK_CHECKPOINT) - set(Blocks "blocks") -else() - set(Blocks "") -endif() - monero_private_headers(device ${device_private_headers}) @@ -79,5 +73,4 @@ target_link_libraries(device ringct_basic ${OPENSSL_CRYPTO_LIBRARIES} PRIVATE - ${Blocks} ${EXTRA_LIBRARIES}) From 5c4fe3d4a5973217f866568907af6ae3a9d2c5eb Mon Sep 17 00:00:00 2001 From: sanecito Date: Thu, 4 Oct 2018 01:43:16 -0700 Subject: [PATCH 0523/1404] Create Japanese file, add translations for "Monero::AddressBookImpl" and "Wallet" Contexts --- translations/monero_ja.ts | 4066 +++++++++++++++++++++++++++++++++++++ 1 file changed, 4066 insertions(+) create mode 100644 translations/monero_ja.ts diff --git a/translations/monero_ja.ts b/translations/monero_ja.ts new file mode 100644 index 00000000000..ac5065d7de4 --- /dev/null +++ b/translations/monero_ja.ts @@ -0,0 +1,4066 @@ + + + + + Monero::AddressBookImpl + + + Invalid destination address + 不正な宛先アドレス + + + + Invalid payment ID. Short payment ID should only be used in an integrated address + 不正なペイメントIDありっます。インテグレーテットアドレスにだけ短いペイメントIDを使えます + + + + Invalid payment ID + 不正なペイメントID + + + + Integrated address and long payment ID can't be used at the same time + 同じ時にインテグレーテットアドレスと長いペイメントIDを使えません + + + + Monero::PendingTransactionImpl + + + Attempting to save transaction to file, but specified file(s) exist. Exiting to not risk overwriting. File: + + + + + Failed to write transaction(s) to file + + + + + daemon is busy. Please try again later. + + + + + no connection to daemon. Please make sure daemon is running. + + + + + transaction %s was rejected by daemon with status: + + + + + . Reason: + + + + + Unknown exception: + + + + + Unhandled exception + + + + + Monero::UnsignedTransactionImpl + + + This is a watch only wallet + + + + + + Failed to sign transaction + + + + + Claimed change does not go to a paid address + + + + + Claimed change is larger than payment to the change address + + + + + Change goes to more than one address + + + + + sending %s to %s + + + + + with no destinations + + + + + %s change to %s + + + + + no change + + + + + Loaded %lu transactions, for %s, fee %s, %s, %s, with min ring size %lu. %s + + + + + Monero::WalletImpl + + + payment id has invalid format, expected 16 or 64 character hex string: + + + + + Failed to add short payment id: + + + + + + daemon is busy. Please try again later. + + + + + + no connection to daemon. Please make sure daemon is running. + + + + + + RPC error: + + + + + + not enough outputs for specified ring size + + + + + + found outputs to use + + + + + Please sweep unmixable outputs. + + + + + failed to get random outputs to mix + + + + + + not enough money to transfer, available only %s, sent amount %s + + + + + failed to parse address + + + + + failed to parse secret spend key + + + + + No view key supplied, cancelled + + + + + failed to parse secret view key + + + + + failed to verify secret spend key + + + + + spend key does not match address + + + + + failed to verify secret view key + + + + + view key does not match address + + + + + failed to generate new wallet: + + + + + Failed to send import wallet request + + + + + Failed to load unsigned transactions + + + + + Failed to load transaction from file + + + + + Wallet is view only + + + + + failed to save file + + + + + Key images can only be imported with a trusted daemon + + + + + Failed to import key images: + + + + + Failed to get subaddress label: + + + + + Failed to set subaddress label: + + + + + failed to get random outputs to mix: %s + + + + + + not enough money to transfer, overall balance only %s, sent amount %s + + + + + + not enough money to transfer, available only %s, transaction amount %s = %s + %s (fee) + + + + + + output amount + + + + + + transaction was not constructed + + + + + + transaction %s was rejected by daemon with status: + + + + + + one of destinations is zero + + + + + + failed to find a suitable way to split transactions + + + + + + unknown transfer error: + + + + + + internal error: + + + + + + unexpected error: + + + + + + unknown error + + + + + + + + + + Failed to parse txid + + + + + no tx keys found for this txid + + + + + + Failed to parse tx key + + + + + + + + Failed to parse address + + + + + Address must not be a subaddress + + + + + Rescan spent can only be used with a trusted daemon + + + + + Wallet + + + Failed to parse address + + + + + Failed to parse key + + + + + failed to verify key + + + + + key does not match address + + + + + command_line + + + yes + + + + + no + + + + + cryptonote::rpc_args + + + Specify IP to bind RPC server + + + + + Specify username[:password] required for RPC server + + + + + Confirm rpc-bind-ip value is NOT a loopback (local) IP + + + + + Specify a comma separated list of origins to allow cross origin resource sharing + + + + + Invalid IP address given for -- + + + + + permits inbound unencrypted external connections. Consider SSH tunnel or SSL proxy instead. Override with -- + + + + + Username specified with -- + + + + + + cannot be empty + + + + + requires RPC server password -- + + + + + cryptonote::simple_wallet + + + Commands: + + + + + failed to read wallet password + + + + + invalid password + + + + + set seed: needs an argument. available options: language + + + + + set: unrecognized argument(s) + + + + + wallet file path not valid: + + + + + Attempting to generate or restore wallet, but specified file(s) exist. Exiting to not risk overwriting. + + + + + usage: payment_id + + + + + needs an argument + + + + + + + + + + + + + + 0 or 1 + + + + + 0, 1, 2, 3, or 4 + + + + + + unsigned integer + + + + + NOTE: the following 25 words can be used to recover access to your wallet. Write them down and store them somewhere safe and secure. Please do not store them in your email or on file storage services outside of your immediate control. + + + + + + --restore-deterministic-wallet uses --generate-new-wallet, not --wallet-file + + + + + specify a recovery parameter with the --electrum-seed="words list here" + + + + + specify a wallet path with --generate-new-wallet (not --wallet-file) + + + + + wallet failed to connect to daemon: + + + + + Daemon uses a different RPC major version (%u) than the wallet (%u): %s. Either update one of them, or use --allow-mismatched-daemon-version. + + + + + List of available languages for your wallet's seed: + + + + + Enter the number corresponding to the language of your choice: + + + + + You had been using a deprecated version of the wallet. Please use the new seed that we provide. + + + + + + + Generated new wallet: + + + + + + + failed to generate new wallet: + + + + + Opened watch-only wallet + + + + + Opened wallet + + + + + You had been using a deprecated version of the wallet. Please proceed to upgrade your wallet. + + + + + + You had been using a deprecated version of the wallet. Your wallet file format is being upgraded now. + + + + + + failed to load wallet: + + + + + Use the "help" command to see the list of available commands. + + + + + + Wallet data saved + + + + + Mining started in daemon + + + + + mining has NOT been started: + + + + + Mining stopped in daemon + + + + + mining has NOT been stopped: + + + + + Blockchain saved + + + + + + + Height + + + + + transaction + + + + + spent + + + + + unsupported transaction format + + + + + Starting refresh... + + + + + Refresh done, blocks received: + + + + + + payment id has invalid format, expected 16 or 64 character hex string: + + + + + bad locked_blocks parameter: + + + + + + + a single transaction cannot use more than one payment id: + + + + + + + + failed to set up payment id, though it was decoded correctly + + + + + + + + + + + + transaction cancelled. + + + + + + Is this okay anyway? (Y/Yes/N/No): + + + + + There is currently a %u block backlog at that fee level. Is this okay? (Y/Yes/N/No): + + + + + Failed to check for backlog: + + + + + + +Transaction + + + + + + Spending from address index %d + + + + + + + WARNING: Outputs of multiple addresses are being used together, which might potentially compromise your privacy. + + + + + + Sending %s. + + + + + Your transaction needs to be split into %llu transactions. This will result in a transaction fee being applied to each transaction, for a total fee of %s + + + + + The transaction fee is %s + + + + + , of which %s is dust from change + + + + + . + + + + + A total of %s from dust change will be sent to dust address + + + + + . +This transaction will unlock on block %llu, in approximately %s days (assuming 2 minutes per block) + + + + + + + + + + + + Failed to write transaction(s) to file + + + + + + + + + + + + Unsigned transaction(s) successfully written to file: + + + + + No unmixable outputs found + + + + + No address given + + + + + failed to parse Payment ID + + + + + usage: sweep_single [<priority>] [<ring_size>] <key_image> <address> [<payment_id>] + + + + + failed to parse key image + + + + + No outputs found + + + + + Multiple transactions are created, which is not supposed to happen + + + + + The transaction uses multiple or no inputs, which is not supposed to happen + + + + + missing threshold amount + + + + + invalid amount threshold + + + + + donations are not enabled on the testnet + + + + + usage: donate [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <amount> [<payment_id>] + + + + + Claimed change does not go to a paid address + + + + + Claimed change is larger than payment to the change address + + + + + sending %s to %s + + + + + dummy output(s) + + + + + with no destinations + + + + + Loaded %lu transactions, for %s, fee %s, %s, %s, with min ring size %lu, %s. %sIs this okay? (Y/Yes/N/No): + + + + + This is a multisig wallet, it can only sign with sign_multisig + + + + + usage: sign_transfer [export] + + + + + Failed to sign transaction + + + + + Failed to sign transaction: + + + + + Transaction raw hex data exported to + + + + + Failed to load transaction from file + + + + + + RPC error: + + + + + wallet is watch-only and has no spend key + + + + + + + Your original password was incorrect. + + + + + Error with wallet rewrite: + + + + + priority must be 0, 1, 2, 3, or 4 + + + + + + priority must be 0, 1, 2, 3, or 4 + + + + + invalid unit + + + + + + invalid count: must be an unsigned integer + + + + + invalid value + + + + + usage: set_log <log_level_number_0-4> | <categories> + + + + + (Y/Yes/N/No): + + + + + + bad m_restore_height parameter: + + + + + date format must be YYYY-MM-DD + + + + + Restore height is: + + + + + + Is this okay? (Y/Yes/N/No): + + + + + Daemon is local, assuming trusted + + + + + Password for new watch-only wallet + + + + + invalid arguments. Please use start_mining [<number_of_threads>] [do_bg_mining] [ignore_battery], <number_of_threads> should be from 1 to + + + + + internal error: + + + + + + + unexpected error: + + + + + + + + + + + + + unknown error + + + + + refresh failed: + + + + + Blocks received: + + + + + unlocked balance: + + + + + + + amount + + + + + false + + + + + Unknown command: + + + + + Command usage: + + + + + Command description: + + + + + wallet is multisig but not yet finalized + + + + + Enter optional seed encryption passphrase, empty to see raw seed + + + + + Failed to retrieve seed + + + + + wallet is multisig and has no seed + + + + + Cannot connect to daemon + + + + + Current fee is %s monero per kB + + + + + Error: failed to estimate backlog array size: + + + + + Error: bad estimated backlog array size + + + + + (current) + + + + + %u block (%u minutes) backlog at priority %u%s + + + + + %u to %u block (%u to %u minutes) backlog at priority %u + + + + + No backlog at priority + + + + + + This wallet is already multisig + + + + + + wallet is watch-only and cannot be made multisig + + + + + + This wallet has been used before, please use a new wallet to create a multisig wallet + + + + + Your password is incorrect. + + + + + Send this multisig info to all other participants, then use make_multisig <threshold> <info1> [<info2>...] with others' multisig info + + + + + This includes the PRIVATE view key, so needs to be disclosed only to that multisig wallet's participants + + + + + usage: make_multisig <threshold> <multisiginfo1> [<multisiginfo2>...] + + + + + Invalid threshold + + + + + Another step is needed + + + + + Send this multisig info to all other participants, then use finalize_multisig <info1> [<info2>...] with others' multisig info + + + + + Error creating multisig: + + + + + Error creating multisig: new wallet is not multisig + + + + + multisig address: + + + + + + + This wallet is not multisig + + + + + This wallet is already finalized + + + + + usage: finalize_multisig <multisiginfo1> [<multisiginfo2>...] + + + + + Failed to finalize multisig + + + + + Failed to finalize multisig: + + + + + + + + + This multisig wallet is not yet finalized + + + + + usage: export_multisig_info <filename> + + + + + Error exporting multisig info: + + + + + Multisig info exported to + + + + + usage: import_multisig_info <filename1> [<filename2>...] - one for each other participant + + + + + Multisig info imported + + + + + Failed to import multisig info: + + + + + Failed to update spent status after importing multisig info: + + + + + Untrusted daemon, spent status may be incorrect. Use a trusted daemon and run "rescan_spent" + + + + + + + This is not a multisig wallet + + + + + usage: sign_multisig <filename> + + + + + Failed to sign multisig transaction + + + + + Multisig error: + + + + + Failed to sign multisig transaction: + + + + + It may be relayed to the network with submit_multisig + + + + + usage: submit_multisig <filename> + + + + + + Failed to load multisig transaction from file + + + + + + Multisig transaction signed by only %u signers, needs %u more signatures + + + + + + Transaction successfully submitted, transaction + + + + + + You can check its status by using the `show_transfers` command. + + + + + usage: export_raw_multisig <filename> + + + + + Failed to export multisig transaction to file + + + + + Saved exported multisig transaction file(s): + + + + + + + ring size must be an integer >= + + + + + could not change default ring size + + + + + Invalid height + + + + + start_mining [<number_of_threads>] [bg_mining] [ignore_battery] + + + + + Start mining in the daemon (bg_mining and ignore_battery are optional booleans). + + + + + Stop mining in the daemon. + + + + + set_daemon <host>[:<port>] + + + + + Set another daemon to connect to. + + + + + Save the current blockchain data. + + + + + Synchronize the transactions and balance. + + + + + balance [detail] + + + + + Show the wallet's balance of the currently selected account. + + + + + incoming_transfers [available|unavailable] [verbose] [index=<N1>[,<N2>[,...]]] + + + + + Show the incoming transfers, all or filtered by availability and address index. + + + + + payments <PID_1> [<PID_2> ... <PID_N>] + + + + + Show the payments for the given payment IDs. + + + + + Show the blockchain height. + + + + + transfer_original [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <address> <amount> [<payment_id>] + + + + + Transfer <amount> to <address> using an older transaction building algorithm. If the parameter "index=<N1>[,<N2>,...]" is specified, the wallet uses outputs received by addresses of those indices. If omitted, the wallet randomly chooses address indices to be used. In any case, it tries its best not to combine outputs across multiple addresses. <priority> is the priority of the transaction. The higher the priority, the higher the transaction fee. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command "set priority") is used. <ring_size> is the number of inputs to include for untraceability. Multiple payments can be made at once by adding <address_2> <amount_2> etcetera (before the payment ID, if it's included) + + + + + transfer [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <address> <amount> [<payment_id>] + + + + + Transfer <amount> to <address>. If the parameter "index=<N1>[,<N2>,...]" is specified, the wallet uses outputs received by addresses of those indices. If omitted, the wallet randomly chooses address indices to be used. In any case, it tries its best not to combine outputs across multiple addresses. <priority> is the priority of the transaction. The higher the priority, the higher the transaction fee. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command "set priority") is used. <ring_size> is the number of inputs to include for untraceability. Multiple payments can be made at once by adding <address_2> <amount_2> etcetera (before the payment ID, if it's included) + + + + + locked_transfer [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <addr> <amount> <lockblocks> [<payment_id>] + + + + + Transfer <amount> to <address> and lock it for <lockblocks> (max. 1000000). If the parameter "index=<N1>[,<N2>,...]" is specified, the wallet uses outputs received by addresses of those indices. If omitted, the wallet randomly chooses address indices to be used. In any case, it tries its best not to combine outputs across multiple addresses. <priority> is the priority of the transaction. The higher the priority, the higher the transaction fee. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command "set priority") is used. <ring_size> is the number of inputs to include for untraceability. Multiple payments can be made at once by adding <address_2> <amount_2> etcetera (before the payment ID, if it's included) + + + + + Send all unmixable outputs to yourself with ring_size 1 + + + + + sweep_all [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <address> [<payment_id>] + + + + + Send all unlocked balance to an address. If the parameter "index<N1>[,<N2>,...]" is specified, the wallet sweeps outputs received by those address indices. If omitted, the wallet randomly chooses an address index to be used. + + + + + sweep_below <amount_threshold> [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <address> [<payment_id>] + + + + + Send all unlocked outputs below the threshold to an address. + + + + + sweep_single [<priority>] [<ring_size>] <key_image> <address> [<payment_id>] + + + + + Send a single output of the given key image to an address without change. + + + + + donate [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <amount> [<payment_id>] + + + + + Donate <amount> to the development team (donate.getmonero.org). + + + + + sign_transfer <file> + + + + + Sign a transaction from a <file>. + + + + + Submit a signed transaction from a file. + + + + + set_log <level>|{+,-,}<categories> + + + + + Change the current log detail (level must be <0-4>). + + + + + account + account new <label text with white spaces allowed> + account switch <index> + account label <index> <label text with white spaces allowed> + account tag <tag_name> <account_index_1> [<account_index_2> ...] + account untag <account_index_1> [<account_index_2> ...] + account tag_description <tag_name> <description> + + + + + If no arguments are specified, the wallet shows all the existing accounts along with their balances. +If the "new" argument is specified, the wallet creates a new account with its label initialized by the provided label text (which can be empty). +If the "switch" argument is specified, the wallet switches to the account specified by <index>. +If the "label" argument is specified, the wallet sets the label of the account specified by <index> to the provided label text. +If the "tag" argument is specified, a tag <tag_name> is assigned to the specified accounts <account_index_1>, <account_index_2>, .... +If the "untag" argument is specified, the tags assigned to the specified accounts <account_index_1>, <account_index_2> ..., are removed. +If the "tag_description" argument is specified, the tag <tag_name> is assigned an arbitrary text <description>. + + + + + address [ new <label text with white spaces allowed> | all | <index_min> [<index_max>] | label <index> <label text with white spaces allowed>] + + + + + If no arguments are specified or <index> is specified, the wallet shows the default or specified address. If "all" is specified, the wallet shows all the existing addresses in the currently selected account. If "new " is specified, the wallet creates a new address with the provided label text (which can be empty). If "label" is specified, the wallet sets the label of the address specified by <index> to the provided label text. + + + + + integrated_address [<payment_id> | <address>] + + + + + Encode a payment ID into an integrated address for the current wallet public address (no argument uses a random payment ID), or decode an integrated address to standard address and payment ID + + + + + address_book [(add ((<address> [pid <id>])|<integrated address>) [<description possibly with whitespaces>])|(delete <index>)] + + + + + Print all entries in the address book, optionally adding/deleting an entry to/from it. + + + + + Save the wallet data. + + + + + Save a watch-only keys file. + + + + + Display the private view key. + + + + + Display the private spend key. + + + + + Display the Electrum-style mnemonic seed + + + + + set <option> [<value>] + + + + + Available options: + seed language + Set the wallet's seed language. + always-confirm-transfers <1|0> + Whether to confirm unsplit txes. + print-ring-members <1|0> + Whether to print detailed information about ring members during confirmation. + store-tx-info <1|0> + Whether to store outgoing tx info (destination address, payment ID, tx secret key) for future reference. + default-ring-size <n> + Set the default ring size (default and minimum is 5). + auto-refresh <1|0> + Whether to automatically synchronize new blocks from the daemon. + refresh-type <full|optimize-coinbase|no-coinbase|default> + Set the wallet's refresh behaviour. + priority [0|1|2|3|4] + Set the fee to default/unimportant/normal/elevated/priority. + confirm-missing-payment-id <1|0> + ask-password <1|0> + unit <monero|millinero|micronero|nanonero|piconero> + Set the default monero (sub-)unit. + min-outputs-count [n] + Try to keep at least that many outputs of value at least min-outputs-value. + min-outputs-value [n] + Try to keep at least min-outputs-count outputs of at least that value. + merge-destinations <1|0> + Whether to merge multiple payments to the same destination address. + confirm-backlog <1|0> + Whether to warn if there is transaction backlog. + confirm-backlog-threshold [n] + Set a threshold for confirm-backlog to only warn if the transaction backlog is greater than n blocks. + refresh-from-block-height [n] + Set the height before which to ignore blocks. + auto-low-priority <1|0> + Whether to automatically use the low priority fee level when it's safe to do so. + + + + + Display the encrypted Electrum-style mnemonic seed. + + + + + Rescan the blockchain for spent outputs. + + + + + get_tx_key <txid> + + + + + Get the transaction key (r) for a given <txid>. + + + + + check_tx_key <txid> <txkey> <address> + + + + + Check the amount going to <address> in <txid>. + + + + + get_tx_proof <txid> <address> [<message>] + + + + + Generate a signature proving funds sent to <address> in <txid>, optionally with a challenge string <message>, using either the transaction secret key (when <address> is not your wallet's address) or the view secret key (otherwise), which does not disclose the secret key. + + + + + check_tx_proof <txid> <address> <signature_file> [<message>] + + + + + Check the proof for funds going to <address> in <txid> with the challenge string <message> if any. + + + + + get_spend_proof <txid> [<message>] + + + + + Generate a signature proving that you generated <txid> using the spend secret key, optionally with a challenge string <message>. + + + + + check_spend_proof <txid> <signature_file> [<message>] + + + + + Check a signature proving that the signer generated <txid>, optionally with a challenge string <message>. + + + + + get_reserve_proof (all|<amount>) [<message>] + + + + + Generate a signature proving that you own at least this much, optionally with a challenge string <message>. +If 'all' is specified, you prove the entire sum of all of your existing accounts' balances. +Otherwise, you prove the reserve of the smallest possible amount above <amount> available in your current account. + + + + + check_reserve_proof <address> <signature_file> [<message>] + + + + + Check a signature proving that the owner of <address> holds at least this much, optionally with a challenge string <message>. + + + + + show_transfers [in|out|pending|failed|pool] [index=<N1>[,<N2>,...]] [<min_height> [<max_height>]] + + + + + Show the incoming/outgoing transfers within an optional height range. + + + + + unspent_outputs [index=<N1>[,<N2>,...]] [<min_amount> [<max_amount>]] + + + + + Show the unspent outputs of a specified address within an optional amount range. + + + + + Rescan the blockchain from scratch. + + + + + set_tx_note <txid> [free text note] + + + + + Set an arbitrary string note for a <txid>. + + + + + get_tx_note <txid> + + + + + Get a string note for a txid. + + + + + set_description [free text note] + + + + + Set an arbitrary description for the wallet. + + + + + Get the description of the wallet. + + + + + Show the wallet's status. + + + + + Show the wallet's information. + + + + + sign <file> + + + + + Sign the contents of a file. + + + + + verify <filename> <address> <signature> + + + + + Verify a signature on the contents of a file. + + + + + export_key_images <file> + + + + + Export a signed set of key images to a <file>. + + + + + import_key_images <file> + + + + + Import a signed key images list and verify their spent status. + + + + + export_outputs <file> + + + + + Export a set of outputs owned by this wallet. + + + + + import_outputs <file> + + + + + Import a set of outputs owned by this wallet. + + + + + show_transfer <txid> + + + + + Show information about a transfer to/from this address. + + + + + Change the wallet's password. + + + + + Generate a new random full size payment id. These will be unencrypted on the blockchain, see integrated_address for encrypted short payment ids. + + + + + Print the information about the current fee and transaction backlog. + + + + + Export data needed to create a multisig wallet + + + + + make_multisig <threshold> <string1> [<string>...] + + + + + Turn this wallet into a multisig wallet + + + + + finalize_multisig <string> [<string>...] + + + + + Turn this wallet into a multisig wallet, extra step for N-1/N wallets + + + + + export_multisig_info <filename> + + + + + Export multisig info for other participants + + + + + import_multisig_info <filename> [<filename>...] + + + + + Import multisig info from other participants + + + + + sign_multisig <filename> + + + + + Sign a multisig transaction from a file + + + + + submit_multisig <filename> + + + + + Submit a signed multisig transaction from a file + + + + + export_raw_multisig_tx <filename> + + + + + Export a signed multisig transaction to a file + + + + + help [<command>] + + + + + Show the help section or the documentation about a <command>. + + + + + integer >= + + + + + block height + + + + + No wallet found with that name. Confirm creation of new wallet named: + + + + + can't specify more than one of --generate-new-wallet="wallet_name", --wallet-file="wallet_name", --generate-from-view-key="wallet_name", --generate-from-spend-key="wallet_name", --generate-from-keys="wallet_name", --generate-from-multisig-keys="wallet_name" and --generate-from-json="jsonfilename" + + + + + can't specify both --restore-deterministic-wallet or --restore-multisig-wallet and --non-deterministic + + + + + --restore-multisig-wallet uses --generate-new-wallet, not --wallet-file + + + + + specify a recovery parameter with the --electrum-seed="multisig seed here" + + + + + Multisig seed failed verification + + + + + Enter seed encryption passphrase, empty if none + + + + + + This address is a subaddress which cannot be used here. + + + + + Error: expected M/N, but got: + + + + + Error: expected N > 1 and N <= M, but got: + + + + + Error: M/N is currently unsupported. + + + + + Generating master wallet from %u of %u multisig wallet keys + + + + + failed to parse secret view key + + + + + failed to verify secret view key + + + + + Secret spend key (%u of %u): + + + + + Error: M/N is currently unsupported + + + + + Restore height + + + + + Still apply restore height? (Y/Yes/N/No): + + + + + Warning: using an untrusted daemon at %s, privacy will be lessened + + + + + Daemon either is not started or wrong port was passed. Please make sure daemon is running or change the daemon address using the 'set_daemon' command. + + + + + Your wallet has been generated! +To start synchronizing with the daemon, use the "refresh" command. +Use the "help" command to see the list of available commands. +Use "help <command>" to see a command's documentation. +Always use the "exit" command when closing monero-wallet-cli to save +your current session's state. Otherwise, you might need to synchronize +your wallet again (your wallet keys are NOT at risk in any case). + + + + + + failed to generate new mutlisig wallet + + + + + Generated new %u/%u multisig wallet: + + + + + Opened %u/%u multisig wallet%s + + + + + Use "help <command>" to see a command's documentation. + + + + + + wallet is multisig and cannot save a watch-only version + + + + + missing daemon URL argument + + + + + Unexpected array length - Exited simple_wallet::set_daemon() + + + + + This does not seem to be a valid daemon URL. + + + + + + txid + + + + + + idx + + + + + (Some owned outputs have partial key images - import_multisig_info needed) + + + + + Currently selected account: [ + + + + + ] + + + + + Tag: + + + + + (No tag assigned) + + + + + Balance per address: + + + + + Address + + + + + + Balance + + + + + + Unlocked balance + + + + + Outputs + + + + + + Label + + + + + %8u %6s %21s %21s %7u %21s + + + + + usage: balance [detail] + + + + + + usage: incoming_transfers [available|unavailable] [verbose] [index=<N>] + + + + + spent + + + + + global index + + + + + tx id + + + + + + addr index + + + + + No incoming transfers + + + + + No incoming available transfers + + + + + No incoming unavailable transfers + + + + + expected at least one payment ID + + + + + payment + + + + + transaction + + + + + height + + + + + unlock time + + + + + No payments with id + + + + + + + failed to get blockchain height: + + + + + + + + + failed to connect to the daemon + + + + + +Transaction %llu/%llu: txid=%s + + + + + +Input %llu/%llu: amount=%s + + + + + failed to get output: + + + + + output key's originating block height shouldn't be higher than the blockchain height + + + + + +Originating block heights: + + + + + +| + + + + + + | + + + + + + +Warning: Some input keys being spent are from + + + + + , which can break the anonymity of ring signature. Make sure this is intentional! + + + + + + Ring size must not be 0 + + + + + + ring size %u is too small, minimum is %u + + + + + wrong number of arguments + + + + + + + No payment id is included with this transaction. Is this okay? (Y/Yes/N/No): + + + + + + No outputs found, or daemon is not ready + + + + + Transaction successfully saved to + + + + + + , txid + + + + + Failed to save transaction to + + + + + + Sweeping %s in %llu transactions for a total fee of %s. Is this okay? (Y/Yes/N/No): + + + + + + + Sweeping %s for a total fee of %s. Is this okay? (Y/Yes/N/No): + + + + + Donating + + + + + This is a watch only wallet + + + + + usage: show_transfer <txid> + + + + + Double spend seen on the network: this transaction may or may not end up being mined + + + + + Transaction ID not found + + + + + true + + + + + failed to parse refresh type + + + + + + wallet is watch-only and has no seed + + + + + + wallet is non-deterministic and has no seed + + + + + + wallet is watch-only and cannot transfer + + + + + could not change default priority + + + + + full (slowest, no assumptions); optimize-coinbase (fast, assumes the whole coinbase is paid to a single address); no-coinbase (fastest, assumes we receive no coinbase transaction), default (same as optimize-coinbase) + + + + + monero, millinero, micronero, nanonero, piconero + + + + + Wallet name not valid. Please try again or use Ctrl-C to quit. + + + + + Wallet and key files found, loading... + + + + + Key file found but not wallet file. Regenerating... + + + + + Key file not found. Failed to open wallet: + + + + + Generating new wallet... + + + + + Electrum-style word list failed verification + + + + + + + + + + + + + + No data supplied, cancelled + + + + + + + + + + + + + + + + failed to parse address + + + + + + failed to parse view key secret key + + + + + + failed to verify view key secret key + + + + + + + view key does not match standard address + + + + + + + + + account creation failed + + + + + + + failed to parse spend key secret key + + + + + + failed to verify spend key secret key + + + + + + spend key does not match standard address + + + + + failed to open account + + + + + + + + + wallet is null + + + + + + invalid language choice entered. Please try again. + + + + + + View key: + + + + + You may want to remove the file "%s" and try again + + + + + failed to deinitialize wallet + + + + + + + this command requires a trusted daemon. Enable with --trusted-daemon + + + + + blockchain can't be saved: + + + + + + daemon is busy. Please try again later. + + + + + + no connection to daemon. Please make sure daemon is running. + + + + + refresh error: + + + + + Balance: + + + + + pubkey + + + + + key image + + + + + + unlocked + + + + + ringct + + + + + T + + + + + F + + + + + locked + + + + + RingCT + + + + + - + + + + + payment ID has invalid format, expected 16 or 64 character hex string: + + + + + failed to get spent status + + + + + the same transaction + + + + + blocks that are temporally very close + + + + + Locked blocks too high, max 1000000 (˜4 yrs) + + + + + + Good signature + + + + + + + Bad signature + + + + + usage: integrated_address [payment ID] + + + + + Standard address: + + + + + failed to parse payment ID or address + + + + + usage: address_book [(add (<address> [pid <long or short payment id>])|<integrated address> [<description possibly with whitespaces>])|(delete <index>)] + + + + + failed to parse payment ID + + + + + failed to parse index + + + + + Address book is empty. + + + + + Index: + + + + + + Address: + + + + + Payment ID: + + + + + + Description: + + + + + usage: set_tx_note [txid] free text note + + + + + usage: get_tx_note [txid] + + + + + usage: sign <filename> + + + + + wallet is watch-only and cannot sign + + + + + + + + failed to read file + + + + + usage: check_tx_proof <txid> <address> <signature_file> [<message>] + + + + + + + failed to load signature file + + + + + usage: get_spend_proof <txid> [<message>] + + + + + wallet is watch-only and cannot generate the proof + + + + + usage: check_spend_proof <txid> <signature_file> [<message>] + + + + + usage: get_reserve_proof (all|<amount>) [<message>] + + + + + The reserve proof can be generated only by a full wallet + + + + + usage: check_reserve_proof <address> <signature_file> [<message>] + + + + + Address must not be a subaddress + + + + + Good signature -- total: %s, spent: %s, unspent: %s + + + + + usage: show_transfers [in|out|all|pending|failed] [index=<N1>[,<N2>,...]] [<min_height> [<max_height>]] + + + + + [Double spend seen on the network: this transaction may or may not end up being mined] + + + + + usage: unspent_outputs [index=<N1>[,<N2>,...]] [<min_amount> [<max_amount>]] + + + + + There is no unspent output in the specified address + + + + + (no daemon) + + + + + (out of sync) + + + + + (Untitled account) + + + + + + + + + + failed to parse index: + + + + + + specify an index between 0 and + + + + + usage: + account + account new <label text with white spaces allowed> + account switch <index> + account label <index> <label text with white spaces allowed> + account tag <tag_name> <account_index_1> [<account_index_2> ...] + account untag <account_index_1> [<account_index_2> ...] + account tag_description <tag_name> <description> + + + + + +Grand total: + Balance: + + + + + , unlocked balance: + + + + + Untagged accounts: + + + + + Tag %s is unregistered. + + + + + Accounts with tag: + + + + + Tag's description: + + + + + Account + + + + + %c%8u %6s %21s %21s %21s + + + + + ---------------------------------------------------------------------------------- + + + + + %15s %21s %21s + + + + + Primary address + + + + + (used) + + + + + (Untitled address) + + + + + <index_min> is already out of bound + + + + + <index_max> exceeds the bound + + + + + usage: address [ new <label text with white spaces allowed> | all | <index_min> [<index_max>] | label <index> <label text with white spaces allowed> ] + + + + + + Integrated addresses can only be created for account 0 + + + + + Integrated address: %s, payment ID: %s + + + + + Subaddress: + + + + + usage: get_description + + + + + no description found + + + + + description found: + + + + + Filename: + + + + + Watch only + + + + + %u/%u multisig%s + + + + + Normal + + + + + Type: + + + + + Testnet: + + + + + Yes + + + + + No + + + + + This wallet is multisig and cannot sign + + + + + usage: verify <filename> <address> <signature> + + + + + Bad signature from + + + + + Good signature from + + + + + usage: export_key_images <filename> + + + + + wallet is watch-only and cannot export key images + + + + + + + failed to save file + + + + + Signed key images exported to + + + + + usage: import_key_images <filename> + + + + + usage: export_outputs <filename> + + + + + Outputs exported to + + + + + usage: import_outputs <filename> + + + + + + + + amount is wrong: + + + + + expected number from 0 to + + + + + Sweeping + + + + + Money successfully sent, transaction: + + + + + Change goes to more than one address + + + + + %s change to %s + + + + + no change + + + + + + + Transaction successfully signed to file + + + + + usage: get_tx_key <txid> + + + + + + + + + + + + + failed to parse txid + + + + + Tx key: + + + + + no tx keys found for this txid + + + + + usage: get_tx_proof <txid> <address> [<message>] + + + + + + + signature file saved to: + + + + + + + failed to save signature file + + + + + usage: check_tx_key <txid> <txkey> <address> + + + + + + failed to parse tx key + + + + + + + error: + + + + + + received + + + + + + in txid + + + + + + received nothing in txid + + + + + + WARNING: this transaction is not yet included in the blockchain! + + + + + + This transaction has %u confirmations + + + + + + WARNING: failed to determine number of confirmations! + + + + + bad min_height parameter: + + + + + bad max_height parameter: + + + + + in + + + + + + out + + + + + failed + + + + + pending + + + + + <min_amount> should be smaller than <max_amount> + + + + + +Amount: + + + + + , number of keys: + + + + + + + + + + +Min block height: + + + + + +Max block height: + + + + + +Min amount found: + + + + + +Max amount found: + + + + + +Total count: + + + + + +Bin size: + + + + + +Outputs per *: + + + + + count + ^ + + + + + + | + + + + + + + + + + + +--> block height + + + + + + ^ + + + + + ^ + + + + + + + + + + + wallet + + + + + + Random payment ID: + + + + + Matching integrated address: + + + + + genms + + + Base filename (-1, -2, etc suffixes will be appended as needed) + + + + + Give threshold and participants at once as M/N + + + + + How many participants will share parts of the multisig wallet + + + + + How many signers are required to sign a valid transaction + + + + + Create testnet multisig wallets + + + + + Generating %u %u/%u multisig wallets + + + + + Error verifying multisig extra info + + + + + Error finalizing multisig + + + + + Generated multisig wallets for address + + + + + Error creating multisig wallets: + + + + + This program generates a set of multisig wallets - use this simpler scheme only if all the participants trust each other + + + + + Error: expected N/M, but got: + + + + + + Error: either --scheme or both of --threshold and --participants may be given + + + + + Error: expected N > 1 and N <= M, but got N==%u and M==%d + + + + + Error: --filename-base is required + + + + + Error: unsupported scheme: only N/N and N-1/N are supported + + + + + sw + + + Generate new wallet and save it to <arg> + + + + + Generate incoming-only wallet from view key + + + + + Generate deterministic wallet from spend key + + + + + Generate wallet from private keys + + + + + Generate a master wallet from multisig wallet keys + + + + + Language for mnemonic + + + + + Specify Electrum seed for wallet recovery/creation + + + + + Recover wallet using Electrum-style mnemonic seed + + + + + Recover multisig wallet using Electrum-style mnemonic seed + + + + + Generate non-deterministic view and spend keys + + + + + Enable commands which rely on a trusted daemon + + + + + Allow communicating with a daemon that uses a different RPC version + + + + + Restore from specific blockchain height + + + + + The newly created transaction will not be relayed to the monero network + + + + + daemon is busy. Please try again later. + + + + + possibly lost connection to daemon + + + + + Error: + + + + + This is the command line monero wallet. It needs to connect to a monero +daemon to work correctly. + + + + + Failed to initialize wallet + + + + + tools::wallet2 + + + Use daemon instance at <host>:<port> + + + + + Use daemon instance at host <arg> instead of localhost + + + + + Wallet password file + + + + + Use daemon instance at port <arg> instead of 18081 + + + + + For testnet. Daemon must also be launched with --testnet flag + + + + + Restricts to view-only commands + + + + + can't specify daemon host or port more than once + + + + + can't specify more than one of --password and --password-file + + + + + the password file specified could not be read + + + + + Failed to load file + + + + + Wallet password (escape/quote as needed) + + + + + Specify username[:password] for daemon RPC client + + + + + no password specified; use --prompt-for-password to prompt for a password + + + + + Failed to parse JSON + + + + + Version %u too new, we can only grok up to %u + + + + + failed to parse view key secret key + + + + + + + failed to verify view key secret key + + + + + failed to parse spend key secret key + + + + + + + failed to verify spend key secret key + + + + + Electrum-style word list failed verification + + + + + At least one of Electrum-style word list and private view key and private spend key must be specified + + + + + Both Electrum-style word list and private key(s) specified + + + + + invalid address + + + + + view key does not match standard address + + + + + spend key does not match standard address + + + + + Cannot generate deprecated wallets from JSON + + + + + failed to parse address: + + + + + Address must be specified in order to create watch-only wallet + + + + + failed to generate new wallet: + + + + + + + + + + + + Primary account + + + + + No funds received in this tx. + + + + + failed to read file + + + + + tools::wallet_rpc_server + + + Daemon is local, assuming trusted + + + + + Failed to create directory + + + + + Failed to create directory %s: %s + + + + + Cannot specify -- + + + + + and -- + + + + + Failed to create file + + + + + . Check permissions or remove file + + + + + Error writing to file + + + + + RPC username/password is stored in file + + + + + Tag %s is unregistered. + + + + + Transaction not possible. Available only %s, transaction amount %s = %s + %s (fee) + + + + + This is the RPC monero wallet. It needs to connect to a monero +daemon to work correctly. + + + + + Can't specify more than one of --wallet-file and --generate-from-json + + + + + Must specify --wallet-file or --generate-from-json or --wallet-dir + + + + + Loading wallet... + + + + + + Saving wallet... + + + + + + Successfully saved + + + + + Successfully loaded + + + + + Wallet initialization failed: + + + + + Failed to initialize wallet RPC server + + + + + Starting wallet RPC server + + + + + Failed to run wallet: + + + + + Stopped wallet RPC server + + + + + Failed to save wallet: + + + + + wallet_args + + + + + Wallet options + + + + + Generate wallet from JSON format file + + + + + Use wallet <arg> + + + + + Max number of threads to use for a parallel job + + + + + Specify log file + + + + + Config file + + + + + General options + + + + + This is the command line monero wallet. It needs to connect to a monero +daemon to work correctly. + + + + + Can't find config file + + + + + Logging to: + + + + + Logging to %s + + + + + Usage: + + + + From c5928bdec6e59acbdc4b1a076755e8e7324fce1a Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 4 Oct 2018 13:37:41 +0000 Subject: [PATCH 0524/1404] wallet2_api: fix build with C++14 --- src/wallet/api/wallet.cpp | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index 23692834829..58a6db6893d 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -96,6 +96,9 @@ namespace { throw runtime_error("Multisig wallet is not finalized yet"); } } + void checkMultisigWalletReady(const std::unique_ptr &wallet) { + return checkMultisigWalletReady(wallet.get()); + } void checkMultisigWalletNotReady(const tools::wallet2* wallet) { if (!wallet) { @@ -111,6 +114,9 @@ namespace { throw runtime_error("Multisig wallet is already finalized"); } } + void checkMultisigWalletNotReady(const std::unique_ptr &wallet) { + return checkMultisigWalletNotReady(wallet.get()); + } } struct Wallet2CallbackImpl : public tools::i_wallet2_callback @@ -376,15 +382,15 @@ WalletImpl::WalletImpl(NetworkType nettype, uint64_t kdf_rounds) , m_rebuildWalletCache(false) , m_is_connected(false) { - m_wallet = std::make_unique(static_cast(nettype), kdf_rounds, true); - m_history = std::make_unique(this); - m_wallet2Callback = std::make_unique(this); - m_wallet->callback(m_wallet2Callback); + m_wallet.reset(new tools::wallet2(static_cast(nettype), kdf_rounds, true)); + m_history.reset(new TransactionHistoryImpl(this)); + m_wallet2Callback.reset(new Wallet2CallbackImpl(this)); + m_wallet->callback(m_wallet2Callback.get()); m_refreshThreadDone = false; m_refreshEnabled = false; - m_addressBook = std::make_unique(this); - m_subaddress = std::make_unique(this); - m_subaddressAccount = std::make_unique(this); + m_addressBook.reset(new AddressBookImpl(this)); + m_subaddress.reset(new SubaddressImpl(this)); + m_subaddressAccount.reset(new SubaddressAccountImpl(this)); m_refreshIntervalMillis = DEFAULT_REFRESH_INTERVAL_MILLIS; @@ -399,6 +405,7 @@ WalletImpl::~WalletImpl() { LOG_PRINT_L1(__FUNCTION__); + m_wallet->callback(NULL); // Pause refresh thread - prevents refresh from starting again pauseRefresh(); // Close wallet - stores cache and stops ongoing refresh operation From a061353254826d7c2904081bd80519763f097061 Mon Sep 17 00:00:00 2001 From: fireice-uk Date: Wed, 19 Sep 2018 13:39:17 +0100 Subject: [PATCH 0525/1404] secure_pwd_reader: Add proper Unicode handling [Ryo contribution] --- src/common/password.cpp | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/src/common/password.cpp b/src/common/password.cpp index 5671c4a4e9c..6ff16059561 100644 --- a/src/common/password.cpp +++ b/src/common/password.cpp @@ -56,8 +56,6 @@ namespace bool read_from_tty(epee::wipeable_string& pass, bool hide_input) { - static constexpr const char BACKSPACE = 8; - HANDLE h_cin = ::GetStdHandle(STD_INPUT_HANDLE); DWORD mode_old; @@ -67,32 +65,46 @@ namespace bool r = true; pass.reserve(tools::password_container::max_password_size); + std::vector chlen; + chlen.reserve(tools::password_container::max_password_size); while (pass.size() < tools::password_container::max_password_size) { DWORD read; - char ch; - r = (TRUE == ::ReadConsoleA(h_cin, &ch, 1, &read, NULL)); + wchar_t ucs2_ch; + r = (TRUE == ::ReadConsoleW(h_cin, &ucs2_ch, 1, &read, NULL)); r &= (1 == read); + if (!r) { break; } - else if (ch == '\n' || ch == '\r') + else if (ucs2_ch == L'\n' || ucs2_ch == L'\r') { std::cout << std::endl; break; } - else if (ch == BACKSPACE) + else if (ucs2_ch == L'\b') { if (!pass.empty()) { - pass.pop_back(); + int len = chlen.back(); + chlen.pop_back(); + while(len-- > 0) + pass.pop_back(); } + continue; } - else - { - pass.push_back(ch); - } + + char utf8_ch[8] = {0}; + int len; + if((len = WideCharToMultiByte(CP_UTF8, 0, &ucs2_ch, 1, utf8_ch, sizeof(utf8_ch), NULL, NULL)) <= 0) + break; + + if(pass.size() + len >= tools::password_container::max_password_size) + break; + + chlen.push_back(len); + pass += utf8_ch; } ::SetConsoleMode(h_cin, mode_old); From 3b402ebb85979528fdc779778c19a01ef169ef34 Mon Sep 17 00:00:00 2001 From: Jkat Date: Thu, 4 Oct 2018 14:43:14 -0400 Subject: [PATCH 0526/1404] Updating Monero GUI repo link in README.i18n.md --- README.i18n.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.i18n.md b/README.i18n.md index 504fc9e752c..a3bd8070e4b 100644 --- a/README.i18n.md +++ b/README.i18n.md @@ -3,7 +3,7 @@ Monero daemon internationalization The Monero command line tools can be translated in various languages. If you wish to contribute and need help/support, contact the [Monero Localization Workgroup on Taiga](https://taiga.getmonero.org/project/erciccione-monero-localization/) or come chat on `#monero-translations` (Freenode/IRC, riot/matrix, MatterMost) -In order to use the same translation workflow as the [Monero Core GUI](https://github.com/monero-project/monero-core), they use Qt Linguist translation files. However, to avoid the dependencies on Qt this normally implies, they use a custom loader to read those files at runtime. +In order to use the same translation workflow as the [Monero Core GUI](https://github.com/monero-project/monero-gui), they use Qt Linguist translation files. However, to avoid the dependencies on Qt this normally implies, they use a custom loader to read those files at runtime. ### Tools for translators From 9a856697246f245795f8d8e18f635237c80f64c8 Mon Sep 17 00:00:00 2001 From: AnythingTechPro Date: Thu, 4 Oct 2018 22:12:53 -0400 Subject: [PATCH 0527/1404] rpc: fixed typo in JSON command error response message --- src/rpc/core_rpc_server.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index be511a2d21e..d8f556d3e22 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -1090,7 +1090,7 @@ namespace cryptonote { error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR; error_resp.message = "Internal error: failed to create block template"; - LOG_ERROR("Failed to tx pub key in coinbase extra"); + LOG_ERROR("Failed to get tx pub key in coinbase extra"); return false; } res.reserved_offset = slow_memmem((void*)block_blob.data(), block_blob.size(), &tx_pub_key, sizeof(tx_pub_key)); From ee71ba9869c37e92cb56590bb344e6e937d91c54 Mon Sep 17 00:00:00 2001 From: sanecito Date: Fri, 5 Oct 2018 00:39:23 -0700 Subject: [PATCH 0528/1404] Add translations for Monero::PendingTransactionImpl, command_line Contexts --- translations/monero_ja.ts | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/translations/monero_ja.ts b/translations/monero_ja.ts index ac5065d7de4..939f9188fcb 100644 --- a/translations/monero_ja.ts +++ b/translations/monero_ja.ts @@ -29,42 +29,42 @@ Attempting to save transaction to file, but specified file(s) exist. Exiting to not risk overwriting. File: - + ファイルは既に存在するのでファイルに取引を書き出せなかった。上書きしないにエグジットしてます。ファイル: Failed to write transaction(s) to file - + ファイルに取引を書き出せなかった daemon is busy. Please try again later. - + デーモン忙しいです。後でもう一度試してください。 no connection to daemon. Please make sure daemon is running. - + デーモンの接続が確立ありません。デーモンが実行中になっていることを確認してください。 transaction %s was rejected by daemon with status: - + 取引 %s がデーモンによって拒否しました。ステータス: . Reason: - + 。 理由: Unknown exception: - + 未知の例外: Unhandled exception - + 未処理の例外 @@ -385,22 +385,22 @@ Failed to parse address - + アドレスを解析できませんでした Failed to parse key - + キーを解析できませんでした failed to verify key - + キーを検証できませんでした key does not match address - + キーがアドレスと一致しませんでした @@ -408,12 +408,12 @@ yes - + はい no - + いいえ From fb3593c22e08a74adddf4403fa13ff36cee20045 Mon Sep 17 00:00:00 2001 From: TheCharlatan Date: Thu, 4 Oct 2018 15:07:00 +0200 Subject: [PATCH 0529/1404] Add check if submodules need to be updated Adds CMake check that pulls from the different git remotes and checks if there is any output. --- CMakeLists.txt | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index c31c14350c9..b890965f66d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -172,6 +172,25 @@ else() message(STATUS "Building without build tag") endif() +if(NOT MANUAL_SUBMODULES) + find_package(Git) + if(GIT_FOUND) + message(STATUS "Checking submodules") + execute_process(COMMAND bash -c "cd ${CMAKE_CURRENT_SOURCE_DIR}/external/miniupnp && git rev-parse HEAD" OUTPUT_VARIABLE miniupnpLocalHead WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) + execute_process(COMMAND bash -c "cd ${CMAKE_CURRENT_SOURCE_DIR}/external/unbound && git rev-parse HEAD" OUTPUT_VARIABLE unboundLocalHead WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) + execute_process(COMMAND bash -c "cd ${CMAKE_CURRENT_SOURCE_DIR}/external/rapidjson && git rev-parse HEAD" OUTPUT_VARIABLE rapidjsonLocalHead WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) + execute_process(COMMAND bash -c "git ls-tree HEAD ${CMAKE_CURRENT_SOURCE_DIR}/external/miniupnp | awk '{print $3}'" OUTPUT_VARIABLE miniupnpCheckedHead WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) + execute_process(COMMAND bash -c "git ls-tree HEAD ${CMAKE_CURRENT_SOURCE_DIR}/external/unbound | awk '{print $3}'" OUTPUT_VARIABLE unboundCheckedHead WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) + execute_process(COMMAND bash -c "git ls-tree HEAD ${CMAKE_CURRENT_SOURCE_DIR}/external/rapidjson | awk '{print $3}'" OUTPUT_VARIABLE rapidjsonCheckedHead WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) + string(COMPARE EQUAL "${miniupnpLocalHead}" "${miniupnpCheckedHead}" miniupnpUpToDate) + string(COMPARE EQUAL "${unboundLocalHead}" "${unboundCheckedHead}" unboundUpToDate) + string(COMPARE EQUAL "${rapidjsonLocalHead}" "${rapidjsonCheckedHead}" rapidjsonUpToDate) + if (NOT miniupnpUpToDate OR NOT unboundUpToDate OR NOT rapidjsonUpToDate) + message(FATAL_ERROR "Submodules not up to date. Please update with git subdmoule init && git submodule update, or run cmake with -DMANUAL_SUBMODULES=1") + endif() + endif() +endif() + set(CMAKE_C_FLAGS_RELEASE "-DNDEBUG ${OPT_FLAGS_RELEASE}") set(CMAKE_CXX_FLAGS_RELEASE "-DNDEBUG ${OPT_FLAGS_RELEASE}") From 977df6315fa9aab63c4f3155e565c93aa49e5463 Mon Sep 17 00:00:00 2001 From: Guillaume LE VAILLANT Date: Sat, 6 Oct 2018 10:12:38 +0200 Subject: [PATCH 0530/1404] Fix some calls to the translation function Some strings were not detected by lupdate because "tr() cannot be called without context". --- src/gen_multisig/gen_multisig.cpp | 2 +- src/simplewallet/simplewallet.cpp | 82 +- src/wallet/wallet2.cpp | 4 +- translations/monero.ts | 4283 ++++++++++++++++++----------- 4 files changed, 2686 insertions(+), 1685 deletions(-) diff --git a/src/gen_multisig/gen_multisig.cpp b/src/gen_multisig/gen_multisig.cpp index 69be70e1b21..b69ca52fffe 100644 --- a/src/gen_multisig/gen_multisig.cpp +++ b/src/gen_multisig/gen_multisig.cpp @@ -104,7 +104,7 @@ static bool generate_multisig(uint32_t threshold, uint32_t total, const std::str wallets[n]->decrypt_keys(pwd_container->password()); if (!tools::wallet2::verify_multisig_info(wallets[n]->get_multisig_info(), sk[n], pk[n])) { - tools::fail_msg_writer() << tr("Failed to verify multisig info"); + tools::fail_msg_writer() << genms::tr("Failed to verify multisig info"); return false; } wallets[n]->encrypt_keys(pwd_container->password()); diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index a1d6eb59083..3112280c958 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -180,14 +180,14 @@ namespace auto pwd_container = tools::password_container::prompt(verify, prompt); if (!pwd_container) { - tools::fail_msg_writer() << tr("failed to read wallet password"); + tools::fail_msg_writer() << sw::tr("failed to read wallet password"); } return pwd_container; } boost::optional default_password_prompter(bool verify) { - return password_prompter(verify ? tr("Enter a new password for the wallet") : tr("Wallet password"), verify); + return password_prompter(verify ? sw::tr("Enter a new password for the wallet") : sw::tr("Wallet password"), verify); } inline std::string interpret_rpc_response(bool ok, const std::string& status) @@ -265,7 +265,7 @@ namespace } else { - fail_msg_writer() << tr("invalid argument: must be either 0/1, true/false, y/n, yes/no"); + fail_msg_writer() << sw::tr("invalid argument: must be either 0/1, true/false, y/n, yes/no"); return false; } } @@ -321,18 +321,18 @@ namespace std::string dnssec_str; if (dnssec_valid) { - dnssec_str = tr("DNSSEC validation passed"); + dnssec_str = sw::tr("DNSSEC validation passed"); } else { - dnssec_str = tr("WARNING: DNSSEC validation was unsuccessful, this address may not be correct!"); + dnssec_str = sw::tr("WARNING: DNSSEC validation was unsuccessful, this address may not be correct!"); } std::stringstream prompt; - prompt << tr("For URL: ") << url + prompt << sw::tr("For URL: ") << url << ", " << dnssec_str << std::endl - << tr(" Monero Address = ") << addresses[0] + << sw::tr(" Monero Address = ") << addresses[0] << std::endl - << tr("Is this OK? (Y/n) ") + << sw::tr("Is this OK? (Y/n) ") ; // prompt the user for confirmation given the dns query and dnssec status std::string confirm_dns_ok = input_line(prompt.str()); @@ -342,7 +342,7 @@ namespace } if (!command_line::is_yes(confirm_dns_ok)) { - std::cout << tr("you have cancelled the transfer request") << std::endl; + std::cout << sw::tr("you have cancelled the transfer request") << std::endl; return {}; } return addresses[0]; @@ -363,7 +363,7 @@ namespace uint32_t subaddr_index; if(!epee::string_tools::get_xtype_from_string(subaddr_index, subaddr_index_str)) { - fail_msg_writer() << tr("failed to parse index: ") << subaddr_index_str; + fail_msg_writer() << sw::tr("failed to parse index: ") << subaddr_index_str; subaddr_indices.clear(); return false; } @@ -376,7 +376,7 @@ namespace { auto r = tools::parse_subaddress_lookahead(str); if (!r) - fail_msg_writer() << tr("invalid format for subaddress lookahead; must be :"); + fail_msg_writer() << sw::tr("invalid format for subaddress lookahead; must be :"); return r; } @@ -389,27 +389,27 @@ namespace } catch (const tools::error::daemon_busy&) { - fail_msg_writer() << tr("daemon is busy. Please try again later."); + fail_msg_writer() << sw::tr("daemon is busy. Please try again later."); } catch (const tools::error::no_connection_to_daemon&) { - fail_msg_writer() << tr("no connection to daemon. Please make sure daemon is running."); + fail_msg_writer() << sw::tr("no connection to daemon. Please make sure daemon is running."); } catch (const tools::error::wallet_rpc_error& e) { LOG_ERROR("RPC error: " << e.to_string()); - fail_msg_writer() << tr("RPC error: ") << e.what(); + fail_msg_writer() << sw::tr("RPC error: ") << e.what(); } catch (const tools::error::get_outs_error &e) { - fail_msg_writer() << tr("failed to get random outputs to mix: ") << e.what(); + fail_msg_writer() << sw::tr("failed to get random outputs to mix: ") << e.what(); } catch (const tools::error::not_enough_unlocked_money& e) { LOG_PRINT_L0(boost::format("not enough money to transfer, available only %s, sent amount %s") % print_money(e.available()) % print_money(e.tx_amount())); - fail_msg_writer() << tr("Not enough money in unlocked balance"); + fail_msg_writer() << sw::tr("Not enough money in unlocked balance"); warn_of_possible_attack = false; } catch (const tools::error::not_enough_money& e) @@ -417,7 +417,7 @@ namespace LOG_PRINT_L0(boost::format("not enough money to transfer, available only %s, sent amount %s") % print_money(e.available()) % print_money(e.tx_amount())); - fail_msg_writer() << tr("Not enough money in unlocked balance"); + fail_msg_writer() << sw::tr("Not enough money in unlocked balance"); warn_of_possible_attack = false; } catch (const tools::error::tx_not_possible& e) @@ -427,30 +427,30 @@ namespace print_money(e.tx_amount() + e.fee()) % print_money(e.tx_amount()) % print_money(e.fee())); - fail_msg_writer() << tr("Failed to find a way to create transactions. This is usually due to dust which is so small it cannot pay for itself in fees, or trying to send more money than the unlocked balance, or not leaving enough for fees"); + fail_msg_writer() << sw::tr("Failed to find a way to create transactions. This is usually due to dust which is so small it cannot pay for itself in fees, or trying to send more money than the unlocked balance, or not leaving enough for fees"); warn_of_possible_attack = false; } catch (const tools::error::not_enough_outs_to_mix& e) { auto writer = fail_msg_writer(); - writer << tr("not enough outputs for specified ring size") << " = " << (e.mixin_count() + 1) << ":"; + writer << sw::tr("not enough outputs for specified ring size") << " = " << (e.mixin_count() + 1) << ":"; for (std::pair outs_for_amount : e.scanty_outs()) { - writer << "\n" << tr("output amount") << " = " << print_money(outs_for_amount.first) << ", " << tr("found outputs to use") << " = " << outs_for_amount.second; + writer << "\n" << sw::tr("output amount") << " = " << print_money(outs_for_amount.first) << ", " << sw::tr("found outputs to use") << " = " << outs_for_amount.second; } - writer << tr("Please use sweep_unmixable."); + writer << sw::tr("Please use sweep_unmixable."); } catch (const tools::error::tx_not_constructed&) - { - fail_msg_writer() << tr("transaction was not constructed"); + { + fail_msg_writer() << sw::tr("transaction was not constructed"); warn_of_possible_attack = false; } catch (const tools::error::tx_rejected& e) { - fail_msg_writer() << (boost::format(tr("transaction %s was rejected by daemon with status: ")) % get_transaction_hash(e.tx())) << e.status(); + fail_msg_writer() << (boost::format(sw::tr("transaction %s was rejected by daemon with status: ")) % get_transaction_hash(e.tx())) << e.status(); std::string reason = e.reason(); if (!reason.empty()) - fail_msg_writer() << tr("Reason: ") << reason; + fail_msg_writer() << sw::tr("Reason: ") << reason; } catch (const tools::error::tx_sum_overflow& e) { @@ -459,38 +459,38 @@ namespace } catch (const tools::error::zero_destination&) { - fail_msg_writer() << tr("one of destinations is zero"); + fail_msg_writer() << sw::tr("one of destinations is zero"); warn_of_possible_attack = false; } catch (const tools::error::tx_too_big& e) { - fail_msg_writer() << tr("failed to find a suitable way to split transactions"); + fail_msg_writer() << sw::tr("failed to find a suitable way to split transactions"); warn_of_possible_attack = false; } catch (const tools::error::transfer_error& e) { LOG_ERROR("unknown transfer error: " << e.to_string()); - fail_msg_writer() << tr("unknown transfer error: ") << e.what(); + fail_msg_writer() << sw::tr("unknown transfer error: ") << e.what(); } catch (const tools::error::multisig_export_needed& e) { LOG_ERROR("Multisig error: " << e.to_string()); - fail_msg_writer() << tr("Multisig error: ") << e.what(); + fail_msg_writer() << sw::tr("Multisig error: ") << e.what(); warn_of_possible_attack = false; } catch (const tools::error::wallet_internal_error& e) { LOG_ERROR("internal error: " << e.to_string()); - fail_msg_writer() << tr("internal error: ") << e.what(); + fail_msg_writer() << sw::tr("internal error: ") << e.what(); } catch (const std::exception& e) { LOG_ERROR("unexpected error: " << e.what()); - fail_msg_writer() << tr("unexpected error: ") << e.what(); + fail_msg_writer() << sw::tr("unexpected error: ") << e.what(); } if (warn_of_possible_attack) - fail_msg_writer() << tr("There was an error, which could mean the node may be trying to get you to retry creating a transaction, and zero in on which outputs you own. Or it could be a bona fide error. It may be prudent to disconnect from this node, and not try to send a transaction immediately. Alternatively, connect to another node so the original node cannot correlate information."); + fail_msg_writer() << sw::tr("There was an error, which could mean the node may be trying to get you to retry creating a transaction, and zero in on which outputs you own. Or it could be a bona fide error. It may be prudent to disconnect from this node, and not try to send a transaction immediately. Alternatively, connect to another node so the original node cannot correlate information."); } bool check_file_overwrite(const std::string &filename) @@ -500,10 +500,10 @@ namespace { if (boost::ends_with(filename, ".keys")) { - fail_msg_writer() << boost::format(tr("File %s likely stores wallet private keys! Use a different file name.")) % filename; + fail_msg_writer() << boost::format(sw::tr("File %s likely stores wallet private keys! Use a different file name.")) % filename; return false; } - return command_line::is_yes(input_line((boost::format(tr("File %s already exists. Are you sure to overwrite it? (Y/Yes/N/No): ")) % filename).str())); + return command_line::is_yes(input_line((boost::format(sw::tr("File %s already exists. Are you sure to overwrite it? (Y/Yes/N/No): ")) % filename).str())); } return true; } @@ -6577,16 +6577,16 @@ static std::string get_human_readable_timespan(std::chrono::seconds seconds) { uint64_t ts = seconds.count(); if (ts < 60) - return std::to_string(ts) + tr(" seconds"); + return std::to_string(ts) + sw::tr(" seconds"); if (ts < 3600) - return std::to_string((uint64_t)(ts / 60)) + tr(" minutes"); + return std::to_string((uint64_t)(ts / 60)) + sw::tr(" minutes"); if (ts < 3600 * 24) - return std::to_string((uint64_t)(ts / 3600)) + tr(" hours"); + return std::to_string((uint64_t)(ts / 3600)) + sw::tr(" hours"); if (ts < 3600 * 24 * 30.5) - return std::to_string((uint64_t)(ts / (3600 * 24))) + tr(" days"); + return std::to_string((uint64_t)(ts / (3600 * 24))) + sw::tr(" days"); if (ts < 3600 * 24 * 365.25) - return std::to_string((uint64_t)(ts / (3600 * 24 * 30.5))) + tr(" months"); - return tr("a long time"); + return std::to_string((uint64_t)(ts / (3600 * 24 * 30.5))) + sw::tr(" months"); + return sw::tr("a long time"); } //---------------------------------------------------------------------------------------------------- bool simple_wallet::show_transfers(const std::vector &args_) @@ -8096,7 +8096,7 @@ int main(int argc, char* argv[]) if (!command.empty()) { if (!w.process_command(command)) - fail_msg_writer() << tr("Unknown command: ") << command.front(); + fail_msg_writer() << sw::tr("Unknown command: ") << command.front(); w.stop(); w.deinit(); } diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 5bdc75b1360..303e2fb5f00 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -258,7 +258,7 @@ std::unique_ptr make_basic(const boost::program_options::variabl trusted_daemon = false; if (tools::is_local_address(daemon_address)) { - MINFO(tr("Daemon is local, assuming trusted")); + MINFO(tools::wallet2::tr("Daemon is local, assuming trusted")); trusted_daemon = true; } } @@ -310,7 +310,7 @@ boost::optional get_password(const boost::program_opt THROW_WALLET_EXCEPTION_IF(!password_prompter, tools::error::wallet_internal_error, tools::wallet2::tr("no password specified; use --prompt-for-password to prompt for a password")); - return password_prompter(verify ? tr("Enter a new password for the wallet") : tr("Wallet password"), verify); + return password_prompter(verify ? tools::wallet2::tr("Enter a new password for the wallet") : tools::wallet2::tr("Wallet password"), verify); } std::pair, tools::password_container> generate_from_json(const std::string& json_file, const boost::program_options::variables_map& vm, bool unattended, const options& opts, const std::function(const char *, bool)> &password_prompter) diff --git a/translations/monero.ts b/translations/monero.ts index 5f0ba0c7c21..8d4ee7ab885 100644 --- a/translations/monero.ts +++ b/translations/monero.ts @@ -27,45 +27,55 @@ Monero::PendingTransactionImpl - + Attempting to save transaction to file, but specified file(s) exist. Exiting to not risk overwriting. File: - + Failed to write transaction(s) to file - + daemon is busy. Please try again later. - + no connection to daemon. Please make sure daemon is running. - + transaction %s was rejected by daemon with status: - + . Reason: - + Unknown exception: - + Unhandled exception + + + Couldn't multisig sign data: + + + + + Couldn't sign multisig transaction: + + Monero::UnsignedTransactionImpl @@ -124,281 +134,406 @@ Monero::WalletImpl - + payment id has invalid format, expected 16 or 64 character hex string: - + Failed to add short payment id: - - + + daemon is busy. Please try again later. - - + + no connection to daemon. Please make sure daemon is running. - - + + RPC error: - - - not enough outputs for specified ring size + + failed to get outputs to mix: %s - - - found outputs to use + + + not enough outputs for specified ring size - - Please sweep unmixable outputs. + + + found outputs to use - - failed to get random outputs to mix + + Please sweep unmixable outputs. - - + + not enough money to transfer, available only %s, sent amount %s - + failed to parse address - + failed to parse secret spend key - - No view key supplied, cancelled + + Neither view key nor spend key supplied, cancelled - + failed to parse secret view key - + failed to verify secret spend key - + spend key does not match address - + failed to verify secret view key - + view key does not match address - + + failed to generate new wallet: - + + Electrum seed is empty + + + + + Electrum-style word list failed verification + + + + Failed to send import wallet request - + Failed to load unsigned transactions - + Failed to load transaction from file - + Wallet is view only - + failed to save file - + Key images can only be imported with a trusted daemon - + Failed to import key images: - + Failed to get subaddress label: - + Failed to set subaddress label: - - failed to get random outputs to mix: %s + + Failed to get multisig info: + + + + + Failed to make multisig: + + + + + Failed to finalize multisig wallet creation + + + + + Failed to finalize multisig wallet creation: + + + + + Failed to export multisig images: + + + + + Failed to parse imported multisig images + + + + + Failed to import multisig images: + + + + + Failed to check for partial multisig key images: + + + + + Failed to restore multisig transaction: + + + + + Invalid destination address + + + + + Invalid output: - - + + not enough money to transfer, overall balance only %s, sent amount %s - - + + not enough money to transfer, available only %s, transaction amount %s = %s + %s (fee) - - + + output amount - - + + transaction was not constructed - - + + transaction %s was rejected by daemon with status: - - + + one of destinations is zero - - + + failed to find a suitable way to split transactions - - + + unknown transfer error: - - + + internal error: - - + + unexpected error: - - + + unknown error - - - - - - + + failed to get outputs to mix + + + + + + + + + + Failed to parse txid - + no tx keys found for this txid - - + + Failed to parse tx key - - - - + + + + Failed to parse address - + Address must not be a subaddress - + + The wallet must be in multisig ready state + + + + + Given string is not a key + + + + Rescan spent can only be used with a trusted daemon + + + Failed to set blackballed outputs + + + + + + Failed to parse output amount + + + + + + Failed to parse output offset + + + + + Failed to blackball output + + + + + Failed to unblackball output + + + + + + Failed to parse key image + + + + + Failed to get ring + + + + + Failed to get rings + + + + + Failed to set ring + + Wallet - + Failed to parse address - + Failed to parse key - + failed to verify key - + key does not match address @@ -449,18 +584,18 @@ - + Username specified with -- - - + + cannot be empty - + requires RPC server password -- @@ -468,2531 +603,3408 @@ cryptonote::simple_wallet - - Commands: + + Daemon uses a different RPC major version (%u) than the wallet (%u): %s. Either update one of them, or use --allow-mismatched-daemon-version. - - failed to read wallet password + + Enter the number corresponding to the language of your choice: - - invalid password + + There is currently a %u block backlog at that fee level. Is this okay? (Y/Yes/N/No): - - set seed: needs an argument. available options: language + + + Spending from address index %d + - - set: unrecognized argument(s) + + Sending %s. - - wallet file path not valid: + + Your transaction needs to be split into %llu transactions. This will result in a transaction fee being applied to each transaction, for a total fee of %s - - Attempting to generate or restore wallet, but specified file(s) exist. Exiting to not risk overwriting. + + The transaction fee is %s - - usage: payment_id + + , of which %s is dust from change - - needs an argument + + A total of %s from dust change will be sent to dust address - - - - - - - - - - - 0 or 1 + + . +This transaction will unlock on block %llu, in approximately %s days (assuming 2 minutes per block) - - 0, 1, 2, 3, or 4 + + Not enough money in unlocked balance - - - unsigned integer + + No address given - - NOTE: the following 25 words can be used to recover access to your wallet. Write them down and store them somewhere safe and secure. Please do not store them in your email or on file storage services outside of your immediate control. - + + missing lockedblocks parameter - - --restore-deterministic-wallet uses --generate-new-wallet, not --wallet-file + + bad locked_blocks parameter - - specify a recovery parameter with the --electrum-seed="words list here" + + failed to parse Payment ID - - specify a wallet path with --generate-new-wallet (not --wallet-file) + + failed to parse key image - - wallet failed to connect to daemon: + + No outputs found - - Daemon uses a different RPC major version (%u) than the wallet (%u): %s. Either update one of them, or use --allow-mismatched-daemon-version. + + Multiple transactions are created, which is not supposed to happen - - List of available languages for your wallet's seed: + + The transaction uses multiple or no inputs, which is not supposed to happen - - Enter the number corresponding to the language of your choice: + + Money successfully sent, transaction: - - You had been using a deprecated version of the wallet. Please use the new seed that we provide. - + + missing threshold amount - - - Generated new wallet: + + invalid amount threshold - - - - failed to generate new wallet: + + donations are not enabled on the testnet or on the stagenet - - Opened watch-only wallet + + usage: donate [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <amount> [<payment_id>] - - Opened wallet + + Claimed change does not go to a paid address - - You had been using a deprecated version of the wallet. Please proceed to upgrade your wallet. - + + Claimed change is larger than payment to the change address - - You had been using a deprecated version of the wallet. Your wallet file format is being upgraded now. - + + Change goes to more than one address - - failed to load wallet: + + sending %s to %s - - Use the "help" command to see the list of available commands. - + + dummy output(s) - - Wallet data saved + + with no destinations - - Mining started in daemon + + no change - - mining has NOT been started: + + Loaded %lu transactions, for %s, fee %s, %s, %s, with min ring size %lu, %s. %sIs this okay? (Y/Yes/N/No): - - Mining stopped in daemon + + (Y/Yes/N/No): - - mining has NOT been stopped: + + + + Is this okay? (Y/Yes/N/No): - - Blockchain saved + + Daemon is local, assuming trusted - - - - Height + + Password for new watch-only wallet - - transaction + + false - - spent + + Commands: - - unsupported transaction format + + Unknown command: - - Starting refresh... + + Command usage: - - Refresh done, blocks received: + + Command description: - - - payment id has invalid format, expected 16 or 64 character hex string: + + wallet is watch-only and has no spend key - - bad locked_blocks parameter: + + + + + + + + + + + + + + + + + + + + + + command not supported by HW wallet - - - - a single transaction cannot use more than one payment id: + + + wallet is watch-only and has no seed - - - - - failed to set up payment id, though it was decoded correctly + + wallet is multisig but not yet finalized - - - - - - - - - transaction cancelled. + + + wallet is non-deterministic and has no seed - - - Is this okay anyway? (Y/Yes/N/No): + + Failed to retrieve seed - - There is currently a %u block backlog at that fee level. Is this okay? (Y/Yes/N/No): + + wallet is multisig and has no seed - - Failed to check for backlog: + + Incorrect password - - - -Transaction + + + + Your original password was incorrect. - - - Spending from address index %d - + + Error with wallet rewrite: - - - WARNING: Outputs of multiple addresses are being used together, which might potentially compromise your privacy. - + + usage: payment_id - - Sending %s. + + + Random payment ID: - - Your transaction needs to be split into %llu transactions. This will result in a transaction fee being applied to each transaction, for a total fee of %s + + Cannot connect to daemon - - The transaction fee is %s + + Current fee is %s %s per %s - - , of which %s is dust from change + + Error: failed to estimate backlog array size: - - . + + Error: bad estimated backlog array size - - A total of %s from dust change will be sent to dust address + + (current) - - . -This transaction will unlock on block %llu, in approximately %s days (assuming 2 minutes per block) + + %u block (%u minutes) backlog at priority %u%s - - - - - - - - - Failed to write transaction(s) to file + + %u to %u block (%u to %u minutes) backlog at priority %u - - - - - - - - - Unsigned transaction(s) successfully written to file: + + No backlog at priority - - No unmixable outputs found + + + This wallet is already multisig - - No address given + + + wallet is watch-only and cannot be made multisig - - failed to parse Payment ID + + + This wallet has been used before, please use a new wallet to create a multisig wallet - - usage: sweep_single [<priority>] [<ring_size>] <key_image> <address> [<payment_id>] + + Send this multisig info to all other participants, then use make_multisig <threshold> <info1> [<info2>...] with others' multisig info - - failed to parse key image + + This includes the PRIVATE view key, so needs to be disclosed only to that multisig wallet's participants - - No outputs found + + usage: make_multisig <threshold> <multisiginfo1> [<multisiginfo2>...] - - Multiple transactions are created, which is not supposed to happen + + Invalid threshold - - The transaction uses multiple or no inputs, which is not supposed to happen + + Another step is needed - - missing threshold amount + + Send this multisig info to all other participants, then use finalize_multisig <info1> [<info2>...] with others' multisig info - - invalid amount threshold + + Error creating multisig: - - donations are not enabled on the testnet + + Error creating multisig: new wallet is not multisig - - usage: donate [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <amount> [<payment_id>] + + multisig address: - - Claimed change does not go to a paid address + + + + This wallet is not multisig - - Claimed change is larger than payment to the change address + + This wallet is already finalized - - sending %s to %s + + usage: finalize_multisig <multisiginfo1> [<multisiginfo2>...] - - dummy output(s) + + Failed to finalize multisig - - with no destinations + + Failed to finalize multisig: - - Loaded %lu transactions, for %s, fee %s, %s, %s, with min ring size %lu, %s. %sIs this okay? (Y/Yes/N/No): + + + + + + This multisig wallet is not yet finalized - - This is a multisig wallet, it can only sign with sign_multisig + + usage: export_multisig_info <filename> - - usage: sign_transfer [export] + + + + failed to save file - - Failed to sign transaction + + Error exporting multisig info: - - Failed to sign transaction: + + Multisig info exported to - - Transaction raw hex data exported to + + usage: import_multisig_info <filename1> [<filename2>...] - one for each other participant - - Failed to load transaction from file + + + + + failed to read file - - - RPC error: + + Multisig info imported - - wallet is watch-only and has no spend key + + Failed to import multisig info: - - - - Your original password was incorrect. + + Failed to update spent status after importing multisig info: - - Error with wallet rewrite: + + Untrusted daemon, spent status may be incorrect. Use a trusted daemon and run "rescan_spent" - - priority must be 0, 1, 2, 3, or 4 + + + + This is not a multisig wallet - - - priority must be 0, 1, 2, 3, or 4 + + usage: sign_multisig <filename> - - invalid unit + + Failed to sign multisig transaction - - - invalid count: must be an unsigned integer + + Multisig error: - - invalid value + + Failed to sign multisig transaction: - - usage: set_log <log_level_number_0-4> | <categories> + + + + Transaction successfully signed to file - - (Y/Yes/N/No): + + It may be relayed to the network with submit_multisig - - - bad m_restore_height parameter: + + usage: submit_multisig <filename> - - date format must be YYYY-MM-DD + + + Failed to load multisig transaction from file - - Restore height is: + + + Multisig transaction signed by only %u signers, needs %u more signatures - - - Is this okay? (Y/Yes/N/No): + + + Transaction successfully submitted, transaction - - Daemon is local, assuming trusted + + + You can check its status by using the `show_transfers` command. - - Password for new watch-only wallet + + + + + + + + + + unknown error - - invalid arguments. Please use start_mining [<number_of_threads>] [do_bg_mining] [ignore_battery], <number_of_threads> should be from 1 to + + usage: export_raw_multisig <filename> - - internal error: + + Failed to export multisig transaction to file - - - - unexpected error: + + Saved exported multisig transaction file(s): - - - - - - - - - - unknown error + + + + unexpected error: - - refresh failed: + + usage: print_ring <key_image|txid> - - Blocks received: + + + Invalid key image - - unlocked balance: + + Invalid txid - - - - amount + + Key image either not spent, or spent with mixin 0 - - false + + Failed to get key image ring: - - Unknown command: + + File doesn't exist - - Command usage: + + Invalid ring specification: - - Command description: + + Invalid key image: - - wallet is multisig but not yet finalized + + Invalid ring type, expected relative or abosolute: - - Enter optional seed encryption passphrase, empty to see raw seed + + + Error reading line: - - Failed to retrieve seed + + Invalid ring: - - wallet is multisig and has no seed + + Invalid relative ring: - - Cannot connect to daemon + + Invalid absolute ring: - - Current fee is %s monero per kB + + Failed to set ring for key image: - - Error: failed to estimate backlog array size: + + Continuing. - - Error: bad estimated backlog array size + + usage: set_ring <filename> | ( <key_image> absolute|relative <index> [<index>...] ) - - (current) + + Missing absolute or relative keyword - - %u block (%u minutes) backlog at priority %u%s + + + invalid index: must be a strictly positive unsigned integer - - %u to %u block (%u to %u minutes) backlog at priority %u + + invalid index: indices wrap - - No backlog at priority + + invalid index: indices should be in strictly ascending order - - - This wallet is already multisig + + failed to set ring - - - wallet is watch-only and cannot be made multisig + + Bad argument: - - - This wallet has been used before, please use a new wallet to create a multisig wallet + + should be "add" - - Your password is incorrect. + + Failed to open file - - Send this multisig info to all other participants, then use make_multisig <threshold> <info1> [<info2>...] with others' multisig info + + Failed to blackball output: - - This includes the PRIVATE view key, so needs to be disclosed only to that multisig wallet's participants + + + Failed to unblackball output: - - usage: make_multisig <threshold> <multisiginfo1> [<multisiginfo2>...] + + Blackballed: - - Invalid threshold + + not blackballed: - - Another step is needed + + Failed to save known rings: - - Send this multisig info to all other participants, then use finalize_multisig <info1> [<info2>...] with others' multisig info + + + wallet is watch-only and cannot transfer - - Error creating multisig: + + + + ring size must be an integer >= - - Error creating multisig: new wallet is not multisig + + + WARNING: this is a non default ring size, which may harm your privacy. Default is recommended. - - multisig address: + + could not change default ring size - - - - This wallet is not multisig + + + + priority must be either 0, 1, 2, 3, or 4, or one of: - - This wallet is already finalized + + could not change default priority - - usage: finalize_multisig <multisiginfo1> [<multisiginfo2>...] + + invalid unit - - Failed to finalize multisig + + + invalid count: must be an unsigned integer - - Failed to finalize multisig: + + invalid value - - - - - - This multisig wallet is not yet finalized + + + Invalid height - - usage: export_multisig_info <filename> + + start_mining [<number_of_threads>] [bg_mining] [ignore_battery] - - Error exporting multisig info: + + Start mining in the daemon (bg_mining and ignore_battery are optional booleans). - - Multisig info exported to + + Stop mining in the daemon. - - usage: import_multisig_info <filename1> [<filename2>...] - one for each other participant + + set_daemon <host>[:<port>] [trusted|untrusted] - - Multisig info imported + + Set another daemon to connect to. - - Failed to import multisig info: + + Save the current blockchain data. - - Failed to update spent status after importing multisig info: + + Synchronize the transactions and balance. - - Untrusted daemon, spent status may be incorrect. Use a trusted daemon and run "rescan_spent" + + balance [detail] - - - - This is not a multisig wallet + + Show the wallet's balance of the currently selected account. - - usage: sign_multisig <filename> + + incoming_transfers [available|unavailable] [verbose] [index=<N1>[,<N2>[,...]]] - - Failed to sign multisig transaction + + Show the incoming transfers, all or filtered by availability and address index. - - Multisig error: + + payments <PID_1> [<PID_2> ... <PID_N>] - - Failed to sign multisig transaction: + + Show the payments for the given payment IDs. - - It may be relayed to the network with submit_multisig + + Show the blockchain height. - - usage: submit_multisig <filename> + + locked_sweep_all [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <address> <lockblocks> [<payment_id>] - - - Failed to load multisig transaction from file + + Send all unlocked balance to an address and lock it for <lockblocks> (max. 1000000). If the parameter "index<N1>[,<N2>,...]" is specified, the wallet sweeps outputs received by those address indices. If omitted, the wallet randomly chooses an address index to be used. <priority> is the priority of the sweep. The higher the priority, the higher the transaction fee. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command "set priority") is used. <ring_size> is the number of inputs to include for untraceability. - - - Multisig transaction signed by only %u signers, needs %u more signatures + + Send all unmixable outputs to yourself with ring_size 1 - - - Transaction successfully submitted, transaction + + sweep_below <amount_threshold> [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <address> [<payment_id>] - - - You can check its status by using the `show_transfers` command. + + Send all unlocked outputs below the threshold to an address. - - usage: export_raw_multisig <filename> + + Send a single output of the given key image to an address without change. - - Failed to export multisig transaction to file + + donate [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <amount> [<payment_id>] - - Saved exported multisig transaction file(s): + + Donate <amount> to the development team (donate.getmonero.org). - - - - ring size must be an integer >= + + sign_transfer [export_raw] - - could not change default ring size + + Sign a transaction from a file. If the parameter "export_raw" is specified, transaction raw hex data suitable for the daemon RPC /sendrawtransaction is exported. - - Invalid height + + Submit a signed transaction from a file. - - start_mining [<number_of_threads>] [bg_mining] [ignore_battery] + + set_log <level>|{+,-,}<categories> - - Start mining in the daemon (bg_mining and ignore_battery are optional booleans). + + Change the current log detail (level must be <0-4>). - - Stop mining in the daemon. + + account + account new <label text with white spaces allowed> + account switch <index> + account label <index> <label text with white spaces allowed> + account tag <tag_name> <account_index_1> [<account_index_2> ...] + account untag <account_index_1> [<account_index_2> ...] + account tag_description <tag_name> <description> - - set_daemon <host>[:<port>] + + If no arguments are specified, the wallet shows all the existing accounts along with their balances. +If the "new" argument is specified, the wallet creates a new account with its label initialized by the provided label text (which can be empty). +If the "switch" argument is specified, the wallet switches to the account specified by <index>. +If the "label" argument is specified, the wallet sets the label of the account specified by <index> to the provided label text. +If the "tag" argument is specified, a tag <tag_name> is assigned to the specified accounts <account_index_1>, <account_index_2>, .... +If the "untag" argument is specified, the tags assigned to the specified accounts <account_index_1>, <account_index_2> ..., are removed. +If the "tag_description" argument is specified, the tag <tag_name> is assigned an arbitrary text <description>. - - Set another daemon to connect to. + + address [ new <label text with white spaces allowed> | all | <index_min> [<index_max>] | label <index> <label text with white spaces allowed>] - - Save the current blockchain data. + + If no arguments are specified or <index> is specified, the wallet shows the default or specified address. If "all" is specified, the wallet shows all the existing addresses in the currently selected account. If "new " is specified, the wallet creates a new address with the provided label text (which can be empty). If "label" is specified, the wallet sets the label of the address specified by <index> to the provided label text. - - Synchronize the transactions and balance. + + integrated_address [<payment_id> | <address>] - - balance [detail] + + Encode a payment ID into an integrated address for the current wallet public address (no argument uses a random payment ID), or decode an integrated address to standard address and payment ID - - Show the wallet's balance of the currently selected account. + + address_book [(add ((<address> [pid <id>])|<integrated address>) [<description possibly with whitespaces>])|(delete <index>)] - - incoming_transfers [available|unavailable] [verbose] [index=<N1>[,<N2>[,...]]] + + Print all entries in the address book, optionally adding/deleting an entry to/from it. - - Show the incoming transfers, all or filtered by availability and address index. + + Save the wallet data. - - payments <PID_1> [<PID_2> ... <PID_N>] + + Save a watch-only keys file. - - Show the payments for the given payment IDs. + + Display the private view key. - - Show the blockchain height. + + Display the private spend key. - - transfer_original [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <address> <amount> [<payment_id>] + + Display the Electrum-style mnemonic seed - - Transfer <amount> to <address> using an older transaction building algorithm. If the parameter "index=<N1>[,<N2>,...]" is specified, the wallet uses outputs received by addresses of those indices. If omitted, the wallet randomly chooses address indices to be used. In any case, it tries its best not to combine outputs across multiple addresses. <priority> is the priority of the transaction. The higher the priority, the higher the transaction fee. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command "set priority") is used. <ring_size> is the number of inputs to include for untraceability. Multiple payments can be made at once by adding <address_2> <amount_2> etcetera (before the payment ID, if it's included) + + set <option> [<value>] - - transfer [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <address> <amount> [<payment_id>] + + Display the encrypted Electrum-style mnemonic seed. - - Transfer <amount> to <address>. If the parameter "index=<N1>[,<N2>,...]" is specified, the wallet uses outputs received by addresses of those indices. If omitted, the wallet randomly chooses address indices to be used. In any case, it tries its best not to combine outputs across multiple addresses. <priority> is the priority of the transaction. The higher the priority, the higher the transaction fee. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command "set priority") is used. <ring_size> is the number of inputs to include for untraceability. Multiple payments can be made at once by adding <address_2> <amount_2> etcetera (before the payment ID, if it's included) + + Rescan the blockchain for spent outputs. - - locked_transfer [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <addr> <amount> <lockblocks> [<payment_id>] + + get_tx_key <txid> - - Transfer <amount> to <address> and lock it for <lockblocks> (max. 1000000). If the parameter "index=<N1>[,<N2>,...]" is specified, the wallet uses outputs received by addresses of those indices. If omitted, the wallet randomly chooses address indices to be used. In any case, it tries its best not to combine outputs across multiple addresses. <priority> is the priority of the transaction. The higher the priority, the higher the transaction fee. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command "set priority") is used. <ring_size> is the number of inputs to include for untraceability. Multiple payments can be made at once by adding <address_2> <amount_2> etcetera (before the payment ID, if it's included) + + Get the transaction key (r) for a given <txid>. - - Send all unmixable outputs to yourself with ring_size 1 + + set_tx_key <txid> <tx_key> - - sweep_all [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <address> [<payment_id>] + + Set the transaction key (r) for a given <txid> in case the tx was made by some other device or 3rd party wallet. - - Send all unlocked balance to an address. If the parameter "index<N1>[,<N2>,...]" is specified, the wallet sweeps outputs received by those address indices. If omitted, the wallet randomly chooses an address index to be used. + + check_tx_key <txid> <txkey> <address> - - sweep_below <amount_threshold> [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <address> [<payment_id>] + + Check the amount going to <address> in <txid>. - - Send all unlocked outputs below the threshold to an address. + + get_tx_proof <txid> <address> [<message>] - - sweep_single [<priority>] [<ring_size>] <key_image> <address> [<payment_id>] + + Generate a signature proving funds sent to <address> in <txid>, optionally with a challenge string <message>, using either the transaction secret key (when <address> is not your wallet's address) or the view secret key (otherwise), which does not disclose the secret key. - - Send a single output of the given key image to an address without change. + + check_tx_proof <txid> <address> <signature_file> [<message>] - - donate [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <amount> [<payment_id>] + + Check the proof for funds going to <address> in <txid> with the challenge string <message> if any. + + + + + get_spend_proof <txid> [<message>] + + + + + Generate a signature proving that you generated <txid> using the spend secret key, optionally with a challenge string <message>. + + + + + check_spend_proof <txid> <signature_file> [<message>] + + + + + Check a signature proving that the signer generated <txid>, optionally with a challenge string <message>. + + + + + get_reserve_proof (all|<amount>) [<message>] + + + + + Generate a signature proving that you own at least this much, optionally with a challenge string <message>. +If 'all' is specified, you prove the entire sum of all of your existing accounts' balances. +Otherwise, you prove the reserve of the smallest possible amount above <amount> available in your current account. + + + + + check_reserve_proof <address> <signature_file> [<message>] + + + + + Check a signature proving that the owner of <address> holds at least this much, optionally with a challenge string <message>. + + + + + show_transfers [in|out|pending|failed|pool|coinbase] [index=<N1>[,<N2>,...]] [<min_height> [<max_height>]] + + + + + Show the incoming/outgoing transfers within an optional height range. + + + + + unspent_outputs [index=<N1>[,<N2>,...]] [<min_amount> [<max_amount>]] + + + + + Show the unspent outputs of a specified address within an optional amount range. + + + + + Rescan the blockchain from scratch, losing any information which can not be recovered from the blockchain itself. + + + + + set_tx_note <txid> [free text note] + + + + + Set an arbitrary string note for a <txid>. + + + + + get_tx_note <txid> + + + + + Get a string note for a txid. + + + + + set_description [free text note] + + + + + Set an arbitrary description for the wallet. + + + + + Get the description of the wallet. + + + + + Show the wallet's status. + + + + + Show the wallet's information. + + + + + sign <file> + + + + + Sign the contents of a file. + + + + + verify <filename> <address> <signature> + + + + + Verify a signature on the contents of a file. + + + + + export_key_images <file> + + + + + Export a signed set of key images to a <file>. + + + + + import_key_images <file> + + + + + Import a signed key images list and verify their spent status. + + + + + hw_reconnect + + + + + Attempts to reconnect HW wallet. + + + + + export_outputs <file> + + + + + Export a set of outputs owned by this wallet. + + + + + import_outputs <file> + + + + + Import a set of outputs owned by this wallet. + + + + + show_transfer <txid> + + + + + Show information about a transfer to/from this address. + + + + + Change the wallet's password. + + + + + Generate a new random full size payment id. These will be unencrypted on the blockchain, see integrated_address for encrypted short payment ids. + + + + + Print the information about the current fee and transaction backlog. + + + + + Export data needed to create a multisig wallet + + + + + make_multisig <threshold> <string1> [<string>...] + + + + + Turn this wallet into a multisig wallet + + + + + finalize_multisig <string> [<string>...] + + + + + Turn this wallet into a multisig wallet, extra step for N-1/N wallets + + + + + export_multisig_info <filename> + + + + + Export multisig info for other participants + + + + + import_multisig_info <filename> [<filename>...] + + + + + Import multisig info from other participants + + + + + sign_multisig <filename> + + + + + Sign a multisig transaction from a file + + + + + submit_multisig <filename> + + + + + Submit a signed multisig transaction from a file + + + + + export_raw_multisig_tx <filename> + + + + + Export a signed multisig transaction to a file + + + + + print_ring <key_image> | <txid> + + + + + Print the ring(s) used to spend a given key image or transaction (if the ring size is > 1) + + + + + set_ring <filename> | ( <key_image> absolute|relative <index> [<index>...] ) + + + + + Set the ring used for a given key image, so it can be reused in a fork + + + + + save_known_rings + + + + + Save known rings to the shared rings database + + + + + Blackball output(s) so they never get selected as fake outputs in a ring + + + + + Unblackballs an output so it may get selected as a fake output in a ring + + + + + Checks whether an output is blackballed + + + + + version + + + + + Returns version information + + + + + help [<command>] + + + + + Show the help section or the documentation about a <command>. + + + + + needs an argument + + + + + set seed: needs an argument. available options: language + + + + + + + + + + + + + + + + 0 or 1 + + + + + integer >= + + + + + full (slowest, no assumptions); optimize-coinbase (fast, assumes the whole coinbase is paid to a single address); no-coinbase (fastest, assumes we receive no coinbase transaction), default (same as optimize-coinbase) + + + + + 0, 1, 2, 3, or 4, or one of + + + + + 0|1|2 (or never|action|decrypt) + + + + + monero, millinero, micronero, nanonero, piconero + + + + + + + unsigned integer + + + + + + + amount + + + + + block height + + + + + <major>:<minor> + + + + + set: unrecognized argument(s) + + + + + usage: set_log <log_level_number_0-4> | <categories> + + + + + wrong number range, use: set_log <log_level_number_0-4> | <categories> + + + + + Wallet name not valid. Please try again or use Ctrl-C to quit. + + + + + Attempting to generate or restore wallet, but specified file(s) exist. Exiting to not risk overwriting. + + + + + Wallet and key files found, loading... + + + + + Key file found but not wallet file. Regenerating... + + + + + Key file not found. Failed to open wallet: + + + + + No wallet found with that name. Confirm creation of new wallet named: + + + + + Generating new wallet... + + + + + NOTE: the following 25 words can be used to recover access to your wallet. Write them down and store them somewhere safe and secure. Please do not store them in your email or on file storage services outside of your immediate control. + + + + + + Can't specify more than one of --testnet and --stagenet + + + + + can't specify more than one of --generate-new-wallet="wallet_name", --wallet-file="wallet_name", --generate-from-view-key="wallet_name", --generate-from-spend-key="wallet_name", --generate-from-keys="wallet_name", --generate-from-multisig-keys="wallet_name", --generate-from-json="jsonfilename" and --generate-from-device="wallet_name" + + + + + can't specify both --restore-deterministic-wallet or --restore-multisig-wallet and --non-deterministic + + + + + --restore-multisig-wallet uses --generate-new-wallet, not --wallet-file + + + + + --restore-deterministic-wallet uses --generate-new-wallet, not --wallet-file + + + + + specify a recovery parameter with the --electrum-seed="multisig seed here" + + + + + specify a recovery parameter with the --electrum-seed="words list here" + + + + + Multisig seed failed verification + + + + + Electrum-style word list failed verification + + + + + + + + + + + + + + No data supplied, cancelled + + + + + + + + + + + + + + + + failed to parse address + + + + + + This address is a subaddress which cannot be used here. + + + + + + failed to parse view key secret key + + + + + + failed to verify view key secret key + + + + + + + view key does not match standard address + + + + + + + + + + account creation failed + + + + + + + failed to parse spend key secret key + + + + + + failed to verify spend key secret key + + + + + + spend key does not match standard address + + + + + Error: expected M/N, but got: + + + + + Error: expected N > 1 and N <= M, but got: + + + + + Error: M/N is currently unsupported. + + + + + Generating master wallet from %u of %u multisig wallet keys + + + + + failed to parse secret view key + + + + + failed to verify secret view key + + + + + Secret spend key (%u of %u): + + + + + Error: M/N is currently unsupported + + + + + No restore height is specified. + + + + + Assumed you are creating a new account, restore will be done from current estimated blockchain height. + + + + + Use --restore-height if you want to restore an already setup account from a specific height + + + + + account creation aborted + + + + + specify a wallet path with --generate-new-wallet (not --wallet-file) + + + + + + bad m_restore_height parameter: + + + + + date format must be YYYY-MM-DD + + + + + Restore height is: + + + + + Restore height + + + + + Still apply restore height? (Y/Yes/N/No): + + + + + can't specify --subaddress-lookahead and --wallet-file at the same time + + + + + failed to open account + + + + + + + + + wallet is null + + + + + Warning: using an untrusted daemon at %s, privacy will be lessened + + + + + Failed to initialize ring database: privacy enhancing features will be inactive + + + + + wallet failed to connect to daemon: + + + + + Daemon either is not started or wrong port was passed. Please make sure daemon is running or change the daemon address using the 'set_daemon' command. + + + + + List of available languages for your wallet's seed: + + + + + If your display freezes, exit blind with ^C, then run again with --use-english-language-names + + + + + + invalid language choice entered. Please try again. + + + + + + invalid password - - Donate <amount> to the development team (donate.getmonero.org). + + You had been using a deprecated version of the wallet. Please use the new seed that we provide. + - - sign_transfer <file> + + + Generated new wallet: - - Sign a transaction from a <file>. + + View key: - - Submit a signed transaction from a file. + + + + + failed to generate new wallet: - - set_log <level>|{+,-,}<categories> + + Your wallet has been generated! +To start synchronizing with the daemon, use the "refresh" command. +Use the "help" command to see the list of available commands. +Use "help <command>" to see a command's documentation. +Always use the "exit" command when closing monero-wallet-cli to save +your current session's state. Otherwise, you might need to synchronize +your wallet again (your wallet keys are NOT at risk in any case). + - - Change the current log detail (level must be <0-4>). + + Generated new wallet on hw device: - - account - account new <label text with white spaces allowed> - account switch <index> - account label <index> <label text with white spaces allowed> - account tag <tag_name> <account_index_1> [<account_index_2> ...] - account untag <account_index_1> [<account_index_2> ...] - account tag_description <tag_name> <description> + + failed to generate new mutlisig wallet - - If no arguments are specified, the wallet shows all the existing accounts along with their balances. -If the "new" argument is specified, the wallet creates a new account with its label initialized by the provided label text (which can be empty). -If the "switch" argument is specified, the wallet switches to the account specified by <index>. -If the "label" argument is specified, the wallet sets the label of the account specified by <index> to the provided label text. -If the "tag" argument is specified, a tag <tag_name> is assigned to the specified accounts <account_index_1>, <account_index_2>, .... -If the "untag" argument is specified, the tags assigned to the specified accounts <account_index_1>, <account_index_2> ..., are removed. -If the "tag_description" argument is specified, the tag <tag_name> is assigned an arbitrary text <description>. + + Generated new %u/%u multisig wallet: - - address [ new <label text with white spaces allowed> | all | <index_min> [<index_max>] | label <index> <label text with white spaces allowed>] + + wallet file path not valid: - - If no arguments are specified or <index> is specified, the wallet shows the default or specified address. If "all" is specified, the wallet shows all the existing addresses in the currently selected account. If "new " is specified, the wallet creates a new address with the provided label text (which can be empty). If "label" is specified, the wallet sets the label of the address specified by <index> to the provided label text. + + Key file not found. Failed to open wallet - - integrated_address [<payment_id> | <address>] + + Opened watch-only wallet - - Encode a payment ID into an integrated address for the current wallet public address (no argument uses a random payment ID), or decode an integrated address to standard address and payment ID + + Opened %u/%u multisig wallet%s - - address_book [(add ((<address> [pid <id>])|<integrated address>) [<description possibly with whitespaces>])|(delete <index>)] + + Opened wallet - - Print all entries in the address book, optionally adding/deleting an entry to/from it. + + You had been using a deprecated version of the wallet. Please proceed to upgrade your wallet. + - - Save the wallet data. + + You had been using a deprecated version of the wallet. Your wallet file format is being upgraded now. + - - Save a watch-only keys file. + + failed to load wallet: - - Display the private view key. + + Use the "help" command to see the list of available commands. + - - Display the private spend key. + + Use "help <command>" to see a command's documentation. + - - Display the Electrum-style mnemonic seed + + failed to deinitialize wallet - - set <option> [<value>] + + Wallet data saved - - Available options: - seed language - Set the wallet's seed language. - always-confirm-transfers <1|0> - Whether to confirm unsplit txes. - print-ring-members <1|0> - Whether to print detailed information about ring members during confirmation. - store-tx-info <1|0> - Whether to store outgoing tx info (destination address, payment ID, tx secret key) for future reference. - default-ring-size <n> - Set the default ring size (default and minimum is 5). - auto-refresh <1|0> - Whether to automatically synchronize new blocks from the daemon. - refresh-type <full|optimize-coinbase|no-coinbase|default> - Set the wallet's refresh behaviour. - priority [0|1|2|3|4] - Set the fee to default/unimportant/normal/elevated/priority. - confirm-missing-payment-id <1|0> - ask-password <1|0> - unit <monero|millinero|micronero|nanonero|piconero> - Set the default monero (sub-)unit. - min-outputs-count [n] - Try to keep at least that many outputs of value at least min-outputs-value. - min-outputs-value [n] - Try to keep at least min-outputs-count outputs of at least that value. - merge-destinations <1|0> - Whether to merge multiple payments to the same destination address. - confirm-backlog <1|0> - Whether to warn if there is transaction backlog. - confirm-backlog-threshold [n] - Set a threshold for confirm-backlog to only warn if the transaction backlog is greater than n blocks. - refresh-from-block-height [n] - Set the height before which to ignore blocks. - auto-low-priority <1|0> - Whether to automatically use the low priority fee level when it's safe to do so. + + wallet is multisig and cannot save a watch-only version - - Display the encrypted Electrum-style mnemonic seed. + + failed to read wallet password - - Rescan the blockchain for spent outputs. + + Watch only wallet saved as: - - get_tx_key <txid> + + Failed to save watch only wallet: - - Get the transaction key (r) for a given <txid>. + + + + this command requires a trusted daemon. Enable with --trusted-daemon - - check_tx_key <txid> <txkey> <address> + + invalid arguments. Please use start_mining [<number_of_threads>] [do_bg_mining] [ignore_battery] - - Check the amount going to <address> in <txid>. + + Mining started in daemon - - get_tx_proof <txid> <address> [<message>] + + mining has NOT been started: - - Generate a signature proving funds sent to <address> in <txid>, optionally with a challenge string <message>, using either the transaction secret key (when <address> is not your wallet's address) or the view secret key (otherwise), which does not disclose the secret key. + + Mining stopped in daemon - - check_tx_proof <txid> <address> <signature_file> [<message>] + + mining has NOT been stopped: - - Check the proof for funds going to <address> in <txid> with the challenge string <message> if any. + + missing daemon URL argument - - get_spend_proof <txid> [<message>] + + Unexpected array length - Exited simple_wallet::set_daemon() - - Generate a signature proving that you generated <txid> using the spend secret key, optionally with a challenge string <message>. + + Expected trusted or untrusted, got - - check_spend_proof <txid> <signature_file> [<message>] + + trusted - - Check a signature proving that the signer generated <txid>, optionally with a challenge string <message>. + + untrusted - - get_reserve_proof (all|<amount>) [<message>] + + This does not seem to be a valid daemon URL. - - Generate a signature proving that you own at least this much, optionally with a challenge string <message>. -If 'all' is specified, you prove the entire sum of all of your existing accounts' balances. -Otherwise, you prove the reserve of the smallest possible amount above <amount> available in your current account. + + Blockchain saved - - check_reserve_proof <address> <signature_file> [<message>] + + blockchain can't be saved: - - Check a signature proving that the owner of <address> holds at least this much, optionally with a challenge string <message>. + + + Height - - show_transfers [in|out|pending|failed|pool] [index=<N1>[,<N2>,...]] [<min_height> [<max_height>]] + + + txid - - Show the incoming/outgoing transfers within an optional height range. + + + idx - - unspent_outputs [index=<N1>[,<N2>,...]] [<min_amount> [<max_amount>]] + + NOTE: this transaction uses an encrypted payment ID: consider using subaddresses instead - - Show the unspent outputs of a specified address within an optional amount range. + + WARNING: this transaction uses an unencrypted payment ID: consider using subaddresses instead - - Rescan the blockchain from scratch. + + spent - - set_tx_note <txid> [free text note] + + Enter password - - Set an arbitrary string note for a <txid>. + + Starting refresh... - - get_tx_note <txid> + + Refresh done, blocks received: - - Get a string note for a txid. + + + daemon is busy. Please try again later. - - set_description [free text note] + + + no connection to daemon. Please make sure daemon is running. - - Set an arbitrary description for the wallet. + + + RPC error: - - Get the description of the wallet. + + refresh error: - - Show the wallet's status. + + internal error: - - Show the wallet's information. + + refresh failed: - - sign <file> + + Blocks received: - - Sign the contents of a file. + + (Some owned outputs have partial key images - import_multisig_info needed) - - verify <filename> <address> <signature> + + (Some owned outputs have missing key images - import_key_images needed) - - Verify a signature on the contents of a file. + + Currently selected account: [ - - export_key_images <file> + + ] - - Export a signed set of key images to a <file>. + + (No tag assigned) - - import_key_images <file> + + Tag: - - Import a signed key images list and verify their spent status. + + Balance: - - export_outputs <file> + + unlocked balance: - - Export a set of outputs owned by this wallet. + + Balance per address: - - import_outputs <file> + + Address - - Import a set of outputs owned by this wallet. + + + Balance - - show_transfer <txid> + + + Unlocked balance - - Show information about a transfer to/from this address. + + Outputs - - Change the wallet's password. + + + Label + + + + + %8u %6s %21s %21s %7u %21s + + + + + usage: balance [detail] + + + + + + usage: incoming_transfers [available|unavailable] [verbose] [index=<N>] + + + + + pubkey + + + + + key image + + + + + spent + + + + + + unlocked + + + + + ringct + + + + + global index + + + + + tx id + + + + + + addr index + + + + + T + + + + + F + + + + + locked + + + + + RingCT + + + + + - + + + + + No incoming transfers - - Generate a new random full size payment id. These will be unencrypted on the blockchain, see integrated_address for encrypted short payment ids. + + No incoming available transfers - - Print the information about the current fee and transaction backlog. + + No incoming unavailable transfers - - Export data needed to create a multisig wallet + + expected at least one payment ID - - make_multisig <threshold> <string1> [<string>...] + + payment - - Turn this wallet into a multisig wallet + + transaction - - finalize_multisig <string> [<string>...] + + height - - Turn this wallet into a multisig wallet, extra step for N-1/N wallets + + unlock time - - export_multisig_info <filename> + + No payments with id - - Export multisig info for other participants + + payment ID has invalid format, expected 16 or 64 character hex string: - - import_multisig_info <filename> [<filename>...] + + + + + failed to get blockchain height: - - Import multisig info from other participants + + failed to get spent status - - sign_multisig <filename> + + + + + + failed to connect to the daemon - - Sign a multisig transaction from a file + + +Transaction %llu/%llu: txid=%s - - submit_multisig <filename> + + failed to find construction data for tx input - - Submit a signed multisig transaction from a file + + +Input %llu/%llu: amount=%s - - export_raw_multisig_tx <filename> + + failed to get output: - - Export a signed multisig transaction to a file + + output key's originating block height shouldn't be higher than the blockchain height - - help [<command>] + + +Originating block heights: - - Show the help section or the documentation about a <command>. + + +| - - integer >= + + + | + - - block height + + +Warning: Some input keys being spent are from - - No wallet found with that name. Confirm creation of new wallet named: + + the same transaction - - can't specify more than one of --generate-new-wallet="wallet_name", --wallet-file="wallet_name", --generate-from-view-key="wallet_name", --generate-from-spend-key="wallet_name", --generate-from-keys="wallet_name", --generate-from-multisig-keys="wallet_name" and --generate-from-json="jsonfilename" + + blocks that are temporally very close - - can't specify both --restore-deterministic-wallet or --restore-multisig-wallet and --non-deterministic + + , which can break the anonymity of ring signature. Make sure this is intentional! - - --restore-multisig-wallet uses --generate-new-wallet, not --wallet-file + + + + Ring size must not be 0 - - specify a recovery parameter with the --electrum-seed="multisig seed here" + + + + ring size %u is too small, minimum is %u - - Multisig seed failed verification + + + + ring size %u is too large, maximum is %u - - Enter seed encryption passphrase, empty if none + + wrong number of arguments - - - This address is a subaddress which cannot be used here. + + payment id failed to encode - - Error: expected M/N, but got: + + failed to parse short payment ID from URI - - Error: expected N > 1 and N <= M, but got: + + + Invalid last argument: - - Error: M/N is currently unsupported. + + a single transaction cannot use more than one payment id - - Generating master wallet from %u of %u multisig wallet keys + + failed to parse payment id, though it was detected - - failed to parse secret view key + + usage: %s [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] [outputs=<N>] <address> [<payment_id>] - - failed to verify secret view key + + + Failed to parse number of outputs - - Secret spend key (%u of %u): + + + Amount of outputs should be greater than 0 - - Error: M/N is currently unsupported + + payment id has invalid format, expected 16 or 64 character hex string: - - Restore height + + + Unencrypted payment IDs are bad for privacy: ask the recipient to use subaddresses instead - - Still apply restore height? (Y/Yes/N/No): + + bad locked_blocks parameter: - - Warning: using an untrusted daemon at %s, privacy will be lessened + + + Locked blocks too high, max 1000000 (˜4 yrs) - - Daemon either is not started or wrong port was passed. Please make sure daemon is running or change the daemon address using the 'set_daemon' command. + + + a single transaction cannot use more than one payment id: - - Your wallet has been generated! -To start synchronizing with the daemon, use the "refresh" command. -Use the "help" command to see the list of available commands. -Use "help <command>" to see a command's documentation. -Always use the "exit" command when closing monero-wallet-cli to save -your current session's state. Otherwise, you might need to synchronize -your wallet again (your wallet keys are NOT at risk in any case). - + + + + + failed to set up payment id, though it was decoded correctly - - failed to generate new mutlisig wallet + + + + + amount is wrong: - - Generated new %u/%u multisig wallet: + + expected number from 0 to - - Opened %u/%u multisig wallet%s + + + + No payment id is included with this transaction. Is this okay? (Y/Yes/N/No): - - Use "help <command>" to see a command's documentation. - + + + + + + + + + transaction cancelled. - - wallet is multisig and cannot save a watch-only version + + + No outputs found, or daemon is not ready - - missing daemon URL argument + + + Is this okay anyway? (Y/Yes/N/No): - - Unexpected array length - Exited simple_wallet::set_daemon() + + Failed to check for backlog: - - This does not seem to be a valid daemon URL. + + + +Transaction - - - txid + + + WARNING: Outputs of multiple addresses are being used together, which might potentially compromise your privacy. + - - - idx + + . - - (Some owned outputs have partial key images - import_multisig_info needed) + + + + + + + + + Failed to write transaction(s) to file - - Currently selected account: [ + + + + + + + + + Unsigned transaction(s) successfully written to file: - - ] + + No unmixable outputs found - - Tag: + + Sweeping - - (No tag assigned) + + + Sweeping %s in %llu transactions for a total fee of %s. Is this okay? (Y/Yes/N/No): - - Balance per address: + + + + Sweeping %s for a total fee of %s. Is this okay? (Y/Yes/N/No): - - Address + + Normal - - - Balance + + Type: - - - Unlocked balance + + Network type: - - Outputs + + Testnet - - - Label + + Stagenet - - %8u %6s %21s %21s %7u %21s + + Mainnet - - usage: balance [detail] + + usage: sign <filename> - - - usage: incoming_transfers [available|unavailable] [verbose] [index=<N>] + + wallet is watch-only and cannot sign - - spent + + This wallet is multisig and cannot sign - - global index + + usage: verify <filename> <address> <signature> - - tx id + + Bad signature from - - - addr index + + Good signature from - - No incoming transfers + + usage: export_key_images <filename> - - No incoming available transfers + + wallet is watch-only and cannot export key images - - No incoming unavailable transfers + + Signed key images exported to - - expected at least one payment ID + + usage: import_key_images <filename> - - payment + + command only supported by HW wallet - - transaction + + Failed to reconnect device - - height + + Failed to reconnect device: - - unlock time + + usage: export_outputs <filename> - - No payments with id + + Outputs exported to - - - - failed to get blockchain height: + + usage: import_outputs <filename> - - - - - - failed to connect to the daemon + + usage: show_transfer <txid> - - -Transaction %llu/%llu: txid=%s + + Double spend seen on the network: this transaction may or may not end up being mined - - -Input %llu/%llu: amount=%s + + Transaction ID not found - - failed to get output: + + Transaction successfully saved to - - output key's originating block height shouldn't be higher than the blockchain height + + + , txid - - -Originating block heights: + + Failed to save transaction to - - -| + + true - - - | - + + failed to parse refresh type - - -Warning: Some input keys being spent are from + + Enter optional seed offset passphrase, empty to see raw seed - - , which can break the anonymity of ring signature. Make sure this is intentional! + + Enter seed offset passphrase, empty if none - - - Ring size must not be 0 + + You may want to remove the file "%s" and try again - - - ring size %u is too small, minimum is %u + + Discarding %s of unmixable outputs that cannot be spent, which can be undone by "rescan_spent". Is this okay? (Y/Yes/N/No): - - wrong number of arguments + + Donating %s %s to The Monero Project (donate.getmonero.org or %s). - - - - No payment id is included with this transaction. Is this okay? (Y/Yes/N/No): + + This is a multisig wallet, it can only sign with sign_multisig - - - No outputs found, or daemon is not ready + + This is a watch only wallet - - Transaction successfully saved to + + usage: sign_transfer [export_raw] - - - , txid + + Failed to sign transaction - - Failed to save transaction to + + Failed to sign transaction: - - - Sweeping %s in %llu transactions for a total fee of %s. Is this okay? (Y/Yes/N/No): + + Transaction raw hex data exported to - - - - Sweeping %s for a total fee of %s. Is this okay? (Y/Yes/N/No): + + Failed to load transaction from file - - Donating + + usage: get_tx_key <txid> - - This is a watch only wallet + + + + + + + + + + + failed to parse txid - - usage: show_transfer <txid> + + Tx key: - - Double spend seen on the network: this transaction may or may not end up being mined + + no tx keys found for this txid - - Transaction ID not found + + usage: set_tx_key <txid> <tx_key> - - true + + + + failed to parse tx_key - - failed to parse refresh type + + Tx key successfully stored. - - - wallet is watch-only and has no seed + + Failed to store tx key: - - - wallet is non-deterministic and has no seed + + usage: get_tx_proof <txid> <address> [<message>] - - - wallet is watch-only and cannot transfer + + + + signature file saved to: - - could not change default priority + + + + failed to save signature file - - full (slowest, no assumptions); optimize-coinbase (fast, assumes the whole coinbase is paid to a single address); no-coinbase (fastest, assumes we receive no coinbase transaction), default (same as optimize-coinbase) + + + + error: - - monero, millinero, micronero, nanonero, piconero + + usage: check_tx_key <txid> <txkey> <address> - - Wallet name not valid. Please try again or use Ctrl-C to quit. + + + failed to parse tx key - - Wallet and key files found, loading... + + + received - - Key file found but not wallet file. Regenerating... + + + in txid - - Key file not found. Failed to open wallet: + + + WARNING: this transaction is not yet included in the blockchain! - - Generating new wallet... + + + WARNING: failed to determine number of confirmations! - - Electrum-style word list failed verification + + + received nothing in txid - - - - - - - - - - - No data supplied, cancelled + + usage: check_tx_proof <txid> <address> <signature_file> [<message>] - - - - - - - - - - - - - failed to parse address + + + + failed to load signature file - - - failed to parse view key secret key + + + Good signature - - - failed to verify view key secret key + + + + Bad signature - - - - view key does not match standard address + + usage: get_spend_proof <txid> [<message>] - - - - - - account creation failed + + wallet is watch-only and cannot generate the proof - - - - failed to parse spend key secret key + + usage: check_spend_proof <txid> <signature_file> [<message>] - - - failed to verify spend key secret key + + usage: get_reserve_proof (all|<amount>) [<message>] - - - spend key does not match standard address + + The reserve proof can be generated only by a full wallet - - failed to open account + + usage: check_reserve_proof <address> <signature_file> [<message>] - - - - - - wallet is null + + Address must not be a subaddress - - - invalid language choice entered. Please try again. - + + usage: show_transfers [in|out|all|pending|failed|coinbase] [index=<N1>[,<N2>,...]] [<min_height> [<max_height>]] - - View key: + + bad min_height parameter: - - You may want to remove the file "%s" and try again + + bad max_height parameter: - - failed to deinitialize wallet + + block - - - - this command requires a trusted daemon. Enable with --trusted-daemon + + in - - blockchain can't be saved: + + + out - - - daemon is busy. Please try again later. + + [Double spend seen on the network: this transaction may or may not end up being mined] - - - no connection to daemon. Please make sure daemon is running. + + failed - - refresh error: + + pending - - Balance: + + usage: unspent_outputs [index=<N1>[,<N2>,...]] [<min_amount> [<max_amount>]] - - pubkey + + <min_amount> should be smaller than <max_amount> - - key image + + There is no unspent output in the specified address - - - unlocked + + +Amount: - - ringct + + , number of keys: - - T + + - - F + + +Min block height: - - locked + + +Max block height: - - RingCT + + +Min amount found: - - - + + +Max amount found: - - payment ID has invalid format, expected 16 or 64 character hex string: + + +Total count: - - failed to get spent status + + +Bin size: - - the same transaction + + +Outputs per *: - - blocks that are temporally very close + + count + ^ + - - Locked blocks too high, max 1000000 (˜4 yrs) + + | - - - Good signature + + + - - - - Bad signature + + +--> block height + - - usage: integrated_address [payment ID] + + ^ - - Standard address: + + ^ + - - failed to parse payment ID or address + + - - usage: address_book [(add (<address> [pid <long or short payment id>])|<integrated address> [<description possibly with whitespaces>])|(delete <index>)] + + Warning: this will lose any information which can not be recovered from the blockchain. - - failed to parse payment ID + + This includes destination addresses, tx secret keys, tx notes, etc - - failed to parse index + + Rescan anyway ? (Y/Yes/N/No): - - Address book is empty. + + Good signature -- total: %s, spent: %s, unspent: %s - - Index: + + usage: blackball <amount>/<offset> | <filename> [add] - - - Address: + + First line is not an amount - - Payment ID: + + Invalid output: - - - Description: + + Invalid output key, and file doesn't exist - - usage: set_tx_note [txid] free text note + + usage: unblackball <amount>/<offset> - - usage: get_tx_note [txid] + + + Invalid output - - usage: sign <filename> + + usage: blackballed <amount>/<offset> - - wallet is watch-only and cannot sign + + WARNING: from v8, ring size will be fixed and this setting will be ignored. - - - - - failed to read file + + invalid argument: must be either 0/never, 1/action, or 2/encrypt/decrypt - - usage: check_tx_proof <txid> <address> <signature_file> [<message>] + + transfer [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] (<URI> | <address> <amount>) [<payment_id>] - - - - failed to load signature file + + Transfer <amount> to <address>. If the parameter "index=<N1>[,<N2>,...]" is specified, the wallet uses outputs received by addresses of those indices. If omitted, the wallet randomly chooses address indices to be used. In any case, it tries its best not to combine outputs across multiple addresses. <priority> is the priority of the transaction. The higher the priority, the higher the transaction fee. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command "set priority") is used. <ring_size> is the number of inputs to include for untraceability. Multiple payments can be made at once by adding URI_2 or <address_2> <amount_2> etcetera (before the payment ID, if it's included) - - usage: get_spend_proof <txid> [<message>] + + locked_transfer [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] (<URI> | <addr> <amount>) <lockblocks> [<payment_id>] - - wallet is watch-only and cannot generate the proof + + Transfer <amount> to <address> and lock it for <lockblocks> (max. 1000000). If the parameter "index=<N1>[,<N2>,...]" is specified, the wallet uses outputs received by addresses of those indices. If omitted, the wallet randomly chooses address indices to be used. In any case, it tries its best not to combine outputs across multiple addresses. <priority> is the priority of the transaction. The higher the priority, the higher the transaction fee. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command "set priority") is used. <ring_size> is the number of inputs to include for untraceability. Multiple payments can be made at once by adding URI_2 or <address_2> <amount_2> etcetera (before the payment ID, if it's included) - - usage: check_spend_proof <txid> <signature_file> [<message>] + + sweep_all [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] [outputs=<N>] <address> [<payment_id>] - - usage: get_reserve_proof (all|<amount>) [<message>] + + Send all unlocked balance to an address. If the parameter "index<N1>[,<N2>,...]" is specified, the wallet sweeps outputs received by those address indices. If omitted, the wallet randomly chooses an address index to be used. If the parameter "outputs=<N>" is specified and N > 0, wallet splits the transaction into N even outputs. - - The reserve proof can be generated only by a full wallet + + sweep_single [<priority>] [<ring_size>] [outputs=<N>] <key_image> <address> [<payment_id>] - - usage: check_reserve_proof <address> <signature_file> [<message>] + + Available options: + seed language + Set the wallet's seed language. + always-confirm-transfers <1|0> + Whether to confirm unsplit txes. + print-ring-members <1|0> + Whether to print detailed information about ring members during confirmation. + store-tx-info <1|0> + Whether to store outgoing tx info (destination address, payment ID, tx secret key) for future reference. + default-ring-size <n> + Set the default ring size (default and minimum is 5). + auto-refresh <1|0> + Whether to automatically synchronize new blocks from the daemon. + refresh-type <full|optimize-coinbase|no-coinbase|default> + Set the wallet's refresh behaviour. + priority [0|1|2|3|4] + Set the fee to default/unimportant/normal/elevated/priority. + confirm-missing-payment-id <1|0> + ask-password <0|1|2 (or never|action|decrypt)> + unit <monero|millinero|micronero|nanonero|piconero> + Set the default monero (sub-)unit. + min-outputs-count [n] + Try to keep at least that many outputs of value at least min-outputs-value. + min-outputs-value [n] + Try to keep at least min-outputs-count outputs of at least that value. + merge-destinations <1|0> + Whether to merge multiple payments to the same destination address. + confirm-backlog <1|0> + Whether to warn if there is transaction backlog. + confirm-backlog-threshold [n] + Set a threshold for confirm-backlog to only warn if the transaction backlog is greater than n blocks. + refresh-from-block-height [n] + Set the height before which to ignore blocks. + auto-low-priority <1|0> + Whether to automatically use the low priority fee level when it's safe to do so. + segregate-pre-fork-outputs <1|0> + Set this if you intend to spend outputs on both Monero AND a key reusing fork. + key-reuse-mitigation2 <1|0> + Set this if you are not sure whether you will spend on a key reusing Monero fork later. +subaddress-lookahead <major>:<minor> + Set the lookahead sizes for the subaddress hash table. + Set this if you are not sure whether you will spend on a key reusing Monero fork later. + segregation-height <n> + Set to the height of a key reusing fork you want to use, 0 to use default. - - Address must not be a subaddress + + blackball <amount>/<offset> | <filename> [add] - - Good signature -- total: %s, spent: %s, unspent: %s + + unblackball <amount>/<offset> - - usage: show_transfers [in|out|all|pending|failed] [index=<N1>[,<N2>,...]] [<min_height> [<max_height>]] + + blackballed <amount>/<offset> - - [Double spend seen on the network: this transaction may or may not end up being mined] + + Password needed (%s) - use the refresh command - - usage: unspent_outputs [index=<N1>[,<N2>,...]] [<min_amount> [<max_amount>]] + + usage: sweep_single [<priority>] [<ring_size>] [outputs=<N>] <key_image> <address> [<payment_id>] - - There is no unspent output in the specified address + + wallet - + (no daemon) - + (out of sync) - + (Untitled account) - - - - - - + + + + + + failed to parse index: - - + + specify an index between 0 and - + usage: account account new <label text with white spaces allowed> @@ -3004,695 +4016,629 @@ Warning: Some input keys being spent are from - + Grand total: Balance: - + , unlocked balance: - + Untagged accounts: - + Tag %s is unregistered. - + Accounts with tag: - + Tag's description: - + Account - + %c%8u %6s %21s %21s %21s - + ---------------------------------------------------------------------------------- - + %15s %21s %21s - + Primary address - + (used) - + (Untitled address) - + <index_min> is already out of bound - + <index_max> exceeds the bound - + usage: address [ new <label text with white spaces allowed> | all | <index_min> [<index_max>] | label <index> <label text with white spaces allowed> ] - - - Integrated addresses can only be created for account 0 - - - - - Integrated address: %s, payment ID: %s - - - - - Subaddress: - - - - - usage: get_description - - - - - no description found - - - - - description found: - - - - - Filename: - - - - - Watch only + + usage: integrated_address [payment ID] - - %u/%u multisig%s + + + Integrated addresses can only be created for account 0 - - Normal + + Matching integrated address: - - Type: + + Integrated address: %s, payment ID: %s - - Testnet: + + Subaddress: - - Yes + + Standard address: - - No + + failed to parse payment ID or address - - This wallet is multisig and cannot sign + + usage: address_book [(add (<address> [pid <long or short payment id>])|<integrated address> [<description possibly with whitespaces>])|(delete <index>)] - - usage: verify <filename> <address> <signature> + + failed to parse payment ID - - Bad signature from + + failed to parse index - - Good signature from + + Address book is empty. - - usage: export_key_images <filename> + + Index: - - wallet is watch-only and cannot export key images + + + Address: - - - - failed to save file + + Payment ID: - - Signed key images exported to + + + Description: - - usage: import_key_images <filename> + + usage: set_tx_note [txid] free text note - - usage: export_outputs <filename> + + usage: get_tx_note [txid] - - Outputs exported to + + usage: get_description - - usage: import_outputs <filename> + + no description found - - - - - amount is wrong: + + description found: - - expected number from 0 to + + Filename: - - Sweeping + + Watch only - - Money successfully sent, transaction: + + %u/%u multisig%s - - Change goes to more than one address + + %s change to %s - - %s change to %s + + + This transaction has %u confirmations + + + genms - - no change + + Base filename (-1, -2, etc suffixes will be appended as needed) - - - - Transaction successfully signed to file + + Give threshold and participants at once as M/N - - usage: get_tx_key <txid> + + How many participants will share parts of the multisig wallet - - - - - - - - - - failed to parse txid + + How many signers are required to sign a valid transaction - - Tx key: + + Create testnet multisig wallets - - no tx keys found for this txid + + Create stagenet multisig wallets - - usage: get_tx_proof <txid> <address> [<message>] + + Create an address file for new wallets - - - - signature file saved to: + + Generating %u %u/%u multisig wallets - - - - failed to save signature file + + Failed to verify multisig info - - usage: check_tx_key <txid> <txkey> <address> + + Error verifying multisig extra info - - - failed to parse tx key + + Error finalizing multisig - - - - error: + + Generated multisig wallets for address - - - received + + Error creating multisig wallets: - - - in txid + + This program generates a set of multisig wallets - use this simpler scheme only if all the participants trust each other - - - received nothing in txid + + Error: Can't specify more than one of --testnet and --stagenet - - - WARNING: this transaction is not yet included in the blockchain! + + Error: expected N/M, but got: - - - This transaction has %u confirmations + + + Error: either --scheme or both of --threshold and --participants may be given - - - WARNING: failed to determine number of confirmations! + + Error: expected N > 1 and N <= M, but got N==%u and M==%d - - bad min_height parameter: + + Error: --filename-base is required - - bad max_height parameter: + + Error: unsupported scheme: only N/N and N-1/N are supported + + + sw - - in + + Generate new wallet and save it to <arg> - - - out + + Generate new wallet from device and save it to <arg> - - failed + + Generate incoming-only wallet from view key - - pending + + Generate deterministic wallet from spend key - - <min_amount> should be smaller than <max_amount> + + Generate wallet from private keys - - -Amount: + + Generate a master wallet from multisig wallet keys - - , number of keys: + + Language for mnemonic - - + + Specify Electrum seed for wallet recovery/creation - - -Min block height: + + Recover wallet using Electrum-style mnemonic seed - - -Max block height: + + Recover multisig wallet using Electrum-style mnemonic seed - - -Min amount found: + + Generate non-deterministic view and spend keys - - -Max amount found: + + failed to read wallet password - - -Total count: + + Enter a new password for the wallet - - -Bin size: + + Wallet password - - -Outputs per *: + + invalid argument: must be either 0/1, true/false, y/n, yes/no - - count - ^ - + + DNSSEC validation passed - - | + + WARNING: DNSSEC validation was unsuccessful, this address may not be correct! - - + + + For URL: - - +--> block height - + + Monero Address = - - ^ + + Is this OK? (Y/n) - - ^ - + + you have cancelled the transfer request - - + + failed to parse index: - - wallet + + invalid format for subaddress lookahead; must be <major>:<minor> - - - Random payment ID: + + no connection to daemon. Please make sure daemon is running. - - Matching integrated address: + + RPC error: - - - genms - - Base filename (-1, -2, etc suffixes will be appended as needed) + + failed to get random outputs to mix: - - Give threshold and participants at once as M/N + + + Not enough money in unlocked balance - - How many participants will share parts of the multisig wallet + + Failed to find a way to create transactions. This is usually due to dust which is so small it cannot pay for itself in fees, or trying to send more money than the unlocked balance, or not leaving enough for fees - - How many signers are required to sign a valid transaction + + not enough outputs for specified ring size - - Create testnet multisig wallets + + output amount - - Generating %u %u/%u multisig wallets + + found outputs to use - - Error verifying multisig extra info + + Please use sweep_unmixable. - - Error finalizing multisig + + transaction was not constructed - - Generated multisig wallets for address + + transaction %s was rejected by daemon with status: - - Error creating multisig wallets: + + Reason: - - This program generates a set of multisig wallets - use this simpler scheme only if all the participants trust each other + + one of destinations is zero - - Error: expected N/M, but got: + + failed to find a suitable way to split transactions - - - Error: either --scheme or both of --threshold and --participants may be given + + unknown transfer error: - - Error: expected N > 1 and N <= M, but got N==%u and M==%d + + Multisig error: - - Error: --filename-base is required + + internal error: - - Error: unsupported scheme: only N/N and N-1/N are supported + + unexpected error: - - - sw - - Generate new wallet and save it to <arg> + + There was an error, which could mean the node may be trying to get you to retry creating a transaction, and zero in on which outputs you own. Or it could be a bona fide error. It may be prudent to disconnect from this node, and not try to send a transaction immediately. Alternatively, connect to another node so the original node cannot correlate information. - - Generate incoming-only wallet from view key + + File %s likely stores wallet private keys! Use a different file name. - - Generate deterministic wallet from spend key + + File %s already exists. Are you sure to overwrite it? (Y/Yes/N/No): - - Generate wallet from private keys + + seconds - - Generate a master wallet from multisig wallet keys + + minutes - - Language for mnemonic + + hours - - Specify Electrum seed for wallet recovery/creation + + days - - Recover wallet using Electrum-style mnemonic seed + + months - - Recover multisig wallet using Electrum-style mnemonic seed + + a long time - - Generate non-deterministic view and spend keys + + This is the command line monero wallet. It needs to connect to a monero +daemon to work correctly. +WARNING: Do not reuse your Monero keys on another fork, UNLESS this fork has key reuse mitigations built in. Doing so will harm your privacy. - - Enable commands which rely on a trusted daemon + + Unknown command: - + Allow communicating with a daemon that uses a different RPC version - + Restore from specific blockchain height - + The newly created transaction will not be relayed to the monero network - - daemon is busy. Please try again later. + + Create an address file for new wallets - - possibly lost connection to daemon + + Display English language names - - Error: + + + daemon is busy. Please try again later. - - This is the command line monero wallet. It needs to connect to a monero -daemon to work correctly. + + possibly lost connection to daemon + + + + + Error: - + Failed to initialize wallet @@ -3700,299 +4646,354 @@ daemon to work correctly. tools::wallet2 - + Use daemon instance at <host>:<port> - + Use daemon instance at host <arg> instead of localhost - + Wallet password file - + Use daemon instance at port <arg> instead of 18081 - + For testnet. Daemon must also be launched with --testnet flag - - Restricts to view-only commands - - - - + can't specify daemon host or port more than once - + can't specify more than one of --password and --password-file - + the password file specified could not be read - + Failed to load file - + Wallet password (escape/quote as needed) - + + Enable commands which rely on a trusted daemon + + + + + Disable commands which rely on a trusted daemon + + + + Specify username[:password] for daemon RPC client - + + For stagenet. Daemon must also be launched with --stagenet flag + + + + + Set shared ring database path + + + + + Number of rounds for the key derivation function + + + + + HW device to use + + + + + --trusted-daemon and --untrusted-daemon are both seen, assuming untrusted + + + + + Daemon is local, assuming trusted + + + + no password specified; use --prompt-for-password to prompt for a password - + + Enter a new password for the wallet + + + + + Wallet password + + + + Failed to parse JSON - + Version %u too new, we can only grok up to %u - + failed to parse view key secret key - - - + + + failed to verify view key secret key - + failed to parse spend key secret key - - - + + + failed to verify spend key secret key - + Electrum-style word list failed verification - - At least one of Electrum-style word list and private view key and private spend key must be specified + + At least one of either an Electrum-style word list, private view key, or private spend key must be specified - + Both Electrum-style word list and private key(s) specified - + invalid address - + view key does not match standard address - + spend key does not match standard address - + Cannot generate deprecated wallets from JSON - + failed to parse address: - + Address must be specified in order to create watch-only wallet - + failed to generate new wallet: - - - - - - - - + + Password is needed to compute key image for incoming monero + + + + + Invalid password: password is needed to compute key image for incoming monero + + + + + + Primary account - + No funds received in this tx. - + failed to read file - - - tools::wallet_rpc_server - - Daemon is local, assuming trusted + + Set subaddress lookahead sizes to <major>:<minor> + + + tools::wallet_rpc_server - + Failed to create directory - + Failed to create directory %s: %s - + Cannot specify -- - + and -- - + Failed to create file - + . Check permissions or remove file - + Error writing to file - + RPC username/password is stored in file - + Tag %s is unregistered. - + Transaction not possible. Available only %s, transaction amount %s = %s + %s (fee) - + This is the RPC monero wallet. It needs to connect to a monero daemon to work correctly. - + + Can't specify more than one of --testnet and --stagenet + + + + Can't specify more than one of --wallet-file and --generate-from-json - + Must specify --wallet-file or --generate-from-json or --wallet-dir - + Loading wallet... - - + + Saving wallet... - - + + Successfully saved - + Successfully loaded - + Wallet initialization failed: - + Failed to initialize wallet RPC server - + Starting wallet RPC server - + Failed to run wallet: - + Stopped wallet RPC server - + Failed to save wallet: @@ -4000,9 +5001,9 @@ daemon to work correctly. wallet_args - - - + + + Wallet options @@ -4017,48 +5018,48 @@ daemon to work correctly. - + Max number of threads to use for a parallel job - + Specify log file - + Config file - + General options - + This is the command line monero wallet. It needs to connect to a monero daemon to work correctly. - + Can't find config file - + Logging to: - + Logging to %s - + Usage: From 3d722db487ab106443d1d30a0b4209c86b9b7f7a Mon Sep 17 00:00:00 2001 From: Guillaume LE VAILLANT Date: Sat, 6 Oct 2018 16:08:16 +0200 Subject: [PATCH 0531/1404] wallet-cli: Update French translation --- translations/monero_fr.ts | 3770 +++++++++++++++++++++++-------------- 1 file changed, 2390 insertions(+), 1380 deletions(-) diff --git a/translations/monero_fr.ts b/translations/monero_fr.ts index 957d2a38250..bac742c56dc 100644 --- a/translations/monero_fr.ts +++ b/translations/monero_fr.ts @@ -27,45 +27,55 @@ Monero::PendingTransactionImpl - + Attempting to save transaction to file, but specified file(s) exist. Exiting to not risk overwriting. File: - Tentative d'enregistrement d'une transaction dans un fichier, mais le fichier spécifié existe déjà. Sortie pour ne pas risquer de l'écraser. Fichier : + Tentative d'enregistrement d'une transaction dans un fichier, mais le fichier spécifié existe déjà. Sortie pour ne pas risquer de l'écraser. Fichier : - + Failed to write transaction(s) to file Échec de l'écriture de(s) transaction(s) dans le fichier - + daemon is busy. Please try again later. le démon est occupé. Veuillez réessayer plus tard. - + no connection to daemon. Please make sure daemon is running. pas de connexion au démon. Veuillez vous assurer que le démon fonctionne. - + transaction %s was rejected by daemon with status: - la transaction %s a été rejetée par le démon avec le statut : + la transaction %s a été rejetée par le démon avec le statut : - + . Reason: - . Raison : + . Raison : - + Unknown exception: - Exception inconnue : + Exception inconnue : - + Unhandled exception Exception non gérée + + + Couldn't multisig sign data: + Signature multisig des données impossible : + + + + Couldn't sign multisig transaction: + Signature multisig de la transaction impossible : + Monero::UnsignedTransactionImpl @@ -124,281 +134,406 @@ Monero::WalletImpl - + payment id has invalid format, expected 16 or 64 character hex string: - format d'identifiant de paiement invalide, 16 ou 64 caractères hexadécimaux attendus : + format d'identifiant de paiement invalide, 16 ou 64 caractères hexadécimaux attendus : - + Failed to add short payment id: - Échec de l'ajout de l'ID de paiement court : + Échec de l'ajout de l'ID de paiement court : - - + + daemon is busy. Please try again later. le démon est occupé. Veuillez réessayer plus tard. - - + + no connection to daemon. Please make sure daemon is running. pas de connexion au démon. Veuillez vous assurer que le démon fonctionne. - - + + RPC error: - Erreur RPC : + Erreur RPC : - - + + not enough outputs for specified ring size pas assez de sorties pour la taille de cercle spécifiée - - + + found outputs to use sorties à utiliser trouvées - + Please sweep unmixable outputs. Veuillez balayer les sorties non mélangeables. - - failed to get random outputs to mix - échec de la récupération de sorties aléatoires à mélanger - - - - + + not enough money to transfer, available only %s, sent amount %s pas assez de fonds pour le transfert, montant disponible %s, montant envoyé %s - + failed to parse address échec de l'analyse de l'adresse - + failed to parse secret spend key échec de l'analyse de la clé secrète de dépense - - No view key supplied, cancelled - Pas de clé d'audit fournie, annulation - - - + failed to parse secret view key échec de l'analyse de la clé secrète d'audit - + failed to verify secret spend key échec de la vérification de la clé secrète de dépense - + spend key does not match address la clé de dépense ne correspond pas à l'adresse - + failed to verify secret view key échec de la vérification de la clé secrète d'audit - + view key does not match address la clé d'audit ne correspond pas à l'adresse - + + failed to generate new wallet: - échec de la génération du nouveau portefeuille : + échec de la génération du nouveau portefeuille : - + Failed to send import wallet request Échec de l'envoi de la requête d'importation de portefeuille - + Failed to load unsigned transactions Échec du chargement des transaction non signées - + Failed to load transaction from file Échec du chargement de la transaction du fichier - + Wallet is view only Portefeuille d'audit uniquement - + failed to save file échec de l'enregistrement du fichier - + Key images can only be imported with a trusted daemon Les images de clé ne peuvent être importées qu'avec un démon de confiance - + Failed to import key images: - Échec de l'importation des images de clé : + Échec de l'importation des images de clé : - + Failed to get subaddress label: - Échec de la récupération de l'étiquette de sous-adresse : + Échec de la récupération de l'étiquette de sous-adresse : - + Failed to set subaddress label: - Échec de l'affectation de l'étiquette de sous-adresse : + Échec de l'affectation de l'étiquette de sous-adresse : + + + + Neither view key nor spend key supplied, cancelled + Ni clé d'audit ni clé de dépense fournie, annulation + + + + Electrum seed is empty + La phrase Electrum est vide + + + + Electrum-style word list failed verification + Échec de la vérification de la liste de mots de style Electrum + + + + Failed to get multisig info: + Échec de la récupération des infos multisig : + + + + Failed to make multisig: + Échec de la création multisig : + + + + Failed to finalize multisig wallet creation + Échec de la finalisation de la création du portefeuille multisig + + + + Failed to finalize multisig wallet creation: + Échec de la finalisation de la création du portefeuille multisig : + + + + Failed to export multisig images: + Échec de l'exportation des images multisig : + + + + Failed to parse imported multisig images + Échec de l'analyse des images multisig importées + + + + Failed to import multisig images: + Échec de l'importation des images multisig : - - failed to get random outputs to mix: %s - échec de la récupération de sorties aléatoires à mélanger : %s + + Failed to check for partial multisig key images: + Échec de la vérification des images de clé multisig partielles : + + + + Failed to restore multisig transaction: + Échec de la restauration de la transaction multisig : + + + + Invalid destination address + Adresse de destination invalide + + + + failed to get outputs to mix: %s + échec de la récupération de sorties à mélanger : %s - - + + not enough money to transfer, overall balance only %s, sent amount %s pas assez de fonds pour le transfer, solde global disponible %s, montant envoyé %s - - + + not enough money to transfer, available only %s, transaction amount %s = %s + %s (fee) pas assez de fonds pour le transfert, montant disponible %s, montant envoyé %s = %s + %s (frais) - - + + output amount montant de la sortie - - + + transaction was not constructed la transaction n'a pas été construite - - + + transaction %s was rejected by daemon with status: - la transaction %s a été rejetée par le démon avec le statut : + la transaction %s a été rejetée par le démon avec le statut : - - + + one of destinations is zero une des destinations est zéro - - + + failed to find a suitable way to split transactions échec de la recherche d'une façon adéquate de scinder les transactions - - + + unknown transfer error: - erreur de transfert inconnue : + erreur de transfert inconnue : - - + + internal error: - erreur interne : + erreur interne : - - + + unexpected error: - erreur inattendue : + erreur inattendue : - - + + unknown error erreur inconnue - - - - - - + + failed to get outputs to mix + échec de la récupération de sorties à mélanger + + + + + + + + + Failed to parse txid Échec de l'analyse de l'ID de transaction - + no tx keys found for this txid aucune clé de transaction trouvée pour cet ID de transaction - - + + Failed to parse tx key Échec de l'analyse de la clé de transaction - - - - + + + + Failed to parse address Échec de l'analyse de l'adresse - + Address must not be a subaddress L'adresse ne doit pas être une sous-adresse - + + The wallet must be in multisig ready state + Le portefeuille doit être multisig et prêt + + + + Given string is not a key + La chaîne entrée n'est pas une clé + + + Rescan spent can only be used with a trusted daemon Réexaminer les dépenses ne peut se faire qu'avec un démon de confiance + + + Invalid output: + Sortie invalide : + + + + Failed to set blackballed outputs + Échec de l'affectation des sorties blackboulées + + + + + Failed to parse output amount + Échec de l'analyse du montant de la sortie + + + + + Failed to parse output offset + Échec de l'analyse de l'offset de la sortie + + + + Failed to blackball output + Échec du blackboulage de la sortie + + + + Failed to unblackball output + Échec du déblackboulage de la sortie + + + + + Failed to parse key image + Échec de l'analyse de l'image de clé + + + + Failed to get ring + Échec de la récupération du cercle + + + + Failed to get rings + Échec de la récupération des cercles + + + + Failed to set ring + Échec de l'affectation du cercle + Wallet - + Failed to parse address Échec de l'analyse de l'adresse - + Failed to parse key Échec de l'analyse de la clé - + failed to verify key Échec de la vérification de la clé - + key does not match address la clé ne correspond pas à l'adresse @@ -449,18 +584,18 @@ autorise les connexions entrantes non cryptées venant de l'extérieur. Considérez plutôt un tunnel SSH ou un proxy SSL. Outrepasser avec -- - + Username specified with -- Le nom d'utilisateur spécifié avec -- - - + + cannot be empty ne peut pas être vide - + requires RPC server password -- nécessite le mot de passe du serveur RPC -- @@ -468,1127 +603,1009 @@ cryptonote::simple_wallet - + Commands: - Commandes : + Commandes : - + failed to read wallet password échec de la lecture du mot de passe du portefeuille - + invalid password mot de passe invalide - + set seed: needs an argument. available options: language - set seed : requiert un argument. options disponibles : language + set seed : requiert un argument. options disponibles : language - + set: unrecognized argument(s) - set : argument(s) non reconnu(s) + set : argument(s) non reconnu(s) - + wallet file path not valid: - chemin du fichier portefeuille non valide : + chemin du fichier portefeuille non valide : - + Attempting to generate or restore wallet, but specified file(s) exist. Exiting to not risk overwriting. Tentative de génération ou de restauration d'un portefeuille, mais le fichier spécifié existe déjà. Sortie pour ne pas risquer de l'écraser. - + usage: payment_id - usage : payment_id + usage : payment_id - + needs an argument requiert un argument - - - - - - - - - - + + + + + + + + + + + + 0 or 1 0 ou 1 - - 0, 1, 2, 3, or 4 - 0, 1, 2, 3 ou 4 - - - - + + + unsigned integer entier non signé - + NOTE: the following 25 words can be used to recover access to your wallet. Write them down and store them somewhere safe and secure. Please do not store them in your email or on file storage services outside of your immediate control. - VEUILLEZ NOTER : les 25 mots suivants peuvent être utilisés pour restaurer votre portefeuille. Veuillez les écrire sur papier et les garder dans un endroit sûr. Ne les gardez pas dans un courriel ou dans un service de stockage de fichiers hors de votre contrôle. + VEUILLEZ NOTER : les 25 mots suivants peuvent être utilisés pour restaurer votre portefeuille. Veuillez les écrire sur papier et les garder dans un endroit sûr. Ne les gardez pas dans un courriel ou dans un service de stockage de fichiers hors de votre contrôle. - + --restore-deterministic-wallet uses --generate-new-wallet, not --wallet-file --restore-deterministic-wallet utilise --generate-new-wallet, pas --wallet-file - + specify a recovery parameter with the --electrum-seed="words list here" spécifiez un paramètre de récupération avec --electrum-seed="liste de mots ici" - + specify a wallet path with --generate-new-wallet (not --wallet-file) spécifiez un chemin de portefeuille avec --generate-new-wallet (pas --wallet-file) - + wallet failed to connect to daemon: - échec de la connexion du portefeuille au démon : + échec de la connexion du portefeuille au démon : - + Daemon uses a different RPC major version (%u) than the wallet (%u): %s. Either update one of them, or use --allow-mismatched-daemon-version. - Le démon utilise une version majeure de RPC (%u) différente de celle du portefeuille (%u) : %s. Mettez l'un des deux à jour, ou utilisez --allow-mismatched-daemon-version. + Le démon utilise une version majeure de RPC (%u) différente de celle du portefeuille (%u) : %s. Mettez l'un des deux à jour, ou utilisez --allow-mismatched-daemon-version. - + List of available languages for your wallet's seed: - Liste des langues disponibles pour la phrase mnémonique de votre portefeuille : + Liste des langues disponibles pour la phrase mnémonique de votre portefeuille : - + Enter the number corresponding to the language of your choice: - Entrez le nombre correspondant à la langue de votre choix : + Entrez le nombre correspondant à la langue de votre choix : - + You had been using a deprecated version of the wallet. Please use the new seed that we provide. Vous avez utilisé une version obsolète du portefeuille. Veuillez dorénavant utiliser la nouvelle phrase mnémonique que nous fournissons. - - + + Generated new wallet: - Nouveau portefeuille généré : + Nouveau portefeuille généré : - - - + + + + failed to generate new wallet: - échec de la génération du nouveau portefeuille : + échec de la génération du nouveau portefeuille : - + Opened watch-only wallet Ouverture du portefeuille d'audit - + Opened wallet Ouverture du portefeuille - + You had been using a deprecated version of the wallet. Please proceed to upgrade your wallet. Vous avez utilisé une version obsolète du portefeuille. Veuillez procéder à la mise à jour de votre portefeuille. - + You had been using a deprecated version of the wallet. Your wallet file format is being upgraded now. Vous avez utilisé une version obsolète du portefeuille. Le format de votre fichier portefeuille est en cours de mise à jour. - + failed to load wallet: - échec du chargement du portefeuille : + échec du chargement du portefeuille : - + Use the "help" command to see the list of available commands. Utilisez la commande "help" pour voir la liste des commandes disponibles. - + Wallet data saved Données du portefeuille sauvegardées - + Mining started in daemon La mine a démarré dans le démon - + mining has NOT been started: - la mine n'a PAS démarré : + la mine n'a PAS démarré : - + Mining stopped in daemon La mine a été stoppée dans le démon - + mining has NOT been stopped: - la mine n'a PAS été stoppée : + la mine n'a PAS été stoppée : - + Blockchain saved Chaîne de blocs sauvegardée - - - + + Height Hauteur - - transaction - transaction - - - + spent dépensé - - unsupported transaction format - format de transaction non supporté - - - + Starting refresh... Démarrage du rafraîchissement... - + Refresh done, blocks received: - Rafraîchissement effectué, blocs reçus : + Rafraîchissement effectué, blocs reçus : - - + payment id has invalid format, expected 16 or 64 character hex string: - format d'identifiant de paiement invalide, 16 ou 64 caractères hexadécimaux attendus : + format d'identifiant de paiement invalide, 16 ou 64 caractères hexadécimaux attendus : - + bad locked_blocks parameter: - mauvais paramètre locked_blocks : + mauvais paramètre locked_blocks : - - - + + a single transaction cannot use more than one payment id: - une unique transaction ne peut pas utiliser plus d'un ID de paiement : + une unique transaction ne peut pas utiliser plus d'un ID de paiement : - - - - + + + + failed to set up payment id, though it was decoded correctly échec de la définition de l'ID de paiement, bien qu'il ait été décodé correctement - - - - - - - - + + + + + + + + transaction cancelled. transaction annulée. - - + + Is this okay anyway? (Y/Yes/N/No): - Est-ce correct quand même ? (Y/Yes/Oui/N/No/Non) : + Est-ce correct quand même ? (Y/Yes/Oui/N/No/Non) : - + There is currently a %u block backlog at that fee level. Is this okay? (Y/Yes/N/No): - Il y a actuellement un arriéré de %u blocs à ce niveau de frais. Est-ce correct quand même ? (Y/Yes/Oui/N/No/Non) : + Il y a actuellement un arriéré de %u blocs à ce niveau de frais. Est-ce correct quand même ? (Y/Yes/Oui/N/No/Non) : - + Failed to check for backlog: - Échec de la vérification du backlog : + Échec de la vérification du backlog : - - + + Transaction Transaction - - + + Spending from address index %d Dépense depuis l'adresse d'index %d - - + + WARNING: Outputs of multiple addresses are being used together, which might potentially compromise your privacy. - ATTENTION : Des sorties de multiples adresses sont utilisées ensemble, ce qui pourrait potentiellement compromettre votre confidentialité. + ATTENTION : Des sorties de multiples adresses sont utilisées ensemble, ce qui pourrait potentiellement compromettre votre confidentialité. - + Sending %s. Envoi de %s. - + Your transaction needs to be split into %llu transactions. This will result in a transaction fee being applied to each transaction, for a total fee of %s Votre transaction doit être scindée en %llu transactions. Il en résulte que des frais de transaction doivent être appliqués à chaque transaction, pour un total de %s - + The transaction fee is %s Les frais de transaction sont de %s - + , of which %s is dust from change , dont %s est de la poussière de monnaie rendue - + . . - + A total of %s from dust change will be sent to dust address Un total de %s de poussière de monnaie rendue sera envoyé à une adresse de poussière - + . This transaction will unlock on block %llu, in approximately %s days (assuming 2 minutes per block) . Cette transaction sera déverrouillée au bloc %llu, dans approximativement %s jours (en supposant 2 minutes par bloc) - - - - - - - - + + + + + + + + Failed to write transaction(s) to file Échec de l'écriture de(s) transaction(s) dans le fichier - - - - - - - - + + + + + + + + Unsigned transaction(s) successfully written to file: - Transaction(s) non signée(s) écrite(s) dans le fichier avec succès : + Transaction(s) non signée(s) écrite(s) dans le fichier avec succès : - + No unmixable outputs found Aucune sortie non mélangeable trouvée - + No address given Aucune adresse fournie - + failed to parse Payment ID échec de l'analyse de l'ID de paiement - - usage: sweep_single [<priority>] [<ring_size>] <key_image> <address> [<payment_id>] - usage : sweep_single [<priorité>] [<taille_cercle>] <image_clé> <adresse> [<ID_paiement>] - - - + failed to parse key image échec de l'analyse de l'image de clé - + No outputs found Pas de sorties trouvées - + Multiple transactions are created, which is not supposed to happen De multiples transactions sont crées, ce qui n'est pas supposé arriver - + The transaction uses multiple or no inputs, which is not supposed to happen La transaction utilise aucune ou de multiples entrées, ce qui n'est pas supposé arriver - + missing threshold amount montant seuil manquant - + invalid amount threshold montant seuil invalide - - donations are not enabled on the testnet - les dons ne sont pas activés sur le réseau testnet - - - + usage: donate [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <amount> [<payment_id>] - usage : donate [index=<N1>[,<N2>,...]] [<priorité>] [<taille_cercle>] <montant> [<ID_paiement>] + usage : donate [index=<N1>[,<N2>,...]] [<priorité>] [<taille_cercle>] <montant> [<ID_paiement>] - + Claimed change does not go to a paid address La monnaie réclamée ne va pas à une adresse payée - + Claimed change is larger than payment to the change address La monnaie réclamée est supérieure au paiement à l'adresse de monnaie - + sending %s to %s envoi de %s à %s - + dummy output(s) sortie(s) factice(s) - + with no destinations sans destination - + Loaded %lu transactions, for %s, fee %s, %s, %s, with min ring size %lu, %s. %sIs this okay? (Y/Yes/N/No): - %lu transactions chargées, pour %s, frais %s, %s, %s, taille de cercle minimum %lu, %s. %sEst-ce correct ? (Y/Yes/Oui/N/No/Non) : + %lu transactions chargées, pour %s, frais %s, %s, %s, taille de cercle minimum %lu, %s. %sEst-ce correct ? (Y/Yes/Oui/N/No/Non) : - + This is a multisig wallet, it can only sign with sign_multisig Ceci est un portefeuille multisig, il ne peut signer qu'avec sign_multisig - - usage: sign_transfer [export] - usage : sign_transfer [export] - - - + Failed to sign transaction Échec de signature de transaction - + Failed to sign transaction: - Échec de signature de transaction : + Échec de signature de transaction : - + Transaction raw hex data exported to Données brutes hex de la transaction exportées vers - + Failed to load transaction from file Échec du chargement de la transaction du fichier - - + + RPC error: - Erreur RPC : + Erreur RPC : - + wallet is watch-only and has no spend key c'est un portefeuille d'audit et il n'a pas de clé de dépense - - - + + + Your original password was incorrect. Votre mot de passe original est incorrect. - + Error with wallet rewrite: - Erreur avec la réécriture du portefeuille : - - - - priority must be 0, 1, 2, 3, or 4 - la priorité doit être 0, 1, 2, 3 ou 4 - - - - - priority must be 0, 1, 2, 3, or 4 - la priorité doit être 0, 1, 2, 3 ou 4 + Erreur avec la réécriture du portefeuille : - + invalid unit unité invalide - - + + invalid count: must be an unsigned integer - nombre invalide : un entier non signé est attendu + nombre invalide : un entier non signé est attendu - + invalid value valeur invalide - + usage: set_log <log_level_number_0-4> | <categories> - usage : set_log <niveau_de_journalisation_0-4> | <catégories> + usage : set_log <niveau_de_journalisation_0-4> | <catégories> - + (Y/Yes/N/No): - (Y/Yes/Oui/N/No/Non) : + (Y/Yes/Oui/N/No/Non) : - - + + bad m_restore_height parameter: - mauvais paramètre m_restore_height : + mauvais paramètre m_restore_height : - + date format must be YYYY-MM-DD le format de date doit être AAAA-MM-JJ - + Restore height is: - La hauteur de restauration est : + La hauteur de restauration est : - - + + + Is this okay? (Y/Yes/N/No): - Est-ce correct ? (Y/Yes/Oui/N/No/Non) : + Est-ce correct ? (Y/Yes/Oui/N/No/Non) : - + Daemon is local, assuming trusted Le démon est local, supposons qu'il est de confiance - + Password for new watch-only wallet Mot de passe pour le nouveau portefeuille d'audit - - invalid arguments. Please use start_mining [<number_of_threads>] [do_bg_mining] [ignore_battery], <number_of_threads> should be from 1 to - arguments invalides. Veuillez utiliser start_mining [<nombre_de_threads>] [mine_en_arrière_plan] [ignorer_batterie], <nombre_de_threads> devrait être entre 1 et - - - + internal error: - erreur interne : + erreur interne : - - - + + + unexpected error: - erreur inattendue : - - - - - - - - - - - + erreur inattendue : + + + + + + + + + + + unknown error erreur inconnue - + refresh failed: - échec du rafraîchissement : + échec du rafraîchissement : - + Blocks received: - Blocs reçus : + Blocs reçus : - + unlocked balance: - solde débloqué : + solde débloqué : - - - + + + amount montant - + false faux - + Unknown command: - Commande inconnue : + Commande inconnue : - + Command usage: - Usage de la commande : + Usage de la commande : - + Command description: - Description de la commande : + Description de la commande : - + wallet is multisig but not yet finalized le portefeuille est multisig mais pas encore finalisé - - Enter optional seed encryption passphrase, empty to see raw seed - Entrer une phrase de passe facultative pour le chiffrement de la phrase mnémonique, effacer pour voir la phrase mnémonique brute - - - + Failed to retrieve seed Échec de la récupération de la phrase mnémonique - + wallet is multisig and has no seed le portefeuille est multisig et n'a pas de phrase mnémonique - + Cannot connect to daemon Impossible de se connecter au démon - - Current fee is %s monero per kB - Les frais sont actuellement de %s monero par kO - - - + Error: failed to estimate backlog array size: - Erreur : échec de l'estimation de la taille du tableau d'arriéré : + Erreur : échec de l'estimation de la taille du tableau d'arriéré : - + Error: bad estimated backlog array size - Erreur : mauvaise estimation de la taille du tableau d'arriéré + Erreur : mauvaise estimation de la taille du tableau d'arriéré - + (current) (actuel) - + %u block (%u minutes) backlog at priority %u%s arriéré de %u bloc(s) (%u minutes) à la priorité %u%s - + %u to %u block (%u to %u minutes) backlog at priority %u arriéré de %u à %u bloc(s) (%u à %u minutes) à la priorité %u - + No backlog at priority Pas d'arriéré à la priorité - - + + This wallet is already multisig Le portefeuille est déjà multisig - - + + wallet is watch-only and cannot be made multisig c'est un portefeuille d'audit et il ne peut pas être tranformé en multisig - - + + This wallet has been used before, please use a new wallet to create a multisig wallet Ce portefeuille a été utilisé auparavant, veuillez utiliser un nouveau portefeuille pour créer un portefeuille multisig - - Your password is incorrect. - Votre mot de passe est incorrect. - - - + Send this multisig info to all other participants, then use make_multisig <threshold> <info1> [<info2>...] with others' multisig info Envoyez ces infos multisig à tous les autres participants, ensuite utilisez make_multisig <seuil> <info1> [<info2>...] avec les infos multisig des autres - + This includes the PRIVATE view key, so needs to be disclosed only to that multisig wallet's participants Ceci inclut la clé PRIVÉE d'audit, donc ne doit être divulgué qu'aux participants de ce portefeuille multisig - + usage: make_multisig <threshold> <multisiginfo1> [<multisiginfo2>...] - usage : make_multisig <seuil> <multisiginfo1> [<multisiginfo2>...] + usage : make_multisig <seuil> <multisiginfo1> [<multisiginfo2>...] - + Invalid threshold Seuil invalide - + Another step is needed Une autre étape est nécessaire - + Send this multisig info to all other participants, then use finalize_multisig <info1> [<info2>...] with others' multisig info Envoyez ces infos multisig à tous les autres participants, ensuite utilisez finalize_multisig <info1> [<info2>...] avec les infos multisig des autres - + Error creating multisig: - Erreur de création multisig : + Erreur de création multisig : - + Error creating multisig: new wallet is not multisig - Erreur de création multisig : le nouveau portefeuille n'est pas multisig + Erreur de création multisig : le nouveau portefeuille n'est pas multisig - + multisig address: - adresse multisig : + adresse multisig : - - - + + + This wallet is not multisig Ce portefeuille n'est pas multisig - + This wallet is already finalized Ce portefeuille est déjà finalisé - + usage: finalize_multisig <multisiginfo1> [<multisiginfo2>...] - usage : finalize_multisig <multisiginfo1> [<multisiginfo2>...] + usage : finalize_multisig <multisiginfo1> [<multisiginfo2>...] - + Failed to finalize multisig Échec de finalisation multisig - + Failed to finalize multisig: - Échec de finalisation multisig : + Échec de finalisation multisig : - - - - - + + + + + This multisig wallet is not yet finalized Ce portefeuille multisig n'est pas encore finalisé - + usage: export_multisig_info <filename> - usage : export_multisig_info <nom_fichier> + usage : export_multisig_info <nom_fichier> - + Error exporting multisig info: - Erreur d'importation des infos multisig : + Erreur d'importation des infos multisig : - + Multisig info exported to Infos multisig exportées vers - + usage: import_multisig_info <filename1> [<filename2>...] - one for each other participant - usage : import_multisig_info <nom_fichier1> [<nom_fichier2>...] - un pour chaque autre participant + usage : import_multisig_info <nom_fichier1> [<nom_fichier2>...] - un pour chaque autre participant - + Multisig info imported Infos multisig importées - + Failed to import multisig info: - Échec de l'importation des infos multisig : + Échec de l'importation des infos multisig : - + Failed to update spent status after importing multisig info: - Échec de la mise à jour de l'état des dépenses après l'importation des infos multisig : + Échec de la mise à jour de l'état des dépenses après l'importation des infos multisig : - + Untrusted daemon, spent status may be incorrect. Use a trusted daemon and run "rescan_spent" Pas un démon de confiance, l'état des dépenses peut être incorrect. Utilisez un démon de confiance et executez "rescan_spent" - - - + + + This is not a multisig wallet Ceci n'est pas un portefeuille multisig - + usage: sign_multisig <filename> - usage : sign_multisig <nom_fichier> + usage : sign_multisig <nom_fichier> - + Failed to sign multisig transaction Échec de la signature de la transaction multisig - + Multisig error: - Erreur multisig : + Erreur multisig : - + Failed to sign multisig transaction: - Échec de la signature de la transaction multisig : + Échec de la signature de la transaction multisig : - + It may be relayed to the network with submit_multisig Elle peut être transmise au réseau avec submit_multisig - + usage: submit_multisig <filename> - usage : submit_multisig <nom_fichier> + usage : submit_multisig <nom_fichier> - - + + Failed to load multisig transaction from file Échec du chargement de la transaction multisig du fichier - - + + Multisig transaction signed by only %u signers, needs %u more signatures Transaction multisig signée par %u signataire(s) seulement, nécessite %u signature(s) de plus - - + + Transaction successfully submitted, transaction Transaction transmise avec succès, transaction - - + + You can check its status by using the `show_transfers` command. Vous pouvez vérifier son statut en utilisant la commane 'show_transfers'. - + usage: export_raw_multisig <filename> - usage : export_raw_multisig <nom_fichier> + usage : export_raw_multisig <nom_fichier> - + Failed to export multisig transaction to file Échec de l'exportation de la transaction multisig vers le fichier - + Saved exported multisig transaction file(s): - Transaction multisig enregistrée dans le(s) fichier(s) : + Transaction multisig enregistrée dans le(s) fichier(s) : - - - + + + ring size must be an integer >= la taille de cercle doit être un nombre entier >= - + could not change default ring size échec du changement de la taille de cercle par défaut - + + Invalid height Hauteur invalide - + start_mining [<number_of_threads>] [bg_mining] [ignore_battery] start_mining [<nombre_de_threads>] [mine_arrière_plan] [ignorer_batterie] - + Start mining in the daemon (bg_mining and ignore_battery are optional booleans). Démarrer la mine dans le démon (mine_arrière_plan et ignorer_batterie sont des booléens facultatifs). - + Stop mining in the daemon. Arrêter la mine dans le démon. - - set_daemon <host>[:<port>] - set_daemon <hôte>[:<port>] - - - + Set another daemon to connect to. Spécifier un autre démon auquel se connecter. - + Save the current blockchain data. Sauvegarder les données actuelles de la châine de blocs. - + Synchronize the transactions and balance. Synchroniser les transactions et le solde. - + balance [detail] solde [détail] - + Show the wallet's balance of the currently selected account. Afficher le solde du compte actuellement sélectionné. - + incoming_transfers [available|unavailable] [verbose] [index=<N1>[,<N2>[,...]]] incoming_transfers [available|unavailable] [verbose] [index=<N1>[,<N2>[,...]]] - + Show the incoming transfers, all or filtered by availability and address index. Afficher les transferts entrants, tous ou filtrés par disponibilité et index d'adresse. - + payments <PID_1> [<PID_2> ... <PID_N>] payments <PID_1> [<PID_2> ... <PID_N>] - + Show the payments for the given payment IDs. Afficher les paiements pour les IDs de paiement donnés. - + Show the blockchain height. Afficher la hauteur de la chaîne de blocs. - - transfer_original [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <address> <amount> [<payment_id>] - transfer_original [index=<N1>[,<N2>,...]] [<priorité>] [<taille_cercle>] <adresse> <montant> [<ID_paiement>] - - - - Transfer <amount> to <address> using an older transaction building algorithm. If the parameter "index=<N1>[,<N2>,...]" is specified, the wallet uses outputs received by addresses of those indices. If omitted, the wallet randomly chooses address indices to be used. In any case, it tries its best not to combine outputs across multiple addresses. <priority> is the priority of the transaction. The higher the priority, the higher the transaction fee. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command "set priority") is used. <ring_size> is the number of inputs to include for untraceability. Multiple payments can be made at once by adding <address_2> <amount_2> etcetera (before the payment ID, if it's included) - Transférer <montant> à <adresse> en utilisant un algorithme de construction de transaction plus ancien. Si le paramètre "index=<N1>[,<N2>,...]" est spécifié, le portefeuille utilise les sorties reçues par les adresses de ces indices. Si il est omis, le portefeuille choisit les indices d'adresse à utiliser aléatoirement. Dans tous les cas, il essaye de ne pas combiner des sorties de multiples adresses. <priorité> est la priorité de la transaction. Plus la priorité est haute, plus les frais de transaction sont élévés. Les valeurs valides par ordre de priorité (de la plus basse à la plus haute) sont : unimportant, normal, elevated, priority. Si elle est omise, la valeur par défaut (voir la commande "set priority") est utilisée. <taille_cercle> est le nombre d'entrées à inclure pour l'intraçabilité. De multiples paiements peuvent être réalisés d'un coup en ajoutant <adresse_2> <montant_2> et cetera (avant l'ID de paiement, si il est inclus) - - - - transfer [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <address> <amount> [<payment_id>] - transfer [index=<N1>[,<N2>,...]] [<priorité>] [<taille_cercle>] <adresse> <montant> [<ID_paiement>] - - - - Transfer <amount> to <address>. If the parameter "index=<N1>[,<N2>,...]" is specified, the wallet uses outputs received by addresses of those indices. If omitted, the wallet randomly chooses address indices to be used. In any case, it tries its best not to combine outputs across multiple addresses. <priority> is the priority of the transaction. The higher the priority, the higher the transaction fee. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command "set priority") is used. <ring_size> is the number of inputs to include for untraceability. Multiple payments can be made at once by adding <address_2> <amount_2> etcetera (before the payment ID, if it's included) - Transférer <montant> à <adresse> Si le paramètre "index=<N1>[,<N2>,...]" est spécifié, le portefeuille utilise les sorties reçues par les adresses de ces indices. Si il est omis, le portefeuille choisit les indices d'adresse à utiliser aléatoirement. Dans tous les cas, il essaye de ne pas combiner des sorties de multiples adresses. <priorité> est la priorité de la transaction. Plus la priorité est haute, plus les frais de transaction sont élévés. Les valeurs valides par ordre de priorité (de la plus basse à la plus haute) sont : unimportant, normal, elevated, priority. Si elle est omise, la valeur par défaut (voir la commande "set priority") est utilisée. <taille_cercle> est le nombre d'entrées à inclure pour l'intraçabilité. De multiples paiements peuvent être réalisés d'un coup en ajoutant <adresse_2> <montant_2> et cetera (avant l'ID de paiement, si il est inclus) - - - - locked_transfer [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <addr> <amount> <lockblocks> [<payment_id>] - locked_transfer [index=<N1>[,<N2>,...]] [<priorité>] [<taille_cercle>] <adresse> <montant> <blocs_verrou> [<ID_paiement>] - - - - Transfer <amount> to <address> and lock it for <lockblocks> (max. 1000000). If the parameter "index=<N1>[,<N2>,...]" is specified, the wallet uses outputs received by addresses of those indices. If omitted, the wallet randomly chooses address indices to be used. In any case, it tries its best not to combine outputs across multiple addresses. <priority> is the priority of the transaction. The higher the priority, the higher the transaction fee. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command "set priority") is used. <ring_size> is the number of inputs to include for untraceability. Multiple payments can be made at once by adding <address_2> <amount_2> etcetera (before the payment ID, if it's included) - Transférer <montant> à <adresse> et le verrouiller pendant <blocs_verrou> (max 1000000). Si le paramètre "index=<N1>[,<N2>,...]" est spécifié, le portefeuille utilise les sorties reçues par les adresses de ces indices. Si il est omis, le portefeuille choisit les indices d'adresse à utiliser aléatoirement. Dans tous les cas, il essaye de ne pas combiner des sorties de multiples adresses. <priorité> est la priorité de la transaction. Plus la priorité est haute, plus les frais de transaction sont élévés. Les valeurs valides par ordre de priorité (de la plus basse à la plus haute) sont : unimportant, normal, elevated, priority. Si elle est omise, la valeur par défaut (voir la commande "set priority") est utilisée. <taille_cercle> est le nombre d'entrées à inclure pour l'intraçabilité. De multiples paiements peuvent être réalisés d'un coup en ajoutant <adresse_2> <montant_2> et cetera (avant l'ID de paiement, si il est inclus) - - - + Send all unmixable outputs to yourself with ring_size 1 Envoyer toutes les sorties non mélangeables à vous-même avec une taille de cercle de 1 - - sweep_all [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <address> [<payment_id>] - sweep_all [index=<N1>[,<N2>,...]] [<priorité>] [<taille_cercle>] <adresse> [<ID_paiement>] - - - - Send all unlocked balance to an address. If the parameter "index<N1>[,<N2>,...]" is specified, the wallet sweeps outputs received by those address indices. If omitted, the wallet randomly chooses an address index to be used. - Envoyer tout le solde débloqué à une adresse. Si le paramètre "index<N1>[,<N2>,...]" est spécifié, le portefeuille balaye les sorties reçues par ces indices d'adresse. Si il est omis, le portefeuille choisit un index d'adresse à utiliser aléatoirement. - - - + sweep_below <amount_threshold> [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <address> [<payment_id>] sweep_below <montant_seuil> [index=<N1>[,<N2>,...]] [<priorité>] [<taille_cercle>] <adresse> [<ID_paiement>] - + Send all unlocked outputs below the threshold to an address. Envoyer toutes les sorties débloquées d'un montant inférieur au seuil à une adresse. - - sweep_single [<priority>] [<ring_size>] <key_image> <address> [<payment_id>] - sweep_single [<priorité>] [<taille_cercle>] <image_clé> <adresse> [<ID_payment>] - - - + Send a single output of the given key image to an address without change. Envoyer une unique sortie ayant une image de clé donnée à une adresse sans rendu de monnaie. - + donate [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <amount> [<payment_id>] donate [index=<N1>[,<N2>,...]] [<priorité>] [<taille_cercle>] <montant> [<ID_paiement>] - + Donate <amount> to the development team (donate.getmonero.org). Donner <montant> à l'équipe de développement (donate.getmonero.org). - - sign_transfer <file> - sign_transfer <fichier> - - - - Sign a transaction from a <file>. - Signer une transaction d'un <fichier>. - - - + Submit a signed transaction from a file. Transmettre une transaction signée d'un fichier. - + set_log <level>|{+,-,}<categories> set_log <niveau>|{+,-,}<catégories> - + Change the current log detail (level must be <0-4>). Changer le niveau de détail du journal (le niveau doit être <0-4>). - + account account new <label text with white spaces allowed> account switch <index> @@ -1605,7 +1622,7 @@ Cette transaction sera déverrouillée au bloc %llu, dans approximativement %s j account tag_description <mot_clé> <description> - + If no arguments are specified, the wallet shows all the existing accounts along with their balances. If the "new" argument is specified, the wallet creates a new account with its label initialized by the provided label text (which can be empty). If the "switch" argument is specified, the wallet switches to the account specified by <index>. @@ -1622,215 +1639,137 @@ Si l'argument "untag" est spécifié, les mots clés assignés au Si l'argument "tag_description" est spécifié, le texte arbitraire <description> est assigné au mot clé <mot_clé>. - + address [ new <label text with white spaces allowed> | all | <index_min> [<index_max>] | label <index> <label text with white spaces allowed>] address [ new <texte étiquette avec espaces autorisés> | all | <index_min> [<index_max>] | label <index> <texte étiquette avec espaces autorisés>] - - If no arguments are specified or <index> is specified, the wallet shows the default or specified address. If "all" is specified, the walllet shows all the existing addresses in the currently selected account. If "new " is specified, the wallet creates a new address with the provided label text (which can be empty). If "label" is specified, the wallet sets the label of the address specified by <index> to the provided label text. - Si aucun argument n'est spécifié ou si <index> est spécifié, le portefeuille affiche l'adresse par défaut ou l'adresse spécifiée. Si "all" est spécifié, le portefeuille affiche toutes les adresses existantes dans le comptes actuellement sélectionné. Si "new" est spécifié, le portefeuille crée une nouvelle adresse avec le texte d'étiquette fourni (qui peut être vide). Si "label" est spécifié, le portefeuille affecte le texte fourni à l'étiquette de l'adresse spécifiée par <index>. - - - + integrated_address [<payment_id> | <address>] integrated_address [<ID_paiement> | <adresse>] - + Encode a payment ID into an integrated address for the current wallet public address (no argument uses a random payment ID), or decode an integrated address to standard address and payment ID Encoder un ID de paiement dans une adresse intégrée pour l'adresse publique du portefeuille actuel (en l'absence d'argument un ID de paiement aléatoire est utilisé), ou décoder une adresse intégrée en une adresse standard et un ID de paiement - + address_book [(add ((<address> [pid <id>])|<integrated address>) [<description possibly with whitespaces>])|(delete <index>)] address_book [(add ((<adresse> [pid <id>])|<adresse intégrée>) [<description avec éventuellement des espaces>])|(delete <index>)] - + Print all entries in the address book, optionally adding/deleting an entry to/from it. Afficher toutes les entrées du carnet d'adresses, optionnellement en y ajoutant/supprimant une entrée. - + Save the wallet data. Sauvegarder les données du portefeuille. - + Save a watch-only keys file. Sauvegarder un fichier de clés d'audit. - + Display the private view key. Afficher la clé privée d'audit. - + Display the private spend key. Afficher la clé privée de dépense. - + Display the Electrum-style mnemonic seed Afficher la phrase mnémonique de style Electrum - + set <option> [<value>] set <option> [<valeur>] - - Available options: - seed language - Set the wallet's seed language. - always-confirm-transfers <1|0> - Whether to confirm unsplit txes. - print-ring-members <1|0> - Whether to print detailed information about ring members during confirmation. - store-tx-info <1|0> - Whether to store outgoing tx info (destination address, payment ID, tx secret key) for future reference. - default-ring-size <n> - Set the default ring size (default and minimum is 5). - auto-refresh <1|0> - Whether to automatically synchronize new blocks from the daemon. - refresh-type <full|optimize-coinbase|no-coinbase|default> - Set the wallet's refresh behaviour. - priority [0|1|2|3|4] - Set the fee to default/unimportant/normal/elevated/priority. - confirm-missing-payment-id <1|0> - ask-password <1|0> - unit <monero|millinero|micronero|nanonero|piconero> - Set the default monero (sub-)unit. - min-outputs-count [n] - Try to keep at least that many outputs of value at least min-outputs-value. - min-outputs-value [n] - Try to keep at least min-outputs-count outputs of at least that value. - merge-destinations <1|0> - Whether to merge multiple payments to the same destination address. - confirm-backlog <1|0> - Whether to warn if there is transaction backlog. - confirm-backlog-threshold [n] - Set a threshold for confirm-backlog to only warn if the transaction backlog is greater than n blocks. - refresh-from-block-height [n] - Set the height before which to ignore blocks. - auto-low-priority <1|0> - Whether to automatically use the low priority fee level when it's safe to do so. - Options disponibles : - seed langue - Définir la langue de la phrase mnémonique. - always-confirm-transfers <1|0> - Confirmation des transactions non divisées. - print-ring-members <1|0> - Affichage d'informations détaillées sur les membres du cercle lors de la confirmation. - store-tx-info <1|0> - Sauvegarde des informations des transactions sortantes (adresse de destination, ID de paiement, clé secrète de transaction) pour référence ultérieure. - default-ring-size <n> - Définir la taille de cercle par défaut (la valeur par défaut est le minimum 5). - auto-refresh <1|0> - Synchronisation automatique des nouveaux blocs du démon. - refresh-type <full|optimize-coinbase|no-coinbase|default> - Définir le comportement du rafraîchissement du portefeuille. - priority [0|1|2|3|4] - Utiliser les frais pour la priorité par défaut/peu importante/normale/élevée/prioritaire. - confirm-missing-payment-id <1|0> - ask-password <1|0> - unit <monero|millinero|micronero|nanonero|piconero> - Définir la (sous-)unité monero par défaut. - min-outputs-count [n] - Essayer de garder au moins ce nombre de sorties d'une valeur d'au moins min-outputs-value. - min-outputs-value [n] - Essayer de garder au moins min-outputs-count sorties d'au moins cette valeur. - merge-destinations <1|0> - Fusion des paiements multiples vers la même adresse de destination. - confirm-backlog <1|0> - Avertir s'il y a un arriéré de transactions. - confirm-backlog-threshold [n] - Définir un seuil pour confirm-backlog pour avertir seulement si l'arriéré de transactions est supérieur à n blocs. - refresh-from-block-height [n] - Définir la hauteur avant laquelle les blocs sont ignorés. - auto-low-priority <1|0> - Utilisation automatique du niveau de frais pour la priorité basse, lorsqu'il est sûr de le faire. - - - + Display the encrypted Electrum-style mnemonic seed. Afficher la phrase mnémonique de style Electrum chiffrée. - + Rescan the blockchain for spent outputs. Rescanner la chaîne de blocs pour trouver les sorties dépensées. - + get_tx_key <txid> get_tx_key <ID_transaction> - + Get the transaction key (r) for a given <txid>. Obtenir la clé de transaction (r) pour un <ID_transaction> donné. - + check_tx_key <txid> <txkey> <address> check_tx_key <ID_transaction> <clé_transaction> <adresse> - + Check the amount going to <address> in <txid>. Vérifier le montant allant à <adresse> dans <ID_transaction>. - + get_tx_proof <txid> <address> [<message>] get_tx_proof <ID_transaction> <adresse> [<message>] - + Generate a signature proving funds sent to <address> in <txid>, optionally with a challenge string <message>, using either the transaction secret key (when <address> is not your wallet's address) or the view secret key (otherwise), which does not disclose the secret key. Générer une signature prouvant l'envoi de fonds à <adresse> dans <ID_transaction>, optionnellement avec un <message> comme challenge, en utilisant soit la clé secrète de transaction (quand <adresse> n'est pas l'adresse de votre portefeuille) soit la clé secrète d'audit (dans le cas contraire), tout en ne divulgant pas la clé secrète. - + check_tx_proof <txid> <address> <signature_file> [<message>] check_tx_proof <ID_transaction> <adresse> <fichier_signature> [<message>] - + Check the proof for funds going to <address> in <txid> with the challenge string <message> if any. Vérifier la validité de la preuve de fonds allant à <adresse> dans <ID_transaction> avec le <message> de challenge s'il y en a un. - + get_spend_proof <txid> [<message>] get_spend_proof <ID_transaction> [<message>] - + Generate a signature proving that you generated <txid> using the spend secret key, optionally with a challenge string <message>. Générer une signature prouvant que vous avez créé <ID_transaction> en utilisant la clé secrète de dépense, optionnellement avec un <message> comme challenge. - + check_spend_proof <txid> <signature_file> [<message>] check_spend_proof <ID_transaction> <fichier_signature> [<message>] - + Check a signature proving that the signer generated <txid>, optionally with a challenge string <message>. Vérifier la validité de la preuve que le signataire a créé <ID_transaction>, optionnellement avec un <message> comme challenge. - + get_reserve_proof (all|<amount>) [<message>] get_reserve_proof (all|<montant>) [<message>] - + Generate a signature proving that you own at least this much, optionally with a challenge string <message>. If 'all' is specified, you prove the entire sum of all of your existing accounts' balances. Otherwise, you prove the reserve of the smallest possible amount above <amount> available in your current account. @@ -1839,368 +1778,348 @@ Si 'all' est spécifié, vous prouvez la somme totale des soldes de to Sinon, vous prouvez le plus petit solde supérieur à <montant> dans votre compte actuel. - + check_reserve_proof <address> <signature_file> [<message>] check_reserve_proof <adresse> <fichier_signature> [<message>] - + Check a signature proving that the owner of <address> holds at least this much, optionally with a challenge string <message>. Vérifier la validité d'une signature prouvant que le propriétaire d'une <adresse> détient au moins un montant, optionnellement avec un <message> comme challenge. - - show_transfers [in|out|pending|failed|pool] [index=<N1>[,<N2>,...]] [<min_height> [<max_height>]] - show_transfers [in|out|pending|failed|pool] [index=<N1>[,<N2>,...]] [<hauteur_min> [<hauteur_max>]] - - - + Show the incoming/outgoing transfers within an optional height range. Afficher les transferts entrants/sortants dans un interval de hauteurs facultatif. - + unspent_outputs [index=<N1>[,<N2>,...]] [<min_amount> [<max_amount>]] unspent_outputs [index=<N1>[,<N2>,...]] [<montant_min> [<montant_max>]] - + Show the unspent outputs of a specified address within an optional amount range. Afficher les sorties non dépensées d'une adresse spécifique dans un interval de montants facultatif. - - Rescan the blockchain from scratch. - Rescanner la chaîne de blocs en partant de zéro. - - - + set_tx_note <txid> [free text note] set_tx_note <ID_transaction> [texte de la note] - + Set an arbitrary string note for a <txid>. Définir un texte arbitraire comme note pour <ID_transaction>. - + get_tx_note <txid> get_tx_note <ID_transaction> - + Get a string note for a txid. Obtenir le texte de la note pour <ID_transaction>. - + set_description [free text note] set_description [texte] - + Set an arbitrary description for the wallet. Définir un texte arbitraire comme description du portefeuille. - + Get the description of the wallet. Obtenir la description du portefeuille. - + Show the wallet's status. Afficher l'état du portefeuille. - + Show the wallet's information. Afficher les informations du portefeuille. - + sign <file> sign <fichier> - + Sign the contents of a file. Signer le contenu d'un fichier. - + verify <filename> <address> <signature> verify <fichier> <adresse> <signature> - + Verify a signature on the contents of a file. Vérifier la signature du contenu d'un fichier. - + export_key_images <file> export_key_images <fichier> - + Export a signed set of key images to a <file>. Exported un ensemble signé d'images de clé vers un <fichier>. - + import_key_images <file> import_key_images <fichier> - + Import a signed key images list and verify their spent status. Importer un ensemble signé d'images de clé et vérifier si elles correspondent à des dépenses. - + export_outputs <file> export_outputs <fichier> - + Export a set of outputs owned by this wallet. Exporter un ensemble de sorties possédées par ce portefeuille. - + import_outputs <file> import_outputs <fichier> - + Import a set of outputs owned by this wallet. Importer un ensemble de sorties possédées par ce portefeuille. - + show_transfer <txid> show_transfer <ID_transaction> - + Show information about a transfer to/from this address. Afficher les information à propos d'un transfert vers/depuis cette adresse. - + Change the wallet's password. Changer le mot de passe du portefeuille. - + Generate a new random full size payment id. These will be unencrypted on the blockchain, see integrated_address for encrypted short payment ids. Générer un nouvel ID de paiement long aléatoire. Ceux-ci sont en clair dans la chaîne de blocs, voir integrated_address pour les IDs de paiement courts cryptés. - + Print the information about the current fee and transaction backlog. Afficher les informations à propos des frais et arriéré de transactions actuels. - + Export data needed to create a multisig wallet Exporter les données nécessaires pour créer un portefeuille multisig - + make_multisig <threshold> <string1> [<string>...] make_multisig <seuil> <chaîne_caractères1> [<chaîne_caractères>...] - + Turn this wallet into a multisig wallet Transformer ce portefeuille en portefeuille multisig - + finalize_multisig <string> [<string>...] finalize_multisig <chaîne_caractères> [<chaîne_caractères>...] - + Turn this wallet into a multisig wallet, extra step for N-1/N wallets Transformer ce portefeuille en portefeuille multisig, étape supplémentaire pour les portefeuilles N-1/N - + export_multisig_info <filename> export_multisig_info <fichier> - + Export multisig info for other participants Exporter les infos multisig pour les autres participants - + import_multisig_info <filename> [<filename>...] import_multisig_info <fichier> [<fichier>...] - + Import multisig info from other participants Importer les infos multisig des autres participants - + sign_multisig <filename> sign_multisig <fichier> - + Sign a multisig transaction from a file Signer une transaction multisig d'un fichier - + submit_multisig <filename> submit_multisig <fichier> - + Submit a signed multisig transaction from a file Transmettre une transaction multisig signée d'un fichier - + export_raw_multisig_tx <filename> export_raw_multisig_tx <fichier> - + Export a signed multisig transaction to a file Exporter une transaction multisig signée vers un fichier - + help [<command>] help [<commande>] - + Show the help section or the documentation about a <command>. Afficher la section d'aide ou la documentation d'une <commande>. - + integer >= entier >= - + block height hauteur de bloc - + No wallet found with that name. Confirm creation of new wallet named: - Aucun portefeuille avec ce nom trouvé. Confirmer la création d'un nouveau portefeuille nommé : - - - - can't specify more than one of --generate-new-wallet="wallet_name", --wallet-file="wallet_name", --generate-from-view-key="wallet_name", --generate-from-spend-key="wallet_name", --generate-from-keys="wallet_name", --generate-from-multisig-keys="wallet_name" and --generate-from-json="jsonfilename" - impossible de spécifier plus d'une option parmis --generate-new-wallet="nom_portefeuille", --wallet-file="nom_portefeuille", --generate-from-view-key="nom_portefeuille", --generate-from-spend-key="nom_portefeuille", --generate-from-keys="nom_portefeuille", --generate-from-multisig-keys="nom_portefeuille" and --generate-from-json="nom_fichier_json" + Aucun portefeuille avec ce nom trouvé. Confirmer la création d'un nouveau portefeuille nommé : - + can't specify both --restore-deterministic-wallet or --restore-multisig-wallet and --non-deterministic impossible de spécifier à la fois --restore-deterministic-wallet ou --restore-multisig-wallet et --non-deterministic - + --restore-multisig-wallet uses --generate-new-wallet, not --wallet-file --restore-multisig-wallet utilise --generate-new-wallet, pas --wallet-file - + specify a recovery parameter with the --electrum-seed="multisig seed here" spécifiez un paramètre de récupération avec --electrum-seed="phrase mnémonique multisig ici" - + Multisig seed failed verification Échec de la vérification de la phrase mnémonique multisig - - Enter seed encryption passphrase, empty if none - Entrer une phrase de passe pour le chiffrement de la phrase mnémonique, vide si aucune - - - - + + This address is a subaddress which cannot be used here. Cette adresse est une sous-adresse qui ne peut pas être utilisée ici. - + Error: expected M/N, but got: - Erreur : M/N attendu, mais lu : + Erreur : M/N attendu, mais lu : - + Error: expected N > 1 and N <= M, but got: - Erreur : N > 1 et N <= M attendu, mais lu : + Erreur : N > 1 et N <= M attendu, mais lu : - + Error: M/N is currently unsupported. - Erreur : M/N n'est actuellement pas supporté. + Erreur : M/N n'est actuellement pas supporté. - + Generating master wallet from %u of %u multisig wallet keys Génération du portefeuille principal à partir de %u de %u clés de portefeuille multisig - + failed to parse secret view key échec de l'analyse de la clé secrète d'audit - + failed to verify secret view key échec de la vérification de la clé secrète d'audit - + Secret spend key (%u of %u): - Clé secrète de dépense (%u de %u) : + Clé secrète de dépense (%u de %u) : - + Error: M/N is currently unsupported - Erreur : M/N n'est actuellement pas supporté + Erreur : M/N n'est actuellement pas supporté - + Restore height Hauteur de restauration - + Still apply restore height? (Y/Yes/N/No): - Appliquer la hauteur de restauration quand même ? (Y/Yes/Oui/N/No/Non) : + Appliquer la hauteur de restauration quand même ? (Y/Yes/Oui/N/No/Non) : - + Warning: using an untrusted daemon at %s, privacy will be lessened - Attention : en n'utilisant %s qui n'est pas un démon de confiance, la confidentialité sera réduite + Attention : en n'utilisant %s qui n'est pas un démon de confiance, la confidentialité sera réduite - + Daemon either is not started or wrong port was passed. Please make sure daemon is running or change the daemon address using the 'set_daemon' command. Le démon n'est pas lancé ou un mauvais port a été fourni. Veuillez vous assurer que le démon fonctionne ou changez l'adresse de démon avec la commande 'set_daemon'. - + Your wallet has been generated! To start synchronizing with the daemon, use the "refresh" command. Use the "help" command to see the list of available commands. @@ -2219,852 +2138,1668 @@ votre portefeuille à nouveau (mais les clés de votre portefeuille ne risquent - + failed to generate new mutlisig wallet échec de la génération du nouveau portefeuille multisig - + Generated new %u/%u multisig wallet: - Nouveau portefeuille multisig %u/%u généré : + Nouveau portefeuille multisig %u/%u généré : - + Opened %u/%u multisig wallet%s Portefeuille multisig %u/%u ouvert%s - + Use "help <command>" to see a command's documentation. Utilisez "help <commande>" pour voir la documentation d'une commande. - + wallet is multisig and cannot save a watch-only version c'est un portefeuille multisig et il ne peut pas sauvegarder une version d'audit - + missing daemon URL argument URL du démon manquante en argument - + Unexpected array length - Exited simple_wallet::set_daemon() Taille de tableau inattendue - Sortie de simple_wallet::set_daemon() - + This does not seem to be a valid daemon URL. Ceci semble ne pas être une URL de démon valide. - - + + txid ID transaction - - + + idx index - + (Some owned outputs have partial key images - import_multisig_info needed) (Certaines sorties ont des images de clé partielles - import_multisig_info requis) - + Currently selected account: [ - Compte actuellement sélectionné : [ + Compte actuellement sélectionné : [ - + ] ] - + Tag: - Mot clé : + Mot clé : - + (No tag assigned) (Pas de mot clé assigné) - + Balance per address: - Solde par adresse : + Solde par adresse : - + Address Adresse - - + + Balance Solde - - + + Unlocked balance Solde débloqué - + Outputs Sorties - - + + Label Étiquette - + %8u %6s %21s %21s %7u %21s %8u %6s %21s %21s %7u %21s - + usage: balance [detail] - usage : balance [detail] + usage : balance [detail] - - + + usage: incoming_transfers [available|unavailable] [verbose] [index=<N>] usage: incoming_transfers [available|unavailable] [verbose] [index=<N>] - + spent dépensé - + global index index global - + tx id ID de transaction - - + + addr index index adresse - + No incoming transfers Aucun transfert entrant - + No incoming available transfers Aucun transfert entrant disponible - + No incoming unavailable transfers Aucun transfert entrant non disponible - + expected at least one payment ID au moins un ID de paiement attendu - + payment paiement - + transaction transaction - + height hauteur - + unlock time durée de déverrouillage - + No payments with id Aucun paiement avec l'ID - - - + + + + failed to get blockchain height: - échec de la récupération de la hauteur de la chaîne de blocs : + échec de la récupération de la hauteur de la chaîne de blocs : - - - - - + + + + + failed to connect to the daemon échec de la connexion au démon - + Transaction %llu/%llu: txid=%s -Transaction %llu/%llu : ID=%s +Transaction %llu/%llu : ID=%s - + Input %llu/%llu: amount=%s -Entrée %llu/%llu : montant=%s +Entrée %llu/%llu : montant=%s - + failed to get output: - échec de la récupération de la sortie : + échec de la récupération de la sortie : - + output key's originating block height shouldn't be higher than the blockchain height la hauteur du bloc d'origine de la clé de la sortie ne devrait pas être supérieure à celle de la chaîne de blocs - + Originating block heights: -Hauteurs des blocs d'origine : +Hauteurs des blocs d'origine : - + | | - - + + | | - + Warning: Some input keys being spent are from -Attention : Certaines clés d'entrées étant dépensées sont issues de +Attention : Certaines clés d'entrées étant dépensées sont issues de - + , which can break the anonymity of ring signature. Make sure this is intentional! , ce qui peut casser l'anonymat du cercle de signature. Assurez-vous que c'est intentionnel ! - - + + + Ring size must not be 0 La taille de cercle ne doit pas être 0 - - + + + ring size %u is too small, minimum is %u la taille de cercle %u est trop petite, le minimum est %u - + wrong number of arguments mauvais nombre d'arguments - - - + + + No payment id is included with this transaction. Is this okay? (Y/Yes/N/No): - Aucun ID de paiement n'est inclus dans cette transaction. Est-ce correct ? (Y/Yes/Oui/N/No/Non) : + Aucun ID de paiement n'est inclus dans cette transaction. Est-ce correct ? (Y/Yes/Oui/N/No/Non) : - - + + No outputs found, or daemon is not ready Aucune sortie trouvée, ou le démon n'est pas prêt - + + command only supported by HW wallet + commande supportée uniquement par un portefeuille matériel + + + + Failed to reconnect device + Échec de la reconnexion à l'appareil + + + + Failed to reconnect device: + Échec de la reconnexion à l'appareil : + + + Transaction successfully saved to Transaction sauvegardée avec succès dans - - + + , txid , ID transaction - + Failed to save transaction to Échec de la sauvegarde de la transaction dans - - + + Sweeping %s in %llu transactions for a total fee of %s. Is this okay? (Y/Yes/N/No): - Balayage de %s dans %llu transactions pour des frais totaux de %s. Est-ce correct ? (Y/Yes/Oui/N/No/Non) : + Balayage de %s dans %llu transactions pour des frais totaux de %s. Est-ce correct ? (Y/Yes/Oui/N/No/Non) : - - - + + + Sweeping %s for a total fee of %s. Is this okay? (Y/Yes/N/No): - Balayage de %s pour des frais totaux de %s. Est-ce correct ? (Y/Yes/Oui/N/No/Non) : - - - - Donating - Don de + Balayage de %s pour des frais totaux de %s. Est-ce correct ? (Y/Yes/Oui/N/No/Non) : - + This is a watch only wallet Ceci est un portefeuille d'audit - + usage: show_transfer <txid> - usage : show_transfer <ID_de_transaction> + usage : show_transfer <ID_de_transaction> - + Double spend seen on the network: this transaction may or may not end up being mined - Double dépense détectée sur le réseau : cette transaction sera peut-être invalidée + Double dépense détectée sur le réseau : cette transaction sera peut-être invalidée - + Transaction ID not found ID de transaction non trouvé - + true vrai - + failed to parse refresh type échec de l'analyse du type de rafraîchissement - - + + + + + + + + + + + + + + + + + + + + + + command not supported by HW wallet + commande non supportée par le portefeuille matériel + + + + wallet is watch-only and has no seed c'est un portefeuille d'audit et il n'a pas de phrase mnémonique - - + + wallet is non-deterministic and has no seed c'est un portefeuille non déterministe et il n'a pas de phrase mnémonique - - + + Enter optional seed offset passphrase, empty to see raw seed + Entrer une phrase de passe facultative pour le décalage de la phrase mnémonique, effacer pour voir la phrase mnémonique brute + + + + Incorrect password + Mot de passe invalide + + + + Current fee is %s %s per %s + Les frais sont actuellement de %s %s par %s + + + + usage: print_ring <key_image|txid> + usage : print_ring <image clé|ID transaction> + + + + + Invalid key image + Image de clé invalide + + + + Invalid txid + ID de transaction invalide + + + + Key image either not spent, or spent with mixin 0 + Image de clé soit non dépensée, soit dépensée avec 0 mélange + + + + Failed to get key image ring: + Échec de la récupération du cercle de l'image de clé : + + + + File doesn't exist + Le fichier d'existe pas + + + + Invalid ring specification: + Spécification de cercle invalide : + + + + Invalid key image: + Image de clé invalide : + + + + Invalid ring type, expected relative or abosolute: + Type de cercle invalide, "relative" ou "absolute" attendu : + + + + + Error reading line: + Erreur lors de la lecture de la ligne : + + + + Invalid ring: + Cercle invalide : + + + + Invalid relative ring: + Cercle relatif invalide : + + + + Invalid absolute ring: + Cercle absolu invalide : + + + + Failed to set ring for key image: + Échec de l'affectation du cercle pour l'image de clé : + + + + Continuing. + On continue. + + + + usage: set_ring <filename> | ( <key_image> absolute|relative <index> [<index>...] ) + usage : set_ring <nom_fichier> | ( <image_clé> absolute|relative <index> [<index>...] ) + + + + Missing absolute or relative keyword + Mot clé "absolute" ou "relative" manquant + + + + + invalid index: must be a strictly positive unsigned integer + index invalide : doit être un nombre entier strictement positif + + + + invalid index: indices wrap + index invalide : boucle des indices + + + + invalid index: indices should be in strictly ascending order + index invalide : les indices doivent être en ordre strictement croissant + + + + failed to set ring + échec de l'affectation du cercle + + + + usage: blackball <amount>/<offset> | <filename> [add] + usage : blackball <montant>/<offset> | <nom_fichier> [add] + + + + First line is not an amount + La première ligne n'est pas un montant + + + + Invalid output: + Sortie invalide : + + + + Bad argument: + Mauvais argument : + + + + should be "add" + devrait être "add" + + + + Failed to open file + Échec de l'ouverture du fichier + + + + Invalid output key, and file doesn't exist + Clé de sortie invalide, et le fichier n'existe pas + + + + Failed to blackball output: + Échec du blackboulage de la sortie : + + + + usage: unblackball <amount>/<offset> + usage : unblackball <montant>/<offset> + + + + + Invalid output + Sortie invalide + + + + + Failed to unblackball output: + Échec du déblackboulage de la sortie : + + + + usage: blackballed <amount>/<offset> + usage : blackballed <montant>/<offset> + + + + Blackballed: + Blackboulé : + + + + not blackballed: + non blackboulé : + + + + Failed to save known rings: + Échec de la sauvegarde des cercles connus : + + + + wallet is watch-only and cannot transfer c'est un portefeuille d'audit et il ne peut pas transférer - + + + WARNING: this is a non default ring size, which may harm your privacy. Default is recommended. + ATTENTION : ceci c'est pas la taille de cercle par défaut, ce qui peut nuire à votre confidentialité. La valeur par défaut est recommandée. + + + + WARNING: from v8, ring size will be fixed and this setting will be ignored. + ATTENTION : ) partir de v8, la taille de cercle sera fixée et ce paramètre sera ignoré. + + + + + + priority must be either 0, 1, 2, 3, or 4, or one of: + la priorité doit être 0, 1, 2, 3, 4 ou l'une de : + + + could not change default priority échec du changement de la priorité par défaut - + + invalid argument: must be either 0/never, 1/action, or 2/encrypt/decrypt + argument invalide : doit être soit 0/never, 1/action, ou 2/encrypt/decrypt + + + + set_daemon <host>[:<port>] [trusted|untrusted] + set_daemon <hôte>[:<port>] [trusted|untrusted] + + + + transfer [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] (<URI> | <address> <amount>) [<payment_id>] + transfer [index=<N1>[,<N2>,...]] [<priorité>] [<taille_cercle>] (<URI> | <adresse> <montant>) [<ID_paiement>] + + + + Transfer <amount> to <address>. If the parameter "index=<N1>[,<N2>,...]" is specified, the wallet uses outputs received by addresses of those indices. If omitted, the wallet randomly chooses address indices to be used. In any case, it tries its best not to combine outputs across multiple addresses. <priority> is the priority of the transaction. The higher the priority, the higher the transaction fee. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command "set priority") is used. <ring_size> is the number of inputs to include for untraceability. Multiple payments can be made at once by adding URI_2 or <address_2> <amount_2> etcetera (before the payment ID, if it's included) + Transférer <montant> à <adresse> Si le paramètre "index=<N1>[,<N2>,...]" est spécifié, le portefeuille utilise les sorties reçues par les adresses de ces indices. Si il est omis, le portefeuille choisit les indices d'adresse à utiliser aléatoirement. Dans tous les cas, il essaye de ne pas combiner des sorties de multiples adresses. <priorité> est la priorité de la transaction. Plus la priorité est haute, plus les frais de transaction sont élévés. Les valeurs valides par ordre de priorité (de la plus basse à la plus haute) sont : unimportant, normal, elevated, priority. Si elle est omise, la valeur par défaut (voir la commande "set priority") est utilisée. <taille_cercle> est le nombre d'entrées à inclure pour l'intraçabilité. De multiples paiements peuvent être réalisés d'un coup en ajoutant <URI_2> ou <adresse_2> <montant_2> et cetera (avant l'ID de paiement, si il est inclus) + + + + locked_transfer [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] (<URI> | <addr> <amount>) <lockblocks> [<payment_id>] + locked_transfer [index=<N1>[,<N2>,...]] [<priorité>] [<taille_cercle>] (<URI> | <adresse> <montant>) <blocs_verrou> [<ID_paiement>] + + + + Transfer <amount> to <address> and lock it for <lockblocks> (max. 1000000). If the parameter "index=<N1>[,<N2>,...]" is specified, the wallet uses outputs received by addresses of those indices. If omitted, the wallet randomly chooses address indices to be used. In any case, it tries its best not to combine outputs across multiple addresses. <priority> is the priority of the transaction. The higher the priority, the higher the transaction fee. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command "set priority") is used. <ring_size> is the number of inputs to include for untraceability. Multiple payments can be made at once by adding URI_2 or <address_2> <amount_2> etcetera (before the payment ID, if it's included) + Transférer <montant> à <adresse> et le verrouiller pendant <blocs_verrou> (max 1000000). Si le paramètre "index=<N1>[,<N2>,...]" est spécifié, le portefeuille utilise les sorties reçues par les adresses de ces indices. Si il est omis, le portefeuille choisit les indices d'adresse à utiliser aléatoirement. Dans tous les cas, il essaye de ne pas combiner des sorties de multiples adresses. <priorité> est la priorité de la transaction. Plus la priorité est haute, plus les frais de transaction sont élévés. Les valeurs valides par ordre de priorité (de la plus basse à la plus haute) sont : unimportant, normal, elevated, priority. Si elle est omise, la valeur par défaut (voir la commande "set priority") est utilisée. <taille_cercle> est le nombre d'entrées à inclure pour l'intraçabilité. De multiples paiements peuvent être réalisés d'un coup en ajoutant <URI_2> ou <adresse_2> <montant_2> et cetera (avant l'ID de paiement, si il est inclus) + + + + locked_sweep_all [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <address> <lockblocks> [<payment_id>] + locked_sweep_all [index=<N1>[,<N2>,...]] [<priorité>] [<taille_cercle>] <adresse> <blocs_verrou> [<ID_paiement>] + + + + Send all unlocked balance to an address and lock it for <lockblocks> (max. 1000000). If the parameter "index<N1>[,<N2>,...]" is specified, the wallet sweeps outputs received by those address indices. If omitted, the wallet randomly chooses an address index to be used. <priority> is the priority of the sweep. The higher the priority, the higher the transaction fee. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command "set priority") is used. <ring_size> is the number of inputs to include for untraceability. + Transférer tout le solde débloqué à une adresse et le verrouiller pendant <blocs_verrou> (max 1000000). Si le paramètre "index=<N1>[,<N2>,...]" est spécifié, le portefeuille utilise les sorties reçues par ces indices d'adresse. Si il est omis, le portefeuille choisit un index d'adresse à utiliser aléatoirement. <priorité> est la priorité du balayage. Plus la priorité est haute, plus les frais de transaction sont élévés. Les valeurs valides par ordre de priorité (de la plus basse à la plus haute) sont : unimportant, normal, elevated, priority. Si elle est omise, la valeur par défaut (voir la commande "set priority") est utilisée. <taille_cercle> est le nombre d'entrées à inclure pour l'intraçabilité. + + + + sweep_all [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] [outputs=<N>] <address> [<payment_id>] + sweep_all [index=<N1>[,<N2>,...]] [<priorité>] [<taille_cercle>] [outputs=<N>] <adresse> [<ID_paiement>] + + + + Send all unlocked balance to an address. If the parameter "index<N1>[,<N2>,...]" is specified, the wallet sweeps outputs received by those address indices. If omitted, the wallet randomly chooses an address index to be used. If the parameter "outputs=<N>" is specified and N > 0, wallet splits the transaction into N even outputs. + Envoyer tout le solde débloqué à une adresse. Si le paramètre "index<N1>[,<N2>,...]" est spécifié, le portefeuille balaye les sorties reçues par ces indices d'adresse. Si il est omis, le portefeuille choisit un index d'adresse à utiliser aléatoirement. Si le paramètre "outputs=<N>" est spécifié et N > 0, le portefeuille scinde la transaction en N sorties égales. + + + + sweep_single [<priority>] [<ring_size>] [outputs=<N>] <key_image> <address> [<payment_id>] + sweep_single [<priorité>] [<taille_cercle>] [outputs=<N>] <image_clé> <adresse> [<ID_paiement>] + + + + sign_transfer [export_raw] + sign_transfer [export_raw] + + + + Sign a transaction from a file. If the parameter "export_raw" is specified, transaction raw hex data suitable for the daemon RPC /sendrawtransaction is exported. + Signer une transaction à partir d'un fichier. Si le paramètre "export_raw" est spécifié, les données brutes hexadécimales adaptées au RPC /sendrawtransaction du démon sont exportées. + + + + If no arguments are specified or <index> is specified, the wallet shows the default or specified address. If "all" is specified, the wallet shows all the existing addresses in the currently selected account. If "new " is specified, the wallet creates a new address with the provided label text (which can be empty). If "label" is specified, the wallet sets the label of the address specified by <index> to the provided label text. + Si aucun argument n'est spécifié ou si <index> est spécifié, le portefeuille affiche l'adresse par défaut ou l'adresse spécifiée. Si "all" est spécifié, le portefeuille affiche toutes les adresses existantes dans le comptes actuellement sélectionné. Si "new" est spécifié, le portefeuille crée une nouvelle adresse avec le texte d'étiquette fourni (qui peut être vide). Si "label" est spécifié, le portefeuille affecte le texte fourni à l'étiquette de l'adresse spécifiée par <index>. + + + + Available options: + seed language + Set the wallet's seed language. + always-confirm-transfers <1|0> + Whether to confirm unsplit txes. + print-ring-members <1|0> + Whether to print detailed information about ring members during confirmation. + store-tx-info <1|0> + Whether to store outgoing tx info (destination address, payment ID, tx secret key) for future reference. + default-ring-size <n> + Set the default ring size (default and minimum is 5). + auto-refresh <1|0> + Whether to automatically synchronize new blocks from the daemon. + refresh-type <full|optimize-coinbase|no-coinbase|default> + Set the wallet's refresh behaviour. + priority [0|1|2|3|4] + Set the fee to default/unimportant/normal/elevated/priority. + confirm-missing-payment-id <1|0> + ask-password <0|1|2 (or never|action|decrypt)> + unit <monero|millinero|micronero|nanonero|piconero> + Set the default monero (sub-)unit. + min-outputs-count [n] + Try to keep at least that many outputs of value at least min-outputs-value. + min-outputs-value [n] + Try to keep at least min-outputs-count outputs of at least that value. + merge-destinations <1|0> + Whether to merge multiple payments to the same destination address. + confirm-backlog <1|0> + Whether to warn if there is transaction backlog. + confirm-backlog-threshold [n] + Set a threshold for confirm-backlog to only warn if the transaction backlog is greater than n blocks. + refresh-from-block-height [n] + Set the height before which to ignore blocks. + auto-low-priority <1|0> + Whether to automatically use the low priority fee level when it's safe to do so. + segregate-pre-fork-outputs <1|0> + Set this if you intend to spend outputs on both Monero AND a key reusing fork. + key-reuse-mitigation2 <1|0> + Set this if you are not sure whether you will spend on a key reusing Monero fork later. +subaddress-lookahead <major>:<minor> + Set the lookahead sizes for the subaddress hash table. + Set this if you are not sure whether you will spend on a key reusing Monero fork later. + segregation-height <n> + Set to the height of a key reusing fork you want to use, 0 to use default. + Options disponibles : + seed langue + Définir la langue de la phrase mnémonique. + always-confirm-transfers <1|0> + Confirmation des transactions non scindées. + print-ring-members <1|0> + Affichage d'informations détaillées sur les membres du cercle lors de la confirmation. + store-tx-info <1|0> + Sauvegarde des informations des transactions sortantes (adresse de destination, ID de paiement, clé secrète de transaction) pour référence ultérieure. + default-ring-size <n> + Définir la taille de cercle par défaut (la valeur par défaut est le minimum 5). + auto-refresh <1|0> + Synchronisation automatique des nouveaux blocs du démon. + refresh-type <full|optimize-coinbase|no-coinbase|default> + Définir le comportement du rafraîchissement du portefeuille. + priority [0|1|2|3|4] + Utiliser les frais pour la priorité par défaut/peu importante/normale/élevée/prioritaire. + confirm-missing-payment-id <1|0> + ask-password <0|1|2 (ou never|action|decrypt)> + unit <monero|millinero|micronero|nanonero|piconero> + Définir la (sous-)unité monero par défaut. + min-outputs-count [n] + Essayer de garder au moins ce nombre de sorties d'une valeur d'au moins min-outputs-value. + min-outputs-value [n] + Essayer de garder au moins min-outputs-count sorties d'au moins cette valeur. + merge-destinations <1|0> + Fusion des paiements multiples vers la même adresse de destination. + confirm-backlog <1|0> + Avertir s'il y a un arriéré de transactions. + confirm-backlog-threshold [n] + Définir un seuil pour confirm-backlog pour avertir seulement si l'arriéré de transactions est supérieur à n blocs. + refresh-from-block-height [n] + Définir la hauteur avant laquelle les blocs sont ignorés. + auto-low-priority <1|0> + Utilisation automatique du niveau de frais pour la priorité basse, lorsqu'il est sûr de le faire. + segregate-pre-fork-outputs <1|0> + Activez ceci si vous prévoyez de dépenser des sorties à la fois avec Monero ET un fork réutilisant les clés. + key-reuse-mitigation2 <1|0> + Activez ceci si vous n'êtes pas sûr de ne jamais utiliser un fork réutilisant les clés. + subaddress-lookahead <majeur>:<mineur> + Définir les nombres de sous-adresse à inclure par anticipation dans la table de hachage des sous-adresses. + segregation-height <n> + Définir la hauteur d'un fork réutilisant les clés que vous voulez utiliser, 0 par défaut. + + + + set_tx_key <txid> <tx_key> + set_tx_key <ID_transaction> <clé_transaction> + + + + Set the transaction key (r) for a given <txid> in case the tx was made by some other device or 3rd party wallet. + Définir la clé de transaction (r) pour un <ID_transaction> donné au cas où cette clé ait été créée par un appareil ou portefeuille tiers. + + + + show_transfers [in|out|pending|failed|pool|coinbase] [index=<N1>[,<N2>,...]] [<min_height> [<max_height>]] + show_transfers [in|out|pending|failed|pool|coinbase] [index=<N1>[,<N2>,...]] [<hauteur_min> [<hauteur_max>]] + + + + Rescan the blockchain from scratch, losing any information which can not be recovered from the blockchain itself. + Rescanner la chaîne de blocs à partir du début, en perdant toute information qui ne peut pas être retrouvée à partir de la chaîne elle même. + + + + hw_reconnect + hw_reconnect + + + + Attempts to reconnect HW wallet. + Essayer de se reconnecter à un portefeuille matériel. + + + + print_ring <key_image> | <txid> + print_ring <image_clé> | <ID_transaction> + + + + Print the ring(s) used to spend a given key image or transaction (if the ring size is > 1) + Afficher le(s) cercle(s) utilisé(s) pour dépenser une image de clé ou une transaction (si la taille de cercle est > 1) + + + + set_ring <filename> | ( <key_image> absolute|relative <index> [<index>...] ) + set_ring <nom_fichier> | ( <image_clé> absolute|relative <index> [<index>...] ) + + + + Set the ring used for a given key image, so it can be reused in a fork + Définir le cercle utilisé pour une image de clé donnée, afin de pouvoir le réutiliser dans un fork + + + + save_known_rings + save_known_rings + + + + Save known rings to the shared rings database + Sauvegarder les cercles connus dans la base de données des cercles partagés + + + + blackball <amount>/<offset> | <filename> [add] + blackball <montant>/<offset> | <nom_fichier> [add] + + + + Blackball output(s) so they never get selected as fake outputs in a ring + Blackbouler des sorties pour qu'elles ne soient jamais sélectionnées comme leurres dans un cercle + + + + unblackball <amount>/<offset> + unblackball <montant>/<offset> + + + + Unblackballs an output so it may get selected as a fake output in a ring + Déblackbouler une sortie pour qu'elle puisse être sélectionnée comme leurre dans un cercle + + + + blackballed <amount>/<offset> + blackballed <montant>/<offset> + + + + Checks whether an output is blackballed + Vérifier si une sortie est blackboulée + + + + version + version + + + + Returns version information + Retourne les informations de version + + + full (slowest, no assumptions); optimize-coinbase (fast, assumes the whole coinbase is paid to a single address); no-coinbase (fastest, assumes we receive no coinbase transaction), default (same as optimize-coinbase) full (le plus lent, aucune supposition); optimize-coinbase (rapide, suppose que la récompense de bloc est payée à une unique adresse); no-coinbase (le plus rapide, suppose que l'on ne reçoit aucune récompense de bloc), default (comme optimize-coinbase) - + + 0, 1, 2, 3, or 4, or one of + 0, 1, 2, 3, 4 ou l'une de + + + + 0|1|2 (or never|action|decrypt) + 0|1|2 (ou never|action|decrypt) + + + monero, millinero, micronero, nanonero, piconero monero, millinero, micronero, nanonero, piconero - + + <major>:<minor> + <majeur>:<mineur> + + + + wrong number range, use: set_log <log_level_number_0-4> | <categories> + nombre hors interval, utilisez : set_log <niveau_de_journalisation_0-4> | <catégories> + + + Wallet name not valid. Please try again or use Ctrl-C to quit. Nom de portefeuille non valide. Veuillez réessayer ou utilisez Ctrl-C pour quitter. - + Wallet and key files found, loading... Fichier portefeuille et fichier de clés trouvés, chargement... - + Key file found but not wallet file. Regenerating... Fichier de clés trouvé mais pas le fichier portefeuille. Régénération... - + Key file not found. Failed to open wallet: - Fichier de clés non trouvé. Échec de l'ouverture du portefeuille : + Fichier de clés non trouvé. Échec de l'ouverture du portefeuille : - + Generating new wallet... Génération du nouveau portefeuille... - + + Can't specify more than one of --testnet and --stagenet + Impossible de spécifier plus d'une option parmis --testnet et --stagenet + + + + can't specify more than one of --generate-new-wallet="wallet_name", --wallet-file="wallet_name", --generate-from-view-key="wallet_name", --generate-from-spend-key="wallet_name", --generate-from-keys="wallet_name", --generate-from-multisig-keys="wallet_name", --generate-from-json="jsonfilename" and --generate-from-device="wallet_name" + impossible de spécifier plus d'une option parmis --generate-new-wallet="nom_portefeuille", --wallet-file="nom_portefeuille", --generate-from-view-key="nom_portefeuille", --generate-from-spend-key="nom_portefeuille", --generate-from-keys="nom_portefeuille", --generate-from-multisig-keys="nom_portefeuille", --generate-from-json="nom_fichier_json" et --generate-from-device="nom_portefeuille" + + + Electrum-style word list failed verification Échec de la vérification de la liste de mots de style Electrum - - - - - - - - - - + + Enter seed offset passphrase, empty if none + Entrer une phrase de passe pour le décalage de la phrase mnémonique, vide si aucun + + + + + + + + + + + + No data supplied, cancelled Pas de données fournies, annulation - - - - - - - - - - - - + + + + + + + + + + + + failed to parse address échec de l'analyse de l'adresse - - + + failed to parse view key secret key échec de l'analyse de la clé secrète d'audit - - + + failed to verify view key secret key échec de la vérification de la clé secrète d'audit - - - + + + view key does not match standard address la clé d'audit ne correspond pas à l'adresse standard - - - - - + + + + + + account creation failed échec de la création du compte - - - + + + failed to parse spend key secret key échec de l'analyse de la clé secrète de dépense - - + + failed to verify spend key secret key échec de la vérification de la clé secrète de dépense - - + + spend key does not match standard address la clé de dépense ne correspond pas à l'adresse standard - + + No restore height is specified. + Aucune hauteur de restauration n'est spécifiée. + + + + Assumed you are creating a new account, restore will be done from current estimated blockchain height. + Nous supposons que vous créez un nouveau compte, la restauration sera faite à partir d'une hauteur de la chaîne de blocs estimée. + + + + Use --restore-height if you want to restore an already setup account from a specific height + Utilisez --restore-height si vous voulez restaurer un compte existant à partir d'une hauteur spécifique + + + + account creation aborted + création du compte annulée + + + + can't specify --subaddress-lookahead and --wallet-file at the same time + Impossible de spécifier --subaddress-lookahead et --wallet-file en même temps + + + failed to open account échec de l'ouverture du compte - - - - - + + + + + wallet is null portefeuille est nul - - + + Failed to initialize ring database: privacy enhancing features will be inactive + Impossible d'initialiser la base de données des cercles : les fonctions d'amélioration de la confidentialité seront inactives + + + + If your display freezes, exit blind with ^C, then run again with --use-english-language-names + Si votre affichage se bloque, quittez en aveugle avec ^C, puis lancer à nouveau en utilisant --use-english-language-names + + + + invalid language choice entered. Please try again. choix de langue passé invalide. Veuillez réessayer. - + View key: - Clé d'audit : + Clé d'audit : + + + + Generated new wallet on hw device: + Nouveau portefeuille généré sur l'appareil : - + + Key file not found. Failed to open wallet + Fichier des clés non trouvé. Échec d'ouverture du portefeuille + + + You may want to remove the file "%s" and try again Vous pourriez vouloir supprimer le fichier "%s" et réessayer - + failed to deinitialize wallet échec de la désinitialisation du portefeuille - - - + + Watch only wallet saved as: + Portefeuille d'audit sauvegardé vers : + + + + Failed to save watch only wallet: + Échec de la sauvegarde du portefeuille d'audit : + + + + + this command requires a trusted daemon. Enable with --trusted-daemon cette commande requiert un démon de confiance. Activer avec --trusted-daemon - + + invalid arguments. Please use start_mining [<number_of_threads>] [do_bg_mining] [ignore_battery] + arguments invalides. Veuillez utiliser start_mining [<nombre_de_threads>] [do_bg_mining] [ignore_battery] + + + + Expected trusted or untrusted, got + "trusted" ou "untrusted" attendu, mais lu + + + + trusted + de confiance + + + + untrusted + non fiable + + + blockchain can't be saved: - la chaîne de blocs ne peut pas être sauvegardée : + la chaîne de blocs ne peut pas être sauvegardée : + + + + NOTE: this transaction uses an encrypted payment ID: consider using subaddresses instead + NOTE: cette transaction utilise un ID de paiement chiffré: veuillez considérer l'utilisation de sous-adresses à la place + + + + WARNING: this transaction uses an unencrypted payment ID: consider using subaddresses instead + ATTENTION: cette transaction utilise un ID de paiement non chiffré: veuillez considérer l'utilisation de sous-adresses à la place + + + + Password needed (%s) - use the refresh command + Mot de passe requis (%s) - utilisez la commande refresh + + + + Enter password + Entrez le mot de passe - - + + daemon is busy. Please try again later. le démon est occupé. Veuillez réessayer plus tard. - - + + no connection to daemon. Please make sure daemon is running. pas de connexion au démon. Veuillez vous assurer que le démon fonctionne. - + refresh error: - erreur du rafraîchissement : + erreur du rafraîchissement : - + + (Some owned outputs have missing key images - import_key_images needed) + (Il manque les images de clé de certaines sorties - import_key_images requis) + + + Balance: - Solde : + Solde : - + pubkey clé publique - + key image image de clé - - + + unlocked déverrouillé - + ringct ringct - + T V - + F F - + locked vérrouillé - + RingCT RingCT - + - - - + payment ID has invalid format, expected 16 or 64 character hex string: - format d'identifiant de paiement invalide, 16 ou 64 caractères hexadécimaux attendus : + format d'identifiant de paiement invalide, 16 ou 64 caractères hexadécimaux attendus : - + failed to get spent status échec de la récupération du statut de dépense - - the same transaction - la même transaction + + failed to find construction data for tx input + échec de la recherche des données pour contruire l'entrée de la transaction + + + + the same transaction + la même transaction + + + + blocks that are temporally very close + blocs très proches dans le temps + + + + + + ring size %u is too large, maximum is %u + la taille de cercle %u est trop grande, le maximum est %u + + + + + Unencrypted payment IDs are bad for privacy: ask the recipient to use subaddresses instead + Les ID de paiment non chiffrés sont mauvais pour la confidentialité : demandez au bénéficiaire d'utiliser les sous-adresses à la place + + + + payment id failed to encode + échec de l'encodage de l'ID de paiement + + + + + Locked blocks too high, max 1000000 (˜4 yrs) + Nombre de blocs verrou trop élévé, 1000000 max (˜4 ans) + + + + failed to parse short payment ID from URI + échec de l'analyse de l'ID de paiement court à partir de l'URI + + + + + Invalid last argument: + Dernier argument invalide : + + + + a single transaction cannot use more than one payment id + une unique transaction ne peut pas utiliser plus d'un ID de paiement + + + + failed to parse payment id, though it was detected + échec de l'analyse de l'ID de paiement, bien qu'il ait été détecté + + + + Not enough money in unlocked balance + Pas assez de fonds dans le solde débloqué + + + + Discarding %s of unmixable outputs that cannot be spent, which can be undone by "rescan_spent". Is this okay? (Y/Yes/N/No): + On se débarrasse de %s de sorties non mélangeables qui ne peuvent pas être dépensées, ce qui peut être défait avec "rescan_spent". Est-ce d'accord ? (Y/Yes/N/No) : + + + + usage: %s [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] [outputs=<N>] <address> [<payment_id>] + usage: %s [index=<N1>[,<N2>,...]] [<priorité>] [<taille_cercle>] [outputs=<N>] <adresse> [<ID_paiement>] + + + + missing lockedblocks parameter + paramètre blocs_verrou manquant + + + + bad locked_blocks parameter + mauvais paramètre blocs_verrou + + + + + Failed to parse number of outputs + Échec de l'analyse du nombre de sorties + + + + + Amount of outputs should be greater than 0 + Le nombre de sorties doit être supérieur à 0 + + + + usage: sweep_single [<priority>] [<ring_size>] [outputs=<N>] <key_image> <address> [<payment_id>] + usage : sweep_single [<priorité>] [<taille_cercle>] [outputs=<N>] <image_clé> <adresse> [<ID_paiement>] + + + + donations are not enabled on the testnet or on the stagenet + les dons ne sont pas activés sur les réseaux testnet et stagenet + + + + Donating %s %s to The Monero Project (donate.getmonero.org or %s). + Don de %s %s à The Monero Project (donate.getmonero.org ou %s). + + + + usage: sign_transfer [export_raw] + usage : sign_transfer [export_raw] + + + + usage: set_tx_key <txid> <tx_key> + usage : set_tx_key <ID_transaction> <clé_transaction> + + + + + + failed to parse tx_key + échec de l'analyse de la clé de transaction - - blocks that are temporally very close - blocs très proches dans le temps + + Tx key successfully stored. + Clé de transaction sauvegardée avec succès. - - Locked blocks too high, max 1000000 (˜4 yrs) - Nombre de blocs verrou trop élévé, 1000000 max (˜4 ans) + + Failed to store tx key: + Échec de la sauvegarde de la clé de transaction : - - + + Good signature Bonne signature - - - + + + Bad signature Mauvaise signature - + + usage: show_transfers [in|out|all|pending|failed|coinbase] [index=<N1>[,<N2>,...]] [<min_height> [<max_height>]] + usage : show_transfers [in|out|all|pending|failed|coinbase] [index=<N1>[,<N2>,...]] [<hauteur_min> [<hauteur_max>]] + + + + block + bloc + + + + Warning: this will lose any information which can not be recovered from the blockchain. + Attention : ceci pedra toute information qui ne peut pas être retrouvée à partir de la chaîne de blocs. + + + + This includes destination addresses, tx secret keys, tx notes, etc + Ceci inclut les adresses de destination, les clé secrètes de transaction, les notes de transaction, etc + + + + Rescan anyway ? (Y/Yes/N/No): + Rescanner quand même ? (Y/Yes/N/No) : + + + usage: integrated_address [payment ID] - usage : integrated_address [ID paiement] + usage : integrated_address [ID paiement] - + Standard address: - Adresse standard : + Adresse standard : - + failed to parse payment ID or address échec de l'analyse de l'ID de paiement ou de l'adresse - + usage: address_book [(add (<address> [pid <long or short payment id>])|<integrated address> [<description possibly with whitespaces>])|(delete <index>)] - usage : address_book [(add (<adresse> [pid <ID de paiement long ou court>])|<adresse integrée> [<description avec des espaces possible>])|(delete <index>)] + usage : address_book [(add (<adresse> [pid <ID de paiement long ou court>])|<adresse integrée> [<description avec des espaces possible>])|(delete <index>)] - + failed to parse payment ID échec de l'analyse de l'ID de paiement - + failed to parse index échec de l'analyse de l'index - + Address book is empty. Le carnet d'adresses est vide. - + Index: - Index : + Index : - - + + Address: - Adresse : + Adresse : - + Payment ID: - ID de paiement : + ID de paiement : - - + + Description: - Description : + Description : - + usage: set_tx_note [txid] free text note - usage : set_tx_note [ID transaction] note de texte libre + usage : set_tx_note [ID transaction] note de texte libre - + usage: get_tx_note [txid] - usage : get_tx_note [ID transaction] + usage : get_tx_note [ID transaction] + + + + Network type: + Type de réseau : + + + + Testnet + Testnet + + + + Stagenet + Stagenet + + + + Mainnet + Mainnet - + usage: sign <filename> - usage : sign <fichier> + usage : sign <fichier> - + wallet is watch-only and cannot sign c'est un portefeuille d'audit et il ne peut pas signer - - - - + + + + failed to read file échec de la lecture du fichier - + usage: check_tx_proof <txid> <address> <signature_file> [<message>] - usage : check_tx_proof <ID_transaction> <adresse> <fichier_signature> [<message>] + usage : check_tx_proof <ID_transaction> <adresse> <fichier_signature> [<message>] - - - + + + failed to load signature file échec du chargement du fichier signature - + usage: get_spend_proof <txid> [<message>] - usage : get_spend_proof <ID_transaction> [<message>] + usage : get_spend_proof <ID_transaction> [<message>] - + wallet is watch-only and cannot generate the proof c'est un portefeuille d'audit et il ne peut générer de preuve - + usage: check_spend_proof <txid> <signature_file> [<message>] - usage : check_spend_proof <ID_transaction> <fichier_signature> [<message>] + usage : check_spend_proof <ID_transaction> <fichier_signature> [<message>] - + usage: get_reserve_proof (all|<amount>) [<message>] - usage : get_reserve_proof (all|<montant>) [<message>] + usage : get_reserve_proof (all|<montant>) [<message>] - + The reserve proof can be generated only by a full wallet La preuve de réserve ne peut être généré que par un portefeuille complet - + usage: check_reserve_proof <address> <signature_file> [<message>] - usage : check_reserve_proof <adresse> <fichier_signature> [<message>] + usage : check_reserve_proof <adresse> <fichier_signature> [<message>] - + Address must not be a subaddress L'adresse ne doit pas être une sous-adresse - + Good signature -- total: %s, spent: %s, unspent: %s - Bonne signature -- total : %s, dépensé : %s, non dépensé : %s - - - - usage: show_transfers [in|out|all|pending|failed] [index=<N1>[,<N2>,...]] [<min_height> [<max_height>]] - usage : show_transfers [in|out|all|pending|failed] [index=<N1>[,<N2>,...]] [<hauteur_min> [<hauteur_max>]] + Bonne signature -- total : %s, dépensé : %s, non dépensé : %s - + [Double spend seen on the network: this transaction may or may not end up being mined] - [Double dépense détectée sur le réseau : cette transaction sera peut-être invalidée] + [Double dépense détectée sur le réseau : cette transaction sera peut-être invalidée] - + usage: unspent_outputs [index=<N1>[,<N2>,...]] [<min_amount> [<max_amount>]] - usage : unspent_outputs [index=<N1>[,<N2>,...]] [<montant_min> [<montant_max>]] + usage : unspent_outputs [index=<N1>[,<N2>,...]] [<montant_min> [<montant_max>]] - + There is no unspent output in the specified address Il n'y a pas de sortie non dépensée pour l'adresse spécifiée - + (no daemon) (pas de démon) - + (out of sync) (désynchronisé) - + (Untitled account) (compte sans nom) - - - - - - + + + + + + failed to parse index: - échec de l'analyse de l'index : + échec de l'analyse de l'index : - - + + specify an index between 0 and specifiez un index entre 0 et - + usage: account account new <label text with white spaces allowed> @@ -3073,7 +3808,7 @@ Attention : Certaines clés d'entrées étant dépensées sont issues de < account tag <tag_name> <account_index_1> [<account_index_2> ...] account untag <account_index_1> [<account_index_2> ...] account tag_description <tag_name> <description> - usage : + usage : account account new <texte étiquette avec espaces autorisés> account switch <index> @@ -3083,472 +3818,458 @@ Attention : Certaines clés d'entrées étant dépensées sont issues de < account tag_description <mot_clé> <description> - + Grand total: Balance: -Somme finale : - Solde : +Somme finale : + Solde : - + , unlocked balance: - , solde débloqué : + , solde débloqué : - + Untagged accounts: - Comptes sans mot clé : + Comptes sans mot clé : - + Tag %s is unregistered. Le mot clé %s n'est pas enregistré. - + Accounts with tag: - Comptes avec mot clé : + Comptes avec mot clé : - + Tag's description: - Description du mot clé : + Description du mot clé : - + Account Compte - + %c%8u %6s %21s %21s %21s %c%8u %6s %21s %21s %21s - + ---------------------------------------------------------------------------------- ---------------------------------------------------------------------------------- - + %15s %21s %21s %15s %21s %21s - + Primary address Adresse primaire - + (used) (utilisé) - + (Untitled address) (adresse sans nom) - + <index_min> is already out of bound <index_min> est déjà hors limite - + <index_max> exceeds the bound <index_max> excède la limite - + usage: address [ new <label text with white spaces allowed> | all | <index_min> [<index_max>] | label <index> <label text with white spaces allowed> ] - usage : address [ new <texte étiquette avec espaces autorisés> | all | <index_min> [<index_max>] | label <index> <texte étiquette avec espaces autorisés>] + usage : address [ new <texte étiquette avec espaces autorisés> | all | <index_min> [<index_max>] | label <index> <texte étiquette avec espaces autorisés>] - - + + Integrated addresses can only be created for account 0 Les adresses intégrées ne peuvent être créées que pour le compte 0 - + Integrated address: %s, payment ID: %s - Adresse intégrée : %s, ID de paiement : %s + Adresse intégrée : %s, ID de paiement : %s - + Subaddress: - Sous-adresse : + Sous-adresse : - + usage: get_description - usage : get_description + usage : get_description - + no description found pas de description trouvée - + description found: - description trouvée : + description trouvée : - + Filename: - Fichier : + Fichier : - + Watch only Audit - + %u/%u multisig%s Multisig %u/%u%s - + Normal Normal - + Type: - Type : - - - - Testnet: - Testnet : - - - - Yes - Oui - - - - No - Non + Type : - + This wallet is multisig and cannot sign C'est un portefeuille multisig et il ne peut pas signer - + usage: verify <filename> <address> <signature> - usage : verify <fichier> <adresse> <signature> + usage : verify <fichier> <adresse> <signature> - + Bad signature from Mauvaise signature de - + Good signature from Bonne signature de - + usage: export_key_images <filename> - usage : export_key_images <fichier> + usage : export_key_images <fichier> - + wallet is watch-only and cannot export key images c'est un portefeuille d'audit et il ne peut pas exporter les images de clé - - - + + + failed to save file échec de l'enregistrement du fichier - + Signed key images exported to Images de clé signées exportées vers - + usage: import_key_images <filename> - usage : import_key_images <fichier> + usage : import_key_images <fichier> - + usage: export_outputs <filename> - usage : export_outputs <fichier> + usage : export_outputs <fichier> - + Outputs exported to Sorties exportées vers - + usage: import_outputs <filename> - usage : import_outputs <fichier> + usage : import_outputs <fichier> - - - - + + + + amount is wrong: - montant erroné : + montant erroné : - + expected number from 0 to attend un nombre de 0 à - + Sweeping Balayage de - + Money successfully sent, transaction: - Fonds envoyés avec succès, transaction : + Fonds envoyés avec succès, transaction : - + Change goes to more than one address La monnaie rendue va à plus d'une adresse - + %s change to %s %s de monnaie rendue à %s - + no change sans monnaie rendue - - - + + + Transaction successfully signed to file Transaction signée avec succès dans le fichier - + usage: get_tx_key <txid> - usage : get_tx_key <ID transaction> - - - - - - - - - - - + usage : get_tx_key <ID transaction> + + + + + + + + + + + + failed to parse txid échec de l'analyse de l'ID de transaction - + Tx key: - Clé de transaction : + Clé de transaction : - + no tx keys found for this txid aucune clé de transaction trouvée pour cet ID de transaction - + usage: get_tx_proof <txid> <address> [<message>] - usage : get_tx_proof <ID_transaction> <adresse> [<message>] + usage : get_tx_proof <ID_transaction> <adresse> [<message>] - - - + + + signature file saved to: - fichier signature sauvegardé dans : + fichier signature sauvegardé dans : - - - + + + failed to save signature file échec de la sauvegarde du fichier signature - + usage: check_tx_key <txid> <txkey> <address> - usage : check_tx_key <ID transaction> <clé transaction> <adresse> + usage : check_tx_key <ID transaction> <clé transaction> <adresse> - - + + failed to parse tx key échec de l'analyse de la clé de transaction - - - + + + error: - erreur : + erreur : - - + + received a reçu - - + + in txid dans la transaction - - + + received nothing in txid n'a rien reçu dans la transaction - - + + WARNING: this transaction is not yet included in the blockchain! - ATTENTION : cette transaction n'est pas encore inclue dans la chaîne de blocs ! + ATTENTION : cette transaction n'est pas encore inclue dans la chaîne de blocs ! - - + + This transaction has %u confirmations Cette transaction a %u confirmations - - + + WARNING: failed to determine number of confirmations! - ATTENTION : échec de la détermination du nombre de confirmations ! + ATTENTION : échec de la détermination du nombre de confirmations ! - + bad min_height parameter: - mauvais paramètre hauteur_minimum : + mauvais paramètre hauteur_minimum : - + bad max_height parameter: - mauvais paramètre hauteur_maximum : + mauvais paramètre hauteur_maximum : - + in reçu - - + + out payé - + failed échoué - + pending en attente - + <min_amount> should be smaller than <max_amount> <montant_minimum> doit être inférieur à <montant_maximum> - + Amount: -Montant : +Montant : - + , number of keys: - , nombre de clés : + , nombre de clés : - + - + Min block height: -Hauteur de bloc minimum : +Hauteur de bloc minimum : - + Max block height: -Hauteur de bloc maximum : +Hauteur de bloc maximum : - + Min amount found: -Montant minimum trouvé : +Montant minimum trouvé : - + Max amount found: -Montant maximum trouvé : +Montant maximum trouvé : - + Total count: -Compte total : +Compte total : - + Bin size: -Taille de classe : +Taille de classe : - + Outputs per *: -Sorties par * : +Sorties par * : - + count ^ @@ -3557,54 +4278,54 @@ Sorties par * : - + | | - + + + - + +--> block height +--> hauteur de bloc - + ^ ^ - + ^ ^ - + - + wallet portefeuille - - + + Random payment ID: - ID de paiement aléatoire : + ID de paiement aléatoire : - + Matching integrated address: - Adresse intégrée correspondante : + Adresse intégrée correspondante : @@ -3621,7 +4342,7 @@ Sorties par * : - How many participants wil share parts of the multisig wallet + How many participants will share parts of the multisig wallet Combien de participants partageront des parts du portefeuille multisig @@ -3632,161 +4353,395 @@ Sorties par * : Create testnet multisig wallets - Créer un portefeuille multisig testnet + Créer des portefeuilles multisig testnet + + + + Create stagenet multisig wallets + Créer des portefeuilles multisig stagenet + + + + Create an address file for new wallets + Créer un fichier d'adresse pour les nouveaux portefeuilles - + Generating %u %u/%u multisig wallets Génération de %u portefeuilles multisig %u/%u - + + Failed to verify multisig info + Échec de la vérification des infos multisig + + + Error verifying multisig extra info Erreur de vérification des infos multisig supplémentaires - + Error finalizing multisig Erreur de finalisation multisig - + Generated multisig wallets for address Portefeuilles multisig générés pour l'adresse - + Error creating multisig wallets: - Erreur de création des portefeuilles multisig : + Erreur de création des portefeuilles multisig : - + This program generates a set of multisig wallets - use this simpler scheme only if all the participants trust each other Ce programme génère un ensemble de portefeuilles multisig - n'utilisez cette méthode plus simple que si tous les participants se font confiance - + + Error: Can't specify more than one of --testnet and --stagenet + Erreur: Impossible de spécifier plus d'une option parmis --testnet et --stagenet + + + Error: expected N/M, but got: - Erreur : N/M attendu, mais lu : + Erreur : N/M attendu, mais lu : - - + + Error: either --scheme or both of --threshold and --participants may be given - Erreur : soit --scheme soit --threshold et --participants doivent être indiqués + Erreur : soit --scheme soit --threshold et --participants doivent être indiqués - + Error: expected N > 1 and N <= M, but got N==%u and M==%d - Erreur : N > 1 et N <= M attendu, mais lu N==%u et M==%d + Erreur : N > 1 et N <= M attendu, mais lu N==%u et M==%d - + Error: --filename-base is required - Erreur : --filename-base est requis + Erreur : --filename-base est requis - + Error: unsupported scheme: only N/N and N-1/N are supported - Erreur : schéma non supporté : seuls N/N et N-1/N sont supportés + Erreur : schéma non supporté : seuls N/N et N-1/N sont supportés sw - + Generate new wallet and save it to <arg> Générer un nouveau portefeuille et le sauvegarder dans <arg> - + + Generate new wallet from device and save it to <arg> + Générer un nouveau portefeuille à partir de l'appareil et le sauvegarder dans <arg> + + + Generate incoming-only wallet from view key Générer un portefeuille d'audit à partir d'une clé d'audit - + Generate deterministic wallet from spend key Générer un portefeuille déterministe à partir d'une clé de dépense - + Generate wallet from private keys Générer un portefeuille à partir de clés privées - + Generate a master wallet from multisig wallet keys Générer un portefeuille principal à partir de clés de portefeuille multisig - + Language for mnemonic Langue de la phrase mnémonique - + Specify Electrum seed for wallet recovery/creation Spécifier la phrase mnémonique Electrum pour la récupération/création d'un portefeuille - + Recover wallet using Electrum-style mnemonic seed Récupérer un portefeuille en utilisant une phrase mnémonique de style Electrum - + Recover multisig wallet using Electrum-style mnemonic seed Récupérer un portefeuille multisig en utilisant une phrase mnémonique de style Electrum - + Generate non-deterministic view and spend keys Générer des clés d'audit et de dépense non déterministes - - Enable commands which rely on a trusted daemon - Activer les commandes qui dépendent d'un démon de confiance + + invalid argument: must be either 0/1, true/false, y/n, yes/no + argument invalide : doit être soit 0/1, true/false, y/n, yes/no - + + DNSSEC validation passed + Validation DNSSEC réussie + + + + WARNING: DNSSEC validation was unsuccessful, this address may not be correct! + ATTENTION: la validation DNSSEC a échoué, cette adresse n'est peut être pas correcte ! + + + + For URL: + Pour l'URL : + + + + Monero Address = + Adresse Monero = + + + + Is this OK? (Y/n) + Est-ce correct ? (Y/n) + + + + you have cancelled the transfer request + vous avez annulé la demande de transfert + + + + failed to parse index: + échec de l'analyse de l'index : + + + + invalid format for subaddress lookahead; must be <major>:<minor> + format invalide pour l'anticipation des sous-addresses; doit être <majeur>:<mineur> + + + + no connection to daemon. Please make sure daemon is running. + pas de connexion au démon. Veuillez vous assurer que le démon fonctionne. + + + + RPC error: + Erreur RPC : + + + + failed to get random outputs to mix: + échec de la récupération de sorties aléatoires à mélanger : + + + + + Not enough money in unlocked balance + Pas assez de fonds dans le solde débloqué + + + + Failed to find a way to create transactions. This is usually due to dust which is so small it cannot pay for itself in fees, or trying to send more money than the unlocked balance, or not leaving enough for fees + Impossible de trouver une façon de créer les transactions. Ceci est souvent dû à de la poussière si petite qu'elle ne peut pas payer ses propres frais, ou à une tentative d'envoi d'un montant supérieur au solde débloqué, ou à un montant restant insuffisant pour payer les frais + + + + not enough outputs for specified ring size + pas assez de sorties pour la taille de cercle spécifiée + + + + output amount + montant de la sortie + + + + found outputs to use + sorties à utiliser trouvées + + + + Please use sweep_unmixable. + Veuillez utiliser sweep_unmixable. + + + + transaction was not constructed + la transaction n'a pas été construite + + + + transaction %s was rejected by daemon with status: + la transaction %s a été rejetée par le démon avec le statut : + + + + Reason: + Raison : + + + + one of destinations is zero + une des destinations est zéro + + + + failed to find a suitable way to split transactions + échec de la recherche d'une façon adéquate de scinder les transactions + + + + unknown transfer error: + erreur de transfert inconnue : + + + + Multisig error: + Erreur multisig : + + + + internal error: + erreur interne : + + + + unexpected error: + erreur inattendue : + + + + There was an error, which could mean the node may be trying to get you to retry creating a transaction, and zero in on which outputs you own. Or it could be a bona fide error. It may be prudent to disconnect from this node, and not try to send a transaction immediately. Alternatively, connect to another node so the original node cannot correlate information. + Il y a eu une erreur, ce qui pourrait signifier que le noeud essaye de vous faire réessayer de créer une transaction, pour tenter d'identifier quelles sorties sont les votres. Ou il pourrait s'agir d'une erreur de bonne foi. Il pourrait être prudent de se déconnecter de ce noeud, et de ne pas essayer d'envoyer une transaction immédiatement. Ou sinon, se connecter à un autre noeud pour que le noeud original ne puisse pas corréler les informations. + + + + File %s likely stores wallet private keys! Use a different file name. + Le fichier %s contient probablement des clés privées de portefeuille ! Utilisez un nom de fichier différent. + + + + File %s already exists. Are you sure to overwrite it? (Y/Yes/N/No): + Le fichier %s existe déjà. Êtes vous sûr de vouloir l'écraser ? (Y/Yes/N/No) : + + + + seconds + secondes + + + + minutes + minutes + + + + hours + heures + + + + days + jours + + + + months + mois + + + + a long time + longtemps + + + + This is the command line monero wallet. It needs to connect to a monero +daemon to work correctly. +WARNING: Do not reuse your Monero keys on another fork, UNLESS this fork has key reuse mitigations built in. Doing so will harm your privacy. + Ceci est le portefeuille monero en ligne de commande. +Il a besoin de se connecter à un démon monero pour fonctionner correctement. +ATTENTION : Ne réutilisez pas vos clés Monero avec un autre fork, À MOINS QUE ce fork inclue des mitigations contre la réutilisation des clés. Faire ceci nuira à votre confidentialité. + + + + Unknown command: + Commande inconnue : + + + Allow communicating with a daemon that uses a different RPC version Autoriser la communication avec un démon utilisant une version de RPC différente - + Restore from specific blockchain height Restaurer à partir d'une hauteur de bloc spécifique - + The newly created transaction will not be relayed to the monero network La transaction nouvellement créée ne sera pas transmise au réseau monero - + + Create an address file for new wallets + Créer un fichier d'adresse pour les nouveaux portefeuilles + + + + Display English language names + Afficher les noms de langue en anglais + + + + failed to read wallet password + échec de la lecture du mot de passe du portefeuille + + + + Enter a new password for the wallet + Entrer un nouveau mot de passe pour le portefeuille + + + + Wallet password + Mot de passe du portefeuille + + + + daemon is busy. Please try again later. le démon est occupé. Veuillez réessayer plus tard. - + possibly lost connection to daemon connexion avec le démon peut-être perdue - + Error: - Erreur : - - - - This is the command line monero wallet. It needs to connect to a monero -daemon to work correctly. - Ceci est le portefeuille monero en ligne de commande. Il a besoin de se -connecter à un démon monero pour fonctionner correctement. + Erreur : - + Failed to initialize wallet Échec de l'initialisation du portefeuille @@ -3794,310 +4749,365 @@ connecter à un démon monero pour fonctionner correctement. tools::wallet2 - + Use daemon instance at <host>:<port> Utiliser l'instance de démon située à <hôte>:<port> - + Use daemon instance at host <arg> instead of localhost Utiliser l'instance de démon située à l'hôte <arg> au lieu de localhost - + Wallet password file Fichier mot de passe du portefeuille - + Use daemon instance at port <arg> instead of 18081 Utiliser l'instance de démon située au port <arg> au lieu de 18081 - + For testnet. Daemon must also be launched with --testnet flag Pour testnet, le démon doit aussi être lancé avec l'option --testnet - - Restricts to view-only commands - Restreindre aux commandes en lecture-seule - - - + can't specify daemon host or port more than once impossible de spécifier l'hôte ou le port du démon plus d'une fois - + can't specify more than one of --password and --password-file impossible de spécifier plus d'une option parmis --password et --password-file - + the password file specified could not be read le fichier mot de passe spécifié n'a pas pu être lu - + Failed to load file Échec du chargement du fichier - + Wallet password (escape/quote as needed) Mot de passe du portefeuille (échapper/citer si nécessaire) - + + Enable commands which rely on a trusted daemon + Activer les commandes qui dépendent d'un démon de confiance + + + + Disable commands which rely on a trusted daemon + Désactiver les commandes qui dépendent d'un démon de confiance + + + Specify username[:password] for daemon RPC client Spécifier le nom_utilisateur:[mot_de_passe] pour le client RPC du démon - + + For stagenet. Daemon must also be launched with --stagenet flag + Pour stagenet, le démon doit aussi être lancé avec l'option --stagenet + + + + Set shared ring database path + Définir le chemin de la base de donnée de cercles partagés + + + + Number of rounds for the key derivation function + Nombre de rondes de la fonction de dérivation de clé + + + + HW device to use + Portefeuille matériel à utiliser + + + + --trusted-daemon and --untrusted-daemon are both seen, assuming untrusted + --trusted-daemon et --untrusted-daemon présents simultanément, --untrusted-daemon choisi + + + + Daemon is local, assuming trusted + Le démon est local, supposons qu'il est de confiance + + + no password specified; use --prompt-for-password to prompt for a password pas de mot de passe spécifié; utilisez --prompt-for-password pour demander un mot de passe - + + Enter a new password for the wallet + Entrer un nouveau mot de passe pour le portefeuille + + + + Wallet password + Mot de passe du portefeuille + + + Failed to parse JSON Échec de l'analyse JSON - + Version %u too new, we can only grok up to %u Version %u trop récente, on comprend au mieux %u - + failed to parse view key secret key échec de l'analyse de la clé secrète d'audit - - - + + + failed to verify view key secret key échec de la vérification de la clé secrète d'audit - + failed to parse spend key secret key échec de l'analyse de la clé secrète de dépense - - - + + + failed to verify spend key secret key échec de la vérification de la clé secrète de dépense - + Electrum-style word list failed verification Échec de la vérification de la liste de mots de style Electrum - - At least one of Electrum-style word list and private view key and private spend key must be specified + + At least one of either an Electrum-style word list, private view key, or private spend key must be specified Il faut spécifier au moins une des options parmis la liste de mots de style Electrum, la clé privée d'audit et la clé privée de dépense - + Both Electrum-style word list and private key(s) specified Liste de mots de style Electrum et clé privée spécifiées en même temps - + invalid address adresse invalide - + view key does not match standard address la clé d'audit ne correspond pas à l'adresse standard - + spend key does not match standard address la clé de dépense ne correspond pas à l'adresse standard - + Cannot generate deprecated wallets from JSON Impossible de générer un portefeuille obsolète à partir de JSON - + failed to parse address: - échec de l'analyse de l'adresse : + échec de l'analyse de l'adresse : - + Address must be specified in order to create watch-only wallet L'adresse doit être spécifiée afin de créer un portefeuille d'audit - + failed to generate new wallet: - échec de la génération du nouveau portefeuille : + échec de la génération du nouveau portefeuille : - - - - - - - - + + Password is needed to compute key image for incoming monero + Le mot de passe est requis pour calculer l'image de clé pour les moneros entrants + + + + Invalid password: password is needed to compute key image for incoming monero + Mot de passe invalide : le mot de passe est requis pour calculer l'image de clé pour les moneros entrants + + + + + Primary account Compte primaire - + No funds received in this tx. Aucun fonds n'a été reçu dans cette transaction. - + failed to read file échec de la lecture du fichier + + + Set subaddress lookahead sizes to <major>:<minor> + Définir les tailles d'anticipation des sous-addresses à <majeur>:<mineur> + tools::wallet_rpc_server - - Daemon is local, assuming trusted - Le démon est local, supposons qu'il est de confiance - - - + Failed to create directory Échec de la création du répertoire - + Failed to create directory %s: %s - Échec de la création du répertoire %s : %s + Échec de la création du répertoire %s : %s - + Cannot specify -- Impossible de spécifier -- - + and -- et -- - + Failed to create file Échec de la création du fichier - + . Check permissions or remove file . Vérifiez les permissions ou supprimez le fichier - + Error writing to file Erreur d'écriture dans le fichier - + RPC username/password is stored in file nom_utilisateur/mot_de_passe RPC sauvegardé dans le fichier - + Tag %s is unregistered. Le mot clé %s n'est pas enregistré. - + Transaction not possible. Available only %s, transaction amount %s = %s + %s (fee) - Transaction impossible. Solde disponible : %s, montant de la transaction %s = %s + %s (frais) + Transaction impossible. Solde disponible : %s, montant de la transaction %s = %s + %s (frais) - + This is the RPC monero wallet. It needs to connect to a monero daemon to work correctly. Ceci est le portefeuille monero par RPC. Il a besoin de se connecter à un démon monero pour fonctionner correctement. - + Can't specify more than one of --wallet-file and --generate-from-json Impossible de spécifier plus d'une option parmis --wallet-file et --generate-from-json - + + Can't specify more than one of --testnet and --stagenet + Impossible de spécifier plus d'une option parmis --testnet et --stagenet + + + Must specify --wallet-file or --generate-from-json or --wallet-dir --wallet-file, --generate-from-json ou --wallet-dir doit être spécifié - + Loading wallet... Chargement du portefeuille... - - + + Saving wallet... Sauvegarde du portefeuille... - - + + Successfully saved Sauvegardé avec succès - + Successfully loaded Chargé avec succès - + Wallet initialization failed: - Échec de l'initialisation du portefeuille : + Échec de l'initialisation du portefeuille : - + Failed to initialize wallet RPC server Échec de l'initialisation du serveur RPC du portefeuille - + Starting wallet RPC server Démarrage du serveur RPC du portefeuille - + Failed to run wallet: - Échec du lancement du portefeuille : + Échec du lancement du portefeuille : - + Stopped wallet RPC server Arrêt du serveur RPC du portefeuille - + Failed to save wallet: - Échec de la sauvegarde du portefeuille : + Échec de la sauvegarde du portefeuille : wallet_args - - - + + + Wallet options Options du portefeuille @@ -4112,51 +5122,51 @@ connecter à un démon monero pour fonctionner correctement. Utiliser le portefeuille <arg> - + Max number of threads to use for a parallel job Nombre maximum de threads à utiliser pour les travaux en parallèle - + Specify log file Spécifier le fichier journal - + Config file Fichier de configuration - + General options Options générales - + This is the command line monero wallet. It needs to connect to a monero daemon to work correctly. Ceci est le portefeuille monero en ligne de commande. Il a besoin de se connecter à un démon monero pour fonctionner correctement. - + Can't find config file Impossible de trouver le fichier de configuration - + Logging to: - Journalisation dans : + Journalisation dans : - + Logging to %s Journalisation dans %s - + Usage: - Usage : + Usage : From cbdd6b910b3140f8ef317bd02ad1398bccff3bf0 Mon Sep 17 00:00:00 2001 From: iDunk5400 Date: Sat, 6 Oct 2018 13:57:23 +0200 Subject: [PATCH 0532/1404] Linux: Fix building of static binaries with hw device support This should enable building static binaries on Linux systems where dependencies are already built with -fPIC, such as Ubuntu 18.04. --- cmake/FindHIDAPI.cmake | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/cmake/FindHIDAPI.cmake b/cmake/FindHIDAPI.cmake index a689fb4eb6b..9b45dcc4c10 100644 --- a/cmake/FindHIDAPI.cmake +++ b/cmake/FindHIDAPI.cmake @@ -39,6 +39,20 @@ find_package_handle_standard_args(HIDAPI if(HIDAPI_FOUND) set(HIDAPI_LIBRARIES "${HIDAPI_LIBRARY}") + if((STATIC AND UNIX AND NOT APPLE) OR (DEPENDS AND CMAKE_SYSTEM_NAME STREQUAL "Linux")) + find_library(LIBUSB-1.0_LIBRARY usb-1.0) + find_library(LIBUDEV_LIBRARY udev) + if(LIBUSB-1.0_LIBRARY) + set(HIDAPI_LIBRARIES "${HIDAPI_LIBRARIES};${LIBUSB-1.0_LIBRARY}") + if(LIBUDEV_LIBRARY) + set(HIDAPI_LIBRARIES "${HIDAPI_LIBRARIES};${LIBUDEV_LIBRARY}") + else() + message(WARNING "libudev library not found, binaries may fail to link.") + endif() + else() + message(WARNING "libusb-1.0 library not found, binaries may fail to link.") + endif() + endif() set(HIDAPI_INCLUDE_DIRS "${HIDAPI_INCLUDE_DIR}") endif() From 0656050f76c8aa9cd8845b0ddcc1df3f3fa378c0 Mon Sep 17 00:00:00 2001 From: Lafudoci Date: Sun, 7 Oct 2018 09:52:10 +0800 Subject: [PATCH 0533/1404] README: update MSYS2 dependencies for Ledger --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d16b215289e..bce41f5cfdb 100644 --- a/README.md +++ b/README.md @@ -317,11 +317,11 @@ application. To build for 64-bit Windows: - pacman -S mingw-w64-x86_64-toolchain make mingw-w64-x86_64-cmake mingw-w64-x86_64-boost mingw-w64-x86_64-openssl mingw-w64-x86_64-zeromq mingw-w64-x86_64-libsodium + pacman -S mingw-w64-x86_64-toolchain make mingw-w64-x86_64-cmake mingw-w64-x86_64-boost mingw-w64-x86_64-openssl mingw-w64-x86_64-zeromq mingw-w64-x86_64-libsodium mingw-w64-x86_64-hidapi To build for 32-bit Windows: - pacman -S mingw-w64-i686-toolchain make mingw-w64-i686-cmake mingw-w64-i686-boost mingw-w64-i686-openssl mingw-w64-i686-zeromq mingw-w64-i686-libsodium + pacman -S mingw-w64-i686-toolchain make mingw-w64-i686-cmake mingw-w64-i686-boost mingw-w64-i686-openssl mingw-w64-i686-zeromq mingw-w64-i686-libsodium mingw-w64-i686-hidapi * Open the MingW shell via `MinGW-w64-Win64 Shell` shortcut on 64-bit Windows or `MinGW-w64-Win64 Shell` shortcut on 32-bit Windows. Note that if you are From 9907ea0694ecf025258fd1b28e3dcc8c2c1b54d0 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 3 Aug 2018 10:21:08 +0000 Subject: [PATCH 0534/1404] cryptonote: sort tx_extra fields This removes some small amount of fingerprinting entropy. There is no consensus rule to require this since this field is technically free form, and a transaction is free to have custom data in it. --- src/cryptonote_basic/cryptonote_basic.h | 1 - .../cryptonote_format_utils.cpp | 85 +++++++++++++++++++ .../cryptonote_format_utils.h | 2 + src/cryptonote_core/cryptonote_tx_utils.cpp | 6 ++ tests/unit_tests/test_tx_utils.cpp | 84 ++++++++++++++++++ 5 files changed, 177 insertions(+), 1 deletion(-) diff --git a/src/cryptonote_basic/cryptonote_basic.h b/src/cryptonote_basic/cryptonote_basic.h index d4558ef7b04..b0eabb0aa97 100644 --- a/src/cryptonote_basic/cryptonote_basic.h +++ b/src/cryptonote_basic/cryptonote_basic.h @@ -47,7 +47,6 @@ #include "crypto/crypto.h" #include "crypto/hash.h" #include "misc_language.h" -#include "tx_extra.h" #include "ringct/rctTypes.h" #include "device/device.hpp" diff --git a/src/cryptonote_basic/cryptonote_format_utils.cpp b/src/cryptonote_basic/cryptonote_format_utils.cpp index 428be1c9cc1..0231a032e85 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.cpp +++ b/src/cryptonote_basic/cryptonote_format_utils.cpp @@ -376,6 +376,91 @@ namespace cryptonote return true; } //--------------------------------------------------------------- + template + static bool pick(binary_archive &ar, std::vector &fields, uint8_t tag) + { + std::vector::iterator it; + while ((it = std::find_if(fields.begin(), fields.end(), [](const tx_extra_field &f) { return f.type() == typeid(T); })) != fields.end()) + { + bool r = ::do_serialize(ar, tag); + CHECK_AND_NO_ASSERT_MES_L1(r, false, "failed to serialize tx extra field"); + r = ::do_serialize(ar, boost::get(*it)); + CHECK_AND_NO_ASSERT_MES_L1(r, false, "failed to serialize tx extra field"); + fields.erase(it); + } + return true; + } + //--------------------------------------------------------------- + bool sort_tx_extra(const std::vector& tx_extra, std::vector &sorted_tx_extra, bool allow_partial) + { + std::vector tx_extra_fields; + + if(tx_extra.empty()) + { + sorted_tx_extra.clear(); + return true; + } + + std::string extra_str(reinterpret_cast(tx_extra.data()), tx_extra.size()); + std::istringstream iss(extra_str); + binary_archive ar(iss); + + bool eof = false; + size_t processed = 0; + while (!eof) + { + tx_extra_field field; + bool r = ::do_serialize(ar, field); + if (!r) + { + MWARNING("failed to deserialize extra field. extra = " << string_tools::buff_to_hex_nodelimer(std::string(reinterpret_cast(tx_extra.data()), tx_extra.size()))); + if (!allow_partial) + return false; + break; + } + tx_extra_fields.push_back(field); + processed = iss.tellg(); + + std::ios_base::iostate state = iss.rdstate(); + eof = (EOF == iss.peek()); + iss.clear(state); + } + if (!::serialization::check_stream_state(ar)) + { + MWARNING("failed to deserialize extra field. extra = " << string_tools::buff_to_hex_nodelimer(std::string(reinterpret_cast(tx_extra.data()), tx_extra.size()))); + if (!allow_partial) + return false; + } + MTRACE("Sorted " << processed << "/" << tx_extra.size()); + + std::ostringstream oss; + binary_archive nar(oss); + + // sort by: + if (!pick(nar, tx_extra_fields, TX_EXTRA_TAG_PUBKEY)) return false; + if (!pick(nar, tx_extra_fields, TX_EXTRA_TAG_ADDITIONAL_PUBKEYS)) return false; + if (!pick(nar, tx_extra_fields, TX_EXTRA_NONCE)) return false; + if (!pick(nar, tx_extra_fields, TX_EXTRA_MERGE_MINING_TAG)) return false; + if (!pick(nar, tx_extra_fields, TX_EXTRA_MYSTERIOUS_MINERGATE_TAG)) return false; + if (!pick(nar, tx_extra_fields, TX_EXTRA_TAG_PADDING)) return false; + + // if not empty, someone added a new type and did not add a case above + if (!tx_extra_fields.empty()) + { + MERROR("tx_extra_fields not empty after sorting, someone forgot to add a case above"); + return false; + } + + std::string oss_str = oss.str(); + if (allow_partial && processed < tx_extra.size()) + { + MDEBUG("Appending unparsed data"); + oss_str += std::string((const char*)tx_extra.data() + processed, tx_extra.size() - processed); + } + sorted_tx_extra = std::vector(oss_str.begin(), oss_str.end()); + return true; + } + //--------------------------------------------------------------- crypto::public_key get_tx_pub_key_from_extra(const std::vector& tx_extra, size_t pk_index) { std::vector tx_extra_fields; diff --git a/src/cryptonote_basic/cryptonote_format_utils.h b/src/cryptonote_basic/cryptonote_format_utils.h index 8a5296d5bc5..b40db4668c9 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.h +++ b/src/cryptonote_basic/cryptonote_format_utils.h @@ -31,6 +31,7 @@ #pragma once #include "blobdatatype.h" #include "cryptonote_basic_impl.h" +#include "tx_extra.h" #include "account.h" #include "subaddress_index.h" #include "include_base_utils.h" @@ -64,6 +65,7 @@ namespace cryptonote } bool parse_tx_extra(const std::vector& tx_extra, std::vector& tx_extra_fields); + bool sort_tx_extra(const std::vector& tx_extra, std::vector &sorted_tx_extra, bool allow_partial = false); crypto::public_key get_tx_pub_key_from_extra(const std::vector& tx_extra, size_t pk_index = 0); crypto::public_key get_tx_pub_key_from_extra(const transaction_prefix& tx, size_t pk_index = 0); crypto::public_key get_tx_pub_key_from_extra(const transaction& tx, size_t pk_index = 0); diff --git a/src/cryptonote_core/cryptonote_tx_utils.cpp b/src/cryptonote_core/cryptonote_tx_utils.cpp index 071ce591eef..1e5a7fb235e 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.cpp +++ b/src/cryptonote_core/cryptonote_tx_utils.cpp @@ -38,6 +38,7 @@ using namespace epee; #include "cryptonote_tx_utils.h" #include "cryptonote_config.h" #include "cryptonote_basic/miner.h" +#include "cryptonote_basic/tx_extra.h" #include "crypto/crypto.h" #include "crypto/hash.h" #include "ringct/rctSigs.h" @@ -84,6 +85,8 @@ namespace cryptonote if(!extra_nonce.empty()) if(!add_extra_nonce_to_tx_extra(tx.extra, extra_nonce)) return false; + if (!sort_tx_extra(tx.extra, tx.extra)) + return false; txin_gen in; in.height = height; @@ -434,6 +437,9 @@ namespace cryptonote add_additional_tx_pub_keys_to_extra(tx.extra, additional_tx_public_keys); } + if (!sort_tx_extra(tx.extra, tx.extra)) + return false; + //check money if(summary_outs_money > summary_inputs_money ) { diff --git a/tests/unit_tests/test_tx_utils.cpp b/tests/unit_tests/test_tx_utils.cpp index 8a9f983e643..55c76c3b6cd 100644 --- a/tests/unit_tests/test_tx_utils.cpp +++ b/tests/unit_tests/test_tx_utils.cpp @@ -33,6 +33,8 @@ #include #include "common/util.h" +#include "cryptonote_basic/cryptonote_basic.h" +#include "cryptonote_basic/tx_extra.h" #include "cryptonote_core/cryptonote_tx_utils.h" namespace @@ -203,3 +205,85 @@ TEST(validate_parse_amount_case, validate_parse_amount) r = cryptonote::parse_amount(res, "1 00.00 00"); ASSERT_FALSE(r); } + +TEST(sort_tx_extra, empty) +{ + std::vector extra, sorted; + ASSERT_TRUE(cryptonote::sort_tx_extra(extra, sorted)); + ASSERT_EQ(extra, sorted); +} + +TEST(sort_tx_extra, pubkey) +{ + std::vector sorted; + const uint8_t extra_arr[] = {1, 30, 208, 98, 162, 133, 64, 85, 83, 112, 91, 188, 89, 211, 24, 131, 39, 154, 22, 228, + 80, 63, 198, 141, 173, 111, 244, 183, 4, 149, 186, 140, 230}; + std::vector extra(&extra_arr[0], &extra_arr[0] + sizeof(extra_arr)); + ASSERT_TRUE(cryptonote::sort_tx_extra(extra, sorted)); + ASSERT_EQ(extra, sorted); +} + +TEST(sort_tx_extra, two_pubkeys) +{ + std::vector sorted; + const uint8_t extra_arr[] = {1, 30, 208, 98, 162, 133, 64, 85, 83, 112, 91, 188, 89, 211, 24, 131, 39, 154, 22, 228, + 80, 63, 198, 141, 173, 111, 244, 183, 4, 149, 186, 140, 230, + 1, 30, 208, 98, 162, 133, 64, 85, 83, 112, 91, 188, 89, 211, 24, 131, 39, 154, 22, 228, + 80, 63, 198, 141, 173, 111, 244, 183, 4, 149, 186, 140, 230}; + std::vector extra(&extra_arr[0], &extra_arr[0] + sizeof(extra_arr)); + ASSERT_TRUE(cryptonote::sort_tx_extra(extra, sorted)); + ASSERT_EQ(extra, sorted); +} + +TEST(sort_tx_extra, keep_order) +{ + std::vector sorted; + const uint8_t extra_arr[] = {1, 30, 208, 98, 162, 133, 64, 85, 83, 112, 91, 188, 89, 211, 24, 131, 39, 154, 22, 228, + 80, 63, 198, 141, 173, 111, 244, 183, 4, 149, 186, 140, 230, + 2, 9, 1, 0, 0, 0, 0, 0, 0, 0, 0}; + std::vector extra(&extra_arr[0], &extra_arr[0] + sizeof(extra_arr)); + ASSERT_TRUE(cryptonote::sort_tx_extra(extra, sorted)); + ASSERT_EQ(extra, sorted); +} + +TEST(sort_tx_extra, switch_order) +{ + std::vector sorted; + const uint8_t extra_arr[] = {2, 9, 1, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 30, 208, 98, 162, 133, 64, 85, 83, 112, 91, 188, 89, 211, 24, 131, 39, 154, 22, 228, + 80, 63, 198, 141, 173, 111, 244, 183, 4, 149, 186, 140, 230}; + const uint8_t expected_arr[] = {1, 30, 208, 98, 162, 133, 64, 85, 83, 112, 91, 188, 89, 211, 24, 131, 39, 154, 22, 228, + 80, 63, 198, 141, 173, 111, 244, 183, 4, 149, 186, 140, 230, + 2, 9, 1, 0, 0, 0, 0, 0, 0, 0, 0}; + std::vector extra(&extra_arr[0], &extra_arr[0] + sizeof(extra_arr)); + ASSERT_TRUE(cryptonote::sort_tx_extra(extra, sorted)); + std::vector expected(&expected_arr[0], &expected_arr[0] + sizeof(expected_arr)); + ASSERT_EQ(expected, sorted); +} + +TEST(sort_tx_extra, invalid) +{ + std::vector sorted; + const uint8_t extra_arr[] = {1}; + std::vector extra(&extra_arr[0], &extra_arr[0] + sizeof(extra_arr)); + ASSERT_FALSE(cryptonote::sort_tx_extra(extra, sorted)); +} + +TEST(sort_tx_extra, invalid_suffix_strict) +{ + std::vector sorted; + const uint8_t extra_arr[] = {2, 9, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1}; + std::vector extra(&extra_arr[0], &extra_arr[0] + sizeof(extra_arr)); + ASSERT_FALSE(cryptonote::sort_tx_extra(extra, sorted)); +} + +TEST(sort_tx_extra, invalid_suffix_partial) +{ + std::vector sorted; + const uint8_t extra_arr[] = {2, 9, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1}; + const uint8_t expected_arr[] = {2, 9, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1}; + std::vector extra(&extra_arr[0], &extra_arr[0] + sizeof(extra_arr)); + ASSERT_TRUE(cryptonote::sort_tx_extra(extra, sorted, true)); + std::vector expected(&expected_arr[0], &expected_arr[0] + sizeof(expected_arr)); + ASSERT_EQ(sorted, expected); +} From 21a624af23b0322d69f07d066dfa6e493afffd4f Mon Sep 17 00:00:00 2001 From: TheCharlatan Date: Sun, 7 Oct 2018 19:16:22 +0200 Subject: [PATCH 0535/1404] Consolidate HID depends makefiles into single recipe Make sure all required dependencies are linked statically, by only providing the static libraries. --- contrib/depends/packages/eudev.mk | 2 +- contrib/depends/packages/hidapi-darwin.mk | 23 ------------------- contrib/depends/packages/hidapi-linux.mk | 28 ----------------------- contrib/depends/packages/hidapi.mk | 8 ++++++- contrib/depends/packages/packages.mk | 7 +++--- contrib/depends/packages/sodium-darwin.mk | 2 +- contrib/depends/packages/sodium.mk | 2 +- contrib/depends/toolchain.cmake.in | 3 +++ 8 files changed, 16 insertions(+), 59 deletions(-) delete mode 100644 contrib/depends/packages/hidapi-darwin.mk delete mode 100644 contrib/depends/packages/hidapi-linux.mk diff --git a/contrib/depends/packages/eudev.mk b/contrib/depends/packages/eudev.mk index 08752909fe3..e754c0f2085 100644 --- a/contrib/depends/packages/eudev.mk +++ b/contrib/depends/packages/eudev.mk @@ -5,7 +5,7 @@ $(package)_file_name=$($(package)_version).tar.gz $(package)_sha256_hash=a96ecb8637667897b8bd4dee4c22c7c5f08b327be45186e912ce6bc768385852 define $(package)_set_vars - $(package)_config_opts=--disable-gudev --disable-introspection --disable-hwdb --disable-manpages + $(package)_config_opts=--disable-gudev --disable-introspection --disable-hwdb --disable-manpages --disable-shared endef define $(package)_config_cmds diff --git a/contrib/depends/packages/hidapi-darwin.mk b/contrib/depends/packages/hidapi-darwin.mk deleted file mode 100644 index 014aba5787b..00000000000 --- a/contrib/depends/packages/hidapi-darwin.mk +++ /dev/null @@ -1,23 +0,0 @@ -package=hidapi-darwin -$(package)_version=0.8.0-rc1 -$(package)_download_path=https://github.com/signal11/hidapi/archive -$(package)_file_name=hidapi-$($(package)_version).tar.gz -$(package)_sha256_hash=3c147200bf48a04c1e927cd81589c5ddceff61e6dac137a605f6ac9793f4af61 - -define $(package)_set_vars -$(package)_config_opts=--enable-static -$(package)_config_opts+=--prefix=$(host_prefix) -endef - -define $(package)_config_cmds - ./bootstrap &&\ - $($(package)_autoconf) $($(package)_config_opts) RANLIB="$(host_prefix)/native/bin/x86_64-apple-darwin11-ranlib" AR="$(host_prefix)/native/bin/x86_64-apple-darwin11-ar" CC="$(host_prefix)/native/bin/$($(package)_cc)" -endef - -define $(package)_build_cmds - $(MAKE) -endef - -define $(package)_stage_cmds - $(MAKE) DESTDIR=$($(package)_staging_dir) install -endef diff --git a/contrib/depends/packages/hidapi-linux.mk b/contrib/depends/packages/hidapi-linux.mk deleted file mode 100644 index b2a49991550..00000000000 --- a/contrib/depends/packages/hidapi-linux.mk +++ /dev/null @@ -1,28 +0,0 @@ -package=hidapi-linux -$(package)_version=0.8.0-rc1 -$(package)_download_path=https://github.com/signal11/hidapi/archive -$(package)_file_name=hidapi-$($(package)_version).tar.gz -$(package)_sha256_hash=3c147200bf48a04c1e927cd81589c5ddceff61e6dac137a605f6ac9793f4af61 -$(package)_dependencies=libusb eudev - -define $(package)_set_vars -$(package)_config_opts=--enable-static -$(package)_config_opts+=--prefix=$(host_prefix) -$(package)_config_opts+=libudev_LIBS="-L$(host_prefix)/lib -ludev" -$(package)_config_opts+=libudev_CFLAGS=-I$(host_prefix)/include -$(package)_config_opts+=libusb_LIBS="-L$(host_prefix)/lib -lusb-1.0" -$(package)_config_opts+=libusb_CFLAGS=-I$(host_prefix)/include/libusb-1.0 -endef - -define $(package)_config_cmds - ./bootstrap &&\ - $($(package)_autoconf) $($(package)_config_opts) -endef - -define $(package)_build_cmds - $(MAKE) -endef - -define $(package)_stage_cmds - $(MAKE) DESTDIR=$($(package)_staging_dir) install -endef diff --git a/contrib/depends/packages/hidapi.mk b/contrib/depends/packages/hidapi.mk index b6b228643cf..d4dd80e2262 100644 --- a/contrib/depends/packages/hidapi.mk +++ b/contrib/depends/packages/hidapi.mk @@ -3,10 +3,16 @@ $(package)_version=0.8.0-rc1 $(package)_download_path=https://github.com/signal11/hidapi/archive $(package)_file_name=$(package)-$($(package)_version).tar.gz $(package)_sha256_hash=3c147200bf48a04c1e927cd81589c5ddceff61e6dac137a605f6ac9793f4af61 +$(package)_linux_dependencies=libusb eudev define $(package)_set_vars -$(package)_config_opts=--enable-static +$(package)_config_opts=--enable-static --disable-shared $(package)_config_opts+=--prefix=$(host_prefix) +$(package)_config_opts_darwin+=RANLIB="$(host_prefix)/native/bin/x86_64-apple-darwin11-ranlib" AR="$(host_prefix)/native/bin/x86_64-apple-darwin11-ar" CC="$(host_prefix)/native/bin/$($(package)_cc)" +$(package)_config_opts_linux+=libudev_LIBS="-L$(host_prefix)/lib -ludev" +$(package)_config_opts_linux+=libudev_CFLAGS=-I$(host_prefix)/include +$(package)_config_opts_linux+=libusb_LIBS="-L$(host_prefix)/lib -lusb-1.0" +$(package)_config_opts_linux+=libusb_CFLAGS=-I$(host_prefix)/include/libusb-1.0 endef define $(package)_config_cmds diff --git a/contrib/depends/packages/packages.mk b/contrib/depends/packages/packages.mk index 9bfbff7d9d1..f814c14d63c 100644 --- a/contrib/depends/packages/packages.mk +++ b/contrib/depends/packages/packages.mk @@ -1,12 +1,12 @@ -packages:=boost openssl libevent zeromq cppzmq zlib expat ldns cppzmq readline libiconv qt +packages:=boost openssl libevent zeromq cppzmq zlib expat ldns cppzmq readline libiconv qt hidapi native_packages := native_ccache wallet_packages=bdb darwin_native_packages = native_biplist native_ds_store native_mac_alias -darwin_packages = sodium-darwin hidapi-darwin +darwin_packages = sodium-darwin -linux_packages = eudev libusb hidapi-linux +linux_packages = eudev libusb ifeq ($(host_os),linux) packages += unwind @@ -15,7 +15,6 @@ endif ifeq ($(host_os),mingw32) packages += icu4c packages += sodium -packages += hidapi endif ifneq ($(build_os),darwin) diff --git a/contrib/depends/packages/sodium-darwin.mk b/contrib/depends/packages/sodium-darwin.mk index 59c6d1ec67d..d8e3de3520f 100644 --- a/contrib/depends/packages/sodium-darwin.mk +++ b/contrib/depends/packages/sodium-darwin.mk @@ -6,7 +6,7 @@ $(package)_sha256_hash=fb6a9e879a2f674592e4328c5d9f79f082405ee4bb05cb6e679b90afe define $(package)_set_vars $(package)_build_opts_darwin=OS=Darwin LIBTOOL="$($(package)_libtool)" -$(package)_config_opts=--enable-static +$(package)_config_opts=--enable-static --disable-shared $(package)_config_opts+=--prefix=$(host_prefix) endef diff --git a/contrib/depends/packages/sodium.mk b/contrib/depends/packages/sodium.mk index 0e6668062cc..c38121bf730 100644 --- a/contrib/depends/packages/sodium.mk +++ b/contrib/depends/packages/sodium.mk @@ -5,7 +5,7 @@ $(package)_file_name=libsodium-$($(package)_version).tar.gz $(package)_sha256_hash=fb6a9e879a2f674592e4328c5d9f79f082405ee4bb05cb6e679b90afe9e178f4 define $(package)_set_vars -$(package)_config_opts=--enable-static +$(package)_config_opts=--enable-static --disable-shared $(package)_config_opts+=--prefix=$(host_prefix) endef diff --git a/contrib/depends/toolchain.cmake.in b/contrib/depends/toolchain.cmake.in index bde7497b3e7..f8548a72462 100644 --- a/contrib/depends/toolchain.cmake.in +++ b/contrib/depends/toolchain.cmake.in @@ -18,6 +18,9 @@ SET(LIBUNWIND_INCLUDE_DIR @prefix@/include) SET(LIBUNWIND_LIBRARIES @prefix@/lib/libunwind.a) SET(LIBUNWIND_LIBRARY_DIRS @prefix@/lib) +SET(LIBUSB-1.0_LIBRARY @prefix@/lib/libusb-1.0.a) +SET(LIBUDEV_LIBRARY @prefix@/lib/libudev.a) + SET(ZMQ_INCLUDE_PATH @prefix@/include) SET(ZMQ_LIB @prefix@/lib/libzmq.a) From f9485a36cf4515dfa5bb4148260a818c1184723e Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 7 Oct 2018 17:43:03 +0000 Subject: [PATCH 0536/1404] tests: update crypto tests data file after PRNG changes --- tests/crypto/tests.txt | 1124 ++++++++++++++++++++-------------------- 1 file changed, 562 insertions(+), 562 deletions(-) diff --git a/tests/crypto/tests.txt b/tests/crypto/tests.txt index 0ae6a05b75b..46f815e72b4 100644 --- a/tests/crypto/tests.txt +++ b/tests/crypto/tests.txt @@ -346,6 +346,7 @@ random_scalar 347065b13ad735ff9e1cd8b471aca4f97d7690b326173fecbbd78528a6d8990e random_scalar a843cbb2a416c655669cbf1a5f452beddba17c8f5c17711c9af6d2580b4b890a random_scalar 93dba1d862d13f16c84fb050117483e48d5821979a4bf468901cf954f6b86d0c random_scalar 307e5c1c1643cba6aec9b1f7c67fde082ff1686c6a53376b2b4a8a84af2c3f0f +random_scalar f02178e283466b8e7747fff209c242dc90b0a4df7f1bb98acb81c6d2332d8806 random_scalar b92d6b60e266e5ffddd530548d77dfb7a3b251748acd16f6ffefab5ecdf2f902 random_scalar 9e6d32f8ac881f5821a146969b33b89dd16e4dfcaa1dcae522aa080021caee0f random_scalar 17072c56bfe20546f15c8bd14e91c861d7795bedd3bedf9120233c4e289c7906 @@ -382,6 +383,7 @@ random_scalar 63b1db6f0a4f98a26e783eb8fb73fb16ea0da816e47eb9231dd00a60e7517c03 random_scalar ffe2b3d84d08d7bcc36695a1d57c5c0d835d8e76fcc9b961e017ed111ee4850f random_scalar f881be752da6bdb31e29f1228760870668550fb793a091fc67d580d236a5c108 random_scalar 013171a3c5ed17b2701874a1f15ec2ef281ec5f5304a497696c611755e39170e +random_scalar 5948ec05726a52adda2ca05c00e9767397ea64fd51dd94ae3bf62772e033d106 random_scalar 69ca89c140209055502cadbb190b818c5305a297786a198c647061e7a7dd5f0e random_scalar 56d497bf314bf6d27cbeed9c7834ec67836b6f9f007e1992164c106b705e8408 random_scalar f0d3539042a82a700d7b8797ef0d7658f5b1b4b8e9689b0c34dd9f2a4262ea0a @@ -406,6 +408,8 @@ random_scalar 0a560a580866b38724402d5d912b827f1fb2964af948dbe0496afa145793bd0d random_scalar bafb7d180deec27520223d6fba4b9abfcff9c141c1fed61a595b788dc6645b01 random_scalar 929e298412d697287b61cdcb0d0afa266ff954aed449e15f49f6f55121e99006 random_scalar 29b159c4069d33395f3eedb93dd4122f9287dc1f55114250b3dc8061bb09970c +random_scalar 2cc4a07721223b3c7764b571e229adb3b6db1452d4b7a2337218e40d34949d03 +random_scalar 7a0e422abc8a0410cc5a98231bb3a987c1d1b669094ab76bc9d618a9789bd300 random_scalar ef5b9b18e232056b91a0b73da1fa364e7192b9276df3c428d5e3556b4fe9f402 random_scalar 7d58f02afa1c64da5e2e05e7b703c53553e914e66c25a5dfb88063c05bcb7500 random_scalar a7c8d18c3f879578f1bf331323358e7c7feecdb58b508444a6466504b6ead009 @@ -413,7 +417,9 @@ random_scalar 0ae41c5656bb3d3a899df86a4b93db9eb7776fe76430025e759387d4df5e320a random_scalar 43ed981404a1cf024c24484477941a7a92285723c0f47d6d57f4e1c66a28ce06 random_scalar 939019df6ff9b3e37034a70527468f51a91099b29917772b12e475cec11eab07 random_scalar cbcb6c3f15934e47741922a5593ae52e4bac95553f3a409742463a1350641800 +random_scalar 1e462b2dcf2617965ce4fac23a5ac97fb37168f9f32fa37583c2c2b63ca5a30b random_scalar 4ea2293558b34a5276e2ce4155f56073a3c6ec68d61695b62ae1459d7996020c +random_scalar c5f696c5c3f721cfe767f7121b2740f7fe450d53aa1eeb590a465ec25a9db700 random_scalar 1477be630f529286ba32d34e15bb95e910146b61cb7b9758964233d17add960f random_scalar 05595b226f810185b71c4bf9b93700f060dd40ea5a4dadfae05906688ff0820c random_scalar 8d4b632a555e02e8537afa2084f545e74fb75cf2955129316c4f2540b667c009 @@ -424,9 +430,11 @@ random_scalar 9d79792bf4708c06fa59f14af6141658a4b778a738ca2bcd1088f45df23f3e0d random_scalar a056651374d7df2ed89c3e37ef9072063281aecd4ff95e48767da04c1fd71206 random_scalar 005253f190dd9d70b900859b3057ef8f5cb01a99af2874b3abcb3a629d51a805 random_scalar 9376111937238f6c53309773217cc89364f8697ea5f3d05ae47adb9174cef201 +random_scalar 9d118c28ab31cfbdc323ac2015a6d5b9b1da84363833d7543a34d617d02b810e random_scalar 1cb13c17e9005e6b38f21ae7013ae9507ade4590b24e5f2b54f842c62993040d random_scalar c2bd6ec6c68431943f0b4d24b97aa83a4f6b465fb5dd9d9c1f73c26286c3500c random_scalar 699eef9c8339ed18ae8129866549a7b6145cd05ea46c7e072491acf0dabc1206 +random_scalar a08326104d8ff1a0ee83b7996c5904a47175c0b7ff206e13cf24d09ec07eef06 random_scalar 4eab216fcf6a99336807269418930e2c02544c3029a281b0ade0b34b71ac6c04 random_scalar cee08c6dc0cee0f301a27733019fcb15888af5c0674d87867ca6629f13622f03 random_scalar 4eb667a0a18c1e79586a74ec59a13331eba48da3f45d83617ac817955e25b00c @@ -448,6 +456,7 @@ random_scalar 5b515323f0a7d7ebafd8d628ef4f32925a6225ea92ec1410bc47d87c33044d06 random_scalar 9816f6ccd6703afcc53026ae742dba2881cdd9fe4e60f16c46f3eed92dde1906 random_scalar c6cf65615f4eeb1b7c883663a3ead84a3e631dae7a2e5c12bb33e0f340e8ac0a random_scalar d915348a86948774d7ea9ecb5dd9dc7ef822d7ef31446e9f0a05a7009ffcdf04 +random_scalar 29104e7b6320b15a4098472bd959836d808d92d85a9e3e2fb89eedc84557d305 random_scalar f9918366b633b8fbe442783869f88e5a231fac34fc9a34782d2cd09b6254e60f random_scalar a443ed5b8e5d178184c940534da5d6139bb2e0dce6a6ecde3f9fc040cea0640c random_scalar aa2bdf5311d205af876f8f0d65a603097a618f58fa9b436fad0856d768fc6b03 @@ -457,6 +466,7 @@ random_scalar 86b64d3333d929a312b847a99956a6e343518f7eed681b21cc81e7e9b690d600 random_scalar 6cf210cbba801072afa883a4db5dea1121b5a0fab727a046f6d08d1b76addb0f random_scalar 1244e035744c4ebb6388543e6938850afd96a7e2b08035a03943d54eb4883206 random_scalar 0254b1c7310f6597914ae0a40ae832ba1c7796011e93df17ba760420ca0a2d0d +random_scalar 91feed7f2c484292d469cd86204f91a42135ab5cdf4d1cb8603334471869a500 random_scalar 7fa680a145f0220c50f677b75d4e39280f74da8e583c7e66518ba12a2862fe0b random_scalar d5e6ae512163e2a522bc94a163e03b1e558e287496c8132cacdb4350513fa50c random_scalar d7a99dfbed9dcd878c9d42d2c205e23fb9bf0b0036be1a60cf023f6a5b9d6909 @@ -484,9 +494,11 @@ random_scalar cce38a425d652f4f0ac8e5ebd99eb05a111b51602e0f4527533325b015e70a05 random_scalar 3798a47b4cdb21595d3fb5cf314347284771427f90dadaef72016f1904681206 random_scalar 789fac4d4d7c47b88f8ca9a3cccc633aabadd8ff51337236b5d3c79cd8912c0a random_scalar b9ea6cb523026f955cb221bb6cc0ad69dc4124b788045a024c941cc447b15401 +random_scalar 0c79a5ce56ce0f3781420721d39961a4121eb5b3021a26d8a8ce7b918e194804 random_scalar a5cbf16377db957b2fb6eb28d188ebdf0823bb93c7341c904959952d16d7da0a random_scalar 1d72637b2f247a0e22fe6cbf180a1ed2a4997697b798d793794b073ab11efe0e random_scalar 6262411ffff10f36232a43afef4ae397b5c65d90fd4bd1ebc0ea177c37f7cf0b +random_scalar 07401995e944c75398aa7ae889d4f98a25620b066bf44088ed271d53a1287702 random_scalar 4b43cc16ecb7dae45cdf9b09e79538bc96371dbd08253a1605d44d525732de0b random_scalar a7e65b1eac2b19da0c5c91717a5487d1f9871b869010790d73fc3553c0953302 random_scalar 2042fa43e1deacd9e831f479d4d2ccc166aae9d0b14445adcc9e9604509f140e @@ -512,6 +524,7 @@ random_scalar bbe7ceee833f824b63fa02b6a49a3a96527fabe6d97b3edff98555498b991b04 random_scalar a319115555a4114e4d68d0427c7e80cce22f3ec01b988949be420b2ca446a40f random_scalar 6e61bcaeb19eac83173799404df79cbf3dd1d6a811b6ac6f1a6b9548a5b12500 random_scalar 13aba81a8841745c078302b4464e26df22cfc420b0c7592a917564df71abd00e +random_scalar 1b9f988c823a596c9bdfe1dccb709b83004ba6bd7abf59a13857cc3f73f99d09 random_scalar b17a5eefdbdd01be4c7ee398f0cac2d71863f8a76c1937f2bc206947ff941c0e random_scalar e9f4aed4bebb6a0696eaa9e325ca919a80730bcf23e5f4e711558a59c819dd07 random_scalar 9b39c9258a4af69da0a90b5afbf007eb8223448b2d24092100f61351302acd08 @@ -533,17 +546,20 @@ random_scalar 226d1f82fb68da5da182a8ec88a4f317cc51c280fd50d2048c7f5991edf03a01 random_scalar 42aaf88cf69e4558541ea582afffd26d90652d062dda640a14412d751ef37e0c random_scalar c87a1f9b1aa80f71d1c78d57c21fe47d7f56aa2d4f397f72fb2af8ef2a401b04 random_scalar 23344bcfa22e85cbd315f370075e3af849f361f7a2e960626564855071b1f004 +random_scalar 288a3ac4adc51aa9a09a55e76e66979447bb5c7f7a23de539bf4021636dc9006 random_scalar a1dfa3cc72b59b1e4bd70e25b10107f1a0e4270f09a07122845379cf56dac104 random_scalar 46b1f9a4a2d23ef6f65190d0d763d0484d5a43e075840ba522586e956b824906 random_scalar 0df69f6758ce363fe6090e9ea60b8e4e62506cb032a16c4d4cff29e8ceb7a10c random_scalar 41322acad808cc6b332d7ce9ac45b3ec216596186e4d2e1524414b1e56174f0c random_scalar 555b681ba25e17cf7a493db2360887ec410df0d1531a21fdb3a70080313c170d +random_scalar 6195bd5aec348dddf3b5a3e80538c1908d4f4fd5da6282a5d68458fc6d08bd05 random_scalar 493d080d01bcf337fa7e81cb9467ee528e245413fb3d100323dbda0c1429310e random_scalar 0d6bd8be3a66d83039fe5abb78c2258bbd028ca211416f3c011a49f358ae0e0e random_scalar 3a872ce933eaaf2f9c845fa8c73651300f5bcfcd59b6faaef12a2f368cd1c30f random_scalar e63184fb512ed19db445caeae0adfee4e373ba93116c661bc60dae380133a104 random_scalar efab70caabf4a2a5a67f12ea27171df5ec4e3394c8f3b352effb47a558c35604 random_scalar 7de9e6ee6a1ba4019077114e83e29078f89ebbebe688248ca3acc3c8539eb80f +random_scalar 410deab0d9c6c4f4f2764ef001280c1d427655d9d814806e2d88f12cad8d2c07 random_scalar 64825ec5a071749732ce7354e234a0293828de98a98c3a563662e509b7072a05 random_scalar dba5ec762e38f676ca8a85876c0bb3c11ab57838893793ff81a47c767261380a random_scalar 22f506889b19f0f699dcce8e3d9bdbe16d91a6105a26e8b3f95e8cb17be20001 @@ -560,11 +576,13 @@ random_scalar f352cd628d86bd73fa5f3c947b9f838d00966bd2915d988ed8bd2067cf8c840e random_scalar c5ab4129d42396221bbb4274a20b231f7507cb0b65911f765a943751800fb90a random_scalar dc8c5d0e642ee21c437fff0f77a08b9a4795c467f3c50354083148b796fbfe08 random_scalar 329b4c0b05d1ce5621612491328cac7f1509001fb5f373a66e8e52090b91fa0d +random_scalar 3514f1428d2cbbcc00c44d43626bdfd8071aab4fccd719da2bcdfcd10339e707 random_scalar 152e4e3f2801194a38b7213fcd2e53859ed22d3e176700921b044db74acaa903 random_scalar 17cd1a31b9b315713c2a6e8295419645918bf8ffa1670441d4dcbac6fcda9c00 random_scalar a8e83c4ddf185d7b048ca69a96753bd055071f43ba4bbd1ce9527b172a796e0c random_scalar ac881d61a4fc26880ff82fe4fce254c5db672b6500ce05658104a39c9792d30d random_scalar 44bd5a9744c3ca92f9429e31a26c1e8206fa88d6b0109767df3dd6b5e081d701 +random_scalar 245923963a0928b1ce93b0282d7e9c5c675edae28cb86c9de2c988f634427603 random_scalar df585700d24b16794d4e931a26fd3ebd666604cf0be40acc032e9e704153f905 random_scalar 444ec7f3eba365d9e5fa91b4fd19df66b4f982ebb76d6a18a2c6f220ede2b001 random_scalar 3f2569c0d441efbb3a15aa01ec773eb67d52bf2563fcb6828497c24d0b64c60e @@ -573,24 +591,6 @@ random_scalar 8dcd442061dcd332fe0ac86c5f97d00cc59c8029f18acc3b5e9de5e8fb910401 random_scalar 40a6e2b2469b93dcd95aec551033180583f1509c3dbc484b54ca2e5a3fd8a702 random_scalar 6d87007ff22b03e9b11a97ef2b7d16171d2f6e3b79970f261e1d3236a179d307 random_scalar e4de14222ccd095e68bef9a3405b778dcdfe6ef5aa178142cfd2819d623ba60f -random_scalar 42bf10f06625682a429c62ee0856ae168d4214aa254732ecfe67fc73cd2be606 -random_scalar bfb5ff7e7b27b0d32d822ea3abce41d30078b9de1e9dba88944ce558d3991c00 -random_scalar 1ce399ce3590215c271c7872d0e7b3b958e108d40b1319ff31a42d5b83ade90d -random_scalar c50084e02284eddb75cf7f0655b63eef571177042bc9e5dc29df3ed4f54b0408 -random_scalar 578d31434a982bb5ec6ce9a931199c7a42176f04264a35ad71847fe06781d80c -random_scalar ca3ae8837892a88874f8c675fd4ebfc59fca8d98e056093f25d88bb07908600e -random_scalar 259ea7deeacd6a38f0d9619cdab2774dbbb93b1d17cb32b3051fc5906c9b1e0d -random_scalar f59d75c0834df84b4aae8c73523388c4eb57c125fdfd0cdc52389751673d3902 -random_scalar 04b222b3c67335aad76448daa244472f40f6764d851cd047e3206d8ac32c0c07 -random_scalar 63ef886fcd7cc534d1e141d64f5af699633c6ce9eb6e4c104ef03300f150a901 -random_scalar e4fac60ab7fb0c2c2c32039cfba4760c6c22a5c2f3c437c50a43b198d817a601 -random_scalar 8b57cb35f1a0714a904896ce7e9a515d7083259f1d325355910c5ef6c79fd90c -random_scalar dc84b8cfce597f499ffbd52829f4dbfb3b110a3482b168c223bbe24cd5aa4800 -random_scalar 3d7d972adc1b1279f71a6eaa4deb8c05a73be74345c7e862ced54822be0f7309 -random_scalar b4129d530f956b59b0bfdfb2036f4ae189cc96c9086bdca921cd348bde327403 -random_scalar 14144a16940145ae1c8ffc09cf5cd15f640d774bb78b95b756de5d7f9af47606 -random_scalar c9e4e8fd6916595ec1b92fcefd2313df304b05856716c8c7ed6b5589f0f72c07 -random_scalar 7b12882ce5338bad3fb9245e65254a8140552f6c89e95a799daeb817d602e109 hash_to_scalar 59d28aeade98016722948bf596af0b7deb5dd641f1aa2a906bd4e1 7d0b25809fc4032a81dd5b0f721a2b21f7f68157c834374f580876f5d91f7409 hash_to_scalar 60d9a4b96951481ab458 b0955682b297dbcae4a5c1b6f21addb211d6180632b538472045b5d592c38109 hash_to_scalar 7d535b4896ddc350a5fdff 7bb1a59783be93ada537801f31ef52b0d2ea135a084c47cbad9a7c6b0d2c990f @@ -847,6 +847,25 @@ hash_to_scalar ea753ba82b4573d428d526de89daccdfa7a33079aa9c9ac3 d9bf5688fed9c3f4 hash_to_scalar 2a5d772c76aae0040915ffd7 8227a0904298e726cedb75746b0a3b41662002b4ed8fa05c2110e4a15bfd310c hash_to_scalar 585900d92342cb8448c944f97d1642 acfa0d43b9c6a91810b0fa1bd42f0d9e077f7f5a62f9d9aaff8418e1130ea508 hash_to_scalar 2ace 427f5090283713a2a8448285f2a22cc8cf5374845766b6370425e2319e40f50d +generate_keys 6f04c33790c0d5879c7973af36464038c3e79cb901e08275b06ab53840df8e8b 42bf10f06625682a429c62ee0856ae168d4214aa254732ecfe67fc73cd2be606 +generate_keys cac7e372a9332f9f485a5264ef94c9acff59e6b6e07cbde280db814f1e3ccf70 bfb5ff7e7b27b0d32d822ea3abce41d30078b9de1e9dba88944ce558d3991c00 +generate_keys 99cf2fc6491cc07af8091c523b5dc6b529890cbef1dfd88399ab144f12777a4d 1ce399ce3590215c271c7872d0e7b3b958e108d40b1319ff31a42d5b83ade90d +generate_keys 07bd418f13dfb48349e9020cfa7f7c681ae60b7bf3576e276a87d15fa3481bab c50084e02284eddb75cf7f0655b63eef571177042bc9e5dc29df3ed4f54b0408 +generate_keys 6959bea2e23e768ca118ca6bef61c16c3a0f3bc2c181aaa217af9269f475cc73 578d31434a982bb5ec6ce9a931199c7a42176f04264a35ad71847fe06781d80c +generate_keys 1b91041175dce27cbba3b57647cc627e752d3d6fa2c4947c906b5fe0aa954c65 ca3ae8837892a88874f8c675fd4ebfc59fca8d98e056093f25d88bb07908600e +generate_keys 99231689da7da67d1aa9a6ef95481dce47cf2c5f5f28e091c416679607167e29 259ea7deeacd6a38f0d9619cdab2774dbbb93b1d17cb32b3051fc5906c9b1e0d +generate_keys ca8a2f621cfc7aa3efcd7ddf55dce5352e757b38aca0869b050c0a27824e5c5e f59d75c0834df84b4aae8c73523388c4eb57c125fdfd0cdc52389751673d3902 +generate_keys 9aa4e062b9a48cc714439b4e47617a9d5fbc2c228aa82374c44b8f1ef75dc073 04b222b3c67335aad76448daa244472f40f6764d851cd047e3206d8ac32c0c07 +generate_keys 8f9e37e637eb0d03252c1529e9094523ce3d943bb7166b7bb33e0d3c528be075 63ef886fcd7cc534d1e141d64f5af699633c6ce9eb6e4c104ef03300f150a901 +generate_keys 7d04eae257ce0b0fd73b6f829aaaa6096c314556f10b5b2c9a6c14744a2caa74 e4fac60ab7fb0c2c2c32039cfba4760c6c22a5c2f3c437c50a43b198d817a601 +generate_keys 7db81818126806d012314d9c0977b6766e6bf8fed716b939d4a13c0d5ce0fff1 8b57cb35f1a0714a904896ce7e9a515d7083259f1d325355910c5ef6c79fd90c +generate_keys da2246c24d068cf260d6ed03049ea48861cd62f20aeb6d4104d3711594c91766 166f341e6174d70a5347642e13992828e70b155768a4f586e45de094729dd10b +generate_keys 64a247eef6087d86e1e9fa048a3c181fdb1728431f29ba738634bdc38f02a859 dc84b8cfce597f499ffbd52829f4dbfb3b110a3482b168c223bbe24cd5aa4800 +generate_keys f3251e2a54bdc6226db4f2982a0ad7e7d5f60171014f06d080e4664fef49d16f 3d7d972adc1b1279f71a6eaa4deb8c05a73be74345c7e862ced54822be0f7309 +generate_keys 3afa5bc3d1316591af2a0337df8ab5353cfc439851a547fc0d4dff53e0a1f0f7 b4129d530f956b59b0bfdfb2036f4ae189cc96c9086bdca921cd348bde327403 +generate_keys 8d857a5f44109593c7b859a168bf7d73cb3fe5dfdd75adab90abe6f6293a693e 14144a16940145ae1c8ffc09cf5cd15f640d774bb78b95b756de5d7f9af47606 +generate_keys a3a78058f2a52cdad57c4d07e35fc3e6137e225848df7b4162afba93474dd007 c9e4e8fd6916595ec1b92fcefd2313df304b05856716c8c7ed6b5589f0f72c07 +generate_keys cff0c7170a41395b0658ee42b76545c45360736b973ab2f31f6f227b9415df67 7b12882ce5338bad3fb9245e65254a8140552f6c89e95a799daeb817d602e109 generate_keys 14565db661b4d3c24df2607fa517e0176d15d1f6be4c469f8b1a6fee0fd46f73 6f407d2247d035a94dce1dc64077dee8767960630a94822c5ee44002c610e70e generate_keys c2b496447b7c5a05499760171e36eca65959c6bddd8f51d5f165ad8451274700 cdb7cd68d4213a845c5a6cf909c055a889c558b18839c21a891705b6bfbd5d05 generate_keys 31e48168da03e468e7794fbb3e1c026b53e2f62d9034e52ed7e1435ac8d7ef57 703f93f1f47b7491569f96367e1f18a321c849d7e083122caf903d28b3dc7904 @@ -866,8 +885,11 @@ generate_keys 87dda05d85a76f76814ab7c069875892dfafb68649e16a5706ba57a4eae30efb 7 generate_keys f78d0ba130522ee4b2e53c0ee547a4923fc2666dbb2567fef4cdcfc5c427deed ecd3d3bf8c5078362014aebfabddf2287f961a59e2c30404542c383a1f9dbf05 generate_keys b8626776e5277e182f432f9bb1735ecbed074bea3e842ffbe736c7fec5a9cc8a 0d74c9368c5342e4fd27d17b1048bc8b31bc45864699871fc54884a1da442604 generate_keys 9189676a8a29486b7b910cb5114c6fbfc2c3693fec52bb3fa07acbc8bada4aea 247061ef4faaffcbfc89fe16f4f4285177ac4a0adcd26b46e10ff87a1f6b5b01 +generate_keys 6e9fe8a457fe8e2c2013ac83ed542aae68ef16a4fe87ad23661bf15d0616aff4 d73518ee96870788fc9a1942508d21f9f24aff835c67f70a5b439d884120c60c generate_keys 1837653add3f972d755fc9f78274e7f77d6c4ca59fdf468ab58c34697971405c 686716d5aa4feda8ca873a53311ea48e2b4a378dce95ed72ce050f0545d93305 +generate_keys 593a60fd41c6a4d5be1316d08c7a491efab86a9bbb0588ecc58149029cfab207 24a25290ff10f81469ec2e362c1accf27ebb4f2b7c3671bf45588284f22ced0e generate_keys 78437f4e3619da94f50d541f3fdc24087fd4a9bea64101f4fa00c242234b43ec 4ee58dcaf14a4ace28c69e5ed88c67c69330a0d0b7a15337003dc44fc2f4db0c +generate_keys d6d531dd6a4fa9145fa37995c81122717ceb3b79563c2eea17ec12b544057a7b a64c8c8de2bf5a19f5559aca3087885411772ef0ffc0d8f4337142f82a36e405 generate_keys 6176bbd85b15f6d2cef74376d248250b482f4649553a01f9db323c222a41580b 5536ed1a93335a0dba2aed5315c7b51207f0b78994d2bc5d17d974e9e7904c01 generate_keys bca1355ca382dc99c1c3dc0cf412e8f2efbcc23e0cc110afc764dc4b5e36e9c2 9c8072a9583c9778cc7dd9e9d3ce1a2c22c18af5e87d14801279568a1acf0902 generate_keys c974c6be8d21d7760719f33b3f3d5c34dff1dc1551f96196978e82575dffe926 fbcf6d11000fab789e43e5c454f286dadb35fa652481e845516a157285b9790b @@ -905,6 +927,7 @@ generate_keys fc4c1d2c1e8ebe78b301cb545a0d2099466b5f5d6793e2fc9bea6b94b2952a38 6 generate_keys 84e8a4694e36749e1f95b169ab0c1873b81161ddc9bcf6f3db1df45a60973c88 da963600cb3925f146650425dde10cd0cedb8d5fb31b4125dbb780a151bd750c generate_keys 4c0255d6d504c882ba9c70bfa008fce37b64bf6c3f0e28766516a4420ae81e26 6657308e8186f157d0cf3a904204a5f45571bcf2ec9cd447918edd483b57fa0c generate_keys 73cff6accb2477274d9c7be7ef9acd6af4601c451273846dcdc18009e0ad2438 9c0f09a10b8a612e567d1b7535a9c01264550d36b08a0e3f23f540de4e94a607 +generate_keys 82cde0c1473dd7bb6c030d58760fb4d4ab78b22cf160817ddcf94ed454e2c9a1 9b8233db7e14c5dcc03d4195f7c596fab68caf5374aa9a79c879b337cf58c80d generate_keys 50a99c17fd6ec8c9a7e2adda758e472f3bf333ee38e8679fb650dc61823a8cb3 06def25052d92ded8abfb5797daba02b61eb3d30df2542e1147906f170fc8e00 generate_keys 7fb9833ef15b807d0a8ed849ac64ac3d57fd0622608e4726c9da0dc1427e1b9e 3526b4818e2efe4dacaa9cdbc6aaaa2f6f4ca8856e1f24565fa7cf20570f240f generate_keys 7003d681d1bdae4e9c0463f97f04d103e013546190453af9e55a1ed5dda89be0 d3f12639669bd1443c08b44afe51a31778ad669daad197713e74cc8b2ca6f80a @@ -943,10 +966,13 @@ generate_keys 39ba5391b0ff5320950406ce07d88e63eed0fc5e457566eafd735438aa0c4763 8 generate_keys 02171a639079d57ecdf6d744bf089d3c7465e69f57603a3935a58345918c3d3a 92fa05e212d1b7057eb90d65bd2ceeb0c7fbc038d391ce1137604d9ec908630b generate_keys 4760d333319da5f9b83d76aad3fd934bfe7066d8dadab26bc752775a5567628f 4cbfff9bdd253172badb89ac8a8430e0d1f8e6869f05a1a7dd73a1e4352a1708 generate_keys 7ce83069c7ecacf944dc818a3679bb8580f2fb0016150636e1a7b51848056e00 736495ac6065995dbf1c9574f477431b912d412e6d54d8bdd3e9becd826a0e0d +generate_keys aa9fed9d7c5d2ae29e411b14a050b01ee12ee971d351f5ab96cd77f95eb78739 40db3b6b043a9e7f1ea8b02137998332b7038fd500682f552c818f3d36d8a708 generate_keys 729f7bb3f7d4e9d626929655fc0616e53abdd9d8131137c2582473b749e1e0c6 125d58bfed4f4b39cf9ffe3827e837babcaef948409cb9b499deed35bfab8a00 +generate_keys 013064368a149fb0108f10a0304cb0b40ac31bfffb7d58153e54f9aa2bc515d1 02ddbcaaa5cccb808212ef624d5a296d6b1475b737a542577e66323053f8f80a generate_keys c8d4adb5355a141bb00600579fa12523397ea283cda27dff8800d34285612859 b4cadf8f6607c0c467dd7795008fbf4311658b3fbcb2b3c26d970d454174df06 generate_keys 078c902693d54e9c6fbc14aacfa9f324415402f4533fb07345b698755dd3b8e4 248f95d8f173aadc378c161f70d324efdd3439907acbedecedc5d025e2d91e09 generate_keys b0ea07474c878e538f7cfe261099db55cba704bcbc5a0fa59ec3a2eba068bf04 a8d7990e8189631f82692180369e2893bc02a9a4dbf15fbaf616636db068c80a +generate_keys 6359cbdb328f49ee109d4dcbd19a8caae0b8949436e2ef06db1289e61fad66d0 4c77970ef160b05f7d93f021317bebe82d3c184f81cbe90bc6fc7cd27a08e50e generate_keys b56953cd97fab52e8a7b5715776f26a25722dc05d474801f5f51bb309d7729b2 b8db3b576fc280c644de282d1afe3dff6a1c792064bd95ed6b65678dc0877d06 generate_keys e0e791293fcefc5c6276347bf8cc3231a52119e66f36f2e4d750158a1f4ce7c8 d3a5f8cab604960fca614fa407a0b3d15354bb9770939d399acb96353365ac08 generate_keys 0d5774f9fb3346f4719d4c3b38760d1f3f700e3d74cac45f96d8b5cfcf014c13 74c691b430c940b73e655dac205f21da6d386c62daf0b1886343935205aecc06 @@ -960,6 +986,7 @@ generate_keys 0c52370043fa3a4d3f40e0f95f9b7ad0da5f5ad4104f49736847e6971b09082f a generate_keys ec87c62cd426718595d1ebbc43370b0409121e8c9d0ba5ceda8119a505c3ffe6 0993d873d5e694427bad3108b48a9182f111301a7daa8af2860588fbf4e6a802 generate_keys 1dd166362a3bde3c56b961ed3fa7694db8325f81414893c903e435a2a44bfa4e fe83a5b475519f6d0ec35fde10e70f753b76e1070a7538196e7ebd6a4d5d0506 generate_keys 0d6bfbe8e454c19a693312f55a87dd720a94e0bc00f09aad6292ebd6b5ee371b 286313c582243c4cf636c02a8cda434ebd86eb3eb8d532ddbdcd498a6850ff07 +generate_keys 411e029fca2f856f1c631de7f69e18cf197f526c160dce37c050f7951006176a 76306894fea0e7140fac32464cba705d022237ae920a3f53804d1582db98410c generate_keys ad6d7326d9cdc0337b953f00151c6d4533c026fa0a6eaef57f4065b6beb3eb11 38d2f407ffa6c9c158f6f8342aabe612cab54e276c7e66e00bbad6fd6a5b2f05 generate_keys 530824d34e0da18eba2230acaad119317f213091c1a428bb1c8308dedd00068f 7a7eb057281f3fdc69f5c80da07ddf15e6dfcdf60e4f6eb7af7bd4bb2b2f8509 generate_keys e67712f62552111211414e0a080b51fde0d521507d1cac94a472d766beb08192 78a91e5961b5d7ba0bd334abee6f670fef5683fe0b3ddb3b4871f2c8bdd5d30c @@ -967,6 +994,7 @@ generate_keys e8de3c189f345c2616c48bd8969ea74d3f6471e2a30c33ba90b943c52527466c 2 generate_keys 1dbb4cc7afea96bfe782b747a6d86a9d944fe2542bf12bf9de7f5bd7e8913547 e98890f85285b4bbf02434eab047dcabb7547b9a9e395b13fdf5a01b7f9ba808 generate_keys 172f3d7f2c2aaf1cc43679981cd1bade2af95d8aa0cfd56a36ba9b6e778ceae8 93c72a1f7ca32272bb41120f1ef5dac3ff656a85b3e5b2563cf5a3749008f50b generate_keys 627dbf760b3159778aa35e1ff1a7b580aca57c77d3b1374dd8773f3b99f34cd0 7c6d58e466f728b78df5bb53eaa8d1c360ca486e6c5d20e869a1703e1c4b2704 +generate_keys eae948788ee0013f51c36f4ffc8bdff414df7edc773db4bd8aec318145cc1fd0 79e7035e58a75caba395221a3319b8d9c851b0c4526f8e9e0dcf5cbcf0b67201 generate_keys 00218cbcbe7e647bc23c95c5e1db95adf0b796d749896da45635124377345b7b 51f6fe7bb3a93c1805cacf222271fb59e7abe48ea122d1e7db39c22d0e693f08 generate_keys 4737fe0445416507aa37539c160ae2ed536c520c523a19369bb4f4fee5b85b85 11532fa7590844d1a9d7a49c29d351a42c14bf7c85c0def65637de146cecc000 generate_keys b44afbc96003f1776485ee8e094bc1d9715efee50f54fad4e4fa701c2c7f61b0 1e43234502a6569ea8b119704eefbcd8196aad4fd2323bc7a1d0574f6cd1ea0c @@ -997,6 +1025,7 @@ generate_keys 497c1259c6f4b963ee6d66beaadd17ec250f2b3b0e6ef10839d6a5fa473f14ee a generate_keys 685dd1514e5b3201083086bb3a6f3232add6975af2d613c11035a4e2cfb0012c 6ab0feb875dd4a8b61c12070057e388aee3809ee8df299e20a042edb7860b109 generate_keys a9d3c7f8c3d47d49ec014460c304326ece7809c979cac0538107ced8e6b538b1 9e2227f40cbe11c4882cd5d5d52f0fd3ce17440d79d4bd6170809113017d0c06 generate_keys e3d46eaa9e91834c3e93ac494f222f45afae96d742636234426544b3226cd109 beb5442cb4b56ebc80a6ad8dfb4f59e9036b465db4eee84293f0107a2308920b +generate_keys baaa20d83bd8829e7c5d1884aee8b8882e701aebbefd6c38e0d6160abae1b021 8899f9ae9dc3566b60246a53a2986150844f08d2c2290844d8ad1c6cdb672a0f generate_keys 667e432b6832985a6cc77c3977e1c408f32d12f1cf137a0eac9ea921be57d9c6 9e693fc3ff80004544c3f498709698318afc408fe548bf40286092ec260e9b0d generate_keys 09b3b22d4b1d5461d8b1912a6abe1f61dcdfeca07ee68b82a1aebabf5a9fda58 094fdb8c5278753544877e0c63cac4c6268b89029e6e855141845a2ddfa5bd04 generate_keys b8db2d557d26fc54a4f06fee7b27578504721aad3ddf12e7e087c2472095f5a9 032989e40d1c51c132d04e69d497097dd463cf97a54509d06038333ed015280e @@ -1004,6 +1033,7 @@ generate_keys 1e0d777e3e35f8c2577a36e7f259745999897803806d747264fb0dfb745b9a2d 4 generate_keys 70c8740757549c8bad55c6c4297d52f02ecdcedf867580eb5069306df18b1606 fa0b21cb212522255a98829af7dbd18efd7f6fcd049f70dd590d982c1609850c generate_keys 511cf9f41e05bf81b34f5ca978efce4f484444f9ad6b901c7b9a2301a8bbc8c7 e7f4fcff0cdbbef5ddf59d1992aafdfa70e00ae405bd322c2d258a392b345c08 generate_keys 8e0757f3ab2147542dacf8aba416894dd6dd514f04bb57adb4ff0be8e954a4d3 4e68d261da8ff63b55f3abff9e10dfa5d974e79d9fbcd2673b9e997a8e770f0e +generate_keys 8fd2dc3679bb6cffeaed8cb3796a014c7a1fcda7ac8b72e3d2798eaec11b22e4 8342806f47c3c3212b6ecfd0c81b9c0eb1732f1a1f318f94000509e3b2320505 generate_keys df151b966a8ac6fee74d85baebfdb9867fd913283bea5e199c908fbfad97241c 05b32b61deee7c6743a7c62cbfe40d8820aabe22883f264021d924dd50598303 generate_keys 57ec524745ad184a059b40c77d64b73d4b6368ec13c7777f983151b50d638ae4 19aff48224747bbf8d34603e7fbcbdf2ac5988a32bdd87a01fa2ab112b010104 generate_keys d081dd8388f51ea462669d14930a19e115c8b8bce8948c362b210d765bc27be6 d637b6977bde5fb34d03f2d1dc4c7123963e769ea69c70a3a2a1a55048d54109 @@ -1026,6 +1056,7 @@ generate_keys 9635fbfd280948f00e3a8f08437afcf9795d25bd56c6c1d932e78d74b06de8b2 2 generate_keys acd801fbd2ddf6a7458dcdcee9475c91cf99fb0cb240c29a316e1ca2fa37a0d1 914eb553f9ddb51fbdec59c7cbb9f87c2509739947db32cee1c30d5f6154670b generate_keys eb843677a1586ca2c60bc5681f4e1898f8f74bd5f8dd503871707ddb275166b4 c679b63a4217e000255d6d7107fdef6d976db30334e1e7059eafc15c5fca3b09 generate_keys 33c9f09bad0b13d00ef984335a1bef40ab5377de6f221c9663603a95b066eac8 c44e6d257fddfbda3af3c8834511973cb661b3e7002908992cb9992e2d97b802 +generate_keys 9491111dae13afa554a2a5932b82bd69e2d0f5e52a3186d35cc892ee3e74fbd7 1dbbaea3c72a3e88b5bf56963304abb95e8a6d156f8f24bcb98c433be3012308 generate_keys 3bf9e0af1a4cfcb76410cf3088f67671e86082840a9207c9607b892618855428 47f5b062568e836719924bd1e4fe2c9aba9c355d4e303c8a59b7abc4eb309c0f generate_keys 43fc36dd5710c6862d20ba045730a32fcac8f263ca5b6896f83a1adb63574a2d 808c71423f6287fc1a79d3ce70f0925e03d6dfebf8ec0238483efd6f6e6a8801 generate_keys 7095c419f290911e6506600d91f1c4cdcdbab57a3b8cbf7f8f2c2c28ed1ecff6 e3f060245dfe2b1e04422743e4d12083369cf45b6e0cae916b7b0371508e0b0f @@ -1036,6 +1067,7 @@ generate_keys 669a57b7146dae91840f394a25e6bca109cfa17f21e42c442d5faa8a823152a1 7 generate_keys 4ce60531b8cb519cf2a78ac06daaa17830b58bb535903c28111f4c00be925764 a55eb1ab79b8c75ce902e9064ca0ad3778d21337dec97bc92a574856b65eb30d generate_keys 50f71761a3eeae6b534e984983bc0549442ac92335794c7b07a7c073c0b7d38c a9f879e65598776e7d91558abc53ee124952355557c767cd3a4dd80cb44d2401 generate_keys 389955b3d095ec81c36208dd641b40cf203f7241c665139715d902fd002ed26e 60f3072e7f6f7d759d5fb403e362091ecfc42298df047bc781b073ab4cad540f +generate_keys 8d73e4b8829f0dc2e82eae9ae25b5f363490cc54fb24ff154eaf11f31c84a15e d36fe0cc948a256aa82dd7b1cb35e77c9072c78819e8b2f7449d549b54fba305 generate_keys a565e6f6d965b9d52e4e7b5c5fae99f9180f8156c9414892228752bc9f77e797 c6b7f89c0317aa07ce75aec0ae9fd4d3368406ef75ae9ee05bcb93272df87a01 generate_keys 8edfe959281a6c723c0526442f2dafeef42ebd37082e7ba6255f6c0a55613e60 f512c30785bbb51bf8375798fef078588f75fbe83720dc9a08594496c48c780b generate_keys ceaf8ba28373243b75bf687d11c7c2c98ad7d5d7ada3b43a9baa3ed9feff20b6 71eb92bbcb7148a2d95eecfc8261f3f6ff11e9390100c60282a60ea14805c209 @@ -1071,38 +1103,6 @@ generate_keys d81d76ba2c93b2adb371649378bd7753c4eb8142d747a7594b3a8dae69b0e375 a generate_keys 562bea1c058b5678aaa558776122a626ff0181167f57f78f25160e27b7a3e20b 6e9aaff46815c66d4c34e8cb3c9d4cd39c2601936c248ba5efec1cb3a7619108 generate_keys abc4b53bb43840770afb0643ddc423890364863511927950b1cf8ad39c242a64 ed1c7cabfab7eb0511dd82d5bfbe93eef17fa5e324d107901db280c46fa8b105 generate_keys b3197deb5a1d42a1b8e60d6c305618b7768c4c1fdf40fe9d4d89525eadb6ad6d 56d5d0122cc5246094338d9ee1c39fbdeb2b7e541dc46b79b7cb26e11e9bdb0d -generate_keys 338a5e980d24a9425cbd090355725975285d7c4c4bf869a8008e04fb55657ba9 6cfbe833b5621ac5085c60b27056f982ec1aecbf9421ee53fc84ab186738eb08 -generate_keys 2099d8091ee581abef48157f6de3e12ed8bcd4b609aaad357e1b228029f5177b ade0e6103101077cc4fc930d84796d91d79d53a126aaadb24263ee1fb17cc501 -generate_keys b2c5fdd235b30135ccc5be70c3c22cc2f93c7570550f79e1f7a2172782b8ed8b 2c45c6b5ff95a6787197a5b10c71311e773d57fa25697516c8cd6af765907f0f -generate_keys ad3eb2cda5c708a3f3374c1b8b143b2cf98afa5de266a552641251455b4cf0a9 c4483d14855aafc5d5c228b4a98da0656bc6b6a5b29ad1da3e34b8210c248c08 -generate_keys 6ff21081a27ff16b9d8ea2ca2b9027f98016a47ba29db437d8f497888a875e5b f78f6da277de036296d5ac65102b8dc519b80c47e2884805f0cd880883918e06 -generate_keys ab310aa5deb6f3e06c4b7fafe5074576675c290fca8342537d309e69edc4ac10 1235768ea6594cb5382af6e7ea8b3f2f3a35ba7f6410bb4d812a4dab9793b302 -generate_keys b94d4bb952473740d23195d4059794dbba28f371da9882f357b8c97f6e508c9a 0c77805834933bf0d7b592fec876ec5faadd05fc6630cabad7145322f5974f05 -generate_keys 5456e980a80276df07ef25181b7241132f3d81d036b3804f876e468a9243fc8e ad5a20b594a1e93d54948d7402e88182ad056424d5193b404b494a74ae29770b -generate_keys 2d288c9f5b551bf525ac7a3658fca13de29da34c6dbf845c98fc7823d1c45687 d78dddff8fe4b48943f6e0ebc7db1228b2af22498f19a88e5d3249b35831280f -generate_keys 6820655049885d86f6cbfe99599ea1202b21b7154749f030a875e23789fa868b a94ed2d9f358c0b3cda2113a28825708ecbf7e51e12f5eb3fd1f5e9fa8792a08 -generate_keys dd1dcc14a48f40f9ccb8eb21187147ec69d26b863e43633fbc4a4bfc103c9050 ce4b8b954babb07072db3dd628c2c56ae5b3bbb8e49efe0ec9f290d55fb5c404 -generate_keys ba413857badb50fb5d061080ab8d98c61609d44251630ac17bc372832ee91936 ead0d08793a756854671c1d526b30a2a37a31e62ff4e1d6168ea0d3b804f770c -generate_keys 922073a150f80d0ba376ab552131a72c73b95a9c81f9dffad03112c5801a7d30 63e122396b196c4af18b15c724123b0801d0dc920d0071988d4c3ffe6828d007 -generate_keys 02321dcd8751ed49d82db9e72bb347f393434d057a4279bd89045ab68aaaf7ac 0feab9cc7ecf85d27f947cfe53c390182fb077b4d52887fcb4d3e86257311b07 -generate_keys 55521b8de00b9ca73e83b015e26dd2656b31c608c992391bd556c9433eaad627 5a042d48700db6b4bc508975d771002eabe11ad36cf634a0c3eebc963d6dae09 -generate_keys 0a57cd9a3c5831f4c291860f81e0a85ca70d18292e4ecf87823349f9cf6a4d36 28fe6d3b4f96ff0763b7a1c27bcab75ac0cb4121203ebe1044285254b0f1c705 -generate_keys 72d9e8a001677654dade5639673b0ab22fd74f4a7b7249049af54991084499e8 47331f977b73c8d3f504f6267c4fb548aff45ab474624337df08627875689e0e -generate_keys f223f63cd65695034890fdf26f68ed0d4e0fa6388974993fc46ef4193f3e4a04 ad6d6b470eba69dcc558856cb1cfec5d7968024372f073d1fbd5b70ef99f7b0d -generate_keys 3efd50e50252c3f97d58a5b59a3692bf07f5107f3c78627c23918dae0b134d0d 7939b1c0b51b4ef008f4b0f408e2e4bc12d25d16a5015ce3351206e263c0ec0e -generate_keys 0d86f9c06a59f65d58797b0da53a6649f4dae67775b49d7ab33a840c015486ee 300f0728eec27699be487cf1213776550c5c57f49ca05e7cca6c4be0eb3e7803 -generate_keys 0a2b1189e1382f89ef7ee33026df16f5e73316d6c530b55327327cf7e438dc93 2ed988eda95ee210e2ce5e717771410a659a2b68e199d2bcc3fe26426c52f406 -generate_keys 84ed9a78a1a9540c56598b2a53e1702567f0ef1adec43080f9453b9537739be4 5408f0126dcc80f0701bc882832d1eac6f5babb4fa976ce87e60a32b437eec08 -generate_keys d65669aa48c36c7decf86f120fd3078848d4568d9a337a9e8af78de4fba59039 7955a64af6e4e4f7fdf24668088666f8df8b793ea77d3e1105f7fe53a4f36909 -generate_keys 23ad296961cbcd310e41eecf365a95fa1183dc4b1d892bc4ecc29327bd273fa4 e7bc1af4b4703bdfad76174e4329af1607872983eb4249477d9323646e8bba09 -generate_keys ae6b3edd13d95cd970dffe4d997299258b9d77da7883701c5e7baec6a2b60787 068ca8e94dec061db5539c1d8ec8e2d53f6b249303777e3e45fc1fb39153ab0a -generate_keys ce7a204cfefdad743e2dd1d06eebd6333a150e0ce7f9102973a279f650e54b5b f10a00f97468e5cba6a79f66bf2a3dbc0c8500b56ed7c98077c9f73f2a751d09 -generate_keys c6224f99251225aaa138d756a5715ec3b6e64844cb09451d85340b88cfaaa812 5d14a4482e4d15db92f848b5fdc8076e97b74cb52d775d3e9f6f1e8fe42a1c0a -generate_keys 4f4182d8ec8c7e0ac7d1c3ab41d79212713a0f657b82d7054cec14ca71c96315 9442c2aa1710d99383d4cf48cb2f89f986699d426c5432bbc8bf0575ff56f500 -generate_keys 22a110dab4e71cfcf9a2d4067c74e37de177201e28ff5411f81eabe34ddace51 83160ff6147970103181024292dbd1fa96c070311c5c98a0f12f324809fc020f -generate_keys f188155d50a8f83e99115c1725e5cdc9466c47ad17f7a8ac2fa38eac9cd46013 26d0f8bd1eab7a3dc9a701457bf9f107cbdec8f38bac901af8d188c103968904 -generate_keys 506bd71758bce525b6e4215e8f25c9da13a32222262e70aa2e59464487133c61 83cf4c6b69e53cbcf20ab0ff28a7cdc74a1ae6477d26c062af12fb096f81d809 -generate_keys 9436667a054df080fdf7aa01d54bfe3ce366433579e2451f79fd17c91b2a01ef 40f3d7f8068ee7b451e02305f8f192bf86bbe6b3ad7c0138a77a55542440430d check_key c2cb3cf3840aa9893e00ec77093d3d44dba7da840b51c48462072d58d8efd183 false check_key bd85a61bae0c101d826cbed54b1290f941d26e70607a07fc6f0ad611eb8f70a6 true check_key 328f81cad4eba24ab2bad7c0e56b1e2e7346e625bcb06ae649aef3ffa0b8bef3 false @@ -2547,262 +2547,262 @@ derive_secret_key d76fed84dce0fc00392419d6453cbe540a5750ab795c74b32309fbe000de16 derive_secret_key 2a300cc0c66bc939134a64d0c7647667ad2bda2c9617215a082ac6312aa3ef49 517850 bd18c8e907248bc5cf0a342af0acca3c22a8595aaf37280c61f0e4d772a9c102 4c07dab384e9f7a69789974fcc1296a64c1093c770413d9cc1b0e73a0e55aa0d derive_secret_key cb99d2c0c11ae7c9274de77f8883bcc78d5ef2ed7d77cc024982ceac07cfa2e5 900712766 3dfd746c1318742d7f374508d2131e5da8117f79d7aa32dbc14788933781620f 3103a53e415e8044c484c8e38a756601d18c092b0e60cf20f833aad242feee0e derive_secret_key a2eb32b0e18438b8fccebd078c7a8c419d76edd7a163aadd80d9cc1097da4845 683919645 8fc1e8972e4a8e4d429d2116c2993c3a9f0405e195d9db1ad6ee5c130d661b06 8287a1a390b3c726746829c8e6cecc47d76203b50eb8d4ac58cc43954558550a -generate_signature f63c961bb5086f07773645716d9013a5169590fd7033a3bc9be571c7442c4c98 b8970905fbeaa1d0fd89659bab506c2f503e60670b7afd1cb56a4dfe8383f38f 7bb35441e077be8bb8d77d849c926bf1dd0e696c1c83017e648c20513d2d6907 82dc395b7fea160c4d3a4825da723bb91679b63781cf32d1cbb9d6e9bec92d05be00246f1162b8127419f2e9b02692a1911ddbff68d2e5cd0e0bee60f12f270b -generate_signature 2ade1389a860c9249a42e45d32a9fdc29286c8dc0c8ea1216ba786c74517eefc aa2521256174ef6566618a6468c7b8a71ce2dca398be2290148b813710d12f7e 344a6ad0374b6ae8278e3f226d58e8bb2796e89141eb0ac37cff8552b158260e 94d0f3c942de0edbe8e20478fd7e82c6d90a34c0951f55ba120df66b1ef36805afd4c172b88b9344a5a19c76ad2737803700280a7fee8c304a0174168bc56508 -generate_signature 5e91901e800a1959b4ec07a2eeaa3a9b28893029a26e8ef5d13adde490e5df91 486dfb4904d81b1bdaf865dc07ff71145d1bf8a9e0c160b9c817315f6cb30398 6a05fa0a97e172c9a8f5d2e24851ce87bb649a46c34b33330ae71d0d24a4e70a d7ba7ce64bfaaeb85c8a1ccf038069010a0271f903d5991e331864be958e0908e9e2cbff97f41c74fad5a692262fa89c9608208eccd7864d88deef7062827c0c -generate_signature 03e29bd7435541f8b12830d68fa31a8976a7a2750e4942f36cf834506f3f1b17 3a9768ff7593e63e615433567e9f2d7167a934a9afe7fbe587f0502942ed4ff6 b1b9f0cdb59910598538708eea87ad5114ee9eaae0bc5c62907688d6adc60004 9fcd306d5514b6b598880fa1d529a4fdf6fb7dc72261db0e40665ecec8ecf702f638fed709d71c1746974053644e12dc80bc32ce1afa17f1c8277eea3ebf480b -generate_signature 5eeeeea12358c1ca323e2bb7f7c4f4d4f64082739f196e5505a116760af245bd 238cb505dcad22cca64e100b9b0a3133b44dec7a6a0abe198f6563a1df53a3cd ba2c8fa6cba86ddc91f810519281abeb43554e43ade7bdb3c238bec0bdd12e01 241223aeb98514ad177e864ef93b19b3746d2c75fd3f75317f7c2822ee3e43031ff189159e7dcad21e1fc9c7af603dd960190644566b41d249f014ef0c367909 -generate_signature f3f9ac0a5f5323f4af5441d5700f9d520e6cf41ea441ba045716b591e743cad8 73c0232f4a68d26578542b4d1e02287b995aef419200f78b3294f1c1dbf979e8 9758db88417ec357419b8e7ef2a75d2c65bdb800d27f6203aa54b5609955780f 4cd6430d0e115a554533d9c34856bb96481108cbb8f4d51ee696a97ac9668d0c033fa070873c8d51b8b89347f739fafb93368e6be41c8504a388dfb4dfebe70a -generate_signature 0c85328313e78623d436a1e3cf04ebadfd0178d4b230004469ff8e995fde6d11 c1429c3c7676607688349d6727135bb442466932035cda069ab2463b97387d59 1e58bd527b1ba1014ad772c523700434791a4d4c005f2ffb81fddfccd03bd304 5d0bdc0ce2ecd4f56a70ee6d4d3d794d2d75663be235345a08eeaed41795eb0747ce02e226dd4ae18b10d384dd2b20ae2d331f0a74e22670baca1b777adf8c02 -generate_signature 9d2ea8622e9d46d17a568571332f14d6be90a40df1aebeec20f8418a5f94ab55 f8b705820107149e4845f2a3f3bd045a55944aebf35d1104632450bba4141db3 d699d458752018ca3ca82433e6fac7547030990c923e4192978ef179ad4aeb06 f6ac03c0aeb13106e3c7d892a8f8cae495958e0ab45c3b73e15d3eee0c97f008cbe20124ff7c1047281e709c420eb68af616f41c7edb54945ea7a03b0cf4c700 -generate_signature de9240454aec3ea2ae6e13ca9457e43003991b9d0f75a4b25117e1ae66617ea2 ef02c34f64ca98543ae5b871197e413a7ccbc008f2366a2552f2cb5f852d8ef0 05cfc28bb9cbe0c4d02d8692f40fcc864665b118064f1bd31a347baeed24240e d9b53a764068be0a0ae22ede9f6aa109e5cb5c6a01628b6d87046c259cca1906c60db6d383b94811f45445da4fa33ef1077f9d59f37d96057f211e773022c005 -generate_signature 806c083034f5bfc469e68a490f512a3c6cc3d21d043dc3f9d4941f5a9fac609a 65aa36a44250dc1e5985676f193b23119f7d5f9e0291e6643fa55ac70b1ebdb4 5f5cbfd6c2d5e22b703db1c64163cc0bb674a1bad00cdc77ee51001af5f8af04 3449eebc2f98c2ae81ea1dee4b860693f2af5e9806a935d9d6f40c42beb5ab0d40b73ba8e77db1eacaea02bf1d342b5f9ebea4186bed8a961f56cd4b7029860c -generate_signature 413c368f9b4327907fb6bcccbbdc0540acab58dcf1a860c98a4340448ca4ef4b e37df5cfb5f8ff60b822d3550bdc498a7557e1c11d82a035782931854b996b27 4dfcc2bbad477e1cfc6076408fc85ff9f3381dbc2ca5f6b61b4423a29f372d06 a3b95073fa4b6349e116b8abb70b31afb53de79472e0075601c43df0f6a3f70116fd4a6ab88604e4d122638f4d0883bf928adf466bf296a38fe72a5e0f8d5207 -generate_signature 3932123f3c95fdb21bc6fc74042f39eec45e588d707fbb7036983f3597382bb4 8b0138170662561e8b4e32f0821d21d187c62e7939903ee747aed3b1bb277d2c 649ae266093e93d0968f31ccf4d0b9307abf1e50b7d2b811f74c9b83f58ab00a 2906551a86e553adce26514dc6f935d02639962760c4e040f0a14e5d15d1900220f02fd7e6ec00a9cfd62cc62253508526c17d80a58464949cb167d6caba950b -generate_signature d17ab44dd70f88e19f0e8ba88a413fc18b2a1ce0ed712d6d110a191c9a284236 38bba4ea01a82f0c16f36f4f3387d6015f2c703cf211f6b73936db53645ef2a4 9d7af7de69861c61d3c8c6f2022e360a6cc8a647a4f3cfbeae6c14f46265da03 132ffe4f3b2bd28b82aae9c202c556f3963f0416457c6721874a6c212ca818091b7286db323f26bd56fc0462edad2a5b4dda0c8fb0a4934e30c21153f46a1d09 -generate_signature 7dd81bb4dcd4d74f4c034a4b6a51a49662ad34a2687401ca00e8a628a298ef44 5edda4da92d8227684c8e30c940a9d4f9ac51c9f1e1c9216e826e6de3212f47b cd386423bdae74357991ad01bae929b6ba9d65803d00f06fc9f0ee34a764bb0c 1fbd451cf374e11e6826fee71c4733e398986bfa21b1d956a915d99ca34a44088961bc45fc3c47d484ae517fc4381e3874800b16ee86039c3043072123507102 -generate_signature 2e8abcd827b3c90a24a65d29e229b5bf59ded9b13ca77f51a98fccb37cf9775c 10df138936f814628662c2cbdb25dd7820c310d7974bc74314131f8423880dba 0d8d845fd386f1f47c4e92d6861026d697215a396f3eb7eb76cd478dd6c1220a 15a2b65a8a9540b1ecbc079d72af458911434db49200bc61aba7cc037ffc6e0df211472b53884922224c8a6e09f9e8801b59dd554ac856356a69fc14a389fa02 -generate_signature 83e42343a638dba6fb5d78e1b9c11e236425b432c77deac3ad098b93ad73f14e 3e855c996c5341e02e85737a2b03e98491037d91721cecaa17a83c2c4d5741f8 87be0823f5741f09e2f0c1a3337afddcf1533dd10e0b28efa8b1147c9ef3a703 26d8904ca8d3266cc821d2b47f0e9706b8421ddff2d8ad45e128d27686471e01d030bbd9f0afa09aa28b331a1ed8b770b157100f7c85f4cb5e4ca69c2231da00 -generate_signature 206fef6045a5e44f87bc7acd4bdcd271e1c55cc8e42f5943f1f0b1b3dc0fcc54 9d32cb01bec30e0d7dc921f1ce44b5a829fd84c37e4304e4f910cb9df501af4b 8adec55623bd61bbf39d72e3f891419b5264af1bfaca43278dd86a944ec04c0d 855cce36bf8774d2aeb4025a20bee96ac9f447f98907387213ccee5b66763008980b2b19b8aac1984d202ec6d030916bf84cabd1536360e2601e3636bba2fa04 -generate_signature 0f1d0617a849e1266d6741e17e8fd8a4af392577ba27783ed203377cce7514af bee8f6fdc7360d9acd4a4076ddb2f572d9e9306940b39e5bdae031db28f201e4 95f785c3db1b8ba26e660e9ae8a60af20a00289d984a83c852482c505d1a940a 84cfae0ce5ac0f6de3cf01a41e321d771bef0e120c69269c5d17f3364e59cb025e3f6195270469d16b1654e1da59559e184d382cb631e22f916a813c3a0cd30d -generate_signature fc47da4563a05a5334fc291b1730ccad835e9469f417987ed3d95aa041ca2aaf 0c1ad3f7c9e60987a84a3525a4dfbec5ad69f276ecb32e3c0880ce69befa4810 50b84ef4f4178228d07435d3105b0fe52ac7d2bfd38da43875579fe65a7efb0a 7124e58514e1959b3042cea4d6483b77f47c31ec61c741f51e85e64c0a557a06057e1321cc689cf27e455061e17228b7c6b90dcace53032eb36eb761e881d506 -generate_signature 02cb1e9244aa1f6736d8aa1d5428b30658756bd318bc938d5fd704d97e4a5d9f d52a28a31f59208e4e41f6473e6bd9229ab6a809b228fae3d8f0a6bc3a34eba0 c6fa9ba77466112e1d268891c5cee9f88d091a93c06cd4ba23bd034cb3d0840c e6ab7b36f234da2d16d6fd72c8efa27518dc21eab5318af59bbdcf30413c110990e878c8fb8092d11c724b828d8e098523ad79eb7f88223f69f8d29343845208 -generate_signature 62eb41777076bd1a90450e80e77cb1aca6a5f08e2320128b704b663402759d01 8e83ce60900fabed38fb560505d26420775f10ad6729b209992816101c1c4cae 14fcd95cd127e1218c11fa267c46f8c2d5c1c736e53f8bd2bf084693d1114404 9f53cefc1a5da70b6d8ad309db890f3755ad994dd0d5ee5ba778d050c09c410221f525d06864e6c48c13232aa1827db5e48d9b0212a4c166f367daa59946c208 -generate_signature b862759c98652ca2b1522a556729103de96cfe831b3ce615547ba70c5f622c0f b7827c8b2edc9b4014cf4421f1c9792c012ac3264b15204596652d5854bc7673 1e62f5b7eb5d2bf88032c98edc74a9c138969cfee15f4cb69990feabc0f8840c 7991860be6e8a268f69d5de5292f5dd566000389bade3053790a52e39c84540e4d5b3a42d3960649adc93143f08f947c03842c01cfa98a3b3351e0f7d075e407 -generate_signature cd35a8dc333ac5f33f3e12831a0a2f9ee03c7f421dffd7900b7661e76b056f4a 31ea2dbe369fc7a4514f3f826cd259eb8baa6bfbf22fbc2ef520d98be6e821dd ae4ef972bdf190ee205caaee156166e180e054993709bed5ac96bfd80e8b7401 9984acbd99cfdc3c0ae03a8b2fc4db337318ab1d734502fa22a55dba9418220a923a3b25f1be8df1440c66d1f5ae2b3fe53d7700a935080866250c3756211e0f -generate_signature 5dee08dd0f483aa67e0b2539c362eca5c4529961935d56f661ba641e5efa7e30 be75e0ce86b68a72f7487fec4857a7a6a188176ebcb9192123ff98c49bd2b4b1 b13ba6bfe9c23671e7bbc4a6380c1ab6c61b09db58220d28781d8ecb1b37e609 1be9e2597fc39bd0363857f3b6b65e86734a9a699fa017a7fe63b70ddfd4ad019feefd8d8cae3426490a2b34d7654a8da016e1f0585216ee5558b5fcae821a07 -generate_signature e88cf519e819375fc59bc8de8ba403278999311c7490290f2f5438306bdaf339 ce99f73115a02e03f42a1b583dc3cfd5fd1747c1400d1b6df73e1a3ddf616932 0a33216348544ad80a652433253b6921c0599d3a80e4b2d4a0477cb121e08104 9e59ee9fddd64321f4ee22b73d27c43d4da50ecdaf5a41e57a732c4c5718ea0d53b0cc6ea733b311eb68e7376a15c2c2f804ba2a18fa2e3ad6adbc1a0fe30905 -generate_signature 82a2377ac1c6a103cc0b955ea1ed3fcc41bbfbf6416b31984f28aaacaf4e2a94 535ce02ef72e89bd86e0fbd9f8f0197a99b11f81128f5179044e4e6d0f5f1a93 dcf1736d7a4306330ffd64315b2122c5b55bdf115dd52680d5ce798893b62a02 15936525f24ad06264e8a75c014cdba6fb11f11d5ef1bb9e1dc4cb1c34a6cc081dcf1068fbf519f387a028a68c05dbdc10024e78b7d4717f709b0f3b58aa1501 -generate_signature b207af5749179b6f2b30f48f2e0c19d90b6655cdeeb3fc37f656321f50ab1508 b2b43995a5ad7f51c6c71d29e1991f431444b5e73019b3d36fca05bc566bcf41 b1e043b1b9f4c280f798aa59c2f62e46c4abbc65ceb17349f4d11b867937ad0d e82bd2ee59da43eee8137763be0c87d94db840743dca70e9d2f6a8dd85f9110ca63c7b62ef1f5737bdcf54109889a28f719364bc68b0278e31b4425f8e6ec20d -generate_signature 63e53137888836ef30cd6a93d34a0fa749fcd9459bafcf143da29956a6f35060 4762dfaa39af96a85ae746326693ca87d82a008b96c84792ed1c570abaaa81b5 723eaeaf84c9dae7476aabdc5fd409a0faa7c61ba8b424ff78d299b9480d5a02 700574c73278532462d73ce2bd90bd049815abba8436580cd7c0e5834033d705d984d2466d02231b70559d84ddef9e5756938790cbe93fa1e3731900543e4500 -generate_signature 4cd5f4b8cbbe735336d469aa8f2f7c7e5195d60f897b9a81b599b2e045850705 4a6dea121c196f56360e8c17273f1b94827efe1f24cbcdb9d3ed61b9a207b73a a8e62911309f4d4fe0bb6284fc4e3cf4fc5b08e3812e27328f68c2a6c278440e b7f46c071cfc09be24152dac2e6803d042f6802259f5da4c672243d90887b2007e89de8647749d9a89111bc793b3872bd4ba9023d67fb1b2eeb642a3ed477d02 -generate_signature 7687ef751d6533aca6c911d70bbc4a8c5252fbd3112969a9a40b9e1dd041756d d17a5002e137731bdad6600b5ea02b520efcd1005008c6f1244deb839ea041cc c848b5aec4dd76bd8e33c37ac65e884699fdd7b5c7a905801f2f46c56f783005 1f1a36b19a97bc4860a97a7a4dabdb70f75257d0cd69c5ffa7670ad5940dae01921a6218ebb957dba2d5c13c06aa2185116b1bb146d168436b376a1ce8523505 -generate_signature 7f75ac1c1d1832d78136ae9459b070982eb03e91fb4b686f17e60be644127d2f bf2d56a4e6d6f7eb153f31bd9f39d23a9e8d06ab8f906c716dde473fb2d16554 c901408e3a9a52cd7b95db0a8a5550aad0af38190c082f5d8228e5587367f500 8d578c33ef47b681ddd5407a631c72a78efadd74515697e2fe6be0afb1b31b0dd5064c6aaf236eafe2d1b06433dbc2cd9637e02685c3915ae3b88cb768ddaa06 -generate_signature 1772e5a77a57fd69d6ce9a68c81bd9c4f81579741103a177bf584819a656e1b2 2ed6932a62fa064c9c2e39c7ab0ddbb6786dc849286af8e35655a58cc9be79da b9446a654072103d6e331cea1425472e0782b532ba9e235d61ced65ad6ff3705 e6aa025a2ab35bb9f141ef2e7cf8b4d18aa5e83649273c2f19199b90901969064d97ae17c96b207fea4fcf4e139bc0c516a747fe927e119c2b9f3a2d1f615108 -generate_signature 8bd02d71480ae9f17e999ca813b837402c999bedc4374d416e6aa9e233818f7f e0380564647a85532bf68ecb662fd8299921c8c51982b6310b4a6806bc056dca 9df00a62dfe8ffe27a34b7bc6a24a10d7f147d620f3ed78953930fb845598a09 6c8dd0c7a57927317469a5b6351666095cbf9c234da786cc7126033885103c04c0a83dce35c4f1a43820defa9cb713c707573b7ec62a4ef36b123bb87aa85700 -generate_signature fda1233d780d70dbc9580c2b80ef1806a8ea2df30c317f7891462d96fa4d66af 5f769cca4be9aeb809e9803160b179b1a7bb7cab0e2b0f6311c9e29a8db20de8 076d27b6f85be7acbfbdb38d1b22e1d75c139072f7ae9d33bfb7590b474abf08 56d25ca5d6b9d69b90938f898340f8b8a4e575e0a247193ce8a3307178ae7504c6405de28ee8f385e7a58059f45e884a97325c27d9d79f9c649972fcd8c5f806 -generate_signature 80a147a42fa1e4f83d3915888f5c22ad71f92c0b2e1dbef9657330912baac097 add3133bbd5b10f3b34304864277b2a335b3625afe0d188ea6f60ba7302cacf3 911a315f1f7c21c97b7dd0385da95b6bb06de39796a538d909d51bfc07439700 1093f3472fa816e92a742c899f06e3b8b22ba9e9ac9212ad652ac91df7feb60f16cdf4f7ca6369b9bc6959acd7ce2271b8d1486c98ba2059342f59ebf33dba06 -generate_signature 870ef0ebf5756b28ffbdd4cb65c2abfe6bbc366408ba42940376e91c750473fa 7c91e023da55901252f69ef58bf56329bb7ab688f444df00c1dfa80916a1851d 1752295c9937a10004ca9884b676bf4cd5608e4fb882f733ba1d91093257320a b52257abefcf92febfdd84a62c6df43938ac0cec9d64d6ea6fc6739f3c6eb0060a06eeaaf5feda7b63e7868e3afd068fc669703bc4524d6de74106e7f7512c0d -generate_signature 711f84e938e19a461e28c770573ba9220b84b233ef6494e1600b432333bed7e8 2fa1edf5af46532c7d290322f145d44487bdc39de5b60d365f9d2937de5bc4d8 86b9d8c1124989a8d5704873ee831f1620e580799fd46e507e55c9dbb2bde40c 6b3e238d3fa1428dec940f3f10f7cbf75c6024cb518e72700c5feff1a8cf5e0f366eaf202c6fbc2afcc987f4e76d56f09649d250bc98e9fe0fc1d122ea70db0d -generate_signature 182159820cf794a0a367e94e6181cd2bce0aa7e8628c89f2aca4315136d58bd9 6252042e2d1f1694b73799d9f4bb777e7019ca1655c73dd79715e42f473f6c29 f34fe47fd0f4e59854e9b1740c91336978ef9b72bd07b2cbc601319adb261a05 c079d67bcde1dd9bafe531e6abdab409792a7e41de64dde26205829fa99e33056108d71a6e48e7d48cdb5a581a3d457bd3daee313893fdbfbf6181d3ed4ce104 -generate_signature ac1490c8b703addaf061bda16c13c0ddd4ab06da053317ebba240be4e794b7d6 14d9ac957f7b65d0a8e55488d62a60325caa227bd28aec1d6a4f7fdb83a2b6d5 1d9d9a3c2200e2a127b68b06b432714b585dfca9fc8bc46c44e1a7fe7462fe07 d51fb46dc7233fb29d8c183e1bea7c2c015cc52e76403c05fc0b213c4f9e5702b4784fc70ab883bb857df5f3b64cbad205fbf28946eb6b86f7c66f2ad6fbfc03 -generate_signature 6712b724736993684f7380ac03ead9b3df8c8d0f131d4ea99ade9d153671a3ab 69f90161bfbeae0720a5e0463f15814ff8267d8b742c2eb612f387f08cf32c30 74d48447a77283726f32f5e726074f1e3173d05a55b9e14796d2ea5436f0ce0d f533fea5941d67585bc65d61e8c661460b1fb5a354dc581e271dc4ea10e1a008163acbf86a5c9f9e6515ba7ddd5ff0301b690c04777f9eecab05ed6a8f2c5505 -generate_signature 2796b04ccbd3ecb7ca3182d12bce035edcc049e6eb7226977e914c22b406939c e02e549729c483263547dfb5e4280d5e4ebe90a7140c79c0eae07ddcd91f2266 a81f6fdda8b3c5efda411f6ae5830c151c1e09ddee44fcae52128e2b4fba540b fbc28bf52e6a2f2c8117c68d3533e71f4538163905ec09e7ef97263b3935b1048abc8959313ac3517ad54b3b973c6cee9854138a8f59679d27f2ef22c2989600 -generate_signature 6b07f68fe4e8de6a194532606d0f64bb2561abd14d66cf4dac2526f8a5f40763 e21eb4c04c5abd7c4b12e434560c636a9c0fb4df380a207a93321e1865a7aece c194dbf0de2af2b96b1af7fa24b4b29f1291088413c9f26ed98954856c411d0e 2ae3c1b1128a4fbeaa74ca1fc3ef5d647e3863952a7a0ee6c001ae7f19a2fc0a4a7abbbc4a846f36099f45bc398f9b29047b48d7a97cdd8d2e1d7dc4ac24ca0b -generate_signature 17bf7d4f22e2e16297c3105c5ea01f9d7210b77a02d2ddc8d9263b536c1c51e4 94fc695c28959e0d68fe1dfaea0bde1a2b11be6eede96a4ab65b68ef0a98c082 bc021c4b80b42671b43227544c185fbbd90d29b5d2f110d5030aca995d0e2905 8df0f6c7b2233d0ea35e48c8670e9eb6911a691dcc0116c87a265426e3b3e1092ec5edef5010453722f5f8b115f15e9601681e82ddea327cd188c5f0dc8b7c0e -generate_signature 0833ccd1e71d03cf19ab214685f348fa88e711bdbc6e7106047b64a151fb6c57 f6064b733fbce4e1200382adaf77d93c542a673573fa492cd4260fe28b14ee16 01f9a1785bfd24fa38af0a5d0c8324f8946b9d0583222c53772e69367597490c 4a4a9e8994c948660dc08a7742f1a11da0e0ad992ff1438d3bfab12af3dbc8093d9e9228a539e9c0281791b7b33de73c4fd67af4c04705e4aba4c1df70b25308 -generate_signature 2185872fb1e5cfada7668e6914ffc05f1c5fde38a7319e79010a8c1fc1c2bf28 23bc7fc2dcc89856410f5f2a006f4030d7880226ec512f8ce22926c569b557e1 e8ff76b5f7d00a8b8e6e2fa40b7385dd86692bb197cbf1220f3b59f00e149604 a3d8d403e81bbf654d0d3914e8e3f1c7cd76d332c7f3f2e8ff12952e5f7208056f0dfccb8cbbc22e822fdbfb5d75b1328544f1542f9df165b78a4e238b936406 -generate_signature 690194220a436c23dd6863d2b7ab4ac41cfe4ec6a6bc6843f1efd4388f37ee46 950f222148c84dbf4d586e7f7a2b53fdc43e45e034d0363883f50059302e5074 46d06e7cd27acea2c4094cbc3ead11d40b800fdde1a0fc1f687048e2b7ac8602 905a28806225128eb7d1ef47c6a27431170f5374390591f69ea98772cbff6608296be2412af3610ec8fc3e9d492d11cddff156e90763cc62c427603795225d01 -generate_signature f2f674fe63b53dbeddd1966c2b3a3fc7d647f63190bd0a183908f782ee65b60b b75b075e9ce254f9efe7baf09897c5f58263397a0cedd458ed8c123a552e08c8 5f2ef89fa9956057bbd7654f09ce75f82188214ad1c75d8a1edb1f954689310b f3d64d9b473ed27e34187713fe55193925c6c53143c1c740913160d6214f6a00bf412c34081a30e1863b4d5a4e1f67a50be806fe81003b611ef42dad9a97330b -generate_signature 98e1472b88f2580a68a4b10b0634bfdd859590805b67e9b2cf8b76be25c57942 1edb8eededb14db7343fc79bba30422af90e92c739492ba1abca24a33808668f a8e7bac4b9bedd6cf89f709fc97a5d9c7180cf8cfe36fea26f3931192366d200 151ea077a6afbd59a303864bfd2f0a0882f87724fd72f03c20597a92cec8800cb71afbb4dbc536e3661572cf637e8691029e84782ce38d3a2010215ceb48af0a -generate_signature 1ad499a52576270197f365c0268892d44b73e9130f70c5fced7746498ef531c8 2394d9a89125d99e13502169301dedda64cb38509465e972b6f20c7f098d851b fe04a7d2a3e4e8c2a870c774483ef0b122b8c01813914e59042fbb76a59a3a07 07933a6c63a4dd7838ae7757cb42d0ee3c5e807a136d0a8d3d50e9e2f4ac2205485f670eb484ca18a4297ee3d5adfe7766a86f6ece6d4e87403d65aae92c6509 -generate_signature 43038508a053508376f5d0595bd8cba556610e673b8291a322a2b9c72dd4b214 4934f2132f6ca4a2aaebe35ee547f935fcb28588e8061d7da267ae0954bcc987 041a55efcb7dd92f6fd6c2c2ae8c93af8d6248a27cf51c3d3770a0712783ff02 4c05071b1ffce1a1735437f06dd8227870a8a1e6afeea04f313cb1dc28afed06213f52a3d24b0511d19052014a56bd3fb4d3eadb73276aa2e5a010bf4b8e1800 -generate_signature 4f9141ce176f61503a42ad71e3713b8a52dc56505809c8d6d98baabafc4e9910 516aba422fc25e0533ff24157c263ec3a284e1dcea2a8a04972b00c29df0dea0 23a237bff637b3008b429465d3223cf820ca4db0d75084831ab81808d4878006 02a732f3701b59a4420ffe5c5fb238ab6d0f03032d2a855d2cfc1bc668ba630ed4273345ae0279871c1d8195a529666bd0913a346234101b568e2db483ae8704 -generate_signature 13dd60b4ae30f8146fdc6a54eb8018493742413dfd267f18bf0d21b380dcba18 8b85dd2808f4a567bbe3bc67bd11209c98fdd6d177678afe6cbc6fff19fa2d76 396068b93693fcc4afac343f8955a705d3fdcec131495c1b131e0ee9fc7ff80c 7f1208925f3c8014a5dc3360660865d6b8e57520c715868e540965e8e41ded0812d602d58d26a4a411bce1b6113bfbffb2bce4ced22fa844cb4694d4b0ae1404 -generate_signature 72b57bd7a1dc353f62cad2299a225484014343a40ce052f66ef2bdd581f3d5c5 57427bb8fc70228d84e191047948c9bcd9582c8f1b3118f56064eda938857a33 daf6de1663b64ca4fb7660058f16926e1fa33175f298249183c6c8d862712d08 96979747c606aa3dbb54d2dce4ebb8e9369216eae26c04635ca8963e4024b8054f42b97aac8bcaaed63917e53576e4921e451a9e7c9fa9daf11ac36888718507 -generate_signature 0e5824738082572ccd6c9ac2b97e37412b432a6bb4a1101234bf77d2a2a1109b 9ddbc2b910f572818af16baf273026b8cac3e6b7f2fa540a9bec2b38d2020dcd 3f4b0832a84cebc9377eadef2fc78a62f6f32576258c8d2424d7dc7669213d08 dad4f5c4eeae91ba65dc3793bbaddf37e18c1616adbb8863d306d78de10f2b0782e8cabaa9b7224f461c2dd7796ad43f0ced94f24a96caee506a85ae66641000 -generate_signature 459f98423e876494700752609232d068596b762ef006c299750cb3ff573b0104 c2fc73baa1d5897f75668116cab853bf359f156e24b19a745966512f6835bae8 9ae7c3212e6695e0f5668d062b2291b27bc45affa1f77b9bd65951914a842d05 7ee993ea51f836088a5ee78c56cf11e49d1c92f65d3b20930486fd3fda87040fd6af90fbe5a9e38efba148ab5c28795b043642ae76688629647e068c630a880d -generate_signature 6d176d259af7ee75ad769376ea2a5ab9accd9a9f4d878192759f5dadec194f35 2317f20756c1462f4d69d36275fc14958419769167eb526fe6a4f3007036d474 1bb97762a71f9aa32b2f7733f1fe1e0bfe8e654ee6f8361f83a61a22f32d5101 93a10eb03ca166d256fb193f4b57aca1ec0a570375e55d77bf5f97be15a2e800c5815b3b727c9c8be61355018ff3bb0c0abaf20332edf92fc4d80b674c7b5401 -generate_signature 88989b9365a33a6a6870f4978d8a68c2b310f7f6f6bcc3c7342fee9a2985400b 09ff1ab9622a87b66f39804ce7b84c1620456e83bd2ac9fce8cf3c8564170543 f3cf70e5732cf0b4fbf81f78e95bddd0166aa597c2a932ca16d9e90a2742c80c 777f41771e4af5d397ff4ec6bca78510e06a8d13eed74a53679d40bf23798d0d8a1c247ebc86bfe877badac16ae5c589bf9e1b729f427ec944e3ba70f8458d02 -generate_signature 72f08a3499045d8e2a14b4872a89a7b53756fd3dc0e538ece7093168c6f88b06 26ebd8fc7bf1a0c6c1c8f55f701d66411ce6d2d2a70f87c2a5ca42e0f8c8b359 75c532399d4cd4475ab2714f0da1f6b1f131ce4f18144b69c99259c58b5ac203 ab59dda6eaba38b990c9010cee0970928bcb976e5db234aa8baaef67bd38b10cd40c6ca3bba133b5aa281b95dda4a5b705b88b0520e07386336444ca4c11db09 -generate_signature 9dcccae9215809a2a124f8a3bf62b5bbdd0ebe6d7710586c5355c7a7283b0e43 cfe96abf54cf0ab6895ee0a7c9fb90ec9ccd654fd526c36d2370398cfb7e8f6c 911e17b1af1be9f9446dda5d6d03de66c64d0a785b60a03ab7b26e2068c77101 6e7f046ef3a3175511576089e60cd9db1f415b1eb0ce81b334b55b52b7ed040e2d8f6f103b71c1c19e7ffe1415433dbe1838fd88e7acb219c19cab1186c1cc04 -generate_signature 841ac5bce833dbb72069a88c959cc59561e7f05baaf63bb7fdc98a4d6e1dbeaa f3e8bb769efab6217972ced819551c79f29bc32a131a2ba05ab26c33d62c6ebd 858dde600179ebea9ab1806ab8e41ed2abb320ba593faea28eafbb5a7f989206 6679adc8583aa23d5b167d4a8653bfa7638c1b8c383d58b0fb827e4c92991c01e813d60d8221ddd25d638797cbbd38a59f7fa5e582afa4445220eb4a63983805 -generate_signature c570dde787ce9fcc3d48c5ada9054346455ab9a11a1d65b0eb223547bd39d7d6 426ccc3e1c17db41bb5c975d08279f1451c00976216271c9bf221910ad1eb8c6 4979107437a699a931e67dc36312a1c0b5b5edd3a60c1b3dee3c12596e500107 b1ddeb09a0ce43f0ab3817e5619da7168e784d8086e60b30214392839aba2d0a7eaae3b6c4f206d7a4b8640d6068b447bd9d129156fb2db30fe1896695eef80c -generate_signature 62e8187f216da0c992208f3f7afde4e5ee42e0c09ed9897b010430170bb87350 91eb9815ec0a5d2d2b98bde3a097db217a922089e11c4b15d9e4109b6c534b56 4d2234323011d6837889e21b7482c69925eb483ba5b367ca48f5360cd92ce708 5bffb00d0dfd3abdd2d2d6253b2f3276eef954107cf6a64a020de2afeeac4307916569625c8703fbe5e5cb95919da8eb23fbdbaf42f734f22428469dcd1bd908 -generate_signature 49d7e10bd4a42988f7b202cce474c577619f9bea53a7914de4d295b8529aec80 2daf7743268523b94442ce3f0be3910f40771a4f18a22b52f3132b6b5ab2b3cd 5e6e1c05e2e836a936e17a1765b30165f3851ab56ca1e38ac459c8b7a81a2004 aa8f7f10e3da2efd297659e0ac753c6fa3faddf72a4860c5f554a6671d1cd901a9a63c340360fb81dd6bf9f0c6a525682ac8f232ee5c17f76e07bafc5a97580a -generate_signature 51c27e54ec56e597dedeb33c6f2fe379383a8df301688f95e37fb695ce64b1e0 01487d5868e86da7cbfbf00ae1bdf8faf8d51a40c5be011a1ef11a2734616a38 e844859d1b9a5328533fb41383c1ed108a110e6413c044cfec538975bddc8405 9ab80828e02be66b482b0ad956aff1fe9cb947f314fd5d799556c8c6689a7901a35f49c69c05b4990a75405d39d88dd25cfa2cb05e9316b05e97240f81d88007 -generate_signature a26bbb54e14b7f7587fdf8b0c308bb80e3bd626c20a6980cc08b9f25317aa00a f248b297c284ef1114629819876d311bc01926a75899ab339891b4270ffaadf0 7c7af96309f390370a9a5031b9bd524936233f5f806b9c45f3821b91da8b8706 6f3a02fc2e6c79648a9238d75b9f71c2604364d2f81f9b8b4374ecff91e15000a2319cc8bbc046322ccfb91566a4d1c93b67c499b61eaff0f96fb3b650bc7002 -generate_signature 18937794c31d063933d48dde3fe7b5c6709f931e139fdd3cbae74e68abfc5cfa 4abf6a6c47126af111ab9761d8136bbbefef57cc6eb6ae4693803ac915351fc6 4acac844a196b1754f42f9af15c6aa9893bd00b5f466fb691ef396149171ca00 a7961fd5ed608061cafc6f83eb2ce21a0399dfe8733c899eaeaf3d5b6b26ab0a700b2599bf08f70e20acfc449b7fe889d2b7895097e936bcd5a97f5fdb45fe0d -generate_signature 0b32c806869d8b25e120317582013a95c995f9347814e4e37f6b948b4ba1e2f2 47397e6dbce5916e87dff2a55c63e49d1bdc826d4ead31401d47cc0a37d99352 c852e5ba498eb8b9686557309591b1e9b0533a07c97bc6a8186c036bee2f420f 5f2b0f0f0d745b96fc05f9cf0c57549452a32b1f36f5b6e34af8ecbf962bde07a9c60870865456ab0c0ed6a3babbdce3c2ac89a28b851e37b1b151a635fa2e0a -generate_signature bb5e240d1bc7e986e6e929dea3095830fa11b51fefae6cb1bdc8e060775905e3 7ae97231c99afe3dcd855aa587f1acf5f62ea0c61cba1e0c14b0fe4fd26a7b1c 25cb0101d7914aa49cd52abc533a7cdc00f174ba107cdd6788a1f64ff0ae390c 0fee95662712429ca9cae0921418f8dbf15accf71ac28157ee57256dc074ac04863125ab2850db5948f51cb7e9a00f521f7b8369d2f3485891250d7101945d0b -generate_signature 30d230c4ebda0c60892e470dc95ec1152b00c8176c6a93ca73b3057cc7c1a494 20200fa0969e8ecccde73758c864cd4079a9efc8476776755390b9a56800be07 4cce1e28f0c62eb772138560e232b27c44ec7ecb7081bfbe07de7d7281c3da04 88478670f74a44813a8b95aed7b939c730a7c302f3d473af5da9b0ea27db8800b7a584a51b98200176de2a0ced978802bb0e0f6063042f6bab0e901eaceaf001 -generate_signature f1dabb3ce43a519e5899d465abc054635e9fb30f3d49dbacf2dbb54725041d61 2be7da84b3e47473568338ddf79a8c87b22f29ef4295ae6c6ca463df3102278e 8831f066a87841b2beaed0f20b93da8cc730e303bd135ca540511c6f697f7f04 e742a94c268b69f404d8d0b9d025f99e0b3a297c79cda1c90d8f482e900969013c5a4a59cbb4ac94b0dfc7a0ff3f99d04f950da5e94cd66b8034b481d8f3550e -generate_signature 5d60c456bf82803cb3830fa2de437c377f5f0ce42b762a0d2a5847b95d6d4400 38aa6f868aba852ee5f2c2444212088ca48268451ad0bdf5b42d020db2eab246 208ca6972527da0e4333b6d756e5c3f336d70f1af86ee8088b050e73dd51810c 4dd34aebfaa17a9db10270137e87cde2d7795df757078a51bfd63e4a0b7b1a04c93f48bd6ffb2e21d31b1c2675e22e1e60869fce534fa61f84cf3ef4dbb7510e -generate_signature 58bf45b4eb8cdd324173894cbb55511bc69ab649e1dc79311bbfa58c6c0f9275 a429e66f32ec3d35fa825eb5b6357f4daaffcac3a11c47ceb80ef1114a06a217 c3330d61f993dd6652cba521461f4280d83330119a6ae92681b708ab32418904 8666e962966cd44f2f1bbaaa102b192e4f86f9518895522c8437d5cd070b8503de647dc701fca446ca46bc2bdc838021b551c13d5113e362879a430da425d80c -generate_signature 2229a90c79f0e3dc230cc3919066d3e4b385ec53b0559f7ca8b611ee2f29e12d 83d93b52f0df0fcfb3d6e2b13ad01ef38c494090c18014296eed91a5d988b2ac 3e286618640c874daa6e36dfb01b5e2d1f5d77b8d9ca6f5ae5b7ebe659b46709 7738834b40e26f543dec2c8f94289ba12db18a023274390e8911bd2faf2239055063d397e3a939e9fed08c4b88dd2d18bf97ccfcc66483764f3eb494594d7c0f -generate_signature 9e47f640aaee7ab2e09c8fc12a32a531768258c5135eb8e521965d8708eff2e8 a8411a8de998e4b6be239be96460ec6d4f4a8b616b94f77347838212ce4b8613 cc4598110b1aff2282997a410e7cbd837e12a98461a6c6ffc307b77a323f0b06 0d40ba0f2254f4631bf4749320781b75da6c877a536ee6592ce03983cf4e7a0a1bb35353383b77864bf849dd496cbab8094935ef331052c976fc2086c4c04204 -generate_signature 1fbd47cacc32270ecfe0bda8b1b1bd9ae4e03a794016a2696eab9cb0ce2c40dc 41d8ee40da7c75784b464f1b2fe89409f898fc57f395b46ed0038e98c184aba2 ce061c022ad0ad3125b6cc34b0d27291874d29575b10e1d44528ded224b1500d df75e84e803edc61ebb345d6cd96e28f0a30e0e455839d841c18c236671f8a05e9410f779ce84447376c3b0312aa52595bbee530966426713f5c68e353ae3109 -generate_signature 0756833a04e57930ad436ed6e50995529036c5b55db3f9ef070a000ea7b3b668 ef541d76406b4b71299d45937ffac3a4d284b61faa8644775e346ec6f6eda51f efe31b98bdb433b3535e792aafb0373d66952196bf564dee8c02ca0dabeee40b 3ab06c0ef27583ee933857bf6f8c195736b52dcdc23098853616cc1d7a07220f63ce77582cea5bd9c8daff96609ab2a57575927986047a54c5833ffdb67ce309 -generate_signature c5d9d4483c4b9b23e21c259fa19136f0c69cf2cee896945510e95141ffbb195d 39e841d4ac6335babb18e8d9e47e1c75111639709335a1310db0ce2edf686871 6a2b88bfe8a66655b29e20294d715cccec610efa3503cafcbcb5323d8e618400 185a330e61a207ace347f4ade91bf02edf89b6f8433c80564cd03461ed9360052528e631badc4e3c8c18bfb8aba9c493646cfe098e84da1a542b3fd8ec7db601 -generate_signature 8bc9f9c21660ee20ffa666e7be110d8accd2d1be0550aa5bb3b4ae905418b060 14b57372138f3995b35fbb964ed0481969684f0338470a637e183b9780191372 85465783ede0e697db9aedcaba7f2aad3f4fda534db37b9654a3325dd4361302 498697da4ac1809e3eb302b77255da7d3c837ff28475c5f8aa573d6b19a5770bc0ba352d8969758646cf2f228944f29d8d58b04b026f45a0bf55d1c67eb0e109 -generate_signature 7ed3d902f3ee03f81674c4452bb1cf33c0601fd27e25d5aed0b618b55aaf7167 458fa36ccf56bcf966910083e625c7efd4114daee53c45f288ec2cddeb444aae 3a0e8fec3301b4906383419798381d28a2a26e9249a58097734dfde9b9be370d 1c6c6f2efc425c9a479214dff275a1af5f878a5ce4544746875c4b40adc057098424b3f108aa13aa47190e3360837a5630b0d5d487911ac366c0d49426b4340a -generate_signature d95b625b3397e7726956f869e61df3634ee46b08a914a6a0b10b9e427a9179da f2cc630788dd65572d325ce0895de974fa3d56e07717ce2776ea51a32a542644 921c6a6d736c246dc6bef590f359f7093b32d56b05cd8639c7e94541caa2a504 eba26d1aea4ff26bd2c2bfb8e1273598158fb06beca35d4bf5438346d17f2f0e94fc260310c6772989965f6a4d435c7091ffc774769336d0f24de616caa45203 -generate_signature d17c7a1189954c38343e20e2b8e8a290e294753072895948d184cf0d1c3193c1 2cfe22dd27f8b7041cddd1b8111a29a1c56b35989aabe9edafab342a484f372c 9317e035d8bc4a451e52ff94e5594885823ad2cdc7eedf66c5231a4f81bddd0b 90744f8faec7ad521de63578935e614f5c017a11fc22d215638377377ba6c80ccdcf147278879b9e501f67d027a1aa230a7f7133addcb4486484a1ebe3e51a03 -generate_signature 9387ebacbf7d29655f65513d886d006af10a8c75a11cbcfef8f530a72fbfc972 006e6a957483b2f082fae65ffd18fd525bdff0d774dc20ca0b48b9cd8f592b2d 811da01862356cb02275a0c7878185670732b10c7de15ce1b2dc9ace1dbd8e09 27b0b5f87521c91b211454442d72d9a3723aba68369f9c8364fc6117a28c2f0249848764fbaf65399c46c025194aa6f5c86cbac127c26f99deed8d553d555408 -generate_signature 307a4932ef8f15539d70e8721dfa1a4142ba4ed7b2a734f8fb37ffd31f72accc e48b91027d713f4e5ec7d0fe315589b36d6bc6ec338e218f31eeea8862209922 0fc991a39d51dbfb559047a95078e8cee3210760eb9c0ca860e5fa48e267be01 297decad20d429f93736693898f434f0efd936f8e271056184693248fc60c70bf6a126e76e14a535722ac8732373c6a8e593540d9ad695c64ecc9b2c24a24204 -generate_signature 4366484aed2052c6c28d935b2300b5a4e5172bd79d96a37393df3584c0020b2c 39ed6f8df7c2d04ca843e2e76bac2c6a4de490f9c8d1c4ff16b70df13588a4f0 de879609d955fb6ddb4b0609fabb4e49fbc7ec017952681f911552eac8ed2d0a 651f4cf9e28b6ec1c5ff34f3e8a2b0a8ff29ae0e8c2bf22f030186a77764d20c8a72f89f358e6c727db157042730c3cdc029da1186aaccbcaafbe17ac6e9ae0f -generate_signature aeac4a1e1552df635de365394f86621ee2f21f7561a71a63c07fffd53fdbdc0f a6a9623f5791d0194199f52ea1e4c77be18a94541e9fc8d86b591ac8f1c0c3a1 c4ffa6e43fad2e9e558118f7b5a1cd2196453f91e1aa3691013209c7f7bfd40b b37b64b57d2a2227c7f47591dc189ca368e39b92c8308c064af7fac34c578d01e13e37bc10269f2e974fc1814172d9f4ccd983cc7d74c48fb57b95abd67cbc04 -generate_signature ed905056a502fe94edd1c28b3f65491330b58a1e2563dbb8d8369651a2b30cb1 fe89f71bf6d52874eb89f234baff8c878ccd22c5c614ad18eac64f1d49ee00db 9826bb63674eb73980eab3d333d90b1ecdd18a227a34e729ac4aa4c679570708 ac774fce9ca2a9bce0b22d8c3a0144e938766fd6b33a8f6f573d61a7d00d140d548b820edd507aa7665a0676206f14e97a97497c09a6ce8718dcca0e8dd56608 -generate_signature 327024bcbf89837e82f4af135ab5bbbae710d4fdbfd771423dba6d089d62ecbf 089a1ffc8c9ee8b28e3181694127146615db3ca57d4f808e375b0844d68e4ba7 9c7c12aa4310db31790ed6c9041b711f0c742fe3c903e3093e2589de9279130c 9484e5fdeb9bb78ae4cab30b1bca3a9616959c19d7e8b284a3f10e5da8406708c2590dfe2e5fadc55bbd97ee7ade1a36f160c3ae7416c1f030ba568301742e07 -generate_signature 5fca639815dc14114fa452346ca15db70f4f3c13a1bd1dd2e5cc6633bd884a97 0acae623f287aa3b9758a36ebef615ce9aaa16f619d3cf56b7ceb515e2ea5c27 11569cbb68a9b17a1ac43c5b6b87a9c94e269ac507541b87accb5681fda30a04 bed44ccd0bd1cf2ca84310dc64deb9dbc606548bef95b98256799c6b728b2b0a33073f8b3fe2c7218a0b8d3ae3b489ef247c7680d8804feca0302ecc39ac7b05 -generate_signature 0f217303b1076cd895641ff1751d2d469def5e140e0d61dc1a4239c3decdb382 d0cdd348b338fd12dd2602b2d5a872e820e1d9cd6f0b2df9c5c4a04e49559a62 fad9edd7ad17647a02d8f2077b9ff3c0fc755e5eb071165bf902654e7ff1050c 903699943a3dad0bd4fd72b80380767beaf83b352fbebab074ba926776b5b105503cc3ce924fb520c580ef21641d75c1dc1c9392dc8d706e9da9501c47152c09 -generate_signature 254a879d64f8532e5f281149b52a68bcd96c2148f33e3bf7cabb4dfc3450043d db123979ed900409c0090c74705fc059ff72c4e1ed05656b117327e7d3f68d4a e39e1a551efa30ce0b2461796147ebd86394c8c0abcd8fa8118fbd7cb5f7920f d44cfd92bfc3e6822b638dd0234d287c099741fa4939645ed4144453d4f5830740c55ca4d9847bb38454cc11e44a46908cc26a76ff256104acef9ca11986030c -generate_signature e65ec30c477ec561fd8dfcdd1a3b89c099ee7c415de5e780618433c3209b9824 e45c9e7b6341e47a07e794556c9cf91869cb97291fecabe4867e036060c81ed5 860e36d4b74b80737704a4db0ac58e4181d807b6a0ccb4064dee200de67ae603 7953e307c61b9b0097afc72b588f53237ff3ab1d85b3c5cb773c6ffbe695fd0affddddc9e293702cc9b312b377b91cedf50085bb7fb914e1bfe83c4598995404 -generate_signature e701f76a8a06647e5e10c677a9fc17ac37c628580bccc5500f34381536b88b9f 1bfe84084ebb54bfe97e3701eb27cd77e585f510270dff9b22aec8a688d0512c c7d74b95fddc2dcfbfa0ef72aa1078f62b786f5c0d0650782a2913b5a22ca609 13617b56d84c4e269981e2eec20b8924b4af22bd686d2504c46859ea49811303ff3f01a20c9816e81746a80e88d5df1a5238232ab3699d4c34565f6bf412b30c -generate_signature f220bdb877fbf2bad58a7e98cdaffc0047f8d4512cf884e45c484d5b2a19275c c37d89b27a7449d9015b93669810f35faf07908d5fbf4d93ebceed9c1021770d e4692ca12eccebdd039f9d234a083b49a184b7d241025e4b27d022fb9f46c802 649f3ced8a90210f95324e8ccb7fe3048302743a133c7e6992b79751dc855a062083fc8b932967fac79d0a497164b20b0fd234fc6c7bb3da9f389ecc03215202 -generate_signature 2de9420d201089ef5c3c1fdc2ecb69446de59b44b84813d4f2c60c4762cba262 ea272022926e4b6059728e63ea0530c064280312e22e82419a5463b34b5d223a eef8d695f5b62c90c77bfd4a4e257de3defea7eb93cd546db2f0c448deb08d0f 689a32023340b961e528ff696ebcb0763f3fbdac8c1ff23e72ec3c542ea1f1014af7ce1810fcda6a4b71daebfa4369040b3e54d40a40934f08294ce064f3de00 -generate_signature d22270489687f1aefc08d45cb567859a5a7f0fc6bb4346ff56be386f3c64ba68 b231b42bafbff5286ccf0828fa423487e3edfafdb3cf84b4333b92f7c5f8e005 3d6b4f25367114c0fd1922209e6d6ddb062c959f0643d6b5f5a54aa7a8dd850a 39d8bf2d2134e3ac428ea055350b17470a0a31a59e4ccacf49859c2cd123010313f312176309f64fe2c06ace20d3a027765d1cf0dd73a595e3f1bf0582417501 -generate_signature 0f80ee01c68b1c0fa0f72a6890a523e2f941a934d5aea5a37756fc9308e3d149 40485d2af8f529aad1cdce430971bf593c40ea5f650c65d24ef2f3a1d6e09844 ba73077ab95f6b2185b68884e757c95ff288af975d70f54e6ddf75446b59ef0b 6284f664f1eaa327e9614ae44ce702e465a3f3eeb58cdadf97cf715dbb2acc02db17d32bc9fb0f9336156fa512283930c3ed1dda52e7b1d1f3f1c82d15b2b50d -generate_signature fb9279828f9075e43491950ee0a6b55f2740a03e2803e20e9baf42a71845db16 a60c8215f4c3144d397db32359b1638a94bd48efb4921488be9bb2dbac2d3621 24d0feecf4a09080ce8cfd390619e0f0c9185278b712b9a22f23fe6d1b028105 4b919c7b5a5387a1d791cd9067f08c77ac3b05306825bcaffeaa6303f06c8d08f32a80e2f40f3cf3f073a4af3718e10edd74db85c0a2b4be60adb5bc4abd8606 -generate_signature 1853a3886d3421c884701015146416e2372be5dff4804a87c367d69a14ce1674 a469f283112cb9e1247cb12358e1d1f0c2c1efeeb0311b4921a4ec3c2fa6149b ec7f0053fb49ae322026d8dc884d02e4784fef17469b85415f4bb269f833410c 349af7c50f673ec3b90f2a3e7bde23ce1d592730064d2724703a94349440b205418c43ad614ad88b6975c230411878ea43d76962e0d370246943875126e42f01 -generate_signature 87a4d1813017c757f2fc7adbb6346bc711619edaa8531378e25686bdde9b54fc 8e1cf8a065ebb111aeaee0458198b02e9df18e357e60604aeaa62f0e7e81fb47 d75a74c80860cb58614643bde56d7d0793c57750af954f4fe949e08d73e14402 0cf654c8d5dd5e5e48a61e3eba21e68b6b862fd26ea66a409ebe60f6b0d67d0c87dbb4859132f7c7f87639dfa107b3a198c3f912b4d9a22937e2d9d83f630a0a -generate_signature 36332e7b2431dd072a158803b24744d01b69f096aee41407d70ccebda99984db f37969dd37feb2205fa2d7c87d62bba58a675b9ee08caabd2496886059a5aee4 fce10979cba19bc179dadec71cdbf640bb3fcdd3afd07dc0e61124d293ae7209 2adc6d4481f5ba5c18bf7393944f7c8879ec4c8055241f7c1835b514261d5606acce13d49a141285ab9cf45d33de4da1ab86acc47f49cf4df4081634db7f9b0a -generate_signature 2c7db34f2f249ab08724d1a679fe855bfab2ed68228668c7e712b4760a839f63 ac948ab799b000d6125355c96a4c7c2f7918831e2cdb86c66f75a84ab3b588d8 711ee793591c685bde7f3b136751e5a39d55f0cdcdac03631ece1622a5982904 9891c04238700e30e40643aa0d7446d218dd0f0a976192743e52464428e33f0e5c94cae2fa5e8197846d8f4fcc0198f26dda091e1e692ef61a22de077d7a7900 -generate_signature 9f92e54ff6d3c2033a5ce9135c8f079a93f0a7ff4c94467d856fc63c0e96fbc4 c6407e167719e2dc8e9e9d719b1ba7be2797eed2e43dd27b02e8b7affc6ba801 9a3b191d1806472ff03203d5602b9cb73d3f0262cdf56ad70d20119941f83e00 b254c6dab28e8caa175c8efd122106baa69e785af9228445c324ca3306059d0378186b1bddb287b4351ec38b05131da322bd8d1809c6a4a76dd3ec7658ed5201 -generate_signature a70f0611b8337641f0d45cdffc56bfaec855fabfeb6fcd3da1292206b4a03309 0baef43d5b9d89ad7572c413b531381745af1dcfd1ae90d4b20f0f78080f2ccb 9e5527c3b3da09edcebf39183c5d912e28f47befb833177726833e281748c909 91674393a0af7360c4d8db85d49c8c8f373e347d3d38e8f1e6f74aaffe46610da791ffd69ebd3181a705f041701a423d1b9f6bab51b6ab6725794812b93ba205 -generate_signature 878f172b5f34e0b18a55df0ca544d7dab961eed79e07fc4689a0a3be8b3ecc72 03971f351d3e118213c1762bb564a2b56e3df37d3217665f16f5193fb82f8a5d 213d506a9731bcdf5bf8c3968d89e7385ee573aba69d1572c3604cdc29e9a200 3938dbbdc1f3d9301eb69faef8997bf406f7dd811a047c721e33986d159f3b070214cd952883adda3bbe9906b818b4375670e1f5dcffe14104d91b395defe101 -generate_signature d5c32a4a81da9af917c63f72b91c14e0a8fa93413572b702cbb3e9594481deb1 b558e0724567f7b4b08318ffe6b0dd0b53cc4a876ad1530692f286b068d19f9f d5a64d1b5c47ae79629ecb770ccdf0bffb1148869360958202ddffb2b5a6340d bc781f0fac469bf1cf36ece152f554b5f325e64625f1b23ba28ced2c47253c0a45acec1edbffa92ee5b2053a119124aac075a0a684fc8c192e4281042500e70e -generate_signature b3f0024e9682ccfeb0d69b0c6d2f0911526899190e7f0fb4eb1e600da16aa63f 019ce545f3b46419d987730c551e1787ccfa158bb42a8f203631fc211e492c6c f03757ea2d5b66ba968b93e5109593e8ec7f4c6809f15c6f6d881b1f76435909 b0662b8d31c13e202584a8f3f524c3a32f3c5156399cb2c941768ca348c5500faf22d81f40bf6be393615f47a063abe63664090bce0c1e86d43d6887cd76aa03 -generate_signature 0b18efa336170b3a6683dc486d1f36b6c4cbe86eb310388f71cdf70e143d0aec 987eb3e73980b221b2524179565720f451786f8de2b3407c8b5716ef683c15b4 d3db1e882a144f312d1dc307d8bdd2eaab5816804329bc36430803d23744530c f4591ad8762bac85c540c6d4bc56233bfafa00217d914604bc2a70bb3466c00b64f65d79d07884123667d1f11ba04143adec52f56e34e8f62c2e9f54e08d7204 -generate_signature 69ae4cedd012737619b29be8065ede9f93b324b50252024d928ef04c543a79b3 35a734465352db4e813f76b446da1c00dc703317f1ce52c8a35a0cd571074568 ebc39ce202ec647da79c7af3ce6ddf307fc44de800a68a255c2e7dc7ff997204 548dc54f0ef555150fe479752a77bcfd9f68bbdc4d80a4d39277e789d6f0eb0447e21e023dc2d08ea5dabf2ed8c73deccc57ed7b38da5f141db2f7bd25c60a0c -generate_signature 065fbb38e8325511ceb9aa041f32aac98a5dc8cfb06a85c98853b25cdfc47057 281b3c8844fa282b064d56acc83e92c8e6568c3a628645f63e40a07aee80aeb5 bca17611de2f9642df95a938f64894a4079ad06f842cfaa9bac087fbaca3e70c a1cd7e79b21d9d740a112ed49167b9e56fa4b618680a6ae3089d06ab95f8e20f97b694a587a2e43640fff2278f488e3a7f8aafd6395cbd98ef5c216761be9904 -generate_signature c13ed90a542fd070e07610a0fee13cd42f8753f3448591ef9c9f57ad721fe9d7 02cb435d82524b0d03853e29c7ec0920556b85fa12886b063cf64345dba55483 e90a0b4646cdded432d27a6204c67a76b8a3db2dfe0bb2ac6957d68e6aa72304 26469332b2791f8178c8998d738b04d405522643014666a81ade8f9ffccf29024b339f8dbb46216e3d077ff7417ec561ab7912143248ab4dab6f7355efed6a04 -generate_signature 7badd2f8d2dd8198990738617145e90c87880b95d5c875be0cd7be0dc25e7839 240b3d620b61537d7fcbde252e9de6da442bdce4e24cf4bcb9d13b50206129d4 9d9c7c91e5d71920ff7db53015814d4360eb7e398cd6ce8233c2a1dcc54ae40a 39937b610f93007ad70b22d0acb3b52ab42e2442607870e18c6f4ab31b22940366825f88437798285b03cd220b0a3300258b0f0ea996add9892e427c3fceea00 -generate_signature c537ab8551726c4d7ade51835585bf7850fbbc1d57f9245fe1b29618e2aa5c30 1ef9913d379ce22e9f97757c68fc5414e73a27e69a55ca223cb5eba854888d90 c3f5baf38db5121dfb177a0cac1d6bc423ee0c8f7c1323d9b691b07489006b07 75991f162cf2991b4b9d8b9fb1c04821a8edd8efc19f76410655b64e235c450fd2e6003a33f13a58dabd88cc5731dee3b9bb17cc56f83a5ba52618977524cc01 -generate_signature 696aed47faaac314742d0e6386548c68617dee798a83105d3746747fd06d0cbf 279e1bd9754759a921c232f37d744d7b57531a302245dbb54f95259aaf923c12 28bec44d3dde67ee976fa0761ff650fb0d9ac60e74725e4c5ba3d7a0575eb90d 12feb731bf04c38293aa30b77c2f00868646d26c08a7fc9eb1c341ece5695d0c9f9d80580972a86a3d327a938fe95115ae113d8595abad0cf4ee1aab460b650c -generate_signature 0215e850ee377ac137b6f9ac06809bd6cfc31918bd0a372155f28d45c65eac25 45621dd9f0fd09053bb23b178886d7f258045154ce90a9576d708f0d92ac656e 0da2f7254d9796a93c9ebb3fafe40d398726941e469c6f8deffc9c32c2b5d00f 12ac07a817e0f19add9ade660c64c8f31661070859379581169753a30cc92404326a55421cfa2c864f9b7555f0f5e4beaa03c64ba16d2d2756fa32efd8c33302 -generate_signature 877a11a34fea5898e4643861721def338135c5cf6dfe1a6fc6777e77fc68d245 9e47630b2a88e9f86555a6a8462a435c0ac2c45bbb85115add2c4ba3dd578031 a3322e75481f1ae1eb9828f57a2b4cc1c786167aeb54547c36c936600747ac0c 705522c688f93fd8baba824ab73ce15588fb6e160a71abb9667639eeca0d5a02dfbccf5ad9c34d80fb194739d945fe328e436607de135244a43f70aa459ab10e -generate_signature 191a17e4ab9fe35c2b1ff3190967be43353c62dba427e461cc6a2dca6b12228c c7312755cd3546fc800b0af704752069cbd24d4d132f4af435c149d1024da97c faeadb6cc780147a42bd6d1968169e088a0b5b5025171ec01fe7ce17155b7d00 6502829ab5c8f077d473ec70b50c7145cd61518e6efe4b2840009557e361f804928acda8f80037f48cc7da3f93056653a73c2dff82d269442895d79d8882c70e -generate_signature 37bcb62ac859e2e85caa766836436533bb47f58b8748f921d3b1bbb81f9b51e4 bc46c4cd6ca4c15d76864b66439d46e8f8ccd846e899f18ad782d6a3608e0c49 28fff080636d9950ecd5dc362736734a75a72851823c7e01e7489b28c462ce06 bba8d06542aa7b119265acfa49aafebf229ffa8c63c6a95c09e0e2d01f461e07936d7f0da1f92cf5b32294d1f5c8d62f455d114cce8f53539d84b6877a3fdb0d -generate_signature 42a48c4a50b87313860ea656a1df5698aec2a2f8d03e69ab24c7f9c7f7f66056 e4e4c0e5cd007712ed5120892e21b0615217230b1370f9a531192e09f5463f1f 0f98ce51f4aae0b3c5c549fac5d94fe0587e38f298354f36e6a259843322740a 4cf47185f64306d939efb8e368a61be874fd320758c1513045a4c667bb10510f3e454b2b83b451782a63da46b24be69a83226ef0dd5ee5f4b757eae763b53801 -generate_signature 2280cec561410c47170e37a38805c146ac1a8b73c1691537fdd9d10a9c430579 21ffbe4f3e0ef3bd589689c0f2c35bb702f121d83d8d1ca62554220c1da72e56 a2cb2795994ed3ff548fe707d64f59369cc6e893ee2ce9830ffa526b2f41f402 e19cd4211041838a1ddfe3cab324e14c537282b3bd9b0344ab1b9dbaf22dca05235b6d085399858d883c977140ea3bfc9b6dffa2d3c1b12f43b3946ea5acc70e -generate_signature 7df35cef6dfe5607c06a64a38510e5c42093c703a88d5d76dfa97b14a0f6add8 9211c6183c199f81e344d3650c92b83c96bbbacd6ae654d8644b76a21611ae07 16b3d5bc1949ecae9e4db756c129c71889214a5af2ff78aa8a479a0c5d0ff90c a3c92bd6865aef52b3628faa710bdeecc44fb1f0da779d9d9c02c3a5ae7cd00d4d9229cb46fc73c4c2c3327e5c0f5418e09b1d707e93a92efcf40a7039295a0b -generate_signature 4991a192c5c493c47372771fde392802036834e9fa14ebf5c4696e40420a4c39 61c0eedb78215458d3c724551d7220f92b8f70aa656514de7fb209b26728a083 ff600a4ce0fabd6602b51e9636ff9b8b9e102f1da91f00376aa2e50411deaf08 1922bcd73daab892e6a580495396892833fd190e8eb074a26121a62b49a7dc01613318d3b418a5fc243d4d50156cb7fb5e2b5839116b0f4808ca280c38738204 -generate_signature e9d7fa57a984dd1d9c7a93125fe69f4d408c821d7c17d199ca494d16405c0daf c536bcc2db715ae003881137da965ff0e30dbbee0f4e29d5a439547b8cd48175 1148d655f1e23fec319f04dcf5c5ddf131fc182b50e39334ec0f312e25429700 61c507d190a114f2a3ab92463ce038563314c164c327c1c37e7b1530f33ad505376b070639bc51423d12aa326492894c577c3c511589599ad1d293c0d7ffdf04 -generate_signature 9c619840f7234b099d0995b905a15ddce999775aa1720f0bc9477bbf0c5525ab 4d8720590ddcf790a1baac573dda9ddf0efc21223335b15a23189fa67f515ea7 dee02eb9d27969ffdbe6f7c22642936e5965f3124fdbb0c51b1c44856c075a04 645d6f8120f7a12bf694408541a72e5e1c08085f4e2f63815d19c8bade6299057093aa996838bc753823d01bd3fb9543cb94d289c6a4befe6597c319c7993e02 -generate_signature 4e5d10eaeddd0acead90bb49b03e73b84e966bb05d63055031cacc8ea5e49aa3 e2c8684aab1e9860370e94d295bac00283208ebdd29910d54c9a28477daf6551 4487e8f01f0ebfd7b1870e7a1cdcd86470fe644de11161309114f120683b3509 b2b1fb0378592a0a1e8d4df646e96c34bd2647a3f5fab66318a9b686699d340273b6c8d482c55a3dcb559bcea7a59ce37abc3f71d9ec403a3f519ff5789f9f0f -generate_signature 8db465446cb5848044acf5826a3c8baea057859846b551238a0536dae39ef712 f3a88b4793016d12031ffa76a5da01dfb771e9255e8de871769717199b83ccdb 3ddbbf3cf5f282afd370de5d33dbd8464b805bf4d7c74be2410eefcb5072da01 0ddcc237fcc5c3a08eb2b5fabfd440b7e89cd94835bd491131a2c71143962b02765ade7dc00ab791af75f610c6124e15d11c796e29af8210014655a08feae904 -generate_signature 24979bf7adb7f8b840a3524c3d3bbcb73490e50e71400632d824f420427df802 78057cddcf685984606870f37a2f0d76e4ba59d169722729695edf4934528856 fb691338b3fe0cd1a272b7ff7e363727c66689c1c7cfca7c007a5543b3abd401 8ae1c701c68851de8a8af85d420c0d1953ab9d05465979b9ea009d54c96cc303b1bb1d12e471ec77109dab9c17a6806caa1c8c2c9e0702f3bec8ce16f786a60f -generate_signature 8866f2c3e0566f73f173a7e0f9e33cf8b1795fde4cb09deeb1efb17dd82ece33 28c508eeeda24bbaa55dbb312f534292bb1d8ca09cc34ceba36a3db1ae5aca9a bed8a7e6bc28ff86ed87c5f54d91f8fd381a2fa9057b6d38b095245dca1b3708 8f718f3e412d251bc6b2339f9e66632bdf4331236e8c4107d73879ac26bbe807a50e5bdfc96b80e20c6f5547d1cb4bd07821c7cc62b2969f53ad1cd37d3de20e -generate_signature 421c152f337614c5a007b4eeba8d4299f7fab313c1e4fa935048288d4b2531d3 862b1dfbfbe3934696a9ad41adf2022714feb99e5a1936ba2dd4cc08edd7166d fcc52b66e50a4761eabbbdba3fa82f2e391db31ed1be585ba5a936c31dc36400 90abd70c347048d47bfd4c7a8a99226f339706cbb60f6c0014b28ad2674be00e5b8acdfc5d3d5c47cb3fd7c4430ce61035e3f227ac4e73bedbcc024d0269e10c -generate_signature cd4eaba1978fc73de4114aca7dc6708308e72a8a861c96c2eaa13dd06f2e82cc 88855b172e2e512fc6ae2bb6e1252670210b17fe4c021f97ce7245ef5e0766b7 fb333a06054d22771b8422f547838f14f4b868e6c0ffc7abaf52643b36e25a0d 4e9779439de8cf8f1f5f52cb204affb110829c4b813c758d095705f01076ce0303eed8e0dc54164f580e7dc30c4b954cfad72dc474a4ee7d5906df93899eef09 -generate_signature 38d2cc1cc489bb0af029d12f1ed624bec71d68c0713f1fd7f5d367efe71d72d3 3b3df37e4fbb3a730b5f9c5a1f7adc45dd2fb2c05d1e141d939536b6f55065d6 f10b5d6fc5830989df088c2a1a9bdf95c37781bc8990b95beeaff246b858770b 8ab91d8d59c11c7fee692e1e0d89efc7aca705d0a37cc3fbf7189f3f7638350e5aff672891f3b387975e55699a7994b76a1e65729923cb404d75ab83e5421402 -generate_signature 017a5495b9f777b45cd256eb87cd298ae9d346caef147cbbb916d95a6b61c677 e8d23a7fa523a7d1d59d23f5929cdf9872ad01eb77347b076121b2a8d24289a7 d77e90cfc8bdced4c2957514ad1dd3ee2b4e841f843dc2cd711df92d31225801 a4d9d842ee79f9fd09637d8b1cbe3464257285962ef8f063065ef57576a79c05156b616c5ca01c54bd394bb03195ad1a3fcd16c06f35ddd253d356f91746f806 -generate_signature 5cdee6559376c9ce870c83b06a6492b019ec54bd0058c2051813cf600bdf25d9 a2def558fd26b3e1b9f1d13b131a324ebffe1412ac2d043f7395f060e35f4780 29e13e332419405883c0103dbabdd28035a4be98f8216230d4a31072ffdbad0f 8c7dbf5351690e1cf84235b8ba57b8227ca87d62349a882de275c2e83f44d8087e1b087fb19330d9b2fb2b144cd01b8801b3ad4d9377344953da1e9fccf79103 -generate_signature e5f7e794c78196616415dad78d3d07439809df7c7443e44b82e54c6faa5bacc8 6fb3a5c5d7b694b63f29ebaafa2cb101d455d95d1b30c51d2027714c6048d81b ce2794dac0a5fab2074829050ff0f631ed916adb97c110d1d9c01f11fc572005 4de61dcac6c2964dba30ca6c8d0bb2dde316f7dfbd1da8e18e1c200663400706499938f22cac48d0a03a7206e9bc48bc62cd4fc898fe3c9f16e65b059d36d705 -generate_signature 5283cc05c1a813496774fe331d90989baca3a8932ed5d6ca1633a96e9881deb0 c776290262741f162cc11cd2c6ac606330a0c53fa543cadf061d844ef306920b 7012ea2ec897d4db791c283cb2f878103fb6e29c831549f2c11fb306a3f33301 727afe80ee09679925d76b89d6bcbbf274ef85c67ff689fbaa1478eeec71bb0d142d8f759199ae5fa11e9d588c77ebe7c16b85e7a969c34fe570ce372c792400 -generate_signature c7769cf2307480649f60e6b97930ec24d3ad128375546675b9da306de670ab0c 3cedc8256aa3d7844139e180826e005f8619adfa8a493da9e824181e4cc007ec 4514bdaf0b277950569d81b5195c63f14fdaadf1156dbb3ee16f3aebe2cccb09 d21dde1a7f581d9582459e573c621a989de819e6b778b528c024ea1be7137b0ad3752080d9eb66364f9ef322ffaa013146cbaabf8876159af8d80b8c211bee0d -generate_signature 0020398fa58108e0b0e5356b87113093b1e657fc5aaa229a2cbed766a1343b13 dbda7c364135fe9237e54e87c742a1174d17d3e70a08051780ebe274e399a4cb 12160063e4e47b248ec37339a3e62e920b41da6ed6b9ef1dfcc1f48a17c0490c bb8c5825747475467cda1d925b752f89d51292ba8b2d08d2abf5ee8b993f3b0dcb63d6cb31358a0c651ea96d7ee85084291de45ec36ae8588a2797a147afff04 -generate_signature b1e43d4e2cadb0e60c8791fe13909d0226ddc2fbc5bd25f73466910ed0ab3f2e a1926c9a221e847a653ec6136e4e23768ee329db20a7a5986b51c0b35eb73d15 5b0d93ac7c30cb138102c03ca6b27291e5adf76d82bc88862bcb3af8df86d308 c4e83d8bb69c4826754c500f1bbbc42e146188ee476abb992d14d9f524d5430e254d80c1337b57acb14b45b0c2ada760e7d3e341e6abc92de12c1aa2e2e4c207 -generate_signature c5e0e0a1d97e19628730750589a954c02102545c18f7bb05ef3e9eb8cbe36523 e4a4af78733fe9a3b297bf135e8b57181d8c0e39ecf9a5a7ce1898db57b18b17 7d29486ba52c61fc5ba24e42ae6f1861f161a0063f102ae3e86c480b52d5a40a 97c123af28998c087fb7d0085b1979754d9cf8851c9e6be628fbd35869b91b04a03d59f8f274897b69dd06ed24514d6780b4d5510e9f4b86eb548e86d10c0205 -generate_signature 81bb454381f1614e7e5764e88664dc47f825d18ed5710899d1e5968169d7bf58 1bce52512bbb39ddb4dbe9a10cd12314ce08fb7ca6b20d8a0855b334b5416a17 8fcbfbb6f711655ccec1b456597b0ce4a5c81adfe9523721503d36f5d5be5708 6d7fe9b31473594ecdaaf69fc4f4fa21857bcc94882978af178bc4abbba44d0f11aa2838ad1dbbf27bc4bf7469e262817a36449d522827f750489699d514c903 -generate_signature 8c9949bde07909984cec9d462c7e1de28f0d47d143790a686bee51d4983556dd fb6fb90c091135388023524466045bfca011113099973ceaf2d6dc4fa0aafbbe 8dd9d958ef607e46a14f742e8581c903661935e2257f24e852ced3f7edc5ca0e 5ff8a67a870444f90c7d34984e07026c171a31cad991dd45f3b50d28069e7e08c3f13e5a2a238c82913be7d8182dd52ce335dd5b19980f8d063fede845a2bd01 -generate_signature ab5556f1489abbd7a0c8c877a18313617887be7ba186a8e0a2d50e9d94a578c8 e7f84cf6411ca6bba208b07ab26e48a09b779a1e7bc907ed587a1d7ebc39e273 601f584a65a16dd59e6a1b86310a66a11192cd5ee68a4943ec38bbbb1bb9370f 2a25addea0c69a5c989407a493d14afec4f81511ea82ec51e73917fc6fea950c8b30997a3b5e72de722d3930c66ec245b732c91d7cd1519463307cd613d41204 -generate_signature 47fb94c340262e3cab3ffb03e06695bfbdc2439807cf26620c8cfe4395391309 a5f429dd825be9eb1b76f7300754a454844d07d1627312b14c70a4ea4e4566ab 7747b7704dbf86b1d338f3d49649e43009e7525edb11e684294fdf1d6fbfb708 6a474e70bb7d0ff1855058a03a42139f553b6eae29d4822b901ca2ce149e140fccd8f98e14c7f8cc9182fe2b88d9864644b2dfc0fc26d2477e0baf9ceb3cb40c -generate_signature 6ba24acafdbcf7ca8125c560f585d33fc166ae363adc8f0422c2242f6a2adad0 b27456386a01d84300fe0695bcb594296fedc0b2ea31e1a176cfa49eafa0e5a5 237401f9b747baaabeb8d8df6f526f00a6331288118c5aceb343908fea8ff708 a139daebc42068703c73c9c8c2b5c0534d7f9a80d282be54a2522dd40ff0cb05f1023cf88611610f45abd326f17851fdf9f8d1a078e966c89fc65b8368b1090b -generate_signature c8dc5ec96cda8c740358be0754fdb8644a33bbdb9352ba63ba8c8ebb19f7a240 d9041763f43939ba1b6015c369bf8596a53584891e45b20f4031ecbeff20b36d 849e6afd48fbab23161a1dfb7d3b60151dbc4b288596726ac07b2fa4fab68109 e2613136e1e553778259c66401fef46552d217a95e57d1dc2c0cfb4921e4ed0ccc13ab2422bbd3bef6731863085c2bcfcfe78af97025e5e8306d791a0fa48807 -generate_signature b6d466f81e80d56104e09dcbc4bcb94e4f095ad48c635aa14f74c106ae57b9d0 005a126a81f80baa89c897c921f562aa44afb0a7f317e2cc2988197f980fad94 08042ce99fa8ff0a4c082fc1ba66bb11f8c94ba9ae38b4f37aa050a3345b2808 195e69f5b202fe7e5b096a0a75935a7f8555ea4803f36ef4ef9cd0c2ddb7170827ab7ece6c669cf144794f4acc6cfbe301995a2fc177265c3f258c958845160b -generate_signature 8c442d20262492d6075a482a853037319738a944e8ced7a26c236008348cd06a 1543c07acd90bdcc1ed42560b2129f52db09965732e1ad75da5df80f851e0a30 5abba5c682b23a2e44b9b426abcea8faecc8da15f0d124fa428cd21ab523490a 8cc7857d265d81243eff39e9921aab33ec737487c692237c3e34267368aabd0c291a0889df4f806e79e91256f67178a763afe05460438301a31ae483165a8b0a -generate_signature 6f7951c7ca70cbe7abe0d6befe5a5a10559b7d05a56fba3280266361a919b07b 2a08f949c037c51b3bc036e3779b6dac628a9fa2561087d15c029de399596209 77f07519b561abd91c4bf4a10be08deb641537d7503102d30b8403bae936910b c366fdd82651f709018875c712e5a98c9bb5faf23ef1fc38e1b31cf074d9b00e72c18d2b2eff556ca99db887d6ad230622010fcf18f773a5ed6016c1d19f190f -generate_signature 22b1dcde0d75d009c1f37df37ed0a79e162049ac2ee48681e312244d540b046a e340d157c047d55d82817a49b51f3c1d39a82d38876a05050dc5296c12a2b6df 8fbe0766b15d4fa7542756b1cc33a62bfe2da4f7f00ca30dfcccd7d63d6a350e 0d99f9fa0c628c56a78bc9204b7b1a8f8bc412c18e45f6036b7bb2b65012c207bd5825ba2a48641a1918672c134fedc6a9c3d47181a03b3508a2b5861bab8e0d -generate_signature e1552258578c4238b55b9c096f1da86bc399ebe8c55df9dd6d0468ce939a7695 98557f788cbd01ac0b77f5e4bd2c0aa65c58cc61b49f5842a53f898461d22ce6 7d052de95e7e055d117709ca3ffbd6294edb2b0698b549f623327a7856d18b0d d3c98fae5f8da3441f45e5ae03ff4f1271938ddcc94e2f29353e6b591e74710b476f6e89a706ddfeb1b67e315f58ca516adde708c250393498eef78f07c37404 -generate_signature 9ef760bfb089f081576ef619c98fa69e80bff76db56853759a98359944af31f7 1e2022577b355dee237be685fc8704c73ec09fb448ce3ac9224c7cb26ed7cac3 e9d5ed5d17eb81635b3f44bbde735d506a1b1002fc963369db98c2da1fb9990f 90a58b604d5b1b720b4a797ad74d7da0993ee84b40d88f031d176da5d4d4f1056407367e4902cb87c789855c93bb39c4a74bbc6e08dc20c4b659dead53bfd002 -generate_signature d48118e8850b94a1d6536e8ad26424db5ff6a8856837b5f2b759991cecc0ea79 76acc258e905aa60091cd19a0453c086bc63fbe6a9b9f9713c5a59e4a1f65735 e7eb8679919eec79d95612ea8d4f42d3dba03c92997a8db4ea7ee670bbda580a 8b0ddd2d0e9e9b97505aa443be20bd7bc77038c15100fee0fa6123caf3bbaa0ccc14e332aa3a7be9052719c3e200596943bbd27cf64696d0a437f6a137fab70c -generate_signature 95b23e89020f166b62f1a0ee717a478c07b3c96d697a3a5e1eda873c407511a8 8d7c53fca9f208f436d9b1a921daa731f79e537a6aa1f54b809aa4ad9b33b860 b702f52f1dfe951d4cbafe535089128cfdf8e3c62e95238614d230bc783fed09 5572be8bb8a2ab96f18d54837dcb8be02bc2c65091f3a8f30dd00c0907a46d0721a0b578492273c43dd256a7260690831ddb1ac3c7924b4f214140b76aaf4107 -generate_signature 73017e127a3cd7c8a9f97f80ccfe6804c5bb683b22b6242a307aa84f2b2e5663 f17f920f113d54ccf8790951b2963d35165c852ed0cf7c6f5da3c1af2874f7b4 7dd05f2bed1ebd7b1bba8060fcad9368c9c114e49ff553273cdca2adbe5b7c02 65a02f3ca050e4130426df233322c51046c0347da04bb4527a18848aded1060db8b12909341828014027287bda5e8bfb10e9e0501c8d077ab1139c84085fed05 -generate_signature 5df7c3d6ef2ed1b6140e0c52d836594ae75adfe243cddc0cc11e253674ea1d3c 5d45e0470b6d249abde6354c3db9e980c56705786c11bef17f94a31cf7d05d95 60e30994ebb7c53ad955faef54e0abf7d7aff8932af097db250fb56099712b06 8297fac84932b47a5ea32d0f030aec465b863a89d8cc8f67d39594bcb887e2011b70b6f270c80731b612193ce974cdec769183dcf107bd8b944f550a523c3d09 -generate_signature 5e1dbdf1b13c9e0be80b4f4e10a2bf29144fa2e66553e81fb50f2f7b55633f1e 4cb10093b5c9cda140ec668a576def490cf7453635bb4921cb9ef8c8e909ee52 067e27cc60b9d9da528aaa8a48ee6cb30418189c8f6bb0d5710dc1074dfbd605 dc5bef213742edc6e1790f5d755b66a09631abca0b31f433806eb7ffa038b707f8b430770c72ce06f5bada556a2d305e56119a2e96577636a108deb5c2ac8407 -generate_signature a08e5b2e08d5f1c70edac02d97b7174e348c6d4bbc201485245e8578efdb2f5b 4a10b74ec6c5ba4cca27a473ff6022c3618b2e3c24625708add88767da62e3f6 50262cbb867b9feef014c6d0f301838bb92b66a8f2109e67d036129096168900 98f69d3e1949a6074e0591f826d9c781e697e7bd4b61a8aba9e4f6954092ab03c1154d32ffd177de61da82df4b051a1d764025f5dd6b2698372d44e04a087c05 -generate_signature 07edba63a2c0fdb2e02bb8b8e6b531d2a542b0674e24f5ed1f3ef2583d2d9eb6 8c1059bbe4a373948ede638667da04f8caf05aaafecbad08de797a804f2e65e3 abdbf74be8cba1d22bf91057a94b1f12a063acd976e4410152a07de48b290b00 b7a94f9645f804354366ee550d8ec34d0cfb9379728b6ba10c80271752d76406cd9101aacdce3beb15f8600ff2e98f7035a1ca5c1c42c98b5700e02ff354d804 -generate_signature 5dfe35eb9c70c55e285e3e9121651110e8daf329a1d3110bb342341f0b13e285 898082df3ceb5d2e96fd5400b22bc169e04d3ad581a63de9ea638eed30a1f63d 1e83a05b9ccc0daeb952e2d6b5cfdd414fca43664ed0ecdd30c5eadf7a8ad20c 67a177c1f3a4a3d4781086fe5a1c452a5ed8df5f3ac32a89c32c54f23b441f09aa749c2539584cbf6927839dc33d08c5b93f95ac7760ca8e0e25d015c7d6e101 -generate_signature bcface283643b5f6fa0140dafff732ff07f4345abf3770523489ab4ff2731c58 85dc41f9264298c7ad08099dd2b20682dfa4c9798e69695f9626f644ef7c7980 b23d6a04cf867f63aa2e6a26784d5c7a00afe68a37af3069248177b77b50c506 537d655129d447345c89dc76ae04ddb1004286825de2deb3283779d188e7ea0230df1c620660d0f4df07c572ed6e6c99b8d072e6f8fa096fab174b2349d21a07 -generate_signature 79444422974fda912b29426d4fb4e8900953d14b8a04b2b48a3370fbf6e0d34d 55c8b685946d2e1720c290ff6c2bab2643e35f6edaf04476dbed56472ccfa242 eabb6c32344af64e15d351011ece8fb6cb2309ecaff2d43d819f59389bf57607 32033a5fb3bc9eca2153666445a6a2b8c699f8204d28771f52612de606ba9500f0c194fcfdd61fff79878871752448d032161d1e9bfd5d1393203cc0dc08dd0c -generate_signature d2c081f5d9a76df2448bf6aaa021331d6e0fc87e31b6fee0bcdcfd499d2dd94e df1afbf7c09126a407fc34b118985ccf41c4e82427e6dcbfb0d3897cd3705d8a 56db60488923ad7e0575b143e71ce05d93e6d273a5bd445cf74ac9d8336c230d c736fd99b281712a1212086ac772cc29b0f4cfec900467802b7e12e5211e7802fcb0541900d9a3c9ce8383ed1e8f01d8417821503576503c2e8030e245030401 -generate_signature 8153ce2a925a6437aa445072075b5cae447affcc86b452290e5aadd91e446e9d a504a848e87137e24df67b8330d7dce41efb6a3ffe99ca69d7a2775bb4ff66a3 58a649099831b7343c6800ced12204ed25408aa9b1525d2f8aef6b8a40cfa908 985b0fd42c384ba1376b0fdbffc9a2023cb5201c74465e963c1ac2e526378a020171ef1b0c55db424db2b49a358b9b081141a60ba98f7086b0c02a084dcdb20e -generate_signature f85e575bcd9427a761197581a05d22e3784e602cc471d88ddef5ed4e6ef26ac1 e4582873da79806344aeabdac709ce261fbeeee546d0068c3d17a64527aa1cbc 1090799dccb3d243647defbe3ae894d316d68d4539a134d8aa86f3a4116be203 8ef01495537f359c3ba907a589b2573ec7815ddf3a036fee401348c6396401037af7d6845ad871f268e837a3f5f787d3aff09996a1671de340a522db90e2e30a -generate_signature ac157e1a76bd72924e77dbf97e15469a4da1be6d154a8974aca1d77da7592e93 f4005a9e2341967cae9307f262d87c064163d0997d9f039a2488fb044d449604 7f8c5592166b6445aa14fa0b765d3908f8ac9af1c6de0f01544ea95c487e9c04 b0f7b82f81ce75f0efc950dbd91e5441cc5ebaeded0e219f1e27c284ae0b29021ab13a375ddcc82188e714809ced5c71cc9421155cd8a55d5c327ec5a3c8340c -generate_signature f135a8ea135688e2cf612a1517c305ba88b556d3d0f5c3cbda706bd485993c1a 5ad2fec0008abc302e993a7e03a99358e7113ad6cce435c81d440463e39fef88 5a0e0b8724a44adf7eeb27ed8cdbd6a11d9f1a00d0729f6325910ca6783dd500 ea713e35ca9040e21cbfb9cecbcf940f6ed77929297c655165f874a8b75b2c082ab4d576b1f2628acb419b520f9d3e47705842a69c80665b4562492a3f270803 -generate_signature 2f37a11507b767b81357f9d03bce2d6f92cbd79af252ec737a2126e1f1a59fa0 2030a8eea7a816364dd23b81655dbd00e10edc8f40b5139cc49506db9fe12c44 907f88dde3d47b78945ba4180c53fab25024bc009aa678b34c6958c3af14a00d 85edbd0eedecd157d416be391fbcd59c075df166a9ee4c6f5c196de9da006309e0aacb072e5279259b7c39357b4db00bb74f60f9b543481aa6e4047eba52cf00 -generate_signature 40f923c6bbde2b5aa8ae568a53594738edcc102ae77d2eb2ba13250bb3701aff 3d0d965e35f87612a3d53ac8b62ae4e7d4aa2f1cd3a6fe3a258ebe91457a810f f88b02b56a6d583e5ee52898fd7124270a1de3aae61ddb45e3fd4d272ca49e01 cc37543dea1809a63cec611b8014559e7ec8b809be43b555fd251d095165a2032510695f9867aefa456c6c97c2548e3f931783a033d756a90b984004998dea00 -generate_signature 3c4a9e9af77301f53f078bb5780d71d3b14e55457fb26308647706d88e7e8ec9 41a6ad8332efccc56dbcfb936c84a1e608467d9f58aad447e7cedc9c091b8f33 6ae28d2f4d72100a1638132f6c363d2d65c73f2f6e9e6ef883775ce3eee94401 8ed9b2162d0ae8f284eaccdf5054062251e8683a4f092b1eeb14ab47ecc5bf0d38ca546b4a2b37a58763e7ee5c97bc5185e0baae593a85c5e9a7e26d0c491b0c -generate_signature 6e361cb98046db1c58738f54bfa5e24003ad2843625089084112d3e956781da1 a40a310dbf5128eb712aa9ee5d339f8c0e78c90e8e44977ec83a6138b6191627 ce3e7483fae52df03b7c08d8f30fa43fa505f0a7948889282f5e46b0882db500 28ff04002010f15563ea46bc3bffc3be7624f1f2c6356216af436807fa5a7d01f6d5f737e8d24fd52cf00da44c2af6333b3b1db2c356f9b6604e5171906a280e -generate_signature ba7df37a2d75e962d42d31a91525075ac836100ed1a78ae4e978afa432a1235a b4ccbeb6c17e719c79942b74b81a3cfb5ccd63e0c06b12fa88f9e90cd9e8a1e0 a35bbaaf8d8fcf421d4307e3a52f63d382b8ff35c7f6e5f7a6eb0a7adfd02f04 8cbb0917f41e745c78d3531b8f7e0ba05bb0ace0ed6c89d257943a156fa90604a9b1abf636c591c2f355718dbc203a22ce93301e85027a99c4d6630a84afca0b -generate_signature 81599211da2f1378330d2bf7f86e0686947fa402578cee048c6fd648a909f2b6 865b484252a23a1697c736ddf81d44674d9b51fcbaaf5bddf88061402ac65189 8509f1f20c25d821fe6057863277222b54399eba3712721c82c174d81650080f 598064b49145d11cd8d1ab8d2d0e713422a63cbf8e518017001a17701cf947096649918903eb3b025cb489bf9005c395c91ce69c9e02a88cab281d11b2a4ae09 -generate_signature 9655414428a87d11c0bbed9af0bb32010c4847cfdf75ebf4a8c73c1d5500f44f 53bc48c7306182d958147ed4510dc3c1e5b3ccfc16938f1a7d757ce7ca2fdeac dc1506e5b95e1d6e6e06ecceb4b8cfaa00daf3d6fa26fb672374ffeceb0cb505 5d34f44b0fe9495d3dc8ca88b622c59d425ad572ac3323b66de955018fc47b046074b2bcf0a4b9e1210704f289669d66fe3c249ec18b81f94b440d23e43db50f -generate_signature 3e1ad25eb7a1dc33c84bfb09d73d946aee497638ea639157e1eed1739bd0a84e b507cad326ce68772dd3cb43c31bdd70cd71d722bb551d209d53449c232f5055 c06773a6b15d1409ccf3bb4182509cd418fee29a93d966c511809b19eaae4d06 ed34e9763f481ac9f530a2300a2b2408f17d2ba824238b5b001548911a8ae605e3d0f703fc48090f670131215ae93226aa201a3c2b2c57436e29437ec5176407 -generate_signature 36bd8cd274466d762f8bd459e5f32dbd7662fed9bcb44b76276e793701c7f5dc df9013de104e242aec33250ee64a3019fb7ad37bc06a421c60ac21626c9ddd6f 6342d56599a678fca61f584facc41e316f0bf152165767f50e3346b7ec74a208 098e0c722c4e5b695884e0e9d8db72025f782f300ec65d50e28d673f07f5e30ae6748341058b0710230d6437ff7c03f11ce89e0ae2c699880d008e1372d7e80c -generate_signature 829f725bd1ec12e3c244c4b0006499fbff693b33d0797c043334ec591a8d64d7 fc559b71cc887e98088cc873b9d7c69d104276e257cb28197644f49e8854e69f 36228dd0b09d05703d7f8b5235b94958439b8a95b43c8e09b577a0fe6b26a901 b26c169aa7c95e62a447bc55d495cd624a36446066a5fae340f58c3c0af69108747c9322f2dd55deb910ae261d458da4e8977129c1648c9ec14af92477c45605 -generate_signature b2fbde52043e979fae4cbfaa1fc218bb87b87b21410c498be62c5b288f0a59d9 fa437eb7611028c6141280c056fdcdd20912575cb57d652f78034f7afaa5e0a1 df1288522dc2cbca4473941856c0f24952f69b5c9efbe6df24396ae25ea91105 220618c019f4e7789d59419056198a5c96385545dee117b50715276a9def240caa09e94601ccf2e5febdd31ef445b0536dc64835c320da8f5d557b05e0ec4902 -generate_signature d21de662f0d58c9f746f0984fc3691a3d0bb0776e539a2282207b8ebd8e5ca85 679397579f31b061479734bc5fb2218c577503f35827c815f22bf53761c272d9 b04aca24dcc7f0c6d04c3c676a6efe8062fd5618d16155ba2a0e4d0485332c0c f83ff67418174b6d532b1e19ee545980a04bf777db1135c4464f8908288a6500f134aa66add8a9d075f40eda38a673d00d3df3cc1c8b77781aa79d64e7aaa808 -generate_signature 44c6eecba5511adb489dfb05815e764f933553baf1acfb6e9cebe010337b24c0 a08e4416e02f056c71ea72e2eb1f8a89f669fbc225a7254e4c8d0f8d39ebd1d3 40f60e9ef735a23ffe2ac767726ae743f2844fd7e8981a96cef85096359c230b b57c11aa25f65165904d994ddc4c067b55ec61420982b193f8e81394958aa20865cb62037440212a43c99d169b1ee478195f9430f887b1e6bd3f8b1a84618c02 -generate_signature c36bf112fe3f11d4a33a28c8f7e96e18c0a561eda487ccc7e441439405c5b339 5d764a790fb3442a21aef48510e95dac552d0f34c2b03289ab11faf21623c451 ec45f5dcff2cae290875866dcb122645d1b88cece607be63783c3e088cba410e 518393c79d1a3a0b5ae250a27c0b7ee8b82a914612b272d9fb45797546fa0a06b185cf414ca709da8b821ec3059ced87592ea4e7342884e9c668e7ce94ee470c -generate_signature e642c5bd2b451b20aab856686bab22f6987a7b5330c2491ad9d43059cb5b17a7 e5c73f7ca70afc4019ba044c177ab7ba3b57070effd788ec950e23f766617ae2 1430d7db432dbcf48c88f489017aadb6bc4a4dd30dd68245e281359e7bc6610c 46052008fcab7b25e4359e1345f8be3bcd3a1b95c195dc4fcfde127089ddbc05a4aa6fe16e0b9ac5be528cbe4e0f9fe1879d056ccdf0752db1938127178e1202 -generate_signature 0b427120304ff23f1906c33972a5ddfabc833455e1c00b4ad522dde15f401237 45df3d7b2ff3ea6a638a360019b6998dff96ba9c11ec454fa763b75ed9e21891 07290dd0ccfcf302d33d38e5b943f338a40e63fce395f47984e7edfb617e3509 ca2c0e4daaff01c720910cc7948ba52167f1fc98c1950eb80a125f626615200e477a125c37a8f71afd9ae9ef3a949f9a07c9b252ee26f7feb32fb30ceb62330e -generate_signature 5ada550217e164eb59ea6506858ae1ab537c9cc15d362f94570255e558911490 891681f48e13a2f0f2a7e189995a87f9af3f3b4aad553deae50f38455eae34ae 9f12a804897eef85dc4f89885958acdab399cea0cbb6c7bd2e2b40185bf53201 33ed93954590ebd70631c2d87c5360bb086ccc0d54c861d3256e697f83ece3006b87cf05601e8c37c8f6489a7f9b44c6ea6519aa901d30012068dab793feef01 -generate_signature dc200cfb46b99ed1789b022df4462e679fffde969ef75181470fe77d2f659224 a8987ff64be78dc7120fa7c7caf8dad90e4b8dc44597769eefc67df636c26062 4f5a3321915e3c9922eb7704a158f64d27d32192687412d28748d2ed4636900b e67fb58146ddb2ab786b9b6eeec4d27f940f05fae39abf908d3c40bfccc53103399e987219901700c5682eaa9490b025047bbc1177c69c7e9db460c7d20c2006 -generate_signature 7462d6b416e428b5c0ee3f90ca39575495955b720ad0e7e4c2955c1ccaae1888 36260bf759635f1628c16ab8a85e22d20d9554f6811bf2d7d2b0e724088c8567 2f37c7a26aabe14e904c61e256d5e2857adb8e94c3d28a57950bd68b5677f302 359e004fac509564560233cc6220dc6feb544e1f001b750870a9d05a5b70410aacb04b1c95236c62230c6dbdb7b3411e10c7f24248e9993ee7bce144f6657306 -generate_signature 51e9571c4b6814a631509d7b43dda8a19fbd9c5536c3355c3b9ec28fc7d4aea8 cf237efdc47a5fbb1063f9851704177751b2bb6dd40f35a35bc49acd95e11370 a37484d7875c479c3df673457b1d82b1d8fc80df5274aae7d1aeb39fe2aa310f 5157c7399de7cb42cc16040646d8f81fc98ae57b3247855858dab76b29ad9906b18bb08515693f3ef3079e76d540f9e36b172a39adbb0940f71a4cc15cf5bc03 -generate_signature 512f462d431403b24c18924eaeddf9d911d5c614d2a5e274c21933a136cd8e10 20f1bb98259c891bfcb5e11793ad38c2ca863d739f7a2c53bf5f74c9472b8fd6 8f92a6e203eb6d8ea988a11177aa370bf142928fb93c466bf18ab55b50492e08 60de330b3a97e9609bfe15d805202ab8f57a41b569b7538562586ec2d30aee0933bcef65a5dab7377410eec4834243e1997668ac9a2a86710549996a93306d0d -generate_signature c67ceead9a67e5bb1211538a0c96cb3605da36f1fe2a8664de0f134781a85889 25adad7475f476c9c02b9fcd3c7865d261e5da7261031256dea250c71f305bf0 8a451763e81d056b3622ff5db2432b5ac52a4565735d1f399d7fe44f409b6109 fac0c7fd890eaaf93e177812032578a0caae37b310555566847f97305bb9d509737ac895d56cddb77b09ffba78a3c1778ffcde410115b732b11d2f713224330c -generate_signature 8744e27a87056f551083b1240834edfdf4576a4b8c4db7e93c03bec8f714b842 ee8c02e0eb978f310eeca705aa7a4bfe38cc10a9a92e8458f5656dbcb22a604f ebba71caa77c769b5c0920033be75dcd8e1af9a19c97c4cfad52bd52efb3cd02 ea958d0b7abeb37e780241004e2fb8db25f6fcd886121d22e9bae96ac5ebc40902ed767cb77f9bafc9125a8a2eb2930bce6bfd43584165f93fbfaa4639f2ab01 -generate_signature 01b5d99e68a458de21c085b50147ba1f67b0930144fa77ee5d4e1d37e91da1aa a928416ec7f0a87dbded403c87fcb073528cd4f90651ab9e3bbdd14ca61b6e46 370f367e7ea29b50ead04f5195c6b0486923c4e7109c8fb79b1059059378b302 3af8752964ef33888081ed30f3d4120aa6d7b1df1db8b2ca246ef21e68aa650452341252e93e7d5e906a7ddc06a56b18f5f99f26212f31e1639e00c267846503 -generate_signature fe682bde8ff2a30f81d616c05b9b356e32b3b6f11fc4f58c133d7b9082f3fe84 dd191591dd57a04d33124ce1af1ada602b3f5304b15d4a90aa0732d72742f501 c2b494b0c63900df29f46d4ff2e56a644aee5cad4105aa0123bd9809d675df04 f62b33ef2864c9d3535938d4852a388563ea188624c7352fba3d557dd9f568028e00056bc1a022d0df98dbe43c09ff3cb012a4b9d628acec222c2b6ba749760e -generate_signature 96a71cd74467e3b95e4dd8874535ab8fe7710ec4956aff40dae1e7849bcd4c24 c55e53a61395c80d8adddafc2bb1224f1e64ca8014fd13d809c86d7b5e299e0a 84356cbf4af292e87e3b7fc328cfe45d5bab67b784405de76533fa4fe049d702 b9fc15218a12c8c4e3011dcef1c0e9a223f0e1c0aac84727216185769e887a0a6585ea87b4ab6163f897d47fa2c245d1826fc1fb4fcfd301c8cd36eff1321a08 -generate_signature 7332333e4bedbc6291e64c3acb32779b6bb94493e83e676da1ea8e7ac593cf7b fc9dd303f58dbb87fd82695b463e1197a265279031cf9ae5a6a7e6dafe2f0fb4 9fabb96d4292617086a3a9c0dc084bb6118f73eeb602a6fb3082a37fe6d7a40f 820c0611ae2d1ed5b63426b9a927de46ea98aac9fde0d5ab4e1528da95fd9b070bf88d17158fb76c96fb0ea53873db6d982256e3c7dbfb2ee32b47ea795ac80c -generate_signature b15d619a2e307fc4e1848688c3388deb8f2e0eea38328cae0886adeb42474204 5d2eedf3536c0d1857d75190f3da8f9e214dd170790722c0ec90b5876f46fa1b f107b7b3f7e17ed25504d36574d01318cea43a84b0c275d097ba0cb83e872601 a80d36596e68768a67a49f82eca4cf829305da2881cc5f06c4cad88a6702470cdc184b82d209b4568f7f22c944862bbac4f336a004b561baa78a91f74872da02 -generate_signature bfd9fa1f474799d11ab3ef3abae59c17b300b8cb899b2fcbfacec678cc77bdb2 362bbd5e5c95c7e1adb900a3c6c5a67b5f050e58ceb6917bfd037abc61457f23 4bb72c886f3d978943afdd5c1e53882caa6b323e0c0836a1485156c569c2b604 887dc3e64c5fad7f37794818ebf4fd319560b637e5387907dbbfe8c569e8de0be34e3f2728226f28fb66dd8f6be93da4223d289f55c50b66fa23f30c0cb7d504 -generate_signature 3b8254fb834017e439bf4c9db7a54b8d72cfdfe734e64e3db94bc42761e52a64 3f1a3561b18915b34bf1b451fa01ce6b9e2124719b4f3843da1caa106f5e724b a9439b2e768ea849b35fe3e8f31fafd2460343374600031aadc7a047d8f4650d 570b9687c0cdfa89cc409b28bdd896e71d13a35169ab67eff7a9cc0fba83420e3dcb2ec7b42c81166d1c4880f4462ff84473c1c9e3889c24c4caa8aff322b50e -generate_signature eee51b7ed448e86eb34c493bb7cde32b654a2c3b94734b54e313b44705736194 9cf9b0c581562977e791eaab88f9574ac7e7b173077b3e67283fa00ae448d19c 7e0763abac9d638732d9cd91c7662b13b14067fb458dd8c13329ba76d9685a01 c3a02a98e1337aaf74fcc83c83ca9db4016b5bcf2db29ad1a1281f267571b101449e9afb50bbdfc780251ec5fce344118c65730a54c99f5c30dd4a9c26c94b05 -generate_signature 4d0fe60a2d913071d88b67d806b451e5217b66279c8a366ea50df32dc7f019f0 30be371fcd62d0aa789bec1511074649e012bafde098d1997867cfd1a1c00020 14ae35724fef1e5136b485fd319f2ddfa31858c2f6e8718016ac61c7bc09a00c c30b2d22bbdad1d644beeda9621902da4336a0a03dbafbf707b681e983ea920e071c59a3c7a01f6f2c2c1dd4d0c1bcd089b02e509402579e6b96511d02f4f204 -generate_signature 52558464c45be054502fd7a95873356906175f45d96a123a679c61293ea82c59 f2df74450892e64b630c1bd242ad6d6eeb74eaaa37817359f0ca6ceedadac351 fe2330bb3f97ebcb29060bad76f122164f4f376f1dfece45ec8e1b74a9d73304 96ce07548ab12899a7ef013774b816759664c77b66340d9fc47754071d0bec06ce4356b4fd402974d447522307f0a29e350997d4cba56c7fe09f85a56b16cb05 -generate_signature 110cb3d607cf3cdeaedf72bb5e757beaaf33735c4e0a6c9d36e15c3bc44f46a9 299e119e03f8698bb3f91c0383fc2fa3566893b430c4181ae75da3b6db52a14c 5f8e1334f8edd3031ae48b6167b69ee954b2dc825d96c1a66115f3e0dd19bc0d 3519a213bd6d92898c750739c323f9b3dcc5beed7d87b374bb0e8efa5b3fe40bbe27cf0794652468c3dc55aaf4069c38b174043aab8db2553579e6468701b609 -generate_signature b9c72e905e48aa099d4618aa4be67625490d93c298ae9a3ad50bd3051825ba5e a2d1ca5b1280a41694c3378e2346b3f9ec16d27905a7d9f1ade0aead412fdc5c 2fd3f1486114d6f267cce879108ab62839c678eb46a846383c25904f47719605 38869854fbe5866df44cba649eec3ba9ac48b74a87fd445b89238b9953cf5d05d6a8b9f592e02745fd11dbca46b4026f8f77a4f52c37e3ce702f88514e54360a -generate_signature a61d5acf702d80aad1e60667f5186d692d03c291015af38ff48b3f627dfb6c93 794e3180afac94b50792ae3b11ab01c8ef4e02973b3c2efa5b3110d2cc5bc5fd 89a145b6365dbcd67ad36878174eab5d3278b2da8a0724fc6b9fff3dadfdf103 c1ff8f8badfb9df0e5618d7e5615184536733acf02c4b7f551fd1ca846c7a20c1ac285c6dddb3b067d30c57e693306dc4333efa84ac82b63d7d546a67fe58902 -generate_signature d0666d9f44a461d56c2c6c35bd098282042d7ab5b4138f80b22b98b940df2543 c97f167d5fdccb9158ab96824c60752f6a1034e163b96da179cc42a8d6f8cea9 443d72bd61cb9ce36ac11839333cab61bc85506b91dec9ae8d0071fe9805ea0c 69625e0ee4bff373f0c0fec580f93fb195c69c56f78cee8544429845c25b1505a9655db1ad290cf2a3b7f4b7b0ddac7da963fa0716ad5d04867dfeb44f6e8e05 -generate_signature dd4e6582d6f65498b6e6dbf35f5dcdfab52f90a8eb7ef7391111bcbe7066929e 1200b5eac5cf70e9c52da437ab1d7da3ad5f951f65baee0ba8aa02ffe69a3675 b72904a3d7180d9c2c16e090c52051ebbebe0ba8376e2defd850bdd698fb6f09 52d652dcae18e38ae04487e6dd838bb836baf5008a00e43293a972e10b68bb0c307c1272212b6f43ba86dd609aa80024ef2def3774c67cc7375d3ea820937d0b -generate_signature 6a1c4227f59b75fd001623aef3687fafa8eff103526023aa6676e8f08fe3c15b 1613c592fccc2e0031487aed9aefc3eb0b86ffefea0f014173bdb28acf4e8bb3 bb93bcb48a4efa8ae0b0fc9393653f2696c77603e9fc3bc1956473f04f64e402 aebf47ce25241355f9716c17a4caaf93b0c30b47dba5e76481ee1353c45a010b7f06061edbce4f9cf24033f0d55beee957c8b79fe534f3c519337ad7cb7cbf02 -generate_signature 2677f450f708876541bbae6ec7030aa87d2f7599bb4f669c190b8df2b62daf66 7a85581bd21fdad5609a84478a9e61a084ab78021c7bb0fdcd6c9efad8cfcbb9 26971f12134601802ef62ff5dacb99e5e0e08ea2455861e63af3cfcbb8cc050b 1d1fb428beee08dcdd0e056e7dff12298596e63139e2261521e1653c14a70b0795c6ecfc6a663c821aaf09f81d9453cfb57a056e7206eae08149aef628abb809 -generate_signature 15bbccb57984f152d62b0c82f12d732b75068118fc3062805c157c33b9f35741 cae0a132e7e32e55906039a7f510d940cbf4dc16543beb1004b636866635a5d5 6d16a1ed565e148f653ae3c20a978dea4f2bdf009dfc8d9e66a201c9d184e906 ca9c6492373de8523df5acc2995d5e64a041b14f0260916e0413971f0741520c9d2e834fc706c8f1c3d34ef90199090d28f5f18b08020e905da1c2c1b463c005 -generate_signature dba1e5cb897f3a931ca00c61296c26aa6edaaaa95d844e8cb29a8818e5d3cf40 8011fbc326c27242f794f1c93e7e616ae847badaae42e30ce345ee2054752490 9b02000c18cbce2c5bd97d9e572ef1124b710bc4bf820b5e740c6f52185fdc04 6bf32ef5a08b67abc7a6785e3d9ac927f7350aebc28fe816558cff2b643dea0a45c4e555b9c4d118ed8ac0a241b9a7ad1467b50ef63659e92acbe306d7fbe307 -generate_signature e9257b43c57e17027c955dc21454cdb41cc59ad29307468ecc595466470519ea 5c841fc7705e9f0691444e3833e84b946f0978500e66dd187b6b4011e2bc5722 ad7c20800b766bfc8d97bd71fdd21b3390f9b5dea4a763ec94209e832add0c0e 2762069c8d0c14985163c29a3c826787f2b7ce36cf7bd338955e0f421fcd070cfda5f2246426e0ae016d0bd14a39bde575d1824e262ce79da377856b583a6b04 -generate_signature 49a91500dd7fd49dedfc127ad940124684ceb5d0d298fd708906728551ce059b d19aa3856ff99afd3a621c339cd5688ad3d108e313cc6a12e76b857a56c8b254 8120cda41048d6102dd2ad998972b345886573e7caa4955df7c3b6632f96a50f d929b5ecf6206fd6106c82e73c0617760c6304d91cb0666a1f13659f5eb9d90658dba6067bc1a45030f7ac837e919d7493cee33c242bc3607d7393fe08b2f20b -generate_signature ee3d3c574231cab4d8b8de5b127423e88ce63d30ffba94e44b7cb52d933c4551 c7c4c1ebb9ac8a9ab8c71bd032c95bf4d76ed79fe8ac53265f8d2e0ff0913e5e 685985d5d94c9702bc73c58eecd4f2c69aed4b5ec1136af0636ee81d94c2530f ed0fb86ed300cbcbb6e08756005255b7c830f4fef04866543d852f70a18d9f0fdfbfec42faa466e4ecf82d4a4f17dcf827dc7fc77cd2f42690f6f5d06f9c2d01 -generate_signature 1473d8159b8f7ea02a7227be5ddd874e9c6e3c114708f7c46de729a81f0379bb 7d995c9eed1ff652f954617817f772dfeaad1ebb968c3b4260c22bcc6aa2e764 302201443d6fcdfd98585792085a7b7e804ea4a79bca8fd6b1d24d9ec30eb10a c2a4b12dc28da79accc33ea50f4a3a89cc2a34b114c84741c2a412dd6aa60206176084d12503129ddfb39667911e7ee96b71de75915bb51a2db6d6c1c885db0a -generate_signature e0b62a10aa27778f06ce587a439179f6be9a444588be2fc6c61fec24e4cbfae9 b31d7350dc561bdd04634b1c65d4d07721b21be061f4ea6779a1a9f0268830fe 033f706a513a3102241553f06fcdbaa4f1899f9776f51584495e0eea1195c906 3256c679796cc12208f70c7a3a897c90222f9aa0c8f5c7bf5baab592b2ed7e0abf0b5ecd176ba9aae4b89cbe6937242d395d68de45eee745e7d1a1990b1f1a08 -generate_signature ce6fe431c9d841f2f7494d0c214ed3f4d470b1de679e30d0980137dc8cca5369 7c4883ca0408e5279b4333cd5970366e7d2cae46e88bfa9efd5b9554b8677438 bf876c480666daf828d4b5aed26b853372ea58355c370ece915600f127dc2a04 7b028a068f221b9e086bcf0b190563c6b70ee19f805f28d1360229d239060a0f3e263e09505a28a2675a3d3f4981d296d1be63b13048bff9042f3428477cc109 -generate_signature ea984e9cc588ddcbd884642c0c1eb40dd85cb049f22688a80f42b0835b3d2721 4e200ad1863ddbec552c937ea0f28577d93959185269c21b5b0a2faf11738582 144de89fdc0a762c28f54d7cfe1d9c7c1dce3d1b520bc8e6b1df39405708b505 8accfb3a1e3f3474503b36612b54ea4f5d25ea35f23e07f571482db5b3a0150a46c0ef2a4b74e2a84d3ce9bda75053453aca35d360064b14c216ba18b115bd08 -generate_signature 298dbd2126d0276c1d95e2e5220bd529b25c16ca670094a667fd3397c7b7b22b afb3b9bb1769caea17f4102c3e59eecabdc9108195d33f0129307af251f8f330 f17b90b7c4bd2169f7f1f5816f8f72a2bbfc03c4617b957db3ffc04b6e42a303 fef52b0852b12a2f9b9029939b23ee6be96171025a4c9c01f39710183456330442a8a3471e0acbc221ea06188ccd73dd76c6c2be87412d81dded3d9eed3c4c0f -generate_signature 9c5c87cc348b763b29665e7e9fc83c2ee425c57bc610856179879b5fc644dcc6 67894b54c0628bb6c56ee847b712253782a9b02a506dab960a5b8eb9e613215f 082fff76e7d834c3034b37eba3aaa231b1b190ea3514791beaadd4b3daaf5508 6c154567386602e698b0e97befc089b8600d1d87248554a9ecdf1c56f3e83c071041058835c497c5fe26d2103648f816fa5c08560a7edc725f2c643bb3052704 -generate_signature ab263c0d9626de5860ea22e2f999676b455641c23e9edd517f420c01e84cd2ce 1ecb565cdd6da4bb6f6cfa57e75940630360f663cf56cc716f1b949419f9b6e9 d22ef716713fc60b11e619292c44ec432fe7bd4cf582544fcf2ebce23d566009 3ca0890d5c72635b0060612f0d6a78383dab96cd050164b5fe2698618f57810844f4e5b6d71252566fe40345351a049b90fb33b360f75adc6aa2d04a3cd30108 -generate_signature 2acd9cb6a9418f90b507b15bd258878fab69afebd0cdc57ade4719ab89be32d3 814c8efa712bed141ffa2e72b2317dcba9e917330523946c2a97e8af3f992fe3 199e9c09c85ff00f9431ef41d7d9a56aa05cb9d09f548d2fad7a783eb2faa400 043e7c233d8d74cc9448ba023767c896b4c24b6222ee85e7089edf7684431c05eed29ea1d580c2bc32bacf54b566c070dc441639636f670372b18235ee450a0c -generate_signature b8aeb16bf5d31ae4fe7e321da12d8defa7923946e197390f0a1696526099cb03 eefaf0226cbb5685e2770e2b72a0de26f30c9ef5673053fd64e762d9efea370f 4e524c57a12abdf684c05bd99eab65efb947ff2a50b1f27ceef7f4cd268afe05 7ea54ff6e9e18258a4eaf50aecb8b8631e5585d0c5fc0455a294210dbb4b81031c3fc6b4c3b46875cb4673a76479363b0b69651f44f46626601967486febc007 -generate_signature 4fbbf6dcbbf50f92d5f60b467c9bcd4921b756691660b2ac73b256e6b614acc5 0453e634c6c63a0dd1a999307b2c9da357b54fc1e89eedd1240f6501475d35b6 ee840259b97e841cdd17946b4b4acbcb90c7bc05996fea802825c62f18c00902 612d2ebe0aa2091ad0830f5bd38d8aa853a575447fa209b23a92d5433ab86c08e00ae2d043720912c08ecf3fc9ba8b632ee79dcd7625f1ddcadc4ef96bf5930f -generate_signature 7038946351d9332520e91640e4ea8aad9642e66a9661c1e62a519a3e03d24f57 9bbd178c6a6bc802cc56f0565ba7ef52eec02c1f0098a3fb1a958aa1885e4d63 f68267ea33101df63bb15fcff1bc528b032d770fe69a0b6f0341c614e7ee5d01 ea8870dcef734dde892415bf0cdf3da8b692cde8f53aaa87e5a04bb29b31a00bed141061199d3bcf302d22cb987ea6cf116aa41e06e2ffe87d6dbf65e868710e -generate_signature e8e5ae2e4f4f51cff909a6a73d0a24be6e049cdf32af893b6156a9f3bd7a0613 54e78d50e27c98418a28d0972af5ff4f7e6a5f2e212d792871f4395a4e0842fd 873ea1693f04187e4b39eeb4395d38e2b12494f3a426dc3ef1bb58a2c0a55608 074767a64b02256244ad61512d18e388b01ecca6f1cc59ce543844c2d5175b046b54b1df249f9a76ca88cc5978549955d434e90d8c9fd7bbd90a5207fb34410d -generate_signature abdb0a9f87e46a82240bcc77f5aa5f8c0981398731f7bc392b1e7f2d569c491e e8551e4b00aa9a74084250f33285a955efc56a310bd287d97ce3f213eb51f97d d28b26262099d372ce35d41e3a289d793b425a62205f0f22fe348510120fbe00 372168122486de89b42e7212bffca1d12f8b823548e546118527171aad5f0a00342160275980d62b506db43eaf8e4aaed7f860652e642640640da626d7f0db00 -generate_signature b6e7c69d55fd322626930ec53729cc2975b911706721a48e41ca406146cb63c4 58a63d82e3245e9771435211f2b415c5739c99370d08011e0baa1b502302b16f e1e4f2327b82c6fd7fbd35905d91056df0c8df6702eb282487ac55b3801adc06 92e1da2b471004dd692acb02de5c81e9894026b5110faea46a507ade972686014d1356c54064ed8be0767e1fb384117adf2de59486249b7eec8cde356a72bf09 -generate_signature 98d1fa8e6b508971d25a7147eedd2347f076b72729a35f9b1a81203ff85c810d d1e1c86421a34483152b3cc8bb124d140fdd638210ee1bf0df30c80b54e61343 5acdd92d2c8eb54a0ea6d466f5c2718d703a5108ffd352b6f8ff70f2baa9010b cd14e80fae2e57e8763ea261ee8047934e99e56ed1714358c5f467aa4c6762084a886cb3e8d308cbe647b6f03550a0232cf9cc7792fe74e068248968665faf02 -generate_signature 91dcff5d1d7ec3854de213306e4e744ca4c04176d4b20830d1a2012e6cb026ce 15b09f757df19ae41ef4e9050d0be8e76c151b20a7f4f792748f83aa14b7e0bc a9e865fee7d1469c369dd683d7808d5ec65cd0f035dd5e09c1eb034a7396c305 ad16e6770c959eaedc99847d78717a0114d4ea2ff2a5aebcc9ffab231937ac0f97e3a1aeb7a08db20eee6cb56cea3652bc124357f1bce24d23c8b59490e3fd09 -generate_signature 9291ce37db25aa4c373266a7aa25d585206492949c8ca4671eb7e470c6a85778 9d35b6133fee1dca194f7a64e8c1dd4a03a76e3805cfbcf80c9c42f9a45e91a0 6716b59ebdead7684c13ead0a4aef3fea77cda742bed36b3dee1d66a475d0309 adcf1f74fc81b559c8f283bf102539961b601efae456613e178daa6071135402aeb0b8995923906a7478380d5856bb1c685d09d73e77726ad3140e392f42020d -generate_signature d2dfb2e50813260e6bda0a4150ad893b32f99b2dec47ca8402f0ab8ae30603cd 7b8bd1f571d5083a8316fd99efd6a3a3dc54d87d78425ce1f46185a2cdb18a95 5d76e12ac8050665387e1c8af4e18308d9f227b0decf22355fefab31ce1df804 50ae30997a05830cecea0569708e71a09bbd80433808b9d603b83dfebbab6e0da3ede4c988d6cc5fc88deeb84849a8684f41ff62b4a9a377a78c8d1101bf6b0b -generate_signature 7ba6dedecefb1d24c087c994a2ce612e568fe90b47eabd04a9f44a9196cc7d06 ee5a5d892f36ba3ec29b259ef936c1a14085caf56e5419ce2c85dc356750231b 648229308a95c52f19b3011f816050496d77dc42afff59adfbfa851ff4da470f 8bd877bf496b8fcbb981a040e3f3562fc4049213e7b4b99a625da77c3557290c8b00a9c7eca02f73af59bcd5f517f57cdfb0fe2ebfeaced50857bb7c1d12d305 -generate_signature e53c575148d6502987c90b32ebc374a2ee76fdb167b4bcc2fd963aa3293a50d3 db099a6c2dab7de4916d3b2e21c2bf1f89f24280c29a4802ee27899089353573 9a34773656675cdf07d64bde08eb98585b9719cb9d52d919d54d70047639b508 01b2fcdd617661bc28e35e737fcd1a9d658f919f605bcfb27445f5a6284b1b046ef2b70f47126243e7ba20ef28de050b2681692297d38ae7931c40a056719d0d -generate_signature aa909b74b14671e3c56b0aa0a39e956897151694e42d90f502f5426606a4bd3d a2481756e3204a51d40c8088e69851185a29c962f97c800a88102b812606ca0a 47d0d3279058134729f860e7e0dec0943115dfe22f052ef7756b5bc263aa030c 01fa19ae7795d56841a2acb8d8e0ee9922e10832ee3c9ece45516afa1ee4470061c5a64d8d5d5a853f2665640559b1e6866f3c8de5472ff69b5816f53ec7960a -generate_signature 09719be164df8ab8ffd5d4b6820d1520992afbf6fa717cec48964f3af489af51 3798b31c7c0f929e70268010839042b76df746c4f46b0e219521eb73e89a0d4c 516de341e7a3e73b96f2a8e3c0427bb73eb04fdc967f6e0d2b87135c0ad72907 458e445ff7bba518d30d20d3eae97975574c3207e0e8b49e58c39bb72abe8d0549673bc9f0101dbbd8bfefc403872f834690c11906d40563a8fac2ea94deae05 -generate_signature 95bc343c1fbc1ac7fc31c617f0944c8a5caa4b91977bad385ddb7b72133b9918 3b510a05791831d9d86d268a3c56f48b40bb3b2854236f4a1457aa573f1e3616 83ed82fb8ea18afb5ad9664ad412d2a9ad5d36119aa08eae5ae48d3b6e979c06 57b902b0934f8f8716502758edc678fc1ce9d145b236b715ae91706e78f8fc05a09c96d05a176e349e966c21336aa2bfa3a69773aa8c5f65cde3d0ce786f1f0f -generate_signature aaa59d9c7b9745cc62775f1a565de3e704ea082feeaabf91897c163e9c453057 f1d8a8a9d1ca40c3c1985276b8dbad02f9b05b308633f8d53ddd23fde382046c 38b8d2d9729218db5231f4e46c2641bc43983320d493eba916c9d99aaac88208 a26c43b0acc8a3372f401bbcd727231da599c0129e05a7be0c5d35fa0b407d007cd0a5334a8484bdc1c060547da745560bbbe325b13a77d8043c1c34aafb2a08 -generate_signature f4433ab65909fad4afe06cdab382d2fe7b1f2d12a47ddf3c4c634bc9cddbd52f 320c05d188d2ecaecc06c15352dedd207739ebde86133f7152b3b83074764778 d34800b40f3d39319a844b8a85c65bc8e7aee303a7b8e423d2efc1c4c48a6704 91f495d13061dc36c89a6b593bf09d8b69adc30b37e5c0a15d6fff53184d1f012f94bff895c797f33afb05e2ddfe982de47ae8189217ac655c05734ef1d9b401 -generate_signature 121cda7dd82e45d586ec16c5ea52fb38026dc84d3acf8b53d9b14c752bbac650 5af2a1a8a88c0e0b53e555a7077a737c2cea050b7dbc0ba3d8ca2ad037c908e7 007af32d54c623c7eeda1e02984238b6799b08db17e3a4231daff62ab3d1640b fac776b68d0563207fd2bc29e074fb0fa0c417c1637b0a376951ca1a0bd41a0f097c31c28f04010c8d310c3a5c13ed73ecf04ef7da7f794fa5b2c872facd580d -generate_signature 4adacb5f6d707bc06145c6c445ef70db86145b78af456ee5d687530713f153dc 2d96e3dd0c90587368b586dfc7954d5d6ecf04523d0797a0fedfba91f65649b1 1f8093909e4e249adbd396d9296980bf6b65164dc6ea679d955b98502622de03 b97963c1c9161edde2149181d3bf17f18d3cb3333771b377ea768bbeb3f69e0cf6771d0a1ff8553dd669165c88db009c1e21446127da78fbd5bd524c46e09704 -generate_signature 86034412d429483e52eaa4f8e3426f920886f5a7302f9733b60c3368f8feda5d f47f3d62360154548b01b27b8e20bb653569dd2e0d8f3e2e4926563b19d2905e 383927c522c2201332dac111a750e15bfdda86b1d47483a11624290569a4f808 e4a23b97db60f54bf1f294e37135294b06e7c6fbef11a931f14722498aedb70b9c1729839c09fec4aeacd074c9feb3d973f3176a1f104ef8c2ec49214b008c02 -generate_signature 891ce05ba0c25b7d2409dd83487ffb0328f515d0dd77bf1c2503e33370f8cb99 0103c4ef2f18527e1630510bc27ff5966b018199c0b82a4a0d62815c74f1d0b7 fb1bf90759993320f4e16f3237c2d63dfddf6594f21c74e20267d61a8bf7520c fdfe25319020489bb7b7620fbe8eff7aa797aa8d6f454934f2151bef4088390e1557c87b3205a07f8e6d14cfc77bd99911e4416adbe8622e5302c1b63c4e990b -generate_signature 268378aaf9b6403f036e24e356e90d67c3b82edb01180fb7d135e895283237b0 32b9fbd425dc1a9e2d6c693699594bc310737d3954b8dcd5f3bec5a056a4e63d fa028fb4484c90b355655014f1f20eaa711d1d61ed8e200b718b46d475603506 548cd3a0d3232bad8329d0e95d97227100736766eef062dd19dd0fcbce2f7d00a8eda6e24c9dcd7c9dd93e364d12be694b874954ae66fe34aa6c197a4d003109 -generate_signature 4c1124a23917f12efbf34c9c90b1a079667aa900b78b8a77297f55e50c3d6e21 ea3392bb6cb736c3053fe90bc64917412190b673f2d1db93338414fafbd15414 54cff91e78a84670f568167ee279e72cb0a32ecf75544b5e4d49ea5a77a92003 04505da749d2eee35cf9c644216dba9941a5b342e23be04062bab1b636548601d88a241af1e02fa14a4ec666b29643bc9c07ca640bcfd001f93075d90ce94a04 -generate_signature 112cd2bc459cc678065ba583a09c83d962e385cb546523901ec4d5b47425d0e3 0693ef1cfdc58c35a00dee5477fa4d8c73422294c88b8f9d826dbc414dec2306 a3d6655a3bcdb2baf44248a541141a67018c9080001457e165ec29d2c3248600 b290e9849810e5835e1d0a0b08049f7940f831a121d098a2d2ea899879cc880398b0467b8c71188455b6532a59500cf5e644d15b408befb2bde9cd2080360c08 -generate_signature 2de379049e28fe930809c36f1062769665d4133d4d668a718b84f9183332a2a4 29aa902ba683847b1b59f09ee480e1814454bc72c48fb3884540d369f1a9f2b9 de444332bd0fcd47f6c1a5b7fc7ae1bb544a2298672288f04effaa04aa7d940c 33aadb26e603ccf402cc1a7f441c063b10c72b064fc9bf9574efcf107d6c5109cdfd79fcf8ce7525d96141cb4e92464eecf51c00af4f29e8d77ad5a73410dc00 -generate_signature 0d2ff1cf8e58881dbe688be73ce844772c6696636469eb8ca9086a3ee2ce07a3 a743a9ed747abc154394fa822224212c3f7cb9fdfcb40a24a142740d1644de9b 08281ab64eca078a437c7f10208d0f5692ae85f6603fcd1d9e8ec2bcf9f64d00 0136e3f553f90dfcd9bfa978da2b9528ef4c55a70c5a9ca71af3e6cb3c9e7f064b2fe3f5350e63577dac7660966cc2b36b73ce2bf2764fed129cb56f4ed1850e -generate_signature 7104100649edb970e649881d4059faee3e51e221a3ae07fc33d944eb6c192322 c10ad6f4972dc849e2bac69e549929822102071d43f404abe8049a529b9c930b 0e060a4fef0a46f9c3d8702b38f82270e76124aac1f6a8e1ec402aa9e7812b02 2d9ece3e8a1470022fd9741ffb1469d458a786e876d3a994bb285366c912f5032cb116d577a457ec4609bcb672f68ff87a5036f81a41ce2b61efd00df6dd270b -generate_signature 995be2688f3f7354dc17d7fbd48e010611de9d84247e1e738be9faae777d20f9 89d352fce80cc5dba45a618468866cbb9414f5fdf608ed5dde3fb446fd665c20 791941512b24ea359aafcf96ae0d9a7f7c24d34e2177eb1822dcf12776580506 811e28243cd55c3f185efa2b6cf544411cb30cafcf688244471cf6111bb8840cb3b9c7b0e3ce1a15d9ff0a70029457a86c2919a68b43c15970ae804e5fc1b500 -generate_signature 77262429448c69ae1fc4c99b652c282af2082b046a227b57dcd724f56ba0a696 9b605f01f72e9fa6faffa84481b9e609f7b1e20b8a33b46b16cceb7f5c0cf411 b912862268d63a977c75fbfe9aa7008190758367379862024232c0cd2504000c 03c825f7d6220da4c28556383578e182da4c266bae998a083f67ef45d8a86a0867ff545d797ed8b7d86952f086cbe8fbc7d4f724dadc39dd5488b067c88f2e09 -generate_signature 382537401e042257c1a0c6062ea8ed101da3193a6cac540d675354f12f7cab82 1c209e65076e9d4dbfb5762f3d1f59896da89efddec6d696a4498303f67d667f fdc2f3443bb4f28cda3ee7cda5255d36a89a983b91361700b63b87db477d1108 74776b5babd9fa36478a8f1139a793df63451c8cb4a4c01caf846b31d9c3560b4b395609f436a68433dc043526a54ac5cbfb6a73febc4f76c6076bd477cfd706 -generate_signature 55b89623ad6f7b1d2ed599d2a04d8236f2d3fb8b337ba9704439193ac7956889 eceb2e5ac867b1603e9c61890aafe7871b3583d38c0a9f4084573497bed466a8 cb1a0a38f92fcf57064b0b1e666693cd1abe9a2b42cd4c7c667c933e3ae1340f a7f8b754cab3e57c1de0343bad94d46cd46cc787871ea186d8093c3feea1580e5980a17520269cd32b183bc15e6068ddc6efdfe203b8303b61214ceb9dead40f -generate_signature 22c3ac4d3466742eef52c67e784050c30b5d5ba7b9235e4fed8190ec18cc50ff 628a568ca8cc07afb1ce46b3f78af000a37dc9bca3fb8f73d7d5e697b267fd3a 0e180e4841d5de78630e900c364dec44daf2f74e72066cc609eb917995a8cb01 2e1dbdd3f768386ce44085d32e74358acc219f00e28bfdc355293bdfbed17c0d4b6bc3da9474caacda128a9d1b8b402e7a4ff1f1d2dd312608e203e2445e2103 -generate_signature 8bcdbe48e967d9e68775be61493e60f6c5488427caa85c0c42b3ca3aff756b75 5de2c451223d0cc7293ca1826c02215c6b519bf3ee0e5b91483990c6250d808e e8b26635c9a1fa4b7f9165b745abbd45cba7258b6420187ee6e4325a89b0e802 66020a81a792108ae070b7878fe7764d6f8e062a323c66b4bca765bef739fc08e60683cff028e280280e5febdedee2265a022b4d9cbbdccc03bee47143a6fe04 -generate_signature 37b33cb52f44b8c43a0d619ba8270eeb2ee657c0e70734a05c2de878f77ab540 7f5ce9f0aff75716477a94c13dbcf878a8a2513920dc40fd2a29814555ce1629 f8afe1e242f39bf2b08c35d6d18aca0900a1e5ae729efe56892919484b5e8c03 e690a20785106553dee2b3d789599dfe94d2c3eab0131aaeb3f408afc0a90a02cb37c6ab2f9f0025c4732477c7413b0d86e69d16dbb4ccb8f003166242372704 -generate_signature 069bfa548d79462cd27ad3720842430f2c4947532513a36f0e012097b77e6a7b e6ebb1f4eae6ec2d813b9213050ac47c0054723d754685452f112551e9e6619a 6cb63dc9d194448e748210d8bae8bbee8e0cb494bcec2b51ae06c9f1478b740f 35819d13ae03277e24bf54f6c1b6bdaf8f67797818da95db1c75a7652f22640a14b45a160d4f5d333e15ab11403d04f457a1bc23c6cf9e1c0c7b81660315a80e -generate_signature 3fbde8fc46fb4f1001ac7fd712760e0d8ada351c2d2c28b6da74bbf9e2795c80 79ae18c7007b1a790ddf2e6080615398ca77b4d06c1b2f7e536cd6b8ddd5a21f 70f8219f8b4d2567a4771051f2988b84f137a4f84cb26f081843908c7d959809 d9e9d7a9ccbc3f77374632d807c2c2d6986c30c0075ee21f634e3f21ddc2910bb1b4a879cfcde156ac9d3bcdd524eb9505647190c07f2b45e9291a8c1ae7200b -generate_signature 7b752d6c0cc3750012b98ff0a4fb325eab31e8bbc0dcd995d9932215386e4582 9519b641893b754f8224bec54da6b5c06730c3f4c7d4704f2999a21b46a30032 0e931c71c9a3789e25f79e722cd6858d0fff3f5198597b056247b9cacbffa20b e89357d5b3c4606102f77e417a4abe0b13d10134ded514bd2da9a305f611a801eae9be74a4db727fa2323172612bc1f1ab4fb074c9ee963998fd1cef2b67a609 -generate_signature e64074ccd0cfb5b56a89cb07faa1386061fc4853b0b2211d87a7af02bc3fc0fb 7072faef529d5daf4fb8663574a4ed86a7fa17e5cf10f09190e280500a216738 d6d77164dd8a1f6859e8a814d2a17367b8a8fcb5e6bdf345d74ad6d70658520a 939e1ba922c1b0ea5ee22a63712187e49d58388310fb12bed9788987ed731005290feb84c4c144bec363cde9039ddd80b7a73799a00a3134952742d7c90a0907 +generate_signature f63c961bb5086f07773645716d9013a5169590fd7033a3bc9be571c7442c4c98 b8970905fbeaa1d0fd89659bab506c2f503e60670b7afd1cb56a4dfe8383f38f 7bb35441e077be8bb8d77d849c926bf1dd0e696c1c83017e648c20513d2d6907 4d8c8c3be3b3165d800a9e6b5b0d683a6130b21b1b03183f488739b463b23e0e74ac4bff5f97acadbdb95118259119e74e24d40207a3ff114f4bd635f141ed00 +generate_signature 2ade1389a860c9249a42e45d32a9fdc29286c8dc0c8ea1216ba786c74517eefc aa2521256174ef6566618a6468c7b8a71ce2dca398be2290148b813710d12f7e 344a6ad0374b6ae8278e3f226d58e8bb2796e89141eb0ac37cff8552b158260e e3569d302fcc809b332a5d6f0d42ae84fb225db9b7ca53627778b9e9923658026766a79960d227ac503e818ab1bd684ebe71ec544a744db2de6fb260324ad80d +generate_signature 5e91901e800a1959b4ec07a2eeaa3a9b28893029a26e8ef5d13adde490e5df91 486dfb4904d81b1bdaf865dc07ff71145d1bf8a9e0c160b9c817315f6cb30398 6a05fa0a97e172c9a8f5d2e24851ce87bb649a46c34b33330ae71d0d24a4e70a e5adf895a9a23295fe525e03906b9021d597bdc9b4595c76e63792c8bc819709cfc5f29e3ef1718ddb658103d96d3f641279615ae8f8710e39de4d692f9ccb04 +generate_signature 03e29bd7435541f8b12830d68fa31a8976a7a2750e4942f36cf834506f3f1b17 3a9768ff7593e63e615433567e9f2d7167a934a9afe7fbe587f0502942ed4ff6 b1b9f0cdb59910598538708eea87ad5114ee9eaae0bc5c62907688d6adc60004 951c9d15d656d80bb867ee265656e9c3c9ed4ebe564fdf6b753d9e775aa6bf0a2e2dce5775b6568dbf5c7c107a8e358e6991ed9ff928aee9507d89d801024e06 +generate_signature 5eeeeea12358c1ca323e2bb7f7c4f4d4f64082739f196e5505a116760af245bd 238cb505dcad22cca64e100b9b0a3133b44dec7a6a0abe198f6563a1df53a3cd ba2c8fa6cba86ddc91f810519281abeb43554e43ade7bdb3c238bec0bdd12e01 5f0c0c9c621c1d348dc56a36a46d2866cc746ebec36acb3ab2dc3c024277910ba125c0d04e26325332d2087ac35996dee755e1556a0ebbb3b565449f5415c108 +generate_signature f3f9ac0a5f5323f4af5441d5700f9d520e6cf41ea441ba045716b591e743cad8 73c0232f4a68d26578542b4d1e02287b995aef419200f78b3294f1c1dbf979e8 9758db88417ec357419b8e7ef2a75d2c65bdb800d27f6203aa54b5609955780f 387281536437e866f40ebc0029de970472d354303e6d21986e5ecdb50dce1d0054ac1389ad3c426d2d07687e45f7c3b4c53a86e9da6de44aea88e98143713809 +generate_signature 0c85328313e78623d436a1e3cf04ebadfd0178d4b230004469ff8e995fde6d11 c1429c3c7676607688349d6727135bb442466932035cda069ab2463b97387d59 1e58bd527b1ba1014ad772c523700434791a4d4c005f2ffb81fddfccd03bd304 eb65c0cc2f7d0ca4390ad40c0f75e2c60ca5b828541ba80fcabf2f36888e8c01c4ca4d07c8e6270a34e9beb19349736bb3b61362444fbf2bffd73a3bd0ae100d +generate_signature 9d2ea8622e9d46d17a568571332f14d6be90a40df1aebeec20f8418a5f94ab55 f8b705820107149e4845f2a3f3bd045a55944aebf35d1104632450bba4141db3 d699d458752018ca3ca82433e6fac7547030990c923e4192978ef179ad4aeb06 9b43bed7a0c1d27d439d1478bd0108c2aa98fd8c780484a48a55d789e04fdb0a78cb723c922e8b0466d5cbfb62ee0c1d2414005fdc6df90bba066e71d017010b +generate_signature de9240454aec3ea2ae6e13ca9457e43003991b9d0f75a4b25117e1ae66617ea2 ef02c34f64ca98543ae5b871197e413a7ccbc008f2366a2552f2cb5f852d8ef0 05cfc28bb9cbe0c4d02d8692f40fcc864665b118064f1bd31a347baeed24240e 5e2163e82b1727adec933d8f73d5ae805aeffe51550bbdf89fdd030854ca0104d47b481dd8b2bef4d44b8494347d34c295675a5ef1a0eba2d85f8993b4a29700 +generate_signature 806c083034f5bfc469e68a490f512a3c6cc3d21d043dc3f9d4941f5a9fac609a 65aa36a44250dc1e5985676f193b23119f7d5f9e0291e6643fa55ac70b1ebdb4 5f5cbfd6c2d5e22b703db1c64163cc0bb674a1bad00cdc77ee51001af5f8af04 1986cb79885b54276cbb3c6330c7a955c18c49b1204c178d66341453f3d755079992c20890d46cb6582af356649fc8cef4c98a83aac5d5d7bc38a0753e4bb105 +generate_signature 413c368f9b4327907fb6bcccbbdc0540acab58dcf1a860c98a4340448ca4ef4b e37df5cfb5f8ff60b822d3550bdc498a7557e1c11d82a035782931854b996b27 4dfcc2bbad477e1cfc6076408fc85ff9f3381dbc2ca5f6b61b4423a29f372d06 5f1f16e996d5d4a6f1cbaf88542be7a7b2cf9e06c408235f54ec5fc59b6071052a0d3e74cb0930b2d46718b0b4557bb8564919ecb5953cd36a41ac3b355c3f08 +generate_signature 3932123f3c95fdb21bc6fc74042f39eec45e588d707fbb7036983f3597382bb4 8b0138170662561e8b4e32f0821d21d187c62e7939903ee747aed3b1bb277d2c 649ae266093e93d0968f31ccf4d0b9307abf1e50b7d2b811f74c9b83f58ab00a dfc3ed48098f4ead6e4e5c33c18fe6e8eb6a563fbf363a40979b38c378691405c38ff813bfe3987084347acaa93b97d2cd37b13ba47b66ab31867fcceac04006 +generate_signature d17ab44dd70f88e19f0e8ba88a413fc18b2a1ce0ed712d6d110a191c9a284236 38bba4ea01a82f0c16f36f4f3387d6015f2c703cf211f6b73936db53645ef2a4 9d7af7de69861c61d3c8c6f2022e360a6cc8a647a4f3cfbeae6c14f46265da03 e031d959720f83a0f04f302bf8492a3c7fcb1fccc446a1e364fe117e368e550e88e72fb0061c08ec7b83a87c8b79db8aba22c33c806a2e9e340cb669e2b91b04 +generate_signature 7dd81bb4dcd4d74f4c034a4b6a51a49662ad34a2687401ca00e8a628a298ef44 5edda4da92d8227684c8e30c940a9d4f9ac51c9f1e1c9216e826e6de3212f47b cd386423bdae74357991ad01bae929b6ba9d65803d00f06fc9f0ee34a764bb0c 98555f5b2c317473d260bb94d34f19a07422570d25d770a2557d45fa489eee0fd9a109405cf766ec662c72234c476eb644e0b1a90e92bf07401fb0563dced801 +generate_signature 2e8abcd827b3c90a24a65d29e229b5bf59ded9b13ca77f51a98fccb37cf9775c 10df138936f814628662c2cbdb25dd7820c310d7974bc74314131f8423880dba 0d8d845fd386f1f47c4e92d6861026d697215a396f3eb7eb76cd478dd6c1220a cb10c75c6eaa37b25ab74bf5a523313df87ce3ef598480f0227d5ae417de850227892573c9efb37dc3903dd40e7811cc055aabddb8a9e07059474f0dd21da10f +generate_signature 83e42343a638dba6fb5d78e1b9c11e236425b432c77deac3ad098b93ad73f14e 3e855c996c5341e02e85737a2b03e98491037d91721cecaa17a83c2c4d5741f8 87be0823f5741f09e2f0c1a3337afddcf1533dd10e0b28efa8b1147c9ef3a703 8e34de7d4e66db9b0b04b363ba42d0faedf095b81b4749bfc7ca4a5573fd310cb9e49a4482580dd382561b199977fcd9dfbbba8fce869bcc157b8f133d5fb70a +generate_signature 206fef6045a5e44f87bc7acd4bdcd271e1c55cc8e42f5943f1f0b1b3dc0fcc54 9d32cb01bec30e0d7dc921f1ce44b5a829fd84c37e4304e4f910cb9df501af4b 8adec55623bd61bbf39d72e3f891419b5264af1bfaca43278dd86a944ec04c0d 9d96c90b88d56f55b0d679fb56aebf4e53fe6c7e46ed32f9d08bf15c4838b00ef70befe91286aefd77be10b0ce2c264c70c27561f7c8b0c1d7224868ee4b0602 +generate_signature 0f1d0617a849e1266d6741e17e8fd8a4af392577ba27783ed203377cce7514af bee8f6fdc7360d9acd4a4076ddb2f572d9e9306940b39e5bdae031db28f201e4 95f785c3db1b8ba26e660e9ae8a60af20a00289d984a83c852482c505d1a940a dfb3bc264686d40e63d514ec0be3b93c6125100df8a59a9c6bddf356a456660069d020d13f012e196053851d02058db9e8b0836ef51395151de088e0cc2d8707 +generate_signature fc47da4563a05a5334fc291b1730ccad835e9469f417987ed3d95aa041ca2aaf 0c1ad3f7c9e60987a84a3525a4dfbec5ad69f276ecb32e3c0880ce69befa4810 50b84ef4f4178228d07435d3105b0fe52ac7d2bfd38da43875579fe65a7efb0a 24890c5066fb2f8e30f6e67665931aeaebfaf423f0f8d1eb07af4f4e505eb602b9442ed605bd51c2b1e6745546fa874306379b4d3adb9dbf45cab720579dde00 +generate_signature 02cb1e9244aa1f6736d8aa1d5428b30658756bd318bc938d5fd704d97e4a5d9f d52a28a31f59208e4e41f6473e6bd9229ab6a809b228fae3d8f0a6bc3a34eba0 c6fa9ba77466112e1d268891c5cee9f88d091a93c06cd4ba23bd034cb3d0840c 2340164e8c084fb808947d22e89450d2d20e28f356607b28a0d67c2784f1a404ea3b14479a6e92a108d4b67800a549e0ba1d41b600a09c932c7c0b5c7ca3710d +generate_signature 62eb41777076bd1a90450e80e77cb1aca6a5f08e2320128b704b663402759d01 8e83ce60900fabed38fb560505d26420775f10ad6729b209992816101c1c4cae 14fcd95cd127e1218c11fa267c46f8c2d5c1c736e53f8bd2bf084693d1114404 bad17dc5bb36c42d7890b70b528ef63d8c3ea1fd96c8d1cb9761433649f00208e83e6a4a0218f55db8f50dbed3303f513fbea540e0160bc01702ba91c95e8b06 +generate_signature b862759c98652ca2b1522a556729103de96cfe831b3ce615547ba70c5f622c0f b7827c8b2edc9b4014cf4421f1c9792c012ac3264b15204596652d5854bc7673 1e62f5b7eb5d2bf88032c98edc74a9c138969cfee15f4cb69990feabc0f8840c 94b53456f54fe5f377ed7bed4aaaea0a2426817affa4ce1d9368dd0eaf839507d8463a8d07b87fecad0b8f3e8090f8b73c15b4a0357220a1f4481f7d68139305 +generate_signature cd35a8dc333ac5f33f3e12831a0a2f9ee03c7f421dffd7900b7661e76b056f4a 31ea2dbe369fc7a4514f3f826cd259eb8baa6bfbf22fbc2ef520d98be6e821dd ae4ef972bdf190ee205caaee156166e180e054993709bed5ac96bfd80e8b7401 fa87e0d00cdfe9e7668f1c84e725c122972435fd3cafe92511d80619f7ca02080ae868bcd919d42b4b6df93cbbd758ae3219d30f6a8766caf721cffbab34c103 +generate_signature 5dee08dd0f483aa67e0b2539c362eca5c4529961935d56f661ba641e5efa7e30 be75e0ce86b68a72f7487fec4857a7a6a188176ebcb9192123ff98c49bd2b4b1 b13ba6bfe9c23671e7bbc4a6380c1ab6c61b09db58220d28781d8ecb1b37e609 fc9bd4f87d96f1eeee5599b72c36630a34e16ec63d36471b3b624d00130b7e0fa93ba2836e5c4cfd447c0ebe1ed9e872cf17ea13bbb3c1b3ba97443bdd60a009 +generate_signature e88cf519e819375fc59bc8de8ba403278999311c7490290f2f5438306bdaf339 ce99f73115a02e03f42a1b583dc3cfd5fd1747c1400d1b6df73e1a3ddf616932 0a33216348544ad80a652433253b6921c0599d3a80e4b2d4a0477cb121e08104 b03c4bff969812e0ed3b6bf6eac8b5d8f2f6827c63c8a2be82c8237a1eb327097d30d45d47f22bbf0a32436e396114f61c22706654f8f27acc64cda2bf584606 +generate_signature 82a2377ac1c6a103cc0b955ea1ed3fcc41bbfbf6416b31984f28aaacaf4e2a94 535ce02ef72e89bd86e0fbd9f8f0197a99b11f81128f5179044e4e6d0f5f1a93 dcf1736d7a4306330ffd64315b2122c5b55bdf115dd52680d5ce798893b62a02 d865090cd027afa520a64196a92f4be2520aee4d44d59ae6c66f73af8c19e00ed4b4168c27049146aa7ae1a915aec7f65d9ba09921081f52895204fd064cf303 +generate_signature b207af5749179b6f2b30f48f2e0c19d90b6655cdeeb3fc37f656321f50ab1508 b2b43995a5ad7f51c6c71d29e1991f431444b5e73019b3d36fca05bc566bcf41 b1e043b1b9f4c280f798aa59c2f62e46c4abbc65ceb17349f4d11b867937ad0d 2b9b4abbc8e41439a5cdccf35129d5721f44a54469c64424f33fb8859e0025047867a4b8971df6be7ab35e0fa73d4d663058202290c4738d89a7717180a9a102 +generate_signature 63e53137888836ef30cd6a93d34a0fa749fcd9459bafcf143da29956a6f35060 4762dfaa39af96a85ae746326693ca87d82a008b96c84792ed1c570abaaa81b5 723eaeaf84c9dae7476aabdc5fd409a0faa7c61ba8b424ff78d299b9480d5a02 0bb2300897337f9f23867d4f9eac99c8094e3d2bfbe809aa5b1798cf4792f00864a34b002010f4b9874f38df4247eb751032292b9ced49c481703b6146019600 +generate_signature 4cd5f4b8cbbe735336d469aa8f2f7c7e5195d60f897b9a81b599b2e045850705 4a6dea121c196f56360e8c17273f1b94827efe1f24cbcdb9d3ed61b9a207b73a a8e62911309f4d4fe0bb6284fc4e3cf4fc5b08e3812e27328f68c2a6c278440e 3fe4a94a06011548f6985840deb85e892888f74aad4f2d61605dec5de1283c06f336fe45fb42ee9598d123b9664546d9627446bf822fee1bed66fb0c37aadb07 +generate_signature 7687ef751d6533aca6c911d70bbc4a8c5252fbd3112969a9a40b9e1dd041756d d17a5002e137731bdad6600b5ea02b520efcd1005008c6f1244deb839ea041cc c848b5aec4dd76bd8e33c37ac65e884699fdd7b5c7a905801f2f46c56f783005 13f1c63f1da9cf9f05efc3f6bb5b82e890e05297eec49f7b69b29da59eb99808f0383d4e1d817dd48f81b7770cd0e9794a1fecb9445624f1088a32ad3618a304 +generate_signature 7f75ac1c1d1832d78136ae9459b070982eb03e91fb4b686f17e60be644127d2f bf2d56a4e6d6f7eb153f31bd9f39d23a9e8d06ab8f906c716dde473fb2d16554 c901408e3a9a52cd7b95db0a8a5550aad0af38190c082f5d8228e5587367f500 7452689f4d73345954b6da7aa3048de09f1dc0dacee70168973f00780d34510db41dd089450165b1375c7a509b5892cd923b94c8294970812d0a61618a77a901 +generate_signature 1772e5a77a57fd69d6ce9a68c81bd9c4f81579741103a177bf584819a656e1b2 2ed6932a62fa064c9c2e39c7ab0ddbb6786dc849286af8e35655a58cc9be79da b9446a654072103d6e331cea1425472e0782b532ba9e235d61ced65ad6ff3705 5cd59d0325eccd855bda74e74c61b4fee156342fad767f909402384f85c572011e689e35337eef1115bca4b3667b41be119fe0515a2bb50aebc78ee54d0e510d +generate_signature 8bd02d71480ae9f17e999ca813b837402c999bedc4374d416e6aa9e233818f7f e0380564647a85532bf68ecb662fd8299921c8c51982b6310b4a6806bc056dca 9df00a62dfe8ffe27a34b7bc6a24a10d7f147d620f3ed78953930fb845598a09 79ad42ba9dbe845a78fd000d5d9725e5220daf385d8346def2c3674f8111790fa79bf6344d2d59a04db72e2bad673b157fb5d850b7970f9726ee6e597caa2b08 +generate_signature fda1233d780d70dbc9580c2b80ef1806a8ea2df30c317f7891462d96fa4d66af 5f769cca4be9aeb809e9803160b179b1a7bb7cab0e2b0f6311c9e29a8db20de8 076d27b6f85be7acbfbdb38d1b22e1d75c139072f7ae9d33bfb7590b474abf08 be0090335595f6d8a968977ef4c5d224e9d7e3be73f9bcaab911a89ef0dd9a0c822cd80e112280e279f9d780013764479ab0b38afa640b192759d140fb6de204 +generate_signature 80a147a42fa1e4f83d3915888f5c22ad71f92c0b2e1dbef9657330912baac097 add3133bbd5b10f3b34304864277b2a335b3625afe0d188ea6f60ba7302cacf3 911a315f1f7c21c97b7dd0385da95b6bb06de39796a538d909d51bfc07439700 9c8131da15c29878b0cfe7c0ac80cc518b983b35a2c1d8fe5ad4981c613dcb0dc5cef2e269febadd3075f9ebed11e39c86733df873d2f4dca99b1da1d933e802 +generate_signature 870ef0ebf5756b28ffbdd4cb65c2abfe6bbc366408ba42940376e91c750473fa 7c91e023da55901252f69ef58bf56329bb7ab688f444df00c1dfa80916a1851d 1752295c9937a10004ca9884b676bf4cd5608e4fb882f733ba1d91093257320a e07b84c4a1cee61148c271ff4645b6d4e286ef19b43b06a47763ae853df7ee03ee3e2651cc7164837d102359df9f7c506c5c8fe46621f79c73cf6ed1c7c50b0f +generate_signature 711f84e938e19a461e28c770573ba9220b84b233ef6494e1600b432333bed7e8 2fa1edf5af46532c7d290322f145d44487bdc39de5b60d365f9d2937de5bc4d8 86b9d8c1124989a8d5704873ee831f1620e580799fd46e507e55c9dbb2bde40c f8c7f8de98d08b0f41a067446a2497a67db6d850454123b3b4e876cdcb364009a71e6507c7cb15196d89a3cccae5b9b4c4759d5b5d8efbcb3feed3003a79cf09 +generate_signature 182159820cf794a0a367e94e6181cd2bce0aa7e8628c89f2aca4315136d58bd9 6252042e2d1f1694b73799d9f4bb777e7019ca1655c73dd79715e42f473f6c29 f34fe47fd0f4e59854e9b1740c91336978ef9b72bd07b2cbc601319adb261a05 c7787c0be53ead0f6e8c1c1cbdcf087331a9302f07c24c0ade0c744db9345807a71e928d3f98494e74fb7b3c0673b423fbd5d66e27bfc726dafa6dcd8543cd03 +generate_signature ac1490c8b703addaf061bda16c13c0ddd4ab06da053317ebba240be4e794b7d6 14d9ac957f7b65d0a8e55488d62a60325caa227bd28aec1d6a4f7fdb83a2b6d5 1d9d9a3c2200e2a127b68b06b432714b585dfca9fc8bc46c44e1a7fe7462fe07 d8007ec1580bc67da3dd7be58cec212bafd13faff58721c580e8e57b100c910449771d1ca65e02f7c4a5a6f2c6cb34a64581a06da6c9270970d22f1debd8f70d +generate_signature 6712b724736993684f7380ac03ead9b3df8c8d0f131d4ea99ade9d153671a3ab 69f90161bfbeae0720a5e0463f15814ff8267d8b742c2eb612f387f08cf32c30 74d48447a77283726f32f5e726074f1e3173d05a55b9e14796d2ea5436f0ce0d 03fd411245c78410dae2b63eda12a8e249fa7c4e6c4ad5605bd0bca852d96c074065fe1ac1137e09edc01a9b9fc9fd0adac2c91a4b1548b1a70a9fa18bdbc103 +generate_signature 2796b04ccbd3ecb7ca3182d12bce035edcc049e6eb7226977e914c22b406939c e02e549729c483263547dfb5e4280d5e4ebe90a7140c79c0eae07ddcd91f2266 a81f6fdda8b3c5efda411f6ae5830c151c1e09ddee44fcae52128e2b4fba540b e7da92bd940ae43f087d2355b514d83ac5ca27f8051c60673b2941672137460957da4f336e04c6fd64fa340ba7fe911a34ef41bfd0a1c9f2ed425c4ab7c4a202 +generate_signature 6b07f68fe4e8de6a194532606d0f64bb2561abd14d66cf4dac2526f8a5f40763 e21eb4c04c5abd7c4b12e434560c636a9c0fb4df380a207a93321e1865a7aece c194dbf0de2af2b96b1af7fa24b4b29f1291088413c9f26ed98954856c411d0e 2c5b10304dfcda3e6240b39fa88294655fb285a554b4ef1253e499b0eb82af0c74c8a7005a63158a1f5f4c80ee3f4615b6a4146b32b44f772438b050c271f400 +generate_signature 17bf7d4f22e2e16297c3105c5ea01f9d7210b77a02d2ddc8d9263b536c1c51e4 94fc695c28959e0d68fe1dfaea0bde1a2b11be6eede96a4ab65b68ef0a98c082 bc021c4b80b42671b43227544c185fbbd90d29b5d2f110d5030aca995d0e2905 9ec9bfc25af333e4c8dfe2bb785f6029712560aa66336b299cc059a7839f5e07b583057b5b2a142624527f01ce7813bcee80cb4fbdf565ef31722c613dd89407 +generate_signature 0833ccd1e71d03cf19ab214685f348fa88e711bdbc6e7106047b64a151fb6c57 f6064b733fbce4e1200382adaf77d93c542a673573fa492cd4260fe28b14ee16 01f9a1785bfd24fa38af0a5d0c8324f8946b9d0583222c53772e69367597490c 3881203d75206d6ee0831eb00258784f682775dad9a10b3710f2fefa3bbede0d3e598276b78a5ed5a9507a827d57b4e3b1e0abc84f18b3aa120566dbb1557f00 +generate_signature 2185872fb1e5cfada7668e6914ffc05f1c5fde38a7319e79010a8c1fc1c2bf28 23bc7fc2dcc89856410f5f2a006f4030d7880226ec512f8ce22926c569b557e1 e8ff76b5f7d00a8b8e6e2fa40b7385dd86692bb197cbf1220f3b59f00e149604 6a4810337dbc74875df912919dc658a5476bcd76ab0263105799668b023c680f836ea16d4dac7b9d7f7723fe12b26f6cf4244b6a7452183323d0d938e01eb003 +generate_signature 690194220a436c23dd6863d2b7ab4ac41cfe4ec6a6bc6843f1efd4388f37ee46 950f222148c84dbf4d586e7f7a2b53fdc43e45e034d0363883f50059302e5074 46d06e7cd27acea2c4094cbc3ead11d40b800fdde1a0fc1f687048e2b7ac8602 873bfd482dc6d1e22f64d4ee18526dba999c0731ae911c51fa9198a6073414089322b8f94e37211e419cf9d1714f1a8437b66d497f6fcfd156a49d0dd75d6d0c +generate_signature f2f674fe63b53dbeddd1966c2b3a3fc7d647f63190bd0a183908f782ee65b60b b75b075e9ce254f9efe7baf09897c5f58263397a0cedd458ed8c123a552e08c8 5f2ef89fa9956057bbd7654f09ce75f82188214ad1c75d8a1edb1f954689310b d2de07a25787aeb8821440b619fff7d1b9a82b053a0f7efd7d366b6572a90c095d8c28fe20224a5da33dca2e3beda32449c7d5ac7afdbc4f3d6a28b1d4041f04 +generate_signature 98e1472b88f2580a68a4b10b0634bfdd859590805b67e9b2cf8b76be25c57942 1edb8eededb14db7343fc79bba30422af90e92c739492ba1abca24a33808668f a8e7bac4b9bedd6cf89f709fc97a5d9c7180cf8cfe36fea26f3931192366d200 5220cb39057da3c36ec7f381e45228ea8d1b328ad6a11d1de8a2ca6ac0981e0039af8c61ed7924f294ca0043037e09244a3840ed5d974c2bd94afc9484721805 +generate_signature 1ad499a52576270197f365c0268892d44b73e9130f70c5fced7746498ef531c8 2394d9a89125d99e13502169301dedda64cb38509465e972b6f20c7f098d851b fe04a7d2a3e4e8c2a870c774483ef0b122b8c01813914e59042fbb76a59a3a07 efb3beb859868981d7820b1d93a7b7dff27bfaa97f3cd39e39dbc169475a970afb92f2e9e57cecf2bdc92108a430f1bc3e9fbec5f9bee2059e674a2242b7db00 +generate_signature 43038508a053508376f5d0595bd8cba556610e673b8291a322a2b9c72dd4b214 4934f2132f6ca4a2aaebe35ee547f935fcb28588e8061d7da267ae0954bcc987 041a55efcb7dd92f6fd6c2c2ae8c93af8d6248a27cf51c3d3770a0712783ff02 45d1666274e4b447bfc6e15cd245cfe31215de447e119febe90871a1def9a4096a247a4125eb5a34fdf0c5f33f40b730df3ade36d50c8aaa7cee61852869bf02 +generate_signature 4f9141ce176f61503a42ad71e3713b8a52dc56505809c8d6d98baabafc4e9910 516aba422fc25e0533ff24157c263ec3a284e1dcea2a8a04972b00c29df0dea0 23a237bff637b3008b429465d3223cf820ca4db0d75084831ab81808d4878006 ea508d64e99281c3d3c721a40c0303b24375a6fb1c09849afe1368f40f3682011a50587992d09c2ff5c9701ec1e9a4bbab29e8c3e1b450a77ef56e4b9b53ff0b +generate_signature 13dd60b4ae30f8146fdc6a54eb8018493742413dfd267f18bf0d21b380dcba18 8b85dd2808f4a567bbe3bc67bd11209c98fdd6d177678afe6cbc6fff19fa2d76 396068b93693fcc4afac343f8955a705d3fdcec131495c1b131e0ee9fc7ff80c 3360cc5e1c167861ed00cf29e33af7ac6723b1d3fc87671974177519cb1cbe043de7c698f31c777b9aab37257133f74e7ae02a077e69c779010a83a73ea3e003 +generate_signature 72b57bd7a1dc353f62cad2299a225484014343a40ce052f66ef2bdd581f3d5c5 57427bb8fc70228d84e191047948c9bcd9582c8f1b3118f56064eda938857a33 daf6de1663b64ca4fb7660058f16926e1fa33175f298249183c6c8d862712d08 52241f8421b92a6db7f3cb5e4354c57377186cb363ebda0bc6b19a3b76d9ce0de2765ffacc1033296492a3674ebbe8c7c7b5e1ef227d6af878ac975cf3af8b01 +generate_signature 0e5824738082572ccd6c9ac2b97e37412b432a6bb4a1101234bf77d2a2a1109b 9ddbc2b910f572818af16baf273026b8cac3e6b7f2fa540a9bec2b38d2020dcd 3f4b0832a84cebc9377eadef2fc78a62f6f32576258c8d2424d7dc7669213d08 e6c4a2c09df2ab7b792e81362d5bb40849d7cbaa04fb1503a418ef49d14edb08ca87e0439c9bd7ee98a777cb6f56e2c7d1b7868ab4d3bc645acf364d2cd4e80c +generate_signature 459f98423e876494700752609232d068596b762ef006c299750cb3ff573b0104 c2fc73baa1d5897f75668116cab853bf359f156e24b19a745966512f6835bae8 9ae7c3212e6695e0f5668d062b2291b27bc45affa1f77b9bd65951914a842d05 4a9e7a5b8714ba08405cab94b3eca9b8efebf50d78ac1cafef4d78308d1ecc0481225b5f8b1ce2f8648c4ff736e6278ad7b2355aaa7d7642c9da210c63306008 +generate_signature 6d176d259af7ee75ad769376ea2a5ab9accd9a9f4d878192759f5dadec194f35 2317f20756c1462f4d69d36275fc14958419769167eb526fe6a4f3007036d474 1bb97762a71f9aa32b2f7733f1fe1e0bfe8e654ee6f8361f83a61a22f32d5101 e3487b2e542c212cc9cd0067052510381bf89147114aad6c12e00d85b3859100d2eaf2f56590d1cf27cde84498525522606460563840d6155d4ca13df7628002 +generate_signature 88989b9365a33a6a6870f4978d8a68c2b310f7f6f6bcc3c7342fee9a2985400b 09ff1ab9622a87b66f39804ce7b84c1620456e83bd2ac9fce8cf3c8564170543 f3cf70e5732cf0b4fbf81f78e95bddd0166aa597c2a932ca16d9e90a2742c80c c2a29a2768211cba83d97f3f27497e4d40fa7048b36d49e66c4f156630dca30adfd23077795540ea4609e23a8c8f9296af2f8a0921f4173763064ed537f2b00e +generate_signature 72f08a3499045d8e2a14b4872a89a7b53756fd3dc0e538ece7093168c6f88b06 26ebd8fc7bf1a0c6c1c8f55f701d66411ce6d2d2a70f87c2a5ca42e0f8c8b359 75c532399d4cd4475ab2714f0da1f6b1f131ce4f18144b69c99259c58b5ac203 0937f5a52694d976464189e4c581668a445808c888c3cba9c6fc69ae7b3fe806f807784ac31b73939fa44b1a723ed81ce93816f54e3eb4c6224c3316424d7b00 +generate_signature 9dcccae9215809a2a124f8a3bf62b5bbdd0ebe6d7710586c5355c7a7283b0e43 cfe96abf54cf0ab6895ee0a7c9fb90ec9ccd654fd526c36d2370398cfb7e8f6c 911e17b1af1be9f9446dda5d6d03de66c64d0a785b60a03ab7b26e2068c77101 3779bcce6cc446fb4a76dfa8c6719116ff1c9890905565439b811ac3394761072475da7ee0c42cca24fd7c94f74e26496575c166c8aaae33ff9a6a632353c50a +generate_signature 841ac5bce833dbb72069a88c959cc59561e7f05baaf63bb7fdc98a4d6e1dbeaa f3e8bb769efab6217972ced819551c79f29bc32a131a2ba05ab26c33d62c6ebd 858dde600179ebea9ab1806ab8e41ed2abb320ba593faea28eafbb5a7f989206 a39b20db47238bb0f548ae4b2e09d5daa739227058f19a613597519199393f00c55f5527d81fc4dac4f70e66c0b28a5a68b151bd04cb700e8422c911385b180e +generate_signature c570dde787ce9fcc3d48c5ada9054346455ab9a11a1d65b0eb223547bd39d7d6 426ccc3e1c17db41bb5c975d08279f1451c00976216271c9bf221910ad1eb8c6 4979107437a699a931e67dc36312a1c0b5b5edd3a60c1b3dee3c12596e500107 25ccc9d2c4e0c722e9dacfa13cef0ccd6c1a9bdfed94f816e74efb8e86bbcd0fc4c184d6065bf72a9a98c801823be224fab86cc5c42d40a94eef7865c84f8305 +generate_signature 62e8187f216da0c992208f3f7afde4e5ee42e0c09ed9897b010430170bb87350 91eb9815ec0a5d2d2b98bde3a097db217a922089e11c4b15d9e4109b6c534b56 4d2234323011d6837889e21b7482c69925eb483ba5b367ca48f5360cd92ce708 503d9bdef5dcd1d1b2ee257ab142f2d7cf104e25f48f5e7f809c7a78fe7c230c96cd3ac35dff748263ca9cfa8dafdfd87fd0c735c1ff5f9d54b016c19688f50a +generate_signature 49d7e10bd4a42988f7b202cce474c577619f9bea53a7914de4d295b8529aec80 2daf7743268523b94442ce3f0be3910f40771a4f18a22b52f3132b6b5ab2b3cd 5e6e1c05e2e836a936e17a1765b30165f3851ab56ca1e38ac459c8b7a81a2004 7f55cc8a43b4eba3fc2a5f46fffd0d497df66e76a0b626f2fa20118853009a0669f29b9b4fabe0162fbb9e8870c3903a8db13b04311dc29701dc99a6caa90a01 +generate_signature 51c27e54ec56e597dedeb33c6f2fe379383a8df301688f95e37fb695ce64b1e0 01487d5868e86da7cbfbf00ae1bdf8faf8d51a40c5be011a1ef11a2734616a38 e844859d1b9a5328533fb41383c1ed108a110e6413c044cfec538975bddc8405 015582aa8f817c1618e040896477e4cb62909b0ea007d20bc8a8c3958d5e920ace7d32f5c91a9e0125bf8220356ebfae0f38454087046fcef6c6e5378d527b0f +generate_signature a26bbb54e14b7f7587fdf8b0c308bb80e3bd626c20a6980cc08b9f25317aa00a f248b297c284ef1114629819876d311bc01926a75899ab339891b4270ffaadf0 7c7af96309f390370a9a5031b9bd524936233f5f806b9c45f3821b91da8b8706 7fe2bb58e7362f90ac1ef98041a773ec6fb8c465e45c98da0a6b4fcb2e4316054dc228cc069239a6555d1a71193a5f72c50f1ddb074a4e75db884d60cb221507 +generate_signature 18937794c31d063933d48dde3fe7b5c6709f931e139fdd3cbae74e68abfc5cfa 4abf6a6c47126af111ab9761d8136bbbefef57cc6eb6ae4693803ac915351fc6 4acac844a196b1754f42f9af15c6aa9893bd00b5f466fb691ef396149171ca00 b404976ed6971555320c5bbe25d84d1319dd6dc7c27610cb6ecd64365e8a1a0f4b6571150fb70d9b1a6423d4c7d6c42ad82a84857efa4fc8a434a62d78104909 +generate_signature 0b32c806869d8b25e120317582013a95c995f9347814e4e37f6b948b4ba1e2f2 47397e6dbce5916e87dff2a55c63e49d1bdc826d4ead31401d47cc0a37d99352 c852e5ba498eb8b9686557309591b1e9b0533a07c97bc6a8186c036bee2f420f af4e4856ec42b7aa72d086166be7f955b25f7122fc3a0de1dbbd5629033b4202ffb4dc7914ca906c7f6d01eef2363378feadd95b0fdc3ffb70aa5f95aee6d909 +generate_signature bb5e240d1bc7e986e6e929dea3095830fa11b51fefae6cb1bdc8e060775905e3 7ae97231c99afe3dcd855aa587f1acf5f62ea0c61cba1e0c14b0fe4fd26a7b1c 25cb0101d7914aa49cd52abc533a7cdc00f174ba107cdd6788a1f64ff0ae390c 2a8a92ac1f138eb8b0bbff67cd560934b57539d35bea3fe3e9d6c27bf06f2609214c4f6e08d8695315cc5f7ded5903e5f452c95c2d2f0f34710ba118cb8d2e02 +generate_signature 30d230c4ebda0c60892e470dc95ec1152b00c8176c6a93ca73b3057cc7c1a494 20200fa0969e8ecccde73758c864cd4079a9efc8476776755390b9a56800be07 4cce1e28f0c62eb772138560e232b27c44ec7ecb7081bfbe07de7d7281c3da04 338ef30be4b480429471b654c2c3b718ba81ad2f4a14945d0ba5a26d097a69003a863ab4b89b8d60ec611c5be670187d8878076ed511612dea33421010ba830c +generate_signature f1dabb3ce43a519e5899d465abc054635e9fb30f3d49dbacf2dbb54725041d61 2be7da84b3e47473568338ddf79a8c87b22f29ef4295ae6c6ca463df3102278e 8831f066a87841b2beaed0f20b93da8cc730e303bd135ca540511c6f697f7f04 2eca25854b9ba1f1c6d8c8741ced2638fba66c583ab48bc3ce6c76307823a100ece3022cc59fa1b89e85e8d99f5e724aa99b05ade28994d0314673ffd0fa6204 +generate_signature 5d60c456bf82803cb3830fa2de437c377f5f0ce42b762a0d2a5847b95d6d4400 38aa6f868aba852ee5f2c2444212088ca48268451ad0bdf5b42d020db2eab246 208ca6972527da0e4333b6d756e5c3f336d70f1af86ee8088b050e73dd51810c 8a674dce74e1515af9cab426630b1ed01ef1a2989dc9386fbf21a1da47a341076c0e50bb3dc1c55b02f958deab49d2a3241f1a23bdd67c944f1578214c24cb0f +generate_signature 58bf45b4eb8cdd324173894cbb55511bc69ab649e1dc79311bbfa58c6c0f9275 a429e66f32ec3d35fa825eb5b6357f4daaffcac3a11c47ceb80ef1114a06a217 c3330d61f993dd6652cba521461f4280d83330119a6ae92681b708ab32418904 a1144daf6103277399a873711ffca30c4544723a2f406a2067afc1294c77760162adb58006d7ecf56a820e3d63f2ff0d3b740a1a803fda34783d820a43d35d05 +generate_signature 2229a90c79f0e3dc230cc3919066d3e4b385ec53b0559f7ca8b611ee2f29e12d 83d93b52f0df0fcfb3d6e2b13ad01ef38c494090c18014296eed91a5d988b2ac 3e286618640c874daa6e36dfb01b5e2d1f5d77b8d9ca6f5ae5b7ebe659b46709 3b899b5e2649bb28fcbe417a052f41d1b0517de3d0066d1f5d93e2cfebae3905b7c1200449c780c191d4cbb7705422e0fff1bf1b05ad3ac4c939b3ff3fe52c03 +generate_signature 9e47f640aaee7ab2e09c8fc12a32a531768258c5135eb8e521965d8708eff2e8 a8411a8de998e4b6be239be96460ec6d4f4a8b616b94f77347838212ce4b8613 cc4598110b1aff2282997a410e7cbd837e12a98461a6c6ffc307b77a323f0b06 34f38868d728155535a0de9c2d2d219acc84c99c927211035f08826578d4d609cbda56fb3184a3c9e4935ad86cab83576fdecc074c1c4bf92f75d5d1e5c7a807 +generate_signature 1fbd47cacc32270ecfe0bda8b1b1bd9ae4e03a794016a2696eab9cb0ce2c40dc 41d8ee40da7c75784b464f1b2fe89409f898fc57f395b46ed0038e98c184aba2 ce061c022ad0ad3125b6cc34b0d27291874d29575b10e1d44528ded224b1500d 4a4c8233ba1ab2f05712e0af21d7e9d38e758c603b11092b9bd1e36a65020705c26e9a9064b6fba349c24c4a0230490eeba2ceee353af5e2558dcd5a356ec90a +generate_signature 0756833a04e57930ad436ed6e50995529036c5b55db3f9ef070a000ea7b3b668 ef541d76406b4b71299d45937ffac3a4d284b61faa8644775e346ec6f6eda51f efe31b98bdb433b3535e792aafb0373d66952196bf564dee8c02ca0dabeee40b a8cae65d2cbf994cc0904c02612c25b9c6ce727f85ad2dcfca97c0c5625f650ec4d9d41a86b5f554dc4f9b2af8cde1423d5a596b37ae8450298b35079ce5eb03 +generate_signature c5d9d4483c4b9b23e21c259fa19136f0c69cf2cee896945510e95141ffbb195d 39e841d4ac6335babb18e8d9e47e1c75111639709335a1310db0ce2edf686871 6a2b88bfe8a66655b29e20294d715cccec610efa3503cafcbcb5323d8e618400 7cf5ee670ec1be776f72abf6540c382815d4ed1d6152e1d776871c7769d22c0783809d7debeb670da8e3131f3d92867f62b8327fc51e3c8138bc52d5a39e4603 +generate_signature 8bc9f9c21660ee20ffa666e7be110d8accd2d1be0550aa5bb3b4ae905418b060 14b57372138f3995b35fbb964ed0481969684f0338470a637e183b9780191372 85465783ede0e697db9aedcaba7f2aad3f4fda534db37b9654a3325dd4361302 11c0069e241ee95c599ed17155b0b7b290b11d747756ef850bee4c32fa21710726394ab1e2936e55bc570f067df43b967d1ed0b02772c63cc3cf8bff0d87a206 +generate_signature 7ed3d902f3ee03f81674c4452bb1cf33c0601fd27e25d5aed0b618b55aaf7167 458fa36ccf56bcf966910083e625c7efd4114daee53c45f288ec2cddeb444aae 3a0e8fec3301b4906383419798381d28a2a26e9249a58097734dfde9b9be370d 367e03c6f0f4958b4aae37bc623e23c029c3cb3f8880f334b2695bbd36d66708d5eb4bcd118c1d98429a09fbf7fcf2a0f7edee796d040634d1816d8947c65207 +generate_signature d95b625b3397e7726956f869e61df3634ee46b08a914a6a0b10b9e427a9179da f2cc630788dd65572d325ce0895de974fa3d56e07717ce2776ea51a32a542644 921c6a6d736c246dc6bef590f359f7093b32d56b05cd8639c7e94541caa2a504 298283996884ae9121ec7334866bda5b6c2eba5f2ad24684fe4eb5b7be70ea03408daad76a8998070549d5c4c9573ad940581cf4a3085bd90216b386d1dd460f +generate_signature d17c7a1189954c38343e20e2b8e8a290e294753072895948d184cf0d1c3193c1 2cfe22dd27f8b7041cddd1b8111a29a1c56b35989aabe9edafab342a484f372c 9317e035d8bc4a451e52ff94e5594885823ad2cdc7eedf66c5231a4f81bddd0b db863411b28fe28387e63641a68d749f3482e77cac071edf321d7310a884780de72c0ac3d2624f2b20dfaaa76706cfe7de984d220306dc0585082f296fe52f0d +generate_signature 9387ebacbf7d29655f65513d886d006af10a8c75a11cbcfef8f530a72fbfc972 006e6a957483b2f082fae65ffd18fd525bdff0d774dc20ca0b48b9cd8f592b2d 811da01862356cb02275a0c7878185670732b10c7de15ce1b2dc9ace1dbd8e09 99bdbbd78277f0c91d639554125f86cc05191e9059a730406621383d321b210c23efba37fc10de0fc4a4404b36c1fd08a51a005d35a59065cba54654d8b48d01 +generate_signature 307a4932ef8f15539d70e8721dfa1a4142ba4ed7b2a734f8fb37ffd31f72accc e48b91027d713f4e5ec7d0fe315589b36d6bc6ec338e218f31eeea8862209922 0fc991a39d51dbfb559047a95078e8cee3210760eb9c0ca860e5fa48e267be01 25f98df0de5032f7ff04f9d88e6a52e980560050e159648869f1c1a89dfe2d0ba93c6704740a4858afd5f5b0afb1c6bb6352e1c6fe20829d408851f9c77c620f +generate_signature 4366484aed2052c6c28d935b2300b5a4e5172bd79d96a37393df3584c0020b2c 39ed6f8df7c2d04ca843e2e76bac2c6a4de490f9c8d1c4ff16b70df13588a4f0 de879609d955fb6ddb4b0609fabb4e49fbc7ec017952681f911552eac8ed2d0a 68a5c32667677cbaddc26ba53373dedb3d01856464970c51d100ef578e520e0cb041b0d2f26dd07a45ac701544b0da8acbf4d5e687b54219b38f52b6fa4a7209 +generate_signature aeac4a1e1552df635de365394f86621ee2f21f7561a71a63c07fffd53fdbdc0f a6a9623f5791d0194199f52ea1e4c77be18a94541e9fc8d86b591ac8f1c0c3a1 c4ffa6e43fad2e9e558118f7b5a1cd2196453f91e1aa3691013209c7f7bfd40b fdbaba776d6e5d712d156001d7cd0e5a553b82e3f47a38a41d20c32a8d434706b5503e0fe2bd9c002796ee6daf7b043a280d78ce606649fb7fe096468a898802 +generate_signature ed905056a502fe94edd1c28b3f65491330b58a1e2563dbb8d8369651a2b30cb1 fe89f71bf6d52874eb89f234baff8c878ccd22c5c614ad18eac64f1d49ee00db 9826bb63674eb73980eab3d333d90b1ecdd18a227a34e729ac4aa4c679570708 dbe59b9cd64e6bac53ffb2fdfcd435110422375678060189cfbeb4fe1ce86109be9666aca6288261542a76878e0622ec69737b0ac2a652534cdf6dff93a4b309 +generate_signature 327024bcbf89837e82f4af135ab5bbbae710d4fdbfd771423dba6d089d62ecbf 089a1ffc8c9ee8b28e3181694127146615db3ca57d4f808e375b0844d68e4ba7 9c7c12aa4310db31790ed6c9041b711f0c742fe3c903e3093e2589de9279130c e5316ad5754f17a634c4b42ff468e4c947d71f4fab436d48006eb240c2daae06cf7c22ea2808bafc702cc87d18021e18a3d64ca1c0c72667438ebf11516aec06 +generate_signature 5fca639815dc14114fa452346ca15db70f4f3c13a1bd1dd2e5cc6633bd884a97 0acae623f287aa3b9758a36ebef615ce9aaa16f619d3cf56b7ceb515e2ea5c27 11569cbb68a9b17a1ac43c5b6b87a9c94e269ac507541b87accb5681fda30a04 0b5ad5a099e313af65823d12e62b592294b84ed7f3afba7ea93fdb114d7271024904f7226d47049e133c618bfa6b637e7ad2af7d38cab5309076166edec63c03 +generate_signature 0f217303b1076cd895641ff1751d2d469def5e140e0d61dc1a4239c3decdb382 d0cdd348b338fd12dd2602b2d5a872e820e1d9cd6f0b2df9c5c4a04e49559a62 fad9edd7ad17647a02d8f2077b9ff3c0fc755e5eb071165bf902654e7ff1050c 90fb5f72cb658cc1a3c0ca2825e4d5f537fe41fc3daf56389a2e583b5d4084077dd3e3aa872223f18d7a6ce32f9f41ddd488cc2324f1181f8755b63897d47803 +generate_signature 254a879d64f8532e5f281149b52a68bcd96c2148f33e3bf7cabb4dfc3450043d db123979ed900409c0090c74705fc059ff72c4e1ed05656b117327e7d3f68d4a e39e1a551efa30ce0b2461796147ebd86394c8c0abcd8fa8118fbd7cb5f7920f 25e422d0be58daff492e940b63b0789323e6576419e968baeb0f50886b16030d0d061a633bb530a81d3132fa4c5ae8560e3189f0d8f374e56b15cded5ed78908 +generate_signature e65ec30c477ec561fd8dfcdd1a3b89c099ee7c415de5e780618433c3209b9824 e45c9e7b6341e47a07e794556c9cf91869cb97291fecabe4867e036060c81ed5 860e36d4b74b80737704a4db0ac58e4181d807b6a0ccb4064dee200de67ae603 9584488bc22bf89b434a1336ae8782135ead120ca7ffa5f433bd04b0d58e5002f9ec4f1b289c49695830f3e65e9073b862c85cd6ddd37602a6027b131547d006 +generate_signature e701f76a8a06647e5e10c677a9fc17ac37c628580bccc5500f34381536b88b9f 1bfe84084ebb54bfe97e3701eb27cd77e585f510270dff9b22aec8a688d0512c c7d74b95fddc2dcfbfa0ef72aa1078f62b786f5c0d0650782a2913b5a22ca609 b828a3b11fe142d18fbf618af1fcb32a7af30b4f46a517a9095f9f2a84c6970f973540fa9d18da9fd3e16d36399834bdbfed9b0d36902f968f04e0b55976e00d +generate_signature f220bdb877fbf2bad58a7e98cdaffc0047f8d4512cf884e45c484d5b2a19275c c37d89b27a7449d9015b93669810f35faf07908d5fbf4d93ebceed9c1021770d e4692ca12eccebdd039f9d234a083b49a184b7d241025e4b27d022fb9f46c802 c064488791109a0790e9e4f42ce833764f9b950d1736a2f41d6ef147d706450decc0c664a4c7fc5149cc09c8e62f642de4e2d744d9d6d3571f30c61c836f5308 +generate_signature 2de9420d201089ef5c3c1fdc2ecb69446de59b44b84813d4f2c60c4762cba262 ea272022926e4b6059728e63ea0530c064280312e22e82419a5463b34b5d223a eef8d695f5b62c90c77bfd4a4e257de3defea7eb93cd546db2f0c448deb08d0f a086005e4a7d2d691071907000230bfbbad3390f349f82cef80cf6767bd75e0dec606b02818ce432397e201a79f5fb42faa018994ca7e2c789cadc98f71b1708 +generate_signature d22270489687f1aefc08d45cb567859a5a7f0fc6bb4346ff56be386f3c64ba68 b231b42bafbff5286ccf0828fa423487e3edfafdb3cf84b4333b92f7c5f8e005 3d6b4f25367114c0fd1922209e6d6ddb062c959f0643d6b5f5a54aa7a8dd850a 16b0c04de8cef9191c82efb19dba677954f517015484acb8b6dea5a57f458c03e1e92846badf1708cd559c174ebc2fa57d830a6752ed1cc06467c0be99d0190b +generate_signature 0f80ee01c68b1c0fa0f72a6890a523e2f941a934d5aea5a37756fc9308e3d149 40485d2af8f529aad1cdce430971bf593c40ea5f650c65d24ef2f3a1d6e09844 ba73077ab95f6b2185b68884e757c95ff288af975d70f54e6ddf75446b59ef0b bd98fb637f018b9fef99da2655d1af03730bf9328ac7425362ce3b84b4a8e90bcc112eaa3ddd9b4812fd335f60c389ec3a91af79d8bb59a8ed09f502de899b06 +generate_signature fb9279828f9075e43491950ee0a6b55f2740a03e2803e20e9baf42a71845db16 a60c8215f4c3144d397db32359b1638a94bd48efb4921488be9bb2dbac2d3621 24d0feecf4a09080ce8cfd390619e0f0c9185278b712b9a22f23fe6d1b028105 35cf88adf40263482815c30b6f768e8eaae6d7f32249a1ba6dcf8a3b256f2e072ec609635da7beb565a553a09527d34f1ff9e0d5504f98c9bb3cbc6d33995305 +generate_signature 1853a3886d3421c884701015146416e2372be5dff4804a87c367d69a14ce1674 a469f283112cb9e1247cb12358e1d1f0c2c1efeeb0311b4921a4ec3c2fa6149b ec7f0053fb49ae322026d8dc884d02e4784fef17469b85415f4bb269f833410c 739306ce1972cf3e0341d83f77f8ec43da75adc4240ad801b46b57d3fa54190d40c90bf957c8a410c1395b5889a3f30838357fbc7e8cd17f15777c829c5ffd0c +generate_signature 87a4d1813017c757f2fc7adbb6346bc711619edaa8531378e25686bdde9b54fc 8e1cf8a065ebb111aeaee0458198b02e9df18e357e60604aeaa62f0e7e81fb47 d75a74c80860cb58614643bde56d7d0793c57750af954f4fe949e08d73e14402 199c92f8a1abdb6903d4ce7f8fda12d9f5bf22f00ea4066cfd3d8c7687de5204508880c4c9e4fd42edc7a333dceba1882b3059abcbdce643e46e8eadb4c8a808 +generate_signature 36332e7b2431dd072a158803b24744d01b69f096aee41407d70ccebda99984db f37969dd37feb2205fa2d7c87d62bba58a675b9ee08caabd2496886059a5aee4 fce10979cba19bc179dadec71cdbf640bb3fcdd3afd07dc0e61124d293ae7209 90364254b41cea89c5464a82a6835f89edb8a965cd10c24240036bc16d1b250300ea72a7f3353331f02635df81499c11a3addaf62f0c8e175596aa6b41ff0209 +generate_signature 2c7db34f2f249ab08724d1a679fe855bfab2ed68228668c7e712b4760a839f63 ac948ab799b000d6125355c96a4c7c2f7918831e2cdb86c66f75a84ab3b588d8 711ee793591c685bde7f3b136751e5a39d55f0cdcdac03631ece1622a5982904 c2fd3aa8794549c193ea064c358cbfa93f580b231ae8b923d40e9b7dfe82e5059e63e2822fd16b5820eb0afe46f8f364d3a9d83f71a7e0d406b6a942da773e00 +generate_signature 9f92e54ff6d3c2033a5ce9135c8f079a93f0a7ff4c94467d856fc63c0e96fbc4 c6407e167719e2dc8e9e9d719b1ba7be2797eed2e43dd27b02e8b7affc6ba801 9a3b191d1806472ff03203d5602b9cb73d3f0262cdf56ad70d20119941f83e00 ede68859e81d18868e8abb438eaeff27ae05e717b56ecc8ee5f7fb79978fa90817625d4bd942cc71157d63f4a237138ed9fdaf50028bec40e7e2c959c39a2601 +generate_signature a70f0611b8337641f0d45cdffc56bfaec855fabfeb6fcd3da1292206b4a03309 0baef43d5b9d89ad7572c413b531381745af1dcfd1ae90d4b20f0f78080f2ccb 9e5527c3b3da09edcebf39183c5d912e28f47befb833177726833e281748c909 c9e9d8909c0e8dcdee4b0ed4303981e07640828a2b8e5f98efc9e3343132f70689cbaf2b91e76234ca85a7a91fb8da68550e496f79cefee8076cd552e9be360d +generate_signature 878f172b5f34e0b18a55df0ca544d7dab961eed79e07fc4689a0a3be8b3ecc72 03971f351d3e118213c1762bb564a2b56e3df37d3217665f16f5193fb82f8a5d 213d506a9731bcdf5bf8c3968d89e7385ee573aba69d1572c3604cdc29e9a200 13e179eed35b869797e1896da4d546a71e4b896447e2393e940081541d1587029c40bc96d6f7216262cbf98c2a09eba791a9503229f1dd63098ec90308739f07 +generate_signature d5c32a4a81da9af917c63f72b91c14e0a8fa93413572b702cbb3e9594481deb1 b558e0724567f7b4b08318ffe6b0dd0b53cc4a876ad1530692f286b068d19f9f d5a64d1b5c47ae79629ecb770ccdf0bffb1148869360958202ddffb2b5a6340d aaa5d73a65d56b2a8435b904a0185d0c6f81e492e4a9f098958865793f0ebf03015a210f04201d8293b1dd12cfdfa5c2eb9c26f6aa2f4173728b86106eecd30d +generate_signature b3f0024e9682ccfeb0d69b0c6d2f0911526899190e7f0fb4eb1e600da16aa63f 019ce545f3b46419d987730c551e1787ccfa158bb42a8f203631fc211e492c6c f03757ea2d5b66ba968b93e5109593e8ec7f4c6809f15c6f6d881b1f76435909 043251f2e9026d8b0e34084847820c3965746a1a1a017538da078b476b8ebb012753aafea088c997bbe33b7ce3062bd838d6eef14383279a47ef7db3e7eaa401 +generate_signature 0b18efa336170b3a6683dc486d1f36b6c4cbe86eb310388f71cdf70e143d0aec 987eb3e73980b221b2524179565720f451786f8de2b3407c8b5716ef683c15b4 d3db1e882a144f312d1dc307d8bdd2eaab5816804329bc36430803d23744530c 72f0be4b9c5ae0fc46a078da0b0dbb79c697842f46a93cb6cd6396edd6030308ab77fc89d46c2039b0f6502682a11ac0800d13fd7d4ca0781c58e5766b7d7a01 +generate_signature 69ae4cedd012737619b29be8065ede9f93b324b50252024d928ef04c543a79b3 35a734465352db4e813f76b446da1c00dc703317f1ce52c8a35a0cd571074568 ebc39ce202ec647da79c7af3ce6ddf307fc44de800a68a255c2e7dc7ff997204 1eceefb57b45fd04d82eca32250e01e69d59afbf3a1ae4864ff0b89ad88f620f0b56c19fd19f440f5c00c66754bb8e0c94bc4278302988676ab259e52e25cc03 +generate_signature 065fbb38e8325511ceb9aa041f32aac98a5dc8cfb06a85c98853b25cdfc47057 281b3c8844fa282b064d56acc83e92c8e6568c3a628645f63e40a07aee80aeb5 bca17611de2f9642df95a938f64894a4079ad06f842cfaa9bac087fbaca3e70c 31d6ce571ea3aea0ccc47f3cc6c476306ae680c872b114db8fa036e0e223d70a10546247efb71e8884897f427fec3c36adbc472897cd1a36081296c2e39a7508 +generate_signature c13ed90a542fd070e07610a0fee13cd42f8753f3448591ef9c9f57ad721fe9d7 02cb435d82524b0d03853e29c7ec0920556b85fa12886b063cf64345dba55483 e90a0b4646cdded432d27a6204c67a76b8a3db2dfe0bb2ac6957d68e6aa72304 8dc9f87a4499ce17b34ebba2cedd68bcfd69424b02a7b17dc616fdc38701c304760b3bed175395de2dca56fa556027f9d796652848d0ad5cad294a22304f3309 +generate_signature 7badd2f8d2dd8198990738617145e90c87880b95d5c875be0cd7be0dc25e7839 240b3d620b61537d7fcbde252e9de6da442bdce4e24cf4bcb9d13b50206129d4 9d9c7c91e5d71920ff7db53015814d4360eb7e398cd6ce8233c2a1dcc54ae40a 3a9e123e474d48dfb1aafb3b94e64eaa2db9a2080bf7588e2241c3782790c109ab8692ae5b95c40fce03b3a0e65a6c32acd18360ec3384c3b44cce7c1671c60f +generate_signature c537ab8551726c4d7ade51835585bf7850fbbc1d57f9245fe1b29618e2aa5c30 1ef9913d379ce22e9f97757c68fc5414e73a27e69a55ca223cb5eba854888d90 c3f5baf38db5121dfb177a0cac1d6bc423ee0c8f7c1323d9b691b07489006b07 79ef50f1d6fb5fecc8b4889556c9636320aa7a70ed2f79b03ef9ac0345745f026a9f3cf456b11ca909020decc176e9f685a026680cb7f8ab28690a922e7a450f +generate_signature 696aed47faaac314742d0e6386548c68617dee798a83105d3746747fd06d0cbf 279e1bd9754759a921c232f37d744d7b57531a302245dbb54f95259aaf923c12 28bec44d3dde67ee976fa0761ff650fb0d9ac60e74725e4c5ba3d7a0575eb90d c0aa4aa9692dfa2eac7de552ce7f28db9511ccdcf78078ff77dc17ea40c9580dbe618432fccbf259000b2327299e3a8a678bf446869ebedddf00757b5d72d600 +generate_signature 0215e850ee377ac137b6f9ac06809bd6cfc31918bd0a372155f28d45c65eac25 45621dd9f0fd09053bb23b178886d7f258045154ce90a9576d708f0d92ac656e 0da2f7254d9796a93c9ebb3fafe40d398726941e469c6f8deffc9c32c2b5d00f b88e5207eb2578abec6c0bbb59a86c4238d868aade8ac1433ef9c6e6e18c5902832887fddb21ca755215ec73543cc2ad13021868f726b54c161960586cd92004 +generate_signature 877a11a34fea5898e4643861721def338135c5cf6dfe1a6fc6777e77fc68d245 9e47630b2a88e9f86555a6a8462a435c0ac2c45bbb85115add2c4ba3dd578031 a3322e75481f1ae1eb9828f57a2b4cc1c786167aeb54547c36c936600747ac0c 9e46a956ff7599b3ac5f3927964ee265aa101f64f8a474d50571a16d8b4d8d0f78405175c104fd1366773e3e7c2a716fc2db8324bc8f4946c681562bf9741205 +generate_signature 191a17e4ab9fe35c2b1ff3190967be43353c62dba427e461cc6a2dca6b12228c c7312755cd3546fc800b0af704752069cbd24d4d132f4af435c149d1024da97c faeadb6cc780147a42bd6d1968169e088a0b5b5025171ec01fe7ce17155b7d00 962cf93f683aef368f8e2890f0541a694f47d781c2e1bd0ac6fe09c33985d10918b870e6961fddea524d654c42be61b7ac64d5d88ba18dcf09728f95bb66e000 +generate_signature 37bcb62ac859e2e85caa766836436533bb47f58b8748f921d3b1bbb81f9b51e4 bc46c4cd6ca4c15d76864b66439d46e8f8ccd846e899f18ad782d6a3608e0c49 28fff080636d9950ecd5dc362736734a75a72851823c7e01e7489b28c462ce06 06b118cc6d3daf76853e79ef4189c28e9ad90dd1775469556219898db0b0ae0236d34e1110af16bd3ca11b5c7d01cf10d774819b7698daffbb0bd70134d0f808 +generate_signature 42a48c4a50b87313860ea656a1df5698aec2a2f8d03e69ab24c7f9c7f7f66056 e4e4c0e5cd007712ed5120892e21b0615217230b1370f9a531192e09f5463f1f 0f98ce51f4aae0b3c5c549fac5d94fe0587e38f298354f36e6a259843322740a 4c8986e523a9a84ece8ca24540c85e24d797e8293612cb608c6de8c8896ac408265e13590313d03b2b3295b3f6db6fca1ca30de377da86aa17f7f3e393019504 +generate_signature 2280cec561410c47170e37a38805c146ac1a8b73c1691537fdd9d10a9c430579 21ffbe4f3e0ef3bd589689c0f2c35bb702f121d83d8d1ca62554220c1da72e56 a2cb2795994ed3ff548fe707d64f59369cc6e893ee2ce9830ffa526b2f41f402 1641215c1460262c438c1009cabac6664d7fdc87b82bf6a2ec2169807f6a2b0d9900f15e1e3ac58554b1147d6530f3ca75807ee8a8524e91e3ae48a4df6bdc04 +generate_signature 7df35cef6dfe5607c06a64a38510e5c42093c703a88d5d76dfa97b14a0f6add8 9211c6183c199f81e344d3650c92b83c96bbbacd6ae654d8644b76a21611ae07 16b3d5bc1949ecae9e4db756c129c71889214a5af2ff78aa8a479a0c5d0ff90c 8bddaf50c0154f316a0aa1c12dad2d3559af4e477d8e3c61ced389609b26cd0ef3c2d3fc6e129893f14ca79ed0a6c7f5a61c2f8bb5eba6f0e8f52281542a5201 +generate_signature 4991a192c5c493c47372771fde392802036834e9fa14ebf5c4696e40420a4c39 61c0eedb78215458d3c724551d7220f92b8f70aa656514de7fb209b26728a083 ff600a4ce0fabd6602b51e9636ff9b8b9e102f1da91f00376aa2e50411deaf08 e0921c2778c1fb90a0891ddd377301f49f74dac323a8571ce1db7e0c770ec609b0cadce3b0842250bbc71390406195c84877d7c247dbf700ce82cccaac01e70c +generate_signature e9d7fa57a984dd1d9c7a93125fe69f4d408c821d7c17d199ca494d16405c0daf c536bcc2db715ae003881137da965ff0e30dbbee0f4e29d5a439547b8cd48175 1148d655f1e23fec319f04dcf5c5ddf131fc182b50e39334ec0f312e25429700 fd7da19d78cca772035aec81420f3d1cb2917a54d0c419fc74f9eb51c7b3e30b4ed803843094220845b8b21967e3fe06da3609422337b3db9e7020ffb9ce2a02 +generate_signature 9c619840f7234b099d0995b905a15ddce999775aa1720f0bc9477bbf0c5525ab 4d8720590ddcf790a1baac573dda9ddf0efc21223335b15a23189fa67f515ea7 dee02eb9d27969ffdbe6f7c22642936e5965f3124fdbb0c51b1c44856c075a04 943f356fa0ab617bd2c91bf2a12c72d47d2a18debda66943325ac5300bda830d5ba528ee4e0eebc21851bd1e2f7eeeee20dfeb236e615e805b53489b40fdb203 +generate_signature 4e5d10eaeddd0acead90bb49b03e73b84e966bb05d63055031cacc8ea5e49aa3 e2c8684aab1e9860370e94d295bac00283208ebdd29910d54c9a28477daf6551 4487e8f01f0ebfd7b1870e7a1cdcd86470fe644de11161309114f120683b3509 b6e37c1f8ab82f987dc2d91e8ab0e42af4d816e8bb76be04da74f2d520d03a024025d595a20ef831e8af6231516c3a2f06e29e19a970a95ac50e9e13bced9608 +generate_signature 8db465446cb5848044acf5826a3c8baea057859846b551238a0536dae39ef712 f3a88b4793016d12031ffa76a5da01dfb771e9255e8de871769717199b83ccdb 3ddbbf3cf5f282afd370de5d33dbd8464b805bf4d7c74be2410eefcb5072da01 1b3e07ee1f304789926c40dbeed125b861321c7e917b1c2da66e95b8401a2209a02f4ccfcd9d7f9d0cb2c171d675b60e9f0c3a7b8fa8b8ec3dce46fd4795200c +generate_signature 24979bf7adb7f8b840a3524c3d3bbcb73490e50e71400632d824f420427df802 78057cddcf685984606870f37a2f0d76e4ba59d169722729695edf4934528856 fb691338b3fe0cd1a272b7ff7e363727c66689c1c7cfca7c007a5543b3abd401 bafd700e423e566c07e38446db0609473023db1b26154a5fe9cb23a3d8d6a90b7cbf663e71739cb55fe72d2b78f78961a328274e575d2eaba9a7128b173a9004 +generate_signature 8866f2c3e0566f73f173a7e0f9e33cf8b1795fde4cb09deeb1efb17dd82ece33 28c508eeeda24bbaa55dbb312f534292bb1d8ca09cc34ceba36a3db1ae5aca9a bed8a7e6bc28ff86ed87c5f54d91f8fd381a2fa9057b6d38b095245dca1b3708 9505253708cbcd42f31eb240d62e79f7b2ecf56ef9713882f5379e4b7c97390487c3dd18e822486477c748023dbba046a0697c68d1e12de8ee93be9ca438700f +generate_signature 421c152f337614c5a007b4eeba8d4299f7fab313c1e4fa935048288d4b2531d3 862b1dfbfbe3934696a9ad41adf2022714feb99e5a1936ba2dd4cc08edd7166d fcc52b66e50a4761eabbbdba3fa82f2e391db31ed1be585ba5a936c31dc36400 7375b1b507d136d6517845943f3cd7afd9eba9a43049890d445d2db8969d480dbea4ad60624406878d621884724b21778c5440f8ae128bd35fa55a69ec9a720a +generate_signature cd4eaba1978fc73de4114aca7dc6708308e72a8a861c96c2eaa13dd06f2e82cc 88855b172e2e512fc6ae2bb6e1252670210b17fe4c021f97ce7245ef5e0766b7 fb333a06054d22771b8422f547838f14f4b868e6c0ffc7abaf52643b36e25a0d cd7c83b5573b8b087d6d478154b35f5d9c4e90a9319a55769e15b011e84a320ecc58e702fcb7fa483fb22e56caef52d0c886fb4adc13c4466b2d1814e705b503 +generate_signature 38d2cc1cc489bb0af029d12f1ed624bec71d68c0713f1fd7f5d367efe71d72d3 3b3df37e4fbb3a730b5f9c5a1f7adc45dd2fb2c05d1e141d939536b6f55065d6 f10b5d6fc5830989df088c2a1a9bdf95c37781bc8990b95beeaff246b858770b 14c4e9372cf5ad1b8a5bc0721620c3cbfe0590d4879a2ae12e17c8a5f0e3db07e7e8fc57a0b96e195a869cf22bd54c2d2f5a536cb15dcdd965853ff0c7865708 +generate_signature 017a5495b9f777b45cd256eb87cd298ae9d346caef147cbbb916d95a6b61c677 e8d23a7fa523a7d1d59d23f5929cdf9872ad01eb77347b076121b2a8d24289a7 d77e90cfc8bdced4c2957514ad1dd3ee2b4e841f843dc2cd711df92d31225801 b86bda79c4a54ed1b3be29e8f79f84ca8a8b04bf95a64dbdf61bd83eb33dd509a9a2b24f96ba9c79b04c428c14e83f11cf0af52ab5977543700d6e6d9867960f +generate_signature 5cdee6559376c9ce870c83b06a6492b019ec54bd0058c2051813cf600bdf25d9 a2def558fd26b3e1b9f1d13b131a324ebffe1412ac2d043f7395f060e35f4780 29e13e332419405883c0103dbabdd28035a4be98f8216230d4a31072ffdbad0f 9baeda2f7a7f9fa051ee0485d67c6e07ab063b4c28f88e67dfb1db25879ec60d48983339e33c0da1cffa49bfa5f3c18131b80f7e3e5e9209d812dbf42ba5360d +generate_signature e5f7e794c78196616415dad78d3d07439809df7c7443e44b82e54c6faa5bacc8 6fb3a5c5d7b694b63f29ebaafa2cb101d455d95d1b30c51d2027714c6048d81b ce2794dac0a5fab2074829050ff0f631ed916adb97c110d1d9c01f11fc572005 2e0a5e8d8dcee1b6047f892a801fddbcc6d53548e6297217a21b9bbf5aed0e090fd05e5ca8b23562ba47119c074bcc778bb1fbd8674a6907f5d21cb1b39f4b01 +generate_signature 5283cc05c1a813496774fe331d90989baca3a8932ed5d6ca1633a96e9881deb0 c776290262741f162cc11cd2c6ac606330a0c53fa543cadf061d844ef306920b 7012ea2ec897d4db791c283cb2f878103fb6e29c831549f2c11fb306a3f33301 20567c1a203be8c928969ecfa3f4cd02b9e2327bd4a5dee6eb9b477dd2c7570fe443bb37b16b36883d75ee9a74ce6b0e54fe6da64744a7fa81627de178c6e30d +generate_signature c7769cf2307480649f60e6b97930ec24d3ad128375546675b9da306de670ab0c 3cedc8256aa3d7844139e180826e005f8619adfa8a493da9e824181e4cc007ec 4514bdaf0b277950569d81b5195c63f14fdaadf1156dbb3ee16f3aebe2cccb09 6b6ea4d7c021f2bd8d7838bde814e0ee06f6b48e6ed911784083e007c52fdb069523c21d143b1fa06a2ae985e568a7c56c08b23c297a29ac73f4c2c402273d08 +generate_signature 0020398fa58108e0b0e5356b87113093b1e657fc5aaa229a2cbed766a1343b13 dbda7c364135fe9237e54e87c742a1174d17d3e70a08051780ebe274e399a4cb 12160063e4e47b248ec37339a3e62e920b41da6ed6b9ef1dfcc1f48a17c0490c 496cd583810035ae1efa4abf04b4b3cb0d614c49f92b52315f23804c7138e205648112b530aebd2448fc1a0f3c11248e2d909b88878a154e5ead8dd065d2de08 +generate_signature b1e43d4e2cadb0e60c8791fe13909d0226ddc2fbc5bd25f73466910ed0ab3f2e a1926c9a221e847a653ec6136e4e23768ee329db20a7a5986b51c0b35eb73d15 5b0d93ac7c30cb138102c03ca6b27291e5adf76d82bc88862bcb3af8df86d308 e1c54cb8ed14d49435fe92aea76da0fdf48e711d4b441479b312b6d1f9ac9e004b25422c12a75eb90a2f1d1cb853922fa9921c47b140010f5ae83b7ca20aab02 +generate_signature c5e0e0a1d97e19628730750589a954c02102545c18f7bb05ef3e9eb8cbe36523 e4a4af78733fe9a3b297bf135e8b57181d8c0e39ecf9a5a7ce1898db57b18b17 7d29486ba52c61fc5ba24e42ae6f1861f161a0063f102ae3e86c480b52d5a40a fca60122e6c7fe89d2b6a1197b1028e398e50197d972aafccdab57c5c7937a0bdd02a6eb61a8632f6a5a9273ec91ab687b84c565d872922eb436227e2591d807 +generate_signature 81bb454381f1614e7e5764e88664dc47f825d18ed5710899d1e5968169d7bf58 1bce52512bbb39ddb4dbe9a10cd12314ce08fb7ca6b20d8a0855b334b5416a17 8fcbfbb6f711655ccec1b456597b0ce4a5c81adfe9523721503d36f5d5be5708 e0dd435102eafd52ddd4a251dcf98bac2be2ab053425a2e1ff8773b3ac4841071f0aad81e429f8f47bc0aac08582c5cac1334115bcd3419d4a86543ea08cee0a +generate_signature 8c9949bde07909984cec9d462c7e1de28f0d47d143790a686bee51d4983556dd fb6fb90c091135388023524466045bfca011113099973ceaf2d6dc4fa0aafbbe 8dd9d958ef607e46a14f742e8581c903661935e2257f24e852ced3f7edc5ca0e 74301eb101db6dadc1d304aac85a76f5453be8ed11229ef2e89bf7342fb391099d8a8f4bd07fbed6dad2f894d6e9578e3da2e8ea4d77e2632faf062e14365409 +generate_signature ab5556f1489abbd7a0c8c877a18313617887be7ba186a8e0a2d50e9d94a578c8 e7f84cf6411ca6bba208b07ab26e48a09b779a1e7bc907ed587a1d7ebc39e273 601f584a65a16dd59e6a1b86310a66a11192cd5ee68a4943ec38bbbb1bb9370f 680ff8b998b48570dec897fc70b17d0663b25210ad75989e7aeaa1beccb27d0e622cc19314b0c4a87505c7ed8b4d9dd98f53cfb402ab8ba426d2a33fe3de760d +generate_signature 47fb94c340262e3cab3ffb03e06695bfbdc2439807cf26620c8cfe4395391309 a5f429dd825be9eb1b76f7300754a454844d07d1627312b14c70a4ea4e4566ab 7747b7704dbf86b1d338f3d49649e43009e7525edb11e684294fdf1d6fbfb708 274887fe1f2ea5100877a98acdf60652de23296bc4a7e905542e71409f2e8c0af5ee8ab0022dcf7d6df904b03f77c340967cc411238b40bb8492008198b0a40e +generate_signature 6ba24acafdbcf7ca8125c560f585d33fc166ae363adc8f0422c2242f6a2adad0 b27456386a01d84300fe0695bcb594296fedc0b2ea31e1a176cfa49eafa0e5a5 237401f9b747baaabeb8d8df6f526f00a6331288118c5aceb343908fea8ff708 73b63f6bf7c37cb934cbab1c56e7f7bf6dc8fe9cdd89e7c033e8955edd8afb0658fb8aa2000a1949dbd7f98ced499590263e0158e4c8db08c2de202fa04eff04 +generate_signature c8dc5ec96cda8c740358be0754fdb8644a33bbdb9352ba63ba8c8ebb19f7a240 d9041763f43939ba1b6015c369bf8596a53584891e45b20f4031ecbeff20b36d 849e6afd48fbab23161a1dfb7d3b60151dbc4b288596726ac07b2fa4fab68109 7af65b5232c51ac365e36e02484888735135a3ddc8d8d2b38fd9519cdfdda1057d84a0210e0f8542e343cb2902cc8def4aa137dcd718ddbed23ba7e91b21fa09 +generate_signature b6d466f81e80d56104e09dcbc4bcb94e4f095ad48c635aa14f74c106ae57b9d0 005a126a81f80baa89c897c921f562aa44afb0a7f317e2cc2988197f980fad94 08042ce99fa8ff0a4c082fc1ba66bb11f8c94ba9ae38b4f37aa050a3345b2808 103b1fdb329b70fcb457dd523342bc7d10d72a6862295479c076d5fb2d543d062c4969c9c2851935a064692bc8db3e713f1adebb6da4569176eef2274aac2d0f +generate_signature 8c442d20262492d6075a482a853037319738a944e8ced7a26c236008348cd06a 1543c07acd90bdcc1ed42560b2129f52db09965732e1ad75da5df80f851e0a30 5abba5c682b23a2e44b9b426abcea8faecc8da15f0d124fa428cd21ab523490a 1333a41088023c697bd6a345f07173c0feac05d30b9ee093d87acfbbc21b0d06ac809857413677b9cc6365b8eb6c1fd6ee8dac3e9285fb339a0e6e84478b380f +generate_signature 6f7951c7ca70cbe7abe0d6befe5a5a10559b7d05a56fba3280266361a919b07b 2a08f949c037c51b3bc036e3779b6dac628a9fa2561087d15c029de399596209 77f07519b561abd91c4bf4a10be08deb641537d7503102d30b8403bae936910b 9ec220c03b30944d7d256160a6d05830840a5b1b601b4a93fd15deacc0e53d07fbe8f74f9fa1fce78ea1dedeac95c19d8c9fddf2694db8fa002d8d2d9af82f0d +generate_signature 22b1dcde0d75d009c1f37df37ed0a79e162049ac2ee48681e312244d540b046a e340d157c047d55d82817a49b51f3c1d39a82d38876a05050dc5296c12a2b6df 8fbe0766b15d4fa7542756b1cc33a62bfe2da4f7f00ca30dfcccd7d63d6a350e 5099d909d0cf0ad066691ef161321be92c4dcb3b875c36c81992fb2cd9e5c50736827b9191b2ceb8e88e760c135f680ab41b7c9c96552e104c270a1dbef4be0e +generate_signature e1552258578c4238b55b9c096f1da86bc399ebe8c55df9dd6d0468ce939a7695 98557f788cbd01ac0b77f5e4bd2c0aa65c58cc61b49f5842a53f898461d22ce6 7d052de95e7e055d117709ca3ffbd6294edb2b0698b549f623327a7856d18b0d 26f9f45117650b4718b3672a487cee8cacc12b31a775fceca7ac9bc826f2950ef33310de1bbe633af6a2bf01bd5a9a348e45f14a99acb5157944eaaa582cec03 +generate_signature 9ef760bfb089f081576ef619c98fa69e80bff76db56853759a98359944af31f7 1e2022577b355dee237be685fc8704c73ec09fb448ce3ac9224c7cb26ed7cac3 e9d5ed5d17eb81635b3f44bbde735d506a1b1002fc963369db98c2da1fb9990f 2f68949f1affa927b4baebd572faf3cbf69a7236a105794ccd005f863c023308bf5879fb6429ef9c92b01bcfd761bba51594400ac61660827c1318dd2a811607 +generate_signature d48118e8850b94a1d6536e8ad26424db5ff6a8856837b5f2b759991cecc0ea79 76acc258e905aa60091cd19a0453c086bc63fbe6a9b9f9713c5a59e4a1f65735 e7eb8679919eec79d95612ea8d4f42d3dba03c92997a8db4ea7ee670bbda580a bcd4c0845fb835023515bcbe31b9d3d7fe06a9ec8d3b34a3900c49bc57e58d09b2b2c1ac9a0827fa4a717e19081071cab8ca7f1b27af8df5eda0df201d447a05 +generate_signature 95b23e89020f166b62f1a0ee717a478c07b3c96d697a3a5e1eda873c407511a8 8d7c53fca9f208f436d9b1a921daa731f79e537a6aa1f54b809aa4ad9b33b860 b702f52f1dfe951d4cbafe535089128cfdf8e3c62e95238614d230bc783fed09 804ce87bd376374c5dc2bb3c97059e59b259952f5916d4a80ec0d2918ed3fe09e1cd4047553e6f3c34971e0506b44e2f20bdedd75ec3505e42cd8d8c3345250c +generate_signature 73017e127a3cd7c8a9f97f80ccfe6804c5bb683b22b6242a307aa84f2b2e5663 f17f920f113d54ccf8790951b2963d35165c852ed0cf7c6f5da3c1af2874f7b4 7dd05f2bed1ebd7b1bba8060fcad9368c9c114e49ff553273cdca2adbe5b7c02 08e18f3fd9e861a6fd6788147ab634b5ddae00aaa66a7faa2e57a7c26011c7090482dc1a4f89a5f09cbd47beabe922bb3c4ed5c9dd3ac8caef40e6131d69bc04 +generate_signature 5df7c3d6ef2ed1b6140e0c52d836594ae75adfe243cddc0cc11e253674ea1d3c 5d45e0470b6d249abde6354c3db9e980c56705786c11bef17f94a31cf7d05d95 60e30994ebb7c53ad955faef54e0abf7d7aff8932af097db250fb56099712b06 3be465b2b16cff9ea84f679da3a344eb6be15e1084e5eaabb2e6d4f941e93c02584bd648ef975371b12f4d7163f196a8b7825c7255926899d62c12bc771b2f0b +generate_signature 5e1dbdf1b13c9e0be80b4f4e10a2bf29144fa2e66553e81fb50f2f7b55633f1e 4cb10093b5c9cda140ec668a576def490cf7453635bb4921cb9ef8c8e909ee52 067e27cc60b9d9da528aaa8a48ee6cb30418189c8f6bb0d5710dc1074dfbd605 84a811de4cfec277e86884886445184aac35cf380b2c96872cf73e1dcce7d406a6fd8ed618efa3f5739958fbf1fb5cef9b9f60969c4af9247add0628e4de880a +generate_signature a08e5b2e08d5f1c70edac02d97b7174e348c6d4bbc201485245e8578efdb2f5b 4a10b74ec6c5ba4cca27a473ff6022c3618b2e3c24625708add88767da62e3f6 50262cbb867b9feef014c6d0f301838bb92b66a8f2109e67d036129096168900 949237d915e5061336f56862da9cf22a1937dd36370a4e24a5ed76a7e60c2b00a729ac97961f80310c3a7256f1d08b8b67b4ae9574a660ba0231217ecafc1607 +generate_signature 07edba63a2c0fdb2e02bb8b8e6b531d2a542b0674e24f5ed1f3ef2583d2d9eb6 8c1059bbe4a373948ede638667da04f8caf05aaafecbad08de797a804f2e65e3 abdbf74be8cba1d22bf91057a94b1f12a063acd976e4410152a07de48b290b00 5031fb1105e40f919f7c215d76503a15ed57136bcaa643f23e9dc1bcf627ad080e62b036c4afc066698144dcf0cbce68a60dd68e03810ace41de2b40b5891002 +generate_signature 5dfe35eb9c70c55e285e3e9121651110e8daf329a1d3110bb342341f0b13e285 898082df3ceb5d2e96fd5400b22bc169e04d3ad581a63de9ea638eed30a1f63d 1e83a05b9ccc0daeb952e2d6b5cfdd414fca43664ed0ecdd30c5eadf7a8ad20c f0c814483e7da4f267fa954900aea16e1fc34232ade15aa043ee4ede5eedce0a52e91123decfc494e9813f9738868da0ef3ca3830538925419c43afcbda53b0c +generate_signature bcface283643b5f6fa0140dafff732ff07f4345abf3770523489ab4ff2731c58 85dc41f9264298c7ad08099dd2b20682dfa4c9798e69695f9626f644ef7c7980 b23d6a04cf867f63aa2e6a26784d5c7a00afe68a37af3069248177b77b50c506 da9db5010ae27d8469f09cddb6052e8f3a181f4865b25c88e0f8ac99aa4bc50e3d7792c6a31d4bb221435ecb23d169f8ef0df4bc9a3cae2c74d1d05157d2f90f +generate_signature 79444422974fda912b29426d4fb4e8900953d14b8a04b2b48a3370fbf6e0d34d 55c8b685946d2e1720c290ff6c2bab2643e35f6edaf04476dbed56472ccfa242 eabb6c32344af64e15d351011ece8fb6cb2309ecaff2d43d819f59389bf57607 eb44c37ad7bb78c8b81321d5d7dbc1e147c46a4cf8b08c3a1a7e321e29820905fe2638865001d440c9dea705583d33d9183999efeab79004475f860fe1b32b0f +generate_signature d2c081f5d9a76df2448bf6aaa021331d6e0fc87e31b6fee0bcdcfd499d2dd94e df1afbf7c09126a407fc34b118985ccf41c4e82427e6dcbfb0d3897cd3705d8a 56db60488923ad7e0575b143e71ce05d93e6d273a5bd445cf74ac9d8336c230d 8a61fe62cf20736645f05274e4e023368d207254d743d6ef4a7eb001bb9a470e29067ca10dcc31eef0158beabd600bdf2488684238ac0b554010b874ed968401 +generate_signature 8153ce2a925a6437aa445072075b5cae447affcc86b452290e5aadd91e446e9d a504a848e87137e24df67b8330d7dce41efb6a3ffe99ca69d7a2775bb4ff66a3 58a649099831b7343c6800ced12204ed25408aa9b1525d2f8aef6b8a40cfa908 7f414c45741426f4060ff6b99c2564b9f5540467fd74edc7d8ebe1e1b6e1e5019bf981e062680459835beae15d5bf4e44a7d0dbb6e8ccf9f03aa348e2e64ad01 +generate_signature f85e575bcd9427a761197581a05d22e3784e602cc471d88ddef5ed4e6ef26ac1 e4582873da79806344aeabdac709ce261fbeeee546d0068c3d17a64527aa1cbc 1090799dccb3d243647defbe3ae894d316d68d4539a134d8aa86f3a4116be203 e0f877d1434214b474a69d631d874e260281cd084f9666b692d7af504cd23905ba29d48dc9045343d0c0cac00f31beb7a115c460a986a597ec4a5d1955edd303 +generate_signature ac157e1a76bd72924e77dbf97e15469a4da1be6d154a8974aca1d77da7592e93 f4005a9e2341967cae9307f262d87c064163d0997d9f039a2488fb044d449604 7f8c5592166b6445aa14fa0b765d3908f8ac9af1c6de0f01544ea95c487e9c04 705fd3c4826b2c722036ff838773ee061395ab1b8395b01ab87655313e845a0c3735d90aab37d0a7df8bfb2c090797dd6b6bdee2e35c7d7721e86e60dec82806 +generate_signature f135a8ea135688e2cf612a1517c305ba88b556d3d0f5c3cbda706bd485993c1a 5ad2fec0008abc302e993a7e03a99358e7113ad6cce435c81d440463e39fef88 5a0e0b8724a44adf7eeb27ed8cdbd6a11d9f1a00d0729f6325910ca6783dd500 c86e974e823c9b62811093b86885aef47f6c154a3a399acf088a7ae9dd2378099126801f27d89286775431f5599bdaf8d1e03077cd0aa529389efe2690f7a508 +generate_signature 2f37a11507b767b81357f9d03bce2d6f92cbd79af252ec737a2126e1f1a59fa0 2030a8eea7a816364dd23b81655dbd00e10edc8f40b5139cc49506db9fe12c44 907f88dde3d47b78945ba4180c53fab25024bc009aa678b34c6958c3af14a00d 180b1a09682ee9e68193ed9384fa5cb3bb368a40cb7478f56f6a090e43bfb80963cdb74d972f5c61ac79ba1be47b7cb60a84b416cbd8d19e2572764b5224db04 +generate_signature 40f923c6bbde2b5aa8ae568a53594738edcc102ae77d2eb2ba13250bb3701aff 3d0d965e35f87612a3d53ac8b62ae4e7d4aa2f1cd3a6fe3a258ebe91457a810f f88b02b56a6d583e5ee52898fd7124270a1de3aae61ddb45e3fd4d272ca49e01 4993c3374a6a3e7f60e9d932b4f103ad3cf373fe51af1fe6635b42179ef89e00afe8faecc1f8001a803764e577de812d6c0a80216cb6b9ead34cf204b2f96408 +generate_signature 3c4a9e9af77301f53f078bb5780d71d3b14e55457fb26308647706d88e7e8ec9 41a6ad8332efccc56dbcfb936c84a1e608467d9f58aad447e7cedc9c091b8f33 6ae28d2f4d72100a1638132f6c363d2d65c73f2f6e9e6ef883775ce3eee94401 3d6fcbac9b2a1afcb0bc477e82c42dec5e49c3b01e7e804dcc1f2e453ec360064ae9b36bdebad20ef352b4cf29acc698c2726b603a4344b3361dbf79ac3e2002 +generate_signature 6e361cb98046db1c58738f54bfa5e24003ad2843625089084112d3e956781da1 a40a310dbf5128eb712aa9ee5d339f8c0e78c90e8e44977ec83a6138b6191627 ce3e7483fae52df03b7c08d8f30fa43fa505f0a7948889282f5e46b0882db500 e890d0e5d69a392a65bcb6bc57d4a0677e4f8df82c711da94dab88b766dc2f0d5b52597c22266e321c8a255a0bcc4fd6303d37094e9166fc8f9d2ec17505290d +generate_signature ba7df37a2d75e962d42d31a91525075ac836100ed1a78ae4e978afa432a1235a b4ccbeb6c17e719c79942b74b81a3cfb5ccd63e0c06b12fa88f9e90cd9e8a1e0 a35bbaaf8d8fcf421d4307e3a52f63d382b8ff35c7f6e5f7a6eb0a7adfd02f04 902512946bdfb12fb7f5bc8f7d4a753ac6c581d6cc6ee3997917772a3715e908e748a6a61f51fd8eafd52e89d12bc1389206fce217e2af2f7769056189a16706 +generate_signature 81599211da2f1378330d2bf7f86e0686947fa402578cee048c6fd648a909f2b6 865b484252a23a1697c736ddf81d44674d9b51fcbaaf5bddf88061402ac65189 8509f1f20c25d821fe6057863277222b54399eba3712721c82c174d81650080f 77a3ec2e3ae64f15629e767ab2f3442cd109789b5372490e19167f7412543b0d45402dfd640900b901186c877bb60d526c0476bc8de1126a83877a47bf76140a +generate_signature 9655414428a87d11c0bbed9af0bb32010c4847cfdf75ebf4a8c73c1d5500f44f 53bc48c7306182d958147ed4510dc3c1e5b3ccfc16938f1a7d757ce7ca2fdeac dc1506e5b95e1d6e6e06ecceb4b8cfaa00daf3d6fa26fb672374ffeceb0cb505 4f20297b06023fdbb7f5dfa32abd287b084d82fd72b52ede9c9b525b08becf0d00706c4e573fc475551c3a2254e0f67a8dd314782a96244f228890482011f202 +generate_signature 3e1ad25eb7a1dc33c84bfb09d73d946aee497638ea639157e1eed1739bd0a84e b507cad326ce68772dd3cb43c31bdd70cd71d722bb551d209d53449c232f5055 c06773a6b15d1409ccf3bb4182509cd418fee29a93d966c511809b19eaae4d06 137c9cc3aecbb4e49a42c45fbbf77c031151bbdc7fd35feb44d74ef5a055910b780a27d8f836e2990649cd0028051b4b6b0e8e1943fa75ad6a0acec60a030903 +generate_signature 36bd8cd274466d762f8bd459e5f32dbd7662fed9bcb44b76276e793701c7f5dc df9013de104e242aec33250ee64a3019fb7ad37bc06a421c60ac21626c9ddd6f 6342d56599a678fca61f584facc41e316f0bf152165767f50e3346b7ec74a208 e262625367d30443ec5187b2834305321c20ba4f50ebe669a6f0bed6882e77040559f3e8666c07d85e2d2782f4d48aec33595b98c366b1fcf3ef759dafe1e00f +generate_signature 829f725bd1ec12e3c244c4b0006499fbff693b33d0797c043334ec591a8d64d7 fc559b71cc887e98088cc873b9d7c69d104276e257cb28197644f49e8854e69f 36228dd0b09d05703d7f8b5235b94958439b8a95b43c8e09b577a0fe6b26a901 5822be12556e1f78bf9bdc9a39e09bbfd6b2ae65ffc484ac018b45297e2f570ea4ae05dc51ec1d52a48f29eec8d7d8aab4443762f7883f263399b4686fb26108 +generate_signature b2fbde52043e979fae4cbfaa1fc218bb87b87b21410c498be62c5b288f0a59d9 fa437eb7611028c6141280c056fdcdd20912575cb57d652f78034f7afaa5e0a1 df1288522dc2cbca4473941856c0f24952f69b5c9efbe6df24396ae25ea91105 0914679fc5d261a5a97300b85f879e56f50fec3ff74a4d2fc13ae749aac537079efedba78da81e8d5195d7c7df20d9b0072d2a03983f11f9284c7c846bf1590e +generate_signature d21de662f0d58c9f746f0984fc3691a3d0bb0776e539a2282207b8ebd8e5ca85 679397579f31b061479734bc5fb2218c577503f35827c815f22bf53761c272d9 b04aca24dcc7f0c6d04c3c676a6efe8062fd5618d16155ba2a0e4d0485332c0c d2400b26900561811a0d6a0891cfe2d05421d4d82a036190f5125ca425b03a0fc3deeb20ea20971aa06c14f555617d3da9d14fd29573ceb74379c96de23a7b0e +generate_signature 44c6eecba5511adb489dfb05815e764f933553baf1acfb6e9cebe010337b24c0 a08e4416e02f056c71ea72e2eb1f8a89f669fbc225a7254e4c8d0f8d39ebd1d3 40f60e9ef735a23ffe2ac767726ae743f2844fd7e8981a96cef85096359c230b 94e41eb1e78e2c59de237965faa07f88d8e4e7ea7c2e6ad7f13250dfcb85f10c728b46ddc5022dc6a22045a9915878506c83c8e1bb2c0a8919bcfd9007b10201 +generate_signature c36bf112fe3f11d4a33a28c8f7e96e18c0a561eda487ccc7e441439405c5b339 5d764a790fb3442a21aef48510e95dac552d0f34c2b03289ab11faf21623c451 ec45f5dcff2cae290875866dcb122645d1b88cece607be63783c3e088cba410e bdcd151abf8779d168a31044c53ee4134e8dea28b24d821ed30abbb34800ff04b74987a0b71403029c7b72bd1eb4282c62fd310506cd552751bcc20657f40b02 +generate_signature e642c5bd2b451b20aab856686bab22f6987a7b5330c2491ad9d43059cb5b17a7 e5c73f7ca70afc4019ba044c177ab7ba3b57070effd788ec950e23f766617ae2 1430d7db432dbcf48c88f489017aadb6bc4a4dd30dd68245e281359e7bc6610c 5d7fbfa4716dcfb866bc2ee9b58c8ee400baa052adf113af0a2ff2d8ab2d560a3dc929357fff96de69401cbe17e67d411a20f98f803b51185ecd58da410f4607 +generate_signature 0b427120304ff23f1906c33972a5ddfabc833455e1c00b4ad522dde15f401237 45df3d7b2ff3ea6a638a360019b6998dff96ba9c11ec454fa763b75ed9e21891 07290dd0ccfcf302d33d38e5b943f338a40e63fce395f47984e7edfb617e3509 236b0ac251430fbd87d4bcae96974308f786f1dd3152c28f30b25c1baf198909142caed0894374de2b846293b0a9c1433652c77523fa78af97ffa68c2cfb6f00 +generate_signature 5ada550217e164eb59ea6506858ae1ab537c9cc15d362f94570255e558911490 891681f48e13a2f0f2a7e189995a87f9af3f3b4aad553deae50f38455eae34ae 9f12a804897eef85dc4f89885958acdab399cea0cbb6c7bd2e2b40185bf53201 b5ed4bf90e630e17150ab395b4ae857cb8185a771727cdc5041478957ad0f6028b64a1787e4d089295c4899277cba970ee4828331cd9e21c0b5a2b99f4418b0c +generate_signature dc200cfb46b99ed1789b022df4462e679fffde969ef75181470fe77d2f659224 a8987ff64be78dc7120fa7c7caf8dad90e4b8dc44597769eefc67df636c26062 4f5a3321915e3c9922eb7704a158f64d27d32192687412d28748d2ed4636900b 37033a8af69311b97c925a08d37effb494386eaeb75c72cfbe8f58fbb245be06fd8059843010949036bb411447d1ceabe0e6b57c7b5e920d72e465ed545b7b00 +generate_signature 7462d6b416e428b5c0ee3f90ca39575495955b720ad0e7e4c2955c1ccaae1888 36260bf759635f1628c16ab8a85e22d20d9554f6811bf2d7d2b0e724088c8567 2f37c7a26aabe14e904c61e256d5e2857adb8e94c3d28a57950bd68b5677f302 2bad91d4787750b59d1600f813e44035f199462ab4bc0b1d37bcccd1b8c39709d8705e625ac74d6fe755e6a9b2996f379581c854e2571535623834f0502ac505 +generate_signature 51e9571c4b6814a631509d7b43dda8a19fbd9c5536c3355c3b9ec28fc7d4aea8 cf237efdc47a5fbb1063f9851704177751b2bb6dd40f35a35bc49acd95e11370 a37484d7875c479c3df673457b1d82b1d8fc80df5274aae7d1aeb39fe2aa310f 82282a98295212a21a356089e9b207af1691cc530ad9847c2c62e8bbebec550df2844f503f81141710593462c56dc449cea9b2ee9e45344a66b4855a1912f90f +generate_signature 512f462d431403b24c18924eaeddf9d911d5c614d2a5e274c21933a136cd8e10 20f1bb98259c891bfcb5e11793ad38c2ca863d739f7a2c53bf5f74c9472b8fd6 8f92a6e203eb6d8ea988a11177aa370bf142928fb93c466bf18ab55b50492e08 339539dbed870e0478783f855dfe1f830a9da95ce4c20871b7a9437351c8b807526ac7befb58df9dd78a5327299f0503e65e57e960f973c1687da6877faf8e06 +generate_signature c67ceead9a67e5bb1211538a0c96cb3605da36f1fe2a8664de0f134781a85889 25adad7475f476c9c02b9fcd3c7865d261e5da7261031256dea250c71f305bf0 8a451763e81d056b3622ff5db2432b5ac52a4565735d1f399d7fe44f409b6109 22c7f9e735c6670acc67a95b3d3c8a5ba1ac748e5cd1d0a2c5b71d1a98827604c6221ff707de08c2bb57b53e2d0dbcd90abdb28c9da2bdadfb75895139a90b01 +generate_signature 8744e27a87056f551083b1240834edfdf4576a4b8c4db7e93c03bec8f714b842 ee8c02e0eb978f310eeca705aa7a4bfe38cc10a9a92e8458f5656dbcb22a604f ebba71caa77c769b5c0920033be75dcd8e1af9a19c97c4cfad52bd52efb3cd02 8646cf7643a3aca7a34766cd1c66caa5ec51ffd9fcc559cbed7473adedf3b900f7f8bbfe4496a67dc933a64fa17c054a4cb2cdca778a3ccf8d349e6041fa480c +generate_signature 01b5d99e68a458de21c085b50147ba1f67b0930144fa77ee5d4e1d37e91da1aa a928416ec7f0a87dbded403c87fcb073528cd4f90651ab9e3bbdd14ca61b6e46 370f367e7ea29b50ead04f5195c6b0486923c4e7109c8fb79b1059059378b302 e4c72bada89b238cd8be001c31d23c4a133b64c7e2f1685276e3b3d9c2234d09549a0881dff2ebc784fe32fe3e2243515ad476452d2c60bc3e9e2e2379f4a305 +generate_signature fe682bde8ff2a30f81d616c05b9b356e32b3b6f11fc4f58c133d7b9082f3fe84 dd191591dd57a04d33124ce1af1ada602b3f5304b15d4a90aa0732d72742f501 c2b494b0c63900df29f46d4ff2e56a644aee5cad4105aa0123bd9809d675df04 46b21d13b96473614ac7c0d02c601bc868f0fc0fcdb6055169ea5805826b530f29f17560c89522177a491deb6eb8992b6ba05f41bb1a322b7ac7ae3bc2b64707 +generate_signature 96a71cd74467e3b95e4dd8874535ab8fe7710ec4956aff40dae1e7849bcd4c24 c55e53a61395c80d8adddafc2bb1224f1e64ca8014fd13d809c86d7b5e299e0a 84356cbf4af292e87e3b7fc328cfe45d5bab67b784405de76533fa4fe049d702 675301cfd32ff37661814c7330c8d8a8ae3e24bc1261a42391c284ba62e04800181ed6e94cbf93900214f12ec30755d4f907df382513f2957c86eccc49f41002 +generate_signature 7332333e4bedbc6291e64c3acb32779b6bb94493e83e676da1ea8e7ac593cf7b fc9dd303f58dbb87fd82695b463e1197a265279031cf9ae5a6a7e6dafe2f0fb4 9fabb96d4292617086a3a9c0dc084bb6118f73eeb602a6fb3082a37fe6d7a40f 2c1a9f028c3d2b32155b01a5f8c582544cdde6339edebd7f3d76f4c13490600e96d38c95eaceec32e2f131b0a9397838b8cb42f780c19ff5a7c36e68e4deb508 +generate_signature b15d619a2e307fc4e1848688c3388deb8f2e0eea38328cae0886adeb42474204 5d2eedf3536c0d1857d75190f3da8f9e214dd170790722c0ec90b5876f46fa1b f107b7b3f7e17ed25504d36574d01318cea43a84b0c275d097ba0cb83e872601 29023024c75f7ed826cfaaec74b65e5ca1bfa33b2f0d243cf4c9cecbefe00201992aaeb2c5ea5f82e26851478beee039d5df5c96aa35020570bff48ce3a0920f +generate_signature bfd9fa1f474799d11ab3ef3abae59c17b300b8cb899b2fcbfacec678cc77bdb2 362bbd5e5c95c7e1adb900a3c6c5a67b5f050e58ceb6917bfd037abc61457f23 4bb72c886f3d978943afdd5c1e53882caa6b323e0c0836a1485156c569c2b604 e748a1abfdcda647d39a8fadab90bb188abe998265b7ef57297c06d968714d09772dd7fda3ed39566683c2efc739b0d620b21ae98582a93ef73b0660e10fcb04 +generate_signature 3b8254fb834017e439bf4c9db7a54b8d72cfdfe734e64e3db94bc42761e52a64 3f1a3561b18915b34bf1b451fa01ce6b9e2124719b4f3843da1caa106f5e724b a9439b2e768ea849b35fe3e8f31fafd2460343374600031aadc7a047d8f4650d bd317c6b254ab2e61936bc56d0cb7cb62b8b338d77b3c2e1b712266f06bd080cce6a7fbddee59e7b08335066cffca021e69d92ed29e741d501fc2f5978232706 +generate_signature eee51b7ed448e86eb34c493bb7cde32b654a2c3b94734b54e313b44705736194 9cf9b0c581562977e791eaab88f9574ac7e7b173077b3e67283fa00ae448d19c 7e0763abac9d638732d9cd91c7662b13b14067fb458dd8c13329ba76d9685a01 6d301b174b139fa13bc256f735f57576fbbf549972b81e27495267ff22711609c71a7fa2a62fcfbd32772317e6eee6b34ab48f1bef28dce7e03bc1a2bbd53400 +generate_signature 4d0fe60a2d913071d88b67d806b451e5217b66279c8a366ea50df32dc7f019f0 30be371fcd62d0aa789bec1511074649e012bafde098d1997867cfd1a1c00020 14ae35724fef1e5136b485fd319f2ddfa31858c2f6e8718016ac61c7bc09a00c ab70c4bed07544e9274e5024429a9a19399aec1d87a8543f47c7d711470eb70dd7c7a6a4c6bec508fc7d2fd264f930326c2cbb7cc27ba86df571b3c5864cf008 +generate_signature 52558464c45be054502fd7a95873356906175f45d96a123a679c61293ea82c59 f2df74450892e64b630c1bd242ad6d6eeb74eaaa37817359f0ca6ceedadac351 fe2330bb3f97ebcb29060bad76f122164f4f376f1dfece45ec8e1b74a9d73304 cdde8b482122d1b619b0fd32bc53accca12b36c44805085fd70e2708df416202d67a8d88cdee6f4208ef21b2d8049ad4e29ff70ea77fb6186934035c24eb1101 +generate_signature 110cb3d607cf3cdeaedf72bb5e757beaaf33735c4e0a6c9d36e15c3bc44f46a9 299e119e03f8698bb3f91c0383fc2fa3566893b430c4181ae75da3b6db52a14c 5f8e1334f8edd3031ae48b6167b69ee954b2dc825d96c1a66115f3e0dd19bc0d f20b445501d16dbf341e9d47aa3dc2287b55a79a1b1cfa2f66d5018fab72080fbb3ca626e6118010a384c2f702ef146eb57e41901b1bd30ca06cba56e5a0a307 +generate_signature b9c72e905e48aa099d4618aa4be67625490d93c298ae9a3ad50bd3051825ba5e a2d1ca5b1280a41694c3378e2346b3f9ec16d27905a7d9f1ade0aead412fdc5c 2fd3f1486114d6f267cce879108ab62839c678eb46a846383c25904f47719605 81d097e25c91dfe2315b8f93cd6f91e319753f57e9ff6e9438ad456238e4a50b1034bf01d5e1e21e1fe6b5915f26f942146b5747aeffe72f419ff390a25c3908 +generate_signature a61d5acf702d80aad1e60667f5186d692d03c291015af38ff48b3f627dfb6c93 794e3180afac94b50792ae3b11ab01c8ef4e02973b3c2efa5b3110d2cc5bc5fd 89a145b6365dbcd67ad36878174eab5d3278b2da8a0724fc6b9fff3dadfdf103 e9523cd800341c43e0dd14a8ea794ebaaee58531ff98bfe7546969082cb758093a9e4532f4e4701195d703fc102cfe75e955ffe7b0c2a43cf53d05649c488a06 +generate_signature d0666d9f44a461d56c2c6c35bd098282042d7ab5b4138f80b22b98b940df2543 c97f167d5fdccb9158ab96824c60752f6a1034e163b96da179cc42a8d6f8cea9 443d72bd61cb9ce36ac11839333cab61bc85506b91dec9ae8d0071fe9805ea0c 90b07fe43d85974768e23c059d9b5055b08931c9bc20effbf6d050340c2f2a0e1a97db5d0c4793d3fe77d5ae26635415e3ce5efc1606044365654a9e996ba105 +generate_signature dd4e6582d6f65498b6e6dbf35f5dcdfab52f90a8eb7ef7391111bcbe7066929e 1200b5eac5cf70e9c52da437ab1d7da3ad5f951f65baee0ba8aa02ffe69a3675 b72904a3d7180d9c2c16e090c52051ebbebe0ba8376e2defd850bdd698fb6f09 b7da055de4dd535d7ff9422c62f752acd75e48e6e8cc2fc1cf713766c3905e0f505d669abf2e4a5e372c8a6b8f559838c2bbab0867a7e6fb9fd4cbd53425c506 +generate_signature 6a1c4227f59b75fd001623aef3687fafa8eff103526023aa6676e8f08fe3c15b 1613c592fccc2e0031487aed9aefc3eb0b86ffefea0f014173bdb28acf4e8bb3 bb93bcb48a4efa8ae0b0fc9393653f2696c77603e9fc3bc1956473f04f64e402 18cd092bc73f02cf80536903b2f54f75db3a4c9ca9fc6e2b0d5dfc78cda09b0c29c02f4c5ac14e564759caee18a1da5918f956466afd18a5ff58c6a3b79c0a03 +generate_signature 2677f450f708876541bbae6ec7030aa87d2f7599bb4f669c190b8df2b62daf66 7a85581bd21fdad5609a84478a9e61a084ab78021c7bb0fdcd6c9efad8cfcbb9 26971f12134601802ef62ff5dacb99e5e0e08ea2455861e63af3cfcbb8cc050b cbff686c9db467574d01391198f1fdbb2bd2be584a70fe183a88d260eb017c077f1d0b8e485fab40325a2fd3eacb9389f7403f2785592f02cd2262bdfd2b370b +generate_signature 15bbccb57984f152d62b0c82f12d732b75068118fc3062805c157c33b9f35741 cae0a132e7e32e55906039a7f510d940cbf4dc16543beb1004b636866635a5d5 6d16a1ed565e148f653ae3c20a978dea4f2bdf009dfc8d9e66a201c9d184e906 454824000b5486b964a3f52e4cb7a54c2f59b9999c8f67831fa4e0499ffeb50a7d36bb7f2b3570b9225abb5e74bea580db0e697a18099f151a259b28ee05c704 +generate_signature dba1e5cb897f3a931ca00c61296c26aa6edaaaa95d844e8cb29a8818e5d3cf40 8011fbc326c27242f794f1c93e7e616ae847badaae42e30ce345ee2054752490 9b02000c18cbce2c5bd97d9e572ef1124b710bc4bf820b5e740c6f52185fdc04 97a13eccd2d807dcc737bee9ed59a69aa630dec7860855e1ffcdc733a26f7c052dc7fcf7eab11915c7739567d1e2a72a2690a6dfa46f953574df94b1a1af1b03 +generate_signature e9257b43c57e17027c955dc21454cdb41cc59ad29307468ecc595466470519ea 5c841fc7705e9f0691444e3833e84b946f0978500e66dd187b6b4011e2bc5722 ad7c20800b766bfc8d97bd71fdd21b3390f9b5dea4a763ec94209e832add0c0e 7ec5262e186734402e716ad856de09c243df362f6e0bf25090b6871ba125570f727863e4a84363479f74e198721ac10f827ee1d3afb90b62c5068ed345ed8600 +generate_signature 49a91500dd7fd49dedfc127ad940124684ceb5d0d298fd708906728551ce059b d19aa3856ff99afd3a621c339cd5688ad3d108e313cc6a12e76b857a56c8b254 8120cda41048d6102dd2ad998972b345886573e7caa4955df7c3b6632f96a50f 0154950234e23af654f925db75e160cd8f0bb35465162ade260eb076949e5702c163dfae3aae0854ae8b562122cb84ce8b1a5d635e7d3d11f4bbc336f03f6804 +generate_signature ee3d3c574231cab4d8b8de5b127423e88ce63d30ffba94e44b7cb52d933c4551 c7c4c1ebb9ac8a9ab8c71bd032c95bf4d76ed79fe8ac53265f8d2e0ff0913e5e 685985d5d94c9702bc73c58eecd4f2c69aed4b5ec1136af0636ee81d94c2530f 211bdedab227f588636ba329c6b2b267353fc7dd2f632f4e86b7bcaf890b3008833e6dfced83f50d985b4c6e20b10bf6f027e760861ac9c16e7293716ab3d400 +generate_signature 1473d8159b8f7ea02a7227be5ddd874e9c6e3c114708f7c46de729a81f0379bb 7d995c9eed1ff652f954617817f772dfeaad1ebb968c3b4260c22bcc6aa2e764 302201443d6fcdfd98585792085a7b7e804ea4a79bca8fd6b1d24d9ec30eb10a a89cbd6e854a2fc7495c4703fec6bf15d3b8ab93a791e413d99f15bad0e3020592a43f7df9780d9b2ca6d869f74631e380d0644a4aa07f44cd7f63fff7f9bc03 +generate_signature e0b62a10aa27778f06ce587a439179f6be9a444588be2fc6c61fec24e4cbfae9 b31d7350dc561bdd04634b1c65d4d07721b21be061f4ea6779a1a9f0268830fe 033f706a513a3102241553f06fcdbaa4f1899f9776f51584495e0eea1195c906 466319a5d827a73c8959e75421e1a664fb5fd4005d3c43d884544bfac7b5fd091c1f008e0cbfff0dcf5d6bd684e883a166702e60c2b0605bb6f1d3412fd6df03 +generate_signature ce6fe431c9d841f2f7494d0c214ed3f4d470b1de679e30d0980137dc8cca5369 7c4883ca0408e5279b4333cd5970366e7d2cae46e88bfa9efd5b9554b8677438 bf876c480666daf828d4b5aed26b853372ea58355c370ece915600f127dc2a04 85414259188aedc51ba52ad359dd7ed42bb26f722678ddfed3c3b6d52dcef400a72ac3d0a615ea47e37549b6907ec5b6c61cf33708348b008d52426ae1242d09 +generate_signature ea984e9cc588ddcbd884642c0c1eb40dd85cb049f22688a80f42b0835b3d2721 4e200ad1863ddbec552c937ea0f28577d93959185269c21b5b0a2faf11738582 144de89fdc0a762c28f54d7cfe1d9c7c1dce3d1b520bc8e6b1df39405708b505 ae51262262a823bf3980bdc0b0f2434a79abf0b5c58b35608602a219f766f80792e60a0ee5bc3126991098d90dabb127ff1b958f69f0b053caf1e1c95f4eab08 +generate_signature 298dbd2126d0276c1d95e2e5220bd529b25c16ca670094a667fd3397c7b7b22b afb3b9bb1769caea17f4102c3e59eecabdc9108195d33f0129307af251f8f330 f17b90b7c4bd2169f7f1f5816f8f72a2bbfc03c4617b957db3ffc04b6e42a303 119f4c615cc94d3441ab0e7d80b38fdf67fc248f44af479243881b9d95e5f5066366c68625364c3d457e6b9510892198715d2a9e26c40a7a9f5421d242037b01 +generate_signature 9c5c87cc348b763b29665e7e9fc83c2ee425c57bc610856179879b5fc644dcc6 67894b54c0628bb6c56ee847b712253782a9b02a506dab960a5b8eb9e613215f 082fff76e7d834c3034b37eba3aaa231b1b190ea3514791beaadd4b3daaf5508 0b2fafa75654a9475c203ffd8bdf68eb5ccf5d9d45416192e9c17acbdcfc0e095b2f27da63396e4b675912b8287f737c061580e037279ab0965dd8b440530909 +generate_signature ab263c0d9626de5860ea22e2f999676b455641c23e9edd517f420c01e84cd2ce 1ecb565cdd6da4bb6f6cfa57e75940630360f663cf56cc716f1b949419f9b6e9 d22ef716713fc60b11e619292c44ec432fe7bd4cf582544fcf2ebce23d566009 98a7193f04fbdb698010b06a64c8788b3e515b514d36e04cc5b3a384a9fd5b0f774fddc20429b293e6fd951a5fb624f368dcd4cc0dc077db4319b0aa13cf2901 +generate_signature 2acd9cb6a9418f90b507b15bd258878fab69afebd0cdc57ade4719ab89be32d3 814c8efa712bed141ffa2e72b2317dcba9e917330523946c2a97e8af3f992fe3 199e9c09c85ff00f9431ef41d7d9a56aa05cb9d09f548d2fad7a783eb2faa400 90b6d1e6a0fcf64c0a62cb94f16f7eaf50b88954ecbaa65f97644ed137732200f981355b9f2a30b76b9277aed25d44b37141383f96f0fa435b7f0385d662010e +generate_signature b8aeb16bf5d31ae4fe7e321da12d8defa7923946e197390f0a1696526099cb03 eefaf0226cbb5685e2770e2b72a0de26f30c9ef5673053fd64e762d9efea370f 4e524c57a12abdf684c05bd99eab65efb947ff2a50b1f27ceef7f4cd268afe05 620de71f6f844d9276122bf200e757ea8fa38e690f38ac6800cec1673ba64d0de43c4aca2bd0923d38d0545cc5b04652f29fe495570b5dc0daab4d9349ae2c0f +generate_signature 4fbbf6dcbbf50f92d5f60b467c9bcd4921b756691660b2ac73b256e6b614acc5 0453e634c6c63a0dd1a999307b2c9da357b54fc1e89eedd1240f6501475d35b6 ee840259b97e841cdd17946b4b4acbcb90c7bc05996fea802825c62f18c00902 9b81f9e7fa4839c114fb78bc6d8fb38b1d6032cb23345b467267e56c4d76bc0e35293a1829abeee61faf70698266a274c91799a2e6e1ffa62c1d1bdcab9e6a0f +generate_signature 7038946351d9332520e91640e4ea8aad9642e66a9661c1e62a519a3e03d24f57 9bbd178c6a6bc802cc56f0565ba7ef52eec02c1f0098a3fb1a958aa1885e4d63 f68267ea33101df63bb15fcff1bc528b032d770fe69a0b6f0341c614e7ee5d01 5f4b4211998242f68c29fe381ce56763e6781e20741cac081a50ce0cb535710ab14984706aecd21825be565f1042d5cb0fc8722e5d3de062fec1030d1d26d20d +generate_signature e8e5ae2e4f4f51cff909a6a73d0a24be6e049cdf32af893b6156a9f3bd7a0613 54e78d50e27c98418a28d0972af5ff4f7e6a5f2e212d792871f4395a4e0842fd 873ea1693f04187e4b39eeb4395d38e2b12494f3a426dc3ef1bb58a2c0a55608 7161a8faa117477f6b943ec1ce4f28518bec2b517d08a442d05520d17e33400807ae101923b353c193d158f0b192f147efa3e32c867bec890121c99dab495502 +generate_signature abdb0a9f87e46a82240bcc77f5aa5f8c0981398731f7bc392b1e7f2d569c491e e8551e4b00aa9a74084250f33285a955efc56a310bd287d97ce3f213eb51f97d d28b26262099d372ce35d41e3a289d793b425a62205f0f22fe348510120fbe00 a5a40edf34100f4f7158abebaa700ea38cfc65f8d3cff61f413ae225fc8a6505607471c6e0d0d3bb5b36a6b81700a39c4d15f2090f216d003ea6d8de21c1d005 +generate_signature b6e7c69d55fd322626930ec53729cc2975b911706721a48e41ca406146cb63c4 58a63d82e3245e9771435211f2b415c5739c99370d08011e0baa1b502302b16f e1e4f2327b82c6fd7fbd35905d91056df0c8df6702eb282487ac55b3801adc06 dc6c6bbc0d93c2c7f4a5ad30b45301d62a133fdca1439486a274fa3d1fe6e702633d12045668604095f840da7cdacc611b3740c22843d13014cd7cc1311b270e +generate_signature 98d1fa8e6b508971d25a7147eedd2347f076b72729a35f9b1a81203ff85c810d d1e1c86421a34483152b3cc8bb124d140fdd638210ee1bf0df30c80b54e61343 5acdd92d2c8eb54a0ea6d466f5c2718d703a5108ffd352b6f8ff70f2baa9010b 71dbc320ffde437f501015672bef041796a87294c116b4427a46f491b3a2b6016e74d506626738ed7f0135006403762c6f7a764e05a1632bf69c2e540acc5c07 +generate_signature 91dcff5d1d7ec3854de213306e4e744ca4c04176d4b20830d1a2012e6cb026ce 15b09f757df19ae41ef4e9050d0be8e76c151b20a7f4f792748f83aa14b7e0bc a9e865fee7d1469c369dd683d7808d5ec65cd0f035dd5e09c1eb034a7396c305 0dbdf01fc7e53195ad9276404fd80cf44aabad5eb35dda24dc3b693d93e9510321f4655b8ce801e179c29d844f165e39e4b8598c253e6351902181ac66afa007 +generate_signature 9291ce37db25aa4c373266a7aa25d585206492949c8ca4671eb7e470c6a85778 9d35b6133fee1dca194f7a64e8c1dd4a03a76e3805cfbcf80c9c42f9a45e91a0 6716b59ebdead7684c13ead0a4aef3fea77cda742bed36b3dee1d66a475d0309 333e7053a5380dccd063e503c5b7804a67b2602ae86c936b7edd6433dfdd390aa9b4cee2986e18eaf793361b42c69ac498054278793cc6ae0bb7cb1576477507 +generate_signature d2dfb2e50813260e6bda0a4150ad893b32f99b2dec47ca8402f0ab8ae30603cd 7b8bd1f571d5083a8316fd99efd6a3a3dc54d87d78425ce1f46185a2cdb18a95 5d76e12ac8050665387e1c8af4e18308d9f227b0decf22355fefab31ce1df804 74cc5795a093ab0703e3658b13cd06a43856a8cce0600484e5ef439a90f57b0531d4fd659a5f4408b1668a91cd24182543b5f18621f273e261f9f39c75513202 +generate_signature 7ba6dedecefb1d24c087c994a2ce612e568fe90b47eabd04a9f44a9196cc7d06 ee5a5d892f36ba3ec29b259ef936c1a14085caf56e5419ce2c85dc356750231b 648229308a95c52f19b3011f816050496d77dc42afff59adfbfa851ff4da470f a83511035b49ad8c7e8b2364ac0b9479b68cb5faa718a94d17cdd0975dbea50591ebfbf42f6306888dc286b04e1d981332b9af27df8edf513a5079f27a88990d +generate_signature e53c575148d6502987c90b32ebc374a2ee76fdb167b4bcc2fd963aa3293a50d3 db099a6c2dab7de4916d3b2e21c2bf1f89f24280c29a4802ee27899089353573 9a34773656675cdf07d64bde08eb98585b9719cb9d52d919d54d70047639b508 2096f9e9ab033afeee8ac2289cd163b14c77777ac1ed933e8141b778f376d80b3ebfbf400c54a06d6bd65d35b42364c4aae8f111b04134bb1ca656ff7d96b70c +generate_signature aa909b74b14671e3c56b0aa0a39e956897151694e42d90f502f5426606a4bd3d a2481756e3204a51d40c8088e69851185a29c962f97c800a88102b812606ca0a 47d0d3279058134729f860e7e0dec0943115dfe22f052ef7756b5bc263aa030c 73aaee6e2e060f3694b6f43aa545cd3b8ea46f98b2f2042418c4c0bcccd0ed088c4ef6e9ba9a387f658afaf0accd57027cd9066b9c16aee086d6308df631180b +generate_signature 09719be164df8ab8ffd5d4b6820d1520992afbf6fa717cec48964f3af489af51 3798b31c7c0f929e70268010839042b76df746c4f46b0e219521eb73e89a0d4c 516de341e7a3e73b96f2a8e3c0427bb73eb04fdc967f6e0d2b87135c0ad72907 d9fe0e18d772a2c5a952b48b465672dd1d7aeaa577c7860e5a4fc1ea793a9f06c512c29e07efe624c1827a1d98539183cf8b426238bd19d1248400711bff660b +generate_signature 95bc343c1fbc1ac7fc31c617f0944c8a5caa4b91977bad385ddb7b72133b9918 3b510a05791831d9d86d268a3c56f48b40bb3b2854236f4a1457aa573f1e3616 83ed82fb8ea18afb5ad9664ad412d2a9ad5d36119aa08eae5ae48d3b6e979c06 04537ca77df512dd2b7c2221c0efca3dbf245a53834b36fa9796ef36f15c78088e423204a1fb8dfddd38ed481c2d240606570f049eecd0f2426a7ffdd2959c05 +generate_signature aaa59d9c7b9745cc62775f1a565de3e704ea082feeaabf91897c163e9c453057 f1d8a8a9d1ca40c3c1985276b8dbad02f9b05b308633f8d53ddd23fde382046c 38b8d2d9729218db5231f4e46c2641bc43983320d493eba916c9d99aaac88208 156f367a85ded5d3ba6312da17ff5ed633c921fa208d42f84a46a89a0efbe50c3fd45db8337d59f09c71305638aea28f1a742474ffba4b0b82f5b274f73d990a +generate_signature f4433ab65909fad4afe06cdab382d2fe7b1f2d12a47ddf3c4c634bc9cddbd52f 320c05d188d2ecaecc06c15352dedd207739ebde86133f7152b3b83074764778 d34800b40f3d39319a844b8a85c65bc8e7aee303a7b8e423d2efc1c4c48a6704 91912604506f1354d0ebfe8627919affc172cea295a7b23deb76f441bbb7170510d56f62f506cb7ce2c49bc68766612e749ff9c20612112bf74f0579f60d730f +generate_signature 121cda7dd82e45d586ec16c5ea52fb38026dc84d3acf8b53d9b14c752bbac650 5af2a1a8a88c0e0b53e555a7077a737c2cea050b7dbc0ba3d8ca2ad037c908e7 007af32d54c623c7eeda1e02984238b6799b08db17e3a4231daff62ab3d1640b e28d3cbb8ac6d890dd13651888b9786d1e290cf9d15e85e9033b70b0c7c9e10edf0b169a08dabb7ca9686304f6eccc4a4419732a1d28c3170b02f9b1ed815106 +generate_signature 4adacb5f6d707bc06145c6c445ef70db86145b78af456ee5d687530713f153dc 2d96e3dd0c90587368b586dfc7954d5d6ecf04523d0797a0fedfba91f65649b1 1f8093909e4e249adbd396d9296980bf6b65164dc6ea679d955b98502622de03 d6f5f3410d592f961a60fd6b491d83342afe0296b80fe7ac187384684097da04d7ff27f33b313e9682fe3cf40e2ebe483914af24f1063cdc71ae8d3fcc4a4901 +generate_signature 86034412d429483e52eaa4f8e3426f920886f5a7302f9733b60c3368f8feda5d f47f3d62360154548b01b27b8e20bb653569dd2e0d8f3e2e4926563b19d2905e 383927c522c2201332dac111a750e15bfdda86b1d47483a11624290569a4f808 a6a30b194d0aca299634d987586c3fabff01e1c6298f252fca4d86fa80d7a70f6ac3ffaad7207affe8597417f36e408f899667211a4b771be824e6afb8c7e40f +generate_signature 891ce05ba0c25b7d2409dd83487ffb0328f515d0dd77bf1c2503e33370f8cb99 0103c4ef2f18527e1630510bc27ff5966b018199c0b82a4a0d62815c74f1d0b7 fb1bf90759993320f4e16f3237c2d63dfddf6594f21c74e20267d61a8bf7520c 3e87b08bdf0ce44c6cebd512268f4c121adeacea1de178cd26bf141208231f06ef01e0a9d5b53aa839a11ad18dd7c419022507890f79bae7e5971e9923950b09 +generate_signature 268378aaf9b6403f036e24e356e90d67c3b82edb01180fb7d135e895283237b0 32b9fbd425dc1a9e2d6c693699594bc310737d3954b8dcd5f3bec5a056a4e63d fa028fb4484c90b355655014f1f20eaa711d1d61ed8e200b718b46d475603506 98e042418febfaad6c4439ea522216bf948b73134c5bde454317d7ce746cf9052268c5715c4b75ea078925f2c38e63695dd5555d5e747b884775684e4ef7070d +generate_signature 4c1124a23917f12efbf34c9c90b1a079667aa900b78b8a77297f55e50c3d6e21 ea3392bb6cb736c3053fe90bc64917412190b673f2d1db93338414fafbd15414 54cff91e78a84670f568167ee279e72cb0a32ecf75544b5e4d49ea5a77a92003 e312ba94001f7daa74bec81e592584fc81a64265fc52cd50b53749980e5f7c08c0171070b787dc05a2f8f3c6c9c8f73cf393498bdb2fe49101b6f43ca96a0508 +generate_signature 112cd2bc459cc678065ba583a09c83d962e385cb546523901ec4d5b47425d0e3 0693ef1cfdc58c35a00dee5477fa4d8c73422294c88b8f9d826dbc414dec2306 a3d6655a3bcdb2baf44248a541141a67018c9080001457e165ec29d2c3248600 7db175555dbedefbc5d11e9d53ce989d5905941fb7ecf68d4f4406e1b860db049885870621bad1733c394bff92439fe9553c49e0193609436c9da83ea35efb0d +generate_signature 2de379049e28fe930809c36f1062769665d4133d4d668a718b84f9183332a2a4 29aa902ba683847b1b59f09ee480e1814454bc72c48fb3884540d369f1a9f2b9 de444332bd0fcd47f6c1a5b7fc7ae1bb544a2298672288f04effaa04aa7d940c e18bfa9531f2dc3be33f8c65c7e72fab9e0d377f2aef5f284ae55523327e09089a7b69a2cca10c24c9bcf3292b17c266057d489443dc489b6ecca2961c2e9002 +generate_signature 0d2ff1cf8e58881dbe688be73ce844772c6696636469eb8ca9086a3ee2ce07a3 a743a9ed747abc154394fa822224212c3f7cb9fdfcb40a24a142740d1644de9b 08281ab64eca078a437c7f10208d0f5692ae85f6603fcd1d9e8ec2bcf9f64d00 94e06838684a7d42210d145ce3bb0a3af17cd9c8e9269ba03b326e41fdbadd0cb175e2155699cf9855a94efbae919416b3232d4b4a91d229dc294e608ec15901 +generate_signature 7104100649edb970e649881d4059faee3e51e221a3ae07fc33d944eb6c192322 c10ad6f4972dc849e2bac69e549929822102071d43f404abe8049a529b9c930b 0e060a4fef0a46f9c3d8702b38f82270e76124aac1f6a8e1ec402aa9e7812b02 cf60c2066ee25e8c4f88571413a5277b793031796b9e3ce789236cd15c93d20c7a26daf3a8d9729e19d6fe3f62f3a24487baf959f4d55ee0c737e93c7c2b000c +generate_signature 995be2688f3f7354dc17d7fbd48e010611de9d84247e1e738be9faae777d20f9 89d352fce80cc5dba45a618468866cbb9414f5fdf608ed5dde3fb446fd665c20 791941512b24ea359aafcf96ae0d9a7f7c24d34e2177eb1822dcf12776580506 588715d6bf8162263ab4984bbf1f264366afbe8d62a3e08a2af4250578d4af07b2a8e7a36925a2248c328ea585f830df5f03a7a04cbc351ad451b98351d52706 +generate_signature 77262429448c69ae1fc4c99b652c282af2082b046a227b57dcd724f56ba0a696 9b605f01f72e9fa6faffa84481b9e609f7b1e20b8a33b46b16cceb7f5c0cf411 b912862268d63a977c75fbfe9aa7008190758367379862024232c0cd2504000c 300d12ab9700ef5255e02aac6aa389bf05d0ff1bb66f6be966b48a186df1b3035896f690b5174e38822ff7eef74009eca902cd1b6490fbb2dcb736d055f54e07 +generate_signature 382537401e042257c1a0c6062ea8ed101da3193a6cac540d675354f12f7cab82 1c209e65076e9d4dbfb5762f3d1f59896da89efddec6d696a4498303f67d667f fdc2f3443bb4f28cda3ee7cda5255d36a89a983b91361700b63b87db477d1108 202c665707b18b265040467c06fb3909508d2db2f77b6bbbb6891b09cd5d5d05b00ddcbf87fc7ce47af0605acdb94b145d6b31879d3a75b87786fde9c091d407 +generate_signature 55b89623ad6f7b1d2ed599d2a04d8236f2d3fb8b337ba9704439193ac7956889 eceb2e5ac867b1603e9c61890aafe7871b3583d38c0a9f4084573497bed466a8 cb1a0a38f92fcf57064b0b1e666693cd1abe9a2b42cd4c7c667c933e3ae1340f 14a0a8f8066129721a4ab94808d4115f769af3290cb580386bd1b9cfe05c690cbcbef2b272818204d660f27b70c51e6c57e66569b9470b2df37706083c2a1c0f +generate_signature 22c3ac4d3466742eef52c67e784050c30b5d5ba7b9235e4fed8190ec18cc50ff 628a568ca8cc07afb1ce46b3f78af000a37dc9bca3fb8f73d7d5e697b267fd3a 0e180e4841d5de78630e900c364dec44daf2f74e72066cc609eb917995a8cb01 dde10641b93f100df1fea2a623493f901521fd3596837080dd11dfaa6dc39807a94a9c871daafc3cbfb3b59b3f986a995649284c03787c559a495c6459d5b007 +generate_signature 8bcdbe48e967d9e68775be61493e60f6c5488427caa85c0c42b3ca3aff756b75 5de2c451223d0cc7293ca1826c02215c6b519bf3ee0e5b91483990c6250d808e e8b26635c9a1fa4b7f9165b745abbd45cba7258b6420187ee6e4325a89b0e802 6cf7d7d52cc46ffaba43332cd15b11646fd32240d50806504988344a5e1e850411d777a982d5dc737c9be327e05578074835b2aeac88e83d9d357d4d16602500 +generate_signature 37b33cb52f44b8c43a0d619ba8270eeb2ee657c0e70734a05c2de878f77ab540 7f5ce9f0aff75716477a94c13dbcf878a8a2513920dc40fd2a29814555ce1629 f8afe1e242f39bf2b08c35d6d18aca0900a1e5ae729efe56892919484b5e8c03 6d8225e08b9e87e411a095d8567c5247b9ad0e4a20f3c315d4ddbdbf47cd900b06ffa84ca6f438d6abe48bca459fbe4e4bbfc93f4bb373a8127cce97a34c020d +generate_signature 069bfa548d79462cd27ad3720842430f2c4947532513a36f0e012097b77e6a7b e6ebb1f4eae6ec2d813b9213050ac47c0054723d754685452f112551e9e6619a 6cb63dc9d194448e748210d8bae8bbee8e0cb494bcec2b51ae06c9f1478b740f d2a1f62bb022fb37ac968167b4ee5bfc643a56b87ff87b3291d3f8e05f0b0d0ee44ac410207ea5cbeca6e81c7e0923544399bb1e685d2f7e4e7bd57ed3519f08 +generate_signature 3fbde8fc46fb4f1001ac7fd712760e0d8ada351c2d2c28b6da74bbf9e2795c80 79ae18c7007b1a790ddf2e6080615398ca77b4d06c1b2f7e536cd6b8ddd5a21f 70f8219f8b4d2567a4771051f2988b84f137a4f84cb26f081843908c7d959809 11d6603993ff0a4d34d7dbb6a7b412d69210fe16c34c3b2b0cdcbd6fdb857609d9c358093fe963fd64114cb3c5e924dc656e3525e673f03687388be92b21200a +generate_signature 7b752d6c0cc3750012b98ff0a4fb325eab31e8bbc0dcd995d9932215386e4582 9519b641893b754f8224bec54da6b5c06730c3f4c7d4704f2999a21b46a30032 0e931c71c9a3789e25f79e722cd6858d0fff3f5198597b056247b9cacbffa20b 4382d6553c00e04a84615e7b58ebbfedf18d9387b4efe975615f5175d464030676474828e4b05caed3aebdb9813791f492c522f364f21c1805270e6c472be605 +generate_signature e64074ccd0cfb5b56a89cb07faa1386061fc4853b0b2211d87a7af02bc3fc0fb 7072faef529d5daf4fb8663574a4ed86a7fa17e5cf10f09190e280500a216738 d6d77164dd8a1f6859e8a814d2a17367b8a8fcb5e6bdf345d74ad6d70658520a 2ffab2c3970c814be63e38fde4f5f23d60f19eb7d24200995712ad643d0bb9096bf728c1ac616e417882f9956da4b15a96ab3e4c5f301d2f75a40eb0167e920d check_signature 57fd3427123988a99aae02ce20312b61a88a39692f3462769947467c6e4c3961 a5e61831eb296ad2b18e4b4b00ec0ff160e30b2834f8d1eda4f28d9656a2ec75 cd89c4cbb1697ebc641e77fdcd843ff9b2feaf37cfeee078045ef1bb8f0efe0bb5fd0131fbc314121d9c19e046aea55140165441941906a757e574b8b775c008 true check_signature 92c1259cddde43602eeac1ab825dc12ffc915c9cfe57abcca04c8405df338359 9fa6c7fd338517c7d45b3693fbc91d4a28cd8cc226c4217f3e2694ae89a6f3dc b027582f0d05bacb3ebe4e5f12a8a9d65e987cc1e99b759dca3fee84289efa5124ad37550b985ed4f2db0ab6f44d2ebbc195a7123fd39441d3a57e0f70ecf608 false check_signature f8628174b471912e7b51aceecd9373d22824065cee93ff899968819213d338c3 8a7d608934a96ae5f1f141f8aa45a2f0ba5819ad668b22d6a12ad6e366bbc467 d7e827fbc168a81b401be58c919b7bcf2d7934fe10da6082970a1eb9d98ca609c660855ae5617aeed466c5fd832daa405ee83aef69f0c2661bfa7edf91ca6201 true @@ -4198,262 +4198,262 @@ generate_key_image b9adc45192d3096748f58c6aa4180c1084ee1c78a116c3480ec522b542021 generate_key_image d8e79793544bab0ad58fdfc3c1bed3759f34dd272a16d487172013ee7a0eb516 d033ad38553f7da7820e2d808f89e13ece56cabbf599e8f89346998bc2607609 0d65336c2af006c30b9b3726e6785765d35bfca27272c7f6b886ed39c9f138da generate_key_image 1b70e47678324ca44632276550876be0e3507884e8ad9e268788d5e03f7119f1 0fc5090c2d4d32178cd217509da32aa88966e3ec90fc0e1ff5ee6ad42f038107 f06ecd7a0b5ae6a98c1d711dd7bee77f6262f84df6ad013a355b5cf823863ed0 generate_key_image 7427c053014583ecd07e81e1c917906c381af54a7ecaba8498225518a156bae9 d9e07405029a8bd4ba75dbda93756228e18674e3138d2f7b2e7796ee7430ee01 1253d396a6e7a2bff50c1fdf54bb90157727fa20466fac74e9138355b17092e9 -generate_ring_signature f23065c28a04de5d38606f37b7cf29c680c06c2a9bc00b2bb3bac696f9df5c61 7046e48f17ddaaebb928ae39dbe62c23441f01ed40d23fbf5f07e5a352d06197 16 9cf7b3138ddeb90d60b31bbad6458bd58bcad71192d37e69444b28f9450ab37b fe25432b3e417e7123b3b55661f9d8e01b9238c801feec00d01b0a144439126d 1af5776b1bddd8ea5e775e035ae7a3fbfa2e08401f7bb01162ca7d2c9ce21aa6 8138759a1d698a79ba0ee523980c7899f5833ccd15f9c224a02c44accc46ed71 00cc8407088c92ac28aaeebfa3de0f699de189923fb7fc18daf1361a7356c203 6493d68aec79197563b65b4d202665da993a27318538b46678aba6df7475e6ef 2193c3b3e8c038d3ee84d30a3e6b8ccbdf29b25f9b576953907b28a3abe41689 3f4692db3e0e194005497b99f5097938659fb00906b308e4413ab07d20d21ca2 5adf105a86dd1d4579bfb3bbd362119122bbf9517c089273570189f368919f60 8e2b0684c678a5e9500332e375fe28f9ce127e1d847a56aa198f81d02afecc3d 75cd3c47a8d0616d4f7374f3b652e2b4dcbedc826b8b0f22c8f2b12d8449171b b26567ad1bc3e39a4d59795b347cf852c35944498d5ab3537ed76f63111bc196 a78edb131426ca91e0a25be0197578a4d4d2876d18313a940814d8c8e65f493e 943aa48aa6dec62a8f30eb16e796196e3a35569203e58067228c8b496a739d4e f9d1ef180af3b08d9ae7ee9dbb687591bdd3f8fd2b1912de622cce6a9e0d25ba dc6c4e0b458a8f3b47d5841dda4b34bd01c845031421c8fc34d90f2356564fd7 f7738841783117a5206b00b4054df1949312437643b353459e02552d2c69f10c 6 35543c375bef62446decba5b701b9d6ab6fa9ef7496a34fa69dd7b356154e70b48d8f5edac9e6747108e4988d460dfbf8e01b5a5bb1092128602117ae1ea63019ccd049cf57ad4ab59814ff218c68373b596effcd7ac9724c80ef24e9cee6609314223945883af4b688a3332e184a9af97fe5e32d9b1935c20e4f01640833b04fdbcd33f12b644e8214c0e614feb23560912cd9331ccf18ad2192937faeb280c6226e186f3eaabbfd7a91d17e7d601fee1ae1507427378cdf6606b03b49ef1030bc1f997c876d799ded665411db272572baf71147d98c72ce9d74586242ed70cc304bca4eec61392564be36699228037ae4e0f30a3b6dbc3ec04b749a8c7f00a1039e06e5448a63ac0f19b7210ca123b8c6391d9d480fe77ee3a3d12f03a0c0d659f299507c415e168f12f7cff9f3a7340616f59edd1814208fb226dd17af0088f35efd0468bc02d1bd4bca546c5767fb776e4988775cfecfb54e5d2a2322a00d6f600c11298a426ce745fd1c8cf83125ed1d0999e62fe8d6b97b1ace6d19905b92407f16854b13f2950c692a7dc2bec6754bcb4a2461e15921baab1b29451003ff160934f063c8a9fad444a2ef25a767016d080f5ac72856a8b58a63e0fe809c3e08eba1d970a3d975d0a0731411f6fa073c2fd9cfe587dd76187643bd3280b96fd38e180420bfa66b7c84ebd30bc9a0258dbead563ba6eb9563058b5d5340ee1d00b24d4edd26b0884e171cbaeeef242cdda9a9b0d6264f55edc671d0c5e01197bfa84ffe3d19b41a8efa617bb340ed0deaba427a803a980665738de666e08432f9e4702bcb37a7161288bf9a4a15f1c7a876aaac365c6d17589a1b63a7a0f80b9f6c1596d9eff3bd4bb4dbac571711314b9dfcd37f1e7a4968248aec47e0c4ea897fec99aa22d8a24064cc1e4d5c53ca2c4b2c2f4d21af89187edb294100e00568f5c1a9423b58a98e734a084d097dd288b63ade91a39844a9d3ca828d5086153b0ea8dea0f853b12579dae0cebad5e5cb47fb1ee2382351c61c689be6d04a82c71c8c6908cda5bfd613378d2adae734ce11093a4420a32a1347e82ef4e034311fd57012c4378908fe1c71568647703d5c16451bc7422ff395e1bf37c3806f4dc672590fe3dfd459796f2661535cba870df5452843db1dd6e98533596080148f3a3d390249fe7d5e40797722496f97bbb68544b38e9e20aff7e99c6e1aa0a9bbbbff6a2cecf105f1abad5f41323cf60a2ea069609e3f480ae1f2bb9dff201c071a7892bd5e212d17e8af5d050058daa2b2a2d380ff0612bb00bc30332b10e67c5e5e5e97be66492e1b081243c53b094ae85d13d0a6994004a9609745b740829ee72857f6de48c90b06ddb3284050c7ca1fa73349295ed9c255186ec64c40b14c13ce99a70f75257d5a3a608429506a6d2dc93c3fa08ea455cc4b7921bae00 -generate_ring_signature 89226689e486049662075f55d46361d821c5ede1fc172581458207aeb3d7374b 4048d63774cf0e3d73059b76c1160f5b36fae2add758c0b5d0a76eccf459081b 2 68943d3665e40eaa5d8ce9a3279e70e9d00afa0cea15d6671e024efcdad2900c fb89cf7108eb3b68243e732e820d716e11a0baa0def8d2d837ab998a9bd642c0 f75b48b628a7f3fd1dce1055f7c0b81c36454e012dc7ead8d5528c11cd52990d 1 095d3a0d3188d2d30e9e662efc69e56060644722f1b37e1d24c831844b322507546a80f5655838f461937500ce799358fe2e589e12175a6614897e8f52ab26068fdf38533987e67574068cfe9f4336818c30667aa4b9db5cc201a15a67899d0945eb2fc328d4a5f9343b2b3041492c3584fc0c63c4648c948f3ab4dac7d9b40a -generate_ring_signature b5d8229ce74db9826924b3c6e5caef7e7e5bf671ed19fc11be2989dd3c47931b cce45b0c2c85b4210b5b11644afc9395fcb69ba98603adddb5553445e9ec7b6c 144 0be075ebeaf99a172e8e39e394767c3a3f987767dd4980c63801034626594ae6 73fccd68f4dddcedf7206f4928e50477501c1efd04ac95c240bcdb1311abe7d7 82d86f2cee19fa0019fdd5680e9f5bc9a0a19fd20fe801693646eb80029cd721 5028c84c1470faabaa794544bdc8cd2c711465def027442c68cbb51d8b26a173 cd8f47f244cc2c434906ff8a3b05cb0cc6237dfca4df80f54f737ed73a8de6c9 b5820b71fcb114db2922d51ff6ce2243fee94e0b2b325539c7ca31701a291b9b f8935af82ec3e9e25665a07e742a0db3ab87cac8f9aa8765bf50edde6b039370 3ccbd63146f9d7b10b80d92ee369e723579b31d62d93c6792de1a4eb498cfb3f cd2d042ef2fe9c3cabe3092cae589b3ac93bc006c44191ec547645ccf67afdff e2d93bf17b1a2fea61e957bfc3e8a9f354ec7716fca36d9d3147608eb3af97c2 8a48888c11261fb5a99f7d446d679b545f9053b2daf6c82afafe43f8ddc2b6e0 1d5a580055cadbb839eee43d38dddb20d970ab5c1a9e2f05c553d299cf495774 f502b134474771d28c63829de5e2cbb68f561ac4424979ad81d9561c77c9a8e5 1d0c147249493a9cc4c6a08a5e1b787bcafe06363736cfa165b1a01347160495 5ac2b45e81b6098c0039a2af48d87b98e39923f3140768b797ca3d33929ac7ab 3859ceeb597d189dc18be59daa68d39bfc2361b820524631bc05e22b78ff775e 96dcad77dc296cfb3929634e642229bb0f748cc674266991ed53d64956d6677d 3035e9feb91d2bdc3917a8c58b76600141a4333dc94499e36b9a75509ec9278a 78b57f73d0d54237460f950433f02b23a8b73fd686db4872dbbaccfd93e4962d bbc589f174313a7862b5cdbe0bdce6ddac58d6167fdd906bf255e3e98ec79160 cf34f7674a32ea2273517d25472e5bcc686f3f461c734e4e93a302b4c9439d0b f880e06bdefa123e7b6a09ba0e0de33fde7cf7ec3b792abb07ad06107e192f73 988521245df44f9840aab96fd90af3c22455058e24eda4f2e33e1269dff4430b ebf20602e0afa497d66b8e34e5269ed831e970f912310e178c1414d25ca71c35 5efd0c9703fc2e020379534b469c526171091147c59d2552a871823c11821e6c 943dc0fc80c35556a653903f09cee980d4f66d9e7179dc26ace15aed32cfb9c0 274eec520fb3250395785dc104e5b914ff88a4d7f9da7e07da6c0830b2f3a1c2 560f145a25c13e0a1f6d9d79edd399d5afc6b09e3103c09f1d77b11a975e3b2c 3b13f3fe0e4428737929f2e13aa57f675e7e3688d8b7b0475d0a950a730d354f c0c4ddf2f2fbf1c0c8ec3873285ffd498b594cb5714c851c27e1397d386e12bb 81ae0f6596c54518ccad5d592123c5960e17b589686cadf3b530b185d93048f7 4ca1879a6b680cbc2024fb37a2a17853767e884cd8e2ad20c2499fbdb53d57bf 56d56a1d537d1f9bdc2505bec8cd238d765e1bed5c245c1054422520d7204ab3 b3c4e1763c0bdb20cd468a13560fd0ec0576d167a3d3d62523067e8e23a2ece4 90805b8b9ad99246003275c1dab6d1bc2dd6bb12454ac381c662048c5bc54738 dfea8fcc3267c854025ec6c917b696c1905e23282ccc042d81ab3e37782380c7 d86baafc5adb032b78d5a7e2047b619544d0d97236d79002d3a091897fa0c48d d1f6ee6693c03674a865ff5f24316e48f844784161afffa57f0bec4d098f01de 9ac006713f84fa6e3bcef13946203f85dfdddac7a471ccc417a65fb929c52d3b 9cbfc2677c60a2cf155dbdcb8065778c7428f6ce8524f483d67eb45e7e9ce463 ab7bd7860210e59ad3231d0309b591d1032b6af933d59506641e55b659efcda3 7d5e773ac67fb198cd86d94f52d8949d3a44bf213980e2e9355451e65d5d3888 3f34184c4cc8f2d7fff24871d02347f522a690866c695f2c7592cd37ce48f9bc 6674fa097442f932e1e25123d663672e2de17b2385b8c41a6a509c46f89e324a 234a35e2f0da5e2182c2450e90101cf1690235894fe8ff5cf7036e03d4423865 f2c8967a786fe891000838ae56ee84891df8dad799971bd87ad9e3aa0dc1d03a 0484249715649583e496f21fec59eb4c35553761ab82b3bf85ce18e67c184ade f6104748641de70509aaaf7330d390f45ed534ef38bb11ebf52ce8e6b1f9cc42 beb938142360b3b1f0933990a5c0e65180d77c2923b2aa2a3f6250c153c8dd13 518d918bf968bdcd664b6bf24e865348f7093ed723e0293ee86a2402b87e5df4 e86e665a415da43c81bb03d895151814ec4c045147a4bcfe37a014a846f26436 1a11a329f7530ab7b31198f03d8d29754ffe53af384fe5d4db3fe90ffa3360a1 cbf7a677e205f9ad968df137584d4c90438505a629dd6b5f3c4f822564b41c8f 903ea1173296e05bc70d6ecaab59f1a6ae55ba8b428d5ea9a3a5e4217ab5dd49 aba326ca541d77ebc99dda5c1ecc21a07010c2945ce9d5d18a2bc753fcc4156c cc007e712cac3f9d64f16ed07dadddf934b1b9373247e0e8d350e5a2551ee0b7 14200d798b42df0eccfa9f36ad0e18f1aef0021e9d3ae7912e71bf4efa947f29 658b34f8f8f575805b075dd65b5493b737b1d8b6ba122a55a25f7053f23da8da 146f311fe86024f1a81034d6c9bda14c3d4728fdbc030901a1ad577bef3ca5a0 9eb53c235a0298a92643833c1705b6bbbbf99e254a74e93a63b1443934ee7aa2 ca1831e48ac62ded5bba90fb71d61fcc7704151495bd72e05c62f780cf9b519c 4d1d68714278a5333173e703409b4303912cf3b44133102fe9eee8842306fe08 4ec22758002566d15055dc51f1386907f7104dd011c8337f1b36fb29e881bdd6 0c90d65ca0d89c7835d5cc8f185c6abefcc68c0fde058a516ddc19aae133f6af 548f850b0f17d7b21e8c1a0d77785253b05737045b944364c157b942d094e5b2 d0607ab0ba606a0765283abdc2db799ab04c5c63edde5554396e28a87c5bcdd8 51d421c5fb161cb1c883e2e81930d9f848c5eb587aa2092431afe368fef3e0ae 5ece9e7ca0757f3342408b957d8a0848fdee5938251626670255ebb0a75ae101 5579c81801b58b99d1238f2253651b143d8e3ec4cc69f0ef5fd5483d216e1319 7bcaa05b83bb272070bb8987bd618c24e45311b57085b5b6a33f3e1ab804c4bb 89d7c2d2338e0b5fe50d17efd1a567460b484d1dccf9d528efa6757d5233c85e cef2cf9153cb0ba4f39713613ba40ff9cad9cbb7e518fda13e721c329e6e38b0 8a831f7a9a03d40371d569427e1c00b4c6b59a804135c90849fb0abeeb203bbc a71b0481ffb4425af168570f2b0b415ed1fc1c181204e21a27fa5862d8a63c6b 99fedf8d43745d034e111b8cc7d99e54d18b53c40a428d2628365b2b720a67e7 d2575e2b349a45027aea08f5b423ae42950e39fd365891533196026f6cd2d496 e081393f25372bf4f1aee37b8bb04921fb8f6aa0fedc1143ad9fce6de55243e1 ac4168afb6887345c8491a395b777fc2ce312372c6655295faed65742ac69bfe 5472335979b474db10d62e368bd14c7fa3681e2aa3310ab5175d47b9b23d4861 3e70485ea10a5d0763c84c5dd9e09a25adf926965c59c0d7d1345a60d0ce6024 ea6173b067f377d97a79537f532b8d2343567e92cb9dc977e772cdc6129447e6 da151c45da028df365edf5ffb819db00f38ceb10fa687d629f958d653c7f7753 4d94eb70a30b0c63513a4f09907f972fa82a2f941be6bd9ca914641ebe8553bc 3907706663fca37513a9a30facd133017aa772f69731be6d4deb4f40047ccc48 32d52817b46b90f7cd04c2aad2ba6f8d44c8082ba397c60b0d982c5c7aa9a8f7 8abe3186bbb742c573ac355421962942ab2c75facc6c898ee4a19a03b2526ddc 9bcbd8770aeeeeda57a9a2dda3b526e5abd65acbfe8b68ab4fb346da066c476d 010c68c46034de748d583b050e2715d98ba852735272e8d11d6e527bf1a40cf9 3407b8a4d8c5b985a20d33d3896cde2cb823320fa4f7a8f2385dc320d83d5ddb 53a7e5fb8541fa6abcbe75a39cd9b0352119fcc84e724a515f3442d4a558c55d 73becebdf4b4ddea87969a7d672b4778586a5711226e7ea72b6d615bd6eaca29 96cc838b65be8f2cc82682aae4948d766751bdcdb162b399f73b2d1b28a47492 2a0780dc42a9aa0ff3cda976a46510ea79ade528d57c66131da66f29046669ba c0e71216cc5e4dbd872b783181fc19b8c838881b8c7feeb7b679bfa700c5f00f f7b1094d3f05868b1d8f82468e3e63d6ee98a2556190e8ebdeeddbe739662e70 7cd1ae917e9fe9b2250d118b320a611c42907668a2a916430086e79fdc31624a 6c02f8d8d410727f698344007fde07f7bb260eff705a031fcca93387c0538136 d0f96ccb71432fa05978ec91f83d522bc6237dbc29a28ef02c9019f7674df4fd 0754988d535e9b8e840d17088c97eb0484b9086fde51eb499f9079c8694b9092 b45a7849a1b66581d71acd062609a406e78566c94863ae128f4dd89db838b09b f2c392a9b334780a36990057be799cd2d96d9544b3388563fac448e2a8e61ba5 ba1ac60d28e8fe4266f7e99ba5dcfdf2c3f03688836b6e39295b4f08356e5e39 3cb51ec951027e73f76394226b502c12549883591c82f40998d70a24433c584b 6349964aa8486223d4b70c8b8fe69145525bd91ef6628e1c6cdd2ce0ba04c266 290f25e23204297c88d4e709a0ca24e3cbc54b657393ed8569e1d9f5110cc2ef 0ff7d6198640d9e511867fc6f6eb733728d4087cbf803cd6fda69743415dfa0a b11f6e8f2fb83fd784b1281372023354d58d84ff26c24f6fd7701537406c00be 0b5a3a445240b4151eaf074d6be3c50bc54fbb69db6f1eae09b6c146e81f87d2 a70b418a5ea1f707725dd25fdb646fc10411acf4ee3495c160e56057d469be7f ba5d677d820274a39f795af23e660a8fa619f6213d0c99051f93006a472105a1 da947bc68bb177db418974a50388357ba6c0c7f7d1c269c6c7e7ac12013aa388 b8fface27affd9eabd46e85658c00b2d1a187938e70aaa0401ce2645e1faadc9 01950f516b23727d50e930d6a7f982701200cc9b4f91e5fdc24fae0621df0648 d1525f7d9ef2244122bc256f431b58a53f348af292547b970214c78677d96748 928e5176379bd48f34ef5acbf25898ddde6fc2de80783d71d01dda12f38a85e5 c4e9fb2607e4e77d6be6651e8f3252ee3b0cb4612adbf38432d8cc40a25b6423 e8c78da2f6c26bec3eb9d0c8e54d7169a780b4eb3a67f60a7be45b3b347950c5 5f198b0d848e36526f7159ede1b985ef4ac6981ec31d337efe6fe896236b48b5 d49ad6a6b2bb67adb3503179d1c79163385eee4ee6169fc060058b8ddb9fd401 80bef6ec30689b27bfee132087ec9308d11746263f7dd4313c400cf98a45724a ccc998e5dc245cedcc864285e40a6453fd2e443a687e0695e1f36887168dc5a2 c6b95bf4bf9d0514ef8f69abba93cf993c4731598e9bef7ac7d1df9b5cd868b0 571028b516c0d6f51d1b7809fd5066494fde172fb962f2a95d9091cc231bb462 f039561da33396d6cf7f3d5234c3b624981a193128904f296a2d49539c5a5354 c254db3225ffbe528992738a1c2a2d659a2b69b7823d5dca1f074768b2d0fa1d 26e8fb8d19c19f48c8a4a6fc8c7ae07d81a20e338b26ebf71eb6fe9cbedf82e6 20c5dc8182e869ee3b7824eb8f3aa8bbe41cbe85fef2416a116a781cc761c459 627f6fa349be0f4fcc820ebab0c373d346081727c41b3e41f6908401c55ca8d9 dd81b44f36eacc86416c791de3b6bab04d52f8a001cb805f71a09572a8dee99a 621a8ccbf534ee9b37d39f83a6f02768bc0bbdd56a9ffad301c905f43f95df1f c0662a10f78456cc0b271e278563c6b269dbf1cf6ebe5cdda87c07b735aa2e64 49f3a3193e87521d12becc9ef14c623e844f1f1a6f55fd6c2704390268f45d90 6623968079cf82cc14b56fc0eb73faf93b4cb5a4f6e1c56f12e2978aca03f6f4 c9f349184647ab482890e8f118a92abb469f62c2756b599706898a7ba2709a8e ab17adae0cb94f56924e7aa5e4dbb18b640fecbeb23f3728694a071af7efdeb1 d3b700aecd64ee52833e70c957c2d6d3f3325321786e4ec6fcd95a4645e016a8 a6e161b23862fcf9f036fa3627f2887089f79bb4e9df6587a23cf40079e07a02 44354eb4d4a96bd9dee8a8e74f2596f5cf117eb2137057758b012ef761ebb94b c66e82e556e827481441c89dbb03bc57e7fbeed3b684ded48c276056bec9fac7 1376ca9c2e462f8d22aac18d284d5ea20b8614a86b30fbe073baeb41074cc51e e098ba48f7f5f85336e47ff10a3edca5eeda1bf9717de3f4262a80e77ab93674 2779e3d23b90fb744d69d5812b0c06bd17ef83d509fd6d5a86cdfd534dceaab8 d66a3585928fcb67eb352804e9905ac7bca6d6011fe5f7e3c15df19a6f9d55c0 9892556bf022ceda7b1f04aaf0328ec0220e468010a235b760069df477789167 d089b93676963402c9ca2305200908ef13f6e5e5539cbd865b44cf6b32499b02 106 12ca358808284501952f9d157c7ae6252b38ab014d01f7781042dcdfda02e708b909ccc8c6ae1162c01cd0616b02665ec2b146c8776e4503297b488531b2bb041e946ffae962ad1ba018cad7faf9d9e40fafa26088ace24e47dc61fecccc5901d0a6aa2c60b46f5d85c7c184df48f93a13976e4937c4b4aba35ace357207290b533951d9f6c6ac7c9639fe0a9a6e3a636d5a2018417b1611ef71e4b90b6a6e04f0228e8ba1d9feab59d8c2c4ccd50cec13c9220cf741f33efd941a17b2af5f02b4c010e33221163023d0b53f3d9caea1e8f42b1c0fa6439ff27e281b03ffe90429d4a22401ac332ab6a4663d66cbc568ab77ef541767796e28213fec2fad380976fd9e62881a69c8dda41c0684cc1bf23471bfc67f5c3dfb3b1e18e9181f310c7fa0c8365f54eb77eb5e642f4836173c987ecddaf709f99a59808f2f091a950e8bcdd07c454e97b82a0e4d14a4414436fcee3574e38234778e7eeec57770b401746958056d6d066a78b66ddd3ab65f31a02f5084bb71ccee24b789e5e13c7305c75ec1ec0f5397383f79bbffc16b3149f7e9a9c5c67478edd0a099bf5a17bf0d018167e32fa63da653646fdf400314364c030a132255eee57c0879683871770cae9d5feb91e8e948d1e973bff68f4cdd78e17134b778ce5ebdd658664533850369bd04189dcd5d94f903604ff517699922246ce4ae265604fbb845347c9d330724d673d721dc97b486fa47625a896efefdf5fe3c41e2b792d23cc8448aac8704f88353b139d1f4e749171a854755a600fa206a1dcfad73af9dce6d9398f2320b48e7a63c58e960fe5b63abd9c8602b2cd36ead9964320e96f746985cfeecc70d4fe1bea9512f98590e946ef76396b2f39149f8506ef93f1be530b92aef5bfc05abc726f85a930f0dbc87ce4123a9ff3b7cfecab3ebdbec6362e0d09efcc8200ca33710350165506a0354d7bf52eb935ba2092289b4f0bbffaf1754fdbbda3a004120813d4fc1e280ef9348c521aa2c986702c1e27a6454acf0feca067147a60ac68bbc269541a50be4c3c5d4c58ee5045ea260982cf8860f4f12d317a42cac0ada69f1b81fc961f6e053caf7c9ea205548d6a7a2a19b531ce9c02573af35ac07fbdc7030937897b8280d70b23f50be0e974179da72a4cc901a88829052d00c0edff5f34b1132999a30d339c079b682efe2270a6a8bc80952ea40117cf0418705b495a0aaa1de3b3a00d04c24232e71449406aba31debd36e5704bbcbce16300826f9623ad2442a239e608ba8eff2a76fbc8b20ff0c60055d1c6fc63f8b72a90c54ff3ee9addffe43e2764bc2d1dca5a7a717ad3e7f395324f88ae7213131ba0dca4aafe2f3c2a5c295294fe689913ccd24947e839dddceb7b7ee2ace87d94d0bd45994ccb16cde1e6de3042d7f3a81244b5e16b03c39fd48e8227c57a294db0f1f1200823cd481fa10cbf07a80b9eb4d5033865e678fe641b90cdf25860a4b07650be4a63a78fbdc1928f147c77e3e1d254dd82bd21e773808525b51b2aa4a0ac2d9d5f330514b1d5bbc059b2598237ab3e419789690b9fd98a2a3311fa67401986d1559144238410aa30acfedfea75b45e65fb3a33add72a5f838bb4f0b04063db28fbd61d4b6c63b69c24c2dd7e6f5f6ce16b914166094e1bd6043c5885208cc5d6ff63f22416e3b7741de9deaf690fea9e4e8642861a2a6a601699bddb800070a41dc4b951351279bed44eede61bdae90f85879376dc7303a849ee0360101ccbe7d4b6dd26c95c5805c3a5ff9eadaa415f751b77cac21c34102e03899920a569fdee21083a63dd9616286747fb47f7faaa08346a77d2f5bec34062f8d9905975bfd3bf35e757dc3752d788c3c112f5765db63967fcfc9f2f4ea5ca2009d00a8a2339796ecee04e4028e9b4e23ad338a6629d4a64816d80f7e55e6e280040f0d2f5e6ed0a59be64c16c193902a1336c05a5999f490e9f3476c9f897058e50039ae25ab0087921748d1f5a38a55c86c3b2f792d057e2959ad2d48fe7fef5103b92a7cd703b264836b89295fca913526bc2922e1c499ab4a85deb5e1a94643067ce40d6b4cdf9d45ea104e2305879bb0c0d9f15c2e4e93c12c569e5299d0bf0559a45b658444dc1e7f2c606cfbb2c544cf63f3cec1586f9449a5bbbe8fff6e0fa9e595b3fb97337b69c58a096e4196d0e9dcdceb68d1c32bd91b0eb5b8744d0a2b142f169253f3025f6627bf22ac3e01e7c6ead6f585af692999a4ff8e9b6d0473c8786a72f0f9a11513d163b1944040ab80bbc56852dd8dd47b880e57eedb06bd0094cfff52ea997c6142034406d89216cbb18a6663d3912e67803db1cf730ab8914aa760106f7b7cd3bcf1b33d1a1cb4ec7c3ee6dea62fbb0c5f44f610f30b3647e3db2883b0534e992dac3909d23b51aa02ac96663414a3b09f53b0cee5043307e95d656cb9ea8a0e000112bb160e39313e9fcefb7d764f4168a19e97030167bc345f3985ecdc0ae589687f34018e622c9385e775bb81efca3e96bc5bf0032c331b3a4ffc143edf7a4f2bd55e6a3ce4751635b8bd30f7cc212aced57db80df13f6f5d273cebd940fe7d4ef8e1d5b4d79b2520408a6d61e8113fc1d9b3c40084ede09f602e7de1397382c6263deeb994b3f956207a53031b506559ee08c200f49977be6e025edd360167303c736d118b1e1796146c169d56ce0792a8290c00943e4cba899c458b051a2d0437b2a7da18a300b5343ce85a19675584b982650b4b291526b70ce2a68304dd5bd9cedcfd4f41357f267ec839bc24d3c6f5906f03d197a921815e124cca619cfee719c432823cb9bc0a95abe35b1c8b4a563d3b06126f8b9673c78ffaab50b9bd4bdaa3926c0561d32b33cb6ea34908a0ad53a705e57f73c18b3ad711972eb968516d32491d98e554c5fc5d531295a549e50a850d09d7a1783ddbb9d4ac2c463a9a85d444cac5b283fd860b6bbb6ee4e74121dc06538721d7a375142cd0b619aa84d1b4f66bb97769847c379df060b4df904da108c045e484cd96e3b3bc7c92d313d7e8133174005806a4b1eb12eb61cc9b26e5026ded5e5a65d20c2a6361de60e93a991814ddfa2ac987d6dbbd238658a620840ff3cde5277c9f0d3c4e06ad1ae6da8a0d45033a6163a3655754d9c5124e80670f30cb163d3e175f199e1db6d54de4ca15910002f2d4cd807592e886acd088310d55b7960c3677d0bf7364db5d9faae209122188961c0e19d47ea116eeb3389d04fd5e53f8fa3ad2b72f302152df460c07950094367a6bb1c76e6c6bc57a463405816f4a3ea003d322e04206b1d79fc07cfee9c26899b71c41c572cc9846a2490f3cb90d79d2d9b13addbb1744aadfe6473f585ae9cd37d56f50eac555b5edf9007826b85fe5cd787ea3744ba57ba705cce3c8f1c172fdd8d10aef72f88cc6900bd7cc665ceaca2f742e129d6d457c77ea8630a56670950cc5df597e3035e2bf02925e7b8555dac8940caa133da12cdd0e18d348f1c54a8b20816c5e17631bc107327a04e107d2181f7a9b86e36e8c1dcd902029a662532303174ea41871a0e4036fe809e71e57ce6b82b3f0a5d6af93e5fdd82452a9c1ff96a58efbf5039a5604c27493b8927a4f318a4dd2c10d5d317a491d63d8d522886b750affb5335b0f0af956aa02414dc94a755ee5dbb21326ea6524e0fe5397f85041e405846ac13b02719a18005ad8cf9a92de839f2951b739206ffb75ab4d4f98516422ce3970dc037241c1a85f18a2c774f0905356743b6f994cf09ccc5db2b89b79689858c5ac09394a265f1bf60f0e4a426e019baf72812e6c3ab377d17b8483630979a2d9ab07139a4f4ac5b764e174a7f4f1388d42bbb7f4f658dbfe2178503384630f018e02130c924bca3bd982ada71e0a7d24a7024c7ac3b61ddfae959ec41b5a44626a003c0e19524e8058007d1315f3278804dae559360574eb4e162784f99a7062d9098a23f04bbbe96d806eff78a217573a2bb6da2cfda5c0f7f5ab9337f22322eb0c58e7cdc2a0f23f82103f9f64030b1f25626abd1664f958ba0f2c6359253977005373a8a035698f6c9f2e592654470687b77e4b9535365250020a82348d3cf7052066b3881500f5c46ca4db97dbc0ab3783cef970a1f4d4f1b3ba87e6e90c1c0961d6638104a1821fa03b30022e66031ec15d668e3ea0e3ff72484dd337f2e30bf606a62b4e320187c95edb7cc225b3c428b8098765fb5b028856eb4f7cff670d04d102ef4a28d1c3cb141db212e1ade287f8975b036eabc2538bad8baddeca04ef5705055d00e5caad11d51478a6e53a34f5b8956a9000bdd51358d5690f5f01dd15e0b0884fa6fc2f7ff5052d5af34dcc4eb596f2a62a068245dd95301f6c026886eacc4dd7c770c94d626ec59b4cc49846ba34aae25a25fd3f4705bcbe47093bb01765dbecbb0096fdad3e35fa76cab6a2965185515ba474ae7eab967f2f01bb9bd7a4335f2841ddba012b5dd9741ee884425f66a477fe46d71e1e6c002a0d7099c010c316806f99ffa8e73388f0218d6b9da1c37c370cfd699a5ac460fb06e81ff7e4a1f835b0894e8cf3646c8cb2b5963b3c9059142277d70b1f978784056666709772b91aebcafc0f2b468b90b7a71a2a56cf1043d308ced8249990490392bc459edad192c5220388a41852bdd62c8831b8a93907eefdc3eb376aa9d00309d5c81c1241fbfae37b68002a0d5c436cb8048c677d691517edc681dd956a0046476866472751d616964ebb2296389d16cd346d67d20a11da12bb80dcfc3d09b6de8a494dc39788dd9780fafde080a7148998517381aae05454c249555a910bd91b65f157041086ac9d0506bd4da32de55a64d06327bf8d225a41994c279c04fd051ce9bef0810d0ddc1b4e9dd1618ea9657adb8259f7878437cf583e09ad02b86fceeeab290cc6b812d591f7d4f9e9143d55610fd20932c17bb39dc950cb09ffb1e40b7b681605de2a915f84dccff50541d34f34f568ec629df5c4b185df04b95ec5f33f182eb4e25af99f91af440df26c6a397e990b93f7c441ac11ee8b0ec5a5832e7b4aa073d0de29f59afbdff435aa08384ccaa65c8f70b36faa0172020e4c23df23f5d489bde5f319ed31f55a85974fc5330035939bf3094a9b3c4f016ce1dc1b99076516849282a50790aecc381f06128fb588e78da1ba33ea9ca60dc246eb01857d17db0a6d669b08c3b3e2967851e43bb886cd33cf8828f5d6a8067147f9a9ac544c315cad56622bc9ead8b69afadc81fe20b632b4296d45d4580aae69e644e6c23069ffcc93d8cf45c7205b775ded3032bfbc2209e84740bc9d077630f79c3ade66ec0c9c4d7c3bb848fc25225bdea462b00e1b8bbefba0b50302c96c69ac2c9c9e57974a733dd2125e6ccc395ca7c7a87bdc7861a12206098900aac2bb5e974bea2f90cfc592f964b122f5525bec0453ae8af8cb7896ddd62f00ca5fa63d050e757a738c34012e18f327bbe622dd30f6e4b54108b9b8f9f47c0109baf7f34fdfd95a61d4b2c7f3c59fc5ecd52961791f297f73d7623d525cf90b5bf0cd7a784f1e7d8eca92e509d4feae8f556b1626cbe8c047d5d290ed01a90469c140a3445aaf633fb2bed3c2b2a30312b98ec18fa00881857373c6a7b5510db5b319c4afe634b7c56395c5447a37b32091ff79f244290569847d1b5d1d920efe05b3e8ada5e3cb3add037dbcb32f2e3bb144472721adc042cc8906ecbbbc0c0e65ad1678b619a6148e6f264379d1dd27545fb43ff133694b0104722bda9d0954c0d5cb4d4ad62b1acedc844574016f914cb7261ad693a398c68664760a3d07acc185fe4bf033f50338f410fdf64199231521c2cc907c11ee0b72591f30970c7f6a05e89b205c71091cb66718438b5d7aca900e0a43d183e81bd2c8916f85060fdf685718e72f06950f08009fa7fde9a4edb0905288b49c366eb2a74b74f9059b8790114ff22c88688fd88098790cbc5d9f7855c9f9c4cefcb3b2800e83210d8e10864f1e5b8adc1427b7769d0fb80050e5faf4e75aa2871f97f59eb7e3ba0bd5eea6e4ae1ec85148e1c6535c1c97902251a05ba805f9716990785e400eab0ebe6a542869fae6089701e147c3a0f1057fe3a8740e04371b47f658daf896190c1743591cce483e4a95e9687647e5d8f24e58ae184717b3db9dee01449bb2e70fe5ed585a5b1e82dc52a25ef942ed8610255f29a5ec564a74b524952548e19606656f08578dae8a9b702ffa20959ed2d8cc72a904bde19ad490720ecc8a5a2f0079245cea98e69cd0b5fe83de7223f87075dc27e0240efc7a8c4054c152e3f30e4e83430bad0b9df601f1173dec4f4a41fd86c9da322b278d780cffa9e255c8053c43d5f6c29271dfeab06d7c104d727e80ab3487d5cbd7489d347a547954cb06439e54d365a7f0a8866ed10b2c24872a5dc31a92f15c5ba43b643080a4d33c0684c9c6632c70768b85ff9e8e7afebb6971c04939d50a272d6824a176b98321008292850bde080906ccf687b3098fd7f1a674c3b6527d409718f0c2715285e80eabad6f381653c4203106b6fef8c313ef0467637bfa6941270e7daeea13fc66091a8e36185b216b720de9a791940ce5cb12bbcd0ff46f870153425ab6454ab90baabf1450feea5338a34a6bd522c6930b9c6b3449ef6dc8c03722df572aa67c0f9c8efb9c8b7198ea5330e2affccdb140a9f826c581b0b027d07883578634670a7e061d4bbdd58d5857ab0c9d986cf306c4fcdcb272d265e075bbcde4c181a408b42476e2cf978b3600353f31ff6e662b3944fd847df1a8a3473fcee22f7f0c040e7920dd689cd49db14cb0728c95fb167ce35a559dab70cc09a5f452c6ef1f0fbacc213d5427aa83d2c63c9bc684131b4907c996c4268a13a7bb0c8d5f568c0434c872682b1ba76a37e771449b836ef0a3dcc735a4f7b1f6474ac0bb1f83880f907ce30c4d53814b3ed8301e4f80e0dfe0f93b02b414d3ad092e0448e984700e97edc1d530054568013e5b75a35e53e31b4e1859a45bd06a8bfe2b0529e3ae0c247755958b4017459d25c7db42eee699e3f5b3662f745643f951bb4212687006ab9547d3d295f869b368fc2d79da4823985e3c102a08f3c90fc2279cf7bccc07a10fbb2ac32008011503d73f8a1350928dc17d99f9ea2a60e10c1849b1d9db011b46ad84459f09ac9b181591d7c812979c60559c71f7fa297457e23fbb85e7038380cc22aa0bfbc334d25edfdbc557aa132f3d6eb5c81b86cb4166d893369006b3631002d920ef7d77440dbfc90c73b2e924e546d7352c00047309f68a2c960fc834a9aa81e81cba608d4f67351a6ecd4bfea5975c0718b63321fed600d96204559d4283a2dccbb1c69c9820767bdad4fa1f0ceb7c2344789ba31bcf3803840d77ccba76ab24e507d792be01684713569d0bd89d18d889775ac28fb5afc52c096e39421d23eb728fd897ab9361592d32623ecf5a9e50e30bcf38740c0c9c8c0a0159d691266f9d373392883203fee2ad141568774463270aec890720233d050568398d1495f04ffc7f8ab8c5062e54baededa2d6bef603948bb45bd42fc9e906e21f374dd4c8e3f9f6b6ef6a25f2fddfc28cc0a26650a427cc1d2484b48d4b057e673d61e211a92a1476f8a49409d1888401bf3ed7bad2bfac236afcc2f7dc07e2d6a1ee882e4e1ff2ecd346b62cd7fa96cad3b8219661deb2cce2c96d60f201a7ebae5d5013b888020978f8139308c90e79ecdfcb659104fa9266a6ec9b2c02e4ff5ba9618a02c636e01c29661d6cc1de76c9d635c450c2ed9b9f41d391910d0717729c780f064ee7a1ae8b26cc9c3e0d0bd7e0d25419d5826aca18a4731e0238cd2afffa7baa3c10701073f6d9d474e8d3671b7a8b4f36f75ee984412c1e02cdfa21bbb5061a692c7d7f4b571aad1247901cc7567668228fc26de45a3981022474182a3eb16e922a00f54c30a44344a1a9b96cc839dc849f1b0723a3a04c05fd65770c644910c94651b72bd8332266aa3506f62461b9a15b39bdbfd368110bb124f08dc469084f7e9e132ac61c33f2ac7174e51d3a37f7b685e2a57536240302b5e58ffdeceec86aa75608f9b7705e55040a165a021d83ec058e6005a6fa0a780350f3dd779389b2a7e8926a8c6e2b7394addcae0266bc76706954489c0c003dc292098e5c4a49c12f000b823da13f5e15cbbe2c1553bb53d84ec4996cb00f8db5042d1c2c592be597839dce5b1989f93f4a7c30bd0104ab99150519b41e018cada1b7bd77724db7551d20ffcb9bd4be1618dc95960ad2b2b5ddfe405b520736104c1245e15b3792afd4f70bf24f20c5084978edea150818d98b96afe03a0f0019c4baf17a250276d745507085ba3ccb0ba4a72f5f9e413d1c82b8b980880959be53f7bb88d06987a6e48f14cf88444c4f082a1639a4ea77cb3cec5f593405dcd8840bbc906f2afef9c452b733311c31fc86f71c39f222ca7f8a2733fe3d05d47905e7f9089f9e279c89a041a3d62ddc751fa179800932bb53f26632232706f1d3457801391443dc5ad2df07fe3c9d191b02ca459e0c30a73fa3bb1899280c0078c67117226c2ee9992a13daee82cddc136fc5fd6c5b1ab2e76eac7003c304521da52df2c5b1b613ceda50549e589d08870d26cddc46fd91df60aa731494095416779cb7927bc3e82f02f01f14cfa2b5f838cb46be26b497d799bd5b945d032085b5a36d26e797266f12a94ac496a526747589a5a0bf2aee4693892715940abd7ea0167596a6bd5b17eabf3e0d7bb0947700c0e1f93aaefd7b4ba09ae38c08e7253f7e5fef906a1aa77b8dfbc359110028decca1db222dbdbb98bffa4faf0e4698287fb73a159242b186d7333af5ba773014e4590a0b2df739f34925778a0e2eb3ea471969630637a872abb5c7cb6f6e43a4f230ccd55f29377a1ee945fd01fc97aff901da84bf65a2ffc68dccc8c6ae80d7e4eb97650fc9d793276cea1b07dd4d6b8361aff22758e78fa21ad5ba314fc71a514fdba96117c7975cfe0d8a0cec4f89f12f5224d480c79bd55a7210a9c7801c026ccb885ab769051a3fa94a0633ace23b654a26e77c8313ddaa82dc2155f3e5417a1485c30d78a3bfc25ad10a93a0277d24b07aee0407d4283d1c8666fe83ba22262ca39655be9d3378f0af044ba52a9a72b01b20efc515ce65314d6728e50775ae83c16e7a05e58bbbfceb0d2990630f425232da59c19c6b020ef3d51e855d756bae6c4d11205de62efd730268be7cb5c64fc50cc0f8717219f2eec52a7cd06d4b3212fea635ba138548d20099d9d806ab7ea5309e5d76d35e4a27ffce2355488abcf8efae2b94a8715b6108c9a4c24178b694379be9ab9aa317fbefc2e3f67a99a3cf33def91cec49ffb501605fdf79694e12ca5a51b3398935e5bc195ffa77c09ab164126cf19d193d97062d610aff995c3dcc6410dd7258298c71434529215fcce74cc8d70d9018e6e00d707fdfcedca40050908a5c977b1b49c46d45a79fcae9d6134400d9b294e8320cb97b2b52496cf80a1f06ea9c7178d289d54d957ef8198152a155682da469b60746b139e6b52ce56ae931e8bb2a964a2584977936383bcd8783ad53eb1f51c008705be4be7ffa7ec6f055e53e590d0f2f60ab705d7f5bcb24f8d2168282b8e3031a338324b2a38d55ad44eb4026ca5adcc45f6e01aad25858f17f16e7f3e1880e4be40597bd72caa0a236e51d6a1216be205cf05b66a3dbd02d0d2fa507a3ef0537429ece34bad65101bfeaddf6288eadb3303e4c4ee313b7267daf20c0e9a301207e9f572e9e37ef376f0b1019d493e5f3d7bbd50467b8cfca2406902d75ec0579b6425f4361ee8c8ce4dc04f045995bb74527501f885563729aa9c84d18a10a3d1170e2111a6fda3b4f52cd1ed9e4dec02f2c81a0a1f1c6f4601916daa4400b57a507ed3a677ec6dbf95dbf7a5ed94c0f933124e073ba5b959f3652d27e6b073cfb78982410780c2300f564c0b4e689530a6464508f1d52c361225f3403b909674b46560d717ecf83896f2d7f9288cfdd34cea3df36bf12747f59c8d176930f1fbf4a13694628c4d238308c5577b0d42c1a7ca9aca6c623e460c6829de02c097ac48ecfdab55f4f7a43b0755749b792c36f2083c35b51e2af567038cb5ce90ae29c99d8b0bcc8d1d72c31f162832c8b8289ddf4ff393e790f7c49504be78e06cd638dded60e3fd27815e03ad7e1cb9a4cc2c46df00fe0719ab0a4f4c9b418065176e8126924f3dd323edf30c99a39e60e89d350604cc47554233e02e562590696dd7fe1cb307ce6d94d5e63f7872f751a8bb956b111c5d02efc949165f4b700fe766276c37bbb7d2075bbc2559e71cae5e42729f1419cc7f34292b8bcfb750835395b7271f667603008f358a423b3effd543525b7cd45a9e2d1342619f0580520051fd65bd303470e17e6b57a1de37ba1b4b53e403ef242252d60a7b626d2094b0a6751bdc36640511ca8aada38d1c80e97b68b9daa85cc04cc66aa48282004ddb1e09a179c6a011e39d42bb593a604edead19b7283cf93625676e27883590e0997428c85e25f69bd82ad882455b4af8f461b1074828bff4e4b4b2e4d25e706a113ed83382e08f716771910f88f3cf3e9ae50c7591527008482cd91bd0a4808d30e2258961fa2c08caca1288a766f88352bf1ba5bc383a28065ec63feddc1007e259ec20eadcc56a9256aac2a24f4294fb64ef8ea0d070ffeee714f67a3f20cb41582b5d72012cff3443f3aaed1a9bd2b83ae4005f7d1c7d6b48fa747546906d5186c7d26a5fa4a8f98b2bc74a541bfa0ea13073fc3549fc559046efe257e0ca2d4d75805e9a179ae82ace920c95f817954db949020974c914b5414423dfc0f440f240595f3d3d8e2b596a02beabb0a98a41e33b1c1de7f93f1275cb8395f03c0b02c94097d4b64d805029338fd15018e6104aff703450e82ca501023e29209b10eccce95abb990080a662dbcef3eda5ba29e37953583fe398c345fc5399d02be48a37bf3bcee1e54f13951099deeb0a95176225487f5e0d6fec0b28045a105fb739ce47c1a7029e70f78d1e9558d29f91a974890dcecbfcc38bfd9a2446c0e87b0f2767b6ed56c8e57cdc6bcb2cac59ae9e1996b11b50f96cc0f083bad6e0085270b9f0d882926588be9b46b2e2fe2528fac404301488c1bbee3fe2fe85b015406c34ac576e2efabe850c4f364dd4f3219c353196251ac4fe4fa998148b00a36d304107c7c64b4b4e9b0fe6c140638be41527302c0acb9fb51ccd5746dd20c1b7538618d428e2cc35d6e74cfd8a8198d8f193e91c98a9d56c3f2f89cf19002c8f96a047328e1f6f68e6e510e50b09f9a5a847fbda2f922aba35edf111f2a0983e741ee2e0cc2476bb912c2df27fee86c8c0a2640894b578a8b67fb3064a0090233a8bd6310a39a6075cd752844cb5ee6f924b410a84b41542f17d88af5010a3b912e5e37bd4ec0433c6429e944b0e1232b4ecde6bcf1b96a6d5104d770c2044fa4d1c3885bc26d780fc66a322353888158d18f6e089af0b02165d6f72d0006ea36ffa5f86627182ca1faddaf9e86d85521e3baae571bc0524987513f0b8406d353ede948f9920c4172a12143902caa700e7ea9e2d91c8dfb681da16ac0240ace547c18836051f38a34f8c406ed36064ec88bde6712d7e6cee851fe4ca83b0fd6536d7dc540a442fb92a96aa0c98f3db85ed731608cf86d7329b8766bea31053204e0635f65d7d55b65c76a1100ccfed34ede7a456872636d6ef547040b5e040aeea6beed27a6acf5e4a0f91c56ac29b819648959d56a06e822046802687e0aa5c4170953888b62db3b528ce3e2b4ce9d86b3a6bf4c190d589cd93865eafc001170fdacc5fb7dec5df5254f7c5278beb2bea0875b7043c4a5d04bfd7bc4400ce0fa5a3f133e33b4f66728191fdcf2e9994773a6c8846a1f8f21fda4993b2c0378686f2cc5ed21e59afe37f98ce0743b92143c0e83457da706eb0efa82fef60e6d0e4e72da366d9eb614207e1a509789f414d1b0a05a7c8e91aaf1fd4550b40cc0d59139e181bdae41f9e510a650014bdb24df5d9b61b111e408b28c9ec5b808c242393af934775d6b7cb48f29783e07fecf21b3f7ece63b712869d5c6dd9b0ed47ae62af3517698f33abfea850b1f34ede4ea89bad89919e73ba5488e120903db1ab87a677a101a03fd2cf2031bd5f27c943966f4c38bdb2f8ac3b85282260c1fa7be5014d236465411f906db6826391ecd77703442c294af2c6c19ea347a0443944798199b945a432025b3d8b1b6e07843e0fb424d8677ab849c2287dc3d0259d60a342d8607a23ce03776aae6d5a19db6ff8868071b9b76d90f29dc077304b1a82125d513992687f26d0f80aaab57c924035843514153bc3476b19ac7700093c48adbbcf83357c4d8bff882b466e9b49ec773621bc22e7d6bb9155ac0ca003e4afd8419fec08143d5924585375929c10c9f6614613c18cb86e9c32257f007c6d3d556bb6b80a269af77e665c01a76e260934b35f179d98a2c03b973222b0a7a48eb439c17d011e66708e35f8302d8612af85d4cb941ca4feff430fddef001155c30767941cedda2027618f381cf77a89801f8d8ea38454b9f98a5c330550f3a79bfb010d0e451f7ae598c143c69d8bb400192d0c5dbdadbb183a08ec727019f521d7aa990524faea91fc83950380d37becb055809c177ab0b28cee792f20e6744493028bbf4acc8f0934aef8a31e9c647a5948742e694d088faef8f5f10025ccaf3eb9e6f00f652d92f376786066b34a56dd2a96b7a990cc03bd5e3d2670bc182ab95bc9428782bdf3d24f8efacf8014a5da84cd0d43d6c55b799223d970be2610fe5c0a06b48fed19eb8791315f88221416c193fcda5857dcdbac38526091da71286ff9f828d7dabdf409014b904a9ffa677e0f5d5c0ce6ef97f88860a0bd0fec565ad9bdfc070eb1aadfd81995549dabcf1ad2261501808b6aa6a2b820f -generate_ring_signature e8020add6aebab8ca5f8b49f2072f631745887fa205d8db1a3aa1b5a4e232d81 dd963b8d2145bd369feed2501aeac1de2fa16ecb1b7cb93564535a6dbccbb3c0 2 9f1c652d42539ae6c7a43a5118d5890a4e1b386b3a400ebd8f2dd5687626dd55 90a86ceb7951f7b5668205c22e8d2c9953d0e44773f7d0159618a8a0238a53b6 5e6dd5608134fa4c64007aecaa340a0f379d1a5c6d4adbf017a239eba303c808 0 a6805f5877b33f74a206d4092d7eea8e2efd63c5d12a63fc9b32e4689d846c02b7c408786c434e9f21003b678e952d6b863c2557bb894fa8aa157ebbe1f2c107c239cc506f5fdb39e31f953d45b3856536ed5eb90904e91b43ea15aecede950853d506cd5165102b11f920a9f25058836404ae1747fc8d1fb20c6837b7fb8107 -generate_ring_signature 3e592f32788ec54e3f10560b1380761329df534096ec12251382a96f8cab7eba 0b72892038d4248c430d1ac93f4985a8bb9b43fdf457a83618b7045980b8e6a2 1 526c98024c9a4fa8c6ccad5101f4afc972fdeb228620d774669c13ec4d029cb7 0741f944319e0c4678f4a414a12c6956cf9d4b3ad04a40cd6030e47ab7e9700e 0 62661198eca5aa9e2747f9ce4242ae394bf740a7cf8b837e27c7553fc8c27b027c8f99a5290b41f7f8c5da350cbe14a399abb7785726a2c3db0f291033a54f07 -generate_ring_signature 701268c079222197470218f73587bdeb19c3620f25d1511042edabd0efcae448 c2b7340137942802b68be0a9cd0deb826a43f7645807c8f4179c64b11021c36a 1 515cc1cbddabcae11062f52ba676265114a44372719b9b5dcff7865cfbbf1568 20966d94502e95dffb45bf07173991a5c0ebca48c0941c8c6705f7370bb2340b 0 6379173fb83310313f391b13b27eb636613c7676ccf5fe73683251ba45acef04f64b90c1f8bc81199d8afcb05892025a39cba255946d68ebd0b03d3fa6dd9407 -generate_ring_signature 847a7cdd3eda4d087626d16b975bd8dea19bd914d35051759ccd76080649a606 a05d0004f793a217255613e5b533efd58bf1d00e9ffe7d1062aa6eaff00bf614 29 60fa9da64e6873d5585d7c6b445aa994181e460fcf9c4edf9c2562432034c418 319cab68723c9c65b13df37ec1080812e182838e83f12a49d72aa8b82f8a906d e0d78f852a658ba8e2812978927a42caaad783594e463b73d25bad7ebf602a5d e5d6e07c783c40cf86479b179ae2c3064df13826f6ef66d1b5bb458788f628ad 97d175ad6b1daeb14df659240d2e567b269604bcbe62615efc1acbb3ebdad4de 5915b9e450d09664efa12f6477a526084e9a1c16c814f366e4175cf03f0bdeaf b08b9168207c344ec1f1addf104b06f4d396fe0a703c6fa22676549e221a9819 e28dd982bb083b405c5888a05b7ff932026a2d163febf55f7f8425df7a2381e4 b6cc0bc087d836d4e29e5ead0bfe51998943e147c259a851e138e931d6ee2cea 6bc6641ed933fd309190317c1b43bf34a7fa205b2cfb041c9f8d51da1775c09b 2438d50597d45379e848462033a037b7281eb6b3acd259a5190d9279e79a3d15 eec728d14ce0721d82e552a13d446866f3e43c5fd45d8459f6517aca1f703e38 6fbf0ca4da28ff0d2457d63f48864662748274eb304d6c22e3bfc83296ae0deb 51aed9ec8aaf554276b3397a30f18f76eef129585a5ac056ad9db42d6b062ab0 6d3b31ca3c9fcfc03be971f1107ec82cc5f5be67a0cf280c899ab566ee3c3cf9 acbf10cf496ccbba0ce691cfcfb240d1392c583288e0dca2b562710f0acabf5c b227447474e2fdbd382e6dc62061f4313c040bd951dd46d380b1fc4b8dfdb341 93ae8143cbcd9604e8862ee5d5942e90114e5133fd55052cd2d9c266a858dc85 2d90f8a4ed2e98285e9ab6938ef20da123f365b84f3b269b3ed03dc885e2c86d f7a9bc8f7901886e7f5b6a62c0f09022281d213e043df961c9bc61554ef4c8b7 58cdbcbb72356615330903011bf7267c8f2d7d61abfa88789d43567e51c7cfeb 742e0b7bfc9382859b7531e43e11e99c1ec1089ee901d502f116742194f78534 bed2b49b3170073561c62fbbcc1397c1e82ec0a252299234709a02b5badbe17f 6c7802b9d92c53b2b8ec8f761a260e680f92b4577485b5f516fc594984381047 3f32e6f5e41eb10431e1113916f94976f4fcc9db76edfdcde37cbd20a22756e3 9031e2a54db5f2d4358cc9486e5b25626c40afc5c43b20e260b0bc301d85bc41 02dca47cae77e6ec54842ae1a80f341adda1e0f5f78d016a06d1bc5595b7574b 65399dff20e45ceeb08f30f42a1e4f45c7c086e2a5f5c30f0645acea610cf4d8 894d72f759785abdc31f519d816040dedbb4df5af332187e9265eb1d4051da06 6f6da025d6473c5c0f30402add3b401e177f144bae5f907af5febe3ce32aac0d 7 11c10414428f2bfdf99191225d6767088b905a4b596dd124289af61b099d6f08d47e7b9a89e2812dbc0f2a49680edd181e2bebb5e15fa87f01e9758ebee6350d8a5d984a00b76a6c82fdd828a0631b4c271befaddea4ec77982da720e02d5e082767fbf8ff0ffc90572ee67f276758591416170972b6501bc83a009286d1cd000c6f7e3b509c26589d2782cf5e556c4096b68c61d72043246245e57277e240032143c68e626a892e073bef554aa840977d9e637098dbd3c149fc92db141d3e0ec4ae1c4300b47617e692f63799ccf08faa33cf444dc33889a068c8112f11220a489a634cdbc16ade92da3bdc970926336ff3c0589a495115eba06d023fe1c80edb81dfcee82bec113d7e478c54f89a25c4d87b33b0677a08ce1647da9fd5a70d85f5b6789e95e8a9c5c54d1ca3d56ddaa0cc56571e98dac6ab82455d1ffdae07c232de878e49181acc0b2cc33eb51d02d3540cae91135333d96b15e47a1ade0285267963070e9303e693d1f1cdb799c045a80ce380cde6a08a194e69a7db16055c92773704fb7fbf4929cb25623be299b398a6dabd6d028014541fc54613000422bf12e32d5fcd899aa4600b0c13e03aa95b26ba1f976b6b33a735af0a94e201b195216e3e2c8bf23501a74589f55a7bcc0370fb7928c76d44d493576d745a0e69f792b234fad8d472ea3f9570fbc297759d70f3673a6932a36a8d42ae3a2d0632376ac191dcdd0f9fc25abfe6ac8e5720f0ba12c13b473da46160be19e34f0b0d09e59545bed2e5b5c2523ce95c2e8ade3ee566256ad9a2dfe68e044c28d80721dcab0a0410570204bb849d12f5796e5bfe4b826e3c0bbb8210fb8dcfe1ad09a77969ca81e43525c5e9de8126cce2d34dd44dbcaf9807bd48464250ca18660161e0c2e111d0682be317670b272d2d698e6c4df1af8eb89daf6975a1710fa80a43bf38befdbb32382c28495af05e15623b6a9c404835da42f6d908d7b14a7b08a136dbdedb979f2b1b29cb9efa5aa58a7ccb839481c505ed76acbe83ca444f09cf15e2ee6139a4f56482f974ee9bdc1b72b315b31a8f8f40cc799902c93f220f30efa8cd4c550e6954e98cc93edf724a803e9b1b7c59c9f3dcd2476a5689590cb0a9e07dd0b4671d8ea129ed9f6a8ed7bc182f549b1dc87669da307c062088050dbc1feb24ef1ca535a8e0d16aabae5aaa6a92edc7151365f61b8139e3ce2e013cb588a239717d7fbef9d6aa0a46411dc3d26ea264d9e579509e45d6f7ac430e891401287e65713cd8939e85b4bb6eb292382d72e20333ef1e04feab76232b01e2b5e39c6d76cf8e678781e13cec166ee0405105174738b535d75f49b133f50aaf5ea2e08ac22474622cd7ab47d87d9ef026f3b4875f9206cdd0b4fb67e7b503ab99d29c2abd8a57d19c3b7225fa02eda9f53479188a5abc4ef7c47b17b0e20c7b8ea2ba759ed56b60a3bd77d0cabd871a0d90f5dc4687e1a91a56ba5c8aa30f9455d249122b3e9b266b251ab7d3b3560bde4ac77729eef6fdb70ab6f0f3420d4c66941021d6b0f888459af5d8a85dbd623910f8d0ec9c8d8996e4197903140a18818b582c110b1e17a1ceb342e4a042344b5ba7e3562136ea695d671578020c8677fb7b87d8fd1750429ddca808393f73fe8d01df03215de4eb0f594f92a605212e90f5ba99ceff394d935c4343014f0d25d855f53510e3043ca5a9003bca09dccd86de85e691d62a48402c6c6120f5a5cf64fce30149e569e01a68b42940053829fcb64a97a4f5b72930c8aa53c589b77a7f1a049e8ae41db16e50098e5006a3d97942750f7131ec74910bee4355e9532bac1a8f0bde476a70a2b7e6105f0656bcb519cebab51b2b34f3e48790f04c981c3424e626a9cee484acfedd42230935e1ab4d9f09860856fe1a3938f348008aa004a92a9ecace3ab54e2dae9b27026df7dbed07e7cb94a78a9c1c32eee7b54c81a80b8c3b5026d55bbf0fb561480a1de994b35ad106b178075e375cd701082883d6d27d5759fa388307562e8596059913b1c893e7f3e156b98aa3e7c5f5fd2a86465e069c36bf90db125b1198860ca3d4947fe14081d916ecb615a593ac25024aeb307eca1532536434fa1d139900e9050ed7af73b9890a34b9e7acd17cab1737085a9fde11f66b54154d62c4c709f71d041f19618482eb9baea9b500cf4d82c1bfdef1808e692d634c85c592670a8d78702b6a6fed879fbb6a6d95ec70dc4e1d3db9e47db31371b54ce682cbb90f8b364e970d343b3bb1c324a5af0a29c7ce439fd8b75bfc95c465623b5ad6c907a64734e690bb24d448353588fd9390a3af6f793aa4d9c1a2892534442cf8aa059d11f4bac838da49957a15a224626d4ed8d6a4a5d45ecb10e6ddeb9a969adf0e33be898686ad6043ae392e01a8b5bef2076064b91d998a8c20bba36e25bacb013b8dae6750a0f85bea108f5ad2ac20775cec6e45a6f5063dfc228295f2f72106efcd080aeec05dd8b026a1d9fcd673238444975d4a59331e991a85032c45e106b02dc8eddebfe9330263b407207da6a304c02a27b9e8d0f4c33f59160dc8b20b93a58e8e4d0b3b4fd661d4f97a0b10038c863a4f3377db58d575d1b0ed523a00 -generate_ring_signature 21b7918f07df71bb61eea02d6eaf44427254d6929fed711b69702a07f2581ee1 2170c653c773adf0ca992a320751335723a37f418473b51e05f39f4c56ca0158 198 a7310ff48dae857eff6a14d2feaa21e9d2002f36130b16460b6c39a4a7f9fcfa 638c6ab8292f8350eba3e282099ff5bf2157e249d6d8f4817c1710f650107663 15a1047de62574f925b50199dab771affb8f588651f52d19fd6412c40510ec84 fb40ef2ea3d9c88142c8014b364e0e0c7a3c588e5f45c8588e54450d51b426aa ca504e824c28187e327b5d7ae7199a246f73858407bcade0f3ec9b4d51678d6b acfc1fa1a602a4e8d7b40f5db21b12cd4fee3db4ad9dc873f6b2f835573ff911 a0c976e070ae6c6ac4950b098f25fd6152cce78b02f9c781500fc12714ca63a0 4bcd175516e2d4f9b189dc916c29ea08625a10439ea0a81ba3ff909ecf188edf 8c68075275c103c3581e9e0c3b70687278dc986cdf603376d8257e4d5d95808a a27f6a62d4aa751c9240553e0a3997eb518033004ffad5ef037ee51358bc6d2c 738354933226891098ecc542f68555f2d16326e5dbb8f8c2fc91db0d93db6c11 37b94158c61cbbd44dca406bf67c8d4c13e7d0c8c7e9d6794d5d29f576c477c7 dc71ccc63377eaa777e6f591ac890132d59e63dfd9fc26d1ac5d90e995fe2bd8 a9c3655c5e63cfaff27b9b7553d34b8dcb632d8d7eb60685904c589665e02996 35b199d68a146841cc6b62ef32f7fcd470c6ba3ac20bf4bde67ab988875ca553 7e00d706b974eb2cdec2dc4de1e71773dc7c7dea234749b3dcd9cb6ceb3d68f8 515e89a78026305838e4e059b0ee9573c61017b25e92bba60f61ecfd06118880 9fdbcc0a4a7b68abb0cc94571b407ebb3300c1a37cd1d629dcfcfa8ec982afce fe328fd284ef8f8ef08babe0357c39c1afc76657b3fb3e76948c546dadb5ae55 473046fbfc71a93af79fe516e88dd2cd2e4a129346fdafeb8458a887434020be 76025b240fd55cd3f0d7618cb987353c22ee6ff017f68aadf246af80d8f53a1a a3e4ef02949475560168d74e757d636ce95390013a9f7598e5751c785a0a3558 ca46eeb8fbe1b6f71eb46039987aa3a0333b2423000e28c8ef91bbf96574b5a3 756fc67e7addf14dd110af278b6025f47b721604970da8af89988004a74f30c8 430ee30d84f2bf7dd3d73f6cc7d25970b0da0e44b346203eaaba6b87c8d1bc49 f0bf365b8ef1c4af3c1489f969def37f165f60bf90c4b1e7d1e64caac02e0d00 90d8d60b866aff84be78562d69efb7c75ef8ad43a6dbf8180c7f3bfa55385f42 81fae7a46b3206690ba6592ec7ea31908b6b7426eafcdf4b72a7d867db80c290 47dd69f8405178eaaeeb253b81d9854ca3a5cf4dee853c9f09e5c018a888a990 3dbd9c96886f5ef5ea553af02f558c12699a62aef89f0959d36c11b9613edb5a b6e92ea527b2d00198bf9d08c2349346bb9d2e52f9a58f6f21886762a8bee77c 5137c2a7ef939c6dd71c516956c0bad82ecce452fbd92d80ad2f893d5db044bb 83b98b57a098b4606091163dd68af52900d995955054bfbf8dc5be2e285e67da a34e7a7bd0709f83d3e8edf3f4703e8e78f5a8328b8b537001ccd78a1fea0099 04bb7b526f22df2a7fdd7dd6d96a996dfc840d3c52e51407e6e02fb48653c17b f85f3534c07f3ede1fe71fa8ae9c41a7a6a8ca0114807f27c3a648e67d010c0d 30772180f96ce761ddb6bba60e078ad2697edb3b6368fd6b5532cb556a14a927 246288f7cfa451ceed0911a552bb87eec77673a9ea63868c95fa1a5cd2595d28 5ed823adf2b16c339daf9a63307143086bbd5d9bd48ad7884e12342177b93873 98d730c7b3ecf9a20d36270912edfb78e51f7d57783caa5f0fb73aca4a98faac c9b1c8b87c927d1ff20e8e11be50ac120ea712d56c79bbd2b0b4a7cdc380f0e6 e016f3e3bca138e242edf9b041cc9fe41ebbd76758fd4648b617c8566227d19d b4785c29d4cf5ec8136a91d82b37f00d32cd9f5c0cc4ed9f98d89e9e12321501 d7530c1336052f7fbad4f88f42f7b93a70d824ffedfc0c677b021ba884f54a2b 8a2b6f62da42c1c2345040a426a328224b6e1dde8046031bc38aa198fc946495 8ab5343f36337e7292f19f4f0cf0ed20f7129cb685d46960181ef8ddca615f6c eb77146a3a92895571947e4624b52f76349eea5415a1f0b64cafe44a1d86d427 144403c29b2301fd621b2192d294770c43271c4b1a65aa8bed9820928939f9e4 6fb15a7b933b936a2b00a1c574f114c8f1fb55109c0db8fbb1f6aa0cedd24411 5c098c6ddcc9775715a8712cc941dc760043b1be592cc1e794ae2df2674bd12e 3d3aef339088b60d7de300b385eb5740bd7c45dab071724717e85d10dfac35d0 1f965c8bf7cf38446d5cbbca496b9f4e1316598c9714ba751abd6279ba119fbf b50c8380cee5ab2db21d19d3790fb04dab6abc77ff660d9249ba511823270917 50f3f18353606ecf073b404fa9fd25dd89081fb2558fa739fac549332d81f426 b317387602d238b8f8e60dd59cc63418f3c911bf08e27e743802a6056d7dbe6b a67b3f52133c365193b3d0b07a770ac1a9ae2f968357f405ec0c5dc764d9e89d 51fb034fc77c16548c523577fb83026394dffcda641352e07f261046aedc29f4 cda39cdbcdee442b798ac1c3eca2c9fd9029387c5b9d6e4931ea007c69c1438b 5eabf6be3369f04af85cc3d9ea07eb7d36d5e8c917bb4ac82f65f783087fbef3 b93813588e0e1c30066efbaeaa1305afd7f3bd335fce47e11eba8613c2332277 2ab4bef429a6b5516c179036065c2f051e75cd0d39f5be376db50fbe7d7b524c 4ba8c45569fb7f9633d85db9c3e614fee637bc57f7d8366cdf43384b1c07d5ce 1c6be33f9df30233ee408d93647939aec0a74010acdcb1aadcc828378bc2ba7a 3a5440df19aa23f49469bb5a49f03e94dc8e1518f9acfc64d2992a424c3ecaae 9e251605352bc394ae6cbc7075e8d83fd2c7b8de90c327a3c3682e1b146fe87a 806ad4707c67355f8cef4711357215018aaa16178497d0b7870c27d8319772d8 f6ef4a8771330a3b101dcda998edd72efb655dc5a061dffa72f20fccce62a05f 458caee8a3eb73594db714221608f3ec2e8307b7bfe4ae6076a695ecb329c105 430bf3feb7d8c15d59db56c928803fcc586b1388a9f55864c9509af0263b1583 3c481cab8ee77c6c9182c23a0813c673b5a79f9e39ba968911926127b90fecde 354c79b857a63799189c3fd2ed192c9c5ad45649389191442c4d5d3d653430dc 06f2fd30509dcdbeb9f1b1dadf07a2c1a0f07f0c09ed89eed7aea5d177ec4293 185158f86c6ae379c94c02a972d6a2b7b509611c01fd464e42d1f4b6108312cf dc9c395819a1a1a4d441c34f826100fa164d746dec8e4ec67e2957cdb66bf496 3b2180d54f65060b21d0e3908eb9e61d51d401c3dc6ba7f9a6b6601ae9fc25da 1dfd4f86dab80474c115ced9f14e9654b8c0bfe960d4783ea749a04af99b329d 2836b7ec30ceff00da114f97fb42827adae4f851c960a82f3afc7649b15f9af0 e2f4901c3f23a57de9bb273e7c475a6e95cd151b0f124913ff8793a54e86b00d 3dd28896b87d391d35d51a898a534d20f08d7551e7b6e94bf1620e35016a8d56 29dc089a27b31456ac0015c122b2f01fd3c2df6a7198068ade70e56790b5bfe2 5f9f159b1955da1e199020a276d8775f5ddd405986545bfaa6d760be0b4f43d2 ddf00a1730b611dcf862d320af05aa81bb4f514d36c7debb5ba42e098a1afcc9 68e9c66c54501f39a8a15ae6ae4264acbab0b5d80de355e451d5290baae5c5a3 7b408eb753d2fcaff19e4660d42d3bdd8ef59e2064b85432b324090d59131996 6b0a02f9766c59032201c2c5b04453741921cd65d5150740d99155d2dca6e647 5c06dd72b563394a34e43ac353e54551486d2be717327ec3907e4eecf84f389d 416ac45ccdbcd8b039df9243151e7b150d9c8d5ceb663049bddf4fdc5b13d35c eea9d96242a44976de1526661b40a94932999919479b04d5c542b354f7fca5b5 2d3473d34a84358e2088f6aa6d9fc1035e3bc513ea613fd6604589f0befc56cf 33f7f9c42a7a1f400f64885a3b8f754f7a37cd240568ed89eb4b3bcfcf627adf b10bf05f99f3580191561caf04194fcdeb7b2354aba272159f16dde7688a458d a191c822cd757e136bf17d7b3255d2fabd55b92fa07c3fd2e7769290ccf759d1 6dccd76d0adc94bdc5a3ef4530edb326e205dacf527efb8f46c7be4e21ed9a0d 6f6c9c24f0a9dfc75ba05a829af930df9c90641e35647ac8d9dfe3bf162e345f 4bf557d5904b29c5ebfd6e8bd11eaa8c1ab125a5617c0e26d999afd1da4c69bf c5e1cb8996e6e9c445f5272b25f89ef9f89be61bdf71688f066a8156701b3c14 70e3172770643e04ec34ac004259f8d2219259abd222f89650f41861d47b1fe9 e6be521972ea378c4cf5c41d0776217078f891f04f630497c98e9e4cdcf0eee9 5bf7784a3925c89933199fea56841277e029bdb8e4dfe7ac78b8467d8f84c97f 70f5ad88720513a888301c9bd51a0d5dbe12de43df5d52293153547de0d4f8fc a62e3849fe1b727cf71319308e67d346bb3fc735e27152893b63ee238d0af39a 241c0ba2326a2cb22561110bfb366b7e6a6eadafea230a61dcfd4a9f6b4dcba6 568c41fd4e61caac83dd975631219405341201cc7afa1abc9853ae67a0a80713 b50eadeb0d534b9d1d089862655f4fb15c60763edc542dbe8b55bcded4e16027 f96b13a63ba656428ec73c7d5f6b22a5cbaaf54426b49252428af975333aa980 087e4e0d721080d22ae9b7163c846daa8f5b589a6691e8963c5da8025f9624a2 e24e5da5e60393b9553fc0a0b7a519fce15c3909ed4d79b1204a67dcde2e4569 8892499bdfe206daa5cd2355d77651010f3d4bd3dba9d9f031bff0754c270479 decfbffe3e059ea424c13a18e0825f19128bf74d3bb8bf0ea9f16c77e2ad2e83 cdd209bf4caeeb585551e50944ec07a319f3dffd5b09a012d18f5f96468b52d4 a85025abaeb2656c57b586cc1b592d49b01b6041f84b7c76d74de7ff226361f3 4698f35cd4064410373adcf01d1a5cc9975401fdd593451d29c8fa8685284791 776ad642d6cd0f9c3272ab6cc24064c5ce287d0c4c0c970bf796249515244965 815a7d461b39d605d0cf09624a01741b100d7d8fe7f5a43be593ac631e5f1ad6 3e06f180ec6b34f02d9fb05ad6b1e18f61c86b30ff481ddc5998f454d1e66557 053342a00d7f7e7d63094f10f2db6aeea8bc2a4604bcf0780b06f670780f5fc9 0e0b9d2d83b917845a3f5e73a04137a499d9e6fa8b867ab98bfa82b5fd76ddd7 8a7478a0b93dcc275f706f458a0f3f7022ac8812b1a0b3fb59a17ff33887a926 2aa41062284cb512431c3da7a24fd4dfca3b3b9d4746e9c281f0a6530c77bdb6 d2aa71095261507fdf7ff02a66ec2f810fb4cf25a1cc061f2fd9e6cd991d29d8 749d7d4fb454e0441786b0b83405adc7521b9133be1e9069e98b0b4a0a019742 9402fb817cb1bdf0ec3b3ea7f0e6de5af7ba40e5dc449a4604ffbddeca3d4bc6 7fb5ad8c1c39dc05b6b9f5683b0bfe3453a36ccffab3d7dfe8b0b24c9affd6eb 1d5beb87c516b2f164cf9b133359af4170f80919ec18b0c32130ac1b72384813 d7a29f1eb4fbf3d5e59c92fc82de7b21ae3dd5592fe5bf14ca947cb07f91f294 7c4414a285d08785d7ea218d851629f3ea64cde0db5f122a7d1234f2846679c1 da461281fb75f952c2d820e634628c9b7ad8bc85b2652066514dbd5081a8f001 24ed659f872fe2ab6ee3ad8e503aebee8871399ef6e8173a9a93f3c927c9a006 302b8bb71116213bb79816efe7111d60102a43e2553085ac726b3e9de9ebca63 ebf8a047c74fce459c1cfb3a267c973f22be621e82ddbdbf5c0fb8e94d8dedd0 82ff33c38beeae7ebc65ea166ca06e0838209719d9f68574bb0403e7a07951c9 856688e811d72ab66cf00280841d83d21d9a5982f971ca1b1406e5c205ccbeff 9314feb63caa7c71cc0c2e1957cd334732875c8da47356dae4982c32eabc902c ec90e690723155d6a5ba149f7070c142f367020035f3c9f7fe394f4c67ff5bcc 222caed12c17c95bacec54ca61a7eb3e37710521dd268815f025c082fa8249df 532fe8d1a928b1715dfc99a1e2fbc51fe9dc599badb35c31bbcbaeb53377253d d41e747309374c7b1d87c86affee4d0747b7a1d9fd1d127b54891d896008c6c1 89170b10570cdb10d9e191a8a73cd46f8b9698db908daf7fb1dc9c7ea34e2c92 4c7c8409f431e936708106858f52c7c54b59bbf7485f66533ca079bc93128903 e2e9f74f6d1ab74bcb1c276143e6bacc3656fa372ae0c55d86365d4bf87f387a 71370d72ac3428b928fb2eb392b7f9b08b1e332c5571b720b1062e60600d4566 3fd75f265802ca0b4e7dc1b45f2248cda782bcc3b60571b72ef62b3980fe4801 094c60456ae08e383c95c2dcf3bc2e3c067762548d57b89bb7bfff28ea5de068 af19c103bb8c17788e90adeeab90d5744cf8f7272b83c51dd1c5076e9da6568f c51e9883759ff945e2072a319879dd1940f8298b2752fdc99c3eab5162a9b9be bea441eae3c9b9554bbdd4f797920a478705209d2c837dfe6a43f76df9102375 68ca55b4f18a7ce36e32f68f31d6e55e0f6056eafe2066d1f4f10a1d36e4aa61 746d492d44cdee213b7b1ba63f380d3582cc84b187c115f13489cda5fa7e56ef 89407326a649e5a0904d2f02dd4e488f88a020c40a9f91671fd3f7c2212f00c8 a28169cb023c0f991bb0035546a78d954d0cee7235bd52bcf88a5df99b557f56 19c17f8474d5daf47659a9864cf1af09a594663270eca53da879650400469a92 10ffea22bad2190a153341c2b0d21cb3a4f907b8a7e926ab8d0c41382e4b8058 5e0f853d144bd9e6f460d06a4a3a39f150448b88835b4532c41b2587fd4c0af7 31754c5684b404654bca97ea38f2c00ad3e5f4ffdb97fbc8668ec58694724d82 b32e6d9773921282689cc369c0312f5a0451e66ea941e6448c98f8e981d48acf b147c1c3510eeb037c72cb71dc37425e963685f84f32a50cc01fd408d0bf4d20 57d2c9657c3e9a7296bb505297e55796b77f5d3db2f4fb1f09d23771ac62f398 b25cf9566236c3997d4d997f3d5670f676c13965d4a4a66fdf6900fc4883618f b7b93b8daffc9d5a4e47f4757a70e21a66df2f452bef667140299482f3173478 7098f7acbb510ee3f9c0499b2395d96f852d21376186f275f02cd7b2d87faff3 8c6ece212cade6da775e5f059b0efec5bb6136903d3e5ce07edb1ae45854b6bc 94887bf7a0fd4b7fd0d2da3bd78c13923ba3416a1e4e84c90aec94b860541a56 176b865ea64c0020c7e8371ffee45df70ee0f2bea422735c30201ee2aa31771b 45b58bb64bac42231f7f98469abbb0ef075568d99de4d3ebe2a480fb52cc6c95 10fdf36454f77b8909acf5478166555bcb2474be93a5cfe6d4c882c05e08d3de 4b0d03f74f8a5dadc9ac19dd2b0811f5b73b4dc60972dc052a656803510b8e86 c275c94455f1a680592271bbc61593c4dfbb38fbcad68743c1500cacfb542d03 bbde1fe13a989e8ae0ccb8260d4c1f3330a337dec7cd5c20dee2ad781610329f 50c2ae0be35cad3b9f25102f68b7e69310e1829d36ff52d23a7a000b31caef02 35bb8d2683e7e25d64547774b2c9e8553c295c367129a53cd2ed7447393358da f8e1f760cc5b69685e8d68d6d023fd5760790bed603f69b3148fb4d245d07e29 ea4174e4e5a6844c93f511af95d507c45c939ceaee14b0a033f2c93fe8f5a040 4f117a7382d9d5c50861b62a8ec5e5905ffa6633ec41b00bd61d0ffee78dd05a a88c98a8ef1d64f1395a7029525d0de4b131db3cda25b1a401c723c64cb5e357 617280cb8f8e0e0f42c62c53c732e87ca4fa192b578c1120e40905df9e8d8194 c409b94d1cab6ccadb8460b3d131cc18becb84159897e15edd646ec4dd4fdb9b 2e42617d8e1bea79f0f399443fbd5f15947ae5e52abe17726ddb03ed0d256d86 6a73186410df4cd8670032cbfb4b62b0b7f95ba4d2c3bad16d5a053f71515852 7205bdadd2305aea4b0082ec7bc8a2a8856edee2560e02741a775f7a0ed2e1b2 cc7ba0caffb42807788c1eb7944ab97f8fe62b73c41300131853c968347864f2 78d4acabd2f716af0d52789f27bf35ac8bfe3b95ebdf90437010ef89324fe1e2 98198945989839786c18edf339f538c065e83985d97dd7eabec1baddef389457 603d891e289f2b8ca96b43ad5c54d284beea789a4ae319517833723feb4ea9d5 8eec45e1c95eb6a99c6223b40f6b9e740709941c848df5ee5c4a1b23f100e68c 948afeeaf126f4fe582998b6cf014b9ce21b3a7e0cd439f40be5384f5c7ec905 2702a8ef3481001ff530a56006930329e095a1f73543b515159a0412d53303cd 1d0c05dcbc95d9a736871d7c12f9bf6b0455de0c6d19a17f90680a39e974b96c c92cbe487bb69a8bfd16a8fd9b4f2c272f564208f1f6a39694ef3a72b7f1c236 5d8db733373e9343d771cc78b6f7fa5b5fac0e5b7b55ef6115c7611fca121150 ff9701d74d4540e1b6494044cb26d0d1edc2c6df60c5f646753b256ef187fcf8 c414793da4b592458d92c8116978ae7a5d3bfaf0adf1a0799427907987098949 83184b7d1d49adaccd9662c4004306d458a42d88d16088ee50ec8d82c9d23256 35905e97de54db871c52e900b7b28c9524050c8508f547ca355122781dfb8dda a177e03fee74823cd5498a30bb30defbf7fcb04a77c7a03ea9a53f166196138a c589143a5a14b61399ac8303294231661f6871f01fb323b645415f9a6f854e54 223d256da69a41c4391235895d0afc14513ad9b8bbfa6c19475a07287bd591dc 6da1397c2980f41a9802a535ec35e3c605cc0af558a2c08a44435c086491c85d 2570acf82514e472c79a88e3baaf0f070386b8e68f783292237d920b3155de1c 59d4fd0553f2577e6817728152a7c6dc652fe69f78bdb2a95feaaeb37e8dda0d 65 8619ccdc28e2c6ad1d78acc92278d57a8f67b2f17243c989d806ffe59d470e0154bef575da6bc9213fc861f00b0dbc60cc124632454f326ebb0edbf5b6527705e9e8fa3faf0ea5fdd4643b19b8a4191645b2b36b254157e273c9f2d34112740e751449ad399729c427232f19bbad4b21080ee37f7812bd2bbb174aefa587aa049a397458ace132f31682b42b50678a800f038266f590468358708702607c8c0b9dc74668e4adceb9365329eb3124df574a7301f380d1465f20129aff946dc40ff8f798a70db172f131d99059aaf02f41665e4f254c9118fc038cd5b8092c6300a821b6e253d4eee95aa27c4253e930280c4bf9bbf75bd99837ee057acc859708a9d71940c311eac800ea335c5d310e2e8a19596ecfe59a7d3531cdece333d000462c667537566ce1ded1c0dd80c6a70cbebb9f02927fec35f316d703f72e8c0fd74b3d4a98e56edfca97de5297c078d66cb9668fca12fbf03423a81cdb3a300d7b61e9559f2e94c329160fd948a5b7204b45355aa6569a3e233fa11a82d0dc0b559b57d4855cdadd13898e09753bb96f57404868dccaa0633f4b3e301c7a2b04147407cad47b749acddbc47cf5d3f307baf60b61212ddefe25e0edcff45996035726b23470e2034dfd16d66d94afad189c770462be9c376310e5eb0225248b0c24ff29dcec252b9843c1cf114a648a9231aa00d5aef14a030ce316d49bf03c05387d203e7e5d9494624da459fee468acb9b7934ae0085370637fe52b7ab9460b14cd0d520dafd6bc2deac6b6f1c13cf413ab5db88f89c2a8b4ea9e1a93aab70affc11fdee8abc3089ee6e1884ceef07750e84e8db2248cf102310de1eb1fb90529ef9d91a7d49a0b6f0f302f37ce9c90ba0b3262616eeeb07ee2e139363d560346f3cefb3fef6c230d941e02b0bd2946ef33739d306eb474698204084a9df50d15e15605c3ae43960c8855a435b50bab454eb029141dae3f0eda3948c0a17d064a12b1f40476ac9010c208006f2a8d795d4a595575fef8a7257163312956bd0a7adfc45a48f4146771713cf7c02385bb4d46398a269b6818be8e391e2b4bd20134cb5f46d5e77a9405a6cd8d9f9b5c0a118ea14284e54e45f4874755de3b62045e2eb67c18934e466ebb7e9fc3fd7eae3e12777e4d7cc88f0daf5fa4bed48b0a80d6fde30f428409f0ac45f6da42eea8a92704cf023f81180442d199d7112c0c1631d4ea6dff7a25fc2bb7e20fcb489655987c6a11f17843e971057f40f7c106fb406ebc6cbaa4b0c89ec9aadd5464d75b61b7b9747fbba7783cab8c352dbc04c11cebcc22b9cd0b8c78b48fdc1acbbcfde6fa95844d20c376c9951936b7b1057c4414feb1b11c8e6c202a73bd927fda047873872a0c8da681d88c71021101026cf7e33c1ff022ec9b564780327eea91519e662a3340c5ad3162a372ab528a04c8638338996fc1a3362c7f05c4cf1a6f74a5fa61a4f2a3dc0a3306160387cb08ccc82274c00a0b42f99b9b1d111a5f8561b217ba8dbff6abcc1f1cd33015c607c32c5abf3f9d1c3ebb6f31dd68ba054fd550511e909124a2207c601168fd2809db97499c12bd13a71460b334ea392d563d1d80226a003e6e945ba6cce2a5370da0026e4e14a612b7285fa13b8a5852022a9faebf32f41acfecd049e19d0e7709671bcb9b962392b13c2e3ec8a2828cb0dbb1abe6b4ccf15c1aa97dd85fe86600626e03b74ba14ddf3045c3d94535784e48f7e20711b6b3cb5af8616bfb65f202f1bb7fc50dd4f26a16444647726e888fc7171405e08b2fdcb54878ddaaf1d800408064d928a3750185c1fedfd6b10a1975bf367cfbebe3029328c3201e3c6001ac793dfebbd53e9633d213f3f5ba00ec363bde58c60579047ce155233c147503648a46c96c253b0c250830b48374b9116fdb2375d6eec013f6649f3c22f33a0ad452c0a066fb8129ebe3e2299907fea24be3aba9f9d6bc743c6a08d92aa53800b54774478c747793eb01de09e64d97cce9f5a4614e2e27c7be4a57e478f1010f0b22061f85a6e65424d788bd560f0545de3a3fcb531ed19973fb401b8411d0089ac008d4d1a241779c7ef6568998815c55771fcab927d1b98cd9efca0ec399047a4c32e548d8a412dbd7f5a7b9e4f53cd645dbdaa5671652075211672a6ecb0d12ef177b2bf9013d34de12d92b8e1bda88905b83c8a45bebfb4d3e257135530349153fcdf84bbda1cbbdd403a0bdd9b3a33bec2ee9218c929cf691a89ecf5c0837ab939d7fcd31166cf83c90b2a677deb9c95e32814f74862fed3f0da8d77c0a0073ecce0431b0bb1158474ebb47f9fd237b3c45c1c63427f65cd3d6aee0f6075965df8f4ff7d2261b767e848a92bff7a0b5477a8cafd81bf91f941b9790560c6762a4db9a3805662864cd9c434780f239532f963dcf037e8c7fc5dd90a8c40872a9d160cc02d3aa8cc5ee51e55cc7251bb7c16d26efcbde48250ac7a6025708ebae57af842db5e3f30490bcf3054b5bf29765275d36ebfa6ec14e6692ca7b08e79658911350f679a4ff16653c3a7653a6f6b996d75a31d39c9e3d8fee15ac05881b0d067849c1381ae8a90024690ffcf8d8d36159c89caa2075b28e6cec550f75f0d56f322fc053f167b7d5de29867ae55b39f500e3127dd5eabc414b788800d7a4869f8548a77c68403609874d7ea599f16a04c9af7d1bce9e9884a27c8e08de49b964af7acbcf4b6a64ed4d6732d706c773d49ba6d15d15dbbea0a1e1f107e4ad91686cfde00e1406a5727d392e39b0528a3d9cc8afe25c55019b648f1504675f5c655f3b0d5bd7fc0de79daae393941f7e3d6621936b38d75802665153095b9e2dcd8c5e495311516b6e9a29c68af47687f1e2fcfb0e7dcc3118c0ffc20b4f0030e725b3c797ea9ff31dc2312bdc841bfddae3a358081df509e35e32b7038a41c1d2a015a7479de99e7575eb1ac25d09495f3b2962679d897c6faa113d01f8bc4c637577d93105e09d07843577d77400b049caf01ab348b70df2534aa50032fe7fdc815ececc6055c8d665b89acaed8186f8c728000a6fc4a068e9d2d2018bc2edf6bcec75f602c7b1867dc4a44d86843da1e88ee5ac82fe8e646bd5fe03cb14e2075ae8c1ff0667aa25ed66d586538c9a2cc0c05e4a5cada5949fac0405f9770e5bd48b9df949c2dcb334fca76358b899b27aad42902d16368b2775f405ccd6a893b9219967feb9e6f78b65cdbeba2ef33eeb1850fb1e8c874331b5060394d7bd816e57bfe6d425c78009ad38d5959eba0907f4f623414f48a64ed29a0b3150dab4744e50b68deeddadbcf0d5d6a3f19357b71713795b9db801f9063409a5b9bd2889051ac8082c8406c1f050a3f7a876c32dfb3d8edee7496eeb470c038c544b91224a3e2278092d54eba976eecd43668a24424e6309c9d5766910f80a3a714c000349003201a98290cb27478a1cc3b0317ac491a77608e0d7d3aa9f0b3cd418e7723de85fbbf3047595abb95edda645fc9f8d5f524756a25ae92f65054b0c77e21df51b08ea79521bcb9125ca1d4e509e04a0bdf44ac57c9438b834069d6b058e49fd85809a264f917fa25bf18c42bd6e95f7b33f0e83667178d7c0035a31b535e410a4a8fc1c46ef3b9a8ef06cc8e0e4aeb18aad6fe199e9dea04c0b482d778577827c300e7f03ce6a94ea91e6b41f3170a6925d5b67912a483e4b0797b7224679b8eb9046a594b0b19eff6fb0ecc68ee65d4f1ddbeb99fc0fe9430bb8ad16cf8b5044a0c27f41dd400b4a04acea2a2ffcadc5d99fbdae288ae3470c75c8da1a399872cbd1cdc12d464f7665d99fbd1be64c35d16d029ae0a15ea0007966aef16ed991c5d4a42b3b18932d43fc3ccb35f6a1ba3ef1d41c0603ac350cd33241cbb9ee8d02517b23fcf0cddab42676e7a321107287133afaab9a5d820cc6da5fe00f2eaa6888df02005d1534855c27d061b098b233fb46ea3e08e9530df53acc8397c577edfa2d270052fed476c5d822a112b6c18c2da94b11fb74e40b0fe0f9e2cf2209606d85299d00961e68688ae637d73a5323774282abef3bae021655638e7e794ce2791257b7621426e23eda15900a51cf4a2b2ff06d1b03ab016887196f2924918fc7ac7d85d355ccdee64ccd37efc65f61fe08b95afa6ea707cc12cf4b2007a589d0a9cb0098956f6e773c534e5cdf10fc0bf595e315e0a10023cbed13710de59ab19d0709682e0a28b204ecd3f0e993491b42ace027dca90b03a61c06b3683280e8e3a6a59d45aaf3743fa0fc480ce505a0c1f8f750a51a0bb4fe3845c35c2c79e158125123f4eb6d062763aea26b5da6797b29147d82c90cc7a8384a2beb58f3909b191ca5cda1f90f23b9452a6c59ca88f28b9512e6db0833ad81ba4c035d1944373aa7e845b94ce5950f688060422422812a8584c15b02f6469a4314f8c65fb35748832a4607ff723cec81cf00483bbf6255d706dc8d0b1c4d2e41b5cc7b5f11dc642ba4b1b089726d61b5e2fd2d20fe6e19b9aeed7d03f0d5c389041985e7a55f5279d0db38950831517df9d963d1423b84b7ef2edc0865c008f1ea007294666a132ba012ccbec1bacb30aaf7167d9894cdde49541d06bd257308c8ee2ecf9b40f66892cca437886328e33ea0501d2ae4bd9fa8255a0ea68b13d1548020eb4a96a265fe2b0ada77f2c45c8e31f4d8d02c73e168ba510f8e9a7664f9b51a53a3f4719c702fb114118a77a886a18755cf650b74f510780dc7106e4c7b7796b6965b1b6b070f7570fe673fe5dd550c6054d42326154aa9051a6d458e3fd0bf430de5661e8759aee5f40398d3b2d04fbd767d36ef544e950979562bfab1663605125b24cbc31847b5f1ca6ce2dc1b841d170bb331da3e41018d341439f63deea85e184f2074dce5c7ac98ea6429a6088ea88a423cfb731405955b752f27f57a68beaf0e49f7845cf5be6802b2ad8d736652f8dd5240a8950223f25bb109f8cc9f0571ddd95afd80b693c08ea4967583430fbf49409df5ab00b6092f8d11774439830a1592c34fc1219bd3f146af29ed45838f4e9ce60b7508fdf12ea11f8759ae7d496e9294189ea5454407834a14765be3b879d5cfab880e51b97a28f8c03ac304e1c047b6cceb4b043f39204d85c16b62d9de496806ff0f3e64ca2a09768da9c8d87c1a48f6734929de357ac5443882b45f9e8a0c6b2805e8583bffbde9e414ec5c8828942f8a250348abc75b7486c0799f6f7cae46e10cedb4db291dc472948c5fc4dda302071395bfaeb0d9b835a8c93057b518ae240be119af0d5b12b0fb832e155a04292c3edd4cda39c3378d6a6de9d1f18506ba00951a0f872c11f979255978ca843314548a40c265df2da63a110b2b9453886807ee1ce3b5587cefd18b73ae783002cd32ce04f6190b61ce0a246fc45b3c875602258f3f709de654cfeba37d15464368959020480aa4801d5d4a954c7916ce890060393b1549c5dcbb22506a2f1543138d18358f56f89aa45c083c4ad2594c4b054825506b4a7600ace9b95e53fc6062b2ca26272f8186dec23d33b2ad49144a0763a155a15cb6b30b5db2baeb241ff372a3680eed3e18a74bd7037e588292450fc7581de41966fe04ac299fd6e165338cc84e99ef5eb2d3a922aa4a15fe5a0d0c8c2f6a9c9c66e8b56eabf5e59273751a4dcd3994c1f6a9d8c2b4cfa00fd70002b8c631a4b1067e3aa48a3be7386652d6c93b0b9c853e74622d0a85f4aeefbc013d43c6d2c986c6346c2d0630a3ecc57c3e6605bd405dd20971ea15bae56e3c00ea3660f570d1752cc93cdd6c363233eab0a08b39d866ac6915160e955dfe570d8c6c1570bc35f55e28f62f65be59697b21fa3ebee111d7ef89230a4e71de7b0da7bbb30bff9e204acd0e6167b325099efbd85abf1f5d24def8d41e261c3c7f0b626a6f7f2508322f74a53733d6617f44a01de0f5502770d50e2d67d9562ca90a6e603b933cc04dbb66ff167c73ba11c4e325796ca86b6963e46ee5f44d2af40c72ab07623694e5c89ba23f1b35c3c4ac3c75548e1937f0f8849cf387fef0b409082cebd4edd726121be4a0c143e6c15a67f5e12ed65a807074f89aef087df00972692dc609a25c25ec1dff2205d1ea29ffcc023c7f48d26c0fea90c343a69f09f60e62f272d0f250b7b54f437fc958a1a1dc5a17b4fda42389a8de1aaa8e3f093713d8d876169a96de91d5bcce65245dbab31bb28d1dafdd3e23aa0288a55608e03791dbded1afd0a6aa4da0c2325f0a9aa2c2596ac27ea93119be3e7969140c03613754b08717fd3396815370e51f7391c5c7d080ebb910c12b6042a54f5909879d5ec494e4bc4a5b05398036b4b5f18502b27a14f4284b4cb61d380e2ae5075171d9394087c2eb4dc8b661a9307213acc391d81d624c154bd318fc910c6f0fb3307a50ff853123da75c9b4a1b4dec32cbd0de838fa160a9710ae3410cdb909e22182738123b078cfdfd1e1bd8eaf644658aee061ab568dfb5976449d87e10dff356bf6603976870a325e19781ef9f50730bfb51f2a75b7e9f4fc6d32560602cdb294f9b3b75f59aefbcf291050c11984c3d771ebde6a2e43b963408dbf720b4b0e91b623f0e5313babc2afbe23bdf8859d10adc00cf9f603d09998fcfa61080dbb860e66ac12b00e5310d28dae46b0ce7ac9ccda376c846870038b0f6f6201d7247ca3a707f2454ce652d7cb063a2c7e90acefa22ba4ace0c65acb89942f0d50f27e1e816eefff571c553d884bd1253e4bdd7f2e37c3d13bef47498264f0040408146405617b176719968e1170d301f28506b1978a1504b46d456a2407cd0dd6bdc96730b8ec017a7d4f6f82d55843d53821064a2819d29d2e92390f56120f987d2469caeb61bbebba56ca01efc2f6793e5da5fecebf3efaa2ea11575e2a0dc6bcd5c56a89c7e5d27776c03ecd85b941d93b0a7305b075f245a0080e70950c38c2cc6c45880dc5248fc782337bc42050d574d1a06a7a7fccce5116067da00feb64ddb8b2feee90a8e86837f074f3002c9ce46961fb8f612ee9cbc1d174300f6e3ac8469a53dd1fa5415796b77fab1560322eb4ae4e3a8106d2a850a8d33606937b62904d2b9062dd6eb0c33849de483afdf44231d33fd3ccf99d5980c1320704a5f76c98af11cf0e511969ab427a46862ae0f11f81161b3e2f907c3d617807efbc8e302fabbbaa4a0ea144b9d1a6fd82fab540d50273031bafd0a91424e008725ce7614bebea99bca36df951f2b0aac4bba241a455a4ab67ab1ac34c43df0ff6f959fb16e85e96aa007c1cae47ada270e4e2f75c0ce67ade4711b0fb8e0801ef88aa18f3e93e2db9bb83f37ab0bcc501a4198bf621e0552d322fdde50be3044b738bf708f222ccd15ebcfa958a7c37e0c42ac868ebb63f02f17f73ac0eca0e3f53d58ebc2f9a53b525648ee5b530e85822c46f45d42c8691ce74e28ed74201e42ac54558906c736f171d29ae2e8daf214d99aaebbfb07c2cc989fa6743100bcb5694ce121d8ec922db8bf98ed386854239e9c9c1d47bf3f57361363701b80bc1e2ef1b6ac58376bae767bde780898b42b12e92941ff9d15e30d28653af42086267f504bc795caaa946744f357061fadd0bcd6309562e26716560c7af2ab30816b8fb2c166aa2b5e62fa63c3b86966bc8c5668a681183621e5420b962589d062b448e3c1af43e20d2801a043417980a333734d0724bc34732f55be216373b0d20914859f22d99f7649ae5d4f74033d1918feb9b4f195789a3976803f1450e08c3439952413fdae92345a1245c598d29c098d1e73ca1432c775d966b5f82e50c300b503f9e85f354659034a2c2084f8bfd2df1467363d92de86507f5b03ed20637b8d97ed228fa49540c73319e577a3a4dbcb7308e5a6bce3de199ea088d160e467df57311278e27b0365fad5d715f975eb4a8d726cd28dbfed324a97e90ef0bb182ab8f03b6208a475f039ecb161218272c9c0842cfacd93a247c9a7c02fa0490a09fcc70e019be229aca0605af8d1ef594c3885b224dba3dd8813df2e07c0894ca783dbc2d65c8197910cf6ba8d51389dc6f5216eb96682eb935f7a26c5f084647dce628d3e98854e1f469760f0789749d1cd880f922f368ff28617a79e10fda7e049d2ede21f6c2d65bfa67a1b959df7e8bdbb3020889f9a041704e230106c36d7a491ea04dc03603a16156eaa80ef870ddc6c56e55e25b781042c087b507d32a7ddcd293c361b502b03a219f36630f2d63633bab05efdaecddc9d11c690d97cae2eda7a09f76515645d297d0870a527348d40f2a0526c3f5144a86e0f00d00a5de45c2e80c8c20da3826ce6f26cc05d4bd43642d78fd2fdac2d88ff20c0603466a40858264b68fccc45825230d3fb92407fa31255b563e2aae5da95e250a395402f0a0b5cdae20ab9fb0182fca2068cb169469ecae7ddd8e9f35cb52e10e997858c84baf694dd0e64a3e85845764cf118b3e47d757d46098867ab3c3d905fbaf67754e7be88022cde8be52fc45fd2b7e57fdd95ed51a27edd6ec9a35f10c1709122eca8dd74305fdeec2b42c7e1b811796eb55bb0419a0a973e6a58ecd0907b2895750bf9523fcfb11da42f486caf875c30f1438f5a178d7805245f77a02eab182431371e47138ef925319a53f1c00f8005ce451b110c109da51ef4503060c3c64b7082a29a9dce8c76b836100a20ef38ad60b67bf1a899e55e5627fcd09d84c37bdb751339b3951235a1501eb2c16dd4c9b9b6ed55fe12e631e90a98d03517197d9e750a34a33f8a6b7fc98b06364fca1af3330a9162dff90543e70f500f02c3c570cd00cdd196f82a8544651eb05c7b24291a01c39c7d27cdaeee73104fc1d59cb7944c5b2bfd853ff20d2bc5c1751f082c8673eb1712a67bf0b984602aaf86f745d1b235759d9fc38408c3875d8fbc8742f8131ff700db8f2846f1d0a89d95fdc6a36eb97d27fe0c4041cd9042b0506ff8896c325f42c58f4a39ea007903b8454bce3f4e634cc39ae01f5ecf7d3ae0330e9e3c91ad6280e80601f1c0fae0527dd18a0888bf05fb6635054a68147d93de570106f96bb2610e1ccfecc0da6748c9ad8083dc79bf2201aece849503015f6c426b0c3b349acb2a469424d0f905731152f2a2d171917405e87ed37d48e823ff53d03e4ab48cb3e5289afc306d34db3792a13cea3fe62f50d59da6029266eb8b63aeaedf9ebde9572a5a4da04d84d28417aa4128f8485efc4cb77943bb99dd408b11e0cb5b0c13b5b7a537b0df6e654723d31dc9865331d759c3b5a8ad8b0ea3175c3f4db121fa8e30e4803044bd283e711f19a75b5239ff019a4bc8204ece6c3578fefd9dcc54014f2a65e042e6f6a4d60eb691a86017edde3e1b2d827da7c097d36c5c5debbb52002eec604306cfc09e9bd2ea65e5007d445778762137bededafd865248016044f13369b045e8e78aca11d79bb9179f593bba08505517fc8c0c413d995ca170ada7241ae0fcb056ec90c62805059555b5d61d9701df410c7399050e04b3e04d5e04c6b3303709c963901a4ec97bcc8332f3ee7f1bd57d63f7527c138e7b6a91c4cecd5f101d272b3fe1b8e7a67f2aa07e618e260386a6e391107c9df1bbd2989f8e4941804571c477e01e4701a1fa24a4eb30015d6702819d8614933bb9dd4700f6375f40e1b925839ee72af586aa73d6e32fbec1fe09d448a14411cd5dc292b9572151509821d36edc1f2534da4d3bcb9ed3adc1e9f706c1727474d3f214a07f01b22550db5e9e90b7dc50069ed2bb578f43d3d9421588361fd9f0516cba3787360cbea0eb8f968333db3c32a9e5f3cb3a76c8ca2fe4f29a0463712620de1dcae7c10880966996e8abcc12e74b213f4dcce7cfe464ad04b2d13c2741a22bc644fc730e20d5ee18d903f99ad6e5a3b63be8aa8577fee5217882c9e37288e00e51c61740d0e4c8a4a7987da408caaa41fffc403013cdfc1dc8c20886d10decd3453e521c60a07af3ec48551a4eb3f2e205ce62130fbf2c2a33d9c70fc191410abbde079c70fa78b4c393723bf0821d161c93552c3efd44ced20819ed504afb6f1e062f6020ccb9e3ef29f2d31deb64c18df2adb3c11fd3c85580db856c8c969efb5eae98c0f192bc444c795856e7c505aece56c907540d27eb974b051139dd977eb95abec05883dd7d2d63941ea51fe96269bbd5d5b513eae06f05dfd16c87443d85166800a41156691ae0624f17753d009f617f393788c45a61269f075873c6f8397298001c0446b0e65ac06b2f1638ebe323eae67c2cceae871a7d43513f40106c1cc600991b2b540f9ba0791a62f653345b84e60d9af3035b5547339e5de94faf1e1c7048656e3c02058d27227a8bae162d58a94a1b0826105c8ce4e7fa47a63373314041bff965ed9b4865377974b56296095b8dff9d370844c83828b18f3fc3db7490c92e529529e6d1dd8dce73b6dbeaf4f3dc1a0ed214041967b263397d0025d1b0065dce22184ed29d330daaf1e2c4aeb92f26d4cd2547e49e8ea046491ffd8290994ad32ab152b6bdf786a490a5a787d8a2b7914aa4872f0dc8431fe510dfb770a6c7b96153f0734dec6ba7b33d0a96e4f4b84292790682ae8be0bcb7a3975500c4fe6225ac229ffdbabfafda099d8d74863d2193a40fde9fb085031a5c2a0560f85cbc2bb6f2965447abc910d5b2ae7bd3302c824b8aa6c60e8278b87287a23080cd8ffb18796b4ed7232f5d61b06cacd65916e802dca4e629b58e642bea251071d4cbf2a64d4e17bd50ecaf689579cec3728ac6af1c9b8c1af0af27297eff30dfe1dd9cb3a649ff36c6f435b06c75969379ba03a44c8be04d1fa34f5f719b0044437b3b01533d0047171e60b40deea816521e87dd71713fdbea62656b5d28a07793089a9f2aa5d246ec6a67bcda429333f980dca81acf675cfbb802cc2965606bc0e9ac2634848137e0b6852761d845a65e1c080ae8c902cbb3d52f769ae9a0f246f62674e15f0a4f39536213d2b9a1332cfb79a9f279fee42972630200c3d03d12343c11c1ed4e9a99bce2cb4fdc7f5c43e453f968b35450238ee85009ecf0cce1fe8bfef4e21922da07be3c1bf11e59c74ce7ad2a327c29c34494b6a212b02b8b15e885e48c15778fc14508ac006714ee9f068b0e4ca94d9c67d1f7cbd570876c9892d293668d96413fe0e532f289d6212f53fb5479c54e28a7d930be29c0c30ae890d52a52bc42689c909c2de568e454ceda568d69e829a62ba831cec280e02c7f32a223d221a7504ceedf7baf8afd54d33e5aab241cc088a6f6ba068c20d825824334318c894e5f76bbcba1ed965de24efef30ee2820fef0e875b6c78601916fc12f38e0e7b61ea0cd8e616a0bdb478781f94a7f19eb0d8ff14836eb1a0693e8dbb22f9e8a9d1ed5589d58459d37c8d4a144ba6afc5c4f6d381db86ac806e3d1e6974f9b22f564dae18a830f7fd3460ca6ef64c12e25d9a4b2c427a24c0903302905106e03ff340492459d9a110d60f1edaee07a761cb225a24243eacc0567238e3362e4c1351658c2d03829d852d92e27011c657e7a8d6762be6f19af0157a037ddd48f20ffbd4bdb98bb35c3d82f903a9b4e428a1484fe191115f2890c7f0ce4a27dd1a31ed687d789399e1205c371315a2c9fc32a42a51099065b480435b7115dfc38115e861e6cd42b0a11e1a12965f598a6d332d5720743bd455105bc886ddb9ab24a2c2423804022f8697cfefe0c4d49bc9cc6f9c254cc657ad200f22df5a306948d6dc8db7e460d8d3f4c58a6c7fa4c993b389167113a95da820cf648670dc3e8ac1db2706a22f13c5c8337e3b548f57b27fadb16a1252696870485c769cdd1dcff083b494972661b3f69ad3c093d40b9efaf40c0f420e02ac20e141ad97c2990473e015ded0c0325f364b023df9974521ddda33ac23adaf9c2044a0ca8a7c4f85e25726f239225925d8cfa19ff4b080639e171368bcd78e39d008a4e23d98b94af3d8d7d86c3dc5f2d2580a3c4d65d5b75d0f080a6f21c8a390f7c3649a4eaddb6097fed596aef580ecc5b746cb48ff059fa4b972d30dc6c18039fe01210ed4449f8b2799c7a84484f6c3ab72aaa331fb77ef6d20aebd19a6f051c19317c09f28e311b3480f5af428beeb9a5f8274fe4829166c36d1b30586008a13711cb0879a5730a750ac2357ad688625fc0ac6eb25e5388bc9e206215a5067eef2c8822160eaf6ea86ee04b44936fdbcc51a15a264e98f21578695a8ccc025bca762c35c2bba953f73efcd4b59306e004b64f570f74043d8889a6b950aa04aa7acbb7020c56def0fe9991767d6010247391023ddf9d19b3d9149412147c00d9c3defadff5936e0289b5615fb0f2e5575d11875b00395e911a2325d3a8b50150700099f6f6fce6655bbeece1988be8786a3157b48b488f8a7d213ec3267d09dc087e8f02393db3977eaaf0a8cb41417832d485a46cf1418dd99eba4f2e7f06a32c2da22fd18c58d7efde55476e207f53ab708e7ba0818902b145395fc5e90d1ce303b2ad97692b34bf025b850c4bf7695c95f3917e39183318033816d1ec0faa8e6d1ee814bba5c2f106ac18d7a5ac720f6bbc57745ea502fd6ddcbe901904c6f4d492d55422b1d939aa37f4481f178c4251622e60382d34fbc42b28892701a5fa2b3446a8cc9268e22ffa08ffe8b28bed706cbbe8d18848ed733374c3e00ae00a730ad101ba3e26ff62006157d2389fb51bad93e6ca8df9ad860f8cd35b04875a7942e67f030a94003b2b1f89a6a186d89dad03cbb69ad9c4a213c9e942032786d1cc3e27d4587da9e989818f2265be650c2c9c0493d75bd843650cd056043ab228b14401f8e14de34ea7ea22c67a22a66874950e966f593cc96996678009a6371cd3ca47dc5c8dc9a293bb94864e2190ea72cac53aefe69c49b30923f20e957eb8a89c25039bcff8e3c88b3bc1506f4b463f5e2618d02afc814aa679a60a77958a78bb1b8749898d1e6ada33a359ade49990094f80f117145e0ad7e05a028b2005e9bdb3b4af2046b9c55c5e9d50d1ce418152b83aecade4453c6555fb0dfdc9095edd62d6f5cd6798bef9a0daaaa7bc5d482b101eac0d7432bdd501bf098909a5f0d08ce4213b4cdd6e94b984a8d99d9920d07c624343aa5eb5565acb0a8a6e1992229d45ed28e01d3a174997ab83b036678475f20664f26b3e74ec1e0165301cd5959fb5e47a65f77c7e07e1070be6620fda4fd62c631a75761c24200ae87d177250d67405733e9d3985d51319b3c5c388bbea41d399caa208b7d2b20b62c9cc9681f7a6fc957f07727b873ab8902ae07be2f8a8434c4fb6e3a58d6006d3949f9ebb6b787a3b7364327bf9ef36975cb13e54ef01468f9d6026b25c3508f634d81840a616cb80d9dc4760b0f1bfd679e355941c18652f1db8412d0d4a0192a10622979b03330152746d9e1e1f0e2947433decf380d7893b99263de67309e33ddcec71f5d3b60cd371feafed4bf4c4977436946cb40ae8de1c415ead3d02e2912195a67b6dc2323b7af8d68dc6b38ba484e09f20b5862e54f4312e3ab2003ab0b118d8e91c5251c48ce461ea48845e059e37ec3d74da5df0742eabb4cb07eede7482fea99b8f7115ba538e132462f67baabb32c045c6df60acc33f144a04f1774122b913ecafe6ecb941f7645a0f9c8c2490cf5e0243b45424074bd44c0870e2406a8693fe26a3f4239d4cb96a03eecd070184f419df7a325f1920efd50848e21b0a9eb6423046538d2af0069be8383a4a7868bb88fe41ad1001d81a0a07aba556c51ce361d4ce9ef2805486f12083d3bed46a2a1697c8d079b93294ce0aa32a9ddd9fed90b13993b3467eb32a355b302ce65ddb27a2c7fc1a43828d0c0995ba9f35c26baa2a1382ddef196c88b0f5da61347fa76f726dc0bd41bfb31108dfb4b0cef03dd95605d0db16ef2659a9a75f875e82ecd12ebd37beb400fa1201c9b40660f31603dd2452965816ff34d5a688ad3345e5e599c2d5f8d2638ce10b4615d4c53f9c722c878448847825af375e6dffbb41dd6b034c784ef6303a340ad741d62841d9743a7c7ca5a9369a99e8d2af76598057ce0b86f52c200d4b6e07fc352aef26ff5df78b20b5c4d8c88c328f9a837e24438d778c2f9aca54c52b0bd02a99e4db1b1c69d052f8e866b58387b06656522d26c66450bc9616ada61e03e33ebf0ae51bba5351bab2a0216927470e07044330500597ca62254a3164490abbd69eac97dbc210646b8c9427f356a370662164523464b4df8377b069fccd0526542a947a61059c6e06aeeae3f67e86974d115e010c36033bce5077fbae5509d760976cf825131ac8977393498c26bf1f6d1494f967d3a200f1e7b24c774e0301742a0521b549345c70106df78eec3542d301a4d8b85aba0d799ea3487cc70ab2630e249286faa9a370e2e22dc06e9f591421e0b9ed9b8d2f31f1e39f1e05058487aa6ae732ca7396505d0e413840b37dd5662882f569539cbf589c2c22b008ed6ae4711606c183dfb8c2da4554eecad31e79dcd0f159443d476ff68283e605ef15df0e1cfbdb7ce7e7e448620192f9723c3e935cf618e432c843c0112aa0003518e7c7fe49c5e6f23666a3ad14db368305a7438b8f19fe8f76c19c993aa20e8adda3139e2884e426479f48fccaf4b30798223157a707db2185c402bafe960de39b3e8641f0958bf7522e2f7c5880b13bdfd957208c58937210c9821ecdbf07442766cf49e759e6a0b0cb7acf82dadd5787e850f2dff3b5ac1ba94a7e765404c04821d5546ac9019350ef9ccda1194d0b2c404e0a5110ca58db45367ce42a0a6783f1bc7248d916872b3c8cbbc7cafdbee470ca3902341ac3f3ef715b40f206a264e816aa64cd0d2ae620f4453c6d2cc9bfa7f8dcfcbcf85a519016801612071ec0cf46f0f96168c3cf05affa63fe0c35e7b8334ae9bc0a8fb979a3f8bd66033ce6b12a8f2642718513de717581b0e96fb705d33717c6e8b68d9b5298f4b4036672ccc3ea040017f47f053e6ff4eaa141e249c6b3823c109393d01ff695d0018898bc980e07fa21c4d13d1f7423496253378234d207b8df126453ecda45510ee8a9c72f3bf391ef02a1d5d22fcc2679ea15c556b2e65e353ae5ffde93c4860e3e707baf5dbcc7b7a7b90d62866ca9a5f32c57446bc6ddffa8a853dac13c9e075d6bed5611a6306a4aab3c6794eed8ebf211e626726571b4418f8d3a20841c04a7868e746afcfd209c9202493f43c9b4a1bd64ddef9ee4a32fb7a5a728d5d9058067060228680bcfc8ed2b61ddb0bb91cdd3b8f5cd983555784c70dd72fb7f0e2cc8930ab6e613a9ad39ad96a78fe487a591f17e44861e372e4aa4cfbabbb60acf6a57090e4d7920ae8b3eac3bd851b5e2eed9280711ee2dbfcdd505ff628105e215e95fa96f56479865cdf855b2c88dd519804fe1d17b6d50c6aeaaaebe050a9564a565b8c651e1fbc257519323d3ea81db8df0906f4d75d540a93a881a580cefe685b776710f5d0892fe810d05690e4602d96bfcee5f1630077ebd8d3c0b039bfa6b3e0bcbab5320b9a02f3469e4c2e85e1d8248ff47d0a45453ad0842450bb76227a4cd005089f9dc5de3446799bf6f6b595098e864284fceeae5e736ec04341ee35f04371f9ac88d6745e3a39fac3be447a4528a95a7203e7503a8d80a042849fa8be0c0b70fb18f8605d6e266a67b13571c3fcd3cf97be922e97c62250afda7d81dbdad5426de95f5a504f825ca7efbc941513224ec52075ffb35e47002b413313c358797bc3be7d1f52a28d7f5afedc885dcf8eb61cd257433be9eec08197106371052b755febe36f540f35555af99e4942e300677f8071c733ba15607f5fe58d2a0769637f0c9aab23c3b972669de8b1b87c1462c896c6d6301d50f0cddc0329ac8738dde283ea9951e989c6a7535b1aad713dab64d8f5e9d20195108fcecfa9c89b1a201ffe5218878c8540203903ed4dca085c868b79b088b1192032141fab809fa08e920a0767cad8478a3cc96f259b81397e91158f1a7f05b0d0aa3847dec955badfbb23a4a97d18d5cebcb56af68af0ea428508bb0ed1093e207a81bae5776271bc27d88ad08139c0f2d560e0808c450ae986470c9137f80330f1857d6c3331facd095111c9fe3c44314b59b9653d231b7b74bf699b343fe3b04cfe233fd697c63b84eeb2df4dee760237e1d10cdc3fe488d8833c04b3bb6570ab92ef5298bc82bf8d01b24205a15e27d487ee1501c2805f9dd9fd2d86a04d10d0159fd0ba00c339148ef6cd3cac552c3015b9e48c89ffa3e02837115b0dfbd0eb169b1c14bf56a53a2ca2a051bf9dafdaabe677c6105230bf0ad96041b67140754b2eacc0c9886b3ca369a0700bf4f3f2db789016baf7be8922129e579f3ae03077531acfb6254f9162f111301f060cab2699dba7a05ef95acf896a1af71fa069ab594a8bede80d3738e1c3aa48f682a1a4cc955672767a62f60b35e6172a501e31a066cfc40673580eb891e3a81983b2427ddfef901ae0049d7bff333e494092bfb22b821f4936460338f50402f68e475df8dbc9592fe44f1a3da49e683fe087f28033ec7c8913ffda7f4291e5e949defbf9ec734883af192124cae8573130c6d3c87c28c62b7e6fe5b0b54808e4e8a16ee60a749abb43a4baa3f2693e7c60492d1f0cd10fb27e19af3252c7d4e82c38e26a345a57befe541b771477b839204a6b73ab27ae97af8014d1cbf7cae900f63f8613883e584402191f9a7baf84a07fb5d210f2f240c2bbdf1d3ad98e518302dbeb981a724275fa084872d96de6c024e2b9c22e9db344bccfb5e3ad5825a1307738265471d98275130afb08ec6fa0cf43e224b17447304534f24af08ff0de68ab29d268bb47b81d1a543e7a1f37f022b417701a2d39fdd05446ce4761fd20e1fbf550ccdddf6d2cdfea409200dbd09e6b1058fb68bc9d2a73856256a80cb5583dded609adbad45bacc9f89be5678006341b862439fffd1b222278e884e6679e8ec99c22972173bd90576629db9eb03def1c4e41f0d8199094214bdde0b37e3a010295eaacae2c4649ddbaa6b7b4107c5f1169b96f135fe7d3dff750f8926ba02150e86bf34014ceb9a60c06fbf620fef05a11601f9c686a9eb4a4d13d87ddbfc17c21098932eb793b1e76017febd07627337536611c5402b7be621a79392d8ae62e3d842cc93cc621ad76d256f4101bdd6d5171a01d087467b4996e49db2ffba41b6912eb9500a05fa287e98440b06fa7ab58a0ec46de5085a82907ed4948ae740421598ab2a48731d15a40cac9e0d1f9c9a9954eeedad3a6c0432e421de87493896d2936e72cddf9905ec2392a508d69e11a75e0dca5ca047215504de09373cfec95216a2c12498f02be10f500605bc1893cabf0034e4402487c30857fbaebec2a4b975fba5ba1a5774d519f65803b6b0e207092c6c6beab86a2b38c7d77280f0d4b2f9c1e4c29c7b25222f0d3c088f9c77980918b82c3c6cc72225b8ccba0f56af2a22213c1a1e098c4558c92a078be8d6426b66fc603ca58848192c8626e87b9ac6af978d87ec84ce603c0f9a04e6864df7cf775796650d164be2f3ef3e5874f841916f81b82cde610d5f06dc032060d0e6ccec47e89e52e94c32be9cb0d5dde9bed80ffe3e6814ea1628bd470b1424ace2d760d639dceacaf5d84787030e66d19033bc2585744aca9c3219f40da5ca77015f25078d93376dff7e79f99851845e08144d1bd75e67b86472d7db0b13a2d10d70e9e95eea44e1ef8801ad6b8a1fd5ea8e0d7d41acbca35fd5172d0426aca11f22227d93ed45177d729ec3f20cce42043a1e6268a8f4bd686179290ec06ee8214c0b85f5fe0353423e5f6a68f96395e57455fe900f736da8bedf7300 -generate_ring_signature 25dd893cae76d6ac1932f11696af4d23ced5c7f8c9b26c443513e7460ec16ecd 5d9938750d6d01d3cae39ec888e440f33fec671feccd0a5bcb83a0bdfda37022 6 c29716ea74a2eee8270fcfaab52d6eb58c02084e6c0ee1efece22d8ff5ac9ea1 85179c988425643a848188ae78fbb0c39eb912d731fd789ba04016020e904dd7 5dbf0ab5804b5052e98871ef45eb1d79fc94177d6cfde3574f118c0418a84aca 3053056e01b44a45d9d4137e3fc8cf10b86865dfce0f9773fdbb459b0b305c41 e35ae435c1147ab31281c1a79c2d3a31678be7e87050d041c61aab1dca9b74e4 e451cf5197c665cf0ceb5a9d7b93d139051f56e11bc31ef258f56c9023b94d80 c53355e6c89359655240817ebebc88e2a7c039a53e718bd076dadbe8b166fa0b 4 d9f5266b90401909e533b6bb9a2dbcd9517f34c6d8bcf830fbc95698c2c0710a786cfc9a950d1ab2cb4885b3c948eba9fe36f1fe42091fb6f34abbc41c6ad5071a09761444050112cb6ccb9fcfef7c9dc7fbd4475e46a0a63005f166240d8c09c5b83cbd9e453fc71911f51ed37bb4896532cc3d53a6ac00c1bba7897330f005001d2117bc7a36c4f41a4b8c90f7cc5b32b425bfc1e4ce222df4eb71d73ed200465fd075f642b6fb1fbe431ed72d11007711038fa568281d82f90b4970f4900131f1bcfbea19ee8e20b0d6f827900775ea58af01191ebe7db53e48716561540263b7c513a5ae81f26ac31a337b86d383e272ebceef0764e89b6164299f4fe20d271549deea01834bcc0912aee097eff70f16d138a4f40ddd54b9085b6150b30b6596979bbf97c1db3e592efc328213060041bcd113a10b1522fce9524f34830070cc220f71982e10178bf0dea9bb1a80e8e363d8481bcd1b9d237a769ccd6807beef50c53be11df97b967e32f7954e08887adea0ed8c32cef99fefdbb5bf1007 -generate_ring_signature 2f1a0d247c8827dba4d4ce33f142f157e2f1767529fc7c19c789f00cf6ffd688 3374f89c3f99dad856ba5ce07e7c38dfeec5b312427bb33c8e1b8c372c6f61e8 15 41a94a5d8abcebc4c87b58bd560596b715bd33edb75aa248955b5e4602960ffe d30add0397722f8cd6238bbc36ef02a1351b0bfd5a462151a57cb156e477a909 8ab0a8764b7ce7c3c10a7e6d201732c6ff4fa5ccc9e2c6e25256db8f88c19467 c12136781dc3aa8a1dfe8fa588c768f3f5b73acda82626cb380ebc0e42a3edce 571d28f9270ca75dabb42b076a8f7febfdecdc966b0c106c4541cb6820380f2c c9e639cb8b24ec739970c8f7d87bea123690fd4f1edd394cac1fe570109db396 84321e4fe650871a21b17061c6581701f5acddb389d49e4cfb8a65bf37fadf7c cbbc7e4105af837cc47494fc5b5e78735a33190f1dce55e2657d2d538ae6fe95 d8d75ba6364a1ef06d0dcda3ab45bb2c245d7abd7dca3574a5f48c3f5c10f3cd 74effe798cf28847c01bf6276a0c24f5be7e6c52a3968b7df9b8545a692bd9d8 72740510584e7b51328029f4bc32a40c715d892cb00f70fe60f7912cd7b7ccfb 518813c1abe65ebdf8bfa96b139e171b30da00b3619fc258770bb316410d8488 bfe01ebd9d90edab131dffccbdb58cacd599d5f0a3fab24690c7e932f2f06835 9d8fa00ff4874b2f6cd130a224a2d2df214f27977224132d1d84a5ea13a85493 d780603fca86efa77fe4d7a9d35617b56e6b1f1cbae974bc5ca9ab4b37f94388 783590cb03f14936ac520b8b90b9a59ee656aa1a35a60774c23c0b120e0ad40a 12 4795468c5f610d05cade4a06010821bfca0b8e9dcf16bf9c69c51d6d89c1100c184f6b85e4a83b59275e963b26aba2ebe3b47ae682a18043eaf71d34cef8c10383c91ff64f199a422f471c9d82c8a0b15946d047c2b09fdb061da7d17ddec40ca7d70cc4ac563d3d9c1bb82b78081f49e574408d79efa00c89c589d7d09d1003e17e7144d1a7d0a47b51953698a34aee5c7e82cd9a2fb4824329f13fc52401010856cb7d009b399683444b19c01b5c2822880175a286471b7b3073c30c98f60d54b16afd9098d3c81bd703733ed3f660b11fdecf6e04adc7a4c61054373acb098a50d8fa28e392a45acf5886e9fd1ca5a55718bf375dae3744b3f0c1af778e0efd174dda8c174ae65c54bf11f5478b5d2b487251f03a97f960e33deac289670df962c2c7b34fd8c9b734a94f2024200f1386de23c23cca5fc416e2393d19550a3bd81744e8d58c08ec03d6bb8e016918b40d92d54011072e8e53bc69e4d2c602cbe593bc35f06cc7256d87aea75e48eecee9d5277a3f38e7154cbb530ba6360f88c225473ff147b3f5fdad3edf8647367b9ec35f577687fb3290cc171b70560cfc171eeb216fa4af87417ab49e842aa8f5ae11041142dc239aefafc7b2fbfc0867c5ea0f842eebd1339dbe9e0731805ffff8c3119a895b35efbe760a503cd5044d1efe829cf952b8bdeecb18496e0ab500e011d7a33fbc90d907b8699abd0005ca6ad6a9eadf74d402e34ae6daf7dc96556d3ef46ac11b9ea94b88204f80ad074e4c5374da9d10f1dfb5e12fb9bd559810c2a96b00484d0b69aaa098d6f2020334fa86d03b316c3ee47c92679bcf4dee94228ae4b52502792ec47f4af77c650a83421becd7269fb177ad26b8596d5ccb37a825d270c9b4838360d94dcd31f00567eed2d33df5614eaa0c6bc94e76cf9d608fd90523ab9164c85dcaf731965801dddd7ddf01b4d5f411e6c8ac484c464d06c28f06d0572695337935daab3ffc0e3dbdd30abb3b182b610768847ebc4b118a4f3d71e8edf474afec079409b7ee0cacc2dfb73400dfc39a9790a1b4d1ff6ff3eb961bafc8c8ce189ef8558f71fc06afb3401e3be6741c8c536355378797d52013031a092901917bf0eacd70d24307ed638249581ab6ee5490471dbbcf857818787fc7c8ff4365e3aa73b244993b08b4dd9cd47b97ed67628b4c2beef1d1fc6799eb4618bacdbfb6c1069c49749e0e337c761992e99221e83f1397ad42df28b35c9fd4c62278a01c44ad00bfe6b50834fe93efaab7fcaa3b8014e4d9b82f66c7a5013755790096889b6e90130d0d024c23c7eb8bc9999a3bb45464aac4f27a857b7a72f487c9c6bbc952ccf24aba0f -generate_ring_signature da547499dc76464e031e5ff7ee251b23e86a65e7ef4a702f49430dbc1a3224cb 33d203426218d7f5ba88cfe70ee0255b22f15cd23d1e7fe94e02fb4d7a835de4 1 86b01d9ccfb40a5020a4d522c02855924147e974f4909a1f15afed38ffdea8c5 55adae1b399bf6a36b775e369894601a26a5395e5092e93f1717e75dbf74f60c 0 56605a7e0eeb0789131bf1a8a46e034a040e7acf98cf30e9224db6fa7821ff021e650c9ce8b6edbe6e7897604f9e0b5673ff50aa491120d00d14c16a47967b06 -generate_ring_signature e7e69c7adb279f38f2e2f05051fd992a11149a8998d1369be08f3dc277fae761 85bbeb775e94cc97da84eb667dbc70a26e60a5276454ef41a97e0aabfb8c2e2f 56 18d09a4f9592883fcd32ff6593cec77b9f973837f03d16996ca2cc33651992c6 974af2b4ef03f64b3051a9a18ee09ebe5ee860ade199631490bbdf6d8de269af 0f9b58c9eb9bb3211deb53ef5b997c8aa68ebb9357ae4b827e6042248e07525d 582bcbfbd1c46085fc27008c081b8c3190d8295ba3ee29a28a811c8dc7ddf87b 46e691e35acd711297c564b251cb5b279d11936e67afe44f321a83e7b2362157 254a0add41690908429a4349aa13813cb4b18b08659017515816181971d80324 97f30a5f6836526a6cff058d570b292595e8b056f740b325334712e9c197b8e0 5848a77ebae6530b5cbb1d1f479109604dd9491182c4f4537f3d857b160f63fe fecb6d36c025378dd87880402a8ea775376954bad2bdd78ba5614c793c0f9c8c 172a5304befb1ceefbcc22bf34479c924f29afd264a3ac3b426cbef3df1e1903 7bdbe1fa6447623cc54d957d4870e2be682aabc434db57e31db09b1472774c85 394ef3d1104938454158def95ef99ce4614a373235912fad5dc6bea68e9d8b41 23e1141711a551670210262fa8a59271dce5a05939d906a5d3cd7ec5277c1da5 969dde51d1fac502a370b8bf4f40d13824fc919670d4671b402a294b6a89bb08 0509b587aa1566227473c0867249948630965f4f94a6b024dcc64faab4cd0e66 7e0462bb2cb462c7af329b6bcc6b9664a2643cd6e360cfd14bf7eb53544c999e 738ec5605320e83f6fd439853de054cf99d9b320916ca3c26c53b914e6aa490d c1613e126399c42bae1c4750d814dd533c406db959abdd275b3d9c3e72984c24 d588bbe6c2c2744068758a5d6e259d1cf02473af17a1368e62932688eb7764c1 26d840d38fa305151e8873b14a441f42c864d873ffd7aa2c7c278109a446bdb8 66ff84c63ae19639ccacc7b493892f8f26ceeb3dd6d8f063859449da9d5b13ff 828787793865161872024f6ba77dba847f0873be2562afd85a64bfa0f453adb7 1874b21b0a18b6b43318736c3b53d871bc491695f5d86c8811ad8b2aa8464648 8f1df6bc7f2a2ca7a7d00f475fa487409a0f4155d2bcb2db1e9b007cd9368b83 40b39d643696c1517209c946e7e797b4c1cee0b339464c9e8b13ae28d9434afe b3621ef2364d2a8a9f38aabb9b624b932300046569e9fb026d36c7e73a3e8fc4 3115f7a4b63d229c5182da0a3f624de6c7e2b50e13a7da14a322f0de69595418 7146996c40e7e9570671539d471235c7baef7387ba12f1fc06602fc973a3ffa8 ddae2cda3fa2d25bb4a412f4aedcdd206274bcdbae18a3d6b4c25d978f8f5dfa 124c68d03ba3fd815102f4ec1e16016e6335ed47d9637771c426c49dfd87b1b7 19a1d656e101999c3b1457e995d8a0977c18bed779deb21ef832e7795d4d9044 b69f73ce720530c3fa94f7f6f0b1c6c8e5466a89b97be80739187695970f6871 ffebe2f897378da204d5e8338caf629744fe910569166768481b76d0934e5e0d cfcdf78c7fe82f7fd14a6766d4cf608d6eb92af30bf11ce79348bb2422c1ae8e 7af0a5c4137717bee0db0faf10813515b700be3b04d639490226b047a32b660c 8e824733a807d4fdb63c7621b6838f70054b9a069c3e8b8e36c7b7a2fde41de4 b51e86c2ca33303c307c9d1080f5066ffd47b67dd1a1a368d4bfe5def1fdcad4 f271285616306d959f3ee158276b7c35364191836ceabae3d5f532d1c2f6cb0b 0634672a39f8f196a86e8afd3028b1ee852026e53b57ab5dbfb36024bb3801bb 84b77938a69db65b4f705507f9a052aea980fb5827062570470bf1674772cbf4 b3f1ae595027df180e6d1ec987e7d4faaa5832d72c759f6c4b33fbec5c197248 b3aa197ab9bc1411a0088271908462c745ad08ba0b6bc52b82300760d1edf3e2 764478333c006629e5be2ede6050b6a9e19f84bfcc9af0cb606bca2a7fd6ff57 42ede624cf6b0648b2219d84e2800f86295b45b2d62c400911936b00d23e8eec c6d188b2593f276c9d8af38786e8dc83fc3f4888357576e0ce1f817c6f4ed0e7 e54f8c15e52301951fae217b400db2f8a42aa824f182bd97e979d38cc6beac71 815eb621be2111a52233f936689d47fa18695b52431435cf969ea1521abfca97 c39db1d00d3dfb57a9e61d8921569686b0cbe20e69915cd65c9530c8bc7840c2 88841ef6d698114e8a73b56a437acaf9dff3fae7edbf85394ceea8d0a0abac7d a10006d29887038ae2678d8ccf83cc66f6e30cea14f3776dbf3b1064b572680f 867185b15807549d9248eec40382c0c2efdddd0b6c18a884ab811bf0ba074dcb e2408a5abedc5ca9c397b10df9eb1d4f327fd875932544769b9b6ba124131669 390f5c7be19c6f50c0645efd3d416b1c740d38d7f95fc6983f5297921026518c 5368d661c76b57e15f8e4cf676aa0c8179bad2cf815856a97ad48db1deca7db0 87494de9d4062f589bd263b6fc826dd85c6b81b66583d6abb6fb6cd5954dd296 a353830ac2b086e36537b57320da789652df3df9b6bbe6f963e9cd6941758aef a0d9ff166385807b7eb58ccbd283e08e6b2194e0dc82ef190b3dcb15bcb3160d 15 6d5be642649373a485d93b635e7b4b608e15ed664094ff04ffdbd9ef00277409df1cf044d760ed37391cb3a7d3dbe2332ca6b0d652ce2c5d1b35b95e9fb74d0f797f35c1916cb24c1ce85d73502dfd68a769b20ffda253e770fb27b5714bd60a8d31acf18cff3d7a2b763bd939b21bc332ba28c068b1760adc7e018a1c3ff70142d7e1178b7561605bef5a3b6a62f410cf385ea585da77d7e0cad50ecc867d0b032d81ae2bdf963829d776d6170ba2e38748a2fdd54af88fe026c0e71f3b400d556bb8959f4b6b87650c265bc69bd7ca9697b35193be51bef4a4ca254e642608961f2e99fcfecb08d694c4e5df1041a94516bb0472d37fe4c4bb33c934a03f02c80d96894faa8deb1a0a69dbed56586e82767f4f8a31490caa5da1ac2ab2ec07172aacefe8462da0db1b53aa3c8f16acee5b59ebaf2f2903d9bd119caf67a50ceb3b384daa28947e5e027a54a3cfb754da13b9ca1a774544f6bf8395b4ca2a0e969a54290577430ea00809519d78faed7555a4a92ffa7874390ee882a197f103fd4fb021a80d8cefdc9c1a790c99dbd68db603c68231298b55cc627514b0740099c4ca2b2cc40d33e34346dfa24fc0ce0a28c6c0deb63a3b6af5a591ae75b400d73c4c8ff622bd8b4337e809c4c9c90143d8a3d1fb69beba8cf049e7e55a28018ea4f895f41ce920aff00f6cad64091797d75ed9611bbd76f4c1edfc2fbb9109185901047ee6603e965c1afa0ec7d3fd32f76cd1955951a4587854aa579ad70759b1fc9504ead94a40d72f184812e48bfea4d48d07c045f27869a0e96a467a00729b9482b9a81006a19513a71ce69cfa5b85fb2b05e9f60050ac9963dd197c0ad71ab74b4d9fbbf1329ec64e12b3b9f3cb2af9fb341468267bdab35721c8ef02287721349eaf9dcfe74fe3b7461d396c69c69acfbd1e1f2beafb83b69d9b580ce8a7180c7fa6613da4d8c5964199f721fce9ae1cf81bf624cc830039df3cab0eed2b6b3f7302193417b0f6f69c537cbdf35aa5782d2f7ea2120f353d7737a40e60f37bc9e42b2295119a73b26a02ef305e19a95deb7b11cc9ff2816ba54afa04c1c02a86298dc2e635877ae4b89bafe609b4717a7055413316cd3b7d8e50e1093e5c399d6cef49fa008500881e08ca9ee88eed262e9f5bf8d23b1685e3017e027ea19ca4cf81dabcc6870da3dcea89314baf3097d841d264d70211358c519602d6d3dbed6fef64d0a54011a6a9a5a9ceb76f0d9342d12099c2ca3ce24c5a510f9b437400a55a0e64c66c8cca64674f96c7ffad87d228f1c25f111f8c8a6912006b192025c195651aa235b4dd768e647eb314efe335e682041e44b1667775c80cee245a64a7f7d3be3c120035aef4d2e6221c1cba57f87e940cdbfd4bf51bbe0676ca07d04739a081de9c87bd212eb87569cd67a1db6d9dde4717d59937149f0ff8a6e7da7c33eb9cf085916e64264310211232975e7cbb648aa375a4a8c9930d1fb29ab9bfb5fb6e4653d5889d8ca50fbaa8edc0cf9c6943403a5b755886b107af9beea17a8af1aa42cd45c0b573251727fe4c9028a74bf5b44e3c5e1322bf0b6388af808d35299a46a24f30c267bacc064731acfe5b84ee14d56064b9571c0ecbc6d2e047e40933cdc8f80cfcea96abea330cc502808795f54291a87ce1c10a7a626b83d533cdac92732035b66c614c73f488e349b221a9c2d9e947dc97af0b4a1207fc51203c1099ad7f45d28460bbc90d217fadd14ce8a62e794b6be28b02e57e34e51a30072523d9e8dfaf5df9b6e0db98096429a9dd9a865e9f0bbad4058ae95488e213cb0d9c3b01847514cf1fb3c8041baa1232c15d980fd59fb2cb0b55dd785d86ac352756241355b71307563dea63f95354ea6ada82d292582fba01fd9e3fa1157eea6d21f72fe6808dfd315137ceefa5348632dbdbe2a2c18ecf0d3411818f032acb787f3878e7973ec56a1f9d11eb3f80d12212b9c65df1090f0d0ce8e33979626891b94125d44bca4b4e1a819a393f3769cd35636a561972830e1bd067aac618529b3b740817f813310538b427934b1d8b5664f51bb81ef69006b756ec86a0d1974c468b81bd261a82aa7cf6c3e5dbff7e47dd800699ec44250e5e7e745345230d76757cfe212205102005e1eecfbbd174f2ff2817eba8272b016c1581a7de86d0674bc977bd2126d3881f73bca9e5baab80bfb9297441a8f4095803e8e9f9b130b5a1d3321a0125409458516b9ddc776284906e2d11978b450583228d5312c26f06407fed2530b4486f5c0d4f7fe69621d0a517d112400a2e03e56ffaac6f2591b36f2d9b8a81d1324dfc4a5ee127071879eed655fe2d142702dbaaac7ab12f8f9925a59907cd8e215c753a2def62c0edda08f4c8f38c07e205e141e359721a07a29e38ad6656fe976fcb16f5ac48d4dc54a4aca18549f6780788f92d6f37fa7d740cc28563592e4be3beb36e7a19d425b513f680294dcdd600bc027bc8b38023ace1e70df496d6a97222d206703a961ae58af861946ba5310fb9b9d93d1849ea15dd3d67965fa8e37c2406226566f36f7204b5018c8d6920000c37e799d501c0da22952a51409377b74a6cbc6a556dce192f036dd570234806fb5c747c12bb32789602d68b972299d430de3f924c309583a6edfb933199380d4681e5df110e82ff578c03dc2942285c882f36dbdcb1522d1adc05b65f5eb50d77aeeae6961a64c94f93c980a693b939a5a672da14f66abf068742b72f898c015e984d533ec98d21a270d368d1589ce066d630fc7e0ed088a98c9cb496ef3703e191f76973085f5a72963a765a2be72e6722a9e6ab3ea9d51e26278e37bd63058bafb4aa684d718c77d7f14e27128e60d9336d59079ca1feeefd1df6ec46b808030de2041d368cfb418241b792982df1479d07bca84fd1d626fa6c6f23605603a1a857aa4b4c552ec42e71e38bbd254f85dd9c1e3d62d99a5c1ce74861e9dc0c30ae397eef0e27c776d30d3ebc9dfdae013819905d7d7f20395159ff0095be07ddff48205ec3bfe1b0b645bd63b5e7d9963f60592164fad0ffcaa152a1634c09d275d541a0abed1810a30bf6025a231f62621661e0692a6526cc6dc007c7db05af9b4f6a1b83d9dde96d1ec97eb7fe8147c7bd6fad8d5a113806dba66d330e02b30078367c0cc49cbee6d0a8077f1458bc3280fe6f596554622c5b50e43a7d0d8bb02bbc9d2384038874a58a3dbd8af35905369a76ca1a84b73e12d0dd62d20e7fd20a9177089eadd274ed09ef2be773e216722f0c76119022ad83f885009001e46091a17abde7b3af9a74ff5350783153bbc488e0e6c2260e918e1fbb121407e29323952c59ddec35b7a760c4c89bfbe620171287a4aa6898c9a187b2827b041efded978dbf705f18f424780e789f2c74bf010d9457cbf89673c275cf79f70ff0b0793da151462b629842e81d8290fb17b419bca9dd6b356ceb415fb1e40f0efba14eacc9702368b688b0df12bf0229a95193f3aeb68727b9555d368306c30b9f4de41c5630cc09be6a34770814535f8b8b5b0530a2184216c62e64576d2c01d4fb7c0f7765fb29e1ab5a4a5e626285e9e8af0fa6ced707c3c36fe2ae14fa00e5b467e54bf7bbe2da3f6b37c714ed21c7927beaca338b08ae9094cca8d6f80cab3bde49b255a8851a0bf55024c9370d65224680a06af15ae28645fc4be38706894be22ec8b0859f7ba6883bd06c8ecec7e5d29a1f5b3d71f36e544b1cd7f500806e1dd4da697aeb779a8934ae6c83e59e81526a02321b790736c26b20002a082e60fac8edf157cb37ec8aa66a6658ce45459a1fb0c3299cff18c4120371340540b1216954dad34c5865277ea3c53e70d954acf265b57e8c2c0444f29a12ad0866cb496a4a114616bcf558d6f2165a2bde3fcb08e6f532761970ee9d99722c0dff77a3c3f10810c91c2f38069ea3aca2886a45af5a0ce80278c59bd46cb4260c410a215919f04b38d7112709e729e41fc1e139410b96017401135eab589e800d4e5d175b4ccb3f09144224390be942f3e8cf8f77c9a7bef263b90df2402fe60276d0dbe9f18c6c682869721e30e1fa326545983d216abdac8e5813ce81fecd02a889e6497833d270609dbb4318520a80a80f0b99a68d1526c3bc9f99371ce20ca85ef2c02a89c1eab25d6e5b60829d6344a031bf0fad62a670b7a53ac21fcc02ecee08fa66123a0be39535a89237570b3c8869f97fef1b2227443b13e586850a7f90e6453e2f7908fd432b8c2425c7818995c423c4ab7d92f88893b8388b5f06e89713115e636eb34c216813afd2027e1daa5821aced4c156f600a94d03d1b0ec0f7f3d44c59223e3d99ae164321dc475bbb9e27d2be9673c423f36e56504d0173f0e54eeeb4497cf84098028817e5c86b6f7b3b852358a65968a1d93ff468025b044eaa2648e1c5353cc3fa60de7aadf7b9e066bf65cfff7257209800241d0c0292974c4ab2f65c38d5ce61e976cebb1d20a894740be4d37a8b73dcd692130e7be0fc910f784bd2b823e55c3c3a0f577fcc0165cd54b632bcb0aa75cfbb4906771735ae1c81ca52aefcee14ad3926b2849ff7f3e1ecb7d31d117f3668a6f809bbce90d190ebeb9f5409b81482408cd107dc82ca96bf65d8f231721f2daa6006d66c66b2c1a0b35cc68a9de2b998595dc30595cb6a29366cbc5711b786c0fe08c47b44fb7dd3fc8799d9ffc7185e064c0e8f57a3e79b6cb0c1a76f6970feb807cccde40480637b909daf8982d5b1c0b661f0173948bfe8981a7837736f880004a4e9f5e70831fe78dedc73ad08e08497b7bbbd84496a821675f0718937f5fa0bf2eb93c5466fba6888abe3ef242149021a5ea5fcc18b8a4be8cf065d6dba4203d9bd771b46c1767256c7782a3c582f9dda2db7b358caa4fb8f593479ad77c808e3edcc61f64433b090450e0bc34212bc28eadcad8d4b8b34c9d42b071be4af01c9d831e88bd88d13c9fd56d0f97ada04e3672a8d21d464cbe916929ce401fa09ba83ee90dfebaffea9537c47ce6b1a7a0c4166b35cc10fbb5642362458416501 -generate_ring_signature 8d502bf01a32e567707adbea7c5b07946c8a91ed1231dabbb363bb6c475debe8 797d78be1bd8f1fa9f586e9710621bd3d480427b45588fdfe8b845b12414974e 1 bf227dd9be0cb17b4cafdeae98527a2bd5f22d56f4a4449af561a5c7e9979411 c1d63edbfe8e5ccc53d5c54f571718b81448166bc241c68f09aa1fc0f337d60e 0 55678fbecb38f36b01de860d0fdae27ffcdf030d4b252b87e18cbbd7111a3901be5451378335f8a4bb33f178b30bd2224f930d56d4f12fe6b7cdf7d4b4166803 -generate_ring_signature a360d6c579fda6e77d9d8f7c5670c5a3e56dd67f9bbeb9a2a51ef4fc846c9e4c efe52ab1e05aa48518f70282135dbffc35e61066c0ff77f69dca8f4f77cf26a7 2 5f4e6fe57d228860581b1597387794778018d9508ae293cacef275f3f380f949 7f855f04ad22ae8e65f0dd48b9140295e23165c1bdc44c2e9caac8f7a4d47690 742392bd543557e97a5c79270150105e0e05099b7db61d2f07f2a4ae1b0bf301 1 78aacede410e8f9d85721ffde4f07085507b6f54df9ae9a8861a043a1200e803f03b472e949701e039688103038a5a525aac1dae6540aa91ed4ce9c2024dcb0547a1ce7072e2d6dc7ae3b9696873d977f87eb7fd722956ff20bd539fc3169b0a09601304820fe4e691d1b50338281744ee1080a92a13ffb432c0ba8b8681e501 -generate_ring_signature f06ada276b4cb15bfe0baf1973b4159c4ae03306b3d1cb6624410d84ff75ed8c 33f1bb96ac7545b8d506cf2332fb5c1221d7833e8f1a58c7e72e7ea0c3a85f44 1 59ebef8302f5943bdd11879b6b4e3bcf098eb70a56e44871a3e6097b2a38f682 11e1c3f3d5d9187ead09f8679cb2172afc020766ce0de7ae29998b7af9895b01 0 fbdbc9c9747925ab3f1e2cbe191652533733de632f24038155d03e5ae585450d1bc1ee2f4990860677d2c9a3d39b4658fef54562ef05b331960239718359d908 -generate_ring_signature 4bf0339d9f8d053c5508af5620f121860191aefa336297ae61c07ab16e830e95 4b74cb953354a6d552dd2093fe6547018f2ee804b950c00ae2cd959d9edffa02 249 b273d48608383a643e8f4fc7a3218d79135807e1f01bafdfd131a6ebb066f53f 13a87abbe6ebc9c0a6a758e2bee754d629c0f0e3c9a8c922f2d6b84ed1d09563 41368a4bca9b547d015b4c88063bc4c61bcda812a31c5fa1380cb9917d91bfe2 7adc855900644fe40f6fcad939d2ef3d2ad64c78c67081eafd47aa014fbcf569 327c96fa149e38b3e54f28f3462c9e66df4c74a6ba7957eb9fc52a91fdaf7dad 421af8feeb282c03a8fb30a7837837641c153fbd43a8331acc28b1c4a9e86bf0 61281e80c633170d98119dfb0fdb35405f8ad76b0f0d867185ff43116abe96f4 2e25524e65bfc6c97f57a92766d15c394e46386ec49d2749df2d45bf52daf1b8 47ee6c2a1c29f513e09d89f74fa7b86a39fc4468cb648f74e0fbc1e48d8f6d98 09e04617608530d57ace72efad388ae1ae3947c0466c5b430b9dbf351f392867 ddbf7d3d8bf539aee1727a3eda3737f18ab63ffdf80651c5597e10fe6676ec56 135947fdaa00c8fef0bee2b1a8e843746094317d6feaca3c12b15d10154979e3 203b1c508ae96f42485d43de27fdb3075f3be201748e51ff0facd328399ff2e0 c336b25aa242b63ae73f67e1bf193f50fa8f40e59110b69e76db94ed57886d6f 8b285837b4f1690849e2b7766d9f530218aebb3e2ff9110a7b149378dba033d3 563db591fe4810c041d6fee4423947f244f35e819f0638b691b2b9fc77cad430 03e7898c213d50755f382dc62e36599c1d3dd87b6c9e0159472ed2f9f334c0c0 22e97507ef3b946b063e25d97f5212b6494d2696f9971ff5d54475ca9dcc1afa 229b32794f12ebb14c367f5f92290fd8190e14a65aeff3fc78aab3f8637b2f5c d6a8b2d32e3fd2fb5d1c71bf9fc51c0095033217feda93d973e49308f5694ee1 babc6af36f56c37f9d1cfe50d88c6e7a1403d9ca9cda788a01669a8ab6f050ca f07adec8757f7740eb2403d1a4b1c29bc591f5f90aa3c8cfa17035b07f0e6937 040390e90391a7eddab4f52925efa58dfaf935ae202267ec0f2aa535266dedff 84a82b340c5ba88f1359f113fbf717a945ccf2baa3b1561f347a70a83eb3727a ed06ddbbcfe612935bb53add5ef26d3e763e820f62d549af2713be22c6c6ee48 422ce5b6fb4e39f0846ce3a429a03fd2dc53df54f806b5ecf1dde1f2b004b074 0de9902ce29035edeec28692133417787524ed249b1cc1e2a1c000c2f35d563c 51c3352d40a920458a2e73a32198bfc93a72732d434b1138050ab8574cbab540 777b6aeae382f6d39fc2e613dd90a4228c2e8d69b3c56a84f250996a3ccc401a 2a324732d151575134c39e80275ea6aefaeb038346fe08314bcc90cda45b75fe 99a2da7d0817ebd800f5c2dc62649fc202068cae2692760a4b5bc17950249a05 3b8676f56b6358890bd11968a18e40f0fa56155aad8f89967a9aa37d8297b177 c0c464a4bd5d573e1bdffb58dad5697660e675a9e9e63858449b89b267271a7d e649468d35445f60819fe76f8780023b5a1ea23f116398ffeb4b4620c68e344a 05e7d38bebe36c388c980a9df464f018f48681ba297ba940cd46853b52300819 56f4fe13aead9eb250187d3f55bad323522841e0b5fed2257cf419692be899a4 acbfda9c0407409f6363b3718432409a2c56ead586ea6f8625aece0d48fbb29b 0bfa3d79778d063ad3675a485bbb8f8baca3296368409d81aa25ed3da324199a 800a29bcac3c088906ddba33ed1cd9e8ad0335d5075ddcfd28ded381179fd5a0 7dffeda14c5e22bd9cb1269ac3326a6a617b1bc277a77f5066aa2676dd6b9d40 8b786c7df9fb0e695678e9cfc79771bc84badd695e7285ac75bff9d6a07c7b8a 11bab52ae903cf6d7eea8a8f14da338ab52829ae7abbd6826e0c96cb1d8ce098 6e0219a5b11dfdc9ac7e6a8ebce4bac2b775a98a12697f8ce2afa792dce8553e a45f1f5479673fcea8ac0680f92089b115fc939bbf97094a430ab849ffea4dd9 e5aaf69d15c087793936a53d679544d2634ff9db1b81cd2266629c5b5d6048e7 bcfd31af3666aea09e3255e835fd931019b944af779af8e22428b713d942694a d320bc2b9ee9a777749dcb6c40e59a9eff375737cdc0c9e63539e89264e922b5 00910267ec94f2cef9ab012f1884518fac66acc140e82bf9684319d438792eb8 b13f3a4fe4429f8c34ca50fd9649193a1d9d42939280df3d40baf029c08fea85 79ddc3b230713949fac670ca6ee8442ab77d3987ad1855a856b20ff8c184fbd0 b9cf848cf9d1aae5f0d066e281b3eb0d447851c688123e4823dbf3bc5ff84929 3ff30f29ca0f5d56e3114205f032febe2c7ce3dcb778ed20d0fde85e42d44a69 49f1c860b9653e21a699f11dc10a11cde10d311512294622594c7fdce67fb8b8 8b18920f526da2a285481fd308428fc2a125c257155fe8b9c439e100c1f4299e c3b822f191fac8d362d2262efde806e5bb0a468290708fd31d6fa83890363a8e 114fc1213163e40d2f246906455a7edbd3070157b2089bbf0f23ebcd4576e7bb f39aaa33d3a77cfc664fab4560c47c230af7809cbff25f6806c59918296e95c4 c5a3eb0d203d502d830ffa7ac764c029b2890cd5c7c48aeffb793dda0e35f206 ecefa373bd1cba2ad4b04d5f2921218abf99f0825cf482c3540f94432cf01c45 3d4313281bc933e2bedbd7cad99fd60f1748256df79c08936d2162bde71fbeea 0ae2cc5b6d3b36b676cf08f4a9b4d754339dfa0d0ea5ce5abf5470b4726d09b5 6990d8de84993016435ca8f23aa18a7e72dae4d8d7e232994f7fa5657d34fcad 3cffa4c6b3aec0efe4e7e977e5f8a7294ee5c99dd5c0ddaaa3fe23351c941d85 a2d0b812fd1c7d191eeb61567298981e36b340c134c78da944f8a2203352e8cd f455055f5002146f5bbded6001d7ce6cd58e7e274caee50cdefba3613388a0da 477131a096bacef938be8fb54088a9004b29aba49f4579c33ca52deac5a8e5ab 67080c465a248f515e86b58bdbe91ff272cd23b08b2a7efe4d69f47e0ac85fdf bec5ac0c0b01a028ea0b29369e79e1c81eff46e9e69031ac3cac91aaf4db462d 2ccacdc26da82afaeeefb377043620a661bac86ff30ac720a847275c3b22ef41 6b20b96ecbbaaff46371c8c5094ee3af5250e9171c3207804bd0ce83c486b5c7 77d1c2e2a5b9048ad9c2200cd77495505c851fe1d0e4872f46de66b7af209ef1 fcd862e7cc429f954f422dd13ae8bb48dc029bbcf9b914244b7ef0a20102a155 3ede83a52d68d2fe4f8d6e721aeb809bfdcc0b38882d41e41dc2087c2b197f26 e38fa088d56b933da20b16848fe1310e4243513dd3c729f4032f74d47891ac8b ff44c21ca0af4dbc2e0dc012ee8e122de372f0d530db0d68cecac9f2a7c33b73 3b9903664c6ef51dc655f45f82de37b954dd70a37af1a45eccff40c492f797a3 1f70a93f7b6b459fa011b5e7bf421d5108430c443b1849f09e23aadbafdf2b22 b957c51631fe2c2dae71100ece8911fa410088a255dfc3521c0b1299d1ec89b2 b4c2e3669df39ada556b367b6cc9ad80b49d3ee765b54f288f31eaedf947aae9 bec54b546520ce1edf6def1e99bdd92fe35fc090f3d7ec249ea4ad7ddd998d5d c0105b38734b5a317dbeeca88fd34ea91633d4eaa90171724a420512a1769f48 34d20eccf3554bc5cfeab0839a5da3af1026be554b8006c91e495014f47a98a2 89b6ae04aa15388f5286393aec911d4ae16fa9e1c6cf991a8edc93ca27511c02 dd8ed17a2d4ed78da78446870808e79b46bd9302b686b72600575324c9a18e72 9127b0709d801ac54b8d550285a6a4e30a77f84a532eaab6c337a1bef97dd1cb aa0c38fd5fad2f1302e5983a9a09ae83edc730a32610a7dd102475624720588f 1b614f9407aa350b5cc5c52ccbc788e943a3f3d6e6a75e332103c7cb52503e2b 7bb824ad656dd32ad9c9ca1deddc41a3589f3fb9673ecb1621da283659139d26 8bb69321746fb816425ac5848e3d941decc629180993180572a2d673766d217f 17a6eb6175bd025118254836943f75898ff81a9cf26cd18202665bfd15c98e16 efa75e34a18284a2434c5b59d0c28891b998b2120a274cabc2d6757225c1b746 4866748e5ce204fef81c801a58339108a51e6d3fea7e5e2c879b22b358aee2e5 eadb7e4fa4a23a04f7eaff1faf8cd72b98215fe213ea9404398c3d942551249f bf44973880920ae02da88d1293672bddb4c5ca8d2c7d128cb244e480618cbef8 466f9c575f9eecb3f26664d593dff14960f4c68b3545a704b99594cec3f63060 b57feeda8456b15539b5d24c1aab860b97cdb498b629f81d57ec233e4111007e 4915b38ba83896e3f34bd08451300e26d5ef5d8d8af7518ac0592d19845b4464 d0524fd7c02630ab477ab113cbd21b5347063c933529fa01813d43a0d7d80d5b 27e89f490f81f8b21f9a08539970ea149b3a18196d54edae47105e2c21262498 a159d840849c5682a7fcc45335e329c3f4f65a0370bd835c70b2ebdc08d2eb83 b6876311d19355a2bcb0990f7b3fb98639eb5306af6624767cbde5013246cf8e c9351e40f566cd7e30de47c39154f598902d0d2a4f78eee4b37cc5412b2a6aab 1e30ceae50d00e8a0e87c28e46713011b4d911da0642468a47b95b98c33f4cdf 0c0f7add9e8f5c60117f50951765739424cf52757f48455162770e2642b44c90 e72c4aad3a76f21c7f40a8f7726b4f25c887fa11de547e5174e57d146462e764 49ae88514ddd003ef502e13b4ac56270888953a5802a5859961459a4c702cb31 acba3157ed52715ab3c0ce192c822fc4d317e84e360103c3cee3115c70a601d8 b05947d43cd215f923d48dc386c3f992351c5034c9112763bc2f89d9b4c323bc 0995423fc0a5997c95e07d3c4e16bade98d1885670ff48e59929c59c6c91fdfb 722c55efaea965818c6588b5b72f63a5b5036964ce66c662b0d01e85417e03fc bf2a2562576c9108ff535b04e023ade892ffe832c81deb4ab1cf69df767ac1fb bd6bfeb30c62acdf53af007c5fffe06ac6479fdc1582dfea2e5100dcda23950b 0c597284b35c159e09adee3c5f2b4383ea00488302714851b99f0fc6a774b7f1 86af1fe8506d70599b9f8f277f4a9467bff5f11c7838feeade171b6d62c109da 3ba725fc3c37b7c284b280ecbd3030d15b99008537d072da46609f1118322170 0185bccbc5d27113551e4eaf0dac7a724fc0c5bd8e03ab58891a5af79d0c4540 a03be24b919fc712ccef8e92f8402b32a5ac2e6ed435f13a01ac0adbe4cfff04 13a37f625c969172b64012f9c07ea8f87606f9a44bca086f77b72126dbae08b4 c370773fc793e240c04a6c6b44ce8c6bc06bbbf85cad228c299a0fe0a30ce63a d1c0b2fdcc1f83f9ff3808ed8246a6bcec7818eb8aa2f985a1cffb4bc71240bc 8df94b166ba97d7b5d3cbc0efac70565d5fd8f1bb7676765433122d3241476eb baed16c73411d8331650189ebe4b53fe9bf2237abe48579f14fd362fa1eca68d 968e2c6632c17127cae014ebf9c72942da77624a3c7e850b821a3b35cf3fc089 042d32521b49938a514044f1d65e69d2e74e3382f9044dc58d33d762f6a3483c 5f6ca5b45a1bac1ce6f04d42b6eef09ccdad558e1b3d5737fdd4c1947d57580c b8f960e7d188503d9c23b47eaac1ebb6ecf00558059a2062d3ff4916dabeb00e ac773dc9ce7620b6372078649fdb96ab0036c6524d2c24af9cb2054372306681 dcc1a2b1cdd61951167bcf580126f4d91854144f410ed394af5a29732c430935 399c3cff7cadc89baed9290842c4d689768b41dbd3402aee4751e4e6dc83cd83 cba3fddbbbde39fb6f01534b8049452ffe7e1837d28e466c0929c7726b5bb3d2 a525c1b382c198a32c710f6d1bbba6ed75ed85bad57ade17efe1deef3cbebfbe b10695a3567eb36d140a8c81b364477e724812c5d5cdd77631f756a678965fd6 100f2e875351cfa296de66052c4a83ae00c2f40bb97eadd1e7ea0068e524c568 65373bf6d75c43fd82031c030df1cbb1f2e54fe11dd9152d06f67784437a8c6b 10900f24d787db19021c7f6ffd05ccd2c1485bc9ad50bea77b5f7e2dd4d168b5 9206d8d7772a8042bd9b3ccae4d1e897dee041227269589a85225df73d51d8e9 e2196925ba83980352a4583ecfc2609194bea93e43702e1efab71c76eb99a77e 5555834ada3f35ac16555078158b1e129d347b2815d0cee6e17bb1db04fab190 9e411227937e25b05de329d88b29a3281e1471c26ee51636edd7b2bdb54532b5 cd812857f0c9592e899749df1d1ca06a4ec898e2d459fd463b87644ca1a80952 c336f88981b39cd2835bb54ff9d40dbaace66bb32005c266e4697d072fcc59fe 2f96e2125d594ad89999559a2d49f196ae3a075651d34351aa729147fbae4c21 d7f34bf026e0f3eb8f989d50f7fe2ca31e3e84f322bab7d956379b6ca67eb8c5 4ee15b8fecc0cd747a0813b82a98727e6557d35636210fa25046ba2ec63f6778 5c024d33681e72ce084e049ac978cdf05f2f690f9eb9c3c0a2f667e9feae4114 eca951b655f40be30b5139801f0eb195e6c1236dc1de5eb982a64ddc5f3e0ee0 b1ec1fe38059d22a2361a3698b04b3633f8c5e90f94e1b2e67c7b0a83d0ba135 249dc9cd75683ea99a1a6d78b941d25f54240f025218ca113c1d10e83e1e0844 395735c24e6403b5f9b3bf6cfec617ac4afaaa68e3861320c6ddc0c3dbba3e3b b88c05ad20d89d50304c08620f6b7f4ef80c50a95cc2877fecd2b929658de37c f4d13f28d1c3d0b5424b8ac845becb99c0c63fdf0d50c36a72a2b49bd64c4486 5eb59459759a26c15ede5971790bcf73fca4ef0bca2b2d2c712ce5d7551c7bbf f1eb0ea50f8c27d0914231446e3fadb7f0f4076b236bd76d15a2178043f15b1f 042c13be06da921788250ae0fe2169ccfd7b8f58258f6a03f0eeeb9ee4b056cb 3e8805215ac6b972a2007adbec893c526522a43ef8817e16dd09637946cdfe01 82f05de9344173995217794bc4d6a239f157038e41858fb49d73eefcc4ce979f 779060ea9dcf24e80010259572f453b743c818fe22d78f73c178f4f8e15f8340 8578dabb1c0d7d98f5e8cdcf2aead724a8fef51d24850eac7acc4ec67de107eb c8959add000fef8da12a32535810a4629c9f3005990500c472ca0c115a0f57aa bdbef5249d2f3fff40c7bbbf5293987ef976bdb15d410a62dbbc80f5066cfab3 82cf3f9a8c5131e29b1b1cbc9f3d48594fe30a5a0faf9f7b4d3518ff44905567 cb5a1a999801bf308a00933438df9adc4ec64b82a91cc819f46fdfa4dc63cd10 a2b5ff7c5043db55d1ba34b94dfbbda40c407393b2b5cdade2a01296d6a2d6cc cd9f89e414c961caf06f311ec8dd62f3941f116b42ee2220f6cf8730af87f225 94f778566f9ef6a3eb371752f76cf66523306c2ded5612d187210140dbe8ffca 1fee562d0e19adb7274d1bef2616122f37fcab620c31b259095758c361749193 2753541527a5f28886b6079e94031216be897533dc3aea2ecfad2d3b34c14a0d 74f9cf2e7284f89e175f7da2ef898edfb392ff850bd0469cec5b2999ea27e413 0822f78b4c9670073a76bbb206aeaa9f56d2d4c37115cbfef360375fc2bc498a e3bbbc6efaace48edb5dcce9a82bdf8b1a01ffef4e0ba272695ed17f31ee0ba6 9c249607b3ec14755909b1a4b96576864490c7c6cf077222f40e604089c2ab78 845830b099308bb31cf8d202681d2f42a709ef7a7ff29f25fcb340b8f6f1af21 f18514f95167651c539492ddcc5a154da18999f6fb5ca844c6661f113f36505d 1095e5095d519c92794caecc036b98052f8f8ab7dbac113cc144734cf6e732c0 4fc7a8f505c45e9cea40249551027ea08f7dbfc56f2cfc7dba28a31f8ed09695 faf4bd877cac477edefae6746b50518756026be3f58c3e283325f8b3c68546c9 ec3ad5ebc4dacec3ed641ec9aff310f11f6528b186175affc0dc3ac494280e90 6762ea15a154777659755f3e952190a37166ece974ab87c0f0ae41fd7e919646 6218aa86f87c50c1d29d74530fc73096cc5770e41c34bed1094ddc66835326cd 6c6e8e95cecfdbca5a6a3f81efcbbec3f6822c94abd6824b0b2ff04d8e005a28 42529960daf5e9fcf69f0378c7fab63dc04a440b590129d09190b43adb33877e 34da8e22d7ac3501057278a7976adcdfceb2f0946f9295163c42b9969f349de8 3567172889fa533c80d6345fc3bc669824b16091528a9ac6363a49555b3b1839 83d3c5a2dc852c1de0fdfddfe67bdabd232e7e786aeabc536e469cdc5a96100a fba85c323676f6f3d712a7fe174a74291ac8a7093279ca4770e2fb8b53292245 9f745cf4495ccdc23902bec531bfe44ee6608599bf528860fe615b5e64cf22cc ec2408378662a405bda9cacafda3bc482bf0ca4a5c3ecda11822ed66dd23ccf4 e6e2a769bb7bc694d5334699e807b312b31dcb84f12c098e9ebe730aa0aba197 86d5f8f8b1ceb931231e7955ac295f22b7dd5cbd5474c6f17ae9c67bde08b7a6 872a5aa1c00ab676ff697bc965082db4b84c9c5912b13cd73e238205b968f6c9 98e88fb15b4e7149d36a4d96369605702421a82536601f0d92a8a22a00196a74 b90582eb1cbf5d567bedb375902a66f452aedcc9fa4e610361b1994ce9756c9c 46f7eac37dadeb7d586f1f2154fb93224ffef25dbdcf5a50fbe2058fe4af4fa6 1f7515f2e3b6b0adc19d28b1733b596f408b3e01efd7a1c32d8a730687c83303 d9095d067e48593259474e88b1bc9d7c1b8f7c9fd6dad38541738577faf674a2 13b7e9dad168b21952ab494ae468f7eb3622eef3c93754dc85ea5076395b8d6f c53c69379620465b2ca2c0b55e292b41771066169b131d8ede8d958de335a124 f0f67b6cba0a5e26f0fb871f3102afe16c95dcfb3984d36f77158c55c2116055 83d6462bb79bd75ed33ac87b5a7792c49f6a7c6ad2fd9462846a7dab0bd5ccd4 e105fda7964432c084b06d5a4ec3ae8bfab30b85fad2ac5e12bc91f570f06c1f 7281cf80a74a1ec7881cb6173e8c82a9878a07ff0642d24408eeda7de48bf6c0 8841e7f99d72d4523a6434bf2f7654354f5d262a9f25d06dc5387cfb53201fa0 7221681239e07f9604d5e68a46965faf97af8c651600abf993177a340f6e88d3 113159560276fff26c42d020fea67f3d02536350cbd7b4ea36f76c70c300624f 14152448ea110c2a7f6efe4bf7173821cc7436646378634944164141e862c083 3f85f4b21ba09576235a74bf59ee99fc7690159934ac709639927a12598de4db 0ffb0791b151164266c3f61fee62371ad4b847681e39f11545fd7e79c8049a87 deab05a92357a34e87684d48570d216eb3a600a40ce746851bd80e33ec1c68e1 704537fe1c46b6175756a847e8a6c8a4eac68c3834e18f1aea4159e867b4afe8 9432b7a32bf0e82599f4c89b837400ad18bd09a29678d52f9ef0fe672e5db9e7 72915931e01acf984fb517b671b1c2a48bde0650bcc8dde11ecdc957deb2ee0b f0abb70c156aa44e30f7ecbe678f9ee9c0036c8912b737f31356714014a28026 93af6cfb7d315152248a3f5832795c53ccfef807bf889c9e08d7159ba5c7bbae 5cdcfbd85daddd4f1342a440b80663d03545d138fa31afadf45f8f97d6215145 bc67d0bbc9c1e729334eb1f625be3f76271f67075e080ff2101f608cfa6c9243 647f856b3e4a7f5b9f0edbc3c2086a14d97c4cfd940804e93e7fa38d4657fb8b b04035320466e030fe02365fc9835b0a13c75e171dfc1fcbcd058af05fe9a193 f804d2682dca7dcdf909155f817115f585dceb66b4cbcc513460e48dfe2e0ccb ca00d1eae1ad641fd240af58d71dd5d1b360773df738f76475f9ddce0743c053 99de32f96953e27f7fc7d6ace8082625adad8f875af5ccf98271211f6ffbf168 cb72c37155f98e712331a709f85b2b4cc137c3065c93b9a8750612e0bbc55797 ac942c8126db13a0a40d133df902ded357717d0cb8791d9c2b1b62586ec0e7c7 09e99ca05570543f80cbcf9c6381e19e8d5f21c00d96c9d04620219b842cb88b 6c754834e8f10b4f7c63b15b62bcd51232d514d1cb6542d44b84a84ce6d35c14 9cb2a9c1e1b3d50718d06faed604122745d6a0c4b7218428d2a40da6694b0b20 37fa13a595721d0ffeda06edcf2a9192aca945e8f845f39c850361060779bf8a f6a1fb231730587cff8331eafaffe84c036062f58c23313d0366d227f936dc0f c4352ee007fa39a2d837e554c96e3ea9c5f30bc131ee4bfee4691af41093e8f8 7888f2ce93dd67b62dbd401a649f2bb53030cdaf63958062a3afc269c65aa1ae d4beedaaab33b1ca7a62d8e3922a9066cc57cda6f5f53a1821c4b93663acf2a0 86c01b16025f966d766445addbe03d51be961d39f02611b1be6d52ce2e65ae03 61f2347d273c611883aeb9d63b502cfbb03935e4dc57815f89b2d960ca5f852d 88de1640f8120b16fe3cccfc6cbafdc3b2cad4aad54cbbddce71dca7e5a93d56 234e16921e47d84dca0ecde65723ffac02962de45664eaeb48f886f502f127f9 a7cc083d50070f1f751506a25c920ced9af70758dcbc609018ea29d2bbd92378 2114d7509873f58a86c60bfc37cfdcfd9a9b3d6bcd9adb7ea2f872ebf105362a cd8022df16cfa6aaa2e4ba5a2a387ea45695489724e472b48af3002557a76ab6 8deef1f124f6471ba33742a17098f6bb74c0d78a02ef47d303d616e393eb4519 48b56089969f9016e90b1a0aa3bab624a76ffd2d5bf7f8938269b114591141b2 9f18576b44e23d3c53213b7b7e4ac62a106282fa4561b3909c2e8576dedf414e 6839dda8b939921fce2234d2de861429ef0e896e8bf53734c49a7d9f9987f2b2 b52f6b08edcdfe3b6c7913e9da807d82e25a549e36202b8a8950f0a14fbc58d3 558d9906fe2fe216003200d96b368d4277f950cba6ec1949be6e42f0bbf522fa f369d7d155899f1fb4351c2a580c78ba591f7389ade0a8be48104a149dba7f13 06ff79895fbdc3db1d298456ae015ed3b7d61ebe3c69df56878fa9d6bd4522de 6e90d3ac10d5efe134def9d541f0d840c09ad7cdbc80e7ae326c132abdc919f5 b51705d06714d270bc2acfce33937dd15cb0329952c899d6f4304c7476adacca eea364ab4655adc1d2af1dac26b24fd8a6554abd57925dd4bbed807d6c40dc3a f815a1d1b99c386ede5a04765bfddcd83bdcf4d3c43cbe2eb768e9064bca8bfe 542eec4353c7aee91d9415ba2fd07311a3936640f91f1b061f1a1fb62d0fdd0c 127 f6f5272c51d08e7f2454fbb8213aa3aad68414f224bdcd1e0fa238ca82565a0737db268e942b8221d43f6d5ad5f751e01d3864fe74c79fe973a6dae825862c07cc550f75dbea9f1044f346c20b727c1e2e31dca662203da8878d752edbf2d30d84a121f2d7cb38553a2b6d9ebaed085c06a89e2603eb0cf7e73073dfeaf9100f578b6615edef6ad73157a48c635c1ca974cc0ccdfef58df188ca036bc7064209af61c971ed8c191256bf0563412f83045919d7829a689bd515f113dd76f4ed0bc231f97abecdfa358f1b2c2aa38cdf661413f4127dcd6312b36840c4b4db16035002fc9e92a5bab000d78aeddbc23ea79b04044c457266f908770d3a1190ae0be9d330cc701054c5e829810763157646421e9600c96030ae9ba1b65c6cec7a00ae260fa5592851022883dd31d7bb527512e57b7978c10752c90dd5b9ca84910c4599655f20925c01dbe73d7824b8d96cf59b0813ec0f2d17535365a4f125cd00df621e6bc3bbf50c57c7ac08c48874c0c6d5b96ab3b24f22c025377260e64b0cb6b85c55995d1692049a9fb4b85a20befa6285ba0005b6f272f7ea52488c9002c2c99f17fa41261bf5eef9e0378abde20a562d9c9ed0dc3047e947337550ea0112949780af154b550bf663372363cd9611b61c0ea6138b4e6fce02216bcee503839b440d9e199636a516ecfdf0a3bb61ae40cac3b8f965a2708fd5cce9446b09293285edafdba94dcd7630e06bd7f15d65b01d9e9cf6a9202cd12a96db6c830317f950ab055d9eec8caf96533802508c07a4e89ab64b73bea2c83d8628906a085682584a09356cec579d85c62b891431532fc920aaa713474df0f992c9464f0b9355e2f41a7ee0e26fd7c836012d1c6cbbe5b4cd4da0cf33e4040fc2f438b2016912743580a0d2ab9f6b86e0558b356778aa2f08f9043f735ea37b826ddfe2029484ca684535826a460fcc3b3ee3b698cf08c8ebc7f8e5ea773414b0895f830bccc2633a7ff3f73350d99ecda55303ae712c9942a9c1c6153e84052077e7e40b9c8f247d4c2b84a6dfc34ecd70975354802f8cfcdbd528122b210f2c4e15830bfd42c45dc9a3db6446ee242ba0208eda031fa6fa028d3b56cc37b14e2110880f16fdbf0d85a4fd50e26a0ca5eedeae8df2e88b3c7699d57b9daf18b1946f1a00061a75b9bcd55c5d04fbee6353f0e002aa24321779cc9c05e9e2a305be81e00dac9f8dfa9b1300eab133603176e4b97acaba4958fa18a1661cdf97382cf46204e5b6f454f5bdfbbad171049b065dd9b8181b06301b1dcf9fe37badeb9b238b0756052a6d5c160caeac8a0ecc84e0958e4d9569cbb2e7bf0189b4f99651939a06d30accbe9e34c87019d24e6621fdce5fa06196b6151b584054d370c0ccf5c107edba400f99f70873468f72a31056a8be38c4562825402bc674af096bc5670306137b0470e771b7a1b3e5e7c98dd10d99d93ae87f2379abc9a485568b4827b200e992660b0aba8ba239080c46c9f81c4abffe8e9851812c9397f4cb7baced450f2752189331d2926594927b159436b12d8aaa120673c1e4719ec533757251e90b07e0964caad10de6618cd27c815535d12d900402f00f766b6c7ec4c8df8bf60db4b5fbbcf8dc387a6828e1fd6bedb3f577fafaacca60ae124e8eb8d199027a0d2fd53964fe753c0b22c02aaf67f7050330480ec675e9e7d8704cbceca66e9d0d87e6582b7ac5b8f2a72d497eeced7e441b9db7a2bec6fbdd32a8154337bfda0482b0c935ad4fe720bacfa874af01f4375441eea1d2aadb2a666d90ca4dadf20b6108bfe7d794d5fad99bbc8c96fc58c44b7a597584fff96ef9ca2a31191cf102e432f92a20e47c898fb778dba7fd5eca605d431167d1a8ec67a8a07ef061cf0a0ba1adca9bfbce010d94fcc84e3380ba5e91b1389e0fd0577d178f2bef9fdb0efe5fa661963ec5f75d709d829c23029fcad65d229ac575d690263b7d708fc50a129a04a8ef845885cb390c1240b4b5a9f91bb5e7bb55071297c9a1fc9261b0080ead26f8188ac0c4ebe7813ab5cfe0579940a8fc5270d06bd090ac40ba663c09e43bb9bed41831d2642ef2478dd4140123418749b2553d990106417e5ad593051b2b21c2710fc3cb572ea10401c1b3b7cecd75aa859f4f3c81ab25e318f7f8086bcd6b76f3c532a83d67c72ab6fa27d5c583e6e90add31689a97ff7020f1f406961f23167a25460e7f59f67f039b2ff65fe687bfa4833211099d37ab8907fc0cc0d6a2317fea72da832ecea55897f8cae9fcf9f53381790ed7eb0703eaffc207507d1240cbcd8614317f6cf3ee5d8ac6365da5816685a6b2913a92bdd2b99b0036ca88f66f1977e7fdf35d0becca6e21c069273f38b269a510b65d06959e720865db712649429c3dcc0344b6cae3ec645e0592d211eb213ffdebb8a15c0f8f09278c8c2b6e0df86bf0d2adecd7cbbaef29a043d0d4273f499126c3657ac397041f492383154eb1018a44b6b91731a710dde36a94c31eed10d1f2271293e2ca05a58d0b258cf7663baa635001224517fe0b2181961d0d988c3c352db63d4e1a0fd50468edf239b6052c62fcce17fc6fa7dfca201e09423b332d82db8dd199540e8bb6c8aa9f0be4358beffcf6cd87b0e7ef1a33afcd617e5d1b94cdff69834c0695be85136b5d49c063ac2b09b06abf03a45698e0125ab938cc2448f1e1118d028201d602f8294ea1a94682fcc8e86d9a20e908fd75226c7d3ca761c78f9c90068b16e334b0530ef5fc980c8ce52fd8126f1964f9fb40a3cc1f84aded8080740573cb4d3fec577be13253d7e790de0c57190774244348d28bce8bb5f395f10d0507a2189ed7c500d5b8fd26a16a5ddc5f06678df168a5549f5b3be5898f24a50ed94e8c841aca1fb02f2dbaa74d07518f170dfa67a1c364d3e99d193ce2ae120bbdc028442a3d83d92813fc8364ad96216ce029474aa35e1b4f47744b7af9af06eecd6547628b2737906c10de34cdd61de2b3f30dc1c6179bd4dd6dbc97c1660f156b932010ba9be9606ed98d63fbf97461799fd1771208645bc384b992b0ce0378e9ecd4d6fad69fdd36d1aa789b60917b46bfbe95fa388d25657ab912717c0d9caabf5a2010c0e0d31048e89fdb72c02556922a04bfe49d568d360985da360576e4f78bf949ad04f13df9fb3060437d7c39f12aa7e373960ac89511eb49470ef5d9dba36cac787ac18ad82d6ef17e2342661122d93887078f054fd0d1ec580a7efad90580186b319445d40695985d9ceba28f6d2cf24ca84caec98ef7a08a0d3f853f7f8409bb2b75f573266df603e76c3bce920245d1a48d7ef3d103136f0b3001e074374100ed373ddc5a32b52fb9cebe98cc173fb3c26b9bcaa9e1df880e529b1b337599d9243ec0f1ec8dce857cba0d8d4c9ae0b0a2be67d57a948114037fd6b02b7c1cd4a7f48bb58158e959efe95608f06ba39ac5c26b81dac2799108e907f0ad19176d2b92ae91b6bb765ee25edbca1ed61b722350fd394cc518040b8af75d139ce28cc739076e52f6045ca8b6d956e5d054fdcf443eb747bdec6d039f36063f87d29e3b29c593d5072b63d5677874ea22b5127c7c4495a85060550eeec8563ce78a8182a190e23a2d17f1147b29a87464a0ece325629b0de7c8b60db316c071da14a4f261391b6887974ce0395f9a5507cbcedc8145197b21053c0b70b006aec59af5ce8a7f67a8e6290987a345287fe1c095755c69172a899a0e08c10fcde691aeb320caec3dfeb291bc3d893a77a6a59b1df837f71dbad054630ac6d5a3b7f8da3200bed089078d78a29db62e66325d737f0076c0aa724bec250610b4eed9f6152be083d56fc480b5d10166ec6a769e692b0cd0bd1ced906368025fd4bd1c4f24c8cc6d2d7668bd5e33e858b71318c7d9d78596e753d536c9060619f2d34b3237a75eafca86b9de47d830063a981159cf8d9ddc927a7b7a21fe0a68d25467b6e3dbd08c7923e8b431e1149c09237ff983656a8dcf78926e214e00eba584507a4321eba6fcfeb88da46a100cb6b39235e7ea4f1f4b6c4b7738cc0356eaaf013735b2fd615cf3c741790e9132617c4a5f01e7359705bc875489000a5e39248f8b8923f8799e90ca6cc942b94bbd83c43941d0b10ede9b6ce0c97700ce736a399acaba6e86bc2efb5bae695bab4f63d5bd55af52360bb02c4a10fc0564047a8cffb4e4b9b3c939d7331b4f17ca5071b24851c1bcd42ada4785b52e0a1ae3954fb75862cf31db7df00265c0bc85426c21e9f5996a789d45f56e35c304fe5063a297ac690bb1d529e6fd4ad9caca5df5ed74b1a82ccc7655c6c5cf1305ef906cf807693044a7fa18a2f432286693d40577757dafbe9665f8b5fa69200774c2492237e705316331f5a624d3400e4e7971dda2c5d4cf9f112f67753bc90cf86f09d26b1a9ece4fee6c6005e1b6d9be8efd92c43824f470db90e94763af0cc8aeaa0601a4f4b7f286940235bbb5eecb60796dfafe1f58a53f566ed4c1df04bff2e17e3dd053a89325943da3685cc01cd0671b81d7890ade28ee591e98240c3c97bc5ddcf320be785b8f3e8f5bbfba6bfaafb65b5f4973f7ac763b80f1c80d76752408a19982d742492daf4dfa1079323309a405685c25c8bf0db264e72a02dbbf683f785744fb9d49e3a0b0f3bc4abd79e8da34788c7c4c9a0d75cd09ad0b308c3f9e869924a1ea21b4e0404d322ec6a0eabaa4654da8d1d72375ba34010c0cf4b230625c002cd48db7258ae08898c00de27a6db101e08995e608a6b7830d5b1e0d60f8089cf0c6056a0ec6d53d56461b681b0af3b0073a6771380b92710a7defd12fbf2a821b13985988458af7e6848b21af018ee3003345afe072bf0a0c685b4bda91a0bfaca848b854ee91e22f107ab30e9af064f0326c92bdc8b1c1025cca3bcb692866578f69f498c130710274d64da47d0320fa8bb8088a15951f0ca0a46abece0a151ef2996c3f359f9e781151e60820c9215d45bd29bcfc188402ce1f960541d4967d04f3fefaf06a0fadd21d8622a36896daed78cde05ca9a10ed44dcc73301c8410bd83ee68add1407f268c44d501aba7039e03b12673245306ef7bfb910d3f7c53bbfee36fd8850964ff419db6b144d99b740638640468de0383a1e8b75d666f943b5f640a5ae0443442a76ae0daff5ab06dff374577edd00469382c476ad38c8248966f702284ff6357a320c17ab5147dd654ded11eb2020f51aaaa2afcb5be03a90fa799e53d94d2768b3dad2bfe3d3d020895dd08869903d78e554bea74e08ac15f095d189b72b2467b365c76ddfcc8ac84855902b5fb050837eb69cc78ba404b020425aabe290d683d1adaa1fbabadca63a44f69bea90e42f4f9f7809f1aa1ddd76906f9f1da1e9a21dd5caab197ff0c048cca1aec290db5b1f1fd3cb20ef4bff26b417de9a754352af14ab44ffcfa4b8541c4beac6e089ea0beedbec2fdd8115ef3b9adf9388e35cdf597196d86bc0679fab663b7c00728ab5373b5b822215c6a8ab23249c9e863b7c72ec29c1b1cf9fc964c5f2ce605b65062a1d546660eb74455a4584959cd66eac8ddd39af96d273f1f21b0a6c808cb9a5c0b9482b248fa98a04f546cf0f5e177125d137ae4b223ed40531fe13d0f7c265c4f1494ce3ea449faf5f416af4564354eb23d2c5ce0fabaeb2244b0420ce1c19c752b4a14f531574c851e4002fa20bbb57405ff19fbe48edcfb2eea6d08a6169e3ed737fc2739d15d99fd722bb76f6ccd49a6de5d0a2cb8833f5288820898a7358f4165f5fae0372f4c857e6cffe477e2bfd42f54848e9196f18dc9fe0c6dd1480014dd1cfd1edc84b2c7ab3df7d7b77e66d8346ed9f1d701ea8b66350ff919a35ec6bed8324124a21bb567369f979deee7984ad81a9d4abb931d1c590c44e058e154508a6f7e5d8bc2d47657c0863f8f00faba53fac0f328dbe6764904f7171500dc91630d7691408f36af7a36c521a39ae7db3e9ededbb9eccffa9f0201a6d22698036497a9bd9af50a2907dbb9fb5b74d9bbd3676d2f74f06bb6500bfbf9b3113b9a1a660813d7f4ab733d6270dd78ba376c4fb81281386c1616d3011ee894d15f41b0df104ab39f9e081e8a66428f9fcdab1b747db91b18353c6d0c7ad4c4be69155d31afea6b5ffa1d5c07544722602546ab26f357eb501f5f8e0293cd216f24a61e600d29a8447aaa426c3cbb8e23cff63b72c95cebe1601dfa055d81e57dfcf7ee3336565992e8c9007ee5adb57dfe6fa14791c68d8539144b06369dfeb7fefee359122e8640668cf3781f13c32f1e2818c9b88954f4a3e0df0f5cd9a1c909e2e15c52673808680a917bf9a8eb6c5ae4fec927661c1d2f63080b4fe29b8e1cd3c872edecc42229a8429b512844c3eb2a2293f1bebc87900a9b0c43cb43e52b1b94e1d2ad05770c89d56c7253b596bd6fa9fd678fe372c4b38a09a530040ed0cb575afec3ec1312eb467af7e7f12638f5355aa41f2f5490346d0db5615192b833523409103857a662cd4fb09549a927ace386e4b56829d937410b49dfe90e1b335f3e94df0d279fd8e9ea05918e9e47c06e057ec5d2abaf6b1a0355b7718580bf466c2f1638f9f1ae6180d3de3158dca3f0a3d1db2a9238d9700bb961ddb1ca3c094279a872e0b1dbb54864b56a3bc644c6d1a73def1c966236023babba52a55dc7fe4e20a7bd2f02734ae38c6d63074327406eb0c2732b6ac900145d11743d7c53c466dca4fb94f539d917375f9faa8627a88cf9e77d8a51e90d33da02857e8da7f02394c3a12c6056fab7f1a1ed17b26b3f08f47cef6b58e60bf5c2942e27fdeeb2aad46837efc1042dcf14c39c93156395bdd9c4028f26ed05baa074e27ae32471222d38455de395c75af840f30085db30f330bdd4b8b1960ad257c1847a5a1a184a2f3afcd3e501945516c030a8b16d37d9391cfc550af80208c7d821db20b25a5deb2a430ddffb80a4ea65a63a738d01c363beb18769760949863e1271c39d812e1067384c50a2fd66eb6491694a81045746bffb1615cb01c77761e32963cf54330714d55524a498832f338065f8a6421b9722cf05e1270a28e2a6c54c3aeeab40ce7177e1d398697c192c3774c9ec8e3593f6bee067130c4a366e51e325360fdf41709f5b6e75a26ecd4349409871af244f9f208b89680f7bf91af4becf97ec1c1a049e5e0bccc380007b4679986dc232b62b85ecd6f105c71450705fa92e032f1ac7b49f27e1be078dc039f5e678fe2731195a20f27307e4303b096fe9772d397c20fc3f19c1cd148609701ad90a6c567bf3bdc507a109b6e45d36ab4c78f27b29a84274d0d52e2202427b5863fdd958c723c51057f20288b0b8a93cf95cf10841197cec5db2f2491535e72026325c23dfd14f6ed8ef0387a12ba95c642a3d198d8ebbbf0eb1de63d4b481d721de518769d0eb79d90809b4844ba44c3317e623018efdeae26e261d0c9e739a5c266a0e8f4eab15593100d577e005c6095f0ba18b2da8d5617d2853bf9f2afed1cf9d5a4481eab86e940de066f2ed877ca8938eb88804de011c14d48d740d47efd1907120d01179a362026f7bf35a3bdb64aed04d2ac4779a0faba8e9f705c5200135fc1ed93924db780295b3a2d9ba83ff76ede97eecef7c8df3e2d2b6808508628dd07b02e3d95cab0fa694555f36c424d754a16e0e4c6b9418e6a4a1bf37848b3ebbc2586397faee09a1b7aff4fe9567009a24209f21079c2b28d96c3a11cf47e45c29b7d98b01780a947ad2e2d43a2fd2b1b8d4e6672af2565864074a06c5580152da38cbf13ce50183239eafda1e75efc406b1830b3daca498e1ee2b9d4a0b298c2f1fadec1f08099a45a7f6637f40f413842f9c7e97d0ae25c59ae9e67e7330d1b4732e01072503f2ee1fef93160b5f3555f5c79c87e9407235198dcdbb378eae108c3e778728012b74cdbd81178ec8ca4dcc907729da68aa45bd89021760143eb74a2303afdb0d85228fda3f71d7317d801e3c36d381de8a5449c58e91c8e74b4a40f8dbc34f0932e36e529bfca9dc7d900b68fdddc8ebca8540ba98af4606a54dfd63442b900760bb60f37d4502e20b777195aa3b9ac6367fcbd24517d68c635067ee72c7a004222ee374cb9cce3dfae04908c9a831938587d62afc3fc3bedd7ebd4d1985900b2ee7742c9759db20e5103a408003251ba743b32a609b6e4d26c4c3539329b90837aac13d5882d3f1cddb23ff159da0324b5bba80584955e25cf74743cf0358029e5173c4abf7fcb45b3f3474ad5fae005277830bb350cc2ac3ff23022f9db500761a4d7c865dd3879e70f9b95c30d33dd3f5eae15c6438ffea0565c042b61403238839bbbad45acac67c7b2ff31639c8fd940ac5f82e2f924130671035412503ba250c182f0757b2f03ef3c563de668f216b3b47e5ad0b338f0734351ccf9e019326de888ddd9e40ae7a9cf16be264a44dbeb550611d638503dbfd367c6dce000835fa7b54ff314f6c7d5ea3c86dbb01f8e600bef4d212c915e39ff84bd46402ed8f11207a4dcaedac8302a3398fe5c49ecafb8f16d69aa87074694b7ffd7004d0a0b05e0fba91ba7eab1dca90b2e504d4c3716b92a3a5792344eeaf7c67800f88ef4bdc37ec0b8bd89f13f8a0d823f2a9eeea9857bf5a58e5314d7dbf1e3e08676c80988456341c0475358d4d0606ad1d65cb5ef340367d9ee055788873500eb5662cb7c47ac9fabfe1c28d71f1a8daf2390d71959db7fd31adbc6b1c6b2606b6d6114d7ff9f33dd8734f1860276d98d43bfac59171a1a4328efe5724f12106a104982e892c3b6859673a1aa0717a51f8932122f1623f2aa8cf3af9e8ad3d0bbfcf2169589266f138171dc5fd4006f8d26789b89e77b8311452c0d08a994804d292e4cd9d0c82350091a198ec34ac08e6a290d1a5a01530b79ecb2095b99f05fab30c12837c0ef97938c7b9c9f6c83495a6fa452ad45eb308baf37c11558303607ddf4471169bd91b3687c71dab024ecb88d48176932c2e840928e2f2017b07508af8d56189eb156493e63c4384cdb6a8a1ec78043d517c43b8d06c32c5ff02407f6117a8c102ef670b616d068dcf557c66b04c140377ec3ba5a9d1cd11230f3d8e6bf14eef37e3d2adc17e3e8ee8c3ebd374f6f3c64fe6d90dfb2c7eccba06a2177eeac7d16641ab8bee79eb9d506f203f643e348c2beb3ce4943d28bf4a0c4e9a67536e8ce38ecfe6a76a24d72f9ff5a7a7bb9dedc8d70caabbda5f16ef04e792b848696797201fa1cde6519d9eae1ad056911566c62b40bafc0e50c0020ec0c8d282593c702f2d09aaa782a341034194906ef0529e48a31c25a024c49e0c96ce4fadc96bc72e08202636ca3de7cc9eadcccb1e37c3f9f6b19b2df3cb8608e4c9ac778cbfc638983a79973f17168421450890221a725927812146afe57e0f346e84a38f3634549088048dcd645389fc3802e3386cfb5b3034cdf9904e1f0e567bd2df52d7ba5580dc6b99a738f3d64979055e4f13915ab582908c12a9e60a1a60f042bca9518c9c804bab036e5b45993a1bf1cf8ce5f03551892ab398460d437e72c8f301bcd9e9a61159b6a5e0bafa7d46c6c908c23a3902fc1eb05cc103ad80b7306fd55d753631589887742ee5611a147ba5b4e01a671102e12c895309097e045a6f80d7fc456d501e6d85d6e8b0efc4a31f03346d844467a6f042e30bab627e91cfca7aba0f2adefff8916f4608f16c28d9b8448d422041129832cd01c163bb381f696b0504e1d5a895443da2a606521b443934c44e64d07eb33eb00a1fe0d0fb1f79291639fe4bfe4101eceeaaf4f32e6a241256a8f843c97c14ba07ad64446868655f4fb78873f81e388c04286277f9ab1c620966b4ff2cdc7acd0ed8ed072e45b7fd48aef7f7569aa8311a19721b2793db09cd4799afaca643bd05da67356950a26836259c009420d81518a54f7debd7b1bf1570e14f11473c6a0bab9495700c5a4aa68eae805c57482a990e9657aa169d1973b9140b54632d2c0ef4d08934c09dd9a2604435a165ed47a27b9422951d0fac8cf9691ed3643f460be1fa096b6cf3439d46074891329cbe5ddd1a8748448873ea1e705b3ee720b50613011f3ff0d7bdb41ac162db156e8ae50f991dd10c9cab055d6aeb5eba58830730c767e0574887d79e58df086e2fed39b9538a8b8128bf57df57a78c6b971200f6a776b5be33ac2bc9242b4a65d4f5fa249e2b788ed808990cf010d42459ea06038df864091398619b2600d79aafd88aa3e99f40bbb1cce8ba2b305c50680e0d5ceede4cb5fea7c327ffcc7a9a6971bb6242831a2281b401b17933318cf5fc0ce4085772d797bd0d5c67b0db3d436aadbcfe4341908ba1b663a3b7b3c0a16c0665f49a03679da9909d84732a7b481af771fb1f3a172425b189e58ffd898a1f004c277bbeea7b0886f13bed84cf27a497367be6d4406e7939e3ab878d6cc96600d520ba28362821c7e61161ab8bbb90f770d0d4a08be8b8bab1dd68219201600c7315a3bee80212e24b16d8ca0ec6472de07726ee5796947dc0987186ff4b910751cdddfe4bf97da4767ba5cc098c606c7553c2928ae06d8e7fc714b3dc90b2009d1579e5253688b050fd10e0643f8e21b4390c0b7dc94a75ad95d727f417ae02030fabf2b30d59bd0f2c3bd1debf741d6287e19a989e6ccfb7d677c31d7d2e0c6d49703f63aac66123dd81ebba7bfa2558c46fe3cea4f97920f7079ff43ec30a3a8985904607379f864d700668e0855000d03e95324eebed56753d9d575e7006eb15fbbb4369263916869299b96a665684b99b43fd18d85b0e1642018c00e90be38a4b08893bc4edad8145701a23c922586ae398012fb4dde8d1176d1043050ad25f5784763cd210e58303641b9d42bb988ed9e738a228781da76105afb0ea0be9f31104bc65f28847d0fb25db9171a5f24c540e7b5aa0a737d5a9d7468a5400e87af021e9577004a8240a65287823cc13d327d603bf6658ebf66c8b6bcfb70f3cdcb4caab4d515bba51bd982670fd81a510a17a8c795ccd9c75890a785f83091b07cbf70d1a7c42f4d914fdeecb81004a41ac79f737aa7917277593f510eb077c477963bcb9ed7e345225ab6f08ff7d96b48543e48f6e0e5c6dd700fb173a0e5cb13bb26b35d0f01f02b1665eefb50d684e6957e4bbd9bc7c49d05452e8e9083270b4311bad39b944309d06a457d360f8bcea6a12e3793b56487e83679a8505be1a1e01baedf7229f1a387bef9fcef9ea88f3383047d8259430108d0df2f20737117f584c047ce37477d0c6427f18e353d06401e43df5ec45daf6b9e405d40d5f087ce513d30f284fbd47cb2bd24d853396155941c1ea0484a68d651b5039047ca0723348881f2c7ed69dc869cea6ae34b5c8e78176211676e1de6e2978e00248d8b06414b569ef0d2506f4c1cb672197a63c2852f51d20681516f512c9bb0c5afe772d9f7ec463c95f2380c4aed521b9ee115ef9fe72a4d744fac2ec86790f8ec3935737a80b9ea172f0610010858314b877c714becd2846cf3a77c7204d00de2153cbdf65a00c510e88817c8bd36c659958335c8275df2b64a0ed531d490eea214806e57569773b395f1de784b9760d721a1fb01b6621e59239bbf65e660cfc3bd18b943e13d8f3141508c1593d7c4154f4c03d139fe5e437a94ed715d50e54861407d588c70710ec243319f70844333810db88d39f846233a913c56a4f01e3165268c3c2381a7f4387eed231dbbd104dd88e6c7d12beb04fe9b2151dc807cde1efaf306047b51c1cba7d4e2aca517f63fb76947678e597512cfa9638610289925501b11044f779bd89a28fc078faff70cc5a2556730f57451d4f938dd10c761bf3f74b54d16a7986dde2cf74d8735a4e4d10d0fcbbc34b3b11f3221f2f0341842fccbc5317c243a7c7e71d04fb9fdbf77d62a4124c9edb387bcfda2e2207f7c1b80590ed0dc17f5c99213e62fe333046e53a5f199804bb113ce591116a0da14138759d1be9d78d966b7b7b76cd8e0bf846a90e929d63d98ed6251be5e60467e09023caf72f15b5907a87eb7a2c1767a9c5b6b4875654c913cfd9b414f60c61b042042705a0fb4864af34cb3de603246d193a23bcd28685ca36e7df7dfc0c879640e4196ba8090693e0799dd4e7e744bcac839b5c0f43a376039709016d03ec5d2e4a800606228b0d3334b7f2af41c383f2f2744461fed225332e76f7e80afeac8486c6ecfa8fec61fc9413c0b46ba83a8e757f3c7af9fffb980324f194013425cb1b0ab66ee3d4ea97acf13dc7e30c28e99b87aa0c1813e3f34a31e9790198702e32d1fef764baa5815a43f8ad47305d01fd5546ea965edf1fa2eb07940081e383b0d8f51d7c92c5b40fd9280049d802667c6b6ca54db5eb0788d06119016a5a0ff93c3606ff7c8ecbdd5ab2491895641283dd384375b535dcce6ae1670372e01c878f54519c394998e89c7dc05211c954cb1622d5a69ed2250fef7de0063da98aa80bd252c2eef2de3e69b0c7a491769b92dacdfaee6debb70425e71e0bd0e9247bd963f2f0da2c4e89b2db8271e6c7d887fa11b8ca1fab0dc7d0ef8508222e223daad8c6c994506c17d358880f5e659d2aea50ec0a2f6edfad3fc891043dbcd783595dd8c2f6bf4cdbb8785a9f23a02a57e398a8b476507082d36bfa0e4fd218026ef7fb57191f47e204212f0b7678fd7d2fc6262517d74dfa482ac802739d140871c445ab7ecd2e586b10ab534a1637deb06be35d2c4cc1037128b700e16bed7f18385f34762c706a05761698714d07a06ac144b6c33bd9a1b57ccd031a9d5979a9a0c6338636add6fd4938e1c9862f4efa99c57165986171e3c768076c52591db5adcb2116e3a7ec40704674dc55eb8bb425024f451165aca11ee3094cd39d0f46e81ce7cb78085bd0f7209acbbcf098877bb77a891bb82e93139207057a2c8280da90a19edc35aad99ffe53a8537d3fcbd688906b94f0aec0aac6020fbe00766aa389917604db0d2fab72d7a1dd226dc40e2c8ea028be4be9fbad056b1e5991814332c37b59e8f9803db4e5e112ae8eac808b144f8647b4aaf806046dcc70f6a063dd9b4c353364c3007ff4fe4ae201e8a09a44a71dc9be05d5440d56da880d131df7c07182c6d0f4771ef2185cc3c1117e6e27c8b8c028a7d2a200f67a54c09cbe831a11f10656eb29abe70865a58ff5b16547b66e43d9eba55301bfac7e816e1086d6aa5d945edc47aa2dbc3a05389aa5ef71eb572a9bff765e0fa99eb8a787e56cd76990a887f0797230595f9b7722cf846f666540a7b6cf4e062fd4d13c92ceb2734bbb3f9e6a8f96f0d8310ad06af7cefaf1a6b8fa1d207c01265e5b2c50119661793b1cc203cd1fdd7d4257dc6535573460769e70da9d880ed5a462582399d4598704f2447d55f2e5678a181383b880f8917f81ea95e24b0a5b793d39fbc49da3eb1376184f89b5d48e3f4902310af5a17c3e997298febc0faec23707682363d8b9ea42b296a753e1c60a680e9f322fdd43119f289ee6fd0f442b239a2a8f5573315386f4e3a3091fc828db38ed8fb2fa7cdd9b52c2d7c40091304233de71dafa3008834f9280024d64e71f1caf8ab56959584f0a4ca86f062fcb56937b8c363dc0029dd5640bb7edc956e5713cb160f16c470de501af3101b06cc4bbba50ae95ea9fbfc2307b773dd17f1784cc9666b6e12d0d76e4866a09a5e711a12649993fa2acfec8c87ae7848ddc7c19e0202760232cea03b2a3660be2442c50595a3f6e640fd6baa44aeeab598e23890ce9aeff723045a439e7aa056eff3f136a7fb6c1d8f668cfbc8381aec6e502888cebd59e86be130f70ceaf0ae99bcc212cc504c1a822615356dd59b7eaa2f2006a67a0e2784bd57bf13ed90711f977c7d878d8ea2cffa7fcfa0802857873c9f06c3f549c33ebddbba0d75307da9fd4af0ad22c9affbd884d3d951fe7ea0231edc769d4a31a2ca2ecb4d83d00b660769c4416a6a442df1b6b4337a00b26161a08a159d38a988b6bbf409cbe0a276ab86eefe55531b030f88636a1da4a49b367c8625223a021b34234cae15a032088ede9e297b984dd15dd9cf867852d13455c92e7acb814421c1d58b9ac6f05b8483a37ad1f03bfb44c8edbee017793791dbe4e7a8eee99cd9037c71444e80b3ef718d6e1c8700b7e4febf9af0802e995b9193c90c482eebc5e648223b70707e3937b32605b460958b1606050f39fc8ba8d509e2ec1bdb44fb8abd9e086d3077e284e8401d8b841f59b750edcd097ed6813eef543762b4fe8d57c837e73c008a61f7b1b8d79d6ba12b1149d62b04ba74e26555809f2175b0239a547fd1cb00d803a1fc30dc63a5a081787ee466e21e5548aed50ed088db550f9807ecb51d508c91edbb5a03bb916fe4ded89a4494eac0c4827a3d0f9e6d83c701b7c024feb02eccc294ab17d746af62284a19bbcd5246c334dc42a47859463b1d81f5a4c5b09ddef8a085401deaf6a92bd0abfcd7d5917494a7393166153dde6402404d4b00dab3c9b2f8f49ab31d44e9ac2a30842843c40390d92d0d1867e0110803b0e490c7cf611e6c1a940fa9b751f52f421b6be69da012d0776fd6fa0389b764dd63b0f46925671ff31c16487617c103731ad8f4e9200f8ac3d2846d96838d07605ee0a61927185da7faeeaf41e94965e85b1b61826fe781035011014bdf4d57f6aac0e5db1d12c6eeefe5b8a1146d4f3f2722384637286c8397d8da307e60986add30899b97c94311a3011bcedcc8a4d9808b3b7e01e24e734976b6810f2bdeaae110539a9b3765f8711d25908266824982f2696008ba5eabb13db1962c0ebcb6a2d0128771967353c97a8f3d2f3b5eb83506a66bcba62e1aecc51d701e502cdfe62060cb7895e73d9538e4223bdfd5f9b84744751b49d22150175b434e649884a550b1a583b02280ae89a3debb6392499dbb7afa07e9426f93d729cb30546168ef00ff4300e49476fd32411f4a9ab225be8a0e8981e917ea0c66b29b219b68ec98504b4bfa74e6f4a079df903fc696deddab0554f84294ebf615e0e2896fa0410e6081f44c4fa89cc57f9c879ad72f64c0a29294d89573278ebed4cdb8540e1b1930eb292c28a9351932f919d1822cfd5f9925a27601a805c62004a6d07c305cf2904581754da372bb1eb7a4e098fd72b164c94bc77df17dd129e037c195e8a45350bf548dfd637b2a441e3d13aa1bdd73931fc76063733498c12441389208a31320e0f03e1ab2bdd4c4264916a562d5567c2a7cca1896ef63685bdf2dc2cd493e80192f688999c983d4966eb2e4a79c12544aaeade2b68193ac1008e16207cd63907babdf19724c32dff882558a3d88a31e92746bde83051fa5ed97cd744ebcf4e0770fb70ed4f7f1876660e145cb04709636a36a47adf0724582c43dccc08f1cc0c25a5d6b3463c120eb9a26c0c1b38524f39c57efb00a41fa0e4cfdcf299996803d414b588ff20e9544a8bf6a3e192035ed080d8b6996a1036bc97516589816a09be4ce86af24e3fbad7b8e36092461a4769bedda2f052e24dab1a6fd21740b90f7442f46ea9f845705f1a6230c67cc476c386c4cc705b7df09f2e2852972396064f68bb7a89b7fb511c637d92bbb418809c548744b33e4a48eca2c436c0b4250c24432cdcb38a5bd8e9f93de511584b25d4e0089905fc7df7b2b8bc26a6dc830a6a086c3c008c24360947f56c95694002da7c19214a3343efd3bfc816a244c608ada515200e21642c0bde287ea3561fce1b550a5b07f8975bceb05d1eaf9335093bf549ce7d8db9133f415b4e57200b43fe72822fef269ccb514a27e86af55d0c403ed72ee1708826aee2a6fc5747b73d839a72af63e01c3e8beecdab49e37f0389d6fa5566c5ad7f2b7204ed06409c4fc7bd9891eb4a9b2762ccb6186f6738065b7149a343029a5677998b993842c24f99086c80826de1636e895cb4ea529408af6de27393b35d724106ad6df6f9d55da7dc553931b2a8cd0c664676bd7cd80e7304927b2338edefb4ac744cea67bba59882014cd5f2a7deb155bd13165e9a03d09e25c3ef33cad96afb67c8d7668a07c7efd4a5b8fa2edd9e5768a297cb070f379580cf977d1e52d2ca1b61daf24a5ce80f656dfa089916a902a9da8c279b076e193cfdb98dd4a726643cfd32c2d926c1739210d3bacd4d6f93d73323451f0525592977876e1d0da311a6b2a7912ead066f090a8aacfb1449ddcaaca3286502b951e1bdb5fd1035fd825cbf397b558b665a2f5ac91c03898845e70c91e9d202895f7bb57a6cd561752f8f5ebd03fd1ca406be3ee19fe6d0d09755b7ed69ae08fd1866ef43621706bd2003c795702bb5151772b7bd3543cac064d3b65acca10cfd19210f992ae64c6389eed1c3905af1de4eed95e257ee8cdb2eb19d6f76dc06530bfd8a3a9d36fe1eb093d688bcc59d146c1d9c5de5eb6aeed0b43a5175fb033930f539b022addf12951d9819afcafd36c8a89fcdbf9c832119796f5b05230fb0cc5a0bd1c7a9b831f31a4d44a5ebeaac29e94bfa6099057d5ee05b7a54b40941a55703d40e45707f616c09ccf95a2785278d335f08b03df960ff97369d6800df3c1e27ee35295a2fa662b05e844d21f4dc319ca545d61be3732c01daf17b0503ddcb047282427c699c1fa5cb72b0d4c156dc2d5d31952e29b99de823516903714309ac840cc4a194abab1d4d370c0daaa26262a0c467398bf8c492f3fbba0a1e00adfa80b22310b65efcd6a8ca1f6070a12a74e9b3d1bb6cdc2e1e0cde5a0cfaba84a315ae1ad1aebc725205f10d925278abf3aaf053e88d95a924983cc10a20224df24ba4209ef7a2513eedd9a065e9ffebe3cae6d6c09fcf73c448c86a080b2936b8910e02b2c0ac9a0e19dcd35d8a2a8b72b1d0306f66aed67875f0b30e64bf180e8c9996622a50f41281a39b401b8c40cb00bc5ef2c8a13068a93da409b43f5f79a7d933efe6664837e1b7d92559b3b50847f71d87a127687b83f91b0619e3d2ad152cb0059277f9d221bbcc1e46d36757523b5b8256d7cf0c3f63ee0353b4f35e9c8b1b1ad97063370ff034e56623ff50ddafacd0d0a83140b88414087130eb92d138864af151615850ac692de53a0ccfa76d8b52bc5613a67d068e0d3c2a307ea6bdc7d3639b99971f94a0c7d7844ba75b796f1bb53f919d9dfb580f238817cdc2b4d5d6dde99114cced3fb5c780358d4463642ff0c656cc2fcf03025b95d9f07211d4c7618d7578b9626cf2d432bfa7e8a523a933ca0b5098e3dd00ae9d9843bd62ccbcdbe9ec51577b83de3355b8409f9df755c3d158dcb76d2f038a0e96db5c220cb20cba1064db7496301a0e876e563a385762b02afcff8e0f014268acad0c91b5df656be0af1054492995fdcb9d5030d4f3cec4b05e9cd3590d3b57aa520ed47149d5a39fdd0ae41168ff0db03eb46d4996fb41769691b24802fbcd8d03a02a3600ec20f151dc53acb43ce49cf35096d44340d18471216d4a0f339cb39677d5608f1788ba30190c000fda661d0df28b76966c1acd077109520cb236a7aafde0343af9693f4645c1b9b5b22f8dbb045af0c08a0e3661f8a3c1038cef8a1218be58327021838ddcfc032b6fbeaedf8476575acfeff95aed5d5f011154814b4ab400f7137eeddbb45f311b6e4dda472f609bba3633e69f99186305eeb293c0b19afd542f67efdd09e35efd3953bbc1999fc0444e94da0e232ac20572fe2abd5d3742135e85a7323f0d15540e890b26b1a9e3a426fc2aefeace4009ea9678e36c05c9ce9cdee9a1b31c39d6cb080f41e11b78d71f0e6365cec8c609b188a19dc5b6e3be592b0887d371da701886e5aa688438d98f0e38dc24f9ba052d1e422cc8093a23500de0945531edbf2026205a93d3c5a1be946dab7669710a8f183ec5df11d474411e952c138a26b0306ceebc71924efc8a1a1987c9af7e096db3d82e56fb4df40b862e455cd5276c7448bbe49f7d8f4c8a6df97168afa40b20e30e1ad005451fb88f7abcc5c80a115ec131376c868ad72302eb98641ea203610d3ca88d67e238833084249a04a5977285fa039ad1b2bc555aab1ac3ba2d077881438fb75f4322c2331bded7a94ced0620b0ecd0bc9333ed34a958555949017cac51cbc12842f17479432a460076cba44f79e7a2ac8214cb42c03ef4fda80f188c02823ad6e164e70340cf13bff49b17d81f8b324aa41835ea00add450f30e366f6906d09a32154cb4b4108c0adc78b29d0297636202a8093f4617a5be6b091d9c71004ff9a9d80ef062da4d2fc24dd3116b9d28a9b1493d92368f0cf5d60bf8237516b36d6cd93fd6f74cf93c1a8bfa5f07663bb494a53629e6958ffe1c0f692894052a574c6dd22ec996883c7aa4f6ba92f0fd16f970562d36994e3479014c3280402591a4778338e11b262d8092a03a95b834708a47d313297884993e0db4701d2b8d52c3b6801889bb3eb337683ed94c6d52430aa40359a743c7cb030d25cfb0aeaa122e45f7234360f8641ce35e3a9e556120cb8164f29edf8b249b0904e2342371e7acc03cb54891e23abb8ecf31934027720fd96879ec2f01fb3c0e7130c26f8b4796f71794f829d007b33d821d6265a3c52256d89f838c160e900b534c8492855b9a3c396b732f6cebbeceea7bbd45d13f45e6583477a3ecd5f30fc26717574be178e7b88491248b4bab741ac8648aeda84c788b6c7514cb74870ce6c73d249eadd3864c674d5ad07061757b71b8455386667ddf99f9cfd2012904a5ff2cf8e33895f81107b921a403bad6829eecead7759dd0848319cdf24d3c0f566f170146390e66944c7c30090f89772f26eccccffdf5a55329f4f0f6b99708dbc23cca128087e4614148dc6cb4d140881a447ab05b0a81eb990d478766be037029e717883a2b8d086058c7982be9ac8a6b4285b9e129527d82a3e2cc2d510f03b5df875f98d64912902a2391f2130ccf51b38049dbf9c09dcb129e177c1409c535c25792b5a06d9c04bd3614a24678af538e62733eb8c84b96da4334cebe0b007e8920c587c90e23c016aeab216f4f2686a48420dd27535f6604f479062809c6bdf4c89ce740c5e4edb084c58606749fd8ae668c899b2baadcbfa04e872f0be5cb4fede713f963bfe26bb9306cb6eafd66cda1d59a9c01b03908e67fd6e6081e7e63b6c8c5365cd556412ceff9a960e0add9ff593d5c93ff7de119009d4d0be9f17982ae291f4a484bd008988845f684f2396e081026fc0e2e389864586605a220bd787a6d0af480f87ea13f9da4f80edc734556e98a18a0f5a00c0fee3f0daa4d10c2fc0086092f430f0685dc74fdf9a72eb9e7cbb9af2c00469d25d9c90aecd0531dca4da73c37ee32e7a29d8eec597971002fd9f1b848725f7df62c910edcb2ce33c0ce5fe29a870a8a3bc6cdb8cfecf69e6d2e6fdcb78995dc7b400a075d0cab2ddc617a2b5fb1fb4740602afa055bbde4e255cc6dd8c93264a185400c44392912856cb50c001e778577aae3cd5cc45ed4ba415d81b0ddb620de598f02511e40461b530d2d4528143d29c6539849104e1030b161f274d28f398bb9bf06f7e485f08dcbb24079d2891af11a4cee246cc00e39be0ecff091db9accc50406459d589a094d071a48777e0e67976f936f17d9d2c852af130fda59389c5556082efe0e1f82379993ba1b78ad9d74f3b5e3957864364ac1abfe375d53949f5e0d154ac58f75d82542c9a353506bbfb43222acd118919a50afed33e4479f0e3d0cb56aac4e16e3dfd1796ef25934311fed6c63a03aea63a15bdb90929819bf790e6a8392abdac98bbaf7190b5c23e0f9e6a1b17278f63c25bb3b4883eba7a72901337e45204e438fffa2f9432aa56358aacb1752b7762f107170f63e125743db0e71e347a6a372d42088437a82a3c8c822299cf53a2d232cb022abace3d9d10a0279cf36cb7f22992ad1c932332c4784b34b6dc6280fcc5d5450fb1ef83f06ba03b575cabca822f0f2619e6df17754abfb17bef65863ab7b9767559e030e7f2b09a495e432f207e86e35bf5511d275c24fcfa39e5c33a3bef45fa4850a5f754d0b05f8da75998716f11f2ab3e239d9b92414e68aa2514f4ce2c5432cbfa6b7ae0c88be1edcc887b83eb25a24fdb45444d7c2d80178657bc2965860314681bb6f0a47973946b7971f7c0fa30414ecb1ee0b831797029daf7ae7d2a9996f1c24150f01fe5c11c52156a489f24979cb6a9d81a602ab8d74f41049b2f910a2d7d7110472b0ecb0fff0977172881c3863a317430cf4b6f0587397ec0b551d485f448307fff4d0dd5a64b68a51246df2185d5e55a192c9beddf15f6e4c52c8af6cafc1082cf0822be81c3e6106f20347bbf89eef97977e3ff36b373ed5a5c240b0074a054739634ef47351f23b7f9b88e532de50daf2688afe8f3486d3f0436c12e6540ded35d9e359b031ab6fa5894587b310caa37c388f292898b59f5bdb188b2149042c102623e44458c0ee65da3eeecb8cc414fd2deba119a3d63d98cff3c5e7450e908805c2feecb5d4a38b30d064d634a826f30118c758d128f5ddb456412190013a113289a6f8288e8ffc52a57d76ee2773572f990fae8885f46236a501654c0c681ba1a96ebb0263413ccc676371bce12bea4aa571f75007e0c1bf2fbd5d070f10d6ee56da9e33b666f9aa1bcc65e77446778e36b90d73fac0ed234b99b7cb047e0823ef0810bc0df0d112fab6cb8adead50546c9ee51524303c5596c278ce09ca7bac9adb097a5107e7d12967b9282837c0b3a2fe23965995ed99e0afd03a02e48176d2520545458b1f97894cc12013e935c8d4c6b3753e722f89d27871c801fca3b2b28b74834ef660436aac8db385ddd527d56a0cd1281238ff12063c410f36d0e69d6c833e0c5506c3c6b74b7cc89ea809652ce34f8f0cc7f8716c1a650381d495392f65ceb0088a21db78c17b9a7071a4ab44f524a03f2a760b3c10a60c3cbeeb5917879f9e16576bb728c9ed1f81cadab42b7445a1c3717e814c413502f249cbfc3cce057f2b81ff8b8f2eb881b485e0b85117670150a6f27ae08140030b744d3360273cdd55a3b5b6e61f3a2558921f677c641226eccb0987311d4700fc18c5e054cdd2353278441734289f43eced9da67adda821906ab15e6d2a7a0331ae751bc9a24c0a7fcf50fd4a3b0869bf90f32ea74039028df67ca5f1a4af06c8e3b90654cc103164699e2557a0e70b08484d3710ec5eb464e09afb7acbdf06a2384ca8a5f7d20f8774f76f8953c9efa1499078cb5522dcbd388785c3dc6601e703316a8feb8248fcb4168ba07fbb84cc5f8c7a53fc216aa5fb8ff5326ec60ba8b52ec669604eecc21bb1d9196dcae9fa11be70ea815c810039c8dc7768750db9968b55a1cf80374cd1a040d3d905fe37795ff48540aa5cd692af0e5685cf042165352d19e2a18da963e2651176dd89338890a86e03142685a6e98c44a0a208e6b8ee605a78bb905de42ccb197b4e35a5aefec5971b7f30d31f782307d2820dfb1f8523fe42af6c77eb4c03d1d5750d808dfbe69eb66b2b3bde178f57200c04b4028fa57fb748566f32f86add2c36dc4fbf21a7466cd51c1e925d73f312d20d9054be0672bb3884618739ff3a1edae980b54984f3dcc5c242bb34b3a5db6c0bfeec3fec1eb158cbe99a5b1b20f3b074f209184bc6c1a9c37f510631132ba90410b7289be357be0d939988323fe8048efe4b5496f918acd999b993e9567b57005e6456035e81097aaf85ac6f79ca25de0eb12cc36a30153b53a085454386610c595638d714ea3b53e767055cb2baa80b2854307b68df1580d045d25ff54cc00c4ed60b2b14d20e37a8d31c7006344dfe6025d2f4300315135d3c921a1230a9044f8e3d52a2db7ecd35541d49c94a1de24f669813dfe40fea5b54526c317c4701949ec3b4a38396bfa5f85c42040d1f5bb9c2bcb87db3072df7e2ed50111fc10b539eba3da5855afea320f3aeec539445a962570d9e234a7cf155bc207f97f60ff50f5fddbb337d8c24a3b8b9cced609eacafeb6b4ae7c981094437ed5c61e70b1b2f9186074046f43f58aeed909ff45d26ef4d5d8f8624ba8978ac93360f650d5cfb71737230c505816f1d1a819e7a7896a3fc0cf3c768ef6b3a1840dffd8d064e684c6e660369139da4ffcc213fbfdb0c400f80efc6a3d71a6da7d02c20200c624aa3e3a08dfc5bafdaff23c062ea5529b3c9d759224fd28bfc124bf7fe1b0a3b1db8a825b89888b305199a30ef93ada4d7328877d3214e2cf731a13f854200cad72002d8a072cacacb7c8c45d78f63d7781beef1f08f7d1a60962ee432ad02c8dd50bb503a47d1c7bcfb9c94fa17547add3fa2df94dd70b5e12b155443490c152e835ac2c957a667837dbd33229df6f66d94ea5737e538136ca0e34e8e13052563ce91fafdfccb437ed12f537db1632c3c86e234e91baa7f90904ba3fb4b08 -generate_ring_signature 1013e6d2970665fae054a3bd9fe5e1ed9e80e081cdb6ca240c0f517f1b415cab 0af5247217901d271584c54159546c10a1d682cd243e66a945af30b5eb427cd2 2 8eb838cd7d2596dde1caac7f8c29e081333098096355a3888f6cef9ad54b4c43 ecb9686dedf61fc294b3c96c56ea43d6e57500ca683d8ce93e4212bce07e71a3 f36383ca3516a2e053540ebabe435e3b57cebf3890478f221c4aeb0b8bd4e30d 1 5484e5a1a5eebab7fe9c5ffebc40638130b616c1c0e871f738111a67c932250159a2df1eafcebde2368b6d7291b768c20a68f940daa05fcc367fb2a615f3bc09fbfe3e0d111bc83ca7f45b95d327c42413c3ffd4bd829327b72bb26c9e767b02ec6f64e6fd42859876c8e413adde1602e0df2493e231708871d470bafa019f00 -generate_ring_signature 0b5ec78693ba8b68d4c52d569772840dda48ff5fb748ffa833e5392392c55dd4 be1f92e110606c4de9745e00fcc25187beeb7caef967abd9c355cc032245326c 2 3957312d5af6b690f318ccacc696d12ae70a51a5e59169a823de76c16418908f 4c4a89e48bcc7beb356d78e9d9455a67c53f4624c53254135d0f665562c388f3 6360f4e3a558da8015355552ef2b95fe491529953bb454449caf53a3169f6f0c 0 39446c0f44183224293aee6da91cda0f8549d4f9a6e97bf25885d6c27c54a30264f42140efc29fd3f9cd0ac126447d81b63b9482d3efc9ee7e53249049f1510a9af989c77712b05c7f140163bca32ee53f45cdc7e154f9e43d5bb42125f557035a5dd8a275f82824428f7cd221d5498c4eaa93d37ead3dccdf37fc0951b7a80b -generate_ring_signature a8ae168867259e89dfddb607d8121f78d9bd1afe3b56bdf7f8d44f328c407626 29c15d05cf7408ed0252e1d25c47b8fa0c34878017937a285dc037fe48c3d9dd 4 38aafb4066b703586c33d7c055353712cea2a8fc42bb1af683af4d52194ae229 e258caa68a2c25529b6d845e6477ddd241646d89135d3e860f9138582b0e2156 fe25ca5baae2ae89f703b0d17d0e9ab8c314eb69ee1b5ad3ccc2f178f7a13a89 ff91b9cb17606444470257f1b2520fd7c7eee2e9f62fa284d96c423fbb32c32e ff3f205bd350797a39e072c715c24902a909dab05fa8b7e3338d2ef69a85fa00 0 0d5b8e10f9212477428a4040ec94961378f55b92484cbf1eb216323e6a7e5f0d5d4bd9f066e33af0048007d98781c7591a91472fdaf00048eb43be05f12ef900e68928686d330670987b84b71a12c3245d2e1976309b5e669a9b05fb5601720f38ab2c3814697d6bd0b9aae8d91602cbd952274ce4fd09c96f6e7ce4496c6001a3a4e04163dd315434bace9ef858a959fc709e12256c62068a866eaeddbf8204137611ddadeb5812a3c965e39550d0e2ba2ba93b11c1127d99946bfb80b17c0e604987b7ee0533d46090cb0264fef70845ef779d667b7dbc4ea6b32340730d0c4b71db29fb59eff5b24e1d157fdd12b65e61e4874fcdf4a5246940c67be92a0c -generate_ring_signature 6f7c9d02e53d619372700dcfbde7105963e017372fbe2918ff0331191453ff60 11bbdfd15b4d972900e7d088fe7971daa6acc8e8d97e2414e8f44fa0acce27f7 217 a9d2c0e8a6674d829f4d758c0dfdc0adaa073d9ffa6456c2d06cb7ed9c7a7359 ef1c7c48c75f1ba091bff82de93d8ff68f7bc31da2ce87c8569e7443d359c5f4 044e3b84c6acf627a821ab990f7085c93abd290e5930767d6a177982d6ca67e9 781b4c9fb9e32d73d2d1b963ce415c90cb1844416f7dd13280e1146ba1fc4e5e 05d8c1d8841d0e5eff67f665ef58e5aedd799e1b9949443cc414932ef536fd46 7cf07260320c63fdaca80f7b8ff9eb61bb4f9a9d3a27cbf0c93834cf93fc259d cbb0202ac483c7c3b838c1fd4ca2f1bb47e91b0fbe6bd10b1e45da586d6ef64c 25ddec4e59d2ee1da3a0736e7aaba1969c8c4baeb25b45f7414ecc3a39f94e98 0dcc6715cb35ec3f1d7808ff671b2433657e4384ad77c6e24f3fb40ce407ff9b 207324c43b3cb524a6cf244595d44d9a9fb6bc5dee499308b95ae8a226feaae9 32e51b3211ffb05469bfaf5366e9818fad3265923f4fb734acfdebee7c183342 f3745f3cc4453c17488dec37617c06e24c7d05bcfb9449c21e93c310aefa4a44 55cca614e77dcc4ba78cec6c46f38d3d0172ad2da45c4693733b845449b7211a d01bdb20dda9148e2ae0b71cce5ea5914848f79e3072f0b248ec7950fb0cb32d a7b51154c60f197d723af16e4e0f324a901b4b26393a71669d0e5a621b6a4c26 038d389b297f53b1d35e64b92e579422c93528a82caa3742747cf55212a7d9f8 9255e412cd98623b34f764d2a55c443d3675ddc1d67e70af7d3903cfa80ea18b 0dfe3c5dcedd604a467aa13cc1ae316c1f077eabb02803dc06de657126484f3b 5ed0e9819d6e2d0d113f1edd978e543056ef6e4bc431f95f2076d0d2fb475d2b 18ea161052684b291829d5246f410d2a10844b60733a978571e2aac29b8498f2 7e2e83d2284a89f30052c7d3607e88bf658f9fbb1bfedebc419fe3aafcc484ac d7d0b95c8140094761ed4460afb84bac314b952bae09bd0031961209a56791e6 76492fbe42a3e2e00f6b98c207bd06dc8a2b9211bd27154f2938fa0e81d54589 dd2d108646f82c50e2e1a84de94cb2bf50d660d604182dd87ddf620ed3751c7e 43b77c3b8b7b4f57657194ea5c26858d5d60849b5c2fde7b9e2db9b2593959af 68d833ef2995aa4b3ecd512361d4f99c912d20e152976be8471b18e5f82d7a5a 80ea13958354db6fb72d24ca091ccc979e37e8f5e866097a199f726a15ed0df7 568bbc6de62162897808b97c17cdcc6bc1986534dd5f3e48a8c39a0c2a5a345a d052a818e54496d2db412bf4ee41ed0152510bc19a0863a59def191336be7393 e9202d3178b1ba5cd5de88ae45610a543418e844e9b99b443292218dc1abf21c cdb6149924a134413884a183d5d5e4f88d37a7c7a85897f5d7349ffc66571910 64066a9b8f720038d5094cea5c59a4d87f09a121bbded687e2913561b7462d3f fb6d221ac1fcf65a9d188165549d595bff987464ff5e99ad035deb282d42dc93 a4ee1970e64f9e52e882efe800594e98f29e8aecf4ea3b662f95fab5b9e94447 ff21f495bd4634f38c0e1e162f3946cc0a593f7c738833cc83737a3cbbe2b808 5160ac5ea648b08a8219878f0fe2a2dead686c3ffa490bdaf76f5aa70ddc8474 7d432689aabb6ad473dffe72c3fd10f332fc667806187a1d0523272d69671da9 17e4a72c95f54d277c7ba2bad16f634ee82bf403c3b2af475acda08042017fc0 7c8d25f7cb1a3c7c76eb0f1a0afcff5e0acd6bc497c99c2aef04284e58cda7b6 a2f0a4e90c503240cfed6db5ebc3730fbe73dcd04e083c5c74e4e992b4bfcd19 61731b0a82a84272a3484abb794d41f8aa35f7ad7c15eda62119994fa82e1be8 9d0cb6dcb296c7a1d9f315d9f3cd1ee319a32cdb26f6ff29eb60864d1653435b c57d7d5187cf78f06a2b4192f4b2af9e6e3b5323f1923d520d675ad716b67e7b 66865357df4a203d0365078d80e8393f91157836e16592ce129aff41de009be4 6fa3380a808f73acf4636741ddf415cf2968b5c66822a3d9db9231b981319118 4705319d29971da7992ed65e5edf5a5d4ab65312ee5a555f92230dff85a8f6a8 84e4c913f2f98cdf68913c688ab2660ac1f77034c2007fa33bce2832450129b5 a2d9ef7d02cea31f517947a914a9731b731ed66c50eeb27c152ab8f3cdbe7f78 a4ebe351b09a08e8c9f508938034395f032d7992a67671a6feca145caa354cf5 584b6866fa954938d5a134b840a801dd0eac1a17c24e45e13f4566bde3e9a140 5abd93a726c61acdc9505a30eebd91df1453e1d7cff7d2ec334d986b08f71672 a6931ec1d2b21862f1cb935fb0d1b52791d8f916c2afb9937b51fa2317f493c5 8209bdfb0f4967a4855efb9c48b019d21ef2d17d2e768ec86de7bc57f36c916b 301e708d9fc079acf118ce1623a4d2eefd5ae3765883eb7ae06f0df59f0a9225 7da0a633b9c97d588d0c4f011db498f7fe262f15de189c77d0052e522e624f3d 69680b2e40a4ec561e8a753bf932feed7e401ab505e55305226121dc14084cd8 1cc70e12f5b2cc86a4c5fece7524a4bb842a4ea57944518d035f3f2f68b44b47 69a0efbd545b2662c8a6b04aeeb37fa58011570dd1133d95b47d544cafa3c81a 91ebc540692be156388b17ec9936efeaeabfa760bf334286cfce9ee9832a299f ffd00880cf9aff6051b7a16388171b8a2fcbe54862015476c77f7b3fc46d7086 c95389fa96f525e20aa521de82876391681fc1e62c92c80253f609775e0ad584 a72805faa42fac4965162a569cfa86887b8e6df3f9155d6a87d9f3c8c03c8cc9 e4f035aa88e869257fb5938c550db6d3400f4a1ad9338d412d04c2f60ba7c11b 9284397d95cdba29d6ea6df1609a48b96117cdbee19e841be496b79337d63281 012c09af03e3d3d7f48221672c5bf4e05dff3716d744647807627d9d7ed75e52 b9c9d1f834c86d1113d8536412829692e6794731e2cb85934f3add8ca5296295 70f4428a1f36c543107dee9931b378e738af47b4c960e2360521281ed01db82b 2245991898701a04d0702da058f30eac9f2eb869566bf5b0832f41449834994d debb66dadd879e7875cdd1fcc2e0c73a7164e295829ccd0af5ca6a44897e4ddd 3a094f9d7b3d011c97897ca7cb55c1e4aee5d2833ef5cb56a9d7c1aaf043e359 20072e748ddbda7f5a4f6a9f30c4e7a640997e5ecf97f15bf0cdb068c14d823c e5dffd9502a42b8638da63500706cde697dc42cf941636a3775d4ecf5f11563b 2decf150e5e635c47b67ff2c4c2f1c3cb74d15724c050b985a0d37e01e067646 8ab05fc816919f8017dfad54062e9f93d2f8cee3c011cc077e442df75c3ba0f1 5929f00cbc961bc693eb17131b6f89de73cd2137a6112d482346fdce4da3072f cd245415fca9190b3f5fa2b229f8ebca3aab2e084f01d0638afa99edd2c817f4 a71c52bd92541494e4a389f667ca59f34923911e17ef4ba923a04d238f89401d 8ae0b7bba43ec4b225e4829ffa00d0fa6f995f4ff93ce9237c645a7d7708b078 3a55518cedb46f2b1edeecdf56914a8f2de3605df30995fb2c331100dd73063d b249212b901d73152d5d6575a1ef5f27079ee8a6660c6dba3f0d70ab18079d5d 09cece2cec55abe14f6c5d3681e6a3da9e3bd75442369ace34915c8a725ec880 1710aa52ba925feafc4477ca73dec7dc11805e393642e5a444442989be1def0d b993ca971ea66dad353f6f5c338b6a2c8aecef15bd79ba03838a82c845172440 c9d8a3bcf7523dcc38d1a5ab4ca71c999310589e9712f54d8bdc13075d9602ef 12e8d6c4b4ccbab7ad98baecd77ccc8a1666474a1d9717e227750f22d6f46016 04a3246f91e86c0cea76b123bc08903dfdce13e5bc1710c656cca5d859148cf2 92494c362575eb4a19462b1b14067534a465d331ac233bd770adc155a0e9f805 6baf277853495cf5482fe08582d79a957f70e0a878e86bceaa9af967a3a1afdb 2b5fa12f48497ae1fb670b936287f03f1c473281dd42b594dbdcde4079ddfbaa fd9f84887a617768d79b3492844be5b8da294e3ea016c1e51bce26a36e72577d e7da58d7aaa13fea9936eb28114f9b7f0ef4997414b569aa895500c0afab20b0 5e0faad0e8d55ecad29d0ac6316e17bcc9eade3bda7aa3006d03a2e9fdef7e08 50c555404f87b93e52de103c405c9fb1cb3c19586a0b90422061c4dd513cb58b e6e9e2aed640f9c58d0e2645f8ff308af33d8c8e8124c0a61c8ecc9ca28dbda5 959d3c613b4151b28c1fb35569fd3d89d1e6d276af4030b77cfe5ee22ea7c11a 404dc708e03dfe8f28ba1ba88f2e5db8757a3a67edf5c430ef6f889711b1971b 12256a5cc36afdb2cde8d58f6b6c996cb4dbb3eabc9ef530f9b93466d53d99d6 1e423eb530434bf15bae5f8a7c2bdbaf7b1c10501a6609dd2446636d90a332ea ad60a07f8e4093ee17b949b1deb91f62e1ab72595402d45062978df4c3266819 1379f3a747a040ef4e5073b3ad4e33e8c31902c87dd3d61e6c08c94c979f78f0 33efe37673f13b2ad5a982ba809af30d7480efb4892f14364b1275f8cf4fbed9 d4271609be30b70ce95cf10b2c25bfae6eafe50af6dea2d8306a760db35eac85 c7d1569541855b69bf0546034f9b48afa641bbd532d38388e96cf1768b9af4e8 2aad5b98da163fb561c7595a5949383a6b9a0805f8b0e2a8844e045e11cfbc22 1358ce01434faf7c8e9b2dade15bd514c7e92c6dec4855c2d10683f45bc6095e c3e37be5881f20a1ab5ce33605713ace69020f4d9969b735dd84e6aed56c86ed 846c51c4b6eeaf711c7aab321c05262e21d1327d367bd5fe08c0e92be077c490 ece46633e4e417898ed86f20cec69668d6e04f5215f80fd8d9b7fd1f310c337d f104dab4913ff4d456ef784b0ee10a7bd79f6693e37c670bde2fff363f2a090c da677636d4db6c721915564b13e6b53d12ef075590f76dae676e4c53d67410dc 04a4fb6c35c38360ce5fdc268739e2ef6a2beed8ff7bc95460d62b5adf68f8dc 5e7559290137fd3d83706e5e7c1121883c359fedbbb94c8ca4b2680733d8b0c6 479b7e2475c7de034efd81859b61288b803c456684be12537de7aa99b5d127be 402fd78886359830d6839113804b636a85d473ead1920c9932cef4d9cea5c6f5 5cb4b5011651d2c69e636f81ca0d841807209d841a739bf2961d1480c4edfc55 d07f7d8beae6750468330d553fe1978cb404f4a1e2488c753a5d0749ae524636 7295ff8a66d12b4818b5f35d91f2b0ad70ccc7443130792f4e484a782db7f8e5 fee7468ba7194735b76a6416aa03e803afb3bc66077f65a4fc666a8670813a73 b1b2854a6dbc4cd826dfe66b8c34253af6c3860502bb629ebd8d4fe071fcd1a6 7df1fe7319b51ad76d1d6e9115456c2100b8fa74e46c3059ae65b32c756464ed a07ae5f7e8c60a6ee0b6bc0cd2b4cac1e60a964e98b4da967722bd3d03409a54 2c89e51f3e4a9ca6c0d283ebfc97ed96edbf16fb0f327548617e86b89e5f8dfc 8e6b6ae609a6940736bfc9a50a7b299b0274d9ccf03351d2a8f7694e398abb2e 2ea8b51ffa4de90f3cb2bb07ff59893aa13de7c32b7726ce1006dc88317161fc 904a28f7389923ee6374cb5d32e08197e899cfc066c96682cf3b0fbc3b72ca1b 729ed8fb818480801cb35ce3253ee767255cac0031cb70b47ce4388e1c28a3ac aff0ba9017e8179fff3df916b714c215b85d094907124b4ff5899450665b8da8 27f59c289add2c9470af149c956ff3b04f5c6388926b2984c24e5b6756c847c3 fec3cb7c1fd7fa60da89c019ead2d966e06fc052096442d3142226ff0fdb82fa 42d091f65225d2e064d979404ab5754f7ca4bad7c51406376fba94c4fd0b88d0 2bfb9b80fdadd222bb08bb790e6d188fc54419aa279c9b21c0951548674341bf 2916e5d0c9a9138cc0166320366ff1a1f7c1f491a64159f903e7a2025441ce63 405628a15b6d835366899c6daa9030494dd33216415a88e6d9645aed8c89b0e7 3b2656ab204b8472eaefa1dff67a0f1302d9aa9a1ad8f23b5481a685c6a856de c11e154bd9f80cc376fa95f3fc8fa3af606256855b354b639b4df67b8e8640dd 8bd2ebe7318b454747d500f3833246e8a3edda5913577d9871e7501f4c4c65db a720d5a5a7242952dfe68637045142b76dff814195f426dcc20d317213720e33 ecadd044840a168f7e83a8f2b3f67f5d49131f75fe3322cfacaaff9ef68a0e86 a23c3b773dc8374a0b50173d6dd2a5b29ce66b0d1d62dfef1560462e5a41cdfc d0a8fe4d361bff92a2c8d77aee367a097c301b1a0dd518cf136f8dbe5af9fdc8 f604bcb4374ee29b3c8e4041d27a08ef094ebbcb4ed19559f7515401dca0744a fbbddb30feff43c8798c2c6ddfe3e8b19ec2d3ce405e1f0ea1d6c148e9491dff 54b2dbdc5399e72856119bf552be5cb3974ddf1a7de015d7e5da1ee02b0dfaa2 4eca7debb202e342051c049226f8cb4767442f13e712d3a1742857e3188fe879 829dd2aae457f9465995c79a10cc1c03488a95de1b8dc12ee74ca66325a7be88 ad6d9c33f993edb38182239144b58430aced85e701780364ae9faa243fa7a217 e78c8e16cc112e8b526ae3496bc19f9fade6fa6778173a88dd0bc18f7262e2c5 31ea3fb778c29313a09ce9665a52949dd6ef52d9fd514ace880300a6f2dd1f2f 8615a6a3d70967d960dc0bc48e8ea0ebf59d8b39a5cd8a99266288f4e8316284 65f76789b071a4e9354db6f980cd2c35f1eaea62e32f34fa1a7e4b341606ab3e d5f40047e69e7b5f819b276bc2d277a11c9937813d5924e467b8c9bc81d215f9 355b0bf3c48b6f3b738c8562887e0ddf6f7fc04d1456d202e6ba02014a4c90c2 d137ab92a2881eced4293511a819679cbbc89008d21075ee21c6bb5b046215d6 86332fbeb6b2f50aa906a49247b09f207c425f17f0d2e7a8a2deb585bbf0eaed 58bcdce587e6eed7609d388a55b05e4240a61b568e9e5141591ce8629c238fe0 d6097f96caa48e380a7ad94df021698e525aa113791a2b8be91a7974d29de2f0 a9cae6409dab5099d26ec34a41f9d646663cdc941ef0862ecd384d27cf2e9a34 2751d3a661a51328185df4ed66658f02393f90c3350eda059a4c71b9d1ed9f67 a989804de21383e46dd2155ec0dbe2f5c127f7cd97a127ad4b7c76f017493f87 7c337941ed522a8f61149434e903942c595e0bf68a19eee809e500395ecffe04 d531d3a090f7b9feeaf254438852eee405bf412d41c053023716198d98f8d0f5 625cecc397c1b67441730cd1701986484c2b24b5c32ca61321ece2c4283c6a05 9f0fa0d4e8999b7adf27f9b31824a7c309b23d2be2ee83e851df8c2b660360f2 407b0bfe39971b9e0037dd79db93d164d079f8e00dcb8d4085dc8b41f91772a9 5455a1f8e7e7cc1374f610eaa089086fa7446610e74aa8bb4afda37715091f27 cbcb2b6fdca0f6e72f741e8ca88271c52d913d93784d69e70f16d6da7477ad98 db1e3d9df06599d6485e2ff371d4a600b43ccc249bbc6309c3f9a36de1f3af1b 406725971985265d7a2888e91b955ed40f4e3d1cedeacb710f8051c53cbaf49f 8b28e249da7f5c9280c01249572cba2e6eecc970a93bac53bcb4b9c8e6e640bf ae00617c7b499f288834e52a64504210be9fa398d113ef39525d6a85ce757c40 0677a6fd22fc12cf3dcdca106e05ee8fd18759b78598eedb7e176ac44d65e2d4 8e60bf4afee39109eea9edac28875c31f30e6e280d898a00f796d85de041eb6e cc7249762d8dc1bc2cbfe83e5a3a28842a48e6bf29c4e78c9f115cfc27bf8bf7 a6810203173aa88357dfed1f35beee6d73e38da78a7583b329810a06c46dac5d 431f2e1fe54a13aa3aad164e339cf6463b8190db5f5632d4c096b08e100246f0 e03c6d93874e0ec2c8ee0d238382040d455a55dc0923fd0b2afc1bd1f7d399a6 73b62b31b6ebcc9937dbbe4ac6907609a68d6eb4feb51f7e70afcc912d22623f afde6010128e5d4fd46a0c2afc6d12b4216b51e092dc050097ad9cee3b6148c2 cf660b8abca78a389af858c41ca235fa127cb86098964a5dda309ace3ff34c32 f74fbd3f2d3da14032200763a61edfb019f6a34492184702cb800ea889bf37d6 e74dfa1a4004ddfac92d41d388572a5f255982d40e207189e2c1892dc5af1130 6bf7dc128af4e7c039f52093b80cabbf0342f923cb372712fb012edc469607e8 ea3f3eed317815434b64a8c02647bb49933a341363351fb463198d6bfdfe1ab0 bf607ade73d9fbdf67d6f66f4757bd85f8084a1de1a5411217720e2875b94722 91d0180661702425c83970e04fdf5bb86511516c400f6618c9f66e60fa79b70f a4175d3185abd7a03ad7d139907322418577644a7394c77cda9092e937a1b401 31f2b49a2f3df0b3966ea20984843e4d40df5d3a115cc71923142d2cc9a5a339 12cbd557508932dad07402d9fe599cdf290d8e13404a7f0bbc30cb0e8f6fb051 e87e1757f7257a30b7fa51fda581127a5ae9df6f3a63c79ea0592b63dd431d51 624357831afe4f7f4acac70c457280b1e5e3d3ceb7d6642a7c5062f71bb73df5 beb1b43f58b59c7926aba3b5a873c614d008ae6220a12623680f2e7d571f2931 61ee7bc796acbfa157a882721938f4c6a38ea7c5780bca69f09d11bdbf4c6bcb 6be6cce2d2daa1c1dc937f44c7b290880f72e849a178f0fde7c451bbb16cc960 c6701e6389ea37f45b9faa73b44dc412c43e15f63827f73253251048d0d1aaf1 3bd90e4ae4ec59c20955a8786436fe323545d7d76dd86e977fae235710c2770d 86e7e8fae2623b5072bee6585705ad8c6a35467053011a29a2902bddf28f2e97 f975164d0117adc42dba23154292e0d9907eb1300524f9f146430763f27fe1a4 b1f1177dcdb89c3330fe6137eecd4e3f9f613456e32db51fa3f334882640166f 507565d88391e24f45e3219a723a2201a02c40c5cd88b3e16e7ab8d36d3f0442 682acb51bb734a6c5a8184f716a0b2bc890dd14d80a268d9ca697cbad6d2aef5 d6b05b529283e658771e12cb8db03e805a6f377515d2ee11ce71bee3f22b3f58 a3b057a1933ed0381d0cd89166e7b2cc8c3613f9cdd92cd109d86b4ffb842490 bd337f0b6ac1634f12c067493bcc741b21581dbcd5eecfd1386b4b50803a5beb d46971f3d8b4f9fbb71a71cc887646ffdf1ac95e40c8374881aed95f4e60d6e6 8a3249e79c08af225720d75c7736a07fa6cf863eea4eaaad647788c43b8f4ba6 f25ff9ae361b090d43b63274a411a1b5d4536307bf4c2aa857c3d515ef26335c c306224eb6323a16ab68eac94a38649236d1124f55b7bdec67c8961e411f1fc5 fbdf0163bdbb52808c79b0670c091b39c906583b379eab71153db54af0fa64db 01834dbe726bb20cfd6e8304bd14ca1f2ad2a92c6e0676640ba2306661d886d8 163b7b5864dea3f7fa0a7205004588c2f58326f0a93ec6f4ed42db249b46552c d4c93ebeb66d6eafebd24ff97dd405b61dabb318122177de4b20916eed1ddf99 68800a101d30377b7cc7ec21e5e5ba8f31098a1426f09c5713c59838faa86a1e 697c8ff36a824ab70fdedf3b4890e0e4d767f4894c7a4fe02753715e4e5504b6 169089c3de0a304c16958b81bc597b22424e08e4b44effcbfdc53cfb7ca32592 f7f4aac8edd8081a5d88e1bf2a1d8af7fc495f279183c1cc8ae9c2a2559acd6e 102abb0de31c22bd64ca47ae288a2ec0945640c9d8736361c8e7e5c70193bd6a 44ad04132ef42640626b75cdb2e4aef4bd6f8cbf52b5097db6ed3d89cf980c5c d97cb6a5a0a76ded4893fe31663fb9e463d6734281adbff52fb386a34c14f60e 109 2ab539574397ba927cb3765db0725c77fcfa104ebc28f5cc7df9590017f50c0ffb69b18235d8aac8dc6ae5633bdb798125e4563374ffe1101f3091e171c05b0696e527af64a6f26f5772c5e761662fd1be77b5a634cc90aac53560348d1ab406375a6c25a0637011115b19fbc97ff87915371a465a4e7b5c5cfc4ab93351d30d03a7f7eaf533d1a565b0466c6677447ddadf1917c7fd74d820680fc45ec44309b2eaa4afc831c86f5e0b6739f3ec3611406493bbd856bdee63bafbccd3d222079bdf9224f907b2932091c14b4966dca07037e2b940bad1f771349a8688019303e279500b5341d9bcde8b026ac5bf20adb4d863641b115f91cc05de3ff5b88c017c17f1ebf944b3ffe9cbed76cb5e62c4024fd9edb4789dc5242a85024f10400bd5e08d49c0da204e8fa386f0684c76fa71a0e04e0e4c85d243c35bb862e92502430d93b77a7fa933d244a3d5e1454f5156eb33d0afee52d5cb6ecc0a789e8a0a1206cf0af1a2257460156ce57d259ff26d97de532a85cfae7a32d9ecf6146a0bd90b978e139942308728387b664956d3c0772cbfb0c21166227af1ed91d7be087c2d1db2d674c9b6c31228ce3bcdb7749eef5be76a58493f55e4e56f6fa86209fa93497763dd4d43bd51c9e297c1deb3c45bcd62816c07319ce6a570e741d805cf4330989568c73cb019078b783ed9cd28f9ff8eec3fa2fa593cc75367f0020b904cba6412c12559102dc94ee4aac8a6a213c7f6413f9999feb72beae3dcf40b3dc3e3b6629e25510225ce4d4be09b915a5061ea2914dc90156b99f7b66e76049c0abfdc7c1da5ad18b1b7b4a5342a0070861f9d5794cfedb180445b926404017d8538feb0d26d31f2e68642a05a9842a76676f24813c79de2e9aaaa7f77db01ea67345bc597f1eac0f31af9af264bb9a3de2776f40fb96c05ed451b86a41f0d4f93c16e63cb57ff22ecc58b68481bee10ec2941f07a46973d9953b1686b0400b99d8fc707d2bc99c664860edff5c4375a0a6ee51219d96d1aa5b55142992f0b8f50d63dd0c7e18009b0e00e4cc5ff0a7e56727f8881d67a26372b85a92cd8082cea588a2e1fbf931f4511922d1bcc49bdcc334886a8cd2e4d42b30be533400df7ea0a629abe35b25d818f06e191de7576aa67604f087b3b593bc61ae30ab5023fd1d299aab64ac0005ba51e9adc25a0ccdfd61a9774aee4387ca5e08ccd940647db087089d6de9c507a5c370a9eca57014a955152166210061e6bef2fb5400a0d945ba0479d964e34a8321569d4ebbf86b5860f199a3c071a352097a3cc0003fe90986a13e3741408b5d2cc1d1e2dd4b3de0e6d943f7eea895af9731587b40df2d61cc1fea009fb5b4d71444a0062e54dd1a034820042a5a0c92329e4dd680508922657fcf444ab8cffa20ad32a2d9f9346c15e9113f51d93123eecf47f0802094660d26a37b1e572308238bcc0cd9d8fc6b76e85002912f36daf80ba94df0c41f1f63bacada9e91b96129f3bcc32ceb7fe39f8e3b35f038fbff14343617a05fdfcbdf0fd7c95321c2e30656d0dcadeaa0f123191959d4d3a940b75cb5e690c95f01b74b4e507fa357ea52cd17dd3909ad06738a5ed827ebda05bbb779b5d01a237b61232b70b62a386ee666a37e516da038d66796c5d1deb9925e7d38888076f7520ee2bfce81e1cff19db41e2e4b433a568ca2d020e63e9ec32da36b03b0670768237eb9f9f2a1e08823922726dba9e360876cbcf8d45392b3a069f3dfc061cae79024b95f0f0129f00ac7929a7d2af75c8934790c5cbcaf478ab9a1b280da048fd10f9b3bab48289c97c0b4338c0509e8f33f401519c19b2d6a91bb1a50c219d5d8696684568efd5d7832b7067280ce6de10de865cea3b9fa64fdcb5060a4d9eba424bd850bf3c3e3bdf86711b6717443fdb41a75a7be98b521ce4f3110e2f5e1923feafbb7c2f543b27465efe799991557b91aa02521e17903b3d9e890256d96357d42a8262e0753f78358d4e1d8f9ec870b721753c24c360ab7b96010435d4fe18f9033b4e01b90f2b474e0236e4d7e0f8fdeea3ec2ddf42c634b4f30eda2b4b57664a1feabeb6c5b830cd528d2faf4ffe6fbb5edc2df3083652f5300f00ce253bb745a9fa3e68f74c5c51bf76fed485f91e7ff87b189d5132853f7b0eb3731bedc94b8d44d5095a38cb5112615093af4a6c5c5a4055a1bea6c57dc40197fe3bd02cacc1fac9cc171b3d3c93d007c589d32c8ce39a6395bee54ab4c70c49ed8d98549c8ce7cd777195cb37734cb7c5e4906b93ee7dc030ca89cee72801b28ee0476a687aeb29892612ec89d4a49ad9113ae7d90e7f5104d1bbcb8db40cac1b7696e00f0ce928abf27843c3f510297d6e61771b54ff05df506a0b51250b5cc7ec66a96eb6628c114b76772ce95720f8be86e70db1ad9043316189ea3a0427f34696bfd245a14fc0de21c86b45ade67b638f262e9db2f1a2c37e56e9e70e7576ff2464ae7aaee52187953e75bbca98808661124ea8fe023133058caf320aa1dd4718a69db643e74434b2a95173adaa8b368a2c5be1eb9b8f9e30c55f5d00035c36f18ee46d4ac09e9c5eff16c4ada6ddcad77b4c8569ca691ff68555200a2b4dd099ceecee0b2b201f289009824e05ac1e4aea0ddb5326326300934c10039dc637d91ab8cf5a54e5913fa620681a3002434126622bf19e6a97b9e8a7b90f9e3f006b897d570cf860a0b5bd967e04e8e8495c9dbfbaa2a5ad3a02115a1f0459149d0f19bb5bf9a79923e6a5be882f96b4fd0bc7f82c560fdc003735cc5807a1625f508e67e150e7c941205d0028c7ebb1f2c092df8e4a68d68d3dba310e06e68c82cc25f595a4881994c1f80f38edfaa8c8375b2acbdf9c99808dab3cb30c000195641983bbf143df4d0fbdcaa368370be391a4dfb67608d9f708525a8d047607bfc17a6909a140fd052a2172ec5b4a09bf4753579af625c53ff427fc480062cb7b2b600d218fa80fb69c47f1cca0b24fe667a5fdb18549e342c9626c5209a33dc4bd4cf490d5cee8150294a62e6b47644fa1cf038e7d8bf410bed465b002628ac65dae09897921645d3babafc0b14e33cb1658c8b1d494eafe549b934d0f41d9d79effdd520a743978d95c1f45bf00b7937be5a5e11d8c150ecec6a5b2006cdb59a6f5d31fa6a9044b3481914678a4185ff2c8edeae59cc5f794ec14d200e0b02a5e054dd69d2e084ec1e3f19bd18045a3e7d23364dd701457684a1f5d0983aea621ce9b1d0d4c2bfd6f4a8aec14414a88e72b2b4e2e60586d15b31a8709d5a543c2d40a68d425ac62eae33a1236fe2fa4db40100697c86e378afcb827066d2fb9a2a8f99b1471fdcf57e0b1b4579663504ce8f5defd276042bafcfff40e5085a85b84e62e90742369840db4b840b002ab0aaf9e3dfd66bfa2840292d70260a5ab3a2dd78cec36eae2a3fc75cfc7409a7f25b920c47e3636e51a2948430931737e2b54317ff7e7a39330b3de5d9ecd168a853604e4fad95b906cd20e4e066be7ea8dcbb55a7460234e2d25287bfc63a9e8d2b483d6c9f5b384fb1be43303b4e0f05965f251b27075d0092abf046a41f9293a546d055917046b6294dd8a0cfdea4e4849f947601b71cebd532d1122bf1168352aa3b47b804f4df5dcded80a9e4a7c5ec51ac379e5e6e44506a3b8d9a8ccd5edf4dfc29b8d45510287e1dd043bac541747509b337340c861b668a96b0caef40f839af9c3a2ec7d1a0b003d08e5a862203299a6b33c7e90245d6a97d826fc3ed8aa956f716102494f7da57c0c80e6e6444c7bfe81845b3d6585449ac12feb52be0540db7ed561969beeb86100899600da327d85edc873df996aabf3850d10837f2cd2cb1f52306d36828e4903ed07edb1335be9e2ca601c8a769df8c14544d570ef7b82c43e29c2c756b15907f748696e286230c1740a84b7b3109d71fa0c9a13beb7ee9d7a5cc7a22bb48a0af50e034e682e77eee558e3c4e0fc7d1997beaa8bb37e273e05d93137417ee407cc7d0f4e1eddff640966d0899cc9eb4a15cfaa5cf7ec4053b91a148fd9e2010185e55f18ccf8c6264735d7789995f424543e9cd6cf19a90a40e2e8b3b5d6970f60898d8ca9ee619e1ef72aa59801218e88706dd2f559128931868935f36bdf0f531bcc33d1415cd6b371987378bf6de11f38f21b517002990bd5c7a216ba660b281fe162661405551e7f22c522c03e4a84715f030189d04060b9562d7e02640bd7d46ccc62a1441086994d03de0f439908366f42063e9bcdc3923587853e010a8f4037807311f557e91492309dc1fd978029c129a93e6ab730ffefd4a7e36b09b6cc6e6cd55f5f66c103d73df7c0bd5529f5ced6db1b80ffa1e7e4a79809f405f0b941c1426aa3aa0b986eb9b2fe233d069374b6129bf43164a578f8bc97180abee95966f374a152519229421b10c4398fd75f81b7586e061028e453ce97e5048a6c30ff82cbb1bc5a3842d355b905ce071093ec72903008bf21bace5173740d0a91a24717dc2da92ba1566197170bf5ca68bcc983abf14c9f724ec11dd38d01e901fcc15e7a483eab8390166af3777b404a37702b30499b29b5b8face9d5505fa43654b5768e86c762f34dde105419eef3d278ac44b1ce94b8cfcccc9b870057a6cf3ff9bc832ac073eab16485d18339fd61c9e335848d7a67e00885538b70da4755c62c32bab34d733a42a378eae4065bc7468af477cadbc0a436883e3440a50ac5b87ca7d006ca26dc9d4f9f18fb69a4e9138746a619f7021f2a84182170874b35e32be8ffe2ce6ad40dbef331872c1e37cd8d5aafdbf52cc9ab03b4e9e08f7e01288c6fba50a7d796d421743e2f0f0aa69283995e497cd921e7ec4d03f0cd72315c5c6fab09ca88909d87fdfbcdcad94386fe69fd7bc597283961239c50f9bbf205898b551a48538f2c404573a41a472349ee0e0ccd6eba51b50834480001070c066202a9d067672b717e48c12b56084ec5f5a2604f1147ad7aecc08d3091d6c665fdcf6e6d753f8eda978b3a6ab1b0873946b221db0aa32eee159dccd019422f7dc42d7d8bda3536ceff5f5d309fe0e52072d175a409d2ceb5b01088c0ed8e0b6a5e1ae8108f7255dec01e61e9983cd8293bb90afeed229e3ecd5d412022535644b031ed2e8a600988b1eddc0aaff553c1a37665e34d48b1ae5d8855e067e54e07c65ac11abd632e0fb85ce84ab72582fa1706387d97e5c90b8402221000ccf28ca27d89062093cf40613e170aeb382d6fbc4e5f3c5766cadd962d40b0d275ff1e57060049cb23434b8591505e4ff4f33b5ec7c52cac57188eb69511d0ce5bafe933ebe096290a54b2e6ff47b6793897f38bf3924043c20d1cbf0300e010a73bc31a7f986f6c24002e8f65c2a89bd124f799fea9eb2a5c960f14801800ea0d204a6734af867a177bc0e738a4763dab837f947a1b2607fc1aca9649d290fc4e9eb7e6250fbc914e68485cdfc5178cf307b2e7e7a1b92a7b99911bcf10d031cf4d8a00c1edd02b79420a85215896723d67f4040c9f64a90482cb2e727e20b20e741ba840f844881f0370fd093d978ee6b36106722d1e4bf3ce6b6ce804a005a79bb43bc9281d0c2905387fbab5759128c6c0729679b57daccba60655bcb06d706e0f3dc843ffed0493ba5808c9fd2ba910e8bbd10cacc89f98d6ea181be030c794a9ceea7f1234f7a1df4a04158099c2bd10366b75753a457f28bbdbf0b003ee45243bd69538c7808ae0fc2e6bc67ca08408e476beff3f31f96bfb12df504c5452fe546ea8c4fe2735e945da1a5ab9569cb0b458034155c70ad9721dc6a0a79b2806d32bed1dd14716b4420052bc4a31943e5781479ffa95d04d4569f380a0667b21486b9fe028ef0e38a3ccc336c148c6a8eb5abfcb0cf891cc202f98f0ac7bb2faf0ecb8c3fa04bdf3cf97a4840707816d55f176612969b4ae9ddadc9033cf2c771ccbb27caf62871ad28c362772702f0801e62a29c286480b15b0c0c05bfc9222afdc84b3a6b376643ccefd0cb20c0d3dceb4c71828f1d02eb1d52a20490ef35e86c034002a6c15ade69bd290697dff156c2d32757ef578ede89619b030622ee254a99c7e6b4c814db98ec2b840f9f855073728a46461cd20b5ebbcd0ba39a7e2f663702a896a2a8a9bbde904e3aa0109e771b589c233ca3298e2f780b5b7ae2690d9c83ef2bef308bfcc994b675e22f55ccb02d577e775af863c3d80c3aecb2f71a2d6d4150c6b2cc6230e735f8895d3758e8279f12b133e2f784a0027e884d42b6f29fd99a2e7a650a80c458c3492cfebf6882dc73c4241d2d3f270f6f86ad5580198c8747ba0350009d505a69f4219d0005175ad000cc8883f76a0c80563ee30dab3a1fba92df4ed28e23a43641b62224c741e4c854344a5608fd029eeff1ad9be6a3ead5536770b88c8d8e7d5b04773d24a249a3f3f086110aac00ad9ce3a2a45630d01c9563256de8941c17a980b8d8ee306d13f5ec2d4505ab0acfd5463d61ef55ddead0ac93e5127513115c5fabb393f292fc5af4711a6b2908c58dcbc1743285aca1b3213e368d7eb5270118a58e7a41cc3ffbb3a7266970060e1127ba0446330b28f5113760753c74783edaf4a7291ccded3627faf64f3100c3892e6e07ffdc4f2bd7836ecf1af568b56b20ca9fded07dde63bd1e98a0580f084d79ee2446356f43b5d6a39b13defa6baf4a444eb7b211d24913033e30900a73c1544e023ccaf14aecb16ff7933544d9f27f8ed0c1b3fc8014635d6db1660955957ab5687d79cda35d6b0c48f4ea3d504c07ad46ff79b29c6cf9d22238d60d3b3b3abece46a228f53b3b604c21a669cafd94db64bce6a6ade854a99f813c07bf0b4190910204a594416588e483bd382144c989be97dccee2fab64c29505d0a1637f003e553841345bc0738a4bfe6a0047fae501a2c4b765cb829bc415bd20f06669b32671e30562adb1804c9f06efd81a9791981f0e66669e481f06ab5e60ff1e3d5d04f77541a8b37189299e9732b2caad7ec05d6d539c6a973c72a061603ced3b45a9c30d479dc5e2abf04094e78444de011630bc14ef46ad9397afb010e2d631724971ed168c92395b2e4a4faa4eca1f1a4f4234e1d5ba1db3d38382a012bdc552d7a85abbb116e92c0904078179b9356574c0776ef995bca962de0f600d97b23a31c780efeeb90710faf591f55dbe4375719575b5ef47494a4236a1a0bbcb903928330b031ac9e26df4d1117391e58ee746bab3b9c001749f13a072904c861dbc8e98d6638e3f4c9bfdadd8dff8fbeb02319f139c662a0f64c238e53007b065b4e40476922dcf25d6b940fbd2d23d0960133bbe7f5e76c14d094ffcb038c8aa436a74ca1c794adc53a8f8d88b75f870dc58568c70b88803dd35944fb082015403989f8d613ff4c73cd3073472d2ecd5ffe05c62b17708b85d4cabc380debffb1dc47a2c5622ae60fde5d8b42a0b50ec65a96c3d0ff5c0566118936bb0a066218c4daae3a29ca18df18632675a1d41135f3674e06e4bba29700550af700ae35114614576580d38aeb360fd15f942f93a198fef462c9333a7dadf4a26a071af4a5f762c1761957889da5d2c46c4b765dce4878d0582cff3cbf785263e2031b81cc165de524226bb781c10e1fddd68ba07b7ba0b7afd34e43467f63bd1902e49f59e41848402a46e6612aa84209272305cad2cf163f507761438a356e2d08521fe168037e6e4f0551d5daa08e3fffa8e759dd7a15d05395344b4bddcbba0ff32c5a448d978b757b164e64bb13d06c1d191519282befc4e8c4e117d520dc050f8acf7d136c248a1e2737aa3e68a80a18c26a602b2bab791c66374723b81a0881ad60279ef5c9111ffd86b839ac13d65a6bd9fbe32eebc9c91b53947d4ce105e8a03f2f0c1f63b9a5f2a2879e44307e1b9349a03ee622e55d7d9b029dac07027fbc310b1f84bdb801f6279b40827d384b818ba4bde847771bcbfb9749dece0f1fac2936633ec35da024fb8d5bf7870845fb5d5d981050e4b6267a4563af60050784cc9c1ca6e6666965aadfedd0850b0195f3730594294a72c6841b0fad7003b2ae869bf6f02baa394a5c4d5a254178c36c0f6c5e07275d78bd9e3bb8f35c0ef3ac447fd2bf1e93c8441a965a40210037156c0ad09c3e2cd5d6fb1db715e206d85147539c3a2a57f6e6cb24c172d4cb79d2bf69c4fbbb186619be3a3ee6d90685c668b6dbcee2cd48704aa5b983478fc00a273458e044b167706718b3bf3a0887a5aac2c299f84bfa50347a6f94298574404a89e17a6621177fdca18f3b950c81076e0d0ed7098f20bfcd9312bcbbf9194ee3b3893006a9d7f6e456ee8b880d9e4c0555f82b6871cc2001bc2013b6247b9a11bcc469d268660edcdc7310c4015a55d2c16f3e1e4456e02cfb15852ed8b08d69981bb5ddf2df9dbdb983cf8a07ddfd0d4304990050d185268b1b5a513cd30198a5d2119e719674f1f210b8e709db3b8852af23e1af2f80eee291136b3c8855741e85f1bfbb4feef6575f7afa08820ec92d1e25a0b3dcfad4a398952c1e308db2edd8c1f8b9ea5d431930231102f61d0561c6f156e78f2c8fdc8868ab80e27e9dc346b7a894a2290f5c0a25e701a468128b291447086e8c4cd8298fddbc5a6bfe724b30007cd6d5d96ef34a290271643027b820a2b5a421aa861c5ada4aef3d90f41fdeb9f90368635fed309b0c703aea81773b8d5bac2c149504c8e9e959da9f55635954eba89683aaced77b02f6ea5e532dd35717f9cf145ada78a03a0aa2fcf2f96339c3e566c3249350d704decb459da508308724aeb097fe49507db4c9a241546ebf0444a613055e91fb025fed75f11eac2d11e38405d2bf322e097f82bba18b0242b7c0fa0300c0bd6705d8b2ef4602765ab42403c26e2f93fb9c1bcd5ca4eeb8a720181190062cfa8203314ac686cd33c7d17a0c2a4ee391f17a917b2550a918ddc3d48840f807e35604671d15f822bd84d6fdd478ec8e3883800189e5b9333120babb27c2d7f35549074d5533eb3e9effb39c34f1e48410144c8aad730667b327229d9bf7b34281a60fe692ced291ca32300721c703d2a5a05bd041dcb95ceec68fd38c5070933aba079902287954f15e3a267fcf74ca95d5ec5bd730da32970d3b54c398bfd8aba902e050ab0cb28e0cdf5a778a9555aee22bb282a19d947281e9e5ed4ebe51e62c09508fa400022b75eb2a053b3ea18793296a812ee39d545e524cef6ef5a9f9470fa8bd8b05e62bf92619c20e66b5c40646ca576add5f396a71ef2df6597a8569020ad23caca82e05abffb7202c57c9874eb11118efdd549bd99005db53d59f350a4c8098fc00aee7ac921a4910853a98ac739fc457a763cbc7fdbf30d6b84d9c03ef4c4e5264f6f4c40f2b4a96105f0a24bbf9f783efe180f321fa21ddfcc1f000aa92823ffc2b573735e1377e71c7e30681d7e4c5ad14df41d1d440167d4bae09fe1ead3fce7f59f5fa8204b7b4f4276a90eec6d9a84ff72a65d2bbfaf7429a066794987432996bf120959f9d11c73975d100261d4dbc79fb3a348512d4c0590724824e7524b124e893cb36ae31bfbad002bd992fc0872fcbd92fe4709291750e50da7cea4dacc882851dfe91d7c517bba5b37c444353f1fc375d81d8f885a0066086e8d7920a7ddd8ef8769ddd9494207569369b56db79b05158e8d83ccdc009f989019f354d7c430210a5ec0ef60cde654f923293ce5149f9d0653fb90bbb0199e30b2728c8d27d71fe48d75b8bb58e082930f13903d121a81032a3f36dcf0e0959c8854208af4723bf52cfcaf160ed079d2e3cee5ed394cce41fdc3e81c200e7fc92eace14c5d7b3ff0ce264ada30f9dc08814b69c19d9127aa16817e1270621634c735a4a3016262824c35ee1bc28ab98e5c9b61a10f2f58aff80174ca107ed3bffb66b0df1d42f2bcd206c12bc0963ec2a7b7998b7a77894f673970c9401d6843acbe4814d40e21c8e5b14d841b80c55db86830054c1a8136760b43ff90dbcee62840119d91648a2c284c72375064e43a18412d30e05847fa7bd4623cc0dd0a5aefd575e4bfa66c2a43a44c1d11688010ef167ad09f47d4ba99b5cf0c002e832384356159c23ed1c87a8d44164dbba2a6db51ed8f8e8c018aeb9daf2ad04aeaaa8a38bda081ac6bcf68b223e5607eea6c35675ac83dc1f63d3bc1b77da0ecc20c5d70c0455484921be7dbdcfaa1880d9b0cd42d61d044706de97b8540607f8dd6ce1b8d84a76d3be26feaccdb57f4975efe99b88aaa91c66d4e6cea9210270fcc25a96d56557195f108bcbaa312dd90b19d77139cce0d45884a22622920497ae02c4632a97f5297e3a348251c023cc7bc89d3920f23f9009b17782ce890bec77fa359f14cd967e85a9357157321e820713200555008b10929370506d22017850867feb8ba0f89989a2cc71810b612a72d7f7d4965911d9ca7828a5772f0a90dafcf4776117ca1a012c3a7445e2abf6620606414516d3e562a6925bf83a0e358c22d4f2672f1648818c3a35ae3009cbf3dbd34e7eecd69f5e9cc54d646a025faad585ff6dd20a83d2ef8433578277a4ff1ddd1b46f9f35b2e6909bb446d06987214fc9a1471ff80d0d37a4df04e285354263a2b9fc8a842ffe5e23c6ff207c0c031af56fc199028005e87628c897a0c6ad4254ed87643c9a5f4dcbf5dff09b432867bcd7ab1040cbdd17d72036313fc24f6fbffcfa6684896b92aef4dfb07c6c28b6b31315831fa6481c7d2b4d3ab3ddedcc0aaaa1bc79171eb8ef676590dda214657362641f440ad87e70854f6275a7b6da27918e4e8d16c8957c70d650d552424ccd52431060b64f996b319a91d4257531d8b96fec2abadda7c82f8ea0c0f2bc9c457cbbbccc2efe29f70adaaa233ec94f36a1cb08b9dca32e51ada68074d600f10b393db2ce0de4fcc6daaa2b9296488f8cf682e3a4d49f1faf8fba804351847f218c87dbe85687a363911441c9b558cc5ed454f9d9fcd263633f287016f21ce184814856dab7936bb6e1501afa43657b5cdc69fe0fd51addfd812870bf81d159c3b19baf08a76aeea0fd6c0dc762e755309d1d6f3d2a1cff366a3e807cd549d514dec42bdc5e615d8ee5783983792c534dd9da4912a40ce57c6eda10ec29c4fdab90101123e07ebd65a35e91db03c107019c05811f919fa85e6cc6a0124f7eb2bf6e97e640d298ea69f1a651a98db3aaa6dab9cb3639508d7bec2f40a0da6f693315d4b3d3a1d0a17bc1be300a3ea90a8ce6350174fdfc9d74d4e970b8658e3f18877a0d52d234a809e62b04607c7183f44060110d65e3faaca400004427415c8d97ebc15320e4f79672e8f959a29376bb5546a8fe539bb5fe344df06498ecb20f169b2a6c7ffec9c48e23e95772f4e581d678351e51a37ae3c131d0cd939d9e23d0c47475314fab9b8e1745f383089c35ae4d3d8a47703a628a76e056b34bc57e918e55f2a5ecd35ade34eca1abaff86210254e51eaa6166506c490d85119b88d7726c17c38878a4c7bbb4b44375fa7ba85d47e345c97cd643e4dc0bd3c0f07b5e4aba145fbb608b9f47c5511b83df8ae9730e92019b0d7d62672d053f904c8affb4366a819b1f057e1d0a165e065e7b23ed0832c0ce4e4379ed3e0a96b85b1c0166f320524f10def2803a4f767b2a90f579a939646a6de77693930ff6eff52b7d9b962013a6aca7dc9140c8ac8b2df68fb8b72fcab97803c280850ba3df73574602b238d826033bc1cd2072c572539b47f71bb221c2ba8da231fa0a76dc35001b0f3cd38cfc314fe79d78b2a1d18474c6bab85b426d08c1df8faf02a38f035ee4bffcc4babab8558fbb7733f273bd89f6bfb1498fca8ee78b971200e7bd7c06c0c83518a2db75fb6fb0f765b6a5fba1f76aab60a210dc26bafebd04d105925b78a1f2f5fd83be5a9f4ff0944609b3458bde93e2f0d0599b79b9aa03b80ffc056e001e43bf73ed2587754c3fe62a1b0a597cb5c3f2788e41368d110082045b3ec5ff1cf549d2bc3a89b1ec602b1983966b618e74782a506c09c1a208ab8aea966da08289ecd73f9ac6970e84dcb5c99fcc13ab1938eceb7d146669023e757e2e27abe42b28d20cc72da606aec4d895a6c6a2734f85b6e78444fa720cf30ab910d4fbf81a7ed6c858055f0655f4d0b4fb37041a37f97d5cea13dd0c007b8164d579b377bba70c9bbd4f98a9cef1fe3d7c5c9435daf70ecd1827b3fe067cfd63b611415417d20e85c1e89740592ccf4a59844d2ba40f2a21ea132205029b63b09ef8f5367272ccee350da5929b671dd56a30e4849eb54b369bb45d880d9b822a4c3dff9486f05faba6a81f3c0655df686f520df93dbc1ef98fad0e600017a5ff725f3562cdb4f29a9f4bf5d0b51a3345ec6c7d4ded504a11076f379f051e539a6d9ab6d06cf26bdee71283cbf0e741ff8e10c9a1f5d53750a836e3c00ba31af2d74c3f2bf44a644aecf2510e21e0b4e0ab997ce26c82f2f699835521046edf1258b097204b62c1a070dfba6daf55c67c59c48b28d1a586cd493713e801901d6f1e717123b75b9ed1164ea44c07ca4131f1cd2799bee2cdf29735b0b80699ea5372d18635fe2bfa3c950e0482427df03d40c4adc50f1db42a3e94a7af0b0e479064193f147db1ab422c340c6e1a67389d89047f8c5dda887bc78600740094716c074059508d333eece20c516a628e3deb722df58009d96475639319ee03086d26db422c9b7fc7036d7406746a8f368b2e903609b930fceacaf151e9980f9c58de852374785c2d3a14194bd43f7c4833bda2260086c4c7e594be2326b60f05cd27166aa69154f1972251606ac671751623931dd32f433990ba44f4d1570c266e1c5464a21b0a3c5cf66a3a9c357b230ec6be5b1a6df533746482fc0344024ac8e69f61491a50861e7f17edd012377dab92f11283a46ba0540d21221e040fcea1ea30fc16bb84a302260211ad28067189996e5338433f3a181eecddff9c0ddf7f4ddf64149b69e73812adee8265368fed21b6c588d68067b4886c8fccdb04f304fea744c0fa2af7b23b111f839223ed247641b8c00262d506d7391bfa6a0c170c14f5602762f77f5251ae2367540d3f19b8b22eda0cc374fda732eb5330001fde81195a6d12e7e1e00c2148c308a11cbf26a65dd4666596933653b33adb0392f78f4ce83591266dfd94507280528be5d1554bcaf86ccd6ab62ca2004e39065a79b85ceb47600ba9cb33d71248bc42e3ce447db2b8090958704907b6871f0ed7a8e6c47d315c39389d75d7158bc79ec293c1d7725aed037f7940764d85740a8454e9f1de641e90033843d67a19938db831833a079a483dce50a34b89c4bd0f92061e9acaac00860aa556e3a6d77c1b4ea36477547b452dae3f4657f798af0cb14870e5a6af4a77aedcaacc8257299a3ef81682391dbefdc7282b4f052f550be7e0bd6dc73e9833c905f7db40c9773b4f658564bc277d683950f2ba6871270b34880168eb8805be7b5c90d0533c811bc406711dd529b77cae3ec93566adc407fa33cddc6703b16a4c1756ec5eaadfb52e8ece9af6faca60bc70a79ed295b601caeb1f563e667145f0dcf626425e721b1aef9321100d56d190f5451df30eef07ce08d22b213d1414c3682f8baf9cd4cd40a6aaec89e8a2e2e823c18eab98b90268308ce9b4e55a45fa6b9e67eafb71b9047a6730093f4d82ed71ac05d96e810dd79ca714eda18deeba0f7fa515725ff1c738c9095abd384044de3a27fc7b21006d15b40b55340a94c0e5724c85007ce4787d37e2e08a62456aed9b79ac8ec30224d011447b9ab3e45c36b2ea8c233cf4085c6d72b46f6dc461346d0f9e802104c99c0b44fff1f27e8b54c87b12bf8debca6c534c83e4bfc1229d25ea5b160208393f9210e4352ee401641b6836d47fab468c34b6d67ecf3cb35c92893a361b07d5ad4a9f3b8378a3f1326a7d0455073aafec91eea2343650614f5db977f5a50adecf79bde069710420e8fea3bbffb97bf1aad738d2aa439588eed39fb642a30a532ed98fa2cd1ba0984b4ca47ec6c01376820985bfccbadc84feb67c5f10b50e801fe2c66f1a6d106dfa416b010fa353c656a18b3cc9e7b67ae64f32993d8806ff36c7d0fd9dde762f0832fd177ff6abdd023b79820470c4d06c4a216b4e1804d6e9ed4a9a30ffa11b933d89b92fdfbf8b1e7e103fc8899027046401d9e1f905652e03458b59fa8429d5c40feafe7c20cb5753efdc6aeccd55b0e32884188602f170783a4505fe8961a3fdb8698eaf5311b5d4cee97451db95ec6d835640eb07fae7a1d5b60721c2ebe336577fd0118b3f8cce7a6d279bff09a9259d66212408bbcce120a2ce1dd45d9c01605a6373e7a9ce7ac5e8f756b21b518eae1f128f05f95d22bfa3b4858e9dfe7adbba447ba09e90c7438b24c974c921b82b8cba310ba8f3b6a17c05aaf652604c15a2d17f47915b4548a577bfab8c5de6b9268e4d09cd6e5fc3b4d694d872392983d4c88647b53807c72129c03b429967bacab4620e38b7a6994d1451b0d1bcc3694ad5a98cbea059fcf9d26dfcd87c9b2611c5f30ed80f22363a671634f82ecee8a5e5735005c1fc9519b8d3cf79165654e21fe601be71d6c665af686b8940e4a5ce7d7460f70d35812b6dddd2ca1b92e3db3883094dbbbcb55a3bea997e9e60c4bd80499eba49067414f21156aed234b6c1a69b0f30cb8d708bf34a43fe2d1bf764e84a2e22d23995b89812fc6d1b2c47ba82720f7d97c279383135c3876c1173f021c312be46027a8228e931a71ba185f784590d9a09b789bafc6604a37b8a0b0b3090ed02a8c886f39f2bff159121b3ef0d4c07dde6fcda2c176638fd5d08c1678cc8becaf57c093fea4bfbf9f173918de49907f8d44fd477ef07fe43e5bd07c6cf2f8ba369c4656a5ff86389e928c62e346b02c7689696549f38ce7c9461a11405ff59b813b7fec52e8fb4e55fb6d3511ec5026378c74d73ea9a024af2c3b4fd885c017914ce7d0b0258d3fbab210541068a035cdccda0df52fe83ab758bc953e45096fe20cc575305acea52cf30aabe9d5d0fadedc5c16bc5be2e7a419f13a525ea2e482f20e14ea9236f59fc9bd5f8fa7a0dc7b7c8c45ccf354fa2467a91211dbdb2c73fbefb9a33fea234add273c2530009feaea50320102f4313301b882c080d2627bbe5556aa6b005044aac9f1008bc0abb7d0beac67928a451623eeb09db18332e60aefea1d07c6e719befd54da3a70f0e5d2b7ced91ae5c620e7e50dd109244bb2a49325972661b0084a3108969940197bc542f896909ff33c8eb4afa44019e9e28408ac3082bb41a53bad633f33109bc54c7dd2c34eb8f7d222d5ed688e313581673e26a294805b035e5eed2dc5d0cb3170db160fdfbca232a8f216c8e2c0adb9dc52d1411c42aa1b3de2746e4f90d6de5785c50f671b158f1c383829f4a9c7ff02b9db0deb49389e9a0d67d3f660829a5d982aa0f9f36bc8c43a7f73d745e198f90ab5522461b5135fba79249550ea9af2ec14a4412c0a36db71e72071e47ab14770c29cfac535b352920ee4e240c6674ca76be39e63846316afb101602e3f22d9776b7f965796cd310ebf1fcc7001092d4699182ad99b836b48704d663c493fb073643f30a50c3e982fd6342bb035f41c3d846aacfd45ea0584b656720fce0178cc9b55353fe89a0861b5e30280fce01d5ac0ed99428b2a20dc74d37f6036282eae971a2cec5dd4bab5f040aee0e4f4f2cf412baa3f94416c77c76e2e2d60287b5d4071dc88a185a83bfc60f010be5aacc7c4ecb42830f80d8e23e141c3c556335e0fe4ca52c9728a0d5554aa90fcbc533ebf11e39a5aa2e3c67180502f8b84972570e44e38a333ad9e25dce2302700eed4ca6d9536c579767a1d017ed28129d2c11d5d5d838dc73ea9406dbb50c18c2b2fdc8051a596fcae1b960a8a80b85ddf8b0b630a55dc9407d1cde281d076bc20384046a5dd2c0119c247a0f54bfa4aacff29218c46e6784e26962d0c50555775330e97640982447796a3c9c780e394111ce1257e50eaa8318b76598010e6458c00d070ea208ec2c6b84ab84c9b96056f2b6b806d13fa78e5fbef9fe9a057b77b3d089f06192eb0d97c1255642f2a101b7e2bea51e2c7243a257717f0203981213bb89ceba129d8b3da69b3bfa361e61d27d6b33b2ba5a9640365583fa056eb802e125c4d8b05207c11a6b63c1902ee8061d0acfafec338ec7ae405f500a0c948176248899ab96289407b2a0f9fdb7bde951c2f34c0959361d71e1dab30300ac4d93cd7c61bd2aa60cef26889a61de24f2cc526df392671d111f047ffb0a2e28074dfe315108c299dc32f2b528345cdcaa91553f580679ecb9338625800a862d1a96c86d14e25b920cb996b706875b5dcd58c2fe9079f3a229cd63506602c6ab0a980f23c860a21d720ab13bdb5dae34e4bbdd2b9c21855e113f8bdede0d63ba000e6c5f251525672b99176229dd7246d3238f89ed34d69d7cefd0198d03ea3658ac8fbad80f44b1602df08369705712cb7c5818f87d4063a27641eec90cadf9ca154871222da6e60815f2ec08ba3acc31c21ccd3357b348450ef0361c0925f849e754a914e6062c1d96176acda35fc6f639d967aab7a825023a036fa4004cd7d7cbfa9769491244409616bff61dbc200d250324eda3063a32c9502f8d06a26cc8eccd445d1a14abdafba0635cac7978fb18fa7fed7e2e31bb4ab9806606585d465766b44ea8fb5a516cee70fade1751baa5d4bc3066a8a90ee0b7cc7402ad642540990254b86f08ea45753ea104baff6b5fd57fbc2148f92226866f1a0ca782835dcb7599fb614f1c26030e7b2de088a2bf56a95adcfcb297f5ed63fe01aa5b95d13cc99579c7e6e8c4e502d6887bddb3b2c5f6fce7375e73070f91cc013085152f71b4763851aa83d6c829d7f72a2e778723d89ad3e77149047218ff077f3cd92091a1642c25b02c581a5cba5d047e7370add238abf3282ecd76946308dfee51a8914216eeb339d4978180dff40847869922f7f040344a77b76b5eb00333595a120c628c2ff92e4edc44b6e5efc359e0024b2d8459c1559149ce1de805e4f057399d93d47404178649356289c7d9fc29893bea6e90afb83ce184771b0b21764b1add18068b4e76fe8f73138c2a66fef7eed06d005b59adf5ddea68f60de53c378e744ea3bd6fdca265dbd761cc641698204937239991e1cddb49b89d0ad558fd3a776a9fec7b75c61fd39ef9c543749920a08dd8b89333ae168db1d608e8f9d5111fccb0b6e210e8e47dedc92a6f40880377613825ab4667fd0287710f98607e291246fdc9d0dfd5723e56f521f5c1ebf56ffc53e498869b74c40dd106018fac9787b0a836d25170986fb2a2830d6341ed45c99bbaf4b1a3893b30ca0f95b4962d0d5d966d4b06fb53a9cb0f99fe8f2f07f8175e9f16c6b74e3c5da700d650138abf1c55a43e69fbf2c9bf83eb24470ec1151baf6a1f829d51ae73030d9d5cfc6390d49932e4cb31974aaa2012d4a3a8c84588c01fe1147e463265990ff26720d70b4c6db955e2c1fd1915e47c7089820982c5bb81a844c8e012ebac08e1eb3a460f00a7a3435ca40f6a5c7ba8ad03411f4b2e3bfe94707e44f910cb010ba2b6c108d102c66f6a4783a06bba3797057881f17856a157b2e4a2393f3f0d1fa764523f363a7a8b59f10dd79275107acd1540775918677463e5a4a934c801ba7cf231688d9507794486dcab53bd886276e9ea13a2d7740ff0c387e2dcc104b532e24a39c6e3fabe529399597df3e85e23797eb505b17440c9f227625a6f0a8b73c9095082b50deb4c6c105a1e9b0bd4dbe0384eeddc8ca12f3091f1aa4203cdc7eff07ff6f27a0b825b192859be9fc6f381894fe98cfd1034325c04c59602e927d948965309ae621da6e167be1e3f2c9520067de4ff6cc2564691c6038507d3c8fb09b10e96e7a8847092765bb5d6d38aee5d39940608837be71cb24d8f0fb564e354e6accce164bd45273ce198c0d0c819e78e868f992170b0efce6bcd0ffdaf09c94b129113f8c6ecea34f30fcc76383ed9e7978e9806395ab15201590d40aed8a7bae628dbe33da05152d5be7d1d30b634bc98951bd7ff95ffdf018a004375f5eb45f5d940d98d8d1a22eb4485c7fd0857dbbe18805bd2a255f22dd60d83c26dc38179241aaaf3c92b23b61861c21d25ea5ad5ed07c471bf8b8f1bbc0118754e125914f3c2dc92e05af909dafc5436880f8f2f6e2a922a39cc54b241079a9a796de6fd96cfc3191c5429d8ded0136737462f822287c7730aa972155104a16f8ff9696a14cc8c83ba3dbe17fcc453910df037be3ed3829746ab68321c0743b9877196268c16f9f51d09f0f89f828f28ecb171aa13f15791399dcc8ee304fb20cc3b23e3330c442460b809468688b23133bb28a1b9c6d45ca8344ae7b70c40b7c480d4c1beed41d66326fc8b40356a285cf81876230fb2963cbf86da8b0e7f40fc5892b6103f2753b496fb65a4109fd004814ed6814fb8006922f6633c0e91bd34b4e2150f9ce1b5a264f3deaa5ee707126736cece2455eedccaff4a2406879de383ccaa21aa76bc38c9451746cd1de6152d3ffac10aa3d8d3880fdc3308b775283bacf20df252f0b77b1ce0dd87217f0c6045cf761e81733b3be4b55f08c14a19ffe72883161860d83ea44d0bde966e5bcd8cffa6b2d23cca316121ee0d544955a46cd3ddda4adaad6b3067c52e5cf27a47efc0ffd238f04d1725d00e0c6750a28bda45bd6a7230ff62057e561a0798aabb90297eb9e6ada53b5b91aa0aa24b49b81877ed9d0496fa6832b40d44872607d2f9f757c64e1b61e239f21502f7489c3fe2a5c4e0503a37b880f63348a81ffb1d9f47eafe2f281eccecbcb601e632dc45b91aa739939f67ed85218e974b0598c729a598770de520629d9ae80ed53c8be585b80456e19ba160fc9403f5444133b54ac109697bd25f524cdafb0d7cfc6579663cd67d01806becddd1e41f603cd71a852f443c66f5accd61c8c901d6574233f3729ce9dc23b251e345f3c9e26a9fde480c80a86196195d820c8d0edb3d643354db09efd245fec2b5fa87069bde57dea4385b6a30b8125e7576640629685fefe514a297bb3fc1705cf630540cda0be703efc10d14b3150deac7fa0dc2141ed9e2f0231bc073d74b7275991df189ba67a6a24136f11c0605bea51a0e88a87a4b0b732f296480abe26f8227febfc48469b0f25d5f89a23cf2c661fa0d363f0720339f76ef75d0ee7113b966b596c51326fc450d70e44450c6a623820f06991c7c896c7b0091ae83b4a4fc277e1daf6c26bf7b632fd97d8504f6536b0c4a0112c1ed00b9efcd4c088146a13b555c9c682a49a2aae35f0bff031ab070029a3d43f80ca442eec5a208dce3b9107ae2037c2ec22e8925f2308b0fbc69bb0c00ffe0135414b9608090d1bc165e7ef850d001a777bf7974b84e79170ee6630334a814a7eee3a48d127c1c4d0bb530eae9cb0bedeb64db6b7334b44c085fa601621177ba74c7f241f3a4bd8d6ed7e47bf25743b07dc9bff7702bb6b3a0714501 -generate_ring_signature 7d40505d96cc9812cda2ecf10be26e35068ef21403fc13a3fe0374f1752bdc3b 58399ed2f6ab0ef8e44f8baabd4b7668e5d9e0ce163b55fde697dc1f57b65176 2 a0f1b1b883584dd18faeaee8affa2076a50d7783086353a9b7d4877f0c23bd55 9c840d84c4e8d918410d5efcfaad94d5be97b5a16738f9ba27c1301e30adc367 bc309edac23260a587d0ead081229e22a1a46029e24bfc99b8d264f0cb550e02 1 d6afc4599903292281883fd892d8372b724bf3458c823300b6937bee4a4f370efafadcc48b857117b2a637573ac27b223f5d6771da6bf22d49ce4ad0253e23026687f79d6b1cd60ff025077161a031fd03f0c681ee2e1b0fd0affa880ea4c8019ab724976ac6539647755f19bc8cd84a236a9da390aab80d2064774dac0b5907 -generate_ring_signature d2b1be9c36ccec50df11eb89ccada0a0d065fafc1a4b7296d85180598d0208fb 790464d130c590b15283bdb524cdc611c1dfe90ed958cfdd1c61f4a8c610e971 3 c760502af5bd01024b414aa8e6ec3291d24a119c069e5394023daa44cf502a35 304177d3c97d2ea31afd6600fab36a15763c2a7d6fd2b179aaf9adf4a6d93288 5c01ec55a849da5d9c141a1da2cadd189c7cc4302866056619bcf541c0ff4c0b 67567e52f4f0d0b41c1b633d3ff41eb5a3af5abbb4b71ea61b57dc39e433bd01 2 b4a0ab8acdba36d8ad4fd610eed13b76770f511f8a1eeb988d6b69a25b647e0e4629d9ebd3e3aec6c364169cb13f4e4f10d4a98b81ccda56791a9d49041fe50528fea8203bdcb4e758fbca191e924dac1f617a5c0466496bc65109b36a921b051533fb23aada0dbd4510e2c8800cc147f99ff12bc5ee255e3412facb2987dd00afa61477ad739f781feea48d750df61a17a8ad2528529c8f34ba0c824e8cb60f3d206c54ec1165646db5cafeb9b547109e7a48e02545a37744eb0b8b9a6e850e -generate_ring_signature 6a7157bd5b7abb0dbf254d14383ec158061c39beb909e371c736bc053821894e 319ca2b6bf52f500ee46fa4e19e07878690597cea71cfd1294d689377803b4bc 2 f579fbadb5fe3ef087c03567db8a70b8b40670bd8e77c9cc58a519e55fa91265 75e979d40c24323138df595858e567668aa8085c2adae4cd9a72ce08499f99f9 0e8eda5e20345e1e85b44af5a172c57f1b7ab0a80d4323bb560058b4bca9aa07 0 2b025b95211a8775c1312a47c4ca764dde880d6084695dd7c15536245370e10624ac36a4d79a760daee6394f5566c3920037d1b3a3a44191b4c979f43a398f05fec59953f4691ef03c31ba26e718982d229ee32101bb940242ed7df0d5a6cc0a38dabde82a143358d4a6044e678583b62509b28a7aabb864b550c2f0a332a808 -generate_ring_signature 2ff3bc41c8184d849397465ce855f0331b2fb8089acd03896ecc40f13c26c6cb 3c0d534bad069aea7a7a99f476537d5fc7d744827fb3c5b1747bd5e628c0c060 1 6a2dd8917bb9f1afe10a52fdc06a7f1a8be194adfd27720f52dc6b68e41dac13 9a8ae3cce36c6a7ce062ca5008b7fbcec509d750a7af1755506ca42987e71f03 0 f67c2e6a42e440b6fe30d961f3c61802ea30df966308074be432bf06e81d530cd8d83fba45f50c2450918df418bb142da8b29d200aa637c7bf7491a9d8611109 -generate_ring_signature 4de455ffd336973df436d8cc5400f445edaadc720ff6edeff859b49238aa2fce 0d974c6f5c5852046fbb400ecb528e463a9ac8ddb7e4f87290675000e4c29634 6 e119b88581dfc38a94fe46a82590cd9b062d321097017947ab2a039d77723a41 7ed1ae0e3e982d596cc6b1c33913cab5e0cac7b70a22d957ce5b51fb86ce1535 d74735989a7d20983a2adc2f7913caebf4fc12e1d33946c96d893ed9fcc41b12 af83956b543c7ab0878eda74010bcce3c086b748e6ab788628c78eef075c90d6 357399bcc7afcb24242b9a1eb62432c35ae6cedaefd732cc858d7eae7cb92e79 dae830a72a324ab3113ae4913a820523184bbdabacfe07bcbd687ade7d8659aa b5558d1400cf33fa98203b0af326975ef9038e19f64b41299a32a0f135bc2804 3 af9c35857b1b065cad69e683c4816456db4398c1c673035a008bbf7c8c029a01336a4b17d5c4a57ef76bf98255d20969d6843dfd2bad7f42aa8660a0e6f239089828de12fcc34680d55d5980c1a934437b48d0ab4875e8a76550cfff8eebb30093d65b7c311961b754df5e33ab418ef81920fe162ed0d23f3a610acf4d5e810a96c9fd97d8a936eb7412fb34580939efa836d249450ae728843632141c2ba103573c225945b429ae750996a64bbba856dcaf8d4ef290846220130f8e8349a5037b93dddde7abc478c2f5a425aceebcd80183da27e68a9602218bb9dd34b0c40f196a0a26f32647313e966dc60d3c20bf8f64d4dd5f7a3c9205d89b4e689c7a0986b71a13ad639b7b3c7b6de3e8900b44b25187f7f614b27e14a99bcea2070c0b0505b4f1f922e00962683ab13e083bdd3a38f7ba35b0b3674b2b98db23ec9908c2372420594da0550d0066901936b886315c95a867b60a77966ba9cb2c876d0d5fce3bb2c38c39099655bf1673658a3f805e60c377a74df65d8731b88138cd01 -generate_ring_signature c5a69d4c24d9ccccb2b7f24569071ae98acf6e03b925e5a9cc09752d801c62bb 3eabbfe3695bc2dbd5901d017eb2e54b9caba0945720164f342fbe3e6ce66b78 26 f43a0cea4cdc1617fdf4dc9aabd74562d8a295c3f85ed4acfe4042119367ea4c f4b250e120df0108ef321a0ada6f8d56a290ddc4164803d62d92e06524f4487a 60c9eaf854e68882287d9f28241a6ee63496342162b018d85c7e547bd65ff258 d3bbab657cf5184747c0ea16043192f6b16743a2aec28a35fb3e03c78114d01c 8b00e6cde3c562f5d9318949c9eac03c685090b8a3d57ba4ae5704ff3b5d1f02 4e16fef6f635620955f7ca6ff8f3077e146acc5591573501874e0f7ec8a6d84d 1ab363c7267a5f871fa39b58b3f77f132c832a148255fa43d488e145b12f24e6 93d47d397bd986ae573e04cfd6502baa9f86572d8fbe2fcfc29d35a6c55b5453 a61daf279f18faf9f001831bc06539fabea3cf733b10abef58a1523414ba2496 063029c65fe66102a1c47575d374692a459a416b9fac9450b3c102494b85b5bf 095fc13758f256b4550452a379a65f8d1c23e670cb5904394d153e4f9ef6f0e9 8be1d43efa5494cd054ba8f7a2ab134fa778dde275d453564e2794af637c26e8 146719dffba2869567d19999455814cedc3384b8a104dfb6512dd434041e8f04 8c21f868f215881a4d8f8ecef15a45ea9c5577c96edc5f2db93aaa97627267d3 c6cd4788daba63903038377a7748208b58351ec9384c96fb1c85558063ca8128 3e5881dcdd2d29e42ce5229d59c8427d266e9c0bfc48bd849a22f9c563ff572b f2ac153edf0d83e0b04bf20f7fb6c70881a8079af523710983e8aa89373700f6 0bd89db8a50f485379de7737d43b3f7899c254190c41f8ed38c1b82eeada7554 50313db4b4816e421de0dbb01a534131546b64539e2ff471e63e8b745b825c5f bed722400190efb4c21cdb4d76679ac6ddd56ef850ea89f5ab43f89d4f2c02e1 c81d8bb33ba874db043e3d8bf303b5790192fea6459adb800a76d48614937fcb 01563d30080870956b47ac0514635364e1c5f8d45e62af0895c405e96be1516e 909d70e1e8813e0ac85788146a836b2cf2d3ade2d2c5b18c9b08cc41b688990a 2da5ed39c9928eaf2ba35e5195ba789f8a01e99bd9bcc55c81c5b1c7bf6caa48 4668235408de2079f6d3111e96e502117b10d42a5c0e7790e9eebb7105a792bc e6574c97ebfd1ffb756f4b27c71b787e0c332b830086081ba57cfa1a699ea613 66dfd1881a176aaaf91af0a512636d9c0049460df4ccce64a4f7527e2d0e0702 16 d92476d6e246736d9149686fe8bbbc0f551561742412ffcd02e24aba4ed1f006cff469eb95d3187a5b29dc863a936f16db138855d94d6c5db0ff7e88b8ef93038d3768aa6a3d964722c39b15f03c92888d547cdb9608eed212547a5fee785808df6dfe582e0cffc872fb55b29d512af69c88b90d381d28b4df087804cae76e04b927234355f252861eaed8cd00c07042e67614948492193ebc2d298e0a956902b3d6c9f965fcd053392481d2c3922a52bc779e321cd8c811e3157ea73c4908003b691a43646a250e06840a88121069912d81df4b8ecc2aa04231cea7390c580fb445db6236ba09baceb89f34686f1d900e1a4fc6a514a87b596963a6efa42304aa6e46a4e138bf34fdff9909d392113d4a117e6a6df512c3069a837f4d41550b6bb4ffae037948d1e4d5642b68528d8f5685e284067195b5de323cf1a9b1c706c6ef640917cee6c33df237fc981b1d82bc05922dd83708a71fd63c9b410bf500f2e4c1d76f26bcc19f92a910ae50e9078f24a10b3f029db1d59977baaa370f0128014b8f78ebc2bdc6c4509f94d959d041f6e8dafb06796be0ca8efca1030b0a76abc3c2f5be6f1311b9f2524ac6f452e57b374fe5ad4199b70482811035e00958c6779c4390b4c7c662243b24c2d79645c7c5b9d52e9a197dd2f4ab36ebc005dd5bffade647d1e77d127edff0e1f651589d483a8db87c7e5f002cf25db63b0b6ecd5140350d792ec8e509e4f6f301c8b7b238d8d21601e2c49c5ff854b3d4025902c35c1fb4fb9d0f1f246923f69e8b3eb2f86b7796714537357b3ebb92b504ac0dbbfb776cf9166c08abc1712329504be6b823add45ba92bb6f0deb289a0038dd6fbcb094ecc7d699f654cb6ec051474942756277f9ffd6bd2e47d0b5abb013e86940468337f70e113c1410c12019c6af7699710fc267956cc8278f8e256011d2a3a2ea94fd34d9a3c2dd7208e2026222d83f7c1a8393ecc66423cccf7b1013f07b8e289fb12097301d2f2e68a18b9301c1aa90103e4f5a32c982aa7d0970db8703bbacaffc6d4a0d9204d362d1982cb42eb5b6659faa2abb2933a7295d50888ecfdffd1be43cbde1006bc5bef47b4ac17e5a0c4ff69d9965520820dcb1b0465f9c21fd07faea80669fbca108ef83f1abe51240fdf0f9685087930acb6e104d6d59d0d1df2a6c00b91fcc7448a8327b621d5556d5efee934b52559821eba086f085fd1adebf5514a126b402d28522edeb5fc4d76c5989c63859c58cf32370e6f320f35fdee15f7cd069776e4c0d82f5f91947e62e523dfb690c6596f0dca0171889ab5c7f4f44195c86c7e9421d20b455fa8019aa389014939e67d0cbe5606d9570d9e3c2c1c99827993738348e8b87bd47c23aac6b864a448d279b675870177c3d3e0e121c3aa2752a0c8c9db30cd31eb01ba9e64636f87360263120ee707c04bd17d9c7114161a185df71c166a51b4182b29faa681c0cde0e0c43e2f8e0a1b8aeea02e72f8dd2cb75c6395738b4a2e183dacce4f58e6e55840ee2f2332041eb213247b66a959c98c4cf4d6a1d42dd66e5f2a7e80dee0d1c716f9650a6e0628e3a3ff87288ffe89bb952a6db212ba96a1620cf14b056961d452381217f50244648ee4f8b501f0e23f76a57e27d2847b0bb0e9be8d4e86431c5d9602a56c0a373b3c97de97157fc2d0ee29e29deae82ee4c93c97a1fe990c1e9c0a963cac0df5e09a4d1ddb3414ca331c5335630dbfcfb07af8ca2e13889af0930232837302625041547613385102a211993380184b3c586bdb3ff43986a744325fcaff5a0f0a5812d074e1900d41ce24324b4f30cb31999f43f7df60d5231342f7a0744f0882f304fafdd5ba3ffdb52323a7418a4c1b246dcbfcc7e71d1f5f37682796c90bd65c2430e1dcc2ec73324b1a88321aaff72dda1e8bd78449984cbcedb1b5ba0fcf3c54662a1b4ea9d1a62a3cb612609ada804f9a9d7d70055c24860701b97f07ceb68e721193c8eaa9632b5b3f7b594be8a9930a9538c8f9ccf2b553aeccbc0cdda71a58b16d594d2df16fbc8829c7632b9b8687c518f2de428fe87e7935e709ad0d2583994cd6f7c9354cb38b4b6b511ab3ee8a3e544fecfa12886f91ec2f0ec772c9bf641bf57bd640ba5f36e57a685b4b548ded06f3d008f81210e02d420296ee50c102341a0ad38e23c312618fcbf11f960c75c9ec53c5ae5c113f0e8602a93b3ad40f211e1852311f85984e3ce48f112b3f469a8ddf5a6fc194b1dc310cfd5e567dd500b3b6800001668fb8cbf88e7abf0fbc38a432a2a3138f29e5440c5fcf5d543d455abe76e536d777331056b00517980f8e3a68b192f7a30f1b5304 -generate_ring_signature 1a25025edeed2967079043c06388cd736a6305609ead156e76233de39443e6db cdd7e9ef74c4f826659baa13cc7027e9d3c0b4379472adae05f36e99ff600541 32 18cbd5ae5ba735e8ff1df944cca9ae3bc07187947d3a0377811c7e0769cc46b5 2eee82ab6674d77e15df8e3fef6d52f2159d96f510aef9f03a7080cc1df8a551 8c891c48ea8e5578faad92012e8519be5415a6397f92493cf368598927562aaf bbeacd239001a036995137abcd6f6df6af0d42517532c177cee3d774a14bd50d 6a7439d1ffd71e17552bf2d14763bad542aef8690dec9a35e7033d941df846cf 99d0c3c86698a62548085e50acff48c7142218ab6c51308429e5d516d27c1e2f b14165aeef86209407ffbfdb583485fd28f6edfc09bfb30000a69af629660478 a1b08ad9ac3f2fab9ae1c44b64c1f8c0d89c7ccfe524220a63c8fd4b80355937 c6f39774cabc63d8a858d76d2b67968c57754f6cf539a0516ebdc2681bfba6c8 e1420e8805e8267cf5cf14ea411f178feae61416ea6097c2936cf255d6da75af 68f36553bc4e176554dcdb4d40911c9d2fc1ad535ed5de68e61214740973721f 36995a5cc2d48acf783e851bc82a7d33b39e8d5fa98a28d053496431f62f4e8e 6e5a2e1e833e72907908da061752783d2bae0fc8ab5a5269693ad104fa23a541 e4c81b9504412585a70f804cda57e30835428f9e7b33db9e303cd8280e1eb133 b21e9d751bc67faa423ebbec6bdeaae824d66f1fd287229d15202ecebf972ada d85dfd2d4c2d681e4da972cb7ac70b9472b5a9cf847e51c7257beaf9a71b6c7d cc68be54b9a80b51446d0588e46f314f0ccc495c7d51e3d271737e76a465c33c 34305ec1efbf3dfad30fcd4e67ec2b03687d889bc2cdc77ab48e0b5ff9cb56b8 c1e4997cb9f2baa62de3da1f39b14b0f81dbd2b5b1458d4017e9c3d78a24d406 bec351ba97f26bcb0b2e4c2dc0c42db166d281a7907971bf5087eda6fd4c7092 a61009cd579acb5a183f65cb11309fbfc2c83f15a8e9a3733dbcce4bcc9b68d0 4652edce62b97c125f033318f97c9d0b6af10e054d424e0054da0892856f8a5d 089d6c08e5cda9afc0e25c942156cba8e1154aa6e108f7157b21beb0b1dd9c71 7e67864cce0de6e0b36e85a0ffa8839316dd5257f1d43a4bdacb63723937940e b7924cc3f1eb11c19f0f92c4328e485ff5c087f7cd65d56266882437cf36b3f9 b90b30b4dfe9bb2e74a319d3fefdfe16dd466c5f304cbc7002c0b406c75f6cf0 f0e309314c00e1052b94b8b61b8215385eb018d80f7d416c7ffd3e303c8355f1 532eb3f1f94d0c51c3c2084e1459abfb07ef8735edfd8f0549904a66bea4a6ed 00c8322013b025dabc91953fc9d2c7558e2d482f7d3befba5ade16bcaf12e2ab e073df0ab60437b296be99f91cb20f4e2739ed54d3f387f1bef16935a75c1712 9e3706f7488756a2813420361f139e2e8478d77a52a07e0e3e0f2f85c873eab3 021fe8333e571ffc9e67b99ba619a42450b5c1fffd3139127f63412b9464a57a 1bbbc450a6b3450d412637420a225f94f6a76e0de606ccff43a749e255e7a803 28 5dd96369e04a442e917f63b0b22d8b21965977601b4f2d933633fabeaa01be066ce1dee3f1fdf05983257f5527964668720c122bf18f832e458efb1f3d4b4b08a1e190f557bc1efdb6cd4e3d347b97e55d491a7691a1199520c8e69054543800d8f1768ee12d514233b1547d56c4031a5d37ae7e54cf101470f525cd268e5f0d36b4d5019b24a559679119dad490e9404c2728a054ec33b39185d2c7b1cb090e8a15ac74761f3c9b5e7a194ac4234513f33eddaa09a747fa2da116852a2dc8089f4fbb21fe317dcc242f6928e38eacf8cc0c802c3469959719304c7d0d27000517f4fc7cef3868427ba8724c6ad934bfa2dac6ebb445afa1cd832dcfafd8cf009907ffe2c8f32a6e6e68f6f8ee24f8f8eed6aecf2b4826d9ca7b0146ab5c3e0468cd93465a7b4102c3fc8b1c00b54561b078b422c06de9acc86c968d9cbe5206890d45c4aa0e1c352e2a219d6b0002c82eeb2706f63a37587feba6a0ab34e40477000502380c2deda391802db2836f9a2ba46239be2daab6892440004b63700d98e22a6803e6123d6e131a463cf0a141b552626045e35d37a2e5bc4f2950b803eb8113514749979854f1895705aa90f8ab222b010d2ef466dfce6de455409606851b25e1100dea08363eaf37c155f5af2fafbec2fa52b0dcd9d26e04d8ade30f6442f7249620dcc6215dcf10a6d9ac491ee410a9dca05dc7a9e9fe4a47318f0e5423df12b719807196abab3e37ee861751f7392f3840ce7fd85353f3a8543708e60012672b91402e4c0204b73628e1771a4a8ede032d6a621fa039c5e4988a0c6d4844bf775efacfb70085feeb8eab5645aa5f5cd7dfe5a5bbd57ad7494ffb036a08defc8af797c9400fbebc84bf619269aaee66a134830f0938c4f40a6fbd0774faccc3fdc72a295f5f06a1c441f4b4db0e3f997e9fde22a80bb7744a8eb90c3bedae519f7412ac666c7c4d5f8951b55525e7327b806b6e8d3e88cb380b4d03341fc62654d6d60f3697aaa49b5fe3e3df7f086d54f6e79d0fccd7404ac41006e83cc4d9a01e84c9db0df2ef1c593f20e64cf5f9545f5006ea0bfc8f0b51020d633cbfdee4a65f1a1cf8d5e3cad8dcefc75ea7cf8c546bb833efc3341898430e7835cfaf33428952622bfb8b6a02139d76ec4b11c75bf2a9d9cb3bfadbfeaf0d18beca3fad9111e8a53157d920d05f3996c0b5cf5d4b41e9cc11f4edc5143b0690451a6bfaa983bfec2e604cdf8ee1b3916889e348eb7ded95a5d4a969d48303c7fa4643c9b88527555b7653b203058415622b0d585a45e00984cd948f9dc40c01024675e29120a7cda2adfff6771244d6140083d76b266b8d9da4a78526880bd8b2e02dccbe6cd5f769c1c0e3f3e4db53ebccbeb881ac5a15afa6e492b1c20d39cca6e04f09150f2cffb7e632879b3860083ef6fdbb678837a5dd47388fd20eb250904c95a0e52ea7c7253e2ed2bc27a61533743c72343f978d1c348f42790e2faddf6964acf1e1e6e8b8f4274b8e877636483d49ad801ad7eed6a9a9891405e0cc5418493cddf8d07919fc110448dd61b7842ba9bb9b60305d3d142aa8240b9e8132722e926f6d002e79eb63fbbd68ee624d9c5fc516400499eb419ecd200ae6f8f7179433d3444a6c5656bc5ac978d92093d92bcf7d23263414f0294a6507760db07e4e1a7a0f5741c5a3fc75815e5a9d3b28b540cacef7515b8c512ef102ec8361a89b4e53014ef0dcd5f4ca7c51cd334f8856cc3a9d734f521753dd2a0557ac07e080678e2537400d67c61d1f0fc056b37ed8fd40fe64fb4d24d08a8808df316f5d45d6c45b1b7c0e897af619acfab1eb1e2f24fa04c96cca43680b100c5470793570392bd07220e0c79fcf83a0b69c17e6f4314261c76dccabcf7ab1014d96a776b6d93411eeffa82b4a44c49717fb7271b0a12748f8bf634f5be8ae0d9b0dd99ce695dbc4366af0986d12a0ad8efd629c0ca4851f5e054bdd673e210e31efe35636506a55ea2476395a88b8a44d4b4ee2cff71a76d3e295a8fb8ecb0c0d51d7b12b9036cfece89ec5e1adaf5da32e3c5a9f176c9e0e7d2295d905af095bbd4f0745be8e113f9e889f3e7f6dd83dbcfc2c9802817a9081ae23480f420384d98489b3752c9c754b87aebaa4f90d74dbc8470506d36f4dbcdf8b1686c6096af43a7099f47d58585c711527c41472b71b617ab69616da0b51802b0260410129a09819c7adba2caea93c856641569916654bff27d7cea29900f6276cb84b00bc985625879d686a4e74b612b2711ee37903e40739dd2c818ee56f9f85234d08da790491bbb94895008484824eb28d6d98db9afeca9a711298c49e72e1adc50485d23b61f2c886001ee4fb1ed079cc014e64950d7255318aa1fc85d4a78959055a7e80747b9c2ac60e1cf6cd77639463a949b109cda36379149b6777074ce10fe7ab50838f4d0bba7f76d021b16a1f897752490e5945e41bd748a2ff34b67f0285e33faa5120ffeabb69e584b30c857cf024ad6a6214b361c3132c94e1ce870720b3942f4b30a0014b9dc1f3d966d32084aff1287cdb770635c5b070e5b15f000694f46219ca844629f732ba861cbbb982e18512a1a5fef494ef31d4c913ad03b44baab077de377a0ba7a0fd25f5a44809f534d9086c7ae6b8175df74764a300db173c82115cca3f68e1eeca4ee4721fda53583b963a49ce082de78cda39020e33f448b8b5bfae1e8dffc235335e3dbf4c5f6fd6d51065235a86e7f654c7700f06281e2f87bcf94218dbceddbe704537825663f7dbd8cbc2215f05117be12807dc2afd452ba147fae7d265fea4b3d39954b5f3d2627d6a96470e47e3268b2f04a0c7eb05909dd3ef41e863643306682c10ad5c04f7af6d097bda99afd9ca4f0a -generate_ring_signature 06849cd0519c6f1009ff2038da647ab6c1db4158bfee7644db31219a32de2265 5ab33b23374071466d658f4931aa7ce87e2800bee54a77b3228c07a8d1f9e4f8 50 f4dca6918c240e30306a9f94dba59fc51a8ce538d71996d1b1ec029c9b32acc1 3cecaf5af6facb8438a4feef5999cb9fdc28fd4e4ea8ecd67ee057c70679a00c aac24c3bf580a2047cb608e7414c28ba41923980929864f3a59c5ddabe1813b1 178bc14f54597a5d3216a28b763597cd9593deea8e59a5e4ffab0d3af17dad67 cb648c43bb1ea79463c31058908db6360ae89bafede111ff9119f619ad40be49 645417d726d5f09e7c40ba1b1b1e17de4b445cc6b443f106ef8cdf20709de226 42fe478f50153993cb0de879050356c81055b30b04c773d07215ce8a05e86b9b 853818c89f9822f8b5bbd1116cb8fb192b49db52881a63fe91be8828843bf49f 43996d323e85feda01a303ec77bcad68cfbd45fa93ec6f52994fe002f3f63320 e80f906975529b458402d90e871d4fe391325a3506880ce3590640c1721663e3 249d745a3bed28cf3ab726767b78266a4f0df17ee72e4c228dd4ad9363198416 127e4eb44765ebb96929285776f2f28b8b506fd2f802709b960ba5122386e4da 47925a49af8a140f389594c6b0f58c0cc7d1e9b2ddf8b4b19caddcc3c191e66e f2c5333dd0c1fa1b3bbeb8f76e0c3ad4d90ae352e5491733577f53653a512a47 41ba603e6990ab4a6b38194ee651f33b1bd811ce9b415f738a999791e9ecc08c f0436f4175cc6fd250f1df326cacd940d9554ad61ea96fc21f7338b5314c0f0e 165eee723e8a32ed375e2454dd569b0a94ded444d743180b81883e7faa4a4f13 a85e2a7516551b743a2485984fb579351503bbf3ffe4a74a04877f9dcbb53690 e94ccff70bc813f73e14eae7093ce6610bd111710c284aec75a04b0f4e931fcc 03671c6ea4b79883673021463f66222b4aec7c91f5bc03ad7efc562fb6fe2db5 26a39e79ccb667024f0b7c90c1dd724da2e3754d810cd5491eac2b3cde14a1be a250f4bf3ac3e0b8aec3983660251dc7d6540d59ef7d05ee4b2c7412c166c98f 914bdb7c9c9b0735dd1be3f60171458b8e312e53ddbbb23e541660bb31fd1a24 a67f611b7bf550f4b54065c0c77352d3c49a3dbb71dde20777788eafcea84513 a9d5fd6f07e027beea34762f024615ff38a74cde6b009cb1795346607e5c1ec8 7c894a201bb20226d380c8b58342ab7b946782bed4532f9f25c40f639f1b1312 c360d3b1274db4fba04b8c6db76de6d52fd4326f7aa8ec955be3c62aed7188db 2be5959d53e19c49a6960aab64e2488996addfffb2419e3c8cef3db1f3c33a35 ba81236a84fbd39181eea3b60b1a85d6947007cf42c93ac15ef7a2cef6a4d3fb 8de16d964a3df51e199b8df767470f4508ccfccd27b7f6d78b62cb9398bc55ea ef9b3fbcbc282afb025ca8db586de74a30264588f5f1060d243c7865e4c18c6f 4dc1a34353b429f5e1811290d12d6e69f3485e6af9b389843aa1b5a067ced872 c8295f4fcc4620e37171d648e229e07c0ad3cf81fbdd49e895c08ca2dbb1a73e 39ed7eb8e5da66c5b688e7447508a283c96bba5c55ed8ec000e42ed4872ff145 4f7f18ec1e2eeee07658a986008ba1086b3a1f030a0dffb339099f94d4c8f3d1 c05959888131b964bb63476d63904d04bf1e62538e8c227fa6d6a84b1b2c5755 9af1683e1561c8c82aed8dc257a5f301eeeb76d538cb62c80d85825c3b58840d 58b9d74270c39051b00080c63d5d306b6c7e8ce773b912ae34cb528723b31409 75c8f3e9399c3e6a233b1f511138af008476a4fef6749b0d039ad92edfa6cfa3 8f2d018ba38454006905612074115d7f52f021de849afb63834f621b0cf86813 edb4523261f4218904cd2316470a97269c10b7dd6278345fad3c78508947624c 89ec4715a6d64497ee9825ec916c66ea04f4f6005f2c09737d094667e0e2c134 60d872a3dd8bc3dcd1e620a448fb2ab021b5dbd0e42bc54fd1a22173b7af796d 7aa0e6c13aa26d3cf23d32b3aac2f4b9f07dd8975532d326fbdce20cd978b271 f20616d23430a3a6df1fec7eb6ed20ac2f5f4d17cc4af342a92928939bc220c4 30ee3a8103f3b6869b385f54aa458acb4fbb8225b8cb1ba1ce0b313b9ab32830 d2c9241543810e19e2d25fb2b82f1bba7a7da6d72093f8aa35ccb1a7eb9d1271 e716caf713eb8e1c3f8c0c02dfcd682faec217cd7fb0a872f9a6e498be2d8f1c 8040cbcc375bb11d6c4da2de3035a95273695f28b3f766a966966a217af215df 118137a60cac4033aadd331dd3c1d1511d4c6b3ae41b929c05a31252f43d097c f28183b869e0e416647dfbdef14a6e7b1276ae55c760120cfb92d5333cbb870a 39 e322e88cb5fcf73e3b2fc0aafb3d20b0d479dc0700138b1d93587c07ce8a000b60fe0465a999d792750b4c6f2eb782e173b8ad94b45cfdc8f0d9f09ab62e9e0a4b01893156ba67bb00089153289a9b015b2b6bca2cc4e2a047475b8ab59d88032d6acbff2a28f0c1e79f82429462c9404503bafe9478118a11c6dffe11d62b0ba4d7846dc3aa9b8ab6c96a8a10bf56ab68fb28502a6fa14597076265361f41094528d8b06288153cf8a0ad7a480f7142fed424a7a1c720948980ee4e10553904eb151e09515b3287ad6d61decad8198b66e85bb47d08a5da3977c36717e89e08e0fdb015aeb9ec358de6cad1cc9559bc0f399d7a42a1529f6a7affb18d568e0d26b9fbadaa2e3324b973b02e7735e6e19903d1cf5be12bac70c36af58a02e40125116de322656259e1d747103704391333f7440c96c5707fb2500732674a200fc80ae86019e26ccd6ac8e3969f5d87a005077f54a06f78fa5b87d78e9bc1a10dc1af47d2f98fd5fe5cfa1117654fa6eceb69ebf68cd243cc31c5642428244a084d49d48c54fcd80a79082c49f7c18f3b0f55bfb75d306e1c08b4b0c8a9940606a6f2063c5d084e6ee6870ea2a4b20d950f69dce8327507dff63cd770fb0490008228b6243865ede83c609e3f5e378b6f3945464c887f7e13f2e534df963dd90971a3c5c5c12c74dfca1b55c5c971c15a13e94c55d8dbabe9ac34fdd259122806a959de5db3b43abb79039c00e63cd8fd068b90597253be2709937b8c23eeb00d3121b82999c6ba02ada2aa745ce5a330af3a22a4ffaa238feef63a9b5ce0d90148899e3268a123cf79df77561ec94220f0c27a770a80fb436a5480fb02095a0398ae5e5b841dda9e643553358243e4b02cd93f8bae7a8520ae5e52f8e3c90a0b9468f6e9f57452c324bd9e1b4e602abe8bc682fff54d6a1aaa0d069f156e4203058f3977463aec20b1e7fe1bd4d58eb0fa9ecf1c7989acec89b65e13242854084d252fb445737c4cfaf0214ea2dcc224ffd12e0631d7ed2cc08386092597bf0a65cfbc691249732fe95548ed68107bf2cfe6296294cb92451b01c016dfa8b10482ba6f72e18476ab8631b52a8d5cb5864e4d2fe43b47e0aa1950c60cc573e308dd0580487c2a09ef5e9d5c6ddeee1264cdf6a128f1b347a338185bded919ce09752b4323c15c9f582222c022907fcf4ab19c1a6b60feb67350ee4ab1ba8cb70bc39105be1a56a14db876bd8b0fab2079a4de9f3a8da13cbf47e79456b97549080d573aec171cf6a35789063f3c5a80b4b3180d2cfff2a1586f617a639284660484722e2eb293a49684f98243e7f25b6241805dda23d5b65e28dc74eee3a7e4079e8dbe531db67724046af715f5fb95e89e835da3b3e84f95a5a7f68dbf1ccf048d0169e73915b440cf6d82db32ae5570fc830611a645d8c754686e4c8c2895077bf0fb0825537a787763f5d026f692fe6d2573d90010295042890d68f76b500864d1bd8ae10180168d64d8cfad5214418693ca10a40df601d7e952b1d46ceb083a09586ffe05b46449160e28a624b2dc797bb47a842d3565e5ad0ece0ce2690ede7ab2137bc02b59a84724e65fba52b58df548d9692c94655ce8b7aaa25a690a81aaf24bc11b3c68797e6531b8904e8cfb7a44f8487e4341c468f5322e43840673c507869135a04b033909dcd70d1aac810931b1ee217e6193f1cc1f37896b03f27f1b6c1b40d2850ba70dd0e076ddc9d432d0d97241142b58575bcf5b55bd026a8d6ece53181a1507db92b858ffc1c04f90206e6cdeda67cca76c56b87c3f0ebad09b5994aec7550dad847dfe7424ee56be526a416b9d9584c0f5c5aff0f205f00a4bc23d5953c56373ddcb1b3ea1e2ac823486855bf51be4d8cd749014f40b4c9de33012ea589c6b0ef91a467abadda3d730286506c5d3561933652a61c804f211455aa4a7513f5a23d6f8a5df1833ce4257536711e2a7d5468269d2608c034b06cb5204f87340b6d7aff69d6eb005a09b596d3603e56e8756c736d084dc0e64dff02b8ff16329362c903f418f9f9314641a59ae97e81f9182f03abc41ce0b5db99eaa901a5a04cc216efbdca56844c7ad930966ff80e57962018f358ee8026c307a61725f67f0204ffdab2acb34d7c136d5f53cda20219b9b1b3ef838630f6ad77d15c509c8895a8f4a409ccc21874d998920cb6e48b57d3a3ef208c3500fc5a915da32251801483995f7d8a8ad39afee832afbf660feec4e5dde80540e05b707beaf5d649de8519df9d8194373236e7207785abcfba18cd8483686768202dd5d1f18f514d4ec8d5da347463fca3e712b6c166cb2cfe93f865deafb1ea405848cf98f22f14335529cb1ae5fc43e85137f92b9d151a8954a3f504133dd01097e9c8f2437c501a9a155926225cde9ee3b5943275ad8e3ab97401d967271de0a7c5881de2c8fe8fac5a89d1188291ccc98c24a68abe93ad6ae8a06b2d29c1005028a340bf2bf53798163010d20a84954ca8ae2d740cab432fb3d17a536e17801cdb744545814b97acd0842dcfa517a8cb73f0eff2b2b4ca6cc6951e810c8f5068dc6dd8dfd6b361fec24d99b8113ee8aaebd39ab36e201c933e45a1a83f6570896f30e71a0e6cc433a422c6734fbf090e178dffe67ff028c09e786a5f82b8b08b2a3ecf1065b8aa6b621b0b1285b716cdd8205728fabc26f389f37323aa397079b073b79d39707bbcb2d350d551b27d64b331770e6af8b91c8178168bd1bc10db48c661458f4b274eb7a275bc21d327eb7669c8df7901249b088133fc6c6200f5c4960708ecccaffebe4483259bf669da079d9fc2d82bec9db4c518a4113b70d6a2d7211f2d264064421d54a402023f70485e5f0ee2ce0d41190e0a041f86008096b0d4d212cb1dbdd66b04751ce6bf8e6b43656523197784abc39c4503d600dcbf44aa810617405e214d1190f77334f2a3e3adeecb59409dd2d588f7e5d320668e55a7f595e71678d0fbf0d3b2535a4aaac3af2553f866b572116003c8a7a0a2bd0e5f1e0f3e36e749e2ac1d5c0127c5355a7d61e34aa76a07370358ea997066f31106fe2c73d2c966896d819330f9fcd2ba14641a1b3d679782224ace51f0d7fea6bef41edaf29fc5af8035f427c00a64cc2a705acd5f9d9b64771c7805b0d8f9fc456ea5c91ccf3c20fb901939a53badd780d4261eec5de7bf8a8cfabac0de00085ae134d46f176afa458a4b42cda58435fde587a675d59e499d58368a8072f5488bae58a357b193b1112b6841bec5d9e9d17aea4afab799a820670aa930d29c69c83c332a403622e6cdb855ff57ad3c0189d76ee017fef6a45ab884c510e14abf06a1dafa299f293a05f66d06ede7b7c71bd4845edf55f0316aa2ce6e909df19ac96e071c85e029b8c9dc64822d141fe3d7a63f22448c6d9f97e21d70203106bde1fcc26a5403d7a1c42e3589e28b503f13fe4c828f4bdea405b37ce7c0293dcc9f934edc67b69ade5757b26c5a660bcfba263d53983a8c229a3a4bc19018238db3bbd7b8af80575cdb6cc399d5c59a9f2073b5de1579ace69ae52a09206369f8f2062a7d3c65b13d55ce5d36a4e029dfcac43a0673937478e445344a50dab8ac4c222eb55381fd2eb44d38241aa29c42698911eb227b1f7bc8d061e55001651375eba227580f24cd8cae5984bb4bb09ee57f4e8c3f3c98d9b57f68e800ad07e3fcbf7d30c27f1cf3c4c47f87145b70a634f513b4aa3dbe7b75d80770e03f263abc6449a1e4ece70ffd79bad231dc7a0979f8b5654c4952bced59024d6008744bbb31113115c8ea87551371cff633f6a8ed4050318af956c11a7d9596b0fa34da56533dfe6d91719131e6a098d4c854e2be30d637891f3eefcce020a7908b4778a70de196233e5b49030258ddc7b128cb843ed6bae6f2e0b6430d169100e9c5180102471b3f5e6804d8ccc14b8d4dcbc72832ea6ecfd7194a427a1707b03339ae7bb7a52805f721a512db6e257c48626262ac9ab7434d36d1e0de6d8ef0a13c87ce39b299e73bda10dc388cb10feb0e79516db6e3640a1d82917639d6b001fa4cb430b74602b0167812108cabbb9036b9160d2979f1d18caa2aa4ccd750b4286783f67d33100f0dca786181410b66fc8db3b9dc1356be6f8fd00adde80068ce364351e316e154a6d87cbfd0c9fc781cf0f6c8f98b7f7284577512abbdd0898ce4d23351ac5f1e812ff7b9c092bfaf5f099c88088873e1542f5c556dd8e0adb066f1340b301547349c04c95e320d9802e27597ad1fd449d7bb32159453d0c12f75e3e42803fbe4f2985444511a292b78e95df63de4c68a1c6fce18c8278056a701bf228be9669fb31e22fdd9011472d23b23b7563a0c09ab45b4d396cd8093b7af9c52d5d254ad563c49b18dca0ccc9481f39ded5b726c7d32837a9fb3c0900853277728d2936e4d56b6b105e76b03a14b24b2a0eb73329b8b9dd306ee604de09549b6ed481df03345d8eb2ceba043bb262d42231cb82950edcaf47067608 -generate_ring_signature c3ed92bde9e8446efb5b9367d42ec2d1422a2cfa450a71141a7097a49688df52 f0dab29e41e047f2c8183acee8e0708501ef4ef0ddfa6e7f17a9024b6ccfe877 63 da39f87cd0110e4eeb4c3569349e603a31a6fb69dba927f28d18ccf5cd877d42 cd374e7929e2a64d6b9be6dbd9cd599d84d20f7612ac652f64372b73f1877408 fe80449785af9cf3ee7a75a83a43659c6f5ba4c18db3270b87fa75164c859f0b 1be6eb1f5707cc1869a2e6ebdaff53cf48ae08eae0a8b1e531b888542cf293d1 8ee5d44e7badf8d5c406edcdd8c530b16e4e0b3b9b9bb9355befaf2efc077aea 3d438a0fd481a0eed145afda32e3476b76ac92a006fb4fd7810ad329a29b858e 6b76c127a388232f2df579062c1bf1a28528be805489d4f2705d66123812b2c7 680039121a8a1928e28f3153cb1176401d5caeb5dba958c24fcfb0474cd4dea5 a19c49f7d41672de74e063361060426d2a8b882349b92bfc808ce1e61ef12f11 8303590b8103b58ae2c77bad446c90df388ab2ecf06baac717f0afc80695caf6 562b47c8f013f3b19c61ebacd5526acfeb1cb804935d358f7c91ae04c5837419 ef3a80c7141778f0a022a868bc60312f647d43431992d300194ef4ba3c35d438 79b1823612b9bf0aadaffb6084fd2227e72cd3d7e4e9c01ed1b49a6676ebaa48 0dc8aa6f08a8927375135075e0bb400446e6444e0e8417e6124520eddf59716c cf05ad9b936775c640987b5c24e97747a556d79438e8c971026419d7d570ed80 c2a77c27771f2b48d872ca607557e68b5209646fcd51d33bc2e7354ad20c64c6 bfb6f453161cf18160d66e15b35b0cec92e8ff6f280c7f8a3f7f6c8bd7c43ac6 f51fa0445e7aea779340cf8be0bbf08ebd182f58075bac9d6444c6ac43fc2f5f d007b1eda8d4fcf518760ac888c98c55e95d31a80f60dd2b4ac977b16ec218a6 621bad8defeb944bb8d45d8e639188f7126c575c99181c490020483bade02400 4be49d6eca74a476c53ea0b10339efd3f1c7b3b7ec94ed1231ed9d98d0c06535 e290e3eb2f3610c491129c37300cd2e06bf327604430c38e4ae74c2378521c01 404bac8f26b8258897596efeb6a2038769e5fdb5bd9f025879e62de6099b79c2 d831751f5e3f66c566623b38c3f42772de731327660ad06a3b074007ba95a0a1 1a82cd19f29451407682902139f2c23830fc162b921ae2ae5aa91829559610c2 15d0830fc658ae55e4dd3911034f674a1a401f09b7cc11c3cc6f4d2245dcaab9 f48765e2972e935d57885af53bca175a9f7900569120d1fcb7986c2f42b3a305 e3e2b896e6bc92b4079ddd3a3490f9c9fd0e759fd4fc2709e1d742a6196e49fc 02c733dd69cdf8e3890d006ea71c1555a8b6407fa2f3ac6d967f6e43db4df100 42d84b667f691ba1f585b49d1d5fa029da5520b0686d8798c592ea844137b3f6 5e6605e0f7abe6131b382e6a8759c4ca5fae77ec8291fa878935962ee02e7023 43db8314dbb6db17309b12392187087f92934bf63d7e052ff7df1dab66251e46 c1de18964d5b564728cda7674a983d85b9e61af2a5012489473cb9bd048de0a7 80972ee2f028d2f4b2a15800a16152d42bcc81ca3c2437145bfca495514cb69d 83ef50ea7e02045bbf4f158d32f4b942284e63e5f2b780280a87703e4f7036f6 87604dfb8212ad16d491f0d56d7b311b4b24888b360c36b5c1a1129db3f3a8aa a3a8d82b787407f866a125572c62efbc1da5db0ef23fc74562d4390c68aaff56 01fa56294a12caea1c202ced527940cc24c4a89dc8270f49ae2d18e842400b28 9f931197ff4f05f68b25ae4a86de55987756793579d84b5d62de550150164c44 b61458c7d49003834597f78d4a2592d1fb486482e8e7bc3c3a57f49ef2b8ac42 d554e6418a72f0331d05757d200c3692558f334802719b53927648acddcc3351 cbed989db9983b608bd23cf0f5dbb80015add6f0b67b1b6774dfa96e70d506d1 795ad4295025bd980139455246590abadab9203a035c50846e4e6bf5faefc729 75b50ce254809aae0d537d54c0156197e20dd20c84bbd9a62e2e301590ee0e3f dd8006d8f8db4216f3a0fdc27a872b9f2d16c511d450ef8ef9204f10860d8453 9903457d3aab9decd7c2e07202cbeeae6191259e348d0e05fc8439f06404ac53 40ef0206e8abb47158988a82b39fda91d6f52d592e49ddf90181bf959be81da7 baf85bdc56b8033c1e262e08e123e5353cb7f7ddb2b7ffd74ccfa529227853c1 a4d2ee759a828c54e976a8bbc55294e0f971cae0f30c4ff2fdb04f07de39c7c1 a0d47358f5ffe1d742a0eec679ec72cd1481f7190cb0aff2baaa141b68899de4 9cf118c576775697f38f65e1605978c2af376fc5e87e18c7438b07b48cb4ee82 9f022677e170ee00cb689b13de994c7ae9f5bac789ee8c8ae1e4c5e7e179e880 9064ec6c92d15bbed8c3ffa9c54dd4a71c3e940373033de467234d6ba7e863ce ed04f38c3d6180703052c3cb1150e685ce390ab1491c85cb317533f10fa252fb 5b71fdcbff0fd2e0d68d25f03b750e7ec3adf0c4455b0e06bfeae8437cf746af 7bd75fd7e511a36f6462dd1242f4c53db2b6fee823cb5bedc26bcc9c3bb52372 3b403b21997c01407a6af4215fd3c8e21b4badfba425c032623b182bb2e22ce7 2d15770f93f004a76c5e4ccc25dfabedf39e57420ec152fdfeecd75eea79d139 f1d12a64020bd216a36776fa960a50453b3a3cf398ac5cd64ede354e8b882868 f1ee519a49f43b4d6eeab6922191742c70adc22d3eb88f68f285114600740f06 8b48ebb921c0dcea1e01d7600fcb33fe4c50a1646de0f5feb775cb9bd87b8988 3485e0a10bfed2eba8dd4dd81e2ea42a7daba5c4ec4a035521004ef90274565c b6a73a9b1b67ebc907bfd0429107f58b7867a029b9e48bde23b821102f2b0ffc d5daaf0ec5ee5a4ae2a10fead09449b44b165bc5e657dbdb9aa0ae2a16824e02 58 a583e89fb43afcdd5e5987dfccb123c297329a19aff3335a67089e7d2de322086f9e14e74198106ca353e31fa3426369f415a2bacf3160ea4e40aa3605e3660bdd62726dbd0706112964a0a28ceb7c11c26a0d0fe05c7090e600f9f801455d0d694fad13006c038eb6b63beafb831e1016c0a0791ad13ed4d17f0a3914bcba0df45cd71fd6eeb60349298f944f6d4016cc72a25c5c071ebb8965946733dea30b870a4804a9ec89c052e8c42d168bf6d74ca8ed846240a904e4260db89d12a500a56cee04ab6c5b5c467220325189b29bd36d544a1d400d41d3b989fff7be38072f479c12f3da5992a926b36f30cea493d9dbd80b653a0915a721c5be1b7db605b7181d4ff939b629488b582cbfa8b55701b955fe7650ce9bf2907c9d87f62300811ee6dbe80a1883e316de9cc92d932d8a4f640aedf587f842d21d6105f7e50052f1b17b79af70064bde9a5657483c8ed1e175de4cd9d54759ce648e7b88d80250986e9dac1372b1571a4c18b0eab28f7f73246daf72468d4e8e34af03934f06eff2618127417c7254efdaf71c2e92ef962fe47e9bde2fb8bab49679681daf0c8eef29b1b1b08181631ee9357281cd5e58a771671e2006fa5389c1190d58c600f98b78cd75d697f132f4240009ccdb0fb39df1239cad5d84fe43272220d0eb01c8c8e989e12b766e784d3fb263b6be2e984e98ad1e4595f08c1652a38763b30e8e23d3092ff96cf7c40476684dd2193139600d0c8731528a0e9c536a8d26a10be91dc5671f96518896354360da84916eb057c9897347d40aa555c6921116e009016af319005cdaa2029196e31d59e782b62ac252fdaee47cbf0b2db408d673051f8db6fd7d6f2198d5c970b2cb39cbfe3717c55eee111f636d39dc1dd6691b0be2a6c90bc560a43196f2399fe935c1ae69e0c90b9d72d6338d5db864ed2ef406f77030daa83dd3f906a9d8cb39d74874d14b022b8d96213b6fc29c06e8996409be201c650d2d65cad9a1e0a70ec11c480f15dc4ebdc69301184154867221c1081f41b2f5e34b7db14463af3c3b635e11eaea1cedca5bf0b2a689a8b0f127ad0c0b6bad404f396161b1510045a6767fa939f33752bdc619bdde7fecc4b76b6607166c34f75ed8d3e9b329ecb231ebb036bc4877e92c8a9a62d1b931768d7db703c4d92e4fdbae790fd5166841925d7e4c06f799698fda93f013a755f7c4c0990d55b4c37d89efe352da93cb83fbd039abc3b6ccc79db99fc5294b5f496df6b40eb83a720f00a0abc43ec32d9ba4c6d12d1f3686b454fa1fdb45bffb332f0c790e32cfb2ec8cec7f124a3b882b086dc571af66d0e766cd1c900d0b9b8dab5c000bd71650bbe3eeac4fb2706d3585aef87eb210b5a888145dedea45cc6ab71ed607443a6a21e1798450b6aadb733dd23bfea8ccf953acc30e16c5a5601cefbec008c6d2f2212879e706274902464fcd25ba7558f3eae26b94864855e53a031c540021391dafbd46b469485d397f4f6bdf7f8f9f217ed09f79245fd57af741cee90f6141935381ce7d6b92c50aab7ebfe848bbd275f17a16876998575487129eb807f51882700b56f162cb94ca3ab246dad56be04d6ff05c4d492922976be28bf30fc4b6eb7371e707c622fbe5820bfa4855cd1901a1847720a6cebbc8a0da338b08db493f6173432cc1adff4d55a7ca09f9be4db6f10b8397887d408e9e6eaa2d02435d33e0d33041e87988f06eb11523afa6222d8c559defdfcdd1cf9a3658340b3261abc4d08d803e254ac31e1cc52dac9570b2f78156bcd8a2229b8881ae5607921d704baf33e124e9da1e82b3debfbc92ffcd91b68a433aa9782a0f5a2c55033fdf8376d957fd308e066ff49461bbe78818b0ca22a89a1c7634206f80a47b05dc0c8f97374a31d3716a76ac6f0ec0a433acf4c1768421338c0f9b86977eb80d7a1fa3c8e5d4588b30d020c78f357b14050adf30eca1b62fc684e50ab0fef906bd3c4aea4caf4bdf8a0aa6ac8ab8637da6fd58bc58def2213f223ebbef60ed00cbb61af502d373c96313cd71dff0c25f046025e2322bb246b07298d640946d031c4c3fded92ca9e97b63ed24a61fc4485d41f99503ccc9a404a9168e4046800aca24ed5c6b2dc4ed2b29e826e66b6f209d26f78d1c9d19a6053df5c4da314b017e084041132bc96b582eb10f4a8c8f51153e74fa254e8ecea59e57d354e85009c10a4c0ee75b67d256e6ed681a5419f0887f2819ddd6dcd1cbc6416a0149dd0bff4db9808c28451662e7f24f76f87ea514e90879e6ed1037bbaf87b285542c037e42b5212d964876db1b9856d1e594e0c081d01934a37bc7b5ab5f999bd4a901079811146918eb45c5420dec19b201fcee8af4686d6a5a67ef0539117632ed07b4e9444fbe3700f5223932afef7f40771003c23976a1766c86a5e9531f249505e41e033cfe2369b04f86a4b63218ea396ed574393f76c0a6bd2a27876fe38e054f78d3fabef5fd3f1dd07b47d332c3521b891eb6f11895bdc89d10908a19ff0bf59166d2a9ef7157d002ddece97d2763a2894e27a5976f4b6c7db82fcdabca0fa2418ff53207671ef2b578a1db6db2ce3d6052ab898d858b96f2211086ca7e085a0e36d46a71831213482f8fc37d06cd6a28093f17f30502c9733c510b56670af649be55ba2dd11a8f411cd19c26814952c864ead414f349d5b65595f8c2e603f1bd02c9623a13b1ed294f8c91bc97df2649ae0931da9544b0948245e1d96508d9abc0dc043514a001f1b924300c19455b047d060864cc490d6e93e1ceb75e0d8ebcceb43d10d801561f8ea90f2392b2033cbd75a1fbd22336013f0465ebb906e59b7a98b8b61f14b82aea2cf6f91d6018f13cc2155fb8ccbc90ffe3cea927063f5e461fccd62400c5614874166bba9d3fdef284a7d4f2bad526723fa8c05c0d512a00105e27d32d61012a8b7ee4111a3b74d9a16573711e0f2ccc646dff380aef427722cea046e514b40d74436c05f191a121ed300bf29c6672e873bd4ef90b21d7585f1db20d2bf7e999d42dbf25e4e2200c7a1d1205543ee8bd3983446d07ae943a4ca1b663c9deefb77507ebd705730d32db113808e9cbaf9aa414fd700af4e54d6ac84981f816b6255e8292884d2095d0fe283fbac5283c211f107ce70fbd12a1b63f3b249ca2c9b0573f2e750865fce07f14f65b8e6bd33df9aa0a6b0577526d63b91fb6b1061c7ab8016e2f77b214583f06d2f5de1404f4d798945801cf2e7e35de62e5d658d48334750f53714695b61ce176c7f7a67b9b3fa86d750591c0421998c69ce9237ccf726488f9f1734da635493b472d2268363b44eb3304f91fd50cdb687a9f31a5f57418c84e40a9748d6f0234fc74f4faec80339cde037ed2bc4953fbd29a1ecfc9a60e25f1d218dafcee9b7753f5dd6fe0f231d0930ea384b470b0473139dec1ab627305d7bed373f94ec58946eabc9135fc12f8c70feb6df221ddfc743e4db05b60eb484da13cb74f0e8e8b1499a40861c16391830a52c7918a777c8362d2fc046ea88ab07a39e34bc8d3045f5594e541f64b52a301450896e7358311c635039cbaae02a6ec528ffb93c41ef228641d70c38337c409090beafbedde8e79bc04b67b91818125ca7b5e0065695d2b247b781aafd15e07f3b227baed57c9452e1cd9dddab46d8a0bf9811a172c4fee936307b3b340f009827297bfb4607ca47515e00d876c7e7bfe3da156011ded770bdd718ed53cf009b3f1461eafb79063088481fdd25db46ebf1689b3015f4b5bf2b4d62d27ef9b0ea993abbcb9c5abbc29f3c682862c7a86970756a058d56110c2cff31342739407cb03dd8cecfabbf23122487b6cff7baff24de7e6603992033877645364cf82034a03e86a9fad14c214fc747212449bec983bb55f7fd28dabb7fb7cbd380ea402104080b3442800a173f762f5bdcd092ad7f121ef1cf5e6c67d9bb5d214f4b808230e9c141c9303ad4ee1ca832472b0780ab425eb6eccb723d6e331e031926c0138e922c74b4f9f0cdd2ef7dd2bb997c2c0e6636350eea7aa021675694a4e3a0f5c6115fab39077cfb2fa06520886a05ce151fa9220fd9ef6562dcd8e5f9db908f41a350172ac6c8f0f52873a67f983e5e5f52d8c07d3e5f329681e36b1190701b217db4e14d0cffae5b8b3967f80dc13707f7665167b93e58f9aaa2c6e485a09a29bf525ffa72fb05b847792a95b03f704897e45ae76849292657641af827205cd8a6bb49a064b14a592efbabe073fe8da513396ef58497fde0a491446f4740331781a6df64bd145486ef305093324202516a0eb08ce7b895b7ec7162b25ab0e24215d6c6c7d2445ba8052280953b86a288ca16c95531e4f6e6e5a569ad80901b61de428146c311e16c566de1202536ffb30b4ac36f58065406d3db3a59e9a03d6ee9835109ef0fe7987845dd3e5ac50d1f673d203a4f69a3f15419d78a4380917c7c3a8478a40ae6d98b0312b185d16b948504fa463bd312a5fb29a4c5fa406d4aad66171b71c116efda93c43e2c345a34463f581f8e85877b3168c7a17470cf7b5ebbc960eff67875b322b9e3f0046134478d0fd76308a60e4423875db440e01539c5a71db4ab4dbc1f1219b92cffe5adf5678dd4598c4574fd791275c110d26e993f75c8bef05e71e33314cad4c037b788b45c3da12574ecd66386a341d08714265ac4cd9ab644d884d76cc0bef7d904f5a58c46792dafd5cc22a9c8a9e00d0cd2a3066995a912a6738e4d1ce09653d6672194ebfa50a93cf7a1d5891d504d31734d114b6463a61147d6e780d60b05ad17231dc475130c7929e5dd4366d0f5217add55bcc70210a8b816dd890a824ffe0ca5f4284a76b9ea5642a4e39e30a8f1602e638402c6929f9dfb4c5d5586a911246b7312452d074b3c59a02634a063a3c7eed78c6c349dfbc0417473be8cb95a11976f358015eef5a1e036b05430ec38972696115944e3d5c5b31329ece434d6d443be88a9fac015291e84692260a9ebd33c9e183100dadca7b76451c31d4286d827aa0be4abbd5dfc72f812fe80f6b886d9d1ed3e1d231f5ad743a10c5d5c2ba48b142ea885d1629e4edf2c94f0878a55b28d7268727a7598db6d3184f4584480018715aaf39cc8c19c7f393f90a5a6487efff93589b4b36019aced42bda34b0a5f109ad8c9a6fb17201c257090bede0ba9fca628369f712eb3e80d1132c39b3e854e5783f80699224a7c2dec009eaa489fd1b692dddf7ac417eb9cdd7138b764cc0b7bd075b65fea073b2ebd1006cd6e3f6091b983ff3f0f57023311b411b843a644926f3f2c3519e2aeb70d2094fd5d05eef05ae6938dbf5b6b722ba07ade6f39c915e38a4e012f43654c19d08e5211fc6d65030dfb21ff0f9e8c95bb689a7a601d2a63306588d31b5e0ad7a01f0a05bacd19fb77dfe57a9241cccb784cb42f1861ca6e7fecf6b9767a812fd08750dc52b9c5f822067860dcb756702ce86e0ac1758e1cf2e5e5117544d8d1308efa70a858bbe8308f42f8a585e333fa25a70971de1752b235b52231667763b0dd9229e538abe0bf30d7cf05cf0d991454ef8461321209f1c3e5c462f352f2e0712e3e609dd7359a7a91b0aee20db249e851a0f475cda61daef225c3a56e1c50e317c82843320359fb85fe525dc936efc92b3af1d02a0298a42f5d2da20219b03 -generate_ring_signature 33bcbd4c5fa68af0c24eec449ea51bb72ef7190c0ece9dffef244eb90d6765da 732fdd3197aa6855d284d6bc4749adbb4c265336b62300984ebcf74d03d31ca1 1 878686a68ba5dd5aa696fd1078dd94d627486bd9c11d6dd328ff0a9b6c091f0e 658be715fa02ba1b7f4eafc02f985fe14c9f7d25f6bb4663ff87ebdc2d63eb00 0 9e33007e5336346e5106d4050e58a43ee43779c369ccaf8885f853d83fe7cb026e18cda48b640f5cf73067b65a3274b33afe0886693a952186f6a104158bb404 -generate_ring_signature 61c98e5693c58afb5c7311d89c4589e4669b52d8ff4571f293ff1f98af9b6224 68535c66978cc85a04be5b88c000167008d36429547ee67230abc78d01b3a07e 2 244b5e8cfbc8ef701d79624bb3499515be5273e45b73fbcfbde5b238121567b6 04cf46394b267c0bc9a5c9e2e5f88b07d59cd5a9bb056f94b46e816db4789be3 f0c934fded787d254b29eac8eea8e249d6217a82e8e9d5bd7565611394277404 0 967aa76d9317162aa629bc41b43b151ce71d2da0cbe8297f422e4cfca5402604dfb8018a2a5d1067d3c508f7f3efe77d249687918f1e6eb4e6c8902e89862f0b24586a542218dfd3218ec750d7e5d233bd231271ca805db28da94b55752662008accf21f16dd7a29fb4c93d510cf33d424b4da8852761f4df5be34b54f6a6802 -generate_ring_signature ccc5dbfe3a1cdf9f9c21948ae48b7cdf305a2054183d05654f6a5b8f1d93b6d0 406a3a1d1b23710ed1555c857e26bb6185a783eef109fbd102728811279bf26f 111 2921b78ba30bd3557e4b772b3d61ea6a16138631c4ba2d8613419171e99e66e3 ad1f075026848ef6bc1680d01e676f8fc292a83df780b49224e9b62c215f6eae f0ce3cecd97138d3819ee595d9c3edebaca5e340afe2f07183373d0dffa0f4be 26eb1a7d2b2c5bb31b7fb377b2523ca36648014dfd16c7f695bed93b718e8733 92523fa28a9a5267ed1801b4d606e0fa5c1b70d54c4634ad0365c6373de04a81 19c3b3475cdfe8c9e801d0be4def7e2d002cd570fb9d9234fca3a0011a8bc5ee 0e9593813368b47bb51b706641310e983b927386a90a00398108e8bb790ce5e1 5d46bdc035314dd0de17318cd46d886dea88c67150375407f34c0039989ecbfb 0e8d745fe8777c695390abcbdc771c9a7038cc327c417f3b754118c65b6d6f99 8b05713d11a953c083ede9778e7e7bbbf4410e35fea2af5b4320b0c7cc4ab789 13761d8cf0742d4b60bbfa4d205c49d4231eabf718ec4234f4036ca168d9d1ec cd2db44254c778fc88a7737e400abcce5d893219d8460c6ab3511b42512657fe b4534bc0527d545be7a0192467837a675c47e94db7ab43ea0773bb968a5740f8 ae5ce2355ada6f7780d89d1f33a249d4dfe7e079b5e102a82314bac3c04c3f6a 3838cd930a369c5458933289ba73002c71134a5e9dbe16c9dca9471987882c26 ff437f8c5906e641d47e53514ff94977417cab286d5f9998c0a7b8177173616d 9abfca55d6be1bc9d37e412d7c35202301060b89e1e700d5514d02d9011aab29 a3232deeb8e763d369ae5c94553aa0e264705dfe24f1fada06a1be1ebe93dfb5 8f51c13bfcf2486557450ea30970171fb0d428c02a823c7f9c704da2402b6594 1791024939f618fe68b0d73e853103b1fcbded7abdb65fa15aef63f55d8b7f44 ebebcd04a35a3eb5c9af95cd67d75289e616092523b8674c659d55209b9b2d61 4702673f7fa85237ae5f20e68b966f4fa9b68c8b6c900316e9f3864a2720b509 bba906384ec35445dd4fdfcd246f38654e8b9d1e3b24911e8c892687d1bff04f 1242ea27621579afd6aae5311988fda8be14600d5fc64352af2ebc4dd383f7d9 7dbe2bf95b8051ea0adaa52744aada889d89e6ad189ea26d8e8c355eb469571a 4cdf70da3f89ee7210375564f8d46aa5fc3a3c47e13866abac7c0d618346a042 dcd45fefa6e3de7ddcd3e105f8888bb6749cb565b2d1fe67bb1b56fef790fc5d 9507267fc1696e5c35cd3718490dc2a853cd2f6a249827f0235d6cb95ecea192 fdd370f6de86d821dd2ea27158eb2ba1a9f6bbe1626485fadd01a80a92a3ded1 575e8265225649995b947201fd76719e51e56f15eee0d5e6462331f69650bc7e c9c8a863860d12415cb601e9d8362990ee855a1e5d1d70cd75e15cc53c07e7e8 dcfe0f0f4119063c29577224bd9a7a1a3800d645e0db7547ea05389f45e73c95 76b4cabbccb35f51dd5f61dc72c717ea336795fc71d7510feba7a84e5beb5fab 0f12bd1eeaa102f5531d9fcb6b79423393fc47bf0e7f753c814f5c273a97ff75 86292224747d0e179c14b7a3e65e4561281b6a827aef6afa2dcb8f8b16571b92 1f9378d5cd626274da52a2b71027b5ee6335871550ffde522ff0a6f49986eaf2 6d5daac0ba917f50cbc0c8502489320d6130cc4a2b332b34556086c5084a5324 9ceb51482de662fe631eb9009212b401fe7138b667b7bd8cf94d9181f955f3ac 958663234c70ff625e10a10779504e267f643aede28861c4a0759eb01fadb1e3 d906f333761bfd3b113903255c1589cafdd20378d85665af168d0aad0367f83a 0635c64549ffedc46dab199ebf89fcc8ac599c8d89970f7133ab5e235e19f832 decccd21d763536ae6d62e9c56019358437f7176b0c6620b51bf59c5f2b0a518 62bfeccab34d00e92ccf718f671b735d07fc475bc5e4fd783e487666b0fff721 adaa55bfbbc3f50b640ccaec02646f4b152fef46d323065efda9bdadaef91ba8 82bfa52412799a07d83541bf6a3de59bc0af127e4310f09fea4d1971af8c6aa5 4c796345e19c94182721175a198a8ad535ec0f547558c7f07cbc3127a1ba0d87 ac9bc2d8ef7f43f80ac9d87142545ec4d14e54f64d4af3aae305bb5407a819fd 934000e2895878f33474fb25b205d5064c181277719336e36e688133a93bfda2 1b3ca7399c0b5508f80a645e083627435056ace47a15f8fe9f9fda75a0b17baa 9e360d75e54b81e3434caf08a8341bfd70abec216f1760388e7d68aebd87e6db ba19f181e552de8ee972884cf82d87abf397949851f9bf867a7bd57656fb8d80 a3d028b2702f532fb6dd99154d310afe882a709f8d0f194a3352f800b855c91d 6c477cc3e4587c389469d20bd69c196613bd50208f91324404ad58201f7549b2 a296d83f65b17869870485296b1df3bb2245aa4245abfad85e826dbd93ca4e21 23bea6d1f3a996575696f69c7b4f4e4b276707eed13cd00cbcc97bcd807b9bf9 5273d520380cd20c951e725147ee7aa942b743b8873b0ad756bdf30851dc2d98 85aca67f1e96a7e3fcf7ea9a64c5eb63dfddf35b692f631f89d179d5d3ecfcbe 84240ec649ba87138abdd7d1524c2b580c95d601395ecfa75ddcefb52fba547f d11fb311986843cef697a42c1db914979eccde0dae94d1fe7ebee7d4974be857 0af06bab640a620fbf4cc5c3a777e209c5421641b8ab610b3c5a2a1a47ae2a0e ba30f319070ffb436f5a5853d00f8013241fb6a6bc472d142f303623d33ad465 34132768b3b2dfb6d97c8eccffa9e4009230eedb6e84ddfed5dafcac3763adc3 c5903c815b8bd030162bbdae429032faca5d73777ce3a8248dd717578f0b8541 adc10a53868cc31d0d81395ff25be93e04212b1f45c00ab5f6eebc4c9b28aaad 1b841fd24b0e7929dabc30e700c6d312a8e3d976c95155a3bb3102fd9e0227b0 56f8165751e6767f607821a630ab9d2132661b4ac81e2f693e5c4f60114ffec2 6865e341bb94717a4de51578360b59cbb07a1660f70b0048d9475d28d7adbe3c 3dca83f1651d518dc458d331c9b9e7c2791ee1019cdf392cd722214e169ff2d3 72099a12ac2a7852c982c2345a5b13d7ee8fd6cfcecdf989430e365dbcd758cc 400f30e5179063981ae05f55129898e12a61dfd6379670fba47f4af1f1abd042 771d8631769dd24075741231cfda6e1bf41d075c4c60af3521546b2f6f2f7da9 1a4ac92b9c6b0ceca0a0914c21934774314b7610eb65d81639971606e2109a13 c6555b566f15c912b0fe560cfa4b5df5dea4e44624b49aa8a6209f9131109e25 944e66c093a3b22b7b64434839dffdfda5b8e0ddb6e93b3fb4e6c0d64ec80ccf 5682de8256dc5f421323d2e18e4fe8e86810eaa87c6d58535dd2c8d29d27d6f2 ac52d43a2c0fe2b23e73c67f6b0db159498140621b13b4bf2fa6311de6cfee8c 24db498c9238003c869f46647b4ec96fab351438cee5fe815ce700f5a83aba83 75ba2f7f38f6bbd6f58b59357fb99a74b46cb4252758fdd4e669d327a28fc693 74d45357edbaedf1f26950c22d83506ab3477bc671e685583418c81aa4c11f4f 61c814f9c7d4066bf95ecfc56a6b60d95eb63b0cdd4c0a45ff4d4f83367aff38 b58e16527d8d0435434e83e64c6ef570cf3717f79f8bb142a3510dce185286cc fb771fda4468b3ea8ee77c23ffa586486cbccd347fcac9fb97960262fc4ea76a a5147a14b7ef157b8288f781275b06f0b785c0888fab930c6a78d94ac818ff89 34557754636e143800091dd6e2a233b47f140010f36eb4ce27b48855b74076ba f95443b3d18f130e37fd03856ec8c6e38ac04dc9d528220481cbb49ed27a9f03 3a6d4cd695f507a0a8a404c02d1e0bd2ac93f4ef2e2056da9ef7a73e70d82f7e 5420f0d5dc5f296057a98a879fbc0d64605806aaad6b574da9a6e2ce9cab60b7 71b23bafb900f95b9d8d0deb139f0cb966ccc1078542b32dceef6849de962e8c 2255481001bf79311d76a4989dd98ff8c7f2660ab70f273b52024560d7d9fd66 2d291106b2157d69bc98c89e4034cfb9988d54b69a413a543074613cb1cf7923 3e38943714f3d3db34f5c12f375e73e99e6947fa661e86065c4041d15408eff8 7c79bb5943468383d769a23b260a0179587e9fbf3224349369bce1e5a49e4869 fe4fbdee077a24b3ac3fabed5c8708f4a5d9f844a8741cd6b2f539932d1a34ff b3fbe8f600c1f5a192bea732df2068edf5ffd6d766b2eba55ab7f6e8a8e4de8e 242b451f824ac319d890214a53a0c84ac7b2555ae80a02cb758343a607bfae93 2dcee51f5cf0cdcae0fca85c195a0f1ff1b3d4e468c9bf8d5dd6979b46bfac09 e35f7bf7fbeb114a5d73bc5450a2e24d171d947268a41c4da4ef322f387a20e2 5247a3383b66dbecec8e74af6fb621f5ae3fd77554c3c3f34f0de68a5802706f d727e6a804a5321ceca5df85777121c501e00cfc1beea4455cf94c68ca3beb8b 64a875b077447051c23ede119427c11392bc495a401d0631da8eb3ddc6af5c79 a1f1595cedc5db42062066166a58c45cf7065af7e52d54c26e16fd98c6defa16 91410d274f804c5ad21393f0f1758a43d0003d604e34fcc9ffd545a3d39b35b7 61ae9820c5041ff102e5232cd43fba22d68bdb5bfee2d997ed97f59edfefee53 d32a713c0ae6079b57526da8b355edd35cedd2ea55b9192e6e2bb97f2fe10119 9057bacde18dd4271192956ae16433cffdb67580478f52fb9e4145658f1fca55 8bae0275e1a2f5b71e2c2beb809e5a98d02b22abb8ade1c5356aeaf8587926bb cbb581f34ad405175e6b591d7f0d68272140a303a4044ad386bcd12489cead56 477283eb5796ec76bbdff973d760fb3a25f5a776d2cbb9f9d3a6e2391064dfb2 2096412af720af30473b8db41ee7c88ae13c2954d7078d28d284aa5bc76ae1f5 2086b5fff76bbc94f0df920c81d101e50708d68f0b18f3b2214c84b137126a8d 68b883a806d347d1ab2efb8d975c12725a68dbd162756d50454408fa12390cbc 427a77fa841f3dea30b971fdd3e26d79a6b1378674651569ce263b68e3654d05 27 c9792a474d8ed94f315117adb8db3de00b852f158492edaa1bf1a9345ce64d06954d455fe19f5736b7fcadfc1b8f44060356e97f3c043cd9053a650db3600e02702e6e2c54decc8c7ff405955cb15fda9628751b81a3e47109b3af13a7126e098dd905b97657689eea887a041a51815abeb2d8a95a655c80d358d10c3efa83011b39c7d8dd6188d040b5b1c507f76f6f4297cbcecb291282a13a72890ec0d60518a9149904268ae6d5b9377405b9d65bec14db2827422d939cacaf30e7015600931d98dfe59a4b676b0a783c1a71e64c5ca7d9f1efc3e4ef3e67f5ba046feb09b017327d692d3519a24464b2faf8854909324b129bf46c2726a12f686e3d2107a6c45522347d38f960a1260147d6bc3c2ce6a7d43b2b81584d091943358e2802a5e2dbac5abb1a7729783873c528d76442c102d32946f86f28bcb538f8cef80d663286613741470885912d1502946fbf9f44fef9fe2f9a983f36cfd16f7b360d28754ba2debba5ef534a566a34373e7830c5c23ec7f63bef8172b0e9d6ac8b0a359a66120df7ef14005b6338f6a7eb9c33104019b592ca048372c324ca3ceb023ae4324c1e52bd475e2a6411c3d117bb21277bdaa719ba24ed8a9a88306a430e26d7c1aec0c3f5e0812f6774265ed99c3ca0cec7976bcfe2ed7998e52dbf320b4536bf38c4b3118a08e5bc312f4df0881e73947c5794835de5708b1e149dd30ce5731cc878081724f896144c4f17a20213f30fe49e20c8d2440e3bb0e8db35063b4f5a3491011032165704f56becfc4c7c01b053ffe8e984d3d6a22eed7c820183b16410989e896f9482997b29b7909cd5d43213175084d9bd40bba761cda303ab5caccd7fac7167b1b2f36ff1d67ebe0949c52e48f851c3e0c1a9fd20706902a5368771f4a3324204c2c1d379b45526bff6e0a21a8a7ddca4f9d4f59bc09a0e3cced0ac95786c93f3fef089a641017292797344347956557642f548ed720d0f768b0d50fb9b6bf723e87dff55e702e6678d07b98f4217dd47cd1e8754da5402ddd7dbef15375f0143030a9f48568fcca9ecb507463db9159b65ffc73937860a586a7918ae4c07958e5f39a4a60f5ab5facc2acb9796775555f22e87a9947e028742ebe38181035551a6ef9cc822a562aade0c24ec78a4502c66720bea45ec0ea4485a61ee9da2a2d151a446e454b4219e84b4e16adfe962adb0c6ded6652e05e8bcdade1c95ce8ede4c77175fa576440a5ab051c75de6a75bcdb5c05bfa560daddd7c2fc2bd8aa8aa65b9bddd6d42bf679754d8b7d27925e7902ec7b1db76059c0da54e82be1c3a2ad50789c84113925717734098e3c2c9faa701392f76c107bd46b9bf99c40cabc36d03612ebec1537d240e0b6932ed021ee97575e98ea506cb439ffd5198266dc0438308bfa88328da606712bdd9e4fc10b91e0ad9a9f40c0974cc094cac423c8c9d974753e707a97cf5ae1e3112ccf4b83ea10cd94a2b076085ae1904701bfee5bc105135d3c5628c9e7692cf3c18312b4b7fee1db10a0dff74f42ea71820bd6e7e9217adc301556b9f4b5d385246bac3f273116b782e0f207b983842766e5fc9ac41720e4fbf2f5c61a6e48a36b0c6dcc5078c88e6ff0b4b0f69a644b6c0b0069d5973a171ede818c3c01a174476de8107772c55d8d80df0b86a14ac2dbe733cf6330a0c7d7548e78ca194b363bb64e2fa1dce1c9321064777cc3b0c65bc8def0c3a11a0fafa4c1ca36379bd35869e85da25d4a7b70707b5b09ab0e27e6bd14be600797eb2d43bf6dcd8d212b8f34f8bae2c949166ae046b36cfe12b3562e55f73cc5d890b5825a46bfe0b9f0ffa5df2e59fe57ac9f10863028b477c9168a75516affebcc52e08356af5157e836ad28f24c3cb38baef0cb87356190a59fe5e973b581febb29ee36644b34ee6c5e660abcf96f15bae58066b2c5021551c52aef8ba74764f78826c77feac6b3999c5d7247d26f01926c70a63211c894eabd7e6eef35773f528639b121864ab30be1e681fcadf1eeb3b330f2951a1e01cdb3a595fd2af1ccf09ca1c3518dcf5171bae0c9a8333db5a086005bae6646960a2f1d1ac25435f2d95dc2bcb5ae9b79c418a23acaa0649dbe4680628d92df7831b5b28ce03b984d429e9486b3c9ceffb045360b2fd8ca3387e300c712263a3c6daeaeb1548a52e581a5078082b9129cd63c23899659ba4bd205400a68c9583f4673bbfa613949b6e7464a4bfb801f546acbe4bd3325f944a316e0e9b6aae590b8f39595b07751f16e21fb4c93d5f3b6f501484e15fbc2134f5a902c082177ea10d152b969837a1d773c83b42a8861142b48cfb889970bbfcd6ec09b01a0d633f3d2426314fc45c3ce964c1ec08792b54bea439a5e91d68b1fd800f5bfd03c3248329d548a51269bd9c68b410926980066e396059ec22501411090d3f988795d400b511d8d1b5f6048b9d3c32c2c4f7cc909414e8acaa281ec15d03fc83f78c89100c35d604bd6a9bc55d542f57596ab9b21646945c24cf6a3e830fc4e61e6a2aa0a944eb65f6f208a35ea6ca146dc0092a2ac90ea9dd47af4b0b079799ce05938c7aeeb8bfffcebea7ad23d2c37a072eed6b5cbcfb55f77ec31803535960213ba8d2fe9a9dfb9c7963f92010789a5d4c45de2487f65d85f28c6d0c38fe761f3b6add1d0799cb918044aa041e545267d5a190c75fdb613fc67f3e0dab57e9f7e7e923f955364dbc9ec0df23a83e362d4bb7f2b687544fa5c4c14901e55441e0777b6515e4604ce3e21490c48a2642cdf94e4c2f1aa5bde5f4f9e903ddfcf2aad0bfcf67cb8f851d30af74ed495f1e6515f2fa89ae363e1ba7b1810c56c1495364922aba32f85deed3d107f083e2773bca35b9b158b78538a75082009349112467d4dff104df5cbe15382b84b82c9b698e94cdaab05603f45f139309b833fa81c9026a4898ede2169b2b962370371d16a4fca576811c4d1eab34a9029b9e2f23a418cee7208e9dc85b6cba50ca3c373c813f0dbb1612d67bda7ed00f511fad92b30ac627de73a83ac15a7389502898af551915c3ec0d0f90c4580c07086085ef82ee35b21656b6f79bc5e41dfbf4d9b12295fe3cf10ee27f7154f004b2204b698347180b69315ca862242b3cfd668ce7a566e65e01e90ed3d18d290d6101d5911a697a6630c1298ecefceda2daec744d9d489cbedc12711a0f6850095b289dfbc65540cbabe34f564bec71a3930d031500af0c08db812ccf2aa55102d3ed80206bcd317205bd52eb8e178bdafa2366f9127f0b5e8250208a20a5520b4ba3d45e68fe65f1569fe8a7802a226db0cc2cea9c39a74e665409a9a21cd90f6696b4a2848e7efc440928f9659c9e214eb428868180401fee2098ee17aabc08785d75db60cbf57cf664fed4dc2483dfa862e9b88fd36fabd9f3ca412d703307f0f12daa2918a798d46f8e3cb37df15d1214ff409340d07992a3ba9b18694a01199650f58e4217c012cdb3a03733d0c6f0c7ff2a90d2d55606f17e2a1e521201e7ce83b87c08746f1097ad7468a8e7b9607c41b0fc9d42204f7139976576b10f1cb5889eb5d358351b98a30db38f38480b0b36b3650c6e0c2249b746ea01eb0fd82ad8ee894f4d14e29c08bfcb115fb314ce63b0319a14c0b3ac58140e30ec0fce3e717c2e2a22676effebb4b2dda1140c9a40bceea3750563d764b9ea470e04bdb8b6772b2ac29ac929d4b6b92d8d16d5477bd6d90b196a1c48314a72f95f0fddb9ca36af3d04cb96ca7c22d0156055724c0dc49f9517d2e65dd1b3c68125024c63791771d94c05cef483c6eac799fa1dd412b347ef47392a20dcb991415003d3260a4e561b2203ad94baf2d654138e162e0d7ade81a93ba7748bc548e4270293ab8fc15ec63d6d9d7b1d80681d1b58fbb36b0ea7f810e8f46f4d7b7b47040aa682fa8c17dc21c4b77425a6c6511d01effbebd4a94bcbba42e7ecfd0ddea50c850eb64e9828100cc57eff447173b770426af2ffdb222e4e8e2c3aaa1029c2066cdea26cc2d7ae3f3f95a407b95385ba9207190d36948814d2d58050f6aebc0f6025774dfbfb328dfe402b3523d67766fec93a061d7e0d9fd7c24d16f62c100bafee7bafbf6d8e9a0557e254eb56fd53fc10691a800b9267bf50ad88280c37049d9c2798b7ed9bca0fb999ea43c307a5f5dc499a1b0a4737572b1c00541c1b0c4c3ae48bfa48a79f54a56bf97fc69bf1ead3296e790ce0d6368a4337dbe757083dd376ee334adf5307e775d4d14872e9acfbb5887ea23d602c175677ad029f07b01b7a5c9cdae1e2efaeae791a6a0d59ae80abfd8270c594eae360ada371bc005f351c69a4f3f41bf0241e61f382e0ed5e08500e36136eed08e9723d7786f308f8d2053ef695e391095a5c2113d8c4b2955797c13d3b49ca93d9392de31e34079aff4edf22fba02e49bd82b667c6e53c6c32d666acb817bbe5ff941065dbca06eef17ff089bbd3e88245b0fe6db5ed4f0f36c20a156687f135b75e05b5403b0c628cecd89f0e682219e900cc157a0b2cd21d9814aa40667c17f23c1439ceba031517d78a9c5acc4a5f41bab12c31d4cef6ac61fb4f4da7347ef67c3da2c2f10722baaace312373fe3e6f0b393de9380478818ff63244dbe32460672c82e928064b536cfbe657f162c7250c47cf15d8a4a4ebb7532c24254b4d06f7b30196180aae8e4c1fc9de4cfef7e764be27be97e80984ef05ba3c71218a0babd22d835305fc7f432eae40a45ee9a426fccd42f5fd816afe46b6e89a2af7eb5f2c21fdcf0af8fb6e9faa6f8b45e249ae77e70b4c1f4a114cc80fad5ee6ce1606787ce1bc0c70d2b4cf9afb1ab48c58a38a3bfdf886b95ed1f0a9fac214398eb5b863d6f401f58ea3c5b267a5087904b03c87c511f7c477c226d3670f6b877e632b3306280564c7457f15d32124193c6f91836e38f1e51e09c670ff4169abf0f962eca96a0a809ef331f2813cc51cf5d5f601acc4a491fa910a788c778d69425db9a6797c0a4590241f68bdc0e2dd9a677e89a2cfd65b094ecf9952474ce9c83ce58bd0290f60ff3d728c1513869de646dec36b863be4d1b6a0608c9b46e304818fa2a44d0d9dd156209a299b840933229b15374ae08c74105694c78e515ad6adbd1b459206b538613fd322752d9de3fadd52e39a87e6a8e48803a1e858c6217841b37197005ec2b50b1ee8a78294d6a938a8a3fd35696e086840b8e2f85b388951068a6501e00d845f383bc3650533041ef0242dc73ceb8bb6c22df4d0af63ab4746f4cb0f582e8fc3a3acb2fe86b18b26efda5ef34a7bb4364195fdcc2673112884f11a094c8ea5970cd1f9032b1d8dc7e1291c4dbcabf3237f8e4aa126f84f7b1d9ad500fb96369348a58f365688a763bde285aa67c4955367f024c841473461da445409c6cf4f167a2122528b68a02ead545186307e435c0b06db0c6a88da8a1a940b0696eeb1a2127c4aa1edfc86af82fc3fcaf9922ecd7e62b9a1524a732022c7d00cd5f53a678f1b8470981137145014568dbf4c141e5ff8e5e6671c2591f681730a394ee2aabc2f7248071870aabb4b8206d46311f0d0eb2bc81fda4998441b64013366ca32536a7df7d5d74dbf0f65116fb133fb29d5a5ac2db6ed501f1ffc5c0ad32cf244233a1ece6de45020cd38b0f8927dfc7fd909ee440c7dc1476f4732015ffe4201bfb80a505df9443164ee829833bb94acc64610f3d441d8d000626004379b94ba7124bbd1bf88171116263feaa514d1bd31446413212e2bb40b2731036106f4a6d14472304a134a022ffff9dc6c5153793ee7ef7adb46e35f99f1a3015ec639e975fb52acf24846e0b4b8852b2976ef046cb139f1fdcd337530c6250173163a19303d101a9198a5e86161fa34ad747a519984620eaf11d24edc6cc8089b1565affdc9ed903823c9692c1bb4cf60eeba36b39a49d00924f8c670e4190e398383539ca06358d0de2a2158f4c5745d09b4308bc89caac1448c512b2064090efcd0c8c7afca41d5c8fe5442154229c53753ea3e333b660c94457da36330081f41b0bece83a0f684c969cc905642e1628ff7e0ec1847328a1c3efdff3efe05f11b6a12c0fd6c1990816d9621f2c533b16ac2474e1eeaa87b8477c4a56c9b040feeaab08331e5be5e6c1f7ddde6baf7a6a50f9e37fceb04bf2bd588e79f3e01a9bb1016e88a66d44b0e38247ebf5fb1a23377c5b4927a8853fecf4fc3e3fa09177949bdf418f69fb761ef79f7a919f294ae06c0dd6895abfe7e6943972645015c06402483a5aeeceb05d7a36eb53e1bb9e818a39c49996c3e0e3af16d477e006cadfb03e5f25eee3ef1ae1da9175c09866b35832283067d98783f222478f30825f59b188d0f69f916946f8614ea06de4a2861a0eebf6b29b5046eb8a0751807077f9d1e896df8d0bf9445172113feb82295136046d9750a464bb808fe13470693b4137b5cdaca5bcfb07f9f0b77f05bfc9643ccd597c08ef4d462baec2d8f02859db7248167550fe484dbc955a4695080996a4901802f49714c956b6a68950e09fefba153447b8119f473570430c9ce4f3e527f3a830b602dbb00d75cfaba019b88177f871bae33931ee3b26c1d94c73eddce9fd9dc9f90436aef8b1eee2e06d126ba313a3e75103013972b2e1e30e6ed3ddcbe0d37479b2000e6126b70a402abf09b5745233f3966c8ce0bfa01551e1b3c2d4c6b82040e9a058202b678a20bc1d96941ffb83b725fcc0b0d1f2dde1f5011007d47ab72376c300a872cc7280fc0e187bb5d9362e2481c03ad1da3e92f7edfc43f17301a7ba9105c42d2154904c91bfb95cca5d801dd9dd626c6163a936250f8704d36d7c94b84b46e2ad2b30ecb58e100d75b274d322f0057e9a48934e581f2e3702c6bc1e619b783d6e81d036cd4b2c4612c6d02927220af8014c185dc4250b1a3805e6ec7a9d3200ced7206478682d3cb52ee69193aa3d5fa750220dbd33d354899a848d562f6926b28910e9f790ca308a6b4d4c1a9d4eb0284318fca65eab48c13bfb8367a19719b7b9d0ed19f3de017fd75d0a8a73fba37cde8a46d7d1d4339aa4300d88f03129dc9c109e3d6696a7236925be8753c126aba9a65c03ae9da6ca19b8f35f1d95f4db28f0cccc61a33c4622e28e7a052a62847d9ea5b5d18c7fc2c9164e7ecc25f589f690bca6b103184be4a0bde9e0ac32bd57096ed602875a17e6a22cdcb910674f04008cad41d6980c53e5858c1fd4796ca7ad8d84aac462aaadab2408695b2a8b539085b20de2ca4968a85b8fc46f3e1a026f7c97b4c0c0e0d7a9d4af11a69d1cb260af5e292201b60955c328a7ddc74e2a3e31f5577864756636b85906e723042c2007ee2033612b209af788a359f96a0c6f88f8e391f6457caca778105fbd5c37c07f90fba02f89ff93c29aaca3740a8a97ba9f43ca764e9fb3a409215b5a708ec00449080bae2cb785e63dc3036178478fdaabf22599d65c6a3fb390aa22e285c09f219bc2c37c058373f37c9b8cd9ce7434759878b1ac4ba5defd4d185453c27002950769793484dd1726257176e110f3f6e86850e267015776a64c008668c52030de7ae99aa19826f71717fde57bb3b3e0b41872db27d215af9690093f945180a97340a943f4b60f358033438242d34a5f36fb82dca1bb9d73538f16123a3390eb524c35ccd3bcefea87838b9a983270b895a03f4f9371533df638e30d361ce07e6d1ee021f9976729f371ff08531b0787b89802fff05854e48761537f6db060c6f6e3e80783a3a5a8e3070319c7804eff9b99a47442dfec29f0185019831890abdb8962e60fa00ded0bc44bd838f595120d29dbb886083db7152938caa91da08fee5d2cfb40dd9a44b35e91fdbe204057d5e44e7cfb5ab62e8eac5483826ad0dd396f62b55de1a0c7585aeb79b95d9369d1db4aa71bc662a50736e1d5963720568bda234b54e68d25004f7fbbf7b55193e7f1ad3f2039c0dc194a977d0cd810a69515f1449c27a48b4406d624d63056ff514e759a55bfdd11041aee06cc5fc007262d75f700059177824fac664911899017c2eaa33e7349cd4b2e6d5b3d2ff06fa9c3b4d70288cb78ae3ce6f949fdb4aa4e55d08673e93f1c01ae69ceed3a20209050e37a9d62239c0cb772736084b6848ab0b151fbe15944345755fd8ee33005b7361ce325f1102640a3c2febaeb854741517126cb93574b61d0bb23939090e016b8da7878857e1c3e0fdfcc2eeec5f5b4ec67ee5e34c4c809acdbc9f6fea047ff1688c45416e3e57e9ffd984c15eda5d935bbca1b3fa79aa84debe2b60290122997dcd973bbba4ec74ef5f4d1bc96993bd23776a5da08e9bb8082361a464023a8a4263b83ca52a960c833fda184b3dacd111c44316ef4a8aec095919e7ea041e68081fc7606cbf2a91e0463b94356ae15202892c48bac0e0fa03e5e5437b0dd7d9a116fc9e4016996f4badca5af1b5e22712dd2d9af083c8f72fe746905d0a4dd0d54f26d73da6d941bf9874ab21e6ee408ce8afdf8e309ab0e711a1279c02ee67b0f76326ad7c61dce46e4e665852b8512c77f6c4549edf6e3fbfcf670c0495f72de17833715b80d8f94f999c5d828298373524620ae4fb7c005e992ff602665f38b113729a5d35b91f7eba475c4e8edc01a526d4ad2b6e55c45a8daf240fa53dc91697f479f49d8725bb16d25979247ea9e249ef22d814172264eedf8a01b775a40c27a551f95ee229f153449214f193b4c46fd2cbae881fda2733fa2805c48a2e2aff9dafb8a32fc33171ddcd876d155832b32fc5bc009415503d4d930f0cc0001c7c93eda209c95d2d3460f02b200cb5a7e31778f9750f3bc6eb24930e19769b93c25152cb2e572e294f514c44eb67b42233fbbc1ecc0cc3709faa0b0861ab09f3e8b9f8d7faf9d42d80d5651bf74871f5edf4d5e48a85cb384ebba400d6de67f0926079f56ccd18d87487065cb4b7261b56d8012e1350b8ba4870fc0a1b61fcce38e9b10571b176b381176128a73e20206203d191cd15723008f00005cd318b58658615d6f34aa6999ff88de209944ec1d0a3466fcdd6f804d5ab0800003f4a6280aae95d97cbe9114a4c47d2615e611344be4052ee933bdfb4d2c50bb06f17858bb352e8f01d23b955532135e62a98876195551a44d42a92fd18030f108d1620139d0bf80cbb174fa5662dcb17131b88c230ed7d269c69a7e45e55096f0c88d4e5c83c4490402e00724c74fdd9edcf799190147e6711b8e9aaaec10b46e247353ba07e6d042b42acb6a46e0ec0846a87eadc9d53e4a72ec24c7fe00ac48e12afbfc4d3b96684025674e85253d88ec4ba7dc66a08d5f263fb290e2800de617a3f0d059608cd803c6097e272e726a348be174bac91d8c777a47c6a8e0f45715e79cf92e407f433a47df0bfcc17cfd1a9f4ceefe94ee4d09940ce7f5e0f2d05a5ed85876d3c62b21685e955c5a69643450725deb39462f63c6c64646708c61aa914fa2e95ca21c9364965a11f19f7fa44ce230bf591fd6b84ea91aa230b45c249fa3d6e7fa4ed826b54ba82dcb8e1b4006871d0bebcb626abde26bcaf04ac9af0a4167299c0a5b49f5f273aa947a06bb6aa492f910c6d807f07940889042322f819555f4d177835bfe6453d464b321501833e5b81169569a6ecbfc4630477d071de15c128b7334680234e608976847249eb38eb5d4530347d0f10abac0ea677e2125bcfc92258a8c8cbab325124d23a38a4bfd8431d595fe546e09c0202a14d7cb39b3b84af9cedf731ffa42f321805bacf31abfcc5235133a0d199c50e7d8905937c6146f8d5b9ea80d5e82e4aa3065fa33945fca1d496d9a95003010dfcf59bf5da37320935a71655427b8b175e1145b0771e6c8ee78c13b07ecc8906406c9f13c18a6fae156e901dcc5f160a5ca480a64ee4920a224f455dd5f5c50ae38c644e51909c5f101ab5b3c4916e52b527f82545e7fb27ce98822cfade59055a76f2642c986674292d35b911208374a76e5f4509735c372e2fb28a758ac708 -generate_ring_signature 15e1e8785c6672c8c9bd42e8b0b9dfa73ed16b7312e067bb23380a5662884e13 df79782b5900dfe6d529fffee15fbbc1768ed0a58b535232124ffcc89073fb56 1 5f5eedb18339d1ea19675e00cf95acdcfbfb71c56a86c51c473752b1a6eddf22 d2494b222ddf6a368339062b29abe06a0fffd651ca408844a13e8e9c1483c501 0 f486ea9525cbe473ccb7d36b4f39c98da0d5a11435a4db701277e89205637a08c548a2a5a40f8e878c48c1fb84fc975aee4f3550eec44c75795ac4c9f1bed308 -generate_ring_signature ca505427f45e3f3327ca3206a0b5da1292e0cd7361f6a625dd9345ea0d03da47 9fd40e7e4bc7f5c892c8000666b514a73ee6603def5babca6b51285c40f27309 2 839e8769b9dd788743f455e74f7253e687cca012d0ddad7cca0e4767e4bc79a5 72e824806950403597e8dd402e2913d458aa49d39982318165e32e5de46044ae 76e8413823935c97bcb8c388d61482247026874a53b8d31daa616b6f244d3c0b 1 47f5bb2b5512a29d55cce31fdd206818b0b81530754b6c8d38c5a14b10a41108254bcaedbfdcba04fd66805b030c12d6adef005219aeeb251c4b0d422821ef03bdaa824b0c8269586c156aac66fc1503a72594d88c366239bcdac96dad3cf900aac2f1a5027224bddc6cb26095841485a9ea8ded51f57f89d337ad328fdc500c -generate_ring_signature 640933f68d149a459fd5d65fa800ed1bc733ccbecf94b17a8a6678dc6e90e3d0 7b3e6f1907ad536d7bb54b41827c3d9483da8e87b5041faf6cc7fd235237f3a7 199 e1fd6f2f9381f01f62f9027760c178dd9f5716898395fa150bd04f9c1bcd7257 0c32219292a9a10f7902af43cb0b0ac820d2618dd2efd266e36d6f7bf375b546 44bb2452da68ccbad071ceddc6bfaa5a7a0471e67235b5db25ae0c401c848ba7 ef310ce28bfbb3868d079d856042448a1890589fd919796efcf0958a6fcdd49a 6138d4ccf0a22b094bf5f6f4bb25431c73194d4cd455225e9f90e890ecc258f8 6cd95891a2d29e19eb1f2368ca8c5bb0e17765269ebe249c2c9d6e4cfec2e701 518bad14481f59ca818908ca5290b892474cb9da9ae78dcb4684df6758a12fe6 486ef59a6090636d737f22e6f3c6786d600d7dd50084fd5a6802f4d1c14aa8fd 7b7ba8bfca6105c550f82244e8dc88a3d14f2573721cbca36bd2abeb5dda3361 6a8ab23a0f9a41e4f83b815e83295d23f704b0a46bd7787e017fb003111952c6 6e629c6afebae95df966e02ff8ca6589152dc2f1a8d79ea4ec806578a861bfe7 bc0df98eab5b4834f520506a48092bccb07099a8a57ab0d05d73a1b644ce5a9c 1bf49181176e9be00603b8697bd817908ffcaa7f5b6d3483e4a20f86a14ab144 2ee400bd275960a00316ecc4f4d62b0c07c77b5b39c9ba35aa21183e01ce7576 643c7057cf4f13239a05fb4daa429764b6c4812e6b31ff31990182c486537fa8 72c592d097ea1d533270ab38045068d9f8d3de716633a16cedbfa5de6ef1d94e 397c51ac125fa63b5872e6ab21da9a04edb4769975f0eb50fec6022b4163ad79 0ef1c332ec45f12caf7434918b5df896d7ff57dd0c5bfd517a99d1e45279dbd7 b85a252d88a0f3c910836ebbc1917a5fff542a8b6e193fb17de53aa7806a9df2 faa62a1b7452db23c98f0e36f6a9978640636d2a9a0f16713e5f10c09fe3e508 328f24953c88403511a3544cda71c6011fe7dfb9d56d3f24f0ff21368eac61b6 df1d055e26e6a5ef462e906fc0deb64d8b29598bc9bd85b5458082089bc96ddd 6c212c22396b7d5581bd5fb81bbb666ce3a184aec90d58d5e44ba1de5cde5bdb 4ce80f8e5c3097fd18e3bdaf8ba2c9319d2c038fe2133cb567699bf8ba797e32 da98eb0d9f2c7b7f966f5160a318169c9ac34176486af194cfbcb3d72860fde7 4660b2e8c795df61adb43ae7c80fd13d4f3291a55af278dbd315450a0c729656 b2847cd4e5196c29d705c1117de6dd6e517140346ec2331f88fff66555b453f8 74ae53ec473006db85d690d8241a46b390c62b66038ab2cc19fbb9557ac410f7 60b875aa3122e4bf0b8404ea36c488f0dda20ff1b4a4f3a23d13b03e1224456c 781f2e0c5693d7ffe37ea904f72274a969f3ca5362dff6587bd7731e3c8af2a1 2eedc4786cced370cc96010b5be504e587357a9fec120022c6f417874e885aab 57f3046490762d405a2cf33b0e42cc077f962b4762cad12d73acfc7812e53752 95d804b52fdee15815250d2de834ef304b6097fc5492370c2e2377290094ec84 91e241d7a90145bf0320818a46cee8638b8000afcf8a79c4573b9d63f1aa75be e5c1a8968de7e4e022a29419db34f969f0a32cad3262f609633f6fb7078f3836 da909ec5404bc643cdf16d774275fecf796a3f114c333dfe55b5b5f3cb605571 3977d5e358dffea1fb052a1cc7aea970ca6f1d5caba77633c8a019c099fd5c5a f4fc33716b9d5a9aaec8707fdc0ce658cd2f45b389c8d0e5cfe68bd986999389 816a38588fb04069185b53c8e7a152992387822555e6a42678a3aa09ed256b2f 73c235791b37af8bc5d20251695df714e2001301c2c42e6f4f0f6933b68b0b3c 37a1f5ffb8877b39e7f07c497b079918e528943284a86ce6ae4bbf92111c63ab 81127e3f9e51799349ca316fbbfc66b9615b81e34c1da957a2523cbede998e5f d022b7db7aab2fa830ca163e9db126143615a14d00784d029c1ee583b95d4ffa 518e4d4eb6c823e6ab52675ec57090510eb1cc5171e21ad531e97c1ebd9c84f4 bf18ff3b5f363af34722d8703b4ab8f6c20a5a5c00e70b29b4a0e2011248d830 09dd17995fba7f2bfcaa29dfefc5670acb38139f95c015604150699ede968e11 a10518e114803f176d8cac931c689a5b9aed0719fed4f3cf724121f2396e2bf2 840cb0975d20cf39161e599dffafd50389930787e3c9f9d5422818074490d30c e079333d9d7a6e99f86881d2383aa810fd16b12bea9762f98a3420f14fdddcc2 2d966bd97f8f3942e1943ece7b23d8125c4ba8899ce0f9f46784a2d1b38e4d7c d65bafd2b7fa8030414db8d167121da88d4e623e43aaf7aafed24ee837069785 672b6a10e067b982811c24a5e83cc2d70e92d45c2de5b499f906a8a9d695bb6d 508d519efbc059adc7eefe047c8eda930330c27d83fad61c2a41b502f07d4e64 239b57469cf421c31e765b9891a5d1a42251bc9e62c5dd55be426c500f5d7e41 bdf54bdc32858ebd44760b9afb27ec803fc6ccce43f5cb72290456c31a120312 4b42e3bf898e45da9dc4c59408e1f5aaf40bf11b8a4d09ccc66b4d626784fa04 0ac47f6b42bda3688fb69a48aa126d607e26ac91a443b2d6fc7669bc52a8dc2f 940f30149014968e9cc01fec132785bb6990f01ac0b7bc1ac97b3b5eb6256183 f81895b0ba67d9850828ccba479bec869c188f2c2d254b5612d627ef719a88a7 854708c1da30f9f98cb05ca5b8b8573321d4f729a7344921a9f376bef75c9e51 8e34b500038eae8d855ed7b80b3a81c05d6664958cda88e2518bc9e7eddce863 48fb26a2d6987b3b5dab2991b90e8a149dac0dc5021b198d2dac0a5900adef51 ff8ffee9e4ce4056d5bcefd56d6b06b481b72f96430ae41fe3320e165d122d7a 0f35e5b2d3736b6a9be73a6403c6ba0f98a938cb6e06be3f700bacf83e2540b3 07334dca089b23c1d1cd601e69b5d64c1af0347861f3754274d9b556ac9aacd0 e02f32c3a031698d5b6eb871798306b9f1f083947ef081e37e7f58226b6d09b8 6a0ce596efab1e09e24f83c816a2910c0f18d054a914fa059c41b1ea4c78f6b2 57f619f188c0a869d043f4c41cf43236c8e1ce80f12b6168233a6f452b667392 417484f00663dcaffd323823225e1cd0cf76ad7df724db16cce584492173e15e ea96e9c65d7759c4a0a9be30e0ad102f4dd0f47ab4dc75af9a8fcac816528d5c c01db326db562a48369f862a6057bf152e12154b9b71233f0b21b80462f56ef1 32050cf1fe635c0df1b584a947d370f1274e290fffc192dcd6126203518e47ce e6f2f9c2565ed7a334c0ca91efe58820010aa1ec1241e1b8827b23935f6895cb bc851af7e8ee6501beb8b4d03a7f0062442d6033ba37d5ac2f25144f7d01aa45 59cf88f92e93c57c35d98a382be18f17c24df3d41d8c8db2a91e6d5ebbb42e12 31f592ae1cae0de2e4f399339798f72f287afe968e9bc56aabc323a9ca0dd278 8ee9c8c5b1d3f378199fc47099e415fcd802651e208afece1284d94b5f45ce3d 508b5b43b6344dcd99d4c13df8d80f02055f094ff3e9d7f6db3447e01bc441a4 74872c9591414526444e50e95e0006a77b98c41b70c56a647772160c77603d7e 21302e5c387885ce1359e32b0988d0c3c0b645002d361994ee2031a1c9c06834 9bdf64b862161a2556140305b68339c517f6f54a247f96f238d45e1834596414 1693c3cd7e03df5737e6f206435972fa8c100d68e5d55b1efc6f26ae85e16942 1aa6e0e8cc7840effa32f20a21c92b48cf8a676f69e3465cd1594dbe800e798d 97598a6b0042768ffcd605725132c323250013d9ffdd4cbc46c71995803fd6a9 be9c2ff2e3b3e788f89547a687a2d8990d51c1cc70be9715840030715f14bb6c ad23094b33c609916ca52ccb48bfc68cfceb423ed2c230b4230d34409b1701ee 98a4bed71cc89baf62675cf7a8617177d6ce4f7c62014274ca9a574e974e7a26 c71c798ea3ec9ebf1f59b949e539a67aee11b8def522f6ed695f433b71b159f1 b298b7f06fe863b3ae360a12e6cca2d5ad1d105ae139fc01325be21f78d1aa70 57932261b773012ce60c5e26bd7a2a87fd66d47f0cc3b94437fea1cbe0b4145f ff35cb6db2c5ebdd155010503ff566ed6d1f5b1c9a36626ade48d76254250d3e dce0046a37851c5026c7a4ed303d870e9766d84a9e6d4e5976dcdcd60f34d4bc 6d37a87c1f685b27af20795e89487f790697b5c99b97dd0a19ef0711a1ac3733 f7266a5cf5b3f7ef28fb4e2428c5547c670a056b110bcb9188ac0a4452e39c25 a2b19116391e6ec0526b4af1f0fb04508fcd4306c91c7124196162424f6191cf cb1cb1c1dca53fa0e7b104127ab69699cc55aa750a9474b9517fb375faee3862 797b0ca02fed4c418405d88223eb001882e2665c0e631dea72205db3249a9903 104956bad09c091477f9906ed41f63490a62fe6e81898b8bc7d2044c02e7a620 74773d104bc903adb07c4e8bad78bbcdb44b5eea9bb052aff7ff96daa8f97365 8787c10d273fc7c43d5b055cbb631cd6fa24068ae2ba576049e029a174527222 6b13581a144e59149e402649568811cfe67d363c2466a272c2503861219156d2 61eb9b1780fc73fab757095aa7731b0a5da2c813cc36d1e607363a37fef27d0d 7672f3451e80b4fdbb01dc2efc655ac562defd71fe7e9c785f8e2d43b7ca09a8 2402e629ec86218046a59177c682f07d298a2327e203b9ba29aa3a7cda2a905f 2862572827a76baea9b113cd3057dae5021169ffaed7353a8e8a150d99914941 95eeb5f30313df74b524624cb8ca9397c97814698fd503eb80a915325d4bfd65 02d6feb3e2e024540b00f37b5b2198d2ff05d1398e1d1c1f3b73399befa20a15 5b52a566cbf14cd15d44df91d26109ffd242010a5bba7692d8b4e180e79aacf3 a713f0c2e91fb5e99e2974efa91de6fc0609c64a0e0cd2bf849cc0ee7fa3fd36 73ba4abac7c1325dd63f832e34dee41d4189d90e64f775f904bcce4bb3db793c 0a7857cd3c77a4c8449abea91b3d424fc8f7b1a39417efdf5f1b85827e65621c 0bf93c473af049f8496ccef2672503d3c75092f1dafcb7da8a60b7c6bbb1386a 5487792ffa3566c4c3c551abda71b6d5b461add6fd81c2db459017af4d3686c1 38bda5d44463d0841416199cd4c7b5a924b1965a77ece07a069763893a9cb75d cdd3fdaa97260218c247273eb293d4e90e4b0a0a2bfa1f72302fb98e1249fbf4 cddd8cb104fed1e429b08285ae0b471d5d0bf74fd1606887171f7176bc9e1253 eb84cafe4857855499e373ef983a2143e1b1d6155e94a3407e2e85f164ba421c 46f8579c8618a783fa34d0d6768d7dce4c4460b1ed21a24d4b48117ab148ec3c f8443fb9597919df3d9caba2a9836b1a3076b78675facdb890a574090c94cced d297d85af7014563f80c4f1a0d1886f17af1a43de53b34c449d660e35d08eaf6 1855741e4cb18127641e4d80db0e585c6f3d357eaa5ab9cf134e52f3023bc2cd b3eb8bb92cbbb2248a347b7d439d6e7deba3e11c0f491dc8dda1234d33ff7e59 48eac61ceea14de3a054bd98e687cdb860ac9dad6f3aa5732c1e2b8c2deb8c43 05a7638c5fe710f66a02e55b4cebb837de550a5ce2ebaf2f036b371f366baf47 6781eff113890f0dbe3421870aaf39fff6f64a9773208450e3c6cc5180fc0446 c52ef31914ef680e9940a5d04d6faa9d5a3bde6f30e4392a9614b4586ea11fca de390cf19cc66beb0772ceb5713fc9d06233071045499e7baa08f0aa398ce9ca 50a4b637e93b15704cb72105fc9e0a8c08e02d4bd386421aa42df597cf32cbb0 5e5cf1bede2ca83f83b04c7a3ceb54283e769734431060946b4092a6e3f1043a 90107a3e5bd79cf0c9d14f7c41afad86e1564a2bc87b96b78f64448278dc83d9 5c12fd730cfd26bc35831b953ef6c88ba99c02f2ed682fcdc5a7b45eb2995e4b 6a5534816c314480e918bdc6ace3924ec8b3ab9f7aabac1e46931df705d0d6b3 d33821f1f4480e39146c5d22c5f514d12de2acac4ffa1add24e94d0aef01e3b5 0ccfdf97d8f4d1c3bbc405a6c0a05cb10c84e68a9e828e91bc95613184285887 d8a28eda02b6054603560444fb09c586c851f6d7dc9cdbd62b627f04ffb1d125 91d7a87a8f597ccdb99c8a1fe05cc9c025aa267c63a8512f4ad9ede0f598817f 095efc60133065f2367438149bb8fe64be7a8e2ee6f0b6d66e7c1d44546863d3 096b3016663a9ba25eae4b5420d5dfd2752753526b5e300e6632a52f581246ff ce0f9ef751a001b0ea6c95c8f93f6065bf242b4ae69d8f0300243e223023235d ce21a3b1d7cc54cbee8c3b2042e2251d2476cd0e2f73119ecbee806492eab3dd 93976f4fe773f35761b0a3d56b5f260358c7b04960d6b01342c13319e11c1ffa 09c8b32547300288d6e9c3ecaeaba6dd15c7fa037a565b201c961f7aee9ef1c1 decb1e86aa6a956e687aeccc633c688d76fa9d76ca8d7b99961b1591b4c3acec 530080918050c0f671d21b63e4cce47b639790caf570a32c88700998b61f77d0 1cfe24c75253c93e025d78117ce0c2a1571e2e89e1687a28220c222b4582718c c4ff26336a238471bef959ffa39222ca67bb4eceaff89a104dbf99671b92d7d2 5a306e5353c2f33d596fe151c616ed716d9bfd68237038714b53dbeee2185f0d 7c0699c7e93eef0f2cbdec06332b23fd9804512d7b9a709790cad1291ee62329 07b843fbe8bb4882fd4bab8c042dc86a597985fe6c031bbaaf14a1ea7b1d2a8f ddcf47a01055394119fbfc49ba1dbe50eaa73f3cf19cf8b60a96326333bd13b9 6884f9ea3a3c4684547f6f8613d1087ce35d5b14dac078dcb27d63a5c3a7c626 c965f6b81f78472b1730b1444d9ec15c9fb6b54918b7b3f4993ea21c9610bd34 bc587636da4ec349e715ef7cedf3ede92d7a3cb0905b2dd35046f01dd12ffa6e 901f65b107e0217ce7569b32cf50ad1cfb72834ab06803f5b53db3464c6f05dc 51d7b3ee2af84871f009a8caddb325823200ae4462e88f3bc9c94b9bff00459d d85bf76e475ef81747d3eda905cce4fa6e876605e102b9b2e8f72cda9153c8b1 9d9a63453c0525302a0d2e7737449abd52163c08f24c1e37f15a527d6be68c95 b6e86edd5fb169911f22137f1111a253fe2bf493cfc989dda1f9a880f3d5ef9f 7d072839af8950bbaf6313a4cadc4a04f152f16ba3e84ca932d1445f9a34afc2 177f7508e693dd881969727b67446fa7eb9a47c26e826d43bd3a6e0ab5834bf2 901823191b933476f034f77d166cc5bda91b0c41daa76c781bf098e13983885b 922f40a9eab5b333199265429c4fcf25f459e09ebdc63d5e077bf733dc7412f2 319fa49be35d561499b802e63d8e2c61849b7639c623785266bf3d6dad7e3b18 07cc2c6e39549e7f76fdd89aa4a9becd09d31822781a94839007720107462159 dee6471f5a489e8ebe6ccc9b1e2b16a2f6d7df7919372b2115498781ff90efb6 29ec1cedd8d6ef03d011b35c17debfb9d4b202d964164928daafc80c66c0de08 9fa044d016d3ae76f4479d8831f4005efcc6daafcc78b0bb8cc8b65f63c8e9a5 768c5cc4a18fea67f416195fe7e1af269ad1829cb8861e19afa6384239e6894e 914752c736372d835b803db11dcd48156a4123895095f124b39279da0dd5236f cbeee34f7cef744beee1fda22bff75f4423bcf170e5abb20fc7bea38cea001e2 9023bf6f93a11c44043ce93f694351453c492f86feb6b44928d253473652f5a4 124a47bc0b26b26c43f6f23a65fc74d995bbd282e1044f56298b54b599c2cbb5 5c2774eaadbdb04a732dbcf9c23c77c335bdbcf686369e6e8223cb0274253508 f46bdd8fb828d1177c62dba969af55e292901774759d2c79cf8a5f1a57acbb9d f2af226a6a889ddc46eace4b559a7ce081395056ea030e1c0f4547b539c4b0a3 faf7b038e862ac015e8190049ff5cbde4f6b437adff070df511866e804ba4959 827f334cb6e5fbb142890c745e63905e0f0778d2a170369adb64658014ecb0ed 7b43a29455e7bf9bb18c406d8220c9dd42e737967e159697f38fd77174949c27 cb633ae5fd91d1769d88557b5a428183ef703293f5065b578e9fe3ff3b1bdf15 fc811147b7c40d424c287edbebaf6d8e7242d285103d1b6f60487287afb4d848 32ef999686ffbb7655c4aed56d167d2d7292f711448e8847a1a05e55e6424735 86389e37f7bc902081321af80c748af26665076e553bad8bdffae84b5cac4466 39033a1d13f762335650c32db88aa2c19a128014946330e09a0a03ebfb06f604 f47a687b06ac083c8511527bb3dcc57b8483e295ef2f24b3e84280e739f7baac f8451f5f9935a8713f1eccb30d1800915f83e0736b8c71c62cd34720c75dc7b5 227ac01feefc3da4c2e7b452e1cc37b20e7b5f4effe6061ecde47332385355aa ced1f5d61e7b24c1f65297ed6a611d92f233473862ad54d1a7134007ffc35e03 cda1925c58a914d8b11ebc4d2668340bd55565aff5c118f8a707141a4313af09 29932f04e23bf8c4e235571f2ec07cb384da86c855d2e3b45fec4051a6bda5b0 3fe044639d70fc0e5e6757db4e45df0af07385c3a25aa4dc8294a477bf39be08 bd398f48990e8183d93117a6162841d3df3eb78811f8a84e101e5931cbca754a 52b8ec2637c8327591ce0f41a81f77603ca5d508353486a5b380f3c495ac2b2b 2b6fac9a5788ac550314d5ef4a85d855ca12f866dcc8e55f63b7898ee77c1c17 880a74bd63ec12ffbadc16c57915d3098476cdf5b25c4e8cff1de4b35009b7f8 96bb3115b7d40842c7ea3631bfe35184e2d16af4261bd3fba60c2913e4911c9e b3d442f2d897e782bcab66f0a1de170fa5171f588761a3ed7f80a04f59644abf 4da03bce5db4b41ef54d3a5dfb5140fb1794332c20c2cc80e4184549ec38ff68 cd6abefacad45eaa7c9e295c08857910943d9b0e6945ba77199ad93b5668eda3 6eb4e11f2e62cfa6164dc0ae1ada61ebaa6949a07cd88303c633835fa98c1e53 e4705bda82e65f57781a596d1e54871dc587e1a614b77eab59911c1721d0090d 34 eab35b0eb7faac20a8c03d21a2d7eeb758c3161c14552c74fc1e4f516a31cc0569defbdc2be4ea96654398883db97bf8d389970c8140c9c708e416216cd1710f66e804907319770fa0cef00cae6aae36fee7b2c08d7737d2df20a76d5627920d0a52c47e10047e6161be0f072e8d62008aefe7c2b2be8605fdb974b04bcf000582aa1d13b07305313319a2c5d85c61d3a75cce95ef64e5b389805ed9d05b5806f23e326a855b7aa0afde99b7ce3ac3ac8ceafd320570930f0bee91596eeb0c01d6b107e5ac724491ef9985797930594d9000e9b0e7dc7221083558474a7bed064b18513aed7d3d0a276e196c7cadc6bde6c71e8513a5e49e7406d0e05744fe0abe1618a0acc19b5c9489be7d2ba5a6269791b743d93ce9098060eac4eec41c0c98191ef618e472988e2f76410caa48128a31f14b6256f650b08ab24faebdb80a91bea2bab9c13baa04d5b779e67e055258b231ef14b71c717b0ae8041060f601597c9ef0124e61d978abebbdd4ace0acacfd7059ba1a606beda4d4d6eeefdc0fd6b18ca2e9390ef07869fad1d161ba24f522352820d2f1c24ea97c3f9e6746008f6b38984d68ded0aa1eb332b4e1b0fa76734ca7925cb329c6c95de76855e60bb6577c3ed4cab0aa65a2a6da4122bcc676ac5fefa9b86f06c822bcbe17ed1f080bcffb11c71504542ca054c0c6162da4da2d2b7c1484b2f3652acff2ca94a60051404d365ef03f79b628ba3bfb710b193e63aad5d7f0f4bc69249c6d8e13ce08467cd348b5e89ada62b9e38ffaacd76736d13d3c063879667f19fc24f552260757df2f1f0b4849b0f31ec64e85804221a994276bd803f9390a53e87a04e8770f0534e0d0a44ab77ca558257fd22b4ef26b74efec0a43062aafafd1c7b0f78d0b1e043ac6a1275329cec962257ffd71ba6519aa7cb2ad4da9507dd8ea1f0cbf0bb7b5eb7f56017acd43533f07ebd90d736f0da9bba8e273c954e171e2bdf96b050255b4c28e773caea463d2061142fbef5dce37d0b8976e5d990a93cffae8d009dd6e3779ed5462d258649bc0da56f1ada0750bb31caaccbb50c0df4d063ede00b4c3a77421ec734358755befc2938b4199e2c683aa8fc924407fa151c32e190b62a1c6542c6fc39ee08c3e7fadf6847788d22632d917a372649a605ba662540b396197a097c6e5414a2d44742fccbcc9e991ba0d6b57c3494a60e65eaa6ef406c19aa0fe9f584587ad09dcd0c2e3c22b12313e54d3db36ca8e60a7b836f5470cc22791fb15394228bee5cbccc65472dc62eb4e6963733d04651b713ea8239c02a0ba61e5f1d61020180bc5633bfc536d9ca0d630c6ad3f5084573d7bc9766a0f2963b0f1adbcdcf9b53f5e1354b273e8c0d142a1c1c1b6429fcf7a8ba6322b009d2e2af428e841a5ac67a05dfe304d97476d37a968c5993cfa5cb62d09f3bd0d8dc5c94bdd0b81a02e53b728a5eaa24b96591f70abef1ae98b7ab07d0e48910a7f56f40ddf7919a6824a2f492f95a2458de211fa488cee1bd34c5698294e850ab454c7dd6e82d547d4f8f283c57ecee87584eebae6b177d9d3bd8d9c68d93d02046bd7d99d0f726fbccb4e24b762a01bad30aa9b525d537d878ce29ce38741045079e2a813f843832576efe11659baacc2510cb21e88759bbc772294ce4b41058a9b5acf73f73164228f76c9fc84f74fdb9fc0605306ada5991b090760c16205bc9b587093298d7f780e45780a0f969fb7abd3eea7f3a56335c6d9bdcb798b089cad88617d7578834ef1381dc38eb1bb98eda40c6d2fcdf1236acfad70dc510640fac254465a4d353b0619662471b6667e2398ce7a4a652cdcb9008d84cadb01dd57e070f179a8d0ab14f92b16f9599dee5647d7b67d46044d9120b46de2a00e6e52228e540c8753d8aac6068672abf64a7d499fb5ab4db53fe596b3dfc7eb0c4e03be965cd11ff438e36a67fff418ad7219053f5a017eb30a635d8119a0f601a9fa844b4b908a267f185690b875abfa621e9c124dd98d2755e8c0570522d80fa5a0fc1cd2395adeee91f2bb008afde5759b97ae23e7a45359d5a01881eaa108f371c4eb774a63f63f2d835dcb67a02eb55b153f467ca47aefafddbe49a8f4018c82adc0a7aa5caf94f5f86cce3bca731cc5d8b3168a92d007a55221c92b690dd99605bc6454b4a2a57529d2aed8fd7ac8402fa3c34f2219820f2f7818d17500b663cd13990fc3d19bd6e933d83abc531366bceccf6dc3f6e678271f8d5ae809fea97b731887dfab5e168bb0c95378445893260b85ba775c3f5f5e2f293ad20514c669cbf62624087651b323f2848076cf88393bc63c707b6357b12eecb94e0904644c9de8742f331756ac61accf7c37784dce1de81d399156e8d51d8c35c70a80c654a623cfb1e8c113f33ff274192dfc75be91365f6d7c1e32a327bc9bbf02d73268f24fd6d59d3e9cb29b46999b330ae8e47de2f9af526f7016979bcd27077b15dcc01fed4af99996a6a169d565306a573495e3602085d4064549bfc61501ec9a11cbc159efa2ab3f708f693684d657e8db33c645896cb644da4f661f3908f8d0228d5dbda20f6c6eab73b7b259571090bcb5cc4af91e180bbe44ac99550a15f6af4d16f80400e14ae4e007942257dc5ee8b444862392e6372815b2989a0e810881afb76f12265a0c72b3535a8e9ed7ab86c4b838ff7230a68905e54bc10c38bc5673e1458269614026fb9d635701af26def6241aae2efa00793e4a76850e2ad6b2035c90131792142d410f673147650a95738355189575cfa523a530ef0cc95c98920ea45f12ccfa512041d3142e30a7978c4c818f563f6dc21e9886ea046922aa220bf17ceebc84e6b65ac969aad536b43201e2ba5627e6a6b458eceb085f4472333da95697cb94da31688fad2de9bb1b0a3026d20609a91f6b1e220f0532c3192aa07b64daf0bc21716792564232fc295c36e99cc26767ed25d9c2eb04f2a1eaab5f9e1f50c061b6eed766f7023649fba0e05a4dcecb356cb3a0a4d503dca2414b253014bade51656835f41b64337624625605f488fe986c8c75a48404b43d38f334e194e4b9e0c70ee0ff4571dfdf1be1699825b18076c23477bf790eff233055e4b65cdb628783b15d2e3fbd2c680eb3ca297faf7cfa0e16a4293209938cee000e291be6b19ac6dea46c532762140c237ef55fbc22ea143601ceb1000434a6268f1fdcc484e2495aa0a2bcb2f46ae85f00e75d122852a9746b3c410c81d10645a1e1cd4d2fdb783418448e4fe6ac22dff3569a498584620fdacdab04c964b70e80d45c6d04036f9d6f50cf0e11d60c3dfa2abf1725f7863cb389bf0e6347a96bfbb67e81f379da165b020f5b58745d4a87035f7ca8b4a9a9bd4d6c0a58015f58779b921bfdbcd2ee1e7592d48512663c138913f4c04580a16778930e4a51ac2271a3942f79ca265a8df10af008501e129048b8b6575184d5544e5f0b0791c5a6fd02990a9193c2cda4743a48649807c679f349b20167842ee39e41004cadf3fa7c17771b008d396673f7de1f502fcae72ba1f73ad88614410108240bcb74f5731b254a9d69aa1074eca75a9af09979d2daae1d76c9718b6ab9aaf609394dd906f1d778af8e310916fa8c09ccf4891773a36e6b0b8c0d45cca9f4d20ae20cf93f41c81b51122c123ac74b67ed461a9a9dbd486d3a90ff62b48ff4b30fd49dc8343363ec35aeacd61f8541bed134246f08d09b295bcc9fbc7668e1090b151fa9c0aa8e4700db71aef6aed6a21fee34463edae033ed97a38124c13db80bfe522b83c56d38ab982e4fab8f8df10aebd284f4b14214b7d758613f5f37c80ee53831bde2d5a94c4063f5a329f0902ecb5b0bb38e599dd0d890da04516042022c2557b93ad33d5f5e80a16692d70d0b8d0bacf61b10a1ac12b668f16db3c70977554cc398d34422cdaa93d19b85fdeac17f337feb2cec7051d69b6d7267740928902ee3e253c307359d2ef17a8031ad472aa4aa4cc57bf5bf8d1b61826bd401b15588a414a35aa6349c69ab5c8866394a0f64e8ee36bdc83b4970b46ff63f02293d8f64ea8c0d05d5f9e859e48038cdf33944e037ff0a082e18b226fd4d7404a87cddbdebd154ca2b429c7e43217872ee687e29c42e2b6ade8ce2a6e98ec2022414bb10ed9b278d59af11a1b5df2849179c50f2ab410f3b17ec75ddc52c2f0e2f90b38642eb70118de75092cd5680117646733ded0a43a281066a27b5712f0da08efd9798f0c2c77e04525fdbf458260263bfe830550178d329f368ceebed0ce18c2c49211a5093d86e403db13452a7ec4ac04cd274a668e089bd80f1f61503501f0ff65cf5f963d81d55000b4235e9ade97214d06410e8949ae6de698f5308fbe56e7644dac995b943927cf9b62c44f8cca03b1ca8b0a9b9591c4daea5800f2b88612fb294b178b95a4ad96d1efeb6ab13dd42d75971239de80f2ee533750e17d92f3b15d7c922d72db517a0eaa91185ed4019f91e41fd39ee41ab58dc9f02561713db73c1e3afbf17bfff5bba516d4c9468e48f3eb67b4b8f4d562055ae0b223a016022b2b6378457f0f3e8dde196bc10d2e4c040d1983ead7c57039f1c0b755f8f63d86d084fc9601223e89bda043adc5839779d7fa408d625fcc450a3003ff5f4f8da4303c82ba03e8b5db1e5e1bb95fbaf18f6ea4b16856a862aaf14089da16d7c35f229c48ba351a449827889d84ef124e1bd811f87243ddeafe482080711354139a90591aca01bded11500981a4da6a6b81c894caec956e1a47aba0948f5745e7e15b88a7b80080443e342737ec997125713c9ad2411e5ea7d34c902d31a412291f52ad92793e3c4afa8f5d90a04366045ab5f975efb433e9f3a57002f5daf31730ab1b73257fb8389f7f260f94e318013a9398fc2506acf5a282806972c721ea1cec5bb5eab247e178119b6a8ff3e4fb5cb5a67a850a74220bb0c098592100f75b2103b3fac1a1629c7c26e5140059b7636b34f3048316a5f4e790aff6e2ee6b59b124cf7aa21a34d9697718495db4bdb65d7d25ea2712c26cf710d6fda52407236a749d455fc172e38d3448afb12de70c9cd18a319cd54d950430471af0367d944920028d0449952ea565e2d53f09fedcc7dc4c2940fe59a494a032b7833587757b7c5158f8ae87bd9d690f779a2dfd0139eda6de8036bb2509700391ee788e5088296effbd14e4953ef3eecd6c0a1ace19d7cfb1a0af28385950b9060b6dd2b875407d4a1a1e1fa3d07fe9b45a8cf841adb323baeff578f0c2c0e4d6a249a512d1236da0135d3f9408b5800d52ded63bb119e51f8fcca4118930ba8d90aec1ee1be2dfa5fb795814c4f7e333925b50747d6b0c686311d9bd0c00d9606b30b28f742426667daecb39197bc003191f92b7f0df945d3324eb2d7330b20729f32f7f182e782e13e570f0f0737e8cc810986986e490ebea392ac391f05f823c0056aac8314dedd570dc9a623d8254f0e782973c0f9685f5e6af5a2400e20e0effb97e2abd96c10c040f1ae0a99666ffb51550be723c2d30767c8fcba08867e75a900c35ef3ecc3c3a28c64526500a6736d250bd5433062e3a8f3443f0374eea01fa289d85adee1a9e2b0d76705a4bff6efc11d97412d28bf8baef14f090755146a46bfaeba4ec6a10b27cca291f4016353a0fdaf9e5dc29afca2d85f01c6ecd40b5acd71f32b9e30e329b9e0dc055088c6de789518875f2b5c4219f30b0bcf43bf91fd20cb0150f7654a123e2f1a12a3d03be327e9610eae6c7fe7d90ea6f866f8cb4bf2772318dec86e4e70fa548321833e4c773da257d28160997802456ac823100705c0cf5a7c6a4aa8b7647a95ca59eadd1a02848f5ddddc8d96068e7a015756bd066721f758a438f041d915865dacb86ab3dfd07a5fa2b0060a0ddfd57bef8bdcff7a37153a69155b19c0b07d7ac2c9b54efec05f5d3cb21fa40905c03e21b8d203dc00044e562f571ca1ff6727ee585699977ff6d1b7d7e45c0f52766ca41327d51ebcbda56d92ba3f5e716aae70bf77c13210d82249b46099015f90aea7dac7bc56f83cc23ded6ee71b342310003f11690ee180cc9f4a6ba701cf52141890f067e5d5bb57aa2f4b07c04afc3e7c29ef6a4a157cea168ce09300f628df435ca397927dfa5df9bca36ebfe8615d43cbc90ba91d0b679f0f696d0905b76efc5b932f13ecffed9fe1a01155247459b02cc2ce60510d5cef99888b03be252493123e0403a42e73eb93b3c60dac8fac42eaab7ccafc0fe2d2f4c7b60a754663a499d5a3423ab74588658d5d35c8692f4e4165e2545a5878daeeccbf029d1d7bb94dc3f91535f262ee9a11de7326031510dce9f5624f6b03a5e30bd808cd35565ec0f1899014e66301469385ece1f93890301c4d1e33e31c37bd9ef20e3011b5cf5bc24a578ecbe6d4f7f5de29fd06e04cfe3e790172bdc63aa2bee301c2a7efb5173417553d2f59389d73201b1d6536fbc963dff96e2b995be723d903e00619e3c920cfe455a4942a0a26fb4d43704a8d61fdfd8738b8ce7bb1b3840f669cedd06ce21de81c02039503e2edefd1c0ca55fe4c5a526ff489d720a6e10130d7539107110285fb6fa54532a00eb7eee2d3986652c348c188e821ecdaf00ac25d0bec27b0d9a4ddf5478676675afcacea9c8b9cc7f4c059b6c85a3909be0b39a740381d9008fabc6f7e04b50b6c925565f0a00cfc71af58881061d3e517056a4873a477f9562da01cd499c504d7f202c3535fb804b1765f7c7e2f6e00390ac86894a1e34a4b0913f315a9a9f18eb10c6f4a3051f7928ce78127413860cb06a183342f910339b8e1ad9805a14c1054f5b4fb2c9b25284363c56cbe32eeb80f736154daf9525492fc776484cb0c104def46f9456d1f1951e3dac1bb52d351088a97d5bd580e1ece6fcc3a8d3b79d591add4df080de958dd1b5c52fb7100c20725e748052672e3e0ae2241ac8c75f70654f0914d8338b03959fc317f95b7e801c21fa172cbc875a2a3b6d1e248344ab56eac7a0bfed84eb013ce4835ed9d12035075702878cabf3286c3838cd05283fbffe21d12b38f14ef6c331b9e7a4f6a06fdd451981d9b50e4757a68097b26b8db036f33aa889b865b0043aae77060cf0784979b396c263e528004d0dc5b665315e994605f21814c89d8428e1ea8b87e05125856a8d6ec3fa6f38ba862b65831463b5b8fac5888039500497043a930430df9a07b96f0026b59a2dd495a5dd13a8fa21bac340c0d78d79fa6c5f57db5f40264c5ff156f58d213b6c65ffc3040db37922d9d0bb9fe6c8ac64e2896cfdf620ec1b232cd5d23a5cfa7e9445cd8876828d3d840229dd7f1d9241aa9f60cc32d0fe7ae7ab4fbae1f572ab13e27f17346bb43280c5f59948e33fc66ad39fe80de02ca16f65bca4b55514c460528345482c4ca2f82937aaddb8a6fb32f09b17a5000fae2f9da3eaf2d3b79aad176178d220292fa6e5ea3effd69ba872c7b3268490bd9e5668d6f18f142b5a58614b132b1e323f32849547cf1c5efc29ff6c447210b748657658bcbfe7b306e750ccc2bb6a82aa41912e369bbe1602f22b65d3e7f07bd90fcffb2ae1fb2f73687585fdd4d947eebb0795596feab9e74473f04e37f0854b62134aeb671bbfcfdb696534c2c51315de51295041cbd42daa08dc9619d0aa39c897e3922c5fe8e45d59933c9c60e6f2bcc00c2b3c5e5c7e8f2411edbe1063624f26fa6763e09e9b5c35cecbb96df569e8b08628ff789813d198833b52a01b79cfb20b5253437b66bef30fe184144847d0a6e4a4aeb4a141a9d6a7c63930febfd7829abe1277cbc8adf2d7711df55834d9511b863d95aa49d6ac164ae4908d1be4a68793711ff9a69a31b49e72f54925403084aa6746ca35eb64cfb9b960fe2ca1c4dd9194f9542b9f321a7941330e6a2ddb4dbc03d4710adfc7d9d6c1d016edc722a77771a4487ceed1d8dc08535088d3f6e3d6cb0a176a29752f1ddff0a1718e2aa5bdd6c2370e0f4f5c73bc4d2b4a64382da48b968973bafa31f2ed00bf456612516b33b043aef2596c33bcfb77136923c7ca0b7820bd76fa3f520d30763d6f76bacf654b1cdcd7eef49dc82ecda4488ef4e3101514380994935ba70043e8f15bcd425915cb0d045d617f86f03b7a48725b1c074f0c6c8fb8739aeaa00b50702bad512597cf5d2010070b1b07595bdd1f251cf2ca0048ef5c3f0c326029f31d92a9be48bd82fa45373d51f78d330ee34b48ca41566f5d0d55a9a117e0c2d12b176b0db888da562ea3290c8144b8b7fb2b39c8ee8a2d7786e9eadfa8e09eae0957e2e385f64ebedf24f2b086528c74f4bf2d3907d73fa38d3e829db470bcb3e7f5e3b9650ec9e2879595872adb8245bc57f323923ae088d5ba0554d540395a8a23097b145115fc1d7364509bafeddfc9ce4b7521e5c19f9c0d21b6f510a8f0c747b82a36fb921599e88d0d276a2d65468259572f8478f7794eab3314a074e5b43009b55d5c61369c35246e7fdb548d3dd6f2e55787babc64ef4f9c2de0864906a765a50b406ea7e157929bfa9a0c38d3d40776070942405ff5e52cc9900606e3c1c55702827f7d57bd2cd4f0a0f65b92297ccdc3f2b76cd93b918e540078279a2ff80f0b35023f06e61322ff75237cb0ac8d60e475341b17d3c92dbb80773ec441583f4d400c98ac6845e8542c81ec4b8fc8c89fbeaa1f860ad476e9b099e03e0114f58ceca9cbb1fc3ea5df9e002f6c32c9db1cb3d7939a066e06ac800ba4f9b587482ceaa7d3e498a756fc8aedb8fa89f8a34799daaf2c3bcf09e1702b713ac06e2ef92db96dcb37bcf2893e88d48d2838c28e4bb4e86b6aa66d0ee0a326075128cfbbf7625066751ab00fb4352a9d0492b454463dae298b9632b660722fd38245e8cc023ec6c134bdd40b1d029b3c6b724e7d9a5171b618ea69c7b0ec4bdbeb6930fb9dadbf4d65d9aba1e9a30253e4bc85903f4d3ce2dc85077290fb45b9f586848587f5c60694fbe34f9107fdd21edc71faf8403f563a4d3e4750c06eb4c82e30604b9645606cc9f3cee535ff147e5be9bedfbbb4a9cd64fa5fe0f4d980faae30d889907e1f67b8b400bcf3b0f2d55b9da858afbca8da48b57310e019c3dd275865c319105ce95d59db7284d205d242dd66028f94a4140fc1c500056b45bc6eb4a6962a567c2a3abc7777193b5a86376170d4f17c2401e2f91c300be2a042d78a7145dea718005dc73fff75132bfe013d9d8fbe39839d8cf6bd3086e0cae4af81591facd1dd20b304513b57986d533e1af38f4a2f23aba50be530d33becb5e996ba173fa189f45fb4df11657faad34f838ff2e2c07df9f569f3a0497300606284757373def299631f71706b8a8013a6cfccb1249a797817c098507963dd16286e02fe1feb3c4534642bcca05c6a3d35764cd166d603ff18d33be0d9deca603b0020bf9775d811547a3d5169c36d419f30036945954391762d1480123ef24cc93b9bd52458d55a274ff91b359457fe9188b77c2d5395eea53e42306bf8aa8a2ee36aa89e04029693f8d24dc31398e86210233cb8447c7da5214230ff416ac2cfd08fb31cb7bcc7407acef6b6d1ced36bff4a0a6e437aefb008e04043429c489392c40ac316bb950143b2f1b3b8ee1725fdc9fcbf91cce00a0fbb8084a28897842dc2a809d2b06a17756a622affd8abd40d2cff0bfa96841828eeb06169b780508e9d8c386fb8da4c2d63e03f53c060132c20aabd9a60dfd5c0d840dccd5e395f9395b42f15f698d5c4eebe8dbb517cbd089051bbab054917d9a60044bdf5b333dee103500f6af67208d5039d5e8b63ac6e67849af2b2c1b3665c403f6656f2be5c08a580f7f8e34af051e156ac0258daeb411759c39becd4211570d15f31d238ffbfa6dfc6157de0e7100d58ba5a4bccbb75a01367a9af9ce6f99034d92a5b879c73e25d81e3b736791f987029fc698fc2f40399f922c68caeb8f088f54dc16fdcb680238273f7b9ce618c6e3c1ad609d7781a0f80fa3116c83590c3df53f8e01df16c3b30f12df42633707f54f8b6c523ad1c3d3be548163905e0c4b4a75c783e2f0bc1e401d54c3057174e28d9c925891ae36ffbeee0184ca110a9eb6da3198a02db5e88e9f5fbd2c73810ce09d5ec7723fd6ec34f52100e03d0fe75c8a9f1cdad646942832fe70055688889edb86e8e0c18aecc5894e05472a04d155a070f9f05a87076a9457f8ab605c8363321df1f525bd19e561ece67e4105f58453971474653d49b70acb9c9b4d53d6ed6b2664a51c2d2e50d9b947bb8d063818286da859891dac3dcd492e1a86ef18ac0153b272feb3bb32c7d61b56be00e42856593ed9eabbdb70c3172d7d70026151a62e4115670935ad1605c211f80fad9f20a958135e787b36b92b9e057f999106de228d7543bd0a0e9506d0938e0e77b86cf4dd2f9faa3578f4377df649a47b7a19532e0fb64e10ae7aab51506a040b5094f995ef556bf9433c7f945504d72084a8ad64e73f40db40ca1e1a487a07ea49d395c64642eb8c35f35e55a497675b9a683f33a11e585ffea59b0fc880095dfbc9ff9ff00a671494d9833ae8b0062c0dcf2135cee3b6a24234b7a7cb170a2acaa5f5378fc4e2030c8ced0496049120ed285cb57df2f6609a3583da40a50e08b9ad3f2535275e62362bce099f575d10c6fc066240ce70a91152afed87280079293a02f83f5b4d592329d29807a7596d86fc52aa6c8997023ea515dea81909afe4316d1e189fe9e6c0f49e165a6f77c7681534ee26ebee26ce12e437ff4c09b694b119794afc5d103b63f32b26ea40996ea076c4652370703101c005c0010f2a5844e6d261736acb580f210ab1da9e37f3497cbbfad97b2567b80430bf960db480076ce40396aa7d2a88104322af9ffa868065664c36b5ee277e068cbba30fe579925de2da9aae4c74efbb9e37638e8e5e5b6246a75623bf12fdbfc1c30d0c567b59303386beb0035690e34da91c76bc30866947cc1c327821456d731431063cb7f993f96c2a47ed70bc6e89d9e59f5209e767b7be3fe32c5b2bd15dba5b05765a2a50f860436f23bea831633f3f5e589a0e2dbe0f15f27f752dd7364356038f50f56a62cb35af38d1662d972278c7fa5998a430b5d6a15ee95a24974d120b4e6a03fd13c2b3bcc0229dc3d50385845bbb16d0945bc10557719ffe42b8510159e5eb0e14c591d83b31c955a3945282a69cb6d31dea5fd1e483a6da4226650e8e21569cd75a37d9b75687afc7d636d17bd0ce2241fb21a0a069388e331dcc04f3cc266394525534f55f6d7a4e508b0080520c31d5aaf03494877afdcdb05003f1a6d8d31ef8e07c66286cb260699f7f018c017b11c173f513fa1dcab063bd043dbd213a41db96f8fcc402b5ef4c5f4df947f481f3960b96e5b7c2045b56d20a7dbb9cdbe115bb40d27369287063941d551e891f752044b599130f94103a3f04e5c9a4d9e7e829fa35db4a7afe66e98d584ef7d73d3daa0e743b6b312d46110ad3bd699bf2935043ab2d7a41ab7c3d95a45db76a75c7e0d949c4bba5836cb5042cd4ecd74cd6be5f64b8aca6f0e36c90b9b946e9642971a5a18414c8d3063e01d8a906d49756da16f69f456d7b5e84c22802132572ad06efefc250c3a0fac50b2cb5d17c198773437047ec7452a8ad89dccae35c8c0ab4fc6ae43618072356099ce84afc5b1527e6fccde920f73b242e2e22e187f0c9134efded66c3c2030d08182af5b5d1637f214fa808f8354fe39a2f1c98a2595c78ebd758829d6558de02ba5abc8475f4860fefe1450571d9c5c8ce04b72a79b2b139f5a1d6484b6a9f0f04b8add7a7d8bd8b35593f63b0e4128ec3b1623d5a602067fe78176ebfb8fd0e530d7c7e298d7dcae7fb3a1e24bd2345ee515dd30484a1b738b4bf11a07e27060b3385e73d29fccaa464e42bc0835e781f1ddc79d53e2a5cfba5e2f2be117903819ef7a93e9e37085d2fad2305b3d1f315a80db604fc467c00dc4fc8b462d30bf4145965ca6a511fc378e587daedfba4f9984f07b4b1293c16d91355d2c2070de485003e58bfc9b0ee39035731633c5745575bc4d3c66e6bb8986a4c7e143f0c7ff7b4efb46c95e36f10ca9443ad485766e9687350732626f1d38f4241f6a209757955ac6f81a4f8cc40b61ebcb88c97dca2774ca40132743a5196d23cdf6509d454e11bd66116aaeb70fe63ba577bdc6bf7f8d619a4990046e834eefc55320fd61da98686829f22b2e70e597b48de276904566ae221a8ce6352d93b35657a01a8a700fd041308c38ed8b35e940a16b7b481b51b14fa7d9cdc8123c8d78b8d0d3154fd72e954575f9add4ab23b5ef6d63fcfa923065a0e164f008eb4dabe320a51339013fb3fd8cf9fb1acb930a3018eb66c7038ec8e9f391342abbec60e830d0500479c6e825b59eb5965b6214d2a76e774ffc1a12dd0c78a141438bacd930da2f82809cccfa517f2e6a97e79f83c27067febb7460384a6094d38fcac97ed028d25bcd904234a9ab70683e0ce71a675846705994c8fa6caa9a2b23725f2c205825fd00c0b71da03e28eafefa2ef65bbb794f603b46ac425e1dcd060a86b210dad690318a5b4b6dc22a1b02dcf3e08cb6bb005288a267acb880253cd91a85204ff6db55410b720c2075773eaf6f415b3cba32647a3ad70eeec2386e355c39803c705d22106ac35c06ee8e6a05fd1d5f3913424325c9b0c869ee685ee83355b0b39e02f640aea1aeff172e4a12f8f6d996b6967dfa10ce1f70944aa6eceafb605f6350240249269b83ba4ba4695f710ed6ec9b3b5b7b7e322883f24d712cd2403da325188651210c0a88b8b443257a8f760627b3633be35314cd936a3f09ea4073aeaa7d8baa8aa301e435981e035dc2d212b764fdcc9fb65a8c04c0b39086d09d42abd7a8d438dbf16bc1e4795091a00751d2e5b554bb0e9bfee1ac62f014f09593122988773fe35936b57d59db5476dccef7651fdaa0d273fcb0a5b106c3c08d9ed733e2fcee4c97f649380b5665f111047a6fa4736ca70bea8f3d1c3a4250bc8a3454f9a88323bc670d0b1ac52e8e08b66c92505adaa95ada0b80929d4d30475be64b13829d5a746d2b3f3ac654388bb8f63e722da0740fe77996751cefc06558d95bae44b05b1351c6852f8176e3475410c4adfa7923da407735a44b71106c5cd8fd617cc727530c0982073b9c94b2b1d7ff80a7c2e93b80182514f2da10a122fb6efa275d9aa48a14803913174b2c5f947f88891adc4e0bf3878c9cdf90c18f1a17f51e8ae766efface1df8fa0483e35a72e95883d9efc4a443ffbcd350886c158ed55f7631af84dcf9974319a87bf6f8478f4bf23752271b2df831af700d9ed970afd8d9f0cf599b4e5ea3f7236a104ba3a82b54f5122441bf8158b550b86367ffbc6d71137dd9eb048ec554ce0c6a88fe2cb31157a94644f1550ae6603e12f853b47924103dc9b347126d47595afe689be4f36c3340aa07447987e4a06aff100f13c793b149c03abb26390e7a26a89a4a3321ff1164621fc7b87a999000208bf895c9193f87ad6d9649e9d6a37a642ea3e1b287729be0ee6bba6f9c90ca625305d4e2db3c2252a5b6a62e8ef322ccd2b2886e78a6ca3f6b5b3822b1804cdb228d98043d0e90a89406e0e591b2f90449a33860a2a7bd985b30497d1bd0545e402218e840ddb7b3f4fffb00a90295e611ff5ea05e4c0ccd8c7760f110705c88f997b8a3a43922e5df0b61be09b236feecb23c40dcaff412a81b5d1e1730291d0378e9fa5f737c4b8adb94ddf9acde32dc6954b2b3b8890254ba4123e18059082c254010e93de39be364e7f31f67cd0c7afd22b9cead93d7cfd7eb647a40dff518c859693cb36300d8f3f425fdb3895a407fac43228cdca6b431e551df50f874bfce2337677232892b364b8d846a6fb7417e85919680b2b7644a0eec22b00f7f2edb10a7075a67ce59acc52cb3c0296005df044afa5c49a538715cbd9ea037a14b1ef399072d8867f70441c1c12f4487b7795413f5c4b9390dd59eb58d706f6ea3212cfa6c883885bf17de4ce3b38788ab699becbfb6096111f74ba624c04fb34419b03daa19acdbe83d190b6772ef62a19a85a59c43422d87e9e92d59902fafe568ca0e9f65995d75dbb75afe39b7a04199be7a1d3eda5569be6da107c06f163728d3e70758c9824f8ab7c6dce1af9f7efb973d1bc92b6833a3b16e01e0f57199751c9c678221e4723436fdbc32622b44f9e70a77b1c461bf7a3709a900bf39d71a686e5aa8422a20341bdf318490377000e467e390d2d3fde090eeeb806abc8eb321a1f3473f93222da9f7cc423b8259a69ce1a7b897e8e000fda4a5f0d2f9f046bc6c32f4460c063a0ebf42052d50a3ff445f9fd1a7bd546da18100d05f2edcd3d4d8bbdb990d4ed11d66184515eb595e2a683e348d28a65cc8634de0b4df2a7229bfe47ff76f889330f05c8aa7d7bfb628b425f2e0ca9cc2c1171ab071a8cb373b96533d892896f7e19156bfd20197d0ea8419009b1cf22458dceea0dde173822faf0a3ac17da322c885782b3593e39f440742f72397e8e3f8c15a30494dd94997e13198abf3e37e7e401be4255646a942adbd493fa44f40c376c0d070b67eb0cbc008b598eee224a9698d99fc11e689ec51fcd35b9bd2a38a3628f0f8e5c0180ae8219774da53ac6c2e71ae9d84a083c3371bfd491497d80e6a682055b7840e0f61df614dd1e22e3a87432162888e7d15b9f64c5a3820584f9de310cd14a6dc3002c6fc5e2c80a19d360ca95b384ae33ee8f8dcaf29bf0e2e7a78d03a137f4632eab694b217cc3bfc5a1a0ec1baf4ce03f573b0b4d153936ab82730990c0aebf8e429444c26c7dd8038eb16e465d7d51545a9e6e7330cc4e9e9c720529e3da56bb8b077c013bfad5b86dbd52401fde7d9d9e1c3798652c6a1ee0c00ccc74087e3a9fed0cb78b2b6532c645658459239e2d410c4383f672f2977d3f09f63c608b3d76ef20f97c4b57edcef1b1ad6e982625a67907c214dd4d6151ad02d414b1c4abb3f442c1d91d42b83dbc5d64f55199f675d613e6c6cdd7644c80050551eae8b0d36d8809c1f4357ba3a4486c4f881b7cd8506bb0a8e3a86b57ca0a1c85fbf97e652972503ddbc33a8bade7e0859d9ae7fe1d54fc6d211db533050ef9f94c06737eeaf8399ae4d5f7a90ec6b1c5d87c6938a982d0951c90df7b2300c4222e8af0b157f89e5889fadfacb299f5dc5fa8ceedb5f392090633c8059a0a9c9e55da44295482fcd02412755951bc77f979c768344f436abae112227394067066199773c7ba88fb6fd73b7740358bb3efcd93c279edf9972f6067c8c1940074f4e5195b3deaab16528994bd9965148c39196ce2753cebba6bd464c4c8fd06b394b48323b1e68635e0faa4280cccf9f15571d96552c29cc5a6ab34516c030006a90c34de9ef01deb2a692da67e3c52bcc2463e9f5636bb38eb32812ca7ff038b211269e6eee27c1f64056defccfb589186580a0e02dab10c0a68fb86edf00e6207072eedbfeff52c58e90c62cc38bf33189703960d60d6fecc9e8891ca85028d464a4efdcb52f5ec9898b77c2adb62c0fb434505084ca0410e2c804cae3c00e69afc48bbae84f62969737ff395ca03b3b31bc2026ceddcf7bd92a3e67faa07ba8c65b2bb2258137f223e21ec362f95d39ee0c6acafec66362f0bc672fb280a6b883a662361413609978b11be0c3064c71f325988143042d9b807b47f268d076d5d4e807a5d0a6984df81737c479fcbac6f6dff715b2c5d6a343e495ff2b80f1719a721a9b8826939f8c4aa7b4b83f8fd6378f07407aa082aca25a64431f500e9852079d1380ea05fc47ca6b665339cb9256e3348ab0b78b7a74f0ca2c2250826497d92cb02405fd9dc99e4e1fa604aa57e11fbfe1f2aee3c58d2c0f141ab00b141c9cc659d693b4a764a298da0e80f797b88a8c6d0922a10ac650abd6ead08a3beb5df09ea0a2ffcfc03e02638ecc1014a31a1f26e763abe94a153d2f20507e5b3674585dab8a95a85351f6fa5466127c37ac57707f9d0587fd2ea7e6b8100d019d24274a0140619acd9c0ba0a20ef09702c4d873197d4db29288a586a9705948a71d9a4dc22d209f6481aa913f1a519ec0d881ac3c1ea07f5ae2b88691a0bd43618e093ca31c99ee842a2218a1effc76d826c42c11cc04d301983c4e3a10e21b101867a044da01ab1654670ef513f55ae830124058d814ecd359aa175ba06854920cf7e3b430f064f9920ac09f5c6167002aa65eae2e0af13985fb3329c0809719fdd73947bdf84ff5e935cbeaed2b6b931e7d477b314756e3d18b6facd0425a6a7a1fc21261b51854cf6d159cf767d9e233f4a1e2018fdac49470cd18d0f36448ffee53314787c44607ba4b71128b9279f43eef41d614b2615fad9b8f70538d67496496365b1bd714da324212dd9c4c2e60ea34ba1579ea54f9620d8160a725c27bf2fe60bacb3130284ec9e3df41243be21240bcfc82d3e8cac43db8606d85b8c7309c7fd24e6fa047d1f5c40070c1928adcf55171a9fe5a95d46cebc0b9a05af9bd49c2d28ac5da32765b3e2e64f222f680fda5fbce47e3eac66cc510444375cf3823302b3cd5c97406896ca931d78b9696fa88a926ffab60ec9aeb80a42fbd6f741568cd1e2cfcecced70d8d2c93d1e4009003dfa49af4e844216f104f1335b65757bb822548525fa68fceab34ab40bbd5f925c1f94b53c32c17ea6097fb89cf9e7bd36fa3977797ba380843b08d479fa47b5857c152bd22e7977b80e65906fd18a5268546e081e85d6befdaaf3c4bdab8d03429c81f4bfaef06f110123804f7710fef85cb19800a5b5c381cb85fe81ce4fde5bf71747de61943f1a00b9befdacc4ede14e0f7836813830cbf9ed38a90cc7fbf21864991b85bfd12e0bb241875594de4aba4e9d6d0a1b36d5b139746d34e885e7f8e3bbf6678fb411074634b7b7557b66ae7571ac9cbc56951df39140b3ea7d64f9f9a89d4e0a4bcb0562f9eef689e1bf86c10868e952e06ebf619ae25dd39f65555aaf244c1481ab0dd995640e281ef327cb9532b2099c61856ced7d73904e0972efd9bae3c37b8f0c3a6747c3c8968622bbf2b2aba8b3ba85ba62c526a28e736143d61eaee37c95038cd1b43fff3ad42aaba089896678c1e29844cfd7a164418b7d34d7e540f1930630d68e1b62a2eed14924c1860dbfb0320848abaaa5badeb2ad70782428dc0f0362370be553d27ec964c927e75bd309e6870c7815b08ab4d3511b592eabf561080c2249ffbf238bf7ebdf73e301e9b3142ab71c42e374d9b51ac24fdc4bde9804a2b87a298e5384b8c73db89349f3b34ec0df8f2ac5b18bd83f10809eecf067063deb995376b3b92b51e9a4fbff1bf671afe97016895ca72bebd874688bc19009d8a14e268f80dfba9edfcf4f2e17a1dbd2233b79ddfc3d5c9388e10a830e1a0984c8c9e6487ec31c17c2403648e50cfef67b1d47825540d72f37da28f709cc012937fef6e2d5ce5905fdddff04bc275f8ec83494e51393d78144e1628512000a6dea0714254e3d68488a25143010ba0e1f5c42828e6c014158af4d3498a34d03bb5f7ff5326ddecfc3fdb8403e0462339d679a1904802626724b9be3a747c306a2c4057c30dc64937f75f3991d22688c919f4f6b993b4c40adffc49827de500b8564f3f020f9add5627657e9cda655a725c1a81c1b065750ed35983ab73f9e0f543ddfc88c0c9587e47a8352d50daf3bf177d7b8258fb02c17097efde166a00978b627b8442ffff6478ad6fda3820dcdf8066edeb1223d7d76ad8bd32c48f40be9344499ef950180ebb5b19ff218c0b322f7ba009179625076f64fe087067f090a5b33c004add071f32e7f253086387632d822faebaff2d353b05e6310a5f508d14c5634d649c161938cdb4b55395e790217918fa145171ae53de2e225433709 -generate_ring_signature 7864676e28d3c4970b0debb0e6f97ea408580a98d226dafee3dd81f5a3487cc4 40d7c9c8b56934e5a5d9a07621a15fb22374327fec4fe966bb846f06dfb62cc6 1 494838f14fcf86139012741ad7732de1d344dec1773830005b3602c49e363c25 39dff26442e1ba3eda6724c73174a46d8fed8eca7523ced7781fe134b2f4400d 0 b07021b3ad94950c2b00abb941b78f962476e6060c5c517fd6d459ee96b376092bb36bfcb036f60c82a9edf627bd65efd735d65826b568966a6224d98754c509 -generate_ring_signature 2e56246c092a45724cacd88af27591f7b36558138dc9f84551e343f3e5f9795d 79b775c12d319addb422da0e67ba0569d1d04cfd608980caecf219b65feb465d 1 fddbe3aec2ab6a7c85a14e5a631db26aeb656f75311c48450ee02dfa54a7a16f 2de3c5a26614cbfa5cebdcf016c9a87afb79822485174be1ae85544aa2a35003 0 16386f62533726a98fa5f36df670b8dceee04f824c35f6e15a0dbb6e3e4bdf0218412c5407c4fdfd442a16f03b4d85641d0d448cab0f9b1da3e27b121d4b0902 -generate_ring_signature 7815f1d4b85b2fa3d55770abd44e7d7111f47c5571935d9e5abaee6bee223095 bf1e67b2b20d957c19b829881f3654396a78adbcb44e985f7aafc4f5efe39231 61 d3399fe1944fc161ed48c41e5199c0d1543e1983de4cfd5d2c410f45311b9f00 e2166c3a7f15b793bd2403656e289d1689feb65f8e6fa9b7d46a34005b261107 b800cca2346900c2e4a7fffce98677cfc8847b83a5c69915a3ec51862f302555 8f8b9048a0d2f4d0e0de1d8aaf9760766ab3b9e7cc5fa3254ce88f951029b04f dd638dac03e070da9c635e93ae6250be11a35cfe9bcfdbe7df3fc5041b11e63e dd85beb9d51168206681cb9e2d07315dc15c19594df7e2119f073566d80d8d97 31e92e147b4549910056a05d60cb44d95c258ec13b82e57d1348265626c938d1 f6d071a1f002aa6b543a8f3c5749255d8c9e659e1ed46e4800976e953b82bb1f a6b081eb3489da635b97f6cb6878848c085a60d8b567f08b001ae48ee1591649 372ce8f0c084f59732aa7312dd360760dd3019a073c8cae656dfe2fe499004d3 01c7206287219eb32f90879dee2d05f3ea36557f828e4b91ba14d30c6c36273f e8220e44b09d67dd1ff189de878989324d964281b678e238dd5ef329332c1b19 226dc13531650b0de4c419171f4265fb88bf9b645396c39835a627424d3162ec 1eb7fdedfa46a4eb222797815aebb70e1f3c0a1bea9bfa3a5bf999d2c2588684 53f0fe809deee1f45adade3acf4b4514a1d437639c98cc9562951b7e95d28e3a aea6a6fc2cb0b865ebeb9047309270eaa1442c10796f8b2fced7458cfcd0e57e 214b0b9af85917ceac889de8f197bb26c5226a60aae4dcb812395e9830b7b185 59ab7c5277761232bb784b937b97290407dc2e22e0712270c18561322dd25297 5da1f62493bde6c3f398172047c7de1217bd463249a3279193df9b6068d2af55 dad11f986df301dfac98557bdcf96e36efb105e458cc3157494319b9eeb90cbc 6ed23b5f3108a39439aed73fc45b313eff7b198585a0de020ebc72543d4c735c 90936fc627704467602d21b46ac283168dc6a8081d0ba0330e0e08f7d8e9a255 0fd25a20ea3a833592279b5342ba758ed433a3997677e84f36a96bb0ed48c985 48b4d1123003cd14d0a9e7c323d1220824f536deec12a4b887a31a35fef9707d ea8c6a93e31262c5ba1be8c81129da5c5b38f4b60290a43bea72ba8a64bc498b 17b8f47c4b2ad10e21427ce62990cba355a82827add46abd01b0a9c6c2bfdea0 de19153bf9d58038d4f0c6cb613e037b2572c641f4bd71b1447aa4c93ac75438 26b47b7e2b2996a659afd62b05add0068672d724a4dc18a33ccb083bfe30d760 fc8adaf826a43673e332af17c4dcc381d69c5f05f82241202f799ab0c75bb58a c25fb0b991261f8b531bc5ad19e62b1b8055db902c2b1a435823c3e9856ed4e0 c46d960e1541d03db8b945297fb8cfb6febf632a151515ab0579595ab9977695 0411911de3280f44aaee23820e05effb2b3947eaba456a246a29d3c41814f5c7 1531a1532463934f0bc7ef7b30eb947a65f7b6770c55689506176dbf387553f4 f620c8e1ca6b327fd3646bf64dd2c94e9acfa3b4d52a008ccc2d0ad6db5ef896 82a462271505cb720f9f9234ac367fa6608edb83d33c1de3d92bbb3afba19b16 d443003d8fca22d127eb6acf6b26c0e8c4feaee60bbd276e19cdebfa2fe20acf 2b3faa1881c5ddd2d4f4b2d8a15872e4c74c1c6d3bbd54c5d1c2e334857369b6 8fec354ba1f19ded18abd2a28812c09019bad7f4ad07a12fe4c3a0f7e5be1521 ccd228e8336c7edbf92a2faf42c221b890a6c2f5d48ce59cb6858551f93429b5 c36dd157fad2ee13582891fb642397b4ab159de4b55fdc5fdaa6a01dd6763249 6d750ef9905133a6073dd0043fdcd7ef1f0d0e7b68aab2e79321b5f904a9da19 800055a80889361483f078c8be0f37c455919ee300b6fab6e637d31e00433b12 0ad3cb33206d1f80995b7ba6518a7626739c1b4057d5169db2f39e78de3e23a5 8e077ff44226cbee60be2f4d75befbcc46e691cfb60c664c1209e1bbb031f868 1d4fbec878de15c747915bc2942331ae6353e2ea2b1a562fc93b264730dd6c24 5681010b12e8f47019e0308f2952ffbe4daec078a63c6e8452bb95cd2ef6ef09 53f5417119d15c1df61673eb903a9509011c0ed83fb8a6863f8d972bd0f1c6c0 a5479f031a8eb4eae48c0881a6fee4fb3a3cc4e2a2d372594d5ff3277044fb02 c07652126a9845578a02a1512c77ac191d20a8318f1f1756d59d14e9f32f2830 8c8bafe3ce09e7721da3d52ee6d63013650d18c8482e57d501c3fd7ee5b0e9e4 f865858cdbb9fb50898b084136dae15caf9574aac8b712679d4318e9edf7b04d e318fb363e54aef5e7ddc0aa2340c2afdeab63e262688c95dd6efbc7f5a2aa1f 4b01b80161b7bcab306ccff59258d11e994258e9163bf55cc8e652416d817be8 5975d6d8d944547346f31768e953db73941c6bc1a189836012f77b200ed8bdfb f9e4bf1d84793e3702091f8ae0d833c7480e474666b169a0884cb84d6e5f55a1 3b161766200c121ce33a1ace049b0b65e29fdd307e1dcde2afa95a4dcac3750b 9e7861e59e8d3dc063aea6f094eefa2914851e0a96f12332a4dfed98f81e1f7b 363cc8d62506cd8dde52bdd490a3d3de6c3314303166ca2a8baf8e753fb3db0d a1e2e309a4dbecf825cc4dc6cb4a6675c1c4c66bc77cf63c5e19e9554de3e70f 282113e165a5ef3f6327066b407b464d2f23abdcc0a6366bfdbd10b1f65ecd07 7522f8708006f4930053063d182a25bfbeac061d4a499d1aa7687f910a2b6274 879fcaecfc2dd21a62fb8478768194e0efe3a920592237ecfad527bdd042880b 3 3f631726928147ed30c75af4bf2c5864eb04b4b4772738073367c36f6ffaa60111cbc61cd34915a4fb8a406e0fe4611ee28c99951ac18fb722e81602d3f74904326d17759dbb8dae65c3647044ccbf5f79477220b76b2cc219932fda9cd5340ca980505cf009be3c9a496d68f93ca3f52c99d49b3dfaaf71bdbf71d093efc20018ba9daf425c152a6cf945b30751ec9a964e179d67b01db5d40a1ec477de6001c19a0a336fc28a1cc77a433694bb1c543ff015bcc6d180c0ddc5d8b2723c1d034d6ef3d4a0473f869484280883e2e0a8aa210186cc4b687b28b4970150023f05206e35ffb427891d5284f4614312b36071f0d1e805a8b54ff6c500ef99f73d0e2d87d138dec9ca8f1307b40253a7ca159828782e07a06a8cf9e15c09c5365e0502c104a4acef1df809f439d3d9ec7f4119132963b2e66e7dac6529102b84e60ea05e061f51e3e358b723810a962ad21e7e3c68b826321c18a676e3c8776d4e0ab854c34a34b2dba312a0b8a662126021825672fd6a8ec3b6ce1b2bdda2aacd0054ef2a648c52aa2c8c0e97284e8d43f6e51f5aef1c402ee14f7b34f5db7eb50b1fd9bec7beafcf5a6f236b62617ed41df044a59094961c4673a5026a6822bd0d6af74dd471e2d4a858ab15b8c6cee50e7edf2c36f8a5af990747c01807f8860c64d48093d3a8d6af04a92b881b6f2dcc017507a44b9986d27d9e0739c4478c045e3980693515dc3397a90628e1c1455ff74ec892121e650ba2ce6f64e2a461047abafcbfbacfe6ce332a684e9ab44ea75b117a109b1adcf202591793f2907408273ae83e5a3634715e13bbd0271ec1259827b7d3b8052e989a422ca7eda91c06f2b67d7d1893ac1180198a00b94265ce7ec5aaf3ffa3a9c4dcc013d256ed5606dee7686092feafb1a2628804b1fd5a840eb5a6f078c9d301862cbd1f5bdc3b02e5e6cba8797f50b49f573747bad9390d5e547c7f3670b05148461dffb513ec0b9d1f6e77320b11c883c4d2624f253f99c2793497659c5020c9c498e6a653e60b38aa00820f9b7bae02a3ff08ab17f89b48a3b59f2ced6ee22dfa478a3dea160c79836ea350fafd9dc40747d2769568aa93560e2f7e681e1ef0758640756d770405de9f99a838fcba6afe49b4b4a2d651f968318112c42bbfb26fd227a6a22e0fc5285ba21d30bdeb3d4da89888de0a0d648b249b54e8898341f6775e74ac3706690e44210447497794b2a69f58c6ad199829b4419d96ee56312c9e5ed3972f0e83d70c7e4a6bdba630c154cfd8b10a9800896c278735172c20c86e9d8f48a0070a7f29259a84268fa2ba607d8aa0de19d767599db6fb03ab4c0d7dfd2df97b0c98870dfedc4b69aba8d88cc436f84788fb2dc300783508cae34c999846553d0bff9c1a02ecc087cf45c2fd5d4c99045858cae3aae821216298da845265fb66061fe1b852627cd0d16463b320c6abcf36577fabbd0da0695edd51926ec7a21b0910cb5ac673662aa3a31d10f2f1582037d28332b30e0ae9c0bc39ab57a0bfdd0d8379de0a0edf65a9fbfa4cb6a4e707447603cc1087f6e7313a24f498cd61a6089bbf6cadfad44c1062405c52bb8139280b51582b5660940f6ce9e70cce5d21027b0493e6f2645bd327a40b367243765c72fb1e9ee190628e14f7022c5d878e0a52442d0c380e43b550d713d68da1fb9d598f7a537ff8cbb9813bd2a41ad0cc0b1361a9f29a3a7589436e71983b8a3d23ae2f85f0476cd0248a946e0bef9f870a16c0c0620e30882281d8fc97551c8083657f7a979f21654fb73ffcfc62fa9a04b39206b8da73342d892360798344e321a60652adb7194690b9d7b3093f538d014a7767c5baeb5959e5fd81acec9973749547309c674086d1de9405d34c4f56049497667487ccbd19834293563b66d934db21d8ae5203139aca821207844f660ba8ce39b383c2ed9e7f62d98472268feab6427bc9bfa3543437b9fe14cd73680e6eb165d92672fd238a7cf1d64567d148e6c6fd100984cb4af69e05e5d9aa8e028d72ecd27ffc1fcbf35ef5675ae890c0788665c554e607307e0ee7b1c27ead00bb782fc919cf21e09d6ed17107384a602a26bf817bb4c9541cfac1b907173e09bcebbf86af32a79d2908224ddef1f43b3d4e2427942a9184bebd2904ecbfb00ab020d696bb077d174eccde7df10a6bd3784b78f1061e0af5f557a76858160b051f409fe1317ab73ee9febf68fe787b1227ed5ee50edb259397a1cf2188e4330981db66828fd7ebe5b42713ed7ce4780d488ebe7151e2e0061b46f94e532f4307a59e5470885cca0e8895d54a7182daf327e9cb3eba38896ba1503dccdb44c7094df1651e06370a2ba30c99a2fb6578b84fd36345e0d52188a3f1d88cc030be0792f17a6f68da8bb2bd0e7c86285dc359299e8f01dd0cef6926d847555a06db08c27e9e16ebeca374cbcd8dfc871a09f62bbaf292fca50c957004df6a04ccdc0bf661311325ab198825bb380840bd2e60ef5fb38ae030a077204aff3c7f52b80377fa4c8210e3a4a2f8b8a5d4e1472106733a6a48f0fb72f6e942ef30626fe90e0c15b3c9dd2ddad58b745c6b45e8bfeb479293df5a5a810f4545b80e118665064fb6d574db8c99f9c599c38629d38c45aae348d3d59a423935e7f105846759024af878e313ba20b23eb3115ef2dd9cea547bce638db4f1aa3772e5823221920700e76b84986685272ebb81fb7914365cae882dd0c99a9b872d718f76fec24f0f6bc85ee6b9c8377f94feffc047aa07741f748f09c2939d732b3dae046e0a68060d51e2f82ae09893cb904dd61610aca06b5e5f4abbaf2e626fc54bace3a9e4059113e6bb992dd30d58af07a8ef042b500f4fb6cc1cd8e55a833e761d083a3b0b07cc23c17ff527ed768cac05c498a1bf34d3ef9a6cb781127b326a9465f2cd04e2e6091f29b3dac0c422dd23ca54d1b992e844204d5394452bca2dd2f47c1e09592cd6c516b7f927a1ff09e873556ee8ae2de87efd8c5a2684ca42f8c980a9006e26d71717c42c2392ef59b60d912a766041415df373c535104dd402d290bb021fb9504471832ed06aedd6674638017bb5d714e88bfb6b5b473efd87c24dc50a93ec6626e695286de966932c299f20af54504dc1b3d7bf93ff0bd258a03038058c926928760a30db939cf2dd7cba2d8e3ea165b4ab9bc3de22e4f1bcc48d5205efc78394d64c654f345be89c9cf4dde2a0185833058d2136d0a5738fc380cf0fa623cc9ac6a3308ee2748b6503c26bf4176b08a3793673c78b7d09b86c100d06ee5ba85af63e8dfdda1ab9deea0e6b98a63dac2efa4baf37f61dfa5a12bd8e029c24505f8f7dc577842024f8acd427fc12ae0f4f091826ffc0dfdb0e3942b40fb15fa642ca4cd11669a5a39c90b3ae93524019e380f7df2cd89b62b5bbc61f0d11ef3299ded766472e37f8346ad29945ef635566dd663742279d950effc81d023fd458c5dbce160b8ce2f9c6e3ea270322938b1414a179a65caa8fc94d58bd060fea849a2819331d73bc4592142c17999e5353c2897b402340a7dc653491240693ac39ead4012645fa7c46ef91d53ebb7c17ffaed6c325f266a95e541737660df88f8838eb6f055c838d2990c3dd55eba13fe08f1d0f6c3dbe28f4ed74e67f08e4d79bd76def62b94cfdc06788c8ccdb796b3bf02706724e81b6f24f528f030c31a927a5b35477c67eb5eadc06bb822b018efbad736616246c4b395a9bb89a00d57f42bddc9f8ed6d258a99d3a8ffcf6b71e6e00db6c357734a51d0e2adeb30a317a039d03c1f31086f1eefbfa811261638c5388f86bf5ef7911006287b92d0208f6d4ca4c678e5ade8907b7f0ee13615d306bf43c16a04b38ed6bd5d4bfc40916f42dedf5d9005d5fee45f9208b4b0596154561347f909598723ad91930750b0274ace6998a031bbb1c124ebb481dbf6afc93b8871f8b76a15ce4a876a33d0fe5d182c78c599211af0ffe283074a8011a5531ffb4710628afcbf0b79da9090d27b817f12b1afd9d84400bce134e06ef872554e29c0fd4174fe6ddcf0dd0520e7da6e491846303b78cfca2ee55ebc7994b1960d8c82293b7efd053af4930800870c5cc62d7e2228f6a6bf10df0aa2b87eb842296ff9854d48040a9e7046c350fc9b60f435c30635941e9140913fd2c1b71cededf2fd42fac3c152ccf87d8010a4e3836c427b16e2efddb78efb203ef3074a5c0d55640bcb6c1d925f225204207af9d8725bede99d3f7d8b2b17665718c50bdb49922e56132d7f20ee3b878230a8427acc57e2fe7d52f87d9e82391625bab2bef706cd62b95cfad1d9320f15905a207ff240a7b8098016fe8cdd1f88bbd8ce2f26ffa73754678e89ac17322270050fcfb359fa88971c7777b8190486908fb0973bd521aa6491e110eb9f173c90d23f2c7fe33daaf3e2c561fddc68dcffed8a1d6dff66c2d5f27243ffbf2aaeb0a6838bd775348c53a2ea251fa4c69d3c998aa1f18853bf30eab667cbe91d94200f5822ac7c1c00966763b45c9f5939cf1f9796417c299f76d67e14a399542240e058dd45e96e8aa5c8504fb567e3e30f7b88183bd42198df8528670e338a1b70ace2485524cdf704a80c73026f8c1a205c90fdcce66c073dc6d7ed0487f27b70ea3c3154d127ab3ebdd84623cecafef0b63d27692bcebdae98abfaf342c8a3005c51ee09d0246502096d2a57994071ae266bcd091e766fec6b84193316e3e7e040ef4b2628f8cfbb7a3f96ee76c2adb53b0a0d024649e6c0493bb0d3b2ca9110902bf99ecd39cb6b0301975e8e66b6f704d1c0b2cb179614d98d2a10425853b04837ffac3267bc9555099566b7ea1553c9da7ee1d20a8d055b490d9521813440c6e089bd8bc4a7494ab74d9cd435f4691deeb7f653e18036cee654bca38df160e61d6f37252c317ad6ec1c3056afa60f7c98709df765224b271342916fd5f2f0dd020e56c68e20a0ef3e16597481b692faa22c8a0954698d89c5b6b624853fa054dfaf7564bbebd9eb56f7487314786febcd47ca77f6aba31fac285be00c1cb0949ee6b5965ef7b020ef8ab2f4c8c632b64fef41d3505e92e8c58609db0d88e092ac172befa54262d9696defd21e761da873f7208d656748c8418324db0fa8000764cb1bfe49d1e225ebbfbdcba65fe6f2dfaba654a8d36b0b0c30280b97fdc0e412ebcddf566bb785573d44e2aa58c5e6a9628b65ebf2424f8027ef62794fc07b6d11ac0c261dfe6163084448412eefa21a0192f933919618abc9b1b0c14ac0538fc99939060d17646ba8943c78253140adb7231eef26346b182eb6ca395e20fbfb68b2d262752a6b065b312e76b7db6c0b9b5e13eecbd0aa5fa3ad51c57f808c11e4847a74a4d058f69881c8919c3c916a39219b7d032139f5a4140693d22091cc2eefc1b7733ab74352d45cbbc891b2e85fbcedd2a13db08badbe20e762207b0165d6d61f222c1ca7bcbebe010f9a1070ffca89bf61f77a916563044cd1d02 -generate_ring_signature 4c4e0ad8f2598b7d77a053302d5a6795c381364241b30287731d6c7d063e5253 765d90a45d3bedd3c6ee22b5783e15ae9d418c64dabe2683135027a4e0f0d47f 1 f0aa7e8a8283be703ec37480d9a1e445ce42c58dad7d275752bd8ffaae075f9b ba659efc83361288e75dace789db89084f3970e73518e0e8d03f939eabb5fc0c 0 7059e63d3f212eafab3b404bf15149aa27b59868331ca891d2e6bcdb53d92a07a0900eebde58eb56dc6d314a4a30001391d789a25d5f2cedffc2ba082dbb2505 -generate_ring_signature fa75d79801a1ec122a36862796c333cf133a930d25ba2faa3cfd8401b1ce65ec 64c40ebcf87adc0ce335ff113ed46236769df40a1bfa9dad5ef83d8babae3ffe 1 fa560cabefb92c708a2018c978743b79a348ec2c3908c74c44f619d4a437c2e5 c6f86b60576c5b4f0f03bc9fa8a52482cbdb490b0eb9d99b79e319609fd10c02 0 bf3d4cea305878a216195cf4e757a054a60076aeba7766e2d28964f12390540e6ac9af1e64d31f80a26ddf856af4a8b0a390624c82dc46e27b4098df7cb1950e -generate_ring_signature 7c3fb9bb3ee4c42954cafd7b2b7e8cdc0bc46262e1c01911002435bdbbeaa392 bda43de0bab94f0f16ba11829801217e25b467af70657b746544a48667d928a7 196 3d7deb9d2aeb577cf0188c2f8e3226415e5c4f641e83da62745d8f20120a2c46 4f74fe1cb3af7a94a70f3c562eb98142765e1dfef4baf9f38bd368b922515c10 ba66623e0a2b563d871badd3c5dc6a1492d92839cd3aa5d2c0eb5f70c5f79581 71b8b0d917a73fe39ee5f2f3113a90b9b26abd07f56239ea5b326544359b19d3 eefaa73d07640c1019bd2f642cd443e98832f82d7890c49a2ea7d01abeb71455 f9d84b3c3389860cfd7499393b13ddb2d1d47ec3c782fccaebdf892d7341fa19 1b0bdd06f4dab389ea44e9d7d1005d501af618687d00940c2443b84e40e72136 3e1f0672ef333d52cab1cccdc601f87d010c82611598eea47c8b445823f5d489 a8aaf0ef9003a56517acab915650d7dbbdd51e702c2ee3d84f3e62fe0d8c86a8 eedd4498a0d2c6810091b0dcba02ab3e34e8dd4a4efcf2dff9ab5d91056181a9 d020f2842103fb4d38bc130cd14439b4a4957fcc666dfb3f8e9cea602d0648ba b2dd26957db1ee390466b7ff7b84cb95d8989a0aeaa0ddce683b254390d6a6cc 4b72466a6f2eb480af8715b9a37cde092b5beaa597f4e6e00746ad6d2d05b398 f921877891f56881d08de5ac9c2981c54e6c1d0ae2b16c7cd1a677b198864ccc 41f35918409892706539e055bb30aa7f2fa282f432e13fe769af5b0f636cd69b a2b4d259f5bcc195075eca1ff07997b0377cf25d771716bd4560238839867e8e bb4fc78175626e852e68fef83df8968ca5f5c4f81e1e6112ed6451b9d5f5bd7c 4edfcc81529f6e8888764bc5e41a1fede7049dd960f5a5d197f229a5bd1e707d 58e5461abca133964190a634685bd0e149628bb202ca0d5445c7a852765ae82d 0fed57463cca6186faf243735eb3ee19296afe91474c492f5489f8ca8a9028c3 7274df448dc3ec321473fd02de58ae70c0e27cb95f0720826d7853fdbdee48a9 add935fdef42a91396a34e19659a13fff9ee76b437d4084594e7575a3de21b4e 993ebeda5d18e86f53235876c9af7e6d10f3fb65068da9bf3afbb1abb464f52f 1099ba8fd7f7cc772abe480fa5f216bfe48f46e6340736f452f5e59b10182349 1c5a734cab5a13536221c7f83bebccdcf2660425b358a30a13d49b8a2c832cf3 c0d64b092dc9db3810f59e3fb8feec10188f38d31e227adf80496f775becd04f 755a97fd034508b8bc2702ba1250f5d71ce48e86c7e351e86790d2ef1ff825d9 e47db52ba46c257338824e3adf22706fa2445fe44805d4bb94b5e0ad8c47df7d 6b2ac3928ca6dc8ef22afaa034f66fff380bafa2674a4cf490960fc8b560dd9f 9c1aeea7d25156d1a58b0252704b71b3db0228f17c4cf95ea71b2b076a8b210d 74215b03d16e45d3b251a4c6367b54f8ae438f6965167fe5ec3bb645a5885bc2 013aea76e7a4f7c15338d048a54626c8fed0bd291661963e1f3567923b1eb93f 97c048ef8421b828ac7c75253c51ccd77e427a6ca6390d554abe773a947e24f1 a28841ac91711236b0e4e6f12fd88bbfdc09e269a6e26d7aaed06c3702efcfa0 e0b8ac3c9851b8aeba1edf45b2fcacc18d7834757c4df91b1e14b102b215982f cfa5948f6bb9b6e5678f5fe5c45d266c7c9fee864289ca3dfa79ce248f0c0b79 db95bdfd0f2429c78c806dd772fb934c3acad4fb5f7393e25b521afeb6a35c38 6200b0a613b936303102722aabeb1212b78f6af51735a3f0d96bd76058ce0267 79e29d799a6afaf916f0b5ce30feeb0eae2c9952c1ae4d7708a24067169b4ad5 20ff221440a6bb518535f42abdb4d5df26b3297a92b74eabe8d660510d103919 5fd147c22101ad4fbe7fbcd32931d55b13c55cbad3db68a1ab3f6996ea25d03e 01f4dac1ecd9e01bf1320587ec8674b88d34205ca51ba8483ecdd27b07f7ce02 73227647ce4ebde477e0cd99631d977fa049e3f0d016df930d38794d45ce7c32 829d500cb72ccce0680b7445877d1b1ad89f9d643f7429aa103a655670501f43 3f3f0ada3115f1dcf6079c0867c0c9e3c9ecd63c5d02920b12581cd67bf4251c 9c0a5f030055125abf6ef7631709f06c386af32ba9bc659647f6f6b349588245 56ef91cc69961d6b417ac27d82ab9a7c1b56fe8ed7935e6802487153eb8bba15 6943def5c2ada8d0d78fd8da01a138c16737992bae209444f4b02d5b96f3ac2c 3517f0a7f5203714e1b2da2723af527b99acddc6b48733b6cf23d0cfb6a4afdf fbec53b91419e5f4c1e9ea47c499808c27055492101ce15c85592dfc9f87fbbb ab7a6bc753aad0d5485f332d260f351b75115bcdeb32d1b0a1fd0d8421ee9ebb 6d24d7851f0cf26a3ee3d3c29f8e69f4546c90d9cbd3402fae623e07d69b022d 1ec91aa4a3898b66a1ca297e32940ef75bf5695d62408b11e46b831b25617f09 78b9628ae4e16b74d36ededb48245ffc70fb823ea0e2b7effffc34d4dfb8d308 e9980856b66d6e29bacc3272d8d94ebd638117ccbddce2f8357dd45ca32934c4 3263b10bce76a36c53c3ff5cb9d439c549c7de8d9e3e28cc29ed094ea304ba73 7d15f57f8ba68ad9c179b94a752888ca27480d439b13bf7b22e0f09c30890c29 76806ce6f9f66d86d585658d049c90b7a55d55035e7b9f426f2afa2c295fb6d3 da9630706b7b980a5e17e988ae2ff5dcdd0928d3baa3b736ff8fbcbf5e6acb4b f8da823972eefbcc84a68d34ec0cb69538491d96d8b9a1ad25ccd89bca2eb227 2649b2831326133828b22a7ae2dd1c2d794bc80fc92fb219bd038e20b4087aa1 41dbeab376c0cb3badd9ef47886d8e91a16bd176babd5c93d4222eee59583951 507d4bf45b583eecd177a2d7de1d303e908a9e7b326ee66c384074a19d0e8986 c4327912595750e1b4c774dab82ff6251392fd3f2901ed495552ab943896afb5 de7c4677a46a08361d3748d31c1df86d6a677a46b2fc52fb18cc8c17e631f9d8 bd154b97db57d73e1489e4aece2e891cc90689461a32ece0adc0252f2ea8cd0d f85947ac2d27b169e3f8668cb5d309c93474e2b4e39d303ef00cd22bfb516488 ce68cca1c16786b5f84d2d1b6be185c50bb7e06b6587fd12e88aa0ad55aef1c8 fbc677f0efe1e33b870182a6c970805dbd587f7341577ffaa2ee0c20e73e2b19 062ffbb81d55a0599d07c2e7cbafe69b85f1562731b7b6ff29aef4910c83f8e2 516bbc709109e873a712e6ee502612fd247531d24de032f46d104c6968887c99 147992743e4b8c1e638e6c84d6b426544cc49051f098366837217ec6839b2832 283508f305a819032db1b7d8e5ce5f6e9c7591fe555558d5e18c1d8308acc36f cf7ef848184c876862e4414cd556f787f880025486a4932ed1428ea22ea83e5b 688407f0f2c3c3e1d46c2481b4507245be9e897b6e6bf2f71ec42ff60cac5233 eb82880ae90623b8601b37b8f7f1cec0cf463aaa4c7041de35da6bb1e8dc3383 be18599f4111c5d903acb9dfb4c435768a3003f1ad1428472263292dfede4e14 036c74f81ef0d9812c60b77d4d996fa358d1059b806a17686115cb91fcb891a7 56c13bdcb883d9ba092af5ffab17ace1bfbb6a020c562e12cb02c8ac4aa56d78 c0e519110a5cff1bd58e27124f7c2ada7c4e337bb8c6df93c71ae1e01c862bf9 58596e856040f9da1b9996da7ecc4eb82201b92d45c7fd70f7a365b95bb58b1f 58c0e7620d52079f74559b40e84bd5206ff697e8a96a236017d646daff386596 7d9971daa6d9b033d5fb18153c941f468874eed0db6c31597e7339d0a6abb824 1266519036a3cb221de554081a56e9b6ab0da26dca30dd1cda0a08b8f3ae2e53 7ba8112ca27bf3efe3fe5d6f839dd7c60c904013e0c299fca13447cdf4046a17 23ccdadf7fb569338fe0880f84c81c843d32fc8e51d53d0e6cc06288441c12f9 89ca3b7d4bcbc5d983a9210c8e25a5e9ba55ccb51399dc4f87bf766e87a803db 8e1df71b815b60d01596821f77f20e8ba714e13680e4bb57c3650110994106f5 56b73066872d8a4b86f780e8d9833dfa56419e278da45ba5e2db20bffeb66577 2a47099d3dcf25018cfce34ceafe9d87438ee4186c41490fd403e6e42b5c2d3f c7a3399f0fa1dbbd9976ecc6b4f939c860b4af7115a14bbda763632b1427ffd4 9e95ec90e3f86a6ba34930bbba2f93439766b2c1aa20d6b372f53aa785ac6503 77790eee24121080bd6cd771633b58fea9554710b771021f74d7438a99bd783a 53a887bb398735352c8b991478de995167951f2a985275f2c38102937bfbae4c 1bca9a852dc22ba46db7b0bb5793346a905a6e45bf2a7373dbf8960b5b42594e f03ce64633d857f3d0f4b267c6ed14e3a82664338b26159ff25dee6ca784ebc6 7d8524ed9d52118efce4fadef93e0a0bc66011bbc9877e66c9649ecb16aedcb3 cf65cab677b16ce2e76b2e675de6f2b324c6cc4ea82fd3d9ef646876ffc59185 d84478e2ff8128c73b3afcef77cc2f1d9f08a49ff3f0587a3a6d473f55e0eb53 f59c7d8fa9eb320ed90a86c92a02ebdc016390dca8c9722170dd5198cd1e9579 f0b0ede5391b6c9c584a61db76a0cec84cb1640472365fd278f6cd5da4946921 07cd121ae416d38443e3f9ec281774098c4779d712b0b7a85d31721c41a7044b 6f33a40ba6f6c8fe17c1f387b2a15a440afc0b262628c8c189ec936c7ccce868 93d3c1f4187815f7afb9bfb316594bbb7bd194dabec5fb906b83fc2153b4e02a c0514cde62fbd830f585c13491d6567b0e287b3b4a7ee7b7816b1e91a34637de 175954ea8c7a717813d62813e3af15ccd10935e95aebcc4ac60473f3d24de202 7f3f6c6efbc30d15995a2e1ad3d5baf1685f65463d19b98d14d7c22edcd33cee 8da94cd37f45d0993b6f37761dfdbe699fb00446e37766f1a8d2fbed8919aa26 ca869ef84d9196b289c84b0a0f90281d787a4505157eaa91f4f1f6b9e7443a5e 2eda08be6e1d26b9d6bad8360c5f30c2f47af50e07b647d69e0cb6991fa37898 d3ddc6aaa0883e6c155f2118c5e2501ef8d182593518889792e50b1403a04246 944b3af2047b28c5bf51427b1c29242cf2a2a3f6882c6fc7060086bb72ce26e3 44579a1b2fb42179b9e10e232c9d11e1cba5e6268e3e27e22fb292c75b550732 6713824a19a140834e3181023eeea4d9090d4c0ef7938ca5ac760798807149e3 d99e86bba4142a398a2dc7852aeaea64d710e086b241fba4fb70ffbbfe4ebab0 241c2ea4ecd5936b02fadb15c2d763aabad2e7531892a1448c5cb1b2e540a928 6cf74d18fb775f01ceb9222574d0d704f7efe797b096dae146c2d7f860d6ae60 2df994259a9e9279a8c08f7f37d1333cfeff7d4c9b00e273ba0bffe67ee91d58 e1f86f218491f4e1297187b600e17dcae2d71362b0aa051bd241981b4b694902 a85a6b762a221ae263dc0f8568b23388879e94454b9b588752e1a16105e4efeb 213b0ee105d7df0ca3e893dde9f50a57d0ff2e91031cacf2cdfc6547c07d189e 9c5879307b9d6957f504d1b2cf3160fc787f747122abdc423526640327ac59a1 ec4fd6c81d070ee743061c36568f2e02cd1e1b75caef5aff94a6143005a49994 2002f3a945a9d21bb9949e8747df9e40c1bca2d793eaf979ddae2c712ee6f85c 3f0eb01b2613ed363e129a4dc8d820fe325c25911d8a480a62159c7c53ec845a a8164cd59ffdbe57cb56c7c049b1e95f3693b0536831c840febbce5a723fc5da 154861f7eb6cc3253cffbfc3bf8e5dbd79171aff9244a205454df5a827bed731 224f573206dbc13fa4621c6b73b036a241ea25619c14628b0d9767743b7c967f 6cdf85e92acd2dfd4399aa1bd45d803ea3d0794c76945e2d80274f53cdca5f88 0d3b885c422d389a29a0928d6b09883f4a309bde6573b495c6f6a268eec2d519 428e5249c0abb96946314d6ae67a91339e8920d2d6df6bd2ab01618a81d353fa d699f8d209ef4bbfbde089705716e162ef404983be581dfe016e0c49a9302029 9eda3826a1477e2c37e40e687959331664ededc5fc4937d03e81da5c0b34ed68 f8fe870c71cb553166802db6027fca173c85ebf0f9537430371b2ed7c3a3764a 622bbdaf0a8dbc4430d89147f9092de7a2637d0c5ca77d1a2269081d85c3dba0 5c372dc15cf0c42e3f359fd4c1a5c2e1a019414a400e0ebd18394b96a40c98dd 91bd292ce4c3c68a5f2579f90b4a8b09b8030e31f7e773ee844c720b079d1e2d b2c171f58346747d6ed7b02fc65873f33b7e08ac8183144fa59ef23f2bfc959e 5cf6a61116fe8d0b9f4804e6fbc07af6ae15207b392c8783e404d5fd293c543e c6646612e88da493c7944943e2e7bc2ec432410e0f43bf7dbe63545a306975b5 ac6f8b9b72dad35d5cc636b93a3b4c3dc77b0e2ddbfe90978be8a66e98e3957d 83171e32bb5e628424811c615deecd0f89377cb0279186374c36d874b5ce2a2d 474871f34e82e8aae1f236fd7ece6b9bb8d9627d12878e64fba94ee0ff917e90 697ac8f1dcbf582bef5162d945b2dc2d1a1969bf59161c5aa4e6ecdddbcc6b75 d0ac304d9ab860d7ad0ca2ba9b3a2c2b47d937051992b547426502dd8cb74fb7 775252729d52b7fbcb724b63ef65d2c14607fcd3f57cd9277a10c95aefcf682b 8e3523b50d6ced51c5a82b2aa13f2d498b504ecbaf220d23665f5c55645faaa9 40460ae2f74bde032b31812d53a373111a7f3551e0360aee6a0a5468324b29bb 2a971e6b6aea6dd849df590900fc1f2f527ef624b7f578ce23a43b7da1766ad8 64228053b65371ae1d0a5b3823c3bdde6ba4e9ab15cf84525efc24ab7810d7f7 673629e04c5f248c138f987e346d6f62aa8b70142768ed8faf516280f1ea6b56 aceea696d75ecf12f55924214699453432418a6ed52b2954dfdf1d6eb66028d4 124b855dc5d1164885ea22ebea5849b513932aa42feea0df88bc146b26062102 2043c7629e30d8554739ea06709d746206c08cd7998b6fe7278022855ec96f8a 114a9e312df49f4650807d8d568da9c868508d970437d843c14f23a5b5c745e0 c6150dab7d30c4a57bbf9f74bddc9775d86d26bb9c51496b5fb0887faf32ad8a 24335733fea5102fb726536a5f8ba5cdf20ff37b4f7640b116dc72952bd617c1 bbe2bb771c7bd5fd1c79eb68e58bf4d49e0c45c00e274bd5876df1049242a729 a3ecdcdf9b4af0bef4676d65a25188742351af5648c9233a76e1c75a24eb7a8d fa75c3f4785cb295c8e4b7cf4f95e9cdd80cbc14a9dd67a1ca7d544afd7ffc17 b02c22ee4e0d7d797a6d7212759fc99de5594b4075454495f786aca12e919678 c81e4d5e4af72bb134385df0131616470a16e312f2e660610045f5d3031884ca de87536fde596ff29d65922de1d67d596238e5a03ac0d4e5251dafda619a838c ad6c7c271779001b676bc498d945e90fbf6ba0db589c5b2d5a2e4a74bd6e8d01 4aec1dbdb3e2c9c272606ee77ecb170e6e64b0c857ef9790d612aec230a8475b 82ff743e813f4ef077ebfd66ec61c32a27afb8978bcdbfa69c8b8988998acec3 5a0af108c17d6deead0149accb66158f8246d4939930b8683bab88c17d30d30b 0f46615ccbf367984b3a89a7648d557ac69848376fc56e77ee796dd124ee47a2 5bf94829a2e3afb01140433a9d57e8a59899db28f19e64aa46ff28f5ef64cec1 3e01bf0f93e546cad6eb526f1249c1899a784808ae940f4d15e0a896191d4e42 86e649034e59402ab71e9e7e577d427d917a4257d65a283f1d7ac023ae559fef 29a914d0f4951aa24e2f0aa63b1f537c2081b11da7799a70258d7651a35c5747 3f0107399325655f8b6b33023796e8e55d75525a31a9e5c256f24cb23fdcadb1 6b052938fcf90cea2e5caea0d9cd4e183990f1efba477314b8ed59faa89bd34a 1c6a32ac2c6d3cc6541da9afedd51357bd81172dea1eb2d03a1264a988f761dc 889927d51a0256466f031595d94b95fdf732708efd504b9d22c6ff54aa3d4c59 c612ac8c54634bb1d7cc614a177c03d4b0bcd024ba4a74a43827af518ee607fb 6bd3a9348b3e2cf91c5e9431dce292fc2c2322d07ca626767479639c654365cd 9deebf112490cc58fe510d7aa8c804657bec78642f236df79317165cf2f206f0 dced935b8c9df181c26a925613e96193d52dd051f2001e74f9b7e6348d9148f0 3f6277b2f63bfbe985ece514161452767ade8331f20c40f3f0d0a1035419d533 4d95e5dcefe000349998e1a3130b7a7357453129503423a0f02683aa941cc0c0 84bf7e3dfac85642c1004cbf84ca5f9efead8b8fb4ca98b779e9e5f5445fbcd3 c3aad5fc72c27162dd18f9fa38b371cfd98b70b885ce7e0c428527ca7d8b7569 c716e52b55b748463b4aa1af2f5e178536b0ffb6bb62807d0a51960ad5cff40d f1bcbedc26b50cc7de6f7ef24a1f98d58564336922df6e6f4a70337e6670c6bd 65323b25016172bd154dd7336dd6beba5ef6f45a8713797a6ee39d0979c746f3 4204c9aae42fb89fedb2270e6e29eea1df504c91a9a50f33bb3cabb506412d56 6113d4806a2e2f16880724d125ec7fffe7c1292ff38cb6c6eb8a8f5af171d781 0f022e7bae4d0615d72527758f20be97b6ad2a574830b1d6f6c754ec4b478a1b 25178ef51a568a7d23ce35b29ab02a618185fc258aabf3ddd01fde845938aa64 f06519e655fa5a6ed6cc80efaad8db30ea29324c7dc983cbfe0f3e931bd5b558 46b4b06ccdb3670d7027499daddb0bf9a1269378856cec1700cee91ecda64497 2399b6d1babe50a8c854a3bec66c0e7cdcc7959b03d8ab9f7963063e3ee7cd25 a7a4f6c54713d2f6305c2800a582092aaca32c34b20aa07d0a968cc1ba45481d ed82f9e92b53a19571b5366ae203f7dcf90f19fa2edc87335726fc22dc09d81e d9787436fda6cd8e5791574b3aaa0283ca7c4a5ede9b2433376ff45160b58a0b 82 c3046b44e97c901b3466cdc59c3166a15f398c66264ca3faa98a5ad51fabb100e57620f66965bbacb87c972f387b8b0b39c3baca36184e7852e871c65f5c6509bc7944b68fbb6cad83db7cc1ba72d5b2ee986db7623e03e85869b1dfb259ee0482a8d8a015c2515fe9679b40fb802a373a5fe4211f73dd62a4711101493fc904ed91e4003c52a474f8ae17d46852cb5031d8893342d0c1b1cae31fb6e81d490bfc53b218aa0f57b2295e497298e2a148bcf219a114193a0267904fe39d8b410529ed7398186ae46ffb616e886706107c48de85cb0d0064b5eac81270a07aa403ce8028665fc1207d43f80af777c2cfcb74793a81a3a3225ecbfcb42a44490602fcd34898f7dcb96f0ad809ef44bd860411ea1840454957ea3e00dd8647894f016b6fee01616103e7e5beb0e1054cc759b16cea10a27e1d6a6b652de032425e07f1eb718ffd53c0cc1cfcce1131a0f72f9b0f51fc8ea5c0aa40ccc8780fed3b0262dd65e03960648f0a0f94f7ce368f1fe1a43a696382e75b8d30b864e5c88f047430a1323b2f333c864aa37ae4957237bbcb6350def725e7b377ea5d2e10d6028fe66d7c08edd6ccc2d37ac70da1cbfc66eb916d434ebbf7bd064d864c8193002f1c8b911915c3969ff4c0456063c55ad6b47583ed9a7f3240f6effe383eb6071c139d9b84c3ea59a5748b4de21aad17e739a02c2f502fd2c6d1256d6fa9b302f05475b46f3f57a36150e7992ee3b77ff09643c203fcf5352628ee107f7cf008628a1cae518684eeb4921eac8aa687fe28d408fcd9639cbae20b3cb97b52230b6ab7b5648e58ebf225c28db3859a2eb5212067d83a9e6f9fcb47ea9974c0d20afcf09f3da6dde29e54a33d2377e661215402ef3239924edabf2b6fe3379b36095a20a4ea32e2484445cc3fc9c0cca14adbd810809c0a8ce8314f6d34e287d50feee6f2015e42f3d2b016079fd462715aea6cbf1e668a66858e2526edc5ef6906104ec309e2e0cb2bb445df9e36241d5933ac651c7e752641e1cd212902538502e06ce87f119514b1f2da2cf79bef1c2fe78a34497aa7332e11f13f06d8020c0a802677625b98d235204f156d26bb96da067530cded07865da982f2da44391d0434d47afff7e6ffb6b0b514769508798e7fec4140faa426d2ddfe5ea8e8c1f3061d84cc1bbe6537fee8a6698780139727476cde09a4d9f760dabca66a84acd2088f962bdd0b3d60114dafcfdd211682fc5adb32b2d103aba139abbfef76162a001159fef3826648dc2f71ad50084c13667d9fe58df188471753273b188238120aed066487e983acf19e1f2989a21d80fcbf0f3a635750e013b5b5d102649125067d1fc40420bfcc4cfed689a98ac15faf97da41457320df6d0243a9cc7da4fe0411bd9eb65c600d80754e24dae239df11d1e1df8db4ae58a317538c39879bba0abcfc859636eb0752e46b463944be1610fc0b7b4d660b378fe963d1053592ab0d56d1b0726123f126fcdfa71bb8580c53b97e1dd1318690a3db66b335e7cece0031f06f64ba57c176c3623a3a126426bf3597cb8f153058a6a7d0e463e5c06b0e8d8a5d750dd8546136753e60d982ec1b8d85fad1dad1a90824776a525a09e10de8c3bc2ac53ba8e3c4955031d757e1a45d0ad85ef522d2d55ac77ce8183c950600bb6dae62a86841b01edb43ad2316c95a6146e710435ea0d9fc97eb44fd590f19d92b06e39f5cb524853fe5968a908704df1c1feabdad9be6a3124beddffd025f7ddf27722c14632e84c37828756d7e8070ba6cabdac7fdbc72b75a0af3cf0bc11b1dddda783e360edf4446387f8e2956d96f34e021cacc21dc19a366ac550f87503dabdb62323980bfa31d7d50aee4531a8a803c1fb226c7b5475ffb9f4e0dad6dc42021a1a38f200c46d5ab2d72f9746335816b53cd1107b780c11c41420acd57a582c94fdb4427f08610f2611dca98d640f3edb5df29ddda610c0527800ecacec251609f968891e8518fda3baa395a0f0c07ba31a44127e990f4c5809409010c7a0b30df7952e63195c2ac3f223202100af6e9178b25c1604b69e6707404aeb82a2080a7ec26ed640c7e60e7d016e02aff112ad0cc40000f4c8395143b0e863586522b75d2d17b1ffbd0b5187615acf1081bace08deecdb8f876eee2db0d7d88764f7121f85ebf449da2203f3d81ca0c421bf27d4aa0c513f487c363310df6f9f72946b98f03c293889f05807dfc9c9ef8b1ed63a4552b0a1aff4c9b46087a1c0200eeb7f8efa1dcea86270d2e4af7b9a0846d4c130b88efc55ab00e0d0a49dc57723b95676210a7917817b8d455310eb36fda6faa5feef4eacf73b162067fc9a42947cf34f8310ccdd4bf5a2ade82ede0856c1e17298cf894ba88edad0eaa28aa6949de6c74f0392f1c4d6a57faec8dcadf32e26f5ff72603b6cc943a0994262d59a82cf194fb93d3a0cce6a8f4b0cd984ed3c4d7d5ec4d295097baa10e5a3abba0a426d1c6fc251b31f87ba14fab8acc23ebc0bd31a0711a5b5ac9ca07cfe0b01819ebb04a3ed1221f8f7ccf1fd1b7323334e69319e740338cbda9db0c7116540bb42fe3acb1395da3701d97ba64701f7f5b01e9266ace35c9ea95470218367055639e99b33f2f552ac1754e38d407906980b94034ae796d95dd6c030f0cea9ada0c6bf305638ca222cab96add245eabe309c845c595f8f025d23e600023792304fe86010e89ca50b7472517a3ea33376a7a7fd3c0cec8cc7035d8090a0e71635c33f5b14aba950107551087f95e921b46da4195cc48da0ae3c0e5ce0311a0b9d7e12a54d12cc703bdd700532c3248f2724ca23c01041e2c63b9523e028b1cbd189664330c746b46ba81c7a469beb6a9372a3484b3b1a2c5c86bb50207bd12f7d66b346ded14e6db68deb4529d573f00e29d9c737343f48f9100a32802f83546bb43f2888dfc1ddb0694cff1b8e24a7e4f5fc36460564f96a7dfa2d9058d6a085ca04696a9d5e278c10c94e2df302995c785aec22061fdb66d87d14f02c9852578e3c75b2ec58e7909c25722b629bbe39c11189fc4dd7d0fcb9e737309985cbc207c57bb198e5a09e2b2e7a31a7346907b9d9835740c34b284e315d908b475c3c4eca4508e8a169a6f057fc28d6a5922990697963b3f896e571d2f310a58c386682ba374d597bbb80335d0c5fccf1c77ffa15f4b72c1684fe8f1cfaa02ebdef82fab044a6f8ca690f207f455ffab777cddc22e4a8ab29eb2af9ecf600579161f91e2e0cd5b769937066fbd66c762e45ec01985e0eb00267b82db081203166d6a41b9e1c03bdcca8d077a21384eda20ad684b4a5f553b644a6b8707b40fa97b4bd4bf48eb4a7707911b41420dd8741563c705723c972f50dd6561920f038beca2d633c31c7788717f2972ab347e7ac2d7d8d7eaac5d206ac3069eb8f508def2568a9487775d61f3f3a085967cc4c086f36def4b4ae9fc486dcb9d145a088b135d8c59f6e5c07db2d994d69c8e033ce1bc46f14093fafa6dcfe7e31bee0d7bbd68a2f560105f5299c355b9f57ea6fe3b41f086bfbecf7072b90ad9a53e01b8bb2e6d51fb575cf0091a5953c74d6b1544b712bd3b5d6b6f629b993f71be0461cb0450b27e8025e736554c94822a0e246a7441700cdded75307e81508c7400fa13655c949ba22bcf757f5f6760dcab8a3afe55b4d36cdef505486f6ce38604f24944e063602d02adc4db3474edeaa7f108871275ee3b3142aed3896b61620aa16b9c3b9141da7a6e2f01244968a99c97f84c60a36c2ea1885d01ccaecbdf0b6835aabb7c099d2d0cb83e3212b39484a440b0b8a8b0b0e6b81fadbc2c155106807662b09e1d9271dfd335133ebf1a2ab1bbe74a6dc9e42214c45e0983251804f1a85b79b4a9427e8220f43f7f531548f0cbeb074a0a469856ee9ea3f10def03429bfa6ea5a638505796a86c37d292cf2229f7199583cba4a37cd30037499d05526344a79ed005dc1d1928e86a021f6913df7448aad9bb681f47c07f11ba1b0ca492e5a9452532f45ed466a52f2486f5f63d879f82c4880d5d40e03ec2f2440551839f4acb75bf239388e2925176230efb301e53efb13e500fe7acd4d2bdb2031faf0974eadd9ddb36f8f5132ffc9b43f90c3b3ffa5fcbc4d32ba32e1e31bc02948f3ab608d7dca6158e555a92ba95b1708358c0486b3b9bf68c166dcb65380678655371b5c2eda2c7b479ed9470a76c8e9b3c2bbf5c22f34270371d48e40e0ba19fc418674a4a9a5a5118956ef0fe35d98760b1861737ecc43f941205077205205cc6cf354daf3eae1173ec197296ed018227013ffdaf9db645a42da368470d6c53e736441fac1a7dda029e94f68d853eed9dc7c0548fb81024d45fb4657a055dbea743485ef4749780b01ea59987e335aa363491a0740d75705c8317c84504dfb6368cc8901e5ec7f69908a910fd5ee6636f96d2f25739f0c5b226cca5b407631290a67e79313176b469df0f41cfaecfd6032740a283da7d0d9d4db20f090ea2d7417fac4065f797372d99b4dc046e53de7d1f18e51638ceb0a98ed0a81b07181ca8124edd86bdf213640b57a907fa3d2d0432ba8f8494ca363227c1fc98068f8b96b7e46bd7548b68295a2a15c3b3b046371e3b5e14504b5d90616fb695015d41d821f1be56b5ab42e9d54cf085c24a2ba33154ebdb8d2517722538616d0daa4fed30e8303c3e404fcc995bd7446464f180ea7ec9f975ce74e3968641e506c9474f56c0d5dacaaf52e9a29a88e5105992605b4abe44c428dacd75cc77360dc39705c50def1f5f3161a62c0c80e75dc3c0cd0b6b7e0df64e7305b844bbea0f275b74b9946b4acb5992b035d38b58d7a9f4e16941e6be1b8c2e29dc801eab029e0141b5abc0484e7fe0199bb727bb34aed76ff51eb7f794c550d3f89d55e70cb54cbe4b9b39c1b4853beb22fad7b8c13b5eacee435b8c0af7b2aa10d21fca09d8e2133173eb652e2ea2ee7c386398024c76b33d68c79946e774afaec4b45a0fd7af0526fce288de2b375946e65e7e7c29dedf403c27adddfddd2fc84747e90a96fefa397169bac7d0717ce49a450ba86232faae76e7c1121f36de845ad76a0eda51ab547f17c264c2a0748bef659d71d4dde4fe090947e54f864b97108bf10a6176571105cbccd03fe904acd9abfddd737b4ed4c778a8e02af6cc415c49690f505c73ef3ea3e3e31bf4444600667772af7f23731890774aadfe4c39a36a6507a082a646239be51d8a0490d53b631823f488b4c74ed26295d66e4622d43a9e015f6a671bdd28c768bef1ca14b24e02c35f9a8e4237a677c1857444f6ef0a8c0bde6cf83fb5ccbf0d264d939ec88d434f60d78c682bdb1fe66ab02f0ee0e50f06c3a6cb85bd9858677900bda38fd541866f727bf04e04f14e22168b8b9500e8078d5b540dbb2e91bf1ed34d917954a74fbec2fbd78491673a4ec31bef383b82031ff314f852761de725d892380de08b842a49309edb4864edc26d8a6417603006373f3fd3682d9be4f8d69d310f1176d692b704640407cb43aecde4251c432d045e66da5bd8db763132c2943c8d419ec82a913778dbee2ec9b3e8ae52161bc20a27bbf6c8aff0a82475ed5071f8b5c7e3ccc549fee25825abc749bfd771d9ef0a2f35b5647b25b83371555ac8221d814573386b5a6b73019cf6a8c21176afe40f5ee24d2e7efee5906e966b8757dcf98bbf5e2936450a207c289ef47226d933017e15384c7d3d8f1d400787ce8f4c3da1471f05f18a2089d0757d856ab1e15d0f41453f431cfdff7b6783fae40673acbb8930002aa115d1b173557c8c4087b40a01c63a382123fc6f26b28ad75e53dde45f27e1425728108859a297815a4baf01b220b05e789fc09b9792090fe91986576cf0280a916c72bb8583ccf2faee5903972aef86002ecd629eff90548ac10c1a1284b7abcc438bbca0cd0f096857bb0f1067a764476c4f7c97011a8f1aa4eddcce8a396c0beadcde37fc15da2f108502206a9605ad7a54249fd1765b446442578f2ffaf7b4001c2071b03fe5be49e40583a9eab074267283475d9f4f494138e6b159a241bbdd4b7bf3f16bad4075800d13fd7ce92c25542ce9dfdca846946921590975e57da0e5d48cb49966d57b550543f1b3a0285d9c7230e62097d418fa79a4b7245378fdee40c3454d6eb213f00e5ab1169927e696da6f9b0d459026143a8a0713a31b55049e571e19c5ad427a0d660f450aa89032d433e3516a968a8d990a38b5db01af52898144edad8f39c304b4af83df4e2dfba14febc75d829f13349984d71bfa7bca7f0ceefb6315e7900c645268f5439c0800530d10f8ad07011e50aaa32f17a7f4b40881da9f31337f03562bb186bec0f06d1f94a90948806f3b0251a407b5aa2faa4704eeb0478342099254937360a83a3201fa4a2c6d8fb1f1601563c783d52966d4a988da36e73d0daffa9d7306adc6cde0aa51352a3bbbc594f61b5fa750441b293810d87bc52503337159fade850c8e86e1438f5ca51378e785faa1de0960f67940adb9fba8fe0cf563b3686163f759b8e95e5d09f93e6b62071360f77d6bad08a9f2db8d5a1d01fd406c2a9918e3319a9a2074950f7d40a5560f57e30dc1a8165ca70ff01ca404681e67524cc8827acf723df2b1c05445e0ae822b879f4f1689ed1dcf8748e909e37ae8013ea05607658d73506192601c905d88051a82f5a7c93cf2405013cb02913a16c9433500c21d87e4c619c4cd0779a133f86d9b6979ce41d2e74cebc7076261b041335337a3d1e5172819fc2ad48d54f82115737cc798789d5e510fae018cbc96708f3cdfdd5df3bf18409a075632f05929d6108760fa5a81bd46c78a015d94dbf62dcc944890d573e4b8e551ed61dcff4f6a2474b71cc6b10d0f96fa0750462bcf57bfefab0dfadb15b5a2bb860dd3a00a3681c35a646b1f81001f820bc6bac2edf351d525ff409d03cec61db64d81e670740a2974195e156208b27d0e4de7540b8793618e2b94c8708bdcd5c274aeaf2c3ec1a37d00ce263fc6f91e0b272e75af30574ec0f7c1e23cbf98986c04b0813545ff7cf461972607d485f70ce7b7aadd0a64f7e6bf60f28cd1b3a1ba96c6dc55b16dfb1cdab3b13533bf250e5997a861c32a4f46fe3c463dabe4b4c574cbb618ae9f1e8ff78baebb6f4e570762f69be45b9c31eb090f0800f1a3b345ff03aa936973dc055f2fadc0e5dd25004274fcb701327c5c13d16fa15919f7939ee45f5d415e5f3b671cf9e7e7c8d90f1e0f703414c22cf144bc9b673a400f5af0a294a1b2081a28b5b4fc6cea86d10bfd485356bca17c9357ce3d7872736e8fc56df7bcbde4624bee3005e30be81808e07a6d18996f09c9a1166b271e94637dd3f916fd22bd79e2aadcb120d8be4700a104f5c4df66d65ba7d88d5d9e385314ad5c260d5ad947c1e6142e202a7b9700b448c29eaa259a0ae9cef0d8a4ba51374a6be1c01a4b45f1e91e2a566567dd0a366853b358e57e6588c34492ca19d6dccd778c0035a992556397b281c44f48002fcfa2da45f58443108fcd1affb1bc5fbb305269c783096bc115de2f375f4408c1a9537fb2edb86265cf2da37cb7e53c4b1106733fd54a2c3bd9d1b1d1ebc206b2253289fd600e70c81e99f8df555a29775c670c23d805c892fc6c8463f1230ad5d14de955990559bff18c4a0434b031c655e38d8026ffb6dc0db2cc0c870e06fc10eaf9d43615a753860fc453022a8a8d784cee0eb52a2a71180efabef2380c3a6950c0522fad2da8fad28029c0a38a8108934929b70b870575228bb185f30cad8e6b4d67e59b48305d6a8764cc98a097f51bcbf8c7a6c80a207cc80058fd0cbe6b20b4cef59d8f5d54131fc238bf1d97a64412d817f86a21b22abbc17e890ec147beff79438a5f73c5446d110bc26275d769d06f464633262cbb12ee62cf01bd2fd4c9ded992f30438cd41d9468d59c7121f7a5c581c41e1bcbfcfda9e1602f42554a5ada6275e69b8215b4bec1e55ab7aed6e2ff89d5ae7aafa0e6ea91404b3c7ed0066d088a8c0efd269de18ee8e48bb4d776f8f6932db0f939dcfb9fe0780c61e39d1c867a22c97df2d5eb4abf8129d5979c1347f33cc30921bad8e7f0f62d44641550da0e36c65158498b6cec865ccba214315c76c301e1a3135f33b0c062e1b38864fc2fb578c58a69a34271dccfb3f9f2c6c9d090c5290b87a7ecb073d2596b07b3bbddd67fffe2f287bb529ddfec683c42aa914adda638116bd5e0cfabd6459fb4bd7cc5c6e7c1764d838a1012b187e9cf8548886b1c7d593444801314db5d4550521b39bed0fc19eb1141f10ef694d164a2f21b5110b6294a3ad060cf5ffd035e81000f4a52574a7fc0265ab1f95bb73d996c0e6857ca53b073e0e405207a4dc5dfe5338c8ac19bfbd18b9cc8777cba715daa8639941d559dbb20727611e9b270b1d13e221b24984f36b8b4773ace4628a84a0a077884214d4c806c0b1e29e56afae63743f7747f1e6301d6292df851ec0d0ef40ef438c0b4214083fb941088bff92d1ff2c18347101b7101ca97d0fa91aae26e257a22daf40ed0f20e6fdf1c223b932850898d3e55f8378b97f040b833e51a8be4ee6be54a7400331f0a931793912ec77b55b7bb18c0354fc77bde55465933ae036ef6b83cd1208f99fab41724bb5dab9c53224c939a1d87896dd01d1c7ce92becec09f8df11a0bbfc92ef6658335cddc8f60d2bdf559d11847f86603ef1ebfa9db447d7e63000214d828a0014dfbaa1a89359a43d0f7c262d8fc6f263141779f9d72dda7793d0a795c26f876562d6f666320956119feee80af68e85278dd25ffe320f6894a800fa1449fbdde07184be07ec769163ec11a52162bd69b3675c32c3dba94b1c8a009f2327b4afedc18a722673117402ba9876c6ccc81e16b62ef06d91664b36d4601746f18ebf4f4b6a380c1c0717abb6d89c0a13ac5e2dd9c44319bc7dcb8698a008d86f97a4b4f28ebc8ad83d0f8d0a396d5e1811f5df2c43e041b782b73595d04c76fdb737ae9b247041f51b8d37f4f06211bebf470d93d8aa560c7a989f65d000c43dbe85fa194a36dc00246a3f4968501ba5745309870388b603aa865615102c8d5abc6c8da5fe814451d0b4cfc2e9bad59b198ccd7bd4ed80c2b36c509440dd8d9e593f86673302b2cc54111814dee6c3796693627ca43093631b138844203ceba574dffd835da3a5be309168750b242ffd44266a2b4667b9967dce3581a019a7ba4184e89a37386b715ba9c801368d243daa408e8fced9114dbc31ea4c40926fb1a2985277dcda4169bb0ce5bd9cef3c4301fd3de21c0d37638912d194e02b1402be4317accb61c411ccc0446964e0e3a9795605bfb129a34562dc4969a0425868ba0728c582192f02bfb3dd37196f256c6b32c96cd0a1509ed6dd965eb0b90dc350a736806acfade7c23055afb38e50b76afc0cdb1f324e79f852db4e00e0a89f1a0318739d50b8a1cd6ade01e3d6b4b63bcc244819ab9729ad0cdad4e04a865f04a59c69f7d82275ea317bbe574e5a42aa31a733e67d235be708eaacd0269fb932855f30524f44f034ddd95df4c19ead1fe50beb5a709d822a259b1590cc32216512d49d4edd7c0ed12a2506e79185bbd9e0dc0b2564379452f723ea503b4d30e235f124d92a6b49d5a56fd018e90e8dd3d169f3867da7e2e365b93840c76bd3914b3318fe15769e7484a0712168583492ef40e5cdba5bbb8069620760edfbe37a8f42100eeb4e930569f3471b3492d29a1faaa696545c50c5a1bd62502e4b7c2c9c25efc4f10449a791d6bc71073986b8443c526c4f0551247bf5cbe090bc72feec42635343a63139963dfcce212f0d0563d2a3d4488515708721f02080be78c58ee2e40a014885eede95ad1cb825de8683392a9c7d0e0656028def30f6c89ce1c27c0342739c2a8dd9392f0a4306dd77547564c44f68987a0e6ac20008d23a013da1d12ee937bbdcee0b4ec4cf0bfaa13a4657fc5f3f694cda123a60dbb44b5b432fe2fec21aedc83fdd66e2a74023da8430ed92c347f247830cf3603631ea97446237ebcd26d508b62cf6d9e276b1e92360ba014e5467052b15030065df45fc5cd67a5baa144aa60e68d36855ee4998889604998336cfe85bede160a19fa485ebedbf33ab73fe3a373235e40baf7bcd1ba480b9aaf3f33904abdec003e1f1a9a13befeefc8d4c3c0c72e7c246f6597e2b22be2e5313e1d4265d99e0c7babb43c8fd6ad5edb2e959598d934dc1b0516d02b3e5aafa75817456bb67c01c4dd6d38a37bb46c327c8514e14b0feb3c65c50f9c43f9562804f10e5f5ad70eaee1c9d3dc8562b2e09b6919c8251cbee6af2f122147a763f7e0e0fc7077b50d45a93e491c37b8cca7f49f1dfad2cc52add97631691eb1c80650219de43ad80569636adc4760b89545e0a0a5f4fc75adb5b251b3a1b97501a6c4fe55e82e0307785d62b193e4ce8410c5b39d4861058a221b77f64a8477062c4a8a49bff7b50088039bcdb453ac577ab0952b07bf05572dbfeb1920110d172e11e266659f7305276e467f940101ad5c6ef06cffd3d9e8599aee30be02845eb1731ca2fc916c03f15c04a6ae5488540964d71123d6fb1b10405dabff16081e94394b1fdaba860e9bab2fd1f4b34aad547175b68d461c209019dd2c6ebd29d565ee0a1d1f312202cc961eaed416a4495985140d947180deb28e739c7a86e3ce80c6010add9ce20610865f9518f63f66456c587d138293d60e382c2532198b1e1b8e8c16dd9ab2077f76649dc6127bacdc33e1265759d48f93c39bbf798f1bf116cb5e82022c880e1d0f8968981cce1db021e00b78a4516eb360631466892d80c13141805f806b063a1f141ba661e8cd96a66a783d9d2bb762eb626eff7cb2ac20bb826b2aa1300f6203c331934eb93f64675f7d80fa540982a6db494cf240b40df950ea1d06ee0beb03d1b7304113671eca73e5ff0c128bfd75a10d840701dbd631feb64bcc7e0189c74eefc3d069b55d656ad81af5fdf846bf9c169ef81d790b98d9e886f2020386e2227bb587ce4f28f54b94c492d88f047a498d51e941b5e6223a047b1d9601c76eb05a8b14faf52011bf63e59dd950eee58e6ab9d14af92f23a13cfa06e40dc4338f6183f5e0281c341d051ba3e77e2c336e35db65efefd5bc829352de5008766801bb0cc50f410d1d3542ab471a40fdc78e1665cc04b70e0819dfd6817e0607a026e7bf6f215aa37e7a242d74a13ef46233212a85bb95a13296585c154201230de56079cb85b9d3915166929548feade266b69df96c9d1734f6a115396c0319cef2f5c36a6bfa34b2c0d4762dcecd618a27344eb8efb9e4960a92eb724d0339e06ec463ace0ed8d9f83c5fa5048321908ad619098a8b4fed5c823c7523d0b7df8411ae9b141dabed269568f289cd26c9181c6141916eaebab2af15fbb190869d2e1a68697d0d82d5f5bf72a7993995ec925da9181dcc7e70f4fc55f16fe013e07c6a74c39b454cac5dba654df4adb31849a7b981585183230329a97ceb30baaed133843bb26454b15b5930883a5d46b0955ed313b090e6de8ba2790b76e003d2404bf066ab82bb7581d869ca53168a46aef1709ff96b682d0128187bf400ac2afefd4938645991c2222e453159bcf7f9df786d4da14b6699efdcb97df0701e16442d73100467afcc6cf64832a8651bc527c0f08c4476920977db779b5c60c8bec26cc1adec998a601a4e003beba0f3762bee2b70437130340d56cd7bd7c0948b394ca7f7aac147b77831819cde8c6db61efdf4df5cac7d6481656db3e2604afe2a85243666e26a6b52127ebaa9c4c8ce62e732ec86a9cebaa27d2ba1e5105bb511c1b56cc7aa42ad24e2c631dbbf8f2d3bf42422411494539ea9abb16aa0f899001604bda965bf3629be7eb7998e36cc9c7570a016290e29afb3d8c4d5503e90e0232dac5803237ce1a738bd18a14ce1686307f29576ebc0acb2d5e9e2701c24aabe397db8d3401bf0e91ec8b55e55e115f3a993697f170a54f9a5cbd6a0abd3696d56f9eed21eb86bf6419d59ff35652552d4f90a0dca304b760fc5fd101235231cce4802982dc649f38b51a4ac5dcff07d99ec7610981a519e69826240d72aece8bb8879b86974c9d17d8d468bc7f40962b385a02c578297c56d0fd1a0646cbe64f6bf8ca304603ca241b938b06d6412e3f2aec081b999e9a13221bfb007a0ae3a4a1b4fe26ced70ae972ae08e8684a6c02881420cf1886081a22d92808f7eb73be68ca0d1c5eef5edee6d264d335bad8d38675e6a071b5775f3f32700038b35e7b505937fa31a0e479a6e9fd0324dec74af6d13bdfe4f8e05565882609b7fafea7c3c45d7073e1bbdab18214627aa98292d93aa7b82adbb1cca6c0700c96f218588018051ed0afddb6f3b39a96336ff6d9e2da7044b46df13e7d815f0e36ae5c857bd873e6193b12eacb2eaa7381d7082429b932e7cb58a0a4325c0809037df603be32385b2ebab08ce07fd4c51c66c88aa1b497d4b3bd2ae8a2270f01459a8811576fce6a8f477e9ce16222038cfde19dce9d64dbf019618efdca100857680d301b7f67079f39536e7857d750d2f5c6782246eee663f8a544d342620ddc1f1a88c9da38a67eb939cca8cc9bfbbec1f653d0964ebfa8b6373d6f988c016f2b673cbe3900f0094d3c160995450d54eebe0ba9850d48be2cae3d4984db03b3423116443ea6e82f04dab1fb425a1d077f927d75461eba92520b93ab74aa0bffb148c2ac06acc04f90b9cf4e282b4b5492c0232b74d381f99f457c6613df01ecb2f4e32427bde1ef6e1244aa61009990fc247e3f2e95231a3f8d709fd9250368c4316fba9074e6531594c9527f2395b78d77a07d29cd0cf607d3ae0ea983001a48c146e8a10d2464e4f9aa8f5d51b4bc369b85032df5fabb6c1283e476d70ce4a9c5b196577df55dd1d610fc3acc5b64b5dbaffe30b3790611922c304fdd0017165bb5b163e57a9e0f05f96966fd602371c6f471df0e32d26ceb410c44e6067deb9de591fd90a2b433610b255b22d5d2598dd5896c35f2f3cd90d5061253045b4ac689f33a7f42009ebf2f59cc74b0103058bea53704342b57e8bba210a200c131d5365395a83b91222e5440e6a696f57b39a1adea63677a452685e88dd203c799a4a792d1f1c16ca9239c7ec00107049d45b124b3f8888940c480bb968800d839779bac97e86c4e438dbaf48cd62471a227339c2d2c4d95de56f9327d080127e0a7adfd10fe06342a08f5182597376f114ba748c7490b49690fd9047a620f5baa78f3494b392e645c5976af1d036e70ef7cb21e4d6ab3d702ee17881e72097fa5e0c1695bfec4f1a64eaaf3e9186b79a441282b7ec4d1917921aa087013049adc324fe14cc4634f56e6e6619a59c37a1ce260b3f089ff7ac6225b92c7100b1dd697c05ceccf65ec1acfb57ed849d8a852cedcfe00ddb20e02f928ea1a90015eb5cef88cc8a88dfdc94707bc2f07d79519fa00cc9e387973116d56346673040d062b5460d2a20ea2e6fbe07372b981736f8ceba7807276b93932ef6b47a20a6ae9da770c578c56cef6741ab2cc938ab6ef4b2b43f67f432710bc0a04cce60b5358714793a5a1c8ffd54993e6cd4a1a1630d697c82bde9275c0a8acb4e8210a45813e8e3e3984e259ad6f224d8a40e8c476b2f2595adda2a0bae542b11b9309c2a3fd7d38970674d0c38de2614dc651362df4103698aed29ccdcef88338340f62eea7b6ada86f78e7bdd34334230e9ba1e10852a965ea50c9a500aabd575c0cbc3e855dfed20de9c9ea1dedefefbe3790fd88ac21078cb61c919721ec050f094be144e6401b58936c1303cc43a4e3308f1ccf39b44d4a413dc4151afdbf050a0ea5e5aa4e4c3eb6ac9ba32221fb0d945a4e555128d03e591a029b6738d57a0bcf7b8482032bf741b46dc0ca5fd8019cd721450bf5b5981378a39e6fa4a39b031cd4233aa74834bdfa15fdd65d798fe8f31fd13d428cf162c97100181a3d4d048dd9d4477e80d6ed85dce7496a23f1d4ab3fa28ccc9ca29e57552b4d1e49200e243de57989bd4f52d3bd59a00c7f71c090f743d84a16bdb6f673f34c065a3b0433f863405d24ac07a162bd6f7679049bdea8ccdee7930aa0911debec26de4a097b2604683cde6475217c3477615bb2a8d0dd136dad3142b65f5a79e923becd03ed30c2a9fc83b62d3eb7e24fcdeae4a11803a9a295dd4cb17e3b7d5f61781a0175c02031a60fa336e3896d497cedf922c5b93eb9b69f1bbfb7c8b8310773ab02e8f0dd96bfaca269e3e2e494a28a16cd2576b6a94e2d3392cc4e3bc1dc30400fcb46046f54332962e59f82b8dc3064f935626b06fe34b6101be66ca92b846b00dab39822424ca36445e17116067a60bd9bd65d1c1d06c59450e9e7d6028390007e11a696a8b3ed26e85ea873ee849feb11d3c7a794dc348609169ef852f20100402e537262d78915788d6ba8bf39bfac0cb7e8290c055469346eae64cd6a310523f10bdd4b32d6855784156028d1918696ff188b5f1f04be078ef3b64db17a0fee064c737323c1c3bb8eaa6a77e577c5a6d7ec353af451a29fde6f0555d679024e542d0e2e0c6256ef74006a9fdfb36591e357ca051294d38957677c1827030a7085d7c1f272bb302e6ee55b79012289afe9ba505896d234f31b3da41ee7480999c585c425953cb57973af592189e7d38c7112b3a81962294673957bde3c2006b52b9c2030646d43c276a03592e501fa7b590448c3d7fe44ef835c73162eb200795625997643762ae42dc7cec3729e4c32820a1880202c1a9ec439af5f1f880685bf74beeeddcdfc279a1526ccb0b82fa2d12f62e2c5b1338284e93b47be590abf0d73d4725c4933d265f397661ab8249200ce7d01675c6767a0e411ff1b460231242465e5295bd9fcfd5814b2766bacb1bdf8bb5fe5fbf6dda3a7943d925806c1737ce82e4400f4c7412d819a344488756a292cc5a27208197e8ed95e4beb089e627ffc5ae59671affe6f12d0cd62fc4f7888de36f4c22d9cdfbff3620a270357aa906800d87e3a3495deaa701999a652b82aa581651b0f3ad643e3d31a090f30486bf44c7703b78993482e8e0bb99faca2a99d108848711f79b32e37b10307e6c8b2041ec8e07aca9e6084edb35b0e641768fa8acbbf0bf3e20b047fa7ce03842632f0dee74e53bd04f4d2bb6259b97079b5ce04935021c0d79f06fae8c0004d11387b1b2d75cdc301e4c280c77d237219acc0719cd9dae4045d0e8f828306b91af39536d6f850273615b51aca9465e8b65eaa7b4eb05ef11ec9990161a60ac6439bc24d1c0fb445acf4b4cba99f4ddb40d5dd82657c518fa95a0433f4410c52492f96ffb7aa814ecdf0ec685a66170816217f125a4a1fa696cec1592fb701f8de55cb334e9f78915a1e3dd30bde5c66c9f6fe1435d7aa908e0245e3ba930bfb0f02960cb3cbd3d331c2be7e2dc1314a8bf67898cce0bfa63813522c4b900eac9f8fe14d31f2f71cfc42d5b0bd48ce3c6e8d5662ffc9db50e7ea7ed0b50200525c20ff6e6302585cc56b729891c98cd3dfafbd729f7e6dbd6338ed4a8a810230b8b373d84ee0b7a4d3c28614ac3f934c366a0487ba62c4ac23dd4224707a09ee2e2c66ed53f84b026c17fd2396a3be1be6aa6150c9dcebc8e13c02a4ef700e34051aa473ae5df96a0a1cf114c094365893059039713a22640e872ed944da06b7afdf02b93c04aa84ea90c695774eef32f50b55e6c5ac2c6cefd0a42025d0073724cd5cb89ae42b26ee3ee9c0c3e2a8e7cec2457bcb77aa09d7e3c9593e7c0fd05492f53b24c297d0314a93d9ee73dd7393ff197f455e73f633edafd8d62b0b2324b4295d1b14d288f78d42cd87ade5aa2beaebf9a489c061956e851ee28001abd5bef6f8d865c620d3e29533fc149697c14a5d5d16e7142bf3dc413429680892c5be7ae973c0db15461c0a7eb4c7c6fa93e9eaa9bf635b3dbfa3bad5251a0ee943a16be6e9ca58ae2758c0e73ff6038e515a5a926e7617059e0867c85e89078d2e2ad5bcde287e8c860edbd671ab942778bdbcc1c7921ffc26d337d9af1a0e115f43fbd394b3cbb5915aa47eb3246af5ac1bb52a139b02388feee54eb47004e554bb49b97774c8097fa018a7d56a68f670b35161d2ff5bab6f53bbb009350ce62227e052748a445c4a4fd56d8e69c9a05b8a36bf049eb788ccfeb4e3385901912ec481893a01db0ec5a8a5b4e40a1d6120231e218018f8414a84f1befd8d0bd8de86c55c0ec8bd529dd4d3e7714c9a98c5ca227a337549a04268f00576860c1f75cc3f4e0ff770e84b6840459a884a3dd11a5c4a9f2bbd477dff1fdfb2e6053ec432a735095e59215e32cb897e9f12cca62bf65daf38660ab17dd3985cb900efbc7323d1ccea72299084ec1ce0b0d58a9419be4d65d09bc24a8a2dbb7a4606846b000c73ed19afc1fee71aa3c864cb51434158c696c9c76023b5961e2016041e7f621d17a76e6481cb8bf28ebf04c213c8647dab68667b9d596267215e2e0cc2c40f8101abf8e4b919755f51c8acc58da76fcf3ba38eea4003bac28f366e0360bf5e632a3538ab92e07fcc4f154660e3bdb270dac301f92e519d46c3461d045ba6befc421ab03f907849cc21563e05ece092e26d725c4974157cd55c1abd0f8e4946b723824364b5e2898dee78031483954d094688bc0925e486c4b9102203dbbeeb14d648db7e79de18582de6c5c7bcc7a1fd11daa8586f685755a4d1b50acb47e43bf0f7b0a919c7bd4a9b6ac8b1df838dbd556eb327516a04a7276a5d006fc4e5e57eb933a93f7ac57f2143f3a4ecbb01bf894e50917e8cf04523c1470a141fe37ad231f9a074fbdc2adf2699ddb43afbaed1894c27a0e53db3ace41e0fe58676b2ad6198c31c31d4c2c9aee45f3a68d3085eef2e7748ee4dda795a390d4e17ce308d2059433eade2147578cbc92bf877179e62d11feb03448a00daf80e13323c0796bc7ca639c95034ed47310b5bf98fadf5aa628fad189294434fde0e1aa0f413ba0b9408aea01da087f516314d5b65ac164f6242fa44b664c0918405c5134d7e5a1880a358e4912d3d9402f55f2528e1007a2875f74359223b6e490b7cfb31f3b348fd689fc57ef24560f0b20f4aaea47f04994bf5c8cdaba93e9d0f4654a40603a34892e68b0bde87570a04f5d4252701738e1161abaa507b4bd40a7ca706b235a061dfe9caccc6ab7a77f3959d28e70d311a41ffa12c8e0553e208d4a9ec6d841063fef1b6a36d98bab2f3953b4cae4400c32a1dfe79ab97f51209517b630ba3c6e86bf110fd27fe5576fdb17ccd7742c48c2e5652cde4f4242206702e4e864f210442545db89086ed156c9e12e7312b6e6d55124ad65d7ad2910be9cbcf85655bae5c348a3059539ff1e13d69644cd6b2107fa1783ff16fbaf5042da2cd33c52c9737c80187d23b8ff82703becfa82a8436d113e7408e475c430d5226648bd8ff368129e0721315cde38c6df25c8bf264faaa7170f9d36e2f8b0c6f2b09d6dc03d1a5ed21b9661d9dcd4070d79217af70769c03c29743929bb809ebdb5c0334231f372e0f241cbb60cab80b763a019cc65f40f466a4cb35b53d016badc7705a8751404ab345f139eccc757e4e226156a07340cfd03b6aacbfb10b -generate_ring_signature 6a7c21315ea9a0794f13bcb92ebfe303e1b0713cbd6bfacb529134a6ed01585d c86de54a568c92e9d6a2be843389eea6397c740fb17949b70fb10a9a58842e2f 53 a4eab03193f38a2e0e982a87120a234f90b0bc7944f51cac35a25176fd58b777 d23b91b0325fe014f6e53b9fe832d97042b4aaa25cc5f1247658419c48a8bd49 1bbb20d6fae0893cf5485c19db5e675d999a0e9dba0e0718801dd3f2707793f3 2b515426826b80e919c7b7acf9616b6dd5572e0d5dba19bf9e4da51c00335487 7c9eefaa09435f31121d5e355e4fa554668f4837a61973d30dab74d464cde65b 37a99f9bd561f0192816187d019987c0ce5f147d4c418bc66f1a5cdfa0e883bb 8638bfc48383464f756c3664e864d326bca7855cd4f8741e29c74dba9cf4847a d67f42eaf5b97140183b520d329b27d245e46cd7bd00c2a171ffcf3cd8a0c9e0 4e7fb336b76b0304d312f2d75b8bf75834b7398db6526008a74874e4e56e6aac da1afcb4fc99dbd35c26712046956d72c6ae8500c3f916b34d670e6c9daf89c3 3d982fabf70524a3fbad979afbbcfb2d0bbf8c2aff2305bdec06e5c79d98b47a a5f8ac98cba7822b50a7073a987e71de73ab843465b744a3435b4bd005522a94 08ac7bba764eecbd335e48c24dd1c04c69e0f2b24165b12a408901dc5da87043 6a9232cec3fa118d16cd669e6dab9a8ec07a0141b3d3b5c97619289400168432 cdec045aa9b95db4758e28ef5d3375c539019188ed4994d59d932c5147e7d84c 76b7e24b615b9c211c78a8b212655d2abd2ba6da10ced987e827163c9f8d80c3 48da2559b00cab37fbb05f10ebcacf51650976a020f045d7dc83024afe1b2f6d 4f16a0f0e1da96f7b885a88f5d203502d5bd717ec8b5836707b2da85accb05c1 80d6ff504ab0531ce476105fc6c6ab4cd39e75f9ba0bd781b7f6e9ff483f71dd a063b2575fc0ed77466d817dadeb051e1264af664b3baf2fab0b3bf3c7b5cbec 69d8a67beb3f99a7844a980df676962f97b9971a1b084a5220bd830cf279e181 33acd6c839c0c7f438189e1f67a8313be209413fbc7b0b39d1265a6d757f879f 97d4d0006c8c7deb27813cb1235e7c914a20da278801c2f3697b462071114276 f006a40b43795409e5c5254e5486c9522ee2fd1a338ed26e9b40ec4e1c29cd52 eb6c0ccb68a8bcf2c2f322652f6e0635d46e214f0bc0961e6ee431d9a6e70e6d 10a6835fabd12b737505f67c3194489ad1f67e2f75982d50c5eccfac7661c050 2743f8484a17f4b8ab423d9a06e648638f50037a20b453f91da821bb8dd98737 ef6cbc0d2dfe502227ce198c737f0bf0f7a4931444bb8ddbbd82cc317449dade 574b33bf68724f9217faccbc97a60e2e6a7497582224aabd262dadf54a73453b 1996d428c975b3ebc9504f329e76d724f2716fd2d6b09436f0241d55525dfcf4 4aa00e19bbb3aa17adead7dc76b7c018a9afc20b424fd11b53514193ebb48568 346d12690e9ec83837d4f402b0fccf731d999b38cc66fa1bb8be1e46398b04d5 a819b6df989fdff76ab04fdd6cdcadfa2751c54e14c359b6dc63f285bb545ae6 8cae8ebd0995b16248b6f1e3c7c61d9f0de44b582ba27f155e1f315b81c0af60 0e6ab1fadc1ce2f6473481649a3a500a86e4aa00a042d0257506022a7abd0965 e2adb81732a12b14a36d92d5ac877c7eb23b4d365a31f6340ee0e85e3044cd83 557afb592dc585c70ad082f601b97ea5d53acc1e8c1cf2acbf73cb2e4ec8ed20 6e2edee22b8a26501186cf35808472bd9c5343c3763d921322398abd5b524e4b 321503c0c52728139ef0c745f510897978d1805ec1366245f14c12472c8f7926 1e94842bf9529a263ac929ac90835961315297f2d01d83fe9212cd0c5d86f662 f278b17bbe1236d3066be0bd4befb0e914a524f85bc146c117e05db1abc54126 d62acf5d9572433e91cfff5d9d319723e0d49dab4a3ff4a7a2c27597ef4171f9 1cb00476f95508a5c4b27c7de7ae82386bfed926cb335dc6bc934b21460abd7f 32855938be63eaf809c44abd425f69aa8699cbf7f8ddb2a94817692574f85b31 6d16cd460eef86fdd3a4ca2642866ce01e7f09d9c7e3158ab61fb4fe7b1f1866 1382892cc72eaad23a9d62e3dcddc3c8e093177fd99076db600ecfed64b60977 d7757e0cabd39e25dbe09943b20145c8ebe5710004c53ef8355b9e72027f6cca dbcccfcb3708dc00901fda1044bba7d6528a966dbbd4c7fdaad104817a37428d 42296a58204e10349dc6b9e58625ed27b7a0fc30d56f275e5c492c78faf3005c f40a1bd0bf96be276a9a65ce584aa7be335b762ac9adef807c31630587ec85f6 4e4e6efaa2112f4a9f3e2baadd787b09546ae6090f39bb2c2c701ea2bdd8a36c f74d02271c39444a3d2742e7e4015fe3f3a7f3abd96e4c04d719ab0324352638 1f89028e20b6bbd28eb46ce5a94886c0fd79ed9142147991750b7ea512c03be5 fdfa920e8a966735aa26527da416a60a777c4e9977d85417cadbf32400d27204 12 362f1e874fa8da5609e479cf975600afcaf36fb8fd6b420d1a9e1da0afd5760b12d7e4a82e8b590e594a0c40b32994c2b2572904126df150a76d0393c2264f09fe8d7e57b1515326e70d09496f7411113b3f8084d224e4b2be5a7b18874e0507feb653c395a5353f1ee55ea1f134aa4c915632de158e6699af6e9b0c366b580e4e4e29f290eae1246dad892c9222b90c414bf12d7596354e55ed2e8f1f5a1502b655eea199db586efb1a415ebe0ae929a552fce35c23072a04a1c2e0c08c8e006e13c97a272ea2c2b1eb91d7974a448e75ddcaf3147723249776abac25814d0a93d772e28c853f001c864f4cb3a1c75c1ae34b180bbc3c638ecee1230d8be30777800b59cb4242b9c9fc859e506edd2f26ac3ed2d30c64c73be355e07810280bc7d4a8e3dcb26281dc6e95cfe861c5b138292809a77d0ca1dbffdd2e2066a00f8c7b79688e1d24aae7e18f65740b899b5e3767e27b36926faa7f1321791489077439c6132c8451f169ce7e96d0e05dbc9bd748ec339749478ef8756767fad10a87a271d42874b85c729f12d514e49c9cb26d6ce7c5abb96b3df4c99a1db81c026039b0ae079dec47b1b861c0caaa53ba18f050cc86214af7ebeffbfef8908b047498ceb0cfae3c52f7c5454d337f11322d0a9d261eb65aefff524a165e3cab0885091ae69aee81e0a85cfef61a0b596fbcc5b864c07dc2d7227a6cd95db426021d51e7a8f1c56c9f786a59f2ee768c1285e526c23d30f9f6f087b8115fde8a009cfbf59104be29e9dfdc982ff880f840bd4865aaa5147df8ba3d2739ceeed900a1069774454116bc7f689381a9fcf5aa2c6daca1b743c08ac40f2ec877b9e80ed491321eca693a74d6c5e19fc63f41bd9124891337dac863266b5d2cc629c80883757195377f736388e51c8e2581321112f3792a6d2b4f64af3502127c6e060e88a22be0e27210da967c91a0954bcd9bf5e09488402254c144ab2031eefffb0a6d09a7c24f8409876937284b23279e91a3d67e775ba79c887ae150372f00dc073dabdc3d371b60eac924ea4802335b2814ef81fd3f740ecc243da8416cd6320ef99f21c94ea31b0a3fc44c5677ebbdfc3d0844cba376f931316780eed6cf590de5addf1704eedf96be395710ea0274a6e52b648ad77f85303cbc408b67c7d50a48ee4f3883629fdebb7347295b7823115a24e70cbaae2879c3eabfbbaecaf50c38f9de0b60bfade3951de997742c4b997759a25951d08ed2b721eb1f4e8a7e091b871cb07485b3861957a21201f9372c5e21f06d0e12b6a0652c3745f5dbec0bda07cf43d0e9c70f40452d340738bbc883c5687bcfdf918840fc3553cc8ecb05624365a429a6e79aacb5055c298cfe4db2c23a0be3be9ac39010d71eb2cb3d0ce3db316df6852cfcffabe94172f31e513e25092d55b944f5535040080e6e920f77fe99a7bdf20fc71ad333cc8a5c59e99274e5a972143bd3633432853cec0b076b31e9ad695b778bf14f5ae3043da1d9d3901b798025b5e62c592decebdb6f044adf0117bbb960dd7ddcf9a5d4c0bbe338139c17bcc5c13974485487e6ebef0cd32c9f153acf7be0668b03ade1e3020b7cbacfb2738893731ce73f9dec630600143c099f919b16be1758eb553ad9c780c8290e2fe1ec44d2fa6a2d7758d8fc037ebd2dad517cac1f698052697e10abdf35948b0d23332f7c264f9e55f2903a0385f3b8a38b40be2f0e7a4bda96f81f60bcc82ae6884b28f01218dfa6e450b2062fbc622d4bd472aab4ff6c8da0c254370aaad03a0852fa5a2dfe58c31a0fd400aca0e93c7dc9150fe5ea164b773b5e5c1df27fb9d72b1beb82f1d11f0fea580bd6273c34033b08a16e927542941576198f359d8f0e5fde177bd1a6ccf4ec9e0492b56a484be513bf4f73390537dbce0dbd6d7e07c68c128376406b636e61ae08843bee3021f19fada8791ec911e9c879fec2ee6eb8903db50c2b9a71ae18b80fd5cc566d9493ab01078495f8163fedc996a4a13c7f1170436e14b1d914a76c04e0082e8b1ee2f2b3cdd86e8e3e0f68c123cc8c04bd517a25e80a681d9a75bb05b74eaa1b946d1b5af93d42bd4a1ab49f08d33a1bdf0516bf110a1f8a064fa507122c188f2500651d5f94b9d44b449d12303442153b042a4769ee3dd90b01580e6f41de0c51e66430a23bfdb67378503c6a7b16e5b491efc4296254070b099d0039114200b61fcd7d070aa2f756fcd1d6674ebbeca19b7f04627afe036012a80949e6c9387941d518081094b9fd1cabea95e53289b62025e801c5a2cd1148160ca04c483d4d8a61ebf93dd9aae43ff8ad33c344a983f80babbbd2dab1a6644a0d54610f7a546840185ec5493490da7142788a19d6c4286fd32d3566e12166600fead97175768f8b81fb354f20e923fb92ab563d3b67e238b8c04cacaca8323b0e73b68b7f5b71e13ad41b5e9e8463da272b5363679616231b7c3200b21c09c4098a3e241e35c65a15fd6bd2cceb3e82e69fe4780b3c963af318c600e020cb9801b9277df3df1cb89ff13a6e51698ec34b6b968bce214fb26f26f9a1a05ed04801e822587da6d43312701ca6e950ec74a4a47404afd14b89834834a752afa50206128fd543095165f46e2fc25e94bf5ff6b7b8355e9e4788f26eef4dad0569e70e0099fa6f66143faad21af9abd709a65cfd00893296d54081c055078e8b6bb70dd791e00d92dca8073a4e6ebac2f3a663aff43ef9c1823cf2844b0173079fae0cde03ca32be734cf8c5f908517ee0473a548d7a7fa2e3f464d41910480dea390232942a44c00590373277eb80b03a6773331770e824e1fdd26d307003f9d3f008ca4bbd93084017ddf51053611ea8cfd4bec3c809e34481bfc835ad9ac1daeb0a1174640168b4dcfefdc6408f46a94add4275a18e3ba410fd5777b81d8bc1a808ffdc6b6a82680a0b684aee0d7b2814381d9e0f281f6f6d19ba5f22cda814930ffa0f447be2f7ff2cbee3c71a41726484b8816376c192e6d8818803c6633f580a458e4489b6278c9bf4e6dc37f5b17ba5afa092b3fcff6f39794b8a5aeac3a0000687f690a5662ac5ce6cf563ac7602ad558c35e8618e2d0eb2ff8f2f5ad508083cc274de7ac5c1661f5865bc14316700d1bcda79622a6c521dcfdba2152f8f0f79c73e27aa5ba0a352b1899ce188868cfd959ba6cde3f1db5a82c2c61bec7e0e46b3bbc7ae294395187a3f6ab65fb646be3595609ae8190cd82af93602848307db1cfac6900d65e82ca93f66e0222bd254e017d1f23022f76b22dbc696fc0f0c699aebf95bfb2876072221a838242efa4c41e47f8cdb231b7b6a4aaf62b3fc0ceec422bcc71e68b4085de03bdd36f73bad8a715b975cd6ed87394a667552750f4a7c45065ff1da6e221006fb6176879e2e6cccf4da34abc5741f0e9c9bf7530b1a3ca21a5c8c7420712cdb0ed3336fd4cb70620e2037bcdfb3193d6feb4e070fc72c6314917a1c3a051781fd5c138706d0de066f959e03c3fdb32e84ba42cb00d1f4d50efbd76bfb78eb50017674e3fdb2536f7fe1d86cf79fabdcd9ea1c700b4438eec37af8005d7f22cebd13b496a0d90eadfbd4943b0d120b4fdf9e890d0aaf9dc3ca85c63c7fdf9a8463984f0a6411324214b7c889cad2f618f537a685095cfbd0666b116c22798bfe2b2ceb0f247de8756c3bd5f7ab17c158658b10cb09131f92300b295fab075796927244d23f1f04db1da1ed711d4e745d5bfbcb9a02ce1a22bf3002a1a4cab1dbcd49b606e40c53a91aeaf6bedf01fd53f34b31680fe11f46a5cfbb0996ea26efb4aeff5a8c4befc90a3e76edcfe4c5eb3cf7949e0b9a1ed381c8e6d0b844a7cd3d0566c43d3c87d1f0284833466d9d49c6d8cd9a064dae2bd409836c3620755efcad2ecdae3b87de6be8014352c9e8d989c66b3906165fecba7b494f11e40686c2d46a28602b3909990f2cb38534e37da6499db901a6e2489705eafc7aabe77a263b0126e36a288437d6b744777c9efb7c1814140944de7893ce7b02a50bbd9c3dacbd4d3a832a1849f4f690ef437ca32751b5280880e85f355e0e3a7e6fee12d74f3aaabbe3d800f76c7a99aee977da97c8168b0761b094b1501cc2d7c873153b1b689f091eb471f6c7053d1ef0c14c61c50957085ab26191f2e43b6a3bd81b94778fe73a457ef7a092da38923cb7bc87f1ffe405fc1d3d74ec288436d39a11b7a937079d8802d53ee484de06071a3bf755ec22040fb335cbd138f7973b8eb5124f4f4408f7bf7061e728bded1a9dfff4c36c18036d3d7a2b8d3dd560aeb72c65eb5a180a04c3218fc3a9b25cacdd0bafb2e3d00bf7e9b7136c517c6da294ca2dc0fdd27ea0bb8c44a79cc72ee009cdfa9c80c307c3fdaf30c70efd881aa74f3ecb22edc67c63ac557491f5d4d037a14a17390f0df51b9cbd46cf4ba88d64edde5b0d31e781e270458dcdeb2586594c1a441c1b0f3d6e9fabb7d8098e2e576a4095d3d233512dc5a33bae8ced3c4b602dded84b012e5cd5a7d801c5a2248173380cb6d2044330f1b833397fe85a65ba0815d44f0bc5dcf3740089f0c2fe6c52ace47c649021404f123ca9db6a31c3eea5e1145607161e8eb5c7324c58061de7593e9cfc9cd82647147c9350cc9336e811df37760790d8ec6616021d40955aa6526a9159d1004f33ea9e480e3c1f70766a174fee08d71c7231079b69bfb86cd32eaf18d1e166e70e9ffe92f299cfd29625fb3c20077c0bb0a04e0164c0c442a95429964d062dda87f103805d71f273fbd98093a905 -generate_ring_signature a4f99b4aebecdbfa9f6c5fd9d62aaf72bf17ba6b85b2b86f6857d4eb5aa4e2a4 903b964eaedb5d9df0b0b34f0b20d58cabf1279e7254300089735c55779f6d06 2 6312750888bfa7ded17884f70275bf7e07062a1df46e580cac091363ce7030b2 155806e2d34207010d5b4149cff7eb191ccd3d38dd5a1cfd9dcaf19891878dfa 04ffca61416f5b7b5d918e456a81e4f50a9d4bbae583602f8544be5a74c35006 1 f3bfd665c411530479d358d573ef0a60367ea9a39f3b14c478c6a6a3fa36760d50626f2f9535e6e341e26d1f5ccc0a69c7acffbd1d902c958b2bfe234002ca0f54d6461138cc02ca1684a2acc7191f7a584c35b3b77f68c92a5e0719cc8dfd097918dd169214dd829ff00faa5d1e1a8a7e82351a74b5b740a65089603cb0310e -generate_ring_signature bdc62a6efca6c735edea0f7c692ebf61244271d37dcfbd89d538eec558460cef 615390da874162142d31fe5efe83e353bb9a35a06e861b6fa96012afb6606e53 3 f0927d23a105b3550d3aecfa2c3115c5730636fb068080dab5f9e2a91455fad5 c7a262ec1e963a0c885a967fea2e3da4374e2400ba9645db84ea1c785d13d07c 38a3d5ff005353ffd24da4db56e22636e1bd2c0182cf4e08a84cb50af7752351 a31a2f30ef28f312d39d35032878250a53ebf5c77325eba642299bf98b567d01 2 b4dfada2756e35f729f0cdd6464e2a3b55eaf2f617bd1f2d22cf7b4c37588a02da8c071cd2d2def07efd3dff48f3ff417257899a74d6c4cdc9c77c1e0eca350fc5804070564dacde4cfa88fa549520d6357738130732ce6e36e40c036079e00138d7e752a8f849a9130a10f4995ddd5d6b56ecfb598964fc8b933f68a3d65004739d5c0817c5e0d6532cbfdba5e2277b2f495a7b88b8678f852e40fbd94c1102fb432a3955212605264172d37eb9ccc3892ffdc22751e30c19e7516414d69b0d -generate_ring_signature eab1439501745d1a9b34fcaddb7a42e8d09aac0a4070e49c193b7eabe02f3e72 6ce8208563bcd24b2582f5c8686df7670c4046c6639d9948bd844d83c79e6813 1 df7698d71575f97f8c3699da593b61ed18f032140799e2e62c7a14e191ac49f3 004c41b8110c26fb97c3ceee44a7423e7ff0fdb3e7911af60bc4a03f2efd5f04 0 60ad30f6cc0a8e1b3f1eeb95aea3f9a24e582f37db8aa73a93241a491ca4460024ee2186348587ecdc00744b6ac04e4898d2feb937477377519e19f55bd1da09 -generate_ring_signature 50d6c84602d35a33a30fd22db0d5cafff513d3947624c0185dbea80caee90c5b f686926e40d3a434315e5f4ec24c8282275b89773f52a4ed817025943ff7f022 25 9a30a185b0624525d23c91ea4f28bb3721a797a790c5cec3a7028bbb8e191027 8f4f792489a3796f4c9ae40b0e7fc91a7de29072db0dc09d3c5890a294d01a8c e12506e001bc9c5bafe5301f3df40bd27a974ae162d1446fcdb268a2406193e9 e250ded73cc3dbf89e920217cfca4c614d4eafb5a40c6e36bddc349a4c686597 b8b0831d43d7e2af8aa0d607b3ac194c33dfdf7d3efe78edf3cf6546011810a0 76423bd48cf42c31fc156e3091f20982c6bdc3db8e792893528279ecf06384c1 b7cc082b2637338f2530bb5340ae7c4fee3a2143bd2453970ebaaef83c997592 f85494d2c98b7f25681f18bc32565d0039015aa5266f65dd232dd731b09b4cbf 3ee9148bb2f4dbceb9c0869f3c4e7a5245e6e549f214bcfe4d91f78fcb15d6ea 1b250b0f1e2114b6cf2d6c7fdd4c7b9da1f7bf721172be560e2fcefdfd4813db 9d1b3233a32073ce52df7d1a307de6b836582afbf0474351e7f0a2b2a7a9213c 83c98a063202a587794fa6d5b0fa0d3c18a21b81a43cbb366d26a91735117f86 537697e77585c2097b51341c2811cda8fa3550cfbc44e529f0a63d63ed9290e7 5da85646a0b264efb575a635de1ef2c955a765dd336351e1b2ec3994d2c04e5e 832efcb83d53c171206dc4cfac510358bba39225f385a551897c4139e5eab6cf c6c37d8fa87c45ddd9c6d9f1b770c6faf56f903b59afaa8803ee7fc6ba5a3e14 6389ab8261b9771d991039e4aae4bcf58fa807749905593d1479a23dcf6d2e3d b6fc1558d3db2bd362d2aca176659835a3d7f34d5ee2c6da1209bb2355b4e954 91c3a0980db59fccc7a90a69af24680af4bdc0d17e0a72fa7fa5da59bfa997c0 e6f8b0e3e397895d57639d5aadc9881aaa3d8ee04d31fc68d2f5b9e411683075 0a4503f9c48b731d1dd8b2280a575c15e5fef2ecaca934969cffc5cd115dbf26 1327c1f73ffb2ac17233d79624a17b1aac6b645f16f2407de8a92077c19a3c7b 6dccf4059e35ab49f66e199315e763efe2815a503eab8bcf44f4d25534f9d200 f947e8966541a52c0835c2a064c2cb71bbc652dbcdb1dbd3314fcc8e75a01f3b ae46f670527d4c0d9f04e0a4ab67d23a4e4089107c3250ef62c9a43a27c1c4db ab8a1a9091fa8089477cb325d13df16b35fceccaea164250309204415733760c 21 d2bf48116abba1d2948aec018e1178679133c4279776f7f23f980a2916a3bf0c06339e395be2bf4d55ce3dee813d12836183ccb9c4c15d3581187ac8d7a60d06d3365526e825344e7a6bcd490306d9c2c9bccdce0c64f65fb6ae948acd3b0e05fac2290d8a9ac6cf8efc42a45fb308815386c28afb146bbf4bbc95c1786bc30a26b70c634f23c8d67bda12b0a169653a89050928f9a9388264db675375cb2a0c5c047b9d0ee8f1d270d153276b591212daf6744ca9492d4ab4680ea4ffa94208ab89c2192d4d093374e5589759e21fa7155a88ebc1a45a49f780287fb31c3c0da1467c0aaf63817b7d9c8c3e9f8c4e913821cdf616b4b467c11cdaa86ad9a70fbb1e59c16bcabdd8cfb26e842c576dded2cd86dfe983e4ecfc5a09838227430c42faf2da6aff8095579656ddd5c073ffb6645ae2df3e20d93de1fb7ca21d5002082f4b0085237d42d51c484a704d3fbadc1c8b413440ae771685c97102f9bd0f548b5a270a5822ab2d23aafe270831d3e2717a1a6ed4d73b7559ec497532c700ff1f48e27ea86b838e5b73268f91e9d8321361f85adab7e907082f806f704503b2620155cdee7f98951233db488799372fad0fcdc3e14506c9b9ff085961f70c2db280b6fdfca72f4700df1a92f301d98fb9ccda81dd1cfce1c8950a790c8b0fae6ecf403b5f9d3499038c8277a11d05f58fa3a071345ad978ab75069ab6ae092d0e65398f8eb45299d33d282a91e7e5c34c849883552762352fe3a0017c340bc5b8ca7cd9b6773e35e38c6dc65efa4ced0ec4153b9e12985f8df1d6ec76f10f6a40b42669386ddaa15e6fabb47a5ef982f78a0cf243f0b26172cc5675cc7a05a9ac88d74ed7f3dbc34992b20ded541ab339b9fefd97f368003af8845316090f0ffa5e4656d7e3c789fce2138b683de1e7a10fc885b438d8d5911b70e2126a0793a3b950996b551e835a5f097bc5553c0fdf6ae6fa138257c490173b4004120fc534d58bfbb22293767d56e8bc866dcbaccda0fe473882f8cbff6ac34d2d9507de095f0e422dee20c57b104d3b08459377d37a2df822e21e64503f5b0b037f038f6ea6095e8f0ec3e33fd424c852ccd5775c822bb3992f16ab58a14674c50608a0d65154ff4edfd430fdac3c695adff068a0e4bad77ae75690e5636cc678cc074a3514acc4f135e7681c47cde9057e883c8d8f874703fa0033aeced1e9d7cf0dd9d844dfb47526eb72177eefa470d3b9547a1ab3afe41dd8a346bf3c28bbc20d0c317e0c0ddf9701a099cedbc245867ce2fd113726535a0552a5b88d5020650fc3bb6b7c835267e8225eb651b7fc81489b6da3dfa5286ef2727e7dee0ba4f6088200b248731ff36ef6478a91595246769f628b96a919b82c6b7c394056d87701f62bf204e948a8bd693d22aae3645a02727d950ddb752bb86f2acfe48ffbc407eb3315311243c8e54835c4a8019318748c61d64b3c6ddd62c6a1a0fcd9ff5f0ede06443e60856efefdabf99a6a1ac5638d69fb8395aa9e5457815b8191ef050017b050fe1288526e735b94d1934c18b3d9f7156a45d073d7cfcd58aff8e69c0afef5da22bbd78edc19cfd9e423386fde0eb62bad363cf2e27247654abe58040dc7a5d4faf95bc31ff5b54ae47ac079ac916f90bc47ad6c542edcaca72d86960cef4522713924447330672851f0e135c571b52eddd4fc327ab4599037dd7f0a05c6c58ec3e72f53556311265d817c769e655130155c6d583190333e0587929d07aeaaea316b3398679227068022b1720b327d4d0aea82985039ed20389d5b2901f2ae0c48d2832a9f516cbf49e53709d28e7237e148f788489b91daf4df267708c34d148683e551aa5af15bc23646f83a84d145ddf210d10373d0abce5d2b9208444ffef792299861ed26b37a4d0db0b4776c3d2d5d6bb24761d25a5bf2a61b0860aca7d99a08f892c474111b9dd79dbf10f0e3f69d5beb498e5aa4454754620b5532c6f8461db6b3ec444ded07fd430d20c0bd1aed791ca02c9a251289fdf60e0b67254803ee39204629265014fc6253c2db7a3d0a6c4acfb143dd8338f94507276760e484a3eff1bfda5572f7fd1c75da51f7504888853eccb1035a31e1a502f4e3faed1810628b9296f127bac4994c0f7bc106a3a12779e40a8fd8ff31df0e072af4413d10941c8126d8c3fa0e927f60dd4377463fd434ef3896b23695de00a7011ec98f709db4e46e9502279f2474e548af10481a6411b730255d81909e08 -generate_ring_signature 24b6afb3f6d03575e6b84afb6fd3787b6554e649223fcc94f0c3f8aefabf9457 c34607c491db77c2a81d12dbf15e28e3fc50190e43560765d84b1d88a04d763a 5 37604db2c71da54aee4ffa062a3ce264f6f968bb22e195de69667a1f6d8346d7 92ff0f511601d6610f31f3a53c956265053e53a94a9b08e841da63cef7480fff bef1ec8aa444f5d1702357ccf31de57d9d08297471c8071ca27d3e9f30302f6b a0dc011ae5cdcdc8ea827b893a02feaa74556f6a50801ab1d5994f13ffffd133 77ce2aea1d830beb85788d5b9b9ea39a1aa0383807299c479547e16e7c633acd 1c5e52449305df865f8156ae662da8e69d334ee7d0c9ac835a359ce5ecb0eb05 4 d17137beb1f1f93628afe1ad77ea7ee3184aed0078771629797403d6e9b51e0fd8102628c57d5b2474ff412e65cf0b6ce2d2f10177e1da21fae8c3123b4fc900dfe40327694e5b170720fd08ab091013b4645bee213f42e07fa7e95214631a08c186096778d2c6df8173915ef1bbe39437d6073e4409524fcbf5045ee260ec0d603a44976af15955995f75a773229bdd7889a5aa0184f292bd6b2ffe2bb86407a066477364b553440ac6cf9f7fe4636b48ca50e89d0d8891ef754134d7624c0db30d2a500c99277d90089542cc5058edc224f06b87af48853766926c03ef2206bf654cf9c3e0f6c74c42fd047c1c025a2709524087259ee25306bb861316cd08d0ca9a8c80ceadae6fb8d257b146f909d9f0691612ac6f0020b6c30cfa8b04073acb18a42c3f2ee148d7b2c5cb36f6908cb393f92e28fa5ed7a188190e33a107 -generate_ring_signature c5629b2c40a6c6c737f5dce8411acc9c276e6f0a56e707f8e57d60a7071557d2 743c872c39ccccc663ca95a73a624559e470c89275f9451e99b943a12c917508 4 7675eb1b2c42c881247435df509b540e8af4b715b863f696cf8f1d7def9492bc 2225eb2d6559955c01988be31a3dcedf4cfd6eab26c1b894f0c89b6625bc207e 287edf6cc3280b1a0e57115d79b5b762b3dd404ed1749aee4cdd0f3850bec996 601df0bfe882a553240b1bec04f63aa6592171d4f1da9f9bfd6509f1f76b463e fa9e98f42c3d58380216f5c6aa0ddeadf5a8f7f7e94b0f5e8d68215838d3bf02 1 83aacd68aa70217780e0cdfb64463cb38de519836613b8f6d6996402c45930041cdebe5e9af616c368e9f10a036c8ddaf3b6fc9d3fed325b52a59fdd5b83f80b4f3db3eea2d13dad10f8b35ba0ec1d299ec849c5247ba5a56778caeda7bfbb07efdd9310d839aa2d42f372ebbb9295023e16e8e347374f7d254938095d3e260e04fa15b33e5b64536294982e0e20b8d36a06605bd040de744171af0f020e2f0815f9e6c52c2345c8966b93324d503e40466335b79beb83d862b30e4e3e15370f29709581eb06f19d8def04d6b3c123a84a5cccd9246d683f157bea5f25f7e00e9b4465540565c021401ca6346c7452c106f4cb5101a0c8d92889bcf6ea18fa00 -generate_ring_signature 1a4197dee423f190acd42720cc120eacd6fdb3bf10b79234630b8858b6ca9767 b4267203b4ae75d442d1ff8149369c96273da7aa9eb82e40b905e1dd1637e96c 2 8dceed51a14035531d0438a6e2c16815792a3e4d76f0f7b34b5491e4da661fdd ae627eb1310b295996cba5c7958b1d0f0fe5bda5ebe6bfb1b4b75e8f82a46b10 0db7b3861e845b76870c76b143d4f559cf6449fdba4bc46075817caed034420e 1 d5a9eaa390fe15432dad58e0257a97c766446b1dcad9e224f70ef82b42dea309fc8c558fc5ee33829a3e671d023f144ce98220c7df65d490cfdb9152fb85af018292e9a01a1f5de0a47dc01346cb7eeefe372ff34a2cf4fd02db30e926f57b00257cdd363c629805034600e5c2e377c159230855b1b4a17d1991f80771d6f707 -generate_ring_signature aac5d207c0aa7557360adc8f06738e1881fe94b73f57368d845f5682f8096f4e 552caa3ef09555e0a216cf8a78b2e18271373231c4758eda7a270b2ddd71b92b 53 6fb3972559f40e71e36a3f1106a7866ee3ac12c0782692e78c10bca229640457 e883af1eeabbbb707cfe9f543956f5bacf9bbc169183279e901b91a1d2481e45 f1ffd5bf8f58e9e1474d1305a55939efef176dd58763423eb3dffa481e103c71 18e614da426c877dc72f4229ac2b4811fa69f56feeb194b73b1df699b14e79f7 a1ec96815246af7cd69ea904d218f724d15dbc0eb271235a4ff1be126925d4fa 344278387d740c1965901597ba56b536d71aa9354b209819bb4a0feeab3deca4 9ae49f5ae55a66e5b3a4b78e1c05756c13c7f7955805e984e8955bc79a7c8640 1c0fbea44d7ffe1a9f1145a760f6f42f7470e8e4037ba9cf4da94228b91d5380 57eb35ad73b6a99178453a88a91be2ce87e886f38b50167b7d121906883bb4d7 2a7b54e7aaad50e8c817096733116f69a22a72aeee7d23b9ea99a8a381015bf3 bedf676e37a4e4dd53b92a8a469184ddac922854327d98a02e5fc648232bac01 e46ca855f80c8e707cffbda4f0cb6ddc8875548aa8d30e1699facb2b1912ef43 89d6e8228d7a16faa1b37309835249ee56870557877394a039fac2d2dfaa27ef 2315f8280152ce6b1e5634e2419b22dd94aea973193b61d9296dd8bfd93b9ab2 12cc102744cfb58b2c388e52fa9c581c2f4ac84e34073a2a96376c6030880a62 f6974ceea92b921fd60f6cb02e275bfa7f500eaa682c915aec114068d6db88f5 138b0b7687520bc45d8bc3e6d9bfa314dcd1d8ad51c48c449afaaf7e7f056d71 e3cf7715e05664a5cd498f79055b41a5bc60b4b8827fc1fca083ee55822eebd5 4bfb5cd9dbfc3a1c41e59f9e1c00237a46819643fb967b5b92a80e7e386430c5 04e91cdeebb6eee9643337c58c719e44d401eec9b044969798f92fc2208d6ea4 f4be010794473e1db3049ecd4d84d05627dd33be6858ada87513d89bebd93778 b422e821b07ec1531698e2b89d50997475f78f8c59ddcf7452fa7094f8994412 8ff811aaa240794bd5e8cf5d21ead05fd755678656e8f0e7ae030a896ad0e3a1 b2b31ee946f51c58dc3d1e831acdacb308da4c894ada26d92cf885b466da12b5 2e333a1e3ee3b906b058bdb67db9884f2286d53bdc14c5143054b1e6348ecf63 cdbdc45b81cfcaa2bdde6d4319b0234ca2b8c464e6aac3ada947a181463a27c4 846eff34251f5c0076e4e2c854983c4044b6fdb836b1d6958084404c842541d0 31b2b6d776b38f559c1e8f6fbd7701a1f9cf067bedc98b91d5992579a6fb20a6 753c3a97d7a7ac59c3e32b3245b4c1e6a8c65fd79e2645dfec9e08196bf5c5c1 facda9cbfff0b7f8bdf25dd6af2534f64f3edf18109def802733de31abe06579 288da7b095382a572bf2b269f3d2f0dd91468d458a2c50be1139dbc014572af1 7edee65275e7cb061af3b8538a02dd1cb54c3a0049d722b3fc295af17c95814a 90f1b088ff7cef4436e5cad92502d21075b84748c2b4758dd0c1fdf0396c45a9 e1e9df368aacda8282da3cb81ea1758599413bff17aa9b3ed4d4c58a26c27857 7d59b675fcd945c0cafe8c2ba2623064e066fcca4127b04e1750baea465528f0 74e8a9e84b57bb4b07e46a3ed620a85ff609dfaee364bcb6c1dbb28de7f1c812 14fd789d3530800344d64a8b0173693bc41a2a909b18cfa88a5ca700acdc726a a6b766d16ee81a8570f6e7e3fefa39954260442b42c4f51a3b83ab6100292baf 6c8a23d96bd60ceb6b63e8353c2fc37efdd85b4af8ef9d6bbb6cbc56b78f3ce6 2fa3b181f1d202dd58d0b064348bdf31c1dad990dbbc8e059a1b414cd126e918 d2621e0e30d4c067701617544e8c86a0d4e2bcee498a419103b500868d9606ae e654d8b768ecbf0b688d3e513858f9c9515de599c70e4403236436768d1a1660 6e05f7f1c282324b171d197a03ab799a0bfdc371ec291a5dc8ab1407bc0d7e71 793bfc7db631e1edbd61fbec38831f9b5e324d2342ba6fb39e1f066e2d8a812a d869a67a0a327f783d72c395cc8b3319fc55edf427f2233b40200a7a76b3e4f1 823c7157e173f079713580b594edb97a0cc24feaa2356b5c493d927b25e576f9 1f52f97a0f224dae75244213223daead4262a21324f336affcb1186301dd851b dc4d8714ba6fb00db603e63b71095e0c0f48375647d8328bd1f1a98ddbd758b1 52b458e8782e3afb49cf0b4142fdb82de0b9a9b02bf66e396291581a96ac660e cc7a1833b75ca30eb566ec98a16e444022cd0537e9eeca574e5279c72cd402ba 492dc7df7860ada5e7fd7c076a7ac5515934d345ce74fc7f4461b19ee0c95dd1 a393560e00ef2187f5baf6338066076d72d170d8f3f76d53058804ff7ff76be0 77a6d6591d159cc018704b75d4ed14a0040fb8ab7924a6504e31263404ad7c20 d68bc46912d5f41a65b74c8cf7b0f387363e2d02b1cac744cd4bbf48c072e006 13 1668a872c614ab34cf519a3a44e3945f5e693f83a0308417e3c35522a2f5290711c748c3d9236a119f322b8f70bab552d3725ee62ac7763ad19b1d111dd8610cb18a28ad3aa5086e98fd9c8417ae8000f5c30cf6ac2d1c2578cdc8f9363a760d63dec8b0e97e5a8e65405d1bbf17c11cf8dd86ffafef84511d61468c7d1d340527ed234c4f98f170d045aa30463d9425cf76b602d8480d27c394a5a3fc6ef902eabfef77815e10843d3e5fcdb5b4c68c9f5ffdc113cfd0fc47e7a80fbf91d90550d696cc326233f97a8e460f75dc229c0e962ea75b4183c75527d8d46ae6fb004cc12bb4cfcc6752c7ca98a006be5d5a440fa4451f3adae49de7e32cf495f9001ff1c785b3823f3689116cfeb584c3ac1ec8395dfe95aa695040411244e58801af3e7b94e372a2cd01edc5646267f7721a41834c7e67440819b5e9413f43010a5b07229c103aa6e71b18dcad9c92cd1793c72a9cf2f3217e53374cf48b7d820d511d417e577caac31677c3160a4f8001d30a28ff0c8fb9a92adfe3290409c70787bb1fd985dc5596801a864f2b6da927d4bc47bc6fe74852825b2fb3f274e60c65d59eb1981502682ed9e3f6b66578a32ab8900e560aebb1bf9e5d2957a22a0f8913f24f65fb90264f400302731b2eb959ec0e5530fc5a2eba157bc268516e0af351cca919bd51fb5327dffd52acf8e9e46711eb6fd0852b9bc6c5cb3350c50de61d0c0d3e0507cca71e0c9adf0fd456b0646254e42c8143affcd4d630cf3b0f9611dce910d92189d4e0535bfeae31f2c2c41d376552eb6b1867cc701df9d10afb27eb66905421fffa2bdf2fea125ac0f132128a6856dacd2b450140b2f4a10b619ac214a953b719dc182035e2f2763b6caa4d0c85f924325c25258ceb936a09e58b7f8401cfc6b1b4359f4a01ebf03f683609cfef9e93a547ef3c76a56a240bb5b41b235b44d863695179091aad432fbab349e758e1456db4b195fa95deb20c3c6ba094317e9828c5d229b1f878899bb37238a35034902677efa451280f7709f8e08099470dbb55db6a56db92d0cd6a110465017d3d27c3d79f2617b0b5b60731682ad7801a6c3bca9c4942abff0242eb88df56e904bb820c6825158154900d4a6d2161c999870d541e59b5f21016b3c451efeb418c152aa3eb0edbd8205e0dcff0e8d59b7e3ed9ecc06211849e0cbf78130bb54a0fc274542137719d738605b674ff39e13f35c4bd855fc02585fe64834644136fd7627c9a5674afdcdb6b056c4ddd69be693b2fa5a70363325953799feb1a9cb94e03e2a0a506f4c0ce5a059aa16b454ab7bf602bf46d34b69ca3fcb1fc0ac9486a3914374120189b183407b99274011778613c4140d08096f03d8b8e161562f98e2ecbe36a1acca78f240703e7390ffbd248fb7cecdfa77cf4328e17412f6351342a3dce3b4814affe7307bdf318d457f330e83f3e7c4b3170cee1a14d9568405d71aa491fa763efa9570946bfbcebf5f5cf641d35124e05f2e61ef020cf59bc5786476561b409bb6dca0d97e051c0fe964e2734b6b4ba9cb92a5b78b186d4e17fe15c07b3e3ddfe4c5b026dd745862425cc1eecaa313a5bb29cf3e3733fde098dda9d18adde0eecd4b30373c3b908c685d7a56420fdd6fda2801be0b92d1a66139002ccd5ce7e63c7b80b745787494d576c732196e4a1473423a75465c8f85a1519711f87b796700be805e962167b4684336656e266d39c115ca2a7a0b768eaad2f1ed4598622a53f230d8b4e5ea984d85493b71223b52d01c9b0525fe69f92f38fbc1f3d6414b3066c079995ea42670a44c2ba4ec7a8ef8dd8a86053aad07fc94614b8656d347a49490bcfccc471878c894f758dbe4620a523ebdeb45d5a0d5ee4a6bd9d1c197197cb0a48d85bea6093cb5c7bc0c42b39342fce57e676638c52f64fd37016bf0e6e590f871d04072d18d8cec1f4e05e8018e7be7f4da767cd1bab7120d002ea6ac98c0fbf0fac99b40fc525d81e943f06faaf1fec84d13da278c18bd43397490c076d050153778e2cb166009ec9cc1ebf954dd930c00815e87f4cee7f969dfcb1d94a0f7b378b94e35ca7c7a31ef10d4b5aa83192ab6ad240edefcb18e13a5c94bc7e006d8a66bfc1129d90b0378ff62b564ea62d26d92c1f14775e44e4f453c4d398065af33ce4b4c0a00b4b64665f91a7d7d84e361c284bb9cd274e85a306b9cde304c0c0393d8da71d7abbf2fde949934468a1425f19b99adabfebe1287d6ff9dc011b6824fce6974b2fe60cc84499328134d386508c92644e63b51405aced3ae20871f7aff4a88951f947517b69e1f0f465f78e8bce9379d90b90a3e2cad4bcbe0eaf67b513a582dacb284ef738384f41246d5938d75d4ee9a39719121a1e56720a0df68a0be66ac9c36e9837f0675ad1030727ca886fb510409ab1ce2e6e0ac208ea0de4d44eea670280248aafd93224a89926170760211f38c13edbf1e41b3a044b7f59d32468dd82865819245f9682df76bc82c44ebb8f451d23183f23e281033bd7f336311f658b9f27fa898249640cca89ad18aaa2eca5584dad9f4ba25005e1a995bb84d74ead317570faa7959b89cf0f130d96b34564d74bd3814f33c50061c30aa0a4d723189b726e89b7eb76aae0f069e4f0bdc9d6d0220a614f6fa100677f62769d2d2f5d9049d333559637a920e92f22e1e64c2b0491c7c3257fca05955a8ff46f4266bc634e2eb74a5f56078e85065fed455eb9a0a6624456a3d70dbb37d3238fef783782c547a2b5b8b91b2039cf6dc21b2d89a1a66417482d3a0f64d32411c2d58c9556a5aeb54af513b88bf26f0b857f17c5c21443a8641cd401d9622ecfd21180593228c9305219d6ce91bed1844b1c6775ca8114f4b960f500a4b54b2902ca36407ffea886a99f8940ea3dc9cfe5e24f451cb19585f6e68f02df59cc218cbea3e0ea45cc72e574d4f9cd113a429f44264f794fafb88da9560e2c9f439ca1fd76da39be85f2f6b953c9c92821ca5619084565cb920d23265903539b5e8abf713a316e0e01ee758a19bb4f8882c9488f8f088bb7cd6f3b6a320b8cc38ba2ddd50598245b7cde53815e9e31809513273b1436b47c2115d5e7260a5e072e5b5d35f3587cc3a763333e712ca50b5189a9714473e061037ea8746e0b94047c5f9d7c99b849a09a1ac46aa80cc56848f1767f3af49d13ae66af9b5100a46e93d15cd4afe575a7489891a49a182b2cdfdac82780ca3a8ca17d52c6f40731cb42e8b48328dd749e2ce5e4ffaa70db997b2ad141ef5b5dae346996a75c0730611859c709c494f09e397bf5ad42380f5f1507278920abca2e5e71357cc2086704e7e93cfff3c27eb4e33ce02efab7ba1c02b7ca2ea6132c032429b937b309b0b0f7bd409dd7f7170c3509cb5d2c3dd42481652c071e44a177b64965ae980c8273f599d619cbee3e91e8deafc7aedd2707783b44ecf39ef3acdcca0c1b00049d88e22ac3d56a07072052e3de82415d004fafafcc2328d69635847e9e35e400f86267bc4553352c6a21e2fae5e4ac47a1a19cea9992f072961321983d63330038abafab2f704fed4a09c8585c6eda454ef38b0ebbc1adfb6dd6c026200b7c04951c45ab5c63e8e355f1eb52d14d7281c211e5afbaa5a1c42a70ce602c8aeb0568f599d03b038f5679fc3ef0871faf4d5ea9bc33a3774cda9c025c0ae300b30319b4b046d467fc820bd77f9a6dd31ac7398c2f7fe769d7808671a997dbaf650c961b152c4f5b0f65dabc534bfd7901b36b5aa1d086fbd374b8ddd4e8ed8fec0ac5e637d0f302f972e8116fdfbe4efc3d71f2cdc23d65c21b73661f83845c2901f0395f0cfd7c7bb0d056f246431470914081f2b7f53be8bb39d4f366ab2f040541c8f1108135f5e2109331ef71924debf1507e41d6ddf137bf2447217f5e1c064b575cc4a93766c87f87a21fef76840f3b2c150e82643d066314be98df9c330a42ffcc9a483da1a136e2dc906cc081692703da848de6736cc27b8270052e390f7c06c837d56eb78ac18c4c5ac0cc20e1e4562da82a161755f79bd6035d720407d54c467f8bb1a70e23b75c06215828cd1a5888db8e6bd5bd43b9f368015a4d098cd41a2c9d60dbbaae059df4a0044636de663c65fa06a0661d6449f5275d200fab38e6c65f7e1af3a15afa1205b453daf1ffa2f621cfceded82dc359be74c30b5fba3e1a0edc73a7a134682b2f73d3fe6a0392e239f66803bd167600abb5650763d782f12f16b3deacc5f31b37e7a60aafc4afbe258b8860591a2c889c42ea0e786c6373a4faca9dcf6b0299db5531ea668f1c1dd81c482d605d34ce7c3bd3048f6ba7847127b72171e258cbe3483171d5c0b511fb054e2d9f5bea46bcc7590f3e9c216e8c96ce3c6bf86b478259593e6b6129b1d95547a97c67d615e8b7920b7c6ba4b2c7a093787ed6ada578221ba058bfc244251449693b240244328c97034b0b07423844159bb77ed30cb5f76c9e7d36f1f8ec864ff03a8e541c81cd540588e4622ae14f99258cbbe0c788984be3f04f066fc5799ab49cae51774fd37a0b5c8eddd8585ddd5a30017c91fcd9b217321def9551533e602861b3b9433d9c0f9181a8023ba5af0cfc964162dad655ea443010473e1cb524854e46f150bbc30b79933768d9a8a49f26058cab67b5ff540338605a193a70ad523b800e7a27d908c52e7fa60d5c9e746354373173862a9de283a764db7d0e4c16d1a88f570fbf0dbd6940100f3c96868bd852be5db570ca36d5391bb32bac33ffb20bd54928230e -generate_ring_signature 1b9c52310cd90de6486e3c8b73ec646b0da895b54b118acff6ecfb69afa6e346 fee5ae2310a6b91390b1f30f2499faff560fb74ebe4b32f965af0edbb140a740 1 8084c2112b5d153f5086f416d6d1c059843bdee8f391f547ba9460225a69cadf 56320a68733e538cca2d2bc41b01634fc3b9e2ae3f9f83635b2574a4245ae00e 0 493ad5e819821f0dac10a9c5de44a5579dcc7c5a6a416da10b7279df37a11b0f1dc2a41e8e986240a76abfde51a8c94d965de621dd9df16e224256665883e904 -generate_ring_signature 65e7321376ed6c6696528042a65a9a4f6516d65fcef80214808c0c3230a52087 fcd664a50e9adb47d9ae24dc83f707e5178d3235b84c17e9a6f586c0bbc52a47 1 42fb2300246f28cb82112974815f4c14cdca8750a20b25ef861621effd08c45c 8e82a603216e1835a583482e580e635a34b431012284d709815768785245f800 0 ab228495a2f462c42dacf3691305079b6141fce9530df961368e680de31f53022dc2fb8d8952c2467c890275bc487d1d7a8f78237f6808d817b7f3b126e8c101 -generate_ring_signature 5fc776eaeb6aa0303db0d4397d093d225c8cc0e00caa55d5e3110e9d7aba1aa7 5745637caf7435d578fb680de1416357dc6c6ef0ef7f00c0d98952cc3eca7d74 13 31b7451326d0a7a224f17225a19e946750d81bda5a2f56ef09fb60bb2172a44e 9665dbe2e581c02838c7406b1c2632ea5898f527c27aee64001f13fb69d4e46b 43522177f2705f00ce314e061859663485afe95dc1103d184dd8445b51f8d943 d087ba1e5c49e3d7f627e402554bf365e115eb049d1f7101bfcd02bbffc09d5a a76ea4f37eacc9214defe54f2d59017a3d410499c3a6c7784c538d480764e2ec 881632bf12f572675667d5b546c761b3448453c771872793058d7e74975d616e af5bd86056c8494bc987c9c38ca25b9479c98749dc1db4a352ddfa119c436afb bc6d378f748e912b350e8716c0965c91fd00798534a25148a9cc8308b0ef1922 bb0e87d6c20206a4237b34b1deeab57891c6750e4d152e28a6477400e4d5b3a8 3556aee9033cd19cac0d3aba95b99694deb7dec82a18509529836cb013f7e656 1b0e39b149d8425fb452857732ef05daab09881647d5e5e648f57650a5fcc5e6 c2a218592130b95abb79673da2204d97970894818e0cd4ed17569c973403aabb 3a3a3c9771727a8adb3029e1022d766357396ac444aa17f75ecd13b7dfc4f25a 20fd014c0ce804e8bc23ced4b22b168bc3c94b8f76b7b0f2366c91e4196e070e 7 f13f64d4aa8e207d2e4e36e0bc8b47c619d3cc1214ec53f4854ce26fef3c680ffb3434a75358b5c9920475283acc64a141cd7a3cdada076c3c753c9fa307d901a118c34238801886893a205fb2175b7845cedc638b8f80a4f7a4a619fa6ce20d94ca17cdc5ef8402a585937083ba55f1c971933f293eeec4be5e1f5d2e7303086da6dbe3834bc8750bb1472438133d40b68fe2cbbc7fb83a7d4d65ac4fc476029c78beda3ff8482417349390807c669ad878c79ceb45d71726aef09649516700bb19a45f8503b7fc501643ca8889a9679694a288bea16febae581f2b2353d30c25cbad97b0e3b33f679604e3a50b43cd60b367605464cf53d79e3c2b24f17206d298e214c8bfd42b86b2f4a772c517d9363851c45c8e091e325e9c1d61f53a0dc64842ed4c9ee025d1caab83c3868946ea3449193fab6a766e0086b8920f880782a3bdd46a7087d95ca1d1856c9d4f437d3638ab6ca6de7d2b1c7e0bf631700b24f40f78624cee8559166b65ebdd8a69c0da92c82b0fffcbb776acc052fc900258f8330d168224a7a96770faf0e2a987a90c251d4e76d5bd11e11833e908f308d4d4f595af5b9a4f678c72e2b9deff2180f96e36abcc63a57a35e446c5623007fd1256aade7c6edba27693774e1bdb601ee4f97b94069c09454bd63868bca10f7639d4c1ef711541a0b5330e11c1b37e8997af2aff9405b30138dca26d9f6f02064b55305d6a0d85cbe95d8921bfd208a1b6580a34a6286566f292f2d2f88e050349dda72250da36bb35fe0d2514b347cb522d817c7d76e43581bb3942165300dee52e6fd22237605beac81126ff4cc4ac1cc3023cffb5dfbd88a46e65130b0b93284a5bb0ea6883c336f28b0e2595a7a8f97a74404df21e2d9a6ad41689a8054570ee404e403cf8952f3e5c78d2b311743fc28c690db778d4c77742045d1108b33c3460b6eafd30389b360ea934a5e47396747c583bb962a730158441fa650176273d2678872f9d628f2bcf2fa4e55348c347f6d5cd5add5d16da2dcfbbad0a9969da01f59edcc8d997e9baef87fac85cb845d627a446494316f698c5156b0bc526d485ede63f84581f04554a7d4d49cf27ffc42eafa570130178eb0cd40d0324129af54a9663840a790dd79b4dbdf8ab468dfe88a09522c504b88d01193a03 -generate_ring_signature 1b670fa7fab988e6f8fdc8fb107b3d075faac101675f315f48f069d363482811 311bd02641a786377befcf7f879163d21693f0eb1bcb162e016f16961adf8cc1 6 81b31e3da8c61e445c7f342caa50aa1f14e957cbbca3d541c909dc5040436732 bd9772f8dcb21506b557824bb497ef50079ea93d74b936ae6ee40fa1ebc5e261 7a31c4a1f85d63b66d38c7656d27611f99b933c8b97c1181376acb149dbc2446 763dab71cdc26c00204f5d24ca3dc0bbf122401d0bab86246774d04adea8d2ef 19d20452c993dc154c00d764ea28ca9c18f92df93067a40b90cd98cf6055f2a2 0765b3b2cb35ad43f4b64d7da4ea43c89d080c201bdb45d5b546f1ede6eafe4b 8ac480f5d3827bccb2bcf68e45cb0ca7cf3e9c876d80405a7c455b22bf68b70a 0 627c61808f2e8c64258b96241bd418678f29ad5e5bb0b54c998bfe0b6b6e3c0f853a06b285c9d6920c822865727d853f19f814d3364d16b37ee09f551cd2b60aa74dc88ad52de891fd4cfcec22815e175e9bf5718225e8d4a4e14d3effceb40dc40bf784b80e7ce3acdecff718860d31c099bb2d73cb5b10360558e32ae05a0cc98f34e4401846088db15ff27c4d615463bce242ab4cf24b42676b53d00141028a1f65e7ffeee54af647b8826ba60954d85dbab97e59d6087663a14c66fb7f0069cda230bcef707cca774c9bd6df0a0346b9d1eef1900c93118aabee9d2f740924339358b4d6e6f861c969b2ab455536d1b70b65f1ec9f16d960d106c1af4f0551e1e45a607472113b6b88e381156fdce641fc66d66539561c0ee6bcb539d40ebec532499c6a5c28966f69c43d6c25e0a208feffad242ddc800b8b9c58e1f80482b48ba52f661e0c28383b5daf6999fd1c6c1bab96d42b4da848b37d2f4bcc00bc788fa7863407b8b5aff00c1c50ce435e058496e0c353b264dc39ebe9a15b00 -generate_ring_signature 89102300bdccd553ce352ef36bd8cfbdf13f099c02bd18596e3d42e6239bd3d2 66b67ac092566c2a368b9a8d9ca4c4cb6a98f299e5f410c662017950512d6031 116 aacf971f4cb8083e4592930c84c2f7e1e814359c56cf8595064261e3a57062d0 4286968d242a26dfb7144780c2399aa1b000e5c64a7fa136663d9e9129974657 914d0875d0b0ebb1a309ba776dfe0a0e0c3ccb37d57da02a0adff77911fa6cb1 25960a259e5be8f63b901767d00eaff9e3b5b920f6080b957913c065036357b9 f42f1612df591adbbd3075dd4d7e13f632262a2aa4692fb66a9b23f929520ecb 2cc95b8fccad32778dc53e534511749bb98138621eba4b49df2ee579c12970f8 b613d3379b8bc47e960b43003a77dee82913090ef169ec2c24bc1e2e849c6ac8 a2fdff8411483e8012060880b6b2ec460ab8c4092dc88b48bd2111593221a425 3991843e89714483490b27ae31f275df2dbe7e29b0adefd7cd2a985edc5e69e1 b04e1160aef718f8095f6d74b592429a04998db7e3235af0805e2393ab257f47 f56c8839ad45f551557e2f0207ef083f0c5ab8afd19a093106f079304bf1fd8c babf3b67c8a305bb3fb26c0a0cbdbf11bb96df24e36903e5d7931d8acd8a4def e08078fa1b5592ea810f70aa9ec2b5af155d158cfb72a4339b06953d203a98f6 24de47d9b5fb0f83502c076360a5f84ff10fae7c178dcb6899f0e54d04c6f744 bf656a168108aac05e7489c23b7d3aba492ddad0a7305b7a235cda9b5f28d62c 20df1335f52c54cd9a273021327eb3cde3927a2a1c589c0b8fd2bf5305542255 6932a7468388e9361a00b61444b2fb0eaf68d975c554ffe92d301fa54277a9e3 c6878450d4032a0a632133777ba8904cbb2e6034d05d5ccfe183d9189403d913 27780f528549913bcb7d6bcb96e14c6cfacdd4a229135dc06d7c5102219f0110 92a1e11e5d0a1e83ef1aa41bd739c136fe4d3b2585c0d8ad15061d93de77d704 7ca5bca6af948f20929a81e95d75faedc991ba2faccdc3f2d942265d0e60beec 7d0299200ec4b03843d3c9c78d33d9cb871902c2da6e871f5328e1330038991c 95250393ccfb12b976d783e295fe5a0986370b05c48e564c2aa5d5c3e3d49a05 81ca61171b3f4f312a2460fe2f3a9a3d70a4c648a1549ff47ac4f621ec00e8d8 8eb43535fe2ed47605beb83118cde781c3699ec00473a56ed1ebb72bf177b356 03e96f6ec4eea800c6f3ef8b2c43f31160570d383285ecb1738a481cd3d5c335 209433cb2ab66891b1f9dd9fb74a75dcdac2639611f72890bc4c2a90ee0ce990 1c64633169c011513bc0a3c23dbab9ebde0d84ec021815b5efd749122a316d39 aeb62e7eeca2e3d46381c5411a47372bd9ed18db465d99a055a3b47c8be3aa1b 8253bd9c2ed9610658e5e339f2531f19fd147d0782ef9f60917e9d06babb7e68 2728a4782111a52f676bee5c3baa8bfb5c346b560510f38921cad69f9de907ba 12d57f3b4d36edf9656e67cff6dbe2d6bae85ba9a4365d04c066cb9b85eb4c11 e458bfe87a0561429dab19d989c37b09726656c8a399d609559db03680c946d8 e273d0effd007e930b1e9e130b4f87497188c4ed48fedd3bc34d169f83735576 31b09e291aa05bcfd743b3da58eaa02c772b817aff414d469a1f668f41ca908a 2309b29e52ccaec650074476470410a53fe1091621881ed61e30240b7d8e5ad6 f26e0a3ae0c0b5d5c53b4fde3a017c0dfb1cc1e67c7e9201c484256c02fbf584 b2369e479b65aa0e85c6d61e312733724eb93cb2c5b34b088adfca7f61c939c2 bb0e268150f215a3fd664363b2ffda9c7f8cea4c47c1efc43a9118f09bb66cbb 71b08cb319616d98b8ecb176a8d109ca5adfc90e7d88838cbf6a92de3e8c7ecf b2e5781e43a4b2c32d0acd5c373cab979b290928728e9ba476791c4dbdd9be32 fa305e51d4f7c53dda4689c34856a691ae3d6a9d01ee043f2106b4bc3787dda4 fe970cb89108b4e704e492c8db64cacaf564956bb1c47610ee25245a842fb210 336aec71dbefc4e4e44fb1e19239080446d9299b0149e1fa171c48d601a41c8e 554b235718cac2e9355720d4acc8838de199b59ace20cd1ffd575f0f4631841d d3ff0d68b3ff9683e5bfecdb8e8377de4ec37ecb155cf39c4e63dcf94b1ee979 f2521d0a0e9836e48760f0dd9910f229175740e2c81ecdd62e3859a0f4e0c7ff 66b07816d226538b578bb1df23e408d754a8a07f3ca5b6374ec386c5134d2697 ca59cba4793221d8430c41eb4ef32de270d0c89eca0b858b420a57696eb30be5 c3c213d1c7effc9e43a51d247f0fda635055491dc26a23201ea60758a37feac7 cb0f2314b2ad60b4230957199a9de58698da7811f47847ca973102a66f0d9498 c928cf24971c0890606fdb0b56f17b9409595764e7e398a2e729ee7838b0de64 b189e86e14c4ca5ae8b4cb715feaac84b921f3bed879deac20c9b27799176d88 65e86d5133b1d43edb8213bd3bfa7babc6e23628e52e36cdec478d39759cd6a7 3407636c7c04d71490e9edb5839531aa4724402b3c663ba22857af6daf6c9353 3445879109faedda39c091848b3b49cdbf61ca6e95ffdde56bcfa28da6d9d27d de4c9c94c1a01d5d177b47355118f70a68e7cea90d162a4ab67d7f8b8be582c4 cd847f96764801152575a86b30e6ed3a75acedbfdb16a5576d3a094342f599bf 6890ecec3024edd3be7e67817ce51b8de27b7a4feb288d8c4aaf6c91e5e909ad 66e74b36d7939f347f0acd6699214d4a78a016d9c2ca512a6f6112fc4be6bf91 a582a1c2948b238fc1de90feb779b9d11e57b06410ef58e74f565b6e3dec4fd2 1d58c0007cbd13072dc8aca24abb0b6cccf238b47c897a1010a300cae267c021 a309f20016a70430700d55c29521f51dcbb9491ec7a7be3666a0183f7b5cc032 23c15ac582b1d2443eb5b4e1aab30a44f108491057a87064a1784eb64023e96b 49796b6bfbbcc45aee369a4cd875e73b43fc6e108ee97d813dbb82f4227cd821 008de8724f40b50fb90cc68e804d395de075bf3f40888741c98fcf0df2cff0a5 c974385a5536e2e7018885cf4efe028760084ba063279d848293ebf7cfb5cd90 827792ebb3a8a1283d94fb063877561a4e92c709e9541b7c87817b28ed185696 2fe3aa80686718b9739256c1a433a37109c903f0be3abb74819cabf35e0c1bb7 e93816be20d1dcdf6ee39748a1f08e06f8f39490991d3058abbda941329c3be8 ee1b391beb2319d8f67e7376e68c069c0011ccf712f5351a82c13552cc890047 eda56aa82c4e114c8aca9636db3b356e7ad9c76c63acfd1ee135d3a2ec3393ac 0bf5483e4bdcf85336f82b77f70b1623d201ff601877bbec4b7b4963268ab62e cd4567b648634eb4ac39958e55ef352570122929639894f31abc61adc2609fdd 0ad0e927718cf2b4eca2e50e0d27d99c75cb7cfd33fd63b7fa7ae38387d92e12 6735f7d46c51519a4a2d475899fcae2f47e08c91a08d28faf81ae21611548f64 210e26da394508be17607c6d23d08e6d93ad5009f0677228c754d212a8e084ec 70bf02ec4ffebbce2020b5db776c6eb463b24989f1f3f8aa2e1bca91f2a4e315 498b3093b6563f383502e489fde1cc1201acdfd6fe776bfd68f674c4e4a2e8a6 8e3a7e23a5cb28de1bd8e28ba5d87a1f1f234fa405dfb5f9206dcc1df31378e7 9dc0a522c312cc2a6c95d954ded0023fa95e049848cbd06cfa85798b91d9b96a e84f3f5612a23c4399389dfd2ed6972e6744b860a8f110ea83ebd77a3a7f632a 670b5b7c96794bf0300575edadeb40dc40f30c52ab560f55a8f7219c2c7be945 b97fdaf538c771ef870edac92c52b266eddf2f41a662c3b812483423f1172f32 34fcb11256ef3dc85e4100bdcecd84e89b64d9c39da22b7f7383ff1b172f0327 0d735c44c9c7c5502d4b4b280fba770b284e2eae7f9446d0964bb6ff7b4e2f92 08f03cdfdeb880a685504b775045779c86135aa6dcfc8e06899bda0391141946 865c08b9ca9748b72018189051fb1282dcd1662aef708c62593dce09981a45fe 647f241c0da6d5e897f96f709e4c5e0ee7db1a90026e297b2b98739662599656 b424e2d17bad69bee7e51c8c8c017a3689b4cdf52cd016af5e65e7084b3c380c 671455db63d8773c5e95e71d37c3bf7d3e6316a39cb2fe7d234899011458c83e 28119b556f4048b9b31e7946073be2b62f74b74a56cbd31fee1cfbc2f5f512a5 a8e89c237d06199bcd94c030f5c58ad1a3caa2ff5bda6d45de213262d038cd03 4d6ae40c4e28276d908f10fac7db8fb0954d0b09f3d516cb61bd9bf9b5df9fe1 7fccc1bfba50e840563c220d2520c9f77afef38d9dfab6e01892e6345ea100e2 28e363ec69911e6cd79ef7ec1c6899a531ca6aa1b00d00f68d71cfc038043847 21dc113dbc94fad84468f32b6b654976e9e2b7ba9caa011fc46394cc0efffab4 513c7e81377e13bd283918a5a44a5ea8bb253f54d554fa760e04523c27978471 f121a815f969fc0597b1b5c518cd49ba4589394fc511633cfeaf0554a33e5bf6 19b4883add93f293a640f138d0599d8abd50027f605f68e78e398568ea444647 ff453b1975ed16f8c0e1411dfde72046542032a9e753b4cf9bdce4cff141b734 4d39889a38efe6d22a0d3569d79b2d5d27a2caa9d40f8d4232db0ceae78c4980 471dd89be76b7673c0948e9dee567ba70acf98d02ef82b8ebcb73dba23a25aed 21a96e9e7fed5e8301c71774469297fa1d0e095ba317b916501070d18be35eff 1529c4c48b69e33c78314005df30b4e24f25341538b4e4784187f8492a95ca4c dd5756c71342bc91146a6d89dd9a1755b2e67297dd08ee2ad47da6f88712cfde e223d3cf9f77cb85b9443da601d925305edea5d1f86b69db533edc032dec4132 9df21b3f335c553a66f4c6b1dd7ac870dc885de1b3323e86dffb116d3012a430 c545e90cde0ea37de654dc7f6cab3042b9c53ebb923580740bad667891f34556 cf75bf6c505a45d0bb8b966e4fd8fcdb60caf6998310f13ef23d48e9f8da5f2c daa6ca75877e1e7eb9c9a1aa990f850d3748ed84dc65be4ef4509ac2dc15a50a 93d1f3b4a03ece48c32a6e8f2ca589953ea4eed5c53798a388e51f9a6a5b9e57 0e41584b69979e002262d1cca78e9ee773e22a4458ebaa53de795a9885051fae 4aba7f5eb551ee6c25d9290c10ec73cc2d1fbb0fb3646f2c3d2959e620d6543f 6e0fea29e401d70f40f8be4d865f7db6add2fab1ab82090f3b85e53bdf9a6cb9 743899b36c46807eae625a8fcb717ae77e7119dd436a6cfc11a7876a1201309e f8fc487e376e13bba04de3825648ddfe435e78b4bfebb43e5b0d40a33859a500 6 0b8026574ccbba296193f1ca37ad91ab5ed182d556aa674ba2449f2627bcda0c6dc6f1dabc8796ad1690235c2d3784414be879838aa68c7f3deb69d6c00f000891d847f551e4b81c697b6fbec00953fcef9062cfe894b070064a5d7deff78d08445b922062643e71ad988f4b34dadf5972345bc8b36697021b9b8868de765a06b6c8b5dd51ffeebae285546da26165e8c096cc90882c34f770552854e143d402c5d7f5c26285cecb7b3166b4dc5aca3462ac707a5229636339c26bdbdccbf40de5ef3fde8afe304d7a2aaa835b767f7cf0d669bcdd476bf3224355b622af3e0cc8759050ded35ed5076d552e4adf9348b56d2b027b5f2b27f127e99fcb77ec0b0a2c9f8621d8326d6b629c8480c748fa989dcb0ac5f448b7b69f8dc32628ca0ebb967d66aca42fd9662f9b35b541fa6e795a4459e8037c839f08f4acab9cae038c2b9c9cc878d358f95f10b179e1bbfb26d164ff665f4bd7f8de47f19ec45f03bd669aaa546df1756350eae70de45e4f66cdf3363bd4bb07e50be2b5223f92014f7e82cb599377a56a2dc6622be8e940df1458d4cb5cd08c839d34a86b86a90e4bab45b770e699968e44c32ca24ab4719e77221c1d5b186d816b4f8ce34dbd029e472426cca9e9a72f773dc248a9ac58433cfa7752a431cf1d795a1e661d4a04ad9404068c21d6cbfa11058fa46ed4682ab4700e0afb893d1973c4a13624cf02e128a4730d146a4f894d1441f3b9fcb0721695bda019ace0a24d53bc19d82e0ba579adb76bd7837ce7243a0b46160862a729e73806fcddb4543e8c06ba88150a7cdfae57aa221fd68313cd9209947318324f7b3e55a11cb9a26602168ffd0100256de511cdb926b31868e1c9f9221f2abb4c799b8c1e1bf9cf67338b9e8c690763eed483e67dc7b9921d1c89de3af49e2dde297c06cd3d5464b58ec9db90600ccb892367604a44f4bb8872f116df58ae643771b357866874937de0d7bc26810efad047ec17247fa1f94bd5d7c8538e126ce3b60a5e89ea434fe879fb07447a026a4ea6a569c0dee9f677473c89950ebd2324839deaf287be71f3fcb450912b0a5cf752193bc2e19ff9044732eb4c46a6f5c1a1d25a2a1c281bb8868732fa800e36443f3ec420b06d04ae628dffc8bd50d0887c5d79f4f84da5a5fe4385353f09e72a36d717ee34a23e5025c20f7ecae38b0092ec721e65838ba575164298ab041846a38162ba033526ad1f90e9a3c05d07c3d8dba8565122b3a99eca239fa30d9e9c618bba7bd6fd0098fcdbcd2e9f215eeca594b2d670b3fbde8081f146eb0a8a28f1750a980b595810f85c5f3f9654d167d71a703c20e8b2ef93c92ca9980a2316e37ddcecf447431b594f6dbce33c4b580f100b3450e08fc8e81a0bc6b403163584d050ac517fc2baffaa5c48d5b673f1b8574c745861f2774b04c5665d0cea4a3c6729236227d5ea8ecdf624579ff8abb604404ddc0f9a81c5aa3e0c3607025106ca3abc098c34a83945d020a0237fad8c73201f3dd751b282b4c3f62302ece2d7da870c5d88643a7eb4d9c9b8cc93d86c4774ce0c583ed98c71c8937903945294478a131f7b2fb66b5f6b3cf4a2c46fdf89e39219c493b4506987694400355d54afd091e0f6eece93deb42e5e513d569fc1f2a97722d9051e0374f9c80490096611769068f4e97b2416b7e1cabc9ec26e34f134c900539266afd1915d0408532b389b8eabc63af405bbf5971a3193b759aa2ec253d4a82de84ad487320271afe8dd2c60d07221f412f2be29a0779f7009a0bc6f62eb7a87bea43bdb3e0a1ebdae7ed155a0d36fe41374c8923d9fdf6b70dd823063b08dc1012d803ea10a49305de83002b6027b0870647bb15c084c944e403e8a1ea638b29ebdb0fdf50509eb7b4aa6fea61e0300085682add4f1e63b10047e0e66ebaa4e9d73a47b380c180c8c318dc111b57893078c35663532693591bbebfa1064ec9614051c6b62091dba74033baf6534c26201aa3ee53f008122de11dec0c7961957c775137b6801ae45151697be0942c015c17b0026c14be13b9284b57baa749f1b4ce250c3680e2027823eaa1bef2ecd308c43d6afe849bdf020ef0279956189a52a880871bd03a311585e4e9373a4d7d9d5b0bb056e24e2b18c473cfddc6dc8f3bf1147295f0302a999e2c615dc1aaa24e254f6bc36f04d58ef686ebd201b5dbd03df0655b20dcecdd14451a116deff2edf2c11f32e94068372c8cd1630c486089cdb2e18050e3cff40c25a3f70d9c53237a20c4507c65dfffda88e4c422b5235d25a0b2a050ad1dc55126173b506c8e7fc92a6a6c9ebb4d1b8d41ff825827d311d4715163a083e01a141aabe248cbec1ff118c93ff930e2b1fccd590d6134ad4b5f3020ab406ec8dc82f6f0763b40c04705f322639f6ae44dd3c44c3bf83f6fb777110880e00336bcfa9675938990fb060cb7d668f9c3ee5bba02ffa6a672beb6e42c441f3080e361b068a1aa4a9292ebd37aad580e181cdd511355d039190491a7ffb70870d0d128f09f91e837f4b712fc4844a5ec90935b550645b4fc17cc8a03ea2c39f0b956b81a7260317cf7e39b3bf61fbcb68e497fa045831e143b0aa80aceada8b0ea7f3c20f7489305598e24403ff1a1ddc5481bb6bd77e2c6fad8a0c59846afa04943b4e22d05bc79cd58c81816011f6945b34c1b1d56f22751e6fe9b22ab3570bbaa850caf74561fa5b7715fbbfd9ae7adede713cc5b2a864cd2b397430c52c0d0c6b4a9191aa9791e25f56cbf26c48e3aea68352e9950a6343ca396b1ebe8704f5892cf44957dc513c7004e819620869cd40bd1f3c1e544a87f91d71e06f2f05716b742fd086ed3e7f58d9b2c09001762a96e3b7e7daa9bfd7b7bcf4ee722f0302cd9d25af1aea9cf4247c7c749280d6ab934a9d1247e978d3d3269e22e16c069ebed3661f043a257242969700885965f99b34444aa31accaa22ca51b19f010446012d08b61841b1940a2ecf0f9ddf1605109e6fee9a77bf81c16184722c3e059c49020686d6d63537fbdeb4ccdcd52ee5c74ce635fe6e8314bedeac4899cc0b46696f07422e14ec73b0e9517bbcd9ab4d5c300bdfd5243bc51ac4b98148020fb77b95c55470d9eb6d98ffe9a785f34ccb0c0e669027fa3578ad3643e9e0590d2c4a0631ef7beabcce3bea977686302702d629422d0f2efa8901acc221ea0709660e50274992c65cec4b9e9e8083a135262741845fbf8879b7ecc59659d5c90a096fe4c7f6bc15c715501f970a309a6ae9b302bd4303457e5d3db366e6910200cf685719c94311c9bd9010c2fe29d4bc7dac6610f41dc65570feafbd1e602a0df8820262a6ab3bdb886b1270abd83ea27540538a791dbab3618f49f2248ce60966e6643b251b214cdb2c8769556dc9e87ffc74cb9ee70911692249b72b863a0fa8d80c39e6fe9fb98c526ab6dc71f6b076aa58758f78a3f1a8d34229ed99630325aa4e1fe57586132cf310fd250501a16a93d5fa4e96927989d65c42ac92b40849f5a5a5496806d3deb5c1764dc1a8e79006e5f96daba8756de10757c5fede006a5aca2408dd58f40a4347d0cec2e0c7cc073038d54eeb6566e77f0d969f3c0a58526f0fe78355f8e21fd37dbc9005c91a6040a9f231251da4ad375ef3467307b93863bdbf06a4e6536024492654206804132eec96fab2468a2674d8df5918005776c7ed12a9c2b9d3ba0da860fb35376a5b6c55e8b59bb26e26ed1f94edcd0486a8561e2fc910c4cb9a7c348a028b2d167b2acc54eb4d243db78c686bae720cbaf7990c189b4125039450f14403b318de0416e85b78e0f4aa2003efaf1bbe0818fe1cf1ef04059288e527be172f4f154e60fec10084f577775d03d8d0911a0dcf08cf595b04d52d3f82925f8e6808eb60781a3ebc1c877a8108798c6e4be40b76b3b743494bdbe1a28e5a42d256a91f7859b6ed43a3c4ff7722f191fc1c7b0ecfd0adee13dc3ea2fd79780159fb1c096154ef9ea3bb02aad18851d8f870a10d50530d5c1b50c1fcfc784350137eacc142220fa028419230e00ba4354d916204173fda0603a66f55c672c0155d049828ea04dfc387dfab5d5f9d331e1c4513091986d1a8a74fa3310b733c01ed012db2315a6dc68614d2135f1eeae13ff872027a6ba1a177de6d619f32bbfa41ac2e0f9c66fc5a565d717b40b4b0eae5649500a067a4d901b99011f862274de2c0fb6f8d3b450340920750a6b9f1eb5a29a70fc6d7512ab7c02147da9dd5512d4f4adddb5620ed8e356f34b5750aef2d309a02241ff73998ec9cdfa9709573b6f7b00f6252664770a90644aace5bc2e19472054a78e9f9e18d4a29d972db99875ad3630f8b8e9a947bf40da4ec1350a352d401f4d53782615dca11a635391692bffd0f4309411ad9b206a3c5c9adce5f57cc0698201351fb4ffc8230abccc674d3cd8dbbd959ccd8b77fb546634c028713b80af4d033fe66a787037fcf38e23b84c8f48b0d5f8a48c24b1992bb82f3c5cb1c0f0f1446f3ac899ad343c187ec29eef6328d6bd4a07c6636b5349faac50facd906f7ca340c935e677e08682f39fdc48da4d8cdbdab86ce696e3bb9458fd40d310b7e06f49e51e729ae2e705ce1b5cd7d0fdeebb8da7dae9ec721c77abf0fee2f05b051f9feb49496fdded0f87f2de1186773cb5fbf3f32f9de67aded3f2deab60469584cb1d1f696c5e1a994f19aaf5596d9cc7193f908a2d34391bee6f4711d02b093ec04e1a3b796798f1a65a656f38c43f851236eea699e610a87df289b42026e9eb4c5263d3693df9458ae32c85e0e778bf7dcad72cf194101dc2aac61540b8815ae39f5dce41cebfcf4f2ab4feaa55406c6db38d3ed6c2538c55f417c2f0d1fe2a0df06dd3351936e7ec94bbcfebe0476a4ebe090c7f34c3712f13eee0906b3918da84b9520b5c889c0924239afdafe9f49a39e8f40af461c8fdb6623ed0aee010f345fa58bd1442fcbb177cc23aeaa9a9c80aa311f92cd6754ceb3598409c071c7557df871a5bd8efd55a77a7fed0a2c2f0b4c4ead01769eac1825b8d40556f8609669c8d04cab252a288048beca6dbe72df33945f0b436d20740c3abb0548bbd93ba41cc3e5baf1fd825aed918eca542b21af05e284fa5d1940f5d95e01cb226242af359ed089f607ed380d46e947058228c818122851f880ab8e7171088bfe7206f84c6dc247981abeaa724ab4c34a21e9557ecf8ff6baeabe6991470b06365d278c4ac03fc5e9bc4a06f26acdc12294fc2bb7447d2d2bfc4ca058c707a26332713bf61117edfca06aa7110b9d036ff090c32556d671d0b4011d139801affcd2b3552ebb196f01210bc97c59b5783242a8b0bbbb046a05310e7bb2850e7f59eda6bb5216d9ed9187dc9536e51b8cc7a1b41c805e652eda91964631060622cb5b2e464bd5ada60d57952e787e2d535df78c812088d088b7f14c6df7df0ac69469c699541ae3863e80bbaa2ff686cd5d2ebafed1aa85c3fd1f5de51ae30d637a4c0f64840388b4b4df37b38f77bc22e9e37a8c03e027b748f0527e30af07a00b09c1cb6b59259f247db4e4c00524831d6d90f433cdbd3a37e7d6025ab200256c9411d1c1e1d688563a84f4fce67b5e11123f116dd78687baa72fff53100693d284374489d97bc23a32f52de697a219f945ef9d27d48c4c75275758e37e0aa8067054df6a037fb51329942667df79958c66512bfdce8ddf6be7b68c88be0ab509da2b26304cf0b9d3b50d02c2102266dbb90e1be618c9c61e9efc151099050b20d3818c5897615a9c99fe42de4c3002c91b3cdc456a825a0cc7df3ae6d503edefccaee3f2953ee2a4823c97dc1184f16f6780059dec788b502458795b4c04ad511550d60312a882177660adbe84ec7827c8313bfd67de62df338f391a13032ceb01b49d9a107c23e5a789eac36b77f900f66cf096093595ffabd43a6b9f0d68c2219740b0549fa2f1662b33f4999a542fee6aa7a3426c8bfdc862a61435013c298cab85f68773df3ca2d58c872c7d376db95faba7a910f1f7757f24618e046c299c43ce8c2977b96757063844cb76d2ad209badca2d2b701439704979ef078c02ffce66f8e49f6123b56cd83a9e645c882f5002fd268aa30ca99260258102efedc6e7529c0f3119bc75b1c4190b9cad55d4dbd4c2cbba314215d8762e6d005b1b5104e008331497c958512cd5a11a0e4209eca49b13862b25eeac38e403038acc74c8d9cd65ac2a5615ce141e156d32bb14620cc151a0aa2d798be6668901bfd8ea74df2a427e3d04560d3f5a82ed25773bd080f340cb7e34daa3ef631b0e24c1cdee1be4a9cec177f3b2d059d620c48729d53bcc7c26f7afdf64a3a0ca0c29949e2ed5c20585182ea370e5f2ba552c503b75211ae01aa44b3bc29fdf7c06213ab3844afdc91f09e598a5f08a2961758570c06f4e447d27307908016e51005c58bc9dc0affcceac613129029eca50059b527395aba3dbc678ace92de6b409c9f4cb5fe59d3db5aa8975a6bf44bd52a90f31bc8a6dd03e53e42c6957cee101551a54ac6e216bbf1a7b0e26186257975b7c727201eb1eca65a8e024f6aaa301f773b438c69b1bbf8b280ae10597a4c7657521d699c70d63019a0a708777fc0e55166dfe0bfb1261e96a433b6da50c7e31e2d698599ddff6da02c6576f4bfa06fa2d834d299057616c4a98871313607f46fc0a1f1e419250582a885e75aa110c70a69066771cd2374b5f5ee40f2cec562d603f77f0d7aba9324ea584cf40fd04292e5db9aebc3e124174a57ea393c2f5a3f152a6fd83531b267216bd225a1706480b6320a639aca669faea87a5b95a8349f47a362620db4da044b2bac38d530d45605992eeee058fa956a79a9a22423ccb959aa4e0542c5e27814b3b9072a500f88e4579aa80e453528f22157cfa4fc90d2399169a00db3c054096813e024a04a358cd176ae873af48447eba47c679e01c292d17d6a743c4c65dd58537bbe40c584a8841d1334cf4a011d713114904ecf79d29690fd474e02e02a04886a08f051ca5a5a1423d16d952c6896187c66f809ae4eef226c0096d352724d69f76ab01e694e178bb95c20fe9f4d32a0166c2d12c7036e4b0f418354b0484e41ede20075347c11f7f2e88f9a017beef6bbe803bc9b1ec967e4f29b13323532fab2e8b032318c6d362e1926ceb1d1bd102e02e9022e44f4e09bbe64f6c84f454a7bca00563236ddc08ca12c1185cddd8900aeb1615dda54ca2b361cc15e2f91ffdce1f0715f16ff14eeac60fef63ffeb0f7859963ed8a1f58f18414e932316cad38229060746a62be7047187e78841e9b534d58077d972d6253e8a4c95cffd63932ea30a391100bb7599e3a5763544db299b6d84a6201dbf19c67d8b3a1ce167f9f69b0229e1568749e08ffdeb71ec520675d24a8d935118a4ceae340b0a7ac8cc0fd90b07bd9420bf2f79baf2c835300e4ca929f3b1221e210b1b03aaded917edfba4073f5c55d23f560e23815f3c190de64b1fee1027da74d36e1a166f043f8f492f07ef3c93544a02cf1e6ac713a1a33002ed2a7d847cf61e8cf990a72d31a66fb20244bd91dbf2d8d0b4ed5daa02a68c166235eaf29ef8da8403a63483137afad90682cb208b05f6a3ea93dbf61887b770daf247b1b13622299193b20fd313985f0e7f15f3f9c8000b257589217a445df19e2fdcd71cd01fec01ad71d79cc34eb70dc955c996f1d1a1ef973a009d28c4ce69d7b00d4b39cf17e9820286c8ad992206f9b268a512a5b909ee087fbc25516a14196ee12e88d7b3119fc4cd3c713dd20289247912b66dead861ad7cd73bb24c29b6b841ce7f81d8d1d87f4fa42e4ac0087073290c46b78366fb297c4a6fc0c3122368e67a94614dd7ee079afe9a2f620961ce78f19c11003f734bf48df0eec2cba8190c98bb7f40a9eeade6d2037ebc0dd21b41ba2fe453862bef3be436a0fab704144d85ce0a9204e36363339baa4507230b6c76b4401ab7871b63f4ef73d568f874ed6c89e21cc62822098141e16f0fd8f136d4464f016ec7abfa58cbc4e604e3d425e3adf2a94ed6cd1392d56f2704b14c86023284c029c04f3d2cf0fa2c4b57980e4a0f56cc5ce2191dd3068cfc0e8d97eecedaf9497470064cc0ce125b9b1e1bacde5a9587fdd97a6bf54fd3230b5523a3b5d85b3393b584b0c7ba2dbd6bc39f799de6d4130a4c830ac98c0c15083db6baf4e2a7f2df5440003b4e42a4e254492387b3de04890bdb7e3ef428c401322bd14ff8d538377d1800fb39623441aa01b9eb22a8c649b20614d04b591c0b2bce9770ea16458865649dc9e0fb34c1ec8c2db7cfbde9de58d3257c8953d00d66ddee0faed1397a7365d3c5d8c26edf42da2e7a64000e4d2f6e17d38d4b3d0b2fa196680e30856f7ba26055cb8db94f1f959f0b0c0e6c03fa6768b1f630e404768b1a88ee8bcd1863a044710449321166c9b5655c217306ad619e873b153b0ccf61eefff7482121646c59ef2973dcc57fa17560588500609e6da786e3e2350e923c7945ae5e9fa99315bbb6c99d294b42ffe48dc3bfcbf5a0b82cf4c2ee2607534bddca825e05f452b1b6412bb05a98363240aece15a731815d6f0f58dbb50b13cd03e2bbc9d2a6a9163c897e5dfa0a9c2e5f9052a64947891598dfd76c050a04cc7e2b6a2433344c4d2f0fa0bb38e7bcccb0ddee095a79371a3482c6622900816fe80f7ceeb7eab85876196c4925f04a192fe59175281f70c2a98afebf230049e6058938c78fafce6fa11e77b5e095f4740a99ad4d61e270bcea34b2a3c60d5bf79ef4c99a97fd6cc9a6db88d23a0b0c6e075b983babb8e33a5cd1295314086042bebeaeeffafe09343825867243fff629fbfa5e43dd8c814d83b041394501f76517f7829ba6b27ade1da765539299cc3ec55aae8cadb9f388d11f5e46540b50c1253f2a74ec837ba6f0c889130a5fadee2c24aaac92b7dd7c96cf3d4764076757092486c32d401921756faf0ae54f671806f57baf20c7504ec27fc840090a345c46de1f0a213093e4feed0c2474e8d00751533eb38f21740f90a532cbab0938aeb2eab72983201e67fb8cfba1522a380c2f7e52c5ebf84d480312a305fb0267a32d20824dbce4d04c5bc81c962417ec834df5b5a2a20b9a64f312398ebe084c1fa73c8c4748a7bece777ba89e5289a266edb0f190c0eb204a2fc00638150eeb167782f024f5bd2a675be480742265b5ecb2f0c7cb89ccb5e5302b6110b801e998f336f53fb681e6024940431dfc8f0760be8c0dac0f4573e2d4aa80a0b50012b36b36a95edfdb7172b4c12586f2e90f0ef029ad46d351879758dac532200d011093124331d057ae84a3054399cdd872c39170944434b270730c09d6b2360680d668677be702c0c4b14117d7a19051a950f20b8c742adc3eb29c2fb4883e0544291dc87e1397ad049d446638b55af62efad0585257be515bfc9f1a290f5f0cc6f20ecc2908782197af37edf432effc87bbde671438723f7da2e1133a7b95015b645c4a02a4aba14c370b23cc006a2ab5132f83edd6c59c7a633631b0279e0464741eb9e7d97234e4d859ca4fa9e828c502af81365d5895a45955ad814ec8036121bb5395aec20006b840b4b45bc2037e9a5c7d319923cd64059cd21139d80dedeafdab448b13bbf0ce43a0c51be7072e5d62a91354f5995644b88492723505cf077984d1994785d6acc1854941849b926995617de6aa21ffe87b4c0e897802e1600678cf650c009cf9279077202b4aa01f3534194af2f2299b426205a1a0030390f0eccbed460d9ff6961c475206db0339323d6af2665c97c56f4574a8a905bf80a7b8c169457211ab0ea0fc31b71fecca1ebbc2db659a3549e84f519d2a005d33fbe158caf318e557f39228c8ab0efedd6a6dce9e347fcb90b0d213b1c807a990953495688d41396f238720d2f942873e42bb2ff280298481f0e4f3f8c80f97a6669d384ce3f333f3f71e0d591f107266cf46388eba3f4d8b8bbe9e6ca804dbf782bff51a998792191cf6048add724e3c620083401b09c59104e482f759012e81e47bbeb8d6b4001cc272ec43248f3a03f77ca61a9a813d4b446be5ac750cd9662c7e398b7862de1dc0e9571394bf5ee5a3cd905f18a58afdbe5555cdf401ae6ca5da84581e9bc99393b68db21a6dde189a8fde88d89cbdb8a78be85f290bbb652b01b3fd963eea02d3c6fc92e9778d585a927da38fcee52db599b74e86014ed68a16b58be7b05b4827d65edfa0d52926b3074a3bd033ecd4bb11a5a2640122470a79aba24bd5ac5ad7dd1363403aa6d08ca05e3049e791f633015b6e6907299eee090468dce011cc5831e50b854ac27dee76168c481a6d5f5f343257ec0823517da298103681ee0b76eb36fa9546e6fdbe53c1f30470e8dc8d782c10f20e1f98c739d022bcaa1f3e9d39447368747b058b18c1ec82dfef49fa46f5a3e804 -generate_ring_signature 39fef726944368533d166d84ca3f2ed35aca6b33e5a3ddcbde30f1a60465c61b ba1907948b591f4d5e7faf586b3f95d9d259ec089ba3ab71ef4b57beb3e5830e 4 cfc47ad6292db3248c65c78fd2db49eea2c2a6ff4c6f50771decac7631c4f866 4454dc103cf1ca4cfba718e2d34dd2489b08e0b350523dc9477dea5f16743e73 74d30aed08a71ab71277adfc83d8e0de28b2eba61edb0e94acce6309973b6d45 2aefe7ed1319282d5e1e01220850c933406384c6a931b0acdcf4957de3891eac fca4d6c4cc291b9db974d09df386a7742740068d0db8e96b1b4faede19f06e01 3 0601bb0d7875d6d59436c781c80b0f563f77445936ea7bfa99c9b202b6fc7e0b18594363d1e66674a09147cd3c0602923d38c6e59ae3015ce767ea7ff30f4c0adb30d9a00c10c1fcdacd8bdde252e42bb633e8cf1e802f37908ee2c6ae05f50a8bf771ea62e4ab6c691f65d66cbfc23cf18e7664f40e9a81b9cbb856cb33cf0f51926a1e0446b105f8387e8a80eee286b643c3ad48b2702164055d529ddb9504d4a44c6f357a21edf9e0b6c6d1c9a2f50aa700c0e9f448b1cb37d6be73263f09eb288235e120d907ffe49f0737c0f2b7dde85f9799374bc02ce177d3f824f00009af09dd5398e4ded6351f9b051cf675f3b51918009824188a58d112474a2a05 -generate_ring_signature 7f0c29493bb36b01a82a2f7077d547890e5bc7831ae6a35a2f31a0f902d4854a da08db64452bb864c36174d98a4e14781c78b7be3ff474071c0afc444fec84d6 1 bcf34b4ed58ed15b02861434750fb724d3101346d5e8303437f56bcd5eaa5c8c bfe6a1b8cb2a570d84f6df232db97b7a8e4fa3dd8e6af54d09fef6aa6f67d305 0 38a871342fdc49ee6df8bb03917f67018139515d6bcdeac2846b174f4e8eb10f4dcc59224258c944cd85720d61c024af48d2b85e1be302f9b00a706250c93801 -generate_ring_signature dbde6983e1560c26fba2a47c5e5d55dc182a12985b43766992a1e1f119947126 bd9bd3dba5bf6a2d3e986daf25c2cad584ae7c3afa7c27f5a98bc220ed0378ad 14 0f89f5a886f94ae576b8e5d8b947f0e1c7430d6187b9cf04c718ef3e99d945e4 d3e5b4d8755f61d6ef6943f8ba572fca1041346eeabaaaf4d53b6820cda40b3b ef305918a04cf633ab888ed026dafce004b79e96ec7bdbed5ed73e7daa14f5d7 bca88a261ca9d4ac8b51c8880ec327b1893d829605be97a299e9f29998a90d42 aa06e88145690e0e77549fac710a5c2279c1cb330203a88efc44dcc81465ed19 a2b43e260b1ad8a55ee794ed03fad1cd4a7a80371fa5799451593dbd4d4c7685 2fcedda6848e5af9ddbb3c94e181c8153a6229dbc896098e1dd4892f4d4db92a 117ea92dd03c8e8d8b54d680f30ae97884be678c4a868e672c5f78de1012f66d 20b0511d897bcf5c00b6d601dadd3bc7a879fda5fd41644dc07015fb95de9a13 38ea2cf665769f2661fa7e08165a9bf94cb807ef946d2cae10fb0cf314919620 d7d6c773b3294e5aa5436ed9c61ed5f03213fab5b30b83ddd0e7c12a412533d1 de39e3972890dd3e175e30880c96f497e180070bb64d726b968970d2e3be7ff4 fec305745ffcfb15d1e52cb29377b9a029b555b6a4470f0b8165e32d7bc8afcd 623eac57d7111b44d17da4b98c585b59eace5b904538060f2e4527b2f08d7d96 7817e9be56a20810243bf96eed3585470b75dfb717ccd52bed820321c9999e0e 13 6d39802bd6e4368217b7c660085b2a73fc81025606f0b7fbae61d078bbbb9a01996de5f69ab332095c3f69fa953133419e4e27c321ed8172f66a2aa28e9fa0029b8d89ceda19207d321a6babb00b14ba41c0f90556e6d528b40e87bcce10a803d208267c873dbdd050c6236dca754d516630b964903df0a9bdb1c16b74ebba03abd556a0ce6c12d0cc7a0aca849b9c2bf51a54c721257e1cae3b6c5091e9e00a2e0fcc498465126d4be33eb978c8bba17ead82de93212aa597eb70de5fcc920f6947781427e35107a86b4140807a11da8f8972af5ab00c0143a898d2e83a640d68b9c7267b29efefbd5aa6dba5d69552aaba10af6b00e28a448f86bf1b95bc080a1bcb6dc37b990723e056422e2897ca1b47005160b0c4f9896642526dba8a038bce61eabb2945ae1165ec98a7456dd91a89fc2647ad3d83c78a0db98fdeef02d8dfe1a2a69ead13134faeea677a5dc60056b979538758736e4c22be4d1c0b027dbc2f9780a49ec94860aa0fc0fd06a88df8c79c1e81b802e793b38da9ae8b066da8cd15937fcfedcc111a355cef7fcac20859b4e020660cc1d568290ab8880e1ebb71904a36f1fef6f19873a989b7c73ec96dee503c2ba3c5af2b81d41f5f08ee2c001ff5d1e278ac5584fdf4568fc29932e66e4b3fd36b441a8ff3a265e108d6cf11dff6571af308637cd7dd4143489760cd5e988c5de2dca986601368a50a6728bb4b4ec22ce0107d733923f74cfc6a3ad8b02946365f9945018f95460e01bc4c4d47cb1c21526b21ef7d855eb4aa5916cd514a73e7db3e02ef9c260b46001dbfac6929af65ab9dc15ef98777936d5fbe345977dcb7f14a65a2c130914b042d6f28c4cad41cec7a58cd817525c22236e1ac903f745c28a034eede566e5200197f53bd9d85170e47e09dbd33d44e9142c15d02b975271b08d7ea4f701d940e65b9181698cb3a9de7a119ad84468d2f496487cd5dd94bf0dbaf6260569f0a0735270f8632614d89a67666784cafb467c664ae0b31e27d4dca9ee596e05e05034988369abe99431eb9151b98edd9b52d7aeaaf58cc0f0a88483dc06b769985051532c1f167b0012b6520c997886c1941c54cdf7bad5c411345eca8d0dd7c9103999e154cc35456b21fa753d270edde105609432c546de1f38790ea4ab6b49c09d6d7105b5c6a473f20924f4fdb6442643a2d2d4fa474959064a35cc64e90210cb62f27656cb2bf8a82c1be2852467a8640e23c8a3399d98b157132a3b104bb0c -generate_ring_signature 4a049f0f8bf7379759bafc11f3e9326258df0ac45b612ad5088bef00d28ddf03 d4eced528b619b2c7e3478ca4325d630c3e79822e59327310e1e58f2a5bf46a1 1 8e1cd22c64de56e319bddfd26af3e8a6213dabdd844ced186eba1fead7172564 ec5b99eb1d24eaf612b507786695acdf1d15372e3acf311457d7197159059d0d 0 2677efcac75a0c9d3e07a23712994352751fd133769e8c7589bcbc00c7495b0b19242104115260fe9cd7eded79a9c7d95414a83317822fc1a12741cb5304df0a -generate_ring_signature cc597b17e1ff8657ef92c92504a0a1638229a7960b286503597687714d90c0be 55ca58a01832cfe103ee846f2a11e237bd11e22a4baebed2548e75ceca9b2eec 11 02c74957d08764bb2dce5202466669f53b66570d75afaf9d6394bdf325be7c93 7aef7a7f9f34bd0716ea1483a0a4372b437ecdf14874b98455ebf4f6ea4a2314 affa26c8824fc326e2024d05c1286ad0797e4921c3c75009f80f965ad0b6061e e9946e75782aa240166ec2d39b2232303b0f79774d2a9a08310b9336eeec17d3 a7c3963b8e1784fd9004e58fc205b2de95f71d24ccb474cab1cdb15080931520 80a6a8dfde031883f53bf598dc1c47061487b20f393d0a630f0b15fbc71449d3 d9583c14e71f5d28c474e25fbd43336c5542ee1a4ce603f5299e0273f889f9d3 2ecca88312088b7e4e346b72d37ac2c9f312d4d82a842eb9393ebec65a0d7fe9 b59e25c663d8617339865199c0500feadbcc5e2b9d00819a3b91304266cc98ed 9acd42dc028bf3b39fe1fbd98d8eb7c7c3ad85acea86efecce7e41831e5a0e03 3304c25e953f30bdbfb6ed159e4a344c804dd52aea4d104bcab5a281d6a4a594 53cc0ff1070cb40bce44ecc8de8f6b189239790afb0545d951992845cdca0b05 6 79f96449ad1cc312d124f83e2435238456297d5637eb1a2bbe8a68c6d1fe490e06b05430d4eeb2c11b3d93afd95eee82e7591872f544d176b44267611c1bed0596f1e9f4b20603e00bff0f1701f672c987b1df0f673b994bd655d2a3ea9b3b042b249fcae65340f381137e34639cba49de5be81eb329153617f0ea0782f4d6048653e96c9b27491a074fe02749a33f0ee013488c9168b18c20582bd6e5e0320abf521fb4df547c4f7dbaf94c6178e2d0d8907ac33e77031e9bae1a80d017a3099e9ba3c16a18b9aedcbfc8d1924618f7ff58135ab0cb3f85295b8766ed0a1d08f680c539cd0ed7b1b24630bc9f5c0cba555f65c4e09f4712da4941a2c733ab0a08f79fa6b2681ac05035c24e300ab3ff54dad61eec6865cde08e69ec735209061379c693f85c3688f4a6e04601fda6cbd0c028a4a80839f743e79eabdcb9ba0c8c333ca605f3a16524d18e3a25dfaf2d06b4cf109e66e5d92f0af1822139460797383edb729bb35ea05156de7ef609ed5ab3890b5b80c46a278c392c3b75ff0996a05ee80ba1a4e50b85eac2978ef8a99f6c7a04d80bd13592ec6f3538eb7007c32822355461ab48fd917fecb647ecdf5c8551e9722ed0af5f5026d98004880666ab29734d77238e63859bfefc960953f83454bd809375ef259dca1dd030130194b4f11dca8767af16c5762fc0b8d300b9e603eed9b68dd52ec723029430840177c555e64fd7024244562b21f7aaf4bed96ca623b58b4e9cea3434401227a20445176bfe361f413d4194693a275502f6ccca6c2f8f36f9db94a6e7e6924e950d802e1e22428971f82e5d68275bd84096ce901ac53dc44ad6dcaa3836d9a8ed078be51c9de93333316eb59a5fcb86eaf42164df35a9a5d4221286210078d5cf0779f249d7bbbab6c7a77dae733acf47082617d8f76b899378d8200477d178a80fc69e37a4463af1e35b45ed62d09a2a53cbaf5e26bd752f0321f091f1e6d9ce00 -generate_ring_signature a1a10532175cdd68eeb82f0f46b27f97cc2bd57ecbdf6e339a92c61d67056fe7 a7cf65fccb2a26a3470e9f90ef57f9c406df48f0dcf5e9819553605479d54cb8 1 516241f236a13881ee4aeb786cf686b340ea9f3dc8f142ab7939b63ea948f9d8 0666045383e7f9829c827c19f36ab1b57e58988af20ce494064ec1783c85fc09 0 213cd32c2a1503edba39aec9b731e2e9a95eeb95253129320cbe05fe175d600982bc615801e9683302a3049e8d5a3e1126b02c14061662a44286ae75913d6e0c -generate_ring_signature 33b853afc4a8fff7ac0b290b524bc23068dc3bd098dad3cf44bb96470ceaea6a 13b719bda608fee66c92ed1f005cd7993c144ce764fbfaf5349de4ccf5b6c319 1 29b8fb819c8be48673c0b35b8d3dc52b81fa528cfc91ac486b7751b1ba88ea73 bb666988157a61e08efec906b79f3e1321fd06d1f401712c0b4c47a98de66907 0 7d0b0a661f8268d9cfd64917f795e5fda2baa98b67fa3215d620cc601d478703049f726148eab6cb3edb8d77bff23686579ab086f79f3bc01767d12bc1fa9c00 -generate_ring_signature 7267598c7cab03345e1d5e770ea5cb4fb41d934079557f538b0fc4115962e89c 72a4cb672b3233588ce82c8aa41613f0e1f087a370871ed79b72983d414d2dc6 20 1fbc0e3d207a90cfb27c1619a4db82fa106454b554910381521071121f7cb756 3abb94f2520ef80147aed005c178e6ee800ab93d91c284f924871ad3baa3d607 180edd37508ccb4ae708ea73f0e56b0bf4d4a0367a7fabb67803e1e2937aaf70 947f5548e9c8937ecb3b13d7f8c17889369ab8302ffb140a742659c2d1744043 54c8e1ffb3f50f9e364aef8d7164980cec2ceaf36edce662252f53ead9a784db 613208344207cd0665eb79c8e0ce0fab7f8b864bd49014c7d3fd20552194b947 338aca305d87c2f8792b82136cbb7396bdb218213d5e282bb4d29cd713389a33 ebb825b0cb8392e3dbd324e009fa3cf5f22662c70b35ed38ad32155942d2e7fc 2a6a57bc6115aecea112235dfcb6908a5305b54c3e1be6ad47f4286b4d94fcd1 1e9131251f0cbe92c17dbf1cf67836afd9dadefeedd0011f3d53292e889e750c f1d339e8dabea718797095a461fb2eb70e536fa2690a639d486c027cf5af6cf5 4f4f6582bb2e5eea2f1b799635680b34e5c7aea6069dd434a5b1df7ec53eed40 00ef708cc6a3843468e01fb87abc5a78fb3e44d1adde671ef9f977652d93f0ad 1b441c5ffb9d966ebe924088aa42718a58e5f21129fbb5ab4710b30a5eab0bc1 28ea41a63a7c05801ffa3f0775114b52a07540e024e4f437d15c6f596999a54c 5d1635b9996f202342826c0462a030f4a3926923201d97e2e97782dde4232e8b d63ef61485f58a8a8421c1030c63b13e7f9285b3d17a97c0ec617156f3d87e12 22e861e8e46894f227e0c5aef38e7144d61ca338a95c8ac00c222afc157a012a dd99fb211e2e4fcfef75cb569e9a5f751bed5f29c5f69e4afebd558e1a915b1b a79ebc1f781ad0bc716d9d20ba4a4be3a41babd8b940b57419d4090ebab97ba0 77458fd9ec7a1208f2ebed73ce83b8910072cd2e4658cffdded55675eacaa305 8 0480c96fc5f281c09bde6b836fcdf9cebebe890a9c7d32b2fcf51dd184245d02b5520ab2ea5dc52943ce647a24d0f460ca71d5becc99e6008f4e4b5504a3a4018548fe0df0b71550f15f6c780c7a46f2e58354080d83748a3833bea7295a4707e6135516e2eb4f825d0aee624639c765f57aab35bec7fe6ccb544152edcecc042bf6919f45cedc9c638e15919d511fccda85ebe6c4b8f3ce48f98dc6114eef0951036f38723bc901ac2a27ae2f399e625e082a7da1e8fff3e53ce08ae798050b9cb7e886bfb14650b98cb2135f836f9991c714f88e647fcdf4737a8ab074f60d5eebb0d22f1a6a156f41d47817f8e71a213a2cda9fc9138b2141b6dd1bd1b40a789adb540f827cd2c481f27904ae6a3c2ecfcbf8871e4cc4886f2eee673d280954e12ab5491dd2505156a77d0b9ba4bc5da27fac8117d2df4417d082152a0603fd80070a826088f496277e838be997851aeb56d6d0684aeba687a62932b4e20621a646f3f799526760c39a0950ed7a43ba336212abfba34540c77a28d9f4bb06ff979e2a48aa1f7e3cf50353355c19fd124f0254c1efcb7fe6e96dcc99d36907fa24037bbb55bbdab52a3df1ee60a950ed23863f12388873f3a96294f21fcc0a7e0c619e8b8eaa2c92eda8c4ecbaf2e0e459d4f956f73b66b126cfb123a9c20b6671bb24889ae896edddac4dc7ecaf6ebe6384501ab97b6cff92b6aba5651b095bdde9bb00f13203219bf50f52e21a12a4251958238f86d8832f29eaff16bb0ab098c7d48c0653e177a106b7709becb2a0b1c0fb1fc46326c9a5c134954a2b00e2c3a8452222627a30dfe73f709fa4697ff5f024796fd5073ffd3fbf0d952102d770dcd629b37a883b0feff338c0ff306c6e8752c2a821ef4e19eaefac504e08695977fc2405e2db07415ea9a157f5f27488a735c95066bcb050a1c583f58607b0796c0732709efa610c18f84eb8a90b754b8d2fac4e77ecd50c7211ffac870390405d9e281624d87cd12bcc435dfa080177aec9e6b5859887d1afe26898330e1fa4ec1473a73d78a4ed539a39972c61d87ffe78d915cc95cd3e1278d7177d030ad3e6a8d3cf1cf28b4d38a529356c58b78da55e5c48103a801505a040307c0207c05942b29ccaff175185691046060ae4ba48a4b7b804bd0506e361732a7b0bf71d2f6d35bc418b1fe686ca6d3915f0c5b270872611e3dea817f6c49b4d0502ff3ac42ee57dd553bb74b0463c1a04920c549c74b2acc966a262f55cbce7090eebf981b139f527c39b6aceb80905f186629919ea8444a89c8a3d8192b5bca702995636ab36940ef308beddeddfaedca013a37beb2fd41224b6fbbcfcf4453b05d946790e7fe83f4ac68ba7fbfb6db4f535eff2cf48dfb8ab991813a6b4d87f0eb5578faa8daf01b197582c51bfe2d6ce1050882fa96e56fa8fc968af560c7c081f02d4d3d0440581f59e940e9379ddbf4d41045cf5759539ce8f4288933c0b020f481749ec39ae928a674cc65f61f9aca32cbcec456b9a5ed457e900e772f40e1be35221de2c1351f192521c4d730620b37ed34d9ee4a3ac7c6e8bb783e87d01fad72001c6b344eb08fc5e1c2cd0be939ebbc1367c884e4e2d40267958d094068ec11eb28fdbb43b57cc5f70c05dfad08114acc8b8adaed02661b5cdb471b009b86de0e47d3e690fdaf72cdbf0c078a20ab46c68a68fca8c6b7db0198203030d8e24064caacd01a80b20aba98e3d3f3a51cc53c998fd9e13efc7f1764507eb0861ee72eaf6daec8b1f639d7314a972a0d2d28af36790b00f851f51b3c10a2501 -generate_ring_signature 43790ade135a2bf9ffbec0b7dbf243f6dcdd40eb7b420d849868ff7aaa0f67d5 f454c7087f82361c3912a9120b4226db15f0d752d96a5f445e19ab88d0bb32cd 2 5513355588936c635e602614de59f0dadd566c4e7a77fa151830e454838fb72c c00a25f291c66d07365ad72d80b0562e7a91e990305a0bf0e5b01a0db499c8e0 30c8fecaa0b7f217aacb273eeae2ae495f65dec9dd62c17965c5f46beeaaa705 1 9af906242d45b508dd511fdacb50df785908d4dadd67cd06246c1759a6f8870ed73acd6dedfab49a0132df0c22235bf78050c5c44f37067a317afcf493d1e80b99fc9ff29b0ed56128a93950e36052ef6c931ffa59299f584cca699b96c0ee0cf7ded1d35c8ea93f35eb760f0aaeb89eb1009ddaf6fb0448b39a43a9acfbd509 -generate_ring_signature f745e1b2dc48174a463a7b510ac8e6bffdc6303c2816ca05195d9ae739db3ff5 eb4669ddcb40d449366daba63b22670bf36b64ea51970a796f07fbe78b6e1af2 6 fcfa341e9f83038e4f10b48ea65db8dcc728b2324140891a0566c0cf16017af2 3abe9c454ab3179b0fdd96e644d37285582e54423cf27904fff0cd060e3522ec 202812d70556c9afedbfbcb2b863e47bbfa43a0f0e366d50fb369cfa9ba40496 f3280be164b445d7605ed53195350d332b6c4a0425993782d6f5974520f433e0 8b368764599684ce8308969573d800ea8e4e9dca46c25429005d23b8c2ef0f67 dac4c7e0b261d9ce63a7afd3a1e7f1d3c04682287567525b1696ba305a26b42c 40e0ef7967fa1af5d40c8e74b5aa2ce841b04634e8eef869a3e25b4920c6f405 0 209983d1d175ae6fe2db2e42545b32411d1a598d1a2e3d684b06887ddffb170b8e390725e399029826bc3b81a049f7d82841e554f34780e3c650d405d89aee04e5066c618c555963e38b930d50be91312d8453dd719481714d3333148c86d507c081fee225310379d1b58a5542511a5d6be472cef944e9697ddf22e143b3100bfeaee842eeec4f27e344b381c00cecd86401f5c0b8c9d2a378963c0b7bc54d03e1ccc05965a4a084e0e90aaad1fb9a1813ec90452c85b86900674adbd1d9f50b7eb522e8a3386a259f12ce42ef2b5c7f3ee21a0602e7a8ad330a3a4f20bf5802e74bfc22706f6fc1d4e5e90b3fc0fac2fbc687f7d1a89e4aec13255b6c930309c9956d81b40cb57db49f7029e029b49de5b2eac7d0fef966cb01461636682801e55f60c2d380fe1826725718c28315f3f7d35aa542de5323337e47b6c7923505611676679c592eddde060f26b4f374410a4522eb35f064d071081e0e9e64b50dd800e9121b7db458900ba7c7fe160cbcce78eb3904fd27a05378b06b7d204502 -generate_ring_signature 0c1ca4f91f49d1955400c6f4f0ad43a05b4b8e5bdf8c10da751c98dd85c47d95 ed2b4cce5bd48fa761dc4b884062ebfcb4d51dfb6add0bcda77488c533ec29a2 1 c31c24cc6088cf25e09cab4318376a3202945099c5b2117ec04a0d03da2afea0 7297a530706b0a9826fee21337f341f3a7df6d4b53e78d21c0ce596ad2c4fb06 0 b79c42e9d09657c39cf189bb6e1c94b4aa3259c6c82350e8a50a910a7f749606248b9179bd07fb8238a0561f3da893e822e813b071a04da5fabc92e668451509 -generate_ring_signature 31e7df067e668a7056c57de9230390b3b21832b0aaf26c1d136442f138615387 5a53279d3b67d60495984b9cb94036d0504971c6f5d582b8a9c868bfe5856de8 28 d8e11f5cb385732a6b28074afb6337a101560a0015e9772d3f30d3948adfa268 4da2764bb6febfd8c8e04b2cfa881095a1311336854fd54fc4b6b1d3b50b3087 03601a88b19d5bc27bab15b33d1ff472d91e7a928a3ac537c709dab71bd56e0b d18d584e85ebc1788a95a43f24242a33cc9fd281a25029934850752ba44d1cc0 a2696d71168201625a49011911d328a514d6ffc61600e807450349198bad3d8b b1c2f0574ff45ceeb330de965a3ccead9738da629a941436b22f709939a536bf 7d024af0ad9a8bc11b1870d4e4bb34a3aa7f615b47728c80e366899088627861 5e16ab4a356ca7682ef9b89c2ff32ab2233393c1f6f8ec39945fd8f68d29df5d a4b8a7093c7b2f11f4ef98f3e17f398c05daa7f6778c4ef6bfe8da1b6ad8638b 0f3c7ebdd53bfd02e2f96ef56487d96efcd6c6d791486816317d624ee90f31e4 744845074f17c4d32bf7b9c0d4580e94a1e86f2c5521c5f3a2c08a815a0978b9 31298b4531ac19393a103da8df5afdca6312fdc9bd467bd488e4aafe0336563c 9127fd630d5000a395971aa0d908cf98a5c7711e0f01d460a2a7684f52d059da ecae915500777293d1a23bdb2d70c17c964b9a63ff8dd4ce6dbcf45772c3ad3f b48d34c124988a550b37c4a1d823017ba38b39b790880379ab0d9cc0031f934c 2de61fe61cbcaf6ea1f10cb8ba8f4fb8df780cc00c0e517bcaf9284cef02bc46 040f04ae028b915b6d1c7d906be11eeb401f7e352def5f7613e211fb0fb558ce 551d509feb45aec8e434baea8603a2ff77b5e3a07cbb0d0b527c8558c4c64708 d073b898ab4ee3836c67d0e5d1dda2085f4c12aa416c81bc340b85c3b8b6ed52 435503b90a2d14f405096ebca6b42282534834fe9ea39a6bba5a4caa4f079766 2acd80251b3515fb625a9a18d2154b9ad1beb8d8b010d8be83ef0b749c5dd6e3 e5215f531238d2c273de542c6f4fcd444e82134080f087d0afc64f6fc8bd44af 22424ee51118bf80a8cef77b8e113cc0330e24a3844dfd84917fbead9b8c254e 2fd2a01313fc8b08b276862efe9a959c3b1a19c26615d90530bb81a9f7bb86d3 ed488f41eab4fe62d9ca4b7b84264cc8320704afcbaa174d0cce1799d577e741 4294f3ccfe624c32b9b2274e0c12e55daa840db023085c7e25ec32e00e889bc2 0f8c0b190eb31fd4afed7e449e6110176f33ccc4d986e948104607a326c879d6 3bdeb0e9895a3814004480cc6f992bb3d35b747f697bb88a04147e419c5c2c72 3e8890b916eea212d4e1e8c72b2775339eae8c42e4ae8b43ed09e24c694e7e0c 6 ccadf6b1749d19fbeeb11a13276a7d6351bef5696f107dbe5459a9116522e3090c204a95ee2e62723425c5355dfbda1e6c0b3d56a8742d52a1ec6ad471e05f0a7d9e33d19a221ff68085df2ee5c562c6e0e204e1236a5b0ebf43584bc232690802c3bda6bd812ba50e521334c5f69c3bd36a5d10afa89493032b3756f2bf8300bba5a1266115337e3c30839b58a1babf35f687605b5ff5892a8ca5124f25fe09d7e05d56341323b9cf6862c9fe76b5cb9e23094591f32d5b9d82a6ffd139cd0e10a05627435f7ba82099355f1833b43184b15970748f4df35bab0cd0b3a927052f2674dbe1f68c5778e04eaeb07b0b8a5d032c4d143064b4f2ba6b02c0b34a03a6bd5fda8766a42ce12917dd4a8cebb0dc120148ce92d6fbc66a98b00728b60e773e83767cdea8576017744683dcf211d6687e174827445418536ea8f8277c0ea10606d2e2e78afc78d00253666ab905de8fffa97860c9ebb9ab72adc7e89d009725cc85ad44f0346f6fc67561a6854c13873516722d964e93d5f5e6b80b4203980f2a574fa5f7545ad67a397e1b6b78144569cca612f895a2a7f6ec3f74210e7d6dbd6e25a5fcb2fcf3ea4c4ba8d358c7bb1524f3e7169f4a7124eaedacd0012e7808c64bff2de2edc045e520eb6270dfe865e6a83de0a967d07e42d710a90e6846466cf9605d9199e4b18253ed635c453285f3a98aba257c01d98e845a2609d056cf8a13c17966e06cff17109e3089cbb2fe04e87308c64cb85e330b9922070a64733849d9a9816296966121e894724ae138d629051de2ade2b0ed3cdfad09d1cdcbc0acf0adbbf36caa14033944640b4d4e1da76f7e0cff7bff0940bef80f370109976f394cdf02f63bc042181edd629474f9ca4bd6e5f777108545ed690b11d5f4332e5b8a04a7984e526123a166a012d1cf02cd63b3d386ebefe90f120c774dd8d60dfb36da55f6f372a20466c103fa6c35f004deb76505f1177980a508d58f83d31c891160ab486496a1e58215274ddb3c9d147fa882ca76c3b50d690a8a06d03195369d0c99bc096fad0672333a0f56ac38b058edb707d7a9836cec00f3a2d1810d4b9f5d88f1f5e4cb9e6c0d6cf7ddcf68efdd0f5c922d301d951703b40171346860d16a37fe8b13ac2d17ee4f3e8596bf095d97f48dba9b453c1c079c2eb2a126b57eb6e2ce33a5cef25cd2f55662a5cdda73aaf60c635b0ac3560cc28f84dd01010300e847a91562fe654a337797e40287f4f7e8c5ef44d730be0d4bd7be57c3229d49e44fc13bddcffb4c74ebd0171cd51e2b49582c9801450204ec07084defb841cd1dd52cfce5599a76d3a5aae0a7030300bb338002757afd0689341be22f73572c5e430a2fe8972cbade9bae152cfc1ad9da8a8fe82f28f401dccc75a32d68861f8fa4a07f653369d0f3621fc0bbe9d65997930aab8b25ac07cd079d43a0b9cdd4a1cf1cce46839d9ab0a26f9a02cb0307066521f4496f110cd74db73458078ccf6391be33cef79182859f04a93d9a92ea48d384c99e48b90282d775145370ad05c3b17dd9357f4149332efc4498bb4065b5b51b00de6314051ea6bc8f61492773ccef7df9b0310c210b65ff7d4dc41d79388b5c4cc6af7c01cfa3e0a5144414c00fe006fa1248891a86beed1b7ad5a055a8b85a33842aa80f8f8f91fc2b9c987ee245cc00799aab1984131d143527a0e1c1a123906c896c0d7807727c60af304bc2034c129cfe002e0a72ac225a2e13b050391d24c1629c0de86afaa0762d65404530585ffb1090fbe04e314ba18ad1001641e82babbabc01f5cfcc92ed3dea9c042b8bd12638d41b909fbec2cf471a6c275da8ff2c991a08ff2f8d0ef14a5f7005dce8a070ac41f9ca74d9f85e5cc7d54dc13277e43c28094a674e4ba96026785f00a1309a1f24291cbd0e1c6aec09b96b43aa18c0b25104208d63cc2cb2d7487aa4045e75e61b999ad56d7412f174ed2676b142523be20c67672c1c55632e379171744ae0ba9ed5127ec93457e9b240231ab97a6bb40e00be85e3ac91470e13ecebc8f2f711592e9f0a5c51fe87315ed53e61e182c3b7062cb071b7fd42976670a7c0e939cff16f3efeb9749550b9b4c3ee4628afb752042da7e1e318df6075336f018f6306365ecdd6e6da00ed1407663a123a81c9c30a7b9154344ebb0056cc69acce01391cdc68b3b7369333b56babac7ba8f12d3c0ea18c88c7ba103ea701691eae77b3ff399c1319c939135233c991371c48b3e9039503c2b3303655740b8ca7823021002d877ea64356e23ec30a3ddbed8f311c084a00102e40e3f03d16f56be99c746c4cec5d855c7ef77587bc9ae58ce52a1e0152305ad3871dd35f73f20cf0ed7af7786d5d9366cd55e03964bbbffc2f0adb0e8d47952b21c1585666487cef82d40d6a879f86c4e1bb39beba3f680122a00d0ad0453b5f68c46469ae6e8a555b0ac2fd1b5dc78f505080479f9100d1c936ca0ae56f610bbae7b68842bd33789faffd7c2ee4b83ee5af164ed694a44fe92e040a -generate_ring_signature 74cf9d48387f68da96ca0b0be54b436b877a40b8d7ce35afbae4930e435245d4 21d51622f92eecdf0071d205840bb1e1e1b8b26d69942dac91980a694df9641a 2 b06cabb47b0111da68931631552e977c5d0f7391abd7e835d4865ca6b6ddc4c1 c59a24c4128d29bfc1d676496b3b275e1623dd226ddb23f8b4852eaad13038b7 880eddb85bcd967112fc751b1512b490b11abe5d054e43126990afc8226dba0e 1 04bd30cdd64126a47d8ff7c8788afd5e1704091c20b9fbd5be399f0dea62850bdc0104759f7475654766b09b7788d3e1a090c59628ae2a3860647907a12527020952fa76b7211af7088e2e3ac5b8f5fcfd15c4eea22420cfe25841184122db093f362e41ad9ebf70074e590d212dc202c1eaeebbe6d4b27095f571078990b70a -generate_ring_signature 9207d102e70ef77349166326193c037511f13940a761e9caa6276c186b3eb2dc b935299ab3141f7bcea9047224771b9eda89b7e65d5bbc85ca163f1d7defb31c 1 764d6791a583a488cdce815e5fa424f70de370e2669d6767ddd3d553c6199343 f6a1a2a2a3dbd61d40508ba1f192c0dc37fe3d297f3df14c9730a6f084779706 0 fb190dadd6f34af307dbe0bec1df71e8c4be3833d096925232453461dd91c9050caa89d49eabc8558e14320f29867bd3543c653757ddb4df5494e416bb294e0e -generate_ring_signature 8f1205e1c7bb2c98ace5d21dfc38b0e52715a4dd21f9b7de40d9dc516c0483fb d27c1b3252173f413b070231ffaa38185f412f2fc8af2dfa34c6923146cb8f8a 229 52c5784d3cda705f4866716a833c47355b5fa4898cdf0f719014b1e06da6e6a3 913c3f015930f8ef9ef6e7113a6d9dc22bd50d047517e2551fa3eaebaf108502 6492b0bc2eb958caa42949a7a154c2900bc56ed651135dbf4b1b170fe9cd7045 15511be2cebf723622ccd3683485b7643fc64514f97a882d9254f4dcba06aa96 30b0cebff8941b4295fceb60692aab5cc46f9080e27da45272545311f81f211d da45fd73c50d7fd41205025c634b92098ac9d86dc163fbc5cefb2bc8c50ffc5c a6bf60e5242b2fed9b365e5e30a7a400c1ed7a182291be8661340f03a63ee265 5bdeb86000a7c98f5c7c3570e807dc75355909ddf07ced6b1b8a49b8ab5b4b3b 7626e7ddfac740421e4ec2f375da22498175b569cb9ecb7cd0ff729f17c77cb9 33f89703bcc1f9888f44ac7af120c14ac800adf93302e7fc80b6953318683540 15ad941b050df08d5fbed5646703b809a4b2bf59fc806f68be975a1659a3d67c be0d234f31dee1eafdb19d6219a12ecfb0cba4b276dbcb9c7926588efe936bae d2c6f00ce060342eda234645e3302365ec1b1634b24a3b48bb83f7c2e38a4c58 68e9268bf9af8e28e84ca36852a5cdabf74996854218b84e62fe1821a6fd3be4 de95a3015e45e1e1a20ebe69066248bb6d9091c33fb5f0fd99c99e2c98b3d9f0 175f159b521035a0d4244455f49c292cb6a84c4820ddfe691d34c960ca04f7d7 8c2334a87ffbdda21fb11cb805d7a0ed8ce0bfe34974660956a8ea56395aa4d8 c7edbee25c17019b2202d4d0536bed6c36c0cfe9368be0a937587792be18a73c 42b6ab41fecf4dca2b1150f4d8952ff6d3e26c124d1abe310be551f2d5f5275a 3622119839daf53fd79c3d53a87e92b7174a4ca560cbad31f906b8def47149d9 1ae04ed9d6599aaa3208bb092eda99dfc9e9b48f08b73b45211d907038f38aeb 01cc92d5068bf3d9ccdcbd5622d574a02bfad10f4efd4b38d0f443da3ae00e5b e862f2cd0bcbd35d523302201f9bc7d15ba68b2c03076e475005de87bc5bac25 0a9d2fd156f8c3e893f28bab84b7b9384e31a15d1131386bd66fc345e5599653 ee3144f7734cb223aca913d15b02900aa7122f079f9a6247d37341be18c7493e 49630627a6644da9ee51fadd02c54dba4abd237db31d8e7f911f9b3669c71ac3 cc1323b8356b0134cee1836eb0e42282f78e46a54ec1406a7d28ea639fbdaa92 3cc0a3b7da1acc1ad6d518029595b1221e9b632ac78a707ed136b597d18ea8e5 1437804f33e10dcf092b2e2de5ff4b0e541b103b05b160e6416fe5d915b3d867 a28c48c79a7694db14669c83277b53961a86332fe3e488074b3b4b637e7b13a0 0f352d25647efb575677787de2e358d579be0aa9bea73dfbe3dc6217b12706c0 ad3a5789bf9a2500ca930d64d72f5166516c71f42534055b3a945447bb0a4fa2 0e338d303c59926ff532b127bfe7f5275b6ba6110631ad6932059d028fa49b7d b68a76cc12b0825d96739793cc5365d02bfe50ceff68afd0b9865f65cb60a3cd 018de5869e7859cb475b48aff57f07f0aa8c1e1ffba1e8e96e87fb32c6d684da 06888fe284c0789eb6e33303797f365dda3cc8f2a2f9ae7754ea1192ce14d435 7f41f8ccdc6f2b5194de7e0477302d1fdae0a74239d94d481d3147ebff4f635f 1ff844011bc0f9a1044b23ab46fc2acbe00d939a9fa90b32dafb8d52e25c14c3 1559cd22ca6b489236f6854add12fc7c2946c2f1f02d378539ecc9305700f87f a597e24bc6f4dba4354d97eec4ae6eaa44428e3f35ca2d060b85bdc2785b2dd2 861635da1e9955e43980a21e4885bb8998c8d86a047f382f94f93da569985c35 66f0dd261e45dedbe26d5e060b60cda4da5aee59cc0ae63387dbed7baff10649 447477689524d8c7a55799d1133c47352dbc5d1e71f1355822fc984ad6cdc2a3 1011c08840a7436f9366091864d940e93e6ccdf7b04189907ae27e4d7e00d96a 954b68b72e15fbb72d805b79c8137d01f8a83546848f8e84d99d7e049dfda8c5 160761e382b3dddeb20b285a60335140f03f3d75acf9005a8274769d3d2b3b60 3fb8f5f3539927e5c4d8d254d696ad4a2eee6d8850d9ceae857851a608dc11b8 5597e4539febf5cf5572df1ae65e58ceed3d7de6c7d8f48d4fb0f3b895951b5f 5c84d316987cb2949689b99adcff5ddb82e0c1996f9ff5e7ef510ad3b679ea1f 2721a20b915b3e8b3263055b1e819947c8b6acdea2fad1d5f2799ec3f52f5423 abbc7b0ba883501c0d07f898df3d11080eb4780ad610e1e8155b4b31458b93cc bd7254abfae24aa591fcc630b3fe13b2f6ecaffa09d1f220ec3d2cc166709ced fc3646f77bd6635c64a53e50517ce45fc7a60e84dd81e4ca06235693d8cfb44d 9fcfeb091e1bf9cefbf395dba14848ffba2714efeb80f49e9b00b00c9f05be86 8c6ef973374deb3c15fed4721bd7aab0e6cb36eb543146d69ba5b99cceeb2ee4 3313b1b042d283088915c9416960722098d20dc06e0805f9d20dc2eb7b31b46f 9e44b4c47f110e2940b199cf3ffceb69c711876cf5f44b3bcddc9424ddcc6218 52387c696e8b95cf5ab7f9fc26fe4a66bf8e1d84ab56b787591619fa2c3e44fd 47ecdc3e664495b46990625452266fc65083351cee005e2a769986e161788ad4 93b8097c3c625640736d2558ff7678691cde0fc00fc2a4e0c4e7bb1b54463447 d32b0abad912cbed3ea7bbeab6db254e521596bd6fd944df06144a2cd685a68b 6b2a2312bdf27d04cb2aacf4a26994dd4b307efe7dc2b1393e815fdea8ad6e9c ebced6a281ec97f6f7ce32d1e62da1ead1173bb873770ccb11f79f9d196b4307 16fdba74bd5741106023583564de63f5b77fd0ae433253ed67d554bd7bcb2826 f1ed0bf4e6f4ee1fd98a9a9b69645fdfbe81ee7908dd0c21cb1860b62e356ed7 681c3a1347ad98aaec4d7031be4969b3f26bc09b03e30497d58d30bbf95f036c 0b9211db6b2a938346aa0ff7df2800f81b4c45a58e5d7689503d584d0a0a5b0e 7dddd689afeea27ecb36fb61df6a0f088152a5694f4cf4155cce68fa2397f6a0 accaf8a5f922e97e1e740b5e8f60674563ef8efbd6daae9cecefa048705ca80a a79aec40aa552d5d2c8564d40860785bcd12aaa001bb3c1beb6846908eec519e 6a8c92275c25fbc29c8b9e9ea0a7eb2d50dfa48836a11890adb27053114e730d 604d081c0de894e8a44412d641baa0e067fc038347ac1302aa2a9b0baced9311 0ada2bc88e8648a5e0c01a90a72ef1ef334849a5cec553309769ab2ff98618b1 3e89073127cc5f0fb823d9d0c8b7ff60974413a12754775b9fba0efc19efbb3c 01e8a98e41bc850b040d84719c66b985e798a624749dcbf8b358ff7d64c233fc 6feb1c7182cf86bd80f8eab7f4681e6f4af6f2701a024d8dc6e581b4a4168ec7 f8e67fea54bffd71c8629a90d50c7f6134a0c2b0c4526795ca2a5e07f146f392 069158fb4cc625c32daf2608bf603d73b7cf3cd344670e218f3e8e54ee691ff8 4cd66ad442a4cfc55b4d0e913daaf33cdfe7dfd1da22b6c2e3a766d2e60d772d 1d0bf5a9a0c93b83efe4143b4cd759079984ee4b0b56512a3a0a5e2c0c9d287f 4df2310fb9561413b277873f38428cb814f221dcd694af87c2a9dfe676a64b2f 370a267d62385479b270e46f8578fd2f68325de6b7c71f7bd6ad678b061b71a5 7695614d59d1ee67af6684e4e9ba5e82cef5880b53b3070a9ed5520757fb2f2b 5c95cabe47f6be580ba29dfc59a01fe24985b9cca5ad1c8f11d94a3c231efed8 520519e1a10836f288b25c7c32c15469bbbe2618d5193df95381d1cc32860847 0d1f46f19b2ddf805247e8023194a97e1f25a23d7c9fbcd9ee75408b369f89ca 6691f76d727a9fcd6729798328728e9b4991ab4ea23a0e8fe35566985dddc1cd 1e9f2fafd07dbe67ce07bdc4b81cbe6bef69b5f69e90db19f37bc88680d67513 3a44c7187849ae084e96d6e6fc5607550a96d597a298c34804edef0c611718b1 f6d7535c498c428a792217943664569f4c301481b400c11a03c03fc156fdcd51 2dda0f1b716a5a96014d1ad7c0b0d6c3049be66082acc5760b70db40591b7059 97b342dff13f2ec104afa7ef42af22d053c0d4724d5c150a600309f679783c10 397b9cd9cf85af509978ee6324ea25ce2ad46f4b6fffd70fd31376c411c98b8f a38a93655b55c9ade39d4d1e6a81b5e3014066fde7e81e91ebf5b82f6dd139f8 1cf789d950a4748478226d2cdb521f75a63c6dc3edb4cb561c2e57b38750cba6 4644dbd47839e1e94946ace12b1dcf5c64920d0c0188649ef4a5fd11b5075599 765b04257c56f566bf8748ce698d77f0961dce48440c50673e838a9f24233a88 6b61314fef1282d0eae6088f5180abde895a022e6cca980cc193597a2854ac6f c2baadb820f257ca6f3d48de4c9768c4b07c7b8d4f7342acecfb12becc6879ff 0cb01561b6391e1493e1910575fed3fc3c314a417313256912de21c61610a077 1a1c943bfd4be26fc17ab352e30988407bc7de6de0899e3c8aac2a575b84705e 0b2dad0d3b05d15de7f035d82acac91d21c7772aab52cf2f35ee17cd1f7496b0 7e3b55ad8e3a235dac80156c46e290880d9568752fc2843851e6de584d7fce99 2cca8f02b11d269de7a1790ba4ab530b9f0d893d6a6cc8369d183170d940e860 3a7191d183ca61fab0c82a531ba5cf5b1da51ddeb1bccc8a3a5de1b3a08ed590 3ffaca59a42c9a06585132bc401078b1e69659c151876b562918cad9c66b080c 482a252d0b29f7fb876666d565f5f67aab6d39f29317d31236129632585b2ef4 4163654838c2ba6d17590d6710cfcd1bce67f76b7298bfcf92139d2502b899ea 94a1dc49c5222983b4d843765d51526f0f1cdb54bfc9a68f62bee58da9cd0270 4887f9ef5441a1f87896339506af632b7d8a9c2df05b002b42c5b6c32165fb66 c53bf4a93bdaf03f2e3c4561357ff9a247cf019dc38b621b916f2c4d3395f9b9 f94b686e0d068cae20b55014454f50fd950ac9f84ca705b629dfde4baecf1ffc 2a3f77e3c18ebe3ec45879fab23e3e3f1db348d06eb30666e3a185484a91b7a5 5a40ba01f1427edd310b9408c5ab2488e5d05ce48b8bc85f8c0081adf11cd9dc 75abf56aa4921954d4423e957f680abbf1e3f458a1717f21b69d26bc7132d484 08a037168f337c2f30ea5db11153eccf6f2cc847e052ef0f2dcca49e446f9c2e 9a88ed5ea6035728e6cfa1407aa9467df401bf31e5b9661160bdb48ff7e1fc36 5c86516712f28f11b0a96055acd38d7819d497ca2e4283fec8300da62612ece2 b8a0708e9b6d8586fc9bc3279a5f0a1bc99505f627bbba52d9cb4e313ebf4d4d ecc9011f2f931508fd5b199f76df7bb96ed8c1345a1787567d80b249d2b0eb55 18275f7f1472730153d052fe574b8e890a4a32b012934ea236b3663dfa28f14d eb3014649405efbf8bfc962ee7706e9bee6a4ef5625eedeafaae0051aa721917 59fee3ebc15513a0c63a4040bf31bb02b6c20c9246d006b4f026c1db2e7a49e1 8bbaf1b46137d97273c26d93918ac0baabba90084bcd209a0a172eb8e7ef9b65 8b2d7272c1ae09827cf434578ad48fe3ebba74ce2d3fa00a35c55523e3a9de89 e129b195950d493e3c9e498f8dbea5b3e1eb4dea65fc03ac2bdb26df885e4dff 3671edffe7f5fa8fe14ccd74930c7ef620eaec25534f1e0db04046db3a09d3ca b086b636c71efd2445cf6281fb3fe8c858c5a345a6ab38314e3d4cdbe962c34d 7ce5992f4a5b9ab21f3b00bd9d03eb3b508c4e8d472f6ce75a624bf69c489cdc f709e736c33fad590fc82f9a7147597d3ef86a3421c0cb90d4f7824f19ee0643 0a758da68465fe3fae79ad29012b6035395373d0959fb27bb440dc9468683f19 5de2ab6d01977528e26cb98e22ce5205b76ec3fe33dfc38beb8ed1ca8d65f41c 0718b9494418118f2da64066b22439acef7fdf1e0f8b14f0522d42d4ecff4722 52ef5deaf9620304b0dc480da5db8e3cf017484061de018008e2ca1c2a6f0675 cad10726ba8716d69051940c377ac5f6a82e2e9a23096597cebfcf06bf515e86 9fbbd93d71ad8ce532ab9b2024aa3cfc17b89137e91f4c3f93097e8a34a10e17 d7d5f11eacdc7cbb053d15d3f63bd0e33554b9dd2eb8c83800f8fb9d0b2dde42 07027433acea249fa21af4cc3df4123babd856022b1a6e7e972a80602a5292e4 4b1cdfb44d28cbe272563682d11e1489f4308bdb836b6ff4a61d0d38be1a959c 9f35f5c9b5b382dc4b6476d912351a322cf349d8b5861df181163a46cd393fab 210cec3467f2c3cc24ef57caf3f7fbf6b7a8b46edf272adbedd8cf67ee954aec 6d5fafbbdb69bd02b98a2b4bdf24065092c0fae33cf6c6279647675cf20f5fe8 6814c20e384dd2298476d630badd43d8834560a06e4a5d328268f03b9f65f714 d8bdec2059ee9a599affa6e3548a00524dc582cd194a66b5740332d8404e43a1 8e446db99e8e579e5059ac28440d2d2c6a6f1838bdfe074f2da61805b213c11e 50a06a07245c8319c6f64966086e439e8445382b79a31513ae10998537568235 5efecc1a91a9a9df0fffb1050cb1ac2905100ca92c0ef87074d50e17a54c4696 ddf9201bb6ae23f18cf6ef069d183d0ecd22b08e481d2f1c4760f3a029a17ba0 6c1f2b0a8ccce1c65d4f411584c4a4811894b96a7c74dfeb4ed9680e4edf0259 300e358000c2b357e48e532f4e02dd6901dea80a371cf4b4856cb5adf2f97c5d 49ca50fd86f5ee0445bf9292fd492e4140e550fc6a520024619501179732d503 026d202c9bcda09cef16ca1c5dcaaef6da40fe42e533562c530e1b4a69c17277 8e4d82f0a0384babe7fd0a4326952955832a37d1152d0fd189094fd0922cf106 c3ccae4f7e60f4fa82bdb101fbff68cd2071e2c7c5729b3e9d7a0a6a51809807 e2c970b2577d34c1a4849fd4ddcbf48bc88627d51c8640b3cb2ff73a0ca852c9 054bdbb5411a58a04ab389997a4c85e38f2c714137252db2e130b80947c505d4 491f4b4d406f6e89833f4255c2f53103a26e3fc9f2d3f04ab6f5e034f9d16a3e d7f7a5fe433888c71ba0567bb87dd28d719e92c71254a1f84238c7a78937ea2e bc03db54203b141f413912cb5a0cf4aca06609dedd432042f8775aa2524b0a58 5e59b2c9319d9e6b450020f4b19b4c30bc5b2be4d32f4f635005f3efdb40ddc6 4d3553533863be129705f3008ffb8cfc0477860675c0ab53a642a13445185e4f 10837c90037eee815d50df2304f1c5f35ccfe3bef086aac31ea953fdd5b1da1b 5783cfc3d851b2043684d08af46bcc28c218c4800aab3e4a25192990c3a3c143 a529b861e4f2a800c334f542c958072804073d33ef707a324ffb950a0d9a3bc7 533de95bee68271d28cf19a0cc1eb17310c12b00516d8cbdf11a0af3d90f6ae1 ec2a1aac02d4a55ff2436405b7e821d9612406a4e1aca17b3e05f54039d47fa1 cfbb9f0fe10486fb902b329603cef2d1ed5be4e50557e366dd2d9c75cf535129 b0fa5d3efcee11d9e75ae5e77cb0363f64278fc6958114c6896c77f776ea456b 92562a6e185be15eb3dba54eba90080b2b556ea4e3244195e97c289f2936c09c 1130fefded4d6afe6bd3c775c6f3d92e7c49dc0b5f4a64ab09dd94efe5bb27cd 03df5ab7e0e605c91b33709c625af020147fd3d14594ef3928524b3e41956276 fd4730ba5fb0ba5e26f9bbcb8e24be8e92768b874ba84bd850d3d6c6bf14fa36 ff340888ba7d0ec25503b411fba67bd3eab35e778299f4c7b8c4685ca0e4073c 7cc168482cc27ace90bc3f551975052082c33d3c74244cbf423910d00122e760 b67e965b6227c63f7b1f7c1cac3f4ffa044b499ef2126be0e1874db9fa24a6e8 af6ba3f29ce5b209c40565ca02b58dd4380715f6f10d872006488359e5a468b6 4267ae8273f14e15165df0be29aeb50a9a3bd312816a9be63bb554bd6b9f82df 4ea2aca4800d87b6df9995e3d83240df97e309fa4de089f8b12582cda536ca05 bbd135a87a41a425b196297e060306fc2ea93a1aa2b9f3e6ed2b66b52f4c1f89 d025ed814116ab93c9bac3f9a4272502bb77bf9f71562de1445129e041bd2831 12ab2d4d0972a1c1cb4edb0c36bb60f3c347a8a651b814325f85b326e48707e4 99f3623de0fedae77f920301cb2380861f9844fdbf0d71991b0942331449f9f0 a0f626fbf3ec494ab01485e7e9e49f97a28784231070d4ae3c6b7a9df94c37ed 580d0cb132d0204f173abefcf7caf3abb8bf62bf65bb4954b29f4451f642bbac 8c97a982ed265af9a10f3f8093be8fbf23bf583fbc5b85e8bceb0aabdef3217e 66c73b6297879ab3b614c47308a3e64501904c7bb61e2896121b3c97b12d3ba8 33c536581435b1ab7a93b47ad2e8ee25d3358873c7f13e07e38ea9d1fde9581d 358af96d40f33c96c703a54970c329d9cb71777c66a41b3706b161730e178e6c 973bd3b32a458e0b238a95feecc044bcc7640de370863919a517629d26a0c100 9d3ba5fac2f6aa8fc46a7558bc756ffed5a1a2ae57aebf71360681fed878424c 95898052706ffa6d889c76a090e8244d2e6e7d339610a429da38c3e454465e8f 6604e03547ecea591a6d36ed604582cd8abd72bdb2b49e0a917771da20bcbaaa 38d80837cb55b1ce698fdd5b8d44ce9bfccad9cdc19e0e3b7bb7de9f5eea7833 16bb6df962e6127a9b053dee33aa890210c2afb63058011a95d5d6217aad5d41 09d9cd42fbb4bcafa7fa122ee44a13b420e77c58add73a305eb3c2b89f81348c c5aa1333b34398e500bb2cf60376e5dd6126f4d3db3034259fdf55bdbd3ba7fb 3fae5690d4a5bc412b86d7618d36dc4ec01009aad3edb77ff776c673109c7548 b28456140a3e985c226ab99a6c80f87afa3519e955faf49227c7de36f93d6d5d fa133b36fbfadd578d68199bd44c7e2b902c2ace06dae2ec52034875ab963d0f 3a5c007a828fd572ff8e0e713c46fc41f5aee958e186c1b88198135b116e5ed8 de33d23fc56054e68f333fecfca5f4d576fec6c500a494b008591f99c2a6c126 8db348260e9bb621ed1ac68622e551e852db4e8bfefb048e31e606e71cd9ab0c d909650ed9a50ffcc7f26fafdcfe91064dd187eaea4dff027bfb66d734a94bc3 2275d3df45c2e67a1831735a9852351d1e40d7903380cf3b33ca7ad4700ad8e5 9bf506cd884abfccf74b7ac9899e5430ab9be9ce1dcf4339453cac9308ab4867 044e10b3a3f390f0e44eae198a8fc246162762c0303b9326cdf01ae02285cf27 3423e004c3572f016a2711b9b7d5cfa4be8327185f4782a83e719fbd880ec73f fd57d3173d91f4f5b7991b70a4a7c17993e783c9a250f909d8a452fa1d4a7d89 4fc2721e8e5cb0f0e49791437299a6969485895295be74f63d8253c3e0897622 c370fea6b41b19482dbffa79495759699404f5b8aec95c42d022e710feceb4ee d76fa502cf3ca64f27d37460208b45d8bdb4343aa09f60bc86b43b45b3bca456 63fa51cc0c431db9458fc137148bc7072aff9e34af67204b0646139b5efb43b9 09ea32768ee291622d18b4ab9b182a0158604757150dae3692016c830f19b508 f2b2230fb64c40711b943000fc9faee75bac64677635f21b141fd76aebb38dd2 5e39938c9c977fbf84df5ee0f5cddded6d55f17eee0a38dd5bf01798a9df1c01 855f49af12792ec85075cd22ce20f3bcdae31749371e906bd96504b1662c1466 4fd587a7358b0de45ad268c237bb9b299d6a9fb06414aa30300e237ba1a3f72c 3edf61a65c3ce5226dd877d6aa70d639d52ebea7fb4f3aa7259e0efc8a5ea1ac 5846a56eb003d2b04f8d8a5cd02391ce10bdc43ea6114b740f590070d28a7003 c842f7022d26e8f0c9b832102efa259ce5e77ac2453fce14a364c590d1ec5d50 f90822ac98bcb91651e1f71f9c0849f821e2810743ff1e8e7aeae5909908f451 45091874969d3c9792356d245536a8da824e9adff04698cebcb99ff8e9e20f3b 9542ecf82586fdcd124636a4ee4bf2c4bebfa0f98dd76b45adac28227b0f455e 38c69a23cfeb562bf698b66058d99e63b5ecd7b8ca0b1354a57e9d60140c35a1 f7f75d344ba6ecd4ee5072363a6eff47161aa6334c86e4f3ff832074fb064b16 f7815c6dd6946ccabff6b93effcd8cdb8dbc9630ec5274dd476b27c3344438f2 b21ed1f01a74f7cc86f2ebb376b203a82bdbcc17a6acc4700d314012d3ca7e6b 54655a6c6b8e909278c760828a75b2daf5bf809aa10d84fc68bf4e1d3636d5f6 6b69083974fab0a280295796253901ac813b657efea7348aae3e2eda762ff923 fbfdf4bf87b4bb696d14bc2b350acd77eae89f267666a092789e0af07a32740d 38 226ec6fc0c32130bc13519edc176fccbe844aa9aa9a3f31e2de4ab27d05a9701c30982ca75fe915fbe58bd1450ee11090e3fdbb6da76e2fef320bc0cef7cd807ba545ccae4b8bccdb79ddf4c0b30aa01e845250f16d2723358db03aaba996005e00ee290691f63ad61fb224198e56f410c348ff76841f3bc2b93c4454aaf080c4e4916fe9d734c952848135e1c63dcdfb26da0f8c4ad92ad2056df19184ba005d612e9ded7b8a67c6f857bca5e0aaf4947966b8f2591f40295cb429dbab62e07ff96cb9af45acc89b3b2f2f9c3a87de522b337c066f51897bebf4b9c2df5a60861ea24083d3997560705d03df070d9cabb381ff9828f94c3fc65af8a19e51104b51dd2df8f341354ea419ea1fac81251a704bd29d347b79e12e33c8a4012040f74c28da721d51c73d200b7af8331e91d183710203d07d3326106895c85abb60cfb0f7a760044d58e2f7c4492894a9db17e659b303b15b57acd5c852b1e636a0e2665a972d2878f3d1999a8e64eb9236b9e08b39a5436bf69a86304e56ee0030cad6b4a9c82e8621fff95554c6ea8d6afb9b691a644d570c34c20d69b49511309f4a62245fc6204c41b8fb2f27f3227472ae1b5096bccbf981516719159ba810254cb039305ed4b83b8e97265203a049707a76f5d69b2f836e298f3f117f0290da44bc9a371bdea377d9274d50a5638fe2f77b0709b7bc9e22a02ada43f38bf06438828e6d4e0aedc22aff795df50934bdfb3235750fe8eadd9d4a542a0ead800a8b529269e00ab39d3b208884bdd3c1bb2d491a1eefda07e6886ff21f031b10d6a1cb099b670aacefc5bf51222b7b1643ecbdce466309653ddb712bc8a65fd0bdf09c79c0e125a2ee8cc038e9b185e0c9906fb76ac4188aeb760cc87ee1f500e1213d1af889a245231aa0adaa16865d2b372c8e90e39d6691859b9bf2e57c407c73c43f7b84856d6e26266916dc7c640ffc9fec6434b300b43871497f680600603bfead9c4d1959291851a4e8adf6d7ef6309b37e54a67a129cc14de00b39308b0bcc499ab843d969442cd1db6443bd0c5945c6d36c965af3125be4c4b93f704e2d7e8767dd7551249775e00b348af8badad1e67c95434ca871da77910e95f0c6cfe063b4a339e8bf0fda3b3a1a6a05391acd34f29e94589398d351c11ef6405f9403954b7a1dcd357b9114476ecaeba72b6f902856df1dbb07168161d64b70d68fda987397d4c11e3e4acb52e6b34d701da68eb7af0bc5dca60900c12354704ab2ce39f67e6a98338a6cd7ecce590e6d40f081403ec9a9889271bb4372a5e01f700028c1237040867b6b60051a32e8729c0c4430478aa67ff8b6a93c627c40ea9d027fa2b30ee0e93b6f937e252955525c8091956bc31eb539b813cae5fbc04b93e4b8b045fc09ecf3fe79d1765c4d0160a1fffb36cb12e386d826e73807d004b3de97c96bd29829f5244bbe74f76118027ad699e2f66da9d318961e0a2490327f1d757fc9005ebc3008a4bec7946ef692ebbab986f7854fc2eb02773399106e21c6cd0af51a6da094df8b017fd943cc0c191df3b8dedbbafd0a7c3f74dbe0e4a538abaa4def3909ca7c6a5c93d37230237ab903e45eee6471c1e505fef8b0cf173108a6127e5017f75341f15d08947ea6b32ba5d91bed3861a83e6a7ed890c2ca1c4b1b48193b9b579e8ed409abd2b702d78d2ab03f5fb0c72758a28644e09f32a8099da4b6b05feb1bd7bf20720fbd5bb9a6dedeb822785dfca363f78ae0e53441b46d77d364118e5c3408f6a6675e1aef56bdb490f4dd95e91b7eccde5084bd5799f7904eebd47ea9f7500ef803a0ed0edce33921c8d64239c71115c61045b1507ab6b4c7efd8135ff7290f647a6fb6770dd44801386e542d6659ecfec0d301677a504c29389bea096f699ce3989174c4d576b1200737bf35187e6f0db0a0c16c7a6bc6b5ea500721d437e398b16bbc0818f28c47e90e2dd3d14a879ba0d14bf09dde41526f71019824eb410b74e43eb3a0e76677bc4fec5d20eedff200b91d506e90ed00adf535df746f22885715642bc425ad03894224160203d2bbb0ef70b23c6289161ad59419742f227294032a78ac7166ed7bd3fcf7faf7b2ed502074e4d17bad33bbfd31d8692238f43079d7d4fb5e92a2f74583ac0cf9be3df0e4c1c5ea6b5fb2fb41b6d0b2cfcb23fb0ccb316529ef6fb1feaa90025aa86b80069c1cc7a7a47dbd32d268f2afe7dc7135c9123a9a28ec8cb9db5b3fdfb2a820b7df382cc5b962c845ca92132d6e3effb5b7a78f12433a85e62693b2e81baa8078bf6df3ee9584ce4306668330bb7625b9f55e995640c839cff378855dc3e6605c376ba4201fe0c18777d2ff98c5f59f012d7471fdc94cb1f317e9b081e4b250eccced3c3e24f940d3c0e51f6fad44d9fbce3a3f41a8d44cea9c06a63a2c2ac03f24faf7af58de7d54f52c2db72d73a9f045aa940f8cf3e96718db99ead9ad40fc5ff99e00cc19fc20b0f4caae7f9f6b03c2ddd7c105d92157f094933c2d1b704a8a6cc52113a1b23b25f458097a0da568536995ef8e794a88ae3c88e49d92d08ad5c65ef8ec62c758a1044a8ef5d086ce9b4d7af97d69de2425ceea3c79939076ac2ae8cd9d2e178688fb1461bdfec2c4ded9b4d18509cd8d15a8eecfb8a730db1e6507ddd993c72f0e54a731fd6afa225e51c5c1483b4b0c6e9e51845cc1908b8ac8b8e4fa4f1bc62402c759b3c7b59cbd6d71baccbb1e883248f0db119640633994ace444814ab140804e9bde53c5a7d1279cf711f9c7eb3d47da09151e10d2addb78e055be6d7a1b303544a10a64ba02e2545eca5b6ff741d8bcc4db98b0de347f9a56997817eb627013a27c185ada17b4c027289263cc36af840f9adfe0d43b76ad46d668de9ec0ce7d4a1e33113786f935bc5c95e0d3d05c9f0e363b2055c416728b8e5439154becd4e8b3b4df2f418999fdefcab7ba80c3325a5c71003ddf1f3730eea84565b18cdce7fdaead6a99fc98bdfb182f90eb627b87ec28801f6c24b90d0d89ad5f90cf00da0ebe4bfd4840afc081c3aca0a661d93624e9102bdec1265399977d48cf0d3d47dbfe5ec96b0cd7c8b2fdca922390c653e49e70e73b23f64cdaeb13e7ead5e9b15d8122decea957f2aed9f71f7ae03d70c55640590ea267c661e816c465115e1a81d115d8bcbb950f7a375c530d7fb4a8c7b620cbcab1c3663e07803cea73f86ea461deab1c75b76248aea6152ee730a7d56fc0ded1735c98048cc316fb16a78f1a51a39468f4c1ec850d8c1c30fdf1eff11a207271485b81d8959e3211efdc037220bd1de4000daf8c4982a6f1c53f7e4ce230ce35d08b8492ae14f2b3cbea4e34ef8daaeec3498c6d6a5f7de06ae79e6681f079d3a1767d5c7bb49724d349729669a91e3e8fabbe565c2d2f5808a658cb0a906dd961a2806eac3e015802c52c6e7022cd2cd188bbc6640d4ea54eb08283df603d7219564eeb0e9de3aece5455c1f90474aeb37a5f325b9a4102ceed1b57dcf0e9f770a283f1545bbb5de7374ce4a751c131cafb631e371245b7bf3003f6bae06fe0d685e00bb81ed7d66ad9d73afd5b7a05d7a0782b8164e211ca69af3d89e01fb52ac96b1f71372ae54db73244c24b8f68b33f03c150a032969e11fb5fda906eaef3dae2226bcd99a085d1cd509b51d08622edb5474ace3fbbc8faa88ca3e0c5e9263a188698f82635b82eb665a354329a3fdcddc591a8a6eb737ff944cdf0ecffed76cc280bf01847bab2579d92d57627a835e1dc2c52280fbca07f849500cc3f056a6fc89df39055578335737c013350f3650811ef998617d713bbc1c1107f5b4ab46fc11a4764d111762c17f18941f8b706fee54537f82c34efed627690f4e83ab5a6b17dc0471576b2e85894793e635b2ecd2c008beb6ce0e1b1a750d08a36b81e710a24ada5990d56900937db3e471adecb06f9c78b20d8c0848f18009ad7e0000d383b3214aded17407b2920e906fb0a2a762d423a8d961b1405f790b5c549cae9be9294d3456642c225f646f35848b9ab332422248b1512eedf4060a40bc57df6fbabe3a57dde3382296f5e5c45a62da753cf4a42d623fb67283d5087cec9a68bae7a9ffcd6a2af4cac2957a4d7d895a1f3e51a0ce24c6c035a75b01e1e51436943b6171811e43d91329718041b64b81bb0f4bcf49076a4b8f8cfd007d6594bd937b6edfd0bd190baf6ab2a96679340c038ccd2f5194293a78e14d02aecbea3589f7a70296cece754f58d20d9fdd317d3b8b7455ae27e8c08df0e80afd423d75e311cb8364ab4404702b9f19d3be8c32cd61322309d929062871af0af6453c2a45dc99b3b723667358d64f205f23a4b9f60212f1464fafdf3a994e0f26efc55732992623186135bfb5ad53eb7b19c1c20ff80f1be94c0a68a2248108f16a1d9476e69b21d7b2e8e45237b4feea110347e266b5c12f904fcddd13810bc5799ee53c47cc9469fcc71d7c670c281dfdb37da2f3b590732fc8c1919697045d1a8f76003d5a7f1a0f519ed8a862cc4bf267a731151166089d74d1e033f00afece9ad7dccc774198a8dbafde6464f77498cd25f073dce5c6e302cb4cf23101590ff03ce9d837c1d853619f3c272e3f50868c7e4c7a756c251cd9c2a5d116071ca2012660b7498487d17fa2c79b98edf53b7fa77797271d76e057f050199909f343d5e9ffd52d98df35b722ae7a13c8b4f6c9c5e5371d0a988cfe7fa77a250cd7065407e6e7041668483bfc910af18c948543abeb6958928ab32ea024ab050fe0ac4f8e00b26e065dbd9bfad0f6bcaa43308f59b8e9ac87096073c5ddbdd90d14713af4b470054ee3bdd95cad78e22adb63f0bf7a4775ba69693ec23bbb7c05eb3dcce7e3ebdff42d0e828500bc6276a7ef75e61c992e7f573bc13004873907e7c620856a6d4d44aaf0e61520fbc2c59573cc01b7c5eb138d92b40da701980c1561351508a9a6f1a0289c2c2d547d63f294d01a4b4578dfea8b9649aa87b804998462bc5ab2d5313079f42f91ddb31f6eda7bcbbfffb024c04b25841b88780275fa3b145e089c079f41d6344fb73af2818316427e864438cf2b899b9a0c60062df1583e756a669d0dfa5f54f530ec2d6bb1777e4e90fe3c845794ff8c463b02cdf42992f50f6304df2bd9036fcb61b32abfd768033867ac46bfc08a90bd3f08c85b6dc30b63687948fcd4fe63391245ae8a83ff33d01a23c21d654f55d18a0256f2b28cc791159ed99f06869bd556621505805be8f151d00e6f0d89d970f50eb0b0196e9636d186eb89c989f5f2e3195d284a9961a5c35c194f87734bd9820d4a55d011d013948e7923b11d22f762459970cd62fd289ab3edc1f631a546da0675c7f877d8fde2341b3374c9947aac2bbf72a2b6dd59668ee1995b5190973d0583a51f60b09bd2f2668b47161aace20555905fb5f178d23881c688945686cc0056ebea714cfd75acf7d27038a622f8b8f319ec60bb44cdbc0a4e8e0e3b47560948a8d906f379380f1d4399c306bb0abf6473e368250307243c3fb62d6fbba50bf316c2981e3936b5518d4956ee8a460e33dcb4c40578a054ce0cf2c32a708c07edf12e999a005030e675a4a0d40760bea4f0dcc7e2cd65eaf582df22f521c50fda5e9236cd8f7d671ebb5edba063b5c6332e2284123c4ecf67760357be1af1079fb440483d44351b98c6ea0b3b72f44ad7618b252051e18ec27453e2994c4a05c9bcbc498113f6356c434f9f16a4f53c19993cfaa0a66bc2f2289abbe015500d27224389f9418db7a6dd306ebdb3fe1074ed221be9360104d2a3b5d56072440893830f08ae9942cba1f36cea473ca8265e87e3bd34de605e8344b6f92bcf970746ee8b5dd837f1ee0eacfc99f9811fe8ec8a2d7fb77852051148bf711dc6020c101d7ac16d28afa399868679d227eda9c93e5227f12309b17fd37e399ea743021b7ab1b6d2ee4f7fd4ad3af7dd2725fa6f4320c1464f428ee30abfbd8ec75c00ae16760df49b4d71f966be497734cde62bb7fe5bbba8507986b10bbc05a74304fdfc3e522287f38806308a06d86203aa211db6c1fd5696ff87ed7c3b759e86059eec15dbf66326e00a5aba1d140d17dd3a1f97e2dcaa5036d26c977d68aecb08d35b6aa339bc09d9e46625d1e7047649c3170a557b7751ce32b77ee74ea0d4050f1189a1de30dcbc65530960ba70d46c16e9ff73277786d8d2945dd193808b0fff56c591cb9f6462a6419318037aaa0bc356890f1f4042f7a1743bed497db100755dd5a649430e5799f055baf7eaff72019ebb3eb10d955c56fc6324a8278105b6a0ced3c827ff6bdac8693bb5d51926075053e957f5167850b7af7105f0a80fa69e67bb13f411d66f3f0ad07ddde933db701a1990f75b1fb5fbf1f93e08e606d369c896b30d39166544ae7e19c5b0a958de00cc2f403d7d4a722d2813f60a083dbed9cf6433c5338d10bde8d56699f89e4b75e255872e0b852de4a0d758ab0846301f7f3ef92152c9dfecaaab1e5b253ba9ce3beea6a333cedf9d6afbce19030ae3377565ea9293ad5885a62fcc86bd9ad24130f7cc2a21627f701b64d4fb0dbbc9a1917a2bdd7e386df85d054ea4b9fe82a71e52b7e9526b9c75a4f51e80070ebd5b8e999d6f1b11221f60971d1e8793d15fe9ab02823393a413018e36350fd4664808044c50c0c0bdfc87137bdd6b6afd6fe5d6527bd6308d31aec39a1409b7327171cf05654b9d80ece30e546d605e8a5092f1bcf7a6875ff29ed7f71a0126a767b8996965c873d09043b4fd2a26023981b8fc05a91ee91ca9bb790d6907415e9abffaa10eece461b2e4bc806ef718e6584f81053d4633cb53acfa1512068e4c07207ed936ed87679fee574a64492e938dfd738813ef4ce02c01b0ea77087587bd2fe291dd0adc371abb2adeacd97de12856fdf3e4033a12bcae71388c0341b82dd9544ba729bea1c0f3e9908536c00ca6e08babbe1a82330eeeafa8d106f8519dc2d530f15e2c8b8c7d20c705c428423c560e036040fbf15ac7636ecf0039b4ff7bc87a3a459e1ec7367f31ade3b6e7cc9f3dfe8405a8758d2e631e9202a33e79ae93762cdbe2458636ffe68c17633d3b49192d062f26daa713e179d90a4eae783c5ad5268d734d65278b26653d564eeeb580ea2123c1789b0752e57d07a3a71d2ef9b0ca3a29c962537b086ae41936be90768a1398aa4c7732cd15e208536fc831c214d9863f224f4c4e46badae6766ec704bf54e4aef06a720900ab045c0628a1d3dfef4d6a60e442a1db1dbaadaf0440b7833d670a515df088949309ca6e9b7f92b8f7bd0af0e2ddcd682ea421c5cf7f93df7fcb750a8747a3ee0c09f46ec265095cb46305364d9357a2d3b1bb10993520b9c54b78649f6e65c8d90c5e700cb96ec53b09cd057cd4265be224025d4a5bde024144e0f47ee09e2af7079d6445e4a156ef9819ec938dde87990854fb2d66c7bbbe7c32f8ec316d0a6d0b945b43608a0bed2b8f792801f40b24b9126df545aab60b1c0b7cf63ed885ec0743995ff7bdb55a930d8896352084cef8b3b94313db1bbe966de158464ffe9905bc08f9b7bd261f437bd476ea4d4672847de617d7c1910f525562b1b17f13360496144ee3e8c93bce388cb9e823b98840994692ed5d249e5533a53a581888f20813f98c7bc7ebd3d35e9b481eea89106ed53e1c0957f6a6598ba7f1efc6917c092647d11360121667ea91f0839749ab84f346a7f35c3cfc06abaf7186ddf5fd0a3b75cd676d0dcd6f2f56a1ca7a495b625b72307443dd26c065eb41468e703a07aad60f535e2e7f060ad8a101788fa3a9d0c1fd19d3472cae3a7e50013354d407ec00b9168e6dd72a87ec7c241e8d8b4a87b3c5d5630a684eb5e2c6fcd928bc01f0c195550db9795091c85bed3c8d12aaad12d3477a9b157afab9be4fae8d40033f55dd084cc234609d3cf469aeeece5d1eec7cced0c4f27944b2d58a8b9cde0135925319d6ebcb54325a34def61354e8c17b03e1c28f7ad2f3fb4a83c0f7dc034fd27c1125cfa56c1c9e334750c2162221e2db0840512dcb4e089b768ae2fe060993029aede6a16eac47326a5c1daa83f3578b58e52fc36ffe0d6034c6124b0bb59fdad3bf63b3f731d0a17344e9a3a9301b983639e8626971dfb2700522e404454ef5e0a40a579fed42c423bcbdf43c08ecb4ddf0bb647986640eb7f6e57e0dd2a02b03521983621cce0be340bcdeb03db94adc99e9985534276f4379fd04026260bed58930f997a461f24c131d5e689c4ed4213e28af4c28afab4e0f672905470b7e5d4201ff7c393fb3df17d16a6a744abb39e334522f283d401869fd8504dc00937235c5ced65fc1722a3ee8e6518ea38ed597ca8507eefe6a420a5fd408657fef85d221e008b156af68d2070e40c8a447afa34c90edd9daf5f9b032490e1aa549cb38764022680fde63bb08f49c7ccf42513349ad93141543eb8723c4057492079215faa1e457e41cc85d004fdb4b749dbfba20aa639277ec5ef44c360f98793539d94f2efff4ff9379d0d53fc891fda3df5a15aeb1265c77c916e60705d5f2fe58ab789cc981036da296b0dc311cc30ecf81c63479a68ca5eff5c6f201bc7e5b7e533f53512c6269a99f0a01cf95eb0276eb6f2b962fc8bcae02b38f04b23b890dc9e0cfaf7e47d39dd6ada779f5fe59230957b6020b755fbe3efbad0ee1a9cb4f583bf144619ba1de7b8f6f02b83ec26f00847454a5e73660cdfbf2048b5dea88a03869d90bd2596cffb17a074871e553e0e67078fb21636e0e13e9019e7a3c3239ac2e3e2137193d002c3fc4a593449e6f8a6ea25f05aa169bac5d0ed0babf744ab79d3dbf1475934fa02ded36b7b615f924d90388bedc13a8ebb201076da73121f10a3f12e13b41284a95469335592463f6712ec909f1a568a1fd045b2a46664fd42a8686cc65b674395e3c6104f8f072d6f08fbdec560cc0ccad0c27a6826bc5546059c982abd75c4764d1c5fa7d90a8ece40521564dd07f257e02554a8ec68376fd1646056b2887359c1001b621f0235fdcebcd58932f069a12012bacd07c0aa0b78da0fa952b9551e5e5223e542780dda3885a527d82a46399075ef0697fb6087b8b0530de6e85b87c430080ee788d965932028620aa95a7100392978c3880ae3412c57fd6bfc7f21821becfc968b4274398d4c08b95b1f9c909d3e63647833eaac1bdb43ab9598716aed4622477718aea434e56589038f7f20e4e65db948a69e60eb24ca706a932ab39aedec10ce00d7652c23cc7578f41d200c9082fec6d13f3d74b88cad2866cdcb8ceaf29d124516923bdd7ca930c2b110799a8601a0c066760d721bff206f60d317a9c4aba466021ab2a213c4e70a5f80bf2f77b6cc0bca573ca1d8b52f40e72df439339cc346008dd26934ac10ca05602386a1e2c5905a97f5f76671cc23bb6e6045f757998c424b6a1ce77ba0f04ec03cde885e541be8aa2f0bc40d1c067d9857e15ccb3a7f4c8352b33e52fd7edc907a84568508239b5dad1d95d23b8927605345946bb7a295a6cd84152cfec6ece056aa39f2e95dafc34cc7b550277db171d5131635b2285e24720478477189d030a16f939a1fb74c7597865734a88b0b749365506ab1d7a078f8ce2dc946ddd310fb5767372faebac4d4be7118bdb91fa58378a63eb703285118d2675aaae777c0e236ececdf98d69d79a414f99a98853f94a4ab5e13223cb1cfb519b3c127ee307c435745533cb3d016f9908f48bd6340c994a3626da9fa2bfcda0968d526ab60887446cc871a01451bbf7dcdc4c91f3d378a0a29d5e8f42bee345f83e5e6643046fe23634b2ff359783a20d94df69ab3a7c7d9b89719c59de941e9fb229e6e80cd71cc4c593792ce04fb9266ceacb39d758b06daffbbc42e4a247d75a0db17b01da1e94a2a1db829d58caab5d25c9c0f458c93df54314c7d7401a25e708d21c07aae4c196f17ce2ed2c9ed8fee96fb6c9b1240afcf52ff1666813dc06ca35b000715845bb324b326dd5163d08c23a028a790f444a79e51a4062df5b395ba339093e1449fc3afc61a5cafe3eba4bb86502cd0cc9232920319bd6b1a24de670a807f6d2969409ccd8adde436d72d92462849eaaaa04fc0e24880060d63bfb96c90a672ddc3af84ac3fdb64a21f908ab54dfb7ff5d0148ab8c8617d331130274840b5bb9ca4ddda77074f85060cccadedb0e8e424ab044bd34c9043d1c29cc904e0a40a07c7caff75383ad205f0c47a1b900b665f22cebdb653f506fac082fc606080db48a99756ec03acee8a9ba52f06fc4598a94e4cdda110562cd614d1a53fb0c9aa5a1939d3734fffb2b04dd91ff7212ae07473d4a8024f52f7f9d6c4de03b0e5239012fe1a1ae1c8fb53718065db6de23a8f0e3673897c5ccba578575528a0ad26be05b95b4b5a703841df12a1156a39e637fdb2204f4a6f0b8d940476f660f02b621bd71520e66c582b6c93a37c24fbf1e4076cbcc682fe2f122a12ac9ba0fd7560ea6b1c8d83a032e889a6a4a900435c1b9c6f7979ddbcb594da8b928b20767731943e701dcdc050c02fa46526165b82e33b37482355a3fe934996afc2d03830a73f490cbbe876a1db7f2b19216293f240f60654c50c94d3cd57e5b5668026919c7d8b3730af368b999e27b58a8cf2eaec6cb80116eed284952a288aee50d760f6f3b4ecfc82784961852fa301f8e71a668b2c5a9198cc302ca6599b0640cb07a49c71d3fa0ddb6997ec31ac9cefe4082433acc71c928d7f461123c4dd30d73a6feae954222c711c269af99ae40f048e6f6b1cb92db3ef82152028bec1b0e9618b5e0b0e107865e0012e3dab4676177e1f1c9a5f5e67238e6b77c9be2b60c5fd06ff0a2d07d388de31772ad047fe42ebeaa4a6d9e2814462a8627c4cc7e0458d4db2d36f47ae440dcebeb6b5dedeffb1eee50b97602e06f14c2cecc56570c3ebbe1256a85b1fa9682cafb31745cdab5278a1bf0c41cea863ee158e166bc004f39c387c0fd9a4716db7780979259d2db8458000df4db26c84d0784b970fa0cf5f7532bef0bb545665ee021172362e7c5a68e4abdc9e0376b323953b31daa0d0b0fc9ec34a2d31fc267fd3eda7fa743f15567ff331a22baf37acac1a1095604ea5a898cab0cf8dba8c20bfd8561827e8e4589e746b53dca0795e2c7c172a1005e4e552d34afbe881e9e0769735fdafe0d237b499b1b417313eb9df1fa13950e369238dec465e8a261ee192f7fecf80351b1567f5d8e5b5b9409127b9ffc18033e1551f94bd2c3d6c2b58943b0a335e78302dd72023ad77a4dd924ad5c55bf03b4036c03ffc45a5c76c4b6db45f2455a7f862e34638613fb85d024013225ea0df500f16cfe58e7948968079b5fcb588e4d9fe8a09afec7bb70294421d0b16206cd4f32eefd342a3d1742451fb286c629b4123c400b36add8914f3a3b0b17cb04307a6927d6731fe23432697368b166a9500697f50fe408ef56cc5fccd94a740a42b252edc5f4a8bea37eca9624bb286129aad3b3ba8ddfbc7f20dc1dcd0a430319f7ca59e8d4059d1d1d70284bb3c53a7dec8e2f3dc6f58a1f1e95ceb21ff90bdc35f9603b44baa41f6d137152bde5d994512882bdc71a80590ed2e3742a2c058022c0300523497f0941efb90d29a7d131d9bf4e75eb1cb7ca1960ee565768075172fa1ac29f3dc07a322752b8b5ea95e6ace26d18ffbfe4dc819018144c8c0dc49323427ffb782769c40a757f1f6253e40b15a4828b242032c7c877227bf0087e753e620da8dc3db98f83a1c76691ee92ec7785110bd86ea27f0df26da06f0f2c862d403ee510de00d36cf6c402b55474fa4e692a061dcd552f74e7c1277d05174e3243ecc50a10ebbaa57704f28effd9f80ff94a4119f2ac8ff5b1e0f9df0381a4598ad2d3e3d0a017e01c7c6b45eff04546ed910027836e316c92541ed104d4f47faf3d3599dc33527e144e0f5c123b1d3fc565c488166f4d0de870f9530b54c4836a0ebe17db565526d226e44d4bb53d77df98bbba7cf8e672fb35ab6d0c1b797983dbead52feae83476ac04b62d1414939bfa03061550d6843b6d0a4d04ad2e73d359891b09a6a7ccaf99198b572808a7ef52df9601839a1fa6f1a70d04681311c2441e0fa321d5b4aa34548a9e1012254c434818cf076fb2ee5e03f50d086ae081b87f1deb29fcaa1c08c791fb24f7bd8223d211abf914aa04a325da00911ebec8f4d5ce3d127e733c6143817d814810689d68ed1cdd24eaa0eb3ab407d1b7e80f7af2a88489a65e435e08cfef4d6058fcc75f939a834fe3260c8b3d0594e7a76381e08be446ca51a6ede565c4ad05a2699d01cc6c7f393d8a846d850c20f44e5d6549f89df33e19ad41a1afa9dc7d59f776707e5f2b9364ea15d4ae098c57448f7d38a5e063778c2c7ee96c22a1fe0f4d6c9d56b62deffccc3dc5360ef84c8e04edf660cc59621d029c355e3b0aef571dff402eee7ed4cecfb5ab2308c5ac8c492ba2a355283a9d6661a657c496d97835c8d14da3286d04b2bc874f01ee2e9137f43fe3439f34bf811e0953783cb371be6a40fca756c18413afa6a70af73758dbc3f2bcbd93ee32fcd504fb13b2dbbb2f10519438a7fb7284edd4fd0daa36e280d6a3c9e9d3e5042e99d73d921b902f2a6c863b065f40d4552c47ee0cbc9ca97b741bfc8e2fac7a21707cc801e68e41609754879e776aa72bf6831f0ad2b8adb9d7c1d6887a34f38ec0617155001d1e911b943324d309bcf562e36309ca2a29605219369a382eeaf2a14baf763bf9d0e46ed1da7624787659fcb1d50e8c14a9e67dd40bc9ec9e23f0bc4847e8573317a3bb8fd1a052d6b2e2d3598d0ac2c6b0732524d496d6d093f36493c6a82d728dff3b164deb5de2577c11f07e053e323b7e6cd815c13cd07469bca408f02318ac8d5e540a6cd96be9a2e4498e0447980bd9f9dd5aa803dd03ac334028b0eb4396d750c5ce461dbc15f9fa2a2b06e9f5710384dec1ff5854f2abfa8181fb5a676b70526d72a666bcc2db617fec0f695ea529fc0663362c471ccfca81a9e829546fea5c035f7e8dc412e8344ec90484e175d0986347f0c02e8b9e3cad695a985fa31ed708682a8b834bbbd427ef04450fe37337fa55084487d0cc9c459b04fd14508991410ada48c546e0518c0801dbfc8ebf6396eeb8d66f7b55251c009d7474cba53d2a72a1fb9981a6e5ad5a09620ae7c1d71b4b26a95989359a2bdb7a59b1c97d9bd4309d374acedd522f090cd356d695b800728b331cd32c37d9d6a709f34ae55a8c6fc7b97b7c506618b0062a4c349504beafb6cd3359ae2b27f8256c6597524e9cf287d9344ef9b790c804af530d75bca79d1965127ddcd1bb79f13077c0f37790fbe18122924817ad860ce1dff09feb06d942dac26d020a775f95fdd4726c762bcbb0d8c399216ce9190469540f82f88ea7cca298b721ccc3f0f5578aef36e9b4c6473fea33ca06e875012f2b7d40f2c48feb2e42009e8841f451db2a5f23ad6285ed2bac2792d77e540b58c337ded280fa8d728401a0d84c4d3186676c8f328a89fcb18c2086885b8705473dfabc0ac481a4bceb4c144187e1f3f2a3fcfd6840aaa08b389696fa4f5b0e0bf26019b574c5032994992e097c204fe2c1378b9f3b4c52e65a076b90dab6027f2f2ba47f96ddf5dea8f10b831a9f4ff092b020ba469178a7852caafa341d0a67004b91e4a92e5a18b9d0c962ed6893211ee012be6ce3a3c704ad4a70cb950d71c3481ebabb4125e224c612e4d6550c8ab47a7f34e1f10e5e27d8d6705bf30f41289c66ee07f12fb35c22161cb8d216e52c87bcbef8686038f31886e4e5d50627f78267978183f3aa5e315bd5e0642666cd56a1e759d1ca884d205c0083ff07beed3d67b4ee1c17ba52ca73d0ea56b967754f223b9ede1f3c57f439e41d930e04de6e29216be06691aef455607b2c17dd7537be9baec2529dedf1586f5e90039c8788fd3fc9649a081ec8dc583aaf7b1ae5146935eaa0b4c897be88ea5ded017a51fe4f4c3f7c2f8421108e0dd2a610a9a85ded2c4a0ac2894a6c7aafb4bd042baeb387495fb151484f6ed0200a2a5941dd7b2beb0140beca2592b75cfc7a0a69922de9e6f267bc6a69e05a4b00ac20bcbb6e6c986bd32dab570eb2a3051009c708b6dbff220c6e0e6e42c1abd42084ec51c4670ecb8e501d9f1e8e73a7380f973586555cf862845778794cb465481472141ca3fb45770a97d079e0d27fe10fd20500fe4c2386cc425bca4fd5a51202c38943b3be428753ba5daefbcd6a7e0580c20f4913eb9fad486cee11637ec662c254cf7d3f4e0ab008bb8bab1f59e306d2b417d3548975f449eeeb0e73038da0c7a051e24ccf268a3a646db8e734d80e6dc63ef3da73f8504976e10de048f599be95026ac79b1508b2c85b8a57875a0d8085190d2c0bd903dc7d3d8000ed22d33fdff4cc0b50741ab1078cf6a15e4a094a006cac29f11e33d799601103a19eb1517427793e90e5fe1a6c21a3758c120530ab91beb030ec86aae1438a1444d1353ce07c6d2af53609b186973c5cf05902acab15c573ae94611b0c4970f1ba03567a8bdee75e8ee200096dcf3bce228503c7b826a5685d4c50e06b6693683d9d3ba36ab89989accdb020793f9404598909167e6f5b83e0cfb4547a3dcc7dc81c0a76716530a2ab6ec2cf6802cd09425303a03dade9f4810517b471d4f086132200b5445c590b241bba071afec7f86e0f006b29329e7e64905ab8bb32497d8d929e45ac441079d65a76971d5593dedb8004eb42eed62d4fcaff967dadeb4a53bc07d518d487aa04a367396c51861561090e4bdec5bafe793d4aa39ffe2fbc853552589f3f80acecccaa175cdd82940e450596cbdc37c5b1c3598af49da2e509f88657aae137b86153f5d296383917f39800bbd46d03c138269f3f45d3559ce8cce7671a0a4104fbc7c1b1ca1c1501a7ce06e37a89865aaac6ecba6211b2fc80841f0ec430cd9df75df232f65d59d853200473f878957243105f545b1bb91afec1bea9dcf0c84fb7e117582ea2271222520940600d19da80f5d36ed73970cbbef20fa99c2c8fd9552dc6db8b864df118840903d310a43c54c946776d6b822deacb72c3836ff77c14a198c091aa6c2e6fbe09a8e2286691a9128ef32786793f4ceb75cb5378aaf74c92faf7d1452ed95b9b0b4e9c19c1e2196426f4ef36add5586ed3d65fb7a7708ca8fd49465a769bb7e609ed86d9020b4d4246cb53c84b63f93f77d5eca34ead1666dc19e621c5543e88004842bc12327618386000a00967376ddd74ae0e5a6dce99da58221204e8e81108df55619be27c6a64f9c82f97f4b4004df09ec82c3fa4ba43a91f1770d2a9ac0c89e5a50295355ee8f53edb968f8729d4267c7c0fa0a1c7bfc929acde8f861c0d3d3d1265f66d0594738f7e1828bcb560bb232b539d3740e7dae5e32f024443095c0d1749704aff2324c39c4b09c7f3ed41b333ef027e60a69cf23e82c82a6d0ec4045d7c0a4da39e3e1fa88b1365677be76f3dfa2efaf137c4c541d5f00a8d004281b3b8b3ec3b41112165032fa59de3c026e67b0a8bf5ff2c9f44356c54620323da07f40a5020981133c933ffc6cb61bf3700e2e8b59edcb79ef28545ed8703fac42e6ea66625aa8e0b82eb39c974816818e7b0ad12b2647da24aa98ddd240a7f6b7322379951113a9341fbfaff02716fe2f08e50304aae89388aeff7d44106448e0820013de42853b9553577347bf570af530e26795971b1aee9a75de92c0a310af83a427ef41a55ef224a49d600dba2ccbf30095dd7f56149a3dc142de20c9d91dcb36b467b6ae0b1b12c5b47831307a7ddcc2967f5573b589c082497e709ba99cda53215192fdd626a09c1390e83e7215c0d3b467aec4d4e2b30e5874809cf27af46cb2708e9c81e0e5c18d41a5afe263985aa658ce7d285cbe7845ecf02d110edaa9a26bada55c9c10a5ef7bc00a66d1fef26df48961faef442566faa0f29929d3c2a2feb9d75b1853f63c8aafcd12148a674b0066d6f26c6f167770d0f6916d0480770ed37fa4d6ec3ef29ce67bed101124bab55b824686f7a5a963f06ef285dc6831330f62f45eb895896f15c3009f85efd45e06babc216e52962390b80eaa9f8b9d0005716305beea0852185c507b09d391090fb75a502a877981e00811f18d1ea1c75262a4f538345adf2b98b2617dcfc96eeb09d0c8c6d627c220140247df13d122376dfe1b764d15d6faf70dfc7bd2a124aae7629308e576bb00817c69ac56cc8c835435efd1d83df31d2d71e6c1884cb1e8b1de6459b3c83a306a649ebf25fcca2f0df87c69eca7ac637d1c4747b13c8c4824a8b32129917f30a7f41cc4c91a43902ef40f8c6d1d7d9d6f175e5a70991c2968d1b62f31493a8001b697112f68956049c45fa5a56b01360ad1ab8ff8b446c77fe30fa7b3b643b08dc0790f8f8b87f79c5c1a3d9752c1ac373c93d24edc2523455629f22c8d6c20a52746944d3806f1f817d75e9f78f113c682426155b8a5efac535d4702ba35a09bec613ceb8b2cfa2f838701e22254d7a26cb144b5b697b3e2a740f238fdf450dccdaaebf4568d74349ff1e5e8c9d3a902d54f6937feb0b6ef8a13360a43fd9083abc2cfeccb41f575fe2a921dfa6f9a4110e7c37f1a1e78aca2f70602bbf9e0f4c5d82f67040c22f736cb0715559821b5f1587e664ffb12a1a8fe8b4a4c1c50b5aba150696176d6db6005c6287a1873d16d0d3db7b9d272d6da1adda52da3e0b49e01203b84fc55375730cfa97e3d24ea648442482a3da4d6629dda8b9819e02ebb4a54385958db14a9c5de6636d06b13af64026912dc6bfd9b339c2b0c6e900ebadad69a2e0e312bfd422c36277e065ff78c4b55b1ee67ec903926c8df117002cbe83c440ab86a67c6ae8f1b15ecda6562daf53bc2bf22bae1308da10ddb4018c67f590471f05a10159410b47657618fc3e70917a9379c84b1aa1f96494e708e00fdd82fb759a83b4e741171ecb6a698b69779a17926a34ebb25a04dc7f48082b8cb38b75418689edc8c2de92630ff6227186edecd1f9f363407ecbfa84f40752224f589689e896ddde22cc8efbd9b5a5fde3387f7eb9f569d5e3234181e4062e57845ff75b0d095abf284e5038d14ddc0bc8ebba5637ea7a6068c99dd5e105e781c7cad2ff62c2a06c866509f773865328d8cc85a86221693c4a3df3698d0828411f79573139717be4a80f2cb7e4ded5a68e3a95eca8963552fc8425f696068106db04216602e3f197a14998db49266fd0087d0761b1efe35279d39a9ce0002e6f2807dc15ffd8e5bae2e38ab7fe08cac64a7698f849ec44d39b7829cbcc0abaf33881e0417f41fee7faecab25ff830fbf4355c2c125f3c2c0654dece8fe0a9fdbdc49bc8df5c8086fd7f6033e4f45a2ee6ddcf1417a6493229d666f324d03eef5dd9de38acee97a42a32000dc89586f5c8acd42519d27da5d6d41f355500cbdc78451ee62c158b09dc2b901c92785ed550ea7dee29b04fde0d5ec993bf00f4b1deecbe41987ff73b0320efc8488e8627ad075872eed9625ed128a3810520e5f90bad449f005dc829ccb99e00517620295bef693658ad2c792398ffb54360832564d65ad58b252dc06461e27ab42f3b94a6a60c63246ccddea4774b16313013314413406c79ae195ab6f421694b95f9dce505d18d2a1f8049e9c50212b2d058f09877ee1dc51f439d68e6aba3a9f53bb450c0c13a3dba5dcedfa7abb04ae00eb81a33070e893f6a3f66a9b01d4dfcc9384b5a58afc607e2f355a32b318e40b78f5c44671c1d834ebb7010a3716fd99eb74c612f34ca5049e9e43328af9060c6fc468e1d0393b55f0ece74e85a57a9ac59a36ea58a9d24de69a6b0fc28c9e0dca02c44ce19e886cd19b33c3efad603b7a7bc527e0c7be1c2cd823d6e3c815069144b0854bd80f27fac1d308688b5340da080016139de9d0b43f263ba559cf08ab63ea84a85cc3fe52f91b04fcfb728c848888bf3878e5ed730226e370ea1a0c6cbda91b0c6fab36c4d95148c335f0d303a8b7ac42b0a20f80aceca2481ff50b68e1abecc9db05f21fb9fba04a47ebe94f78c51049fd9619db5998047525190a8e989f7b4ab4d003f0c6f6dc9107a1b03882b287c49259b0ec38998aef62830176d6524ae19c8e1a19d19c787a1305d2145831ba455a813b7e29692ff293170b918c8503d216b7f88f05cfbd7ef20bd78a90a1e353cd3bf78fa0eb66869fa8076e14e5084dc6bf07808f7eebf07ec1ba84bb965db016624411f415b71137f80192f28764d765f748746652f139a328deb75a7fb08b39512137f888eee5c70009a6129f5689b787054d399df0238a18429d60483304ecc85984f09b124de1e10d6cc0cb0e551109c6356d89914cfc84785a6d951fb094212973d76614b85fab0216f7ad1cf97673b032d92d9a5362326b790f5c45a0f9b5df9808385e1dd08502da953710f6c7e31ff5170333cf40d30022e04d68e917c459a9329c2c8a53c1071308733466e8ff9cf631d973c14838dd364a1e0fe77d6277351823d2e5b0ce047c6a1ce56ec2d126d47454ec6533ad0ea2b121793234ddf99595c80777144c07ade44f3d81b2e26634e69da5b29e52fd7863089d949ba5321b5faec4ab3a33097e440fdec037f97a6000acc5372b09011829a16dadf9eb7dbe5047a5ab28610c9a04267e2e622f742dcf884867316f4135335198e67a4ec034437b8e98e6c107cf0a537162d7a2f0249076be0c326f6a4b9c7c0bc050974287efcd3213509f0d2743d2399bb3e987ca46593e90513faae5c032bdfb5219ca96919bcd57f3270524c74afadb045f3a36e0cd1cf68e3bd68d0efdafec8b0d56f11afdfb04cac704c7365a596c169fb44ed7d1fe690c7150c82aea164557d9bb05ee53ad3cc14107e18afcd2f153733f296f977ba883111db4bbe58b6724346cad4b7f720e191f06cc98cbf3a023206972f9f821579d997a16ce8dd7a77e04324aa8fa8fa65f6e0c8cdbd1c34f64ab225f2c1ce17615cb049c452882694934bb23a30dc722bb0903715370dcf2cef4cde7db004276474ae36bd6d244597c0adc6c794b3e1ed707000fa82bbc98558714f01c9ca28c855e1a3ac2ccbdcc090287455383a74022020d1be04a2b5f7abe2850295f49e7c180ac8e99ac4d7f8626b997bbeaf2db4b7c089f445b634f4e51d96b9cfc37fc38fe84dbc2fb24c8372cfe04fdd54f03d45c0cb39d79222eef1e5706d32eae767b0a68d5915049a3c583e16243e0a5272a7002fa9759cb6bba23e2c2c041a87bebc1221dc3474d609803c213aec7866676ac0cd3b0bb9f4e360c5d0e9670e5e3ef6ebbe73fc5b9e82b28c0b1b53fc194d0940c08868b1150303566229a9192e32bb8bca9d3ff8e0d240d0745895f7e9a2d2401e2d70008ade16084f13278a6b22ad92af563fd9c709af36fb9cd8e7ebcb3110f7b2762d32a2c1e1d662486351ac820b32adcc80523db3405fe448843dfd59a0afeea65c2ef6271e374827ada480f037062b7a0316a1dc8952f6fc133199c280544a125457f9a001a45fa4760c763756fb9ba0acbbe777b429348d834f5866d0b367f5de43deabf8aca9682a161d204ee115daa3e22d1dc19c8a981b4eee804010b79937bef1f78f3cc581ffd9991f87b3349dcdc69c1d68bc673a6464e39710935b3eac50aa3b85fe3cb6ec793189cc617cd30eeb5cb57429281e69961121d09fcd189c24fdac0e7b9437a43a68c76a4f7529d86a872fc8a74b1b666044bba0deedbfacbbdc6811a305a8e985ffbcc4f75ef1aca4642b624aaddab2bca1be107a2c0717a976c1408a4a5967d14f369af318ef0e1e14e2056eb12324ef207310c863bd9421654e802e4a5f90f8899776a984de4dc88c43cd69cc4163fac367c0a3b3a1eda93a52457409e7c9898c3d63cdc7fc5684dfc68ae8b75f11c654cd60bd595e06bc45f6975304f88264cb6ea76fb05b4529c246feaaa28777dcbac6601f27af7b803bc7d2f66888772f4eda421d26979fe2c69304916c26e226a01cf080275ea176e0bd2df44e1bdcec64bc63141066b87d055ea85b5344c08c314d50b11359d2c67d80aceef4b984d08234a4b881895109df15412f1ebf5d4d4355d058352f701c11756a476fe8559f6b6b30307fa96ca39f8418bd8268550a1321108897aed8ab31fae2c6e1aee769577323e33bfddb05e4a066fc704c6fab6f3680b2ee0891f61e1d827659df21ac609340e17ebb73a2f7f270e54cc65a84fb8ef0de481f11a136fa80c18b7373add3037a737767c8cb30f314d9eded61b35813f098ac2c7ce53216a76c6ad0ac9c20c5c2f9a9aae534986f6efb26df8153f4ff102ba0ed11b06bef078241b195412f44bcee5633432ebfd0dfc8791c51dc16cc002553b32be3f0879e82607c58b38517c4dfb8acc6dbed3f63bb0805f7854f6200a63c90bfb0e86d61ea69130358a6a3d24e0cd991d448dab0a8e24f2cde55ac801765b7433b15b5cdc2e351861db5d4a01a31a8c76bc9177e01871e7fe832d540f82b1617ccdec3a44f6b2d610655b10c04063cc243af1ce1993e3545d82d02b0a9ee0795ba84d22eea67d6113cfc3e756fa80fb4dbbafebe7ad261b0c7d8c3104 -generate_ring_signature ff48702510ab57e5c8d1b6e81acafb78f7012e10e03ab15b0f1827ef34042dd0 82eddeb24599f7945a021098f99c7a1cb6edfeaa937e824e2f529708dc25ea77 1 c2e3e864568b78db05944261bb131881cd05c85c69033962d3c4f7e2a25a98f9 bcde8c2f9402b56df766a132e18cea4bd2d2c70cb537b599c2e829b7e3af4700 0 c571742a37be6de025f5ef62548fd4eab4bacf2c20aed554fdd33d105a5f4909084bd42a13e701c9187e2981482f72d75acae930d237f7f56afc63826748a30d -generate_ring_signature 417b4e63adf44a5a461ff2ff030a1757fd342e7f93ebd920c25b4c8e6d740187 bf738513972b08c9078a1b21cd261df99f52670326ec59dc94279e626c0ce9b2 2 f204e5533b350d6232e62308092afee62c4ec0f4fce45938a2bc17ec3bcea3a6 50ccf363fd401389de4f584b3597c3893faa78316cb8799ec6c20a37596677c4 a1bffbc16adc7bbed80ece0ae2b4978a0b3325257a96a7e3c3c097584d096e00 1 0059dcce443421cbb13458781836a8ab1d64451c83e987d7393abc34c198fd0a79af78da553af13701594af9f7d49d9908487cd2ab7326f2d267951293be9f04d0b0649f4c357a17c6b74eeac81b0c2925f513fece6769b735d5e1fc91115c06bbfaf5032d0055dedf834e13f9107658a05e273237320f3236e0b0237943ea09 -generate_ring_signature 030be7e561c24a2c9a75cf63dfe5bbb49addd7023362f7688d24ad9369967bea 158ff2e128941383275c97ac9c0206450dd2f16dbad1a0f30b045cbdbb76c2c9 5 205a0dc801db4e3d217a9a9e91bad16ab78ef983f799b8364ff9ab44add8ad6c 0a6f06fed33891ad9f778c3689968dfb1283af71b48c1690dc941ab115ef4d69 a19641d3c2804dd2450d313d1ab0ea95b16c4cc1cbdfebec643ba05604625dbe 93bbc7d65e865f094d1f7e7cb5b07f734016648b7d3e104db00b0d8899d19e8f 68e6e3257e7e974b4ceb1f7c4461950e60c1e5d1d8e935e02f4116bf89f5d842 0e0e85834add085696b849ea3ac806740cbbf7e2dba4002934fe022efe25f60b 1 7245e840918957f7a1d4ba91b88f77b7396af9d183a5f77add127919d78048033fac954ac51d5eafa75871446e071b1ed2c4cec22a87f3b764b1169fa52f8c0123b1bac004fa75a6960e34171fe44525ff7a091f47de2844eada697cf0697f0cde7307fb9985576001673431b4512d11977bf10185a75eac5c7814739a77680d7cc2d004c200dbe824bce46d848de436be9f4d776e628fe627741d1278292b03f8d325e471b62ebe4de12ee5adeec74333af0ce3ead8dca6678c41fd9c07240624a0b7cb1a7f0d12e76721976b45bafd3e73994edcad29cdaa468fc0b538a407fe640170b06423d575f761b003ec60fa058a896441456b112abb9f253abcf103f5519579906e0e0d11b7c4f2d93f8a5a7e50beb53424f04110c47caba39da10cf3bf5d06cdafe550fc5e9bfaf9f8b82257197b8bcac41f3186e36ae6f743e10f -generate_ring_signature 676d9d3f03cde540717f27be188a1f3f089b9de4264a66906be09441f3910378 1b768edfb4e27ae223a513c913ca9122e2da6bd2f88efa1718149068e6a000b3 1 fdb9b839c1c33c111ee96c1fba14a6ba9aa851737e89a629ab9be2c215c1bc5a f8eec233f256d39bf9d6b86bc1858e47748c5686abed539c8c44457cada7b202 0 f8c2b4185fa150b774a465cb1abd8d82f738ea939bf12cde5873f158674f2c0c9e68745b4093c9c4adae7f008da842cc62d5112a9714a6016efed540e8e11200 -generate_ring_signature 6b1284d1a3858c8b7932de78cb05afbd1d97616929e31905ba975dba70d7507a 4ace77c3426f132a65348c8f2abc450307cfbb5aab6b83748cdd9da470b37162 32 8e02737d48fdf2acb36863a26c00930d9d7022156fea101760d6f178ce30e0a1 7dd3e2d772fb4626db9ceea8dd6e455d147d86a4aacfe0f8dccba90937dbc2d0 413747d1db30c5a9e8723aca5d8e2b105c8be221c1bb4337222689f184c4a6de 297bb55ca843b5b0b291ec07dc6af5746d7348920fb57138bc2a6ae208239223 eebe4b70a7669dfd49a6996775053955a4f03e98b74f1ac029c1d3ce1579fceb 62d36715fafc49f61c615704f85ef67bf6c82c876abeb029e875c3847e9e8d21 c003d25e8b139b9a40b1e169a8006e335b7cae4a3736026a4ce0ae55f1d1154c f89a5190779476851312b1297ff0d8bdb0d6032edcbae23b48f9a8241fb7b54a 22a64918ea6e8d63399c1837210716fea97d6022c3657c869d7c9255cae853eb beb2dbcc8a4ebb25afade190a4b396df94791d873e34bf4fdd04f0a5a5f86753 51779058b46699ed76f017f551b53beb6259dce7238fcb23f62553638c0a0721 077f6bc7e6eeaf2cb3beb60ac00eca6412980fa9eddd932b9a408edca4981ec5 82da8fbbdd764bd252885dc704945be6465fc45cc30dbe3eb6fff9692ed05210 1df2acf66b87bdcec1b2d8e454e73fce0e6edf521d9c2ce5d8a0a5d8e7c4bfc4 c4107473cd90199469995334065883df47143ef21704bad6fef7db88a11e4799 616adf3cdbe84f04cf90b894eaf7a3e420d8606b3f5638feb3dc57684b63f606 180972d031b5a05c43c1822aa2f391de533069c98bcda58962e8f124a5816f79 e7fadf78edcf92bba764a30e00c626eecda4fe7b732e588f1fa668dfffae6425 369a2989333a13a52af60cc2400536292333a78735b5e2cf12ae1405d8e29c70 0036cac6d8ce1a2a5e623db6f9dd8eaa231184c85c1b7661e4a391288d82e7c5 dfdb8d0684014a38ddc314d51c948bb1fa93d2f491a0cb71c0119e7c3c677023 dae655dc8a1f360aae2d2f97cccfc95244703e91841372ad4e19ff62bf7ef77e 79d75438920510587b7e8935b56e1182a6b1736ec651a624385215d8d461820f bc7705ff99d0becd021efeb88f032a9f1a8afb6e4215da04aae62d4ccadf4b2a aa92d90286a2e97e8deb50bd1f88b0c17af828c037f5d6ca756461ce5aa50947 8a93227c8c929fe5668877d141cbd701b9221505a0af068d7de66651c3633b1d d8c07205cd999930a681e3df4c93d0f0306d2f7af98f54b7b0f9cc6c856164f6 71027190c84d4ecabdf9c6e5e4e441532b31da01076940d9ec6f8212674954c5 16fa8ffdb9437d1ea18c12820a0bdedc301adfbd3c525408784a5af4fc6334d2 dcf6a3df67483bc91e2cfcfdffbd967f1c50724d15754c08f46cb3afdd16775b 6982f0182fb32a36fe007faff4f28c411a686f97a40b44c4c825e865f9487400 2b4cc6cd754512d8987ebd79a820b681787aed6cb7bbd7cb5ef792ccf181b228 0b9d7dfac5f2c2203dccee3bf001cda08ef2b12f8cb201e2883df36f61970405 14 1e166cb3b92e27e5a7d5c1739e5bfc00970b2e6bc90c460f760b210185dd2d077ebefc4eb216ba10debe0feb86e5281506ef231fb49a4787a946eee0cf268c04bc000cbfecb4d2a9923d270ab34b9c8222f81e63aa73574937f59a8ad84194026ff39ccd97650182451124dc48a5f30a7f67187014e976438a01eb0881435307ce236da50436c9116e13d8bf8096abfa94cc7774fd8dfeac3e741e750a5f0d0fd6dc9d52d702eead69b11c32f46d3a739138f27b0533e9222a2238408c31c5063b3e64285824714097b78f9c9bfae30250add81c11677bb9f26be3a2833e5b075cc3f0559de4b3a415b0a971c36da19a7978e85d905d21a001bb19796130b100e5f3003f382a9b7a56ae251a95bc8dd31ddd20e4b8d1784573a089bf32f50e0142f258948cb600b75d719cd3ab341ba0529bf2ebe56658d3c787eff6619acf04e9df5ac6e5376a11f22d20246099d1a55e6d74af05c861a5292671d30a25d7088abf432cdcada442d32cb37852c6b5e885b460da78d2a19ea030f1ee4a018008bf9a5589a284c7c7055eff2757f905e6eda950a5dc337f2bca9fe04f28d7260e4f8574d3b62c2cd5fb3f6f720ec8101c18e1de5be99047836780751fafd41b01aa109292605e1913d02b300d263a7b1b7cb994d70e744feafd1b288ce8b94e05e69059e098b42900529473eb831354436c56a5431530b172c60647491ffa110a7476bd5a42e8cb412c0006df2ab7221cbfa371fa66c6b71f595675887d809c0291014a92c329c42e2f71ea18f45acfe18bf398c96be94e1a147e79f64c0030075e445a671121810b393481a7606b1fd2eb2d224db7d29b01bb63a2c9c865c70d98f598bcfc5853357362fe888b4a959d36494787e73327e5ab8d8b13db59d707d78c62d721f36d6ec561a17b651f52c24c0038533fe190ecd07cd3957aae5f0e81b559588532c205fdc48fde6134cda60fe1db5630015d3eeab0eab1e17ded01b3cc066608af7444e4c54c189682703b0a59ed8a89f4ba48059e5a0cf6e2d60bbc2706c04cb5ad2ee6f2e1a01606f45fbfdf7942490440e0afa00bb01cf5a500e28635216f752e78cf0f51c7958f8c8aed819379a3e839d4dadfb55171329401334a54ef085265bad5a5c17d283b6043dbcfd4c720859b167e704c90d6f7d30de187b76df69c5fb76c2f5722d119bf1fc9117ce46f3cac75c71704e9a41fac07b8264b498a1541d761c7ce5ae88f69fefe807e7afc836aa0a90e04e8a6005d073181a8ab532c2887c656497af3ee9be4565c47ea71fbdd0c10b89f083f24d90f46ec587325352f8beec540f36239132d9a6bb2ddd6f5ca2d74f1ac5cd9a4660a47a96286e94f1b99649bd611dbb061b585c214ec05c6f03bcacd5f1f402b5e091ca3e292394394845af85526efa20c8ff54f96510c2eb1c8fad2fd2071e3e208e80d5d6040a7c04c3747d8be2d744ba39f40a90aaff13290fce0023553398b0e36ca418ff92002973f3b5d520bf0bed2ca00ce79d8d89c0ba8394b10ae44290a397f3e50a86a64e47f29db45d3e3a642c4bc8b5ce2f8ba6ecf8dbef6f301140a1203fc5d9d76e9a82ab32dda8b4159c6b06b314f96fadc33ff58844f55f30e0f46ee7d6a0444ece436af6b74efcb447e50b48fa80c1805332dc67510b01a8e076a44fecab6831026cafda074bea61ee36ab18a6b2885faab940ff25dfbd5880a4fda7aa4c607d65ae4fcc91f98c8ef7fd31127059734c580faeae0c9a445f10df39229869914bece44d18bf3fc8b504a06c4076aa7054cea1449853c2f77eb0304800ab486a51aa7e789616ce7b1f2f4576d822e7f0a496d81c4e989dc169e04697a53388baaf75b8b505dfebdf2f348438355c026a6cef96574ae956da06f028f5717ec26c7f9c8794e808fc03ace6921865fa532074de6e2b86d531db04105a1f85b24cc8159b59043c558523f69c5773efdb35e4b165030c5494d345f5d0efbb419664861b37ac45d86936fb1417e25427f57fe63a34668c3ea856621bb038b79b1becf4dda51c1a7b9bbf561fe2fd47b49298afe0591d7b7fb4db026e30e1b5c76e3f9d62a3f0816f63177249bc85f6f53cc4316fe1aef1c987834c10c02ce8fac4b9cabe0cf5ccf69132f42a09414455b3241f34bc85aa4a3f06fa93c0f6589f5f2594172cc802fd03e04a8981c7282b6a5b9ad2cd932de76101582d406be03c9c20466bb6b3eb78560bfca6457e52520235dbc114268771431e8d499090522a2b60c769de3baab55c1f7ab1ba0f2e237f8b0780d47969b92b7f42d500e98aa2e11cc0ff955fabda4769b52627c178c9681bc8ccdcdf5983ac1cb851b05ea2d1619f8385e0a23e4ba3c993614017d74a7a01e16e68cadbd29c9c9d3fd0defb22b68bf6ab829aa1bf78ebc1f8d30e6067f005a8f75b0fc6e1e28bc0e29080fe70d937af655e3189dacba1528775183febcbae8b3fcd78523eb6f10537d0603c4ee2c39f1bf1bd379092d3cb42b4580c5ca3b278b018f8b3c9fa868055505363f9bf5d94e5b55575f35daac172617c9672ce781030246549e10f9a8449e09a8259e5d76d90f681564a98baf2316925936cc73191059b478d80972d19bd60b6be7ea56a240e808513811ab998b13bd170282fc439b421f7c1da5f497c0230d29be81e9b091c1bcc8ac92beb571851fba0f23abb783b4b95b1e57e943f58e09c2745e303ed17c24d99e319996288e69c52e1683c98150dbedc3867f74333503ba6f2dd8e5b8999772fedbb111c5a6c4db6d4db757a6db807599e049f78dc20e2322b5859278d3347c663795a6e2f2042c6d1a3af3e2d7462de2d17d5310530ddde5a7a0304b4c0adb2d80b9200d143d90c3f5b93d7028b0ea9e2779fb0acd03 -generate_ring_signature 2877c2572723b6c1de6b392c1fd7e6b9b06be04a33b2c87ee27559a4f440d301 6be1605f102b685abebe480e495a6ee11b401fc4f8ed2c7a9d59372677c51133 1 43f61d5fa46d25cff1552dbd032d2072537865b23f7631b16355d6e04f57406c 5768afeb110865a5c863b330dde198fee80d775dbd33ce56e639cc750825e60c 0 9cf705b616975aff3b05be50dd3d0bcc5150195f3f9d9aadb47ba3fa50234d01fc53b9c7b464cac22c534b2158f4478ad873ed37359e45cce80fe5fa0e04c80e -generate_ring_signature e85672e6b328687d087e5e27a222d908708082526eab8d9d1ed9c741309ae642 1f47514331ac0da99d0bb8bd185aa97b127a5c428ead41a49a1df9795df80a21 9 5d81cfee1bb4c5d9f12691a0a7f099897a6d2b5db92a5ad6e4f16ddf67ed1546 b352738407c3d1b642ec14ce4e87783c3461995d4a759f213a302d2f6644be10 2561702e1179fe38a61701da52b2c6470b7ae222b7be59aac2a4850a00a0517a b8e838d471c530f3c0eb2b6ef7a5e91cba4fe4ae976f5b9dd34d388678ac2c7d 00e859925f2b79171a96df93368feca24d15d9160aa005d86343f69b2147319f 59a3b6c9ca5cbca2b1b27ee13a158565ecff5267bfe407da2bf94c863afd94ed 51eb2c685edfba3a962e435aae95b54f866eb1429ddc3ffc79573c076cf4a638 bccd0d7379c0efd79aa6f27515826d350b8cbbb6713802a834a1f34f49a09fb9 5b7b1cc1529d4eebc5fec0b0d478591c0bb0bdae91eee28219841262036a8a60 4442e0c6b2c4e6cf3f5a1a33cfd304e8fd6c0c1cd8047061a19d83525b50b30a 2 bb40985067d244cbc7a03a5624fdc0a225c41eacf3276cb53ed5e6b108a0fa0c55bca1ce5b58ca218e994057375ee04eb95b589a3af45033316631483ffd620532f80ddfad405303f9dd68dd88ce5462fcedff7522346a63eb1fbb1906d7840540e55a08348992783fdb36c530cffc272c113d96146cb5d73ba2c5d4f25b290bf9c4bb91440988ab2250a4e5ed791198f73684ff30e650aea40c46235e08870359c27fc6277824c679a91013d99bc2ffa5aae07bcb2bb7ae6921ee8bde25cf0c87b9a43d8cde69813cc46378e464c2ce1fcb3c63df8a26fc42b1fb79449356051e019daeb4533064bb7f62539e2f6e9631acf12a0049e19a5ac1dc49b5a4fc06e0e5b56710c89b6905912b5d93190d46fd14c77416acd56177225901d85f160b6f772504adda9dfab49ea1aa2549be7f7357e8c57fd4b950a051f1a448bcc40db7bbf6f8668441411c756ae84cbf1932c95891859846cc181e28a746310b1807139982afc11cff2962a9410528a4c73144e56109b56fbc55f07075ad7dc91500982f633d6a0a1ac393ab8566266e05780f949dce7e3f438c29b63d6a05b69d0c6c52564680830cd0ab643cd730839bf5b4a3ee623e56dc927abb9a322146ef0670fb48c4adf0a26aedfe3a1441808421022c9974e3136097a07fbf2b37fc700a557b7a68d8fd39418b74499ddc7ffed68dd834da6af967e17672685a06f255015b42eab4b8669072a7bf00946d8890f2e1512645fd1885d845fd64060ca9e30c6179f5a48ae2377ad79765a25d458076862b56f06aebcf10bb15f634dbb86e0b -generate_ring_signature c960ac5d7f59051cc139e56674bcc5a3f0fd10f6d4525685133ad71da9505d03 d110909f0a37135c260f7198ab302d84f9980cb0f9fcc863b9865e0fd76e88ec 1 5cf0e409f6ce507bb1d619490f4501603aa0b5b11c2040681f5691c5e7af95b2 1f795ee966c8a672d15337e590b1d327632928970274de649f6d9a6d71cf8906 0 13f7c75611ca96fc467fd475a9c79e68105f7844bc97725f334c1b33c6099e0d281461587ea3a35f50da6c84d73dcc60ca22877aeee26a23bd0f575f8df25f07 -generate_ring_signature 00480a8cafb58dd4fac3d4e0b78615ed33fcea74c26ad9c23d6f610dd131879f 2b7fbd562eacf805c3bf9aad99e90ecbd22a521dddc5030c2777dc9f139219d8 2 66f670b720ad778de82b255292fce0291b5729b4dca211c9f0def3ecd0f8c143 58a740373134543502818f2326766e0188b935de010814e3e61fa44a2d1944bd 118bae54eddaa218b8e7b8c9079fa5e42666d38b08677a64606708e0d6574206 1 f5e758369ddf3a20458883dca10e6fd1ef5fa3e19012869752c5c30a4d022b0247e585ecfe5458932c060abb6165803952efdce7c0333bcef3823947f0041202c1adb6ba852ea57105a66a7030acdb6e3ea854d3bbbc8305b6a2c0ae1fd5d20436df8f27009e3cddb943c75c3abf35560c92beb367eb7ca344e80b804aa62407 -generate_ring_signature f92442a7a535e39ea68305835767d33b4a1527539ad2a184188fa473673e583b 9f256d40277f781e035f26613b45660acd3f41f24a8179b60a56999115a83d77 172 fd881da6976a55847f9590a313352a1a24a7c5b3c8c568bc37b920abf088ebb1 ce48a50c2e6ce8edf3a295368278eec1c9b73b56435783886ebada20591770af c1533314426eb66f59ad0f7469d65e8019f902b053502e03b0dac849b7262414 284bae0be6d2bd1ecdfb9ad7c6857ee9a9ff6ed1cf50233e87a997ee2bfdf909 060002eb3f3b22f173e4ba9a50b9140c3cd5c7f7c3c307369d3c87466ba9ed8d d7d32b301bf7bb28ead0c666282fd23a0cf445219423997f8dd30afdebaae9b1 56367289441de9f8938588385f9a3a9f4f9a63f871cd3b72c471702b8712cc2f ea5c570305cbb52ba6d889d8b7a340e9335972d520ae71c9c13832e491567464 071eac248b47a2c4653eaee65e55030239c5a4477254f12b2edf3b0be8367ac6 3f3b3221a7fe3ed062a4d5af91c3246cc7d2bc30e9d3f85947970944058d6be4 bf078413d79661eda1d8774f7003a5816a93632a8cfba1f3730d8a127e28a9c4 e889d95ae3eeee41f3debf69be540254c8e68347e8b2d19d96b7759dfc14e377 f5dc25d2ce32defa29030708ae1d6d36673f8b41146cf3d89c7aa2f0f2b6e313 5205a7793f0a4b0d6e1e9257dcad29614c9d0f886fdb16ef4dfb4fc36af632f1 cc33629b3226d7404851fef79927fbb90c1df81cd95b7703e38ffa526ddb206c a37b372b2c312ab90f771dff4b2053d95ef3099c76a6fba984bf916b29b56341 f4044c8f9a9801a5a04a157b7c9aafe40c019207ee66d29fbd3a356bf710fe84 dee92a17e7bf79f7ba3d6b6d01b213bdada853621087e1ae1247b115a98768ef 0d696f6a9682a9f2d4c0e582925281368f16efb8c3cdb700154db78114377dd8 57728b15184b062d286f942fd3a163ba68cfca54ae3304e739ad3076e1cffce3 82ab1f9afa7f7f77289c00080b1f98a487a14a2f073cf36bf96a30440f20ca52 139c5e0efbbf51c5af2b9117b7fa58d6412d46d67eab6997857701230a94825e 4e17a9d3cbfff868b70ba95e4243a2da2df968ae5b0b67fcde527eec0c084244 923577965437137b6a933bd61505ccfffd228aae996d25fc96ae9db5b98d07fb d841ba1679e8ace379aada4547333b839b808c514f6d19e38895ab48b1a9d16a 8175b06c4a1b58b90b367aec2e81dd9c41db930a96b3e08cf06ef9162a21415e 818abf49a345f93c1275d55321fa70cb50f458e9e67fe911aca8e9a4a253a34a 2f521138d0edd239f6e54d5e4d94efdd9749f6b520fea327e642a0fbee8a5c04 9f46910d6268a0dce884a538bec4a81c3bd550b739bf5d02035174d1b6707a17 64656d4ab3a6f1c92cd71ddf45659176f2f37789d0338dcf124b1e7ad1030f71 f8c3b190e7a9091781da8ace4b6019b2d68f05dc92e54960a1a8571702b9c77d 9ae039b7b615745cb4e51634a5b8a10484d73b81a9baf555e11efeebdb5fa24a 75157319ba0ed1b0208d3764bcfe60cddfac56aa2330c76439923d245bd7d81d f997a99abab99f25b18d72b7d5b73d4dbc1902dc3e1e01d1fd0d12b633772533 aad3267d5679ef6eae0d094ae5df02b5228d3257d9a1edc0af7aaeabdb9051b6 1eb4d7c23a4c40827eefad193fa0c778f1f84a07210edc51b42d3150048f90bf 70e8bc3d0136207ee441333ab2f463ce6a456198aa7d7acf7d1c966f89371bdd a6041cc33222e72a94710efa35235937f843d4372ac73668217849e4f908f4d0 265d61d8b2272325b2b69dae4f728d3fbb3073da4c4006d49867abd4c1575806 502d8c2e6f00025493227c87f12a1319199c3283503748b32b23af0bd20eedc6 41d762110cf7279095cd2116f10ec88f99520c860d3ced6ef72ae99c53468ec3 1270155b3fa3602160171da175a36e4ac52adf90a7f52ae84d8be8471863f681 f4bfee7e8374801d097ba1199f24c2750bee92f29f5f84174200dc8f25234023 ab8ece6cf4ff2b2bf545f8c57791685361182df280ff1bbd925e8fd3c16dc329 04f1c90fc0b45e41ed9cc34a012d623d0a71e726eb61a157f7e5fdd331cfea20 ee65cd19e5b17a0dca4706ebfaae7474929503051fb26bc70c7f78f986311e2e af5af1a0079b7b62f0b2eb03964ca314a21e0be6685c1c6e6dde44bd4346b943 96b9d0ba83bcd6f976a964068dafb5329d349380910d3eb67e550fbdc46ab884 1956cd083ace90b869f0fc7041362b47a5317164ff30d5d4597f205d53a71474 402f7334d4db7dba7b1ca8572cc893223cdd7fa520459080f70cefe98a093635 ba1053f093a6d06659433238b107558e26a28d2db942a84d9747fd05839f5056 89b8acb7bba7b8cfdb045e158837a4beb36c65fd064fdfb2715ac78a95dd76c7 7d16961a37a25cfd9020b44566facccb9ee31c8663c3917ad7ba0cfdee07ceef a731d84951d200dc2dd94d18c77712c558bc3704acda6085d6bb46d0021dc7b6 a14c510bab57377909416af4573bd3b8a551d2d35421a706758408880bc4871f 59f8144bd5690d28f29907c8a958a1a70f8bd305a36162e310f04f2413ac55c8 0d56e71a5e55aa4fb86756dad3e4edf87f3a01a81987d1e13f86099dfc3b9494 5510c0c357755864b4485fa43bf7dba01d33052918604ec41533610130dbdc35 9fb86f44b481cd504258ac3975fb5a8534673fd77765f5ed90cd310cdcb4006c 3298a93187801ec62be93c5aa5900916e6ef57a4e19331597b82dfb9c00b2d9c 55b2800d41d2641fd75a214efe80a565fd5f27410783232f2a44f7a774ef9f2d d6e2629ebc2413e4f294af37b2fd948a7d1d71728fb59794fce78ab173f32a5a 23617f3efb68af4f70ba8173f61b95356389c4cf788e3b8f72cadc0d681975ff b06347741f5fdb84b017a177fd904913a409c46e97cc00440ed449900e9ec2f7 e88ad1d059d2d9aed7150005189836ce56dec709b8a22f63ed2304ad6c42c87a 2fcb144f29caa8d4e19a07f977ae6917e8fa0c830c52b0d4748ebba4801edca9 a432e7e9cfa465440d9a0b1df528d974cc32d4c7f25ceb53440e5995e9b8feae 0b0428e6f8ed8c90dc54832b15a20fcbfc82cb5f908f36045b1dcf6b69cfbfca f4064a72fdc46314d3652c0bdfb6f08f1b248d5c747998fe412cb86e97c6d83f 45cba132b9b9a0ea8525b70a158d531e0f4bab123078906e1257558b9904a24e 9b41948b3d7e5d4636bb670ce3b66d16a939f34320f0317ce20ede8a9651b700 bbccf33c2d44e1e59f62fa8144edb353961bd522a1e380f95ed006172c88af0b c04093b6c7bcd3446060f9c18cb62503acd1b1d4d5c4e7818292065bb29bdb88 5cb1f7658f73d542bfe7b10a988f45db4bb9d777ed6b0f780049ae54e82b6ca5 3a8cdcff1cd09bf1ba1952514ffdb00b204322613221f6c6efb969a36c164adb 03f8c02d0dc951aac9f5b70c1c044c06c9c98b0f61c5b30d6045f9f1a12c1fe0 0e7c767d8a26d57dd35d5aea58aa9d3a6098dd8ef2732c66bdf0e183ba23f6b0 1d6d4190d0b0f4b8ce1c53f1ab48c601ac7f4c487834f2ed355255a62813e11f f609212468d6b96a6ed41dc61708c2521aa924711029801e8e994095bb5361da 87f496fc9296d7e4a2b3757e555083f89192935073915fc41bc8f4bf447bdecd 1741f6e096f8cf208f002cde14274303ddcb25c2533b9dd10f0728e41f28e501 b091eafa29e0509f605d0671e4c207a74acd926ca791824d0e8751f58124bfa3 8fef9d73d54f5984806448be3a3b469b1b9a50ddb6c7a69a53e404ef3940e32a 5da7b39dfc6181a2cf1ee4f9f2e695b5bed73909dfc32f575eb815f53c8c0484 5e95b7c9f65a53d07781d57093ab9cadc813bb153e669de752f5c97e05571507 505ed9f5b5ae645ef41900a40d7157b332290bdeae3d19f866af304989d5d8b1 1f8959af3cf6788fa599a3a00d84c323bcf29dee02bc65171681fe42c76d4b5c 443b4dbe3217e35f436a76531ee08d19024edc0453c2d341991a8390fddb1568 7f3f7aaa9da801b08f6cf47eb23b3cc7d098c5d21bef44ab449ac00fe6a19299 6ab9c1bcc391acb14e701e26b32b7a6f7fb09da69d51b289469f2a977bde80d4 8efffda82d82d52bb1cefad9ed90fd82956db431019d040f6100ac161eb7779d 59cf1ebb7bebd9e07338a515fa9882239bcc77f6e8709f4765b463388a7f99a8 efd4bb0ff013d411e7a7d220f792ec5ca72933777cf416c5d7f2a2945071beed d1daec9942d6cfe1c2a082609eefc2c9e831a0c07dd21570390e2cca7f2dff50 bd3adce1a245373d0073f2430729aa57954748d90e3718399028fdbdf68b05c3 b93a50fe5a835ce86b56209a1057babf323e6dd32fee9433db6ed3695de7d9ab 3a1f69a2825eb6d2c90b438e9d7fcb3fe8c1e38983aadb0126aefa31bcf6c645 ad8be22685e2be87180baf881ab3dc90cc636da658175b732adfdd968e6e0246 9d57a848b3079857b0bd7c7f961e61f3d6e3d52c2194e3c3bff28dd1bd5080db 1c1d0bcc04c7ce4cc76effd19c8469638abc1e044dc2a79c7eaa0613528c7c67 f562f27b6b50c3cad5d14e7c4f12ecae046dc008d5dc888a89d796aee7d92272 e292217b111114bcfb21cc886b628e57eafb81a6f15eb72478b3a58c9fae5ed7 0398e72c9373ab5a0e21048f4fa031532ba8fa7072d08fc12fdb9cadaf0f160a 77c364f1d84650d7cb383dbc06e3c4570dcc2a343ed4f0cac0bfd76e1a0d250a 067c1fd7d8f49457bfe82088af2622f1d164681e70865f2b317e8a39b5b1d5fb 32a06673ae1fafda5af9e2a4400b0b29397a71e148ab0a10588d97cd408ce759 5a9d9c4d860c661e86742c4b8a2f9f641a50abd42b85a702f0f9078e06189fa2 939d92a6c0bab1d47d8eca4c7a2637498e41d517bfe0af1578ca6ac2d43fac67 b95cee77da3467db2e20ef20a9b88a31f1160cd4c96b6f08cdd41f719471086f 36ec08a5ba086104f1e20aa1bee9d911e499d0a83ee3273a9afad03c64d86885 d322470a709b5aa95a584918ef8e80351984e58799fe061c8112dbb8ed166e00 3bf94b5d1bc3af26948c9e95f0efe70707b7567f6c868a26fe0b3736f4eb84c1 0dacec7f9473b709d9d08eb0ebc09f8d14153596afc5f2151e21ad5f4ffc552d 91f9062ae066dc0cdb72a9caa0dfa82230fa2db6df73e69d4ce042a8c060d813 689f47516ae100226f511b2491ce607d33565d036c270dd879f725fac4f85ae1 38a3a226fb70015ede495dcd52d5317c8f837ccf80ea444e0872742b959617d8 2698e10a18971e5533259238128acaae4db48bddde16b3671e0ca608ead5705d 41e5a86e6e2986ee7803d5302b70e50086b379232a81b8985de0c6d8072ba128 9904888e211357fc304f14a141953565774a8681ce718cd669ff3d0cf1b796d3 6f7470a432f921806f7704af9e2ae3db09e79a98691f5fbb355ee869c824a9a2 71ed2c4844a82c9bb02a0ed2ceaa1bfe5011364c90ee2bb86bf2eb9ea4656198 2872fb459f19143c897500e78f4e03ed59836d14c8a99abfe1c45c25fd9dc225 afc7929206da56bfecb5fcbd92f504b8840ba109f2bed578179316f2840b7493 740e6ac8f581a0d48d1c890ae1efa021348711b7668c2bf44ce4df9de3900d8e ab637889c7cf30038e81325a13de81d28096719222b2eab649ab4dc46e306abb dcbf3053b800c54b354f4ab29aa1470d2cb41f516deddfe7dcf37a32c6baa4b5 43f4f3389d1301a51d22acee3a2bc19d8ca3b4051279ffd1cdc43a81ef0f3120 d53a2897da965ed3ec8fe3a64c84e94148f5a44e26aa618f2d7d6ef6e1fd5f45 eb36446a8f29a00e560e3539910c595e84159b0097ca45e1251e211efd4457a6 fcfc38062f786c990627e08f21824cb2d7aaec22f7b375e5f9dd32d82fb9ce7e 8b639553c2a999201727a1b7402b65f75e048993ba308690ad9ba2b65361f507 e71aaa0bbc4e864ae50dc86b9ebb8644320d91352025fa0eb0f60d0a3d054334 3e0aaf77018f0588e9bbf0adb974a83cc2fa77f42cb4209c24766282cff72286 52d0a11678b725c2199ffbda6814f92e9033c2eb5c5ec249d4fd4fe94e23a726 8f8c15d5ed07695a7d4e815cdcf4dd3dda0926d0d752468948ed919e4729ccf0 aaedeb70ef549d4196dca9e3fbb7ae4bfb81e2908969840c384d2afd34af9f1d c4033ba752ae96463c8c6d0d2b84ff52be33f3215de8f78e89d817ea24f3dc76 f1207b72213b423c405c200a7a5fd7f8023e1f239c8e606ddf71031525f23b37 baf147ae7bdea07b53a5f5e75ba469179543dd0ca592095ff616ed04ef9beee6 2cce4a9d993261d22aacc8b5846160c0d1c4fbadca7a70479fcdeb550c959c83 c43edda5a8413902774b1f8b09b27182bec45f69e5889e202060dfceae6e9ce5 22832c1ec22d2ab3b19093ac102a7aa2b8b0bd38952b9e35e44c223de1269a0c ccaeb8254c1836cfe80e63c41f1b0b12cba744f6252a115ce428368eaa19f561 aa56531344d04ac78cde18fe6182151d31569d6989ee9d5dea5292b21da8a3d9 543c8af32bfbd127e6719470804887617c42fad95fcfcb653d49009ec615b5c2 9c54714b1fd93b07226cb356e7e29e1fe9c093a22f3832f3498b6fbac03f50af 0a1d9f709bac257082747850c6adca588a7497f5853cdf90843a4deefd0ebb61 1accb6cb2ad322986802e1cf695a77856dea76a0aca440e6f7ddf7c6995bf1d9 5a077b219652fe5072cd730e904db6069475fa9808c9cba94668189922e3fe14 90a84ec912c8cf108a484d9305d0435f6f8aeaf48bcc4e11f86f5bcfa27f1f94 0600ebf5b2f516607f12411699f6dd8c8817c88cb9f3382d854d4f943ad23f29 2559aba16f4afe15749840263fda8974a76f6ae4e5643e3f6b797a3c62cf58ca 7ab8110bfecb7a962ae0c9c503a44ca616d3fe93adbebb1daf376ea312adbf6d f43d50e52ee7398b0fa244e144ee76d30d39a41e7199424950d4f00e9796f9ec 78cf72929a258d65087e8568e2088a6ceb6cb807a9ed07f5af1af426a59c5eb7 1d840f3efef11524a202b6b4c55d15f152ebec1fbf543292464bb3de84a3c0ac 244cef52f6ecc47defa48791392fa2300201d8b10310aff5450038c14991948b 74779637e05d6c6634b4639a77837b33a5931db6fd4669514df6fa3a95db181e e2f711df4622b4ede67c2a90f1b54e90e8cd0a9e9233c30ca82ebab46c68d97f 935c0b5866b20fd6094efea23ec5a44d9f38dfc5f9e13aa13681f6c944903946 d7c11734281c2918cd59b872819af1fe6808b67aea22aebcbf9dae2289eeab50 b8496e518ba54a9047c61e1c88255305f43ddbb46ee1952e5261728f6f233950 a12f64dd6adcefacf9a68dda0307f97699f45069750fd2842dcf49d30d9ed03a 8f98e2e04a873d27b24032446f3802d96921a3d364c825e6b918f4e8f3e8ab85 03b646a54e208cd8e396a18b43b96fc1e742843437aa917e6cac0d706b0782d6 30322a0e721a026ef6ff39158e4315aa2a492cbf3763723e7bc78657d9573a3c 4060d5f0af5ce15d7a8d1479313e0fee2b20f8e968cebe186761d6c01317bd05 825b5e2c98d748a4749705d0ebd93e0baca50c01b072dba309b71e7c52be9e1b 57deecc6a18a1eb81dff5424b8da3f6a6e34ec7af818a361d583b905ff7296db d576304b302f68dc38dd385a2241268c204f6d0219615dc123b0753ba0cf35ef 13f693db38785ee165adf0cd566a7402f0d4181d2bd633c081e86c5509013fd9 258de71f92a436777203c25e57a22fc51750bfb959022cdbff5fc1d3eab8b41b ef87aead744d41649ade14650cf65f719a7a1083495fa3908acb756f84468a0b 99 19bf3e88892fe67a104217c1ecbd02648d861a44d6c128d55c56acadfa037b011dc6201b4a13909db017475218ac303130174040563a855978e883582c1ad8095aa23b1265d9e7eca8434455649d4e6770b445992fe368b15abbf5240f318c0adda4d6a8f06ff6f89328d45fd772e6804c4658567489b5818a1c16c241a4470dd500e3a9883982b9da0522853d835d7b411785c9eff31494bb89af48c279c505a2e2dfc13aeba8ee39aac52038bf1571c79c9033b269c8264b39a616a85a50076faae678254409888f5b1124866f01f3413dba6ef86e7b77d9336c0fe01794032b4f1e048288dc1dabbd94b0ee7729eab754a0d35eae60345b1ea197aa1e2608a352ea941ba65864c883e406e512f2befcf8faaaff3c29a8cb39277fc1bde70f59d45a6e7efdcca8f35395dadc7db8c91e869dfede900888518e62e652e9be061a5401571aec991859219bfdff864e6d10d0e74c81530c5c856a0cce0eeb00000818ae303c1e2124e11b949f6b0fdea7449776394447c7b807f46f89974dcc005291af72b726a8a2eca3e26b34dfbed3bfdc372de904ea64b88129a2fdbaf300055fbe3230ef2b290ac14c9ff173741b008a6fc17c8961a4a431bb583627010717403319af8a383ad64d0c68a2c28ed80a503ea473c199b49e06d1f5cc056005124ef390367e389a6ba862d827bfa0e64120322949f27d5d3522a4b039943b04f0bfd20b793442f74bd2781afd6bce8708e41ca6aa9b0894d4b4480e5c5929090874ac770c68b5ec20acc49077d1e512732b591ec7e7668d8acbdb5f4a3bce056720f06026fa04ed2a8d928a6a44417a983dcfbc28cfd8323861d2abb6a268018b3755d48c49df8e7fa8bc81c49618639aa6d9c0eabbbda56b5546e68bf0bb0a43153fb732723913d66f34f0fbf9f2197fa21c0b5736b845c3761f3ab6d71c0174b5949ee5b0e8e4c5139ba340ac0a874b3408b6ed930a097bb4c528706dbd0f2bdf376087d3ea5e565537fb91d89fb5c82e34edec4de79bdfb6295e1194630d3e4b16ffa9e030335fa329d3de09e3d61a151b7bb4f7865fea02847af6b58c017dd5d09ea2a92ae2c1d146793ec17d82990ed31270998ddd8f59b6220527d709b640e8102d3a1b3817d3d33e3de6d12a11f66ba893b266f68744463fdc21c209c49410774bb59de4fcb2b496cc5ac85bbb99a68844618b9b5068f2e1e2452208acf255c33d8a9be21eda174c1adcaf61a08a4cd19c9bc148a85f78e9ff24030d492017bc46d829aca317eee3f82f1a5d4f48e99aa17efe065c7215a656e1570b56088257b444966a3f257d692aca91be53b2871e6638440de6bb8f31e469b8084d9403a11f219d861c2cacdfc33b35938181d6efec16620b2f56662a4506540ebdb6638648361d0c46d03ca92969a087dad38f95c6155b3f0ba08f7f972f520fdfe68848f59bf320bac3f6fe8f0d4ba0ce1e9382694106a5ea6606794891ce052031e7cabef2b11d748f53b635db2f2ecc4e73f8563c55399daaa6bd2ea7570beffc472b5f4cb53231f2fb4e3f2676ff6c05eda6d80c76efafaa4d1c770b04072d8442b5dc23f3fefb0810861b1abddedb4a604f0381ac0ac3874401f1ae9f04a5480976abbdd70182ba817cef39fe7f0046fbc7c70f37570588affe7ffacd080c0741c25bcef17183d7cf83cbde7d8d0c9b0fededef7a924e07b75ddab0df08ab25994dcbc003c70d8b7c2d56613f8bc6f89bd2cc24f5302c87ad9acef6e0080eaa548b00d284a587a5d21cce894c750cb2487212b97a839c985eb0d71c720879227ef868aee3c57207a5cbc5b5b2cc7a9434bf665d11177c237f4b0f7bc2077b9d7f51e2edc55bf66480afd0777644c287e7c562dbbaa3682c868d2681ff00fef3abd2da8266689d3b005b1d812f55041a65345a783621b5e399b20a33c00719ea615e443fd40e431565115a0fda5aeafae37f2bf622e3f4e74c456cb3140b63dcb1523d841413687be06b0bdd2f783ca208672193e77d93245225286dd207820989c3783c5c8dcd6accd3731df33fc3da5a9ea4cb1888fd9691ad0818c00bf9313c27f4ba9af58209d6254bc7db105b3110963991be8d9fc2ef396028100c592ac03d67a52113ea59e9bee05d2cd55a3caf55aada1405582063357e82a7083b679d947f0308a9aa22fa78a348a0c84e1c27afaa0f49259ea86a72aefb4206fd3439199437e5219b9e3f626389fd19dd779e8f260646ebb4079fbf3f9dfe0e47c23d32e51eb553e411ed708d09844e9be693c97b1f8e04cfbcf8864502fe02778c725e20a52b1213b49b1908e63c5a01e04735a6aa633f149b53049785dd0fa341f0a72003e112a5a25622cbe636dd81bf5189616ef90197a1f5cee5ac0f0c8c09dbf72d2cf6942fde5e7395be69e97c9a9a831c3a90fb3020c39e2042170cb472ef176d7f94ba117c5f81fe49adc7fac348ba27fcb00d324197c7eca4a504ad10899c8e68949ffd867990984c1f333aa965feeba8ef4295a844968117e50c801ee46dabb9844ea170fcd7d9d2e200cdfc337fc01b3fc8d45d95f39c11d203bd747c153b48a51d995ccc7c545c3c2eebe79e54b6a99c7c81b41809d748d2096f01bb503680db4822d764611dd19777244eb6f0e65326a514cb5a4594b45b07afb9dda3f4949842e244af88db19cbceac5236b29dae48853c19d2c5f7b7f601578f66701d4792af936a8f9d5af36ae32f66c04e19d2053c3c618144ca9c9d030468fe5be2d4bfd8946bda43ba42ad3297068854d532a5ae30d9069305ad7209c80b0ec1c3c555ecf8168c2d07c108ced64554e1dfba9c847e6637b36a3cdc05c6437a8aa793887cb3593cf64e58a32ccd3afe2a56b423bbe1316788084f6b0d5f592cb4f04dec5fcec736ed25c1e494452165d299deacfb053135d23940fc086f43906197d023f6af703835583127e316c4ded2dd4f2905da84f64273e9b5032db5cc85e3239b363305a401dd5ae7b45da846adcd7e9b242ef050e76639610e0dbbe40247ca29feb097d5da8cb83e3c7d5ca44a87daefe25467866282b0270ddb175d4d8b2d58a00e6d3a1be655ec920a47ad2a1b6838c20ae5fba3b85d800961b591fb9f656da834f2ed5cfc4513d3577d2a3e6dfeb06d9ea306642f99b405ad8d0ecc1d7b6feb92ef5a0990c6e2ac6cfa49b5f525bb65cad4fe7093d8b7072c3fedef9cd270e833289c67eec91e8cd36532306c84745b3bcba4e26f26a207e01c97da39f5c284271b83db54b7f6e0268b9700f83ee8b8923287499b31870621e7384e37b698a5a6e0e6340e8c46cdb0e7da7cd6b04042aee93e282bb63a0d86864900277b779f949f679806b8e84c62e5096206bad213c86480f7110d1d0da30bab6e4101306ae2b1aee6c6827e60aa2424f6ee7dec2ac75c419e0454690b1ce88c87510d00e01dcb69eac3808a3df2cd3c4fc166fd174cb3f505f1382408c3f644ac5ae85829b62a7743b7b927fc7e3ba77a8d3a9544da65055b07f85506ab72b78e733fc115733554fca7e1f72e6a2b6187513d8c2f8101dc5325488e08de8c01266473fb088de76fd9533412c321d5ea7fca27d3d7a86940e3f861950eea427e79a750cb7a62c5cea77077db02628a55700f3256d04c74c89a4eb8350798ad31fe813cc138760418bb7bb070d31a12e6f554ef0ff1001801074b6d510b49e6c4b7d5e5f148138e58ead90cf74a85d847e0028fe2acb386a1cd0e74f504647cce52c056f691445f8879e38ba016a394296a76c64696effe043d7574d00cb64887703991a0ad3e05c00fa9881aedf2a7dd42bb8fb0028c1d8bc80bd5100c07fabca514b38ad34372d732dcbefb185ce06086c0dbef0a29fcf19eedacd900a1432cbc35f0e70db1286d60b334b9c706156c43a79acbf6b60c686a4f32760c4fed0e56a784368df788c466ac3f42bd1183fc505e151754cd3dc0fef66be40804254e961905ce11326d87085d8a2b27e4fe2deb60ba6dd0a70f25641b52ae0cb27b2e81beba5a1059c51c232e84478b8d1fad044a1c2fd5913cc9d2f435590884eed1456a21f7a32a122ca17f77037e385e9bcfd26ab9f266b9f0e8ef25f502a8b078fc067a70996d3ec5979c9e866694940480324303e0ea9afe5fdbbb8700f0fd271e18f63b313c8926a825d8a7dc4e9264da9000d6f7c05470237b6cde0bcc33f51f5812444ba3a3effe65fb5bdc75d1a3025cf0a7d902af749000db4704f1fc3f994d991e9d7c898a19a9d03f9e265f34a8d7d503066d6bdce43922b90b753377ca54b7ff031f5f177f4aae7805dacdbd351998a27d4213078c568b8a0040ec2400665e35b4bfb9b713beac4592662535180d4275ed7659af611f876c0b3ddc86653724953cef780dbbb150d76d8efd3f6bde2d26d3861b351f13b9e00efefd79f2424b143bfe7f0d3fdd346e27de4de2fbc6f4a12173ffa68449d97c0cbb859850e7303e597bccfc4b372c5ebb2d4b591088b98e6c9db0348712261b0cbc87167912ae868e85ff1008adf3ab59b4a54089294585cdee5117f486c83709faab527c50c415d26967101e613c25bf0c9238ce685f99c6f83611b72d073a054034dc2066fe5216a887e509784c9c6a794f86bba684e345d77b595ac37e320b482fd755886c6ce4f76c22e73c1c9cbb8e4633f6a97882d5764da0319905c20f5e110622a0157c689be8b35ec4484d903679981ed0ce293015a2ab6926a84d0b7d91695756124810049fd53fb6759c4c15302f49e75a73625ed1e43e4813060802d1aa9f4e02ad255a200ddfeb7aeebd1cbba209b4fb31ee77c25808df013507994a641b3b1397cbfb66c77e1a8b8308e35baf1bb57c4c66e687a2aa878b6707c2ff0024c004402bb0680fe16e27e020a33c4b8b63187db36260f379445d940afd90d0b209412b98d1ff3dd82504a85c1731e1ca912b23ea491062e441bdd70de74ad8b574cad5b38c740718938b54072c8cf1e6e2b0aa840acf4ef1e0beb104610b337feed6d95ec83ba94d557a09f808494eb73664518f47edddcf1ae90b0b3d07a15529b7f8f38489b57826d7393219a234365917755ef5bd58c5fce71504c584409e6b661441d8b1cfcd274214f448e92e3dc60cbb1c536f0463770852077e8605756a28056ed72ff446952a3634ede88d13f6cc3a0667757862b5d81b09d148d15844eb504df6a682e4e6cd76b8246c7edfc94c7b33e1d24c558b29510d5fbfb9eb946b4f9eda707226f3da34b365116cb953e1333d44219a94ef4d170e10ade3e9211d80eb971ca6b91b214ed168a8e29335228e6a91bab45ddf08790f6f4b428043d21eb53371544d2d7bcd6ff1a27ba7f384db5810003e7e73cfc107d40167f4b7992df9bc168d9abf68b495460769b84767869a36cab441c0255b0d48f2b51f729392c7eef43304cfe541605d34525d32ab013a3a7817f451bd4a056f7c4d21c56a1618da1ad2fc7e379372213ce4bea71c3f18f0f579273ca9640405d499b0d35d7d410b57e01182b59911ec9e2ca7fb87e6bd4d9bd079c631ed02484bd18ad90c09aaf8670506373e54544c2fac82e891b3324a1bd1ef996ed209be20a5a53da64d8b1c3962f268f96d08f4b81e3d3f3eb756560d4e468a50430736895be5ce333264aca116707b5d200182cf3325808b078b3f6dd1ee20429d09610efcff54684579bf487675dfcc11fec86f6559d1da7e8e12125fd5315b910700ca2573dfdc5beb5862db5f88ae4dd72016ca722218e9a0f13226448fea5a0c0bc7ca27e3ffdb2f2f7ff80a8952e0871b9070e2d3d0b4c466ed3b7e3e146c0ee97b85607a2bc7de7253fa5bf1d5518d7ca6bd92a246502710a0b3a13bcf7602c2df9791ed0744c75fc227bf74c38613e1177d9617904a244eca2726f824040ee1d2a7c3d8e5d430a43ac409ef65fdb4dfe4327d41f213601e8348384221220368593d426b0a33d4933458b1c207217f82cbcf8650cd0161482d69e66a0f810daa72f1efd3c03241dcd1e75c03da07b79cbda10e06d68798706c9145d82dce0f261d3f1ce448f4f342038806ce3fc6428df94bd4325234a2f45299b4f168450ae38f81af1c5e128173891e021d7a87906811d2c688a8c188f10cff72e3324e0d22095d9a89a30ae570c3540621ce1f28e3b452fd31d410b7bb8a462e2839cb06fccf49086a4d22cda1e5f023d38e79cef825a4d7d9258e071bf0bdd8087dea034185ba7e86b85b5c902257d1616b7c94960b64ae8538880bb59edde568ef9208050f0a648bfebb15cf919e4bc29ad0ede506d8121d974c56b07b1f63759fb7073b7800e9e5bf895e7a825e61d4d1aedd0c9460acd391f2949ddefae69f451d0b3e986f5f8ae455df05a629f134e9f813c888765cf95046c6b16a6d505cc9170eee008feb0acf2498583a9be0d00d613a1fddc81b8e534185a79d7157bd50ab042dab11a214f7cb3e9b2373233a7c7c1f9a1f1ee4f7158ef559ebd4965aaa3e0d5e47aa407383b97a8cd5f13e786038e92d900dc84c86321e19133c261af5df0e09a47015c1e5aceefcfbd9d757de9d873c2ae1be3c36c5db5cf3b797d8d760081ea48e1ee12f8b67dea697feb474fa182e2060c8b538e16eed5de732d251aa0a4aeb90e2eccca92b8778a0e1f504dd4d75ad6e518cf6906d60200879bff98907a2325b509ebd5e5cd3b53c6e2397b22b7021231ad881dbdbc03365058a572504b589295e9b33eee2f2b0dfbd2482b00a5fb6dfe9dc8d54ef775880dc11674400bde795a8e574808ca5ee84a936e01d4acbfb9cc0bbd86cf5bdad7f6e090b020085897d3f628c2534362f770a5d20350589be62f77efbc7da311ed79d9d2f490fefd460271a8addee32b9fd4e6692234beab893bc3a3613f350b091a84502e908ed83fc164ee45870e27675cd8df3707ca09a49c26b208fb5ee72b3891a9593082c2933aba8e0868cbd1af14e6fb72b36af8ba1ec6c10551272b036fc6fcbf302845b3440c8d8cf3c079f368a6e653b07045b026c54ef7fe987daceb4725f9c00f2bc1bd1233574f93914bb3c9042d7c4b0b0c272ddffa95e096eb7b64441320ce36904b5b07df38d1ef9a22ec5b183fbc3eaffcf999112a16c23b8fdce5c0704eac81e503110e59156fbf64561b857cf39e156476e29ef87f4a6c08640fec30b7faf06bf4a9eb1e19afa39057bf5d2d00ea352fa0681d64fb7b8325eba168d0dbdc5d283f631b0c55f575f76328ee49c99a65e3e7a0d329d3c16559d1de4a602cc02e44f1852631e7e190c10a70ce79fbbda7bd8915dcbdd7ac1e23f6e34a900ea22a7cab6b2d19a6802803fd9d1765b8067273eee4b80f603bd701c5957040f81e74c0eca1b183d7e564bdafc9d460cbeaa740241f72d0eada097aeb61fb9082be81ba42d22f3741b5c4d28cc59b99be2b9b0485acd3bff88dcaa5cbb15cd0e91f601ba0eec61f606d8c70b87a0fd02b769619da91d88d9509dde0cee67d900cdff92781a293fbf52d10fcd52ddf7089c3a9ef8be56837e52018a7d7b0d3c06c7fdb183a424ee4e38d59cc7ba522e8e2fb13dbd5a27d9e7b9df116e97bd860aa04f8045d8cdd10ff8a4a13176f5bea2ac1f4e699f12c2c6f6072d793fe99302836771cf1f28ae03393b150c12273cd6bea973cc4d7d8599a1aa84adf3bf4f0bd4f37564cf4aab35de09f4305a3ccd12ca7c647278e5442a8ee61b1eaf9c5c0758d015bc699791c37508ff52835e1da95f55d55060c6dacb4350c32942e0cb0d2d8f0e1ddd6a393f8308f649c294f3c505d429582f66b676ee50873f174c630c221502d922650df39c8454ce394ccfb9df4e1b5563ad16f3ae2615fc6e33e508818e79b782b533d1eb5d79e929f1af9d38bc2452bd285e268e19bbfb2a99250d760a50920aed383fd1636e6ec4babb5c1e7f0e4beaedf75281180f1344934c056337040d33b411f4459b68020a9e8cc838af1e4f027b9f5cc17074418d082a0f525cb684a6a54c6d61fb3fd404e8b8c20a3c97802da110b58087253065cb150b93e3a27fdd2c89b7e269d708565354ba73b325d112bd1ebdaf5219e000742a03731a79df35c40b007ea43a6494c804614f12b5c0a313ad9f575e6e1a13ed3a0f692ad2ab1b2b1805c074c4512049d58f8abd80dd76afba02fafd927ad593d60001bbe5e795fedb3d5687c879953046a9636b8da6e5679553b05871ab1394c005de132a9e652a0cc1073146ad9be58c573d9a777b84329e1ad3ce61a256e05c0f8abc4803b2012bf2caa0e00048d9e3db981241d4802aa39c75103e8a91f3df032f12d55d2bddce8533317df105b4d8dae3b14371d13ff3c86669188ff2af39005fc3bf577463f492cb6a016d79312365111f9f3cc1cbcc14192891a2d6ed860064acd4d129e3f334c96074efd67c16863e93bda32fcd79d242a12d666a3da80b2937f1b966baa2a853a49e15a427f0ee0adc14913fcf7eb73743282c0a726402964b97d68c45baa50aa961508c3e430f44061260619c51ba9d7d0d67a434a4039ad6b26773669810af7b97fae7da6cec672a644dee16c308a692f4dc31906b058b1ef2d137e317a87761c18cfe7788af8de1038933f3f38232e18aa5165fa50aae0545aa4d5df4310bc004155527ff40dfbfa06a7ff79ca0c4978e058bc57f0f06ea87b9a75c95f34399a9300dd27b3bc379e1178caa6bd445a5b08f7e8cb106c6e81e9d06fd2057df5551e8235922f7a7cfe9095198d40b686a7971be7c7d058e65bce59cbd2e5ee05a8d45e7aec14cab8b5849ea0b6693094274a5303522089e2a4f6e79ec1868e5389206db372859777be80a10577a5d60a472b79874530a5c4bf979daa7c0ef9eed0a296681fc67d0a02419e6c9ae2053866b6ed7e92f054414e3a54c8df51ebc2ceeb10b26cb10683145af60f395bc19bf40436f1280012c4aec3b9fe42a51755c4f463f2ae37aefa7a671c38e891d5163d7efb0bc0200f5872abed6312f1c4fc160cfac84d5104f9cfb94fdbd2246a4ef08a2ebeefa0c1464a1a08b018c9763ad03ddeb6d6b0174844142ee62d803a7a3f07a68f58805c1c9404f12a06947e22fca1d0ad78e8b4480cb7b3b517a8900eaa66e525105051e4434976348d91fda947e0887d888968bcb4296776e8b5ed5885889da06e90b5dccc745e24ecd6de28a08ba6fef685ab2bee8aff6cb2538fc6f00a2fc08330fa6b357325eba9797023711e612c1e56f1387fae09db90a1b4a4054794e9f4d0ae0a9ec6e46587de68b6203eec353976246860faf9a60fc88b3f7c4191b6a3a056bfe51500bfb297c3e2f4c371aa9196de46c2684d1388a792b6e605034253c0c1bc7d0925ba6417c141c5ccfb5a424438649cd70d8a35ffb40290365de01270551b7dfba790a506e528a8282165809648ad15ab29270d5297ff13497575b170e214f2fdd82d0d6c69cb32a457f2057cfab1d8f159aaa1a99c59ca17caf9da8070d5683120e5e9618cf9e7f03e198ecbae43127d67c7a31549b5c5c978f7704031f101fd1ab5bbbc68f5af2a782308fea09bfb51ce4d60a68714c920bf337340e3bca9745d417541b02c6042ab9049359a497bc5f48bb923e05748e1f8adbcc02875bd792eb643ffaa485ad848a32168c87a4d1d4f95414b07a7fb07c74bd51060a048d6932c2a38bc827d87d1c6d9cb479c2d1049665a51f39799f5ca0d15d0458ac85a2ec8235cf5077b049565a678eab6c523531a15d233d50c9c094d3ad079e3d7bece0a9ad9673a77d95210a40117c8918ef62be61afc746626863bfdc0481c5c4b531d362a625da44120c0ad73bf452ffadbc1391d3c66d12a5aa0d430112ac8a6e2ab872bb81fbf3061167ed45979c76c391d9f7a7197b8c03825b660c8886c9888a17a622272997746905ade13e92774e5b1768e3875012a9d1f63d04e659c0bdeafe671f6ec4aaa80d810fa1d574fb7cdeb86e929e42f0f5f92b36018397e5a0ae76721d994a32da4a555a8e0464296f511a8f9e050e7d1804ee810afa5fccf2ec13bcff5cf7154fc7b5dddccdcb3cff4aa08e0fdeedf6d53cd24c05e3fecb18dc5526727c4202d669e7d6fbcd921a5cb6dba90887227f381fcb5f090ff84c43b7778e47bb5b8b1265321817f505997531471290766535624c2acc099fc28e97363d3766bbbda47638b958471c2075d48d84a863f930d457c5e76705fb2a6b243bc6929f2777a13c6994332a122f003eee154c24fd60f971e0fdd20e34805f76b1cf51809b496a79fb7069a542c05d3fcb107e1f2632795503a6a30c24b52b6b2abb96d3cf61d4aae158bc62c00e0a87d2c2dc9a797b8edc1a3035024d4fb500efd5fe823a379a15593c8d67993cb7ba5be34656cbcca8035f7327003dc1ad2942463e474eb6b9389ad1e6803fdde73925e7f8fcf683d47d8157c00c50239a1c4d207d9621dff960aab87dd18bc365b36a13b80d937a809c3ecda4051a5e5df9329d89b03e771a98ccf4833615f5dac627c7e89aacffc0ba065eb7005d542d8d48ae30766bbe9043ccc0256f7552d474569bdb735778d81dae3e32046e3a710f970373195c12c728f437a76d075ffbf6779a0fe7271645f08e37e7094a13376737fbd66fcad8fb7e1cc2a9a02ae25c66e0f03d6b29207f5b9c51a5067ff044552a82caa4260a34df846f6927dbe34749af17cb57e7d3c1b26a683c01136d66869da3419345f3fa323d7cab08cc1ae43296cb217684cd0242acc41b04246ed22fa78c7b83200590e9d4ee706ac36d5b764cae469022ec200340df9202985a9775b2c6e449e7baa996f1c69015a7a4381061ca019807335ed8c017e206029731585d6976022f7108dff3caed5718bff56f53ba8f109f7ca24964c2df01f10a0061a61b58da384953ca0465f4f0c0a4b48ca78f8b2ba1649bbba84d540a7e9d0f19b1250de23973101c0e4257cf5b91f1d8433791b4a6450ce77de5d001a1cf52e0a728bbd2df289a353ae8aa2f4fcbe78877251bcddcf8c189d14a1400bd4a65d1546ba357d1c46199ff7e4429cb8b29ecd446cfb1ea59d7c693d1090eb01c67e6d7a2384835e71454bdd4f6b151b27388a21a536f9133bedbf805110e814081341022f0e2a38fb646f1d9307978cd395a3d69052a427f40dbd41af40e045a4475d035c0b03c1a0955a91d8f691979afa99dbb9c0cc0d5b632c62f1d0b3923d31a37e8dd56ee3b4e3a6fb8ba89ef135ab5b347eaaaa72834e61cac320f6e72013948678e2858f39d29a79a9f0d89abf766e8f73bc03d0dc48aaa5495048566650dea62bc8a382b921807b5e602ce1b81f60d7170c43141ff49b848b504b17c5ac85f789fd9abd0925622023b5f03ffd409b57f5f468d5411493294a001d2da9e4dd77a2f730bacbe9a3dfb624a5ac8faa39377a7a1eb8d3a2eb5742004886dc84967f90ab000bcc5132e7487ae0b13ba7c2b226d622505600be9800c0b32b4de820e742c3358e75ff4330a3498fecc808b31a255a1cd1413a478cd0e0e985dbf21b92fb2ae1cdc19a7240e3a646a53102ce10c5e4c71a24b637a47bb037ddacb3e84c98f2c9d6d9b26d02b3a270be18bbac13d58972fd8e1923aacea0526c8c2a6af34ba6867940e58f7753e6d7181b1b6ec9d9bc5315108d43e0065041b5bca8b38b5d4c55cefd44a9dff77d440e76832fc951f376afc47e318452b0d1a0d71f26b57af2b7d2cc77c7f410b48f8bce9e265c6acf07d188d99554a4301a07aeea9d979ecf35398ac4fa3eedc02534c50f8e6841770818dff3225958602abdc21ad474ebdd429a6332d803aa9bd3fdcaf19bd57c58b1e7bab9fb9fa8f0f3806b837c259acc0d58c6012a75314054c84167a56e057515d1fa8b2fe5ed500cac8aa19ef1878d4dc5cd9f1b489a00754f60e95bfbb4d4aed701f70c9bc5202f19357d3ca4e51c2a58a163e2d715fff10b0d7288bc557d45bc82cafc2bbf10fd7f464d72a2a80634f3655eaf675755fc0e81f96a55f994bc96d6e7100c9970fbbbd7e875fc3588162194519485476b1aba3329cceca66c7697a15a30c48b40eb85769620eec4ed22db9ab7a4beeb196a94e4358464c39d7df59e8fc4852f9012a68ac10414a132bbf5ed54c585a9ae8e5d69414b4e4d8c044170f369efc0402ef1067f385fa6a6522dc79f7e1272dc2d8817e326a5dbe8183e4abcf6b33a4083b7f531f641e90c9c418644e749c9c564078c0cae8150313f0056da5bade9a0bac34c6aacd391e5c7eba8f8b9c9f5f562ad8a16e3a92c0f1974575d0cd39a20d90f7efbfb164798ddc03c0312cd3f7fd69fcb880ad1b5e73b78f8e61442cca094322c42c27943df2f81ec138abd29ceb7cc59e703ee881328e50f26818f5870e7e9118cdfd85cc34e79965750d74f7e4de803690b346395fa634f865acce780c8aa5289c8bef404a33d345ec0df8fdd8668775ab09d7d057d05a1d8bc103ca096e53ed546bd808915900746e38ba9f118fdb43e768c0c02d9d21e1cffde0c00953dca684886c45b3d34ae06b11434ccec98b85b33195b9ea23a7d6fef26351073eb7ad717d4d81574ce32e83e775b67430ee26f5838ba1964b810315106d7f05c1bd4cb51f5c893fc78a92cf2e711e39ae74683138a0dcc0173ecb930e11c8005002a196a236963073c8e1026066e738883588bc04e5178a04a6d2dbd256960735d218d4cbc5c42e68a5f7bb2ba529dd8aca08bd3d3393f33a3940baae3e850495dfd762cc5feacd8c146567f2ec17f86ea12f79c342dc5f70bc5b123b6b2c0b28a339dd26fd6c5343537f788295a16f23e43063424f7353d2524714d5415b0f32a2f87c9bf591ba1dbfaefe2f0e06b60a7e8c73848216ab32a335ab09eab90ea86e35405a07ee026fc43d4c299b7d158665ef55b5e31fa9a31aa149144aea0324cf58148ff4122583868123edb10517799f9a31a3ac6e6191b17927c9037601da5059c37d2e37cddb48643f25af43b9ac5e78372343a50138477ab361c4e90f1d83c10bc23a31bb32e5a2859a32a323673c4d839bc457a941c52f5869b2a401f22b108839c2d02f51cc6779e577473969fdc0a9e1505dcdd45a57dcf9239700c2f7c0931953f2d6e3d958af895d71cc83bd2ada24a287574ab9ab7b030c84066077576bc8a9ca5be6f43e8566068b7f3620234f7a119bddb6b714262321eb0c39fed73029b828ae4a0fe92f6d0dddb49bf7afdbe251107ccc0f6e42b320aa0c3db8cfd704111ccf7fb57e33ed71fae0f866b90ecf090043a27c0630f2943f0d39cda3158180f1f8fbf77da142db4291fbf8657fbb0a2153b1b09d5d9a778307325c6eac179a1bb6a54db77f33f0a055ac57a91956b9f758d35a8ed6d5756e051eefdc28cfa90541c8894a25c6bac132f50880275097c9b402b9630006b33b0dc3904d2f1f0b5ca838725911b227674e9816012386093f8b0ecd771bb615f4027c5e21838b6817cbb3335f5815a44b349baae068d4e38ba52f57f9831c90860e1c449665183f7a22a0c54551831c02ca83876f9f57c860f4b681cf63793fb8098a518875d846dc4fc9e2a2304213893c9783dd8c743bdd80764743eba8b2350f2e59c48cdd26e6d8cf8e4d9adcbbe034eab37a1dc9ba9040a1a2c0102074010d36552b1cbc7717179728abd519987c011b1318509dfeb60fd42c706a77023b05106fee3a72395c528f72f02fb3dcb0fd493f87c8a4b356989147ebe416784007a55457089945f608435aff0d0a2d18893fd810a4616d6030270545436adde809349ac3a9cf2c927ec40428614af36868d14d627cfdd93bbc5f6cf8a21cdcda0fe9874ac773be6eb5022ca057a7f1002b6b9e4dd67ae4029bd16d6bfa620e7e0ba70524683c7ea28c1128ccdc8ad7ca4e558027fdc195607aa1e2fa3cca509c02dce1864f338b8ae1a0e6c4d20b9261755c0649eed775ab774a8dbb56f82ea70b4f1d4ad58ab8aef1c222736d42b8a39be6353ca29981119a1a7404bb21d4050d0f698e272311dc1ba5eff85d07688f190e8709d008b047cd44ca90f716adfc00f345f2d648ef2acceed6499bc335082b1b7bed72b9989426f01f2112897a1d007af110ead6941cd742ef1a0dc4c90963407fdc5f10d8898acb21be43d275ee0d31737d92b33fdc5cac1ed8f5838055fb13f467c8eddaa2294900fc1cc808e40b94d6ed317a76f3788f0c857ba75a8fc6ce752ec3ada66dc4302f776e6cf8ed0aa7b383becdfedecbf608e3df224304f93444e6cdb936fb148ada29635087b70802eda172f60ec61a883b9fe424274396c919f50fd07ab0754c8170a7ccb62c081ed8bb96c8381cb68d3267bd6754b6995f69f896151f1633768ccdb30ce8ae082b337d82313bd5172b7cf3cdaae73a08fd4f2d26f1e96f140aa062362a02e2034246a8fbe2d5ee0b6b82b15b6caed6f8f54f539bc3b593b9d8ef43a7c1574302fb9872aee45ae0fe267a22aee24cdce5deab4e408919d91a8b53e75d95473b08c82ac4a5175aef72e5ce6d834c401c4f9413588702438e95775aebc970c95308912b3f2179764d1a6db3785f03df237b745f5356c5fb018801b42e74ef6b830258d46c290ce7f3d619d75e5110ce7602c32dc1541bb74e94aafea42f003110067513d74aecacae98cd1363871626668d3575bc70a8c05bafee08b5df24ccf30cc20cf10a3b9d9d01d88a2ec9dafb733c077fb90cfaa9848db658981188088b0208b3faf042e2b35918b6e8ab3f557b9eaa11f0726f6c4d672bc43c1f61be440f6989955bf71b92d45c9842666e9fa39f6bb7450355755ad2d05b6208b4ea540bf516d02a72db654b8ae882488d34c3772a63b121ad9881db19de51a6b11c0901874af81c08cd8de93d47cbcb3fc4faef512885293f23db9c1a78270b0f800f07a37f38297534629fb1e36eb31bb6457bfcee70aaae60d1f6c394e5848b12190c7598279433edd0e90eab560c442da49b8fa7c557a33db151b045ec941521f10fab4ad5a179924b52dae1fe703cd06eb83f1b4755f2f5436c8aa19117cd4419001b35c0b04653ad2308831326ade6b82bc05edd2b4ddf57001a3341fbec0f9307ed56a77740adc32cb20b7ff73a1ed20b0e11c860d857146de643ca1664a2b602edf49af1993946126b0523703f3d979f7ecb30a5f677b9a672118e07f1abdc08730940b4b8b2f7dd8851b3aa9a6c02894e90d3ddc1e4f20fe3be395144bdd00504ffc8dc8fcc6c3eed60d43eaea4dbf3f3614759b86457fad2ae4c3b0e031c0b54c44234dfd529514bbd76f952bf1512a6d84b5bab97a7f5390883de21aeda0792eb6cdb747cbbce8a71725c79a283a99a93dd5d10fef0afe2f9093ca39f310976fee51b6bc5e7b8793c74d75e6bf10e86e2df13ea32ac61526f25e3ad85b80fe260f0e874edf0b7f62522209676512cb3f88bc31d17d1e9d981162da3651a047a8c006d50763acd5ff9ec0f83f34176055e198fa75442e9366999c5932c280f8ac50bef654479571ebe95c808f91410a61000508d1833b98eedcd29a96cda04 -generate_ring_signature e46bda5f8d177a85b3d4f3f9b52ec30d3675e7239eb271e9756c1b9dc52d0c1a fb375e609a51a1a54d30f6554e5481a94158e49705233340357ad15f25c832b5 1 d9515348ed96ba26f155412e0b4b7fb190f48c1adff6d6f7bb0d68c4ebe432fe 70df55444de1551ddad38c3ba80c388032316cba280052447947c02151dbef0e 0 454e4c6db4545872d762426ade93dbb89c1340a249540451b887bd081d90950e883b17bd17696372f1662b4242149a2d14024f3c1a6667bfb0609cf42c475202 -generate_ring_signature dd3a63be8c1d0e7c20341ff51de1ae69be6ad9a4af2494d969de4b5c5a3d1213 bed9efb67d0318e6e2fa22a32ab381332b38f0894f8a8ada7f8b4372ebfc967e 4 066b617b0fb79775aa1b548de8757a121e7d0e38459a6bc5dd0a587a88fa26b3 193a48039163ffb32e1ffa931abbee6e1aded29f388aa635a3ab148c8d5e2568 4732c8e3a686c813b657069e304e06df196259ac5f1634bfb7713438345f053f 4f1a2284c22c27aacd12d68d25ab10b1fcdef99a708fe2903c30e185fbc99c28 79be700bc846a57a8404540767e0c3f22a32f812668276c2d6b9a2938d286a06 1 c20441c768a58a8297250b384d6bcaf2572d9a9b91c8b5cf3158c39b0fee800de9a77b7643fc79279a5dd6f25ca4490fe3706b8ea32fbfb4f98f0896a3e52f0112ec54955efceca566e3344e522e09f9a85f312dab24cc897595349f89e5e00082aab587427b2a4458fbd81531009b086b427d2ed28937d5cccc4f01382bc90605b0e826e4f171a61690cf92779b948b54f41a3943fc278ee0ab8479494f770086006ad414290d877ba9650d8782679246e0c53092c8b4120e1fe545206b0c0a73bcb1430e95a6ff86302047d5176196a24890d7d7c8e5da204ccd3554547b0abe99de271f9ce6b4256f733d744b282ab35ea86c6034c18ef395f7b9efeb2907 -generate_ring_signature 2a3fecc5533445d1ff407029a75e714626b2eaee3f6479c67a27201ed06b2b9d 01c8dbe8167151ed13b296b133178b80e39a38b0c0c38051040ade484b69e4d2 221 8b859d51a272d7d7f42eecf840f7fa9149a4dbd6a7724f6afa17c4a1bf26d8ae 8d6f021c956ea9a626077dd81b5fb6e459a370b56deb4f4fface816bfaf00668 815a27ec056a040e436e299d9632876500d90f3dde3eaf1107d9503788cc0c95 a24932b33273a1ceda8f5b32f270fc6e0df415fff64ad6b39017af5359242b6f 9a0adcc60772f3f654b3b417943433b00b6bfc27e8b8afe5fbbf844147e8c5ab 90f1e5a5891e354d4fd02205a33f49657a5de15d7ff4ddc31f39d8643d62aac5 450f08d11598d355afdb687cce5632cbbf31e3c1e0766765ac352dcaf036a67f 3866d2f6934814b1095f0e0a1f147b93c50f15716e254c9e7d74645a4df78e0e e3491b5ebfe4fd7c5bd3f1dd3a40ab2825bec7c4d827e0599f1145fe51629f3e 62c9bd5660490a1d2e9ff39215eb040f736051a242963bf2f272eb9d73fe132f 71e01607b9b65d973be08bf5983d862bd42ef2c3f0fd6be7ba33a54a5cb909e3 d561622d76d33431f0a7881258e4906e5774696441e93194eace2bede8d5d095 9e1c44bf1c3513bcf7ad2c087c0eb6f0d19ac9288a21838cbe629312543a7d32 506331bba887f11ea4ad8c9044e332c082302838c750f0b0ba8936b8dfc70715 a1f5054ef80b2ec1f151f60451f4c982280f128b8ed831095ff4479f2adc8b19 19dd0493e1890f8e0ac05b9fcdaaa0bd9688e2ec9f47e2432f02ca60992e4bb7 8d2df708b2ce601d1bf7d762da0d31681643d74b8ef61e061c1f67b8e88f3975 b765132b1552f0c34491edcb6d601849aee7557ca805edc5296334860376838c e4e824505152dc5a1800451cfa73437c266b6a0ddc9a6b13f8a82f2a308195e3 33be8cd89800df491acc53ec6eff8132fc3875a454d2fccd019fc6a3d4aca291 f7289f047047bfe7fb28bd1466daba43a6b73318aaf082fe49a9703f2650c54f 85aab4738840c444824cadc97dac6f1b738102ab8d495c5760ed553ec8cdb8c9 eb87a343835083e45d815729559c3e1de67d69ce50f0f1bee874a3e90b8701d8 c58861edf4520042f37e873b69016220706fed7fbb5f4ebc1e21b9fb5018e20b 9febb8ed1c5a2cae82c73149c0fd7accdac0451b8fdc81121b954dcc500e5dd0 c5825602f9ec7f01f1d7905c3b3e7a4671178863672f3ef2c83e1abee6788522 fb3fa6e53af880f0cf31f95a65b5181b4be788265f9a1c0cdd5c3f1718b5bf83 f6b63d638100268978d89ed0f6005d24a1b143c112c73a918ad27b6eed6f0f1d b4589104a456ee51feadfc532a6f52e5bca5eb9070d0a77a02624d774d4d2eca 1fc0934d38877661c56143af7277eb837868e8465f0a3351efe115d1f61a6b20 bc4f9dbd0695c462a8b57fd6b0af3974104bde49c6062b5c4bcd09615d028b7c bbefa2f48cf69f7b1df407805e23764ac7266494449ab9fdef0641b4f13fefc5 1c9ce4f9a6a3931a3ad753ba874fae5acd3e85351b3456dad3c0a8ef85028633 b378c247b6855244af5246a0760ddd1a703629716b16b50f2f7afa5ebfd57e01 88adc9c2bec405b609d7190671cd84ca990e3f2794ab3550fcf5ceb569009fa9 d063eb0d4f7ab5f0b7bff340644c61590014570207189c73846322e0b77f8392 7ef5e21552383ce730b75abb6de94d8623ef8987e2d382d85dca46d2f5b94324 7aeaacdda090144ac43de8e04075421917f57f1044871738ad85c3933a7b92ce 73efd0833c7b48c215c0f92d2e72c9584bf1a5d6f15c9d99e19a6f6808a9ed8b 65b8c07bc87e20fb9ca0fb72787720d7a6653b04e11d4d31f6c699fed3ceb5fd 250511f67f3ec1d43778d1c712a6b72090c1812349262f5aadbd37e6f3cb69ef dd168f71e4b227d7417b3a6d0c6dba72b1fe04e47b3816569f149ff7e736c6d4 e1837515ea0184def7085f6455cbb2ea7d957942dc3a67d8c6c2ac5c3f588528 be818f8f702a06b5757bf5620b6ed5c110924a73252d5818b4903036b38e9ac2 9348070e09c4ea8e76401842a74161a94a84a46143a1242e60bcd7769f8c10a4 775b7d0938679d7a173f56742e08d0eb911378e65e137548d9ccf8ac6f61e00a b44cbf01cc2e599fdce193fc44b58c7e9f67532e6aa0cc18a771823b7709f379 2452d9e33927a56350678ca3975ed7b2c8e6d4d567ad7ac0e4126b1a55dde552 75b837f6b344b941183721fb45bed9ea0f5ad890e05f465b4f0078b8c860ea30 626ef8f4b4cd5a655548792ac7acb47f3bd0f22411a91d39a6460773b8b962d8 173a57cf7b632d55ba237914c471a75ebd5023b471406501db025318dd7c5afb 6e50ed5141d31c5c71a32b01dff6c0f9b4d1fafcdb59e7363d0b0de0f6f22049 353c625beb6b2e3ad123fbb9a5b2596f38021bbeef7b5e0c10ecc9234f6d553f cd24fa2bd9e36f5ff131b44852c0bcb99d82f055792ed8dff48e837d1b4212ca 2388231a621404db213c4060ff612cb04291828799919be7462d9b0ce8db9642 f05660f72b5d1b9c8713a957604cb32b1713a8cbe32a3ed4c30f9f581d1b10e3 a9604a2437780030a1ea1fc481a7d2d538570c08f355696156a8313c4dd6e00c b759e38e1dc01c64386765f177c8aa429cee8ead275d6ceb7eb13ba22780e430 aca51ddfbfb7e7d73b15a6a620bd5be30c5ea0fff58d2952f2764ea5378f8e15 7bebea82546589b5992f3990f088c948ffc03551d65275de4cf379fe08a231f5 637176cb1b76867a4a07c3f4a7e972ae621c128cef42937a4f367b5b52688ae1 444b6b8d2ad3f031dc9884f1c8d33bf92b0f8e51a9decd2d1853a613a09ef956 d2cabe5ea3d6b8e8539f6ecea33ec239ece6b1d362ec8ace8900c7f5d32b18d0 12ce5eb61291c983d787b75b4bd4b4fcec42870f7f1cfe8d7864d955705863e3 ac72fb9e0b6a14fe14a7e4313c3df10a5f6a4e424c8467547d02ee590df993b1 8e7757946777d73b44177c05b9beae59bc440eac16f548c95931ef4d608401cd 649919d443bfae5542ed3d35dda9d4a8f5027f8529eb735ed8cb27ee408783c6 59510fe2efccbc9e071932f0a1d6295e9f0c23375af49e4e1ac630b6b2ef7ef9 bf061ea258631a7bf55c1bb2e782d745f8fef6433688f4a254b5871f1a59cc8c 37542533284da65b04bb83ee315c0aa265552d9583d7805f02a4d683ebe87846 b32389352f301af4a588d53580b2af6d76947705d45414e18c4ba8865dff0f33 a48963364cf079b4fe0128013c3d937ec06ac6d3cc4adb28165c39a37a0319fb cd8c533905d1ff627b1e69130e3758472c86174adb5002f0220369d51133aaeb 61e6c4d613863a1bfa1f9124380cce995ac5dd90732f5979bcb29d42de22eea4 2d0723b5254153eb2a17896b7fc15b8059d29e5005020ef172aa50a9ab7ea7a8 8a743f0297f0efe2ddca293984ee8d3db4f5cc461c2aa9a487f13494b59fa25b e6a3d6ec91a5aa85079c7580779b393a04c54782b61e0bceb0d0d0275cacea69 0df4d37fb5c42f35624003a5cfca5c3d6eb240758569b5d26f16616551574ab3 c9a427a5d45f34e865751f516eb16c1645fc0d34f9d3f29d15defd41c90d7c11 d611c94f2cfee7f21c74272603f669eed93b9be74f5e0aab045bdeaf02218e11 4a414aa192df1dc95b7bccd5352c3dacee9da798a587be8d0dcfc2d932e53c23 d432cc97206bf2028e875b0c39e098029670e2c499b88ef451b95c526bba2f09 bc4594e40b1c06c2dce163483efb045e8378d73d819d4364f8fa4519c8704a16 37e6a276d50c83cb2ab5a9f7ced57f2b229edd17e2c91fdacc32d93925f10145 290c5152fffc9d95e8dc1d815fe633cd79b1427d50c8db67e8b68aa2c4b98470 8fc57c77048100572648ff75b9c5d82dc1c0d0fa5e06f05afbf9f201712adc4c 0bded12b4809fc9ac013e84373728305e8464ef4c098932625294a4011450e25 b8e535e32f58d625101ff5318e30492fb54507ed21adce526c366d5d3e6830e0 f8377198a04f2faaeddb95b3184eda59ab45d52583bc8e6277e326817f5e288a 8ebb388549bac9d0bc58a50efa290860bafb5c558ac5ff97014e5e91e2b5d6f9 80d45483e6d7e55efe1cf485514b7c9b15ad69550f8228adab97dc7dedb77f2b 860df3068a952468d7de7b480ee3c114e2af6a1bfd57718ea70a532156d316ae 1e7db7133690a920da094aaf299bce31255ebbd1ff89019334069563d97a428f 4f729843f70c02f0aae4848573fa65fc44fea293545643effc5a0414cbd02b72 f6ea093551a609547278602ff331bffcd788474b51a4ccf1576e9c8f4c351d0f 0d1425e162b53dad767159eefcc68e3481e7aa15cf5dde15c560e86e9cb0735a 0e8af77622167bf235723615c1c3d7a819ff60db6c5ae32e80f5a2856a68daaa 1e59be38167c56a186e49d712150083c5c1dd7588433b428e23062de4221fff7 b43c12e5bf504f04fdc66bda7621af9e8660bdfccb2816075159ac2713ca3f2a 9f7666358a33abab5fd147f4adcdf57bb32e12150c079be07d8fdeaa9ea8beed 521a52a0b3e192d19c0d35c561f27f6eeca58ff13e58d529beee488b93d2f3d7 7e7effff3b399f59166c50e33f5cfc0bb5260ced230184bf8992be8dcf887b10 ace89f0a5fde0334b8c4cd6372814c0e424e477a71838d4ee4be6b063ef83011 c6f19f9441c7443c9aad2a73076f466f5e4f905ea61c3f294bc20b588959ee2a 2a635eef90451e80ada03b004c75fab54e15e69f569f9c3e9129f58c713f2b49 4e822815ba216e4e657c7a4ef7937215b6d62196d294345f321589d5dce6e209 a68901cbeaa21bee99f17aeda62c6fe986a2938494df1fac1741511c6c11cf88 43d3eb9c030f4d9964d5e9c6ecf2b639525d633187e11f0da531bab6e5ee8a01 c0f196df45d950b38ec8d2629d12aab07856874494c29f71c6881c1d9854d2f8 43c9afda75cb063deb114f4b37dbfb0078f8cbee6b8cb740b97227cddffa6a18 d75e8c0dd232a4a241372836d8f12afa216dc4aba3cca2f7238d37cdaa54e1a1 edf772170c15f645d820e11095642e27a7fcb2a1850d1fd08f3e8f17655cb480 9c47f9140a0615c1532835e0f0bd405ce7ccc8b385febd11a1bdf68f3e921259 9d73a698ad37b84a11b02b49d6bfc96929194baba505645ef1761bf23fedcf61 2643526f0e6e1cf9f2af821278710b7065f0cae0ab880e9058c329ffdfdf3ec0 bb76790fa787e08a246e71857c921d4748b40ac6e8451baad291e7424edb40c8 35e56823de9d39318ad185de2a1d4568835b54a6abcf49eecba5f97ec49006c1 9c705590ea073318a4982ce47e67d422e751ad1043f7510e6a104fa8a7a81496 1bdfd29ec07987e54e8c928f680cc028dd1672ee6415727a8d66639cef804fb6 6b389fe787a31b450f1818ffaa0db20cdfa9b6880fa0ce0a7c541a90dca40242 dde10545b8e2fd54ffdfd483c18afccd262e204605538588848ea1c0c99efb03 4a10e9ff89c15561cb1157fc992e6133281504420f132623b9de3174658d28c2 c05d11b8468a9c285d195c40436785b680eff853135ad3202d85fd6b3b71efbc 709d67e136f6fbb15c6b209d56c894a0012d3a3010bf785ec6cf46565c7ecaa1 a551208cb6283ff8ddab393d47d2f930957a931a70212bd55fb83eb03497ffb8 d65abbf9e83cd429a72a2c0b11e878e5a4de06c256eb6ae8b7ece0ee7ec4e899 a03de6ac90e23e9470213a21b251e70e74ecb35077f2485ebb2f7d150c1f79a1 83ce4ba9a0d68fe3ce9421f42b8a010c550fb6100c2f8ab008be42902f3c036c eb4edd84bbf5d3299f3dae898586c55b4483e0afe6f4c253a779b59a928d21a6 10b0465dbbc0d6ccdd8fefadffcdc6cf636c7d6eda6ba735718e6e0bd4ea0ee7 f77556104aa4371f176d65aa01c0c3dc4eda40e10df76beb443b450ba666c16b fceac9a002728d8175618729f289d0e9a1dfe429ef2b9758aeaa63990daf74ec 96ac9c4c4680607dd545ce646e48ba793e146988aada0c9c0581a41c7d9de1d3 0f749c6dfa9b9d40996764cca834443751a654c1396161ff146f145158946816 873a8cfa29fadc9e3fa66fcdba54a99c0d5a645880fe162ca31dab20003988f1 a9138ec342b3cfd9fb544a5e5c148d2ca59bc62f54d27fb22c39e6f6915ccae3 e43bae09f5a5fa2596ea6f61dce8d81f0b9d51ae509cdaae1a24b963c274ef40 7a1b36d60806fbc951852e66ef531c403925e9bfe7ac324fefbb2fd81a43baf8 fa159a7bfc0fd40d2bc5a70a1bc9852bf10bcdef8d8e2e1ca9c62161b719e1e6 ca533537e8ea96c9b417a07b70a3ec07d0ec256f1bd14e8b34e4d1c89f72097c ae8e7c402fa13e037cc53e9fcabf06811b17afa21020b647b1dd686834a2d67a 6ae329a6be8103e2e4976e0950fc024639b41b8bd921ce630667216be4359db5 1a5b42a72f462555ea91f9e43c52f9154feac0f2b4ecdedb450143d090b93d06 f6e54cd2ebb518244dc737487da1d95e303b6ab78196a6c9c59212f18824b0a3 51ad7925ae3bec9cb057ca255ae54aa59698c225e5a0293e09d0e83fc4e5b7df 4984ed3a617023b33947a5644cd4bbe8ca79b562a091f7f61080c96de880a9fb d9fae11e8d2eca9d88188fa1b5dd6088ce1875eedb85335003b8aae6abef11d4 e20fba3947dc0003f5b6ebc49a40df8c029408546659375d5b22974c7d3ed1fb ed2616df75233d9c496a1f0d130ec9bb0a318c0b05047fd8bdcf1ebc2edf62f8 482f06cb80351780d5d49898014ed4f4582348c4ad33f283311027382516c54d a59efdaecb58328250fe5e506a5afe36662a5265cb4782eac0b3839761e87b5c 8870807b28a03b28321d06f6296ecace564cdb01d7f37592168b2d2ca075c7c5 62a59c15f0a3dfb0c1949dbe156c2294db9f840dc3bdd4b342b8405aa28391fb db2b5cf69db1da72690f3d0bcdb621e1ab6bf2e8e40e1d0202dcf35d49904067 31aa4a6adbc872d7d62189c83880b5ff945bed0da282b75dad30a60790c0f33a c93ac37aea14d028789d9db7fa08d8ca9856c4b1c101e5f212a2bbd71e1784d3 c8bcacc9a538112417d16664b694a837e6d397b1a4de3aab80db4d326750cee7 a784aa9d92f6e9379e576afd1175a0e5b0540f3145173fb30784a830e85487dd 0a564ec2c2d56e504bbc96330a884de705a47a25142e44e039b915264d74fd3a eb71a630f352135a48b03da1d0cf9d96832fa4ce940e6bc1ff73c47371b47754 36431d4d8a33fc41e52add24baf42d705860d5cfc83aadfccc6e4fbe7c6c972c d16de71a298b57cb77398f2f0226415bf28602c439bffe0334b0d68905d219b5 4ffdcfe47d26d0d30ef567366c6eb46275989a8f847b39a8f30d89a74ebde012 1c1832371c681f58df93c6c4fe33292285a801c2e21ded6e8191f80800b981ea d5fe4823f4b5c221a275060de1d1bdf72672dce31bd3f2e0a474fa8c38c41077 bab9b85dda37d0559958c8cb542609ac95a5963fd4631ccf14a6535d320cf9e9 15f589eed4f67eb0593ac0cba466f4b1be1bed4fca49aca54536a4a24a77f29b ed67d0fca43e7dc33c83efb68375c3a19fa8eb4536c49acc27621ff542e86ab6 ca9dbdcd0b47bf2adb0e773a0cede86e7dd2aabc5e6b95c2fc3c1b8c5210ae59 5e915792e97503b657a9338082f5ba9b24e2fee4d1a2938a3acc902e3dbf4997 d633420dafefbf863766da7c63606d22239d6548894e26f06d891bd406b21524 cf4f849796892dd5adccde35a74999c25c6ad3c803718becb4243ba6d8c7e1d5 13167c9c0793e87af248369823f245d99592a43c60a15349d44fe233c5bb9f2a e183538bc5ee8ed570eb66d393fd056793e269b109043eae5c01a0a090d18b3e 276b50ea1b15f2a45809d0b22cc7834e406c1c1054c0c7f7d8525c32e10719b3 191e9285d6d24e95fd7934a09d44920af2042291e4c9a8586e0515225c8413e6 de9d26522bf6f4ed113d33a1443c5f160de08691daf09272e7ea03232f4e6e84 d333341edd7b38a52be376f8287d13f89bf6b01dea8a50aceb421d9f602b05a5 b8a0364136c444f623e4dda641be4088ffb270a4c455c6e941d341681c6d6d6e a62fee4d42bc6db241ff2513f2540f6c7db4fee24b129717e3004f95960397af c259025eb09f04eb7431f508b3c6999a3418bf6bab8af9b55cb7d3fa4eaedb87 c27c59ea6664edb562515b3f4c71715483aac96657e26017b054f01777db8887 f91c66dd8483e994c9ebaac3688076e8797d4d67668909cb2192d279d42379c9 9f58c7886468a9c83ba1642052eef85ed2a45ce928fcf10d173f4ff4c89dbb44 4653a8407b6d1cc000eb05b1b840dd14bb6188ef1d37a8ac72e12217fe166472 93b14e264350b8c30798d2a39b9072feee77dda2b52e375196b3106022b4bedb 5273509157346436df1b7f67faeed0c0a43e999f398466c246bc2e9286b87459 10017a84966ba1304aa896dc9fc5708330f19054d064687b39376a9fa3241fb4 9d9063ccd0d2d540300637243750c6bc652c1f119651f81140e5322879d82f8a 940eb9dccd7636d1f891ade6e4306521419fe57030ad7175cb3c8da0fc72304d a820c82bb599fdf8d086d39acd194e39f132a85f18d8300f4cc56a8220d225c2 233233f3420506c6227ac4a72e37a4bc907d63b291a4f32d4123a7da47159514 7fdbf1d6f41f271391410a05f7a8d30e55e1777905673b8d4b3d5219a47d656c 2661848fb57f9b929c0982fa6fb2ed2e504d2d44448cf087952dde5ef2ccd758 07621d43208d862972ab719f654d757ceccec4f52dd70f013e2cddbb6119219d 53f0af85e186307dcff8b2ae1b87f9dbfc5250f5bfdb77f36ef3aee934853150 34c6dc33ff9b31f5cb61c6cd9cac5a57494ac89ff81f6eb768373507e2331f2c 9dd70bd2b9228331231be2a27d40d3d9a1bcef5c5065ea456f091d554b47e5ce c3f51b37f9ca58c36acc1832c7a0ae39d1a4cff8fd2f754d3d05d546d2c0c561 362b58b15e123a74435aea20c0c96ddb5ffc7413018c4ff6540b6c3b941d1b88 445a8726a0fe3b24c15b4d1df672059bace0614e07f61b3984a624b9eff63aaf a49862a6574460fc5e9b3432da844458d407ab51632f28b02aec0d5af356937f c9d3ce57149377557ebb03ffd48b85bfe30f5fe44819e2d589ebeefd05f4da1c 9184c6dcbadcc0425ca7f69ed9a5f120cb9fbd9126c761cfb10307c90f75af22 e5b3ae461948bb71e5d5c1c408cb68a6fc1773b89f2a16fb06bddea5bebfe84d 78717fce1d774e4857b31faee17551a3c70a161f9733b9e82cae3839de372e16 5259c3ef1249d01c1b4270759a51df4e9c04dda8a96eebf5031b9b49ad47a5fc 2c0a3e8f2ac581b434d56652e75893f86395d8e34e0852ddec0496e35e286b91 3b2af796e8f040399653cc212a709de714d5259e74869c8ca1879c4c35b16acd db2975a318a9c2e4e8fa5be0c62e49d88c4b6606dc1d9cf71538c4508d68d92f d5c78555d2a2a033d104c868c3d908855247a0a9b31047b29d169b1be2eb20f1 d1a7e0ab08e9236ee740f2c42c57212be40213c9a5507d1f383d1c8371b47669 f7b7d65be98a75555b03e382aa01c790f1952dc8f8b019a34f2d6418ff64466d 773bc5294e59c1a624f89f25b359df72dc603234815e17abc0afe586dd33b264 25806ec523cb87bd4c3f5425a78fd7f879978b9d0a5bc2efc4db3a80340b91c5 1a0d6042f55fc72643c05ef6bd3a0c3a05589780b9c37779dedfabfa12be6926 4474180e2cb3f2d3ee664c63cda703a82d49c0da75dfdca8aa10c173a85d04f7 d2f254cb36b039214a42b95fdefdb45ccff86bcab3f38bbf3f06f7bd6bd6c324 fff1eb7b3a6e85f22fe1cf95d3d7e28b2c750a3e711b75ecda1f21279301cd7b d82cdfe7dbb0d0d15a39d392e5529b61256cf6dac2ca0c04f10c81feddcb5502 1f7426796d532dbe452a65916e56583c418ddab1d655dbfe9e4047d5fefa95c6 acd10a2211b93dbb4ab82079d09b712f7060980023a09ce67440af525afadf04 190 91dae06180a32c3b02c5218eae4ac14ce9c629a65fc44ff15afb2f50f30f6804649b4dbd8f7c746e09977f7ffb6b29b3a7e8b4aeec0cbc1f9db85a9124fd08099dfa126d9f9335773b043a888f7438836e105cf31b5b35294403ebdab3f768046da009bdf6a9950e0735f88ac3d33bf1a8b38b109afa3830991e3e5a708ecc0b5dbe69c6be585527e96c50362a69404b81c18900aabd1512201207906036740129a050819c2932489c68976ebd621614b63797fec6dae88d1e80b5181fe66706ff0ec97149908b97947f5ccbd5298c2ed346176ab7adfd3788d277bb403b4d00caa8f3fbe5cffdb903d9ecceaf81e5db0b0151d6472b5e1c2e976ae5c89a040fffda036ed7228a7c40ccbb6918f270b227b1c821a73fca4edc92dc486d63ee004f1ef59f87f491ed3fb801d96b499bebd8a794f8c643e14ff0c0061486b58a0b100c33f4b94e26912b06a009bef9a6541b23311b865aaf1a719844e9f9761e010f77bf3f76c6c983f88d2e8d31d5af52899fc1c1d37cd879109781937b0a240ff7c791420a9efd7cdc75f2bd4bd78cdb73d81d3b08b0aee6228653bfc12cc908d72e70745e29e0f1c8e13067d1d02c2a680c83dbb8baf6a290fecb1e70bbea0bdaee2bcacf997408db98949ec0b36e29eaa190fce14a510ea2dbc6935daf0c01fdf17211ff4e74e7d0594f2d9ba5b99c29ca61a0a4b29c1fcb5035cebd171d0e43eb63200bfb73b7a2e01049a5f0935829c03e5333e158cfe40068f68ba4f80da446438ab8fc425f9a43844c085bb7fec6e5cd47f56b15d63ff26b3b79542206f24d3445cbc746207a9456d3626bef29bfcf8e67316fd2fdf65cc6864de8e000add13b62bbc801a87ff46a2a2773fdd883e463e1e34a73c6d7595f0a09b4ae063942ff1fb61d3479f0a1c0add58ca93edc7bda90e1198b5672802d9093c6a00905089c50c440f7dd9286bbdf24ef57a4714ac09cbabd7e6a19209b9311d6650ea87eafb06ae4a8125bb3c18c81a7519de252d0edb0651364a4499c19a2ce5602e6f101d0e1c5d4029e2cc94816fd3cc3c7cc19de8fb6795082d55e4b592d480f126b5ea3d12e0ca7b4856aff1707a19402580a79d5c241f59c0956c15c96a70fd3c54366537a68e5c689b934c1ba22955e65359ecd9f5812f9827f0507e7ae04fe6d660a566dca1d40b6e8c91adb94f3443320bd5394ecf9c41d4df136896d06f3068bd9ff11899e62cefbe32968127e19cb4bfe19e01d100c95f253315258084fcc6c02d5a9295b123712de898aac3db16be9ec5f4447ff6ba26558856cf80e7101a8909d9933d8099b42a4a80ea1f1fd2dd65d1d78bdf67ea247a64f6cec05669d75f76c57afc346ddb6776ba53f41f65fe9a1f4dff9c8238710738121e006529e87db539acf9df114a9220e1e5f0e2ce25dc5ae4dff504ae3dd5aa7a81901a73e1e7b9d32c9c70d5b81efa57b6c259633b1ddc9ef98b9a00c7d341f5dab00fa30796922db48d929d731a713447927059093ca4f3bd9e2fc07f5fe4f873f0bbace7386c733fae904e0e2e528efa66c9e06ca5e3330fd980c41fd72bca38e06520d6f997f6f6bcaee71602b7a5ae2f35bb6078b6bf6d815179d32f723b8e40f4cbff32f1a32681aae630fcb454e9e0921921bcdac9b19dc814232040bf7ba0be80152bda1b084f32312ec0b33f0628101e5f2fde33ed1a47a170a233f988f0c5c4e7c069c66894d8ba3ed3cb9ec47d5d7010ff1c162484376d1595a09dc7e00d85454b69212351e18ba29afd9bd672867316b78b34192514ad87b858b2f5b03379979a57c3deb37fc8e437354956609ff384a5f9177f9f29899dc5cc3647403b4f3f92942633888f2ddfcdc636463a3dbe0a4bdf8b37682d4cfa2bc03a1af02b243b3ed11c7e56bcb8518d2af6dd97f52143b64d3e003ebd5da95c14bf5040d464ae3d976e7bbc143f87fd63928b1cdafcac2e65d5ab5a4e7c063dc576fab03920bf2310814694920ad659f87290b245340124e5a179ff7e8e1979383e5da069f7458dc3b9c47917d710b431d26c3884d37c188a9cb3f9638ba323fe8cbd70df3fff5bbeabb10ed8dea39176b3af732d56c2acffbe52226557b9d304b87c50d108cd063f135568fc32672dbd1986062ca90724e54adf7483e5206240df20f0588bb84c60859b8dbed44dacff238cda8b9ef478b6396e096485b8d52601395062578d736ba6ad91747d52e3f228f19b6eca84cd4c984190f5efcb3b3924ce60478df2e20845626432db7066dd9ebb07836ff742b8b349760e7392104231a8c010bb6e5db488566176e337aaabc15b4f9d963ec59f41f91567f7f5a2c5c625003a8ef2e0d4c0b471384d4ad6bb561e881992e74d317f8962cdfddd3afd1beb90d16264e0249ae0c139ad538be083aac8ccd8e6671f3972fc8942311692bf7ff06721a7e0c4ae333e78d366eedbd3378487a4f4f137ea6eb4f221f5f6720fbfe0f38f7bee76cbb6a997330bb4791e244fa6fb9a75731f90eb812c77dbbc15ffa04241cf3de420b6e8b2545998109ad3f35c5b5e50541116c609e6f1ec90a957001ffb794427dc5e4dc913a7043240c27a3350623bcae1f431f6f8c03ea41fd240aed5c0d77ef9b6785e866267d8b62599933da672ce47fcf2e9ef796b8a87fe50b1b8c4f1d30e4f6c103367e0a20a53e9ec0a1c1d90001f23fde8bac1363dd500e8d0cde78240711c2154baabc630c5dfbedae5d1532d5fdfb78d2b7331791e601eadd7a2f191e6061dce3f95c8490495d51b06f13cfb8a2086b0e3f89b8c8150d4bf14c576c1cc8cff43835f6154fe91b2df67686daad2201cb59669fede1ca0aa0d4f554cd58d7fc1ef49669dd20e24968ed73a92920482ae11df6c00eb6950437b49ca3c12db5d12bc2fce95900b99dfe45be731f9eb58c04b928d3b4a38809b709a3f34552904b14dae710ebc4f85274694ee77d7f721457904caf6a3c9308b4bd79276abeaf4ccc26c98173b96fd3fcbc905f787d2efbf9228ccb179c3306090e008d0d1b1e93a9414bf24c25d3311a7cae586d1f30c6bc5f4d2effca6a09941df962a5cb2a765663232e6720171174c0caba8772e8ee4b619e9025f106017e4fb3a6c32f7c902c41ddba24630f64b48ad42f57c778918b17eba9d2b853081f18f2c8d4e6b0fbe1d2ee08c1b6ceba4761a0e172ee2c940fdd6d0a7ea576060febc836130cd04f3e54dcbbfa430556e9fe547f59f394e565c96d5936dd760c02e61a15c1fed0d247bd33b57353977afd85f6322fac67292ba1a6b4f768e802a6f7b248d236f8abdf7ac55ee0a107eef738c31bdcd8d17e68520e151582bd0789baf0f9b6c37ac95c0dc75824b9b1d091fcf9e42b80c5fe8ec1c35d901ad506119e56966b63eee2e7ef053452bd66f5059ebf6aae5ce0eb0ecad8e1758989006f86ca3d8d1c85897a09eaf95f863c9200a6236305ec79ba2fe9434fbfeafd09fcb7d0fdfecd767a968099e1f755d085dbe36b8a247a009e654488438eb4f90ce71795b6e5dfc2e095ee0adf68338e8eafe6a7842bc8a9df00c945caf85676057d9c4cdab14a9776f1c4e765d30f637eb521710bc8462d568e6410e4eceb3608751877fbcb2b2bae5247f0094bfc1f675e160255919e21275ecbec348d21bf03dcb3d184870ea5b214abf0a59e8a81fe0971895b9fc8a9a4c1abfe7af26113083423db4470aa9c5f2a17744c1ae408eb84fc56e9988af5a3816c36446f041f00fc3d732dfb776c5ac88c82fd914641e608533461a6adeb90d22d6d20a3c9c90c98eda024ee8e7516b0bfaa4987662eb804672895cac25483960f010db61e5402cebd00a1c2a212d1077b33cf684d70804523349de60c69627d8d208b8ce0d20276d58c8ad7600b9d0ebc05a291fd9a7b697845e9a4db4123996414dd58ff130079941f0b3f6ea62ec2f7e6bd2bcf6d171a13965df1b78a63ffb9f7b4fb07780534bcbbda694e23948855a64a6dc033fbf9836b3d2c8c9e341dfd5b098621bb0d9eb1915a7c15553f9f6f7322cd57334ac147ea26a32d3f8fe8b3eeb59df78600921d7f37df0a335bca22f6d6033036b3015df9b85c704a713959bf7b123ebb0b26ba2149f292eded1d35f269f1cc97f5ed39ad726d2ffcf39f01635ad365050e2532822ccf263df7a37f57d4a92d398e3c69aeb2db4333a4ad8a31289e602c072f88c7deeb21faee4e2e5edc8d44e6726298971ed614f22a7d6e194ddf94380279678a5737bc6c66757c35ab471fdac89dbec0038a8ae1ffac103c922ac9d903ae97d68692aa51fa3b0a124b737949fe6b837446e14726fdb37aeb8d32d6700932a8e3487de282779e4acdca23c5fc6d2815b04680a88ad77c7f107a60096e0d900094cc338ee81c6c9a31d3ebbf45e925ff3701297d0b2e6bba4a8a7b392306e588b702ecb907784bce3f0d3925499fec8ecabf6ab3a1835bb9c0e4665c850c399cec505ccee4f8d3e93a8885c481c4e49ae83dca2e8cbf4eb9c731717f740a0fd06f1923493c076647bf7e5f67aab18ecbd6e1c5aa7bfd11638f3e3f58c40da3e2503f4f2d6e42c6e59854f0908ffb1abb35f118ff05cd38efeb020236c90f06b4e18c247d2a4ee1aa5f56055957cf8150ab2c0dce289cb8a3e4e8574c14059dc9a121d7875d69c2cb42dbd7e39b514b699d8864310cabaccf365e3eb993048f84f60bcf2467035c7e093161e39db898470ae91c85da85db40ee85cad4d702ade9f6e8eb2afee1f69bafa935fc89fd79593da0203764b2e91846593d1aa6053fd0f087874fc1c3b810f28b23c2d16be52d9e84ea33fc49bb730731e283400a9fa73b07dd605bc5e3e630ead6c8367742050db8b73022953d4c36ea2f21aa00a2bfe7ae0f5beeda4dd96132a9a84aef3b6b37bebf67da239e9dcb420b014b078a692195f4fac817c5506ed9c57dca7c1299876a7fe6d6ef1a1e54df9a987e0ce083e5370a0e920a72138ae8d67ce08732389bbfcacf359264d6aa07345a47009c32ce14458e89d7a3dff9000ab78bdec0166e18537ecba2e6a452b4de451d062066fd747e158cc7e9250164e810c4d1ec397f63b68afe3a5877a81acae32603b0ab12da9e0242c2d641bcb3108bf6c99702d701cd371f6ec7361fe9d9cdeb0854967c62b2702f8d0012a9b3f4fd8b1ccd49112213d85f7160f59e17c5df080e8329dea18ef49c6f513e4c24b94e62fdd82f91373bad888326cc0f818a18e009c4fdf30e59278a2c2b0e8ffa1539aa8aa97305b52728d3f329f60b796c7c5f0fc8b49f9464658433767c31c544515d8a321a3eed8bbb16007f4c019fca010003b1e0219c70d54d3b38b2ee4778bb6b677dfa61bdc7148964d39eeb7fe42a3809d3b4ba22d276169ebcfaa3634592f6aa7affb18fdb31407ddfe25b7cd910c109c125f9a8be911fa2652fbb0160b1573bb50dc86700509984c57e3fa5b07bce08c8a5050a89325a27c77e746693ac5326d2d7e52a9483115aeb1097f9d3c15f04bbc1c22d855059f3ad26c336dddab464b54a6d551f07b6acc9f8d56b103f630a6bbe13fc8d0753efb2b0ae40beaa06b009df2a7f6ff59b3cfea5c7718989770cb0caa371d9bb42961e3cdb9df499f1b415e659c5c81da41cd115afea47474204044c863ad26602504db19ccb016a43a7e222626a16eb5a0ea55576dea2c2790fa6b2ca68b9383c8d1fd2162d3f15f1cfbd16fbb8171d2234d2ef54b7c81f5307bf8dd8c21e4e8f2cb0544dab8b8c38e01ea90e133a7b8bf3ecfedfd976517906c3b9a213ae7a1ff8f0727bdaae386f9f855b457ee7ab219904f644804d21100293cf4e30e650d8d4358afb5008f6b76294caf87d5f6f21b087fcab93bd4fc109ea50572841fec74ce6bb93d540aa9977f48b74b52d33cea53513033c3176e60866a8aa56d33a3ef01cdca4ec142667b7cf9f9bb91aeeca2db5d0942a80160e059ee319dab36e41269c2af1d95ee90c75a1905e274b9a46c78277fdb6d7fba30f13abb9f6a00204c22f1c478de8d537fbb7d7a419e992c9247654f13e64a11009bb2f2f58b705f7d3fcafd5f273d235ad22cd5a5f149e637624810f2a7d15c9065b65ebfcc0108bfb281377eaf0465a9d5f348283e06139e9c31692a88ebb6704867750e20b49f577fdf565ec146a8627deb36137ee3cd19af656c2fb6a8963080118048382a852fcf6d7b63883cf7324dde6e80cdeda1073582cd7f589498104521c71b8f12c6c9391b632a0288077ce2a17c039e517880f9ebf1a54fd58de0ce25c0baf8bdfc83b272864712bc50052317968ff8d0fd777eaeaa36d51cb1805690d394eb19b872274e73d95aa9e1f57d5af90afc8de74d10b4aa27b7e7520051bdfdb548d7b881831f49b9bba55b0f79030c99c53386d9303bba4323b5fda07fc5f61420c049abd3a4d8539276ef14c5b46022b531d4109ca4a2c972912e707925cf05655af9fa2efd347708d4fea96e5330a70a5add156e9908c657fb56909616570ff6d756e41c9c6470f19e7f6c1948f159442604ba5dffdedf32866850db1dedbf17f4ef852ff232c126c566de1e8e10fc0a4d544733135652e4b7be601d9eaf2a2b25146b92506074ecd91d3826606bf63cd80451ac851d95dd0c996076e8d9e8dc544e3ec75151926099169f9fef5fa3c962e534607fb810310c89f029c1aecf615c3315fcd0b8f5498f7f102bb207d19433ecaaece0c6531372edd0801885f9822ac577988db7674e4de1f2ba7966527f24b62c777f41e9be516210f10ad775cf62753d62ac12033d4a415f65d3fd56115ded54544deb37a9839ae00afa1f722391aca00dc92439b14a814d801de52c95262f51ebb82d07f6b6f1b075fcb52ee33f6cd9443ad27ec75cf685002e06c8cd1afbf0be2f7b35330fe5f0ba2cbee426af9a537498ff7f6bc8f28233e8fc3db1ffeb329a907eb650184a705dcefd32f6ff6dfead03dc1c8c7da9bb4eafa4cab334a22a11be3a47676f4280d83ca57ea87fb46f5882af55b56bcc5ee6025fa4760ed2e74e94e9e5ca56ed007828910f35ae0c35b4da2dc805f064501872c4e5a2906524bd33e79817b28f30e29d29f097340f2121e2f27c48fe55f3ed4d6e11411b744ec4aa8cf6ff0078809a4e92071957baa59ca1b7a8f80284ded272c12f2238921b61f591d6b5389e802c68ac968b598bedad618d5f47d692227607dff4d1a47ba61ff6ecfe74ddd200644e681f4c35a164d7f56070ae61ad807186ae9874f058c9fc5a2d8f53e0dc207b9fd3a106414a8a2daebecf36b5de9f057d20ed61143f52b1adde856d909350c1332ab7a5b5ebe7300d793ca298212347a538caa2aa174b11bd66844bdcfc90ad42c371483d832d49d2c17f2265628ff483e88bb416f2c0996638aeb365ff6051b56deae4a8ae8662d20b721cb81fd96b999d0fc594ef0a9bc05c1c98da3db0343966d2630a8a96480fa6d650061b636a576cda32602fb9d57b54ee9deaf4c036678e082851e7c88f14097a1bc2a87e0d624f4fc9cf7aaee648553fc1d94580ef565fb436a2348da9e153e052d510c464e9adc72aa5beae76b65668c2f043807fe658432492c64c4eec2d55ccda78ea16a687ed971ef841a1dc6869bea84e603b3ab9cd290f41c3df348c0856b0b52feb35a9d2688cc7a3d3dc2f7f401275101d73828ac2a674b879e250a5248eb50ef970f8b76801113f2bff003ff5bd90904ba35c9c0f2928c3ec58adddc5e1c0ba50819ad468b0fc8bdcd76a53910568302c8eb13fd88367618391431bd63301441c55646ab6cc2eedc05e233de3a7ef40fc9f873b4d36d6a6839702f56af687f20d0139aa616052edf007edfbfbec0270febf061fd4cfca4e4cc5cedfdfa5ae3b0f60cd90219ba23b9963a5ae4bfba2d0a11e697d37f2ca997dc723c21551083690b0dadf3a8eb6cdfe80b675f57fa610f52994497ae1e7213332ec3a05049db58b825f9ac0df696e7567249dbe733e40e13b0bf7a574bc7f344a3bd75fb92acde599ef715373d7ea56ac16f74c00fe50c3bd54a9850ad2281a0de9629a8042feb256a0fb737ccf9e5e7223ad53c4e7f0f5e8ab2dc6720378ce8f8dcaee9d430462a813a6d73601d65425cf3c72ce83f087cb1100bc71a5e4863e44877b0db2eb83683468a5821fcf5623077eacacd2d000bbb5669ec44c80c47d44691f03ff5492430edd26ee9ed9c2610a235d36cd804e6aed960e4abd542c2393d149b4521ca2a11470dd8a660a8782f167e87d3d90846c330e39507ea7ad281acd0e0842c0615f953e476b6aba89ee157b904fa6105dd713307414d80018002cec2aaa20f70848e25c50cc1cbff7f890811b5397405d657b8f71ceac9da056102c67b1d506da30e8e703e367e4fd3f3b9866ae7a40aaa953272b2ab61d93e6573f2dbfa34a2c894a925ea42048a5f29d4c36531a20c3846e8200f1e7908e143fb738ba457a39530bf3264969b56f21bcdc093863b01cd9ba4b97fae73c626bd1bebba775d9bd3410da5e7ff35c161d0433be4403600a9a0e1abdcf6d4d9fc2a54b7b7c2fbbd987efa816f3fa503b1783bec61acaa0d2f44a73bdb0ff675c6f9ebff8a541595e4dd1c3c8f9f77f6a46bb2e575e0b80941c88bea17b67aa047171ec0723f2a6177f72e90442309100cd2daf3a8e91b066bf83e119430d3925bcdbb033651a31def9e0ef1b278b2134143d8d43257940cc70992704c00231bafa60a9ecdc893698c62653c6c5b147cdcdbcd23f7023407914c502e5111df0b196aa9c20ddca9512c3a66103597d1bb36b5c1abda9c9008f994493efc87d1efe733f521fe415765696691cc26f9a6d1ea4100cfc1c9b007f545d66894af9afe5339200927cc93abbb5ae09e4dee03850d141a8291d88c07527d5788bb4670a3016dcaaa1e31ea5cc8708d3ccc1bbd6620febfa220e8d509f9e134dc6a91ec51d62507dfe8e5dfa291c5234709e4177d4d1c13107035e50bb70cf190f4e15491b56196d6db28c2c86e7cd1aa908e6da422ba49ca68b3330fdfc2fc547a7d853068e2b43ee434b96097b42dbea15bcc3bfb85c25bc5160f0d53a226bd44f3c2f0aee264798307e579f1d8c9c16f51d353e7da0722066fd90dda88dff6a09b5bf860c998bef0063ae4c26112d434d72913a1ee89df7eb97606886116d9a213de523004a7e817cc52e61d978caa6b2cffcc43443ae190fac1010b6d73517196230af2c9bae8efaa0b440343edb7e02adbbb26a8065df63dd60e52d9f64fa4a107bfa05179fb551eb245f399dabb80816aca08266d0874f87b0a331275a71adb02c515eab7cdc96db6d83d5fff17151b1b7c87c210814ddd2002525d4eedba5640f0a3447ad10bbeaa22c75af875a6c9ed5b08f5a891138cdf070b33d2aac362a6a078e8ff0ee628810d5b1be449d06e0e0054c39a59aef01107ed271794920905eb30a376997a1cfa6671dc172e972e0e1d4ccbabc2d7994e0aa3aa7e65563f5546749671fc0d91789d75298312c45298cae158a30d5da4790da191ac1335ba4ceb5b4f649c093cd0c1929029bba3ea62613ac605578a400a04d9d88738e20a44c8518def60d06fbe6bc6d8820d724734dc7535de7bc95501045cfa791d55fcfafb5fb6796af6221f1ffeba3f0bfa2f867c8d773a15bbac6b059440d62401ab2bb315ee3fa2419e6ee57f586d60849e5f3f9d4c72ebf2902108f59233ff89bc30e2a8752163464db4817080711d54f58fcaf848fe2d0a5b380bb74bfc580a047bd6a46232a855907b41edaf556df8ce6de61102fb4db7813308e164104f51ccb9b8bb92a52c735e820ed703a709d19e77b04c5e3bba66d2a2030b305aea6ffe10438f6307efd7ff43ec44998bfb91a4f6c9dee1a826ba053707fdf442cbd7b24265544b42c3b242eb6580c3ce47ff8897648dbed8a187777b0b978a31f16975ccd0d0a6a44a6f6b63070da2dc155960dc5a906107101d3f8800794862402d395a0ca2de95d7e4d7a21ee69706e6ebf312e713d9b395080ea905b79c3845745d4c382826db160faf5d3d4bf87f8621a2f2983cd6f72961cdef0a26e216a99b499e351daf4b803db95c6bc72b57a54b17db5c4a28fdadb3d40d0d40780753d1f9e7fe7933ba267dc9399020e5c2e3c623d2a49a7a50513538180402d6f21e57eb71f8739efd4269d80b6396901ad8aa12878f7302a3600d3dae0930bbebbce8948af3aba9e2066e70b79afbcf5f3e036c5329ca4447c696e4af0c8cac596607a20a382beb88598e1c5544392b7eeb14734d1baff9c00e5d546a034afdd9ce9b2cf891a521ca6030b377d52f1790a5b30f3bc06af012ec26df9108e67a76dc1e6c748a35fd06f8ba5947a4c4ee894896345c0bbd7dbf1b632bdf019bdc4d3741a904daceab5df2f650fb419c46483ef9b6fa02640c5c80aef11f0c1b36ff945cd67dc78da67e90e7003d95e1dd2f343a1a302ce7d91d1da7c26600d8e0bd5f66eb9fdf11ae5871908809afa33a4231a34a3ca603247cd623ce7d0ef8b62e82d48c5d258ee253f58dfc01b999e6de56666f6f9ec2356da0c9abbf0356a404246b99adff9e66f32e2e95bfef63a46e4e6caaa9fa7f1827beaa639209ee3c6b5ccd0f8df3d584f0519430ec449f7b669d34844b2a1746a688b040e60af90238674d8d2ec47900141a6bf470c823cdb36b717530b52df807b7fa01540279022b502bc50534603d38f8dad001d7378a960152454d9f0336e8df9cf38e087662ec9b90f4a43cb141ed49be79fbdbeae47791067673b01ab1cdf587886c0ac9d70a206f65754dea18586d384a9bca6f30db22b04f31d63bd919b6980fb006e106718f2f8b7d8f9f2c431b5d985cb36047000f8e99da44ce6ea50cceb46305097c447bd959ce3280c3f6bb4e455c172936d8b8bfca3c7baca17c63f8dcc20ff4f5f64268a13847ab2aab05c587bb0a065ed3b2c2c98944a7736aeebf4ec60c9de4c7ef0aa74044abbd78800b848de18a5f33f48f74890895d985ba470d2c0a95921d22e5e88d7a746c84f0096c0543b2f400eddfe4b06a04b634b25f2c43060e4b00151e0862dfbd0c882a5baf3875e3dc574d849853992fca4e8edba64a0bf0c0913a5c2caefa9f1f372b543e2fad581659bf2b86d04c32b115b57042e601209fc0dd5ef64c69ba67d72ba389bf4dd478102e99957a1f8a518d27951343068473675f372ba861b895a3d102ed8f1c0c1daf15951b78d154228391f9427900aedf7311a79fa594acd29311df3327a4bd120f13cf5ffeff1633f98558f5090be19d0aed620e35626443a1d0fa9a652016852742d2ba5c8b9844b1a766fecc078e2af3a65c49bce92f6fbf377fb44d824ab944699d9d26929b76b8d42648e8039d42b6d87c1a57beb98ca6dbee45ea89876e6231774f9ea1e4e9820b4cfd5c0b68486b6d0818df67145d0568b5d8c87117fad3b7f94a3a3db2a6802a574ad804c8dde57905880fdc9938901acaac91fd867d2337fa278896e962a0e74bcac60746cbf9ad449a07ace8ba6df78305ee8e1e887733849b09d2df7c4feaba75920a7d82db7d14e3452c0ab08ed08a4d42d80aeaa56f41d656592966b6d6b542890efbd63209bb69a53561e6e28176c405a699d3dd358dd6e00228e824d5167ea406a9db3738fc77cfb68c8a57392186cf3ab6bba4de90493c4186f3f4cd06552d098393de955a26a0b1e600d2357c1bfdaa7e4d4002172f6f6695bfcd0aa752860579cb787b102c6531a07aae7bc8e379f3171be8076452f6948a47142216267405c14339ddbec39d2263c423a5c562e2d89daf434d3a8159300c14bbc3941ab00e4c21e550e23e9c5e6925e48e83eb1a9aca0026ed9f45ab4c03bad98cb110d40d11cc7e57610316979b40c94a91cbf83f6200ba942a8806ba71e5b82726d3ce04d68229f83db82468dc6ef41f40c50e4609d06dc817b3e26a751e6362994e6b0cc599c5b863f4d7ce46af7dd8168335207e08e6aaa0f364fa8140851bb2227e0e5cc3f3f2a3655040d32ca9e318f93b86955f67ca2a0acdba5a7ffb4580a66b08896cb3776b658aff6a7b2f1ebb25ab20d39d87a5f1d2a1ee7ac8b7dfb66848076ce1807db53c6b6e58740603094bcaf45a4f551baf0a2762069ea1aff3186c0b18dcef53704f18d4a278335f80b0b4a34c07a936456f4771d1c5a095a6f72b0fba9c665f52b552fbb94b064d161b4f1e173c9b99ad9beaac3017b73ef1d3590d0130db21fa48cd48cc974b963a2f1a0f143072747412921591ad23f0c3e672046f00914f388d1c0ace9a08982c0a85d7c2c855e846825632f329f627bf031b093db8f97e2cf66318fd1029c7b6f110697a2def0135084edee2e755bb8a38cf0115f7ed905d5e878d20e2bebd237cc6e1d8700675f6a745a9e031fa4826e2f7052d41aa8697b224afd4efc0afa3289f74f86cee9319cfe8a5b068c16bc96f740579d4a87ea6e20cc4894b491d85f3f6bd5f53adb07773fce2f6ce54392b5a4408bb6b3d653e40d81997058aa9c62317c42e788bbeb61b523fc774fe8bf675010778fa38987d06b237d0674b9f5d0ac8ca47fd9ad7c9f7f9a66b96a6e55ae3bc0ed08a3b43186112e735d00801b77e3a8f5c330e29db8dddcea26cb28409aca509faf1f4550c5d8ebf707cfcb40b4c8873e50e5b69c9bb214c100336474589d80d27a8b503f6c916f1e795b7193d2118c7a4157f8d198be9cf967eebea6a57a205ee133b0ccf95531313a693c2bd64995913a6625f6b83a91bdd7e83788d6b010dadaba59ed93c4902a23babd4f0246e87010618ffee1aa720d5587d9648c8150a7563e51f340de57f6218ac5e719dba15be03df5a144d52a6fae34b6d7f9c7d0c9b3c67f28b7fce791b2364827e41caca673afd3951b83a5eeced6e013d1e47094253c574029339e7461941ea2683c325e42f8a6a63b7998da597fb81532771056ac413f98c97ad74f263605a3228e081f0e93075ef8f09dd8fe863c964c92f0512face777d8d42478d812f33befb43aaffa3b03e801ebb7234afce6664bd230b96a7809bf6ad1dfd5462d5931ea61fb8e55daf4cd999e1231c2b4e068012c50ad1ff32c4b54efd4c281ee54a950b2bcc1cafa46bce884bdaabf277c8ef589606ae1c919a4185400c197f71a67c1c3b6a26a8baaff87a3c4329d5da13a6be2908e892daaaa07a9d785fde0992d089a2cbeede5e10fb1afabeaceb8c94ae3a3a08a3dfae81709bed09b3598086a4a0e1e586df8bf03e23a0a376a8c6db5246a30f520d8541f5e0002ff082c94b081358e8c065c61e0d251ec97efe6e8dae4bce0ec14dd5ec3d784dd5bbb65489a926d794401af4464a81fdd41c10f5f01ccd7b0d45417765c89cafbfe6723745444159dbbca31e6ebb0fc637b34485a207f5f90bfbfca520d4bad2b4ff3b91bc29f0458c2c3b8612fe29ed6d3c0244c2d6c22f0fdb13e5088ccbc1603a9b1e2480a5a8b19b01898f8695ef57bb7205d8a9287a0783c121f221bb615036e9a333aed788d4ef208df46b1cac1fbf3273787e055c0cfcb259073ba9bb09310520808821f9280948ca4e2072662fabee3f062eae940ca0b0fe6d4e2db78fc1e29e18d9fbeb12d6d7dc4adfb72e90488d0e2c77478901af88cba160a94b41cfde1ccc7f22ab3332801628e6882830a6e42641ea02d90d0310238561fba2423b43ef75f003606fad9afdbf2a040831269f925715a0c3034027cc831aaa5cf28686c36b3b89389ebea5e0ee36457211756aa3dc9bb51c0603aac2a84f1ebc9e676c94e17dd0750ec5ec22fe87a5a30737d04899a627f00c74c7837892028f8091b4b7be653f9cc6ffaed035f7a253e1f21258c3e2698607541c66f1a6c42778e9d6b2b45e53329d4bea3cf04bc8b83ca250f148fe0b10034723e0e38513938a9f115a576d385a6dcc3251aba17237e57b5d214a5b19de04ce4af3634d5824bd124a48a727f318404a87b9dc9cdef7f6eba710e46e636d0763a5a5b5e482a3f46efe1c2cf410f5a77e0e7f3f8bf0fd361f3e7f956da3300800d47e4fcccb3e7d60cd4c9c224acd7942e693e7d7b807025e8d2f8dbe402c0db64e5713ce1aba30c0eeb1eb99cf680b4d6139c26abddcdeb94f796298038b0d0ba52832204a46bfa1f1721bcad8101b914e712bbc1ed58087024e514fe0e307a4416935c00a4b811ad4f78a77f915e25145108c1d683ec8ea85624a1fd94e07005a282bd0e9deefca82dee1c1bdafe99f3030a15860a5a33e2c043e3504c10da2cf11a1307a92754b5b807907913a35a9a3b2c3ff565c5183b38cedd4e5900acce4a01df53611bbfa8c9676e11ba13b1b5b4f44121916f87265fed991350503b9f090c3afa665baaa3b7669eb18403bd23a43d4bf247c3d9a3533eae3f3160dfa97bdf61160000b2f35291aa6c6847fa26d3071ebec8793c66dfe90999ba209b68b502eed1db8f075aba8ea7a02040253a5303709f41edaa33bde016882600c2791e4a42f8ae4a55ceba608ef1d0a9e75557ae8a66a6722d668fe29ba229800b5726c5dc041a76b2771a3d4781df098635c8271d5d0c5574a66ce24e56bd9040e321abb4fea1269df0333724ce8efd47b15e9cb8d80d2083c6768a96f4dd50a4f958c0dee540104d3b61ea658c9fb05d371a03ba802dea9dde14b2dfb771b0cae45da2764f7f6ded818ee6d0a1032500f327f48335c3928f7fff6af469206011450dcf4f203ee43a6cc8b41d331383eadb09946cbf5956f79c8861ca610610599e858febd45996caad51720ac8d66e075ea0bd4821de154053337fdc4c3940e2e6acfaed305400f196820eeabbf17876ff2e1b9070a4f5c011f138bb0e797065ea529739b3ebcc2fb1a5563f62844562850ca9f994c235fc088c74ea9fe7d04200cddd1d260614e60de29323a0984bbf44522f14f9b032d8c8ebd1f9b06ed05f3d5958ef7fc1da6e2d01fc0f402e4be55b48d3e2448e5841ae2789f719ca004bc072814cafae46928669642c7b75add8d184f88c5ec2465a6c587a556bc110598033b7b6a9454754abda131ac45dcf195d300bd805244790035c644a77cfa039b26a7b87be7eaa3193b660cad41695bc6f7039aa2395dce1ad76434d25f6e010136a8b0d1190d59a2299bdf3955480344716df405d804affbd32b88db283b006187ec38fcb5a16f4aad00293a833e68eaf73a4ecf5afa60ffc507f709b0e309fa9c51aa73e4cc70f927ecc5675e839176ae7befcf86fe179378b4d1898ae400d20ca7c6c2c3e8967369e52e2ae41397572775ec2583386f3a1c2a3d30896e0d07ef5f31b4a04bfd61f08f76e02a9b04bc75a849155e8bdc9a3249308ddfc6007007ce7120d228bab4190ae42a121c69ffba72f122cea31eaf9068a1d355b1054a6295dec77ffbd824afe5590c8172fa12fc6ae6950785a101b6ea40418a2506b76b737479e09af0f99186d14de0f4513fc4b80457b7d80f3f7b95ed9d3b1e07870ee50c593513b6296ac361fab232d53381d251ce8ec4090266fbe5adb8d7034302647da066e492641819592424f619a38abc7d12b2493823334cd3ef612a0bd8625da2d21a37dcecebf1d7a57c05a6e8bfb385708533b8084505257d888d02fe0f7fe52ffd1877cc357433a603d29ebe9c6b559b4ead2a3fa7bd7985d21a025bc4adac4aac357cd8b97546de4b24bc4a21c01e0467f61115ac3a948b73260ff8ece22080def2552d2f95aa2deff214ccfa02baa34dd42169260827215c320499ff5d6fe90b58c61cf0cb52c36e182f842976b1a361c12e61b859609b0a2b03c55a425106f31e4e344b1ca3e2464ad705964e89dfac1533b0c3f48e829d7e00724cdb27b3ab18217911f2d347fdc35785e32c640425a8a41b30224dde40650fcab569576fad67a98a70bdc5a9911191bf731d855ada7d2ad19ea0b9588c800ba18e098bd0631f13e54a03d4b1c28210db97b607b74ed02c6f312e736169af0f3fa72678de72d720f86a198d378b6873581c186993cff37292bd66b2a2db75011b0dfdfa67ebd77e4c4e1387742e1157c5f9d0293066ebc5327fd010f9e2cf0859c111085eab993e42c8adbc819e19f54a315534d6c682900db08ba33a09b402bd0c0a3697346b315521c1cb49e8c8c551c57fad7f54fa61195708d6ed1ddb04a967d8e2c35a9addb5b0616650bf3256d11dc75a5c8d38559cbb8cdb37e6810b046b997049a2964da6fa2cdb506e79afff50230443f601257c90fd826dd1b4035b0b1652d980dd8d066b247dfe379d5c47c2b6779b5a8e9f9753f863287f7f0fe3892d67f4cc4aee6835b9d818b118d09fdfe827f5998ac704c1982a76fe9a0bc9369c4839f9fa92a10e26345cf3bdb893693f60cfe9e303f926b22e3726960043f71c42a900bd067598ecc4067f520f8c877186efb67b43dbf03690d14ba5072c618b198635473135e3964fe55638e5306eb8b89447a2bf33b7b5ded2dbe701ac43de1037c40455cd2bd0f4b92fced20560b4544e4623de1471b9b129323a077cefd2bda2156870c4d3d22a2b82952af40593d364a49e48beed8ffdcad2330d6ae95f9bf156b0931ff1bcef9d536ca619f31131ffbec74636d1158cc3f6d7059bf30901a2c96cd87e1a2c77267afebc5b75e4ed22255deb1b005a3cc1d2630ec6bedbe4467bb2ca4b73457f504dba5ca4bbd928b80798e770d77cd74831ba051f3e147741f0174c68029e0d59e03bdb3fd108a2acf8a96e4f422d537f03f2039d374c851216c433dc0a6091e68aef707eb76951e4772012fb6776e96366330c1399d6470c69d1b04dd51c2320fad7e804587e794a0fe56042bf729553e287088f226e05ce230b1214b3e605cdbc8c1060ffc130168d1775e7972a645b20ac039ba823b5de8eff9af4399ece8112f06a8b5c871918d5977f2879ade0d225d408bf66042a6b1e6cc847c9f7865228d3737a19238a20bac1dfff6e0ca9752d960c25a8301a0ed70ac6abdb61e1f987081d5e9b834626da0c176fa66681e2164f079f0a5e5a0f4a205b9603efbfaecbdb16ad34ca64df2bab9c754e087585cea304d159bf4ec959d6b56984fc59217fc42e97315ce059cab3623bf45e16a8d9920c751718d330337787a9ffc9556c5e2792383d915ed6fa92603fa49c4fcedb1606c5ebe129cc5f351f94ae386fc54d251826d1d17983d92719dbc48ad1ba7de0035087449c2b5bcd9f8121d4774de48371fcad032e4477cc16168a38790487e40f87c62abd33b2501c81e0b263e1398a0ca031f10f91db4416987bc552470eaa0a69ffe32f38f7469c44b4e6cf669a25d9deae27a32d8af292d724b026508377012a24f8fd21ebe2183cb662b1aaaba8d79c1f35e4f016764dcd2a61e9a3e7c80bb9c337b526ec23aea6fc68fe9e80c6b055f331191fd317adb3d83cbbddb07002ee1bedb5829d89ac4651ac3ce3318cb794d5205fa1acb3ddc6033c60dd5c020c67cd78d6f67b8e28212db0adfd4ac29d544effed0bb6288cd009b257d6f76e0e5f10c57ba00f9148bdcd1616adc7204020124890bdf01652617d19f9676ed70c630094ebe0aec7c9ae2210eb6a03c169d04b693313758ece011070c08ec6fa08c50ff04f6929174d8f6bd76d52fdd9a19956347530318e5aadbfbecd8f6e430fe9bdf03019c9bda49b8b45dd6a593a137a6bb42fa12d00491e4653300a59370362dea01ec38a685b58406cce0500540595d79351adecad060bed02b404ad840638239903d414792102828c8903232b4c2cbfec9d1d53c0fe33d8882be347a4095b7ae9a982082797c98b030be27bff6b4623a37d6b3396a4f56ac545e74a5501edbacd9cf5c98846a47c6af9e81fe9f622b260d3aac0b4342293df67a068750712712966dcad17f47bcad7360c90c06eb81cc61d1b16d840556c24fcdfbfed0c54274a05458a995d58bb0fa421964a54b3031fe9887f13c7f80a8418993caf0b06a3779dbb07846d617c645732809d3a286a0320a643b56a65ccce0e7788240793119a50d2d47d4113b72256a90d5feebd424dbcc58783b7ae029b533df9500ae60de13ec48d30d6681c94596223a3e6f31f67bfa1ee86e054006ebcdc813b0148f4548ac1c448348f26878acf8f49e896a0aaa75e44c021eda842d35918d50b7e98049b4c636cf5c6c2f14361e071fd5cf082e0eb6f957cd02496390a212d0670aa22b16e29eb828361cdb104973a1942c0fa525cc955712c39dd56c3dcf10ce647ca40722adb7c18e1dc458d87724144b89f56a61036a610e54d7ad7eab506b2d0de75d48ee90a5af4d566b784b3d4fce7314e95564553ed6c4bb4b579a8050398b909d7330b33d28aac3ea097cc4ca4789f7401515afbc0d65896f4761601bc7037b40e746cd17aeee78fda1edc72011788403b33bdf5c1fdc7f01358c40a0656a64ae6f9e7e123f3feed0eb4dd9d972d258404a1dc8bbc05cef2090c3304db27144e75153f789feefa8f1115b020233906c8160bc0458e7d347ec404440f9f541aacbef1b9f640e60e1ed4e473242e47988fb941d3bcd863d160bbfea800f59cf1fe64d6e5d959c7aee993129887e520a0dd4b4f9b1598b97afd8e7b8c0dcf475f46b8c826f9c2aa6a643fd44d488f13d10208c16d7d881d5297f758870f033e0316414853e507699f407073af47fc524af1d2ecee69f20e3e2da789e80fd5cd632247aeb256c22d8b8a2e4d73503063d0012ce4eb967e1e4d08e9e4be0c886d77a351a4c3f82c22ed761eafeb87dbb09da667f6b01ce23d29892615c604fb25e9360bb86b7719638abba883b449ef20809bbd01d6ded1581c3fb855740a17ed02d464bef0aa12df75897f5d1cdac3f1535440435bf0b5a8824bfe6f0208976bca6f22edfebdd7cbcd1d94faecd549da31dee042de099bb57028b2b3e1039d0e072c7da8b7cf3463ea9caef7b0bce770d3f6a85cbfe153f58dc1fdaf8a09e5f2df380e2d27f5278a0d5a2b00410a563b1e6368d88b85ac38ee3411997d0134e538d976bb4b8aed41a527ce1b2926f101891b102800ca34aba4a3f623df0e92d6f0214b3f7bd4667e766e3e83a54efecb2e9b77c99db254b054f904b19d0ada49713d1e24ff4a6f200681f0c9c11c4463e37d2f366b15a54ce815341ea5073a12f34cdc89cfcca90f727d478b0d67d4dd755199b9493f16fdc9a50c373d07e08df1bd2d52a65524561b817828940eeec49aeaf86ee22f81271aa6640ff1032c342935c51b37f7f2bffd6686a050fe16241ee8fc836b174f08ab15131a2909cac1988f337211067071fbd0eab76de06eca02b7d7e77b65aeb641b0bf1a8f03523681161d5777cf307b6a3170fbc548f9cfdcd668b17d02fac72e4f5fca3f066a442d561e9439bc4f96af69467b27ca6de1e9d2c365548e99d7eb2f4087ca0c2272b75753907c43d9c08d5d8d25b00778e2c0fdd4893c2d85770d09593cac05d50b61abc9c1e73bbc2abc8892975f70b2f7aa25d80e7e1962623021ce594408a0e02f3fd8e0c5dcf3b3deb43fa79062bb026aef3756bbdc2782eec9fdd97b05790b691d20c2c30d695709bec05239afd15d5fb220279365714ba75c5f874b035135cf1ae832d4b62587ace504ff21b039a7ce70941633a7f5140674dcca3b0ae9c071d6960e92259fdd7a90cf28758b9305e6f1e4fb5c464ffbc81acd32b50603cf348028ec4961f8a0730478963f1145889fa28e9ba87c4cdc7dbf2310eb0a9d94ecb72a228a87706da1e4c9b3564f69172a39663219c68e4c7daa8ad4200beae6d421635f5f24274f3460968c091c6fd05efcf2130fe3e775bb14b40c6d0bdc77f81728b5982cf7b46c5951d2688168532e377ee2466e6295a96c35158206c3dd86e7d55e3c3760a9a9eb42a0aba66435463705f9fb747a4c944f7950d007e5347501b03293147a73bc7cc312427359d8edfca34557b70b4dc1895d80a40f -generate_ring_signature 67011525aec41e62812368de7e56ad8ed15cd4845e00871483703bf9dcba31ab 819d1090586ddd1ec978a2f24412bab7d583208042f35c68d6ad5ec98752bba7 76 dd964f5695c3a008543c185f0a1b5fa461e9c22fea98d1af2a1e5d60cdaf6e01 d1b04957e8b634589c062eb1b743fa1cffb725a8e272703bc10f50d1f5a0642a 51dbe51747f44800638559a63971289e43845c9286aded8b96d55d982fa22637 81e0b148f3ce2bfb07971eb4acf845ea0b1cf1c08b27ace8b0714a78daab6097 9ba5b03511558e8edd7b3c0336d9086011c5adc81aaa03664841d8c62f1fd2f1 3ba3b659ae309970f3494eb0ec612a5cf70da1357bb5beecb2fe946a9913f6f1 882ea42f44c98b4e0d85af542d9de44ed889c3d65f9ed6d24c9eb751b640c12e ce38c8d666e28094548fdd3dd3a9e9706e0ab2c863d5e1c43fdbc0682bbc00e0 b0998d216a9ea0224668c114c8db2efa0a565838075f96126bfcaa22085b4745 c83836b02f86df9d9c8f753f52670cb80ec5b6b2720cb2b7fd7fbbfa2d838e5c a441967d23071f5a95a1f82f5e2ccecc2dd3d31bd1849d137c37e9415c01595f 0322b7bd2b2430e428c8f1273fcc36f45f97892b5572bc53163675abcd987753 b9e1f6ea22472fa3db4957c448172b1d58aa61f6048aaefb75d3c647743c87fb 348fea709d69b0752a6f382577e2149d83b39c5aeca6f0474d13b1d4627da825 50360c7ac3a214e93473db297a3e1211f08704061bc868bdf06976a95db2e6fd 8a5ed26f5ceb633b0e25b2a4410178c89ab38634fc36175aac9847fe666d6876 9b2690089def921d1d5cbf63ddd3f58b5d7fecb4ebb54b9b900032ef0d687084 4a22af02b3ee18ca29add712fb0890f5fe0bcd52848f1f8e38df6f38dd5c99b9 46b943bcc3d9183b473a4d522e4a1febbd713e23b7ec8959e5a4fccbaa9a4e58 ce8f7b05f9e53ca34b01d6a5bd63ee14991d7ba4e42a236a7248855832023dcd 851d02e432b19150a6f451c82eb384d730b0c3ce3c35a4505765785fc307ff36 7ede31e63912f8434bb525222bdc771cc1539e42cccf8953ed50b7bbd152f87a 989a4e49e2169c52b2e8ed72378944c7f4ca80cdfeede413096d6bc6bb481987 8d197160b9241e31906172e6e59aca990939999d13f247f5a5d63feb35218761 cf685e95fa014fbd3a7d23c9345b54c8918a82cf04519dcce9aba2064e703bf8 7b7bf2a667d37550e4e55a37e728ccc78cdb856082abaf80f30bf1b9c75805b7 b63b938e1ae69254a31620ed6db8a682a73755ed347b020e245209f4e5aa344c 8e1bc9db8e3ad41682859991867e2096c5e39067bc4157bdc5564c3391902df7 998290f1542e57fb84928d36aa67ceff21910f22bc8c3558a0ef10398fdfd3a0 f4aeaba0a6d239ea8a01d9cc7daf6ce0109d26aa92cb30f4456bc5eb2f3d6738 17833eee37eb03a9ae32973d56754808205ef421688f18fdce0caf0bc66e4f27 30b5341b038b23ae69a365a896790cd64aa19eb31864bb1638c08285909d764f de2d29581951dfc59b9c1d2a2a4ddf67919dbabf8a58651b2fa527c414a5cddd 0457828b3514870bd16433cba46f8738c173d41eb59ded65acc6c7dbaff5dff8 5c9fbb3ee625a4c846821074213e505f9c51d5358abaf3148a8d154cacc6d6f1 6c4485b140aaad6b9e994c7d2e5023d0940d85e19af437fd21f236ec3a4733af 88e354a64180b6869ca2192aff2b69fa306c43be47a4f110b85dcc5495f1c977 6a6a1de9b1c8cf8b9226fc675ddaf1a032205f897d0727829da84b8b89b22d51 78d1502e1cd1fe7c525ad2bb4f7c9f742d679f7908e1a2d4f7bde67ce95b8ecd b6ec7b175273d7aa77355401a3c36d43d003f6efbdc0a961564bad698e8264b0 b4cd90e8ea0018b56c069551b6c43f45d5e330633ef17161b85acd7418294d58 25281c2d7629d25ec3b0bfe45d621b5429c1588f2831611c642cc7dabcded2c6 6dfc0172238b46bc6b48fad9ffa9ca9e8dffd0667ef95611bdeaf212ecd6457f fd8836213387691d192f743f779da56e093a435d355426422afed7cd71d18cd3 d32a9bfe7024fd29b185a1308ac3fe06e21b181ac51ab2de211270a503ee93b5 d65eb25830cb331b5bf837c591ea4e4a350fe3cc01e795e0bed07d38b4f84991 c521b61b43eb63632c484c6955c669f84095d769f226524f2b324d4243398cef 3c282a717091456d676299112eee95a854a7a6a74e890c295071033c4086760e fdfa169265150e45aa61bb1450d7a1ff82a7f9f076bfd1ea5fcb3fc35b527299 139659524a57f89d2425bc842cb106d8c84c9dba73fcc03d61ef2b7e6ce2e002 dd28d9609d1d3e4f53c48cb86fb6af584275dc00ece5322836b89209bbbef2bb 139e90fd22032ea389767357866c7b2ee9a19d9637d5911e74556e89687e79ca f09a6690d27cd15743f95203885b89545c0d31d6f745a0c1830e9c2c613b94f4 b39a46aee128baa794634dfff8157c6d3971de45ea87b210a568b21d4cc2cd39 8f63b5d7703ce9dda77b5fe3a6569482d1a42a2a7e7aaa46e3f50a1bdd217e12 14f4e2881ee2111945d653a6c5ca978361671932c50890bb355f277be25d2a77 e29737b0d67fd4b843c907975e594978ece72d400eedeb0133e875cd65710c3b ea3cb93a2202ce93bce08902a70149ed46a56d01d646ba50c39f8fc122e19926 e60ded286f6ce5f73348f53cd99e4a3de0c5e60297be0d7f066fe19c08c13c16 77754e38b821d49702150ebf7f43fafb0889c05cdab297341578cfa18128133c 434012a532762ae1ebd6706d4740128e4f1c4b97488de8844115de50d7b9149f ce7d22e2a914b82c673b65b28172af3760540a84a45b62bde70bfd5833c33509 7af0361c2aed7982fef7782873dabd2d0ae6adc240dbaacbff15409d916a6a01 d3bebf533d2922ea2539d196acc3e160a8c776bd0305992eab11cee72b51576e 68bc2a53fe9f28b0fcac97ff57c0dd6469b3387741aab8bbe8779b72264f10bc 97741abb965b119d339269b289ad2b630e8152a2e8ab33bc9da9fd155a2453f2 a549ea88e7610bf3c7d92fb93bb175146bca920a8a566a6b8bbaca0c73fd31b3 fae4b40e6dfda8cd353c561aec3c9c8da0954d888b9c6adf883621b50c3c99b7 55e367e48d3ee954646ac39548bd79e9e56d216b1ffd07d7cefd5b99bf08ffa3 b5611452300c2159de3e38795fd01a3fc57dfaa516ade34e288e984ef3a0d604 3ac22c7a0b7ffc3611cbbb883cbb1fb6b4fecfbddf4d43c2c4fb484d4b103868 94d7a77a15bf45726606d1a5f4ebdb46609b7cf029cba1e51a629e1083f3d76d e7c85510fac98d7cdf17ff2c1103f65411fd196c37571a4d2002bedb00258cc3 023a807dfef8c1e036c0a55e6b1b09ba9aa0637caafee1fd1b283f48dcc7d88d 72638bdbc1acec76e018aa9671b04f82e1e0711f708f6424bd77f9c048060ab7 f19bab5af9575092387a5070f63e2c8bb72c14cc9f1ffd5944c2c24d82e880c5 b5a3a51d296fd23efced3457f883efcbdce7b2e7ac1514e5c51211be8c328604 24 753bd8c42b0d38cc3515c05f50ef12bc5d34cab0cf6820669abf85fe66871c07e344df3f74ac05bbb7270e51cdc2768725c5d50f458e7421c37de6bb9f2cbe00e66e46139a1f40742ad48310f09e98601ab636000bb2ba047df9f232e29ff709e8667295b7ab8a1ca7f21ac429c6742490492c53f4440920855cfbd132df500a2d2825b622ecd08a3be6a421db918bbc8a394e5b3fa21b1e9d6fa4ef6b4d5c0105ad1278836f10ee948fbd4cc7f33c76fe457a187b11fad96f07a5c3b4c61601c062a9484c258d37abd2da09bfa1f74aadd31c850138b69ca03ee2ac73362205eeae22f7de0fb04782cb2a27aea68a31f31b9639af77ed8cc7c6e7ff1fcfb307a9800c1c10dc42efbb2098df81096ff82b4ddd6fbc1c4f4972a252bb920a930965df63a2f6c3a554b571ff23ed60dd5d430f59c60761ee92883b27ba5cd3e60074227bcb9b44997e043addfab96cb438f4feacab64aa12e06684e6546bd8e608032ab771fc1b7433fa0fc42ccf837081151ba0346322c587c7df4aee726e620667d1b509d5b093a3c0f70b130d4be2ff196a16df768446c97bf9f7d6e0db4306921052ee334b04c29e790dabc94674bbf9b8192eb5586ff4aa0c94ee66121d078daa862be6e228085e30ff12e4d17b50b57448c0540d74b0da051a241c7b3005f6e54fcc2ab4e1cd20e0cffe22f12152ecff46aacd1482ea10f56eadc6363909e256d2beff3aa78337008ec9723abb40ce6a8dbe17d43d0ce8d5f062c999600176edc662c93a3ead2501295b827039bc5aa621ad1ecdbcbe0d132602cb9c2505482336c253cde5639ce3fe67a1d660a4aa080a74ac876d5497c448841434cd08d2fba2c6831f923b1634a09e599da97a7606d10baf902346a5c3b92ddca0d3078606e3986d24d5cd5c1bd1c7a3f9d44325f081c388ef8d2f76dfcc8339fbda00779486cc3f8f7becbdecffbb5a918d218c30ba32c2a822f63a8536bbb9fda2085dedb86ef1f3567b6d0b2555f9504fabbf5f889fb1a517109a71dc961f75de06d8519d69375d802b9cacd2dece92e6b837668b52ec32d41e093e184345b8f70f3f556d19653b10b681a67ec93c54af13f6e7608a17e688e65cb1300510baf805f64f49b9571a855318ae1ca8fb05824ca7f1492db8f6d835033184ecb185e804b17f4e55db4439eb2fa94095b7ae4228f7402452b3efd473afbe2460ea269a035df5ed266089f216b9d7e16acd0bc1155d92d3b174deaec3b3dd1f3e2b968b0a06db920045ac4fcd7bb6ef27f7f45ec8c1366aefa7170c3d404b293d6339c0054ca8a6ac94c9d75eb68ac095f9c7688cc3efd08573d0f72317f3e32f0e3a13028ae25edbfd3103c13f0d1b03e326566cf25266276a1b64a379aa775354d0190cdfd8f2c203ec0b1298f0fa835405589d55d4b4f08294e6324a5ebe1dc1ff6808a93822859fd379872b174f8eb2c20c2bd7c307c81aa37e1dcd5cd2a0d4de7b03e0c033161cd653ff9e52b7c13c9fd951af4642eb4ead0587e2ae20e37c6b640d2255b7abbb9ed77314dfa8dfd97792537c0e1e7e3ee70a0329666e49b88d8104baa00dc0e4b257d6183f5b063a25e9d305cd46d4440583d6e2bb3ca35c30c00faa5a78f62acc5c0d6386b32bcfcc1161321052403669bbc16d3bdc414385b60f394a2301b76e5c3f3f389ebfdb2e0af058a7090be2b7c93620dbe8106284900eecddceb90e821771977e29dbc75bdf1c9a4b16334ad85ba19b8c0ec932cf79007c43dbf91e4eb769e51361066b79f94de6c83ecb3f7bec8ccbabfbc2d266a30380f1eb10559aa693631a4233239f7b08ce11e3009e5b7caef714638755b5450374644432183abf91c2b05599e16d4ab248ea9e19115b810ad54c602ffc69e50b644caadb02d241a7f50e8073a0eeb8e784e12273a8d90d5d4f421c13a3d5f3017ee68883f64a6742444116f1c49cc5adb8cf8a3e294d81731d9278e3f881b406a266bf1cf9c14e5e48a6abe135cbba1174051be1d95bf6dbcc417ee654b8a50bc76968e04211bbca98e7289d418f09667afce3064622bb5733f6801d9e93d20a0e75d6dd6df5a612231bb868ea9ca70dff09bcd8327b81eef9077dfeb07fcd0b661cc09d8c28c94566888fa4e91a38ef9d809d8cbf7c2a96d42b81c8f32a58049a04b71d721cb7a4012a4e54856b0ab788384d52924973cfc319732acbab550d63d9a934493ce8d2a0af58c85602b974855b31a8540c108848bc780f68253f0114c1372017135d52450c930ec01b4c0ed2075e24792f16f93c6a76139b9342023804aeeafa75268c4fa9ed9f5839b156cf54c79fec2f0d31fd9a117c780e1f059f968b62d6da1561c19154e3f0c37f157ca7c7e454248cf3aaa6ff74fa465108c27b0d60c09ba38e9373b014e91b39f1ac35276c7f136adeb73b43300371ee0d0f6a86726f2bd80e55766a4e2f9e1f9f2c25a81412d489b77708fccbfa7bfe030530f72653645f666d82c9674cf61aed175801a191fc1601a82ea0009044ba0dd14580081885e3ff96775735c7df0ed9b96cd3d8eb2daddd8d3f528143070605962084b919317a1dcfe053cfffa5293e76b4fdb8b08bfc2c0d9da67312a3530b6ed45d5445fcfe67ebae6da3e1c2a58de06e6255205a27fdd130e476c1aaf6026007952e4d2646c8521f761ad029083f2cadc38af0b4a9b1dac5c3fd36f7bd0a85067ae4b23b9ff85741ef3b53a729f492c0c8bec7f9049b4be7bf1949649f0464343951c0184d3aa11a2b7845a47da4ffeaf75514464466cb32eb921cd2680a0585f660a28d21da67eaad8bab25fa084f8a2d9f362a057bc4426077a261040b8cc31147dc38d10fa1e242b1d66aef3a370223944a615732861012ab156d53048f40d37fc4611904476af631b7a2b77a356b4c45986a4e4ab9bb82c676115a008492833044d89a4630ef78b87b74233b1274c76ad59a9871e7e335ca1a0df90bf55ed0c80b84f6a8a43e645e1bca9ec5e22bbe0d8f065a3110ecc1fda7534f0cb0b3a408477e93d04402a9f19f64701114664ae871f6483d734bfb3f7074540d168de1e3aeb0f0d660592cdb24dac3731280b60372459cabcf3bfb1fed2bf2047173d1fede108ec244eb1216c75e8b0d82f856f3a05fae239533c70c657177014be0201fdef4c4376d35f7cea673d61c643430085b24c667d5d1e8142dc57802e16b847045db744b472f0d409f9710b571111d8d6062464f867e02a50271090bb7a80dbddaee992d3b09c9ef54facfc24680c9fcb3c32e3fe30f7fc8e4aa8b0f12fda6b1faade1deb60924fea0055d24a3a689698e794ab8e4cf32a42d5ccf04b84d4867e5a9555365fae23e70ec38a183cb74550c78cd7996f209c04f10bd0acb6eb3a27fbd90bb38e53a10b9eb2cd0c68a2b2a5ddb3df6c86696f52c9f0d0af63671d453db964ffba0f8239c02131e2910bb4c6a6874a45a32e3dbc558ed01b741228be039c14eca86427205fc5003c9a75bd8c908634f9dc5eacf3c98c60dd79a5dae7ec16b432cfe720158cdce0aace0b874aac03a75fcbc9d4f91cfae0ee84888b230ebf0dcede39fd808e8ff736059094da5ebea2dad746cf199b3550644b9d1daff0da7a1255ee669c44ebebd115788cab8d9baf09454323a507e0200dc75eb17af2659cb89f17faeaebfdf155363f5a0a4feb7400047715c9cd68f09cd659db80d8f77f40408efa50dc621eeb53c38e39fa7ed54d0033ead2101a4023b510d8c2b0ea7867cc566b2df81d226f690b179cd526439284a8261a8902f04b91dd0303410fe78cf091d87db65454ec11d9a4ecd78cbf51643838e3afaf405b620010e2a0c03711af3a53e463a1b4ceec7fea2ec8dce0c9f3a09ec2e9d1d02a5f42f16f729be1a25a2353917ea1c599d8b64b99827ce87cee56a7d364f0c08a65d7ce5ab9984cd84c401ae3cdadbca83f6624e6a4db2bb730a42a21075cc03205dcc9708260173b322c19eb7d53dd446314b0c5c333e990327d7293495e20003326be5a6cbba34555404a114883d26741d4318cc6859ffd88e1abdeb27200a470858b413ee882dbdb29fe9a6d974f3375e2ca484c32509f5c017d3c4058c0fb8fed6c5e209bb0e55935874dcaf290eebfa465347c1bdfd656898f67e8c240ff5484de93d094a7b10b1987845befebded340f90480f677c2d7ec1dc2bac3c0f2f8d1cedd8483edd8dc20039fb02261d375b788da6e4f88bd6bf95e1d4b25d08937515e629c155c74ae6242c02daaa1815dc4911ef0e160a13384897aab2360dd53cd21fed9dfe414cc82e279a47198bc0798e003260ee451bf0623c6975160af198bc474af3b7f97c606d96708e262f3bd9b6d124981893914d7a6ce59d0d0b4c252f5e5578762d67d90417560ffe5d3d43173a78394c4147b2afa5f803170b2d9ebf03b2fa4c9e1e62352648f5c2c5e88150f5d8e7a22c7fb10cdaa0e804048909cd988eaf170faa9cc9c81eea6779163ef4507027673c3c87f876392f270d1876fe7cf44709165611c7814a7d97aac477af52d92760be03fb9bb5a016b00d8a124238fcac4f782696a92bed8d97a35ee341930f5eb96bb83b441dcd968007a49231469ca502e0e39eb68792bab8200f5eefb2e9bc0e6540703ffac6459d02f7ec9e749e1ddcc134ea7afbd346e3109d7b746bac3c63ed47efdb9e74f61f0ef10ba3bafba5b9a53fbdd84c25ca5f1539a1baa68cd44529693d474c4a451101250725a5940f56b6b66767f0118baa1a54642d610e9e4f58bb18a07e7ca5da08571a351763d9fa4065f9a36021fdff0d1042c405ef847c7303cdf092269a680cce3cc2e688a60b9d83caf08661445b07256d790d6982817f051b97f4a593f3093d2d135cf780f7d0ec4d58ecf201b9843d7281cfd0c6e203eaa0120b473fec0ec85328b811e67170eba7ec33388c1ecaf5885165b4b181690587f695e1001e0da014d2622285a91ac52181365aa1b4f4307dd6f1fe4f46cfa1b88bd883b79d0298f7c3555e9192cafd175e351caebd8c96bf66a6164c97b6e2cbf2a8ceeb89068924b3907220711c688f94c001b59169a060634c4cb77e7dab09f7b1a768ac0cc4f194c0dbf6f4018e087b6875b8a66215c52499ff22d10c71520ae9f4f9fb0f3223fc16d58d04d653b3ec085af7277f28b7f08e1472613788a1d43bffc2370909d3a1f88dce132ee2df2a839339cfc502a7201d89635ba243832722e5150f07749204cb85bc5cca30f4e177b578c9621660d17966fdd6386f4dea45e825a20f9b59b8b4de70bd3b7860b09e8167bf88377c79fadcdb40563b91ae3f7da6a709bf4922a7ac03682d7552e9d7396be0a5c728e57757acc5ed9923036facc6ed0645eeb7f2c6a07b602907a2ee4da70f292a219404a0997b7729a52b3f2f546604e2a3dc4b12f6ad6e2e9915120847c1c91d46a0e13b1715a92c7bfa48c7d8480a73f8c3698228167fd686aea41c27e0ace3b81cbe69aa74bff26a324d6604d800c3ee9433b0979ead3696319102e3024c83bdbd35a5f34264becab480b4aac50414e2ef4368df08a5d1d3f7e895cf2830c1c09e8ff5ea527b966c75bb07a39a0b85dfee234520e39d237446f28ae1c7b2110aa098050fa3cb073b8ef9a215a609234129d7e9ce505b79b10db50ac955c6e8f757adf170f99a660a0f5b6317770c584da3a871196a929f2585bab2462508e40fbb87e0a7147c8b9b2f9a5c36b40e22adc78a4f782e75f160fecfaf866f34a92627965e0854ae75e1ed5e243fd1054f74cd38cb66bf0149ed7f98542bf8dd569e0daeeb9c46bdcef6d6fad12dc00fabb4cdf0515fead723efb704b3a4794aec05fab2cf19a4e80a0c93aba79854013de5a1bce843641c5b30184d2ca391f0a2230c62bd004f53ba2e4011566df1029400057a21803d83301aa02e9ac6381c5255bc0e05c0b44ab2dd94923aaf50046b06f0ab3471b0f8b235c5b51b90f4e84cb3ac101bd33fc3f21590a8fb70d40df846bef3b97de9960c9b32a9fb021c271652f2d0ffe5d0ac271ff1130123910adcca86dd60c54f2ea150842a7ad876e28ebeb7854785ac25ac83d1612d95200fa58ddadc4403459bf5c8de88bda35de29b8eccf8b6a2011317c70cd546a2b90ee6bfd5e96ea87c7845032ca252afa6629ce46b5782ad901c1f8fd7499fde80093c98a5098dcf97686e5ea7000ccd3894f4ff67ef913713c6333faa28e172a00aa7488aab58a6e00bb629cf5218308404f95d3a3a63d439e143eb5b255c57b30261b34d39ac6c7049c6aec81131a410d641244b6dc897ab5e40668a574315170ac818616056412516a73b94cf9f5a845c48b38be6775c0b76f27aeab53f45a10e2c546a8aa21d9641c6de187a60a987f39d5705a409eba6b5cef24a70a47322004b4a2555987f48a485255fb2f236b6f2ac959f057b032c8769473d0437d4c00bfa14c7037b331295954a883098122fc61a206d8c7641c15676175e6518e2c6000ebdb435c702cd60a4eaebabd9c28c600111bfe89aebb50a94eff36de842c60216b8b01fa94b5e50072f5cfe6d89956f7bbee1965c375edf9bee628c9f8d1702df04215204ef6894d814bfa0bd88b7157c26522b3788aff2dd58181bcf98440a6fb883ed81261dea166316e41193940f5930cb4b003d473a66b5e4c52102ef06438e5f7dd895c693a7c69b3acf0d4bd0690819ec7938a82df134e0b95da1210649e136ac162f85682c715ecc1bb947118d73aa13f217cffb58b80ad12dd8ee0760196ae2479c60932f404f2ed1579688c723d7bbd89055a8c6f6d75b370f7201adbbce7de6077976939268a9f64357e6f4718e1a09525401ae5602a8be9f2d07 -generate_ring_signature a204b66b297db8196c5e43fa172a9cf72d610c1a3e5c14a7e64b6e77a902a7cc 360b1eeeadccee686eabb2475524e968331807e166ea52bcbf4b806e056ee332 6 3a819e2a86cbb7c93d82eefb25ffc9d557aaba2e76ea0cd35ef017a0f9db4a74 fe4bd986465cd8d4684788e3abfeed9a49365cf9a40225a9b5f7a8f0d27d4992 ddb1f912f0733db94098ad017feee9b550c2afdae5e0bc4bdaab634dde7485e3 ea08a323f76486e70dcdc071e43b7458b070d5e15148e57268b4801fc0e9ecdf 16e7e2e0736b76d75013cf19835f4b3130b7761fa2f1243e513b1940deac38ed 9330ae8956224cc4732f9c94d92a3470737d4943400c6b837d8a5e4769e4c2bb 5f961793fa7fab2364e40a420192a778e214079b163c235d48364ed07808b50b 5 9f9c488877ea024fde8cb4fa37fa85dbb51003d7120c6cba58a17371f2d9200ae2667a2d3f8f5cbd84f45c42132606fb1746bcac8c0cc87ca7e94d8dca7eea040b984e9b6f135e316fc91a92042025183eda4acbebb37344f1f88e7ac60cde0e505e1ffc6e946b639855222642218d7eed129ac7a3b97bc18bbeb8824339330b01147768e63e31834ef9e0e892a5dac8fc67fbfbb8c9588c78675deccbca0300a5e0cdb626032e763179942404b9ef462fa24db12d6fbec69e30b71913ccb905a416304aee4e78ba5220f6a75f9a25fead1d2ba414822775de23e81d590dda0ea48e81668c4a2a937eb3919ec320ddad7e0745908515bad872b1967507a29400646f55505de0fb9d6b25e5f940166c858d88ccb324470683ea7e86bf28247805826fa9322eedd64e24dae1539267e425eb720cf3c28b82253bf73cd26bcf820c76df3fed0266389f61b37e700868a29a9cea712e6f7ac0b48175d079166a620d1e0d597124bef8c9ef7170132057840025df0762210db37ae05c10f10fa25a0a -generate_ring_signature ef79e077a9e0e9424ccd7c150414f789a14759ca964a0e13fc01fa5466c81e78 f946890532edc31375a4fc9e0201f16121acea77989bc1f44f7cc9e27d618cb5 7 7833f5dbe94cb5307fa63720d352767c4b1eeae31307e89dc2bab1b483eb07f7 a7c0d7cedf45355d0d2981b5f47390c3e452becb05e015647e39fc4221dc242f 883664c5618118cc6d2e9982d3e3b6d28916edb1db5db6db7bd5120dc2ed1f7c 1227bf14d8642d1ec64199c7b955557194a4bfd8962aac9236e38d25a8fb47f9 eb41c810fb20bed74105bc9cff98bff7526462572cf709a8b85ae65462769d60 55e0ae0f1a539622d44ef3ec8fa1565017e9f763c45f27eb5cbf6b926a414bfd 7af96fcfd866484934851fda064375a00816e21466ffa8fff2425997adb7c190 0b0c659145f52a67e664d6c06aa96452c552c1884cdd0f9e5b7819adcf173003 2 b2c193d0310a28bfadb3c726c54a0c196fe466c5e2c8ced323a322c488745e0ded55711281a776ac22a3bc7c5cef72fdb8bd81f6b51695a2cf53832fcd1e4600909691417e737b8f8994853277ef4b5035845c3a236293daf42608213992090c110cf0352e38fbd28bde85b126e3229beec14be1418531a4a17ae7e6eb0c110d4989c520e60c2a47bac25a5164103e540ff0eabae6202e023dd252a8a0c94b029ca74148a5b0aa1e01e517a9b81cc0e0e6059809be94d6f9c144f0c7eadfda053ede2ae1e97872ab4efb93aa448cf67d6bbc8dfa918465ddd93174369e82910da68d261379b8c536247121899a3db0c4399be8282d0c2114b72aa8e3eeaa4208a75a6d45d091b7a0f73471080c10599dc9f0948f1d485168cd5bf862650f3f06d3af954e97692ce6ef56031e87e3afab01677769865b1543756554e1b5c88e0eb5a6d40f4e5b1a0dd3093cc20f4d9e4259238d3de00cf9f13fd5c32a702b590fe3e0d624540afe8aab9c71cddfa39c40217fad437432654c75d409f0243352032f6747b5eb02ff0871895f86048955c2a895e3226397003b17acdf4e6fcee10b6ba7a94de7600c5451b8b964911d82de6175b90ae72ce80d043fcfdcdeb6bb04 -generate_ring_signature 78e0172eb16bc072a3b80ca90d93b44df26ffbc7dce563174f5865f7c16e5f43 7479b29e23de8cff8858d8ce192a594e7c4c3eddd8b898300b3994efab06c5a6 4 ca91beaf2146e06879051b0b1605935a0df5b5eb1ec9ea4f7f8d0c698e7c5bf3 567e5e858d217b840f50e66af8547d3bfd23c76290ee017b1c5ea07abe4ca354 c24d4eea3e2e636a9a24934ab8174a05043ad5612cef38bda772063f291d090e 7c51495c749ae14fd8ba542a7fc76aa77d4160b73f84e67811c1087eeca84d05 ae5a08468ae07c2ae86e46255bad3bec88fc27a5697b88a0a426c8ee41372102 3 e94e9ecf96317298990c34db47c7cd0e72fd480fdfaa6bb25bc2d1c2866fcc035937e40270c9c8730c7c053ffe96e02567b64b496198c6b1bec8f36dd8800e0cba0e8a8001f274105017c73fa0c634b556431f24e3e72b6c82df468aa14c7903bf9c0a7568cc59b6f5196af8059fac305516b7d8ca5a636e5de7a04d30dfbf0fabf8b0b9baca85c3672f674e94c3b1d1cf2de1ff1b5ad03d5105aed3e3940e0ba2855887982bb0012ffef9bcc1b788143b9300d36e3155548d4d5483141ba40cf1784181339d6658fdb41e9f0a5e7b0b1db4f2d29d52ed7b4d438a3238764706e5bbd9e440aa828913aaff250cf4179ddcd3d4800362fdb8ed3bca70845a9509 -generate_ring_signature b2b17f8eddfd010da161593db87e352a00d0f915b3095657ce31bebcf0531d77 6d9765acd0f77a17564da4a5f7f07a0b918bc309f136baf6b452eff47b5278d4 7 0dec43ab1cb7836dd07b41edf9013b8f8565502509bd853b73a3fd7c2d8332d7 d007bdb3584697e80ca791a489452dfaaf96690715738e9d0a9772761622910b 3bb2e27057e5cb52140c29f86ec9072947931c4496da0bf88eadea327b45e13c 85fb98fe49d5489a55a67860a4dccda34a81b00bf5796565a5bca4258eae531f 3d1c25052eaeb43c950506c73a61cdc464c3718a502d77dab4bd23eb34093bbb 386800a322ab462444d1bdb3cd943642b4532cb4e6304270d56606daec593bec 898e99644703e2ab485bf42197618479f68c7767f1a81a754254194df7bb5541 8ccf33c9539ce3697c1ec001f6374d950dc0f5888fb2459f0bf5f1c12dd3fa09 5 5d55b880fe032c4b5f0a7066573260b4b2db33ff8ae15b325d6ad85a82629d0f1ebf913a3bd8ef76332ef71a5ea7e60e4a7e1d2434e243ef70ff00c27f4a610f3aafe8a597d791d503bcae33120755248d622a5fcc9c815646ea105e0342e10e2d0f4b54539a67203ad6a65963083f6daa7b3f9d80edf5dd910ab2fda07e7b05435154cb94c1ab236bb3c9f898a51046a9bb2f4a8542b4dc30115ece5a7327072b79e76d8604a424ee065a9fddb2ee17e85091339bd5aedcaee25e606ce07908876d81f643ce0cc276d1d6a24b9b162cce63c39a9f868aa9c631e4247a2d6804a449286340b2a0cab47f915c301765496bad6eb1059cb6d7fea2c8dd461277085a539877cdd601cb6bb4e15aacd0cf83c3991c29a5aaa63ae98cdf12be23a408f22753d6d5e809e0fc39ee3d7f384b5ae57a03901df297d019c74ebb4642160009a87da730278191f8a7b29b8399374257a4947e0451aa05e2913716c595e50716f5161844c3f98fe8e6dd6711859a1cff84b4d0db0f673740a1818b5120200cb5fdc3a18713428e7b3134d5dce455c6418b0aca3f16d8a1b8f099de21515a028508ed1f398e780f0b53cda46a45561ad37d3bef7e2b8f0d56e643b318523f0b -generate_ring_signature dccd54de1c3418a71890e09bdaea34de43197a723146a148dfce50806f6351d6 beb57de477af6fff12b965fcaf9ff7229f670af5466c040902abbaf20ad26281 227 de6f898395f9d3acd113f747c1ec42b3c669642e954e078a84625e04f1203a86 50e0ec8c3ba80da72d39132072ab6db97355bb6d689df8d49323d6519f7afe76 33214ddb5c37f8ee1f71d77ef8f8389cc93ebe3e0750f0ca750067116ba6dfef 624036187cd190236a3af52cbd8ff1551af9ea618b7ad46c4039956372f2b37e 4d8a76e47fa3de3b4230220d19f147eb2ac1ea9cb0e815d5210310474bd722a2 9dbcb6dccb566b9b4ee3d9f4dd096ad64b86de505861a4b27007ed26bd7fa057 8afabd34bb7fd38de6ac26b47d135d6a0c05b890061de8e26e719e87d1140637 7d8f3290f75e81a26454b06a408ad4a5a025586eec86b8f1d4a20c006b0cfd5a a81cb1aa8dd7b2845b6f62cb21e37327eafab75c305977a487c7dd9cd9b72285 586bd7a57d35523981bb88c3a05c38f6ee6f7a4596a5c70d9bcbe72ff94d4230 75c17101e9fd6868e7881f5dede9b009aaf2e6945858c286ed121c3b912b9466 bcb29dbd41f103217282fa56c1c3e49a30bdc22c29f490a0c87f15979e90db94 10865f15bbda6df7774693537a26ffecf4f8ed4a9be5ea8256fc5128a5f5ab24 79c038c008b87d3425473433d22a6ee12f6a94912b2b473be3525df20304632a d2ea981b421db4622dac964447a5fa0f0b91253cd985fa651a0a94bb06a2e531 40360e8d38645b50466c06c3d2f366fd3b41ce9dfd854b4b6a6bbd91b9f2bbbb 85386e78ee0cfdb9c053759b698aee59487b080a3f9bb73636b6e9a36e80ff91 938980150ff4af1e402456d64d15e1a932f5b9ae0d03b7122c952a35456b6540 aa274c87ccf47e58a227424fbbee715aefb91a6a624217bd3ff62f4295200849 17ba30bae32dc6e3aff58c7b865f28ccfe8ad56d1779c3a40c9091dce280cd21 40182f7b319e2332eec3a96ae080935a8d06376a85d50e1dc1e54459b00538a4 18772a0589e44334bfdb0154f4ab5168e7cb33c2aa77a7603425da7c12d6479b efdce92d903112472849ff28ca2f7e312873d9325d28a378ed723d5fc2d0dde9 c874df6ef1ad049bc46597e1de0533c878070fda32bad8dd6139e98b04a44af6 09d67f7cc7632824c92065fea6024d80f1a5c9c2fc97d66150a5e77e9b069fb3 7bd68164f521e0c47994aa974693f8bd236564ad73add045a1f06d5d9a42da0a 7aab16af4335f06f3f24f38806b10c20ecd7f75e16905a97caae144393c880e2 096bfb98ae3213965acb6b92a60066b8f8ee495df93e5a2944de1646c369ac30 460ff31e4a5363b5aaca21aeedf944a713196ca387f438268f5817de68c22293 2ada78c71e899bf785980d8cd94f027e365684eae7c4292cc7da633e00edcb90 b84693047173cbc9166b0ad16a918d2dd5cd5b4dcb3175343e2a061f83623e23 7fcbf67da417193a649c5ca188c3c330d1615cf12e6854cb45a7899c24e08aca 1324e3c54e30e3276fad872fdcd7ba4f9ddc97cebe3f886658ac3767d60843d9 e555257d3f2fd95dc9555f8d89ef1dae813d0f6a9ab1e181f7d5ada73c8efd3a e52d868d66be401ee50f7eb5d74746beb94f677a4254eed91ab753f738e75da3 1d7355aa5837df46db3e8e8993e73c4a294d646d190a73ba40aa3ce841a83534 1aa2563077ff97e778155ceb8f8b4c98e45295e005fd9a5202e62ca462f995e2 f4e6929b34d3833940d5d30d2a7b0ccaa425360c42effb1940a10f49abb4b8d6 960da78fa1fdebf415e0baf03b63eff38b81ed99639186748188c348a3d5d8e5 ab52db15249b26d53920280b2b4686454ee211ef81bc330128e7c7365728d36d 72182c9dd9736c191efa97312318003e1550e18af5ae7b5f57b75a78d93462ae 8a822606f03874e68a1297636b7ddd645966b342ed11e826390a85d6fd13c3e8 77ccb0cb46f40123a379e014f46103cb21169b8f8dc376f34602a61ff3a164f6 c4a730fb9e281d183e142c9c6256183bba9f5871290071ead468d2b5479e2cf1 def7aa66dd688039565fa35a223d34c83aeefe5a6e702889eaee554028efd3e4 c42af9fb89579eb04d706df14cb7c47aeb844fa1c81cbec70512323ab608eecd 6f7331a1adfd3751d3edc5dfa02caed59d35813e646c474854241c7573beb1b6 91573e6964a2f69fba125d7897ee25fef6b0227a51a2deff1dc9205ed0778ee4 ad9bf2a571f586d8368024107021af31d5f5751999b25129f660f5b63cec9c97 0837392fd063e15910d2192b3c180228bfc7f12dd2428a81cfab8f91cdd44bb6 9c363f58e7440049d17164c4bb1f5a8a1ff444e1c26cc1ff97e43f4fd21f1b56 7e3e818d6a8d02896dcb8a7d53add542cf205c46108773495810bd8848d13e3f 4788b7a8e7b695a8ce58a3d61a7a3bd37d2c65cbcddf0f8402dfb8a5b1996394 fb352441f47b8f406cdb638ac64e7b6e8088eefdc9e757b9eacc7d3f72222a82 02ae8186d25a835ea25bdd979a85e2ed9681180b51fe5812ed67f431ae4c5f62 099abc67b8f2b1411f365db32e1973dcb2f6193e674f5ba9615cd3665a509a22 f140bb04214d43c84272acd28a16c49e2b6ac054ca2edb06542c2c2a764f6a80 cb2a56e6e72f1017eafb033c66e4899cc0da2b161c47265124ebaf0ce5c4fec2 838c1eb32105b74439cedb6bc89f68fd1e1359841994b58a4a7aaf5475c54323 e1c28c4e44817363335668ad1b2a38b2cf8ba1c0da151c1793d6cba07af2c5c7 3cf16340f818400832e2f4d3ac53242aa822b581835c701be359124fa74d51da bab07fc6e7065cfd2ef24b0d99bdd0a37b7c3a94890a0ba6e112ecefd9b59fcc 69ae4430e581881a5666177cfdfb2aa4d076e7c7fb1fc1df85f4a42631b652f8 5cb788dadfc2f0a8a71a53cdd95e973eb56ee8d8120caa38c9075b1e1e00620b d37ae42bacf5b181aff456091f7142680b056d866e1dc537b4c9c7c47d892068 5d8879fa016d9badd76b8e69f77bd6f02ad5b0ef79323306e8cdb3d3175fab59 b7e0d03bcc3356971ff4e5f2014a1ea9e2b8a3f87a170d25cd334ef440f1cca3 1cfcec5e2dfcb2243be40ae986f597f5e62efe2056fc0ae0f093a1ef8eac4c10 d45fa110c2c973d529ee68f8b00a7599c9e9821af0006cedc1ddd6ab3e37d9cb c003ff2afabb78406fdd999a1b48796082f4106fb616ba7afc27b475f2d78b79 6579d1ac0b18977a0b4a3184901abb13148a833b29445112fc0936e7710834e4 d2742bb8b5bd2fc348adb9121eb048b842ff428edb4cc0e628a51b235453c183 bf09a5bc58fa250176148cffd82244e614b0fadf94c23e53abf288facc92510e 6d37834e2372f26ad0ba8c510660ce2d74a495419065d78480332253bcb686e6 56d85f362ed1b9d5cf0cc9b22b58f704d7a1d18cd9799621c158e0cf6eac263f 15c8bd061f78d454c02a67973a18883014ea1a506740e3231a9b942b405099de 444188e9d62b697974066852422a091f9f9687895db8036c4baddf71bff17b63 652c85a355ae256e65bb47ff48c0f421633806c127bdbbc99b11131986a17476 d6d364039ad279968195fc81f2b2b1b8e315fdbf21618d05976eb434533ddbc5 082d940a66f319b4f2a26578dcb0277c9dd241d209f11059aeee396c767d6943 0f1ba3b82c1278d71c7ada842514e9e8a6b03d49925f279a55d84cecf4b8ea97 64369ba0557e73fdcb4fab986fdc58db1965fcb1b5fc2baaf7c86bea7e4cf0a8 c612977b21bde87630763327ba5fb9bda6312a03fcfed6aaf40b56e65c945789 8463e1860d65d4b64eac0d5367a5af5bcfa203c16dbfd083353917a61a74bc14 4ad00dd4cb6e7b62c74a36ce1ed7720b8c6453b570677012091f47ec7632759f d544467e93925c0fbe87a630d5a2471f3b67e79abfa1f8d3e89833e36d57c7cc 1127b1aa8e3d8bea0b4a9184720eae39e1879fa973d87966cd3fddc8901e0ed1 afae49672415a9cf1aa4f5d5a921e6ade299a517a5681c5888e1eafac35caefe 1bb1d53b9801aec3210277fb8af2e473a8d1026495c0900735d89f91adb54508 53c979d5d09084b0d729b5e740ef1ed51f99cc416b3096869eca4a287598cf60 42da4fc63c86c7a13bed979e30195d45e73d0b959865411465c2ccc30983a0e8 b0e4fa801af1c5c2834b31cf80831fb11d626020255f2b0bbc7c96465f523288 2af31e59dcb552b4eed7558885de0cffbb5cfa7217b423c590017588b488bac0 6acfb4632b22521dbe49fd353a239848f53e7265d43c89b59da8b0cc3f8303ff bd720e79385f6d2ca5f70fe141d02b50e21648cb3f959daa425de1e4392e5c75 0e3e941a4fb0c523195cc6a0dfdea6f52c566ab82c25483b9a10a9d11465589c 85fea41341b6d49d4823e309d4468d7cb655811177bb8a53e236c0747368b819 32593bb228b2f454aa091fe6b2e4716bfa29be3089306895310b1f2f57230c56 4ce8bbf39404f890dd296a2718624b8845eec23023437ad83dd3f90e3ebeb52d 434fc3946e64e1d07b97b77988cb48b8ae1133912dce2b9c1902bb9c83f63886 14354e897eba43297579a03878669d513e37ac42bee8e7c64e354ed39ba6ac3b 67c9bd298f98c083736bbd6511fed9fb238ba09634d630acf8c13fbf5d4139cc 3c77c3a7286acbf62237efbbae13eac56c4c0bd55f7fae2176550ae2f49b6b46 5149f6fe63a8042b6fe16c6e31b5b3da0245bb49ec249045152b1814e777efa0 e9c51ac176861a0c5e72275c34686f38e8fbcec99f81176a4f280e50c1d68de8 6d2de0ce0adf4eef46e9bc7a082ba9c45384c5e2766f7cd374f6e7ecf767afe4 7defec0b1eb6aad14ba9f886a3bf94df39f8f9beb4bf3dcfdd8e9f30c74b62fb f816bc04b50cb419e5342f8d9c1c45f61e8f7ee1021271cab7f60eb938619856 4e740f39bde4b3ca794f5218df1045ad330c992f198de22c7b1581e585f6e8bc 2a707dd3aa3ef5a0fb89987ef90884797e9b679941563b1b0f0fe482df2cb704 780363e8eaa858601ceb6eca2a95ff77caccf4a4ea15893dbfdf1d97f6196b5f 7954b47f519dd86d64fe572f5c83746dfc6cb7ffe388e257e5a27242705a615e cd730bc5c83ab9fd608d5e4d0ed2fb3a090e993da29c0341120f94940fb31a54 50eaa1323e56b6cb217c2728120f51cb7fa19c2dce41be26baef95c6f61cc276 811817719c3c0541dfeceff7756e80f7c868d56d6bd06feb1027a0c37b9957ef 1221701c18e4cb5e606260d2a85c99433c136549ffcbed18cf2e843ac4118695 972eada79c154e43a5ab8797202f266809d163f8a1496bf750667667d1f73659 812e63c9f4f8225eb94880915c2142e26163ef6d27d4a240b9bd3424ab0568ea 65e81550555313646f8ea5d2e1d998039c087825103a1cc9aec56489d6e7cb4e 4cadbcd7d0be79f6f32dfaa6a45a7479081bb824b5029c3efcfd51cd218791db 163486ea2d379505656ed6065a355a2b5a0bec2f1c56121047e2513708af0ba6 e312f142355698ac9e4705286e24ce259986cbf9c475be9908484ded0aba457d 5cf113e03b26cb54d5f672b6997a44b28af8421d258d1a51244abae07783dde4 7d19c56181925f1502281af675823ed9799978028ea3fde25e7cb7f2b2bf1af5 561035986edcd4fb791798407f9351b9d15ab7ddec1b1156211e4372f65c081f 9c2d63e65438f187250b110223fc1a41600f1516b75299d418698ebdb278972d 8db1c5443b19e83d124cfb93cb7659022e0ab39ca30fceb1cb7eebccc04c33e9 2a0beb68b0cc5fb35a2c8ad37619b7faec3a573380101bb1f3f8f930e22f17c5 95a8467c529e347700ead64b0f2b2520edba9317e1988a5e05b48d88de6b23c1 1f07b7c84d37af770b0d4dd3db8031fd8671db285db5fb292bb24b4cf03a66c6 03c26aa45c9344f2108ed8bceda99db51e2b3e476a9507305f4a264cfc22487a 7acc92bf21c416c9c2853cdbcb75912d17768c0558f45a4f9dd89f9950c04925 570dfcad88ccc890f989f6fa90445433718d1a33c2f6cb9d06f01e105641a0a0 0e703be79defbf776eacf28e3c02faafd451295528af4c3c5abb85af27a26c6c 8868637d4111edc6b9967b4817419116ff4e08342f94a1c919f88cfb66511db8 ad34c041687a0aac2d5b95645c3be9a092854f05ca306fc4df87b66fa20ac854 3c9c07cb12c72cdbd9c0a53a56dcf49286ccf1443bb46e389bc7f98e9c1e5a08 0a73a13db0b8a4cec8adb5f49b3ef1aff9116ea1b40907e7d7d53747914a6165 301e0e2771166d45a7db4739b33e67ded6f458140e03b77f7dc37dc10e5795c3 456ac73e71f674bd8436300971c616ba64522aad67f199c8600ddf5de5a5e6db cf529e39c63f83fdeaf309fe226248dd7c0d8ece4889249b1ee3f42802e84bb4 6c593de6e012579b5cc3f43dff7ac21e30433117f39e1dc5d17627dbcccfa8bb 2310887b528cdb93fbb62059afefb5dc8bb1ad99a6c26f470e8dafea9a21b75c c874e7cda400a4a7d347b9ccc3849b871083bbe16ba4a0961b5407d885d885e2 7d85f52aabe672cd6374d1f45ee2b8bb64bc9aa713c9d5b11164358dc0618043 ee63fc1d84e5ce7b6fea05733df25621d53e2f8b59d46cf6d3c206e0a1a4bbad 1945742ad66ed67a7e7be7619553eae5772137b123efc125cfa56771f73628c6 16578d8f6792c063ffefbb9fbda79cc2cf4a217a15d09fd6d9e8c6f5cb3edfb7 2fb93dbad3bdd8c52e66bdecb845ac12df250668987a46533e64ee1702586c11 0dc58bb319f98d536c5aba1b410dd591de2cfbbcb9626004e73f953989a09063 8146f9ce2ec99a5581481f5f9ccc32e31550925b16239c20adebe48c2a0dc8ed 6fbeeda5f57a6c3bec5266808f679ebe1786ad8556df24452d9465ba0163cbca 434b44876cfb72d61daf97f0bd8aa85d323af4320f52b880dc55a9c06c3c1463 c13ff3702adfcd8acc2a0b6e4e675b82f503549dcbf14a7c29f66393fad8093d fb76c1d8836c2a1f718106011e3dc5ad544b893726fa72b941d123ec6a20f785 2b7ecd72aea8643057327b542103b08e00b79ae6198592dde61cd62929089e83 e7cef581b5062606b00a2ae4310940b5f18d56d2742705a8bc3c7c54042c1694 d5e7cf1bfe62c0c6abb750e4b22fdc9b11355e4b34e884c94571163d21a81d6f ce69ce14cb9153cacea90c7fe3e8460653d27c61d239718a937f1fca35f99d17 b2aceb0c7bb9a7f865b3f01d06eac0cb1d3ef32a4e2145a49473135641276c0e e0dd5fac25d7e724aa18512adf237fc463676a5d1c9ce42c6a8b2c5cda013506 f7e58fe431e81b6fb8d5db68f69a7e4596c76227c0291f73f9754640085c7675 b63f41f757fe3f6eb32d2b7ecbcc86dd06d3ed3c68f22e41db42550913169a45 3a143a89e143e860c93e71c71f42a04ab75dac68baf92da4fefa6c6b173523c0 7417ca2eef7e735a3ab12e4a0e3b3c2439c71dfad03014200b8192206acc78f1 3e0136f18d38405b67567e2ea5165d8782a1ced39df60f06980d3d2ad9d24f3b 4a3e58971a4e4f94ce12c054249d1fdf9dc371bc7b5279f78197800c2a51bb46 269cfdf63c238721dba6bba1b839759bb0fe9e83d609a2901d141786090c3f94 95eb48427cb6c4f471c2ea568ed9df6633862be02cb2ca025c73b2d624b056af 421e453d565a46650ef8227bace09af7c068466da857357cd9b2873ad1ad09d6 822692d9465ea2005914901f30869c138f9a6351ee06b318ea9a26d443d97d39 fa23a03fc5b0b6e1150a54fe2bdfc78e925f4a66443f7e4a918a7c44cdcd05b4 c40394856093b0f40c439a94508d1fc3cdd5d6f1747d7a876c3250deb24261f8 0082526a179e1f85f2c456b87355d540144ae21c53cadf9783c5a00cc145fa73 91265fa8f7e3ccf55f695c17a0004ac429cfe485463c2dca00a116b84883ed3c 81dc3104800931b6a4c5cece8d3e2e8629722bdcc210bda4cbcbf9871a43edc8 aeeefded266eb59949423a42d3ebc1af07873e1e954696bf44daf3a75e7dd5cb 230cb94219fe81dec527b602ceb442d2fd047f5f69f94e5aa5229e18ce6eeb72 e495df1e8aa30144401c58dd35d99e206efef5800b42ae2c855ba2933ed1641f a3aea8193f840fd72d17b18a89b937a4892fd267a117d45db566c334763275f5 21df42a8e0ebc06d0be965488fa6bc6fd56e9a8071e7d7066e9c0be8a18715e9 199b10cff8216a7ee22ceb37cf3c03d219233e65a473f26fef00137d8c52be50 69375507f886578c829b830f5964a71243a21a88b9aea1555bb34b136776ae64 53d95e59a7cec2ea6ba975536d48a76fb560f61e988a7d5e27b2564c4e429450 9840ea72bdb5e374a4b515fdbaa737f50be2d4115e4192d0973a652c10d553d1 ad8d699290c6ea3864c6dbfe1fc2df282a7033a115ac050af16cadb47f1814fd b9b84b4655239182a7c635dfda8444a090d56ad9f36b39e49c9cc04c9b903d5b f5c7569fa43ac9d2639d582f90467c15ff8edac68da3368307be710019cc73af 52b6e9e81e004a4d350d87b19d86dcd759429a09954105b4b884508d0fd2b022 194d7708a9ae555cb73ddcd4b5e41ae54e297c19b03525eadf28030b16c47388 d158efb1f35f7d182a24ae62a543440c932d48b056d62a6bca8da8fa388fa251 6aecb71bb4a7ffcd6c4d15bccca66e1f2bc16d4a6c3d75990c3dbd30800d0282 5ecab698f65743d8bcd619885f36e574f28ec1f665847dd87ed66c7bf94bb8f9 5824367f4a299d2f99dba1bed610b421f6486ca776d3b1b3e8ac8e9c7567b4a1 9b1a0a3bd2e544a7cdec5b13861e77c8103386e57710ee6f31ee7ac777d3e089 eb852b565d9a9a4b1175a30bef0ed763c6d2d0369dee85bc28671c7d50dedc79 845b3fa7475922b76431644b6be2f0cd48c997ee497b0e3ad7ea8c3d1061e1da 1e9b85af0b4eaf78c85ff0089c6888b8cfcdd54f876d7111a3df6f96bcfcc678 d30f8e5b89b088b901c943e89f093b40838b706757139eff1b47c848edb286f9 aafba65a11d917cd3b38bc357cea2ea44cd018cffa6fcb9045e8b35501f93ed0 931de2cdda585f0ff0f7f81e747c1c5b20ae49a930a943871c8581dbfa5467e3 6f7ec2a05580bea3790eda02264a371e19ea10a05818b2d1f9df342945276a76 f5a08ffbb5b9317ba9281c7c77f3f6b91b02a17b8bcd51b7e0b2a84bfa0399de 4737d5fea3b9e3567f1c74a6527c2be489002195ec96c75fa7a412fa020c4b90 8693790f118dd039b89e51cfa8b7887c98bf8145dba4a981adabf3e354e45d77 b8a660ce9846d73f1ebb888faea6345ddc5c2124c9eecbab120d6ed427c59f2a 4b1ebf45036321b68ec8b0543203d21bf9a639027fe58261a98b44c0af19bfbe 24b6b654c8f53fbe508e669b3804cb9cd55056540a9b7df24ce403888b3999d3 fd9930b13d52c5d70440304d0e1e5b54a4b6b1ca2a435677c5f118f07e2b36ee 717eaf9e1090b2f1894e4678602a3cb5c0f374858af6b0d4fc723b396353ea1d de9c034a546b2c6cd88154fc9eb766f30b986dac38c3cfba22b16ddcdfe9dc7d 93c221d3be2d20f4ac343b9e67beb6dbe181acfa2e03e80b536b74bddd754f38 4e3424bb19b878f1845783d33117f315a972e4cb8a4f5f39218ad3eb081e07d8 4f254a776c0f3bd7779c82c22c33390aa41909c89ea9bdcad91dafc6e4003050 2050b427b2a2ec9dfba6c86014777901852742c5783c8e89d114938ab728ee18 9dbfb19eb52ca6e2ae0ee44e5df65bcc5e7439af40389244cb3b54fd9866aa26 d8b8df21ed6caea91e075382344d7e63de02addc86beb7d76be24ef2155e8353 22662c59ef7015896560440498100035ee0763c5a6de0b263dca397058523c34 0b8d591914e20c40bb5fa99de44ffbaa993b8fb08938e3f5033c07f9e4bfe881 55f9e2ad6e2699c81c525daa2008c323a0978a3b85dc99d591dda69f0aacba72 d228415156119c0157cccc74483b149d443a4ce32971461239627b7aa02a4b89 e8c7e96a2ffea407a803eadcf5ddc6254795c329a6f7332fe8220bd26cfc83f0 6e14ae550d01199ae9b978ace5b382fde0807d8671827a75f3951b18827527d3 259bd7e461198a3ca17d4336ed9f6214cfd10404d3a6f6a9a0bbd87c26bd9076 25e4b2650205fe5dd3c4b1ec5677389b00a4912b5810622b505d72125200a42c b38caff798124570938f7d2e92aa8b0a37229020355e0525069cb2c2ea16eebd f6d09aef0f9a2820f555475e44521817a2044a407da71731aac20fc4b6f2c26e 1fd4bd9f88327912a6c6ccf04009e9bd3cf0fe43fd654e1fe96382ff0ca4c509 35 7e308f9108d9f3921d5962875e9b937cddd86c67f606ea8492b5f38c1502a103121e3a84f0121bd0af0304d84533bb273d4d7c58550988ead7c9303ba7360d08eb80d12d809290ec94c35edff2b817b85473f87e4b58e26a356017bdad08a60ba11aa5148c5501c75abee978b9c04b5fcd775977f8455e76e52044c51d6ea503ccab1c00cfbadb06876ae1e0c9263e459188aee53da19294f5ddf5f72b94350f52d3c66d8394111a8e6924df6e039569394f84dfc973130a06155f5803024b043e69295f2637c10e5ec54538cd77f85077c1b0c097cac40f437270c289b5350dd3cddd37bea0ad347c914fd540de6e52fcb0bc585e045ed0dc1c92de785c570e9eea914820ad7346e3cf1b1ac5f6b4569797fa85602321442b6856f8325ae70751cfb870eabcdbf06feaea72edbf929ba302a5f658031b6234b5261adb534a06da6dca88ac50da346c000bc249f183a09d23c79cc6ae9dff2c100c8b234ce40f1b4ed62c34da2f877a8686282e905842ae30654d7f4e1ce7aacca0ba8c1f780f69e34c2de0a9839ae12627afff7280c23e0db49b5d101c8b342f3282edd917052e466dd7b71f2e1f35dd6f33952527c28bbe51138a21abb702b5a24f4425400bdce16ede18534548eead784e03ce7e331114f7ba36c5ae3d4a91f481861b0701ce3d999e29e2c14378a1fa7064abb54cbca5e6df0d3f290245edeedd8ff3910d6a7848483b0f8b623b1fc92abc4630e71a333558d846e86e53a73f14abba4409c23cbf48ce04822d7db21d501594aa59251a5e932cb75c968b5c752c3709c4064aeeb62813a9c66c3d67f9f55329bd2dad112b6fdcef7e58a9f18a9dde4ef30ccafa71f2e7132ac62217a4b837ece3ed2f858ba5f244a1c05fdbaea5b7cf6801e18c1902c1a4c4beeeea4ba074e786471c553252e479805dda2d97edfe7f16033d3ebdb9c86fa3eea9c9ba992d0af03e98643ecf495bfd6f7a7c15143510e70e833719e3713de590e6837552da1ea891034447e232818b7eb8dbe9732a4a4905996b121cd8fa375f761c31cf6e3371c98f71bb30c7765bcdc9a4aa6f959b0000f3cdaf5d4d2a12e9cea4bb5428429a1b0446f1b6749461b18d28260a494aee064f8a1f6f92d8be76dd9056ed37bf0dd0b8362c026c90ce49a85a52bda6f7340a900a44e7a673b10b42e398a64c8ad309026acd9866d0df03a46ba1866077490eba015528244082dd00cd98fe0811fbc9ea543ba296826632ba3d2b40ef79be0a1f5839d89034afca07073f656d3742322b0545c5f2c8aa300bc1868a7569190c4fe1e9ccfd5bac13fd99b797c9b0571a08bf73ab8adcddcac3654c24f08200024b5a82ef4b1b99539b7dc11e57a18dd2cc744e12bb19b3060355dd0540018f05b1894c3ba57c3df94d78367bf49e795d3d3918a3b71e1ddc31684939fe49380706ffa3267749183987d9dfa191a30c79468ca27b4e48ce7ea29ad6619a35b2046442e99e8772a5478c5e2668322825267cc60e71259e1ee21b0fd71990911a02b3c0eeb2e32f589d42a9d7e15bcd18aec78f11f6d3bc31f0f422cc2f0afcc501f2932391447ba4afe5da0bbe7d783936b8b70bf758f3b28641f07316c69af60782dd9f89a4d75596ecb66e27bafed76186a56156b2c98fdc54f96272233f4207fabe4a1f5e647add04ec9cb25de359774f844a86b69f8833be44beacefcb200c143534426aa64d28068e1a84ec617a0ad31995873e47fa91180460090e4c8206123d39777fb7d83ea96d4d13a78b6e45076e625a227c79a6fe0410d2e42d080ee386f376655611fb1e9ea539c991cc372140183fa45a9761bec5de4da7353c031a4a6463ced9af2c960b6bc31dc43750350032be1a509fe1a9f6b3a7b0522c0ce9340b12f8678452c36b065757c05c4a2690c4d47048d05cc49d92d667e8ff05df1fe2c5dc184b4793a8b2552b4123a35c93f661d7fe78441cc377fb50c3dd06e6021e4a1b84db5463e801ebac2cbda82904bc076e33a78746d94378b1fa330475b3f01b6523524816c3967e57af098b681e46f23c34fa9af4191b6fd5f74d08ce536a5d770924f875d886ae885695ff96cb6d1a4252a0d381fa4dc11b92c904553adb374fac14369cc66101aadf70317ef7d0946d9b5b6019aa85681d49bf02433b99b625f001c652bbf0c1256b4a83938410297f8467a6b035c8c24cba2d0afec80bb6dbc6caa0a4ca980298e03a2c047913f23ef07b26cdca76705ec3650d630fa66f40c088e4a82f517e9ff3c26cca3518953405d194e54726a3324a8a0cee9257d0c63f1f6f07119a5a3b400c1d7d9fcb0d3fc4470c84eb40b74d14710400ff55fdb4e27e4be497c28c96f20f962901fd8b1a9a25d2f93b6861a1afb403855923ca02f030e18d9b8409f7ed72a020fefc0e59941662bfcb5285382f420119f6bfe18d79b078c30c4d2639001f578805abd1f5b022df991525acde1abf08fc3435eb89634ec41b40baf9a4c6b547d2142cebb06ec2ad2f9e0af044655f0323aa305767a24c301ef4eac4b87f9724fe9d9d7c5427cb07b12b1270decfd9062976a2b8e5873082d95774cc0292d361d863d96915f61be5e02898255c050f04e6e7a89f7664ec47899e55497deb3fae8c9dcda6dd8d07587c65c78cbc93db0cd65dfb91859a94d9ce830a9c84ef7b52ede7e721a1c0c3dea72e26fb7582ff04c2fb7a7ed8445136ef37ba1b91945adf2bec1f3da834e3a3157037db7634800dc108d32bd426d4c1045275a2b6094aaa58f0b68b4fa258e8fcc4b01b02b8ac062a37c09be69c54830efa33c46b00f07816e7a845494896f96d8f6cec3faa8d0c222b00da1784ccf31019e409eec43e81f865d5767e35fdd9ac90725766321e0e02cc4335d67be30c859e2e66d09f651148e3250f3bb15bf444b87b503f20210b44e03a233322db8ce32829a58982b70439c56130214c7bda9fbe3d40eed7fb0e0c3a9e68c02d43a26683cf166521745c1935b039b3728ab51a9893e361057a020cd3e9e91a11b8c0df4bf8ab8f6c8b0dc15d62506868e1ea72d919de2b0f920ef3722c910032a1a4cf27870a7a0ed6decaadfdd529d75a4e742bd66ea4a5ba048e9d1d8cdee7f0dcf3299bfb01032aaaed959a6f7b7b2b2738ca3f4788eaac09202ed8db29b02d6a7e92c01475fbfe9253b897a908bee5ed1d0bec66a2e5d50865c151c70eb28629407c003c314df46afca3aa82f207b690ec8e4c28a3d5660e42c335d028a33ad9ddfe2fd4daa8a25cd2a5d8c213fc69eacbe64a97044dd00a5434a5d3e20e771530c72065c2a86f2353d0600e308a8855377da0c64f6bb90e9017294660d13ed05302631086e5c2faa87263ab8a85f620c6835898c6b86a0941843df16dffac01d9c926ba0f02ef85a58dfea5a99533c95b110c4e4c220b0d5cff8e6cf98bf37debaedf04cd25bd591368ce6ceb39a635f695ddca1a69ac00fd3b1668e9e9a851da6fb6975c5f01cc30b8a4ad91c13daeebab72f15bcdde0e2a2728a60369eb1dc7e96f3b5ded85ba47b89bc0b080cd7638f64b2c1992e205f8170aa457f84c5d831b2a6bed84085ad9899ed0a09b1a88aa0a24d069c98d07426dbafd82b30ff6baff6964d0f41fddd6719ff4f13387a89acaa62e3bd2b501dcb0681bd3c08d9b6682e044dd30bd25086191c2e184a5780279fa69a79f550d773bdfad65b31b7520749a0a2362c3347c0263a13313f7dcb9ed079786e8a503e678fb740aebd8a8f0995b8ef1f87ea9e83e3a459167a7ee260d52e6f0ab940ea8f018fa2c052bb5ae444fb5844dcb914426750fe05e99e71566c9e44f0cd906bb2c20c508e1bea70ea03310b53a87dfab6884530f76b64390954ac9512788013a876cf8fccedd160493c05ef77474050f19570b732fc23c14573f9ed628d00a4abec2fc43ca826b7a77e40e4bc38af18d04c1fbc695465923526cf047947c016901b45f6cae865346f580da8fba6d38731d95155e111f0f5ab4b18377056d00289a8d9a00fe70082e1e7dbeff8b8e5c8827b30a8dab4a2f93ce7526dac5950e9303a08e6c200cd95b25758e5cb4caa5dc3d3ea8814a06338e8e753d6847a10b41810df0afdfe1b4564b148867ae98dc136b0bf3d6e7ce78b59e38e885d3c6001de7186f6024b5d1df8a5798af652345e84d99540c7c03425aa59add7b10560b2466bdca353f3148eac51f0f020223d1241091426709afbe8681cc9a378c9c0f54648c49751971e97294f14d4b80075a2876911b80b373a1f181e9113f2fff038c893ada1cee7b7d1910f1701374aa255e5d991cccfd3b6c3936495fb37b7201935d6a83487b882e62198ee84a75fe9f6ad09086d998b0f2dd72067365db8c096030cfde05b6243df956207ba3f41dd20b518d16ebe94e17248df8d6ed3654069861e13c4da96cc3d96d67d609e01bfa42c7c2bdc6106b918ef7190f20cc730592bd1fcf0a338df807152712c4f88695d202de2c132c4d00696a6f6113bbf1090d3b9cdb98cce551dd3780e259c9be821b0d71defb7f5ca9747372f589257807a18fd5cda88586cf49ecc6a019293cfb04e7d542106c74c79791d3cc763a660539cb649c4cfc8593bf95e131609cdcb3b51dba3022d429949d3ef92112b9c709e815a19337ebad3156fb2fc0d93bb4f2042a012da19b7c6baef7b6fcfc6ef7050a0c56c816b314472a28c30739fe5f5c34b3199b316a34625ec2a916278122085b4a5901e753908c60fad34fd5309d2897d942063d748d73c2c34fbbe3cc5f0f562af9a32ff51222487ad1a63c6e1094f459e648ae2ded85c8cccbfe8f301d0a9c9c2bb1b3d99c2f48bb4b0c35848f4bc1835cd5df5343b17b65eb51dd47c2018fe1ec2f4dec7ce976bee8a5f10a0d41669c773ae5f01d503b8118fff4a0b90fcd4e610588fc781edae15c8339b8889e2df6ebf78ecd3a8d4677043a69a2fd017a083507c67396cd1fdd8630408d71c16db0254b5ce5714e141ab8b4652d34087e5a2a1fb9be9688e1bed67e2bfb38c9ae4fe14b394daeefb790625c7a5b7103845bdec58e32783ab54ae7d1890e1c01fc7d0ec9062441d7bd9249dd53dcd102e9f19db13d5019e20271d38b2b9ce8b57d321722a6c6a986233937ed4d82ef0a0589077a5ce9980c4c5f64e5f8886f6744ef0392cdf81c89e5e14d4ba81f950001200a976bdd964481ffdc47e0e5a28a931c95b5d8734df7f93c3875c57c44058974d666a4f3077e13103b52b3228670b8149c4a0e44d07809e35342d126960863c3afe8e2dd5fb7940c235e5bf535cbcf1cdc6facf0d37de514ca07c4f6b80ad57e57cf80499db6ac9d0abc5c3b282a0bcc65f642ab02358f12dcd0f89a11093e808f6470ae209d9bdf9f3e1c5246d9669383183cc94e8f7d339d48adc7ea08b428bc89d26cfec99921488bce8eed207d9856f1cefa9ba69ec9316d04fff50e4a297db97f966152fdf849724e979a529e2fcc98acb39434bdf16059095133003f05ff953c3128d2157134f539f4c68e61a09d60a0b188f28e05acb6b5b22c071f9c54958c3ef46c281ca99ed65c7ad6fd4304b8a2d927bc1e0cb4c82208a204fdea679cb68cbc95382598358b4e8036ad186ea9db67285f590d6acebf3c910fc3acfdf8dc27a6360b3c095f6bbb14e77c593f834d237f45725477ce97177f0c61aacffcb65509e3d5a2cf96c17df6c53938a51efbca665c4b0cc9383ead280f2384427a134de05aff0df35a3fdef397e84d4f9c46de86b1282c24523b6cdf0e0db46e5b33160606f2f1d06a20076ec5020701705110f9193dca35e480a91d0389b3da164d94668702a5db7fb9fb11960da05f77d112278747ec9dd7dc725c0deb8808ed2a1dc37c24ee6dda732fb9a57cded01dbdb174522cc8f48637ecda09fd23953c1ab7f7cbc47ce6dc66806f14dc8ad6db2e44a0cf7522f51a8b8771022e80cc3188012f3d388ae648b47756970d7e74c1aeb1350e1abe2c876fc915025368a506ba5dbbf56ab8a019d6b63fd03b4a526e475cb0cb0aabbfd2348fd30e2280bf99e868d2220bd936f954037adcb6d93ad2e6578d24872db2afbf25b40aff7e03d45003dea6bfd76767aa4cb287b5d8b8dc0571d7aea2c9be170aa7f8096338f26eecd554b123e6334887034dbd86523c6ae1db1458f13d152d6d066a0be1a6add88ad6a2d7e02fdcfde77392735e686ea8b198059b5e364d72204db70aac27b4f6239537699bc716c988a8e0efc0cdf883b967aa84592093c2e3c96a0373d1525cbd7f3aacc02908e0a5151aaf6629f0b639e4fdb44e9e6ff5932e0b003c3d2a13e09a13f77e4a8dd49e5d518f72ea69bbf7491bd7a25ce27d896df608ac898387dc5f4236173f4caeff65954f6bfbc90ca1224c1e7ea2b41e7f3eb20547fccb087c7295adc740881090273371979a2202250f8615bc4b843b629be90413cbeca3532b7e02db4baa9c8cb2ff62b7a8f17c96da0de8a96135b01313740758a0e52e1d12b4e077f3307b8be96239df0628b3c47e152bb986d2e228de4502f761e22a2b74f011c8040f55b56728e508a5c0d4fc8946492f8152d9d88ae6049d45559752987c257bb38c8443b460864b0d6addd11839b5c0bd84c1862c630f37ba19ce48482fb55387611ead9e83cc89555dd188c0357e48ffab30287204085f0a81d9fe7d661ecdfddf37f19c1d2f2f4343fb6e8bfb3739e96cb92c4ae40ab8896b390e8a3f5b936508206799d96483423d2bacdc2cc6af73e8baed1fae0d6769886aa370f610f38b028746887caae32598b41c800571f1994bf3b490ff018655235dd40585dedb9aea2611949f8680c75bc2f5a27ccb162a95a68e95ae0f788361ef0e67e927db71a2bc083eb94d6fa3064bb287b6197a725197cee0f502382c3a1e9ea372d0416185dcfdde027d512c796f25114e5d216aedaf0d41400b4e9ae7f7c97b68e53871cc90130749bc38646d9a031cead7bbed1706d1abd606b9389d13c1895910e520e86fa5bc8a37bc60b92181fb5e83324029eeda6aca0647e812c5e3688ac421cc919126a2324b345049418769507170d324cf131c5d05ee76cfd78cd9265eea08eb6883303ee6b87fcd92e6ea4fe197ab05f74d7f0e0f87ac1bc6c2f7856e493e3dfb28b57e60ba62a7fb3e9a779cc34e3ceeadbf9304ec7c441b6a5c3cf2a51b45c80b4b4d39c7f76d74f3c39dab5514cfb5eb562405747ea911ca5a0243b6b0df4deb8cd31d0c268267ddb82b25029fa52c9cd32905bb531375a95b2987f5ea4e6c82db0205fb74ec3801ee725a5869829fe5abaf0caa5412a2fba581aebd07ac193bce1be8a6ecdc06ce7fc1d53005ddb72c7ff0048a8214009150d6e364cf07243016bd5fd7236c179011af2c29f09b5cb2abbd0738c4d84430f1f330c16d09653d88c396bbdad126ffcad281eb210821baa4de063fb32f803cd07afcce7f83227b3556276d113d6b4c0aa41b06d766334524d805726132fa59e479aa3fca261fb1b0dbf7bac6d3040a8cae36a50628daa91e940a6a8730b5ebcbb8b384f5ca886e9ca9767c567be0bb3bb6049c0f8b4b95ef8c031c80add827b75e22373a80161ed441ebccefb66d1b023a7a6cbfec90f9c96b0a5a2d5b1dba2b308816f2906073cc5cb9c3244306c728afdf17f3a6269e8a540d69f8a60aa4c2f62368a5cd7cb9d9d44b6a8e1e9910f53f8cfc49df4a4c26920ea41b4f0f862a909d6bab09abb805c11444f56d4faa59d53bbc747e4c06ed6e075efc463e802fb9e6e74784086c0fd8796ba99f30ac047cf418f40056862b9b0ef7f31f409e03731f97a72b1c0fc7a438d8baf79f8d0e837ef6a47de55e2dc10ab691298735030640e91277a872bad7a760454ec7a8f8cb5b6b6dec410468a30d9cd3c5ebe00ddcec846681133a97950893bf4034145fb79b5cae017e643b5d046c24f40ec49c9d3271d9a9047b477954955065bdf3a4c0690cbdb5c79d6a6f087c8ac80e88876e8dd21af696a0a625e6c8367bcdfa66acfecfb352a2fc926403a5278e77c75781da2cd38525b339a255dbce80e4ff72215333e5f72969593501a2f0cea5a24458ffd04ae63c5e60331a5f49bac3bc404acfc065b26afe8d9a08b3bec0d08b594ba2bf8a984540cd1d073236fffef040711a7d8360e59e5e7e0b1a6216fbc82a015fca730ba100fc9fbaa9c19970378d8735769d644dcc19450d19601d5a90783afd8787e13c78d39499e1f2aba14b0b89935613049242956507a3b2146549e776d9f9ac61f0024d4e0dfa14618030d758c2edbbc618c9ca8b0ebb121a9e5bad7410f82a49f4cb8b7c6fb14edb276453318703f5253d595dbe0c181ec17dd0cb73381342ca4cb4d5f81e0721f7d2d1e44539b04b1ccabeffc90e91620d25d659b1addcdbef9a851b0057b0552fdda4bc0193e86bf409ce65a10a310a9d68ddbfe3fc33b95394823e74af06880920ffa47b3457669368089be10bc7db22c5f64025c24fbfacec410d6db94bf0e20f6cde7e593d33ca980ac2ad07c0098f7479cd461f3de966a6fb63907a705f3828030a1a37714001eb4b68420a3c032988b60b4967bbd86906397734af2a9f1acf28e6415fc855278904fc0e08e272f2f150e42082e9d9c64af3df5376aee6c4e9c65f01c6f67c1fdcf7515f035995db08f465bac830f57e8768d0d504f713f26488087e1b4f4d83bb4a959a036e5377118a3029df6a8aad18d79915da351166bce1626ad3b70fb103d7187b0040f36a7cd387c2cbf75ca42e1f3753cd914b9b17a2d7468bfe6cdc26556a5e08bb0169076e6bd98244d58a87472540a28c8e3624b71335c02c08381049140e07e7064668a6f7250cba79241beae7bf761b6fc6620353a6de8f827b75b2f92e0ccbe815f1c48d41fd29dd3e1f57275bbdb06275c7255fda1db2da004ac6afc70b43aba531b3d40e8172eee3c3af0cfed3d0b3a4d4e7da00d5fe4679d37ad38908ba924197b8d3dc5036178151ceca051283187965f381e7fe2ec8a14fdfa53300ee5d983eff36b87ffbe125b87e9c16df9012c147b6a58e2e395a182159c3da04bddcb5df1231ec7f3638ee1412e3d06859f55abb79af3b0e760fff6805d2b30f6901838ba2166f9ae285d296e5313a36b9739901d682345c2933ef7eacbf80069490d86f9f5a4f44f7e73a2c7d0a1e486f56bec7d1fd67475bba6c1f0a5813089cef1f2de4f3ab7924ddd0e730b48101ba69240ff0a99ed04bc1a999c8dc1d0b117883381cac06ab840ba890c65aa93103aa29045aee55e393d3084f42b14201353194b6e05a91a886c22292d11199b82e1bbf3e30b329b2e03e544df0e92b0ca3c203f3675ce8197900f960d0d1c23f547b88d3a8ecb9022355609239c56d04fe8ba00b10bd656a62b56509f26ec99769515f9c58864c483b33149890c27504fc7e041da197e7065a40e938bbacb8e98dc180daab05ea379904536bff5cf409145e2a06de08b3701ce310bd2a90af0befd4fbdcf9f9ed1218bab1d2f7971a0c2956d4e98415bd16f3ce7c2a92aba3098d8793e174db9603eac09cd22c2704061159aed9f3d9a2be8f00d73d8e73691fdec4fdb875b4be159b378494dbc10405c3421ada30693f88ab3ae0d34a9e8ddc4f6bbacb564aa0d8886bdce425c9560cf6be7d4028238ee8608efd4b5c3443b8bca5cc897f6ea173e1cee38de66b02020d2882379f321af12062f9b21c022cf0b32a94ed95236deba98f1946ad4b260ff13710590fde31f571e3863dd71dbe6adfddf81aecb8781ea482466ec30997010bf656cc40d44509840be524f976965f1f5a84a9e71ca305c4d33737c7702d0067362b7b1c25d23d50eb211c94a5745f941ad46ff21803261b8150236de0b60367f00942be190e9b6d5120b8498820b5c464947596a30d691586baca77adab0ac1c342e42fcb0dcc28bea9fb9235bfbcbc210da8d424e25b3022b8feb70dcb0223e6965a516bd68002b0efa7c81f3d06d83b9249c9db8e79122d7ea899a5a20a4bbae76c694bf2680e31ceee22f19bf82ee098d5a52f5870380e7373ad395501fb9a422857d1fa766ed2332be2a69c9d8aed47d6137193409e2c83308bd48d054ff0de9fd763701e6159ff7a315f3050ebde11692dd4d64e901569ef2c68680b0a02f78e5f65cb748d0b75d533837feebe3dedb8f4263871abbfa2bef3ba1105708c0e4757badde88f56f5ddca7db74d7738045b95a6795ee7a31cea52340b0aa8a62fb37cd0260fb5ed73a45ecf4fc6f10749cde7e52d50ecbdc69eb6005a003685180b2ec910ae26216e2c0a1e96a4fe69a19d6db41847fcfab992532d5c0dc233f5ee18756651209ef425e2acf995dd44390acbea1a23ba52e0a540650e0a041ccf345ef07c4fb5d78888cf248e758be46d3ec90d0a69ab3af5d85fe91e054370a75edc76c5b20bdaef3a4ed654356c9d01f5c9fd53e30f7404f5f9d5d706a699b87e59b6e524c88e62fb4d28753ca6c53b96813a1fab2890c10da873e30eca4fa370c22a8acf94d004d78add522c4c462aec16d5c8488f39cfed78b8bf0715d40f78cb86c849ce67c80e851b4d621d0241c0fc33b8b7b976f139ac4a6206d8ac43f84c33ec00bbec205a7742e8098bce09f542f116b9da3bd36347d6e60a8745cf842d82ad31b0cf60153777615ffc03708612186b0edefa9f1e4c877a0f18cfa7e9899cf5f70670a9474f6b1eb19cc5adf6b30297530408ac61ee3f48073cfd1f878da326a384b563ee5beeff0af89be348310f6e1b81f1e8b972eb6401969730d4acf9a806bfe10b040389a7cd994de800be734a27dd691d7194d1040de3f0260119c21e8a8f764944bf95987ca37c20fe7956d483a45f3a4dfeb1be00b2387d40f2f20032e805f8e52e8647bcb5127c012e7cc037c2c906641ce74709c30b0d00a68c6ea654e56f2a348ee3380cb709ccf20a299295fee486df40e5096569eaa86d7c7d0223deed5b9bc94763ba679f8a0d7329bb5b3fdf0aa660360de7901d2957944038147802fd36b1c5f0b24eb07dc4d208bf4ffde380953f5c07a7eccad3f39c4e1911b5ed640745e71f052c712c6ddb401fd8ebefc9a3aaf408759a209970780cf39c62312c8690bd77cbbc95b87f3fffbbdb3c1f95efcc150a9dcd37d73af6bf1ec4328e63f5ba6c52e3f2da253f96608f0531ed366ff5290d58e2c24bf594ca78f0e791f20172aaaa838f61b22dfaa332f36e18767b018f06f34b1ac8ce7ba882e996d6086c3a264e636ad16721e8f008b5b19f06a2de3e0abad7dc659d340d8e36c2fc68097e99e994255589db459cb17c361ffc303d420eac111e8d1601d6bed243a4d2db89fd5cf3883308181b3f0b3f07a5489544ce06a27647744548a2f25e27ec1651de0e962a00812039b5c6fedf998076f57a880e9519651dbfd803d4d723e17b7040c05101e3b69421dba0f7ee6589ae8a696d071aad4a8faeb258be7ec6c456ca96944c502b8ffd6a97e421599e8f94d5780904d8871304f8835e74c2480892b54e9f8e237efa1dcdbf4f1fb8ac81f3dcc25c0219efbd2fd1ef67b23c7da232b5bf2bd681686005bec01b283efbfd524198570d62e58e9f8e39d383e0caf88ea81b1566ea75ddcbbccd90adcfb8ed63a9b40c0c8bfcf34a3f194081585d90f35a79082bed4ea74f739650349ddb49595a980d01a74df4f85ca9bc47401bac53dca8548ebc97207663fb0c5fa62dfe948b98a10a56e78f138c5d07c13b6a7252058b3209b4f178e010dbc39e62caea46f49dcd0fb53eed92d2f554c26e7362a6b1d8ddbf0e121639b9eee14b202e485a2ac3e209e90581243dae633c655743ccdfb9c34ce1e901a30d9095097caf2bcb475ad301efdc4540e55f7bc810a02aec6d3948dae0b760237999476f2a272afc1fafd0046fddf000e67a2d3efca78ac096e9dbf05acd314e099c9382ed9d1592c2335708ee198a353e639a57ed639bc33dce4c0646ab8d3541eba898856738ef00411e09370a0423451a64bf09004a8b5cba32f7d94fff9eeeffe91df31a5728da88ab0b581c7dac573d08ff551501f7c975527e58bb7be29a9f459343dd05c29773ea08732a6da07be781d0cf8d6549cce95647f8fc977872f541415ad6d442a01c360eac8345d49b0278b83bc4fe3563632992e4538092c28ecb22d0606acbf07f20027362375eb2ddb9cd367b77fb6f909f4998fec5911fe39bc7b28f6c2a6e56a701dbc21dae903e89b9d12656f1dff6f1f67416223012ec32878b8c680d69017a048117a926c571cc9406ba3c9787891809437736ffbf1d29c6463d00e458e1c60f89e1b9498d1321c2dfa55c63822499e4cd4529a033f382da6580fa8818b79d08e49ae3eace2cde08fa76fe0a496750c89dc682c278062f597bc618ee6b079204551a3e33a5023076ab12fdaee5da6db11d3eac4df762d8f7c752f4b3d3439d03de70f5d58193b9e343d4413d45ed52fb06d22408411c4eb2b77134376e444e0c8e93087b2cb4788c3fb6ba616a82c910bef9dd3a1c953946b8a8bc07f876410e69ba45634dc5334ca1bd8ed1461254c134d03d2b3f79201810ebf0e2baaf730e3aa6ae820b6e2707b84d7bd150809b3b17d9848111facad44822750b39d0c9006cd6a570ed1243d09600cd85f745be9713e3dc6abc45a28461b3e66afd88c20a8b7a5740185cd455bb201f6f6f9335bb0ace02ddb89b409d5268ef929ce63702324c558ed020218018ea3d28ea64f69d5ad843f3edd19924066d271d6e44e10d67e20711496f92d51d9cba8837df6255170de993ae0b3278c70c520cd4246606f04de5444492e23ffba975cd4aaea44f6c1b2e3e6051e7e62485fdbc68515205a9645802b9bd82839085424b0ee46a8878b1f402849f56e6b9bedf2415fa3c0c0829d07b65cc44a6610a0da42d3e1920754ce696e8644af377ce57489c2cda081356c1b8629a9488a208bc0811b0bdf79d5ce0977995ba728e759ce8fe84830cf7b996258b07fd6de1403f3168baf815b3a189f6d83034f3866f6e7d3bc20503c4400620c75d9164efa7b721016f8cc101252b879e630eeef8554ab186ce550af6075198fe8fc899cafd03786ea7c490f57b44469598c1f5d137d767855a0607cc99afaf6e203542bfcf48b1fd7d8cea2f183f6ee47213067efe336c9017170c2b25b21554710c31a2bc76dc3780a036c1c1c6e05ef351e29133cb3d1428f808301b50b062bf0a34fc525fe7736dfe2711969650fe2910704d9ea74e9955700756ee0193549b09764a1d17ff01035094300590b48e2b461582fcb97f89e3b9088380d720e4b762a8f54184abe5558e2ed8f2291a0399147202ca852dbed31e0bffb652976cac42df3a41bb80a9205b8d7531ef91117661012aebc21422a4c007c14dc4a0a14043d3c9bc2ba41883ba53a5fc0a4f7cf95a9df9cd54752710f7095f2f4ac4b6e3f855a08603f1d75c412918d7f3dc155b3936dd137a6d266f20093a02241cabbbb78818a1af24075ee409c2dc7790ef7d0448c05ef964daa8f40780404c0b6f4fc1a46c3420ff2ba8970d365a3dc6986b5af4d33c57e2a78a970109ca32d1ed14f99c1cc87a15a323e6f2341f4720360681bf9da6d932f2d77f01a56f946d5256e4219baa0434f1a2a26824d3d21b105b6ecb1ae32a50eeca2708dc6f5064eec90df5a4e586a2ed0e56189aaab152bc823c881e7baf3a8dbd9105cad4538e70d403db8fd633067ff869d5439f9ce186fe5a6ae5d4161e22e5050f00f1eaee484f60428731b9df5f0585becea0fcd1101e50a0dfb20d64f47bc506a42c7e512784f414a8c93ee1bb4db9217d7bc60a263429935cd8d78615973603d8c90911c72f85b2b0db4212df4136e56e6929b75464e0485de93f5955b59509fbe6a66ef75d35f7f5a48205e743cb7df54ea681f6b3555323562a0b2be17d028e4d43bf6d5cd4107b9cf3368da9f7b9ef75d27a2bcaefacf45ffa53f4cf0208fa1e45ea0151fecb0ca5ae78123bbc374c120524ba487b08515016a49323130210ac4777678f6a757533e55644462cb9d2c371c032cdeb5df1b3ff03bf2c5f07a64843da23bae5931a77a306f816d7a0034e06ff77b1bf3b4bcccb40819e860e1d6866df65c00006257a80c8e366a827658f9cd4531c51f51b92845a5d41ee06bf0cc8a7b41a93ab19ba40f6a24af1642f297b222ff5c0ba7d53658a62322006e187298e0fb1107ca216ca491a0c8d9201b00384c08a9932711bf5b082b3c40defaf5eed1dc613b70ee379cb9db6efafe068e4691c950bb5bcc4bff4929aa70e8b51aef378bbdb8c91c6ddfd366c9c2dc1d002c2efe1b275b8032167dfd1a606fa30af25f40f655c5c5c256ee850acb325952ce59fbd71c8e63ab81dfd89b301649bb53e38f01bcd2c9d2cd2bb7b6f8e1c3f590ca020e0093d49da2abf4af70212047b62352183e196df1083a1c5b164bb7f0ba00ec4a6b7f1e7ded07c6dea09ec2b6172ab6c3f0f8f79ec8583ecadd38c54e0f9eed122f4daa1294698ca7d0a1669b616f2d2bcafdeb73117ca1cfb9c40e8b32cbbce61e7f1cb478ec2aa570a8387c3bf9ce73315dc907b9134b4c2a842799d0cc9e54405d7a42db32a64f00322129969e226463e13f1ff4a7b055d12c718e1b6c67d1afd8a3d2be623b7e403762f4c890ff6e40fe71a6bfdcdf9301fa4de66733429bf135e9c0697bf507106b1278afbd8f4576bc6ea5dfdf9789890501a0c392b578632983f6b1c7a67fa08b1e13a6000c576f8f4c549b5308ed1f3123c22eb1edc9d2ab13942e86e9b6f058899a673624eee13326330126b7cf121c35951dafaea8444af2f547c91a2b2076ea0a791ff0c7718aaad3e759266825d9efa222b9d5684baf9b51f1c015c4e0248f28d1b309097b09d0929c5580b108319274819b6db42953ebb07b9b7c1530509502f194b66f34a982bea46e37619ebced90ff09ddd23ce0511f20cef02120edba8bb7aa844199b034bf02475e022902e18029789ff4e15e880b55b9ced560a824bfa6e7ad37f5102ffa70022b2bb061fd6aea7f038f8d01c9384097109b50bdb3fd5a3a0e1ccfbc7429a3e1d9ee8b07db2af1b0aad79d8f1f8fe675bf14e079f0330f116655f14a2c2c3572a69f4ea3ac8e7750c91501e435d059aee3ebe07e2fe3661bf5186b7c4c5bc0dad0954f17514a8de8a4bd022511d89c645e2170906c347ae4e4fc77e40c73786bc1ca49f6c37185f4b18c98296712a248c23c909becb1a4251b343b5f5804ef4643bc442d96246244b3a5e39edaf929f9675df03456d8f0dd9124656fa8615ba5d679e6423a4a5adde1b2836434c65591b6bc20bc3c002559725125416492b2d937cdd895eb4eab2eba576e3a996de79dffbb10cf6c48562c604c78388d135c88f856721dfe5448a4c88d6727d973ba577418000a3a9c7a260276daa1ec7514d7cbe7d47aea92ecf4ac014e3828f86cc2080de089babded2773d64dbfb5043b5ac220e37d3aa6401324f107a29a1ab5e919b2b078a5f31b00e931d9f6bf5ee96a5df1064c5d96291da01dbbb135bdec9a9897f09ee1c2d98fcb2df112fe3cc53c7679215cc0b937cef825b5bf9ba2823f673030d7a7e86ab41c616d58ddcf22c58ba5f58948d745ed31b6f92d8eb2d31e95adc0a66b5df50fa836da39787433f70ff7de475c6cb36db478cebef0f2ef8873bfd0208e3db699097ae772ed80958d43c17428c2a09602dd260670a7e57a8d9774006f3cbd405764d7a9d33017dd7dc41503a90cc6ff984263b7aa5ad8a7ff7d8400e31cdec158c95ca6a389e4cdab231510a76160514c24ba73d7cb65a5ca1c57d0481471330e55c072406520c26c22c8f7aea87b8303e1a417181dfbaf1cb4ca10c8776e513a6e4412c3c21032e9d6e43e77b41f4a8835ec5b0093bc84f4df5140b0d7f02e6de0072d7ce8f8935279cec4a40e365d296f838ae92cabc6004234b0e2a5a38a8a2ebedf1d6f61161f0bcfb875ac3dc2a11a29c71a6af852b863f3a0e19e3e15f2182a31d68e8e194f42ed97b870eebc6ed86eb4a7520615a23fda302f4d7d756ca6c22494029dbf44312a860106cbda5c88a737aabaa359b51ff9f0d86616e89f1e292fe3a3dc27c47d717e68617e083c262fa3b697c07d04806730b4a966a6e57618e33f278364d919a5ce60a8f174e0321802e07938601ac3f070f948accde198526df578b916c2e1e02bc562128818c70f5d48e077b5171057c077f4b36854b01bbb63430635b5aaefbb06461449af9c4bc5bec48b708effb3a007a1c02d16b40dcb20b11e166381efe6dfcce5cda786354d66a6ec9bae8c58e0f7955b6fb065662ef797e961afa00538259a737d32671e5b34d85e9c3a748fc047cfb99874fd6e6c82a5b9272cf2cf29c7e05750da17819c0cb37e8ba7c00f50b14f1302cb747858b197026b77309c2bc88b0dad05693ee76551abee1e6d1c709578ff4a26e5689d8e9a7fb03d7dc0409b205196061b2edd08a6ce6e73774b40200098dc823f5c393d9183f751fc71b3aef4a6bb2bebe5f54345f7de75e39c8096c12bd7e7ac994a4cbd70903d54c817bf758cbc5a00861d60ab1432873ab7c0c957249d43dca6ee291143297e28db4ce7aae348458b9268d8208ecee87999d0df0eaee097736bc5aad5c7915afd956b439ddd4548b11cc2530955e5cd9af2505b36017c712c32b7e6d9cf74daf01bfdbcd85f06c949ac1979609c7a427c9160a35090f7a102bbc8057f85c8bd21acf0d688c45a95f886f066745944cc6f67b010de33898ffc94e879db9f2162c564acde3089fcef5d446f004d23ad0f116310481bd07fdc4f06bf0ef0636d6d64755825c35246c6719de4a4b0abcccb088410fb18af86edaa62f20856211b700ee1708f2d65dbfd2cfd6ca995ee5c108c5740c36b92d8e65b4959008b7a726e0d9aca1acd7a6f5e7c543061eda54c4beed3306a39b9cb892025d99ba58a8fad99dedc462ece235b74b8d1f90cad933e8f4260994b6b5146c97887dd30ecf0618d8cdf40f30a05b4043bf5412fc56336e34250f7346743d52ef21865344c531731bb9fa25993567171d56f453edac0a2af25003a08e35eebbd71d6bc371df17ad61918a0ad6ff7586909a4986a818d3adc0a30f478b0f261a7f0276d35a93106a7cabbe33680e71e92f3baa6f8ada72234c860dab3d4f698d02723ed8ddb0bb6dc43da792fdc70802652ac1137bb651879e560b51ea5156cc57a6e8390e1fe5f1ddebb858ac12d67518af739d17bbea1492c80246ebca7dbd471690e9d9eb4f0d7693899923aab1731184e127f819bfe65a6e022f1aff8e5f0f3a9ea9aab568e6bc0a0bc009e7d1a358f6d6c13e50b7a78532037f9958900545259239ed40383365dd0ec19b422a18009f1d6ffc260a129a7b09781f054f4701b19241340cff7033c00605b12e7c831443274dd25c4608e17501731c9001cdc97f378cf9e49c9f064735639e9e7cfea635e953b728f4ad77c20ab387ec5510beedca6fe2ddbd0f830b84ef360b683fc0423c8d660b477977a808ed31c3b980bdea7897c7b53161db0efe895beb770bfa603a9470f8b90169f40e6dd4dd9bb2485e166dd18f5df7d73d938bdcea271321c32660db40053485ba0bd598192e3dd41dee39252cb89c5a3e09e2721d9be1d11b5a1cf63d3cea1eed0088e9664de8f95d2ecb59162473cf3e3a72331038a307f13a563ede0bc13d0e023a680e9111151d5327e902b2ae9349dd1f92aca2ea562a92ac857c6fb875f40624ec08548a7397df8ec1de57daf97037c37aba167f663b81528c91ffbab9ec0922452216c801cda812daac6f354527241d573122cd856fe597d46e8941b23702abf12c53f4bce240553016f1c782cbdf505fe8520b924f6d7574394b3bdc3b0734ca8ddd85183cdfeae42b60b4d8fe54303ee2d369c6d1faf539c194dc2bfe0652ca395f7b8b8d6493d5efa117e990d47368f417cbb219dcbf5e8ded6e252c0bbc4d25485705136d017dcd54da69832a2eeba5c9b95cdd24a2faae543d9b48097bfc26598dc19a44410c9fc550c81475d0aad3dd3b57046a7b8e4a1664493605854b091280acfdf0ea45dade294e843afc1eab373c45d44a2a7e8cf50273c008d270fb42a8c85e99cc7c04c631f5f784582b27b0e500bad48f6faf9173caea033ddf2269e6d08b9e228279bf77ce7ee597e838abf73fd5d4f30b67215f9fc406816d76174974ad7d9079cd22ade3b8f8999084d7e5cf088e4f2e699ffbd11704a8f610318e7ca6dcf4caa241a6650f9146d13a7440405682d2422a9c7c03de04f38692e460096e2770c20967b2075b4adba508cd872128acb869d12528b91b0752965cd9a8756c5c994bd2759695eade0745b9fe16bb3470ebe68687a8bd49062a78f0bf36e46c99205374a21cde0fe9c1d1613354546e4a9aa9c0fd9e03b30f8888fd33152a35693d5c8e1a9f87587885058b1c8b02934baba31d06cb62720263bee8d91f98e2cf5daeaa95cf572024ea9ee551083c88bd5dd7cb26b3b52403bd17043722db40515a966dd2bff59698ae17a92b35e12b8df651d6801bd28d0d242718a2915569874fa517abbd2ac08098fa3f17484c146a8b2fd5972ae9e50e7631612788326ebf00a7e523b72e02b570e7b2c1bd01878fd2705760c4b3e902614f0d004943ed7a36d9cf58224e8ac2c3783cd4db22674815af9d0eac2a860f398dbdb8037e3df5c43830998287e5393bac6cbdb852087934725dbabbc931034dde1d50549434ee6b3ef41770f67fba6313b970155a8a8f58c474136e896f005ccfb28127af4ccf76a42190e2f4a30c891bf29a42263f265728b7a6523d6b0f6c7bbdf1a9a3bdf7a3c252c9b591060399aec1598fed9dfdfb6581e37b72990f589162219d1a5d8acf4ea8ff42b168e3919cee23ca365a6f5060c2fabf75240b7dae95b17b4b3dd58e91aa05ebf1da0d99ad5b9813278d3b9e3caf40288daa0d34302f040ac7db9f659e13593661ff7568218f60c26cf00c36b979743ecd2803cbb9bcc007cb4042dba333f23db423c0f31e1f3e5ad34a6041fc4a0b6bb2110c9e76530befb7bce295cd79d3226314fc148aec202da8d7992d6c058f6fc4d90ba411f5ef862e9127ddf48c42c27464d5aac0aaabd127f774f218111a8c308902ca0c0f4f7e2006097bc189fa3b6fcb4b7c75cdb7910c9d72af30cb034a7ed2076b223716865f71d2a54c362ed2086ec2ddbdfe8953467daccbcc9337d9e42c00d63252c8be531070b638b5d2ff2ae2fd307d481d7b679c14bc1f862f26ecb302c56620df8ed8b24c031f8806918b2e5dc83f82f2bc8604d62039219e527b4f0bafea9ab8f880bc36aaff06649fdbe1a84baa029362bf6112d69022c3bb4a40045f2cc48aeb932ea562742eef2ff63a5a8213c365acde7e59db54f48c6dc0c50cbd04c187e68661400719187095e8ebf509c8caf823a9d0cc932fc267025bf4071488645280c04ff840361366fe3110f0a765b03306ca84a51a30cfa0f722fe0b661002a6e210783b9cb4929466ec6f72749137f4b88b6952943228e4ba7e75007e6d472952dc010460c52fd0ef1c5909cd625733f6495fc3c2a99441aad4e90fb78c88850708079e1b8467efe3f38791e3e6e494e7b7d52fb6b0c6f89859e70bec7621d076e3849a527b5faf963f1e19cd3a53e6a5b0f1d03e8ddba6f08cce056f14b474ebef3e7473ebea0b078a4481679669b54da1c15ad1245df986857404a73c2e8ac84408272110efdeca8aa91c394ee911535228eb9d2ebae9cf2baf0ce19baa2b708c009bb8beacc220cd91e2848da73cf6debc8714572e76063b05020f72e4eb4e9a991b8d0a1ecb30955e4342e026910ac49c4885ba75039e01400f5b9e012af76691e0dfd584e14e2f534e209877bebffda30f66948713f2516a0f5cbb65c96db3481cfa7eb35f9170d068d81129a24b82cda3c4c04ce76a3195067bb653df0c40741cdb45b76e33d842061b64c4141416f5af7e78f391fe90e402a864419bb8a128afd13c2eb82b4d303cfe7931e7acbfcb60cd88141c08c2bc047481cd80a1209a2aff8cecc9ce26e1f51509b2af8c54b9f6322553d36317ff0f270c0f4675aed8064f88b5b40636ec95090c32605adb0f1a0d67b951f16f0f06db419d6dcdadfd0284d080623ed5cb70038086dfafe87278683f3b92ffbfc1094567ef31d92ee9ad621052bab4cb36b3e7abc242b844d391aa06832973d5d10b3be7e386257c2d98d5b838af5d8a9decfc07c2b46bdc8beb4d4972f69b8140013a589f32386732af2917d8e367b18f3ddf73722727b72e927ba2fb74c4bd3d0bdb36a7d302f6f2cc1b849d36bc1291c1deb3a68c51fce69f8bc1a87e6192ba01af34971c4c031c662a2c6d4552d1ff849b6abd49d0272dd1367844ad73994203d1946e548e369f6c6f5258a117c8a6ace7cc5c2474ab80b33f6508e16d42b00d -generate_ring_signature 511e40c694c62adccd1cd78dc759ad6f7e6dfc6f65c233300edd092548e6837b 1f100b5863c7a8cecbca53896f81bfb37129724d1ac0482da5e3c041ff57c7dc 1 25fe770727b3555d648a78a3e962b1bf25f9733c0fffed8837ced7e2b3214a07 805b31538ebb4de57afbd5abba894511ef846fa6c94906865803092d7302fe0a 0 959d747113240dad99f5ba2b9b2734587e1af86df7006b417f6a719fafec2c0a3b20635db2190709f935f5fd6f6e5b7d7031d81cad4af84a16b2fd8e13d4a000 -generate_ring_signature fd1acd2eea26d5ea5f10a0b1e26b68e4e89fb9760cb610bcee9a0e6903a25904 984b195e9d1b1019e51a1a00ef3fead39de4a03464838690084b5b1f5b49eb4a 26 bed2706f07b60e6e4ffa986002009646f5ea5fabd1b164bab9644b690893d9e6 a4fb36914b96a15fd407598ec221d6d6a06bad7c21bfd0079e91db6b64048f91 ac5d1a9778bcb740b0a04211516231df150d714a929520183c6dd54153b07eb0 c82a344033f5709ae3398a87d909ae637b24c0b355d25b76a04fd436c0c01488 56bfdeae80cd043158fd30b06ebc4605b4dcafcdf90db2982f1cfeb7e5494ddb f6d0da924cabf77083bc07058088dd8c4074e33c1839d7137b59cd665ed18637 d4897cf986fc87998d75784803bf73a1e9854612b9cabb0d5a91754a9587c83a d0261ce00fb5edefe2cfc4b2f1152a5802e64bd8bed7ca4d5ce7ae8e6be671ad d5d7d51abe48bb01095b73590a322c95d0c2d8aac5a30cd9c071d42fd222bca9 f6fdb08161059d27e267b29730e633e405365951a79a9af34e64b2831510bc5a 5dc9b05fb96aff4ba4d6589ad93d086044a0e6483528706bd65550ed154657e6 a7766d3acd6e9abc9c2925c3d1060214894f6b084d42907bcd8ce486a100348c d681d22b74d9caab9d5f8a39c2072a899fe687e74c04beef4dc50c856eb18407 45151bebf7146e8115b238efdd4a62e9902d8ec3b2d17bee96e177e6d7aa19ed 9f0f1064920ea54f315688deb2138113e19a1ca6ce6401fe778a01749a2a8736 e862feb485334845db565de4af0b91d08cb2e4488553054918a4fcc5cdb99ea7 c07283ca458cd202e47d486a82345801fd8351b6d511a7bb5f4ada80d3c6109a 7ba6f60b39d016bf3ab7752a09d9a9bd72b34af8dfa098fdb1f75e565e5323d4 75a784cfc3245741caf9e5d074b516bdcecc8e27784230f92084850460c6986a 1b852eb3b1840382e80d8beb354567e7d6ac15e5abf7e450358b70f067388d68 9b06b82abcd972ffd5bb38d7c6bda7a4e50e79350d0d22e9ba34231db2c69d6c 0a65a8633f5152b3b3514e838be2c0a6cb63c686f3be5163044812ba73372779 e535860453422af9b12fad4a50fcb1c233b303c4c1cd254923b7594ec0e1e95f 5c70df9727b368bcdf48d910dd9bbe2d113d468e22eddd03bf268c6194f5e46e 934174a3b129c761d997d0df693377e44c5303aeb2036e6b26fb0b6f7c688146 a3a8c856c73b2c20025a9db3d15ed505a5a3cb453c7dd31f318a76fe5098f59f e7def0f0562dfcdf304d8ee8b7483c1292e92a39a2b6616e0763719aeeb4c10f 9 c6822c67eb2249f3a442dbdc2633297b11f2fa073bb076086c0ae5344b40d6093277f11ac09b6775117872f9bd66ea450b148effe47d8a3d4f28f6a252d8860db528078987e3a16da014a935a111c73cfeb23bd53b6740501f862359fd43090bcaa76038c8e479d9e4cb303c4fbcfa06ba26b53a035dae3ae36fa6efa1b8a9005ec1b16f9d3f17dcec0a089ebaf66c41e335bd4469aa3369d95a7277f5388c0d2b28ba53c01d7deb416d950646221f976bb29c3992d0ba9bd4f0267dad66db016e79123c8982509f46e1d72694f45cc0678ea1fe396db43a715e0f7883cbaa0450af1a7f190fa8844a7ba235dfe4d01b5571c07111cf491d5f2801b83b4e370bc3aab84513bf9cdc84bfa6cdc7af852054ae9fa8b2ef1776729246059e18b902341b373e955ea3127d0d5461895e6f4c7c35cbc86f4921066fa62a59d3f00c0d6129a8b39b45bcd3fa0e4ec29a5f0a2440ae7592518e5464068744e3ef547c0ad2b9ebe218f5f8cebe2ffe16a98c335bf7e09367ce4f7e38f606223d9179280a124af3437f031d41bdb44e70f5a1c8dd14467aa0742ae0a12c55dae83a39a80bb3bb951bbd367bbbfeebf304349dd228844ff9dae04a4e8a359cb9eb89084e0885b1164ba7f5393ae319ef93caee9891ce3f3aec1d5edf2f57315d0f06c8790357ae1b362c443729ea1f990a1549bce040de2c8e94dbb369853a412ef37794032a1538cb8ee4f637786af56d3e1f2998f15f1076b7dee2b55ebce5bd1f026801d4c12c5515a2600b11778af74641174dfddf99c3a0acf1809fd63078dad8d60fa6598b0cb1f74765ddf46a9f29fc2d99e6dc2f1c9d8e5b73d3c5f6a3970699093be2c69f94ae2d73ab58fe8a136bb7d3800465e525486bb85b34f43ebb9897084f4289c4bdc663c62f00349a22eb9e80586ceda2fd20378082f12d54a92d2301023833fb85903e1e376d45a1df47f94cb33865e23bf120b88faafffa5b76b30332c0b1fcc7a76d735d33104d4ceb00b3ff5390b1283e9f9bfee5300200a4d706bd8e03f46373fb9fff891decfb39d58905f82fc53124275ad2719eb121814402cce6dc9d62af0cd1e78483e4a6dbf72f6aee3da68329210713500853900f0a0660bf007998ceac9cbfaf6cc3c93acbef21ac3e84522b4177262be97f4b7fbe0a4fb4ab9f79f84c214b6e0f306e51abf05763323635ba6c3ad407641d4cc0180dbb6340d0e7c7ffe42f64ddbf87fab6880b044a38a7f0f49ede342fa6622c5e039c127a2a115a35aca8bac60fb060dadfc7f68000582baa638a861e5f7dbac50a5db8503d00cd91662bfb55714701d38d812d88e091c6b63ecbffb866b7e798001f4f3aec6b3713781b99527b36871e5520e87a6c0109e3adf92a7189d301a40a7894cde0b740db61d37e53fab2adb066c00a62500231d2a3a47a6c053da69e0c234847784ddbfc80603b16d1463b7226d3b61f457baf9ea0521b09f92f350f088ad7ba2d43507c70ce1126af4883e7d43876cb701968818c448b4f65f8cc150cdbaaeeb5b3ef7164c6c5954ef8dcd9a553b86aa4c4676685304f2fc45707010a50b2b431c3ca8a390e54aa03c6a593c5def5cc821f1e66a569957f5a68ae390f7bcae0e4037afe17e17a02e19fd80a199b167594c1aa50d0a8447b0318c74806e8f9315b78fdaaf486a03a7edd1efe9c309c63de886afab3accf5e3b5ed03d0d7d8fe554623e2a4df5f22f7ec9bc22f62a0b2a716568749a7ad3e2b7a7c01e05831ab5a8811a11de433b39fefea883eeb995eba8e00ce4ae308c6fff7251f20fbeec3d1a72219b74bfa609fbca57b6af8d553b8e823141e3bccaf76f553e9904402879f25c086fbbde800f4477b8501312b3c7c8b9581fd805508dd6ebe4ac00dea99795b05efc8886a02dc69a5f44bf4ee87a9ac851dd4b572b6aaa5a996c0784b33e066abc1d5fb6f1f07c5424b782764b3507735cc9d4af89a534a57cda0573fc73b32d1b2d3d1d2f3c072db3b43fae0573b4513f11912411c1d41bdd4b0af0987aeba25237b47c5cc352747906fcfe8ad1a563d6a4026006c5101d1e4c0786f1dfb9bba6356b8dd01b05bb40a61b29b4579fc92d92143727548f980d05004690d4c4f3542503bf483b122fe98b98a105bb9e09a49756b62dc3ebe029ee0cb83626a67cbe182476f78411e8b20fcdbf4f5756e0e608cc7590e0dac0e17d066237f3845f78fea87a312b6f6b6f518bdbaa014586fab04d0f400241884d880ee215ba9e24b20d6cdec807d21d72ba39308aa2313bce08b0550894de2589330d1f6b31d8dd215cf7682605026da9589b10913996da5ce613de86228dc20f5a03 -generate_ring_signature 5a353b51a4b8e73c0ea6abc2c657c04a77fdff66fc6725ef72f3ad627f1d475c c9900fb41f580b8dbfe112df57f0c06e1ce7fb0130751cd61c16c51120cf2805 31 2082c30f4fb72d624bb617a6a03dd887a65bc0f54d266caf486fbac1aae5442a 0eda2f4ef08a2c05146633a297314ad90dd695da99f4f765c18c22c0c808812c 749d789d269f4a8d46b4af5ddb19cf5ee7f86c54e0c08c6d93691d96a1fb348d edd996b1ac97ae752bb02bdd19976097ddcba82f6039f8115498fbe93cfc09fc b7123f37c63a6ba41ec9b4868113d6f66cbbbaa8d80427ee6964172265fc2e8b bf64bcfb1fd3669ac6664c3e9352e5f30ed3060fd80c97bf43105bb062b9beee 991ec7c190a8f7b9accadd7c2b7ed031fe9c579f343499814c4c4d898855a3f5 420ef41f473736a88f14d32ff6416ee906a4bd91c3a573c9299a4f1c054471f3 236a9c9deb53c00f8668443a9515510ab1e9b968f4be278a94c3bd17c1bf06e8 0d96fc77c34ffa4ef13b0de637055983224a12c1dcd206b283c797aab5e7127b f11026dc54d653154b6f4a9c29d188e9c8d4633605e689426cc3fea64d70b114 04b312d50cda555c47641f109b9f4c6778d9a7c52e2ebd57bbbbd32c459b5c96 b1ba5c22e1a25e62c099f43f3e62f3bd082fe17b2e0334d30b45bddb8290fda6 b69eb8187d101ca4ebb8d84c72592d77f57d04943f3ed6ce93f56bc370b915d8 a0c257d7c690866b100211a0555956726db6192ef9ddb6569b0a452e7764b0ee 99cd8de3617f2f157fcf318c43e96e37df9ed7303c27b039bad46a8ee337b684 e929eebe671159239cb71bd63468e94a2cb90b756400a389b49376f84aa269ce 8f56d7e009725f04ea1ac3521329a1572dbce6fa2098cbe4ed7132fa5ccea342 47e13fc70a64213fe9d3278cd08ca6aeb844bbaf556df1c57d927c30f6d2c75e e4632e8650a1bd477c11dfb50e3c48c77c0bb5ffbcc2de1c41ea85ef50214b7f c8d0e4cac23ffa3b13127de2c01f885d110620b899d6d1ed4fa014c809642284 33873c867e6a07e795638df98011dae680049a28824194c4c7d5924d0f7bc18a 7930cbe3c49a2067ca5a88383454ada7c1529ac75665be86acc0906a63d0b84a 803714d531449220c83f1582d3a0b96462e6a1c4fac0f73bf486fbecfd99a216 6ab2e19ea796cd40a1973def711a3c370edd3afbc452afec3a27375c468232a5 5f58473e62a5a53727b3556b0d537a5210c77effa6ef4c00a00e6ab328f29133 228455d4ae9f1cb40781b45e6bbb26562bc01fa91b81f16f46e018f299454b57 5ebd456bc28a81eb4399fbce59a8c8ce0d765543e7d1dc876ccf2401da38e483 edb57f628c6675f649fb2947c2b7abb9504e00aac31d3be494b258daffff3935 44d94d6c395ebbd88bd008d0ff9802d38e06bac61a71f3afd0c1d6b75f5517cc db1c12682ce582aecb3ae22f6040d3c804d0d000de0b051c8739587850534ecc fa0835dfecc3c5213938d2abc5b71467e1412c22cb0cd2c42011a03f15af9f0f 3 c37c7fb6e87a004b2f619e89b682dd2b0fcddaf4e2b1ddc7374e49ad2236d00300806a887ccbb198f7a5d483fb2fab1e76d5367e4139c0830bcf343c808dd3043560c44abde6517a4d19f2ad0c34a21e4b442a002e3e3de01b1ef2ac5921030d44e36711cdcd8696af838815455e49a852a8e48ce6012abdc89214f555a974013a9e625cd686887a071428caa54f7e41dec52190ab3e76372064ed35982b5a06c562db689e42e295ddad75ef892aa9589ff69f01a537d0cd33c06726e7272f090c71d0c55b9246994fb82cf541de4c0d7da069d9f9c8f70c33d61c175e2ba301624c56203d1f65ead50c8c8ab962eb750df0dfbe16121bebaa3b5b5c633b8807478d594e8639e56e4fe1da783bb733a74f5a27027c85193c0ee2aa5248a5590c96ae810ba38cc3997649dc90be361bdb84e711af41091a9dc8a3536657b71500679c30461f74b4246b0e18d00c0e0526bcef750a3b4fe50c87e6414484c7280c12e3530b236684545f3596c0b32bf83efed4ae47c209f6f83c257c7d787d080a474dbae2c47d543fbb0ceca72dc7cb1eb0271a6b4ab1f38304df705d76f2330e199dab48d4c985de0b3f85de47e09e0dbe95b4e2d1beb1bde359f59253bec50fa4e389b5345611f537aa154140c33f86c306b7250406f57261e194eedc5f2f0c8e3735a618ce013f0158ea52d2aa0415427f83f1e1251882fe6a6bad068cd10c4905eef24a059aff58b51b87f3280417aabcf15c3df498c1587e7d20da0e3e0003528982d52fb5585c5c2ff24c974d5b4c358a3f472c366132aa10c9b342fd0d089bb3ab6c161ad99081d05a84da9654b43bf20f012e4df7a8fe7b352abbbf0834910b6736160ab726a10216d06988610bfe43a7eb48051f348f819730730f0687f9b960b4b956411416c0e97a950fd0b556296520d480c8dd5454fdae9f9804ee643c30d81a88f34a48c1704f4a993e371edcdc194e0b69022d014b87849e06400056cf375c4e9c4c62400653cd27b2f21dd1d2e41ed7f4bbf7ba1455a9c200acdeb86611cea04de14cb3004e53148bc5c9f195e027b2b0e6e5fbeac886ba05d823e57d600e6cefff70d38aaa02e0231326386f910904551535319bd249b5081b4592d5576ff1735ee7af8c7388aa5bd96709350ffba4f25195297a706601067cbfb228af229e363b0883f4cf87da258af323a14e007cd050f82ec0abe8030be3c5ba50c52f7e729aee539a73ae989137caa58fbc900495c0aa5a8a72b3c4044c153c7d659c440d8986bea7c03a62b83006d55ace4ea136dda030cbb94f7308c02aa29d52a558f3e798fd45c6c1a9ccf731382afd37ff62dec5066c01535b06f34ca8f726b18295a1c077d1831e51ac0b20ef7cffbc5b84f3d5a3f6c58ae7033924258dd46d6c86e469006076c5c100407c80efa4ad693bc76fc17b44ad620df51054cb47037e7cb01838eb585cdf4edfc79504de2a7dd98ee7dae95071300be73430d5b23cf0bd463aba726e2c05919e8fb8b736063164fde649fe5a5e1c0d2faa33c957418c9e6cd229bbdc581f149c5a48e53b904abebfdabfbe35d4e9017f2af353a51b6b9694ead1dce4e0050c871f22183281e9508a1633b873ba4802e6cb728d118264d848fedf0b6e0fd32cef9f4c9dbcff7e46f7cc1477419d8a0ec097a9e2aff6586a7eb9704d931de04b4f19fb2ee125071357cdd18b97ac3c0badac0b1f35dc882ac42cb94ce34725d27af65876e042d9e25a1cfcff0479540fa9fd42b736a94371fdf4058cf5425108f5e3e4c3fc14f28a0fecff1608367000907aba24099bb53285d235bd7dd24ee976904dd301733fcbc0a0f9dc4db79607dbaf2a06f9a48248cd59ef6a29bf829ce8eb5e58fe78da2cdb8f00ab96def10d95e38e3b5a105ef1d4092329d7d56cf6c16a25fc3644aa72e52fab26ba6ccb0daff69b73eeb0a3b1f315b52961e865b9be70ff95c38a18d73dc86b6771dfca029132a46cda910a4826a3142e72a1aecae7646d241139c2ae99c63a6f094b72003dc42510ef14d0c3c0c1dfa901b9e6871d97bafaebfaf529be2308f3e36cb105575f85c40eec91f18823b92e5079d3ec5c15a8fc7179ef282c6cec79fb9c8c0eae6a5ff61aec22889b283ce3372e73509dbf156db41987cba704e4ea30de8f08063d4d774cc494c204e570eccac5706a05ea33960a8f6fdcb98f4fac9cd16e0cff82ce221fa5f2cdb6ed8b1b368cdffd6632b612d4e6361cd3109d84be576f08d5fe4f350fde798530a61dd297d75422694f0045f6eb4c6807cce283de4c8a04c5092e22ab8d0586c5e2482917370bb1106094dc3a21c2df7357f45c4a06a305cc9ca5641760cbf7b7320d3d353362c71b05f171f510cc3235862be20f19110196e97a157077da3c35d25ac91ff452d9855874b4c7694189e6765e70017dd40f9d86592dd045301c51fabcad41d3c0b43ce137092ae87eb7edb32c9e8542be01a2df9b2d0a3bd8610d75eb90adddcdcba1b6f4450fc149dc01ce57cdcd00e8026e8f019fbb00badac170329fe83306ac7d244963ff31bd2b7169436c384a960b2475e50f2678ffe9e0143374f8e29b2d9475e25c638301fc2daa72a5e506210660eb7e094078aec0b907a4e8be2b84b4a51c1357593bf7cc94c24de5b21b020aee576c484d1d340d0c7dc94bf02ee222397b9e933be7bcbfbd9227b3fea6820244e5f6918868486a070eaad080ff566c18fd7c40de6e15cfabacaf2f32977d06cd807d306adae9f1005e441b80bd2b5afb8a5536397e2ec269ffc9646889df00 -generate_ring_signature 6b0442ce1572d31322392813b8f2796600e1bd3a7f311cfcd5d18db158df3f83 005e98bbb03be89de4e329438452ac2466eb114dea37227d0583067af043e361 26 d753796ecd8caefcdb6d6eaa0b9f198541cad7e68ac70b436a7c5fa4db496797 6ae845a62d710002eb816996c34cfc5534d1323ec9b664c20151c4571617185d faae0348d8ea4771b6855852bb5fcc60d992a0fd9ccbb4418f9f205fb3eae956 c51b9891e997455b4b220b2f1b57dd8cac0e35489e9f8156cbac6b7792a35f42 eacb4149226b18cf68ef4cf0bf21d09d3e094002482accc00b213d0c32010d25 da150b24184ad0285d59eade87dcb51e73222301f46a0b8cff4007576858e11b 731a7cfd4b0bfc6824e2b5cb6e3fd66a77981d707259a9d2dd332168310e0128 b8ed9d7e6d9db63cd5dd9f5effb80843648c18a5c63c32b67ef6232240707fcc e611578ee243322c9d2e3210f57abd215388bb1e79319fccea9944dc8640e294 410acc54a3f9762e069471c4686a7e9e3e92c61811900962d6ca2400188a6ddb 87d32d08f2338c3da64bfbdba28f6b75d46dc4ad7fa2f018f033d09b608871b1 bd0219ef9562064deaed5e8d494ab9a22c32f468c5573258d6f8963c5402c62d a56e1100ea7ada74f25f46b738a3101732b7fb2a6a24e860fc42a122cb295603 b5c1a4655e455a01e68e3f6002e6aeaf2ce8e84d610c5e4542fe2d3100a01df6 6d324750ad63ef0f6be7ba47418601b0a5b5c1317d151a9449c53d27425131fa 46deeb14d5108ae9456f2b8375dee122dd93f8c03fb326866e6409586b44c7a0 ff9057e3940c9016f2dc47f986425e3d4f7f751ef2f54ee3aa1aae85a2f32460 9598db5a457095e3d02ea5c1ea0c56f8d43a2febcc64a089907219992cc8e4e5 afba70859ebbf598a74ae65fcd63fce51abdd43b5b6485bc8dcf9a2eeff66d7f f0d872a622b64de9d6f5db16745b7b7dcbce78595217317b73637e9c253cced1 c244cc03d464699ce9f3c9fcebc80a8131104d03f2fff938dfa1f76dcaeb6ec2 ca824fa57901f10a1ad1fa6c51a444064870adb920046153246b60176229569c 2ca019b674ed3157c84475298abad0f6b62dc1cda2a11a92fab49f9743567a24 68903516c9127c4e405d1583738f2329755d88724995901269931006aed07600 0f8e7fb6d91889ad5a0aa80048a6a1d941f666e2f5c9ef5b322bd3729de419a9 6f87fe92f681328fca521963539878521e1549077f5851e04b200e3e5d9859fb e4887420585b1f8449d75da7de40507abcae71e73652b880ecfab2d43a9fad0f 20 f759dd4dfc3335ba9f866f118c4c60af808bb6ecc6fd03ee05efbfd5a867b90573ea38606265a5c902b961ebe0cdbe99d8c22c014d8a641a4b8b807eb7cede0bd7bcd41af4ac0e3fc4925ef0aa7a34181ec477d55ceb294d91e647cd35f0e0038419102bb6b448c89e66512e700a1a979876f65b5c409a97878f4f54d6f8490fdf211c368cc4d27d905f80926e52afd618beeffd666e8b82ba957a0745c95b02939ef192eaf2e1462a8f785d7978945f0b6b66792fd1c7104339a044ae94ef055cf84f84b4f2d2ac397550122800ebd533b904b861587fdd7a7ed21ba1ce950185031d63371483c4a3b725f685a004c6c50d248a70c84aad172adeb42312da01b9f19df0fff7071223422e86494cf0d3c6b32dfbdbe9b5e44342d1b1a05af70ddf792526495e999f6830d95f5fb1c042e6c3ab8f3281e46e94635846f5fd1c03714448126c5096a9b0a16b35c2999d0e35bf1aad48428967669160654038fe0b50972a6e4f9327950ed21c6daaa3163695c909f83faa4c72766f10d5e89c0d0933ddb7079d20a3ef2f69c19478352a34c07c5271aaf22dd646491b6e7339420c6ad94ea0f676ad968077e30950d4d67b88ce7232236b4a5817191623cff2080f85d72b67cd24a5b1f1dcf7b201ea7e7737587f67266a2f224e50e40d169aa10b055f7bb7b0c742b07fe62b1d72da02e667fa5ae60bc27a5c93ff4a7fac1ee00137c17561e4c1741c7403b5352999d560737003c3c6d7529957bd669126ea950e0021bf7b8396f7bc845ea2c88d41503bb8e5a73550ce30359c6c1208fcfca40e9e53b8bcec5912304979ddc9a4483a0393941c3324b01bcb175bf9567fc59c0ddc66d9b9c3eaf9615d0160428a6151320675e104b047be7e4d20739758704d0a7541543d0f86a5da0e42bb18a7248e3413b0e51a184291b2226de651ecfd420db854df5273bfebbece2e31741311008c3540af4c1e157d0a09d65ae3ee6e5c06436b2d82e8c8a348bec14601db213fa843c43887576b1655b8f522c326f9a60c1feeea5cbd128c8397cdf3c63f97b843a02dd368de1f919a6c7534d0d21bbe075099dff3aa75ec9474646a6883da266a550fa9b439cf4d1b9313ed3242875407e7c954fcc807151d6d16ccd83fb5ac8eeab8ae43af5768c73442700616e04900e04b85325eaa695071cad0b0c8b685e053a06edd4ed8d82e921b6a6e6ae92909404f36a771f95ea10e3910e5da87a79bdd09f68c42f2ab581a79114023d64908d3e2f95c81ecff1bec33dbc54adb8ab66c1bf1ad9b4def708d077623462c5d0f56298adaeb0066c19cdd2ab8a3c12a01791091690e50d0a39087ee09856bfb066726258fcfe26d43046fcaddac4880ed85d2e4e125586474c43553620b37d10a3e81f6137a17356ca613263a387c1060a855f175678ced2e1b2f0487e9d4a00300aade217e03de51a135f0f5678f32e0307027a02fc8ad219a29e86c495f0a0a282a92d4cd6358ae229073e6164481451b48fd5820ad04637d48f23d8cb346069af074eb9a672d4fe5177270bb64646dc24c3ec03aca505f8a9cc4bb21e8590cfe66f8f59d313cf86bed554bd8c8b63f06b8e92bea191cc37a8339fccb2e51051632f84c0f9870d2968b9ec55bbf06c566186ab36bc43122340c99d74a07700b512718a6f701f1b172eb6cc44435a2d4120c66282733fa7a22e116391e51ab06ed314b106cac92ac73b7bef6962d156e7f9ec23cd2b241536fd8dcc8138baa0b11e3044a60fc69f10523ffb84ae1a435a583ac3e028c588edbdb75e138f03504139c5f9cfe2c9973f1300b4353a1480e26e2f9728b45a0487a441e0f8168d30eb4380473998f635109eadbf6812325f1e897f5cad7364ccb5088790a54343b0a6271ffb7e88d9ec23873c16fcc1d92798235496f182b2745bd4df1028074840e06a1d443907f515d834f79fab8082273ffca1c8703706ec07302d4a1dc5b6901c3579ef56cb563c8329bcc739005bd7d057a2e6ffef889e543d6d20c0c69ba05a907c05bd2ef258f61649daec2202b531104fb1193431d8f12b6464ac5cf520b809f0b496e0e34308a65a25585cd8d4eeb6dcb7d73b3389464862fc9164f48092f74b81a2c99a57f306055f8bb8338b82627b19c35877cd578ffa6447564c704912b88dc9be5fd8703cf96ff3643c7c8334d61f89077f62eae126d8a69c0b406298cef11bd378804830e958ceca623ad97172d5f2ca7b5353eeb6c2d9cff0f067d1c7c29b5fbc0fba774220384a7009e7f8ca0e8db8b7bf73992afedb534080ea6df6a7b0d1e71145a7117a5e16f607d132f2c2512c3436580e809c45f797b0f -generate_ring_signature fa878e98be7bf61dc3b35153e4ab01e1c444b109729d4428288fa17dd1277318 c711a0a27fef0f9531030c710cc93fcd675120c2b44fda635fc3fe58ce68d5ac 2 8705fbfb8783ab5987596b73b4d1ff0156795c951e0c58b8091724678d9d4174 aeccdc9ec7f198a2f4824e33ed96d40215f9b22fd69cd2f0f84546093fa44c67 128378ed23f8edc0dd5305fa3e39996706a4c77d63b5ffda8b294b057c3c580e 0 111247f22213a60f396a26e60768d4772c2c674fc227efca69f0a9ea88586a044e59bcc34a4e844dd232d9bca065653294b347e78156207e54a8531efbc73c07b65d3a9e6ec44fbde5ff8bd520b9b29cc572e413a428f9005cad9df8183f0601f23537d7c3054c51e4a95cab5acc4f1aec7db14b6febb75e3266367d5d596e0d -generate_ring_signature 4dc38ce91c25bd6cb03ed5c0900c0b3a3d171ecd3f441d8bf38218214082d784 4279f878215bf5455ef8c99444b6a8cf0484d54010b0d1e8fe7adfe97af85f10 114 bd939bb5f29b32bd0b9b884328cb04da64b7a77b9d0f1975a42d74c06222906b bd64c1eef3f01b60b4c60765795c12d9d1e67df40fee313bc7b0cd7d769bb669 85917e8366e4bb6557e7e3d6d0851f0bb72cdb2f5605cf80287dc503333cb4ff ae20b8d284cc742818c38e284160f244b157b868fbb088f6125b8d039623d282 67676a9bcf5d35194deed9e14a41c23c81f5e96a82ec4aa28d9c54574c27cae1 c57610fd10ed400b939d00134aede8e719c8e4a8abcd18ff1b32f7fc96b9c937 494be2c5d57bab3e0e2df1715d4ab09912124a8eb65d44745b6ed4bd50718f10 d89243a7661e6cf3d7ef223ad3eac26916c71f9337e02d56db8bb710a41dca38 bedd21b55f596d4fc3ca8577ab48af6d0e4caf878bdbc81c61368799da91b655 c18ae2c77b4f87e07ed8564e9eec44a7fa841b9deaf425ea1be3ea7c800cfb94 789dbe3cb82cb86630f62da491b353336731b43ecacd9d76841917db6e265b50 d983f5d7e4980efdc335a6eef84da9d194aea20eadaa2b1cb2a2dcf6e510cb24 3fe08f0aa6a0bd5ffacc337d8cfb3ffa5ed8b1ad2717d0d51b929d9a33806790 686623a9f75270dacfeb22784afb28c0fea864434b32fc76a7c7908c378f1e8a eb2af211b6ec6d3a7583c0a264a4de602ebef841008dca26e06a659e933994f5 1e02ac91f995e252099050a2f9bf0a107500f689fc785f1f9d221deff9354e50 526b8e6185f40078ccdf886172921d276549dfbdd3a0cd27fc65f172d3bdc2a0 8e3d39b85ed3a05da76c47c8f70b76020002823548f8d45fdc27df8e6852af9d dc0915a34b7411206b4694438d629c17f84c2c07aa745284b1e84133765b88b2 6f1070b084301889189f7db4aefba51bbf037bee8dbdb086ddba9e9fa316502d 8cc4b03df9b06722f19ee747f5e4adb7b6a5ba3f9473bb33e98f06afca0155ff 7913b5b2a3371c554ee75295492f120e4fcc1761a2e90c163800e0bc8566530f 62a6ae54a6ad56bf2cc867132bdc8ed65858367d7ad38ffba12b25bc2f44a923 afd37d8701510ff65f36a4089d28a116e923640d83cd013c9ce2bd232f689cf6 d2b980b075ad35c7080194272558a004747fa3d4f54d52e54d7eb15a55fe47b5 09dde581825bb3e27bf96127461b67b50bb5258077ab5773544f29fdaa4cc48a d5e555016be782538646c00925362d280850ecf85d9c1975254025187739a0b4 099ec3a3c0b2448c583d9279b8df8d6f9d95a524d7ce35e5ad796619559a3c6a b1785018039ed1f0e47624cb17cba05a29daa6af97847e985c77b5de9e2f90b6 4d1d4f2af470f9e5c21fdf9985312b217dc866cd3c99798322d4332a75ed3f83 61bac09466cc764cd2549229e63b21ec0e00125211c18d2e784c027edd279198 e401492fd7498a1281617bbb9750fa4dea2f7ac1c05d4601d7b4d6f93f05e05b 4b50fa467dd7bf98cfdfa98b87a924aa5122b6b09fa19e2c6b1aa82e4f109d71 e4c8c681e9f7f7f0607ba801fa44816a802e81e8fc42faa343d0c0ebfe8e3e78 8a0e62086feaa8a6259ce3a21c4063fd4cc807c1beb371372f45e7aef1493123 fa7a5f9e71d90eb5ba18bdb7a9337243ba0d8735c4721bb9b65be880ac4ec6e7 8c89e1ad8f42ee19016d782eb7489ccb70b72be6ed9bc3861647bd742fba528f 339678b140b4c32ab322905eec017d40d039ed7e02c9ae57c68d2fa7e8584626 6c2ceb1dfd2d66f045bd54bfd7e5908a57bdd5216b7b17ea808e479c0e4d7d22 f36e853ea751ae307fdbe0837663f823106aded803139620a9cbf8d65d85c1ca 1514c9820ad846074be5e94a1a460efdd832703bab36dad44b04f6723e6b983f 4734b3ad5971011990b5ef237b152c24a493f304b337f556afba97607746fd81 8cb350acbb7d6e8c4da9dd936645a8750b052a29414c450a9bc886f1ee58bc9b 90f095ac9e66ecd8c321797c8cac0b6ead2328db02968466cb2a813a14b97b63 b6ca5b532fb55b28dd5788905f16a4c017617e45bd9b59ddb916a910c004c906 2acfe7532da865b1eb9f8ac6d344f535c2dc9e6f38be192986a02ff8968af635 e656d87021f86642a2f147b58869913e1721e8c8320ab77acdf32775ae4b816d 45847b41ac23724e860ede8d7fabed5982702ce646484b7c008a603b48b08a4d 6d0c71b58f122a7c9e6f4a71c0a8ef6ab1d598d67118486fb704c4d1fa8adc99 abc2b523b9f0111245bfa413450546125e29293df013aaf0783cfda5db038302 8649564fc912d27de342dec94c8dee22da9d5953045463bcecd0cf111a2e19e4 7ebc13266c2d167736b1fe30b6447f51d915a09cedccaf68726bc2dfde668816 dfe2f28066a7b2489f85153ad3fe8c42301efc3792e55764241f0377b1df59ae 53337434326613e0a7e86c87effea7123084637fd9ec488d6a5cf79678418d71 2fd4edb314b9ed004f702e97673312cd247ca6cec9dfdcae83ad266cf70d70ef 63627d0e850e46d81bcc136ad2aa095ad1a451f2d40d0954b5b9a99009c9c723 22d3231a1d7e59481188324c8a300004065010feb4408ae9bb8c5a6028099005 00452956c834dc93e022257d4ccf84a981262c21e5b0e364d606e5b8b35ef10b 8e4624bafddf2d0659dd9568425153ce7249d66dd796ae9d9a3958c207ff621e 18e20d7be6b02026da91d6a20212fb83271ca5c0e41d31f7742f9e5d7c9f75c8 0321996b983d708e35a7062ec8de17bdb8eed2aa44a33da0d2d1fdad633641ce bd8512058818d365f2a894bbae3fb48a8d8f683cb5b7ea86c20876fbcfacfd4a aa8206c4c30cae28a9539f34a06071b321f3bfbb1192890beb6cfcea066224b0 208e753a3e2ad56bc88aaeda6673781d74c4adea302c24bb430bc9d2ad99ffeb bd60707aab510cfe9aea55674eab81065c4481d823653bfdf4ceed5aa4cbd2fd d73cf31ffb213b78260d22a47f44376c188231f066635944f43b5ae6c487fdfc 04acf87b977a025d7aed687524a1612b4ec58f042f570dd537e2b7bf1f374819 b67f9de08052c5fb3a0b62ccb6ba87ea75cd0ceaa251708b40844267f6b014e2 1ba734db12fcc7ec682fd0ba5e50f8e201f88bacb180be02ca298bc501a04334 36d7d9519faf0b07448f93704963f2d5be500feaa60567408b62c2feee1abf63 fb64b38df7b1c5c931251663722e1bbcf64aeb53da9d97989e62eef5b3d33185 9934ed813d8e4ff4b96ea8691f2a73701443643065025a46b8d0ffed57deedcf ab16da45b8ff0732acd013c1a3574d7a76d4f8c510dc7dddf54723f8bc9235d0 cb6174b0170ef65b191639424a1d18c67ecb6366d5e789d006718ff192b74e31 5e832d1026ceafa167e47f8e846d6f9fe37d337fcd63cfcb0327ae049c79c253 7495dff8ec7290d6672d5ee2f5bef600fe0b573dd51d4d356dc3093801b1db78 218cd5ae20837d084034d5de568343e729c8c38c4c1aa9a7129eadc0b155b2b7 9466ad947fce451cf7fadbe9aaa5c7b6084f72b3421abe042121d6947579543f d221ba32ba6279137c88c55d22a192535e263d14033e775ef45eea9c076d0dc1 c428cfb99f6446666b862996418db4a552bb045ed8d8fcfd2b08deae775398af 8769dc3e2d1ad107d4f0d96cb5823602daf842cb159238809a9088752d340ca7 4083c2735f5c674f4896893952c90d275c6d28ec96961b36e75164b42d63469f c1f161d41107d3e258a1f77985876fcf02f05d98183ad77c5c943944024c4ce4 7e5fc3484d81d4f393f6fafe0dd4357ce73d08b6db85aa6d4a5dfdf61586f9ca 5c39e84b62a9dbcbe60d5625491f3cd44621d990ee50b74b7b3a7862057c7c48 34383e8918f547536c4a41cc91b2f02f642905325140b52f53589bba81f16383 1cddf5fc7808b6466f75be06c1b19b90025a85e241a44512e65a94b275058179 141df5ff0158ea408546d801ba6f0c6af9bcdefdf23ac134cf932835091c2f22 d476b262891e7be025046acc191b5950f598d26b940d883be486e5032cb31445 522d85cc71685afa492a62c6f4508207849af1987a7116cfc90e4c19ebe400df 1d1f78f69b6744335233e84495b0511e32d3c720e2373bf669c27a4990668e74 905d0a24578ba9bd1bc7b3686d1b08848b470ab12f4956824cf3b0fcf056c0fb 8a3768805b5a09ebc505e5c0d4242b617adb9e0f3865199bbd2a7be7579024c2 e7c80d659dbdd822a62c5a2b5580932a55c9baa6dc0818e8ae5467869f87375b 1b32d833d664cf3338c60fa3205c119e1ddc8c9cbeb44702459a97512575c832 12abc274c96f4ad4062309fa3060d05df560ec075cb78a80251cecacc9000a8e a01aff1dc079be3335a3efec1cb3be3eaffb22080688a4fea7ef634f8b1e5bac 1f4f6d983fe92cbac643d9a027741439190bd7d911d90c50a86bde27d8f4dabd 5d82c7c2e907bc4f46744c8bf1d8e441c21e28d3e194ad9acabad465022496bc 8177edd83f79988face73a00ead13d6c0ecb1db4f365b2e3f9568ad58cd8c641 2ef4116bf37982032ec32ceb4e1415331f4fb988957d113425ae1268975157b7 58a2d5b81c539077063bb9d470cc5aaa3f5014295ea7790b6bb002ba368ec551 452b24765b3f19045dd8c1b37e16823a6ea3c91d9500709ea0aa83880ba159f1 2e967b6d4aff868718cb1a7b26f50384b1012eb02313e8947d8e9a7c52d28a7e f160277b45b9b94f4004260a2dd9b2682f13081a1490cb873207ff96874d296a 6b9995916945798f3a838a968e36a030e64a240737da517d6c29fcb56e2e033a 3d3a67122086e15daa676f97a1f704008dbc47084ddd75bff2d52de83ef44377 e2c2c8614448e6b57b2bff177ce01d466211ec6cc80e26e9c4a9238657b13932 240762e3135c801ffc2df0714bc22bac52f23089d3698fa14c4f9dfd1eb09377 1b120c957aea684c4babc04fd213fcf10b20f56f8873a808dd707982c73b349b 468c72e026931e245e55e916ac2d0235c52e4255826f113b35c9857e171b2901 4dbd62c32aa0b9fa0cbb9c7f8174387957f23d2b7cf1386f39400b48f40802c8 a3d0a864f3226f810e69cfa3c6fe0e2967af9d69f12d39f85ce0c2c43e6310d3 3e7bf7715c43237de8eee1de54dc56b73f8e7982b30c1f93bfecedd49be9adbe 00ee10408bd89c7576d8e154cac0c7f743aad43ec26fe575f3a005ffadf84606 44 ea036c798a978bf880423093d4929c6d4b244a9874b5cd3500095b319b8e0107b33b4d9c60dccd53ff651565a687a88dd5f960efdcf29467b5d04e321c2773083602313ddff9a642f4ffe8f147d99a9eee00aa5ebd66764d172f2ba22c038c0811bdecdd5788bbee3e4363bada8ad8830d75d2465b4070f4312716c5ace376096929f6ca8867557eb0ce0f9012e6aa397be54579234001dc96fc2c7cd03250098ff6e080b7228981376b93b6af674bfe0d39a6c9df262a2a80f73b9531430301d262e638a96aa55fff4a00f5ceaac44159ed85ed37e475c524406dd096643d0735797f98e52db02565ba93388fd3e75c38b0c4d3276327dbbb12a51c1569c601723682178103a17295b81a060c099371a837569f362984a722e7a4914fe046050f9a015a2f91a2fbd6a0a76ae4c1b072ad3fb9a6122d25375033ada429b4fc0f0d38870229bcbdf4f94adad372b332ce787a3ceae44e84514bf26c6eaad4ca0437ad225442c2974481568fbd539f9059be80fbe69fad56263f0f427689f6910828e4679dd1ca75430b69897fa58065811e1e6b1b2a89ed7226b17f31a7a59a091aab5c83e2fdeb2c8b7bc52d84af68d4e3d57e0e540d38a3751ecc3cf5111e0c24e6744b8b6e2b61da27d8d2c40d90e01740a900df55f5f5dd2d21f1e2d4210d097c362d0d66f38894b4997df63b06d41c8094231167439afed0c23349b8b0095573ba5c72bc1238ff91d11a7fbd1bf5f03d64b72f7ec8e8d0330cdee9ecd5026f23829ff8175b81f03066bb2b7589c37b88230e7e18840504bfee024148d10d0d3e246b90670c04aa1981bac67ccf1c0e81debb920c53e3e4bc31d56da46c015c4039eea9c3bd4e9653ae03c2e634490f2693dde90aa6a38525eade34478508e4d8a6062c07b2020eaf40b43c8216f4d0730388879518c31f72bfed2ab8e105fb78a06f9ad875e380f32ac2ee353cd5a293047d14272c9c8d046bb297be680286075eab2fe1a5ef43ef61e338bb7b487a788c609e8b74cedf4fe3eee9c2650444887aebff0c33649a23f25281f7393eeb6e4ffd211edb57a0a6a0d2ccc2eb00509427ad297934ead29d912b86d7e47f52b975957dec8a3aec22018f93248e0771094b471b590fc011cfc6681d8d5bb21ce58f87e355cf0fdc27443b05ad60084c7fce3e3d401865e252df9725c49875cf206b9c15f91269bc68ba2b9095e80beadf571b5e016cd8ebd2aaddd3bf3cd512787a6fde7e3d89033cc1f30e5bf20749985686c20c612c7348e2f37fb9356d292337d90f0c9a495ad8b447e031fc040dfcb46edf034cdcd7f7317b2625396dcf70f384ab683c8480a6254308fdd307c8c962a133d2b685fcb688d7e0d6764f4efe37b1e3dd6f6aed7bc247e1a0060c061811214a3d0dcc33e7a9519ac4d7f1ddb1334c1bd28307eafea3e3131b6f07caad0dfd03da229cabf9a0c504edda6d3bce3ef3e754d5d4cf078c9361a5fc03d8a1b49ef905630712609ecde497c1b54e008b3b38527177df60d7e0ce88ae0287b34dfffb0daa0f9ff632c779dbabd8edda87d779ca6c11296c168f6cd49105ab86b375369ce70a82a0c1e65338584db8d2c2f007dd9e28ff816c95d9c4c10bc439acad9a05fc90a4190a10b1a3400f97b1c34f2bf6630bf279183567ab24018d461817de728b1b6727fd62228fe3702e67310bf6f820483b24a43dcaf226005761495e717fe30e5292f051f82281a9e85a8cfa0d9a52680a056ffb53c53f041a7da1db4830a5b14899fe12c8337f1a00f88796541e37a85c7a8b6ae8cd2c0db4064eab0f390d15608123eecf7cb49292021d53a28016238073073f743de50e6e01f71dc1c36244724857c176c7340a8907a195b2b19f76a0287d42a9b5b407b570bf698b40386c641bae6f0c2cc72afcf7706fe65d9b78192fef1da8ee690f93ec8ecf413c06bead493b824e90dc8c5c958f888c11a6873764751d17f9e301998d47b7e4d1e00ad610eb772150bb77fb498ef0f3a844c7d45187e9d04db3075e6b9ac21daef6e3df7314c6627cf646c71c85b05dc0a0ec2ff4a0ef58331001d32d3124beeb1212538450443bc5ae823506d4077eabaafa414509e302e03b08335ea3964782b982c078dac2c271edca8cb798a68b974962753935a822c72307774fbca91cd19be4bd8b5ab1a81eb007c2498db6ca94e74ed61b1bb5f556cb0419c28bc88e8be8b8877745e4d050794b2b1a5c549585afb5e4022872b614c8088f05ebfd7cace6e82f9d069d74a04b67e083e58b1514cbe89bf5cac23593590ab744712f1c80ed6499e25ae85dc636c965872c7441b96af6b9e1766a4502d60436adcae780f3ee4d097c6b26591c62c10eecf62dcfe8b9faed1cc3436181f9096fd3e6546b8a683d04ae9d35a5246e26c0c3d67a85523552ac135e8fd74c4e08916a7051715b4a6d8bab0c88cafb55ccfd4d77684292993ef1a62ddf3ef00f0ff1f3ca2be5631066766d923ea5d45c04513b645471d2c698fee5c11449cdad0420191d2f6f7130db95889daeefff4b2307f441ce2e1d2c7b9dffe373a6db4e058796df6a4a485647a8f9f660adb6f35a9298c0f427f3cf212757ab50a2d470008bc2dd5249fec9d0f2f401b8ec7d172436705d79dec78ed38d3c2f9284413a07db64c092b429a9921c904aee753ad3fcc1c5779c2117fda0ed10a6ebf77d310414ef8c089866f7125f66b759d1059ca1c98ce2ae4d90e68d7af18d7931a5ee0ab40c025e00fd8c2bd450aaed9d6d16ed08c4e499ebcae6a45cebfba66656260c8d5dbb7a0466e3ec9f214327c2746c946af769bff38da55dd5ed308424691d0c1ffdca53753ddb7be6ea081da0b9596fa8ea4d72ff582b86c3778012f5005207f7f8dd669e9108306d477530008c43cc8cf08c6497210532f729e3f084fae80a8bba574ef14b654f503d66585eb2acf19e2bf1a67fcaa554786452e0bfd75f0d0bf6b10c066f6de7081bb5a8b9c4e613a3391abad46bd9cec3317f15c8d12100d4549ae6bf63dbbf3937a6278b31bf80a987a10f7000de1d6181a7a6bc633d01fbae3e7a41b8346027170806b2920c6cd8e36e62eba3103947fd45988ac07d05fcda94ab8708da35c84fa5e4cfeb59be89cb51afb39a3a6ac28af8172a547008076d47811ec74c8627c4d0306ecf1f51ca03229d63f31de267e7ffd299557f027cb8b4b8fced8d08561aafb32970bc3dca5e617a91861d1243ee53e639712b07ac23e107742b7316f184ca6ff68efed2ca5bf76edb065a0080df2443e488350278f065b68920db1efd041dc7663befe068b35da3c3e94e04dbd13d929dafa10fd164072166633844f21423095e878fe5902418c2a2152e8b78a009482492ee0fb521d632ffe50ed732d0005f9d47b57181f7312b1a41b1c877ca02162d66d601aecf6ca866dc9c8f2a4430a9b9bdb740f24014593c53eb244651b0b09c605501e3228f67b83ab6ff8fe70eb5a14b66a44e2ac7180a4976fc37dc8227d20a2007fb88b9ca14c8d298fbe9d7eb42ec2f53f10c8a1c54cbac585d169a11ae73c508b097333ca2830915f8b90673ca90ce52d2c3fa9a8a4d756a2919d3c22a67ee0d0478f2776bfb69796417ece78a52c998492d7832080f5f52ca5691e16b0eb305f0bf5994284bf5a83cdf3f14cbcd4a5ada94b0b2760f3a865ce2e736a527b108e1757d33ed4c7f13fea7e327207bbd437f2f9372f941b53874ee6249c8dd46066659d4b020e1edd486de54d73fe49a1e030d9374f0139a0a544239f5716d7a089b2f72002b6bf05886408c2064f7c9e82bee35e0d2946c91805c26276f01830e000ae9b9532053074c0fa5949c2fce2603b1ad334387f0bc64959c167da5860f1d8fe87a047d577be053459554790e247b46a2e0c537bac03440b9a0b49dd50168654f834fc64604a3fa582198f8cb184a1619c5ce144ce86033b14f520ef30f882e24bd959bc5f3ba43f809d3705fa574bc4e7b8527d85e1a6ac58f735e01074e8dd639aed41ca1d533d8a74b7e691de1dcaf11815940db6767cfbdc842c80fe4502989de3aef26a3b77340ac584cff691d3628ca727bed46be34d186fa19064aef1892b3eb4e5265ad2c14815dabfca977c57412c2ef14cc22dffb4e975a0654b43d8c4b288d4ab7b7a8766a6d37f5f534dc3c61063bc3d2e01ecb534de806e6bc1e003ae518dca85db46d1f3f5b1a76ac554cadbe80d83ad96a1ef524880baf80746fb9253e0f56c7eac147fa9ebb98c66cf6292806b83d75ea8bba8efc0f3859c2632502cba122db858f09e24d8b2332e0166848be6c09007dbeeea91e08c61cc0482496f4faabb08ee57b1826843f13bc204cd1d5d30fcfee699b0eb50603658426fb52f84b934e900fece01c168a5f0870a1cea2c7387f9ae232ddc40b611915a52f77d95e2eb5892bc2d1646387a1428f36981b44e38987040d0ab10ebb9887b6f0fb63cea6debc60a9c569bd56f38f5f68a57d51adb6fc0aa3a0e700b9051a49969343ae55625fd1be7d46f9c1c399aa82b6c57e8fe808222dff0e014e15879a6fc3c0e3d49c6ca77da12568f78ec40a3dd05e466035dbf987046a03bd1c7973f04d98c8bc232fc8cc5b65583f3944bfbe212dd961caaa0fe48c9d00f0857609e752c014e83a79631e866f86e7796b8b2501f82c81b513a4b59bd10c6f506c4bd45457f45a332132c9faf89ed50bf74faca6f79ce2ccc63d7e528e077874de714a5862df11a193a8a2397928ad3399a6d812511bcbd579b40871c60af3e87e9fcde3f04031f8ef4e1581b3792c20df1480730f57c9e0b56ab8bea30b6fe472c524ee3096a5ac53830d39659254108d2d31ee7700ce45f8566b394b03269a3fb390646237f7de1d9c6bb9f916a2353608d354d0ca77478f29557bda02fa89154b3184b63fd62d953d52f4e1866b72e38fb1129c5a7ec57532f0be18051861c0b9ca8bbe81d60a3f931b731603a2f2faa76b447ba2502f000c1e11460e8925203532a0b182b2287696a430ab2102e919f6c38142c685207c4d7119d00b6ce2da2cf249fff1298c815fd41768873677207c28e25bc4373f1cc32463c00540812b690dc1fbc38cae1573176b9e08c59c0ff6c4717d459f234ab209b426058b25e7789188f29ef01def5ad20237a3d0d2197da121ed2920b2852288d6bf0ce025da3764337443243d16c5d2dd1a6557df48c4ef8b6ab1a2c215fbd904b308be4ca810aa8a0f9acdd70fb3ffe66d2430d1ee242c4e1fe2c3dd06a7567f930d3119aa0e6843460f72e60c67844a2f5d1e501dea1c9d3892dba51f6bd7b55803560212c751244f0b3f97c2100b93b0f096d0194aa51d2f865179dc00e6a2ae058f9563a46c8ab11dedf879f079f6469056a0f9e193f60f4dc6e803dcc7e97f03d292472a20b336971cc84c0df1ad14384433ec148b0c78e1d349e78cf00e440c9a7afc466cc762d9656973ae25e19886182b5320bc468d71c955ed6013d38c04cca336b21e593e0b5574a6a5037ce4c8a9d7faa3141e5e57f6399acd04a6cc00e6a28ecdf54f8fe5be58832ef1e2a0e9705974253550c1a05e69c3c72e3d6506d4431b8b57769412492b1a9c9fcb7190d551d7199d023ff5d858ef92cda2c70551e3b7a0f4a32cb83b5ca111e4cd1005616d8464446c9e70d32cc39dac3d3c06753f849ae0d60d8655418e94c88ebfbedf0051727936cbf825928a16b0b33d07e08ab155ac6b7d07daa2a83d61548c3a27ea09fe450de550f0ee29cec2f03a08388a63328c23b1a0b66eeb266f837abf657b9a649eb35462b461bb533e3c2d0b69af1ee57a6b1df990a4574a6bfccca5da1c23eed9fef2a9225663b63034190008f42234965875731dcc76803da1a58290fe2e5e8f88f44d9bcc035809627e0bf6632a71eda38b9e4abf69fc3cdde99a897920136517cc5be2f3db7043925304ac27ac718da62b7c550e3e8a908831cbd0a5811b211d9f0f25b612811b7c7a08c51e7fd191b966d012f22b75d950e2ee11eae12de1dddb061e504c2bde0cf50499a643b46797e190635206a51da4a9e124a6c0fa78827db20d6e21df67441a0223e7cfa5ea7a34a76823550084d3b8a6a41ded0b321b6f795a792d4098d9960580d89db4dfca3eba564e4c0743d4e117e8abb7366fd0bd6a175dcd020770930bd8e6dc9856b3ebca486f38190f5a3d8e9bdaef525a4275b2cc6411cb06f2c30700c365101ce07900cc0b580556c0afcd0ee7ad0e3c42273ea776662081b9030c94a8e15630532444e843927db3b78cb90771d3b411e19bf7ab09dcbd3ee3ef0a6075746f5afce41aa48e77b6680310ae06fd532ca3b1121e8a1392a55bb9cd04c8ea8bfa4153b24acf1be0d25a9c1371f43d3fa02d6ee4546524a9e3a1c8610694519c16fabcc3940f665816ebad79c5891f68a357b8cc36b8fb3950ef42780bf0954598c888d722527f0bd89c856e2e9a99868082ce00d22136172aa2201f05a3d812e20cace6d618d8f27af5727117fe13159d7b66ece3eab1926e5a22f50bbfa6f0026ff592ae7f2860f78579057db8f7d95ec685b07e47e1505cf7674c0f33117901aae02d8cfe31dc75f74bab3acf5d708e725333a586810e5cf70647030998795eb6bc85216ceb58f1f3e966c52e1fd1e641dfd6a389f2f3baeeebe50eb464d2e5b3b0586aab42d33449638378d83c5e33b68a72c581a3f73f851e20094e0fa8196166d1a392f6318ce7571c3052398b82cc427ed01a1084074b85f40a592d22f3afa34d5b87c09804f4d261b491b8eccb3ad0b6ddf6bb30b83e611c02d018f85e808bfcb5e85388e1a80dd63252bd7e995ecaf60b800235386ee7660a4cd6c4c48adb98cb487994c4bae683b0c4d21e0e6b297d4404a13c7ae74c030f54825445da3ccdea4916d52c9da05c0e209838597688fbe1fb71c0f17d1cf00a07c909ddf3515bbd8a971999f0927f1748b535031249d1e5c61a97475c09b60d13b2c49ac131d2fe8c5b20681921adcfcfe0bead3db0cbbe9600966bcab0e309e0da71d8b3f00a0dfdb0a74f0337101c7b7666acaee601ae248daa8c6555060ead14fc3a792d05e35b99321429b1b81a392faf52e7edb4ffb0375b7f6c39e606df5df228ee7cc06b285a2556de8e5b7fac02fa1dbad6a1d49f89f23087fb2902b46ad07e8b68857543002ef7a69a65fbcd04886a944f8fc358fc5cd1bd8aa5087dc0d8e5a44fcf955d989504e837c846173cc1966cec776108c991f097c9970db3d8cefe26db33fc78cd1556b6966f420c6405f8ae4082c62151af10e7ea730fb1e7e63373d0e9a8e0ff77bd4a2bf63f85a2536ff3402997b7dffb20af408409c391bc20afc1f86f1aceb81b04ef5c22106a24900cbae576340c191dcde898018989906d357aa9c9bdad2b288bee23d9c77ecff2bbf35d7f2a6f097bbadaf50a6f1ed67abef43550ac80267233ff930e9d7b313d1c21745854e16813fcae820e8f4cc8725769aac0e6f00b1c862ffdb37dd3f8fbc62532bcf667e1ec86845207077d85868156d541ccb0e1ebc9b96d63660b0b1a966e994e61b572302f88e908f85092fce00b56e943c54d861705ce9e0e5531f1a66bda27078eacc36ef3590eaf00c63b5c1675ae6ada64cf378e777e7956074b04a813b34fd929d0acab10030b8f4d6a68cd8d7b835a83a6b0ba7a6afde6862bf328b62d67a8218dc20f190cf57e0b5e0f18ddea2cfc96293d235e5943ed9f8afe5c33746df67e6ec905050a6024e1d446198252bbf4014b5794b8a7d9fb74873c1dee6c60330ec1d31bc60dee15c391b205f71a62da15f7dbbdf52463932a191884388609d9bfdbcad0d0055f334ae6822bced23d07d303cd35841aeffc65388ab80fcedc66461261a81b0dd8cd34270be2542eb2d4eabcc42a9e083a07846efa79b351db493c5174d84900bbf4e442660c051ebe059550e2c67a7a8434faab10c6bfb2802b1e2f04cced0519d047ce3c502abe2ff94dc8be59f6e415016d2c77f1b54bfbffe179c9ead70aec1fa21d4a814abd41f7d5d19bc6b77f4ab14298a1bb14f7483197fff2b6390e2c3230700ff95ef79681078d49f0ff3a8f51138771989c3c1a8e18e13e49a90a3fcbc0a7b8b2458c0b6beca04c5dbdfe86b538e57850222a24eca0476c73b70654f3b8e29e227998fd54ffe06ad51f831fb4dd0b07258a17a9d27595738c060b4a087c00389c596fb33c838f4c7581663317da9a3a941056d6aa000734e380005b7c550677862c23689eef181e8107d1ff986c9491de02d8f6c001789ab222032674a1feb232ae673757ef59122249400cc1ba29a953815aa8da9e8275b3de0f3361574f998cb95a4087c880c87c8593c24d82772867ed9f2437f8b2465a0b0e13e36a5a15f56e04f4e861c1dcec4913c789a96e556e1b8155fa9e472c8007024f2f0d7978a7aeac7fa54d59acc6bea8758dd0b215a240e8fda5b05440fa1c0c933d982cbcb6e543ae08fb82f33f97a0509da0f42ae235f42698b1244e6c9902b5cf3fbf95688c9ed3ac06b5073a03faf591624f4f4e76cec2a590da26d60306e34eeff5b3f27e6d8cf3ad0dabf417397883c7505b911519b5bd8aa9f9d9930c720d534e362a1582b9e3e008c339d43e1e1c3a9f26cd7800cf4752491fb2c70a3e49b2bc960a44168bd04284b7abf6506672a29e5a5b42f27ffb1596c544af00709b832bcb891e9fff8511d0c75481db9e7a0392f16db45873fa1d45d7a85f075d05b31ff42bc26cfa9c87a6400939e9f32a7388a867877de470cdcc39e213029cd6eabcd67af7bc6af0a981d403f2cd2a863fcb3d31cab9e34f60115f5be00a5fa74fcdb120cfe179703ba26691838145a2c0b1043d0296d27e88f173f3160cfd1fb21cad26e39ad6dede6aa15b40cce45443a1d9b1de22b9e9c63ac9dbf4084e1204fc9cd654f68572705fa66eafb934e924f64682ccd4c65cc38cfdb956079dc39002ae8995c740352ddd5b84795b53a7262d231d2c309ad1888301920408173699be4eb061febb4351ca82398893cfeacbe55e41d7c2a7046846ec0b940bc6d1df129c92618fc7da7d8c7a4f5b100faaa52ab538d592e7d38f446fb005032356d8a445faa7d655efe6d6459d05480d88472df5ed85d3b34ff34ba6a59906ef7b7849802186ba3c50abe2f2995456da2803cd96d0d97478e640942a63e603e22f2e89274c79e8fb3f5bbdcf3c91426510988730ac9637ffffb660ea1cb70ebd66f0563595214a563452163252b3db9be60d5e129f90f55bdcbad2300c780ba70b30678e11e349bcd4d5b5a80ba85e80162dd08470eb7faba473c3f4605d0c92cedeea55f423c1cccb7c0a9a2479671b96c9ea1a3329c1df5a71b9011a3a0941c3b646395255e0e65ad84ab2de4a8e04c54995dba0a8c36bcc4d56971e350b71d7baed141a9ee90672ab3eccd024095a354915bd6a3e8e6aaba2194ecb8202c3b328b8fbc2b8587b75d0ddae8edc5b9129f883e1ed4210358a99b74fd72205065f05f72ec6f988ca2a5d713761c56b85f808d36638bab082c7e670f578b50f60cc3cba90bfbd2868ff15909e92da705f38a6e4ba0b0cc904a4815930232600592483bd5d4b550eeefd8a1567c9e349159adcd3c41adbac9c55ba8ce494c10569410b9aea3b33dd25b887d26a618e5f3bd323672a56f4cbcff3ae8889e7ce0aa2b86431718210ef6a49cc5083fbfa5ee538ff6b95fe4dd7cc6d565228f8bc0f9e3ab33a4bcdc3c49f8f15a8084b9526e874b71989ec8be22245e65944588c016cc701f511e4c5445eb7e40dae1011769be29ab8cc5a3a67cbbdc60e35f48c06ee90b79a7d05059137d32fc78ea7e7d302c616a5bd1d88fda2a6ae963f7a5f02cd71f007a49718f0afc04f7b0edde7c517cefd623b89bd6b5644d4afe353ff0be7ef516ff029ad53713ff564b808388cbad04d200f396d69ddc9f152856d930fb36ffcd1919822df876c3a43c9e867f69efd77b5698cd7af8d63d1ea7a5b0d030f6fd54945de51a6a99aacf4c1be292bb858fb44c2e018f62f66d78fbb5a5a0f5f8bf74e1cec27f4fc3236560949fc57577978c2f06284e4dc5bf61b501e650f09a314026731c54b61d84adf4d9be32e88ffbeffd4f094df5c5f0068d13fd40c35ba9e69a21063aed2846db36ba7b0c6653c1771f53710927bf5b58faf7fa103ab86d54d4d8b58fd7271845a874ac1cbfeee7bfcf0b3e69c466d80dedc808c0d8d704b4a52025b34668b8bfd8d50f00eb2b4229f7c4a4c6a03d36771b093170f -generate_ring_signature 52ab38afe83396f4a7abbbe478732d39260c8086f1b221f37018ad83a56295e8 52144c557bee42c63d3943a1da22bf53c53cf47e18e39ec7618060fdfa2a056f 113 2667878a43543a286946b627dba44ad682f51a0ec0dd5885c0c17706e1f27962 56070ef37ef631a894808bf2998c18079451cab0c36076b94ba7046928c7a1b7 09d61f61e982a1e500603b81bc56fc727b7ab3822a8af511a213619776bc559e 3d16d0811f8d8b1ff564124ac0735d77c2b62ed3339a2542deb20167ac8d78dc 8d1e73a58271f020e21201b77b3e1a0d5802960cef0da9c30f88331d1e53b7b8 b15c3a90c6d12c50c12ad1f1cd4f5cbca77abfbe26dd1003c5f92d256b7d9a2a fce4121de27006176bafef1742169e769f3a25a075b413ef0c3f7affd5150561 23e7a79af125e2cc0907abc7b61ebb2b6d712691a6c556474ca79c1cd2873c0e 1806559d6d2e9812dfa4d97efabbd8ecafb8331b2a836e8b94212ae21d9e935d 3d825ca551aeb017319626965977dac767f90994d4574ea52b0600086f745c96 9e5d692b5e82670f6284fef4efb6a3dce320ca9644126d52782f922e2f1cb62d b04d172573b99e1b8074d362c5f6b997b05ca74a5aa4d35c957853f9078ea90a 451d26bdd2e49368fca9c893cbf9a2551aa0bdb1df3d919a75f16da4615ac00f 39123ff66a0a8cf47643625b997f3febe9b843028a733213f920fcb0fb224321 2e344a31a1f4abb279b66741e22b00d72342c93ddd66332b9480f38ad010dbf4 174dad431a88a820abab2a8ddf30bacd6c0720da30f98ccab7c821f2ce13570a 8fd6651dd86f87c6e9baa019e3222459a3495c481b5983bc1f3bdd43600538d4 70bd22972b4e8c895d93c5313a2c4de3f096b2832401b34492d8b63e4c828b93 724ac189a333456efd5ea95bbd6c3174180204a0541f56c9d3c4e82c1b9a9430 ee0295b969e97cfbe6d47f879da1a789cb27f9bb2af514fd549b4c8d8db67ea8 6516c5de7a016a83efaeb52dc3810e147831cc6a7bf6d556aa711e3f26ae74f1 3f8aa4e1f31046011579a2efb2ff2e98c2e94868bf61f529b28e0c9dc922e65a 40f502a1fc081d3e0870baf688962eaeb053998be9e61627a99ea713bedb7d33 f5de2cbe86740e20007d273d240495720423986ad67d13022a69f67eb65c9db9 a72c9067245c4f1f7d3c6411578b2e02e18c373f78957b0c011d1ba817d5fcfa 2a6f3ed6fe0238355cf8a1bbab5f2c1f5674c2432cd8c964f4bde62d1f84089f a1be89b4e0cd03a7080617e61c08203f935e63429a34f8ae637c6290a5f855a9 dc25b39ea362c07b69225919ec631e1de3e539a0c99076421a03888c136040de c42663cb57da0d67ce5e40583deafdab17af7f37f4e9cacac7be29510b77ae60 57e85e3d5bb0bfb51da145a2345412b3d4f1bef0a9ff125c2533e3a76c2fb261 51d82c0f6c42340c6f37f6a4d24eeddde756700a2a674f0e5745b35b0493cf47 d6239694cf291b89f96ca1f448b250193f738547b70c7627a72d6d8c3edef2a8 3e3ef92d08a0f37954c6ada45ef29204a7dacc626c7181d7613f2fa613a7d515 479b439e8cc29f89d9dbbc3acd98fac89603d262b014a2f876ae4646ada1d401 ac9c823e44caa76f6d0d2ae5da6684a6fe0fc4c623cb28bb0ffedab0de052047 bd47406403e535790864051db0c1c2d09b0e7a0fa9df52d23036106cc5eb915e 19abfae288790ac7d77c0581167f78415897eacc19ae8d537d118c47fa2b1553 b5a5323c096b566ec9e91eb9f4c870869872499205d05cf3d7b58811d60b1f58 36d46886bca44dcd834fa408bc22275efd1264644143dc3fde9814b06d1ca44b 758aaa67d8781b59b605c687e993e1d43913e3693a351d9addaff6a21cf196f0 32c1e4af3a5597eef97f47511fd377a961dcf5e3ed4c1b8ffe981abe8ce31a84 ec3619a0a1928ce8907e344b0c7a8e87e15fd53ba8693341138a5714442e2c34 c6bd33f733cbaff02918e91d14855be7966fbe29a04b6149833010293b04920a 6b8a52bf35bf9bcdb80f5c22fa241b7342b4f6bbe085f78e5096f0647f0298ba 3222525c78358332d2571622030a2c91873a8912f815486ae7f1b38418cccb2b 5ffc8527b801db0fd1f9fa568e6fd8915afd4d2a12c9e760f8a0e3f65c13fee6 65cd0954290ad98ee8fc0ade4ca8931b1f6832199e69496a57d71b25e0c4da6d 519dd0ce81bfb0e2e2aed5c4404fd08cc5dfc32cdebd2c4eac8e8341a99593dd 028506245b4782ff02e531921ad41f33458316d2a04701949c9a1130170f667a 65479d0e3b8c5e054a42165784f71979e44c5ec86edbd9360d364bec827c38ad b056a8e8159cb51dd0ff6854f0b5cb7d73eb6af8a5056f7a157000fe950956a9 121bab1f2128a355510aff20a2348ca726015f44b14247ce79d17147a4c4060c 67b7dd5f52f743bf4421b83e735123e1c17173c562bebbc92f241f748c45c682 1e8a956e74016f5431b773efa5ecb58a388e94f91b9a5a4d928c90b987298361 928bca6f3ef639fb399620438c215df701e992d50c32a7d519c9c4029777d8a9 4b928f62407d82ea63fe4ff44757b0e2c99b3d809f473a2d9856a4e7915a4048 2638494a4220023d7abb69e0663e321321a39f10ed88170cad8c098adc2b5762 b4e4fb545e5daad738d95438f9caafa048eb5b7b6cda4984f0d98b218f8ecab9 4e1da4093412883d96f6080929c73604c9b918e4b4eb3263edd50d07cd370ac2 1825bd87bd4c8621c5d585e3a150b810734249c5f8b98826d4fca4fe17a479c0 afba4add454e41364dfb8f3f10e613f4379125837d809f9617c0c59557d8ba06 be6c78f9549f725e77166b209a3a62be24858c6fa6730b751b454d8db46513f8 d72dae253fef5793e2c4b21266aa5fd4bd4bb298ac12024c3136bacc698bb5b7 3cccdd128fb4279cdb8782fe4e891c92b9212ca7df38c1281a7c23bf0488c7bd 38d08fe57e0103171b5000d5f7d6f741eb51c405dbcc7ba351ced61d610b5e0d 7877f90fcf24f4cf3fca2f456e942af7cbb7c713aa9a26a437870bb0dff5adec ec4175d3e5369062603878430224568d1d9f6b01b48a0377dc82f66f9ca309c7 eb039d354117c3df90c53e9eb0492b4cf2e4f6b4b57bfb6cc809c22621edf016 6ceb1f244447e6c4fb2f4b7abebdd227271ae511383515241e146e3401b47afc 65cb32f44ecb9ce88d08bac89c7b36eeac2e52fbafae9c104b65851d0d3fb689 7ae2db115d82335fe2d49f12ef8248994b958fbedfde2f99dcae864daadadbfd e547d52a6eeb118a7cd1147895f5a75dd0dfca914d50706f6a7033235e3ba07f 6ab9a84a88f767a1cbc69b2740c05acaa9fc9d84ae58cdc77e01b0900cb19ed7 601efbbfc5cb40884d47fba0a5e1e5750685788e4225ad69319f0dedc8053cd8 151f98617ce7dca0ac7bdc7910fcf5afceea4b8acb144110377b458ee877eab7 7dc10dc41ad38ef8c879ac0c434ba7dfe0c4ee458285bfc0b07be3127c970bca 171ce0ef0830d4ed99f42e0c20d063ccc3c6ca50b747b36f24572535d28c77ae 3f55caf66c98c9d773a90472b2432e6bcdd84a286b518a3f3f95e2ff104847f0 98ac27c0ec172728c1284698b62bf64afcc2cd1535ab245b6ef9e39e1a1ee486 38a2e914fce5327a0510a04ebfc2843e5ca25b995ec616bc1e214df14cdbb2d8 6ede2050ccc3ce325d911fc26311bc74f9f09f51096acb61ffff4b751705e68f 527e46d547a1337da68feb3065d017f1fc3d37fea430714287ac3bf13de5e43f 8208fe6fa4af1af795df8b07f36dcb08d87f9b5fdbb06059b6ccebb6cea006ce 41cc08d2fa9ca82df528502af00d1d8ab83840d3961cce3a000909322c393b15 c04393bc4ae049524110432433f0290f23a88dc4b0bed266bd3c139180f4571b e41fde50f6a402f874e366619f17cf3af987c3641720207c0e48b30f3321d462 d680c870d7b43c6ae308b818dd6d14b29758aec61195bbd3e354221c897f5458 352582605e62e595ee051a22e47c7f10c86a0ea59896c907afd853c418893bcf 39b1b721c361d5cc7d3455cfeb782aec7f122348a62caa0d67f4fdbc98665503 66e0b54e8d0d0b0b61a6641685c1f3acbe72bc1e55254d88a2cbcf50399fad68 c3afda12547982d6afa86121d0eae5a549d22192469079100f272ab6d8831338 3d8021bf75cd1a3b64192e532e21744cca23a32a7a50b459b635d8cfdd6e28be 176079f943ba5100d89a6f665864407996e7ea1d554cfa79322d38300fbf3cb6 735b236716beb3d302ad5518901b9a523d51fac099752fa0c1714fa342ecc944 d6d69f6f715109ba6b81cc303efda0abd38a312f526b49bc058ccc44d076be32 1e2dd2d69008482d8e7a02b3d026e549d61455b1add013bc88e131dbae129b16 e02a717aae13cc6ec9a9ae8bfeb952c42d8fc709233cfd1ef7ccbc1bbf912bbf 009b16fd9effa67a4c4c8eda2b15fcc67adfb5aec379d592c6b80c63d3750a9f 40cba48b6fe177037c2d2f652e35278d5aa779ec0e37a01a3338154b0109c7d5 fa94d28721ee024c3920937cc597b6e440056d873da9183a4121740079e91c52 b87e5b2635dd6e4bbe8d22f83e55a9b088fc79bfc79b3866ce37aa3be95a4783 705a31ef4484a1d621a26ff0b0250ab8f4b4feed12227f709c41b03aef4739a3 5c424ca54db641680633f09638dccf23657a0fc34f09c2ed97b3ff0193c330c4 f00e3bff74143e2eb81b5aa10d4f8e110d6f82c12a6ecd870f786d120a863ce5 2b57a48a8c801c44fb5a193e27b51a3b63ba879a68a603f282ee29bb06dc7b20 b02e81daa731f503076e99d7e975ccd900b8a4f4a20768e03225a74ee7769c6b 2dbc9cba9c5b1c99149fff8f97be8faa42fc16d0be7bc5ac77dcf8886a290e32 0027178fea8c180bdedbfcb2bb7e394185b37a42977afbb70ef8cc3f5b2b1997 c417798c3869d3bef770f05c7eb4e89948bd4c76031dc429d6a52fb199e16b02 829c84deef8ab603af8fd2a06bdd0daeb12820c32345488d074fdf0c8f004879 4be25ba569f1b4aa9c55a3e6dcf232d3b3ce4cf9990a6f8698b24db41ca20eb8 37165ce3e5c4b593456f947b9813754b44e40e8407b81178db50716df2352374 b899ab2a46ba764730d7a822b6e24afd6d6d276c4a73ee66fc39eaaf2ed57b84 fae3ccc4f506532923a8f03d2d227f60d7870ca2c855098aedabd89abfebfb04 108 1a5ef4371a62115c4738ed0475f022b26564a53f4fe400828c73b632922d9409e44ce61e1f42eb35de0279c8f2cce7576937a66ebf13c317ab8d1e16497bb50e8cf4a3ac4f69616aaf24bc2a788032fc98a81e31eac6a15e7d51a1c356e9fc06953254e68fd218334d1425d4428a417f5a787d848f95df4fab09b3a46abca60db13ed3720b4ead6923126bd5d801fec0ce795ce4f2e7d65cfe67045f1aadf20e713db4ace52a22c88d526dd37bbdb2debeebee5cf8a526bcb6307812d6da5105f8f5271fb36bb1aa9ecad5639d5d1890125a34025bec5fd3d3d347defad9050ba5d4cd28c1b83afafc7383601a631efc3d3e7b8d9324d9ef2e482af55b4d00005e2f48dfad0757d0ea6868f66fee9c06a75f8097c417bd52b5f3fb8688d55b0142128c7814c5770e39e0808fc5e683953a2c4de55d481802e53cb005c16374036821eb2b0ad3cccfe05880b051e6ef461ed4c99a155152dd5e4d2976837575024572406d50d62bf88713cd45406e59b96fcbb227f41b9e1d738300a9ab504b02745dc8be0d830b453eb453dfc1351396ae66e50f65468cc40e93d58b5be92805a11c74feff56deba891431fe7e9e07f585a266c506fd79c8f7f6011375a5220991a3967e449e92a90b1d8c823170401d49e721e433af0a58d7e46d3cc534880fb8757cc001a8870309ae882f765d248f7df51d9ee0d0f0e3338d01c20fdaf4007c83cbe4188f767a8118e855a805b386ab766529b86f7abc3bb216412b94d60bca2d0be6bedb4a38627cab5c3b23576be308c2d6c261588e4f2f97907a472c028345c9fa3e9994cb617cbf788faf4c60efe7b4d97f9a88635d99b1e9874ae4017eaaf6bb0e0a508a7012a4aaff74f55fb306af3c06ba731f53f5e3496c40810f93afd0a700d9635c74afdfcf71d3256ecedcfb42752d2409a43d8c2fb1462c0f920e94edc3e3fe99994a5f81cdcba5cd20216d6898e76de5d92d708a8756bf0e93737aa75140672b92cb03cd32b276101f3f11fbf4a99f643ffd111f7269e409d9d3410d720b7781d23815d4cff5567b6d4e46228465888386e462f0a00b4308f55cc7c7ce54342b6cbd055d91d1a8f4e9439d34365feb193873ac8dc89154083081c38859913fb9061b781a877c7edf186ea6b0f2d2e52d8580e908b39c8b0621fe0ddd6977d109f9e7358485f9f9a52c3809611c646e518ad1bb8ac253db029fef063d2da49d4c2fdefc611fd41c286e3adebc3c10a6638e2d1417015dae0257dedc7b75644fef8954191eed5fcb84b2609cdd906d0e819a3fc1ce5041d20e360435c0f0f4030b2928eb9cb1bc466e9b180c2c5c496b6c738df0d3060bb10ed00cf5a74fd092bf8eaf5405f8b6dab8e769247a172293b4c031c062506bba0e3aa9ba72e1711c53ffb12e91ee280e357ee4d8f3cf7cbe521b804c7024903b0dd84f7a2c7b35c4d40ccbf07265b043fcbe60c5575af5febe457d1b03e0317d004c6b19add44caadf00f38c32f4fafee7e79ce7e1d88ac7df1ca9222e0975d70d36b8e303755f8281563cd489395db91ff6e79ad496088aed0f69e3a75a24350e2880261e04996a0ae38048ab94f55e7cfd2b8b626f0e974064603f9de1cfa10bb16e4d30c7ebed0467cc763f9a11bc5c365b95e32afe92ee602f1bd087ec8f0779cdd310ea136433fdaaef52c8b1790aab02901cfdb877f23aa9d550c031bf0ccefa6ae93f5a7f5fa29874fa7db6dd9cf7491021fe0d0d44f7d2ef908af2fa0b017f313b514f949751a675513883c675c3906750901ae6b6c8b606519b33b603425999bb393e414d361f2eca1f78ccaeb595a90ad0f54736dd52f33428eba701cde108345e261414b0894a2dde8e608749dad05b53ac95a9971e9b1558d365054ec6b2c2bc3c0f19e626d7b28a1827972565c4a27528ffa288a5ea2475115e0b42cfce09fce1c3f9c3af277a5178e1ec17056ef7503d61d3eb6eb8a7ab30430d51569d165736fcdb0e2e132ea336e01e4cf4c601ea6e8e87dc3fe2317c4611052208c9db676af426791d8a7cde51314ee458551b02f44e54960d55e4def9f406eb755aac9bbde7fdd13d8c697149247150f8f6601c27b4f44653662a102b9201edeae8fdc3f7db4ac6e486b7647a496f1f3733163af928202e87fcc2bb6352036b47ad21fe16846877e873f082dedeb92698a418db3ba6f66468d9f7a9d9420266661dae496379488b67bff119c0bf8bf76c3e1c11b3e5ac4e28b77ed9f99c0f79232081f2e49cf483d098e55b4c4c0063e44f16ff7ee79ca87e0791f9966e057dc60dac78e8033b4c5112f7d1fc487663394d60725f485885961a5979a3ac0122204b36d47c0721e1e0f02cfa75c9dccb1d336fa120eeb10b4d4f4f68e5730e3f8bed25f378742697bc7b88a50469414d42973f02f49d16a2d83a7b9c998c0768ae277e57ec5cb2f9f8be5e9247bfc66be5e8187902d0d96571e2980ccf3c0768e52ad84d08fdbb6c8f48df91fb5d7a3cd7434f260a2a3c8d9b43c9a77ff909ce44c4edc26b5cc82ae22bca3e4231044a22b31e396b803fb2a03e9e3de05d0c75384c67725a0d962540169f348309d00a501d6d05f9f1e44acfebe40e21240087813f2649e03707797db2871da837d1d5f187179e8fcdb7bb1d7618dffb2605bfade539bfcc9c06c8167cddaca619fc400f0412b4db4ccf34cc51ccdb3d1505e0b96631a802f937e3aa2507afb006e5dd996880279e6c9b32a6d27454394b06ba8a4050e39371697b019a2dba3c494c579be7879071552ebd6f19a947663b05cd40ce1eeafb7c8d0a374735c4aaa6611c3abe4cd648219416eb2df625b4950adaf0b3891a8286dc9e732fd88da59f3a4510777da9bb8021c2e947520a716c05dc033297b8b210cbfa750e12489a3ec71bcf3a77aec537825a591d1c40a6e30bc471ccd02ce021a7b03fb5394d4dcc437ad55e6c88c9b7a441cb085da3583c058beb975d8b5544ac8f3c24a73010b3918cf3359425773f99e787c1060a0f3e099750919f5c779fa46882b9a2fc60bda1713ef87be883986e44e6e3f7efd8e60aa97970164451054f3a8c7b09100cd62d0c28de11d28a012866dc81beddc2e30a1a98c6eacfb7a6629e2027649268de17236194551e249b6a5864be33fde6ad068a832b22ce00ca7659a6e4497fead24f915eecdd73358dc87b2d10049afe46082289a6d84121a1e9f91048dd0c54949d9762e38775e71faaa3c1b433859266062b633f727cf2578d255f1e593650db51b64d4fa94aa63744978a6330290fd0025f2929ed346288aa5f405b3160ab1381ec6b16d1c7137187e9c603e14c89140bfb6402e8e72e90aeaa3f04a13d9109d81b19bc171a08b95658d2e4f83e9bab0c3cf7871d77ff40b8b5062b1b7b3742489fab3f4f8b14ef0f8372232e5a559c0d393293d9d70518b6b2cd4c9d969876c239ad55c349ec8976e9c03fd21c42c90f7663a834846650d7d7b143706bf8a03c247800806b5ebecbe81927f6459bdd0a50effd74f5d581692b6aff20a94e7046dab342a266a5c9259d8100cf9b4446046c6b4e747a1e2fdacd9e454f6059a35b4bc8ded4912f0001afc3e0be9882b702099343a596c45317c8944fee535abba364fd13910bda3a07781ee2f671ee0a01b531e3cd32413787e48785c887bc7ccc8aa85c18af5d4cbc8629dd3eb97806098789c054a723e5a0b7106d0a7ecc0755608c74f2423a701984009bbd30f625076beb2dc8b99e3d60701191992e125e635c0153403ef6437d6cafdd84070a610bc1ad231726f7aa0e608914509da1cd68f20f71d2cbb3d13bcb502dfeaa65fd081a837d233a2fb9809e8971baaa1466f189be405df4f402e26d0f28974547290857a6cc7879193e54f8ac2dbc42f3acb16e24f3b6fb8eae215a439b1bdf6e240723a1dcd6a8a1f088979fe7076481c6a3a54c1841c67abb4873613858a09f1a0e8129523d6cb0a1d2aef5a7f3b090390a12b4796ed1ceda834f9c4997750a3d00eba2f76a66ba699d20560ff27f855edb77152c8551c5bb90bac6461105801703687107edee576299ab5e468f230cd84309b5db5ca0818911d91c7fe988272a08c52ac5a291e45e6890a52849fdc5ef90dbc3d93b3c0ef67305712a56af8c2203e337eb974bba80feb7706799e77cb5e3738c9c424fc820277055274e24f5c4016f165ccdd3e46eca7e328164369bdc95c68600adaefe90cba45ef24465740508a2f8a712541d6fd3631d1ef1c96223d5d74210836256ad28fb247d110b4270034c404bb4248c5b0279a8d0ef775bdda7118925f25bf1622bd952bbce96522b047091e52481d2857532c2d30e0224c27b15c9c8a9532695a54680bd387c7f650d4f7d0af3912fc3ad0abfc81e4a505d728b8d2d9b1013002cc9f99bf5a969ac07c92c62cda3271262c731461cdf2d9d5da07081c6ddb6f4fc4b19c43ad0127a032389eeade1076c02c9d10cb2cbfac86b81a005fc89441a58613c2016b25cb30694c775d142c6923094462751cbf5a74367d11eead92bd18b3fe39e0ea2d63c0f9b7108c4641d977daaf015558a577bc700a6260a62d1fb6e13909d829ad54d0b7904ab823fb89816d93dd8c5985da32a7d91154a1222259ec29f8882fb1e850c5f1ab0e571da4661ea79ccd08cf91a43c3cf6b4841298760e89c4e8bbcb12a00cb9eab296e40c90404c8180efd65d1fa65ada931f3d9670977d4a4533d0875032d9744af2e148a05737bca54ef678dcecc8777a42c0fb5e9a519717f05bacf0761595651ffa03809c719163add0365451bf4e12dc9466ea51f80e8029f61af0b5c57abf94b1366fa3d686abeaf57f7d8d97f817e0986a4c97a59d5150bca3108271177fc026a66d9bd5961a2ad61bce9d1800c41e6ec76e5080d9d2472e7210d828a23a7a784bec681829377af5fcf6d232bacc453b50b13eae91b7d8283c40197ec181c777c907e3dc5aaa61e581e037a66e6996266f2b14d48c108587ea007d3a118bd8e4fa7658f86d6a73468907d98c6c5c6b4148ad6aa1c082867df0b041b4ed7eb08ca6ba4b92f11b54c3c2a1d176c4d135d020338d125f659555ad706d50ca12df34eee61b990997a26fdf2df2f003226694097c431dd0f1e12f2110c1bcfa523c55dcf3ccc9b6d93bc6e1a99ab46d1bc57e239a11ed3f20f3ee72407beaee8aa6e515dc08268e0a8354ab4606269ff6435f6720ddbbcadee5720ee01fe0e6299167fd1b6da222b59ff7f375efa176f3de30e736b703e0975c191e604b8bca01e671dad21b36271e63e8c7203fcb83b61069c82190728537df4f8570bf49c88ea1e4608925a207fc998700851a1ad3b50909c5cb1c5ccd2988ef6f7044e9e9c8713df6823ab7607ede709000305930dab1f297793f54ac2ab4b8397003b48e6ee50adf19e27d928d63c0792fa2362e3e278115bdc0a528676af00820a6d39e813ed3898c4b0c52fcd5bd91a0ad9b3606cdacf9635080348fa074b6d0dd9d1cc3fe3b0c4d606739d490986fa3b20e6e17ebc208f3a2136de78b9bec308b74148b7ddaf59d650ff0906fcea41d7c04ccf60834612867a24bc4b7c3013036d3d501b933c9aa8bfc4fd92db6ea8982d10011c7b48cf8d463466264ff2c904cd04cf8a2a684eb6358636883cb47b7a17e5202bfdae039f29fc5157cbebcc0d24d47c1d64213fa64a17d69b25dfeb926a7a30a5ba4a69c3dc0d86226cd4e400cf06fb56af9342d892d9c60c395b7e2359fe40a16006af791fc8d8f1d4ae7205d63f6e5f2589c30bf87a12d86ce777428eb0be137ef96b46df6a545726983904a78a62aa9a53e10f685fbd8a72c77dcb99b5db596adfeafb86a66d2ab48cb908ac2419dd4bdb9743a56effda54f9d202763cd4bccbf974600ba9774b410c0c01e60f890b8d5cd85fbd72deb9dce592dd0f629ee6d47e22ebfd65188a59147e0a5138ae8abf6962dc5a617e9a8ff173f51098ba5052d2416accf7625d5d94df029c38119eeb6f19efa18debcfc986ee9b8d4e01d5a6d68d953594f3bfc4cdf30bfdb5964d7265a52b3937fa49d6b6d971b6fa93389c0ca677180211a0301c190272b833b16d18bd9ea3bfcc675a65170ae6fb487bc1be44f2547a0422867bb20e295d27df2e8658b30de1ce16d8949ddd3b27d04251411fca62e2b52230d3e408444729a1ba2e421c4145865d8b99f9b873694453d629233cea095c4c2e1f100d17ed670f8a87a66480ed3ec9f32b50b95b38cc24bc3b297882831a0a70996f0160b7b0750e6ce880ddf12d50738651c02d08b5fae1e78ea4618d39ec4ec97f06be1d7eeffcb6d87e9a219dde2e3a9f5ea4811ef5e02baf4cc69f43dddfc2240a4a51ff4671d4826d2a3f592d3795e0d49ccdd1e19b30e44343f635544d70e6095ab184356778cc98b32816756e0dcc4a3e9b0ad18cbef30aa93d27c242c601084a22d0c9d061c25350ef592e209b03a5f4edad466d45166d54a7fb02294d6a0fbf70279b0149927673d629cff01f95669d7c66a3e52b39b62433f2f81fcad00045fd498037b5fc8da56ded0216a66d8cbfcddf2adf0baa6ad45913fb8c6b0b009ec2ebdb6bbdbc704e634288ed6f644e9eedd23c9afc74fe20037a27016fdb05047777eb66e0c0950ddf98e26ae8e51051934e00070eb3bed3622781dd2023050d00051ac182fc638afadc3cd95edb75c80c38680d43307b7288461ab76f3909f9d8fe414fb7fb68fa84612fa4c266962469dbf8745f76bc0d2e0ba83b60b00ff78c9ef8be32f69036bfb0849ef9f32d742b3693d102905b43df77bbe5ab070e52df078746535674b4ee2589f79e819f85dda9dafa3b4101beeda425c283870a95767577553260ee68cdbfe364e63c0b3372847e00fb5bf3c817dc3ea7182009bbcfbbf9930323993a38df5030a9f258c7f41ab5b803eca4f1245c3ae5aa99038cfc0cb289f1b73b4ea0edc6b52a4acb24fa2e1bfb5c401d27d8e0b70ee1b80dc9698285867cbf7b1d1ad5bd23b3a1d135070e2fe8198b9c3eef7c3e1badc50c1ce39ea8251ef0dc91e53724b4e84e0183ef753df684c4b460754dacfc8b160b8f34c1c4bb1de364353e8efcccdfb26d4340a1a5bc5a238e75571eb96e43030b8358de0d88327d6f112cd6ce0f640a95b564ea7999888466638b0bda438b580e950c9b8fa38899f091783f5c0e686a1d306b0c764a78cc7c0a679e744d813e0238b4c0562c66affcd5fe659c27c8ca29f0c5759ae6115069575e924ce7843d02df8412079582e12e2be58019fc6f22456b6367c9c9216eca68d7bc392f08170fd0f67f9a142e98ef570dfbf4a7abf9d01ec40dcb7fe1594b4edcaabf04e27b08b239643e5d28923166119665407586cd44926bca5671710be5cf7bfc6173c30ae8a8d5ff90f8e78fe98fb2003a04a43a7d3ea311e808546edb2bd978ee349f0bf1cae6bdd5989a93a55878ff144836a56dc7325834b1f06f0d74a85ff138670770e0b67a92942ac86936dd20b1dfac9ad4dc1b2c5dc459805f6afd2b973d160ab0ecf481faf1bf274a6978f9dcb1acf32061005f8d38329ff1ccd828c107c50674c77fd7f4c784a81c6a6b4b31e22e7be4148bbfded3cfd478f8706acfbf8f0428b6b0c3c6645380a8e6044bba8371c18d895572e17b204d8e54a6e687f9380236eac290d84acfd089ae8fa82c995b292950aea1c188a0fa35223e38d73085091a8c8862a6936b34a3723744e1da6f2fa41dcd912b14061b8de4b19e5b032309d0e99c5462575b3c7343b6a4aebac0ac2072ef31b9ea8bd47fc089e862b04e007fabb3f0cb4e6e1217fec843ab8f4507666aff47e882d4d367f14c17d0d2e90c7ed7048186db17dab048e1d9bbb9efc950382828ad6a5417f19f615aa500b101baee484c5c2ae6db821dd8954a31a9374093a8d61f93b5de70dd8a78d747080f9e3f9c55e5c094bbfeb3bb49ebeacfd00c9961801fee73ded65e132a60e8fd033a74f19de4ba291127b50303982034bda3ad174c95c72a019e6b4c6ad439bd071b4bf2b16c4e24cfa7b2f696815338b733c6b441390c0fa621f742d90e6cb90457ad1bdb020bb07a019992a750750dd35c9c14342ea53139eaffbcbe83dd840f01135546a59793bbf054327e3ef6849fb60386542f306be1d92ce0374387cc0155a0ecab065a16b7566ef9809009c4e02eab1bb11e65d56efc370d5707ebd301719bc91212305a3c1e15262fcab95565b2851a42a8efb02491fc0ae62f933a0890878f126acfbdc21181a05538446a8512bc94272ac18c21aa152ff0e1105f0ea96806de8ba2bc3bfd2e8e398c0d4b559f34805fda6e6d64cd9f35c5064c5e0872edb30bda7f405d4ce2c823e70656089e011783d77f1e95231185eeaeff210b1df82843f2b4a2b0535c38f415ec0e93cf9785c4e1ab9cb25ba793a2811d460ee6de6289ad56de3f8bb9cbe628ef301e1acbf394d1373461cc8318049ce86607f6ba953dadd15fb6dea6aabbfb119687ce8f5cd36f905d6d9059535efe8d7502c9384a0f5e4ef7645b36a5e516187b20192cbd1c6d3278142c2e829a9980a105e31324a47998c1d0c6885f2e7edbe71913ba6dd8b79e651566f402a2ecb6070b12b2c7e93b62384efd51a792edf02b63f0096994754e846c07a6dd804023680285f9654c0fe2f3cda3a9e52b8e9474a00b8e15f959cd151da93d1f11c40c540b918b68a189e5a4a102fce3731e12ec0dce5184d3544ea659ea5211caed66430616a965962cfeb1c38e31c3948809b1fd8c77082165afc8cc6da51893a3d4b901aac0afa2471ab5576f183514902f39d6779a9a54e3ba5816bef557ca0c35dd0273ba3797375490799ecab1f196edec4f6165e998f83d8e97362559fc8cb5bc0b230ce0876b73f8b82a2d07e09a3ee8987f66516739c78b5335c163c55d257f0c5cf569d28acd154de447d970b9e3320b7022ffb2c8fe88460d9f81a03f62260e27c916bf72b86d8f07185e3f72216557c616d5251c7a8f4cfe45c349fceff305438c8e334ead6e2c5794529edbb9087b4a3ae57f68fd01d979da3d065e704c0e6a2780bbcf62f286bea2d8f78c7a63b967b629d00a16a5731cfd5cf1339f6702c61561248f1e4651d6914eb5db9228f1cff0f04c9a173e910ddf0607b3a48603fca6e770f42a008d5a4d239957332f887ca4db45e082861aecf991bfc9bf060b11e036d9caa2ae07af619eb1f5ba082f0e20ec6f24279ce752ec7a6d75e6b301d21affd3b23ce9082fb68ae24ce9a04614c072371025c820b4014d530267dd062baa78a8e6e8987357d9197fea662b9ea2e6b9f4bf986287d987d92e7315c00a827066c843697d7646186cd6e446efb1a1c36a259cad3f94a279ab044c12e50dd418b145fa43296d01baf33c3ed9ae9a402cea0ee2a4175937c8800496375c09bbe92756b2f40c0dfee7ea8cd0a318c209a76b00c0ae42b0787be71fb87e5800efa4ab8f1dd9850e7873de963a5ac14ef40348909255dcd3c90b18053f617a0b50f90298a404163a9c3bc1c041de2e3caecde9bcf4f75faa854a543e0dab060bfd1127c40f03b73526dc16f68522ebcb9fcac9bc1ee66d80cf95e5cc43b4730db44fefce108ca64be56ba53cff6077cb222703d9f28e4a7cab64a8097eb4ab08776b22eeb9e4b724cf731096c1961dacd1b42a0c966343c9805df4c9248dfa036605945617b3933d10997f1756bae444bfb8abf4a29778ef446198455ad5360001a41e33056d7c984da009f066acfa67c1dac27e4c0d847397e853bb869d2c09baf820e0fd26335cd9ff6435dea48cd89bbda4eab984ccd8aeed2fc42ce9a9096b13f572d951c83a2988f97d6e702a81b789f566a4c8675ecc4fc9cd7906280d1f474dcd6b1a12a1ec7c5e09202e17453952518393443a60a3f500a29fad820237729e5a805314c114580684b4cf8b80fa2effbe48c43bbdb4d07d831a2412050acf1762ccf5e96d6a8db03dc05b21e3adcd498d0cd7bdbfb196e5ebf33ea6074890ae4330cfb01beec7751cec929ca36c53b46b92bdfff1a9c26907c9bde9089fd18dfd62c528b04ca7637e3c291a0e24ca9565c90ca6cdfce6a241ae68b709cfa503146b532d8b4c44c906d56df944624cd79f8880bdac10102d8d63b05d09dd363ae9fec9282cb42ad80e1fcb364f7582ae97654f232b17345b723880a904 -generate_ring_signature 869c7247a0b0a1fb90d96cfbb2e3d17682be83f64ea5718718768a7ff74bdd41 cd4c787f13645ac936e8e981b3c725d1bf042959eab770f7c28179f6f37f959a 50 64a0db89add71f4dacf4b369f61a58504533ede4d3740cecca23b82f3a75f4ef b37094f708fb5ffce04e866a09c3aa4ae4e9693abdde06a7b22dff21330d2865 e80522d9eaf7c40d7ebd82c81d893dae50ccc030c67eae697aa0f7df696b87d3 603bf0a35d784c9788ce83017240d87603463c6c41f61896f24ff3a54e5183bd 2879ca5cbcd35d10079dd6b0875cec28e8f177fd12a7e89a597d4041c60b28e6 8f16a24090b39a5295926e9f7e1e73b85fd8f12f96e58862d5a70b5b8067fa74 af4aed48e14d50dd553871225b4cb21dc488041ec203ae1f4140c3a46595ba69 91619b1d38238a1d1ce66c10b3c474893e0484a3af833c55a346d08df4520b2b 4fcd543fdd76e2a7d5a31e6d0d39ec96e4d70b14ecf3419d8f59f8a87e31a26d 84fb3a395fc0fe8ac6bb8fa697cb62d3d56cbfe568c24ed6f6c973cc674e63e6 2c9215c4af4e6ba4447a7ff5c92670eaca21bcc140aae3ad637a93ffa4e0c707 5384ef060ba220e21f947772eb03716fb79aee73e70298e74003d6150f4fe609 ef2500d30e8ece7ae0a2ca2026d4f176ace683edef6326a16f631e42029de6c2 4702a38b0795b6c71a0b8934c2b43b0420c29436fcdeb1ff04c5d44470e900a7 bcf1c1b26acbb158b8d4444fc9951fb62388c9e2e37cfaba70a676a0166bdb04 32d56ab2539721810ada48acaf38e0d28a2dd6e4eedc2dd9c82cac01786111e5 9c3321f4e62d07e104398e1e405823e9e1ed36032ffb78ead40900ec52779db9 8b732bf86cb9368873cbc81c0390eec33c88cefdc66d4063c54b09ba3aa2e78d 2dc91d83ec7a70f8fa663e49c70102671b43eff575c4ec9dd2ebf3abd7c03d61 f982af7048136c5075ba09d0d82da852eb1ff9879a49967cffdb181dbe08d576 a603ae7cd1acd05ddae21bb0803fd7d4af1a4fd56f995abc3343b3d86827093b 296b597866b55b7eea58f80d58ddaa00b7be7058e07b24563c4732a3de2c23d1 1a0a0ebf748500c4c58716300a78bc636d8616b3c6d4cd9d27224ffc6ffd21a6 2a2ccb830271f5438d1a91b5255ba79c629261eda2dc9237d63963c2650e7038 5644c2851b08f83e350bd96db37015bafa8100cdfaafe8d9576b7e7b5d52f76e a69c652a4d177a681277d5c1b4ad6b9ef22737b1d49789380dc890069aaa3046 3e7facbdea7a85a4b59ff60f20d441179fe72d3e07719597b11f544341fbc223 55358eef614502ef018661f3092b076e66612215a378f67e060fdd3a625bd41a 92db53a847ef3a1e0786d07ec6dff53daf37c2358d1b76174406a75bece6a192 6c248b764ac844f7f20317da6ee0ade57c3addf491a6309e19954df847fe0b57 c2b3c177d65920dc7e16352f4d4a1cfa82da74c45c502ffec6148240b048da0a eae88428fa3f81f6a565c6f71a34a1a282550b543d7af367ae568cde957a5adb 347737d568850ab93cdaba356e2f2c932df6a8b41fdec8b05b9104950766e803 40473c24f38722a5db64db65f713d670ca27db39726f32a728eb93774a89ad9d afb366a426ce9f1906a9d3673aba98e048aac4e4e668dbcb17054378cd42ab44 e9f3ab459f7194bc370c362fd7d8a6a75ca199b9ab4eecab528939d27dedc48a 0d4b4bca9ded630b3612d80d468b504d0c180d0cb434c46ca42c68ee98dc2cbc e1a6e0b00675b3b136853539a4e7f82877bee50b7ecc3fa26b8bef50ea81e5f9 7ffb6985097f8b43c0b1d8e645e7a7a92c44f72d6fea1ac90e429ad8cd6bc389 9cc6611070bee3ad7b9ee4cb7019a55e89981fb33689d4ff7a876d543d0e9112 e7bc5ed886bf0e3d7f6f5c226f8fcabf9d1935e7e2d609932ba263f451cd9d27 1c65db720c1282e65a0e49d0b06ef80fb5bdef56c74df1989eb020a3568bd4ab 7cc29b80758c5528e9534bf02225fe899ce2d91bcda453e046f275cbe0f1f9ae 51094251b02255d1ee35ef29afce78bcd0082a50dc86a164aababac34c46bf55 ff25ef58937eac01fffb1d4c73935ae9852a5612d26c13174f7199bf48a6c25f 69c882e768b9f5ab02710125377cd5f2e137d22b25ab1e8e3ea7421e262391b4 c4fda0318a3ddcfe2017ebf0bd023beac85fe29e49ea175b75846fe20a97d461 49eee66a75a60155e24d1337eac7f8110bcd122a9a25309383135d43b37a80ee df2a2fd55bdf1941f0a7d40293da98f050c253827d420f6e020d11e81613a0a3 d56f97969fd7002aa6a3b9448bcb83354ddc3cf148c4e9a5e36c050fbaa7df42 b61e797b24014d16d9cab92b84a7a057a1a05054a1f1de374cd61c040fcc7c00 2 4183e534383995cfdba1e890eecb79fb62785c085c966b0a95226f526cb10a046e693fb62cd3a2c16acd4692ce06ab668a178f0af8f4f126878fc69863a89a094214f094d6c89d48f7b743bc9766706c7457e64e53b1e1020bb79e9b0c691406f2f62e0671f61ba2e18f2aabae91e692170b953ae2e177e73c638bcd726ade019909d9f3e028b07f3a040f43647803c0a01f1cb3b2544edadb5180e8c98c9709eb21e4257463721a270d0cb34da4e4d99a441700c93bcbc70d84c505cc7c9e0c85a172bf87bb0f6b9a9b9cf3d861577fe87570e459710dd0ccec0bbb0adc040f9bdf17fca8217cee35bdad24e41e4619ac237010f54c60686b252c2e4afab30ce89281b0f28e1afadb7af51fc2b61d51adf7432a6acca555c3575558e6f413032488720c172e0865a0eaa3cf8a11b7bf714d76c9eb068f4edfa3270df2139500281e570a4ddbf51d5e487bbb9962c1f6a8d552c4df717ee1ae3b3e4ceb05f00afe1eec5bcee035b1ff64b5e5508c162ce396daa938a952a29cbbc2968dba390ca0f5f97c23a2c9fc2d22ad49d1cf470d3426c5bdc5531b503c11576a9ac52c0cbdba208e30e2d2727a7fe893aa6b1df6e327c64123dac1a08ceeeaf860f04a064769c973e446e0b50b55693c0128ce7d42e3963115070cccb077a2eda5cc5e035ecc8bb77be23e6e15bbc040d6d2275af2e1a419cf6b3175aa0d914e0fdb6201d8e5642a492887bfb713b5a8c75d182ef883020b3ba0936a445685b46cbb74089c605f786e8226fd38f3c92ff91a55322639584ea423df71bb84d7aa87181e02424f4fefcdb1a546cb6bc78c9979e546fcc43bf2468a85a60e69603ac7aea30265b5abcaa22f9cf79e01f6f15cea4ea9fdc9e3c450a5d4f16d8f907325ac680ef926a619d8c4cc94e847305c1b20ab3069fafe02c798e0a79319a84080ba390cbb4ab06c0bdfeb70f02beef2bd04be87eded76532aee885a4e24b23dba353601a5dcc790aee025bc3b4cb5f1072027c58f2f639cdfe2d1b8a6b58b7084d22c033320199435aed1e54ecd0f57f639e0216b93c8582a6754a00f7a89ce4f688f0e497b954a725b3a62b7561649e53e73671d61cb4e0318a905f08b4ffe002a5e02307015b2c3fa9e6da1625620a925d2ff3727296172466e9a345f8bd2122c82098be157bb4cd58e9edf0fde10e652ab93d05980762ccf6a3c7a4d6745a6dfe80f3f4d3f2a610ce844cea7887a7b14d9a105099b7e39140b90c17b3990c9401c094aa5c6e1e8c107dd1af7e2958e2715494265d81ca7e362cd9c6bd61ea538ad0c4ffa4d8d38245533454c9180d0eb9e34fd68f1c877540ba5b6c7d8fdc4cb2c02f5faef49567f8cfba896f7bcde64d7e90bdb27c1fb88b114f3ba0794ef40120686b79edceda4109f272c54b6f36bbdbd496ea8f32aea7c86f9a78113423e970853571082a4229478d23fab17558550efa02a0da8793d1d9f9a06c77ffdcf77091afd82034b8e46db301b248545cba23feb78ef3591853ed78b657c0e714c9c079d65ee2124bfe03f2230d2828d659a6d0424e1cff8d5780f8a3a3a7f59a41c03b493d0a6d22820f98b2c58d1ecee371b5d123dd3247ade42645162c9ba9ed109bf8db464ce6cfe11d9a8234ac3b34c97b16bc2325d0430f5cdab7e5a7d6a760385e186dd93e84e57b2ca645a2eef108d259e2f273f273632ac20e0152e04140b0f3ed6e69d6af352f1a06b49a83002256dcf99ea88a4a1ef175ccd96ce93820177fa68184d3e9cbda09d8b7a9027b0776460b61492951941715588c29bd01101fddc9a4e183b4fc33af3d2d43d33927987889f6bd545d3ddda6fda16262fa30a6c2649fa2cc30df112a0476fbfcc909c272989001b17fe1c184eabfa355a1e06e558656cbb731dabf8d483fb9c49ea74e49555d25a910c1fbddc301cf3c4ef09e1d7941fb5f2a5276040aea5e4ae54361101de06e3139e94e24eca95640cbd0dbfb8e289d335b3d1daad70b96710019d98aa7f74b61f7adc99993f20abd949093b03bc3ef2987b73425df926586b3e83a7230371653d60a9a5f7582bd6c0380aacf28742e3909997d16b7b528f25fa475c188aae67e51b152c2e245744578806c359ab4773595a8a2230b9979cc37267322892233db91a4b3b7c781a179c93004957ad257a75f44ee0988b6e787750abe90c3095be44aa35ece10cedf5472c0df33dabdbfe526a133d0b07798898babbcd3f09a3efed5c91f901cfb8adeb0b0274836bbaf074d7caa22163b722a1e65c8cd301b0e69a8eb232cc7107fa6da60e2f75f65b87979894bd9cf3a4461970a7afe77f105c120abdabdea1889fb8820d5b4cc2bb8011f12123f89ed074a19f89bd7e19e5730ca1d95ed5f4b1ff38b80eaffa55c2a2c05b76745a3cb58db2f0d6f0db013b4c457228786953e5a13bbe048d954b4728e6bdba9f8fab277e1a008b676cb36ccea12e45739c3e00ed031a02e8cff03b7ea8beb9035b98d2459a250b69fdc9785635b42d1db42b81231d820ea5eb39aa9c4767f6b43b7269597c6462c193760333d821da73e6935327fbac084528e307cda0bda9947fcd732ef3c54246e3ee3b394ab06608b87626bc97e30ed23632a6ced000af0c795a1c9b5722417f17ccfb58bd80b3c1a854a9326668099e433639b9b28552da5b1b832afa54c83859971db2887d94d33201e31b694509f7f4d23d772dec780b7148a28d958ac2b4afb248434b9ec5792ad0b04045130c473e8bf72eece12bfab252fb6187f88999472581c8d7ee5730faf7c161492005ac2345fbf684e1e755e88b23b8fe3d0dda4bed52da6837cdf637b2afb8e78105dfc2ce19c4021f908d5b5f42938f367dc6b7a8bc8781ef28dd9b80b0230e420fa925e61db3ab214b0358012d1ae09d7b8ecfd4c9c5b66f5533c4e657c7f5370ac6016a6c9ff46c9f1d29be4d14b77813f3764da1360f7a85ecbebdb6f2607f0524a000f731aaa124ab2bc9775582d91a8ec11b227fca7876bb2233e4fb8a1603154272974ea41da40244cdd3108e182bb1175acc9e245ecf0120c9e335fcbf06990b8655896a4d047f0f63ccdb30a9b99d76a62560fdf7a7c8730380f67823096e0b1d698367eb78832f5230e6688755a549498e971b8c3b335f57c9d3122a08124849cd8acf22e5b6193eb778f29e013ef6f20ce309bcdc4cde42450739020b7bea62f7e877d2d962341c7c04a9cb371c8920d8bcaf3b5b1577f357d4eeb30550d2a7b035b5bd3134026d84f6792524b66ad775da003e06c018b9e6e0622002eb6c77aced104b6a71c9fd2fd5e6dcff4997deb79436bdbcf5f243c9fa613d061f9f1c57de7ff728244ee73744722b3b1f8eff492814f3369b08e1b51c51590afbd8ea37cf8a23a8c4419fc39b64e049dd740523bd6b7690b32d8bdce0bf9f07c75050c4b2c509cb3e4e84643394c978d600c7ce1985805bf8d40839de3588070cd1d0ab303c185889ef8904d2affaa1f1aa42b071b6d48add35e27876939e0100f999941e5b8b32a22208f7a09d753c03bb4d7e5023ddd422ab3848728bca0d864bbca616d7b434f3bc10d86e069074bd618754a5d42b931abc861a45ff7f093360324ef7a6764d7e4d41993d457873f40280327c990536adde132605d60e025f0fc8038c2c577a16b9f7868269a998f238abc1b1dcd86d91d15f9c6b0b63011ee69dd5191c43ebc5ea6b414226d1fff0fc98b5859fe31063c0fc7f15dd5605875a262a79986bd5371db23fd2f6fa7b2e033a28d8665e46fff39c1895ea8608cd0aa32ebe39e8b0eb8e96cf594419901c73de9b168756a3a49722d83c2d470e916b2aade568434d123bb37aa30211a9c972164efb900a152891c20768839f0d2afd2fabaeac299405a92d8941731d86f5aeb7149d140f17a6e950d92361ab0a1244341273ea65ded016704cff76d539a5560258ac11e612e3c2fb8dc2b23b0b2caf309e545b679cefa2200cb5ca63956b7c58e7e7ed79827fbac2cdb188d7030388cd2ccd1dfc41349bb5aa2356ec4c44adbf68bd29f06210717f6b6a854d0182278ccaccfe43b2aeb66d58ad9e4d9575f89b1929635cc7824c1210a377850c605c943293f9c15a890d384bf42f03ac233f2d92028b02900f8cafbdfb9d0a06a7f2784c0c2a3df0ed61e9c4c48d570bec77599f3fb3c4cd60d27c0b41f8b10e087e8c4a9119c724a918ebc5638be8c94a0d88151434af46320031bae18862018b5c0a5cea422ecccfa854d0c9ae4fa41ddc2cf5d1ee646c7ff652de27acd703ca5b9afbcbe46a9f046a3b4711b8dbd272b46b5b2e9860604a8a4ec7175caa0b9e791b3e35c72de0ee3aae342eb420ccf1e67a925ac619ba5a91f9837ac82d06ea9f783db129eee6e93d51f679dbd6e90c39510b6e390c4b764c218df059330dec562368f8e82c244fa6c727ecaf42cbbbb26e3e677af63c80622ed55352710b538273db07c6fb56c99f429c7a1af2913a4ffdb2f52db11f8aa6878c6c5b2b0b -generate_ring_signature 1d0b119ca5e69bfccdec2771ff272dc2c8638c187733438939525ccb8005f4ff 8645d7285139bdfe550241936b2fc8a26757ffcaa7bac9084ff8a85cebbd1e89 3 8a075c4f74b5520b511ee1a487cb31c4cc93befa5af0887414efe0115fbac0cd 5407168cd81fe9cba6a34e7e30cbca17f448f2226131ea57acd74420675830fc 2ffe6b476ff37d8d6487cc1c7e5e89ba0295a5d0128e6ecf8ae64165d6d14d84 ffacc2cc992ccbdc7cb0cc1a695ca31a92f6d6a2563b03349688a63a86f8ed01 0 d56755bd0679b1be0919c5e0e278196520f2bce1726837f3f0827eb65670f501eb7af0ca3a5c41cb0a1e845518a63ba7dd071ee35c2b1e82cf8756a91218a4024603069ca1eaaec9792b0e1bdb87d12b8e5136b08143072c6eaf12a0120181085ea01c5864525b04f2face202dd1dc4f4cdeeeaae228a37e6e93dd62fcc8e507ff8361c115df826824707075c8ce3863a5ed56bca381b16a29d3ef839d4d8c0588a634fb9f19698bde67c2f85960b546196b2d503f941561af764750f2ccbd0f -generate_ring_signature dc0159dbf72ca06954ef706baaf14f7c84f6d6e96aa47cb0fc100a73f21506ca b0dce2128797d8cd8ea9c4dc13be5ffa76e3c68278d8721835f7bea75d42621e 155 55bdfd958e609cf23a67c353b313cbbfef67c4b9f888aa9ff96473fc9c841e61 5cc2a7c274ee46db7d80a45e09bcb4701ca0b6c651a3f71698df8681f565da1b e1ec3ba3b9a5c1f93867401ad6f4b8f45a81fc04b057bf48198f93507db0c45e d26059d3c9f334e298bc08d99e64060f1c68e8cb0db08904f3f2e989c8eb0da2 06ff2aa52649f6205f044d65d691168f3bf22da79ae46a294a362c23fd674cad 376f687a817012087ac04320874b9e169806b3a384ecbab35aa7caabe11af3af e6f0454df292cffbefc002e20637b78ae2c52993e2f1d7801fefe9894babaf97 965c5c80ee12cdc2b0ef724fea7b86aba5ffa1ef7a8d371f634177fe0c77ed69 5cf4164fd1d89da37159323130a12b0dc4d22b6743f7a14f37d85675cb7711df f43d9857a3c0ced069e7e66e99416b0f4969010ae1ec4f90568fd9bf1ab42d27 15a59c1dd5958f6b8b48471d7fd113b4a761808409dd5606b5eedbb3fce5749d 4e8c6e51b88c5df9d2f41a66e80a5b443be79333309de74e36d26867c5260f29 84d85995ab7fba0a34a50d10c6c9ba52922b7f5f1541e7740dfa7fceba592b3b 04409b6c2ee649e8836796a2d0413dae06f8f41cc352fb00bfb108a6f608ca32 9c207be7f2a862125bc38aba0e04e98d16d0d2ff2d36461c0480716a95ec0a36 39fce2d41d67bf1f182d1ccf23e140ceaba4322266b10ca3c950055b6be5b023 fef4c2cf18221ce0ff9810a1928d9168e1b88db9f42f668d74c2876286c848e3 d1ebeccee551e9c25aaf9c6f1f6334fb5471e5e94f46bbb1d80729919c5691e6 9e7ab6f141bd4ecaa6d7885514127168338ff935f1ffc56eaad972e19b807aff 4aa10541e1a9124fcac6896b065066ed0d73150c9e9455f914e4d96ec07adf5e 32ca3c20ab39e82821420d6e2e37cb9a72f19d1df5908fa1fedb306b16c3273e 8172aa6cda51efb16683412242fc3b90a3a37c9381e4109c1f00521546fced9d 2f9f8b8f71b8e1d5cb244c0f0a14caea1c5d77b13ceb6a2ebf2b600ca2d236e6 719b3d1256ab1d9f286f9cb4bac235da7131be9dd47388b91c6eac952c53312a 14a64d08f79f8b03c321fdc291fc633fbeac766125cd5bb1293028bb7e86e4cc cdb1c8bbcea2d12159ca80158b1d8944d839d82f94e1c0c3ec84c71d00a67659 aface084d21571606d8b913198308fc78bbfe4fef087b2cfdceb8c335d756d43 66502a8fcec2e95a45c10fd5288ecd64e1f880a6b7757a3921e14f432ece527a 63b5a9153f88dc28a9dd325a81edb0bb2fd73fa2ad2aca31839f9350412b357d 7c377465d285f2f6d31834ff61cb8a9cd5ab2fe99b4ff9c330e0d59d305b21a1 ced17c63ce3dba87598c1adc062f3a8af81d4937447ff8991f7c780bfd9d9631 d33c03c444dad20c86164d634bcc2689be5724091a95c006723756544a5f88b3 42c6f935724c8f7f3f5c53645123165daf862cb05366a895eca0a7656cebe76c 4950dc9ec99c46c3d605d97f96407e9de344b0bfb7b19f976c9c84a18b3d26ab c813aa66c11bb66586ad3af0cadcfd7f4678f9ce71478135b8cfaf3f7fccff8a 91f5b936445454387f607698a4b2ca38191ae36dc6fd0ab0580ca4b80b058d8f 9b8741d58259a1f279a397da968d1df565d8e133ee7c69e1ab72448660625adb 4a7c1752977c764cd5ac091751bff8dcd41500f38b7c3baf458963f2133defc3 2d2b4a25e1f4a864c3f45e0bb5808484bb51d2e35b9180e3565dd2945c13a9ed aaa64e83a28293a0f8759ed4cac37082552d3935c827293da30bbb5d3403613d 66452a289e79a5361719e68d911741f20163ef4896f0257585bb9c832069c5c3 a14e0b54b0e02a7e81fa314190c273653926c342c819d74e9aa461300f238855 1c1322db318ccf855d7a5ce5a6aed6bface24d4086cc7b26ac67733be55570ca e10ddca61c7790d94984c7ed066543eb76623923557c610ec2f8de6f79f635b2 7588b14ea4f0e4a215d9a982eda345bdbc6aa4cfd04511867c40f9011556434d 409d5ba7c80a7e1c29df9bf923ce011ef47e284c0bc462533245be82213b4b7a d5e6a2f9f50e6e03b514ed898ac28f24fbec78eefbb3dbe0a6a00627f72aaf4a bf34dbc0ff7eb6c8a74f111324fd8eeb2be93a93c8975635ee06b526ff1b3ad2 ebf00d242b0667a638b7c93c397e45f92d1e8acf73bb2abf73b03490b3b6c3ae f3688388ec40d6680cdee04c6f11d79bcf9eb4e5ac2ae253b4ba852826b76bf9 0ee543f8b280ba4381bca59e1fcfff6dd425851c4119e251a7732c99b8d2f168 d872db69074557c2102d81103935a90bdea94ed0b0200c3ab0847b526e29b4f7 d4db1b4947a3e86d998ff66601af2f2da8cc84e9d724441b464143b171d7c89e 746e4cd0a3813757876862505807b4b78270c0a170b888a3bb9e3e60c0101908 8d3606dc0218adf7c809f52dc2c19c9aea02db66e95b21a4dffe48680bad98c7 721174c2d937c62637e492c02253ee2a7defa7d4a3b6d5bd47bc6f0addf40ef6 97bc500b936ed73732b36010e062fbdf149dc2a5cce48c1a648828156c956c28 f3422baed25571091c63261b7f854a389392cfee89a8aafe67b0986c77f933f9 3224b4758162f6a3ad94d76f096029d96b8542e883a50f48d189fc9ddd927efa 8634ba86f40b66d5e0ae98089bec9c49bf7ed348c857e9a8ae6af9fc1a1f1f66 afdc97548124eefa649ccc49f8c4895e1503a0b86e4732dfe073b18a14c6e0bc 5290911f8661f9a415535158f2effbe89a9178c4194c922b660645825e234584 a79798f044fe2f369b259d2b59c949a0cff4c4eb54b5464a1c07862032180273 36f35784685306db02646934f6ee0734226dd836ee407bc83ca232539af2a10b 1a689224e864e3777accd246ff0ec5ed4f78b44c7af8f551bc9b411f8614693e 37d040f52915b13ecff79dd88fb88f109f091a5716ec289f72adbcd9d1877221 3f8d7c20a19fc64d9ca3b1eff52f6c4c062d6ad85e9b242b3d16618cc5c359b7 8fb07bf7455db11b402bf9548947b6af97803a02577e538e9ad452fe51960335 e457e7a04242381202e5ec2f466205ab1cc3fe0fa20a97eed9cb1afa6f04664c b8b4761d93cb707c2e0fc386991643d770ce3309203f1b0c8f182ef797fcbd3a 4a5c2e5aec7e342cb8e6cf16f17678553cfd06e5359be5994ee6e50bf572fb22 5c16c16457e9fee9a62c4ba6b6dd7e46aa1f3f85d68f08629ec7a96b85118869 b7299ed0fb01db3149fcf30dd1d4ba8c99b9384bed66d70c3580e042a6f7833c e6c61646a287ad0a53d582ffb300a86755164342b83f3a6e5fddd5471068658b 21e7442030bf8d818a79e5674f1b5798a576fbb1ef84472add4689990f62e8a4 7755f1fcb777b9182eebd528bf3f60f6a2da148514e90695acf897196bd897ac 8f50370b4a8db13017296ae820c4b728f46e8244824e590293dfd9db7888ba5c 2fa322e48e87ea8a9ce4f70ac45fec4bad47ed617817a39c6ebb38bf007f3802 add067ff6442a2d93b12b6de049b5fa78541e45dd0a0f74c2223ad7c61a7198e 46554f1886d0ba1aa9e264a5f29b08c957b5c2896974664b222e60b6e47e1b0d 4e0a19f0b65791c9b0b510e49c670cc06bbb324d67f657a4028ed9f03784d8d8 386291c7799ecd8cdcef7fe30f6021f42ea2918b06be3fe8df2f47a0fcf2d616 2f4a5d75e0868c2b8e523ae394049ed0ca390bc64f641ba203b9b4dbc8d41d2f 841f17f2584641774eb003c751d0ccae29152dbbadbba8eb066a2ded5dabfe98 d96e7157019eb21d9ca9ddad08a2c2cfdd752fa10cf6aaf11089c180f6dfb13e 7360d3476419e86cb99abd0a33264327b0b5171557d812bdcf6800ea0bab7131 463083e7e2a8fd2d956876dec974816bde097827365b94a1494d5f7169f69686 44e8016a6c0e2e21241c67bcc8b844793daea164b5e2a14baea66c3c7a3cc773 22764f41e82557790205bc987553010dfc5bda9d8b9cab62def4c881e4ffabd9 9a52e8f5b5cf1bcde87297728a79c4154c272fab3a31505e9de06cc22db12b20 45ecf071a3f952a3886e388374de59aaca3109b8a9424b7cc4e9844de93f3c38 04bfda9f253526a91d0a4eaae8e8449bdfa9d200346509ad9fdcc90c9bf72046 baec1c08bd22e1d1803758f86de1dab9d472cefa662c4db5da8bdc3efbc19b34 efc658768831e5dd37bc425c2d958f14c088f72d86651f63099b6a59fc956aad 8bb4c7e070bdbdcf0bc3ccb85a7f636c40f6855886fb9e5afe3663c78ad39122 a4a4c96cd0361853a0f6e464a2ef681a05d30501c71191f74883a7071b38ca74 1f0eaf33404e3d3acd817f9f8a49200f6610b730b03dcad2c75829a92b67d8f2 693c7c1e884e24acb6ea9f79df9a4d82343d2f00a36617e80b2341f4a7061e9c 57e1c0ebdb580b1ef2eefe50d96d81d9182b45cec86b8c2bd8bc70d8094c3a64 10b2ab4153737474de7bc454ad5f247fd398761bb29f665cba97dd2a446f7397 e80e6ca216d809401268bea920b105ae5bdd440577b7a9ae2981b3d75b39bb55 967fcc98c8830d8374f52e19229f50d39ae130caf369227941c97f432d6a381f 6276cd137870acf4b11d90c2aef851894fd6aa8d8ef3a2e6a7464e0daed09c87 43a43751bf077cba57e404a62eff1082d66777f1e304f0251dc884a472d65730 199e70fda56e43e548af5bff218590ec49467db20d932ba7b93490df9bb79ed2 75621e025d68155e2501289bf8caba1ee9b89e6389fd2ee595e72baf5ea3b34d 6b7e8ef6c4575bac539bc5fb12d6428cec7286e03ffe1f00d4996ac4214051df 3a28bc22d5954f34ab59bca16162feecad41b3a25ed1008b21729917e9e4dc4a 8996cb1d13337dcffb5f57547eb7c6be1cc2af29d9bce46c4dac048159a15920 7b0eb9a962b2673a4c3385f17fdf9059f202421a36ec76a2190eec8ac1680d1e 6da2909d4ba18f512cbc36a2221a2e16d99a78c6e526e802fd9fbd38b8608ddd d989d9c710f84bd67771a252197af590b63b424ad1c89d623181ee4dbcc2acb2 5f03667eafa7c0eee3c741d4aaea93246d9085248002cf92b4fbf664da891e5a 4e885b308279d51ff6ba3294f150cfa79de993e4b4fe240e0383926eb1b72a7e 48310fea7a0b977e646e440ccfd050663c1567bbcf716c18738631be861bd928 2d8e36d9c5d5bf1a121d9b86dfd22dd40df765a8640383aa3a98e86815f6b202 c2ba1b76ef0a6ffad1f816d5a195b4290ea5bc5a195c21b99a2205a0fb0fe19f 9264af1aff612cf6c5423fba55d78357d35f1507961a1e5777cf9324496ce5e2 ce48bc8a5d073ebbb81bf4e4a31d30b43869d47aead425642fd590c2c6b71ad9 415ba1f571226cc741585b624b408f244b226a3ef68112a787b7f4ab117b7070 02690c0871b65ed57af53dfe9ad9af13fffa80e17d559e3ad8bcebec4e3244ae 28a00ccffd9aa89903398ac9b36e8bf24e77b25000ec0cc05435f70caec00f5f d71cf74bd0d2adb4721b4c2a2601109e25b41290716c2dbd20cd71416560042d e497022d82a79c271eda123b3f2cb12d7e050eec55c7ba3ca5c18f90456df49e 5593d828a1c1ad5fffaff2fc0e4b82c488a4a4fcdee2bee513fa7f2b8ca21402 ba7deb31a63aa2c63a9b6de0d018061bfd5bd1305de6cf9be496f2d0b38f0f1d 2fdf34d6c3f7b14cbf3d6dc057f68f9238a01843bd79f65c343f8888c57e032f 0ec1bebdae697a17fd38bd3c2b1d466c6a2cd62b221663dc14450fa9645f8f0d 64b8735f9e0d05b013bd5649e8df55455cf90d0a32e01a5bd47ce4bae2e2663e d3210f6fb94b6157defd835fbf6e24f335a53470ccc876c8f24b9cabc4bdc689 854d5cc84664a763d4ba94fab9f6826e4b6795b2c4c4644d1272fafcfb38dfe2 ef8341e4a2cffca78f4f85382c1bb11231f98e313e8b4b158f1f3858e0f1bd89 721bcfd1800fb89d20114f080b739a7f577466853064735708dca02bce6c08bc 2b21a17310ff3da504aba575ec91cbeabfe7cc7ddba637140f2b7ea4a087e5d2 c9002d13dbd6d60da440acd4e2fa2a96a51cb18380b2304c45f8d55d9e3a23c1 9c5743359406a6ff0228502eea24354ea96edfeb39d4149612a9f84320c3752b 4fc972f7883ce8ea63a0579fc59ba3626876688dc96b3b2774202f8c2d514671 8262e5c2fdc9e2d7add1a28b1ab3acdd49dcbbd4ad6b733de427fdca8bbdd9bd bcb2d7f1ab48ae12ac447d9006b14953c7b75a45eba9d7c28f3b596ae67d90d9 26cc343804e2dca0d14ab0d2a2d7d03b794576bf270c1e79b737191a78dc02b8 9acf167693db32fe00a5d4ec090129992b39c269833c6666c1946d8523bb054a 507659e6454ec03bc6228a469717eb91adc0c14f2dafa266c56522e037bd543f 6dc86ba6eb6846cf225842fdfcdbce3b702041a03dede7e5003cc402df63335b 1677fad719b74408ed9e1b6e2f9e5e6f0d5d54f850f8e4a4307c3fd2a5db6d0c b27e2f7563d41739dcc27a208e3c1cc6d7925820e953b36906bd63456c23a4c7 46a4d38ba7ef139dd455ed55953279c6229eb792833d5360a449e84d31ec9d8e 97d72d2c3deeb90f2b7bbea277bb5409dc5e185443550599dee57628b152cbe1 06cbf2f8915bad92aa93e100fca048910bac864de2e67b786924a5dfa47ed2dd 5f01c55600492999cd80dca1fb95bdaccc1ccc4807e274f5e2d76780a62ab9d3 bf3525ee75abe62a109fb35e297a6ae6ab51d430756ca8a44c2aa0dc558623a3 3a78768168d7f695ea5c96485f80efadd5d0434c256732af421ca95921cbee87 1748b5a61482181b63b42252c7663b9c19d3386000b73e440d49a2602801a192 47fd23ed6490a2ca505a01a2a0216e518b5459c621df2fdf9f4090575ecc0819 22cfab5bcf95785b94f17529b1292b25b8ef368d6d3bad17438c9fc4c41709ee 3309c171ccd892d173085fce4bda373b15bbd84719baa5c86b00bcf2c04191f5 6e8a6e2009014f498970673b8c94765896e485f7f5fdc8d36c97e1daf93c6003 77 964f1aba91908bc5383a86e42b1d12a626767ae2266a7d51abebd85e71adc90a5eacb3cb318b4cf9841c622a66267e4f849c1da7c1918f34b30b981051925c0c303cbd855169d8e3929bf41ceba6ac9083a0e4497eca9f90d858324dcbe1cc0197e01e96953944ec5eae98109276c148dbd8454b843b3ccea46930324053a80f53fa7ce8847905357dced42de31f37569666981abd8ad79fd134817609cad30838cae4119e243621403f3ad0e648f8c554e70c50a573a9b12de6d063a0ea710c6b7295caecc2467b14e6c23d119cdd6a683988e27a427d641097d9b3289b60082c07a17d3d2ce85e2d81a51e6fb6e72564c2d83b3fdd8e13858303f211f82202a25825d8f5df3bf90b4c44476cb27b5afbc2e1332fa5d5407280d268e6d745018b48606119d478b0a6c17211004a141a9d260d762340eda14d37db218fd5e00e9379b467e3d48fc6362f66b7f96d30f0c9f54a56c7bdc925fc19a0305b30420f1abb7c6cd5b70b2f6aa4f7c93e3db7475e7eaa85fd89d35ac303089d4076b5068e60b1df9495bfa5189ce85e9e488cd50dfd8396563710225c182f167d882d08479cb30929b8f8483976a25c39308e4aad44202c71891f0293b6e6cce8b7810fd44c048f6783dd77aa26a71050ee3d0c42b29cf387634081d5170503919ba102f5d88eb9a6a07da23ca431e20c5735a6a125e056e645c23aa7d9f98fc64e9a02392dee0d10ea6add93eaf2a61730f0c23697156b41b172197c811186efa0c60cf2792c23c8669dfc57b631f4c255312c01c5581333abb0050509503ad762e00690c6f7b793994e837934fa5a570073fd26fe4461e57387ccec4828ecae56500671adc03ed8ccc6965ca95d5fadeaf27ac9b16c877eeca9e4cd0151c3d279420c70b55f6dd6717129d8cad62d1accbb33b5008676d153a755db1ac8a13ec3d7029351c1a17c4eb10d2f675677233af56bd5330b76b4ab3da25ef737fe3f79e1011d8dbb369786c10854a06dff14982cacbb665c6cc959f073d4794ede912e1d0537dea0239cf59cf01c02a85686812dc24af2a1c526bf2e82918e92b3b6f6a00c024775c6fec3893dba077e51269e15ec47b961819f925726e0d18f8d528b7800b0093e7418641f60c58213f6005f7656afbf0070c794fa8cbebbc411a8b9700f060e12667176ed4dbd6646412034c7751d42ca838db3c567a75303c5d275df05166f1f1c8ed6124529297535a7c469eb27dbd934465f02b563205c553c442600f7046e75ff841607086eee7cc70d93ba74ab011909e678bb6b45ae91ddb0670ebf3289cccbeb98df119488e228cd0f9d7c1a0be9010075faf4d9e2ea0b72e90d007d466c688664c9fa2061ed008f4a3614b9f651ef631b429d660488b0b30e033002a699da2408dbb877ad5b1d8e0a7a651df2996d9d41b10e0234c78169e009f3ede121b2c80b7832496679fc1219a7cdb048ab06be00839eb740cf7164110c11e1ef1b2aa204e8064272ce4c142744a20e93595bdb9d0964577244ce79bd0addfddd902f85eb5ee839809647cb34e13a0f01c3cc1a2dfbd7ec8ea477e02409e44617e6fe7981c882919114241212aab2013754b7b66656e892d63cb3be82096c421f809ce17560595af57e4578345602d0c448b40d32c6192a05a8dc377309f6c03c86334397306f231c3b63e3ef152b06f6bd2553ef10fd02e89d9c0e0a014489afdc53cd7445d3ff3ae1babefaeb5c580b9cefd1fe27322848854756d5070f831a406ee809cca27bb1ce73027879a639abe6433489845c8d8060c8af0a01f2b9f04025daf6cdbc4639cc9a20031148d73ca3640c2b96d7cfff9044320b07446fe1af874ff741fb7a626c9a87bd99b4964c1f52e2876c6756f78ab8172704d0afdfc4d93531c9ff839b120346cd31352c31e4d1ed6b4e41f4d59a1bc3450cb0e6ca3c711e3efc8bf73065f8ad6566b9d4d7ec6e8dc1a74733ef5d5a7eb20b73a11159b4552d6149a5cef62161f389893d9bee70792c1dc4328166aec37c0f24bf63a1a1cf456576aebb89f405fa1d5e96e3d3fe44c07dfbd583ec4fd6c40ed080485d844f35be2b856bfc9d9c444beb68c3243180da045afadf292d41250f01a0ee65f73131fd931184dd272b957f6d01c7f1fab57539cdbac06bef0a1d0afead4e2f8b46d2e3183fb001ea2467889e20c5f666e2f5d47589eb6518dece031b0db48e33e87a21d6d258bdcd24d9dd353226563948c40208656f889852910a07002b8bbb7bde2093466d5700f124fd6d9ca7f8d00acad20356b10439fd410e9e050614364268d392d975da9d837fe281e4b78d81a0a56cff2c30dc844ed40254d3c73aa2d51437e276c56d6497583bf42c6ffc7a8596428a49f5743976450b2e3b4823f670d038e2ae12db8c650ba4ec7962192f479b859956bd952168a808159691b912f197fb98217426a8b6b5385d93d6627ddd6be0c3df6aa944bff10fa32f7c60444f6bcc460417acbbff9adef5280360e32f61c35efaaca7f36a0401b50ae6673e0646ce64f527876024381a2a096d3b3d2d3d739dfea7afd6940604c27db89c4af12437451ef23cf034ac05944617a19234b9107eb65bdf54d7c702e2b88a4ab3fc44c7b0d797e70bbcd73205c20a0d982c3e04c8b2d65ef2e8f2045192b64f637d296e789ba72d4bdd13ef6621a97eb692030daf6fe37b4191340883761b28ac501f2196173af058be041bf42ed2dccbe507005edefdef1db7000ee6a70f3751b08a66a30f8fa6e1676636a33e2776e7d3281760013d1fd4e1980df6ab95dd154908254b248efaea1e63d279b465319e5f9312133da9f157496500ff5c4f1c88461d1a03392dc1c7103ea33e25976ed50fc2d1d0df87337bc95d0d4b8372735aa002bb94838436b278979bd3e37d46e7e98b31e8b2eb00dfaa080f47061c6e5275960c9a1b27b54e6fc391ada47d3b86c5a1d03eccd8084a136b0ec0c2a17e22738baca1cf014df191de8eab17680d8971b7c3f8ef79245dd66f072ae39c4d1f2688267a053c604a5b761a30a904fc4526af5274b25cce9b206d06cc840f5ff52531a81bcd786762117cc0bf3f8d32b79d602747f02b8834a6e809f5bdc841e92e7ea9106852a785d0e8b4b06a953d44117854efa30a18b3600000f51a8b106bba84b3da14393c9016b8fb1d2c2b14f8e9056a183de02b4963130948e5228ae038d24ee2804995fa2269a447e56de83b42e24589d11a4b191f0205174e7610a308d8d7935912ab83c63e6047055a590b374767f6dd59bb210dca0e33dd7f8738946f59c2a33436c51f71558354b51fe9ff0e4ba465fc2b71e9790172478ce72ddb2779aa3fdc7b6a650b0fb0b515342b4a553c2ac8a717a526c40873a2c912ff23ef5f4c3a3318f51a369ce989c167688fa2ad999744bbf28b730780af0836b2e88138165511bd17f1e77465e9785d7f4cb814e683396e20da37040ce5e77b5ff607fa38e769d448450bd30adcb397d6771ad0c20b0e9cc4ac29063499d2bd323094aa3ee406184c6fddeac2469ba61095483f61be306003a6dc0a41e0a1b14d6b65ad9c2477a884062dafb551e42464928898606d702858fee8027cc101e683e444645f62c4509d6cffe43c9cf259850b2bc02b6b9be3b92de409295338805b407327e8e2b0ab7d40869257b2675f1d8937cd486746aa431de20c7185be7e083eeb8a6283929cbb52a7b0b53c21f5f706d1c36f93cbe4d3975908425b7869a85374e0895d226099e3907405cd1858ffff3b5e0857d488b2a7b40a4fd5f14a480fad18d1efbdf2aa0ccd269083577debf4b1eb73a76c93f517440abdd1e3935edc2fc5143e73a908c8ee75e5c78b1c6b4a38db93a81e4a0f9778050be6f9e19682af9b9da65ec9fa09807549bdf0d12b66b4e55fc82bfe0bc7320c3d5c4a82fb9384c7565cfb542712f6190ec3bbd89b5f0abdbee8c6d1bb065b09f5135f9f0246b5f42d664da17164b9cefa3ed421a45170ebd28b2856f6ac4b0c5dccbc052b3c4a406046f5dcabe0b0c444ecc48e61a2cc0a93ef022dc25ee5041f439cd52bf536e8d05e469e2ab59772a44f3b5597e23d7dbd36db4d616f6701887da077d80fb9c8c7eea8f21eb699dd4b7206fcdcc696c5cf947fa41794ed0fb88a914c9cd7ccc54a03faaae73dafc3ef44cd0cbe9334135a0c919cf7103c00ef004e7611dcd5444005e9770abfd4b9dd8b779b2152216f9351d7e7ae10ca0935f03a04d7f5a6433892200c2967f7cf95714b1d3ed7a855550d0891a5920c052607a40df1ceb2306f3c7afda563f481155738cc7eb0fd4a2e8e297d02b5f8095e72d4f7673ca79d84cef4c54d8232a948f01d2fa232ee25ca68cca823d5340c641acf44703e17db8f7d3c91ede3316e2374681f4a8ce817733c10e575415609761f1dbb03ad6d761f7e10201b8f067aee6d84c3b84de6d64d401c0a94348f05551b0d811205aee855c2d675fe4bc2f1991a80f8e39baa7ec1ca9e1b8a48c10c189004b1f52541c38f654436f971199cbf0fbc5548a39ae3d0058caf64eac501166bd366cc08fd9f12622683ff7de0d4c3acc33685b4a18b398bbc13f32aad0eb513b4944dd4b006adab78178f71ffa9e8d9d3217afe19283ca5fbb1d4383609078010b21954e09edb71a5ecbbd72d0603e6395ce25d2c066a97b94d6732280f9c5c807e912751bf5b36ba1dae4a3f725feccedb740e7d4a273da96934b3a7030b40d0a18fd3932b14fe482906af27924395f28763b4e068cf86ce8ecd20eb0207147da79d85c77281ba79ffc0876441ab14b5d23e13db04ae459e752a1a3b0b2c41fc23499f98a3aeb0d31c2e56300301363d932a8c48e6582ed07cac65e1061a7a76a8b7cea34f5b2ecd278a86acf3e7658343fb93c354a5c7f0e141fa050c0f516dee7bea5c35b398524f3e49c8b0f078b35a79831d5c548e4a56f6f0dc0830ea88cdb22d257608c149574e7e39404bf0e75fbc2dd2dc72cab53d9c099c04621f903d7d7bb08531ab702f05fc8d984f122dbb6cb5237288252d84c3d56b01186481ea688728b45b91224e10fd2a3e68c798689916e27d270777b1926dd90d32cf47b1b7589575edea7c1bd9e6a055c087a9109b358526480f6470a24be406c145337c0f2205b861920dda31f6bbd57f17bb7dc5091cd4473b5adb6aac590f7cb7e8e2a005f4b8a54252ef4fe3768568ce894b56174d685abf80fce209c20820b1c9a4776d04d9781fd3b49a4717ea8426f4d5ab8675790fa1d3537c41410e41f2b1e573691311a1e1ceceebca95155566a3d154865a03b429a2e2a579b70e95eead357d6178b5c2ed300feafe558f40de06079780f7028e7923b0279e0d0b2c2f2aa06c1bf9f2838b7d4b6e5da99daa9068029595d572248a9ccb7b58970751f3d506c6b8e8fe436b562a28656cff90b56df696b178eb56f102a63145720eebe88e2f471d7fbcb6b9f367d05b653ca0da921dc52654593b53d3b44fe2e50cb98a3154befedebe973f33f9ab36c8b79cf8d2645f6ff7aed1c739406549700ceea093c34a5a040e39740546cd9b36d43c0b4883154e74f5a2ace94ceee2610160d133137b6e00b8d075b7156033ed718c4d7375ac4cf35280726acd13d91503ffa7d46c887c2fc764b39a184496eba03113b49dcd57b5ad99032a0749736b0d2219fd70594e2e4c371bbc77510f10de9c517ed66b313758afca036487ff18023696161642bf57f5a2aac821db1d5949810a0a1b722e0436deb2dc94a67c6001da71a2bee39a5441a9b90cf30f7f30e675375e422542bf6d217d7987432bcc03ccb5c143e3140dd0c1636e93801b1e7d7d361734c884145973f019760099df095c8b19af1c46e8cba8428492c46ff802b720a11b97f6d55f3157718ac1b47b0f7742ab6886c5ee0b5de548ea69b2a451b31d39d641bf451fddf5e98685d6a40258cff4c156a85e292b6f452df7cf4cc0946edf0e9268839e4175f2481665e2083cd23003e3b8f1d8da11730cc83eb8357f4d1312cfea06dc47240171e469e805cd472b8269b22ec99c1792594436fafd734b265b4338567d6054888727e9d708f297035ca0e4aa2a3f2bf5960d5012d01d0f7e934da0c25de43487aa42605d0cb9f5504205005030429ef943c5e4880189aa6d96b9e3641faf7d19a7dc6fcb067fd1b6182e38316e2d8351ca1ce92945b63541e51405b224fdb9f1e0b6c1670e6018706f263c7e8ae43a221e41f664d1641bd89b2697dc4ee8b2d6b9974c400c354b7b5d556faf76d5b926da008aab440545514256d1867de6ebb672e7cef10ef07666c0c575228094bfb5016fb28553042eb1bd880228a48401d0bd6793560b0186bb00e0c8ba9f9960d068633025609e6709289c6c6ffbbec50cc20adf7409b6ee102bed2e085cfd1d9160d1d3ea7581f1023ead418c24d89d4a933a4f110585a54513357e8ac7cbd234a835bcd53a9d1c5f4832f07aa5ec213c9a6736d70419bb412431cd4dd4c5ca857f939677188560c4c2fafc6053bab6e4361d7350064b2473e267d14f4b2bfd87aa2ace397ca1ca62ae417b35e497795573a175370ef3ee8626412dcbce1593f6e1f7f310b550bd8b54c4f12296c87b1fbc6ec3ff0d2948789a4ca19105d67235fa749467ac14682d5a5680b6267bf38bd4c589e9082759940fdb611a47eab4a2b92e5c51d63ae2f17f1aad8df975125a4d43e79c0c00421cc83193dc826cf90572e57da088a0262e9b0851c5e974643263e445c900d60f3d55608f081b106facb4d20b1bd93f80fd95a29e5fbdd0886487a2dfdf0f1355cd98bfaedf9e0d65ceaf59fb575b346b70d74f4a099966f8bb77b86ad60991767c375bab84e221f5b11bb693114a2f03439f2b52939fa7f50bbb4f804302ab40e17a7f04a23ca0890b6e77cda648c3dade5ff6debd9bd6ba2edee9fb660d8af65278d73621f0122d2f547929bf826004f8037893b5458ea6b82c4b4b9b03241d71e04928f505f940f1ed7c196f246198054a24e41a0b99f790dbad76d3026828665e0f5c0ea785f39f6c18a29aaf7b58a0124483c9c33a474b597e317f0bce0b6562387280e261d94eb5070f2a05c8794612f1ff1dfc3b7b9b39963aab07367f70d908e4bcdffb3c9f0a2dbe3fb3d4209c7e046bc780163e628edd68650104052ffd574e9bbb478f73e558e70206c04ff0fb625b4a537ab0062bab7ffe084948c3db15498f5d4a5421d7a7ca5f4ed6d6d8c13969ba65e50ddafa1e3c4f073ad0be8e94a7156b642fd05393d502ea31c7fc6de80c540e4410793e5496bc06b2d74fbb6acd6877085df8174566d60bce9c74a055a1b4260e65379f2ceab908076f5d551c5f5b7d8c272f573a0e0e77163fca0d908e48b6394da44811c1e20731635492dffdbb3f625f0d73cc968a1d31aac3e4d7b0d62148d1240d0cf691041ff63f9bc0117374591a7cbf75e940756ca26f9ec2cadff72800b50af95b1b0625215639d06ebf0b502e6fcd9cd3cd9ea58b899c5ab9cc11fefbf2d82914e10acb7a5d88d2976df2fcd6e76b5777a3035a7a0ef8d862363bc1c352e32a3b25082a6046fde3ef65e4057d0d5b49f5a99cb7c9372469cdc8c52514e57acb4ff20efae06293f2650cd3cd15bf74edcfef458f16b9f258955674d5cf336606400b0fc363c21ce6e1665bb8b3394ae3ea1a9b5e36678da68b8956ac0ad9f904f2780bf03b6db98470ef4561777798009e858b53d83d5111a90f58550e73dea455480d6476d06b41182c229fb44296b8b3e06040ca154f31a41c8350532c6118eab30585c145aa3252bae17c5600132412e6bea08d343815e172d7f10386c9270fae0deb06ba0e995671c98fa279141aa7329970d53e762d1308303726e1f067a39d0509f30be03a052e9ea26e93843cbf5c5fb4b925ea78a5c8ec7cf00b925d079e02dc525662e01e6facb323ddbef6f6a47e30d4f2839b4d5813683a35bee0035907320f0affe129cfa6640645a12ea630445351d62ddc626d7ee9b9ba57a4005f0a890595e1f2c4a61c22e3a8cba39f4e8e7dda7d59f5ae9a1b440129898d6f3208b5502afdff42029b20a98ba62723c16fa2f07291a6d769bb062fd08e054390094cb4b87e0fb32100ea4dfbab06f8564e4bb4fa5d9454bcc6ac4e1cfe615d8e0446d904249e7ff62315742268d35e23bea759fc0f0122e3fd9d820d429a505a008d833bdff859f59493e5e47e7fbf0d1b4012fbfa778314a7fc88b00fd23bc1004f7957cd14d2f28852305c2c7db50e7fb90446a72e49f886251e20dfa48c850b4ce6594a35d33578adb837431ef0ca16303bf35431dadd8e0481aceb151df700821d546f0902b173475c562d65e85312266370b97aa05e5ef6c9b5b5d043ff0249d051fe80450c1cd6289db78ea29638611fac124253fab2b453d099fcd973082bf12b9e810cf5587bd43b2cd50c53a1bb544df0a674792fee29c925ba04d40bb9caf043f967ba9ef9c03a668fcfeb9774c5914d231985a794d10aae7837b702adc438a2cebb9afd2dc7aa9ee4d8fb448b16a8e963015674eadf289338215c07d66e5ed23f72d02bc316e861960c414a45eb69309fedf7685506a07add025b0cd64e3d9605d9ec7106ba103b34a498c03b10cb0e2ef5565c45a72b7db6b1d5067f711f044b2aba03b6a78bd42b07ef71431c462e8034a5672682a0619c5a6301f55f096a3cdd405e538e92b10387369412497586769fe931cd64939cc5760c0c7bd12b399546a1f7140795b6ac8ce0652d5b2f8ed6c44baae2d61e4a216239073661df3a8121dced23831728e2cc697963aea388fb2bb21dc86a8a34a09d300591c9336f48ec25cadfa0a74e66d190b7cf9f4b825f67ee9a5db593481aca2a0672481feff11eefc7b1db169173b591efc318c1348fccb31f21e71faa9e575e02f862ffbc8578bcda117226a5acca369bbfbc50be304ef0e774e9488b1279b305541012ffab6a015b55f22748158a72ec38c6292135e52620a9b8c19b30b1e907dd6862505652b018ec29a4f268a538c26ac2b07c76fdd58e6f02e83356b1d100b5327f65a24e2d4c19603f4506f2262893e6b29bf2e5ea6b17bd816388693805be4756ab75f2538e89a4bc196395069a0354112284730ca74f83c4fa2859820d2999c33aeaed013c54c1c6b53404a4ada1416060a6625fc23a2f31482dea97028839136beb71935c1441e7736e0cd282ddc65ac21a4364c5a15573f4eb33290c5e3cdbb84c0f4e79009a41c7a10de61dd6a65076c4e239ee947226be924f6f0a5cf8caafd27772b248a4eec556c7e7f490ac9d614dfc8128a6d1de5078137e0e824d7061e7303327224970670c613b843a5b0d48c9692c18d6554baaf6c6cb00c82c0d3882fcadd5dd73859c8203d0465d2f1cfc0d33801beac9ac4f3645c700690bd497f538d820299dc08b509dbf9914547286f558b18ad6ad38b0c90b4c0e10014d5a975b1ad6096425157ea0a970c5096366c5e03284e07a4d2dcb1d3605f141e7f0d3c0f823844cb20de86266da8d5471ea51541f84fe73fa7c02bd5b042d56ff58815b8951f723f63b635ea4d2db7d1284a613c71cbeef9c0c13675b0079a22f520eb7ecd63b819dafd026ba49b442646067e1705ae967f46612b029045d5f2b3542afe57d3455afb5656329df7ecd80632205fb3b6de3bf8433c3800bae113de16afe42173241cff67f4ff1b634c8fac81d9a14dc736b99669b27d80072f987a19626b820dcd22bc88f7914f1a9fb10a456b106aeee1e9263cb1c6c079a8180ddcba1ac6a1b411967b9fd716defb6f456e1508bbf7d2808d4ba559000c2b57f62ae16283ff7e2e144ef2847b5dec2803323210d78b80b5cd798e3910d6813363c7c3b3476d0105f21591cdba1eb5df9cc1c051e34c7c6ae7c186f2908f12abbbfaf07b6fa0184ec60f18ab4f5ff5cd331029c7c632bd2bd9aefaa480c6a59bd4acf9300ecd5f2b352ad905c3fb0b2b1ae1e9aca717caf9f5dbbb3750393de2d421f373bbaaa6d2772839d8e5dc45fbe3d5bda2f544ab58d578351b10e14416add30cf23ad95370d404e703c4f64b042c04d355671666b3a90e631ce0f11e7007027a514823f7354976272f14191acc00c6a937a518057d79b4ed26d0aa7880bb71fe6938cf3c3911e83a066a658fc653c97b8fa00795c4a95d4c5ea0452f424303c656066fa52190f738679389910f163221a6287956216f4dac8560af631478bf56ffa954e772987ca8c2336f8855b8dac6a547090bcca325c5b020f29bee4240c111025ea6e87e97301fbe0f23b81095902eb6ff7528c89abb37d037796c1def752c3c6e48e5addc2f8294deca3a27e5b02c016facad87eeee52c034678fa092605ab1bd8b1bc246f2012acb4b10b8be70a6cf0210adf5542d377041af11e95833b3d069bb9f56584b64ea09f7d8e0c81a9843b18c902ee5d544b0c1a3cab85cc41071a96f3a532f9c1ee0f4739e2c6644e8db17211aa94e658e5084b3d7d096b543340307467d13cbb2f19f4c29f9907decc7a1506e707af264d0528a7b66bbee451c535ab394691f9a960e85cbdbaa98515f9322ca3dfb11d8f0f954e8179164072f90cd9e3607001ed790fdfe7da2f0be821142bfaa5246fcf0f2bfc7950dffba24847f0f6dcf83f15f6bb03436e5e4f7c90aae0205255e6c30642d1c5dc71ca6895df222a16ad4ba71aea8d3e08e50003c96e3fff9566a6670a6debdd28130b43a6c4ba97aebdaee39703ae4901742d254808026affb5fecf0d182d9a0accddf7becd0561d3b09ac1935845037d618690788b54c032e9583a00380bbbfe6c327309c65a62e1d52d9b310e217f66b795e39830bce54dde81f800d981a58c89f0fd73c5360e984779d4912137f1ac3db98351bf31815ee69f51034a0f34050d57b1713b95ff0e04f80b5c90c98e62c284d5d163f0834611209f09a8f439e5fef0b8e1700922c27ed3f735e04c4b44a7e112b9a93e0d7a35eb3d0c9271818541b912251e336327a90351b94234f1e68b16f2d5e8a449fbbcb50d0179cc4c1262f883c1d8ec610de1f78a51964278aa5cc682501e6cc26d069d070d372946e3e647e44448160273a0a0cc576e3ec9f094eca6c2803a025eab8cf50573d48e9714c4c13ebd8603dd4c1c5b790bf24afb8a477cea592fd278e9d07d05b75ed86d75bb89e340f620be4d7eee61402703916a3dfd88285af2a5c3ec370a20941c79f0ffceef712d922c9501d61a774cec163e54a54cb804c5f9982f210d00489a33637c59daf1b428e3c4a42d25c54a917d206e5ba5fed7fe572748b50c8549df20fa19c5bb041e303ec58f4fa2489b4ca4426b6dc340ff5a6636d53806e05d03998b5bb41eccde4794040a564a76590068578f2315f9eb5013b417b909d62856e9ec7226cded9ee1b9e3a0b138aa06eedfe4ba0ef50676d6a4dc4aec0d1d14e4c0ff95d02372a3d86e00e720e5da906e54b8d1171ea45a7f457229ae0da1c298f4bb2dcb65ca2b6ce6314f329b1bceb8d537d193441d2177ae5f6a760830b77a027fc52de393d317696b1e5f5539e8c03f827c0bea22c3ae55b5707c0914b6b46e29e8ff49b1a1d587f862cd923e02a21930c056ab1981dea1e9220f089f66de862a1b847433f09c8660d1fc0410e96d1d8b56ba5955213f64a0fb120e9d0f1931cf023f7e30a83d035a19fad0eeb7a094e0631025046b25da73508e0ff9a6a84a4e9b43dab3a1a81c412e554e99f5c5907161f0ae6abea1ef20068e060bf9f999e47a021db0c1160ea8c51710c10f934b1b923c4c8f80a4adda58280e161cc8901dee6ba80f8a88c96921f3896ef991d935a2ce8cad2a7c9b410ef1086d1efe57b8477a8b9a9419dde319bf226d0a2c2716fb414bc0feb40e940aa50d274b052d390123c3daa4f7559bce7be2b14ed0915a5fad3a6a6cb8fe4e76220c4e0612201d877a5c1533ae62bd2a0f6573220285d7ad4d04eebf9c35d91cf607c0c28010248a208cda7a3bb44774f6129c7edcbc73339c1410bbde758ea21b0a5ca8d90a720f2f1a8f3935f353abfed8331b50fa5c6bd296c9b4a43a5d451c0fe35da26963bd5ed54cf9ae77c7393e7f734465ae293cfb945a061184bd5548003d4bddf991e00bb3ac577796d32f28c2e2c874e7dfb732bb51fb5fd4764ad103a244874b963d33fec20d1d9baaf5ebe99c7ca445e0b86747e16544e1486b260bb43e048046520161c2da0408fddd4c5f164040fc5cf3e51f8f1d687f00f260005e22593815324593c150e274c2fcf2d53cdc96c81bff8729b06a82ff76e2800756a33a7ace3e03275473dff91df6e5822abca60a960a2faf4ac5d60a1fb7750cc2e6a542eb7e25790007cd2c472fb73ff102b0a8037416420408bac0535ef807dacb574c20d9ed95cbbf7854e2038e160df701886ba0622b21dba4f1cc901a0b74c8be25e7a0dcb23b729f669e0f2edb78d90c9660b05e11efec9199bde9960414dd33c318df89222adf62e0220a31ac169c7234685e72f47e45ce1f82c3d30bdd25e810eae298e73ffd99eac44f9869f1947c39b1ea43ecde0397a9e3b49b0f1a3b8766a34c777701debf292c2087c0478e1ae7b9d21fa69f76fbac53aa590185056b7b86993601c2870615d23364e1cbd851f47f67eb72881c31319f97ac06636bc1fae90fdf67e44c614438e446a3f1d404d722cea1a3a4549499c0d4c80505e5913484cd245b170c4d7875b3cc7adc40d1f639f92075709abcd0afd07b0d9354f108b09191ce0765383accf5eeb0a3e28a8496de4801f3fd2e458280fa02d53d4b41a36f85eee29805b18d0906ac7e23af7c713dfd7c72810aaa8942a706e946bb57686c22465993bbd160af52445e1d35fcefe4bb50bcc84d7f0c5f5404fa189ca866ce2cd5f131340e507106c25a6954c8f5696b00d4d6f5e094a3fa073e41b50296530a0300fac26f8e4c0a8f36fcfa95487d0b9a06804aadfdffa20fb6a78dcd773f5c7f091e655384b570b948d13c9db744d2ad2be61762fd528304a55c93cf9683dc11f35d683165ddd1e708f5373ebaa137e40731d331688f4a03272586e31cb38f45bc4a51b5b48e2ad29c6650bfaec726412cd045e73f78880b37deb590d996bea622d8c6456a92f0b1cee5cb9f24906fbab482eb69782fe0062584bcddb549a91c20d1271e7b38fb40f59bc2840ef42670d7454f048822d207631934280442fae0b4a69ecf5cbf087d9d00de490fa703413c2476abecc5a10d975e8fae0a1870300fffcb9e8ad44afca4b85ba3e42cb54a998683991025ad0da2a01a3135ab8321e8276caed898db03008b0d2a1994b2c7cc8078244b58fa0b8e5a9cd24184e2fcc23e8549c68a0351a47da14028b5ececfb82f366e76e2e03c75bb8bf36e3983fe07b2f10f73bf6746c11b19798a1cad183d7c3cc36ae52013c9074b9755a6e95f619abbac953d6626071883ec5aa91af02c41c93adf6bb017acbbcf94505d6271f02f445f78fc3f081e707ccdb4c6f271953df358b6f1008ae795c6401cd319271cbcc0b1e773d7d89f5cc4c20a270c1cd687a72cee8f702863e40bcd13e274e4c00019da0aa6e42b25b7b52935765100f63f04694dae6010c129969c7b8cbece68d081f1712b23e4dd7f4eea2cbbc4e7ecb6d851f084a03f88007f4301ab7f6d14eb3707e2fd65bc1ac9d61bb264c96d38c066016eefd02ece5d026f6a23c78aeed4c515d18c85dc3c98a208ff9c31a87338355a1ddd4032d233fb3aa34d66634b910e7b0daffc06e582d91bdf0bf43581eeb34f60943091cd79aa5979bf310cbef72a315ad6cc311ee8d570501da0c3d01d1a09e72eb01d175620df6e35279c642ae384f090c2cccd0d2e7427c7c3f72d8d6c531f3720b1348d9e3bce76471479a6dcf62b52be88deec325685f484985e04848b8d8f8087eecaa711fc0c71e1f359a12e0ece74e7892ad33a53cadedaf896d914f917909 -generate_ring_signature 62d9fdf998799ad5f0044a6a7c5bcbb9deaaf78ac678f0ca3750aee518456f70 e85e6d34b7a44c497b42634e4e8be3a56e714cf5c44792c29204903769a21091 16 21b726d56ecad082666ffe11fa61408df99af9e47500483421b47a34bcc410c0 314d10d957af2e4da98a98de73d201ce595460aa9f842237ccb0347e9493d3cf 9cbf055445a0e7121e267b3a73e7449e9eb5915b70922f47437dad8f4ec9417b fefea43cf649abf557a369c3f7a11d4812394d1def349ab23e7c2e44d019288a 48360109d5b6da985b6701f47397ecbdc59d97383be7e9b067b318acdd109ecc a97375453701cf393e2a8e95e5b6c2ce365c461eaa62cf1449633e850b5a889d 7ca32230254a075dd5d166b06603ed21cfc4f5215c94bf84ce9ccf3721c44df8 14beb62093b4712b058f10f8232884896e4e85d2ed8ffc1fc6e03b6e1080e2b1 a453729d18b0d90e519e241710b05547b2569dc07ed744dbe3a5c97942e3ca75 5c9896d9305a062aa12eee2bb9b6c8e3be3641528c6d19847081172694d3594c 574a15a29cb4e3c1b244fbb9e2bcaf0cc6c964d19047d3dd44efe0d5bb5ea1ce 0980b5584b6a796bcd0d1e87ecf3be9f1615413e3f1ea237bcaa17108c11a5c8 653a3daca423c001f6f69ecc804ed50a82d0d95cdbdb35c7483fdaeca547d807 18f5a832f606874954b4316e40f69c9df460e505c0a7506f0d42af45c58b483c cee4f248d50d0302575d03de3e792cc5ca9a2cdf58abe5b47be7e1e4a801771a d63afdc5a0cfc9a531b4f099fe8b74b2f9c758c39c1cfa3fda7def01abb4e422 7c23e8fc27aeb5d238494829871baa490fabe516081fa922f3a4161ed0e7c502 10 05881846ec67d764935d6c040dba760a8e40161753de1639dcc9cb740a2db107fa10da97ed599be265dce75afc232372e3a8df3c822682f146be03e780f1300fd24e1e33d1ef5967386c9d20b4a360bfeb1b4767c2dc13a35338aef71c312b0d5c176d26abb5b92aa80122339200c4b7cc03963c34bff697f72ba071385c1903ef2b9010affcc4c76356e6228c3c76047c70905d987fff388239eb48c25cc4012af1d7c05f3b33dee915065392d0ed1c5265935350cdcaf638a86db2d14cd00360fd0e6e2ae2fc172c73c1ec090beb344b3f54db1d0824f52d7488a9fc214b00dcce1443dbaef00ecb07bcffd9ecf8ca45cf4e3569e703c94b2540655e13110dc04bedadf1c305c8b27e7392bcde501d294f75cd47bc58855b1318739c7e290cc87beed32932ea4a48293e47f367cded0a1c3a4db4e9c99daecfd1d1097cba05af0ed127ad8f4a3ebf7784ac7477994bc3ae303b79d1875947ddc6c0da61fa070a8e59831c3cffb03488ae53cfce79bf4e6b7bc98f71e274c44520140bd3130c45e28fe5cf6bb4289649fa3538c476cd294c53bb40d715448e43d0bed5fad50754aa52ec4d686e75400e7286dfb39d6150a7272719d89ea1f922657bc5dfac0f365c468633f0679343b8e625e36b18bf9f2c5d2d6feb156945e862948f6d2603719f830650ec2c2bb194059821c9b6c74a1f064abae3c72ca801439b03d16605506e7524c75c84b97b6e7112c48a1aff4bd563fa3163b6fa4bd1e415cd9d480d98be4d519ab047b3259c1cb99c1e199142942b2ec1fac20e457419d27310b20ef5822441014a18cbb9aae5b75f5c07e73e17f90cf1ead39bda032a8424ee0807375e9f69fc74723b1b11737fb37e06e1419faa9c7b55dce9a3593d215d6a2904fd41afaf6ef2e2229f0c27f094bf6bbe29f57332811f031dff28a71c59e3f903009289db494728d72d5b10fd7bcacea433bf2804b53299b76574916368a8be0ecbe5bc089fa687dde3091ad9bb81a9a52e0de14eb6fd680966d3e5c55e29c5011607feb7514a3f436b31bb480e7a2681c708833dff97a8bff685a1b15b2fa405b14c398582cc7d730493235e38a1c3bfa5705ec909a36d66309dcb87347d3d0ca8c784a70d4608ffa0f35ea3bb2d134cd55caf098c6dc2c44d4379a3b78e4c0877464d1f3ef97e1359f005e5c095fc9649a17ef962e3f3d6c9aa0caaa6fee5072fd4d47675fc1b2377f599dfdc0d6cdc5e5e54919f1d0c9fbe24c896b8984101abac9e1fb9e2cb88c502e5d60f46771195903411a48fb4545a451453871e4c0ccf001f74c8c3054b604a9fd097da2adcfebc84980f9aa493a8d57acf959310016ac9c84254f0af0c75cff4b9708bc1081ab6e9928f6108f736b1b49812ab7d005af0de23d2e687ed153e43b176f0f2c7a95f14ccc2ee1082ee73f6b97c86ff0a -generate_ring_signature 10f3730c2f6a2514d89bc556346e2a37fc87d7e83757248623edeb9fab8ac049 0add4f035c3579eea25efb66d55d73cc4df230ee3a749cb5ec8ed434d913e41a 1 8c906227edaf3c2236ca3f0e9b6aaf208bcf39ff871919427a7719ff092d26f5 1fd2d19d0945cd70b8a3104d3f0a7548a9acc311648967d48ba83203f213a50f 0 bd6a966ff889ef342dffc2e96befc46b687bdfbb776d823fc5e58acfc6691704f89e6e4e90ce5373ad30a99d00a92d2ced7b6f755e5a8a7f9b0ee9969d9fbc06 -generate_ring_signature f4555c7a94cab7fa9604d4c106969a84ad66a469599db08ba49bc24b1e1488bb d97745abc00bb8228a40b47a0344c9556d8dd15964fcf20f0fd82341d6f18670 171 cfc4bef5eaf34348f327005dc7f809d94e495153532aa7bd337e1230de4974da 79b0a445dd281c6c167dbc468a4946bc46d7ea55b1393895ff1e8008f2f53a3e 411bad6789b69ae4a955d7d5bee956a5b7c73ccd3481b6e331fffa049a45d625 c5e8959c5e9f915e5af54c1f77a81fe3cff041c001bf163243bf59c206b9b78f 401660dc495dcb63633517cdfb4c83f7c1bbc39b510caa85d77861bc3f221618 6ca7e36b209d1e1c5b20b78e26ca678325502c504c43e51fe2c9e12d2ef289e4 b295c552bd1495b7b21d99ae5bb7b8025e507a3042b6fbd81cc78f2ab2878927 1a10dd6423205ce8dee6302ca9a40f2257aacc9e954bb6fce5cf4a14889b00fe 4def6357385d6989e58e06f38a50fa51503aaf3b13c2bffa146d2824f93f2fe0 8efd4d3019835bec1143624705a46b9f54dd4f410ad23a75e39f868728585a6a 448e81181967b7c2f211960907184ccc9dc01effeaf2bf0ff3246cf715fa7841 e12006e490cd7037403ccb0cd61a54d6016103d8228ff5bc5b178a4be5b3d5a2 24c6dd871e6f6d1e466e1c2be9779ccf2567abe7533a6d0868b3497346d6f08c 4dc82719c9af722b68c9c9b7a6eff94600c7e6a08240307ec343762f9d24b142 f72f1ebd2e930a6f94e2b3487e3bdc9f8f82c78e8a890635c76455bd12d2620c 508410277ea62390fb737108acb07efd9b4afa8cc3f0492f1735381a6bdd85a8 f98a6c409991d99882a77806afee0f5a4f479f4840279447945b2873c992d257 32e736d6c58b4a36c91cf1b8d9719430db9182e15184a30797eee75dcd966b22 f6fdd93cdc90c61980ea7069933b6dbbd53075b5a0dfea2079a461b4b4ea6658 38cdf0a99c31eda7fc48ffd68989add1a6224503e269a5536701b11e4028708b b1aa00ffa31d1d3d7e645dc9ab2c9d724b3a699702c877e7ea610808dd2ea9f5 1a8f990377f923a024effcf088cc532ea6049d533d59563e398140dbfef2790f 635710a81651e374f11c728151a6b81f8904f4036f70cb3be54c4a1f394780f1 d4c8b7a93f5aa7b5029651f44bfb87ed4e762e9a166e37db35eed7ceded15017 5abde930e31af431a396c120cfb04ae6377f2f653214c76278b5e93ee91c7989 596acbd0cee6be1584cd61634edd8ed33f0fadd3918e8b3398ddee785be0d2e7 197c6902281fba43aa82e272c5cbeec79ba8b28dc0bbb1f1ddecff2c91cf0444 88176dadd11bb0920f9dcb0b53cd9bc720919e663588317d6d48c38fb4c6a795 c623bab71256ec174614b343d79a6b1c7c29f89a94ae9758f60cd5f2c01fc40f bbc6e9dd8b262f08d09935723bd9ceca95667d6000b64d6566985d8ceb02f5f0 e8d10be27173f52da0711d82d8a2ae34925fdec44dc232eacfd24932b2e459c0 05d9f54e15501e036c89fc60c8fdf9ce39a7520d0585e02e852f944a76cc47b4 bb548d6df5b6e342173a857f8378450dfd1c1526399648a05f81b7e72cc5676c 037311c8983b23d7ed1450a24706026c1930d7ead062b4c5806127061c96fa18 41d01eb664f28f27bd51f1959a06120d7eddfeeed185fc4b5b9c67593ca3f750 ee1fa901b60359598a0985f6642fac8604aa654e78f1f3d18a1fc4ecc87536cd 7d69624959350420a65aec671b481fa6ca9f3406e9080acafc5b6dc251e50a56 5315e45e258217f50b9bc1d79bc411aa350d3427080b3cdf5c97ac703861d519 a4ab5d6d62a398b29a4bb0ff983060158e2ed7a81488fb1271e380839d5ab8b5 05985546d407861f5c113c25061eb90876cb7b1d043a74af25c62b406b821337 bad614857399ac6ec7ed7887bd1e9e0fc0320b595233fa0c6797f8dce599ebda eebe0e178a0e1fe78e6233eb6d86d3bd701173831d7f40da0d64cbc572826cea b329aeb55ae0de7a468892cdb57aecde5268829aeb84bb88245111fb438dffa6 fdac8651768a15a0f8e502319d3cf2dc41fd737e975c2b4a428b42db1dcede27 88d3032b6384a94593f5c782e3e7bdb027414c3fd99d8730b2252a7ca744a3a7 1771480e847547152d3aa315218a1c2cfd01cd45ba95182a1b6447103ccceb38 37b27bdf4740b2a100f54f9a289cf58c2b66a4e4f1b13e577095403c46ecdf86 427e01a2c9473a771c6fa5e2dbeccb62d351e429e2947b2cf8c228bb10da4649 15762146795864be7e50a35b2f3155b05eaef4d1b01c61347db5f11d5baff073 69e02c54680cc7205966f38e4a27a37a4a7357026fc38b1b40bce7e7edd5d1ee ef15e86c3ef859f2c85cdf828cd425d4f4ab7aefc11f5a0f4a16daa2b5fd22b0 618cdf2644069de2e8275619d923ead50f93789a4ae2ee8b68b625cfb206b39c 50d292bb61966a8d4beb864d9c120170f1570bd63cf07b41332797b6c29f5c9e 0a34b827392ba1883b610d1422c11696cc281c8b6ae75e961c42be3a3671acca 14730187723cc634969ee839ecf45452304a07bca280f7dae82ab0edc0877c29 699a24951c6ad1649350086f99a1854c89bda4228cb4a472179987daf854cc10 09aeb8ef6645a359661d04a03b7b7ca72c17a0a31b88997264cb49a38b8d7c13 85b0ab6229bbfa7a3b5b7b23811785805f63909500c94fb51d89e8f0554e70bb 3b665ca09ac6eb886900c99aec8e95270ff3ea73c1d8654cafe3acfd40019003 cdadbf8b0087a497967578999992fdb5f6dfbca21531dc5430e41151e051ae56 0ac2710ec9c037dfc4c3f93c5191a14737c0349a2c0ad7f6a44796b7bab64ea0 ceb2bda229231c4d5dc7c031cf687e6bee4d94c75313fc3d662dc8a6925dde77 a530dd5485fb70af577f782c9bb1b3694f832fea5555309dc992c65950137c14 2f55b2ee169c1115ff9eb53e24e1a240b7759af68bf305667fedce45e3f0d619 69aeafa707c68a9aa05a273a1b3fc158a5fdc20e143523cb6362f6ca37f99c25 2e03f9603c7abff4fe2766d73c4a39fdef14d144b0019896ec3685684443f1cb ca18d53a70086097a653b294e2eb531efee7c58909c0a35c80202224e4dee356 aa7b68adc147c53ae015e29d898a72b94ef6b6661f3a54c88a03075c73fbcc81 70cd2e3c6c589814e7a3191ec0f20839bbb2849436b9108110d689abb9e0027a 7dc0f441b3f1c7b35cb5ea301dffdb55e8bfdbf0acad34bd01786107d43a847a 3f08b78c2866d0779ef8a6751b7814b6a509a091c99e96fd3734886a6d001ac5 11db0478b0e9a19dc38723770b1798e3addbf704cf8f5d2e2764d139f0d22a51 f961c491a8525f841bac81919a905dfbd249a7a60ee6257584dfe276ae7950f5 28565b59bedbe41949595332aeb2711fc67733117120b1822c2e9a327e977125 022098f9fa98ecdfcba9ed3a8bf49c72ccd8117cc14d21dfbd22db54deda7ba0 68f94489f7a92f0a9222e201d2b4bba8ad6daa41b4de2c57c7ce9b393dc9365f 34b9147b28a3ac76dbb13ecaeb3f6d6e722a4a8c7d0bfd8efcbeeb187b096a79 b9a3ddbedc533698050e8c4dd60e05919b70fc5d487df6d01b62858b05f48aa4 c214edc1e0d6f77f18a3beb648b37a547ad6f929f3bab96586ec3034d6f9fd8d d04804e17f6bbd4fc3248c049895d3911d3953ac98790ed97f7d038928be2946 08ff0bf8daf6b3b57164169c2b48ca64ada70baa106e164513ff2a5e00a29ce0 bfc8cefb5c9e90ac9e5a1d92e9b7529d813f8041e519fa527b224151af806e26 a07f1068e09111fb6b58ad945f1c7705afe8ffd16cbe5488bc8fb94b8d53caf0 a451ae9ea7500d137403e431646e4ffdf6292489608606042f1a0e373e062f7d 6fe4867afa167c2f14ef976eb6403af05c632057e0d0c5884a3b83052a9cf32d 2e2e29ff51678630871baf5f4b1c97d6474647749de46d887ff31916194cd43f 662a6698e43f9c15cbeb38c3a8f31fb131e1d9b476f0f3a0bf40ee0e8e6a11a2 8421394abfa917e685bec5736e2492f825e7b259b587d6e568929d4a05ae2a0c 442f5c4e472fe9a9ae50496e3ee4281a7945d55a585aa296873e28e2cae5fea9 d09852ea077348503bd7995157301f844afc568e86d3cd18f0b6d415643e3e20 268d7e0eb3a46a11012dabeaba1f7e3574d1e32074bf4a2feded65c12b97453b c122fc5e2fc268e3b10f328e4ec77edc375399c1100dd10acc894903da97b9cd 9f39d95d583cbd4cb2779e0820d40954218f009a7cc5df92e030ecd9329564c3 98e17aee11922bdb6a06e8d5b639097fe9127dbf79d12aa9a8adcab208b36a2e 43250a86c378ab45ea8f34e71a451cdcc8e0709574d40f77357ac3693db3d4b2 f33bf249022052081d7b5da52abccd5fec3330fa176306958c7e3d6291f52984 020c044368cad6dd0e1d4b563ff0d334233c6a875a652e551d0bf8855c9d772b d32198328b301c208a8ffc272e512f909012a07cc99488ca9bf08c8fd8940f83 0832c436f450191a0e0ae66be14f07ed5081fc53f66a0df1315d7358cb299d52 fd2d84f1afa4fbe177b4da82bf8f6a5b46406e4dcd6f308fc54c6eccd018d707 c650a776d5e5f98ca43507f46d2d28048a3e21a5196dbaba699ba1904ff2571a c87c053930f772f7cb317a3191c3c114fc323220d74216c28b9d1354c35a866e 7dc1e344d66e726db6bd8d296dee9d3f04ef75862bf5380e445ecd43b0932618 af34f348b06c894e95c02c6a16fd7e52da6b45663df93c6a2533141959729f7b f99155d1317e0a8014da9f95ef688226097cf59080edfb3cb1a7d8c40c236517 bcffc6582fb43aa29d9df01cfd043166191ae64ea184af279d3c4cec05e46572 dd56ac7d1111f96f932fc79f3b9a33bbe5ab7c2dfac8d3c082c1d07c31147ba1 af799d2346c63aa6e1aff8bcd560b70bf99b3a84780a2186b018f87cdc7becb8 a613368b100cc5a85c4955ad5bc04e5f8d95fafbcd62908e699b984210e548a7 f6db5e3e8cf9f271b129038c706e1a335baed4cb85a23934722e22ed7aad4814 f94f6840a410db8d7809f6f9b6e4898bebe6ee6c792f046cf08a0e192f0af4f6 cb6060273be798eb8e209509a44301993613f4fd5b56b4535c515fa55b4d746a ed1c8d689f250b24e66c8153558ba88317c26cc02ddd3fcc6c9c59cca038dcf8 68d13f2e581dcaa275f2523c351febf927fb6c93e65ca9a293ad6e812b413663 46f723ca4398207c1531aab36297a3f437f1a26458eaa3b7cae6dede9fe8bf91 d74bd9c293e3022a7f7a2a5cb8c22f6da99c4dfac585bc91d4a54428af645a8f 30eb33b7a3cfcb4ce2bb552ff92711152b2b114b9a3dd027a7367b8e8426d5b5 f61968957b10bd9c877b50c5f7c8beb6791800f1f3bbe9b78648d3fa0faeccb8 04e2acbd42014dcb32cc1e7690cf83bc017378fcd3b391ebebc56efb6b515ac7 bbd85c1e26423b3c6c68739a82bb5bb1bb045b8e154ea69faec0c4e55400b79d 87e234d1e02dc9a3cd8167b7fc36b72b2cc9a510dfc160b87e4c3f08046e504c ef996edd8c19019436b04acd173b66a4a881ddffefad2180c07abb7534021af3 2b75cedb11ccebc4875dfbf2f4b18ef82ed352924567cf9b8bedba2e79ac9b3c d012005892ad3bceeddb59a34f98d1717cb85daf2e9fdff671d38d29919c3306 5934b1dac37b15070667de83bfcbfb87386f93dadd3d90abe02115ee1be25511 8c8367ac396509cd1bdc3baf341cb8659e85e597c321cd8b07d8e349cffb27fd db8e1c01cbe556892ea60baabf545c1944bcaca8b03c22d4a1f5e9167b5749c2 26929b9a191e52549d227b3798974397b97d8127911f6945dcd52c3a915df871 bdf06c632061e00b667dff7405eb578912508ce8b1cda679ba90637a6ec9d755 adec4d15f5b6858c8c7fba49da88d68d4bf31cd5b92b34ff29ac5d7cde88cc98 d70026fa9b5753d42069c8def719a909d34d6cab3b1949b5398564bf6f408d70 ace300fd54f1eb3b6428c46feee6824f1ad1b7519c099800f8c90c92f4c52833 0d6a0b164b9b02a3877f8326ba35cce82e4eced1d21af4d2e80f86ca1d8b72ad 3e65edc9aa0612700e957251c766be20acdad8ecbcbac2a645e286c3c42889c1 c797cc975661a163376b9d0ac24abd415490f5c0e5f70f87c40cdbf7f0c76c29 40f0a14168aa61ba89b9e5c40a8548858efaae2990b1a102a6810cd4e14a4200 422dc367b19a024000e1f102653a2db986d13c1be3b0fd9a6d9882f6456cac0b e69f2c3a2328b8546bc48b72f489d782e4f669f0a95e759e2c58101b10fbbbb0 d0b9df63e05f270697be34988384816f750d2cc5f1b058637cc4e494bcb247d1 cf39c2a55ff7c176830e14298088e6482b864bdc9935a880e54000ba2eb0ce9c 47b5d892a0a98abe0646e21bf3acc9414e08e74d9d7ebc46b8c5127e5aecb478 df6c351cef6ffc4738adf60d0f7f995a651e93dfa38d18b5e8be83d6cf7ef6e7 9f765118726950e8cf8f2f6f6db2cae8e49556b0432fe6960fb31f4262acac57 9cf2fdb6049dfecd5cca8e4a5fd17364a89ad0ef7524a28f48aaf7b32309b4cf 13cfdc82e184d2626825ccd9f10915fd95ec8adf1f11dfca92f5fb5771f48a0e a3aa71ccdf007f88880835328b070265dcbec5b8b537af52774d99cad5aeee68 3cc2fa6550177b8354d2f5e241f8b721d2a5366c34494df2cab5ba99864cc23d f2ce0c858dc7639de0154418fdc0f5f36ad7237830b43a200eb1e5b8ed713345 08ed90cab1be91c643d1d9c620c3bf85f3cfe1173c7cadc37ad2dead9b5ad176 00ad99b49db1adf38784bd79bf313794e45de8dfbeadb26dd8a2d475b49a6b71 37f121e481a9282144254a27b07b031e44bf34a47e2ef1bcf7b9b8524db52050 e00d06925df9f5236e69fa2a78b78de859780ca4e8345d50efa819179759e483 a4f6188c99012e9f26fa24abbb7dc87a3057d1bb4b99f4f384e5decf9c17c36c b5a5e73fb99c679b4357da96990a6d19a881e12922f1466958a4daf9af573e88 4a52f4bd038ea6c046ac554f842f88a654a055e5e9bc18bd6172f78dd2c97cb8 8efa9ea083dc3fa22da94cd582a3fee99d6a51db50444b1aea61c4bccc2b6140 ce192be6d8d50cc467a4b520368ab399e880b3eb4d93b8a96bbec74070e7c155 f22f7b08626b947b8d6017b846b258c75d55e1098d381bcfa7440d5ae51ff91d f11cea7db46c8bce9a69f5eef55f9a4131bcb465ea1153c9144fb698b1d936e5 b134a75ebcc9fa5b09567511151c8cc6cadb20af14e11c040f19519a6f613102 59b74ee6ed5cd8ac35f7dcd3b5b3497d391a3cfcbd21afa40a72713cd45b23f1 7f31beff2c2c80200ba74222781dc3b00d1cb9c2a92d0a0deae2032b8b6ebe43 acf94b13dfb0480f64d8f24874daba01d93f09d2d29be66a48e076dcfda39980 d31da219eb8bd9b7ebb4d6723adad876c59e1cb458f66a11b8b1e494940ec782 0f493b671148ad3cc994506460ee02bf973068429f9f1073d2cb09096918a5c4 e7464582dac622f032f7964927c94b2d801f92464c59ee34d533b474e0c4c568 faf9274a260a3f80554a0e9b6015ead2b3c7e39d9d5041cd63f834c5af85f398 424919660a91ecbdf5dc035759894703844ee28939b9eb004ced281901dce148 08df61a2481c760147c0a65a188ebbb4c0a3394bbc7850117317032ba5f7a31a cb51316d6b4d9797ef4ae30b6ea1314aff7d91e1e8b0cdc3d56649b9037cb193 fb517591cd9ed330972170eb93c3563848becb59e905f421afe8fd8f37e5a1e2 9cd6bf970dd2da5899dc28da8446cad1bc6881cea822a9a20debf919b23ab20a 103 c4005b7f84099af86d99f4c4d4d88c86b1d309cfb457d2836b09d6507427e205584ceb61fb1c4c6a9f09042fc06342c94f48ccb65f3b33af21a5ff4f00a0bf03cbdd1e1bd5097866968e0e30ea8f5178ce5240c22c8a974e08adf8d0fe3ae40af32492a7a679b89978cb637ce74e7cf2f71f206d504b0f531ee50bdab1960209c90d925d11af2ca2e502fe88c91e8765d05ff658fb23e51acd408e3ed5542b0a38ae390169c8e8926503662f2a7255ef2741d0d4fc4da24b22e24d4e4409a20533cee9a8b287eb4e7c1d868f8efe97cdc470401b0991df22ec7e46644b3efb0dab6b5db335ad574f218251cdf9f2416f7150781ae7b5697b4064290a284606005d93dcac56034235ff268ea7848befebfcff1084b1016505f65dbc9da14a180b92a0025050d4a89db2dabaeab5ec1751e3dc2508068e22f4d213afd6d590b80959a971ff2425fad3bcd13992dbbdf9785305cea18f04ce131770b4d2e28dba02a00e3b05de5d0fde61e63bdb786ade3ca58947073bb488764606b3ebd0600609e9d9c0adaf2373024a69ea44acdbbd9a84ae74f25488df0be2738eec3409b70045ac54ff862a8c4ddf5d13e95ad3898925280c2485bbf589dcf12a02e1b20a08f8699b92354e4a384cb2ca5fa840c04accaaf7542b9c049fba901a262c96b60beec5d4c2c6022cfa86c49f9e35532a66c94aee232245fc805bd3001cdf05f107994eaf7c4d48018ddcddf742f73dc706880354e36761736e78592a62b72916009e2f80ce8ade0dacfe8655dab6e1d8d4b239e6779620c613778c93c066c6cc0fa61c8d21e5285eccadfa5726dcd0f2c56fb7188ff06b0245ce0579ca01d5e004a9ccfbe6c1e1950cffa962a60c61910dcf778ee4ca227011cc7d7d9c0cc8380ef863d563834a3ebf8cd61a2a399d83ec0f5eaa5c2ec8f10fe068e72892b74a0d88a34a33380f16bdea2437889accfda618f3838ccfa393df5af6d97c7939c20c2224da8f7eeac5107361b7083caf977c138dcc8bade5f68d2aa2caa075b486091d7969480ad8b535041b2c93c29dd893bbb39ec6eca8242120795e6a8aedc10245c83d9f15b0916326980003eb60cecbc47751ce56bc9df1dfb63959170726008cfff9ea58554992292c866075f5778ef572dfabc69a82325949eeb8177a5f09e9aeef78e9fe58d6658d18275548fd0e147b8d60d6e6a577265994b741a3cc06f3c03da8f869a69778d9327d094a5a41c06e25e49901aa8b194aa08660a19603767b339e28050f4bf2d2dae2010e8ab75fa99a1c3b29253a8601009e17168c092ebcf4151f2d51070fb2fef8943cfe456c78cdc4bb7dda09fa768984a88fa30593198e31fea0a546d46f22c7de0c0f6dfe11226f27691deb01b169adf587530f3bc9270b9e1d78a8a67e4b9d70d9e12d9b5f692bd108a2644b1be761729ca40cabcab2cb7bfdea3362563452db9ef7c884a82d0dede94538f5145f313c7f1a0c73cc2eba8c27fcc0b8955d920de6bc8c92ff3e4e7ecc0d1a1221d9407e5aa1034ec66fc12a939a0f2767f2af343a46852fd64c850d5fe82af921eb60840f320319de593ba7c3ec9508c70172aa3b9e0bf2927b778b42b413920fc22d4439d505b6e6469fd6448fd92284c69f912ecb7f4ef0ca274ea68983cf4859d7bd847803fbe21297cb423a6dae7a947384944fcd0c510e149ee52a751ebd4ce214b82908aac302a458417da82e3af5234c925df0551312615016a197ca727adf6e83740282039557233a0e92788b195d13bd59a6b348960dcfddf971274d913d7166a1083dc6ee8c25f1510d056ac0106932d9fb49b48cb1a059e70ab14feb18a15455084252a94d7520172a28ff09631273278741808316fbee95fa1a975e5070281705867fe68506de1bd459805414cf2ad43ced52da167396d1a5104297eb19cce901dff29dfe5a9de9e56f1276d842727b6a1fc2a3b003865b4be8b6fcc44b31990bd9f4c703a5c1081168c57ccb92e969272294d354050d3533b51ceb1c9444a009c09932b58c6c798abd336c413de060aa96c22f32baa3dabb0187d5b45d69270359f61a9832c53ad31dbd9aa5010c9d6954a95128fcb64ee5c4d7d2397188eb0d685d5e4b123494f6094f3fbe103c6ff75e07ff6899b1cf82e17420840e049e03ccd1e9e3849d1e02c9dd3d17b6db737f56b669bc67038befc638b1699bf7240fd7f62de49cf873da500655c3380865c72905f4e58036802850d5d2ae883d220c7c222f15cc7437d37d44dd891c20f361b7d0e1653b3b359d504affab6c25940089be406d304b3cfcfbece51cb505bda6b4488fcc9c9fc7da6babc9bdaf0d2d09447adce0101b60bbd76e1c0bff8b9ebb25932907c9e5e21e88ede4a884e01e0354013f067ec762031c18d4ecacce884da16b7ec49d753e636c9bee70e6b83700892d437b7b6970de0cb3f632a92dba085ba5bbd8b2c02b09a94787daa3e0c90ee4015aae33f414223c3f5823b4500b517bc050dce4b6d8f8f17ed44d8322300b30abdfc14942f17cb13e5da605266c40ef3b386faa9533b8d042999f36f68e0b7002048f4bc81912fd84ceaa12fcae1957d91fc9195b995ebea3487a88997903eb932a8bbaa1df1c7940ccb6369eb47737e95d1ffd34ce9c51488db480471c08fbe050b02dbda9253163237f14ae7533e1b29a39fca035fc1f102a80a7d4160d3feae8f5739ee867d319648fea77094c707846e4541b607e4dbd4f0bcfecd801a76606bd1cc2edd04bc51217f693e873ac4be75595853a402e1686bb5726790a0c7da0075291bcf33214bf86c9dc3fda63861373713abb9490d6c3c7b2a1970a38edc410272942f4758aed302f2184945bfeb26e538788116bca0133e5a79300be3170da7e3544577de06f0ae3746a9e5fabecfe62e80d7c830ab081dba8c1078ce784d26e7850149dcfa5c4be93a6c8621ee9fe02029de2813c5263ec4ba70bd04b6a9f852ec02e101de57b8a7ddb6e7b4e812722c9e1ae1845ef8ab78b800351b4acc41e2160825036c58af2cd05166fa597281eef026c647c3acc69b9170a408783f88f183cff88549c049f3c54f2a29e3b7891aac7d1839346378139c505781b02293baab8a2eaf7c40d11e684e7c3e8e7bd04b5b376c663d506a42faa0f2352dc209c724c6b45bc1c7491065864df2c7948b7cacd0abbfec7597f930a08046a347b7e18089e75f50b65878e59a3ec8158e8d2623828215dcb45a97b410d519421f7367efad7d0b6590c23b495b9d5f751ae444e98153e7929cb75c6ba0b86c63e16abdc3eb2b0f802d8834dd66c36a2b899448edab4df2d42bee0ed7001c952706f90052599beae04b67833a556254b6c505a92052d4bc653f1b7ed2c0e2268b852cdfb990a561d98d1d621245b3ba56f2317dffc97b2a548ac66df3305e51c094fa1ae62ba9cf528bc831439e17c2c707dd900982a1e873217b1ad9d04684ed87c7a14bed1b3e314cc1ce27098d38a9f985c2d575e1d73e5128467ce081fd1841d5dc5d495f2e559032f7136d1f9c2c31c17f9f54ddd0419eeeb9f6e04656f902111736211a21d464a249cff3c7f60bedda472df2525783e2711571a05315cf172b4c1fed6dd6551a068d17bf71ca0e65a945de61530619134b3b0740d335c10e604d5b69dfe3b4cfae2f75f0e915221a7a944243dfdbb7c160f6e350d1a9982b0c172a914480550e778bf345729014189a2e2a7fee38e8db067cec807222476bc4ebc7b409915b9fcaa2dfd3e3011dc50443bc110053c7e434fa47b0649f66b5780e8c9e07f8cf1eecb416d6a078dc6b5e7b6649195552612b91fb603738a109038de305169b62869f27422102d29743d725e72ac6c38db98d5b543040ef47039dfa2b7301efa06ca348c69f3d0a1343cb2b81a9bce0e5e7812631302545adcd437356948ed0440efb14476623608bd17fca782b8df715a18a80a8502203555efb76dcaace047db7b58e14a340a4c40df976cdf1a611a800204eb3d06dde5af61a81c2bdedf93a0982cec2d7e7a2e06f05b0b29df58d22ac0e3f96b0c5312fe5e33fc0a2d8889612765d7a461c26ddc95b6344d6af2f1f6f47b895100ef828d42b6947fd3584b41ceb237c7e9d58b81d95c2e8fda86829e7caf89390d8b7bb98c8064b32e244461952823a7b486d4c77c84f6f97cc871bb397369b40cee4ad48760a76d265c236fa2033fdf937d7cade969de7a63c93363834e1f9f00a5f37a535dd2c1ec403f2475d91464f7bc5caa3f4d7474a30c2cd67c22515007173f2aa863ca6e44adc2edf8b70f6b0112a4134a4c23eb2a06baea34befb4d0e4c6f879a3fc297ff740310327926312228c24e3bda34b1f6d96c97cb415192092338896c126659933003f85bcdff65572fd081c4507cb56b138c488b9a41540835a8368cfd81e73988b1a68c7b1c3f02d3f6f2cf3f72fa4c2a5cb312deb6a10555075870dcf71bd3445f6f234ec13c07fda4ab1b5601c7de4710e9d9de7ed10d0c4ef6661650815e44ad4ac7cad3fa30906caaa9eca732c7fb834fc06f768204039be141561016e8811a36244dafd4477637fac2d8739c4b65527cb540a2980dcde5a701d76c311809cb780271fa03abe32d6a29194432e19d1ccc5c3b821e0617ad6a028ac4897c698d730a9dc9e6b1f5214fca28b2962be458c5f282809a030b0055344efe8483fbe7357e9e77fd2451a524e2f5013c86bde4c748acc6d60525c22e8581a116629e190c6f216d42e47b2deae5bbf9e21aa58066c11865a30df9951ca6d8a4b94745b8042ff09d3cccd4fd5048dcbae803334f81849e07510980a959409402ec713d84edf4cdbafe8dfac332615004ab6e63bb82273101b2027785f7d4014a0b380deca671056d4fb9fff4f637e900ed312a2896fb0efda004b6abac0a75694b3cca9cdd6240edd6b90d338bd752880f4d66e6a29f0c47940053100625e39ae536797a4fe909f5b779a4ea7fcd23e6ce9b345f2d86a4d69f01829b711cd78ff96036fbc6082b74a41de3953080fa7fe34976b0e3318a6ae3075063081e3f7d69721b08b18fcd9bec5374cc049a12018f726d150087b36cd60d513af6a58e3466c8545e650e4bbb7a6e95a7596a8651cf3742291ccfbe21a909321081dc89d0f26b1815c04c437d304e03629370974f109a536237ab3764db0491c8251627f14250e62c371c5037eeff2b264ad4b720ace967dcd3fd7a0bf70ce9f0910bbd16387cbdf2927bab2f77859d94c5988b6d740d2315f9823d987d00b4dda02907ca773852999cdddaa3e74069aba75eabf06602ed315c341993d3090dc2b077196cecc070d99f374f271535cddbcc4de93addcedb3ad21d370359084bdd3df0b5ec967e2684c24dba4e50fc38a6d14aa8fa4490341b4c993824b402ca3313022f243268d53a5628b894cea7e8021ce4312717d649241319f04fe90a5e91f8ad38e9795bed6536655af386e299b45e7afc3198b090d4d04ef203cf09ff64995ebacf67b4f40b6cd16f023c2836b72e30700fc9bd276163227fd1aa0062fe4f494a855aff9fee649872bd75d4a254c1a1849a0f6792ecbee041ddb60fb8f42600e9bc83a81a88b36f55fc966fbd86dfc2b8b9876811c36d830c29a606e6f79f67842ec1eda59a530180eff96872eed57d50d5c223d7b10b07d70ed702a9e7612b81bc298554a16b86ea9fbf491f0d03ec4c63e9d88570d10ce78e6600adcf7ee7d949cb8a51277f7aee1f9925c1327870161ee54de401a89b0e54e30bfddffe9f5a45e8e5ce2c406d8251f8ee8519c0cd949f189e2a805c8681e05808ccde4e1f20d48efacb9f407bcc2f30130e0d94add8c89aa02badb1463560ff00aa5faa03df299ae067bf972a1910c7ac2273281b3cdaa76263eea27f432d710b206c19c819848bc0293a4f1b57efb5c5d14cd075772c3e2c0f3bc86551137c062c9588a7e2f98bb2f6328cda55577be501450a737ecae4155d031b5b4c616a09e4b596583962a7ec631e28c49e4da6c7c6d45260d74f128ed7364a54fe82b205ae56b8c6fd079cac3886abd2260aa96ea1850320c3eb90c90e52b79d4bfd4506417b8cb3335540665a6c35131f5fa5695a6ba89e821ee622419f9c3448172906ab19c61f2f190166419a79a00020691da7eba2414531c5d94f6c2b9eec972d044b85f586e75ef500d84accfba411397d141b5c0898dd0b5f4e240106e4b01901ec315d143fee6cb7ef1129cd814d4baacd49e6ad380c58bce567209ab449af03e7a1d392ea99c810ebcf757915f601383fdcda4ea584a05e0b6085fec3305e0ff11e11e298e7bd2db47d8c40558bb3568fba5503703222b8947ca69b8c63300c72e257655b8a905cbbbac6fe9646c61fa59193358037201064e84ed8ba6af408aa9bf4eefe93893d7190153524640d8ed37adb397f0b14067dd1b1f98cd65b085e53f66f5d597d4c467651826b72d5e391c7066a6559375271d7fa6f12ceca0be2af57575eb9d729185ee66d07072507f37e84f20954d722402f3d6a88418704d78ed32097d3a1868f3dd38b1690c6dccfe7c18085626dffd8899e860f19ee07d8650453643ba6728997c9228077e63225ff99019d6645fbcab469039e5a1a07c63e611ab552b6ddd6d6bcacf5bbdcfa08a2a0a02d11ac2ee35dbccfedb46c0c1022ff7873a3a3ce1cbb5c7633005f85c352ebe3fa1e6aeca55564334b776f0e75bac8f1366d8debb23d36d306a47a6c4c1a5e6caba7d07d0c07fb7c90729401a28a7001e1be3ef09b597fad5bbeffa10cca5871ab50797bf58bd31425d40e05cc7035ebcc23cd214f9a0882016a798cf99473f1a63bc1d650f5da3bd2211107779497a9ddc7bf0801413e600fe02c520cc9647dc48917bd2e0d4c6514d678071415954a00064f863c6ef5981bad9a0361f77d0920616150c67c9db21b5f7601af40b4432d6ffa8df4a1a1a421e0b90dc29d10ecd50821235fcf8d0077da0f06313ba80169ea4fac296376a43ce092d73d597292548e3c9f0cdba6daa27c230453b4c83109c9a38b468a99185261adef5f60c4521e168bc092108abd38531308548187595217a1b1bf20d39b0add80fe3513a1ca2d4ae118f69dc8c2433b230583783c168825bd87feb84a40cdb826a66d85ce7ff06ae07b828e315da385a903ab156ad4c68ccddf1cee86e01c9b7c0144b311dc09737d57089cdebaaf7905056e809f18ee6130f241ef59ebd6252fbbcc1048efba218c877c1a55ac7945640671e0ebd27ecab1ceba6501b8878c9a7c77ab06704d665cad597f0a4ad6bc4e046bd3a59fbace06d3f1067b18307d42b278c6ddb961ff168c205c0f4f872dd10fa077b7daa4fc25e2bf234c1d94848689cf4febeb7dd0817d56bfbd034524590d3cd69e780fca7f49ba6ceb76e8c1889a3c2cbbb0762bc8b434854c0ffdfe04085adc5d7fd8341e1d3f5ecfbbdab41cd4c80c72b3c6dcd8ea9630c2221c0be80795e46530e21a828f26cb0fa55880c127b059b968d910dcfb59b044176d82ca0f2c67ff28e56218d44896a861228e769cf2616d0b1ef07a598cd8a940ac4f4f05d62461c9c14241b9f5072490b9bd09a4e95b9f3e26afee603ea6a4ec95dded0f1a2538b0efcff1ec808e73669c36df0bb9de2a7b25470b28580807fd7b685d0e1cedc076ac99dcbb166c2349a6046c4d3e8aa7ccad710a15d6f7ddc461b7710182402da2d909cf2eee7427ab38b0cdcc9505928a76a2ece24261b9e47ffdd60c824e4ec14caa27c5f185878a5b5832bee3cb90b8966f08e1df25d3913c72c505d5936b43051cba251785724f3f21a114e0f184d3ecf62807260b2ac86fa8840f4f0861554966445394c4082905e5d8da75a3f1e068366e98312088a93e111602ae3bf228c29390ee8aa0706245750336bf77ae40935c24d92e5c4815cc36560d7926e77238bb7aa2605ccfcf4d2399d36d9c0e9b8c1b9e973efba8e1fbf0c502c6964a26ba707ba9e743c5f05dcffd4f2f13b2bf187cb12f92a2a96c68be9506b79903e78dbc97b451f113f9827377a33f96a89e05cac3d5d7b9e2f3a68e610dba78a42da14b4ae6d4de224f535933875e98cac72a322d83897a46de2601a70a0935b938a07b2f1b3c3229b8c213d7a8454645de3b4c0abea05b177cb346520b4b57ab4dbd112c902c295d5e9c70caece6e848c26250e1bde26147370089a1028a385f4cbcf446f2e4eb50a640a6f754cdde3154066d657939d89560173120045229b5ad986b8da057649afadc8e7d32a01974e40ac0aae49b81f823dc3fbb0fcaad924dfd5d020df02dea72c5d9d6d4e1301f7c9031ea2115e88003b210b60f0e1557463125960487477583bf42678c1e36b5cc21a57f562a42e8759d2f06098bd1a87edd22860919dabd94a782a2edd71dd92b90e672243c8ee039d6498b071efdde3575f9fdf02edbbaf1b7e4543814aa416e4005d3367dba7777f0549408fdb65bbf538c332c39f64ccbe47526d3d525a4e5e8418660be5a68c29308660cbfc3728d762977266701f873128e8c0a1be969c1bf3a6d0146b76d79d6bfd70940917542a0807e6ae41122e41568e9defb378aa97e81563357a811ade702d60adf978f3cbaaa5281b50dc36f6cbaf422dd3782fd2fb6ceea22dfc1daa58df00be4590cf1ae0fbdb601913511ba4001340c859bdea4c27b2019534f3bed911c0ba285b4c2e86cb7bb6d7a63d42225082ab086da5330ffdc31c7110cd803fffa05a67f387eed0e2c8ad597bc70dbc6256581a71fc783c6a46482aa337e19a18007bf9254a3cea617f52c0914ab8c55b3ca424797fbef9dec1b1968f8d3be700d044c01602a7fa630cb1333d93bbb46a06bacf33b41fd9536f75c9e2cab52a99e077983cca9680c838d16ace80671ff7a8187996ab3ba728967f0be634ae4452905ea248df97cbecae786d8de14787c6d9bb50cfbf0a0941e181d2f8b2988538d080196b32319c11afa12a1fd670dbe1da4da1d512f16163c1cc24c81a2a683e109c0855489e7339e0c02501454958273b551db94e2416e30585059f36f7abe75060b55f4195ead79c0b47ad74b3358a05f4b8f48dae88d363aaaafdc639ee36c000b634b766f638a92137dc1bd35e515f4faa738285f6e62aee8e1f0b479172b099dd621fbe820165aca0c4cab402cde02666486f51a137b8c9b5bddf4c1a70c06798bb0b105c5e4ca6669cd74886cf41542419b4012567fdd220d488c4bf6d30d8c8682347026a1aac889c74c31a6c23287ab862f7174ca0b95982fbf465d8107045b4741c6cabc2a6c804d49ef00f82ae33860249d23e9f35ef8a9d09c92e90f07b569259ed27da7636b0b515ba6be18bc9c11066e26808a3142e0cb8b5a5b09780d3f92cac1621d3a78edd3027a191759792ed3e2c3346132892040a1f48b01cae02f2fc732fc280cfd11f7307020bdc5bf3fad212919d49cdfa886eb104c0c914f91ae618347b43ea84a7dabdb4673f9a779e61137640c8581552e5bc14f0dc71b08f4ae16ae354b00939ff9b9a934a96872c4ac5752f0a4558322364efb08a8d6e8d473a28efcc482b5ecb8e82fae20ff6dba6be658e31a1ae789de6e6702f5bcdd837241599f6b674bdd17e07c1b5bd3a9f49b8ee4e8a071a1f3049fd5095f799898915b64bdf7b95bf6107af49108e37dfc27f5b97f9dbb97d36cf39605a870cf8ab5b2d6355c39ec8d707fc4e0be499c743d7ccc657a3b6abe096b7a0f11cabea39c3a61cb1a42c88d3bc36d26206b13d7c53e497726e65f454f18ae06acd7ebf0d8bb2f63ae1537373ce69ed9c65b4c46b4d582cd5471dcc9d285d504fb0a8063614fc87335ef44f77020a158b53097d72ee85b19239fb866aa86150ae870cd6c1f8bb86d443111d4248b945eebfe6246e3cf17fd5b13fee1ff35b50eb5f58f2c8a36c13e8447075929ce82e509ed19e63d347c144a2dbce87d33e40bb53e7cc2a31154de4ec5ae76146db2c3ae5bc8b53a472625a047b986ec305007ace4e9309794e215a708dd43932692dcc6364c5f94961221fa978ceab4725c004925f96dc6405ee85d0f7722b3075360f2aa08994947add14ce08ce2cf69d4043db4c8dac58fa3e3e13a80e9e1087cea9adb228b87b40922e8bf626b50a531056b7c29a9dbc7f2c3c34cafda827cfb8d1a29608f96b629bdc612ba08d3c1f407b769ab8f820adca00226bf94f10572c09c16dabef485574b27d4a592302ba40ecefdf9e16472596adddd35539fc41e042574071fd579ab487ab58d4fd8d8b80673e3809fb09e00ab1cdff8d628df800723db33095e9644ea0b078848051a6c0815a597990c66ed58a49c1a882f6fb98beb92c5a4f95263f99acbaafb310ea10eb572d3e7fa254af28cba87fc73d12696d52a9666e1d74b2618c61f61427e4e0ab06a53fc1aa297e1c1cc97b85b3bf0a76e1ea2d84d46a7929cd9c33f4ee5220a689c0b99f1b77bb677a89d4919f85e152302050ca441234d6668294e8554bf0bf05c12a024dad03956c3e71b77679dfe44e049f105dc37be6f07f7ce034d8a038cea4756c5574ab2b08af331da602b123e4c48ec5fbc79a1e1619de988bece0ebc8d4699a8342d74aa51de0297ae4c0bc372ac8d5ddf682cb6e8c575d9ed7407071e6c4844fb93ab55b8216d67c329ecba25361f54ce5f129ca7d95af68f5907c7dd785f2d44b735078d9d85c23d24fefc3c2fbfb73428b37065ed2a37e4140745810f5d09c84029572ed5808d5328bf647a9267493fe6bb986d5dbdab587e08c82f7d0541f61927688af3640983368df5fc4dd7f12406f526622688477c5a0a7a999bd74539b9d8394039574adc34481b8baa55194ae8ba963c7aae52f92809cf7c4e08bf94fcf0519aae4b0d9018de1c5d54b74093fad40a1e379c6d8bad01524c0a1c6140b8d0980c6cf202bb9600dfa914e025fe1e60c7a4e81af8bd3b05c3892b87bb58aeaaf3664d01e6cad4c1e699f50842b2235c94877be821f6af08ec653bdfaa75288bd30eb8951ea1d3f1c07e6d95e153185a0183b9519d0e690f5226a0abebd8426f3c8b81a2ef59adabb9acd956fbfdf1687c2b8858d4624707c642a5b942c628a46be04015703a0ecc6160b94a68ac59eb428c7e730c04410240a1dfc9c2d4f86ff0b515fa433e607ea565f3316e356867dd6f284dba6ef700b7bf16825d6e87ae1da1d260421ca3a2402da69511cd4b55dc0e276b4ca61504c483e022b236f9ccfa169e20c7b799631a1a8144245453b8dfd0c415cf5c0301d7c954434150048d2db19e665c4eea2620bd8c92eb4a6f0de4be567b2621670ecf253972dc86805691f520559edeaa18a3158c145f73a4174b9c5e14b9139e07fc680cf861aab7e581ddeadc5bfc25f3abdfef060341138449aeaa4bf8844c010c8aa2db9345a4673488ada9b03c0732d796f74fe391ee337b9d391d126c45035a9c6285292c9dbc262c43d6ea067ad9b78af6ea05d7e372a0c3395199fa300669de109c9b3a4f73d6feead31dfd04aff779d51afc58815c3eaea53fc6312202e8244fe07792aeb38a23d267cb90f0b62f871d63010ff351804cee6a4245c009d5559882150c1d490fd72c5ceafadb96518bb31dcd23ec7b3e67110e815ba905f8ba7354b08e9a9442552637d6196ea82d89d85c55f8a2aba141883e6dbf22043fce50360c6841f821550820e178d7aa65044cf8df8d2e3ed0165ec0f17c280d55ea15df2f7591c9b9b5430bc48154966caa556b124e9c82a259b54417afca04024a2d4a8404b72b42ed3aa8a0d58681ded9b4c79f9e2f7b0f65fc0f067c290ed7a813cc3ccacbc6ad3b3beff2e5db5923245ab9ce5980e04fec4afcebc3e5086db8917d93c4c36ea6dd8fb832b42ce017d242979f9622f4dfb56f19b710c50c86fee367f08ae398fa12350cf6ea9e3f02304ea51a4480f0b6e5db879912d70a5083d7fbfa72d9c2312282dcd14bf6d7df02d76217a57057c6c018f00b720406d586aea6b712aed2153b71495f6788566efd13ef83af4b97703852722696450aa5c95eeaf7d233e09a7494a622e976a36b2024152e3d2550e3c16481cfa51f0a0251dce06d84a8356c4f44ec2aac2ec3a6e318f91154262b9a66cc83d4524e039bf7661be91a8a74ee96e6667db9ba9b5a5b77fb3fe01c942441027e6cdd010ca2f460d1a7b80b60fe866bc4de84d14891360afc0ebac9b712f851a420b2510c8f6932c05d2664e86ef37604ce4095a2c24e4920298e31188f0b22621b7b010eb751a484c683ae034c334c912cbfc31f06cd1492b03210ada53fc03c871cb6020548c5e502ac55873bc1234ba8424057407f418de57b5e1e65e65e84befa2b071ebf7b5b205791a716c24d60fd7165e25ebe34e523eefbd6a7dd55e6334d0209644f2a77e4326dc4d650e42e2734ec12f943c5554d480e206d4c02b326ca3401cc34f1812ec0c138a8a1adc4ff3f0900d0a6e3ac781e514739816fd9b3ebaa01a043d3722064168745ed1e7abd00d13db2f817f341699e1d95a057020c9c9a0c8471f8be4d78c21e0b8fcc4d6988683e399cbc46a77460f899d698e2769ada0ed6304f05cca2cd8d492107ce32328f6454e150e1c568b9cb9ae76e9fcd74da0f7bcd4fa301d47e6ceeb64b513d203cfec1a14b1b0ce766ca2b7b76344d01e00328dc94f66262f4d4e9f05d7ed3f631b71980242a3fc3390f5b2a17931cf0e6020dd405ab2f6dba054f20c15ae5b2530e20fc9d3b5cff6ae03bb2099ad8b6650d47e551e29840c7a3af5592b12bee0ec826c88743cf17319b9237933544fc30040f467150422d493469a8bc7b290419a3b5ac0e12dd82b185a3d689b51308d20660471b6e27392468e6bdfbe1c8e97e71d28f217f4766b392e7966ff2e734ed05e9434ed3865228e9bb974410a30a4db297c1bf6acd48116b9ba6c463a673ed046610918f4fb74eecc1befd3f2e69d3f5e198097bd0c9842a541779f92eb27506494ae483fa7afdc1191f394ed70cddcede4819039dd3d6ef388858050cadb40e5d7354ff9d1ee52674e8660b815551415dc266568213832ff3244ab70292b108d448ee52eb8b47fb5b71aa5eec54f09d595cbeb021e56baae6609348ed70d40d234d65a1bf4f409c859a5f2365083398fd5463ee536ecc9755b4754d69692505551ab8ce1f18cf3c1c176ccd3ed94c53964801cc9f45857303fbc1ee56d8ad00ba6aa5a54245715de37762ea80e11561fdab977b48f489526c7eacb11de664077c831ff5716a56dfb371a87926be80c339ec86f37bce3f4b319b339b7271f70a928ebef14e8bd021f30b9a2627ecce281a0d2c21c240231a77f0a78d8642630d5bb20dae55979d6b6551e3d8892008e2af2f4da750d9ff2bf0e097f6d0f80109fe016be02b7279117d12ac833bcf63c46bb274d71dc3582540d0264924a3ed0b88e1b27c02dc0c2c6b94a01ec02ba6ceee7c0d74cfaa0315d607f4dd38112d00ae7c1091daaadf9581a1d95e8fa16a51690ca9a32cd1067bd4bdee63cbc8f304e3cb7e2e70ea30f9cc30dba0d50c26318bbc3c4353501636a4fd092dcb52030dfb10033569457345cea511310b8ac9c3f7638dd36484040f9fd358568679af08d8c7355b9855d515f47999eab3b135b39e611f94887e1bcc278a8084b57e8e0fb2892c956ccc0e008e84e167a228508b03f49bad890e1c7ed0791cce3a7d5d00c9b20a71b62843ca34c3445aec9c6e76c4adfca122e998a82323ea609a462600cff9a28597e67d3d19a1d9ba47395286ff836bf343a042a6e3012fbf0622bb079cb5ea29833e6282fff347a12b32ccecb2a7330dbe6512be4d4203ae9ce9110de41d3681cf43d0a80fd83709028441ac63bcb940d33fb7bb0c01bcab116f2f0428eecbf87993a73bc55b93912ac8ebc23631c44a4a9006653d035d196a0a2d09dd0dfbcd4f07c56cf47da88e48c2f67d5396364920b8ea9b3ae86a7df54238077a4458cc51589ef2078b3ebe4f8b078b00c8156d637672a719edf12e5fe9ec067b2972b80ec2f9747270748f042dd2a9b2a35e96c64407b4a345b6301f590407d45d9828fd360043d9d0757b8d32a5a5fae234bb43645e5ce4d95146c910e4072b9bdf9d3f555abd7438c20821630e72a62e640a374033a971d52c8612053705262560181519e2822f9bc350514442c58b0761f7f67c5726025e23550c9da80c97ecd16e9c56a8e8e2b33d4a076973d7cab7b9c119052a20cba15f3305007e068da9c16fa464531a47021c6ed27ac7a6c28caa472a306214fd2d6bcfa9d8c50f80edda049a9b927781d040a13ea5446a334056b00f7581f58bd0b9fd6c552a0a2a6c3913903791b2c841fea4321776b647d3c367a710b68ff661de9518646b0834021bf0119e4b2e55bcf4d5e7b04626a92a3e124c8296382883ecd9a9cdc70a058760d77b227eb5cbff83c631346572a04cbf4ea5f101ec578ad598f2cc0b01396f8f7ef41ad3e6a0610436f71ce32a80d2272426a91be8b6899a4fcc6af107da520e92f43a0c0b2617918f2da88e50724a69fb8f08ec39e43ebc86355c0c071ee9deecf828f27f22c72b9dfa50a330cc16637722dfc94869c8a03395131201f61ff990e94e936579d0965a7ed235b093eb1239d9303f3216726153e4b4e90f4a864aabc64cedb7cde7309bb29e8f1679e1d35dddd89914632f28b789747f07c23604cf99a7aea96bdca25a7c0eb1048f0d0ed7c538b3ddd7b17b9d663cc806a822b1b6c20c748743511e29bd6bbb63a92905be2fcbb9cd59141cff7c001800bb621adc38dde844a40136d2e792c775833bc280c2b830b68ea2de1b8b833a07d7a7ed08645bda54ba4301f26e0f29e0576b66c854bb2b61e11f4332fcf7a90f03346f16fc29d2190bd5c3c1e5c3eacb0b1acf38a5d805901173199e6e4fc608da64284c5a840423aa43a32e39615cb717c924176b7954da847eb619fa6a6609f39281f51130ec5175209361935ad03f32f59038234b0555d87eead6fb5a2c0db639121d4e4003caf2e27058e5709f78a53f6cc4b5b0fecd7266e528f0d70e0515845c905b136b752975689bc8604a188405fed19ce6d003b829c601bb6b17020c4ed76cf1c5e2b2c13b36011f308a7cce19b2ffd8bdefd0297fdd2ed96de705e8b5e0fbdadcd83fdb76cf89c23c73b325585a17772a5bfdf8ac93b94480660c2919ea069fa4b0288669633a3d54b930f5fa395f321cccd058a2d7981253000940eb471834db32fe7700dba57426c23dabc2499ada11aa8e5bcfe568c4f5cb0b0c2a9baff4957d45964a7ec37e97450681308f270dfa347943e3691e8faf8e0ca196a02732a75d0400fee8681d2a49cf83e04b162f25bdd825fe058d4493f102a609181b56261bf5d4a195c1f14c07dadd9a9d3ad47e20fbedcca3d66a6a3005 -generate_ring_signature c4f2c0a39e10a7d2881c8753890aaac6ac136088a60bfdb67d9f4b17f5670464 8330fbc47f5ef2d5bcf3109e30396640363dab70a4760847e1ede29a99a660e2 112 e47318b68aa07faa35ef3ae5b8ac38acbb0fbaf3def0a4bcfff10395650c1eb3 0059e481fdff16f9b76cd2203d707be8ac31d48614585fccdc232a1e89841d2a b16f9868b930ad0f327f095f2f58eee17344ef219e5119c2a4d23078f3288af2 d375e7d98e7f286e38459c0a90a742c0e9db87f173609b3e088d6b0d1eb3a71b b8aeaad7d8293c44eba8932b8d7ebdc654d48ee3bb99a2ce9320b5356c363aca 016437d4ce1c2ec84d278ad1151e86005ba0757cdb110145110fa26b74038590 70bbd94b724d740a11d7d4a816c4c87f3eb7bcb2b17004c0ca11afd3671d6bf0 8346fdf809bce48d576e43e8005cbd6290633094eb508f7b1b9c2a8225a169b7 d66885f9ef8d956b5f7f65641651e31302075ce1b5865c5a241fe9eea9b29d22 8314224a7d53944a56d8ca1db88ce4f86a63b4663eefb779cf8153e2561fe761 bbaaa6e1e54ca7dbf72e37893f2154e027f4fa16175be97626efbbdc69586e6c 113bd03d75850ac055e90308da8b18f336ce2f7a33a88403dd26f5252506311a 652f8202dd69d0d86855a85f39f01eb961046f598588d8082ff4b4bea0501af0 24342da5fe22d17ca26d88f21357e5d375dea5e293ea81bd8ddcd5727213f196 392997ed25a6b43ff9f800a04131a81fdd28b839bbcf017337d407b145856907 abcae499ea45e168aa579ca1d7f5cde47450cf61bdb768b74f38684227be9a20 41793fa54c828bd0eb3a127ccbe27c4400055b2e7199ea5c918b88ee0d983314 c57bcceec6f61fb7de098dd8ee46f9d5cfc42cf864306601ab6dac9229efe5d4 2f9c53e941b12af7d6d1f49617aa8984be45080837d56c015ef28041d70bbb6e 16beac4e9d17d70a7eb517f110d8e7ab5cf60b4234f0064a884330e7af8f8264 fbdd6593adcbedb594273cdb4f71d628e6922f572ae7b94d560ad1ee543e17e8 3cc322f308c404283bc3a5476b262744a2d23402b4c8edd729b963a1417f08a6 1cd533cdf11e6c7078cce85de6290555fd75411a556c128a9962b7dd6f4d5c09 51ecac75cd208d0dd87edbafff96006fdcc82eaafc0a6a4084444802ad396758 1409b737766520c2d4789b6d59c26341c7aa1ed815f5674e794d9d0af5d83647 ff676950fa8d97c9c227cec21314b2970fd0e7fd688c64737a246eee1ce7167f ee4cef48d1ecb46fbceb2db3768f26d5d41424c5c7263e5df8dc206642445681 635b8c1a27a5ad357b46ed43f5911da74f20df7538ccab7afe574a4ec152b4c7 8f1ed5f84e8d913a43bf95b9f1566cfc3a067d30a28194f3fca347d2b937851e 1b97e52ae79c274e456980c2930f47174197392c8576111136cb6354d3f844a5 114069c991bde006821a4bf4d7981168046477d02fd97826d812d9f16f77907a b655863086ef74aa89ec9dbeb65bd766bc59e600f2fb91c06de972d813eb2ce0 a0f739a4e1b36c8cc807d771c84e081ea83a49ba98e194b0604d2d516665b778 58fb8b5f2a78e34edf2caec39b3f382d5c5af7e6efc6260e55d139a7371d25d3 821efd6c97ca6faa08c9b14624e4e143e52cde411d27c05cd1ad1c4b126f6e67 b20135c3919f7efc856e39bab403fb72d11970c31660a418b8aa67a427a0b303 fefdcd2c9f62d2788aed73c44a3632486974a7c146d5814229132b136e21e413 db6b3594fc8d6e4086aaac78f29e786d988d88818f175d2ffda41d5a472711c7 07b7567fb88ce0f9f467d8c42f1432224fff4629e4e46ab69026340c6b2a7e43 a1644980560aa39caf5deec4a899557be279f3d9503b973450f08c4dd6e82fae 74eb226428afdbcc7558ada5d79a5b6bfdcc9bb8338e75b750a27ccc083d919c b9239985dec6a42c08999efa4e7817fdab6b49301f04559daa1b80adf42b355e 10113e293205f5847c7c8d8f41083e82f3a525b5f56de04359706e5b473babe7 62156ff12c819c07a5b399c6a5694cf81b3cad3182d9c89fe9493399df4d7fcb 0d88e5842e2a1507642926ee6f947772799e27c4276e84a0a377f28e8787766d da17155ac68b93a53b3450cff5c3627b633b2b7464d3f0a74d9f47bc7a9b63fd 027b33143e2bc160670358bd4f206b7650ab3ca6120f163eaf7d52b1f6419ad0 38dcf4d9c7f711c8c3f333ecd93ec97e22abc07670a8c8658e6277d579923d7b e88323c9eaddcd13f3348bf85f5251f593f034ade1ac3b5309b0d542443a5e37 fa14c4f0c750ce4b52d4811caa95d3c9a7ad209966683cbf083e24d00e1765f8 838365eac90f707d698b76078f9ec1d896590f9adfeb5c0dc219d80b42929d63 e599e66bf44e7fccb2722f3a97df6fe782e80811e957a6d79108dd700d46deaf 63e1ac5f9583e1463db1c0dbea5f5780eea86c3863fb7f2f7144b094d52713c0 f30040af05b4f9d55c2cbe1de1c2be3d0a464a3c2a07590ca5248eaa76947ed8 f293f6c22c31534998d09fcc9f83c6711345ef9acb4799297af1a30ba50889e7 6adf66f4a7655d9e80dd691cbb6ba6b0c34ddf01e00bef4020f5fdd993bfa62e 7413a65c5acb2f77d01af852e1d17d05544d767f62761ec4bba98ebeab91db0a a604ccb842e25ad2739ada5513fa55c922e98a1cb47db75cd945fd1821a8a84b 760744f0d42cb0fbb23e3be8e04860eeff2c489c93e39cb7fd3e644313376de4 d3319b1bfc438b6c652aec300eaba43a8d1ecb0b10ce7b1456732042b0d6c219 f8a118056a6be318825229a5bbc9800b6002522e577ddef8382d272513672bad bc8b2c33bb713ed0d4cbd68d7c118e312fcad230965cb8086dcc60e0f725ce87 1d7bf114a3d4e60c584046097d308779be8ebfd81bc4cc2ed7434f700d0f877c ad197648d3c14b3a127c04a02b1bea5b8dc8e369096a3dd74c6eb1ffd9686449 6d70e6bd2eb0a115f51c5f4a1a5fcfb052340110b9db067916687ec8e5f581d0 5708259e096e2c0f23724fc835626eafbc11e98a8672e36360ab580e8abc23f8 64959a011b73de580cf315a0799bd7f4e24538dc149ffe9fa582fe16a1389a4b 987abdaa231434b344aaf7dbe32040701e1c7a5bd7cde69e92aa17800574d6b7 71e094387ec015e8bdd1684f778d544e0868a27a2cce16e3245863e279d36858 297bca83ea72ace5734a5b734743de740fe5d17f2d9a61afc28b5d3174a4d899 4b676cd079de1a9222bb783ea75c17ae3e16bc08b3618e4ff201a9fe849c042e e927115998105d74e0f76b3ce1c3a46b0fef5c68f6061949c3b3fe7f21a321c6 95438c8204ca32bd089293645bc0eafe8552fe14ec29a232fb1ad4fc619932af 5b8d7ffb49e11b950d8cafae7bc3319c9b399dae92de13a01ec7226a7821fa65 a1a64ad18b13235f8ac0c33eef61c152050f8ec646f4f92d7fc9848ad59ecdc0 d728a9547eb0391c9009cfc4ac05409c42fdf5609034354004f0fa4d5441878a 784d8afaad3bf0437067961f1ee47c71e609cada0b97c636aa87a4da8cd640da 9e562362de224ee6178006c3f3eaa0e211fa9c0b9e4a909659e7e0d2a28cdd48 b31b9f4a7e801c99f98d1b4523457a434d623afd8631163f7ee99e14a7b6870f 9164ca548303e3f8e0ff9614e064af82f8cedcec6fd76541210bdef127eb450c 82d00ea6aa39897b10534f681a739925d179e3c25f87d582982988ff8813d4c2 c3cc009f53709178261b9fd064f31bc3397e4652f37db77717cac41430f68b0b 3c6a52119f73f153c9b740b10e160e88a878a15a685c6e426494c1a190bf2ba8 787fd20038a29141633d628f1d9be6c78486db3256641d4c5bff7bc6ddf3787f 5f8f4faa52b8c26f56831f67bed9ceb8a3e98243b5d89944a0e21e78c523be86 ff57fef6e81f2c1d5ea0f39579e30a2330b7b30058d65cb5100f6fb91db481e4 6faad18ac496e6517c09c2ffb99ef74ee010f1a70016477aa92f04f1272cd725 8bf8561b844beb409a9603af71af4ffca292776452278995a2768f924069132f 0573f9fc57a663c0ff3b2cc35f4301465e2c5737ce086d2159adb28100a9d8b1 44273327e8d0447d0205ca23ea4991f0167f82a7dc7dc9a30040eada716d2855 223250e4ee92ab39a66d8c5d10cc1d859f8e129522763c03afd9c1ff982c8d3d efc9951e85bd27fddc0dad8179ad34a125b21a24f0f27dee656e5be965f54dad 2b31f9237409aa3ddd3789f095404bb3bf9557c7d1919227a4d812f2eb7afb9d b4d5426a38af058bed01586fd89efc34fc60cfbc95d4a3a34c722e05c5457c1b fa9cdd9ee59c34fa855609a835d3bde169d545ea653702f0a9b7645c8a8d472f 377077cbe71528c3b0bcf76ab93ba7451da7adea27d31234099289b05b22ab1f 8e5ea1ca5ff58b0d5e73e959b45a650332202e6e2c182672bdfc365b4b19f550 260285a632a4ead67966de806b34a32cbdc69352ccc25299509a3415bd6ac2f5 16c8b44afca503068a5117a0fa39b1cb5866672ced428ae720393e8140e4958d 6ec0f1ba70f1bfd60018d9780c58c09ef50ce619155a91fed21516d72fa65549 112cbf0a773da739a875b800bca212c1dfcaa8a7b006bb0b1cdd72e197dd5fb4 909c49c403a2533b78a8dc96119852c226b8a93e302d9a34ae910a8c61ecbc7a e16ca70091bf8859d39a34dc2fbec79f00888832d287e0bd0a3442a6067b7206 b41959aaaccedd4c7b4ad47f694421a9ab5f18365cf45a88d85dc4076e87c649 aa268761491f71d9cc3b911ea0db0efdc35676bcdd5194952d9d0f1dd82d18c1 cb1fd93f7c9844cfd85a4c0f89f5a41793bbc43cee813fe3d5ca10fca6538b04 9aeabddccfe892522302b7bf3e04f4b94773b0bb89b28f6f19e24220495c0caa 81cdf2134d20d8536596154521f3cc259e5b917c9cc4373c6f7adfa0b19e8009 78154a42cc5ebc11375cc35cab1a274f024301edee8fff0c08b5cdb0dae37c22 c29ad72588fc320a019c6c102e50ed32ff4123df1f8417e0e3ae315b8a6a31bb 5e053eb278d157296654a846a072b83224dfd088d8db57abdcfd8de00b2a8bee 5804ac28bb2465a4a879f07c063dfa4cfb2762fb9015da611c9b36fa62b10c5c 314e8a0ff8ce6e952150be70fa590a975e6629b6d6905e3f6233672af926dc0b 46 28ea3d8c5f1bbf699fce60869ef4ba24c3a92952ef89b25de660884ff5145404d6e87dae6a06b41a16cc020fa0840653bc7a40cbb248c58383940856809c9e0c09add695143f7ad570b8c5190355e5ebe8b2a576b98d7968b67ea0e5df6a5803a263ad7b3a2a9d9f0e51bc97de6d7ba61410fe17652da30e76ba09048c90b20369e091ea6c9627e01d0c8eb87be56123cb8c8643ffcbeffc5bd0c10078cb950e13699713db4efca2b055c2ebf3bb08d7b1f2e6911b58668ff34e81b1e44dd2023e494438fa17cc3d7e2573149a648f81fa190ffc06b9c38a4694d49723181104520c5496eed090e0cba75fda6813b95ce5a07803e1cb7666246783ab3702e30a10bffe445304ee6250edfa9c96193f35763b9ab21eebfd78d75a146ef7b27c09e6394e928331aea229cc0957ed93bbb3828370e7276674876bcbe9a40a4a0c0f9678a22ff7a4d289555b0b3a46d0a2923b1d99d06228f0da93c15813de6cdd0a99da82ba4172a6fe8896b605e0eb8f5b156be2af8afe083258ce2a38cc263305b521b2dca996c041fe1e999884f467aaaf2e5678941074c05a7844a88f619507f49e0a88ea12128d9ff573280d65470d385a4a14c3b461257d1e6611fff34f0b5a61d745cd34f6566089203452498690faaf2392bc11c376e0078eb16d09d3003f2b9d4937fa80a9f2b41f09f0ab08d46d84dc51080639f4d8e08195b03de20b0a9db2cb9a8b33ea4bc495f90ce781563298ff78ce77bef39ea96e37d66abc0e2985c5bdfc1782d4b6a351da491ae3303ea79278d07deb9053c3ca5bcb05a00a7fda7e61e1f56bc195926b9fd767a7813b3eb0e96d3e4d8c7b44945ee58a8b0f6c421ddc255d106541ff9505fcc73ce198c5db718426d207e9965c2f665a2e0faefdcca951c06887238fe0f63c67f38d3caab59cf140942336522b7476f7670977d1d2aaf6ad40b177ecbbc3e52d4686401c4125c5fc8afb2e7fd21ebbe746050dbde8816924913ad82e86ee96e655ce994bf9303785712248812282670d0e06e4a93bef51eddb447f7a65791d64616658a5ed0db0322e0f2b24a1e1b8dcce0583e6e0f87b7f93a5c2011b65f9924f59bbcb2060a92e36923c6a9d13b2e665089eb0dc6114a685fe05c1023ae256353f7c1ee96b04a578704dcab7c5e17acb087fe1ae38602bc3ce37d402421589baff5dd56d0a0a66f3df855697f4c888c20cbc3172f2cfc795560c3e5c3823c7ef1ed9e1515ba368ebff408bf6c3371a1406255d67bc70ec69b86e9738c3c26c4967fb1efc493c8cc04bb45558a6ee8d470f4fb720eff24573dcbafb75c533b54a4729912f067a35823a56c790f457f6da08deceea2354bf5a99afa8960afcfd733d9e24dd15d4df748a24bfd11bd7461808c8f9ef61efebef8ba60a37e484ff0ef885381970d0747b10ab3e5f21f6b3f10d658ce849b4ec0008dbc5e1a2a0791a19ca0aeec6896b9e8c152ac4452d72d903ac9894a9a3049daceb988d6ca4a6808f9079b58ede63dfbc262f94531c4d2705397b08ca15958b64f52b19bf1e98ccc2326c688c10831e269303e6b22b47ca0dd0934de1902e302230a452904d545bcca1e181ba797f977ef43d8a1f9a627708e3354eab711c3da5bdeb95d32db8802d93d221e6571feac35d71c331c4aa2902fb90e8749944eba5ea59f39414c20befedaf6c08852c54dbc22e033c98e137087d1a853b3f61a59bbcfe03f5fa54828d13208d84458ad9b0f46180179a7bcc0aaa75f1305409d50bfcbdfdec8fdf1dbf618a5db620a276c55e8b222240373703400f79bdf32a7f392503007743c75f513b645e9e0b1e59427a22690faa284608bfd12a949e1c3f5bb01b8d2ae604e84f2f4c5f75ae996f49bff9778622c9940c33d27fcd39d754a1c961b4dee0375cbfd42d89ce9a57efb356bbce3d5e1b2c0978894b43385366888bece77f0160fc3efe1e9b5a7570a74add6cd94cc2899501a9bc57514ac9dfa1f0f72eed1334476e7559a6c7709a2e9199df8b4e4978060e14f535bae6776868a9b2d207cbc16c1c8fcc0a72963dc4b1dfe1a3e23df8d9025fa3558683789af8d126282de4d6ca9b3f1a61644a10332fb068b34bcd9e210fe0e202e03fcd5efc4b1e77db443f75f7ab7e09cd4f111df691d71a11aa67040e993d6c61413c6a8980f15077322ae80f6a2b715b8ac75017ce5c2afddf9fab0bc535d9e90a43770669be198029c656c412c2a923483e6e6daad77344a2819a059cf8f651c1519ef1fe38b55dbd718f99195a3e36c5d1527061ac851378a3aa0dd6af9658355a1ac6c15a1e45fef3a511de69aabbd6bc083f554dc8786d5a190469d7b057fb58727fa537aeb0b5ca9efe4f5d89f210a8198e6a9081963ccc480b701c8caa3885521503c80512f575cba6494017b740b55deeb3be7c49002c3e0dc25b51b2cb9b2f89bc37baf2d71339d430b0f9f0c5c7bd4f52cf031ca74b7706af7d4dfd2ecf2bf07aa4765266c1e7db0d7cf946425d77d918161affd5f22d02d18fba12a1f8cdc5b7b1a57515c4edbe26d3c11e386f9313458ffa8dfe11210edc973608a9b164a2fcff94fece921e7e52b69b08b16a5d79584129924e47e709399b868d00f6fd334e63c19aed2ba7a7067d958d79cd347389211bced8b12c042281692d047d66754f0b51c4be4c173edeca1f7cadf6f4e2e4c93a5291149503cd0b12a9e5804a9ad7f3614151744a12126bdf6f720dc16c5e60bcdadca7f00ef615b9c50a0a6fb224acc72cc04d3dd91372d6622023cf81a7b0d815122a680ab92390263a254424a9d30d0a58475a5e48b73538394128a66bfd944a7379b70ed5f8a69eb4f9f22f076a845e9113d8c46689235a337fb4cdcf33a2be85c3ec04621a7f2c60072e4d79e2fab3246dd26d7b4d00ec1dee1f149d3365a64cc0510caf4e6449a3c7fe5ef8e485eb5e7abd30c60965d149819ed4d172bf1b53c6ee06c10294716b69ab466964b50a18eccfc4f21a03618f305194dbd168b999a8390937b7a506aa191904fc8b55ecee44bc54fbe4598b0a47f96fbce77bb858ef600b9753cd7f97fe1cfa747a7b4e0a35d7d154f88cbb58d0f6e219cb9849ea43d102035d6ea4cfd79e8c4df0c9b0251f07882dd0bf56d845d4e77d3cb5e06cac41074eca8ca8be72041a753c682776286c0f1a5cb1e462f61c4fe34b8e1b3a5d520f6cf7db15cc7fa7147d9a4aeb9bc9a12d32967d6d731eedd9284dc356cb0f2d01a59c9d3f3fb1738dd939549acb35cdab7ec2687028b46e90384009797eeeb305ac0eaf530329635c0b3127fb9f19d8e7499618276306e2b3a529b2af617d9c0219cbce0fec136ea8f70471fbef448a1bfcc99258c7d11523094fbbc7d4bce90af19e38700a546e80b1a9c2f59c0be7947201f26f50bf8f2dcec61e9b331e1803dc0cfd5e7900b9048680f3929880bb250152e15d269f387de0a8609efed38f0cf80faa4738d2ad80e41dade9f4a10a86e2cb5c53b075c4414a5c646d64e7e40e01cbdfa9e743f7a020ee7138dc8fc3179b9953cb1e6e889632f0006bbe10bb00b7dfa2c47840936f11021dbba0b20909dab77852c7f91149e348c2cbf36a880aeb52d41fa689d20e9e693d9f3300f0e46547c63f06533f41cc84c8243ce7dc099fcb2715177e2bd057fae0f563d17daab53569be0c0835110b41ad804840d20971fa941f4eb15039725d160e085da2ed5f1e392e2464d0a02e1479cc21e3d508ad3fce67fed18525be8fe5bc1be202ecb79e7635484b35fafcbf19fe806e410a5bb070d0dc93d28bcc6c754f0a6ac66d0dee99f64bdb77abd4dfc413873a2c016aafe7abe0dd2c8b3d5314d39258d61cd6a82a3dc810462da092eca95737530780d4597c9b255564eca32c4c1296a6aff4f5a7da83b7fdcc65364845e25c4e06ed6435808edb9344e6d231d354835301f342d32446984f76a73b36c01984c8028c7002f0ed1f1c19b5972e41cac78625cbc18e6d207ee7769d955a4167ceac023dc0b85f4ea3b0d7df0925d5ab7dc79d9f862a68b49bf7c3926454da6751fe0ae558adf2c6e0f685a5f248413b31269d552b887ef939f4305cd1a0b0d72f9302b257215ba01b4ce5c4a138475d95d6ba7e16aa70ff64f680526b4f5289afd00755542316edf852a3514a76fb53232ea96d54a066d9f4ed948f6dbca8732d77021f78763bc87684010a7db33ade6f3ca5474fd6c47ec7577ee20aafaacdfacd0dcd366946e23af146249884017d934c78aad10f4af1c782980a5067bdbfef8702092be91187b89aa2fedcceb1518f9b71210aaa487c810ee62c1f4696348596030f4895cd2d8fbc904d43dc3493a913d2fe17b897db84c180874a8a0fa4962f03d39f2b8dbde2b3c699594d83eaa986933f13ac6c6fa10b3f42b47a516892450a2f6cfedfaad75ad0f24094fec0d41f6aa7c6e5b7458ede51f0961d58a160ed0db6a7935df9d522f23268e4fe8fb500e4aece2a3d240a9cd83239d1f11f368803e2aa58163bd9fdbe880c470f633225cb64d61617cf97b017bcedfcbc6a64170e2100039fefdc23f7af3544d651daad7dc1c3baae68519a014dc737a5fc0fc30e251515605904d9129e97040c503fe92a73b3d9768571aff0c8ec8a322179e603263652ccff21c655d5a205df7dae200975ae9646ff95e1a1d913a6c74842ef0dc7d472f63c0a177e028f1716c9c6d3a22373d5a8eedf83dc185bc23b90426c0635116f61518ad605d53c2ce996244ed2c6a57f522040ed31aef386a1a3b3ef0e6d45be7c1be2082fd7e498825a0a8260f16f4ba6c70c9482a1b061e6cc310c0962f48d9fbda1577b892da605bccc93808771ea1d51192b309714f4f2341e1d0764b406047e1337ec83af00448df774ea0634e5dd556f20af18d69b163db9c60cba79ee212d8c54a3696646f9bf15bea4100b18170aee86bd35da56777c51e10d1ea56c440e99142cacae9bdf8e83478cf4f7d1b340932d2f816e26ea20151f014163e355e1c237a905357f5b66347ed3e27f063f2b0b0b9ed924cb0075c6f5090d1be47f095557390873e0bd46b07d7ae94c9e8667eddb2252a57df76b3f9804b8ec8dfaa02396ed96f177bb0a41a02917428b8d601ec2d6b95e4d27e754150f2cbf42b99ca47ea9c322d10526314d565096248f202554270fa93e842628850506abb962b3101b35c8791cc806f0f075279793d207c528a6c3d6f0d3cfbe4d084e3902aed389cd0c55930341fd213d3ecc937c0a22690a8579e1724e5354e90951c5d8800df8be11c8673c1b8746ef18f33f87d284007ecea582aba0d85be30581e4187e29c2888d457b4ec0854ecdef24401115db8c16f106e07cdf427fcf092d567284f44083a7bcbd6c35eb03ab7fe90e791290d4d5c055d466aeeb2dac0d9c0e917857d695d444e202988e0e198f15f8e15ffeb7eecaac3f950405249503752d9d1b1f86077d9827edb660dde77d0d893eb025e2dffea829a510d12eb409cc44ac404f310b36af86a7f7611815231491b64bc8d2f2c3aaebf9cd736ab50147b0a8d2ff62bb9ae013b5995695d19b80397b6b4fb23446a2b34f9da2a495013a7203f86af6ee795e68ffc16ac60bbfa684ba1a5f73eb67be117788a41b0d00d9c12ee62845d4f3cf4992ea253e0ec0749cdb421646aa7f0b532d97d8bfd5023ad501a901d6acb77fa267b675b323c1e0e0086d9b38b909ef402ca7f94f5e0286f6cde162bbb220496f01711d1f5692b8d5f50cbcd8035706f812eacf3ea50aeb739c413817378ea22fef8d031ec12157b88e973d13412c08b95c3711df570f77b99391ada37d4596bbadb80396d2e715782c014ea7cce672b6bcab8482a005d3726cc8cb2139d498a5ab05f44d6d61cb4e84570dc73c6c55658a34d77c63091feb693618eae5467d91da37e5fe68c044336c31ee89e5224e0a7a8477fa31068a6526189705ac7ae14bf99caa56262b564568e3e396715ad90e36ac1777b1037ff41ee261e4a1ed11c0b3819587523dcfd36611ae9872ad51b8506212172d049ee4a1f0a13035968eb60e4bfe87c46e1d7083a2b564962e11bf8cd790349a0e0c370320723de812873dc4ad195491310d1a877d6855414b994f7fdb6f56de0fa27677ce0980485cc189c477915b5b8c1bfebb850bba88434031bfdb29cbd10568bb1c70f25b127cf2dad52068976d99bdb46e2b349578b7e533a7d2a2fd53028ef41438439928dfcc1da355ab9c21b908c45b3e1678cdb403da9e5797d1bd090c5ca2e5a5b5a332951eb2feab1039cef63876fa208f9bc49badb1792f79c8064eb3b02ba1c662a5563e44d63c954f44d0e1f1eb86842a6fdd9b8015b67387021af5ca34f2170c85fd3ce4f26b2bd79721c13ff069ff53e6c6694c6973f22d023661f063749e120ce23800e47eebac0054d8b262248f17bb9f3437792d725d091f4f319a2f631788bf75d16f33889c502c5c51a469868ab591cae2c73834000d9f2ccc60973c99a4aaedffc0df9025eedc50e8fd3000bc51051f4b04e4eb5b0eb6560f9ac97fec4a83e36e98bd49da09afc2a464972df66320ffa5138d237c0434029e6b51d2fcae707ebff362d7ade57290c2dd591acdea5e71c1a05475b70ea7bcfad566e8b55af8687aa76b5da905b0970048b92aee41b5981ded4dc8d50070182b30c13e3b4a18b99e0ea92452a16bae195de5f91859298292dbad2144070a800456a64222251b4e5efd4e9be2f715bdecb37b64d9c194bcabac6b01a70ed88fac5880bd593f2ad2f0de9132687ccdb7aaf6b69cae0ce88ae817cf542400dee0dba2c2dc53651eb79c894951893c3e76a05764020383b906fabc799691069f94103d9074f2f7a1ae8b369bbf532d6eef8361bcee1f482b01452ec064e40c05f39e295a074eda84b5eb646e84f09765ddf8a4770e091bda48dcfd26a85a0ab075ae6bf2231fce46d25a87e520d379da873b3bfc23620f611140b8022fc30ce73ef3378e3dfc5aef08510c39637442f9dd24c7044d44a00ff4120cd0266e0e31cc7a09e5b2a4d5087f0287e7f53a0cf2b590fe8a96e31d7941dc41d105f20b92214b21a916c6dd7889d634d5c0f8fee2d3a307dc82eeea1c1f6606472a2e08d8d0092d118676582d44639a7dec783126eeb889bb74fc4218642808cb016a05769d0117ff78e8ceced59e8a65570f26d589efce998c4256491b86b77b2391037bcc66d903766c6e894615180370b419af30f5798921faf9707662a995b6b801e4e1e044fa5eb81ee59d70619ec428bf36ae5416b678e61f62016b9fc0c04e0417325862c9eb59b3bcc338cf9a8d639b77977e3a75b81d31f6c7ec8f47b9320d97b01f87571705eb3f3e544af04e300f2cd02e60efcece137e0aed7d19d2e00604eb7419d6d7a59cd6449c3b8cdde052cda100382e5e9014ce71763b74bf8106a0f659a8b0f03c18c2a2a51b516e8ce7fa028c30c0980c02cf2e6f0a4abba208f4d99858bda95cd632236fef30a7a94223771a91f7c79b842cb4c6917db14f0b4dc01258f1eee375c1de7f203e87d148ba893dbaf2415e7bd7412345a02c630f04778e72b1c93f5a3ab2a0355bada13ce60bf92bac252bd9f7e2208aaf3d8e092c77d4a1e7a9ce39b0434cefbf36a7efb6ab9d79fdc8abb0e43810cb75f7930c20d5ca6b6b5f83921a33d91d4836c1802a2b5973df88285aa21074158d24c500bcae16a86ef6c7aca08e6685d8a06c5db9bb30e893796a366428c81151b01a0e018d0f6ec7686819abc26ee15d646fa441981db9b0a0b97151a629590dca5000a612a24a2617927bb95c0b579eb0e383355d6b913625f90116bf1e216649040d59ee6c9f806151878117aea663053041f60618830ed344845a9abea0e57cd90c4737572fa1773f2e71746beaa3787a9efc487756f2fa3d336fdd3506af1c0a02c0bc8eba12f315926a0bb9f81927f23544c1529e58800b954f4d02426917a10af2bbf3e675c41baeb8ee6016a6885c6cc869446669a5eb0e9a0b252ada0562035379ececaa47f0f81e61fd74bd1fb378e12091fe1c8bf5c13d2d100d9a59620c35e7478f178ac8eb770488a2db4152188e05dacf8c544e4cd48d7b24b2275008986e38451610aa74f9f9e5aa366392a1789d7366de07204abb5aa274ebb8b1001e1ab30b156b3be2c74e77e47e3a1d8fd48da9ea4b4fb2acc2db2bc10e519602c850b2ef5ae991febfa7a60f83805c4aaa91f5fbac4071b3f9d62bb43847810051a04e88f96e8e259d775895d0ce6c4b0491c9856bcb1dd95cf599a895694b09f348f64c643bed85c714550aa6b134956b22f785f990dbd227e4be2d32646e0a994326eee8b82509d9bcd6db7320aa61f62cb4f2d98a48c9085c6deb96f6d508cb91ae1ca2586879def704ec86066b2cf446bc4490332a4746028788a10eff084d44ffcf25af752d22d010d3c293bf5e7961bd8fcd1872484c2bd4206793230643c13fc7aec0cb2f2b544188c505b4b35f7a111c3efc977b26e7f79284fe20093b41591fb2cd741acca1a73372b22291f1549308d02b652a4501d9043545320661d35e132677ff12b0248703307a545cb8e6475e324635609c824d1cf74bc50e33f1e855370b4cf7be8f92e9f5efd7f235670098e351b31f4bf66a6237dd3601d3cf305d2067d6056812c7d71c7809febfe24e9ef206ec37533f77dae26eb00b92b41f1bd57e194fcf21a85c6adf084ff0300e7387230975cc932aa5ed1e9f03c3bc7f5d643b93b7f4aefa2c768e4b1f9e8c45eecae224cf79efef73f533420a07305fed8c760bd5bbbd929301d43aec516f0f47483312469b24772027c11a0e021efb35fca3c8338c8bd1834df5cd9163dc6356e1a0fbd6e209140284657901a842edd438d192266db750f52de081c8afe3d2eb15ed02863fe2fef41fa035002c92322e3dc56d3be24fa75d7a43ec4cec48d75ca3a5e822ea9625a42feaf900996d37d4d41f488c263651414c5584168adcab739b52b7688a2c351088105903556e60b3fd66e879755df3f1dad0ddddc46a9da8699240db2819b5ed5aed1908e848c84cf5276d8b5df2e564ab36d5f1a9e80f847ffe9dceaa852577b53d110ab5cefa3e9ee90e72ab73775b1b7428269d171b3ad8ba6a1696a77c895b8fc90049dd1b5dd899c125486a8545d957d1b20f97e3be534f066c4661bd4da4a1bb0dc1d7a31b2a2733402919105bcd1f2310b7c1ed4bc9f2ea3ca5b05da71da82e0d749b2bf513ae3c6983691001ac60ba7a99b42cc7d5d4665a15456cde340a600c3951559e615638adbad7f9eefd6e5b97462a8f805016bd6cc28b1942b569630a5c71325e319533c92b343eaa009823f8503b2d73cb24038fee9462c55c7d920ea11b0c1fe35e0d8cf0d1c54494a50511470c991a295b8f1409fd7bc43c3c050df37179f8d27a8e8056be89f5dc6d575769a2665a11e1e5bd93f0bacdef9ed70b96c97f833342aa5c9e6464d2a559be3f480093d690004dcb2de4060999c79a086e30663e4bcae4a68ce731f408e890af7ff4a9e7f0abded0d9f1541ce7e7f80920c03e125cfc30b2558fa7ba6f6248f7c3746eb33329c772fa7ae63ef791350cfa930941e51ee7171d234b39242dab32b7a95f28eff35b652234fbd0629080091cb3816dda3d746ce0046410fce064614ca6066273bb234772e639e7f6ff4d0f34130af8da8958233076001e84c540683d1845b82e99a98cea14136e611ee609f62b7247d849f89f9d8f99dac928a5a8744f843fe07fa4bd5e263615dff5a90e0ecfdd8feeb33180e0f72e5a687228b82e0fbab979226a0f01b53672db4a740daf6e233407840063542bbbb6fbe176fbd48da75c12a86f0f87c7915857ab6b04ce70351953dd52cddd9325b9a045f9ee0a4f8dcae5e902cc25165838df9dfe0bd79400496b81bba078a2ed8e2e4eaf612831e75b7003a0638d25ec52fefe0a0400660bbb895ffa8cfeef54bd26607e006a6539769eb7fedbecc3a44ba23aab0671b26e7f8a12e08fe0cee83fdc6f629810be178764cb1a6971fd297b9e9a5e01f91133d0c5686ec65379052c1bf04d61b997a217ceded857fe4756ca27c9c204 -generate_ring_signature 2f70a3f91cd8002c712cc239ee2488b0046d4b010eab9d35554f4e1bd9d61b0b ad0464c09da58d6bdb00c070a4e094c0883cfb6eec67fb6532093193b4b1f6dc 55 e19f645f9ee2bd327eb51f336a58e2c9db7b1a64a97d6803d6b56ed4fa9cd560 2c9c1ca72314e021d1cebff7509cd33dec503d78b8249bf712cdfd55895a3726 7bc59484c4005b65ef76ca020046038e3a524db3c456d48bd6e6dd45f38a252e b849fae09d21667f19fab5fd6c7d0544b59bfbed39a524a942d48828106cb12e e16fbdc60de52e405d04eeb52b3b00c0bd1fbed1d5af08f4f35be6428bf4fd8d 9ab4b2beb1c9a8ae1c4c33c53fe19581488a836bb933b45bce4884604a5ffc96 0fc8e4a7ac769f9a692ec80395494b43e581f82aa65b4247ffd7a67291fa3b58 af9c421abc18053d52ef23a6846ddefcd717a96fd90f2b04ec33eab670dbd39a fe83d20f114e98d35389b23ec392dce2f3a110c45710f0c61905bc5c77bd6389 ca5eca751a73bb5c81f1f85ee75c0b6933f364579189a9e010b470495d1d4166 678ddee968928b2be7d171f934fe13a681556c03c4471d2b3a0c106aeb16e0af 58a41d7ce0ed959b012e587542acf3031f7a878d0668573f296e95e79c0c56e3 1f98f698a97f4bf968d8c4a073d62bc50ab6768dc8f0253a6ee7d0a9b8e400e9 203f2fdce050523df6932b625fcd99c5d592853d69594c6ced0d8d1a27e2df4e e682c2bb504201724f9d6831aeda7347efc2aafc78397f237ee36a2d609adafc ded39b9bc8849e513f81e5c22bb93f4d7009522eb63f580e9e5ef369a009f63a 9b4566467f59a6f545fd438293c87f895979da6151e0f713f0535a6a78edf605 b16fd389f7553736eab1855cb33822999b4c4a28ad1aff92776724fb2935e91c 1ce0853c4f87e754e05213c1804225ee1a2120494b20fe23bcf133b33166504f 0ba8b536b491f31696ab1d4dedba8164f7a1774d648b6aaf571e7a1dc00962d5 df770c4d34b57b2cf0dfd5e59b8778e4f6821796acf3e5a8a1ef2b6c6caea12b 000cd6a7b4564634f50f4cb3bb34040bbbea725ba56a0226603e12f16c75c838 68d4b4453419af5172aa54d1ff68004e0e8bc785605c87dbff1522a69fcf5eb6 6ea42b2fdfabfdac3d4e4dd520fd923c25ce1d66c5c536f21ac14af60a3a69b0 e6c455a08d17e0f82fdb4b1d2952d1f03bb66fd20b40d80f693e6f1621261b8d aa4da257da007f817e0ef63694a46500b56c649b6430cd8ad2cd98cdc92fd5b9 ae71c3343d5a2b6124deb034ac1c381121b80e9cdd3c1a3f8ca645be245c802f 55c97d73d6cc3e432460456234d554ebc88f4153ed753ae69a42ed8827906384 7d2d00d52c811603b99a492a7a2459d1c88cb5113a22ebec3ca09ffc126c8757 b487a8889eb4a2e809205aba036adb11473ec68efe7bacb6df6fb0ab34b5f563 636fd45289cc28718d29ec8dc3a53ab20f2dbdb81aa8a7e73ab256f16b075484 d4c8554683898da5f9da8a64c30fb3e343cfb19eb88f7fbf567901a9a98d2d71 160b282251304e7cccc5682a64e5f4a4c7356e199b023ff9818c25defd1e47f6 1a5b8c9c7a12ac3f2470048f0642602e07796a4bc7bc095aa88a30976fd3b873 d39fa1a932a00e90d0c2339bf81666c08f299234846b54124942a9db33365b48 d023e5c5466fbb9f9f4210e53965f90b51efaf8181b4eb11f28081781f618fac 441afbbf59c3ce187d0ea4b7f0b8c886509b32f762f84b64d64cb1ae7e4661b4 7d9521c9c0476cb8deb152527709412eab60f79e6dcae7d6a8bc764b521552fb 7753e5dd3407847543c066c3be5b57568a00ce2156029dced77a7d7f84b51c49 643c25c296ff6b10e044502b84bbbb8066dd60c13642ed568cd3a127269cc683 af193ec7757ba760a786e1bd6dea91abf67e0d09fe1dde9636ee740cb64b3881 97c86f4eb82cf9b008f34d77be77474a9dccbc7a28f8af3135804c4be4d7f4c1 42a123aee5df04b5d770c8d135ce837ca7b61f889afbfe459affc364683bbaf4 82443654c82e18c320f5c5a91eddaf6a009a9ab264e19fd377094165553a5798 1c281ae660b2a48d4ce02e48fc43d19d9506fe50ce98be6138ce2a12f104171b e697e05d9ca04429039d3b472766d362bdd5bc09edc5749244a4e1631d24fb19 c64794f14532943a569709acc96c45ad352cdfe950a925f2d6d5d15461e24dd7 215eaa77ce36cb9da74e8b4cbe60fa4effe632bc03b8ac2972219d7197b1755d e45863fc78b8e87aa3e1527121dc382a16697e8febcebf7371975d02a8c019d6 64c05b4302440eebf39e77dfee67a5c556c28240d204684197c36c61add87089 c56a0a14c23839ebb8fdcbbd53da469670546a8a4d9b988e4283fecba308ad63 ecce137e698c68abfc86d1e30b88654aca22c4a81c5badfbe97db39fd069f4cf 0261b71c4342e73833ef38913e92cf7acd43191b63872739d4c419daaf35d193 978a3653c3a18d1f58e3a222176e34d37ffbc816b80d11625e0ea049554b1778 bd8028231d3f30ef112db35443850f73b573f8a9dc3345ca5744bb7b97ff4009 8eedeeb643d1d8dbc79f2f62d47dad575e1965b8cf1c25dede6f010cb2e50300 24 eaae9417376a2727ae456952b6c77c5f38b9c92e4fd3efb869a6cdc502010b0396267d2697d38dc2c0d3f9885fb0d4379aeeac7984dcb723528c058fe7fccf0acdd9ffdd8405dd4d9dc00ffa6ec3672a19d8f6ad2d0458eca37b2e6c7c6f89083e9aa5422f51edd7809b07cfe04e6efacc67fe42fc77787bcf1b2d59ba0d7c06859a2e4232a0171db5d55785d4b9a48fe62455761947763acdb568b2b099f007aa22f02dff0da9dd7b90862fe231b57a967ed78c686ec94e2797767b1d36db01222ebd062911c5dba991ee6faf24229925290dce2d25b3eb8b878009b5332a066ba4354a86a64ca714063bb14589b6b12c891c3532375334fe553feb4b32b6056340bb3305d8b1fbd423656b6bf75af44f3d386a3f141166bb65c566c8bf80007ee029310b82e1f2efd028caaed863b6b4c649992c5462e82ade7ce699191f06f152c4014d8f79d15e18e0f6bebb9701c14d261f3b799329066b5253cd64370af0cb6924072f3fb7dc1cd392eb78e71e33c7772ef6393563d12e73509c9c040617306dc1a88f66e3e7f988647a810efa4e4a264781504020fb8b7c4072ce8b0b89c17f947b2b17c92f71e9476244be865262ee2407fa3339ba5975ea97f98d0c883e2e7e9539a06cf25880341e885e84d572c14acfa266319b0d37ca83c5e00a1ae825930d8c9f0f6f52e475222e0190541602df549615130b9cef3867117b02a6a2195c5b439764938e418a0e08ff17ba4aecf71a15a48a3d34028c897b000fdf8d73d8977464ddda4eb7eb4768d8ac75193e56d05b6b66d16537985bfe3402cc900fccaa26b56a1edb0193f1db92dd70daff618243bd88ee1b859fc7a8fa018bfedc642ac92a75f45897d164442d937801482f835012377acdb6013e6c9607036d14fab4bd810ec7c52f3076e2c3312eae3a6c9d9455bbb5e03272f7abc805fe0c7930ff94027f0045cf34444969e84289270c63000977de2d1eccbc4c660bbea06d523eeb97cae1bcc36ad1258c23dfa3f98a15593db287f89905e587aa040d16849607f0c55658d96f82b6216a62911594347fe259b81b1707cc963bbc06a8c0a3a2667e0b011d448f931fb5351aad6dd3579578328aadb9b6e711531208de59fed8be1677896b15a46e9685fe60529f7c19c1209175c87cacf4d5ba1a0dc47ee237063d4a8275d60ca98e9e2e873094df5be087464536ceadc366e9f80a3257ef387248eb702b85ab4cff32c55c25bf52ec106c744e6f482d5431e9b709468be2303ab951539c1007988523cf47f32263347de210296975c7183848a202aa110f4d57801a1cee8b8f80809ef5a4c620e1f66b148b8445301409157ad203809df456ed951adf7e693d046b372b3a106f5d162348e290f650762d793b850109951f7d11cf72284e937ea6bb27510bac5eb19a722956917553e369e3ba21037c52cc32506c4e4e54d2f68ce4a36639cb5b8161eda46329e0f6ff9337b8a20b4c82529001e10e9faf5520aa8a489b93f5b6b70d96e63df35c85acfa5ab48d07e161ae389dc35429a9cde17e258e7d83acf149190505d13db631216ab9e67b095e818687fe94cd4df7c6e0b0ff177d71aa27f8874f320ab0d599f635ddc0c70227fb81b00f38f23633ca1a5c43d672ae793be916ddb2a671cce2a57fbde01b0921611c08e2f0559e197b3a6a98fb2172b81e3f50b2dd9119c13a3ecd0df960010857730c8997a3839de95ce0e0681c4f0458ebb22ad2b9ee6bbbd2f6849ef206a716bffd9de5e6fb898393c3806a80c000b20069bd363d940fd3538a58bda70db5aa711c0f919b7118ea3346c2a796f41425d126a8189dad387d72bd7612150c350c51ae9446846bd499a6a1f53db916f633c273275cfb7260732a949a4e5a0141e7ebd4ed46c0db37a87a66c8ebe624ecaea0f2c3c954ef090322504544840e52857fee8bee484f3f633dfaf1aa06b2bd654f42899d551c317534b8c13597021e736bbb6329e9fb48aeea0ae1c8fb5a1e7f79f2b1e68d80412ff757c848b80483b19b8e3d350b37a140e8ac974fc6414223993d77ee9868456e09db2f9b880833519221072db682a5c6ee98b6efc523abaaf3290654a399674729d6cb2ad80fed946c2ef29c0cff0e30198fab09d6683487d1cdb7c0a26ed6685eb96ba8d500a3bf3a9a0710e9b41d8a3c7a69c0dcb51d7ff935d0a53da7799b2bff122dc109f9298f1038fb77648e15422b230d848edb699b66c3dc11135f02b64752d1250593ac5dc0c5c36aecf65b197de594b686e33cf9d3882b5586e31ff54dacd9ad00e3d57d7e9a7671f761fe15bef823b48b3de7d07e4e51a0b2a5d30bdd2fac5c01315fec3dc7ce5a002168920c597bb91a4da2024b507a05d29a75fd82def5470531c558cd909cd07ed0cbec68bd3a41d745b663f35b962bf92d1912218482da01dae9d9e7d38b5d5a6d76a8b2bbb242466d91a9b3593f4683b83f2671ab2f1302957e815c7ed1562e22fcfb19b7f59853691ee93b9fcdffa9ae42780a01f3430c0f58ba37d5ad4286c5c755048350d01d3f5f8f02ec83205d1effadad700d6b0eb93ca6c851e711f1201c4a5c60f166cd81133093409a6072437c0a9a778e910cf7c392bf13dc9475b203d02225d98220435904a145f7645532a24663efaddf05dc2bd08ca29b6cbd79308f9e79fa50938b16b91db53ccec2acca982c7960200af4b2740914d9f8cb018a451e4abe62d7834e450a8b99d16c8017a5127d2b4708bb062672f28ed0a90250c4cd754e04ef17e7956bebc857600835621a854ad506165904f04af967c5cdd86b276d908eb510c29e4b35726e40ba2660994debf70385c1995a4fe36601451926d0a76c7beabbeac83fb9e56bff487dd07840774e0e7ad674b8a7fc89cf6d26f14546f7373fae88cdd4e6faf8015f9ac31996fe2908a3dc9dcd66a34a025fdf4eee018de32f3bc674eda88923da8afd6744d7e0230674d3e99b93d4affb1c9a9bfdcae352556e44b0a4b478a2c8c2a809b27c9202056a40cddfacffd4c4d31804aa715771c022ceb6b1b55ecaa78a8d84d6c0600805e2eb0a8177d9080ad7be1cfe3b1f6cfee69ade4341eebdf3dbeb5976295deb07ccd24ab4c7a4809ef48cfeb8c39db8f931a9fdda4ae52e9ae6b4e91a075a19090b84d65381051e02d0dca60ecc80a315698608207a8963fcdb341af8242ece0e3b75341340e2feed3f74de77d21f0b2befa50f8f7e3d5059ce39b5aac6958d06c4cce49425dd6b3a9df40e9c1fabc82529d5733306332ed0d111670fed29ea07ad6251d7f6e3d5937c865fd319e230f236b29c8882c8d68b310e45d79ca5e209bdce9813668c97d0689b58433a73ba8f3bb563025350cdd2c64c0632d5d86a0fef746c43766309dccd9ccf9e5cd5a793055dca574597bda93935f62b96d1630a7bdf953d3594587085e7a77acbc10e83c10b40b78928c98cda3c172b455a01085d198bb2560d587910055135a549b0b73b488d30389b055238fed433b6f3e303135d34e9796498ed2891a9ef78d9dffe0c68ee911eacf947d2e1c216d2e0740739543419d67c80e479b1904d751438578b73d9a50b63b8563553eee59f45570c95e10187bbdea4f19814e1859119077e23147ae8cdcc0e2ebad55222b3c1d7054214a3e7a34f6e161521cffa00e93d09a1e339f9b2f22844ee1124f5334bc5003360185c32550c06eb51ad2a01db6fcb59bb9b39cf4f46f52e146dfd6b6655038f241b7397d5240f9c2942c95e6499cfeff0a5eb3e602a9d56b305689d7cdd03a8bc9e601a41f080c7e1010e2c7301ceb2c8bcf2bc5eb0daf9726bc815aecb068009ffbd354ce575f0372b1dde38c125559f61bc7dc8903b991fd67165d75c0fa2c7443145836fe814858b5fdb77b7f096bd461e2a1687045ad1fc9036baa10631afd54699063ef52f9de8c2dc11e15223e0f01af2258cb6b404f665e04ec60b1d6e2acbd2193d702d63358604e6ad9d9c9f1bcd0a3601232cb970258e56490a810aa07de8c58f212fd098db0818d13ca4bfed5d0b44a78a34c5dc625d045a0137789e1cc12c5d2ff583f3e2c7f5b845ea29231a23fcc79e8b80450c6c86840257606a73f3cefd3027d88c23ccbf6a2303d490ef5ed6e1af0a849f74364256099e7774784153f26129a683fe15a87970a9698faf4bac120754fee7218248d30d6e7c39562d4fb59bf693dd22d89c248d18b90938358f4eb49a2e3633422add0385f735d14008fba4fa218c2a834ae780f360030b0a1ff5952ecb8207b304de07a896c255f098da3fc9701e89dca066ea3c72692a8fd0e8027b34bb0b191ebe0da61a9c1f4fa43a993a4d37b6d33fc80b5e841f9aa39ab459c4fba269c9cb0601b55df58b2a5b3b22844ecef8d8d1ca84d73c325955264197f2b142983f503c0b16f97c1bc644280cf6c86263f0ad2589ce731e5265c22bb29b7930d84bec8306e213ea4ccb67ddfb51311796f5db70fb7b8eef92de905828c349075e91eb3b0a84b2b8f91d23913ef090620b0d9e5bf504d9cffbee67c48189f6d38e92e82c05ae13a4e54d0ed852411ae332dcadb4ee45409187e19fd812eaa4b614edefcc03cbe27612876f3a106173f1c4d948da216876826b4096be5ae2b8320bc915ee0fa80240fc1843348ff92025f48d6259fcaf2984b2c46c9b631cafd366de874a0370fd5e519256dff86302bece079e9b1e9bc1f3f063d47b41b24bbfad6a44790007e7afbcd892175bbf1a106895490070ba5dd74a4ba6e97bd9c5f1dc5e6d9d03344384259cf3984160cbb7ee78765bba146647e994fe29569c8ee9e551bd3302f4a7945d157b128ad73c8658d060b750c404f89a868e4a7113b4e8026fe9f7006918ca80ff6544497771ceaab420caab0ec60d9cb59a44fec7a75914a88fd7028180a20fb2d9c91e20abf0bc4b1d9b1c1caafdf9aaf0580631d64ede07422507 -generate_ring_signature 0ba30de6e6d2dbc9fd491a2874728fd0280ced45df74f8c8d32cf2e77768e7ae 0b3de9afe1359f3f83c005c7631c44a73ffba60a836ae3839d8d5927c4471ed0 152 3a358906b6ff770b1f077ae12d8f7f3cde8cc6d3c19a8c9f5bb6ee11b7388e7e 472a126f08b58e6cbcd37f5ac22cfc6c662546bac3da95da2ba09452f2437a91 fb4235440693b05fc30539541b237b7f9b5c7ad68df2955361c3de5dfad8e9b3 591196da4d50c814756f5ba064cbd38d1af3671a407d8a05cd8d0e056a4e012f ae702c49cb7fb2dc5df1a5e1c39f2b2ce402f2945ddf59241dc047b4ef81cefc a585b5a0091db1a7ab1bedf09903aab8d0b0a844d74e67de526bc99cfc23534e e092843c820545862a9e6bb15ded38458d654c953971ecb97fd1bf7603c1dc6e 322a688cc9f928b3499215ac5bcf36b55b781ea58280315c156841adff146995 ed842c7281842528fa2b17d4d910377ba970d1443a686d3742a18816b10b4ad9 daef8362af755e7e0d52afabe3a44575242405bc654d2c5c59517b98090bf334 1c3845699b373abc21d24b1107ce31e3d3eef3e7ea96c491f7c6652ce57465fb 14261e794ec119d49af09f570dabf6729560be83464419a93d02f0da7ddbbad4 33b42eae69927195bc2de2fb5206054ecf6511312a2528d4929a4a9867002813 c796fcbba62668d7a0d2348a1d9b5f06ab4fe9b157b0d846443b41edb9a1b6fa c95fc5d08879a12aa68952ae167d17993a7e2623297f75fd2272c82e32bfc37a 6cb2f098c37f9b3c863566c29d56939ebdbad47aa24cceb566cb410223a846b5 b77692430504c280106fdb7f639c495d8ef0d7d558dcbd75b23c03f51f8a3e11 4159b10f826b40b00b32991efab391e8f6c0222bcd8ebc0ca35f01c56023e0bf 3fd351cd999eea1dae072737a2352de6e0d35cc5c03b7b66784aae6b5e8a1466 9a184ac51ae37b7fd07b91e062c085b6cfbe8d9fdb539da59cae447ae7a7b78e f3542561f7f3543004b64d6f2d45dddca1fcc7ce148f8fcc240e5dd1edd2c294 f84af966409541a111985fa9c0ce4d203fb6d449f337eac3527765a7026ebe87 c85ae572e78401bbc3594bc748bca1f6996461cbaf8c28064aa3ec6814bb41c2 4aa24501b140eee2efa6fb6980eb6bb4b703eff58c9069b54f1b30dee82f46a2 c61f9c3d781903381bbc07c6ccd2631e6592130c7ef184d2936fbb156ec1560d 146811dcf8fc8172c1504d032ddec6798525e1d8a9429517ad73641cf2ee7499 556394b1a728ad1d4cd280e5e56837702e029da3d8718f50e5bbaf6f0b05ae17 0d694f1df6ef6156bc6f8c5cc2a3668801e9a9f29f8436d22d25feb331afe929 7ba9d54920fa1923eb772fc59f73ec3578fa08866b226d11e983cc8d2f8ca631 9e4f1d09796df5a2f21b9732dd9a217ff85b1acfa8ad071c0f8cacd16c875025 77c0433220a41feb333fe7a4c76ebee4dc10a08644bf530426b99b21dd0ce3c1 1a34d9fe7d8b4503fae56512bfb90821147cbd0d276ced49d456da3aa59f50a7 70f6147b9a07429cda27dbac9f7b69ff87293a49a6bb32e48e5bd7c6cece8bc2 52a798ffabffe412acd4e9afe2e21ca0f5c3b7b83481d18e98c03f916fad4f3a 6fafe4e0b6a1bc4e19439958d5b5eb6300e5685637a976595748955ac294450b c9081e39f92a9d9c0b0d120d3bd1bb757133873ee5e02dd6e428083df7c89cac dfe7f7dd98096fc79fab45669c8173226c19f953bd7d7eb2bb2739d79dfa2e23 3c3856df0f874c2d766f14ec708a366d23128ad13df93d73db9156b7d9f074e6 866dc59ada75d08e8c8ef670218581db6a3fcff78d62d8f058c4d7f767ce6e21 53ec7e68ff4f0c96dedaee098825b83a4bf56c3f6e590f787e4fb02a0491a035 66d5a0aa788d312053b71aa88fdb961ef22eb238afa1eb8a50f739d821a27e1f f5cfeeb51f1238168ad45c7daa7caf2632caeccfe64a2d97e6f93bea5fe902ea 575fcfea8d4bc586a780acaaf079f4a75d4bcaae1ff008cd5e961fe7f7a7bbfc feccc7e6f4619532c2b5cfb43a0e7240b35e7824b94086a93c2f8ff76bd01392 7fefcab77bd3284fa4867c63503d1ff728ebdac0574632be0dd8afbe601f3b81 6bc526e9c885d131970279cfc139e14d213ca40384f021541e669b8b64cf6c5c 864abdb33355465fd8d918c99bb840169d53e1532c8d82aaff27ccd1197f8ba2 2d37d9199ec4b6660639f4419cf184293ecddf90f8d40420e61bea0971ea583e 45890e5e880c0607c04b9e703270355c33cf305485b9c15b9927ff2728bfa5f3 e5778f46b5adb329727cc6f39685138b0ed0932b3542873dd4448909f7d3acdb 7fbc3ad923ce86f8ff5c34ea3b657fff3eeaafe9be2e00ad74e02ab4a579e558 a247dd3e76847f4c67f98e8c62ea0552c69410b57f59bdfcb820dec44fe377cc 31f23aeac4ecdd85e4833c8adf88b7c481a6f687b45920ae746796c878ef16df 7a812f1702f24563d4af961789b40be3d5b7836277f5c96096385d5d373c02a8 6ed55998c547fde1f832dbd3c496c4b1de1d17cb2e2cace2cf188d38ecab1df4 f24160e9814de8c26b075a6f11f085788ee84abdca189158a0420aacdd9b6eee 8255924674ecbcd958d0ecbd07c8962bef86f2d2a5aef852ec8f1fb7f9d69c35 129592cd3a9d7e1463862b37909bfe1d74927582b6c45a6afd259ab07ebccbbc 5d44bbe0a39fb5f35dab4e900a447a42ec4153eb53f69eaa47bdb9bac8137d8c 18eceddf52dd350616f472d7d1b8fed16d25297913d7f2f8ed04883b999ea48f 3af3a5785978cc66e200dba17afaa3b40ce4a5783fa9333a6424c4ba06fb7618 901492fdad5dfded52b21c9959a178334f8e7168b6609bfb077e2a7b93ede507 f7d79b9ec77df91be73f405bd30216aaacd3a055c63efc4f86f0b109798655f5 dbd4023695ec11a04d54740936f407fb2c2b6cbe979e6ef5bf0767a801d0a178 93ab27f15725d4630ba876f4a58ba78e4e560a410aad53a8a735911b7196c6b6 545578c450ac54f055d46e661d12d4d0adbe0849a61e2ea03d6e3d4638da7cd9 3391411ea286cfb663d15115d113f7b2f0826a840395939e8d5b611433f2d113 57ef1e875e3855b4ab4670fed3d630e6cda0e1b9341891e59b7e2f9914c947db 658ef5e1bcf430e63a0b9122ee3a84ecc9f47388f7b31c6fcf366eb8fa9d93bb 2658013beab0b98888d1eb2f795f64f16c8855dfc503d3a66239a0047c5593f5 69277ea51de11f6fba8a43c41fd968d901c4e31e822f4b5b3553318f847b1995 77257767504d0df2a8e5500dede46246b257f7a9f304d5e034268bb02c23e209 06dffc165810f727b325edf311d3839cda6b71ef24fefbc3f455121092d7a938 a50590f3d9193887a33303080421388a59ad10c546a1311e16c8a7313dacebe9 4a0796e932306dda67589cb37f697c2810df1c1747e2b2fcfa293b4f4c40c4b9 c5adb0180758e9a960558496d341d36e639d8355e36e076ad31859371c8b1014 9ee7528e99713c1bd54457781063dea844349199756561b4be6c384b779ed8ef ec8f58b2e690c33d2d30384142e787c7b380d422bc7845a99b9d36ea5e1a3c55 151a797a15a5814ba694cf2bb077185e5b1977d9d18eac0ad772e1282ba5ce80 c08da9572b6395a5cc018f6cbd1da4883d6a2f7d3d76502fdfd72b655e1bb5f7 30ab619fe844790c294a0434cf663d887898c694b1bd374b2558216eda20e683 196b383d573af3795f23d86ed51dd55d9c3ae875464825141be817a0be662345 153ef73d1f5b17c75c970bc76c29e4f2ff6bb212d76e0ae7ac38445c7c76ce97 4c8e7f367d39ea4210f0154dfb333766531983d43313240753483e00d1b72116 dd237005fb83081cc37a3d4a781426d7c72fbf170ef7973256d3b9cf212f2f8e 8baa3f9913024928a3c62e82d8d89ef7438fe7110b9dea1dc138c7618837403f ee427f72f1736d9a3b416b363db0e19250a04cdeab9969f6209bfa556cf79e5f c7662161c5e8c2390fb938a10669beb2713a9eb58fdf61d4476bfef7284db7c9 f9bac6ab6e3346e88b5e48eca67640c104dbf8520f08e240416c3ef27a29a332 cbc2ee1e4553e90b60ff83126c636bb047e95d7d17e941aba994bc5e122280ec 601b1aee63a11d7e9f930d9a64f672280b17787131d12e46ab0140ff01a3372e 90fa3f00483356004def6de99c8dddf30d340b96a065d0e14e7e2ab5929771a0 5f762e1c5d76870eb65f31f7751070f2664a90dfa7ae1df568f64f8478368bb4 8c03cfc38212d86cd7002a0947128016a0131983552f6826d20a84bbb052aea6 b188375c340056fb061f1ae3bb28ad17beefca3e7b5473fc9087e257cae927c5 dffe5382445976ae1ec4261b3370718468ba4ecab5cbfb50a22ab6d38285d3c1 7283670d3cb766afff7f34486489d5a1a5b0bb823fab655657166de815abec24 f7713361d1b7b267f55265c03a458233b8d077007687308da0ae02a3075c0d4f 202b3150f80754ff14b48f57a8cb648ac372fabdd91874e45fd0b6b5b26b2de8 cc9ad47925cae8fadc55c3005147ea9f93b4b8d7b1517104c24bf0b893ab46e8 be42bf5d3f94f09be5042260f359c8292dfc6f7c27ab8da88e9d4d80ee553e78 3bd3222539be23ca0edc229d3eeeedc1b41b8ce296978d73c86e762af79aa887 4080704d2b09ab7ed4eb0b8b0e439ec8a2ead3a06f817ce3f9363563950fdb0b ad35286dbd79ecb282fc7cb18c5dc7e7abd53bbcb26c824a3141fc054b8d15db 4aad9cc0beaeef5482e2950883d7253a309ef56e7fcff5df54f5f8e654868cad f1d06103cbc99b6de6494047a63fd690a1feb3082c52eed0b2ecffd5872629d9 3b55b2070aa5fbbc6f5a160de4e9f3c2f4655278f38d02bc6f5c5987f6a5d46b 8b6e16fef782152e2e03abe8a72a0c961bbcd899b27e62fc1c59a9b703aabc87 a027b3419222bce3f34445a08da62ff76712b33bbb62ff25436046e8c97ab78e 07fc0c70593b4023360ba9026368f270ee095f65cc7ea57f459b739f73328ae1 a2bcde7f605fd6d05046e0eb333081e8491057e369713b9f9adec7af1b55665d 687186ddddfd074aeffc932f3d56fa3edcf50479c2c537dc81c712c80f0d60a4 868475fcfd1604aa98ef79866cd4a7ce314850928880af9bc2e31eda14f07f79 c46158842eded9c26dedcc934ca9d51ce3eebe5a6173710225bdded6ffba7643 1ad3ae4a9d113f8991939e7396f74fb0d96280837f67caad692d18722a93a200 0ef7b4ff25837eedf897a910f445eaefe4520042bce6d666cac0b206db3dd1e5 7734da981ddc91893bc8a527007f824a22b40844d0b4537209e5d3d37bb27091 ebe6fbdbe09907e6fdf2c89841e1c57cf69c6e324f8934559cef075f7ac0e2cb a5ffed386db8174633ba84fb978f88f8217a6afda3ca2e5acf6e70249b0b4937 c130b6a8e631f76d972d0f155949fb7fa8d093e576abdafa584cfd192cabdef6 7edde741298c76767208089c421fc96b728a108eb96e60618ed458a25e62a0af 3453fa314a63d4eb7ddd4ed46dccefbc3c939afb8eb2d00a3ccb4592e5e1c39b 001057c3d37ee9247b99984c5e5270911227156b442a879a9ffa598b9c0521ee 337b79ce5ca206ad07c09efd4b90abf7d907be87cc842cf4c33a4a75f40d03b0 13f81b423b6fd6096ae3e0877ceb6fe79453a2714d72f43b8779d3f9ba7bdc56 a6f2f646deb6f9e205ddaee63ef8a76b3a5b54a928ad4f3e1dd88d16fcff3992 65cd6f6ebc27c988cf885cf88705f1868f66350f1aa9eef50e3e9d032f8f110f bfe7674163556b3b7f7d14d706cb97df910eaeac8e523d564aee31a02d63ced8 5fd0212e3214b85eea338d564a336905fa46275d7207da69f5c94cd76444b66c 0745a8db9d2d5d2cd987cf39a9719da6ccc397dc8a979bfe1b98755b83ced8c4 8d47529f198578fea2df336e62bb2509a978e570d20a03d0cc5ddee398b90489 3c7ab76d75cfe7c749c818939a4d00b22cd55d17811f1e81a2943368acbc9930 9032f04a5cfc16c2560a30ddde438fd67b34f03d067266cc1ed0d6b1f21a8da1 82a319462f7be63add5b1de1c57d4f96b7d01a37d37c1402b8ecfc7c23172e45 5fff19c41880b0ab48f57fc5453d4a14d5c22d78f10ae7bc7a5ec7c6d397be7c 7b4bf36034148946aa80874237e075fb735b3a5ba7a25a085fbf70fc19f63f16 5e367870d1be56519048148b998a8da59d93b302f8136ca1ce8265d92653398e b1ae44dcdc1345606bc107fc2ff729fab2c215540b088e6117fb82f8469bcc49 09a26ba5b7ec87989a79d47fdec7febf79647abfd0efe6ba1078a15b345116e7 a53e112ca9565d939ac0f80c9218438e05ee1cbb40e094c4c6b9778cd1ee9341 4480d3899ff9742956f8ea9686d3d52082cef3e150fd8d647768701d99805550 0cc2d147b225cdeb658984df806fb6121de31e3c43d73cbe979c8fb8251b0bfb 42ed973a0b53fd57c7676e9facffdba1fa56437683ea6839537af906ed04733a 00ff260d90022060b2c237a1a68251d150fc53b94af0b47adc9d661dea155785 8d7bd44609717a19a777658dc0aad8ff10c8fac87e643378a683f0097e916057 47f05593d2ee2b00a2ddaf85ba6a7bb9c56e09cf8ade444503dc4d426e2af9b2 caa9a7998ed8f932e5d50c969b38da96ba85fa5501bb63af160fdeb987f6bee8 8629cfd12d7e4d98962a96d5359d6df658740b02e3178842da6536006b11b20a c181bd65f77ad32482f0e347091f9e6469d6867b55ee420a6201fc7eaa5f9c4d cf322dd9ef25987283f3e35f9f457c5a8b04e8ff506851d068ae1b0ff35ad2cd 7a1257577ef572707447108a254387944435ae9e37eee8b92ac3b21164599e0f 57bb7764c06e796b8594ef58c605ea682790abf86cad7c3fbb6842f18c70cdd7 1358110fd9c8fca453bfe9f048ce071eaf67dd7f1cce4f4d52e9cad916554c03 107 848c0fdacf662cc9fd1cc58adc663b801b1819348c70d7050c3ffa19f2818a0f7733bbb9dbb547894d181c6d11baace57991c8de120fbe53ec12033b6e773807a84d6c6bf1f117ddd8d1f610501e24da2fe67de79ec69e485b383239b43b1804df1450cb1f2176aba74e58ac5b30a123318aec9cb1b8e0108d246bc489577a005ce98fbd69220bf9e31c989be15d0a3f4bf9574c4e3dd7d6f61f7e69ad7d5d061af789ff84cfe334e2dc01e9be3b6fe460160fe76c7fabc88a76a5c0f357cd0c85e6f175494476ea8a54b4fc342510e6a0f90a699c53c732477a91c97f908e0b7d48672e301bedcb5c8b0130e158894c1e14ab2df94268ee9567539247f4f30465cd47c5688b42e321e8692c9d6a6646b83db2ddc67de0f75e2aefe06e49b10d29b9af0f392611e6ffab054f62ecb84317f2a907dcf3232dea6c3430f67f77000beecf89388987705dc8c9328c0e2d00100073cc0d299cd22d824e5fe1e7250db49a56fe3fc9279b7467e1201f75fe684de2ae1113ee0800a385356853f36303ba7b9d43f8887aa372e86828778d8ab0b7be04b31b8c028907d9e808c3cffd094fcd5149150f914109e69b0104d3b4ad23aa2085625d721028a11c460da9370b517fd19f66b72626c9df5f8ad35a4492826f9cd57f35f0ff65912dca47bb8104db074ff6df7701c2ffade238672332bb92a46c2c6799fb1a63904818cb654b09a62e96e9a9c362d0bba455474bf7e9d6e8f20891b85aa6171d389b0f4425a10a670f82788cef79b731d35383387ed525698b923960871ccee2eaf214fb4c510caddd1edf31c1c08e2d6364f718258e928cf1ce118aa8d7b722502fa20fb97801f227fe677f35841dc50ad6eaac30fa74fe3da2bfd5bbc2fc85e40d26f54bbd092ef1830d9db570d1672e2de8a6c6e10be99e3038c00b0db4fb77c6be170a8801abfe3c6747b04920be58a8a0dae9ca15193c5a551e6506d541d4209ca859bb0abe6acf7ad5e7af8df47f4d77e59dd550a95a1c0cc0b751a1935fa9a8deb45b09ae093ff9057c2b34cec119e6c343c6735a9f8faee189fd4941eee4900c782200c2685a5091dd2278cc27f89fddfec1ce6ab2081f5f38f3fe855c8dfd2916560cb7e91aeadd28bfe333ac9eb06bbf7753240df35940b8326524a929745164f605d6d04196efdb0b93867b4ff6a91e533d11214e31d0262bb0099e373451c8f403cb3885c4556b71b1b0143c1ab3bee5d18efe396bb9c2087ffe6f1092c7fd620119a5b3f2db9ac9b5ac608f46c427b6bacde43e05707e0e46ecfa3bd72f26dd0dcd0487db71c83a7275348cae3ecbd2255263860cb6063b7f80ae1189f966030ca1da4e230e18156838fc7e674144b09660f775233502277d8586f2d81582a1074287bd247e03420160c2b3e9a7542e0ffa7be9c199df46c3e8c04e2cbb49990e735cf17afa86dfecdc9d4ffffccfa5e2808656eb5bde2c9b613b86133d21640748258e09e0695e14adb92c9c48bf81f3dc5b6cc638f240242ebd66752a6c9c09726bd5b92093ceed83c4e2c0462caa3a9f9437ad942c33126f76731ff9398c0049275f1d186e86594b8cdca8b7248ab82094869daf35db534c980de8df27fe04d72955c77389f8a12d887a578b312d024a154a1b92b87e854d50134b9e29e50eb5bb752789d1993dbdbda5160c02bcf3a49489f959c9d101ba657aff043d7b0801683ca20213b780edfe4b5edb32e5c1433375092cd808f3aff7d8db94e81309b1a300a7abb258e417124a2e902d2790ad17d31b534de0475f74d998628b6f05504da3910e262cb27c35f06a724802a321262f2be26e6d2dac4f959b2d928008a22631cc8d75341e48393789a9527ce77452da8519fa394726b17d83fdeaa0008f7125d0e787c9ea999f0c9a9622bb6de26b7ad69466a0ca88075741d3296b015da065b467b731096ff052b2d22b332a811f5a35a13de51e363afc12355bca0fe065881920f117eace034e78c7345bd8fe56397816630fa43efd7227d67f4707df8293b1cfd588f0db13eeb16edd25e264e292121b25ec031c95cd6f8cec120cb79b0caee5b2fc39ab83bd6dc69d2deb40babd36b5ad647889252eb0f911af080a6608f0d76e5f4c5984e6dd2869f5ada91cfa0faa23182350f423879ecef8057486c664ae11b6f7a578b48e2bd4f68cb857716f3dee5470d08c9068d2c6c7095bd09bf5c48b5fb8bae4b14ab1eb28527935cc4f9684b0b3bcb443344d00f3008a08659135815cb29cc1c975c9af8c5c3631ab47700a5606a237835bac9cf2058f87e6478a060e1e30215538d9a174d0c9247104ddd4a1953639d693b49eb90e84ba9175388fd9ffa986d0b9eea4829bab68f3d347be37a9bd762d2a57afa9065e45e91e44114f5485dfa154b2dfd11328fe88da51883c65c3be1b248a32610f8050ad7f30c41b0e73db7fd3b1b7b89ddd25cf804e33f6bc23b774284ff6e60e219b8df25a1869713d23439b9d5661fd30ae9176a3bd4f7afa04933dd5feca0102834c162776b07649845a0bc8f03e2ec923097fa1e1c0f64831e2a19357370256e423d20fd5dd117f20e8082c8859ce148653c2694604e93a8821d309c6c903bf82b5becc8a905dcd923ac964882a119a2de0ae2a31f31707059bb8f11c530944228c218ad16fbd11285833887d04a627bf8eaf44c4f55d9671e72378bd55015b03192e5ca22a6c048d9b888def6a63653ba6a5a286e2981217b333e3bee403538d475bc8c9a03ca32a229433ce877093a3099886bff0bcff17a7bb3f4ba205e6bb3579533fa92154aed047cf14579ced6ae4a550a065419264e7f793595e0ffdbd40119ba7bcd8f144fc8fafdb1a57919fb207950bb842a8d0781d08cbf70a2430dc76d9213eba9da4d6c927afdd2b65cea338f2a50deaf1592374b76dea07e827f1f6d41cda7b176be2987c09ae7ae6bbcd95132d075cae27dee168d6060f6a8876b9ea0a7c5c241c8b5bdea491ed694f7c2eade495819740fca07ae590048da949a90964e43ad5be2adf0cbe2e865fad6f48c2b8e5cebc81d21b974a060f2bdfb5ca38bcaf8a40beba5c36a898721a74549e570ea3bf7e3516a7062c9406aab469e0448096d52a0b1a84066db8780746c01c30659132f8ff264122b1b008b029e49dfe3569bbbb92998c409326d98bd3acf5f11c4f2d647845ce2e54b0012d619f4428abca24f5255cbec003c91c70b72767b467728fed9daaa229fef60c7e182a0acb350cc1a6cbb3e9ddfa4854ffe7473a7d0f9e8cd1881871ff69990c0b20f40090db92a22c768d50dc8c5fcccdb7dfc18f96d2acf6c3e1374533dd0d1d65302779242fbf2406d92812818405caa3ce9ac3df979fc078a40e2ae9700c5549087db7dd29aea603784ee2e5105a689ab84857be90ac2e02caba82df080a3b91183bad553d7461409be7535a4392af205380fbd9c933c60e3b3870a89403387b5d6f33131b3c5fc251495045f7fc4cca8d61d386f10811ff97249e5ea70a1c0b5962332c27ce1d6f378cda113b9dff1bc08283b8493d13757f308889eb0968347fcd23b9fec4cd28394eab2ece663392bbc7b8df9f6f7c12462b1986640f044b820929807bf0d107807580bc6db0d52b4dd89b9395457079ff982961630d25a1e8ac911ab8bbeb1b61f7537ea0c48bfc6e20d6207e3add7374a29dda490c92a429ff6d174d086f7ba00ea22d9d14f3159d48948a696d5a86bacea90e2800d32dd0f193935273f09c498e25ef22512bfde8d8be17a0d87cf85086c76a010e0b5a4135cca42f2b6d053f16480a25653f9ff921513087028b40bb759e2de90197e55599327a5eeba01dc4cf5ef8c88be88bec1364d011e4ad9b860fa4490a078371a5b1017d7c3c0ed07ac256025084b63c1bf8478c8dfdb7bea8bba6abb30d180d07b23f6c055668146dd5524819ad3e3919fb7932c401fa54e76724ae2800c297191d86b1a9740d872eba9b27259b870c9432545d9bc1994ee6b7bb84d60eba61b3080600ab778e08564b76d5a2788cb874e1d2a176b907e205564422be0a3c1f9e07648bfac95a698763c42d34d345b389dbc629a34a61b62776b7eac80ad18840dc7424e930a4de4697c0b26a3a2cba81feb6e3784b88131dedad5fad0e1f5922daf2506c4cb6bfb0d2c60afce34f546cafd14522edb79dfd2077501f0612645d312235698c37f1e4ffaf36eb4c9bec18f89283bbf022b943f644c19d0652a413ad6ab38947a8e2bbb1a9b8ada2fc2fcda51ee8d163fc7cca74ab99e3073f84024893edb8dc5280720a729cc8773f93d93ccbe0a2506f68da2b213578070b693c9aa896bfb690550dcd5ab9494be35e14a7d814b7b4d399bda90c438008e4416081d79c33826ac671167939594560b3e56b23cf319942e68ac86163c9053e5a9a5a38537eccdd797f2ce5650e204d447ac152d6cefe448c95d54efaef07ee67b976ba89009d9c7da32150fbbf7e2b57c60cc2b77edcf68964f0dff1e404eeb5463f7e0a6e612b13407922e2b69c30f3b59358d52f31ce60f37e40798701f251d2376c87a530bd497e2b4a4bfe80c839280888ff1bda10541c7dd182bb025177deb3b9bf6c4597c8551cce3a5cff2b8b6332bc4677c48e0a589d0f46f5074ec287bacc81357cc1982b06c7c1975debd9ef4ba815c93dd1cd25a7ec985908e59d7b7d353f0c19b16131011275cc5d139de46510f3c64f6e9514f51368830ced8e08b9c33d0e56e0cd2dbf6eda68eb0b67126df0e0224b20e03c8ed5a5980f4436207c314a9af8ad76868c6dd0be717e8010162eb491b16bff18b3454e7f0c2a54c20609b403c3d52b09795c38286d8f08d8ca6665c1c9f1eb2d7a6871750d6a0faf7e45e3cca6b7e58a81124ea6378ce39b3e350780abeaa01e5b190393045a7558fce19385867f17b50429321ff5bf229a2889ec96bdadf7db29b2c3940e6d6b10e0b4a58890ffcbef0cc87fc466d36105f6614619f254b6fe895bd15f0048864b3502d902ca02d03002a2f2e91cd5c5258268185a43e51ba911419a7f05c9bb405342822338f13a0a4b6597dd779ba7aa1c293c858ca896badf74a6b50da4c0795ebe9cddcc95acdce55b9a36faa9870bcb4fad7dd694216ce07973040aac8d509dc86fd616cf50b96e0cc30bc9bd0a48a400fe3a16abf875ec30173f07af3b795a4e6ba9f70b36c78bffbe9f1332305d2db3b659dff4cbd6fd65abd207eaa9985c37648cdefc6abf1ccd6ac210118555593561c1a7e2ac49512239170bc76cd1ea7bbea3601035d9e173884f722d2657f3026552646a259498b4dfe60c1b8a9823a3b92c1d6c169cdd654bb5255066ab180effc1893383211fab1a2e0a2e65904c3797293925a3d4c32ee26194e6429c778313c518f9bb64ec4394340c2caab05339016465f9476c7eaaa201ac8ade7c480a059d6f291f32521d389d0402da00b01d55f131ceda07ff92c2fe2229943f40e31ae1c9a95a6e5a4bd3180d393115f29d77e5a11fb448c180fa7f6ef89666e114bac616f2001a2322fd5f022b389a6c55d1dd49efeeb32eeb338ba3f3f8358dcc0fccc17579ee9cc4b70a0ada47fe156c120395cc19e9a4bed46938519a4b274d413e96a4d631ed291ef70ce61d08a712b10bd11f78d9faec82e69609251ace440636fb17377ec6e2dfe2061fac9d0fe80c04d9e3b4d4681baea3c5288a06b007d933ec2216ed81c73add01c106acc2067d07b54306ac7e4d024ccf257e5647b62583fc6adf52ee766ebc041af8897f7ed6e09b4c9cbaacc99ee3d3a1f34d034a3934e9105d3bf623cba40a07b17514d664a009067740b9fa2e0e4e7a8014ede8a6e71477e2a5c77ed8840374bdc3ba5d844abffe8989f450dbc7256ecffc2a85b6217179c74628ffddb40c052b3193929fccb11941d18cd50ff1e47876dc7d10e4d48bd4261d696a8e3a007f245f9c48a8365bd64b6975f0a8abb994559df4b3213df56ae7ef13bbcf0104ba6408a7f3047b718a83ddfea0a76d954dbb2dd747ce3e07eb1bef9aef5cb0007fe30d47cbbbf0dd5aaea9df5feff1112f4d56c817169c6ca8bf8faed26f210cb0251f9cb82413f510c32d06068b942021f12cd116e61275b97505e752af7008b1b55dff950dba575586e08c3817b8c98496b3c8d6e04ada9e2c25b4b75b810ff7741130fa6455307291ef8ac8f13ed9b3c93d6478cc42370288855beac3fe04235fa66f970906acdfdd7756594a388feed42505390eb0ebba94e80daf126a0b78b226c5fb7da592058f9e624b5131167a4f45b30041028643ceda71f75fd20689077661b4f48801fb6dbc9a17437565d3250a85266bd0f3b92130677970ec0d8dacc889e16dac31063d2f8c0a64b3bc258485ec7acfef24d21325dd6a8be409daf37555212f985769de8aabd4f4987208524a08b310fb82bd2d77a49e32ee0d08e4289345dcc750558297dbb5ce64508b70493735f96c9edc10ee45b0fc6c0a117ca42176f288e75c599bc66b1b2f7c678d17fb9f649e49c1cd6efba829dc024246e2ea3db76fe309420f69e7c62ab339f205f3993f9e75713422e25806bb057b53eedb05630e1213d0fee48cdf09f9d6a561dfe3c856eb2ad5e19a7d5fc70064cce0d3a57732c6c6bdaf95605080f190d13592f22cfe82da94d0a634a8080090fe251b1147b8f3583c3af98d09b8c8f427fbf92e0baeb37872628bf5ff8f06663e2696b3130a46d05d6d850be0a781d16596d87c5f3324398b83cba840a102a12597bee995a49c7809b9a0e8912764b0963352a63b95fcf630dd2400b1bd0d206fadf62077f953d5dc3fa2415e49cfaa29f07ab2cada25a5bd82ea0afa8b0039896b299b694da5dfe5c9e0888eb7913bb4a19e361bc634da386a41b5eab5057fe9aa84b4a4888c82f737d5bdfaea35e25438ff1f5a7228491d93e2f684d809875bde27cae3870448a70abd01ad15c15cc253496e4d225b18d30387e7b9d9032ace4fb89513c8a3bbbd8c95b071fffd7527364330a4c4c73312a6b0b48ef90b0166747924511a378511cfdb9c1db2878e1b822aaefd2551a0652a85a2e18f041d23afff2c809d6ff422e455e1f40227d753c86e1878c98430e62bf9ed28850ec9f8b5760276bf4e4bba1b416b55dad0de5309713b067a723f8b0b741c68f00d2f3ae8a07a555d77b90bbb70e6fdd03f1460d2805a5167cc53668ab705fc9d057c5485138a89fbb6d88c52c89cb324a727134b10fef2bff959dbe3a29326360b90891be9325c73a69cbf8d918b634198de770c793728edd27cacadb5c0642e04f203107e109616e1602eb9f91b6f0cee7716387615f9eaf2c4a2cdac13a35009fa2603c2e7df7749f64d901b6a6b356c341d44a8c8489436cd1f0150afc00b0708ebeeb6c1272e3f840b2c3bd0a97dc54fc5e58fae98241c43f53f0a5a934a093d77f09cd0f2c31d5c993868e31062ad2698921b2f6f53d4cc7601246a04b3086f65ce2e21ae1775007a18e117184cf079477dacd79d8c12fdc4512947f343026b8fd5f85a5e2678ed3ec8c574841c9a5cfd582b8d7f1794f097ff3e04a8e00c1b05c9ea3a145c80ff849ccb8ecff88aacf3dfff6f37bfddfec05457167cb108b137eaac5e17922ceb0f564e06e19eabda5e99ecac8c5f1e0681026ecd3021027102d0fdf04c0593b69854b0468adc2b0f230fa0eec90a59ad74a95cb71210080d1c2155eb51154dc6b372523c7fd8d7a95a98724ce60ffc3ab5bd7511a86702475bb2267c4f632521692ca34d2c2972376708da2805fdc72b8f5cf6baf3490e2fe6a70a09d4ff4bec406bd1ca11e6d48313819dc488196d0796b7901c3ab8040048f6212b847707491c04445d6a5b6a25b6f6a6fc85b7800a5ed1d413b75c0a9a1e0664c9a8b2660b238d30c20b872d1c65b724ce6c82775611571fb67e850de732d8ab1751ac2f9b308f6e758263997cb12bfa83ff18d2a54f51335ce71108394233b6b5cf0924cebd82fd5e1ab5bc8f45a090fab15c09bb0f375ae127480d61e0223d001a07086c7a28a2daaf95255293285a56e414b594ad4bae49a21a0038be0ca7732e64364015ce9a271c570d8ee9f5893a50b77ab04e0b9fdb34b3069831eec9619b63d1525d16f2a70914ba5011bd92c586c9129790006ee91b6f0094b8f0c2d75aa3ea399dba73e54c73ac7afd02de9a6ce6a81d5d13bb1ca5f0078bb9ec2b63ba79b96df33aa64514184aca3a2816569103ffd38274fb9fc47f04289bab6adea974e2aa6877950c3df0103b302da4ec181892e78fbbf4483aca0f782da5a872afef5468d09635c24aacdf676d840554c22109e781eb2d09097e057a21b25ab8bc39c281ebc21480e6d9bc972e2b1ffadfa450051dcb1e5d637c01d0391506787a9cf0bed6273a3efda0a0321dbf48c5fe4a1e5a470ce108d5480ee24c3eed1330051c46874a674ffa202cd545f5b22354a9f91576dfc7024dba0642080a5e080c5b9454d3a0025fc80fd315e32d83f9677cfbd8e0ee5039011307d1d3a9e90184a12be9b390fac3f9d8242d4e8fdcdd50caedbfe6b73c9401dd0e95d112b28a604b94bec5dd802eb0db53d8ed849f81a85190de6e71d9894d3c04782e2923c57977b30aa55a58f7d73334537c90d263838279b939777a657b570a7e1d02c2debd71258676e57c804ced408187a2453f9732b811e862eac282690f6061e39d80b0ee493d603780a60e97398fc7ba7b97b853b74dc58e38ba53a104bbf6143269c99075bf7bb68be828857ab394b0427925bba7b7c881003ec7e50cf3706763308aec4c363ae11d6ebd93955a06105611d3fa76cdce4184e81e45067ca70f26da138c87643fbaa0cee071b69e01d589d18d2326392853d2a867d6056c3948721d1b1b489047a2895972917e14f50ba4e787d12a42b4cc27c43e830a88a6d787a62f27d363fa91c911215bdd9980c08974b720d66b05da3ea0a45d076e6914b5237006a05d3fa7319f690d22f9e08cbc81088a171159db515962730463d08020499a59cbb41d88ddb1d301cdedb7491340fe0cf9656daa03f2e24f0e36dbf1874d8f54ababf888410598518797d4b1c96358b9be3b9a4bb39b4787086605bf5b4e91879818f499f7648688738cc417269be27ae828c1d9edfac8040fd409057fcd2b97d8f024a6b9573cf9e5cd69346ca6fa60f9b9574d23b8d0de073e1c60b0a8db7dcbd964c058ed804b019061f9298b9e9e80d28d551b02e9630e2447e309497fecd794f5b430c0226c429ce6b5fcd586954569366b0bef476b07e4c1d6049dbe4838a212421c97496b9d2920d96f6ff94dff6fcc965551d91706733325b3c48a360fac99bc2436f612447a7e74655acaf68d6c0327829067770da573351126a1db507326ba31404357c53208386916f9ef8d7662e8f6b3471b01da0e2886169c9215ed1cb9fafd1da3a2cf00917aa7007f8779ec4455277ecc059d933e2afce960bd9f7222e7d1f4dde040fb4558f69048bf3a1b46cb78237801f91a5a0676436ec0b7b0f10b1bd89b790214bb54f64f5867ef8deafc51004d0f3cd89555d9312a768c49031940ee3e108ba6448eb24fe02bdd04d0916ba0b203636ae7e564274559f211d61f15528b646b5e0354ee792528d224351d4f5ef30c94e3fb756fd7334448cfde5c4ea82da49c587a914fdf69bcb04dbe6fbffdbf07f31dff4d0740db30e78b70f731bc361aa4c8fe84e28b3b0dc62fbb9a7defc20e14b9ac25d1e90d4188aa783f6bf5d11e2bd288136f8567e30a8662de1fdd7d020aa61bd50a63777c9099965b1825951321f901c8f513ff03b5cd9f2132a63b0ff8573b857a57798978fbfc1e4935316e11bf2dcf6d0a531b56ad312604dbd00aa8cbe091dd4b4a632052488e0b843bd302d8ecb77a39684b49564364205bcb09d4b2d55b6f2610c309f4f4153defa51a2c71f7dd40408faec0ff3397295a7508f75d9085940f7cc626c1b342f73f056885db0060ec6816467cb60b885486160d7e2e775b6254b3e13310a4229ce0d52ae7af95792a7bf11d956f6b77cf58eb0fe937453c16ae218429f759b617fd1c9a1b656b4dfb6e37d0590af9e76d5ddc04caed59fbc5cada04ceead6c84e3ee30cd46b01a71e25fe56f5d713735d80a00bf8fa9bfde7093f88aeeedab8968fd0b5a1d0bf9ce57d042853b3c79767c4c90e898c551fc62dcdd4ffcad2f53970c17881aefdefc7b31672e0ae7edd48b1c6000f13a3f63e0658ef31f2d9b6831062ccbd95c1077fb366ee5cd482d6f199b10be0da73b421074bb95b7d4d4fc77b27741a21891f4070ca8b6c47bad24410c40f51cef80f07ac0b736b81c72f489bf1c98d5452c9b1a376acc02f316c3d6ca2054d9ffd7b9a43309e90b0252719090073389dc92fb283bae482e10b00bdf9740b623f154f2e73c1012463c5d1bc1fc0821d4b73a70cf147c3044462b0ee8fac02ce9082e7552eace098a227cfa2bb3e770abf744b8b7c5a92358c853bb434b906d0636040b6a5ebae45bc63876faac43885febabf59c8d3c3f1ed817a158e7606e522b17a5f61b2a5828561f52ba7eb9c15917f07ef58dc330fcc27e9097e390a171854443c37d603f1f3d1e263accc5b219d9a79b7b415c8d860b9c3624b210a89dffcc7aab06f1d13cae86c3476f037f5209968e471e888bffb41f016d1c50ccd68610e402564580a4bd54d55dd850ee19ddd482b5d0994460691a62de36c0dc175138b7ae6a5ec21d0f2aa78393081ef7d638517e9a11cfed9cebb1dffd60386eb62ba5a7622d5bdc7414ef4ee279492488e8a06996c85a7cb27c06cc88e0318ea764c56bbcdff92b07747de1d96f12d29d3b0802182d7ace3c08f7fc9890bb84b992cecee38f8d251aa7ffea6071b1d2d5310ef322ba96f577997ec15260e11c20204aced8235b879d4bcfe9304a4032768324549c77726474ef6574a530aad630f393522c8e2e1a0e93940ddf368a7c62bd0ad0703eb24cf66db597a9d0d2390e9424e0f119b0940f3bcda29686f25fbb9b929545fdd075346eb88941c00105726fbb36b4fa9115c0359bd227aec612f9253188377f8db9751ba768076071881159d2f5d1bba54825afbe244665e4dfe7ea562b62f28b746ce0f32623601682ce2c3229a5ff14dcba206cf7d8ddca1e6cb93a29a414b2cc65826faa7e706fd37c4063cc19776c4996c87bccc37538d45a0c80b907b3720ce606c42c5850f0f8c3d34016dd6ec1762663ef3e83013b2c1655e4e4c20e72b1be7f069b2bf0ee1d4858c37ea52c3a2980ed79e78cfb6e86aa8f85686299f23bc1fc62c55570108992b33d13df169abd62a25595453e57b5c6e1c2724965c06b7aa506fb4ec095e0c86a73db10b2b16a3667e045fbd8c4ae66b636156be75418be3f71904c1089056026d2c527458eb04744b316018d3bb17b8acec3a0f8e919bb259fb88c70303c51da13dcfc9a1fa8e5c13ad5332b30ab297f8c71020d17d97d4bde7fef30c3478d97a496667c876cb4575e00868ed4eadcf01138450aa42287afd5f188000d15fd50f43e01508a008c4f306831995dcd88cd80a3bb9d133f7eb7112b10b0bc5ff61a955864b72309d25dc0e45053bdb884ed93e6bb9f0779a225680648b0039bf608b39c3da583f5ad7670077afbe3f41279c9df7760126e74ff5055cf20e0de870fb41126b9da1e1e789fa09dfb2cae73a5f3ec2c9c0ebf563e7502f560e0b24fbda4ce29f8805c24d20303e9a3a94772a57b6668c12340391b0a466f60ad6970ea7a5813f77a20b3620b9e1e433f003ac04988c2454b776da39ff89920d9d5a20fa2b3d4bfedb40d4c384a91b719483af62ddfcb141230cf957de6b5e06cdf17130a8c3233aaab69a4c47b3b67ce20d47a39df64f424e160e6cc52e210f9409c8ec5a6711b97f20096ec1c3f102d4a9daaba9c3f867dae846cf11583e028fe0738057a8a0dcef9a6ccf88510b7dca83260001e3253092f3ff25791d6400880359ecdd45c79f48a2acb125552dac5e5926faf0e7262f795704a61d3ae2088570a4dbedc6ac3b6f58639144faa2eb8fb09a6f1f5a8ceb42e1173bba76ce07bbe535d782d42d4ef315ccc95b92cf8f8dbb9e8322b17730cf7e055898b9f0096e5dc2ee04ae051dce7e9a85547f7a85f7fd17d2285d3b49ee0695b95ee6f401440f8529ca8f2503f2169e95de3fa059a82b7eb175555d4c057e18487c97f6022b2ea08c73223f41b5179cf0a5ad89314f12463041e13d3f1812bbff34e923008e684d70a5d6887fe9648ca59172d1ecdd396f278476b9913e029037dd9d9806e3c6e1031cf6e76251c7dd239b9110ea6fed10572bfd402d8e4505c8c689ef06decd16ca06e9ddd74422d0105720b7e89fd94037b3ad11118690d0c7cb66310f2b6b355304dd4f37f41e78c02385bc2acbcefd4639f73cb867af108cd9d5e400127f2c204dc5aad3e7e71f8d7eaaa4ce6a82fa6ee58d2300dcd3c5b2d09a43099be2e7eaa2f85e53afeb9a2c104b1058bf862d99c0c0fcc7861fdc35ef5a710e45c496802905c34e71a170f60c0d5ff5afd149ae285bdbc1c61f1777c71fb001bc4f0d378204307ef691a36e5b3bca6ca2e1c45b9059a7d2a54b7245a6ed540839976d0cd7c33dab7d617c22dfebc09372cc3afd54e6e4b9ea78ef9d5a797e07fe5689dae6a53c2a790612d53ef918df706e4a120906f56f7bdf446d84733c06319a7cc3712dfcf83eb43ed60cc79ce7c8a6f1e1949d7ae10ba40cb6ecfb290bc7f1766e342f4dc52b59ca2430293e94ee1fd8e6a49467b7d05f13e7d7940709a8198613d885e9cb81535d1bb823f37a751871ff44b2635dc251736eeb868606feaa52ae24e14c231b641ae1c7e224f3c415a4acb46a1d358bd1c17307ebea0aa7dbde0f80df0414b68c1797b5cbf208a2c1cb900005e79ad08632247e501509ff883b41224ef381d7fb414e58e6e4796e5da5e449baaec1af97b14a1e44880d658d571ae26ca9b0b9f9df5ef7cc5ee161a4c19768619ebd95c5c0451895a90912b02449d9f1242f7d57c56a07dd6cf7f261954ebc33837fdb3ec53b443c990128e79f778b674e83ba210e8b2f0146d6529b348a2a884fba193a00746894de092e7c6a084202f8c9ee82eeb40de38d1c92ceac085b032345b00dee21f4f3c10066f6462ef5ba113918dcc1d785c6cb095590df263ae60660f9704862554ec80e2bfd353b7ce3ee7fee8c27e46fb3307c4ca19ccb3a76da4c242fc2d2850fac0961e358bcec2d97118c1b5337eb35746c0f13156682d5b20eedcc23f421da6001ea5cf0aeb54ca01f457e9e5f8007a24be9a5e6c6f644030c55134d25c704af0b2575a5d4df990c460edeecd8eaf7e18ecda4c710824cebd9492a0d4398a0790d4660854973ad4311eb71a9cc41c97f6c3cea1ebebaafc7adfa53bf9736444c06ad9d63355882eb4079d88c79422a1b363b555a413803dafb9844cd7716ba3c0136bdd402eef6669328cab8e858fc24d831d44f6f35b6ac15abe4925615fa570fa60203ae74cd04e55d3940dc223bcede1984ef9b9c9bccdc3a6d020201381200fd7545ece2194e831dce2ffed5221372ee9fce6e74063e7256b6681e3ca2f00b9a4622b907ce1f05c280fa192d943511354e29146854331554707539f4066d0c09899617d3bbe68d3d881ad6826a592e972ced5c50aac80818df150180e31b04 -generate_ring_signature 70b22a0dad712049354729ee250c77812c8d958133d282a7ccb5c7ce404a7b18 fe481951dcfd22fe788830a0710c2d3aed379ba59ce250ae71ea46a62487f0cf 1 00677c75a2b2cb1c5b946207d8f6a4ad98d9adacea1e5481fb84af52da4c48e5 e6bd4c1635814c70cd87d7b1e256e002a954c151ac5e0e6cbbecb0ab7cf74801 0 bd3eb811cf89f0b1f6e6af53c69bbe51f2c3330395ab3df88ce47f7e3563af0de58ef47505a0845bd45366c1a8dee5712614087c96146edb5288635431f49c04 -generate_ring_signature 70c5e47ad8fea9638668fa564ba969be479001f55eadc7869b102d77b5cc5214 af2e9cbb09401113f23399722b7118d38070d67810cbae3c18e82e7b7da6f472 206 a18422fb54e5bc1a14964fcf02ea976ad907a99c9de543aaeaf998f049f92d4b b51182e7531087236c2fcd70ba1c99604a790e6543540582a638652844f962a1 6f8b2edda8e71567c9961ca07b818c8dae7288aec02c148da0bcf1fc96bdf146 7839c78aaf168c645786b1e646261a865ad44b0a83024f719271bd08e9db5cc6 664a1b716e8f6d679237984ca759520ab88db8dad87bf34d581c61137f93044e 3c3120134830ece01517323ab484002429a65eeb0bb17481bffa97774d5aee7f 933409cfefb692004ee42f1c281c0451050964c115bcd81f79dea07885f44948 ad6921f89907b538f3f08e34c1d253cb0d5b8ff668f335a9cc6e718e9964d170 1c2aa68c5a31e9fe7bbb8623570e67bab36522659cd88321c3f1f1dc468cd8a5 66078deab22c8b165e30dbcdf5b9ffd5734f7df003ca3fd515e535107e0deb0c 4330428b7fd83c847e0949054d66632dbb03bf17f9255126175d6d551b8acfae c6a01a0bf78fa2b335f0d5eba32fb0fed7a5453ecc58042f97a57abb4ab93768 36637941f031cf30f9abad0afa6ac9df869c4dc7239035f00b2536c83eb07f6e e582018b9874cb88b09f9e30ec9b471cd154ffe4b79685342f4959c653c7e30e 9d988d6cbacba45ce8492043930ada21ee880e0f9f7c2bcfe34a978787b7317b be59db2de9e9c18d852a319b8021db050c8684e9b4fd27cdcc70d7972f262235 701f1b7057adb0d1a917d09f9703ca267c6523a15a42d4b2903d1c68bc9dbdf3 ccd77941930d74002e7be55e8cf6b51b2efbb534250352f9841cab4b09d6101a 9bb62944367b74b680de5a491449c5f4c89922e21999e4749c63bddb96757b45 d4f1497d5e1602dc78b67cc2410369ca0b65ca3f43269d48a505f35794abb7a8 545c4f9c95f6387f51da3938bf05d6e1dfe24ce7f47d00673047c6d15b775b85 9e16b849e343e81d0a8e4856e5b61f24e5839b3aae6e3bed70218a831b7ae4b9 6b85c3a6f57abef1ee42b1929198b80bcfd590d2927465bf86a708a8d8935016 45702a3b1ea9f88fa318ca774673c9f5305b70f81eabb243ebc51a4d456a39ff 14e17ab717131d76747f7f89529456ed4b7e0b4bfdbefa66b479a1cdfc2cf1c8 b28bbaebe6e5a2082179a5d40f82b858120207ca642c4bd208f86d1981e968ac fde5f21d0002ee5fb68b31f336de025dca70152f8fefc8c96d9369c8657bb45f 6b6250e0a9e114274c470d03b3a09adb267eddd060fd7180a3d59f2b4c84a255 3f912637b519de978a550c9ce554c4cd8aea77b7c67bbb4b61b7d35dfcd6cfe4 2030f7603dea048af9075f8d5f94503afc5e9de977d18606915200f186c7cc91 b243f8f735967e6d2fef727ce4ddbb07260033eb06e22b2c2a6e7c573ea8b957 a4d61eeda78d420b95dff8eafe85b94c4876872f1da29b344d213ceefc4913d0 c6f13c6b465dfac7794fdc9b025461ee81a20a5e12aa1bec8169cfe89c66ef29 a4653ef4b4897b15a9842bff5c3dc1511ee1fd1a4709eb9bc11d479628b5a49b c5b6f5f523e94d64365318fca8eec9f9b69d7517b9d0516273478024c07a552e f9e16f2e35b355a188119e38acf7bc16afc4e0e239fdaa97dc487b9789ad2093 8e32ed09461857c41eee4eb6f4697107373ac5bfd404b2aac521aba53c1e4606 da10a8ca7ec34b14f75c82fda8a295339db1a95dc0ce4d51c92dade911a7118e 00ba18bc2201748eb0ac8a35142ef36a1f054f45cf328ce6d4c3a7399434b51c 77e93d9c6e4d0fa69e03244504cf715ccb6e1771a68c5a4bd01e7a7ce1e89ba3 cdfdff3ba97751f3181e617d7303bf4d8dcd5163bcc40af5390ee071a729370f 36e2b4cfaa03c516f3833c40d9d8ed3e5f123f81fa878aa7f56ee21c687a1c2d b978726e14c14fd17ed98d7124feeae05714a8109e1dbc14f503a7f04178ac0c 29dcd31fd684e980ae3fc46e5da92f7f5e78babdb806087ca36ad8703734af60 c1c96cbb9e90a08dafab66240ec2a427c9427dd6cbe2c649aadddfe2f2cac4d5 ddf16af1d9f17774b2d18a01f21633c88e676729fb92ad1ba2f5a6e4515faf3e 7f3bfe385a2aa865c30d151d1d70f7780b9404c3ce0b80cd8d76ae0bfa2d48c2 33cd6755c2a765f34711898c5cdcaa2ab13ccb4035809cc714b8ff1b49e312db 6a9c43c587cbc9040a1bbbb5ee5b31b5fb15c8e8bcad62df9ee4234c0137b330 ed1e2acb487769fcdabedc3a39a4a33f00689ba64d2853c247f1a82ab5d9c900 a7d0aed414d50b92555e68b1627c84600850b2f67023eb90124900fb54dda7e1 9c6072d044134b540033a836fd2cc23887a8a82559355d7e05934c46257ddfca ff51af4f58762907aa34f99131a60a7d303391b059d05fae1d4d4e52e9a7ee12 ef073664eff8f23d2db789333d15a35901822b75146bc40e6a9a60bc348b0f43 b20fc9ed263e55e0d3b17ee113d43547e373cdd165339861d1a5f00824954bf9 da9614943a2b671472b15a1da5689b2a22503e468857e9fcd5a80c8fa0616927 b1d8c70e8927f0a0949c7ccabe0f8420c7ed3db8d0fdef72cfae6c34ce3c236b d2229f0f33a9fa68062a177416c13ec529f36dc95429c6b3e2030e7998137076 1cd7d9d9e748868bc060399946dc24d156a1b392d6894bc1ecdc54874eb26d64 870f6592e775e7787cc3141ec39a4bf8fc40216d768b215ed2c37c020663f568 c7ccdd2c40f607a04ea79b14cc3f0e1087c1c1aec9cba9be3e9a040153878cd1 4efb40425203e4db182ce267e1ce8f9fddcc7dea9c231875dd2b477f6160f93c c77b8ff4f3990a4a716d9ce40d9dea47d675d4e2e8032eb4e158e71e22b593fa fafea62add24d9bfe2c5678c3dc32c3460686350cac842c68ee8c9dce4befd5a 0d9d2879be4d5c54549a652a0332937b19a171d08b87355a057c155a49dd3511 7d37429fbd0fe2b2188a93c537c7e55283ef4fccf2ef28dbf393039edae68d1c 9647f768f127751f8d2e8df5bdfcfe0b50ab08d002b859a36be77319d2f626b2 25a2befe5ecd5c5afcbee35e708c513e9980d217b9982a894decf8e723f4267c 49fb16a530ab7e69538cd98e63e69dc602ecf3422f854d0bd9dc0c56a6fe435e 9a4cc03169cccbe20d82c8d1c88cdad2c0e16bb32c22fed4a5325ba24c5da531 92ad7fd060ff5e009df4146670aa6f3c48add5eb9201ae12c3545334efc68d8c 5508d45bb35097749c0bddd3041a15f3f30a73f8d8e6bc26edca97d2b3ed8eb3 085d6e6167b1711a5f0246198a0ce033445c56c308caf2ffb67ed4bf492a760e 814453420ac7935b45302b60a76d60ceb3d875e27d84181cd995ab0cf7c194a4 66a3b5485bdc1fe37020e728315e4952d501260adcfa287640a15e4ff29934bc 7d3202c71e8352547fef9f2effa942969472ba6b2b711685f1e7ddf767bde373 c603687740f6353258d1563ed82e5e72b7ef7738425b829089e044312668092f 78a2b79ae83a3be22e894c61f9dd9944816749201ebede34760f0d97bea2d29d 77915a974635dc324949da90d5a6414a9abfff9c6a1fd2de2397ad515ed1ec4d 394e0b6cb9bca29f0889e679730af807cc382f846291fecc23ef891bf4303352 03bf7c7d9e5f15da8d8f4d8c8242eb7d0f8173f66c9a709273e1803a073e7c0f 885a745d8efe981be6a9aa5eba6b24b23032229968d62ba2f680a437051957e4 bbbbcf28ff988f39a47cb062cebac6a1ca52cca0b7553f1e64503c9508ee27e6 e4253508f5c9313bb1519bae17f58c685d49e4194ea7b369ad0e079878d253be 33e5fd5640355e45d35e4dd7f1e8e7cfa2998a9ef48c9c887fc31046881663f7 8aab3224dd2af87ddcb5831c1282df4fd90b353f4269d4da50252aba203d19d4 be6533d22fa31ffe42c80e1a1eb71d65bb27a8e63c7f382d1a00c254dcd986f8 9c0652aa08322b9db87e6b8bea6868994d9ac10fb89a5c1fd79e6cf05e4f451d 99c615eab0941b915fcdd01beba0f79d841455986f64b211a74631c04adc45f0 eb7436280e32dd1c1fd11ec57d44e4b960630debe8fd3fe9b16f7e25455153b3 dab838aa3a6646ae8523bc8e612c5f76bb5cd21bae09af8ae035434a9545e1bc 6b0f31df949cbd288c5d24063851ff1c107938cba425be209728e09dbaad55bf 83eb14b50510e48cb3f336fc4aa0003c1fd2dff27e26484c9e3cdaae21e0f73a 37be5905a9f43cd4e56d974dcfd016461c3d066d3fb227d6411e942705892208 53963e49426946c393d7051247a746783821d70b04889a094a5c99f0107a48fa 3797c08b0780ab73b9f4897a12174a42871968f024f767ad713f8a7c35995b5e 35f08b38741b662a557627dbb793e4ddab03a144afe29f15d2582f528e3f54a9 34087c19447afa9ebfba8b6312969741fa38dce9a3c52294c08d949180cc79e4 7352d684d1137c23c8d55ff9c01d6dac19ab3d6b8f8a30ebf5f9789920e5c3b1 d20ac3c612b2b275c0c708040418562f80ec07fa66d5b8bb166d115e059421d1 b34a94287b0b92f980fdca1985f12feffbc33e22194cf83f7c64f9db3959d810 b00a64ca8cd8d6dd32d2e23e22fcbbda2c979e3a25e095d9bc984b75e29263ff 88905bf24369828cb2d7789ede9efa19100e1de11fb23fff84c5f919caf1b200 f550c0946b5bd38adf6cb83ee5f66a33f6df9a352920a0608cd6973294b88591 b84f4771e6e6bbfde0ba6380d7b0adfd745782a79c665ef158b35f30c3f2aeb1 6125547278feb8959d7b44022834dcefacaee1821a2b9ad98072409fdd1fc5a4 2bca5c5a2f52ddf22fb94c1ed7f094beedec8423c2d908530d56bdc6dd8b33f7 d122a98f60cbfc9afb08c4a3665a4297bea2e28e1aa14248e534792ac9be9754 0cda988e49b35ca39b860348c499ace34a1cedef1fe980bd791e85cc9a3eeaae 50405db106877d4f79bba49df0d00186a34ba02918f38d10a6772a08516f358c db86f45ea5f440499c1fcbddb9e094ec839219d90ff65ec5db7f00e401f294aa f2b186741767357374da23c9c787b00f7715f6ddd2bc13f4ce0c7fd8af871af9 45dcbdacb01ebee6cdb99b6bf299a327d7600f911eb178b6b040e427045893a5 7c665eb4e236b8fc0df26b4e7006ea202158ffcde5bcb01bdc76955332dc4612 e5c13f2bf51221881634a0b3be87f9bd3b77ca6fc5190ba73496de2025d9e9d3 0f5d6fed8deb031948aa27118f24575d129130cf4d54e8c03bdd5b403077b9bc f5f83a10c8257b8571056cf96b205d6e29ae49a045165f9e81661ea60d210b46 04f5cb095be125daa3ad6f020c25bc4eb60fc210c3c55ea6975579c57704e006 310a935599ac84e860c339e951c795d560a8ec6d0a9915e36f54bf7476bcf7a5 02e7263c5f19528ab4807e5cb0de10f754189d00768dfbd2ef200aa1ae8bb2f3 9e7512f100cd122c07927b9797b694e4829ecea344c5aab64c31e2dd4fcd1f4c d5882dbd1118a46af3c624b569edb0f66d6c7eebbbc822a09e74210605d05858 02fa26513c2e35299ec5634188849a3c7876f283d8e6d95875184dcfe90f928c 7c1af6f4bfe0fd44c10c6e17382585eb1f6f6e1c0c0774fe66517839b4e74a69 ee9ccd0b683adee6b6c99eb80bfc58d50ee650f2089b23aad6a53d1514204d3c 7a61e0013420f41c550184125d3810897caee0c1cdb035921461d30c070f5c44 5a3b4acb99a292959c09c3bc164aa35b8cfcd80e8dd1e5a0ac18b4b20c1f7beb bfccdcc76e71a16d7cb5cd68daf3c83ea71ff3ded30cefc9a574d75cd7ed3cd4 0d37450354f70f3421920262eedbb2388b4b3025fa7b7872045e108cfe506a69 7769a6ff5204841123e2f786b9c438157a68763baa0ef3b911b1918dfc908fdb 0281057269fd5c1792dafeb5dc50f5fffc205ef14b1a1b495335d41fe9833641 eb74bddbde03604204f8d59400fe0977d2fadbc4591d25092dd8507ff1ca0441 4984c0958c38d0bf7db3e714963b7ae2be3f14e4247aaf8625a1206bfcff624e 416cc5e0618e97a86a868b745b730cf5082736b28f158a15fe97d46dd18943d7 c0334b9b1bf615d4e50d93c5cd68c004f616c96448640f96abf15a05f259b6f0 72228197fbf42e0bc7fb85daef82dbe1f5e2dd7b173e80b590bc6cf985d5a065 74d54c40ba88a6e7e407696c81045f77b7ca443b5f676f9e7ed2133004c220d1 da36cc9ea4368c90ccbb962bb33c447d7f1c57c6b5f0073f1bc1f453a58a51a3 29346ae924c2c641fddc37b5db1a1cd7d8e8dc6c10f397ac1e4296fc50f957e4 8ad915dcc38e22bbe3808ddd979703b55641e5e082d922ddf95eb55bc1dd1c99 f9602f4dc07dbcae954cb895a266f64f0ae8067aa0de93c828b0b63ef77b1286 127806dd2c84355c8f15d5542dbca5d2f545bf7aeb97da34d09ee353c18df7b4 4808e46928e4e630867cb664a2a594e847858220ca93bfb33e06ebb991816f60 f45ea82edbdbb2b01bf809edd7ed8c7b5d5bcd610eea3e3e65fd3d2d0813e738 b9cfcc23cc02517f75dd3ba4f05fc727681786f67e6cf81edb3bd39e945bce60 e1f5bdd3eb6c2b9315ebc2fc8a2265b6103a68a966992ffc100e00a6bfff91a9 b0fe946f3fd0a6f8005f857f84d96a929971c37caa1b7ec7f8f76781c10f7659 a0f943eaa5354a75121d68f221d7f4fd05820d142c52ba0e613ce40a94ad410a fd4885348d62e21aa70db54056909d38d6a0434958a88bba1b00bde9b5d9a68a 81f0f73e215de76d5d9395ece7e348290fc38c254660d07e5bc1ee3af399c349 6299a39b5110643b1ca175cb95efa2384e6ef5b097e104d538f791b61c934039 7107d2209213b68af9ddf368bc4cb14a7eb4a97c2d12cb016fc4829df353b976 26e8f097cb2958051fab454439aa1dedb6929a218d6ebc0ad71ed240288d5848 03c22c8c6e0d2b2566d02486f8d281d47757cfb55aaddf6e9ce709d0b434bdd3 8f0851c2b5c3ca4878ad6c9b7bc5d0961d2c10181b346bd04024a1c3c01ca783 49ea1b8ca3b5ff382ae05817952efe5b1e492dfad8864520909211db36d9a3a7 4b4077f01a37c8c41d0a841b197f90bd81b0f60e8e85ca241ebcdc75ae7ca7b0 c5094f0a05bfe6bf19b8aa4294ad8ad350c9da12fb2349e19f20fa468a8d767f 28620428aa5d407387a24846a62cfe882bc32222b1e0a11e0878e69142f927c1 469a5d58cda2b621561089a5a84923527f3a7c34d73429b32f12165453fe919c deb95037d7f978e00a400165cdcd21698a9abc5543a86a39c2a3a2a38df0147a ac3a1f62cb0e149eab665764aaed0d534c57bdcdd61badfef5e8fbbff284da4f bb95b6728b9d49af7954d46440c0a38d95f97dc319a6bc801fbbab115a9d227d 51e67336b6a0277b50c6b8c9461b570341de5c545d6ec069133fa52fe0fbe96b bf9ecee1e808820f1733fef3d8b26831fadce90ef25c2c9e69f4ae1583c2ff8d c2ccafa1afd4a0fca65c04cf04372a9949fc82e1cd44d3fb62372c4eef025d49 e6df379d0ae015a6e363eaddabc993ac719fbcbf0c426923cef3ce181bdf91b5 715a8e056a708fcc73e370b8c8189b95be07c48f029aad9d78318bb4211fafc9 33086563a891bd5405a0c4e6c823aa6caa40baae56af2987d0fd5926b7bcf92d f135dfaf3da2b5b44d02fa04f5808eb9d2f98908d5be6084694a0d0f0bef6b6e 86646a742b57067e655ae93ebb181f34b2bf68226725b7fd49b7891c5e8d3c0e 76f0c9c066cdaea4caad46ca803c9aefc3a11dc75aed472479652f6d75f9f593 4d1772db532f9da6aa39d0644867e5ef9c429cd7986f97eea7c253a0e43e8698 57bba026eab4e72dc1a9636f0c63988275e9eb38f66c56b0f6d998fc281f2f0c 628eb7273a6845f9636813f810760b69248603686f21ab695f41304a5259d7ca a83d219f94eaddaf2bbc68d387db7d477f41a6fffd90b307c45342703c987a8e 149807be6e62c586c635336a9cc8e1e12491ff04528da3ccbf4c62dd49014d72 5fced27002f066f6e03292c55c36bc9f4e5be911738f25f889f12d0ab576d690 619142a6d9c825b6b874b1619f723382fb91d1cd94d59814adb195b2df8e2afc 46a9f5f54f2c432884e00bdcf31a1f2832aa3fc08ec5d613e39be20a8e82ace7 1da5ab4a03bb087697f2149715033ecc9fa93f3503c0323f2a4763ee8c8bd51f 481a87a30c3af37b339dc69250c71ca8a922e816667443710671a80dbe52ce92 6f72c0e69adfdaf0dec11ab94da9e9607fe1af9b19d2942499f2f3b06c70ec10 3f27220ae74e591ed12f7a5e53d9899dc3016346747d8ec9fb0d8c6bbb7c2a62 7be4d47e1841078f34a8bceb4a63e3dd7d84a7a15d2180fac6c8cf9409ce02d9 4d781a55176f8de667ae70e9c1bc7cd4fd263930b82f4a4f32ee12957d19b4e1 b5df86e2b91c3dc2f216c0c9ab065ffa1ede0d6ba02fc98df834c77d65b2ee60 448250ef38aa56bbd00f29317b2a48212acd247ea64550dc411ad9b176f129de 90899eabd9f089088d271399c46d2550c63e6b3eee2ca47eb1a1b01220abdbba c3e9bce3229262c0e60a477d16d898464d78d94b5ca4ecb64f769ddf20c5c440 67ca124eedbfd419a80a46588fcda09e2f5fe336b8e69d989c210e613a82e724 f36f0c8aa0d5711221f96dc637e34e8d4cb79166136b77637401449596507b8e 5a7b4baba7c4e9e7b0bf849d2f3734a5b822f30f132d3dd1f262ed9551a2bc58 a528422be9a71e011bb84759bae4421c53e306cd112a78ea9f79f1a2b19b6c17 8a80facacc40636b9445e719bef3a4f732e26fecf9fe9754919ac27b9c53fdee f9cd67c569544c62a0c73392289fb7682d727ffd10e7a79697a4433e06470f8c de9b69ec46282341a42fbbf8de6b54a232ef50cae2ad1d774f3630e60c8eea92 929033db79ef727eb67d6dc4aeea650c01271b3d2a6873afe6e6b0bce5650cba 301ce60b3754afae20c3deb7ca76dc542dcf5e3c37650258e11f666bd9b978e1 b07597015e477302bd1f044c547f7246b74de2e4b069de91314e7023383e891c f6be00b7a0bff65e3d29bee8a690717c412bedad30eeb1b9623dbb7744fb72f6 afefd6e6e0544512a57fd39de6adedbf6d980abc65de3ec99f89ed06315ee378 43dd0a609beb44b6df8459e8dcd769d48e892897872efcdf1783a5a913f5dadb f765aea469de90af8389652bc1cd0aa52fc42e57bb8e546e1618d818128264cb 51e47a387db4adeab17ef6abacc440cea6fc47e00dd448619843ee6aca670536 b2027fd7a8e36de5a752b54332d2449ead5213aebf6c678c54c91e35e6d87fd9 fbc0102c91c3c12182445681f5f123ceebc65bed2d9932afc8a2f6fa78a04b0f 48 c752903af2e755bc1e8858338e2b765c44f9404e4a18fd0f91b3596c9271f406bb75e09fdfd2f4904455c4ea70b054b751fff7136a773d93839d4f02edfbce0e56cadeb1b6f6eb529d8242cfc8994a58337c28d01228a29d31099263f05e8c09b807a97e73e015d71e133b986f5df9282833690c6536db7e7379a747f0b48b0a84e235c633b3331840483b7a8702a6fc3c0fedc43d4ff22e0da1d018d389700ef635d82e8b5f530984a6af95acd2d0d0808d698fe1538a9355b141a9da3b760c8535800cfb9711700786653cec731f1774a04c4a950a6a109db39e2d6510930210e6345da5412f62cf67a775b3d62822e78c32c9cac5cab36f3f30d3edc1d30f00324828b0ef0be92bdaadc599097bfcefc43e4e4c3624ddd7fda7b52a22d70241e3153bf2b6acfa043eb9eaeb98054bf9fee0c0f634c18a3ba9eb34f1279a0bf6bda8447a37ffb5964d43d7dc3746e680f9971147fc988db3a90e80f0a5c10d1ab067fba00d24c912db2a7d9efde5b169130f124c609b2c2994334d9ae6130632dc2b46a184502a09c2968d07ef8dfd6097ef014091ff2ebddea5e3c09efd087a2ddc5cebe7f41f8f54bbd5d26b651291aa5f53a3a825bb8ae21e9a6f78c501fdfc728e3591fabfe66f1f3a00295abec0179d9cdd9e6d875621ee870c14dc041ea89f7056e2344698cf7dc4617f694284d8b6a2d206bf96febbb49438df6c0dac8e19f37e64d0e792713f28e771dc631a01ad06289af2de182282f6a1e89305beba4711b86a82a4ec54ea0fc3226f07c2966cf908c8351ad685228913863900cd07e8f7ca27e1e5c2117bda6be169dc92a392031af88c52811d169ae2137f0d1639c41b4739d4691f8f57d467945b0b1dc7d82459fa678f75debb643b2be20fe216298b5d526c1f74b503514ddb32b4c243c10813339db16eb91c578ed1670db4a9d2e7fa13450b67e317ced5ed3eb4edb3b848985dccd452ab6be85d29c70bdcb1d40b3646753c393d0a93c2dcbe9b05772f3a2b2701c30828c541ebe0a50b54df2180afa9bd2ba0d8925c8dd9f4fc6cf66bc6698cab1d5a522d57f90afc0e250ce977176561b3e909d7a2a2383edc06df64490e39b503d5f6ac44a3778104ba7674715304b05df024969725c024c55f01ef131fb9a0048af416d68cf8130dec17a7afb80bc03912e4b31d2eadc915de1bba343c007b5b5373cc32267ce806dbe00adf2c2b714332b3eb9e9102f20f2aad2f232a9b46d251fac0cedd64bc0764fc6a982a589d68e0a8f787e41335ae22e6a1a5930870ce413ca7cf86d13f05c90b6d8fdbcc74c35a34c24c73b936f918d5ed83b4b7f61794b033a5f5e81a0fe189252508a23582fbb86b071d0de463923eb699fef4e9d7140b4a68787f0f054a4d81f7498be4010ea8514782d16b8a486d0ed80a7948bda0e0273bc14b2403496513885c5417bf007a5f93fbdfba9fa3ecd5a3a871a77670a5ca794d7c950a50d316b3350a4f1f0ca2ffb150fb729c88905dcdc89fdd6a5d13d0f84a8e7e0b94d15f66d5270f4dba5b7e809bbfdb8cdf50e0d61311a1d6e5b5c94f5901a60e9ff691632f0aebd28c989ec35f7e2f4e234f7cb9f2961b6211c5563ea95ed5063e8ef823f742950b2b93107ed3fdca7e259225c1a5d6d8915f2af9da37feae0df0f4a7f3efd4cd358529cb0aca99ae40b8220cd0b14491914a5ec266556bce0d571605481662abed71571f011d6e7c9f31f94bb2a2d48126d8478e0d49ae2004e5559a482a01ae8c1ad0c736969891b07d17a878535c1bdb572bd08b23acd20a88128123843c4db57b52f18c880f3e3983f93df6e1b60548cd65246cffb118095bba492204d33367da8cdea1439d5f5b70376d689f42b1ad7aae4ae71e658a09d2db14fb0473d848dda3980149322d3d0581ef979b723ac8cbeee5ef6e6ef30c99ba14abff111fd533e31dac4efeb7e95a52883e9b2e17dab4765ae77b6eef01e1ff2abbc2bec91e033c5957b53255182492a6e95e4c3705c0c95b4e37eb3b0aa88e306e3e2e25dae952b8302f0ad9a635655884e2edf73735864b0814299f0743a6db7d478219fbf168fe10905c6f868300b7d976c04cde92da5f82318f440bc3fc2328e9c4eb7f4c27994cd45916939c0b4eaf610f1670fad5ed0fde56520f8ba2093199fa7050e6dc397c68a1628ad0526ea89313c4c83a52fc6165934302d82568e965ba51758994554e5ae32169189aa0295d3452487ed949b2eed4c10bf63222b64e8a7662643c115a0ed6abad31df5b8f59ce6d1da8ecf4a7cadc1e0b1a799e0f7c3290e2338c17674a51e150f609de86a99ef91b1006297b4c37f80ca43711dcf7b6180204e14ce0b147753d55047729df62bec151d2cdd24dfc540f14bf6e1bde71d4eeb25ba6d6b652a8f3f9618787cc665be918f1759601720c0ef386e71627c954605269024bcc08dc713edc59ecc568edb6af317cbd9448c30b5329b0c0d950b0e1a59a994aeae062f6385d638ef3af45b9ed1b121cba975105a042b692c82b722cf674ebe787980a4776b150337ece086d26d13a74b04b4205b2003c5819029659d532ab6328924429cfd9432965e9caff62d5daadd2d5ad00b95f6f1284a1a276dfb75791d92cd4dfb75a497a84979a18380d17ac4a4e290691032287351059ebc30a157a7828bc987a271aa2a094be0d6d27e3b5248e9a035e1365092ffdecc6c35b5d6180c32b3f9ee5f98a3b87e0744d598534b5c4980d2320c9b07c0294d8fa323585f7d61f1eef8fe92f027af2687277949222566606305c8ddbcf05bca04a157a9513642789db3dd674f56225569870ee3cbe18e707a4f9a5507aa45f53af37f707bd03bf1ecd90eaf80d59d7d6d7452db69578e20f0caf1c15368b7796c18b5d911a317563b4ba02e0b877e1a94e403301b27c8b01570e149da07f3b5e5e1b3b8af933cfab70d971b15dd5ea2fd804ef0b0762280011765913e60ee2972b05e81fb1a17ecf00ac29c4842ba264ef3703395eb62909f32b5d852a39234cb8a77ef60db93cf564f9a9dd2aa2c736b63f79e8e5508609febb550a2dc63e6901f599a02a43a5256ba9b1e57e56117fbbf3d67a27bc5c0de2e83a44fe167cbffb1903c3ac24649221375a7be3730959c96d66928f907509c00e242ef536f72adb1244230cd0c9111f0464e98a1d8db78005ec45aff6df01ce2e4ed27770c7b1393c05dad6a7196d2adcf18e7f89d819a4e077973fd3030306faddec05a6b0aeae8a196d5407de08c7a65af9b6646b5930168646465516000a81c7cd41620892da603cfb9c5b69a0a5519c0e88d33da4db2fe4f7c8e19d00be28025e2be62a58997d7071cbdd29169b0040f1714ead974dfbb4944abcd40b03ca39e49153973d4b5db8c9086523897bda887fa239c6f0276aaee5a594280215cb987d1a9fe3531256be856c094393222f2ae31239e7c6a57fd751f4fe22045849b5eada89f216fbbd0e70126029a265bf0b1331ff5c9739f5f89d31a07a08d9eb922701ffd0a098ef09ccc4ef2c8672d29432c21bdd9bd1b418ebbcf55509d65d16ea319219082ec9d7313f71c78502da080e78465ad72cf983a8da8cc10b2aadc39e74d5db35b65c5fcf169fc2ec82799f5dafdeeb9edbb234095b3a5b0cacc31fc48e0add8b664bf64a462e21d68e1a7121036af4160c09034676bbe50b1067847250f781adb2cc354ed0e1b16d1a8eb0609f3a5d824d547cc5ba0891008e57232028a283ac84d511918728b20f8b06e0f7f000526ebc2bc92878ede8002f0a3b2c179609a23548130bd942991b2fa4b63ecd135ac04cdb685ca41d50084167a81b3bc3a772eb1e8f55deea9cc055e7acfedaaf7dd5fd31ff7a0fd9bd0425029fe6942d785963d0b7b37045de8598cd19d12be5133162a004f2afcb5e050ffe67939f08ce4bbc9249eff06d402cc814c36ca12a0e7dc8ef95af6b5f4b0ce3ba69f5abea7cc5e74dccbf9cf7bc431ec562dc8d0fb6c36bf6d64fba64c004d0889af8395674f043c732256d1993ab6f861aac75330ef56921449260ab95097a92c1b75cee135ea8d0e63d1a0d5b5121f082f8fc4ffa2308521a584ccb2309541cb395ae243bb1fcd8be306d1e2ec6042ebad97b12126098e52be84fca7509f003888a248256796f038f30c59dfb2133e9f42f6febf4af86dd0e815e73cd067548ce93303cb226edfe90d637d13f06d73f70f5d2f407fd60a45a6e01bf32085a83e9b88fbae058442a96c6843bad1529156b5144cf36f40fba36d5c5e8cb0af59028e1d629b55166c7bfa0fbb65354970ca7eb18ef7053d0e6629a7a75d30a5eb83db2a8852456e8932ca68285f0fbb3297cea2964890d7326cf41dc44500daac69b7d4935c15e3da87eec66585181fb697dad9a510aa07fe9c01ab68d350aa3f7ff59d3188f25f92c0376f26d0d322e7d65038b96b9436aa1b95cc4677503be84e6cd5f01359e5eb862c11ebd8b45cc18872518326b1f9141515b8747f60665015cfca5c04715d9168663a2b190cfcf198b9f43fe0e39b48d9e458502d80b9209bbd3a50c3cbf58d9620b725a249a9c849d0cb8388dcc7f2a90502e5efa0dac0f9eaea95b72a31afef787d38b3d1b12753cd5427a3549f97545918acbc10a81ab5eb0bb841eab9100a7c81028a32c8d3826b836136942234e829ed2212a059e9ebd5f7b37417b4fcbe5c29ad97256d4c8839b18f930e88492ca2c24b59b070460826fcd5185c05edee9740e6c10733a047276de769b55d352d080a7227d05ffdef14076d65e5f51ca1aeca5611f7628714a2be8e482cf9533ef9040fd0e03ce97908629bb2ff05b40da50d3e52729793df7b1af7b80ade2da610e1cd3af0c171b287af79e054a9586ee6dd208c1236c301c7fba9edff173f423187c49f500b0c405ec84e1d116cebe34083e7875a80a3cc273111239dd088a137a3e4166058ef6682eba4ddd344d3a2ef203fa59213ee8931e73faa6d87ce98d19737c76009445ae9f4d31bb8a708127ac42daff428b8479416f7770e26297890b292a9d072e087239be25a2377479481aaf3554cf74850e77afebe01f64edfbe5b9147101aa8afadbf7a7bd80d25b2dde042a67dd45b2b39e99897473e858a41e5ada7b08431fccf2e2545bff71f76b763bbc44014718498a0bb4af8c55d2f63233192e05bae827de253f07ef551c81a3e75db3986e281619696da63b623e29edf998170f069f1c4702977d87d5914e6491d5876af99b0b70c778b53123fbcf92bf10030df768f6b40eaa62f06ac2450e8cfe45d57cacffba3f938c3152c3980fd178650d80532104b9e0f031d0e9fa15d679df8264762bacfd18c6839e77b8349b462f09ccfc92c01f5520694ff846cee53e587870710c32d99e25b2b11fcdcfa096520b1a244add0e6ecf2d69ac0c9f661efcc88b72ff8148e5c4669f8b9c7e6321580bdf1c857c0af522d1c14b86274fdccac009a83ccb0171e449af8451505f80d000eeac91ab84eb89f5827f0081a3ad92f5eeac3176e65d29ab4bc2e061a5305a0422441a8a146fa6da58f08557df4fbcc7d227d5bde237ee5d4fba7baf132cac0e9f5d3db2ec2f5e42776b8d455ec93046ffe391fec70f3c327f09eaefda8799026dbccd6c442d31efb4e47488acdcf8b7cd15c35de959f181a083c0c02b759e07667805be536e47ed617a228dc9a57cc50c1b732ffd44b0d912845ff3835a0608ae492f7981a6ea57c20c310813f675056fde9bb1b96dde014acef3fdd137e20a961766d4410711df70bd5ac52f24138425d77358a3b43aaa5edcad2518dffd0c6bf1394b8432be78275acb5593582872617baffc7aea38dbbba3c9beb4dad7061b2cbc664ac0ec7dadb6b1ba3e234672c8b5ff79f15e2804036a88f2545b70098946bf46e0bbc30c7bf95fc42d4fbc6b0acbf3139086cfb21dbaf85875d78502feed91b22662f5c8740e393e8bbe0bf3d3bbd44bfcb1fa0eb6de4e2780c33706346cded5a4368d68824ca575f98ef74369846de062c0fb855fa1758685758203f3e3604e55eab54d33d3e238d54238a7a3871e6309f0278b62ace6b8b9990408c977b6eeea0310dfd37bd1f3ef165340ec8bc0c962682ec53b66b615f72b16048537d80633ed368d483199d79100c10e1185a79c30afdb47e1d202a25655d004ada330ace94a5640419af934888f83e6a60bc88249d44a791589e41d9cf1170d2066d8dddb16be86c17b64de3276bfed708bd8824ec15408eb4fdb87d5f8a107305ff5d4c21ca34c26dee48f22d06998b03829d6fcea08a1fcdf0750bb2a510fdad8ff1429b48cc4f233422badf7eed7db6f04f32d711e77a4a1a21ff122430dffbe9577805a689770fad814643d398aa5c8f82799a1b45e2d001b8ffd0981004ed12065d46d0c4613844cbff8bee75c592e89f1b3da4383f8ba00ae6d28f60f423488b01fd96996f66590941d325d4fa202b04c3f073f74c441533d16be1606afc3ee9824dada8d7f5b0d27b66d2f300d0396a1834f8f4d5f0f56ce61c4c50a6b12f9a2a4930f9cab4dc40d1976c727f8d785963711a5a7545149fbb7ce5b050ed4f6792acaa88f182a5f6dfbd3fb2c0d565e7ffb3827bf31a03d1ef7f6f900c7a83cb2be7d1c3c3d3bd3d5a4b68fc2d8727796572758fb1c53e082e3b60b0e8e829bc6916e2e31ef802e1228e5b095f9cde3d9cecede8d5c1e846a40e0650811f0d6b1668b97b9fa401a70efd3313e8a86cc47f0a4a6f090d99ed659b6800d7e657486f4aed9e00d05e643df3545855361b5c556f8cc13477f2925c1c8dc0af4c73a0432f416f04c0907b89f5fac84c1ec8cba4f3dfc58617dbcafb512d601d0edcc5898e1124364fd41b1d513dbf5131ea81b7d27fb89e2a69b4b8ba35f0789d53d4a69a43885473a7d194ad09489592b194df1c93dc8419ca0313fbce20ba9d129288ece220a546a28e524a9c19331bee27c73e98873786044d9aa60460bd04e1adc411385b7a0894ef821068143d8cad40ec0cd901b514aee6d9b2fc401b83675f28c36a4b181958e46254059e0df2838f2a2975a18dce87a34ecdd100db72c4dcc5c9e09f7b57b707b91886ebfefb8c938928f8927268d50d61270720b476b046ba1023cad1a2e3b6643e1204a229d82077b5db9299722aed9496a970ca3bbcc18da52f0f56008df59948ffe24f9cb95a132e60a1ed30c4c952134a90fa130f15be236c08f7c612590ebe317bd911d71ab56e27f573eb22b66f3e3500431f87fde1d4b85a9e63dbbae60b8c4979608f669d367f382e523b8dcd1787b0235f9eaed9a352c3f1a95b8423696eea1115865794f0e68c869072c07518c9301187d9143dd7c9abe19ce1ce324fb321ce260e1365603f0a36ab0138d9c40b7035869608a42c31e3fbf9a53a17f3ba787a63dfe964b25fe1a7c242af20d65440d044da61d7b167467676cbc69174fb78c9ef1cb0fe813771ace6ef039fbf7b40cce1cef74723c9db28219cb0f609860e8fd6c0bc4dba65b9208969f6c7e665e0450ed8ea343568a74d063bf7d8d02efc81a840a65595436952ef934a65443fb0a273454e67654237a99c75db85819442ebcb0e01ccf6bb4e7135882c796aff80b1b137e7f2e4e1e17c73fd95e40d8ba75484b5c9293f70e44b0f22c531af1f7036445cda03cf6e253a0179d4392669ff380ca9a632349e04613f5cc1f171e5008bacd84dc144ba4b3951ac8975498dc129f8c76155da3982e8bc1001a07ace3085c7320402035732865dea7630a66ffc43ee7a8ce372fe00a04c3ac557e86950bfc40e4a28a9aa2c83fc323cdfa3100570fe1c811e9cef28229b8c571e7fdb2033c0b580468a97e52a6450c469299870d80fcb8c22051bf15f225fbc254141608f4c0b673da5402d2619cd1e0114fbabf6997a9f111f7dc56482d398cdab907024fced55a0b847be69eff5d491eafcd34f1e29fec721375da9153dd3267ea9d0cafc991254c1f0a3923d5fb8d63e1dfd41ea1fa27c0b3c400335758880c55ae0ae4cd55795145706c3ec04a3bc87eb8e46858fc600cc6415539ae986a60adfd024fd93d63e1f3fc5d32e5bccd21630aa700aee62f7b1863b468e33243a7fe2c0be096261e629dd6a788fbc4b3c2474d444c0e2a3353a3193d8a66c73806abc60f224ae8f031adfbb93fc59d36bff07adc81a53a0bfcaca429584080d8627b4f0cd66dd0556186af14de6c18456c7ed31c4e3cdddec608b3c37cf182bf885e910237f2281ef98c41e0b54120c5cd0a71a6aa89c3a007b02b3165b23ec22d22c5032d0a64fa68b4d7f79d927e10c056a5762ce492817547594a8b12c81ffdcb3408a0ce9459ae1f59f6ceeaf05d5907accffcb926b8bd89fa082bb2a0636ab6f20dfc7b22627300cd52fab7f48bf5ec2ec66a97ddce816bacccc21cca27fb26fe03bd234330960b687fefe0fec7d80cd0f3a2ed84c61825ef3f7816768797aa7a021c76b6c42ea236fd4c50d80b4254f472079bf1009cfb106b10b69b79480beb03572ce46761b729efef9374690df2c6d274d74b122bb8aaf2c8653cb1cdc3b4045369edb043d9a4b9f21dd2625de372c74d21395df7753e97f910459e6064d20021216ac20a1dfb58a0e3abb7db16dd001609ba833ff20f63ca6aa0fe221db305cde31f51fe24c0d049045dfe7669216576a9a1fa9e5a266093264c39dcacaa0a5bff93c4fe782c44149171f03b3265f9011bff9888081b8537baa41b436c180f21532ef05670c2ab267367369443630c67a61ebceda6534ad52e06e2a5dd560215db5849fe97ebc71214e896d017ffc327593fa538cfd85a80fef032ee8e7d09009f066ce7993dac042d11fe630da9b36a570bd13c7a9069ea3f8da33fa4a50fc1e5c215d8ef5a08c1c3f7bcb3604a86062f52792f71e17b6df3f95b116bc60fe793674c39e9613e6fd530678decc35f6bd0c534ea90eb024826086e8be69005e26970fe98f04dda93d17ffd17af637a780d44a0d414aeee146b7cc03ff14f06235146d4d9fe85cff4442743bb3bcbde3e313caf7c6fe38f496411e1ec9b2009b28c043af665bb91f4d67c0a23a55ed28cacc344b57c7b93f1f3220592b07e08282790a2f4d4da49d5f393dc6374f3de747d6764d066a401be349994bf99c90a18a739574b1ab0727fd79a09e11e33e5b1c53eaa17c97965dcd5078b9ddb2a0bb380cd9faf8e2e5fbfaa036dab63b132d5618efce5bd8d8738b313dd91086006a809d52e85800e9f098afce1955ddb561ee71e4e966789b8de8ca46921b41a0e80095d88ca7b7957825e7d0a5313f383f9b0168e675f6767dd9cde055576190202db994c851ea2a5e7f5b05dff3828b994a6fda52928ed1e1709506a4762280d983941fee08ccfee9da56404c73cef174a122c468aa7277f31cbcf20170bf70e278d9a40eb8866d7a554bb65465bd77a75d61bd5b038068dbe36283096ba4304a99342bd100c5100c958ab9b986c6a5eb926dfc30dfc7e4afa8ea4d1a5b3850e6e0cb85aaa97374ef0ac6b863ca699040520edb93fd26f3e1e2d5e9153ef4605210a626d5a6d87e27e76de71b1b6795439e020dfe0e1318dffa1f912f6e9490fbcc0a3c3715a06e43a70da3a46511212fc94f32e9224697df4bfd023f334110388d86bbd40fa09a1c0ea819a617a294f6d7234ff959fe6e4b6f34b85d02da709375a67fe558ffe4340d5a44606fd206a287e2812abf1f30aee96d3940d0f9c02dc044eb15670fbad317a2801a38ae9b572c31f5409ef67f20955dd747fd021059403b3417ca0b6fe1d6656848dd23be31e945a4fc864d3064151bcfcd4df5f02e8b7519886fc6d990d08f923e712d3e971f590ea0cda3d4194fab45012c4cf06fb090b6b6bbebd887782d56cdf7e92f6a42bc8defaef6a7879594e1b4b9d1f06deb368ed5576ab75ed61eb9ba4927195c683184c9bf52cd4704ef11c31fd7b073cb1148f263d70d0c20c79b624de1696b13376badc92eaa859e8b59aca7fd6067d6a310064c6369df31ef634bb0832d1655a5cc7d8b4def43d7670148bf05f030248b0c189466b497d6474f421317b520d5bab86af02733210a0293d5dad20089e4fe71dcb644146747adf422d8f279d2508dd1781909ae042799798d3c7db004667384b154f3891ca8a155e41837ad53088b0ff8e9e8e63b7a00a5ec26c910dd7e213093cb5f9d65578a11e909f26d9c21108da3b07fba09ed7b57d69f34a0b5a56ebcc650f624477c4994ac53b9e328394265e72b612184be8bdd57fbb7c092c15c3622ee553b32696b78fd789ae40f50bfdaed583c129b7cd5d098a3f6f01e1a6e1272ad7e47a2f9253a313655b409bd9182992010f5454f20b07014ede016d4bfd954dc5e98462bc560e4233b71a35cda8bb502ef582e09971389375720c3c730af280c9c3d1ca4f960240dd1424b1ff29b82363a6d81d93c4561e69d00cb32b005ae3800f2e90b1495dbb31b609d784928d1ca9a1817ad515d640651e020404da4123c876e291a61012a3d7fcc4b3c257ed1c3c36e368a7316b6fff840ba7526f19dee288d1fabd20a86e426c1b94812f1501dae2e3143e5a723b7a59068f42717f3c53bb509822a200c026caddda75b5f45611ae0427c68f0883063c021a084c394fe33e5fbf73c3eb4d7ede5137fd1a3dceacde6f049ea2826b7b2f09567db7f518aaf8a1190cfe2207175fd6b475bacb7e7e27c851c9e7f9d702b20fc934bb7992540f75d7e5f43d9a48a0ed0f554ccf4e86f5f9cc3f92b8d725e405dd47a0129226965b6c127007718e78366d1c19d3d7fa4e9afdeed5ba822ec90cae5faa72a5f69a6d360970d76ac37fc458cad791278ef874208ed91ff4c2c50c8897b25d9ccfc208245cd5b4e0f428f5e49d8286c2f862ecc2674cbae2a29903152a389262076882bb2a227ed5a3a14a7e09d9a291da498cd7e8295c77b66c022d2319a3354c453ec3ed86413fb267e2b8cd3ea7b9c69a898423544537e20800e48e8f72b7e7ce02aac099db89fd629e0bfd312d1b06eefe3a15648654ffc20c74513478a8ab14e76eae374162da10679c907f482fa4a9bb1714921a457039020a651493e796719ce61bdc19eb2ee5968655dd6698919e169f595f76296d4001d8307bce3b2abe36c4b630eb7e21a4c3c47e3dfa8dbc82d2c10288aa750be10ce9e247e5037c4c21893db408c5daf1026cba8012c8a717f4ba6346bc58dfe102fd2dc28e50207139418c6171f130aca3aaad5069b2de5ee91cae621bdbe1a60dab4c22fa1ba8353c9bdbd2987b021b55a97e45a52f7c391849d8387459dfcc01424fe127902932560127aa7b853a31bcc3148aa9686dd54a6781be7e92744209d337f929a0a5e66083bc5739f56537663a0311163eb1a6be7b81f1681dad45006ce86e013b66909e5be7f487f0d249e70a8e272dd4ff2b25e473ee8cb7596402b4b2cc077fef5bec3c816552f8651ad1f29196b6ba7fe202cb4c43d5055d4209482c2b19d03b179f9a40fbab0f364ade937c47375151805bc2bcead236c8cd0681d6a8285e4e88864975b358c4164c2ef6da9d4441444ee25e2d23da9847820ccd4dcb064d69c5bcbcf23b5823c423f0f8a4f97ce2405705e848dbd67dfd1d0f9d79542127e6cafa454b807db865c810fb6734248ef1d12d7fe79495b5be7602d901b23f20ebb0b997fd22d581c28c92207b56966f84f8560b8a2f2188b7db063c903fc578c2e36f6ea58399153575961df232839b30ec1ec2d9327b5a91a80f255bc0e671d85a3edc916009e94a362ad8f57fb9d9e8039a53ef7e6033ba9407685dee8de695a409352bb98e09958c0a78728732884f4503c891a7ba224c6d0a488425e05265d745f55c30dd2a67ffd410a9ff3b4ecf38f422f8faf9fbc6ab0413d489af4a20a553f0fd9ad69cac2d09f21eb74256a90d4061d6f4d33468ee007491c9201545b58dbf7c6e630790946c1b5fbe127f36982e081052a1e0f54c0c52facab270b821cdae76c49715bddf5a1bc27f9ba3f3370c838f0cd280807f003496f81b9171ac3672158e9cb9cc23a03a260f2482538e7c243063a4bacb3f00f364faf3ff9410c8f88a1ba981a37d17595fdbd2a965907aa91269b58f933d01ec2726f38973aa54247d11781a0f34b4cd893fb278c2a98c37515b8fcb74990dfa86a9ccd268422c4acadd62909f11f49f9dc60ae24f97d511a80e7040aa7502f1eff2615252a09a5310cec8afe79c35074e8d8c425cb509041bb165edaa9a0bb48adbd37cdb77846747a84e3566fa5cf06354a9cf189b20ed289bc3edff2f041e8e0ac5d81f24bfebd536b90432c4ad2e4e275fa9f8167289796208be628204dbbf38ec58cd78baae7943acb57b843ed0af82ddf40ed88edcd569074256d00f4b1103b503d8e33a13754eaf9e133282c737fdf59b0584b6ae7741424a3626029b705618d345d6caa6c85f3905d8e0b786cb40fd5df8c7fac652487fd88bd40053647419e772f38176af89a43482a0f7ea57300b047581b22600e1c7d960bd0b530bfc4bb4fc0c999e6c01b60bb9db93d813fe69472e06e6d4d30229f761650d37b1a08ff1d6b06cc3e30ec9b712b15a85b0d403c2f67f2d185159c87abb0d0577071d668fc7458d0b9c87b893eb000b5e36b4a8897878f59afd3c718db6b60838abe11728c29fe9ca7438b5ccf8db1cf472a7437bf3bf7156f033cd238b7206e428b653399946d64fe8253298e0bb825ab9420b91bd6fa03a2e05f3fcc4bd09621d40ab275f971b3d559ba4b7f6d9a41ee00652e63d0e1a55aac6fc5f454d0de9fa69b91333924270d2403b42b3cd6c4f2ea20a5e895b46152b2d6c463bd60d903d692db29fa7ef438a8921d05b1ca38a650e3bd91a78ed66c96de3176cb60daa8b9ccc2953e59aec1925d9f51e1671fb6c40454d0e3d1c4e122849be16920ee7b9e5b541643b577a83788b74161bf9909ed6e72f767925aef9e07826e7b7003a100c97416d39ff3465a92b4582c3050f3988756c80160b7efc697f1da042021334bba7df3a859af711978d365c0b03cd8e5eb8a92bfc52f0c5cc931306370038d5ac79d9b0357de65b1789113d65b1f53a30241fd4397fd32e16f3031c590a3a3c94c75758f19b9dfc25851d148413316d822eac18fd698dd2887e5cfc6e064ea07a82c35b7d5b30bff932fa089105e8e4b1cfc41f4653bdb5d992404c70050922af0d0f735a179e92d39d3422c6f8b4ce3c8aa1f99bf320c1960a38cf840901f26a631433301d1f2c8aff8907fb860a3326b8ffc816d3a71af8f7ba93dd024d573c49e9b0fffe2468e5f410e84582dd360d4ec50e29955121db4844389c075198aa1fb3b8dc7a43fbb1eb9811dfaa344f1eaf21d2e037b3769675562ae00899064076cdebf34fef46ceceadef79acfe34b1b170282d8f26208d84f125f70c722bb047814ed3f2d07d748f112a8e085290e21d1e56e809f330717b0e7bee09827991a35d04dcc9bab009da86c1c3a3b98d237771ade3a79d7bebca3678f80bbba4ce3e2a349bb6c9735235fe88658426c46b464ab5db2a70783287b2fc7307390d9ef9cfeaf1732aa297de775f22ed56c4e8547e66f55617654716a652fe0d066306458d6aaacdad6c9d0c9e91986e4022bb310dce8d3e3d649915556f63042f0bdcdb49f62fb0be06df83aafd3cce1288caac6d471ab3b2fd14e40b947207cba62596b423d7839292f383b1c6b32cfeedd33e83095213c466e48836f12c036d26e1f407494b9a28c887e351fde02141bd93355ecc9a35e8485b3358c8c50757b56d582157cac6bbe507956b8a249519adafcc9249153489e9664094122d08554e98cd6b0a0c236126c8b30430522e94268d57b92d19e0f1626eaeb88f180c47381f988477ee7ebf4fe78b56c6b44053cd10ae8c0fff2cb462396a5832a403a701da48f0b1c8ae3120e35f13973e9ba93ef1be1fc2138ba0fa60d8b3db6f0ce006234c894491cda1992b85219f7c44ff7f9f8170b49effa4dc8f3d2f200308b06d8c6e9517ebae3318113e8ea894b19b73c1ea3c5a73db5af999a012651f008fd4cae064b076b0bbbd04f501e8e2afd6ac0b7308aadd18b37cb86d04e1b606c8cb1d27040522b195d3718cfc9fc81acdb4f0390db61f2ae62a852b84b6db0bdf29c7cf92449afe6854fa0f2a9bfcd27daa41f061d881ec0dcda6bca5136c01a2407deb420b56f4a5e299c116ad6b28abfcf7b1b93a6dfc7146a29665a89b0268993a701d4bfe97ac264fd4c8c1fbf5b08520e801bd433ee3291868d40bad07fb00f5fc513485c6fb7867dfb34d5bce47cffa3c890223a8589ab8a104d90b06adba2d7e86265e0b9ab00a26ccf55616c92d0fac50a241fb8f09c3707955be08ca9c589e532b2bf928bd54268149f09c210431b1c719fcf2785517d86348da0103ec98a9944b92546f4ee0d4982c8356b4c58d72086f60438d8615db634dd80bff4209c07add031c5ef00e238c17e36717cc636c787c6ce609946ce21f62530f9ca43317fb531c1c050654cf901aab3bab440b05a50acdb96013038bd2cb5b032c75b8cefb1ed5a28c989efcf0c8fd2d27900c17bb3bc87770d1132a1f74b80c4b691bad4d3e2e165d35e9e53b8ceb891ad903f030777b484ea7cfaf0a18ed0a510e1f7b2c3dd46bd292d29127ad191466cd0f3595ff9a0979bd4af445cc7a0d4d9411617c2bc0efcf025c13386eb7c0167bd90f5c6e1dfcef9c3a028888970786be01ad670910470c4537a05b25d5fae80f1e8e5fd544fc151f79babdf43f0f6f20db2885a2eb017bebc02a2080e25655ce8d63caaca184a6cde01c9273d00e34c7783cad2a21f57cd423393a587df3f7ae9a191b7fd3998c2859723ca428073439f8b6ef77f7f3e6bc894712e595595c6a9362b9fb159151310237e8c2a80a771bea5352efcb34d37f9cc378d71061d605239be13d53aefdf15eec2101950d60a78109c64c8ad13096d074d9e8c571e374d456e638fb31c700ab64d7fba305c85d95cfbb18f2e364027a26b24d013209a0ed549a74083f0ac2128b4be294051675448af081286cc57f185def44fa5cc92518bcf26240b6623de279d1575f0f7d2ad23dc8bf69b7f3e13fc28b8a497f51eec09f311dd0521e4f33b722e1bc0025800a3fc6ab930d32aa4b4dd7c514425143f3690781eef513557d1058c2f0055693aaa2ffe77332bde0a50c175324047770da0243d87015667a58d48fbfa403c5004f7e538fe639bb0cca21f3ac21b63194bdd7a991b8697d02ae3de88acc0bdf021483c58dbc328c607c474eb638b234768e9c6092cc5b44c4b6b41a99fa0bea35e53be5f1f925dc09b520037a14b4d85fb163f59a8e3a9217c16b8edd0509887df1fcf3c6d2d2569702be90958c8a5f2cb2e8d5e177a48ae46a1026088709aa3aa5aa0e8f8b9bd41aec8ea33bf0de416b91a6833c9e6794840aa2d59fbe071ae86a3e884002f7ca2480f2ccd0c8d1a50343a61eeb649f30f79c2a5039fb0f4ea7bd1f1b633cdb06df788387ee870b2747bb74405fce0e7e43bc89a432b800f3ff885a43f2898ff2e9dac151ecc8e1fef69fc3357ed8af8f5594064e3dd20cf888a2239fe4b31c5d835d6729716171924a171ebab8d6edf94afeddd700c403626e5d6150848529423071b15abd0b642746d5946de05a0db3142ce6683dea037e454e9814b67280ced1cfac9c2ba145a600bafff7fb96cee795ad5e910f3a0aafb5b426bf0faeba4baf5d625d0095e3f5844694129c43fae7724afdd84ac00f10c9bf35aa1feeca09ca43787593efc6f3e9ecce378e084a7359c564b1a66b0c23f83f0d4567d5d6feb806bc689f9e105a4a501b5f2db7fba02eb37a435c76042397c92ea3c0a60b596766eff99eb82a83fb0543ee2ac766c6680aed4808c90bda954de7e4b897cf7d937b2226ce78f5fe6486ea9dd0ff7c66cb6060cf638d0bf3157b43ecbe6a28cb8397f62bb861a605b168bd4722da0490d4f0bc31aafe02d67bfd2cea74e01500943382551189d50f0c15039545e24b7d8a5305e5fe530efd5f7a275dd7a474642724cd319a5aa33ffe0e5924900d66edba8b86cb0a1803a96c79efa689232d28c83364226cd8fd14db16792c9968f8632b8530b87f030b2e7185fae3a4219f0e552585c5a74542b8c41ac2c5a34e4fa3d19dd5c8288d0ee06f56d6f72ec343ef5de521d8e75f2aaee739e5b56ba3e6d9a89b37fb9e800a27335dcd593839163840cd451c94383eb1b7779dca10cda136387e5768570c0ea0c6242630b25dae727ced70f35e9385f6f78aaf65743d4c0aea6045bd59b004bc76261c573629f23e889f4be4c50c2dd6bd42e8e5b34f5ef5938203ad28a10b80706883cff45a206d00a9ab21a3b784fcc4517753ae2f1e2fb1a364ed33d10d6b99dbd07000bb490d08039c7d3a0dbb9763a4158cea2cd7c5eb24bb57570f0163a7f01a9534bff3d6c9fa1e4c82c4440277a9ba25adf7f6c9260be16247790cb65d629c89757a353935c999c51fd8461e44ceab8495d75504a54c71d09d1f00097fd82a0d9b3b115759d1f0fd76ec9d452779316ca09c158418be280154710968597ac3fa1f7d8dbeb521691d9b993f7d70aef2b1d06a48cd20fb8c57d60904d853824dce3011bfbc07878aff4dddd8d6c0c6462287aa2cd128821a547c4e0ee3cc7b6f4c629f06d532df3cbf76ef4612158f3e19d1c9db7a1ca6a32aef3209f1d3cdd52aca595d2b9e54b5eaf58ea97cdb1ecaa2b22270a7127ffd96163805801ad6ddc2346f146ff99d4d160f0cf8f933c715263c69387c695b28aa6ee2043a0b4239df708840080ad08e5ae177340fcf91420a1f0f6a8195f3b2fcc79f064de6b2503105c98a69b64b512b3e2e4179af1ace089f135d7cf06c5424e34f0fc8c67d10648589fb37c31e151b18bb121dc40cea2abc88e21cf512a411f46105083599f08f8ade88564cea215ac27f4592a556cde38d407972baa5d8e21f5a0df73e2d0943a79640b004d6eb1b29072106613d4ba8b5af8c10af96ad2059f4027bb5be10aa600b16b77c8628db747cd898a11f017f9badbf8db345323a67f20a6bc8d24ce2480a8469ae192b12da5efcf79eb97ebb627ba0e69c35371d0be40b03d2a10d9d91829b145797b42a9b4fabdb6623ac2e2d1faed5b48ffc8964a6021b2ed8a0bf6d39a217bc47e2ec052639b96529ca89891a0b0977417d0d0bfb02b0372195543e6be33359528cee86f5738ca51db216599280a8d81cb54a5be80031343edc6af0c80fe21e32907c8350e166cf0ec7611c58beb253e231d88d7c0930b87e9bf63dc74c60fbfb75566b9a56cdcaf12fa2bd2fd2c03ab3925b31b50e633377272ac262eb63ca1d9d5bd3f5e455d4752db11ccca6ce28e153b75c2b0fbb8be1083ed1d220c92ddc2be37f8706f8231cd194df98033fcfd64ad59565082c286bd5ac7fcd1712fc5357e05cc0508e4f9f24a810931bd6e7e4ca8b7225044b21a47649e3db8c415c7dc82c3e601f1faa3af3789b3e9a71c7a72a01425f0eaa520a6b751a71d5f0ff7f0287a9522f6471faa5abf93570d09e65b7d1782806d8160db43c7fbb5af25b04ed2fe7746fecca5eedecd66b51ecfed54f62852b0a9b84960fe643835c6918c0744a248f8cd9551f712021d6af55ce62135e314a003127543a4bf2fb4a74afc81a30744a9f4b72eabe45652970c11379c995023203bda96f2936c21fcb7ebeacd50a4a0427f2bbdd8f0724260f59e2460ac9f31c0e879cc29ec9da960488c964a8e28ab4a0484d8110137ccd30832a9644f8a3a8080bdad28be182a714fb0f8a13fbe7e200826e44899d10e9c4193d3192d605be03edbb18fde21d8f7ee69dae1ce5aae5949451c0f5efafa900a28959cb7bf2c00df4decbc27fcbc9fc884a7e33c33d2e999fd6c25c111fe0fa1ad6464315276a0488e1e906792f06cf78dbfab354541314abab22aa4af14d49320f196bf1dcca0255fd42b86bd8faa279e0519ac64ac3408c7cec0edabd83683e56c86b27a5d40f190e001ffe97fe14bf9a5bc97c41e3017e755fc12db568733717b3c85fe2ec06793e60045a0e7c7336eba35db6e1597ec5809ade6e1a043bad9713c20f549b04e38197c186524f08eee4faf1d1e8968c4a6c5ed4b2c87a178e64e496d77f440855b2967846296961ff4d19da5b4d269a2fbd52060ca13cfaf5618ee600b1ea08c6718b761eca00d1bc84320e14d81371112ef60045ed6eeee7e0515dea378e0db75ccf8176aa75c7ec237d3ae4004ca8defdc7641bc924dcd33bd128755165067a1d6023910b36a406af67ebf2d25a3dc821d23e6ef63c6a012a6cb83408b409fb851cd56a21494805c54cf6b839eb7b4be17334385ee614a90876d4dbd84e03f3e23ffe0d26aef43be2d0b475f864df3c2cd9e291b279728d3bd2a6970a6c00c9089ae1c90f5d9b8f66281e981c67c69f915e3c24d45dc0b05106042e07130690bff522ba91492aa07256ccbcd8b880d86424dacba5b69fa8dc403c1c527b0b417bfae5d56cace665919661ea0211a4cdcb8b823442c7cc207f09642dcf4b08 -generate_ring_signature c9ab4560e364fa2cbf7ec6e3aa25251bcaf2beb043ccacb0a4624058fd88d86a 5d6b8e078641aea54474ec65784fccb1e95d3a0f6c1b97005d6fb65077c145ac 3 56d63e1b1a487fd4d7975e66dc212e3ba4180fcbc6ba777bda12c4a957f359d1 0ba8218820fc5a47b7449974d27f4cf1fd7ba6698f087a6b007a15637c8c56ca eec4a9fdb41c38be9bbb62e86f0d497b8adef1c297306cc3a209a700f1c76981 461bd2cc07b9c8310b74820b6b7fafb962605dc168cdb11be6995b344bf95305 1 7056d61edf517715249d71f2d06b2695a3bea07d84401b27fc22e5ea35afd0035c73b2a96346cd8e12ea502d2de1010afe37a84921aac4c95ea197437e7f8d03e83eb0b5d2e7e2cc5f971b125893b998efad29de246d1afb0fbe4b698d6c7f0e56480a3f8de3d84082b9cd157965afe3c3a0ca6a3c8c2de283102e19ad31910c82985bbf27f70f84b65eaf1102477ed4b93db467f23e91e982b7dbb80fb3470ebeed00c63713327f01cf4118e067f6e8da3812b2426f37a8aec32f9e059d8b0d -generate_ring_signature f075672344407c2c73e656aab50a5308cde472718afb873c3813cd31eb083a22 725e94903757033d3ebc8b23440f0c208e60f8932872536ed80050f0ce714156 1 ac769e6e63bf28b0e214931c70c958bdfcb8b7e662f280fe1699e9570b3e4df9 34a4a033910e6aee4adaf7aa902b9bffaf1ec93c9949bd358485f59c45538306 0 d497a27dd0187186d3b5985b14eae557f19b1981f50cdd9410b803dd226a75084be605f6783cb961a6a8f1292452406a862142e5367671bf176c9b136a9a200e -generate_ring_signature b5c9ef14b9a08e78925da00b25dbfc501c26ecafbfd1710539c0ebf3f0c4fbd6 bfc8153ee1fa82bc2a51d1e74adb672f8354f3e11591ffa0253c4a3a7843d759 42 3cd53e438e8c719ef4087516c7124def7dd74122134893d56fc52917f9ee9b38 f2e4139786fbd913a4f13e19d484bf62929447b8afc84612094b0f5bd043df10 5fdc376b219fe822837a4ad8c483f1f392a1cd70141368a20b0d47ad9fe1e009 727d639f4fd6588673837ba332ad5c0ccfd832155dc7e194dfa9a8aac128869d c98ec078ac512c8be32d11374bed3fd019d46dbfab909f65b47052a0b36a20db 272da4cf42e06b9645cba73ecbef4c57b64b99e481cf3a1867ee7c971364904f dfe696e5068344bf3f7994528217694b8b3d359eb52a362423ab2dd23aa0ca5b a2b507f5f68b776722b35012a49fa542f58eb1f6f5a4f2b03ea507e306dcda46 4235b88c24272d62ebfb9b75e6af06479fdac9f26ee03560b53e33d48a820c78 74f869ecf9de6d2b0835ed98600e6c24794e549725eb338f0243025d9945c008 daac00d7a944c053a2e423047c1ae410cf73d0b3e3a0c57072016552cf940313 a6fdb201cf3f7b8aaee0409bc5098d0fbb791e4da2e87846e69c0387ee917048 c556403a2899b4a1288c81e6f4e7f1d2b8adc7e1baacb2272f87407a5987ce4c f9f08e37a0c25ecf700e25fc7107c9bd008931cf9fdc39c454bdc4a8f66b716e f2c59ddd77f72881f42cea3a1a1053f9b4e4723e20e65a1874fbaceb9fced0be e7ea591828e31b78bc7634275bc73436b23dc1eaeafb86775428e5c0a10cfd39 7c15e3d04367d0638758a8c9ab73eabbcd356909c04377d9c3008e70e1ed0eee 3bde3e1aeb47353edb3a007ac3fb658471f6b3c33a67b8224534301d41d55769 8b778667d673944e41147d049effd5e9197f6943b65d8bc47e251c7d66e614a2 a566e5cb64d80f5eb09d5d8fb0c7bd67fa50b2847b7594a6817c94a2d2cfac40 44d95857ad4b5254b378db5d6f5de3aa0c866e0eba2757695e5b955a71ad425b f95a8c8f71f794894ead92d7a8343b7f92c2902376c1ae65891b425d7beda62a 29ec148b76bad44d003a3e53c1cde766fa9918b011696402b0b5546f6cc98c82 2321dee77fd3f088ff04ef45e65fe6baf7cc57a2e8d5887efde37d4e5dd5940d b094fa11e71f396fd2c5efa723ae3b86784d3b4a3ca28c454807c379fd36d8e2 62b4e2eb263e0af951102eb0db0ac934087876431fb5169f04e3033ed66473c2 3043bd14d14c669e87fa0c47de6b5253e1c9b660a200b528d5d008dc6372ec28 81837f200ac02a8ff2949baf7e2046dd41475237d01dbf8a7ba05435d4f56542 b374ce8d68abd8d30d806eeb3133692abff1cc03ce765f4d2189f420792d0fa2 8aa45f186737fc070eae52127397db1c1d921b3716233a76538ef4cf8d98b006 094a03faca408ec2ef2422d1e8e93ea9ebd15ed6793f909327515725fd245d8b ddb31f1ca2d6a32e1d6ce1e3c7935d2afdf32bc00b185ed92832441705af8ca5 cf9923a125eb3d3c6a27bc8abb15c9a1f5ecd97d6bfd4101395c547c92f53b62 70b0954b770448de581288b0c28ef89c95ba5bc78d70302f1b3f8befda378e63 f32f02453664f8af919094f4d8ddfa34781c83f4e8b8e76bbbdda82120444532 a1c063c2be3b23be7ce11920ad588cff49f0be61f4cf1d86961ad990e0d74af4 c9ccbb04e610a9d99373863ff6334908a4218f3a27fbd9e97b76d5a1db666609 7352167d50a292ad64b7c1911ff0d3245ea58be061baae00519af17ec81f1cea 7340ce3795cbaeeef9586e1be8bb45d0e11835db89cb991b1b9042dccca58de0 8486d053b5274d48b7174efe4246d6e0ce405a90d0101558f9ff98fb0f1f684c bdbba1f4e3c95f849d2cada4a88daba5965c9b1707e3b963cb130bf9ecbad8bc d077d77756520959c488b842f761257f35fcd5190e86c4676a4a44d6ce2c6bec 553cc6b8ef0b6b67e23e2a5474a2c74b954bbea6e8e8e8c6e1fabbf5ae4e3e0f 10 4df8d42fa575ef8d2f8791c00868d126a13611e2e723f2de9e0a65c52938ff04a54f81690e76e29f41c629d02cea978cd1bb318813524db6cfff08f25592da08cba6f9bc070fc52f371cb4a7cefe0cc2eabc0f59a26faf80c2592adeaa367b0648693b7d5f23623a820c98d527a2d6e911251d0f23f8ca8eab944be874211b0eda6b16811b36552a25c4d3ce5d12b537959c985fcc0ef435d3aa66994bd2890d0b24d1956a2c2997acb633018f38a362c53418701d2ce92bee50666636b0f30607dc1a48d7396d8ca51b6b07ddc35ef2627c8c5231f3faf6e7db34dcba78ff095ac35abefc2b33edf1608cca98631634a04914d296b8b74aafa442a1a0e38f0168a90b49c74f9b03b4b08bac28e985c0f121ad175932f202ee027c63e842180be407571b4db2ab7ef165e33a4b6afb1a77dc042482d734009d47d629ea4705058c4f2ba79c3bb3f7c2c4ee3c22ee979ac8ff1c66787f0e58ae14cdb11b3bd00abce3d258e999ada5e2bf72b551299749ccc8540172a38456c8793d59f1b85e0bca5cdfb40573b257d12ba020402ec3af7ffa5a0152af83b45142ce727c3d590929dd1d4ca76f258364c46150c8f8301512afb6fec95af1336f74a7537b3ce00078f5f36ee6d02c65ee966c0e8d06b263c044a59ee0bc5bcd3e46cfbbb4fd2004fb934520cffb00d5bfd389fefd5a6af9d0e4695f30ed4a0f77911f2e2d7ec10bd085647604450989480445bbe222d8bcd0fb84d01a20b328dfe8c1363ac6e40dc3165b4057e621d42108cd35d1ef1bda1b5c82e925d4e61ed8bf5bca2b096102a7ceff744a1bfcaa750927065654f105433516f6b533592fe90ebe34b78ecd014a665f54e3189b24de6c2c8d2231840d0606e4e75ec0e961255c7ec554c9fa0dff4ea2a6b6604f63591aee139617b86136038edbb3df02aa79d9f2a8d0a43303212281cda7610d9a348de7d4263f9f2768f1551cbd77fa720a7273bf57a94b0a19ea5b978602cd52803ce2b66f8d2e530746b57a35da4bcd96c2fe88be7b33041208a6445854ff23897751f4fda6172098d872b46f62e0df23f1ff933b832f036481068286e50a28ebf24f5e47da0f9b1bbb51f8ad7975111170cc24c89ef806aba96b08d4a274ab16fbfa4a1528d72b6a9b9e20c58ab2783d8103247295030ac0a4ca63103c146df9615277392c6379012f692bc957a015200f6ab669cd630ac7c0500b67dcdc9a8f62bf2b64197f394eab6cc01d8d68d9de76d82defc3a80c5655dac9e556f3a009212cf0204f244bb35d8d61eb56fc33896ab75d1d2d7e0b7c02a8d08b6716fc5e5e19feaa2c9c2d9d5fdce6041c583236d5cbe8e4a40e05911219b6e21efc65e1c950c350c98867e7c329342996d7398c9036b4a0f56502bead90b8dc0bf3e88e8071f10f4112f319905256b9b0d9c2c68acb731493fb04fc4101d6af21735c3f6c9de0c653e362e48dcbd6e5e27438bd4d3ebf4e44bd05ab7e5dc585a1878c5d0882e759848c97be87da85be3efec25c9a7f304d010c07b21bb74d5216f6351a72c666d4feec708fdaa4c77360fd160502b115ef06d20e17513476aea0368a9e6eb863d2ded816a01894a511d33f22ba5c2addead5300f184c4414172e0ad247f7ff57aad66ca351daff60e2c4e13f5e4c8403c4b3b3085f0ff40145530ac4343b5b13e3ccd47b1ae9f645f90ce0909ed7e37ce5d1180bcc41fc4a0740962f5b45b6761c1d2fa05d5be6b8c0816c1c3383f26f02c5e309fe9b3e311ffcbffaa381c08e3f8bcd6d130c58c0730f56e9dc7293f9e2a859057c3ceb3e812822f8aee6f9acd2c34809c5fec7b9e6ebb0331f012ac29dffb00492741e3aa236116a98e25be43effa716ac850021cc5be6339903591dd2ff670d00bf17525fdd4967a0db619773e0370aebaac15d1651cfa3a3c2d76139fffa073b68318850b8169be86170f9e59c37627cae33bb6595f510e5bd0fe525ae1204c15b89fd37b1f7fc89a190623386712363f3e96b39d108bda90da8458d32cb09415f6d0d4c3279947ac5e45d105ad712b45301a100fbc1e8287a07fd782b720440971b8897de518bd40765e5540bb8a82a6232ff7e9c63624748756e491f7e024b4100a5c9754df4d1f52992b7529db545b03444332b5b3b4afe384919c64608e04f6defc8b34bbdf283b3b7b9b1e8642b9de2c4d9498a068094ba6563d89007863de6d863feaf14d71405610fda0d21a05f5e1ec6edeac4d4a742e73794fb0a585fb55e3f8e2ccd0438b86b4843b7ad6a9d39591e219087541448fe15a1f10e3efcac14b790cb60628a1be3b9a63ea4ea98673f2b5145a0390e0a73f1cd6e081034fd0e787ff0d5f2e0b55f19c0bbf333886509ba577a5d13101f70bda15a0ee60d1589ecc0818fc9c00e67b35e306fdf237637461c91f69e05f694052d5e04ecac599c8a07ace93cef69d604849bd621259e1ab16ebaa2d26a7c8a9441900e46c36e14a31de227253abf35f826a1b0772ce60d98a9758d000a7f8a94026e03949af06d2528a82d7ce9f9153ee0d03dde08c8f4d532f9cd3804c5eca63eda0a149ce7afc28ab4ca5c7b0ec53597dd311f1e507de2c86c7f4cc47ae22270b401ceb4e7b4a7dc92120bbd5f18864578c3179c607212bfeb7b258836d81b342d04ba37ac88879c8354674aa021e8109f372e06fe9d948974f5e526d85767966d0a7b6b572c1f9548f2c024c29dc5d7536eea97b0bfdc938d8cccc2769db32eeb00921d03c41c62fc6c0a222ef6e6b4684ab505f18e7f08ec6733b2dc5115ad3703f2a1e817e53114919e606b1c71b0ef595673c52e8ed0f4f6b17d09e08e72d40cf69a68a179d1fe04af217ec526afb028906d1b5dfa73c847d95b9bcb4683cd0914a20c8b15684d4f8f91dc4287122942a5d0cad863e6a9c100649d57878cbc0e914fd43bda744193ba84437d30afc9e3941125c71a448476d63211389939c50fab39878d8962743382673c015c0adc18d9aace62eb3eeb6964831e322f7ea8037f67eb8eee9131e950dc4f259adc15f4c300979668b7bf6aa69eb4fbefbb8d00e1edb3fe5c07d6bcdea9597b6623468ab0f816110ad72ea5bf2f167ffb96b909e9e7a704496600992ebf26928f200867e4b74b2c93b8a4ee0a2a8216ba5c210ac13953d4bab22f7c00cab61809563cbc7363530c63cdbb922e0836c2a179580ded4a7c9f91809fedbd1e4d12ac684defc9a8f58580cd6a532a03960185568203bc0e2c75755f9796e13d21364c86c7591bc63018dc2ce7122a2231eb667b4d0189a1e9ebb297b91ad182c654b475905bb456f251f92b67ed7663e3be2e89a701263413be601e1b24cbb683676c977d7a998f64eb05aaae5504065ccc2bb421055a93468ee30986ea0f71c933cf1d0de159534c1bc113f664c83c6460285aaf062610f9bc21467827b64c0eece7684c1bbf1fffe9889da6b2c7e21a03bd3d7306ec1f71db25ca599c89c8663da4b882ae01bfdd05831feb158e7f2e4cfea47c01ad9322f3de86b09f7747aa3f44d7b5d1b0b4d26c77bda4381e26c6c5bfb3160f1e75ac8a363a209b5c37d5f93466c48cd84d120ff94e112fd763fbdccfb0b10d1b2a95f43f2f92ca47d4e08aca57da5e2adc13c103071ac95daa4491c0866e030acb0756abcfadd58c09dd00983ba0d6871b9d3264753d12e426f688858e640b46c70f5b81e2291c49be1ffa2964df355781f5466e1eab2f388382c31c98ba04c788690f3bf5356fceec3009f56dfce2a137dd7ec8ffe93007953dc048ff580e -generate_ring_signature 9b329f5487db8fa896767fe45c6f92096dfe0be8251e4cd36d07c0919a9c7dfd cc275cc9d60c681f4d00425e16466fa9f2c36f5bb333ec2c95aec837a40d0fd9 4 67e340c96d30549d1dcea389f686423bf0eadc42fda684a21ffde3fe2a0a1d3d 118888e22f09ffecb3a079f729e993bbe3a2f8ad9b3ab98943d7b6d039c93e3d 1c03bb98ac40e56b9101b0a628aff1f3ae682a026cba93ede7304f942261f04b 9f8aab00b42766e651e5d5c5a71ea8399f52bab563afa059823ec0ebef99ae32 52b84d2cd9edce72b79d4fe908f59290c7231b8fd6479b11b81e990097c65707 2 c1581d9540631ce2966868d0fec7e4c676906887b94a4d16679852b625fa9d01a07990ba0a3c457e78dc493427e406afb97b002669959fb1c768c55a7f7b96016c8da559c0500fdfff680212db4b9c30243aaf27271dfc5046e9a313aa40560eeb8849c6a58f0617dc59b1bfba8429979784cf63bc1e7bdec89bcc44eb3e0905e1c306e49e593f9d349b75b5d4f6891b863c4de88219ec5bad1399dc50a3030b976fb8e5501612a3ecc517b0f2c9ff9a07120586398d169d3d812aa3cbf81b08c8fe286d93a6b1053b5e6f5e478cbf2dd10c258e1c34fd4b431e56581afbd1084e71e97da3c2a4511a2dbdcd8cf7d8ccb89c264714f941f49da7941fb1c11a0b -generate_ring_signature 3e8b6dc355913127e29a053eeaa791f7b9b25c96292970b20617a77f2941ce66 864ec9b981e85f596e34ef440cdab5ac85878584c8bf0990753d53c281430f5b 4 f04f4e042649c5340e6decd37d44afc47a81e00907733866c866d853e10616d4 d7ac266b6fd14bb55b3a4c3fff28017b1f2d51591a8a9a38a921b55e0ecb2b36 c579e81fea2b7202b881513f371e0b5cc4ba5e941e7378cedbf2355e5df74ec2 16cd14d1087769260824246f32fbe6c6e8068e7efed639574c837904fa186e57 1d46be9f88c8a3546d8107e6460b04cde2821f0c815fdeea2a5ee8556b36f006 2 83a241054339ca83784a712e34d3a686d829e50de74576380be5ae1c1cc99e077fe486fd923a435a19d9b6555ad855734add72fbe7e719b9578f63e0f1ff16032abfb77cee40451dc6e1d9d5611b7f54daf589966824cb3227d55bc4f2b9fe0960acbbf64e522ef2f738b7fbc8f3cfb4ec092e7608e6f56c0a94afb881f57003a88e35516894ed42ea1da2ee62b836d33ab649e8c11adef7149efeeb40536f0f108f4740761f9186f96df95e6718cb69538c367051dca6e656a348b10977ac0138bec3b830e9da4dfef7d9d0316d4803ada80812518d6db64438dc4204b6f20f5fd2f368d3def5df71cb8f882b283393b0965637d8ac400dbf97147197503407 -generate_ring_signature 4eea1e52284c31388c572c77b4dd6bc18080d356f22b220fb2cf4a1706bc0f86 0317dcfb34494772c5bdabcc64b2661b8f061614472914a82ed7860376853ec3 4 35ac95d061c9f478a387f5b33ee56b5c962c622cb8335e3f5b05a50dcccb9567 eff1403b48ec6986a6f3a436fa75390d47df6b75578cf7c00c9545fadbf9f9f8 bafee79e294e0d60c0cc2c4ce4fae48df86d0ba91e46e4a2e6cd033b34c74414 1161986f00835114de03ed5ac2f9b94de944c1a2b9f3a237c8e3bf9f0b048349 324b8b23740f0a95fb37e26b28ade940114335d0c395200e3df2dbccf3fe0b02 3 2c5102599b8dfb098bc41dadad8553f3d8de80b97f7f825be031e4993720ca0111a5cae362cc831a2fe2886cf766382700b8cb3b63e667ef57c99c1f7e964e02acf16e1b4858d5aa24afd02c8117f8841013476bac8bda6a87731fda498efd02b77b439f4258f3d88a9cacd23fca62eea7cf6bebc9e74448f2979b92662fa9039f751f3d49138cf3c9f996627b3654828bc118c761e2e674d6acf2b4e892820b9eb34e9c5f3755f0d84ea3a75e57947cfaa2c1d2aaf9e391906a92f5aa25c00395ccc07ccaf8abea39686715146bcc1311dff525684bc6538a97a7738c427a0905082f90b7cc5ffba7da93940854ed5e3572069360df887d52e3b093b3bf3909 -generate_ring_signature 3897f3826263809665c0f79b257b70f91ae560f59533df45c79aa481f14ba6aa ed20816021e0870adcae45b087cb64978c27a36fb94967dcddeba7878c43065a 6 1f644846ce53a77e39b3e5ccc0c578bba7b854b801815d27e4a279b929752d66 9370f055164552e621e0ea348794778e844013451447dac7420ba92789bf23a1 58d1f500efc8f928bae7a56d15ffc8b07b65139f4578ec2ee706ccc992d46cf7 07c2dfc3e82dd1625eaf9edff3a2fbc75e6e641a41a2ba56ed0c939d2b81768e c2156a0f6cb0f9d9c49abab8124b36784d2f0c676c9af313d1215357e5203e4d 016c41e09ae240dccf9ec65d33ee94ccc74845a9c8c233c2a2bfa220be6dd093 2753960b51aa5cef3e1fe2f7766429bf0bf3cd91268e609c44e5e1779005d20c 2 f738c0489fb544b24d02a8d28e231378f0dc1cf12814571c0aac80f14eb04a0ac7f5632475083d272560ac01537389afd92a8e8890de713a0d2bcf6566c72d00229672b135459b0b00e180db7feb3f48084c6b03eeeac5e2de4c97102a366c0d645c9f2df8df0d6bd74eb96020d532c00be3ea652572d0f7be8eada19a1a81061f3ad675397f54751a62802c8a9f1f6dacadf96aef817144e16dc7e7359fe50057de3b35380699d25fd5939a5884101652186bdf46cf6fc3e712ff0a6e056005e52135d38bd7d7622e928f7fd375aac38e5fe379d04efadfe315feecb25f05065591cc787da2607eba658a5b596b3854a805ed8aca657cf1ee00a9fc9c0fca05485eea448330e740647790c3a658040d486c79cfe4d7d59365099e680b0d160399c8b4ab33083b1f9b2d30edbc4eaa7cbc9ddec02dff44055920da09cf69a90893fc900c2703aaaaba3cf9dfe505cb8173272660dac59cd86aa15e1ab8816d05e842d89aa868173666c2e077b942f733052c7f2c971ab720599be50fdfba3b08 -generate_ring_signature deecd93de3b6635a3900d9205d9a0445035451960faea63acfb11ab858265584 1157cad423a531df089ff8735908f4cd8d8804ca6f796839239c18df3adb224d 1 888be7fe0293f3f66e8d0bf3606153505f274fb692431e258bea9ca2d140d85b f8dcafb41c048b60f3119e9537c0e2d44cb10b1e6734aae324e473769a63870a 0 0f20e6c16be22265ce6241f9e0f5be348dec032def584279cc0cce918567c707cea6a5a7ab04df2caf8ba67802e3dc4d6fdf0d8102310ded9495a3bcfa0bfc0c -generate_ring_signature 81b47dad14107e991eabca238e4b65e7367928f92b795b2f3a2508c79608dc19 c05b11bf0e1c3e0f47038d6aa49ab4daddab874de20e0886bd6f6c578ff6db01 2 b7f3a03806831c048c28fa2ae2f5fcac0e6d4a76cf9fbccdf2ba1d0e3229b35e 6638087a63c6d537b87a22004cf2364162a52246a68162872931a6c0c35aa5ba 75c26eb3a2b4a277a7cd508b08879524621a628ed9ba713b276161ceccd2ba07 1 f386cc4ba9b309500795c7a56dd86fc359a7a099d5af9076c0944c08422d4503815597721bfac1b774e9b0ceace12ca6f8bddc8dbf57c5cdb37047b924cd2504a0f7e6d53b7ed4cd092d685d6d1cdbee60a06cc9e317995ac505d3f7b346eb02f960ea6d977a8caf6e4239670952366ea226d3b22f5915726ab5ff1e12774008 -generate_ring_signature 27f39c66f6a61282ebe04d3c6116a57e4133798f01a738294eb6a52c813585a6 a9750add17565f877b2b22823ddcc5ee4b89f41c728972523d61a69b78e9088d 1 b02c8a057b5cea1652d1a4db956781e6ce07e6f86e747ce176a7eefa3d4d652f 8ea9d363e3c1ed113f8b6ae7672511441cad4877336596e940db05efd1e9ec0a 0 98d385d75e74f0d67b4e56814f0812e6f201a04fbe17d6b189b1250e5a057f0f49fef4b0551b7e19e6aa8b9874263cb821c10f3a73168a4b52f2ee5ab3a66f01 -generate_ring_signature 5c37b80efb792c48e4c99395fb19626df810aae0e617e587415938611e35b37f 952470262e4fc8ea399640c866cef6d408edeb219511cd4eca7006622386f661 14 84cbe808524c622505b5e67783989b593fee2b939b5932b8c3fd77bad3b4647d 60f3e7f2287a85996d5aac23f440ea64c6354fcb1f7013a5b25b081f2dbd991b 3f4a81047e7b9b1b8d642f8586c6f98bc20baad33f75a7504afa7c109b0fe612 40d1ab12988d9c1766f01559399c0e0c3451da872781825364a1c42b46760ff6 549564baaec6c44ad533e9f01a4a133f2ba260da2127a78ce876c30e97c6cd58 82961348f57fc20143b0d6b30e6bafc63b8575bd0a55a28df76af8629cdccd00 119798a0eb51777954ca108539337f40554d4b2df47a4d21de97e135b66b9dd7 ed90cf28e5407fab6b8d8fb70a98d9fd064cc0beb47c61ac94845dcdeca4eceb d4b35cec0f367a4e133cb2c6d4e63c07ee92247ea1033cce6bfdc7a67474e224 d37a52f026a589b51064eeeb64a14aea05420c2bc70d5a04ab1d5128819ad3b0 968604beed7f12f1058ac5312bca68c811064117b189e0e4333db93e603c8be6 b3793cc7a8a5daa5f02ca4496390157df25213562e7d4fcecca72e9a23119e71 9ecb2e012c207c1d09124479bdc8ba9fc10cba28ee51301b5c7eaf9ef68fa355 011a63f6c9050800a210f1015674fa555ae80133dbea7e709dffcefce488f691 ee5d09e9d9167824d3ae704ba8c5b4d2236f69da9002c28cc0faa08954caf708 8 a77f66c1bf3aee949169c2fe8d9acb131e32f9c2ffd23ec56a2829908d562601f3eea6208bccecd66f31f596fa9f00d18a0a15691560866ef26d3d430f618b028916cfdc1fbfd1afae37da8f99a13edf81a0ff3760af4be77e4240afd58ee80bc7127b404467ff65bbd3a8f9e4b642d471461279091675c66ff651d1c42efb094148b87d094133ba314676a5caae8dae51aa4733ff8974b913d8b0c4186e3d0ab3291402abc9f06317057cb2c430f6b2a4908d8f0337a4e562219ccc1d086f0343fb1a2d4f512e98973760206403f6315a59cf5f57272449d04507f4c7330106d3704033300f50e6224fbe3b62dcd74bf951b8a7fa0957ecf4a226b331ce7f087bf4b8d4e15b2f4d5d0b1b82b967405b9aba037efc362198157f61c4646c1b0e2c398117c269d0777bf546cdbd438a9d820bbd538d2028cdbd8d9cd481184a0e65edc53e29ef19d014298a3ca602259eb53099265c4bf08368466ea5ac9f8708df1c7796544e42665a130828257889c42f0f87a7e703cf9a4ba6a73f0c75f00e79e6a2077f7d711cc50a9c70ae1a111764445ef92e677f1008ea236dfe232d03e4b26105b27f1ac86c2a31ac87cb176e415b529438607dbe899c8d0e1c2dcf07369f9e4c1914b03389c910d8ab43b81ef611b35b518a8854e7be3daa14451605378aefad4ccff7dc639326ac778ca35538671bc8825a110d5339a6054690920af37530856183484de3260a2fb5497ae21e27856766e3e678cf7454dfb2e35f0ecc758c412d8d0be58f3b9c71705dcd20e81abd4a668b51c4f7f165d045ff490326d7aa625bb405717826284e791bb9cf4deb47fbd0ccb9ed636e296a28882002961bf7c850682e5a5bce94f66aa244cdbaf5b69305aca483fac58546d0d4e10d3396a42f0704bbc70dd830a500ce3d1484c41c475af49ae31c0e8e8f4248a5087a77d595078a1035042868f98373f7966456cd8109711ec1cf4f6a9f44dd010a58ed9578348fcd9d1c8cb569d81b95146d6b42909d46fc52fb06528344f3160c0b2388c2ed0ceaf32da74a5547feeda44f08bbaa7d04628109e73649b080120192c9ea78a19e3c1cde8ce487d07f25e2c4df78ad19f41fefcfd245eb03af380c097add1152d58c51e97d830ef654b15a93afa0fcc0f319e7304f7e8fd331aa047a23a03e544c33471cc97d6ef893b61cc74295d03d1dbfa105e5a02207ac47035663503fda553319ab0d830737ecaf8ec2b27e23cf5034bb003176e98319f40a -generate_ring_signature c5541e41e2a344f29ecadb669ec32474ec245ef6b2b5f06717612477a8bda42d 62feedb1dd4c876f4e95c2733a958adadf85d278f946e4c82f222a7711ca10ec 125 2804aafd5a6c29ddff9968bc1d6cdb3ab4d346e83d219fc96abcc5729f6b703a 77521a9631c275477f4163fea03d7c3fd704fb7b5efd379320e005840539af0c ff24636208c5422a9c83e3e8de964ed134553e54fb7e8205d2e77b325c66ecd0 f21c08aebe88ad781e69ab0026121fd5f134720075db02bc436db5b6ce9b459e 0535f689f85a203f3612be098e8fa8f06ef477af3395c6a116bf9e3280f57975 ee82c0276beaf45e6efc7ce81696bfff42f557029ecd81e0df7887b7dca4b0d9 f760789ca02a9fbad11532b7cda06619a32e2e6e66973039f4f843a1cfcfb906 f41125c6fec4783f9578aa8fe9fbaad0dfa5612a829c9d5b055c778f404a120a 0e9f1968af9a915ce9c88c0ee9a1c42bc4d6f606262509404362a80feba773a8 43d5caeaae639a65004bcf71222e8ca2d3b2851b152dbf7bb89aca28c456e63f 16a37a71f407d8ed2bd161cb9d3f45a9f2801ecbe67ccfddcaa31cede4cf986d ef3ad750bcb7779904bd2bcb5561d505c1c1891d8c78e46e94cf1d63a0fadcb6 746178b33c58f06a6685b550b789d398862762f14ba9a3f8331b2ba82e971322 ef470ff38a5ef2fe7edfc030a77d086100965fae7a341afd095867a411eb2745 e8d43a2b8c03e0eb1d64109b1bae138ee4578dd584480fbf9becd4e63ddc3ef2 f466151692eaca15a47af224b8b220c1f71c51a78b581b1f12695947c790d09c 0b064774a419a807e737a7ef8ff52dac16c372448243428c321c024aac33078b c4763dbe8d39b3f2eadfd70260acef19c336a1fd80a89d68bf8d5564c467ccfd 141ba183ad8dbde5e67f98828c4adc018d53db2cae83c34e3be314bacf438591 41927644356a7633faefd0bea5fdbd25bae1e720819d2fe79500a950e3fe6d98 90d8b56157bc869813104b12784c4e88e908186e10435d50add8746552f89c08 9f3eef380682bb8b6e0998bf7e3391992492183d624b829ed3340c34a6affa7a 87397030797108c58eb9502a52a18973db5700f8bdd610dadbc80aa3b6c66e59 2c5d5a0d6bbeffb62f288ee87e5762d6fc9074bf787d5df4dcde7a49e6efd1bf 8b2c1a8dd258009d5751dfabecc24fa5935f0d252871dd66d293ad0185915a5b b1ed2a53a023134c2023a77c7725817a9205f831864acbca58f9c79d417bea85 7f55eb07859de3dfc0a8e206d1d1cf4a0342488987a6d2c18f41e97df0df014e 5188fac915e9e03a83302e4ae128f1dbe63b89767b91a4ff3b9295c30dd3bdd7 10ff7af1f258091927149854228a5c7d5ff271bf4bda55a9ff34d1ba7675c509 38e38959c1ebde70dff0a5bf2257a13281b11d432bf7f51d3737c500c36f7f2c a19df45899c18cc81122cbad000567f8fcda8e789293f9b77b5845c03892fc4a 4c33aed9b6b8ac36afa9892e7183675ae5490d21449940b207aa6466cc1e97c7 4292a31b6bed65a201ee83b71915c238b72072ee439b25a2cd496d2cbba85250 77891645ef71973768d5086c5e666eee6cd22c18c9c027277154c294e58c1107 761de841439e26204b01290f022bb014db8df120501179be8f3764d7c19fd8f5 9e711d6b3abaf3a78048a9e1276e6d70aa4ba0df9673d7b7cd653494125e585f f9ea96c8410b15246095f15b1a080578a3d2e4a42c9ca7ac41d713d69c674237 13b6fcf0056f59eede98eadae5e9308012083fe4986c744e1fd93db114c389c2 1ab06de0e3067113d4ecc48c5321fd6928e8f30c0b4c073e21a629f794c930e7 f2ab29c1b2669413a71b096379c3fc8b21889ba95518e3fc576215fce0033592 9463f8f4a96d456025108c444132060d710a6895da60ba9ed29cb0e7fcd222e8 f17341125a334c6f5ba759ad278014e62bab511f9c7fa45890f99a07463d5299 0cc4709fefdfb59de0bd4bce0f3833a2e3e9bcfe56ea4469f9c1bd9e219ad662 cf4a202bf367be4e41350c1b3a49a90f1faaf925a73af48224b02d8679577d84 844e462cb86c7906ba82a2cc5eb86828fb9bfc790abd59c2cf54ca6c0524b6f7 265cb58a1b994444fd2a9ec13e22f1e219ee2910b87bcc809e592d4597cda731 eab37e95ed3dcd62f3ef9d98ded850d36c437ae0f7c4d360eb9bc0006567dbf2 a9bc3a316ee0f0dd1245d33aa0f2b0a65dab787db3b093b583c1d183cdd87388 c593bcdf8162435ede45858a3f82d18b6e08ec9917574bb630b019885eadc15b 519bd964446b95b440fd2832a2bdafa6fe9dd41f91bf9c190827ecd014817149 3c97c72e64b47cf79222f987d41a9f95959b05f60aaa714ecdb375f6bea2f24c d0799a853482f9870120c87d82efe93f6ba672e361244a1308f5a918f8026204 3fbcbe6e8afab9f595dfc5d42092b46ced57cf4a77a7732010e5435df18bcb35 e7058fd902caf725e1937654bcf210f8a08bbcf4c174127a19bd013a4ed13c5a 3538cc278efd09cfcd347eec14091864878040351f128a36f17f8eb1bc1bca93 d023d792c2ecf5e174d78de4f4eb1c972f05fdc31510ddf285ddac03edb6e9a8 e3bbc9156f2d1efe4b0e5bf24370772956146201f29458902f299e7b178e2926 5ce05d70587d6f961dadb852cbf573da00d3575902e5a111204c29c1c1995524 13d6a79c71d331cc44369af63eebe06208038fc18a35d65a9bcddfeac86799d3 f0c18a55b75b0e8b8e34b5efe0d3227be9898fbaf8b270b82a347c477442331e 6d941ae5057bd14443053028bcc66e3df3ef8002ed4889722a619d27af42c4c0 65d401e25158b5d536010e71ddd6face36fa59159c7147f57f98dbabd6c03e3b 236e3b8f14794886df4b2e6cfbe93af86a369ed36ac7a163855fc84a917d132b 2e731fad62c827847155d6b543e91656f33a37f29802b99c74b5ac3113bcd3d1 8bb40fa5f58d155cb5875f14fd8f540b6c5dd7fb230707cfa3914d8bb76a9985 b741f627de1025788557446cd8571f965165f3b87ba70bf9593d2543060b1dce 40c2cddadde1a33d787f42ff20b897470f6c49a2d1cd8fd9c9aabd4ed20d8098 b9b5944a94b1024f7f03d858245bc7085f2bf8c9b559040404b2a8f1f5668353 229f8995a931a23752d3617ba4c06a74e48a6f3c8bfd40aaeacfa2fcbc6ac031 c97156ee1235c56564ba3bcfb12c645e5eef9bcfa4173c3f7efa755646a3999c 207cdf552b7744e8e68e4721ee56795e2ea0d00db61156784b351b07d91d9b16 cde0f0dc90d52b59a17d9f220a8993787153aa1b2dfb30074633219be62ed6ca 2f8add5d450bd257117c2c40f33b50dd4a85ca2946b10217344e842ad29a3f57 198859b069a1c48e96f6420b89dc69cbfff32867574717ff02965e9ca9aaa9f6 da1c7cfc3af582887f4bf517548956f949e1a83a8b82a5c8a396df15e1ad1fc8 a0e5719a9ffc23af748a8e062ce1ffb8c82da607c2ee26993b0e76c01c1951a3 05776203a11284fcd79016b18d7b4544d10b9ec88a861baaa3b980ded424da0f fcf8075b1a91c94ec4b6f8412da739dd74d749a3cc55e9574e0b9d982d21ef43 d567383f5e2709fd6b640dad0915a229e62d8e44880cbc20b71637ef6c883cc2 8c1c065394f4585c0244671370ec3aa9f31c73c109f50ef0290e82b41730f3d4 a6def36e17f8f1bf3225f801454d57b2f8792adc94b1933fa01af88fe8e967ea f61371c2ab70bc8012c87fcd14f12d7ab1a2eb51875c764acfdfe3a2b3f69487 aa0303635376c96892e2e2f609d626b6dcca8b19879f5add956927ecfb6112ec 327a716cbd42649a2f59d3fcaa59cdd2049e94a96bcf21d77249246ffeacabe8 50123f75a9558eb33656757408e2ab0250269a03f9ec1dfa8386b38ffda39f6c e38b6d66bb8d6be42b76fd58aa01f118da2cd9249adf097f1b3b4f46a54d7ac4 108e7f9b934c25bc12d2fbb18031bb211dc7cdf996f111d0a2e56d6cb9c6a9dd 5074acee3dc35437abbeb588f5d942f968d449823b29942c21c79a16f05e65b0 6038f6d5324fa1e378010bf8ce3bbe897d5165b764396e25c243db6906951f3f 969d74fcf9d04f95b465ca2b88e50a79c678cbb3d1e1cfda6002379972eb676f deebec572142847258635858b17708930be57b82404f14d028005099d670675a cc8970b85874d414e741607ab7a9692512cd3108c0622c89128101fde33eaf83 ddc7f19c8d9a83c0c32191ca81a3430383c6de882741218a4150c9779559cae4 2270ff3129579cbaa7f986cf738785ce9fbfe70ad863cb39de9418905775758a 6e2a1ad2fa32e93d2dca5ba62643116e6b0788fcda52a9dd74c656e358370580 0d7b0a579493f836a031ba1a45411f36e4174ce06998bd231bb22e4a61aa4340 eb7759d06fe9444869c5fbfb6276a97ca4c8fa577030618ee50c7684900237da 799b5c7306c3f2882eade612689f64b84e03fd984f229b2bad363e9d22b33895 271ece66610fdafde9ca4766cf6e9cbec05386e840d0b161e5508b1a21d025fc 74528f7ec8243c31361f0c20b8fbdc3001c7af4a03e156d907048fa5b02a939a 5cc57310e3f8c9b79dba293353aebb162d7bac8535ac93366bea3d4ba7421dac d0440a843f298088834e32c073f27400c239423b5964cd4cedfccfccb0dbdf7e 61b3bd3533f547cf1c95755751fed8295be21372be495dbd6adefc160bd8e3a8 0a62af61f1c5b60e5d8e0a73ce851c3118b3a33b3e98248508ffc21c12e1aa30 a2bd0986f7ba0a3e2acd06c9e5bdefd96e901ba630b261ab1dce6ba18628513b 87a2209b6183ebbf569c618925fa782208943c0c4ba3be4ccd1dbbfe4fbefa80 c929225b641df9a42614b29645ce066050299b51fa4f76e4d5badfa50f62faf6 02b21e01e28dd2f197fbd265b82c6febf31d13c357e2e524fe770938786c0898 e224e1d91b7f496640c10c658c983faab5626f2ef388c8573262a5c2b803d737 f7648874f6126260ba9b85d3dd99419ebd95a187a00f113efa1cd8e26e24e66f 70d018da92f36ced31cc9298b8be6e9b7d1873b3443c604e0911be4cd6bdb190 0f7966f0a07e18ff4715fb0d0cd23d99c8c6aefb0295670e86eba3100458c466 327b0f4c5a5d34c9e69b5f3c2d9e32191ea616f31afe0cbad2db717b29cc4e15 e6c620872b328204fc60016bfd6ffcd5f475868e9eafdad2f90fbe8fb604653e 367bc5e3d53ecfb280d5bcdd33d37c3995a9ea2f4488f20b0bcb8bfd84b9e655 b826533c6aeee95b7f8aa6b9bafd6604065aa3f048e55acddee13898e7f9d41f e7a4ad5121e7e60dca0ebf2f49de2f19f622ac3bfccaf7c59d548e08f5176156 7b9802431d797c61656841f32129f8a99e23f625013df856eead6a0eb63709d3 6a7337412e7935d128dc296c0e35be814127f25ffeb42b64ec0dbd294985ef11 2ad88ddc9f00c78f253edf8a26757c38f2ad59cbfe69ce6cc0cb6577dc863782 d8e3961134ba49851ed743543d66a85dae08fa786e859ced4d4f1e9eae6c148b 157eab6090d78b4383f756450430cb3a4a993ba3f19e73d04fde2425aeea0703 614ac9e9b8f848877e3ee8e24d1b4c597f6afc2e518175b02b209a69c0a565c0 0a4d2290321b8aeace3965cf602c32739a0beeaa91567df3ca40efa915973447 8ef0ef41d7e977f0c0f2b955e8868d331a2cc5e1aea3cc014a64d96f1653b80f 6f8dbbfadd4f875bd3fb7860e6b522be87b40a36a9aeffb7d8de0115f2047b0b 66 4dc0004a909b0ea093f3f5a7bf9395f1836f675f63225958ca66db4244c0610824c5ac795e078a3ff4d70c021ccdc506e522f9b57d6ccd5c61a14e84bf7bec07c458a7ffe6df15fbdeee0f44e89a762e47229748764fd8c2f653927dc65bd7008e6a6807b9236c1f9c8b4a1fadc0cca4f821d3f8442743f1df5cea766f4d030278a5fba36f3953aa802e48cbb5b420f34bc3d29445f7099462637dd769c1dd0a3a01e6d2cfce3b2bf86dab33d4167d307d1d25339d10854dd345ab54cef52600d6e377bf9c92ac9528a4ff2bd4931a5860e7387523cfc7cb8f70e95ef5752106299d63a321d9bbebdadc554982029f0663e8392a53829475308fa7ce53b431082023ee77764f52e79ba7cdb268deb5d8a13cae9921bad88597cac23a33d931025a12788a0254eeafc6f242646e7723ba124c3d30476ee70097114a7147599605eb282bc86b5c012e08176e40cf3930fa956b0f340af172933bc824c21c177903c70f70185bc2fe378e6279e1588a25f178ef000ad8a6008647d53742cca5ae0871fb150c700cc649c70d19262811dea8912ec0900429e554e56a6412cd492606e2bdb0b2ca12269727ca3e3b2e8cdfa738f8730914c63725af4c7a8f7b8c100b414794dda4fd2477d63a74f167131088cc070bb9aaf9f14173fc02db48a2ff0a8d6a78a7d3ed7ce29b4869fbb4fc1404fb4a9d2530d4e0cfba4afb242758f5004b7b1efa019ea7e30ab61f25a8ab5bf92c0721e99700eae5ebeb9a9fdd3e66081ff4ba01ac9bafe46db58d196c11cb9304b14ae3d7a3385f6cdb7faee3dc1905722eacd1e4d770923bcad99a45aebcc75b602151d36a2a83e7c625cd8fca480e2b969e32a19ca91c13b003e1d038f1b1b61dcf5c03a44d46932157d48e77d50430d0294cc416ac98a306fb9adc248d16cd6674baa2b24998860f366e3f2b4f076d037dc636ee28c2590cc6544ed6e520a71c55d6d458bd6013cbc7156c8ae405e1b1b68b33b8abf7dee5dc42cf1fa130b9c9e5108330d374b37f154599ba290f9b983c7942f66d50bea032c6ee85b9472c186e50c53689d09a97cc657697e9018a3207bf4d9859058074682b23d7603fab003e18b19c0975174be4267f275e01284d7ffb807527bde515b9161f7708a30ef256ab7dceac24044a918d8ec01701282e8d37142e45b9d6ba348e23e2aa37cdc6c0a4f882f48fbd93ef0d843f6405af3f86e986e4fff771a532d0f1def54ccc8c7fd0a7ecc6196069ed5235d35505c24d5e3e1a1c9b01d130f1eade76aa4643ac0434ded63599bae730532253d605fecb9834d901326f3928733a002d9110d737a79f397630af8be30a5e4fa027046414c36e1bf9188419d80ae52f9d309e1fb1f45ab402aa2b4544d805a5c19207fe45708f8f01111502742e23a63b61763e3e81f48f88344771d8b50740fe940edfafa34d2da5d0312d68a5de4fe27f95b40382eb048b5d17edc58c5117b21c0478b40196daeb4e191b9438401d51b96a43664e8425b1ed3f3a834b7866a5d00dc584f7e68c4a54a1d02819f4855ee91a8639513fdb94ce05d936461013a4420c8b14ffd7189c6e88124bdb69855e23790cbaff2daec526ff7daea1cfda670c086c269540d92290dba760b24ee696ff5e061e6d18b25da69a6cef3af82e08f708c027bea729340395a89a239d4636e7079f95c768fba29b7d2c97c7b57d54590dffb96fe728d290829f9382e50bc3fdb14b832fab00ce44c5cf2bcc9382c3c3038fb4e05aa99d90b3e82c8d097279eedd21317307779aa1217d4b9a65349cf209394a10b7fbbe87dcd98f9defad1c39f2e59d2b5b511b0a3cef72846f339cab0a79e2b8fc82666e6d6164ee050ff1a74a586e9bd7796acdb337e154cc35feff013ee0337cd1b6291902f904a3a0c0463723ea2efa0b530b2748d30367dc51b7081cdff2cdec7083a3df621271930200412fdfae3a1cb69ab25074ac6a2b260209f185c74313eb8986e4d99154cf0011ef57778013d80205d8bcd4ec3f3d602f00ec4d597d9c207b6ea53062a36e0f7688de8641cfd09c418ce217e08b7f31a10ca52afc702255a518e24f2d9d78fea290ae9eb780ae51f5f75073948960fce00d906fc4e84a7670bc547708d2a52b3ab3381e7a540a77c1ebfed53de87dfc81046e8c74e582d864379050c61296d720be792823eabee0fc7a2f783a902ea020099c1e090e13bc4b92db2cc059b32a9bc73cd598bcc023509f6191728f353c93069d637c734d5f860f801b4855c9328bab9b9d59eb5902fdfd2a67e1d26ce7a30d5cc693fc0bb0100f246b24d59c784e029eb490518402fda1cea9d3cb5bc62f02a179ec62f872374a1061d9a0c05eae1bab485edbe8e53ef85ce34b89de471d0888133bd506de75de0b29d3c5c40ae4c78f15ed4168edec1a0d931fb30b457e01bbc51160e27c79bd01a440bc79d0b6f49e58bfcf3c1e2da8e11b7b4354ea9f067d3de16fc82e9066d181744203447c84bad3bcaeb81a737ddca73e29405f7404a30b2cdcf2eccb50365d45036f902abcbedf6e3b9781993f3d64880ffb65b5066fe8c78c26c1f4090059db8feb5aeea02ccc86cec3a6f18379465f18fb80b4073c6fcd99601c397911d4a053407c4fcc742b6043289d036bfab167ba1d85840d9cc14e881e52f68e7ef84f895986f5a5c4c82c4d1974acfcf348bd382e6cd1034300598fb6b438164d7997c93f3be5a3c94c4856523209a9768a5ebac7866406e2a3fa1b21d7684afa80afa7ae616143631f6344264be67968e4ace35cd8850b11b94c9711f89b43172d0fe7c4119e1480b50900399417976abf1096ac278e08af40882eb26f137446c107b5af3de323287915b727741afeb8685ce8a0c5160761b2397955cfea1e719fde1eb180bfe4375eb36d4d358e4ccb3f05e1e955ec01b5e663b12629cc8d5afc25735a97c1a8c53d53deb8c49014423f6f953fa86f0ba4dd5194abe5f6a1ade9ec84a152e4bcd95c909a84e709cc015ee37999a4ea0891151e185cf33c68854011540159a89ccf582ae4b605e168ff3d7c447242dd07cd19610631cb4e817e879ba59c637d5e0eedb8d37f75f90274fbca5d8da2d6055693069546f92530d1225a73e61c37197d224fb495e6a8b2dea86d9ba472a30f6b207101bb41f5bfbbef12d043281d6763881c7f58dd3a0ce83f102cb1e24f07afe7e3132a83eb35d7506b842192b407041a219b8ff76757a5acbcdee2847700696796152b4e794fcd481520fa47fa9ffa14da0eb305fcb5c8c71de1446f370679bcffde16f2c7a30979e1047855e8116a4738a82ae4fb2dfc3fffe893b8830a63b1587bf7fd448a3cfca6ee640552adfa52bb32620a8b01fc0544f6473d3602bdb97235dcfb7d12dda8a85dcb4d346b3933873948f3929ac8f54fdefb486d00e471508918c1f7426d654c32d1ca8d2517dfa246060670e7ee56b2e9d7a532073f356fec3a2c9d94824f3f702c036b58e71a76ba9f309736076357cae37be606751e120e05206bd1133d022734a2ca465639b1981a2ad786fa791ad4f6fb3900a5de9268dfddd7cf39a8105c375d4e5ab6003602a66d33e63d99f966ea569e0a1f221471be8cba8575f049ead182886c339e7af1c4ad797906faad46997fb508f5d288d87511e5963fd23c89575990e7f8eb9cc237b14740a16987b99cb4220caca046d3644e742f8d099657cf68945c4e1e502195c382daf2429bed25dd1b0761d9141faf6256ead78c7aee9dcc42a505796bdb35d94ee565e468bc7ea00006ca58e6f4c6dd591dbfd6f3306d2d765522f68bef4ede7405190da56d2f3f4907eb98883dbeb5f022bd4190e8a34b2d77f7c626e75675ee08b51f1693af8cfe03f9ea10294467d8c136243972b0fdbb2196005c6a1f3a1d6b303fbc3e4ab33c0c977bdae51d5731bdad097ef740c6b12ebc634fdecb82090eee099b72966356006c89681f1fd2776c29950605daa95cb1a0fea3ec6be1a7dc91f8dd626c570506fdea4eb5cb013ca970c104e0a39096d5e4a7388740ce44ca0d64c2c6c7448e0aa867712abc79beb2d5366f368a75edf4f6870094cc06c091b0f188cbdab84d0e2838297f675506a5914845b282bdf3c6e1120a64a64fe0ad2ded2f5f4e86970d09db1b11d7420b2aa592ea3ba101390beef50d42596a44042abba6b7d8f2f40d7c7468f1048c90df151aab5192309ad06a3674ca1514d92236d7b22e1181ce0f4eefb20b710fc0dcccdd141b012f9142ab7a35051242fb329a56e499d1dbb80e1ea14af799a5e00d5245115081686a6e16d867a37c7a0ac864b4fadd125e0c02b80dafda7e91ffb641ff964384b9201c0697e3fd500babda58839f453735eb0c872bf6dab726fb303ff13cf7b2730c0bb8b13d96333bb01e5e27caaf4b6f760fbecfc17f0359b7e33745774dbf05fecd45519acaf6d0204d582dd60df48ca30946e6f2ad7e28ec2e62d3ef75a32b7434c723c28bc15ff71d0f7f8a6e2f6151021b4502ba6ee6042d56cf7d8ddf9d7a45728ad1be62aa7078e9baaa6cf2cbe80d336d7422f29c6354f73292f3ef0d19dff321bdcfafab53ac9afb5ee924e31f07dfdb9c832f2eefd29d1e44435e8ad9fa64b41b612382b1362a3509d78632e104167cb17bb645a4e261db047ea86e6261373e1d67844fcd7e87d8d6b24b0b99083569d0497527fa36c53bc9fe0bc4ab94347fd9879a79501cd19565d81718970d9b863026e088463a9181254bba9bf233fcdc22c5a5e3379ddfc33e950b11c8040ab1557a7753244175a3d434862bb07844dbd2da6087f9c1524e83c7e8c9d60ee56b8f55cf0b61cf122aae9592d0af9a9abcaa9dc366862018892676cb9645027c17e9493bdbc53e5ae8bc29ab6751f25c0e041a4f8acef0a2b6534b5036420e2bb54030f05959225d4c9d0ddf2f9fe0ce1841d4380e52ba5fadb5e586582a06e68acfa26cc07bd8b918d4d59bc9900cbf525586b2da060fcfc9dcae2fef270e25a28ff4f1fb449aab653b865061d4a3789c3201d6bcf00c13f8678c934d0009fea4ddaa82e5cb4b270b6142572eba727903033a45267567a39c86a3199a1c0eb2a97f5c5e1dac1fd99a88eff6f1930f3b5d32261ef6ce8bb3f721ec58d2d10dbc8b669044368af81efde77025e31ee9c5e226acf4856341a47bff97e97d34093db3b6d11d4c3a892fded7f9ae333749e1003df3878707f371444dec9f756804e3414a70d52120cfedd38aea649fe4d36ef2dbb4ad8b7e9c58a2dc586a416500c8f96d8716eafc2310c17f6760b6b3451dea0a31194dc4fd84a877b6b9c41700d2ed8eb936139d08bd0548dce4a7437e6aa6a17b528f5a9d8f7f01882dce8402e7b19d53c2f8ff1e2738dc2bb6680c1be82abd48ba09f9452ece81d35d1ff9058c38d2dfc00b6b4dde5df1b0e59f48302332e35b5227398497aa80ce4dd891019879a820f92e3ef301842993ec1d810016d21148543991549c110598d2c7860d2b4a459e75f8a5a3bf8aed975bc4335c4f6aa2da40b9b4e9875125fa7146c60d535f56953630fdee6df568aa32c406e0fefe6fceee7129353103cd37d977790fb18da1650495640fd49772dfcd2f8397a3d01b9a76ca4139ee8e9f6fb4399e0d3d14412fe02a78c0cd3873ad2f3a32c31ae05a98427bfa42916223fdac912b0190111e7ad6fd7c3a438594e9a2b2264abea9639773bd22be11039634a35d3b00e9daa8823e4fa8b077df4379f1ee1f32f31c384db9da7590b3e30d1cf7225b02272a19e373cae8c7a1d9e7310183af3dbd3ae19a6060d3b23071dc8008487905a0ef9011dcd39badf5e1e4092ef2bbd51a247d84d77820686d38c26e7630970fcea629f12bb7983c28be7d73a92fcc401435095ac66bd40350056445867d7b0f37aef3134b3d01444c9e670a6615557c283a7592ee5f446480c40fb6ae961106bae3137c8145e7514374e55cfde7001fd1276aa41a36b68dd62aa438faa5070cc5d8e3f02b870c254ba2328bd9d29dc8fc1059d5dbaf950731fa620cb0ffeb016e0ff1dd7a9cd8f278b76170b8092ecbd63236ccea97539c4708fb212a4ee5001f67a175b927bc5951c9c0d708340248e59103b86b7d32c591251b55f753e30d404bf8dbf40f0800348b301ec6b6b9b508667c8548cecac573ff2b9a2247630c51f58d56e558e39fe65d104cc73749d686bf8071da26a2abce09f5e91cb39c0c4f28da63b74a1f27a4eb9fbaf27732a3341daa872f87e74ab38a694f54da55095bb5c9b4ff005615c7cfbac47a55a79000f3765b20b8e409a664214ec86e5c021412f6505b59d347d98c644f289dc752b97a20767744864d6c85e46fdf543b07290aecd9cf9afd8dfd2cde0fef08fc85c3b5fb1c59f3725869b1e883162a2406e3aef1e43e983f1fa91d03d6b24cb8100717138a64561bb2582884d7c0cd3b017f135a7ba72ff7fd095cea52906040ae914499567342d59908544a8d1e7c830af8892a55f6408f8c45cad16a23a1ed76262a3a5dfeb8b174996263a5a39f2b0aa458079ac018f048658716bad78a2f263fb041d322a8a437468b5292ed835a0ac46664fdb56173d4922102414e37cf65473b0754ee0112ab49cfe997323a61001f581875efd538f8c6ce3bd16d191a3515c8248f4f5c4b6e7961dcda9286b50350b600ca80e76d21db282c2706e5dad59a1d8067b20ac564bf5bd6fcece0fa02e00ea91e70f1ef97f0109987761778ab1ee5dba88db2cf88c652941b5ea0ea03c0d41a55845fdac42fdbbb91b4c78ca2fac40f2786d3445a116f50cc22519606ec742626a24257239c18640c7d548b08cde9ba01c2890eeb975bc1d7e90f57088ca8e24a5261cefd62eb6cca15415a23c050d27da45cc0066a80db5bbef4d90928178802434ace8553ff4f54a12b8f11981f757be7776010e6436e0c72820509673ebf236897067cb144ab2a14e7c1a38815c08d7ad3809d86f4e4ba3f12060faa3cc3d56eeeb259feb81c1e19dc6ff6dbdc19b59c8005dc530915453362880aec9cdeaa0f720c2e1f6d9dcf8b2c805a42779ff93f4f0281e9191573cb29ae005b0b3f61fb7a1a8672ab2386a2a258cd3d579cceb7b81cdb78c82030a497f80876b4293ab137f0459119092701cbb0491306e89753a8fecd8356d068f843a500b3924e55199d1de3be74c9d722864018f47ace0b74dd4f9d639ce80d04abad00347ae4583990c44454d7331cf8cccea96427a4de05d34a3a955c4f1e746a430c834de19404a77fd33796ba12600690bb4441f7620a7b21e5252021d6f460cb0c59b0b022c97236c98550f26499beb6a80c8c5c6c2ab487d15d0d285b366c1d039378e2710597941242a64a2a9d3a3b9eb72118ed58b18a2c5bb1572e91806d0cebb74327e2e0760e1e81a2e61792e39f4f6e8e374fb015d57219078f8e8dcf00ded933de3cea84f7ce9f47210d43cea3ef0971b5e428f141ad0dc58ade09ec0eb36602bddb295784497b2ff23d589a72900b1c5bd596f7f3eb9805c9ab6a7e0dbe75f936497cbb41a029ee027086da196ab971cceb28662a1f0a7ef8b5fa1d0b1824e3a45f9619b5c6beddaf9d6de9fe6247094e73667d6fb0f86340e9bd4b080ac02f73abc702cdd9ff4cdebcc512621fe0e6e533c77e96f15dab1766a4b00a15e5d2219fd4339ac2ce51b33d8c93994f80b6940812a97d6ef5d75f079e3406397254b1aa03d06464fcf4b1175e2986f76f87eca47bf9a12526d750084f75026ce571abe2aef2dcaac4d34fb704fac8a375b71cb24696e51d783cfce68b6b072775589a34609c5644e1282b6df5b59365fd7b6e67e2a1c38dd3746eb2d56800ef7fec80fba750ac12e0e6253b0f47011d422b73796ba53ab7f47b3ffb5cd300b0b8ad0aa2f416c22257e6214159935a352efe87bf1407856784f23fd1014f02d44bfd5e56094eaddb138e33d79fad843507acb4df64dccb9a3e08cc7f352700fadd7c8fdee2868d5f6ecb32da1d12d4d782e788ebe071b78fed25bdb41589017fbb07f0dfdf9b45512c972adae139476da1af30eb19c34dc1b09f846e4b9e0d435cd56833b2d7a053e5e3475dd788e6f184b75ed53c9223012fc7385856a6038239e38e3b15169a049ecd44a04c3dd7c4493ef64bbe1a4c248eaa47917be80833b2ee25df9ee4f2748fd67439e41309304a8a8c797d14849d939bdbaef2c706065600ea940ad7952238c7fc512d9cea31e927a2b6799aed1c2a89a5d13e8607b49aac43bda321e980538c88bc5698c7141fe6feff5bac98fea8633aa96d3808606d545f289dd6741ef12fab862b4ae4e50400e140e930540dd81cf78b2f110123dbb0dd77cd6481a245e16d242f2d9aee4d1c132beb8ea45ff2e33e6ad8a407e20028074f1a89b6eaf6c9e2c76b8fffa8096603a1f41843de0130d968cf2c0b9ce0b54af3c39ae5dfbef615e8a437aefd3b4e8f276edb6a79b802238d995602ad64f4c7dfbbe2803be40cb3ac3af6a67ac86e1bd115500ffc95af0c68885102223c21be4e38ba59c440d3ad778ea28a312d93b8da981b615ec14eb638ede802f6f8b7a9659a178a5d0e428710b407ea1f0f981806bba9dc7850b15c3371c3092b299dd69bcb72f2167a50ebfe66d75b4f256fe16254792f5518e1389585d80c141d1cc2116189e70f5d790e58fdbe1055e59d4670810169d19ad0f5eb40af0dddc7f10e86786ae05cbfd4a5897d00e3f175203ecf39d6e33e252972d10ebf07559e89ce2e7b2a997d2dc922d5ea14dae614c0fb362029ba1c439fe104d0cd0adb0d2dd9515c7577b2ada453627af0762ea281cfec691f15daff0f52394fca0e15e77e76f95291c4fe218a781fc5e59c686395abaf78919f65a89c907a29ad067c30a33e5ae5c6e7d1b11e2f2b18d89f5a765467044a6ee5c924f580529413016a8be05349fd52b13382abe407f08fcd76e82f874a380be55e02c92827783405c5a4c201e4136d37527fdc5450fa536dfc5c029b85fbe2df2a451c65fcd1520a73d6d78fff06142f260eff5f68f6c6d6a473985d4259a50ff54a15a7ce0d9a053621fbe346a8fd4535137cfd5198a1ecc5cb2ec7e8bf4f7e48b4d4fe8abf9f05ed467dab5a373d91ed646b7183fdf5263dfc4ad173714ae7b236d1fe6ea45e08e422649589ace8c836c991b959155a8a61f658a60f4c7b1771a0b56bb37833065e3712f8a4d0f41adf484a03a2db86cf3ba761759581e9f339c8723133f2e707f5415c1e6d32c65f6917497a739dcb633a51be56c46baa4196e0057feaaa2f09efa4138cce6f362fa26d4664700fa5e7dba5a9068dd05e75279d7862b628e503d6999aeb7c7455fc1b6ef2bbe1605e0cd615f80c17ad4f50f568c75b8b34d40e73b271c611f249a76150db0903d82350b73d46fe2da93c618fe16a8c0991240295bb38dfe42b9095c27d51a8d4ab6504f2b3a4e88912dc7d44636ed6dd1d7c081ae6b0f0c7ae51fbea62feb5c5598265532b4a6c361fc45346f02fb9fcdaee0bd094a11ba49e0a635affecc691e01515bdc139a5761e263e87cd26bbb4480a04535d7a04daa59b88e7b7772c450059531b7aad08f1e313792eb5f106ce52000a078ff032112aaba4b6b461b650be2607f562e1f00578e0043952cd49db32100e1401f313f1f56ee68795aeb76b51a09d8effb36343b73bdbaec36ee10aa6a909631477ba215bedb1a9aedaf0f0ea6a8d187ccaa3394394599da419e914822903399a12a1ef3fd6669961ccff82ae74bdb2d35e6dcff068c2646c78847e69e604145e0ca5bf43956d33a228e877ed4f5fdeb3a618bed80c5e67c12f59c3c9c0008de90e6b9e3444ed6b027d8bf42798002622eff383220f1bb884fe3552040c0c6687e2bf0a5da43ece1eed36784a3e31a7c6e8e57643b4e84f7896e7104468048cbfa14f2758539eb3c62041cb9c511eaee8feb4581e0c31b1f56791986c1e09cae2c1ab6a4158e83016d29542da36fc7fc15fcb13285badeb45b462c231f10513667d398826b6910dfe0d65e57da0eb687a31893228f85a81b61a08e77af30aa24302fe34a3cf2b4c200209c63737209619bcee377a31f436036767d0cafc08f7e95b09820071030e49d7e74cbf0e6ba0673ea8d713ba881381231fb19c470cbf069190607b448d0e948a18cd75bc5028b8f8fd3d8500fa70c5aed87ecae003e10772fc6cc5da07abb8ade5c72d25307a7edafcada5745637398666e0538c0243ccab54690242751b84a34f0f7cbf6793eb7c1978d57f2c4648eda51929040ca1dc2e63351b213ba5b9242d1bb82fe5ff347629f73dd7e846533790de4a0c0fa51576c8b5939c98a57896052f84e4158ca39d8456a3d6ef78ec751a910ae70a1ee20227067149ed052e4add5edb294875b5b8a27206ecc2036df19cd42160028b23d984d1900112711083a1465576eff6b0bdb8184479079ddadf3ba6da74058118583787c7994d247562a67ecd8e54e316890cddc8698562a909d74e48390d4bdc3489613e9b6a5e48006163f1b0d8f93c4baabb54d3b19e557f93580a1f00d1d9999d7f6c510a267758ea5f4e5155d364c47ad0405589272c5ce3b0af8908b129947c63c6de0658084111b8e82daa500f2c0d487f4e46f7780699d9be4806e77798a83d366511797b48294a314ec53345db6e10ba721cb3070c2d3b17e90b31ff22e89ce20bee3e7f0b665b3cc60a8ed58a413b13b7368da5ff89b32ab104612f092203d286f9e8339a998035946b6a165f9182a6eb66d2f7478abe8b660eba8a3cb19164ce0f43cb350b3547bea194fd120bd5110a100fe6d9738207ee0a2d8447a7a9de8187e2b303003a246b0a7697e4bc3e3ab082601b9edf190bf40dfd084c664397d05a644a5445e4619ab71b75c8878be8b638f6a0f4d09e81f70887323fd93acb4b0584e3ab1aba68655e90427c9a1a2694ecd77a80d6071a8f0e811fdef1faf5a2cd2809eeaea033e3f918fd51157d0538d61a22571313ed560a33971875c62813364e8aa6be1fcbfdb3cf8e261994ff2f3916ee0ec7ac9ac70dcd70a95bddb71fd57da3c4e3d22d657b8ba8b97017d531c64817b6c659f51309b00fb5794c7086b8b9fd719af69074d01f3329ccebecef1479ffbf02abb6860b1c6b4af6bdbb7dee15c2e5e824f1a3d8a19fc8e8fdbf17cb541ee6c8edc6fc0972034bfcdcd8e0cf6568c5dd8815f3821d254a8fdac4c4c9f59075fff2b3fe02f371efc09679bde59822a4d17f37383bb528fb0722018ed742823aff2944830b -generate_ring_signature 6050b724afab4e33fc1adf423689add9c50984ef8df4b93034fe824ccbff6d6a d1588a8bc29b2df0d555b8b0632841b68cbbb4623ee615c9499862aa5b732cff 120 ef87037b6f29edae32f566f14ea179ecf470fbbcabf36b62d252b85af30f35e0 0adbf81b03429adb494d54bad3a726c919e81df09046e8f237eb9b5f82882a4c 3539a4bd1b04de484d125456d2135230caea0bcd47d76beea4d4521918f7e769 e01e642c44600e167c640013562d0d83d62e9eb696b70e9ca217725dd0b46ad4 9e748c90a8fc9e8839f770f94ed282e08b2d0319dc1287bb2fb3b240d84dd055 43001d2b2c696470582c98b0410879d5f5afd93d7bb90cccca625d289bc6a3a0 dbe579be20a891db9f42ecaa18e45ccbc4abd200ab7258131365a4d5c93e776c 69f3b6889f8d6d446374ae878b38da35741eaa4e4a88ef220d16163703c244d9 f8b561dded476626a7c0ba1adea2c1dfd69d96f0dfcace37406cb1bb25b19d14 881e289bc813db26f4a718a6d790d553c8edd952fdc6e70f1434e0cc81918098 0a56f9f412b6080c9bbd334a6a0ef92ae6a61b038d6a09b0b4f1fb38611bcd21 2b8c0b64bab89e760d5a74376f331ff45e5a5756619836c00ce5882b08e3c164 fb0b6c65d3823cffc4fd3fbdf0f7d867017a8c2d2070af7f69753836a10b3346 d58bf0a33db172ad6cd6460498f3fce85fb3a138694c3beb46d48bb5fec7fe26 0ab01cf3ce2a8d87dfbadb6477bcba61ef64981fea057acc295aec2dcc9ad983 3936b64dd7d013462f90d1969d8c601bf6a4f0bf45b443534eb2ccfb03998ede 412213951f3a59b88d161c057933de94085d7fff3fd1e370b22b16855b6df2bd 5d6a58959691c9c8a62c09bd015b84d4fd7a4c8ab3826100512dda43cbbb4ff9 7eb29652b5ed2b1080aca08c6eb2bf7a41e30df6c229fd56b5d5096905b2acba 732bddd78f3cfcfd342547d80cd2010ae931a76500c9fc073cc193a843839d02 e48eb1544264647008711c351e81c32f81e41f5a7c85a7141056c253e8e856e3 7288c441a034b859a5056dfe0fa9911c24775637c5f707c2eb28c90961747514 f06413f8ed307215eea8842d1f6aca79eb1846c21e1c4c243b665ba195a1781b 01b8892d2d57f8bf7cca182387a6d0872a92fb1a72df366d873307ea3927fa12 d729b986311d8c0da3ca87ac439c5fccad8128a5be990850a3f5820846147146 4b4d2bc6efb964a9581dd77f183a55567cfebf196353ded9b03bab6649ecd10f 4f12843900a11cb9802ad2474cfd41edd21405a2bdd740e9739c66a5bbe50223 d93acfa9256172f808b269ae6faf49ef9cc3c86026efba2002d2eb6a1da504bc b869d5d99868014123044ba4808bb55ff416a7590cc23bec4d46f7a517ba5b41 35b15cfed88fbc7ad9034d47394fae12fa8287dc371b78a7970c51c0697d483e 24c214c701ad51ac08e0a57a8da5c0459c2499910cf99a6a851cd435b47627ac a92fb963867312988139b9e3c29d2076d1a7f051513b6f2c3c24d74f377f2e37 7e4054200f5643f3437795f83bdcf506d97221196ea711b1d69afa5e4b345978 a9c74fd8309fffb5f589dd48f55e29653b8d0638473139eea572bcf4356bfa13 c8ffc26337fe41548090425edcfbb21989da5914326fa7ea776b9214252ec87e 3e25940afec823a365f77fb1f13aed2180167df85773ac52edc44f2346ff7c59 ddd96cfccaf4b991b9fd6f819ca1ecf8e0996e42862b9d0ee71f0a446620822a 50090e56c723895660648958c033bde07fddbbe89149b1a8c8656a6a35a4bbd5 74912093994ca326fb15659020de86bbb291c425b8937324a0070ca4c60a9165 68fd8c81aabe602c09f4b37046db3fd2c15267d974c3697c5f6f35f25efc8183 44f490d63003625443747a7243287d28b3f5fe4ec2a273131d8bc7e09c461d05 6359841de569f54123834720941eaaaaa59b8f9c2671fc7c83ded6f831e9fcc6 ea75a676b28568f3330e7d9421ea92f3ad3cb9ea67ca6a1662c45eadfb67182e a6c073f05c29b0ce08c57cf36d71490d5d40187d51d382e09fbbb2a54114a376 f4b02dbfd2253dbe13ada78a09b53ee799fd347705c07a9538cca79fe719845f 4535ea3b1f6aa90a6b98e0b940c164b31f4787b6a847758e88ffc7fee05d5cd6 8772138c613d9fc8f3f62a0373d7040616a372212328d0acdc333d62fabf9a40 a8f52f5a71eb7d703f5478f4f382b2cf3b0efd6538b24ecd632178dd432dae5a 97215a35ff24137d83c793bd9057ebe8ee7e310964ebc5d41eab6d15342479a7 e7ee87976fac8c0a016d83d26fcbaea4c18103ce971a1d695333632432cfa1ca 00d81de15264f2c13b567041ed3a8cd5331cec6305e4ce7772138be437d62437 d452fc10e867349bcd7f3a783012d1b4e26befbc821b29f8fda8cf5e5b8c9b03 96eee1a1c980b51b51a3e47cd893a4930acc2a61ecda8beda97ba416fdbd6f15 a828686eaf20007dd299f869ef52fae1785b0f25810a8b8ef5b47d2294b77349 bcd0fce555fc44ba05a71cde39e36de093d44149f803781cec70a1c040d4e114 e5d10dd7877e7feb14ff7ff3074abbce9b62b7845de46880a1a3e400269e23fb cf4cdb04cb0158c1397ebf413b9940424f4eefc3aeb315b134bf1c012de95886 e99a22d2d2d0bec068aec00b14213757b498140f6a637b3d14244f4a83391640 e1d337d2a691a786e85f0f2b6be24fbbc6e3b0916986de5551f2e04eab58bbcd 1dc8e63527bf9ab5699fe1b2e6d6aac976da2fe8c0f3ba390239067f19168d03 f32db1edd95b03ec7a6cc707383072b2f1d9bd7553ca3213a76246ed050e5ab6 9b6a260abe2e997d96e528bee60949c7c79d22e88e3f97ff9740a40c030e05d2 ba6364da3293a9a53a9068e15387444500d6d5a91c45760b30b900cc5a44cdb9 5739ad6c057d2c16e74312a7123fd6dbc46a1f34067f1a6d3f8751f801fad433 e6bab56a1a5f21bfb687e77f65d0ec0cc0063576708f02b204a6fe716f8db773 97407b2907cb431220e5553ee94eee4b92fcbd7d3d26f11e48399f5c9ee85be3 861f72244c53d41969ca914dd57e15b82158270e5f133876664d689d541ed165 0de2abf3741bc18b0f5937818f3a1e33523630ad8e1cda086b955c2b13c4817d cfc22f7d43b1effacd87521b5463772e8453a2798f2d48c2918d264f15c563b2 26c67ae82b3c310202c1908af303e69ca94c0ba83a8176d54d509781da183e86 75b7727257ace3fdaf28bd08606f4e8d471a805858a4de3af7ff1b295a8877b5 8cb68849c5752cc92d8c718605eb5390ba07013bab0659429981cb8249148107 10e3bc9b4705f0a5545deffd9d83f16268f4963d4d12225086432dce3f317d82 994e0e93851f6565314a797059f6a68746533f6d8afd7164538c279e3983bce0 0026807556960aa20611418df4ad89b46722bb4273305ab5beaee0518bf90b4a 5df227da750dc157f18d54abfe7ea27bb64eee746b41315c843b42ebb60a93af 6c3d71b333bd43ecfd9bed9e9c6a3438ee386283c9de2bf1d9cc6fe63eb3c7a9 5862c6323eeaa240cbc96df54754b7f9b38a982646dd58b29b7674fe280afa47 e59d1cbd91f6a1da83f811068d2c32d3a4dce1cc87e12dc670b3c8f4cd837668 e01b48407c6cc98e591279d215168d45f8a9b0bdce74294c863520a96c1f3578 d123f51fc47d4a4a23d2589b3ea6afacac1523ae68c9c3e987efde55960da989 b24cbf3af9e8598a861c21eb667e305b275091ff54d619de94427c4e8107b4b1 ce5ece118e978a798e3bec113fcb05480ca08761b12f4d82de6aa20c351dfd4a 881e216b3ed4aef9321ea28d6f31d565356bfc4b1df72b877881273b55de257c f086d77f57298e4001ff99f30c1ce019cd81a74fb77dc8d3b5e286a972b52b78 22359fd9b4985eb46e587cf19bb5094ff334ea115c12eedc31b14a0c1a883306 f4ca0ca1c5e64f8143edccbe63ce32279457871600f83f726706a37274f0dd82 2aab22b4fd2e8d361accdb45a9035695c109d53d69ca0bc4b6cbec1c5a5f73e0 d4b27fc292dfe7d49fa7cf49901503f5c6fa53812247a99d09fe2b540c1393f4 136c4bcec68a9a9d675f590e35ed6a9e51a85be74c269c66345265620f5f196e 2903ea5a2ff85ea2f92a411467005858b3a315b6b6b9c7a7b406fcf2906a69cb e86aae3208c02700d40ea0c63ec911ad32aa67fb4d5c7b055ea2161a877cced9 c2523fc728db90b1111de2a58e424cabb7dc53810094cbfed40e0f1899b31cf0 239b2dd969fdffe6ae68c2a227fd066b0d08fa305c8daa807db24925d2a7ba90 2d64e595338b931b929125659133566e25fc56874ef3eae77760e289a4e12227 64ccdc4872ce46da3421e9404faf86eb8abd0046239c71b8fcd9f2b7e1fa88bd 85c0e8646ac761449a58f53106cf8406af576c36b70c76410a038d36762d4abc ea4dbf46f8653be48bd964ccc8171a87d06c3ee1874eea739df7de9c8f06553f 31ef879b28137a2252476dc796f988465ef7640224a5d9b8b75bb890e8a8544a ab061319d1d6fb3a9cc5c2cde8201a208b82e5eea782b88ee93aad24785c2d6a 43d69deec91854813f55eff328db76ca3f1973eb498eb0f1951a15c806b8d9e9 f9f07b9a4f5969d9ced711bc842836faf8049d5feb3f4cbcf094089d0213a079 efb00a7103ff7a73f36a3b7daad7efbd296feae5fbcfc8aea798a5921fd4982c 9ea8ac862144b79a9715c672a206686f4ba6790c08159746b2707a8f20845b44 22f9d130216831692a2fb6be1a5956179794cf0d3ab5240aba781add81c8eb7a 2e43959ec556d13a5e30403182b5d786a6c90b1271333bfd306b15525c3838cd 7dd14d4250642888be66c827013a345b63ef223c79e5410d86fedbe880f49511 075559e15ccdaa2be0d98e6a4e9248189168612117f1161da8523fcdbf62e725 c071bc9b096c8e911e9f6a4b05726b4bd8cd54ee358d611c0bf0caf467764aac 82126c2b00980c6bbca68944706644332100586eae865a99391c3a99fe206209 955eea558bb0799db9ee5994a88e8d323e27a63075def3cd6d16a2ed82359240 23a79218069842b0364c4ca540ca80374492d6d3bdb578515eb5c62935e5cdec 8c107f0b24dcb8d4d5bd749b90782be058a6665c71e09052e6521a5a23148ecf e55ae85f46ff1d248b12fb35c35df934f4da4865c9969504dfe03eac92112b11 9d4c8f0d6c14ccc7a9c9438cd11224344b31f50c9231fab574d3923bd2ab6276 6475135f9644ca979ce611a47ce21f55f16da6edc548a88b7f1f2d5cb828abab 3f78644f896131d3080551c3b5dc669884f66a90069fc02d0980c8cba93dab5d 032cb098c9caae12e929023e907322382e07864e0e03891710413bc9baa6f628 03f519eb0e73ebfdfc8791829aacaa3c10fbf4a69505e658abf61ee87b6d101f 8dccaddabddeba280e5df36ba751d672ed25f786c12a8c725f972b6f11e3c638 e567bc025376197432a4459ab346d82730c42b604ed31e27b6f16fdc61351b01 16 818312cd8553bac764c2aca009f760d68a45c0e9b6e8ad23cbda753edadf030ed290d87a9d7d91c864655460fe6b91f412f51263baad7a3fb9cc564556cfd403d41395a65b03e13a2a5a0d318f728c494548e83f0d603ef6eba9f0b30965f8021d9c1559dd557050df1aa1a2b7e2c2bacf2aafd842645871d058e273d4802e01e5af08974742816515cf26a531e17834ec7e774c7dd42b1b7e327c0f1617f70a9e5aad969b628dd5549c859d76f097e0b047a025beefd8f12ca947df02c6070ea06f65a7253a736ce1056ccb5e3ff713e451a85f97ce54185b54019708b8930ac2ac265b7559df6d5b13354b015c967c0227816e7a881cfe073f2d30d72b29014953ce2e87f4f7deebdb9d415a050e8f891e8059bd9c923d861b4c479386b4012c0f25b5c00c54989d377d4560ee99d973e57f90009766834f0765768e0f480f414f29416dd53d1da19c27cb4b18fa8e6c0af16d34e787a463e7fee3118ca90377b1a51daae573b5073d0ef6af05640f78317823f11be982016e042905da000d598ed64481bd862d689b25815e27a97674cb25f4cabe85803be0239bc1b2b909828417a1cbb97af967a3245657318f5c73cdc436738734bb9c0954a63879330f26551a552f0d9bd2fc9ef55778df4f610fa08297103a2163dfbe15486e908a05de50591c9e5c000e3c89e45d37dca7fb7d81e42d213b0f8ca6f1b57329f7390cb92efe1bda555d83f484ec9ba003df8e94e16a58b88de6b475362d28e6c2e305e9cbd35fbde879713d5f3185ba07f460f0ba71977fae4e1e8a78033da53bda062288073960b02eda2a5188b919342da3947017523a52ab1a4da5b421ea726e036c75a5801a209bfc18bc4e010076e51f68f557c14e4f41cfaa25a163bb4916034a0904e7d2f01f9203fe6e89c0a90158fc1cc11d9eec28f931916cd4e906ac09caf1c454d72a44f52e141d3a7448da808061b59c2b7f7fae9cb96f762377ad085994f63970df0559ad8f8a84ef17ff34d3646a87641935fe667459b8744a3f081238a79b3e399de8560f4f7f86e29ccb2285b5e66119ce55df50b8d86c926a01f8d2fcaee416919827b2fce6b6193d912d282b35d4176224da640ec70f61c406af8dc514634f852cd5364cc625b9b3bc6158a1ab8281030b5c8555f85cef4f0bf32ad788cc4e7bb0d506a8e612e439854ff7e8725779d89041806cd73596ed0a29fcc805e9a15aad6b8595f2c74b48191fd71996460563df11a8b16a6d047b096d6eb0dcf97c901246b2405d6e081aef854ee7513886e9ad523c8e9876f03c0da84e4da9a01b4a577168ea293f6cdc8da22637d5dcb9f33e3bbfce6fb42cab0b6fc84ada32d366fbde8a5c3a2275ad295a5dee6b9e69195f04f197c817c1470a4544d9183502d01ba36abd8e90208131c4957cb08bb7994aa66a61caa798940ed89579f116f86a4a8b3cc948265ae688cb6fa73774bea2d46a23b944c508770961b84c17e5b4e691558c902408a7dfba96df06987836467f71d33e219beea20aeb3404002393c1530f63991f175afc675a20cd4402ec1a1bada9e6e4853d5f068300314303312a498fc5a2eed1f5257b5d9425da7a40ba93283fed77766b20069453839299fec4eda12df847453a20f26d2c614e39c8c0d2f36dc08af23c470d0805c49a1a04a71fd0eecb1c1a39f95003892f1101daa46738247362efb94a06bfa2a610e52f7e798b3a98d0953f76cd8b27a58e492b1e78aa64abf30a316307292a39812a6a3206e8bbf7787d4a5cd42e60e9d0bf59e949c058f954efcb57040ccdabf5ff5244083e4074156bfed2640b75fc694b3e1d17f52ee37512d0e20802cdaad46c3d7fa436d6d8b6323566b6cba14a504983eaa6d0fcf0dddb53ab00ea2f5c7269b6a8b5796b55566c51de992166ecfbda52e138e7acf1dda84dff050cf4c6e9ec192d9d774d7d6f51491b676e8afa592e9a9dc52586d0bd7686f302695188350e3620e559ecae000c698292d7e81ec8bad50239c0c0c8863a85c00d286115c7de5139ccb1c889e2a5e6235bea499c04e5ec369e0e761e2683d5b8060036916866dea40d1860c5f6ce5980e8dab61aaa406b1fac146300847a089806d91be7394c54312dcb6bf0e218d88bab339298d8c69f6ee1ac1b24614784bd01c387dbfdfbb056e35a5fbd387a6a7f9ab5b2c24aab2732e4f25dec1d3f31fd03aa358c1a72587296086743bcef616d05fbb25292c2d3cd3a9da32b2db6a1700986338e5c280c660724497eb0f0751b42ff55f2a2fce5e6a93ef68d348e20f40386aaa3dde23a7936a74a47b632f8ac89bb7f32a3279d00022932f9c0ff32d707cc91c527a5c507cae8b4c904eac893879e9a09854f1688c7143fe688a35ef809bfc914f8a2dd6a54da70eaba6ee9440639458ab12234aab3e44ef953419b6c0460474681609cca8a841ba607c2ea91fd20976e421a2d340085680bbaac63350e02f48fd6a8c6799218416f698f13ba3e14ca3115aa96abb4b7e89f000115180d9584c1480b2616b9e7ec0d37c2eaf7eb0a2ba7e838de84281aa980bf67e60804f4955648d75dd6511642fd1ff772230bdb65f8e755f719789d1701c30360bf02f1a23132dc981fb0b897f2c93ed635311de94829f024a7e8060cc74714ca010ff8ae0413cf0f4a378bfd07323e318ad186f7bc25a833c5ed36d3209032d9170c1cc3a5f7953fc3e8edefbfa835258e322967ccb0f8241034fa6a36b93d3e5e0a7270c01c6dd4a3d576d464dc368c41e96ef686d31d846365e18b0822ee7afa084d4c276bf832f306d69fdefdc419dc4239b94019e8d108543f4668078050820ba13f469fdf41b088325fe90e1eedc2c03158fd8cc324feceb27aa745b7bca70eabb4cffe24e0b5fa2814e7f0b625eb0f5d4a005d7cd7528b9cb007af74917e0f20d1d9d2a1003962d3a6c9e33d1e36d15e81a26b8b540b35c944774a5109f7037e28452ade490c56e08c14e7d9f481ffb49f7146dff13b2e48533e54deb6040a8e710cce12434b5d11e4f7abdc7963528f42741b6b7a52bc1eaf81d0d6b833084eb864c30a01c60fbe36c4bb90410e0915c121386c735a7b59d5eabb350e0403b7489d204d5bfe8be85e75436566607c8ecd0bc8bc53cc41890d7b20b1db950982aee4933865a304aec8c81a83e49bf6a1330d0b0beffb133add63fcd9ecc400a2719348f1000cb9b296ed33ea306dc9fe1b5e639c689b9959640b70b47d9906198c4e3eba23cfb7f2fa45c9e5cd4d7b163aac12b139658795274d33a7f8e90281f78b40762225e68cc880f4434890df6c44020027325a8266ec379c984d8f09cf5319b419fc2bdbc518abe7740236853e691540a85717f061a218821f235e09936e427dcb532fcc9a9708c569587b7701652622ede9bb16efc9820e82cc2804d976a866ac237d4955f39e27c3f0cff435173e9fbd778237b64460e8d502dd013c97b8b863b888b40ec9dc946d2e58ffc8e554b0f4459c772962387d416c6405ecd6a9b167198aa5bf4e92075f00a93fc33905846d50182a5500253c9d93750d8d72ea9d2eef4b12a570ce1ae838dc902693efedb9c8deba1a24e4ff43f79906983b3b062563a6b359eb8ccca0eba1767eee2fb3a66642d8fd058d47c9b42301ce5d9ca6db48b8929e222b81b2faab5a428cc8b8f477e1e464ad62998173240a03795ae90f21c6dfd19bbc9676ff9bf07d97dedd957fb71b96c4eef3c2b3a50bed78ad13967b339f1565f682da373ed3496d82be68be00e59c81e495cb089501621e2e2675a2a2d2f335808ea4cdc236823cd6a3868de639d5869d18a34fb307d9ac341f8360e85fc130bc6b8755fe475c9ac6c88854d7f2a33298c2d9892b043d1d66008622f34065e4d837c2444356fadd2e26ece01a6259f9b6e0c4e1010033ffb401f4056724a07769565531a0a6cbd73f66bff3ebad1cd6431d33ddc90cd904696c52c71178dde83a27601fd186916d781a44f552c957ef234b11378005e293310e3194c0ae6e0dfb764085f9a2372da945e01b72bce7033238e15cf20f62c4c9134b9187de1c5a263f542f238328842b4e126972e6a3886385582d980306e07ea52c75618229ca83279865fe5e5a1d611f2efd285deb8480033ba8170fed74e9ead22c04d1412bfd610bd320369105bca72a00b4bedbadd16a50d58c07c84018e0da506ec11ef232079fd2f9ac8d62bf4d875f2dc8bfe62dad52c7a70bc7d6b6c7eb6160a6d069c3c596a33d5647a14b5d1461b41c550d74a9744da90b783030dd3c2d2f904261caa44b26064b223e071c99ea2b5439edc42521f1ed0d52c7beb8027334a223af2d5e2055c1c6b9eb43c75389ec246b82835677d6970e121646e85f7401e30bc65266ea2164eb6d8534ab3827a9360a8848344ea27c048aec4cdd1a1719ff4cf8075ba4f4c3cc522385e319157f444a00ea63d16a470ab0fa44153fa0d211371acfd5c28d85470ede784da43ab32e79e335176ecff101968e9e0d77e45f9efd4d54368c33773adf9f189a0d4b80a2dbff98b1f67b5404aaeb17d533d9e0957adaec0e8a3c8a34b168d437485c9478e7c4b19c51e6810c86497f36d454de4d95413c6d02efe3fe638c65b4c5204660f7366f2358ef7202b49feacf535dffd2090db55a1f3351825d94b528db11b4f681bfa7b3df42f80d4502f7e864021ba6707cadbee6746e0b2641ee0692098ab45e509b37d795550e0c3effffc44daa7227b6b708a3d628498cbfcfe13eb0549e098de5ac9ed9d10f01a3877be157ba860ed9001ad4a68b2cc035d2c2234810225c57ab774bb1f006fd438ba034cf055e278b86cf584109f85854ffba9de710b68c3f835de3ca1a0b9aed77ae64f4d752fcaf489036d5cedff31036a40f3b4f16304c6a31b4b0d80c77b7b5ba7f8656556a21a856538cfe251baf3a5dd820dfb2c1fb453f0caaec020e0dc0fadd1683b92eccaff087dc9fc6977b6f639ed2eed120e6ecdcf2880c050296865c9cd088ee84bd52917fdba4bddee173346df948cdc89c64a4b861d5084559666c6ff130422d0caf43db44c3427a4d5657d4226ec51158b690ec6cd50b6ff6a016dc880a687a10f20fcb2057317faac6506dee3f906b5fe32b8b416c0fde397c8750a61388c798b2641d09f0bfacf8a071bd0e32db64458464967fe101a3cb5bae64b48558e1bcb6d01517915de66c140f287ad79c39a957b20ba6f30fc36303c1cffe0f37606eea932732a7cce7d0c8a3e7f56cd254c429f8634f730d2652c9a6d4088ce17470bf650649691b980533e671301cf7a5ddf4dacd2c3c0bb972445be569528ac53f5bb3e0b426af8ae8fbc2c1b0ff626d7670085fc75a098a5c7d5cfead3ab28a57ef6ecd3aea2b66c79b6bee959288a9f6e3967a06010070e3433961e33ad162ea260dc5235cb51233e52ffb770dc922b361b47d80e6073c1c5cccb26c0a4e5d7af61ba1d6ea820cf3859595c3d994f16fecf1826eb90b23a191ab80ed8f6225282ca112f6518649916fcfef9254d542e30ef2d76b1800361d6fb6a9c559096314875aec90b1baec12d4bd8e8a5eafe2902ae146396404bb7523b81d55d59ea7cd196065b89ca9256f34c0878048bc7baa53763fb07b066f59bd9397a9ddafb4986fb5bdb5c6a68b52ee9d88412f506917a69d7a098303de15e1349801e828be2f3d46cfc51f4acc60d3a56ebf7690f6ec6e62b97be202922c4ee870906dec7a7d81a7575f62309d191979eaee2dd8868e338a17b18908ac26cef563b488c775efb792bcfe501898e4b1081f5d8168f43851ad7571800cbcb790963072d28accef73fd99da11a180a5e73c3b136e088dd3109a87e9170cfa181cb228d01588467846faf834fa8caecb758f39910d8e96c21432f2d61901560d6084e30073bb63008b25323457828048a351e39d23577ba982c29dc2c9084dd3de94dd6fd57d3713969b059398bf7ed6f3088688424e03bf0f5ce056110dbf84cb9eef5996e1a2a934a158c23a8bf009fa8ec1e6463ffd67db8628bd8b0e997f21300fc9aed4008581b1d67e2a5afe9742dc2e4179cdcb6df9701af3fb06a28007220cd9f8b83c811b86f8ee31b9f8ec9decfee25ec0cec47465978c8f0035a90e7e02cf8ad1ddcc3e41af3f90a78b0f5867a3ffb620aa412217dc56360642d86ccc9a0d98b0d9495f150086f5fc5f20dc09b734aa640faaf2f6b6247a0ef18ab7835892733f1716de94ebfda388a9390eef1f9338238a131aef374ae00df939552ef1075de5a600f917b0f13e69186a075629cee5a3ebd4da6081166908330a6288f1a53ce312275226f5aa382168f5ec6444ac8444048ce859df7e820b4f50d9ce8ef300012b227075e6e2a2cb818a3eec6c6d6a5dae8038ca3f9bf504267d9ce0c37e69498039124891027c11278fe22774524a0932ed77f99d8bac0afa523094a8d0ecbf6b3930ad084f7cbf5dfacaefab0e56c27a1becff63260208bc624befb99b5b5f3e63960941f370d70afd8d513552640978100cfc150b0601e584d598e632cd2fdc39ab91a0d5cd9cba480d6682eff3389a2d3e168eb76b0bcb2eadd8b48e43df853309d2ad2783c76e50e6e1710d48ff8446a7640073b700a4eeda435047a1fb5917137db4c21d7d2a46d4e6249dff862f0f8cce3a5e1d099cc25a374eb88386ed47152f1a6d67fd1be74d91a23693eb7aa6d5bbfcd8fb0ea587266278f698a6500c4c23fe8668d0827c05de246d2f0f59bb005a3933680d214d1bb3cb624b226c21e803ef2f69cd15bdea593ecd27a2ce550b1046eeb50d7f4e7fd5948dd78a109eac27a0ad76be9d25aa229dbc0469eeb57a170c9c6e0700d7414dac696c2e043877cafe52d3f3e396c64134b1dda19c85e8541a73540b45b068cb3644afebce94a24ab65888834476fae8d6830eb7416e09787a9b1000fa17e7d99b2a3ba5322bf100cba7285accb4076dbac9585908b044a925101c0dffe83e945403fdee3a8aa97f5acf6939e6f9893244e4459df32dab4ce705150e0fe137b81018cf05add94ff99d166b3dff07e9481bc7d36045985f5ea6a71d01e5dc8a06d65801c31352095ef736c453456700b51e5334f7697398ba1f9d450ae40892eefff78d676cc3b291aeba484d7c6f101747326401931a1fffd9adc00aa637ae784ad424e1b1a912f3a7267706d68370761f3fec379a2c4f1f2bcd3704db2d17db46ffb2bdfb82c0e6d6e742cb368b39a4e76f663d801004c31ecdec0da4eb79ceb526fe005ff8cbcb33a7cf3c8fbb6792fb5fa7d3878357221997d8034e126bfcd4884f8a08557637bfd2546b7cc55a3f76cc4b1f7ddeec632fef7c0a16c92bb4c42dd3cc93dd211f1e44e42ee309ab5f1be2b33872a3e46df8671a091cc70bd9db3be7248a4acc2c39193d448cc29d63be907a10041a25dc4a7dc508fcd71212c351039fb80200b7d031b0e098a3dfcb19627ef047ca56cc5ba21303a0279587c03a87cd1de30e44fb3d3baad883e7d5381d469669b19d38800f7e033e1e78dca43f7ef5b2e58a13cd9df531b79d6d8855eb711bdc52258a9ef81009c07df9bb9fc3a582bdf0609cf76853581dfbc7c068ac8e47b9be82e7cfaa050f44438fb534c08e84bc3052e130e10f555b85ad1e191f180042920f9e4a55cf0e2a6403c5eb62502835dd66bdbefea6892bc4758d6c505dfe60756d276493ce0c1a4304b4bf614ddb558a841ab7c08db5b4a6d721b0009b03adcec52e79b90f03c14caa0ce55af33efa8cbd9cea826a4df383b0d5fdcf0702a640e99b41a077015b84d7415d461e0c3f1be93b544f5195f775968221df61b9d686f14c35552b0e8a1beaf68827c429e578b5b73a3c5e2c5f0882cd3502e93b51d960eabcf73107f030c1e9dda3ab056dce89f86a5b246edc40392bbc1384368fb7da7a988ae50d3a63dc437e03e96727f43337775dccba1e38c70af957b94290fa2985231f4d065582a14a05614ad207b9defcabc16628cc04afd522a1d7d3d8321ae9c6311a0541bc7af99c98196fddfe1eaee74c3b1c9cb033b77fbfbda3342059439fe9e9029801c15743af9341d3e49e55c4bbbdd818b1b126763bb899cd1c78c62d7e220ddd1231220465d7ab33e66168338a9939ce17d415877437531b3424b201c0d4065828734aab0415dbcc08f019b55f5b82215bfb81077e38831dadf4441ae0c504394b7e89836939ff2d594880612f3dbb076be9970bfc037c88eeacfd25a8c003873383523633818340e4783327cb55ee182e056535816c4fb1dff9a42997e308d09bd6d61cf01e6523c92b59400c0adbe6d45b0f9fb7a13871e5779981e94b046d7e80484f5f1611479fc592033512ddf3d0469d104ac149b0968c3ca4849f04b2ee55599d73b29b911175b69e2acf20bdffaccd1ac62336a67755f107a4cf00b117924e05e5ad2ca073e9fa719003f6c0c325575d4b6b99c469284d1dbe54091d16dd8e654f02be709b8e6f58e9f90a58c5c53710ec4dc8dfc136abf38ef3033c1cbe049f2361fbc192a8a3467159b9a3403bc608592ffc1fbac9eea8148d043cce444bfa2716cdab74bbdf2e43918e20731ed1b6f2eea45676227552a69e0d5e32ae384626bea99ed6c775943850d9883730248c7fe65cc6e862e7093db9082168a910480bb246aa6c08e47ef9376eb9b6ea0b471a8da411eda068c79af5097d8928fd852770e8afc67936024f1eb489e610da2a816f5c4230082290104b0f023a2c810981c26d9576146d44ab5cffdc414a92e309a31a4cb78e2da6720a0e4eb3318e6f58b44716885449175d8fdb343ea1d96bc884505a4636d49214a00f3dab8c48730000c4037d77e15ecdf1153fddb97e3e14938b17858aff4b892b056d28fb3284f370b94fe9af7a319d333eb13196aa4055d529a855d9976048130c24203da087b5e91aa9df7290fc93892512cab93309e76020391ed56c0641df015c008a9dc2b95fc44f8cf0ac69be7c4603948f1cc4b5072d19cfaa966f8cf507bf9ba5604d6247d7b897d0f32a8fb035b7dfb5cf276e42fe0b7442d1f092a1049a541fdb1761bb9f54662e80b786c027da502c39146bfc5287b5abae1721b204b2b9af8a8c178937d6155fc88faf7f1404e91cb7e4d295d8a8287c21383366058f0306529dde88ab8a8c9b6ca3647e0a4539c9f479287bbea67c29fc352f5b0097fd382d4e0d68d10d85eab181c7a1f43dfb5709f5bae0adf30b245fcf25f407086822d2ec8cea0e72ed4fc1dd1615e5dd2b79b28d0e9a97914f24ddf85d930c3fd0335954ed4c83bcc9662297b6c32dbec08fc303ca208eea1790252c838d0bd3fe76c6a91165051a39d775c01d65c2d926bf6f250f6abaceff71ed884c0304fe05a755cc465b3bfb04afd6eecf0966db8cacd64953969ba2f688f5858a32045692d0b393cc68de8f9e014c9f8cfc82d18cdce0e2184345a4b3eb9f0992d80966b9551d5a8aa0866beab77120d7b18c3b8eb943f9a5bf79a5441983bcc3250d9a095ce348b263c67488719795c6a0261f0296d85b5bf9374040274da4ae320a5d50ad8f96ca0a5f644f837d55fc1f11ed451576ed2feb697814c5e72f48340d813e48912c75000b35cd14a0459e90b63ba21e2a7fc4f3cf69078931ecad0a0ae5fb72e7f4c4453ef29ae2133ac7c0aad70afad5707b28e657a76bd8daacad08f58c4382a374887182e8e167ba2c09a945ed23c3938a4e806026d53b01186508ebb44636a3a81c6d88974154bcb981544893275401958de595867d81b13f730ddc9446e1f18ece26e0ea789caa8e9725a4aaa277dee3be0d6d3cbd65bbef7c06c973dfaca004dadb831abca4aa30e7d318fd1dc05690242f880aa9949bceeb0d5bf0620ccefb0e50f709f99874abd55deb8432ecfbb91426d4eebb959ced4609ad0681d81c230df81f5719e8086acb122f1cacc1a2c679a2d27aac5acb26a909c986a83890a49b1bbbeb67f1aeb77208b00a201d70ecd60f3d5e51f87173410ab3442e056fb2845748e308daf5dd7b60aa6ea973784c08913d5e970f32e3160f65947585d6abc7034ebe1baa675487c236e718a0e4f92f586a7d415b46c52e05f46be003058d4fa734836c07b660024ddb827e0d81b7f836bb3deec6015eaa004d4808475ee9b1835c172801a0190cfe4b8ef3279e4ac3dca3a95f5b13884006c52169cc390a884c45c7fe6666ed9a068c90856c94ffc8cbac0530ef5e672e062682d76d6d6335a0abecfba8a82ba69b7b5a26a2816b8673967d8d56fa5b110c5a0d59a5981273aec80b7d020e55a448a7b7cee91dee0e78933cc6ee1f41090f8caa25915905e576aeaa3e8f2ea92cd989ec2e18787996f772e119703d80b40943abf8d7135ccde0324292c3b576014ed5289c6e3c7b5a77e8711c4433a3aa06e9f5719f2a7e09631a05a4ff817db2d0546c4ae6217810dda47e5f6309928405ea9ee251f0a32f4510db2329585ff35810babf02f3c7dfb6fc22d68f5540b00817c00512526b92515e0600797a57c38998142709815c46e36315a77e6e689d0e3b8ac6d2bbb06f1a0a4a99c45dd4033b7ed8d04fa3b1e84f66ba63c73ba6a40e549b5384204930e89dcb0bd0f41a3ae7f937076fca2050a65b4e4b4319625c0cadb73a9f83389bebd8ebd89134c027cc197a55c29224de9e07a284f539fa1705db8da16b637f7264f3acfc0bf4005bdbf492b970d619ec7155492f8f904d2f05c0848a739fec2ca92543777cdc1dddd7d5b7d870f0ee8c2e1cdcfec75abdcd01664ee8f4b84679550e782481c4a897e9ef91ce092cc93318fa74e2c0a3104806 -generate_ring_signature 9dd907994196494b8125e30882384a88166cc89ab3d0c91f6fde9e8ecefb5e01 90dbda83e0a10b0070bde90aec62e4dc65910a922c21ce75f37c425cba693876 1 b8780402a1279c031fd5dd4cb89f48bfd15b5a8dfc140d9fe0eae67a51d18df5 5631b1eed32e0c03cc92739379fefaffc2389bc6059c623a2df77b13f41e3209 0 e3e17420d786d08aafb0db583d180794310b6ab26952ccd3442358fb72ad9f097a93adc1ae1e707e100277e5d049f0907a1210cc37a692c0da852c7b684b3309 -generate_ring_signature dc57ae672f6691bb5adeef2f47e8eb0cf0c5af0d06d479a1b1294e5b3d41c06d 3025fa55a862d8127dd4c6c3d757a2e555e7d50356c3028f6a0027aa786c751b 2 e6ca090d9d3cad0b144faed63a19b9f263b793259f0a84d3465d9567893893f1 ae20f4f7f9038da72a39b4618b1a9032d77bf1787ae00632e19b50051db8de04 0c51d9cdc712d60c0fa724e226ba82e337451ff47b69da5acf95fd41db1d8c09 0 3d16afdf2addec448787b2371e0c46471b063ecf805ad36f78b8ccf0dc2d3a086f3cf9bbc1f2377d030fcd11504a40d992e266ec843e07fe6ab201670128c80243ece5700864c385d093520a063053219629bcfffee6c2d16f1aca8f5cc35c01edbf3e94a71b4a4315653b12d9afbeef726beabf0f29b6b76d13217438323500 -generate_ring_signature 2e3eccf3241ad759efd0759054ad22f235e33e882f8b3a0b8dcc2bad44cc13b0 46083a5c582071ccf8cd1c9f33bddd7b02328a1d68cef46e82f41baa171a2fd2 2 ea16dfeebbb925edc360cc2034ed4a7c45dbd9a2ac5c2699cea5d822a31b6187 64c01e0efec188fdb60bbf359b073f613efa84fb51d764cc716938a6e3cbbac7 1742543972b8075a2df32275289d6be641db11e78bd88f72215a5d2376203901 1 36c7583ed471c8612970da5033e94d7e40b8ea94504f48b0d552f4e9548efe08b76eaeab49c551f6e4d318e1888bc33cdbe394d0214c41b6f2a9373544f39e0f7b04e0b55e33ea864b12bb0a7897f04a2d224f19edb2c9a743305a418918680a3156c3b8028a0fb3aae2accec272932413e75b5c6e19a42629e3f44b76b61407 -generate_ring_signature 5324c8a8ad37bb76e86edb6127499fcbf161e173ec4cba0d426e25a26aa7c2e5 a3d91b89186b32fb14d79601878d9b5cf0289b0ac68d19b474c86f4288c8422f 1 ab633b5c1c80ad6bf183d24f4f37eaba1c4776e8383047290503e920ccf3ef59 a7afdd093c3a4e7f64896c7fee5b6db5f6a57a6f14c04f48df5c4a4a1219cb06 0 765463248c1f9976535b6160effc3d8ac895d41cf27d38e8da4718085b103d04be968d56d80159572c67703c87eb3282569f3c52a533523694a03e2239f71605 -generate_ring_signature 9ac64eb93443ad9fbc9abc23cd255253d3f61fbebe818fb9436f2f12397ab717 0d24970317c011442316da2d75ce0bb19dbe3c33ce6efe6495dd185ff0b77a26 3 29316431df8bca23ff0440601e07bac7ca40edc4fb0f948d1cb5a4e62153a1b1 3af36e933fd1d37481499f583155fedbec8bd3e131d278a28b209d5d6938ed7f 5ade6caeaaf734a3f6435719f88871b4094ece978b37ddf6b0554aba023ad626 94c2c27e71b84624c2f55761fd6b36a2f0f835a67fd12bf7b892dfae86c03d0f 2 c03cc94609c87875401da849432e482449ae53ebb44940966d4f65793069dc027fa23a5ebb603a1729a86e964e5febf65677d38e7de8ef3947c156608101dd0b867f4a9fd07e535a152f6db6ee3488bdf6245444c52d7682b146b3da79aef2049591a5d7eb40194f84d019e18dd626d367d7890f59b8aa7e2d4d8ff4fe284d004e6c3a86e85b41ceb4393e2eb45242ed83b0ba6a08b2650bd4f851b1d3cb5a0900b00ecada1ca3cd2c0b8882e7c8469823b9c2aa327c0fbb485fd657ff4c3508 -generate_ring_signature 733c77641afbcb27266517b8f24262be24609a4729609ac2bec71b4aa86f67cd c2474848d390a19e55e10d80834f6426d2421d3c0d9348e81312b9c644072e79 2 5eadf01c62bb7ce2e1cdf66e49982fccd9965769d41846e1526cb9eaf2d15395 ba67c3a77e88593f8ecf2c137cb0fac4ce7d2259010854396991fcbefde294f5 c3c9808a7c62b5542b5c054bc98c5f140820efb26aa6adc2f48f4d0550536b08 0 bc9a215c32f1cb318427a4fca6e12d2e24726293b5a98a608fd9609fdccbad06e8f130069932a32fa2fb4e0fcde31677a77e617c14a5bb983ca2af358f5dff061199dee6b9c040c9de8e48bbdc5d9b5fe703d401d75ab92702f83fd3b7c54c0c4a7f5388c1dcef3e9605c9cc0166fa830695a6304383e3e2a17b233186e5680a -generate_ring_signature 1792c680135fb955b884861240d9c55b6ceae3a130059fe1b3ed2e9ad5a9de97 ba66d0c9bddf46eca50a3205269fd2c427e40a9ca780d09d68b953511561b396 5 bbbc21787e57aaf4928a2de53ad1d1c7527586f1f8337b1f3f8d4d37fecb35ff b00f959de68120641ae043cc9640ea2a5fa040ee950858c1e9f16b427128a212 c5d5354bfad3251768658323c092fceb2918d8a700557c79ea5fa638c18fe516 e731fbb5657553ddf378c3d60e0b12b1a5e375ba1154c6b79ad6d276e68f75ea 1936e101b0e7612be29fadb4d8a17e3dc20c44a7f065fcccb1f372bd8e3b0c19 747205001f75da90ded4f907a518903090940432f8959d8ddf25413fa3381304 0 be388bdf42211c52634000c68ffc522040c889339f3f9fc713fff18849a6e50183e72bdeac7dc201ce9240d31e046cbd3f211253723e2f8ac2d5436f8b700d05ab9371f126709020928a85fe94102f8508b1641922f5a5b8657bd2322fb7eb04b98cff5d4e0129f5b3e66213a42f42f50e45e414018ed5e2005722e7222f180fa2d4e4eceb08b7516322209a8fa61a533e7c327a542ca7ddd0eb0cc51b65dd07b468e2ec7be89f8d61d3216b4d533b69ab8bc7308d224708f7ec713ed89de90a5cbc132c5dfa27d4d253ef77afe3d2935284840d019ff78b6172eea4a8ca5909435e89e3fc34b6a35f9f017e2c91d0405fbe77b99df628be43cebf79361d9909469ddcf9e6729df07da7024731f0d6d1fc35ba29b0fc6e265cf8119801d6c90d64f938eec1fffb3b11a7146f979944170da4223149936e20b2e89eacfeb71303 -generate_ring_signature d432ceeb293d5ea12e583f0e6a858000ee675b292ace24d2ad53d641988ff416 03776cc64fcfaf6b9a23a85f9577bbdd429b653f732b43a0e8f43d67efa63c87 4 200d574e962228bc5b74f2785ca3aed1662541f7300c818a4907279fb763f5f9 4c89d8fa1e9ce61a0f8ccb300b598c795baa7dc1aed979f22fd19958412c6fc8 287c5c314c26c80e50266bfeba4676980391addedde9789756dfaf37f5d5c120 0c294071658273a2f36d3bf44863fc65c8732d46e5f907c4b68ec8aa0ede4643 4ccc770ed00b9eed0888d3d6346bf28324e98418aff1d7903daf2ed68548090c 3 29dc937dcba837e605950f9e441cf7dacecd515f8a3df4920d57559910dffd059f47828358ebb5cf25a78ea3af7248fc6f615641a23252ddeffef9f3eb96ea031f94ec7883d39ea6edcb6d63afd8efed2d39e17ac655b81ff63209cc456b3603fa4727205e613fe4f2e09761bd0e2e7fec4acca582a17ef208f250556ea930081adf925e36f15f031ed1a91ff880b33077d7dfb69d0a0dde81ff3bf8b265df087d8d8d3f76a993480a00dfa24d3baf594df68a136cb341c4cfb5fe057b413f0940b57072f697aafe698ed6651806a452f58a4283fa3d264ee9cf049533bbbf099312c28fc2034fc6d4a162346fa3e00c33391667c46e50a6229928d68433210d -generate_ring_signature 5de707be4d1ceba4a9991eda597450d72c752ffbf85f714725b7a53086c2d611 ad59d7abf4332dbb03984884318194af02d41844ade4e9f91dccc65be8467abb 1 a2103cde970a000902d7bcd98045d5a688cea83d1a0cdd1bdbca4c087249f11f 6eee07f5e11d2601d7175b90135c8328c797d5f4952a125a96f934aa82aeb006 0 4181bd761eaded0ebbc3b8e7ce7fc2f6fcdf842afe478bcfdf96bdcbe464b303b9d5cf86fa9e6f75c99501fc3e327d94bc56d94dd1d16081859146c94696960e -generate_ring_signature 1516afdad27c8277ff77ebe9cc6b9889061ab85d504c7678b51d1b453e656f4a 77eeb7c8666282fa03e473416d2b495c62e9b8423bd30aa054cdac22f0173b75 4 294897bcd480edc6eb3f302bec49fa75aa38de4ea0a5b3ce458e284477a4df2f d7b3729fc615104085544ea277352df10f43e24e8b7f47469893f34df7197e95 9e4ab024bd6a82f3544a941c0dd88b2985601df873cfb6c82c669562d0ccb2c1 768ca9255bad7ce9db858bf51271f6321620424e7e24e5422395b10e3fe48a04 7328152575f971140847e181482617d1220f0cd933535cbd8a5492e8c857030d 2 1e21afbe9950b0bc98ee57693e5c62ecc2ad3e8154491a6f1c567ed277461201a1dc25da476c5e6952221848af24318e94b6297c80a82a657824539de6814b0efab54c65e1111b735cfee5a90b76e429dc84206547a3a73d42ae322f39bf3d0529a8aae101f990028b54615d20b957556feec904aac2aa2bd2cdcc7064be2a09ed5a2b89e4b5743dd24d6ed4c5624b3dda88db6922e625473fe751ea9087580c046a3c258371e23f7e5a53f5dbcfb9ffe6db119ab3b0377b1e21c78fcf737904c54aefb383d4036d11f800c03404a0f0196929bdcc009b3ed3b05297a99c970ebf9322f5371c9f6009688ba1600d1789fea18c18a197e1a850a4c9f3df7a7a04 -generate_ring_signature fba004537aa4d6db41185d8c4b12b3256048480c65d99eac669d156968585162 ede0caa2b77adaa6a0ff9cb917a79b644d2587b7d3821e5632955403ba4c9d68 8 62c27334f32b1587534ea7810f3356eec492bbacd7c6bfd2cc91b084dcc38643 c6dcdc2bfcfd81b385864f46dad8d1a1f608eb4d5615debaa651bad8eeab6588 806806bbbe254061c70ae2ab54ccb2c10886f792d9b3d0569fa9753e4c44562c 9e6598f7e3b174857ae16fea6948156db8093c3f991bff0da5a2d3c8f302c9cf b4df03f1889ac5a3b21134d6be2deb378a4c228af70a3317218fb34acb3ec86b 00262c572aceb403da34678e64368766aa091be815bd42bef5db81946488b366 c77fd924daaab02e4f95d653c8689ef3d3b5c71fc70825483bf36eab26280ffb 9af7b0c4033b7f3a8de1ce74f80b1ec03fcc3b92f2212ab8ee5a9c68869b5995 404d2882583e961947d1903d8bf28d3ee2fd467f01ccefac2d2ac3431ae30506 7 bd7910a0bc2f158cebdafb9ae577daadab7160d5a3a4bed0f63064308cb8e802f59be263479ec1b4a7cc3514dcda06954513737b7d6afd30544cfcc548ec7d08a32249e0411f75234533459be0534b730f82ef24cf49b54b8d0e67358d1c2c0a050a87e88e745b0aef72da495d72afb76397b15bd6608002802fad039657c70baca8dfeb3ae93709762eedad733ac13521d5da1aac34833b2d4ff42a10be6b0e514f401001f1c4319bf29f40f117c66b0c5788fa2de6a9830ed970c8719f23062ec54f1ab3d47ebfa50cfc9895669390d1c09cb553e2babf43c3db252c929a0fcfc2a1c406c2a8574b9a49c728e4701934a91e59d3ea050d0c6a34c785d6e30c695bf977007100a2013474c7e46366171697b7bbd3e049917f86a2fa59888e01148d356627024a4ceb657bd5562f87492845f234f09db51f19c5e67d4ba61f0179a23e12fdb34b15a16d39b9f9de4e4a03353a9e9480a782cb984cfb3d815e031d8b648a8d2dd103f9db0b321d2d43a8d1fe84f98a1cd7fd36e74381c6e38e0717331f4379c732c010d0deb02cc63893f973ab5cca9a2f01c97a3dd76764200ff3a5f78a3910710c4e34c41230fcfa77b0683f164122baf65a43ce3a5cc58d0af6ba33a62ffbdc5b9a11293d46cb43b4fea07d0dd19414f6eb1cf1ba6dcebb0d51c939753e8f67b08f84275340ad941c54150b6751e3705e4c85dd774af56f07 -generate_ring_signature 3ae3d9ef2c81e08c3b85166fd60d74ae81f1be0abcac3690480a1ad0186e68f5 27e97d10d75e9108d8c4e89e43ba75a843f4b250b3873aabf158a0379f55986f 7 1ef624898a6fb35a3396c517f90afe615d97e1beb426a8bd63de31e3f4725ff9 db1b81edd507d1773035e62c458b4597707bd5f7abe52f6503c7e94372699daf 2d0b80fa6db1598fa34207dfdbdded71c01c7a6e0c48a9895d103ea21f88a765 b75766e4545583a57ba86801b55b63f7f7c875f6ccf2e567048d191230901a02 6c633936bec4bc1d110cd8c1ef9ad728d43871ed5c25496493cd5f853e871b42 37426776747c40a3e60b35730a93d3aa3753431e7d8dbb2b61a5aad33ad8f6e1 79ecff41ce5665dcbadad024b05d9e26f9e6c7b9a7d10d6d608ff0fd4364895d dff0e02f69750454db98ea1c0486d8a27a105362ba9b8679b0bf2725967dae07 2 deccb28ff9032a67a1aac0c4c0e3e0b4bc56a86fd342d36024ad8c72398c580acc594cfd86f3828b70dbe6a20269617537b43115d0681488847674610d2a23007ee786cc4a1d833d446047fcd6aa100030158d60f1e35eb6e3cb64acd7756e0411a2e376a1d1c6d055885bea1717aad45dbd3c25340833187d6710f04261a502c4d9d4757bb719bd0b9ffbd39cc2c8136126333fe1388b35e60f5d328c3b8a0441d4324b7d98cfe53a3ff9c34cebab953915a0f086cfaaad1c69367c60a02a08a200f94ec13a6a4c5fe059d682cc61f8606cbc370ad52060b3297e664adec70160f308b42d4ab41c82c26dad8f57686251cbcd5fb4b4b64c631c857e9ff9710599dee2fa471b50ba8981dfcc5c270a67df5acc2dedf010318127ae7bd358550e0570b3ea79b85615c44edb393d3191216f6ae6bc6de1a03fdd00a097cf24950bd88ae2ca1bba92ee207186040eb76ec6c6da2f877e7081b0e48cc661096bc003a3f4778e2f20e34ee38093f9ddf03f7c1589549c606606f01e912fd2705e2d0882583a8c640a3daf385ac7995b7d0f6e790af5e16f7aec2564a2516d8dc2700fa69930edc68cc4bfe8b15a94016fabc986e5535d4846faa0f6b6fd6f89f5f700 -generate_ring_signature 5ee5ba303fba3f86f72178eaf964147c0f4728181baf66d0d69886ea57304f1d b252f1d78bbe4179682604610e30821f9a161cd81ce886689a1aa9199ce14c19 1 cf0e634f021ae9a7741780f42dccdb7ffb0c5dd00e99b208872e604210ad5926 ed4ecd59c016635ae665994d6a1698ba6b39989fc31bee19aa099bca24bca605 0 001a778568dd97e3f6e3d1f5d073a5f174fdd6ded27b267dc0b1630816dc4d043164193e36db8e80e5406841afe1b9ed379f13779c6faa8ed71a8d18e7d3800c -generate_ring_signature a168acafc3f3634fb3afb2fa4c85302807788e0b1d6db633457c343b83d4ba02 2ed829909d69d8bff8cb268783d3e844ad3539ff90fc6bed846c15406c237801 3 8eb8412c40ec4e21cd9a4fbd2d7298232370a4ba83ac5b0d1e316888b817b511 badaf9725b5d536b6f3a66ec3ef108a4b3115025df7f43e7ab249f3f58059f6a e30cb9b423f01c24bdc64c739a31419cada83692fe0c4d64fcef6bcb1776d592 7e76841378d75e2606cf68cbf75f3f3db2cde74b2faa01a701a3c432ea191c0b 0 7a99869b5c979655b5263ccdb4c63a26e332fcf5822c5905a261c7509556d30e99ec48c2e5244fbfb3c8aea94886aaaa964105559b4e02d60bba7f8eca137f0b2e82da4609f47eb803f442a3a6375c9e0b6797f0b9b858e4c1f46f3ab491620d115b42ab21664127e12dce3ce6da36f7bcd00b137500a9c36aee81dc4449570da5918134b6462c76ba245711478f74b3c5d2b5419b4c7bd795b4cb86861a8b09be7ae7af1b12d704d53055587eb26a20d9fd0f10e69144a746e4e44086ab0704 -generate_ring_signature 76c197a029a4979d0388b7ec7e68ee11ccbc66a8c504502adb011f718f46ea51 b1490f09967c786694043061f7b498ddb9b064091bc8437ce133d71fabac2f6c 31 72b83a2fcfa3b21c53d07c617a198a000d5c4446bc05f142f8d42e94938d3c89 bda3e253729f9b6b189e923f6a5cb3b36163b06e8e2e5c1c6d685cd272ca0ed4 a706710f83c922e3aab795c56883186dfa8dda3408ee314f04816e9b8fb92f90 5c0813cb343b1cb1825ea4378014a6953f5884a8250d5766b23a9af5b43d2dfe 506171faaf0ee551b7c2dad4b1a4edac2239be4e7fe76479cfaac59551deb880 9e58cd307a14f6482f0c00d9342fc0b08776deb879c567b53c9a5ecd58d96364 aaeb56ebb9049870bbf2c68ed50c74c74f738c282da33a5897213c7e305826b1 d0e9a71c4f7b38d6e32f455107a814d6ccab122a3d027f320ca36f072f58caf0 a7672d1d2178777608f058280cbe24887cf1c80d54e4d3ce8c6572600887238b e550d5bef941cea97a3ca5587654794b2cc95e4ace85043435632d6fc9e6c239 08eb2a4f3d70a4e70bfb340724fe58e71cf75de3952c474abd8270a1a4145c67 4c69a16fa040d1904535fc5addfa4ea00c6478c97fcf349094fa4cf540fba173 844f1c9064fd2337d653c08109662a533ca1ba3adf29f71ad52fec1d581df0d6 1f38c8da9e1841686caccb994796b0d462db73db8bd5b023f47438da226212d5 b0a333c00453586b2585617916edfb5d7c1e0e41a06638e8131960b9058e0486 3e1ef31dbed8e4ea54aa290b70cbeb6674f2edd2f1e187634e7489fa52bc1d68 cd5dacb09ceae7dbfd47d22450197370322d2e34c5061ff0f886147a4cb3b444 90b87ee3103b7ca76cb968e6781417438fa9603507b72d5bb1abf1157576bcfb a8977f55e6aff38511520a9f07959dfed92a336e0aefcc6d10d21bc9937ce18f 5efc3ee01e44a3a68febc8ab9dbde69bc47f943de706c9e1309718b18b66d7fc cf1fa8638d4113d43b9a9795c37891dc10d1f6bd71919b014529f59cf2bc0d6b f03eb4af7517879ea5d377e49ddaaf2533e4a1e61935ea0d857fdd1387ebc899 763c9a6765b4b5ac7f6e53789392a1ffb43a0bc850707018f6419ee7fe8c7d0b 8a524e3d21eaa69a18ec74a26dc2d2c6f93689abd3fa4ec469a9effa1cd4bc89 8b6581e491580aead5297d1415c29bd12e15c79ed2d0bc2a25246b97a8337604 7540c5547acdd0df7dea03b49e2cc77d255385817bf3cf6ffce2caebff519074 45dd05c9ed02f168c67efadfbffd2a95f0f1cc84f4292826083bd10dcab56b14 bbe9ed0564e875b45735b0fcbb7e52ea810e54fc1f8fc7e1c704a16183f48d12 0d8c787d3134f90078bd47ec104b03ad4fac9bc6d61673c780fb445bed6c6da3 83cac99ef2be476a29ed5a3af28a348574576a086eb5ce2afb477f5ce5aac752 d5d7bcd8f0c14dcf5f621aa4f9dc1670efbe7b82dae76b43698fc21a2c05b2cf 019a7f813771d3f579ed6e3bf4722ae6ec129e1efc8854fcdff5c992a8e9710a 14 a6e35db50defd44043da303dce83cbd5a0f4ff3c05a17bbf1bd5c9bdfc191104adf136de1b8662cfd4e8570c738e86632f26ff93d63863283b89722439f86f0376d5f5c9d9bf4a97e7acdad3ee41cbc69dd76c7e75cdd166e181c1ab58ab9e096fe849972d98bf267a00c7e6eb86750323e935fb28d99e1b7a36650ea89f3100f51636a4de174fc899480cc8615637d34845e99ddeffcd03262383fdf35c6302c07eac1361a586e306a911a0bdc8ca70eb0fbb599c462d6f6a4de41751c98d0afa3acf3a707acde0c669fc91d4465af29c41eb3ea365f6bfc653019d91797b044e186d097cce7ebd06c4721c06d8d5a20828eeb11ba707417de1d328ddf36a031fb1de95f29dbce1d377dd7e5c8f1e2e3ca3136209e624b588effa2997856904f4e1937397b743050215c000b227d110bfe5263e96830aee34dc321b58b49107400a45ef4344c27aa292170c52cd93a11b9ca7325094d17a414fe00080676e06097625b0d262a2142d29e9da764eb52c7d5b78825815e02e070872ca62b5020048b1221dd9c23d7bee7af6083a267cc0c200b220c8d959ff32e4100421a1ee0b787c29f0422d7c1bf657c777d4240c811982cd1ee38d3fb3cdf6cae4fd6225073d45caaad9ff3975a5e561c728a2987f3763ec609a224079114b61661044670c1c9cda2f3a9320e833db5beba7d82ea79a3ab41fc14596280244c5bb75b63d038f2bd7d605f0be1e8a7823d17b2a4451c2073d617026848f6efcfd8643ee7d0c427f8d1877767f2013d87a4dcb9d8ce9a8ed1fa0a39d400abd2f25c876cff10432781675d3e1854509e4f02f05f54c5aa0004726802b4b6203a5a45002c299033b1ed32c6ffc69c87192d4b15c02a7ed7900f697a6c0d9afc0cd4fda8cf1e5018e01fcb365827eee1c9bcfdba067587131e395338be94ff70a5e1af23c41300da5cbe9ef39047fa4f15caa7b8a48937790faa1d9ecc003c592335e11b753e00c85799bb114b2d77586f32f085a202b31d1820794470c7086745cc047b028d90e14a0a74df45f4d325443841b1418e58e98edec29512a6b365dbf26f4b790260bcd51db03ae50601b12221043f02a374015cd69d7255976e4fa164b83f980740f368be694f43183dc2ff469e6cccb08f7abfac8b02b3addccf6a7383151fc750af1cb3591e95b12e58db370f245d60038df752bbd70f65139ae11ba9d4a6fc802242d15c70d96c5b307cf673c67a53aa3b885b36356fc55d5301f6a9a66deb20e2bb0d0d66da832717b8ef4d30fd36984ea881739ccf243d1e0bf84ef8835790dbc92a2e736ac6c687caffa57787d7e21842f5100e1ed9aad7588a83cff0788082982239837eff586ca787a9716b35112f0e8a1085c0a9c8f39c998472c69040f915174193bf367ddc9c5ba00df82dd92d23076df3889c6c1505083bf8fcb700b4f24baf11a07a194a1ac2b0d6557c075ba82fa4298e19d07972910fcd6f595066e5b499b5c032ebd316150b8feac64837d47d6cb48f1e62ca369de4878fe850cf46cd3ae66a658c9c4ca7286cd1db79e854679e9c4959154bb642886a8cc23062a62d11b0458974feb9ad40df7e9147f2d4cb984082d64017c0d826d7d57d2058bad8bcae5b0ade7f4d2cee051a97e04f5e9f8272a3cdd068141ade0c2816400865707f4bcb1db7dd7cd89e856b6a10005a020e514024b55805da63c833c1d0a32d3ecbc38cb9851db07f183046a8784fa9b6700798fa293d942879748d3a004035a93080dcb8b93c9e656434ce3b759615d9ef6375c9ef65da4e469a3e13d0be93ce82a99940245640b8dd1343adc3c902d13550d0009076610f9fd5ae2a20ff6cb10576ec43074cca73e39a108eb8be2b771df9478c1bef13e018bed8a350e53a3eb816116f57123414f791a170b83298f1f60dca8fa84d8c1083d200a910bfc8131cb692b924a41ead51d512bd64dc7f925a7cdc5d789b4ca860e17a618087018bb410997e4cecec255d1fb709b9fab7598b02f56ddcdb603e170e054f10e43d5455e673cd080a167344cf5896bec713f814b2de7839b69acb53e5e35c10b77200e508b9fdaacab4ee81b0ac42491d3b48179b17f4034a94ec80505a2ff00c3249e6bb42c067949a4ed48b06842415449512775e53c07f156793d4dc0ab004fd86e64fa97c8353b696c6a81b294ae66e0336a8aee21d301a1c2cb19ee920cc58a234e65fc8ba21605d397278a5960e3f86693d6283127e3ffed187bfbbc073b9913ab6a4aacfef80ff3bca9d6de3299f6783db8146047338d113159b81f06bd62fa90a77f4dee7bca61ebb3ba6e963cd9c5c64c938b2a0ff56bcfadbca90215a310ac4ea3f2d37ce487acdbf5a70c0341d257bf30d5b75b30cc033c07570debe16ec26eb5539dacf6a06e71b609cac5bb41bee606a7dcfe26ec973c412701b90abf86a434ce93f80f14c1fb739b645413c4146252a1a6c965416ca6a4b7054e1c3ed057106e6f41901cbbf8759fa02495797692e744c2256b77322ef12d0345c9b32693250b5fe75a4d25b82a04e3c7349356c998e822fe30e92b2367d408f3643e2617c6f022acdc349fda1003d1d904a1d611cf47578a02fe55db4b09039497d45d503eab3f736b8de10c744e469bf8a205e281f82168531563542aa9047a5cafe4b5ff237877488e9bfbb29ad53764eb80264d92966f305cf515842101343236e743efda7022cfada2ae9ce027b9d07c7edb6e0f4fe09b4338a111db0366e2a32b0f7c3e240eadd98b09c85d706388094a8cf1434fee740843e257cb03 -generate_ring_signature 98a0d34ecad7f93a524bdd5bbf05df6aabf1edc86e6dfd555409eb9f12b970b6 9472ff1d85dc16f590c6b034169850cd9a7c61aa49991a2abdaf9924eefc483a 17 808f7fe95ba368b13e3b5f5bf8375263e293056dd4c0b946b15b6811047a07fb 8b7a69805ccfcea3c45182def8cc422c1f23959e156bfb4b94c5fa3392589454 d56fa8d5afe511e42e28e98f7e24aaed8fd49280766b40781ac01b14e402a94f f9e92df33c3a7deaef73480de48f287a96692bb05f964f331e86a7314ac3cefa 44bd6fe3e84c4b5e931d1751bdb85138dbc2497dd25ee0acb2340adabcc3d2ac 5e0e3b4e8610b3ad968c0776fe185d5821b440b1044c3cad601377b92792dfc8 701052782deb012f529a3fe3636fc4e6d4f3c5319c5727db36b7a76a634b983d f37e7aeda09d6da3b5f9d06dab4c809a9a70c58c9cddbfc81178cdb90be5d511 ed952f75fc7fbe0c454228ba22ee45bc4d54438d88e5544cd74d33c5e851e80d 60efc3617bdbc7957d8511526338e57e34f9a8502a8911684d24de2f76a87ce6 17323e276a428b2b238a5c03460a881ecae3ea9622f35add96322ef1b42c1ee7 9e51a49147a1029412502883ee90d0b683925d611e354913af570c8be1549f3a 0ea8252e6e1faf7fbbc70ed8040f9c57c87bb42876ffd8564032776d81dd5dea a7924de7fe171159aa57a7d22a5647f2e968396e6a4e424afbb070b98d0b12f4 f985bff093c1fd67d92b225d00bc4ae37ca856075940461b790c17c1a73debe4 247c72d2e0916bdfdaab6abd05ef5a877b7f175bcfecb8a499b59c6577b93e43 12ec03a07f4f01fcece9465d8967580241577df2d6da53db9a23f5257a62ea6f 4b9071d06ca415811ebf1038c1bad7d7a2338f82b500a3a61c0312d48b09040c 13 859c1e1c5cbda894530f44e8ded45304fa80f98800fdd4d428af310f674c510b466d49d63c20a0e17cf5b0f9064b01e366fe8200a7dc9ccb8d0b8f78a168b409699898a81b8754dc6e21d0b8d3ac6480a75e36928468f66332e1e4f816e89c02e177b57ec9a0a8e5162d25c28579c79cfd0bf52cc100d75d5d2513344cb2af0dd10a614ffd7682bfd04e879696dcd667764045a30b2f0be6b4ea82dca29f5e0c1a7f27c3ceaf4d095f7c52ad678002f3cd1064f2571255166115e0bf92cce90139e446e1467a363e81f8246e019eb0e0d8bd4f7eb08e8fa06e14bff30fb5f20b2cffc01b01cabe03605404835509413100dd34d4190ac0482879091aedc89e08d1e80e76ba33679093aa6dcf1520da84592ce7555ccf219ab3e169f58945d60d0f9503d5d048419bb9d45e0ceaf5a7580e6c5375cd938210be5b509e5522d207b620bfc3729eeffaa1568695feb283fcb0cd822eaa8b22fb1a0b59178d2fc306571263f570a357ed0aa7d2e2cc2f1983ecaaff08323ea18daf7e850f2a26ac08f5768a6249e31ebb16c745dd6a2ec86f3a54aef8dc4acc63fc9b01f7a6af6b05d0b25cd80c41d4e7c012e335c93f5de6683d86eecebc8cb662c1b79f724b380cb55dd140afc90f4b6e06679defd00098ed175ad20bc15b8295c076479fe09c0a3ec14584145f2ce63210f0447890e01642e738626a19beeb6dacd55ca322d20e4c9fdfd9595b4683e4e3e3a602911d629f7a119b201f131916475532f7839a01ba69e15f4ff7a681152175ec1ebfbbd40e2d8ce5f27aed2274608ba0e63b8b05530c9c1b048587436bb8c9bdecfb34abcf8b20b977ef06de844bc7234abcd401086653aaa4bba433347c4693e062565efbdc280b313b4f29370bf495b887b50d3b4bce34bb4ea94088ef2e073d358765e4daf994689c6107fcf7e5dba13b2f08db464a06ed3ce6bfabd11cb5cc0b2897446b38c774f1fdb59cbb8c97dadc100e296a999880693ba6a27bf84a0ac07d6485fc803cd52887f293e2e7e5ead4df011f36a05078ed3baf55eeff5d1a3197ad465741c36c51157d8f4674d5abdaa10cd04675451479f3bd2ee43ab3e9f7a574cea3558babf4602e34210b0ca9cf680ea08ca3f5fe73a030066f67c8904e5ace16a590da9f0557ceccc2012f4946a10b025aa5da1cf41d42a7a630649211c9c6a9afd7166c64d0fb5a82d91dad52b60bf49c78ff9e52752a72c427a594fa5226dc98e45d82d7cd4fe1993a42fbf00f09b3421d70c2b86ed644632535ae1102da43661347756f0a95bb29967e5db59e0ac20ce20f5370fe18456c59bbd29159733d89645a0e2e2be82985891dbff9fb07e31f82dc4d660cc440733bc6ada51b4e2073a18820b3cecd3b1a9e8024a77509acbfa539afdf4da2a4fcd6b15135038f33046cbd005032adf44842a9ea12680cc9b88c1e3af379575e40899d2adec5cb83e5a3a5f640486422aaa7cbf07a94008fae45d390690f1f8c63805df6a21cfcdcfbb23382a28eb8fad0cedfa1255704 -generate_ring_signature ffaebe5a64fa21b3cae1f3c067113c4cf958a4287801c6a10ba0d63a82a16c0c c44d1d1d668711d4ccc3cfa257b24e2ee233322f078e4e6f82a17753e4f5c8b5 1 47d76caa6562600ac2e06caa3b861cccc17d73eb25df6c34308e21876aaa54b7 005c86d012510a55a17ba3300ea863c7179c91e876ee0138f8e987fb4d7e0d0c 0 28142d4e0903609bbf490cd50c1317501f90f26397aa3b85dc72789ce2c3830499930cc6eea1294daf6b25c7a27c1ffa0856da7e9bd49d8b37b8b7aec1d10206 -generate_ring_signature 73733ec5b86946952811b69e7bf7ba5c37587c91f2d34cb2370d2b9be05a22f7 d8e042940c4c896d6b06bdc0fa35e077a07e6a39cb15c2cbf9ff5c6df99b1cce 15 9161d68233642bfe6035afaba1645dae605eb345093bc46f03d8f142d5fb9fb1 128cc56981de785308fc47e1edbaeee881f818971d65a8a2c0100ff1921288cb e3ddb25dea20e2fae36efcf1c8cfe7d3514b371157a39108f4b0e7609928c716 d08ed18856d466f1ef3f50e49d0986e0cc3718be4a5dfc99311070f7b135891c 56cd77bf457f0ace98ecf454913a8b6940424c0e12e183b5bd084c34122084a8 232fb54db3f3758c5329d8d37726dc8bbdbf44403192b6dccfa8d3783f657969 554b67d108192723af618ec6024f9167100dd252cfff52cc9288da122350b548 049a7ded353d5a0ab8661f1342bcfb123ae5c8369c8921b5589a0ee96ae49512 ea95222a7f00d7c6aff189b8fc0c3844e24375a511ea4d62386dd6d5599f165f 558cad957191b01fae7f274202d8faa3b5b71348c1ef4600302a84f112bd2f37 c046f7d11f1a0eb7dd6f25dc1e83c2ee571e43241fea595625b06b18180e64a5 2f203ef33ccbcdf41813ddd96364179f00d04ab0c475744f66eac76524dcccaa 1f427b4add5faad3d81832802e23ae646a2ae862c51575d6e753b35fc87c51fe 9fa5579abfc3a715e844651c5985800b8f77f4b2d2c1ec4168e6817fd97cec7a 56144586fedbe763b3b0f3676cae4e49507c79fb23b7ea08a8b755e70c46e634 5e508d8a5e99e8ce441898c87d40de7b4a0cfb2bffda4b57c89d20065c9c3604 6 b3c351e57cea5c5759ca0b57884fabe028dc9c6185cc8b4d287dcd69c6eef20d0d3f000bf5b9f494261e1baccf24c395c3847ec397343c46958cc67efca1bb0e0cf2d6dcd53918d59651a6de5b0998d1bd6b7b36a2139d37536e20a10618ab0e18a2c1c53670c77f096c3a6076fadd9dca4c463c546d682bb39161fd41b2d101fdb829e55148ccd65804db07eed4e90bd86f7bb8710cec2526c0abbf9d3c620227574f5d1be708450be128368789ac3ac393c052d9975c22e81eca28fd02eb0faee0231339208ed1ead0b1e6c9af1a5d13078045cf2a31d3f9229fae9ad9c806d9c80784748bc18cf6773fbf3ac595b926d2242897016cb2dce8532c0ad2610008998882fc468b2eaded12d9c4f7b488a434d9a9ce7a609a7031348a904eac05170cf699f45ae5352eaa2ca804fe6b8f68f83f2df8a08f7298bfbbf630b4b10ca42aa71bdb7ae231490fcb5a65733afe50acebfdf526a2a5139cb63e21c2b9018684d7137bfc9d3da812b8321a6840f957f4a71a629f52881bcbb6c495133c06860d2180609d495d3650920f5b5a4a979890f54190d0a1ad45119b39aefea60e8b3053febe287cbf8b391f1c63054e1cd6c0feb3ac98ac33b39bcbdc04daf00de50fdbef8d14398e33df891ea4cf25bbaf32612a0c8a5bd8c03bd428fd5abe0bbace413f9217636270a3b6b83036b7d98ecbb0d0ccdf64978feb7f3afac426085788f852b8f4cae321dfc104c6a1daab5cece5f65360de4fe5fcb82c89d45906e4059ace4b54f91825912db55afe71f2d4734db085b01aca8f2c0e6a3af02805ca764ca6ac38bbc3aaf92addb8d918d269c0d6da32931836a4cd41c755dc4903b1355049469fdc2ac6251c16392b21962614c446650d1472ce5404b6bdb0a100ddb8b582bd55b883dda3521d61ee48718d2fb924f74ec600308c3130b4d1d807aa92e305c02b74466647d3f7c39ee8368a3fe2a8a73b6743b5c1d5239f4db60e349b10ae9902208c0bca72576fed11d77d2cd51e120febabcfbbb2093e7e0c053e9ed7b820acb48f2b95fe94cf61e95ca729e0a7d03019c65d6e6ab01e6fc10d5191425ef7421c768ec83a4afa8be811da71a2ef50aef054e8b4999012fb9b04d148881fdd57b3294b09d115da884326eb0839e96fda767053b1eefc5716e9018a61796bb8436d12c1a7e629f0ced6006e5b722c31d914431f61d6552f11210af88519be0c5db6ccb80d7f064b998d29dbdda9bf32e77d5b1e677a055744f20cfaec6be4bd9dc4fe43336217cf393024e656d98b1a9dea814174b0bad1a3090bdba8c7a7a284dd56bfd115516bbffbfbecde1401cf62692c4c0110cf1f05ec06 -generate_ring_signature 778bceb8b14ed5f0f51e4483bca6ed959b851aa5ff880a59320d30d0e9c9ad06 d8508c6187929646cc7231cb18a959d0c181e5f40e550c32ebf11dab277cb36e 2 dd0e07b025b266a88482d72d7d3122d2513b11966b5f86b0b3190402e30343db 903bff8f1f2a393c6f31dc6672be383ae112737329059fc46090bb1a9d684c1e 4a41c53827294ab03c79424b60a5faec6f44fbade5e01d59228680eeb4a00203 1 860ddad8565df50b6cd6d246331c76a3f9ab5153910cf7f91c4e5852789af006e504ae33ebe172677529875fc95bc74d874578b9d37472016895a31a5f9508019e7d06c6e26ef936fd78e92823224fe7d77bdedc83021dec123405b75edbc20f1bfad95c32d0858d8059e31ccf4a45986643f07c730fee1a87016ee0b30f3605 -generate_ring_signature d0af946a7168a2b284854db904594d1ad9055cf337f25b80c56f218d3c0d8dc8 ce5be5577913123659cbafc8e7fe1b31227e976ec379307282605e229aba392e 28 b5bdd837cc0aac94b3fbcfc27fb023126a3ca0308d6f9ba22d24322d5eb01d57 e1b478d03240bee41ee60b3ce5c2875637836588696ea197fa4cc240d9f6ed44 4edf95184182c3034efaed72684168f95ad8ec6250c25b075f0912e2d4f63459 1e9cadb7f1407262d64ef55add50c2f6875160c2fb645c8c8ac96d2605915e77 4d888e5b483d209c20b60c5e5601c42deba7f05592ec656cd665576a0bd3f742 bda224dc161b87b51264856b6478fd355106105b80380ceb36d2e18e5981e6a9 37f0548f3545620b3ef1515fc5b522bccfd8b8a068598495c4d2cebabff9cc02 e386b54fa02234238ca51c9939d2e3fdee81abcb94853f033c615422babc8f63 4247bd9140878709938dcb0432c0ad95159a00c66902c004d2ea09e2335cdee3 93ce5626ab90afc7f16c8fdd870e9d3640407fbc4c29cc12c11108a852208774 a68380092c8ba661fb24f30b21d7df4b840dbe8fce869394986c7ae4e2edc0de 25be966db483ab07c610bdd83a5e26019a21c9cf65d582d3aab40025284dd4c1 ecaf62457619b1c1362b35dece2209aa5eb9ee8cdaf59ae1d5be775fdfa987ab 5ba393fc4c396411dd2e3e93455b31b17037e14f9564d9896de7455a1994ba29 16b37fbc30dce9dffe460ded732c588b2a863563c0d724cd909cca18df6ab50d 4e97f0370be8608c175717e554f34aa4610daba6cb16fb96c4136a57ba230b9f 86244bdecbd028e5be7c98b2d347e79647333e71784a584eacc308c094cafd2b 25839bc668849bfe64fc9be62997fb93d1188108f7565498c33f3f4ea4e1bb23 429f58dbe7f3eec5dc1bf30ecef4293381c828c5bfe7b7d7bdb6acf942cf858b 2e852cde3f41675c3a1aa901653dd6f91229fb5349b59b95a86669b806ad6d49 b0a6aa5f72e0b404b831b691b29c810c533abad713128efc640b78b46b8bbd6d 8bc504d0ccfc3d825a1de31f005b02e5d7e8396b213ae78dee2745ba94af12c1 4653b7942cb6e119836df837d39f7b43339ae6faba28856d987a50ad2794b292 454a9c491f09eba303fd689918d9ae6ad662f8adf029fb687dee072d8809d26b 861cd74d161b68c89564fe7621ed75778aa4fa86bcfd60b981b8fb5dfdcddd16 da9d169079abbd9219fc9d1ce674378a218433cd3058d7f48e71891724f8e3cf 90e52c87f417b04bdb42a50cd4344c89d2ef87d722ac451d8ce89cd148b9a69c 6226f09a0649ce2c264d056d3dfdc74cc8edca34091423845c78bb691bb6d89c d06d01f7eaea9f5aa3836471b07f66cb15f9abf5191f03245633a73abac12609 25 7bd2c4792bc05abc21bcaebf0923eab275c9144c80ae806a781653c40abaa003f8b3b085c8c653338f002969b223f61317162fe0cfb7c02df508e539adf1bb090afba55f7be6fbe3d73164429c946a29acfaba89321368fbf1ec88fa0f7cfe05ccaa3169e9e9249298b47730a1ee026f0a1ce38e84f94b8d79152f6fc505ac098a89f2f816b1510e983a61e6fff890275a5d4b3f7e6cca6856ae85d21c0dcc08acfa7a0323ce302ebe7c597773fb3876c37143e175572990de6b0e66038d8607a52cc9e23ca724149ca49689b65b9f877f0910ba87a3468fbd1f089fa9a66104f8fb2087a7ddace7d2817e91dba58878fbab281f4a69d273fed6cc4f556c480ae527a2b69c2887844217c6381065f1da64d5ca2fcf42dd4193b2f156adf5810a601b5184473ed3b918353f29c1fc70b2f2ad706f1367d17cc2a8fee13199d70d986a1583b2033055a81581688a8f4ae84d676e160e3cb0d77481446bc41f5907f39d9cadd88a421412342994a8226ddee48256c08ba37b7849e824f885c5ac08ae60dc153b19c11a605f7f4f00dc48749dc447e507aa593065b2d4da1299670b89c882589707c218a5def3ed377575fb159160fbe2059bf32c0ce988cc43500db3dcfae3c7388990b1aa7535382c60e82885b86bf0bbb8a1b47390911cde5303e9e16a145e17a7e4b2470d6c95a063198579bdf344800676b67e02e40318f20b9beb98709c039a2cc7642f921e367bed48c3c5b03e6be8b056a8e7726752900e5b0a139ec47ff32a493d12adb3ae7c184a2e88dbb7212818c2c580dc465aba03eb465a4c9e17853e6a6e617f87cf294094135ec48b4717c0abce8a61eda5140d506eee7d820553c62aae4dd01b404e85feb69b61ed525e0e8a5b288211ae0b0b89b3e4f9a3c042f5dbf09ea88cee77e359207ef17fc2767b6a7cec02d3fcbd0a88c6fd3fac1f8617686fd61caac6ac90bfcb4362fded2da360701b319f1f7605e3669e59a1d191a63c991bdf24c50d3f970d648c763cf50769192cf72489ee0079946d777a82adfcfbe5954b7ee6c2cb334c8bb2b03e2cbc7f23f73fbd8adf01752c5faa7c0a3fcdd6e591a5fbd3c516edb919f4229e413bc3c50fe9baf25b0535098eb0e315fd63671c5b248d203a71a1057f5688fc8213817f3fcc98b0db0fe15983768c57a5d4c28aff5f7240c484fc1f2763d9c2e36664ca3f0b4d2e3d0cfdc0c42f84deb8597b9c223d4ce75587447e8a7b3f08827704874c65e0db6e06742caa0c741e16dbe4c60e9554ea42c8ec97f899a4b79a934fdab4db1cefc40be3fb1e63b85abf67b1b6fa6293bb93ca95a6e4b13ded72255426d2721cd2960a72d62c0bb5431dc77d79d2da0a47bcb922b638ea10574426a83d9056f7ea5d0e322e2548caf7286f2d7badac6d42ac61b2b1d0524bbe11a81edf6c98dbc2060174a3b5330a88c0b046bb042e6ec11b746f03c35305889ce2152439a01afdb60696bbf66b6699c03686d161d9eadd1605f7ea34302a4a798fc6ed5f71e09d8e0f69628629566261f40f41f753dce47533d4dab4d3359af56719e4eb8c108beb0849fdc0e99041c2a64b8444809c7737152c749742d3745e3733493c9c1c768508c94103969397af990202b3d5e45354a7766fabd502e8ac65cb9d95e7766221055b497766a15c0fdd8ced4664a0f178631315600fbeb8bf88d4fe5676c372970ad15387638228f0fb751ae7a675e206f9efc31b3783b5809ddf508c840d4d11035e44f6c3cb140162c3e606b222fb22ea62f15be1b4d3d228778c71993b0b9c016ed70b6bdb0885febea0eef3589c83863d45e5143c243f33921a1db96a11d00214682dfe8b8d097ff779e4d05279182433fc84d884afe6d59bf1fb3f904f3c02fc35df786e57a2f2bb97d979b60026b49d7765378b394d854c70ab016e8b170e3450a20dbc46af7995de979e4c776a2abda7b657f2a7a606c76cf534258da806ca1ec84d596b13b1cd199a120e5ee866e240dd51a23b2dd184cf96a09c181d0ff215f5148823f9d86431d7e02735ad4d553da0778fddf8faeb8cacf52f765b0a3d9a873a4f490afe2167ea8298824647ec7b50b945af20b36ef9dce700ef740ff132ff39df353020c8f547580f4b7e10275a1f8a29733c7eb60e9bfb5ae84002eeb384abe845a2d71d3efcd97f83f6c51bb4b5b66f25b9f78aaa2e76897e130900f77f4487d3730f5671e2badb83a3b91dcd33c949ade6537a86814fd226f0056c59312566d7a4dd098ad3b3b31f643ec34057cdc2aa9e90f3cd30ddd9c53b029d1988b0335072623a7419fade2ee80eb2398fd17556796e4db6de3481a0a309d133db7c07cc91c9d6f7caef4bf7716f2ec6281438dd86e751bd943b145c1409672aec2f673359ab6e61fa2a2a61dd16f2951d6b8e0a900016e1828e2a79b50b8104e4aab9cebdb578e8bbba5a6e9ed3236b655173667faac09dcdae12fc6c08b3a69129ea3b203634a00a5aaaed6fa6f14e73795bf0f5e00f1df33020e90a08 -generate_ring_signature 32fda91fee7ffd86261cfba41936bb2a57a1babc1e41c93bcce51132d3e9b507 faf0d72af0aa2f1935f9dec6852902445190d777581a778a7e1a92c1bda327bf 7 6e89346180becc891a370752d14a032274b762a037103e634b45bfac60809226 97d5764cdab85f5c11a86546a36a96bae26bd080c451ef596daaaf5ea684086c b60d1e57e2d34be3512e8a2be4b3d00ae2fae3632b5f9b0ee1c71705600b1c0d 3f10de099eee553e1b45f8f68a760d6516e2690ab993b9bb75d57a09a0556f70 070eb1a8fcd9732847154aea827954d8eac4034de82aa78cd391ae8c8f395e19 49ffd7d2431e8db5626d37d6220b630a431cf592bd72faecd522feebba1f2291 cc1127fab637befc072b81cfc583d2d93180133cd0ead88efecddd89bc46e4d6 84eb3ef42330604ceff1a7e02b17de15c717f1a904f34db6c724f1702876480a 2 489d9b6007165452979e4ecea64fb5764b1c0488a6b9489c5691e4575caef50fa42e51e1adeb9763c5a84edccd78da62e5ccea264d4f84bfb38d70981b713a0969d670a07bdfd488eeeb02f722e281dcf1d83c29d5fb5cb1b47b54fc37bff00b340ebb0aa0d0e7b2b067e9030dc66646caef70b451c68fa40ff7c7f3f3ebbd0e56f144734e10dde12c0f45e70841cb7be099fd3fe3957e43e66d15ec338e890410727d7eca4da979913a9a3f22b23a5760585d09a4a9438cf99f518375dbdc079f220b487d36e27564de0a7795f89f842e8cca4d8667d9a2fb2703ef54c78700cafdfae9e01d8fcb90555f5dd7f0e7a05230b871d2935dbe51dfe4dab2e0fb0d81c551b476df8be09230549767ad79b1136d64a6eca6edfdfe9148a3e649e50131bb54ae13620964cb242989baae1902e2b74c1b353d51d084610194963d83042bb8af66a24a2e43864dc4a7730585acd16f2a43bb50d8532446937b77cd2d013ddd668f53c94a12956543ce125b4237cac099d8bc8bd50414cebefec3c0ad01f7dba7104bdc71bcdfd275aa9e55980dcc3bae1b33ab776fa84df2bdf0886208a112a3f59ae23ad19d1590e5df17a93d145c8372c6875a3dfaf134d86c721d0b -generate_ring_signature f95f58b92f28fe5c8cca37b620c77466d564444b3c4b27bda95be071949ee7f5 0c7a668470179291b39470d30af192a98277cd4c831bf6fe9f3501489ddfd8fd 57 9e703ea6f7136edcb82cedcbf43a68c6782e8f1e57c5c2303b4131d1d5215b78 2ca97ab38e7199a22fa6cc16ceb84711e9f2a41a8f997386ce5b60332e752d32 15cf730ebedadf9fff98d1d7c42b42328259a37645d31ac52a1ef24d67208fb5 bd41a6107e68625847b3eecad7c295f58cf612069e47eb4a713f707452687a1b d95b91d6438f3fb2cbafc7977aa1c84e9869cd78915800f9560d3f0274b2accb 698db750d8b5ca89f0a4b0d0ced5f429626aed733dfa772cfa6833c2b97fdae1 4cadc08cdee26ac4f3f3cb932ceffa9ed5e936df7bf97492fd07bea007d5aaf7 5ff610158afae988f8cc3731b3f0613d1300df97a6822626f845d8a73f14d839 46aa6f92903c682a2897c8d73559f1db32dab440f857d9d3bee4e098fff99656 20bdf6ed73381e3da67d3a8dd62e82bd51afe2f750b7ae124b08583c450365b2 640e7073de6dbb3a8d8497c4a3f9c60332b485645519b2283a238742da08fe46 c9572e5cab158cc073919303f7c3288c2245c49de1129aae637b38c5bb62303e cfbb23c7d6ddab8a249ef1e1dcc823e9bece5a2eada6ab13fbc1242131daa612 8afa786d231a0c09a13d63170fd8226330f81f708147a1f47fee7557ba27d3ed b365ca81fdd650d274f1ea94c492a4a65e64adb7b0fbb6de9528782a3650118c 10984b7aa7bc1ac1e54125f0cf87f802a9d64ef28ebdf3dc91913a4eaf8aafb3 cc880b625163e20cbfc65210c127325e4b3a17af070d1b74126c7227bfb6d065 0344b564c3e1b91487e607626636a5027dd2a9590951f55647853ab8a886bfbf 5c568bf19007ceeac73bbb66d53c2cb062eea710fa82590f76da6845517aa8f8 cf31e75da46ee36623789933c452326df8b33609ff549b6709bd747f3a973ea7 c3af861bc2c38f2d94889e5738e4a756b231fd38b39d9fdfa307a6af6a26fdf9 3dec71fe173c27b02df24e8989cf7075342710a13976e63bb740be0ed3cf8881 71221e35d8ff84de6fbe8a8a23a6c94d4a2e5c9dd5e0806c337b78b40972d968 7433536546f072ddf45b1cb55fbe68a3c3b6154b4f43540974df4c96c16df3ab 62b2725a6f4ddd4e5bbb0728efa86630b41d8ab848d746f73af95767fd57fcec b6f19566fde140878f4f1fdd0f64ec397ffc1ecd66d780af774b35ec329d2139 b1e53d524b31832419ca5ef8960245fcaab7cf1ee7211f3de92737fcd32a2256 156c9189c4541514c557e3767e3ee07429b397cf1cefdb7eee9c978d1b11f6c4 4babe5081a6e5a1dd8ea579522c8f6a44db64bd48229ab6ba7f9758990c333e4 3f6ee7d39a89bb34bc68f02557cde48c0f1e4ec9654d65bd704637d16fd26721 4512838f751891c81732898eb640c8512b6a77cc7d63a3e61ae0827b7f710627 a69bb4cd67c4d72585a8d3db4000429a3ca1691f2da44e01ec0113ff72dc895e b737c73f3572c71efe7cb285879ade2b174b65cea150f180eaa17014d7320cd0 7d472b19d3b3a828dd67749dab91cc686c0aefeac6f10b021914362e9e3db34b f6158b35f8a7d3bbc700d39c43483aa57ce38c8e6ca1195eeb87c673e5c2eb48 5dff3f8b3f1d276f142d69dab6f425f4f78b1a18182d1ad207c027114a694b13 e54fb93a3f15286ce5f2284f284927eab88e48b8213c1a41eeb586ed999404a5 7dfe099cad93d510b49cd166c192e813c3e512a62dc4ed11d9b9f781f0194c80 773cab6f26db9c2c36da474052966fcafa308ad649ceda337f764428948abaf6 aa7946ff4d12c48703c04f6bfeec86d18c4a854109739b3863e4b5007999e2c2 b386b4f889db963dc4daeb14893a3b0b6c3b09a3920a1bcfd04dedd217e573b1 0726fc793b0c7493cc0439189bd82d5d0c3dd117b9aa33d6a0d9553de873e88a 93f9a1f0eda3a75467c99b01005ff16a4c8b006649f77533cef185c4f7a30b08 377b1b8895d0b601d45a5dd36b93eb460ffd9e30a0cf1d96b91f7f189f4d8732 5730b7cb7e779695bd04de02fbc180f8a7b1a7e6d0fcc066bb28d4c94b3aa021 548678ab5e2fb6d870d6c05e39b3267101011c0a842f373b0c9735313fe7c207 788e4bca36e99ac9bd39e7e4188f88b78dce485fd8ce7408ab5d5588a82eea61 c7fa217234c15a4b2af0aca4ea58af4d3762dcd3b17a7be13ad882ea91539416 12c3585925770b39fe686a8d04ee10251d504c94a6c16853f7ef1a6f0a525556 f8748feb866849ac48f120940c6731791a593e5973c9228693397d8e3ad65268 8bb30ab41ee5221c65b21919b8a784de26c55983fdaa5bf635a1156b51213f77 70a7fe521146c4e2c6d0673e9e31f9931c0fc79d516439422dfcb86cd52d6497 a85e0367501bb37a0c8d92a311d3b6ced453790b116246c9c5b7a3f6022d7dc3 062fc5d81991c3e216ddb0caed6fdfff321d73c2c5fe54b9d956d18cbf7b7c53 fca490659c54bb57d385e33e8b67773bc912ed88506ad07f33e4acd1444ff692 1cfa1c7683fd787875092bb812b4064d0beecac871e1af17e72d359f27e2d87a f2a11397ecaf7753c18f0a6970da0573a2804bfc4f48c099b29e71ec6397c2d7 25a78e09d6d2bbf9431db8891b048d8429eb7768ad2fe5f2482a76e03683e908 5 27f7d788a2035ccc721b7c41d23a974ca4d742e1125e99a1d4a4d36f449f1b0846485922e0663680e42c890bd46625c70fe5fd55e1c7cb02e59c9951a53a94005ef3caf7a7ce2c650b2113c33e25b30957c94cf0538b7bd53436ad2041156c0d8a60259468ea85ac380d57b0770569210f00d6b9129d875e80fdcfc27cbaeb0e74ec0d7fb08c28f14b03d2f3a5316ac1de3b87c0daa4d1447e911de5d284fc0d7b0f0f77824313a58d624c2f261349ebcd23ce34351685260126f2720fbf89064629c7b7b28e84c4d3d9f0aaeecb9b44c7443c569d88f860506ed8f09cba35055f9ba5151b934d4c0243448fa1d72ee4a1460043da70349a534d7f6f3825390a1348788daabe964f014cca277e72f27145875e19b13f9bc94d2be573ea1ad208587204c10528a5a43198bb0a94b990ee680c3bd6016e14ef2b789d6ef61c1d030404c14e6e79867d75e0d566d77041da9e01700b9adef76f73241455aa384d0e723bc0c0478109406b4d47241eaa7c8674102770b31a03b5c021e3eee419e20402253888463cc64f1902c8790a0acfc653f5abbb1becead628556c770affac04a6987fc9cb4eb55179b5f5f36b13b1b4dea9003db2202ee5a7e6c6bbe6fd4e010034f1a7ad53bac65dc590436e12ee080ad65d819b143e005de20c58c746730f04ffcb50c4af2dd78352ec4639da2e152988e9b03318b33f39c736e62b2afb0cbd3ae7759629f2d11304551ba72801ebd8a2adc7c071174feb134ccdd46c720b554e0c00d6421274ea7f45613cf1753fe5385181bb4584fc96fbcd5ba16f79000f7a49f2bf7529d577f8118a3ad60aa200c4e54d9b5fcfdf0144db9639bfc80c39c99d618d969a8df3321ce8da00692e7bf7adf30bc28936e3abc5d89deb11037c38e1534b617e9eafb9020cc1febe2cc619db3573ee3239f3d571999b198802ca6bfa30637a1d3794050f9dd9176121c3d373a45cb54c08b706617dbc4f4b0a5abf5cfa37149a54b30475d95f1481ac287375f46c9e186bdf0307ac3d2a5f07e7dee417e5a48834b45f26b5745bc09bccd92ed1c37c765347e648b401321600869411d9629f5c141edfdf1a9e1187cf1fcf505c22a12e5e1925b5828d2e0f03f849fcdbd78579deba7ea616e5eba425697f5904b660027ec4731a481dbd1e0443591eaa4bc33f189d3483cc3ae12ce0b1dbee50c0c5508398f6ccdb21e3190e4c64d2d920654ac28695e84fd6f7124d0faaa1d772bdc09b88d9f115f642c707ee2118d042fd17dd46eb049fcceafd63421216b301d099222407d642dc64330dbf96b9dfbc4616c8ea6654b0bc4f51c8e08fedf0a0ad25a08bb9a544ceca520540c2d4af6eb450c69e0b720ece8f6a8dfce12103e58461f7a34df2aba942a50996f0526524d39c46c2372f88e19bdd0c56ce41e6d4ec3913fcb72649131df8001768bd61adef1d3259d2edceabe0e53ba004da1c518b61119d29d4db0f16d2024631bd556963760c923aa51579921e746fd669aca97eb77bd3f478e82adaa30f7ae3a2f0a55f83061b6eddf5339c72c79bf8d5b00942db42b70fecc6b947630461bfaf14649d0dfb627231678c10c9a85fd661d078640bc88e03ac4b1c71cc0cf90306ef97b4817c368f1fa9c4dcd66fec7c272fe89a2446a8fc93e28d81a60c86a730b89e24f970987c83d4b7c364f0c905516858c5f750b3fcf894f55cfa030281eecee1b921e2d0cb62d63d58f5797cb42be3b65adab953508824438dd104642f8df0fe521048882440b71f168518b27e87da854b3b056d6b45b2e04dbd06ac7d4f733aa0994993e0e598695835ef31d45197e699b35060ca6e4cf807a602c835666bcb80b84ffe021e5425a3b20aed0f73a46697a0e0244e88a06cf4550107e2ec78af5c240b856de50e260a3a4732539f9f70187a64b6841aa12dc5630ec694ff2f617cb7c6b4b7cb858230afffad0dae587d0185aff1dfd43077cc640ec912bd629e5455a905f11fee1f7054a2dc1852bc6ada4e933197d75e5837240dff6c2c100507a72fe847a8ab6318358d307409c36e0ac8b0600e0f8c555c8b07b0b329ede640adadb5ddb7aa2389bba1dcb42ea0d4c62221b604bdd10a20950cf9c65f05236dd3f3176bc06b0a54b1e6475d3d8da7e3f6791a8811ee49c30b05368c7f6a8a1b99251357a8421878936e06723cac41eb4af36f4b2aaddf233c02a7359f323817f3a46266f4fd0eb6ad1dc6091569182dae3566a22672e360af0c02df92f3aeb714b2bfd08407b307cc14b12562492c6e891baddabd8d6b20de0aec5e6028f3a84c7da60d69a928bc9c89c15840c3000c076f484dee413caac605501a2f3a307d1dc44c53672290c4d2973c1bbe63503bbf617baf643021434a06add90c9c16ddbb49bab63ba8890a546d3e6d8f86528951f9a0d00bd20335f004a6ff52b2a95cfa75960cf61edf80b71d38b2c866efcad95b13a2ef78cefcd00b6a236aeec96e01295664a8e8bd04285918790206ecce3259bc89e5d766e3930fd351e8d68955a6a8190651687356170b93257292a99627e4b520e9c85d43c00462ff0a0b6cdd481a0d8268ab5602b663f9804b18ef5fe3b1b9f4fdf361f3fc0b0800ab519b986c77d595ea12c4dc4a78d71adf643cfca4cefc68bd038e2dfd0c50874db65c84b38f98a4116639922bf29272c36f32363b850a19145fc9faff01cc0bfa12ac950d2b2240a83110a080c58bfe27c57f4939b7bdce9a4badfb28097e1b30ca6701fe17171369e1a318ac031bead9140d80bfa4f8e52434418e7c0fe81f9d0d5bbe329bf34e25d08ebe114aab7b9fc1fe131636d977a083ca1e8700b4d52cbfd16e0edf3e90a6363e88229d9ae295322a2e22804b3bbd8da25aa80ede85d8b0199a6dbb3f3cb0abfc92248605831fbcc1be9592baf51b528ec65e0245b8ea254a8fdcd84d13ba4707f48ed4c4439091bef66d82ea19feeea757b90f29cc9d07e235245d0f78fbe7496ded41621cdffbd675cce3518f0ac63c1479015d5a7eb7439a6cc20313a7bec7171a9a5163cc34682d84feec5a1400c590ac0c68bfdc88ead31c34bb1001cb945ef21b36fedcefcf9a522ba8231fb9ac7f4807d17c3abc706da669722eee6cf82fa9e534fb54c87c0808fb158186b3442be8064b3c32b257885c4f20eac683f0f0adddb1398866464a6f9655814164857b7c02008184866c5972acd64bb760da5a351fec2f04f16f50a041da0e68032f2987067b1a101732a847430a698cd39bdbdbd59e6f173956be1fb7852e0519238e1f0d113de9b1c3e6d0a4992100006a02ad18f9ff6188930f58f98ce8a88712ba48026f0d2321b20c9c84bddab2a649fdab94bd4c0eefe7a9b4cf97ea80ac7868b10ea48287edee1171db95e66fdfae79435846d93156508d2b1cd34ddcfc788bbb0894112564fb6567caab167d4f99bcc7c58dfb0f744a966760212f22c73df873042165f9c59d69212120d250ecf5ab45dfd634debfec9270800a009921cebcc900ae88fe11dbb0f072adbc34e02b408c41624e2b05ffbe91b1cd7b784a9e12a00d34350602c97d6f12a5078709d34c0092516400371d56d54938ff8208921b9a0f805a2d106a901f11880cee0fb0904242cfe8a281b0ecae01b7a0f564e77c0f0bcd7d27f21c15ca2f6a54737e9f5711d34ed2bd2ca9e5c9fcbad896f5ea6c6f0e8c0bc8c74bcceb6d19ec1259a5bd3e931c06fbf835afdbb865f79120fe38ad0a7687500f22e5066ebc51982e5468f80d9afdcc9c8d15110933ba465859bbcb00203f9f06ea6377eb73471f746fd58befa74dfcc0c86a361aa56f3d5808942b062d624eff791432b4a68a453d7dcd1d76c76e4616e3fd7144bf50f764d0cf990b9ca9d60ab5e3ba2476af33af73e5eda3b05c69df5628f4d06e0fffccaaff4c050e511841e5e4ba66890f31b7b4d650b15461e96d3a054cd8800e569fdd695503719e17b9e5c1405fa9478c0e594bf656fa5517a2e1315b499dd57ced95ee8d0a9f41ec2c66e7462613a93a0aa29cd136d89ebfa4e7b2fbb4bdb81c0b6b4ee30bd8bea453f78199758a8dae88be26a4d6056169c19f0b8a669c4c01a20ac5aa0809c03f9b77f4cbaba79b0b3bb267226ab457a98f3db49b5ddd76938e173045036ab73092f038ba4b8137ce824bb678eb566522f17a5b5906e56591cec091990267877d5d53d959c777548d2a324260e1b02a6544214bc0079088937967720405760e32111d911d6d74675bd5da8cfcf23971dc2f569bff43d9118f9229921d0b01f9371cf50e56699e72d4ffdf8bc0fde7b4cae29afe7e628894a2d1a83c160c4ef81f235b8055c2cd10171c86f14ab13d85a075586af8cf950554eaf102c403ca8e8b480087aa11efc9c07042c50b29a66069e574a38ccbe4196233ae7ee807c875e24b8f056e7b25bc7a16bb539ca72c8c9cbe9454e1ad0417417b0d40e109542e18e3cfc116ab87f05eba020e1218554d63e841feaa7cb2063beb9664dc0b366bef65a49456e91f554f627d51315df7fb8bcbb97000443e071ea82959140bd7b9d11d8cb9535a5afc47a08a87de27e18291a803326693a4f576dde707ca02b5ee92a30fd8cd837ff094060b75490f8848136453ac603a5508f9de19044101404a5f45fb167be250b9dbe50bb51217dfeabfb2f36fd402a9e9373fbac8d30c50eb415cc80e5b3ddda0861bb015afeee835a346a755d06fb0f83a043462650f549bcb0b353d421de2d171d9bff3dbfe67bad88fca379752a76d7687c987920e008aa8258703d05597dc0b0aee74d0e7f7e24552e62650383f794d422a92da0163f1b9586ee00f40d8ad8ca406d892b06cf4325fb2f8e1c319b4e1ac3ff43b0765c29d68efb289478a52694144a02fe0607bfc467af3d67dcfc7f3f3421be10bec530ae754f26939473c33f1cafba3964e6ab44be00212d0f2340f7fe3912b0bd8c348471ef8d6c046e4f372910f1c6f86f667901c9b7f6145852f392f2243074b74e1ea3922197654bfbb8f3e7ecd737c2a3c54966942874062f165a226a70ae64bc92d4079e5d2badfc83089ffc9768ff5f8e7d17fff1394edb4aadc9e9207d6521380e7a6503d3277c97ad1f287aeaf2a26fbef9a792f77adf052bc297e09 -generate_ring_signature 3f9d1e8c4afb644f791e59e8cd50f02ffc79029629d27d18462bbf999fa28537 6b176e3de6c8e0d8fabe2e2552415c02af6c8d2352d99c208af199dff42d0a89 1 0870ddc2441fb0194765bef22a548260bca7fe9f8480a961f108f601798dc115 8c1cf72b82661277cadbbedec6b11e35f7bf100ecee3cf1fa9dc46de1d9d1201 0 c1a128dad68dcfced3423cba8dac8cafb6c890e6bfee4595a5db341af268410257754821b4f4707e1eec8e99e072a5d4924aa408bd0b357c96d886448ec07404 -generate_ring_signature 21021e77c02b18cfd15c18a1a6a46fec6a5473f49fe7f17f408c9c5ad3bf9f77 b34901b76b34408675b429898c4c3adb74859e3577bc512353ce51e338bc1b80 7 bf6aeb0c9fdc617e09d5fb43f98f77c0e4db61b442411f17612d8f7d68030d8c e3134f86f35ff93536f816ee7c71a22658f0922dd6d977141fd3251117adad6d 0351df4a97e005521dfdae09511278b8789a7f095ef151c134fda3d71db05438 31ca8ba0c25b68cc7b0cfb6d37ceed81333f87469880e8a4dc431daf9b3213cb 12887b59f4b5af889620abf19a52a7bd1973e04d7b949ff683dd3da922457017 2aaba178d00eb926ecc67d750d6ce2f10402e59cacf46e1d60cd2bd2dacc7c06 ab66bb5959bfea90f0ab7f1d6c78b6e380a8c91ae5b77ee24357abfe128fc625 9e682a0ce957782de7ecaef9010f9bf77483a51ad18b513a8acf4ef2c7562600 1 b84e4b2a4b3c7620863d63acf8f2b62e6dc236b590bc8912b651a840d628d808a0983263e06be3a710fd7c6c2072657daf66861adb5c766c7f58169fb93649051b8712e084d52fec30a99117d5e8e0810548b58fdd947162b9493913b8856d0de65920ed19777857382ee3e463349f139c45a2b2b53510e379077e7a371c1b0566a9aadc5e070cdab52d60b41132087a2bc1892d8f5294515c595ec5306c68035378b35c912f0dddade9dfd15c93ce31aacfe54c61f8ca69c8441897ac46370b9dbe7e31917c7235b2b81fd805bb907677a52de7a1a10db66bd5b0fa822fb1073c5894288e20a392c5e3d0db38f295917f71b0d98af0786df5392169889ad104ac1bfdb0a87c41268fbb66995d7f6f0adc60f945e70283c2ea49b8eeef38b4051faa9c714b412dd76c2f7ea358d7637f1c295ec920e7630f705ba7f7373fe50b82ebfa730640a9f28b5136d14420304a62f19187cafaf131aecd7fd3bdf1eb0eb667b357b2e0f97a7bce57bb99975b44921c3aa43a23332a0babcd73e4365308cb3d66bab782c87ddab879ba13665afca42cdfc474956db57724faa7371cf90cabaf45e8f873564b4b606031bdc55980811dc78a2094bff14c28199403d57f06 -generate_ring_signature 953630fc2044cd653f7a0ba4d3413eb62f6e17867d2a24283d65c3ce6318f887 e901f99a3e172a3f7de853302c9ca5a1ef79f5762c13ae654c4259e8cee66e33 4 de453f88739ae0d8339d5176fee305db6c4558ba79cf29f7cd9d578ed6f93f73 2662a16a5721e8273db4eba161eed7f6c55372ce1b57ffab8347f94ea937ba77 9b838a770120cd96e91ea3753b6728aa50c8428a6bda2dd55ebb2ace9abefb72 7555929a6e983390a6d99b9fb21340bdea436104456f7345e6319e772f17fa82 47baf5064573771c80e48605222461043b10259e4b1d5ab00ee4c5109eaf0509 0 9e8b1456efb777fa6e495d19e01d157961c573f860c58bf690f59a28ac19b607be3169f6a38372d28f9855a9d92c5805908e18ed15b2510745c348159f262905148e003d9aa5839d2d4e17da5ea6a026eee750de02a7047b10154f30ed812407993dd3ae451da38187863c025e0d3a8f0ed98b20a1200c355174e76b46e2d908b7d480d1368262f147f71cbfd8f5e723a739677a316b6d9322cce582026b1c05d2a335ae99c77389ad718df8601f70ddde0e5d323aebfc709d27e6574193a00ebab62c02fee131511399a7c06ca2c630616aa1f626feb21a0eb98a9de5d471076fe3b331438fa57f4e0b84fbccc55b598874ef3e85bd11bdfdf6b15353442a09 -generate_ring_signature 389429172b85700ef011454e82461e30d9df22fd1db8366c615775683e19d8e0 999b6e454561a04399883e2349d36225d208c3426018c8a5140b5ed688601c1a 1 ae6e602587bb3d69854b8ddc46a38071e6987cd8dbd8e5317bc22ba20e568653 1202a2169995970dbfd88fd2d6bf35111e1a13e6b0f32b8bc93d3b0afd206804 0 3cf5eee0e7fef22f4ce8b9a84f366f10df2367d3f1d154aefe306b5e3706360854b704291e0010f786d30af6bd9c4832a008e9146448cee2300d91362d875103 -generate_ring_signature c37605f12deb3cc9723bfbba6a865ebf42b6f55ce8f3d6f097771ab2b354f3b0 fae39afd0699fbd2a2ab9403bcde544420b66224cba89e6ef4bada7ec9f7ddf2 3 7f973eb742664756b20e8dfe86e1e12a62ea6cce44e987474d3f0d5fd115da7e af79056ddd39d65eba37874cb91566c6ce0a60305380f8007535d2e7dfd7d8fe 720f9e325b8d682a17ec1e8c9d118535f2102e9e03b570f5cc74b50e8ee1d12c b57bfcf6db2519fcc43ca7650cf3248d8385e5cfa280d6d90dad4b2154e90b09 2 9b0756729ca85252b80699d68190879b0dcc3bedd62628032b5df0437b04ba0ce941f1d40b98d3c51508b6007946b6c74686241369caf65df71fc64c970cc503e9e09d2069a8cb439db5c3397e4dbbcd0ef7fe8093a2a8ab28fd8ad40412d3050ea154ae7995347014e8e2b7d44f5243092155ae20cc1b7079ef14b970d6ab0625cdf3a8d97f49ef6710bbdc01b5b198644501883e1f273854627193a5d1690eabb98183177abc826cd9b01cd7a58c47e1a33e40330359158635b2258132af04 -generate_ring_signature 37671a1d249e1788e7c6193b699c5e54f6be1ca35714a3a3e4bb5f45572c01d2 e0e86972cdf25612d13ee325998e27afe1f5ca20cc558021bfeee6b8437062d7 14 f41482621e6d7291ee1837f511206f285589e82270c54f3528e9188dc39cf6f2 ba41a5e2f314fc05a8f86861c2e3a05318556814c9d209e4363e363476591f6c da784f4125cab4638fa28b78a7e1040b68b0f4b782bc53403d9b311d1c8418ee 1c765f10d5c8315d648a1ffe3d3c81bfdfa6ffd0e32935b3f4e63f3f563afbc6 107c411025027e2d443aef21a7587dee87fcd8d0201a679dbfdaf4f2ab19a2a0 67d5eda8542bd0951035449c08bae062f28a7eae03acbd4a85c70e98321df6f0 90ea7de0f1638302ba6017957756f60b85de1f18d415b613c0c7c9d1fee2b020 714f410e3f73569ba9dbb4b8f973b1432cd573d33d24ef35ae7cd2f7ad385cee f2c0ee9553a23167d0e49f274abc2dd3976c1cfb91ec2254c8c88477f46ab46a fd5de7201cc934f74b307d5f5f63758e633e9d60fb9d3eaee47f2fd2856fe9d8 086925bfc05dcc018b7b0494d8743328f5dc9b7d6078c3db4f03e2d8be5e2aca 8fd012389212df1806ac761c9a70f1e358870177b639ba7431cf708279e1b77c ae9c72de597032207f452e71b0f4885a4ec933c9908068b41e9a1a5d08eddef6 668fd60e30f1bdeb678c538fb5d94a1836b1e038f20409c6608f857dd3c47bcf 1a128edd23b4090c4eb20ad7ed850a3bbfdbb4bf0ec91066d34d3b8d90433b00 4 23e5f4060305fe03b4d492d0373a2fedb9484be4184b84471b5ce2aba441570082d6a4c0113e3a5361666e2ec206b3ac148d7b6482bbc5e74fbae3841169eb06496f4b5f529d78fac4f1a59f575b5ba3fb14279f87d5a37f78d9278ef89a5a05792932728e7b18c071827b9559f5d235486ca241b493c0e565fac373dce9af00b70b72afca797e9eb1300ad9c8728f48045bfe249a1ee8d9ec772dc5e99a8000041605e39d17ea3b20b2844e0efaf113f5574092b35da1e338aacc48c5dd6105691990852e49b5052c83821156ee2f8cfbcdb30c7d95d2f2af56c47f67a96a028955e7051907435e74216e0a14770ebbef577155afa6c30d881df0ed04625204c61527a870f853582ddef3d3b0cafe64c83e9bc18687fdf21090402459b95004fd2924630e75e2f476df7bf10c9d7586543ff728c41c523a4d97641b7a9240052069adbed7a8ff2d8531f5a2139422794fb07ffd9c6bab272b322925a4148e08f3dc866d6c34296225cdc71cf328f04119c468e73a9be25d6fe611635ad62e0719b9ab36102bd1f17d3e70f80ba7098e146d80a510b576f5a0c1c7e21c6f38046543f6d0aa261413de0199f12ea05bf03f0412395e91c63a48485845e362360fa5d41f56e746084c8ef2c6e6631c2f938760d42c18bfe8ce2b830701e1a3ca09a8493e9a7dc9ac1db6e2c0bb8ad6bb5ea0c95faa2dd81e563a28bab999fad70735c14ae16d5e117e7e31e0eda7139dc3f3a5b75f78cfe2a898ff148b67a99c00740982d7425b45b2e572fa458dc5c965c5f3551b3a89ecb2b281faaaea6d320c0e9333342bae47309030877b42c6ebd3f798289ad26560dc2348637122630e0e39797c36c61d362444e3001ea8c214d6e343b15b0b071e2445dd8374adc6790b7c6a36fc24eab5b7e25fe33899af72649328d30e302e7c79969fa4bedaf0c404c680e37dbc3b2060459ba126c845ba1b0a14d3185a06875a596b737de3f0ac08f20677c99968b8630258e19c531aec38818cdc490e1790abd76eed78194fdb078d4e3327576cef04c1e2ebd2e8ccff0669e305da2d76387de39f0886590cfe02aeb9004ca8702de77a68b87327cb5baccbb773350617b87c853baef120a41e0bc666e3c3b50e914195085ccda3214dad2b79e2a855191c6c2eb8e50713a108092ce90c7fac40636e542c1957e97ecd5d83a5db1c56d4c6b09dd1d0e880c97e0fcfa64a73c3394fffb771ffac7b429ee67b97d81d041975f9ae0d925800a5370d -generate_ring_signature abd653f7ad7fd63df48ac6a7e0192cd459c65a8240f9d2cea3676a8bf574d3a3 4ec5408a2203919766e99425df38cf93d465ee4e7179d4140f743b780950f7ff 127 f7cc7ad1d5529686cb49d9a39db137ad1c4c480bad36c87e9874db399df6e2cc e5603615a39caf26768f8f006d8e7fbf75559d8f20f1d668603021cbf5b06ede 35c398f97a395727bb667e2d1b2e4ee561ade6cdce28decb955ea40e3dc81b19 39f7d4ce324bfa1d831a3ac2a26784f4d9858a1a7ee913f0d753560f0fa205c7 3a70187619d98252083118eeb6c43d9f23a441d081c06da5cf1538ae2f514b46 324c0c07fbbec5cf493db7a55f4f2944a88d66fa29495c17d2ddd7beb799a981 8246ceac0bfbc5c3bf76bf1e188c6edfcbe7e8a8b92c0930a0e9f6d7d9bc4efd 46510f6fe33f0246f667ecfac5e9d01e62f9a675b3c9b43267f7ac6e9e055eaa f0531b6c747fa76dba4a033571b5adc2affec1d8ee7c3172aa25a35643118ac6 c3080d681c17a1a9294552e508fbc3a0183a17a1e2708fe32552580a44ea4cd8 dc56dfdce49cdb9221d288b78779a6bd84c4cf8e3c5d7dffb0c5ad507715f174 24b46cc382e88800f74d1eb5cd8f81cf60002b5ee67fdb2f7fe69aa3f921bfe5 28672e60cb393cd812809e8092b639c73e81cc2e0f8867bb2f136cde06c4876f e1ade5706d9f09a98f177a310cd7b8cda1bb72141283205abd27a1db228d8778 be543df678f6169184ce2ad48e7830679ccccfc52a63f8feab3d5ee79f0c1054 b464788b8d9c73bfc942547d9a2726e2c63106f18e322f9b690efc813c68afb5 4fa40c9e5d07a7143203d40e401198a589eb8751b4dda6bc8cd1681586150590 ae63e7c95e21ee618a4bf5f51a9bb1349a56e13045c1ffa54d51fa732dc88a98 788277e0c0c724a631417c4caa078aadd6dd7ccce6a2dd3b6126490f9cb29b03 0e7f64e648f936b92ba48d50f642d4c59bb972837a8d0c698ebd3f6e8d834756 8c9b5957c1c93c492cf3ec80493c9a2ca5c8a296b47c45a6ece5a9c1829249a0 8fba8d91bec11803d4c5762227b2a87248ba4f468c2ab1b4b99d37f605b6c942 25d297ff24cce2c8763580a2cac0775cd77a4625f877b6ac39bf8b8ca06eb121 983429fa4e9308edf662f138a32bb5b5d667fbad795482bba974751c1cddb922 a4bf3f834a9f55d2667a98958618e667207557636d212af92a5f6334c812f8a7 f35b326077681c3290f31fe27fc154716e58b7c9913a77cced12a56d1c492a9f 323c052da3cc1228ee8c14a16ad1c955df440e58cb9811673f49acadcd111954 f01661bdeab726471018f51d247c872624fae8e5cbf34071c49a269957756e0c b67dcde3c5613b2b3b599264f06050fc144b0beae6e234830548b6af5a4ccdf8 38af053d20c292fa28d084721700d138b911430c73f397ad67cb878e115b2beb 27c40d8f771f20559e775713e71ae041abe5e1814e6631ef3eee841539e2d262 7a3a321751c2b1e32dd96af7d33d60184d8d42aedcede75d4e22c9572771d6c0 166a8a31964185aab43c20927b122f4457005786e3d8594cc5512ff88f6778fc 37d33c370f64d5c21fcd80739846a39fad9eba9803afd1e3c3db6c933f600845 68ec83d3d23e6054492ea161ac9fffe1a42ca54f28038bb1c42d3b7e29fd333d 5384f0857de1ed5fa5db5755828eb4e117465dde04a15a90215612782e29f4c6 f93dfef70ec9bced9b7830d6dbce44c1aa4eb671d5ebb3b815d654224db90f1c e386cfd691fd2c855bf8a5c86ccf228af6d9481e38acf5cfbaa9d8accdb6f740 9380536decd8199c51bb9cfeccae5b75d423f18e1de0651bf9419cb9b484084b 4689cb44017f37ef8ef63fe68d4ad26018e7beaf3486bdab9c6c3c459c3e7835 2baa6a00de237bf2bb2fcd3d106601e57f33c27a1214ebb317f53f20052bcd73 c874b88ba9c5e2ce74996d132be4879f2a589b21efc30314e80d0cfade1958bd f1485b76e02c7e9b402c577182a4a941fd4c60618c6f92f7ece4f1f03caf723c fbd10d5ffeacac15e16cefc049ae124ee6dec10c66b6f5b62bf21d8aa41acad2 f48ca5d7ac83ea3e8d9adde2745fd6f50fa7639824db2b311bab61adbcce559d 0d676c1b31a9de877928419723a5eba5d37b2378d2b3d9e846b6cdc865cf6f6e b45daf0605d323651751912084602a122b3a39611bff1cdc36fd74d882e17201 0ef240390d83a22f5e7b9685545e94434d7f667141e55e9344d50753986d4a2d 138d36e66f4cb547e3d2e2e32856a10f04877cc45a4d94d2853c7ccb9c29a05d 3c616b6f7303de221b0eab85a33ce48ee0fb91b125b69c2bc13bde24316fb146 c808d1403d310e1128b18554da2a8dff773e7711104495ae2d4c4cef80c4adef f00a9429229a6c135aeee20efa63cfadc5cb264f965d5bb99c2622283688953c 9f763318111c6e949abe452fc29319e2c0cd855ef13973355a458a3b7abfa5cd addc8266481e686f0120ac9537d03348a12823ba9ad2064c8fd3c056af87d9c2 9e3163d748d6fea21146280df9c2b01c6b46300a40d5d313749982b94caf43b6 09daab1f1bcb2f17d13c0ce3024631c834b845ffc29656cac32decbdceb36dbf 966e9351376dd899b4b8dbcae80f03764a7f67ee0d1c883ef1ecdf62af6a4c70 526b72a160bda748d06fcba2acc635f2370c642defd1c19280ce29111912adad aa37714f944dc1ebb4a5bff29adde7ba2b3bcf1298930cce423ec8bb71b46301 7cb9588e9e2a967a8a5a1486864106a667a277e28f65db460b3086772f06a97e 94fb2fa586d2e0850db9c6d30b4cdabc2f2f12f59bd1c9536fab74d9766f134c 1c466c9515e20c18a56bfdd03bb7478c965a7d12d702d7d4957a63a560d10266 609ef0ebd972a504c818929e4b680aa2d3af312353eb0f8c886fce2c2ca18434 102793b591bdf4a26d4277b57abb796e5066edfc167aea07ffa014cb58960e75 4a94b3f8577cbcaec6b0dbee10ff8f66320707497543846b19aec8f922b4293b 6697ad22d8ce84fdb5fc90cc5af87d5d2d962de46cb53ca843e9938681484d68 18457de093c4a00394f250ae8db6cf086e33f6d5d14dcb3040efb86b3a3f0b04 aa9742d9efbda738d62ab7625bbabcf258ce112757d0278d160b15936abf3cf1 76bc78c14142bd24d386b391e510a448084640fee6d5c89e78912563a30c6082 5a31d2c3cee04912b6bdee1af63d791bbbb9895c97d40f64268d429c29142985 575e2c99943ee27d8962991068c135d934f8d7189ec84114dec86da9f8c86d7b 653841686d1dd0025aa32fc36e2ec002f602d6063433722d32b438d5e2d75319 522bc0507407007529b25d2eec729a59c0f60e98373a0f539aef20df7f53ea69 67feb74336641ee284e032ecba89e02428d2829af28d12b0e5bd9376a60be00f 3c865f1ca2bab0db4eff5eb9e4108ac90e1b33362d43910ed423cc670035f039 d6e4c9d3d9d3d99471e5973327d308fe8faf93727fabfbb0f748c4fc9fba82d6 d70a8e2810c33e4f5ce14666e6282b71db8ba4b4f25984761a61bf21a80098e2 20202ef0bd6bf3faa3dd42b314b620e15fa03cf525b7fafbce8b1f620c86bfba 8576732069b9c355cf922f86b90041baf4d4d44f79055afdb35a958f8ad9fcc8 5844694e707777f2f9499d4bdf209fafaf286fde9040b51657217add3e345bd9 ebbe61e0c6be3cee2b8f2825806423b8368a7f649a0c842fe537873674061d8a eb676ad1281e502de98d3e9c4732a095ad28db45c164ce7893547fe6d776cbb3 ee6ae2f3a4e2c55c347644afd43a8e3ba646259a4939ca5d0acba778d05249bb 377de8f232e2702372e38e93858ae04ee8729ff65a76fefd8b961e64c4eba497 a0ed9e2d5ff4c1c414edb066792f9026692e49855bd6f0c3d69a1ac2413feb9e 1f6c215399aec9b4d8bf289d372c2467f560896aac9b941d3e3c0646c4611f79 a98c0268b55e338c26e4494a4c8a8022d9653647103a0f1bcf91dcd8b3ccffe2 7ee898c856b5fdcbf8003dee310084f8e5d7553febd3d4d9a4dae9728ce7d8f6 e549734c11a2460d6a99322b248dc590074d36a74635666c0167f4d6455a9b10 801df1a8e2aa0edf9d00861bd977b0e4f5d369c182765852539e4eb98d58d9d1 6b17009bc4da1114f1c40a128a5f9238a5ff65e43b0e3ea6be474245c63be927 86a0be9a8e802b0a83eb58fb288e8eb0169085599fabe73df43d98dd538e4fe3 b52cc90b3082ea8bb1c16f4398fa9852f1d7bb3984701dcc59403ed0ea11756c 2e093e60b05a8dffaab61ee017987832247f2980afac47bdf83cf4d4c9b09fdb 23ccba870003d977788fb87907888460551f208dec112af68b493e339324fba9 4336845a60783d068c0693181b155ff8a927e2ade96175842294c5269bba5fc0 885cd6d8605166246c5ab4add221064ac3307ad12cf0d76961527722baff5b45 3640a825842e15ab7fdbd183861ccde121bd494d55dec192307f6e26d7257d42 51e8532b10c76386fa77df1f0972770a67b83d877946387075f159f58814e449 775cba5f1da6c8d6ec21a32821189f46c4acbf8248b236b1e14ae4550607ae54 6a4853aff074835c48ac5fc3288f185e2ec3888ac41ce5fdc6d7b50abfad9f66 73789c6ec8839b4003a2045a47c9602f75a55fd2632f4c1e9361f939178ec98b a548db9423100cba74cacb67611f617ba18b0b65d29fd55ccab9f81a0613f653 e89c5116940301a8cbab6c88056e479ef6ab6605b0ca9cbdffda08dfafeb697e 77ca3397a46092795d90efa06d1bd62da05986b7fb733ef666fd387e39dccb91 b16e1b9bce92e7e72834af0964871ff655d94223f297e2a916bc054b0c4f8a73 b53dc02837033e165910df5ba952c0ba9c3afeef594666dc0994bc8cef844cef 16e983fa02cf9303daa709d001566d3683db349cbca4ecc9677f7da6a6a873f9 dd6a59c284336c7248af37f73cd62638b4a1126028f3088f7809093bb9f40c43 ccebd0ec548dfe03c8576949bff066db53c08d2ea96fb7bc3eaa2e35bc4e549b bad4168d208766a192af7fbeb089e80fcf3ba50783063be866b2afe7f2bb392e d45a233a18f241b0ebc2e27342eb7d059257a766409c5471a725cb13c4331ae4 99dfd92c18d6d113ade3042702cbbd6bb9a79c0c7aa934fcba3382c31dbd7e7e 62ff7d2b14a161f835ba981bf2d97470a2df1438e2c1252441555a898e1e0aa7 ae772f8ce1d252f53aebb7307c17c4e0e7d62f419acc3a5ef596ac80f5f95d49 95abfd3722194601a8532044be8d20e29d027d0e2f4055bfcb51c000439dbfe8 3ea674df981be77af3101ee05efbada58aa84c552b85528c9e96137b0b317f9a fce584e406a2166a6e4bc7576e45ef838e4ce27d2065a45a19320a7baf163f01 0def1fecee4b5330a6488df6f248df22ba92af84bea002746a0b647e50941c07 d692409668548677cd700cda6f35836b205eec4de848518b6a883dd1270839ae 7e1d912ebaaa566002057b1a6a174539ea9029cbd86342a8aa3ffc748a36da7b c281754f3176f07b395486b21e69fe80cbd853438f37a25af70b8becfab39fc5 d0accbde0cfb2474ea79917c09a5c6948393d625f29affecf51bdc3578e93e67 0a299a649e18631ec310b45ee09d737eda2cd32e6ba100ed3bf0984ff88b18f5 e27bee21a8a3745f69a8bb71b60907892f43bdb270af1d4e682a28112c80e4c1 d8f5963afb20f917aaad9e0d0f329df6daa524c0c4d80627c537eaeb7c6eb783 0828b7115e8631dcaf475cebb907e6aac977d9938b7739617e480846e8b0833f 4c50e16ebc84e90f0d414526b053d3db85e9c0f1273217537805d9d7fc03d001 82 d0cabb9447631c59067c5656d09ee3badc392c06963f817317a84132048d1100f833db1780685eb559d8b88e804e597d9d3f178eb46f7ea3da4980798291560fdcda32f5070f1f3462d1fba3b8662955ffc6b1249912a84519ed6993d1d5920a459c587bb6be73a73dabf95e3cb87f608ce8ff0d26f62aa02f8469c284f7af0bf15dab84d8eed10d1152f500cf24a358a7c52f703f078cd5fe9fba6a328a3b0988ab8d821c5c0538d14224a6829c88d43194254f8447ec922ade8aaedac07e03939440b9d880f5b78bd216df10dffdfb4562378fa48ae2a9309bd887b0cb1d03f995828e5eba9fe7e2db66da3322b19f035b2f4c79f995523d5c6dadda9ba20ba1eabce9b12cd26c80df1e7724c96371655dd0c5772a0a652e762ba79407290dab34b4331aba23d95dc8b61eebc132eddca71e16f9241b6435906b97e4cf54084977bd6e420fcd253edf6cb11ec2e7aa189b3d02163fd94c3ee64552f6c9230dd16639ef54a3f205ef719abd460f6d4d6667eaae7edb6e87d717977eeb950e04a3eae2c2fe832d539ec3ae4de06cc8bfaf2054597f56623e466190d0fb930b042bf2dff9d73d31b3b8b2d6a713495248597fd4362245d645c3248dee19a5970bc0dba8a070cab0a0b478fe2053c4faa28bf295baa80688954250a2907f8db20d218ef14fb0035fa4f663cbac04d8b78d314bb0d6ab1b3e7a53a7f760048f0c069069188e313020720bbd60614a5d68a2d0805c64d73d3762e44ae6a7b83f79036e1025915823feaaff5696db8cab673ca6ecd4707386202f2d1cb68016b0650517f7cb4fcab6f58bea3c53ee72d245bc6e27075aa2027f020f976f73f76688081ea64d9ff6b730503800af08ceb57529b0ea04e07e994a81d5a4b9e51480a10e08326a66df552b572401e2b2f441a67f71e1fd0771d204981d619351ca0e1e0c0d41636e99f7e3f3007c8fe8b75c0b84e2756b2b86f7785ce81edd3a7c75c507f46b3a6fb8b9c993225254bf4f848f71121baff7ee788e808de4e9222282000cd2dbce4ec700c4b572ede4794dc4ac19dbb0181c698134cb06acc69c0f6abd073b9a139e444cb973a7d0b1954fa10286c28c9b7847d831573c2c247be3ebc9064ac329185935cbf456a567bc6752c8a86e5d89ecc2e82e4913ea4c73d4fe9005b65db02ce67ae8e2080362c4e41bed6228e9fc0ce4095c3290650a053ecc9c0404ff3d1799b999470aa61fd44d06a60c34c08ea4705afcd7d5b86bda1cfa57070ab4c723ff193653c5bee763d016b952bac17ab62062bd60bfcf024f5ff92806eab845e34c83b374a37d962442784bb061defca3f86bb92d3f89fae544ea6f076f15fa92be0903d1dbef149574c9976297e5f0db8c67a2b380c4b25573f7b30bc472f90b8deb89846be7e4458356c5c65a97008f34bbbbb7101ac47aa75b9f089755a98602a5bc148cbc882783f4a9c72c5ca4a9e6209585898497b1f7488200bfea01fa829fb56834b0e0944e4d3491c56948447e038efb74b9530f73205a0464ff8ce80092e9a4edd49f578e697b14f38b675e829ad1ed5312887c5d644707a8d5f8784c1b59a6239c6d155f12b82d08dc79c14b1cbb83476ca859365de00e9553c678d595507f2aaefb206e8950eac162d40e342c07c20a2b3a3e272a720fb82d333c3e66aba6411df3a35f3d1f7e2c2181901aaf53872aec11b4d2d678083859e17b45758983b2d3f6d2c143c3f5c40250428a3d5610d73471884a309607a5ce1ba1db3ae9a61ee61b992e594b66277f7172dff0a517c96e245c2156ef04f75b51697dc2d3d55c017906ba365db248774833538edb58a3a9031de8b2150b2cb1f8f9c8aec9e7514e0bb38fd7e7dc5e429f4d10d3a8591b8d4200a813e107f08aea7cb9539c59d03ece09abf6997de541c4e81d0ccf68e3058d5849fdd6038a6e0af5d7f8e5487997b62b5c4298ef4c1e7a99c8acc37ae898074d0a6137049cf07a1aa9004bc2600b5a239d79048b835f86cd51906a95a4e0a336ae5a330fe026d96b15011a4b03a514c66687aeaa0c13fe27e3b21ea3d04bf98bbd088502f47326e436baac4a794f1ac63d8657ed66de24c852d6e7db6f548ad4e1b8bf0f2e29604974b48be27fe24034f8609edc3a481d3b298b4bc5dd67e5493258a80a667e230413426e0d82f6a29d240977fd49b506603bb766643f5a23791a57c3093b5aad541b63860cae683927e6c642afa861403d106e55fc1d97f5ca8f80590d6da2cd340900d6ea679aded9df38c442a19501f24a4ffc4ebcb8cad7cbf4760bdb4b6a1ec37f60ead4963aa29cf94a8a817df929d39b161454025db9ddddd70362a33d3733aa874f0daeb710796b67d898064da0a20e8ba0f69e21fa376f220a6462b340c0e40ddc53357245ec302f85822f65d839ab7e3737876f2a488d5b0ca65fd2be689044f32ec0376f14b1a8e991ba8691534f6ef03708670033beb00e1c8cb1df6caba8886e7c616352bfd4dec0f7736dd60a7c9572e1d9a75a2b210029bbd260ceb701b099acfde721285fb12c0264873f43d6d22bab3e3acfb86706d51db3b945b88cc2bdbe7f79322abf30242e551c588d53090fca816f2cc84a05149ead4160ddbc28ff63ba1db4e1379b534653528045fa03c329bf0497f1210715a37e457b737a053bb4bbf79b8467c4f08d1a4b710f26c450db42d2ea5cff03647c8e59d58e92ee4a069ac47b2a2b417a345dffd439e317f258219eab3f4b08ba431e34c9d7a500d188d827eff15effb5adb21d393539dbe558b3917bb30b082722fdc84249d1450bee8feed23b350448c9c81c3a55b096c165468f3edfca00d52c67cf526ecf33935770cb960cef064b9fa5daadfeecd2071a3bb944af540b776b93777f1511db5bae6777180f2c950ab7156cc5dc1801b4384ab57781090997fdc8da59c33248fb118f6f0ce73b9f0d704a037ad7f461a0f41a55174f0f02d61bb1a125c6aef7960495b75016937711df3f3cc0988638c1244f256b307104283ef6e1fb0ae4f410a2c008cbd11f7c83bc605f1a944b7d55732fcc175e5f07f61e47a48f7ba75f56af869ec4c35ee8c6221388a6fe8cf903a7f82226730108faf674db9c9933fb7f472401e2b0710516c42cf225b4b92dc9179b5ccde34f0ba8eb0100076d1f0e4985eb3899cdec838d2b77b211077754a829ba5b00bab306b2533aa2cd9161453ca0ebd2c3b01c8e84699d3202c5e366c6e00af61f9b11089a14ef51b95e8f69191b66ede5cf9761928041a325e5b005165947c4c03bae068343da0b8d6787cbb76a210c6993270be23f9bda32420df5ce92ed71e1ca5907ba11d83d6f78a4cf82ba4bba67f680fbfa8f21d3d21542c1a64d096133b50a0263c13e3c9a0089de524433171382181a2382bab9b843c4b6bfd90fecf2558e00051658a22508c135be89e332ff4e9501f859e83cd96332e9244fa4a03a0f8e03d798975f109c75a588b5d17bc9c95e19dc09f94d922a931af2ccdec06cd21c0b2739d739bbe1db39e2da9d6ad72042eb236ad1b9d6c9cb7331e15cd81b728600a0511670c8f1d0f9498e36146a8bfad5bd565ded10c2e4350fca85e19807750ec7433f0862b35e7136183e5cadd1bb3789fa9c45c62f4841c7cf6cf8ac1b5a0cb3216cea185191b2697e15c38c11816ecc530b2cbb067d70588c2a1a469b460865fa32d45b876588e90d397557a83a30ab657b6e7e97478fca81e3175414e20d3f223d3df672806509b676d369bf8095221d2e83cf05852887b8562f27e85600a52534ece6ffec518585369e603468a0bf0e5267291dd2d2f8e1e568aebe2a0c9dcc608708066c80e39112a5f2a8898f8adc590d9063a851d1be1f1771d5ae01aa61fb93ef39d9deb337c570a3626a2f3f4931365376a56743a22ddd35f47d016e12bed89505a3db2396c45e23cd1dfb7adaccfa1a25d9d6afec6a6d1c10df09ea56357ee470be6a4f5126c638db97c4f2a389ddaf883ec020c619562f003b0efeef365236c9ff90c259d2cc29570bf31afc3d39d680a30dc2551f6ceec9d607a973ed9b8c888a43e77edd806ae982cefa1cdea0a0308946627767595ced46001791e317ef3f5f7c2de8f8233bad1bf1532abc1b27d75d27a8023f8b85305203c6ffc6c0beb7c107c45e032ecd135fb9304d7ba9fc145225cbd26bfcd33c5602e86d8a55948bdba71338d5e2891504376c4993f5c428666b77546b0ee6804b08d2de92c2f5cfea0a0be135979305bfd753b840f201b3d4c657571c787b61680910e7e79063b81d6e23261b62694492ba077eff11b21e24d36b12e979d907880b668547f23450358144c02310a16c7ba2cc3d5ae40f21ed1afe6ec48412bf8d00f8d047ab9e959cb708fa5732e59d826c2769c8d39115acb794499bd720094703ddcdf0b0085a0715c8678df6e17ae5d335df411640de5cd830d7fb83fd8b44092121275dc2aed3e52576953e672bc6e8008408c881b61205d29ccc377431900a863ce783e08854cb296570dbcd915c83278a5c06614f447d56df8e934b5e5a09b1fbafe326ac97e45fe2cfecffab74f64d7ae75e31f750f4470b80dfbcedc703af0ed97a656689ad2253af666e385c04e2254184e294e93fc55459e0c8cfde0faa7bef377453960b8bd144e4a67143aafeb93f3895314bc6eec354665d74c30b536717010ed0e006d614d7ea4f66a72872cd91cf5cbe4f96f4005890ce513c0d342935d01316c7c203e3917cf7a0ad1bf1750ff37f146a94c5015712dc08470b8266473b5ff202a9d7b4f317381686d2d58af6f7c8693cd5b517eda879603105c307c62e00a6047378319c5cc013cfde28950f8b7804e126ae71801fee0a5c00004e7ff8f81b803bfd6f47578e1125ad2db84493095f34ed602297991e3ff207bd54419cd577cb09ae5b6316e25ac1c2cfd4f89a3af6e7ccd2a6f91a187d2d0cd6ccf6e0e98928855bc6cf1e0c78abe76a64d9f300a78b06748a9a79d2832808f8826881c1f4de1c981acacce079bce2425207961420e88787108ceb2254a10db33f7ed9abd936ec22b5fa09103c20864c9645c48f459499e2b8de99fec29407c858c429b2495de220fe24113ad3f1979fb6f15cba254c6bbb452a6b6638f90f8f388018205042d7c26a1385115b6d97305e51649f88415ffefda5e9801b7c0b280127828fc62f771da09670078bd53b7a1c0f7ab483c72dc881c43c59c20b0bf443f077861ed6cd8169ee62b95b0ab5d8fcba335a2e738de0202d7bcfcb8f0f665a25bf643d59c84c16b6de2ab3158a64a56e607a9419936b8bdb316937030576c90b147ba80251ff45acd9362deef2347a46e30d16740b7d6f44263c1c1c0b6540e2983ac304aa152a28690dcf573107989b3c7e72bb0dce9d9ca747ff1c07b98c8caba334ac334c77ac11a35ac3dd042f4a682697b7e21a461fef0f584004b8b77c4f84fe8901f7ef7b1b469faa05e1850c3de75c389561b70e03ae8b25087d60095c59766172690ab5984edc8d09aa26858b9a7515eb67f12c75d6071b061182c4a3db65334ea499f5c0314f794fab84a4c9ba692745fcb93726666702070975c3fbd68abf7d829e8f011821090233e298fbfddc3771c4a32e7789d1870cae55bb6725c3fe23ac65a3021590e3c873db6eb499b13a4a5b9e1ec6c986e70e48008b04e7fe7604aca5ad73680b55515f32935b2a9af9dd3e4929587f65df0de6223ceab75eaa447f1a5a710b6acdfdd84f85ed4555035471600b9e4ec5e404861b299295881bbc77cc931fe0433f5b7f3bf59c247e63968820e1e9f601360a6b032bc837e53a6f362f0de6fb58a2631e3a5747ec8dad4e263b5e8349f2c50019158309cd5f64239982f6b70167bc5091919879289bd552afffaaa14f43030fb924ee73fa988566e6c47dc603e1a6e88ccf2a0a80ed55302b6bdf94a605fe0f32f7ed12028f5de76c9ca6dcbde595222e594c6b93c8c8be341d6c49f513480ffba7e0ac8ed96ff53aa6276702b9709dedcf24f4c2540a5efa8f5125012a1d0361c0f92b7e14f49f7fd3011a5ca3d1be0e9e03be1ff94da43268ed12af4cf906a836a2bc495bb653ac4dbc8c11e79ad34a9ef66126536f38b7ff40b6d725140ae85396b4b26fb72f19b30e805c89f0094898878fc48c926e71c13e204321600759fc9546bf3eb84ccc62e20c1949aa5ee092c1897ec237ad1027ec2e576a9700693301c0b8d4e9e831bc050d1ee8f415c0ce32a24fff5ef50b79c6060f4a2e07785956c6189a19763bcbecacb60026d3776af94fb88d76186d569f6011bea60d932c0b09186301d34c3a39e1ed256bd7ae5c05b3c13a42773c3f37eea372a40e8f189c5aded55276bf1006cc0d0a37257056619f92995f24ac70f6e1d7c4a304c9cca712ed6ec96bae5a17b3a3dbf089dfdca2dac6e23b0b5da1230374c5a10ede5032e7de45ace504e48811673f485007470633b160ffcc0de92a5268ce4904446ba2fd351bf5ecded525b8de01e002382ca32200224e47f2997cc9de7e9b0721fab6ab692e58fe49bac57b8951420774dc85baa0f214fe681d5c6e34d8b6063a64d70dc6bf4245bcfee926c5cf78d3defd7f8cf6e6e3941bdd4cf220f27202cf18f1bbf87f6f21abb7daff15222df52b62856e7f7b2baa284687a2dbf62f04fa376b5d93a0663854dc3390b505001368a54755ed4a3cc91f2564bcc1d63207651def03eb6a2b6fb686a1ac11c6ea08e8f02383b21b0bea3c57051f2246c50de206142f69ad7a051b23aff871904a74f971bb016d482a99c30ac8889d0768010d6d9e94a1ff0eacb229ddcf9ef67b5f54c9c04b3af634e1ea46831afec0e001fee3fd46cc10d02225d04d26b34d9599d74298b272aaa1abbd8a1e338935ea0f1cffb6518a9e7549762f5c5b4addc6576a77302c7ca826d49b517b1947d2cc09e2a82a758d7acc912d9de4795c2bd51b988648d7b11b6a1a221a2b70b412120de8ed32cb44db2c43c6016b3767acf4dc57cc27d10aeeda01a2669bb69c9671078be05f0081a399727a500508717896c24b7d92416c673d24f30336a4841ca60adc8e12c888064c120c1f5f8edee0932374ae62a8e0634188bea1267580746106c715d88b86401ba01b5818c63e52107e24a1239effd48a3afae452b050322e0a30dd158b7d1617e54b9c4f239eaa722f670f1233ecec04d5c223af3417966f03e107ead4bc0dfd6a931a6eb99c2d5d6cb7d9f3797902b06e50049da9e31b3f02963cde5e1d06885c6da542ba65f332e09708a8e3bc2ebd5571e9cc45f3ff4c055a41ecd74a6fb856049637c6ea6b50b7272cb023d805f179296b642c9b22450a285218c637538eda7c62bce016474b6ddde8f3f5a2cbab94dc99ce982d47240d34ae96df2bb80a4501514df26fc711f87bd43136896087f8f70153fa75478308dcd77d7b5fd65fdd1980e4fdb1b162af9e5319ab3a7359c436136d89274d68022406137fbef3aa02a430201edf4315dfd91ee7f768bbd0ea66e8f35c04d816019555159ad25039f0eab12b2a244b9a496f689426d224c33ea1ffb57b3564a20739920a5d7cf66ac54d47c45877c86896dd96d8b530a9edbd7035c1722069010cff98fedfbe615ab12dfdc89b954d3b9bf0bf8d3fd7bc9df1f7c1dd36887715081f1a5d6e105ecb8804395cd65b4134b28b2b7d8116118fc8c05b95581a0a4b0ecf90224a639824bddac80460d55ebf8e62917feec850d9c5fed88371708c090ec874c63a31d958df041b9ca37f6bd128dd7480bd26bb33e9d97ab028ba736d03cf471e66634bae963a8c34fe471e8259fb17e6965ff2ebbb279c40a55cf0720b78ea31ec64cb4e9a3242652b016eb308a34ab9b779fd9a3862221698de03380f6cf16e297fb217dea0cb6ac8eb8f22ef55951bbab237407f84c748c1ec6b750ec708a2f5505b8f48f1de0625fd58d4498620345bca8952d3e24067eda38cef0fbf70315d79da519acd58cfcdd0b29ac016bf744bc86b9967c6ffbdf7cf14b206516130088b246c0caf2f4592dddff59800e121e32338839f50d0ab708dd01600366adbdc79a39969f231bae6e76d9a6dde4bb01517d1ee41e0c01efb4b3ad0019354130b9cac0b34dcfd2078af5b1ee29e0f7a04162835cc88ee3631fb170c0c77eaf9213eccda45c0ed9f486369d40a989052ad711708d1935388d99212af01c452c1dbe2b3043e8c32a82f868cb4a2169847ef3cdf231c9e5029776e2cb00f98dd11063242e7c3383454acdeabe2f22b824c908cf8d5f3e62070d696390a0c417e7964cbffa03657bb37e42ffcc64d5a0b42ecaf103af00d3df1a33fab10070d9bf58a728c05e274264966234c25a33a876a87b02075a1c7ab00620e6110030c7231c34312960938d9526b3c949ee0534082d332cd459ee4649a1fbdaffa07e2719e544af834b1eec3eb1c64fd29d4e1c49c9e80910344c9f5f342075b2308efa5a3d2e7da8644a1637bc872d2f5b8abc5deda5f52cfbcdd13d1a5b2f87d05e3926b6344069ab238da7a62638abed9cbcbe7751e15099459ca9c874468120d7acde5ff0a32ab0bc631a11a11820f8c23009b6c3d123493cc37d2a111a1620d3197fc09d0bfaf39b50937eee037703d9de092c0724eb6ea761566893317100a6900ec0eec169462e681798c160f3097d94216b0bb8ba238b112e2593499d70de840aa37c673b2e1fdf7b9e654dff07a556ab53b26201ff2eefcb46dd20f040a395fbf2e10c26be9721b08e67b7757827a0a67b889dbd0960decd7227458120847953fdf2bcc35215d3af046bb92142ec4dea4d008d824926180608f8e281000d02722f6a884f947267d2c896c437fae2f92ac73f2af64d900c5b63d9102f50f7aa5cb7974f26f3ebdfeb28fb7792a2ef9f1ea313c5f3f81222a49bd1da0840a0236391a96c281d360e866a5c72e477bebe911c2a06ac338e1c57d4b2aec8e02b8e0b2e8610ba747e7495c3540d2ea35d673bcbe707fa87df23b016d58b0eb0042ea961ed2d75c79d35083b9567e358ef5c069942456282b1476cc4a4cab660b4eed05dcd63f6e6149f290e830d1197e621ac604830d5ee81c281f443d6b7f0db8b64e870f915ef180522381195de0ed5a26f259592dc693f5a835e6f5899505b73af986f032b559932dda202a583b388fbd4e140485ba385e43b755d7a8350508adf1317b6f16ac0779e095bb4fe4397abb1f0e68ade8e37954272a57910e036488be0f17434b1aeb76473d0fa69f4c7b524db6e1943290d57b935ce3eae405bece6e6f469e8d09e843054a94ed848fb34d5956bbf721baa011f91fb4c66207e2c76d7ffc59104022d43069035679855419a7a7984e397edd38289ed536f50c3ff95bc7305bca5e56967136af04d92f11dd9eae0b3b27f76f719f71309b3f00762d9b3ff2bf30db3e455044cd4d7d570ee35872411a40a7939556f965bc030704cb9b6e07e00246febe622536f6a5a5f0794b292a8b80ebdcf95ebf52e00a07a09ebc9db5847e97ee4a993d5f505f90b659a9d0203470d240771bbf2ef9530d27d57830d62e55cfc7d31e9884e915c5b7cfde2b78c0bbbb50ca4401ac6e790878d3296e91825409f2aef37bfc3dd4b74ce5f2784ab829ff2bd8170e110cb10a93c206549033f45f6a06c7df431d4c4c7e92bed0886ef8a9bee5134ea9a68804317f612e0da0f23075b53d3310000648d33dda6aa770153b0a0c04179e501f02de7b76c59b663785e74eaacf61762a7a1cb6b74d7728016c74742cca5e78de0162fa65a69e56504181391c6545e00ff37a622bcc8f013882dcd9010bab7e9f098085d0ae0d80f43301ed3be3ecb7b6e1a1083faddf87fabab3c78851c2697408e3c60fff2148f9bab332925e6949cac9ba74ab260813e69749ad24d18d378105021b7d1aed602be328141dfc8cf8ff982dda82a5a96c40b4f3ce751a3cc8790e5cbf929b4e43cad2e1af6245cb700a265b3bec4b61d1a8776020a5560a070d0969f33f8629c5f66489e3f8f90dac2f692864b752bdeca029221592d88e6b7409c3f15f25c5768b0edc343dcba5939f3c94565589e8077713b9a0de02d79d0b093cc4b4ce1db3eebe85eb1a44efe2d31ca2ffd9fd780d6182d0f830393522620bdb771278798511bd8f8eed10209b0fb8234e59f1e68888eb2ada93d10113bc04b1b723a36c961469377e4ad65aeb950c46f7dcb9d74f83f3540859e64728f40f283a331bc661e6ba172d75d2e0f1aefa8a7ec5a394b77fe75691e40486cb46057cdc2ea549c85fd72c81886698799115b19d689109a9d338d33f817016577b08c13865ce1f4fc2497200f9bb495500c3bc2b9b9cc1f86fd9a9c0af1c8e4a630031c3b9fdef6a1da4790ea9f05b99df5bb2b065590926d263a530aca8c2b0480ab50647b857f0bc80ee5e0fffe473f59a6c6d4b685fadae2a1b7611f719660f0a1f4413a52bd14663414bc0257002d2fa7e2e86526e3ad5790ee3690f14d75b0abae817c13196e5ec62fed760709a3e579b66863b8a39cbb0ebfa063f0a7cbd06eae1df4ed46c98b756353b18af6b7eef296863a3d31d619d34ee80e3953232011a3fd3fd645aad0e9f2d38af95d27f5c976a5816f76ef6e20dc70c80d31733092402858b465c1cd7fe50b744af8a58e11388e3a5708ab3834ff9d3506d0ea304851928b1e2d5d05677015fb3ec4e9beda7b97b4d2586a34840629c26f637e4052a9bf782c974650c2be26fb96282180550a6d90ddab197e9fe4d91952c934c0d87280b5ca14e21550837643977b73ad24d9cb03b3ee618794cd2e5a3155b6a02b93d5c2053f2a17df7092788bcab93a731c93a831984125ccecaa14dffd0c807df6ba0a49cd5d4c0caec4d670bfe168e4d923741367b3f50a03c1ea1ef863b08b25e4d118fcab730177457e73b5cf4faf3f4443c8521bd07a7df18329a71a001b56ea45a1663e3f1f11d8511aa49e15e3cb137254e954b7ca7f0fc610d26b20ac9b5c43c58fce8e4e28687a1805af6334a9a11b736a2402cad89712dac6e440572a993e0f4d4455a6863e6c6b10a5bd58d5d9172fa603acdd3c488bba9a2640ec71d62f8665458e564cb4523c1a895e096b320c5d5f34aa19a048ccab96fe20f50aae4942be9343cb68633d88f0bdd5ecfdebcdf347a55553a4b97816ee1ef0ae78786d7dc7502f778542b163fefbcb15cdac5cf9c6accfa0daee47d9f6b1b0d233da8e62abbf61c666044dd77eb68f3c5a79eb3ea99755fff33e5085a5aab0ef462fcd4d67f9c7b40ea18434c01390e6d3cda376622a166e0d7b7072e3f2503d78d02fc2c60a3f64a287de1d5653bb05d2da6f32f1b86b14772693d4e12060ea80457812ca2554ed9e44c8332b80a7383337f81e2a94ef97afa9a83451e38049e9d3c04b41bdebe0e1516a2ab8ca0674c707d01c82ced0a7c3102d0254e7707 -generate_ring_signature 1bd71e0dbcfead5409660f7e8c3127a3d0c42fabf9ad6c0d82d88016ea624ff1 df36b0898025fc214088fd247b1b2e2c49d8b7112158410e9c2bfe9922c43dd3 6 c2c9a12dbbd3c694aa2dedff6015a22fd8a849edf52bcc7bbf8a05cd53f6ff23 437bbaa4c47db3859620b779dd5d2a04962d006d16f9bd04a7a6593ba645794a 4a32f65c25000f76a5af3a41ba8bd88d0acecdf6aaa2c55688deeb75625e2de5 f3a5c02e272bf7d1028f201e33c3549631c89ba801b5ad1cc7981424cd1c7c25 ae2910331e8e8b26f7ab2075d3bb9608f666fbf0c4c71063c58c2bfaefd7c055 889845375f869b5bb75e58c169cb19db753ca9f558f806b47c160f1dfff52084 4cd9b90686b4bca0ae584a8c6aa7aedc6d22993c380e03c55abfa047978b4a0e 0 0d39061d4c0467ebe36e13a0b972081e52828e8f0378b1a567091f9a8376f001d04e9629b22965fde5dc22df7180b4fffc614310486dad7f4bdbf66e622f3b07433c34e8ed59ee83e2d6abf144beb911ae28c47143405b67bb2d8d9084c5ad01b5e212f418fe441b5a353876d6278c135e7d9aa33fac3a93733b93c61194e40688bf935c1ab6162866348fb2740f8e67bc33e26abb12b825d24798319303b400ffed5daa25e306074f5a09f88b20fa6bc9ea86615d9e98c2c0a82078eed95003498a2fc1933fc2996c73d82e1ac955948f3e8460947779bcb5257b0fc41c6f08b03c2d4e9f9a437d2437325ba78782bb0de3442c907a27b7f2a7ffc761fda50407d99b7bbceaaea57875e80f26126ad88c8d09789941228a3ec34800d7791906e34f21727ecc3ff11534ef1c7843946469e33d0c385644a082cdf94077d76c00977d380552c2e6629a96cb8793ae9c58a3f88e04312b2760f91ac5d692bd1e0b766a667dbb9771d955572648b7dcca0a75305d3bb370df1e577b291698cfd107 -generate_ring_signature e424f4b58872a89f8d5cba2367a5c518d6f33932e829f1b82ea361005fd789a7 3881cd8adb14d6d3486a135de75961ed9ed7cb92294019d81adb8eb07344173a 4 08af7a24d14e395c22d134f5db91bce9bbbd0e05625a0817e7925c7f5d3a288f a0833c919f066178070650518818c09a464418c0579ad6b6f834b377035dad02 72d51c7e3fd7f62b2c2e108749a71a52e59072f9fe9d9eb78a9bb7c7d04b5358 33f26e4db6b10c1ec6205159c9292d852a39d8d4eff9b34147f9021e15cd4021 b0d7f260e8c049f4773e7a7957656bdb3426b3ed2b77a92963e25351afbab80e 2 3cc2bbfc7ed47c0b84402f29b0634632eefe188e7debfca0cbe7cb48b3cfaf0bc768e20c4014cac3e3ca15da53537c05c5429e04a43060c988663e4fe1aacf085f53f0a55bef34192913942db876affbb3fae1ed5f17e1ebc9f9d353ab1c08014a0c3646e77b5f25ead7ccad33c4d40c75c09fb2208886d077c2bd625eb8430e0ccd2f67acd092b07bc4e90b240b9f1485af4efe3a164d5f91d7dc83e8017d0f9447ba608bb38a1b282ce925bc0f5af88bf62aab2a08983492b92217bbe78b0f5f35a65d3ab8ef7ffdede43a840919ff9cdeb17de0e60ed393712ab339d7e9089d2ddfef4864cc21d7ed3f96880e214c0f8ac8c7319ed7dd5d8152075e95da0f -generate_ring_signature 5bbe504919c3ec458ead546da6a26736d2502e6a05308cb5625c39d40c3b0c12 af979abd7a21ec14aec19f65b49717be0e48846576d34c27404b0df29997b81f 13 d151a6ab8f224c3b9b2bdb438d5fee09275d134d320af24552c67af777d396bf 5627dd6d295bc9bd10039aa49cd520ce164dfdb98b5586dcf38ede1cab9e41dd 1e7968c3440d4a9994dfd89fda5d7f399bd609d7d428562aad4956efddbe85ba 49058138f3cfa317adc8439d479aa62adc11ba92670252048020952d91a949e7 206246ed0a40f7d750ec28040df4d659ae116b1dd5863f4372eda67025952d13 d3c2e54b9ae6962ea49eca392655fbf8027346ac63dcc309a0899e62420dd273 2b13248ac56f9f3bc37b3d49b2898fd783668af84f8a9738e610931e59e66842 b76331e8fac5e727716cd2fa1b24a6c5703bcee81afcd923b5efa452b0d6a869 87f3a2689803f1d825b4b7d9591c4631e2319b9b9582666d23b15e738939a03c 83179f1ced3540b0293518d30fad1b29d8227da77f1a32a19132b638d72d6d8a 7d1ff46774fae090d9433060c2c38b3e0543d2f9291f469f4ac0d75d6b8e4f90 e293f3c438ee82c0bf1d5b7d0cf27f9daede67da84f44436e395aac9021c1d3d 49541a49b7d1df796b7def54c7b0f8fccfb1a6674680d18f67f5e7124bf0a8dd 579e68d5f338d7c7e0e07ac07e0e592c9dcd18b87291a19316e66e57513d690a 8 af368c7d42a787fd8127cdc6a412c802524fa4329af935876cf1599831c42c05e8ff5e0b65c6e46d3826cb2d5534b419ee2c6b2a2672ee1d92b42ce70f2e7207fc91498fa72cc44ae7913f8a6d42e1a2db89deef51c1b41fee656718b82c71081526fe4675e2fcd87be90a1287e71258e618bcbb7db48f0ab80498801aff4a02e2c2a16a5a177ff3aaf50096023b686c547d084d0056b10e49d4fa85864d650a7ea0e3e0a7861c49d673ac53266e9043a6eef45357cd268695f8216e26e97106f79c7cdb585246396d27f3f65a0be7542470769028255c0dd621b189e83508031aaa83afdbef5eb9e86714ed8d8465eacd04ae11992f3aa8ecb1ac9efa8b4206153255d219812b70490ef8de637d6dec98f19d87b330b614fbf011ee6c0f3a02ad49d1178157061716615182ba12c949d379991bd6eed839e105fb88233d660bd20f42b5a1555298475c0270f2b7b47015bb29585442ecc71e7a929153ce7e004380838f0c02a7d3e06b6639fba7d250e1c975d89c1cc3bb38d0b19845d6e504e6ba41fe5436090affbddca7707fc95f60ca3955109bdc77601f06367bad4c04f4eeed9fdeffb79cd8e36fdbb3038c4c624715fd07a48dcae04356b577bdb60dd5fc6d0816a9cb4c9c7195d88569f4a6ba4b1f62dd2375f7add1b85dea22530937b9dc7ab6169a0c897454476bf10e120fb5840219344c4de65d0aca7e55e808e77d8a3eaee0d406039ded391570a3c7b8007344b7ad342f5b20d862fe93a001d0eb127452a0b85cacb931ffe51255bfe6fe80f0ba32c51b5faef13444a67602300b125c8efcf418b312d23e4f15293ac3194af866a7b4709baee85de56c6408a2fdd94d0c7797818c2d2309209a6e32724c9f1e3c8828bcb52155982ae6a0000ee4211f48be5f0b2b869729a575068549063d97f0728e197831c3525df51c0d2d87ea585677e743812498b33595786860050bf07602795102fe20f05a42bb0301c2a59d3d6df160c3c7fcd44ac27b6a4acca59a3163f30295efaecbb3ad3603e09cbac03b271e2fec9b5598f612c1d093233cd790ed9c5228ec5e6edfa61607dfd29302f33b84786c3fb17b6febad25767f153aa2755aa3fb0c3e06d11dee0dd9a22a3bf73afdc14a126872384c64f2f636aac5d8a87db1f9837662f6154f00 -generate_ring_signature a0859f82b14dfa14725b605a1f66c4e7e3c5703096a3cfa833f996b5e105ea0d cc280d0af6534604494020baf8cb120bffdec0339aadcf45b3d97a2b7965d393 1 13f43d83ff833e7b6ed996c43f01724dde26aa6d0ff8bee55b7f2a7ae080c893 1693e87cf56c39b63aad39216be18df69feef1484ef89a9d20eccf59584e360f 0 f2140da73e59aca72c93618d0384e734c49998b2891ff21f4b7cdb6f9cb543084f103fa57b8d61f852da3dfd5afc81aaab78d8a6d82ee5476ad78590b1f50101 -generate_ring_signature 0b93af27e122fda8495915b5038a4135290d292040914c8322751c075859176c d874a4e8dbce217f1f0d503daa4d2833dd8457d2d29022c39b63c2ff18295462 1 fb104b44d767fe74de8cb1c133c2e1d253857eb003c6c63528d7cc67b3f7e49b f39fa6fd80c7a487e67ce834393545ccc91891b663b57ae879ce6ba4079d600a 0 938ec36160d9a37319b125ccd54668b71e2532ae16d932d284c72af1f7fc000453e3058faedb6c5b6f0aa88383a6f0d64c29c370a7b1a59f20c09b3712e75d0d -generate_ring_signature 88ec571618126ea8a58e3d9d6d3415253a7a9e2a4833ab4c68869ccf369a1aa1 0fa962ae711149af59f3923efb7e275ecd525f065faddc47b94d5b77a1d058bd 3 147eef3259895073beefedcdbc2d0373150bb6dfdc255b9c58326fb443cf76cf 48b26ceede8691804ed3476374d36b2e820ee44c0792361a511080de5fab88f0 2798e56eff7a2f665c67a1b615e19834f2095580047490ed3998cfa1a5ea645b 55597215ee22059246d3b0ca241f9fbeea9ab39876dc981ff1fe7f70ea10cd0b 1 37494fa889ad5c6686b0a69f0a573538f5a7b86543dd67d8b960d04eaab1920341adea81b6b7d4e0cb2eee4d56db0317556f98aa9c266c0f4d6ab6d09ab18e00bbc1cdaf5bd37f2958034781f9070e507705e30ae51da563b71145a6d554b40a2b94899419f4553e984c59e918535bb99cc75201467084d8c12b6b97a334d90fd34ec919165432c50f2dac7e1ad1cc7f2dc387df81457153ab759b27740a04043f958618e8fbe419a0161f0ccabecb820943243fec18856b0979f1691e40d404 -generate_ring_signature 7d0e728c17d84fa5a6380b5d42c2874053f090ca9619c72e8f856aa878300a78 406841c5266b911d87602974d79515ee3cdefdc52ee5e319c934adbcbc964735 26 e134815b7a0d32a792a347b2f87e63aab51b90b3e63b281f4832e2e18470e452 7b5604ae9a247eff5c40efa5965808a9294586dfbd6d96ac962167a4134acf4e e7040750c32328c80ac5608f9a0bf591ebef09d0630f5022e3628558c91ab2e9 3c358a038783a448cf3286470996f23f42d97e3ca2cc4bf4fe62ae3a4f974466 5ce0c0f74280351838242547aa953e6ef42f3830c9763f1f201c95e116a9cbf6 6db16ea86470b1e4bc1aba22ffd2f6a309b8c442cbc82c9ae1d7503ff44ba235 996b564c3f75320433dcb6e93ae0c45f1d1bd63268b2dcf97feccdc2ce8fcb02 2a7ea0d2ab7ecd8275ff6c47dca7ec0fa796fa4abad68d20384a1314ff83db12 31ecb4133c92846d600a7004a619e5867595ff17fd4b4c4b6685a34b0a64a212 e065a64af314013132daf153c9d7079292340724999e9b9e0e2ac22c4be269cd 44ae0fd0320230f57bbe081415831d4da5ff3a4c6fe4ae24a2f9721df86e8de8 8ea50a4f60f0c9d389940d28fe7bda03dc202a8187b2287c5609d0a86d80b970 b0fdab7d002af49b410025b061cd946fa32b4a4fbaa400d8b05ad89ec6c102cf f39415c4eff393fecd622a777af99d13d6ada020e6b5f154b0721f91bd36e005 99df2ae81c858e669d81270858e66861a9bebc89abd36b232d2fc70415f32a66 a3bf1ab959108e6eb6044b8d3cc8ef0fac7c56d326b225948974c66003a7a49b 3296005eac9a12ea9a9160bd2d224e549a050f37f7b8a6643007f953cda3964c ad1056f0da4946f740394f3c044438c016bc31ef30bd1ebeb4da58fedf91096b c69e1a9512027cbd747a968593c936fab41dfc0de4189316f49d731dd5032cd5 cfa3c7a19dc9fe7654556910f7e6332cff284d94787d3f1c142213b9ab388aa1 a8a526d7c0cbf45988a265d3f3874bfc16fd3e972b5c6a02151a47aa44f07394 1b4b025ca5776cb79a04b73e1cd207f7bf18f388bb9732279f870a94c20eb53d 9223e36a7f6fac4560bd6a82a48d6af7bc91bfcebee656f6f014673a0289b485 9a8601702f972d76ff9959082fd1ab159e5607070cc24c09633ece1f65d65aba 73980dab5271d0312d3736daa5ccffe20edc58040be9a59d833ab288f2623b38 9838bf0467b2e4c5b42c2558fe202360778154a2df347fb3cbe950d677b7caed 9da3143e8f9e479e66ebb78947eccfd708b8dc2af91e13ddbccda8beab2b560a 20 325e456c11776379052eec1f5e5bc3c6f7e44ec5d292e651f5dce3111b1d4008e6f9ba7a184ae6a60adc9d8393b43ad77385ba8117555d316e2d7dc3fceaa409c9147b5727d66b95eea2ce3ea847e60ad0be4a1a455072fda76748fbc424170ef92aa6ba23b9a63f6ff892f5c536b1e9b09291e7cb1b47cac2657ce7cb1bfd0fc900c4a89f8a6e676fa39538ddf0c8c572181dc12ca66862bf13b0c8db29e10792dd4c1de50e0b3ff1d7cef0a4446c52bea00d794e999ea5b244c87d459ba906b944fba8dd559b2df25967379be7e631f382f8d05659ddf1aa61c06231077a090261bb6cda9b85227b3803db6ef276da6a412cbd524ac3403bde9396e401870b7b851474faa7b4ecf51087fc5a6b56f39812914a47961c8fdfa0cdedfa59e3075b1687f01292c8344c7ac61d2c4cdac3c02ff7269f9f6c1ca5eedb58c1260b0101d6b9814538b5018f5e5ccf0b30292d73560d63c380fb73fcee38788ec378047a6c994ceceea7ef000241d7d9f2cc2cd74b87dbad5d3d66f32fc1a37749fe0148690210f553511464e003e689ac33b8e1c8690d330f66df4a48fcbc52b92f0b3fedd7376e970be7b1f4288ee94fb9c0711008b032fbddd8bd8086ec5d088c0383740ac83379df53bfefaf7803224b8951e1849c31ffacaa16465cded123090108a76ff73d8fbb9d567abbb89cf8166e6ecbb8ae98f282193fe587eb2ab0cc033de4716df0c6c245c5c31dd4be2c89a2d9bde699c0d7c95516dcc9e409c7ad0c37614ca66964cb196d4fea602bac76a60c75937de567a26796a53be834a017013a4600971dcd8df3c4758f6bd21f8ead8ee17ac0f7890b19a0bcc377b172080815c16f420f0b4d55884210f6f153b5ef5ea6f1f663a3991fcf0f25966d044502d06727812ff7f40ddabe2d2d6fcb83d2ce79a65f4883f21990683a037272140911a5462221a79dab36eaf72c1b6cdb249990b52a0fe7e4bfabbbb12fdd1c22076a88f39ce2ff234cdd70e97048dc40fe21154e09df562b78e0768e3c2ee2fa02d774b997d2b49245b13983d2494bf7705a1ecca7dbf7aacea87daf47bd813408b3db0c6a004c47075ede405cf729874015299950c5bf825c7908336ef0d5e6071de715da2e8a476f73bc84f89e39cabf496b41794002d5f32b4dbf5a9289f40938b32016eb8a665e10c59f989cf089e9d05d3d286b2c83c5092d613e56abff006419bfc0d0009cd5c3ba8477a02abb4c9cc6ea0cf7482daa6f3935b896139b01f5cd778d413aa5b83ae4af0566e02135c2e7293b75d7866a273c6aa8f1ee8606058c6992e269c9b51116da6d6d3250d79c18675c2b5697dc3eee68f617420d0be76ec01a40aac669cc29f290745181ed264b9446e08c0e99969d46b1556861091df36faf365387a864db8ba7154a5453de2137a8d2cfbede31418522f709e60fac6b782e3075fba83a26c6c001610e043e7fb2f4d979597bc50f0c87a291b901946aef3ac851e81d0d06bd324f1843e3a3b9993d94e24714e49b24f48ae5960d6eec300c43e9ed42a5dfab3d0df7664552657ea1bc52dfa00874572480f7a904f58162df0792be94dea4ff03a5586e0041156b7fa54a6a52a25f6eef830511062365eceecc8929d9dcb3517da5603b2b01b501c090a871c2fbc55c76e7f380011b86838d1f2e96b18c35634188154f310917caf7c243ae1f6b437e9141c8330e8d60b08f5a9536796602137d18834176aa48fbdc616a27081a61fee25b317d08b6a0d9ad0f238e0af6294b066623a782d26cd6de9d1de327e8d6188090439d0a8a146e88a64132f46bf21699316ee271042f90016145e23e423996f64e43de0b401ffa59dc03f7a2726f8ec61b201e36dd12dd7984b18c9271f284505ab4bc01469891c447c680aebecd3f92ca45b838cf8fa7b2dae97749fa0aa33d5a6df00e645dfb81149422dcd4018550464042f2ff4b6d948cf6bbd15548d39b3af061059f3a4ed82d641749cbb54decffec80798916b3fac25cdfb33d772bff165fd70fd2b11d7b465c4ceadf498f7869acd2253d694e4c88aca2f5f1ad426d920b7b0efac57d3e87c7f39d442459f7563d70f5191fe57e582c635fb90ae1bb580ead0c9ca4bb61b06221858253526ae3683b9ca03f69f91a85c806324f56a56d4dce0a4ebc72bda5955302fda39f081180030a79acce778fd0a881642bd2beca36ac0fbb83309b5f5113800d51783a244730d6b642ce3957a9ac1c8ccef5fbdf156c0291d178d45b21ee8c2ae7030bced30324b3304534924284468ff909e35c08e303241b1140a84f7312a9aea26de5fe87cd7eb189822191f8635e7514cf69fa0803 -generate_ring_signature 65915976dad0b79ec2f06123198fc3a123c37fc4a73773487e857da9a5c54c71 7073df50257577f8b723f2b7268b6d026d172b9fa7bafe8e0d1712112b026e09 31 a2b0a4460179b04820d33421d1ed3c5f984c770738e6e734176c1b69ba5be705 d172bfb94a40f6d5e3a512f19094f112ee1ab673f54ee9968556b127169952cc 39c3b2c50c49fbb9ea7f49ebd3ff1b21c6120fecedd83b6fbc88013dbbebdbf9 36fc7e7fe71707ca4777c30c05d7020fc7da04b333676201efe55e93a1468797 7f1971181921ffd10a03172ec2726b7c95b714214b94d040e914a4a549d24623 2013ffd455d969d46ef413e6cc8114d2b6a25de9e5fff6aabd4f1c84fe28bf58 61e67258e2b7ffab772eb1e68d832f940bfed8d3ea7e56f9fcfe7ea26a8d1999 64e5efb93d450926f1e6115688897055b968f93a6972172719d69cdc21e93e9a 1db41b3664617a8e5dd42df836433ccda0abc7ff1d94590706d506cd83da9c79 722a3ca714a18f10ad273c30a5ed6104a00bed41e5c6bf5f955ab7dd41a49f1f 32367aced52479bf495d60e499e1671537ac2d8711fcd83ef8874c667fb94fd6 d4052c8067abf0112c03f4ef277f7ee5e0c351b245c1f922d2c82bb855d6c341 537ff1cd54e1e52430585be5197b3da8954b631e423beba3e39dd3dc6be19004 66b18195c83e626b6a9c315854420167a09bdb29c02175eeca18bb1996893a0f 6c8982d18575be7948224abf0157ef3f788de7294ab6ff5c1cc6d9bdd5647527 6251ef481c86959a70d700a5ec9892ba415d370dd1fb140eac186f74090b59ae 25642851c0c2540c4a43e28ef53f2923af198546242bbec704db549933ad6498 002c5ef9e09a9065372ec5c408cdf6975b9673d416d78204ca1ff40b3a2307bf abc5cb8512bb1db9b640c4890e68dc3a7e0b492202e48c68c26ab68501150352 0b8a2f4c3e989eedaa91cf4c308738a050b33d28cf23940ed9070a576b07aaba 97d865b7b22cd95e121679e94c5cfb4b1870e4779058d308d565151de1ffc754 b3ed05b092a76c0031c064d505430a15b1bb4071bb70991dfd111e04ec77ad44 99096c10ba7981946e06381fb36672c611c0aa8ea97a3bd8d171a4a188d70e91 fcef5abfcf89053360ceba60f1a0742f6a209b4e9bd3ba23a0491b25b18d12f8 61c1720dc08e8fd3241ed1c0f59562dc5a39337f2a33443ab4f041ec9842e216 cd5d30ee14c6d6a6ac28024b55fbe2c306a0dda2790957ec2ea3806615e9965b 65c60d4eff3add02a38c4876bd36586378abd726502755756b1ce232b7fe06eb 3e7c1ec3ef42e896cc5564c1881ccaa0ba7a5b5e616f069a185cbd6c6643aea9 521bea98888e7645384b810063c034332ea7fd15cbf091ecc24a9b2d629af997 5293f3c7583d6e5f28b5a08282ee05a2aa2e8afc2b35db0ed6c4aab167de4273 a18fc87524c2eee2b4cf3c001ae2969837e11bfdc9e135cdc09a3c5854b3a04e ffbadfc27c1d36aa85dd83ce4143d666a9fef3b90820373fb96f7976117db103 1 e1e031054aba103d309ff23c6e1ef6ebfea2ecaa4d134b3c5a26e750bb5e750bd1ec3efba130c1c0e764ae0541877432a9a134890daf637c6b7aaef90bc5c70e6df8ba664ccd004f2af8ee634ce569f6861b8911ae25c13b12c3225b2f069b002164371203234771933053189f76d06474a90961b500a796a35d176efa9bb90019d3e595b90e8a646c8e1e5005ab83ef9284d499d6420e069cd8b78138137b03762e1d0582f50946687193b607f292c96c38ed1903914cd771f45b0da5514a0a1ce1c47fa9f5745168d44cad0b642c7f2d922bfab2dd1b502cbc85d4584b1d078d9890022f7b1962400bee36f4624944f0b7f995ccc87a6dd96dc6a430e65a035cce61b1991b4de039865a7b8fa678ea585da815b26baf5d60ee4649c8e89506ecf7af186c82e68333db8b420769bf255bee1e53b5abc0784724672225658707449a66e0d7eb33bc9cd9f6b913077bbe76c3c1ef2f649fc6846900a9e4fae30a333186d3af0819e34bec9f01f278bdc83debb2251c456df0b84cde6d16a2ab032811f6d0c95fab9b7ffbec0f53147971bb28d5e70927dc1b595c86baaa15c80cfcd3c79b16e0095e76466d482300294cace148270a790a337a7dcbaa430ed105ca79b271a80af7dd38e28bc97adfa8155bed333c671461d9666e2db21d64d003a7126f26c022d3c1b36804fa0eddc311dd84a7bfc4a0848c701b1bcc9076160646c69abac06425953fa682947113f33720dab0c7e3622e7dcf5a520cc7f56301be0cfb1ee90854f8bddbcb6d63bc83e6db96b0296c2ac97905b1c0ac9188f80e6d6acf6a20a80a24dd41c8c1cad0303200999abdf2b6efd9816ae29b812ede013d5a17e3e2653e9fa4b9f5ad693fe06152b9314ecc9e8b8e32d8331cb09ad90c4b1ee92bc59d66741bcd904e6b204d87ef3cbf135e19a7438970b571109a8702e399026238b71f5a5a458e1cd26b32b1daf994a48a327afa5aad003918e3740dd9dd391ace6d134f1003c570c059241f8c0683a303411c7644d86209fb99ef05878e6643f3f474276b5ce4c1d76037fe50bf7321e995cdcd8f07994e02389e0e5fb41ced19511837ee37e7502fba3fb5e4ab33d2caecc2dd8372cd346e0de50458105d8e32bc180992a6231a3192a686878e4ee35037a2f36d583d7dc910bf0cb09a6072a5987797255cb632ce8de054bd9c2abe3a772c0eb2198bfbada57604e62f177c630b9cf78dd72f85ae17b5584946e8d929e418fa9cddef5366ecb00926457109c1d6a920a7c053472b5d54e0c410af44ce0c562dcdb194c02fefea01513244b2838132196da731943a1bce14fece3ec9bf22e704a2cdf259c918d40115fbfae1b09bbcca0f753519b5f7e017a26572d9d53ffa376bd3508595c41f08a10612f500efd9e10ce26dd9545d696bf411b98e4afb80e84505f09b8cee0c021f03aad100e80bc15ee1fbf3d849e1d092be486415da42f0a25ab82f7df576076cc138d9ca6ad45ad33239f334c242aa5b750d5b5595ed6d6d9c94bd7ab91602c6512e7216bd8c8a990a7cc0e5f9a2866f07e3b6e4df0a001e5dd7ace49c91058af2771e18f7672d8b48615989e2c7bd47574983d57aa46f9fd562d8d7442100779eb8d30ad91f5e9543ce5915b47e0b9f8c4a4eb294af64da76348d29a6ca074a7d1d89d95f82866a40a85bb5039a597269d3ce9ebd8f3d6c71e4430382bc0f4eda4ea3b3616a1b94636c7d492a82e9ff868e7f261881fdd241faa5b38f6102e22a5c69f6b69be687e52bd487bbe64a75abb66a58368aa0b4652c133f9a3b00c92c2306c3cb5b8f5f6c60f26c7ed101738c5328ee0cd1c46c10462d2b4dd0078f2ab1d86be6fd2710be0fbd1bf5c148b574802a9aa0038b38f2d82b264a87050b770885637958546d679eed705b972dc9ca0395ee8fdf43c3b1d76d76f3b10785a6f6a9611f43d2f09a82c544f462c982dfcbc32429080e2d4f05a3984bf9075b8516307ed325cf84371a50350c86b1e155c480578a1130d5df634f55cdd20c79df9b61f6c2cfa3e038db159e8b36afd4a5ec5b012c52340edfc63255e0e30f564f6e12c478b0989d45029a975f5c3e5a7397ec3b41408b4bd212735ea697082650d2f76929631250221697812d9f29dfb2f221f868b84b23477f35b8c9dd086113c3ef243b321be5b7a07b1f1256075229db03f5b4fc15aa00b08effa3c40290dfa937e90acde15289865763c9ef197367bf716a79b47c598105b08708700099743822bd5a049bb8be2afa285d8bfeff7992c95a5f28f2749751570cb3320ce4e62649f9389b222370dfb832cef4664f19e1c5dec59bd0549ecc7900aa430b625be6b684221e26509d038d58f25c6d3f32db09efa6afaf9f0313a12cc659063e89654c1d590cff19daccdc5f94675f1d1375999fa39f3dedbdaf2d65fec105903ea624e0d9009e1f14af218f669cf9a97a12672ed5ecc52b746f1fab78b005ed4dd717042c4a7300baffed39eb2c2fb5584d7d1f442ab3d741e61b63b8c90d87f896723315285a01cdc8c237ab2b558caf27883b2250978cec131c8eb4bf0fef06125b4bd986b095a07e4d756b9dc2b8a9e8b7ec749f75e7b030026a6d0b075ec4aab8c3808112cdee7c16fb29154a1ea143a57f45d468fea51171cba8d504075849f5d72b9c8e395ce119889c0b5293e493b59d7ba85cf95963d03989890586d31e41ae2affb2e6a2b96a372866ce646fcee6c2740bb0e4726d601d25af0e56ec3e33fc94286c0c2102cf35a98e8a2153a31cfa5e9f7bdf52225579009006 -generate_ring_signature ac3e3b092c1e38aa072cc4210d9bb2e4ce013216d06cba6049ca7b679bf4822c 317219905c41dfc83057f3a6a0d538f47add4d4931b087b6d488aee4b93ff7f4 1 a957ab83e1decda794a51d72c6b411a526b193dfcdcb97941651ca61fb40a066 cafd4da25098d3a9667da2474b43613aa07f58c8cec919c02067c70b02f18902 0 dd277ae6f3162599b15d772ea0539729ed4b475fe6613262b8e7dc6955e71a0e7fe1ec37e52d3555d84977e85d6584743d60c41121e813a6d1bd171b7be19209 -generate_ring_signature 6107adc2ed0507e69710223b8f25ad15872db003a33231b30b13e693977a9a04 28c0cb6ee37eee1a2282c28676b5b9b3ab652ecfd46c4cc29b8b6f4ba8cbf514 1 ddfb74774075531723882e838680b6087bc40a394ddfb6ad1235e233491f4a7a c4aae45c82526a21a7eedc6caab8558a2bd4f54cbbd11e95e72bfe372bd1bb09 0 a01ff14755aae9594bc222e3c0a1e0f2f57c8a0aeb01e963e1a8a6a594db0e0b83d4158edb1a54b75515dae4f3b33849905d03308bdc2ce0020df8aad9587c04 -generate_ring_signature 2100a5f4e410e5dc62f19a0c5e87184e1d148a8dd98b2466964959ff2381da66 d6d869463615fbf48380f9d11e7461f8cd5bc1fe91fb99f685743aba5ae70101 28 793ef8c181dd024839f3212f25ae757cb80bec1c219db4246c6ee2fa052b6f17 2aba2b63e6491a35ef32789b0c7a6274377cc205254e9bf2f69cb0286dae5523 3fa876cce9035a3e6a4d5d1b7a8c09e2df305701c39878bab4da23bc717f2eed c9e31cf9d3551e3fb2af4fd1608af9627f0a417d3cdaae50424f7ae0ff69783b d0c8613048181c9a3694afd2a02589c944bb3eef2b82b77c250775536449aee1 b9ebf2faf7aa48e64ad3dd91d6c719ac4f765c31b03bcb8fafc869c8c7636f91 e9f8b1b1cac9b84936264a4752dc97d3028a209be61bbe6bde32f8faf5463a72 2e66c163b5b33bc4c675b99736bd883533d8cf58ff92a6c6632cd47898a8060e a993576fd3d80b0925a900c3072aff58cd46dff9d10f166ab52fc57182b7afa5 da828df878c59586d46a4c49696661c3bdbdfa9f36a053d5806692c42e3f36f4 d2f4e92d5e5d70fce216e8b6d243fb32014d8abae37bebbee14ea9b8a3d84b8a 2f1f24c37d5eee2ae72a2dcd3b1b6ad30f5d34734a762fc4ab7b8956ff49dd02 cc7d90c3c1c794b6406cb1fa52f61a0045a2fdfe1bd83636c848f18a100bd98d 481a170059326d8eaf5a6885213b915fac4cb27a8eb5318489a75fed10cf50e2 dd2cd3093ca1a6169b55964de9003e308736bfb3903f3c8f8d6b02f252df474f 7dc1c9c1fb20dbf1c50b213b43df2fc31dba76b9afbee67b8b0faff9a7d269ed 50e9f8e8964df954a2c522a8d373e52d2187bbc76919287a59d9b506249ef137 8902dad485e7e3a7b9cbb784074a11b2f75686b7bd6d613027a0bd91f0a96c2e 33f2908d1fd2d85c821734bfee6f1f08a1b70b82b95a7b592d02a3d09e858cea c70aa472bbea4d8d55b345cc2948ffcbff1f91294eb23062088332a5d78b22dc cf57313df41a81100e9abfe9d50146d4bd97dee3a4633db1d47cda8874de978e f661b18e8576915d55a6a23e8d201ed5286a576f2c81c2fbf4ec7945e9b54f7a bcbfc29481405ef6941a8865ff80219df8f16f972e6db14ce5ae1f05aa3e04ff 1c5b932bed93ed0c1b0cdd46325126b6049b4def7a3bad6b7edbb8c6adb628cb 089949b4a4c24c2131989b09abdd0aefdc12c1c1f62ae87e04fbe0e9da567544 0da2fd4af5735a1b90aa73c411039d41dc2f61aa1d1ff7bbb76ad39e8e2c5082 81073bb6dd2b22897882b3c9d865bec7f568b80ff23cdfcc22421fe9771c3395 5b87191663fc83c7ac5c0a1936e414fa357bd8c83323c08c5aec0c65be085458 6a668a19b1bc83605e4aa62bc2a17c45bcd68210cf2b05980ff205babdeb8508 27 648d6924a11369fc5d2bade59f09b707b28e90f1dcf89e52276d069679ab940dccd995fc380e89ad64efa795a244ef66441b633d286b3855a4a70ea63dcb8e0372cc9ab9bb112dd6b596eee8d52ab50cbd5dd7fe62c03fcb14554ab4482cb60466c9a0565eca33f75423ee4ee506df9de4d2ae801070b8bc3bbd0298bddfc300ff7fe6ec8a3ddacea1ee7c658f08bb97e264a944cc17bfb2e5212be4ee9ce10e329f88c610d9205a43f5b690fc17ba426971197ba0b7ab6717503c9b50af880407d3702cd16495adf74d067f3c67b51d29f3a130cddf8ef785af58dc7598830a0a7fda5bba20a94a7dea5262be385b66d999070b3e542ae9b4cecb64793c8c0d06cc61b0aed2017c34529ed19fc056500db5030dcf684c5582aaa353be6e3c01fe0424fac328567920ce05662c2b8e7ab7ef3e4acdccebee49f3e00e8bc9f70e30b829d77645f9f76db0d2da4d1d6343a3b31cb1338a7655f0161127b74c4b066de546b7a96b09f516c2b71e0cdb3b955ca4795d385eb223f3f8a0650491020683edacc93b3ff82c8b14e468615d09f702a6a82de37d0e09300c2c8228513b0d10225a10d16b92d46b1116dc764eced1ebe164eed912b2dcf835866038f6be075b5fbd663b44e16b44c864585b74c199f8c960dda5a456a7c471233d806fa8096e556b365384956823c73ee290c0c9fa4f41cece32cd60b72c26e9e9c7b6b00cb9c01f645e586a2112cbac077143f9f10fd1323fe5612ad0fd868ad4fbfa0a00b5db0edd482c8f70ac15dc3ccaabab86f23f389101524a48b93270901e0a59052f727a159f763909ff10073ebe1b612eb5f5d4c128a13285c30fc4551626d706e514e3fe8c0c410a003ec217ae1188e3c8b317030c21cce978ac9193caf00a0501a22913a76d47f5c6e6e53a88a792336053e7e0337d3d405845de0ce32d5b06f7ab9fdb839cdd10956f59e9a4b595de1558aa57dc305e80a13a781b209987088f2606e5bd4caad396fa2852a5479a5b7068fedac6e79d8c68c97512315ccf0faca367b8616b7e954cb434afbc335e7e01ea7d552e77178b1fc800c771b79903ac9711e1bd0772efd34c41c90f9bbf384064fc049e431c6413b23bdba75d3a0fcb79ff0bb0901fe0321fc41c70325a33b702c56375d9bd85945bc7b833c32200b249c959d814f048be4c7c5414e5fe801324fdb2a660240da411172197bf4b0880627bd74f967b4c15b2d289740fe4362777e7beba012ba7e3c4ac088ea35802a5e42e695d86be71d3c48f85598e2d547c6adc27925b16a6e6b6c51dd29f960ca5574519107556c3a36722dfa78fe6f217c9d15b5494455ca7b7813b3f49e60b19dd6f01184120ee68f4bb80cb7b3c043b32ddf0c8e67d70e1afbf48c0252a033b410a7913402b87054b54b759ca6bed3acda99335218077ce1d5549b81491089343c05de444ab18a470761a6a19e918fc8538d7b5b23c2d6c76568e65edbb09abe66447a7261b689a8c5165faa9dbc6fe384980148adfe99a4e7222ed929407b71dbaa0a5c448a79d80a085ff5035a9ece54ea89a353f5ca61a0bf57703720e6e4ac852485bf9a029e6c24fb973f3f3840a6b532a597a91595f89441cf4b60979b40cc1d175558e2da2995769b12ca6bb22444730601acf9f2d575baaa91008912e2592f41b4916a9f9627c1421864e3f19ca0eaca32870e47e402c4f114503e2d0f6dfc6d1733d090cc82aaf8ae82b892fe1da599acb3e8db2d3eecc14d207ed7cb97aed6d6aa0fea3d7b6f314639e092a91199c9b77eaa9d1c363c0857d06f572aaaaa4991f7a55a9f036c373fe299e3b8a85cad50c4d55e01738acfb1703ef4f1bcaca154e2767f56f40b6b01f60e76072fe91b1e8a0405beae04e103c0f5afd1f9667e1ff33bb40fcf6bcf3c760201e39912dd8149cd73abde8723d2406952d166c303c7f822195f59d48e42e1926b6a35085ec3317c1e9d024498ee90d3d45f5c3aeb18a6275cf207caa116136dba92954a85adcdc5d35bb2a4877e609ab43fb2f51792862dd6e42d2ff5215f8599774d346dabe90f1b1e6ec30f9180cdcfd1d2c93e3a88a48ceaee0bc5f231be2976529be0fea473c6c9b0e7910a70308c2cce6d49d0441b6d0869724f81c7cd4e565a55db9738cf4d52aedda5d6502ebb03e4d191fa13c70901e22daa333c2eb7c259b44798303ae974375d0b46f0e11b516dbdc8fef6b5eefc4923aff4483becdd5d2b8bf34ce8cfd8d0cec5e15063a5d0109e704ef7dba81eb3d27798cd9ca77d607cf18251302eeb0096795b205ca8e9ed46f8ef0d81eab9316bbdb265d7d25610239e52e6bfcafb598a01d950ad2116b4fa5c436676734d9a46b3a483f6ed2163237bdcfc30f343e74bab739015938ea48af543ec234debe7061a50fae07698d0592606b768d1368b9d9ee970df52a28cc4ee6ed16ea9510e55394df485f0fd539a2ae607795c4ebc3f46c3307f6937b8f648d034c662f2343763d8e77c44c3caa390383d3d57c4de5169eca09 -generate_ring_signature af71df96a0ce2242af718f9f36bf7b1e2e54bf52afe541127dc2b87f6ae259aa ca8423fd0e91f8872e0a32bd20940a577b0fe5939ff8c684a67587c699407cec 22 1258734c3315b929610f4c8215a30412459f294317eaa246b5d8867d12c69363 aab540da690563ec5971895052762a8e8998c754f895201694a17c476dd598b1 74990fc817461aa56686cfd5f453b41a69f8aa901fd4e8d4d1f1a0b5f6ff8c91 be833b7186433e5d760182cd8ab18751036d1b0e968396cf61ef0d903633991a c174deb550d5ce869ebe3ec05bb8fb582cf4e27c35d5c190b7da5de77f28dcdf 8dec97e5c5778667e0ddd106fa037ed7c45eca6ae55c3bcf25503d41bd807fcf ebb1afb03b050a92b8e559eb8e067b545f63d8099821e50b1c99799b7d1991eb 7e9eca4eac87463a8a0706af0dff91dd9000aaea65a691255bf3d4891b55244e 51b1223f80ed22dd385f378a33a14111d712ca3018ba967a587bed35d8b798cd a1c58243c6446bfc860b55045ec7607dbb23081b72265365fa90cb90eba8ee23 41910bf9644f3f9fe19c5a219d3b7f96ea6168133c19629a2889a73de302b5fd ac0940d8dce80710286d7b2122907d0c2a8cbebea6493e44d3ebc88b131e98e8 e10b2dceb0a93a22d8c62e6e4f8c7d766c0733316e6d9af29f6a70293b2ee109 f2553f4c0fd73109c497c05a0524430184d0995adaf87df4b35b48dd589db98e ac12b75019ecd593017d62134be2c9145b530b80d0c604f3beedde286fbca385 366ffcc3d9f2704507e10f7945ca35af510d4917ca6a0f320b22bb9bf2d126e4 6e8396f83467caf6c90a774f7a35dcba54461a71e2ee2990df127a5271b8093c 3d1e476c5c17a2a9d875e8e23243deaa16b8ce4bcdc2dda4f11d60a521fd83f8 c52b53a7882356adf4167237b853e0605029776fae814d3fc7fb9f5bb8722f2d 524c073fa8195ef0ca8eeba82d2182b8cb026a4f0759296cfc02e5a240e6b65a 0214ccd35e3432dba23b879df17a8efcc9e2e56826be4d0ed4cad87bafd20c9e 9e572b2ccc41e5595b3c74f6b1a571dad53ce4efdd29e04fb953b7e8e5abd8c4 c379fc436343668d69d752ee38c2e4f989cc013b9926f3c54568eafdaaef8603 7 9992f500931ff7832bbb50126894780e600da117a548d25e7e98850715e9b1073ff3d31215f1d22f9ed6d8ed3c501585a35a2a15abac89cccf016100842b53044852569b4ef3ebe7f616721151bb96f8b7c0605e2f6db08da5fd9028ffb77b0fa7933c55e7ec5aab3f476f89dd578895365a15434b54ea22923185ea4195ba0d9c1ab9b4b168dc8e619b811129c12d6fa3bbf34684fb8b3a2e26e42ea6ace309368fe6a2f687df4ce7e55fcc992d54891bb9b158a9bde23924206fbc198e090649da00f34ae324111749b7a87b1d932bcef1bc86d5262f79abd7dccf6e2600025dc1f187309f39d5d22617803a0c332b11ddced9bc6a0e6f13ab3b6cd40a970c2211ce4663ac96be5a19065020e41d5c9ade02ef4092f178d507e8fc5e05740587a23d310036c02d3fb3f6ebb3b0f89e338d521edff6cb43ca48d9fef7fe7003e1cd86995864359357325a63826f23bcee9261e32429e638ca44c7a26832420f6e9a25520c0658a24f727383e0fe4ce6ef8f664364ceb69b67982119764f0e03d51936fcfa210351d569193956f06fe0945a6c206d56b5f88f466364efbe6c0e32b7ac71c8c912c7fd291d4a30ab857d9fdc38471d0a43d97ea4c98da064f60f4520dfcfa7ee94e29f2a71521489a09f68ea92f7c5626a9f1fa370d18b1de807bfa99afb301ee05041853e33c21747656992d5bd779239249060bf42c1bc170c4ed19720d9ea9ed4b56e41ecf233ffe45b2b5fa0c560fdb493c8ce21a4327d033a0b44c105f1f453cca7526c80f726a77eb3ce460d4ac74a10053d0385517a0836b69d48e9c3622e883d1805c3ec2eb867d34cf15e27cf5a06e203907bdbb50c252ea6185a1c5210188e55c5a5f269390d46de4257dedf6bfdfbfe3542db170319be1b50ddadbec2fc0fa5d51572b5ae48758f5d0c0ca9b70900822e8bbbda0382c27cadb9e52e2e85e62ede48e10055b29204a882690b2c0867d865f210db02a62f5f2934f86c9f102d7dcf8b8542fb1b0e1007e4ed5f2ca7114beedbed8307df73093abd8d1fdc654d4d054f0f97a669312f086ec8f921f88fc80870d6c20309a5ff593f5ff3295f4ead0aded9ae766692912b8781c31d4839d407be330b0eba7413611ca47f2f2f5850eb1bd405418f32e0afdfa938a3161868312b13640b8c2d85e64db452f7f8abee33c16e41d3ff5f5ee6fc92dcfdef484ec26db23d0ef99aac0ea09e2510cf67ba9026c35d25afb3df4622f0e9fd90c83820a710b3059246c6e621e68ee3f54dd0be02cd3ef6b8f7123c339c99cae1415bde70dcf80f8615f3b0d1edfa80c36b4ee4146ab9c5ebbb8a98b4b06b8b9cfb651c01f72f0bb08cf0e2280dc1df8c959689b318c85bf26f36d4bbbc4af33ea8a1ffaaa7970d21807f71e356534536607e1552c248c5a2059837c4ef95f0a68b93e98896d8042c4021f37da1bd996516b681d2e318c812d535e44e0bcf03a3182e5f3f9b130d0e3563b5e520c269ba709823784272b111a0aecdd7fe8c716a94acca97f41e0e8fc81ee5ca1e91977ddbbf4c74e1ba5a5f4fa6e88c7eb572633ae3b626c19c09a55f2a44da0fedff7dc0d24c27bffaeb448e90079e22d9827526a05e6d119d086ca98786b81bab9a975ecb97186697af5d18261bd74334e6c153512a3c9fbc07eb9463ea338eef67fda11fcdcf32693066e3b40dac22b12da3bd874aea89ee0676090323d26a5dbfde226b4c7958d9055e8151bae38d0cc23f443d169f49bd0ab79ccff97b47b3660b41db58351cec4bb1bd93589d1520788344bcd0a0c81f0f270679613d96a024e27530e45e96456833fdcfbc4045c2a93f8c43798f82e000ccb6f485d88d592765ed40c5775a9f6c8e59636d2181ab5bd07657c856bb9a05fa70638081a029acd938887391d4140cf35218382de51b58dfd6549244ead60a33382bcf3d1a98d7d644216b59005880927f4f4b592199ed9365b0feec5cff0f -generate_ring_signature 4512063bb19daa1ba31eb0145d48f7dc9d06fd806ca89540becd2b5e6826c1e0 89a8e66b8da6a4002c7cbd2dd887bdad6a08e3412d1972c1ddba31be29ad779d 4 104de0915736fd728e8c86d2458441d876f459f995c7256a276534f438a614f8 05fd6561503fcb29ce98b70157dc619b5c4aba732aabb0b0d10b8298d1f5e155 2c1a30eb5f25ca5ef00b7806c934794f03dcc5ac58606aec2081f9522a216749 133392cef31dc52c7de272824c7d487327b3320b2eaf1caa827083872002b16b efcc49cb457ad9394617b802a3be8b9292ed7e88c782508899ba39a152e73200 3 c3b6e40232ba8c6aff262f04bf4d4668dd28ff8356bb8615fae335445cdc4e08fa93846c258f76146d969c380a591c88dfabf9e201969a94f250e4035c66db046a373ead5283dfd066616f1295961a55d24af8b7a75384ce05ee3c67103da309ec4c2a1c47d5f92aa278b1ee3bc55ec919e394b69687e756299aeae435c20b0869c137c6c933f2bd088d5180e580f497e25ede00d2c4785efe9c6a5eada7900087147e6b6cce9666513bf97536a2ef3014a63b5a18ecaab4f7f28d98f561850250d2e477dc42f5b6150c4bfa4681bb9e90bef930ffd760e00ce7182e97479e00990e014373710ece4bf82f3c76b344910591eb61fb80ea09aa754ccbc4c4160a -generate_ring_signature d4a171b6e8c5a7881d47e5e059129c134a12972e6cf446760d5ea3f5d9d8a526 4995bf794e131c0a54e6b94218aadb1062967bc29978ef1e04a5ec2a622bed38 2 8a1e660522cfbc1da70e1f269efb738b9efe941435fe7ced85bcb8b2b445015a 52a0a2d5f69efa6666a07a177725d76f60a1fa078e7328132ea81e2a1aead5f3 7799976d0f2f488ab7dfce43781bbf6d8f64f4c17c6dc5e357a98ad83f23a20b 0 58a9a6e5c44b00e1619155aa12e3d145ae3c03fd0eff58c8422bebc7d7cd99021039d15e9e448d4ce5aa0bede8167d4bac58c847c403fe9f0411fcedb6b1cb0ea8715857d597305120950eddcacbebd54a2bb7f0287dade22a9be012eb84f70dfc1a0b459b65d6c14ae09ed0e7b1ab4466f5a7623ba746335357ff157d670f0e -generate_ring_signature af87b1fedee0e438f10aae5befa088392ff4dfae1546b8c8f779fc50ad51da97 90e690a1501ff3a3fb14070bd46a4b4a7175bb31f57956486ca61f2e49cd4ab0 9 35f64d12ad8dd1c71084709e6e868e68b934eb407aae5fcda2505773503025a7 c80dc8d3d3abb474b56a5c054df286f87f5c58881451feb22cdbdae8f3f900a7 3461ab681e2e79261f83d37d82d4982892320fa386cc6e6f5bed308bc1c13d12 46d70d6342274b6fa1e9e3d5b5de51c4627000ba00432606cb4aeb362e25da6a 2b561ad67dd4aa1e38a5fe1c8454218127a73881a206b24862e136cf2b255780 31053ebff5a815b078f8e94d9f0f044f1688b0133cd883d479a94ed066a1d1c2 968233788adaae5fc7466d8390d94d6198074b114e2ec9b52c4d851ba6832166 f668b4ef8431e75554c02db408f359ef81436a0891490210bd509923a7e0a749 fa4407f6eb50927d9c17ca60b21e8129a19684ddb746e4c186b88ad5113dcc86 020bec4c5ca87f397f5c0919116c54fc4fa83c1a650b045ee4385a3d7bb40d03 5 4bce93bbecfbabc33366568fc36693b46840df702626579e62e9516f0e4b9e0202ab6b9d8a6f2823b05023fac29ace4b15b5ed8b8c6bcac06da835c106aeed0c1638e5b29b5572b72acc82611047287fc2b3a59093cfd318331e6504950437094dbeb8961078eab9c99d8fa5bbe5bfbffdd8527c621daa6e7dd5a1a0442e240cfcd5b5868bfa2da07b12bca77492675dc42cfcb566b34a359091d12a1056b70a7d6e449a11ea39fa27d7d7a696411c95f10b6551896611b1d4c4869ca2ece20364266d3b028c0435a1f136ff012de7c1360d18111aa87e7f12aaa71f78d2d8010fc6b0c6e87def7a42d42dbed8834da0e67aa29d84b721396fae096582c822068ff9378df71cafa852910d5011168379fcafa138bf745931c08de73a89a81806c8b08f388321ec17a80a34c88fcb33d38760d46cd3facd1d8f22d511be64af04563fc7c8b44012016059834897aab1bf30b7c779974813b003adc3104cb9950ec03719ff04f556a8a1dfde07d48a6f1b10147f9c035dba6c2a673c3d9ac1d701b121222acfbfd61222a230256ec665ae0f9d83f85694ae5b48bf2d0c2f801d020865529a730c1385a82dcbb77e7fe21421f5dd0b15340bf446945f101b3cc50fc17e4620dcda2c8f46db467c3e3ebbacbdd9758a4f6f81d1343449e7535de20dcb984a9958e1ca69466298ad207e287fc363c19a60779bc9e6b1cdffec47dd0c8e4255bfd134463693bfdd636398d148923dc78a1d53248df34db30bd8524500f91f907930cbd92a38fffa0452d7bc42ad1a025aa8e8b6372617ae071f3e8204 -generate_ring_signature 2d2c11cf68486227b17b80fe6353ae59abf53ba0b8bc75f32a61007b557c53ad 35e426514c73e7c66880bc26f90365d770c31fb753926f24d23848697bfc5a88 57 a9919f6a2b7acaa39c078c3b24e3487a89c1e98f72c6deb29190efe461915b1a 97078cc1079d661dd75108310685f568c813d7486064fc35b2f69b38fe6e94b4 d6e58715307fdae6fcf242a4413b3264d0550ada54a02a233e3488d63dac0307 a6eb853278a50ed8333786b3b15dcddd3d8d784cc647adda671f710a3cd46bce 204fc5f49b9e9247b2245d0b29b32a697e6cd5b1c4347430cc2002ee6682551a 19f3496ab38450705b89e6ed8fd2c725a72ab9e04daf81b3e84f762c6f368858 cdaab9c4d1337195fa27a23ae9beb85e1b421162b0175b01e5022a8a6693a2b5 da64b3606b987f43092b844e787c73e147dd3f1401e5d4de45e6defeebc4c542 8636da56ced937d7942364cf6791b38e8819902811b071d88a7e9f31c9158318 24d943b065ad70668edf4dd47bfc174333547e92fe95a4840aa1cbe343ad8c7f 4f633908fe98dfeaf22d5f82a7bc5c591f68f0a5c087df556804a218ce5b539b 550f1304058b94162f04b4435bfc4190006262a55c326750c93aadc37c491239 2a8a480ff6b6e352d3eaf025a8a2d5f7170e8c72063216af052d14050d68573b a7a57ab7e2dbb84b1b687ce4e37c966aad92e8d63a80a67b399c3bc1775bc570 b1b394508b9e74e44ed21f6f38aa8844ff16856e1179b51b73968582babe8570 fd472fe1eb884e3f57567ba1ec8d6a3839e944cab78e639146224f141aecdcfb 5ca43dd0779bc74b551aa2279219b77dd51b6d84698949e23a5fd73663f5e5f5 4e4bbaa324ff9e710ddd5f5e837df67df9ba854afba57819b919b3481fe374ff f9f50fbf4109310755f80a3920a2bb47e297e4420b5859045819b8f2ab68bad6 4bc0b971d4bab80d4013dd6cf6363ed852b6b95a94b83380eceee179611f0126 9f5fb2b0052f5a19f77eaf98b94282e1f62d09b3dfd090074ba6a99d7d3cf0c7 570157fbaf6de931c767a74e01be9e00dd875477c18a556c93187cc9aa9f51b3 5b581778bb48ba15fa9bb4e03524b5206268bd2ad52aaed0ce60ac577cd3ef94 00c51c2ea99904e5d933635fb1715c3abb4ff07642aea7dbd46624fd9f1d825c d50322d0ead2433b3977ba3dce5964d17979cddda84a13acf5f5c824f110cbf1 bf25117e9f951431febfa8370999db5159ca8511fde188cb0166624773c5f0f9 4c64494a15cda58cc9def65c4746cf88207d32d24b90a0c134dd0ae01b09c144 01032bfc7e3f6e6b0a30a3d265e01e385771c8886aeaa6948965c05728b056a8 060f286518d97854b81a3ab11a55141d4edc97bbbc93fb0bfde999234b05400c abc25998dea92a2cc721cb7bca4ec8890b18d31fe9b130e706e9f9c231933aee 0d462aee190385626534a8642fd579da27e8cd596f90b3d5f5e7fe55407bb405 04042ded8ccaebe4ba0dc9d67fb8bfd9f51c6a43f4dc154f3858b2f34624573e a8c3ee54ed71f11805178141dc71af46d0218cafd72ea5adb3b54ebfbd2ba1eb c4d103d2800689c4b1f2b984ee4b0565f5e214b8209fe622db3f0b8b8fbd3dfc baa5fb8c4beb0204f6be08ef6aa3ea2351a81ed5f169315206a089e7abe8e691 9f69cd4518c0732b7e0d34fdf8fca4f449b391d66986f4032568375532a596bb 2703488d85817e84c47dbfab7d96a741c5603fd38674f9a14dbf9c0785f90a3e 18271655568f950df4aeeef27efb9c59680db900232da356f3bc940e77a661b1 a260915c171adc5c17beb5b39d073adb213ec6206ca8cd6656ad0207f0675c0a f3304b1d21a211a6a64be267512752e8fe7e4682fdcfd42676d4fd7fc49605f3 7910d249a3c64181a1737103cb8a0cc85dec99873b99169cc3995662ad1c2397 b19fac0f51b23d973d0b7daf502321f1abcebd28f6f8abfd75b377db40a0b99b cf3cde5e0b30b42c5b610d5087e32d21bbc819a6ee816bcaf928ba20c45670d0 64f3ce114419a4bc0eec99a1f65fcb2096c94c65ef8a5ca44209c48b6f364045 7f757bdfd7cef93d1ad0b987041bc741ec64dc3cd78293506a5bad36e0926d2f b0155d119bc08df378af49e9eaaa856742bf7783f3ee16d9d545a6064f12acb6 5b29fa21c990c8b43186c1d6d842152a2fa26eb6c0866c10014baac7c82367f4 6fa38c3bea221425d2fb979f5666b6b0214f9fe9ce0abd6d39427dd22bc20c6c a6f86b5127deee81e39532d897d37407dd7348beee9685f0cbece5decfa5d3da 1827e21b68bf6d7348b325fc74d8f65289f30a38ec4f84bfb126d3d94a6e8cef ba62987bca2eb991d123ef903a7377415eb9ac77f244042a87b0538e233fdac1 be54b474591ad0d58a89c6a696abdfa17005717e6b9360c34cd95634210eb8da 78ddfa3c43d923ca2037e3774c9c4c8260cdad274487ff71dc1f5b739e0ece84 6a2184d5c8013feb86b8fdcb6885ce3331320d53f4c02245bcc90c34f4f29bc5 db01a4dfb62b753a5ce1109a566604b9eaeca13064b13ac2fa24b4bbdb859236 de3e15f15f67e61b3f05022c387ba7029432f417c1ed96377d0fbaa9b9474f4e da88bf9678f4d1594a8899ebbe347e7039f5e23287dfa64d5809f0e70a359f2e 098a6833175b819831237a5b135e325dc1e10b0d348a07ca831c60ca31862806 1 4199f8d6909ef38d2e8b6cc6802dfd1ec0d06517d3cd0293c18281e2b81a0b0513f67bc2b37fa38522d657843b27a6cf762ad6ccdde704a1fcb18e18047d370a2d3b2959819f0f1a86234badadcb19ba33c7c7a5be90ae3c834e360dd86bd50c0a6a454360674710395393d161464f7407bb47918de45b965614e1e783b0ce0cad263b58586f566f7fb002c90ae537386e7df7ad41d8398fb7ad0b7ce6b64b0d52832acad8c5e6f25599d4e90079c0f0be88548cd09ffb976b3c4e87c7d9ae0ee1d14675476b15cf87290a5d1a0bc7e2376c7c594b727d1d0d156996bbcb280d69c3353a6eaa79815ebfd2c49c52df4bf46fc256005023845e4bba0750ca80070c32ce3d6549cb135f05bdaa3de1730cb3d1793a3907b5ce1bda53a317ed3c0b05f780848676d2df00a812b2065e02910bab2660ea9525aae9fbce20eff9a306e80a54b06297c79ee6e29d574d8d5d5a6a8e6e7f32a74e13bd007d9cf247f40a116a086c86fbe1e7bc2e0afd9c3e0bbc3ae7b04379ea77cc55c93c279903d10d3ff3ed6aaddbacc75779fa9146892a2d1ea3901f19fbe83c191e634e62e8660dab541971156a0d7e483471a40e5040a3b75f1ba6df46abfef2ff2ce075db8d09065452a13ac563ca9a828f945308a74efb39a0505090f01e02d396eb82af320b14eb31120fcab6efe256c00896cf4b84ef1b6ba19b5691430b6cede209d6a90c63f9810f550e346999233ad550f5f7fee6a55364b26e51fda1cc5804b41c0c0ea52909a62b7f00677cfb7cba9c18f14336c61436a458478b7ec4e06c41f828013f4f7559115967432a4878bce9f861c377d4243162c601177255b30ed8f2720915581c12ce5f7a3f63c4ae2eba416abd39523e85279ade2532a00437f6130c03e4ebf29b8aafcaeff4908aa9b587459670a70623b9d56ba6c1ffe800e17bc90cf5a926310d71f4cff9c117bf5361c6229f960b46245554437b31a8bd26de2802f47f15cdcadeeae51516fc0cbf7d8de2b3bbde11b45c3c7c5cb6a58cc564a70d377cb8c1dfe198a2f3bd9569c6a01ff94d71f3944a779cff6ef278dbe363800073cfbb463b0fdae43f7005111fd85183028ee8644250c9670be031939da7840d08e63e81378429db095cab27595802554754996bdd2fe580350800781d64b80c59b87a01fad1383a5467c719a1c0ac0c0217401b10153b476c3ac6a818e80c0228b215a75ef433a597ae3292dae49d532d1b6812ee9291f87f248ddf47e99a01dff46bc464540adbc59cf39fb12e4b2dba9c64e0cc207a78d50d676e37d9630a575d1c5a8bd171d7211f76f00121bffc602ec6cb132a431c1281ab99e9cca00f8b653d76ef6c38b81a2f3e8092d1de12e02ef2d866aaabfb59811921651df206daa281d1a854b77958b955c68fd4d536f3090a8dca403ece38154f812940d101cbb9e4d85b2d796e84c2124b4bf364b2eb88bd80daf50300e011ee05fb132808493ed12c5752d9834f3281da388bcc75ba5f9b8cfd709ceabf63d50e700d8107828c6f5304fb6fdbffcf874cf05dfe16a7b2a606d739abe3f991dc68a60fb7074f32a9cb2affa4c1dc92a95b19bb4868f3ae4bbf4a0629e3cc5a46dc259c0d00ae4fd36e28036f1e947391326644498e1ca4e7a8f043057d455e53dbf31d0f0c741407b5adb73509e838bb156f801809be25478a7c7433283bae5a5a3864dd0bd49a0a8e9d0cf8d2d0005d6354758e91a66f45ec95e74861195a156dd0b36f07b8076a14281fe3f5c42a18928bcaf61cd064b36465aea87b7aa0ec75ab38f10a4e6fbf370031a9428baa85d5553e3ac0a1b1e4f56ade90d7709cd4f12a4c9b05c02511ac95010885b8a6001ad6c46497ef1c1810d80839662242fbc65c2b700a82b2b06afdcd6c4d253f3a06850ff1c1c04a4879711d9cb7ac593f53c04f5303f03c625938d81cb575e74921534fc7023792bbaca28c85f456d6c85bfccdba08080c7787f2e66a5dd660f21356a281863eaa5f1424312739a65560895fc0bf057df124c8962b5ec2dde027f46c986ffc1a427dd428c73f2575a3dd8288cefc08a062a3c67240e41978f3ff93436f89777b58fc58805e60f683f9bd6faa864e0126ee980fd563b7c0442a0cae99d33565a28a7372469b371befca5466a3846000d87056007539b2f0d79b5f413a182af5794ac9b950f1981b36bb9ba50c376604f2b7bb892a1317ec666afe4b2eeec32cbb7a4044e944d718296e74c1426d940d7997512871325ed1fe29718800fcbbc009a5d93da5c910e60dfe1b2430eb0a04e558e4f769f93616b079c1b5efd25db61c5f71d44f0346db37380a7f7846c60d29362665b7474cc37f2657dc2b7d8e495fc0e8c75158c44a365dd7deebe16b02e36a6dc11b4fbaf9e2d74497ab1978052e26360f8a07c3ddb70f7d73b11f0407fc1d285bb960a853f3d7db8fa5a9b2dd07a664fb62de2ef4c4a5ca9ad79f30046fecf542355b6dbcd761e55eb3ae9ec32b969f64eccfc5ba41c1415d0763b90703eb78735c7ff5372a65236e1043ff08e77170487acd3476b82e5f0f21dc1f040c8460813abf7ace96d536e4061b64d08208dd3855d3d77ff296a0193cfd010dfafc42748e8f44bb89c2c385ead56cb0a6500fbfa83aa2d33c184ab37f2ea307ee97f0ad6899e1080396c7f5244d85a972b7b5dd7b101a8c601ea71e9a56ec08efc784bd9e755bfc7081763b2bfc3241b2382cc2dcc746c1c2adf2e0cddc4e00748b299641fb1749e4444e1df71397a96219d4f371edf754ae1c3bb859130d031b73e213d4d533cdbc6dc0702476273ce93127c95e87f27303436d3ec424870d58ddb13cff028723a2b211d5ab87acb3031f0ea5a03b3ce14199283dc2fafb0a8b8944d0bb6a8ba068b6848bae939569b554099a8f69b8e8aed526a4227dd90f2526c3bf44a34b679a9eb033df96a0815bbbe911c1171b4777f6ff6bf516c707dd2c8e59bdf6110df9f8ba3349e23deb479199b5d9998e61b3e78a57032f6b0ce3750f544215a14a72b434246083314684946b2dad612fcf550ae0e7a60343028c3384754a692098c30d38ea2100a4319067f73349a8156b94dee3a8a6baa50c8a02a684cfc3882cff7548cb608fe0f7652e7c1502370656656552a0ca7ef708d3ede8f5e472606a2b92de0c19d40f5fa4bdd6e24cd15bb0319ee66b13c8560f8f915d065f3542d6fc7fa4b28aec3ee3a92113b75c7bbee2ce7b85caf9e6fb0cf78c919c2c372535678f426be28a1b6aa4c878589e985b62494f52a20be773058c5a4319b5c107d135151e0567345139a3592e071fb96a035d8364416f8353085a4da7d68093a2a6adee33f8951f8697095ea093b5a28d6a2a6a1c09115c920ed3b40825ab8755e2b1a3bf2a57662aa3dc78404731ea989d385b674ac4ae8f0cf3ef4b51b8c82cb3bfec29c12934f0922e45b5ae2641788c82f9378abdd21506f5196b1f1c4e15f36c8268c2865681f266e98a616b69a19882d816dc5ece43027afb68d919b9514e18ed47fa5f9906686ac07b6e15642d7acb17f1ceb03bb309d70be29ffc8f714169e214148b628e014e9ba5a840ebcd48cb45361332f57109f69a1ee2cbd31ae2b1dab0a86269758f5ee68828b9a506539e83157be2d078001a88662286d5ddb163f5fcff2a305132f1c643d644bf829fe5fb2d1d87fe5b0df774f29b7ddfbacbfa290d638eec8e4087ab74f0d0acfc9ca6e19cee44ca9703c39867bde6b7ac6a654acd8f799e81f551b7df6353f844708ad15026a330410202cde7fb5eaafd7141150e89af6e23c31cddf13f430cb7f5e174d121cd94c10a2bcca9f833521405ec35f9bca031710c4405542d936a4588295ad30536aa8608c63f55adb8bf1b5478bf3c8990d9e367786925a2ade306ff8a4f44310bcc7501594cd0dfe9bcee0d004127f13c125ac774fdff5ac9d975e1b773a236dd02fc0ebe3e81b7c3fa5e104dedb761831388b49a08559700eb53be744cfee4ae2fca056e78c14495b1a1be73b257bfcafb3fcef8ec6410bcce57457355ba470b23f30add59d49217b65f06b77dd23bb70a3bf4cace891a1c8ab20dce3eb5889c56db0a912b27a53c3e04191fa516cd32f3d48f64d61e316dc4d751d4e042c07f48ce0de119e10d7ee53a6468d03ee9acaf0bd36219c1c83f693090c039cc53befce1065901717de9718811ba5ca6ebfa807ac0936a5d552670fdb141c12f19911bcf0afd846f5e476747374a516af468711dcd39fbfa923f061489352b4763fc554702d737d75c6b70b925cf3ce8100069972d25124039a5db574b4f76929ad3b4050a06712d01f2d14ce08ab83389ac5dc8798277622fcc698bf97a36e13cd4cb950649193b7ed63a830159a33585539f1d22d1516659fa3891f67c281deaaa888f08583bab5e9d2f4eca6cc65b1eae7e7935f6e4e85f709207d4cac1d5394964930e37874c30b18591a9b420c5e0b460e50f8cf27e929800b8006f2be503763b8b092825230a2e884d2d9a26c140605fb7953096f59968af356b661acb65cae151071996777d3160cf40204cf833cb24aba69d73127cd5a6f53e101b293c3f07df0127c4d55cccd4d1749cc5c9612e128b8e108a0c34011687ec96b5e2cde281e60d776aa652d7f08e73b93512eeb14e14a60a93995fb4518592f1eb197a879dfd02afb1de3926e2c3141742236ad35c5e920d4549a2a8d6a970b1e1ae00db9bcf04f5e0f08df98c8f7432b531492c926590ac6136fd56306f8efe7043614ebd17044deb383d2e219fbee78f9cdf63825ae6f757561654ecadd5b8e28bc4e7dc3f0589b26005d4b159a6a5d8d27caf9a5507eb7aa3f2374c0e2656a3d1c088341106d1c38c1217e72b155babd4a67d301b3a109cf932ea59fdc27811d7c0c4762a0956eb4940c88d113ccc65681de6c854102d77a82c6075f53b7966d5421c424b0a8a85f1e5fc72a59338b59cddb3daa816bf6445f0ef0c02ce87c273d2e5de770751328d4b5869440e0fb58292223886ae2de3dcd35a0350eca38ec536df24330d83204fb31746c6283c8c61efbfa7d99e4cb17c27f3c9572014384610236dbc0b90ebb3c4c6be726dec00443f7e7424abb4edda50598e658b52235eeeed929302 -generate_ring_signature 5f538e02b1461f07c98077400e3cf540b0626000edcc0ee4f184bd1f72604f9a 6be7e0a471ad7fee54ade0d6615b22f36f5ef44992917c3981ef7a0a4d561da8 7 4648fa9cfdae1cc58f504ea9031acb69b269d148e581e01294730540f5ff6930 9dfca258921efd4ea725792ba16046d64831fc572f3dc72ee4fca1100b966ddb d7f734b73581e4fcd011a57ccf613b25ab32c3e61dcc3aee5b65ee0d118f3858 e11aadc5cd441dd0c48f584c8f92cd17d1aff66ddbb7f86e4ee6deae1870f0c2 9893530b57c30c46ffd006059cc5ccf20a35632060107d8ee0272da1cf219b7c 0f5a132da48e2f8dd651651c00026b9ae41d909503fc5e7b64c808201a06d2fc 69fcfadb6d9b4b296d86583324f22d124fd6764ce0dc6331efe72adef2ba1a26 02043a6d949d79e1a8c63c231e3db3bf406bf741d9e4a35b14c41e9813e90a00 4 2ff1b37eb77c41161ad4554270d6ec2d11c25dbd4b36d6d943429fc4e4b52902b081e532196f8597b497d578828e3f579a97962d7ebe328da5b636628bfc5c0be8dc2d0db4e88cccc1e0cef2c384e43c835f58cf2219dd3de0870129082847090804eca45c7059e07b9e4febd2c77c3bf02f5126e8921ec357a36d5fd46ec30e245865416799024c61adba5501d1e6a1810e27ab3baf2013da31949566b397035ddb58031ffdb585eea0fb301b0b67d739bc80c86ad4ec3c809bd49dec27100381761103e82118cb185f2c07a2a64f9676de0e0534205fd79b0c9a968ad9af08ecc95a4d782cbf56b15b7213082c25fba5a16f3999574dfff4cc9703792d6a007943ecea1497d8114b1dcccd008e61c2cbb315444aea5e9c09d9a9f4aacc370d3df96a8dc4987c11f20476ff2f9f58c0f50ff3076e3a4e17d1ad4efc8ac1080edab3016868bb2e68f0c0d2aee37174665d2783a95f94cafcfb448d095c89d9098ec327a99e893a1ddd6b52e7861ac018fe68a2dbb8e7e1a03fea94af644e1201601450698ed41e83ee306bfbe5859c66d028082c4554eb6b4394c5fb9ee06102a8f4e4497abb3af7b99264021ca8d2de203b37464284f672299b3442c7a2ad03 -generate_ring_signature 39bd24a7043973bd7a7f4c8e448e7787d1e5aab98f333b25391f624a492c1d35 e7551719351d2f367292d49a58da045bc9af52dd31cb565a2020f5fd3333728d 253 87211d95088dcae2b1d8db1e408b1eea3050193114a62975b8228d69364c2e16 d8080eb5ff65b1e88262670190e68166ea45b3b603b37b6d7373cfaa636f165e 241d5a6bf98df975d09c3fd82f28daf686e24e8d577632498cf47a80742fa4a9 28044c0d9ec190be78c95859877fc3c71a5eafb944d87af6657986098d5df61c b5380a0808e51b9bb85a54c703ebec3c1dd5d00ca4c776d3a4d88a3b15fe13a9 9b02e613f1bd267ab8ba9f17e1f44dd254e539ac7c4ae51db640be7d43aee526 b4dbc4c9bfc1aaad51e0a4605f40f3e4d86bc7483e9034c60412b099eb36dacc 94af0b33beeef150aac1ed32fab925099b688b72a6d854115507e893d15663c2 7e8e137d9e882f0fb9e8eeb29e607cdb113578b086780a0728e926492017232b c025e69848c70343a238a934746b0dc0edcd2d545af7003bc00e647cee2f635a 80c47dbf387d8fc014051cdb257585f7b85b08e41364e3a6267652db57967cf8 2f16c481aaaedc8ba701a58363f4574d21fbe7d9d3b225c200b7fa9725f15948 8ea6e2ff9ce7991d7764a92cba2c865f5e2a5e258588a43f76e17e41de039808 75ba819f4fbfdff82330cc7afac927e72fdd861cd9243693ba5745891311fc15 23a6a044fc33f8f2e7f8aff58e94283ea45fc8202f1e84c71914073fefea600b 3662f25a290356e28770c62bd711ad6ad929ecab25da3f515a3c053e6212311a 192b423ccbb0d6d329e42a8d0317c8a00db0e5a2decb455b79f6b7d9c0737c06 054535dfacb051e8f8cca57c43ea456cbf563fc80371b8ab750b4d9e660270a4 892ebd8b4d5abca1c059e3aac1cb2543194108cd999debe33e7a65d112760d2c 6627c6b5e09c06f9329edb030089e2bd60212ec499bf9e00ead218f3b4c4a94d 0323b5f1c8df8fc71595693abe6a5423cafcc7379ea59a1fbae58b1c820f5f66 8cc337574d514c3b74794eeb2cfd25a2b0c10768e631bee1c08499ba34b08c50 42ac8825861673461fc8e350040863a663fc03a8c7822d4d97ed3d80369be618 c4e5dc0e598b2d75087c6fe50b411588803f1924ac9cf53882d952b6bf175e20 f93279c53d909208453b55837953825d8fce2dd9d88d98917e7946016653d867 094f2ff19126af7b3c0f71d8f0d569edb7d4ab103a7b541a85defdfd3f516473 00da2bfcf3f2f224fc3536837ca93891dba7254213f5314015eb6be4f365c6aa 99fd38e293ec436c242c51b8d7688a1e0da8b8d9498f568c8c67b7fc7b03ff35 e8b3c064790b8c6004889e828c28e4a13c94de102ff868c0cb30fa7a1b310455 e009ac341d6cc6f400c9aa812eae38b983ff1c3950ec0ad32f54a637714207c8 41d6e1013a167657aca3c785b428458a53751a3ce4f55e7f69d1c2a732dfceae cb6646dda6cc5d0f46c3eb31b6956d944b63687ffd3ad12f0d58fe4a1412021e 989c30d40c19570e0fce6aba0e6789ec2351436a0deac9f4bf373a5621583610 ddc4aaea5c7d9b2619c2cc955076f63ad67beff0f57cd678a43b6aaa935629c5 7137f11a9d15db1ecab14ea167381fa81bd092040a9bce49661788380f5deeeb 92670f852c2cd07a97830d3f4db9577d9e933d72b46524dd05c65325d9a2f875 d053e29032574a0eb93bd7f30dba833b1da8181e2093e3b50d28a5f39dca6b6f f4e4496e938470ad634e61275427d3fb275fcdef514164975d93a320f5882e88 80e0024f178225cb0b40aedf698b8f4b85e413a34e29df3e3adf9bab15296e0d caa36897b70299c47f3a0cc3aa7433b9c5273e803dabd5986f84763492a41a90 7cd4fe492d5fa570d769270c0cc8ebba8fb7253dedb141801482ecc5fea15a98 a2a76ff27b18546a18b6bbbeafb1ece1272a03e2f147e9bce94da684f018e322 90a46155d50366bdf2a2d7e398aea33224a295aa3455fad40dfb4515d365d60d d08d9934354be29e663f1c8e50b6e3610d8fa15c10e12feb4cbfb1d64a07a059 8555cde97863f54e6b9ed852e3c2a26c2ea56d815d710939d89eff70153d579e 265617d3c9973965a217a9cefc0200f5300f3ecacb1487add5e1bb141400147c 6a45afcb5a02430e57bf0af7572dfff1841b9740f5e5052540db3dbd64fe3cc0 1744e17c9cd258c8a7396e7d704bbc4c6c13b06cb92bfd176ba1567772cd4ae5 9236692660d3647d50a61c688725baefcaa8675ee933596c42886d7e57d81b46 a8a82c4536579ebd4e35bf3030b1d18b00f3b56befdcaa94e6e6dddb00b3aef1 eccdd01e8deee3c4ea8fffc8b41bcfe089da7fc2d0d250c8d81c790a0a340127 a1b33761a1691e68c0ddadae7965b912a0a2578b27eb6e5234cfd1f6bc175dd5 65df31e331e3a1f31f01892821485c796e18ae9be5046c567c2f9f31edea5eb4 f51b804b2b4c7047e874db85fc1f827c78facacef789bba5ae0bac362a8719c3 cefbc7087a409c7bfeaf01713590ab8ba8cf200f0e253ee24513acadd14eace3 5e24afee28b5670695148232ff4ac7aeb6d028430f7e52ec699bb8d2fa423543 75ca63027ccfa35301adda5929b6d34db711287e8c20049f04682940a2574cbf 6d028deeb9f33d1e2dc9a7815aed9d46c8bc5ef5a24ee180c51b992d02447feb 0b4862a56199782c3db8d96e126b52966d8857c030bcd12bf78dcff477de2dee 6f5a8b4bee8f6d5f58db280c52b72b24011168a95f856460018c6e942cb93018 a8866f4e5582371713d07a92b9db45fb32f27fabdb45fe78b71175af9b9abb05 f9abffce30898c135700ac2872cce9820f6771733cfa615a271529c04e619c2f eee59d2fd563cd83bf665f28f327f28f2236e0d7c72e034da73634668014a050 22a58e501959e7d96664eea0a947f95643cadab389bd257649bbcb75e52b101b df4b5654604c4fa3b69a9303047f39149a9ff62e5294ce1cb9f03a6599128759 892b52449e8ade9f51e72afa4d16cfd61025a0b852b4aaf0b62771ca4e4d1c03 e922bf917ec5c7181a864a2b0fab689c90593a47591b3ad0ad442ce8b632aabf 09622f5fb64ce4b5d67a424a280af79dbaee60a557cada15a72045f27c839617 791107d5f62b4de6c11a38bcd84a836ac5799cdb4fc736f1f5e14f8d4bb56d9d 3279edd7b2fcacab9810ac647eac12ce8b16994829f283d3eb83f8e13a4d1429 739d415d0f7d8d65675ab5dee02fba52b22baf769fa331477768e8fa21341d4f b6bfcd65dbdb070b48930d06a83706aebcd7be61fd7595c8402da87f09e8303d 9007464c9c734c561094b29f09be140baa1565ed3c09196f399e82b489ca5aa5 be6092d1c575e39349a043b24d3df2a8e9b4938e8ba67796428ad0a7309f808f 2ab34408c3bacd9bfea53991e4a6e69ef5c6bb25978bc25f62244b143f9bf52a 76a04b6675637ac67513c8e6f9d89243f059e3407a7d04d534597bd0c7e46897 06325655016660438374f04b5190d361b605d30a2df103fc4f1acb7e28b7e6f0 5f3db63b41e7c2789a9839ad5fa73abf78d9ee2a6ed7fffa7ab485ae0b1c21c0 481f3b51347b46eabc5136721c1edf697febbaefadbd1d0d72f8f792de0fabf0 a8fb1a7241ea84c5e50739aaea6761d538a042bb33341adcf37e088aa1d51477 10c28dfb14d43cb7914d21f8a5875d6825d44258b8cd578706befdb2bda02092 5fb5866fb47158c2a5bf8abcb52a9c53a2d4f9c0c549d352394aa96b79e406ce d3c12eeb91a1d62781ebb072b272ab1bff02d2fc5c0c8f2af0db54696121c282 d7cf291e972479633e7b3b3c6fb8019fbcc26888e6048d9e78eb63058a5ea47d bf0a952706bbd8adf29a83b681560fb826be849ffbc69d7fada22e90fe83f9de 83b805751bcde4a3174a5f6531efa627a47b849f702d6e7ff871ab792f0cc818 c3e23cbc18a7aacac51625ba45f180e4fac85c76df506553f5c1864fe17c086c 3abf67694d6026a2cdc3ac07aa3281e527594c551319a2e6d9b257b7d46b58a1 382f03e3d26afee7c3f7a8aff90539d0b05521bb3458161f05e472717e050924 1a2dcee09581ba083f476068b6e59c97b3179c2a9759fd5062f6ed03854e6738 a1837059296356f67330d65f3ec669a77140310381bd40b8833038da9ac9467e b786b45a36f1fb2ab17fa149b179db91ac9214a6331990f899b9c9d607a96948 b3d9d31002353f99de917df0fe2a29bbf9468cb39f3ac6f15cd3693b9bec9500 afd4d04039fc38e10cc42211f091880c106b4ee88cf223d674b72691ef04f0e2 779fe4986cc325cf9d43a81527667f5477bc6b0dab67b5deaecc1c78c3b41228 6967bc33b9751da2b33cb217691bbd5fdbc87db54e360c236725df760271a6d2 20b279076b9092772984b180cfa0984e2ed6588a2c970d6a406b2773170a25a0 26109422f9bc97fb8374c815a93ed9634c8140a8d0e14975d7f1e52b66580946 7eeac26b1ba8efe87bb115537a60ee5a2bf6b16a269a518d7c60c3a17b3f89bc 98e996ba603332be1162e011edfec5b73a3a6184918c34d81a6d8b43a4badbe2 f92c62570ca18eccc7778273b29c5c817e19aa5559a5104cef66e4e89dc9ff9a 96ac32b1c4d83ef670005b00e79131cc3bea1253d5c082475e6da0b399b8ec46 13bae2d12ee9264bb9291453cd7019a57958f502960c34e42017c13a0d428ca8 a344ba1158f690728f90eff634b62ef5f2ce2b8485e7ec9eb3d523df9ce9e20f 540926e4afd31183a2d64de8c688960ee63aad78f8265810d24750989af3703a 827ca48a9bb00510c9c89c629ab3e7fa5870fe2493bc6e07b86390c7f893e91f 7724d7adaba57802ed1509ec9030323f08e6408afb4cdfdc05655161b75e1525 a21c61d032a71dc127b1677cdf43876169493bea45add0649cc9d059bfd20f9e 62ed1053385cf71c7f25369a405d4b30b726588341ed822fab9949216c55269b 0012dfb8eebda069b6b178448c062b01df2580dfa68356ea9b18c466d7c21a1a 2b87f48472bb3c7e4d2cce4ff0cb1ae568db0c8731e6c368252a791bf83836bd fb4c4da5dc1f72661f0af5db7637f1b7940d849103c430d63968c320a02eb9b0 9c52d72249c8cfb3821117962383eda8c12b6e3f8d23986ab4afd3a488437fa3 d2aac9ca8f90feecac1c737690c26d3b3ca4cd90841001749b22b16bdc1e43e9 0392f0abb11ce9258379706f9da4703e71454fc7d8d67536b1f607f207751b32 78a67470664d2bbc9b352cb4b14ef9009d9e90bd4cbb0026803d1968a3cd1078 d3c46ae28f78b2aed09cbc451d9923667908a5d43a8e092b630ee4be84394500 89e4bd7046c2f12dd51effb3d2c800f3ab40ea4da49977c1d552a12d7f8aa959 1989bff2c64645d0364924db8586d990ae186ae9f313e91dfb4b3b70d05c8f93 42a1b41f6cca4410e04819ed22f20c9182e7a9b9bd7e8408c41a2eaf48f1d192 35b4663612712abbe565843fc2c8761a1b3b0fff08fbaaffdeb0e3a7f6853638 3daf4ec635bb2e54e527afe6df275c297f7fe217724781c4f8739ccc9c9b2fb6 5afe40899e4e7b817bad34de512ee2077227facc7bf4d3af21ae52fd13526623 b0ff07a6f55a5b48ac6112e029541eadeae416e99a88a2805f230346eed89629 7d6a9122f5df8c327f111e1b3bb934067ef9d0070c3e34681560145cd3544e61 bba9cf327cbd4ab53e16379ff673f14ff209e9f69fa78507504290352b22f1e0 09f78a7968c74f519ec07af117b5937a93445fd1292b3f4c6c048ed3f2ad473e 4cceabfe35704957d8d765ebfbb4b25e7aef813bbb69918e328a82ccc9cd0ef5 4d072bdb1e147c44696d3ac42708497cd14bd5b2053c4231e0f269ab68801d38 fe2581a5df60a4be668cf4eeff3b81b1535b62a8a073663d25cc40ae7e727bd7 201d6fc7a1d66b80f66b2a7e7461ff48c2f7f494ce75cf6a8fd07994315c7d3d 4aacc4df48cb39506847e47fd1de8aecd36644ea9a0a3715aa6410f90bdd17cf 391c4953fe0d607a5fe0757d3b6d89f377eef9472e2df092d84330dab9032c3b 7c0b6f25bcbdfddef802981b87e2d2602e796ddfbf38c612a1ee279575b473a8 7d918f300409fb5498d22dab33d5fabb006e03884a8120342e8c7de43a0ddb41 b472faed5d81a2ad3b5abad7bc4254a17e227e6cf61dd85f808343ca66d04dcd 56046d829e429c14ca5bdf58c1735f9ed29b23d82dc82b865f4404bf2164f78a a777c375406fa5771e7014fd9c7f1195047cd7afafbfbc4e90895f07b25bed88 2c8a6d21d61e1f11e8c4384310f41a6d170302ed1693767879280e6d26a2745a a629e4931c6b0932c982fa5204e98907abe2f36060e8571eac9faf4214983ec6 26b14959b10a5fc133763c1042da576408b7ef0722e74c7c990771ecb67194b9 33c948c923205f23965294543804368f8d60f355d0b812d7ae49100ac3dec0dc 5e334c7ded8a7fc203e7b4b7e03842582d3f87bc6d2e975091c5b1749dc3afbc 9ede74c0b6637781bd4d617a1ce397c5c6d985f56ae0a3a55292139e636e343e 397a7f6cc7cdd493461ed52f39dad6f3250a8463472c95ed9d549d6d64c934dc 0faba779da6568d7f50b60ac6c02ad0abbc93f4da87aa61940a159b4ae7f96bc ff1543693433d98347ca081f5450abdaa0cf029ccd8654b31920dc8630feddde 333f205783f49fa3f0c93d9b97791d7d477a9731131559f998bdfe978885604b 1d3b152f9ab2ede0ca97f01ae350441b4aab27bc99419b2b65a499785c4fb523 67d42536c5b0cae6b6613aa4f2a9655fb108a1cb9c741ec0e8acc47b0f50bb5b 0ac2a1b9379b4eb1c63d742bf7789f3f9ebac237ca1890cfd527762b7f24324e 8ab3bbc165e4d338e6a06221aa9d7bfe583a02ea4e411c5712f7890e87206362 2b570b3e4505e0c909b0b5c8516bf506c47f4a46857255536e9ed48cb668c8f6 c88352c9f9f561fd50106608d456a26849fc9b94b9b6fe630685500c8c0a9374 2909dfaaeafaa66a84f68336277f17295af6f389bad3ef74fa3f998fe1d7aabe b59fe7f21465882cb99b1005442917cccd40b9991f9ddd1c0a5caaec633e2a7b 0af13e35bb870c074cfd20e6488817842223336da2ac422534a4e47b5096e14d 31c21003ebf459dcf7d60d87619e953d60cbac7f3e56a3006f783d7409231169 58bbdc210a0a3c9e6d7a6c1a739e71385d260acb26caf1971161f1ff129cc850 a0c1fea9d30b676be2bacab78a0a803cb91d0d5b7dca34e7414b623d31d760cd cfd7370b09766f1129c6f7154fa9173b72b15492c96ff5423112d81aaab345a3 1c4adf684aa1c063c88f48b6d755b6ff3e234b4abfaf11e669b4b049f3990ed3 10287329dc43f6f23651cb0b6ad8cf44a733a6d62f5e3bf0bb564f9411b8fc53 eae584bd1ed75c877553e40e71f0fd3368fcf5ab466f60fd51fdeef2f3163ad9 20723ccd0d19029a89eb5a705cbf37c67fa7482b2b5cf32b269713f9e82df0a3 4b435f890fbec0afa63df7ff10216141b4cc91f2ef43e45960fa369643490fae 3946de1a98c9dbb03306cb44d1345352df983c0ee81c599642037ec826a95a73 81f7954e59f94dab306d170894f26a64a3ed794e58da478fa4b1c97d3e7f702a 7b17975da7e5f925b7c00c9e49ab67c1c05fdc84ecf7c7c2bd9052fc1792a993 c2a95abe1d8563d55b71d31539718f81588180dbc1e7613ced3cc8883b7a4ddb 4151bc7f598d8b5f51c06994350ce4992cbd5cf5f0af19131f2eeb2930e0c2a6 e6d57d2471095e9557746dc61e51d13d23a13a39c21cb2f5a025e6e57d955319 f6948af76dfc38f9f369132e61275d3931f73cab4be55e0c74c26f3ac06f25ce cac3b7a21c358d6bfa6b75afe0ac3ccb00c8e1e42c1cad83652d21f687953df0 7f80969a5e9ae3b12dae228ec1a9705e9e5cf8822df26f4b2e01d33d01960ffe 7f88d5c21dd827ae7133e3685fef7cd74348ede6ce206f77c48bfbc3ca8d9d16 0137e2fc4336f7f6e5c58ac7de684ed6fbb80b94f9a566e462397185a83aa3bc 4e09fed75cc9ac94c945f26099179285d323109b9e5e6495a5fe70352b47b23d 014decd861cb19767ad2aabb1df2a1a9c39a90ebfb0f9bba99336101a3caeb04 6a8e6bba480e9236ff6d502a670c9c3214af2c2c54d68ae26373be3ea9f6ba54 ee16d722d807012e9b33dc391fa877d6ff8b4c28d8a60bef98407d595fd84bea 01c76b8e1b6a8bc8da70dce6e764ae7c3a5e84299e5ee08cd549452e8a7ad5f7 7f9dddc19df35383423444f76b977bf5f9f8aeca8ee4d12b1c2d47050c4865aa e31923c564f3331aca4266e02a02fbb38082782bf920c73266dafcf5c584a938 e9e1a4257cf560e8c93178a567c31cc5b0da317989e91fbba69e19fdc144a850 d99b31b583a68991c6a729ab3e014e8efa878f4d73428a3e7837b4ccfb7ec34d c43dd8c6692eba9517699c6ecdcb4addf074078005e66901131e3a951c1b0edc 0c4adff4116912e3cddbd8af1e0a54252745b18c8069f00848c86fcaaf1b7aee 71435bb799f392c3fac1d37fbf25cc104123c4200deb7027a25c83f14e1376a6 106deb263504c267e112d2a5a0119a2c56801262370e5005798474cef408ba50 39287346a77fee5f44ad898f17384b4f8d6fd9f579ae9b1a247c0092ea61a459 2227d6bb59ff0f8b917decbf5da7fed3e6b1da14d1acff753c24e8900dc37e74 2bb4d37ed8a566931a8b423ec83989cbf87d6c95be7348060bece8b781706a01 08092eda20bdacec2c77841cd6cc4c5520f526f95d6f0826633bdf037d9d4113 1c2c619b184d8283b6fae53673fb2b157236ac33610eb5b83071d2ed211e0ff0 8e2cfee0c3e50d555312177c397f1f7e28c137f8c66adf4480320f809feae542 91e29a009e5a842cc42d63fb6df9c01d2d166b97128e9e30747cdd6bcf40b27b 015e3a83975191cee58d69290ca0bb424a728c4016bf7d7a8446045f3b445038 0e1644bbae9ce4dcd9310271cdef3f5a1e50dded3fc252ea67d4622f6cdb3f59 15fabd6e07c43e4fd09387cab79bd79d2d1167254e03ea4b2bc14a79d86d3d73 334c7d74257b32240e6cd3715e5ccad9c9854e499e2a0a9a965b05e66a158c11 931728bb4e71c9ea18ca56741a4ce91611399a12d55d24c5e926d860c9671f67 e8f5006539db80c08d514de53e33c196fedfd6cc69d25cd854ec7e620689f605 184a126faeaf6f5a815c95c47e9c04a7944eb0bf9a333ad1e170cc214e0c89de 30243847c6842b53d49252ec2cb97d6ed6380c6a1b51f38aa5c22734eb444af4 4fbbf2cee95f7dcec26c8dbc0c6cd3933761707f72014b23754cff935cb17bc8 9bf3ae0a3eac604de96bcd6601a8c7cf6e4b50a0cdb19564202c56ca566f7e76 1db713ff96be67e88510ada404d59b61cb72233bc848a67a49218fa7eb03c580 11e055e9d596101236c23c1ec2547d92d43609e34f9df12b5921a1eb86d94c6d d2577aa223e766db511f51c5b2e682bcf7bbeac3f1d62688676eae35d0676a18 94249cd701e10680522711d48a1c700f65ee7a588eabd3d14db6b00d3b0a883a d3565a2fe13cac9f6fdb94345fadbc56e9925d6f4e23a724aad18f2ac389d0c0 e76aefa00ff29a4f6b79828490b0cf6a5f1d80ba4b6a261c1e92db2ca2394f53 ccafb3c0c1bf893669cca0d35e6cca664e3506dfeedbe2e9ad03118c97101ccb 2f93eca42748aca9b75b35d4e93605dbeae6899aff2243302b90dc708b619b91 9c5f644fffc89210281ed0be8025d6f26bc199f6acef6c7d540db9074d22d480 73d571d06399b629e06ddb6570e0f8c2bbd1f77e533ae6299f52d41a5f7338c4 15ce90fd29a054ba8b0b6d055618880b09bbd224a023ea05ab2351fea9d803fa a989382b2acb0beba1b03b963333f871eeb5d33d3fe7458d5070ac2792197585 42f972b27b415208048a2b9a889b68da93265253bab8e0734d669999e1aa6d0d f26d0f678d98edbf10d7cc58d02907283cfb4af65ee711da72d049ca2d45db46 bbe344aad8977ce28c67cb5a68877a4d7c17f386a04a5c17616d1180b46e02bf e0ab79f061bf88fdd6779df98d9dda293f3d2795aa708a2de6490ba655834006 88c2506e0a3f6e896df79e698020e240452b32c6fac52bc977b25747f19f64af b4f594a10eede8ad967bdfdc073119224909381b7dccd689adab2fcec0144fde 397a775a0664b932a0d72a7c98af6d08319dcad3c32db270120b0e277d79a3c3 7d63131afe8aea760a530344ee8deb6b7da613e3ddab7f85c3e1f7035769f782 8a71c81729b06a8c8627850015e8d7561040187685d648d0e4099ac2dc090836 5d17966c6bd9faa7cda178016d03b4aa310d78bdf42eee6de5dbf5466a9d6c58 9791a2b9abca7e54d48afe35fcceac3910298b64474e867a3a3a8593201ec83f 65829bef7805b4703ad88fc1a7cf848b38c3e40c0b16a7b7104bcd0741403e56 60512fca2ed6efb77f8299b3b33688bb3e81f71265a1c4fbe1abdb266ead5057 2021516ef3799cc02c7a956d40f0efa1d4f95ff405ce8dfead6989b80b32bdb4 8fd4b02b418427901456dad0cd1ca52671894ad62abfa6f227ee2fb62538aa27 1be3f560260be66bac669acbe18472ee86b338e045fe6821d0de255ea839cb09 d5ae308119a926b3a4a359e0114ee2f938158c61f484dd750f4489cf0e535e10 e218771ec2e03708e7f885f14d9b233e1b7a7d262c56d4f009311a2aeda0ea4c c01c9457d1ad329799a61fb10620f4c0c0b500ba26ff7cc5c3351f1ce90f5b3b fa27dd75891242297128724a5e0f2cb0050f3952b21c3028e29be67af64d7747 8342bfdd59eb7d54a07830ae4d09a0d15064fc19fd4f174427cc33e46d8e535a 0ced66b48f6efd8b0230dd1bbd8c5550e0019d8def29913096e02d39292961df e98da021b8ec628abb0ecc8a6897f3324d7680db16ffb46dbe762c30779e038c 258d59f6081f46450745f0f7c8a5fdb41fbb4ddc0a0149ddc267630c2e677d9f 0f86b6e98a65ab1d035e0fd02262daf83b21c8ff9cab3cdf7afec0c43af3d1a2 25442f835deb1d4ea6333eff39668c84a94e30c57298169935b5afcfc36d7adb 303b1075bc6b497650521361175962ec0385dd87bf864def6f575c2adc3cd0a2 8ad76afc1d698b50abbdb1d94a5c86ccb49b17d49af4d36f39dfbc66ea953fe3 b5218d6cee39e1d1a74e500958b718e406a89c7f468754a744ffa8a39b54e8e1 fdb05ef89c3a19d21e8bb34783a60888a9be53e1e7672d1a3e6f5c6cda4e7029 1aa8d8d92f535ac3bcc0af296a3ffb2c977953ac1e75a25cb65c4f4456bf5ca9 890d1506854a507ae800a7edefe094979769b1e9ef9e376296e7b328830de625 7017bf0e6491806767601a30572fc2dca74d1de7cd3e0ddcf42d15373a04bc8d 71c0cd41b5e501771a72c57a6868c082d4076256afba7569814caec598602f27 376b3198ab6e5849451279c39453c528081337f80ef80e882e954f60b1410600 22 0c9e8f9068544fee2c1de301bca8fdb4e7a996ad3958c2335078618d8ffa470ba59652c9cdadcb367d706b0010e0a38d88d1ba52ba4ebc93322c2a8c54bb010f2d2499194362cf761d0d594464aa63d8d3590b5c4c3f8a0228ec02e9a0012f01b7233fa6f1bc29dd211463fd4224ebe57dc2c557e7b3fc50a8ea38721513f90906485d1ab6cdcfabdd4d599016aa9a0fa8eec1bcf49943292320ca7bc4a1eb0b34f786e8a4dc7b81ca378ccd121c09f2dbba0ec20f7145aefd4868211a3c52040649c49d07147be02435df395ed18daae787448d07671bcfc8697e624b12f1023c5710b65b7de9f62c9e253427b4819e17dee5d7aa2a509854a7d399ef1d5a0d08e01fc2fd942804c21a5ed4271d98ce3686ff2f7c1278dc15cbdccc80168b052ed1a0cba1e14c8f7ba7b065953d6ba340cd56dd8d886a9f8b1e1ef2ac3a3d0bd5009b9e2bb20feab1e0440c989a2e94829ea0f01ffc5dae5b45b2eaa25bc5054467aeaccf6170db78aefafab5aebe9bb3cb240141365a933c2194c40ef38c0eb6707ca694719713d74468eb24887851174a44a53cb8a7a4bfd61e8e051daa0ddeb397c87d204400958cf72841ddfc9cd71268ad962a8b815f030d057348b40d14a599aabd3dcd85755f4fa178b80a97d3275c1cdae97d26f45d57810e75f70cc0ac9ee4eefbd522f56890b3332216c642a7eac26345d1c4572d3146b2b4f50aff57f73e97d7ccfa54e3c276affb495ff39244d1eea42efacd11c3970ba90508281c67476d48e73048674f6fbda6c80c208df1d01c460ed8728947260dc7a80e25e9fd2c2c8155f0fa5dab86d9c2b91c417f18e8e24845646ba5fbc96293d4016dcaf2621d40fd3c3d38e5cbee5ef24ad15c26175152031622b4a7f81bd015081f425e3716c2f940f7d1b73490274fc656243526180f8be43be13c2579caad0c98472be6173becc70707554feb37925c96ff08b9b97de1ee95eb976f915ccb0bcc0ae009b6e8d0f116394e7af83a1ed6070eccda664f038cb4f6779dc20a690ff9296e1f0694ea293627acba003dc4dd884d2f4189997be8ba66988e51057b07b6c9471cf71d25ef0d756f3dc76c4baf1c9de13958174af259d92c47e186cc07e18ade94d882809dd2c577a14f043fffe5fcd7b6944e56cbc675b88f1c77420c92c625567a08ede95f185735389f4ddfc9853e7543bdc483d878eec058860d0c537d8134777b23cec6bb11b7cccdc9092d446cca977dba95c830a4c484a70f0e98e6ea670901800b150877188330eba41c382b14c62b9ca58a65a8a11486d5009d2a01a67f54328c3b009a156b44f27f7ffed30e0b04d38cfbcf091b91e0770e53201f3d5e841bc8677146e61ce3bc647a0acba5d3a940568e0c0e74027a800a1b83c25be06ba0269d9d52cfc16e511303071116c4841965c0de3105e5405e09e7b2dac14b9fdf7fbba45eb948b280d73c83bb8d4266062f4951034e1bd14301e510fb1c7db15e79dc064757d4f4d2a4747f8bbd5fc9d60404bab83c7b34420c5909c6463632342ed87cb3b00d0f18707e37bf1139d1c6e854089e09421522036ddf534c2e5080a60883538b31aef144660e92daafa3daad44503ae5d231a003b56e45558694782cea04c8949dea9ae1866514a84efdf850b1500926feca2e08d4a7c7a7aa1c3a354ad7ac1926087185c19d41df381836df5b743e646c24190f7fa5e3a1c404d8f27655c65a989f97764cb61ffcd0624387af29ed0374b6df064de9a91efda7e504435d7ed70cc65a47b3c5c2e5532453211846d7e607844c0f42ed7301493ddaaf2cae5ed67b5dd1e05bdaf6af277a4001641c19787d6e780d4742ce77456da93ffefbf7feec6a2102063797715ad167cf0d9e5e90f5d4f503ccaa7bb17bd10015444b0a31395fc23d5f62028240593fe2ab9c0c3492dc4f087dc0f63e66a6abd11070ba69045ef80a1f2c4f2cd44e8662330f56ef58010903ea719bc357ecc139f62cc71bc722f585895d0c019709787d4c3d3f86a3938503754d3c9818edd7b0328d8392435f42734a84ef3c2728ff32d7fc84949302670a8179923c4137a24202c0b4ff9e682a5aa158e8172d1efdc523d021ec9f37ef02f16c5dd35ed1b7484920b33ae4ff69655b89f6f30eb1f6864090736761fe4e0aaf8d5cc7dfcac9802da71934f8a9b50520badf21e07dc4571dde08cdfad57a0c9d9dfc0e74ef75b7333fa7349a07485ddd0580ac240e3ac2c78a86d47377700ed83b138fbb77ccbbc17ceba465280bf9f3836f91f8c7eebed4d419a6a2fca40282da21586766aca928221900268c2a6436a1c0d64e18fef0024688402d86ab0abca985171f1a977f8270cc60b8dd5ce454a3b3b3e9231aac844a984a4f151e01d601870e91a3faa786aad7a028f5b3e5e12409838e2b2168e46e17e57f583603404e9327ae88b00b94b914dfec1e54088af2df9d8596a814ba44a0875aebba0290602e11a52f55913c0d20926c23715b97eb54acfdb4032e3b36531567d3cd0d8b18909ddd7000586c003b4ea027095d12d3bd9abb19561f1295bce26cd2e609c2c36d424ab894da43c47cc2deb4f76dc570d67dae88ad482d8d0f93b31a1d0efb1b94e0700d54deb6c840d2db4de8ae397195e29d819a5d4ececd13a5f23f0a5bfb181ffa6fcee5d86333abfd95b691d0d951f8bbe36297ec8607eb00e6110153cd7ad846c15003e8c9e2171a979b956196c175dbee74fec593f106a0219302786f7fd0b4d9e5a9dfd944a1513899a0414ee184ef86af17c5ef7b5dac02ec0d820906640c1acd6464103255ec96f7a76dc90f1bfa65dbcdb5893b979444e609ccbb165a73dc70f88abffae3e1a3786e9cc75840d5243afa0de2d835683ada048524bceef407639edbab890c6fd2695bb8faf801e960c21bb1b5e23aa2e94f05e915c1d88f296a34c4fb88eaa3fee002b9247234a65d2b3d485ef784d7bce806a7db21f14adbf56dcc3615d95a0671ce0e973754924c5ac891e4029523e9e106f00f39ad39afcf02ecc843d9e93c229249ad547f1f6680e2ca27581e9d621a0df918ae6366e80b367edf6e97cc59773d7bb9e3691e167f3bc34cd95b1a8e7d0d800012c97cd4e67942d5cf249ae36192acb01d8b48d04e0cf4475636c7f8dd0dfb09890bb32b263c0cb964d703edd21b4e3a509697d7d2589074b930f4c5de00a9a347be56e0f4c4f59d62e091ba7057ac9520437bcb0b26aa9c66c668273c0ac75cdd7f3fe72d8d0792a87077a018744c02292c603a6475201886ce83d8a300e89304190ead434e518057f5d8dcbdb277c65d247b2d2c668bdaff700e783703ed92ba2a2e276265a5c58e70fe1b418c1219d2411a15c79d936263e26164520df441948bef0404d3d1c8797c84811978021d296b6926d648ca8b28463a840801e7888dfc2c5c21a6cf6e28aa0a74101703469b0af8086caaa0f3bddff0581d03acedba8da5e59f57dd354d413cf35f65479031aa5bd508f845de206d7d20c209755c12bf15a924e6a3a40f4f7846c5d02750bc219a5ae2130b8385ea8449850b3055ee15e2f3d62eb4c98d180b0a7aeefdf485c06623c17768007620bdc698044508f5c444bff75e8e4ac71094f0d380b616b74ff5e813b033cc18c7f2d91006c64b36366f0ff561df09cdf0713d8792c98f6dd17395ac14447381c5b90c4b04d555124609db930c7dfe231909a766ab3ecd0eb32c4d4cfc073a60ba5ab07c0eb52c10613d3ac397920830c13e7e8f6d09293ec1f86b5ed1772115352ac98d0896a47a17992c10ce9ef5e496e3003733353feec37de5105539242e4efb4519021ddb78396bbeec0eac62ec54cddc4865a468c1ad02c07f5d0c23c1b9a85a5200efb9c51d6e3ea194c5c8bf9f4c8d770b92035582679893cff995898821129b02b9630e7419b4166d3969654d8fbdba6f0572849d30d8bedcef91e0b472b21c0d4a896f9e0a080434df790f9284411d11ed1ff5da21552a6f979dd7d738d9cc0865fb58d781eef2cba1b0fba6cbc06932de1b19d1d0e4904118453dc746c1d1079828f0d4ffb5ff1e1c6c3449c230777dd0cc5519dc702ec8720852c63280380871985c651fafef2a7ba123b4a864affd6cce48fc2810e667d30722ff9f19b10115151b154f7c7983deee1e9666976308ff77ed799a9acb7588c2cce94db8b50c61f5a839202c67803ec7370dfc5f3d0619d578fa9aaefe2c6ce58a3eb1729a0eee287dc746f0db3f6131f5aa42a98df719604721ae0ef13a0f0c20775c53d00540d049c1e70e203b672331e66c1e25ec54cf53f46e324671af11422ac599820a9f858b8fcb4e4644cfff99a776ce4a06938a12d1fd04d76bcad793335509080e34534fcc2839197ddf8512b1bc7c14aa01e137fbf0db058762250271e7bed10d183517e6972bab43084f640d0895d03d06675997063cc373a0e055508cafbe0661ff6fc761ddfb10063b2c61b37ff53de1e8cd04f6ac8b4fac4f70a5e9f3fe0da33d92fb6c714e3ca71bf5244f8ff0ee9d51256a0656e1869f28e9f36cf2b700cbd0a10cc20f92baddaca5689dd096fa4168e0b59945cdd4219a5854dc2fde009fc31a0a742be28dcdb8de52ef5cc20e6026bbbc01a5f742f251f189ad853806aad1adb69c2cbd12cc0328d76c696e88eac8e252e873f738b5031efd1002d40817e9282d946dfcebf2af541d66e7ce645cfdc8b693b4653206ca88f2bfb81e0a74a916617923709274e8e787707f81295603f196d1a1c11ae23d10ddec60920f1c86607fb381d156f909d9aa607a6589fb4f6d810e51694f826e56ddbc49280228090e40fb7e170fec7092ccb40c23267ffd1045b21b58aed25734b6a62d47057941a0aadd627c4507bbe86b1eab8f4787bfa62a2dc480a6dc255b94094c210a26b69430aacbd7d4ca446aa2dc8320719fd765c8a616dcff7456c925c7cea70cdf6eff6cc7480f53ac9a183d7653be427ea4addde8cea992f41a90f01d1ae209f3116b823a70cf5d25967c7dad95c637c1b81fbc0ae77ab23684af73fd70740e7b92490dd2d02e5ecc03f0d2c31a3514795668ff3cb4c785665f063ac8e3f506a6a1de911d46e32fc713c5fca9782c9041b4a771dfa4a7e000fc4ee27e89e10b3fe9ec821cd7631f108a1e7a1980ba068b73810829c5768de2441131567ff70b4b5e6138a341055bba7fb16599f8704024761931c694f6974ffce9af0f8f5a061aaa06ffa631b457f3d67b17d946195ed83019826b435cf65aaaf4cf3164e405fe9cc4b25ae704d682823f77b9a6a3e242e26341c51bff6db15ad61945a085030b93baf4397ef103e48e97001b3fbbab794154ff7151f369c24b7d737241ef0d3f88797a365a787a916151cd9392e76f06c0fdd2da2e85ff8e3013168db9e20cace5780c92a6e85bbba2655af55341010fd0f43b76c513b630257a1eb2c20c09bc1cd68c5015a5f73b1254bad08bc84c7100350c7cafd43b4e10c402835fea0f6edef05971f166899baf1ebfb4e0096f3db17dd8f9387e45ed58ed642b3b5007aae7a88dc8491a07f33505c6c850662b292a3f2c3a65c1c9ecfb8cbcaa8be100db4ba3c506df12a9ac8c19595b0c86add4c3fbac7d45df6e91a2046bfc5fd20a52ab6073f99023c58c25bb97aaa51f21b8b94012893b3038b25c93d050c135036d57cd81557832dbd01d2b230b220d0dc7a84e682edca4a48690a017b3fcc504a41c40fd023e565aa6d15e99d504232a1c0f349555b8370d3421acea2e66af0eb82d84175c4f98da1fcb80fb7f56b4f32836275bf0bf790f1c4ad3b17423d704a5062fc8c654285e6917bfc9bffd80e68e665dbd74b5727e1fce02c6cf060e06dd479fed34c5a6e6c085729ba45a34d09c94ac9d7bb648a6b42d679134e8bc08f32ffff623557c9b53960cfcf2cd78fb00f0b4e9262814b24799d9a44b56b002834c91db8d6fc45e36df359360d452aafaff806d588223bcbe0fcddfca51790beb54068dd340bb4dab0f345e3d2ea30c20151d37966e546c23f6bf8d73738a09dc9c1d8f43c6831d30349221018ebb901b44bff7b43d2a174c4adc08d28e9b0240aecc1374723aae047fe34fb5763cf0aad158b50b6525a27d18f6c06d6d7804e2e9d39f7aab825de64278ec9ff183f08b7d20333944401c6b5b7a6ca2a5fd0e862a0cefa42047a48203d4ae4e427e9a6ce04c23c5dfe00fac0fce5eddcf76024f610cad126018f8bc319bb82459216a36928d964d6916170191ca9a8323dd0b141a872419db99500dc6cff91528d582ceddcc245290868a69b7f6d9d812a509a28323917bf9e0786dd77dc22af6bcffa6d6fa908dfb9789eceb96426739aa04e1971c48c9ce5418df5a0fdeb2ae84da5d5d053ce5eec9324ec49930892637064b60a44e917c25a61958de20d994d123bb71c9729d48a8704e1ed199d842500fc8325252a751c9b426433fedf63344ad62e7517e567a9edca5ba06ac1c3e4005c28a08894f1103ecebac79a7308d69023a995c2ff73bf9648235b78e22b2c1064def363aad8282e74f15cbec4c82e705d64bad9188b0dad43e3b4a7a495b1c09a1201eaa5c3aa0e23628051a8b2cd91f170c69a56a4c12862d60351018462f0abcb0513fbdde9caeca851a6973f96e7aaa31f86efe9ff92d6cff98b1bfe2800308fe607f53430000f104c31035f9dd9f21a6baeb63dae98fa0d957accec70102313f304bc6cd2248b7f7dd9b497b8741c0fd746fb10811781591082fad2b570ac869ef721bc8909a06a9d08a665579164c374cb839594c0c1a0fcc4697be6f0e038a34aa3ce5356f99c75403de0f4d18e5524c406aef8b6ca85aad16a6ecf30f9e74b05d12286f3b7000877eba9e9987dc920d7588bdd0fe9cacee871bc3290979947d782eb432f4fbf2d6b0bdd2184ef72a04cd2316ec824e6500d788ba7e079e5cc224a7201e4083cc231b00fb6549627cab00b6d493ebba1cce0d463c99042792d05bbac96cdbb416604809e1fe1b27f7998685d5e49c27d04f04efac3b06061697e839712f098f9596d0cf32f6bd1ff76b7659d1b627fe7b3209078ffd0bf1aa7c7d48bcf4a0f98409e8b01dbf329855783838b78906feacc8ec6d0758091aaa46d79f13766e433b0dfbe42ca183797aabc3c738353cb9181a09cadd2805d9c46e32de01c6725b550e3cad4ab4d5e047716cdab08cae95b953ebf3da130b06cecadc0db4601a60b44f9847379a5d961926095e6c16f9789eae958ece700cf6acae2143dce6ce2ded99e546386630ed94ce2879cbf2082dee3a949d38040f0902a37ffd8627cc9445d11b05e473b11921f958a83f2694d85f6aa6d5bbc60811c26449569217e887bedb2c21672b5bffe6e2bd4842a4787f96dfcccd06f3007aa8ffebbdbd67c54aa5394ef8a3b05c37c0acb19542c87bed99be92a7845004dca73a6be6de8f03d86f78f887610a45c22b375be60c89e3bce66025a7070d00cef35cc273425624bf81552d0c8cdebf21853e1f27d7ec30ce58f0baa63372098fee116ae8e719b0fe6ec51fbf254ce382e47cfbc768d12c6d9bca35809e8308e46579c1fb9b931f1326cf8cf58bc4ccb06ac4ca00727aaea1ec496b6a94ff0caf2a051331bf31151433ea38536c9a64b84e856efa7651ded40817e78bbb6009660cc8e1ccc97167702577ce3c3efbcaf5fff94745cf737379b42e9fc2ddd504080c3abf9c6ec85867adcf51656531833343d33f341d2eb725e3c5e80a3d6b00b90c38c93951fa8d635e24b0b0a48b2777e13e28e58ceba05c7f9bc8f3322305b530f6a457319701669c0b135583c05a2c44c029988bfbd50827e57dd002130517f4b8c06b1b64d89f44708f9f9be94d0e74905a8103dfd8f82f5dcaa168d40a7da2839690c9beda2cc82f8b81467f92c3c478aac981ceb429b3a6a3e5d842016361fcef9522e3fb1f8511a9ce446a37ad86972948629d783ec7df5767abd20625fcaa37690501207d502eb1729f30394f4e1688e7e7e266b85c317ad2ff340ca56d7a23ee1df0c73fb27bb22965c03c5cf5cb04cdce8fd3b1e7e5e05578410a9579eb3ec10a76b94236f2920efdcc72d3af010f6fa23612808f660bf2ce7c0a87af607bea164a8765703af3d16f4df4d20e69f987efc9edb5a555124db54a096572308654574d0612f64676c3b223c8e0eb2007363d134609c8d6a742968d0522b2252f8cc03a25aab5774a64c5df7a7f6711546092487af84c4607483a2d0b5bde96f702df14927b9907ccf6eba88977ec7b24834253dfb6657c351321660848d68c7fd33113431c12089375bd914a449bc95526aa4ae83e3d165217dc020cd6e3f60c57897ba86e29c056396c0d27b491c6aba1fcbc2f402a0f2a1033a8057867ecf746a1157ea58891e314ef9a4652d59e415ad82b9b887c19d138f9650753286821bbf782f934a0064178ffe6bda710f4031456b48cecfe4aca1bc42602e918aa46e512d94adf4f218b68f955c158794a54a13e69375c0e48d0c43b710ac30118ee440d7a501e724c79bc9d8f08dfaafa4b658aa47cb624990fcf6dbe0e26611ff81f1804487257c6094b228c555d5330f9eb617294b9614574e2df9c081905a187b7333629bf71f1bc89aee43494b1b44746716d301f1b0fb4bc9d15025b2686b4a6fc04d026196a8ed9f4444864fe4d0aea2a8b38a3d2dc39019c060f27cd572411b5979e3b4519aea6540c21ba95d55e11b9b3ceb26d522458bc7e022d6b0359b12ec355dea821c82ddf888473554083d0f36dbba8e9cca999effa0c8b0f4e13e9559ee50fe42d7bd071ca97f3455a348985c25872593b0071e196059bb82db11518a38ff6b91fbb263f46728ddda2f357c992b2d803d5faecbc0e0891ef4f92ab23d624dfd30a9efa25fd88456c4e84abf1bb8c11f268b5e0ba4209844e72fc68a6c3a26564d0fbbfc88431da1d765c292a81391ec5daba34d31507ceb8fb3044d9d1d1f99b4941a126e0580a255fc98fa2bb7ab58259d23d0a5d0a0df0bea82ecaab07f30d97d769d5b40c7605b11735ec813875e61a551a192304f8fb030c02c6f182bdda1d6ae85b8271260bedc2cedc301947bcd8a19470eb02969cb572cb4290989b0a9cc0938bd92218c65bdbdf97101cf7da93be851d3d03debda376dfddbb869e2f0c669c287e71de604a4682b30931f8178c70b8ecb8032401da4c9474ee1693df584e3bc074c6b3284b0962871a784d94012f0ca2890923f0f4be09049085029f38dfc47bfb9f289202b5d00a3d19de77ab0d07d88f0ff220751f75ab02690e02afc8bc3472c502ebabc9a09a20e636ac86ffc96529013c74764728f136711f653a8c9c3c2b08e6c9822691973a41a7bce053b5002a0a84e5e6a91e15ac847330943855a0aaf01b9a20e4da42cda63404e9d1a15b1005daee460c950776a19cdc223b1fc6fba907a787790f598ca5d0034c8bdd9c5a0eed4b0070fbf1e81b572499d71fa5595907ca348b232a56b19e5f14031f08ce0707dab2b3e7c7056ccd9b259f4401c6533a4a18b9f6bedbdac04445db0e4b4a091548d90dcce9ab43e248bfc01034d28a19ec4355d927849f7553b4b289830303043ef8b7fd5e3d1dce67cec39463a3859de87010a75e964cf2c09ea2d983710b055fb8000c5f8ee2f7c10d973d010548ceefb1699c5c80a24f9fd264fded960f0a5b9fabafca8659199e076a78283d6f291d79890dd0568cd654fed2d0eeb600bf96b8306a3df449b2bca6d176073a106e53e3a02eb1d5b12ccbfe00341b3b0034d3a83e9aff008d2f029b1252e62d55b5a7c0b6b30aea3b78ab92131fca1b032535f849044c6567bbe70ebc388d898fa231bc2e61fe387ba4df0070fee797005c443acf774e192170bbf7df813650dff1c4f38374e4b5a6cd4c1636103b220c93405333c6622beae311836860a889aab363eac2610166c6912619b6b4b69e061a720f5f99dc9556b8031b385f732db873767b09d7f550d8ba348a686393d208589bf347f450bd4a3e5e02936cecfbf85f3d23828f3faff9d94f3092c13d640e33b4ff8e7cce8e22f86ef223c3d20685137817bb092f3e9253b3fbab2431a20c33d7999bef831c3a149bf9378113389e162d3a8229fe5194e8c5f72aec836309cbe02c4bf37b6804d6f261b6d0d982a7fc3b6652b6834ff24e01925dab1a100474ef064fc1efbedc116ba9164398db2b55f59289575a9a30184895c0caa13c09b271afa9455ba5ad23295ce604c2623600266922eb11d82b0a3fc7ff5700ce08fa19ce5e0869a99279011ff7e58dd46f0728d2c481d4e25d7298b2a57755d40b9800794d6eadc7cd744fb28d72c6fc830e84b71e5552af17b083120c6d688c037be9909484db11efb31fe69490e694c903f1bf93671c8b92995347ce56d5a70400779af62ded79b10fadfd9ac1764a8077d7c8949c477847701459f10baeab0da8367608f69e61e6c14ae2ab3280042697e03717e6b734d70280bbc9e60dd309b4167d9e058328db60916d48bc61c35511f642fc84bce5c485dfcbe6b130f60beca65c2b424da75a24a4d3866368561520f8c315f67a14d38a47b60147de8a0000957e317d015fbee9c1721829939c95b1387b72023e209bf4ad3dc55d7cd202fc77ed7f91c81d1b55e74c6d56f5584b68161cfc978f406898e5ad417eafad0813aa416e5b34cc21838ddcebfb8d276bd77fb0d6a6abacdc774c676be02510001aa2ff2387d1b6abd1baa58c6a14668797a57d5487381dcc4abba1cfe90574000ae7ef1a808cdd6efa461b36b54c30881bd975f619772d5fc9d0b3c503637209ffe76828bb602a12106f1d39944b45b8fb516ba7ffd6769c5a56900a4e9f130f5a64a5173f468db7f56fc52d189616825dc996e3b4b2ccc097b3b858c1251106e0dc8d665150ce694a3277abf1183d62a7a0730e16286e0476bfcb32e287e60b61ddf6724578e6f6ed5b221c565fae8df2efc90bf5b90ebdbc1a3fadc6f0540f9f01b0865da5c8b83bce5e7d2b1c1c3361cebc685d97445af9eb36596867a0084b483c0011f0f414443909d368fa8e1d3578a88e69b07dab67c7f2ccc9eb0e0df783767bdb2ba1cf25d60c2bc544d40fa98389e22fa65879321dd289405ffe07237bbb2b33aab42aca679795161727fe472232597323df6ec7e7d424ea4df604a596fc8fa2ec797d79e6d59f84095be5c94187717dd7ec6ab419cac59415ee0ce437583baa28679f2eee32d4f6b492f7645c9586bcd19c2745e32e7dc7320b0337512313842faaba7e55fdd51b419c2ae5c805d8288e66852ab2c331fff8af0c48dddb147aadf4943e045e0448b5d2bab786a85a11c710c51828d6ed5ee9b504a53a08e5f472dccb019ab8875e76e73572efa1d6c88c849a8b975750a334b301c7d6414ae413ff6dea7778142dd808998cf3ae2495668a74326dae1136052a0101f1da16df5fd0bbd321d7f5f693801757d6f12a20c251791d6391e3e833a5099960b5329c02e88c48608a68af41d360ea63c73c95ebe52fe624ad1cfd3a1d0216a1de5fe4fd39d0b9521fbbfd78eed2d3703f9cee0208f78250880c9b0346090992b95bc551edd606545654e3f4859b7b1a1e023c4fd7f86d251c282504870e56b3ae705d3b3537d4b1cd114c215ceb56731451aba5db7fd8edc0a2f9caf50f17af24fcc8cb6af0f0c06559efae18fd8e38fd51ae7a6b03db906783a9e8c10e1da5d4e0e37d3548c9bd14134761a8688dcdedd7d9ca6cd03e9ce2ede7d0ef0865f6d77b4e472ace4126cce423f878a2581bc6d799433f67e7ae39862f59a103d0c5fc5a9a86ca2cca982dd0212bda765a48a0a4551252ea2630388b235ca30498b517fa80fee0f458d9b1640b6441fc6078100d268b7e473054834c52f3040591b845f08f5473419c1d234a4a83c57aeb9db06e65ed3b5a42dad007bbda600146a923187dd64de324a1cfe85df417742db730ab15f6ce4d48c28b59872e8b0ceb4e906ca27dc1badf99679376535e315499d9abe3f1ae766ad155ab40d76103efdbb3b375db760f2f114fea19520e9c30edd14ed8ba79af1909838af3300a0afb231f18b59f2a416840b92cc8480657ef2df2226a170bb5f25dd9e4ceae7a0856ffe10f5ef5dc1fd449decf21b5fb558e556197d53827a2ed88c8976e713a024cfc5643398b158365216dbfc9c1f657c4a81d999d63df9dc2259126f4ef3e0503432b9ea78865b14b8413c6721ae7290535869ceda71d97a5665459ed9e1806a9968e6361c1164968dd4cf4bcc9ba28c482ea38c8ddbee268d3d9d50163b4013dd5cbc611b7f6879b068672ce19abd9bdd572753bc5b5a61a291ac6f4afb202016d2c80b2bf39952739796afe4fa699b762c85587d11f18163dd3f0f308870514f4306968e96aaa4212bbd29ff39ae075f02b2a0ade1031b30bdaec186e9401b615ef26c4badce802cc714ad5b808913e41ecfb72163702b97bb18a14a93101bc9dae3936df14e170670545e460acbe1e51ec756fd5542bcf1ecd9e50088a085f1923bee4dd73d1a82a3abb492d4ab7287b9f33c548cb0be80e6382a4782f083e9a5be2709986a52a4212e235438bc28ba4e8d74d5e3cb0ac122869a42f460e3bffb1f0022f7e8c8947b2fd677d3c148876bc11bc0d1ff48ab02744736f50020df4e2b738984dab9ece08d4cb6b2244466a8005cd381f766f888d6edd123204a4bbf94a5a92add61655834a959e9516afeb5ba4f949464713c9bb721c64ee06c607705803724ec1aaf927352d90145dad07f1dade956c0d4f934a0df14b0005c5e46891291084d940792cb48fa2f0b985308a941e9e83375a1fa8330157530652a878cdb1a0747f1517321424181a5243c0786474c331b85a2c599b1a1c250698223770a4a461af7897491005c71be734d55572bd22fd8fb4fc4e2fc3f6c7024144b0a950d58e2b0452333a903b694c24e5a668e0627f24435f66c3aceb2d03fa326f7d5f96939c333003c14b2e1b076162cf9d1beb1136a0245bd225ca910f0627d8392c1b13a2d8ca1bdae05ae4c4668965133b15c88f9b1bf76897d5900290996237522e41abc39bd1c71dd8aa8377989d5ac6215f23a274b7c1bedee40cea49f60cd0fcec773b3dd99827f3460b12f759e592b817a4211bb6cd3282bb0c47388cfee71250a08ac6191c7ca93e67220a25cc7b787f8e48093e3dedcf030eaae63a1e03cace1f807e7b6a3bc4cb4d102ae93eb8309e5e4aeeb08a18af730a045a4b83a00a258300f506a99963773439c9c0bff4bdedfcb4718298645455072ce38c10cd912f31e78d6d0508ac19bc5daf3c00273dce16293b5bffcd2b8c0c936a4941d7f411a0420039da80c8d0677730d4e2d4b8435ed7fbfe4ec042be08d70ebf121fcd9581437ad540c0483fe1b7d012a9d76e9e0f7b202ee521027d07181d57b7ddb57cb608c41653b3c79b2359d4c7783bc2f8624e72691a26d35f0df01c422a9c744e8267110efacf09ed678fbb539743ad19251a82eae5a2cd840e15652a22e6d60f9636df5c168c34841c5aa475f8e2007fcee564b5a445387c07303fd415b2ea74aab647febcf5c73c974c6f321e52df8bb827357beb42e60e0f43dd97c23b8e73a45f9f3046b08290008066d4bfe5e8c770ba4af256fcd0f003288074bd65529500ad54ddfb45a6e2ec5cfaffd7716f9ba14550602f3de7d801dae6f1a480f7836b51381be68a178f41997a8bd98cc54afda137c96003bc320c0bd4df74e3929b66a2a3603737a6274e1b03dbf7abe7a814a0c1a91ac6933701328b142ae5247f5b91d5f1d6d5e676bbb0b9a790da87a4571b2d7f69074ff60c57665303e316dbfa008c8e9e7686fc2c4b7e76813def0a2d007e7a4c0bd2e104a2edf7b87ce28011b78debc7a7770f0f493674f93dbe8b6cdde89aff353e07095a6a14090f9c4215bfe23b3867843b6370319c9beb1b67af02fe44f3806e500b1c2dcbc93a3e741c8414e073b2290fa3bc21aa90ea80357d73a8ea5bff5be5012ea41b72dd7572afa81d7b09845b9d5318f7c4197359f0c4f40925b5fdab8e076b81407b1006d0de544fc16908fe6b59ea2e11c621bcee70d9f9cfaf21e44800e2d57e48c6daccdb6001bd23693e36bab674cca1eaa27e3abd617ce2cca9040de7a7fc5fd0549484847b142bd217ec43959e092d950bd4956582886d84689f0c8d8b75a4abec6ec3f6beb0d82084d018c33a57704107457961a2bf8f079ea209f90f391cb50dae3c2f1611263576448d70449fe30645af946c86851ddbdf18019727f506a36b00ed5e35abff78ffeb2d106a8da732d64917614ac4d5556cb6025b971b11a5308eb18bd74aabb8270b7ed94d68858020ea416c2075997475830a3ecaa554b812c104f67472a94b02ce9b5c766a90f40ff0d35950e862ee3cf50cb1556e62bc04f564a13045255c7a80fbbcbf8c8ebdf82af3fb8233ec80c4c705539d5e6ae645766c50e0905266b734484e032c529e1747242d5b76bf03ca9209cfd5d35ebd5a4b311a6d3bb7e7afcf340c18afb53145556a8cf78933360d380963aaa2a945c47a134d5183d31bcaa204c83af7458b9a357e0adf1f2f63a9010c7903f3a4fc2189b3374a7c27a9d95c5405fb787af01864db6986713a25ea770db16de51d9bdc11eff18ff26f871fd4e9920c57eb7ab537f56898a368e615b603d13b5cb665164fc6696b5667a2ebf828b37993fc9d3865f4ac7cdf8d97e52507afe89e453cdc359402aed1f7f4b45000def9c1167ab4723288849645af72fd0a146ae27c0699864e20224c6f5e9914425d2599032aff78ed9f27c240ef218304d9992cab0170546dc6b6b0ab40aa5ee824e14169a5bde5a42dad7107a3d08e0f8c2048d9581cd470d10049f3769683634f9e6b387070b54f2985e3a87fbe920f9f89de81ae8b54a62021089c9e2518bbbfce05be6214d3cfeb59b74b8acea001814b7d09d8b0cf8bf103ce787daf009ac204582622b7959384d7973d6da30f028d64255fe55e2563943421848e7db99beddc76ac1415089c639e21f56f7d8707cf9dc0187da082d52347469bf04fd685b69960f2736d6198a710c8ac92c3fb07f73dd4aeb85751f6a4c52d8498de49087d717a994e2005b34e9360f1471e700b523258b3667f2a9b43caf46eca4e48d78ad8e46750b175fb4e034581c7937c0159f135606df3e174563b8c4e7d6c90fb6025308d68651363e0adb803c42f0e03aef76901f55ec1512cbfc47dee2c871339bd6136eb99d95d7198bd790b3cd9036e8a7bf90586fa8cf36180f9badbae530cb946375ea286472093964dc539c007c05345a441903a7c55c92b92d932afee86e8b7d6f923a7cff3559dc2da358c0fbe2dfaedfa165adfc03598f176fd1e420c281001727eef40452725b7888cdc00b8abc3791ad17c7f29ad6f1e360d87ea0ab2349f33f049e26b7154a5eaeaef017c06ee0f4fc43d0bf8fcaea7628b42a44d95b2c0ae979c37cc57733c45ef1b0ba9dadc8c65a1061b423ed1e53d5828d6e63125105723cc2388bdb794d590da07a50778792bb4005601deebe971214179db3df008fe5d97a49b6365d916950406e02159a59b1104f72a08cece141b7df1391a81e5e62e92102ec74c93083f7c02bb126758ce4b5072468a8c9e78633252a49f94b9ef0045696ecdc31cc9306c036317b48dda42e4445f307ff910d29bfebc640800c6709ca062645489fbfdad046d4c364ec980070944cd1eafbd222c8357e2256f4606718377b72fc42e80df07f6314dc9a9fbdf81956e69969e4c4a8155bc1618f21f5745b6e95dc232462806028b302d7dbb6163fa3788c14ae1b69ab43853f463364a93360cbe7b8422410c861f4d13a3c445665d33a87da86405287fbf82a45e26e5c10b5550469e9f3507cc1d15fc4f5276f22e03ad4155e8ba56f56bf802e3927efe024a0258227232049cf306e84ed23c7419ba12fa48d05fc4cd7d0f9fc5e0e5919dde94aea775aa0d5a698236d3f3653499ddf3e6e32246e7e4a0d4bdebe50dd99ac8cc47d6e3910c5b724397812b387b0151d9c7fe1db04555b7bccbb6cea9fa037d70673105b500b65f726d2b3928dab55afbc5870e25cd176aadbbc845e1883ec1d8b14dd780080a5756b7a820455393cdbf75aa6da11648dff063dc1182915db97f0cda33cb00ec955b8109ff41e7e2fd519859f72ee2199d73d831a66f4d0919c637c630d30e0a8e967514c71d2736fc49418283d979f969c39a503c84b607f737ac1aba8e0b48aafb11ad41e963a0c7f45211bc524503ed2017b97b553448d69f343ad248050ca9669c8be7ca1615c1d4836d8e2a4d424cc2c2be302b236c8a46d8bd30960949fe54726ad3c3871624cd94ede0b6aa69804ba9f55544171db4de0d007319019abccc7dbf158a8e628708bc25b80ffdeb4fa1932436f50ae7cdde5bdcfda602db79108872caf07874914b459b246417f8aac5d7a563351731d29c21c4d6d30605008961747a04d20db8fd7c6189811d672a8a948ba843e6db59d439c0809e0e69e4cb7ea493121893fcbc58ea3afa7e9d4909e6a364f536d1fa1255f0f5580636dcdad4d6ba5e15f2dfaaad2f8f05345a02ac39d715a5c22cc5817ff9a72a0d0bcadc40c6ef13ce30bddd72f146e5ddf77e8f6d3b5ef001d9f87bdd160fca0618ae77ec6cca2f7ff437cc78934157f495184a7952a69d0581749dbb4a4f82045f2f1f55776cda7fd275ac14d1e62abfa868038106ca8151173df33f33ee9c014792395c1d3fce9a58a6da428f660fe6758999fdd52475bfd5e1dff9b387200b753006c904c3de0bba37650a66004660f51444cf0ff8addf1987abf051d4df0eb3cf2c718f12decb2d3eccd95a53e087ae0b85e47f1da52966f9e0f34a7812028b0a7ca592c7df6ba343f2321c2217fdaa7a659a9bc6a701123176be3f2177086025e5bc3111ff6fc3141c9355b1cb1cc8be1df7afe29256203a98385110aa0cf5610c1a996c4db92024a57a63707b81574e40f31f4a7bf92b3954f217f69c0dd997ce5d1e55600501058b578566330d00301001ae2b74aafde3a6798c40110438d5c2a30d41d6a1e2206bfbc3610990e92aa9e986bead84d686f9a9e475720b2fbf9cd04f95fa2af4331419aad79afe7407f653619a85e3619e624aa6565404876651dde1474db090ecc6e7977d8bb622c286c9dcaaef4c240c0fae20f34904e4da7cc4015e260406c87f9745a2cb6eee401630d8287954a1c91d0843a1f702ee57a0900a7f948bb21396f406e350496f5afe83d9abcf8eb2483d0af453c30a5226324449b92f753aef636ec185edfd3264f7d2e0bf08d31caad1dc418de50579661eccaca9d2d300a1752ea1b3001e2be58a6aaad0249334629882ac5d390db863a3863a6045e98426924dde4ca7eb9aacf94af1808c4cef5152372cb0590585a3b668edd6dde937426552c854899115ba031f3e411cc3147e4b4593a5bc0ef9d0d24e4d20916927c6a0e494934fcbbafa48ee568dd31f920f48b45deca200a08c9ba8f441c2056c25e8ecf0019e39a8a0580ffcc21f3f73e5bf10d620150c54d0b9adbaa036096e2df0431d983a8c059f2920195ee805b85a4e8d24885c053b0c16025bfc6ca3d0ca125530b1d945a3b03bf149d2cf3fbb625ebd29795d0e098bb366a8dec65ace29acf8aadcb959e02b77796cf0db16877adf112383ca0964420dbb01ac01aadce2502e597e147c5373eb38191f76cb547182ea56bc870451409b30f88225e808ad47a6bf20dd902ba982ee34d87103387638d11e3e0b0a9c5f61b28897074c4f551b3e1dc62f30b020b39b6805c270ea68ed9cc4127b0eefb2fe1cbfaa9ad1086bcf8abd803a02059c02481fbb8bedc26bac402449d9080b5e7cd3a15789e2c5149cae4cbe88d3e0608697c22fd0428073bb2bd015250dac9aa8f6d149ba336e027d1b8a4d6819c70b3b2a04e38731d95f583fe4cd360bca59351eaa03712316ee6645a0bcee7ceebef343758744eb14c51499600ca600a6edc358e4a6b166fe2daedcf8988d4666083da05522287d98f555dbd0945d05ddd34e4dbbf0014d122a14892b0c9bc0f35d79e2770bc0ccc6203b60b11a32029e42a4fd5fa07efea52d7b50aae093d08fe2bfecb9fae99ab33c8ea08f9097016253e5d9f991a21b01ae845133556d1cfb3585d0855979d40c3e7de4c97d5e09e20cd0481706d2307b7626c5a593008198f758efeac817c985495ad96eb0ba05ba6a8a1a1a9e8dede26043d6c3f549d0f6164c11f8953030f24621eabad6880a96a6fc846a2a662fb0eaf75d89609d7b5362ca39f7898f07dbba0ccdb82b7109349e150a88afc4131efcd2a4153af304dad2dda0937197194b2ee7544af2e4070d91f697c71e237d0966b7785570c1e548ab63d374cb26743d4d540e299abc0250fbadfeb4982ce690d2283e56481d809f6f0413b79f5b0a6f98bd6c7b237e0da579e1ae7fb621ffcb814a3fc1f99689b78a8f992f47bde4d355944a47e5760f9c17ab5596f5a2a51082b08c5ddec7d55256ad5071379654e937f141fe5f810177628c21daa2af136e195d0ad690faadb41debd67ab93eb1e514293fc94bc407a234183739693564be9b873f7a7f788969c1da1883336569a00fea857dd9810e7604f45c6cb96865ad1503dd4b512d703bf16f652bbc0a653bdd62b4b1187300294c876f11c30210d95ce56d61f362aeced7ecefd73a9ffa2ed6396e8d98030917aaffc8b00c94135dd010a80fe8e8f8164c86fa28b18bd9caa5e5fb608e7d08749c8def35aefd86a151db658ad13008dd7eef3ed3d78260aa090a680a7d000220a5b95fc0af46442d1071a87be783d477cc43dd93f80d0a8e577e0c51a4180c5f633b3ecf5124a5ba1ee16f98eae99544a37e17e3397617ee364b54ea7faa0a0e21be35940d43ba8cff07abc1eb338b9dd1804259686c9fc344310b93956a07b67c6f9c393d99884b6bd0578e394637f8f4bd330f67826d57e095869ab0ca035235b55d90b1f0811c2c941f03ccfa51f3d0546e1c3ccd955d4ed8521a40cd0e97e331f03ca75fa8019486cf625cc2c8c5751b896ffffa4b932b5a8ae619c805add99024dda69d3d8839c7347e899f2a70658ecc9ac06c8fff77a7a4355fb005edbd0802061ccbfbaba8490565952350eabcd9573fba632b0f6edda7430f920c972d48ce4ffbef02840029d55ed21dcab4a0c090a438cd30ca1405d7b878060ce36b2fe99c9544d27e2cce11e9e3d55470cad45df05840f29861f189869b2c0c0a665ba6807d5b10028d49ed0ddd2c7ac0d411375164217251f9f2853d46740121fdf5642e94d76a7b46515293e98b9ccf1f6f79c4b1e338b616d1ed8d72890e9adfb4bc23678e19917268a07b698ed65946946458ad598ba604000978e0dd05872599ed4a8f8ad2d0a190eec904161d21b69095f9070f12cfcac3755441a906d247128a6f3246f7f72b119e8abf9ea022c946181dd86f04dfc85e871299fe083d31c7866ab001f247a6a56b1fd5341dde2dd660c463fba4a72dd3b06f99d2096918904767a9d31d0296d8d043d87b0ee73a3c5be6dc2abf871adafb6393230ecddd27402000636724d249809ed419f4e7dbbea5c4d2cca76150b016309c2b0734f866b46fe186b18e0616facb1e4db47cb070366839fcae651a172145e3790ae96113f75419fbe836769161e9eb59e768f6b449fe84a68742d88ef103fb690cf37fc41843cdf1c3aae75bb779742d178c426eb1c41b0b89fd35b01621e8f003f5b71fb9d881c5bae0e414f4b869052d53af1e1188cb6b3fd40cdf5c953181061e4f3299f4ce6aa95bef8bc523af17c64e16504bedd089d736ab771f7e7cbe0c1b9eebff64bf0337010609833cef60ddee4abc46d34d4f2a2d76086006f509029e7a04822798a49af80dfc53fe3cb120ab5e7db0384ade1178e04a8100631a0cf4252d454735765dd6ed2909af333f4f51bba560aa7fe3a078c8c6aa8c2900024cbcdc41fb28934b1134364df1d8f5b49cc964db7bf6d2cdcb61514f8b540a0bceb7f71fde4b917b7fb625df5eca84d9418b7639b3a6505d98bb3b047b2aeb06bd6ea0f6cc677a48c79d4626658e5a4c9f2caf520b60b2d99788b79bf6886808c7bf34bc98ceb9915857a7d8870b15bb6f2ca47ba60d40c63ec12e7816bd7d0d9af43c6e3850bc454fd3c9cb492086466fa603f39f877cc28ad41686bdfa4c0165948e78b6cfe916913c45cb1908b918b15a9ad22d811da44084fe94a2284d0fa53cc2267b53e5d3329ef53d4531d60e51688a45e1556cc38d6dd787305d180779323e6d837cec5fb2c3dd20089c2537a95523bd12bc1d46e4a985f64fd19b0aa7ee84e9e2a0cfd4438b4949e44e637e432e3fda35b6d3610948adb37ed84d0deb4012fd79bf4b34c0d3b5eecd20a3a9beefedb224afa0ea1d61f5fe288f8f0540f4515f2fd4731ce06be5a1e3fe319b97bdce5c9162a4269efaa55f40be760eb711b1f0770f7a8f3851f5eed2403193c57859ef3a9c0dde5c7ccfcea87607079bafeacc171cd555b4683cac10174e7691b6050447811315245ffbdec8b10707a61551307b1731e08501c4c77d8705f69a0fb5f51d44bea7a1757ab49f04ce0ce5934ce66033bb3ee52ed145c02852f0c817579ce9bbfd0dde35dfdeed34720c73713c4d18a4620608747f8ba3d7350e5e607ca0d53efebd3521373a49cc02063164c9bca26f0837f32c1aa1fc5c60bb118d1e760bfc7cad26aa58a106a30e0ef26a4376f7c81394c686869e80dec659aeb631521e643f8a561fea7b2274750982c647d92cbd9ff751b37249af2d628290ede9b6c9bcddb4ee2b64d977d2680e765ebd18e7aa240fa0e7033a84c644ef686ec0c92bd4b5aee22cdc307b2b2608ce0a6937eb4caa3624251e940bff10e433e85dc72752f4fbc020ac9ca0ae160ed6e345bd91b7f27fbd398d1a271b2a910025ef90186d48e134ffca401f6a6a0beb675c4c8f54d2b1008c103147f1a034486a05e7150ed3fda45987430573fc0ba4bac3352a84342f17d1da8eaa5b81014fac3e5cd165e4659beb9ab6c2b4910b3d906e77f7a8b34c20de1348bc9cfbf1ab4c6f824f3fe4dada7169cff734600733abd9bbf0137aef6901d11163ef1a0e130f918fac0c90e8e1c280031c070f0bb38d38fe55b844581e6677d313fc84f0bec9feb764b9f3dd3f6e63b8ceb84f0e2f8501f8bec43dac4416bf2af1246b4acbd734abbc7404905feb8bb936420d01c24f0058a5019696d548db36ca2a8e8c30d66a6887f37972f90305aaddbe5d06ea9bad064434673aa92466a2bc13b729f1a5acfe1e33bea1552b283b2eadae095aed91a34452ffd064e1033868b7869829061046022bc7ee97882c1233536105f25125c17a81fcb43bb1d3ef920cc4abcac82328f6f01c8fa1fa763e2d1349074a244833a3b184f08fc04d8d4bda3d5c157bcac3f1f6138b5cb8eb989a9f3c029f2cb8c010fbe3743cb906c5ff6bf93a881914c930b06f0322f1f98bd94c08050fe98befb6c4776b52f7a58a114181e511108fbc262c1af6d6a922d82e4f9505e59fb9b74428c3e06570930482f82b2e2fbf1ea57e149d828f251ed2c470110f89a0593b75e411f597b5d8cf22a24f207dc4147e2cfbbc520e75bfaa6f1b0d0d68eda97cfdd52858f3859c7bab7069776194d714bf7de7416d50749754de3f087703207495a373c29f096a00ae5bd03daf4510392578bd580d77b355d0a9b40cc9a2a25c03770192bb645d4cc61499b58f46b8c0ed3eef8e855a2fc7deefaf091af3501da6dac9a885a481e4a4e03fb7a1dc3f6cb919d01f564e7a6270d88300af4baa2a140231fdcab55106e91db93933164d60d76e67f5b100a4404dda420e56f5bbd0d49c92ca07515e74edd629030f8d2bd2ee0a2deba9146dc5a7dac409d089823c8685ce1bdd7672ddaad2330541e7c69c224b4bc560f50c2e6fc0c70a8ebe3d2117c03e88a9edb8b4e322c1684c0890797e8b0cc8feab549f1b9b3e0bb83c49bc82addb1a69f533e69c40e3ad71cbefb61fcc4e49ec1c0cac07a943052c640b76fdb3d2bd7e921e932bf28432beb3763614530a9d641122150a19270e6ccf518acb0175eef69879627637d8a762ccb87404d391d4dd217eaab0189f079ff8a990ce72431af49d4d5badd7462b78a606fca18f52a23a56d5ecc7c7c80999bbd1564c9b43b04efc80a665141ae0824d3e3e071153bb7af1e7bfc2b8b30996b9aede9c29d8f2f1c7694c9c465f62a441051f581fd0e8b9dd2efba7543e0920958cfa74b569da0fd27e9837637589d66e272f4ec4b8c251998dc10f8c90094e817a1a4f64047bd4e89abdc3856a30bca6465a0107ebebe62156803c6751040f4da71043cefb5d0e2f8c72f8f9dcfa7fd25f7338a8806edd293fbcf25b630d1a3297eb00c3bfde2a32a45f3c8ec85c83799d8651d5673a5990564e6b46590c90283944d53fac2c44e4f5d8355b1d3a09bd39b4e9c648a97afab387dca29d0a31aaef5a786c2890ad36af71099c8531f57cbbc75e15c505b262d05216b77903fcacd0689630d18a4422bde8c9d929a0efbfa2478c6e7ea6d825c12fcd815c09f6527c09a43545b6eca924d94369d33df092f97760ba1ecfbe962a5f224c1a03be6324db6faea93ac57b2c7de581f74f8b729ff4a6e1d8de33ef0f24471e6408b3ce4b12361cfbaebb894a6687be08a04544b9037690c6648c9d29d58030e102d40df79ae96091c3370832a4ba2ee7166c65c5f74731cb4fa53f795c6c6ba10e -generate_ring_signature bdd604377d0adea441787f06cbd71142cf15d55ee20ec1bea85f573661c71201 2487ddcc1b463edd2a754b037e4ebce25e5fef7d87a227d6eafa588404345543 35 b089255060a9bfb64276f30e0f156291d61ed74376751cef0c3c519bf97005cb 71344fa8a1d99b228c90f79d26b10a0936b43185ae7e456376f6bb527bff449e 2798b734c642a9ec401cf1a33c14c239c2a59c5f48eab81450f06b8824589ae3 3aed394a9a3077b2b7b6254d4ff647bb96b7438be120fac9204096b5f169673f fc7b91dad8e497665fad7726ccff5cbb5c611400743fa25dc395bc95f09c7704 d4a4dcb19764fe5a25a5963903ff0c0b9134c4b8be2cf7f33e7bbf42b59618d1 38b63cbc455b1340fbaf53d4284ed6a0db201b4e4aacfcdafeb6273b1a307e3f 0b8d7c46fc803560f12ef832de6079adcbc1ef9f787b45de01a737ff5e95836d c50649bf69d8dcaf869c7e42948ab1784cfdf39af5a7e4f710726f439b25fe51 cb559abd56c267e40422864b8e58cd27ee9f9076f08da0fdca1939d20e6500e0 83c0c2a194cb48bd356a96e50ba828121c00be8e57a8ffe7460fcdbd543ec13f 1cf20a067afc5e04c12b6ec9ed5dcd70d647fb13e390b35b24503db9590faa9e 77ccbfb6fdc8e4fdc4b9f1d5b642a0be86ecb52d0760e9d37949c2a08be3b17f 95061c0154e8feeefdc01a2d1fed47012266721d0fc3b81f1e39ecce4f8b2850 2009e95362d812eb87acd566bc521ad12249dc3b1919d8af30ae0aabb389b6ea 7a8431ca2ce7199d1db1a3c786fc9405dde6d0628952cf95174f0d35267a41fb 8fe2a2d24ca5a08caca966a0a5175b9807f3030631c5b93b7ac35eccf25724e6 67a3edbba5207baf306674a7c24b2093a4bc41c3d43bbfbd545175c8c6028bab eaa0962f91596cee665815b64415daf81bfca68eb9cf5eaedb9ac0a0c4ed0900 2f87c482a8ebfa50f9776869b0899613d31302f61bb9a8066182782b738db442 1fb8bfaed14ae9aa8f13807f7a07d705d47cd642349fb1787734233b7c85a3ed 35be0bfdf4b725f01e4f05af0bb9f02ff0dd4f612ded87a2340a1574b3aae315 4a0fda512f352146f7859748681ba33d2760bcc72a20db5922691856f7e603ca 33f200d19c8fad76478d2eec8699beee2061ea1f31a6f28e764cb7025691266f c968d9bc39daba4299986612b3467ac3d45ef0b830dd62b497e52219b175fccb 22460e30d871caf28c49e4cb59cf296682dc386f5eec6279ffcc150faca7cfde 1f92d5efa075ca1f939b27e1b0aad182cbca7f20d2da676ba1992d5696dd17d8 73cf74bf162bae040c2523789a0bb3a96cdf781f1a766754a2e2ee1bb200791a e669e339a639b731135147dabc872053f458a767ca38c35d3e9a3049f7e8908e f8183272074d1f826677ac9d576cb3f558fec821f5e32cb337107ad6cc85e63b caf86118b2e212f253e171c428e935c8eb6cf6ad085f25af4a0a93f9ec59ace5 9dd75314c1d8ba4bc81914778dd77308015880272b65bc37c402b1cb832a69d0 bf83ed74a2f7948e17828735a98afdb798a3f8793d1df6d4d1d8069ae7403dc2 b553310ecd79a8254d329a3d6e2002c89a501c06b6ae3b7feb152293dd26397e 7d483ab7dcec571e016a577f74301a6614a34734be679ecbc324170dddf06aa6 57993693eb82f28e7ec9a7bb128da07bd30e23fe4522dbc460d96cbc87482706 9 eb06eef8d06cc359802f960c4475585f95a05a1ae87a181d896c034d996a450732640f58830f6b2c4ad3ce132b4ab0b6c8737546eb45a08585bebd3ef20c0d01da3a2798cdf9d1a734394f3900953f43a1c62eaa2d26949e2f9651774f61a8094ffa30a399ef4204687a3464f8e64ea56aea2f06e17e2ce9d24403c8f1c23b0a6cf0fcd37c5836d9d37a50eeee6058790f745d7333e0196a6f12788deed3f5078fb201ff59faf7a567f09a9f03548b5725982268c6398ed0c67580fe7df5ce034f7de93e3bbd7001dc6298ff56b885b21a1ac6cfd4fd3f750775bdad9f913d085e004843bef5d9bd0ac9d62cbc77265f7cf053ff15e81ed0d481edb801103a0762e12f0e6867cdfff1a3f03e8f99dae3db4fbc5b90ed791d109bcb296876dc0f1344309c66fa2fc37c2d5aec0e831c44cc4acf3937fcafbf0266103f007dbf0c2de154e44e94d8cfc797b53e05dc2d3d61115dd154ab15610d74c3424fc78202008398463eaa06bf687780829df04b59120def1b22a915d0790e664f5d8531030243b54f4824c77418aedb58eacca8b850d5006a8943193349c3d74811c6570dc7a3b596d308d44fd2e3e64f804160fc2f797c5c15ff4fe1083a4861cdc50e0f07b331b4f32e15a5aabc617c74eea170c6214b2786876d670a5e989e4c006f027c8bf25459e7cd40d2f9834a2eec52c3dae4b933fe39703e8b69ab46c871a2029b96c2e758cda159248b93543dd85e3efa10a003f5bed993532f43dc0179cb041e5ef9cb37a0a5f10dcdc255523228cde8d3d3636d93f906e302db718c415e076856b278838e7f08e6f624a0d5ff180948638a44eb4e587bb9ce5dc2c68d2d06954815a2caef8445fee06bb3e502959eeb61d36fcd7c0224abe0ef51d9b9b809a4c9db1137fe5cc1b444e04d3a82a3f1d5b7fade44c00dde807f54b2c1298b0b0da9dc829b9c7798e10688bc8554c1bc148c67f53d10eb956d9e9f7ef1fea50ed0e6c3831d3edabb06228742c3ad6d314926f2eb73a5efc9cb396b98a2b3bb03e848bd4e98e46920b99512ddaf48d6476d59d199180f389c57a08eaad39bb009568daa0b4a6a7998f0ff6675f207309db1d30a565bf50c03b4063c3ae0319601faeb5b69d5b7cb0f5cb7cd7b7de440538651286be6825d087270759649b2e50260c794e1f45bf0837934fb0c565e51d659af70a8133c0e9cc734e856758bbd0b3cffd96234a40c5fa95595d5f421c947db1054066909e409a24c13ffecbfb50e70337af56c5282ee673c0dea3af3599667482563d53f174a32a2528ea4c90c0b04c6d9d06aa373e0efc6d4eade376aa52da5119410b62505da31be7e37954805ca39989ecaae27e8f36fcadd566acdfb8fb28e0bf8b728a8b16bb7c0fd5186079b6c7ea2d57dd12cf0e7d2377ca1bd3188a96f163b05e1a4c7f2a9a648f5f204e733187860ac9ff9ba9f5a41fc8599f090a11f34ddeb90d3ce9bf69dcda4600de7fa9c30c0d623f858d4c009c9454929cd853c62ee265186513c3524457b1b035b3a6d1ca267be5a651b7024306c525c27d298985280d8137af5cc583d59e50980846774b9977597328e2e565d65a206be18ed5b4fabcd32f830986ea5b806079969bc84d4ae99a7a5821ac2f0fa11f045de8295765ba588cb38eecd98b35e046b51a9c9a7865574cbe1911d04ddccf73c410265f21efb25883bf2ff99860e0805f26a7c36026ed5fbdfa85faed307b08e05c85bd1db3811b4838ed24882ea001a647b6c993b623181b2cd84414433932091252ffac639edafe643e05ffd2a0f2fb4632a5dc478e705b1b0d6248067e9face7cee3eae57c0952ae5d895fc19048d989393e3fd9c83440127e5fac6bed1e629289fa4f290f1abc711dc9f74e203508cb5427d5f283618ef311364245897092a8369be7550d4064ed8bd283d630690f25e786f726a96bfee1a7d062c7086a5ff2d38e01a863c44c8a2b3d88bca019c3e5c6e8399f5f58eac9890ff36dc343cfd89d59aa0e0bed2faa718a988bb040e5bb8c301f30b9b26ce46974ce6c6fdb0679eed96abf6c46d60fddb4cd37807376c5ec9e6d541d11d3d91eb341ac86256fac22f59ec73ac87306b5d6b1f9c0c0b44f4a886f0c929d79186e9c38ef0350bf15987a98584521b17fc7d89f0770c97e42dbca4eac4c90596fea1b1201a1053611d088f3a2e524d53705365911e016cc3303b6aee6ade52cc6f88f54a8d103945a2e17250cf49d252c34a7c00a9003842fbfe5a664d518aabf0c8147c870d8fc0b70b4ab432cf968398d52494be022fb016dace2e2520fb832491e793a19ba8c7da4b4bfed3c1f19f86e2dba44b01d2586e6e1d60d869dd40c2b08ad1b743ea73a6701fe5b1fc375393e7a3788c072f9c5bf84fbfdca94340a9731223e44e03c7936d7e71314a6ecb00599d3c4e0ee15c6a4efe7f11f08220da2d758b4d444db3892674632a28934c29952a143505e8d6ac31ea886d59e6cbbc16ed12c4240b95b0a381d363bd1c73dbbed79edd069821f31a189d5cfc742e89318eb6134431f3c746f17c4f04a3837647f11bbc00cc92d981b05200c9eb0057484a8550be48d8be660fdd90b827c38a551b6ecc089c2d4ab9a37ebd9fed1c9656880accdcae9db7b4a183c8618a5b7b3f07a6740eb0ac7ed2276bb1980e9957da5813af0fcf3bf453338192050720ded52a56a3034155bb47c3592b977a46062d4ba1e5468d30b7223c8953bf1a3af50ce29d4d08f0654388f8735b2ae0f12cc96a7f71e1acaa12e0f2fa29a99dfb48076e549d0384e55f60d9c18e407b2cb1acf2bc34f06c7fc49fa3f6e79911bf885764a34f01a568e48ea4b09382d815a20a50574c760b99efb97a0a43665b2d81944e878d0699717339a18cf15d5059abbdeac15b86f2534048c0de4f079ab6b0a6bfad0e024f9be3b8b986da1f4b05f85604b38514bf69360f9632cc373ec1128942338d0ef64ff19458af3e94b8eef46ce2559c1250f0b0b12e3561d86106c284fac78d006e91430a08c5112f5067ba27fb996410cf6b3766f2c7408831f3023e96150904f4770b73eac0f83bb16f4b6b77aeaefd0dd2ed2f1511a1bd1233780dcad5490f4e3989a1f646f1555fae4561333e6cd098429523d84ecf3bf20aa27ff41a9800 -generate_ring_signature aca251d46d3b5ba026de943e11da65c82565e3661f1aca050799c330dfad3d41 b26123f9a3df78771873063ddd286b3b07e8c622f56903f2efce7499de4478dc 1 332885ac700a35ecfcfd9d4b87366e052b2b64a9b71cede41d1498ebe448e3e6 e884832fa0894e874fc141dd3685f50e5db4e63743002c9005997948552f4b0b 0 f1e70c597eae68ed7ecfb6dbc2e55ef21405edf3e1008c7039cc130172e0cd0acd8313afbd9f598980f8cfe0a86ce6c7f010a875fcc662293ea968350f7fab05 -generate_ring_signature b775c2fa902004d9f8b68d5e8d02f5a8c1a5368339cd1e9a5509f9cb5bde73d5 7d0c3bbc111cee95905b30f237c3a948d90feaa912301831d3b3d0d765056b96 1 8594f65a950e1a254db53911145528dc04b86702259d209172a832b7886d2d2e 007a01d31abf4ef99af89f0054b67b885b1964b5ecbbcc2f1c9e5dd1157c2a04 0 8e65e615a34134d21f6fc41e56952e9b28f7d6fefd633ecece9c819e00645500b048ce13c1e513cfb87584879d920da56a094bdcce7b1716bd0feef968fef106 -generate_ring_signature 677e5736293b69b1f168f92d1f194d05ced68b431ea47d89d1805e285a75aa96 c7fe04a57b9bc912b8b51cb394ed14d95cf3175482764e5a9e311b79669acd05 3 bd2dd96a9f97843f6f9f67291f4ac742f7dd142e4bc174d83242322ea2ed6578 519650d18da99601cfe0ddff1cb8a6c3c5a3166ed0f4b4d5df08e731f1794af2 cb6f9d2a06625c1ca76b07ed61ec15de053473ddd9b648ce6c912a3d176ed6e3 7fdad023cd867a5981be27fac5ded58a4fad51750510df61abceb2cb6cefbe0a 0 9c841cf647c280baf63bcb627a5fbe62dd601950eb8517602fa1e2f4cb7f060a81157febf210786651b9f7d772a1cea0c3f8619523f780498b20dd4614a9f3011d1831136846d7ed5b5a03dadcf02b22060f2c1a07cb51eb1012dd711edfdd0f02158ac98b5e16ae4655164293bdad41a73e49fa919cb18b3540dabfaeea6006d04f61cc1c6263a3ed18c9be162dddc587604d13246356243ae3cfc11bc7f00fa8370bfce21c305439b37c109e59eca60f93ab9638d47ac0c2d1f72fe1914105 -generate_ring_signature 2c56d0869d611a98a8583af24571d794a9f7d657bc9bb2668f54c91339377b9c fc4553cdaedadf367b81758a5b277e20625549411a88c7325bd8a7129bcc476a 2 e82c7b44deef558ac8ca40818f843d27480d89f5851c2e0ead9e2fe58eebbf9d 66b8b0b3f84eac9fc67ef4d8f5324dc53526d1906f0f830d21bcea089e0c42b7 7e23cc4cdb791d7b4ec1ceb2ef16f65d29edc81cfe5786a62f4abc457c4a600c 1 3d3efbed657c2f625b44d21d3390d2faf56052b71e2431583191725e902eae0a12756cc894a7341dd1ed147d7e57b9ad6daf653d921558bdb14afd0ef601330e759689b79e77bde4d641f1853df9c0fdc7fe827bab030f0687256ee7428829068bf7d985164bb91247e92426479ac788dea2e16755a89b611ed6b3a0745fc40b -generate_ring_signature 8446c895fa03164711b6289f8dca06a575ccd8558fb070d1f07a053b6ac62a88 8f78a27ab3ca15e3a040e4ac2fe32902d418b7317666becf389f520169099aa5 1 05291f379474a1eb448d27e66553fbe9df2a5133b15392f6a966ace067a870fc 7bb9bf9cf9611b1609b19513734454f28f49f896fcf29e689a937abcc07b4803 0 76befc22f46bcde58f10aa79595b3552dae008d60dd5d90058fd59777506cf0948cbaf4528f5680f585a3b0f59f97c8cbb0b49cc593e86eef7e9eef2b0cb5c00 -generate_ring_signature 89919f2cddcd14dbbd6510687053b794e5b8f01565250bc8968b781f24739668 0279f476b72eebe10b6db1264b63b2b074144dcdd4c0c2764cd4d1815acaf713 2 f82d16dd5f4d75904b7b9195713ee68a1595aa8d9de4fed48c72c0529734f52e 04569b72e8939aec2b6db3b83ee34c21326a951c83915e1f318435f609445b41 824dcfe5b0051072ef13f8c9e183c0d1ff5f0dc9a2c6f397bcf1bc3d626a1803 0 c8852dfb86649336b7e56f5e20b7e87f7ca1d6d39348992b52bb108ce183a809f1c5c1e3acf7a49ecc4bc8d655e877d92f9e366a191e1522d5e1ee2d9a5e530aeec566ff54ad190212f115478e7584c4ee7bb51c0165287f484ffd846f335307bb675c3e0bdcb3b0feae1a2b66ce58aea59fedbc7637a0e1cb8ae06314c55d0c -generate_ring_signature 0d45a262ba21084d5b11dc6f7df59cbed5aa38f517ca4a814ff490923916a652 9b86f1081957871b7575cd304b3cbaaa38d3f00703de9cf9e8e184128f864375 197 d0f935d39c2c505fd5a8121bd2d4a741896f55ee2470792d924dc0101a6a0366 460c54ce55f47189b9ef38efcd9c2677e4f3b7705893761ca701d0fcbb17530f 497b4f9949d0021f02b66a82223c4cee3471218d072dd5bdfd3cd75f22e7c0a2 86234ee6f836e7a59fa06e91798301543347f89beb69c47a5610cd6a48b80f81 eb8da56b6a7011e63dabec4649def5c6b1e90ed607e3526ed329dad2d201681a 2d35789d85de0049e0b401689d2cb627e88d8fffa91cc98cb6a0cf41d596c936 c3789543dfba6c37c771f175366171d91d5ee954e9a0626df83d371e1c03600e 19096bc68e91305027ce6f497869c5187fe39a606064f01dc45f696486fa24ac 59731aa199ccc0a87aa746c6e6cbb76295bedcb6269906637ebf0ec791706b0d 608f849ac3d56aa72ea3d80df7d5a784dc4210ef2196ac1fd972e9bc7828b378 e98d5ee1f8de14bccf0abab68e749a9c3288d3d0fe6406d668f912ce03e5830f 3ecbf97e70c7bb254d6afe53e6c15a9e240f26fb732e9c8ad1f13e6f5bdbd766 bb32c560feb78ac2da03f973c620b99dcc06c726b2a570b732c862c95a1a8f79 7475630c541b4b636a91e93ee3610ab5ababc266db827320a8814c3a347b9b8e d0a372bb94a4850540f4796fea6bb38d394a47a935d2dd4a5326d86d501c767c 74c6fec0ee4b8931974d59e8dfdc4ee0e28e3d1418d23428234b48ba8ad5393b b688d964bdd826f6748bf989babd2c55febea15e51bc92d3ae961509c0e336c1 90d569472bc45d3201db602f71869cbf528b2b6b3fe19105278ab39bbe1073b4 d9a36d7afe645436cd02b3e61a86cae1999ab5242d039b4097508861a5e247a8 22c1541a04cbc85028279c5acf1b54b6151b82d09c16513b646a14c6944c3f5c 958bd65d4e627939b83672a58ca806770be18562c061c2a225dad1efe0d95b6a bb3f7a2da142d46d520a1741c9c24d85804ea3da66c691d836cc3b8c5f7421bf b5194a6a410afccb0a4963cb71afe4c177cf90f9a7a3a3d4e9660f1ff064ea93 4f7fd2fe01c9dae2050735a578bbfe329a5f8e6153816ac53dbd34762f87a8a2 6596a10ef0c3eaca356f8873042150216ea7df61d25e36136df4746fa77ec0b4 860402d1f5982aafdee30a7f8830a0f4fa64877255abf4a5e2abde02f43e5c2c b86ef36d3b6873de1efe9e02e2083d3bfbde0ab81aaa37af162f1a6fa0eba0b4 5e7000837abddf0711071b6d205f999327522033d068d3d029187b083b5d88dc e7c7f0e549f921bfc164a4b724f261759c46b147b8bbf0e6e68c39503c198b64 fa55f966b88976d859d6e4776ed2dff074761de70d589a1c89c86279f0005a3b 75900a1d92813d506556b6484ca988d165f54008f1a8b50e0ba8c7b23bdf871c e7bf33d171b351a061ed80d3c52029b6627c7b73167d04f11e339da701cd0d6e 655314b8659808e3993409e198dbfe6555053ed5e0c195d39808ea0f45972b49 89f9d50a6e5f312d92da3763101453a9ecbaf69e84f47c21d855a05bb54a219f a64975ef5c421dad5eda121b3c3e58f818443428a05263053be3da99fbe77df2 5fc852cc8337bd39173274d4f3cf102e0c2fab30ed3d8ad70c753e3c344ed4f0 61298f604f8dd7e109b8600eb9a84001c3d7dca3efae6229dd12adaa4e3d2c6a 07f4d5b629d9c2efe6705f2adb2b9342822e615a9fe6d98897d74fdd89fe2878 5e0f62fd8243023c0b9e7dde96d4ef14e3079f4b0d7e76630c670c76847aad5a 345e572794d11d758bc761d9f22f977c827ed332108e810b21b85cd6378518a0 8afce8fc456c8724baa05516fd9af2ef8ddd29e538aaf1214756f683188a1bef 08897543e204208ef1c1118b4153c450245dcfb2b7746e48e8e389275e816861 d10372082654fb3dec772174e0967ea577c5d93d4ecdef99c70867d84829a10a 4e038f0e07dfc7e752952b19ec3ab3355b2b92e7a39182d3eca79b6a3f87d14d ad67fe5388f5e92e911157fb2b034c1a615a1eeccd3858f255bed067540dc711 c40a5eff26f7221382959ee22dacab3668894870fad1934eb8ad8a7e40ed36c6 ca5f7f63c498e39e135e8c18f0262702de1348dc79f8204c216651a9c7f9ac71 e578f8694a5e92e48f06d5709ccfb681b9c8a738abf20861aa51d61872cd3348 b238cc4ba4613e26d7d41996e6a15b7a1b4314aa7157a984eea7d2bc0706d00f 52a762e09b64943a304ac9c2e110ec56493d00ca752ab2c715ba72f7d69bec89 b5b444ec26de88fc6ff295056545fd65ae83c012a45683b9a22a379efbf186dc b74cde735535b0acd9ac6da6a18c10c4d3e889f1b0ee33aabbfe3e942e9f6321 ae98eaed6da71726406457e4fb6b8fd6577e545ccd7aab51e0c5d51a44265e4c eb366a542d43efe8e33f1f3733dfaacfcd97e215a44ab0cd2aea54a485bc1ee2 710ad4431c43814d664c3a3e0507f59cb8c65c3fd6f6b9382e84bb308ce2dc52 957765427ab11d99da32c054c7c68f888f02f1f2fc10c5883fb866661b3f0e1a a453b57b38dbd7b2af86ba28379d8832e3bd57f3b54897875b458ddff98e7949 a9b5ddbc6bce04caac06aec9761a507f0e09a8bc3f2e7fde93304b5fe5e08673 e8854e7fb42255389b91010d08f90ac2d86da53d338f914fa5edecd400d29a7d 15dd3f808f9f9a4e1f0f68a6157d3541dd24f5e3e51b0eb51543c1ba1dc7aba0 4f187a0886930e1129c97e3373b873c4be9875f745ee91a2e20c2e713a133d8e 64f4cc62ffefdd1b73eba2cd09c5f7ee3f2433faf82129ab79ba6884040fc3ef 735c79657390255c9072a9852bb1d92425811a7703b9809eb3793f4119e00463 5b88ede31e1f160f65b6063921a2bf8b5a28788ec8c3f54089aed3d5aabb983f 7c1252e028be184bdf13a60a46bd476217cceb393d19db69ef94bfe6f3380ac5 d2ca845fc232693381d777903751160a1fb43e818d548f0a0151dbd23815b260 d8ac75db6b5eb39e3f5e23d0b88f82a51552150419ad35e2ce1eeca110df4552 71686dbbe145ed2962b857fe2b732bb0006638ec2c91889f6ff79e1729554bba 2ded0ecdba64251aa08c39c52d0ddc5eed4c85df82f9856cda1e0309315ed47b 765d23651313db76c45f44ae14865aae2c57f8c67beaf102cf299f8f4f5d856a 998456c7aec76489456b3700990c3bf373bc85eb5643a15225819c91c8642b85 340176f1b4bb47be1b0108d75f4f06adc449a28a3ee4b3b7cd58d186cd6c7c19 7d3293cc9c72bbb56250a56c44225e67c63cbaaaf65f2e3671e3a8553889ce75 ac9752288d33835a09b7110f63c38c74a3f24b97fa26471a9d5adef3c4d27373 e152f9eef552515995ef4e9ce1257287e5c7848da1a19697c0ab30f14f7d1d97 08ffc32c3134d2e1ab2f57d3b287a8f6ed83231e756905136b42c1b3aa69928f a5180c65796b1a1ab3bb051ba05483f31e84f49c1eb893392fe987187ef85c63 8e5e0df1415bfe86c31d128a94174622e9323cba4e1adb06805e92e7b0620fcb 8bd78cb19bfc01c452f2ebf461670e00a3c3aff5f7ac6a1a789aa8e697ac324e 4b65071088cac227ca7b21538cfbc0cb09d0be5736b852e44f1b02508900cea1 6256b8a6eb474cb662edf8e72d45598a24d9c9a5d0c2d4ce90e6e4eb4ab72f8d af391f1ce7788799623687e8f64fb61e742273a8a7344a6098dce9f6e1b1d78e e74249c6cc363e2017d459fe26ff53745f0ebb45716b03f735b00889811935ca 5010e69034d8eb2d46d1a8b4f0982c2ff82f6a13e81c61ed262939eeac5cf3b3 88f249e548a21b58d4e63560be38b14054257f5b88adb0c1b2db7248035dac78 925e9971e38bab4865df5368cfe172581a48e8d8a25ffcbbca2dc00a1c765488 e3087dd5dd33f774691f876a3755f311cf77bb6972eb2c6f56928346973d8e99 56c686807919352f157d48805535d6824158026d99dadb72a104764d7983c2e4 46800e442d18322d36f6978aaa3790dd145716459f3ad4a04be9597725af3767 b32dd7aa461a9faeef0e495886d5d8c7fce4f8f5b7fb3fa42ac513bae5903793 b58508c057d5cfe3a4c4524c96c74f5af636b5a9a56cf1bc4cd816e6d4d465bc 5cfc9b6f9b677441ae34f285fe46e36c922c15ae955a9169d490984bc8b626a6 d58b254b005b382262704dac32494e11e867b30a92df690ad0883a5bf7bebedf b582fd6d2430c8651f15e80ce36cec80b79d7283bdcd5168420fcbd779d1e9f5 3e684f24aaba4b871045cc521acb81cc4a9068e06ae0a7c0c4a7d5ec7e2947db 809eac13d43c2fff1e919d0d5a93a17f97775858fd822b787cfd9d040cb0d341 6a6b279a65c84c83083de47e5ddf51965103b6cfdc39be8f0920cabc138d1b32 11753c1fd004eb63e3296009423f7a455019ea6122a22e06d300edd724dc054d 2d32f6d1bb3a0761755bea3b77ad838c0e0b3fa3fb52186d2ff037fdc3c0f198 3f8fc50bbfa21a30257686beda5a777728a60a3d187fcdeb5e361ea75b4d66bb fa0d4059819fe819544297431c1f676d1b486f9daf8611012e8ffaba1030d767 1902609eecfabcb88c566f1f737acc62b41c4131178eb8a8ff68506e2972875e e536199a16fbc159054a32e48cc24c7f151500e82c2757353cca78df3b72d22e 659e8fa4ee5592b00551f6f3c6e8746e6c05a31a46332717964ffbcf24643b30 594bf8cb04d93a3811fe7bd3d3f1a413775aeaeb28d3b06f7f29617e58e3c3ef 7c9d7a620d47d9512b5dad55a6f4fe98940264b316372b74c0d39a1f7b355663 ce45006b86dd046a5a18e70801360901b0d6174a695418bfef4dd036c55753a1 f199d0a10dcf37bf413f9f8617863577ee4a3b86454e3e4c34833dc51ce8a9cc f65ea29258bd880c7924c54f538b08a9795212830240e606e5d751b3ec8e3b6c 4a4093fcf50674032c280905fdcbd285a626d87b9d077973eaa690ca95c4c71f 2f660dc6c1a14877e7898aa47ec02bdfd278385536fd9fff368f5303f1b82f70 05821ee0f3ba4a2451eb951afd27a624348f9bba4561b1fb9089e974aafd1316 af33925039e6797a7d7663d6c693ecdee23099b60ff22c559635b5aad147814e c3662d59040b9dd071b53edf6e41943332e09c6ea7e4e2ac12062992376e7b9e 67c71e230ff2274b8fa392df01d43ee6958d4e6f07da64410754ec5c5ba52881 e2d574143ce458f5886f24d031c49180b3f78c55309365715ebcc8a3620a7575 63160938651dd3ff9cd282fc5aee479afa13705c7a2c43cf8fbaa286cb216070 e51f74ea5fc1d517a12b69e5451f3f6780c5640e5d590c380f18a3300b935c3f ea8345cf437c679dbdc05fe2bf30dba47b35fe45c5ab2e5b68592c4adb6167ba 893d54c1b4fdca988943c4d51d87f37185139c9cb4f8e3626501e36d6a6c5042 92722b7d3ecd3d5f6a50ee26efb4de32b26545b58d83886da5d09518880a6ac1 59255ac1b983a2a2e1551a8f19b4ce70575d7ecf4970a250e6ce5634e7220a39 e76d98db55a8444e9d82f28903072fa1b9a940298b2a2b892ec198bbf576cd39 0c624da5122c9be9c1f0f3d6cc3caec5eb3c5666d13241be0e7f2e0f8ec303d8 0b5b10255ceecca806ddb8fd3f09448367cc961d2cbb6e544811e5a6adbb7b82 264ab5191f54099261d36b2fa67fbf503042ffad98ded6ac5caeee9e638b5a6f 1ea24d52a43ed3fddf2a4fde7d13fc73c8757e7446cf7e95bcfeb16008895c22 a369d000754b106a32c7105623859a2b44d167ba2662474d64ec90103cae1782 87e6981a1eb1f422b6c482628204998236f01cd4f563d9b883decea4781b65c4 26fd4292f36e22a31a5f17f918364d0fa1af7038b64eaf2c77abc071ff324911 1f7fba3490dede66a8ca0b0f67f069f48943844c22261c69d586cafe9d3f86f3 ff7c7a106db23bf12f87313679605e6b9e0c5364c20da190d5af82b5b6b22fab e33cfd1d5133ba44214fb24911a44deb554830bec522fd27f0d9c3084613f413 6dc71dc6f84a8bcbdef2d72e2fe12f21b29fe1253a015fd95dfbfe755486313d 6ba2e3f0a288699640458bb12023f8b1c6cf01e292f386730a7e04ebb17462ae a1ffb0a73d396083ce3a51fae7a47c10bce4e53f736331ea791393d9149ad791 fdb87a472b97c99a0c6bfa8c8ce93c6327dd796f3d71a3a34c4c81926d6da322 91990deb0c917e2d68ab5177ea86fe68552736ac7b702fb81dd994581f60bea2 093beb91bd66615026efeafe94795e5749797c8339f61f9ee3ae96e8a1a5e0fa d1bbd9eb353fbe9bc9c78fcdb9f49d56d310e287dc698ba37f5943660f2294b4 8342b638e5299018ecacc42e496e7b476d7f35ffd6568600573da43331f36226 0de99f05c276e608427855984b17f6a513383f9f3fc5914d59b85a214dfb85e2 c9013f923e6e641de8a15f21c821c00ce771fa5f36a35d6eb1cf71fb4e633296 a3ad1f8872b82cbf7e2607b5058ae202ed1c4f6b5a963db93c46722295f31479 f82e25aecb0428d36f2cd0ddd00a066b4d1dde1150f0a098c2df2c51003a180e 0b12cb3bcac1c79d798fc51b32de043cba54ead89ca92ebacba60d25c1eeb057 d5a7bf94666cca12b65df7241020dadb44f4321f8a1784a3467c2a2950723465 c439609c1bf47a2dd78f7bc5fec76ea959e61a531ecce3c5132f0470a438ca75 88fc504b3792b714cf1a6f09ea06a61fe625aeb30656061e42d839f0edb32db6 e2564ee229515f37c70214195c39dc123f988a4ac285f53676330739b5622da2 ffea0d4088d7364d36827ffd49a02c5e67d2d1fffda60e2268a361f9e665dc41 b90a124da487e998d333f782e11a2811d686dbb2387fdcdda8a5d03197bcb4d1 de4c52684e022e46d24b2085c15ee9750192427e7a9802e1a36cc1ed2dffbdd7 656f299420103185383c44c5962ec031388ed71a73bb7044d789f2ef6cbaf1c3 970fe375583d4dbcc1c7ee1ab99dbb0c96090524ba6eb8824b1675c15dd88d60 aab8f210d3a0fba5dfacb2d7173f21afa3ee0d6a987478f4feb3ad810c5f6f03 3396e08c2650fe809a12b5f44bd599b2cd4fb31c4f46ef409a49fd84a77a01c7 834731fb9db2e5f2d6e27bd7301e26be7bdb804bf83c1937c3c4161aedc86cb7 f242fc60275cc30369ead9e735952f6d7dd15057595ca6f609dc5d58a4791703 14323f5c93b07305aac7426b3799b4887a475c67dba4765ec8a70fd80606a399 dbab57d98c775867d49d6c52423c0b126804542d411ce9ce25cf3318c4550e13 d3311c559c2b7a814beb3beadaf4222bfb4cac71bcd7983d7217cdaa543228f3 de0667f53f27ec36fc2ae62706e8f1f31ad40b7c41c75ed114bf0e330fe87e2d b69135ff45f3dc2aa4801bc87c3fef26d331bc43ab564b85c13b068af18108fc 661d565d6920d157cde619ef12b6a1c5423c76626316733b2f292d9cfda997e7 a0c95af16260540b15491b134d8edba37295aea89c0100a1d48ed2c2b6d48503 92526a2a4f3a617b69c8f512790a038a7108df70693170aad803110083c50aa3 721bdf17a1d57f5d868618a0ecc1b30e5c7c532d4313586febd088aaf1e6e0a5 b15a677ed4b10725e31febc960832c10fe1aa4fdef17c7689dfe10b4ed11981e c556abb91a48f5bf50334d76d1a57a158b724738b1958de18091dd10c248388b e15542597c42cd9489f86844ee28eff51fedc25230ba60b8cc0e4364bcf91b98 d393f580e3514fb5b11a65053286acb28f27681933179ea9a876b5f8f93b438d 89e823eb75765537b62451caf9716647134188fd01e20fcf5d64c91de0fba39f 3c2aa2372f3c532107f71b749d27c2e6ff868b6284ba697cdd6248a2282f7abb 47cfaaa08546aa6c24db8b82a782231827772af391cece7b743e458a4d2f27b8 022d88759e90a3a7fe32cf5d4a589e2b6945a534e72ab513416410d5e062d0fa b1fd2452b06dd3cf267af2838907ad6e951be731fbcc7948b41d4ab9e2f3e6ec 1e669738dd5d5b4c60d68ea7b83543be705962589858f38abf7c94154778340c 6704174c51e37e606f2f59bb4b0d81e7911a7106f067275bccc9cc728167969f 27c3e5582d3ae50115cc2ed27ec69dee741d220be586a8e4a15bf11061f5199a b3e3cff7095f52fd5d429ae13feb9bb07d950421ce8d6eb5545c9c9f37ad7e98 c4e86321804d5a694a9cb01c250da8cd8ad9ed0018a57f753a9727939ef43636 8747be44c733b2774d99dc0ebbadd1d5a265990f3a89ec700cf2c2bda2432055 f3fb7830410b46dbbba5df560109114c9b93cc53944cf66657bbf67a4a3dab16 211000e8e40923d62ce5d876540ef386e9bf3d3198644fcd5aa5e3e82066424f 2013502249fd648ff99a93f5c1084e6b241d048348cacf5c07ca96323798e31d 3785d7050ec2fab2bdd440022329e791610e489f9a295b71e241730578e270bb b58f968cded07722e0ba9b26a0be633d011797d1a98328b938aeda8a43815466 81b119e54b8a5c218ea177dcbe332564e8f58c22c08965e6a697595b708dd9f2 569f521c520a7bf7c26733a9cafa9f68da5c35b0c66f737e632a1e8789e68dcf 20f86cb06e37a7a714ac5dfec96d1b59babed0d5635e700fe647de211074cb96 7878ef11b7770e557684f57ca6bf19292f8249ecf579320baf1cb189643f0b9e 80b6f868d06af612f5b7607126bce9df1dd538b90bd227074a493bd0f5a3f67e 33cd0f98b0d746b94675d42147c90b1bcafe16bf4234388af6cb8cf5672e372e 96bfdaa51f13ab4b987db32c5f6c2091c930e2141eddea4e71060d78da517726 f04903094be80d8fadbc7adfa9dfa821aec0e7d7c412c43752b1b18fc02b2f59 1e1a145654e2c3a77663dd13c2e83bf42b183438281cc5ee4f9496120dea8405 50c1b066abade654288d38684a982d38a346295559d77e14eb87ab9c951b7300 69 c400dcd66c9ce4ce86685cc8acde97f62a69f516225283c4aff3db88a91cf305197f9b94edcfe8a28a4e78b14e4399564c04cffcd13d253eb700317fe6e0c8063261649161e04ae8179cb4413e0078462c2cf275683e24fd045e28a0fa505f092eac2d0c056a2366ff5c8112351b25e24d34deaf2b6a949f0d7acba177b5bc083634802751410c043f6243854921c7497d229ee0969c589c2fee4f7b3bf61203688d748e066e7caf88d71369c6a683e34a8da494e17ce743d8b159a10d7ea80ab05f4d852046c5cb70efb4aa0426d602e5dce0432656eda9863becca0f523c02fa44d25787c83770581fe7e4368a3e3a4b30160847b701631a10593634888c038b142816d58f2a7e43adab6759e7551c085a6bb3111abda63cdb21f4bbabde03c79fbeaf3ef2ca3626b21d96f7ced93fff24ea7bb0d6da1c5133e480baa44c0afc2b61f593ecddd2a22ecd7405f87798a70aa3e7f2ef617c1170f850f7d0180cab1414558b2c17c4b9e0716f39ab6722e0d331024ac2f2a56029c9efa062a00d4bb1ff9801bf4066c3233428a9f7f968cef7ef5ed9c3bee97758651005b4250385ec154a1aa38deba0def3d5088cd98b49ed9d7b689fadad356fa00eadf59a0218c9d90b4adf0ef371b4b0e46ed9ece4d7a6e2c7de2070476f871aef20bb1a08b983bf4cf20e174971ac7bd35312ee8104d7ecdd2f95d429dfc51520d90fb0059218483beb000c8506c10c7f89b0f818dd47181a52b2d09dfc3c001c9159780d0f257285050863192edc696178fbfd8aab58b01e0a93e28d472a7a64a33b8000ee37d2d8cf1697180a1829335b603d06adcf68f746074853c9644c55dd9d0c039108ff7a9bc98a6e8d4035a05f0f9011157d9172ed1353b418d34eb29bbd860abbb7cb38ef8c2d28d103ea47ec924b3a90321ccd44f948bc6d547aa4a4d68a05deb5694be4844d0776aad0ea3f05eda75ce3bc4a9673c18caf57cd761f95b40255de7869b3e4d33a87290ff6b647d57b9dff39ea65e8339f773d7e57295bb90f96f4f4701156eaecf778ad2a9d2813699f6f16488c073efb5b637e4e725c2d00f9f5c2040868a7705fe6b395356cdadb21ee9ebb8504f6a4730050a4672dca0cd4e3aa473ffed04bd6e13b36f6602e7f5da33b7683eb5b20de30ecd754eecf061bc1752eb0179724c1dcc45e84bd62c92d430ef2eef0f3afdf6832a4c8946304f8252eae0dc9bed1b7f971b13975459da031b81a7f68834da247660b7390ff0afe5a4b2f319201771e5d97b9c4eb01505511ef60860019ddc7168bcd3054b6014c3eb6364ecdcfbf4fe2a2e928105a4d9e0596f029f708356ebc8f7170cf2203f7994c32101ac0f20a49796c3522ee0d6c87946b0aa54eebcf66a87b411f4c0e92fbcc534837ea3755beb2338cf05651e06827206c8531524621c96eeba0fb06eb99f61d215d8e873043cc0a39dfcf4a917c94fa00515c4a60d79f8504e5140b29081e52b82cd2d9da48321c45aea6667dcaf982583dc1f46cfa052fff911003f2be27cf931a1890f9484d8044bc65e4ec9386c24ee17e95c00512aa9b1e40048302e62066d4f64364f8c03f783a3c21f37fe8e6838d3ed31c90df360fee930710453502ff90f05fbfbb3d09815b086c082b385ac0affda9a2b10b670fde6f0bda34038f56622c38ef7cb0ddf88c1a62960cb344df5ddbc4e9e7c8a4a7669d07eee0492dbb5b9b3fd1fa48047e24974c6991bd2a98791a340a0effcd42cb8a0dca0cf4a41f2a8b7fd62b8015ca06f2eb68d2c9671b0a59cc9c6a78814397c0022fb2f61948dc917375ef30ebf8f8c8d65dcc9b744c68e20c0504d294ee1093060fa8adde244a30a558a8f48fcbb0d928ff8cf446315ccb460159fd1b53dad90a59bab26b208f99019054952c65d4c70e80d58129034b01697e7dbb72345a310840143dc46d5f19b4bb14692e852edfe2b0aec265ea67dc08a7eda3c01d05fd0d3b05f28eb2dad0194ead51efc141a77be97135f38406e932ab1e3257b731000adaca02e1faf3bb7e5501fc7b327df9af47925e33de27a5179b60e4338ac8d505ee41ac311b80df97716990e121b9f8e1433f341cc70d086bdd781a527b1e870bf27d43c38485aae5757903b479de59124cd255323f0a549f64a78a5f23969809deb5daa333afec4042da25df55f5d132dc2a7f941738c7fd196dfcfa76db1a0bb9216ab4d6f366f9c68e17153e36076f9ca8c5084343b5925e19af128ccb210e4292fe29190c5ad23d35352704e0c1de097a60914e1958253dc885aac2e4b108906b103f38ea55ba036a60595e09213237211181bfa4b3e002fef08749b1740f516f1226477565830de455436ef4ba2b15af376ccb0a8d090d2204780b30da01f728ca300c5c07af81422a25ec86946630cd61a2ed574e9b42f11720e937b3037e6154a3d38f08c400d519756360142750accc313f0ca989c747a8488c17fc0cf2603c61a31311e83843ef53f9cad5034b35e19328db0b2c9338dc61afb340031fca9bd320a3d5cce21e300aa4510198d84ff85a4d211799b16d021a8f4e1f02f345ad7fc08566bfe20edf13f4b113107b20a78cca1bb99c70f06b8304f6bd088e2b380418007535e18173c1c7e85ddf5d5816f23c36480f4607cf4e0881a409b81d11f310ae7aefa24ffd06ba45f812043b76fde0887d256452bb8d6822740540918ecb1c669e373ebd518a40e8ccc6e2772657a03dff6ac7d6b9dbc42aa00b6c294cdb557cf06ca1962f365d5d94706cac4e9ae3a5dbd0c653f356a753330f4f75e995e211fe14583db569569ad5ed133d1f2df69e34b83e5b25be881da507a0c65f5fdfe4db0b964038cebc1fe9aaaec0e9df0225743a1e1354152cd5590e04f1014158d4048db661a701241793f54debd8693d671cb9d09b883762e4d20e5f0e9f72136447d2205bd1e465b29dfd37664a5b4b3bf3803f6076487352a30e69c9371c8bc8611f446145ea9163a9810bc63abbf8d6b3548fd2ebc3ba19650b3528eec70b1c92e39124272b41a3b023e87fe10826154668f6566950341c4c0ae5c00cf4a94441321a7c3acf5a6058ec455f299acf966942bfe74cbe75e5d2079b3c87404cebf90870b59e4552c447d8cd50d3dcf913603934d06ef0b668c80f3a65e62c34ddd7dcfead42094e0f904347036cef98e8f87a5e0acfa7c105da056ee1dd462a18344cd39f60637ce06499cdeb71e63eda7eba803ecf26ff83b80ba905229f448bcbd6b4df58e365ef53b3116e842f5d51de1367a74e64d9d1320c2598b8ec8a28210d5ba475d17f7abbcfde3d04b3e81dae851e32ec9a7cec2c0620ecb02a2f1c3067199071a533cb967809e4fbedf147c2a38c82f5c40193250af758eb6cc4b732d92ef6cae9c7e4d3f733ab49f89fe923be0c3a8cc8cbaacc08a4c1ed0f185f6d66a8e6e7438536e9fec766a89d8ca89e16e14c58ef9f25220e43436f539a30ac553a50a81cb2d87e832dbef46467eb62049e7f80e87ec0a208ecbecc910f6ba99e929096293accfb020b843cf006737d0c69e6b4bf10b17e07f68bad5fe751a7e6c015d46c042eb0f100e6df524d05b2f123a3cad2ff2030096ffd9cf62314fd6a88382e2e0466ce91d929614fe0ca6b64aee977618e65ff09fa70d7deca54c729c009ea73105b9e6475017f680cb34bb5f2a255f868400c0193c376f7ba24e556641cdcd64afdfaa6f3f0d378903f2f841b320b85fce9960abf3c360a659951a5afbd16fa9d4a0fee00534831f5ecddef5788bbf26093660267966454f49f775500f05b3a5321bfc2e7c6f06474b50cc565940becf78796058e20ed7ff0b0d721dbc3bd3e4bd71ac6b7db831e4bee48762f9081de1603280b7027e9364c446689540f1dd3e7d0ea034e7e76357468dfae80b4cc2b353bae0d2ddf35ebf0252f281cfd23e61d6b289508ee40ce1ce453c2e1e2cc9e3a580d08978a2c48997703991a3922f68d36a147c817fd9d6eab228aa1509b4b850de2078c2415df86a1af7d953a31f97f7ef99bb9bf43a8a2c1a2d553f4b016a694410c4867a0744c3ed5e2a7ca4aa77f727ba23ad4c8430bc6d0eb7e674ee493271e0f29d882710b6bf73c07c1fec97bc84b7816b3189e9a57cb7518178079514b780105578f6a783ad337ef71e2b90134d504b6bca5446b57d357f4b1a6ced2813309aae612c5bcb48f71e538a22ba88b2793955c71b3bbb75de05f10941e83ffec03303d7a221e4ab1c09bc767f66fce1b0b165c8d6fc4590bc047252c53a7eadf08a64c357d5e442298cbb39d7e744d6365ae4869efe258d3a775af25282e175203db96e2c8901365cf0f37521f166f6aa35d71810bd4a5bc3baacb9eb3fec75701839bbf432e8bd02da0616216dd1b3b1bc1f4a87acce1ee084e00c4388d487a0b906ff188b5aff18b9282a16385bd5d8b27f929e9f2bf4eda70b5b40a3a067901b336a0f76eff2e4aa73dfbed34fd46dc7e8c025a631d7274a5c7f82c1eca1f02dd32bc8a5acfdf2c59ac7c0ba116e362f2d8a8291c11fe7ab43c14d1c995eb00b89ada2f48000c4137b330688739f5bfa8fee35d2264c4bc512e078fdfb28809be3938b9e91a0576ae7fdba0f03b04a73250bcebf651e98aaeb19657101a7e0f523f96b01f89a52055c5173ae44af145a36b779ad1acb70ee397a2834a240401f8448e04061d0017e6b6d34d4b96305f238db7ac6dedca3d5643a648f672b506430da4ce89c8a33e8be5b60d2dcf6ef0a204f6a6e0867d0c3344effed8505706bab25c7a54a57891c97429ec649b7978d4c415d29662dd2147f37ea97513b20edef5d683963c3145fa73a33c6ebd93b6c9dace9ddd69191321cb5c2ed406fd07fdf31309530bdb101fd46ce639ebd158f2e8ba786fb1fd62983d22276f167301ebcb33991d030eff9acf56497021a14620978773e3ce6f281aa56ac154af590829745ff1d02317709745b2e50474050d2807ce953dbfefce566673a7249b17034621026a6df859880f9c4871745ce520b89b7cc2ff558f17f93f73cd2f21870d609a4d8bb34ab213dad81c15bd3c84eca4f6a70f548fe6f4387585a04a6e7f0b5207b947be5d6bc4c223c614422331cf9ed419c2759cd0bc7bc24b2ee5f38009f8c22893be48b1f8b84159303d7f8fb70a4d155f9c9155fdd3383b0d933e6108dcc17735c3a90f667f29cb7db6789e9f0862b89b51e9503dfa76e300bf4cb402b5c147e306994904b828f39e11679e41ea3abfd3afef0f813ba98c9654d62c0501edb686e7c22bc923c250e312569e102b721a55bc72f61255fbdc19b23c640813d9b35005aa8536d0cf7ada2342a6c034d44d180e2a4b78ab986ccb64a79007e75cfae974cc46e90c3ebd3e492ab761c437b914e53f6f9b399a30b42c03640f48d7f779f49d6d2f2315f7ce6a40ea1db9a59e4d69a058abe94e7708ab8d9308da8de9c07cc5b0b79086d5f9d2611fa4c383b80b62dae2b7012148a694d138035e22d55503a71a5a8c51ef6cb730b84b694b9d640f302a767513f3647716090140dc7e9ab86b73d4d72ea069275a168667bebf74bf634b7fcf38c29a7645fc0e11ce0e5c7bf32e418228bdf5114efe80538fbea9c54dcf9d0b38aeb0de60ae0a201dd20af7297d3701c3369ee9ab69fd437ee170e3e03328942403bd67ca6d0ff833986e2839904c00888a2a61e74dfb3fe7b9dda80aa5b8e31f1fd2ec154e00fa93819f02e63bbc267a621df251f1c69a3404d497921a3b0cad359562a1d801e2dffedf404be7c36caab6526ccf425bcf939406e815f85802e7e5f9a3830d002834feebcc0cb68c56615b3d5c69733b63756a5238193297a1c15efe969c6405f0b82683b2201a9bfe3c452b67788bfecea92a4511a45f812e639b7ca78aa40d77ce773666518034887b921756a5aed92b79d8032fec550e90c5002a5db1dc023d39359979ae9dc880e715d2f25f592be9510aad4c52cf33f18d9cc800a51d04c20b8bd80b4b1ecce94c453fd73083812f05ba69c5258ff5b192c6639c717503166d197a5c02174eb0422e58ac1d2461b5e8bd0f722178565f879d723888530b543ac81a6c07e4a62422f128252636525b7a78f3a60745e5cdb1783338be5f0eb70c53c769c047fe8e479326b8a121d4e4e3b4d42fc833ec65b6e6bd7109f303ded7aa4e749c68a8a8b05a7765c172ae4f0b541402817ebf08cac386a228d3076e0246c6aa2893a7f2a11e37b8b3e02c6905e9e18992affe4c36120e94e1050089cee0252279797d24c076ed9e67d9a803e978db88593a47eb0b542af259da05e5673cf2b206faeea1aedd05fca70453210515cd09c0dfebdb0d0b532e8cbb002578e18b6620adcb39cecbc79d67a7ddfbbd9eff51bcecc8ae5693cc632425037e2e097ed14702da2dd08f154d2c6df01c8de2f496fb4a1050db0df38f4d8a00a9c8e79cb3bda8ba286177d77fdacce0011d48bbb7f9ed536789b5402624f106fde6caab2d466884f4389275af6bb4ed4a5e845b37ccffa5c2ce8930c1169409ae87c54edb44d322c07bf7cd485804f843a6f2aa93074c077762f45572e02501dbc53906d0d056785637d272572d9bdaddc5218f3a42a39a2ddd7f092352e2057628cf83613674832e56c552aae03e85074bb6191100a0c110c627ddc76b2b06ce9258be5b2efc22be6867f2ec28083c210a886c9d3d02874b9dd986029fe90ca7e2ad951dbeb8933785d70ed6ad7f2deaa3da8f0266a196714f7e7ec72cfc02ee14a6c48780d75db575610591b23a5039cc5a88f3116b2c9d619976c399f30092c2a906fc2d60624614123496df4916dabaf7bc7a108c4cdf4ee1fd5acf590589a394546d5d930d6921ad89059e928c84c1d831b9dd84837a0b1c0e218da6094a5f9e784c0d288d3fb08a26dcaf7b4fa01a04cb96019f9c7d67dac56eeadd0471058cfe766cb95a438036679e2389081963f536312115991b956e92631853019a85eef2ea4a792e0bf9940f254dd411ac7856960a8c46a5d4ce16746e443a08ca4e288772180219ac48cb649f284d875a6d26b09876d54b23d3eca0fc04c5080af9741a9408fc857c9dabd101fabf6765cc4dcfff839386f9347ddab7d85b0d7b12b438ec95d130c169cebc81f0460961d124447c06257f7b016d3537799a0f9317ad97415da98570f615923fda90f5a12ad6999d0bb1b08903c366f0ff1405bfbd5dffca22012908ac68f964ea9ce9bbc8af6da6c00a35fd7970c6c70c40003e4e6ebdc99fb03928b98e49bd25a2f5b727809ace603f46c76b6f75f15f2700f418d88eb5a195267a536db9ec99a6855aee5c120cc198c67113b1f9619c1e0af4f63a9575290b71dc0efbdad543dbc6d8334e4c85b47d8c82009bc477243000b0a5a27b25f6ea31a3ff0303e53ce4b46ecf3030ff3e47849478882e8d4c560983ee5001be5c72112ec58a0fc19087ccb075f83244d38fbe7082ec952b2d4a0a19b1cca4d5844638c28e1b6c6c57fa4203fb6677184c750d04242b8e216a1b067bc6ea154d72c86215cfb6eaa611d3d7afbed45308cf1aaea4bf4fb463ac1b0064d4a9ece22700d79c0ef69f28cfadb95110b187b8e8f7337c50d5f51ec6fc0d85e69751c968ebcab44dcd37971e732c889802b1d3e1796e66c23da159f5f8015ff85875b6e43318d59042601e668f5c1fe995dc6ebddcd46e5d65b2efc26501842244f5fe28e998ec051274013966785f76a39dc7be21121c1a27c1a1988f0f6bc3a157319c2a129b1e5eb220e01873b5b3eedc88739a34cb157434a4655b0adfa7fd337dd235e44e062fc75ec7dee81fe39169777fc246cf29bceddd2f5603488472441d6329d7a7a94af8fa7d01804d66fae0b1c9c987928c025ca7b7b203522ff96496fb70086aa61f3f12a9d94fbdc20ff88a3dd524bb0c369804c3120ba47d6d0d22e2c28a65b7c376e51f4bca00e307f4d2a5be7692fdfab601b24a00690eb26cf2a3f305fc565edb277c019340f5cb4ae72062cee928f40e3905f30ed58bf864a824bf8d9bb68b46b1e1286883b3a974e54a8b77f0dd5a4476949403a1b0da02e687e6d1a21d39a0f67cc8c5c85c0f299e8db42a5b247825787f180a6ff2feb3b8ed46743af099d36aed6f9a34fefd9be2c788b765ae6b2e07523b0518047035f9411699c63e8595a4b9320a481207e487332a87d736c91cf4eb2d091eb33911b92fe4678e1d8f13c19ca251bb8f7b20441d47e6c799dcf4096e6109a05327c481e6f79df04c2ccc993ca297b824baa8470e4498cf43b826aabbb302c43fdef413815ff25fadf84e9e3fa3086705b62cb710c74da79895f723a7330324816407de1271b60e731977d1af4d86e3d034a14a3e352f05f88885766cc3068ddb16ae7985db7b4edcbdb70de4cd4eb5f94654908c4039c3e317049be20c0f448022c0e3e9f04ad699e15a8df6da8ee0d9caa317cb489dc5ec276e77a05a089b0a0861d69e58788266f7d1bdcff298ab878ea92172354f08314b2a3fa10b0bc46225b16593630a998e044ec5a9bb1643056e3dee4c6f2f5ccf9daecf6a5e0894d7030025f9fcd40e05c9e73f6a54d730362e1b01cc9f2d5db45c336b18b0014fb89a21a8eac0e6dc0403b9d032022d44af3671489b8b6bfa7fafd3df8e6b081116778f7f3ef02a386730b643bde59acc2db7ac753c7270cc46d141b7ce900ca1b2b6ea29934ec3b0cf15f8f5cdf964f373156581e78eb6848092953b24de03cc082ae4cdc71501b994c65c48b9df73f2c44c7092bd53532b287dbe7faef701adb22b822c36c19af77916590bc395d298d4e6ddb1e7ccd7bb7933bf8ffd7b0f682527db4141308c3af779b5e2558b66ce7d5916ea7af096418fe3d65ce4300a2a1328d5ebaf7647c6ab32399151e1b8adc25e0b12d4ff960a440d11f5b0d005eef67c33ed3f4b4744d7d98f1d1f50d368dab30c16b1f7b112d9c212fb2c6a0c69cb437b2b248f68eff447ba39e4a7ac8d97273f2c507378b168ae88779e6b053455e7b29e1294a4c8a93124532701c84541d428a84de5acc2b2e9d7eae8dc0b87289445352cd29aceb27b5a6f0a9623e127f84b28160642ecc003aff0035b0ecb224e50bf2a10a84e9268b6417b7bfd12c33bd27d7c5a48d6960c01d8eeb40ee0431f73805bc6b3516780c77b8a70cf5b003354c529f39f45c1211647a2d30c8a810c54c0909d6482a5aa2e4a9f0218ae551a20a93980eb11c80d584342b30e0ffb450e790246bb91bf0b7ead7161666c2cc64f79b745c646f514b45c0b1b0f39facb1925899eb80c2a50467f301e7f1b7789b6206d9658222b85400b19990e819c1ca5106625324e25a890e46ae8b5817b0d81a47f775f06160844ae8d0d087779a213a0d6e111a2fc71f860b9f80df3c38e08ac7b71fb3af53172f20d2004cea1ac76af87c6117749bfa054a3f69521b34b31d58ae8456c4ae33e36f31d0d4a5f3429a5c926b4fa2e356fffa44d9ad1a562d6d9be6a1896fff390d5194e0b94c2375aab43f8a147c5825f054932bd9b88decb2a01a65a4618d26a4862440d3e3a8cd082c654ee9e011c3531f2f4ff33b1a9eeeb2025075e3b9fa6ff3d2901a2cce1d29cf64329d601f836731b2574ddea2d89738016949a36539a4969b4032b29b12c8e1029a8875f675ef74ddcbc88773b3fac28b6cd53fee4cb47821c03d4968eb2ac0e2e91b41e38dab10cb030389e28a7f73cafcd567429a8418e2105ef8ac5c2ad71f3f7e2f7682a73c66d087f67409f736d04d4b60f68754e1dc90c2412d86bad431dfcc8a869c5ee1bf96ea7b4d2a35ae1d3e0d3bb6bf76cc8e605241a67f2379d7aefd2bf3abb9a987c565d88847ee6f8c2e64eb15d1e3b2d0205b922d042c20bc376cf61a1386cd9b079d0e504169794682fddfaf877807cfe02eea07662ca5bb79def9b0edbf6b15af6455c3ff18f81896ef039f28c195cb50565e96c47072171879bea8eac4e0766a59d1d19d404366d897bcfc55c06fa100a72bd1b764d04cb5d94ddd553eab51caecb49380fd56c3bf23c40dd8ff37f7e0e852a6f78760a395324b1deaa1ca0c4a52033bce9d37665cbccfb0d628a0e4c0ad05e7c5d37e387d90cea4890d7e9c8f976fa4ddb60b3de212469569db616a607901fd285abb313c11a8959080b3d756450b1448a494642d5bfd29c1d92175506014ce8419cd49ceb69b9e0cc11e78001d4387c16536dd0fe697e4f49f3d7d0009722e254ab780d37fecf8bf58a2b5674a152b4ca68e3fe7e5baf6a959395f60ce3eaa6ecee3bb34c47c84f825433cba087b8c9e2f0de0f62eb2c755d0861f30a05c065a53439be2e794c3d5cc197b5e25b5124d743d9468db8484c65bad12503699c33d0306b5e71df09cc4cf50cb9bfec7f8ada291d7ba77d079a0291e09e034e8da6a27347d48f5b9975ad92a4d84c7939fab99e89f319719e3487ec8fe409629eec569c9928663cd4f4efe35ae8b6876122ea4b1e8fc4869fb16ae58ba2052f4ce344eb030d2f5b958b8a0a230639b6025ff6091db523d74654bf6c22830a77663dc6e3c17992edcf882fec55407772a403a66d74aaf7c4c8cdeaff486f0056cd005772903c3e8873c2d98b7abbcd35b7d154795c2f73dcad880f390eb3035fb9fceb50a8e82dbb11e9e27b7d08d5ccba4b7b825be799c14204c9c3cd990f0182abd0675108e7641219e2e287aee0e453026618b384f7ec0475b6c2761d06539dda33e894dabefd0595654ae0fc1fb42c70077309fcc58a221630bbb86d0dac4048b707e179797fc99600c758edf10ff549089f7d66bc096bccc7859115013be21833ffb1169cc1df865999c6e18428da149ba4cec11e46beb5cd8fbaf40412b4b42362bcd36b56b78b04c882f99ddafc0e41e0675fd463919fb10cacf80ffb45859b8726ec0ee70fb2ab873caf5b333ea273ac9d6bcc59e4f15d63891e08c27fd61a9f583b90d4cd19d8c06b91a78c6bc6eef4f81920d4b001be90a9520eccd8b2018d1c62a38d27388bf91c620739055d174c16f36f0abef8828a58ae021b174de9664d41dfeb6206c394f5a0ac2bad288d4951ef45d09028c07aae8d016444bf52187752e9686e8d07a51a906ffb72f0a653870e29c38148fdd60da50771b65d9c65eb306efd0e6037d1f4356baa6f0af7858e1827d6cc7e0fb7a6970217f6f1ab34254a47a80c9d615c5b1de576350345eadc68cb05eff8fdb7140d014dfd4977d85ddbbcbabcd33844ccfe1465a180c22d844ac250c6a36cd33d5505411ce0f59e32b073e75fb11426cfdca258f37fe8ac4f86b237910ac6b7bd6b0790f2ae95924abb00c3adb71eda0f96828f0a548c9b54f4b521067ec61d643607f235075a90e1d6605c3342c26173f941234cbce6a6bc94f8110b35b5d58b7f008d232250f6a4fd39831a5ef6f11245f3a36cacc3d8f21acd7e113a5363ee91095c4000176df4c981dfb3f976b7d3f90fcbb7f9180578278ed148bc10eed10d0b663b50375760ab577f84172d0c7aba9c8f85c0e805f123fe1bbd8b4b3438640bb3b1de5d94203dba6a929b33c4a7e8694c7394522a48337af5cbe1e0c203cd017ab565f66f3ee9a2892b2da2f4b905a5d715f0cae5b66576a592830a5c8c6301c445e5bc769310893c33340a4ef72aec9ede613f6ee25c58068427c7727ea104d163ac5a8441b156593059b6670ded5bdbf51a1a9a8490fc680eff9bf6630504a09e0497de94419c2862d9a70441634557ab3706f3b3a57377c8bf9327d2ba0a8ff2959855984b7f85a425ca73f83c1504a76c9aabfde872c1de60089a873b009327195aac6b916980434bcde81fdb1a2d635e29bf77d0430d27b2a47ab6250c422fa9fbca49e7f9c3ef8cee46522ea6f1b1dee38e91b7261c28e48576c9ad0302ea8fb85c0389060582e699a416dc503562826c63fd18b96aad2f7150356f01e962db92adae0cfd872f3e14216606ade6df351dbd477751dea063d05438390d9ba2b48519031a0ef8bab9560f27fa18add5aa559e5b7f3b4660c121a990b30f1605ffcd2907796dc5a83a2967474e33ba236f4460b519bfc5eb1ef9dcaa22056460a35cc59469e3d11b5562a3311f8dee918dadb629186b7fa80354ad09920bf1192144a9d3b55c4f617623cd705b8f87df12701c740e59d608620d051cdb0a681240f5c3d0738eda7b6992e6b17d314a46b99467c56e7dc3cbce4421d7a40f6f22eb6f5530c938b11387aaeb7e86295dcff2bf89049611a2df51d26077020ab668a47ba24e2746018dd3e0a95376045c187064a5c09fbe38796fa5beeca80335dc4c3b71385101bbd8366eff8240bf7043b7452a515807d7976d754e1dcf0dc3f86be7e2b5f7767c2f5454cda40b2514ab9d7fd1b2c2ed4ed832021481c8028c3525173166789b5b6e85a9900bec2bb826f6c469ae68033dee628716cf0b05213960bf87f65fa170cf3981bc53d9f0e4e7060190bfcdebf7047f881b1bfa0df4e4868b44120186524cbc65f081ba132c094d990c36c53e78a386b89d89920ae39525506af0312d9a8d53e3288d69fa44f5b7efb80fca91f71354da3fcd0001d1cc5efff003db4208994c93b1c8ba8752611ced0b6be64385dff3e3a1d99603c3cd630aec5182a7628a9bd583d3e0307e130c3c056ce53df7823781b2be6f0e68366929e3db1d15163699cb52edcff18c7754746628ee8b83c94e32fcbb540433a3e1ce3ab88fef6f402090efccc0704f767fbc8c37e697445ac53c3c7c500d01c921f64145178fe1482a7811ca416a917f8c708317df8f6933c586740b5301ef611168662eb451e3c87cd9cdf97ae0ed16c7e8833f617af3fb288eea9b3009df80cc7c9f3e0d87d11ef867474a9f5e2344a777db658d4befb424c855ec0c081461edbcae88127fd292f7b1cf535a0fda7690d37b6945161e8ecc35f638f40bd0832f4aad78ebb0e584cc30bfe8ccc2e75c99c0777443b8959fa21385b75d0958a141b7fb96b9f2685848d287ca4304bb4390b3758b856ae6995d7e67a0340db71239a5993911ef80895ccf5b36338fce1f4fe234b7c722d912ee0e94daa80abfe2ab020437f57271af71cdb218d2fbc924008b5e667a062fd08c6db388430c30863da329717b25acc19f33581fcab0e3bac58c208fde5ab9498dbf22ea1e0d3f5247b9e1549e55bf786de6e1a61ba4f12f19ccfc53e01d79fe76d180b657092297fcbddd5e079cd763b3fc25c9a65fb0a7e03061e14340e60de0751285d705dea83c50aac7de7821758e4b754e5fa8449c7a7630addd177721a8a51634bd0a08e4497e610d8ea8a63d317e9ed4534bd7773e6596ec0ea2b4c2927a17e50f0241f0bbb40456d859a1278340f9e48f7bf9bb7b588a290acb19c9ba56a63ba20ab053242b08f86eb28b911f313913979f06f912ca40681ac2b01066410108ac06390582609857a672acb93cc2c50ba103cce1b4558430c8fe3741909bf5b011010847f0b8083577cedcd5282bdf132d8c3f556b3d3e31e0e2220b7e87742ea2017a5667f2c7754777123e60f783f2c3ff712b88a46462bf36db6265469cd85a004473e6ac6729bf13285a046d8149f2ca793b39e90531f02e56f7989c2671df024c7084bc037d70b28959d8a6a9b8263ba30325d42348a4a066c9b2d56fa60c0e522c097187ba5909b5025d3c578746b97e411ead24a457108e905d2835bc2f0298717ca1e9a337529fa274d92c8b5812806f9da9885ac354200f9f289f33d603643b05e0eeaa0fca529ab7f8234a9ba3c67bfd146f29922422da65310b8fdb0bbafc5901aae1573ea8b37eb05e4c0d09be63d4b62dc13f3077d58dd3ac43cc0a9de82e3c83f8a234c1f2b72621b09c190f828219c364653ced8621e3172c57062f32e42e8134da67e1191e8b995d06cd6dbf8a7e8188edbd7157e8f1043fee06629fe0c7e045a3c28cdc99caf78f6e27bdd8273ba5ad7eda480b1da10a87c9057020e229e972f294612b0e4ece4035f6db503d6b5822b12e82359d82e2bac301d8d04cf2ae0b891e2cd4a98136d211184bfb5bf5ba1b9de0f93ccbe270fc1a073d3be3d8b76c7faa81e6f585bd87535fc65420920d81eadc6b79401f65b7bf017759ff27515ecc89d960ea73be9e55ad57bcd4cdd032e08359a5046eb8d13d0042daed43382c0c4724a6aee3eec70fbaec9f28f3f35d8130e2cc738a04bd0e0bb49ebd470ecb7fbf8fa434f848e8aed33a1fd7cb65fc2415e3ad3bc57bb50a09747bdade1e58fd31e5fdb498b8f110bdfa390fa7651779521d1fdc9c3279ee0a68921330e26177d3e50ec20cc2efdcd3bc0a3fbf42e1f85eb184559d6674280ee849f04313627801561868e3228b1d3e5a020190131c914c9a02f9c60155fe0cfdadc634aa3f052555b2d9b0352d5710cdfea7d838104daa0d1b9467f06b070dcb39dfeff739f04f5224e9d4bcb820fe5c2f1771731bfe4b759140b7956fae0e7978415117745d1d57c5a944119c62b02321f549cb8d1a487078d3bfcb278907491c1808e65df0fa1e365f7d8a571fc6b43c8756093126af5454d4468c66f70dbff9a48615d7a41e4f32b4320b30df1c23d7f03c5632c40167a5ad8f293a150b56f91b38928d56132d78f0a28e1f01829dd27cf6cb3f75f00770c68df9fde10b3872e42581fa87ed4407c088feaf408d9933cd7e1ed859894df3c1de211c1408d151cfefff4295e50c46b4f7f7d3747bd5983a25a616ec095134663747079504bfe13d03791da920f5c23091338a07ef5970e76fd98e76f0f241d4aec484960523ce18f4a28f19c68699aa26d312e1a8407b7282fd5dd7ee4641a391eee7f20b5e693f4ae9cef7219688d70adb14bf582ad3ac36c9cd678e257c52aa27f3ba034c06408ec109986dc525c3fd0569f4c87af71e2e6998fd52a369cb7ca52eb10732dd4a18f1a76bf1a0216db6d3e89b5f29aa661570328708c84ede69aac65b0249544addcc3fb1898d33b9f87272f45a9b797f97880f77c03d16e84195041400c071c2d900cbbfb08a5ffd7183846726884e77a506321dd0c395271943a9dc08f4b5c8cf5872eb64d25c34d3830c4c45f7b1565647b985834be21a00fdd2dd0bb6116d0621d3eef312029dcd6e67941a0d13a3132c2db6f307eaaec88b5d6b0f8b9c1489ad9792006052c0305d03e1ca72d6e0bfd00254a7bdb0b5616c3b5f0bc70b8c47f6abf58511cb7d7474435f839812d0db83bb5152fc7c1bfdca2cc40d7db234280d0d07672e262533d9be4e7cb4fa38d266991f892c723d579404aa05306cbad97e179acb8ded8be33e87c3c1b7f0d13c63515170a5888135280c360b6554a5e57920a494728709709780766406988305edc2d3e70e36a335a2290908f749e424585f34db52bb23f266cfc21ed7fbc3d8c361c5c841ad52c05203e40d987e8c9f277efed398f9c02ba060a8b368794d9441f003b5485709680b5f1506758dece3a5456739e3a169f54f2a297d1f4b7179b996277f306db2fdbbe76e03041956fd992ea5a6282b81bd84b7fa17275bae847cbf6b7b0ce38b3b91548e02abd2b9c4d3ef42762a6fd8df382124e0bf149e3e9d4323d0142e898fba78830ca0344c27f1616ffb7f8353d0dac1cd7d8b172f8daa7128ed7f79770fa476ff06f23b93f506f4908ac698d7bda1351c54f97249be14b744a943de8a03999e630bb059302079a6c3f1a6d8e56e8a48b7f76e05dded7e538be30271c4d5b3bc880fbbca179510f742078390c7ec3a99a1e982775a035c071b653075f4e2a378f0016fa0dd3a0c9bed8ebde17c9d1d117b8a3aa9a4ede663fd4cd91314905df06b0a83ab5aff06ccb5e4569053161cb76e74be5749cc50c3fc26238b6245e580350cb966b1d375f2060409cb0b25620c4564b5736a12443d83c7b7720cc6e2cf600827f9a6fc1b221e59fa1e14c827dd1513b512dcdc743cafc375cdbda5bd788d0fa0b5faf78407eb59b77c34eef638e28e155b21cd6a744c0089792c54e0647a0459540d8c6c880aa75840e85a791de8a0cd107d6aa802584b6ff25025020b490b3a178c1ac3adbbd4d024efcac5e6f7507ac9c4955317492d53a6c1a5bb070f05aaeb989b5dfafbcd0b676b973057e7d683d1c891cbf5be02dcf1177caddebf0225bd57aaefaf5a1723887aedca0d8e55696222a81822995fc4ca91001c7dda0604603fd3275542b104240752c8cc50bd45453f606c0afa49cb627e302088c70a6f403e7ee6837a9b0c3d8ca404ca829b777f7d5eb3efc96481f05ececed3370fad98b4da97e7b8e5381e82ee4f3d07a1682a0405b68d7d8b46ccb2d106c1550ab4f31226e835e7ad9d5d63e17a57a6bb14358fbba10e6fba89fcb419e82112084f217121de144660f41447151067229610227ced84f8829cbcd7059c074cfc0f9f604b5b8ddc02f03a371d55167953557700fba084a7e7a6fa5046b905642e031366d52706175e0b09027adc84d6b799b305ab984e7b86c3def7d873d8798603655ec65a5ca477d64c9e5cdc589091d702a047b28d48f4bf97d748717af65f0eb7bb81fcbb1f6e56f3df0fb12f89f21df6b0127479f84b75f9f360bc76f4de0ccfefcace88864fedf39ca25ab28bdbdeffc63c9d8bc84a846f91ac9cf4ca7f0f5d4b48cdc30af64ca7ce116a5d7db2d02bec3897583d786a9956af7482cca70858604b9f6defc7c609670acdbd2923cf0baa48fc9c4e3512ef0de03597a095054069beb01b72bfba5803c427da8cf7a650fbc45be786c999e9982c35bb1b020f988496b7966daadb50c9182f0a96251b2139bce6d2157d257c113c0bd96893071757a8ff890aff9dc94cc152691b8487705855b61e21ee22a30fa1ac3b43ce08949226151c2b8d82de0073d0187c2580a53f0eb32147385829bcf4442901500378cfc3f49c16c4d7f226a0d3b7fc6098a5c7a5b510babf68795360b4402a1b09647bad1da714a07b8bfc08627543480b4cb96ec61a3de329c17d5f055159550839db697d633308a8aeb50f5099ea78bd484457ec9e2e81224365ac78adb1e605b76436fb16950e4e13e9d9b9308f0cdbddab1e8e68efc62084849cab91ca820eb787605021ce1322da05638501cbe914ed3ced9ba73115e4746f06f055c65102313765aea37ae8349f2b22715484ebb6d05461ea394a79f02398eb668aa1ae0bb7a3dda94881db130b704cd288641321a90681a54034165a43ddfbeb2cfa9f01669eef11b225bef91b379b88968bde6bc8a638a574a3e3300ac8143b13f5f902d5dcf331c168304a7668a783e21cda7ddf3286bdb88ab40d3630b1bf9527e708e05c02ba08fec5b4ae0771d173c97b81773c3ed55e900579d1f4abc01ca7d405cfd9d5b877f8b29a2c3eec59d652f5f2b16ca729fef248812e65a7d45a3a4c0f5b0c6233e9c2287956411be9075be3b05bd6610979eeb54683fd473e44efd509e52e64df862b1a7d5512b26d4fcae2f3ae4dccdca15603ff2c46a71be6a3e806aac3227c5479e067e410931975f5cab25bdd9e52ff3be6a99b94b6263f6ef60a8fd8ea935c62f6b11386ad8da37f288fb137d985c199d4f9a296573da418ca0ecd01b2418499e64c5244cd9c254decc386f8b6ae4e03d0ab17a78cbb2e89c702ce1e1c9b637e12079f655ce79dbbdeb45714ca40275606a80b9402c83ae71809600040018f6a0bf6fbc6788463acf5b25f37711abdcfe77cc2059d7d06af2b082dfd7409e505447cbdaebedaba426b7562485034a2a7472f934a41c2ca902d0c -generate_ring_signature 4b99442b09d30a94e40a661547b075704ff7076e432907efa34f28fbf8f98c3a 8c817e1ac218112b22b9f47fd1d7de82f36e48900926febdb32adcfc4e4503e1 2 3596cb2eedbe31599dd34e5577a038571b60800da674802f977182ddebf3cefb b290773d661bfaa5cc2d1db49b3633963e714e9c1f3cc8d843fb7f9e57e199a8 1452ad276c1cff34d8ed225e848e5f61080b131f2500d6bd06b7e85f85d08106 1 e2b501cd7d0686a838e640da652b738ad74ec10381cc0bbf43130ecbf202be05d0e2a4e1987d0b31cfc1e6736d6825b974545a03d23f58655ffef5304c0cda003f8b8126ae215bfcf0e6c3fd67e14684f4c77085f54d8807debcc7cdc1257909ea2b9e2d16949ce92f881f0cc0b373a28826c9d97d89d3fe7f7817f250943e07 -generate_ring_signature 308bdb322aa460f8c564d2134e23833f4e567150599cc3cb027083c5172a9ea4 6025c2ce5f391b371ceab7d0e0a0718eca1a660603056c887e0117f0c838737e 72 2c78fa1ff09188a0c4c26871aa125e9078025ac1e49d77593ca0dfa1989421a6 4dfd23fb69675f8836f93cda1dca1f2c55b68a16435d8ed3902c45709f11ce2f 9562994d83657b04fc8a26ec25ec00dd6c2650781d81e866a1e4ebf78e0ee80c f5d9c7e8ad13a830598f6f587c48910b3cd4b8d338c90a3194f3cc6b8c0a1a89 515aef296c04dc838810cd2dcfbf8f4f42ebfe4942679929cd3247f333a7734b 0f0f309eacd4b8ac55489aafba5cacd746a1643399130d56ddfc77e826feebaa 464b240b5fa690508441a556a6e7043451d269ba3fd38658a46dcd78d449b781 662cf10b9f6900077f61ef37bf3b21a4b42e0d65ecabfcd5c593ec23da6ef016 48095d922127ad39050e52130b408147dced83e557b81f237e67864e68e2103f 55916c66cb8086f42769ccf831cd4356d31eb6c3411db78d8b8a8e5d7e26849c b72e843b6a252e3d6cb4f98bccc29bc494779c107d385cb52fc9d90fdbbdd43d a9264eb06d803e5d7c9f5054678d649d87dbfc7a2c826a2346e6ac929b55fc48 df7e5cb855d1dd60f6efd88d00d9439c92fde3b71567cb46aef17c76fe53e51b 5c3b976629247f7f10f4c6d6c52846b9b4de1d7a258d84ee0811d0a417939e98 55a366725e671d0136fd0b711fdce650b77559adf56753f07c4130f18a36f024 c205931d4633f04b17c931fb00f2b47b8b7fe2c9de6def25496ebe5f61640bca c3baf879f8a858219e26c7983a5cc8618af19acaf1871fdecdaba355d6ed48d3 5fb47938e220e2eca446595c8396c75c28d0e180c2b3b6c3b4d9c9c1593d3ee5 ba26eded826bab30fba456ea53f5c34c97a1b5a985dccfaf6718d1231db6bf50 d5c01cedcbd2bdde6d37011766d8a864ca0f53943501cc0ab3933a86ac0a530f 434dd5d7e49617695b786c2ed3e851b1b76a5140369e2a390168d21cb99864e2 f87c29400d724f5c0c8e4e48049538035a4eec610a74d2730ed76176efbd84b1 ff55b2cd5d13d4d8da692ca9a0d1af104e1df3414e4b0b1821a56ccf676d3562 0cd6a4a67367d58f553ed01253582a17d8c85d8434b790fe0d79157e8b7a3fd4 27f75211c127aa1f68a3d22dcd83200fc3916cd1c7951e538600a31d19202599 3a23e03dc653bdc805cad9a038cdec71a0bb2bcb6de17e2db4467df3639ca7ee a1b76ca66834623b4a2445c7f369cedb1b2c0e884f938598bdadd245822ae52d 57e14ac6cf4de2ba8f4a4fa39131a40ff02398b2a2217a0747b4645c5eeb5703 0bd34d6cc73e4824f3b96cdc68b939fcbb3f3d64f85db0e2f404189d39e1e3e1 abc73c606cffe36f175a81afd69e3bdd7a6d534aa8da4541aebae0c5d78ff2a6 23eec063528ed18f12792077cda4ddcd7623b8ad7a8d9f194c0b25495d6d774f de751896b52bc0803437ec63efe0f68e6c25b17211373a6c083e9233cecd4c2f 88be6f30459ff0a926a4d7f57b9a71b55250e9387208b19b235f4e69511ce887 d64854d2a0eedab738b9155b41b30d425512d2165fdf68b60963e6586f0a75f8 29a1235869a8d737511ef31916570692eaadc9fc5a836419d91157959bc7408d 0cfe56ba540437788376011d248c5b0addffd76c8f183e1798a81a05995eca77 1d14bbdc17e017020d8c6696a1780ed8a92c1d25192da8ca83a0835ae6ceeecc 603114e5f9220594cd088b81a0cfe4b54fd73e0ef821e478fa1b3ce7dceb32ac a0fc34ac08c99f31ed89e5e310ff55208bc3fa2b53a750383992d460c2a12dde 83ed317fa3bcfcd2d5119593bd6e24db04b15669af5384d08d1bf32ca220a60a edb90d89c6618a866613546e755fb50ae469b95fc7148baddae72f531090e1c9 d29ec006b1a758c0cc9c6d3d9f2493295c574f90d6c6ee53840ad28c629fba2a 1e8eaaca2c4a8ff4f4dc13490237a0a97f061741c0b06dc5ce2a6acff153bf94 68df50cb1ef38e0ae678b4d449ead853d4845db7c1d41fad198cf2aecc594d13 e01e97370bb7b5364dff8611ae6da78f1a38102bd5e03c5e24eebf24f4ec00cb 5553e29cbf778f6b48e8c5e2a68c9bf3d81442e1ed81f25ec700a9e23a662466 23024f3db8eaf17fb8e0e05330a1c9760245afcadf2efcc400df7c7d17a2f767 a827106be8428f79cea92551f543d37393685ea2a286c66cae92f8182cd39605 0b08a56085462887c422032a20128ae61ed97dbf5e74fd8d93cdb1b6e19461e3 3b2c211dbd384da555d95b648a128b56ab6643b216cd0ef0b3efd2383428be80 ef5b4344e2685e2cd651042f3f03dec4e83d3fae8e19f7390756aac1252e8a2c 6874856045a450c60b61ff651b69e34e2c834ccc28f7f483485e43f2b7e619df 397019b0b5f8892c9cd5578270342cc060f29908a62afc7833f2132ddd890492 56cab4e804d55cfb63ce4827aad3984945bf52a980afc5fe65fe9724217bc007 516eeb63ea2cbd880702aef419a1a7d6b300523168bb60c1c2f2176cf1d3f0d9 0e9ae60794a835ce5d1988d7c461eb18f25ec62fdabce76be9ba7937a9f69217 0cb857ba94a45956dbe3c9634d5c0cfd5bf97ad640af92d0b99fb0544d7693bb 2095075d719d82294627cabd25b7dfaf7158c62ad211c771d3644c29a5e8bc7f d45ef2f378c76e52ff85f3586e3fab64411eb387e3a68d54834160e9d4f75195 28e2f30d2847b093194a3effa41f01428cdad1f2d20cfba6eb5139f9e0163a5f 65bbcc93001475bf68096f7cae42be2412e3ab74a60ee2f4926fe5c6aef83f0d a61e3661ee0c48cd01ada4fd2be409d2b461acddb708ec548cc1bb0f7eae6f97 d26de17c60cdb8043f11c087f8cbdf014dd27c7ca80a08fe8aeef96d0a1745d5 1a493350e3b85b91b47d8f1f52effbe30f2927f6f86b73d13a1bba57eeb6c67d abd5c231d76667750f9d4a234631bf3f72d067f1b88a4b0926dca81004b3d0cc 1a3e09ab6047738e738872c6cb46d5157a0978ba84af4e4f4436fcec61ab6e6b 29231906cfdd684e0f4f21ff0b0ab42e9969bf6d6064b38b735f90428296105a b4f5a4ed8d4169c040d9a4c387eed3e2a2282668f95e5d59cda02b9282432bb3 321e8195768e71c1859bc220da29d8b7315671615da6f7a1c70a65c08a1c435d b8b37d590b0147ab67db2bd9cade249369b44bca21e7027d56b61f375e643a7c 4f8253bd0d973bd1079b792ce8e714edfafc5b30625babd7c6c2a9f04a25f074 466cd2e93630e51c245a31c4d6cd4c6918374d45324eb23022b87214a23d4fd4 990b405d17c5c8583ba5b4967781da5456316385111c52d4c1761d44062eff05 19 47b66bb3d477a5b04351efc2172d60e25d7de574c6a9092d9dad8c760bbcb60816898fca364efc2d7b09d4021b2bd01d5b2cddde228aecb8ba95b0f42e31d50618fc5ec26512bf8286d0b6df805b2cecac7d721822138e6cd628e7823b221b079569d5acb33d57c44c3c95b473c81f9e6c508ae6d47ca4549df79ee1a4ae4005493383663459c326666e639f292946a242c6652ed0d804a0bdd9496a6b4f29050202ca0bd475463aa1894cb91ffb176e466df39c0e11d34ce231bd247484cb08559c28ee8fd0bdb4e94db9d995e3b6dee8ebdf263fc9515d99a555cbbf0bc806fa115a233832d759195a91fcb37d2aee13ba510e5c10dde8a4d33296d97b730a0fd97ea4073ae4cf8e0bb8ebda36bb58baf695d888cd3076d7eb5ee42ca28a0448c3663455792061b2b95556d3d6b087f20902aea04a2327704a8aaf07abce0269343896743263ee03f888c2c538de426e5d96e33de2d478f0949e1324fcee0df6ede249618b5a567d430d9b6a2a2f51c0d887b55ef4f9364c69b7f29284d7081ec2290ebda8dfcfb61c9dc988db38035afbf437ab63b4b3abba168a58bfc702cbcff10e2d992f1f3caeaba8376de5b8e0a73b20b2fae09f396a9960a95fa00248a774092660bd99afeb6b0907d372be6973a97646a4f7866eadda42bf5a40097ed147f36a3c8238f2ca734a153e2e52def8dd344dfafebdfd35bc78569cc706134cb21608532df9083c4e2df48c74f6e731558e6a244cd50b221ce298d3d1066f46091248718fcd9c45a6534b37a37cae75ea2a4591323263020d076f6efa00643b4a4463fca29efcddd070ee4816713eec42b57f059b7f4c556ae65991b709a1e7566b7b493d70d40c6b7813bfe2c127bb75165e31ddbd108f4dd9af0a5e06ef4b1fe7a9dabbd69e38b5816090dc63eaee84362a759a171c6ba361d17fc907cb5fc186931c35085b4a3879d54b4046b20e943dcc03dfdf764ed5ddf31db40eb912568d855607502149716b3209638e4827fe6ffe17322217df4b915d9adf03e2b87a48bbba16e3f430dcd02c83b453a9c18d98a00097a8b76ad7eace043c09d3148435b866df326835049461c52a0a027b7c1986a425608f98325e9b3f27067f96b197442a48120993cd96f8df852b343b1961a72397728e4bd398d623c30b5e9cd054d1bd6bb93f9208529e7ecd4474d250b811132a3f0f0d67fd40fc07013feea269de72c83f7bac8c7578807457e199c56b6352d3cbe1ace5ba28e35704b94137bee9c728c07265bdcb8db835f35ddf935b6d6bc03a1535f6df38a2f60fb5a703e07f4c013c9c2cda116effd81e06f2d39bb698653c716d1108c6e47c0c1eda383a446e6811a28c07af7b42b8f5a43f91874ea2f5358f0c12f245bbbb003eb9d288b53632b31114a332b26abd81593258fdc4eb6c31f51e36eb8cf98c00850e45e356eb37afa936140ad2f4a7ba0bed11433c8d798ef9d4b06b3aa038010eafcaeef2b942d5a5ef611d6b8e8739105035f4c3ef069b3692209e66c06e00fa07bb8b0e2b5578a396202d2e3117add3c9aeca2c95c742a1f161c3abac3f07a5d6cf09323a2ab782873af2efe5276a25c08d401c5ce1d9a2c7590f1b848f0bef3c40837da48d04ba3005efb74f7a6de2c689a210d46124f32691829443a3066c10190929c43912e0f042ecebb33f0a2b384a07d8757f62371845e5eac8d6090fe785f4bbd390a293de6a2ac8b57a14de60b014afa8f17de6e20059879aae0a956fbac23f2deac2c0cda6614d52837f71210f5b0448415071f69cbd751dec00a002f17a6c4adb748657dfb832d43a0c412376cb44bb363bf0cd0ef205c66d05c722d2ea1780617b156e531ee0fca7845a6a8b66ed0af7ba056a6d230e5f23012d3df0a011fb91f74f1d14487eaf3e77b0c2eceb7533c6ebe2f2a3dc6d655e0118b11fb316f432a72b5c3a28faaea2a14bfbcd3e5e391c3b625b7ecbda2cf802744996d081263741d9821dc1533ddf2ebe767dd8bf697ff023ae7f63c5b3630ea71e254a5eac99d0c172324b8f06824890222833c4a3a6d3f7127aed56480007702d4644b67c18d92906dbc980efcf693ec31dfea9ce1f9061ed3808578d7b07889f27633c4bebf62154fef766bb523effd4aeabad0d950b86095e91ebbcce0b99fbbd5ffae06c4a2afaa187cb9bf488b95a7bd32d5f1f9d08e04eb80852510df4c6da68c332a727f927a31fefc1b1f0c63344218c2a4f416cfa2fd7acf90d0c3a0289e0631060516e08910abb896325cb39e2a9293339b7fd450da494f14c0cc6282ddea0cc43ff2955f1437cce7c72617d85a3853f88b9814daa33039c940e2dc9de37dd680039e18754161b906f0cda88bd8a4f4753a802a90663cf10e80ad1229306fac3408bfc346a6f53a9a32487119980e8db237b5f9cf206d32016054c6278eb767f894f546ffd94c6d391c85c8d9e00dbec79ada19fc151a2e6610a39f60c64b7a73e24595856caaa1d0d5a2dc693038289a4369ee0b8bd6514910598e10cf6f68e4bd551466d0aa4640981b05739e4ec2d52bd545e3082775bdd0d2eadcc3db115a0fe7a2de0c583a4c769a3631f55d0ca147a17d3c4ef2d866c0f31bab53e282a113a55b7548db5ec963e4dc7ce58fe0ed0aae72902d8ca358c0f62b1202e22c1155ba86a7683a2ebedaebf5547cc574265c4452a3bf83664f5022c61662fa38267b1a36df9fac97cac0b8d39737aa86c9ec3703d4469851794066784f7243d37261b24952f5bb5a8ff0add82b0e017e247db0073e2482e00ef0e0b1127618077fd0bbfb20ef0eea0699ee9f9c9b84bb100120b76fe27fd89a6088e9ee3cef1a8efca20c6928c0cb498d387515c65c1b77de63a93381a1f642b07902d2533502d99b112422ae5147bc3cb014b77077a25f56586e215756dcf540e726f0c16f688faa65092d3c319767ac5c15f932d6af960e4a288de367731210a42fdd27b6e17dd7989b7fc208c597a8464eaabca174de591024a83d6313f250e9f30cab3a53e963e32cb7feb40dc1fbd5cfac3cbc03e6ffe816025798c63ca0d388747a2308c5a4506e27da110a1994df51f9dff4c3078ec266df02358e96506e32587a317ae37dafc381b18dee2440aa58ebed74ed9982cb003fb679cd7f60a60b87d161a706128cc8c3bd934749659170a9d682a500de195abda568e96350da68c2a3af5b120807775a44837406f4f2548a8f05897f196ad156611ce2d2a0ea7fd03b5e19df312eb8688c7d86730bc9cbcdb307936633eb4b2d000047e660e19733502e2e9de1b4474ab78de43caa7069b5e8a87b9fe942c14fb0d495d55045799df8f285a28770e44e27a20e7ca80c06c764136c4d6f7e21c7fb11303fc0f508703113e8e3bda762379065e243807d8f04a1ee82a5bfdd10ee3cbe7ae3f0c0377f69c949717afd959f7a7dd38f964d9b18759645f2efcdcb32a22c808ba0d13c679743b13d056b0b405cdad60b222d2cbf6f8c926e5055c1af6110b0e860a90a83ff0a852923ce25e3d41511c5c0c36aeffb7b26cd05f24804e82efc4f605434c53a239d12fb53df1a997d68d8cfc7604f86aaed1f6d7a6031db005a552094f390c4cf98475007b2c319681fa9f1b0564cdbd3be881a596167cdc8d76f70a11680bd2a47ea2daeb85d4dac3ebaafe5c190ca369501cbe21623e207e3ede0a6f0adc0d89d967c6a7fbffc4f257649d8d1d6e1b6623a18549147207040fe30bbc6c05f5a8c8df8d59e9de86a2228562378243d79aba40d79d7ca1e50f78670662f447e6561d2ff8493b8e66b9b24c4685ed5669b721c744534dd04e3916a208a02ef829dbf6a1ff4b4c538f39984b34828c0f1b38445e7faa071c16fe8ea50256904880545091d19695c58a9302de787eed93dd68689276222745e0d6b66f0855f58f712298de9c45593df3356842d9a5a15e9da10a2c6d59d759d64e1c0a01c401a226dd32222bfec5e6804f2f2bfda6e5cfdfe1341c27729ed480fac36505d0aa2725dae3346b63f56fea6b929faf34bb144bcdf66daa426cb3ebe2a8d905372f364be7424754755d9d2e4d384c8af4f046a082a3eeafa0ba68bb9aa6d705827f7eff085c961805eac36882cf54fa46f65138115fd99fd0424caefbc599078cd6ff873dac15902655cd0890953ac2d0147b6c65a2663a589df32123a8b4091c1baa35d69d6f99cf70512e62b1b27c872122d0d5444342dd57b63ae0992d0621095bb7dcc56899ebf6d47d536b8341eb28db0806cb5c4b480e97c56a6195033de18ec030acb08b6453b562e8ddad0bbcab5b4bf5e3a2a91f367dfbab0951031d219a2423dafea825e61e118259f5bcc1f46e51133b217b102ec8b3ce15920232fddc9717026b8d78c18c6f565bce766356f91a1dc7f89420f379b8abba3203c205b3a610dbf6cfbc4e379bc2269f4d7389cd8c6cf2e0bf1c8fecb4f1551a0b56c6b87307a788205800d9c371bfed642968eac96d323c530e49f92a7a43bb0ad23bed8e642670ea2ac1b70e74fac28f5f4e8a9c8356a259b1992a44f7e6860ca2022c7731c6d34640857ccb54ec53c85e051c67d49e87fc5ba6a35bb268e10f9e917958c72c091a170f222bd8eace45113b78e12edee412883f6d59c8b021033e9f00e6d569311d549ae95edbe49768651f16c36791faa6dcf346f223a31b0496d6d39a82b8495960aa56958f423cecb69a3dc5229ef2da50d002a57567bd0e411ef01549190a9dea6bc2849e143784a8469e928dda2db82a5dcce15bf02a0800a6c55186a68f6e94329dd05b8a4621f1a8ef0f87ac79a4d8f3e3462833ac05eaba87aba0ae563789d17823ca6d3395e42fd8d704110f8f0014b0119645500f0956463f5f19ff420bfaf94574297304a86e943958567f6f2398911e4ea3530fa0de3bad6c83de2c3a9838e3852ec17941c1a907905ffe3dc9ae6cf05116d409211d397bda1831ae225402f07e8ac3ac910493f7fc5f4c13adc64e17ba6f5b0cceaacb9344dc593c57bb5ed14123e75c8930a2470c240f079d6c002eb973a60ab868bd8ba803f66c889b64a2935f3044500a5aa39d65a6521289d3327a10bd01235316b8f29897b372f7911fc4ea88edc38ec2aa3678a3890f2bb38da4e253016af8b5c9b1764d4c7d40dec931326696b353c706ffabccc8051795e332efdb0f85d50fff295938fbc91fde7a2c2f8d8e13ed9bb05eddfc83aa8ef48c17366609150686186cf718416602f3ab5714a3217be8c101488689bbd42e57825905fd05d4f02e53a21b7aea582c2349e750370406e97b3ce971d561691876c0c992950db16cd05c786618cffaa8e9f3a63b9ced6782cc69f8425274a5c236513eaf830fd370b4460e5b0ccdd066e50ddce13779391d210cf4bf524152e92569d8ce830c1f0f29a1c5c9ea885301fd0abe33ac10858cd6849181f9982fe2c8f169da90047d61c3cef15c70784541d5275ac99402dea98a8471955f974d09761f55e4bc0150bc778c4889bd89842d8b563313d597a1c9f8f89ce41205be2257a45217f7079007efe31adeec3e1cdc479780cb46a8a61cbf88de3394edf91c0c368f424904f8e7748274b801e514ef3a9df2bd74b60c2d4553ede7acc29a6e18734e182906722e39106b6b406c5928286a94340b70661d1055d0ad9205a2e71839981f330ea0d62f0f80c155b9e20606e180119bcc506c98cd5e06867b3a8acdbf8c3ea0091cb0aa362ca0f716d473c18239a38bba97624b04d537b95710b260b12dcbb1010030b4537d1f7e4996b14ec7991fb0d9e5a9263863c2c1d72fcc4e38777ebf0f86bec748b36497b57459eb23bbeca2fbcea0ab111ceb0994462c7e74e54fd106eb725875c68ac9647d543d6c9421bfd12ff9d5538bfc26f4fd234bedbb1a0701916b9385013a0dbb26087b32a74df8ba2fcbb706844d1c308675f742bdee1c040affdf6bf4b2ca84d0ffc8cc8753f4f3142557161a02c43d38124c04a9c2c50325cc9bf144cd479c764b44781e441702cefea42c6977aca1410a003ba9685e008240546f3fc9b3956ae459856803a27193179e8e108bcabccd2c38bc25a66d00f0ac9f5191b7b215d695f3b0c43f8af577b26e4f9c1426a43051d22e5a4cbe0912b27395b6d9785c822060d710b2e2cb87b41ebd70cf1bcb8ac66928452f030dab293cbf2c1ac966d311a79711a9f1c9df8e6848dcf5c60424cf4f7aad0265002555686be44257961baeb71087560aee3d842b84970bfd5125010d5891f85a0065471bedc4db39ba93ea8b729e62a72c02fa78d4fcb23c106ac4431e6e9adf08122717cdbd02c1be09b34a45f0314756109e0849ea60102e39599376e7f57c0b5bbf2c0e71fccc029139ab6bea7ceceb47935c3ef3f844c48a204f476d99ee0b19589c40beab8fa1bc474b5a85fb8177e42665ffcc21cf614f320f2212fc370451e9cc7cbfed55774cc19ba8e4eeba40b4fcdfff8027721e25ff1ab11ea6fc06 -generate_ring_signature 19b59e689992b33510cb3a4641a0487214eb16b6c4ec84f17a725a64d241eb02 35404cf6de872e69cc675a0c374d23085e607a63b198b243575c3aaccba5110f 55 96bfedb655e4ad5ae088e1170a9f8b58daafeddb84a3d8922c7a695e82e66963 b57b0dd3ab098d2e5a1cb9df3b9ecdc5e05a5a79a40c818eaddeb80ef3502be5 679c0ec82de8ea3d0fbbd4f41f61cfe30d486e8d2807a7b2da091c4e79cd7efe 1859dc973eb9798fbf6c7cd07db81acce29d707577dd72eee64a3d5373103ff6 4749c0e2a2595df0cc7608c1893942e2bf72f1ceb3e4609d0a12737f3268378f 34980ffb911d8f163de0a1f2da648b989d2b38f41e9525b4fe2e1f5647a15263 8bb5ae4cda78148b7b0333f2ea45fb88eaf413880cce4254f6e84dcea809ce23 e02027de37ca5fa4a7490a35a52f26d3e1bc1a462f8357c3ed9db7f7ec1bf6e2 dc30251df7d13fb90afb8a6360bab069a15a7f18b2db02a80da3a3906b96b479 e72e6290ad812eb649b4a722ffa13919d2e4fd49ff4a44d5e1f8d80976553fb3 70caa5ef3d0766d48c6d9a6728d0d539290b4a68452876c9b8554590429da46c f57560f08f8f42d053aad27a4387fb09c0c914d921b518f1d158dfdf16a4e17e 4655b554d751125b469451635239474a94238a5e59aa3ed0766083e11ebfb9a7 259012fdbf5f5453f7aa98d6b021c4da9214f34f297b11e32f1effed4bb13909 81b66e8895b7411a2c7b8390c0b20673b1ae12dffcbda94d5561646a6d461dee c62b504e655ebf52bf1d72cf50cfe4b4e25a67cf6e968c939e8baa09fe05ada9 139ed705b0a5f440be69de7d7d3ad3a2996d36f8f809f02bfd42398b02e91dc5 392f1fe7ff4620ae2a3757acdae3b4ab011418e843153628c3193db731eca520 33c7d48405a1112a28fe51de2e6a9a9fc35669d246f23b66b88fd65480c23a80 2ecd2cff507a023c57776b03ceb8e75536c3ba9d734bb08672b7aca0a81dff95 4608c674e9421be71e557739fc5b2b868c8e46717d893c499a575d6d1f6af1a4 5e89a1023aca7015745d6e134222ae6d36574605626c816396755f0be2d68bd8 004f0204e19cfaad23af654dc4ac284fa89229c2ef95b9bbd5aec1e0e106fe02 20087f0163ca1244c5d52b7d9c343f4859f0fb8151f0ca44fd58e9fc28622cd3 d061d7f431bfcafe091420eb0bf49476ee690e680f63d2cd8e57ee04ec315d43 17107bccfcef930b408cbaefb05256f430fea8dc5fd4250ac9dd5f4ed3fc2547 ca339d35f0fe605eb0587ec378bf0f3bdb44e11438f999c1bb1c3c5637b7e2f5 2f89eddb111d0b6d8ef5a6a5c265c2ffc491d0e9d09d0017f494df2dbc360f0f 88719f6e415b781c843652d420fd554d291f2c0e8cbebd6d7e0fad80c04c809a 6e0b292ee1b4bf0767a7163b61c7d9cb7d4cd45276a78913b07b73f1c3c1d64a 4eabea46bd0266491d0228719fc8073a404fd25a4e837accd5251a61cc87c02b 2017496e7854f8fc95f17df21bfae2c0afeb43aa28c245ffa1b5047dc149e89d 24589b406b4a7698016ffb8aeaf5185993b8502e46d46e3bf37bb078623496c9 e8e32089aa49dfe331db6fc798abbd5c3f2daf7e1c98263cc7869e66dc7adacb 05b2f33d72ae54e95169937adc6420774768db8a9e3d7236edea05e4455e18b0 6a4b66dae9632c12f4daab911b0d8b9945fb75c25360549c23291cd63744c3ac 8f423495dbf74ee63563293a66074bf15713e1d3f3a16ee4d5f33d414bff6fa8 3f53e929272cc0ec8e023cc81acdfd50852273ee9274732acc069ff56e8644f0 f0a84ced9df533d2a34935af3d0bd07d544b2e84ada7c3143df14a529302f09c 39100e3dee3d1985e04694de6a49a082df90f3f14e07837dfd96298ee8019252 895b06215ade6268bddf5c5accbada81af003ff3a8326701c7c4b2e4af1f3f66 a99ddc831aed70624f0e17f8e1ccfcf5add0d30ff448ce7bdc08d6464fce9e1a 60ce654405b62dfea39278dfa49bf1cd1cb640f60d3bf18fe3dab964832bb7b3 0f755692573303969da6af7969a990f4fb4d0d5a22c1c446338c7aa394b8ea55 10da39906513a827a213081f345f6253bf93cbe5717b8add690d76499580ddf7 b363a3561c0abb151066a5a39b623c9c1b7745333dc16e3ff858b227588b525c 6436c0ca822aa4068aa89054638665f1936cd2a00ef7c68c18a200d5731748f6 310c66d3a506da4d1cab6649b14f875c07df63139e6c4424791fdd7ededca9eb 5137254f8aacf7b9b331927b27c932f68a248df87954633c08bcd84cf7bdffb7 1616d9caa4875622e3802ec550aea5d449b648ebf710a77d3c22eeecbbee6f76 56441b786f910a220f80a7312b1f58175bea544c2831d29af67671af474cc5f3 dbb337b8152363bdca9c7c7b15992fbf2797f113c6e035310becf222ab12b970 36fd79a1708fe543dd785cb28caa54f1d2224b6959821294d8bad2ba982ad4ab 745e169e91122070e4320302d73305504072395f02a6eff85884be79abce0c07 870a32a647e2db50d03544860d834c1c4666eba0e351ff13c0198bbfcfc89c46 f708b34270bb6c3817c74e5227e43d13c13ce3dce8162b8ac558da1c2e81be01 54 96aeab993b2ba87dffa35c2620687f1081c79fc15b8964fe58636102c6225c04538ba9df4b089bde96357f1b7529167f606f1d22a191fdb698b6688cffe0aa0761174141368451014097f5488c7e03181d0853f54d02e24e93267f54ca9bcd0d2298af2f543c5ef6e9850549e739f5e0ab0ad11392db95443f0dda8d3a94850df8f0198d55ee345c55a2db1c85320390e2154ecc7df786efd7f483b96b3a170de973d0ad93a6cce8c6642261b08a1f99da721d8d0b1aeeaaec4f26ce2a54db00cca418c80877ff5c5d391b38bc71f70755d197322c10c72536cb69c5d1d21b00e3df4614b4443d28db24d6e8a03e95cdf2467cd6741f017d76ce34f46585c30fc0ba9a74fcc1f3a4330a8945e848465f74a52beec2a4445773f4e651bd1f140ad655ffe8ed4115ee277e974881aa42076731b4198c9c32a5673b86d4a3574103ea525dc4e406d75869d30a3803c5d2b0c86c5be4236ff534a84e5aad8162240d35b62e36979566e75d6793b21aa4602a8e8b2173624e62989f409a914b97b50ac872b72eebbe0aacd4d044fdfc0961c9d79644cd13d6b87ba4b8cb3452e9a90d0f779527631294f7a6f630f9c6065090f59eef2f5d6f65ed1545c78c7107e601f8538cff77718952c0b4d14d222604c649b238fa9dc3201487fabc4dc768130d22ba9103b06c5584c3bfde2ebc8b9c71f339e99c33ca0f45d4b0ee69a3797603d9a8009c7e4d29c7ea7ff3b21d46e8d64cb40c59dabd2e72e48d282f5b6acd08f307760cad0f368e9ee84404acc11ef5384ecabf20dc1b244f99754a63b2fc06b18fe28e7c26b2cba2ca8adf264598c2ee67857ffde4d1ed34080bbb36a54801bd3cc4c04016950a8768a4649e05c941b17cc790b9615d04bd69a8a05540420a16561d7010e9f1baf7cb230b30c07bfc93d7fcc7a6c0be20cf62c4ff3ac12d01537a9ba21209f62ef0d5f26373f3ee755a4a7d7ed51f4cbbc0433c0950ed930bcf65fc192a962cf9c5dfcf8d62969adc89045750a6a1010f0ec41f9027ffc40a7019a9ec3cacc52bfcdc3f0ffa16c27074295a6464c4bd2c49c075e15b0052099499a34575d4b7445b023d7802f010d081662ad83ca601599f15ed6c8140fd02ca088b91b9aa0135dd250a7e412b7a59cee80d9c1c9adb96c8c6177d17a64407d718317c0c411199df2fb6716d8e870c582e299072021ed25769a60e3774f40a4f240fe027b405d6958241118d69224e408c29c6c538b4268125ee56e818f70f20dfec82fdc6879fa3e699ed77537ddefe769101dc6e9228cac80c7607bcf50ed1aa7c216f2adddadc28def5243441deed5f5a18bd64fad42d52d000d9a5cd07c462be3389c4f8ae83c0f6cc1f614437547811a29abcc49f9ce5a6132b781e04d2c2fd70e4d109d76e235bd755759ec32d2b858bbad926f7d389dd64ed971b0d478f0da009f31c34be549e50c12304ee6a7e9cdd49cb80aa0a191fc2c76dc501fcfec231b79083ae656e0eed52934f0e16f078e32b222e745df0c29cf5dfd205146c72d81ef244508d263053f8e9d5d78d9fabc095c970ec8901764f4b44590ce37a3d763ac1dbad2adc2b03f7cf2e71366648e9f33a83bc83145fb0ce145c0d5deff855c81d8d6db6fafa8d8d0dfd41a4cc90eccc66c0a78be5f2f7ced8fe0f1bc84892f4a08a764e2b240fcadec69dcded216d15ac6e006436d7474b0b1a007ff1f544c52886bf6592c3457753a60c7767a983876579ab60e02460a6c217062efe9cfa3cb22e648c370c7182b779241316033b7421bb25377af4e81465d80a7b4a5d151428bd4a6c4284df63d3f5faad79c310e26f31ddc8ac02c998eb7a086dd6e13ccda11d5d90961ac81859be8143ffd28224ce332460d134f1d702cf0c4d64014eecdf7a881522ec826decc97ac32eddb4edc8ce0f57b3e0c1d6a14d0f76eac2f17711261f9a820b200cadd522b11e4bc19a31e443a884a4edece40e0c658c9be300378b1108fa6ca8c178e65eae8c4e8881486d802705cb10f81bac01d60e58499a2e62435c653a41a1892addc2638756977196050ca196c64fe7980d36408819b9c71fbc83b1e3b753b155e2c4376738567da7f31fb75f26fdccb503edc4bd88f3701b6cf4e0503bffc578c5d112c6151bcc51da22e186380470900b95685260a428d08978cadf34b8c0c6499bae140b5a9bc5ab29dfd59ded9eae0cd3f1e02ae5a54143af1e23e12d8786febc69ae01730b1771947b0f4f280c5b0025363a6f88bbe38a2a13dab3ac92ff1b95002a81fe1c60a4272fd4ab22bbcb033dfd63cda389f41f75d968502efe105fd20d80676061835331ac556ab47bd6071188ac78b4cbd545c11f4d6a9ddb4df28382677e5da30301bca9fb95ee68a70b4059bdfd8a0e2739c22032b92d6912f1617387027d3b677df2ad8ae660ae0c0dfacbe6bfdef0ee9fc15b73f3b462652b9a28a46c9ebd1df5c5e8d386a43b59003c808fa828c76f556d5fca6a30e06f518194f87edeeb7cd6a8b91d437121da04a7147e686741cfe5dc9f49711d3227b12395bafce9def7b98319888ca1b85d04ccf772ac7af47434d132f15d201b95508a57cf531a69643d03edcc115444c3031ec3d5a5482d1b2943e433a5d3213287ddc363f64203c920049e6b98c433bd0b0f818535d7f72d565c5e474f4450087f490db02366339963859d937f85782502bc24604022f7a3380e707644219bf7216d42f85f9d10db65d44ad53d37bc430a336f5546c949e67aaf8c55d74d165f7bb3096123702872f96dd5935d8e147709f3b9934a9f54a2562d151eb58f603f0cc86afbc69358175a38ea8ca3a38f8706317bf73b80204c0e010eeaac2f076259ef551b1bbe8a6dce11aff53072aa2e08a25348b750163413ee37eb5653b53b255c8ab6f9c024d8b07dbcbf31e835740fd5ee986edc0638378198aab5eb3096571ffd9bb5c38422fad73eb96767ef980a47f0b27e1caab297ba2df2e4901f6e509f8cc583c74394b6e15fde83816e2e013baa1cca3e88572af1e1a2d627bc0046ba83614ed490bd3133ec82458445fa0d366928a1fe6246745361f2090e329ac938e171108ed3816e9eeb3d2a247fa80e539911235a92d83c2cf52409dde29bf433e6471439470db6a1dbe2513ad9db0b4d185865a3db9b23b42ac3b85dbc26b70260f6eb677571fcc7cc0e7def9c3107d223a558aad17d12b6a38b8b2d2fafa1751606a0524b0e5e4b78324298c2d100316c352d472a7d692a0bb3c47aa02ea6a1981d150adb3926cb4fddce5ae030019ca77e496fe4ae604295d158a2ef4d1acbf51c991e1fefa33502c0434aac6d0a4610f3791e03249cdcc28f52f23473dfe7bf651c53de381a64ba8c17592de408794478970be2bb5fa5e77f2aa3e2080e6bec490e5d2ba96d4e24c207adc4b30cbc7d9c6956c132dcf6123fffff763b9c7487ed53732b028b5b771650ba857e0f1a739d7343ab7ef4fb55a530450fd11d04eaa9ebd533815fa4a8e8210ad4800641337172b7f34c356ce0c7ef0fea248d2f5b92ecbad795faf3f6e30e4eba270cf0bf21e571523fa346935f0966ac8c1cf8b870ae7dffd6f8cbde8cf4fd126a07c91c38d7de3aea2f5246b6c6e2d45baeb3dfbaf6caaab1271c57b5c0097eeb0e6523d34601abf5bad92957419365dfa53dd33c1694f91a255ee5a6451fd3470b3498d34eacc0132518f74ade75deadb89c2c0e92004ce7349bdf0ef4077ea90a70630cdfae50efb715a95739c01735c1ef461fa66f41f337450db30abcd75105d3cc27692f04d44d9b02b387e55e61ed7a936221c0ca532310dbf9dac51b2f006cf2249aa834c5e2d59b264e077dadb62367851b8e9cf8f67e1905f01ad98501f03220a4fbc719d29e963b2f4bb0c08441c68b30f6aba6eb8a946bbfedbf3c05471adb66caac242c0bc6763e09cc61458d50af2a71edf7a86d055f72f5b870029aad9f7320ff3124119ebfd9cc8f5c1cb3d9d7a77b8903dc03b4e68c5dd9e20ffef22824cac4e54d3cd025d472686bb3314373266f54ce21ba4ad4ffcf3fc50c86434358faf8671fe1039260497fb9d2b83b5519d6bcd235b9e44305c472f1028b6fcfc369349d2bef66664e6f66d5ddb5786cd1f436c5d2307300815481c603c63a95c24ea4ac6909ee1276459466758e52a74a0d6dbe374047e2ef0ec88c025e18e97152dfa259dc82b43525584a37f0f9984d31c287c9c4194ef1adbadb07f8e9e5525f40dc1fc2eb66c982b851c5c18f1673d9f50b4fa6b3178802ca62030aeb6a71a17dfa87d2ca5d0fd3994a404ad861d0e7f7c275c4d24fb555da4901820b849c9ba5eb3ea9d3cc7cbe945e22739bff621b23fbeec57cccc1486760043d92b3c66b3f97988de4477128b596af6115533d319bde580786202a0d5a2b0151cb97530bc5a5482818f88938825065e19d5c17a3d644f8f97d035c6618410b7a4ff378477a10c53a9a7b1268a5f8387a038bd4a7e9f51fc14daaf7ef8c830cec7e033717b9f8fe567754813ab4d253db95dd3b756d63491c5d6ecb4d203a0b24e629b047f0ecd9085457ddafba19230f8957005c2c35c8aec41145cd812b01887e37ff98d3191246d9918ffd8b6e1912e285d5650d0b2a38a3bda948502a033ea9a70098548621c098d7e055bc4199066bb0ff476358612388c9406d964200a9c1879892bb6c4196d9c8f51c66bdb0f465cb4c4dfbe3db874694104fe0c0089c568dde8721f75516688093312523400955ebc2d3f51d1485035555b131890f831edcf7fceaa7c628ce50bec8d3d83b9109b64398b633f6bdf1a9f20b552003b9c0adcd5860bab47e8961d5c4ae05d02819c8a761bdbe761e57bfadfe61a509d4fadecb7bd14580284ae6f4e99cc8ef3cf9012483111b2203f3ea9b4f7f080d0f085cfff344e0b28b4d842ddf55fd8cf560d2f83e7892a4d111facbcd50db06 -generate_ring_signature 4672f905fb42dc0475983eb5a48f658a06268290809009c1c1b1d2c76fa8b2e7 1e95cfc5d2d71497ba502d461fcb7ea9f9768e229b9c268e3f8341a4f254990c 1 3d62ad8a5671f7bb33e660429e1a8d65f454ca409bded90f89d659e10284641f 303c8f50c931bd0703582650568fd4d3382117c521de689e1630abaae8a4440e 0 b63eeaac17ff2b3250f2551988df8db96ac6344c28047fc752de0a86d6953d0dfe32fa395601e090350d595c98a61693417075355e72fa94aaaf95a068802108 -generate_ring_signature 3c562fe4a9bf99c509f2e8d4032d42128a7e92b168392d9538b06fe8f57ffe7b bbf7fab81426cd9d7f448815014c590fdfdf9438021002f067664326bf4c972d 21 f766397c6872286d33fd553796dbfaa31e7b3d1fca071ec0a4460dbf140a9f1a a6a61c5ffe6b77455c458b3cb3e48ec99c70963f3d0b7476f2f3fd49d798bdbe 23ef9c123a46aba32944da02867fe6dbb83456fd81e57ac37c04fb398d748e2e fb3870eee10bc2a23e5cf82e0023c95d7de15ac76adc77a78e4a4c15a743b52d b433b1ea0f29c61a863315d234cfe7abf86106226da82d4de2a59efee82d4a97 90c7cc26068024a6a5ea19303358a9768440254c5fd395e60770dd114003b12f ebdfdeffcad6e468659e5e55c0702cebf169e0b6505bb802101acfa7936b5971 b558862930e7fded4b64474777d7e762f252d7e5ef32cc0862f259ffa0cc074a d10554549af45f28017be503d7f9d838d8c20afa543b0da9cd28f45089401038 939c678793f7619c5985f478b78f90ffc9b3de93a2aaa1674ea9a3b3f5b4f369 e639f8e4a4196ea5094fe047663e959e329ed2a9c5b205d4192213c5f3c45201 1b4d75055248504ac3c2821e2cab34035c4c28941960bbd177208e10499f0bc6 1b6e7a5ea11b4e4a042e2e197f06dbeba08163e681566fd83737e209ecb69678 209c34fe577f2dd7cc74706175211a65b87489553fb104c9da0ff1cf5e5c402d b1ce728ee6de1e258471179dd350967dcba276f7faf60a9659756e1d545811fe 8b8d736c76157dcc516cef83ac9318921e9d6adfc0e4a40cf686fe71504d79a5 96331d76ac46063e2d60b4bd809c91e77b893c8d9e2c53d27a1c378c20ab40be be0d16a05eaa11ec4ef8283d5f1b3ad1cae73883c059d855c7d0355c1249dd23 ae7000fefca7f670daeed94a5f3ad56697fef12313821842c0981271f2142d77 8c5fcddd3f4b3855f1d17487883f33fc05eaec1c7949aa9effcf1e17eccf8895 8900e46d54a0a2ead552e74ba2753b5c8d2b15e381daaeb48569029433d6e8f1 edbafd6cb274f37b1783450c83485489e72afb99e07afebd2853f9997418980d 19 998c597fadafa011815319d24942132c3bdcc700d4ab3d6af8d249a567f6bd0062e5a8af71825f21b31cc2e343c8fbd927f212b9769cbdd202fe8de4e6eeb705c8fded12288c91aedc16249ff6737a32cc97b3190659649601f12e6044d56f02a01687e4a9df3afbbc3ea3f6c154a4d847a14aad1c856f8d119b38ae408c0a0028a4103a17afd564556db21fe908591b7dd6af25760089fc249da1e4e2cefd00989137f09498c2ee3a5b56638b7796511367a6f114f3893b843bcff886e4ad030975073c6a76e3e38f0753ed2960b5f7f85ece867f584df5d164cd86150b8805e3667802880c08f55681c311fcad9fd2fea62d2193590354e0aa451312fe4708acfad1a9a8e492cf3a03418f7613220f662fd56d4889936eaf6e0e2e3c401f0d88812640f02576d42f00235c72bf7a5fa6fc051e58b75d5bd07e98ed501d210598567534918f09ee5a554df4958b7a0979054905c0b378f61709977f5b2bbc089b503abf4e563414238704dc7027c02e116c6880f42513268ea30673efbadb07021964f0f109e9d3d50b4c9ec2033e0b3bfec3bb6e7d4c5bd943105087755507556460633cc8f4b4876f2d1b7a37ce5da9787e09b842f2f2f461846840439a0fbd7491c068b496fd622d9bb3e4d64161d944c60322f3db0ddecb6b459707260000155b55012d89eb61c830a798e591bdfd7ff132302023bd89d77896a4ccf30017d244663479f9bcf7b38261fd8bd2888560997efe5bf6a5ae665b9dc803590b35c3e36d2b24f398ff7c75607088095d86fe956d26ce9aee10749c734c83b40abf88a1ab1ece77643c87851229645cad07b04a029c01b977a203ea9841bb1c077ba9a319937d126de3828e1fd3a43d33f7f925a4ee2487f81dfe4bb6d7e2a5092fb00260d1b6d95a26596edff3da72011a923132b3b8e7cb743947750e1a29061dd223b2432bb5dc6a91a3efb5a887907c201d2402f5479267a56bb356bbf50b8906c1550f06534c2ded19c9991b12dde68594d8787085b37c51e70728ac300754c61a0e658d279a102a6b9ae398d8804be338c518e9da6a9a641b998aefcb074213a1e45a946c4a9cb136d27e02c72be54be5468da5f522e73f52b43266f20e6845b6b2a2594f5239abf145b596b68455d8430a90785c940ee91b06dcee0a071c2870d3a2df188daf811a19ae31342efa8f1d5b7ffb8f72e056d5dfbe52430ea818cef5a80c33b21d34d1c4e353f930320dd1beb465e141e7d45f122cd8d1033212a928fa682499556f8b39497f31244ee070f128bcd0016cd8eb35d97bc10d9d117d43edf0748ca7c1f7d7c6cbfb43fa47b216aa166a5b2e8c6ab4da27df0190e0faff082ec92633eca71f1f53b61d4dec4828502841588eb1bd3d31c5560b563297de39119549e7d71a5656e24c80ebdd2380a7d435fe4e55b136345065001de98a6d10ca53244f09be66917bd3c9d89f682faef90652981274fb4912c309474f72d315ac5ac95591ed34d4c78c7ffb9c74de7e299d3c9515bb5bf2ce7a0832168d20fc064ce000f715560de40a22c3ecf058be09fd7d56d5a0dfe7df43076b9f5161d34119f4d3300b8e9a670d810681eae268b8c88f2f70b2d4d428b2074b60718054e8e943ee4db73faf8b5794175140242b890298ddc98e5ba9032405e1a2b6be0449f24468121c5c152a44bc3ab199bacf1eac52487db43ee73bed0ada3878ae39b6d179ecbb70d55039fc3a643496893b9114067ede6f5cc17caf0c4932e6f2265f815a1ee52721a3709b32cf6cf3d1ce37be9b094a5c813d4491054d719fa5e4b8971b942d584033b5d67af1357be6a835ae3f06bc26455ba1b60e6b891d71de1f5336c9b92747585de5c9dd005113cd54687d19eb71c1b5eb360b -generate_ring_signature f429996a2c98e465ed627943859394523fd3edaf7ab2a919b0d190789e34546a cb0dcebde38589ac7c0859ec7885c7e6952debb5cd69e6b0922e73eb31ba5068 14 7d8cf927a4b89584ee99103b0203a51636d454aeac07e163065565f37a22449f e5390b19dc3e87c8f2e8adf3fae09348f3abd92f936d3a4c1ea8700b29e31dd0 af63ef0b26af519197bc1c456474274d00301c68111a6552a781de6b5989d86e ad8a74a8276ce918dcfe1f633b4212d61782b38396082709eec6547be24cbba4 dddfd48e4e8599d65a87c7ffd853402dd1dd945df61402d6f5d1ee5997bc6f04 f5ed6a7ca50fa4352ef8b230f8f043d2109bdc4d914492233a24bd51571c7bb3 e9c6b1dbb79c841d2f39407336f0536b30466d76dd96c30634acfb4132718d99 06d9d6ab1381c4b6e3f914ca3273965aacb4479a575468aa84dcc088f675009c 4bca1da62da88b5c0194f893878425959abba8f1b59f70f8fc35e90fcf04607c 537de67dcb5b0515251c02ceefe5edebf29f96c3dfdc4cbdd80f677aa7a86feb 81979c19b915ed61382c40953dc698d9d2ab5487d8cf9de2958dae69d165f8ec 939b33105fbc1114c67916681405b0dffd88c6b9d265023f1889a0ded6b6f715 1da95472cf9ea935e2225531660b0340f21d8db50b8d7f9ef889ea69386884b5 bc01a282651fb0e13e062be2ae48dab4533bac2bfa7a069d281db33ece3bbef8 9f70dd260c97b39abfe0fd3a4773ea3e31f4d4a440c3f998f68482a6a580910e 6 49f32c1a4ea7f629cbd25ce611e62f3b1f15afb097b741be253699e7af38b208f4ceecdcaf0859bc947bfd19bca13eb0c4a79edbe179dadfe60ce6925365ac041554d97ca72204756d8094425d7fcc47ecb56159b9ba6ac3013f56c9ad7ff50e28abb30e9fb96eef331d2c30cf01a2e01228743fd427c161e4c406c952979d03683d5a04f98be9270e2d72a7e7508d47393d59cc245a4e4375836f5ebb50220fde69ddc39b6620e6fb367220226a779f9d48c43c2341aea8f494335ea6fdee0228b59bac066663f519221be8243748e672d8869ed6c4854bb55c285d984889028b4f3922d68ea29a4a78d70c7cf6fc5e0870595d6a1b637a0a0282574146bf0e5936ebd37c845bd38b24de7e811148c4056b75a073afa81a5b300ec67430940cc3d689e46669af53d9005e5ded7d4540e98d52ac6f8b94d8ca8b748de3b8bd06132f3b781124bdfbf03f3a15723c07b834c5e8e2e9781b9b130f4a284e850b073ed20dd04344d534a3f034cbb5071e827412c0dcf88af33319352fc128f0ee03ca8e85afee01e5c427e0232f46827da409dd01ac03c9c90ebefde041ebf8fd02147bb163966a12a64c353c52667967a986813bed287eeda8e0ff40a43f57f30c717d735093ed42396a08f82d55681f35968bd891c3a02460a46e6a090260500bdc01337b050806ee9565f9cfcb1321da58c19fded8de7dbfac35c15f7287b90ee07e538d31607c23bd31a9c53972dcbc824deab45342d41b5c3fc28dfe731f0dd161b6440fd69ab03d6a7dcf8dc47ffea48cf0624f8160a1c817d0b4d2054a087275210884126315d5b537490a67f851da27834a40d0289593c5cccfff3fdb0510721ae833298b06b1be4b23d77dc510255d7cd6284d5d67e585d0071ee3090ebcad8c7deb1ff30e3a04c4f0455f559351277b977c2d6d926a0c7b9c55bcbc067d4664557df0bb5f48e30873f29005b53a1ba5e36ce7541aad33712e92642505b5454547505d08a28b3f944721f4db925328997e45d25757e6b1afc18454270e71aebdbc5497ea2ca6e11da434c39954828fbcb0cfebc32662af2b37faa74a02c4e313a0ea1b35a94e77ed6fe0123a39516d7d7317937c1b1e6f2cc3895d6c0f93a983f7c0ba910d408aeaf7d4e9c0b775d57cc2a81af7cc1aec102fc786580b7a75238b2bd01a3f164ac617e58b9673280131b43240e850ace213c2c08ed6094bc7fd379452ba684f1e2ed6184aef38751c53d70f3b11180e1452a91ac24b05 -generate_ring_signature 8d14132e5e17124e6b09f1e79ae09c053856be240e8c7382f8d6c420f7904300 96eff4358f28c028507c8e7f0972dedb4330b822653b2b313f4730d46839464d 6 c616538cd58a741d5b8a384cdbb2d17de994f61c90f280f7977f1a485c2cd43f bcce7c9de27adf25424520a824e71ed3e00652f7a08dbab59ef38b690bc64d3a d2ac5e67b93192d3714c302e2c933c9a303d484ce2c69c242fba97913dddb0dc ea7b1655ef60438b22f1e7d37324d92b4b548aeb12d7b870f2cfceb28d0977c0 2cb5bfe3c0d79f7fb25ef00316e250bec871a4ad7f103b6a9d0c0ac7cea10375 0d5bbb4a6990c3e1c33f173354732dceb862bf580770829ee361569a8d77b44c 1196427b5966709ba0a1ca8b6cfbf60f91fd6a2c7c7e7a6609a63ee0f801300b 0 9f13881215597742d89133c1b5ac0eec371bcce401d3f8d4e5df9511db9e4b0a6d4240f0f7c5f3f51172f2f2712bcf11ffc49b7b18afe178235279042c432e08537b092b97f25e9617f54b31357f009265eae4bfd2a8dd2d8586b0517470d301456358a1b1ef9ac19879b6c781ab2ed5931760b1da7c6045cbfa8ac216a4130fef6f678681b13a8b38a5c34aafc4bd70fcfe996d29d00b7754c4b4a014abab0e037536370b390b09838efea5d673f1ae4dd5dfc832fdcf51303259ecfe7a6a0dbbebdac8e4e06ff5cfaebd35934c4ea095c6a7d9301c99a4db0f335daecfba0b42fc4b4274879e19aa1a612d84867fde040706d398d80b07e096a32f6c77ba0b281b3440a6552f990db9c07409b5813d1b15169b771d50a6e68077f3eddd6f08454a80f3321a3e77a56d091ec57547e95509cfbf41fd3ae364387073549162056ac4bfb296d50dc52ab4a73e32cba3f99d1bfa92fd0dba27fd3f7fa25089fc0b9f4233b85c338e10e96a0c755ad6d8f5073e19882753ddc2eff46d1eb2950606 -generate_ring_signature 05b70ec90350d0aacf2b9be53ea0a7e0912c456b4e0fe7f3efa8c7fabb1de215 db3f8c06eef1a728af728ce5e028068dbca392543d43b30e3408e70a5301e4bc 19 fd5ead415e17536a9a92d5d8063001eeeaf376a4408d3e4d3f3351200f2fcb0e 05fcda741a631937b631b745ef0451150d9442798aa28288c6a2f7c02d12b08b 04bca31da70f07d8e34aa70087d78dc097c478267db53798b1a4003ca02d9e1b 8001d9b613fc98a7a3fbeaa7382d71a02f566f02e1281abfb83888e3c83c6825 36b1a72a97fe92fff3d983c734cdbd2187f34763c98e85e9bd42f90541aae212 11d585e09e6cf199be4e13c2bac0b996a4addaad8b1451e5b0afe18b53cd941a 766650cb51e01a611c1724099e9999c60634e1a3cb36295c58ed35d4d8ce6860 8151bbc0a9b80790b44e6ce4542749de5e5b91171f0240b2a029d27e51c56836 a84a16dfad85aae7dc4f86f3576d396cf2867961b129eefba1c40a9b87895f0e eeb46b381844257d051435756aeb6e235b92545095136c1db72d84edb64fef7f 78ca044b9a1ef85d91d558d71046768bb5ca5e4cf1a97a693a51e25c3590d514 953c11498b63f8cbcd143d0515aba347c20c61fe47168e7c862466ebe875908d f11936b0f1dc77561fda254ce5ea7998bb1c00f4e3e69e6c63b0795d99961d93 5de09f7a41ebe987ea155d42edd7283ce1548191db18aede9bf0ade990936baf 26d83642ee3546f8334921be1fe4c766739b3e92dd8a29894e1e348bb6fb428b 48a98894200a4b25f10741c42195b8700bcb390b790ba0e01000d3477b36549a 4bf26521935253ad92ba75a2cc88b931d82b3c79fef8e5971f228e34168ada07 6a5cd2c7a32cecd50514eb46046ad996bcb2d1eda668a7f82ea105756cac2b08 63b28fc379bba0a51da8d41d1d89ab5cd2d5e514fd7ce0830963bbadbbbb236a 15af32d28b4350f74faba181075479bb3c5527f1ed7172c00ac28b703127b307 8 61339564afe2b84312162545e634784144063d993112f8ed45f4084d24affa083c5b72117d290735121aec439f4626046066e9dc30106c8b682aedce0e2c410d552e53a83e5aead11edfc421e2778e09500bae95687fac02b68facc7c5dcde02d996fcfb0c95c49b627d1bd3493c5648135ba09ae031a8303b87ed82ae746f01eb396ad97b5c6e971e63f425581d4d41ddf58f2d5d834e7c7c6229dd7f01fa0df7cec1c568d6b1efec96bd5267a061291b1bd4b85196f77232a235bc94fefb0cc4b53a739609476371765654b03bc91086ce943cb6e41ada1200237034cbbe08982830f70395059a92f1fccc3152c3fa18573c0936f7bf927907046f71a1260ff9fe34d659ebc980e5680ee03ef8228c76beacf36013427e6e21a6eafca14101d7fe079ff665a57ee84780411543981440343c4a9860a34a30d057eb98d44a0de3f58f1ff763ceac4f192de936719ea5015e604680db535f8299939aa1a03c0866fdb1d6b276babc2589bd7c009bf0f46c96dba0e24d0e2113046fa39c55aa003f61275c78362fafc8f1f880622f1f638f03bc176e4c20892b9a6829b43d9b0d791c5b1f356a31e2a7b727d38fd61fbe7a2ec653c3ce835fa8ced27381bff604663d86e9c855285d9b35213cda849efab1fba58f162a3db7ef7f7b7e95a90803afbb23f999bb012b0b9ac0728032fd6e35c55eb23856075eeb520ad9f71d710c9acd5c2ffa4e90a66e6cd02cb1e02de8e47431934802a042895f5dbcaed83e0608d6f44145caf24db0ea09648beae153d53aaa356643b0b972bd64d530692f029295984afdd6ed2dae90a055443106586a2c97736c60de180b1030ef84d90f0522d47943d6c8673425e1acba41b062c97277d69efc842e4e46690a69e1a4020f5acc288c518416192370aac4da4e984ca9fb8ebb1cf23fac7412daed53a2060272b2124f671fd583527c2c52de057718264fe3aed450e3050a62a488dc7c6301630c13e084d3f15362a4618f3f6393d888664d4865f32bb63834c71d74c9fa07bec0314d8b6f1d925094c4a7cf98ac84464bad2793009c723128e07fab983b0e5d2b3bad4a8e28b23de778e32d5e3fff8c813097ecfe8d1ee777c10753481f0d2053e080463c670ea38112dd3494f8a6d9c01d6bcc10a20a6665a99ebcc3100559a0fd1e43e29b7b4a922dcf8ed35ceaa7e04f3cf144e90db0d758cc5107600c86b289b70b54f87dca3cd81437f5e9f45d06a332cbb107d8ec01f3d1b3b74801eaffa35f18bb3fcd209f55d5f255ca4275c39deec9dc29dfd7a9831884acb000506b821849582959ab0279ae00513476088d532960b778e627a8a79c7d0dd60ba676bafea5c6e6260b2f38953d847f560b4661c0cb91f8b5bd113c297d35800e0a6ffcf2ceb318a25b93d7acc2057d5edfc955e5856fd5555683b5c181f9290b4da2ac34e6fca822dcf8e406b769cb85615f02c82baacebfb7c24bf868e5dc0fc8f8f9c5f371b2060b7a1707aba634581626fe4566874e6a5dbb5b34f85ac300f3ea63612aab3df71c36cabcecde86dc36d241d84e102871a4060018bb825e0bcd5ab727c7adb2c2e5042a41a7b5e7fc32033405534fcce627f67d9a61853000a09dcc2a6f7ca3b885ccde29fd51160ae7e778f42581a62d806c71ffea308c03d475b6e100379fa04a22f1887c94c71856a9eb2f1f0cb0529d10bf3a25afdf0b -generate_ring_signature 8e7b0209eaf2cb145700a9519b958e08189855775468f752b72dd90602caebf8 e128395e02c5766dfe8e16fa00f544a0f7e6cb5e698dc00de3683278a33a9d11 1 702d51626defaab76f436ecbbefb7a1bcd953735c1b585149a4e6ae7c87ddfec 9caeec6bceb4ad72ca57dbf1562d53ea0a93feb6a69236f8166f061d93f2c901 0 b0136b3f767f4575c4a02bc641328b2fc1843fd29e11636827fb5a7b04866b088fb2301818e9bc4a100ed08af0931a7738368b8761a48322267b6f24c0c1ac04 -generate_ring_signature 8bd72f709b787215cd41c9f7953b0287d08f4e6a73780b558ccd03c3934b5918 8d3904d2c864c2c47ded0b25d5e6b125a902b075738deceafb17fe51df3357bf 2 ad75d42030a6426aeab0303392ce21c936df265c5557d800604aa7c3c0e390e7 7d31f2eea57234b7d6d6f09d4ff58cc4bb7d4be04d9c40fa925706466bb82f0f 807012275c28d219e1b973c1ffc62aaf43bda1b49fe3ff094879f9676253b102 1 1e58a96cfdb229ff1067364ab778527299238252c2f5b6b54ce52b404a58b00d3f116e0a92e439730e43afb362be35141f8ebe7e836104938d345ac9bdeea2047932553bb0ee58017aa60a69a3ffdc6c52d5e2d918bb1553abcc0792423c51010f9a7409b61649aecdd855f500a37683e43772e09451e1627829219f49da7306 -generate_ring_signature 21a6e77355023a1ca3e620126ee0d7f247f9a814e40125e2586bc9c6773928cb e2814a98af57ee26de7edb7da847473008e128902c90ae796a9980863d8e1df6 11 c81a625349ab970763589667eb533b4fcc76f764e525ed10673e5bae74bf508d b2143c5206e143dd966e38b3d6e785a87b631bc5dab23df88c2e357ce6753e35 44f17cfc3e461746a121975377a05cf87c6bd41ec84a480fffbf045a4b17c0d1 737dd0a57c5bbb2c5038fc3d084871aa281d087040d47b5cc36c381fb25e0620 8bd71c4407f1a7e9048cecb474fdbb527f15964bcd2f9a06b540050b372bc542 f31ddccd182e3b9530ac13eec95786b47013c9566499c20fcb8b31d6b3dd91cb 074096dcf159f4a52337d58bca2b695a95f087125676381832a4547ae6161d4b 8536caa0bb2488682c0c5eff2a2cc7723dbe1741e3ee28194228235a363e7f6a 3ad9404f37be6a07cc9de38bdc1893c172c9674cb186070af90ca0cba027035c 9cbf183bc430c397a8eea5cf733dbf92f7ce3a0365319b979dd32cfcc3285cfb 817989a15ed0e35bbaf2d62d94a2a6bb3025c795f830753679c3773a8dcf6cdb f84b370bd4c5aee8c383a1cde66c5ccdb497c2266a6b15e53d4bb6cdc0535305 0 28d4938434009cf81d06ce9e86550a97cab92ba094d1c16c61f3285081061605a969fb42eea4b09bf670b1fbdd6a5ed130e6781a5b328e99dbce4acb1b77fe092b1550a9d385743ba10ce6c1c01e222334a61bd6a6eb03df16bd3941e61fe601632191d08465fd08baaf22f7b0f9741c2043c805f58a1481db63931d5b12ae0838c8911d1a3fad7d6721b7bc02ffb0af8f5a163bafc414f7acc54d145e4f430a58abdb5b50a7d944a0f154e50869438526d10c987577134689ac8d2a98b90c01a9d705db5056137206bf5d2854631769970e979efd3d23154f7be752ec3cec009d4a6284e4852830837f908fcd21fe7b8f2d7b9675841d14e91e35e98320bd0339c36f4423cdfc798d01950e5ccb547dc5b45ee27f578e4d22f808ffb37039092b188f0fd24eea22ef91cb20a57d8ef85db92591929af96e47641ee8c478140b148f56e466b5ad3ace2e200a8d900c77bca53936944de83f1e99e4eab716440ff391d234ce1f0505750548df78274166b79a7430d0d3d79a589e9f9f71bcd507c75a0b61bcae847371a9d38a21e129eef8ce0ca16bd069e142e88cbfe829ab080cc2a7d2e8fdc80d3fe0a9026183c2e1c87c4a47dbdd82a92be831b4117fda028701ef5c1103fdba5ef685de1c36750bb7324b12e1816457b329027bdfb1ca05ae46072de09885fd53a7c56f03b596988014d0797724e3404216daa4bdc8970715a79457ff653aeb5f8eada9a45bdb12ce7077d1b8bc4f8b00a83e08198c290022b242d7b1f2325acb8cd457774ac9f54fd12233a8006171175e588af6e75d090c8f4c9d44ee19d95e418d6801841e5a74d16ceb29313e8d6413caa28ec23905c05f3022e84c1ff7c14603540f56bec6f2c21c90f5af510dfbc8c6a55045850671536c95a00fa68817b05f2828342a862b5cdf318869bd45ee4146324ba4c50850fcdf100b7459fb95e7047090e824922b2ad57a8a0539a7a2e2ce9d87f88506 -generate_ring_signature 267687c04961afdc4dd6108ce46e82c9c5a60a6db9b613cb071a86dea4069053 1f8b194a5dcd594d315df03b43f6e6c6d8ae58b51fcc06678c93ef532a7c2ace 1 6bddec7e07d75ea37fd6fcb93b7094d1e73a56715a904647d5b0d572a6d2413a 83e12ef8c17ad0741746e1edde68f34fc3a241e9d612df464f282e449ecc600e 0 e6ab686ee1f55c5c814478789c9af8cc419dc6aad4a6f8efebb0355d14c2b80b9788f7a429b0dc6ee13d5f0744cec9ec9c69e691e9ac94665052bfe58d8f2b03 -generate_ring_signature 9310255e1d6492094731550312eec4fed0f54ea5d706cb2a611434e94de2f67b a6b30c2ab8c81f1e2b826258e3800503430825f7f111a5643034ddaefb4a9fe6 40 41d5a10bee397e496d882d7dd820e3854bb222240d4fde670ec2340931bd16d4 dad6b13bc8910f762c3badfbef8a686fb787b6511eb20e7f6fbe390036b2d3a6 100588d38cca1dc9c852d2f89a1f11a246fdb955ec2f73b8d5f8ff3e915beac1 b8b3ed919d7d75574bad2a81c5057b596f62796d96acea022784cb653bd4ad0e a0e37438540bd74fca28cddf345ef304b579ca4fb60457b02a9438dab0cf09f5 e5971444bb60d3b5cf42a088f0e318d2b3c22e356f97457dbfb7362d91f866e5 a159413d63e3466dc7cf257d0e5c094d0d5f87003c9bca4a00e0898fb137a364 38af861b033f1aef2c996952cd44a0120bc4d04d179e74b2ad9d21596a9b8730 bd0a348d3280f6b986c74e6d3703729374158bf1a069701f4aba16f73254c0aa c857108270e21b98d602fef841b56f0411c12305f38ff38d14372c8718f7d2eb aecae0afc964ab34c2f5564c220cda5182896dbca21635ac81026d3c29d0feb0 7bbdfc571e773e399b569f4ff2830e8d9b518d0b5d113c66c78b7f4286a97848 d4e44123919ce55ef7753229180e82335c43c064d515bbfdd16af5459e6fce9a e9454bb7137d5562ce7fd1a5d387441792a1d252af2e7f8249da3c143e7ca5f9 f8ba4561c33a80e40c7ca29743aaa5f9ff45831ea41af4e1835af4843311b5e7 562e1b85aca51bfe7fc455fdbc0060a3107836ccb8e72f197ebbd4cae6c38240 314d5ca7078b9cf66cc9a127a84346dc7a07934357ebff1f03ebb46a71c3b29e 15899bb4e5e5f5ac515e9202d3be847d7bc4a5d2f8b10606a5c25d8deea117fd b088006581f476a6f974389367a8d751e3e77db254b44c37007512fceaaabea2 ce728b492bc3de4934263add7ec0e8b8e8a9befcbf6d303804e8d543bd00935a b18e46b791bf0f12877386efa7dbbadf980d2f090fa2e59826a8eca0417cbb8b b1367e0a7711b395a74cbce696bb16102f8bfcb550ada075fdd28ae6f91ee820 f9d2d946be5236f38240e7750defb8a2e6f9c938dabc5773e8fdd8debc50d6fd 4423c9426972d73a83f73448e6bca19ed2edf3230ea018ea9cad3b1f1d76c4bf 97522c269b46cf817c95aa9fd186b9c09f9ab2da7af3ab55bd562d3cf3b51ed4 9f9380bdfa737fa00f31bfaa185c89ddb4a431e18f4a17c12afc3233e236483f e9544769948638dd359c9fee61317f0c93e4d476c457238c7f8741d4fdd20ffe 033aede3e0d40659cef397ad6250db441c97dea5c26ae4771a9f4dbb1941cc87 a436a016cfcddaf1b1f1fd9dbbfacd6979269f0772526309cbbbf7f40fe45920 d40b3ec269a31e412505088853b395405ab22a54d2fc0b7cf8f36d3746ba3e22 5127aeb4f206ecb11b191437e41ae3ce2e8734066ac559921a470bd656d56c24 89b0e22e57e9182a1a7d02ed51a5e6811f3ac915de5164664b296033124ec6ee 5937d8236f189d12fd6cd9a2ba68df4d5ba2bf77e72ed9449bce95cc0105c974 bccdc7108b06aae759d5b4d0e7e03ae74d5dbdacb6c5ae6935289e231a1bcb7b 15b5ddbcf9014f7f31170a82731321e9cbaa1d3b1d374ac8e9baba8e301fa1fc ece21182404939af7ec56ffbc46d8a72ae114945aa53baf864c7dfb925506b47 fec8930653c39e756c9ead78a921a02bbf3cd46e48053766a3ea05efce314417 bc04ea71c8a2d6774c477819f1e2be43fb4e8990b064e6c752756cd8fecd2ded 373a4b62cacd09fd6ed9e3b8354445b8316d15c11dcf334b28c2bb7e3458e848 c81605bf680bc260a7090ccb84ffc13e1f1e78fd94c2320642a3786f3fb2bcbc 299563329c5dc60a5689618c81e1e4edbfbfe96dd877c27c30e96258ed287405 10 35dcf7c756aee357a62bd3f7118f36e1ad541635cd4c3746c7f11fc4ee9a9b01ba4fe13dc2997be0114fc7c69aa617bb39c6ef84a1f1839b3210e3f128de1e0f8d4a4520ce53fc5eceb402d1de6230ccb68b2de43071137aa36c592a6bfe62056556ba6d14b5cc4f73d844cbf9f8ef77536334ca7984a7bd0947bde09361870fa25a6ce41f7ab117188545b5cf311b1d88415f2f053443b6c6285a3def1df603ce3d0bda6f4c05f9daf2214858b8b39d2f28df1949941b651b77e1128c0e89099440b8b0e843f0a29bacbe0118749993f1fb801f72c22280b4dcfce350c93b012f9867af2a83abe72d9e6ab2ad0e597c7b9e45d2b46f46588eda644ccab2a8020f6ac108ffd076f6c0516bcd0d550fff5da3b14aabd29f4c27335b0ab4932d00478058792e7e8da0cd843c3798a3521f8c270f0ec3bad76b20c7918622c18b0ef7f7c65665b2fc39192bdfdd372d7377019215546fd7d5499d67095c2f094909c77238e7301ea963097ee3f11fe8f4615c72db8f0fd8a550bd406a4500383b06eb1bf39d35d136bcc60c8e6b836234b79a86c72fac01b1b4013b3c6d81358c01c95fcb809430c91c9bfffa3c61e2bb89c06a80a9055303e59316191a6751d00f99465e18f23680454247d470366084811d7e1f464ca01912bc94f2cc624e0909346e8948f995d9e59f7d30dc5b6e40f3ae2c60668d5a79c91b1cd66b97d9800b438ca0e5ce3ba16da904fe02cfce462ceb9e8aab7a90e7bdc045204e6e84d60c6c17b4a3b647b6e446f19c8fb0c6e24a74e03ea8deb9e57999a532fddc9b8a044f0097570277084dd23fe2ab782a19dd0934a4b0f7378af215e345c7da9cd90a6d17b682725c1f50ebe09f2674368f725d6bdb7612f5a154f4c1fc2c97545a032290d1dadb3b21be2b9fef3aaaebef163db4f9f116ddfe602f5a39fdfb89e10d357bc61236d775748e49e6bfd51efe16f98b26ea130b48b5eb346d5591b2110650bb26f85eae2548b791cee8e606a99827ba4cba107de05c84afdd24b522fc02771afc8c206fc57833b8bbc5888d8cbc484525575afb66e70ab5021323743309ef0b032c1e16b039ead8d87b411f24a4e0be3fa47c21f610840736a7c7c32f071e062cb729d351b92311c2cd074ecc7179c374a0015b5ee84b493e897cff9d0a53b69db8049c710282db431a3bb00ff4fc04e30d4d745248812cdd4de9baaf07af2384d96b8c48e3254c65e646a2b9ae57476ba0c7eb75730a471ca6e997eb0d289e832fc38f939fe53162f7f39f22ae71c6b26a13baa1baf00f1519220ba004bfe9c21488408f71b1e9b05c9d69ecda5152a6400815b12d8b4db63b90375a0ba7bce7dd7ee54ae6f80d7e501ab6d6ba32b61e4062d0f3cd73b22b8fe9734e02403e509f00431f8cad1a39e140abc67236fa1ee226b62c54c8b729e0ac434b03e209d78244595bb90950056f0ed002ed9b7910f5ee350de05443ba6beab04e087706dfeb36744085f5202772d91dc13704390e887f7cd416048ff1151342090d6b665088d1c17286cdb3fc21430f00c06a1852b201c9e8f892f76a5c2a68740a57cd673d402e34bff1f2b6ba4332230175a4ebd0a497b022a14736d5d6d73203c9bcdcaf79cd226fb84c88295176715e7f717ae8fb623f9921eea1a30620070ba4db3b822878b6105d31818f8188993d31716893327e1ae9159b3efad6b0f70242b3525825e62f31faeaf23f455fea0478e64bd1e94de2a7e9344aee5e9e490e3ab9d2e49bda51a70174cc2460099856c820b9772b92f80ec7278be4ceaf670084f233618bd9f0105c19f44f67f8ae9e0f2284745935266a0a13f0be535efd09b2ba718e4fb68e05db1b9f1080411f3cbaf0e70cd0e3ec29242fba231c91f804fb9e3b21b18825fe552b55c8ae3e5f0f6f532724dd3df9606470327875f3250f5291d72160354ac92566794736330d61a2fb53dc2836ef05ccffa11fbfa11a0c29cf06602914637d6703a422bf2cc012267eaf10f9c0f1e5a2f29200c5ffc1031d2ea4acd4e94d6085a370a6291b2d6c03e85ec7b53b37f33dddf5bdee99fe008442fb0bf43c8510c46135a14e7692c986875187c18970822168c4636d4f4b0f7b27ad184bfc24d0a439c3914d4c4f1654b0d785fead82fe14dd65faf3e7550de49dbfb75ab8f61e7deaaee7aecfd8987e3331036b64d846f6c181547a68ff09498e8f3072b3dcc9cdc75e0dab3d53e0ec4ff9b9b8cbcc8f13b955dac8cceb082f235a784fa6f15912094941b823f6420667c83d663518558957908f617dee0e785b718f95cccb7a4e5a57cb9f6f93e1790b122a39cefd67d5e35455f62bb5031a44232614a697ee369bf5a2b17a287d7b2c112399b98f536b59aa0c1aa2ec0ed6f3e6cf419d072485fe69bebcd5987cc8240b40ef0d4b0c289ac5c27aa291018553cb7d3b29b866e1210fe18888fd30f9a750532f7c5fa040ad69d3f2327f09497b6670dd35e3ce93001795a469e97fed68e7382791c9f38be295aca11c5b06b6169e068b723faab33e667d09aed5a11ddd397b5ff36a7630bf7a3c4e6d4509773876dd25089c008c2f7050c063541bda43a85f9a3c0f6dbe057f28917cfc07b8727f8e56dde493df3d34a701f3d85bd480a57bd0f764bd7d3e9351e20e900b12b66263897e006da183828805d9a32ef7458651bdad8e30f0896398d5d36d0a88257c70b4921e21a9d87611c2a4bdad9980ca7c07f44d7cfb605e85d4d8540944a8890690ba252fb02466ad3d78aa12d69ab0c0da23b04f5bd1caa3d91e5c0529de3830893598202484452cfde5416fef5741780b43fbd1a0dce7abe3540a0a8ae162cc27d3dadc7d24b4a2eddd9946b360cf9b50de985789479206d5129308f8dbe1c001fd74d6489feb42f632aa4bf10c84c1b403c32f1f7595839bdca209a611945f3813b3a46f951f6fceb9026118a47677e8fd6d4b7979cc014ea3e40abefb3822d551cb89570772fa1e508191c0a7e43018b61d5c2e0fe30ab028d40671e982212222c3df945172c35b3986608e78fd187a774fb661dcfb18923c67091872163646b8fc9f7020f981b840781946d0b1c4f900e5fbf965ce0f1c5df800f308c5d94340fa52139cfbb6dfb0c459e050bd6869a7b51efd0fe2ba27f9e801464d0eab476d705c01819d0e61cc2af785bf5529c2275f6e163fb261c650250d5119e7418f44032be271a5ca6174454040427b6562bac5518d0004bc9a493c0c286f5445e0612188606243acb2d8220bc77dab3e8475813c9490d7e7c98b3208775b850b39580dfd45981a761b12408afff711907df44c2286bd18fa0a64d6081393638431da4888aadb3547285c544bd54b27a9d9b5f5136e28476fc789a30bffefcbd9d67e9963e47f8f711a9af038e992699dc63fa1b3a895b6eaa84478036b6908e55abc08f1bc2afe6bcf6f4b11e68d0ee5eac4c8017897f9226e27a60ead22365f569e8bd15fb85985ee8e51e0e8bb6dd70a4c5f0b87484ac37e4e6b0bb80b579ce4df91ffc56c3479b9151159e92f56316918499e1a950ce596539c0e3ecd955894e288a9c23e14ba741dad99ccf57d5bd03e5b6411bc64a1c4475603 -generate_ring_signature 657d817c2d4d9a393b1d9eda25141349f6daf11cc4d6fd7b8a1f279c12187e46 e8c62926823b84157dcb6145174f85084425a5c705ed0d37842430352451a394 1 6ca123601f7636c3372154cbba220818162efea295240761066b059d12148b96 eeb5e6852d87a410c402eae49f12242b35702470b648824c817adb253d119704 0 cee9b14166a77a6373b3d4077f1550d8a72f54a823a0aece5382269481e8a00b6d5032386581077ac954bbe3e4958dd777885fffb5588cdbf94f124b469f8d04 -generate_ring_signature d2ee8055b5e9fd86cd9e9cdeb95450590ea7a48e976337e8af699593d15d6232 cd23327ec509965ac07ac7f31509833e47fc56b96dda646223d917c02c292ba6 1 ae47e38a341217de4e0562f9b146299c0f7cdb971d314ace7522fd4fa3e20fb0 8c4730239e8f96e53ec2e30b856645506887527e7b6ea24fbff166a727a74007 0 6b65eacd2f7b2b882b7583dd6bb6345388951c77c1e00058c0bd01bbecc2ff0e33a4e0e0498ac03a17e70b0edc107710ff8a9d83d93fc05a3a833970c2ab0903 -generate_ring_signature 5f6aaa96065ebdadb91cfc017ad7e81c1052623a75dc979d052c43d025572d46 e96c5739f208695639dd5120c89672abd6148f185164bbf7e17c643f2441ed18 1 71b0a7ac6902754f376699e4526f3c9882787ef2ec7477dc2cca2b5d6791af28 0dacdc8c2bab9aa153b91ff96e7dcdd6736e8829632e92506efa068500ab4b04 0 bf9418ae6d50d252d0b743b10663324f0a0fad3ade6fd6e07b54e3f3e360af073f1110dfe23d7b3d6cd85f42d2f70bd577428ff3085ddbb3527a5c7a77c05307 -generate_ring_signature 210c782e19ba6264cb91745cb5cda5ce13b234ed030561310e88f6dc2722b483 b439238aacc9b192a9d7d1b73f845d24f574997d88c949d54d372549419baa7f 225 385d632938755662d282c477ad5bfb19bb1d153e5815077216a4c4e2b394e211 5e180c966833b87b3003a824abf2c6966441760258c201f7002e823306a9c2c3 5d040395bd315a7bde66937473a7d96ea72829d2f2f868258edb4cb265aec30d b5f068ea2e517abcd21e927c7ea996c5974cb5c681805ceeb348244594a342b3 327d212984e2e84026317ccd150142a955be2dfe34d19e194a4357bdd221a9bc 752baf4d17686fa71a2534a6864477b608ded4d869f847586fc6ff4e4d266255 05c39810cc10183513ab20ae2caab4448a0429f50673b239286c2843b43a9aa8 f60a2de08e5a77b7e56777d6ef84a5b6b546e6934cfc3a635378c9af0b7e2f1b 9986783276b0dd845c3b0c56ecda1b29b28bd7a3ea155ae6fab4376012a40180 0b3b7b391bb34851c1720cea7925614177f278deb37b16d52197fe8aa45118be 9d41a41006737c046947fc176183ee7baf4c37114479616e89d7517b9d039a56 d057fc6060c31d7629edc250791ea60c9f49bc5ca3872c6146030bc08ceec217 64bfe3083a136ae58ba33130ffd58a60b338245e368db66335d4844bbb0e25a4 4b7b5ae9dc755559da17de7325ad60d80b4f442bf508e304d2dfdfd9a3853a5e 2a5d589b910884a9267458678333e8a3818a71a45005a7aa3a9e088ebfcb88b3 eebb3f88b3f7951e243d7d8db9003edca9f0d9001244476efd46074fbd904fd7 a9ae02114f76adcf94ad23a9079337f1d1edfd74fff2438ddb47d0cabc1f5af3 db9edc21464cb1d6a8001bbb2933214b5ed3a2824935d63c05a55bc5b681953b 5afb033005752c35a5e1fcc95d2341640f532d11107b3a54c212b610845db203 8ec392866d432fa11d21b4293ad1ebd421fa859ef7c5311eec1acf73da95f366 2d372298a9b62e508013d83cbf0d95ffd72fc8fc6fb0d623db98a47e30640eb9 7d13621c703d7bbe44a196819e192422a9211e53a8a6ee138d99f541c7c45bbb 00a26e1fbae064a1c2c800ee32ff1bec58ea68e6bc3d9319d0e6f7d9d4858943 b3e674609b98360bbb85e1f9b80b779453ae635c894adda35bd100e7c6b1879c b9fd8cbae04c271e2e59702508d371c15f180310b6b5b16bf78f92c30cdf3451 c7d7b20e3bac8ac1a104ce0015b29d6ca9f66e4b921f3b7760d14b0d0ef806bd 50771023cdca6100850cdd2f60cabab98e9e42e6de099a1f399b898efc79321a 736e41ab6937e217c5ecaaf37eb7bb7136e8f9e29089c8be9567e8ec9bc76002 52cddd12cdf1c506e78e29591392fc24e7be5b9c5b6136ac2ecff15a94fe0768 132e70b68a7ef9c5f401b812458745e73c3b1143fbd6d548266d446e8ed21800 a5c2587a5b911408527f894750bc2fd3ad05d88fea96eb3fc521ce217cea614b c36d3589279a9754ed703a49e284243064bb8201823bb1e6c65ce1f1cefeb3a1 f352384e3ee128d4d9f5e53677e193699a4a0b32b87b494abbe9affe461c8224 623cb978cbe7068f3f5573abdbf77acb50f1fa21456d6c426ee7d14d925cee2c fe4eaf7646a7d7edc86ffc7bb3e02ba1bddac47517eb676a73c8cba574ac7583 74564c30fe7b0bed8908ed7550c7a1c0e81dcf9972c5588223a5c31f7c5f24f4 3957b9c95219d16df5af553548404c6ed013feaaaf3311ccc19767979f81afac 52e16912389caae211a4ca91048df9d62541505cae921b1adecd1c92a5e3106f 9aeb3d3ba84f8209795c9fe000a920d7b58e361c2ad09856e4b385cf1d3fabcf 52e159e9ed060899121461c71906a83f47c1337db4d487590c1d7b742e23e29d f4042896920a3546282ce109658cf0f1535d98aec685a54bb653bbb52f71f560 81581d762fda656949b3c072d157a16cbc96e27f73410fed70cc2d816ab76632 01d2d48e6e5599d24534f3482427eb4501508b8c1068583f7354074462aacbfb 323f4706c986053428d87e580cb45426eb38eee25fb956345607f71f76e2af64 c48ae1b3e86ab129abbf82123f987b773cda8a621313d5e3e12a82504aa4b4a2 764a1d46d895c15e2de0d8953d4a349c11ce203bd8fd5853c7acb497040e818e e59cb3843b69071cc6a876a73433694f55ad77999c1f2d9840cad6f4d46cddf0 f0c7fa7f51616a2b8b3d3c66fcd1b999ce236223d899a24d21d7cf51c7f8320b cf103bbd9665692f9ebb884207be5d48260d16cc118184ddeebaf8db303372d5 718247883127bebcf88b3c0aa856d2c82f205f0ad882cb40fc308f011d4c6ed9 12d01937773a71a29ddc4fcbe01005e7f3736b25414dbeb69f1f15504d65cd2b 8570f352e0cce7b04f9a415cbf78ea49b22c6dd1e199d2bf2a36eb7863b52633 72f0a2aaa9927f98bb985d97aec6cc91caa36f06ae37fd49e717f3c465943bf6 955bf1291296b84cc7f1a01a9d38145a747cac1f901739405cc358c1a791543c fdc3c3ca79c46bbd3226776dc9ed47e1ff3f3a7748860b9894812132f0290cce 5277b50bcf94ad33066eaa0d94885009bb6a2ccecc8748ab5d2b053d1c4bc8de c82775ef48623297b0ff928b04c03b1502645d58ed9c111478bb9c158a6245d7 88cd50b04d60e049714273798db1af00d5106280849efed1a9c1e37ffd97e253 d77f732b38cb637343ec20a66da97c5b39c9c3e8ff4b84a444e3d32164612791 cab9c3f461b91e213008d896bba641ad24993f98d1c7c872626c00a8b7c001ca 4c62c3d7c18c0b5f240c56b6f20e1ca4eca30b7d8806f66aa43f5df8a8d382ac a70644c6f3c2cabee8ad982a2994c09e81962abdc94f0fe48a5de7b155941de9 3ca3119e94ef5a78db0e52495b37ee42d3d87917cdee4cbf8ff53989939b626e 0df77c493d22bd419c3db7ee8f464acaeb6d83d607a7bd5a6083a62c1b318f94 011fdb5a7173b0924e03210b8ce3d0bf52a5982574faf3b110739d0288e5da3b 3361b6da469b5eacd5a5699ec7474c764d1426ae5caab18133252b5d186c1f41 eb68f27174f1dd2d342ea6be42eb3635fdd597031142086cef689f143e149faf 1fd91c95a7eb4552eec9f60fa61d3f904ae3c8799fa1a80926dea02cb8b69fcc 2fdce58215cfcd09250e3830dd355ef53090b5f8ea6c65ea7db805d720ead1a2 b1678ccc6288b290116e3d8341eb32e9bdc0e7c4bf1872afee58eb9430f82c45 48eb663ff455d22f43f0b6cb27f7d5eb0943d4b60b683717198002d7b76bf4c3 2423895ba50880058a75cbbdf5a535ce176ec31a14ab32d9de4dc3855156b8b9 06043986a58cfa6a7fa0668668b39f297b00dc2aa7470bc9608021165442b15a b881c686c1b423b1c36eca6246e5bd5d0dbf2f1bcba8462a008ca7dad7b8ce9d 1222668bc17ed0be0cd5f58172d8c058cf3869d26b201b850e46e2d1ba6528d1 1bfa0e56cb48d57372b35238c12746ee52345d52526ebabc79bdc28ac559f1ac 0dbd0538a75a12d385837b24a7948bfb3e7b9ec2e10b05516396041628e18a30 99910ae1888e67a0de161a454574c19c1f008f1c8559204e575db88dc80f1a94 1f000d2ec665610f784536730adf1b3e5b9b9db13a42165d9544acb881b0b92c d4970792197ccc02b043555225c505be8495eaf84b6a70d55139dda4c63b20fb e4601ad66a30cd9bf6e8a7291282a91731c303b459843c73d6b3538bfc3656ef 256a7723b4f9b7d7f652579beb53a1de9e7bee8e75833cf73b259452783f3251 613669fc70b3044796ae69b20a3fb3ad32f2e0e64d789a00dd880ec974e6afe3 0a9baa39c17da06593fefecc921b5a56b08e41dfae48234f0fc23d8f6a376ac3 8fed0f0e6b4d42105fff2fa2f5d17c1a29959a58edb4fa15f137f0676c6ea7be 630f0d0f9a93c58f56d9cd2f6ffd88e32588b5a2ceb0e1888429d3a38db25ce0 7e12a83617a864086ecf6b8c440f108ecbffc96bcbc8df5f3eb3c72cbd5b4e12 d267956f1075d0755c7f76b498b63e7f51c6e1c4902067d3be637346db102f1d d2e828db06faf7dd49e7e5223c21231a3b4cc4c3342cd71a3a92f06d727214de ce057552eb966328262a82f0106561632761a1872a5348c00e2fea537a946a4a e44b42268cb4127b29e47f5cc6499e67b618afd48462eaccc13477423abebca8 f9f0712fed976ab4091ac0dc3c15b3c2dee4bd54eda3e08c6dd2f9a4e4acdaa1 0e65a84589f21ec3b9ab6aee1d24ac1be5cbf7734450bef6f523b1052601341a 12fee1a3b1c49e1bea073a7bfb93c8879582febb9c28354ed5ce0268077fd176 4bc6cd65e4451fae3817ae3210d8a59303520d0eb1fe4c5d5e43d8466dd784d5 b96717f2b1ff0ae2dde66343dc8e00268366d0e8688337f6ab7d18508968ac8e e095f6be7cfbd4143c065b3f74abd9efac0552e91bd66c50fafb075534539ecc 750c8f66ef049a2f66dc488f51a125873b97569c692b9c31040bd4da3759d5ad 7612ac603bee6381390e5a20fff937b2532f880a0e51671f2641b67d58135f60 ebf737d8044309797fb792a059b02311943c12876d3a5c1384166ee2b717820f 80849e5e6d4f4415d77a52dcd6fa289881920aee19735d03fcf5979c2b001993 c97589e7bce4464b1f1921265e8ec6af29040970f09f2c3475487e7ecf1d489d 9c407322aac4ed8983ca8b8dad19b211f0c0770195119157fe44f1bfda932ed9 31fbe13706f2716dc25b149e77da52c1ecc55b70053e41e8bd92221b1b385783 b6ff14454cad1917030df735d2c9220b40880d7fb0ed08841e9e705eedf9036a 06afd0020bd36ca861f8202223febfb21eb5c7e338c907a4ce654e457cb2a315 be6f0907e14047eb8fbfdd1f878550770c6fc2630832b81948927b6f80617372 163f776a0f464f59a6fa6bbbd7e93aca17ab04b04b7ce06d34226bb7e900d7e7 f1550dd1d473f38ec080a6dc9872442350179aa80ebca3afe3286c61b1ab732a 38610d484a664c9cced181b42bb7a96a076921437de67f6be1c922e07ca7ca25 5990aa4aa00146e517e50f3eb04336597f2f8deab18ca1439ee5a90863d4a72a 50d7699edcd2730a75ba37516553f0548e2abb5044ea89cac8851026aa73221f aac4a77bad6ea4d5dab8b739c0a01c9fe78dac1fec94fdfb1168bd5cd59798a9 95022a31b8a904b2ad50d8f60e7779fbed16e47c07b1ea65c19a13ad8ada2291 439ad6b57a6dc6b7dbe15edaf44d7582f0d6b316ff823d41cf85ab381d35b860 38bfbeae4653dd4124de9a1a3e53bfa3066d4e5961412d6c1de12cf77d27fbbe 55992a2b6cb34982966bd89eae451b39c701e79de02d7f3440a8c493a51d9d9c 4b44ea04e75b073a4d597f4d1fdd414179bb7bc5b8ee9001a3c3313eb7a81028 3bd990d568b7f23824d36b441004334bc7d88d6552e1b1b9a6f772ccd02af8cc c6c13ff416fe823ac2ebdd4d8c96c9dcdeb678ce832218eaecaac26d227aeb93 384e2915e3003d1e5a32fe42c165eb31ce1cf2f7972ab7080ff9dc95146cb44c fa79bb9d556b1063924ff37f150aecc8590ebf234550f4c90135fd301010d020 a06676590cf910a41cbe07a80e3711ecea47d43343eeefb106027aeb81f11fc1 359028b96cf085cdd4156f29c48aacfcab8974c51e31d1920648d844c48503b9 f4fe5c34eef748c3c9a3dde8eb3a7cf88f894f7f29275ea11ad9d21c2da7487a 7af8f1af63e71d95ea3c26f57a9a3818945cde9f1f70c77678e501a6ed2c5b53 df5347afdd221d101381ca39489ae4a40c79e60dfe914640b7c9a15695bde31d 9d68f3e4e218ed9d94181094ec24d78d8e5e74462edf23a04e0f5c309a488ea4 f69e67c58bf7d1f54daff66bcb9f967f4a800bedde5c1e8e3388594d2b6162e1 4e522383f361282994b5df8bec583c9076915963c387cd8fb79c5a143b77b20f 649473a9ea78b524cd53b328a181eb40c0cc67f93545baed9fb9d013a218829e d3dc41dd315b7390789ae65cef880d5b87f4b6f1806877f4828313e8842fcd02 2f14539f942ee6e9b0f10fcedffc6c2928737cdefb8ec12d1100296ceb74d56b 9dcb4fea73d041a83daf21a9cf9cecc054749ce61b55161cea8533d0ffcfd66a 8683d13dab4da5823053c4623a860b0f30990ee197a528780a276f81c70b639b 700509bd43a4d2b1cbe027b952587f67bf07617ef194f1d182718bb1761ea6d2 41a1e9fda04748ddd5fec36d1178167776ad5fad58ca7ae08c83ca4fba14897f add8a8ea5b584b8eab15afe856925109b90a73f21700ebe6be92064968ba8766 e6db06fece8a3c69c8345ebcd3a5448348e3ba7b3fd15a05f2eea0ee45783d5d d4c104b6d17052fc549dcc764684c848cd7aa470cd478752b814010cfe8c31d4 e36fc9e9c235d35b997678b41b2702b931498e32d22911d3c47aea55503b0033 c46fd9dc260f6d25ceed78280b2d5bbc08657efa5f7b962e30e22ed3893f768f eeb7a4cde0a142ea63cb9580b0938366b3b95324d18b17da2be4d2249106d3ff e4987600c30314a65d4ba41c132d4aaa6df9bfdf43c9f6edb4aee71afb3b1ca9 26c450378bd5de84dace9354dc11164c0df30a66c2623ffc9821e9ba96573474 3768619c48bbe89a1929c7d18f71dc321ae9147c29e1e9823e15389af035f1fb 65c1b6c65fab69cf2026b344b5b0cf7e312d8554c8ca9fae97b87edcdea5f945 79be69b0f629b18930c38f588b76f86a2c91dd5f8ff77265c58004639539767c f5c4443cae1a272c3ea900331bc349b72f7537e4ebc4ec9995a5388ef922731c b71e544eaa0ba427d75dfa2bbbe03694da48ef2be247833d143f81812c7b5a47 b7b2457939c77d5ea8ffd71ea933e4efe986ca8b455e53e0ae148b4ebc4d48e0 10fca1960f5ef371ad93ba33f223b7d2851ec0c9c76f70b2d629d6581368f90d 988f89d0727350eb720066e198c14229bd9c6b30a4313419df9ed31503ddb02c 2f81e060a1c1d035ff2cd062ff432329b6b16dd5e5ccd3ba1e3c64907ca41351 335def56d9aadb5e550a9a87df2c4171ddedc3257cebd96a88ba2ed0b13f7301 1bfe59272dc577a9821aa956551cb1f045c67d4b9228a04e0b4449a8b462ff0f 444cb383eb3f041882c5ece5a3ba472f6cc4586f101c63050d8ca8e4ccd0106f 39c3b97d1ffcca4f7d0560f49f5d913f67a3903789743d29b49b902b897774c2 fae6d7e20a850723d9764e97e3856dd3863329e66705d8d0ee2b9889aa70c327 1a095ec877ccfd6ad7c098273feb65d098e1f573626e57de6ae4dc61784c9042 d562adedb07334daa8ef9cbbefaf98d64b24c9741dd72445a9e120e183a63c6d e80223440a373cda0d6adad064bc2148b320113d7406e1eada98f1de93472b0e d15f5a5ad17f8a3ba30847d4e85424da0114eb96dd2eff269cccb4b3cc364591 48707fd6560c4cf9cc8222491d45810fe75673ebe2a7de749e9dac85242c1104 8963b85abad1a6a24eed9b63dd46902766eb8b0dc13d02b9e80e3ed5dbfe6511 49d0fe1c91a4037348cd6f6b8c2bddb8da46214c33e8c39c0630483b83305ab6 f803ac0ae722d368b352c47dfae3852904bdfe9bc7d4642e1126a482d5e9eb72 1084f0fbc396d6ac455df3ca4fdc22a24b8d1e00b3f904ffcb6d2fab8729590e 4314465a59abb470b1e05dd90925da4035ba6aa20ea102c56b6365865cd05a9e bdb6292729ed47f044cdc357e7081b249a275ba904149ce9719b57dcc144269b 033d157a3b91aead40b8a4f029b61f2a40d39b86ff38e8e57b52c03c426dd10f 138d03ed71356e08e6ea8d29c57dd8b2802054587a058c80773a75bf74bfe371 397cfd01c6fe89a34b41873338fbb70b4dfbe8a954d895e88cc87ab2cbcd7686 292e2bc17fee6d9bc8f09c710411605ba57f8534063ee3608ef39bdf73ec4db7 8150e00b1e5b68586aff8612515be21fc803344d3d0329badb0cdc99b80a1362 c92a75c221dde7e996ae5f8ea43a5760563a0532dcc6360a60dcb0c7abc13f75 49bebc69b650aeb8ad9b8382ecdd65a74d640153ee047060db71536e5166e58d c03564454188c4a7c29c94b4d128246140bad2a621e400137c7c8566c3ed98a3 75ccedb4415bd43430e80668831b2f667bcb086778ca43805d9b67ebceb0138e 47a95d5a7fa6e64dfd42fc92f103795e8bb26d8548cda156075d528f4ea79c37 ddb756c6e856bf99b3cda839a95509f50a9b18b87c8d9e07395eb1f37f351004 d813a9adfed2a92f4d2a386acba181fd12d26e8cd139e67e39bb1a45775b3f8f 36c6a368a17becb5b42b43855f1a1ec9b958077077a9ae8b2b6186854f2d7b96 5698732e696ef2306c582231ff9d48f919f4f5d59bf11df3e2bc7a9511a8555f e0f7dd725ddf4ca25c9bc32f1d6b33a562325573cb3bd9f9bdd03eb15a568bc7 6ecd766abcb6ddf79fbc0b7b510d7be17bf5497fe57e54fbab5f44b59522eff9 29ccff9bd63867d4bdd3d99c79bc54b9503d3312ce4792f280da7435e22c773f cee276d9b36560b5078d93780431258456ad73e47b98741c4d885b7e917a620d c465fbc35b063492301a77e868a24728a238335f1eaee5e7c94173fd1d5c2bbd b8a805da943c09244669e79bb62d6a58e80e0cdf069445905b332e0ff2e0a4b9 081c277c8956e574ce8315e55a4d1bc6a1f7084e3ef79c565c5c920e09ccf7d7 62a0c62da5b16a818926f5568bdb97370612c34391831bb8fe60ddef00333e80 e98bb0d8e342ff5ec1041a49101a848ca973b3143373404be9a6c2045244abc6 4667b53e81243320173ad6bc9564fc5249fc89410c70b915602a8c164529342c 26c435b82bd68d0b7ce15e81bc391b2b60b554be5867e72a9566a101e27ea9e1 9587ffe6e571e2e36fe80649da50c303ce81f852ccaf2b2e9b7b4664260c00c9 3b28089761856fa94678ff62ef7f5ff7bbdeba5ab033f2bd71ba5850bccdea9a 5d92b8261d4196ba81ffef7859087b51a05ae5121554181cc687a4a9490699d6 297f21664140a48c0cb3fd6e6581ff9acafceb152c76fc4bc9d31d946e2aa54d 87d4a74090844bd351a393e7c9fcd7fdde317947281a8680ef950e5b71cbf8f7 1435d4678846561678bf9d5600b2a787b6339d4ea3b8187a47ab5c6541817741 a8ddb8e7b2ad56a9d46dfda69e17472e610b68af71a3f7ea908c9bd69d68ac62 d8fb6e4733f9fb707f77994667f77e4e3ca818bf8a6af8120e50ddb02ee65f83 8c34552da56de382f71218a665199132e665a1aacf11cdfbc77e5ffaee59c61e 098d1aceb9cc5ef48ba98dd731a3440ee771b9ba272a2c3f07cccdd192f76661 eec48d281e1ced0a49fe3259dd5b4834e22da58794495cb12b14c50f42f352ad 01bf24b8be84788a89206619db1cce831a71cdc4fe0b51c61d0d93144557c19f 53559b4a639f13b09851a396796fc6ca68211f3ecc2a00a687dc3a526bc1efcc efd8b29f481a043c230077c3eb0afa01d99835eb6c5432bfb855b4edfe57b43d 69cc325592eee89f8c1219ae28e35e515f167491b1a1a99987fc1091e3d7ec8f ee3e949a0957a440e8dcf6401c7483a97e78c90248ebb05bf3fa2259fa3b767d bda559c3325659ebeb39078896a9076f66ebdb69b95d21477d422bf892c81c0c 435a8f4867263036f7af465acc3c052161c75a6e983e8ada56e5a88fe04b4a6d 2fdfe818559614cbdc8428e814aef218181d888820c66e8e92b3d0e038b7d971 9f7a9fb7d041cb442104b1042a85085541b4e19332f837a6bf8200ecce1e4e70 97b37dd1b11cbd70c90f0c80fc8c4ae8ee7dffb1ddb1a9b89fbbf94a53152516 238255be5a20745de3541e9e269b10bdc886707eb44e6b7677ad29f57c2e5380 18203e567a0bc5b74c253fe7f3f55632d245f17e1acdd0efbd806129e2c57ce1 183149a1e04a061ccd380488268820b73083007d21f9f09ed559d49b2ed9cbe5 2dc198fb09f54ff3988d9c90d94238ddc96197722d71cb1793164eb352ff4a5d 45cbacaea04505e21430785d1b66aa47eddb172a078a578fb976b018b3902743 98938e4ccb446cfe999b091234c844e2a6648de95ea4b7408320b1b37a0157a9 d6b18d00da6c1f1585aaecfb15f8913fa65cf12466dd5ebc55fa84b28cf39e2a 56fda2b69037627ee3958547c0eafc7f376c0941ee1dbb2dd5885b63727392c0 b3538a6ac0908d7f225b58f2185a1735e2f0013d66a5fc5c7371158fe97b1005 4f4ea02eb4ab62fdfd94367557069356661e2d2032e9ad94b04e9ff3a7c44a01 204 16e419538ca50ed94680f3a13ecea37c21e8cdaa4fec621163935c2218035a0abb8be68930c603159fd635f62592c22c842742eb2d845a2a0f00a2da754f2d05babc0534b9b2c908de5138142cfddb036796d4a7c057ef56de4200294f886f09cbeff39b1a0f2b98c59d33b9709095aa7b26245c8d7b28fce123677db92f0c04db68a76cd0477edffff397b7e8ad1b7a91008ed41d8c1ad2a5f32d32ead9800ad8327c39a72f33cbe342a0b610e6b5f66a0f083ecee31b2d40b9951a9333910f0a7f5a91a5ace137adacdfe969fd95fa101acb192ed6f1873b2764c39c85570679075806bcf626ddf4c9966e103379ac6d5ce8d8b1b00d97b6d4494fe917c706a8aa2265fd1c60e9302a903a1e7b198e1e879d7e828da04ca9b914cc8751980e2e60ecd022a942ae3b0db4832c3530259c3e4c9aece72992070e5c925d6f73020b0f42a08162e91ae14fc0d66217c9178b008925ad08cb4aa956ce8de60ad50e954c1e7fdac604e1783c138a6ad51f295b8d85ae91c1f5c4b35028e1a372da00f84137eea8de263dcbca6935810e7d69e714f778db75701bc03f99246f24d009dc56f481cf8ae2c2ef03c8143acb6fdc8b3e70348b5efc205ab8e407b76cd30251b53ad2cc7b2a18aaa5e40647b4c1dea3cd9634747e006f8d3c7def6645e100e34347b44b1a5383f014faa710d4f7a361e8868b76939858df4d51b2ce1623045fd3065dec39648af3973f551c40638a61268f4ad4fae0303a26ebe12d554406afa92575ac82e732965c1352e3ba0ddd7e5512f8fffe614a3014cd095f2c5405b9689ba74d1811762645ddb3fe09b3d8bcc5ce45cc63b945adc45c2e6a6e8e0a2dfb265ab77590c4636f2975b5c9eedef807c7dd50915caa2896931ff9e43d000136cf77bb49e4214490a19ba0b883e93ca056f108f3b0c7b3f83c9b0291dd05aed430c3fbcfeac1fb2a0356759d1fd18e08f2eba1f9527d5d8ba2235f648b0ebb636b067b469fb9474bd9a180fbe1d132fee3563cb10771faaa4ae8eaa76f0ba555d2f723903bd0390fb518e724060b6877840a523932fc3349e739dfba7805e26d5aaacac5b0bcf57ab48971bcaedbd3553c474e809bde1adf9b2e24d254000f87d3bbf4c15c31b886a5e94694e3f3b737670e7dacdad2a257cf3ce7a1a609ff0a1316ab8e5942b2acfbf93bfa7853c5396e301d126927a16e283eaf35ab04fef2a1a9fee695a3d503d9c27e677b2282768a7a9717ff1fdada9fd2399a41060d93e02b05c28a19f8223a2bc4e9c24732252b79154656191b2baf8abc61980920c7cc5e425339f24d3ee8440208b0d9018289cf7816996ddf40dbd17b15d3093832f025b9e2a828a5f8e68ab0090e896d8b8d132da4972fa06bb77d6f146202e41d09f2dfb81bceeb916a5adee209f2e7486d7b53c9f07bba4273166cd1af061d8ed64260353e65ddf8c04e47968d2deb6ac37113eba1066d5ba933c3b5e50c253e5cdcbfa765c33b079fb5a21abb809c14d4f8ec3154e1066e2ecc6a56f10c18f2c71af5f566239552628c37ebc1b472e1350fa9609ef7ae1f57de662251024913a9de56b950c37909c31b58595634cf69a377243620a8b3e7f566ad6e500a9c8bf63b1b1a935bc7762b2e56826bd8e14c2f4376fff3ca49a9542dfd6e6b0e6e15ec91fa4d1b2ed4c282e65e333aac5446408ceacceb14bfded640f760720c37851b04d6618da04172b0c001e2f21f16d9c94d56494ffbdce8881223e5c302a14febd128995b8dd1d3c367d82bd4c348bd0817ada2d50e92b3107d688bca08ec1c9edd4e6a64506ea264b7378b3a87251bf39aa53342fba1d680dd5bff270d5d04535e058a3378598f2f6c23c571da0ca0686821ef9d3627a08d561cc82b078612bd5a0abebb9213f319f2cd0584bd1780a14643e90b22f2cc2e3e41b0a30040ac76fad246d6a21db47d97f5dbacd06610bb088f2db94256c129a38f6b960145185dd690b8493eeb55f859d2232ca1fbaf02e939b78151cfe75b59f99af701d8090c4f96743f581de28b9bb821cea6708573663111f34365203ba7bb08b2094c08d93f664d2c735778f384d51a87787f17ed27ea8bf6fa4d26408d03050705c7444ab7f1ecd2b5358aa70770c53f1dd90089b0aa442e0e6cc01045b12c2b07397211ec466f29a556dcb75623a2982c57d5925ebd9a69f4dde7b4842f26d30db278f6c4bf53699a75aaae3663e6ba25ab2c5bdb3179018311d128818ffac3078a394e13e5328a3c6e517adbfbbfff80bc4b9455dd86f6ff59cc5713f0f3bc042c6d4b02a0263e296b2877c1808a7ee2635f762c07019c490618dbdeef741105ae62e6fb6ece1b60518eb1d66ed80bbc085ac0f354a74705b3fa325dec07b907d6d4caded00e9aae8aca23f722fc6070120711f9e20b97ea5aa8d2d2f119b9054a710b6432a940f208ea1cdb44690246cef0516d811ad544afbbc4e0b067160d2f814b5e6ee49492142c6fce17e855f256982f919cc492b50fe57349c156db0baac808c51f0c9b54be9978eae208506fdf04f01233e8af1074f7becae18a620c7a8065d9b9648c28186247c0371df92c8c5a5e5f6b19d80016864bfd61efb608af98f7a7eae861aa6d7212f2586dffc8443f4103a9f0d8ad2f8dcda496a5690275b6759e25f8b41d5f5c5112007860cbe40f5608a507aa86ed8a1078224e34041c974ca360398fadb5d4b0581e1af384e6fd86c1a68815044a86ea731c2564078c17e1a257270d1c84157d1025bfc807a022e6b54ce9bead496ddced6debf00177708c1e6fcb221442c417c27bf8e7cb3ffdbd48e0ae023695498bed036808091df16463fdf882bf8377ad04b4141de04dcdd242c932b6a0932116385a9275062fb5835a8c1e0d85d5da4772b7c73a43c6b540f099c0cfecfaa08d12c521b0043c51f707e8873c6701eb49b613601dd8dbc5b3ce3a9c5423747d840b2fcd190d4aaad085d3e99eaa28d37e1597b8e3e0414dddcc5da32a98839e68cc572745047655b5938b6993adf0f0b1d5f7c870e56b375a2be5185e7ed119e9e0c1e19703da6d7a4b36633220c311546e8952c71834ee7def771346f2f09bad8141cd4c02e40da9d005566d44a514dc8cebc4f70d9b410b6cc13830d2b2c3558f1dacec0256c1e492aa3a72026a08012658f29ee26e0c4605b4b768283b373336066f3f01062ab4cd91a941f84abf7f20680261b22893b0ffd524afaa9abf35cdc66a880a83c4b60c404a7e398ac61899b9210df8a4606d0dbdd3619cda46af803a03500428cdff39b4af0435cfeef4c21246b7e017810255b36d19bdaf09a500165dd702cf05d9a51e916a88f148206cccfc4a57337fc7ef3f3542381fdc6299a56d4a02a21dc849cbfa65c7dd2f79ae66f85bed57ea1baee35f5e3821faa5c6b6837a0afc686b2e3345dbe76a055c8adc1a40612fdb090759f0666cf9dd707405e08005fe933768e0eeec889f8d74596463cadee4f4be1ea919cad166953f9ccb8ca90ae4ae642f224e7b93a7221b44324cd31268c5e48aba977aad1149f543edfb490edeef8d06d1629d30fc4608019d29d71508422d0ca83e49aaff40428707e1670e4a4fac31834ebbd978f4f243ee7097b064ec400a486e5c897a0855980764e806923857cc350a97feec97b9108821d4b12e9f60f6e16e433b89c4bc59f33479039ae9390e27d395c014fb68d603acd915a0ddff2dc9a2d3e7021ce08d363efc032e9a83eea905b02b5cdc10e76645c8ce41cc99893351d707eda9f782d50d12087886d5ca78408ac81785a60f2bdc2e6079ce07ca4c1d921b6bee6fa1913b430511672278a08ee394d6f39eb13281a2b8fe6ecc6118b4fd3617827c911363130990fef13c790f6f82f3f0607e9abffa36918c7ac64dc4982788aff25ac85fea029e4c8f6c7600ccc74fd4da2b2840774327604582f5f80c0767a0caf6cd02660efd6a8927774bb9e423c3e6fc1994f921bc77d48b4cdef75c4cfd2c5bb2be2d0615c8917b0df429844f796b2c56b05e4c19be253cea6ed8e30495dcb65c78410f94b1e102e05945643e3c20a729e040f35388aa0c18dade36068436a9268e980def3dee9143b1616c035b591f762cb208239fcaae1165feb808bd9ae739458409e882a944f826ee5f18469a21357ccaebd0a935d25c954ae969890edce7dad40eb3a716a957a339944d8f5e99ebc3ad4eafd992e1759071f3c0307fb33a595400c08099b22cfc8f4133cb18d3f4743ea56bb989cf1b1d603970895ffd54817f0b04cb00b6589a6475a35d42316c9bceba6d64f76d242ec8951e900cf86141d70bca9d1558b29144724f97117571a7528a291322a2ac29ab4a4927631510b0c20473280fc992cc3b0593e8316118e3ba44975a082840fb7aa5d5528c372ea5430e7a391f6ca74fecda3dfd6f7d1755f66c1a5b91cb39b5f18f7d842cc02aabe206cc75ce799e3aa5cf603b5c226c4e139c84512221c35609e6b97a0e0a54792806f4b37740f15b3e185dcfc89a4a1849c272a3507f0044440dfbeaa4a46921d50822f69a3a6feb27c6f9f1dadefec8f967d92fe0031152b220199822320072ad0ce836b6d24ccef0af6db6a7a6b1a1f4fa2d1350207a50d84db55688a4e4f0b50ae35035298490fe503f3ca00b04feed7f3bf008f4e49eea7078cb81fd9c4e6201dfb2713f2f57913d36fa6f2a95fa2ac072432383135c62ae31d97e3ff0352407504cab906bb799179b5af1809f82904b5531a02c5b5f7a02cfcc1a07ad9509015d29949c67d4588999ce67c403aeb48b289157486e83632571a737bcd9abe80298d8e140de8ffd0268d8cd2725cdf2ee78d854869b67758609a59e544e35790f036a3b1ad729a0372bdf1d6d001073f9cc702bc9d009e4fe66f9b61389ce920b6d3ca0bcf2569b98f0171bcaa81f6114e9600a5c59e808c5b8ace5c750fa610a56fd72c8304607a2a9fc664cc1a63a49f26196222286258661edc07af5ec5c0461497fa3216e66589607eff192818d6fb27616ffc69f91b4b57b46d37564e305d6d39875648d9537064dc19af6ff1fab0df15a898efed71fddf35dd853621d03b198a832f91eaf8967af31de0e3b04c5057ad3e33ab68f7bc80358971602290718825ce1dd00a6d71f6557bab44e983ae84728f2c817cb269e486c061043de08f31a676e5a20d53dd661568f75442cf4c9ad103467a3004f2938155efb883b03e9e5d96539468dfd90bd0364179f101f338f49d2455eb7c10830fa65d28f5007404f56e864763a6217e54b5b8e915a84026f83c596be8c6d03cc4f9790c54d02e98f44e7e069679157101c0cd7f4898e7497ed566f95b9d9373a5ce3e0535d031e4d733c5cac601da4b6ce79d092eacddc7814dd10e8cccd2f18f2c0f3ec2b066ed3f2b39659ea8b3a5d948b01b506b618befb251e6f2b664892629b235aa60d6dc9bb820018ec47a97b5766ccc4276bba11aa66cb1b77d014916557d43d1507cd61e671d7034943b5b8dba7543645cf8dc6df3268625c312b70d42303d137019be8d84be1bc63af870ce559417eddcfe146854bf2d771508d2399534629100a648f9879e698ae4ae238b24e30b749384b2510b96d3bcd5abc00dcd39d885001e010f8d9ab5cc12c1a1e90dfb6c93c76e799e10f340cc907a053bfc30465020905ad33ca6b9680d5ea7592808b8a47f7d0554347cd0cfb4a2dd10d9916853202ca75b6d3a8d6eeda543bbf14ba9b71c7c0193f3d7b615aff09fba7fc4c21e30a1bdb20ff5348185c7d153af260a7e9ab1fa61f44078c0a0d02a28e58e6b91309779a1eaa221a031182c59af732ec9409a760b525ff4677829a80fb761eae8d0ef70e0aa96c6329814b8ed3c42ae6c027406bd0bf4ef1a3ab5ab52fc1d98695000292597d4f4d4dc054a0ca200c03f38fc4163d1ca9b442e707411c12907d9f0effb4ce25fbfb2253fe45a575d9a2ee41e3427a84dce1c32a50cde3d2a853ff0071f0314b2916526c034e23452b64123f11199b771ee2b1af0e486c083a801f0096a0dc342c13f29c1a53af8b4d732f18e1c4adfe04c83c3694023b0a0949ef0b35ca14f33d67090c0c01d516110f26151ee2fd405335ccfd8dfb8561507dc3000717094149c49aa7e28be60c99b5f7d9a66a09a91d0098c081ff825a28c270048db1c81ace94678cb22e908bc69451b77aeedeb2f9890571049fdd39d9ad280eb63751f0b4963666afb016c990707252900715688790c642026fca2f086b6e0573e4c42cd654a9c8f606cfd80d9199301b8440644db32a411f9c9ace3b55e1084b65af44e1ae299f808d0c21453e99491b439bf5759b10723ff74d22ec9680063ce4e984a7b2f630ca95303dfff9729ae09113af350c36ad2aa53e3ad5641908972949bddbf204e3138c42dff2a4fc32d16b54a0831fc5241c89be25d8efda0c069884d2f1fe5f5690e65e111a713e4ae6340f4d7387c3975bf7f09318f6b10cb1b77ab274697410c3b4ea5ced839a0c642cd89e0d0a9da6bb1a958a93d5d70951363f4555cc34e2645279ba3e20352a0d87ef386279649ea44dd2cd9d758c0a8d8de625cd4b227c4bc29fb6b0b36b186f375c4ca65af35ff85c3039501a050e85c72fdbe25dd3bdc67ffbddfcb3b83c4231d6f1781dae5fe365b2062ef4330850063729202e507f2680f2a0019d06125d24bd0c728ef8b73f36858892c53101926f6c1c068a356be4d67a73131d6e4ad7c34db092a873f6adc20bec57e308012a22b6734f091ab85850990e75148eb913cb2a2d14cf843bb3b5705cc431de0ed9fbe3e1cd15a57a22a63fbd69e3e380b8c00bfe3ba89a4838123b2bff9b7701f2e8a65ab158a7d37ef52fdafe2cf69afc13ff79694fe320b6f81642f20d8108b7e54ea1da597e65539f39b505f67b053e701ae6f978e839030df1e0e4da4108163ac5425ef2ca7d15f8d65aaca655f35465561c3078b27e6b401a9841e79c05700d65fe2d4af9ac60b44940cd193c79e115a583dbd2cb48bed93d9ec859f10f742c06317eb859e5f3f0ea5ddada506232216d1857f94c0e88b952ed47a6910430ab3d73a770c5331fbf93950d202fac53ee7be8eecd6ec3eb1ce7ca60321c0e21dca91a3f95e7626bbe8cc99bb6f32e00a5c4cb021108f0665dcddf6e51e70de5d6773c5653196e53065c0a55db5150db641795eba6f4dc6a75d9e262646803f3b42dfc878ab46ca75c5c972240a55e09a5213bd818b002125b2187379620002625592b59e844c46cbe065caea8435306cdec8fd75a06297555df76bd22bd09bf86cf07f53e7d65b63dfdde8f850e704a96e19e6eb8bbd883070f00a5f1af03e2cba2b7d4ec4b79b00c26fd1ade75cae58db490a969873fe04fd8495aa6820b34d48a59d16d0b62c6542109b5d0f028febeaaff7be514b2a11bc9295eefa200fc1eb14982cf794bc8356f140925c3ccd6d56a3df7aed7abd5990a2b448fd40f35c8f88073099f8a4bacd22a4b6842ea6c1ce5009a16b66e0ca62532b5ca0f09f8250cc4ec1c0d17cfab374af2641252df977b6266aebe6a1f8e4873ef20bb00a2115674c94d3ad83e39a7ecdfacc69b6f41f982926b83e45fcc6cc84713050a67a28576ee08599431bd7bed74a451ff793aff50ff17338405f3d8d114521806a28c8b27c64871b1baa08cea4a7386d36d3a8d27e21e9ae541a1becb55504203bbbf53a14c550d5842a7d83c304493e9d2a194b4bfd25996761b634f669a120e0155fe83c0f9c5abcd68c235cbe6d7a1142c9aac98c87fd6a0beceb5e4e39c095ea2784e03cea3ab9d0349f33f07aec785838cd41fe6d9979a3b36b02b1b1406892939af622a34a2ef570ebb7a4b2bd8289e9560592ed2fe324e844a5be1190c12b6cddafe21b50f2fbe21c31cfaac26675c5d1a1cf76baf9feec4f1f1ce0708b98c321d6e881024ff6afec0f6a3251167e91817d904fad589039dd8db14be0817986f8191143abca5dffdd49f09a4cd9e9d5455c23a47d35f84d0569b379e09580f0346aa8d8837e7aed4f0fec7cf67b80fd3f5f2e2df48125ca6b98c518101b5d8d187ab91bbbd1accea50ba1bb92de1665325dde6d7ff89165d882ccc78025dbcfbb5795d83b797899206ff8ee04821ce86cbbf53077fc1441f9184ab440925ba058387019eb286594c2a38152548e027e8c2ee9109da30c60d8b2c42a60d961e09657811c6df334d66aef12fc5cf88e273569cf44cb9406258496044e808af22dd00a1561b4ef75343dd2052735ca8d4a8c81c27447cb0be59447da28108ce08d22221e90162c875dec610c5710b40375928af94d7d59296754f52050107d0ba13af9e404b0b055a76149e9cba9d2826c24a5ea34886b015abbfed3fe603fdc0002c6a2df1f274f0766ff9dc5f5579aa59290948163146ee04855285f90d24e07459daa71a6b1ecf7b1be2147696655f18a23e7c70bab4b8c63368aef10d37459811086cfbd940f11fa2f140847148851eb77c08c295118c490d8f94c30a08880489a007c1ddf1c37aa56f23d0f80bf2b1d012f4d6fa0eded13178240503753627914817389eb02480e1bea1997ad0e0684f1a319b0e3760e91b392ea5041679da09728a98ecd59742fef00d6cdb70ea08a2a2535d35c092937445551f09ea87b4410d08ef6a2c9441c8b32e410edd3a9e2317ab32e8d3b6a2374e00420f64c01ec37ec2ecdd5aa92b48628c11dd30209092d9f9a26c4d42a4e414b34f0f0005a349f8c7b6946313ec69ba63bae3c2013442c64d27b31c4763eed7f24709a6103a39fa57d94446263735995cadfb4275d3ba4f069b414dae108a4ac8860ce01251b6569e3a005c2fbd2973bba835869a4677624e5df55da1d991c0edc50a1594d733958ffe22a8a35a5c9230ac051b7267873f62e2ff056103c2730bf50821a36d8cb15978ec745a936a357326e66b324db4815cf3e775837ea458860109b0f07beb8a52512b6c3d2c2983956f808a1f3eb161b1674f48e27f97ebdef60f2aaeba48671e01b83808096d3d3cf5c8ad96dab79090b2a55a219d868fa45c05d8ed0b962a61af84132aa99cb9fd028149d458c0426fdc8da30147c9e39c1002a02730a489224bf62e20d431dd92350c1fa9df17b21588cd3e1eb7b238e07c0d9b9c834008a434112d33db8b1bf23c0920dc450c10b9f219dd42ca3541025406f62b3fe1e5a7bbb24d97febf1241311300631ff4a31c33871c0063d37339780e8669563ebfeb03027c736ef3e4140b3c3c86bf3f7228586e71f43612200f090875abcbba5cee99ee79d3ac7a68c91393e1e63eba4bd2f33846b21263a84bfc0f8f7da3a5adb861e4b0e4642bc14c269eed41312cfab11a8bb449d84e81cae90199e589e375e8732dae771246923a64e92baffe199abed788577f4fd525c7e40aab1fb215d64fbc7e4c3c4f01ea4376de6e31f6a8a010cc658420d009d36e55089e7f0d30a81d737cdca49a0a1008ff69fde6ffd1cf2e611859ca1ef81819ae0ebc557327f33e38f2750b6d66ffcde96ae11e625dd3b3380a8dc199f68a063c0380e8cf746b154b0bb32e13746498492cfea4f64e08225f1b48fcd49bb570be02ddc692fbc8525e38ffdf80ee1cd3e206349542e04597273f7263816959b7970e31eb189c37c67dbf4b840b984497102c88d0400de78ed3a39b52aaada390d20bed22fed65744eafaf541661470a0be697eda136be7ddec44bde772810e147f0791b2096138a56c0db00acf93054f91ac6bc0e3657e203410ccaf656cec06480820791ba583c3c89fe6ed44d39cab0fcb871ac47007b942bd974ab5b80e8f9304350f5cdc608bcd4e9f65c6f8ca48bdd3d17eb196971729627183f4a485b872032adb03ef61807b494560353a65b3091d41baa37955cc7cd4756505c38f4671085a84845566bc7b1d19ea1c23a08d9ff0f2db5cc6d6858e9e1e969075dc941106f3027c7e26cc38c8b8df447d6bd209ec9cbf963b2b60e2030440d0801c4e110358f7c55801369b23f8e4559af84ccc9d87aa14e9f00a1fe65ab1c85d0c070e05b2f2d7f553e032af8ad700937d5830f650880c51033c2c1a8b89e0659f2f3c0a7b937bda0acd91d2c1cfa3ec862bc2bd49666464a4b71b86d0321daa0139be093bd15251aad8f6af5e026abe6a3063f7fdf76453429265d136fa0eb62acff00a08fa64f9e63377f34644e8ff27db06357cd0e5fa31b3d1da7f02091abefeee075e0850908296d9daf74f360c9af16474572c38d928aeb7e7e3b1d299a0ec570fc63989ca6ce05c42df06e6409e37c3fa263ce92d361ea315940dee71267d9b0a09bb21390b2e37d7fc0e2ff5b93f3dc93997cf0dbafd27bbe0a999a56bd71906e618f93df4f53e309147b58b3978a1b7af931d884cc2df5fe18a2f5f6256640e2498eeed0874cd59217c7a5a2e2572006a2c3eafb08da01f31291ab69cd4b80a0d46f6cbb5b05f15bbeb931567e3c1d47f57902cbbdd9708cd8f9311e6fec00f266989a39d73ed9450205aeb878c05af1314c8d062060355d241af56145c590fa24335ee26b5b010b3494b93a37bee088c0185bc1751c6e01f4ca5d233fc2c056a08ce6cc681323982281d32804b50f510191c6147517775135c118415e4d40463f5bfb4f36152d050f908fabf5d714441d08474f5facb7432972fefa599ca0496f314d1737d48dbbba0ea092930261298e51f98dd2bf2eb0c3c2e9f84f0c308197a7ea2738129dd2773d1309e3871b01f9bec883508fb69ce6fe5a162fcb80ce851e11aca3f4cc918367e40a10bc1bab765c4f8872eb60f34ff5d282fe00f0bfd5d3e5168ae90fb4200851850d5a88aaf650bb3c0f356490c7c8cb5dcdc18026645d891455c98be8b0abe0067d0f1e9aaa6930fc8e3c3155fd0d8748c79170b5cf8e73c8ef4bd03851a485ee0e8c5e4ddaf24555673a06debc8c1a47818520b088becbd7099212c1c4ff64382c63d9ed1f2cf25f3ffeb7f31b9bd589e7010024e3f99eb151c294198dc98f04d08596da3460659f82ba21369a62c0304371102e903fd4bd2d78d06f97564307566642b50147fe28934cf197ecc8400bc4eac0065f1d2e53672e229ec0bbdd45e26e3598c523f4c1e7255329fa501e64d70580ef7efac180f95770585486b8c620cf724edd86d367519f7273deda6efb33ddb071df3cee79d69811fd4f8075bc03f8619b21c276fce3bfc45a92e0baef02e240b8be54dddd9d8ca16a49d7894823b6b22e61a0194210c5bf5f643e946421c40067c2c3614a2e7d6abaf14de09f720a8ed5e038eafffb178910f66f2a275588e0a1df2abc2014d2db73259eaf18ac722d94ddde567805370d1f860baf274fc9d0e2b0763d44270c11f5873e9f8109a4f83eb83b2e4e7681923e8f30467c6ca9803fdc5905d54b66383e3cfc1b7c5e7de5db9985eb335c997fe2e920da250b7af00fc82f3ec771a90a6cb9762e7421e39632c8beeb223a46a9391b34f1214c1050bb377874ed8e6a08b0634da87ec1fa1afc76460111bb7b607dcca77b3e7d9fe0d791be4d72409292ff9bad34e67cb21cf5d6ba6b220d77d768546b977f4c02800c502fff761a4384d1fda556dbcf16ed0fce9213eb1ae4ecc11cb50a16f60db09f0705eebe70056716413da666920d7ab0fdde72f90fa49774449d4c2701c9e054573968cc5719a63bc4b7ad50b7ca9efd6b1d3b19a1f135c6128aa42c6a0230f1f398b45b88f69c9c148c8bfd072dade1a7789fe25f64a4525b4e6d75761c50be2e8f0ad01d966ac7599b409a101c0959f22870db5f8d330cf2fdec26b34be0ea54801cc0d685f89904924cba6af5cbd7bf2c5865caa6c893886f3f1d6d8200619fb7b86e762f94d35081e6512252f3055216bba4460b96fba0e75ece44bba0705f5b413a6b88a2fde10630675aa49cb926b48ad1ac79452937f49cece871c050b39d3a54fe0a8504984e68f751524120b3aded08ea867d8faac8c688e6f880a5accad604206897b8a8b067e15a41aa053aae606ca5963ffa6e538093ad8280913e56105e7c11e9a3335033fd03472d76f50082cdabe05fae23b1c8a5196a10b8ec387b1dc87902010f25caf6e0af83fe5299396c85c866f436b5fd9cf20390bfc10c7a0056bb78d6537b918a826d2f8b80520bb8f61d3186fa6b57685e2a2061f1e2dd90687c2317a5fdbc1c5d88c99eeeb4d03ff66096803ca14669e2bcf03ccb4722719db4928c9fbe97298999a21abb195cbc6fa331c851faab9b6f84c029dc9702d89dfeb143bc11c80a89929f10d4dab8c7fe04bce0defa51c3f815306c75497a6ac01116e1110d500a855243a8c932625969aa74f504571619e8f7f00e5269170c817c605aadd7ea60dcf5bd2bda783e7bc763832d0b158bfa4ad9c057cdcd027d39817ea1d9451f7e35a469b5808c909423818656e6e49ab7df8590cbf6a39883e365e33cee6f69f9d7389459a1a27e9a959b534ee5d9734d03bb30d9300842d31a83f480f481927f9aac0b45addc03eaec44043bc3ec45d19f5e3043cac7f300058406bc9acbace495c8682c7bdb74c7bdf07359f84c378bd21b905a08d7507cb76385a0c8d15926e3cb29cfac7d02cb52e2ecb0681c960f6de3c0ae985f5e5447f204d3d8a1b53e607029dace2bc74c42deba777a34239b99c080a61648582e6e068b153542c3cd5af2f5adbcf29dfea5b16e28a1d6be47cc82b0c616acbb4c79b149df5c052d80e4a7ac4add12a48a22edcccebe2657cca5a2f000ac7300f55ca56bdede30cb3283756a0c11c01a607d9c762f5826862beb83b039366c5504e2c70ae0f5b18c1b27e49033b0f30dcbeaedf2afd6e9425f90b46065dfcc0f351fc6064406514ffc2b3a93174d80ac67161531a4b39538e6e2fbb0e0d1e2049d8d11d58f0893a8a913857baa7afe9dfa0c4c615439e654283b37d01aa0cc42f90b06903e4fc4e18ad8fd003b19709e5fbd8f643a86b8d083d4e07045002fb6a8aa4f414ac973ef3741fcc28437a6ad79605beef16bcec0f9807580b77566ee36360e7e3ddbf7163435bb1e98d2ada225777f05ca84226f770fffd042b12e0ae3594fc3e8ed3a290322c223f553d334211c0f381d43836752042480f8926171845044a4abd09e895bfe3ad2c9d01574ea3d008a228ff827c5cf25e017e8b969f07b04d0eee2edd6f2e80d5a7395021156fcfc7227d4f4ea224ef05089dcb07f8e0ef02136cfacafad31cdb783dc31502809b9486963a1bb24eb7e60c2d43b048080affa018b7913a847432318fe68e6247942975f7ced72039e860043bcfc9a2bf30b3214b1240ea03959071729984df735129eb9b1d16c956e30e0a34098c4fdc8c22817843e2c08af864a261319877e35f06b54542c4d80ada710fee11d3030ba1df9d61c572b58154ae658a052ff4cfb7ac3bc5939d4f27deeb053417eb3c93927f8fd7dc5e20961a09a5d544c10efc076e5a25507406bd29ed0f5963fba069d4dadd406b143e8b361f535ea3697aeed4b8973c63834b50293e0340e2bc94f77eb38647ec0ca02122dab1a3205a538b12bc5911448e9e4da9950fb3457dd06d06b3c284e3ea6dba3f183d2be52d9263f1103fe009613637930307dff5b15208a7cb76aabd1434210cba0e734ef02a3f8ce36764f593acf23ff50cf237540a827b14744f3482cae9ff2928a5de660920655fe42c87e9e22631d10f5e16190481dce7cb7599481c27d9b1ce20046b99e3697992f12e599695d9bb0c8149a988e7c93eaa91fb3228b151cd26cf1dc7667817364c53d8460d307a930e028cd29407453b8ae839f0989171dcf70ff519e5e9e4fa15e6ea3b6b0b41dc07d45cc09a08a09da72561f891e79492130f00b2d3faf1d2e5f220f1dec531800100d1d82016385933fe98190f32c81422ed36a26391f966f10fcaf91f762cc8047f9ce379c2f2962c9d1a38308d6e10c95d540699b1310bc041d1f888b4229e02c19c760b77b37d9985459eeecb12273a423ae0d5605c39a7abb0ce71841eca08a006b9579c1c08d2a29bf8f2bd14f51abbe0e8ac9e8755951377171b13164c091998712b360b8742d0102e67372fa53f9da9220e09c8881f9926fbf7e6cba608235130abb062e209a5891733e8202e8058f78370bca5ced0e4aa0185b63edb03b54c372a90f21f75613585a4fcc20f08e2c00aa5ff0da55dc4955c5e7361de0aa561488997704865724a8581a010a9ffe74560c24880b2b4a0c8996920c29d0b4460cbe6da64df6c538eb828f16bb3f82438ee3b594a9cf4bbfe13e5bd297d0c7cc4920319ccb0efbe3afceaed02787515d82d99ef73093aff2079e8db8ed20f75fa33dc24a823c6665a249d4dc661516a4128fdad663f0ee72d092bd41a1b0ba5ee01abe290f1d86ede37ceb70933d790ff53669f9b4e5117ae03172cca9b06afcca3a72786e5d5b19851935ae54e5659540adbdf7c9c0a9d38952cd9f5f10da48a4b0930a5dc83887af02f56438334a77c8db3a683358a558e715c0e56c402c78a7d09a78e765033a9d42e5f20ef551d980fc7770ec0643cbb7762d9504505fa41eeae028b235fc636d46041d772d2c180df0ced81559a8bcfac842999850f203aacd834622c2260ecd19d825951ef1ee1aa34a2a31f4ec23287de0eb74104efbc3ed8ca4c5033f0d95f427ef3a483417edff1fcbb29271d0edcf154765f0fec9bfe606453d3c77b23905cfab13cb8044b2776c4c5d13bc73c60b025b48b047f35fca842ae3d3316ae2a0aa61eca434fc79507124081505404b8b32ad4070f635717d289cef521b4d6e1a0e3801c3ad12c5204776cd4f9ede04f45265bc5063c7bb74da601392c99199097c21dd43a1c39fdf9966f65ccca7f3e866981060468f423d4896043bdf4dbe69e656dec113e86a0af6dd8acf8030482c527d3d501f430b0e1cce73c91a626a5209b74e79a508d8836870fc85c28e4e4dae58a5900f3fb4b28223aef935f783b2aa1be5670cfed32dfcfbec9fe4956ee9a5742710164340b658a2d51760a3f6b992ac0d611566ff537fff9c85568432298abe6900001adf3372e0bc5c8adf10877c652ddf6c0c7b3b7e1a55330cc61990101868b0819b60ae89ce1ca35d0f91c88737b29cff8cfff5fefd7fbf6760aa66c0373a704d9ee152dd3ac8a45b1feef865f60269d71fdcdb5e64312bad7ed57f09cb9fa029319eb67a1860dcc6dc9293f649030bc0b72723b6719e1d8ee1a16ba8bbcae013e6b209056ec2c0d71e72a29679f2e09b0e9d96c5a73c113304fb2041c15af0a719c570209fc35b1cd2e4045395cf71126068f850b2ff54bfed7d7df10537c0beea4a69b798139b9d7091da9735b8a026bb0ca2d75cc2a2417ffd14ac3ba430e628666c91d88d920cf5e1d672cc732c8e0180fc0653bd48d344e67abbf56e8044f1ebc12e05cc48dfb0a0614eec0fb4a4369fe1f764a28979e43c6c0bcb1a30b654861cd62197b0035a40da110f4239b07339d06d8b9cf3d918ec7fd905a7708173d5fa3addcfad525ea1301e08e8a2e4e3ab5c84b33a8a9bc861443eab6ba00596719ff66e957915f0fdde36c96ca0127ae10468de11ed30e0d9b3245626404aa958e3214f8c2a577ae9a66a4156d0180e1c955b36f9d32e046cb9d33553803215e93ceac4afe88d91caef2efb0768bf3cefc109c1e9a12073d22d46e2e520336ded892066f76566383550f04089f2a3fc23594bcb903b580aff57b8fa1d104241b3e18b2441eccbf09710c15f59f41c1e606622ebd803560cc2f0b22a5e80d8322bda71d6d5562dec57f57961e7ba2186f39812f391e08fd21fe6115836f07722a88e2d2f06d16ccaa65f1d9406711d6e8f9cf1f9cf28e9b65e3eda094a50c96a9dfef4dbd0ef94cb5327694d071b17a17fdd86f81c55a25e2268f3af4670a32e8873e35c7320aaa8b793f060011fc8e71dd3594f106f340d86f3990d2b00b3d016f8778025b2f2c241507179ed165b8bce6001157368a7e5ea50f7475e60e91f4cb2b41d23fecf2835993295f2c0e871be5d8dddf7cb2552c61afade985055d95a8e6dc6f5e69ed49c9b05f4a0cf54e969c9b10cca60da19158f5812c6c04cead56dfee889193ab86e803dac1c5ef2e187c6edb3dc88ab775c1e422ddd3090fb7933dbe284baeb1de3f10e999a9a745cce17da2d7c0a360bc69564f92860a6fce947c3d284b6d4139fe0c7bfaa26f75492e25d7a132154719e5319e6a0107bc81424c0c2d0590ec5e2332c7168a1b260ef2a95f2820148541c343d52cf809638581ce07c9f12efaceec80548043fd7d47f4d25e57dbf80ba55054b6890201fbb4ec8d061ba553d9b099999875a6fdce15af3335db0651c98c30e68295de03c31d8dfc059e94a24ee022951981a11ab491d99f01d2fbbfc21e5ea2babd290ebd0107cc4d770dac328077e71f7aab62001184301dd86ba403e0dd66a9fdbf07509b6600fc7a32cb6f36013b77987599b0adbdd507786c55729b284a83bf4d027bbcf3ca4315634ea40863ba60439a0933440178f052288610b22a73220ce9012ef08b538b46fb24f06c7895ca84901e14bf0797494b3ca0c3363b078ae9960b0ec81b0bc89762e50acd775cbc1f27f5f1199792b937287d5ee8d2e33bd1e6003969b29a36333b16931c5c409258833440e662301533c678b4f2bdb63345070b0512dff61ab32e0957d69a87a5d2eac76d2d045040a73db8bd1515122fe4d601611014aaf1235d5611371d9e8a4e5200c7806f591c48168fc4a86d37adac2809ce0bbcb537a5b469e131e79a08f8ef9cf8348b11ee2ce2e938d76f1bbc59220d459d4bf756ed9867fa30fbcd369c9aabe2e99482d39b340823fa7ea24a63470379a61e196366c5cac51d8b1bbab1a93b0804edca1879a7a3631d6e110597e70756b7e2ea006e4a17542bf1593fda7dd0cd84eebcc497425f355c217cc222ce015f29bd08cc1d5cfa95481bf2029320ac9fb99c2e5334eba5f1699115c28a89066f5307f2735c48604281d0df91a43144baba41ffe1d6660cb24f6373ed1afa0b928892a95b235b78d4eebdba762b96c688436216fb48e5e34eba10281254c20dd37cf56aa24f97fe7c55bed85714e5a0307018262885191f8481d5a0bf8abc0d92978c85a8587e855e86bd5a92b8b96ee44add6754f181fe1bf129109cb283018c34846fc01c2b681befeec173e0ebbbbd9262ab19a9c1395a8a363018b47a0799edbc9472040266ffe8af19c1e0402c290eb718a86ef0387e397c945f44e800935b4137144d742899d3111bbcb06b548a8b41425474148da36d9ce21feaac0abf7260624331a4b6d97cc26cf652b9c51f94f23d933ad8acf2854b579bb6ef00dd709624c761ea2d706fd817faf74c88058ba08e568741b599937d14c2183107bbb012884340ab18c6a17ecacea91826ec7a32f282912126eb0e78c468723f0c4525a6749d3618908fe941203ed085122563bd69b49e10f4c390469802a3240a14485da825b22e44d685ee6e3fe170c554b8cf2960101b6be98b3534fa00c4038839667928405113ea086fff8818f0bf9a45ab0ef3b7dab8f8bab618af7df704d19f569168ef56e22a0e203d9e693a65c28e0e5445dcc90f4691e397ffceb30ef2e3c57bcc069902f708f2e9980c2a06c7bec2c0153785864288010f539bff0cd801ca46e1d153b2474b4a92815d32eaa47c3d07d359b1d71bd2e1b5459dea08fd3c78385cf7abbad28030e9b2673636a108ba2ac1e98ab85d1632292399d80fdb0b60208bded3a97ca63fff65cf13d6d482f0d8cef1cfb588d677664cafee064ab4a739c5cf99cb51bf7fe4e0d4fc36670605b2c59dacf9f5164a66ac119c0f4038ba38ea06b8843c3040a3a2d9e836b4eac9929d60ca82ff81f339f80b7a0aa81dc77eca85b503203956106c545c062f3634d017f3860e870352aa0ca03f046e1f9d609a155633bc25f9515212a3f4349cc9da21b8b2fc3a4c57537c30800b4bb5c597ed3d7530dc29b6381627ccb0e8145b8391d277800b5ee5386fd8ab092b6d5098bed8341a7f062d58e5381e610792828ebb3973b00c7b941361ab2b0759a21dcb35c31aa15833bdbb3abdb6e1a24a28c0b78a42cc2fe113665130f3028210f60c294910a3711d3bf5f060ce1ef07b9947f9ed243ba94f4f97414c060f5a805c1ed5fff948823615638384054bd322fb128967e8e77ac1e8e0b589e908b086434b474c628656cdaa2fff803d50f688eb4f2de41da0679401d81253a1028e1b826083b0cfaf4bb527d38051e4214dd492d3fcd93c0296aea777c0d3690efebe7ee1104a6ca67e109b35d21ddd615a4e4f4db02ba694d254158413be0605b57dc9d2157741fb603ae5839c1f4f67fb4bac8575dbee59287310d625462f049c5b5cc5e9c9f762cd20972bc6c8991fbfe5a21ef183801f53f1c2802e752e022716d7a4bfe64eb808f582276c1cce06377b5e7b61688b896bc66882bada5d0824727ea47c3a45f1666bbb7913b15d8a92e49303d12c9e811b8d1f895bddbf04ec2d3b87fb2e70cb87a0431d5d7a887116c45968fb79a870dfe3e297a00439063dac09f303f07bcb73665552a4342490cb60c30aff178319406cb9d97de7aa0c3e5e5254171a11e813bdf5494e13e65d2c08d81371815b9abf1f31031c185f0717874be10ed6e8735c298f0f1696608168247fd2c070bdc670019880ecfc7803e45303ec93afc4941e2a6a90cdbc9b463114eb5f4eb9ef3b4c24909eb96b510dba51f59eca3351844cb30be921d1ee3e2520418322e68751c7e6aa7c9cb8ea0e3e551da1008be9ff608b80a3e7ca60512a03dbc46946f29fc83be4c681f7200a9b80e6721aaa118a2810fc9459114ee073eaf6601d3d88b40806b9ba4edfca02f63c8e69182b0feaab2f55dfec2847e0dceb5e8c52802c43edc34824190a6c0417f52008748cbf0c252a7436d62eb974bd2b0a33a15456373d7c743b952a680e23f3897f22904cef840efcbe6abe093b900ebaac721194b20ba3666eeaeeb8084cdb8b2c180d7d0a6a9191a2a3eae9fc1eb8d386caea56277c1eefc8cdee7605efe28cd8d32d53c4a98f6459979683fac109a73a52c878a029775bca072fff0e2840b88533ad7b08ce0ccf3676673d794d76f16cfab5346634f0b6548e32c5051bae29a08c2a0bc46dd7232dd60b69bfa66ddb1afa271371ba83ce725ee5700de1f66da0234dd99b8ca21b7760734910879051f46bbe2ac46d4afad0150c360470938abf0eecb5cd0a0619c8bd65b47594d6de315671a26df314fc7f10a7cb04939c89117a4815f2fa4373793462a8c71607044e567bfee8b619816cd204d80d72e9155da7a82671400d1a2cbe26bf87756951dc57a893aff7a1569951c7c90f4971a8e5231e2ff5a28b9368ec4f0bab8d270c6f00f8896deca5347b91fe4a075a7af8387414cd330967b59238e872b218f6de2a0329d8fcb38e5005554fd40b211a91870557d6d647cf244344f4878ea1fd4f5d56b21f9bd2616e83bf587b08d3c868014c78c5be6c4070b28a864966311976b4f6705787861f53e5379f840d1d96ed4b7b2aea312b1f34214ac5663a8f243f288152023c85ddcda052660f0b3bdd3e48a69e7703d53d579fc5225e7ea0ab9c716ff8a693494ee85e46ba9b0a2383b2dcb87aebd42f300971e57c03c963b20f8b59c0ce68dca2892da8d8780272253bb9f3960a1d18e10a5cc272deeb2458e7fa3293cb4356e8559e2cf64d023441b0a116a4b3822b086bc560e1f4670ab2993ba57aa4b2db1a55bcd25fd40e34f4661cba161df71ac414fb46e3f9ec4660bf11d207afea16eedf9cf9ab4702da879313767d399577c103c1559452e8087690df929694b1b417d4a5b367e70852d731399b7765897567418f9f52cc41ab6af07487c0538a5a080a81d9bbe80b089f98c52bfc59ffc3977c0550a832d670b695c5a7eaf880b6f951568365e508889cae5e25da80836ba5da46112b7bc90f23ae0be21ad84c303e19c450d54c081fa3b220460418281559d0eb7c6b6ca0129bdd16b2299cac74c27ad4f02f7904a3d39e398903e19ccccbff8f07db4d0eaaca8568baa49c056bb6139b594b8c02f9e4972c86e5ac463e0ade613585c2e546c6c3ae9e293ef9124143c19c97aa030f93af932fccca44286143691d9d9f6e1b0c7f9ee73822104dd0e4251b7b400a0ab3bdae3a10605db30b88244fb252405767da5035ccf292cffcf09fdde12809e94b96f541aa6324260333c6914afc72ca1bff2a3dd5a057ffb20b94d008850e -generate_ring_signature c94de04cde5cb97d00fc58347c9d43c5e2ff19eea4318f3ce8eeede86e590181 dd0cd265cb990195247bcff884ae711d6d6e27a552da53c3871f9f1ef313119a 41 fa1a0c591987eca8d52794fdfcb34be618b911c96bf95dac8383b875200f160b 5df51ed1944ca217cb3aabdf05f9e84481cbfba603863402a3b0bc3032345285 aab3f16311418e85c428006e09f6ab4cb938193554381b480c2ef44782ab092d fa2d0b52bf1b930a1f0f482813498bd527ce090daa3f4285477aab43cd1c45c0 954e9aeee405d40869ae3c3ac2ffddae7c00e08574f8e28528d9efc994885f51 1ff131463c64c2c3a1b7af41435e737718ceb11fc26df0e48f192cc78b49606a 05defdd1653a375ee7b758c3d7bc997beff91c486ef9acd1f7407599ef9eb509 4db7cf256f884cbde45e0e580207029b4f5fbaa85ad7464b20e36100b70083a1 581797147df7936051e989bb4881f330344d5733220ab185292371438a9a7660 606177bdf0e2889440bc7c26fcbd0f17c5a204d1c20205dce16be9eb561361b1 94a10e3d908cac01a162fa1087d08145fb3db70cacf955bb70d51bd0c0cc2915 a40e979b15e9b750a010c4f924a6da214bd0a0356aec01f392e676c9f9d42a4b e4e6ba7de1cb51d81bf38ad8e9bc3ba7e3059b170566e6188e5dcdc197f424cc 0bb2355397a31022e06f6edbea574aa43c63a063f8287fe3de58a438193cddbc 1994a036f18f48016570cd7ce9c56458df51f532738e2fd45fc8e09dd53ef228 0ab9a2f03402f2b2d2d7e850d682cbdf1a82b34566059897b754188b09f84477 338c19bd0bcbabee027bddd1b819ad49d5c37fc77c7cb6d6ad64862e23ef632c e91daf7d75e20f04693b719c473fc61a15d3076e1e372068316e0ef056be00a3 f547d83a8f49db95529bfb04880da8d1856c39a35ae73070dc84b3694e49c82d a3f131eff367e0c26660772e1598e71a430daa4d7ff274e3c8da018a40b7f765 205875d30d48dbfd44277116bb0d80b478d1f8a0a83ab789ac0404fdbc03617a 92c1fe822b784d0dbfbb2ec60c8c8e9e9a797b807648554bb5105b951a3d9d6d ff72f514b8adfc2861faf09820604be178b7daf92202088a85d2d0e66bd57edb 223c5ca852fabf656ceb333b5bf01d7f1f37f2566b54b7c4c29a86f18d1b8ae8 14a1430cd78fd2039ac5d7df2e3069b24cb36b12f2576771da0c1911f49a273b 69df315a3694a8a96963810c498a765f95eb4f84478bcb38a8775797de768e86 e94d3267a6fbec3acddc3ef82a82bab6ee8b0fd4a7defb1332b936a5f1013fb8 ccf5a0bbf7e6addeed9cdfc3599a5a94e47c5faa02a3ba4671a4c008bd403f49 45f5cd60d7af60fcade4173bc0ed1a36091f206dc28e13844a16c675422ee1af b1907d4d139c71715303888acb530cf952f70e777b3801a0f402f2f0585d6930 ca8744c0d9e716417589570bf615e2f97e01a83992b36558af37d7ff28714523 fc201c54527f971e930f3d35bdc20ced6a666ac12d851398a7fa9f26ca509f53 6efe0cfea913636b65c218d9eb260c3b9ac83018e18623135af56184a418b565 e35ed29a37ee3eaf9d9c8133a0ffe4d0b7b4b0358d5889ac0b3dce4bf4bf12b7 4f336bedd6f2044ffb6866bf87bd50c3bc3f0d814b18be816f51beec2f29f1b0 eb40611822d44136ae6779a5586246a2ded4d005c83334cbeda1aed834b7f888 64b8c042003d59c3e9e1c4ae0d2ef6c1d151e02c4c5e7c36c7aa5b59b7a74357 e7ebbdc1ca40f25c085c1d573b4951e9d0e15cb0c0ad0026492cb6b815ec2595 2148ddd29c481bb590a19dc5091847a0a86d1a1f6cde6a8df6f594be8dea66c8 96c16cd72fb2150c2371c2313d1d24520bd660b248c5cf3ee368f95d7257cc0d 9cf1735d67ccbd6c29457094d09bbe4b18c3824e97168d9fad6c3b97a177f9c4 25cd26b4ae877a3fc7251903d65736fd36329f066ef5043d715032b1244cc803 0 c9ec6588ac6cc707a200c7d8d3706c74be5aced3efd7d2c124c9061fef31540681728c1c84e7ab3ccbd43e21aaacd4471b4cf605ed42e05919b8202323481d036d7343303dd58f09d36e2985707e0f41768613efc107e1ce3f911372b6dfe603921aebba2bb8c0cf6febd785b6ebfb1ed5a1452f2067ddff8bfabb38c9427b0db0b64ef0702e53c995fe7084407a681e692b689b43b900c6c7980ac934a5bc017f0fcea68b71c2f398911ad990faf998746ecb3a7139074a4edb72f0da158b06ac869e7dfbce2c0bbb59392dec43df6ca1005fe55070641cb4e2f9a098449c0d18db46278dd1dd0687d8c8df5e9b4c8f2b28cd304b32b60fbe144b9655ad1508391655bae6fbbc044aca2768605dd2fd29950eadb9d2595b0c85348eebf80001a975eb9cd8c1d7280d8eb4fff5a8583e85ec03b864211d518e038c28b04df70e08e3e5847c10a6c389ae0fac0bb738948c702df6643509541cbc6d72b0512705c026cbf1958a90f5a5c2afa868f4977aa0654af18efd20f6c23eba86a8cd3207bf8f599c70dcfa6c62822bc97a84f3e2c555027c444e63448f7e390bf9d9400023df81a5f7286047cf7ef6f599331360559011e6d223ebd7494e3f685bda7e03d527abda260db3e74c8d3dda5dec5f6fc86d0e06b689df5d5f33fa47b49ad40ab62897bd66b9dff75cdb66477a23b99e6e6fce13329a25e4f8963c5482efeb06f3f211a5ca7e3197c6959e56256444ca318b52b25547161a750b754663a0df073922158d2201a3c238fd558cad934d7dfb8b5841d2b08ceaf24f6d3f3f8d1d0235053c2dbb961661cd49ab3ea072c7d5323b73b9742d0a413296fafbc54f860fd6041859f72d9ac9335529aa0f120a55f56c41df4573ecc017c3f293a4219f09ad36ca72d7c9565730dbd4ec15c3493166bb8bd24ecd05ba50e19baae305c405688eb9c2b5ae213a998658aef5ecf98344d9ce29d1a4ba79a58aa118929c830b4a0331b6dd837dbb8820b33e3417e3479225dccdb72898e80b2a0b0f02a241097db0a9848553024adfd5a6ae0dded46db0bf8664e3119da60523b707481a830e60bbccdaf4fec18e87e5594c4e725d9c86b30f42d33a6871da9592c49284a10d39c97e208850b25043dcf5fb57c85e024e55361c163b0a0d31374ac92e40db08d8a149f7f7ecfde9b39afbbac78ef9a2f62015ce7e0f7c8d1dde4549fa896f0dadad0f8b2868fd06e7c5dc20fa1e8877eeab6f65a4c73edcce23921c989e0a029ba93869de4f2574823f1e1de909c5e1f773a2dbe5102223e61fa84ff70d120ce4a46a450a62eb338f2fad947ee40b66d3586c0fc9b760677cb0a64a6c128a074f5e5138f2cb639a12269f9326ac484334879a31d1f11fb7f7a6b6ee46c6a7053d2d30f871231942d5660c3d3f114406c683126b64918c19d5f90f951710f6024708e2f569ceaf166845ca952c093ce5b138ae6c2f33d4f307b22c156cdea00063404304e0bfb586a5d4b88f70a3a271e47de6c0455f330e0c7b94ac93d35c0309e9cda85387821c6ba956bd39b1c2721aee2ca0fc1251339f2f0a45d0e38c02faee3b54e3ccf5e6770560b80308395d10355dfc3caa120abe0004ae628c1e09c88f6e10d0431b1a3e90762d8f138ff7e54e97561a0f31ee9420a13f17cda1044efd7bfb956c812bc6ef656077557b7fea2d5d0d47bf18e2faa5fe3f393458096271979951f4307cc8a69d1f2eca3e0e386f159c9b43cd28729900c0646b5204f9959387c4d54bf4ae40b3bccfe312673530d2aac16ebbd4455f675bbd660403a6a8cb841afc7c01f28536762bc1c3bc57f0fde5ea248822a88034acf68c3f0ea42d352e5bcf51c5ef62c17bf0ade7321dee5de65a95c0161751c08b2444f00e9039b031eb277fbc08e45b8c232e87bc668b0493bdcf0d7b1498552c82fa6f050772eb6e20a111d6bf6caba0eac1958e084dd819396f08056e5141ea74cc2b08ffcbb6f7b2f19d8aba02fe0b09f6befacbd2308fb3fa91115975b6923c006e02dcbaf76b7a4c2efada4eb81d37c6a2be4b3f271eed710fd13e4170dcb9fab80af5f2bfe6cab56d5ed2528edea9256b34620c363f4ca9f064e4f937e26a801d08866b616762d65445b4d11be160928afae8559bff299222dc532486e8bc20a7008928b10428fa49b2b6d03a9914f9bf0021f649bfd62b32b4bbb8a85acf68390997440d34c1bc8b7a3bad0c145433ddfad62bb5dc5a79ec7990ba1ff36024a60988c05ac4cdc7830ec062934b4e4c60b1664006f1a82e75da8c87a8f92eebd10dceeca0df6af81a48656e888b98b5e18108e5d5d9424f448089dd4feb9a3b91025687f6584d810484620302c7cd27e1b17cfb3675de2f5977887307a0e8ff73021e663c816ce008cf979eedc9eefc8a252d577bd187c96883dcd50da771e09f02a4e9d6a7df61958926d317afdccf83f32d92155320d718f6b2f3230249ed73018308ca28a0a5d0c517ca50682310cca727252f82bb852c1fb79c883f4ceab20a2b3b8c3421b94125c16ba95a9e551e9e688a263023435b2c66db2446046a5404bad0e48ab943e93d754013ee4de57a38dee3a9777367dada4158ecbea6b4200431a81c33fee4bfef863a9335b086af8872e06dfc691c04e10cc15b1727344709e2b77db6f6ef04dd4aefaae96cf568b56dad2ef0ee91ba79a8800d8caacadd07f8ddb4c72519a34b5688eae4d6c99517489a0acd3094ff34466664664ab0060f92e76bcb678b52ba78309b89b5b27e082a02745f60adc650fa104b66fc2f5205b2b87f5cb966a73ac301dffa8c50723b433cf222bcdddf4c62bafee78c3cd30866216d56570464d76f2c45ad4d262363489887589cdf787dc66f405bc6a66c0b85c9dd5d3418d79ac8ba6e6ba49cc2a45807a4fbd78183cc3538b2a93ca9e50d964e451f9ff097c9ba6ecde12ba25d18ee0917fb5a337278d085fbfd17466f01aa5e0a018f804e6cd08e22835b48a11ab8d08a10dbae6a511f4e1c5ee7c87e015755356f9fac2f75cfea0012ff04fdfeb4853ff200d34ccdc7ee62ef3b39f50471ecbdcc0cbf1fcbf7233de10b141a0b4fa167932a22e7c94712ad0ca9b00f08f5cb26e11765c1075a0608afd6e10671dd944cad6eacf44834ed32bd1dc5e9039cdf55334debbf5af0b5d7716afe8885c4e447538a051e8f355c3768af32a50821cdb3dd16c6cf865e278883281ab0813c8184f7bb373b09a8a416bfa9dfe8052f1c2391a0152c43c0b16fde0e74a0c8f574457128a9a311421fe485621fd80969e4d2b8886de5954dbb5ee85c8d212c74e8da355dca69405d8c3e006d509c030fda69ca35bbdc55f8466aa5d91ec91602afc111c2896e9758240df684cad008a7aa75bad2414cbbde2a0b1203fa27eeb7e121e726db0d5116d67a87ba12a90c8405a3fcac5e66e154fe1e4df99f2cfb5ab1356f005310739049f47181d4fd06e1f7987587327d506f10de7931e5c08954d80848b53c4f5986e501d3ab59760ffecd7189f0063699e6dba205d9a90b7a91be2badf2e693b2f77f4314bca2160b1633d4f09375d36925856556cebf3353a477d6085e2f3aecd4381773bbbba70735480e0dbd1ef69760c627e2eb9dd0a772111c4d2c9cab14e53a41d832443d03f3f6c3a5a6ec67c3286b230e406820d245dc4490a9007ae7a92455ea4c80b900 -generate_ring_signature d6ebf139a45ef1fa4b4be6a9def628430e9a0ece08b67e60646b44d7581f62fd 009fd2354de45de4f50f4171609d01f3af1da8c4a966e6613656ab7ba547bb4d 2 6f791430dc6089fe5726dcba4999c2f277ed2515275152c3615756c85ca7fce3 fb5ed1804e5b50fd5e0faea33284e7b91d9d881a69067cc49560ca97e1828d7b 689799e5ac8fae88a1fa82a03c0dec56a2ffa69f983d929bfcda6ea30e735f00 0 1f3a9646c1e9c1a54114bfe70605b849d0001331d297da070a524f20782777096d5cbe0511d7d855884e5ff5b1facbd0158ceba7deee8ebd5dda35cece103b0c416ecd95a60c405dfbc05331d385ae1b53cc06fff212db6b55f57a7e3ea5ca04fd0be79230ba3b22bda44012df65f57183a9718aff784cabacd38184884c3009 -generate_ring_signature 0ec44a65dc63613b9be298c6039f1c13f58071e1b8f642259fad92b82e70ea6b 9c1b7841db55bf2d432d77363bf8f7871946d1028ac185176bd270b9b1c8245a 22 0ac06d71a291bc9cb8a65166f6e267aa94f67345780fcc3a88990a1fa5ddbbae f4dc2655b78a75a5c02d1da6fadc0e7df8d10024cd2acdfa75d6b926d9df26f2 b898a4bc2437e9c7aca7ae42b40b9f55801251be6a22e06b35cfdb9b519fbb07 5fad8c5d05bbf6e210b426f1c85cdf65c610189659bc1dee66cb48e155b4848e 06c86b3de6e5b17a6b18bcc3bea96bba1f0e53011abdfc8d983e7128686aa565 1981523626d0ebc509c4975cfe7855f5935518d53bfbd8020103875bb04c54cf 71c3b087b4ac47bbf525fbe96f827dbb8ff8cf477ec15e188df1f7468f12319d 76ab284bb2b33394e49a7b9b6ab4eb0fa9175f8cc0549099afef7a425cfdee73 035ae37614df557e2879632c43dcb2a6e731f45ed71d4c24813a2fd0b33e27fe 4619e99967c91eddc5bbb3326d8bafb2d91a69e99120dd426dfdbf0fece87b2f 6c25581aef5948e25e70cacf2143b8aa5d09c4f0f71d6aa72b8f97acf504c9a1 035708993aa26e4a03188068a9053b2c4daaf5e7fb6fb5b63e4c5b3d5fced851 7046c37e9f70f71033fd835f432553e867df9ce1f42bf67e883fd1eaaf6fcd91 fc9c1fa387d94e42b000d5196cd36b4d9ae84e1a4cf2de91dd15a8e9c655995c 80c9ac968a6a22f994705609c7f829f80e9c56786ea24c1048814c9bd8ae39a8 4d7eee539bdc72685a3e4239f78c67756fe3972e1b88ec61f5ce7de6e90cbca2 9f080a14a10352a412dc2067d03db1e23a55fc00e1c90ebe2e3edf4c9067f8ea f124e82150067aef668390263f418cf8bd5ce9a01fb2907e6847231178f4cedc d28e3033f536023ea168cc178fd5c9a9e3ed795f8598f7cfbb973122a860e43c b501e956550a6e563a89978557633f9d61ede3fdcb4f5f73b10fd98961369c19 a7c9d688441935ef112dd091d6f934bd055fdc508e39f78035da02849b5e374e 5585b13645eb85ee005065fdebdd45914a3afbefcfa4d5aac0b2807c21bfe0d5 ef411cbe80bc6bdf3cf2c9ac140669cccc0c9355fbd42d32c545a67b3bc96a02 0 aca697bb06144d55b3906d30d0d450341c71864b99d9f1452798777cc484b8025fb67b8d7d930239fdcdabc762d1994b89a91c541ba2b0ff77c3d19e7fe71b0cd4e45a89681a1b412eb5d4f7a4ed374ce365282cc3856898961fa6427029040e451a45d6508293158dda5d7aef9be79f430e126d35efda48a96c2d5187e677097527728328fce97d5699090ac1deee665a9f0bdc73d5fbb2f68db89c35e0120beb46178a1098248a484f2a8bec5d839953069258ea50db97bbcd347f206e1d04c92421b273f6a14cb45804b6cc406cfb16460c96d29d070d1e77a30cff01020b4845641c2cd5a4289f86dba16dacddfc05fb087454199a68d9c4ad448aad9f00ad13aac6955b148ca394237ca8d607a168ab1e9788357f6b8c71abcb5afab009ea2573a4fffdd9c77bdc93847645bf39fc08f02936efdd737c6ef9f9fa697505f42530421a12759843125ab0ce55f491780690a697bef1078afe63a7ae782d023a310c941fe87d9df0a4da465403b88d10e228cc540578247c7dee57fb598e0f5482a1b00f1f071ed43e5a9b5bcd165a8af1ce8fdbde6dbc2b90d2c8c97ce00da2cb537811b79827ecee21ebeb477c1f0fd0301817f28774ca21c812e6f3140c0b651f08439eb7e5d68fc6aa52fe5c3466103d65f65f2e60a303b004ed8a6f0b3e2bbc4e9c43ce1873a0c6f15898f9b1bd465c06ce483763f862c45a7122c100d37196d463d4a511d3323490961b9aa6a98379b5c4fd2de367229e800c45e201a08778d829f529d38bc23e1af382f8cb8b5b4b2f8f9e2b1b954ce99c4ec2ee07536f04960af4ca938700b84e8bf3663dd80f4e6eb4e5543535e417c0b690330ab17413468dfb6258f2d6b8f6c5ae26ab902655e92ca3c795745b5799a0436d0a0a67e9f8a786f608669b7f2f3ada944e9db979512b3f89b64842533545f611094f319e0f8c434de546649b8213b241630335201e350cc9c49374c4127b96dc07a1a86c5fa76dde1863292eb7bf7fa33d9f254c63865afcbb0060c6189553980b247168b2cab577006578ee58b1cc93c53f31a8197de6b908734a84be32a17c09dc585f7a441016186ddc8b4026b89ed255a48c5df17a5257d83c75799b69660e64873f3576357d10656e0f399d334a5ce77dd7633498535ddb91c399050f63082bef1f63d923bf9fe6fe2ec7767aaa8dc1d01c9d396228e2bd318db4c1b4490e91d767ab0f89af08a85ec0b1a5e5f5b664ecc53f7a67bbc9861f4cacbab7b40ab93ce0780b42ade40849e8f51560becb47aaa2c5c97c30f9defea8d4e86d110e62603bc1ae24d41171c1a63af6f18edfa7949af088d12971059156975be9c40e00933b9cf66f8c691d29045070532d0a75d3c155cc6c5c89c811158fe439250038ada81184e0f15ec57bdd814b10915ac3dae65f57a47ddd875e8d9ea35c80094b89a38907ebd6b5c28a2aa87a64e4994a4799b1ad48206c64c9ed62393d510fbb102a4060b0e25f64956b9b3883b21df88500577b2632f3b8cf53aec2f6ce0f576aebc66d943b71deaac22d455136e024d318f6c6aff081a091ff6dea200406098006c1359b4d8613cb8ccdd87a6da93d7b5ef7031d54ddc8afab77d134260cd7f8292836dd49b8f0d44acbc7ccf829a229577b928adafe8331010e0b90e70017dc056496d6b1caa56929300911f9ca8ed42544fd7a51f48f777573e54e060370f005a3801734c8a3e4cd783f55a6204573a282ddd0c3957d49bc30d71342098899eae59702de18275d2a6ca2e9d6e2e0c9102b87ba2bf8c09cc57ed18c43092940725f044a0d73c7dfd04ae5c5c12efa99c47ed0942df7dfee0e811e4a8906015a70b6d9b4f94b6fa3f35468433d1117ee04937575a6cdd1e9be0a11303c0e95dc7ff3f792cef5922a19d35b90228d2c020830ca174a38772648d4ee11f800a8464ce9505e48de3c5653bfbac11fc405c20354d34e745f90738bb5bb200b0c -generate_ring_signature 93493d23fb44416ee09be43865923d0e1dc1066d56f5a3d0de81813af3ba0dbd 7748c0a5f3f87b8261d37a2fcf576f3641db82845d8c9baab1255a025e947fa2 27 2d160aedc903d73ca9290d7a6ab88c0e9f34d8d1eb85ff0c2123e1e619aab827 7bb969822d0b461e0f71f8582559be4532c9f9ba4b98a3bd8037f3ae2cc99086 3973974ed9fab1a8bfa2bd46370a22e6aa9ee366333379802af5647a360dd8c1 d6b7ed18d47a17ddb488bbf95b3af8435f2beecd767e1cef3bc7fc0a4ef7c4a2 0b30df77534bf83ade55d67c5525d900a8a7a4e45d22af5d3fa8036ab3ec3a28 7fbc57d8de03940bc6ee8cf3f5598cf04672d58cc53793c1f62e0f193a2cf264 300807e7fa48d0b41448f7307d2e53d080ca6da2dbe0b49856acbfb5ed7e126b b8caad285713bf9beda3f580194fd57152f67aec0e70a93c28928f5f486f59c6 a07af8e32900154e837d3b7b74d5eed2eea6b5fa431e09f4927256459a381021 73aa4e3f354b1059b14d1cd48f52502231c181cd06d3e377e93ff7d7e4493348 ce6d6cd79f36aa44b40bd05e6a087cde2fda0d82d4cdaa6e2b0cd36b0b3803a2 038030ecee860ada3ba07323cfef9534ea0a90d2b5d30f2af5cbadc1bef72e60 dea728c4b55cd4db70c5c69438fd75ea5180675a5b9d66c02fe1a7199e9bdb69 f27326d480de4e3b79274bd526dd79d24f11ebd046b9a8c5b376c44061d23a9f 136ba04808b1ddb0227d549f18e8322002c8f9e75600a3abd5c73559ce0260ea 601eed6733db13d4cb81bc73faf4f9f5af74ce56405f4bca0a048333bbc439e9 3c0c6f776ae4ee29c1c691d0b57309ed56b96a31a945079f41d3ea89e80f32d0 7249c2ae5af899b9283262f07f522602c6d1bf3d88ed92e0c1b9996d76f0447c 9518aee525652a3dcfa5a37d3844e7b2bc051db5335ea438673b94b01a04f8c9 2843e804e929b6da93b04026f44ee744eb364f20610f686d5a73fbc43b92663d 6b5281b6cd170d9cc7ba7bb56af507aa3dc21e816f0acd1659a908ea9a416366 ffe9b0f145fbc6ea4dce6273b27e9287654cfaf82accf33885fae7b152fca9ce d1bb3f05ab3be0fdee933d8492e1c5cc24e7488e44c4258dec75b4b650a9892d 8203602357f52070b4bcfc3c5240704ed87ecb93a0b4812512e3db7d9d6ed061 1568a18b7fc569e1cef7a211eabd5da6fd118cff94cd726d1b4d988598a51117 0080b6bfcc6e68ab3bd176987ec4f0ebfa08e2d5bd6901cbf24586bed9ecb4b6 6b608f086d9d4c67f3f1daf684659a389c5c9140c7b6e5ca71ab5b107cefdd79 dd1d64ade0adf36ab84cb537861c99b79a6d012ae1f994be411baa76db298b03 0 bea6d698f5433c70952cc7caeaf17ddcb69f63d0d7a4f67265ddc34142723e0965c0df9366b2c4e68dbdb9ea20baeb077e7817f05c7177f1be1f6ddf8452be0bde569a5578ea72513528aba714f00676622847818800c503e370e41b8150be056269f26db3dd87047b8a482963712f15089f2ba6c1aad59b44947fe4e3f25604a9e448fc52f5a50f47c0b00991addfe9c2b1e41ae2eb813065c181a54148ba02695efa2cb55dc8d84d1f574d03d9e0295229e6dcb3b9eed50f130e91065daf047b4c8350a02077a31d59c5cf73cb70b8d65a5efd6b66beea277032a4482ed60f946d7986e1e1793a5cfe16723af882b0e467d2d8d8c68bacae688dc5b5ff2a031c40f15a610b84b55b443ae6f7bdc463e839688724f9ca5764b5822f2b4ded0ec9ae74df3ae9b855d8da7c0af915023f81155bd602c55ccd76643b79ee6ee804f51c0dff6a13b4396a551ddddf56f87a908a356aa2084c241dab30ad1f5cfa0a5932eb1e3f268d29e8b70c2d67d3a100800e602818ed1582378b7494e5226b0d439ef4bf56d18abb171730090fe6a8e2077baca9588eb8b4568c4acdeb09cc0b9ba9da84092772b73a02c10d68904e1df5211e2314787cef870eaa08590c010c28cbf16fdd74e52d987b67b580a3b756c9bfe9f972874814ff00d9123c774c0a1499ad079044c423a4063a746c221b7196e46d0ee3cd727dbbb2b54a98def806ad3b71731873ff59f9322d2aa05f8d3f4bcab6b4274253f3892375941c72c00de58e2c2b80002ba3c8bba8bc240d9b9f2c28a9467406011604037c2609cd830cd4edd516946affbbb55050c4e263654d1d766d4f391f2ca76eafb48cdc3e5f0666f058e007d9c96558e3837991d42d69528900f804780d8a7ccdae4772cfc90bfc13ffe902b0312568990758a5e4526f4a623449b1c51aca4f98dbd21fbd740a665ca4c724bfac62d510bf0fee0daebb042cbe407b7c9d4bb45f82b17a849803090209a422ae6b579dd14e015ad5b4730fad267b0490165398ca0538d638d70988527e1fc8967c07bfb39ea958836c5df6854cad6f478e468ebdccd9c7abdd0cdb41fb600c43750a50d369c502ed069954e1f3eac8379079d2f73846b6464e0f7711c2df43adfa9a4b5e72d39bf1c965963748e489bf0ec0827ea2a43c74530492c34d31c156eda9fe249cf663d391d977f93d07577b5ee9102c9b6718ab0e0c91fb16c6e2be9986fe301f09e163eb13e583bfb7c660f31017d2e5b67337910b18955c54d4368a441cb3832bdcc064804129a587d2e1f5f1aa3fc48c1877e305793fc5a7b6e1b3c46a5b6dd2e1a3cef701e08d06d6620009ff17d9f5f8bd310a3915fc9620a214b44ca4330ee16bb3547d21c9071afb83007181ff63ba16cf00451edb71ed22be3347ebdc53a1f01880c7d3fb4a57df73bcdf213cb557b41a007e1a65e5aa0f627124158d05d41afaa1c6045519b6bdae349a41d06709827507ef59db9cffd08bd51e635879e6ae1bb66cd53995bd84181e52b3041afb4a170d8882369e2aae3edaf892e6e12cafde26862447a91e518d4b1ae26dd68d5c6a0060af9e33e4e890dea8a4a791ccb27bc575b6661b57f70434e070c9c410757102d62a4222d814429914e376099387c35a613b2b84f5b88bc68a10d00b88cb3805af4eec30920b5361d68fe562109d816f08a3ae2f48292f0c37f72eae0a9c610c91182ce834cb8640e4ad8658fdb0ab0c5607e93eabcfbf6c44997ed09d004f07afe14f05f5b8defabe47244bcc50a0db18ca66db64deb8f6672a5047bb76060ab1d0ac9aa708743f36936433f98c18dbac3545d36f4cfddb0455951ccd0db1090fc8adb82b12308243fadb52961b2068669e12b73d931a34d7f9b1b95d22c00e6243c01d061dd6bb1f9b49b59decabed3fc2fe9c643d61e8b90f839a4949e90f35032aab34dca4c06e4e3832186a8dfe05551a7c85eaecdf4a0388a69efd2204787609978f997649e6938c1572ba589c551d2f63f65e9132053fa5039216280c59210de7fe150f42777ba6453fe65ee80c9082e8079a202d90d2090278dab6032be9732402408d8498f471ede447e4a08bf543500e0850b8946f949450a2230d882ab915e7b45730af26f843949f22f2f42ffb28ee075358c3bac90ea2f9710d99e913986f3bd7302a8c843dbfadd16352df4aea22da55e79379457fce72fb04b2898ea995c9b73d03a152f5c4c41940ac13fb049600ef29445cd812d8196d023f24143e72586d4cfdd1273cc8d73d9f829df19938a81bf5c3b5cb5d1266910b1d0c65d258e7f23e0742a53d470cd859d82ed831c32c946ba7df9e8a956707031e3a5480623301c3f92bb429e5aa8b25b24125c2526b17afab1b74493353c90d3616d12f19a38cb37db8e0bc5b55b89617b85b3343c8d805f3e102b5366bcc04 -generate_ring_signature 47a17eef9aa4f45e349474251fa7db40f4dc3b2d9cb74a0f63628730e4f087fa 67206377b4229f14b901ee725025ad9067500f80708603d343cde543f6220070 16 b9d6ae2e4275913b39ded0e04f23f415e21d0755e6f5f15398ce24db187b80be 7443760d12012330c27c0d958be3f5bf3c543ea087b2e710f1df3aea40a3597d d58c7950dfa2cd1e5d5958b03665844c4ec07d3266238af73c026b2df9c9d749 22c2ac91231c9618673ee8d8cd01a3fbfc915f6da971019e5ed1c04cb848eaf6 bf376c083d6e0298d8e0bee38fc8a296ff9db39661ff0acac51a7cae47a528ba 4d84063c54fc35ef611c69679d6bf8252aff307262372e127ceb6ce28a4eb57a 191d335866139d9a9d70172dcb623d9d0d589bcfaf29d639db9408105350a6e7 16d1388ac89e6e7be13d79c597bab902ae51c727f4003e0e32d6a9944155f5eb 7161b56af7340827dceb9215c1ee38f21479d7deb8ca9aad4cadad12dd190762 eadf42743e1b0f76badbde7f1b5600c599eff2855e4af3ba1e832b56c745121f 0113294d12228749802c3502457a95df6875abd7dd63631232e319cda4ea7176 517b3628d5b1f6a781dc2a65084e515d45f745a55acf671b478d2553006ae2fe d1a16b1897c1379411ea2bad7ff86ac19c519141174182de83c372da21e04676 9c6e206bf2d21ec5f36fdc489e51f9748d748a8064e5ec0ed21e7210b6075d71 fa2fe7158dfba5f1083ff8db88ca7510fa17a08da7da486f3194297ed4ed0afd d467bdfb269e9f5e8c72cf6c27b5c7eb4c2beccacd7c0fa37d22cdbe2d15807b 02584ea85f70c3ef4912ee658341e6b6dba3f2b9e3cef23d99ced95aa50d680b 8 a40c1041eaefc3bb058d049d1d27265f5010428ff77c469fb1540e8c616a380993741a55f853fffe8f63159741fe6472bdefcbffdff49467008c3b45f45fda024a4cdd813b7f9a83328746863f0acb9dfad4f0a79e493166ba0e81f95ea98f040ab85093b2a1f18d58ba5d3388a50a7be32ce5e13b9087b9c1c1cab2be98f6056ac7bc41b7795ab46858ce7250ce69cf567d17acf4e0d885e898a26bc0d8600ed21c79a9642e20db569f02a5e5762ad5fa7dbc1de01cab8c5dad06e0c327e4065ed4063b87d629759d18e5bff5eb23b184e118e06326f0ebc015aa8c082e30032da8a9f908689b28b4d68a2faeca724b8a3fa4a19400064e88120aaa001c780ac0290d023b71be7a6164e50bf3b40b048514cf780ceeb1b959d0c5419e017008be397a37e7125e3e0fe28dceef3d0530ecda6853ff8e8c9ae8be5aba50a48f01cf4050224dcf77ce8c641bbd98b61ce2034753619ca96b99ab206474932ff2091ca4b0ee5efb1e722e22efab6392bccc03436434ee6f554ffbef847d841fe701bbb7b4fd8b3f669c2c463f290f492cb406d05651d0d7ad5d5d0c59f21d07510a0ac2f7d8aa9f17868a69aa9c27f0aec811de5126ef073737d1b3d9e202842b009ec25bea5b571ddbcf5ae407ba4b5fbae1b25323b17bc58fc17ea4bb439e6800a661f99fbbc3a4b8f0b5c7df581e59ae1a48fc5bf973548af75da1891d05a800a4d54d33e57944d29795c26d25e36e7f64fe959d23f1a01c794bb7e7eb3a210fb4b280905833d866ba86ff5ed90248ae3920e338bfe6ee173fd59b3160b84a09de48bef6ed248ef54510ed8063158cabcb1cd1d831e24371e1d8dae3ee25380d068e61dab85ad6e96c412941806ba9f3cac029f3f4877c904636443063c716077073c657eef097f972a81cd7e663dcc05538b6a3dffb5f17c10478603e06b60a2dd44dc881914bd8ee321e3dbef5005724abcbde7bbc013244f32463b1edce02eaf0d30a408b981ed8f58abc6e3e353737e7e9d4ce242e6c3b9caeeea9bbac0efaca348789ac733b1cf0009a18518f7bbd9cdb14a29cf7400387b395e0512f0be046d4e16b3f796f938016a263535c43d973e6c687405b52370627af9a11a304990e02474b54a2d999aeb53a10ae7f0b167ba9aab3064d8f5c89384f71296803cd71aaaa0d408ac449ad47f424fc15b6c63eab1211a1e3949e1a1c575047e604590299ce3e4ccd17484998e3dd92570d9487abd52213eba890b810ce1f16a60763df8d0229fe69d9f44a4e789ea00f84dc23256de7075f989a9dbeac670334072e1ecf44994057baed72f5c5920de737ec3b23b37397a1df52dd22e2c0ddbd04bbb10a48b14fc757eb4b95f72fd4a6f6191a950a3d2bf83aabeca10f7b8fb80a461d1d663d78e579e24123448beaf5dbe636fbc5de1876234e84eada0fb2290a -generate_ring_signature 9a7086a8ef73356a7a113ae91f791ea57a32bf63cb15766556218bcb2f7582fe 9eb46385239b056e240bc6a1180c51a025b0837da355c5d60055da138d05cf09 75 d716b1fb66fc9eb0d801d13e48a4795600b8afd004cb31bd7bb69ac7bf26a5b8 facaa9a563716b9d22e3e8204e8c38ab90e4329812d9bf33dcf49322105ef8bb 092504da28d4089cf7fe33cbd6cdcfa5ebf9ece75d42805975564bdcccf34497 893aac484591284d041d6cab92ad3a0e187834520d90b3a6686276070ac380a7 4e8670484c0779ba9975ac8bf55da8ae29d278a0334b23e670fac6eff5bc133a 7fe32c5fc50564dd44faae596d56dcb817232c1e50385f248e6dabcf382d0d20 5560a7525b10a96fb989ffffaf9c2a20291d0d28be117be81cf7292344c26a89 7abc90f8cf53e7b31349994334ab3388d209b38b0a1747d656b064255933b80d 4b5931de92f69f1272ace927b49149cc942276296ae99a42e52d7d83de3fd414 a2e67069563571d1eb1f16694869da1a9582511ea81b005eecaf1f63adcd779d 23c1f904a58e6ca0d91c4caa3dbd7cd5baae653299b339f9859ce870200db57c 19d779d89ba045aa643e717f548b247156a41d35e7be773d5775eb452587d44d 68e999d5a013b3d945434a46adacd5a2ef18ced42ee0259d949aaea9f06479d4 f8c85c1538d967eb627ec666563cfa8203b978a8f88a236f4fbe179fa791c596 19909e7839625dbff33ff26ed186110acd7b34179b0e55fafc84a71b7a3f7e31 53312455ea259a3056ef247f4b25e63a807fd2fb463b0ffe1417cff46ff4dfdc 3380930deba9c016b036d71ca525f349ddbf582ecc80ed127da3272f6aeea4ac dbde3d341c05dd02ad333c64a9bf35866b1f44da659a07135875cc65b4d65439 06577af36ac3f0ac371d8a2167f637c741b9d18a79afedafed8e943a081733d7 944ef7b3bab2fd90c5a46437b068c23d4a5074ccf0d0a6fcb960ed9763c0b880 01cd86208d37c5512d16d459a4581904d13eb862fbf9e34c24e8a1c3233da0eb 8a10901b4799674e91e6c496deca1f904b720dca6c3a81364bc2ff03a7e57897 339b3dac7330ec98d76e59a6e414629a1cd465d14402c6404c9655036cb5a010 c0f2a7cacf16277a4c84b115d21db048e8180a15b013af3f295739cb0e5e8c2b 9bb9566b6ebb7e992a2e6138c810873127f3406860cec5d3e867f676f23cbb73 3a31153daf08deaee78252f49b658c875d195343bae2a187c16465b4615b64a8 5047c33140837bc18466fc3d1e178179768860faeb7ff166f65450d53c7dd290 bc66bc18c1d6601746fdbf34d4f9c4e7213079d15eb054cf570edbb66ad70591 fdfdd237e667d70a530fc4f387af0473f99a1f2988dc480ac1606df7798798d3 d982e4a92446e08a2e9dd91b155e580f3d181bde1d5f5e69a6cf5e82d35607c5 f8b566a67779c1ece46c432d20b280c5cb8da1184f7eb242c33759434d7fcbc1 6fcc2b37559dd4cfc22abb8b5fa07eedca9d56ade5372f2e4a038ad1d0b92c0b 15ba3fe0679958f07c6deb5369d356b48b668ec67d4832ad33ff37b0abcc629c bc70e3eae472a511ab3e547f91aefd53e40dccb0b030fe7f0f5c382f41337be6 e74bbca25c9180dbf84ee1feacbb9e6d0514cb6acadfc7fe9269d8f1441c4004 0a681694b9181b8b0eee8388c9714f2920c63b46ddc955643d9ec4781c2a820b 377e5e05a7d1fda80259beb3d2d4067cf955e08089a74a45b460799745a52532 198981c0b8d166af3bff21e762bd85cfaa43cd7241d7fa611905b4fcc67d6236 1c533cc6c0bb0f4eeb97e36be210351f39b1a4ab10b7a339e92aa187a6e08679 955803e9b32dbf08ea6a31dcb5bfa460976d058cd5d35fa2cdbd446dd112b3b6 b6eff72cacb508187b555d67f22abb6e73ef2528404ddbf17411ac4510c44fa6 f55361c4f5dda43876eca6af215d10fab43d1fa4f029a370a08996e08fe5610c 38d3a3e8c7d263cf018d96a9c81737c34aea0b2d3092b63d79ca96a2289046e7 5a6424e37427e45b539c65f46272da6b578868ba83a76e6a0ac240c3004ccba0 80d9e5a24fa55306544acb007c88c87a5c65b6f3f814d3138b56d914a7c1f046 7635087c2fbf829094a693322f3f3ade9c719ffb418e5265c0186f4604a294f9 00ce84cfb403560546340864755a5e399e5b2c061645bb7c12a7c99b6098ae82 dcf0053ad01d6cb871a35dd4f255956e50f487ed81b0135862982eb4843df3e1 ae49f8be1790b697b4e8dadcfdcb5786ae07e2623798d494c18f22181ee1d4f2 36cf774f577b9ed47efc0b0b8d38dffc78e6372ff1c3326e336b71600789f487 e30e26abc6a8adc3d0e8a226fee82efdb0462b698b98a6a1878c640cb1cbed61 11ecb444c49b4a7571625fd56f8e30c9008cdb5ca45acbb8d8b716480ca9bd6a b89f7406af985457210d4e406755e995cc07515a414c0c6f751c4269ecf03eaf 7456e8c3d729f835db509c26a5b20a9349d73cd5a59c2f09978df89650f7db88 378ad7b8655c6acb0309dee66ac1cbf99454869528c5cd45b17cae0386db60c8 86cd4f6a7bdd988a848508a368343b0fadc1193111fa561687db3cac67e2696e b62cc61b3a0e5cc7b99bf1107caf5d810d95ddd4102ed701d862426639bc3735 01b43550d9efc0413fededc10d91ae556a36c29151bf36e94c391f162ca325ca 75c92f49af4ba741ad911d40d2c4e021927009b71602601d9432468069caaed0 4952e76de4f8e470060e655a18c697523d7ae0563ed61dc70a54cc3830e3c988 562ef593d5dd96f709bb82c1b59ae43ea34b365a05026a7344010abd6c419fbd 4699941b91092f0cf560e7f9f35db0295f952f11c5821635676538495f663201 ea0d5276b2de554255c56fc0589e3795fcc9f39234b354ad8cecc554b7b8f6bd 685df7e6f427618f22fd206f70ae5b44d71f408de837502005777fdd92bbb4bf f364ed496716cd88da9cd2b9b404f620868e8195de8d2f367f2c4c80e708a16a c7c368adbe6409674f1a2fc603ae4fdc1a7cb31ccead9b6e1443a6bd90fb35be 248ec8d63f6c5cf526819521d866ab1bbdb6cfa84b0e87dd8a7ad7d1a47a841a 4dc9ca992f062b7a011c372b3e99f87d54de6883627d42cc97bc7a576a551d1b 3dcfa728ad76ab77e634add3524207937374f3eeee11a566b5babddedb647fd7 77fb0e227ad84997ef3a5f33c800b7ad6033aeb0f3eca8d688abc8008dba62c8 b8990b5dfe159a6db927724d9572270071593aadbed7d4e9b9acc4a1458afb70 72886ce644da16d21a4eab8b7d00dd65f639a40a5dea3579ad7923cede155007 6ce176ac239a4afd1d9f61f7cea9286ee6b5a6c3f5d8c76c02a3e6c59600deaf bb67aec3e0b94a73aea72380edf585f9c47232f471b81ba1725beb05d6352cdd bd4034fd4c3d280c619ab29786e48bcf7bcc8e839941cac09d57eee9a67f0cf1 2b9967f5aca1eb4e0601d393b1989438c1934a0b0dbdf515637c2fc7c9709a02 17 f87b7b46f6e5064ac532e94bfa9383a8081623314eb3a47cc7bba298674a740fd8060cdb214b0ae414845662fc04687cd879a6d38d42b47ec048898145e68002a54a79ac4362ce06f7d2277648302a21e34382b7cc2c0e023bc0df5bf8e1dd07f4dde27a71085f9168c403c8f939a304c5f38e527cdcdcf7012eb4d64a7d11078277dfa8c57763271caff630e4bb2879c50f4e6bcd09619624b1372ae8f074044f2504c4d7d74449e37497cd23aa1961685d41e8ae35e8d4768c95749f1f9308fd35da60b895712f72e4fcfb4a0e5d53864513183afcd5c43d4ac9807d56d80da444c730e68ddcf048a78a8f162a268d872e84b053771ea6257a94549efdc000d1dc52f005443989cc609443e4fee4a2afbf420b8c5b0b44bc0da731cdc1d80358a4c1b47c9b4351752d760b45025aa4721b934a407f1ea096f88a6693932304f4514001a93582899773a68e6c63cb844ec4dbbcf2f5492d31ce3daccbc42b0aaa4ad238e08cb446c88839ab5889bd1ad120142393e8c55f04532f3f38060f0c40356f9e0d2769564eba415012d1aa74391dd2c54a45ea3bc995968608a0960568ccac49e52825d585a07c288c628ff1d943f30cb7aa5b652bedd37c4499040f49d83f8b3aa4d4f6c65356212d0b26ed2c6a16a2d786e4be3afadf70bdebd407d615b41c41a80c9d339580a17daae33501fb42b5e3b695f579c2bb3527818305592976625bcdb79d2965fb5cb240c46ba8a40506151fd9bbff4c472ff421c60408df1984e029617398296eb1e9678e7416d0209084d3301e1d45487d51f74b0b316d6bf5afc988d7402bcf14549c740f477799cd6a0dbfc67de22fdf8e9a02077e81bdf026f0c71a366b6c903030a72d60dfdc7b442b53e57cd60ab912ba6305becd9dc1e5207b68db934f1c0bb6ef7be553e94c517423214d9811048c97db0f7a58392b53522c938ec81016d4ae7735b6d9de1cdc049b57edb67855ce8e7c0f475f2bd16fe3da64f040e3037be77c1243f13b340b98b7791ac8a763136ca40cba66f9735c20627728c2a9129efca42bab42a2bf9edb62f43d3bcda433fe47026731c310d0196990d47fd2391e387f0fafd71385bcc098fafe8c01a415b8150e981a0bec2238bca76abe80f07d4752f50d3d7f5b9c7c9dae58281445f8aeb30dc84de88c4f138eae11bdf4feb9b4c3296319e774034c9ebadf14e25175a6a709b00b10fed2d75a40293b2777dcabbbcec7d713d4ce2db9a64b929dd65189010544cfe03c8f3f3979b30a3d5ce17815e36f03f6e7b9552f96cf98dab4ed0c8306d840e78dc0ffbc9467cacc058a87e3948b371cc93def309d57899e26d1191a022a3495a39b7a15a425675260595af6deef4bfa8695cda4f42ec444d4d4868b09749f06d7d2acf0f537ded7d32bccc4f17f6c34aede942b8fb9be350abab5ab0a6c328515db6a0cf897f59839d85be6506e0a4197ceb3ceac9c6043e6e9593708b2d2257072c46d0846e46848d28f7c99b0361a76e0fd22ef7642ba95f8dd1804c6e62ab836d4e06df27c2182f6b7fe5fb8d83432d37a656dffe62d8630b2180a89bf89ee905016b54b01c03f40985646a3a5338bf9e7675df2ef9c5249987d0358044a274a9edafcc314d902c680fa885e308d17f254d07cb95a41961df7be0bd7d10c1eab703e50c3fada009bff2d054524315132b27d660a97aa600ab4710dbc7c1a7f94420ddeca4cc070b45402fa5f267016749fa471f457f87e647058009ef22567f7be3d81bd72f6db6aa065eda0cb7a2fdc46d2d82830c16f13fa8b0a2e213790d8f0e403e93d1e8ae047dcbf51486f647e73da8ff397c72e53c2060c3eb05c88b60997835f4143f82f750881ac768230af688d267bc200338f4ad903faa91a93d60f5206a6cfbfcd8597419d0a87923fcbb66bbbdc139bc6495a710297514767c4ff3e2fe52dfd0b0570873bcba18c2cc36990253817d388056e79096b1691ce2473d4ac32445449a8e686b62b9a373aea9cc44d5a46c2ff5adb960cc218a24e1a85526a751d7dbf273f28ac238998e253d35c537f665482658d460b6faf6bc51c0012e301af13c403429077009257765df26178c4b1ad69698a7101a77df2fa2682ac01269a0fc4eef569cc277b1d13b4c1b04efb86823b2d8d1e0d58ecca65966c492929667efb801256d8a26996c8193aeaa54e8c4c026b7b990af04889d455f84a4fe96ea86e2a96ea38e44871414b53d126391183ebf9f06509e0709044435b885b1b866047f6cbad74e0fc269064c974a98bc591e4d7272f04a48ad884e29c877d5a478914e7eb5fa1afc14315e2794e9ddd9001d29156ec0dced39d640528f2898daf4c71ee72f96a424cb060688b1a1809d06d41dc76af07f7be406d259f320e56643780865cbee72021d9627d6cd0d034af82a52233eb0ed1a0d0c8fd052a443bc924639d729baf64357455012142f5ef279b72122ca30be5697b1115d24eb30fe7190484ecbf1eb8e313f491947c82a337af83a15a910df11d9bfe4d9c454630a9d834f5f2c96b01e905217a305e2ce48b2531b75a7f0bd8747543eeee305ff059fa87d151b8570ca7cb5f2e4aa8c13bfc77567d9c190cbb4aa17b661ae327db5c069b93c156876fdf2cb3deddce4b1f5788cf5c8c6d0cb93a5ffccb8cea902cf2df27d1802bf8ee44efb0a57eb167f433228c84d9bc008a1540fc4a2aa84b6f62cb958ef83d2258cc091f19228ec36ca7795b0b5ca40efacea614ae0bbb12c8b97da05cc4993372828e745ee902ff8deb05a2c7652a0e851c75fb96f8f70c67e2d165a22c96e50f3d57900833d372f5fa0db94a0d82075b9e9f2be1a03a45409bcb580c138eb322b1e5e2bdb87a908b363fc9f3debc0d5ef894eaf3b5415eb418c627a85a4a04dbaf209aa73218213e6eaffecb144e096d3cbe84cd6c48777a0872ff4f77ec3576037c8a7ba87cf795e263afbee65d06a58726459942930171d204d554e3673f9711763e13b0e05d586e5e144663f10900e52b6de41be11d57a840c602df5b3eac5cbc2dcb63328ef6d58768a6ba370b971265d07ba7969e3ded0dedd059400459b61dc14bdd60968741453eb74dca0decdf36a2037496588c818296d4b3733d5230cb5db82cf5c3c52d32f194696c0888a30efae07630542add79fe1761f41a72e7bf4044d930cf8d94b4650f8cae0e6c80aba538396988092503a7b53e1baa96e3b520ae1295acb358f5bcd855c501f67365790f7b568c85b5fa8b360f9dcef14871a659b5fad199f3f4a265d30004b278e109980eaa8f6caaf471348b3815517722064e8e31db372717c9dd76110fd13883bb23ee54c145ad851177d7eda3aa79bb5d72e85124618044a1945475038805f65c66cbe58d5db81870d62382580345a4e2c0c4c72d9aae34779f29340e5324b085ab8f20c022a1878c568bbee8e36ef0407759ea4471e1927e7eb80e0f68d15657ecf4cdde557117d4274b5e027f6164afe349637a52a437adc8bf630a9f0019817b317c275a55d4095793bd27b6b08eae54357eeecfd28887c8857c0ea717f35f69df80d3db21b68aaf15a4133e76b8d898c12d84464efbc2b3525903dbd971b570c4f9a45aa21a1dbc37ef54411ad261a377f2c450d590c4dd630b0e96e581eacafb7dbf115038ed36015aa1829b8460042b781b4114d84e88b7940dde26decb1d75b6ee4a1db215b4a75503947c7cc98bd04a367b0342b7877d960b2be7ef1facd457e6e1a93d58dd4b96e3c2faf684498284bb2f2cd7d499e1cf080bf975859169e7b4dbb1c20b26b807cb65547facc7dbf3d1f15b50f143639604a615b5460e4c4705a6a222657d83080b07e331a5cacd71e1fa96afee87ace3081788db28b2f3f35e75ce4ff4e87fdbb3b3a457abd4fb9497c29663164d6218069e65d4032de080a624053e9ebf7279f5fade0af4fad8750a44d825be50fe6e06dfc73597178491e4fce8e7a70114eab887cfecf620586d519bd383e3975b2107466bbb16301686e958f38a71a3ce774b4fe1498afd0816e4c23c2a638e308500463ce694f3ba203669a149282b09f2e5f1289eff160e598953f6f8ab9f7dc30e147a1e18ba1418f379f59705610ad814a507878cbd31ef0ad99cbbafe3c83800ac952d05194e52410678e3b5a5fba1edabc351afedf1e3a1eaad92ad6f4eef0eaf7cad976cf571b9a176c27cdc0e461c3a2afdcad5f7140761037bada6fe1208a53c485241f9f3118e0f8f6d93128e4965a1fddb2a08fde668b321dcb889dc0a955c88cbf796ad42a8273faafe8d89f9fb4f8c12c900802d303b9376b82513078a63de112d845f7eb3d832de837bf9be5ff110d8eb80fb4b01a03b2d3d82b20c83f128bc61116b00274a8cf1eca764a54e73d7ea64459ffabf94b810c9eef80da761dd781ab8bd551cadc6de9b855fa4897dc790e15d04defb4609397d5e3401424b60672ad99f626b66abf6db7116f79d979099f4160ed85fb53e6b8d659d099455e0cd6a79a31b95861c7c3d99d226297870d08d8cc3dd06988fb3086dd3052b91bfd3691f1adbf3901d7dec727d1cf4e1542440695bdecb589c8b2c75ce047280c0a085ba396c34b30d16d4b3af5c413b333a1842fa0a0678984168c0d40f28c4fb8361b59e9b616871e82c280b7c17addd59487987ec023303e2a962e1061059a97043e3914ad1fe31423d9d0a30b052eb9672332128c8ee5e74ee2b990b8ba0de941f522131225e11739a26ba0a494831e19fbf2e6289d21c3815202c029c99b2fc9a7a492066d722b43e2e3fffa9b8c711d0b9368d2ba19b87226385042f8869a00538995b5d028981c0faee06cdca8b4c6308a1d56efa7fd772ed4807f28ed6bf6fe2d61056055b7587ddfdf37bd31ede1d31e78b3a1dae0b415b6a0aa3d4c996e95cd9e3af4ae4f527cf5df69d654580bd7a54f4afa946a262db1a06718e075a0fecb603a3da413d9da00020e8db194408447d173503a67655dddf03f8f87cef2d55d4518cf4be3d6fbb224d08c74750f5c6ceafe7eb44e4e70c34012a7d1ffb9f929562c234c39f5300a41e9fc5abc95455cb8c14e1238ccb107b0a720e9be6307b844e9e8226b9a5a6de44c87faf7f1bb1279e97c8a63e4124f903e1b811655f719835dab14596612590344f854fff143f27e57bbc588f945fe30f6890abfd567cb38ebbdc01da0d393ada5cbb98f753374f33792843758298d001268973b9c4d71679d7969d4453e7e2f35b824ae86a0f94c7f6663f7052b8e40ba36495e9a3b5d5ac05ce1eaf11b766052b13b03bfa7faadfb3b0714e652ca005da1aaff1d9ab4ab18e213980e015ba6fb176e4fb281ba0645e229b4b30897003e47d5da289311db7677a754a1b6672087b54adc902b537c6366ef7b06fbcab02f50d9ef2cc95db03f5a46375d92cfb01fa364f6364a1c2305b2d4197ae5175074379c17d0e2b0c624487044896ac762afd7293d4b6dc02ac78d41e23dfd192056288522512eca4c1b7390e15592159b1781ec042b136a5f62b674222f52e2f02b0d151e275b3e4560a7eef14bfbdac4728e4e66f85ffe9f88dc765212413910e262d430180bfced320cd67d7a3458eb53f6d289bec1b6b2e19c70de043a8f70253d3594d4ae3cf974f16f40563d27bc35b31f343dd2dc5437357197b4e4456015d309d774d032f219f205ddb140949432420a4936cd0956fe9b94aac518b180e6019acfa875a441fa41ee165a6dcaa0189c314ea519ad916d5579387a9e22a07e62fe791688f540c2b41646ee634529803da6e1879fd4832c2f8d6d02ead0c007dd46f925a8dca5f3326f100cd72a41d1bc250d008a9a443b8ba968e59ce440be6217b354b3f00dc97848bbe5d60a03c42127056f2a7993e58185bc75d755d05b9e9301d0a8e7dc7fcf030e5784a06da6ad16afd03eab8286ccd73e299a61f0fa37bb6584934cb6fe79202892743083dfe59bc1a739a33ff9f3703cecc690e05f0841f210613d5c62393d39fc76f57a581202f5b57aa5e48fc20c5b85061f3091f4e75699944dd669e8a465766afe1a8c3dd40509ca18bb9149c4f9d48572a021a85053b9004a114e270bc49c7c8f95ce9f5488dbc0b038860a857013617270e58950c202879c830b131c3c7b74e3a5dbde2989cd9c980150c7bfc4da920660f3695f1c8852ca4e8b1f8be6e1fca07ad9e06a88683a1d65f23fad09ea52f6c00b1696494519fdedaffbc464ccecede3f32eb1d4aa3d2be87766b678b46c1430dd0a178d47bdbdfdbbc9cb5b84864f4294b1a8ed13366dde262c771cd222c6c05916c1524299d1585e8747f1fcac2e87f52743291c3cdc4fe1204c59eaef1090887d3882bc8b7c49fbb388e505e03dc48527da85ebd8da0f293cc28f32ab2cd0419baa20598b2bf4d4381ddd69b65f24fd73a1ac075c9be73995e69a08b35fb0808fba65c0e09c1c678eb8dac6aeba3008dd83e2031a3a22102b85c56b0c98a0cbea48333548a44cbdacb81af646c4692d3a2fafaecf1d557ae38d44c1e5f4106afe4715b5f6e5ad018cf2cfac81b996c0641a94101cc2bce4b2cee501bb3fd0bf908ad106a6550cc254f4a62691202555a3835f2f0708db355d5229a9b2033098756a5114a3c9770306fd51b710b14e1c0b695502673417dc87ca34c7f74d8057e39b2e738f74812729a4a5f0fc6b1e17a40bb20cd1034647eba953139267e0c0a9ac35f04fe82dc4b770dfb1e20b594688ab9e837856249bbc094b67ac2220b -generate_ring_signature 9d3b5964da0a0dafb5a05244d09c44cc052535bf41794549aec9b85b7a238998 b7d7b25d39766c953ee51d4057e3746b82c88731470657ffce7f90bba173c870 1 a0dd3438cf05d1a81fcb179ce77f0a61b8a47525d2faf17808662ae3d2127d4a 9046bd3b0ee4f3b9f6874301485110cc7a5af1806eb93acef62718598a837d08 0 21fa54f297c87388cdb9f180d3ebf5da753b708afe3247661116982a6b348202455a29020e8de76f7419a3255688ace36be49037ee17457c5bc8019762df1108 -generate_ring_signature 36c2cbcbb47f9040cb04a7e8e1bd128dd73240ae49d6a91e793c624f9a1b5680 faec603891f4e9da1cdd3e6d205a4173aa202867a1eb5959cbc9e93f353f36ed 2 2d266206059c398811fc3ec5e99c37dc51af758c47f094bcc5354884fcf0e83b b76f2f51b0e270cebb200499466d003705c19d8589e62d6f2abce941455aad68 5b96fa1f454618fba2f17fffe6a26238269fbc0cc08b9361aa03c451d1fed50f 0 8fc197b0d5b14d3456f532345c260f455a0b2d39ad78d0aee7dc840b04eddf04f038549bd94d79fe582b6293830455e977abf32abe7a99deffe14edce2fd8a07c905f794e8d5182505868ddd36061fdb019451c2bff2a3160c095efeeb5d5903fa4217e5a11f414b678737b3844ebc995a6e58715b9f44db071ac3e041766f0d -generate_ring_signature 8ec53a5456f968a3c524f9096eaefd8df6bac7b84fa3a94d0bea97696520c66c e55faa7e53c815159484d92d413bcda25408c57907fb97debcc43b82bb72454c 81 4e6ec9a062da749b4c7ce55c1771c5be74038853f1ec913878dc89b65a98ff82 76f3b513e21d14585ad5489f7e7614a99d0f565ba1d9c74d7d3ac6f4de6cd0be ba18df4320a46e06f5092c242c34b1e5780ed295992b8988d8b58fb4f4af8fb6 038270478eba4e0041656da105b78146c1934c9e8c43e54e37e4103dd9b2d84a 1d88989753b94a019df5b0d526a04355b5caca57eb89dafc2ddfd166cc2d9b16 a18d580f24cbc8814533279e50c1b76c62a3f84c8149a5f28985330342a5510c 8c97df0e0ed3cd10d4546368038ac04a493fdb4f776b71f2883da82cfca39ba0 d51124bf2d485048464f1292f68ac4f2f2d381ee4d776f442df04470341e5dc4 76e1c7489de49f7c029256219e12049f8868c0326dcd9fe7030d4cac30567dfa 0481ffdc653eb171f9f868759e310680c494d060876e40ff8259129ea672e213 c086abc24ec1e5d6c8f1a88fc25aa1b8ded0665a092d57678eeefc7a8fed692e 63068e1ba561aa0b5b7e3338583da9dfae07af9198024cc19636f2ab864ca457 2523398a630cd53e849a151ace8438fddefc758d65a0983556ebe4849cec0e69 05715bfef2fb4e3d04fcd6caf880db87df20232d9e6c22f3e536a051833eb97f 79afae0b5d5f0340259f26c0ae49f2d7b2efcf451f721386bcbd3db0cc516cc2 32dc102cb33a1dffc0551aa87f896bfa4ca364d7e473855053ddcedad08f6192 28772c16ef4767cc67a4802d334e62b3cc366320ddcac5db6ed6da4a3a518e48 ae62ab17972d879f26be0dc8a4e0e72cfabbeb48a1f6c862a566744c73f0d5b3 95757dc182b24d6e76f770585632e8e2293ecb0b2e068b1415c5aa6b8844bbf4 a9a01f9794d39e2135c734007c792c0170211b2f31d22ec855de6c2c9bb099ce 809e8daa0bdbf2845adfbff43ed329bfad793cb1d50964b76b0d2dde834cafba ded1d37ef497ee99ab7f47ba850d3d1592469939b25fa0dcccfeffeb6e487256 5f0c974156a91fda39cf74c75e8418f5dd735a30c3e1be7fccc6e66ebaf51bb3 df40904f087cdb5c2d375fd9e9cc1b8fccbaddd8f8b366bfd6c4c73d548c89ca 86786f17d9de29de6a6f3a96c1f15efd1b795d97f316b2945ea60497416c4d22 571c315699e3f94b0b49c31c9603c952c1cb7b4f501ebc975e7cb9aa76d22486 6b37e86d97644021c4aa8a7570dc5c75697d8a5f48c3d1dc40c5d3bc534ab466 37bbbb806e9bb6515f1b506b554a53590b078f3fcdcce8accb1503d83b4fef11 ad7c90bfb7c44aa341d1e52e1b2ca429e29ba4db54dfa33b061cf6ea1eb00f73 a6562d8af16e8877bc51ff84af564dc25a03f7d0d7535bd2c5bbd130b3e762fc 15baabaae493637cc08130ebdcc165d6250f863613d137e986c3318eb1e1d53d 30bdc4dc44932cda6b13dd13848dbde684a136dc36ddf053c021a789875fa5e7 486d213c4c340c96fab9ceeeb238077f728c3ace8b01862fceda8d0552b285d0 31b5688c7137a1f2caa7356b494db41e82e22dd6a8fbbc1d6f7f2c95bede6e04 df368afc6547d8757f35c2f1e925b2401a976e3dd656790c29df95da30aa7d41 5b457edbef079804a73b9b2c07ddf6795cba1dc7b2b378efd4e090206556c0e5 1de5856c9fbcc5b9320f75715d93718fc5ef2e131c202c109ec452576ad8c0af 57efee62fcef10bf49fa000f0859bbb6bd87601ddb2fb92c8d5b7155ac088d37 a66b2987ed02312dd641783d5e0667e1962271ff175d92a9cabd9f8ee5d7efe6 1d295ca90c2ca4e776946f589090f8b29ed75cfd4912b24335d1d3af6904a165 5836d1b2feb67fed61162fc0547a2989cff2b8223a54e304731ddb1777a53ce1 cf6c69da62ae9770b7cb9258bb6e8b14ecfc4d48157742c2795dbbc757bd9730 15d960a1be78b06fef10a1d748f4b7102e16e7b9b515b85e4ed56366bbf4d347 6f4084468f6082c7a3a09ba66ece708020d294c17718c8a15ecd4c84cea2e269 db08f8b2fd5a814a77a829c3d81da8aba108bb0a30d0ec79095f6272fabb29e9 bc4658cb9532f7dd50cbbe9cc3b69c42c684f1baed27c68f7bc61a3d0ac6fbfc 70118d671fc682d4ba0975f6db97a1e946ae1523a1602e980be873b3bdf7e262 100e6a645828abdf759c25a993f3176bfd861ec8b14fee0bf2257c6942f3cfe8 dd4a958e1f2f175763bc424c37138f7f6ace5505b4730c1474f13e29e9fee09e 981f550230c2ecc818356c5978e09c7bdaba2bfa50a18cb65861c757fd2a92b9 c4591c778f2af35031f0b8c10a9ff2076ebf4743dfcc683b135af85c68692734 ae21b69401e1dd790b5d0ab2a9f302f9596d399233c527fb03a3c04c174e9deb 1a479edea2a50f19902e1a6f7222d5dca50220170d3031d932aabbb2a2c60a50 ae222e075a61554b2931b48ffcced2deeb724b0931e57a7fa42c83dffea81be4 e09cffe50c23b6b501416c062abb82c4350142d512fa7718a1a7b4bb0c9c89b2 140813e40064d016f31c79e98732ff2488d8f5203755206ae3ff8b43ae29c2b8 755adafd8cbfe400bc7f0d6c7e9a6745f805094fa56411511cf71a842dcf5e95 d2f4bd013ff2a5614a0f46d8f8291a727974291ef07d6304cf43a3cdfc8020cb 77b077c9ab0974d0f7feda41526b5f3c49aa93128c71c20a32210320e1f64b76 5658127bad4fe2d7f6dd4dbc60a73717d42208a526b6d0cc42a0cfc72dc5de47 d990e1f4737f9389eb33c7ffa8b5ace778993c3e7843fd8eff9e5716d7dc3823 258ea018c125c4f407fff137b02e0b3f5746d2ea37e5b3f0ca6adcafd4d9fc36 15df78d82effc9eacf8420cc9e11aacac8d5e909cc54acc9c5bd7c4c871451d4 dcae2deb75447b6ea9fe455cb32c80b6d9e756547da0645f13d1089069238913 fe5566a38dbd13b5a0a18c0e6b39c5774a98c588d2fb2fbf062a6a313dca8e15 852091726bb4ee8baa253c406c0faf71cfc6214aaaede4a378465c6b8bac7d21 927934f87ed1e0f415b63e60145baa00c5974030a1c5eb8f24b7188f10fea32c c7db3aecfd44d679199341764a02c3be4a87466677263b8b7d05acdb694c124e 3afb101244955d7befd723e91c81a286ac7f651be08f9895d2273e2d0c49a300 2500f40884d6d748b93f852b6ee47039fec6db5d3377d28569d0fe76455ea246 e1b1e24c5a4eb749ae2e2cab6fcf3b86634b426c3743aa9ccb25f50d99ead1b0 c344cab976b25c1785abeca1946186a1b4d5c1ff5bbab59d8dfb6ad5db01a8af fe2baccb1e96d61f5be322ff3e39d5b72d5cc799a758de1313d4793b5a723fc6 83779c08a1567d1ead921064d6637784b8b85a2d18bb3facbb48e530cd5830fe 5560502b0c56672bcea621eb22343c543696ff0f25b230409984d89b083e399e f78636ef78c66c68d74165c4635fae714db6ddd9ad3b7ad51c7c45f125189b37 f344bdedb278b1a78067d1effad38e71921c10c7ee69b0f597e4a481b4a9b014 164d7bf3bfec8c94798fe21c02efb54b58665b26d7796d0ffa3679b5cfa02b31 a7e4fc8d4ec066b75bbe98d9cbcc648ea13bf6b32646dcb58e5590edec327232 03220e9496c7b992044f1d2f3dd65b6d0e8dbd4bf2f5fa7036508d9fcb28a147 6610c3995d2a266a69d4205027ac22196f9817d66da97a547a2d9a41ae582d22 e95249c7c9018c9c5e46e59b620e8e412eab445e832cc46cc99b4c9178100504 68 dc051e01a95bf5090b0c6a81a5c30ee2230bd6c946ae9cc01cc5b4d4e494be0ca5be00c329853f08d4e520acfd6cb627b307dc20600eca8a0d916d5cab538b0c851b7de41f13a230d6b174cbe94f6f45481d2f9a40171d6eb51a1c0603a6fe02d7a3d24f008b91c428bdbe4398a2aeb93997ac4cb0d8ecd3d6608f194894990892fc48cee0729b6c31813e360b7bdbba134ea93ffb24fe41b075ae8687ce400ed2524d8623d36e8312998eb41dba8ca73cf605f3df6be3bc0ceccd673dd57b0c4fb9efcc44e7d099aeafa41c03905fe212c26a90d0877d64ab9cc0790871f602cca960f5852539df513f152de3ffd5827978dc351288c5e0138b54e697ad0d03a5cfebb4d71cfa6d49dc358f4309a3d47d19a8d0d38908795a470350604eb20c57ab4d0b9896945f4a88dd69bf135aee3ccd44c777dac24379adc149f3915a0a12bb4c02b908c5e932397d6030aca3df38937a37d8f4883081662b762707e000693c2709362f1134d960efd0c121be47b599a3117244b207ca673bf6f777be0b8cd811a6b50498ca92a92a19d141a8ed0e5343fb87c41e99b3444d103c679d0aade2b9501ce663decacd5ae8478999631bb00803da5a40e81c8ec7ad896b4d0b90924d4b1987a19ca15bb9c5f5b10b79f3260d6d64cc7c928ad759098d69550c3ac2df95fc5659a6a61e1ee20cb54f9fb72ec19f85bf8a056e2b0732b42db702ef76cdf48529074860baf484958e088636eed5c2b6dd0abcc0e506c778dd710a4026f766ab1c8081b98ace78f5e53a147869c761c5bf759fbf820f40b7f1d40663219feec371b348319f4d674a50be827987373d8563f4c9e36d35cfef442c05caf61e094ee2c07729ebbec7a2b95e38a13018e1be9b22fde45acdabbbda55026e7fc9ee1cf27b424364e5411aff1f6ce1b0bcd404145498fc7acaabd15ad30e2e4fb61d4acb3a6ee0ffaf1939a0be06677978fa7aba04d973e4687d7ac2fa02c0930e5e874b48c6f7d8714898ebaf642597d5249851b5bf85b9cf5ebc247d0d82d1143abb5c3706c629c1b9739f7814366ec1fd406bbabfa25e52a30ca9fe06c3358826e931d82e30e12d2eaba19bc5d82c53cce0ca3548793ab16128a8650863df1ed605e009122eb2ca75c962bb5c4108961ce12ee957ecc212bba620ab046f96502dfc1693325ed41f38331c83ffe959cad53734f5c20bb9a9953bc7c6092c6ec30294c4caa14aa1a41e0b4d7d68c41f0cddfb2350c07a78908af8edab050b6f2c46859002f874ba8f8aad578ff72b472129b4bbedad565ec4294961a60fda096986f5863aec6fd0ecb0174058f52d0aa1574cec66547db47ba81d5302028cb9767c904744b20cf80a8af5fd60de771c4df2dea19d648289300e2d87d907fadcc3a4f482720b1e0b7b21e4e392dc8032719e12bfaf19aea01bdbc1f7d006e1cc33ce14cde0d3823504d6326fee43b0fc44ae7e7fd38fa6a38b60fceec301d2e9b824c501556fb498abcd38cc1c9afd68b0bf1c01e11d380c985f45ac9408a5e9466e9ef02cb335052f2b1073215b1a16f8ccbc46aa01ec42b1defad4d800a22582ba59e41451bc28f3a8632fe3761926fd2d82dd56374616dae1a8cc120e8efde44aa4f379e92dd694586e52004daf0f35c10fb41ae10751adca30c61d0a8ab0382ce938ee6e457925abf489d4291bd43c4314c6cf30ddbc5a16a2e75b0713d49666c219e12e0c52d1bee6e74f0723eeb16e8d17f95d91cdace8ee6cc00199d0c661f39021ca96be4d3053f566ccd11451749bd77ef5654d53c96177180a5b62159b0b111e8bdc29bb5cf3a866f70636d341de3a2b15562bf8139cb9c301c937cd6e0a5b29c471c9c22ee2395170eb4946f45f6c7bcf03f0e69ccbc5280b4d632f1b7e8aeb92e6dcc6f4c59adc99c7711531daa387e6a5197b11b35b2f0d9a1abbe7a2be58f15d3648de884a855e8be284da30078167e4f41ecf561943044083642de348abb049f2e0c7ae8ae5edeac583574aee572c9692e2e089fd9501622cf6346acb8bc59aed648ebcc19248f0399a286ba21cff0232808d39ded00bccc01d8cfea2777629ab002462bb54712cf0dbd7f7d5679676104a77c7b2890af00796c798a78a3a4e9e1ff9e3433231c02efe3056304f8b02ef93b85873110c66ffab4ff083839e7f5b7d2daf7db0a9c35c7f0aefa3b6b230a5f5aab4a6a0036005746dd22e1a4134075d1d9ee24c72aa7c9843c1c9562611644a9aea236303b1191478faf4e4f7a99c3741cd294c1d9e61d18fc9ffb16853640f402d3884016668b76f1d0ea844212f43b649c016a4e30a33b471da161cfeffab4ab2a59b0f9e66692763f3797b64037adb5a57396ed94a3f5373a0c5b782f1eea94006d10ceaf552c3c68b7068b2bd223abca4977ec6a6ee180ed881c9c09c0114b9ad820b66042feeb500eee8b7f5b57205b5e4be05d2e2ac9973b74e733df1cf3aace509f3c752e828f3af4d9d94e054540c5bb96887ac025a4802285e72733b64d4120e1c23aa5f9cd78a3d6165ec6b463c2f9fc67f3887f1394d7a8ff7b0a779483d0f602b51c512f29d16726f4c0dbda42b69bb909eb85c8d3680cad54d6d322d4304a127370f548f0096187c90a867f7fada233464a571419a4a370ec7476f78f80b04f45468810eb03f0305054b9c771748595686d9ca26af4562ba93490f993608eded0bcb2d17354bf8b08a62bd8313b3dc1d6befe8ba9931bdc337520a36160cc1d45749ef5d55e30df5e551c9dd84af1ecfbf1bca8bd7f05a70440f76f55203fcdb87dd42aee80293b1e89e3182ce4e1d5f1b7c5cf5cbfdbb4f4bc7af8a900ca562c974e870a3c2a05e8d15316c3f83edb7b9043e2503373c12765363bedf01c01f138f6700c9021da1d362d0dd000cc7514e78a0146bdfed847c8f64edbd01280a10add0f573bc2278154db5078ef688cd91df0eb1bc82bc6616c7207574012fd09b489ac09ad2544e7aa922328f67e1e2b0bfade3e03cf09656d7658a0402f14c10de89893b007b15c52e8bea8ea89c8e15c47067eee237079a08c0adba05391a806b5e329dfacba975e6c6f0c639845c72ec2a8f11872a5ac3007f09980cc730aa193cd66ef63bf52f9bad55d68366cb07421f2403d71db514556e2fe900af988b561771ad0c71af192b18969de454111ce873f62145370aa9bb7dfd7709f6591f4843a2539a91b0cece37f5185c8b3718fc49b076f05bccdcb3d475d807e602ab60a086453f204622a9ac975c263e048b3f3210dff5908aa3dfd46d770f3a2780ddf4ef203225bddd898024b05123aae2779887fcf4289c53b35fcf3701a625ee97863203f076147f221e7184273a30e99929aaf28d2ca6ba90b6f3330dfaba4fcae3072b388cf57b32cca39b06d634dcbf24f14971d954f96472081908f7be2c7122e71c77c39c89af70b7f93ee6fd833be132d8621fe4d92f79a6bd000aa9262c492cea68922ceccbcaa0829e62a61ecf1a83d95cac3e08f4dbc7a209f295530b04cdbb0eb8448c3679b0e0a2a8c8a1d99de043919c1f7430aa85670aa0dee47db68e89349d8b3d3cc889e5852c55e17209a71c1ae598e7167fe2bf02493ac42f91afc58588343a20c1482421488686ff456f1aa74cda2a103f38220d3a6448890cc050abad4dafbb17c0e1fb0db9443fee995b1d574287046ec3570d2208e6cc6ebd0c864fe47ac692a06eec096f4e0f9fd469355001cb864262510a46e15ef5f45f3370a9521f402369ba5692e081678ca073c7e89c0f5f840b32039a6a13ada04107cc722c36b1f66e66e3d9443f228e186c407a67a958fc19110b576585e343613674b3f63c1082e9f928f7eeb4aabab3a09d115936f7dc2fd80a70e8d45db877ae0a62ae2067bd2b9bf2c94a3e8eb32831e22c591054c804f50dbe2f59a7d8c62420c2eb8f3b73df5d328d9a87cae2dfee1eb88299769ab6700907cbe9b7274b904fa727b886487a6cc8006e419fe53c20cc858f9b59f5b67d00fe80d668e875704c86f1c59b2c228d057b627ca54e346bf79f5a56f1d623310dd187731be8b0803022d94898a7cad048dca68256976969650c9633f4aa09330b7cd80871c0f15ccf3311f83ae7b00af38cecab64a04319fb5a4aaa9377e5c502f2d64e1db85603b1241666998caa97026f71d57b891afbb4474331477d17540519d23ae42ec8181efb0a1441e48bc0aea6037c8b77d0b0933e06b20d9d36d70dc13e95f1cbaa6518e59c1a91c42f81ce38b518d526b02dd8dc7daea6db0e9003e8efaaf5c8f7fc780e5010069670d7e5702748b4b7816a76dce6a3cdb4899e0ed29708444f7d200da63740fb7c6537f6f80feedc6aaa8d4d754c75e0fb30e50f6ed9e46cc0085ce15097d5bbe5573c157b067a0a9ac489e8dd0ccaf947ce7701eb39660c0ca3dd107f1771c5663edd23636590677f5e9be06152acbaf383690a640b14986093df332005f0f3f60a69905f19d1a9cbc5540542108a2c199f830f3a2d1c5e28c80e720185cd62d43ccd697f0ba0c95a770751b6091590709585032b0cc3a0a913db766c5ff09e036722a61f15b8648dcaa864fe361f69ae6a5d002fe49cdbdbaa26ee7982755f756f4faea78040447df2832f49e1f0957bb4ef02a1b6b560bb2d2b474b71e16c9e4cd2d5a1df41d2df68fcaa78ebdb70badf340ad9de775504910be3cae4fa2e2ba919617667e591fc6ca7c6d3de32aaec3cca07b5b9637b95adc97dc6499641a1a31780b4dff64ac5aa8f9a7d02db722c577707a4ad72d6e5e4adb18325cceb4fd6cc492510e0323def59e62d968a870c6575090666f02b6df7baae501833f8822f785190d4a4231abb04a83ba8f39bfb7b7205d9a981bb8fb491f8d37f8ff4314d5c1bcf6245c296c9fd4a9d754573bd5d5e0d2bbca164ad824025f644588b431985d18a35f0cec6bf7ca6631ce825e670b3068943b553c4891fa50a9af200122bc97a262a1e6d7366c2329284cacd8387ac0dd196d9d8f996630d2ad1ef99bf5d171317caa0a0fb94c86115162c5b9e76750e4f5a081df272eedabec854c4dc9efc2aa3acc03e5cbd52980ea8be434830730098b71bf670b5ca1c50e7228f09ee5760e9fcaca30e80568e5a2d560c5a49b704353cd3982be53a945d9bd28db2e2628e47c3eeb3e2b07781fb98ac01958ed90a77de31823c9cc5b7aa1f0481edb215cdee65bf4f5c9e0f1310870b4204706001b5b9bcceaf34d56abc9a111125ec04a542938af9f39b20c8a2a7d9c78dfbbe0c4a41e595be489c4dbe92d040817144e9d49b4e32e125f7609ed031763150e80c29244ebcf8833f2c9afc31385504c302cde0101519e823c8000725ed290091094f9ea1152c164a25e6785958e304a6b72a986672ceef6996261ca7595cdb4d005d5fdc609d88b949f4d628b53c8d3c5a3ca33dd8b7f742e4f5f32e82a63f130485e9924b86256a14474d87a1a670b672daf38f5d7c7ef1f7b9c45d67354f1604528528ddd60c139ca0833ec2853c3b847cdcfb1195b0929a8be690e7d627f701980cd6fe9250a84a92790222193217deb7c423256b9dc795e1a61cdbeccd780d3be9d2c0117f47cd278f87e9683673f6dbcb6c412e5921060c72f691c61540049b96068ffb5129c7038d7498d14ee6be5ae2db581399a3e5d5b97fdabdfb770ad5623fb28b4e5dd1012af0edf9f3bdf8ee2ac859db2cc56bad939c2b237e4a05d08a44534277230f4e144a462738e6bba05fa08ffc42335e3cba52431d62450cfd734241def81f5301a1a360d3bd8fb361267e76b7921d554a0a18be8ea13d0b2f37f7167b7047cba35e810975e574e63fa109ddbc304aaf543a7f250c4cf10b4ef39fbd2125e53aed3bebc387d98b82b31b786cfd8a489254aa6a4c29ac5f05063f77bb3d8f5130d2750129eb1fd1beaf7c9700a8814662d79cf0ce6440bf08c1b5312711bf66bdfa6f7e31b6f3146302a5bc13f876fd4dac1ddb50a462eb05a292b799f071a599c8f509f14357ab9e2d64f0bb4ff963a3f0bea411c24e5b07880a720d52e1a9fbba609a86e57149dc1f4cb9a9e98c7e42936f1eac1c63930a9c9602e214e422408c6620069839fa1f2490df1eb01d57085baf334cba8d790597d487cc1e46a8eda4d5c250e0e6a23f0a4c2c0a0467b689a7afe4850126cf099c9488d1c2f62015161d5a9197fd48d057af2ff3c99b918189d8ad56ea56200afe2ac392b3fcffbbb69a3cf91a5c7e611e5dcc0822b6bf453fb9303bf96bd5011cce1be35dce1f2837c80df1374800811bcc9f0170588b3f8cf012bc7151eb0a0e22dddc7d5f0508afa8a4352fb218295d78f021bd54c704d9de4874f258ec0a451e83ed9ccd023c0d35ddca5acb2a91de6586c617d942e97a34cdd2f9533f0bf9d319a8965e7ba75cdee818b4c0c1f5bbf2f46e97808ed3b498f79a6857fc016d35d1b126fd1f1fdc6e9afcda7a184eebac4d82aeb9ca3106777d5e98d3460a23ac4da841430f2936337645611bba2d65ba1481be6959ab29d0901ea471780f8d15a5e151d55015c22576856bf054bc3d69755e253a4a34051dffd6dd45650c3e2154f5bae965173ca2f360c44eda8dcc07771266aa132db5bd6ee67ad27e099b2dda62d907ea9d35d3eb24da2e11373093d69c261d3e72d70484e085c0900ce4e35cfcbe7e60026b4590f2fc098f80fe3b410bd050e957f27ceb56ce187a0bc0ebcff5d60750ac1daef3458a9ba47a52738e87d585073975078b0144d06d06546f15f137b3919b5d5ecec0e0b9dd5b892a25b3dfb429462c9cf4d1ca005c0eb21a2d9a298940ed8a9f46d13bcd36908946e563034e21ce01eddaa8ed112f09e293e5e7cf56f2853849f16502e76a124e2ff78573dfa8a999468c1b67b8e501970df04b863aae3f558331f4f953bb357d8392ab66fa82b528fbf897da00f2094879e82a71ed2baf5f8ba0b8f616cb50b435ed56a7c9aeb6fe9d3d06e01cee0ba16650b6b53f852cc229d46a7dcab1f6a9bf0475ec8d98acaed8a916d061340c8fce4968583717b7673046ad6c2b402687e5f0cf86832d05db989da49431b00663f599718e68040631d86af65ebc2455ecb13db4678bc4db94e8c07ad07f0703096298328c559f8ad15896177b9ae22ac5300df758c5f5c6e0470071ce7e2403e7b9acddfa7a8b26dfe474b09367d55432de66b45203f75bda06c58f5eafac07ede66027b54bd81ae0b376523bebad5b392bfee557512abfe94dfd9a916ea4005b76627163172cdc7874b118f219cef35f8fb9e24f7280e5d72d4c0fd1adfe01 -generate_ring_signature 713c4c8803fc00e35bf91007f00baba57486e0d70d332c9608d09b6682d5a554 794244933962c44feef2118a18c784f069ef776f495f075dcc22870eef06fe25 4 d6ed7494656d90bc14042ae12e0dbbd7b9f890d2347417b21830785a32fb3b25 dacf20780d08fb684de5ac429259951b81deaf781a6e31dbcda6ad07f10ff0d2 d0e455ae02f0be142f67090ae09f49ef67d65c5ca67a2edd0e612bff1b04245f 61b716ef95d86ea4d50645d4ecd717cce494c2d4d42aa30fd13a45e3370a7f34 da2cc4f0ebc2d7f6020c9dfe8ede0c1b216075c6c2eda1a9e10969a69a35d10a 1 d6a43c90b85f2acd859bb242fb1f9ab325d50092f755df8a37092da21bd8d701776267c1a9f824b3bb52edb31a55346c1b036fd9fbfc441e1cf1896d9a52330b18cc5127a5d74b4955c36ef99e8d8535701a8d0dc4c449beabddd6e6d9bb4b07f05eee58a06413ae45e2fea73d83413112fcf3a05c2375f89cd3309f590ceb03dc52ac61f6c8cdca14e30c49570bfc7b983f6a86636b451ec9317d5630174e055e72e58852998b0022d72bfd9bb7bb924cc4189f33f8178873735e4e6190160c7d43d52e958605e89fbd223c62a75c8b9190a2e07775511b2983e65423cc4000e458bca4cf0713aeeb81a73ba918dbab9631e1cbf5914ce74af2c62975885904 -generate_ring_signature c24d45d5956e2ff05c5900e0e3cc833784a44af630d7aad0beaa57fd879825eb 359095816f878d15ab2b0a2186ffbf5d95c777c21ca2cb72d2266d083a4a5809 6 bb3e5f6ae5df3c4a43310d86dad1560abc1f6626a09d5a76eddfb78d652654d5 7a4ac8d3528abde1baf73df38ad03a857739fa446862d1e28c4ac3c83edfd15a ecc674b53bb166ac2be28a7d51e66edfc9f95a324277a2d9d1b908e0f65c5e9c 5841a26c6b246992664b8d8b58769db51a4f7a194fadf2c7a15046a0e2770023 75ee9e843ab7975fbbdd99bb16392452a10f7e7b38144f29b9d706bfa22cfd65 3de3c211b808c4235fba01b016862f8305f0deaab1fb58823f1be27f151acbc8 9b8031370c48fb1cbb36f400453a00108841cfc20f4ef9d9d206f33159cef60c 4 6f1b8ebe057c8ccdab02361c0b26ce0e5d625d19741e8b5300423aa0eea6e20ec67f938ed8c01a4ac90c61578ec6519e85f87e7419124fa2284c84be6ccd860b7f519abf1b744b8571038bc38589f17fb3770e224ff19617c4f710a5a5f6280b5e8cbf78a2e7fc08027582b163891e642a82a38672f5b7ce688a36fc20e0d50ee4e38c85afb5bdd0e9ac8def5c39a8849aa7f66753f1112616f768e1026ec40a490c71beb31686b6df5c3e51ccb38cf41ef403fc0725cf625ea6094ea0a74b00b874d65bddfc11d9034f5d4a273c870bd75cd07bd421f4f915095b6aea434b064088ae3e2265d345d4a7564c35a19e5c9e3b5a3fe6932687695197b419c4140bbc7d73d569b62f20155c97278c440c3b871290195e40b58be6c7b08767acaa0692e48b4a8ef22a9d78dcc5db4ea50d1e601df7c59e000d6042a4b310692cc405e40e12215380a43ee0e23e586666eec332bab42e5baf42c3c300b1547df5f902fb47e77e7a0b0675d58fa7cdf28d7efd405075587dde50f1ee1a4e4d08b8c001 -generate_ring_signature 9b9e3fcd3f946f6741e6186234eccbf58f97379d2138b6ff985ede15f22f113d c3b4076a7f669ba7d594c52e660da73a565bde7416c9bb99e06e9259d325724c 31 c468ab3f7c3268f7a1839d256c30bed690689a39994e4afe3512c765f2b21ae9 9e8f235c70243e95b84ed1e454c1c86b10446b699f059f66c46e5a1b37dd396e 5742fde4cbfc7422341b8a5449fdd125d30e41357237e469bf0fe77246577442 5402efe6137d07e5b71f0a1192ff212ea8959e080d32b012f937fefd0edbf337 2331347eb9271235c789b6d2be8c6dc18713e878968d77f72b9d644b16f26dc6 0bd583c1d0d8b4b2f8b19609c8d928eb4f4196a491ca45fb1f7d27e9b1a92c5f e1841b87e3d529490cd95098d08b589f51f0c84321b656ba6e0820c41bff4574 aa142732d8b972888a704821a22d62e92eb09b620f0de227981a7d9612333591 d80b7da6a8b938599b5dbba5941781b1217e4ae5a90efd8fae4a65c49c30ca3d b6d8d385a96ab10a4cb6a82df537be9ed042c8f6c42ae261baa487f7e927a384 131a2aeaa5db3afb87093ce9ece283ff1c3c8f7ae83f016612a4d3c866035a54 23cd7cbfd909e99e696f371875e9d7d6fe1f99a0a091dec26dc79fbcec6c29fc e496229d63f8c8b61f17f330dfc30417adc1306275b55adebd8b2a2863f72c8d da2db6040913049257b6ced576f35e4bfa431f25ae137c6cdfb562118bde066f b658b88b06cf34f1897ed99a26a20b0eef67ab280913d42a688b3d862cba3d09 da1f11e2eccd81c0de8498f22659197dbb66bb0512188ec900d44e8036bed70b 9fbf6c6e3f494eede2ca88646a1f3c3e3c40cbc0216227e0c474f5ed7355275d 5246031ef62f9bb1399d8c444a9e3ef99b0da76ce5cd7ecc882dbe9847d1d62e 8e4c0645b710436a74749e8d5531ed8719df52a68293713e61fbc45d5fe8a68f 18a5567a6e61cf2d3bdd2fb0a58a39783afabe754ef59dbd81e370e35954f1f3 a4c53ef53fd1c7dc628b6cac4bf35a5b84ce3703c07040924d2db4ca523cdf73 7ade9695fadb65c56bdbeda04307b304a04b9d5876a1ebdc9dfe35f7484a3ea8 82c7f5919c40ac93fe9c30b9b50b6353dac39748634b5afb52eda0aca5c00c68 12ba3162a1d7ea4b0c110e4b2f7b6f83a3874ebc983698258e9bdb187efd2ec6 62f6e20381447fe7c19968f5eeaa00c04df07ad01f96cdbfed367b6d34fe7b33 f27a9baac0ee08596f753298a41c704066556ceec1afe5a3c300e339a709225e 6b15357d01194d299f5dac3ac8dd4ad6cd02588038c3d9a8fc4cf322cca7be66 bcda402e1596fcea31ed893a65251f5fd60cc2b3571c5fcb1c8418bb6ff390cb f04d671aa35a499ff64ac633e18ced492f00946d4e3bda70d6274350ad1c313e 6b11299411b8c35cf07ee188537faaec524cb4ab00b28055554673450183d288 cc0b067ef56c580d768f01f0efc8bf7293e408d0369d5c3f0666a2ee4c70c553 28c2450bf0e73521266c017a04fff88bd10933d316572e91a497ffe3e9a48f0f 15 f52078f936f4fc464275c8d7efdaaf204fae9f54859d771b611bb4f40726660d6ae2c5c5c817c1dba6aecb9c68ca7920f9419f01778a45d4a780016faf22040852e1bf7374585a6c2daf275bcf53bae45862f7fe0e7e06425f0c1580f3bc09042af1873c896a9e38d3b3b9a0bec1b5c2834b5a34d1768d1c72cf0f7407c99f03dec7fc38b807e0e71fe9130516a44319a94138ebe4c5454dbe2ae97ff84487099c7e4dde05266349d93a277490350f5344662695ee32e25d2bf81ba9810ec8065f6bf1042e41fcbf0d39243528fdf6dc2c49a75440f1d4f2d5ca5c63eeff400b2a5fb4a6d99e264ce6a3377ddb452fe77b6d4de1e311c35b776f1af20afbac0c005cfb8f3e85e7a4087627faf6f5d6e2dc23954395da31f6db131208587403096f83912d456a82c29cd8217a5104bd5b9f3c447c21cbc325a3069e93fdee3209e31576a78f0f255e6f22bf9d344f4deb823ac8ad341383d82a07b8bffe1dbc08628facdc6992579262679cf2248740f43714e830be162832a93d90cff1eaaa009f3c15f0d7bd2ce540f18b8715d85111b86cc79c857079d33e97f4bf1b502507bc2eedb85db6f4b28f3eb957a8d21fbff8234773e43e697b01dfb8f8c075d60cc7bf48cf9fcc106d5ccfd308e777130b151e23c2eeb5c1408e9783fb0e82aa0cab6755bdbc5a231defecec0c9f19ae0b552a941be878feb41abb94e0e3beac024e4b606268058a85049ff0096ea006bb62e912a1512dbabf8f7ccca41d8d960ceb3a7188b2ace696ae4827156dcc91f064e95eb254941805b77ae2650f00f70a78b4c200c690bc0a0dd392169dd3fd3453d91700e87a104b9dd051018850f7042e30f42cd278f1efb1d57ca40bb0c9be1fb959fa6205527cc325885862272c098357c3012fd27494789a50b4aafb9d490b7b8da00480add7c400c820477f0d03dc121832e5e9b2f4699cb8885556645f839d639547438199ca87f14b23b423041a8fc77436154d6633695aa4916bb0eeb77927d31fb7389ad042b5cf7da6f40b16da701a7c5228786c9aa5fe0c3b2b2abbc973404cf4bf0cf77c2ce992f88901ca7910708b537aad408351b23ee31e8a71f2b154f39ee7ec071e7d8617843e050c173fdaf5104de4ec725f98088fa8aaf6b2ad1b5e1cf535f5e45b303bac3a0e49f324fcfa06ca00b195f994e0d5187d5b07482d9bcaea296b272c506f99ea01dc6cad19632c034a01a546ecbe8d1be78a733653e8dbc61a721deb5eeab57f0115ccbf2e2a70e6cadefe5eb104b6809d78ecf8727519d1716588a792819d72085603969d35d2230ca826a8cc760414c7ca768b384dcffa708bb14abc3db107036b15a95b9ab54d3d3656258be0c84d8eb0427b9eb0aa7af6d74578ad8ada2503d8194b22b8ed704a88f181204eca01d10616b29dd4d7047c08d787e3a0016f00fcae305a8afe1e0b6e2c4d568b43ffe828d0c40927cd5353db9a4f13b8d14901be99ffba31fbf182cc90b9e3f1a9f1b501cdd26e47b28ab2154b27247339460759ef84a8b47fd2d856f190c384f0265a0058937e2220c3a9a10fc81547b77d0da8887f50b80d5a92185ef165a9eb170f477b13b95922debee1b43516ef599a07f54c880d5fed6a106e018792ef76be1abaab1467bd9f2d313b1189e63fb7a30a2fd0ea02746858d87111eb70680406c86f4027b03174c08e7cfa20b9e266aa02b258ada57a843ff0036cf1a881c4fba860dbff1d244eca76d114b6ab636d720574d0feaa8dc4fda1eabfc723de785b97d2c616508927f17b28e571d98a83ec0dcf4d22101ab265aca302b87dacbaed23387bf69ba86d5e5b5bd4ee97671da907db6240529de4708fe305d067720456dbc945a219f0c40a953d5a23be5cb1de0decf6e7947312214bc60666791e71fd801112c2352c9bcf3031ee5f821bd35008a58d8eef0d6221083fe92ab0481d7d0deec9800c923dfd04c1a8a0c152ee140ae56e7eea817a7d4a63e721833d72c9ffd3bc6babb949a5bdff4eec8c612e44038e3d59d95ff73bfec83024964f4456af52bea09c35a07774290ade9c77d7600c24e3638ba1896157bc0effea656ffb444ab75f36777ba94f158335f10ec9ea07576148d0d880a3f568ec361689e86aa9eb4b0ca1942f11e28fca9178c352ae09e0332d2cca0c9177f907a38ce644bcd499d3d0a6bf45b8fcf4a6ea90c98f01019df4cdfd3986023dedaf1f12a2d6c83e3eb223afb9b7577db81f12b0ed094901eb84118895d379330ba82914aa3bbf884f8ebf703c99a7bdddc9d65604b43f0fd1d54026db98106a6e2b5f05dc7213ac689ff0561f1940622f2b50fc5460e705cd15d45703bc316772cef60e91a7463f7ea85a68b77de5e387c2b592d708d800cf16d0150f33e8b535a2a66d37077a6fff7d4fe21a70e1a64589d22a3f7e680f5a0d323f53f3831d71127d98946e3ba4882913c8039c7999aa505e7285ea7c076a6dc4ee6639d14b2038d1332a7632283ac9116ea7fb823bc37ff901de5a020494f9b96618b9da1b7794407b91d97464690be79eec33480aa378f31bd2ccf002156493a41e1f852c89a2e57eb878b95c87cafe8a0401e2897856ea853acc9e0bb855e2a398128226aa239206fa5d638de4656f7507b4d226d20866bb889a040cb88e6805d0673d552e46fd9e436ff53334d3fb5423774b0a3f799fd83114e80dd392363e5095333117c49a962825fd195062e800bf70c56113bc43552e51860d238d88360ab32c01657adae7ef564af13f9fd4204866dd7da5f17853bf1a2300 -generate_ring_signature f31b094e55ac7802f86f73c3525a6cd8e769ee880a8c63aa2f5992c255149a75 b17df6621687c3f37b1a5ce5ac09d95546679313dff994083f079b5cb462c930 2 9085d0eae6ab0e2b4c9289ddd4b4405dd818dc5218188920df043336d491f918 5048e0efec6bc401a73f920934508f4d9b99ef5ecbb439f178b7e65c7d08f410 39467bd15083c5504b8e2a161aa69a6e54d336270f30814bae129f1a53972306 1 0415bbdcd6d894bc056d377cc4ab1f08d694bdf62c572f6c359a914c1351a50bd378f1467bcea70b154150670ff65547891b4857a5db01ad7b57ce416bef1603e4ac86a3a0b160f999338a977380ff22148995358a87227cee8de27c9b22750e8818ce2639b448b0abc1c4e6860443e426f5bf045bbb723db9e076bf8c72b30d -generate_ring_signature 2b1ed38fd7d880a817ba1a9e09166f4ebf6db200b718496e4b0e1611b699a71e 71dddaf7adc360a91390f506b386c69a1cfdd89e948d803863f742d9fb9c5453 11 7670293c88d3439719814fa939089f77b31911384f46863e903d78704c004a4d b0bf008c1c329ea891d807a1d7ef55316b480c19dd90a00233ed6027d566aad1 726dd2e15afbd42415d0bfa6886d84e1f3eb9442f71b4088b04c79747aeab11a df4a86c0db52362d3beb62ee1083fbb3c400553a99b793ffd20d4afd8cea6a14 ff9ca05329874655d5be3d882099ef375017081b3b16a0b7da8cac1ed3820988 88baad2c438cb5208973869e561254d3a38f4848039b445b9a4e8edd2522c325 7285ad0124a73272138c91e701daa44aa52cc647a73351f1f842bd7454efe5ca 0cd91d92f61536a7fe4f7c9a6dc04e2dd74dacc2f2f6a93af216dce3f69ebcec 0062e10dc722a08dbaa346d3c2070973ccfbd511dad2db35f77e8c31eb95b913 a9290ab629ce61f962650140314ba4e6e23ad47e1196460bc33f6ee92b18eeea 6cd25a8510ff4112e5f2c4d15801b7a006db3092e24e2db9cc216c7834fafcdc a5d6b5f4dcfc705c6f42f3ce3854458503a5b82742a2857903bf9a57f297e703 4 4e922754f84435d2a6bba08b1d08cb54b84cc3d64c36bd893039466aebb48b06cadf8468100b5e921eb05ba1ca821fa8e6754988db9033e3af252e66def1d308a5c106a7964734bab8c5678acba8f5b07a39bcd04126d2d05a1936e7e1ad570fa8b326e66fbcb73b86636b12e7cc728139e4d62197f428fece963074cff7b30de8ececaca971b44fc0a19944eb7b48348f8da3b0c9b4e7e9ce4f0abc618f4601a815be934cff18e9662771b0039a39168c29e2ab2b05baa3fd8659657f5f4c00c32894a8cc1754f7ab3c13d06dcc53276ef617c6382fd1b4bb4b601f688aee0c23890314d6f4509ec1b00bd8a3df2775a5032c54ebe6d1aa6c9f129f4214bb0f21e6fdce50834183a9347b3c894eca2c79a2be2738c282ee62f40409a849750b092838c541959024ee799f8f2ea07ad425d1c9fb1c1deb59105c854a613600099c1476b2027ef8f475e15da2a8df91929ca0f4ce687590d95ac9e6e2d0878a0ad8c38092dbc484a278b1a0ae2da6e9b1fd105f3454dbdabc11c39bccf3302000ec9ad809de0b247f86d4e38e644ff7b29d52cdc5dfb23567232955dc7171690219f164a52b7c37b2d59973e1e58cf76cfb38d02b59a206d212093492442f4705e8969b8450a74c0dc4d9681d43ecf433c4e72ae8041f2fa757c562715ee5470aaf2ba2c951a8c7d6bbf6fae29b2c6ffc193c0af2fd419c2e87d6703f47465008353fd035ba7d3e29b40642d905a47235045fac3bd0d65c186fc83543e82fd5059f02cf54a9998ba3ef2e3ce397fa72260c64ea56674e8e9ef96b34d3d05e1e01373c5ad927623a312281fad2b6d8d0c4292917304c147df8e1aa37e6517cca09fda6af031fa6ee63e0a6692c9cef71143458ad073e3bc956d27c3fac68358606b1efcaaf2118bf88255ffe8318c1472d7ab5f4175c1266514a06e0b8233387025c3f5e3c9d46429925800d464a5dbb306cf680b5a8c6fa047fd6cf299fca380f -generate_ring_signature c5093dd27848ac8566eaedc9052fcd6197c28bf93c33afebaa7ce2dda5876a00 323871a9a595066bce864c151cafd777f14afb4943761ad5a574eeadf5cccb1c 251 39937708a3b6828e2561d337b4bb62a05329a677891317ef1fbf021efde4a32a 7d853d909949ca86efe934596d599e2d77997f771aa4877b6f66c9ef477cf18e e983c352edee9ff1590f1b148b377e4a2d5acf31be2fe34df8f6a8d4c4e4ba05 7e1871074a0892900d96e11c48249bf88d429d7bbfc5f0a1029dd27ae601cce6 053b105d261dafab41652fd16d5f6fd87e2c3802d396097745891a1f41af5c99 3499b5ae6a1a148a140469d676a0f42c4422cb71202196215c5e35b35dda93b0 cb14c49d87d8a24cfb716a35bfc392ed5ae3695ce41a9c65c34834f3c820ab4e d4d25b34a483a582179cb1c171532f16f1672c5350950936984484e8f2256c1b e657b8c50f8c92b530cbb8d9e77f3db440326b397a54ba56e3cc6a0b123a092d 4dd6a6ef7f8d5bc9f80693c0a886b0c4432eeab776c17346ac19504f46d622b5 130e7e54142cad6b55cf6143df37f9d206ecab657e1fce433ae278806ca1d26e dc7113def3d74145205db0179dd574c8b00b0e708ae5eb7510c2624500274532 7335c7e6cbd4e8153b9c1ee028e3412f1681058440bf04e6613c49fe1f1bc75e ae3948dd1c2d07972a05cb741943e766fa304e2fea461cf909eea69de08d4e79 28408929a927aee09552e8a162937eed73757ad37ba996365672f1204ad92717 2fdfbeda21d8293c0594ba9671d0110421fc35888908603226cd7eb0995e1ac8 3cfad5a488402caf59a69746fb5cd739782d61d6e2cf71c57bc8c1127055fa19 cea1d4b9951267cdba6ebe4c6bcfb7ff7f465582a73f3b8d60726ee1ee4564ef 36049feba81aabdb2b44241538d15f60fb851dfbc19d13a6e4de240be745fac3 d4225c2a253f2cbd16bb65b500ff4369240284ef1a4cb1f77e6e8143300d0fae e8ce1f23cf3ec0f98eecdd9c744f9894a5c8b3dcb0b29431c4bda4658b4ae24f c1456aae2024890d5c914e9e0a2926e7f004c2e2532572ce815a9ab91f8e282c 959ff70881dbc4521285d80eb7941cd4ba94418da70f041ffcc7479c7c513ccc 7aeb49c0c8b4256f7a718665b575b13cc0688964a8c947edb78eddee1b32656c bc18deebf265b86a577800c425c3c194a390b295063a87aebdf816315e04a0d1 e3e9862b6b0ef2c15cd2058dc35e03c2aafed007e25ac3aa59cbe2654afc8d42 00d1abb0f3f487207b0d764a621ec24265e35ef7d5256647b7404976a8bee167 9db522d9789398ae5954197c166cbd9f876c342836c100364716a3d23a3cf885 30a63e8040c68e413514de31646a18847214d70363850e40bcf5e9f5c3d92ca3 2a78fde45151e821a1a924b5241a2403ccfc6ed62d2463b09e7070b7279c5a9d a26c48fc6e7e82af42fa3cfbad13812bb6395aded22dd250b79f47ee7dfb6581 a801330fb5930a20ac3e1d928ad94a70faa44e7d8319cd6c781d3d5e0b7ce0fc 7ced5508691face4c0630aeee6dd57f510911092109720982102ade5361833b9 2232855cb3aac2d84031685bbe847619d358070216d20f681104f55bd100c2e9 6e49a809ca9d63a6a21496ece8bc4bcf3768b8588456ec1d909c2925f60e995e d3a035827bad58e5a65284003b112160f3d2f45c103bbbc13f00a92f101b2e7b 3abf72f7ac3ffb7fc6113fc42e26031badc02e4d8fdb3f7f08b436514c3f5a31 2d3b155aa6fb6ea05e5a8367039d257fa0b334039d5ea0ef11fc56b8117c306d c7fb3b153d7b88597b92ae7d79f0e3185716dba81744ddd9ac1fcad2e0543fe8 1325b0d92f95fa8ff2cd42384cdef4d2976e61e76ff73072bbcac1369013789f d8cb35a8558bec532c929325f135119af32d6e46a2aa9931a7464c6d469cdaac 7a7c8d38b604d98886d467cd766c40abc920d751c2c94574fad91bbd6a8aef5d be6b00d3cb61d3032253e1a7347e346dca1bb51a92a3eb8b071af0c891665b37 0717cb474be340eaaf4a144b64dac510b6e8e63e3bf9e8bcbb3c46fed88b0f52 3542fe85a3882427abfe0b026c12ae8d195a3db1534254a8d1af81d25992489f ac694745614759040e5dd4608aa75f9b570f77d2e166364d6912cbb21fea0823 5cd8f7e533b3c94444c897c8c91212f0a228fc719ceb8bd55a5c033700dbeac2 872355c0b5f2347b4d2d7fca96f8df10e41b45b9ffb72043a531274409494385 ff1d9017025185587a0a4cdd7c884cc70cece019b26ebc15b09f716560dedeef 186ba15a08137c92659ef620170b35f6b1bc9ecf4c029732451394adfc999590 9c97f115d584768d386ce5204bd2d3629aa63ae97628146498ca37b61918a442 79f55796b89a671a06bfebdca61971130b23fb61eae4704d2fac27538ac499e7 b90a0e602d78353e01b476c3a2c35a5f0c7e5f415bf78d46ede20024a1d3c4d3 a670570bf89098e8eab1fd7afe3e00b01c07049c3722d66dff9b359fa895d70d fd465b0756c76e548d9ca89b6523c153bbfefb1ed9f32774f9e114b60a551d1f 936420d78379ac16945964a1dee95d71940e87c2e88c0a309f70ea710ef1a1f1 7821936f936122f4adfe274bbd32089d4cec99401014b67f0cffbdfd3253bc51 30d8216ea0f746b13f5e4a43f6c4d297d7e6db7e19445749d595eb028ab09ccf 7681954cc4cbb792670aa9a21e64b4723d86495e4d6d27b3cd606b00114c4397 e9663cfa339901c3282b9d2b4560015f5ebd7fbbd4827023b224e54109933369 dbe9eaee8efc256b605483f862a3dc3d5f70a4053ccf29fa4a52d008441decdc 633da88c3db244e95ef373d027a09cbb954ad208fe938f44c3dee2a5c9bd7a23 a1aebb8588a8f423fd40bf80763d0a0e5c4b1bfd3fbe76f14f9b307561ed03a6 9863117a21bc64aa4a8a301aa9798ff1fe6febaffb1e2958b259e1783fb5737f 2eec0edee3b2793b4c35d7759054e7b0c15d4d450b06a5025fb65d8a60f15372 8ca8ed9269c7c5c7ffa431228e9f688716fcc4133c8f4394513251ec26fe3159 f345a4005a7c36b6b78d97a2a030e84cc8157daf4a5a02d63675ede07529dde0 f7cfca247ffb2ea7cef102761986bb68b2e5032245960d800d5fc65d3947f9b4 59afcb1a6088b9530d8bb2435c23a885ee82c3aa0c2e685631a21a228cf07e16 1f4b5692aebadd8d08e8f4468b568a94b2eb59d5d4bf26d5343cabeb20f80f01 897ecba2d98f671188525193fe5dceed743d68886ab7fbaf5be3d09564b619e7 bad4d546d08f62c1e14500f7e340a2eeb8d39e5aa99b9f66d329dd0bce311a37 a6fedfca7f611f99b505df971c935955e2a5179efba1f0323340d67962c4d84a a30f18f05878d4815c755fbe7569d5c042331ef9d94571b0e506892355bf79b4 fd7bbe962d8a76822895dc0b08c9fd60cf1e66e7d91cbb4d88a32d876941463b 5cfd62b1a9c70f8805539e08134011fda0bd8512815c04a96ab1b79f84c6ca91 07477415ae11c9974543d373acceaa7ffac870868220a69e06c3b733939b383f 947545a4a8315cfe81d61a3caf96f5f1a41d7cf354d7095891525ba18d5edc7a ae3e36b290fa5663f46cb37b4d8785778b6e7a5b5da34f28f7a1771ccefdc084 54d701879193377dc59c6df056652fd546b4f3accb0fabc6b6019320cb5ea088 18e49c30704466549121a72ef2756c34a208791ffee54ba4838225c88bb8a8e6 e569ad0ca542a034679e69d502cb792d6a6f8c864c6abdee43efbd188008e9d2 54d46ad00303564a2ee8ff3360eef10952abe041485f2876016994dec2f7ac1b 612af9076950f88f166138e7bd7eba7ce342273816ce9f701b6a3ce6a385159c 527ad33d10c16c2164c4493cb5ac7e3322cb31282c6abc8fc60d5c19395c291f 85b25c37f30e9b1e6afac878fb0fdef3b5145443bd9ba551bca138ba379817d8 f1ecc0173843823f14104228e526537ba7d80aafa2a3f048cdacb2c0640001ae 1cde7b25a4c9b20d6915df9262e220cf42af1620dbe43fe677f97a1c5e021194 40ccb1739d05ddd2e883bfe67a4b4b00807aeaf758a68251b109049674eb627e 541beec1429c13d96d5daea9a75dda434c11c07f445326b1132582caae01d282 646f5b9cc6746bcdc22265a54b7c8043cbcbeebb1ae15b9dee7f6d7c7ec0ff2d 71471b752190bd2ced82c80b406ae39ccc88db4ccc597a24cc1cebfe24e42e63 8cc5a53f3eea773c29cd1a51706f768177f0f388b88d74b2b705fdca00d41cb2 dfe6ec108b198bf4ab586bf12f3f337d341c1a727348582b02e9649a2efd9ae7 02a64a9f14c464086daac1deae485eec3b15b57ba73a3cb6829a45942df6c885 7993899437d2e12076f3311006ec58cbdc36498810766a5825434b3cea5a9fd4 8a99015c83d9788fd6890af7711c4a3701a18cb6691d01c2911daf81a7036e52 6bc9edf373af0048417561db069c88110da2929002ea335ec61b7869501066ba 212d2fa1f059a592c9698bebfa9fde4e1a42a7e5cd3ebcc5dfe4cff5e61150f4 eb4d38583d7578738306c3d8d49bdf9536403765691d7397dfb870b056806878 5086416d7d2681eaef90d4767bc362717d1f75738fe63fdbf399fe987db43485 164a5a51776f41979c044ae85dbfe48a718dc637e7f77602ac4745da472cff14 93cd3e88854c1f515ecdb84622489175df9af084ad7f7072cda98f3b4ea76031 83a6a90b6cba9599afdded09ca949ff11c59f21d715dbfe1b7bb69e8cd30e39e 1411f05a4deebdcb245c7fc6e1260963e848d2d5093bf81567abcf9f84823b36 0d7a2530fb5a816088760d2ff8268e77dae532a31d162fe4538f75faf017560d 0fb071cf8e1fe1698f021a4817531138967476a396b75b3cc904856f2d973caf 030725321cc53ccb7f6131762f53c5e56e9267c4b43acf557be855e62ac1555e e552bf40c1ffaf28e1c467817ef78ef8364d939f5005f981e3c15af2a8e1f68f 7e11b3ad88eb6d844a993b9de6b85439e897ecec629a27e001482461dd40d0ea 16f0aa77b2720fc1c010682081d6cf1a4dc995ba28641cf5bb3ca92be1396083 0d74a5621aa39cd36575996f46f70cb2e17a65a48544ef8153fa3ebd092cd2ac b07a03f471b407eae66bbd77e5f32d7df9ba2bd53600644a6c43ccf2da37380e 2453ee3f5ebc0f2c92ec0b9e520fdc85a5bfd1094774e89dbcf7c47f6a3c8e44 d251b156ff019aae5893d61e0a97d790a6f906970232452c420c2accc10150c0 c5a56fd86a0fd7b6a17cc704b241840b377d47f47eeec7a46b966354655f8b11 9cc2e36eafb7a43d447577fd8ebd8daaed083338bce3c7d150a562e5c77c56c7 1f496612b8a0a887f9fe7ba917df15a26c2a0e5367528b7dc207bfc731bb0ccc 9e53a519f3f0743b9b02d1869a9acde5ebc122f764facec61fbb1b344d71c26f 090a467d9f5b6e63f1d891861ed4b07ca59c39957c94a58b61bc31b0a5fd7ae0 0ef42450c94af1bc08b782b5fd13d4def2d845f1340e911cb3cc1a99deef0987 f56cf062d1975b7d2325078fccf313b0ca0f9bf47fb81a1f9496c59a56c38c9e 72a867219b50935dfe77b3b1df2962f2693f35d1786e586c1106f275e2fd43d1 0ce8053f27a719156673574afa97f4d4472cc0e61508e5e46f34d60349d70a24 7ae67af34879c1a48f48338f3c6a43e28b7adea79209cfe307929d2c213d18bd e8bc556cf00960590987c6b8e92c034421699462da965550e042b0394ce5f3c4 006ee6f0266bc3d1ee4de3c4dbabfd237dad65f3d8d1883be6e8261800a3f504 8bef60f2f5d2a4b6f92113fc6e6644e25786cde40c5b5756f0f0c00660905ee4 d5ddc9afb5043d875d63729e1ff1454e33b625c4b8809d6cc78a722fbad332eb eb9e35797e0754a91ce08ea61798563703fe8209ff6689c88b36d6a30440cc23 32e5d527199ef20bcd81d4a66bdc2ec4e06cc46ef4fce8f047267313b26b7739 bcd9bd9ca2c136832d0469c6e38b8df493072badc35ed90bb6063f10f565ef95 587cf5785217edbd9406ee2210fee0b0963001048943eb4360da7a922e09a6ca 66faac7543d5e35abc41ff40e93d26dc3dbbc0225b283e6803a8586a25d8f83e 481a8dd416ea4cb95da47d4df364da485d7733bf9544956082673aa41655fcd8 87cfab1ca3d4e95aa3419c6c42fd3eba0ec55dbbb4c1dcb2f344a10e0a4c1fe5 66925ceed101bb8ba62b1023f59f4caf1f30ce11d98971e5d26ea31419849e5c d5b3eebf8aa62351c4b731531ef851d9fe093327fb7a4598a5e3b0fa11d1aefa 144b857e9e1fd993dc9bc2677345d5cb721171e42eb90749675e782664e6c002 b93b6b29b90149848c2452df74b309a91ba6b67db97f03d85f0337ebc8cc624d b7b8343623c8331428a87549b7b93cf932dc3008a94920a95987226883bcecb2 7b4538e82a894a23770c06c18aadd0806ba8a9d2a001c07cfaf4be39913c7d2f 925df872ca181459906b39330b8238642f0c2ee8455e79378077064b41615316 c474eda94a395af8db75357c336d7093c996d9d73572fb646bafd395c8ee14e7 0d73fbfc77b3fe3a5750280e8d5a5427417a5474180efd55342fdf464a286cd9 64be1caece30b05367337ba9e0cae59a8b5fca0f872a256a2ca529db87bb56f8 8be33680a41f61ac87e8834991bf5bb066d6761d6b8dc1ee3261ab31d9489e2d 181df403060aaa9e45775e7711244a3eee977179a1a8b265ca469ca37ea8b918 c75c38b856bcd1aba8f0b39da20e99454a829a64aeda962211501db1f44fea65 ade8b070d01fa4825714e9cff03d3888b8e4494aa106ece1b5f19ac93ad662a2 be8cb23501c41cc8da43ff11695b06f03a7bfcb556eebefb3ef756a4d3adf39e 635c8c8532405004c47ec4f598eaf557d0f190101f2dc9092cbfb5d2c194a632 9f0303816b3161b9a54ed048c58f2aebd4b50dd1fd74759b77668d55aeca6966 6913a5aa387f5fc1df608509e94efeabbe1e645a7a0b2ea1731f5c2b76c4487b 3439a5b52138d8489f8e4ea2b6eed403591ffe406c92517fd92a0b94699f4338 e37f7d0be2eda682c76b916dde8e1f59e49a3f728d0e6a384a70617d20d67b1d b608f6462485000a57441392ba0a6f3e080a5702a2e1c2f0ee076c89396e626e f45b075f8bacc04dc537a68e3152714f9ec771cca01e8dedbc8ca0556bae3e50 ab88abef0ba0c0f80019a69d3edf35bb3acaec3b4cabbb999c2811fe6c0e5201 4131146f23703cd3a999104ad4de0e6cbe70617a18db2f12fe205ac0145c9939 7533bddc26066cbd331ea83cd47bed0803dcddf53e44efc8d08bb25622008642 bd4128d7a66046ea22c24c4113bb19550e8190873148ae4f8f7c2ce5088813db 97b798c7b5bee39cfbdcdd05ee03cc41531902fbf2510e2173fdf65f77d33e4b dcf940ab3e938ebb84705381e9bd8fbbf28d38147333027c931969294a73c5fb 8847b8216ce4370e2d6af86d9078e9076dd0d1254d39fdf268fe77fd555ac582 1164bc0ceb2209a98e74b2c8b0208d87e7b22faddfb85264d193b9e635bbfa0c 6389eededbc5d132c379c19f329b97a17504c1d15a0d1394f0c5424d71afed2e 23a1015cccd6b3e32b86be91c5cc6e284728e7361365492b643695ca569bec6f 994c0fa0662667820fe339e742f682eadb7363c2f32bbedb3c0581eac6103130 a07f149f06097c6867f376efdae00a1eadd77df6dd962fef3ff384aa62dbd994 132ee638838203d2f73abf3d2d6565a2b66a49182fd1efcf4e74f337dcef8abf 6e88dc3ef36edcc0a0855b50c9b74918c2a97c143158015391435bcd51cd997c 2f667583cbb5396449332bbc7eadd753dbfddbd858aef3427c1a612d5816233c cea8feb61dce52b4289a8d5340b4309b1c5eb6faf13f1119cd4b2bede2a197d9 784b19bb39c04a0c64d8e03cfa02cccf2bc421b68641a909367d0328917b707c 75beda4e48f1475b63c4e878d44172bb4c4c8223af792ea94ff990bca6b3069b 7faf00a416bd08e6e4ab4ddbfdda1a081a00a64fa0392b95e9230eee21b652d0 2fb7d868c25ff5d388eac019245e59490a83650409c4e6e577df9b3c42f0bed2 b69baad2f76263308d1f073fbeb2f1e8c00bc4bd11191eb5def5a9fdcac60ff2 666f450028ac20329193551b9c2610d8b25dcb9fa8f5940f43e7a60e9c47b7f0 84a1f6033685833b55eac7e04c741af9f760a50d56f209d222b0d2b46d42c532 f227596d9f75206ce74f2f6078986571acf0eba96100e76e01333836cca5828f 787811688624da50922ef60b9fed8899a3734ed64a553ea174255c02b775059e 0c38b0514b4afa77993790fd35a2313f70e2a5c7f2e57504e86b15b5be965b92 33e697073eedc087320c51d1ecf61b54d5578e355c636418b217f98ad8fbdc53 4b64e1c35f9eb38ad17344a4165489e69ea3407101b409465ad17f8b47dec206 7d6d53cbf0c498461fe18a8e68a19ab04374d171951c61bebd95dc4564ce1c98 17066d4063e136e4ca567b7a2dd79aa7aca40634f87793656fb38fde733dd759 fb9ac5740f31fff598eee443097156f3790a54ddb14cfe02aa6cf2e5b3263f46 b7f18404b45acafc9bb8cb10a186a696ae7ffb84126e5458f50a2f1794e15469 929ec779dabe57da88b1281ce1792624c729187e4fdc0d50585ab8528702db2d 8cb2b46ffcad9dcf356bb089b8beb2a461e4ad8cbd5d2532780350e37e41d0b8 25125b917658538e1d422595b7c4a5917d81e47d397b74baa5d713d1babbd730 6ab9febd5a2f5836ad625968e702dacc6babc7fe52d428d7bbae17ea7c3ad61f 4874db513848b00dfa108c53669ebc17506166896af48e3c68148d4bf8b99939 89872f5084d9875e216519bf5a6c0b9211d3ae603683f411ecb84b381895e863 6cd5ae3853e0abe7ab6854b6d0b0926d92c144293ef4dd4f9972fef1ce5d6e37 ab3475fb2007c87278a0b7a48147d165640842d5f67169dc0f3ae2fd305db10b 1ff198d5440e28b78e48d730cb6c210adbe74d42862b41c5ed42515b967b3c68 9cb44a35a2a15d1aa9a94b71d06fcba5758f1f691b48d1817a063b4be7f2ac5e 3b1552d31a70d241c3c33b5ef0358cc846b2565427d7c66094f08fdf0dc553de 5ab0a9edd5f873c69035024f5ce6dcea1fcbae7c75a35ddd3212783d0c692957 ad1d6326f5f0f122e8702a8494a3cefda9c5ecacc61daa3b3fc1dd94fd58e5e1 f8545840aa70b10af0918892800f095aaa228d8cc94527e93b1e5040d1413c88 9209e8b69e44f10f57fd5b60ffd7ea319ee94679bda2321cce9251f1260486ae ef5b023e360229edfcb38c0357ccb791b337630d5296f505763d48ca6d332114 641bf6bd2b1b1356498ea723e0018eba1c4126bfaaf51cedcb144ab2d0362398 6f3e871c296ece9eddc5461072ef8566bb4ffb9071eb591eea8eedfd0fa8a97f 2487fa37a0778bcd6560c8a445d09d5c7fa8ea4f5682c04cc446cdaf1af9f579 7126fd6c1918ff3feb8cf821b586142a07680460d7dfe528aced2980dc403076 d4d6a5d8655a8119d0686d5a82f74606037da1a7a2996ed25e9f3fdf77ca391f 8cc8830b2213ec306c201584340f4b4f5cbd3b3fa3eb36f75786c822e22e594c 32d173fabfc93d005f3ac3ad2366703b38907a2672b5bc724241ed87289d48d3 231ea19cb8dfd1538ad9358c94f381884e5eb671ebee6de0032dfbcdb53740c8 81346e00b86ca5b96febb07161273ebe427f374522df2ebac148d83fb1c84b65 6622ad031e4a255114ffcc1b993d8d3e31c6d65e40a59574905b216a352a37dc d74f5f46d0a64f9dfa77600f3f30b38d2a0143a770e664119f9f77ab9d2f09c6 8e653adfaf5064ea5eeb09d51dd65dd8655fa7fcba239f4ec6ff41379d44768d dd71ac0050e36750b6e33c90efa389b856cc6d06b4a88a2ec732bc505886f114 005dbef9166eb9d661b8954120bc5d3a26f0019ce3f3ed9323c45319e1820551 f5b548cc9af5856e732b3d2d40d73ed801d809611be1b95d3a0a4578974b2d20 defc412b437cad2fdc7299390a68aacb97e78252f38a80d9edabbcd3a4abf3ce e0af56e16a967fb94821b853f6197c6d9994ee0aab2b081ec16ee21f228ec0fb 2853b9be2f74e4731ad0d77b05fae3297fa7087f6e9a6a61dc8295035ed081fd 4d6a31f13950296e764b25be58d3f379d79b3bbd6b38f01fb6d3e473a4f51712 623c7cf4765775246300ad80136a7362cf5c9d862708aa7a19f57f56cc06dcad 548e4d75e578610781844f271035461009b327954bb099a2c1e45ac9b08c6df5 191e63823ab830693cf02c68e19d49e9307c7a4f044b7698dd94d476df250862 e8fd2c2f0a999138fcb936569f3f470c4d683580d72cbc1bd1e4b36faea2d972 925689b1d36695f45765184f979dff2f2bc70efe57e37d9226319b50b90dd469 8fd4bb24b442796650c1f0d825f468b955a34c5b61189a1c44b6df47feb40d8d 0361ed2dcb2a570f577b35cc6d7eeac9b617d70b7967fd4d30f94da25ffffc39 d732a6674821514311651afe2142d14be9526596e9f45e9c8cb658cca6fe53bd fa76c7ea0f87084e9543939eba23cad3d3ea2a0df03733044e73fa25859b8cd5 c8ddeb6e648cfc07f8ebf7a677ff6a038e747e37923849c15c45342afbef8493 5c431e3a46089c0b41ba2136374f0e42ba6f9555b0019995b690e02d1800d814 93dbc3d9dd0820e634aeebdc808ccabfea052b77199ae04fda47859928a371c8 df9e4ec31e852616503784e26457faad8ecd94428884bda9ee6d81c575394867 f9707e1b484adfcde45bd506627735685e0b3663ef00a41c48bb1c11cbc266bb b123a4cb6366790bac198d2d3d400b201c75cc4af7a3430c8a546b9891ae05dc ef38093824a8b47c138e20844f923934f435dc18e84cce31a679a61fe68917c0 e81b5c08382f256377a7fde70b56421e7962d4b9ee90378b201666a0bf88c6ab 256e4f321a15a634355690e49bd7ad6b92cd51c8b1629f85c49260c0c3431f63 ca7b3fdca514a286c7e8f440e8353af4a030c31e5f27ecdbde302a1f8269c482 e6a199df5a488561924613397989d00c595ab63019c2bd1d8b5df37fb1ac7ada 4610c481631c285a6acc307c9439f8f8acfc306f47194e2343cd74efb7007e4f bb2d60b1aeeaba077329246a5a406c310a98f94f3c51ee6dbd102f9e6677198a 834ef843a1b0508f4862ed8013d6555cc21a23c52fb5609c2698c20db9912922 f624c48256355e29be4357a424d2d5d6d9d0d4d8ed3045fd28957d1b747d1867 6168d8666d802eab8552a1908420575c4cc9b5dcda2bbad9c10df93d7b665931 206b2c718e2f34a009b1f1ee7df74b9e813831f175ad5d06e520af8d0012d178 61981a77843ffebd3625d850e0419fbeaaa7a08688d92266f33100d7b6c40d0a 141 4e67a7ac6cd1300b79a1790f1fbc535ec33969dfd79f9b6311e93f8dd35da50ad98a60c96327e1d573de75d5197ac694ac66774fb4a5f011b3442462a9c658022d687fb6ea3dae9c33505085ba037046155851720940686e88085f18311f5b0a046782e297b0ec579aa52b0002c62cf368259710878f3b5661af152457f28b001e1e243964635c80b1721b4bf11e5515c972743b9b33906c36f9c89cfd7c1b056871b6ad76d71cedfb11fa2d29a5ba3d309c160f90c4f48f1718ffb2310ba7067d769e772a0f82734091062885b0f379af0dde6090751de1f179853e48752603b03671cded7081c873b4de5a61082e9bd1235f7abff43f052b5457cbfcb8fa074555384a9cc8722d3ce79599b56777238aa78c09009ce198a57a9160206bfd08bf4fd8a1d4f2488174c67c49b39fbeae0ee42f2323a232b0bf380514b120960212e1483a0db6dda991f2c8736b9df9e7d31c981d7c928daf12bce0b1fdd49a0728f9e00861dcdf5fd9b15455132a5240d5389ba594f15438bdac5e4136181806c422e629d74126f876a0a5d14b0f7544980a085ec1d119cb9fc8d6379577d40ca62b66182b2e1790f55dc0e10fdfb3a8cd7b28da07e78512d640a1668eca950c5fb61dc96ba0d777ef2287b042a10c8eb9c526a31927200dd1097bef7c25ca01eb78c3f71630b880ac4cd2625ad980c0d2169671c8efc42e016ee7e5508a9000ac015e392716b3b9e7151013725e06745076684abe6441cd18e554799d12f70d9f52ea2355df3546b3835c7f9705c6ff7c9ffb41ff3b3263a9ae32eb7039f70b9b4bb332647fb99d7d61d05d38132aa68261f94bb6ed18bf100ecf9646dba1017ccf1e9aabdb9ead1974eb1d1c1fc9a059ab9788c0b201d8e5de85d76642e30ea6f7353b99dc6dd80ab0a740c50e0ea1e7151e318007dbb6d51fe42409f5b40a7f6e01bfabc19fd3149b4248e25b016219b99d340acfb3d9bb716afd8731570dac6bdf299bdb7e4515d8e0eb1fd251506074e08db975a975e620a5a31f9d7b0aa6296ebcd0d3799784a3a151143349e977dc8bc09ed6d89d0d62b93d143af50f2b6800dd90aa984bf33da5e6881b8e2e541f3bc8d8fc2f789319314f8ce8850ed369009d20ffb10daf1634e68c3c011dc8d8d9d2817f759b6e9603d13f4b740f71280430425ede8c579f344964dd22cb449aa31f64537414a80633c1cb02010328a7b38b703bcc632979024c62e92715e8f2adada6dabc007b8c78675991d703b2b935754000274bbeeeece5afac04415974fa15434575878f3006ad4cd3890341574661d1968038702745bd86e3ab8e88ad387ac2983aa2b7955370a6e25705cf9571db8d53f7ec1b29a9a9bd79f5df1139b42e2a2584776d23d4b43ca23209a5ff9d6a7ed14b42885adb3c17f3da044059a23fabc7e7208de27365612cdd0bd18c042cdb282a3f7f3f16bbb79113df7b4d2182f1bcad7d695882cf0607410cbb3791961c3ca45b5ee8a18d8ab6d14f7aeff98294769cfc0e75b6c03ebe9102234c1eb380868c0f247f5abfedca6a071fab84bc88dbb249bad56fa54e3b5408453873905b0137da3d5e29d778f37a989d6480d13e4386fedbdfed3578426a0619393d81047fe2b099ff81b61f41ced2e97ff14477fa6f506e1aeb8fc9fbfc001d77fe4252427d74a8e231a95546861d0c1490923e6aa0458b24e2559d95e60c9ba5d9c8533713c8fc975f18c534283b95974571a78352d467ca2775f64e2308a62df53bef2da6a945bceec7b63b279edd97f22eacfacdfb68a6835ee183f6043f0232c819e33ef8d48265c98faf68826d89a1784641618ea56ed1a18ac98700f40fc9778cfba5f0520b2b6c19e88c67f63f36a1d2d3455b40051d095ecd360a0516cbb8a7c86ecf4bd9987c6bd981c3abed6db965815b0ec20571c90016380500bae8ee2cc3dfa744dcde36255aac1e5fb795617e1e67d0fc9e8d766ce26905dd1b6c455f095435ec412e7a93637c734e810eef1c862a51ab12370662fba80762117a0a65481b98d7f92528be2469bc9b7b67779ffdd06b7b953f29fa040807840913ee988b4974920bfa08e71a8a09d43c38c532fd9db09344390a610e3e01d8d27573f8d6f0618b79ba6cef66470c646b6bba1b8501b57127c7837b24c40ad3002e6740418aebf28bcb3d236c8fa312a0dced5fb5e30aa451a5179da783006c7801089b53a6539188bda423be7af7c42a56216cd8f74d9a2878246d7867026086e912efd959ef41b6974a413e6d5ec54fb39d0caf03e76d6e52f2ddc1b401737b847da8c1ea3a26ea79437875b582fd22c2a8c8a1982f9925c70a088fd00f5a60373e26344f28860ccf8e6ab361676e918cb73d73c3e1ff18f29574459b076522d6b6a7d7292fb7bbf143a8c651c14cf179ac582a532803749a465f8cce096064409c944aaac8933c344b2fb714645dd9dda31306ee2b17960929beeb13032e058aad5888297a1fca21041e51cdabb38157f157458d3a546480d1ebea0c0c218bae518c72ad1f096c1ae339a02933c110121f1d28f53c7d89729bdbf2e103d117eac5f5f7a0408bd9d1a702e25142fcd9ec521883c11f818ac58e54f00001093d6e0daa39a484336c4aca8c444a093794d9abc3c9bd3c4f89d46d64af040c8cbf27dec713ae9d8342a9c5979735e2726fe7668377ee37ac80cc7a605afa0704983e5ce71c0ebf5069575eda2f54396cd2d1100adfce24796c7ddb5632ae01f3cdffb60309e4748f4b5c8ec7eca298757c9bc9f1e73a843bf65477fedd0e0e5a61765bdcc15bf9cae2df7be445b91dd80855d7570ddadeb318c1786d9e4404ebc558369b09a0cda3939cd9e7ee6eaf4e22e4dc3336e4b7650f347c8642df06bf192fe5f4aa21abd106ac2e093d107b243d2ad48a2aab1b3b5832d8e472530845e573874cab57742cc31fb0b1194f7e6d77d491dd377b55fd2a780bf8e3cc0791b6529f5897038e98e2d33d3f4465800c67671c5cdedcb21c2e792ddfaa250aa4eea5e7f81b56e4ffb717bf0228679cae32ff073e0f0a7fc6a7799de52e49026738d6cd7d1598c840360cb5c168d98b86365817c50427f30d5e12d60280dd00c25bf5199c86a8f58c7681754e6dc737707a6f55347ffe409a04bed4b577bd06af96d8545c8c0cd7968f9be5f7c0bd9775aaeab7161604d7e36d0b0c848de30002807e8ee3a5045d591489e0b33169c5882ee700cbf2570a17ee7cf142ca13036d816b9aadd06fd77f43a9e0b40b7d229375a7eb5f6186453d6668fc18683609a28007d19f1d56a08630da9a7e4e3ad2e70fe9247ffce23eaaa6b74a33346503acec3558784ebfccf56114aa3ae7f69311fbbcb8d6d6b0bc5fbaa281e2abff078f6c12c4a289d0211eb036c8b036f306b9b6c32c88d3731f27c7e45ef27f2b0b1d975a5a614ab986d65586339115121dac6fef2dfe50431b6d1b023b50808002eb36beab4532eafdfb967675647e4b6d7310e902210d22d125173a817146e20145bdfad526ad1e57b57dd6bcc86fbc9e8b44051f5d0b69128aa3a965200d81036b26ce7f4dfa4994acb50003f8ed28913cff52a9bca774535ce8f78045688400169e42475e1174d5da6412e0879aad9fb77add817afcf783d9ab4ee83aadc50103f670cf0f7792d56112de68aad7c1fef98fa01ed39375d0e9855ce7c032010a902b51cafd56d37364eab9c3ab21f648b159e3cc082d652cc8bd21754e2cac040f838be62e832e995c50b0610e5e4b3e767f8521c1745a3e63791a36f8475d0181507f31220558f352c2acd8be5beb2bf71c0c20ecb2439b94d3db429797eb0058e4e7894d0aa8313688b5ec6df0823783bf606bcb14de2f040805c9231d7e020c66b902affc191fc9fc25bce65c42facf62f6a76892c054a5531b48b0d932057430e5f81cebcd9bf6c33161f21b5fdade5647792c9271bc592fd173a363a00a0460f0c19dbbf29ba107026cf0cf172b4c7ed9cee9e9890863c65d666f205302bb6af64a9698ec2b1dadb2eac59438d6d3c6a4e6fcf79ff765cc9b28efbd8d0bd099635bb30467bea5a53e6918f3d9a9616e26ae2553cb08c44c4332bae35a0cc151842be36c98515c3c08719740651784ec93d2e2a47c7be9cbd8c71473d90f21c803384233e07bbcf10964985b38d7d935f74749cad59251153f4a7d27ed0c4ec1276018522f6f2ce46a97ca1b25989ad5fe9b92bc6b1396112df79ffb220833d75be1a6b9d0e6d17314396d0320188ded49127b721e113d51d2eb3ca6fc02ca7cc525de7bfd0ae0ba54aae895fbbc260cd2fead7b5055e0664c2f0ded3501c78b6a530857967ba3d942fd0ed3d0796a11c03c1f86778b8195fcfd84d952057a533d04d748c77c0d365b77f1c0dc0dfc69cf461580562322567f8b0f46050498a29529b624df50c49a3ec4102e2db13848631a78777ed6df003afa0206d70a9e5d31e059dddd0206dac32bf083c8e51f26cd53006ff2aa45edddc588a8c103c611025b883f9597fd32e1a6c9d2b99db3ff6c43bee87c0b55194b0b927cd909da8475b2a19a24843a492df9d46dce549db892c5aad79653469b732b794d1a07b52f572b6f6abf3a1a9fc4489bcc30ef9f3444261b944a92d5fb482d50ce4d088a79c14cc07953072d778b0b9dc7f6d91b4aec1c1a1218d3700a8be986d4f20171e51855cbe709567090f2ac9f50b98b990a66b25ae90bf30c8260898c3ae10fa033c17f463ad520bf60d18d159c1017fd1ecbfc4159fd97f7dbe94ef5e63e009fb47b0844893954c5edfd40f4c11840117a275b76a19f484a0943422b6a8e09742bd7745bf312b40324ecf9cdde61c7b2b7da2dd461e5f585853eab3556870f81ed26b26954c4e70c4ada951c3a81913cb5e01816f554c9c3ddd9c50480e40382a826235cb02887af2c37c9f8f983788713b04f3f3b8bc8c1b3af3ae98afb07e07c19c47559130fd03356d8ff2e766a0af558ec090b3fd13c6e4cecf1be6a0e37d02e193613c3b4df12a4d7229bb6bba87caa16d21e0bb6a447cde94a5b210fe1bf41d1237318d7cf841c2c1e88afca135b46ac4d3e48df0972841434165106264494106387b6d693bb7579e71daf6e4d220d22961e7acd111255401300c4039a0ccaca5a4eaa7d2483f41df06cabb1d6e35d4ac321bd13f45a0360a3415506811ab32cc6c32430c426ac484d12e6125ae53160747d76a2620582c14410d30d4eabc1ca13e0966898282f9493457b1472fbbb57a7120ba8af7acf811426340f658a0ca5911e76974125cac2006093b71626d6883d8dc82c23ab38531f17d802000375368f07a5994a9b4c8c651cce873d4cb192328f6d7ffc3dc32c292d47048e6739a3faeec4a0d99c4ddabfd70442a47756ee6897ba86b0ee4bab30dcfc0422af7400a5bb9ed4a57062bae7b3e9f94194ffc76087f6e986252a68c2eb520d8b4d8fea463992312272ae6dc503053bd8bd58cafbfc6cf0e778a05636b93d04de19bbb8a764f2663d63247bb7cba7392170f097795ad3e20205479b9d6c3d0c1e3d1aadaabc580112dae35ecb5d57addc00dd52d547741ea58fd017ba4e5b09f8da838d473aa5aed7fe26b054b217982a7bc79b6f1177b930c36eb5a0c4360c58cffb85abfd55a9ac6f3a9fc521022f18a377a5f874c6fa01ebd6ac2202150fcfbd787aec86c540fc64a636ca92636fcd290cbb109e206deaefc4e65b0f2e00b0190fe0dda4353d4e2a929ed9e4efac45a9bc93dcca748970460355b9632307cca776d7c41d8a77dc84396c4bbf5a9772c40b007a0df203531d2884e83bb50343a39bea1e436b3717c29c5dd29a36c208c22293fe75ac1e0ced941cf2d96103739401ba57d1c7d908d7388dd8d4daf08c1c9b678e8393614496ce46a1bd870c64912cba2d64c528955ee84e35dd9f85476de412c22056c1546561cb1f113e0f511e0216f12e7751b661832a52586fd908fd42e2584a658edb5158514f13040a77010defb6bdb5db11067f12430c30a28d2518c01ffb670e439f730aeade4c073c6537abb4fad0d09c8cb41546cf8a03e1fbd0595e71f75d034de629c6a0600b3cf372cbfeb570f0d196bc51a938f92252e661ccc023ec062117b3d28d163e02d41a651b463a5dcece0669b9278e3132ea18402986aeeca50008f5dfe9eeee09a377ffbb4e897e0af1c7d5a48c2cf87c4a30b1e56dedb1d25687520152c2b108dc3d4a7f7105c2cd2816830f0b27c7795ef4d58a947bc6476b10e6e1d883cc007dbc3cd7dd0cdee2f4ea6b023d54fa01a5201268e80372d7279dae38d1b0060a5d364891627ae4972ef69051d8b80289dceeed2554f0eaeb7efe0fc75173dc0cdaad9c881b5cc5da881594eb3aa35439e80ebe3cab22c99a5e927ecafc0b7d050981d651ad100f90c81ad9fa1d8a3c9e55162253d80626712b5076c34cd82e02ff2cfa0a9b141bd1e60c83f390415aa89c6f45e543e1141d60ff7292a240b00cb2042dac5e2f90f800855b1f46ad67789bb2c15a413376202a7481a31bea9b064966cf5b5c8b88ef8149963cc3d486b8f2b67d568b3eae8dee0cfb88ada5470d0f7a843eb7aa0f241f6ec12cafc3605ec246c3f53e4e9eacb3fabc1e2669e20a756d5b85a68e09988d3ba58fb2891208fec425c32e143f0eacd1ab18c45c4b04ad6836757009e32b1e2aabc93ccbf920141645ecb84d8529b1e80ae4738ceb0c88e5edd988e61de07166ef5fa497f62246508e2390271f14603b2a5682c60b09fcb7053857cb24b29bcca1e9a1bbb372dc55436871199caf0c06ef087b08f405038395d459aa07594c3b18151ee930f4e608faa998739da5502ba8d10ab66a052f72ab36b4ebf52ce3aaf1cfecdd99001448c2b2705a53babcd9a4a303ffc8057cf9e6482be3c3bbb1225e1eec07412028f9d7e00cc59f8fd800c4623023f2051c2ffe5b0bc18048ed78196bd0b152269f379b23eff10e6cff2a367ec69bcc0e6df5880402e32cfef89622781316ab6a7d918ac84ba7c2849ee0a02fe1f9c40dd2a5af52b04426b3b7588469d11c2b9ea8d113025e4b2846dd998b9629ce4808c8dc582dd2af193f027e4b1207f71fc34ee81586813d950275dc33610b38aa03524cbd0e5fee5b3ad6f6ed4ff907233c7ae14b3849782e3ac2b5bc87dce5680ca5146536f323c0d7e213f15cda539b7ffaf6eb1707077edbb9e21112a615160d52df5b4866199da8303824879cf62bd300ee15362078d7996c14ba490e90ec0f42cc321dcd24b226a1312113bbd2b7e9044d20be8c3a505989f10ac9ed30190e8596691a8638d247700928d36db4d5d3b3fe2e5b39ace8c9263891bee7e43e0d172ed241411c3b52b4867601a27bae38d141c6f34dbfc4630fd6f523b7ff430c4567702560b222efdb6a4b091587937619248f016dd348d7bd364fd81cc9d80a83ed1ea01096f30dc3ed33b88a7ae6a88cad49e9885fc74501ff1325dbb02f05ce5070dd2854c66ec0a03b256d4598b26bc073f9161c62668f16fe25a641880d5b14de234b81eaf22a7a339e9cbc1525f160555014f1ad0ed92aa4f2ea70660f91e5d586c0e2b8055396813a707f4e336fc1f29ebf23bfd43e87af0930ae000080d19fda872b57d018fad4d217fe1b2f30104b9db0f7643c649ede742bb33e082cc736c78f5c52f8158c149e06496e58065aca611b9233398c03a4fd4de2ec041981a48729a575eefbcd9180bd2530cb4f554880031cbec9c250f4b736bac10888c97e0c0c673dea61e28b45cf86909937351d8791a49f389e05e612dee52f0b61b053206a9c88ff9e9bdec0c69c14b408268edaaf6d04a21917bf05848d2d0f0c3dd76a5067219510a382ef3414512994004bbc36c28098bbca81f7e798a903b04dba2e3c4a03a63d72d1220c6da3073303afe4f792b4416907b31d8697230672887657cd684132bacc87314a2ea693ff6f55f006175965793f10dff6e3960b60764dfdc141284bbe78d2ae1c2b1431a16470a1cd563992918b565980c6450c40d957ca80dd7a275c5069392a5f1de2094ff2e0b5154798afa64e199cae9107d34183134c6675041a3364fed3b7cceaa5253b9bc99f8d7c3be09bfdd130ea0c59582971248b038956be5aa44e9437c54f2342c6127d312e39281af5dcf20206e7c677353669c3a36a06316765ca18984062907b6a6bd2d9e183a0d9f16a260402736e71e54f4fbd27e02289817995f7ad15b50fc8a2a27be822acf7ecfb78003e829d6bf32e4aa8b64cbe3a66de7e76ec40f0666776e5e863c48d4115fbbd0d5ddb3a0adb8558054102c34a9e9b5c0a6b4721064c7d11aa00080bb9ec3d11040dd6d8ed85c0fa1633ee2c1a7688b8fab9a16ec6bfc42243c0a80c662d20b40e7ca9322bf94591cb392d2f1affb4ecb2b3f0512d94571088a775af791e4603051a4707901a550c3104de4741078ca43b456fc9acbcb67aebf46856f2a062440846450e6af6e635d6e91cdccd8b7d97b95ec5fe0a6fe0616d2192c27f43786f04df12ce29b07e57d97f354a2377a29fed4c63bbd9dc2cc8ece9b07d8c0501070b670df267a924d21173a389bfb1d0c37708cdbfa2951d907a2b878cd53ccb360f1ef154b28e8452c201db3f644282edb7d04330a2633c42917bc07bb1980b550d3398bbb2ad49070286db8f9a46f17f6ed2fc9fc1518156dc6c1395685a0609023a9f21b809008e97832399710ea9e0d1c12f4456a878b2c5086e915166c11c05eba04041b199c295583b3d5c47f5f3a4f37a5e6ddda05be78a1c0a7e3e3bd5006c59410330fbbeeaa2024d495e77d3dd82d7c629eeff41d06ecfb2f6f0bbbd0b538366e6a2c971abee38a12580f1c28a313aa08b80347edf8bc9ba97cc3dec09c9307b37fe6bd16b31b5aec3c432ddea0083c6250758b0ab710d550e797fbf02f563e3296231ac35a26a85bdc40d50f8a71d7b0a04fcac8d58e721eedb1c850e5014485ac3eba8a1f838aee68eda74474a3835424994098f369992aa8e31520beaf70a8ae3a29f6cfd38f3c3d692df3d03274efb0f2522e0234927bd327d470513c21e34f7d0797211f35b17a276796322201fec51c251b160ed49a833922b0e14f251616a6930236f3075d222c5c1fe7c5fe3c904eb4f2cda39cdbe4bf3b80f3d99e00b12fc167fdec761dad45d7878ce6191eb440fdf8d3b590e5a791a3d0902734b97c6acf63d4a03a3f4fa923f634cd31f6eb5043aa302466cdeb56179034aedd4195541e6aa0ce4b14ed48f494f9cac4d5900de8faea15958302f8aad0a159ed3316425aa5a8da34c30f798ecd5e0b8deae2dd89e62a50f2a093a33d80d246ad0e707fb290a05aae2299232860027aa27dcecea6b8cef8fbb46ec56f405fa85d97b631e3944bc29d606f4a897f53144c66bc6236a4ca0df3a996d2c900d9e7d05e2e4f6744b072c228a963642907e44dac5960c41b7da56354b9ae5490945796143ab74cdf9227dffddb856ce63dd1705d57802d7bf5cb9b85c6e2eea0c837730139446d602414c94f0798911d8ccbd3d1ea4a878f799d8aff09a49e00d160841e884251827f78dace87c09fd5e69754bd50e16a4a7246d808f0e6b640f097aed72e68d63408dbe6fbe687bae732d1223fabc1804fba9c1ddfe40d65d07328496fca25a94c4d255330f20b685600525c111147dd5064c0df65a0eb3dd0a2b1614251710381d1ce49d2f9e4c02d2dda4d9445c6bbe420552a7140ad16f07eb8b90901bd2f43fb72d575539bbd0f1079645d9dd7a410097eeba6868396708e131f344e3139bbb83b3acf692389d5ccf9933f5a4f07d972dabedb5142aea0924baab9baab4a16d6bac38589b43a9a8a631b7e84d0490ecd6550575062406065a3120d5586a65206e9d54c529532262556a6f0f4c41821d69532094ba2a2c0f2c1150e4d9c829cee1f48279d208a1aa18cce4fb0757e50cb7721b1b57f77406e8d96c180fe8d245d67246a51e0e3ce476644fac65857c6ac29e26af06b760006b90f1e5a0ad8e3d069aaf50b7cac0b71a8e336f989cf0633bc42bc1d2c9a9009ae9bfd5d540873a99af3a8bc06f35b31b69ba58d759fd6129bd3df27c85ae069e71be14e7e223b04809d097a3bb04a16612891b5dce672bf026480ffc23be03ec7cecaeda733e8d94709de1452711bb89bcf2064c5fbf54f8dbc42a09a72904904dc6f61cab975f5e6285872067a4b2dc81db55622cb349a952d0b8423c9604b2dd54d6d7170bb0debd886a6a50fb28ff77634c4f8e8b0c949a989b4f0a1d00b1df6169e54bb450ece8282b6fab5931e825d26cab9c75afc68cf58f03d2ae05de6df4f31a0df8a31b8b68261f2c5a28c10ab713040b99954917a75ad31e400bce87e455c3e39d6c5ca11bcdef5df0b0d066531561ca27558d346ec3ee57b7097e2811824cf7d441c42538fec40f6783c695f6cfb698361d21ccf30cac708602bb18a5e2a627d2609e9c5533efc346a2a6bae08b1ff1d340cb67264e0adc8401ee9c24454dbca98653c19ec92e7def47d9dd398fe2da283ab466f2ca5162b1027db2d7aa3a85ac5786d1869caaf84a3c715d108121abb7ebd91f1e127c0c880364aae15de3901be5e0b6fa1f6181f0586f9df59dab88db9af0aa289ada08ce0ee773cfb2917358248594de7b0fa9be0d0699f54c16ab3ec581c2c9fc7154d703fde9316c838244f1a9dd3c6009f4f6f7bdc4e2826a0d400ebe322758e218980cef3044b754a86e113023cf973b55649e482e2ab18182bd34b38ca33e544e72023fbe2f23ebca510c1d257dcbf2a68d38a1fe1f9bfd0af64f8c8a5ae6afb3a60ecb3f8a43f0a6f589f0e178110bc6f2fb9e46ef810ffaebf9e1956a1a8f82c601a73e6f4d6b05740a1a8941ad4a53465041a8cb0082e1104d26e992842178940bff69d3bd914133a7afd4be25d20b0d8585ca5852b4f5caa14c1099a4430e4901668c040437235625422b6a3ce0ccddb961f3002bff5a7e95e69ca9f208ad950f2d96d5de67fe4a3efa801431a12a274eebb021816ed9a32f40245471e34c5d0cf127148d3867c8f98a223d8c30589a0a55d3ce32a0b07d5bbde6453d77d62c04e7c881a4ab2f2f8eec40df0fbab2e7f223f21da20ca329fc78bd1a8b082b5e0aeaaa110225d0bb065e40b269beb295b16126c04c095b58be9d4621f6995e7103b3fddf74f9e26ad5a5a1bf7884be7573d29a04baa7e52452717502314725050017d4e6f91e8822704460820f700bb33f1f51464f4cdbe9081612e7e5c907790c481acb9c159636920132508dee063e943d3df139dd4b5db4fb07dc82394cef00ba923a769ebfd0b8752c3c7b8e0265e0506b410dbb886f3c99071a15cd192a030a41b11ac3ac39a8eb39c0e7a5d326c590d33a3038f97ba28a0c61c787047e04c0815a502e89fa1d7a3cbb0b5b233c1fada19c6799991a4f922309f9201d7106ddeb0bbddd8310085f7c7d5e948621e2b3283379b3a9cfadb94834f9fb73760edce404bc17cd91c29e7f0ad88af434fdea071660c59e5c46c028f232844bac03964aee4678eaffacc944b06db61b66d716c3749077164af13c539ee41be4450d974522ea59219c73064da294b70f3f6bcd08d00c49840f28c31aea56afd25b0d772cef073acf5bf95d23e678203446583093621840d854cb02c8cd1a52ced50e2390465b5e521d937f6bd58d43ac6731241bd84c085c3e29d7335765d28e3106648be285aeb932d5b069ffe21341c1845cf964a74044a6bde2842a63e7a5ae0f8c36f434f1e7c525fb3ea5b3f3033dd54e25d179c9d524cd5e7c3ccffcc694050a719728aa8f54e1a3a889663569c0e65d603d7331a82ec3fa97814ed8a08e056a5b3867f45759823ba8dbe86d4b60102d026605d1ab8ff9eb8cf81ba561cc0aebd01b5fa5b046adf96ba75949fda75da1a3ff3d091307f721ad4b4fdfb62b06a69b6f6bb6fb32311484835290e384c17f58f1c36e235d41aecd8aab78503c0750746e3fe4dc9a9ce5e7bb6ce59df759554a9b057dcb3f9af95be4178c34640e7ee8c1b32bd50ce661528a209c3b4967dc0abecde33701c9b7f806b31dd91d0f6f4e76dabbfef290721bf93e0c3e9a2250edfe5fcbead26e0cc7804900945707eb7dabdba53b56fd428e52c01612a04f48c8077a0fff421aee3baf2c23cc400c129f98f17900c53560520570a59b5a3ae121cabd2edb7e19cf482daeb30e7d08f57afefe104efd55ab162d2ed7e9c171f78f25a54272cebfe57cb2a20e2d4306b27c0e2690ac0000e8da87a7ee66d8cf7ab7e324837f9c8cba33c6e404edda010735f5d925fa66d849a4cd2d7cd56f5ac1228951244a27e542ffc6b0161e9b044fee3e4c4b22bd328a4e6e74add87f77b1c9d69531e5d43380f7dad20ff3eb073ecb0a8227ea7fbcb85c314497700277105e705f8daa79b3d4fe7323c3dcda09753b9f99208f3622fdbb826165bdcc4508df12e5cab71ce52b1fcdcb218f1c0c6123ce1c73032a6d63b0847cf75ae949d8f9ebb14c2ca1b9a9d832a8cc1e770950a726e16a2bd5778d27ec46ecf4274757649301194cf8c486328b705e1d6e01f195a1d4660e78f5c9facdfd09606835fd764f7e21e6d48ace420eed20dc9e0787b6a8bff2cc9aff530e5b63396db5c0c479330577f3780529a2e7890366670cc2ff7f3466955b86b47a72300df87bf43f2c9cd2be23163151d041765f1b2103563f82bb2d964365e6faa7dd7705247d418ed6f553c67b121423d76f292cf20a483331bcd42eae8b63376e303b2890cbc4a7df739a4c0c81d7640f94d0b97c0262107620b2bf856b5e76c8792d86e362eeb7c033082c24c7ea4e745452c4c209bf5a053e576c4f2fc07417352903c345be0d167022ab65ced737904e2e2c010a9bfe31f13120de87534497a80e8a822f422dc69a38e246e329ef1f935d15840c065f0d9e6d1426b40a0d04936f1f484427486b6488d9c649d9c91d6919bc550699d213ed84011f2b749885ce2be1b4c6999cf3dd11ce329fb6672e614a82910906c0e65ce0a49194badf352d05d505fac38cc9ae4eb8f246d245328467459f0e405a98b2cde688114de7e73f2cd6b26359b30b185af35b9fe33ddbaeb355870a5ae0a86d3deb581df03c0a26f8d95327c9e10f35aad9b811a05a3829b81fbc02bd36781af9873dc983a8618e27790665619d9d0dd2efb6b838588ace940852005f615367bdcb7e3a6025c49470395a674bbc113728a0ef35583ebc5bdc8191008a6adfd195a2741ede9a6a9a0e114f66a695124c5b1e28125df3a98d4407c40d2f0439d43ea5110ad5a0e9ce0dcbe747906f83e6b020b63f65be447c30081c0a29a66b074825584c67f3eab551fb82d2cd16e1977fe60e7ba4554ef1d986530774d005fe259ab622fdec78fffb379e8ca7b16c538b1185cd81390fc297125006208af0e06e8bb93d70f2aaf6b3102453ed4c73cf0842a598825f7a3d4d3d040d2ccb60ce93c393392d11f60da7b06d786db98ba632726f2cc73654073fbbf10aace74a3ae6ff693b3325b7efe95b5f057b6b05629eb4e230faa1a776a01abd09adea1a20e7e1e2ca29b497cab9c752406619a1d4e0f67e1166be08a1b57a7f0dbba02cee84e977cafd3005a8360a4f9f3807dfcd973bcb0f60c6f6aafbd6e802772fb78e769a009c627b665059ad3c7734504e391f7e091a64ac8abbeeba840a871f7510529a982f8ed0ef0bf5e2211f19284d900b894c39379d72c0e54bc8018d7dcb9216681bfd8ed84842125041efca0add66d7dfe5c720c0d7a4b01c1c0c9087309b98c3213897d0159864d53c70b326d5d786f17fc62e2431c8060db901018315b20162ca127d03f5fdf689a57c7a542f05add01f500a25a0450ab922096c71e4c631d27d2ca09049dc81bcbf61009ab17ffe167fb8f10023352997850f3ee3a87f8fa039e9a1b22449f08d531cd0e248e41774653d56d99156b41b5609ff9f481d2e3b7e968f4107068b36ff4a5da44ed60a88f77b7b70f55a690b590c749fb6a4a3180db6a127f548b96171bdd8fdd124766d5fc22a5ddfd48dc07c086eaa9034a7a74f62a648b7a4f8d09ab2fd59fb428540f0ff1ddad1313f415506114e23f574ca17ac664e0c09fae3091489f8426be2b983044a5c7b922b47750a64a4ccb21000e31c85d92b335a419086ba5e256acb7335901e467c52f6e7a70db25ad7cb48d7ff1981a3b22264b3d67090d656599a46d80e05a886fdcea140069df7eed27c380cad9464b071f61c51e54eb35d977743bb49188745b8c8628c0174ef53502575389133f4d28828ada9b76cbbc82f419217f6395f88063763ec09f9e2d04ff0162e45b264b263e591f07580305f8f2bda16eaba552ed6212e9406ed4f6e57aee271e9599d98176f5c6b3fcc2e3fe6ceeebe48a38e3b8a6104540109af1233a481e61c1d8f7190c69b49c7d18f39bc3811398323bc664c57573309f5bac76a5b51b63cd2c284287884c2857dd9f2ab87d306e67c8a637adc085b06812f76f41dc1527101783a2b942e88d694dbbf5df525b22d5793ae7f7d7e8807113350e14bf7b58b7a4e11a052d8c2a9bd01d320ebbc50c94fce8d859af908081857d11c50ea4f97f7b357fefbf64a9365004ff3abf7ca7bdfcc4bbc2aaabf097d068434d843dbfaaddcffa3c183731f5484060467a732b61a0f15192800020f15e9807d6ea828ef8595b182aab943f04d65f267cd61a50244ca27a03c4c4f0bc4d69be239988132591adbdd8dd76dcd85647d768cfdcd5842ebe8cca88d5b04f20a465d4bdad27813d1c00ca527b2cd3fcdafa14e21d9423da6cc7c04c87f07a1454eb92c178d7ace7c1ba90156d2c550a49843ec9d13132a817065f8c5a3030067d24e1299a0fced20b8e62fa4811c2e2dd2dbc1d7a87f91939a057a570405ac9df5afe55691f94c79afece38c030f6f355ce4ed4784bead56a3d21c6f76074e3299a8cf37ed66c9f76e055fea6dc08d02d8a4051df482907f6425cb478c06433553a8d1da6b3e35f395403dfc1ff6302109e1b6395030934f32dfae4ce20536892f029cdfab6227b5a3e527cd0be1e7339ba449a900f1529fd654f8ae3d04f209afbf4b1b1c519852a6296ead7d150525389761baedf44ac6071f878d71050beaed1e520f631a0bde169a230038011f07f2f990ad52f07625021a254b4e018a4ef75b6014042aef20a9225d3617838ff1b37b18afe43aa449e3b49f9e2f05bd89598a4eac23c70b3145b1cd1577d8dc2bec5551165956f7eccfa5a507da03a65f6ace76a6057cb4064998eb3f960a5f91f4bfcb40a9a1e0806d22781d2d05da58e2aa1b183b5782e65442511b5ce6dd776e3f4a2ce95e8a3cf900df1783041477cc1dd735ead1425381e8a80d123c979b1a835e0928cb2b84984adb60f40861b5ff393a7558baa72dffd3c6ad39f7dc023509004a12722353db205e431606c9d85a7265c08c38744ed948c8d526aa252a7bb8971498faf318fc9652955802aac1a9b15cb8da39c54e367a3f3ea782c2b52f532f04fe2bc4c6e81bdcae0108d3e3393b7a7179da8e6fa02d219050d13ed3968a392200abf8df9b2139f5b50640d7f253f8ceca1c7296cd353559f850c5e5d5403eaf13192d3715f9c6db0d076a2bf0fa3228a45a3fa3bc4bee114dfda6565d38e9a70d975a0bf649dd4bdd0261bac65cca7b569ada19ddfefa126517ac5e2ac3441ea014b3f84ec7ce69a104bae63da0ec377e02bc209fe08262aeb2a3182aadaf87337937d93cf0265f880ec4fa6937027da17ba90de29893fdf65d0705e4c1c3c3ae497a6b8f702fb3df0dc2cd00e48ceddbd668f4e4bc5eec6d332500515bea352e8d897ef7d49e76390a5e298ec66a838f2216d3f1f0547199f40c4c4a453c8cf014c09a0a1b25e96c064f6367eab9c1173194917c15fa15b3b7a7be00dcaceff71c265f2ece50e67e0550689f939b8792e202a490c0a6060a0a452185097974c96c4e990ea59d0a140b23ed8bb2422b96cb27a662bef936f5e2f7ff3ab4b019a4d6f6051e334f4fe30f5c932fa98971075c6483fe6a2ce29810318ce787c101d4b1ba15a457dca7cd02bb2f4f7678d41bd32114202f7492548957f6c711754aa348d02143681a5f4905b82ef496b1535f848bcd4ecdccc4c8427bf2403d3dc0a681767d0f7ed7491b002d11c85ba1b8352887f981f5247d6fb62e23d034c22b202618512408638b1b03339c7867ec8ea70d7694b5ead1372b657595fb3530a9738a9908fb0e24fcd306370324f7993150b96215ff3a8fec8f1158f91712ce37086dc7a59d4bddb588054f22284152d2b3b1e591c6b27b1b6af1f92ec615cf5ef9076fb90089541da5026f651189cdc770cfb0cf6abbcdc9af8e36128173ccbd3a35afe1829f9cacdb0b6a20ec5035a4a4cffc2e85aa284e4dc6829e553906f8d39b3a3142289e1c080b5da4e5c489185ca64ade170750f0c3c872b899a6e89b62b644713c56e035b804f90d95a071ec6e1f8805edd90f8c57e8d886b90ca362d6416fb34063cf928609b5fb77b6d87135e2a5eb160fa79f24761aaac4e7bb740bbbf03e5a3f42a6ea0a432d348c279af085217c0bf2fc05bce508dbf1408a211d025ea73adae34cfb0968c223974583cd36b178aa748751209e29a0aa9602f20a2d7cc2cc9411fce3032a742deab670b9550cb7be44d47b8c5cde9362a3642baa92d48bb3be29defb0000d4ab583104e1ace4703894a88c9283d6e1340682377f3d7414a6a002a3e705a8aacf50be94c1c56b699aa95f7e6fd92d8cbd4397490e3751605a62f97c1909fb71ff9c5115abadad7e79cc844c4891df22a869e47febdab14da164c7715c077ce0d312b79887f8dd7a6606557409c654bba0929a98815bacdf90df9c42a202d10bd228f4634f8a5ada3e9c147cdcb65c509594db62959b5c891aa4f363b40f58d6b8c547a85b527bff20d067a12eb2571f7dd98e857e0861f62fdfee5540020d590a06d9397c973a289e3d9a7cb95bb3f807db306158c931f2c30872471d0c33c49e15b70bfcd3c238bf2594e222a33676c0259ac6637668704546e142ae0961cde5a4ee3a56c584bf8c86f232aacf5b6758d2f15c36b7ac7c9f05e8f7bd04e4913b31ab651e05efeb7052dd8277765ff5c23bde4ca362f603a2463a6417053bb56338e1ee29bba920fcb86526a3fb849f57b5622ba1bfd8223f21f947d80198c0dc70e966a36d60c97c3081e84a495fa9c184515162d7fb0379293b192c0a2e9ac7ab23089ade0e4daebf4eca54c658ac16e79f0562f206e1bc8f927b190c79f87d86dbca24d116c086fd3eb0a2247aaba0ba1d3982bf92ec30841fb12a0706c1c22266bc4a9ec7aa5bdaa65d3c78d0ef04d4e7d31bf4f0df889a57bb18002b3e7004e9f7f67d39a89b72f44141bfb7beb00018536c5c06b6e2ebac45d50a3ef547cac7806d02ef06a328fe818ee70a906d4c0a462267406eb280c8b1be0bc734ced8874e46c5eaedcb4eda3f5065fe668e58bfdec19dc44099739303f708bb311e72e172c8500c093e336afcca4228e12d14e7e669b578fb9e03898abf0599a079a0cf3c8bf5bcfde3feed2744edf02918c1712c6b39961a27162311350cff9289e670d576c142498b8f9b3bade0c250d6511fdcf2ac65398845bcd308043315ffc67ae233a28178f1de692281cb749c8867de7ba9115c04ac1e67709e0e137eb48d3b09ae04b0e8692c344ba6e8b6ae3306a1918d778845d3264d6e660c10442576b87edd5e1ed80115a39d5c66510b4287af5bfc2b329239e8a0ffeb0a518977958b11b0376329e59ce00fe2689bc4d2b9b67b98e0ead436ebcb94fb065db636235c8b94a11a249c14c4f26c4d137af658cbc19ab0ca250961fa3d1d05b87bf1047bee2a882a3c8b14c8ba50f32742081a1c135b3c19ebdb5a445b730ac123613196b99b058962108e5ed86356c103bfc4de82f16c18580b7d9a27bb00ac153402cd742a1259a8db7e88bdc367119c81b204187204944c2e42601637019c9c50fd7ec020d1451809b687f3f068db50b4e8a39475f8662c6c76c86ba607ca94b40b3e1079fd3f321f4cac7ae196d0301327c7dbe6dae5dad63d4d16390b5a66ede93167fb71b1bff91d17d19baed6a5df2694375856c99eb1e7a7b0d1022cc27072ef7179bc10ab3ec2db193de33447f3ad92263db9bf0a5582ee2bc50aa947768c343d11eeadd55a5b258dd1b8209d2a40441e61a82dc80be68958af008059a3aee9c2313983975c472bf015831f0d3a9a6c0b8e75e6b9002421bf9c0d7d4a3ad73c95fc6ef174066adf6da2dc78edb5646e5fa4f4b54ff330014e7401947ec253ac9c2965ea0114ec9970a8678e137a31ecc79d230820f7d66a926c06bcde6d63da5fbb76ea477d86c5f9442b6fad3bc6c04be7552845618b709a350a1005be0514567e4b48d8cc1937f32f962fa8450541b1a6b97cf7a8197bb604077a0fc5e75da43f626d866fc72bc3c045b0e728eae2b5cd7c25c6171504e070081b591420d8251683d91b69ef95eeb24efcd2ea4888eca0dcd1e1606c35575504776e54e00e3682c416a25bb0e650889e75c395b5d3745fd76b003cceb78b100574f900ba6b907313b8e1a3e3911ee034323efe369cb42fb4f10dc9ddefd3e70dc2d7675edec4a31783422069eda787f42735733bd5e885bf5cc91598385b2f0cda66e63e0ba6b0b079e0f9eef7b281df74514de287853e3183b8926b8ab0050a4d1eda80b80bf7a8663eef022cdef9b49b5359cbbbba4bec5a8e755fb7a8e304d2ea4b9285fb762e8de825ef8989b6c79d80f9ccf627992d3bebe30911abed07562c3ed0df19d8078196705dc2e530d6168872697fe9ec82318f494d81b7dd00e89510f37fb753e6e919836f12619ac5f42922292bf68d04d0f669fc74570605ef1952e3c7d7cb4b5f230af4e4098e42c3fd1d0cbbff9c3d66aed4d385601f04912a00071e446191f8a576dc54b4a1843634de988cb5518c64e2c484eac5b409214f3aec60478be5655297390fe4ce4c569822c0d349cfb818148a5d3ad0930808f40d8faddb86bbee303eeecd980d70a6d326bfdaca3234f24cd1d5e94ea80ad49888b14ec6768dc5f1f2666dc1d0da9da22099bc33b2b64c5d757292399d057ef6cab0e919bcc72806b6be6c2de83f4a83f52f5bf390e69bc7c279c53ca20ba002ae8b978751d01388b2454a1027730b62e0f50b1052edecb9d4bfef2ea6075a93d40b3b241d26d26221e4c6ac078d3faa5e20267c5b90735fa2bc6a036200561bd27e254bc28a15e05ab65686e2ba2c87141f90606ed2085e8794e59a730eab983538ddec3eca8182d44d93212aa0385b7212a48d34199b9657a39cb6f9030e476432d2acd77834b896fa2667a0517e2cbb90804d3da16647255738e06c000c44f606d9cde9c614f02c8b39bd3de77239fbf71fd26abbf65aa459a20c340ac807fa8efbc36c879611fc9448c7b288be230156478b6a5fe9612297932dfb006d2258b39e6347e7a560d29c9aed2a9cab6ed1c4c02730f411be183e8746cc08badde9636618d17541656c2d3b29adb73be7a1d946372a385605581f7089930dd44d04a4b4630320d67b8300ad2abf82a09c49ce1dde83f5021a5bef2ef069079d019caf190f32e9b9ba45abd4819bfc0a2a088fa11f77bb34a051a041bb4004d082cd3a4a1ae2669eed564c0a5e1d1421617ee62ba61e97cea0f24f19572e021bd3283d07627d59673b929bf5a706bcdb54901d238c8a8871fb5deb842de60218d891f5250e89d63c78932d72c9996f017b528cb0a0659d95df403e12b88a0d11f2b408054408c6089f2618cfbec6358e8b50e321d41651993841cf11994407f09608a97ebeb79a1a02c4e92b8313c24e70a0e9ba5ccbedf26bd18d9d8152091e905ce9baba0dfd43c13d28bcac56cb659dcd5f853d465b9e065bd6d9f2420bbe12fc595918933a8cc6f0f02a13777c777ecb8732f8977abd928f4688764b0f4490153fda77c4802e00bc720b85ab986936bedac95e3ae1bc5c72fb83d8ee02b18b774c56d1b5e8a0137d153bdf16e39f239c31141a79962c079a664a47650e2a5a48e23e2a20a82f958714ba4b6ebefa43edf8284a66b7e9d950f9a38d5d0bed183ba5f6b3f328bc50c8a995ce513cf922a8ea65b9d62109cc35955f82de03bc498deaa2f0f013b64841c317dae3f2797eb0b5033f7c591de9c001fde03a055946124caab7773263d9d499b71d924e3cccfb713865fc12f88d7bbcc1d1a50bd01b25055b53a67fe44d11ab7809ca6c9841026b05e282c9ffb28c02335a6c0b33267fdb96528a4313a7a19a54ab6af0de42950b908236367b30180cbccb830f8445a270ba8696f91c51b3f99ab2b37a48c36bacc8b60e168ef7a348ede5ba0c6a7b0c86d53de5d3af4919d40e7fdc9a7214bbdd97abbe4eb5eccad9d758fa0c5fec0f076dcd067a1d699e7d5f71581ef1cfc9b810ec1ecee98a3542e8c949088419f3c95b2c73252d7ed2fbd13aaa3adbb10a6ed9f9e9fecee741f3ebb3890d8cbf83eb83b5351bbfa4787793e75c11d51b80ef8afb608d4857b1d6faebe4080fb1b8d69b094203f78cc2312aacee53986cdf23c96d3181ca6b108c10dfde062ae92258e0929d0ee3038ab0fbb70b0df02f78fe203249b86a4e483a54929f07a1088a5d4a400efd984742efa00cda5d0a5e396076e1d0a4c7a906fdfbaf820623f29772932b9f26c63e3e78aae4fa79cf4b569f4ece90658c82eb9aa0659b0f839c86032aaf72ee5e834277308162f19985729af0fc21f96a3ecfc91a1dd10dfa511155eca952b071550c0e067d15d75a19775ac744ebb4ce7e913532ffde020d37bd305c5f0a63cc7ad2f5bb714a3f73fb6c18946e49272abdd3513babfe03c9fad33965b53dcb506dfc7dbf325c308cd83e360b3361cb156998dc79900a02fa852db74398d27038e6a805a7942dea87c529bf7c97857b61c81d02b638480b897d8b8ee5d6b06e1e0aaa3375f28b9b11998f9644f973345d6f0931d26c410e8b2d06ccb2d7be1f80ff41a1aeef00255c9511b731a86e845eb05164d3046802636d2d7f59c2b909328d574e8f84238db71010d421ea03d79b353f0c5ad32a069b271d4ae78beb72e3afdf4403f376c197fb3814a18c135e22e4cb7032166d0c6034d6aa3b26049375eac910719e377d9c73cb91fa0dc72fb3c2936daa74260e1d81b82ab1bf9261be6ee42f1039fa785071cb474b540771953048feabfc700782f355e06a5e57fa4cf64679b53cf3f917c58c0d3e7c9b7dfd23624baf9784027b53b77f3183510393ab91b8820eea9523dba15c9e6ced65e586c878ee379a0d7f1b58ea05e54a386f40f3621430d0d39454e120f7fd426e32a69a94c5e77f0e2dc1a019eaead12bd794e4eba898d203bb264c3e8af1b5ceee4872eab16e7a0c7d4f1ae1c1a1e97ab4d2b2e2b9f9862810fffc6c406baa895372c7bf252c1e09bac4f20651131dc487fd60489dab1aecb3a1a1fc628e5777aebc4d33ae6f70068c27f5e80c3c0cdac9f9a3d7d5dd423a23dae655222b37e00aecc00d39e40f06db09f83590612bc8a0c23ace281018fcb84125229d05849ce22fa24266852108109ebde425b24807c09a4be7889f9f6bfd151a82ccad7fcc71a73fa6f7b22b0fb2ae4d7930ab842ce89c6286f72ca3edb2e5517f8a2b10740df2aa4ff500510afc4e7b75c1f6211896b68aaf84f83f371b04b440bc0030c4df498b2ea56193035800bb9d69c677ac42400fac66b716b40672337786cd61ffef2f854a1b2e6a030688ff39c18ea36e684034fcd31037eb9d7ce15f23e9b750a6dcf7bdcf6158061a5ae95770ee24b4631f5856b019db3ced63ecbd912ac4f4418c3275631b9a0fada3885ffbc3e77d411831ed026fb4acda588f776ea5425930c5a347e8906804ed18dc5967f9c02ec6211df275518215909b3500327c298a34e3278b94ea5d0c92d47f8a911ff48af2bc8840803bf86f8723f496f59595adbdea5517fca80308581e5283f0fbd1bbe2b781e7921fc8fb528ad5907ba7d4c47cdbd4c67dddb90c29cc743d57a5f6ccf5386026b46670ee547e8a5a572706050aaee31cd7307f0426af789bc4583bb52a30be1983bbcaf0477a9ff96d4bc4bd6574a2ed4dd17c0da471b0ad1a2fba6c4ed923ddae0e548aafa53228e298b6186008c0ea61dc850e29894f98dc0c244d7535873b4303d9cbda241380f2079b056f914447a946e405d0bb72d1286dccbfebc5231cfe4c60f86cf20aeed368d1b589d4758c5743990e100f0f00b23da52a24917fecaae9780ebf8968df5d2394e74763da1a4892ee033c9c73be5e52a9d77f12ec675216c33b58f29289978ba8d8c0074c26f11a1e0844f2eda5a7ee8d3913d3a22dfafe5d150791d3cfc8f5f5b8592a1c644bbfce012382d59f1f5cebd915c9e04e1304e2f083e45d42c5b67f00fb40d99bcdb41109e698966696873e672b60425d5cac9d8a8e5d77ae5476950afe5c635dc08c2905049761c0596aa2c5f320555e77bb411c835373aed3a8d64f988ce5d216e8c50e02111a005a1a7776bf2a76a11c9e47c1969cc670646745e7bad5cfed1d42430c -generate_ring_signature 590a993f96a147077c4f5cc32f91a7c802e654a5c8586e9598d0b6e4fde6f48c cb9194bc59756cdca58afcda4960aefc51304bc4f6ed04c6c2440488f42b6a40 2 87fde03c5506336f088a68e7bdbaca0f4f0e8a9bc143808305a7047194242664 8b7ef8a586c5feec0dbb2e72d8036764ff5da197557bf179192e31aad1d5beff ec722447735f098024e362a928af39663703309bdb68b3a5e7e42ef7601c720d 0 6cdb051db188493325b1e132abdd978ddee23a79fcf20b0464c21664888e31035fde14ea9c26dd22745afaf7cdd50fea33056e5f68bf7e16167bdfa2f3635009306bbedb2716fb5503048ec1f3182b65f1919a42dad2a46d7de705998b0d8c00b4dcc465764a0782f41c636643bf767bf325039517be584d7a65f12553844705 -generate_ring_signature 0f36247a48d15d7ef13a065c7b1a1ee40149510d7297677108020d7b2a36375f b0226919b26683f35354c80532460ae47ac0c70b2f18a7155f9582904058976a 1 24c4f1e4a4d51bab40ff04be8bed03b4337518300c1a0073bd59011b3a0fdbc2 3dba1bb1ca997825b955163be13e92cb7f823c13db9de96570a1b539c4c1f607 0 fbe547a84c9ee1b2f58e589ca65a0a454ee43bdc8c886a377daa9d2aa19f7c0a569df96c7da5b83c53bcf8c7d839700fc3fad6f162d59267a4a6d7415d911909 -generate_ring_signature a1831c2903f8c324b1693adecbe806b81454c15fef50af931c62204b3e135bc1 261a8904b68f5185ce1657f266f83cbbb7cda81229e313d568a15cc55cc86521 40 9f6fcf297cc2dee2ae973a51eeaa6e940388bbb75065504f449cca9b25267d00 98e264b0c531599a8a579074b2d927f85e0c8b62c437f6372267a275c4e10169 42549d197b01ea3544e172e32b102414187e707f3bcabb6b175a48e280ffbdad 5cbc67dad405316a18ff8863ff2bc32fbe25e08f8c291e790733e39e83c42947 dfab77a0540c2ae33cc85efc014987cdc19cc435463a61152c70336fbaae0715 3cfd622fd739f3eb972eaac07aebf78208c6558bfad030afe4b6b7af38ac5581 2625cf12b9544cbbb8cfb6c43d176cc0722487328d7ea20a7ea435c7beeaa4d7 aaf3c52ed69792553ce48eb77242d350a8cd10ca9cfef151c4634a284338c15c abfb16ed19fe8aba99218c2734a87c62966e7450f66592a47199615d8f08073f bddb2d703ab7e9b711ae268e900ce8ffc7ea314ba16f69a839c8784a22dc1cde a4a71e5b7225ca67a6ca194f5f650cee3ab4aad608f27e05c2a72d5ab1417399 397f57af3d7105156e93c47c7e75205ce4b199d3d668e701414f62bed9f30a35 aa89fc94e7ab1e9c47e2be820e4b852e2841333375c7181534c1d9f6338b2b22 12574a1031709e503b7b55dca440095b5ae32e7e49960df3f13af0e3545856da 2cd8ad3c4c1479a755671fc233c1ca15da6d1319d58ee7fb46634cf5e6d2c2b3 f72d700961cde8dd195b2bd34b8b33c27cd38ead4ff52cd65f32597d9f845c25 dc50f2aeb860353b9c32c9163d2f371cc9b1966ea4099b783db95615d1b347db 02f9a85d776b326a14e3c116f3e34706f831e81ccfdc4323500d28e525c22fde e46577f6631c36676b6cc24d154902c03d8cdc427b8eaa9a16bdf1600dbacc5c f61fb173a89280c2c853240f345f1f4acba37c73fab295c6153d5b0c84ff5f29 78d202b46b95461b1f0850005668ddc15e38757853e48bbe6f531deeffb3c5b7 b4fbd260b5c4d963f1f1467eaeebdf072c22141d53d5b87d4b8b98a162977021 496e1bc0694a985482d5e4deb35a8f5b048d3f6af8d0d12a8c7e8d1425c16cec 5ac968233f0006c4849db8c0134ff928fc34212a6955d2fbe6c4741f29ddd1a1 4d29eb966788a97031479b226bbc1fa8b8124ecda89c039add1f48095bbbc2ef 3c721246302983cfc7fc5505fe05ba883e54bb4b50e87eebe01a44322e356cba 634e670cd777a9df99da4be3ab467801b4d87a42d93e057fa798e08fb239c91e 7f972510a0eefdc9ceb8ad6bc4051d8743642c8899ad90f81882a972042aa120 431c662db985b69fdb1492a243237c0b6575a137065ffd665be0df48773453cd f0c20daf7e7d96d1c1282bfc919af7cee54795c544bac82adc854040ce5bb142 b7429f8a8bda10eb7e6993550f7e967b69a4ae04bcf1be65bdb252f0bde3f097 ed293f21c6a1269a56ca8335e5c492d520fbde3f70b877f22a6a780eb1109bea 0a6b31f4ca18c8cd4e1b6860664e445c32a314b3cfab312f7485b35701b8d033 ede2afc010140fb9b7e89924c1a3696d1883d3255da5aecb64f64ba3d471a90e 1dba5ccec546590f8a9ea6e91170f0d0c505ddfc6793b150feaf64637ab6dc2e 9398df9e62ad755ce37bf7d03b555316e7a3a54714031fc3630e997e8ca226c5 39cd3807d39ae3de4b43789511593e57e3ca4f44d3ab87de4dc50fe8c79fbf79 678358781b39ff78d276740e2c8c2cc482c5bad694edaa12b194bbe9dba7f78d f965175ebd6af2efde584f6bec3180fe5da01682603e7b9a54a610da393fda68 793abfdb3fffc1c2669b18d68e3239c0662766c5745a0f715fe9af5530fa6d2e 07a99a07a14dcffe0a3dbd5e14332410dbb5a035cce4a0171ed6540eb09b1505 2 5b8f2db32b8f2739071b6c4839e41001423ddf62c2cdec572de0e94865058c06c8b7a4e79c37c20e21a880642c9164aa532b95ece108f018a39ded89e48ab500b832b4ad82163649b2bda47002ac8054e36831a8da997ea155860697ed2c2f02e91d69f842ffeed056a28be2156a542d458d59edb1346b95ed1d78e7c04d7a017a1715710122ad02921df682089c2b7e9b61b3cebf68d5312585ea7cc758dd0a38c80238489d00dd2d200a7d789014d9424cc53c671560f89d499dcf37aa0d08ea9ae5b3dabceb342afd551006a735a2dd1ca5e4cbb34918c52f20a5629dae05b3af34d91d73878dc794e19927e7ad88c70e3512d6e6874f4620acbd150ca70c3f8581df9f682aa7b4ae93db7a6b2a194a7ebde275e04c13702c26df94ee7308fc006408eebf1c39968a0cd7dc6df99933b0ca7724e9dfc7130cfb26f758120e9cd51382c2a509927845f7807fd06b5d5b0f6c2baefcd032971ecf225a4be00a55d4cda55ee6ef81e77cbb78c4246b30b794351980f591e71fe98f8355b08902a9c93fece9febe0488b9d1d0ad3b46c32e1b11d02bb83a45f56f1fb8b1089a0f8558c1e58ab09760751a63a9f1a34eb1265edeca6516bd35983f8deaa3c14309300a1b6d671a73261fc260963e438cd174b3b10883097c371a6c5b534a6c890f0107111e98069aed13e5dcd8ff3e8033a22b20a58356eaa173e3b4c5629b54048faa8c4564ebdec1a86a22e8efec9a7aaccbdf30b5ceb73dc19a913e4157aa07be61fb36087ca3d5b3c4338254ae693b9be72125579e4be6887a675deb034e0fa0f93342171f10a6b301d12591347138a4452f08310d511d77b2c86db8d3b50ba8bc1022f59b3f0debb920dcfa6ad32829f1874e9440aa3eab0f49bdb7a1260b645e56fc93792ce94a830a2f2455161fffc7434bf6415a30311e1aeb2f908108f3886d6dd8fee7f359b241cefab4276258ef836503d7077e7de9595b474c500b1703ec7dd1bac6b0aebb57a5b7fa75270706d21b955685b4c50e36a2da0f290263b76de4df0c17114a3e909b8e4fbf2a37cb0560ff3a76ca8df82972582e790f1d67a5f7399d4cbe094d2dc252de3163fee75bd9ab17a838d6992bc3ca0e9405f81050902f13feb35d50de7d1e3ea9ff282b78df49a32c70410849465a91d00c2b86fde9714474081f9a37327faf111240ff7ff81dc5bdcd3d800348325e0d0d281385d2a07936cc43f3ee625e977005f82ab07cb404156902dc8de21f5871052210f5a15bdb2b8f6224d7cc88980631bec59f756427a180242becabe1dff400b838df84724e49938facd892481f18384aff124d065f15a4a16ef3eac27b630e3a76cf606855bbd0e2eb097ac4360bd004ec2feb7ef9e12cb7047ceaee19a70106fad75757b02894f017dfb2328fdc72fc2c64a1feee065cb781f4aa5a549208c1081f6a903abb309db7dde9a1fb4be5db81b80e93885499cfcf20e263813800ea2c9efdda98b3323a6eee42676b4b7577f4eef37cc8b06fbd7c855472f2e305b5fb29e247ceca85b4198e563310093713f6101bb1b2f03b49181f8d857a350fd197d6f216250b3775ba694794d122a6277f0e1b91cb28d853e9af4edb7f6e0950c6d97a4d8646baf4667c78827caf90e9f948a131e051b95dc31a1479eba70b3e9664e415344fdf746e8fc115261777209f8208a0892167e6e47383776c110aa66e892ba130e61b3744e719cf4b503f81920537169b8a9cb9797516d80bec0ab00c7813e985a970815ced3cf3d154426870a2e1f9f1a1c5151f25f54474080c81ce850ebb25accc0a9d456f5cc5577469b8653a1ab5f2625d906f1ff88a410332c99e06696f5037068f8db6c96efd4d8d54df5b1096b06ec2f85ba1b1101500391ef6956d235c8b5c37aba56f7446f67bce0a370492ecf0883c9949cac27c021230ec9bb86491716fb65fa7aea9c6e2deecdc85d9074b7c875839f359288f037a9e339186e2e690298ad57c95662e2e2899e7f33ec32f06b74ea2c3fda22801f773f2ce3070338ace22c3feffa8cdb198c844fdf4e2bdc7ccb546f0405e6f0308f7ccc5facc037727078d9645a6c8193cde45dfadc2ebfe661b030d76bbf905a2c1ace08199c5dbc5ca671f9b7026b0f6804f4aa77c87efa62fe61eafb49a0ae78e136cd1b24d73c7e94d872134165f6cea6582fe278d425bd2df9050b8c802c2e713cc06358da730fdbba92dcbdfaa0f72154cfe48979f5d5e1afd3b64d7042ef74e7f63fe2b2b25a039e1351caaa91512d827c763f7445a8610b900a5890e70bfb237d7b5341090da9a954cf9010e26031bb8248327173b15b82cf87bb708e446461c1e7d1198f02dc08ee0408c7dbe648eded6846960745d619c0b2264091ba71ca5760deffa13cf5b07cd93960a07ffad6f0dac9c6595c92115f2740b0a6d177f8747b61e33ddcf107edb858be6e143c2a72d987a3d75a2e37ed750b2046a054dd740c021aa622816b2546e565f05f78fdd7b295db143a81a038403ac08158cb03c7f741e32235316141a659e06a0f7da5ce18f6055e9f2fcd109735b09b3fdfbfffc6c634c7d76bd0be666466cba7b35113e561c0cc445ebc016c37901062ae0e1a5db644250e32b5817e4cbfb9cce8e5995af01905a95b23808b9da088f22ed2488cf7f64779ed7dfc1bc2f4fcca4bba8934bd9f17be1d26898f54803be0317bda7e600adeb9b4a2971c828b379243676a23947fca75580c3c8667400e922a8ff1356832ab1563287495bfe1242a4b2f5de56f02b9e2af1173e59570bcf4a2677469a88b8de816253633f344ba7239a8a628754821998322fb01ae30e2957a883295145ad4b7e3651cc008e6473d00f0af5ecbea249db9e7b10622505dad2eeaedd3481852521cfb0d61dad9ef797697f918e8744d2ecf37948a2a003e18b4fb6d0dd1e5c6c45b058178ef75d4a6467ea2013bdf7d5a903366f382906482bef4bc476b834ed11a854307d3a09d0d06937d07f47a11fb38a5120f6c00ea507eb0593162071fc570e1210769acdf2d19e4e7864c188286de498336c4503600beecbb9669fc81176c471900c0508c63980690f623035b380b942db27bf0da5e1c34d5a2386b9f585191ff59d95d5f2ab94ab453e71a94d79074eb9bfa00c448c4fd19738c9b4eaea1e1cca372b10fe7de4bb7ab6d9c14bf58f1ac47a3509f7968fdc46d86ee9d49bc132e9c7c6d09330209cb2751b9e9838e0451d581a0b4bcb4dd8d264e5cb021f604155df24f375f693e406e88f4e9e3fa935eebcac0cd5c2a37cc33c741bb494676c710484f00d5513828da123360e1d4b6427294900f746e74e783a85650b27957a0f1fa6ec828e13b5b702eea4372655ed569b0d0f0282f2d11eafb5783b01a0be4b591a1ad7c24b62274502ab446d896fb59ac60ba5c2101e9d0cf5854cf942a41cce01e2863a492b750c773d6a71e589201eb80d2e0141c0e5eda8f65c5a56b2c3c44fe2eeee59cdd8a2440c69a1f9340f98fb09c3b380fd7acb101c204a652d001d7329d154eb37ec91cc481b94db779f21eb047a74c1e49016f06b0d3bd5a622c8e097dda9ddab02f6da4c650c499f8d1c820f -generate_ring_signature b9ab0b10afa5860d279a36ca738754958805e5bd3290604096442b8a4dcf9730 425b3eda70237941990ea68b9889230fb9badbbccf08a74b7c4a59ce552adb78 1 a15898bb31142d00cea651b265aa4ee7e73189b383c616eb0f6e37ed23ef7eff 84c6db89df73d478f2725b0ce36218d81fa68d1cd5ceaa8a1f8f2f10892c3005 0 fd75f1cfbca2b0c17988fb391db4fcace441e58ec3610c04fbbf4e3ad7c2370d43398ba08055cfe30bb7210ae3d1e26d580013ce15e9a7446b2c7c1815e25700 -generate_ring_signature 77f35661d7757d7d03c765f11350f4df7bd28708a37163e7520bd8c8fc622503 2b129b323db1ce4fdb915ebf2ddd4be3ac0798b4f9fc3e7188846d9993764fd8 10 ab241334d6e35fd6340511d9c72d5a18159c125336c76cc06eeda30ab23ca126 80832676389cb95756b34c8df992e88936b79d89114dfcc8da1e6eccf6320239 c5316c4465366ab16e1922a835d9a8b2f2ac89aa43e2f6c254c703b7cdc88519 74ea52737c3db895c2b38faaa7d1b47496998d4defde5cc72d070a15d622908d b1d7742def18ea0edf1a0bb3931b3777d85902d969b9a2a4ac30d997773de9b5 8f6ed97932d63ac5c74dcd60385536f4d3905e5697e6c27ee1fbe15edb0bf7e8 be4a83371f1d524c26abdfa898e92049d4f3471acbd5fe9b03cdce08d7fb3f4d 3d1d9750e65c8d279fde02c4c08a86c781880d3747b0d1a9ee063bd978c0921a e9d2250439b623b02e4915ba6f3eceb0a74bb7af6bd8ca70ba49e477f69f5cbe 09aef8291d5f369067a350126fb37112cd0550cf6534ce110100066ba0a8a7e6 7d6228970998328079db9ffb11b5340bd972a39cb5fd090f400fc2f4d9261008 4 2c3d6a3451c7ce0426a5704f312990c040758d0ff1309ae349208d4e412cba047145b28c3fc4a1219031df5b4f265215c2bea50861760319b34c0f3b73c78b0c89826b18249320d04cdad4fff35841220e9c5c26ec03ce569e9cc796bec87d01dc2abb4de17cb29d7f1c09a6796949e8a752a660b3c723bf709afabc9df91e04babf5af074d528775d583e0b247943be9c582251c902e973fa7297b4de0ce002a0f7561f9de2ac6941281dbb0be67b4dde21383b79588c6994f0912e1ada5c0571bef6138c8db1fa6741de3dacff01d93093e3acb8f2fd218104c5fd461b750d80bfa0fc47d230bb2e0dc16de4a585d64e8210dde3bfbf4c57748f9bac2e2609e97a20deacf6cc3b96add1478622cf5b57bbee9f718f856eb645a2909437f7022e8f2f09f3b3245e574decd65e55ea716dbfd69c10baff663ef9d1d8b9dbd70aef65990acc1a94470a719b3db9f37eb8bbfb87a0cec86bc827c16c606abad10a813caf397627ffa3d70ec8729e7b8f194c8e79cb63e72a628204a7dd96db0b0fbe815ca9dfd827e343c845b6aadbc2e99a7567bb22f3c408b8fe33722dba3209b04a80d481492ca510e76afa5b83f5d826abb5b16ed1e286e5d3d9dacb6b250d7ec4ab7b65ff6d7a84635b325c7e2b5bd5d579186395746b289abe3e5d933d0bcc87ef949cdacfda5be3b111ecaf02725c1c901f807bbba49257b4b8d1e35800f34241a9f56106971af83837b87c6f95dfcf7cd2e55ba0d644d41d614a0d5f0647246a1617529e4af6adc449c7d3fd0b599d23fd0a84ff680090a51eb821ad0c61b2cddef7e79ec969b4c4802e709b4bde35d392d3557cbf6e6de2502156b90f627082ece6744258443f92e5a055577754bea0b6a0c0b44bf2c4d7464882f80a -generate_ring_signature fe0e906842bbf5445baa4ecabe98dd6ef27226ecc2b5ad9834e08c046d0f578a 0e4320a4732e89ca8a53cea98b44492a77168316f9e8ce95fbdb5197e9d14d62 30 b7258c82c9b3e0adfadc33a81ca09cd307e70ba065c16144f46f552414a9a629 54d94e93bfa71f1ea7c1d64902bd8df7602d7687e43b983ec3ff6ab88b4765f4 637d164f669b1111c41a5533ec52a4b05a515ee5f46f74ab8e50cc481157edc7 66fe4748dc0ea965eee04b152c0dcbe2a5fea11333951d568491103296730f02 acb9c2c966da9415c9b09bdce3492e7962d10ac1c2a5e46938a6651e09fb5516 c2d7dfd2f498249427aa0eea3b192f4802a3d212f7fda58c6961e231ec0ddad8 c830fcb8aa75343dd3722955964dcdb9864c7922b872c1a209f477e923944379 bb23e3cba803bb4750739171050a55f31d479ec0ba27f4c0fd5e8a207257fd29 a6afa3958567c006ac5a61fe4d40b5e34aa0f796aea13c2ed3866a1f12fb3e0a ad6765229bf2c6c88971c254e49acbe6cfe0310049673e1cdf346cf09da3f56d 0edae6331033f401c42e320be461975a86552f196f495b3a4e3b0f547617e963 a920e55b3c7f3a4a8653d75ed1cc731e31ac880e175959f6e267fcfcecd22955 b4d509383bdcfd434c407c5070f125c46a46733da20a3f0fe9979df94c31d5bc 25daaf9f93a6284f702e02395babcc7598031c6940e7914a93a8e9ec722e7fc1 2f1c7e264b1262b445f4178156119b3fecc1f458f7044cc4de63304e14c97587 a487d33950e0d251e92691a34a583e13aee472b4c841843ba5fec5b3c7656ccd 48971038b01b0fbb7ed269349420385f1231bca51ce21913572ddb33162c9619 f222a85417740af8ea34ac66322a4f494ca309d7722ffca6713400e4be4fef81 696f40d88004d1a539495a61f35483afbde4fb225483f1bbf7c2f82c7e59dfde 9a3b1cf51901567f6e504c0c8b08a503b8cd633d6f542c9020cc4d238e6c00f5 3fc2d5cd18e5788a96f016cbff76554765afc0428cb260219b81dcead3edc978 ab62d587fb8948401a9a3cf874a024457791d3f2532803e39e00b7e522cac759 3dedef9bd6efc320fa1bc5dbf0b0313244efaec9a7fedc18667bed47a1e97af7 b0342dc39ce8e3f1b5053747f6fd3a450319d7568779dbad042e76b31dc816f3 8b3365279c15fbb8c7d2758fd0de17503c48d87aa9802593c22ad59a27d845d4 4566ca51354a7da4bcaf7f457bf803c6e32ee109a939a69c342ad61534831c44 da8e0ff478e7c6b7fe83467d5a5cf90fc769db0d8230eeab2e79424c262229a3 2cea0bf3ddc9e0ad18757bd6efc37a6e5923dbbb76d22a06463f25177223f3cd ebdecc636abbbd1fa2b166bbfd742ade21ad6a22dfc49fd667cc807b8f51db2e 5293d8afca91f70430a90bb0d05bcc4e6062b20578ff2cae721c767b6f929fd8 33c6805f3997d25bc420affd33636816459bc9b8cbc2f2c65fe2ebca22879b0d 8 9eb3443b52a8d541fc0dd9b4f1d0807b9bc5e9cf6c79672f3a53960fd84ca10445788f3acaa4df522b14b7bbab50e7b0581e1c297bcd0f7f06d43712be225c0fa86af386e1117928a5df4e73746cebc724db20ba3a1a835fd96419ecb851b100645980ab312a193b5c480a684edc9febf18000bc7306b02f0a2671db4bfdb400aefa3d9e221e000cc0c121daf7977005286baedf84fe482faa6276f9816e1300ed0c574792c6539bbb812e0e9dd9414ec9fe7e752b1ed20f4e8e886e29514406ef65cb982a9e2bd4227779549121d5c1863bea2eb2877e656199315cefbe6305c2689dec309efa27d6bbdb5b9bde168e1b747b7606215bdcfba9950f31a6850ad45d4b623b30b09a350cb710adb7b7df91fd7ef369c0f5ff8103d1d645e9c60c2920bf74cfc36d58fafe73077cbfa74c2fa20361a8b24b44835f0f6d2426e90beee9356d310392f802c1a34ab52ccfb3995504546eb3a9d8b6c5b8f977f3180800b001e292a25cf9de46b3462dab637e9a296d16d034d5ed8d6b5d039ea2670c071982eafd2ae5e692b0b1a66ecf016193e2a7b5b7ef114cf2524a4623251c06acf4780972215c5c375a2a2e4bbe637f9ce7858a6fe084451389be7e4c6f58081ba550c3287f36974dccb9433379067e7626b7ffe231f2469f299dc645185404dd04adb1228d303c459ab917d1237d0838d2171b7b26161b8d7188ff18d8bb0bd82441ee7ccb2cd324320c7d72e0e5aeb3c32aeb807600154300868bf3afda0300b49e37e244040351c6f6d2be8212c1717b05a2606f4aed304f18155fbe7e0faf684f2ef03ec401f9f0029a52dbcfe37bc846eec343eed97ae8862a3d385c0937594af033128137aef41f56d15ce6f35fd0749dec9d8d7fea8693911234e30121b83759262d5e3a12489d405b2775e072d59a47fa5c7d63fdb9731a77438a07298e3093753353ac9223827fca0599a725cad6c8a1f90630fbf50dc480211100240c4706354ccaa03d4d750caa9785276ef60115331dd1818bad57b45e7461015d3e97ea37e852dfd8202f17b5d9b4ebe72c5d3bf75080536407786a042a1c026a4032e5d1b95f849bef992006147750295329c7892cde5c8938992f0835c40164722b07b4f22823d2fca62826430024f05a251e38cd26dd201e9aaecffe2508440ca1d438be15b7d3187d4dc7e8cb8c6798a0086a81d8129d13b8be9087ef0e8ad5db0979ba37249f8bbe11b22a7ab8320550cd7713c94d36c8520dd8758305797ae9cb593219b6fc8ebbaeb3a1f3860d041e79b9f733756f463cd8c8adb10e8d7a1b9ca71fd87c0294d7f69bf2167356e10c5b5c2f490b4ec1d68449a0b80438b1e5d00f74c31d342aead66ac2a17578493674b9507f76a234ba886666ad029355355fb95489b89682e35e9ad288de2beaf33701e7bd078ec8e9e0061fd20b2e730218958e2479f256030c68c1b97b8deb2a3e2d77bccac3cdd07d8fbcef09eb980a87aca6032ebb34dec70846acf258dbc147c0071def9da0aeb3920ce90ea268ecd38514f199b34574f1af45ab62279d70e15a9b2007fec23c4ce336440c965fe7e2728bb8657ecfa8a13751c8bb611c01d11e9023695e87a199e862270b58b536ee8d2f801a9dca33d7f48854fb432cad6923bdf95af59d4d3fb35b24018a181b272c6206044168b0719da3f473d05c2a7fe2c0254cbb3a0ac51990700fb2d2633f7bcd569ae7146c0f1ecc9db593c4c37cd2954357a469967e393d8b0059d0f147a4b628a606d96323f4784e9b1e923478741d06d3cd2a13a709755501e6eeb698a242e14693bf8a7450f7a8774d7aa957f7cbf01660c636141c657d06c19dbe686b547ba6d62dc3c59ae3f5f1d967218aec23fe6b494def21115d5a05bd1301a86442a380152c30bae6d623eb85f0dd33634e231de178738a5eab10024f03f7700efb88122be88b7e23e5bae2d7d9ac36cac0bf308612892c2a09fc066c396ddee1752737b7a820bcd35a681ec7759a3ccc9c0b70f47249fb9bf3640870abfd5345feb1f034cc276f3d3c8b250905f1931bcc0703a66bf6018787520d6894b58beeff41777a509076b68216c09cd6f4a5a8fbe9045500ecf3487f1a003779919555001e5c1749736bb73095a1277ac1acb9859ad4031ead3c7c16b30bfe9e82574e5736a0baacf56cbd6dfd3fcdc2aa3a0d4010baf1d22fe4bfcb4b04dfae9b5e7e49ef88ddd959c23327ded194035f07406a21830f34049db6fcdf0047236fbf76defeca1573450e8bb6f519937d105520b854825a04d150301033009a17f81175d24703c3e5da295be2ef589a46c1adcf3dd0e493af7900241ed805b9f4f8ddb3250abd1c698b769f00624e85c48f95dd5fe0726f19e2c1f0515f08aa59068ffe6eec5f3862536f41e69bd7bd53a5cbf6fd67f5dbbfbeb7e790e3040dc97c7cbc0e3f2d574ddb27628de755d6f7af504f0fb9ce7b2dbb7b6b45cb04ee948cd0faabcea3e2d23a119c4625abaa4231814c25ed15e16d893076e2530a5fc5b2daf9a492dffdc587555068c8efa9c36d64476872e70be513b06638600fef12a4a8682b371ee60ca22b60669e462371d04ef987b4d2092cf44f15126c03775a837f4028bad9749b4ac9eacdd99bd4b7996d953b5dc21ac15e6566665d0e7e90b7de500076c68ec74ca34305f6156e7e34017145320cca59c686eb52cf05 -generate_ring_signature d44366265cc5e6e8a13f3d2f55c79baae89befbc5ac99d063e6e0dec1d5f8665 345925032111e3150664a24dc32937232ef1bb457d164c57c24ac1160fb7d6bf 1 0875f733705d14f8dc398a529ca0bb633914572d8849d084dde057b82a946fa3 f51168e99b11a987b49c956969326d5ab4bdcaa1313fa7ad146b24932208d607 0 c5f770668602662d20226301fca255f0df8c7b89e26ba282ebc79b94364fd0035638e021b0ff2f2f6e92eba40cd775aa6f7958d5d9187180d37c090ca9cc8c0d -generate_ring_signature 9b43013f26543ae86b8194bef3e6999d7a787ac7d9e69375e117de5f6a963a83 ff20e8a898dfc4e8c3ee2864418a757fc9c52a5a6941bab0c745d23846a4a0f6 5 551ababef74f99377fd5d68b2ad3a2bfe14ec6b4afee4c39686508184f87cc8e 25d84441eef268e987eb34f24749b4a13044e341e6399a5cd6b155a87e6ff0b6 c3850ee99faa03c06b6574ec52d2916e32b399aa82286fabf4936b39a916e431 42dd11fe00b24c64342341eeb6643d229ad1673a51f09192b3f9ab4b93256cbc af4d559997026bb67abd8ee6e469dd35577dd529ced3a657726d0c02edc3d5d6 b34b5b99dbb26140e778ba6fb61449721e4c7c8c1bda6e70eddbb6c0672e9e0a 3 4865be89e5c547bb98da5822bcdb92a85c98a2c9aa2eff5bf21f212f8cfeed09c1633b725947aa069c4d0842ed007f9aa8f3fcd3df6a232fd20dbad713f47604fa2b387df7262aa30456394ddd3d4972d7901ba8c54d830f005a87daf3988b00733bedaa0a03868b7cff21b9f7a7b2c5eff79fe08c249f7a119868236874510ea0f2e7e0383c97a7e78f43555d4ed5f1a81191fda0533c0d0215bb225886af097d07312ee870127d57dba00a4bd7e5ec165a53f7c916430e6e4ea1f715a2e3011a46c2d425cafe22688f639e55ae94c591c94056d102a771a57cb855b306fb0af181599841542b2548ab2b14a3ceefdfc123c0d23ff51debfe131e79713cd70c650c7acb0249389c70087564d0340eb8cd8341920ac71ef7101c5d05b76a0d04d6c9e3b7c4a609a6066c408127693181288fe3740758bf4c12c0d7802188a40d -generate_ring_signature 3c53431eba5c16dbdd1fb2f98d6388541b7b85cd5dceac9238d6aa7dd5e8ea8d e9c7b96e13b6225b03c9c774e90d2e3f71b7be553dfbd22cf1767888c4f83b45 16 70b95fdf50d68749f55586737433d11e0506886dfcfe08a82b5b01c173dea87f 9a8bf41dbb914dc402d80b6f1510e0318fb908181d765b578776b034d6359ceb 87a438d3d769aef03c070d3853f17c4e470a4a7ec5e4a118a8f00bb0966490e3 0ff0242f7714946d5b872f9fe6873d237f1f4fe501b88119795f64f744e8428b be4c9bd9231f25e1743419a2858c014ced12708662761fb36083aa29710d1120 94b62bad7ce972fc7ee236c909cf19203dcd46fa48552d86f85a2dd6379e4e6f af00070c55a10d7ea2ddd2953e593fd5cfb29eab6c6b676472d029152120fd95 e839d97bfcee72acce077bbf20856232fc8e31d27fd71da70a6f0600f85dc9a6 f35bd0d64ca12db2c9548255626727e57b4eaf37b42caeb5c4160e5053b54903 7b23cd36d3ef9cb17d8afa4346fa949be91492241c2eec7d02329f4ef37be3cc 7f5580e1ca9c5ad57941951ed0de16dce9a031761dc476b0a3f4733bfa76eb75 6bc5897d154e23c56cb1216534610d1d75241d7462aa11b8a80d6191dfed1354 99884db56a229765f038e573fb018af4e2c4e75e9750908a6dfcab2d4ec7a821 656458506acc81d90dfe82decfbfbb6b5cc2462b6e18ec798b486add559ae372 0d165d983a91a7228b44dd25f84ef8246fca9530a6f7e15924c102d4f4b9c038 83169f90ec014494f2e8dda969dc1dd5fb60f47e3a4386945b787928e56817d4 580db78b04c538d490ba0e0cabd646e357f6bbbf3752f0ea350165b3fae41a0f 15 8c8a00287044c0b083c3137365ab72cd2611abec4b7a7b800cb473250ab92e0da194616bf7f76bf5848b0f64b800bc320cd05a8273cb58c0f06dc1a117f39c032114dfb4ba47c7fae3bc8fe012a9fe0876322330aaf5f60068ad44c4beac2b0d34da67ae15b691339c51bf8fc0ab06eb480de096ba989ec346b98ca5b7cdd101d0371834480b2ab2aa759fbcaba7c21c44983d51998fd76782e4c3bacab6a409a9ad50b32f69987e9cb1e273117d2f1c64f540f93477c346135a7cc8256446073126e28ff988849a5127405768b21128399f0d4bf018e3db08508393a1ed8705378e795153c9fc6e99cdc69f0cad6efa1b932fa7a317e6737e9d63e17b87c1063577dfeef974e82a74cafbfa84f1c67b0b1055ae2fa613659ff34a088990ae035345fe0285439137c03c72e234872875bb31baaa249df56f14ca0bf0b9066a0b1af02147f735bfe562d207ff3b2a88e91f8f11dc5ee6c0fd769f2b736159bb0fe36805a6780ec62a4dfd121fad7a6ef5f4303302f61cd421bc54b58dc986010c550ad6ff49a1a4b818547f5550e1b132ad887f856a60b913aaeb866e22cbc60f31649c227a5597dcb65a7e391fd2996e87c72affe6d5693a3de90ec5f8d64c0167ea55430816f1f85ad67274c235bd428b0f1b3b76185167c4b7b314f39b3c0160b63afaf1d130495e677b1de3865bdcdc06d22dd14612a764ce8dc71cfe8f0ba4026efd5f13da7368d45a8879a4c0f06c2a290ce3eff4fb79465d3a0bce0d0591abeec4615e0005edf47bd6f73ed4935478d8a52974665429f0d464c610270bcd1485a8b67f7141723e9d4605fe047925e8374d6306fc977c41c1473250230b25a480cb1a318be8cec94783d0d9e8c5c1ddebc1ce78b84728fb36488ff2020d1d4d08554d757b7595fa336d0cde8191f50e471d219e08df5c33293cccdd680eb34f81b5da93e1a450f0a3059f238e4643ec59ea19513d733800acd83dcd0302259cda4a13932e4123550795d63770af8a6533568c78d288ac95626d77d4060e0bdee47ae8db7a3d2336b495f9a3611d69659d5ca7e83c3e40dd1286e6cd040388a27da7674891c4ba96d02186c6ff01564c927c691e37e0056d45523ea1db06765f8b6900b11aa51fc24ee8580496a5718ff21ae9f5169575808d917bb0b10a2076f2f5ea019cf7602051e9bba0c0c4fd18a5ac79b1741fd5f823f6de57ab0e5d8356634c1202c3c1f8ca50fa8a7bb64a286a46f8e322c92fa64c93c739080942f261516a1c7d6c97037d1dcb5f7744a6fd68776309e23fcc4a8d354a658d0d67c88778ad32280f0681db5101493cfe2b73b9e12160b73ed918012cd076ab0d16ca85c95fb3cc9bc8a06ed2aecf0740fb17793fb62682dc8d05591a47ff460746ce19bdf90049732e63575b18cea1efb698ceafbdd73d5758973bd271cb0c08 -generate_ring_signature a2848f3b36302284378d0f599c7fc0b7911a94281e33d4eb4d47f7554971f7c4 dbfbd2c763cc046137ae025d4fb22914e8e7182f3cc727d2177e5670ae95aaae 2 184816f5034af3d627d6d835e5c2913953f985f597b11370c779ca34048fbce2 85172db3a5e5418276b0b57bab2962b26cb6bb159f3608bd31d5eb6fd507b799 d598ef43cbb2381ebc6a1585786dc1afd6f3b2fab6ec7c7c020b934bd44f8405 1 e5e814fd7e2aa476364a67c34b32928e97fb3184e1bffa18327a6f3f087de000ba044964f187570aaaae6ecab02dd2155a4b35d9b45afe2d228fa6b69d72e50e83c8578ce076eee06b29cd26dbf14527aa4ab28224e1affc226fb73e1b5a550a4ac4261c5c1a4ef49a071912e67b2aa0566b417073dc3d09fe09bff37d109302 -generate_ring_signature 3bfa708865ea752237a4797383b6ae2d4d3c41482ab53d42ac8feba9f80b62a2 2e4d774207848de566e97eea6262bb6ea87dddf04f5dcf911ce9914e9c97f528 91 38f7acdbe90c284805550b51f1e9929022e7d533bfe75bea0dc33f717a523e4b 817262805accdb4febe4c20a8037f50aa1fa7a00592836fcd3d213fcaf022f45 13c55f83cf89d5272dd1d638e2b3048653167468c11a8bebd335cdd72e7158fc 71d23b3dd7bcc81b2fe24b32e9d3e57d638c21ebd4039a73c15bb2d6455f3b8a 2d30c73750bf6f92c3bb59771cdddc7d0d6e0dee4d4ef60b443224b418b5689a 5c0eea3463693e350aea35b9b75c918ab6782971cd4aa830180fff60f6084ea4 72d4ba8604da472325d32ddee00e21d844275c3896911c26f94038f30a59b305 2d09540ad3704fb6791fe07161f12cf7c634b173b4348dc2fa2b2d91b5716d6e 6cdd2f19ce8037aa1d0a1b933e2c619c65947fc732acd11f115c62d40e77ea41 db94bbbe5548ba971a53006889f253361d986258b9a641737b88d6d1c1a83aba c23eff960fa6ce574cfe1f74248c29b10843be110f8321641c2147baeccdda1a 47b9f3ad5306af76c7045c93b914dd0a90370ff9e3888129421ed8b78d5b6725 b749de5034ccf7c0c1bcc7a76fc3b71a90b395f8ce74189101c8b78dd08f176a 9c5c2afa18b46a710f5c2bc387d61eec6eba8c59a00781486a5504fb35b8e46d ea7a0f8cc1e7896b4de7ad84a8f2223c1f4cc320e466ddf80112b01e9f7383e4 8408d1626f6123a3c60271334ab9ce1568f4361c9e8961a9813b2f82947dd0bd 5ffb6207a836af8db9df4bde8a1fe896f864adb6d4a1699cebdd134250d3627b c719c4908860d4a4204a100951cd6f6a5f523fb1c25af8f4e1a7d91566042583 1588e45a7cd74dbe403112737070ea504804a951b592d86308aadca05e19bf97 2bc329ac65a06e56aa65e146f5ca37b15c707a1a856681f1b6438b876a257a62 6cb6add63061cb03876eee0de9733db3cfd194b529b92638d74ad1e3197ca55e 869f1663036ae025ed0696a551fd6153fd0eab16437e4f5ebb740975530cb4ea 565c6df71b17757170cfd147fc7ef7ef5442d60f420779de08537e4854242034 ee1361f2ed36794b34210f0084ed1e853d79403287ac7e422a9e2acef5d43171 396a2402d29083eb8514421bfb8b841f699d607565131c44aa72680c47ae6cd4 bf3e71778ff37c494d0d588e4c240a7a1fa28dec561e5fc95ce52d0795d30b0f 0b1c1afd2a06b982d99df49358e06aba690af29ddc2f1c6d5003e6b44b670eb9 0967624c84307d50ff5823e51dde55c2a0f4dd3e54c478bebfc74567bd7a1b27 12a0cf3cca9791f9d9188e21f8d5c4c5632ea737e4a548a29c6116009b1f583a c26300d73a72480a4553d325bdacc91f0c3b10b7dd53d490cb3d9a69ceab15da f91ce4b28de23f339c6ffdd175ece69c2718e2a3d9b22a80961f6aebd990f09b a5b04ed97ddd4a071ad49063c159c6e1e5bd53be621c2103efa9944a504c430f dd1ce2e7ad25135322058928f7e0176d77097063eb384130b19e30357b4d1a31 2987d815b4fa2b7214a0eca3d95b3dc8bb79ba433ac66aad97f3e5ecf8a67575 dec31d9c26307ed50d5ad50c5698b88d561b3732303d0ab09f2ceb106b2f0af5 7ca0b73fd6dc919d8380b22294516103ac26831cacfb46e0907223290d5ecab8 aa97e38b8e804aecb8e2c1da77776e3d0582341b2d39be6a45f8c513c99cd5f7 cb32604020edae3f1ac43f49a50364a09e9277ad9481f1632c7e446ff24d376c dc66cd2e4cbdd497703f1bd0953033cfb6550f46412250c4b62a8e44f20ff7e5 d713e361ad8a2b8715bc3a8bd0a64cdf4fe19d6f4bf2c20970572a5aed81c14a 1952d47e81a3e2a02d59f47090da2972b89d8952d498bd95d24256efbcf56fa8 33ffff80dbb6a235d0d6c0cc52dc0dcacb4a692cf09cce398423eb8742080853 1e328b3b594f3748288881bf6df6c2400fbad42e1dc53099f98945d0776357e6 e45396f2fc37f54c9ca3af36970ca0ddf908dbec065b232678d5fdf9cdc115cc 369cf4a82d27c83b295dce181970028fe53d6265380cf87c01819e36d1563ce4 7a68d3887599907fc20337d5f7b7fc4cb4f88dfc6759a0a35bf7a2a077659f20 0e579c35139219430eb86bc0da2d37f269893ffb6d10834a6d04f0a7f283dab4 52101d8a151e0a535bd2f5fe4db6d1826c47e6ccbd8a06d1ea8e05537fbf421d 3cc96ec853bf5d99eb69530adecd6aafbe9a95ba14ad506d33772c9de1cfe1f4 0ebb479ce8e81559493318897f9829cbf14499fb639f99c5a6432900b955befb da8e0e7f63a5f460d64b71a38263100afca62d6ddfeea1ac0db0a657f1296af3 119a595ecfa1514529f07f7a90cd78d4b7a98544fc4ed0141ce1c7fe2a9acee5 d9faf2641c4726c711f0d4136ac280e2972425b5ae7a3c27bf5b1171b378909a e807cdc919a38d3c286abd67ae7f0dd3376ac8e9ac55716b52b5c44fca5cb5c1 59c7d6c794047bcc905097d7fd8569a5e4888aa751065b66de32d8d00890bc79 39a5852394a61ea8ef293b244f4a2e3251939dd6dc383940e4b0e5f49b963c10 7fec3deb4d91e02f59d75bd1e7ac2e92b5f8030d696bf88463288e7a12bd4ede 9b1364077d41d4c6fc13b5e4a77e52c27b2fd1dc7765c1696ebde46a07c4abb0 2f6956d8fa81990810a2ed1f01e04b4484c0fd3a76cd84ddd2a9619e332e1d88 5e135779ff608a35cf5da0e30656090fe76c2cac427604b732fdc826b7265198 8c27f69dc560a8e8a68310264d9e65e7ac88beb237a9abbb7708266844057ce0 efcd93dca818910bceecf477ea4334af40922e56c9f4db357cdfc3321abb0f68 fdba4d25f7845c2858ca4e6c1b077a2b7f0f54bfba930670c255f254c277ac2e 04a7e77ee772a997a8954b709cfe1f660576adccc0bc828042a6bf9978594618 917d17c135e20aea7caea1dd22cc6752e4fd5ce05098d2836a146c793eac61a8 b45cb5ba80a17e6c0646adaf9cbea1f7dc5f3799a826c9f088722a5cd527b82d 29a200178549a18673e0eb92e8bd384dfaa695e130c54dab6cc44e826e7c3112 c8303040ae302f47eb64d676166d16b6e38b8996a88e49a0cf1a679da3dc40de d0dd2eb2093b1bb607c5b53a9df148a7af9ab8eaf50e56a9a19c4366a4b873a2 54107acc6285e280bebd6c5314d9ada8311dce901bc812d700a3e938fa628ee9 5b6748e854d70e02a91b3642e600fd32898279ece0f10729127de0bafc7db0ed ecd46f5a0d0719d60a71739ea93af27400770994db68ebe7fa98f9cbb97f5e86 9a5cdd5c941a30e5840598daf4e0e4dc646775ed5080ddc03bc88e74e57dd7b3 b76f4e14bc66012de09bd3fb98724759e0fb3cfe68b17ee2f236ca35fcbc058a fb0f82776ca7899d5cd705d784cb0a30d9a705fb5817497d66c048709392fd8c 6ced641a3d4c402a7bdf2de0d000f9512d6245cd946971429ce8abbf13ff56a8 fef493c6555d43b0f2303815f086b7de4c13eca9fad0bc7cdb75a118bd433392 3ab468a92ec76d5acd3837aa83b7ca933984be9c167dda2a43e86f5b5a70cd01 155bd8eb9c7efba4b2e45083e6e70539f68b0cc02a23aa1ef40d94424b0cf257 e51320a48a75b74d223cbda29a41403195300d6da467c8d636bc909bba0d36b8 3fffc66702b2144fdf6fcc3be9545f7f5ee034e399e4db1c44f7d7960cf0580b cee707102ff886386a1afc90ac11eb95ff796df190068216737f85da4a99a85d 27a33317c49c01e292d23a085ebc2af814f406665c43f9f8e064234bb9022a08 de53a7f01a8b11731298523138bc022a5def631ada76f4c513a71e5398674225 585c96a449f6712c701e231b526202e28a7e7878d8c09c79f103f2c734b106ac 55dc8746379b0c79871bb5c3fdcd4d07f16567e4ae20fb7ec2e794d27785facb e9a7d69695afde442c58da9339bb8d00fe3a90a74f813f00880f28784797a309 6ba12954a1b735fd842fd5534d28304d9819625ac0425b128850b5369e2c88f4 d41537e1b86925fd180c1dc27f7c6491813f3344d4b9e7bf26062f3c93a4ca24 d04d80299b1f40f97eef90c85799b317154baea13dee98f721fba3d6736e99ff 9629084c2f4e36d15a963af5ae25a8ae70a20d6d4029b19f313cecae8c810163 5eb7b30d7c80f24c1cd46f488db56a4b930cf1c595878682025f0315d1595401 43 f336d6453c3a30ec8367a796c44102f82869fb1d8cd15a83878925b976db46082ce45feceb7e5ff50cc41f78318bfcdfde684c1e5529ecd390b15bb3b2e6b402a14552344ed57b0d80d60b82a61b6a32de5683e84ece310ec4ae54c3576d5b089e80d45da5d6378508cc8dc70a6a332abba95313f3b19d245e5087f51bc4af08542317f9339a58e606aa2333f20faeb15ed86e20afc3044889b1a09904939c099525771d94d84068ed1527f387fa7171d84a83d4ef28aa6a035ea5062e942c0805a161f960a680cbe7fae0411b82699c4787ab597daf6b280cab430921d1970dd17f87fc1626c904d558f2cfbc74e8d9686ff3a0d05d8fbe693934f26b9aa000fb5ef204151bea5a8b39cbdc9150f3551d3d2cd9de389d4fd4db7e7aed3e8d063f461d8162d15bbbe55909cb9da4e4702413471cbc1d957357e6b29748a10608d021f14dcb7ac9761740e169b9aadfcd4727b4c99ccf66c00b41f6c189ac4a06eb96d0f2f3f97825811f42ce789c07c9bc02b24e554aa471559f15f87dbfd601753fa7cce2dc4403e22e365c9bfabb11ad394d041d11c4fda96031199c83160bdef56df1066ccf8bd41cfe5eea2affbd596ff975dc1eb55d9524f0417efebe007a15ef47e71e9a8abbace6b33cb89c40e532219d8f76f51092e9005ff5332f0ada57076e7d0900234eaa853c5fff469e14cad4c404a721900f8d9095c2cb2b00c6e0574787237836ebd0ce53d898d55a967015cc3625d5ff8e76662593264a0bd5b3b0889e1f949569adac819f53044c912827e9eabbd2ccba473532aab815065fce040587740f7f56f79aaf4cd31bd12d692aebcd2a6a9ee9ad5407e65eb50d722d9091d301b49f0eefbdfa3ae7ba7e6e516e940970e125e8a963e3bfd44e0b0b2ef05799af592610f66866f375da52e0fbf9f9bd75d979d6ab7e95e9a71706469fa2e5a12ab5d788fba235a2ecf8f6cd7d3ca4d4b99aaecc73ddedd60afc0932a62d81433e4f91f9fe6c197749334f37fba39fef3bcbc4ba72c036639dc309fd48db5a8a41b4fa128929ff9685a9a0a6036b816590ae7358eb10af8ecb9e05e0a311397c03b0bc5594a6f1a94fc2ab3cec20ea4c7cbf2dc462aeb6372e9903c7718317e03a76cee16b341f769d3666c438184bbc22387f6273e2d7329b460de2a40c00b52d11dd30058a9d49aa70b84eabc97cfe8a6c4c7424b52a4cec8309d8e11866f197c0b36cf097a5dd1cba984074194b2859fde7afd8bcfb8e757c04a203ea3dc101a6aad375bb151eac1b36461b7d9ed359e47e462d1083b4eed709fe77aa12c6e98be0368fab0fd09b0c7e8c4690ce708a9dbe60f2fe6ec10532025f48755e1c8696373602b7918f54c954e1dc8ba0ab1aa3cf78e7f87906d47705d8c4e378a3bcdd3216c944c0d4fc7631a31bc54103481955d59f20bfffd882014bf07b8a5600ffa46baf94023413edde9ae24b2e2b92fcc67714795230405f001ac0c9cf6b52d63b0e4dc2322ff9dd4c17f300737279bdf178f4a9bea330c30dc7f6b3470d81b338fbf73d53585612128adb9d86df73703dc8b57b6def5fdc0395b4a8861604f7b927fd6f19c7f54b3801c62ea0e282c25901cdd447fc2b790612a1ad62947946ce9aa32cdc4906557a1d88610a078635b3d7cd88df2589880aecebd228623fbcaa4a5b8dd86c1b4b54cb6107e1408b2493c9eeda4b89ddde0e96f832972cbb1a69998799514dfe2d21ae7a00de1cf19c6f19b30652857d4102937914e5536175e3430c9cde0e742439313a6ff6acfc8a3e399899261291a80537c1a8296128ff523fbef6f67f0a0388d43839b17bc711fd19cfee42d8d2fb02890f8f617fff8c0c74c7edd7c9f1757588038f0d7f2320fa65cc02c5eb184400495a978df82c24ab7374b0cb95fc2cdc22efe70744472340dc650d7ba81fa90ea0fe27c6d86aac4ba162f80c81dd51a5b957db08b0ccb2147077e5fc39f5c805db08788241aeec6ba497a8b09416395b327343ad86a4e186a811adae66dd970ac3e9323f2f639b5d9fa9ac20beeb3c2e4596ec5855c5fa0ec13db230ff51a900f4bd76b44541c835a95d52a1d97732773a735b343450a013333e068fe8853b073bae8c01bd3e89782df7d5da58774f07c919c27ac21dc7727eac824d2491a00451f48f2d62ab8a3d383cdc7eff66a83eb88d3865a037f5b6416a63f6cba554061e5f076f31e448933e066821069ffeac74a8b1dcd20dcc4e095f4ec29ffaf20d3ee9ede95ff2ae4bd9dc8a615334ca5f27655c455655776147baa73f58ac15099dcb17d6c3d178450f4f8369cec8d2e41584cb9396a95e2cacedca6cdf45da0fb37d8ac92775c798407d588661cb3e1848da4196d29d6a1e39c7bdbb1fcfd20f20e648a526f184c339e5ba2fcb97ccce3746d45bdde60f98ec978c5dbb81fe03a91e1e7ce570c555f270c390767b8680eef6171c1d247728936d1d407699ea0296eb0b6d2f1d11e0004fd9b918efcbe13a16bc5c0ccb9a1b4468eccba6a7760e8f3b87c88dc4adca7f8b85eff3e3d64bbd2278c92b513e89f269598ac170440730ab0f83a0975a57bbf932d596e72233156255bb8f75aac45c173168ba29e40230c5003896cf9b2a1a1b1e594ba2742435c9cedfe576714d248e995cbcdb13069391bacef5055fb0562796f076c46ca59124ef5cfc5f68a720233296b3991b038232555fd81cbd09423c05f7cc34e2a802e5767cfa62d4d134391aac04629c053ea0a240c5fa0e08bb31f222d841cffaf0dd98ea58ff9b9f98924c84e2664d07120ab9995ed6972b6199343cb7bcd6ed511d5f051e4d9ac07885ff57d9ce6602fc2de5447578204e2802c4b08ee2d70a756be0e1500a2aa0b0d25d72eceb780383d9840d4332ca8f56aa43e08e31ed5fcbf982f6fadc4b75e2d7bb8fd3c8ea0bd261d27c8edc81bfe94cb431e3a56646d7c73158312b058f1928b165c75d2a055e56621219f274c79e48fdab342c9a4efd2e50414d2deaeb62cc7b57facb9906d7df14f39d7f0c9e5cbb966be6907fb04826875fbb5e12d18fb6bee5cf37ba02f259769ee50bc6597d95bd5b7d79f20d31196717698d920778f83c3dec8195033b7099d88cea6f0478164ae6c9001511989ae3acd759b34082de19c0dd28390d68ca78138739dd60cb5284c095f50504a470a6239689b3882864d6cc042331099a2a1669e55046b106df947a28091601618525b34f3f383c9549f9e74b0a5407cbb875f7a1fae649c93cf48528f25bb5713f79372e636d827037f343496202033e5a4aabadf8f5bdb2f1bac693b51b558db8eb6cd933515c0675f90676b6ad073e05b8b4ee373a098547391b31ad1dea62b001a9432df65ee2328a3ba8637b07b22a4086b14ebdb70937f51ec7ee20f85e9b2935d8e4791c8aac31dcb5d4a1077a995c779baae86691a9b89517c14044d9e84ef9b2770363569ee6c0520ae30136f6c6940e6a089d74d2a91a7c5bb7016e986f538109540256da1aa436002006e3e5ed41a940863253c4eacb25faa05a0170d987913b2a51ca8f29a4fdd99e023479c0cb0478b11c5ee4b9fd63963117715d9efde2de988812542f301a56880c5e40ab8a9175a7e91f53ece9b991cc6913642217332afed0217a57b40fe62d0eb511378d1cfe76a316f8c36339a647aa0bc71fc916e5b9d334bf0cc1bf7adf0e7bc27c7f9ef44603eae38eae83d420e9ddf9fb5bb5dda168fc4af47cbab1370d0900a1ee0ba632f8889d6cdcdf44df62c3162c9de25340d2b406330a472c7e00c598783110bcc6cb69bb606005bbeef6b043cb7ba652fed4c012e987ef26120bbc025592cf475bf37e8fe615282cace19588a8f396f1dc88a02b29db0d74fe0b1ed4dccc404594d9d2e54b62ef9e04b1e6836c2bf151d4f8a9b94c16a190b10ca3d109acd57bf9732b4586cec825e2db4acbd6f5a35c4b47df229ee9a1f68b08e096e63a2b5115eb72985e9c798571272952bd9276a4b242fa5d23c83e0ed20afe4d88cc69c7de6145d4963d87711ada259b3cde77df8db5c0a3679c0be1e608e68e09bfb25f17a82864d44a5b01394bb2bd47e9bf0c4d5bb06ba6854937490d51eeb4952a307e1484a285a24a9d83426395bd991c9c2d1704f41ff355b298060454a052881c0f6a133cc9d38146512f6b1a8218a3bc7b32b062e41451f8c70d8d6dea558bf1c8dbfaa1842758a5f05f0377f35b8ed567b7bf48da58e9dc540180efd88fab346c66b87f14955c8c12dfdc63c9069353a3f731ab91daa0831103813f6ac0c1c8f74f86366afd925913c08b017306499d507c72472d3d10ecbf0e72cee2a2425d35170221e056aecedd3081a155377fbb51cd901f0d2f02b8df0c898207071f47ece88b1dd2c10a3f5f0ac904fed7fc48563474eeda726b84b20e79428b1204d475d74eb519bb6598d976455bcc90634afbaf0d965cbbd42fca0650d2abb6e6a4443e052bbd787efcbea78a1e3ad85f0fd841e2152f23f45cc5047fc8488359b43b4aa998e41d44ca2ca1125d1f4d63d7766cf73104d4915d8f0db8befe72f1f64871081577787f8f1db19ff0067459513742d1c6e4c5d08e3d0bb938ebbae33ede5946d1e40f985a7f452fb76a614c8c1bd5556cf6f8d712ae005ac78ce150c16538e5914c85a852da119cc0388f6168f894b9530b6440fcc905fd9419e0c5bf1ed2673b41dc533a80b6462eefd42aebda44226bc58325539c08f9306b5bd00952bd77075afbfafd124999cd637bab85f3c63976618c7b356800310e888a66535dcb16bd5c790ae1178d17bbc16790d1e685574419aa3626bf0e29ddc5b573a48ca3f85d10e920eb24b944c0e15ae88add3e1e81ed7c6361290464f8dac9ccedc60746000107a47699a5473dfdf53700381751957e02a07b0308405223a6afcf95e949ce29612aa7ddb67b5ca47edf272728420100c2ac5bc2059fafd5d5dabdfcb1d31bb71b56de855c1d8402dcf38ba04cf6a8841849622806be820a42e871d70b840ae945945df131eeeee530f290b8f89e5fb518d107ea016265ef77bb910c3cbda66bd7381bd60801c70549386cc06498411081cc891409a7e99ce434e2481d5824e9ec0f7c4e93732695710bb852de917a62be9ffb040b0ceef880075d3318cb028b47f59363ecd9925b27fd987c256eca83f868426b0e8a5d3b408940d94195201b9e3676ebbdedeaf30b4fe3a35abf36d4e0f2a1dd07d4cf8cd7df4a68b5c2de1f65db90d8ced5039ed98c6d5b54cab8e26e739e7608a2c0061a8ac18a8c06da340a6cbd2868fddabdfa3e0c709ce304e651e8b08e08d38f570f572a681e0452cfce934cc4be342cd030ac2db872581945d5d0b446029610934d35e9ebb44629d42b14e1d227b3cc717cebf39b1c382b63d11e930603734922d22d03e0d72c2060259e1b71d2e35712feb498a2b4919314d5aa467103144494cfdffaf66bcc705937fab5a477bae868d0671017cbabb4e7fdd861990c29afa675c3fbfafed07c5e6df79ba67b6932f82bd5d266833acd4505166dbc03e325fa2c51b420a6281c0c17ac57970b5a3b2407b4287f8490fc47bc50dd430b544b4e78a670bdde495896ea2a5ac55f5b16d28bd00bc847e618c015719df90c22e0347e83b937bae5cab1411f54ee6a88b81749a84659c338a6d3dbfc51bc0613390a5c44f3155f20c4c8b7d7ee8723fbad528b0374d00274592d9d1cb4f0042356010188bbd30e40f5b63068871de6aee6ca4ef164443257e0539e9322b00b182cce663aaa350ab818b7dfbf23ce4b373b004cf28011818bc1592dd9047505719b94a330fe25192d4e566df7ac855108fd15695eb0625895d679a370ef390d718a7da5ecf43449d3284c5fa53af73efb2caeccbfea0268339c03645dc22305f4dce933d41184afe2a2d8fd8cfa18483c7a90e188d8274f4d89cf796d70b108c57411d249f1607847595566a20b430b36011bf74f0801863ca1028dd0489702569a85e98c6e12ddc382f3d097519d0551116d94757666c638f478038ac0f5002158da5d14b8bd9349e6aa9f24e6f9f68166bce65d61dbede0fccc153011cd0f583e3f0af65e0aa84b1426fd7c20cc95a72acdfab8a09a5ab1ec1275981ce7045170fe75a50a7b3c1de6ce10a017ed9d11d675c4d9bd86615131beb646b944076ce8557fdbb9614b40ed8b63dfcae03fc2f3ac6247c6a5f967be9f4b23f59f0b68849d1434be889efae22768e7f3c3b572a9afa58d1f824079c4689212b26f0af5dbe233618c5a46f7b575dd7fcb22b51c2c668fe35d4d9b124a64a0929c05053faa00c2ad98e97e27805aff9d3c23c66b74e342ccf48d5a44a2c25f2b791403dff4c04d40b30be1cb10b607a399ceac6a1d567c7201b5cb1d68fcffcca31c0519a9487e723dbb104c165e2ef621b1dab018564fbcac873c81b5997543d63206505e1fc04c4ec67942039f399c5dd9ecdd99c1cad6d85a978813d1b2c824180b752f4e9ba6951a89a5ada46bd08fa901ad06beb5fdd841d9012eaea30a00d20be0ac0cdf675a023c94f0361f11464dae390be8ec77da865fd4fead09e29da70bb41a4cb873c4ca9a1226c6bd825dac965f705a9f2bfdf91791d6b49d5f9e4401ae5f56f1b39ebec11e0bc52bd19adab9f33d4350ef32eda32697ee371a5f8905a17370a57b9ddb10c3a59d0f33105dcab393b615549aaf791d1cbe92e920a80b020970969a06b3afce3de607e4118db0169e988f542e43d76a03c5ed9ecd2a0a20c66da2407dcbbd069b179b800f29c482126ebe32e7174cdccf063beb6d190efde1df5884020e4c30c4d8fa83807bffa5199314f05b2b1fb8d3842fd15c4e047f2dac875263b2de232dff225f24573f7ec0994f7cbfbe3e50cfbebbc61a5f01ba5a8056d4721bed705564935fd080114c8b37873742e416c04c6d897b6aaa0f638e0d96c809f8e46b3ab671e37e8cbc73ada9f4ceaabfebf7c32c50a415e60f309c0605b88875e377e3d6af881e703823b36133c2a1d173e64bc998102df70005f648583b0024d9c56a45689a711f89590f7d7e3e1f7cc906c776ba3a278304402c66967ff338497897cef8f4756b533acfd2614afde3d8dc7677411484590cae3e747189445add323b37794eab1b61ddf952ae72c0d7e47f81560da4a9c40221f4e5848cabbfcfd67fa623954481857436131ae689b1c61831bbb9bf17770f7627a53926097f618ae0e6e7106209abdf0164e51264dcf198b5ef6a08f4c10faa9d8ac222f6fda070612a9e8dd051c20d1258142c67d3ae50d4101bf58a4a0a9d1ee2a14c48ee45369f2a10688d7da8cd331d5765a7f5567cde91188875b500ce6c9bbe42be21af3c550215b24d08cac0e09a259faf427d3b8cf2a990b18d04aaba48404a896bc92c5aeafd067fc325a2d5460b50dc92d26069af2e57cac40b4a8d2dfff81716189ef2a8febb6f06ac428edf6d1a0cc342b7d3389d40544505d96c0942a02804e8c7300e8d094bcfde46a64c12d87c62552ca5df1730c3b0066c98067fc7c50a810f819b16ace8e841ed6aee6091b1977028a992df92ee5200ec9ff4919e8106fd9815e7f9ceb91b1cef4f37dca1962ce297f43f2fd5ebc90cdfe99c8de010085ff020fd2f53e5ff9c5a693aa856c9913b2d6266f0f74d4500e5139b5e5076c94c66d7ed42260fb5d4f93e0910b6ce736d28ae1baef6c5ef098cc3fe587d87a211ed80067354a31aa33eac4e6eecc58e7aecb385fa4d03090873a9b28baa5fd69810e0e7dbdf1880c1e9faba696bc17d355babca58b0635a0d2eb6bbc0b544dddb849f42b92ac6f02b5d94df207f4d68e584918ac1f85d800152f7a8a21af8382749d23a78375d30bdf760ecde8e77bce87a0f640f41778b07f8cac4ae1d0d1247ea8cd675ab94ec057666dd5325b6c8f898553e8ab5a88f0ca3ed47ed793c289e7a198c5b115f6075c1cd170a0717f31d4a14150532c601070caee6aae8f7f756ccee18b28afb327f0902800c5648a23551358bf3e661630a5ed926ca2fac1a2b872cae900e19eec41e565a52a9243e6bae3852e263ae2703689c2aa49f520ae01616e2adc70aee9bd62a78ad63545e1a268661aa9485ea0179e99ade674e0e698da1c8015b284cedf8bcc6cab49e964c34c84713537bb50e495cf0e0476664ce5542a660133495c993ab363f39bd9eb288825b764e309e02 -generate_ring_signature d9e4680311d1cb9dfb01254b08fe8cd0dcb7b3c503d6859c59dc47e25615024c b8f8617d742f1a96ab3b79e141b52c76545b39f00fd00729ed4325c2e8e839ee 35 c67863dc9547745b0a1835bd2bdd3c7f866df86486e79b5b23c60dbf330dafbd e837d13ecbb2a5a0bd620f59334475bdb332d44f44ae4220e5b30f4134416a87 c00aa3422f0a2f3a5a89edb8eb7c99d642bb33d2b5d4ae42a228ef3dcf990ade a132684aae1af1909b7d32035c7853359deec9fd5ad14e5548cc2e241223988d 39b05e702beb2ef5e6f02b67d46fe13923ba2629cf6dd4d813f92b88ce94e049 d3f8732f7598b1e7d51388a77e409698b1490ba56ff02df6c9091fc3723ace03 54929c78aa0ac34a796a230b66ae375a443a067a07954cdfdf4f34dee0a08a23 81ef7d3eb1c810cc7fdec70dae0a75ca8084651cd9a446005f7af0a37ff68c8c 06081276051edd0d6f829948a77a1ac5c8eae9e492880cc52c3a3b7e9442ea86 6b8762b54c942dfc5a701842fa7cb5ecb20ead13d5e72ed2475e00ad78f292a6 031c76372a7bb536cac58af77c11daa08cfb59f990cbc8c2eeaefc4ac1a598ee 660ca8b174d65ee995bceaa95e922dbb701ddb6c4df76e7cb46a75fc19b13eeb 81822888504f072acfca4da1c30575bf5efd17a769aac9cd0ba165003de67927 ffb833d7a1d148e158172ee880a850d83304eef3c6746427a1ef1370fe147474 23234646c19c1022929d56a62e494ec2d45e7188863f375cc2ffe681701112d0 227bed9c4db50f0e54a70edcdd4be63ea2f6e7b3563af10e01c9a9c5997fdba1 d5aff7375658c886005af953d831d6e4e2898b1490cad7d44a1b428e87e8c2fe 546e5da5f5599dfd4cf7139f92c80d70e6361249ddfcc36a64a4a6a4579ed6f3 4617768b70f157ad344866c8f0d9575c7a8029b2f693ebd75202fe238c9f334d c505334dbf2103cb24205e0ae4a660a592921c5132906cd7f634b78ebe0ced01 688761e17fbe6024f8e51c6faf00112256440cc4a2c7d37888ed4f5692e26b71 fc48bb2d96d7ade8b51a189f4ea1a1a427669fff066da86108899e2e069c37f4 e80d7e7298e64f4f273548bf0e2bdfe3cf8ff76f86f1291d28249052605dbf46 23345ed9e65ee1d2f9f0df21ec20f4fc506f837f54dd024ef770a81b12588302 b572cec572366f8fbaf5ad8ca07cd1cce301e56ada7e15fe2ceb03d6f7c615b0 ad65e69ee23a2d216d437521cbc41a946f1a0e2e350eb1c242e53d845e664a19 9d652c7154706b781272ea4ff1a5b3b84d83b2ab86389e927b6b0bbe611a207e 05bd82e4dd30ef4ae5041ba77652e173ed932b90ae8f26f0c7a1c924dbc16bbf 95f6523d233a5180cad52fa70fea8ba013a1f565f9fa5d56e6099b99018a5948 e376bffabf575bcc111d39638e0559a8d7e6055e594f5f43fefbc5add7904697 022b90003abcdffe3444bf899962aa0173489163c45d6f3a1c023fd8f089cd6f fbacd6c6ba0fe19d9d48ecb207c66a7578648a77e9c7e4475721510afbaab99e 88f5947ac68fa3b16c1c768a9bc70ebf76ccd2800e6acba428ef6d8be78b8b34 c4a86247b999e7c8460f3784603080ad902ffe2e71c986278c7a26db99c32447 1b83e2998db2167cb4f9133ec411f62fcb2246a84ec1ea4acaed25ba5fd60d52 a3480165d43b66938e0169faeb99f7d09b4c918620775b48e699b9e3e78dfe02 29 4f3672b263968374fa854ed0e6fdeb25a11a81943cff7d96308b68a8834dbe088f5e3bfe4a6b1477d9248d89eedfff7d71805bd1cdf81c18475e3a6255532806c14bfed5be4f67fcbb24fda0298dbcb1d12a3ac8e64c582dfd2a359d8c64fc0d5193a40ca88f0f760a28205950655c2cb6dd3e3445926c274b7bfb92f6923203d49b53ba5e591169998293ac19f23c1bb579ba937f5c843ba7f5124cfe12cf005d1ee3005f897c0d03091144207e3fb353c37c6855f12c96f60dc4b895480f06e84876bf871e1b0a08e6075bf0c2387e8f9210abef5e51b76e1c236cad84da0d5e63e03d36b55771ab22c0537003128bdd9ce9ff6ad2605aebc9f8952509bf0caa108acce225571004155a51f8f3ea552af7a10647d4a8b3563e4f6b95329d08fc091404508d87aba2246088504539c113fdc815e78f41b5edbe18523a944d087213bbca7ffb46514164e849bf0837b04838809d5e4bf96b06487c560f074b0ef9222f8f8e2a1889bf8b49053d23581376ac4abe4c45934b6aa93fee1d6e2e01e1db0f08cfb5cccbfa4face2b012daa3cd84903f7cb107f44938385554dfbe024e9ce2e3d9d000f53affae715e90279a78435e33127377bd7fbaa9054be6d00c7097737056e7565fca7792edbc2984740099575ea3c1f663d3ce237ac6e18e017aa614fbe5bce8c90ef9a5d58d4d5bd3084c7d4c290a8f4ddbd9add898732e02ed16945294cfbd77500ec361e148f3124213d8f8476a43b803bc377919880d0f16cc6cdee8f4512136b030c0335acb862fafacce525b977b3484d591b2c93f0396a45f559f0bcdd53a3d98bcdab020baf7917363e5ae4ece87d159e5d49aca015bb7df31a82a8ff485e3f41ed1a4dd45fd0c023b3ebe67ef5886dd73168089066f253aa431556dff8d113cbf16d8fed89d6db3baf83099b77083901bee90dc037e49e74d2a66f3e2d93db8607ccb946423f4c6b49833e814ad384bdd2ec6a9020369cd7c1717ed59c7a526e60d01cc166ad8f3b000095b79ee8f90cc4c272600d3aaafca34afcf7131975e0517ffa74f1e0ef8e51db38d0fc0aa90a1662d880ab31a000deade65f92768c6a7419f59ff25ab952f375f42ea249184075aecf50ea6ca7a1cb9e374d1d7acc7d7be78344876bbd8cb87fca86b2709145098764f0a0e823e57d69ace750ea58bf6f0db549531a655f23a85d2a05334dbbb55d6a00a1c3a1dd6472017f97436898075557af1dbe64e01e5ad22bf81be33ff0e2ec007342f3d6f586a14bd0cac0ed841098bd9104d99519b290dab5a8d90d3174ace0fd2e870cf2b903b73d614d4195e7f8dccf4308d3f4268349c13b6d02093429802665e2111af809831d8acdfd81aae3fe4ff196f33a4d0cbc93eb32335979cb40efa5f14a8120c102c979e98fd02d12d9a064a93d0c9bde6bc09d4ef2a22258c0b93c772e3fcbca3545d533c88d58f83b1e015b00526ef92b76f24d3061c4c7c0a106f248383955392e8f45fbee1503ea8e6a7c0e40dd1f451fe700ca88501a007280f337d5eab54185287ad30b921c454dd20b351d606ce7688cece3b3016a30db7c2a3b7e28aadca9cb50864fdabb82a7b4c14f072cf0ebbc90ae94b9299db0d490e612214bddd78c57459d55be8f40dc815c6a23f12f9e3172e9482d2f07209eeebd59d5d8c2aa1ebd5455541f26246b5a85212bcf309c51d8cc571811e290ac2def5cca70ce7be0259f2d1e3cfe5657df05e776c24910ccf04b589cc6f1105f06f200ce863e672703e6040e1aa61143ebf30b4f8d06bb951b8037241c21309aa6e5f7e5a505b817c965fb5230a8cc8db94bae06c282225eae54b8fa3bf6402a4ed950351406a7b1fd6ff2ea14de9bdb5530deb1bbf88bf0a72ec0e31ab3d09c937c367f3e97d7f20ef150c6be10f03acb0c89e63d46d64f73c31508eff5e059e29380453d02c27c28c51949ee208a8433ab61bd1e776996835511f0937e4014fbf2354935e1af783c20c2789d01e480b3255dc9342bd76065a28e904e0780caff08eff84bc92e0cfdcd324870a227c9008fd7396274b33679aaced513a670e5b6cbfb0b480eda0d3a1001e0936534ee564e63160c413524280a9345b66d90a9b542d786457984d97bcfb82f0e7ace032598b9ceb2eed153ba1a599c7b452014c509d49f481ec1774a896890723100352c78275c4b4a053a50ee0440e93770156065bbc1c2d9e14ef6a021816db11bf6b60cb95dcb0d11891815cccdf034e012a8c0e4425aa83c2206ec7d7792522bd394d176ddd859316f5f5e9ba51f6cb052be14312239919f1abf04fd8d993330f59b6bc202379695f0950aece7cb3110b1f6e540fc240b9011d9ffefc208018993d5df994986c3374e2bfbeecca834d0ff3f7f2013c9a70b5a609c9c0960ca43c42e8c4327748d33aae982850da4f690fe788cc121d73eb71ea878d2f5f746174c46b49a486ed1f0e02ec5c7c17f9bb0f51c31a90e16362e4327211e8f33804fa36803f298d8196c0e708d699ceab7304bf62b94dd15a657949fe1f67d10ef6d2fd710aee911ef0570aa599317d45f40660bb1a8cfa5b25f51a93d746660d8095070f1310ce72ac7ca0c0d282991b080acc94adfbb8c7658cf13a75d086d7de4c27e5427baf82f92c864317857e9b5802310dd0eb170a744c10397e10dc1dd9e105575b78b5e4b729db87cea1b221410d42d06ae16bc94209d0d3e317b4f06330bf7a5050dd79b631c3ef5404b9608d09a95ba6e87b631150a661b63a82bb514f201c0b0fcd670ced919023c7c15fb2062d2deaf0ff3a6c5c003bdd29c0322497322c1e126f3693dd9b5348f032d4200b68d984be379698bdbac416c8c3c91cb7cdf5d816320888ac4f6668b4faaee30ff5689195a227306035e6291af75514012a3b21667128821f6a28e26a7528800c1d83be7e41d78340bb4fe140b5df70f37d62b551185965fb8afde3d986a1dd0374f942d95b77eb9b56e3af94e93996951ccc64ec67c27388ef6c035d055c1202741f59f8b8e0b8703992e086a3bba8adaa3f916ce0ad5e6750bdcff2518dc807373c468728183e05f46bcdea1470d8d798ae7cbc2955671bb8cede79fd764209736717295f7f10ce1e7db42e1928d8f0f4711537b19702078d2579f9ff316907 -generate_ring_signature baf768d9e42fae2c59729a1d2b8c1c90345ba669b954316a5674d1de6220a76e 4d7d7d439f8fea0f37accc6bb4b1d9b3d2c6a8f1de4c4080d058aa9aaddbdbb0 1 782b450055215a1e6bc07b6003187d9560cee16e6f5262521df26ece593c89b4 388fafcce2f0aa9c54a2cdeabc47075bc4c6dcfafcf8bac5a99cda75c3e61b01 0 e7d04a6d4629a8a42dc237f45a0a0cba213ae340fefe70e4730812ae012fd40c50690523910dbddc97aca9daf6f2408f799d68c27e46c13bb8b126f1b82be70b -generate_ring_signature 01916e70c1a168a97075a7d873849f7ac03e65094ee16f44ae66ad4a3e0a9a61 19388d755023e1604e7a357953c2692ebf537afcd3c5b175b18b4480c1770ed4 53 242cfc91401e49fbe717545536ff1288d9929ecb3fc5fcf431ed7e457bbc4b39 5720bfc3064c22d0bcb46686feb470b9483cdec909c4b70c63a8144c603de1c5 3144d1064b96500b9fa8ee6ac4f95292f6d92e7c6ca34b5974511c380807739b 90aa403c95a6a5ea6866ce56913201a0aa37d5c09e3e0c0150b8f23e6a8f7074 29a6167a49da7587f4a4a05e534e194f7fb0858c8b187e681104939611678220 47a0530a95b6e548ed923ddaa08d8cec94bb9afd43a716602c73f7a563485426 d7792a71e94ddef9dfc4300d460fa547d0318c04a0f43513e98fec2389fd03a1 84ef4f5e3c509f8110ea7239b531431da9420ef98bef880020ef1565915a9a10 1a32deba9900d23de6dca4cfab511391e4bd2ecc9d153f2cec3a4ce4fa4c54df 371d5595b2a49bf72d7824711067e3248f2429e086383d77de2035a5da111c25 68bf85ebcae2c713975dcafc9200acca99d4d740f05bca766d7eb82391767f66 837dad3d55f3fafc1717b116c0f7e7121ce70b4cfb542b5452a08f92dc59ad55 d66ab88fe75167e16bc30dc727e2862128b938292e5bc60a5eb03390edf0b133 32f67f886be12e0bd1c98eeeb1690acae74e7c6e321ad14bf6dfe7bd843176a6 572c46495fc92467f1bf1142893e0edf08ae501f806402ed1b8ba58cc767f8ec b845884ca93520147dbfcc37a28b5d899faed2f821235f2b055ff34d2ce0ca2e 00617cc79d54d01e4bf16be365e673c3cc6bec51a5c337fb736a885ba0acd573 6a8278978430c6e99cb857f4e4512e8278aef7fe70e4b2cbdb0aea6d5baaf14f b0177887294e8717c7c4300e3daf4e5d35721a54fb85aefb99954ad1cf15228e 5d0a7d23e13f32762fe5e66ab9be9aa73336f9c385772269046132a11795caad b229f7653c5630d1b2ac5605a5b2419d7c8ad9879d6d296eddd14a1ed788eda4 97e01654bef78ba3a81c4bcb36b07c6dfd723a867bb2d1e1009574ca5a4a4444 05a16ea1786cec3520ded847a0addf9ac3dfe96c3315e785927d75834f73b5a5 995498a9ea1ba686429c39efe8fc92cc2bfe7a4a3da23891b302d56b18c61075 e4f2da569effe699c788939b58b7271eb5b63347382d7b9fa650e0c16caf7dbd 62b2acfa240fdd2888dcd3ae4b3b60d9aacf48d627ad4909f0ee3614d2a1d8bd 6d52a488709aa858e528c36acdd35cac6cc7a1e0d612ea4dc68a4393ef095a56 da6aceaf9e86d64910f2f90e7e7d30a3459260c9dfbcf427b0704f5d848ddac3 84c11c1ddf0fbe484e80c375f0bbe985133c72d3be646e4a62691b3ddf02c93b 132529a4a7796849c67315835700cacd444e85a11b9e71afb5ca41fa81dc8373 69d1d9c94d25a965f96c89bdfe92d5bd5a10a145312a46d990d8747d9b54ce72 a77f3f1c05b8b70a0203e373031adf0596eed28b5a434cdf0abaa7c2221c5a93 a611e3a402a42a33cd8f832dd46c794c6adc9df2bc32747686ac7614a72d0c2a e8f60d367002f5fda4a024992dba1b161fe1802e680977b2e2f55a9c3deb27d4 e887fde0d2c22351ec22b8aa8889a80c0917613ab397c978b2871af576c76fbf 9a90e74a7e602b11e1085681ef5e08a59827b7976ecd8e14f5c7a81312a9b84f e35b60b7564725d9a7dd34a6ff0dfb5d488e3cebd79f8260a7057b2b2089c233 71a3cdd1e3f0840ce0cbbc9049efc87f376938c4a6fefda0e4364684176e6bac 6dabcb53034fa276be2e73851d9a39aadd74b3ac7ecb0eec92b43608cbf4b02d 13477a1e0cfb62bdeb1a30889489446cdb3e37bed0e868906ee91da6ee4de0aa 1bdb6348c889278979c6064c256adb5e3bc76b61e332b2981a49fbd79a14dfe0 e3c6ad547fcb3afd9dbce9593931897d8f736b5044f543475757c7775a58168d 379dfebd4d07db29982a708e4a03ef03a583e869e704f36dd62da8fc41867cb2 4249977ebd5483286439256595142ce68bf2a88d40fa2a0e204bb8918b2ab1f3 08710fec6ed8004733e622281bf186975b7c7ec6e2a85d9cd7fd146f9386908c 0ea6d13d9634d24f6aa872f341b2bfaba220045e6dfd752f0746cc1b5e9732b5 201ac295b1b5a10a4317748a29f94ea0e5b6b069a8759eccf5bff9067bc1c829 623ea94a4d6de8a3db5e0c19026fa2623559fa878030c98495111ee9b2951247 f1382fca2d626d971af139e3873405119843d0b0237b48d08287d893e7f003b9 5e536ecd5570c8ae9165916d9f14688dbc8c82130e1c589e23ac5ecfa8a39435 f18095254b9f272c5e5f10322cbee3cb62cb40e75a900bfb69b1557c254060e5 d1c0c862d42a95b7008215dcd1cf6c4516df9830ea11c217042d0f3df720814e 4eed5da8eda2ca0a8436e337346fc54f3dd38b90748bd4023580e7fbfe271dba 8b58a9e402a6a320edb50825a07279ffdb434d5bba8c2b93bdf568144e098a01 0 609aa6ed537a983497297b9dae67a3d740f9fe31b632506e2e4a4c051dc5500a8d5100f9c8de1b796dca38fea0d48ee5d7c24996969d2875d0757f558240b70b98d8cea11b261dea4996a426bf66eb63820706bae1ad65e864dd117156e71202c4a51bccaa7c50d4651b2a6a18ce1f73aa5d443f531066f494e950f8771e90071c6c2552edb05d797ec2e275cb3653d0089eadfff5a1822c94c39b331bba7f0bf5b6b92904c9fc6ba8029bd61b18c540aa2f6fda19d9dba901a11fa43033730aa8f9976f1b307299007eca72e21a0b9f1cdc559081321bd18acfac996ce0150795611f1ea27d57db1eb548b9b83e9875dd38f1481d6c14cc19ac880d7d2508059d2889b0c4a4932be1a6aadf7999b300e8fb5aac17354b57ba74fc5b39f5860ab5ea394af859c4008674311a478ad64ccdd6b9f240ac896ba5e1431c45c2e905de4e9f4fc6b6b22804a817ae2f65c71f2739236b0f452ce622bb1db320b2690e6513f1c3cf8382d264b01a9fe8175b9f22f4752fd7934d37c8d203ef3af7fa0415759656ec966e46e8661f6d80a05205ea095fba0a5e1183ff625e116d9f4c0a3e3112bd160f7fb0a42df58c96664a0fde979e1c1a6cba1643f1f48895d4a607a92d715b31b68bd5ce1c2a8bee3f34b6dbcebc56bac2e480f6caa66b816348096602e3c9f49f2a43a526c7c252513c6b6289731c6fe2e9f29f3f428d608db0040d182278959804dab293cb5d201d49fd032557a54188c555b70dae8a6b36ea08700155ec01b8407a6ec7f40ee1add38ba72dba2f971c95a06c257df268545d0ee7420822d8f62bafc20d3c41852a22784408caf29d87b1e8743a43d146fd450143435f388d53d027bec2f3ff634aea41b98d3fd0a91fdc5843f5aec3ea4fc607986bf868c4839d767efdcb4cd47fcb9854a0c8a464aae9a569c54efd413e8c08de1136a11a22e37c9638f8bc144b5e477cf7aa521c4bd2c3c4200cea51796e03c54c9f30baa7b1530a5d0f19b96f9f731cef39469ca82e8fb943f7b72289db057bd851012bd066f97037ec08c16a5e5bdd3ed6d58e06d6cf62d14e304253f0067df2bcbfe93d5768a80b5647341a7e377be710eb154a5d7cf1f836d3e2948401c38df5776f04bac6bf7ef95c4ea4afaa066602fc2bfd2ec7966f215fa576590f2b12ed60a54017e42d4df0874c7ca833e4c74c6b104439ec14f6c0889f713f0676fed40e35319ff57b391d5cb1c707307b614eaeff8141e44232bccd4ea7140853185f2384ad78fc0ecd4262137d4a4520639e3d8475facbb0e8540f7aff7705fea17f5dc23a132d96d949dec90b9094296826ec34b4e915baaf3b6610df5a0d8fd3334b810f00e1e06e63618adb3bfd8a9e7e1478d1c3b1b2e4d5388248bc0ff2bc6b13a057effc9ac818f3e0b4bcc46f9bd36f3a913e28b76a31aeb33fc50839420d269f799394d86efa1006a61581f464b099e1cb3810993cd789adffbd09087504bca594d90ebaf0d18648de5f80cda5d8a64018cd8919b5e021a3d5e005c00da6b225b8315e1b6961f0b2fab2aced687df2a3c0dd14463579703fefa90cd45313ae0ec87915ee9a2fd71e47c155c0efd00efb49c88e1b5a193946bd8c0d086046114c15739c423919beb362923e4386139aeb1f889146a79fee23de0f01d4176198988ced521653793347cf193de33b42a904aef79e6bbd02bb485ab30eb4da829d4139bc32636d2e39a865f058ad516777a96171d567d07cc245041501f4f82bff8674c47f5b21c9fbb9a4974ff1516c005c02b94a4a27a50536926609bc0ae6633d3a7ff01ddb28b0a2b687617801b9d3ff42d709f7bc009d9b4f9e06f0fa7f66ed13a8f49e4d97dc6fcc8b95c7d7a88f6af9741d7c6c3542ecb84a090e4bd9f5496c11b669bb3be377223909d374af355cc579b9a129b5108477bd02c8369ac5f22a9fc44899890e55f8b59bb42a98c3ab61ab260d5da7f3eb98ef0f8cf10688274b7d6f81a89679ff0e6556d8aeda589ecee73b3e5a62e494a92f0af36e805eed96c63b69c6c1e139a11b98c330711075bc90d55ad592fa8fb6eb0c96c2277fc1cb2390cc3b2cf48b9251af71a9c81465517cc650b1b9c69fc2e60d6c20c17da0cff1ae874af430a43ec8f697af597ff731221dcaf7fceb974dc702900f58393d6101ece747af3a341d0c3ca4c9b75193f037623b86d231acfa43083eb7f24ee11db68345ad5c9ed7f5365038a1d5df9c304ffe9f5163a296492601c75ae6284c268dc1ca4f858ad7617fda4d8d509df0671fd2ede7faee5f68510767af721bf06ab3ae1c4bcc512bb8b3cdbb721a86295620c8ee21d096a5734e04862c1a054a708241aea5ed4bdf4f7a45094692397b5971db4566a26dbe42a10bdedf116f64ff38cf29e5d773955c87374bcec4e45b3fa12ee8dca9a201415b00b562e112cffb04c20e10da981c2ed18c33b1fb111b64f077d1d8db8b865e5e06c391f60a44c16186fd1473d2e7d684e0138859e927076802777107fa5a345b02bfd7cc4c97bc236a7db2931c3ad4581ac7d7e17f5cdcefd78956002354696a008c17c6a4f316dbf6b9a42ee397b434458097ab9043507e1941359c9c59d96306c53449864e6d6e58caed20f3f86cbbe254952b2bd1c697b1a117851e5efea80535bcc44db680809aee50f100b75fee39c0d6806c5c2562c605f125163618b70f4068859c266336476aa69d13bbf13e352f6ec58751f59226550c3e229eec9200df314c83e93d70ab8675ce0210f7d05b85da4574d55d293c47ce0e4b8c07a60020d00fd6ed703803d28ba3d86392d615449dd5db7a6ef8ba5fd39ff24cc453036306583174a75bf83a08fb6c1a3637e4c26551a9cdd28ed61226c6c8555de103373e3ce46c71a4a3fc6fb0aa806970844c7616b03c88f6306a4abd48d7f3d9062d8bb2228e968c3ef155f78ae43ba48350b957efd15e117ca1fa022b4481890eb66afaa914ed9da87b14ea4e4188decf45e1af8f78dc082d72f2b3fb3ba6410105cb789f8fd317b65396ba8b34d742d2581917b6de1bfba41c33d35a947ba008acfc83bde74e9a37cbda481a8c3930b0c6d369540bfbdc72c244e09b9996d00cb6886e6c07266c24cdea522b68fdde31f458e7d2990b848c448b69dec0ce450c339752b261c678415850f0f5e19b95aa2c126cc65db8feaa78cdbe80c31ced0418f436fd805bbc202f28e11ed0538df15698dbd98d2d44ec3e8001819c398a0d6c239b9c3b46497659fe5ecf96f5b33ca09f709c5f61b4b8800d3ee1e1c7980cdebb148b0b3808362e8599ba1fdd2666735ab8a9fcd5be987cbdbb5b6f863e0f82fecb7435a67c29a6e5a20310fdc85fec8989f6220575ef6e2834d58989da07b144bf7199498823325701ef1dcb166b1875b0dbf1f92f8d4b108696f71a660d84a1b6315269f8d6bd0eb2c9a161305a3f1f4269cfb5e85d4a332e6b99e7c4081bd128376494bc1cf308d5835f8251f72acd55228761997fa2ab38fc0e02e305d20a8d2181f121d4af43547f5fd324c147bbce81a371d643e93c66dd98196406ed9b7f9711d057f9f2d967e2d96c04abaf0ffeb56769a66763955b9933ff7e002191a2ad9851a6cd5cb7ebf52c166f271552f283344f2c70a5ff7403f72d1504fe6d8a6d41d16027c15d2661654ce416e5ac0936a4a95a011b1221a5b38bb6044733c983ea0d7c11c488dfb577ef1379a625f594461b9ba24783f062d96f7e0b084af26ad7f8d28b87c85bdb7ca6c1eb35dae7510f6f5d0ab9680eaf2c1f0804e63abbd55b87acdc4aaa1cc70a08c214a37b95098414298672853b8859cc24017fd21045fc2412fd78f1b69af0f08bb1e4be003dbd785ceeb843f651ddd0a906b17d52f06a635cf6d692f4b331fa850687d0e541baa7ee2fbc7669becfd3d3027a218a18b6c0814f04143a64d9a4d37391a006c79eb148b3f4d12e70876b010827fb7d1dcdc006dd3d55876f3fce3a90f62b7bc5c9ccd84aa3d28a94dce3350b76f0cb5a5d4048198a506dcdc1ed301609c04a011b4b7b89535a72ba207eed01594380d224173317d071e9fd4d3d6f72cd2e5918624bb4a9b10c60f6c30b9d0d7d5944eb974216c48bbdaf4a7f69ef92b20e03ad824ea78f41bd7ceeae73c00373139435c7841cb737304ec5add479f140fc06e28f67cf92de2044f4c23c0400c39ad413285f51e76c087ba5d927a300d44a7e0317e35be94d41cb89f66af40c6994d0df763b00428f7e94af71aa00c71227853f1a1f5878d02c85108bcf8803ff9e8c036495bb118eb35979fded8c3f8dc8c331278d0f3942d1752a2b79ac0aeb3d994f8c4204fa96787571924379697db8ebe0fb1688b15c6853f85c20110dd972052874cafd60b866bd163e83a975fa19f1eaf5daaa1c84de7acb867a880cc64aac0160eca5641070631d5f85e0845430f1fe3349fdb95e27dfd1e095330fd8e531d764605587b36e7481708978bec8d6a0f5efc188e729f6475ef9170c05b2510dde7fc1dca90eea757593daa442cc156a7bc09ffc40837e9395ffcc640350057acce214e5c3ecfc7d5834a9ef1029b32d84ac1f93784c279cc72bbd8204fd9b5af30c739d5d0ef8b5121394ea1a9fac2d1972366291a47213173e26d2010ed400128c775e733af9ab3fcf090707eed20b3b477fa3753a3455444333c805ac46e2c235c79fa20c71beb420c3ed022561076d8fa8c5a96e480d09562951021009389888f42b29f8dced9208ea91f9c5444ae02a261efe1c907e55ad90a209 -generate_ring_signature 0fdd09cc2513c1d346346db21cfda3bf3da4d65bf66a91dd87c83fda9b9d407b f96cf77c4abc31236ec66ab0e3c56d25860cb2027897a5fae40622a7908ab147 16 a5f687daffa9505005c73221669b1a728930db51d4d40b78ae6a821e9b319440 07a6ddc48dfad0383024bd700f46f56c88129345439bda5e7bb7f356ce11c8f5 585bb3d7f53e474d75cead5d1ebd12c68757bfe231091adacb877b09e34cf3d3 bd06689f501f10bb2f4d5e3cdda3954a6d55ad18c3ad485fa98a4e83b163fa15 0908f88df8ad262a40203ba72994f9bc919d79acf8f3847c5ae72205f8ec29e7 6ada309079d68bab1123ec762dd8ac04166d8ea824acd9408f502731dda08a8d 80d9f89d04bdf2c205d6955cd4f285790a4282a6d75bb555b1342138b42c9364 77e56ccbd4d1ad228e32d2be7837e3871495df21e94dd1328428251d7bc3729e 6387f7309b7005b5073e718b24b2b7fef3fd84749fa98e91dd1f83912487cf56 6b7e693dd187ad5c38f73079c040366abbf2990dd30eee6f685f0360ba22eeaa 819f739bedd0589653cba095292e436678a1fd38767d4a6e9449f3d4246f12c8 5b0d6f09b5ad89d7cd8ae24e8ba3c2c4440cb8858c2483ff1363037491953fd5 e3a579a3dcaf3ba92fb5f57cc4469d1ac00cf048f0f3950819875b1805e43a46 0b650b9de62dd063d88549fb717d3ebb8fc15b3bf7338457c53b7cb4c2b1350e e8ce03b5b89b392e9d2de32cd39416b7c189f5ba81954c37bf64586dbe707ea6 feb0d23239826ed5813f0d76ebc0934a4ad72186f9e43a7895c54e5e03c55b32 e01f9713a2feb7d4f9a06fc9b89f1f438d990269f8650286146ccb2df251e309 13 ecb57980bdbb5ef293359718719bba53ece5c16e9e91037d5500c599c5836c08473940755b3f465558847f606c162022f061e9daeb0742876661b5bd730285040676f5bcff5da1c3c13049dc3faec02267424e7bbac0fa645b5c5dc6b06d1f0034a89cae995d08ac91aa59b7e81e476f6b250b1c14e6c42f4603e50c8dcb5e08d6d054b4b5c93ffa94c1889a20bdfa7893c4550c1547ca7ad21067a06d1ed9081d2a97e21c46e453719e4f4ab4c55d3844f4c50d1a63bfa4783b9bd1f6a20c0ded2d41ee2218e45e74daaeaa11838e518cdf4c7f95e94727a15a6e5ec21a3108ae5a098b3988da0fd6bd4601fb84434d5d110eaffbf5fdb3fff485f8936a6805ef5b397667e8877c878ffe2991390de1b23e075b39d85eefa1d6948390b033064c6f966d30df6e2270794a671b3185b23180e01b48288c12c1cb56aec222a7032c1840c0b0a8a93702996416103907888902b9c41f470b70cd309ac8802e5a0357968ccf1f821e109543af1d90e9675d07feb78a5c39d7f1007f5c8c1d9fa9090ed07cf5eacb033f2375fc6364c3bc30744ca66e20386109b28842ad0994170e0423fb67e47ec48e0407f3627ffecea725494de2dfd8e2e061240f4578310a042c39d7b3a61b969a3645f2fadb91f06c2da7b717e74ae8d48e8355360aed24009413e4917e37f291c842ae6158b62b06c6582fb37bbdf6537bf40a58b3ea5d0fa81f0be7c04e0d8d7a9813caa3f64e08d72c3e8093fbd4edc536297b5388c10729346d4ca87bcc2eef0710ce23a884ddde40efd7ecee8efa34ad191184eb6c01d95e852d866e4c531db1a7a63c8291353fec37f838037a9bf4c4b21e97b87e088698f4352edde777b66f41c7bb1cb0711704e7f6a85e173fac67bf4c19c73b09e8f2369999f12b625e3c5f85d62c9e2336216b693606879b1cda7e015abf470c8d754c8f129ed8919bfa9ce0e9c4f7baa02666a352be858a0a789021ea1aef0f04d78e33db565b093ab0bd5ec9e73ed57d9274f851af5d90605a2727986be006821a1c54b0918fc1256fec86bb33bb9ee767cbe01bccf04868625908bd71ae0cb18711fac846b4c9e3740513373e910025facbe7c9633b09ec51d5e12c9aa30da1189c040c5b3d0a0b91befd7e1fae61e9157cb9487e2d99251922f65443a80a290ffeea5e13c10609301c7525d9f2cbc4cbee4a17d8d98ce68863411ae0ce0b2ace143a3ed6af756e6dff9ec2a365b63cd61b6f04bc471d128507befa12f903703a1f34594a0de03e2b18b6d61c449d742b4ec98228ec23638da3788e2be401ce7501b18a656fc5192a6dd2eb05345a89d2fa0872e470539840f2f10d9c5d02ee60b0fcfcbb8b4459d73a094ecf2407e19deb71f58325b13d037dd3dc74430141b8f2ecc3cdeedcacb087c659d186f36de2e9c643d8f8283daf4cd95631170b -generate_ring_signature e3495fc2ad438c8128a655249d4d6b69b6c17ee1dbf96ed1acea6380d07fb724 2adc1d2a97ca897fe0342c2165b8ac198eceb0374a7320da794d1bd38a45c962 5 dada0d56d195390308e9eb9304b877ed930a1c15de8c0a28ab04066cb0cb379b 85e9284587832bd7e59fbd7d3fb9b37b4b63e801522f187fd5bf45cc4f5454b9 ca11ed3ebfa24d358807b3ccdcf4c623af574b8c07c825b4b4b6f68cfbe9bef9 f77887b1191a51859ab5bfba228a360ce889059a60aaa740a5de8e9d0a7a1e57 c445962d9a4260693d6307b3305fcfd65807ffaeaab251349e02ec4dcb4f2d02 5d0db6d9854ffe9d26d8df64f9a0f5ac01aaa30ff2819e6377724f76d5c8d802 2 7325ae6c41730b79e34d127212f2834a6c62b1dfc56fdc676fca7f6b1b42630d92dd1e5eeaec7c20840ff55d6de5bb7cd1cf0b6f1b941011aec0fe842902d504149136b3398da5144fe1cc8c71e9cb0d6794bac16e3abcea9792915fc21a060267a8c0b46b7c60e5d9ffe12ca4fc69625488c723e96ea2faf0af2b430fc828055bb77a895c15e14f6cdab67db820c58ff5c4f34ac9891a1c4b98c37f343580047c5e2157ec077bf2ecf987f775e0853a68d803902b664aa6e98efc3421e7710e08eb5fef74e1aa83c93d60c289dfa174cac28778dc0f5e50ca8193c18f33f5049fd2d5982a2c5d3007625159d873a71ca0f1012d3e4200a6ae396bb12c41f00d4b95d8cbc1e4b0d887c14084b5d795d528211efd24d8b0909669a32083d6a7004f572503d0dafe8190c4f5960e151fa549f5363f6fdccc6939dbd597cf5db60e -generate_ring_signature 923a11626de7b0b613c07f95a600a1ae1b62ef2b0bff338120f4aca08a518f0f f12c207e70b2dd9bbfc6988a0ebe5f07f915b5bf2b1f1a8ba23c5db2475e8d26 4 eab9a7d80ad2d7e9060076bda332b1e2b31860426710e7071e5f34afeccd51e8 8d48bb5e57d8c7b0950384093d6187ccd03b0c535a0edd6da35a6f015f928cc6 079e64c24eee1da696543bfb274ead55ee33e3633b9601d9e3512371194dd4f9 6348488a0884315336961ace563bf106bef9f2c5e745e5204531a3e00695d5cf 2d4e461db0a3b63a7c05d4699b1c790f86351dc1fc96d1f8365ebace89d9db0e 3 c50d0ebe8a92436ef20456bfbd2b91f21df5f061757e6297cbc1cbeaced3cb0753dda12de97ee3bcda38122ac08ef4465e52313835f4e5ac60a2d5c84a21140d48bd38427129f7319fd2d37d8fb0d0ad65d319fb11e36d13b8c3b1fe79156f0cfea6d38a94ec9ed6d301584220ab9fb1210ee635af52b726618c3f1b0ff42b08045bd637569c4e3d8a32c67cce796175805d472778cd43a278993c94f7af7f0435bc0a9ca41bb6cb123e7d652d2a70d3e8dca0db564247ea617d29c25810d20c395bcc885554b26c751a77311310f487f2e5030989d02042985285a93a18550dd203770e98cd3a0e5874b27129e6f880aada3a1359ff8c95b62287e8e0ee0f0c -generate_ring_signature f7eeba3fcbdc35c01d69774f6d1e4ca270680e87e1af40b2773dac9e9b0a56a0 6f1f04eabe8e9213c7c5d4c4a4df1b5564e63b054d3e09ad81eb6c4d930ef44f 7 ecbc3d0e415e2c5360b40747cf604fcd6e4751e125ce2ed8f7922a317bebe282 60887705484f05a205b5e6c98ec1be1df8cdd4115f0f3edf9b545370d81b2283 4310521f1828d60b124567720d381d203d81d07b8c66434011e29bda436bf7d9 4280fb5b19e595916a02527cb77814868af83620174dcb21bc8da271ef680004 f2f8b898e39ad301c969cc191c11b162995a5e1f813dd63aae200a718c7cdb86 867556a39a4dbde3c7fabd5180eb0e7ce275ea211db2fe90e13c4c040c0cb28d f6c1111d0bdc89c508faad4ef2015ea98e1f5416324def724136e030ae647603 f01ce7ed8d9dca5629b5666899c8aff6ce0b58ad2fe04b77ae75c2ed6b9de30f 4 7a49dc7b8bf73358eb0bcb5024db650e740f79bb3b62de674715c29ba2887a0a1e341701d54bdb791d96252bbf7f2125ae29d52549cbd7800e72ff25b0014e0f1bec3dac65f1d1324aa604ad53260f2ee3a8d6ede607394e5df1a3a80b8cdc075f39840ef73502adb13e607f61945dab75a0274cd07d22d41e3b534294d073027b9e3829affd4b8e0a7814a64ac8ed6957dd377aeded35987c412046e6b22601344648c9f776501cca0f7055af58c9b553862731e05d2d6951b1e10acafde109be17aeac17d7b7be8fa4620b93a633df9b0b087fc89a927d280e39bfeed79f0ba6c6b08a540fd5926cccff9184cb0e39258bc531c06011cead0479d19a9bf107785030699ce1b31915d50d27716eae11c90f47a03260d371678daf8e53a6080a624d76522da2495401b211afce50b17ad336c2b89b610c5f000da1d328957502da737613ee8e1dbccc0d977a1512b9177fb04553c1891223fc352c91951b140e9bfb3b1847aa182ef05adbebb105a769160e2bd63cb2335d4db6947876d8b50f0e68fde6d3c687e114bf72cebbb8ce98c9def1c85bcc579294a119410c3c58018a55ceb9f0e00dd9d5a9106057529fdacf61750413e3f538e82fa367f19e5b04 -generate_ring_signature c4dee30ab0373ada5dee59e1362d805d6a2eb83f1a878dc9e82e1e9dda566d60 66250b52e8ea438a69f00cec532367c889ac73727ec2b41f696bfcec68b5788c 169 f2f2b60d7675c181f68eabb0946ecb76d12c2ae176c8d7f2b2f5cd7e5390b732 743abfcb7c102fcde3281f0989bdf8aa23a38aa28370e9caca172f5befcd9fb1 6925c7c3b98df3427d346866a2063fe956404bf6dd718ad32176af5f55fa4932 e13d9ea26cfc623476efdc90ae5e746d5c01ece1c662c1b93e9cf19a70830b7d 05d454706821b18b611d100393ec3307138bc84b0c0958cea0bc4eb1e9341236 cce2645b7ce500aa4fb89088e6ba8b90d488e7adc3403850b6fd762500c3317d df18cc2a78fbe16f092a779a7f0503cd26e45a8258379e4535d97a01e4f3af7e a9d9437498986268c6e2594b4932f8ac8798254bbbb9f17e9fc5be12aa35beda 9a8f7b61754085f10a5ddb5a7bc293ad28e8365f8b51e94a18e974b7b536a883 3295ada3db3830f1a2887c4f1403eee920a836108a42a748414da663e7fe9108 80545b9db676fd424f717135f3978b39415e045225c2aac5f340af7169e68b06 3d9d86d009b0be89558db01f5ab33c2bfa32242167d6a7afe06ac195f6c500a0 37662037db5136dbe094d1eac43c18c9239969f9d3e0039df9679cfdb4f3d18a 3bea446ef619e281d5fc24c358629581d78385b0556d72fb244c66d97e2c6483 a28b168bb36453c5fc8ac19c55ed117077e0f450eab8972815682e1fbad7327f 6e28882488bb1a06b52d716767a61584235e7351b839b84fb2d340230dbb75c7 52a7690a81457f57c9e79fcea48a019f3bd405bb6b66e61a6f63d9151d719a7f 9a80b4b0f25383a2c2f70d94ae5a4da7b15857d3242d05669e84f02d66e92edc a801c63fcd5ae02c607cc766f3ed531cf1e94316ef2d2989ebc611b200a300a8 34e4278c824bf5d404204de86aaa1a806ce70fd062eac373faf51353e9fa6202 4b6ff12fb5ee85425a5ef473502e22bb78aaadcf63ad4d93ed327bfa691d5a40 27dfd189f1ba9b2fc2b0d48bc6086b1a4f242130ceeead85acefa3a92ef86ce1 37b68e0abc377dc3c967591e5615c78e49ebb08f1b77952b5bbc8d9696f2aa7f 6bdc04cd1004485d5c703f63811fda02ed60d3e90e0dcbbc4e870027807f7db5 e106081b39c6bb35c5f5607e57b1eeedfba6a6060f21ed360faa2a8ca5af8d1d fbfb46345f0bd2e1711359b292ebf8bd6a7e39e531f986591954979fb09fbb2c 37b5b8821be52009c9e93c3ff1010474644f5b2872a8736650134e21b387ab31 c83fe1124f489b72f050473d52932fd5d68f2dfaa2dcc21136599cb918b135b1 cecc1ee7fdfb34c04581d11c61b300decf57a42e280edbe70320993be72af60a f2b51106e9a9d3d144df1c152a80a090331df2efc91e310403f0a05b9eee87be e17858155d4549918dea501437c1148710a310d0f90620fea070e83e02e53d12 46b090083bd9288b6acc4812e363601ddc83c03ea67b482eb55d2a790b77e4f8 bbfbcc1daff1f526118950dd3e2709c845523e19026123c5369a13c4d59939cf e075c3c1ea09cc5b80799103ec90cb60576c33c3b93a73c24b0d1ceb45d295f6 af65af5d55b0d0d15fcba45eaaf58ab174f985bd4d41c2d20b7a4526247c9714 80bb8136d6e99bd8016b594c39e7b137326ddb543eac6fedd100dff6d6b4b5b1 5339bc37bfce89a3dde58c7fa4db847a4158508bb50bafcacf80255382ac7671 c81b42947cc5f69eaa5c203eae4df3b3d020dd3a0c5018f8a9f5e34348624d0b 6d510f05dbe39d83d6640cac35d0a60068331cad220b17c520f23fa4552fcc87 d25684244293b811b3551f6dab2a5050fe9dc2bce52c15740a2467009222ad5b 9d87b136332a86b9c5bedc40467ccfb345ca8baeea24b84ca338928ec5eda14c f2e3f7adad9febf75773a3c857d62d3263120100e2022ae9b77c58b3c7b894c4 d7d5c94f7e248cbf878428a2b144af620e4681da88e766ae43b1d6dea2169967 30efeb1e0e068b2c359373c7add7676425c35d7c7fbf3bdb90b82dd824008ddf a761d5d202b96868e878551709c2b5ab877d7fe56995504bc9052ae0f0200bcf 55aa7d97269864c26740e4be038efc3b0c5befd21ee78be97639826f5f0f28ba beaa56cb576bc41b36f5751c3c7251a70398e4db0a3c09ca35791e8981ea1f91 83d7a2343fbe5165a375540f07a124513fcdc269f32c397a138f8af02a44ded7 26efe640c840ea1a9322378e8e90f84a17627e792190d3753a4d87bd730b18b4 f1a97f521feecc1049496d25dd3648443c601de11949d575b0c550d8baba51c0 d3a56987efaf33ad73e78557598c79892562eccd7a36207e1b4a8f2580b77bcb 5d74020c3d7c93bbb99d658b8fcf1cb18497208ad1c44bcfbd8a8b956646051c 0a0c3a31f1d7cdacaebb20388dab4a8ee07765177f8cbb039719985a5a942e74 e8fa5aa4fc7d0821c6c013cbafeea5d28eb6a24c9db8779e0be56f87383a3acf 78c803ef1f3df340b6cdd5855289dfde75746ae4fb6e6f79b902da3e6edcb510 5bde60d09e8518e581b09e441d9e9ee10fe68d206984d8da451be2969dc6351b 9f0d4380fe0bfb2639c08219c7c83b8d0dd4f8a37b9e7f956315cbcc91df1fb3 ee57237455930f2d6a13257fdbc4010bd95502b156592117befc744e68b6a4bc 05845a51848c70ac4ca85654d54962644005fab916c14b572137bfd21c90e893 cefc22e0ae4f7bdb5981e9af5226769bbfb4117a75bbfe9d0c0aaead403ad02f 79df1350f974a43e500894a2ef4d069347a199579a198bfbfa7309818f11cd3d 0043576d4b1652fe8a3006e223fe2907221a22c73ae9dfffb4af218b970c422b 178c74199c65363956e4c567def65f8ae5fc0ddb7aa7cf04873db552a18631bd ec64d4014cdce524f52ebaec746e4cb7985db07efee7f230f7a45ba0ebaf2667 61b2796c0984c0d366679762172eceaf6cacea13c448f10c01d9b95a3c0bd932 78b5e5e15304a5d7deaa7fb92d1dca0db3e8caf4a621596fb5efc9e45b56763a 8307fa27a58136ddaab152cb540dd912a5f9cc467f87529fccca7be31e3360b1 bce231185b0032910b1983e47b427164c87735004579b80ff136b48dca7dcaa8 14b20e79d48470cadb4c806df5af9b034d8df8097a3be1f2d7928fe34d2b3cef 4247fdc58e6f242735df1b4fe9ea9fa87c0f93ec02b7a1f57314325ffb849cad 45c59346b2e561a5ce513d69d53d60b0513d0c34fca33d1a82147187c05da5f9 5f7f797554373b48cc0b9fc19fe98fdf53fd7dac6895a3f7a7e773f616501c35 aa2bbb46678024ccfc9a9087a282316f6af2bb04c3757ef7228831d9833dbb83 dc399f899363da744d9d4153065b6b684bd658c393efd3590ecdff3d9c2ed52b ef6d0d6e6176b560256198b8e349dee4afaba86decd2bcce727dbe95e2b70636 d32ebfc10103684af4d1d40e94d595541aa541f4580b1f772655c653a02051f6 7eba6d64022bd3abc8a5c86a8895bc2997d77dc9a1945ce761b65b15da53c6f4 cff49af65ffe5745dca4cb015ff2c06015337e90d3eee87ed64de0490989c684 4c63ffd66a6dadb52cdf67ddb33d710dbccb0207c1e5566ac61f22a21bdb129a ce65e5281d0168a505605fdb278f1898e30d0a941f8b0f9764f13179b3401aae dd903cde97be0cbe8286e4fcb959f165ade330f86c365bc5373cc5d6921082f4 38346157f200df62a9caa5b51c0b39266e67e8c8b22f31876a0737ecb3924c64 7a59f714f0e320e8decd66d486b6aed8285ae48f77cdbf8db8496e43cf327dc6 43549791da8e97a5b368cde89d57dde61b4c7b1826d2bdca03c98547d6c1f210 5a8087729bcba594809cf4a31b6d2a66713d11f44282fe19fee4555fa38f33a7 80de3bf86da79e5ae3c839ab0a723291d3495dd3903c8f64a1aec3c388e774d9 2953ca5866e7d6eda4551ca518fdd1d8b88f5621483cbb9ecaf29097ef6876d3 bb9814f0e3957bd5dc7ce3fadea884545594cc240806159fe497ba5888164654 335a5909bb1f4401db26d7e55f726b209d154f632e713e1bda691e1346fb1fcf 34865ced6ca72c9e1ccd2635f6ff2a3689d3b968bdca016b9775e4df7e58377e cdf0adceffeb97350dd41a3b6ace99484a4f4f7980ba08c3c22bad35b2d25622 1095dd8c0c3555876d83821ef96927bcdeb3c33f5785b7eff6b23012c84e6bc7 e04fa71c25a0e3484b479825afbaed78bcca81d1ec16d0e02b0ed26732ab70db 383dd6af2e2638681fb025379fe1a58438a5b71ccfb44f62082fb711937dd982 3dbe16c9dacc2a7c3d4f2a7aadd7323208de7e81cc92dd4f8958768574ba8d0e 1b724b508e6f688287fc9adb44f097748eebc537b8a9ef2e5ffcd5eb0db256e6 a8ed0d0d34c15f7b9bd8539eb4f0db1a73703f7b1a684590737870417889d75c a1ab8437038769c0677d621cf9bcf25c02fd4ea19386e18e7a1cc4e27ac591e3 d8263c35b3be8a8054c7bae2c394b6c6dbbab44c0a3a8ecf0d705dd1fc8cba16 b903646b44e144d83b2a5338054fb318a0cbbaf2d4ae28f4e683241c72a45645 f66868f006d85e3cbea999cc1de9aab9dab8ed420e887eb6d8b8bd5cab1094e7 f062cc8d8b539657ee47edd97f11ff9b863157b951af13a736b8d16c82a84363 8c9fcc3758b9ab2b89d1ee8457b0d666087169ac25bbac716414ba5587047695 bfd1f74391c25ee492570d813899533797a11e321ea9e3461bec229061206ab0 455a4eba5a915e05782377566b72dab647d9c633dac408aaea6f8ba97581f044 e0c54ce9fe9341fafc56791db8d5ea68e8cbbaf222ff855120afd8dec9f62695 377c0338298c3f0f80e509182a3fd5a40c81f40d15b46b762c0e5598eed8ca41 1c107e944813fe33970f7d5cf7e1b12539e36364181450a0f60631c3b10f9813 8997bf02ac05b9491cd822642d487c72084752b5e4150b0c703e032418d754ca df6b6f5942e2fd0d674c709dc04297c935b33fb597edc3a5734af6196318a617 ab7f3b2f0558840dc839c5f281a190d8277534848c9f64534d9b47f887a54809 78ea46342cb94abffecc038e4e00ea9a5699dc34f89dcb625b35c09c31c20090 209130802e9d6511db53dd050c8c20963375343d5e47823fa970c40ffc553c3a a7b4251d7524597f481c5a5308d1bd3b3c0254d352a9a5b78f18336eb123acbc 7dc00f08f1299dbf52bb029f19685213291c8cc9508a1b2143d5e7b7b18b1882 ea3bc724e789117a5c484b5b84441b515b758399062f2bd98209706bc9a8c3fb cb0f6539b7b8dc5dc22b2301428b9e9a2f6ab08787853b664f6845ea9e85d9fa 315c2d8496df14859b7901592b800da7ae4db9f8c92653dc6d0c108e8b4e0eb2 980a0e7e3ee524d557a0bd194d2b81c5c305aba1c8c195691e4d4ddb4d257f52 41763dbab5729980d1bc0f26643e657852421493e1bdde3b0ecc756780fb6c57 3ba8348fe5bfb2577e997df67623e132d5a290c2eb381144bd42470baaeed8c4 6d9b9089ee7f98aae119f6c6b622bdba6955abe4e874a085245635b8f2b94393 4c82127d23c46e3053c6490ddb8aca8eb567ce3bb1c3972e26a4d47dde728567 5a9a20efb7bf87a0e7c91eeffd12efbbf72d7ae4c0a8c25b9d9c3a4ff8411c67 1d186b6ce7892fa7a1c3f4f00a8b1fdafb82d97e9e7aeebe18750f45d114069b f367f4b4179fa6d22d6d22a828c33de8ed0298cde5fb5d17863509f9417af0a7 2811d5b3ed14736137126bfbf65204cfe201c5298f70aba9d4d3d0470d729ae5 cf4b99cc377fcff0b7ddc95b4f4b523c2310394ca5bf1a17e802afdeb906cf32 e6a2fe52632d2367d577c8092a66c377ad37ec2cb209c7786886e5704bdbbb25 4ced786fa5fe2666af4caf36d267efc92906c327c06d449b601005fe4ecf2ba3 c742a00349add70c374c25e31f1e192e97705e210bcbe81294380e8df3802b51 6bca8c87b78e364890984fddbccc8c407e512a5e691f8c9e4e04f526eb27db32 33f41b7270fcceab849b60835b0c5b8f3d0a3362db18f6428ac8d6cc629a52e9 f6ccb30dba421f16c87224c1fc6c910c28d928947aa80a4aadebe14b5472e3a5 b75d3c6321f144f78bd014df235649645bf6eb8d15a0b412c8791f97dba6ce96 1ee09cd0a0c0ffe069acb5feba91b0d1eb78b810977acc83131b03415a3fa2cc aa4181c9559f8e935047ce91591939a372dac2b5daee47209472f919c6035a02 147b4551c883e364fe17ec3d1e9a40592cc27f8052e1fc7d17a6c5ec24e2494e c9e70b872854e07077e80d0f66a062e15c46c3e108d7cf656a5acbb5097d38cf 32e3e959f6dfb3e9e3bf1b1fb3c822bd8bb584966fa11dc1e444d2ce4cade8c8 842e0eee7a3fe30185d75956878c418aaf33dbadf2d95223031edcd9334a14fb ce6acedb380e0506c71df56f08e37c9cf6a6a7419a0a84d67421f8c56524ce82 c4dffae7b32ae71007f42fefa57835500094a53a7e59b4003da57be6d752a1df 7c3a885c38d04251b942ae97223b302ea5f38383fd01eab497709c42d5798a6c cb5531c9cae36b968f165726c3918cb6f0904779851d8ed46fd9504a90a39776 7229ff35e6048fb93a2ddf96abfd5642442d91ed4ee2928e2dd2d9819604b79b b79d0f869bab272133ec14603e03fe4cbbe5fafcd859b8751d5cffa2f13067b1 49759392147b1f835d36bb592ab1b387a433067c4ff3ef27c65046c868028b51 466ad6d9a84e0662e61b8a8abb0bf5df7beda276d68909d944a28ffdeaa8dbd1 a3b15d7dc0194fb2f1aab6a358d2b113f8e82a5effc3640a89671c76e6a70dce 43b84023745566f7628a0c30be507cb86dfd35b80dc8eba994cd1d2e0af685f7 b8fdd27e4e758747e0d303623d0eb13a255037f5154e0d869fcd2912436d0036 1070a72d4d5efdbef3183f5a0ac7699ca4f91f3d17883f44bf7fbc51b1fae1cb d4506ec0ae0bb3ebcb7d481bf5ca8a68e19125b15df4c15d955dbee943294e55 858799a67150b2fedf3544b0f1e6e3c3d26f7f886926f57f53dbca535c8f6cd5 3c2938a328956c51e42ba52e7bbbbdd2d85cd9ab1807ca2451822ae8002a9f47 2a1e89c0299dee6608a8652416d21ee18b71282dd0772c65d6d2af8174de8890 c710afe34c83d62d6fa2713f8ecd695ba6f7c3556f7a84f9f9e817abde3fcd14 9195d5d5b1a96ddb0f8366a2ff9e0ba783767d0bccc52449136ed07c05eb1729 8324d8cfbceba7901fb55976c5fa6acb13005fbd838dbb42e77495c867adbffd 5dc1c7243b65a5a6de9bfe9493ee49c369548a1b0f7a707c50b49fd0f9273e5b 8cd9443a7faa14d017876ac6116a4de3633059124c9f55e71687317ec4a4d4c9 0749cce6e8c6eb0b30cc9599b9d1e2e8f536af0a52e3883c9b1a0eaf60bc61ec 1900841d34f0df254a39a2d586c69ea435e108c903e9dada712075752da3c470 72fba31c4645db0e3fdfd6c4bd4bec66dccda0e3b1f39ca0963dd6ca237eed10 00de416b0503948f1eedb67da56b1b6b529f648c69f43bcf1a91bfe88a8c0e15 40b401518efa872876a64431ac7463a297acce71697edbf95bd5a4584385a91f 6300f0d82da691b8d88cfe2e2766f7547cf1f60ffbe70ab984cf1b8946334fd5 fa3eafab921b0a825cbf4841a68094cdae5734aee61725ae8c598ed4f66d43ad 45f89106066fb315ab9004ae5031b1f914599c3b0d9f59cd27ae15c9289dee0d 51 550f2cfe247143d73b8ca8c94230aea263cbdea6941240298a96522a699ef30295e4ac9fa151405a5e545c475d6f6d864d4722d248b5e9fad5500c0c46e8b8085305395183db1f246fae4c0ea40e76be4491e0e5a888415a0b15b2dcbf17b80fb3a3248300cb1d61c8418929ee52227e8dc819d4f5a332db61956e17ad428b0cff00324b1ac30ca7993b73942f9e9dc1b3775414071f066a531ae765e49b250a6c21fa9ca1f051603e0eb0c49d2b1b637a022db29f19cfa983474bd62a0fe901aee7329c23f522f01473f5d764a78589b97f24f06e82acfd38c07d368ba02607e0d0653463b71d8d9d228946999b6487bc271b0f285876705cd02b2032b501086a45fce6019dfd7e8c43db56ef03f298ed1843d8af0b8da285231c65a8e1900dc8a3a2bc427c23b03e1efaba87938fbc06d549b12ed7072cbf9f8c10edcc9c04748044f3a359c9842357ad9150b225764d1296060b68cbe961fbf7e8768ed106efe61d4d2e7703bf1b26ee9d01aef6e0889d5a0a587c82f736e49ec248e8c505abcade97aa5a018b78d3fea96c17f79f6db7e0b9a729646fec2752eb5e9e460f1d540c660f483f26ccf2afb61cca612f85533a0834b523931802fba1bb5bd80635a85a5988bc00ee6b59368bf520703cb69112d61d957e0c044b48228ed06a028b3ffc9e4aad4ec9a02295581c5b6859b6e1c6398cbcdb00b48608be1ce546035d2b98386520fb3fb03bdf36f58ae11423821cb80e18f9073ec7f1ae14f98b0e016d2fb7d6fc2be583fc4271eb65456630b5665d913f5334456d65ffa5425709aa68bcda0225092eae513213c35390aaff684cbd0c2646c3a20415800ebb3f060ef700a8b17e146adfeaae163489f3b738166518c5aa0555c3a2cfbc2a1a020553836ae870433f1c3c0e507d12ee7d80a2d9ff7b2dd0efd78a0a1ae7f788170ccb8b65cccb581c3a0a9391b7a52a2faf7536c3ef816e40ba7c4ea75b688b240a14cc3ab6dbb2209bc81182df55b68623c28293e8888ff25a790ba8daad713a0ad47e978ad935d2be4b07bd281c0ab2a252038d227cf9bcc2d64f4193cfd3b601b0ead5123b91e69b2afc0c85539f7083ab4cbe0a99fe7a414723bcbb7063430687a18661b7fa620de67e978454f87f634cf14a28c684756cef67135d60982706082007a751f92b55a693a77c0e6199cdbbb85e0827348951f9da840d07cc6001c95cf68230c8c6115668e682cc483e76a3af70bc770eb464d3627bc041daa9016ed9ddaa9205295e272bb3b3ef3172b52ce39338929f7f944e5258798249a30b57a3397b680d70ba5ef5d6f30d1a5c829a82d7119a7f80a578a64e1bfb5f85081b82124e8ea116d5bccb8ab41379a52ab317e51ce76506db98f224c138a84508ac7ac7056cc9820db1f5991de300159dc2975ed101345e8e715230b27ee8ae085af51523e604926cb124bb70529238da91a8094ce8ab8d12480e75290b5f4d01c959b8bb60bdacc774764deb71d000e5fcd2bf47f888519e10b5524d15df38090655c1ec3a231268f8ef87929fa92e2362d33e566f7c5046b78518232304bc04909b52c267d5e41e36f998254074c97a56ea2008631156dae267581e93da140d17c03b20f2f53919d896a0b52454bb8e19adf1314f5a9d9ae5aeaa8349d7580fd4c08c1da9209cb9f3a17583af0cffb89c75df70080f6653ddcb20b4b326d308c337636d587f90fd2c0eb13060617a4e16d9ed033bceab9f22bf2697dea10a0ff1a186837eff11209cbc4de5b035274f907ef87d5b8e6f1b3adc9eb8fdf8200aa04a2c0d28000e0feadff43a9ad985f0b8e16a124e4993254ee22869ded70f0044e26562145d155ef60f77aced604aad361b8eb573f7c504398b83ac25b25c0d6ec98c238860462d488e8369ab2a737835d476dc3e7b7f811d04858a9f346c0ded28c9ab941d91f81abf983fccc22841db7c0171e40b8aa59d41b3cc4f618108d7c91b213f6eb2d5c8aebbc41d5c3c2571b3b155cd1f2d85d0c961e7e6d51d0a10a393ad2198fc4407b86de23ded44f86b607f094cba653aed28f6ea4137eb04ab06a5b215940fb4b0547b4e1e416d6925a1d94b58d76b46c31ea3ee082f400c312b0b62b731f6b0ea7880e3f157da73b8f642e34380f064ef776fa49e9fd90972b948a54818660920bcd731cb00880dd6daf963b51b708bb02eede2336f7f07ba366256992311d522ec0c9e5e86c8ec1bc48278e33490670c90daad6970db0177e66a4d09af016837044adfdd519f85393731a9a2e9a570dcee585ebd1b3f002ee2dd1fe11afca75e34f980ef12a9b69c39a5046dc15febb1e53cda653fbc0c0660affe8cc0e6dbce8a1c5f08b1a9ad15b0c25e635c4ad1a59a37120300b60522527f5b4907cbbef7a739cac0edd54cfba16b42ac201fc22b94b9e2848e790ff15b0613a0382ada2cfe671f6929b7bfb032ffec6a4e6f389ebdf8c1167dab0baa5397e4725165e21644189c43e61f95768a1a4cd72cd4a299b6036f5325ba043fb0be698ea3a76126e222f5f656f858bd1c5c3a4fcef55d55a81839072af5044529efb2b8fe57875d426615768435b89da774045cfa7c43ab48256282547b0afbca3cd889d897b42daf38680b5e505bbea6975fd83a5f92c6ceca2c2ad48d04b5be5f14882c82f255d8265238237899a22c4e8d2fbd94ff7e8ec7f7ca12af03c13c28a3a5e94979e158482cf922259c27c96c7d0e3113af8b699a74960ecc09a62cd9c86c313000cbdee1411d69c3d28e12e067ccfbc78514277294a80cc7084680a24893e6ca23c9c43e31c7d3d3817933a029bb236230de0e0b918a44a603069c7cf48ae8432f23a44c75c38161168723e302e9b22c9d7157b7f00ac7970be3eb599d09df2313c0c0bdbcb6bc77e8de48246d7a4598ace9df80e055e96e09b4b6a4a1c826b5876c5965873647b7daa63665058c45dd5ba651a4b518b0270f018c5d10476e7ced2b9f6eebd132b37bf3d2a13a55d338d7c5dcacc72f4dd2087d39621969d2183ba266f22b145758c4d82df8b54c7a2c82135021a8dbfc4c036d4b51f51011b1a51a5a0cebd6a511fce1838d330288d02d0f5149adec4e060ad664500ea6eca23f74fd4063a41ce356c287345a04de842bebe17658de4c33049fbe98bd5bd68ad9d744dba870066d96658384e21c5a27284055c74b40463b0faf29c0dda1106b762c2d4633ac7fe6d55783f563c51807fc1b725d8d5d92450ef05c97a28f49dadd28d260696fc8e3bb4cac4217a5665de8041c0efcda79420f9f58b372437efc4a96afe6a2dcce3e642648a270cae90fa4117e75c605189e027fc84bffbcf3cb6c0304fbdab524f50afde3d0e760a15f82e35eabfff4dcef0595384c3dd73815be1902860070a92f9582c372440e45c4061fae081f1738dc0679214a61bae33a47a1cf4ab43049d271c142b02c4db231d6407d7f047d5c120a099c81deadabf1bbb59ddf3a7b30bca9be16b98c92e28390c5212c188d7196074e00a084bf47f983e4d01cc390e3f0f33d464310e57cbb813a4ce0716b07510b7a319df55fcac89d55f28bef3f33da75314ef9d782c617502694b7d517d7530361aed5ef08203e4ac01aba6fcb8c1404d41f52f8e746a0c91fd8d89f00df810b9ee016de75a61ef80111752a39b413ec940266a9fb120be4512f30a3a622a80d0d8a266c9be3fe8b742ed8d8fe2c54e8eaef06677a754798d6ed807516f9940306264a77c824b064f0ab1a47bbe197bbebfc5e0277dcb54c4415e35338d21e0c933080097a69ba5329e52f9c927c1c143c68e0eac2a3f139b37ce8cfc761be0aafa2a799b8bef98c2aff2de4d30606d031e490851894d9d74b984127734b19024d0058c98d86a530e49c8beeb714ed3197eb09eae8bd8b9fcfc0d6cf74b11300a1d043ba357927ff94392bf01e60e021a540f962afb1141902858efb36877c0c7eb33b1e0851b354f5cfbcc0d561ebc9cc53a443ba6240cb15fd7d56ade3130032f5038d8529d014d10eebf3e3cd57e23a0da2d672b3556b10fd89c1cc37ef0c6b6181e3dd4fea0d276084744e53bba3e68b81f851d1d9e5bd7130d3579faa0d977c914c6aeaff3354a6a197057b6737c757533ecd9b7f0142e097304c014a04ffdce71633d8f8bc0547a3ead1cfd5e16e23767079cf80b12fde1348b586430d6b207f71b66bf200a9dd7f4c9d16293500c8a967242beb8952a2932f2baef20772b02152f6baf2cfb847e1d311bfdde2da511d9e27f84ce338fe73e70f16fe030893af010bc2b1449b316f28e0f7918f5946d561188fe264a131b06faed5750f90aa4ff1d6bcf0ce5ff9347ef0b4fb53d8df66bd4cbbc0072fce0b9193b2560908077e7b9ee0a18011947ee0a1143b87ec52c87d418b909a7ce5f11e0d3e410e8104337d687c3b53d3eb821d6c7065cca45f358739a7adb4f8b99fb4d40b6c0c833581c60fdf04bd10a03dfeecd68511da41456941672a447ffacd7c75651d020553f976558cb338977e0aa2a1b26a722357dc86fe7381b77cdda3cb164cce0aaff5d0d9e21c46fd188b378b2a9f460cb3db1d934c18fb75302effe56c8b570eaaa445869dcf7a8c87a38ebceab762648cc3c933ef80ac10d3b1d6317dcad4014858c3a9ff75f2f7528fe81829605845998750e91b9afa5e3476766742eae7093428ad317749322b4fac899e4fac6e1e36ddc42083579bbd7db80b750a94e0054fb39cfef6b40b1ad03fae31f385170143c684c3925d4c25c6e8d0cf2ad8a9012733018daa75a0dfe6dba31d2d5af36833dd33521219c0e5fd921ce6b728af096284cdaff9e192b9ad120192f0a628f8f399438ef6275542a30681622eddbd0f19d08b98ee131d1303ec5637996f5b83f0e3748b84fdf5bbaf7d3f911a60fd06f64db90d5228769ecec72c4301a7b1f68ef14e0f6533d8e2f4e460d2cbb79a0a8512b7a6ce9991355b53fedc05b1c696a10bb0c69fa99a287de4243e0ec8490322c214baccd20bc146fbf5f8e5ee85db2afc2d8b834bd2b086e28632988356058790993c85d636c00f1512a80eab31eaf1bf0fd481ed8a9b8f5945b04289860a42f561b24ce53793d69934a0fbe93a837e18f4041ec6e38b620db9f7dbe4ea0a408d4187eb198a3eb31cfcf7d20f1446f78a412538ebdc74c9174ffe99f2180eb179a255caa1fcd072bcc938a17024e799be4276fb9964e60aa9d4e465a0e20b58eee935f84c3e2abf731bd6968a39bba4af52fb30f8b4129f187bfcbe9a4107986c5acf2327597eedcab62924bea6087aaa162807b77f73f1271f338571f703baece5d2c84b9ce9effa23f40c6562933a9e63d4aefa71bac4b201f1229ce80905ea987f725f3818cf3112cd7ccd835059932337fb688feec3e24fe9160cee0360122164528078155d3473196d63da43da105a3edb8dd56126f5c00407290e0600d511250ee64f9308ff23d92bbf720e16cb8099cc1b414293625ec06edc6309c4a519ad35ac88bd3dbc3e704614d8582d0fb0bef4533cfc15127d8cda67590155cf69ed41aaf9a71dbf83545991766cd7d68e47368f7e20af227106f1bf2b0216a8e199f9f0fa3067d9387e0c5399f0bb6e6bec51749d489a6489d8de4e360df1cc51722fcd8fa18775e05b9a5793961fe0db7b5826278aa91ddfbe15ecfc0864cb0b50dd8991b554bbfb029c72b0fabce898943c603d50eda6270bbdab7d0bbc2cadea95f67911aab0e151cef5de98ef9f0e3696b52926f26b24e2fa9be40cc1cb5e8b2114b38421e53d083fa62d7bc9bdb8be7b17bd8323685628caa53005c93d4f4a0daff10d166df97bcb632b9ba2a234df630792002aace248971eba0144275a3e15b7fb41ef51365a635231291e4b6ff2ade0b4123c99555f644351003ba557cd4d0b47cf8a1bb91378e0fe2c90c680d812816b46aaf6f8ca7456bb0599f4a76cfefc5cc954534fb0eb1a2bfb6ccc719099c669c49bf02ce660e0b90ffef7308ee778a7a4acd9f561e983417123ab7eb62f97f4b89d48f6150420850eebe38a49dbace14c90e531d837d994697023678c5d6ebcc49e1715cf03dfbd0bedb26f477e970356e58d4b5ac533ea479b0668d19c4e6a39b9fdcc709063e60e1b16a9604a70fc71afe4b33177c372085663a941a6c6f6077e5e521be105fb09d5fe92b1650cb1d8cad7ebd2df6a5993fb236a080d38b6398f82e36ab01c28005392a00ff0423fceca664a23b529de58994426b98eb259e49aa85435b392f50c2ca96a3142fe083696ba54979a168cac9a9e89a254c8ac610c938bb897050e0a5c7808e94dd1867a34d2222e6797f053b1ba32c4157c0f040ee2b46b19c9d80f5bdc8788bb88c3ddd5975ace0881d39fb23ded2300c62b0bf2793a36fd92e300adbe3b7958dc71bb07a18efd4a939ee3b65d16129777bab2db1f9575d126a707b398971b3afe8fb5d8e4aa830bf7ebd7348155fdd7ea62d0c4e1d21ad810950eac247cd29ee894e2061cbd3113465d2ae91052b61499cbfbc75622157b9d200487aee91a452d767553fd1eedb11027c612e2494b23a1a52d52e91fc4b8fd8d0492c0dac6c0b1d67dcb28385a8cd72e85b5919d1728293ee29e711d60bfc7c400ce7c2a0cb0ae9e4093d11955007dafd6ce1eadeb1a24377c830bce37036636069d93925aa38f2a6d8b6f1f48f999842010f5e01a395726c9e5734866e71ae004d444a58acabca849265f18f749a967b7fc17e56a49e46dd3253f5bb16cd3bd0ca6fe23661851068f0f4fe15f998e2ba86857bbb451caac8387f77effffb22a0caed422b23792a5bdd681bd97283c2b59df097b7fa4ceb89df1ff0ea7a1de080d1ac6122ea8130cf9d5b4ee15cf3ac689fd019ae9a0500cd6d7b17ef8d8215d00b0a40d2d85288f0aa3c94caba8deb695c4dbd6066daf8eeeff9081e262e3640eaa5aab602329b4c7a42aabf380eb804fd56dc475d58cf9f6eb6106582569c80719ae734362b450fbd0d51740fe2c44f871cb7221b9a9b6a7a4f281a2a28fc60345e9850557bdac3a93fab7f302106ad2ed2cd0e4717c79456b69865959f98b094331860bbf05393e18d43038446c185b728480035e1ad29652eb35dda43b330b361b63f34847448c5db957bede36c009115bbe7151b8ce5c7b7a72bea3b9a2042e0a907d9137581f3341b77a92b4089b01031097f40b1e23db8818686e1d6a0c827e44ed159fffdc44b65cf56509cb539bef77ffd8bedbbb0fa8f2cc846385001ef1adc1bc5fe6cd7f31b7409b3b07b40ee67b02f2c90d8de7c4dba372065104df9a1666c09d7bfd08b14a21f7cfb758137e296386ea1ed73eee68fd359f1e03cf94507002645e17c60182ae3462d44a9d9aa522156d7c2173d6731bcdcd3e0a17ef4c8ceabb347fd59001815fd3cec24005cb9aeca0a24bcf16b5b799c14d0747aa7485aab69c7914d40e765b7296556b470feea7e497b32e26387a6134fb01f170aa9899aa178219359b0d182e14cff5f7fdd3810b8f17fdc4c88a15364a0db47dba15577acf0e1d01e9f45029364d224bb671a616398f9c101855991a4604547649a901b4006a34f5b46321a2f6ee5f2df205c3f957414e39604b9272380760f39f1a44f9beafc65a88c853c0c9ec755dd9dd0eadb572d00e994a48e95a04d95caf2b6416bb39dbeb4ca5165d6b7a6578d96f13d922c1a3801c03cfc5e30cea4c8371ce829a4ca30966653469bd488f890e297a3c50087f07e9532a8eda0cd2957b1f99f00cd63dc7eeed770a51912350d65c58013d159125bc2f350413047bd081ef106e72c2f19538c9b7bb0202800bf3302d9cc095da9021d5836eb20ae09678c6286e175d47fafa72f5d04fd5833415510d8994a41037084e99f7eb01942be52177cbe5938cca162d353d5531c612be695f341b69458f04e6f277360cda48b874dd0f216161c87af81f450c42d3118f9734a61c30c0f73fa572ddfd099feb99360e6b20c8ff24609393b6b825132b67df31cc042e0f7290ee727f4802ef838bbeac63fad050ae612dc8d7f5ee20a0dae75dc43cba61d88f19c6e1fa0eda384f429d11996ef2a7e669ebe126593d4efd86e83d1591a3fddaa42544f2025d0f33e31240ccd925cf1985abc614ba50ec49430e89daff56640ac587f3420686af1ced9944741c869ba7ea50cd6bf22ac3a1647f015d471aee150fdfc05604d726cf0acd5e84a59b168b20ba2a7994b778fe5affca0322ae2a6111bca20201dec905b7d3317b33319cbe00d4718c64db3a11d6f365ef311816936ca3052d0716b57c354fee3c06d90eed59577bee10758538e4ce2a323a09446811dbe02f0c9aaa76595555977fb03073f1d30df66429472749ba11366497ff6a658715f504abe5f5ff5845117efe1057a6c6321c8f851ceae6af9f4641c0e965cdd5970005d32a920722b2da3b8ae5d178f5d3fdc5697543934d7be637d52aee1e06ddbc0a6aa682c560baae8de3ed965a5ab9d8dd337f60c9e577db9bba0f1e6f7a87a204cdb74687b04c7dddbdf079d29b92cb3d21182bff1d83fbb72b54ae7a598d44023e25e1abbedac7a2dc20c303b29d931ac18d80bed543803d65dd469febd9900a12ca20491c6c1c6925f70accfc25481f1ab619ca358a2ff8aeb0ddd7c465890244ab0eeb0b4b6ff35c0a198d25b679dc8000e15a098d02ad71dfa42b39839607213b35cd7d28f47b0d15eec12c1d1d6e0cffadad7ea636834f244e954079860985b1cb9aaf8465729623250c669c481737411bf4ae4c9eadb78355c6f976ba0d4d5cc6d696186780bc7f53941d7fc6c50715c4c975ba2d414ed065db028fbd0e136dc631d7a2f9419be922277cfa8a99444ec946f071899dd973340954612b08ba99d140aa2b3b0ebffe0e0858157b33fa8aa635d1120cd5b4f091b2f7d4d70d752d34ea69b42f9c24f8e20db8f8b48d6018fa3c1f68e82945c3d2d7c75c4001ac6070e9f37649995d18f823f2a387910959c7e4b45b52f20960f42ff335f40f6b29eca81894b8327dc340d2e349f3b89ac3d81b899f4b3c871f8dedf0d32103eb819e999fb0d90a75d71b9dc4fbc7fd9c8091dfd0dd9ac9e0f26e39dc9b280ff60fd6702c6dd246e21c7c0bf8edc4c2875cc3d0fbe5cb880ed1f4a95b8c3401a16b811bda412dab1d3a0ebac89c0c63b2e686ff274e3ed11fb81e5a8c817e0536e5088246a51c1888ab3396623933240e1e51b332d8162cd39c3414fddc3509ae1d46107d16e66db727d4e6833c9bc37b41518131ce274a20b2919c36ba0007dad9f262c3915c174d286808d9d69df6db283fbd916b4984d986ecef6cc24708893ae279bfb5346570e0903df8893b08d81e6c08072d6fcd1b5e1201c652fe027302b5121880483d2dc87365af6ebcd76096419116c79ef3323b43176913450fbb1260896e75a0be1d17801a8bdd6d8bcbbe0d2bbae720e384b728aac4f2970cfd590820118787936bbdcceaed78df2e8736628545804baf900780de46c09b0e66b035d6f5b5e6d185ddaf2919963447c18ac502837d5b4f51c6ed68bbd3bd0799589499c41e62779817ea25f19561e02082754e88c931c50c24f61869e5e101ea4b9ff34272e508cab3c9c07cd7b800883c33a065f2ce490bdf12ff3342400efd68d22c3e83a43303daa633b9b6500ebf8e42d3d58d1723bf72bd98294310077514f10278cca3f2caa7a8d4d2451502e598f37332da1c9bf86805f422d5bd08d97c2a12c7a72a5516962d966ff44a5b45c1b12bbf6da474813504d1edc693062fd1551b84bf4ad7e7b2ce18892905b5fdec551d38b0837f55caa56910c5cb0327188e15872b48970d75661c5b76f7d0f07dfce3fae5a7d28417d0648d383d097bdf15b78995995f581cfbe9fabd1fd7097a27c2d15e980d1e2bf6d70ccc5b089ad000145a4c0f78a1667258e695cc3b46f82c596f9807f81d1343d787001f0c3343dfed759eb42e66164dc0bb494d99b55a8748e1886cef1e47b15af0a6ed0b69aee2ad1511f8b1ef1d688fea65238939dcbb397c8f2fef42c896ffcef95907b5e101af3cca741d52a6f2807266ebef644b9b7396652c2cd6ae773795c4ec0a852ec1b9be565a37bba45219f3b06e8a0b00a8cf0503a25297068e9840c4f509d21c4ef0b03c08f3f8117f4fbf703a60b8be2a75736cf387f33dc4655bb571094007359e8ba6d1e3c0d8053086587e01a735339b7d2dfcf466177faa64c19e0410eea74d493684b06c376e695ecbf7e04ea14f5f0c046307d874228259254a047bb1263e30ea1fa1f8d89b1178e83941e192350f8284aa75b38c3541ba0097094ef1d1e002adffe4e4fe29cd2f56845e6cd3c11e4ecdeaa96effb41852de400f1bd2e9a712e7cfc601cf62051ecac58d86dada2acfe7e789b511e840883e55064e2bb20c115e6a5166f9e879e83ff1f16161ecdc82c233f9f9e93c1d3463010b2154a51d4cbfbbeeef4c0eedca5c73379adb1d743c5ba66b28dfd7d6f8691405581650bea1b09f5e9eb95484eb2358392d43e0afeb1b8cab2422e8212f09700a84fafdc7bb6c33557620356d4890c925470e30f31e7cc431fa72e5f39e03f604cb21362b3a19e9e4b85700323fbdbdbe2111aed6cdab45d805ceb8c715c35507b141bb3e093c69f9de6212822dbb5318e02eb40a171cbbf949e31407b5c9ee04d1a483e85fbda27a92287d85a52490c3eb86718f139837c060f802a3c4b807064c25bf40a9e047f826d1f4af5fff76d04bfa99a54ced5d80cf8d28c73a181c0ba3f4aa59a28a38b387ebb46e2b7b94e8c960446891445b54aa52857c29c47905f41d6c00b711ea92d667e4ee6abf0295f4104527440e4df9b9e238196988c00eae5517d7f41723ac9eac1647d02b500db7e7eb60d5612e2e89173562133cd80959314f95ade28704cb9cc63c8967354789581142611b8a63421a56bece799705b929bdf292b065015b4f6b6e582a29a4f175a13ad69ef897ea2012fe2b7c3e02009be466bb14c8a39af33edfd6ca5907a2888c0b8d8e5e1b99857060fd4e74053375370318927df8608de41b6f37416ad920e8813f4dcb47f01339557f986402451207edb5b442248aa34ca5c4946bbbd1ff93bf51b9001f0d735a0c4857880deec9b59ff810cc193b8ed503f8b8d5697f2d736030e61cd40cd1cba00aee4102eaffecaf7f49e224a3425ca9cd850c3fb71a1dafcd52a948eb0e3dc57bd0510c1ed101d7d6dc0ee5116ce9ae3ec0734826cfa6f9ff8271248c72acb807072906f90ce457871f922b8d74c95ae7cc6ac98ed610f65fa986b5d49523e4f31e1b019cef1ca0f8219ebaea071b116fbf8c1d38c239de5341fe9f899420d777183309020d86a19d9dfd2fb0caddb266c8e7d9f3207267405e554df1b33dfd519ae400d05c70332278841d8d62d554ab9e43db5bf634c0aba75047fdf8f11bb40443026107eb3c7e401ebb07d3b298e8d335e5fa84f8d835495521f044fb2927a625068216ba33efe82b36f6bdc29b1acef4d7b999887cd5d944d639cbbc3b38eacc01508e8bccf0be4687ff8b58098a87ae2b1978960378ba23e6a0052fd105f0f403ce969dc2be880f589bcaef738cc90722883c2b0c48556f14b85b9a14542f97081cd63c3545928a261ebb19757b0a57cf4b3667c2702b96649eb2d9d18e8c8209e96a4c23263c3b51033f733f540d4da59c9ea30e1f9724c745b7ab421d63a10dac08488ae51975071cb8c4ae6df0553fa349d62e5a7eba57912979f4efc3050f33ba087decab2a48d22a1f26bb48467fdf54eb986fab3e74270c0d89bcae0502c32cf0a88992ab7bd11184b638de5b1dec393d0fe0121f31216fc26ffadd17050d76fcdb239d02b2e70e5954cb19ecbb84f422e583a81868c63a2b13b782d7064c506f463adc2809de72e51cd718fe686e17e30fedb13481318d739d19fd260173a5cd8a3d00742e3653150bcf677800bed17ee6b2e1a63903ec7959b5a1f30b9b9ce010dce7908da3ebbc3c1ec8c4a1003bf4e21eb779a42f5d03e04b934704d1e9eb5ee8dcf795cc8a13376c96fc4294d8647af67f590abc4a18955e5c71078d918556d56b9b07a00e435b9647021a9264bfa93092fd76857529772f87a307fb93efa2190e897f97880d259697c22e8cb8a5880f1408a299ccf3368854b709e9118c03384911690a5a0bfb49364b2401fd306eab29e0804e6b28e03852f408c9247325ef760ecbb6e2cf29f4cdd1112c7188ce37803cd9cde6e1ba3c26850a7c11ff91063972535830d78907bc5d8ee2f1ccd53179cab41870cf1959861d03c1a76b3b7106e8232f842327f7836e52bd59c45f260a81576f67cb875e5b1601822a2927fe05f67c48d029d01fec72ff9ea3709c7c117659d8b168d22abf0600133bcf37195ceff135d7503da655409949cff3f3b6c80e3c73fb533d618dd30c15cec98d5abade4d78b4761efc4b608f1db13d243d94431abc8988269d3a28047a4b33e8f7fb7cf3b038208d986743ba37eaaa41e3ad9c22c59998f2b23abe0f5f741dce25dfde2e8fb69a3f11d51efe4f3217586b42eff8e1a8969e473a6f09ed6090ebc709ad61783d512c80d51d08e1e701b26aa2e152a55020518ba7330653d5893d0b43c18bff7ad05fe3655c8895c5c9d3184d5ab221699b81fa875209b3cacc6685ac2aab99df5fa3f53fb439fd294a2599b2a12a8c4a6548bbba8801835671a3a21d39dfb6323d8af9cbb3c44bbc7559f9946e7f4103d7b0a9605a051e76976d328ab3dc2ce4a5a1350e80db20826b285f2dfae02f969c020b59a60771a85eeae68b12e65a7c1bb9647c34587145143fc3a5b505d140261f48de2f07910f8fb905b22379217907253ddb8632299b57aba1197c910b47cc4551b13c063df05e049efde3f7516f100f441003159cc23d0deb5b79b456d07a4a80652d08f05db319131f5ba0f54f9c083e445ade70fd3785ebf31b3e651af7aebf9f1e020e88d404416c567e1345dbe50ce213ae900719dfb0a4737999432a01fe8c9f0e12227668445d56215fbe2521780a07c2e33a16adaa6a77c13c74c5cfcc4a42005697d8aacec53ffb0b85ecac45fce1330bd8bc4899fa91cc88e1a1caa223b2031fac5120d7bc8aed9c1e59d52ae28239aca69d6cd52c3950715e27695db8980ab38afe5ab51a4252b157c3ffe7c6bd2b75b8c93aa80c2c733920b2ca6d52320248483f109d327f79d4e78d5b1d6952efe147979c5e73a15af1bc6789fbf4b80c9cc7c364013dbe522cc37f8e86df62dec8e4a12d80b5ea89cf8f45569a54c50e385651561acd3eddafa2db494d257b66bce2be2feeb5ef0cbd74aa0c550ca70f1d51e324c4987c8895ec75046756eb8cf7d59f2d8ed32a247edaf432ac7d5e03baeb9ddd615057e6b863d453b00c1a617bcd4d9340c196b496bf39506ff7b602586b6466a92ea148e4910f7a106c002277da66f3c2e2e88cd816bbecc6aab20274f248fc73f02d3dd2efcafdf2309c24132f231949ef3555049be4f08dbafb0eaed210c99cdd6648ebe093a93ad20cab93dadb8246b096f32eb2516f6a89ab091595cfdcb3f252108e1271d8f6ebbe0ea5277047a9c4f666599fa31a1e8f3104ebe636221d96653821f54b1d745a1e448cf61f6acf62bf22592f6523eb0c900e46b0547622d76d8481585cf909c4e04c2e2850f3915ca7946984a21b180cae0c1402d001350e4c08d67e239b8b3515dcb1bdda1e1df691f38e6132cb9e80fc0bf81dcc709c7e29b01b4a1ee43ad9f24c11cc804f26c133bfda04f1eafb16c60ce7f9ea345fbea54f7397466cdea5814fa843dd372d30ac49393b9cc74d3f3b0ecef4bd90217b8c231ad0462c29387b039529033130ba34279986de7e5489640354ba38d1bea2f146684424bce21e4006be1c1cbad51e889a94ff5c37992f7b03c0a898f1f2c0976b16359339b27e0ad286d3da23337c49d79d8305bea06ff40b56c250391317c0ad3dea1f3be23be5bac9ed6ade9e8c75df7659a3a00b1c1d0f7b013fb839d66ef102174096fec84c50af0362ad372e4423915d36f3d812e808a2467598e952e516a29e16e92f1cd6ec18e57523fc2d4b70a0552d1f46cfe70e403517d598eb3d1a20b87c03a909bbe9ff5f233c88c0124e7ad7a4d2ff7e4005590cb064f53e5f892504049716f98d846fdb1bcbef0c61f83732f7820f7f21048f6ae1a3a4450b897397569222ffc2893d4fa280d45d23e2027c8f6314b0080d733dd234e58e67b4a31b5324d7033672a19620eee270e85d6a0eae3b921f0d01582151e29a844d0cdf6c1da9023c4941b189ca2c049a69ceb8a868837cbf5c0cc90361e66c6c1b0ef7df5fdef097d5a447c0e471f145d29b43bfeaf66f0509049ce9619ec2ff8b7b457f6af9ee79a9cb07033bb6ca1da962c2d6b678f7065b051e0147e5ef60e9aaf51b8cee66580daf2c69c67a9744741fe6e96c19195b0e0c99d0f070a45dbd0af6bfa3bbd210e50d2382d858f9144104942272321bdc5a0ee42e5d9315ebe848c16ffd85dc464194bdfc0f09278aed72beac0e8ec1940505cfd3d874594f4ac636fbe6729014f239e43b471a3ae3012debfc944a8fc0780142daeac8f8cbab98058f94d97a5f000634c7d77d797c4fbb23eec8747b1d8e04cec159026526333304c9a869c5360bfa1532f4b2b658a269cb56da17da943204c43e3583fc9c6785b4fdce469c32ff433da659f91ecff57c9c207151bf36fe0a06de682d18630614b99c15c3ea1347c293b8f183f7ae3e009e88d2fda0109f0f54b80b9a2df089c1d0c6a5494e6044c5003fee21ef217c8d802e1b6310cf6601611d8ffed3ec3a29c786b5bce3bc8e4d3d975dc674fa1f17c15180f5148b6a0121082e9d9b30aeb18a4058c2ecefce3f3e9631d5e93099c9d47f87f374630d0b29526cc2a15c76fbbe774d26f0ed70b86e7ff67463e3204ec8a482ae4dbe2309cdd5efc4fd6284c76481b8e7365a4438b0d9463b8163411962a12ee301d9020d5b31d8d17a3252d70fd56159cbbe3cf5145c1e7e4659098ec58ad38fbfc1770173db35636560e3f7fb12b6785f62289c3f2aeeb81937823cdff2abd377c9540ed38c1e0983cafe1c81314f3ed8445aea70d354127b17fd6ca0598ea50808b907c0ee41e6bc76831017fcea66103f153666aaabffc496084a43417b0c3a84630f3b97ed8847d94f5b8053dc3c00acffa1544aad18b4fd56c78b3c9949990bf201 -generate_ring_signature 23427a37b63e16df40a94a569475e086585f88042e3c6933cf1c7c089b041a45 a680bcc8da17f610deaae851e49a1153a41cf2a0e7b4df452b0cb0e06fed4fc5 27 76ef12c44e822d190d8591a0606a6246e3e339c3c0d09a3e4ef9b9f3e537c783 dfd8e238c9d58b0b26de9d6d577cb9ee7fb353d5e624577d8a7089dc7ba75c2c 75d01aa0ad629f58acb073ef222e20cf3ed9cc497ca0c0faed5328da617201eb 039651d4977f68975edd0044b8c69aef742b79b553550bbb89d03996940f6a65 2e20014f619c3253a8ef76ada8eb6b6a90b48d80a0442afb5c49bc677ca30633 b6172170bbe84ae154fc5815d4736578dd9d30e1e0436501dd36b09da27bfa7e 8cb4fd174df4dc7db427d121a19bf938b4e8aaad7830e9e638f653ef0011ab54 049bb382150f20b7205ee71165393d59fd4cc0d1a690a088387f8039867f5b08 a3ea8ea364079b4a02debc3fdf1b6d7a3791981efb1688a876b97008d3aa27a9 ffa5fa37dceb7570f33e6c757291119270ac62a9caf79c61a1f73347996b42cd f3d0898f92b979728369d0d9abe73aa690292800f3e4efb66de9544e95fd11b1 29291a457166cc60ec27368846b57c40876d1f573ec9af3f0adf44e478c1d561 195ea8899ba4e879cbbdcb3946d8158b0db04d9f147a07342e73089a7aaf5c53 0cfc96a760e8bba3e0a54bd8f64868fb7230f025603bc1d7517d5238f309223e 0beb14a39881918f54137e99f016277f66d022faf43dd0a4312de3611ef62167 87e660091dfe3e75636c7dd731a7aceef1aa6509a4ed0a5d9dd70b5ff02fbba5 4e1dac7838a601b8bf319f75d333a2488feb652d3f2c1610fb24bb66b5dbd727 6e2959ddd5b086f020527fb71362b3d902b030a27a873654c22794471312ee44 2c75319035311b2f58593503dc6f6f0f5c9d6a5b414586818f48452f5e7846e5 f204e336a2710b43975568558f6845fc099637b05cc8921e0557ef5121672e06 83210f8e7869415f49bb722f02ecca7b1a39aa5cb90ee5f62015c26b5aa7e23a 3ccbee824b4d6ba42ed29e238af0d36fb4213986ce70582e2b52e8e519b23c1c e42edb639429562afb90c78af73c0966e4a38f892a13e29c778af7febd30267f 1bb904406811cbe96212e0b34bea451f8fc119a7559e7bfe57eca21b43eb55c1 8414918482c2104b258f9095e099492c5da1aab97173d21dac8c083206ab739d 65bc67958d05c81c80d48855041e321e1e74aec3006c2cbdf0d9c6adc0549271 12fd3cd5de5c0cea07d92c01871687c5dabee74bd383fe58a0721e5c0658127b e3bdd2c25f7b73bd7570bb5c6d3dc6469dac206a4483a6d8dc173bd2b23a6f05 2 3f529b5044beb9d23a55096ed9d7865435c1024abd2ca87dc213e1dc7807af01193f09b542b30aa3f4edf5bcca516f05c8ae00943a413bf8c2c5845a9dd5a70d16576f2c229c8e8d8a7a38e317b72adc83c1d53bb9c39af06addcf33b297ff02756b9e69cdebaf6a035d79a9f06370aa8c86d549109c642b418245f097e20b04a8b61b81fe20f7449bf4b10832219df14ec26a958c0171e67ca6613ed9bfa70eeb807c0910459e5d971dcb852004fc62609b47833922fcc411bc46bedea61804ee4ae28ac9c73f16e24e95d3cc2ad92e79fe02be99a7b66ac7c19fa8f4873000e2591eda2565967f65f23cfc082d5c26a1ae09927aea39467ed67e53200bef0318792d9432d39360f2b267422a9dd6f2bb38a971d905ecd0c548647a532ffe0c147a0cbeb3b7109b3df7fb487636e58826dfea0b4f100cea7b8a7b797eb6e204800445d108e4b2cb3d750294989de215a9f801db9e13141bdb7066178de30b00f54c66e912f66fe472a06c106f1d9d581edb74b02d51a780fcf4ffed6daae0055d3944393edca64e8ea3afe95b640957398153348bf55c94c03ab1c6105f950a6028cf6d69ebd747f4ad241ad908d54eb83544276a0a8a3143803b89bd2cc80a00959bda8845088916105ba8dd1b7627601d43533bb3890bf85e41463055230829a5b5a1245a0b1e944adedc436e254be2366ec5e76d2d4b9318a86b5f7f490da0bc882d1d13477d11d17c91f7b63cebb1fb10bc92967014bd16c8a5965e5f08dc5f2369ce95b34d6c8d2d2d25b2f7f75b03c41fb002307a906d29610931ac03c4b800978d38de16cc23ab92d4caa800b2add89cb119d0a7194d50d1ef5287003c4fccbdcdeb8176dbbc1e964e8d6bf9a9be4401c1cf0072c2de2e9515d6f700a78d470677986c418e43dfc6f0f08245d3b8c301a28044c418625fb06ca4bd0472bd0fd4b01137217866b2f4d5273444ffa94b1c43e156add2dc3bc22546f903cd80d6a9e05548c4672e70a93a1a797c6015358efeebb6807d283ff856204705f428a3a06a011e07c8daeccd0fd63373b437611e457094f7b2d13172374ac70a8161f389fbb6421bea26b092a4bf8a3b9b888eb47ba82ef780358efac8503307a7834cc6e376a78c35ab7133f1e482b4c4d4effae5facf4a9838d89c072d7e00f4b7effda8e257ac2eeb46ea82dc6396d52ed9607504ff6b852432f318a83e0bcde26e122971c7dd9d92a1690ce70b5aa7b947645a49dd3262bd2c1d988c3f092f61bf555795d4dbb539ef593b3c2f5ccae19087a9f0cad6a47d70fbed6d5a0fec7f88d6652c529835167095c69be53ad2224e3b28369bbe3ab42045398fa70738b9f0756d9b0beede437616e33d65b9cc09f306d6983737d5e62491e0a1090ac852e6c83f13dca5d4a9e08c76d82f774760352ada589ee0a9bbcb756ee71701a02dc3df690218e664ebfeec6b782fd06376edc57d10513676b4054cfbd08801135356067624bb2030cd2f644616e5526bef25df44b8e3b573b0c2603b514d063e7d5fb0170fae65aa37be832ed397206f333e76094a44e7be4ae4f14a175a02163fbb4363f672b2e47628d86355363660a02c02a0d1f7ffcd496126451c0d017cafd06bdda34dc028d834d77c6e7ea508f1720b360c86fa296f37330548920b48482c0594a8cc8f0db2d250d1d1a95c11f27ab5a61099b7a949490db45fd90f677bc646723f405f29076ddc98c3a8f40e1c82f85d3ca5565de539baff9cd50f0c9c0cc6c764dc1657defcfb6ff11b86e483ab4a46507f08af763dd40fb4bf0aea5ca36c9750a32814f5d3df4a27e6b14e8155675e593785f25efb2d4db44b0e71a2f888b1c0d2808db436ed38b9d8d564e8bcfa2c7274dba60a02f495c9c101294d74260d8b51569d7ec2c49903e8c6904078b324dded75463a1e285a95510c56d30bfafad9b6a184ef93147cece263b21542f048415f8867a580d8a02b1300b9c1fe225cf0ca30b89d0c03d118e46f91f513356976a89df306a988aa96cc055d1a221eaf3a00fe25989d14dc40ebdf6cfa48bc50fe8c676a9380147d7ea70387287c180bddb6ced712597189b093966ed73589fd9de75c8872069deeedf000e7e68164c5d5dc4ef5c8e052e86617e198c9d90eb775d7a91978a035c2873c0c1a5f94708028ff566de96fe461dc7ed5e537eda47e8365ae8245a6a2bc4fcc08492f2dc757db6b2dd42bb80550371202d9a293d9ea1d966efadc35d2d4f7370e33c9d6685c207a5e11cba02bfd5b45970d76fdaed73ef87e7e52575e17693f09d0fa58d03cb64798a267b6ca4fe130c967674224a2ef5af6db8e2fbb8cb0470c2e7e8d73ecdbbcb32f187272690e238b48c5607639fd05f0f44e2a7f59adab0e9ed38ad638b8cd5161a7acae3e9c4876d8b00750664a663d1df480538e1d160f -generate_ring_signature 4a79e4858cf84189b1a787a39042ea6be11532c2bc4c1e87937546cf467d05d1 e6e7e9b18970c931eda06ce590197f03131062d3d1b5a6ad71ca1e5abcd18e3d 12 2fb5a4364780ab0fac68569071652025fd8b48e74aa242c27adef75f375d94e4 3c46616f02f3b6d9fcc70f74f9319547b6114a4b2af80da69a665b9edaf6a4d9 1c4d1f6b0f21a6b2a975e2a76e7c4c735bcad9430ae693d69b99db3623944c1c 2be94122483ecf69df0dd4920519ea1ce71af7036f03e27466ba7272421deaac 8eb99f028055ca92189f78726acd9b93ab452227084fe718ac4dab9e8f4389dc 226f4f356c64efa694b8311ad07d4ed444d940d98a54920d2d1f9fa687cf20a8 4bbfe2a4a7b779fa5336295fff9be27b165d9a01920c3fc3ff28d4861997c3a9 3c50a93bcc8b0ac7cb7904cb370d850c81750e2de67ac9d4a769f551bd28727c fa5880974ca82b96b3b64d419674cc7efba182815d23f8424e7e354cc3e63dc4 710410134c629f3565f3706878f34c8b4b049625d520c4efe0b8d0ff9ee02fe7 b0d02c0f6de3ec3774101beb260500d636a2a48c5be06472d17723377f82e0c9 7fc89baf0a1f51aec3e249259b8f22194c406956d27410ce5ffebca081fdca89 1d8156cb422b95597ac3b4ae844934b8e729b09dc95d288c4826fb2b3b8cc90f 4 613856b2ca411b309d246c66c23a061fdd5a61694a4d7e3151c5289c77d51b0a49dd2b84dca274f08d870e857b375837b6c00e269d73c73e20393bc45fb5fa058edeeaf8585e261c854675d89852599914862f451867ea48852c5bf87757b40b955eb8a953b2a9180795f69485e0eedc9ef69d534d7aee0d38878f69be585404defa08da1eb84e80ceb58623ac4ca7a6de61a786080162ae2a8e134c942b5b0f3a79729276cd16a02aa859369b5cf79911d123067b456247ae1c1cbe06bb990424a406104bf2557d86f6238dae70a5b6096fd81581bb7727df228adbfe885605b2f03ed1ff9b3847c77ba6b73eb273751c0e01975d52b04d3b5f05d965cd410df83232ccbc1db1b5b318d533ce38084c8ddf4de67cd594a9e3ea0ddf997979063839c830af8632ad08e652b180e15cdcaa5e115f12337a3e00802ac8bb12720f4eff8ee200f61047ca8575dfa779f6facc4f74ad75d1efaf171f9c004afb460740ebb29de09dceacbce81b590061e213e62d2dfe4761bf97ed9d7ccc2391d803ccc58e5aa54cb8641209ed899b1913d9b692142d7f45632915000ce507874d0b3bf38e7e7034c8e60975fb6bcfa2e0882c8681e62859acdd5c339dd27f664003b33d730635b7a262e5c97dabdaf00753925ee57920f83582e93fefd1d5073205e610d05cf7cb3c32af73feaece4a84596feafe08c714d8c24bd7bc1e436d710492790bb7320a56df42f165e47891fbd10a804a9278266c260e1c348cfa5747098c0f37d780402e08638c7fd2a0e0cced7ab579cf7f935d91db49dcd20499ef0a61e9962b7c454e9e8308e9a4ebc0187eb60b00a2fd007ff4ae11c53c29804c013148a67e294e754b2765bc8611df29d95c3fb468e6844f724136dc47bb082d0a453e6a8ee62bcfee6d9602766ffa50497cc4c278e7207d3ed115a132a4999d00139b6725e139131f4bb276f823bb823dfe6be7902caa8f2811fd136362589b00ae178b3160c8256cc7c74100a5c9bd14cd7081862c85cf68f4ad664b6183200516a6f1a8df7d004710a22e2f28816d0ec76a7f8e76639f702690a3bd87653408 -generate_ring_signature b3a73b35af9a583cec3e5ae026c0974460a27f514a1e57863ab5b18303b49bfd e40c3300a1834002a79553eedb269f35f9d7041cb646e3e6db9fca5f153dda0d 10 ebcb749e504392105e194230671d48c40a2122d199dc6ae4e0ba73efba82bdad 7859f6fd20abfa37a3c77079adacf378ca5e510c878c37c588a9646e93fe8388 427f5fbb4eab74be396e86079a6f7efa6cf50d058e9f050686421abf936cb4c1 0d6be5c57e41a58ef7f994a0face6c73c93aede764b54cb3666453d57c2ff4ca 7d0a29ac592eccd5310a9f6ef5cc6ebcfd854b437325c38254981fa83a35e333 67d601df6e64415b9113817e05b525ecde13d8f59020680a700768f9df9ce0ab ca677493bf0e87103de142670672bab6afe534f83fbf59bb7efbead953f2ae79 0146d12aa7dbdf94c4c6c9fba9ed250f5edb2042dc0a6b6d143155f2f5352452 2dbac320da8dc26a2e4483bcd01eae0ad7748c28e4bb6793f182f976f80108ed 87db7f074e179a7998b44b864409980beee78a7ea3befcc7fd1fa697735562bf 35b98eef6f934ddf49deebc1786ae2a7c69d7c061a108ea63be86644aa65510c 1 c134abdd131a057ffeb9bd9138f65ef2aac6f1df3245841c3e253c7744d7ec05e81d092d0aad6d215c4495729db9db88cb41dc8a2ebe480ae5d102a97b90a10ddc7d0e029c4cb4b48960afff2aece493752a03307cda81cd79ae059bd3b2e409b92d68a4142de43b55605df424f59f29c8698e0b8a72f90535a904a163085a038cd6bdcc824ca94bf647b0a37d801bd9bdd35cca242f5a6dcbf4d7df3024d30756cb694ec36c8bdc928e634a6a8ce4121debeb7c97db6dd7641b5d404e5c9e0a0300d423011fcf8367eca32333eca36931efe6f15cb8cb6ce9e2c352189cd50c6b2a1474c267f5f7abf4f49e2284d03c019006a9615bbe11949743eb64bf710f0ac80c7219ed024a9297c882be1087ff02ffbac14b4b2da87ec9ce583ada0e0018a1db64a022e9669b537a4b30ccdcbab69705dc7ae85cbe81009279796fa901cc4b3fc14b24ce61c8c8e7e0921f7ae468611a0dbcaa5c68cbd30a2df1e39b0ce8594d2a8fc7ba0ed48c09550682c648cbe6ca8302a2d16617a6a9bb30bcc201b123a72aef2c024cb24a8ed16a0d839b801b0432c21d6ae0db155f045ad812099355d3a3afe5002a6098010220f31343a446361aadb2788e1b7b49aff354fa0ccede8d1e5d53e011df1242eec4d74c03c5f5eb4685822cbe6a497ba52da04000e9f88ed91565f4fc65b3fbfd8351f82fe4d960e22f71942755cf2d3641271407fa5921ffd803e00d3068bc1fc0941eaaa6685f08552bcd83b8482ba63379ac07087893e4888f32d8f1f618aee2e3c35ea84b5940e5ca3e580d12793981955b0f9975ee6006450b5681c94afbf08be38f518c296f4df53b70e338fb358c1a7302590162dac7eeb32c4eacc6d6c4f4f7749d9c9687c020f8a9ae13a84b77805003 -generate_ring_signature ba2cf253cfce5ac728cc5bdfa9e02621cac271d7d640ef00c7bad3612f7ea07f ebfa9411cd1911793cd8c3c3751fefb1a4f26ca477020409303e36d624ba31f3 5 a8dd3253a8e5d1aed79cd6b19d6f8f006a6f755c8da0b20e2c9187fe203f6a18 f7cadf8be983339ac7aa073402ff1d5152c7db7a2b1ab927aff355d4ccb97538 beb5217ff6e8217cfa07a89e132171eaae685ffc7155e1349bc2ccb6289970da 6f7165dcfd5f3310abed2c531905a5c69b118b2d708ade5721f0a452248e458d 04201b3245cbf0b7225718ecf5aa71db813eb5ea54b21423f93fc9ba202399f0 d5d5570a52159caf96724aafa56bb402e9340bc605d665b4fb283c87f3404306 4 89af7f6a3e191273e7722963de426fc974226463a5e34af4d73a64a258d5490f929ff430f05f9e01197cdb143fb85ed5461fcb8be21eec1872f5bde0fd192c0cddd2c4c12711f5b3968e72e5cd309a22f492eeb13dd8db744bd764a9cf6d10027691aba258214eea546dc949e9c372c30b17ad10a9a56b755915036d09d8e2021f4cba99574d24cd898c297362ed91389cc5b148965934330720809b50cb010c022f36cb9eff3868f43acd5b3af37945c5bc51c61a95c0532fb2cbd59bbe310da48d59c57f620c43eb9f7e2d9e278903978813c3f5b6582eeb7d7790d0a81602b8e30b363626d50390b9afcec243a348535a3515ee78a9d3b81720c93fb75c0a0c26f6d88baf87b10899cb147691d159f731ff3aa72d2d5e7f648231b8caa90668e5a5dfadbf6ca81168d2d5e81fc3f632da23988fa3463feaff17f588c4db0e -generate_ring_signature 07d546720ad955c6ac191f0ea16e5f7d843651501ec655bb7352f0cbfc540cb7 9c58ecdc8162a18d21f31152b55b98c21529b1b918752e05d6440062b320e36b 34 c5de097b79f066a3bae592e76e4f681f9f9cbddf2d1bb9819d8988f12e657b28 3f2e00f51eaf8c766ea0a750a72aa798c3323c630bc6bbf66796d9eb1dcd7814 0108036469ee5180b39ab609a7cb4a4ba3f4c6ee1399f5a5475cd7abf56f6562 c607fb7f081e02725de17473c3358cc3863eebc8306403fc097d8a8cdf56bc5a 358cc0d47937f554bdfd5cad829d6d4c04bd7b4bdeb8138387409f51f5bbd233 c585912932e94cedab2246ef0f35ba86de9ac8816c72f39fac1b74ec849513fa b967097a873c451309dcc10c8a7716802451bcb666e7aef43c23c4886e958f59 edf0437f0789c2f1280f946e027b9e9d3cd7a7450b77c0c29cc2d1a3a4048e59 6e96fcc4fc42ec8113e21380f6e34dbe8a1fc39c8ec605413a6e4ebcef95a5ee 3540744c779e95429aedc594ca3e7f144fb36f2354c82a215ee3921eee58a687 eb13aa3f8418b863153d0ffa0a185060ea881d0bf26ec385511efd7d5af9a9f2 7f6f7a45c10655b51e31b6d8a8d718eb55be8263be321b959bd31d8175067833 403cf1c69bd4293df454439957b0b82b4fbc10de841225dff9bfe9f09d0b1e00 8e79d62b1b537f5eb4f56e876c25c5419aaffe859b9a281792009937d39e3a18 2c69ecd10e42299b2775570761c7f256084b0ae90d49e77a84f1f728e1ed9eff a15aa70042d5321cb041d4bd2204a7b3cda423ecbadc33aa57c4bbdf22e9464c 8a01554c542f2df7d9ca32dbad3ee49473c064baa299d3b659f30fc0cbf19b9d f2a1a19157f813c81ba6633b903d7c63ef3aafdbd1001206e1b6b1334260eeef 577dc9dae2487db1bb743f56d1a016e8352d2eef75b8439f7a0b06d2484e6329 c405f1ded4cad4af0084231859177e971c5525f1bb1854113ad7c5bc62e21dda db561e3f680279ff441e82c2a7251ac1e763ca0624ea03eb64fa2a6ac72b7b93 a38531b78756f9ba162cff58ca174efc1d776e9c3375d43a366c5da19e854a6a b12e648ca8bec1098b935a509295024ca47bb4f2f0f458cc371afd286ded5bfb e0e9d1e673bbfb6785f45e855cacbfc081725ee4f9572b152976c70166ff6f92 be23ab7ffbe883d989c97c708ce49e6b7847a4b42a78d7a2a122de8e7ed05aa0 d5a6a40407c2aff52cb6d80fe6fcee2cb265517744e655dfcaa45e9b5b159e3f 90e4110de219aa004caa3aed8b67108ff8888aa4857c54ce6d7191e8b11365cb 9dff22c48301e57a059f72497b5b04695bc6f69ed27d12650b8b5e48c9f8f8ad d056b92a46fb72170ff6d95012156b3c75abc6b19a70969ec132f93e628ccc4f c6bfa573278be6a28702532d85b56cf3ecac307a53474f1599bf35ce0c39f692 cbd889739f84e045de92495e182e47d1b87f2979de5eda0e24eb15078756ef4a b56c32d9e333345df49bcf546e4b7555e5820429035314341146e2eb555b736e 1fabeea644179badca2d5a7660fe89f61ef1e1975046490dcc8ab54a35b2ea12 8576859fe085697bb50b74c16b8f8d75e00ec1f6bb6207ce506df6b1bf8dd8ef 85c20f8768a15b7819a3885ecf1c285cfb1b5609a93007fde3e1964d014f5405 13 4c2af572af2615bccfe88571dcd9aa4df64b6218f2c083889407513625761300e8bcbef06be6bc034510979835818ba827e9336dd2670a5be31c6031a05c1005a45de60e7811890278f6c7c744f8ba59cc98b24b3b4ab69b0d8c9801780a9003623259e3fcb8a0df87be2d5035081cf934a97982927a2489b6283969f24ce8080f3b07076f61cf859b2dc96d76fd39e85b16f891059100ae9072be37d7a9c404d1eead8761e72e927a85728606da45c876ae03fa2d0ee79eecbe70d8450a41089b735448461c9be0f6b5a1b7b156e718f7520a06720b4ae337d6f6d4bf4d18097c9517da14d2a782374d94626fb388a2a8aa13969d0a59e067704617b010b503e6a008855d9e5c54700ca8852cf2f424ebb0f795466afe212f9c7e11fed252010e4c983c216964c2e08ca015b4d0f61c338f6663450576cb583ee542a1b1830d938a8febab8d207d6d680caa72226cc0a6d23f29fe67b61db2602da3fdca4b096af3a7693aa5bf1b7064ffa0fd762a670d79f577cc9eb551625df85f647434039afcd243d85920e39f59197fae25ae38f1655e8111e4b3204ee96c9af96db80cf2e7f470dbc808bf6c3b7138a2d902ab02331cb8696f5cdf42df15f001a33c09068f93d56df4a57f5a35ca0aaeacf4e5044f07799a47aba5741c81982142b60d33d6afe1e8cc4ab0500b98a4abc3d26f70fda407df8d606918dafdb089dc80085e37e658ba320d15e9692f022aaf7b6e2e282dc6d192cb409c5d1085099b130cfe71f476300be43345f81005dc3d7b651f6080253835837c428a9e702d63d805e64c67d5ace65cbfb6255f8ea6f7f19fbb3644a3dd92a3b7e6fb6ab3c78156027b471ad6a218ca5fce83c3ccc671f7b2518a9dfa230cee43489b35fac8ba7108a2363460caaa18bf63c7a1a8acc6e2773598a308ec4a349546580ac1ee363205c5473eb275878ec49f2cd30064ab14e7aaf69e7bb690fb11c2e17327c04edf0236090edbf9506aaadc3c793c97f36b8d1125ff24b872371e4586d75b1276cc02abb2cdc21e1dc603f9506f501d369875eb982ac684769b90a42eea5797230d0818de88160ba2415c5bdc5da9b15fe698dcd0358fac44a62fa3a6bed7c3dc9001cf122a1eb07c9842d4edce098ab99c8c1511070dcce0c3f0aabc854876d7100a42bdf0a278523c784a26f5e800c4425ced7fe7d04e16409271bd42ea8819200540735e9eee26e081aca217be30f893c46ab7063c8687de439b713551327297096cc51c2d26938e32ed3e7fe2f6dec46e76401f76548a541612cab694c6dc510d655e36951521f74488f73f8c748b97225642a6eee956e6217fb561e9f20bf606778a2ea44f9365ceac5d1a2df14ddb71f455fd52233817a942db0c9aaab9aa0e563490341af8381e7c74a6d7c5824b3d2d1975fea8231aafcba1a2ef5265eb0636e6cb6fd842042e496355b13dc4fcf1ff6706b18c63b30c374e2c5bb3110c059c5325f9cc53074351b3805ee1135d015d8f1e9e272c7b9cd663815b27ccf200c66f3db1b7f037ef149082e1157df89338b987456e8c31459e6a62967c58600c886400b24e5ed151eb1d02da908381b569963c1c30b3e24f99a19bc8482d300481d17ac2310bb527bfaa80f502b317d63da6b0b5d12cbc28d690f1a239af20049cbaf8276a36ddf2dc5713b86cb89fd80c24797a3442b495f6238389f466c30250179e19a7a30a5557c6c34e3d4be18eb7be5774cf0dcf528970676caf8f3c0b8dc2e8b2b459d978366af8da0206126142271ca3065e591b7953808ba039360c981dfeea37cc01a88326c80cb579ea64de423b3bd9f37fe60e5be036f76b2a0c63207787a8585ac866756d347e52fb7dc329cc96ed47c5756815d0ee698571048890df269e9eecb7923d93f62e8ab3d019542ce074587ad5f8f5055aeee5780407fd50320de0c3f43f3c4efcb2ce606c14d68e95854d241a5b275181a2ce760bf904803119422dd9d1a39a4fc8770498ebb652338e23cb93ce7889d9809a0f0d0f78b08f8dc593bd107a1d7e185932f9640f66814fd0948b8719444c9a317f090778b29287f2b1a9787191f90d46590e28ebd40d83db74de4c08ff6a1f437006d1514db193ff0124393df6191b56463a083a7afdc3726c0cb3f5382ca4bf2000367500d770f5e6e9805ddbdcf58745905463230fb8d6020a6c169a34f5609a0cebf78f364216dbe1a85144a5bc0e08ab21e22dfc88c78eada90c27b97e8d1e0847fa7c2be13be1e41caf3e2ac357f3a5abe6747c724a79411356226f22c88e058dcad1523dcac5399446fb24387cb5d1480a8652c5ce87ccc10cc4dd0c412c05da55f3ff8b591188eb5c3c6364b75ac7752ba1c4df5359e7a7695eedd50e80052d65574b6ce59b0e07d30255a1119cfc5e43d5f0e97eb92a9a2253e391d9a105e42c41debca573a6e0421037d451bf13473b80a06d2be6212650d0c244db59071542e9477a14cd35ab47744ea265f45d9d103e82c2828baa471ed9376a4bf10d5745a1caa0f634f3326552f56edfb4b44c6a4317642b6c1ab39cc19f7012f705ebd6f5afe9fa18c64d7a5b5748a5c39429fa9c53c310d74643cbc516f237b203fa66ac08c73c2b8c8a0c4ae8792c73e2ed2850af287268abe8080da8da91b0028c598da48d0e8544eb3449d1de52b7b83dd6a6f86acfd42bbc99d680a2569d06412e0de04d11509efe404aee94d466df845258599c70e6cd2c5a621c2404c00efac5594132ac56b4fd04f35e2b0c0178c571de1059dd4bf5642c7bf0416c820b9f5a2eba303de9e8c2c2c6e894155640713c80be86310441812cfe262b389009fdb8bd46e9fefdc1dbe2f6560b56f29cd706879ed5ae794bdc79ecaf57a7450a9c15235e55cc262c02b2edcc2ebf795ec9a37f039741ec3435ec5e9ca49fe00a8facb37d6d69565ff7bb35aaa7a1933b988f6e5fcb3e44f73df4d6a31e3d650c1e999bf28cd34556770d0086f9897c9f07e755ee7eb73f5a4d18a6114f651b0ed07afd371cea2d8d831cfc62228a38bd1762aec43bc018fff038449126e81f07 -generate_ring_signature 0b1546a77f70112073985f8a806f191e83e9d3cb9f19e41f15a07671a7d7d173 0ac40cb07f49b58cc8d7c8f9eda97937c5cfb83fdaa0e897e4aa8507cf55f2dc 56 fb504b3a12b0e8237788b91816e395f566762e9d85e276592b9f42eb46b932b2 2de49b9fed2a1379e27ff9df1387c5704728bd81a61381ce8da4d0190e310cba 4b42cb7ed8b175b0d82c6f6577b3c2ab65bf0e0cbec21bf00a6d0a532d508383 6996b0dc98d6d30c76d3424d16591495301a6981e8a4afe7b0b7b0f1682c0c8e 8c3d881ee3a14b52541c96ea7ba2200a17a9ae2123c15774efff022603808f16 c7bc7b4e978d13bc12a50ce744db1c964ea624d53d57ed21aedd761fa9e5dc56 84230301ac02a11ef46e5e7dd0e2e73faf19b28e3bb579374a805a3a07feb800 0f5062ed757ea9f3a1d9680003733cd69ac3077de75e715da4c72774fddbcb78 da0d99811f920053e451849dcb82c6f9c5f6f2e1e0435a52265dfa695289bacd aaaf445a2d1bcf716480c3f552efb0335d76633b4ab6b71ee6bd9460fc24a1ec 1f7884f7dbce7ede89ebec68fbd6765b2cc92ffa7f8c819b1a98c86c0166f142 5e1592fd36141e29de682b90ffd13d68aea02e67c8c75e872ba66690187fa6a4 78f92af4fc0e7fe8c117f1dec9d225cc1a690abe427f626466ad5768a1578932 5f34d7ff989f08ff0839d1df276a0905c9ddee265d652e2be032f59b86686d95 7525ee1aba8272b610df2377c0c275202fe06cc0589b9213e70a127a849e198b 43170ab7baf0ffdd0fe6bd391dbdb7e2dcd1b89707bb00dca9bf12e5025b0fda 8b84df55edbb4cef851c9674e435639a0b147456333b55f2ef083e572313e347 fe394491438a5a7860e525035b70555caced54a2ee0d0d7b23a85abb6b8c6a49 682a71ed5b369424431725db66925de5175d081d65f6a4fe049a5748836737fe 45ad3881f548cedcef87954b2c02363e0a4571bba5c5462d16bf5ae4f2dae423 8c3947dd8a8959059543c20250f6e83bbe841ea641759cf6f6cf0da204dc3fc4 11f235ed2bf9246af8ba9fc067374693fd3150529e05b138dd6b2942b7b8ef0c 57a1abfacc42c7ec4f8933bd93b59fe01f0e49690621379993bbb03ac100f036 0ed848133ef3457e15f0977f10a76d7e535b26eebf9dbdf97264a964214a05d2 fe282180e425a1bd0a8b7479be259067de45fc6e89243f0d22c7565148acd310 81857e68f244e6d5bbb6cde0c7ca8e5b8662870472f78cda46ee11f21ed85b48 545ec00ff569c1ed27563608e09719ec282f47e8517ed95bd1398ccc0f7f0404 015730048da5e50a112a705cd3110fa035634991efda22bcedc9ca6eb6940979 0e3c5354e7e30d619cf5f89a31817892507e64ef94dd9ddc36296da893c60389 9f16a094d0358ef39bbfae78c967677fcb3b701245f66de2faac17fbd9a50b3e 3ea3265959f6857f1b3de569fe573268e3ccd4f4fbb3fafe0b63524c13df0375 1b510080c98faffca1796b5937611e55d2d91ce491789a65df068ba7a06e2fdf 2d1f63f2b1f54755b4a3d328552f79708a3fa6db59dd970eb068845d8536c432 d9f5b0a6f2f92e8be14dd2ce300333d20579d3fb6f8719ef8a8d5ba5a3e72cfa e4d7b759782849314e443316bf297f0ea63370eacb54839ad9a377a54f72be32 be3341838a0f2bc12009e688ca22178361db99df8060bb520740138ddf5645f8 11d8b6d32bc7a35768648d631e4d320ea8b56e932f1a2cbff0d40095fc29f80a af23e51d53106147d060673d7fb4f2a47bb2ab72b2cc255797fcd818f5135220 3ae4c97b960f65673fd5e01d2c92b3220f6b285b748a546027a8b1bf59e8ac6e 76d0434adcad2173c4259116139ad0fde9b2ae309c1c6e332571794cb588d152 08d4b2c32fca4c8879102396427058c720babe1e39b90279bf202931a0f621ae e8e3cc79d0e05cfe951707e1c903524084e4aac4c30f9179c0be50b7beefd7ee bc9c3ed0a2c25b1e59d70d738dd9da4ae3fbf7d535fe58846d523faf47bc42a5 127d126506b2c410e2c8ac9ce472b1d261932e3fe361dd90880864d003812be3 c894d10d2ee5c17280e3e53c026084229c4e9e875cff20d608a1ef71e4573320 02b412846cfa7350e024294beb8fdc1e2587557e8a7b4452d4e9e9eed14c9702 c5fea97e5de8cdfe09a77f6f78a302e9db64df2137117a848c46c79079b6cdcf d7d0505214530ea4cd905270f664d9ffa08585a7ad28123156f52bed892f5055 fc78f270e04a04f38ecc4b754ed926fab86d7fd5cdb959e91b9ad15ab5677b6c 6fa027b87aca6842606c8f8bc4159221eb056c00d56553ff60884ade17445fae bf5e94eef26a03762c215cee6dd95140ef2ec454c2faa05d2964095992a47705 29440b94f3701df4472ac2647af0de415c2fd191c23dbb8b8f096d3f7ff490ff 6d6ef06df5e113380e27e8ea590b8548c81a715de4b54dc9a5d35d1dc45dfee5 c8ead1f8291072758ec6bf2c68a6d31e5f18a9906b3f22b9de6541f4d09f4068 179c75dd86d37ae7ad1ba856d31f36d6a0089ee1a363c540f0676e8dc727be9d ff186cd81fc7dffae43c919c60cc0da74e722d269741c3694c8ea925ead329e5 901ab283171d866625e3b45fd7fe13b6b829a44224a37795a61683e6addd1704 25 ea7f6a21a379e80b5d22a00bff43605a5c641ec986b955d75cd6983a03e5a40a15ce6134535179a72be5ba5c83502a052abf50cd6da4aacfa25ee7ba1361930821c2a9224299c1c3268131866e4f9f304bd406276e04a369818dd4924445ac085b5c6fa5174fc4b659b57102548835432cc67cc9c182b853b0c235c4ff93cb07bbde23d68de1117a7983807ab4362bee70f01eb91d90ca5f788e196df69c32041d2e0a8e37006bea18932b84ca68a744ebdb6aa6c077ef32d5f38b8aa91b5203280d0dc71dea4cd0ce59dc3df4239706a24ec314f08c64dc3a9ad70d4102a90b0a0445306210dc1a7e2e13b8acfc87a5783810e8e1114c3653e57a8c6fd92a0ff00861a19b6c95e8488e5b941c734c04b3ee61a6c8d8bd96b256989eb9aab506c0cc8f425c9b4b6b50c28497d7e451974643148f79eea014ed832e359729820649a6b0ea41d60015b39185d4e73839963cf7c928787f045a393ef4520d423a0268d6ee70e252b2dbf7621662a0ff8a4b50f65972f3401145103f3ef39020c109445ffe882b116927ec96214d4eaed46118840473f03e657fac317417168da50ab497f018501f319826c069349a509da85e91a14af7ceb39bbb03e9076bea4104a25c119e34b58e1f06bfbe89137e74337dd379be370fbd71502a44e28abf9704b174c466b962ea73d91faaf09a520ace560918d3db0ae4f596817195b552f405cf375ab3cdb82edce5cdabfc4f718a0e33fab7d735616c6f83ffa4271cad66019d33be509eba4363766bf685a64938b21666d786f36ae8303b8cfe82c3296a044e9a33293d814e7cfe7f324d10bbd55e4b3a4f8e7ac5f3b42e019e485f3eb9073694a88531baafe2aa47621519a2396a48b5d38b6695f5a99eacc3a627e66205fac696ddfee0f963f1435f29f0c59d8099db7b25e61c44d5aeb3f6751b675c0ad4a0e77ed524a5e8d30c5131aedaae113bbfc2d9e10c777f46708af2ebc9eb0cfa26efb6834499ea1ace959b09d00a20d99b928b607e9b38c748e1a1e3bbed0f78beb707e00602e7e2a78fdff44d2785f665864c752e9baa744b0b717d4f3908cc2a1182faf69702d5d0eb2a3ac119ec6fd00ad6bcaefd2c1a975b00fb5004068afc023901f8a968248deb99cec8a1d00d457eb5232f7cc58d4432da1c6c930a4def75a5b6850c1eb02b133a6c74dc07d81995c45120ef6cb09cc4842fdbaf0d4043e912a7e9c6d8d8aaa44358efdb8f5ef0d440599b02271cf8259d518e490a178aa3fea33f6487f8bf19b8754d28d9188c104a0b537d546ecd2970a9a70805ebf353a68c761acaa7a7382abd45675d8ee1e3ea2ef18410cb690a94067ba804a2f752adef8c7887bb10128755eda3ee220fbeb54f46dd17c0b0a4d22067d7024393fc9af0ae196f660d9bb91b2235b88606a8d2663d5c2393f785de1dec5204d6dfd45c9bf67a3bb8f768caaa66755103e7a14ea32ca2ad47449e01ba98c6030834a5ea66ac038aa6f97f1af2978c9c6e167d99ceecf6d03960eb16040b910eb83e84062eefbf3983d775eddad5fc7cd18d60cc146c7439c86f186d950b060e83fa8f838cc0f37cdc964e364bfc78b73492fbc6e599033a745558a27ffd2b061bb9dca3fd448bc00e37df84b03548bbb774c0b4fbf877a3c3b7cec2c001380401a14d4bb61696da5d5e4e6f5557cf6f0049ef4af152477d630226da11dd0101f736e1a7507fe045e5cf71b4ab749da4b9a88982716441f33f2a1b2ad286640a2acfa1e05244be97251e9e01da118edd4258a1d57e35b06ec83a02b7293e4a0c6d3a8e7b1191a5786ad83c068a3996c937d5ebfa236b9bb75b0a5408a61e160edab56a2ea40069d07fcf009fb8f38e88159ed2565e0e5d71d43faf359b0cec08b25d6cd86fe8556607f55e4f63292b5e63de758094ddbd4e119cb1baff0776034b0e8ca5ee48b4dabd9c06d89ca726382868180b0960a96b974c4acdfec55e0e3a895d58eb158543b5f70c30a07f4cf1933e498edd6f1594bcc7f269cd2b670526f5cad7cda88db8dce8629d8b03059f44946aca76a2b8aad3ff50547f97aa0301ebb366b542e75cac85550a01edc5d9f124b87b65a26928b7237529c270b809b93d85eeea3393abef724784b8307d8f71e5d11d5d57c1fe67f65f507eccaa063bd1c7509f618d6dbeaf3b697188402db41b353736b0761d08d88d883a2f0b090bf2d6f121f9d28b4d616d6ac91e28c4edb6877f5fc5557294056784a798230fefb467137c63d1147a7108f53c3d829d04a54b351ae5ebf6dbba15c52887aa06073a9c95f68e7745a2bd3edb31de58e11ad25018b545039853ba836203944e074c390ab3aa66299a3714fa0b8a91901ed65c701580398186d7ce24ae1354290c8f397c93a409beab93e6091dd842f454ee3e62cbdfcc29492bb503eccb2a1c0c885bf070d23238ec149c2daf3c5be9a9991a9ba255ee891799743f394f93520b2d20498495f8c3b5069d904914429b35c416057b71ffa2bade0b6f81e3beb40bb54a716ccf419c65d20fdf51eee71415e68ef134d1b7315b2ee62dc770229408d3db59c0e6c2c9efb19da96bd7f84c7fcc703388835911e2083e29accb9c8305ba311334ee6761e3b92a399dbdc882af86497e5e9e9d80bf00f6bcf53ea21208f7dc2ddfe35d3fdd46517cf64dbf149f5ef2d02cac8bbc5f43b8fb3cde77c20553732322ed3db3e4686b1c57a1b900a7e7a3e3af6c4c0e907f2274df6343b80a7f6988670bc7a0a8f0675eb99a89d861d8834c6bdf28abf44ef694d549e239041ea31c10ba19b0369a11f61fb839f328a39b9fd833f64d740e74241f4db4850f22382cdc8654fa088d55ee6b62ed8023dc363aeac53e95653d6fb9434e27ef0f275db1298d0d11fd767ca64b9919273b075dd4e15a109465ed413ea59a6cdc0948c46562055cfd7e6acd121b63fea957186f778c8ec1eb0bc12ca80ec318330ee996963b34a4646e82a38437af659a18d0c19cfaaaf5e119cb1dc50f21f3b700822986d743b03cbb34a36ce09178976ab9cfe8c12b26e2f0336f07edf471de00d25d772c234c55be41166c12d83a4b51aaab4b3ab77b54ddeeff4b68b003fc08337395a34260b92f7656e6208524b1217bff180f25e8eabb136f0b1e51d4e605d298558b39c34ea22f04654a158527ef85889d7a9d41551d8639ec5eafae5e08796345c40c31b7c28da1b765be18d5599137915fecef65eaca9c7c4c78c50b077836cecb6c522209d310a0c07f170b09c642f951325adb402d595e53d73f5e0748ba180db3b070039fa090302f73271b2dfe937180327ed3b1405ac9df198104dda7f8c8ea6f31b3e92eee292451a9a5dfe3af2703580707221a9ff8291a220854b3fe4deddc77b9fbe331d4e74e2e89b3fdec28ab411de71f87bd41af8adf008a6a7dbf27512441a5999d362b628e634fd3febc7fc5eb862122aeb1ac6bfa080ea30c00eb18d5c047656bc3a796ced004759f1ad04f1973901b10cf4ef4490f58001d1592f13fa33037e4bc22ecf72a2970db98d1e8d82722f1d443c2501f0e4f423eb7767c41cf4e1bd34bf17a1440d6ac28053b8b0df157aba27547b94d02e0b288abf2efa8b4348025fb47e1fcff1fda0cab0d6eee78607b7fc25ee93d0fa96a93fe64890827b8c31d267027aedcb2a9747285aa83eef4d557ddc1ba93011ad0823ce50a6ab1906b1c2797fc7cf576736b3ee7b918544535c0679c656c0d5aab8ae696617ec546e90227d95cfb956fcd15b46c0742e75f9da9f31ae30807b132af9651833f08264a742dda198646c69fce8c133a70c1846bb1a3f8e10803a07b25e8ef629b1216aa3e2e188daaf8a7a00e7375b4c12aa8607d73222272025a1facc8bc5e1112e42f836c8c9306e2d90f98ade556f4f321a7774dbf68620b6d0c72b20732f83ec7a355a4510508ff5ac223a87c193ae2598f53eceb0eef077ee2ea781164ca42da4c9995fa956f566b93db3fdedcc368164f49b5523f66035b20c6787939d349627a821fb78aa4e51f4858434c15d9d2e82fdaba90bf820eea738ceaf81093afd3ad7d1ca9a9d7e940741c8497b5f174c8c28e490987970580842bcb73c62e0b98695d43d6c877ba243cc8e13be27224f46b457d187705085ce1e4c48ec8c425fd377331ee7c495585ccfa993d598da0639d597194aba207891ca46b7f5994f5c4a6a067c76bad2f3ace342a3de56c99a58218699e41580ce9bcb060b69561c29bdb39ac0a32c2ac38547b36c1605380cad8e584c28d0b054c1e110d0c9ed87410151da57a5865806828386ea8401f84f1e864ca80d2d60767f6fd35d9965b2e5498a282606b90e0f07305a94753a919fabc1e12376ff204b155f53c6b4aa36fdf67ad2c306b1e1e7c109f9c521f25545ac6af4e1336ee091e7ce65b3f281c57294a76caee5280283c98429ba6191e6dcc3cc7b0870b9f0ad76748877be793830ae2a96c8ea1d4a2ae74556e0d692498b296533d4cd27404fd67a698fc96c43eb7d40eaa3787f3897466945ce1ab3a825a9c45e40a783003a5cdd368a6879e842982403b9988881c3b1b0201def49e6699f98d9be55e4105c2c879c99f93da90caf1f004d30f20fff6af22273ccf4f03f9aabec9042db802e0774ce4695d25c33e1b4ba73dc91bae60fa77dbf524c25904bdfafff210a20e08a1ec6194fbb8a8943bc1c3dc626689a7df4cc7f404e29273a1c617903911095fda7276bffe02f353d71d6362085e3d537b8bc068d8d6857d2b3cdbe0feb9036c97fd04a9047927e652a7759ba47e518c527ad8660ba9a5a3deab787e1a6a0022765a4a0645d610b6d7b7ffbc165c39189f92d160345b2250b657ebd5d7810dba07b24e98aa686c455d96b35d7df3883bb4befbe54e8d512600052f7062fd014e0f925af398c80d8ab546c8c144f40d8f6e4225ff13609d0d95f1ab7db8580f6785f5562ac2c50d8fa5153c3e151c46952adf2027fdee5bbc378642efd0790f403292110b08702f9495037099dfacf8378301df774ae05b16e29c37a606d20d -generate_ring_signature 246b0d03ba218cdcb8142ceb5233708fdc1f453f1a492cd962e0dd9b191ea00b a58a14933f07ddd1da220d588cd863ac95d4745b46b0bc581c8b608b8aa48f94 198 0db161f672914d04b3da1ef8fffcdb431771826fc0c197e8825f2b16f0e1ff2b 6a77d52d3688f88f6b8bdaafba20b78065cd9a9fbd05213a96a125ca224b8eb9 e2ea7c4d07361af255042d42a83fa1ed8c49a9f454c7a8f6953ac4a25efd7803 fd944b028ccf14871fa10383afe9e30660a86635d20503c3001960c37de2e69a 61410433de0ad6345b5991355caf4432ae0f6d68de7b8b92fbee59a8ef3461e6 b2a13b81e18cce2587150264dcc9a4b7d5db29199601ce267381f912f3950b7a 54e61f2d4c10373f8ea74c8c262df8130e8d9b92529d9f4010822561ddf431f4 243e9f19ea8dfe283cb4cd1532744223a9530328854c771da9815e411c3ca262 f7c880c639b2cdb9f5bedc069e18dd5891428036391b3872de804d2347e06bfc 7899ec95851d38036fa9305b0d06221227ed571c0f488f99357e74d0e97f2f41 b55d7ec0e21c1b1c397775e2877af9d50328d9bc1d1d9b41075b2c82e59c60d1 86da65cf84ffa0efc5d851f16927ae1ce5cfe37f22ca1ba6bcb0b0d0eb64e5f3 a0baec146642142849fc2bbe21b3fb8dd36c5bbf4c4c8400a9a718732be93895 18e7f44ad2e9dbae18937a04deaf986794c4d1de98423a0ffe9e838505218ea2 085fed1c67c7a5446f05ca2258b1dfaf302d2c357c5626db2f6ff1472be6b789 9a7a89bb811e02200372ebe05f263dfea069011f51616bfeaeeef4d06b471123 c7da7fabfc31f636d3357b0153cfb6dffd2ea29dbcaf79a51b0c8d2b9ee141cc b49528744efb738fbd350f830e7c94d14fcf929490b1a7a7ebef9cd3c0506bec d392408898105d4fa4ef7a113c5e51fc9ec12e73d3a3b681895c5d1f8d64f9ec 14c14d0a7083d434ee67caee6b865733aad7e1c6b53260adc4a67ae429001630 d0a66f5a0b9f256542b2f47d20d9ef0c9f0ce902762e794cb6bfd0223bb03309 53640b7ae4b07158cad551f8b87080c9665cad08ffae960cd538eeb202566880 333c1d4edb0dfa89b137577862e6280720ba32d5a8985cba281e9708a350cdc1 9c85633679a683c2c00a653dd983fd6e6939e9a5b3fb1a91857226ec720de3e1 bb1f5067b77b9f97c17f4a94e9f23c83ce86290fa675f0af3a88bdde6a41c8c6 0237b12838ac6dbae1e8129a75907939d6995c2823fad75c981dcd983ada76d9 5222c496e1219b242efb961b9061080da46d7184ea84ba3f443e5f56e6f8e301 1a73f7cfd54d0c215775175106a40c23a418861b0ecb10d9aac208b5cb917dfc b2ff34e86f46a9e541444950f04822c87a9f3fed09eda2e354ee00a25adaf5cc a37896dbb89f13cbab665ebe009de96110741380b6ba0fa3303d025eb2328c60 2e123842381b4b15f6c08d5dce5b7cd375414fd0309fef0f0234da74f928545a e594b714f1b8cb58d6c1784233d1889c036ac446f4a109db65ad3e504759d3b8 44e1e6f6481996fdccbc53330ec84315b23f2e6177a04a838557bdb2bd967ede 21c095ad1a318b4cb437bc85018015dd88f7be33d7a8dd9866c1748e68586c5d 91c5ccad9252630c2ea99586a87420e8af58c3348ee379c2d1ddf0a221b8a6bd 406ec1214dd65890ee515b98612c06de1fb192b5a9c79a62a30119622a63fc92 b97aebdab39ded19b5151cde77e0dba3bf884b20d76e989f998555379e279d1b 2ce46bb4e29bf4020c8f89041a0127191c91609c94d10167a8bfd0a05436c7af 38dcc4af14c5ef185e7eb728ebd9856666ff74e289176bde4e13f2e89c916b64 22858147faef84673a869220da42dd7ffa011b3b344c2e78d5f060207d030e34 efada635606131016da8e1cae06032667d020a4647fb543730168e424a5b8e3b 267cc134a2557c011b156d1b9d5f9d12d8cc1ceebf5e3071fa96878aefb28980 ec9eaa5c03b8773659309ddabdaa3cdcd7007935175ea3a6f5b3b929b4b45994 1832d72a8c8092c0d1edff25076be448986ffb145a7eac5f4bc99665bc4e636d 76ec16c44937efe375a1923cb4cea0d42a394233d00ed4e77461c0bb19591f46 387cce667b9796139aeffe1f79997a0d019299a8129b946a849f5f537cdbdf6b 79106c954375fcd054ee0e558d8959a6e8b100c1b2475c1f2a30e12e936125e6 7bd06529c0c944410e0027d4b418105022bbd479d2791066aea07763a2beca9d dba510ea42aa855ccdebbbb71fcadeffece08803f3c2760c1822a1e5f4fa9663 c98f55e44c5213fd51b52e869c7096ea5fe9620f8b183d484d447ff8a7779778 1dad1ca1319f93621fba40e4b7ffa7c939cb43525421df2b096361be1b8b1ddd 4ba49cac2da0d71749bd8a99f60bc231bfdc4304ba7d33323a362ba045003af8 823f604a64a454fae850acd9267ab3a7eb96deec9476d2ac69c2f013023ff2b3 afcc56fb9d925c8c7172178f518f795dd100013a35a22469aa01850d5a2b8933 3b02bee1a2346707050462002b2e0624a7f3beb0f61199e8082503ef7bbeea13 a5bf9d17c3fcdd0678cd6e02a3a44cb04127c3cbe0905dee1ec3e2f207e8247b 167cf06f2b5cb320d5d8e82a25312c768d176c1a8d7c2a978b0a6b355d9291a5 121f682795848f0cf6881bfaf24cd161e1d48ec64861a2d3d6f5bd5bdd8d1c19 1c9bc3a726a992f1e7afff27ee6cccfcea8f103dc018161d0bc3cd3367bb8424 4a5293fdea6a697bc9a561326b2ecb6a364a38d28d5b51ac517f8ac36a598633 fdb2f1a88848466354fb68e56ce89e17026ca60d049e063a67d85f6a29848aa2 8056d4516ab1042b2f40fe62a3e1df9eebc350f4cd39d7c60744a25f127f9f24 43039a60e66e80cd341f0d915608485413cf54abd00d6f74ae7d6b400740d82f efaccf178d84c5bffbe97a3f0af3f982049f3b4f7ae981e80b5caa19d887b606 532b117cf74e4766c38d3bc22de22b831a81496e04eb9f30cad2cf1a08c2e371 517fa25d585f74a45202d4583f084d656ea1854b0d03219792f4f3a28f23d58c f472a57780e1b0043e1c06079f64664ae911603ea663becd80d6ee23009000b0 eb6d7f2c9d42f5e9c5341ae6df2213ca5aff31308b8e56b6d9600fa2b3283a29 224239609d5215fe464286cedd2a04cf5be2b4865f3867df83543b1b9405ff32 54ce5094e3d07710882a35a255de37cdf2cd31d9b9c121b3bf91ab7bdf65c887 a0665b64ee617e93e6b2656ec248f99b568dce3cdb93d0cde77a59a63153f467 7a87b59a3b81eed644cce872635415ac56b14e8ce1d57758e3b17d208f802ae9 cd266d34ce426739a6817e41b47da687da15d0fccb44bcb63afe3e6d21808304 91d6a6f62da7e9086bb77ef0fa1652678f4112bf8c58966141812a41bde21014 7f11b0dce98b3b5b753547cad841a49bc70c50979cb7e26931188b2805562c11 6ebddd1fa58696e991018d0bd21507c9fac9670dfc09b83460831d0f99d6dbc9 d2a836ad7b06d482f0473cfcca188e2667241e2fac3c61446616d85e2873209a 0787949bbb01720189c8c681c73df597e5604d268f8ce00990f2e3d5f21e2cbd f7597c8aa7d943292de1f422b9ab12ae9fa26e6b029a8001fd6af739b8c5d16e 3938d0588802af284691bacc09e8924d5339abcc01664cfbec99510906fb8f06 0655a083cfc8ced5a2428b8ac6d90fac5492cb59d492b50c82ec706a9ad621f6 e67d8a5f62c1d7284daaf885e438f79fdd3f473cbbbcd7a12955ef107ee85c17 fe075fc6115e419d5e51a05bea69b864323bb3223ffd2cecef6e0c34e5b4e1fa 231b075c7044e047993781d57fa4d637f5df0cee973f033702ba468fd11a1e6e 63be8069804734a71417e6c6492aac78377a94411dc38f6ca94c1687fc0fa574 a71d86fb97b927e7a2c37baa95b6be30bff078fcdea7cee9ffb2ae3032631867 2868f924fa5a7633a1465a19064f6fcf49236cc893570256b5ff83def889dfee 9327dcd74a80d8e531c1355dab619058fd5cdc4fe01a06f037818a975a7fe74e 2499435cfc2c9432b3888b7a2807f192ba19b62b45d7d273d7f5b0a204d3202b ed5ddcc23a3f887ce3fdb9bb25d758312e7909f9535f9a3ff1624cd86f903a3a c32b0e534cbe8a00ebb0576d80b80dfb4b75f5b17f8f38c9c84e3fe49d13bb2a 9fca90b55ba9b6c5f2c050d2b2f1dbb3bd9e5e51e7b8476cf1355421b0f59b7c e7f9c9aef7db8e8dd1078201a3abe2293b43236ab521c8b0792d56bdd3008246 c09f4b85470610997c9946ed24c314d56689990756c11f8544420cc87826e683 38323e53c2f779eb1622324be8a5fb006af48ea5ef5e3ec00148a147b00f6542 1a06eb4466f0ceb6a73be5b1d799835e86f6ff309a8397a7738351b9f5cd1a00 f1ce554ee1a5e1bb952ae702018ad3ca23eb11ddcd1bfa11b0c1c55256b1547d ddacdb96a51e0a4627d47459904ac22d64a220a1d1bdbffe16f5aed08820fec8 0022e83f981f666993ce52fef3d82566be91bfc1954673b07b7ca3ff5fc58e49 4fb2031fc2197a88d496854b3935daf08373093ccce1f3e69486e4b35c35961a 12f5aad6c8f040e99b9426b95229f2498481a69f4555fc2a593768b68d8df433 a356628599009e89718a1d1d6966f28da025984e3b7dbb310de1e39945606db8 35a5213725ccf4d076a71e87981b63097228bc1043506e1c2ec429f13355ad88 1ec779087cfb19cc36ade2d26f64f9bda01057e81ff21fba482aeeed4ac12335 a913b76c8eac995496954feb8bcc402d6d6f4e800789ba28bdeff1025d818bff a75d75ee55b992b952ca5cd226383b3964322796819dba518628f2b4ffb2fd84 e2ea18fbd119c70d15f4223d594d40202614979a08ca9177ed2c8c76862b2e22 a89ec97b2fe0a79fe66416bc90f396a54b020381e2c7942fede9d56c8b2a36e1 1d27f8fda1fb4ebb4ac24e91716e636398c702214188591e68243123e126186c 02cb80704c2f7e0b1776e82a7e16258f0a8a4c3ebdfac82423e3f72b7ee56d4d 9a524da42a15229a1c21aa734d09e4a71c0ee202f5334ba972c59e00b0871fd3 04cb489a417d31f07b8aef25651e7ecd6ca857605c720860839562f66bdf19d0 d543251d5e015dddb344028dc9325456e3a65d33c5de7d3ffa0bc4f77bfac4fd 70f3c9efd0122620789d9b67d84eba1218f40df0ee75fc6e82566e83e888b990 643ac4025e417233bdbc3b1de190bbb1a0cd3c2dc8de7b9ced271bf3a067e391 1f86b62e7e9a8373f99508c94eae94e160610de9a2885dc75a4704f05d569b37 a329f36a793abf7b70440a1b88da55f88de436e4b65e75cee79957eccc454d94 20493dd3fc860598e2953e9fd29ef927ecfc661443285e7f44b55aa2853b8b40 dec1cc91cbbead300e627e6aff37467273a6deccfd077b4f016bb2ac7f54e591 49bf801c8c676cfe1313e692b56aebedd91546d3e3037d72cbb957253777313b de0168b980e3e80133c7dfa74f76a8eaf7a6e39c4c6f5e2bb9cdf9bc8ad94055 26385c685bb1bd0e809fc3732de977a3fdaf31e8547a78e18225b83120bc6e69 90dae46f81b1994f6952fe1639f741006af0c02c44c72aada46d6fe9aab0b879 cab19752deb880f776f0fe03f32025fd7cf1258638a487698a90302727eb80ce 90a85fe21ccee828f07c00b4c85f6d54516cd4bafd16a8b6a6ae81b2b5466785 430fc038dd9548be476dba2c31c6748c7a354e3c67966d3250900dd39c809e28 49a2d2e4c02a5b94b5ce54609f7dac32aca0a4b2051b784ade638db8df4eecac 4d6ae1194d575bfea52a4e09266ba41676b7a552346100db98908e927ebf18f4 1b0bb0578845f9c1f7c568ba36e7936729fa907f6c1a8c1899229cedaa1aa306 10d3aa986969f9c45562adacff4f74cf399294ae3d6a56c6ae72135f51ea90ed ac69f664f096b6a8684c5f014a8b984f1eb9ebc2cd7edd2b28b37ef25e3147e7 d348cffc84741e63d73f26e953e1a0c6dd8349008edaa22da7612600561a5ff6 d8513404ebf2c16f6f7775d3586391168e0846a973f2229d569a12a06429eb93 fdb70b83761ac9e5f0efaa7af09e44edb8ffa0183b75ce52ecb5f6dbd0abbede 595b8d61b840a5659888c9bc06ab291df02f4cf5199157e0a5b509c382198067 54cf1293b535ff5aacc99099974dd99a1261249472edd6e6b4086fc1cba8c90d 97c875ce3d7224207c85371c7b1def7eeeb5b4690762b719f9f04bcfb866c479 c348eb0c65094a6127d7fc807507c8302a1c69895a97364a2f01ca5482a20cd7 b1308b60a84a9941dd55462ede7f9a54be15fbbb3cc28d7d3479252d0aff9563 c9517163ac2088e0387a0cc36e85345d134fa4104cce020658fd8f542e836edc f64f3d2a5ee687e1f8dfdafc7b06b3b62f1829d4510ae5c41d1e637141b45f80 44a63864b9473c34d0eaf8ae941eb127d8413c6629b8c36fb08b0ca4dc214df9 93a1bdf9e791ee990bf5508b6bf513409cbfb4ebcb16852a38fcb493810ae898 fc94f570800fb6581c4eb3d277be0a34891423b22e1f3391fdae809f9e75729b 12122063caa42018d7beb28e347bf7c0831da5a492b9faa318f822ba6b543008 0a6544c33ea85db30ab5ef3944aa87b5243c6e44a3001f64ef98b86edceb9520 64b6729a7871fc0f35ec31ec51dc6e444b2a5afd02c508e336305f46d5eaee60 bfb50061c92ceda81088d5698f56c4ec4c554354060a38711460332d38e21a33 d77091a9d6d90d9e8d68d1070ae328ce59f7a6a6599068e7520af35fee82fc94 d09449e1b37600ad3a249b93f2decb928c3ab1916c38cca9b56c38e93ef4d987 bd1feb8de1340e0ea9a15ba6ea5bc4f897f0b3d86ab4b882743620cf7133754f 00ced965f3ad93b796fc644f608b94f5851bb8be1559a5ef9a072f07437cfadd 610ac68246075026e34fe339cfd31c413a4fdb201bd7cfe8773875ec5f08bb79 10ef02726da9fa5618dbabbe19e3670ee4167b7c9228f0c9367a53cd8c5cdd95 0137deb5bc0cc9a05e0f12f261da6584942c556c0598db0bbaba1704362a8fc0 f69de7e6f5116310708c3395a3e4e95e4e58aee0cf39670a69d167cb308daa8a 17a8dc951c11526dc54a5b877c78de0267a21841a3fb137f0037666f996795ed 28d84bbb485761c0177ff4b35cf8982f98f7092339e8d2fc448aefc8845c0876 540ffb7709f0e719fb5125e443b79c3bfcdac655671d33187b750bb3a27cdd59 b7e545642adf2ca59cbb67c5161f4fa5c82d42307201b55b55dced00107de1d1 bbabd53bb8454036ad63cddf866f7e6942a5e65d740efdbaf15393a46cc2a5b2 6a19d37e31a1d58fb394814334fd5a85f9fcd1bfc3b816990292f8d81eb2519d 98393707ff0ee3143d5c0e397dad545f7edce40c334f791df327ea542d31cd75 1f71b20fe2cd0064eb2688696006fcb45d5ad0b722891bd7c8077d9717914e01 3751f297c85b2064882e0afab06492cf1b103824cce5c111e2d841d8e8f459b6 ca24b137032418a3d5805e53f04ed21bf57524caaa5cabfcfa19d48061f5f612 b89270edc915387df517efe1ef3d13561001ac485733775779affb438e71d8cd 051a33c231cdddc05ee22d707ee5e8eff9d1ea12dcee617f7dad29210e08f6c4 9119b076330d06caee1dada7957dbea09e03144a122ab049bc0564818823ccaf 04e925699f33291b1ec77269f0f3a02ac1013939697ef6e1ed1c040ab59d213e 9ffcdfcbc96482ce62bcfff69719e9a6ced6e3cf172d0cd234ed9c7e8e5586da 9c35c55dd07ce906697dd1b1951fe1149ebd04cd7f31a0261804db2990cb6274 359f282831d45f2d2f915dffe9daeb8acfb4d99c5e2f1be17ff3671b378a80ec 1c10d7e23136bbdc3b829338ec8a11bca411d5faed84fce332ce88f7426c43d4 6a60603aeef94a7bacc061cb070fda7db41fbdcbb654a4999637af158acdf1c1 97ba1f621d285ca0222a2e31ad7685dc7aa40921f8e9dc0925bd84854d05dce8 6ac558b2d25d64f6b3ec05a6f1515f50474071883e3212cca0f1a84deb99ecd0 e490ef89f3ae5442e0e7afe3bab29cc80801fd82f32b65237208a5644ba942f2 7a904de44a30db4455676a6cea297a0bf420f5e9cfb9fdfae8aa833995fd7c65 58f12241a8a1ba839dad5b602c6ba45cde6113125e59f9052da4c832d14a66ca adef9f1a7a83deb20af284ab72e4fff3c573bd3dbb991106d40f4f725f289c8c bc3930af2f43c34e6d904ee625bd2beebd315d583eb7523c18f6958526bb131d 86e0d0697f039e5271ef13abaa28ff801f90e6bbb79ea905df55801bd0462286 c239f19f3a2be47fcd0a23cf95ec09547157010d8e6efc3315651f23e2655176 99e7300e40160c9b8dea5c066c1578aedbb8c9e35eebcc8ca77f2b2ea2b02dff a1a55f538026ddafba163ba90b5f348df85a214e35420cb11477d8391f62031d a298dec7818c28ffb74b359efcc38eec4b32c1e73e0e8881febd3bd04da66040 b2bf08059fff6b7ac09b835bcfebf106c4386ffab05295dddf769653be295a54 6a3938a46207a8cc52ff35a881a7ff5fee19b918c8d354ec074a2943636508c6 4e96f1e895e106ca0ee4fb9ccee77b63ebbb14c1d514269affa917a0249413b7 4f1e9be08fcee2d1ad61a1fd15c8f0c9a1aae378a763e1a953c96254f38b99de 8321c7ce4d12a3ea5003bb741646c771079875712d53d9ad50068e88b7de9fb1 d0a5e457c374b7d188834afff3ab9aae45c86f7db697b26b21b7a8220563c46f 4565759b70e9741178085228485c81e1bcfd8a8ed97b9e80850b7052c3bc7ee8 133d0f3f9a55c3e714eecc0d582a6efef92f78eb607e03bd505970d631714a63 83a5bcbd0ffae9cb2f11def4f599cbec5bfb02705415ab68dde796c121523a06 c9028b0ade18d0b941882b9ad366b079ff9b1074c0dc2b9b1286f2e3b8e6d15d 3fdeb6bfffd1afb9ebfd745e369eb82c8a328985803ae385f265e7c7c5417370 e24eb8851842daeacb0f9710304506f90d32cf3c49ea8a8463779a8ebb7add0d 25 91699a92fe43e1ea98614ba322db6376e183cd772112f41bfc616da7b5a8ae0345487ba93cbd8cd70e4991486634eb4033e06769be384223ce423f2dfabcb8046b6526a18d3fad7866bd596a389ffaa41859f65f92f73f882c77b3037aaa16070aa6a4532d034356090206306fe901645c5d779ca039645f7ab61908be7d9d082bac632faf9d769705c528d02af6b67ee1a36b15f12c5778a49c37735614b00039d8e5e29b4c411dff1ae7d0a92a60fe069e84ae4583a5099103768b82c80f02e264ed40e4d965cdaa9af657a8b5be12975c453cd9db34f0aaf434fdc170ae03c73df3fa30ce5847ca5c44b5d67997e3deaf0fb1ba9fc5acc0b1acb685600d0b1dd56bf8bff45af73fee3540a57a55830e3e7aa63f450b11f9b5421ffbf0af0d62e5f7904e585485f939f8fd99fbc93ba3ea0555ee2880b5379741f166cd800f7d4cb49847f030c49b0876f04003ce065442df33f687cc6fbdf82310e6d08a0fea04dac85fa056c299547df83280d996309ee2420fe199510c346a3850e79b01f6ee83abd38802bcdd6a7e6ae198946fc6d706cab83598f3f84299becfa1e00daafc7f4d96701b59a97645858c802b54256abca1ca596d22f18c9f440fd5d10e542ec3066c2412b7aebd229a9693c2751070d55ff1e248f11966c03ef3c6ee043456f3593f6eb82d1b99c21a73c826ca0570e8a93790c5250cb3935b6b62e00075c09978b2a287b93f30211ff1dc8a06e4d44b240d3676ebe738fb7c498b2a06e61bce59d9c6aa2e90be4cf1e62de6a3d4e329b978eae0284a7bf36228aee903884cb456c866b6ba662d621f153b30fc1fab7527bc8d3f12336f9d4afb6c3e0eae495ed06f2ac43ab52eb144e148ffedaabd544e16bd99d10951172f1b573c0b08d5fa8afca8e3c5a4ee46ca1a9b8f3107a1326e09cca06e83590e63e4b9e70d94d629b1203c54bae9f329105e9eeb447aed1f9ee2466b918bb36039e69d52075a0fe42273b217acfcd403af135c49b585e88bf3a8a984ae22ef4567878aab0e9fbf012e019f54ab863da4430b62ef2c924f2c1fe6be41d86303462e24e2bf092b3638c9d65968d158122b9d6a964a94d4d93e1a4b066f24341c95c5a8a9540b73ccf7b95826840f3dbce8cdbfed75e3ecb3857fbdd3ee13f69a110a96ac280bb52e6fd2500f35e6d619ba24f47a50346efa87a3ee50e97fda50c6003252e10101bea9ed9723b3d5faf71d1a573c5f1f588ea06ee149519d68ab52d8b70124055d056221d9c5c72e8c08375ab3c960ae6820e47ec0cab360b9b98049b8c7cc07a599802a7658fe6ed6527619040db407226db299ee03733be5d92037690cc202f0a828e072a47936ee3ea94b5fdd515e7ad58980b85c79c0338a44cbd52ace018984eda632b3e182c2baa576243ec05f772d07b3623dc54611ed9062f352cd0357df021827ceb55bdd7c11321f6663264acd85856ed7dda036241e6db096a30ac7d7b0ace5df2e568f9fd56d2d2071ddde4805d2cfc005ecf74d99c5e5ef5b0175a54799f9368c2f4ac6d22352dd6b0ef765f19dbddac7e1864173f70a19680891fe7d839c70444320d0004c0de49f69eb0e19095355d1add6768e0a6ab8bb07d8f9e8c83bb088a182906fd0f0a5fb211f0ff5e578181f0d2a5241a8aab4e30141226e5afe62f7c524df3b447ebd2cd0512eee099da450382c9d46266755cc06ebe0e10980f777c7204b50628b09dbb44d04d2a5066cef4c03830178b2b3f40923fe5a13c683dca11086152475e9e3c78320ca0baef3b20742a15225522fdb004d8e0d330d35e128ef26897f39f49f8b33cfe292209ad6409e6f0bad0a30bf06b5ebf734a0774fd388611125b9e74c2943b5bc33409356c9af1965bbb4ed5008a0232979193615dd2ad14065515f34b77890c28de49cdb2e8642fbf7ac251605438ec69cd43f0161f913de67729f2e5390d84d6606164dd30917ca54b9a84c0efc9bb6254cd6aea48fec1efc9a2335bf6cb232b406e925df5853630fe8d7ef06a75bfc6b9417f76e4d22052655da4b0f177df410310efa14ddd089f904aa8c0c459c0b20d106a92902828c2fa62dd8f44a03de236870f6b2c7c649713607b104d9d83fc4624c793c933a806a5fbdcc49d26094025e09fa63970f436d56bad60f4af3a66c5729237f613fa7947521e66425d4ade98222514c6a373bfef59a0b098a0fb50c7f72116ef28d4d488e4ddc92a66ce2e508954364f2f1107a6ccc2b03abab11440ce824f3aaea307294baa0fb59376246c40f3fe42a251c39f7fa6a0e200d7717a9b5585dab5e45ade7a1b9a5f27569b9f78988daa69bf81f993c4706af089ed739c7482f430981a29ba377b7734465f767c8773bdf5719856c241c00d229d4e42b4f5f780b74412e712de26c63a5c1b88bf09a631fb47aa6e802470d7d3f81375c0e0c8f8635d2a8bed3c93c0828b6b59a0c426a22e1889744e96f0a1a799dfcb662c9bd87e7b51eed8fc0fa28719328843e443ec2f5187ed5e58102dc22a0e0b41e98ce363fda693cd1c0ba1a2aa00b513260ad8943efc6f566cc085ad495aa0dc142edda93a60a7f0eed7e62c46dd0d0f8598f8c14e6f7d3e1a005832b30d50a8dc2cd075aaf50af96a494c077462fdd283e2a8ae7462582ff1f0eccd12217f9d4bff5e99be831d4d21e0a5629ab5b12238d9cb8600f4b101ad80dc4d0d62047fb5986ce9f714a831f39a32b9b90d9aa74276658b5eca31b894905af0c026d4ff041bd351cda99703ac9bcf057a3ed5c4a7c6de834c8f72e60ca0291f8e60efc5db2bd0bc02c47cdb5dca7a0a14d6f5d24650e78dd885dcd4b5c0fa478327ad6e358d54b4e5f6eaa8e7f0ce2d9922e7a1a15b221f6bb9f3174ec0b91e81ecf00d141212b3e51e3b2e8fde71c1e77c3208f2b3681841bb6b87b8a0fb903333460153249e52af97a82270ced5e2f3dea8718d1665ce9e611130faa0c0cc166cf79ce59616237806eff24a9bda63bf2ac5e6d48979623cc736d227608ac09742106f5bc93a9cce7d166260b313da04e19b31bde9c3887a3180206660e6ce6d1c3fc94117196c4c890ac73c6694f9b414a337cfd264aa864d6571aad04ba86c688d497372732be3b6b6e80a1600e9bc70fef1dfb1ad72bc66c077ed60ea9a03b83160cb86e6d4cdb41c3c3e82f163d2507b182aa8191777eb9c79eac038350babc394bbd84de52f0ab1cb5f0fbaa30680e304fd470fb7c6a2144a8310433f9f7972fe04b84acf7bd7bdb112115a9abb0a357692c7731d8490faed54a09ef095859ec566a698dd88ea4412bf3d62059acbc2a694f639436dd60aaba4f0fd6a180c5ef697a299af6e6a18461bbae187fae4762307ff28c29df48ed647b0f7b486e6966565f4cb03b0f988d520e2bc9465daa7f97b914e0678cd064751b00aca3dba380883c0353195bd7f261d67331708c4f602e2edfc43ca987b47b60056ce116ffb54ce23c2c767b261fa90d1821bb3efc63af74e9ee21ef793fcaf506303d3afd833f825ffbb8117047e75370441703085d86a4a64e654d9d261d0a063645166d6ad995240f0c1830b3fc3a485410da57403b9cdc591d9db2283d000bfd47c4d17cc2e024a77d45a897c779473c7e225cae0436527b70bc782db5000609fcab48484c8ac9a513e86aba1295d7dd7b2bd97cac26b192211386c5d97a040f2bc0b48ef85c5021c348b98b35b69cbd6a2eb8feb54949b7c70a7158e16e078ff54dbbb6abc552f0559746754e4d5feabffc5e6ba3affef36cc01c78def10ea4ce6de40fa498f21c22d434494eed600df26622d06a54bcb5c8cecb425edb069b4111dc66939082ff71822086fd4e5fc6bc4d1fa6ec15575385be72e084490ddc1be5fb2d581d5d909ca46a0796461b6effe82963d0d571a62e9086278e8307d94f0e9d952413a08b4a0733f704e4ef56486e8971ca01967305e9f9b6f32b0f7cff615d1f584cf5a72d99aba4c401f609a6e495891200e6163b9fe38ecfdd06e69258f73492a51f5ead29c642de50a4853839c78e5bcd9987f02d43ea860501c5932f115f12738783db8ec19ae959a8825698def6fc56b1424dddfb781e4201176973e8c028ccff37f00c8466f7443bf24f0443f39b02b8310d55d7d3f22e0615a097e8968450698eaf33978b6abaf7309a8b3ef120db57b25db0d60a32d40c98224cdaefc5fd58c28fe63810303b3bd856d511da92cebb5de3f5967463280d4d8b57539813f692e3566d1981e35e85d891c41430f4b73c0bc58ce2290a480c26fba04bf8b1a34ac21a0976b9d7741e5b6de64c40949a3f3cf27cc10807ef01063f22490855680a800a0642e982ba524ab62d972960d90fcc14a798268500041bba9fdc494310f11411b5c6114ba48f8d796395075b7faee63712ff463f010dc5ed204f68692373dae4d5da1399aa47d4dd5b5395defbfbdde24786bbb28b066b46e351b6155761543790ce065804e5264f0ccbd6fb952a032df317b0d9620cc78530ed46ac12c9162a4c840b8dcee4958c8a334820f6070db308e811174d01915d4bdb08ce81dc320ff528d0ba126f3ec28cf95c8d83dcca655cb225edfd000a939089c26eb8b8968d3064d3af109b4a2ad71a05cd167720f280a2a8777b09a909c1d571a79e045ef7db071134d26aa241483c205e667e77a4c406a5d3f1038b0895da9052b2c2fa00b26772529e7487069a48c277e294bf0ec4db923ef000bda2f96f9df6bbf3eb15fe17b077a8c96db63db56af40745a6902520ee797f0fab36b30a38754e47a88e379299af6ef2510bed802b110ddc9872d52513c2ac0486ce5c202dcfeaa326a67ae6c1195531eb828f7f605fb5be826ecd4ebe1b4c067e0e8ec0d4a8c2429a4314d58dbe983b78e0bc88bec77bd585dfeecd6753b807f280f75aba2c23201e555173e9d19e081b23fc18f561567d1fab358c9be7d508f96b353c5fd1bd9aa31283b4a2157ee947f095eafac073aceb2239ea96fd590d2bedecac821958188dd1ed47c7a5aa9ff74ae83d7e00e60b4f618f0647e6980d20c42b9e8765c19f739c505ca33158596e4445efe28298c9ddea8bfe174bc30a18432db20369124bf7f24c78110e7ff04b35a2c8fb76c992f7d820d526d04400ae9ec601545269d6ea40c6ffe90be99a4605b91f1f2f2c6da4b2eb1856bd030944a278e887c80d63446c74aeb34c3209ae9bd165ce39e2b5097b4f2d0b175b067512b7373c4431ff39e448675481311888ea483e4f4363f28776ebb8fae743035bcd5d91a985d0c4372b14af50f9c26b92cee26887c30d20a822a8404c99db0728170d61d523f20bdd125f3c4903301f4ba2f957f2b11e487cc73b81ac28d609589975c0c87cda5c8bfa9b0ef1ea60a8f5dcf2b8c892018a419f5596a80438053884ec38be277792183667eaddcf424645b12684ff816bd1d4482abfc49d480f2f038784dffdb22b7d60d6b15449b44d4b2ab4443bee8de5a5c22694f695ce0abbffa1d34e3aa8427b91d21fb4bbb43deee1d6fef21f0136575dd7c7484b670d24726ff39b10dcb41798923b2fc1489d22a2e5a95e1a394b8c89be2beb1985073056ceb10c9900ee4a23b3dba2658db70cea0f2a14576a17a3d4c46dcf63c902ce1b74ebf3fee12c55ba9e61c88b58303d89e918d12b96842f9cd686a70bb20ceef9cbbfb0d7f20c0ce6e95e93d2ca69b382132252b5c76c5c588ac07b98f7014324a11c1de768084f4c11c09ecaafb84fe934055fe0c455d81e0429fa74510d31f9fefd65f25cf573c588baa13a36ade0c005931802243b8e45895fb5f733022f1c3039bf0e353d63c790787b32e963a5b64a6a055abb7fd65a02dbd862eb07a3c25bf473bd7b0c6a319da7b36d7a24b280610bf26439f860c6915c1d72d5033930162f0a344bebae233bc3440159a5517a8f3eea84b5911d2099d20c099b092fd75579b2cd71c0221e31d55cae5afaeb221cf80bf24029e10496f1bea42307cfcd880562756bc12138edef7f8f256ef7906068832e4db67c1c71684d0dea05d6e8441102a9fae0e1e1e01854354a00b2e510c677e37f7928657b28f900ed080fc8120d61fb0ab548d74ad1ceb030b271ac6a6fbc4957b7c74b326ffed88306625f47d9307327492a2f89e05ed78422d300eece87787bc09fbb159462bc250b25b472c34dec48a8a5e6fc563e535295843cd6940c28f8fa58c5b4a0874e0f0268f81aa54f62969d9fef3f50608ed0c1d9266516b2d459e7730590d9fca7b70e2cd630d0b43e216264eac09d2e53eb7bb27b9b708b8e4f20d9b5f2b92917c90e1c13d02ff1c6af4fb468848844e1479997816a71c57f617b7a699170409a580f54d0b139b858daecf9bc75dd77baf10cb699e29e982b8c8deab2075388cde601739751a4e4e44084cd919b3bc0a45dc32c9d5734c688e0d9a6e693b7d12fbf02756a38aed80dc085ed934ca0755785751f4d3859f06375dda170b6a088e5ec08de6a7c4683d356c58a385b20efe7d3f2bbde5c7c9c7ed795db106cecd680070fed9afb5162564594c40d2f398b2734b41d24274023d11efe08fa540166e4a303af233f140a410f77f53beed9e8bbb971cdf2fa15bd1fc1029664f7592b25b20f20f08ce5750fd8ff3efb2016ec90a2461faaefc8ca4ca38bcc5f47f39d11340beb19222c43d0aafb2ade825e7125a967c2e9907432816db7c3bcfc418f577e042eb8d7a8d449a23a821efa8c5421b20895e97b34985c13f18d185ea359061b03e3ac695997ec614f82fe8d59a99dec3ed2984df4286f0e5b39e7998c29a8cf0b8a863ef5c6607c57d2d1996a7311a6aa6102342d3951243b8fd5262da46fb80241b55647a2e01dd6aa1de77b929d9fb0d51ed6c862d39c282d1832c0f8c11b06fca7cc3822478816988c6cc5c9dfdb009a642cdd1de78c243fa39c6df308b7085da84a1a17996bcf1a47586e76b9d149c32bbebf618fcf5be646ec708a64b406c57c90a490a0e5c58a41c48312db76fcd1a16e3528293770749cc40c2bb0b90fa9522183881a324a971c112b3dad96c3cb6052ae2adaa03d7496a1caacea7a0aab8031919839edb89e7e8c749a654cba13f8cb8344659859a379aa6a832f700667e12099ea769a1e8ea8e7108cdb07bed50ad60601513791216b4c2382135b06cb44ce9f5d4a6f36e668145d9788495c310217423d859ece5c014e9de01c430e89e3689272ea2d7badb629cb77dae0eac2fd6b695aa4d7ed7a0e3c7659096b0df7717c27cbbd2a7bb777440d3f1b29a3b2eb2aca6f6acc1991af0cbb1458d806b1753696c05755befb92d3f23c5d42aac1975242dc2049c4d4504b462e65410707623ce04282c159cd1dc0eb2776d797e40691526052f717ea0ffec3353fa405e95fb65467eaab4fb248497508c41d0ea71554fb026634fc76960e78ae0f0b0432e799657f175738a62f8f08afe00c43e442c93fbf643d4c2633831a4f5fbf099f3149fe42194f5aa19168b4b5f1640d0a4e9a484464f365914a6251d1720b018579ae58b287f58f8961922c015417790ccc5f3196b81a0140f2a9f2b3a12a037f6730eef1b164d9331ac13ad44ff1db00dab5564d2b57729184312d0808910c38c6247da6dd22c1926d957ee1607527ec835042b47b078628e2c0e154fbc3092a8bd2b26ee18f3be850b06f74754ec40a1a4f0a9683c626eb541d2edbab130521b1c980bf71acc5e3360b90bf62507b9cfbc2e668b2e0b95ba229fd6f7dd9095e8899b6bb49866d68a7d6475c6c305997fd823e265106b3622bda831b24630e5fb4d95d66cdb8e5624f09df6e95ede68934779e663da92d646691108df27b04ad9ce0e62f411613fd0f25602db2337ada3be3194dfed48168db6ff5aadf0608b878e66008ba56da671c1d58515fbd5b48d2fe120dd0745f6c64115848e0ab061c9f9f8ee13d13614331f628c3f5730bba0af989d86912e31f80c2eb8ade5e08043c58345d18f20cc71393414f8e052c987d3421519b665e328146bfe115ce0cc1daec25fec9bf801417ddd038c9f01bd37f38ddc35189f61d8b97aecc5356002394aa1175b5b234208ec7f639c5891c2dcad8c773cb4e65cd9c75ba9599290158c7bca37c78ccaf3e4fb04955d7afefd7a8b17d13b1fde83dbfd7e8d747e402f2bb6f5c271e44787e787d5be836a83f699341c07e1d515c4d22cccd9bce2f0781d35ffbb0e35ca9aaa5d8ab6d27c566b42f74ceff822a200a96f17a216b240ba0da2a917a34d256ed6cb2af3e8c4172eee2263cd85220e9d677af879d31da0991965eee5e81fb7bc547c4735201d798a3bc3e8dd2851102a7138ffec5892d0c2aca6d8610104621f0f54c78e68750f2e873f20831ff4e982d5efff258decf0b298e4538d1c55a6bb163985b207156d492617add7381c7b387a6f01adf576e09d36d62e80bc62d77f8ab56b6fca813b24e9a0ebad9a44723eb8657bff211c50c0b7e6ac8d63ea9cbf971160e857dfff9835e49c47bdb930a4920958e6abfa70c12d5290f2e7307942b22db66b6673a5cb3b96109b43ea5ce59acdab85ba6e30525800e98d5cfa6c5f5070bdb0109b37fe14c68c21310659beec8ee27f62aa50c31d0a0aa2d21ee33f4723870633ff89f776f40ae7ff9f1425ee32516dec4f200613837fbe1f00179b58cd6a678b53560362c5e07b770211f1bd297033b22d40698e78bcd093dbaed71f67fe0b1f8057ec3a0daea31fe76be6387f3f66d4f7506bf30d809b9fc3c234587f6b31c8d2c07b3d88b7f8fc232259de33be47463510cfb87f3afa13b67e44e650c26610672c5f067740cd54201984b07bc8cee2f0e02277b0a72c46b8042da256e57d1c3eb66b124335bf97dd06ee20f91cb83e48f02d163b34b853c800b507da8505be1a7e558ae5e8f8c7e7858d4403ab8311c3208de79c79f235612f6137730ea4df4c4e0e518578e3bb6b24c84d5f56ff05ba3055ee38f93d578422ca60e6ee9d893a95b378096ee8a164b6c7e403cd5e754df03075fc826b87553a67f33e93e6dfe51e4d2f242f4222cb8bb456119ab5ecef600a2745642e57ae63e62c2544720d3658b9534f6ffa010cf4032f5b3f16a7c4108bb7ff357b070c8b83554fc06d1ba2881278d28be137471c72a4e6af08c4afd0b91fbf9395ebc06572c7875b43d06ab3e4d9da771c1fa3e5ab78962d27d93b50713fae3be2a4a59675eda0104dd1b84cb42aba8c501b05c489f10a744acf02f0250bc3d744c76e474d5f2ca2d49cc87589e8ebfb3611a9d370227d41e65df200f729d4e28f0f6d07612ba9de438fee323e61e1ba275b4fbe2cb24940feed4240ce05e7a5c6a00b8ee8bee51fedbac4ddecff70c4c9eb061d724bf4353c7ac68099ac8dc498e8bc9cad3c3baf1bc36cf10dae1bd63f91e705df6a28b945969ab06831e4e6a4e7ef4a486c2253810870b1b6281a7e5191c4efb5fbb3e0d4c3f16016a06c9cc206c116496e4ddd85c7302599c1345dd7a01c5f5aea3adee1cc88005e343b5c5f0856b2908f6b7176cbef56123e07d666cb61b407ac310cb41cca8041a11515f6b66ec23110ba4126d96ecf2822fae8bfa98ba130e7d467ed93f280bbc20802a528bce488e9e48893597f20377076f0d134effc68e343584c404f703a846c5adcb5db29e56f4b6d58e74296f6c38ca4e113bc6f175994666b3f57a01a99f216f4465a29c195fd991165fa0a50e3dc5840999d214792d4d368c15150f5142626b85461ce3f0e6b4829b2e121f64c733a60226530244fad41014774e020f6939a396182bc6f638cbb64814d4bdf6742a2798f4da39bc9472cfc6755c0a911504078a3544e44d602087654928e84d85aa7e4087f9e831c05737bf29a108012c93376641f9e78a3059bb38d9e12c11a69e4a87ed0255869c94b02bacc70e7622d1c84df9ea82c4ac6d31de599b2af9f7ca9fd5b3c345b350b9b01c21dd0c8c101bc11725cac963e593d3bec607cfb393e09695300453574b23e01fdc490838ad57e08dc6b852d02abb7e4588dc708ec9d5fb21db2d70a64b4d19437a5e0439d95da018acff669c5fe760591541eb0dce5d7104920202718501cd69bcba0fefd7e9ad7857bfa1b004f41ae97e1352fd9c8f5833660dc677a78bd15243a90225d48df8c28bd5faf7a8cd221f95b6d38103ea4c3520cc4ebb8156f5450b680c1e4ab0c38e9de2d871ec17bd7916eac380f4900f965ed4e6e4a76c2ff7f3ee013eb5bcff371183857c03f62ab73a6cebafd705cf2d476a9ec82689d898181f0e52896642ec8b96ff482b9d1dad4f311feb5fd9755dff1f26ec9f755e910737008d00817779f5ad405700f00d2337e38dd5793d0578f7fb26ef62f8363c86a604c9258905bbe52992be611ef36d7814296aaa2fb124411dffd2084e88a46632046554604dc57863e7e6c02ad8ebfa9b7b58f24ed0e7495eaee3eaa23f60decc03a5d9870aaaf3168e2c4c5b28169e8d34fa8ccbac6932a277db6dc628051de20c54289e5b922debd3029cfa453232b592e182b8429ad33504ec30021a48c5da04779395912260137868a57d6359f63be0c4f7c80c742ad73e2a7518d337f4b60c2c6a5371852d8d3db84093354c7d82f6e7192da0bd27c383ec20be13fa90140d132215ad5160a3c245ae9d9272aac87454de860e2fdb08bdf9a6961021c90a01b8b0a7c2b27674d231e5766f9f206284a2ebc4c72e792d8fee29caa5eca92e04bfcdd858480311131ba4ad3305e5b4e2f8370eb3f1dfd3d14a49f8eb783492028343860414a5ab3cf645da149b623a9c25a5ac2eb16f6aa761e2436426b003024818656bf12dac5a2dccf67be874458e3f28764ab48861c13c03efd1f79ff202fc362b2911f386d0f7ab062599b881399a7405eb0ffcc3ae90d611bd92e8bf04f5062c399cd6008b46c7f5a72e608acf21cbe708ed72d7eb82889ce8edce1203add0f76ae8c2920cca192afdcf8dc2cfa027b3954a6b135be14c7916da25e70e8502306fd3c50a9e0477ba7d8b91730e77c643182ad3aa4a48f9725f28ec330b6b8f2d0cc4a571729dca34099d4cf199a0661244c17a02ae4d9e552d13faad09710a3b6b42ccbea9f615b001f09b02dbd2b9ddff9a923dbff04aa61954a9be0ff1b8500c9e0796a17b36f447191f5a5159a03e2670cd0e8cee6f4f13d6591a080f84dc2b10c5d663d90133f86caf52f7f751efd7c46806bf006acdc21a1b07073aacc92f5db917564312fc8d068e5dc4225cb1230454bf989d3c3c9b87ace00c11d019878d3cf1c267965e4ac2e381f8d3bc33bf6a31b684b9e4d3c5491bbe0e5d368965739cba92380d58f7eed8703eaed23cd0a2fe7f6656e05449604ef505923f50e2dd5050c1b9691344a6bd2b63312cdae32385a4153c72890baacee102da41085079ea544bf09e228f186d8b94d6fa7615980459c9ceaf0a933e4ff700f5156103452b7466d8d3c9d0d686ea0fb5e39feee4511abe3c73fbefb0612506140e9cde797fc0cbcee9fe01396a294064a11188fba3d8c749e4909623840607eccf527978073b6206db80253333e427b65c03075fda4c9901bf9df87387510ce7f4abcdac44d4d2b240a859ed839bda0dc6abaf175b30de9fedb3c99097910ecc96b4bbd92023af202f886a350b6d128599bdbba366af266582c92cd661630b939fa1604c47882585ade45814f94d717e6d67a52f5fe13aa9d69a78fe41a00669bade2d2ec8ed0197606ca09ea291881c061b3ce8b4f2b1f5088d839787730f4f232402962f89d359776f81e82fdb8d90d25c0f396a70a572760e69165d2201671b0b19f69a641bbfa7e420348347301397ee1206e6da5bf67642c7c830a50676813e6251feffab2e587956de1cdf619efc0b0dfe85bc2653e335ce1e7d010b15adc24eb4ba2a064ec2b78724bb586524788386a1b0ada4f8dcf24303993f0a4b0049d7385396d764d62d87de60b2f20e7bfeb771b4f42fadbb975770f4630ea1cc86ed9825950a78e9a5c27e5e7983c61182fcda36e75423d87ac9041d320abeda411ab96dc4bedded92752b0ca4ba8860bfb8279fd4d17374c606d4e09d0bc85ba45eed1dd4f869af265f032cf89058adc0a8538ce8d3243f96123a430c0309f8a5ccc588dc9d7cf08824d789aeaa0a405e687ba7b97b711da0912e35fc0f9c946ab6cebb64b0133e441d8d02bbf061fb77cb36ca452be312e0ae699eaa0425ed7d62876d0c945545ae1a3c9a1a4f7cd5b3764bbeeece90afc6a629c8f90f5097b9e767a4486d4f8607ddf40dd25c2df16c961b0ad1f75764df8a72aa6907f97e1fc0c11c5627b95f68378ea65d726f56708780cbf0f076009783f6031a06f3711c43044c89e9076f1d9c57c5aaee2d921a6b32e2e88ed91b7d800229b60d26abdab5ada93edefb924150504c95ca0162c49d45e234c1f4e26a2a96d7ac01e0e529d97f78e0aa1985b31c3750e4c7810e99416d7a09660732f5671848e909bc42fb8784dce3728579d04a09615c6a09fd49e1a4f985009672037e34722b0c77c53229832750befd88f80aeffa6f1475d873613695181a7c9fe64e9e8f3c002aa73e0eb6a21a85a512ccabb93ac0e1b089b2ad900f717bd60841008e97400968ee123a39eadf024077247a56cba05f8ea1e4a8b0d616498709041d3971960d068487d9a6d3386c56574c6ae9b1fee0aa09c33b24b206c3ffc8e6739ceb910dd7c12d235ccc5007e1c91b91e9100040c75eaf3bcd41e5f5e8c62e84809a050effcd505448b3adcc70db588b9782c09dab732db476aa483366927f76515f9502e3012971da123c8ea5e5901382da58cd269bba30a9682737bbdef4bf901a0903224e938adf0ea5b6927456fc08132f6d52411f53f5d41df9eac9f109f8d39207439b510a791516de6986a5d008364f2e006313af6ef60438ed42415d791bb70b090d06eb3dc7f597f9b582426277de4b7eabb260e647366adbefa72f45d50f047f0be10fc5e9064885c66ed598e460311140db05615192e5e059c55403777a062878628b75db81fff138611fda011a062ada194cb80e2bb635e657d4c5773f03b973fed6428fd0932f21a7ac026b8b2c94dde1cd3182d525211982a75867360b36f819f2359665c774e72bd24091fd7c4d9160c62fd92367eefd9115ccf57c07c5a61ad23406fbc369ae3e1f23f635a9126fcf89a525d7f28e1df60a83d0f9063d0c7b57befa8b2ab425b43301d639f7579af638eff5ac1cebfb0f1dc053240dd018221c868ecf390722d013b346f21a755592049eb55cc066bdf9a613ac18013c95771c8e97d38cc2591a8e6d053145d3a4c15b71effb726bdac189af1b7f0f0e4eefe9f006c78ceb33f4f48fdbd3567dcd9a28ac763beb1a3e2e74edf43e02b6a0e08599d58ea64f8732a8fd2cb39e76ec80ced1212ed5e35cc7d18c2d770eb4d7e24db9e651bfcfc9e5e22c329c846dbc42712a3009ac257cb6cef04d760bd9c23a51f09ed2080f72b3e34f71bab5a601458f2106d3e6e6d6d7e6c03e0b0997daf163144ed852c2200e2450be39258ececd5c117becc500ac5b79e2d52707f43b12090c31e2bf9abcc692263047243c61dac20ba4212079d5a2bc364f1001966a85595fb413102a7aa056cfa886363d55cbd9ab0f58a365e36e7a219cf10b45ced70c0b5f81793fc5639d7dba3bbb06a53a81d54e9a33a26b8b005a82fc083a84478e289b3fd24d3dbbb5e5730bf9cba3dac4ca5dbc362935b971af50920c9eb46475734f7d42f176e78c8a226baf097f74fab4a82ba0dd721c50b8e01809718cab67b368ecc007ea05783cf80c464d7acce98a44a6eb2053393d3acaea04c7c88c0ddc657b807ca68ea0953823186097c0c8b304ca516a1a39cb35d4c20fb2072fbd0acce7b174f4f81726a45e4b032b50c5fc4d50340766c941c5d8460aea6f068a4dc113e8c6e412b14e347c073643f347daf88cacfb668a309462600d257fe62504693dd813d55bd5e5b6d75c6f24dec3d10fa1ca8b062deef38a910611399ef2ba65ce299a088ce2f6760861000703c9a8b63819f8cebdd87e0a56062956224d5f08319ec945f50f57fd42fc98af19a02075e0ec217a747dfb3fd70e80a57b31812e9995f4780870d81457ad3723a0ff1b065f3606482f2b64d8090e3231f373245376f49e25a2a925f058b632c699c32e4d85f28db31bdef4786f0e1432c8af25e8a66fd924feb1bc49ee1c418b0dfae82991edbf9ed677c82bef09456942f258efcd9b203a140a0352cc84fbf276c94323d37410a474130d4ecb0f76814c9830408ab5764f1bcbb6a7048a4bbf515826bfe8282697a4cfbe7eaa0c65895648d1378164a9bc787d3e6ccfd3dabd7d87d9053b53f8cc6fe88279f80c2df6eff62c7bdfe626507a67ee295100fca8b1df2e6887516177e727d08d5a0332dc8be98862c587d2c44cffd94cefc7aaea5cf195643be9b0f855747e44c50b45fa6057d9e3dec3a7af9e63b8650f598504243d1a7439b5af646692f1de92008e23015b9f5057f78ff5aee33d946cb38bef508caccf03965d1e16042223950209de0d44c16488482a4bca2c4e882952d57f22f0d58ad488e5fa25395a4d6309ffc01afdd5d134a9423f102254304d5339a2d0db63711bf27e00622373e11c0d35161da5f4915e520aeff3e0d644437335e18d9145c38078714f964fe9774702ea31907cb1e0443338640bdd07bfba58edbb8b3f3d4a0c2d87a503360c78b20890dd8457408b712fe95b93c8eeb81224ce7373ff394857d4c0b220ab8b10ca086a3e33782e7b6b1fa84ea82b97061f430980be5595d0493e80f3c20f2cd10b0539f97352b592d42e00091fc312860d6506609f68e682eea30c972b50a53ae70ee812265cdeb9e136f953e95930d367989c2fdf7a1a8c01aa07fea12ce29339062c1d01eb0997b7e606b9112c48556e7254cc986cace4980c2f285d492383d30eb452e83cc6c4a1f04d469d86d8618c180bc92b722ff0fc66aa4db222c0bfa00c36d9e980a6558d3aa912c3c198e30ba06610a2cf53421565974678b4035d4805973c939dfcdb7a35d325b4f22449d1e83308b98c4be1351c4647dad678563d0e471f2fa769a615ee51be5bd56810f54e964eda3a4a02593e1dfe729a0925a00c3ae32a9513eb8577d59c9ca6ec8dc574cb5e6ced89e376453480dcad563f350cc5079a8f55c252c8f5982ffb66fac1bee2988a0ef6f14fc22a0c7a5f2e2a2107f7f5d0209f0bbbf09da9e844b1a3f59c4c12655664b146051d557ecfb3f12905a7bf8d0bdd033edb4ea70b35d1df5bf723f970a8717d83df9cd4db3760832c0218a302de4a350540914ff78ad63cfd5e311a6edd172084d15fe7cc30a3b59005cfa82e2a744966b85244b494b51d913d3e3a461d72da466d2e44092d451eac0614e3c3f981e56a488dd7c2655048e283197a3cb4223d6b38df3262e20add570aeed51819e04b645a538e476442782310782df1e971dd61d7b77014a464198f04711e3a7c9bd6f62b37048446db74080b6637ce264bfb901a583ecdb3f01ef404c2ecb491e10a299eb20be84fbbd929d833674177915b88338a80aa4654114d08a8390ccc731fc443454b005c2fb964a51b220cca7e6442df825b812369e6960d434f9debfeb29a574595e8126795bb75babb700f79d0813e91a144b2fe33d90a6b80d03374c62edf0a6b3b56d7b577b6a8e515e1fb0c079012cde87a8b6bae0e493b7649d9c76f48a7cb30a477c77eac6689e5d7db9512e3275378fa9a6bc2079aa5d456da9a504906428de84f9b34e0df23c264b789d79aaea6c16db4f81c041bc90fb45facb255dfbb0e5d2521ae74fe4dbcc7b056ff6a2e19085455cc9c0dd18a86e5d1ab695905dc95f39f653d6db128a281d3d08af9ba20691aadc4170a6e4a3ea298fc04c45fa7a2f5b8007ae277847128180a2e7375b9498fd517130baa931b6826a0a20faae159a1087c4e2ba26034652c0b38a53b0aeb96e9ca0b0f700ce71e41249cb4182997dd04393d81131d579902335abb5a177c138865c20d0663ad8395afdb154c78578c58e4428907bd3569b9d7cf58c0e1c53f3e57e305d2d79d0f023944cd466d16212f4501e2ba289883b151dd553908141c28f3cf00cf4894d8e3bd4f0bd4ad4403a72fce4822ca9eadd4acb82cc086c164735131035b46c652f905f48bf2509b2a78150860f4ab8a8d65dc610dab2d453f12bb69036119f0746eadcf8a4a03b1de0f8c22f0801884334a4b027a0a47d3c4bbe0460938a3f002061ffa01dabda2f96a786a4a19fcd0d0099d1a498c8b76004732ac0f5f518d07a775fed793d45508a063c3fe77135cf68e512d2093bea41bf7d9ad0e67466088f4f5bb5614b7f3822993b66e88fd118b3c48f9e3452314db4f884208e7622c37caf11e219f09cbb503a00f7f2ea0d7d1b0147ea225411ac039b88a0b4989287f23603a1a32a19823f0e238c7418e3002d7c8a5ce212b25108e738101db82fe2d88d5ef70ed231804e1716333e3cd21f8976c556e2af79643ae21100755ac97b7a5cf7e660ec162a4dc7e6661d31a804a51abe6178f7ff2b4a5cb0f0716401d87bca54f1d10b3ca6a44457469fb4052cf008cbf4ea6a0292de65b7b085e630d2f9f57e0917ba530095a5f512c236e4888077c64eb3fa43b70974eb6041e71305259839f7cf236d1e66e488204c108c9f60da7369562bd9439f9d1a10bb3dfaea30e7b1eacdaabba3696a4ab5df990fd7332c58776b4a87eef1f98ce0efd45bad2d47965c955aa7a2e4a93a6cde28e67ef7d6be89ca917b77c4d7ac30d42fb9bd5f1e5e42bbb6ea6efa84ee8a0e119a751f6be839882eaabda76b2ce0c8bb85b16b5a77cd0849040b48e63a96551a1416e712e09713acaf1f6bddd1a063870aab5ac79b04243ff3b1a23d265c8bc6c151d9ba5ce38175a736a38b63001aa96f91071849fb26e1f4293419874c887b0541ba0a84706f56f6793d1119f067a410d679e909e93fdb99b04a42fc7d1bbe873f0a6ddd23f511992c81de5d50a43d105d6db074c03cafbc6905323a0ec9109501c41231e913a9cb5ecb01efc0357c6a24a6286d8833857e19195cb37f5e18148852757b19b3ea136d2b544560513e8094a108ad8b50136d7ea1a5689f748830d234e7d6b4e4b9fd505af622a024ea25c55dcfe2503b095e085957a30b9ee55e79023c85f7a82fb1be5260f9908d63305e4267448fea8efab8c2c6c124b3a1e763b28685280c37d1adb002f680fc17a7e3632dbe18c430f4b799c3a4660faf039ad7e67d724bbb2fb9b10e5dc0ef671094a78aa561b3b55a7f48d64975d9e9affbe91004b50d9d94e2244434702613f71f6a4d2ff96c5ec14b35f04e7d6634aa8df18f7cb354e3410b9273eee0a215bdfca3db61b3961e6efc29e6036a8dcee279d82a32008081cfa4c8b7cab076f34223f8563f8f4406f0cb88c8b5f9e30ff3f37c48eb80814682828dfb77c0129c1ceafcc2da0bf24133b80bade09fb6651fbd68f2872a44d4fb47be30db0006670be7bc4f0f6d3b0cdb31276fe0deb50cd17bdab908d3f931cfb0870dadd0ef87fc22eb156412208e4296f221bab9bd2b9f628207ac5b5e2daa40257892d05d7a32e8b8cfd84db9a2c048b417b474f859ad2110ec0d7cc99a1431224acdf0c102423cd4cae34298f6d5bbe999359bf1641d9b184fc4c65ad363cb3ef8f410c5a696eae469985d0bfbbc0aab3ff609c8e88e6defc0c1a2f73d2b98e93a5ea081890eee8cb87780032722203f865fc149f197f69a597409535b11986a9fb9a06116e557210f7b19b3bd48428b6b5a0bf560e27c196c5c3caa0b2017bd6278807 -generate_ring_signature adcdb7399ebe573a2e88f555775d8df55ceef28b0f51d1256cae8f109668a24c 447ef2e9196b56579e07bf5411b980ed2bff6414024f79852d9d4af70a449870 10 9152fedaec98aba200129eab484df0a6366db49094419265a78ddc2b57380e30 b79be97e80cfdd3ab77c6855acab29c655e3d1a07a6e2b53462aa7b9ef83bcbd 69864e7c079c36ee8698ad1b54f66bfd101bf1c835670beca641f9ed37736f99 ca0520d1c7e07f43082c5a46ad0dbb4aacb97e88d7246a60a8ba3667f2458c9e 053d4ad64725e121493158de54d28826417808669ce746b807d65b1f11839a6f c299d75d2531a842aad42ff488b2b76112caf12bbd500d503f43dfd9708746bc 04dc77ff1925242237f287d9ccee87c27ebd75ac6e3a45c357d9bceecd937f63 ca3895a0d1560e67451efdfd8cafdc838c7f1fbc22a4504323ee47e7436ee49b 3a8185cf5f418483607610c29acc87d2b5b0eb5705a25252dc151d28168f65a5 7ccbafce5134beffb22c2906ab4008a62e784b46bd74ffe5bcb2386a990adcb9 f00cac90c0731f2c2eda3e61a2bbfa99255c22990d697a1008ce4a53d42a8600 3 c34e5cc292e6a45929650b358c9e4e06cdf8720eefcc1652e6e05f7d10fbdd06ab7fe0cf50b4de1cee7995ae250da3af0256ecde371d60098ed174437a2776064fe895c89b8e1cef0964745eb829dde215303abf16bd38d6152b195c89764f06a9443387b5a5a993d8a92afa46f8ad2dd6ecc9c71b0379ac7fe21e6a20163e0ee949ce04e30101f2a4776df1dc04af4a6d4665ad9469a54f2b9fcb948b696a024b6b6e0f1b60e7424b74b0b735e9232678c87274fd931ac18f7fc0064ad82a0c19a469a91a4cca00a4412dd423687ff6bd23410f4c9c2da14101cd249ce28c0467fdf5d519d4e5467e10346430d1d56bf7ff798b26e0810145428ae7621e0604994bea862bae4298b8fcf1bc25cf5f5501d6dd6f1d9aff1a9a642dbe001e45029f0248687977fc591ed17a30e572a019c5c939a892a76cc54be41d9bbf3781061ec3ffa5a58109a1d28896c4f5b2dfcfcebb48ac26452deee2102248ba46e1088ec88ba7a5a015c08d2f9f67b89b43e7f19fb1a6608d86e3094c48196d479d06653e53cfd9e2b415c0e2c39fb7b60ab1f72b6d682aa85981ff6bec05e36e8a0334c707b7c92e0b42aba055030938cb1e60c9c61e24a3ee2568b394da6ef1af0ac20735443c6033777ae285d8e239ec9009c0c1c18412889ea5a064c30359a5032701a66d6cdca2756438765e401205d833e397aab5b68da09ec63d3958994407116e2f18371d2493ee497c8add0e58e3c2a08389b0ff709eb6790a01c16ec40e4f93c285b621db15f45be3acdaa81eb61afa47b4b95017c303f7b1122f38630eb0c5d5694630f5533c8bd88d33c0aaa299f67e7c30ee766565208c428f51560fe55c533ea708f10a98fce6d7924e0c95ad8ab7f32975f2e36aa2bc69cd0ee200 -generate_ring_signature 8603bb45eb1799e1bc1fe6038b19a52bfb9ad7cc9cf44446f164acc3ca40da1d eea6a1e1451c63be7dc890f76883f56b1fe5b41921b4e9a5f7757b92df2b6a49 10 278124100668fd040c4da5aa9dcc1961b3e51c0c1c427757e00938d4584b9a72 1e7124d6beceb0fc37efdfb8e2098b24518f431bf8c0b1d5ebe553402c025b99 73f76c9a7b6cb8d8bc3996ce6fdaa00ef47e3c4c0af2fa6cbc794153d4ee0fc5 59bcb04b3649b9d1c6a697ef8465128d7ffa20ad00d4bc2f3651ea8893e75e17 8a996eeddd61f604a74434deba30fdc66efe9a92ec938471b11f88ded7365510 124691caca477ab72bd7b69ab0e991ed6f6d30e910f34997a8c826e5f9c3e502 e80acfb48cd74ae789a6591ec70de50049fdb9319c8b4cbd814e463113e97c23 4be725c440060881b1dfc58d506b5c16bdfdd539bfcf02ad5dea50748ca0350b 57f42b791c1ae5f5dbf20e303be722ea94fd993241ef302e94da5cb017802ba9 dcb72d6d16b55946296733c9638c60dff04002c51c55234878846bc1b6dd1864 f0dacee794abc5fbf38c017120a19c7e1c65637c65b4103fcb8c148af9aad201 4 f8b64affdb7ece73942627162170aded7a030ce093d1c90f986786156bda4b0a698140b00b2c50b8aa0b7fd5a8aaa2e3aaccb227093cc68d2ac59eceb0ce4d080744629f0b40bc96ddff73b312512b3e3886462fdc2c4efcfde9e74e0eff8f05b0a3ed6de5ec750cb68bef1a99c4dffd2bc3fa90a76496c3e890ec1fb54a1d043481b7d5961827c355e4e27dd79b67f03ce7d92075a368a0716dff52a35bec0bb54f00fcf2f317a7cf26243ec4764a90277d1396a9bf175e4503bad41d34900a954bd362cf87d97282c5662ec5c2c34db7ef9a2707cee69477956ea7ac99c408772cd1f86a4546d3716b7c0019a12398c06422da7f743c51d45bbde7f500af078f56333249798082fd5ba7178a6eb8f42f2a92b3ff781a4821425b96622e21033bcbb343d70594bdf46f722614b615fcd62ae9112ed56078a07d7f4719f1f306c049144c88327388bbe0088fb8af619213eee94c43b79c0a6cc6a330eba89a090a17a2a79a01313e701fc5d89bc5820462baf7233403117644bb167567ca930dce25f0a4dd3fd82f31bb60942cd73e9e49e17fb8e200acf57fec304b7509080f74bfea3a21626125b4b784610900713739222f7e73a2a547b6e26c991dbdfd0622f96f5e516a97a909d983ae811ad66a9a95134295d4765f83860ade5866b409098d5ceebfbf682dc288417b7aa06018f87fcf84bc9657af0289a1f4c99e240822b07d9ece9dd6134e026de189194270547847d85a323e48106aaba573230d0e6305e5674707af1ecd706d61fb3ec9d00adb297c70a7e49c1df9f36702c38b04956a41b2403a02c455691495ac183640746df4b3034182ddbf92c874fcc063011213dc8ad9a4bfd250103e98c73053de4d014ba146e8b92dfdcb0d2c7b49a500 -generate_ring_signature 393bd6d7e3d18366b43c7f64f94c7f3e803c05a357fd4c9ccab41f235545ee23 ad175b4b3efde3db1a37d5860d51d8c1e71bbdef25a0d88f29fdd6ed07309665 4 beb4f6cb5a63cfd76a7e66a78ed35d788947613b90e7bdc25ada761b87ea430a 0e37fe8e05c17b8ff67e4fb79a6849eae9631a41fccecca28618418746d0a9ea 087f54a5745a7fd38e45534fa0a878b582fd7d16e6705e0bd195d9c11ffe9638 3c91a5cb9c704c6f70c36cf483ff5dfb14b45958ee50b244de2f8c60e3931a82 c022e13c7fad4dc5e547e92919763f8276ba20a839ff4790f7423bfdbf60eb03 2 3a72a16440938e6dfb3fd9eb25859abc57b5dad2128a7d835bdfc78b131d840d968bc131d314a50a92cd05748666dce92f899bda29d8d80dd4590d792b42b00277cf912bd58034b9ee25d633da6958e3fbb312d129fccac46602aac8e7a1c202aedfb4a68eccc527813534ac26e1cc4df49281949e7dd8887cde8b4474ce340917cdfe0c21e51218202206c77a30b3a05583767605894b6cae1f21210800700d43fd3160d8140da1812f7a6b1e68c294d915ee30039c4f8c0cd4838812be760923bb6f5db6d4d89a2d8f78395b6961f972a47daf5af9bbfebe75f233e64ae106dd0199b0e92ef32e9b2a8d34e932000f06045674bd3c9a5f48b85ccf894e050d -generate_ring_signature e565abb91e365168b01e67f4a3c568d32789fdc1e6dab2f448e9f38d091a3d4e 9408b7dd8b3cad102645c2011cbfb0edf8b012cfde69948fa41745d9b2e0d375 1 353fd42821e9933571099b5e6cf214b0f20f3b6d012e753818641c618d20defa cbbbb2095f92ef10834a02629b43f7cdb9c44d638c6972b9030d793472a63207 0 090ccce3fb90269f460bf7fd200d55dbdd287c81fbb2e40c8403564e54736c0627389f1840c1ed4e9c063f2c9edadb6fab94ad7180df670f4d0ca30b9dc18402 -generate_ring_signature 7ca2998d7e7f0adc4ce35e5b474e47699d5460663aa16ab449609fbb00d500f8 7fc9c544c3e934782785e9f73fe032d96973111f922034ca48bc825cc27736c7 64 483168d096ddf7c014a148e53be2d680079bdf94ddccd86b087f8b33d357b7fd 86bdeea8a6d0fb1d4c7d5a173d5468a55f6787f612cc997039307ad56f2ec6f3 c60a76b900e4670551e578ad687fba402b25416433d72af5291ed69e630337ea caaacc734c97093c58dd64102a4246b2a5840c1c813c4e338394bbac7dbebc75 1ef3e4e217cce535fc694fd8c6d3e113914f0a519038690ce7324d06e4d1f8be 916111041980474203cd2afa9043cf5d9c3d636fec3498e1f6b06115b668bd1f a09a5afac52945be31fc7a2e7046862c8d4806649359fbf8b5f7c69628b4f167 6b5d1fdc1a962a295746d49938647583ea088de7f8e241497fc6b41bc9121e95 0400eb6d12f71290e0bf2e6af6ea14c37198a7e686b63e0569f6741844f0db28 235285f1e1e1a1bb6daf5820e3f9db2f5ebcf8a3c1dff2a988953d60d77c2c90 847ebd598b288b0052a82a35c58fee0e477556cad2bbf15dc60208ed14712b08 a8a54162f2a6e4fd177b3c727dcd2034643d6a43970f2bb9804cbb5f24366b5c 37fcdd2a3b035e5f5f4951d3ea120dabbdc219d9a6c385d03643a7037eb26af4 c3c68afaa4e7cc9c7cda7569cd3b0e880fc2b39e418383c4f7d6aede362acaee a14729c01c6f8c78c68f4863e384162a863d304e780cf1d87835369d6c0f4c42 ba8de594a2ebd411901147db78dce4c6b5dd35dd2c85ae6ce2e2f48f3e47c122 f9d0351c1c41aedcc6899d3c963c410b3e33510f747c1a3cce8ab1823a3414ac d8f4ac38ce7be2c45d3b74880338bba84e256042ddcdf30ff25089726edbcee9 4b33b840fe9c767bc2fc9b8f989aed3d8012edc993776b1d2476f6542ae15cdf a7b39b9014bde6702c0c7baf60a3245a70f8db5abac0fad30bf98c36d8b201a6 3905a5aa52ca2d61f49e770c53c6663687c2b6565736b51527b8332730d283b5 e9932651c0ae63cf3738fe067908dd46644f1d2235972e027beec99724d013f0 804f17dc20b27060756c20a2e3b1bcbc8fa01db1672c7aed51c20a868b0360bc 138c0ac27cb68eac5dd51b8f75d1c31a215f26b14445bcb3642fd00a176a6639 0f283696b934fbcf7ce30f78f8664d8f28c986a202a0c340b2d7d5782cc7bcee 36415f082045b4c3b1b0f805d2e6ade990723d39db0a53cdd723d10a255d41d6 16a9cec53d35cc2861126070e57d228c20f3350b93ea4b46d8b62d01f3209f20 a5c9112fefba9ad954476e87f670af6dd626898b3953a31cf3ffb57886f10d11 ca10bbb819ce71f840e69d53750dd43adf4cdc2c785e517c7edde54034a3baba b1f8df73868c6f39af5d6a71b16fae7c882cfa335115f62f0b093873c308d06d 557f9033e27f5ffa3268c4a3f3fc94dc73c2e3bc70908888c3644143503796b3 f2cad5e736f9839d99cf515859fb8464fece1f85450d5bd5558ce750e60e2942 9becaea439d210fd173a374e51586d125508c458ffb0eea0257416ae62813a81 a56164a43e06f4da3d2cfec1db2c1fbc2641b9f86dc4869e06e55f0a9ffcf751 c8a1abd87b2837247515c9736804424e235d61d6c41f90e748e72859446633e6 210074f11031e5ce0d2ac8cf51e9fc7981155fd1011745ef1e74b342672b453b 8fae668e0c5e30c77823534bb06c660867896f016907fa8b05f5a18452c7714f 0e1a32d6619800d7c2fd5873d124c917752f058318d43b66b2512aa1667c5151 6803d84d7b5a3574328f3403b2f4a79b5262aa31e6f1c971c179c4ec2c8560ab 44c61ae79c631193c330a66d64dab906514cbfec06c8bdf57d5cdb1844b2f789 4000fa60a283326fdf215ceffc849f6f58cd74b60bf19798cd6e54b6e3d8d06a a78e6293da0c64e26b788e55a0afbd8e4273a226a514cab6e3b4beb8d214c9ea 12980cd87170fc759380afa60290915389ff1929ab5de05f1e8549f014fb2b44 a1b83d3f280f162cc37feafd51e90cf6e1da6c82d25ca9ae12b4acbee46a949f 6cded174a9447758824a50352936a539d97c0977ecde4eb9df83c0244a44b89c e6a9d7f60b9d6f367befcfb0578605aefffe810cd55081232176f23cd4901ba1 13fa490eadd0b74699af3be95e1f5b2e488826a104779877768d5e10a506bdd5 5ee53f2482db49cd3373a9195fe2067e018a081189e9e0ba2500357fee9f11d7 0cec68b56dbdd3f61c5b5f242928af02ac0f1bdffc5461b5855fe58b582f54cc 6c17365304dfcf711494cfeeed75cf5e121ee6b59cd67219edaae7ea07db4250 6e801ef00a31b59ad896ab91c68c67ec24aac24b03436c7013c18df945666875 8326bacb4818132db5ca61d73897c1ec0769d9833b9ab677c42bfb9b177e0f09 2b460dbd272c99ec3ee39938db0555df0f23e8090da8e115a912e15d51aa913e c3cb48550117623033617d274569087b44d1da2dbc0257bbf45f35e3581b536d 110d6abeaf7b20668c3891fc61240bb2c3d67736f5e80361217156a55e142f5b 79e8696c0f50252f81b5f218eac91894ea15a95a48ca68594de8aafcae37d7e1 7308404a054e1005bdf9eddda7a5d0ac0b395efdeb81a655f4b8e8819bdc583a 38239bcf14fe4fdcb14d6943c464ca8cf401cdc46d3d212b4f1ee52161e3b4be 3a8bf4e796f2cd92a78240df064c3520f4966257b89e49ffa2ac6b4f70322a97 b4310494a59ac8bc4ccf06b17f70ddb789b387215fe2b73d180b3dc1779350ed 51275e0ac22a92d506d12e7969e28f060ee7fcdcaf100134af046d622ef7c8f9 d509aa1c71eaf0259e74c41d404c25ed03fbf75f249214b7525570eee1eefda7 388f6ce354823892c1cbe49d7fd79d8c62627308a48291af34e16bca292a6ac9 7831cb8fc935ac1eea198f48f6632475f8feebf686406eb00d43f4054299f8f3 d62eb32ea87d2f0efc686cf9d79d111ee668a65a48c3ae2606bee28d8dc54202 15 a333632662b109c834144757de67974f272b3aba9d1859bc2ef8c1134c63a502760c7379af1bafb7040420eabebbbd57d62386df763ed154f8083607473aac03dc69a24901d3a18d451fee65fe0f7a8164824e50405ee161280a6df87e817b054fabc2dbefed3afe6bed3115e43a7e5df306505b5ada2f1ab5b3187117ef990c83ff703ecb34cdbcb4699e39b286789762ed6d9999715df080a46ee815d9210997a83ef9369fecbef4daa6edc93fee2312b0282f3e31f4d91631dd74341f410a00b4b9900f54f9d7c232a56f2c5f15882b7cc9825d1804b104b34f1bf93810046d3f5e5500220d0b01824b4f849b3a7322ccf25596bd35e245d2d77f0595310f064bc5ad7e380fb7a22f43d2fe0c5361e5bd1ab523c488ac85b293cacbedcf0e21c988ffff0cd27c4c752473d51513364d741f43f3b2d4a6127be86099346f01c37cb274e95f79ab87c4e352e74aeccf91aed2b9eb2d2299fd70fe909f793104f60d22470d0705e2b698ee2185e1109606a0e5ed99c31d25ce85b7024179b50dd6a9d328f3dada191e642697d342337cfbea7d75d36a2bcd2d33aac7d7c35d0c6e82bd855cd1aca16e28a2512d14bc88f70386a215c3864be3949052fb93eb0e9ea6ddab0b83fb4233dde93d5829481e8f5d46fc3c16632eea27b14b5fe5bc0785e6d14be9490af7d2f4aca4de8364995c7c409c10d0a01659773d1b5221bf06386f77f50c1d2c76356c2b726dbba329099f0ae838a96cd5d464fe4a5b4ca901e7e0da04584bf2d484afd1599d4f9b0b568fac7c1a75210dbfc7e060d1c16e0c337d1b66b22a3a6c3f57a437b3cea7e8e5eaf9baf1812b0d771383796ca85f0ba136d301e4607f016eba758272d386c2f5276cb4268763210d1a4c31783fe30f624c783c8d22ad77ffe57ab95cbabc6dc3a3adffbcecc7fd847b37f4ea855b0c8c7da3c84f95e5ddcc6d0ae9443aa6427cf4fdd0d14e4b5c40b44b94145e9a0fa5e4b38b3532d2e1d85d9132ac9be44589f202d879455af8ef5b13e3f1ecb700f619bb90554201c6c11c1526304384a2120f8d4d54e8eb76c20a729ad261930fadad049fdd619f5f17521da59c45516cebddc632d3ac694fe9ced9af2266f20e55711593671b8dc1b26431dfc24ee1fc90fa4cb5b60d35c8ae06f7005af2fc037a6d458b86622353525fdbe333f05c7b760773d28eeee8abe96aeecf51b0e906b4989a676bc0b969da9a84ac6375c28d352a651ffa93dc068591bac94635e50862181ef497cb0df2140ca34294d19f91f3d2718c7869997ee00463d6a6a2f4031543d42df3eda9e21691b16ae19ac1c74a61da42eb8e59d5d642a7b471c5be029a0df5034099acc6da6ed818f28bf6aca8c75cd5fb755f54d557bd2d5757f20e934de129941549b7ba8b2a862d12d47f760b8009dae3eb23ed360194f540fc0be64f74cce7fbec163a29b34f0f08d4919b2f44ef9ae9aacf6c5d302a94fc440983ef2f059d0522caa3687373db330eab29cc72b82d72a4de2f2c593adce83502558b38226979c21e02b089b811f6622b72e379b3f7dd4546649fe8389b830b0736a2f9622422d62a5ae128162e05f62db8f38a33012d4c74481c02a0bb15610a3b7fb827bec033d8876dbb9d7ff72de78cad1c748008aeb1337e595eae13c20d26435bba69cfd5db7c25c18434dd251d2a27116d992022069385aa22584d47059b3bb38580429fc06ad2149ba9cbfb7d863b0d6657a1256c4c87e157443eec0d3ceb35d3e10666b4a18547fbdba0dc9365f584c364678c0207d32895f8903a0636a195be0d094a41d5d3507f11f1c815ca1cef7ce1a36100a8a8a9471d02f605e51c46fd906e4e76b207d71c3719660122f71d9ec1cfeb4960f139cd9a08f1011081b8cfeebcd41b353b9c08cf41065bc237a1f1232197926c09eef344a45c0d38910b056b455c149fad535e7c59e1389626c34174debd7f2db12ac45a93ce01eb8ded0a128780323f3054e2f1f2192a8ce55b447e5b9e1617c4b15148a05203ffefec27fb7cbe277e5632bff7a4f0437ff13c458fabd92e9cfdc01bddddc909a5d15510fda8a9224663746e1c4c366a7a270afaa3c54c090fa7e60e988ec70665fd9d8a09547c16ae57c688924add0840e6ac44128c08923fd5d57c3769f80923114ee0dff44b4b388402d581cb11af854053c667784465cbfb9f56133c4109824daf9338127f0152d659e61aeec034eb2c0247c486acaddecac0136979b30f7188637d847995cae9a41398c2c8986e8552b02841136d043b40d6828503440645a89b54af132ebb3c35e77289a8efc81c45bb54cef7366ba08e20f6db1fcd0871008bd048a0c311f104f350a1be8cf926164550b54de6cd9297eddae6172e018dac19ffb240e415a98d106544f2f1fe39ad079df179f79b4f93fbe644062c062593b29506cba09e5499fde8fe7de847c2b07f218086674f157d9fb52e7313046f9d6c8991150a8d1ccf7a77021a84c877566096261fad8f616a411684be9002b38070b0d557dfc107b728294b4d30b8e83bb907b50628c8b4282cbde0314f0f15ac98e382257c5eaf2086e588725d27f0ccc6956d46710ecd425274276c360281a6ed410a83ad45584ca08285a6ce01196d8e8a745d0ffa084a2c3a64490308fb8761db21acf32596291fc4c88c68604542d3b0b3863a53bc5327631c5af10887d68802ec663ef019538437c3a460ccbb17ddcabc9ab16bef28079e39c94b0b00295b9ebbea216810fc2b81c0a508294ced8e679270645e01c7acea8c528f0373fe48957e5e1c1ee6c29b255d36d8a70a9947c4bf5856889287f93af6b6bb0b70f65a31e00f547f218dc194690fee18c6487badc8adc9b29bb87d951a194a09e7b281b0f644b20b1ce8603ab8e7b2a9a08ddbeb9d7029c0922d3984875e3a03b4a654a21cb4670ad5417fdfabe083a6c514d2543604fae8cf0805f3025f73088225315c5a55202c1538a420509134caa7ca8b8c1efca3f9c7120a7ca675210a3e7aa7dde18781f1f749a1ec811aa4e9161faca2a0841f63b3eb3f1ee653000916f287ee81c0398b62ae7adef993e8c5041ef803b1ca8549bfbcc1246c6b000b9e6b4ce81c128f04a95f7e13069b576a62cef7056b0c493e8bed5a6ae9491304c7bbb98886a9043564f07ca460f7928a4105223e89d45ba5818246c752803e044b9802c16b6998b582d56c34f783673958d8cc918a5aa9c5c5915aa30c647801dd523b0fbe07e1b5c7d53a2f341e942c6a368eee64f6e2e0d4d3f7c75893bf04c4093d346155491f6e8c8b89eb55caf64a076cc6ea8085f9c7e7a5ed6c419609d22b80e81a3a2a943319cc58e32e27906c2dcaff6040730338d558d4aedcd509ff25243e4b54527a130553b959acf977c6eb9b389d4233203d67a0a12d1ca10fb54823621ebba093cff9d029916d9c4ba6fa1eded597725708428d7b12586e0f1fa030222224dca5380356cb7ca4c290c13f3f1c779734310164f393e50121036cb61b53e2d32a5bd21a2574c19a1cda83db8c940ba7e1753035afd7e1f7900667948b6abfbffe8a33d3a542aac78c651a1e59665bdbaf32a53f39e136de3b0107dd52481b55eca80a102c57e8a7df29e2f07dfd16eb079e9c2970e1df447204110efa43bccfce4b95dff75e00df256c112ff419a99355774e3efc7b48eea400fba35a8555c0da7486bed9069b0aea8d9e1aaf9130af2588255d5f8c88480d08052f4577645d83f1e845055d2a9cf7c84ce40fe2fe6ac1dc34ed323b3d185403f84de40cb52a01813be3b3783662ee51c07c7e49829e8d15f553c2d56a960f06478cfd28f06379f02111dcf2167073b20f88a4b80c4fbe24844f4e4748e70b0816639950a6c55e50e17b8ee0f507f15bb4ec5c7a7eb81cf40bee7e248fd8d30b5f7480e9ad4b5010d129995706e427b62030dd41dfcbdb256e7043aefa04f5052335b5c2cd550c4ab1dcd9b263768052f1f2a72fc2f5286f1299861278744e0afc41fb83d88fc713b1837ad301da8a0b8f812e0ee2320d278fc42273ab8b7906909f79d7b3cbadd9bfd7537c154a5db6ee8c50f95b6fe85eca0a9dbeaf50850fd86c21abc867919520deb226bb4f3f07ba445cbcb1e0796fe674ecae3c54c5032c8b5c34154bfd064aed8e986c9a94f8aabb5ae6d9ce2fff397a61dbccc52107e7690c850c50bd04017afb9c3abb073ed9a6dc557b3bc132a0ff03f7161fef06939a11e827c067e5a75fe793b3706ce75bb08c94d116ad8a74777ff4200a7f019b1439dad0fb9cb761393e919084dc1feeab33fc3567366f295c1ac295710f0b435b78a13b79b0b2cbb7c76803a445393671f1d5611fe5deba74afb262bcd80e63c779f2a35685e8d3e716761a27b53730664ae360bacc0243826315fd6ef807826bde8620f6529f8c603f981f8bde830f5865ad7cfe1b437124607a1d2eda018b6edabcb9ca579d6c47cb69837bbb7c84e57118434fe4ab78147b683686fd08fd9607303565e8b3b9354999917ef1ff0863f73212e8fb93ab70514aa521ae0dc659c43aa1bcb919680a149b5644067e403a699831474be553837228348f5f0ea65c06f4a305701962fa34167218a1b615045997b4572c6428a3eb86a1a3580629e100f493883ba229718447a3ee62b13948c0963458acfe95daa9f7ae78580fc496151c936d9549074bf049d6caa62e34130dae14af4799c844d3eb58a97007854ce2825cadfb50c5b46117fb305747af7a2bff971aeca767aee869558f55012b46a8c332aad6410320e09202e3f77a7548a3a896f3a8f9b08af406015b7b0fb7a4d2bd5cf904326c03650e9d4b6658fb8f9eb048cd6b64e462c4c51fa4ae003ab6c7a3a9c2f1bf8b4682f589890ace121c6d245eedad0e52330924e4b73404567949fb220dac572da95359022b0c0f4462a3b979d5e95ea8359a45cd40dd04d5d83155c1891333e6fdaa65da29ff050c4c3321d2ba039300e2ed5e77cd230aeb7eb38d4c6b8448ee303601a85f6179c5cb166fb668057073982c33cebb0d0b9f8fcd338abaec4bc0969727b4707a92b105fef3ce1135616bbf1b02b70eda06fad46d17522dff516de9ab3aec2341ffec1cb1920d75e168e13db8b8c12e3109561f6c4d3daa129bb3f63d0a0b14fb29d4354ceb039e5f81175161572d3a5b099d744a8808bab5ef03bfc77cf7ccc28b0a6082794d43b2f7166ab55fe060a6013b5b4c3fd50e374c96344245141520861e8925f782691565ad55b4a12546d20dcf026a39a9550d41d8293103068bd890c9c379ff780591b18d48c90091f05e07eb783b7216f08dc6e3069fc5396b3e4dede2b2c8e6f5e2293033ab011f672e0f11bf67f011d7ab70ba2bc2e404d69d924d7ec4a2efd01040c46fb62484ce04000316cb029af783d17f4fba05c762562d6eaab4f239ccd2971c9fc14d19fdf307ff92183dcb74caf6a7a8332dd851e602a2aa0d32bf9ba0048a265da63c1e7d0331fc0d5a30fd339ed82fe677706d886641935399922fd7fe721c3f29c955e10b3a3cbd728b15e5ebb6e61c69c79e228db75b84fbfadc91c73eba6f81a2b734031774979b2d72e7c0f41d22340e370d7ae24e2e8cc893688122d5f6305b485b0161adca3722e75560941c848b03eb96d14c4c5a3782c8cf9a361cdf02b73d070fc5e3465ce8067c827b4738e8c2ec7bd328a5117c5a917db0e8eff06c3d5af905048b9551ce371d26d48af1d8f7577377984948822e6fbb212165cef59568c607 -generate_ring_signature 4e6823185a8771ea63ca39e1aa4dbb7578a6fba3da566cea485c82b3c0efc115 0f0ffd85c0de4993b1c69e5b0c21a3811b49f6c0f4097a81e33448c1149afaef 2 cad1c5a19a24b36c12ba2812652309ba4234f04ca20812ef2d76405d7740afe2 4ff3063f234d5d7b4717b2c2b9e4c09966f69f1c1083099420e37baf84d8c9ba 071ae9ca27b1072d58abfc39b4bf143d61845202748c25189525172e1d081b0e 0 a99e4e3910315f33c42dd531f1951d5a36776284f55bec8714a6c6535bb28a07093aaf0ad3fc8d9c9d090e00e51edbafddead4f56f9b6ccc19b477b91daea306846c639bb01eb2bd1355339693445a147ebb57dfa9a8f3bb17e00201c1a8f70a80762b98f6e108ffff98800aec6d9f3cc1ca98d9af0e4615d37387b3bd2b6708 -generate_ring_signature 91cbc9e360956a6ea91cb542dc70d6cec885b8e2a1b763a3e352389041be1aa4 077849a2880ed750d786a1f1f0a17cc01c256f896373c465779e5154b3f7d955 113 1f4bdae2817d67f27dc0ceaf976717df975fb8353daf7fa01fff17ae37770146 fdd656b891d9e758069f81b2b2daf433fc84b3dc8eeefd2d295b7f5ab9d75c92 0859b4fd8c367923f1061c416b7e8e9ebb99e3829e50d8d21fa659ca4078330f 7e2da8ad81daf87ae03a1f42ccad8d8f3c8e1d4f75dd89416119b1c71e8a2416 c32fdedfa2464148a62c7630da8f598ff39f13628b221142f7f2d5b61fd5efaf 76737534abb4dfdc6ba72c40f3b406f9cec3a51fba44b56284fc62db05394e79 e4e6d4b71a8e963a256a300e4ecd265dadfc6ffb1fb7c46a68b9badf18e3e4d0 b4ce84dd04dc6ccc4b85eee0925825b4436237ec68b324f66f5f9684de4f66ba b1c379e91b9b72abdf75c4d25174bf029926cfb3498beb075586c9ae3cc105e7 2267492b2a3131012bdf68b84d681f03824aafbba96b2b9735343abbd74b8cb6 00ae5b1f0c470c254facb1b12ba8ac6a31191cdd01709d2ee6b37c1caee74cd5 bd859849cad2bb97e2da3908dd8b90578455599705674fc0016ff46c3549fea7 bb21060aae8d8503dd930512d7a080a6828223b5fcabda0c161ec3ecaae3fefc 66bb5f6b1c081e0393d2224ea522cf385115dc03b1970c2e7e3a5c72d8e7bc50 33cc10d158229e8603dc7895a81a8b6c55edecf6f43a506625074261d402fdc9 43a36814c2c72f6a273fac7094f9b00124e0bbd332b9ee61ca41ea231b018816 1d7f55694bfd332d08164d331fd276e56d365f2a7c5fcd3824c77a6809b65385 c2eefbf1779a3dc88c493b6512ac8d2393b2a65f195681baa41bb05e2e58f689 981ed644db662419628670953465ebe2ac3997ebb4d8292b24d2ddff64fdcd86 123dd138e0f62fad60aa9656d484aec34ad87fa623b69ca4f77f36b5b903d71d 29976e6ae7245518b0565907725a15e1445bd1c0730b2c98433c47e13f34e4ad 541da01c43ea8445433cf70817e7fccefa13245c1d6ff34ada5324ed8dad3d04 82a982aae68c079dedcd3455d266bcef67e4d39028afa0df883dfdb4b36ce979 7561dd51d46d617281a314a4e112228a6abc1ec8ae26f59f5d691cccbd96663e afa6f7640f981e3d4ed2b85b74151061aed19ff5390c0ab3791cccc1a21ede59 54c84b57a8749841027cdb4555f8a8c03dd9763957d801efe96096ce6a58a1ee d4186b3f8ab4c4fb9256161f632c9fbae63912909c22636357b001ff34486de3 e845c35b7b319a99099f122f0acea3e39e82bf536c9dc0a7fe5875d6b0739c74 faf9769154330257db19465d7ae0f2f211f730e4c83220c48d7648596e91aba5 3bd9d7b85fc7ae7d7bece942d0ead33b882d0c35d96d491eaebcd6fce5f345da 1cdbd85712849004e3e4069802d7387ef46a4816ffb84fc626d5c1b9b7a60a23 d284ec26e2df785de3542990946152a736f729a787913dafe58f8745b0d263db d23bd5efd82b47c8d982ed9e1b4dedd39a3ee3badfee703dcc33d3c32b2075d0 f1eb248b21f1a5abe42327d1f203121a0a113fb1cfd61281e639aca1a15798a7 dc124febbf2b8b75ad19408ba905ec2660f8641608048d4053b137c18118285f ff0a4193b301b36562d8c1ffab7bbb9c62834b0116a48b992a85c1ebea6b0a81 8ebd75cea0d1f657d506b3cbdb855a9db21f87d8e0cbef42849383f093b7eaa0 9e254d83820a33fd0971a3c7efe2ca64c35a3d5d50ac3884bbdf7d6f77d7dd5f d2a69bb8e79306446c6b2cda1ad9c3d1bb571720b43e55491abb505b6aed4f6d b60e0fb3e1013aa0c1d196cb9d2fce8c1f201c0a013e6634fde756f6023c36a7 262e4efc7730fa981014d1567de9d429eda860bfee22ede1c9941ef94aaf2159 8a40d305e04999c66638cfb169b2a991c0995c814ad978eb9f1e951bb3465c40 088739da61d37ad97d467ec8bd7ca4139b311f7f8b6cea6469aa4e8a4972ba51 6874ee85e75171ed81a59ee5030a8f5af72373719a8faec2680c00af75fbe0ee 0ec37865399810603eeb33048982fd83b25e9e1fcbeaa91c0ed06fccc47c3f80 5ea898f527a48bf7a4fddf87f26d13637557b6214da4eb1b0e4e0495111f9f36 b1dae8a393bf959ccf33a86ce48214f4250458661892fd8bcbcbed768f792548 25a26575e7b663bc898396ed9ccb961e9bcb00d107f0662edba72d2697a78d4d 927ac8a2052c2635eabe2602f67d1e024ff0f04dda5a09a1abe3475cff9b4f78 ef6c5437357e0b31c6a3ddc3474ce246f49a669ce06acc00232a4cb170e0b7ac fa6ec2d86e6f12a008a2415f6bb6d62d8f4530b7ee264e6335421b464033b6f1 77027c9a7b41d51ec9851dd80e52221d541330c4aa65ae9604bb924874dce56b 6229ab07ca58513e9a5fc1db57e4c1bef34e4007206e440b26707594b3cf88c7 137f3bea2550d2307d8a733cd7ccc1c6f80fd9b4f307b75583193c826805588d 48f0e380c36fa89f755054572914efcc6ccb415d77348416a33d5512f8376380 4f5e4429027959682771f088b7101e1cb07d0183052627e44191ab65a4d11e98 d40ec8d941cc48b7edf706b778d736183e05856d03fd929adac4eebf3268cb68 8bfda414414ad854211292ba745ee96f200c80e3623506fb8525f20fb2271fc3 1a615b1e5534445656037b118b8b7bd13ae02ae23436b4ee338d92d37c3d4896 9bc163d094d4f87154cf3593c1edb359a903dfe7509524d9335623a29c656663 eac712841f6694776756f6a89d043bb062861a4378cfb13af76d301c71cbc6ed 7ef33bfc75e86390bc080acbe35bbbad5ddc51da165009bd0daa10db962ba996 054e6574ef55c9f9e94207ec107352a0dccc240ef20c033dd6d1182f7f62909b c32880fa3ce928700ac24530ead3571072e12b03a49b26ef6b3d35d0f4c59d53 b98d0748298b80029c901eb4ec4cd8a436e83c7e01f440c786fd16e15107ac49 ebb0e6eacbc9c5f35484faf27ecacac6cc694ef4b09d807029ae6f510efdcda0 b490c1cb481c3ae08a73f63a1102b316d286d93852adf5648cb0dcb6818126d0 d4f4da3f84eec9c39a4c7cfe27a3bcd39cb9f291005614f1487e0f45a4f1ec00 aebf45b322432696c7b67d3cfc0aee50ff8e40c51ce8cae8a626812449d3a54d f606c7fa4e43f558fdcd80fe7a73dcbb249056b368f5a44f3dbcb9324b503c34 106f85af18b3197ad3351fa8fd8425d260e7aa0c05918af757bbd3361bfd69d8 32256ba58423e1ea10dd4f501a5d238f31499624872406fe500f498e66d008b4 d292d6bd8bf7432aaf9c61d7dc96c73f0dae8132673e85fd3aac5fac1a93431e 52b24f1118795f20e46b90c1b047ee5ee2d3652ab2bce842a9cec609fa62aefa 449fe393def04380cea7cc4c3db08cebf0482f2ff57127ffcd987d92c3fa6163 7a8d3c4d6eb1516f2fe284e496574b3f189b5015a489fc50e54a9f414dcc1b0b b39077b6c733c1de5db7ff8fb3873f024b40fca72f9cac1fc08af28b18e1b932 0b203cae9dada2034024effca44f99b52a36ed00a2f4b67f03a4e0ef5b7c5dcb bb56bf108fb574114fc98c31e713ddc445cbbeab6b01d4e568dff3bd169fbe2a 059819ee8457141574c6dda4edeecda5e62993b800bb2148b16529bf6c0a7d13 79688125c0e6b72e56ea7872416735714947f586635bea64120945fa37cc59e4 5adb1c6cc2ad2431222dd58e8a6f3571063b3ef20bb123e3a1071fcc60ecc9b1 cc3dc8f1ffec0e5f45293b94a5a7bf3ae58a4d4f572a9f5ba8ec3b4f34138369 3999f2af3d71f38b14e98dc84e2530f037e83e7394bced0b6d681ac73029e563 67652807e4ed16e014757a70974b4f8db9e6f6bfae39fd0614ad1f06e525c081 d3813e43348dd6866a69fe1755162f250e8e3de00b7ff0060cf6475d2445836d 438dc90ded371c003c1038b170b925cecb796c3074ccbebcb98e3168af4d39c9 740cdd1999f72c2a29922d6ea744085a4d9f104636ea931643943ffe673fb15e 585fbe2386bebe0fa46eca5ac524a3cf4665498c01929b98854634b0a0d113c3 de3706dcad8c44c755bd56ec422c99168fb5f8683866a13d7cf8c2145a49ba38 da4157d8f82d2a54744daed564c987c996120211b30d482fc0bed011e8f94fd9 721a665b9b6194d077108c0797031347aa6b2d6183812e890ead8f7ede04fa99 67cd0f8158601c13c2f85d32fca57f324985a20c4766cca265693c77d87d5143 9e8803213f74d6e5869fb505e8309245889a807a48327769b8476f16992a8198 eb535a0a39ee12899ad81b8327bb17194757e760e134a0bd34a38ca32b7fc9cd 1a94815e92a49195b30d5c6d08c6011f615a876d9f84562d13e20c8796bbf944 ac695022107503164727535604598ba50cfe2c8d2555345948521d94851caf8c 4819c71ab8e12d613df31c3e4302178dafdc21ae33baf2ee3a9f2b161aadd640 c5b8a211af4dc95ca5ddb7554270215cbdec87c34dde54c484e1bf8ed7a5500e 375f63727c1dedae8a51f8424156f97f9ee3b54301423c8be049492638476587 cbe6d761149db8e9231283d7d3e27b6bb107068127e49ab0ea318ae47a52d941 d78379ac13a4e4e05a8ff9cf451d22fcc16d17bf4b06f66c55f8da8584b08fab 6edfaf19c5dc75df69facf72eb82bc2c016b2650f32c4afb731f7f3fdcdadaff 44d3fe29429f69fec91ae3e2e33b227fdc00a8ab78e3510b2bf03322f20683a1 a859389fd668d9b8a8a5fcbd3ad6c49c751032078462fdf6aac4e5600b8bd37e 6808fb004f96d638abd7d164167fff230265214723a7b65344011667814ef5c1 6c3654047c65a6312b5f785a9246e064f1d223913b7e11db00bba27b8d6e728f f1383ffe364151b77625632904f34bda76a33df6561c30d591c30cc492b0fc41 6cabd4505bd1a98ff5f7c1c75d7d4e328d691510f4a23ab74098c3b54a08ac03 c4818a93d8e531cd4534694bf915b7bd5d65baf66976fea183e3df1ba69b65ed 4ed680ad71299f0ee843625af25736e020ed7bcf82ee266c0e34537b1cbbecdb c8a92d1bf5112f1781f2a7a528b196a3bf4ad426a48c30174be6ded892be8be5 6ad5557af5108ef9b99735168c733031ca356c49e9a4cd26bc6f3a332d9ec219 5c0ecde6ffe8646985d4552fc134f6d013b984474f29e62d6626dc941c23cb0a 31 6385a2b5033b9b43d35d5efb16e3b6840fc969452b509aae79a4a3e943bfbc0b7f1b4a1568a8f259400fc897ad6e4a2784d709a394d44e0920a0c44956330f03aad95900d0e6a4a847b2fe2f0bcfd1691ea0dd1e9c07f951e0327789e0ebaf076c4d206e1a46cf10afc929d4a69e7869ee1520b5e5eec5ac1eb23393615bff0ddca0780b25ca9562a6bf8bfebe04210a365afb51189c397fe83d63ccb4d2860ba6b43534eff3cb34daa96981c0ac86644bc9290ea47032c7b560f5f390acdc0dfc7d81dcc4658febe025e5cf95f5f2dbb8d7399279189346a75ba5bf4d6334010883701d18c54fe5e01338e2d6711ba9c29b4e146f69012f5200f88ebbf7840dca4f1b7bfd71f7e666ad17152b46e5cde06f929c5abe28db8cd2204d648e8800dd4a0c2a67cbca4ba6a61e25f97bee017460fb7f47c738bece161b9eae3a960ca05638b0ac6b4e746c2ac043c3499d6d523a1aebe5177b0018f54140cb8cda0d6d83b9220e2e809b1d8f9cbf5d5553a84de9ae86078614a0f574cbbc009b100a9794309b25dd619c98874f1ff133d8e6ca66bf09608c827b10fd6e42b94e4d022ed1bcf20be54f06f90e9906c1a2e0f1c22a4155d40a1adfb6caad724a9a24013ffb86711c3a195ad78f1127c7681b74b77ce99e66cf275536f4dc844a46590209f51d62433521f949cf143cac1b8148be44eca20e52b7c92a26d664af842c04e201cb7cb30c07b219853f75ecb47b579bfa7ae3f94c7550eac063e928db070f8370f06de67b5fabb73096ddf3da41d0c6f9b961797f3c791214c3fa234b5905c36488531b0ca89912a16f1f283f431789ec12691f409e06d862a5b8c47e2e03cc8dbd70b23379531a87cee403a6181789062912c6c302333c477644d987b20221768c39beb0e6453dff36875f26baa630ccac287bbe4a1fe86491b43d5a7b093e42003ecd73751c39e1a0aa4eb34a40cb635c4a6596783abf109ebc0680f10f83126ad88122f44223ffc737238f5040d6eb7d739919be413d7e2f82fc6e7e0abe29291ba715a2fd0c258275b77e050adb14f4e0585b8ed0c7e0359ce85c9805d604d03521dca95faed1b123fadbd90fb2c8d9007b8de02e1f519758aedc9c08ff5cf64251d6c7f6e7264288294ba4313d081152979a54fe5d1493295222430bd20133342177957d7626014b57ebeab7300faf53a10731e9de7a4e0372b1860446a26d53ce5a0a04bf18870dabb872d4df5ad06149a0f50917e2113f4adb520f6adc81be64c0e06dc615dd28e2afaf50a8f99bd4ad23025d065998ee4dfbe20f428c3109d77a5dbbac52f4c609c8c9a6da00ab052bbd8a4b901acc990426160ecbba9cd03a35da8d39649722acc5e9049c06ae93e81a6cfee7410dca76ffbf0ecac5fb11654925292d7d339618ee4e6783d3cdc16566f0f8c1d15f9ae00ec20d9b97e252f785c71929c5c9258a0ffcad21bfcd6cbddb5157dce90acbc50b2d07c0ea7700280055e65d1fb56edc5e6615d31cb0ef56f103a417d4025d225a8f056defc8a5ab783a47266607de5e78604ac3baaa88d89cc8ff05d0763b85bd8b0f177917e985c14b51f6e33aeb97cce5828250b4834d5df42fab0b3ad5fa3b7706140c130e40f26e8f0b07448ccdfa2c7d012d3c0dd267f0da542ab9413f614c011bacd85de612ed8d475fd0c45d40dbbb6ba123f20402933e5c61d83c07fd5104bf08de016c5239eddcb1cc728e2d685ff28ddcdcd0ebd7eb1c2e69ecf6736a0c653a044a8505cbbb42fab5ae6911bd42af399abbb9b77e3554bd2dc2df08930b0a3d835eba3770038e5f2931b003429646a14b97648a2a951e430a7ee4a0fa032663abc7746927e9a14a5605cf7f883f409c7dcefcea97d45204a1c5e8bb010e1817e4faca2d6519b9316836379747267d840a750eed44ce1e377440500c4505b5e08756ebcb79c22b3617a05cf278f8bee3c06b10741ceb29689c079844c0038565c6a96e5a9572afca53fa0ae82af872d901ece8e51c394a39fa7eb9806e0607c670426bb1474ac60bd1ffc6c841a00b2530dcb7aefe58ac4bd84953772f0adaf7a65f0bd15bd6ce9a94085174fd75c6a7c58787fe33132b52ae46174bd4007a65e14fa380b8ef26f5afbce7b3464625515fc72d7084d44046388bbe91a2036ec51078daabbbd7eb8c34ad4ffe772e1560f1ecf498b5198a594d7924d313089f3735fa05ffbd72e1113cb5ff1fb5e7d534677eaf624b1de444ddf8d969f60dd98af763bcf7f001dbae6d81a01895d7a51d097982bf4b4ea70ea5d60bccae0ebb7b1e9aa423dbe0387cae5fc23d56975e1a095e956004ac195257b0ac7b6c0c72ed294963552aed3d8ee825cc1ee1b14fbb367ad5d74c14f170bd59dcfcd107f1f97cd7f9830f8ff29ef0f7c473af62a650997a18ae31570756d7a3f1f02b09505e23944772c0c211807a2f0aea7d7b1e6bbc48e124a4de74b7dfe52d41680af7ddd077ecb1211d28385c9f439cd0eedb3c1d22b4ad2e9aea3fa0a6e46fa80978068aebf2544a24f8a8d7d42b60d40963af6c711984811b9ea15121c0c0e10e81b67134c7892b2b9ff906578f4d9b15e548751591d2900b6972d691aa027402a4ecbfcef7d92d97c25eebbf613bd7d488afa4fb86ad1a52df12408651aaf8088a5dd7fa8dffdc830378fa77a5f0b2494ff14ff715b1662d2c2d2b3e41ff75021e4534a8ee6eed9ce7bd4316264aee34586883227a3e49f8615efdce701b3a0c04358c5f7ff5edf26fb2aa906ad1785ab2e3acf3be54e23313995dc74a70350a76c4bc19df287dafe2d7f257c56ac3836e5ec3b27a1e9bf1520fc9cd634d2b08b1c2bf5a4511b6ee79d7f66ed7623627eb57929ddc507275bc6411a12dbc1308dfb48b8699ba0ed77313a30101bd66f732d8da39b6b7bd276ab6bcea9fe04306fe06d14152b8979775699990eed5bca3e5c95ce916dc22e279318568b9fdfc03e9abb7cb1a65b6ffa1c8fcdf03a264487df8e63efb32742c5e26a3eb66005c0834bd512c14f3e4898888070a065814595f19af5e5811c784dc78280c28d96a0a88aa65445d7675559d5ea59913f4f8f622bc8a15c2b4a1f74454b8df39f7540b4200397ec2d18a52bc22f78e80481044b037bd3c7d58f066f63e9144283a2905a65c57cdd8a3e3fe2581c7ec4a013e3c9bf44c2adfacd661a6fc2ce4b0c0940ffed569d437e4baee3e228484c28109bdd878a402964b226a446fe3d5fbf7af047ab52cd941a8230d73ec0d718117d6a5b7ee9ccf75e37f7e91f6ec218e66350e1d979a297ca386fde3edb6339e58c5ca546312012542857ddc2fd88c8c506906ea906f293f7c2491fe1f418b6f51d6b868e13e85f6d603cd48a79f1a27c4b402b337eb2a5820c54e7437f06facf1c4300d2ad086c4f84befc05e9d69a653710535078205a48ed49bd8bb063b5da10393fd501a6a1fe3d07e8b86ddf1fa11d00a17cca8c0d1775bfc56229e45fedc1723b9547cb2b296980b2dd42b58e1b39c09520064634c52ceec4e7e1ba80eb2ae8e3c4d51fc47785d9f382982347ede500ea2ea308c834bfab1068a7798810c29b5f2e5221e68160a6905424ebf4fb8b7037130eddf2d07014921b3d6d8db50f0868fba03fb4dbd724484994885e67c3a02e7145b0e55f57afde954094db28c9c6760009d629aa234e3ae0a7ff98579e30182513d104f668545bbab88c95832c2ae20f6b6aa7ccaa3cf99764ac3065afc0e588f4fd18582ea7252fce172cbc15b8153ddc538a4bd9692cbcba1f2f3eacc0e4103e4b16b7ab718b2f6e98368db669e9d9f36cdaa0c77fc78ba4885074b830318828d27c0d297023d4b4584b52055278d11cf4d4e948ef04bd4256ec7eacd09676d04ecf25a0d090a3b0bf930ab97b510013327b8b2612626648181ec2d5b0eeba57a690de924fd8c3a7a399a2c1c5fd6a8dd693e754e0e2e581deb2b92a708cec9a2afd01a110b2b22766d21a8fb3e7f7b751cd8e5e40a149895a89cf6920fe5d2c620a8eec7be1b57db4ff09c5ce1951dde409421ab713673d28439ba3e07bd8cfb78d21198edc9721b78d2adc7f566da02ed9595584c58dea46dc0f006066e30ac7ff056c7e2df7ac94929fcc05db096043d1ecafa6a45b5d8ded06d1605a8d16c8c27028d5ed28d0e0352291145eec9965da61da9acee89720962d5260ab1c08f8dce882b3168ba3f0928204899338d7228fa61b6faf2211243a186d90819edd992e132e347a81b396cfd302fed80c2d24ae7a06cdefc243d7435790a0ab972a25926cdf6a72f3e60631565aa9d347a64c6a0200b2550aa4e59f3b52d01f464aae7c1f2d3e93105cfb6d8beeeb384992a16894dadd5f003787a1ce11f0adc3ca244e4303d6b95fbd0ce0ae7f397b7c4b0110b3fecc50b687d08ce2d4c046383fb09144e9c9f171e2a17333cd6960ded59aa9325f199f953e5e8dd1c1e06f16ee645c56bc312ae0a348ab802eff03547a0292ec73aa45dc2583bca96ed0dc6f493fc33378d886c3dfafd314ec2000f8cfb657b706a0356f8957b6acf11055b9be40b2ddcc8ac81fd8a3fb440c2cbf67b61296a2428741c140736cc770704e96c3fabffa9aafd1d5d8540431c5312379859a839c23f429ef1122e283793033de9b75fee2e685590e4e4149f50040b736b049d07b3a8801545a41fd1b0140a12cf6c833ea3f6ca3065e9ef492a0960e54fa5ee4ab1cac89d26b406f2b2ba0ff2a34ca1dd3b9c16a7faacb534bfd06079ed992d8bf11cd9150eb5872ef5c40087f6de64cf9c491a9d7d96cb43f0fe96edee841cc30c163e2e1ac225faa9cf08c72f1769a780ef7a314055d2d93ac671c7281470d8517c9179e8ab4577844a05db273f000e91c8cd2df555159d6d1922caf25954a673c05262408fd4612f410a7d7e1e9a5c66b57869f612b28193ea1c95cff6b6dbd438581adda3f3f8b097015a6fd6642b0f1903dbf3bde8e901d432ea546ff95a2977441a221670a2f1e30466399271f64417efe20e1100a91f5982cdb91a2f4c43128ea12ebd541d9d1d051923905fc96c3e42aaa4b0b27779f9919a7966ec9122fe7dd4c5107b8b5e230491530628d82940be870f4267438d02417fade252023ceef3a605635316c239079ce256d52a662f0f581fdca98521bb99dbe83082b7f60cd73877eb13538ef80bad30de72c4c58f7105b9c19947e4db119fa0481bc82e5bf068f152a74270830f5ca7afd1534990f6a3bd920b1ac84c4b24ae08ac57000ce59a7d4d3dedd1310e77bf6afb2338563039be41bfc237e645fb6d22d62db7b37334aa1c291e876b0cd5b8288e339552c5591554c0aaf45301d99fbbf8bbb1c5cb97e2ab361468390d9eafd01110e51a1e22bf72c64513c985a59e36e614b3a1bd18672e4307b6260b4d79538e20dac5d50c40617608ff0f5e205e1035707af5f55236d8afff0cdd0b0061023637570926a2e58b4fd08bbe0fe32b6c403f837d5affe5d995644b1209c71939fff7e2f89f6cb8443403d7a3cf46b683498f6cc7c216609caf627f7307127c816036cb239c856f5e33c9ea8ebd4a40fd5b1c2d4bc6ef8c1c334ae54702002e5cf28a5c64819bdf516eed479adcba165abd00ee032df3be280aa19bfc0c515c9e6e17709d2955fffffdbb1c0a17dd5f952517222f2f6cf6fa5f804e2603137f674e2e6261d6d9a9d57f2d1270dc49967a3718774fe8d65c564aa0ba8c05b4b579bff9c9181ae9d03f7604d31e87914cdc4bc9029acd943300fb4330bc027b25491669aa99250578874fc4159bf839a2e1d5bbe19047032a8e40902b110f3de1f863fe026b7c38f8442eb29f72f151ea6e1d4e885d3640c2333c731764089c17838fd51a9a8c6124ece27e55983937a3ed94d9c4446b6b2fdc5d17621a03ae2d322b3509b7df40bab4afe9103ef4a8a73345b2b959f0d54bfeb1fef66f06878cce546ede1938cc65d43d697694b05c6bc299fc247374cb202e2f75157e0b635ba2e003d48faeb4f8a981c21b93180a05bac8362089b9c3baab1c021b4e0029c2250b79edc82255cdec523dd63d18789da4d1287298b7d33c84f5f81c0d0f1f0a24462d1b254f20e6e3586ff0f7278e0cda790e6fe5208753f0f10b1ce50666b7e28cf0e6e99924f366737ea7e3d0bddb5d6bfdf8cb85d2ab78d8eb72fb078b8d7518f20d345b45d8f61143e3c15694a9abb02f213a80fc9c2e1628c06b061ffcfc52678d707ffbaf3aa91b9f06687ae14fabb9ac62ce89a9cf470b982607dde67839485e77b57ed5b62ffed5da32f66498eb1310a43954f0b36629e7f1064b159d1c274f68169f93260f19ce102861534f2a9d3495d8df69f6847db4d70795da715367cbfd6ae7d1656e0bc8ade046ae4930d18b6d7e6ce39c8877838400156c1b2856adc94882520e4c58e7187966a3c55fcdbeba01fd38775f64a82e097223b0e0637b690748f2ce412687815e6ffc7909c1ad146a52bb793186897206274e4f84666bae653a0bfa2e22c3463e51fd116a040325372af6a57a638a670f55e0f581303beafa5f0a3dc4fc020e416ed7498c7d48abb9d1e0c3dfe078100a6c2d76285631a5b3b3b6769a3066b9a8310c5775c84456d7e88ddbf55ce91902150703f951d27ab3eb1d07d1640430e773ddc6352b34ff28998f8b688614cd079a78af0edb42eee1d8fc734f02d09297c347aecdde4ad7aa68ccd144856ede0d23290f15a817359a1bd5425a7f140796379f026b5b8d737282eb9c6330ee69043d2d88c240495720c2c1bc7bb181b13b0ac1d4b18ad75fc7ede8ee6da741830b639fca1809e51cbd5c6bafacbe10e3802d71f09467f9e6b255213fa67f02bc0be4ca2491929fc90dd1119463703a1fbc234ccb92f79f442d02ee5b409ebe450db8ebd1f2fe8b46e94aec94ef0932a6203a61a220419cedfa64a5f34148382b0eaf4bae04f66078c35b925e615c9a90b5ae2c39fc27ac00686e6c1df84caf0600c8bcd83a73f162c17f4b89b50ed5fcf9d5c90e3ecdbc54fb1eb97964b3189e068265e3934c6ae35455e2153d0ddd3414a2faa734c42e706c9a27f4dc66067a0bdb6945f8abcd81d88759f63041326c4881d70c3cee69ce087b3d03342b5e0d09dad6680d60894edd2c541bbed393b756d601c1b583636acd20875edae631f903782b7499515db9b1169dc73b771855f45857afea2ad034c458521bc9d6c4b00f00d6d3026a6a9ad19b75429c03197f05caaeba344def20bc79d63b587cdec50fbbceacd128cf4c97479f2a4b2459296beb6fd06e6f86aad288389483925ee60e8dfebd5d83ea6bff1a70fb230da92adc9866721bbbb2260b06fc83c72553870fcd63ff51281888782dd82b5b539835b1788c36f33ab82ec33b8848c8dde6800e66bf2005e3f195bbb300969c1512ba70611a15a99c58b5f7f59e38e8294f7e0a7ffad0c97de4c7ac8e2cea73ee7872971ea49ffd40c578f8dc3c3478d1699b0d8f39643b1941c47d0c116c80b51b9b02b4f738ed5b9e8251b2348f628c373708bcb529c2f3740f1e402be0d4bf634437054ffbad80eb59a273040495751b13046617bd3dd0cb9a7fb0df5a02c42bba95e6013b7b48cf5c646e9058a6c053ec05042affc6f1ccccde0ef89547a18bd0b0053e2424d9440e972bfc7e5d42956102f14e9a78fe4a018f88cbd007dd03e29739a1cdf6b5063f070befec0245987406ce9a0f6bd005c8e28bff749df87ebe1bd35fdfd5b2ddc843d97a212252b28200f3afa238422cf35ccdccc8dc477d0458ca5121ab3331ca43cc37f108e80b9900c3b11396d2b7e4fb7ab684f26168176fa326a25a632ddcf7a350b611b8d8c400e6e27f40896b5d45ad192a1fb9cb71ca858d3db543902d4beb72395c592bcd0be8c9492ad3b48bea50a91242710afb5bacc5a3cfe4c835e719d0d443b0be140b4f5af5fd4f9c34ff6fb49b75469c28b2bc4cf05998c9f76bb66ad283d4431205d730d816cd10dde0c30e706eebe1a0c174bab51924895b98e4bb2b4f5297870067d3f21cbca98b0001037ba4e897d288813bf873c05c9df06b18f864555d8003bff35d8f92a69e5428b63f978395c1c466aa43051325a4bf14c5d7b335e39d01ca1a872d67b7dcf6c73b0c566539c146b331f75d0056045721b81bcae7d1ec061d9bd2f57c64e09260e0f61c31e439f65ff73b2a92174e19c26c8dab42d77403b2f98e19417e62c2c65edd2563ad20d7576a358a89dab0220c054c48334f940c455a411137a66659d18d5a5ecc6954b59278a6ced59eb0d69f8360754e29b50104ef6f08f40d0d973762a7445c3298f30ee1733e17da37e9e062e1c247cdcc0da9ab3e8e8fdaccded2c633d348b2e38f9ca96b31f79a503d3b435730f36b3c06e683dadc4d671152fe94256897031427f02e45f149b6048261ae6b45a39f23087b921cb5438514344e101908abc2daae2e1d56410d5fdc938379aa41a162860d2239a6b7ad6a397bc3b7e95d0009fa676233532e5d9ebeed2baf10a1991e560eb2a7ccecf6ef1a5cdee3266283670afd1313e50e3bb66e7dd8f6d23e7a4bfd002da637ed1d1c6e0902289813fbec0874c6a71ab9e594ed2d8da4edb0b514180cd02bd629191da55700014c621bc174f6e4ab5d09ee4e2179aa6fc481d3bb98029d15f74776784cf3a67cb1b243133332aa17fd040a02ddb0255f8fbc693995026e106cbb6b2e92894a63c9da19d53807c464a8822814be812b56de6b678efa024369be06e561fbfcfc1b67b3953f25025e5dfc2a70d2a2cc265244724ebd8b0ea6f3d8c83cda7db16d50d265f11e7c5d65f179c9264c307ba9fcc0ac53bb6d0a4f2dd1e2190a934f866d6502252fd4449e7f48b4d7875e771e0d91c266867908dc6e3dc4ce9770054a9d32370e3dcb1dd770b8e0c9786a06463f722fcb8aa40ab34940f621830709cf3d5c2429d14772fbc79dfc637bf73ca7fcd7641b10730c35ae044f5dfad5407f71c017d17c311a2d37c0d982fc05e464439f724f3f050e00267ab187e7966bfdfd16cb385938e32aa0399a68af799cfae4bf4cb00f4c0e0ff0d8725ccf6e8336bdb9e773efd4881aa2d6bec997de5c7bf006a1b5066307082746846460b321b2d2bbcefb439ab20ae1ebdc5149a85b0e8c476fdb74360c2ff768d80276c04bc70dbe73345de2330bfa8d1b6d6f3abba7b7037e71ee1400cc9ca23f8634aa50056e33b262ebabd83ed5680bf650acedea1c96bbef0f460e311870b56dd5090ef60aa400aa0e51ad52df6b2ae69de4652dfd73726187390c0fc169c93e355cd9c449c53ed46da672b88cbae1bbe34cbc464a175bcd20710721df6bcd3968a4c7f0f2df2837a7f56c029d72d5d1d3f23b447922283bb8a705427100acfa2eb8f96c0d5b5880dfa0f8b0d02a2d2212e441b44fd088b956b30babf1c7252d1df7775d4fed2768aca06b664ee4daae142bd7ce874472ed64e40bcc04fc20a0e0cb585fc1cefb04aba3daf59977db2ec3893fb9ede441d577c50dc7189100475976179a0de1fca535a102b2a8b7a5484be7bc024793fe240ed70b77549c3bc22b439b5bc32ade784233529800b8300d06ffb24556c3f7669b7b0d8372ead3214b4ded23eb67300ce836cb76893018d98739f6552870acc3e8f5067f4d1f4c1dad5ce6da2c660b7463c7a4318d7aaf9acc358e6ebca5ce2579b80fc2c9988ac09bb5833c13bfcc1179a31fb496e1f86bc9f968d82b06aad0aaec081ea25afb44d6432084706727949b731df04df9b64adf236f8e84e8123bf17e0b6f90bee496e84079cef86c4a76e01e550e17ca1e32df56d1b42cf2df53b55f0e41e2a538daa9c499a2f8435105c86ac0da28c288bf145ae53c1d4695d0ce8e053be37f770734f8dcda81b66fd3ea98b87beb65fcaaa9be8bd41847631c82d00b6cedc0e13f6f6859fe63c6eaf97a5ee538ecbb1edeb737f1ae4640e60ad68d0292a549fea041ca35f037cffb46919da818e8ad732a6e4ed405e351ad35495c0b9e78b752e484634224a1d5e348c7dffe9e81d66c166442f8a5a1f5b4852fcc01663429c9cf8aea5abfba61e4139dd50e79e108fa29dfb367f5cfe6a6620a4100aa234d568f2746461dbabd0b9e02b9405513af4d5da32476a66babe276778705b3584ea525dd25046fc9374c27244fe107eae13b7233eaf10ac0fa84b3fccf04 -generate_ring_signature 5354b346c632fb825e3d4facd91c90176cef0f333295379b68ae2f3d646796ab b232e87202790b0f139b5abf06b5ed9dea9fa10b45137abb748bcedec49957d4 2 1d18a97357d5c6f3660f9cbaf1d05bec77fc60225b96c643af06888b41d560f3 e4943e8f3d243545a50e1fece9b992cbee891844f6920144321f5026d51c010f f0967d0fca4c4f72d2cbf40eeffac2483d32aec3c862f83c0e938e8788e95602 0 3bf521e80f707e0a2212329b98afcc721af4e80c0af3b3b4b52d3844b8d84d0276d32c21a4e81f57c428b76e25cf3e635e19d6715af3fa547855487f12f2cc098216c97a7071295606498ba068dc4d0be56eabfbb6764c50b12eb95b5be6540fded08135a06b5a444c4dee477d683597f5a9e95035df813b6b025ba22347a903 -generate_ring_signature a3e9f19a6821d12b0cae5d619f939c5d9beaeb9a361f9d7bf374b3814ff23ac3 5be7e561234f7b6bdefeebaf9a512ac4d97b77dff058f832100634dae4b222f3 4 1385acd49f2edcd1d00c3e69d899ff286d76ba1ee5e7684e480d914d31f57a58 5c43220a60eec00974abc48b6dd95e001d4b1e362aaeda5b712d8b6aca644304 02365a93fcab00824d25ce4e4bb58a49b9f68ff7b1748d1f03f8e5edd6fff416 a1abd0f71d7c05bc675f911d3c4e54c1b8618c603951a1de691eb38f030199ea dd3b77701253a786191b9d59b556f2b96336027fe8b0de80bd2b059acf610d0a 2 0615431114ffb77968c2a151e88ac4f7e9c76d0111d51532902385643bb19b0dca9538f3b1dce56e87719c5c9381ee912a3da53d70a24b1767ee83d7a6f0140f411c77ba4d43f724dc9a0734e14d151d760e660a7f6a563586978fd9af1b7d065251d60ad97b5f5459dc57c939a57161c2b8fe8c953c96a3ee9123ae2087880d89de70aebe162bd22db65536f5daa17b252581acba7c708fe0ca99f115b38d06e8f470bd37c8253a791e98b576b1c61fbaa33d127652dfbfa04d216638fbdb05a35daa5bcec656bacf32cf740402c6d611734427075266b7d163cea31c1e050f705aa16ef4aba7d720232540c56033922c07d10d3d2a61e0b337f2e9be95780c -generate_ring_signature 095549371963a6086a8533f8182dac9a9288863a9816d457484aae549c10c454 034fecfec4129e575a8f79e4a2f329ed0d6e804db3c6b30178a298acce98562a 2 80e4e87a2b4f800d02c125ad32c11b8d667705547775533c48d73f12f39e1c41 c404cb7233f19eac34d072f1f24f9edef448ab74024a8ddd8d72639079be844c 47b6e374cfb1b6ec4adcd3183f7b9f54812d7bb6d87adab41e1713b89a918c0a 1 9ebc27fbeb7d11d32db4957e4dd7377662b418712c824b453e76336bf64a53002365ad9e783df0164ab73d780cda0a05149847ba135a7f3f905c7aeba9e3e4048a993bcbbd6fc5b99b28ce2f11f420842402d3d33d19a26bd0e56419ec694301286953bf27efcecc2ce3a6f99a1e8effb8063046fd3183f3b7862a5849aea302 -generate_ring_signature 765d32e597159be85cd75d6f4a26b6afaf0eb7618a10bdbd64c3629d5f37829c e5729996355a7f45566941b16bbbdc3e9645df09d11c7a4fe0297d9046d80876 4 ab4b00cd5d57edf83bb7e34a17d61fa41fd2bb00eeb8b182e03e05fed0d63047 1f121a1695b39b438807592a0364e537459d9ecaedd7be6ecdb71b9534ef74a9 02849420f9ae2c064385bf53eb6a49ef1ced6fa4b2a386ccfbbc279633f3a4d7 4ae5357926a9a9e9204bc6f2c8d3c05bceffaded63d44c2e52792fbe87964b89 fbb4fa26dd9caef9daa40f9f451d01747fb79eb9bef92b680df1b0f52588fa04 0 523b25238b4edc3b55f35b6f3e58175c3f63b41268444c73d646405564b982095d30a67853047027f2713b68c2145ac132e004877cb1d4ec6018c98e9c3feb0a98d9e880919772e856a38194afba119a698a176578dfb545a67b311e8a54c10088a7de0b11d2fb5518222cd683488bb8cd9e3930d8d66d0dd976af3076f4b10bc62837fc63311ac9f613b24976917e833d3ef1039743aff5d36b142bbcbf9504b5dd7bce1365a93b0e9a245aeff2e8782a880bc5ed6c35bab42c377e0ff1700d361a48fa64b7e529edd291f83fe02e01464a509ce62b6472e02baeed9cf2160dbd545244dcbc322bf3629f756cdd2a80459d89988012cc21931350fbfab17004 -generate_ring_signature 5607d2ce8ec511c95bb7a1ea452f4574df04bd5bdbaeea20ac0e508f13feea37 66293e624f791235be8fcd3944938a213b1449de9b64b840f46ab25c244e2eb8 15 252083243400c45f44300a90a5a72cf124586df2fdd358c3f2d8a10a9d3b9f84 dbfc0f9a60359bdb13b1eeec597ce25a39019303f53e214106c1b6efa039f41b 1799c271c54721cb9b9aff31e770a27db2016d3462e8f7cdf97e58f5d957d1ea 0f3e0b58254ed1f16875206a5fa71d8b2bd3051cadd875a85c064fd43fe972a6 db61f4aaef31bec7a76142570228880bc1ba95aa3e130efb815c392daf1f648d 69a59d101b46108998f7147556e1a489d50a8de2822cc4b616fb316ba6308890 3216e6c0b89b0e9dc558f22b0646340901d4bff8dd6d0d37b428de10e1fb3d8f efad5c615373f47a5391c88bd7fd426915d61f772f8813fa1998007ee605b032 aad7a20871ffcf814a70a4964aadea64a2ca043ea3ed90c04bc0c581ace03197 ea57e5ceb849204391f7896c7cc93ac01645bb955a35e7295a6201d3888072b7 ef11e7753f6973b1f3c06b2b7dbc596db6f16120715339b17824086c12a18686 8a8442aa38bc171b5fede9ed5892c4c6f8b7834f6d84d6818134a2f61125f86a 12fded8c7ee0e7511d3f37d7fecb900ee662c3f7dc36756d64fd7bd5221ff607 97e073bf305e6e79f257fdeb5d5b126f12f818edbf3502a129a71948afe94933 71827fa61bc9bdf166378ae6e479ab50eff55f8498804bf26e92fce047f1a76e e1c93e67a747dff61c24a89817f62f6f84ebe585721cbb46d0139ff7d410250a 10 812b2d04456b45b6818b807fbc30a5638aadde2466818ff15281a09be39a5a00a61ea4ea3604a53dec3deea1c7df7ae2e023554ca5f2a3ff4c53cc8b3d83940e9fe5673b3138ebb57e77c7cc9c3d2e1a263d308266761abb58f6175b6a5b8d0193554bcf59025d8c967af773552b72fae6b8243606dfab55d081b4b640a27a0542f79afdf02186c46f053285b7a1d636494e2a10d8ae11cb833d46f24fa49f0ca10cf87572538f21baef377f6a4df585d6cf4fdfac6bea8fadff9eada00fa8009d35812b2bc13dcf81f82edff22e1399fbdcbe2c92f93aa769fa457da766d90c930aebf85cb20d9bea893cf4c4f04514a904836fb9a3545e9f11c6e9cc97110a086e89386835eb5e25774a05358d85604540fa68aa9424bc3d801f53d2394c075b9df7fbf58dc1340f2daa5f004ec67b4e4aa5b8b426026ada481762851f4c03632e5e62771546c253a94b507f7dceeee5cabe8f09eef8f944f16f2296a93d0be350e229edb889ccd3ddb22c984d33a83e94e9d7840932946d164fe8e1bd6c099c5c27a237c01d8042d49d089b129bc2cc142f12ddf77a856f817ec2758574023ba29e804de155a7f3dbfc7fa77a5f366eae53f7c4e962e01168c3355ffd3d08d33a709fc72f167758ec133fe90f2513c5f912ca82a56e5f17ca5cd6865faf07d3d5a1ecc34f48979e6f1e67604a44e749c33ba48b971b2d65920aa29699fa087a0dd99766049560d4b0942f7e3085d26ef1b44e88be567bf042c9979a3ea10c5105f85c6852b1e67beef8a3e0739b9668ee46a2b0dbf1e3cca85935744f10077dc31186c7225362c3ff5f5aee4fa7f27977b6a81b10d22c09505dc8e957ab0513e52d25b6d22a5af1a4ce02048749ddaa74f671f9dbbfc44cfce38c533cd1011460c5e5d18b26afc0687a55b313b790a3f6eb458d7bed7b076afadc63284301900ce9669e3f430879f6375f44e218af5607bbab9c9e7ce0ce12c1daa55a510895e3eabd3d91ea0be0511f2ecd6a313bfe74171ce6141f49fdbb06442334350c555b069e9f809ec0ff6fbcb19f873a7577d752c5a133e69490b28b7958d4c80473a41311b95b90f122b9a2e52c21e18c43b0f15f18cd76b308334db95bb06c06aadcb55c89c22e9793a99d3749568888be47e6c8abfaf5279aff0819bc8fb2045134c54d3cf4c44681b501fd8f32c57b1ab9a771c1dfe1bd122204b9c91e81046ed69f04959841cf300b8a766871107b1913198776f854217dd3e1de212b270d87f01dcc238f7dd4bf6938fc0871809279e14c7e2f5655c4147ef3908f22490350d973b2557ed2fd6aa6e253c8a2fcd3c84f2adc1f1d65e9715a47cc29384c08 -generate_ring_signature 781f7aae6916cb86442605ca1c1329f08692a28c5cdfe0cfa95d964ecbe6f9e7 dabd3b459a7ca4562fe5ca42191a402e51cdfe567079cffaf7ebe599459490c0 49 6d5bbeae1447773bb341d4f6253c84a563d776c81124bab9c82b8b55966ef9ee 5f3582036521d737a102ab0878f3482825882d869168795c8d29501d25741e71 52f9596059344eaf95f52e49d2ec46868c01a0612413866c50b351c5628fc8e6 57d139af17d71882f6a385f6a87caa657970517595baf226175248877f5ddff9 a9f4ebc8be21b9348b62361a712c90841c4ff4c595b36049efe2d101c4efd321 e8415eca3390ef846f46259c0e0fba6dd7427a7b5f1af4cbdae1cdd34abf7569 4f3e26a193a4cd0e338fe5ac6b4451217ff963c1ffc7ed2afb8c86d534b0bdcf 438597f124d1177200a93dfa9d8d0a9cbcceb2cbae2d93640147ff1eef7a2233 f5339ce166c0ceeb926acfe4a017911f9e1e3650e4eec6457e20cf52355ac4f4 4c59880049b15c24b6f54586a69b8935b5d8893a7dafa1550c5123cf0a253cdb 18a2d68bcf6db85d092a4362767cffbc973fdeb9eaff23d260c78777f8f57681 0d45035f8548a968acc0b0815518ea3024360446580b0e5878d75b0610911340 724763763a18220ca705b3221d8ee2231cce67235d0a75ddf076e8ad5523cf60 65236dfaaf133b1dce26fb478a81305bab5dd30e426ada4a399e29a7c02ba4ca eb7717bf4521a54e02db70fcb96c20d9d4d6673378f6b2c196bfc22a3d5dffe0 6752e1853091c6b4aa5f88df39696cdf2b7ab14d3e4b140d1c3905a6f86766a9 ffaf2190b741ec45ad0bb7070e66235764c44449ab408c6769eff87ee92df421 9a3fa68c3398397d68f7eac4a19e7235bd4256243bf8667082c4bf5bbc06b386 c0cbfc6b9c6b2e6c7c696cd7a89e4c79c7dcbb394199494981a6342fcffdea0e 661f99232ae0a6143a994c679146f2756cb4dcc6c465145b05d7bcf57aebaace bcd4b4ba71a309874300bbc35f9a9a541a7ee8055f798a4ca4dc546ab021f25a 8937c53d0262f927ef14ae7dda2cbd277363612e98669e58604547676a862af7 bf01ff591c0a495d7938ee4e3aefb0e20b121823451b1d902dfb8f2b460aabd4 6e0d51f94745295a30af485bbd46ef078fc52172f142f1f73e68d5d0d9ab1222 36fd64b91f05287e299200deb9fb4cb0e3de6f0240e2dbf274912579a8a75eac e72d9f05f370aac2cc5e0777bad931d6e4e193179e822583f02983f8c6bb54b7 d589025c70a128d8fd4b6364ada77612879a039ff49a61b710ea5ba856eb4cde 78c4e48f85675999752f7e6114460354ef4615e3ec05d2f6f96b85a0feb2093e 50f02e55a8df0e9bf0c44c66403ac989c630d21c2389ae73f3413f1d07f8fd2f 9d4879da832b2027ebd9dfe0d5d361a4dbf32158d039d6951d20d353d0129c6d 9e5f72821c4ac472b040e19d85234f46aca39613013ac66788c3143eff365d85 9d1d9d4be6eb21f591cef27b387545a22dfa1882a7dcb30fd5f2141b398fd2e9 35147ca542ae0c529dc9818dd8e4bcdc300a867254b9dd0317bf74a860a66817 17c55d7febc20cff8ad649ba7ae32318cac1750da6b4ec47c31c0f49ee1643f1 f24f5e7d6a9a0e9c457a0ebec586b4bb0a3a5564559cbf377138f64562df2484 952e0f4633631f3eb0135819fdb7aae7742ac5b2024c5f15ca1cc65e1cd08f24 7b9adc5c33bcd0feca677012599cd9a7fad17de30a30efc0981029e15c4fafbf 11e437c3869f6f8d822ee753c68b860560480a37eb57706082d076e0f601e54d 7b89c9df6a8a9f261d298a996c3a1947a6f676c2c7be4ae7643c2ac5d8e89c62 ff3e6f71a711fb28a24a2ea10170ec29d4fc3f3e2f2de6a8698783b0664d941a 919436ecb0dd620ea94686ac3fa07bc17a777d48f9c0224dde1703d7a2ebac63 7088fc87666f3a284b325d1a50ef179e47ce44f546bee688bbee3377b5462e3b c4bf7d3009908f391b0a36d2768394e429a4afb69d789d64bbf1377402f39b01 5626037572ef6d5c06636c8d2c7a56e19e4eaf2bfd6229204d4a60157b7c3d9e 94c3d100bdf06065c3d6c21e4d743d925065888271afb52777926c1bb49698d1 45591bae02bab2a3b21a83305a149c0f1ad63d16f23501ac471fc5195eb4c934 fe5634f879e4007ac0ffe322a23d96d428ad09df6eafd123e67009c77d3826a7 e131ff9519e778753cbb08ea67fad0bf1349f8bcdd3e2a9e30556d11ead0dd5b eaccdcec9531361e1b4559e4a9ee176075cc22a6770864c21abab1a28fa062e9 b6b0a5cb6c1acf7b032fe3401997b9be29ae854fa7d6c1dff8228a85426e8e05 35 d8fe93012fec5d202fea478b51dc5af3c87e5b412c3fb08bf2c8bed541fdf20d7a3ec008c2f0d60942b91e669108575e40bb33ba976fe62fefb9a79de3c0890d273bf195b698a43821a8161bc9f80920edade208cb80b29a56196793554c8d0804e8bae8036d3c9a4fb4af31dd43184f8337a6338950d32af747bbc2adeac705c9077b246ec19549a57c5bf1aa94037ce32ba7a9099351cc1316d9328c6155076b6e82c6d098597445b96e319023e8b46ae190567616d2336c91595135b7710dd58723d73fb870f178a5f224482d2c2d9f55c1ab612c2606139312f5164c5e0bc8ffd8a991d926e3a266347e2c004aec31f4bd4f98db32d39818e1aa7dab87008517faa1424239a44079b4c7161943d6b8cdf779374f0bbf2012fc73666fdb09fb18858d96b4aa594102f414fb2c0aad7e49b520513ca6c62d64fb843fec4a08bd2285dd7f572e7199d149a83da6464fa93643a88851a018ad6fa6ef201bd2044b272d5387d6ef576117e1707a2752be17dbb21f5822bc6938cf388900796f044c43f557f4c4b2a24ccab892f6cebb774ca186568e1a80f0ae7ba5073d3b3b01e1151b4f538859bbea8c7c682bd99c424d6426553663ed1201f077ff9808db0c76dc6e351020a0dd0f122df4f5ff545381313992c3470a6d1905ba87b000870ac9513dfff24f1a1bf3c5c26e0fc35f488d67467bb21fde9ff88621a91e32de052303150b9c06bf289539bb254db0f03b7a8a2c0376076d355c9047368bbe010032c0eabbef36d528ce939f5c81f67eb265d980e42d5ae529cb3093210397600038441cf34b914e2b8d315a05a3ff2e0215b4d9efa8a000b96bec4064d7654101c6c880205947bdcd7303d1ad34c848538d0b24662b9185ef3da41c7223dace03581374907a5c9785b9135f0199c774207c7d7b0ecd7b8ba464bc3df4830b780ce885ad3fb2387ebfc34b0e3c5df093ddab9d036d970c399ec4bf28a71cd7ef0baa5d353cc78326c42b6ccd49e762cf4c8642cc1c59b536356d3c6fbf44ff0d0e094c977a10cbe6bf26f367b6e9f9c02f7f469789d6b562d1cbf1a36f07f7f70a3a8fda9c53f03e721ffa7887e2c6b8965410932d888115ac62ebfa92ecd78d033724546da824872791287c8d6a534268e2abdbeafa488d8128c6d26ebe63f20667f461104a0c8b1d061da0f3b28b77379d3258cf58847774c2f888c68db8420e19bd67ef3ea2a47deeed384de335996079bf2c703586d7d66d95c6527f33af07af69dd5be66b65404f70f3e9821939b51aa5de51765a94e5d1307252b05a4c01e77adc9905cfecaa29fc8122b13f7aa999dac6cfb50fa2abc16460429827cc0f8f24d22784e5d7bdfdc6e55f31be66ee1d7c9a3a5a7e2d92b7bd1501414feb070b1dc12e110ce7d80f7e1dd67849b3855672008fbb3f34c42f5b7c0cce9a920f9a601d1bfa583a304017f1f600bab311d41f855c669c0f8c4284825f7b0b030c1358b710a4405c2cc3281714ed036eafdffcffc65625f42ed2386c01af4d7601bb7c2a89ad69b05bbf41e3d1deef6d63c3928925beff4291f729610b505c1d063752f43914559fde2632839a4418c505f5ed91558ff3b21ec145d5578001360dd4c711b46a1bb705d2c64a0c7df1480395cd65aa38117ee204f427e55a7260042c29b971490cbf1d542b737d5227917c7080cbb29c4b599c211efcf8de5287013688244d1566963693798ae8a0268365dcb049480c2ce85f31a0b13b9aee02030145a674a3e700150ee60af5520213c46c7c3480027ddac5df0a90c4bf1ef406fca63614e7bdbe19ecab77bbff6b6daf355729573ca9b3a409aa9e9b6ac57a093ccb1be1c819badf96c0270a7e033aceb54157fcf16a36373d0746080fa0e405e3c882a36fb846b82666eb815af683265c32da54e42ee0942ed1c215a48f7504a96f59f79760b0c8269eb875189629f9170cf3d50cf6903380e9e251245ee10c0e1a594c041a6155f1137b3ed40766842d19b8d48990d74319328caa3b380a01df9d09f76b7dcd156ca8d6e76010e42cfd93e5b779835f39ed88feea44ea310b689d5a19e7da79ab595135c35889edfe44f47a4e698a859cecc471bb54543307e198b1efee4c03c8eb53a4a404e31eafa05dc5c9a70219769fbd197eb3a91d0dc9b5fe08bf44c47ba431582490efe1a870370c71acc30be548a683024202e50ff14f8cfef3e3716f60ba95d1a1cf6ab1550dbc204ed86246a2c5a681329600066d8c3d3109fe58dc2f53c682fdfa0756e15f752e12cad949f37c8a38e2e9590d0c55c25c69b692d1857a1c42f31cba94b1fd96db9c0558999d34aaef5633c10ac69f56d70784fdc58505c187c354c07bbebd39188e5ede3e121b1e96001c450ff488108ac4415edf332b9fad54da22e67a092502b3816b291ef4dbbaff0dd100c3f5134be3dce80dec29f7b521431c85d3f036ef92bded0308657446548cc9088d8364b984cda1bb925225ebed08b8f5e02a8d38a81ff9c6a2fd629a94680306312a0f1f3e01794b44a905eacdec9799baa2986d258dd63bd701c30364bbc40029861ad1ec428d9ede1f7dde3196e8506178f87f944dd3ebd0d656e4a275600b29d3e5c1a6210e3db235d996c04073db321871ca6d9afe9c493cfcd987514508befbaa7276f8eb67e500285f9122d62eff6982a44461e035eea4d92730fc4f0df353c5b0c2a0af6156dd88f6bed1d7e54fc3dcb4dda2425d6be769f9b8cb020a50b36ec318ecc6dba966eaf88bbfe06aec868609e64b8ee7293f8d240c7ea4039722442ac539c0d22942c7869919d18d8d10ccf1280edec595eea82fb3b7ba0e81e54540c8767c168392204d78d4e6df6c5e48228a817032806c65ac0bc1d505b92e208fd7d27f9e0bc2700b110879d2ab802ee2eb10b35e0521540aa0b0c808eff9b720d0cefd4e8739a4848e0b8def2b2c88c0996c2c6613407b435ebe7403e806b2351cc60ff776c6b3de87c741f48b07fe45de41102d2b55f667178c9c0797c7d50a08cda2b20073fb3b381ae40a625c8f74ca1ffd5124e1c342e51d900cd386ccda7a5433cdd88a2aa31d1484bf8c90cb8791090f6b0096ef1d84d4750145d7393919cbb29ec50fd8bf47f85c38990cbf76ecc4fbd6ff98b77c8b206d0c0af0f5661b33a8933dcc535c916eb49d6e4c7e335bc36f04174c67903339fa00f5925cf4e975f7e0102c12a82778293140520217c18d56e37fa00e424940c70d7785a4b043f5d0324ce3785ffda5835f36506620ba1740aa368c7f1aa6b2cd097b8c606b8e03efc987e716ce0c2a6c8bff784690d15c1260aed6551ad2645d049853aceede4a02c36ca06b4accc3f66c2c80ae41415b6a0ea0e56ceb51c45c0483fd17c124575fd0c08e86cae2b471dfabbc0ad8a3fda70906f78b42d6093e0d5f0893134b5c4c28f1227779a8459f5f6b1d9c6eb52c60f08123245ba695570ce89942bd9e679a8554eabf42450a71458995f66bb276e256c3ada9abe0aecf0d96aa0bf1650080feba619ab005612f60151ca6e16818f5b910c350c6d0735407da32644a33f5e367d35cabcfa862f2d826d5f67f3ebea6441d124260eb0cd20f131e09dbb16a2854dc974552e0a299dfe9c5eb43ad91cf427b3181bf856f870b740ebe7c935bb0cbd3128383aff964d5d57c16f9249ae8173515bed3af0bc00c59464b1113188e5029dbe8e0cc3d8fa8eb2d272164215f60799637cd183d9b02d56e985d9f15a3b50c519d47b6a7b6c1369deffcbb59b18b60109a8e0b23140beedf4b17c862c238adcbff74d2871dadbc48b810936af86067a68d4b95c1c200a0ef7f8eff402fba8454a59712758db0cde2be748ef886e94919a9ee0bf3170af3a699c40f2143cd8afd078b4b2a1b7b10f6e4d398a32b77b1576a16dc71db03a44f027c22fc8b3fbb2b54aa4c309a8dfa39fc1bc12adf5058da648e44d44804c1366319c7c50f0ddef4a719c3e660be546c223f0403a7f418a28ffcf2951402e2443742b0581e13545eb945072c5eab773a0dc19f5527cdac61176249ee2307e45a41d9a0662dc14ece168ac4da94dd652e743b1365180a9390bec3d246c50b622c0eed9cced72b762a4244e26e89048ab6c8cb7dcafb23dca9d4f6d00c850894c53bd8d3c4db9f1cc1944130c2813a84fb6e013d3a73cabfd4473801cbe3096ba631e8bfe65fba0f04ae32c6fc7ea44e2e8ee17c4c04f4cbf140248f66f3016952e867339f1b3d7853b25c8180a452dc30e126f0942789997c0c5b6a812a021f9cc3369fb058d46302b5862968df852d9387aba6e0f72ba131958ee0e35d0a6f8015225713d6e457be74fb6d63de9a01d7d6a6d4cb103713064cc297a7980712e62d7406b48015f8110c3723dd528ddb9a308e7e7ac6e61db847a27efc7907 -generate_ring_signature 0ca6be8442f73c39adb0992700397888777437a7de0da8c8f80ac7397c51f46b c2483ecf2cb22c97aed88f73d621ddeb6f26c3417ed263dc17a486ff77274806 1 03b86aade5f2eb2b8868d951161eda03d4a47efe945a9dff7d630de9089a94f2 ef2190e473cce3732bb33ea3a62a4d8172a78336453c32f4419d1437ad3e950e 0 e70de0228fdafc3555d07ac61e61a4368fabcdab228cafbf0ec4aa2c8a59260229d579203c9f5ceff024ba0fc2eb41aaeace5ab5ecef65be0c2be53844a85f01 -generate_ring_signature 79c5aa9e030b432d9269f7e35bb8c1ed8eca493372526a02ea1d6ebcfa52b385 9b9a27939cd35b70d56eefed6bf3473df1bf5e1cf3ebaf7de8ea58ef212170fc 16 1e6c15fded661246e31217f773daa1c73c6b5a535dd39cdeece6347aa89981ed a2dac02c4e2ecf3f06cf95a267f7e1db2bc014bfde9b8d5e4698279429848a43 ca579ff489a24164edf6a84c9bdc87374b47efd80c3f994184b770fcce575296 2e247431d695436c7107a280e966eaaaa4d90440d824f93d1c96ae49bc16d4d9 f2609d17a8633257d1cdfbae1690892339e49f9a46eefa9e7b97d5b0570f9cf1 bdca7e43816c3c6fd0013557b1e0f2dce14ead44b6ceaa381986cf6c3055adbe de1b17fe16b510a67e0fc049a5cf4f801a9d9550e7ab8a5e4f74ada85a4057b4 2e9281c688bb6d020dd8a4079acab780a40329c6c54e1947f0e0e7d90b614aea 51dc71ffbdba2913ec8e578384e4d25fd2bed760dda9649155d4ef3f49898c63 88d6f98dd9e6c2a990aa0120152a372da00c455c97b22767803121f55b9d3433 6ce4fc262a6b4837e5f7baea0a512112a2d72dc6d36570cae857511197eee9d0 ddf3cf1ccbed6da657be10b878abde58598a07eb6be65cb870978429bd6550f3 5469427cb812861380add098f93b02d766d7b9de7b5c34d17bf8b2105bb99cc8 ae7c7772243d1b1a0939390bf562946634fb8f3a3df045c15f622aec4a19b05c 62f43f2a86d3cc9a59a82ce2c087c9442c8f0de3d648e319793e8c7ae4158735 1fa26c1a7685ce10b3734969ee68b9d8f441b0ff1beeda2a6a5ce41d7857727a 7f846dc0a1378202cd26c66f6d80322192979c9191c9bad913accbe8f3b4030f 7 0bbe1868d32efb1f473ee1f659d41e42734649005026e1ac279d5a4f517c6c0e5e346ef16de3810d7d88d57a2d9f2f6fb447ecacb08c84e9bd4e4a72aca50808e03d2c29c27a831b25786821a8cac11d96d11e5bf72116719b0816d642135701a1273adee1d7d1d6ea3141c1df8d4e24abc0f48a3f3223f1b230f8294f5e1209bf5493093162ac098f74abb5ecde6772c57378e3987fec98084ae4ef2311ff01d2055405ab8ca3803feae7beb18f3b30c06c38fb3f7e31aa2ddafdb5f2e5860f8b7a8c95779f997002a36fc230048a3ce226bf2a2dfb29ece8e835189301960ffc7745eb3ea67a64982eb099d59974868b8c86721706600b3e5675658c8944046664f52af1d85774107cc40b326496c07710797c3c475addf9c4932b0fb03b0c1ef1d853253c0c89a6805c99e7c610bc81ea1f39559e5fc3f7893da4d27b0b029ee580fe3ae9b8abd48810746be3b7e5374dd57751e3593024f69194b498b804e4ef37bd44f2d0d59ee4cbb6f6a114103e1cefc8b58077b6c152221ce75d0b0091bfa7db1dfdaf80a122d3d92b60279802970a5d5f03ca33c51812582d724b0b01abf07180a7cac8118aa5ecac6a516bbe9a2a62c72b59379bda7d93d0a37107fd3f78067cc47207911727df5529a4d4615f96f047094e51d2f316cc3d280603c922f432be163c96c277b3899f3b5bec9787398c91e3b0b5a8f25d8e7ebb0f01423e4b9cdf28651fb7dee1ead55d02bbc5b02722a1c5d14588a9f27b8cae640d9b7ef746083f33f0873455d0e3ffe469fd3383e510bb9184f52209fc5fe3a20f7b01928698bfbd1f33201c3b2a6147a6bfb78cb2aec79312a2271d41c03ffb064014c7ad2e736cec5bdd313fe08d8e44953276e3b9f9150432cb0de79183290750a91785fe2a5f7052b56dd4a96af3f41760984a032171615c65a5eec24966049ae0fca22086de48ca7222ebe52d03e5bcefdb174b45b8c96801f8cafe19880b60e07801447e325a06aab7ef883c275f49298b7795063b015eae1ff9d6e88909fa967f53e74e7a50a5e2399d8ecba2b7714bcbcce137acaa3e778d767f6fa106c43bb83ce741c0e7a9f5b30e1b52106aa63a4a03d0ed39bbeff96116e3c9900f44c6b560835c311721ec3cf5d22d2a023dbe4cfb904cce5921ebe6ca88b7ce07922806026209e364cc8a5e29aa60b546bd56fc37fceb6322d7a6e92d7dfada02e2e72c22e3fde3c1d7cd93e93621b544e4a72fe82f0f259a19bf7fa5294adc0ca42df2ceed1a1256142e86ec8cbcac099213b2352be87c941468d9ec1c149f022469523a31842723324589f45f4567028bca50e6d238812521b717b0cd8cd40050e17ff8812ae27f515a2ca37b261dfbc50be24b973ab2d843f0cda2e7917703ce7dc6d30ea5a15d51caa3e2cdf08f3459f0768f9df8eff5d8b2dfc0380ae30d -generate_ring_signature 80e43999bc762b25fa9d83c6fd111032ce5582d573d6f0f3e82e0f989fb6b8cb 1437aa8974c08719cc58a5334921c3201e9f45a5290f2581b26345a659b86475 17 40584e3d53899817ece3a763d73fbd5d5a09cd75ff713d3e06c3eba2fc853319 e4b87de4b1b936bf74e0bf1aef738e47297e65f2df50c566a9d5177c75917534 98449fead3b69ec5ab2d9f524ce8aa5e6d84f14917b3b03575aad4d4c291a634 23619e368d268b186bef7a5fa0165c31ad8a3bf4b0220bc262f484067d9ad4c7 06268863803442caa2e603b4791a097a8f33c45683ef55befb3c0d7e3055c36a 61ae5b158be8ad2e489dd7b0b78da17166a97f09629f96342e8712788c1dfc0b 4852fd68ec5cd1231f48b79a8bca21277e347d39af3349bce848312452874d59 5446a777840c6f3f75aaf437d1f84addbd22b5d5c848ffc3b7fe16e1157e4819 29984210fb9b7462a3cc598bfcfaed8a0d704d02851f1ea7d3e7e3cb8d73d3fe 93d4243ca3db959b7f3d7af72a0bc85e27226d1c716dcbe1c5b3743ef5c289ff 7efab8e221de5b67335eb804564b60b5be37ea20497329bceae89822d9e530fa fa084a1bf2c317822a31681302722b4a8001fa2aa9a0190e45b68346298b9aa7 bc15f7855be9e63f35567ed80b629c270a1cb1de56ed670714645261edd0acb9 17749387d62c8c3f725f1acd5b44c955bd4b35ece33bb5b3fb121ae52dd07750 7b83783748d179d74bf5f8a177a8e400ff4783ba1a24b7966d99b587f6e4210f 235b414cf437b43c611267566496a834045ba01b3f16c7b83a838aedb35e5863 0e326a30399705ca5c7fc10ba5f072fe06656388cbbf8f2417032c6d3e1f1519 0be7d9b1884397738bd4495a62ef77233f1b262363539f6bc3834b3430c84408 2 eb94166938408577a45808552cd51352f6ffcc40f03f596ffacfde3e9ac7b50024ddcc93c7be70deb202a923a924d41a66cbb1a4ef9ab6ab660c3b8f224fe1040d2e79825aedd60df00ee292be12da0cdafc1f893f51b37356e5d68551fba30db8f8ee0e2a033fda20d1005c49464527589bf9eff8bf5e14e1dffc5e81adf407a93c8ab8a007fa20e963df974238bbaece93f95470d9cc8415ca7b8b3d32400681f82b8cece5e152fd54312a9c481c124ebd94fea24ef74c4fde837e97e31e0ffea03a6ed5b7e864b1db520897487d6758f91872f5f2f2e922068ff3ec1ed4000302457a48b33868ae02e1ecef336183708f43935e26f77261a8a7130e9e0507ca91997751576260ed3566ce29b424705a14072c3a8753bd0350e26daeac1007535d6a441ddbc8c68a74d770ae1cd23105322015967d2366e103769dec98a80859f995202d2194affae7bae42965ca2dd820f0073ac34f32c9bb4d9e6ee3070f4053bd90e8dbc64aa724766cfb0f00e0e06ee07241e748f216e9749630a4290dd5c610fd6603022398be9d0db9bd6dbe83cfeb3ccc7d5c5b86acc226415e7f0957b52baff307d52c144de8c34ef36480046a04cda7b3221c3d9535b74375fd03da2491e036c7de424c9f2aeaae40aea5b546c57e12b08d170ab5fad3ee773a0b1e1bc4d7086d4794bcfcd605d39eb33494826c67cf5b3e989082eda1d61e950a6e7878783bd3a087f61e041c25926e207aa2adb686b124ee7d7e1d1641d8c20d380e986cec276bd823bf8ac174341074f568e85b865f466d2834bf66bdc8b80dd7e8a531ae71353f78ceff67154bfec3c5a5474a20ebf517ad2d68a8433a2e04de3e501cdd60e9d03ac21712a3c24de4421facb07e164ca68025ec71c47ffa00a68766dc2efb0a9b0c85ced09a48fc7d7dd169b9b8a64d916afea303540de4017feb4f5be92aa6159d0801f3bb8ee8aefdbeb309f6d6c639f5c25eae0fe92a01b5cc58a4c21300afd5d165c0900fd156374dd03482fa376333d1b168d7ee120308d8809a35a78c0434057efc2b51ebf6f7d3f2fc771277950197d3e13c5b6a0396385539bd059ffa3c12eb42ebc995ab020a5d2d026ae3bed525397026ee000e8c700fb7e2704a9dd41ec8a895a610619e87591e19fcf7c5957365e6306e580420f0c09f2a6a4e9c6853a44de2773d302c9d9fbb24472c2cad4303edae9c1c01bcd95d9e2ea2050245fae50d6e36347647dbbcbd6306f67ae9383c63799bbc093a0351597e0d56cb8fb43c2927ebdc832ce401a98a16082b56bb8ec10a986209423583e1cc2002a3ed6f307cb7abc97b270c61da2ae01d9d4ed927a97f30e106a8e1a42f2238bc8c54fea101d53a1752885b0f4404fcaf285e555a18c9dfdb0e9c0a1d9cc57c296f91c7346adf51945b0c31456fb3758ffd4a395a40250d020763b726a95b2168ca644ba999d40d68697e8d9d2f5970c8641b9a53a61c2bac05be5684c7cb5397315acee2e1e63f08cb7922469f532a97d2aebd5195b2fe0602 -generate_ring_signature a1cae1149c356e23100ecd1987c21504dfd7eb93e828bd3e67010b2618b67d12 41ef904648e97ded7bb6cdb675c0eb7c3e265920e0ed3a43f6d9d52b9cac4950 4 8af0cd069e8ed1cd5b5210d4f3b1234fba3701a51f71223a642afc1ab7c2ccf8 68a106230168fb6b767c70b9bc50adae625609a4deaa23607eb431a125d62acd 7f77b78b984a927a28d64f19feb63bab35f545c419003dc6804fcbbd0a6a7d89 02e2ec9fbcd3da1d36c4426320a0ae5dded7b03e360e801c842d6da1479119fe 9bc45f2111dce6e47ce84bce74bdb15374994ccfdcc33c497c8c6d02621cc402 2 60ded68241c357732f79290b35f199840589922d852fb66f6d5534199617fe05c15610a6a3821bc2d263be5dfe32b5b59bd5cfcec4ae1216fc61204047d92f00c900344a42ef57b8a104e93cf7b945d5e715dd39d86a5aec83d20d6674a1210665f31cebcb661ba213245498a30c716dfb459494aa7f7fd12cb5e2e95ad82901dbebe82723d550d6a8628447ca27997aa720a58e1bc4745b2c75e1263567c108d5bcc19ef6f64fdb392b2a52505a7255d5544d9026037e6586a81b238ef8ad083d9a46873035d309debf6bd4ddca8a292a3d03eb94800c11007af0c25444db03445ee7df8dabf763c5fc83151f3d09da5781c734d966e4a2a31925e36aaddd04 -generate_ring_signature a4a40107ca42f136d1a311b9dba01b70ab6e9b488c9d4bb1f82137a8de045b0a afaa82207be08f51be6d9e58d2029c812fca1215e9a4c9ce7614fa9b1ead0e33 1 21eb68d3bc8a11f481c4dd1aa33a7eb137bc0266faa99ad02035de1940e96ba9 4fbd33282caac0fb0207af80c006ae9da1993618bf7f2eff764426fd0fe7f204 0 976d64ee5b223688bcf5fd91721d245cad10601776ee516d040b0ad824cff1001c4b6005796b989a09dec70073942f48b38934d412a2521d1b57addfa751eb0a -generate_ring_signature 856142e92819b975d12e0fe26c75ae88da078d345246b985f3d2ee7b2c2f7c5f a7869efbf3517f7b3217cc10c8d34aa79dffcbcc332863aeb9dab30c14f2c216 4 c2fe2e312d4d1b483105304f0790bd832bc2bb194157b215d265ff8f1fe5688b b6db213bdac6431e7a58bcd7bcad322ac7dc04a2ac609b2e2c1942aaa2acbe7b a9a365b5196b89e4ead8efe752dec4900446c49a93fcaf75c6e01d6155adaab7 ecbc8dbe05d49b8c9ab8538a959747d3c965abe9bcdb090829e109aa9af410bc 4ab503c0deb81e5867cb8acfe683c713eecdeb27bb51b5cabe78fc3d287ad507 3 c4c26506f6d9797347707dab7b7faa9def8e26e05ecdd90c95cfd0704de6e406e64cceebeba4114144d568cbc334aacf1637f198d872b3b9ef24ce091f64bb08994ce6c06b12670dd7790e1f233da881b966679d21d8803d8563004431c45e08afa14f7c60f0580fa2406542194a468a9b634ac519dd099780b7f7930edfc004ee20ec07d92a8e74f130ff63699824d78716ceb89c745dc745f6669c17e855038655b1f968c43a4037a4b2cc590b0243157bf8fb185d98d798ed0ee8be82b608c5d6965dd7b46fe833fef0e99167ab4ecf154f540055567d87778bf1820c5303b57cb7ff52f507c3f6dafc0dbefb37f1f57bf8074fb1f38e3c2e5b85db989a03 -generate_ring_signature d43a0443d5c3325225fa5986febb44adbda0422c3198fefc5c6f004c12eac890 48a03abbc676e7c1e6595e7871d3e47c65756783745b6c83beb3966c83b24426 7 54c73df736d858d956c6ecc78f71bfa621f1668529798aae794a2ebcc0cfe8a3 48f1b704c8549362e4bd701890d49bbc96f029f5b51740cd9af23be3c8e330f8 34d8123aa5765ff6fab0fc962b525c8ae0bce152a59c8336155f62b5f8261ca3 82f6e242df725fa43241d94c88c371eff160c9bca40e27c1ccdfd5465f8d1195 75ea08cb3c88c0dff75d744fe587524a5b3cb012c2f4e7299673dd24a07cd85d 8a3935e33e50f5f9ff79a529f18ed5c88340893bb688622625bf7007e15d5fe5 3261bc8eac8e3f468997a2bcded49c910fdf5b97056bdb5e81535665a23cd13f 55cd53ed388daa9179037a611a241494ad182883ba0fa337a9d6608a6f554309 2 f539a11cb38fa78190c72dd5f4dfa3250b89e2b3af919f8fe91ffa94fd5e7c0cbf0b2e9af4474c4e952d0235c131923050a04bab8414789a4c167ee3446d340404dd270ff04df4da5a7e301e9764ef56b816563559cb4cd2aff94e4566defc05de40d8a2e4ce90b661bf5a96b85ec8c7366b562966d7d3c4cc50963656074503eba2b2fb49139643dde2e53641cd91d53a95d373a4e5c6283a9d050b6379010c7ff82ee48198c40728c4a9293251a1c586c179196159533fe1d5b95341ef97014d0fd482ab810f60aab332adddced38b17f257fdb81788482a3cf02a064b2b0d5551f8a94daf972e7cdcfe747c8b7700f10c4294ef4cebf4696a939870d87e01ac8d9f55d19b9e1618a2ac7894c21672284758b3c285c20ee0254ffee04f550c5c083ee98a440821eb3776f15f7f96c337e3291c25f8c1527007a78ba03fe70e94c6d30c8fa0c7a331c793ae2921a53aed25b369f71e3e1c2964f5be379dcc0f49743115b9202da8862a013496964782283bd78a22d500767dc3a84feca7e10f80746423dc53a08f99db9f9eeb840c5c4379883a42df2a4543e670d55c23ad0119a2c04849ba4ebd15152c5236a3a61714106ec8f0d9ba6c8b908850c5c3b60b -generate_ring_signature b2a6d43314f32e184b6224f1d237368213d205e110a06eab248d8ce14dc66b35 589f05748c0e2fe74f2d93e8bf91c6dd062456acad1a8c440151142a36e37366 5 d1015e1ee08e69977d25d7e23692136b840a702afaa2ca04c7a1ff3d844ebcd8 bbde63dda3a37b486f0f12757b2e5a560d47a041f4bbba732ecf55846ef97288 839a000be768e6908d46ee701b19eebc912e346f98cc523c2d8ae63caf9c6c57 10191593db747ecf070bf88ee12a5f8c16f8ef506c6f5caac4fc0e0d0dd2b6d3 446c82636100dabcd3fd93bd36eecd42dc10ded9be9b37fd1c868ded993a8bd0 c188a0b57f2c52f5ab7ad0e98f85d05d317a631d20a45f2ff8d65e4dd9d00f09 4 020aaad06adf2a007b3aafcbf35d2cbcb49ff9be7f0e969e4deb02278406a80fded3157393258f5c7ee083ff744b34f887e8e729bbcf3c13336b6a1fcaf29009e8cbcf24af6ad6659ed9f5a142349d343ffc8b3573f055206823cd80f627430038591e3616bac855c3007806a6573083f609d32f3a3e84f5633a7465d0cb2d0195f562dfc750a95a5e0066bd112820959a64c139f6dc821d8f5533a6541b7e0d41d0105dfba7c3579da79d20052c022f3873199f49f046d350d90ff8b3659f03d8e2307a338ba175fef90ae978498bf74a6c6e0bcdd47e6b172825f04801300f09bd758eb4f229fc412951a87acb062d0e0c085362cbd7ff90ff8077867397095d7e9a10f2b8950ed4404dc98fe3547c14ea46ef33c69763f7c8a33cd09f7f083d2d14d277b5ed0918052d527ca9c484a16016f8d8885b57d4d14488bd8ace08 -generate_ring_signature 2cc6d7e8e09924db18a5cb8454d9d4651e68b7c40c10f2124c4178819c166731 8a63ba236ee52e329eb0cec80e8281fec17a4553aacee39d386e7023e8c49f1f 211 402a41ca99cd815d081c2481ea92da40524a3c0d1115380d080643fa3d9b0261 c643929caa547433cd00d1e0cd02b77e37f5de0bd28fd21018040431e47ceac9 989a1efbfa052314e3f222b0d6a1870ad23a7759208790c4fe0dfe84f9a682dd 28478f7385c01c31b55f667e04f71eaafc7615d65f818155e4da35a160cc4d85 94a4beeecbef6b573e9fd9b3e959fe53b47de4d0da7b9030066e8ffbb31357da 090eb96dfb16bdeed9a02046eff14c0b3da54c8d93d4708d336ad86355ba116f 7b77baa8eac66b27d8c3a7b0156fcd94d958be2551351438d075b91b63c33a53 a58ed775d98bc67e07945599536d3d2b4cc06cc37e4f230396218bfae0fb1c44 29320ad589c514098a8ec72b9349f0940737f2bfa36e9330f2d42c00a0120386 3a9a27201c7bb4bccef5dc6806d040c30a8230d34eec0d685a6c4e40751f49a0 13f2730c4bf1c2b5a9d35cbefc51d4f74de8ca3271033c7c4a9d0944dde08c1f 73872a978fb3ec59c4f7f86f5c008cd9ee9f54736b180ec968930983176bb464 a657bf78310f970b68ee552e2bda5ebe5da601b5245d2454a30c0c276ec18efc 5ec07e85ab34bcac5d5172a700868c29b9ccbfaad9858276df0f2eb75e142996 9af36317b0c92e94bf9f88b3cb32eac5e2022d67c5843fd761328eb8d2f85783 65631374959a3b4b0fa5538d510a13edd1f616c1a7b304f1916004695dea5eb2 3ec33d71054ad6db9e4c10680d2dd4dccaa408e8796ad3f04f63e6fb6c9436c5 eb7fcde5107a6e168a6bc7bfd51604c60aacf0ffbc41c4ee9f004eec92a441d5 4d6264610c118362726519f09cc02b1dadc926ad5d3a1d334d3203c42a7eaa9f 0053001c4a77bbf3aebd3956aac9b8420bc67d84947128c32d651d78634f2890 98c403100bc3d1135388a8899718c67ae72d86fbe32bf4ad6430eb0e5a871f59 1f0f066060620f72fc27072fb04d111d7890db94da780753c565114b28fc61d6 c7bc71f7658a142c8c2f32112d5f2fa2bda5140632b577c4ded16dc0e0324bc0 e1d43f558301785c73f060139a114b59ba8260645d3a42256fa1554b50d7d1e2 92b26634220d7b331cf050d947fc8610d91f129412b01c29969001e7eaa9b770 a6db9435005617130ddcfbacd5f432cd688054c7bbd5d6c74e35a6b87b36fcbb 8fab60e3f2b22ee77a896e2f244f683e0215baa6c22124227548473e8226ea42 231600af4603f92dceee9d17187e0218993f6f60a91205a361a63a1edd23f31d 41065d0f4a7c7bc1e0e1921ae8db77c6e8bc58ed6eea0cfcbcd8ed75b7a657e9 83db2a59e355af551f2385866b1479993bbf3297877032f14a53893db3c0eacc 1f286383aa01569fb3cee63a35dd32f41c575261cb68afbbb832b3c4eb9135d8 14157b52262d16b5b2b03e0bfb051211b111776d2a777872dfae2a1f3dfbf780 8b499e0ded8c18e93b012b857352ca03f7c53d19a7b662b45bd6f0ce0fea230f be7c42646fbf9d17dc2c09924595162813a1cd92b41ca029465240f105ff498d 5bcc54cfc168b31f99d15c2e23cbbb33889107430b86dbedae47393121c7e575 9115043833cbe33d775d09472a193354f00cca070b86e2f2c55a3c587ecdefaf 62dae9a21077417d38636f9766d2b23e8f31c11d371b941600a68123a0e5852b 2f880dd8a7785fb4f6584ca858b7a9df171393eb1dbb997e049c3c74bd5c28ad 36f51e55611ed2b0ad671bc3b5a67dc32e9424baa52b7dcfa886fd16e32a4a92 cb88c24731f1c01341bb16711cccf0262711c5d538b5ee3d91e618a0aa94ae57 774ee3b25c731b1563ab1a4023808d1083f4d019438eae312a071e0963320b73 80f340dcd9f0edfa672adf1028fbe5e7933a5f412609062951c9c13984477c1f 846e5bb742c7d2c5e540a41b112dc6743c145a76dac105de39dfec0c03804934 6c3731202f8f8da0d540616f32fb1261187b0d5dbb8f4f86e36324adb50379c6 304c36bc24065978e078b24b16bf8811bece463f53926d8bf4cf14141a8accae 6e39e3ac724c929ed1fe8d2a1bba08b81b757f32ee119a07f8feb66c990cb484 2fe3718912375419fde8d55acc1e9f7ae8f3e8e4f7e8c253f180967ce9f6e277 1422514f9fd55ec628162935e2736205f7b19562e10f09a24b671fd3d363e807 ba7c20056d4c14ffd0f93dda7c39233f2b9f005dcff265a9ec774d54a16f21c6 4e024cfd9598522249b0940e0a532f8a2e460a06db24702c8503a7b88d8ba9a4 8bb41c4698f0c4e1afe52d5110012d704e063471ae568e4ec407fd6a7f97f179 1e5665d9af4551c32f5b41cb376f6ec70dea9a3b7c8568a8ce24c525ab1cc65b bd2af664fa7374d46ed3a2d6de4295b2b6d6d241af3df086abe5b9ee7765c8bf b598e6304c34c0a3158998db4877c8025e27de6ea53305f445f774a7235c60b2 7817004add781de4c2bb6afdcbcc88165a8620994a27e96e822c8b599cf9181d 402afa1c572a3e19f68ec8dd5c3f2ad4af9ec3dd0685019da41b208fe624dfd4 e1ff43e5787e2832904d4f55ccc3f24a896a673b5841cfb82590ca8889074818 2b8afb146c7f80094e3201010ff0f6c0503d4c916219e7f08523b6eaabac0627 671fac59f003840250f4070a05f89d73abbfefa4f105bfd8f85f377db6409f70 bd10a4554ebcf405fad8a7371ea2aff0589d0c554e83dda524a04663a7d3d694 81c03045a55f1c812b2280e0cb4fb6e122466d47ec60e10975eca58ab9a13d1b 1b3dae6b495af9875c3705bf1ea6695e7bdaf39c20947e061eaeb820026e9920 d89c734cb3226910d0ce056da4cae31648f5f7689f4879cb440afe2b783cc92d dfe58bd956769ab6129bc2966895f3eecf98d596a15b90dce4d41fda89f51908 90b7690e81a72de5b64b94be37faf4cf2e053c0560786cd3832ac0a887b6c2a0 97f5440f4fd3fe8928b1da92089fa2f2a2cfa22ee836e84bfe6f0f0ac2c0b639 78733374ee6577eb4aa1fe1dbe30fc49a26ca6709515f9fb9ca30ecec256e525 0cc4bceca6c04f76c8cb0579cf41869b523ed6dcbd0a7cca939040b113c5ecb1 a77eb08d2f61991f1c5d2d5c17ecff62b6f355f2c184586458aaf6f8fe1b0c47 e904d9ccc3b5d0727786b623997e85477572f374a157dea7a2ec15ac0b8cbd5b 529a15c0030b5ef91a2b172d050ee093b6a008726a5f30b4ada8e51272c89e61 e484b6e14d164bde5bdf5e11ea0360f4b3751add6dcb86e0609060903db9508c 678782c7d1fec628538b8bb22e04867e1cd37b82fc3bd27af6376049edd52027 7b75224203be14b0d9648feb66944b90a538a38977dc9def8515fae3d3b65446 471cfb7f41e6716e40f1e43fd3bf60619bb61cfe9647bae7bdcf159c6c011b6f 9a9abab7e9ba931b3e07d8b5f2a6b367e83b7fd27b876efdc15ab64c56bb3e99 3907d8e20f2cbdcb064d0f0b5a9cc39869aca703fea645e487ae7569fca87229 46a2823791014ddc54c9d56e45f469871e4f14f33443f7dc337d5910beb1aa19 b30317411d74a37484df3768877d0e92b5522e5622cdeb19db037688633a764c d63f7433d699967d4dea7f0353aa6d83a1db6688bcf05eac535d806f2861136b 05e06df4e9ab60de679307e3b12231ef37c0a7e8bdcdbeb792a140e0236486ad 543583fa163484a44a43267e2ba34db90e9ed2cc570b7a25c0e45b22b9ca1eac ca42a5c54f83e796076e644a436af8565132ddc1d075b95ed355c7f2b0b556b3 f3f5efe8d35449192c8795d2cb1059b720d0e1044a3ca69b1f57962fc819d56a 89bfa2c9258aa739d4fdbc65fd3b6f0a544d9cf4b22de25be25b036e9d9ab2a2 092f55c0c3bc3a275c4ac457c0b15d676c8d5b2a072472b624f1874fbef6c372 11595707e8ea5783c592db350724d7463f8cc141fcbc60a9863ff890d783131e a381570de73bc10fb7f4b4ea3876d21bde840c0cd5aaed2c2521cebbff77d258 c629d6bfd1ea0dcdd46444c5a560a4f386735c7e1dfba644e119da62637e6edf 4605ed8bc6ad9c8734a4f9fa61964edb5b73f1746b568a3525b088b7e4e28738 d2bd09313210ff4eb285b2c1171af27b1f7d3a14cdd78da5d36d5df33821097e ddecb9ee20cc01e3e00884943e5b284088589501ac73c033648430b6403bb0bd f983fa409d58a694b709658f79f488051a5a3627a1f5d3d8ab4f29b88ec85591 11b78bd153835d415546c093859342a069c2f9179fd77487ddd724441f8a1d00 a1be919083bb6e1f0e9ec941588f3ff0cc80bef966f32c1e57fe51f9d740ece1 c5c5a8d4f9a3c212c8ab2cb9bea0fe4a2d1b28ef78d0d92948ae2bc58d879924 2ba7ee31adfff772224a6915cc1026733d9750ed78b0a6003090d01b5e4800c7 f31856d475a6d9e542a8c52940e65ae76aaf239c58c7af445011118479ba2e97 d8ee080399b781b97e9e320be1efed8826ab3332ccdac22c412b96b587e49f3e 046b156f6016b3e8c4e28ec46e6a894a14bd085fb98d272f954646aa6a68cd8f 67da4100ca141d0b335ce9aa673c8ccb1c241d54e8e2d33cefd70fceb5b1d25f 93bf5b1c7a6ea142589637ede192df057328a7ff99325e8d143f5933e0e8d094 2013b7002841ce5b4c162f543ce204e55ca37c290241703cd444638b7e1956e6 e1fb9f7f35c2ce2f064db2652a494fc4132d67f1435d2c422651282e2d5936be 6b84444a3fc1f4ed71f07fe9807aeedc3d765e86a308cbddb549e41a07637540 6922d129dffb6f2e7e05e25d67a675deacf4a3eb7591cb8f411b4bb02fb352fd 807fa83b273eca0d0cdc181b9839f806164eac0da439354f27bd4c999fa3e112 ddea38959723583da1265d5bbbc23fd21d95912ab44ab7bdd388b8f816d7c950 d31e177c298fe21d24cda414ce09ab970fe07a16247afc1931831858f682913f bf212d78a9d4b8141c0e1e03b553c0be1bdfeb5b17cf2b9b109a1dd2f08fdc19 07bbe8caa6552571cd4fd46f165a89c5eca829e04ababadc29fc862625ef3415 43339e682fea6b57b40f7c199386f392488077429cb491f0380d649a87324411 46c1cd36c1efec4e065c8e64700fa8e225fdee7e7b5d0455184acb459785e924 09ea83b1d7d5a7b580b54e8e95f3204374bae8ed92e1271a8573d9d72baab4a1 8a7700e99533fcbbf456c9b22d519a25f1f5bc3099c71aabefee801277d0c19d 909b328ce25f2e5f270ed63fe45b43d242c03c3f74f3ff6001978ebeb418cc61 f8e3cf614e7bbee6985e1d5604dba2374630bbdb9651e91dcdad5ab53409630d d6052da08cf10157fb2a2e3b44c85cf76df325fc919c588086972c6bbfe193af 9a3b931fd12fd1f0d68fd76d68e44ac2de9f0d82fa476f50f0dacafcbc38dbf6 f07f3e41a4f7f79500cebf1b351ac5cf8346b068ab1cbef602fd60136b20cb6b 15f00a7af5e20b918db78299f63ab9ff993ffd9b9c3e50d32b89d8d68f1cb6ea 0402a24ffea45621cb1916265651c80c570a55e8ef7aa8577afbe2b15f3560ab 0ca97cc9867b2edec0d46d9d0fd14071c4c904025fd0c5ceaa5b13391a7303c9 755f312086e70719cf72de5564ccdb28288ac1fea0f41611f5437754cbbb360c 4f865ab94b9747b5219e5b62f06c8acd434b8ba71f9a49656bafef74e0fbe000 eacabe372936f28985b08db8fb4c2f8c9b4a48c261d698e77ecae3c3232c69ba 66e7c6a7231b1d3fa2771babe3dbbd42f1d32cf5f80e8112dc65ac08ea630c05 826d230e93e779a6e5d9ba481cead54668b91f7934e5a47f6d2067356d88a432 38d8c90a333e211e35d18433fc8e97a2912a4481a31007dcc3cfd7d02d58e445 b89ce70d879d1748dc572e179124da2922b52a9561c4e5d4ef787e65c3092bc3 dcd03083e2edc956b86afac0b6b72eccf61a96c0b9b1d8c49ed5fead44c22164 0b3b6bc0b8dd2aa42ab1909e31585da82da55ace9c06a9075120bb1584c4e65a bd7ded926e2626afadb1ba4c6a6484b02fbb328965bee31607f24c1f11cf35bb 1b822414e321327b717a8b73a68759528b22454ae4f1efbbea4acf27553bc7b7 6fd47667514ba98fb485a526bfd3eabb680eec1e6f6ee9b0ea242f051dfcb62f 10eaafcf82fb0ed3f7ac45f994eebacf1794bc7b484c3bcf1d4385534a7ac8cd 2f874b03e03e634e4afd81c541ec6061d956fdffa59940360b6be44ba08baa39 415a29f704999c6a1d57de28f50b6c8a28aa3a45e0f090971f0350169cf10442 65cc454756fe9f0afcacd7ebf5ecef668966b8d0a689ee9f8a80a0d672662d93 ae853a35374a5f088e4811ce400ca24b7292209f5591fb286a840f811a1a3a4c 13fce6e2bbc79ebe247e21edb3221333aea417be828cbf7dec641711cd009c0e d0d1c7e0df37e6dff7fca30ed034b174b6395667162b7ec2f81922433700006b 11700c2375553b574770775e7ecf74d356024c6cdb73a12040f899b072febab7 9f2045dfcf371ec181d645514a7200019f6917be67e5b29b27a690fdf434720e 9211009b28ae0d38739fda0cbc46242b9c091dc864e3849463f7d5cb6eb4224e acf85b5927f99c616e7f0119c2635f9e56689f1cf771e94c29d2bc6af30e3b73 fd8889f59b8fdd47277bea541ef26a7861a890898a15fa55f19df6eaac438054 dc0db4e6550ec7cd5ce876416f54c3bc59447b83c02415fae918adea6df7a4f1 29f5752cd58eb91887c95c38cd3691a9b404c99c5022e3e644947c58a62405a5 90d99d090249b1fb30482224f3c6d3dbc285c8a2f35a417a1a4559479a5af592 81c3b1b54dce4e4bea68f2cf57d5d10c6eaa396eb66880590a9d68409ee96e3e 8e78d814062170b9da0f2df53876e50d658e8abf5175be44f1a847b25456adc8 5c8e9a46ff66124cb418455a16ae3b78eded90d8d0e6b87f69e0d2dae684553b bd3bd4c32d490375d0652524597c89eb2a8dcda40278e9e17eaa0895fcf3cd63 37d269d95abc4010b6dbb4a1f13b0081701a625fb496a554ef50fa790503a0ae 069716673161003f4f4e03e91e61e4c35307be3285e9b9f4f96fc87d9256a112 3bf47bfecfcb4f13b54c8a5388623355fff92f4e991b1439ef4bd2c04c25abd6 8322ec655d08bd2297a67c7d218f22caa679139679f03a0bab4c0c07e74f5a59 4c7ffce48eeb64c719286644bd4dc84a6ca8a09976cec4365ed8a9f1e52020c3 1e66c12636713e73ad0661b537ec450f53bef1f34c814f53c780068c3cee944b 3606237da7504cdb79140ae14dac5e9970763a3c9f4a7d74a3721c93c7e1431f 1fa3ee9449761613dd39dd33e274e6d86709e83d747edc926b115659fab77121 14c7b97c2462aa3bec7a691242367004906e2b2daeed42dd2ea8c0aac75dbed4 70c011309fac84d7b2e71fa0702b49b21fbfd75204605c5bacc0c74dc5e8dc33 189ee51670246ce6697a111207191553afd5580f11cdf6859746993754e7ff80 1131c9a3307f39ca82c6232e5852e4461d0ef37afdecfa26b5022a93a7e5abff 9b130da9fae53bc05605bd8b9b7a015713699c5be1f02227a9c74da3db31a018 2ae654a588871677c065d03dd1d1d4232e1fb67b191a32573926c62d70ac7b34 a1a6f173a6aebf9df3f322eeb425a596e71bfbd8b75148232328b593059397b9 deb8c6156bb9b1cdaddcdcd83b0286e746c59d2888dbfefcf77e5fe1cfcd72e3 d007cf856dad279f734fb20b0e5c97839ac2ed18a607f8581b43fc5b0dd076c8 990073943718cc401ddb885dd082049c0168da46cb6568a56a4b287c9d959fa1 dd5c9a93ea17ec7174a091e41d91f3a03666ebd3411e5ea74938ca33b05dc162 c182c5d4f1d8be0d7a5f1b6ee8d126b397fc5d7fb52dd58b0f42f9b1d65e5d55 7adaeb0744f933f647464c92a39bf738067f6e1b4b996b4f2919289f2fa5ce9a f9adbfd726b517b55912e28c83f19d182b8df969ad4b12347131fbd7e0a8a2ee 0073fe5086e6319f79cb9243f359519f00da4d3be8855fa79f4233f8a50c5874 a6c4da072b6da74e25de82ee19976195b8483fa6a054856792d89b1e1f68ee44 d5f638f1b8eaea5e2da06bb90bdd5466830f036e6093df3bf23d03145d315529 1483358755a29f66d238dde5e62a125d50f6d22ce820553e207172859000a96c 72e77707ee4d52fd0ccfa34c57ed5d2339a69f39e6aa6f371080a3d414e25505 b7e9e15b52f4402ca9fe210563a213e1dba8bd02d0da57440a03a98980550a80 4b1480c24c531e5bee169e7ce570d89ecb0f0c7b3d041fd93f04abd1ca8bdae5 682b6bfc350ad26c92b229abbf92aa82a3d1c789f124c63c166555f4f960f592 94e2a267bcc92476200aa4d5fd0b3d6fca8f2c6a2c69623a22931b8e9242ea40 8d09501afab7b3142a44a531971cdefe348be311412347c0ed1ff2be5bae723f c6a8695fdc9043bf1a698ee3e2df46bc211ece66e0a5215bc16c8db3c676ae26 fece9504781b2b510ce438143395d3ce8f6ed69dd1179b9615680746b4260b56 7e43c14f5b5b2dbe7e3b37f9726c32a04244507d09cc1056084afbe29b97ea99 feff4768795d67539c95ebd9a89f8482775f595d8ef3852a988ab14fe95f5227 e88d342f5edbee919220c56ce616a548f801da5186ecf00a3fae3ce4eb163fe8 bf8e39152682f122fdf93ec892eda4246911194534a15293badb5e09e3c110a3 6654a6a6b4a0f3111b9c1bc051403c3d0a519a5721718e370190d833f63a8b88 a05d4624f0b50a2d8cbd071ad862012b3b01e98b69969f9e5d17b7660396eb4c 4bc3d8c6921685728332e49f6f4ceecb659504966c1d3bc49e57a79422c1e35e 3c497afa0fefe3026683a39fb701024d7ba204a2077ec0daf03d6bb1ba9ec005 3034c04416c57bedd19c75e57f79e9330bca3456c43418e5a6bf002d969b425c 5aab8831da8f0c192081f901a62c8768ea530c96a3b249aa9cd0cc83d404eaf6 ec54beb7e480aa22d6fd6f0c785e45c7f2a191bafdfab0bb07d5b48f092d9c5f 9839537dc7da3a8141b41c159b91c769d0cf2ba6e68b94217c602ebdbfc6a599 ed86db43e4a4868efa115d887acf04af9da75527a4fcd3c6244b6c4d61be22cb 8c507a0484046c536423e8d9af4e35f74ab7d30e0f3d41378b95da880febd57e ffd1a7b87836064b4247e77e6d4e88e944ab3aeea5c6e069161227e0d540482a 038230489f2a54958eba36e8609ead2529e3932f8b89c4204fc21ad19920adc1 ef2b4e6713f76a01478aa2d01d3caa45467de753c810df2f9b61caf0a526b699 abbdf138313206132f30d3c3376318e3c2a1754d95ea64e14b22e60d8dd8af54 5d3dae9e4dd24dff168fcbe2b21ff0313df82a1180b7d7aa0cd4aa1d9584bb5f 1df861a9b7b185f0eaff1c005d7c2bb89fed2ebe0f32f478d2f345bd678b49b5 829a61bcd2a3ad8a8d419c5c8b7fc26f2d13428fd97b7c54d6f888255640762e e6fe97cc23d26246af4db30b1860072a4841915bea8e575ddc5d7739a6b96954 7e5dde760460e9b910e216540728b4ccfd67182c070c6ec2afff9d1bcd0674c9 cce6c582a6b4d913727ed464544472bf199cabdd81769de5009814887650660d 170 ea82e4ba658f34410739c4febecc7c08078e0b62a6fc4597509894fa651a54089f3044db749f92a8ccff2aebd6063c91ec6a1f83de8e0808b177b70bd7035a08023fb3d69dc9940498d6cf2949daffd3171934aab529b7866f9628cc9b0eae04918e5dace8731a07508f66e4bcf9775bdef966ce4cd393c8fb71a25437d53209cd3b7521aeb7ad211a8c2daab389f2c263cf57999278d9b86e67eafb3c2623001c62aa6ba5e6d685bc8c579580fd3c6326ca0b267a5c53fb11eee3b7dd9cf5009106dfbb0e9388a1474258baa3344b41bf28d0deeeec9b643e974d92f6291b0bf6688b42199aaf9aa0897dd9e6a9d683e9d2601512cccd6fab39c4b93b275a0ce319c00e1a6ec063db3059312d7ffc149c24adaa59c42e590fcd332371fd4200da0011ac335c02ad7531056a58ace93ccc6479b5d6cdc838148c03bb01047e08da80a00977a41e2e139f6b8b899f8ac26fde88d7a84b6e5fd1e61833e4edf605d76b9c21006d29ace962274303672379e9a089bbede499f65b3e2d909273e40dbf3787444cc0eb17554d0fb392d57f437fea6a79042705eff3de784fbf60740e9b435b73a1d249eefd988647f3a165e859209183c36a58d60f4c1540329ad7078ae62561a9877795dde84995587a087653dfd767d2f176053bc4b373b62d7e0a054e93fb84d8066f0aa86c9ff4e31f4cce6835e3f95b3830a2b447610d36bd0354c98ee84bc7c352617822d9aff35fea5627c2231dd169672697981118cb3804afd33269e443c2ae2a8f1776c606028e4c6a04926d03b9661484f491f9a3b708908c658abeddb582b959c7eb022941f8e62e6cb7dcbd036bbc3f5ba8b7a2220054ecd2798166942946b837e1a7ca4f362a5b8aff3f183b155b571eca80880a0518b6340d2a77dced510845165cff01440f68397c53f61b409cb715b146714204c95c85a9c955a15df3565ab322eabb01632a4422b3fd83faef6ffdab3a2eb602098b926a0e0f70b838bb57d40f3b84012b1c0a5f8116b652da3d66c335d9cc0ebdb250bb55b877ad4e56a7b40b5907761f3485c0b56c6ae15892032165b14606fba3426836f8f3e7329379514584a6da97841f77efc48d86f9cd96c6e37beb0b1b22df1225abae4c98529c6e87a43a99bd5326c9aede7b1e664e0796e6ddad00e20c0a23a6d2fd0b257676bf7a56b7ed86b1b7b7bd36186742e538be5b809c0baf17056ead2c67b8da3a7cab6b508ff3ecd2b3b69ef7cae476e66369c76136065007b911a3e0993a156113ae5947962a5729d455e555f70546bd61821910a80f7081ddaf56841863c970ee7bd52429133eee999db65017867f5e5f2a3b7be3059f9835ee8ba1edf7578cc00cfed61278e8938809f685868acb94d6f919f1230ff5c34e89a25c04cdd64b892ae9a58bf75f5f9c6bb701b7d533a53207be0b930ef088241b033a967c4943d7338c71bafaf7bee5a0e58acb7d772b310ece706f0102bf10278c0171ea744e663b04accbafbc2328bf401aa1d5836408bddff5850dc3cab068a3059c4a68934df5aa446e3371829718357095570cac8d82e4132509850d421cbae0f0be02d919888c5e3cd32dcc1f4e883885c12d2ced8d740539071653045621f6f5fd88b7f41677ad932e4341899e6d5c0d390c769dff982b940278b2635d69f3f8bc4dea693058de53686bb6f7771d2f993cd0fd4eaf8520410231b66569fc70bc8a3b28de7e793e5dbae35a12d9c8b01fa484c4e5c2fbef4e0659eff7c948fa96fd9a3eaa8c89162916ae13c32654b6cd13c4d6cc1fb79cb70b5933434543f90e05cacd051abb6035344347334c254a1a940cf769b079a68901fa07b639ca3110f56c9f1ed5d6ab34bce27d9eb0fa5d5adb9d2b3d54424ba70a6f64b710ce76f4f515d23eddd937fdbdb4fdb35069bb2fc065df2f5a488f2f0f595ac58ba79ae6926bbd037dfc213c57a4693cfce1a22182ef5a703b07bcb80a93dfccd673acdf5deb9c266f775c9f726127bcacee35e0067f90749d994527093f89199b67b7aebd8c76cf68c769b64696d51fba4a8efdc7440df33de2214a05aaa1038d5608d57fe65af625528612fb46d52e3b64f47cd0e3d5211c8291d6057570ed14e632d34e0c67948e5fd9c97c7b582c70186e5bd1103b93357fbc2b08633f5581ae94ddea01f51b46b5ecb450aeae04e48e1c8bda8944143bec41260fa5bf3b24c9582632a60fc6d9921d64a69114ca1c5135cb55f94cf3c74a0fce0ccfa960df7e7f6a393fbd4b8ff87886b9f91d13385fa20a9df466d82b11ed54027004d239b763be04fd871018695af7701fc91562f89c485227638309b0d5750f0fce584bbb68d47dd659ab095e168bd524516ed76c99704d9b34a84962358a092f72f623fda9b89a414991030ffeabad1525ee40c5b651ef10f564405372840f5548955932df0305be158029800af45b36421743dff718a385b620efaa911f0697930d84096ebb3e0dd2fc60350e210532b7bde44d83746eb1be631c555d8e0ee4c37684906af04102cf63ac460938bfa90302f734f5b60c283a2f885ebb0e08ab5aeaafb2d33cd9ab3363094329674ccbccf7c36cec4e158a059d7c338f9703caf25c88f2bde6105fc530609f123955934a98b11faca11335b3bbe0705e8902175a8d746200450bc84f4d05b0308c9331d29d9be14caa5a2edc0878be424906e4194a94d7f8e14b692285c73da0fc5697782edcf85fe648d691ee6132cda8053464008153251c2efc5ad3c295930a1b6a33cd62def4da8b2aadfc88e69e4709b7afbc4ed1495cf3adcaf0333f22f11e6a59906605cb7bd5951461f7915c6d0d7d1710786973745ad7920568de3f1f266bd502ec31c9ca177d3159f921168306243b5363814c6b068dc7c96206aad242c6dd3dec185964be40d8ce7faf267d0566b3ea1f3017e5b12769483541b35d349d7908d16db98d39ac22a3b6b72ed1069cc1844ffbf2dc5fe2fc75e7f43a8447ffe1d08835d8a261303c246dd55b8102daa70651c81d4d31b2e2f9575aba18aea66b277d394a8d4c78c22863ea2c2a03d379ee502535345a68805668c7b99d3df69c74e5ba7959836646572c7705610f1ac3e2468e373deb7c134d1e979d5d2c05ce4be03987d264af8829068639490494cd984d7cbb6d24ccb5d900aca12928df14633acdec01bc4332a70ca195ae025f88a63240253cb37ba8864fb5194f898a32331714af54ff44cef61a500ffb0ea0aeac16c521cf92b344a13407e6dc3b266b5ee1610321b7de87ba4dcbded309eff15f61146ec007a1da5b07d28b0e32a81a76e800bf8c9771478c2a614a080b03d3daf34488df7510485a63d47e5d03c852817a924bbfa02582846fae6ac908e506581317266b3404885faa619b6e2907438b7e1cc388c56b30cbc44e93af031425c55f84316d1a4033eccc1f514ad544ac9e84b80e0fecb96f56af70a17a0d3d2c03f640d4856ee4410ba5a689c9ef97cac5ea9e91af40a786861fe61b81013b8b6b3fbe4906fcef61d38e6163238d141be7e80ae1d438c5b9f19fbd4543056b8aa7ff5e4f256a1cf1705ea41a100ca4ed369017ec71f3766c75d4bab01201806c87789ec0f1a8f9339da9699ad530cc3cd558d66f4fd3397fad7d91fc8c0c0fc8069473ecfc66ea6713698a9d9f21d55e09439eea1ede9d0b7b503c7b8703a242e0b0d14acf2e616272af6286de322d8aa06e49fbadbb16c1e245413fa40b19a25937b6768d5f6022fed900e125d27415454a6f0d3297af1ae58f41ead50a4309eab598976b5b4f31c4170dde86396f7519dad2841e059d0deabb311c1d07764988a77e1d51b54f79222fc0c6762b8066393faa7ed9af08de137dbeeb3d0c8cac5151a57655dc21e603eb32dbf32e586d7556d8795830e3cbc0712a693a01db7932a4ba68960814e94f11d0e66e6ec480a951e6f71eb728817d1ef9aed90cc1fe35ce64a578316ca373a229b783f88e7fe52605fbf02009f34935bca7410a7955037fabd3de7023342b430bc36b50a8ae68fbe346898bd685958aa8dfd50f160346c7d7ece3a09685051e8e59293933b64ea7f5c4656e17dd39df19b24303a3f8a001956f1c6bf2ebc9bfc6dcd14ba1b3cd78a3236bd317b5002d1189860902409a36b36c818b5e98cbfc324eb85d71bfebe59eb2cdf4defb40f197c2fc09085313f3d580a2b0b31fa62c8aead58bc2490ef88a1cbc6b8b344e3d2cf29006acdeec7ed75053b2a713b3ed4bde58e12290fd3bc49b44a9d196e89958e0d80836476e3de556445a6eba672f1ed2d41517f6e920b1797cd5b1e9075278db180bf8adbb24027fca68a5b9e536a22023ad09b1b3881e31cda17ae419ebc3d5a7019fd7211250fecf4af524771abbafaf813f8f8c479a763d1556bf3f1ec140a806c3e93c1194f8194ef338dc73c71288377d6fef28a6a2e02a01daac2a4c541e04eec3befe0b95e0078dc38c5605793dae820cd99446c19f5074e9d19222eaef069550de74671969287a8effd874089acf1f2184b01f0d08322e957b188090de01b26c09c842b0f2beed7c491a8e621ce897b13612ac2c6360d19758c79292b207e2d3556d75fd53719078ad3a112b49e86d80171e3cc97ce7fad27c16c65ff105c9515d4a4f17a1a1fc439b053cfb8cb2117f2847387cd26c719a912818c67e070bb80992dff1f4dd64e902508b7ff33fbf841d2427022fbf51dd4aaeaf3d6c0292a7c59e6a7bacefa10d5da2df89f79a443f840c7b661e142e996be43f5eed04ebd89587df6e2b917d45ac6d9f4d275ade6d0824e199b4e4cdfd76463648380c4638de95def2bcaf1cea53fd876e3698716ac20029adbed1f5905b400b31f40eccb6505e6000e12887f8ec10950e7d44178036da57d0a298527dda5257ba1402ac2d6676bd8ae155e4beafefff3fc7955edacd2f31d688d5e23c170dc08d300be162f3337fbdee3266b72b3dd9d0db781996176baa16524bf1cd90421bd6dd01ddc9ce76f6d0c9bd2918cdfa202ba49b18a815473263e1e2f6e23c54390c4601f4cc49762c2c3733abeecb4b0bf00f9e540e2aee6b45320a755e7ba68183480bba619c4bf6750650668678294da95c5a170a00827d62199a73884d2fff3ba6027586973b62f6c4a2bac364ddcf57e6c3c61968d6efd6933b1b81d3a1d93963093c4fc335fc0f0353713d4b1c8b25134c6f800f1b5628ea1361edbb7077819607619b2156b5e161426f7a3fc97531a655632071d843f573ce9aa2efd14e5d7102577a07c306685b0e2a3214f01c7d125270c167a3caba9f7dc0c6bd37512c67061318180f4b5e470c42ad4428f8460ef8d1399cd9e3a90a24c8b366465a8b7c0fa3d5dad8b7ee3a4b177d4400b3d84772c535d0b694762b6b47475e81dc91910e188f4c5cadd7fc51c217eb99c176aa78852f3c45c24c1efcb46ebf38fa53ab018dca8c99bcef24c46009c1694756c70e88750ab8813297ff55f1914f51b7690351d86402a2117ec62963ad602938c6f05db866f9667c9a4445cd86b1bd518c07f54fa73ac39967a88066092d369f2102584cc09683860a6713f3612b87e65f0238684a953f79167b78ebcbda155b8d6ae0e61b01a03a15ff2c966936e34efb079831ca0b7b9c92bfc8cbdb583245a8f6a6770acb465f6c35353d11d43de9ba0b0a45b1016cf76d0e6035b121a864e803939c85ea3ff7007c4b8c3c204da17b07d82b2c6b85d2cb097649b777d48d8bc32e67c88bbfb6998dc222e90a6d8f0500eb8ecc4de7adc6ffad76b2c864b714d5b30242fb8680c49ecbd97abf20e08d05d5c80d607e847c8b8f08119bb58d0aa1558a4c9f40256b9fc6a41f3e1f394e082fb8c740b095a5809348562218f8bd8409014d7f5ebfaf53840efb0138d4650bda7e9f0d768d70e4f133c9a9ddb1bea36e67df24d9de249bf058e53a151860091e3f98160840ec50174f45cc3d7ff2de1f38c22e5a9afad25db629a7066fb8035fa6938a8d0b07f9653f07517d9b9add7aef85de5bc7378c42d80cb517546c08fb77c47567ea0d6921a16307f1fcc67b2a022a04638d9de5c4b23f338a67680c372794dd0d2625802d623a53a228828139f126d1cc2db6fd1e13bd8d414c5109e99a096d4f91f2d84151f8b9d18f9213445fcb21a5016a530a8f61074c67d904a12b923aecff801afbacbc6f40d70df9fe568e182fd12e1122b9c87164509b0e86ed0c2fc1bf3865d44ecdb861e4564e34f95ae61117031d7054ea9d7cd1a90c949302dc61928379c496c3edabb7096aaf68996451c4c022ef143843156d6e0929f0fd47ae8e20e072f35e4c28b6e208686d7b45c0f45345b01b4f3421d0ee07037f56bc0446ace25fcc393aebbd984ab9b1a07a699a2ed18ab01aec144b930068383e648893ee195136515563b427a94d402ea6cb0b3a5c184e2c19945fe109927a7fe2b2e73fda2084d77b34e6972000eb161ba6ffd63f84af801b81f799091d9cc65d4307e2b1d307c70e4c72c38f6306505fcc66b8dbc6d176f9da92db0cf82b8f13050a1c60964bd38bd63391c7bfc58980c41438e75b17515dd338250018f039d0a1f317dbb3cffb1cc60f35986fcfb7e815b8ea93def314457b58f3087c0cfef3a6793527bcb11af4979557df291307cba7b423773e6484ffec83d908690fa62df2b0e24312004abfc2994eea1a65a4ead06018da44cb5faf5597ed0abfbf0ea5fe176070399c41fd7d72d4dfd8060d0027db4743a4f9cdd5e445320d60679d56bfcd0a2bbcf4bbdbb25a97f1ba3f858476dae8ca56e89a7f3dabff0151fa98c7731e3f7124251ee86b61fa24c883bbe593ebc1dedb906a612bbd810ae90b978ff0083f1ac026f7c0dd9bcce74637bf3ab2a34367964aa0ec2e9cb603f5dcd16b3b4031af45fd01be83037ee6f213c8cdb9ebffa986b190f571886f03bcc5f533ed810015d84603e0f33a13d5f759d34875429539c75548a6e2c2740eeb790e35a90f4d4192c7a3ed18dfbbad5112b54817ce3deb05f622f683c70a08386b384674f84588abe318d7031bff03e8c6f7908397d06c15f863b42de3270d38486faa9549bf55ab14d6b9c1249f1ded1b0a02eeec5046c45fcaead486ea07387ed579309ee7baa2611d2a659a42de78164b008ca2652d86073080dd4ce7021c11c666fe1649d4234952e7d30a9eeb280c4e878ae2cfaf5d226bc9bce0b9098e26aaec993bad328f8db4b24a0a77447604d623a67927e186236b0cd2c1f10f7f28d1ed128dca96f6575846bf1435fa0f1d6cbca9ecf0eac2288b540ff26c01d3cc88fcd1ba73cc5f43d9de3abe308cffd736c1935acc62ff7da34610c2f9002c509d32eff88e593cc663d94b1b0504a00a9578573ce1617794994c32213b0601e782d5da1a630c3f79c54d1e9360fda61b0af5db28ee79b2170639d67d2a0ba3bd4fbcd14875353faab2a1b8c251563220c066f386e6c56477378044f58e0275e4cfe864d6eef19f34b7da898806430d461fe6df89448ec9dbd7c8b8837f09b72f4fe32f0afb9eeae48f411752e140f0f27f37856a27d2791642afa96d0a064a18017d1c2f751e92515431bc0972eafeaf4a785a728c557ca711d3958cf50aa4bc5c088f738d05bf2d3d874f8fec8f0b08709386792c6df3ba6c90deae5a076205f194ed9c89757694fb62abfcbf49199169d29dee298053c066e3b04c6a05e5cfee89cd749cdf860bdcdbca95d2cb9560aa0a7f877ad40ecad7a098944a006989be2f14582077764c54991119a2c8fc026de98da44a04af8630aafe28f3041730337a2f9b2854f58493ea965a713453ab56a5d6cec461fa3e5673a9ed6903fe6c77c2449257b2c2a4e6fa5bbdf0cfa7f2e3cec0626471aa2fa00c842bc60d0365a1b1515fe36a53026a9ac6da01c508c50196bf6869eca5f3d2fd4328f308aa21b3d4e736dac9827ecc5eafc34df0d5d56ce1774916c44b5ef5e6eefa7b012479b8762877f381b556b9d22dff3fb16013e573d957b6bee4347e11f8cd340d7384c733ac852a6c41eb5d6bb314d0d5cb0a9b03903a734008e31f73d6532e008bfcd392595df1c73e49642e11be863227a941788b115ca7155d1783f2d44606a6d78fe1a7380b4f0307e392726a5ae2f34a57066e8a21a63d4ac0fddaee53031c6f98afe310e0b79afd64b01dfe669bf8c2a932b3c4db36f760a459b101a805ab6ffc9a717bd8613ce14d0b17ae78ab119403a154c9c79c26618f60f73e730d99305da08c4038adc745f7f4d2fbeeba4736a7c7c9f170f1cf9d31e8cb16db0acadb13ff9c9a373572a45b05551a3a18cfeeb365f77f0709fd95a39e144b4a055d5b0a207bb93994acb3deb6b1b8132508c3dec36e04d30a029291a7a9a47b0872b5108073c140cb4f3d69ef34096558671e9d312b6f769c9c022e76bba119069fa4fb636bad85f73b1988091c2444ad31e69a15d1779a9b13d661513537de02a10c1529992ece48dd9ee96a20d80006397a61fe6753af5b904c39cd5b3a850fdfafe46902859eebf2719e0498605590752c66f08b575ceeac2d3cd3baa35100b5ecb868b30adf6188049e0acf553779599804427e74cfa6451b782d420db50696e441326d39d3b3b52ddaeba5bd499eec4e7ed35174e87a87b66316ac06750416a43dd86fa575200b1ce704926ab53da40ae77c63dc87200bb8ab2a90ac8709f9aa861f8bac52bd77f8795753dac43e5030cb4d9575f694e87b9ee0179ebb002c2f4172e893c267fc11645ee3cca1d138494a7b87be31509782f2f9e1e05f056e47b72b06ad3d33eba0b7e37e8ae875d5babad59825de9d478673cadce8f704e1724fe0c47ec64a6f1c255babff74d212a95f261ef4a86febe1e22cd8ed6b071742ae32c3b2c15d1a4f4be8f452977c2c7290c64e4e9e59feda172071c30e06927b87a8f077181a78e088ff18a1b1564e65deb3042c88aab6b4752eaffd9b0298da07fbe75c5b3f64d6c8b0850064f7e7634cd0c3b74be6ca4c58d2cb5d8e006d317d4ea6d908229d00b5c2287bd9425ff34d73ba37fc3fd5907e559886770ddd93a9bdf3406fdfa139df1a5e8615db82ccbc53718d5b5f95dd8e906386b50bc09c03eaaed033b2fa78455282c83bc7e351c1d79a83ea148c1a3a43ab9dc30c53e3c83d8d877b677967b7e66f05b1d52bd31da93b789645d8d575461ab22f087d0aaa449fa2feda216185ddfe77bd3e4c9bf497527872830ac737ad0ba9bc08d3076440f4bf138b9484ad88368272005dc697228a9d1ebe5e4327dd2d5d2000d73c52abd3ec4989357ef79b1ee995ac39f6a3cd5437cb31983d39ee4328a00eafd708a321fadab321472844b13e8182a55faeb4f628c47bd6c90c959d43f8003e8f28ca6e18cdcf7fb0ab397071c3cedcc607cb8b56066210fa9fc96702110c565eb058e4311604640f15fcdeae2231dfe90dc8ae089895f2943b902625c7051a59db64358d791a3ecb47b4a7077398d315266e9c77688222df9c6c97d9b003a168a0f5221ba3fa362dd07e015c108ca61de0fae55a7b1d197e336f0d6e740fe3ab219d13d478cfb9eb8a21c2ffde8851f1f9f49fe63bb47dab567ce7e30f0ca82acce07a50099f904e4c2d5dbd3efa5d8242a010d84d8e85f1b758f186820f3632d051654478bf5f107a90eac5122cc0c77b01eccbf54cb4868b7ef78dfa0c2661f6372c71cdc8f878035268f8d1cfb4831cdc2b24fc6a3f3fbd2a0d30230328f434088d51533f3bee58b1a777100555ad68b42cb64e4b901f99c020f96601de78bc69c2e6304bec0794cbf8578fc6bbcb2fe899355943b251a27296907208008497cb97f2886021609adc12442689fd770dff2034ce32d992abd5319c090b8fe62771b4f0f40f338e8b9c911bab369c7a7cf853ffa242438b9467011eee09d568706749cfd61211500ae8ca593809aa02336db24bb1fe3fdec111608cb90c9ad0c802934b83de3c628674121199674464a90578eab26005ec51cafbf3990bcdf1e6f9840161fffb8cbe2c2fa34801ce7740e6c2666071103bb82b58ebb805f2d9e7b782f7382a7f9b976737220f8990e6076a1109d08d0bef610709918803b795c4b159f2d74d67219f03e61d148e16d8fce8972ce588b6f22dcc57a85d007d07a76b7f6ba929570ab067b89d58c285fc5e9e9233305178a2412f6fb1bb0d9d95ba369fcc75e9a4b70aacd44de01e87518f5cb960ed55375d192ad27f2f0faec8b8dfa0b15de459ea61357a22a4fb13b3167d06db99e0bba66621cf84390a27cac81c8f93ce9a2684ef8fe6b170c93fef9ac11a4af074132406df027e060de58dbcd39f18473ebec4a3393e34f297eb606b587e37bdd7463ec4d173bb5907c03dc919ee453c71772fb7420e1d392091cde9a37f445ebe482ce9fe8da89008a8dd9300f2c041e8e05c9d7de844ce06542936d3625d47444a4eb9b0f0b61706cb539eed9c93e90caf7ca70bd9ef30f8b46b342a286c358ddc4e35069b473c0b5be0be97ac44d7f96af292d0c46eb76bdae7c8f171078f7d724e5da56b0a0b01bddc6b6af7e1a314a5129593f916e4eb5a17d61131cf3f4b42a4870e2c856c07145d3c065bca131e4c2655f415b9a5567851145fff7f66c010c77a0417513f05ed3373e6a09e735999ca2e7ebdcc2b18a1ba9cca0fa9b3dfa4bf9f234e50240bcc5969fe08085cacdda15a1285283f800323889a3413f9278348041228aa5a022449c23cc347b2014330f46b983bbc4794f7090ecb28f31a7a44a017d3e4c803d4dde349b0c38cad74627dde575ddd03dc7c99c5cd05b2ef0be90c10dde1180a8e86b63556d5e835db8159fef524cb185313cada56cf45aec57623fef76316051858968767f813b7ffb7b947ffe6f50f897bfb792bcd9312c1ae447012b9490e6b751e1ad0aa8b3489ca2f70f606acb0cc1472a466c6adba7ae5a60a9a8fc0049eb53a6169bb998a13278f4a2e7ec1d8440f17574cfee884ad6d92635f4834082ba6ea8980de0868ee97317e3c6219f76e88ad2cdee30c59150d41830cbb8d0cd2fc3014c744bc8167be03cf94fb007d8e4571a3c4c0373a080348d6c03cd105c7e56b1565d46fac84292708e3d8df455226d0932c508bb0052e356057e48d05e01ac1a8f0e8ca56972807e88f50df601c043e77521299426500d2c62addd5012e23b3a32485e6748e61124b01897b1ef8e02496fb7b0a27b62e8885b4906e0fd8510c8f9811825dbdab47c9934b49dadcc52d87f0e485af5222c6b1a8ba5904a1bdc8d6d3b0d68f1047c290c3fcf4e6bde3fdf238284d66940b86b9f6842009963a0bb126a22a1fb050292aa8382279f7c6fe381933596839324284bb9ba50b19d13535fcd4380ad7dfd5850f876f56bf2147fff15b7d70e06aecefd8fddd0ef1a5e985c4ba9f41749209e705e1f2a31dc392ed4ec7d058165aafa5ac33d70aa36bab7ef7c388024aec4d6d8c5b3e08e6200a59e13b835b6667f379986f230740aac74ae49e3a4d0b9c8f84af043a1edbc5717b907098c870d5863760d2200938a5bf7df814a745ba5f712d9a48d3ee092b6c26602356a889c3e781e6385604973772884df951b47a0d5f0ce1bc427dd13abdee9d1689b07c0f7c90117e510ea3b3ff29e08a93b22ce332f59007db999d35b30da16b71a4ec08a87a80d63f075a47d380a8a135abfd11dd86dcabf94885c4d48bd8ba2d8209bfa1ed9283c501de35214a1a7e498d003fddd25010e1521521f8a9f85333d6b2964d1522026304308d3d59b0ea32194930a325b0b95a621c75b4c7aad6e849472290decaf47a0f1a1563f8fd732b771eb8fea64a0bbf06eae14000d1f9604824046a5366d2bb08b8f517d5b54352eee63a7c5be168b7b0e331a497469b15219dcbc098e35b2000069f258c8668c2636bae42435d2a6d30a53b771e1a71a555329524ec0922ec0a461e36906d8ac66a9d0845b29e86e97ce369d1d46d657d20d0ba253117a9700ba8b8e48a606aea466cc63c0cc5ef934e5af0632a37a1893c05abb787b19e3300d078350d0c68dd41ab34c07af29473647db4ef20c27026a86be8553c62dae809eaff1ad84c7cbdf2c6203cc90685a8507adabb27f64b368d10fe7069f962af04977547d385fe8ca54dd0d2c2969464bfa45d7e859c57b8f195978f430933c50d7cda0935feb7dbe8992e9871479af819492112c6cabf0831db8cd7ff9f54720e7f5da5036e07c2019558817b8603b4c4b8f75c08d2a6d011f43d6ac27ad83605a7c8206d23811191c283690498172dad0b21a8b45ef12c06e29318013aa0d40c2a16ade5ba3f04deca705a58d79e87b9bde8ee1b3b8c1aba716f23c8b5d9b80547e6bcfb763df7ea03286aabcfba25edca236acf954c73d92cf3e93af5f537063d39224cbe7675a0ac7b3b705d1a340226ac43abd1435a1045fbdb40330bc80c7cc83801510157fb53cc9ff5013cf1f408b5f815041274d6165a538a59a03908244002b713c10bb308e47bd14ede806c8cbc82bd668d024de0ef85ad247c4d0fde988bc4d59a9254ecd0a8d6cde887f7f3e832aa7bf911183ab8c82f70c21500f5f59b7a6f5900d3753f849b6289501236e2e8a73765cade626ed7b91c6d210a73640f612123a01f4e8dbdfe8a013376e3096c3382bb6767887b35d515d8ff0c3d1719ce14cb924af6b4ecccb076c679045f7e8cad08287d7e15aeeb68dfbe096a14dfbc1e4e9b8937773c88264ed04803a4b2374c6cb688b17bb367a3b22900ae8039d0fd667ce2f87e722a76d42b33e3af454ec79a8502f83ec5d628f7c80a2c0d197c739b192bad58f6d51cee3d231172462fbaf2bec8380568d04b46ed098b0f64ae0c65db3a6eb669d975b5af2205932e9c90ffa572e41087d2712c430b7b511ad2f3f65feced24fa0058e8741ea2739c9f6bec0ec60c87d58dca0b510a6feedd0ecdcf9e5e9965b467e4444ee744875e989c5d0898370939b9095391084248a2dd3a1702e8ada9bfbd460fab92822ba94e7537a1fd3b6503f051476a0db0e4cb7119d6b16059df4c8407510840fa07f320461881f831bbc28c52fbbc097f33cf11a214857dfd574745e3d4d38ffe77bb0e5ba063c2edc40ba8c5721b003d9a5f67ad09adc4352f3ba6e8d88ad97584035f632db9c49584801f5dd85b0180fbc7bc477d1d60e86676f6b01752305b233bd9ad7b0e76f58353826a481906d909b232652897c683d390f64bb124a59d3048d817b72ca11480770ff79553081f427bf781dbde2f276b280035e67f9abd0a8a68badd4bc7863052c4c115b201d331b21c91795c3490803e61618c67dcf8a7e7bd3c4a49b71ed480c3eceba1030bdb168ca1d26ab967cd029006e64a799bd9921beb00bc8a44f5f9e7baa2d40d1082ede87f5ab758107dea2fcfad9c0741ea5558d9ef1135ecce8fcfb01b240cc6bb0810ed22c097de84354df1645876a73a7653d4360424defa369ff6b2570050b3ae1f16f4d92974e8f6164bead83891df791826a7edf605ee91290cc2e8080fc3923e16541612485405a0f04e16c360e114059fe0b1a9a2ee37fb6dfeb3087746679dde11e49a663e4f2672dc2d6e0b71b588ea1702a94476f0b3a497cf086851c738ff397d00712f126844cf1f96bcf6258d76383f19b4118d7175769e0706b810a573f7f324e04d6d900a9e61a8988900699459c09053b07cc0753d39045c1e03d8ffdeecbd9ab3f20a25bc0e0e26385f8ce9a245859b35a3deeb055a0bf44a5dec70c627beadcec4671c3db85b5068131884c5ad81d98d5a560cdcf503a6433f6f6f1cdd4b8afc95209d08b2cedaa0178fa02b93cc1c9517ff00e9970d7809a95abbda9d065e9af67822b94de4be4e89cb3449fa068cc4e03b9e5ba10372e2a01bdf54cc87aa108343b6c333c7d11bb682c977454febb4bf7ec57cd90a68c0041fd3b271ca5cbb474b71767fd603816570c6520e2f9e714595a22e730b56c3a55158cac51fe8865f9b7a70723124a20f7517ca236f1e3f68c6abc8ab02bd681ec97c68663f62c68733c44654c98fbab22d1e45334035954b0cdf05b505004a4162899fcc06f3070235e8edf4192c581046e19c7087047b1829c3425c0fd3a16d8760d15a4fea7ecaea1f6eba43ec054af18ba848f76c9fd52be591f30b322627d555f7a4987ed128c318943d16abf2fa47cc6de262397e189570318d0aa3ac54afc4c09b4ce0db6c87284d16f15b18347abf8160d7b5d383dfbc182e08e8d6cf08eddc7fab9554ba0357326db4095bced4732282cda21407ed6eb39e0cbcc9deae090f632b47b81bdf4e947dd16e6a826da60e7c8976566da79bcda00b0b1805148c5e44422a6ae5e04746ff500b72f1210b2f379765704195dd6b8d06facd1964f05afa6d92bd65e0ac7f55b0a23ea64bf900f48b18b6d7b5d062a00a2910eb7dae2f8fc88596a3f585f1f3238b030278a77391de9c55ba858182e2064f493aad8baf78a6c1797c0425a2a9e7b31fd055e1e36e50ee19b1cc86b7e1014a87120e074b5d4b556aec3cc1e2372edb2dd0f93b94b941924c7bad46af780b24329b1dfaa5773f5a512b855e6d4882c4bb4954a0a99f8c5258445202a729067e5c88daf1367ad23e6a5d621d77571ef37054eb29a1fc763530da23750aba0ef98c4b91de987d756f47f4c58b532966a001b9e2544b8889b82dedb5b2a8070ab31ffbf3be38d6b387b378771d79832c7a9919b5c7222ee9c54e56c4a9e91f0e17f6b161676f19c0f2d4ead33b9de2c02d19d032f22d27b614addd30e10ccb07301ba09e07a8b9ec318557791ba3fb99a326345c44dc8eef0181c157c2365a07286fb992fd85e103a76a83d4004c611b21a8369967f106b156530b4f0e0e680992243ea896649a8db6d843bfc615eb7f22d8d1e391f9dd6372b5c234a1710302dc7be655c72ec5baaf1ef7f9aed62f90c264c7edd740e379ae2f8a8cc27b3708165de70dfb304e59b48078ac23453e0950c853230fad4c21332adf6188226a0ea635e5d190d2a8f6f2a35ac3b6a94ee3c418b8df2458e8a305750c439c187a03962d02698708fdbd5335b94ba1fba7e676f5bf77fbc563b0b49bc880dec1490fd4eefbcdd476316728e71ab2f7da533adabfdbd073d8c8766c23147c3a107403e52d144ae5ed4ee6048a8171be14d1d13af34ca1f3c90583f468d8ec6540bf0b022be81957614cf3ece7dbb69a5c435836379e4a480d73a491fee7ee0297450c313237999166df052b995f9f0fa7efd1d803b6c05225fc1ae894c4b46605fa0cc3c22f925c2a7977d2b6ea1d89c34aabf2a4f1c20503c974421e6f182e362e04d8f5f85fedeff501deeb8f99d863e640811a99f3c80724561f58950cb432b9069e271082b1f33910b4f148882a83176da4fc862b03c91b9d9ee885c2495a17085ebfeba5959dad04fa9c8975fcd80338e4b387bb3dbf78f8e2d3bab98229d40fecd8049fa13b8d946c71b308d2401d95cc7ff23e94b1a82bd152692d64c014096bc8f3f157a01ed7ffc5f9e6e8e701a7f60f7fd19f4b92f7474dea18b474140cf8844b51d3a0f572b82088e41cc7d47e2668dc23db7b85ca8d8364ce7b65f20214ff226387ac618a345ecc95b0a88b14b1c9b1ae1759c2633128bf0a3cb3fe0dad463f4664dd4dc9c03c0c389427fb598a1b440ebccbcdd2b7e89c0ddf0338040f16159de1924ab8877a6d95c8173a278beb7cdec33c26e084785b4215afbc0624fbe9bc92f7d716a4b98c1c7a3c9e28f2c5385490f3959cd23f63b4375eb7078435e064d9ff5f27b09e1ce17d02741bb5098def2d2d3db4311317f9cbc99403e0ea0f5d395de36baf1b1ddeb56100812e4354dfefb8a09f7f48bdca5bf22a04bb4926dc4c3caad3bfc4da5bf62907cd3e0004e3cc8cb6e8d9846af8a0985b0f2bcbe26a35445426c79bc35c796b3c6fb4ca76b9d3f455d4e6816ec13a344006d5b7e952a0c9c940596280609205f77ed9c15bab5dc5d8d0b88e3408cfb96f05b372415201d78d85a4b3501c742f22f712af44f2c89fdd1c81356a3532567506ee4b2c5752cbbb0c9935f872f4fcfc17d313efed0844902200e50a5a14a3d403c27eb43dec67b5818fd28ef0eeaa5fc40f33fdaf0a444dd612402d79ae52070cdb296c1b75cac20fd1ce1e73d0f99801c5824f3e0bdb48e4064878eea42dc20ae481f821910acbba3f17f212a611ed765295810c69b56bad736b11f7a1de7c0a4025f3088ec49ee57bdafb549b9724ddcfff01716fe155424fda1c76a18d180170cb99ef30338b10170e1bcc757885df008769aee7285e567b7de24788d85c04ff49ce862e929205c2bba38dfe97c1fc3ae3653e55973a048a01150a4c94fe0777883db93969f9224488e100966e3c407dad2a5254245319e830c7c2eb3e9f085acec121b2dd5ec4e157e0b79504eea5d9f9d2d6c92640c08e4100c2338f650e051776113af50a15928571f7e41ea7fd648947947d51b0634686316272af69029dec60afda07ce978ee1dfc65eded7979065a3672e1782ef28f183ca3102730f868b749d37f86de5e46c321ca20c7a203aa482741ef19bdce61df3c6cf6c89021b1d9369b2d4268bdbf0571a13d5225ea2dcf7936fe22e2e00fd00f005b2c20522cf0013790df6781f2f22cceac0559134f8fcdc4dd58ec25158416eba21750c913190ad6a90cbe6be3918cd13c919f9cf98ad89bd9a19dc5883bc23be838e088963da6ca14012fa74e3ee7dd406ab9fc09cf0efeba819b0b8f98355b35209064a21a1549a11cbc2e6f37caf69fef5fd42cf5195e7f9c9b0a2463096a356370b67fc61e307d6414ae2b20e5fe3aefc0ff65d6f4664916899278dc628e0380b034b45321613e620db6a75a811adc81ef79dc230f6c4242cb163f497d560d799039a92e52ee101884d6babb8cdfd033a46056174c4ff3b2faf4adc1bf07406db0193b2959776e1c3a7608f62ad9d4dae764390a9bb703ca661faa7742621db33054aee8a2039edd648ac74fb71454f6adc79ed4f69531c56d078c76c90836a9b08569377cdde7c7dbe1471fe1a4629031e53e6bce924b4985823dc89e962ae9a013a101e37e9f36b2bb267ed6bd88c7dc5336b8681e21075fb50f0357c8e063f0eedd46706205aa375571c00f39879570db3615eed51fa45809262206e02b0f9023280f450ae383c904825b3a556d973a0a267604b0e72e5a67db849d5a4fcfc0d8bceaa04927476003bf5a05df280d926bdf3abdbe652b2fb9d2a876041df6e018c8d96b038eba0feaf802ebf99c19cd12a90ca479b2ffd40a4b7b0e573a35e069ad3da0020f171003dab365c268182eec954301206bb67eb87260f2d28872703498ead8f0bf43d75ba473d296402c6d916df49082fdcbc99a3890336a01c41000f519521b0d75036523ebeb79dc7b279737f9dae384e3f106c757d68f0629d0a9a5b3cc60e419cc4104b17ebeae2df82a4dddf58643c1ff9bf38fefce8d3e80fbe1376955cf928fd954b03071f93ae1c22fdc2f7dcd99efa191aef2d17885c0c1f3f682ec81eb15fe1911439f5bbc29c4d76fc48c4dd841f121154adf2e0e1089ebc07ad453cbf13b67af52e098bf67e9906fd61e4209e6171fd6c32dc20eb0e29ae16d0a12f5ce48c9cdde55a78ce1e101a7be97bee2c05167c03f8f79f46073b9d7381fbdcbe3c3187bf4febb2bae7de420ecc193aab1ce77f303a08a5bc07cc18ba74606c05adce43fb4564238758569651de4820cf5a9d61022ccc3f260786ff9debf7b52f3817f76aea519bdcf8d1d2171f4bdfddcbbc87563ce42ba606d490eafbd0eb73af9e2e46dd1ab768d687220cfe70b0a5fd2dd397b3acb73f0153dbe436142adc8b20f0a7fef52571b6e2a724000621d550a2325b87c4e4d703f12c2ea561798d102bd77a720305956a59fd19c7a01ffd1b46c0ec314e674c0156b9f5125a838f4dbd55e29b46ee421bef326155b061209feb1409b44776f40182ddfd1a1bb8ce600741c871a5015298ce23978702f07b5064a1b324a917f304f0f03f051bca246b6d3465368cb19caffcba95646a82e169cd25adeff8493c0f57794ca31d17c6bf2116b9999f1a205c2ac5cc818e74f228852e5c0ffca6d407d665837997a50e8bf39ad9d1aa9a122ae3e8b3df3a96f8cc4f2d5aee62e1ed098deff6123fe0234b755fb9ea3d6f7501972a80ecd5f5c9c59b84bbcd5b078e034763e53e6c2455be9145e5f5e7ae6a1779bc07d988936dd6ddf549ccaa15b80eadc33e1afd1582d8d13e301e55e77a04bac08cc3b6f1489b57e76165d99e7c006e54902507b846db97e4e1a374e80a016e3c0f2eb31f6d831c52591cc6ccb202fcf043f325e61ee50d656a47d2447921ab1069edcda80d3e82549975099b0e0200a8f0e7f4afb4087ef95cecc2deded038b8aff3c4147b9a1c04bdf39ebc8f00d34dc9072bf5f2d77cdfa3347b00b13d56edf25d5cf62e956a04ecf1e65f8a089eab496c191ea906bc608d351b22bcf28c9a2133595e76828765c8d7961b6f0b7b61d770f58c57e8f7d36a00f3306e986f3fc2bd3fba35dc798c111dbc2e6b007dce9e9ec63e2835096580e1f91297a7e626c49d4cc3d642eb08b1e230866905d51bcde350a91efc4ca6d54bbe0ba7f55177cf9798a537f24f0e80e0d51bd80d53b5215eeb7be7587847c0ba7c9bc73f06c029c0cb660bdac0b153ee5862940e2edbfada8fcc4ef04b3bc3153a81d5f8568b291d17ed0463985eb0889279cf0db29777ef278929ea327b5aa1728719876b0732e25b735b09fb78d76326e8690ea316bbd5dae90f6e69fd549e66f05434334b682e0cc8d147fbacd25b2a7f480adf76b97d34e5947b88726afc5c40a8ff0ce0559a9be52a23d2a607094f86b80676f21c05f7f9b3889ac4bccf8c780b51874ccc39fbbce7bc4b2083cc1ac1480c2fef8fc480f423be32e217c2061343396a95b63ab4d6a8b947eedb094bb7120c6118018eaa626fec20c8525da789fd50f427dfc3dcc246b77f1fc944dc518100 -generate_ring_signature 68d6b1b9d874d51227c9f0a28cda04d8172e7cae79e06b58aca4cb5fdabf36c0 3438eed5f743a6715d57581bf005efe4fc4cce9447e6d296bd602250d8c16fa2 32 97f5294c86120cfa379c931f3616ae5853e5650dccf93877dd3d22a949deae47 20456187ac5551528a6c1e1e6e6ae511a8835a1e17c2d8376fb0d3d68f8ff749 0b8117871c4b8a996719c93ec132408c440b9f0a73d36ce619e4ea5099f46f3d b258d44bd5ecceec9ea3b4fc8e95496d997234a63c7b6e5128b39bbefbbc4bed 7c68933b4898deb06963efe87110b3a446466a13b18660dd8f550622629d0c0c c2a30c8b5167c28eb95b91920dc363bb6f933bcd64986c3434ed89fc077536ee 3c98ea89ace2a89cee7bc6e06f63cf5ba9297273b9044d4c6ae81b9e411bb440 cfbad8a65a06a22fe90f2bc11ffc003601392504fb1b3f85177f769e22481b8d 8085897e5683d6b33672d6e2e7532337eaf1a6c47d6eab0832460f5460f9f495 a4545f4596f607250e5de364cc2e0e070e37d1c330a6ac6eabb3d03af415209a 9febfeb7e7bbcfc9ed9d663b81671638f03555fdbad469150cb649f765d60d6b b7240cef5233c3eaf7ea031d7ed769d65a7362c90448afe2fb5fba997ceb6d45 0343b7459f99c7ac11c3d9f829f5615c5bc60e35569dfd97b78e6ae2e00f5d8b da9d89d8f350d85ce47516472c0e73dbe1114aa92de1e847d7c6a1903651671e 3bc5140c5ca0e3df7a69f91bdb268dcfa28550c35d64da809ba9940c03dc842c 85eb184236053a9e30b23e189e446a46c11be28359c0b4f70fa696e88a425b39 d08857473602cf81bbc7b1b17766fc09a7fd97c3994f8bc4de94c6aee03f3e99 7edfc9671443ccc734800a1479d6e7840689872b8652be4e97c9ea7aa9971e3f fe172c59587584facc28059d30713ecbdb2f38d75e967899cce7043592ad3e02 d073e626d083698553eb8c7d60e5a526e06f9e288f2c0216cc71535b443a3528 6621abc1d1d77e0e65d363b1dbea5a4651a810fead6d57fb116791f83824d668 fdffaf67600740168caec34bfd37b0fbb16275b155de1eb1bd7b39f1150d0c88 dfddcde4c8ac2861c78bc7df7cf627f7bf8d115c825a10d470ee665ab72bbb22 4d93013a13fb22587ebfdb63e9d8ccb1f7c04bd51c199346461fcf3314094887 f008c557a5edfa69389b32f0da30d88b2e6b91a4e37600c14772ffd578a67930 d1ee1579b8fe43e9faf76280b8c012985465491c20bfbc29d9b2f2e69083e16c 5056cb0d29f7e3fff91d6f9ddacb545f2266163f049ae76407df41c25101658f 80b56590de9d2826db4bc73ca7f18407fd0e5c5c03dd9841c9ded9b8dafbbea6 62571c909782c70567e9968ded1c05a4226a3e04a07ae9db48e0153a56b2a468 fa3243902d44e63e78a23d70a6706efb6e453d3ed42d087cabc7655fb83ca3e4 976c32ea460a47aa3596d836d70bb40a84e6f3d9ff1e2e01a516cfeec938a2f1 04d573623956db0dd22344c139dc7170d8ddf6296ccf558cc2bacf230485aa05 041c83987a208dcb9b7658b567e4baa29c0fd278a85a7ed885d26e4356250908 11 192c8007d03ce9c87ca63deabedc7e06dce9dfeb418fdc73e07662528829a40102cc1c120e9468234ec81ffa53d8c68330be6332a9805cdcd0429390cb303d0ac85cc6abbbdd73d3e1d0913a29c205e769b0da64f0a3d0f3e65c9b31d741090f99c23a484d3452207fe3309a6384db4454cccc9e9820dc03204b4b7e09f28f09ae7a76427627d23d4c308c243284b8277116cf656a6d69f79270238ce318d204caeaf4842ab1b2983a117763fa56cad0156678256a8890686da86d8a94360707648a9468b1c5c32675ba598f6e00f5af06fba34cc65434f884dc935acb3c100a3812ec0dfec3816b2f489d8815c59f37636130e4dc68c8ca4399d479dffa1f06ac3b16dac1b2ffceed29c58763697aed9fa9efda4176f18a7d3d571e3d2eaf0e4647e6c8d29daa1de4c1ed633401e4445e5ade6e293ff678049da2b4213e5807e4d337d7f86659197efe7d7d7c7142d102a186fe7821e4f409e839136c20cb0fe4490c8597959f324cb18c4ff3a0ba84dd5040f5097a218eeaa452410962c90a58a20e7eb44dd3859961dc35fbec539891907bd7d399ad145dcc5997a2d0cd0eedefe61b426468a6c0f6157b7aaa2a1f6d3ca1d065a8fe9e8fa784fa723eaf075fa3a5382f4022643c755b2394e3906336997e7889384ab97efb6a4eeb17320457797b825c187076a1f2f0ab0d26b92ec99041efe94de8284d57da2b03afb70d1277c95989150936f48f4574bdb97733d816a1b9f93be80f9a22749c0551eb0806e336b2f70f1a7ee42980d3f1f18779734fade9545ed72c5604f79596719507ab555fcd213f6e7b340a0af18ece2040f757e452293f5f3060ff513139e52a050eaf85a5a8e4b062a0bd62ad0cdf3aa797e9498cbb3d99b3763aacd85caddf0b7823f65051f03e7f08fe69eb79a1c6138a5ff0deab2f30fc4db152fd2064e0048203e7cd3701664d112c2550174e0d55cbf7e63be9501f86356e0c55dd3a0201a1df11a40d0f94e9cb82e2ee5272da83d55423d755be9330fb903be6de1dff01938b1a852c033978e704cd2e64271a9e96e0756a6655699ab71fe66a2ae5d507083a1abdaf2f092628c27cc8788c5768058d019f8e3a92ef526d06966c7e720f490aefac705994e51c7165dcb78bc68172856869603e4807241c446967f05b09e1870731faf6dfe3473e490581ed18df25dabb4e0c56a95f9ca75582ebbb670604dceaaca2f5ece824061f567648b9771abafc155745a2e18eae96324f5d390135da99bccc159140fcfa7456717bb40411a0ba1692c8b1fd77d0f4833e83a5043327474ee9fcab8e2d1db3236930a802e6f776b511e712c164d23767281b4108b750c3caa8f8da5058cb55e2ecfe348bb3664d24eca47b5eda3d656630f66f0ade294f9e140e2d0141648033f37a354322c5db452149a9b0f2210c77158b72020f28b12c43ba796ea82d55a52743e2486a649a39bd3bee12620c4fa04ac6900e4094aeee014e110646ec5e2d8bc1d3a661f3a5803368e9983bfabffcffc56500a84480160fbb0f651992bc03d74e295bc2677616f43c1378468c6a4339dd780b4bdf691e127f71b1cdd6e6eddc7e16c8e215bbe12aa25ee78f0e09eab4645a03917d016dd7d2f3893f185b78ea0c979acdc57fa7863a44dd896d89da6d8a650a806b34ca3a00b55d776fb234e8996f57e9658b36a453794bae74e8e862f0ea02e31cd0cab539dce0440c00633f656cbb164d17d96f9fbd4723e0de5a60b4e10b69a2e9b4f9a42cfe39c1d6832622f1bfc3ce1c2c0e7112bbac6c28b9e31c7b0c7ada715c5433be35eaf0c8f0d80cc9c4e865092dc98fb94da7a16b80be9e840ec67a1636590d40883ffbd31211546c32fdf518035e5fd0bbaf889c9485f7be08e3ca350e15e22c02bda292f7c8802856339319139d2185c55e68a057541746038c4a60f02e965cbc3a36147c3fadf2ff60de9190bd7c3de92b817e98a89dc902c3ab90db51aad66ed26f02d5ba86154f420891129e1a789ef1f9a51c72d98c05e847d8396e3fbf7934c5082928a78cee65e080232c28f3cf53a360a3a333250391dd7bab5a67b9020bd9bc7cefb84b6f7cfc2eb36a51055a96f09b98e6372d0fbbcfb4f9c42fdbc544180b572c2d38c3e40c23b11e0e5d58edb8b9ff55a3cd0a73224f90088c08b11a9b6c5fbbc0190e1780e8c1b832c561ae51b30b2de050026204540792cb734d8ca309d9963ef3659d9585195752396df0a2c0c46ae5950fdd809e10446768cbc25898d186ca5c6e3bc24bb752d7f1e22fcb6f46a99a6008f5417af0e2dc04ac032be3a136c0941d4c9f88d27f6a58c78cf4e17673cbc9006b1856eb776fe0860e53b03dfb691b084e2632267fcb2b8011e780f96ff4eb0997d73ed67c58219add3b97fe8550a8cd4f7ab013b85d5505109b2a752d5dd40b7bb39a9e7fa605587942c9a27d8eceaecaea5c18f98ac3698e3f2c7209168c0321a4c233a0010873cff7862fcbb8f8f75ccd1eb180ec77ebc4b97aa224bbe60bad2b389d190f50d4d7b483cd18fa5fc39ecea58aab034d6b9ace25899fa7920d36389001ac4a50b6a7ab12a94793a3e4dff3c814b33c44d6a8c749fea3a2e200ec58f4afd85b7eb9d07fefc9386d5129b89cb84d874f053f2b7ffdee1670d60bdec3d96010313576c0dc8d8cdb252f95b8dde6699b25e2f2a40e000ea8a0560f3d476f94f7a9034c33dceea828ed48d5bf6588f72e349218cd9c56e502de560c96e2bdf6f4f9859ff9c13af523794e630d68c3a5f62eb8dfedb14108cc0e300c7d0587ff5ce77b478061f126609224e47e8cb591816c038f90b2f1c2854ec407db4cb3e76229c3d3201610f8f21b85f0a70876247ccbd47e1533f85a76cb2f00 +generate_ring_signature f23065c28a04de5d38606f37b7cf29c680c06c2a9bc00b2bb3bac696f9df5c61 7046e48f17ddaaebb928ae39dbe62c23441f01ed40d23fbf5f07e5a352d06197 16 9cf7b3138ddeb90d60b31bbad6458bd58bcad71192d37e69444b28f9450ab37b fe25432b3e417e7123b3b55661f9d8e01b9238c801feec00d01b0a144439126d 1af5776b1bddd8ea5e775e035ae7a3fbfa2e08401f7bb01162ca7d2c9ce21aa6 8138759a1d698a79ba0ee523980c7899f5833ccd15f9c224a02c44accc46ed71 00cc8407088c92ac28aaeebfa3de0f699de189923fb7fc18daf1361a7356c203 6493d68aec79197563b65b4d202665da993a27318538b46678aba6df7475e6ef 2193c3b3e8c038d3ee84d30a3e6b8ccbdf29b25f9b576953907b28a3abe41689 3f4692db3e0e194005497b99f5097938659fb00906b308e4413ab07d20d21ca2 5adf105a86dd1d4579bfb3bbd362119122bbf9517c089273570189f368919f60 8e2b0684c678a5e9500332e375fe28f9ce127e1d847a56aa198f81d02afecc3d 75cd3c47a8d0616d4f7374f3b652e2b4dcbedc826b8b0f22c8f2b12d8449171b b26567ad1bc3e39a4d59795b347cf852c35944498d5ab3537ed76f63111bc196 a78edb131426ca91e0a25be0197578a4d4d2876d18313a940814d8c8e65f493e 943aa48aa6dec62a8f30eb16e796196e3a35569203e58067228c8b496a739d4e f9d1ef180af3b08d9ae7ee9dbb687591bdd3f8fd2b1912de622cce6a9e0d25ba dc6c4e0b458a8f3b47d5841dda4b34bd01c845031421c8fc34d90f2356564fd7 f7738841783117a5206b00b4054df1949312437643b353459e02552d2c69f10c 6 964603b118505c20b3987666972a5a5e7e891c3e4e51580f284688e2481bd70b498796f0c813fd29c10e0df30beeed0b1163926f327918b35be185dfd44d6b00fccbd4c205948abff2759be109f879ee37462f837f0d9eb08eb51f4e186af20e282fd4377a6b31e684956c989a3cebd51676201d75cb48c11b16816bc08a3d04ecdd368735193e485218aa317d492dbd3b68e47e8aa96f98ecd566a44dfa780d9a53dff72424d0ba262698e1dc485b838cde36bfbac9754a7aec4cb66256a9094e151e045713d497d795f0298d385b791893205ec8a29dc2f0723b4544ec4a051c22203aaf8a1fb9ff0b81902dad6d0ee2da1a5f3c9be1d3a448bc53e8abec00c4e8b14eef82b27c6dc7106529041fa7bd34dee37a0f506a4445babea0299d06afde9b02d14754da838239bcb4b51ec24d7afb08c9e14b9f2c52651bd035ea05a09fd7a7203b7069b4fa663a9c06d8072e0335123ea3a3bcd5ef351e4a3c310a272ff7ffd79773ae1cdcd664a20e149c98b5d13775ff9f6279938ad546e4520429fac293fc0ca6fbf225a2c8626e9edd7adbeb8a7a606f2de9ae94ee4aad400152113f70672d860f0c26fe2b482786f1f38e1643a938a39a04488de0a3f5c30798729b1464dc926130c41c8703eafb4ab2ebe202f618547b1bf17804ea31710d8ed7fbada1c2c44657de76f80d701071249b17e87a44533c499892e7a296340c5c1a0e3fe4ea65de541d3ec769a4fc3eac5ed91bb9da498b89a79a124a9af30135cfa0ce7e2c0d4c526d34085c186442a308c68a2b71d8bd81bad2b2382b7105f78b870b18c32c38104388ed277e1b2ccc15720460a87b13c6a8d6ab0cf7ae0ce94d4dd6f64dcf6b3b0d4853e28eecdf09c29cd1f11f21f4d690a1d4ece87d0b3f6b0f87ba59fa78cfe9105ab748f10f6575978e41962c423ae58b0f6f1b2001cfc6de6a49cbe2819d37b2eba07e7b55dbf8f6516f63de02f10e9c7a63354c0427763f12232941b95bb89ba559a38043f833bb54cde19f6084b9f1a2a301c706d1661ebe1bef22d6f37c78fe559e1d729252798852d2992717616bf67548ff09e268d36867a8c31bbe5928d9660bba90c67306366f972d6ebb43b89cbf40e305ec56094d951be06ddef44fc954208791d80ce7f7efac7b59082bab0fa55f39084ce14940f646bed4c8ca9ec0ff6bb147c5788e3482f3df0409107d9ac1c35a0db29b65a861b51ac08191bbaddda91285a0d7b489d123c22365874cfab8d3e8087d1082abe722b290e95b63660ec41e677937d8d5b76bff9c328fb08b6b7f270838b0011700cf2c2c6568ec91d6608dbb9439bfa67f0117aeb2c5e4418097d20d9b61cd4db115422d07e81c5721384fc32ab7d365ce8491c36faef484a1a8b5031a9f9075c346560b5da4dbb19f56078ad0794e54584065489f319436425b3304 +generate_ring_signature 89226689e486049662075f55d46361d821c5ede1fc172581458207aeb3d7374b 4048d63774cf0e3d73059b76c1160f5b36fae2add758c0b5d0a76eccf459081b 2 68943d3665e40eaa5d8ce9a3279e70e9d00afa0cea15d6671e024efcdad2900c fb89cf7108eb3b68243e732e820d716e11a0baa0def8d2d837ab998a9bd642c0 f75b48b628a7f3fd1dce1055f7c0b81c36454e012dc7ead8d5528c11cd52990d 1 eba04712d647d7d1b535d767e859f23538caa40788b38aa3fde25bf875966e0bafe9b2f7ed81cc2dcd087eaea08b9551c8d7de011b25d73c1f0eaa178b613701900446fab1a384897600e0c3f67b1652cc7ae208157dfd9981156cc3c7d2f807846c432fe678a286c39e5d69f70d6d636659c46f3c20ad50a05edb833355240f +generate_ring_signature b5d8229ce74db9826924b3c6e5caef7e7e5bf671ed19fc11be2989dd3c47931b cce45b0c2c85b4210b5b11644afc9395fcb69ba98603adddb5553445e9ec7b6c 144 0be075ebeaf99a172e8e39e394767c3a3f987767dd4980c63801034626594ae6 73fccd68f4dddcedf7206f4928e50477501c1efd04ac95c240bcdb1311abe7d7 82d86f2cee19fa0019fdd5680e9f5bc9a0a19fd20fe801693646eb80029cd721 5028c84c1470faabaa794544bdc8cd2c711465def027442c68cbb51d8b26a173 cd8f47f244cc2c434906ff8a3b05cb0cc6237dfca4df80f54f737ed73a8de6c9 b5820b71fcb114db2922d51ff6ce2243fee94e0b2b325539c7ca31701a291b9b f8935af82ec3e9e25665a07e742a0db3ab87cac8f9aa8765bf50edde6b039370 3ccbd63146f9d7b10b80d92ee369e723579b31d62d93c6792de1a4eb498cfb3f cd2d042ef2fe9c3cabe3092cae589b3ac93bc006c44191ec547645ccf67afdff e2d93bf17b1a2fea61e957bfc3e8a9f354ec7716fca36d9d3147608eb3af97c2 8a48888c11261fb5a99f7d446d679b545f9053b2daf6c82afafe43f8ddc2b6e0 1d5a580055cadbb839eee43d38dddb20d970ab5c1a9e2f05c553d299cf495774 f502b134474771d28c63829de5e2cbb68f561ac4424979ad81d9561c77c9a8e5 1d0c147249493a9cc4c6a08a5e1b787bcafe06363736cfa165b1a01347160495 5ac2b45e81b6098c0039a2af48d87b98e39923f3140768b797ca3d33929ac7ab 3859ceeb597d189dc18be59daa68d39bfc2361b820524631bc05e22b78ff775e 96dcad77dc296cfb3929634e642229bb0f748cc674266991ed53d64956d6677d 3035e9feb91d2bdc3917a8c58b76600141a4333dc94499e36b9a75509ec9278a 78b57f73d0d54237460f950433f02b23a8b73fd686db4872dbbaccfd93e4962d bbc589f174313a7862b5cdbe0bdce6ddac58d6167fdd906bf255e3e98ec79160 cf34f7674a32ea2273517d25472e5bcc686f3f461c734e4e93a302b4c9439d0b f880e06bdefa123e7b6a09ba0e0de33fde7cf7ec3b792abb07ad06107e192f73 988521245df44f9840aab96fd90af3c22455058e24eda4f2e33e1269dff4430b ebf20602e0afa497d66b8e34e5269ed831e970f912310e178c1414d25ca71c35 5efd0c9703fc2e020379534b469c526171091147c59d2552a871823c11821e6c 943dc0fc80c35556a653903f09cee980d4f66d9e7179dc26ace15aed32cfb9c0 274eec520fb3250395785dc104e5b914ff88a4d7f9da7e07da6c0830b2f3a1c2 560f145a25c13e0a1f6d9d79edd399d5afc6b09e3103c09f1d77b11a975e3b2c 3b13f3fe0e4428737929f2e13aa57f675e7e3688d8b7b0475d0a950a730d354f c0c4ddf2f2fbf1c0c8ec3873285ffd498b594cb5714c851c27e1397d386e12bb 81ae0f6596c54518ccad5d592123c5960e17b589686cadf3b530b185d93048f7 4ca1879a6b680cbc2024fb37a2a17853767e884cd8e2ad20c2499fbdb53d57bf 56d56a1d537d1f9bdc2505bec8cd238d765e1bed5c245c1054422520d7204ab3 b3c4e1763c0bdb20cd468a13560fd0ec0576d167a3d3d62523067e8e23a2ece4 90805b8b9ad99246003275c1dab6d1bc2dd6bb12454ac381c662048c5bc54738 dfea8fcc3267c854025ec6c917b696c1905e23282ccc042d81ab3e37782380c7 d86baafc5adb032b78d5a7e2047b619544d0d97236d79002d3a091897fa0c48d d1f6ee6693c03674a865ff5f24316e48f844784161afffa57f0bec4d098f01de 9ac006713f84fa6e3bcef13946203f85dfdddac7a471ccc417a65fb929c52d3b 9cbfc2677c60a2cf155dbdcb8065778c7428f6ce8524f483d67eb45e7e9ce463 ab7bd7860210e59ad3231d0309b591d1032b6af933d59506641e55b659efcda3 7d5e773ac67fb198cd86d94f52d8949d3a44bf213980e2e9355451e65d5d3888 3f34184c4cc8f2d7fff24871d02347f522a690866c695f2c7592cd37ce48f9bc 6674fa097442f932e1e25123d663672e2de17b2385b8c41a6a509c46f89e324a 234a35e2f0da5e2182c2450e90101cf1690235894fe8ff5cf7036e03d4423865 f2c8967a786fe891000838ae56ee84891df8dad799971bd87ad9e3aa0dc1d03a 0484249715649583e496f21fec59eb4c35553761ab82b3bf85ce18e67c184ade f6104748641de70509aaaf7330d390f45ed534ef38bb11ebf52ce8e6b1f9cc42 beb938142360b3b1f0933990a5c0e65180d77c2923b2aa2a3f6250c153c8dd13 518d918bf968bdcd664b6bf24e865348f7093ed723e0293ee86a2402b87e5df4 e86e665a415da43c81bb03d895151814ec4c045147a4bcfe37a014a846f26436 1a11a329f7530ab7b31198f03d8d29754ffe53af384fe5d4db3fe90ffa3360a1 cbf7a677e205f9ad968df137584d4c90438505a629dd6b5f3c4f822564b41c8f 903ea1173296e05bc70d6ecaab59f1a6ae55ba8b428d5ea9a3a5e4217ab5dd49 aba326ca541d77ebc99dda5c1ecc21a07010c2945ce9d5d18a2bc753fcc4156c cc007e712cac3f9d64f16ed07dadddf934b1b9373247e0e8d350e5a2551ee0b7 14200d798b42df0eccfa9f36ad0e18f1aef0021e9d3ae7912e71bf4efa947f29 658b34f8f8f575805b075dd65b5493b737b1d8b6ba122a55a25f7053f23da8da 146f311fe86024f1a81034d6c9bda14c3d4728fdbc030901a1ad577bef3ca5a0 9eb53c235a0298a92643833c1705b6bbbbf99e254a74e93a63b1443934ee7aa2 ca1831e48ac62ded5bba90fb71d61fcc7704151495bd72e05c62f780cf9b519c 4d1d68714278a5333173e703409b4303912cf3b44133102fe9eee8842306fe08 4ec22758002566d15055dc51f1386907f7104dd011c8337f1b36fb29e881bdd6 0c90d65ca0d89c7835d5cc8f185c6abefcc68c0fde058a516ddc19aae133f6af 548f850b0f17d7b21e8c1a0d77785253b05737045b944364c157b942d094e5b2 d0607ab0ba606a0765283abdc2db799ab04c5c63edde5554396e28a87c5bcdd8 51d421c5fb161cb1c883e2e81930d9f848c5eb587aa2092431afe368fef3e0ae 5ece9e7ca0757f3342408b957d8a0848fdee5938251626670255ebb0a75ae101 5579c81801b58b99d1238f2253651b143d8e3ec4cc69f0ef5fd5483d216e1319 7bcaa05b83bb272070bb8987bd618c24e45311b57085b5b6a33f3e1ab804c4bb 89d7c2d2338e0b5fe50d17efd1a567460b484d1dccf9d528efa6757d5233c85e cef2cf9153cb0ba4f39713613ba40ff9cad9cbb7e518fda13e721c329e6e38b0 8a831f7a9a03d40371d569427e1c00b4c6b59a804135c90849fb0abeeb203bbc a71b0481ffb4425af168570f2b0b415ed1fc1c181204e21a27fa5862d8a63c6b 99fedf8d43745d034e111b8cc7d99e54d18b53c40a428d2628365b2b720a67e7 d2575e2b349a45027aea08f5b423ae42950e39fd365891533196026f6cd2d496 e081393f25372bf4f1aee37b8bb04921fb8f6aa0fedc1143ad9fce6de55243e1 ac4168afb6887345c8491a395b777fc2ce312372c6655295faed65742ac69bfe 5472335979b474db10d62e368bd14c7fa3681e2aa3310ab5175d47b9b23d4861 3e70485ea10a5d0763c84c5dd9e09a25adf926965c59c0d7d1345a60d0ce6024 ea6173b067f377d97a79537f532b8d2343567e92cb9dc977e772cdc6129447e6 da151c45da028df365edf5ffb819db00f38ceb10fa687d629f958d653c7f7753 4d94eb70a30b0c63513a4f09907f972fa82a2f941be6bd9ca914641ebe8553bc 3907706663fca37513a9a30facd133017aa772f69731be6d4deb4f40047ccc48 32d52817b46b90f7cd04c2aad2ba6f8d44c8082ba397c60b0d982c5c7aa9a8f7 8abe3186bbb742c573ac355421962942ab2c75facc6c898ee4a19a03b2526ddc 9bcbd8770aeeeeda57a9a2dda3b526e5abd65acbfe8b68ab4fb346da066c476d 010c68c46034de748d583b050e2715d98ba852735272e8d11d6e527bf1a40cf9 3407b8a4d8c5b985a20d33d3896cde2cb823320fa4f7a8f2385dc320d83d5ddb 53a7e5fb8541fa6abcbe75a39cd9b0352119fcc84e724a515f3442d4a558c55d 73becebdf4b4ddea87969a7d672b4778586a5711226e7ea72b6d615bd6eaca29 96cc838b65be8f2cc82682aae4948d766751bdcdb162b399f73b2d1b28a47492 2a0780dc42a9aa0ff3cda976a46510ea79ade528d57c66131da66f29046669ba c0e71216cc5e4dbd872b783181fc19b8c838881b8c7feeb7b679bfa700c5f00f f7b1094d3f05868b1d8f82468e3e63d6ee98a2556190e8ebdeeddbe739662e70 7cd1ae917e9fe9b2250d118b320a611c42907668a2a916430086e79fdc31624a 6c02f8d8d410727f698344007fde07f7bb260eff705a031fcca93387c0538136 d0f96ccb71432fa05978ec91f83d522bc6237dbc29a28ef02c9019f7674df4fd 0754988d535e9b8e840d17088c97eb0484b9086fde51eb499f9079c8694b9092 b45a7849a1b66581d71acd062609a406e78566c94863ae128f4dd89db838b09b f2c392a9b334780a36990057be799cd2d96d9544b3388563fac448e2a8e61ba5 ba1ac60d28e8fe4266f7e99ba5dcfdf2c3f03688836b6e39295b4f08356e5e39 3cb51ec951027e73f76394226b502c12549883591c82f40998d70a24433c584b 6349964aa8486223d4b70c8b8fe69145525bd91ef6628e1c6cdd2ce0ba04c266 290f25e23204297c88d4e709a0ca24e3cbc54b657393ed8569e1d9f5110cc2ef 0ff7d6198640d9e511867fc6f6eb733728d4087cbf803cd6fda69743415dfa0a b11f6e8f2fb83fd784b1281372023354d58d84ff26c24f6fd7701537406c00be 0b5a3a445240b4151eaf074d6be3c50bc54fbb69db6f1eae09b6c146e81f87d2 a70b418a5ea1f707725dd25fdb646fc10411acf4ee3495c160e56057d469be7f ba5d677d820274a39f795af23e660a8fa619f6213d0c99051f93006a472105a1 da947bc68bb177db418974a50388357ba6c0c7f7d1c269c6c7e7ac12013aa388 b8fface27affd9eabd46e85658c00b2d1a187938e70aaa0401ce2645e1faadc9 01950f516b23727d50e930d6a7f982701200cc9b4f91e5fdc24fae0621df0648 d1525f7d9ef2244122bc256f431b58a53f348af292547b970214c78677d96748 928e5176379bd48f34ef5acbf25898ddde6fc2de80783d71d01dda12f38a85e5 c4e9fb2607e4e77d6be6651e8f3252ee3b0cb4612adbf38432d8cc40a25b6423 e8c78da2f6c26bec3eb9d0c8e54d7169a780b4eb3a67f60a7be45b3b347950c5 5f198b0d848e36526f7159ede1b985ef4ac6981ec31d337efe6fe896236b48b5 d49ad6a6b2bb67adb3503179d1c79163385eee4ee6169fc060058b8ddb9fd401 80bef6ec30689b27bfee132087ec9308d11746263f7dd4313c400cf98a45724a ccc998e5dc245cedcc864285e40a6453fd2e443a687e0695e1f36887168dc5a2 c6b95bf4bf9d0514ef8f69abba93cf993c4731598e9bef7ac7d1df9b5cd868b0 571028b516c0d6f51d1b7809fd5066494fde172fb962f2a95d9091cc231bb462 f039561da33396d6cf7f3d5234c3b624981a193128904f296a2d49539c5a5354 c254db3225ffbe528992738a1c2a2d659a2b69b7823d5dca1f074768b2d0fa1d 26e8fb8d19c19f48c8a4a6fc8c7ae07d81a20e338b26ebf71eb6fe9cbedf82e6 20c5dc8182e869ee3b7824eb8f3aa8bbe41cbe85fef2416a116a781cc761c459 627f6fa349be0f4fcc820ebab0c373d346081727c41b3e41f6908401c55ca8d9 dd81b44f36eacc86416c791de3b6bab04d52f8a001cb805f71a09572a8dee99a 621a8ccbf534ee9b37d39f83a6f02768bc0bbdd56a9ffad301c905f43f95df1f c0662a10f78456cc0b271e278563c6b269dbf1cf6ebe5cdda87c07b735aa2e64 49f3a3193e87521d12becc9ef14c623e844f1f1a6f55fd6c2704390268f45d90 6623968079cf82cc14b56fc0eb73faf93b4cb5a4f6e1c56f12e2978aca03f6f4 c9f349184647ab482890e8f118a92abb469f62c2756b599706898a7ba2709a8e ab17adae0cb94f56924e7aa5e4dbb18b640fecbeb23f3728694a071af7efdeb1 d3b700aecd64ee52833e70c957c2d6d3f3325321786e4ec6fcd95a4645e016a8 a6e161b23862fcf9f036fa3627f2887089f79bb4e9df6587a23cf40079e07a02 44354eb4d4a96bd9dee8a8e74f2596f5cf117eb2137057758b012ef761ebb94b c66e82e556e827481441c89dbb03bc57e7fbeed3b684ded48c276056bec9fac7 1376ca9c2e462f8d22aac18d284d5ea20b8614a86b30fbe073baeb41074cc51e e098ba48f7f5f85336e47ff10a3edca5eeda1bf9717de3f4262a80e77ab93674 2779e3d23b90fb744d69d5812b0c06bd17ef83d509fd6d5a86cdfd534dceaab8 d66a3585928fcb67eb352804e9905ac7bca6d6011fe5f7e3c15df19a6f9d55c0 9892556bf022ceda7b1f04aaf0328ec0220e468010a235b760069df477789167 d089b93676963402c9ca2305200908ef13f6e5e5539cbd865b44cf6b32499b02 106 c3599fd9f3ac17a7abf7d088363a558cc65594f9846ca105209797650fe8cc02595167fe0b2d7ad1baf90680597cc298a4e8220e64238b9ee818ac0d1c3cb901197e458ed8797030650623bce97ba8c042a816937cacdee1dc80de61b30c790b5269f9d96eb2a1c4550ec7f99060852a52d4509d30e30c6ad480651166ff6a0687b9cec9d9edad0a015e25e0848433ba87eca019da1c3f619632c739add00a0788d60b7e484c26b9457de38ec2209694630f40a64d61abfd0219c3749902a309e2873db2d19f76309a05fe7380966802888c45a87fea1327660c3a131a4f7d0f84c75ddc7fabb0be6d96103d8c7bd354b70c2c7cab6085cfd4be689442fede00de86af3406c041122955e5db1fe4999245d0963d5bab00b9a57c39b7cc298a0e8d215f7bc83a35943a4206449cd0cffb1bd4b4e38674e0249de866f1c92b1d0eeaeb66cacceca6c08b212de942d9e75622fe6a74c12cd989b5deeedc25fa8c0853f622018db4668d4cacfd8e31703bc920861ccbd91eb17aab22787f512d9e0e9c0eb85c7d55fcf8d6f36dae60864d8e6b1ebaa83e050fdb19b0b58601735a0b75832687dfa9287646b10e1317cab57fc0e839f111c4108587779144f8a5fc0aad888425362d7075f35c0f465f3dfceddaa15f3d5ba98663ebc79571e1feb10b209509e263633be46fb90b145319143311c4d68f609204dc78d1da58b767e60068df23dedf83274e3c745f779501410292aaaf3a3e1ca869c26553b2a742a3036362dae711519613b2cca7de675489f7eeb70ce561235cd5a025a6ef3541d60e8ea4f8a708fe58e7dc4b62706ae6b36b5edc4db2c79e5504808c646373f1480a35543c375bef62446decba5b701b9d6ab6fa9ef7496a34fa69dd7b356154e70b48d8f5edac9e6747108e4988d460dfbf8e01b5a5bb1092128602117ae1ea63019ccd049cf57ad4ab59814ff218c68373b596effcd7ac9724c80ef24e9cee6609314223945883af4b688a3332e184a9af97fe5e32d9b1935c20e4f01640833b04fdbcd33f12b644e8214c0e614feb23560912cd9331ccf18ad2192937faeb280c6226e186f3eaabbfd7a91d17e7d601fee1ae1507427378cdf6606b03b49ef1030bc1f997c876d799ded665411db272572baf71147d98c72ce9d74586242ed70cc304bca4eec61392564be36699228037ae4e0f30a3b6dbc3ec04b749a8c7f00a1039e06e5448a63ac0f19b7210ca123b8c6391d9d480fe77ee3a3d12f03a0c0d659f299507c415e168f12f7cff9f3a7340616f59edd1814208fb226dd17af0088f35efd0468bc02d1bd4bca546c5767fb776e4988775cfecfb54e5d2a2322a00d6f600c11298a426ce745fd1c8cf83125ed1d0999e62fe8d6b97b1ace6d19905a093655682b905cfec6dcb43bbdabff8c176d8bfbd028a878c0a5cd20f3af203c3e08eba1d970a3d975d0a0731411f6fa073c2fd9cfe587dd76187643bd3280b96fd38e180420bfa66b7c84ebd30bc9a0258dbead563ba6eb9563058b5d5340ee1d00b24d4edd26b0884e171cbaeeef242cdda9a9b0d6264f55edc671d0c5e01197bfa84ffe3d19b41a8efa617bb340ed0deaba427a803a980665738de666e08432f9e4702bcb37a7161288bf9a4a15f1c7a876aaac365c6d17589a1b63a7a0f80b9f6c1596d9eff3bd4bb4dbac571711314b9dfcd37f1e7a4968248aec47e0c4ea897fec99aa22d8a24064cc1e4d5c53ca2c4b2c2f4d21af89187edb294100e00568f5c1a9423b58a98e734a084d097dd288b63ade91a39844a9d3ca828d5086153b0ea8dea0f853b12579dae0cebad5e5cb47fb1ee2382351c61c689be6d04a82c71c8c6908cda5bfd613378d2adae734ce11093a4420a32a1347e82ef4e034311fd57012c4378908fe1c71568647703d5c16451bc7422ff395e1bf37c3806f4dc672590fe3dfd459796f2661535cba870df5452843db1dd6e9853359608019fe7d435d2c3bb19d80eb9b9375c58539bdde5da3d36eefe1733dc916b7ab70448f3a3d390249fe7d5e40797722496f97bbb68544b38e9e20aff7e99c6e1aa0a9bbbbff6a2cecf105f1abad5f41323cf60a2ea069609e3f480ae1f2bb9dff201c071a7892bd5e212d17e8af5d050058daa2b2a2d380ff0612bb00bc30332b10e67c5e5e5e97be66492e1b081243c53b094ae85d13d0a6994004a9609745b740829ee72857f6de48c90b06ddb3284050c7ca1fa73349295ed9c255186ec64c40b9d5126c2103472f5834d52c4d2b9456fdbcfaefeb6566dc3dcbffce8dd4a5b0714c13ce99a70f75257d5a3a608429506a6d2dc93c3fa08ea455cc4b7921bae00095d3a0d3188d2d30e9e662efc69e56060644722f1b37e1d24c831844b322507546a80f5655838f461937500ce799358fe2e589e12175a6614897e8f52ab2606bdb8ac75c5fbcd192e0bb0a5dd0cfca1c5d0a3048cfe6c2825b70f4967bfc308a2abc16bacc2b47df24383a83f3e530fc29b5f5a87cfe21904c72c64b50a100712ca358808284501952f9d157c7ae6252b38ab014d01f7781042dcdfda02e708b909ccc8c6ae1162c01cd0616b02665ec2b146c8776e4503297b488531b2bb041e946ffae962ad1ba018cad7faf9d9e40fafa26088ace24e47dc61fecccc5901d0a6aa2c60b46f5d85c7c184df48f93a13976e4937c4b4aba35ace357207290b533951d9f6c6ac7c9639fe0a9a6e3a636d5a2018417b1611ef71e4b90b6a6e04f0228e8ba1d9feab59d8c2c4ccd50cec13c9220cf741f33efd941a17b2af5f02b4c010e33221163023d0b53f3d9caea1e8f42b1c0fa6439ff27e281b03ffe90429d4a22401ac332ab6a4663d66cbc568ab77ef541767796e28213fec2fad380976fd9e62881a69c8dda41c0684cc1bf23471bfc67f5c3dfb3b1e18e9181f310c7fa0c8365f54eb77eb5e642f4836173c987ecddaf709f99a59808f2f091a950e8bcdd07c454e97b82a0e4d14a4414436fcee3574e38234778e7eeec57770b401746958056d6d066a78b66ddd3ab65f31a02f5084bb71ccee24b789e5e13c7305c75ec1ec0f5397383f79bbffc16b3149f7e9a9c5c67478edd0a099bf5a17bf0d018167e32fa63da653646fdf400314364c030a132255eee57c0879683871770cae9d5feb91e8e948d1e973bff68f4cdd78e17134b778ce5ebdd658664533850369bd04189dcd5d94f903604ff517699922246ce4ae265604fbb845347c9d330724d673d721dc97b486fa47625a896efefdf5fe3c41e2b792d23cc8448aac8704f88353b139d1f4e749171a854755a600fa206a1dcfad73af9dce6d9398f2320b48e7a63c58e960fe5b63abd9c8602b2cd36ead9964320e96f746985cfeecc70d4fe1bea9512f98590e946ef76396b2f39149f8506ef93f1be530b92aef5bfc05abc726f85a930f0dbc87ce4123a9ff3b7cfecab3ebdbec6362e0d09efcc8200c159cdab45451c59a35d02c0f7e0d7f862031504a06c5b822139d0daa73397900a33710350165506a0354d7bf52eb935ba2092289b4f0bbffaf1754fdbbda3a004120813d4fc1e280ef9348c521aa2c986702c1e27a6454acf0feca067147a60ac68bbc269541a50be4c3c5d4c58ee5045ea260982cf8860f4f12d317a42cac0ada69f1b81fc961f6e053caf7c9ea205548d6a7a2a19b531ce9c02573af35ac07fbdc7030937897b8280d70b23f50be0e974179da72a4cc901a88829052d00c0edff5f34b1132999a30d339c079b682efe2270a6a8bc80952ea40117cf0418705b495a0aaa1de3b3a00d04c24232e71449406aba31debd36e5704bbcbce16300826f9623ad2442a239e608ba8eff2a76fbc8b20ff0c60055d1c6fc63f8b72a90c54ff3ee9addffe43e2764bc2d1dca5a7a717ad3e7f395324f88ae7213131ba0dca4aafe2f3c2a5c295294fe689913ccd24947e839dddceb7b7ee2ace87d94d0bd45994ccb16cde1e6de3042d7f3a81244b5e16b03c39fd48e8227c57a294db0f1f1200823cd481fa10cbf07a80b9eb4d5033865e678fe641b90cdf25860a4b07650be4a63a78fbdc1928f147c77e3e1d254dd82bd21e773808525b51b2aa4a0ac2d9d5f330514b1d5bbc059b2598237ab3e419789690b9fd98a2a3311fa67401986d1559144238410aa30acfedfea75b45e65fb3a33add72a5f838bb4f0b04063db28fbd61d4b6c63b69c24c2dd7e6f5f6ce16b914166094e1bd6043c5885208cc5d6ff63f22416e3b7741de9deaf690fea9e4e8642861a2a6a601699bddb800070a41dc4b951351279bed44eede61bdae90f85879376dc7303a849ee0360101ccbe7d4b6dd26c95c5805c3a5ff9eadaa415f751b77cac21c34102e03899920a569fdee21083a63dd9616286747fb47f7faaa08346a77d2f5bec34062f8d9905cb99da501ef3a2530090e7023bfd58f7a8ce6a9b5950926fd5b1dd194f982702975bfd3bf35e757dc3752d788c3c112f5765db63967fcfc9f2f4ea5ca2009d00a8a2339796ecee04e4028e9b4e23ad338a6629d4a64816d80f7e55e6e280040f0d2f5e6ed0a59be64c16c193902a1336c05a5999f490e9f3476c9f897058e50039ae25ab0087921748d1f5a38a55c86c3b2f792d057e2959ad2d48fe7fef5103b92a7cd703b264836b89295fca913526bc2922e1c499ab4a85deb5e1a94643067ce40d6b4cdf9d45ea104e2305879bb0c0d9f15c2e4e93c12c569e5299d0bf0559a45b658444dc1e7f2c606cfbb2c544cf63f3cec1586f9449a5bbbe8fff6e0fa9e595b3fb97337b69c58a096e4196d0e9dcdceb68d1c32bd91b0eb5b8744d0aa8c1f78298f047871a3027e432d394cba5c5f42a8ea2c5b2083541b011dd8a0b2b142f169253f3025f6627bf22ac3e01e7c6ead6f585af692999a4ff8e9b6d0473c8786a72f0f9a11513d163b1944040ab80bbc56852dd8dd47b880e57eedb06bd0094cfff52ea997c6142034406d89216cbb18a6663d3912e67803db1cf730ab8914aa760106f7b7cd3bcf1b33d1a1cb4ec7c3ee6dea62fbb0c5f44f610f30b3647e3db2883b0534e992dac3909d23b51aa02ac96663414a3b09f53b0cee5043307e95d656cb9ea8a0e000112bb160e39313e9fcefb7d764f4168a19e97030167bc345f3985ecdc0ae589687f34018e622c9385e775bb81efca3e96bc5bf0032c331b3a4ffc143edf7a4f2bd55e6a3ce4751635b8bd30f7cc212aced57db80df13f6f5d273cebd940fe7d4ef8e1d5b4d79b2520408a6d61e8113fc1d9b3c40084ede09f602e7de1397382c6263deeb994b3f956207a53031b506559ee08c200f49977be6e025edd360167303c736d118b1e1796146c169d56ce0792a8290c00943e4cba899c458b051a2d0437b2a7da18a300b5343ce85a19675584b982650b4b291526b70ce2a68304dd5bd9cedcfd4f41357f267ec839bc24d3c6f5906f03d197a921815e124cca619cfee719c432823cb9bc0a95abe35b1c8b4a563d3b06126f8b9673c78ffaab50b9bd4bdaa3926c0561d32b33cb6ea34908a0ad53a705e57f73c18b3ad711972eb968516d32491d98e554c5fc5d531295a549e50a850d09d7a1783ddbb9d4ac2c463a9a85d444cac5b283fd860b6bbb6ee4e74121dc06538721d7a375142cd0b619aa84d1b4f66bb97769847c379df060b4df904da108c045e484cd96e3b3bc7c92d313d7e8133174005806a4b1eb12eb61cc9b26e5026ded5e5a65d20c2a6361de60e93a991814ddfa2ac987d6dbbd238658a620840ff3cde5277c9f0d3c4e06ad1ae6da8a0d45033a6163a3655754d9c5124e80670f30cb163d3e175f199e1db6d54de4ca15910002f2d4cd807592e886acd088310d55b7960c3677d0bf7364db5d9faae209122188961c0e19d47ea116eeb3389d04fd5e53f8fa3ad2b72f302152df460c07950094367a6bb1c76e6c6bc57a463405816f4a3ea003d322e04206b1d79fc07cfee9c26899b71c41c572cc9846a2490f3cb90d79d2d9b13addbb1744aadfe6473f585ae9cd37d56f50eac555b5edf9007826b85fe5cd787ea3744ba57ba705cce3c8f1c172fdd8d10aef72f88cc6900bd7cc665ceaca2f742e129d6d457c77ea8630a56670950cc5df597e3035e2bf02925e7b8555dac8940caa133da12cdd0e18d348f1c54a8b20816c5e17631bc107834513fa33b347ee7d8b86f7ea6859e3dafbb468ce0b9cf6ddb88d8b48caf70470fc4c2e12b7dd1ed1c0f94d71f4ade92304c4e37c54914a3e71449d18729109327a04e107d2181f7a9b86e36e8c1dcd902029a662532303174ea41871a0e4036fe809e71e57ce6b82b3f0a5d6af93e5fdd82452a9c1ff96a58efbf5039a5604c27493b8927a4f318a4dd2c10d5d317a491d63d8d522886b750affb5335b0f0ae7b240067c9303fb06de5358767a11c296152ce37a0c3b9b9b922593bad57d01f956aa02414dc94a755ee5dbb21326ea6524e0fe5397f85041e405846ac13b02719a18005ad8cf9a92de839f2951b739206ffb75ab4d4f98516422ce3970dc037241c1a85f18a2c774f0905356743b6f994cf09ccc5db2b89b79689858c5ac09394a265f1bf60f0e4a426e019baf72812e6c3ab377d17b8483630979a2d9ab07139a4f4ac5b764e174a7f4f1388d42bbb7f4f658dbfe2178503384630f018e02130c924bca3bd982ada71e0a7d24a7024c7ac3b61ddfae959ec41b5a44626a003c0e19524e8058007d1315f3278804dae559360574eb4e162784f99a7062d9098a23f04bbbe96d806eff78a217573a2bb6da2cfda5c0f7f5ab9337f22322eb0c58e7cdc2a0f23f82103f9f64030b1f25626abd1664f958ba0f2c6359253977005373a8a035698f6c9f2e592654470687b77e4b9535365250020a82348d3cf7052066b3881500f5c46ca4db97dbc0ab3783cef970a1f4d4f1b3ba87e6e90c1c0961d6638104a1821fa03b30022e66031ec15d668e3ea0e3ff72484dd337f2e30bf606a62b4e320187c95edb7cc225b3c428b8098765fb5b028856eb4f7cff670d04d102ef4a28d1c3cb141db212e1ade287f8975b036eabc2538bad8baddeca049fbaab5b6da8ffa8a0e5eaa011bcdec8a21cc6e180e96570893bd54684dec90bef5705055d00e5caad11d51478a6e53a34f5b8956a9000bdd51358d5690f5f01dd15e0b0884fa6fc2f7ff5052d5af34dcc4eb596f2a62a068245dd95301f6c026886eacc4dd7c770c94d626ec59b4cc49846ba34aae25a25fd3f4705bcbe47093bb01765dbecbb0096fdad3e35fa76cab6a2965185515ba474ae7eab967f2f0180ab999118d5fbae3adb202003d3e72923f3bb4e8d33ab32bd2da0e5baa5b004bb9bd7a4335f2841ddba012b5dd9741ee884425f66a477fe46d71e1e6c002a0d7099c010c316806f99ffa8e73388f0218d6b9da1c37c370cfd699a5ac460fb06e81ff7e4a1f835b0894e8cf3646c8cb2b5963b3c9059142277d70b1f978784056666709772b91aebcafc0f2b468b90b7a71a2a56cf1043d308ced8249990490392bc459edad192c5220388a41852bdd62c8831b8a93907eefdc3eb376aa9d00309d5c81c1241fbfae37b68002a0d5c436cb8048c677d691517edc681dd956a0046476866472751d616964ebb2296389d16cd346d67d20a11da12bb80dcfc3d09b6de8a494dc39788dd9780fafde080a7148998517381aae05454c249555a910bd91b65f157041086ac9d0506bd4da32de55a64d06327bf8d225a41994c279c04fd051ce9bef0810d0ddc1b4e9dd1618ea9657adb8259f7878437cf583e09ad02b86fceeeab290cc6b812d591f7d4f9e9143d55610fd20932c17bb39dc950cb09ffb1e40b7b681605de2a915f84dccff50541d34f34f568ec629df5c4b185df04b95ec5f33f182eb4e25af99f91af440df26c6a397e990b93f7c441ac11ee8b0ec5a5832e7b4aa073d0de29f59afbdff435aa08384ccaa65c8f70b36faa0172020e4c23df23f5d489bde5f319ed31f55a85974fc5330035939bf3094a9b3c4f01dd55ed4edca8a2987cb66e0c6b04fca8a99cba4d7e882dc403baff915949050d6ce1dc1b99076516849282a50790aecc381f06128fb588e78da1ba33ea9ca60dc246eb01857d17db0a6d669b08c3b3e2967851e43bb886cd33cf8828f5d6a8067147f9a9ac544c315cad56622bc9ead8b69afadc81fe20b632b4296d45d4580aae69e644e6c23069ffcc93d8cf45c7205b775ded3032bfbc2209e84740bc9d077630f79c3ade66ec0c9c4d7c3bb848fc25225bdea462b00e1b8bbefba0b50302c96c69ac2c9c9e57974a733dd2125e6ccc395ca7c7a87bdc7861a12206098900aac2bb5e974bea2f90cfc592f964b122f5525bec0453ae8af8cb7896ddd62f00ca5fa63d050e757a738c34012e18f327bbe622dd30f6e4b54108b9b8f9f47c0109baf7f34fdfd95a61d4b2c7f3c59fc5ecd52961791f297f73d7623d525cf90b5bf0cd7a784f1e7d8eca92e509d4feae8f556b1626cbe8c047d5d290ed01a90469c140a3445aaf633fb2bed3c2b2a30312b98ec18fa00881857373c6a7b5510db5b319c4afe634b7c56395c5447a37b32091ff79f244290569847d1b5d1d920efe05b3e8ada5e3cb3add037dbcb32f2e3bb144472721adc042cc8906ecbbbc0c0e65ad1678b619a6148e6f264379d1dd27545fb43ff133694b0104722bda9d09ace850b2ae4913505c37f222db5898835228c0f42cbf9b24b33f22c7fe80f40a54c0d5cb4d4ad62b1acedc844574016f914cb7261ad693a398c68664760a3d07acc185fe4bf033f50338f410fdf64199231521c2cc907c11ee0b72591f30970c7f6a05e89b205c71091cb66718438b5d7aca900e0a43d183e81bd2c8916f85060fdf685718e72f06950f08009fa7fde9a4edb0905288b49c366eb2a74b74f9059b8790114ff22c88688fd88098790cbc5d9f7855c9f9c4cefcb3b2800e83210d8e10864f1e5b8adc1427b7769d0fb80050e5faf4e75aa2871f97f59eb7e3ba0bd5eea6e4ae1ec85148e1c6535c1c97902251a05ba805f9716990785e400eab0ebe6a542869fae6089701e147c3a0f1057fe3a8740e04371b47f658daf896190c1743591cce483e4a95e9687647e5d8f24e58ae184717b3db9dee01449bb2e70fe5ed585a5b1e82dc52a25ef942ed8610255f29a5ec564a74b524952548e19606656f08578dae8a9b702ffa20959ed2d8cc72a904bde19ad490720ecc8a5a2f0079245cea98e69cd0b5fe83de7223f87075dc27e0240efc7a8c4054c152e3f30e4e83430bad0b9df601f1173dec4f4a41fd86c9da322b278d780cffa9e255c805e2d627f9a33d864c658e0449cd18815058adb6b6707bdbc10fedcd152ccda80e3c43d5f6c29271dfeab06d7c104d727e80ab3487d5cbd7489d347a547954cb06439e54d365a7f0a8866ed10b2c24872a5dc31a92f15c5ba43b643080a4d33c0684c9c6632c70768b85ff9e8e7afebb6971c04939d50a272d6824a176b98321008292850bde080906ccf687b3098fd7f1a674c3b6527d409718f0c2715285e80e6a32c94a6fd96a51806c258c4eef427f21a5a22ce0c0f5b40a6a30a83c53e5079b7cd2ffd96c1b88537cd776e4c8e4f8ba929e4ec777c09f878cc3df81d663041a8e36185b216b720de9a791940ce5cb12bbcd0ff46f870153425ab6454ab90baabf1450feea5338a34a6bd522c6930b9c6b3449ef6dc8c03722df572aa67c0f9c8efb9c8b7198ea5330e2affccdb140a9f826c581b0b027d07883578634670a7e061d4bbdd58d5857ab0c9d986cf306c4fcdcb272d265e075bbcde4c181a408b42476e2cf978b3600353f31ff6e662b3944fd847df1a8a3473fcee22f7f0c040e7920dd689cd49db14cb0728c95fb167ce35a559dab70cc09a5f452c6ef1f0f80051029bcd3f06a275b01adecbd1a559feb46b1a271c07ff534d1482edf4004bacc213d5427aa83d2c63c9bc684131b4907c996c4268a13a7bb0c8d5f568c04ca695b2f7a27c7200a42159d75b7ffa6b467df73cb622315db2be7a6e256050b34c872682b1ba76a37e771449b836ef0a3dcc735a4f7b1f6474ac0bb1f83880f907ce30c4d53814b3ed8301e4f80e0dfe0f93b02b414d3ad092e0448e984700e97edc1d530054568013e5b75a35e53e31b4e1859a45bd06a8bfe2b0529e3ae0c247755958b4017459d25c7db42eee699e3f5b3662f745643f951bb4212687006ab9547d3d295f869b368fc2d79da4823985e3c102a08f3c90fc2279cf7bccc07a10fbb2ac32008011503d73f8a1350928dc17d99f9ea2a60e10c1849b1d9db011b46ad84459f09ac9b181591d7c812979c60559c71f7fa297457e23fbb85e7038380cc22aa0bfbc334d25edfdbc557aa132f3d6eb5c81b86cb4166d89336900682ea279155f88bafbc9e9b420116131f46aa7a4c9aa7d101685dfd6a1a757301b3631002d920ef7d77440dbfc90c73b2e924e546d7352c00047309f68a2c960fc834a9aa81e81cba608d4f67351a6ecd4bfea5975c0718b63321fed600d96204559d4283a2dccbb1c69c9820767bdad4fa1f0ceb7c2344789ba31bcf3803840d77ccba76ab24e507d792be01684713569d0bd89d18d889775ac28fb5afc52c096e39421d23eb728fd897ab9361592d32623ecf5a9e50e30bcf38740c0c9c8c0a0159d691266f9d373392883203fee2ad141568774463270aec890720233d050568398d1495f04ffc7f8ab8c5062e54baededa2d6bef603948bb45bd42fc9e906e21f374dd4c8e3f9f6b6ef6a25f2fddfc28cc0a26650a427cc1d2484b48d4b057e673d61e211a92a1476f8a49409d1888401bf3ed7bad2bfac236afcc2f7dc07e2d6a1ee882e4e1ff2ecd346b62cd7fa96cad3b8219661deb2cce2c96d60f201a7ebae5d5013b888020978f8139308c90e79ecdfcb659104fa9266a6ec9b2c02e4ff5ba9618a02c636e01c29661d6cc1de76c9d635c450c2ed9b9f41d391910d0717729c780f064ee7a1ae8b26cc9c3e0d0bd7e0d25419d5826aca18a4731e0238cd2afffa7baa3c10701073f6d9d474e8d3671b7a8b4f36f75ee984412c1e02cdfa21bbb5061a692c7d7f4b571aad1247901cc7567668228fc26de45a3981022474182a3eb16e922a00f54c30a44344a1a9b96cc839dc849f1b0723a3a04c05fd65770c644910c94651b72bd8332266aa3506f62461b9a15b39bdbfd368110bb124f08dc469084f7e9e132ac61c33f2ac7174e51d3a37f7b685e2a57536240302b5e58ffdeceec86aa75608f9b7705e55040a165a021d83ec058e6005a6fa0a780350f3dd779389b2a7e8926a8c6e2b7394addcae0266bc76706954489c0c003dc292098e5c4a49c12f000b823da13f5e15cbbe2c1553bb53d84ec4996cb00f8db5042d1c2c592be597839dce5b1989f93f4a7c30bd0104ab99150519b41e018cada1b7bd77724db7551d20ffcb9bd4be1618dc95960ad2b2b5ddfe405b520736104c1245e15b3792afd4f70bf24f20c5084978edea150818d98b96afe03a0f0019c4baf17a250276d745507085ba3ccb0ba4a72f5f9e413d1c82b8b980880959be53f7bb88d06987a6e48f14cf88444c4f082a1639a4ea77cb3cec5f593405dcd8840bbc906f2afef9c452b733311c31fc86f71c39f222ca7f8a2733fe3d05d47905e7f9089f9e279c89a041a3d62ddc751fa179800932bb53f26632232706f1d3457801391443dc5ad2df07fe3c9d191b02ca459e0c30a73fa3bb1899280c0078c67117226c2ee9992a13daee82cddc136fc5fd6c5b1ab2e76eac7003c304521da52df2c5b1b613ceda50549e589d08870d26cddc46fd91df60aa73149409252ffb3419305e0ff6fc2947e3c88bb50b7f2cd58cf8fd15a925f5aab812310c5416779cb7927bc3e82f02f01f14cfa2b5f838cb46be26b497d799bd5b945d032085b5a36d26e797266f12a94ac496a526747589a5a0bf2aee4693892715940a994a4af08ba3e8efd0c63a67eb6e40fec16dcb15fab6278aee94a8ceb541de00bd7ea0167596a6bd5b17eabf3e0d7bb0947700c0e1f93aaefd7b4ba09ae38c08e7253f7e5fef906a1aa77b8dfbc359110028decca1db222dbdbb98bffa4faf0e4698287fb73a159242b186d7333af5ba773014e4590a0b2df739f34925778a0e2eb3ea471969630637a872abb5c7cb6f6e43a4f230ccd55f29377a1ee945fd01fc97aff901da84bf65a2ffc68dccc8c6ae80d7e4eb97650fc9d793276cea1b07dd4d6b8361aff22758e78fa21ad5ba314fc71a514fdba96117c7975cfe0d8a0cec4f89f12f5224d480c79bd55a7210a9c7801c026ccb885ab769051a3fa94a0633ace23b654a26e77c8313ddaa82dc2155f3e5417a1485c30d78a3bfc25ad10a93a0277d24b07aee0407d4283d1c8666fe83ba22262ca39655be9d3378f0af044ba52a9a72b01b20efc515ce65314d6728e50775ae83c16e7a05e58bbbfceb0d2990630f425232da59c19c6b020ef3d51e855d756bae6c4d11205de62efd730268be7cb5c64fc50cc0f8717219f2eec52a7cd06d4b3212fea635ba138548d20099d9d806ab7ea5309e5d76d35e4a27ffce2355488abcf8efae2b94a8715b610868fb67a9b2c8a3e6ff32149c87bcce3bc5b888a7762681961c911920f7dd6e08c9a4c24178b694379be9ab9aa317fbefc2e3f67a99a3cf33def91cec49ffb501605fdf79694e12ca5a51b3398935e5bc195ffa77c09ab164126cf19d193d97062d610aff995c3dcc6410dd7258298c71434529215fcce74cc8d70d9018e6e00d707fdfcedca40050908a5c977b1b49c46d45a79fcae9d6134400d9b294e8320cb97b2b52496cf80a1f06ea9c7178d289d54d957ef8198152a155682da469b607b7ca67c62f84ee060000d17492356ef0cdb57147615f8b8a4b2ea124b54a17011a338324b2a38d55ad44eb4026ca5adcc45f6e01aad25858f17f16e7f3e1880e +generate_ring_signature e8020add6aebab8ca5f8b49f2072f631745887fa205d8db1a3aa1b5a4e232d81 dd963b8d2145bd369feed2501aeac1de2fa16ecb1b7cb93564535a6dbccbb3c0 2 9f1c652d42539ae6c7a43a5118d5890a4e1b386b3a400ebd8f2dd5687626dd55 90a86ceb7951f7b5668205c22e8d2c9953d0e44773f7d0159618a8a0238a53b6 5e6dd5608134fa4c64007aecaa340a0f379d1a5c6d4adbf017a239eba303c808 0 a6b4bbc1a960f29d8bf5e64c86f24d7ca19106f22cce2c78322fd64349a39e03d022aa167f277e6cee337361ceef52fd5a71e41d5603077ef10bc8145465d60f37429ece34bad65101bfeaddf6288eadb3303e4c4ee313b7267daf20c0e9a301207e9f572e9e37ef376f0b1019d493e5f3d7bbd50467b8cfca2406902d75ec05 +generate_ring_signature 3e592f32788ec54e3f10560b1380761329df534096ec12251382a96f8cab7eba 0b72892038d4248c430d1ac93f4985a8bb9b43fdf457a83618b7045980b8e6a2 1 526c98024c9a4fa8c6ccad5101f4afc972fdeb228620d774669c13ec4d029cb7 0741f944319e0c4678f4a414a12c6956cf9d4b3ad04a40cd6030e47ab7e9700e 0 80ff0faa32c308136c11720c51480f1b8c90ef2a1e3bbf72586fb6b5974a6403f71f857f3b7d54fab53648f37f6c162b996c7e59c2b8d85bfc193d4553734b0e +generate_ring_signature 701268c079222197470218f73587bdeb19c3620f25d1511042edabd0efcae448 c2b7340137942802b68be0a9cd0deb826a43f7645807c8f4179c64b11021c36a 1 515cc1cbddabcae11062f52ba676265114a44372719b9b5dcff7865cfbbf1568 20966d94502e95dffb45bf07173991a5c0ebca48c0941c8c6705f7370bb2340b 0 0d994e0088554b40c9b2b1283bee325ae62d8acf6f12c40fd63305db818eb10587801376ae209e164aea4f23986b8b2df279dc71d0df51961a42e8e04c3c7e00 +generate_ring_signature 847a7cdd3eda4d087626d16b975bd8dea19bd914d35051759ccd76080649a606 a05d0004f793a217255613e5b533efd58bf1d00e9ffe7d1062aa6eaff00bf614 29 60fa9da64e6873d5585d7c6b445aa994181e460fcf9c4edf9c2562432034c418 319cab68723c9c65b13df37ec1080812e182838e83f12a49d72aa8b82f8a906d e0d78f852a658ba8e2812978927a42caaad783594e463b73d25bad7ebf602a5d e5d6e07c783c40cf86479b179ae2c3064df13826f6ef66d1b5bb458788f628ad 97d175ad6b1daeb14df659240d2e567b269604bcbe62615efc1acbb3ebdad4de 5915b9e450d09664efa12f6477a526084e9a1c16c814f366e4175cf03f0bdeaf b08b9168207c344ec1f1addf104b06f4d396fe0a703c6fa22676549e221a9819 e28dd982bb083b405c5888a05b7ff932026a2d163febf55f7f8425df7a2381e4 b6cc0bc087d836d4e29e5ead0bfe51998943e147c259a851e138e931d6ee2cea 6bc6641ed933fd309190317c1b43bf34a7fa205b2cfb041c9f8d51da1775c09b 2438d50597d45379e848462033a037b7281eb6b3acd259a5190d9279e79a3d15 eec728d14ce0721d82e552a13d446866f3e43c5fd45d8459f6517aca1f703e38 6fbf0ca4da28ff0d2457d63f48864662748274eb304d6c22e3bfc83296ae0deb 51aed9ec8aaf554276b3397a30f18f76eef129585a5ac056ad9db42d6b062ab0 6d3b31ca3c9fcfc03be971f1107ec82cc5f5be67a0cf280c899ab566ee3c3cf9 acbf10cf496ccbba0ce691cfcfb240d1392c583288e0dca2b562710f0acabf5c b227447474e2fdbd382e6dc62061f4313c040bd951dd46d380b1fc4b8dfdb341 93ae8143cbcd9604e8862ee5d5942e90114e5133fd55052cd2d9c266a858dc85 2d90f8a4ed2e98285e9ab6938ef20da123f365b84f3b269b3ed03dc885e2c86d f7a9bc8f7901886e7f5b6a62c0f09022281d213e043df961c9bc61554ef4c8b7 58cdbcbb72356615330903011bf7267c8f2d7d61abfa88789d43567e51c7cfeb 742e0b7bfc9382859b7531e43e11e99c1ec1089ee901d502f116742194f78534 bed2b49b3170073561c62fbbcc1397c1e82ec0a252299234709a02b5badbe17f 6c7802b9d92c53b2b8ec8f761a260e680f92b4577485b5f516fc594984381047 3f32e6f5e41eb10431e1113916f94976f4fcc9db76edfdcde37cbd20a22756e3 9031e2a54db5f2d4358cc9486e5b25626c40afc5c43b20e260b0bc301d85bc41 02dca47cae77e6ec54842ae1a80f341adda1e0f5f78d016a06d1bc5595b7574b 65399dff20e45ceeb08f30f42a1e4f45c7c086e2a5f5c30f0645acea610cf4d8 894d72f759785abdc31f519d816040dedbb4df5af332187e9265eb1d4051da06 6f6da025d6473c5c0f30402add3b401e177f144bae5f907af5febe3ce32aac0d 7 57a507ed3a677ec6dbf95dbf7a5ed94c0f933124e073ba5b959f3652d27e6b073cfb78982410780c2300f564c0b4e689530a6464508f1d52c361225f3403b909674b46560d717ecf83896f2d7f9288cfdd34cea3df36bf12747f59c8d176930f1fbf4a13694628c4d238308c5577b0d42c1a7ca9aca6c623e460c6829de02c097ac48ecfdab55f4f7a43b0755749b792c36f2083c35b51e2af567038cb5ce90ae29c99d8b0bcc8d1d72c31f162832c8b8289ddf4ff393e790f7c49504be78e06cd638dded60e3fd27815e03ad7e1cb9a4cc2c46df00fe0719ab0a4f4c9b418065176e8126924f3dd323edf30c99a39e60e89d350604cc47554233e02e562590696dd7fe1cb307ce6d94d5e63f7872f751a8bb956b111c5d02efc949165f4b700fe766276c37bbb7d2075bbc2559e71cae5e42729f1419cc7f34292b8bcfb750835395b7271f667603008f358a423b3effd543525b7cd45a9e2d1342619f0580520051fd65bd303470e17e6b57a1de37ba1b4b53e403ef242252d60a7b626d2094b0a6751bdc36640511ca8aada38d1c80e97b68b9daa85cc04cc66aa48282004ddb1e09a179c6a011e39d42bb593a604edead19b7283cf93625676e27883590efba0fc488666d9535ac94c4f7d7e99063259d975957777aeff435195196d8401126ecdab0977404aec36f58aa1c87cd8b08835c15a88a7029381a00c45eb9708a113ed83382e08f716771910f88f3cf3e9ae50c7591527008482cd91bd0a4808d30e2258961fa2c08caca1288a766f88352bf1ba5bc383a28065ec63feddc1007e259ec20eadcc56a9256aac2a24f4294fb64ef8ea0d070ffeee714f67a3f20cb41582b5d72012cff3443f3aaed1a9bd2b83ae4005f7d1c7d6b48fa747546906d5186c7d26a5fa4a8f98b2bc74a541bfa0ea13073fc3549fc559046efe257e0ca2d4d75805e9a179ae82ace920c95f817954db949020974c914b5414423dfc0f440f240595f3d3d8e2b596a02beabb0a98a41e33b1c1de7f93f1275cb8395f03c0b02c94097d4b64d805029338fd15018e6104aff703450e82ca501023e29209b10eccce95abb990080a662dbcef3eda5ba29e37953583fe398c345fc5399d02be48a37bf3bcee1e54f13951099deeb0a95176225487f5e0d6fec0b28045a105fb739ce47c1a7029e70f78d1e9558d29f91a974890dcecbfcc38bfd9a2446c0e87b0f2767b6ed56c8e57cdc6bcb2cac59ae9e1996b11b50f96cc0f083bad6e007116c993820c30c29c6c63d63f25d2ecc488378f15598cac622d8897aa0e970485270b9f0d882926588be9b46b2e2fe2528fac404301488c1bbee3fe2fe85b015406c34ac576e2efabe850c4f364dd4f3219c353196251ac4fe4fa998148b00a36d304107c7c64b4b4e9b0fe6c140638be41527302c0acb9fb51ccd5746dd20c1b7538618d428e2cc35d6e74cfd8a8198d8f193e91c98a9d56c3f2f89cf19002c8f96a047328e1f6f68e6e510e50b09f9a5a847fbda2f922aba35edf111f2a0983e741ee2e0cc2476bb912c2df27fee86c8c0a2640894b578a8b67fb3064a0090233a8bd6310a39a6075cd752844cb5ee6f924b410a84b41542f17d88af5010a3b912e5e37bd4ec0433c6429e944b0e1232b4ecde6bcf1b96a6d5104d770c2044fa4d1c3885bc26d780fc66a322353888158d18f6e089af0b02165d6f72d0006ea36ffa5f86627182ca1faddaf9e86d85521e3baae571bc0524987513f0b8406d353ede948f9920c4172a12143902caa700e7ea9e2d91c8dfb681da16ac0240ace547c18836051f38a34f8c406ed36064ec88bde6712d7e6cee851fe4ca83b0fd6536d7dc540a442fb92a96aa0c98f3db85ed731608cf86d7329b8766bea31053204e0635f65d7d55b65c76a1100ccfed34ede7a456872636d6ef547040b5e040aeea6beed27a6acf5e4a0f91c56ac29b819648959d56a06e822046802687e0aa5c4170953888b62db3b528ce3e2b4ce9d86b3a6bf4c190d589cd93865eafc001170fdacc5fb7dec5df5254f7c5278beb2bea0875b7043c4a5d04bfd7bc4400ce0fa5a3f133e33b4f66728191fdcf2e9994773a6c8846a1f8f21fda4993b2c0378686f2cc5ed21e59afe37f98ce0743b92143c0e83457da706eb0efa82fef60e6d0e4e72da366d9eb614207e1a509789f414d1b0a05a7c8e91aaf1fd4550b40cc0d59139e181bdae41f9e510a650014bdb24df5d9b61b111e408b28c9ec5b808c242393af934775d6b7cb48f29783e07fecf21b3f7ece63b712869d5c6dd9b0ed47ae62af3517698f33abfea850b1f34ede4ea89bad89919e73ba5488e120903db1ab87a677a101a03fd2cf2031bd5f27c943966f4c38bdb2f8ac3b85282260c1fa7be5014d236465411f906db6826391ecd77703442c294af2c6c19ea347a0443944798199b945a432025b3d8b1b6e07843e0fb424d8677ab849c2287dc3d0259d60a342d8607a23ce03776aae6d5a19db6ff8868071b9b76d90f29dc077304628215a00615e934966d1f7b32de99b6a8e6b6f82ca85350c25d770367655d0eb1a82125d513992687f26d0f80aaab57c924035843514153bc3476b19ac77000 +generate_ring_signature 21b7918f07df71bb61eea02d6eaf44427254d6929fed711b69702a07f2581ee1 2170c653c773adf0ca992a320751335723a37f418473b51e05f39f4c56ca0158 198 a7310ff48dae857eff6a14d2feaa21e9d2002f36130b16460b6c39a4a7f9fcfa 638c6ab8292f8350eba3e282099ff5bf2157e249d6d8f4817c1710f650107663 15a1047de62574f925b50199dab771affb8f588651f52d19fd6412c40510ec84 fb40ef2ea3d9c88142c8014b364e0e0c7a3c588e5f45c8588e54450d51b426aa ca504e824c28187e327b5d7ae7199a246f73858407bcade0f3ec9b4d51678d6b acfc1fa1a602a4e8d7b40f5db21b12cd4fee3db4ad9dc873f6b2f835573ff911 a0c976e070ae6c6ac4950b098f25fd6152cce78b02f9c781500fc12714ca63a0 4bcd175516e2d4f9b189dc916c29ea08625a10439ea0a81ba3ff909ecf188edf 8c68075275c103c3581e9e0c3b70687278dc986cdf603376d8257e4d5d95808a a27f6a62d4aa751c9240553e0a3997eb518033004ffad5ef037ee51358bc6d2c 738354933226891098ecc542f68555f2d16326e5dbb8f8c2fc91db0d93db6c11 37b94158c61cbbd44dca406bf67c8d4c13e7d0c8c7e9d6794d5d29f576c477c7 dc71ccc63377eaa777e6f591ac890132d59e63dfd9fc26d1ac5d90e995fe2bd8 a9c3655c5e63cfaff27b9b7553d34b8dcb632d8d7eb60685904c589665e02996 35b199d68a146841cc6b62ef32f7fcd470c6ba3ac20bf4bde67ab988875ca553 7e00d706b974eb2cdec2dc4de1e71773dc7c7dea234749b3dcd9cb6ceb3d68f8 515e89a78026305838e4e059b0ee9573c61017b25e92bba60f61ecfd06118880 9fdbcc0a4a7b68abb0cc94571b407ebb3300c1a37cd1d629dcfcfa8ec982afce fe328fd284ef8f8ef08babe0357c39c1afc76657b3fb3e76948c546dadb5ae55 473046fbfc71a93af79fe516e88dd2cd2e4a129346fdafeb8458a887434020be 76025b240fd55cd3f0d7618cb987353c22ee6ff017f68aadf246af80d8f53a1a a3e4ef02949475560168d74e757d636ce95390013a9f7598e5751c785a0a3558 ca46eeb8fbe1b6f71eb46039987aa3a0333b2423000e28c8ef91bbf96574b5a3 756fc67e7addf14dd110af278b6025f47b721604970da8af89988004a74f30c8 430ee30d84f2bf7dd3d73f6cc7d25970b0da0e44b346203eaaba6b87c8d1bc49 f0bf365b8ef1c4af3c1489f969def37f165f60bf90c4b1e7d1e64caac02e0d00 90d8d60b866aff84be78562d69efb7c75ef8ad43a6dbf8180c7f3bfa55385f42 81fae7a46b3206690ba6592ec7ea31908b6b7426eafcdf4b72a7d867db80c290 47dd69f8405178eaaeeb253b81d9854ca3a5cf4dee853c9f09e5c018a888a990 3dbd9c96886f5ef5ea553af02f558c12699a62aef89f0959d36c11b9613edb5a b6e92ea527b2d00198bf9d08c2349346bb9d2e52f9a58f6f21886762a8bee77c 5137c2a7ef939c6dd71c516956c0bad82ecce452fbd92d80ad2f893d5db044bb 83b98b57a098b4606091163dd68af52900d995955054bfbf8dc5be2e285e67da a34e7a7bd0709f83d3e8edf3f4703e8e78f5a8328b8b537001ccd78a1fea0099 04bb7b526f22df2a7fdd7dd6d96a996dfc840d3c52e51407e6e02fb48653c17b f85f3534c07f3ede1fe71fa8ae9c41a7a6a8ca0114807f27c3a648e67d010c0d 30772180f96ce761ddb6bba60e078ad2697edb3b6368fd6b5532cb556a14a927 246288f7cfa451ceed0911a552bb87eec77673a9ea63868c95fa1a5cd2595d28 5ed823adf2b16c339daf9a63307143086bbd5d9bd48ad7884e12342177b93873 98d730c7b3ecf9a20d36270912edfb78e51f7d57783caa5f0fb73aca4a98faac c9b1c8b87c927d1ff20e8e11be50ac120ea712d56c79bbd2b0b4a7cdc380f0e6 e016f3e3bca138e242edf9b041cc9fe41ebbd76758fd4648b617c8566227d19d b4785c29d4cf5ec8136a91d82b37f00d32cd9f5c0cc4ed9f98d89e9e12321501 d7530c1336052f7fbad4f88f42f7b93a70d824ffedfc0c677b021ba884f54a2b 8a2b6f62da42c1c2345040a426a328224b6e1dde8046031bc38aa198fc946495 8ab5343f36337e7292f19f4f0cf0ed20f7129cb685d46960181ef8ddca615f6c eb77146a3a92895571947e4624b52f76349eea5415a1f0b64cafe44a1d86d427 144403c29b2301fd621b2192d294770c43271c4b1a65aa8bed9820928939f9e4 6fb15a7b933b936a2b00a1c574f114c8f1fb55109c0db8fbb1f6aa0cedd24411 5c098c6ddcc9775715a8712cc941dc760043b1be592cc1e794ae2df2674bd12e 3d3aef339088b60d7de300b385eb5740bd7c45dab071724717e85d10dfac35d0 1f965c8bf7cf38446d5cbbca496b9f4e1316598c9714ba751abd6279ba119fbf b50c8380cee5ab2db21d19d3790fb04dab6abc77ff660d9249ba511823270917 50f3f18353606ecf073b404fa9fd25dd89081fb2558fa739fac549332d81f426 b317387602d238b8f8e60dd59cc63418f3c911bf08e27e743802a6056d7dbe6b a67b3f52133c365193b3d0b07a770ac1a9ae2f968357f405ec0c5dc764d9e89d 51fb034fc77c16548c523577fb83026394dffcda641352e07f261046aedc29f4 cda39cdbcdee442b798ac1c3eca2c9fd9029387c5b9d6e4931ea007c69c1438b 5eabf6be3369f04af85cc3d9ea07eb7d36d5e8c917bb4ac82f65f783087fbef3 b93813588e0e1c30066efbaeaa1305afd7f3bd335fce47e11eba8613c2332277 2ab4bef429a6b5516c179036065c2f051e75cd0d39f5be376db50fbe7d7b524c 4ba8c45569fb7f9633d85db9c3e614fee637bc57f7d8366cdf43384b1c07d5ce 1c6be33f9df30233ee408d93647939aec0a74010acdcb1aadcc828378bc2ba7a 3a5440df19aa23f49469bb5a49f03e94dc8e1518f9acfc64d2992a424c3ecaae 9e251605352bc394ae6cbc7075e8d83fd2c7b8de90c327a3c3682e1b146fe87a 806ad4707c67355f8cef4711357215018aaa16178497d0b7870c27d8319772d8 f6ef4a8771330a3b101dcda998edd72efb655dc5a061dffa72f20fccce62a05f 458caee8a3eb73594db714221608f3ec2e8307b7bfe4ae6076a695ecb329c105 430bf3feb7d8c15d59db56c928803fcc586b1388a9f55864c9509af0263b1583 3c481cab8ee77c6c9182c23a0813c673b5a79f9e39ba968911926127b90fecde 354c79b857a63799189c3fd2ed192c9c5ad45649389191442c4d5d3d653430dc 06f2fd30509dcdbeb9f1b1dadf07a2c1a0f07f0c09ed89eed7aea5d177ec4293 185158f86c6ae379c94c02a972d6a2b7b509611c01fd464e42d1f4b6108312cf dc9c395819a1a1a4d441c34f826100fa164d746dec8e4ec67e2957cdb66bf496 3b2180d54f65060b21d0e3908eb9e61d51d401c3dc6ba7f9a6b6601ae9fc25da 1dfd4f86dab80474c115ced9f14e9654b8c0bfe960d4783ea749a04af99b329d 2836b7ec30ceff00da114f97fb42827adae4f851c960a82f3afc7649b15f9af0 e2f4901c3f23a57de9bb273e7c475a6e95cd151b0f124913ff8793a54e86b00d 3dd28896b87d391d35d51a898a534d20f08d7551e7b6e94bf1620e35016a8d56 29dc089a27b31456ac0015c122b2f01fd3c2df6a7198068ade70e56790b5bfe2 5f9f159b1955da1e199020a276d8775f5ddd405986545bfaa6d760be0b4f43d2 ddf00a1730b611dcf862d320af05aa81bb4f514d36c7debb5ba42e098a1afcc9 68e9c66c54501f39a8a15ae6ae4264acbab0b5d80de355e451d5290baae5c5a3 7b408eb753d2fcaff19e4660d42d3bdd8ef59e2064b85432b324090d59131996 6b0a02f9766c59032201c2c5b04453741921cd65d5150740d99155d2dca6e647 5c06dd72b563394a34e43ac353e54551486d2be717327ec3907e4eecf84f389d 416ac45ccdbcd8b039df9243151e7b150d9c8d5ceb663049bddf4fdc5b13d35c eea9d96242a44976de1526661b40a94932999919479b04d5c542b354f7fca5b5 2d3473d34a84358e2088f6aa6d9fc1035e3bc513ea613fd6604589f0befc56cf 33f7f9c42a7a1f400f64885a3b8f754f7a37cd240568ed89eb4b3bcfcf627adf b10bf05f99f3580191561caf04194fcdeb7b2354aba272159f16dde7688a458d a191c822cd757e136bf17d7b3255d2fabd55b92fa07c3fd2e7769290ccf759d1 6dccd76d0adc94bdc5a3ef4530edb326e205dacf527efb8f46c7be4e21ed9a0d 6f6c9c24f0a9dfc75ba05a829af930df9c90641e35647ac8d9dfe3bf162e345f 4bf557d5904b29c5ebfd6e8bd11eaa8c1ab125a5617c0e26d999afd1da4c69bf c5e1cb8996e6e9c445f5272b25f89ef9f89be61bdf71688f066a8156701b3c14 70e3172770643e04ec34ac004259f8d2219259abd222f89650f41861d47b1fe9 e6be521972ea378c4cf5c41d0776217078f891f04f630497c98e9e4cdcf0eee9 5bf7784a3925c89933199fea56841277e029bdb8e4dfe7ac78b8467d8f84c97f 70f5ad88720513a888301c9bd51a0d5dbe12de43df5d52293153547de0d4f8fc a62e3849fe1b727cf71319308e67d346bb3fc735e27152893b63ee238d0af39a 241c0ba2326a2cb22561110bfb366b7e6a6eadafea230a61dcfd4a9f6b4dcba6 568c41fd4e61caac83dd975631219405341201cc7afa1abc9853ae67a0a80713 b50eadeb0d534b9d1d089862655f4fb15c60763edc542dbe8b55bcded4e16027 f96b13a63ba656428ec73c7d5f6b22a5cbaaf54426b49252428af975333aa980 087e4e0d721080d22ae9b7163c846daa8f5b589a6691e8963c5da8025f9624a2 e24e5da5e60393b9553fc0a0b7a519fce15c3909ed4d79b1204a67dcde2e4569 8892499bdfe206daa5cd2355d77651010f3d4bd3dba9d9f031bff0754c270479 decfbffe3e059ea424c13a18e0825f19128bf74d3bb8bf0ea9f16c77e2ad2e83 cdd209bf4caeeb585551e50944ec07a319f3dffd5b09a012d18f5f96468b52d4 a85025abaeb2656c57b586cc1b592d49b01b6041f84b7c76d74de7ff226361f3 4698f35cd4064410373adcf01d1a5cc9975401fdd593451d29c8fa8685284791 776ad642d6cd0f9c3272ab6cc24064c5ce287d0c4c0c970bf796249515244965 815a7d461b39d605d0cf09624a01741b100d7d8fe7f5a43be593ac631e5f1ad6 3e06f180ec6b34f02d9fb05ad6b1e18f61c86b30ff481ddc5998f454d1e66557 053342a00d7f7e7d63094f10f2db6aeea8bc2a4604bcf0780b06f670780f5fc9 0e0b9d2d83b917845a3f5e73a04137a499d9e6fa8b867ab98bfa82b5fd76ddd7 8a7478a0b93dcc275f706f458a0f3f7022ac8812b1a0b3fb59a17ff33887a926 2aa41062284cb512431c3da7a24fd4dfca3b3b9d4746e9c281f0a6530c77bdb6 d2aa71095261507fdf7ff02a66ec2f810fb4cf25a1cc061f2fd9e6cd991d29d8 749d7d4fb454e0441786b0b83405adc7521b9133be1e9069e98b0b4a0a019742 9402fb817cb1bdf0ec3b3ea7f0e6de5af7ba40e5dc449a4604ffbddeca3d4bc6 7fb5ad8c1c39dc05b6b9f5683b0bfe3453a36ccffab3d7dfe8b0b24c9affd6eb 1d5beb87c516b2f164cf9b133359af4170f80919ec18b0c32130ac1b72384813 d7a29f1eb4fbf3d5e59c92fc82de7b21ae3dd5592fe5bf14ca947cb07f91f294 7c4414a285d08785d7ea218d851629f3ea64cde0db5f122a7d1234f2846679c1 da461281fb75f952c2d820e634628c9b7ad8bc85b2652066514dbd5081a8f001 24ed659f872fe2ab6ee3ad8e503aebee8871399ef6e8173a9a93f3c927c9a006 302b8bb71116213bb79816efe7111d60102a43e2553085ac726b3e9de9ebca63 ebf8a047c74fce459c1cfb3a267c973f22be621e82ddbdbf5c0fb8e94d8dedd0 82ff33c38beeae7ebc65ea166ca06e0838209719d9f68574bb0403e7a07951c9 856688e811d72ab66cf00280841d83d21d9a5982f971ca1b1406e5c205ccbeff 9314feb63caa7c71cc0c2e1957cd334732875c8da47356dae4982c32eabc902c ec90e690723155d6a5ba149f7070c142f367020035f3c9f7fe394f4c67ff5bcc 222caed12c17c95bacec54ca61a7eb3e37710521dd268815f025c082fa8249df 532fe8d1a928b1715dfc99a1e2fbc51fe9dc599badb35c31bbcbaeb53377253d d41e747309374c7b1d87c86affee4d0747b7a1d9fd1d127b54891d896008c6c1 89170b10570cdb10d9e191a8a73cd46f8b9698db908daf7fb1dc9c7ea34e2c92 4c7c8409f431e936708106858f52c7c54b59bbf7485f66533ca079bc93128903 e2e9f74f6d1ab74bcb1c276143e6bacc3656fa372ae0c55d86365d4bf87f387a 71370d72ac3428b928fb2eb392b7f9b08b1e332c5571b720b1062e60600d4566 3fd75f265802ca0b4e7dc1b45f2248cda782bcc3b60571b72ef62b3980fe4801 094c60456ae08e383c95c2dcf3bc2e3c067762548d57b89bb7bfff28ea5de068 af19c103bb8c17788e90adeeab90d5744cf8f7272b83c51dd1c5076e9da6568f c51e9883759ff945e2072a319879dd1940f8298b2752fdc99c3eab5162a9b9be bea441eae3c9b9554bbdd4f797920a478705209d2c837dfe6a43f76df9102375 68ca55b4f18a7ce36e32f68f31d6e55e0f6056eafe2066d1f4f10a1d36e4aa61 746d492d44cdee213b7b1ba63f380d3582cc84b187c115f13489cda5fa7e56ef 89407326a649e5a0904d2f02dd4e488f88a020c40a9f91671fd3f7c2212f00c8 a28169cb023c0f991bb0035546a78d954d0cee7235bd52bcf88a5df99b557f56 19c17f8474d5daf47659a9864cf1af09a594663270eca53da879650400469a92 10ffea22bad2190a153341c2b0d21cb3a4f907b8a7e926ab8d0c41382e4b8058 5e0f853d144bd9e6f460d06a4a3a39f150448b88835b4532c41b2587fd4c0af7 31754c5684b404654bca97ea38f2c00ad3e5f4ffdb97fbc8668ec58694724d82 b32e6d9773921282689cc369c0312f5a0451e66ea941e6448c98f8e981d48acf b147c1c3510eeb037c72cb71dc37425e963685f84f32a50cc01fd408d0bf4d20 57d2c9657c3e9a7296bb505297e55796b77f5d3db2f4fb1f09d23771ac62f398 b25cf9566236c3997d4d997f3d5670f676c13965d4a4a66fdf6900fc4883618f b7b93b8daffc9d5a4e47f4757a70e21a66df2f452bef667140299482f3173478 7098f7acbb510ee3f9c0499b2395d96f852d21376186f275f02cd7b2d87faff3 8c6ece212cade6da775e5f059b0efec5bb6136903d3e5ce07edb1ae45854b6bc 94887bf7a0fd4b7fd0d2da3bd78c13923ba3416a1e4e84c90aec94b860541a56 176b865ea64c0020c7e8371ffee45df70ee0f2bea422735c30201ee2aa31771b 45b58bb64bac42231f7f98469abbb0ef075568d99de4d3ebe2a480fb52cc6c95 10fdf36454f77b8909acf5478166555bcb2474be93a5cfe6d4c882c05e08d3de 4b0d03f74f8a5dadc9ac19dd2b0811f5b73b4dc60972dc052a656803510b8e86 c275c94455f1a680592271bbc61593c4dfbb38fbcad68743c1500cacfb542d03 bbde1fe13a989e8ae0ccb8260d4c1f3330a337dec7cd5c20dee2ad781610329f 50c2ae0be35cad3b9f25102f68b7e69310e1829d36ff52d23a7a000b31caef02 35bb8d2683e7e25d64547774b2c9e8553c295c367129a53cd2ed7447393358da f8e1f760cc5b69685e8d68d6d023fd5760790bed603f69b3148fb4d245d07e29 ea4174e4e5a6844c93f511af95d507c45c939ceaee14b0a033f2c93fe8f5a040 4f117a7382d9d5c50861b62a8ec5e5905ffa6633ec41b00bd61d0ffee78dd05a a88c98a8ef1d64f1395a7029525d0de4b131db3cda25b1a401c723c64cb5e357 617280cb8f8e0e0f42c62c53c732e87ca4fa192b578c1120e40905df9e8d8194 c409b94d1cab6ccadb8460b3d131cc18becb84159897e15edd646ec4dd4fdb9b 2e42617d8e1bea79f0f399443fbd5f15947ae5e52abe17726ddb03ed0d256d86 6a73186410df4cd8670032cbfb4b62b0b7f95ba4d2c3bad16d5a053f71515852 7205bdadd2305aea4b0082ec7bc8a2a8856edee2560e02741a775f7a0ed2e1b2 cc7ba0caffb42807788c1eb7944ab97f8fe62b73c41300131853c968347864f2 78d4acabd2f716af0d52789f27bf35ac8bfe3b95ebdf90437010ef89324fe1e2 98198945989839786c18edf339f538c065e83985d97dd7eabec1baddef389457 603d891e289f2b8ca96b43ad5c54d284beea789a4ae319517833723feb4ea9d5 8eec45e1c95eb6a99c6223b40f6b9e740709941c848df5ee5c4a1b23f100e68c 948afeeaf126f4fe582998b6cf014b9ce21b3a7e0cd439f40be5384f5c7ec905 2702a8ef3481001ff530a56006930329e095a1f73543b515159a0412d53303cd 1d0c05dcbc95d9a736871d7c12f9bf6b0455de0c6d19a17f90680a39e974b96c c92cbe487bb69a8bfd16a8fd9b4f2c272f564208f1f6a39694ef3a72b7f1c236 5d8db733373e9343d771cc78b6f7fa5b5fac0e5b7b55ef6115c7611fca121150 ff9701d74d4540e1b6494044cb26d0d1edc2c6df60c5f646753b256ef187fcf8 c414793da4b592458d92c8116978ae7a5d3bfaf0adf1a0799427907987098949 83184b7d1d49adaccd9662c4004306d458a42d88d16088ee50ec8d82c9d23256 35905e97de54db871c52e900b7b28c9524050c8508f547ca355122781dfb8dda a177e03fee74823cd5498a30bb30defbf7fcb04a77c7a03ea9a53f166196138a c589143a5a14b61399ac8303294231661f6871f01fb323b645415f9a6f854e54 223d256da69a41c4391235895d0afc14513ad9b8bbfa6c19475a07287bd591dc 6da1397c2980f41a9802a535ec35e3c605cc0af558a2c08a44435c086491c85d 2570acf82514e472c79a88e3baaf0f070386b8e68f783292237d920b3155de1c 59d4fd0553f2577e6817728152a7c6dc652fe69f78bdb2a95feaaeb37e8dda0d 65 93c48adbbcf83357c4d8bff882b466e9b49ec773621bc22e7d6bb9155ac0ca003e4afd8419fec08143d5924585375929c10c9f6614613c18cb86e9c32257f007c6d3d556bb6b80a269af77e665c01a76e260934b35f179d98a2c03b973222b0a7a48eb439c17d011e66708e35f8302d8612af85d4cb941ca4feff430fddef001155c30767941cedda2027618f381cf77a89801f8d8ea38454b9f98a5c330550f3a79bfb010d0e451f7ae598c143c69d8bb400192d0c5dbdadbb183a08ec727019f521d7aa990524faea91fc83950380d37becb055809c177ab0b28cee792f20e6744493028bbf4acc8f0934aef8a31e9c647a5948742e694d088faef8f5f10025ccaf3eb9e6f00f652d92f376786066b34a56dd2a96b7a990cc03bd5e3d2670bc182ab95bc9428782bdf3d24f8efacf8014a5da84cd0d43d6c55b799223d970be2610fe5c0a06b48fed19eb8791315f88221416c193fcda5857dcdbac38526091da71286ff9f828d7dabdf409014b904a9ffa677e0f5d5c0ce6ef97f88860a0bd0fec565ad9bdfc070eb1aadfd81995549dabcf1ad2261501808b6aa6a2b820fdc5c89118dfac5b9b2d8be5b8c5e0e649ef0e3b514e6400aa81a131f985b180cc239cc506f5fdb39e31f953d45b3856536ed5eb90904e91b43ea15aecede950853d506cd5165102b11f920a9f25058836404ae1747fc8d1fb20c6837b7fb810791a0c08285d6f0a7e36dd001103a0a8146da6b44cb731706f10e08deac433100598d8edf42611df4cc7ce39a582129be8304f562187e8aafbeaf885f9680c80911c10414428f2bfdf99191225d6767088b905a4b596dd124289af61b099d6f08d47e7b9a89e2812dbc0f2a49680edd181e2bebb5e15fa87f01e9758ebee6350d7c53abeec62f03854c06d38c339558e92d9ed9b0df4d895bade5541947147c078a5d984a00b76a6c82fdd828a0631b4c271befaddea4ec77982da720e02d5e082767fbf8ff0ffc90572ee67f276758591416170972b6501bc83a009286d1cd000c6f7e3b509c26589d2782cf5e556c4096b68c61d72043246245e57277e240032143c68e626a892e073bef554aa840977d9e637098dbd3c149fc92db141d3e0ec4ae1c4300b47617e692f63799ccf08faa33cf444dc33889a068c8112f11220a489a634cdbc16ade92da3bdc970926336ff3c0589a495115eba06d023fe1c80edb81dfcee82bec113d7e478c54f89a25c4d87b33b0677a08ce1647da9fd5a70d85f5b6789e95e8a9c5c54d1ca3d56ddaa0cc56571e98dac6ab82455d1ffdae07c232de878e49181acc0b2cc33eb51d02d3540cae91135333d96b15e47a1ade0285267963070e9303e693d1f1cdb799c045a80ce380cde6a08a194e69a7db16055c92773704fb7fbf4929cb25623be299b398a6dabd6d028014541fc54613000422bf12e32d5fcd899aa4600b0c13e03aa95b26ba1f976b6b33a735af0a94e2013a53fbd3de17175445efa6ae467974a2a92ce559b80cf4e1566c06d4c204670aa41f160276f940fdcb42be1df0f5567e6d50f0cbb510f75655c770808825260d32376ac191dcdd0f9fc25abfe6ac8e5720f0ba12c13b473da46160be19e34f0b0d09e59545bed2e5b5c2523ce95c2e8ade3ee566256ad9a2dfe68e044c28d80721dcab0a0410570204bb849d12f5796e5bfe4b826e3c0bbb8210fb8dcfe1ad09a77969ca81e43525c5e9de8126cce2d34dd44dbcaf9807bd48464250ca18660161e0c2e111d0682be317670b272d2d698e6c4df1af8eb89daf6975a1710fa80a43bf38befdbb32382c28495af05e15623b6a9c404835da42f6d908d7b14a7b08a136dbdedb979f2b1b29cb9efa5aa58a7ccb839481c505ed76acbe83ca444f09cf15e2ee6139a4f56482f974ee9bdc1b72b315b31a8f8f40cc799902c93f220f30efa8cd4c550e6954e98cc93edf724a803e9b1b7c59c9f3dcd2476a5689590cb0a9e07dd0b4671d8ea129ed9f6a8ed7bc182f549b1dc87669da307c062088050dbc1feb24ef1ca535a8e0d16aabae5aaa6a92edc7151365f61b8139e3ce2e013cb588a239717d7fbef9d6aa0a46411dc3d26ea264d9e579509e45d6f7ac430e891401287e65713cd8939e85b4bb6eb292382d72e20333ef1e04feab76232b01e2b5e39c6d76cf8e678781e13cec166ee0405105174738b535d75f49b133f50aaf5ea2e08ac22474622cd7ab47d87d9ef026f3b4875f9206cdd0b4fb67e7b503ab99d29c2abd8a57d19c3b7225fa02eda9f53479188a5abc4ef7c47b17b0e20cc7b9e6ceafbf811c9b7cded83d6ec2ce6265e33362f54dc3516406c4cba8bb067b8ea2ba759ed56b60a3bd77d0cabd871a0d90f5dc4687e1a91a56ba5c8aa30f9455d249122b3e9b266b251ab7d3b3560bde4ac77729eef6fdb70ab6f0f3420d4c66941021d6b0f888459af5d8a85dbd623910f8d0ec9c8d8996e4197903140a18818b582c110b1e17a1ceb342e4a042344b5ba7e3562136ea695d671578020c8677fb7b87d8fd1750429ddca808393f73fe8d01df03215de4eb0f594f92a605212e90f5ba99ceff394d935c4343014f0d25d855f53510e3043ca5a9003bca0902b7459401fdb868ad4e20baeef6a6b6dde5550037b9446102ac54d88c3bbc0cdccd86de85e691d62a48402c6c6120f5a5cf64fce30149e569e01a68b42940053829fcb64a97a4f5b72930c8aa53c589b77a7f1a049e8ae41db16e50098e5006a3d97942750f7131ec74910bee4355e9532bac1a8f0bde476a70a2b7e6105f0656bcb519cebab51b2b34f3e48790f04c981c3424e626a9cee484acfedd42230935e1ab4d9f09860856fe1a3938f348008aa004a92a9ecace3ab54e2dae9b2702c1927effc3e78377518eefa74846adc0aae32652be80b36344d4e5911a0df2036df7dbed07e7cb94a78a9c1c32eee7b54c81a80b8c3b5026d55bbf0fb561480a9c63401e53b9ef30484799209db38ca62fa8e6056c2bfb03b3f6805a1161ef031de994b35ad106b178075e375cd701082883d6d27d5759fa388307562e8596059913b1c893e7f3e156b98aa3e7c5f5fd2a86465e069c36bf90db125b1198860ca3d4947fe14081d916ecb615a593ac25024aeb307eca1532536434fa1d139900e9050ed7af73b9890a34b9e7acd17cab1737085a9fde11f66b54154d62c4c709f71d041f19618482eb9baea9b500cf4d82c1bfdef1808e692d634c85c592670a8d78702b6a6fed879fbb6a6d95ec70dc4e1d3db9e47db31371b54ce682cbb90f8b364e970d343b3bb1c324a5af0a29c7ce439fd8b75bfc95c465623b5ad6c907a64734e690bb24d448353588fd9390a3af6f793aa4d9c1a2892534442cf8aa059d11f4bac838da49957a15a224626d4ed8d6a4a5d45ecb10e6ddeb9a969adf0e33be898686ad6043ae392e01a8b5bef2076064b91d998a8c20bba36e25bacb013b8dae6750a0f85bea108f5ad2ac20775cec6e45a6f5063dfc228295f2f72106efcd080aeec05dd8b026a1d9fcd673238444975d4a59331e991a85032c45e106b02dc8eddebfe9330263b407207da6a304c02a27b9e8d0f4c33f59160dc8b20b93a58e8e4d0b3b4fd661d4f97a0b10038c863a4f3377db58d575d1b0ed523a008619ccdc28e2c6ad1d78acc92278d57a8f67b2f17243c989d806ffe59d470e0154bef575da6bc9213fc861f00b0dbc60cc124632454f326ebb0edbf5b652770532867e557d4f446881ea43af76f1fb8e63c7ee2b25ade32752f2147015a34b05e9e8fa3faf0ea5fdd4643b19b8a4191645b2b36b254157e273c9f2d34112740e751449ad399729c427232f19bbad4b21080ee37f7812bd2bbb174aefa587aa049a397458ace132f31682b42b50678a800f038266f590468358708702607c8c0b9dc74668e4adceb9365329eb3124df574a7301f380d1465f20129aff946dc40ff8f798a70db172f131d99059aaf02f41665e4f254c9118fc038cd5b8092c6300a821b6e253d4eee95aa27c4253e930280c4bf9bbf75bd99837ee057acc859708a9d71940c311eac800ea335c5d310e2e8a19596ecfe59a7d3531cdece333d000462c667537566ce1ded1c0dd80c6a70cbebb9f02927fec35f316d703f72e8c0fd74b3d4a98e56edfca97de5297c078d66cb9668fca12fbf03423a81cdb3a300d7b61e9559f2e94c329160fd948a5b7204b45355aa6569a3e233fa11a82d0dc0b559b57d4855cdadd13898e09753bb96f57404868dccaa0633f4b3e301c7a2b04147407cad47b749acddbc47cf5d3f307baf60b61212ddefe25e0edcff45996035726b23470e2034dfd16d66d94afad189c770462be9c376310e5eb0225248b0c24ff29dcec252b9843c1cf114a648a9231aa00d5aef14a030ce316d49bf03c05387d203e7e5d9494624da459fee468acb9b7934ae0085370637fe52b7ab9460b14cd0d520dafd6bc2deac6b6f1c13cf413ab5db88f89c2a8b4ea9e1a93aab70affc11fdee8abc3089ee6e1884ceef07750e84e8db2248cf102310de1eb1fb90529ef9d91a7d49a0b6f0f302f37ce9c90ba0b3262616eeeb07ee2e139363d560346f3cefb3fef6c230d941e02b0bd2946ef33739d306eb474698204084a9df50d02b433b8718bc02f6fa829cc5611bbd44edc6e8d6116b95cfe94ce40f570230115e15605c3ae43960c8855a435b50bab454eb029141dae3f0eda3948c0a17d064a12b1f40476ac9010c208006f2a8d795d4a595575fef8a7257163312956bd0a7adfc45a48f4146771713cf7c02385bb4d46398a269b6818be8e391e2b4bd20134cb5f46d5e77a9405a6cd8d9f9b5c0a118ea14284e54e45f4874755de3b62045e2eb67c18934e466ebb7e9fc3fd7eae3e12777e4d7cc88f0daf5fa4bed48b0a80d6fde30f428409f0ac45f6da42eea8a92704cf023f81180442d199d7112c0c1631d4ea6dff7a25fc2bb7e20fcb489655987c6a11f17843e971057f40f7c106fb406ebc6cbaa4b0c89ec9aadd5464d75b61b7b9747fbba7783cab8c352dbc04c11cebcc22b9cd0b8c78b48fdc1acbbcfde6fa95844d20c376c9951936b7b1057c4414feb1b11c8e6c202a73bd927fda047873872a0c8da681d88c71021101026cf7e33c1ff022ec9b564780327eea91519e662a3340c5ad3162a372ab528a04c8638338996fc1a3362c7f05c4cf1a6f74a5fa61a4f2a3dc0a3306160387cb0851cf3a288fb0f719da6b0bd39d73bd6f5aea2988108d7f47067c9712ea19000eccc82274c00a0b42f99b9b1d111a5f8561b217ba8dbff6abcc1f1cd33015c607c32c5abf3f9d1c3ebb6f31dd68ba054fd550511e909124a2207c601168fd2809db97499c12bd13a71460b334ea392d563d1d80226a003e6e945ba6cce2a5370da0026e4e14a612b7285fa13b8a5852022a9faebf32f41acfecd049e19d0e7709671bcb9b962392b13c2e3ec8a2828cb0dbb1abe6b4ccf15c1aa97dd85fe86600626e03b74ba14ddf3045c3d94535784e48f7e20711b6b3cb5af8616bfb65f202f1bb7fc50dd4f26a16444647726e888fc7171405e08b2fdcb54878ddaaf1d800408064d928a3750185c1fedfd6b10a1975bf367cfbebe3029328c3201e3c6001ac793dfebbd53e9633d213f3f5ba00ec363bde58c60579047ce155233c147503648a46c96c253b0c250830b48374b9116fdb2375d6eec013f6649f3c22f33a0ad452c0a066fb8129ebe3e2299907fea24be3aba9f9d6bc743c6a08d92aa53800b54774478c747793eb01de09e64d97cce9f5a4614e2e27c7be4a57e478f1010f0b22061f85a6e65424d788bd560f0545de3a3fcb531ed19973fb401b8411d0089a6d5ede41fce337bc5ff0a7a720c47d09e0c814ebf7ee7fc4c265ea19222e0df3ca1956c32df9642a8d6427e64b32049b78bdc7cbeeb28d4b98766a7817f10b7a4c32e548d8a412dbd7f5a7b9e4f53cd645dbdaa5671652075211672a6ecb0d12ef177b2bf9013d34de12d92b8e1bda88905b83c8a45bebfb4d3e257135530349153fcdf84bbda1cbbdd403a0bdd9b3a33bec2ee9218c929cf691a89ecf5c0837ab939d7fcd31166cf83c90b2a677deb9c95e32814f74862fed3f0da8d77c0a0073ecce0431b0bb1158474ebb47f9fd237b3c45c1c63427f65cd3d6aee0f6075965df8f4ff7d2261b767e848a92bff7a0b5477a8cafd81bf91f941b9790560c6762a4db9a3805662864cd9c434780f239532f963dcf037e8c7fc5dd90a8c40872a9d160cc02d3aa8cc5ee51e55cc7251bb7c16d26efcbde48250ac7a6025708ebae57af842db5e3f30490bcf3054b5bf29765275d36ebfa6ec14e6692ca7b08e79658911350f679a4ff16653c3a7653a6f6b996d75a31d39c9e3d8fee15ac05881b0d067849c1381ae8a90024690ffcf8d8d36159c89caa2075b28e6cec550f75f0d56f322fc053f167b7d5de29867ae55b39f500e3127dd5eabc414b788800d7a4869f8548a77c68403609874d7ea599f16a04c9af7d1bce9e9884a27c8e08d45600d414723507fab9735e06633d50352d102e1821ff4b981eb762048b710ede49b964af7acbcf4b6a64ed4d6732d706c773d49ba6d15d15dbbea0a1e1f107e4ad91686cfde00e1406a5727d392e39b0528a3d9cc8afe25c55019b648f1504675f5c655f3b0d5bd7fc0de79daae393941f7e3d6621936b38d75802665153095b9e2dcd8c5e495311516b6e9a29c68af47687f1e2fcfb0e7dcc3118c0ffc20b4f0030e725b3c797ea9ff31dc2312bdc841bfddae3a358081df509e35e32b7038a41c1d2a015a7479de99e7575eb1ac25d09495f3b2962679d897c6faa113d01f8bc4c637577d93105e09d07843577d77400b049caf01ab348b70df2534aa50032fe7fdc815ececc6055c8d665b89acaed8186f8c728000a6fc4a068e9d2d2018bc2edf6bcec75f602c7b1867dc4a44d86843da1e88ee5ac82fe8e646bd5fe03cb14e2075ae8c1ff0667aa25ed66d586538c9a2cc0c05e4a5cada5949fac0405f9770e5bd48b9df949c2dcb334fca76358b899b27aad42902d16368b2775f405ccd6a893b9219967feb9e6f78b65cdbeba2ef33eeb1850fb1e8c874331b5060394d7bd816e57bfe6d425c78009ad38d5959eba0907f4f623414f48a64ed29a0b3150dab4744e50b68deeddadbcf0d5d6a3f19357b71713795b9db801f9063409a5b9bd2889051ac8082c8406c1f050a3f7a876c32dfb3d8edee7496eeb470c038c544b91224a3e2278092d54eba976eecd43668a24424e6309c9d5766910f80a3a714c000349003201a98290cb27478a1cc3b0317ac491a77608e0d7d3aa9f0b3cd418e7723de85fbbf3047595abb95edda645fc9f8d5f524756a25ae92f65054b0c77e21df51b08ea79521bcb9125ca1d4e509e04a0bdf44ac57c9438b834069d6b058e49fd85809a264f917fa25bf18c42bd6e95f7b33f0e83667178d7c0035a31b535e410a4a8fc1c46ef3b9a8ef06cc8e0e4aeb18aad6fe199e9dea04c0b482d778577827c300e7f03ce6a94ea91e6b41f3170a6925d5b67912a483e4b0797b7224679b8eb9046a594b0b19eff6fb0ecc68ee65d4f1ddbeb99fc0fe9430bb8ad16cf8b5044a0c27f41dd400b4a04acea2a2ffcadc5d99fbdae288ae3470c75c8da1a399872cbd1cdc12d464f7665d99fbd1be64c35d16d029ae0a15ea0007966aef16ed991c5d4a42b3b18932d43fc3ccb35f6a1ba3ef1d41c0603ac350cd33241cbb9ee8d02517b23fcf0cddab42676e7a321107287133afaab9a5d820cc6da5fe00f2eaa6888df02005d1534855c27d061b098b233fb46ea3e08e9530df53acc8397c577edfa2d270052fed476c5d822a112b6c18c2da94b11fb74e40b0fe0f9e2cf2209606d85299d00961e68688ae637d73a5323774282abef3bae021655638e7e794ce2791257b7621426e23eda15900a51cf4a2b2ff06d1b03ab016887196f2924918fc7ac7d85d355ccdee64ccd37efc65f61fe08b95afa6ea707cc12cf4b2007a589d0a9cb0098956f6e773c534e5cdf10fc0bf595e315e0a10023cbed13710de59ab19d0709682e0a28b204ecd3f0e993491b42ace027dca90b03a61c06b3683280e8e3a6a59d45aaf3743fa0fc480ce505a0c1f8f750a51a0bb4fe3845c35c2c79e158125123f4eb6d062763aea26b5da6797b29147d82c90cc7a8384a2beb58f3909b191ca5cda1f90f23b9452a6c59ca88f28b9512e6db0833ad81ba4c035d1944373aa7e845b94ce5950f688060422422812a8584c15b02f6469a4314f8c65fb35748832a4607ff723cec81cf00483bbf6255d706dc8d0b1c4d2e41b5cc7b5f11dc642ba4b1b089726d61b5e2fd2d20fe6e19b9aeed7d03f0d5c389041985e7a55f5279d0db38950831517df9d963d1423b84b7ef2edc0865c008f1ea007294666a132ba012ccbec1bacb30aaf7167d9894cdde49541d06bd257308c8ee2ecf9b40f66892cca437886328e33ea0501d2ae4bd9fa8255a0ea68b13d1548020eb4a96a265fe2b0ada77f2c45c8e31f4d8d02c73e168ba510f8e9a7664f9b51a53a3f4719c702fb114118a77a886a18755cf650b74f510780dc7106e4c7b7796b6965b1b6b070f7570fe673fe5dd550c6054d42326154aa9051a6d458e3fd0bf430de5661e8759aee5f40398d3b2d04fbd767d36ef544e950979562bfab1663605125b24cbc31847b5f1ca6ce2dc1b841d170bb331da3e41018d341439f63deea85e184f2074dce5c7ac98ea6429a6088ea88a423cfb731405955b752f27f57a68beaf0e49f7845cf5be6802b2ad8d736652f8dd5240a8950223f25bb109f8cc9f0571ddd95afd80b693c08ea4967583430fbf49409df5ab00b6092f8d11774439830a1592c34fc1219bd3f146af29ed45838f4e9ce60b7508fdf12ea11f8759ae7d496e9294189ea5454407834a14765be3b879d5cfab880e51b97a28f8c03ac304e1c047b6cceb4b043f39204d85c16b62d9de496806ff0f3e64ca2a09768da9c8d87c1a48f6734929de357ac5443882b45f9e8a0c6b2805e8583bffbde9e414ec5c8828942f8a250348abc75b7486c0799f6f7cae46e10cedb4db291dc472948c5fc4dda302071395bfaeb0d9b835a8c93057b518ae240be119af0d5b12b0fb832e155a04292c3edd4cda39c3378d6a6de9d1f18506ba00951a0f872c11f979255978ca843314548a40c265df2da63a110b2b9453886807ee1ce3b5587cefd18b73ae783002cd32ce04f6190b61ce0a246fc45b3c875602faceb3e0eb3606be31501400d26346a6c927c81161aa43ec147343c607602e01258f3f709de654cfeba37d15464368959020480aa4801d5d4a954c7916ce890060393b1549c5dcbb22506a2f1543138d18358f56f89aa45c083c4ad2594c4b054825506b4a7600ace9b95e53fc6062b2ca26272f8186dec23d33b2ad49144a0763a155a15cb6b30b5db2baeb241ff372a3680eed3e18a74bd7037e588292450f96b1a71a88973a763f069a4e4b5862517fe63ce78a08c9a845478dd6a5f5060ac7581de41966fe04ac299fd6e165338cc84e99ef5eb2d3a922aa4a15fe5a0d0c8c2f6a9c9c66e8b56eabf5e59273751a4dcd3994c1f6a9d8c2b4cfa00fd70002b8c631a4b1067e3aa48a3be7386652d6c93b0b9c853e74622d0a85f4aeefbc013d43c6d2c986c6346c2d0630a3ecc57c3e6605bd405dd20971ea15bae56e3c00ea3660f570d1752cc93cdd6c363233eab0a08b39d866ac6915160e955dfe570d8c6c1570bc35f55e28f62f65be59697b21fa3ebee111d7ef89230a4e71de7b0d67faadbdca926d27592818e775f6b22a5e70a8dcf8773f3fb6b07b77c468a3056e603b933cc04dbb66ff167c73ba11c4e325796ca86b6963e46ee5f44d2af40c72ab07623694e5c89ba23f1b35c3c4ac3c75548e1937f0f8849cf387fef0b409082cebd4edd726121be4a0c143e6c15a67f5e12ed65a807074f89aef087df00972692dc609a25c25ec1dff2205d1ea29ffcc023c7f48d26c0fea90c343a69f09f60e62f272d0f250b7b54f437fc958a1a1dc5a17b4fda42389a8de1aaa8e3f093713d8d876169a96de91d5bcce65245dbab31bb28d1dafdd3e23aa0288a55608e03791dbded1afd0a6aa4da0c2325f0a9aa2c2596ac27ea93119be3e7969140c03613754b08717fd3396815370e51f7391c5c7d080ebb910c12b6042a54f5909879d5ec494e4bc4a5b05398036b4b5f18502b27a14f4284b4cb61d380e2ae5075171d9394087c2eb4dc8b661a9307213acc391d81d624c154bd318fc910c6f0fb3307a50ff853123da75c9b4a1b4dec32cbd0de838fa160a9710ae3410cdb909e22182738123b078cfdfd1e1bd8eaf644658aee061ab568dfb5976449d87e10dff356bf6603976870a325e19781ef9f50730bfb51f2a75b7e9f4fc6d32560602cdb294f9b3b75f59aefbcf291050c11984c3d771ebde6a2e43b963408dbf720b4b0e91b623f0e5313babc2afbe23bdf8859d10adc00cf9f603d09998fcfa61080dbb860e66ac12b00e5310d28dae46b0ce7ac9ccda376c846870038b0f6f6201d7247ca3a707f2454ce652d7cb063a2c7e90acefa22ba4ace0c65acb89942f0d50f27e1e816eefff571c553d884bd1253e4bdd7f2e37c3d13bef47498264f0040408146405617b176719968e1170d301f28506b1978a1504b46d456a2407cd0dd6bdc96730b8ec017a7d4f6f82d55843d53821064a2819d29d2e92390f56120f987d2469caeb61bbebba56ca01efc2f6793e5da5fecebf3efaa2ea11575e2a0dc6bcd5c56a89c7e5d27776c03ecd85b941d93b0a7305b075f245a0080e70950c38c2cc6c45880dc5248fc782337bc42050d574d1a06a7a7fccce5116067da00feb64ddb8b2feee90a8e86837f074f3002c9ce46961fb8f612ee9cbc1d174300f6e3ac8469a53dd1fa5415796b77fab1560322eb4ae4e3a8106d2a850a8d33606937b62904d2b9062dd6eb0c33849de483afdf44231d33fd3ccf99d5980c1320704a5f76c98af11cf0e511969ab427a46862ae0f11f81161b3e2f907c3d617807efbc8e302fabbbaa4a0ea144b9d1a6fd82fab540d50273031bafd0a91424e008725ce7614bebea99bca36df951f2b0aac4bba241a455a4ab67ab1ac34c43df0ff6f959fb16e85e96aa007c1cae47ada270e4e2f75c0ce67ade4711b0fb8e0801ef88aa18f3e93e2db9bb83f37ab0bcc501a4198bf621e0552d322fdde50be3044b738bf708f222ccd15ebcfa958a7c37e0c42ac868ebb63f02f17f73ac0eca0e3f53d58ebc2f9a53b525648ee5b530e85822c46f45d42c8691ce74e28ed742011c2b388184297c6c8c6541252485898393da4e72021aa8c5f1d200572fe1f401e42ac54558906c736f171d29ae2e8daf214d99aaebbfb07c2cc989fa6743100bcb5694ce121d8ec922db8bf98ed386854239e9c9c1d47bf3f57361363701b80bc1e2ef1b6ac58376bae767bde780898b42b12e92941ff9d15e30d28653af42086267f504bc795caaa946744f357061fadd0bcd6309562e26716560c7af2ab30816b8fb2c166aa2b5e62fa63c3b86966bc8c5668a681183621e5420b962589d062b448e3c1af43e20d2801a043417980a333734d0724bc34732f55be216373b0d20914859f22d99f7649ae5d4f74033d1918feb9b4f195789a3976803f1450e08c3439952413fdae92345a1245c598d29c098d1e73ca1432c775d966b5f82e50c300b503f9e85f354659034a2c2084f8bfd2df1467363d92de86507f5b03ed20637b8d97ed228fa49540c73319e577a3a4dbcb7308e5a6bce3de199ea088d160e467df57311278e27b0365fad5d715f975eb4a8d726cd28dbfed324a97e90ef0bb182ab8f03b6208a475f039ecb161218272c9c0842cfacd93a247c9a7c02fa0490a09fcc70e019be229aca0605af8d1ef594c3885b224dba3dd8813df2e07c0894ca783dbc2d65c8197910cf6ba8d51389dc6f5216eb96682eb935f7a26c5f08db181ebf7d3ed0a480f82be852f48278134cec2c2fa6b86e9b961908174c940c4647dce628d3e98854e1f469760f0789749d1cd880f922f368ff28617a79e10fda7e049d2ede21f6c2d65bfa67a1b959df7e8bdbb3020889f9a041704e230106c36d7a491ea04dc03603a16156eaa80ef870ddc6c56e55e25b781042c087b507d32a7ddcd293c361b502b03a219f36630f2d63633bab05efdaecddc9d11c690d97cae2eda7a09f76515645d297d0870a527348d40f2a0526c3f5144a86e0f00d419172cb9ecab6db9f443351c5e2fe58cd1a881e3297fe8f8add622f85b3680a00a5de45c2e80c8c20da3826ce6f26cc05d4bd43642d78fd2fdac2d88ff20c0603466a40858264b68fccc45825230d3fb92407fa31255b563e2aae5da95e250a395402f0a0b5cdae20ab9fb0182fca2068cb169469ecae7ddd8e9f35cb52e10e997858c84baf694dd0e64a3e85845764cf118b3e47d757d46098867ab3c3d905fbaf67754e7be88022cde8be52fc45fd2b7e57fdd95ed51a27edd6ec9a35f10cd274a37aaf93d67754fbf71d2ebef5c686ce03f50486a403ca9e0633863742041709122eca8dd74305fdeec2b42c7e1b811796eb55bb0419a0a973e6a58ecd0907b2895750bf9523fcfb11da42f486caf875c30f1438f5a178d7805245f77a027792dfd6bf2eef484ad287133b86804b0388e6bc12747eec04e75c4ec6c36a09eab182431371e47138ef925319a53f1c00f8005ce451b110c109da51ef4503060c3c64b7082a29a9dce8c76b836100a20ef38ad60b67bf1a899e55e5627fcd09d84c37bdb751339b3951235a1501eb2c16dd4c9b9b6ed55fe12e631e90a98d03517197d9e750a34a33f8a6b7fc98b06364fca1af3330a9162dff90543e70f500bd71d245452a27a750c371344b305f1c17f464c30a33b49de76d50cad41bd300f02c3c570cd00cdd196f82a8544651eb05c7b24291a01c39c7d27cdaeee73104fc1d59cb7944c5b2bfd853ff20d2bc5c1751f082c8673eb1712a67bf0b9846021fdbb9853ab503eecb91965e6fd7346ba4d0432476c457b5f96c780b6d451108aaf86f745d1b235759d9fc38408c3875d8fbc8742f8131ff700db8f2846f1d0a89d95fdc6a36eb97d27fe0c4041cd9042b0506ff8896c325f42c58f4a39ea007903b8454bce3f4e634cc39ae01f5ecf7d3ae0330e9e3c91ad6280e80601f1c0fae0527dd18a0888bf05fb6635054a68147d93de570106f96bb2610e1ccfecc0da6748c9ad8083dc79bf2201aece849503015f6c426b0c3b349acb2a469424d0f905731152f2a2d171917405e87ed37d48e823ff53d03e4ab48cb3e5289afc306d34db3792a13cea3fe62f50d59da6029266eb8b63aeaedf9ebde9572a5a4da04d84d28417aa4128f8485efc4cb77943bb99dd408b11e0cb5b0c13b5b7a537b0d2a690ca45ab1816054bb199ca020e5cec2f696f1ece29d540ccfec2c52db5301f6e654723d31dc9865331d759c3b5a8ad8b0ea3175c3f4db121fa8e30e4803044bd283e711f19a75b5239ff019a4bc8204ece6c3578fefd9dcc54014f2a65e04634f64999fcf57352d2356d96805c50da5ff3962a9460e3a932b7c3a0a783808b6821e020bd1ee37272c45fd8f4678ac1fbfb0c928dfe564b29fe2f8240c66082e6f6a4d60eb691a86017edde3e1b2d827da7c097d36c5c5debbb52002eec604306cfc09e9bd2ea65e5007d445778762137bededafd865248016044f13369b045e8e78aca11d79bb9179f593bba08505517fc8c0c413d995ca170ada7241ae0fcb056ec90c62805059555b5d61d9701df410c7399050e04b3e04d5e04c6b3303709c963901a4ec97bcc8332f3ee7f1bd57d63f7527c138e7b6a91c4cecd5f101d272b3fe1b8e7a67f2aa07e618e260386a6e391107c9df1bbd2989f8e4941804571c477e01e4701a1fa24a4eb30015d6702819d8614933bb9dd4700f6375f40e1b925839ee72af586aa73d6e32fbec1fe09d448a14411cd5dc292b9572151509821d36edc1f2534da4d3bcb9ed3adc1e9f706c1727474d3f214a07f01b22550db5e9e90b7dc50069ed2bb578f43d3d9421588361fd9f0516cba3787360cbea0eb8f968333db3c32a9e5f3cb3a76c8ca2fe4f29a0463712620de1dcae7c10880966996e8abcc12e74b213f4dcce7cfe464ad04b2d13c2741a22bc644fc730e20d5ee18d903f99ad6e5a3b63be8aa8577fee5217882c9e37288e00e51c61740d0e4c8a4a7987da408caaa41fffc403013cdfc1dc8c20886d10decd3453e521c60a07af3ec48551a4eb3f2e205ce62130fbf2c2a33d9c70fc191410abbde079c70fa78b4c393723bf0821d161c93552c3efd44ced20819ed504afb6f1e062f6020ccb9e3ef29f2d31deb64c18df2adb3c11fd3c85580db856c8c969efb5eae98c0f192bc444c795856e7c505aece56c907540d27eb974b051139dd977eb95abec05bedf85e1bd761618ef9540caa75308cf1abd02b72109383dc886f357ce86090b883dd7d2d63941ea51fe96269bbd5d5b513eae06f05dfd16c87443d85166800a41156691ae0624f17753d009f617f393788c45a61269f075873c6f8397298001c0446b0e65ac06b2f1638ebe323eae67c2cceae871a7d43513f40106c1cc600991b2b540f9ba0791a62f653345b84e60d9af3035b5547339e5de94faf1e1c7048656e3c02058d27227a8bae162d58a94a1b0826105c8ce4e7fa47a63373314041bff965ed9b4865377974b56296095b8dff9d370844c83828b18f3fc3db7490c92e529529e6d1dd8dce73b6dbeaf4f3dc1a0ed214041967b263397d0025d1b0065dce22184ed29d330daaf1e2c4aeb92f26d4cd2547e49e8ea046491ffd8290994ad32ab152b6bdf786a490a5a787d8a2b7914aa4872f0dc8431fe510dfb770a6c7b96153f0734dec6ba7b33d0a96e4f4b84292790682ae8be0bcb7a3975500c4fe6225ac229ffdbabfafda099d8d74863d2193a40fde9fb085031a5c2a0560f85cbc2bb6f2965447abc910d5b2ae7bd3302c824b8aa6c60e8278b87287a23080cd8ffb18796b4ed7232f5d61b06cacd65916e802dca4e629b58e642bea251071d4cbf2a64d4e17bd50ecaf689579cec3728ac6af1c9b8c1af0af27297eff30dfe1dd9cb3a649ff36c6f435b06c75969379ba03a44c8be04d1fa34f5f719b0044437b3b01533d0047171e60b40deea816521e87dd71713fdbea62656b5d28a07793089a9f2aa5d246ec6a67bcda429333f980dca81acf675cfbb802cc2965606bc0e9ac2634848137e0b6852761d845a65e1c080ae8c902cbb3d52f769ae9a0f246f62674e15f0a4f39536213d2b9a1332cfb79a9f279fee42972630200c3d03d12343c11c1ed4e9a99bce2cb4fdc7f5c43e453f968b35450238ee85009ecf0cce1fe8bfef4e21922da07be3c1bf11e59c74ce7ad2a327c29c34494b6a212b020e977a9bf5de685324b30d8d2b837e9dd76cbe000fe7f30c8ee0eff30f7bb00ab8b15e885e48c15778fc14508ac006714ee9f068b0e4ca94d9c67d1f7cbd570876c9892d293668d96413fe0e532f289d6212f53fb5479c54e28a7d930be29c0c30ae890d52a52bc42689c909c2de568e454ceda568d69e829a62ba831cec280e02c7f32a223d221a7504ceedf7baf8afd54d33e5aab241cc088a6f6ba068c20d4a689877654187594e1abef27249507093d9039d0bf93be0df0fb5e6a9825c07825824334318c894e5f76bbcba1ed965de24efef30ee2820fef0e875b6c78601916fc12f38e0e7b61ea0cd8e616a0bdb478781f94a7f19eb0d8ff14836eb1a0693e8dbb22f9e8a9d1ed5589d58459d37c8d4a144ba6afc5c4f6d381db86ac806e3d1e6974f9b22f564dae18a830f7fd3460ca6ef64c12e25d9a4b2c427a24c0903302905106e03ff340492459d9a110d60f1edaee07a761cb225a24243eacc050ac408cf9897d0cc98b0e1b91c2ee1f352d4b510035eed972847439144ad3a0967238e3362e4c1351658c2d03829d852d92e27011c657e7a8d6762be6f19af0157a037ddd48f20ffbd4bdb98bb35c3d82f903a9b4e428a1484fe191115f2890c7f0ce4a27dd1a31ed687d789399e1205c371315a2c9fc32a42a51099065b480435b7115dfc38115e861e6cd42b0a11e1a12965f598a6d332d5720743bd455105bc886ddb9ab24a2c2423804022f8697cfefe0c4d49bc9cc6f9c254cc657ad200015984098a667dcf167c9928a1317b568535683734fcfc26e0f497bcd1358000f22df5a306948d6dc8db7e460d8d3f4c58a6c7fa4c993b389167113a95da820cf648670dc3e8ac1db2706a22f13c5c8337e3b548f57b27fadb16a1252696870485c769cdd1dcff083b494972661b3f69ad3c093d40b9efaf40c0f420e02ac20e141ad97c2990473e015ded0c0325f364b023df9974521ddda33ac23adaf9c2044a0ca8a7c4f85e25726f239225925d8cfa19ff4b080639e171368bcd78e39d008a4e23d98b94af3d8d7d86c3dc5f2d2580a3c4d65d5b75d0f080a6f21c8a390f7c3649a4eaddb6097fed596aef580ecc5b746cb48ff059fa4b972d30dc6c18039fe01210ed4449f8b2799c7a84484f6c3ab72aaa331fb77ef6d20aebd19a6f051c19317c09f28e311b3480f5af428beeb9a5f8274fe4829166c36d1b30586008a13711cb0879a5730a750ac2357ad688625fc0ac6eb25e5388bc9e206215a5067eef2c8822160eaf6ea86ee04b44936fdbcc51a15a264e98f21578695a8ccc025bca762c35c2bba953f73efcd4b59306e004b64f570f74043d8889a6b950aa04aa7acbb7020c56def0fe9991767d6010247391023ddf9d19b3d9149412147c00d9c3defadff5936e0289b5615fb0f2e5575d11875b00395e911a2325d3a8b50150700099f6f6fce6655bbeece1988be8786a3157b48b488f8a7d213ec3267d09dc087e8f02393db3977eaaf0a8cb41417832d485a46cf1418dd99eba4f2e7f06a32c2da22fd18c58d7efde55476e207f53ab708e7ba0818902b145395fc5e90d1292fa7fdd0c43864590deffa413e1fcca4f420853d98ea83ba6a15d40d34d0a1ce303b2ad97692b34bf025b850c4bf7695c95f3917e39183318033816d1ec0faa8e6d1ee814bba5c2f106ac18d7a5ac720f6bbc57745ea502fd6ddcbe901904c6f4d492d55422b1d939aa37f4481f178c4251622e60382d34fbc42b28892701a5fa2b3446a8cc9268e22ffa08ffe8b28bed706cbbe8d18848ed733374c3e00ae00a730ad101ba3e26ff62006157d2389fb51bad93e6ca8df9ad860f8cd35b04875a7942e67f030a94003b2b1f89a6a186d89dad03cbb69ad9c4a213c9e942032786d1cc3e27d4587da9e989818f2265be650c2c9c0493d75bd843650cd05604e32d87404781df143b71545fa4785a3a3870f4e3b26947d7acb852564885d50aa9564465153d9d9eba5a9ffc7fd777528d2c83be6fc9d4353b5dce25ad83080f3ab228b14401f8e14de34ea7ea22c67a22a66874950e966f593cc96996678009a6371cd3ca47dc5c8dc9a293bb94864e2190ea72cac53aefe69c49b30923f20e957eb8a89c25039bcff8e3c88b3bc1506f4b463f5e2618d02afc814aa679a60a77958a78bb1b8749898d1e6ada33a359ade49990094f80f117145e0ad7e05a028b2005e9bdb3b4af2046b9c55c5e9d50d1ce418152b83aecade4453c6555fb0dfdc9095edd62d6f5cd6798bef9a0daaaa7bc5d482b101eac0d7432bdd501bf098909a5f0d08ce4213b4cdd6e94b984a8d99d9920d07c624343aa5eb5565acb0a +generate_ring_signature 25dd893cae76d6ac1932f11696af4d23ced5c7f8c9b26c443513e7460ec16ecd 5d9938750d6d01d3cae39ec888e440f33fec671feccd0a5bcb83a0bdfda37022 6 c29716ea74a2eee8270fcfaab52d6eb58c02084e6c0ee1efece22d8ff5ac9ea1 85179c988425643a848188ae78fbb0c39eb912d731fd789ba04016020e904dd7 5dbf0ab5804b5052e98871ef45eb1d79fc94177d6cfde3574f118c0418a84aca 3053056e01b44a45d9d4137e3fc8cf10b86865dfce0f9773fdbb459b0b305c41 e35ae435c1147ab31281c1a79c2d3a31678be7e87050d041c61aab1dca9b74e4 e451cf5197c665cf0ceb5a9d7b93d139051f56e11bc31ef258f56c9023b94d80 c53355e6c89359655240817ebebc88e2a7c039a53e718bd076dadbe8b166fa0b 4 8a6e1992229d45ed28e01d3a174997ab83b036678475f20664f26b3e74ec1e0165301cd5959fb5e47a65f77c7e07e1070be6620fda4fd62c631a75761c24200ae87d177250d67405733e9d3985d51319b3c5c388bbea41d399caa208b7d2b20b62c9cc9681f7a6fc957f07727b873ab8902ae07be2f8a8434c4fb6e3a58d6006d3949f9ebb6b787a3b7364327bf9ef36975cb13e54ef01468f9d6026b25c3508f634d81840a616cb80d9dc4760b0f1bfd679e355941c18652f1db8412d0d4a0192a10622979b03330152746d9e1e1f0e2947433decf380d7893b99263de67309e33ddcec71f5d3b60cd371feafed4bf4c4977436946cb40ae8de1c415ead3d02610c17cdd859f834f431d714eca3b2527e3cee8c5585e7bb9a445a393d418d025cc0603e9e4e181fb70e3f51b551acf84efb0d84dd86ef34f09d5734d0b4ed073ab0b118d8e91c5251c48ce461ea48845e059e37ec3d74da5df0742eabb4cb07eede7482fea99b8f7115ba538e132462f67baabb32c045c6df60acc33f144a04 +generate_ring_signature 2f1a0d247c8827dba4d4ce33f142f157e2f1767529fc7c19c789f00cf6ffd688 3374f89c3f99dad856ba5ce07e7c38dfeec5b312427bb33c8e1b8c372c6f61e8 15 41a94a5d8abcebc4c87b58bd560596b715bd33edb75aa248955b5e4602960ffe d30add0397722f8cd6238bbc36ef02a1351b0bfd5a462151a57cb156e477a909 8ab0a8764b7ce7c3c10a7e6d201732c6ff4fa5ccc9e2c6e25256db8f88c19467 c12136781dc3aa8a1dfe8fa588c768f3f5b73acda82626cb380ebc0e42a3edce 571d28f9270ca75dabb42b076a8f7febfdecdc966b0c106c4541cb6820380f2c c9e639cb8b24ec739970c8f7d87bea123690fd4f1edd394cac1fe570109db396 84321e4fe650871a21b17061c6581701f5acddb389d49e4cfb8a65bf37fadf7c cbbc7e4105af837cc47494fc5b5e78735a33190f1dce55e2657d2d538ae6fe95 d8d75ba6364a1ef06d0dcda3ab45bb2c245d7abd7dca3574a5f48c3f5c10f3cd 74effe798cf28847c01bf6276a0c24f5be7e6c52a3968b7df9b8545a692bd9d8 72740510584e7b51328029f4bc32a40c715d892cb00f70fe60f7912cd7b7ccfb 518813c1abe65ebdf8bfa96b139e171b30da00b3619fc258770bb316410d8488 bfe01ebd9d90edab131dffccbdb58cacd599d5f0a3fab24690c7e932f2f06835 9d8fa00ff4874b2f6cd130a224a2d2df214f27977224132d1d84a5ea13a85493 d780603fca86efa77fe4d7a9d35617b56e6b1f1cbae974bc5ca9ab4b37f94388 783590cb03f14936ac520b8b90b9a59ee656aa1a35a60774c23c0b120e0ad40a 12 f1774122b913ecafe6ecb941f7645a0f9c8c2490cf5e0243b45424074bd44c0870e2406a8693fe26a3f4239d4cb96a03eecd070184f419df7a325f1920efd50848e21b0a9eb6423046538d2af0069be8383a4a7868bb88fe41ad1001d81a0a07aba556c51ce361d4ce9ef2805486f12083d3bed46a2a1697c8d079b93294ce0aa32a9ddd9fed90b13993b3467eb32a355b302ce65ddb27a2c7fc1a43828d0c0995ba9f35c26baa2a1382ddef196c88b0f5da61347fa76f726dc0bd41bfb31108dfb4b0cef03dd95605d0db16ef2659a9a75f875e82ecd12ebd37beb400fa1201c9b40660f31603dd2452965816ff34d5a688ad3345e5e599c2d5f8d2638ce10b4615d4c53f9c722c878448847825af375e6dffbb41dd6b034c784ef6303a340ad741d62841d9743a7c7ca5a9369a99e8d2af76598057ce0b86f52c200d4b6e07fc352aef26ff5df78b20b5c4d8c88c328f9a837e24438d778c2f9aca54c52b0bd02a99e4db1b1c69d052f8e866b58387b06656522d26c66450bc9616ada61e03e33ebf0ae51bba5351bab2a0216927470e07044330500597ca62254a3164490abbd69eac97dbc210646b8c9427f356a370662164523464b4df8377b069fccd0526542a947a61059c6e06aeeae3f67e86974d115e010c36033bce5077fbae5509d760976cf825131ac8977393498c26bf1f6d1494f967d3a200f1e7b24c774e0301742a0521b549345c70106df78eec3542d301a4d8b85aba0d799ea3487cc70ab2630e249286faa9a370e2e22dc06e9f591421e0b9ed9b8d2f31f1e39f1e05058487aa6ae732ca7396505d0e413840b37dd5662882f569539cbf589c2c22b008ed6ae4711606c183dfb8c2da4554eecad31e79dcd0f159443d476ff68283e605ef15df0e1cfbdb7ce7e7e448620192f9723c3e935cf618e432c843c0112aa0003518e7c7fe49c5e6f23666a3ad14db368305a7438b8f19fe8f76c19c993aa20e8adda3139e2884e426479f48fccaf4b30798223157a707db2185c402bafe960de39b3e8641f0958bf7522e2f7c5880b13bdfd957208c58937210c9821ecdbf073059dee2431dd4fac81fe982861eaeef874b6a00466c889745aa80710e35710331a40885561985e139126fd398a8a3355161e9597d04a7ce2ecf5220de128f0bc04821d5546ac9019350ef9ccda1194d0b2c404e0a5110ca58db45367ce42a0a6783f1bc7248d916872b3c8cbbc7cafdbee470ca3902341ac3f3ef715b40f206a264e816aa64cd0d2ae620f4453c6d2cc9bfa7f8dcfcbcf85a519016801612071ec0cf46f0f96168c3cf05affa63fe0c35e7b8334ae9bc0a8fb979a3f8bd6603 +generate_ring_signature da547499dc76464e031e5ff7ee251b23e86a65e7ef4a702f49430dbc1a3224cb 33d203426218d7f5ba88cfe70ee0255b22f15cd23d1e7fe94e02fb4d7a835de4 1 86b01d9ccfb40a5020a4d522c02855924147e974f4909a1f15afed38ffdea8c5 55adae1b399bf6a36b775e369894601a26a5395e5092e93f1717e75dbf74f60c 0 419dfbcbbf863744a4ee96a357a627b688cb2a1ec3ab1225b311a9e68b764e05df980fc93795abad8bb49003a90e6a13edc1063a712bb8669e5ee3baa819e40f +generate_ring_signature e7e69c7adb279f38f2e2f05051fd992a11149a8998d1369be08f3dc277fae761 85bbeb775e94cc97da84eb667dbc70a26e60a5276454ef41a97e0aabfb8c2e2f 56 18d09a4f9592883fcd32ff6593cec77b9f973837f03d16996ca2cc33651992c6 974af2b4ef03f64b3051a9a18ee09ebe5ee860ade199631490bbdf6d8de269af 0f9b58c9eb9bb3211deb53ef5b997c8aa68ebb9357ae4b827e6042248e07525d 582bcbfbd1c46085fc27008c081b8c3190d8295ba3ee29a28a811c8dc7ddf87b 46e691e35acd711297c564b251cb5b279d11936e67afe44f321a83e7b2362157 254a0add41690908429a4349aa13813cb4b18b08659017515816181971d80324 97f30a5f6836526a6cff058d570b292595e8b056f740b325334712e9c197b8e0 5848a77ebae6530b5cbb1d1f479109604dd9491182c4f4537f3d857b160f63fe fecb6d36c025378dd87880402a8ea775376954bad2bdd78ba5614c793c0f9c8c 172a5304befb1ceefbcc22bf34479c924f29afd264a3ac3b426cbef3df1e1903 7bdbe1fa6447623cc54d957d4870e2be682aabc434db57e31db09b1472774c85 394ef3d1104938454158def95ef99ce4614a373235912fad5dc6bea68e9d8b41 23e1141711a551670210262fa8a59271dce5a05939d906a5d3cd7ec5277c1da5 969dde51d1fac502a370b8bf4f40d13824fc919670d4671b402a294b6a89bb08 0509b587aa1566227473c0867249948630965f4f94a6b024dcc64faab4cd0e66 7e0462bb2cb462c7af329b6bcc6b9664a2643cd6e360cfd14bf7eb53544c999e 738ec5605320e83f6fd439853de054cf99d9b320916ca3c26c53b914e6aa490d c1613e126399c42bae1c4750d814dd533c406db959abdd275b3d9c3e72984c24 d588bbe6c2c2744068758a5d6e259d1cf02473af17a1368e62932688eb7764c1 26d840d38fa305151e8873b14a441f42c864d873ffd7aa2c7c278109a446bdb8 66ff84c63ae19639ccacc7b493892f8f26ceeb3dd6d8f063859449da9d5b13ff 828787793865161872024f6ba77dba847f0873be2562afd85a64bfa0f453adb7 1874b21b0a18b6b43318736c3b53d871bc491695f5d86c8811ad8b2aa8464648 8f1df6bc7f2a2ca7a7d00f475fa487409a0f4155d2bcb2db1e9b007cd9368b83 40b39d643696c1517209c946e7e797b4c1cee0b339464c9e8b13ae28d9434afe b3621ef2364d2a8a9f38aabb9b624b932300046569e9fb026d36c7e73a3e8fc4 3115f7a4b63d229c5182da0a3f624de6c7e2b50e13a7da14a322f0de69595418 7146996c40e7e9570671539d471235c7baef7387ba12f1fc06602fc973a3ffa8 ddae2cda3fa2d25bb4a412f4aedcdd206274bcdbae18a3d6b4c25d978f8f5dfa 124c68d03ba3fd815102f4ec1e16016e6335ed47d9637771c426c49dfd87b1b7 19a1d656e101999c3b1457e995d8a0977c18bed779deb21ef832e7795d4d9044 b69f73ce720530c3fa94f7f6f0b1c6c8e5466a89b97be80739187695970f6871 ffebe2f897378da204d5e8338caf629744fe910569166768481b76d0934e5e0d cfcdf78c7fe82f7fd14a6766d4cf608d6eb92af30bf11ce79348bb2422c1ae8e 7af0a5c4137717bee0db0faf10813515b700be3b04d639490226b047a32b660c 8e824733a807d4fdb63c7621b6838f70054b9a069c3e8b8e36c7b7a2fde41de4 b51e86c2ca33303c307c9d1080f5066ffd47b67dd1a1a368d4bfe5def1fdcad4 f271285616306d959f3ee158276b7c35364191836ceabae3d5f532d1c2f6cb0b 0634672a39f8f196a86e8afd3028b1ee852026e53b57ab5dbfb36024bb3801bb 84b77938a69db65b4f705507f9a052aea980fb5827062570470bf1674772cbf4 b3f1ae595027df180e6d1ec987e7d4faaa5832d72c759f6c4b33fbec5c197248 b3aa197ab9bc1411a0088271908462c745ad08ba0b6bc52b82300760d1edf3e2 764478333c006629e5be2ede6050b6a9e19f84bfcc9af0cb606bca2a7fd6ff57 42ede624cf6b0648b2219d84e2800f86295b45b2d62c400911936b00d23e8eec c6d188b2593f276c9d8af38786e8dc83fc3f4888357576e0ce1f817c6f4ed0e7 e54f8c15e52301951fae217b400db2f8a42aa824f182bd97e979d38cc6beac71 815eb621be2111a52233f936689d47fa18695b52431435cf969ea1521abfca97 c39db1d00d3dfb57a9e61d8921569686b0cbe20e69915cd65c9530c8bc7840c2 88841ef6d698114e8a73b56a437acaf9dff3fae7edbf85394ceea8d0a0abac7d a10006d29887038ae2678d8ccf83cc66f6e30cea14f3776dbf3b1064b572680f 867185b15807549d9248eec40382c0c2efdddd0b6c18a884ab811bf0ba074dcb e2408a5abedc5ca9c397b10df9eb1d4f327fd875932544769b9b6ba124131669 390f5c7be19c6f50c0645efd3d416b1c740d38d7f95fc6983f5297921026518c 5368d661c76b57e15f8e4cf676aa0c8179bad2cf815856a97ad48db1deca7db0 87494de9d4062f589bd263b6fc826dd85c6b81b66583d6abb6fb6cd5954dd296 a353830ac2b086e36537b57320da789652df3df9b6bbe6f963e9cd6941758aef a0d9ff166385807b7eb58ccbd283e08e6b2194e0dc82ef190b3dcb15bcb3160d 15 6672ccc3ea040017f47f053e6ff4eaa141e249c6b3823c109393d01ff695d0018898bc980e07fa21c4d13d1f7423496253378234d207b8df126453ecda45510ee8a9c72f3bf391ef02a1d5d22fcc2679ea15c556b2e65e353ae5ffde93c4860e3e707baf5dbcc7b7a7b90d62866ca9a5f32c57446bc6ddffa8a853dac13c9e075d6bed5611a6306a4aab3c6794eed8ebf211e626726571b4418f8d3a20841c04a7868e746afcfd209c9202493f43c9b4a1bd64ddef9ee4a32fb7a5a728d5d9058067060228680bcfc8ed2b61ddb0bb91cdd3b8f5cd983555784c70dd72fb7f0e2cc8930ab6e613a9ad39ad96a78fe487a591f17e44861e372e4aa4cfbabbb60acf6a57090e4d7920ae8b3eac3bd851b5e2eed9280711ee2dbfcdd505ff628105e215e95fa96f56479865cdf855b2c88dd519804fe1d17b6d50c6aeaaaebe050a9564a565b8c651e1fbc257519323d3ea81db8df0906f4d75d540a93a881a580cefe685b776710f5d0892fe810d05690e4602d96bfcee5f1630077ebd8d3c0b039bfa6b3e0bcbab5320b9a02f3469e4c2e85e1d8248ff47d0a45453ad0842450b1609e08313d868206aadcdd8bf1552177b1c06568a66017790f08b1beabe720fb76227a4cd005089f9dc5de3446799bf6f6b595098e864284fceeae5e736ec04341ee35f04371f9ac88d6745e3a39fac3be447a4528a95a7203e7503a8d80a042849fa8be0c0b70fb18f8605d6e266a67b13571c3fcd3cf97be922e97c62250afda7d81dbdad5426de95f5a504f825ca7efbc941513224ec52075ffb35e47002b413313c358797bc3be7d1f52a28d7f5afedc885dcf8eb61cd257433be9eec08197106371052b755febe36f540f35555af99e4942e300677f8071c733ba15607f5fe58d2a0769637f0c9aab23c3b972669de8b1b87c1462c896c6d6301d50f0cddc0329ac8738dde283ea9951e989c6a7535b1aad713dab64d8f5e9d20195108fcecfa9c89b1a201ffe5218878c8540203903ed4dca085c868b79b088b1192032141fab809fa08e920a0767cad8478a3cc96f259b81397e91158f1a7f05b0d0a4274c9bf90076906d37b95ad7aa150d71197ce5c4cd2b6a9af6046f1b4808e03a3847dec955badfbb23a4a97d18d5cebcb56af68af0ea428508bb0ed1093e207a81bae5776271bc27d88ad08139c0f2d560e0808c450ae986470c9137f80330f1857d6c3331facd095111c9fe3c44314b59b9653d231b7b74bf699b343fe3b04cfe233fd697c63b84eeb2df4dee760237e1d10cdc3fe488d8833c04b3bb6570ab92ef5298bc82bf8d01b24205a15e27d487ee1501c2805f9dd9fd2d86a04d10dfcce069cf6f5c98df81c76875d2543f0ca8401af359c185ed145b6030e8f850975e7726e2f6a2c45dad9a0b96b2e3aff465813e693275032edb1546484ecc8050159fd0ba00c339148ef6cd3cac552c3015b9e48c89ffa3e02837115b0dfbd0eb169b1c14bf56a53a2ca2a051bf9dafdaabe677c6105230bf0ad96041b67140754b2eacc0c9886b3ca369a0700bf4f3f2db789016baf7be8922129e579f3ae03077531acfb6254f9162f111301f060cab2699dba7a05ef95acf896a1af71fa069ab594a8bede80d3738e1c3aa48f682a1a4cc955672767a62f60b35e6172a501e31a066cfc40673580eb891e3a81983b2427ddfef901ae0049d7bff333e494092bfb22b821f4936460338f50402f68e475df8dbc9592fe44f1a3da49e683fe0800a678548b9a4de7d4dd785d9a78f30088a7a9a5cfdb8346b07bf3ac41ac040d7f28033ec7c8913ffda7f4291e5e949defbf9ec734883af192124cae8573130c6d3c87c28c62b7e6fe5b0b54808e4e8a16ee60a749abb43a4baa3f2693e7c60492d1f0cd10fb27e19af3252c7d4e82c38e26a345a57befe541b771477b839204a6b73ab27ae97af8014d1cbf7cae900f63f8613883e584402191f9a7baf84a07631be32641b6a7416a2f0011b7a51ad89f0bb0409fc5f94517084fb01b8d2c05fb5d210f2f240c2bbdf1d3ad98e518302dbeb981a724275fa084872d96de6c024e2b9c22e9db344bccfb5e3ad5825a1307738265471d98275130afb08ec6fa0cf43e224b17447304534f24af08ff0de68ab29d268bb47b81d1a543e7a1f37f022b417701a2d39fdd05446ce4761fd20e1fbf550ccdddf6d2cdfea409200dbd09e6b1058fb68bc9d2a73856256a80cb5583dded609adbad45bacc9f89be5678006341b862439fffd1b222278e884e6679e8ec99c22972173bd90576629db9eb03def1c4e41f0d8199094214bdde0b37e3a010295eaacae2c4649ddbaa6b7b4107c5f1169b96f135fe7d3dff750f8926ba02150e86bf34014ceb9a60c06fbf620fef05a11601f9c686a9eb4a4d13d87ddbfc17c21098932eb793b1e76017febd07627337536611c5402b7be621a79392d8ae62e3d842cc93cc621ad76d256f4101bdd6d5171a01d087467b4996e49db2ffba41b6912eb9500a05fa287e98440b06a622277c8008a7167d8fe8bf233f8898c3fe0bb082a27112b688caf956f7ab0afa7ab58a0ec46de5085a82907ed4948ae740421598ab2a48731d15a40cac9e0dfed20ffcc8198e9535b91c42aa78bbdb1ac95174ea4ef6efd00e7a2358c5ce0f1f9c9a9954eeedad3a6c0432e421de87493896d2936e72cddf9905ec2392a508d69e11a75e0dca5ca047215504de09373cfec95216a2c12498f02be10f500605bc1893cabf0034e4402487c30857fbaebec2a4b975fba5ba1a5774d519f65803b6b0e207092c6c6beab86a2b38c7d77280f0d4b2f9c1e4c29c7b25222f0d3c088f9c77980918b82c3c6cc72225b8ccba0f56af2a22213c1a1e098c4558c92a078be8d6426b66fc603ca58848192c8626e87b9ac6af978d87ec84ce603c0f9a04e6864df7cf775796650d164be2f3ef3e5874f841916f81b82cde610d5f06dc032060d0e6ccec47e89e52e94c32be9cb0d5dde9bed80ffe3e6814ea1628bd470b1424ace2d760d639dceacaf5d84787030e66d19033bc2585744aca9c3219f40da5ca77015f25078d93376dff7e79f99851845e08144d1bd75e67b86472d7db0b13a2d10d70e9e95eea44e1ef8801ad6b8a1fd5ea8e0d7d41acbca35fd5172d0426aca11f22227d93ed45177d729ec3f20cce42043a1e6268a8f4bd686179290ec06ee8214c0b85f5fe0353423e5f6a68f96395e57455fe900f736da8bedf7300d9f5266b90401909e533b6bb9a2dbcd9517f34c6d8bcf830fbc95698c2c0710a786cfc9a950d1ab2cb4885b3c948eba9fe36f1fe42091fb6f34abbc41c6ad5071a09761444050112cb6ccb9fcfef7c9dc7fbd4475e46a0a63005f166240d8c09c5b83cbd9e453fc71911f51ed37bb4896532cc3d53a6ac00c1bba7897330f005001d2117bc7a36c4f41a4b8c90f7cc5b32b425bfc1e4ce222df4eb71d73ed200465fd075f642b6fb1fbe431ed72d11007711038fa568281d82f90b4970f4900131f1bcfbea19ee8e20b0d6f827900775ea58af01191ebe7db53e48716561540263b7c513a5ae81f26ac31a337b86d383e272ebceef0764e89b6164299f4fe20db2ba70227da2c0d2c4b248ac1f802752d8e17334c177dee33727f8756ef4310470cc220f71982e10178bf0dea9bb1a80e8e363d8481bcd1b9d237a769ccd6807beef50c53be11df97b967e32f7954e08887adea0ed8c32cef99fefdbb5bf10074795468c5f610d05cade4a06010821bfca0b8e9dcf16bf9c69c51d6d89c1100c184f6b85e4a83b59275e963b26aba2ebe3b47ae682a18043eaf71d34cef8c10383c91ff64f199a422f471c9d82c8a0b15946d047c2b09fdb061da7d17ddec40ca7d70cc4ac563d3d9c1bb82b78081f49e574408d79efa00c89c589d7d09d1003e17e7144d1a7d0a47b51953698a34aee5c7e82cd9a2fb4824329f13fc52401010856cb7d009b399683444b19c01b5c2822880175a286471b7b3073c30c98f60d54b16afd9098d3c81bd703733ed3f660b11fdecf6e04adc7a4c61054373acb098a50d8fa28e392a45acf5886e9fd1ca5a55718bf375dae3744b3f0c1af778e0efd174dda8c174ae65c54bf11f5478b5d2b487251f03a97f960e33deac289670df962c2c7b34fd8c9b734a94f2024200f1386de23c23cca5fc416e2393d19550a3bd81744e8d58c08ec03d6bb8e016918b40d92d54011072e8e53bc69e4d2c602cbe593bc35f06cc7256d87aea75e48eecee9d5277a3f38e7154cbb530ba6360f88c225473ff147b3f5fdad3edf8647367b9ec35f577687fb3290cc171b70560cfc171eeb216fa4af87417ab49e842aa8f5ae11041142dc239aefafc7b2fbfc0867c5ea0f842eebd1339dbe9e0731805ffff8c3119a895b35efbe760a503cd5044d1efe829cf952b8bdeecb18496e0ab500e011d7a33fbc90d907b8699abd0005ca6ad6a9eadf74d402e34ae6daf7dc96556d3ef46ac11b9ea94b88204f80ad074e4c5374da9d10f1dfb5e12fb9bd559810c2a96b00484d0b69aaa098d6f2020334fa86d03b316c3ee47c92679bcf4dee94228ae4b52502792ec47f4af77c650a83421becd7269fb177ad26b8596d5ccb37a825d270c9b4838360d94dcd31f00567eed2d33df5614eaa0c6bc94e76cf9d608fd90523ab9164c85dcaf731965801dddd7ddf01b4d5f411e6c8ac484c464d06c28f06d0572695337935daab3ffc0e3dbdd30abb3b182b610768847ebc4b118a4f3d71e8edf474afec079409b7ee0cacc2dfb73400dfc39a9790a1b4d1ff6ff3eb961bafc8c8ce189ef8558f71fc06430e515241e5f3f734ec1cb9ad56459f781f9aa11086f19b79ce23901c428b0ab4dd9cd47b97ed67628b4c2beef1d1fc6799eb4618bacdbfb6c1069c49749e0e337c761992e99221e83f1397ad42df28b35c9fd4c62278a01c44ad00bfe6b50834fe93efaab7fcaa3b8014e4d9b82f66c7a5013755790096889b6e90130d0d02b7c762ab0ed2ffb23cd75259aa98f370a8fad5f1178c1c7a2071193581252c08 +generate_ring_signature 8d502bf01a32e567707adbea7c5b07946c8a91ed1231dabbb363bb6c475debe8 797d78be1bd8f1fa9f586e9710621bd3d480427b45588fdfe8b845b12414974e 1 bf227dd9be0cb17b4cafdeae98527a2bd5f22d56f4a4449af561a5c7e9979411 c1d63edbfe8e5ccc53d5c54f571718b81448166bc241c68f09aa1fc0f337d60e 0 8474d191a4dc4edbaf7317ba5f22e2e3ecf48181f09bf6f6ca878aecd7f9eb0ef1a01512fbd30c659899e2a9c26b07ea9a399f4035d013075d53d8fba0042702 +generate_ring_signature a360d6c579fda6e77d9d8f7c5670c5a3e56dd67f9bbeb9a2a51ef4fc846c9e4c efe52ab1e05aa48518f70282135dbffc35e61066c0ff77f69dca8f4f77cf26a7 2 5f4e6fe57d228860581b1597387794778018d9508ae293cacef275f3f380f949 7f855f04ad22ae8e65f0dd48b9140295e23165c1bdc44c2e9caac8f7a4d47690 742392bd543557e97a5c79270150105e0e05099b7db61d2f07f2a4ae1b0bf301 1 c0f352b2d4e382513711017e089800d1803f2430c05e49f7920d2b97f1b9ec046d5be642649373a485d93b635e7b4b608e15ed664094ff04ffdbd9ef00277409f749552cba38a571ec823483395216df8cbebeb7a8647c152197f4ad1151ef0e0e1662eb65e6ea3f34b736c6f02de514d45911ec83223dd00c014617f80fc506 +generate_ring_signature f06ada276b4cb15bfe0baf1973b4159c4ae03306b3d1cb6624410d84ff75ed8c 33f1bb96ac7545b8d506cf2332fb5c1221d7833e8f1a58c7e72e7ea0c3a85f44 1 59ebef8302f5943bdd11879b6b4e3bcf098eb70a56e44871a3e6097b2a38f682 11e1c3f3d5d9187ead09f8679cb2172afc020766ce0de7ae29998b7af9895b01 0 67df283aac57110a96ad2c8a9451f244f0c04be6cf86fe7e8381ebbc30aa2a0688cdccc4a7010edaba7646a70bc03a81b064eb61b161ae438f2879eaf790410a +generate_ring_signature 4bf0339d9f8d053c5508af5620f121860191aefa336297ae61c07ab16e830e95 4b74cb953354a6d552dd2093fe6547018f2ee804b950c00ae2cd959d9edffa02 249 b273d48608383a643e8f4fc7a3218d79135807e1f01bafdfd131a6ebb066f53f 13a87abbe6ebc9c0a6a758e2bee754d629c0f0e3c9a8c922f2d6b84ed1d09563 41368a4bca9b547d015b4c88063bc4c61bcda812a31c5fa1380cb9917d91bfe2 7adc855900644fe40f6fcad939d2ef3d2ad64c78c67081eafd47aa014fbcf569 327c96fa149e38b3e54f28f3462c9e66df4c74a6ba7957eb9fc52a91fdaf7dad 421af8feeb282c03a8fb30a7837837641c153fbd43a8331acc28b1c4a9e86bf0 61281e80c633170d98119dfb0fdb35405f8ad76b0f0d867185ff43116abe96f4 2e25524e65bfc6c97f57a92766d15c394e46386ec49d2749df2d45bf52daf1b8 47ee6c2a1c29f513e09d89f74fa7b86a39fc4468cb648f74e0fbc1e48d8f6d98 09e04617608530d57ace72efad388ae1ae3947c0466c5b430b9dbf351f392867 ddbf7d3d8bf539aee1727a3eda3737f18ab63ffdf80651c5597e10fe6676ec56 135947fdaa00c8fef0bee2b1a8e843746094317d6feaca3c12b15d10154979e3 203b1c508ae96f42485d43de27fdb3075f3be201748e51ff0facd328399ff2e0 c336b25aa242b63ae73f67e1bf193f50fa8f40e59110b69e76db94ed57886d6f 8b285837b4f1690849e2b7766d9f530218aebb3e2ff9110a7b149378dba033d3 563db591fe4810c041d6fee4423947f244f35e819f0638b691b2b9fc77cad430 03e7898c213d50755f382dc62e36599c1d3dd87b6c9e0159472ed2f9f334c0c0 22e97507ef3b946b063e25d97f5212b6494d2696f9971ff5d54475ca9dcc1afa 229b32794f12ebb14c367f5f92290fd8190e14a65aeff3fc78aab3f8637b2f5c d6a8b2d32e3fd2fb5d1c71bf9fc51c0095033217feda93d973e49308f5694ee1 babc6af36f56c37f9d1cfe50d88c6e7a1403d9ca9cda788a01669a8ab6f050ca f07adec8757f7740eb2403d1a4b1c29bc591f5f90aa3c8cfa17035b07f0e6937 040390e90391a7eddab4f52925efa58dfaf935ae202267ec0f2aa535266dedff 84a82b340c5ba88f1359f113fbf717a945ccf2baa3b1561f347a70a83eb3727a ed06ddbbcfe612935bb53add5ef26d3e763e820f62d549af2713be22c6c6ee48 422ce5b6fb4e39f0846ce3a429a03fd2dc53df54f806b5ecf1dde1f2b004b074 0de9902ce29035edeec28692133417787524ed249b1cc1e2a1c000c2f35d563c 51c3352d40a920458a2e73a32198bfc93a72732d434b1138050ab8574cbab540 777b6aeae382f6d39fc2e613dd90a4228c2e8d69b3c56a84f250996a3ccc401a 2a324732d151575134c39e80275ea6aefaeb038346fe08314bcc90cda45b75fe 99a2da7d0817ebd800f5c2dc62649fc202068cae2692760a4b5bc17950249a05 3b8676f56b6358890bd11968a18e40f0fa56155aad8f89967a9aa37d8297b177 c0c464a4bd5d573e1bdffb58dad5697660e675a9e9e63858449b89b267271a7d e649468d35445f60819fe76f8780023b5a1ea23f116398ffeb4b4620c68e344a 05e7d38bebe36c388c980a9df464f018f48681ba297ba940cd46853b52300819 56f4fe13aead9eb250187d3f55bad323522841e0b5fed2257cf419692be899a4 acbfda9c0407409f6363b3718432409a2c56ead586ea6f8625aece0d48fbb29b 0bfa3d79778d063ad3675a485bbb8f8baca3296368409d81aa25ed3da324199a 800a29bcac3c088906ddba33ed1cd9e8ad0335d5075ddcfd28ded381179fd5a0 7dffeda14c5e22bd9cb1269ac3326a6a617b1bc277a77f5066aa2676dd6b9d40 8b786c7df9fb0e695678e9cfc79771bc84badd695e7285ac75bff9d6a07c7b8a 11bab52ae903cf6d7eea8a8f14da338ab52829ae7abbd6826e0c96cb1d8ce098 6e0219a5b11dfdc9ac7e6a8ebce4bac2b775a98a12697f8ce2afa792dce8553e a45f1f5479673fcea8ac0680f92089b115fc939bbf97094a430ab849ffea4dd9 e5aaf69d15c087793936a53d679544d2634ff9db1b81cd2266629c5b5d6048e7 bcfd31af3666aea09e3255e835fd931019b944af779af8e22428b713d942694a d320bc2b9ee9a777749dcb6c40e59a9eff375737cdc0c9e63539e89264e922b5 00910267ec94f2cef9ab012f1884518fac66acc140e82bf9684319d438792eb8 b13f3a4fe4429f8c34ca50fd9649193a1d9d42939280df3d40baf029c08fea85 79ddc3b230713949fac670ca6ee8442ab77d3987ad1855a856b20ff8c184fbd0 b9cf848cf9d1aae5f0d066e281b3eb0d447851c688123e4823dbf3bc5ff84929 3ff30f29ca0f5d56e3114205f032febe2c7ce3dcb778ed20d0fde85e42d44a69 49f1c860b9653e21a699f11dc10a11cde10d311512294622594c7fdce67fb8b8 8b18920f526da2a285481fd308428fc2a125c257155fe8b9c439e100c1f4299e c3b822f191fac8d362d2262efde806e5bb0a468290708fd31d6fa83890363a8e 114fc1213163e40d2f246906455a7edbd3070157b2089bbf0f23ebcd4576e7bb f39aaa33d3a77cfc664fab4560c47c230af7809cbff25f6806c59918296e95c4 c5a3eb0d203d502d830ffa7ac764c029b2890cd5c7c48aeffb793dda0e35f206 ecefa373bd1cba2ad4b04d5f2921218abf99f0825cf482c3540f94432cf01c45 3d4313281bc933e2bedbd7cad99fd60f1748256df79c08936d2162bde71fbeea 0ae2cc5b6d3b36b676cf08f4a9b4d754339dfa0d0ea5ce5abf5470b4726d09b5 6990d8de84993016435ca8f23aa18a7e72dae4d8d7e232994f7fa5657d34fcad 3cffa4c6b3aec0efe4e7e977e5f8a7294ee5c99dd5c0ddaaa3fe23351c941d85 a2d0b812fd1c7d191eeb61567298981e36b340c134c78da944f8a2203352e8cd f455055f5002146f5bbded6001d7ce6cd58e7e274caee50cdefba3613388a0da 477131a096bacef938be8fb54088a9004b29aba49f4579c33ca52deac5a8e5ab 67080c465a248f515e86b58bdbe91ff272cd23b08b2a7efe4d69f47e0ac85fdf bec5ac0c0b01a028ea0b29369e79e1c81eff46e9e69031ac3cac91aaf4db462d 2ccacdc26da82afaeeefb377043620a661bac86ff30ac720a847275c3b22ef41 6b20b96ecbbaaff46371c8c5094ee3af5250e9171c3207804bd0ce83c486b5c7 77d1c2e2a5b9048ad9c2200cd77495505c851fe1d0e4872f46de66b7af209ef1 fcd862e7cc429f954f422dd13ae8bb48dc029bbcf9b914244b7ef0a20102a155 3ede83a52d68d2fe4f8d6e721aeb809bfdcc0b38882d41e41dc2087c2b197f26 e38fa088d56b933da20b16848fe1310e4243513dd3c729f4032f74d47891ac8b ff44c21ca0af4dbc2e0dc012ee8e122de372f0d530db0d68cecac9f2a7c33b73 3b9903664c6ef51dc655f45f82de37b954dd70a37af1a45eccff40c492f797a3 1f70a93f7b6b459fa011b5e7bf421d5108430c443b1849f09e23aadbafdf2b22 b957c51631fe2c2dae71100ece8911fa410088a255dfc3521c0b1299d1ec89b2 b4c2e3669df39ada556b367b6cc9ad80b49d3ee765b54f288f31eaedf947aae9 bec54b546520ce1edf6def1e99bdd92fe35fc090f3d7ec249ea4ad7ddd998d5d c0105b38734b5a317dbeeca88fd34ea91633d4eaa90171724a420512a1769f48 34d20eccf3554bc5cfeab0839a5da3af1026be554b8006c91e495014f47a98a2 89b6ae04aa15388f5286393aec911d4ae16fa9e1c6cf991a8edc93ca27511c02 dd8ed17a2d4ed78da78446870808e79b46bd9302b686b72600575324c9a18e72 9127b0709d801ac54b8d550285a6a4e30a77f84a532eaab6c337a1bef97dd1cb aa0c38fd5fad2f1302e5983a9a09ae83edc730a32610a7dd102475624720588f 1b614f9407aa350b5cc5c52ccbc788e943a3f3d6e6a75e332103c7cb52503e2b 7bb824ad656dd32ad9c9ca1deddc41a3589f3fb9673ecb1621da283659139d26 8bb69321746fb816425ac5848e3d941decc629180993180572a2d673766d217f 17a6eb6175bd025118254836943f75898ff81a9cf26cd18202665bfd15c98e16 efa75e34a18284a2434c5b59d0c28891b998b2120a274cabc2d6757225c1b746 4866748e5ce204fef81c801a58339108a51e6d3fea7e5e2c879b22b358aee2e5 eadb7e4fa4a23a04f7eaff1faf8cd72b98215fe213ea9404398c3d942551249f bf44973880920ae02da88d1293672bddb4c5ca8d2c7d128cb244e480618cbef8 466f9c575f9eecb3f26664d593dff14960f4c68b3545a704b99594cec3f63060 b57feeda8456b15539b5d24c1aab860b97cdb498b629f81d57ec233e4111007e 4915b38ba83896e3f34bd08451300e26d5ef5d8d8af7518ac0592d19845b4464 d0524fd7c02630ab477ab113cbd21b5347063c933529fa01813d43a0d7d80d5b 27e89f490f81f8b21f9a08539970ea149b3a18196d54edae47105e2c21262498 a159d840849c5682a7fcc45335e329c3f4f65a0370bd835c70b2ebdc08d2eb83 b6876311d19355a2bcb0990f7b3fb98639eb5306af6624767cbde5013246cf8e c9351e40f566cd7e30de47c39154f598902d0d2a4f78eee4b37cc5412b2a6aab 1e30ceae50d00e8a0e87c28e46713011b4d911da0642468a47b95b98c33f4cdf 0c0f7add9e8f5c60117f50951765739424cf52757f48455162770e2642b44c90 e72c4aad3a76f21c7f40a8f7726b4f25c887fa11de547e5174e57d146462e764 49ae88514ddd003ef502e13b4ac56270888953a5802a5859961459a4c702cb31 acba3157ed52715ab3c0ce192c822fc4d317e84e360103c3cee3115c70a601d8 b05947d43cd215f923d48dc386c3f992351c5034c9112763bc2f89d9b4c323bc 0995423fc0a5997c95e07d3c4e16bade98d1885670ff48e59929c59c6c91fdfb 722c55efaea965818c6588b5b72f63a5b5036964ce66c662b0d01e85417e03fc bf2a2562576c9108ff535b04e023ade892ffe832c81deb4ab1cf69df767ac1fb bd6bfeb30c62acdf53af007c5fffe06ac6479fdc1582dfea2e5100dcda23950b 0c597284b35c159e09adee3c5f2b4383ea00488302714851b99f0fc6a774b7f1 86af1fe8506d70599b9f8f277f4a9467bff5f11c7838feeade171b6d62c109da 3ba725fc3c37b7c284b280ecbd3030d15b99008537d072da46609f1118322170 0185bccbc5d27113551e4eaf0dac7a724fc0c5bd8e03ab58891a5af79d0c4540 a03be24b919fc712ccef8e92f8402b32a5ac2e6ed435f13a01ac0adbe4cfff04 13a37f625c969172b64012f9c07ea8f87606f9a44bca086f77b72126dbae08b4 c370773fc793e240c04a6c6b44ce8c6bc06bbbf85cad228c299a0fe0a30ce63a d1c0b2fdcc1f83f9ff3808ed8246a6bcec7818eb8aa2f985a1cffb4bc71240bc 8df94b166ba97d7b5d3cbc0efac70565d5fd8f1bb7676765433122d3241476eb baed16c73411d8331650189ebe4b53fe9bf2237abe48579f14fd362fa1eca68d 968e2c6632c17127cae014ebf9c72942da77624a3c7e850b821a3b35cf3fc089 042d32521b49938a514044f1d65e69d2e74e3382f9044dc58d33d762f6a3483c 5f6ca5b45a1bac1ce6f04d42b6eef09ccdad558e1b3d5737fdd4c1947d57580c b8f960e7d188503d9c23b47eaac1ebb6ecf00558059a2062d3ff4916dabeb00e ac773dc9ce7620b6372078649fdb96ab0036c6524d2c24af9cb2054372306681 dcc1a2b1cdd61951167bcf580126f4d91854144f410ed394af5a29732c430935 399c3cff7cadc89baed9290842c4d689768b41dbd3402aee4751e4e6dc83cd83 cba3fddbbbde39fb6f01534b8049452ffe7e1837d28e466c0929c7726b5bb3d2 a525c1b382c198a32c710f6d1bbba6ed75ed85bad57ade17efe1deef3cbebfbe b10695a3567eb36d140a8c81b364477e724812c5d5cdd77631f756a678965fd6 100f2e875351cfa296de66052c4a83ae00c2f40bb97eadd1e7ea0068e524c568 65373bf6d75c43fd82031c030df1cbb1f2e54fe11dd9152d06f67784437a8c6b 10900f24d787db19021c7f6ffd05ccd2c1485bc9ad50bea77b5f7e2dd4d168b5 9206d8d7772a8042bd9b3ccae4d1e897dee041227269589a85225df73d51d8e9 e2196925ba83980352a4583ecfc2609194bea93e43702e1efab71c76eb99a77e 5555834ada3f35ac16555078158b1e129d347b2815d0cee6e17bb1db04fab190 9e411227937e25b05de329d88b29a3281e1471c26ee51636edd7b2bdb54532b5 cd812857f0c9592e899749df1d1ca06a4ec898e2d459fd463b87644ca1a80952 c336f88981b39cd2835bb54ff9d40dbaace66bb32005c266e4697d072fcc59fe 2f96e2125d594ad89999559a2d49f196ae3a075651d34351aa729147fbae4c21 d7f34bf026e0f3eb8f989d50f7fe2ca31e3e84f322bab7d956379b6ca67eb8c5 4ee15b8fecc0cd747a0813b82a98727e6557d35636210fa25046ba2ec63f6778 5c024d33681e72ce084e049ac978cdf05f2f690f9eb9c3c0a2f667e9feae4114 eca951b655f40be30b5139801f0eb195e6c1236dc1de5eb982a64ddc5f3e0ee0 b1ec1fe38059d22a2361a3698b04b3633f8c5e90f94e1b2e67c7b0a83d0ba135 249dc9cd75683ea99a1a6d78b941d25f54240f025218ca113c1d10e83e1e0844 395735c24e6403b5f9b3bf6cfec617ac4afaaa68e3861320c6ddc0c3dbba3e3b b88c05ad20d89d50304c08620f6b7f4ef80c50a95cc2877fecd2b929658de37c f4d13f28d1c3d0b5424b8ac845becb99c0c63fdf0d50c36a72a2b49bd64c4486 5eb59459759a26c15ede5971790bcf73fca4ef0bca2b2d2c712ce5d7551c7bbf f1eb0ea50f8c27d0914231446e3fadb7f0f4076b236bd76d15a2178043f15b1f 042c13be06da921788250ae0fe2169ccfd7b8f58258f6a03f0eeeb9ee4b056cb 3e8805215ac6b972a2007adbec893c526522a43ef8817e16dd09637946cdfe01 82f05de9344173995217794bc4d6a239f157038e41858fb49d73eefcc4ce979f 779060ea9dcf24e80010259572f453b743c818fe22d78f73c178f4f8e15f8340 8578dabb1c0d7d98f5e8cdcf2aead724a8fef51d24850eac7acc4ec67de107eb c8959add000fef8da12a32535810a4629c9f3005990500c472ca0c115a0f57aa bdbef5249d2f3fff40c7bbbf5293987ef976bdb15d410a62dbbc80f5066cfab3 82cf3f9a8c5131e29b1b1cbc9f3d48594fe30a5a0faf9f7b4d3518ff44905567 cb5a1a999801bf308a00933438df9adc4ec64b82a91cc819f46fdfa4dc63cd10 a2b5ff7c5043db55d1ba34b94dfbbda40c407393b2b5cdade2a01296d6a2d6cc cd9f89e414c961caf06f311ec8dd62f3941f116b42ee2220f6cf8730af87f225 94f778566f9ef6a3eb371752f76cf66523306c2ded5612d187210140dbe8ffca 1fee562d0e19adb7274d1bef2616122f37fcab620c31b259095758c361749193 2753541527a5f28886b6079e94031216be897533dc3aea2ecfad2d3b34c14a0d 74f9cf2e7284f89e175f7da2ef898edfb392ff850bd0469cec5b2999ea27e413 0822f78b4c9670073a76bbb206aeaa9f56d2d4c37115cbfef360375fc2bc498a e3bbbc6efaace48edb5dcce9a82bdf8b1a01ffef4e0ba272695ed17f31ee0ba6 9c249607b3ec14755909b1a4b96576864490c7c6cf077222f40e604089c2ab78 845830b099308bb31cf8d202681d2f42a709ef7a7ff29f25fcb340b8f6f1af21 f18514f95167651c539492ddcc5a154da18999f6fb5ca844c6661f113f36505d 1095e5095d519c92794caecc036b98052f8f8ab7dbac113cc144734cf6e732c0 4fc7a8f505c45e9cea40249551027ea08f7dbfc56f2cfc7dba28a31f8ed09695 faf4bd877cac477edefae6746b50518756026be3f58c3e283325f8b3c68546c9 ec3ad5ebc4dacec3ed641ec9aff310f11f6528b186175affc0dc3ac494280e90 6762ea15a154777659755f3e952190a37166ece974ab87c0f0ae41fd7e919646 6218aa86f87c50c1d29d74530fc73096cc5770e41c34bed1094ddc66835326cd 6c6e8e95cecfdbca5a6a3f81efcbbec3f6822c94abd6824b0b2ff04d8e005a28 42529960daf5e9fcf69f0378c7fab63dc04a440b590129d09190b43adb33877e 34da8e22d7ac3501057278a7976adcdfceb2f0946f9295163c42b9969f349de8 3567172889fa533c80d6345fc3bc669824b16091528a9ac6363a49555b3b1839 83d3c5a2dc852c1de0fdfddfe67bdabd232e7e786aeabc536e469cdc5a96100a fba85c323676f6f3d712a7fe174a74291ac8a7093279ca4770e2fb8b53292245 9f745cf4495ccdc23902bec531bfe44ee6608599bf528860fe615b5e64cf22cc ec2408378662a405bda9cacafda3bc482bf0ca4a5c3ecda11822ed66dd23ccf4 e6e2a769bb7bc694d5334699e807b312b31dcb84f12c098e9ebe730aa0aba197 86d5f8f8b1ceb931231e7955ac295f22b7dd5cbd5474c6f17ae9c67bde08b7a6 872a5aa1c00ab676ff697bc965082db4b84c9c5912b13cd73e238205b968f6c9 98e88fb15b4e7149d36a4d96369605702421a82536601f0d92a8a22a00196a74 b90582eb1cbf5d567bedb375902a66f452aedcc9fa4e610361b1994ce9756c9c 46f7eac37dadeb7d586f1f2154fb93224ffef25dbdcf5a50fbe2058fe4af4fa6 1f7515f2e3b6b0adc19d28b1733b596f408b3e01efd7a1c32d8a730687c83303 d9095d067e48593259474e88b1bc9d7c1b8f7c9fd6dad38541738577faf674a2 13b7e9dad168b21952ab494ae468f7eb3622eef3c93754dc85ea5076395b8d6f c53c69379620465b2ca2c0b55e292b41771066169b131d8ede8d958de335a124 f0f67b6cba0a5e26f0fb871f3102afe16c95dcfb3984d36f77158c55c2116055 83d6462bb79bd75ed33ac87b5a7792c49f6a7c6ad2fd9462846a7dab0bd5ccd4 e105fda7964432c084b06d5a4ec3ae8bfab30b85fad2ac5e12bc91f570f06c1f 7281cf80a74a1ec7881cb6173e8c82a9878a07ff0642d24408eeda7de48bf6c0 8841e7f99d72d4523a6434bf2f7654354f5d262a9f25d06dc5387cfb53201fa0 7221681239e07f9604d5e68a46965faf97af8c651600abf993177a340f6e88d3 113159560276fff26c42d020fea67f3d02536350cbd7b4ea36f76c70c300624f 14152448ea110c2a7f6efe4bf7173821cc7436646378634944164141e862c083 3f85f4b21ba09576235a74bf59ee99fc7690159934ac709639927a12598de4db 0ffb0791b151164266c3f61fee62371ad4b847681e39f11545fd7e79c8049a87 deab05a92357a34e87684d48570d216eb3a600a40ce746851bd80e33ec1c68e1 704537fe1c46b6175756a847e8a6c8a4eac68c3834e18f1aea4159e867b4afe8 9432b7a32bf0e82599f4c89b837400ad18bd09a29678d52f9ef0fe672e5db9e7 72915931e01acf984fb517b671b1c2a48bde0650bcc8dde11ecdc957deb2ee0b f0abb70c156aa44e30f7ecbe678f9ee9c0036c8912b737f31356714014a28026 93af6cfb7d315152248a3f5832795c53ccfef807bf889c9e08d7159ba5c7bbae 5cdcfbd85daddd4f1342a440b80663d03545d138fa31afadf45f8f97d6215145 bc67d0bbc9c1e729334eb1f625be3f76271f67075e080ff2101f608cfa6c9243 647f856b3e4a7f5b9f0edbc3c2086a14d97c4cfd940804e93e7fa38d4657fb8b b04035320466e030fe02365fc9835b0a13c75e171dfc1fcbcd058af05fe9a193 f804d2682dca7dcdf909155f817115f585dceb66b4cbcc513460e48dfe2e0ccb ca00d1eae1ad641fd240af58d71dd5d1b360773df738f76475f9ddce0743c053 99de32f96953e27f7fc7d6ace8082625adad8f875af5ccf98271211f6ffbf168 cb72c37155f98e712331a709f85b2b4cc137c3065c93b9a8750612e0bbc55797 ac942c8126db13a0a40d133df902ded357717d0cb8791d9c2b1b62586ec0e7c7 09e99ca05570543f80cbcf9c6381e19e8d5f21c00d96c9d04620219b842cb88b 6c754834e8f10b4f7c63b15b62bcd51232d514d1cb6542d44b84a84ce6d35c14 9cb2a9c1e1b3d50718d06faed604122745d6a0c4b7218428d2a40da6694b0b20 37fa13a595721d0ffeda06edcf2a9192aca945e8f845f39c850361060779bf8a f6a1fb231730587cff8331eafaffe84c036062f58c23313d0366d227f936dc0f c4352ee007fa39a2d837e554c96e3ea9c5f30bc131ee4bfee4691af41093e8f8 7888f2ce93dd67b62dbd401a649f2bb53030cdaf63958062a3afc269c65aa1ae d4beedaaab33b1ca7a62d8e3922a9066cc57cda6f5f53a1821c4b93663acf2a0 86c01b16025f966d766445addbe03d51be961d39f02611b1be6d52ce2e65ae03 61f2347d273c611883aeb9d63b502cfbb03935e4dc57815f89b2d960ca5f852d 88de1640f8120b16fe3cccfc6cbafdc3b2cad4aad54cbbddce71dca7e5a93d56 234e16921e47d84dca0ecde65723ffac02962de45664eaeb48f886f502f127f9 a7cc083d50070f1f751506a25c920ced9af70758dcbc609018ea29d2bbd92378 2114d7509873f58a86c60bfc37cfdcfd9a9b3d6bcd9adb7ea2f872ebf105362a cd8022df16cfa6aaa2e4ba5a2a387ea45695489724e472b48af3002557a76ab6 8deef1f124f6471ba33742a17098f6bb74c0d78a02ef47d303d616e393eb4519 48b56089969f9016e90b1a0aa3bab624a76ffd2d5bf7f8938269b114591141b2 9f18576b44e23d3c53213b7b7e4ac62a106282fa4561b3909c2e8576dedf414e 6839dda8b939921fce2234d2de861429ef0e896e8bf53734c49a7d9f9987f2b2 b52f6b08edcdfe3b6c7913e9da807d82e25a549e36202b8a8950f0a14fbc58d3 558d9906fe2fe216003200d96b368d4277f950cba6ec1949be6e42f0bbf522fa f369d7d155899f1fb4351c2a580c78ba591f7389ade0a8be48104a149dba7f13 06ff79895fbdc3db1d298456ae015ed3b7d61ebe3c69df56878fa9d6bd4522de 6e90d3ac10d5efe134def9d541f0d840c09ad7cdbc80e7ae326c132abdc919f5 b51705d06714d270bc2acfce33937dd15cb0329952c899d6f4304c7476adacca eea364ab4655adc1d2af1dac26b24fd8a6554abd57925dd4bbed807d6c40dc3a f815a1d1b99c386ede5a04765bfddcd83bdcf4d3c43cbe2eb768e9064bca8bfe 542eec4353c7aee91d9415ba2fd07311a3936640f91f1b061f1a1fb62d0fdd0c 127 8d31acf18cff3d7a2b763bd939b21bc332ba28c068b1760adc7e018a1c3ff70142d7e1178b7561605bef5a3b6a62f410cf385ea585da77d7e0cad50ecc867d0b032d81ae2bdf963829d776d6170ba2e38748a2fdd54af88fe026c0e71f3b400d556bb8959f4b6b87650c265bc69bd7ca9697b35193be51bef4a4ca254e642608961f2e99fcfecb08d694c4e5df1041a94516bb0472d37fe4c4bb33c934a03f02c80d96894faa8deb1a0a69dbed56586e82767f4f8a31490caa5da1ac2ab2ec07172aacefe8462da0db1b53aa3c8f16acee5b59ebaf2f2903d9bd119caf67a50ceb3b384daa28947e5e027a54a3cfb754da13b9ca1a774544f6bf8395b4ca2a0e969a54290577430ea00809519d78faed7555a4a92ffa7874390ee882a197f103fd4fb021a80d8cefdc9c1a790c99dbd68db603c68231298b55cc627514b0740099c4ca2b2cc40d33e34346dfa24fc0ce0a28c6c0deb63a3b6af5a591ae75b400d73c4c8ff622bd8b4337e809c4c9c90143d8a3d1fb69beba8cf049e7e55a28018ea4f895f41ce920aff00f6cad64091797d75ed9611bbd76f4c1edfc2fbb9109185901047ee6603e965c1afa0ec7d3fd32f76cd1955951a4587854aa579ad70744961bd01b5d6d626f621b798162fb9c07849e80f1bde063daba70edfd068c0759b1fc9504ead94a40d72f184812e48bfea4d48d07c045f27869a0e96a467a00729b9482b9a81006a19513a71ce69cfa5b85fb2b05e9f60050ac9963dd197c0ad71ab74b4d9fbbf1329ec64e12b3b9f3cb2af9fb341468267bdab35721c8ef02287721349eaf9dcfe74fe3b7461d396c69c69acfbd1e1f2beafb83b69d9b580c169197c709f622f3e76782fd0bd78d40b4c45e3041d7c6020c3414bc2d43210ce8a7180c7fa6613da4d8c5964199f721fce9ae1cf81bf624cc830039df3cab0e43fcaaa28079d84fd80de958ba331837d1be4f57f090a301ad6d96d5cf5cde0e82265b1f7439142f85b54a0dd829049adcea3dd59ee74a1c2d9be7a0b8eafb0ded2b6b3f7302193417b0f6f69c537cbdf35aa5782d2f7ea2120f353d7737a40ed3402150abcf3e3327929175482b7220bf98e7c75e62c91c29d6af978a803d0a60f37bc9e42b2295119a73b26a02ef305e19a95deb7b11cc9ff2816ba54afa04c1c02a86298dc2e635877ae4b89bafe609b4717a7055413316cd3b7d8e50e1093e5c399d6cef49fa008500881e08ca9ee88eed262e9f5bf8d23b1685e3017e027ea19ca4cf81dabcc6870da3dcea89314baf3097d841d264d70211358c519602d6d3dbed6fef64d0a54011a6a9a5a9ceb76f0d9342d12099c2ca3ce24c5a510f9b437400a55a0e64c66c8cca64674f96c7ffad87d228f1c25f111f8c8a6912006b192025c195651aa235b4dd768e647eb314efe335e682041e44b1667775c80cda7e1facadc791d9af2cced42f5156375e8cda1caf1f281fdf99d67a621b8908f8a6e7da7c33eb9cf085916e64264310211232975e7cbb648aa375a4a8c9930da2a322b2ad42a74fb98f0272ce042c917827fc6803593c8386c5fc14e0819d081fb29ab9bfb5fb6e4653d5889d8ca50fbaa8edc0cf9c6943403a5b755886b107af9beea17a8af1aa42cd45c0b573251727fe4c9028a74bf5b44e3c5e1322bf0b6388af808d35299a46a24f30c267bacc064731acfe5b84ee14d56064b9571c0ecbc6d2e047e40933cdc8f80cfcea96abea330cc502808795f54291a87ce1c10a7a626b83d533cdac92732035b66c614c73f488e349b221a9c2d9e947dc97af0b4a1207fc51203c1099ad7f45d28460bbc90d217fadd14ce8a62e794b6be28b02e57e34e51a30072523d9e8dfaf5df9b6e0db98096429a9dd9a865e9f0bbad4058ae95488e213cb0d9c3b01847514cf1fb3c8041baa1232c15d980fd59fb2cb0b55dd785d86ac352756241355b71307563dea63f95354ea6ada82d292582fba01fd9e3fa1157eea6d21f72fe6808dfd315137ceefa5348632dbdbe2a2c18ecf0d3411818f032acb787f3878e7973ec56a1f9d11eb3f80d12212b9c65df1090f0d0ce8e33979626891b94125d44bca4b4e1a819a393f3769cd35636a561972830e1bd067aac618529b3b740817f813310538b427934b1d8b5664f51bb81ef69006b4058b4dbe78ec9b38c775098891a222be44974e7f5d4283b2da4c88e9c1bb05b756ec86a0d1974c468b81bd261a82aa7cf6c3e5dbff7e47dd800699ec44250e5e7e745345230d76757cfe212205102005e1eecfbbd174f2ff2817eba8272b016c1581a7de86d0674bc977bd2126d3881f73bca9e5baab80bfb9297441a8f4095803e8e9f9b130b5a1d3321a0125409458516b9ddc776284906e2d11978b450583228d5312c26f06407fed2530b4486f5c0d4f7fe69621d0a517d112400a2e03e56ffaac6f2591b36f2d9b8a81d1324dfc4a5ee127071879eed655fe2d142702dbaaac7ab12f8f9925a59907cd8e215c753a2def62c0edda08f4c8f38c07e205e141e359721a07a29e38ad6656fe976fcb16f5ac48d4dc54a4aca18549f6780788f92d6f37fa7d740cc28563592e4be3beb36e7a19d425b513f680294dcdd600bc027bc8b38023ace1e70df496d6a97222d206703a961ae58af861946ba5310fb9b9d93d1849ea15dd3d67965fa8e37c2406226566f36f7204b5018c8d6920000c37e799d501c0da22952a51409377b74a6cbc6a556dce192f036dd570234806fb5c747c12bb32789602d68b972299d430de3f924c309583a6edfb933199380d4681e5df110e82ff578c03dc2942285c882f36dbdcb1522d1adc05b65f5eb50d77aeeae6961a64c94f93c980a693b939a5a672da14f66abf068742b72f898c015e984d533ec98d21a270d368d1589ce066d630fc7e0ed088a98c9cb496ef3703e191f76973085f5a72963a765a2be72e6722a9e6ab3ea9d51e26278e37bd63058bafb4aa684d718c77d7f14e27128e60d9336d59079ca1feeefd1df6ec46b808030de2041d368cfb418241b792982df1479d07bca84fd1d626fa6c6f23605603a1a857aa4b4c552ec42e71e38bbd254f85dd9c1e3d62d99a5c1ce74861e9dc0c30ae397eef0e27c776d30d3ebc9dfdae013819905d7d7f20395159ff0095be07ddff48205ec3bfe1b0b645bd63b5e7d9963f60592164fad0ffcaa152a1634c09d275d541a0abed1810a30bf6025a231f62621661e0692a6526cc6dc007c7db05af9b4f6a1b83d9dde96d1ec97eb7fe8147c7bd6fad8d5a113806dba66d330e02b30078367c0cc49cbee6d0a8077f1458bc3280fe6f596554622c5b50e43a7d0d8bb02bbc9d2384038874a58a3dbd8af35905369a76ca1a84b73e12d0dd62d20e7fd20a9177089eadd274ed09ef2be773e216722f0c76119022ad83f885009001e46091a17abde7b3af9a74ff5350783153bbc488e0e6c2260e918e1fbb121407e29323952c59ddec35b7a760c4c89bfbe620171287a4aa6898c9a187b2827b041efded978dbf705f18f424780e789f2c74bf010d9457cbf89673c275cf79f70ff0b0793da151462b629842e81d8290fb17b419bca9dd6b356ceb415fb1e40f0efba14eacc9702368b688b0df12bf0229a95193f3aeb68727b9555d368306c30b9f4de41c5630cc09be6a34770814535f8b8b5b0530a2184216c62e64576d2c01d4fb7c0f7765fb29e1ab5a4a5e626285e9e8af0fa6ced707c3c36fe2ae14fa00e5b467e54bf7bbe2da3f6b37c714ed21c7927beaca338b08ae9094cca8d6f80cab3bde49b255a8851a0bf55024c9370d65224680a06af15ae28645fc4be38706894be22ec8b0859f7ba6883bd06c8ecec7e5d29a1f5b3d71f36e544b1cd7f5008090c177134cd4b3ed12ea4465b4745d22c3825614a3fb71b35c0e047704ce01806e1dd4da697aeb779a8934ae6c83e59e81526a02321b790736c26b20002a082e60fac8edf157cb37ec8aa66a6658ce45459a1fb0c3299cff18c4120371340540b1216954dad34c5865277ea3c53e70d954acf265b57e8c2c0444f29a12ad0866cb496a4a114616bcf558d6f2165a2bde3fcb08e6f532761970ee9d99722c0dff77a3c3f10810c91c2f38069ea3aca2886a45af5a0ce80278c59bd46cb4260c410a215919f04b38d7112709e729e41fc1e139410b96017401135eab589e800d4e5d175b4ccb3f09144224390be942f3e8cf8f77c9a7bef263b90df2402fe60276d0dbe9f18c6c682869721e30e1fa326545983d216abdac8e5813ce81fecd02a889e6497833d270609dbb4318520a80a80f0b99a68d1526c3bc9f99371ce20ca85ef2c02a89c1eab25d6e5b60829d6344a031bf0fad62a670b7a53ac21fcc02ecee08fa66123a0be39535a89237570b3c8869f97fef1b2227443b13e586850a7f90e6453e2f7908fd432b8c2425c7818995c423c4ab7d92f88893b8388b5f06e89713115e636eb34c216813afd2027e1daa5821aced4c156f600a94d03d1b0ec0f7f3d44c59223e3d99ae164321dc475bbb9e27d2be9673c423f36e56504d0173f0e54eeeb4497cf84098028817e5c86b6f7b3b852358a65968a1d93ff468025b044eaa2648e1c5353cc3fa60de7aadf7b9e066bf65cfff7257209800241d0c0292974c4ab2f65c38d5ce61e976cebb1d20a894740be4d37a8b73dcd692130e7be0fc910f784bd2b823e55c3c3a0f577fcc0165cd54b632bcb0aa75cfbb4906771735ae1c81ca52aefcee14ad3926b2849ff7f3e1ecb7d31d117f3668a6f809bbce90d190ebeb9f5409b81482408cd107dc82ca96bf65d8f231721f2daa6006d66c66b2c1a0b35cc68a9de2b998595dc30595cb6a29366cbc5711b786c0fe08c47b44fb7dd3fc8799d9ffc7185e064c0e8f57a3e79b6cb0c1a76f6970feb807cccde40480637b909daf8982d5b1c0b661f0173948bfe8981a7837736f880004a4e9f5e70831fe78dedc73ad08e08497b7bbbd84496a821675f0718937f5fa0bf2eb93c5466fba6888abe3ef242149021a5ea5fcc18b8a4be8cf065d6dba42038e394581251e70f0f5813efca2aa65674511d0637c2ca3f4ee7b46626b56620dd9bd771b46c1767256c7782a3c582f9dda2db7b358caa4fb8f593479ad77c808e3edcc61f64433b090450e0bc34212bc28eadcad8d4b8b34c9d42b071be4af01c9d831e88bd88d13c9fd56d0f97ada04e3672a8d21d464cbe916929ce401fa09ba83ee90dfebaffea9537c47ce6b1a7a0c4166b35cc10fbb5642362458416501e69ad24380a4e646e310b5256adb19fb87a054d5d25a060ba0833b77bc4d320578aacede410e8f9d85721ffde4f07085507b6f54df9ae9a8861a043a1200e803f03b472e949701e039688103038a5a525aac1dae6540aa91ed4ce9c2024dcb0555871de7ab4e4719fcdee7fb18358e52088ac23c863b0030cd00c799ea1c9c05d356152c898d80550cc9775e46afdd959a3dc12414b5ba166765a2afa86b4a0bf6f5272c51d08e7f2454fbb8213aa3aad68414f224bdcd1e0fa238ca82565a0737db268e942b8221d43f6d5ad5f751e01d3864fe74c79fe973a6dae825862c07cc550f75dbea9f1044f346c20b727c1e2e31dca662203da8878d752edbf2d30d4fc4bd001ee13fdc6c36dbfa06db4816095a5a0af45932732766b231380fdb0d84a121f2d7cb38553a2b6d9ebaed085c06a89e2603eb0cf7e73073dfeaf9100f578b6615edef6ad73157a48c635c1ca974cc0ccdfef58df188ca036bc7064209af61c971ed8c191256bf0563412f83045919d7829a689bd515f113dd76f4ed0bc231f97abecdfa358f1b2c2aa38cdf661413f4127dcd6312b36840c4b4db16035002fc9e92a5bab000d78aeddbc23ea79b04044c457266f908770d3a1190ae0be9d330cc701054c5e829810763157646421e9600c96030ae9ba1b65c6cec7a00ae260fa5592851022883dd31d7bb527512e57b7978c10752c90dd5b9ca84910c4599655f20925c01dbe73d7824b8d96cf59b0813ec0f2d17535365a4f125cd00df621e6bc3bbf50c57c7ac08c48874c0c6d5b96ab3b24f22c025377260e64b0cb6b85c55995d1692049a9fb4b85a20befa6285ba0005b6f272f7ea52488c9002c2c99f17fa41261bf5eef9e0378abde20a562d9c9ed0dc3047e947337550ea0112949780af154b550bf663372363cd9611b61c0ea6138b4e6fce02216bcee503839b440d9e199636a516ecfdf0a3bb61ae40cac3b8f965a2708fd5cce9446b09a74d671edf4d9a0f41e81da63d853cca4e15e02d54085f67731a46a6e4f6c100293285edafdba94dcd7630e06bd7f15d65b01d9e9cf6a9202cd12a96db6c830317f950ab055d9eec8caf96533802508c07a4e89ab64b73bea2c83d8628906a085682584a09356cec579d85c62b891431532fc920aaa713474df0f992c9464f0b9355e2f41a7ee0e26fd7c836012d1c6cbbe5b4cd4da0cf33e4040fc2f438b20158f89df674f9f94dec33e41860fd40d6cb6c2d5b90c6626f39edb0d53386c0006912743580a0d2ab9f6b86e0558b356778aa2f08f9043f735ea37b826ddfe2029484ca684535826a460fcc3b3ee3b698cf08c8ebc7f8e5ea773414b0895f830bccc2633a7ff3f73350d99ecda55303ae712c9942a9c1c6153e84052077e7e40b9c8f247d4c2b84a6dfc34ecd70975354802f8cfcdbd528122b210f2c4e15830bfd42c45dc9a3db6446ee242ba0208eda031fa6fa028d3b56cc37b14e2110880f16fdbf0d85a4fd50e26a0ca5eedeae8df2e88b3c7699d57b9daf18b1946f1a00061a75b9bcd55c5d04fbee6353f0e002aa24321779cc9c05e9e2a305be81e00dac9f8dfa9b1300eab133603176e4b97acaba4958fa18a1661cdf97382cf46204e5b6f454f5bdfbbad171049b065dd9b8181b06301b1dcf9fe37badeb9b238b075a36e6dfbd8b7ac95d06ae765bf49af8f6aec289ea89f67e4d790dfaba6d350956052a6d5c160caeac8a0ecc84e0958e4d9569cbb2e7bf0189b4f99651939a06d30accbe9e34c87019d24e6621fdce5fa06196b6151b584054d370c0ccf5c107edba400f99f70873468f72a31056a8be38c4562825402bc674af096bc5670306137b0470e771b7a1b3e5e7c98dd10d99d93ae87f2379abc9a485568b4827b200e992660b0aba8ba239080c46c9f81c4abffe8e9851812c9397f4cb7baced450f2752189331d2926594927b159436b12d8aaa120673c1e4719ec533757251e90b07e0964caad10de6618cd27c815535d12d900402f00f766b6c7ec4c8df8bf60db4b5fbbcf8dc387a6828e1fd6bedb3f577fafaacca60ae124e8eb8d199027a0d2fd53964fe753c0b22c02aaf67f7050330480ec675e9e7d8704cbceca66e9d0d87e6582b7ac5b8f2a72d497eeced7e441b9db7a2bec6fbdd32a8154337bfda0482b0c935ad4fe720bacfa874af01f4375441eea1d2aadb2a666d90ca4dadf20b6108bfe7d794d5fad99bbc8c96fc58c44b7a597584fff96ef9ca2a31191cf102e432f92a20e47c898fb778dba7fd5eca605d431167d1a8ec67a8a07ef061cf0a192cd7cd352d7b3238d1fe153c7ae6e14961b75dcbe9fbda1d7df7c8767f840d0ba1adca9bfbce010d94fcc84e3380ba5e91b1389e0fd0577d178f2bef9fdb0efe5fa661963ec5f75d709d829c23029fcad65d229ac575d690263b7d708fc50a129a04a8ef845885cb390c1240b4b5a9f91bb5e7bb55071297c9a1fc9261b0080ead26f8188ac0c4ebe7813ab5cfe0579940a8fc5270d06bd090ac40ba663c09e43bb9bed41831d2642ef2478dd4140123418749b2553d990106417e5ad593051b2b21c2710fc3cb572ea10401c1b3b7cecd75aa859f4f3c81ab25e318f7f8086bcd6b76f3c532a83d67c72ab6fa27d5c583e6e90add31689a97ff7020f1f406961f23167a25460e7f59f67f039b2ff65fe687bfa4833211099d37ab8907fc0cc0d6a2317fea72da832ecea55897f8cae9fcf9f53381790ed7eb0703eaffc207507d1240cbcd8614317f6cf3ee5d8ac6365da5816685a6b2913a92bdd2b99b0036ca88f66f1977e7fdf35d0becca6e21c069273f38b269a510b65d06959e720865db712649429c3dcc0344b6cae3ec645e0592d211eb213ffdebb8a15c0f8f09278c8c2b6e0df86bf0d2adecd7cbbaef29a043d0d4273f499126c3657ac397041f492383154eb1018a44b6b91731a710dde36a94c31eed10d1f2271293e2ca05a58d0b258cf7663baa635001224517fe0b2181961d0d988c3c352db63d4e1a0f1f22dca686baebd32ad587a6626e366f198780f03bcb53a59e879268376e6d05d50468edf239b6052c62fcce17fc6fa7dfca201e09423b332d82db8dd199540e8bb6c8aa9f0be4358beffcf6cd87b0e7ef1a33afcd617e5d1b94cdff69834c0695be85136b5d49c063ac2b09b06abf03a45698e0125ab938cc2448f1e1118d028201d602f8294ea1a94682fcc8e86d9a20e908fd75226c7d3ca761c78f9c90068b16e334b0530ef5fc980c8ce52fd8126f1964f9fb40a3cc1f84aded8080740573cb4d3fec577be13253d7e790de0c57190774244348d28bce8bb5f395f10d0507a2189ed7c500d5b8fd26a16a5ddc5f06678df168a5549f5b3be5898f24a50ed94e8c841aca1fb02f2dbaa74d07518f170dfa67a1c364d3e99d193ce2ae120bbdc028442a3d83d92813fc8364ad96216ce029474aa35e1b4f47744b7af9af06eecd6547628b2737906c10de34cdd61de2b3f30dc1c6179bd4dd6dbc97c1660f156b932010ba9be9606ed98d63fbf97461799fd1771208645bc384b992b0ce0378e9ecd4d6fad69fdd36d1aa789b60917b46bfbe95fa388d25657ab912717c0d9caabf5a2010c0e0d31048e89fdb72c02556922a04bfe49d568d360985da360576e4f78bf949ad04f13df9fb3060437d7c39f12aa7e373960ac89511eb49470ef5d9dba36cac787ac18ad82d6ef17e2342661122d93887078f054fd0d1ec580a7efad90580186b319445d40695985d9ceba28f6d2cf24ca84caec98ef7a08a0d3f853f7f8409bb2b75f573266df603e76c3bce920245d1a48d7ef3d103136f0b3001e074374100ed373ddc5a32b52fb9cebe98cc173fb3c26b9bcaa9e1df880e529b1b337599d9243ec0f1ec8dce857cba0d8d4c9ae0b0a2be67d57a948114037fd6b02b7c1cd4a7f48bb58158e959efe95608f06ba39ac5c26b81dac2799108e907f0ad19176d2b92ae91b6bb765ee25edbca1ed61b722350fd394cc518040b8af75d139ce28cc739076e52f6045ca8b6d956e5d054fdcf443eb747bdec6d039f36063f87d29e3b29c593d5072b63d5677874ea22b5127c7c4495a85060550eeec8563ce78a8182a190e23a2d17f1147b29a87464a0ece325629b0de7c8b60db316c071da14a4f261391b6887974ce0395f9a5507cbcedc8145197b21053c0b70b006aec59af5ce8a7f67a8e6290987a345287fe1c095755c69172a899a0e08c10fcde691aeb320caec3dfeb291bc3d893a77a6a59b1df837f71dbad054630ac6d5a3b7f8da3200bed089078d78a29db62e66325d737f0076c0aa724bec250610b4eed9f6152be083d56fc480b5d10166ec6a769e692b0cd0bd1ced906368025fd4bd1c4f24c8cc6d2d7668bd5e33e858b71318c7d9d78596e753d536c9060619f2d34b3237a75eafca86b9de47d830063a981159cf8d9ddc927a7b7a21fe0a68d25467b6e3dbd08c7923e8b431e1149c09237ff983656a8dcf78926e214e00ddb47f6cde71490ddc58b36db5b0bc523d7bee0ae125bb614195ccf652359102eba584507a4321eba6fcfeb88da46a100cb6b39235e7ea4f1f4b6c4b7738cc0356eaaf013735b2fd615cf3c741790e9132617c4a5f01e7359705bc875489000a5e39248f8b8923f8799e90ca6cc942b94bbd83c43941d0b10ede9b6ce0c97700ce736a399acaba6e86bc2efb5bae695bab4f63d5bd55af52360bb02c4a10fc0564047a8cffb4e4b9b3c939d7331b4f17ca5071b24851c1bcd42ada4785b52e0a1ae3954fb75862cf31db7df00265c0bc85426c21e9f5996a789d45f56e35c304fe5063a297ac690bb1d529e6fd4ad9caca5df5ed74b1a82ccc7655c6c5cf1305ef906cf807693044a7fa18a2f432286693d40577757dafbe9665f8b5fa69200774c2492237e705316331f5a624d3400e4e7971dda2c5d4cf9f112f67753bc90cf86f09d26b1a9ece4fee6c6005e1b6d9be8efd92c43824f470db90e94763af0cc8aeaa0601a4f4b7f286940235bbb5eecb60796dfafe1f58a53f566ed4c1df04bff2e17e3dd053a89325943da3685cc01cd0671b81d7890ade28ee591e98240c3c97bc5ddcf320be785b8f3e8f5bbfba6bfaafb65b5f4973f7ac763b80f1c80d76752408a19982d742492daf4dfa1079323309a405685c25c8bf0db264e72a02dbbf683f785744fb9d49e3a0b0f3bc4abd79e8da34788c7c4c9a0d75cd09ad0b308c3f9e869924a1ea21b4e0404d322ec6a0eabaa4654da8d1d72375ba34010c0cf4b230625c002cd48db7258ae08898c00de27a6db101e08995e608a6b7830d5b1e0d60f8089cf0c6056a0ec6d53d56461b681b0af3b0073a6771380b92710a7defd12fbf2a821b13985988458af7e6848b21af018ee3003345afe072bf0a0c685b4bda91a0bfaca848b854ee91e22f107ab30e9af064f0326c92bdc8b1c1025cca3bcb692866578f69f498c130710274d64da47d0320fa8bb8088a15951f0ca0a46abece0a151ef2996c3f359f9e781151e60820c9215d45bd29bcfc188402ce1f960541d4967d04f3fefaf06a0fadd21d8622a36896daed78cde05ca9a10ed44dcc73301c8410bd83ee68add1407f268c44d501aba7039e03b12673245306ef7bfb910d3f7c53bbfee36fd8850964ff419db6b144d99b740638640468de0383a1e8b75d666f943b5f640a5ae0443442a76ae0daff5ab06dff374577edd00469382c476ad38c8248966f702284ff6357a320c17ab5147dd654ded11eb2020f51aaaa2afcb5be03a90fa799e53d94d2768b3dad2bfe3d3d020895dd08869903d78e554bea74e08ac15f095d189b72b2467b365c76ddfcc8ac84855902b5fb050837eb69cc78ba404b020425aabe290d683d1adaa1fbabadca63a44f69bea90e42f4f9f7809f1aa1ddd76906f9f1da1e9a21dd5caab197ff0c048cca1aec290db5b1f1fd3cb20ef4bff26b417de9a754352af14ab44ffcfa4b8541c4beac6e089ea0beedbec2fdd8115ef3b9adf9388e35cdf597196d86bc0679fab663b7c00728ab5373b5b822215c6a8ab23249c9e863b7c72ec29c1b1cf9fc964c5f2ce605b65062a1d546660eb74455a4584959cd66eac8ddd39af96d273f1f21b0a6c808cb9a5c0b9482b248fa98a04f546cf0f5e177125d137ae4b223ed40531fe13d0f92ca59019aba062f4ecb5fdd85afcf0231fa09ec5bad0db69c9630362f2f610ed62b4f5ab6680ad21dc22e9b6484f9235906059e4497f7d3c72d1541af407008e1c19c752b4a14f531574c851e4002fa20bbb57405ff19fbe48edcfb2eea6d08a6169e3ed737fc2739d15d99fd722bb76f6ccd49a6de5d0a2cb8833f5288820898a7358f4165f5fae0372f4c857e6cffe477e2bfd42f54848e9196f18dc9fe0c6dd1480014dd1cfd1edc84b2c7ab3df7d7b77e66d8346ed9f1d701ea8b66350ff919a35ec6bed8324124a21bb567369f979deee7984ad81a9d4abb931d1c590c44e058e154508a6f7e5d8bc2d47657c0863f8f00faba53fac0f328dbe6764904f7171500dc91630d7691408f36af7a36c521a39ae7db3e9ededbb9eccffa9f0201a6d22698036497a9bd9af50a2907dbb9fb5b74d9bbd3676d2f74f06bb6500bfbf9b3113b9a1a660813d7f4ab733d6270dd78ba376c4fb81281386c1616d3011ee894d15f41b0df104ab39f9e081e8a66428f9fcdab1b747db91b18353c6d0c7ad4c4be69155d31afea6b5ffa1d5c07544722602546ab26f357eb501f5f8e0293cd216f24a61e600d29a8447aaa426c3cbb8e23cff63b72c95cebe1601dfa055d81e57dfcf7ee3336565992e8c9007ee5adb57dfe6fa14791c68d8539144b06369dfeb7fefee359122e8640668cf3781f13c32f1e2818c9b88954f4a3e0df0f5cd9a1c909e2e15c52673808680a917bf9a8eb6c5ae4fec927661c1d2f63080b4fe29b8e1cd3c872edecc42229a8429b512844c3eb2a2293f1bebc87900a9b0c43cb43e52b1b94e1d2ad05770c89d56c7253b596bd6fa9fd678fe372c4b38a09a530040ed0cb575afec3ec1312eb467af7e7f12638f5355aa41f2f5490346d0db5615192b833523409103857a662cd4fb09549a927ace386e4b56829d937410b041ea1a43f2bb80a374233caf745b5bc4728bab3410d6ea705afcf04eb121d0249dfe90e1b335f3e94df0d279fd8e9ea05918e9e47c06e057ec5d2abaf6b1a0355b7718580bf466c2f1638f9f1ae6180d3de3158dca3f0a3d1db2a9238d9700bb961ddb1ca3c094279a872e0b1dbb54864b56a3bc644c6d1a73def1c966236023babba52a55dc7fe4e20a7bd2f02734ae38c6d63074327406eb0c2732b6ac900145d11743d7c53c466dca4fb94f539d917375f9faa8627a88cf9e77d8a51e90d33da02857e8da7f02394c3a12c6056fab7f1a1ed17b26b3f08f47cef6b58e60bf5c2942e27fdeeb2aad46837efc1042dcf14c39c93156395bdd9c4028f26ed05baa074e27ae32471222d38455de395c75af840f30085db30f330bdd4b8b1960ad257c1847a5a1a184a2f3afcd3e501945516c030a8b16d37d9391cfc550af80208c7d821db20b25a5deb2a430ddffb80a4ea65a63a738d01c363beb18769760949863e1271c39d812e1067384c50a2fd66eb6491694a81045746bffb1615cb01c77761e32963cf54330714d55524a498832f338065f8a6421b9722cf05e1270a33ccf7c41f9736599e776ac8e7e9a4d38ce96061f6746cfaf65b79c2003f250528e2a6c54c3aeeab40ce7177e1d398697c192c3774c9ec8e3593f6bee067130c4a366e51e325360fdf41709f5b6e75a26ecd4349409871af244f9f208b89680f7bf91af4becf97ec1c1a049e5e0bccc380007b4679986dc232b62b85ecd6f105c71450705fa92e032f1ac7b49f27e1be078dc039f5e678fe2731195a20f27307e4303b096fe9772d397c20fc3f19c1cd148609701ad90a6c567bf3bdc507a1099f7cc427cbe5f6f16c9bbbda10afa45c1a37dc668918f88928ff07ddede85007b6e45d36ab4c78f27b29a84274d0d52e2202427b5863fdd958c723c51057f20288b0b8a93cf95cf10841197cec5db2f2491535e72026325c23dfd14f6ed8ef0387a12ba95c642a3d198d8ebbbf0eb1de63d4b481d721de518769d0eb79d90809b4844ba44c3317e623018efdeae26e261d0c9e739a5c266a0e8f4eab15593100d577e005c6095f0ba18b2da8d5617d2853bf9f2afed1cf9d5a4481eab86e940de066f2ed877ca8938eb88804de011c14d48d740d47efd1907120d01179a362026f7bf35a3bdb64aed04d2ac4779a0faba8e9f705c5200135fc1ed93924db780295b3a2d9ba83ff76ede97eecef7c8df3e2d2b6808508628dd07b02e3d95cab0fa694555f36c424d754a16e0e4c6b9418e6a4a1bf37848b3ebbc2586397faee09a1b7aff4fe9567009a24209f21079c2b28d96c3a11cf47e45c29b7d98b01780a947ad2e2d43a2fd2b1b8d4e6672af2565864074a06c5580152da38cbf13ce50183239eafda1e75efc406b1830b3daca498e1ee2b9d4a0b298c2f1fadec1f08099a45a7f6637f40f413842f9c7e97d0ae25c59ae9e67e7330d1b4732e01072503f2ee1fef93160b5f3555f5c79c87e9407235198dcdbb378eae108c3e778728012b74cdbd81178ec8ca4dcc907729da68aa45bd89021760143eb74a2303afdb0d85228fda3f71d7317d801e3c36d381de8a5449c58e91c8e74b4a40f8dbc34f0932e36e529bfca9dc7d900b68fdddc8ebca8540ba98af4606a54dfd63442b900760bb60f37d4502e20b777195aa3b9ac6367fcbd24517d68c635067ee72c7a004222ee374cb9cce3dfae04908c9a831938587d62afc3fc3bedd7ebd4d1985900b2ee7742c9759db20e5103a408003251ba743b32a609b6e4d26c4c3539329b90837aac13d5882d3f1cddb23ff159da0324b5bba80584955e25cf74743cf0358029e5173c4abf7fcb45b3f3474ad5fae005277830bb350cc2ac3ff23022f9db500761a4d7c865dd3879e70f9b95c30d33dd3f5eae15c6438ffea0565c042b61403238839bbbad45acac67c7b2ff31639c8fd940ac5f82e2f924130671035412503ba250c182f0757b2f03ef3c563de668f216b3b47e5ad0b338f0734351ccf9e019326de888ddd9e40ae7a9cf16be264a44dbeb550611d638503dbfd367c6dce000835fa7b54ff314f6c7d5ea3c86dbb01f8e600bef4d212c915e39ff84bd46402ed8f11207a4dcaedac8302a3398fe5c49ecafb8f16d69aa87074694b7ffd7004d0a0b05e0fba91ba7eab1dca90b2e504d4c3716b92a3a5792344eeaf7c67800f88ef4bdc37ec0b8bd89f13f8a0d823f2a9eeea9857bf5a58e5314d7dbf1e3e08676c80988456341c0475358d4d0606ad1d65cb5ef340367d9ee055788873500eb5662cb7c47ac9fabfe1c28d71f1a8daf2390d71959db7fd31adbc6b1c6b2606b6d6114d7ff9f33dd8734f1860276d98d43bfac59171a1a4328efe5724f12106a104982e892c3b6859673a1aa0717a51f8932122f1623f2aa8cf3af9e8ad3d0bbfcf2169589266f138171dc5fd4006f8d26789b89e77b8311452c0d08a994804b0a956e2e97203ff29339ea53ce907e198add326438e8734f0dd5fc23ddb1c03d292e4cd9d0c82350091a198ec34ac08e6a290d1a5a01530b79ecb2095b99f05fab30c12837c0ef97938c7b9c9f6c83495a6fa452ad45eb308baf37c11558303607ddf4471169bd91b3687c71dab024ecb88d48176932c2e840928e2f2017b0771e36b16f1a90a6e647a89143d1305c5d38cdbb9698db8ebfc3424ae0c30310b508af8d56189eb156493e63c4384cdb6a8a1ec78043d517c43b8d06c32c5ff0220d5a2c6497864126247723eea92066d1ebc3b2e0a1ea9e4e2af09a0a82ae30d407f6117a8c102ef670b616d068dcf557c66b04c140377ec3ba5a9d1cd11230f3d8e6bf14eef37e3d2adc17e3e8ee8c3ebd374f6f3c64fe6d90dfb2c7eccba06a2177eeac7d16641ab8bee79eb9d506f203f643e348c2beb3ce4943d28bf4a0c4e9a67536e8ce38ecfe6a76a24d72f9ff5a7a7bb9dedc8d70caabbda5f16ef04e792b848696797201fa1cde6519d9eae1ad056911566c62b40bafc0e50c0020ec0c8d282593c702f2d09aaa782a341034194906ef0529e48a31c25a024c49e0c96ce4fadc96bc72e08202636ca3de7cc9eadcccb1e37c3f9f6b19b2df3cb8608e4c9ac778cbfc638983a79973f17168421450890221a725927812146afe57e0f346e84a38f3634549088048dcd645389fc3802e3386cfb5b3034cdf9904e1f0e567bd2df52d7ba5580dc6b99a738f3d64979055e4f13915ab582908c12a9e60a1a60f042bca9518c9c804bab036e5b45993a1bf1cf8ce5f03551892ab398460d437e72c8f301bcd9e9a61159b6a5e0bafa7d46c6c908c23a3902fc1eb05cc103ad80b7306fd55d753631589887742ee5611a147ba5b4e01a671102e12c895309097e045a6f80d7fc456d501e6d85d6e8b0efc4a31f03346d844467a6f042e30bab627e91cfca7aba0f2adefff8916f4608f16c28d9b8448d422041129832cd01c163bb381f696b0504e1d5a895443da2a606521b443934c44e64d07eb33eb00a1fe0d0fb1f79291639fe4bfe4101eceeaaf4f32e6a241256a8f843c97c14ba07ad64446868655f4fb78873f81e388c04286277f9ab1c620966b4ff2cdc7acd0ed8ed072e45b7fd48aef7f7569aa8311a19721b2793db09cd4799afaca643bd05da67356950a26836259c009420d81518a54f7debd7b1bf1570e14f11473c6a0bab9495700c5a4aa68eae805c57482a990e9657aa169d1973b9140b54632d2c0ef4d08934c09dd9a2604435a165ed47a27b9422951d0fac8cf9691ed3643f460b82838e8e13b3c04b631474c59fba205477755d74f2b8964cd98036375c835806e1fa096b6cf3439d46074891329cbe5ddd1a8748448873ea1e705b3ee720b50613011f3ff0d7bdb41ac162db156e8ae50f991dd10c9cab055d6aeb5eba58830730c767e0574887d79e58df086e2fed39b9538a8b8128bf57df57a78c6b971200f6a776b5be33ac2bc9242b4a65d4f5fa249e2b788ed808990cf010d42459ea06038df864091398619b2600d79aafd88aa3e99f40bbb1cce8ba2b305c50680e0d5ceede4cb5fea7c327ffcc7a9a6971bb6242831a2281b401b17933318cf5fc0ce4085772d797bd0d5c67b0db3d436aadbcfe4341908ba1b663a3b7b3c0a16c0665f49a03679da9909d84732a7b481af771fb1f3a172425b189e58ffd898a1f004c277bbeea7b0886f13bed84cf27a497367be6d4406e7939e3ab878d6cc96600d520ba28362821c7e61161ab8bbb90f770d0d4a08be8b8bab1dd68219201600c7315a3bee80212e24b16d8ca0ec6472de07726ee5796947dc0987186ff4b910751cdddfe4bf97da4767ba5cc098c606c7553c2928ae06d8e7fc714b3dc90b2009d1579e5253688b050fd10e0643f8e21b4390c0b7dc94a75ad95d727f417ae02030fabf2b30d59bd0f2c3bd1debf741d6287e19a989e6ccfb7d677c31d7d2e0c6d49703f63aac66123dd81ebba7bfa2558c46fe3cea4f97920f7079ff43ec30a3a8985904607379f864d700668e0855000d03e95324eebed56753d9d575e7006eb15fbbb4369263916869299b96a665684b99b43fd18d85b0e1642018c00e90be38a4b08893bc4edad8145701a23c922586ae398012fb4dde8d1176d1043050ad25f5784763cd210e58303641b9d42bb988ed9e738a228781da76105afb0ea0be9f31104bc65f28847d0fb25db9171a5f24c540e7b5aa0a737d5a9d7468a5400e87af021e9577004a8240a65287823cc13d327d603bf6658ebf66c8b6bcfb70f3cdcb4caab4d515bba51bd982670fd81a510a17a8c795ccd9c75890a785f83091b07cbf70d1a7c42f4d914fdeecb81004a41ac79f737aa7917277593f510eb077c477963bcb9ed7e345225ab6f08ff7d96b48543e48f6e0e5c6dd700fb173a0e5cb13bb26b35d0f01f02b1665eefb50d684e6957e4bbd9bc7c49d05452e8e9083270b4311bad39b944309d06a457d360f8bcea6a12e3793b56487e83679a8505be1a1e01baedf7229f1a387bef9fcef9ea88f3383047d8259430108d0df2f20737117f584c047ce37477d0c6427f18e353d06401e43df5ec45daf6b9e405d40d5f087ce513d30f284fbd47cb2bd24d853396155941c1ea0484a68d651b5039047ca0723348881f2c7ed69dc869cea6ae34b5c8e78176211676e1de6e2978e00248d8b06414b569ef0d2506f4c1cb672197a63c2852f51d20681516f512c9bb0c7bdaab6205815208f8cd654717c12d0f42c754f3354a64c0d27e2769e2532203de2153cbdf65a00c510e88817c8bd36c659958335c8275df2b64a0ed531d490e91ffc6b6c727b45455fe18d5816cd0b525e32acd4b74fe4f3d3437cd0e543b03ea214806e57569773b395f1de784b9760d721a1fb01b6621e59239bbf65e660cfc3bd18b943e13d8f3141508c1593d7c4154f4c03d139fe5e437a94ed715d50e54861407d588c70710ec243319f70844333810db88d39f846233a913c56a4f01e3165268c3c2381a7f4387eed231dbbd104dd88e6c7d12beb04fe9b2151dc807cde1efaf306047b51c1cba7d4e2aca517f63fb76947678e597512cfa963861024ac5290b035f8da2318e63716f883e253c8d69c6ae2fe9b42be538185dfeb90389925501b11044f779bd89a28fc078faff70cc5a2556730f57451d4f938dd10c761bf3f74b54d16a7986dde2cf74d8735a4e4d10d0fcbbc34b3b11f3221f2f0341842fccbc5317c243a7c7e71d04fb9fdbf77d62a4124c9edb387bcfda2e2207f7c1b80590ed0dc17f5c99213e62fe333046e53a5f199804bb113ce591116a0da14138759d1be9d78d966b7b7b76cd8e0bf846a90e929d63d98ed6251be5e60467e09023caf72f15b5907a87eb7a2c1767a9c5b6b4875654c913cfd9b414f60c61b042042705a0fb4864af34cb3de603246d193a23bcd28685ca36e7df7dfc0c879640e4196ba8090693e0799dd4e7e744bcac839b5c0f43a376039709016d03ec5d2e4a800606228b0d3334b7f2af41c383f2f2744461fed225332e76f7e80afeac8486c6ecfa8fec61fc9413c0b46ba83a8e757f3c7af9fffb980324f194013425cb1b0ab66ee3d4ea97acf13dc7e30c28e99b87aa0c1813e3f34a31e9790198702e32d1fef764baa5815a43f8ad47305d01fd5546ea965edf1fa2eb07940081e383b0d8f51d7c92c5b40fd9280049d802667c6b6ca54db5eb0788d06119016a5a0ff93c3606ff7c8ecbdd5ab2491895641283dd384375b535dcce6ae1670372e01c878f54519c394998e89c7dc05211c954cb1622d5a69ed2250fef7de0063da98aa80bd252c2eef2de3e69b0c7a491769b92dacdfaee6debb70425e71e0bd0e9247bd963f2f0da2c4e89b2db8271e6c7d887fa11b8ca1fab0dc7d0ef8508222e223daad8c6c994506c17d358880f5e659d2aea50ec0a2f6edfad3fc891043dbcd783595dd8c2f6bf4cdbb8785a9f23a02a57e398a8b476507082d36bfa0e4fd218026ef7fb57191f47e204212f0b7678fd7d2fc6262517d74dfa482ac802739d140871c445ab7ecd2e586b10ab534a1637deb06be35d2c4cc1037128b700e16bed7f18385f34762c706a05761698714d07a06ac144b6c33bd9a1b57ccd031a9d5979a9a0c6338636add6fd4938e1c9862f4efa99c57165986171e3c768076c52591db5adcb2116e3a7ec40704674dc55eb8bb425024f451165aca11ee3094cd39d0f46e81ce7cb78085bd0f7209acbbcf098877bb77a891bb82e93139207057a2c8280da90a19edc35aad99ffe53a8537d3fcbd688906b94f0aec0aac6020fbe00766aa389917604db0d2fab72d7a1dd226dc40e2c8ea028be4be9fbad056b1e5991814332c37b59e8f9803db4e5e112ae8eac808b144f8647b4aaf806046dcc70f6a063dd9b4c353364c3007ff4fe4ae201e8a09a44a71dc9be05d5440d56da880d131df7c07182c6d0f4771ef2185cc3c1117e6e27c8b8c028a7d2a200f67a54c09cbe831a11f10656eb29abe70865a58ff5b16547b66e43d9eba5530187dd8fa176cd5da88f48410e17cd29aa0369075e85cbf4e18b81becce75f02064c10cc6559c56b7535121bb90ed2165e721d9449bbfd9e998b1565eddbd78503bfac7e816e1086d6aa5d945edc47aa2dbc3a05389aa5ef71eb572a9bff765e0fa99eb8a787e56cd76990a887f0797230595f9b7722cf846f666540a7b6cf4e062fd4d13c92ceb2734bbb3f9e6a8f96f0d8310ad06af7cefaf1a6b8fa1d207c01265e5b2c50119661793b1cc203cd1fdd7d4257dc6535573460769e70da9d880e44ed4be3bdf9a8f31cd903d25b8ee91f603746c7b7a6f79d5b9ab39e392dc70ed5a462582399d4598704f2447d55f2e5678a181383b880f8917f81ea95e24b0a5b793d39fbc49da3eb1376184f89b5d48e3f4902310af5a17c3e997298febc0f172471a562aa4698e9a9e235f76c8fc625ee7bc32a6bb1bd3147057936acb00caec23707682363d8b9ea42b296a753e1c60a680e9f322fdd43119f289ee6fd0f442b239a2a8f5573315386f4e3a3091fc828db38ed8fb2fa7cdd9b52c2d7c40091304233de71dafa3008834f9280024d64e71f1caf8ab56959584f0a4ca86f062fcb56937b8c363dc0029dd5640bb7edc956e5713cb160f16c470de501af3101b06cc4bbba50ae95ea9fbfc2307b773dd17f1784cc9666b6e12d0d76e4866a09a5e711a12649993fa2acfec8c87ae7848ddc7c19e0202760232cea03b2a3660be2442c50595a3f6e640fd6baa44aeeab598e23890ce9aeff723045a439e7aa056eff3f136a7fb6c1d8f668cfbc8381aec6e502888cebd59e86be130f70ceaf0a58959ead321db8787c00976d8a3ac14b8645bef87236abeb9f310c0757996702e99bcc212cc504c1a822615356dd59b7eaa2f2006a67a0e2784bd57bf13ed90711f977c7d878d8ea2cffa7fcfa0802857873c9f06c3f549c33ebddbba0d75307da9fd4af0ad22c9affbd884d3d951fe7ea0231edc769d4a31a2ca2ecb4d83d00b660769c4416a6a442df1b6b4337a00b26161a08a159d38a988b6bbf409cbe0a276ab86eefe55531b030f88636a1da4a49b367c8625223a021b34234cae15a032088ede9e297b984dd15dd9cf867852d13455c92e7acb814421c1d58b9ac6f05b8483a37ad1f03bfb44c8edbee017793791dbe4e7a8eee99cd9037c71444e80b3ef718d6e1c8700b7e4febf9af0802e995b9193c90c482eebc5e648223b70707e3937b32605b460958b1606050f39fc8ba8d509e2ec1bdb44fb8abd9e086d3077e284e8401d8b841f59b750edcd097ed6813eef543762b4fe8d57c837e73c008a61f7b1b8d79d6ba12b1149d62b04ba74e26555809f2175b0239a547fd1cb00d803a1fc30dc63a5a081787ee466e21e5548aed50ed088db550f9807ecb51d508c91edbb5a03bb916fe4ded89a4494eac0c4827a3d0f9e6d83c701b7c024feb02eccc294ab17d746af62284a19bbcd5246c334dc42a47859463b1d81f5a4c5b09ddef8a085401deaf6a92bd0abfcd7d5917494a7393166153dde6402404d4b00dab3c9b2f8f49ab31d44e9ac2a30842843c40390d92d0d1867e0110803b0e490c7cf611e6c1a940fa9b751f52f421b6be69da012d0776fd6fa0389b764dd63b0f46925671ff31c16487617c103731ad8f4e9200f8ac3d2846d96838d07605ee0a61927185da7faeeaf41e94965e85b1b61826fe781035011014bdf4d57f6aac0e5db1d12c6eeefe5b8a1146d4f3f2722384637286c8397d8da307e60986add30899b97c94311a3011bcedcc8a4d9808b3b7e01e24e734976b6810f2bdeaae110539a9b3765f8711d25908266824982f2696008ba5eabb13db1962c0ebcb6a2d0128771967353c97a8f3d2f3b5eb83506a66bcba62e1aecc51d701e502cdfe62060cb7895e73d9538e4223bdfd5f9b84744751b49d22150175b434e649884a550b1a583b02280ae89a3debb6392499dbb7afa07e9426f93d729cb30546168ef00ff4300e49476fd32411f4a9ab225be8a0e8981e917ea0c66b29b219b68ec98504b4bfa74e6f4a079df903fc696deddab0554f84294ebf615e0e2896fa0410e6085def5bf4560d9f722ad68ede3d7d918af7f83b4e470b2400196cd907ce0a05021f44c4fa89cc57f9c879ad72f64c0a29294d89573278ebed4cdb8540e1b1930eb292c28a9351932f919d1822cfd5f9925a27601a805c62004a6d07c305cf2904581754da372bb1eb7a4e098fd72b164c94bc77df17dd129e037c195e8a45350bf548dfd637b2a441e3d13aa1bdd73931fc76063733498c12441389208a31320e0f03e1ab2bdd4c4264916a562d5567c2a7cca1896ef63685bdf2dc2cd493e80192f688999c983d4966eb2e4a79c12544aaeade2b68193ac1008e16207cd63907babdf19724c32dff882558a3d88a31e92746bde83051fa5ed97cd744ebcf4e0770fb70ed4f7f1876660e145cb04709636a36a47adf0724582c43dccc08f1cc0c25a5d6b3463c120eb9a26c0c1b38524f39c57efb00a41fa0e4cfdcf299996803d414b588ff20e9544a8bf6a3e192035ed080d8b6996a1036bc97516589816a09be4ce86af24e3fbad7b8e36092461a4769bedda2f052e24dab1a6fd21740b90f7442f46ea9f845705f1a6230c67cc476c386c4cc705b7df09f2e2852972396064f68bb7a89b7fb511c637d92bbb418809c548744b33e4a48eca2c436c0b4250c24432cdcb38a5bd8e9f93de511584b25d4e0089905fc7df7b2b8bc26a6dc830a6a086c3c008c24360947f56c95694002da7c19214a3343efd3bfc816a244c608ada515200e21642c0bde287ea3561fce1b550a5b07f8975bceb05d1eaf9335093bf549ce7d8db9133f415b4e57200b43fe72822fef269ccb514a27e86af55d0ce171418f0fbfe9a7cbd1c30e8f7a9a995314c0a3016b91cd541ecce7667ac20b403ed72ee1708826aee2a6fc5747b73d839a72af63e01c3e8beecdab49e37f0389d6fa5566c5ad7f2b7204ed06409c4fc7bd9891eb4a9b2762ccb6186f673806 +generate_ring_signature 1013e6d2970665fae054a3bd9fe5e1ed9e80e081cdb6ca240c0f517f1b415cab 0af5247217901d271584c54159546c10a1d682cd243e66a945af30b5eb427cd2 2 8eb838cd7d2596dde1caac7f8c29e081333098096355a3888f6cef9ad54b4c43 ecb9686dedf61fc294b3c96c56ea43d6e57500ca683d8ce93e4212bce07e71a3 f36383ca3516a2e053540ebabe435e3b57cebf3890478f221c4aeb0b8bd4e30d 1 5b7149a343029a5677998b993842c24f99086c80826de1636e895cb4ea529408af6de27393b35d724106ad6df6f9d55da7dc553931b2a8cd0c664676bd7cd80ec51041510e4095a24c2c9651738c6596a3c8bf252f9f58520ea1f7aae38a510bd1406d4600f246f92a17e5b2e1da53eb399163ff81e30f9dc9b10f4661ecc102 +generate_ring_signature 0b5ec78693ba8b68d4c52d569772840dda48ff5fb748ffa833e5392392c55dd4 be1f92e110606c4de9745e00fcc25187beeb7caef967abd9c355cc032245326c 2 3957312d5af6b690f318ccacc696d12ae70a51a5e59169a823de76c16418908f 4c4a89e48bcc7beb356d78e9d9455a67c53f4624c53254135d0f665562c388f3 6360f4e3a558da8015355552ef2b95fe491529953bb454449caf53a3169f6f0c 0 a14287ad1943b58ab4aa151d1784b4e5aa068821bcf8bbf3e60b26fc565fbf0af2031e84a70f31b6ec95e3e1c6bc29328ac67595c854671f0b23826cc2293002379580cf977d1e52d2ca1b61daf24a5ce80f656dfa089916a902a9da8c279b07acb5cd051626bcbee618968356d8d68730becaa709e193313be3d4b109633704 +generate_ring_signature a8ae168867259e89dfddb607d8121f78d9bd1afe3b56bdf7f8d44f328c407626 29c15d05cf7408ed0252e1d25c47b8fa0c34878017937a285dc037fe48c3d9dd 4 38aafb4066b703586c33d7c055353712cea2a8fc42bb1af683af4d52194ae229 e258caa68a2c25529b6d845e6477ddd241646d89135d3e860f9138582b0e2156 fe25ca5baae2ae89f703b0d17d0e9ab8c314eb69ee1b5ad3ccc2f178f7a13a89 ff91b9cb17606444470257f1b2520fd7c7eee2e9f62fa284d96c423fbb32c32e ff3f205bd350797a39e072c715c24902a909dab05fa8b7e3338d2ef69a85fa00 0 41dfe76c5ece99b90bc9d4ab80081cb9065a7e9d6b6cd9e49f9a0c804a64dc0feabffa5392a631638e178a175d87b95342c30023f05da8ccbbe407b686447208f2d62fa18b087e201f02a1ea21751767117fc383e8e77f449499e4b7e7645b0d25592977876e1d0da311a6b2a7912ead066f090a8aacfb1449ddcaaca3286502b951e1bdb5fd1035fd825cbf397b558b665a2f5ac91c03898845e70c91e9d202895f7bb57a6cd561752f8f5ebd03fd1ca406be3ee19fe6d0d09755b7ed69ae08fd1866ef43621706bd2003c795702bb5151772b7bd3543cac064d3b65acca10cfd19210f992ae64c6389eed1c3905af1de4eed95e257ee8cdb2eb19d6f76dc06 +generate_ring_signature 6f7c9d02e53d619372700dcfbde7105963e017372fbe2918ff0331191453ff60 11bbdfd15b4d972900e7d088fe7971daa6acc8e8d97e2414e8f44fa0acce27f7 217 a9d2c0e8a6674d829f4d758c0dfdc0adaa073d9ffa6456c2d06cb7ed9c7a7359 ef1c7c48c75f1ba091bff82de93d8ff68f7bc31da2ce87c8569e7443d359c5f4 044e3b84c6acf627a821ab990f7085c93abd290e5930767d6a177982d6ca67e9 781b4c9fb9e32d73d2d1b963ce415c90cb1844416f7dd13280e1146ba1fc4e5e 05d8c1d8841d0e5eff67f665ef58e5aedd799e1b9949443cc414932ef536fd46 7cf07260320c63fdaca80f7b8ff9eb61bb4f9a9d3a27cbf0c93834cf93fc259d cbb0202ac483c7c3b838c1fd4ca2f1bb47e91b0fbe6bd10b1e45da586d6ef64c 25ddec4e59d2ee1da3a0736e7aaba1969c8c4baeb25b45f7414ecc3a39f94e98 0dcc6715cb35ec3f1d7808ff671b2433657e4384ad77c6e24f3fb40ce407ff9b 207324c43b3cb524a6cf244595d44d9a9fb6bc5dee499308b95ae8a226feaae9 32e51b3211ffb05469bfaf5366e9818fad3265923f4fb734acfdebee7c183342 f3745f3cc4453c17488dec37617c06e24c7d05bcfb9449c21e93c310aefa4a44 55cca614e77dcc4ba78cec6c46f38d3d0172ad2da45c4693733b845449b7211a d01bdb20dda9148e2ae0b71cce5ea5914848f79e3072f0b248ec7950fb0cb32d a7b51154c60f197d723af16e4e0f324a901b4b26393a71669d0e5a621b6a4c26 038d389b297f53b1d35e64b92e579422c93528a82caa3742747cf55212a7d9f8 9255e412cd98623b34f764d2a55c443d3675ddc1d67e70af7d3903cfa80ea18b 0dfe3c5dcedd604a467aa13cc1ae316c1f077eabb02803dc06de657126484f3b 5ed0e9819d6e2d0d113f1edd978e543056ef6e4bc431f95f2076d0d2fb475d2b 18ea161052684b291829d5246f410d2a10844b60733a978571e2aac29b8498f2 7e2e83d2284a89f30052c7d3607e88bf658f9fbb1bfedebc419fe3aafcc484ac d7d0b95c8140094761ed4460afb84bac314b952bae09bd0031961209a56791e6 76492fbe42a3e2e00f6b98c207bd06dc8a2b9211bd27154f2938fa0e81d54589 dd2d108646f82c50e2e1a84de94cb2bf50d660d604182dd87ddf620ed3751c7e 43b77c3b8b7b4f57657194ea5c26858d5d60849b5c2fde7b9e2db9b2593959af 68d833ef2995aa4b3ecd512361d4f99c912d20e152976be8471b18e5f82d7a5a 80ea13958354db6fb72d24ca091ccc979e37e8f5e866097a199f726a15ed0df7 568bbc6de62162897808b97c17cdcc6bc1986534dd5f3e48a8c39a0c2a5a345a d052a818e54496d2db412bf4ee41ed0152510bc19a0863a59def191336be7393 e9202d3178b1ba5cd5de88ae45610a543418e844e9b99b443292218dc1abf21c cdb6149924a134413884a183d5d5e4f88d37a7c7a85897f5d7349ffc66571910 64066a9b8f720038d5094cea5c59a4d87f09a121bbded687e2913561b7462d3f fb6d221ac1fcf65a9d188165549d595bff987464ff5e99ad035deb282d42dc93 a4ee1970e64f9e52e882efe800594e98f29e8aecf4ea3b662f95fab5b9e94447 ff21f495bd4634f38c0e1e162f3946cc0a593f7c738833cc83737a3cbbe2b808 5160ac5ea648b08a8219878f0fe2a2dead686c3ffa490bdaf76f5aa70ddc8474 7d432689aabb6ad473dffe72c3fd10f332fc667806187a1d0523272d69671da9 17e4a72c95f54d277c7ba2bad16f634ee82bf403c3b2af475acda08042017fc0 7c8d25f7cb1a3c7c76eb0f1a0afcff5e0acd6bc497c99c2aef04284e58cda7b6 a2f0a4e90c503240cfed6db5ebc3730fbe73dcd04e083c5c74e4e992b4bfcd19 61731b0a82a84272a3484abb794d41f8aa35f7ad7c15eda62119994fa82e1be8 9d0cb6dcb296c7a1d9f315d9f3cd1ee319a32cdb26f6ff29eb60864d1653435b c57d7d5187cf78f06a2b4192f4b2af9e6e3b5323f1923d520d675ad716b67e7b 66865357df4a203d0365078d80e8393f91157836e16592ce129aff41de009be4 6fa3380a808f73acf4636741ddf415cf2968b5c66822a3d9db9231b981319118 4705319d29971da7992ed65e5edf5a5d4ab65312ee5a555f92230dff85a8f6a8 84e4c913f2f98cdf68913c688ab2660ac1f77034c2007fa33bce2832450129b5 a2d9ef7d02cea31f517947a914a9731b731ed66c50eeb27c152ab8f3cdbe7f78 a4ebe351b09a08e8c9f508938034395f032d7992a67671a6feca145caa354cf5 584b6866fa954938d5a134b840a801dd0eac1a17c24e45e13f4566bde3e9a140 5abd93a726c61acdc9505a30eebd91df1453e1d7cff7d2ec334d986b08f71672 a6931ec1d2b21862f1cb935fb0d1b52791d8f916c2afb9937b51fa2317f493c5 8209bdfb0f4967a4855efb9c48b019d21ef2d17d2e768ec86de7bc57f36c916b 301e708d9fc079acf118ce1623a4d2eefd5ae3765883eb7ae06f0df59f0a9225 7da0a633b9c97d588d0c4f011db498f7fe262f15de189c77d0052e522e624f3d 69680b2e40a4ec561e8a753bf932feed7e401ab505e55305226121dc14084cd8 1cc70e12f5b2cc86a4c5fece7524a4bb842a4ea57944518d035f3f2f68b44b47 69a0efbd545b2662c8a6b04aeeb37fa58011570dd1133d95b47d544cafa3c81a 91ebc540692be156388b17ec9936efeaeabfa760bf334286cfce9ee9832a299f ffd00880cf9aff6051b7a16388171b8a2fcbe54862015476c77f7b3fc46d7086 c95389fa96f525e20aa521de82876391681fc1e62c92c80253f609775e0ad584 a72805faa42fac4965162a569cfa86887b8e6df3f9155d6a87d9f3c8c03c8cc9 e4f035aa88e869257fb5938c550db6d3400f4a1ad9338d412d04c2f60ba7c11b 9284397d95cdba29d6ea6df1609a48b96117cdbee19e841be496b79337d63281 012c09af03e3d3d7f48221672c5bf4e05dff3716d744647807627d9d7ed75e52 b9c9d1f834c86d1113d8536412829692e6794731e2cb85934f3add8ca5296295 70f4428a1f36c543107dee9931b378e738af47b4c960e2360521281ed01db82b 2245991898701a04d0702da058f30eac9f2eb869566bf5b0832f41449834994d debb66dadd879e7875cdd1fcc2e0c73a7164e295829ccd0af5ca6a44897e4ddd 3a094f9d7b3d011c97897ca7cb55c1e4aee5d2833ef5cb56a9d7c1aaf043e359 20072e748ddbda7f5a4f6a9f30c4e7a640997e5ecf97f15bf0cdb068c14d823c e5dffd9502a42b8638da63500706cde697dc42cf941636a3775d4ecf5f11563b 2decf150e5e635c47b67ff2c4c2f1c3cb74d15724c050b985a0d37e01e067646 8ab05fc816919f8017dfad54062e9f93d2f8cee3c011cc077e442df75c3ba0f1 5929f00cbc961bc693eb17131b6f89de73cd2137a6112d482346fdce4da3072f cd245415fca9190b3f5fa2b229f8ebca3aab2e084f01d0638afa99edd2c817f4 a71c52bd92541494e4a389f667ca59f34923911e17ef4ba923a04d238f89401d 8ae0b7bba43ec4b225e4829ffa00d0fa6f995f4ff93ce9237c645a7d7708b078 3a55518cedb46f2b1edeecdf56914a8f2de3605df30995fb2c331100dd73063d b249212b901d73152d5d6575a1ef5f27079ee8a6660c6dba3f0d70ab18079d5d 09cece2cec55abe14f6c5d3681e6a3da9e3bd75442369ace34915c8a725ec880 1710aa52ba925feafc4477ca73dec7dc11805e393642e5a444442989be1def0d b993ca971ea66dad353f6f5c338b6a2c8aecef15bd79ba03838a82c845172440 c9d8a3bcf7523dcc38d1a5ab4ca71c999310589e9712f54d8bdc13075d9602ef 12e8d6c4b4ccbab7ad98baecd77ccc8a1666474a1d9717e227750f22d6f46016 04a3246f91e86c0cea76b123bc08903dfdce13e5bc1710c656cca5d859148cf2 92494c362575eb4a19462b1b14067534a465d331ac233bd770adc155a0e9f805 6baf277853495cf5482fe08582d79a957f70e0a878e86bceaa9af967a3a1afdb 2b5fa12f48497ae1fb670b936287f03f1c473281dd42b594dbdcde4079ddfbaa fd9f84887a617768d79b3492844be5b8da294e3ea016c1e51bce26a36e72577d e7da58d7aaa13fea9936eb28114f9b7f0ef4997414b569aa895500c0afab20b0 5e0faad0e8d55ecad29d0ac6316e17bcc9eade3bda7aa3006d03a2e9fdef7e08 50c555404f87b93e52de103c405c9fb1cb3c19586a0b90422061c4dd513cb58b e6e9e2aed640f9c58d0e2645f8ff308af33d8c8e8124c0a61c8ecc9ca28dbda5 959d3c613b4151b28c1fb35569fd3d89d1e6d276af4030b77cfe5ee22ea7c11a 404dc708e03dfe8f28ba1ba88f2e5db8757a3a67edf5c430ef6f889711b1971b 12256a5cc36afdb2cde8d58f6b6c996cb4dbb3eabc9ef530f9b93466d53d99d6 1e423eb530434bf15bae5f8a7c2bdbaf7b1c10501a6609dd2446636d90a332ea ad60a07f8e4093ee17b949b1deb91f62e1ab72595402d45062978df4c3266819 1379f3a747a040ef4e5073b3ad4e33e8c31902c87dd3d61e6c08c94c979f78f0 33efe37673f13b2ad5a982ba809af30d7480efb4892f14364b1275f8cf4fbed9 d4271609be30b70ce95cf10b2c25bfae6eafe50af6dea2d8306a760db35eac85 c7d1569541855b69bf0546034f9b48afa641bbd532d38388e96cf1768b9af4e8 2aad5b98da163fb561c7595a5949383a6b9a0805f8b0e2a8844e045e11cfbc22 1358ce01434faf7c8e9b2dade15bd514c7e92c6dec4855c2d10683f45bc6095e c3e37be5881f20a1ab5ce33605713ace69020f4d9969b735dd84e6aed56c86ed 846c51c4b6eeaf711c7aab321c05262e21d1327d367bd5fe08c0e92be077c490 ece46633e4e417898ed86f20cec69668d6e04f5215f80fd8d9b7fd1f310c337d f104dab4913ff4d456ef784b0ee10a7bd79f6693e37c670bde2fff363f2a090c da677636d4db6c721915564b13e6b53d12ef075590f76dae676e4c53d67410dc 04a4fb6c35c38360ce5fdc268739e2ef6a2beed8ff7bc95460d62b5adf68f8dc 5e7559290137fd3d83706e5e7c1121883c359fedbbb94c8ca4b2680733d8b0c6 479b7e2475c7de034efd81859b61288b803c456684be12537de7aa99b5d127be 402fd78886359830d6839113804b636a85d473ead1920c9932cef4d9cea5c6f5 5cb4b5011651d2c69e636f81ca0d841807209d841a739bf2961d1480c4edfc55 d07f7d8beae6750468330d553fe1978cb404f4a1e2488c753a5d0749ae524636 7295ff8a66d12b4818b5f35d91f2b0ad70ccc7443130792f4e484a782db7f8e5 fee7468ba7194735b76a6416aa03e803afb3bc66077f65a4fc666a8670813a73 b1b2854a6dbc4cd826dfe66b8c34253af6c3860502bb629ebd8d4fe071fcd1a6 7df1fe7319b51ad76d1d6e9115456c2100b8fa74e46c3059ae65b32c756464ed a07ae5f7e8c60a6ee0b6bc0cd2b4cac1e60a964e98b4da967722bd3d03409a54 2c89e51f3e4a9ca6c0d283ebfc97ed96edbf16fb0f327548617e86b89e5f8dfc 8e6b6ae609a6940736bfc9a50a7b299b0274d9ccf03351d2a8f7694e398abb2e 2ea8b51ffa4de90f3cb2bb07ff59893aa13de7c32b7726ce1006dc88317161fc 904a28f7389923ee6374cb5d32e08197e899cfc066c96682cf3b0fbc3b72ca1b 729ed8fb818480801cb35ce3253ee767255cac0031cb70b47ce4388e1c28a3ac aff0ba9017e8179fff3df916b714c215b85d094907124b4ff5899450665b8da8 27f59c289add2c9470af149c956ff3b04f5c6388926b2984c24e5b6756c847c3 fec3cb7c1fd7fa60da89c019ead2d966e06fc052096442d3142226ff0fdb82fa 42d091f65225d2e064d979404ab5754f7ca4bad7c51406376fba94c4fd0b88d0 2bfb9b80fdadd222bb08bb790e6d188fc54419aa279c9b21c0951548674341bf 2916e5d0c9a9138cc0166320366ff1a1f7c1f491a64159f903e7a2025441ce63 405628a15b6d835366899c6daa9030494dd33216415a88e6d9645aed8c89b0e7 3b2656ab204b8472eaefa1dff67a0f1302d9aa9a1ad8f23b5481a685c6a856de c11e154bd9f80cc376fa95f3fc8fa3af606256855b354b639b4df67b8e8640dd 8bd2ebe7318b454747d500f3833246e8a3edda5913577d9871e7501f4c4c65db a720d5a5a7242952dfe68637045142b76dff814195f426dcc20d317213720e33 ecadd044840a168f7e83a8f2b3f67f5d49131f75fe3322cfacaaff9ef68a0e86 a23c3b773dc8374a0b50173d6dd2a5b29ce66b0d1d62dfef1560462e5a41cdfc d0a8fe4d361bff92a2c8d77aee367a097c301b1a0dd518cf136f8dbe5af9fdc8 f604bcb4374ee29b3c8e4041d27a08ef094ebbcb4ed19559f7515401dca0744a fbbddb30feff43c8798c2c6ddfe3e8b19ec2d3ce405e1f0ea1d6c148e9491dff 54b2dbdc5399e72856119bf552be5cb3974ddf1a7de015d7e5da1ee02b0dfaa2 4eca7debb202e342051c049226f8cb4767442f13e712d3a1742857e3188fe879 829dd2aae457f9465995c79a10cc1c03488a95de1b8dc12ee74ca66325a7be88 ad6d9c33f993edb38182239144b58430aced85e701780364ae9faa243fa7a217 e78c8e16cc112e8b526ae3496bc19f9fade6fa6778173a88dd0bc18f7262e2c5 31ea3fb778c29313a09ce9665a52949dd6ef52d9fd514ace880300a6f2dd1f2f 8615a6a3d70967d960dc0bc48e8ea0ebf59d8b39a5cd8a99266288f4e8316284 65f76789b071a4e9354db6f980cd2c35f1eaea62e32f34fa1a7e4b341606ab3e d5f40047e69e7b5f819b276bc2d277a11c9937813d5924e467b8c9bc81d215f9 355b0bf3c48b6f3b738c8562887e0ddf6f7fc04d1456d202e6ba02014a4c90c2 d137ab92a2881eced4293511a819679cbbc89008d21075ee21c6bb5b046215d6 86332fbeb6b2f50aa906a49247b09f207c425f17f0d2e7a8a2deb585bbf0eaed 58bcdce587e6eed7609d388a55b05e4240a61b568e9e5141591ce8629c238fe0 d6097f96caa48e380a7ad94df021698e525aa113791a2b8be91a7974d29de2f0 a9cae6409dab5099d26ec34a41f9d646663cdc941ef0862ecd384d27cf2e9a34 2751d3a661a51328185df4ed66658f02393f90c3350eda059a4c71b9d1ed9f67 a989804de21383e46dd2155ec0dbe2f5c127f7cd97a127ad4b7c76f017493f87 7c337941ed522a8f61149434e903942c595e0bf68a19eee809e500395ecffe04 d531d3a090f7b9feeaf254438852eee405bf412d41c053023716198d98f8d0f5 625cecc397c1b67441730cd1701986484c2b24b5c32ca61321ece2c4283c6a05 9f0fa0d4e8999b7adf27f9b31824a7c309b23d2be2ee83e851df8c2b660360f2 407b0bfe39971b9e0037dd79db93d164d079f8e00dcb8d4085dc8b41f91772a9 5455a1f8e7e7cc1374f610eaa089086fa7446610e74aa8bb4afda37715091f27 cbcb2b6fdca0f6e72f741e8ca88271c52d913d93784d69e70f16d6da7477ad98 db1e3d9df06599d6485e2ff371d4a600b43ccc249bbc6309c3f9a36de1f3af1b 406725971985265d7a2888e91b955ed40f4e3d1cedeacb710f8051c53cbaf49f 8b28e249da7f5c9280c01249572cba2e6eecc970a93bac53bcb4b9c8e6e640bf ae00617c7b499f288834e52a64504210be9fa398d113ef39525d6a85ce757c40 0677a6fd22fc12cf3dcdca106e05ee8fd18759b78598eedb7e176ac44d65e2d4 8e60bf4afee39109eea9edac28875c31f30e6e280d898a00f796d85de041eb6e cc7249762d8dc1bc2cbfe83e5a3a28842a48e6bf29c4e78c9f115cfc27bf8bf7 a6810203173aa88357dfed1f35beee6d73e38da78a7583b329810a06c46dac5d 431f2e1fe54a13aa3aad164e339cf6463b8190db5f5632d4c096b08e100246f0 e03c6d93874e0ec2c8ee0d238382040d455a55dc0923fd0b2afc1bd1f7d399a6 73b62b31b6ebcc9937dbbe4ac6907609a68d6eb4feb51f7e70afcc912d22623f afde6010128e5d4fd46a0c2afc6d12b4216b51e092dc050097ad9cee3b6148c2 cf660b8abca78a389af858c41ca235fa127cb86098964a5dda309ace3ff34c32 f74fbd3f2d3da14032200763a61edfb019f6a34492184702cb800ea889bf37d6 e74dfa1a4004ddfac92d41d388572a5f255982d40e207189e2c1892dc5af1130 6bf7dc128af4e7c039f52093b80cabbf0342f923cb372712fb012edc469607e8 ea3f3eed317815434b64a8c02647bb49933a341363351fb463198d6bfdfe1ab0 bf607ade73d9fbdf67d6f66f4757bd85f8084a1de1a5411217720e2875b94722 91d0180661702425c83970e04fdf5bb86511516c400f6618c9f66e60fa79b70f a4175d3185abd7a03ad7d139907322418577644a7394c77cda9092e937a1b401 31f2b49a2f3df0b3966ea20984843e4d40df5d3a115cc71923142d2cc9a5a339 12cbd557508932dad07402d9fe599cdf290d8e13404a7f0bbc30cb0e8f6fb051 e87e1757f7257a30b7fa51fda581127a5ae9df6f3a63c79ea0592b63dd431d51 624357831afe4f7f4acac70c457280b1e5e3d3ceb7d6642a7c5062f71bb73df5 beb1b43f58b59c7926aba3b5a873c614d008ae6220a12623680f2e7d571f2931 61ee7bc796acbfa157a882721938f4c6a38ea7c5780bca69f09d11bdbf4c6bcb 6be6cce2d2daa1c1dc937f44c7b290880f72e849a178f0fde7c451bbb16cc960 c6701e6389ea37f45b9faa73b44dc412c43e15f63827f73253251048d0d1aaf1 3bd90e4ae4ec59c20955a8786436fe323545d7d76dd86e977fae235710c2770d 86e7e8fae2623b5072bee6585705ad8c6a35467053011a29a2902bddf28f2e97 f975164d0117adc42dba23154292e0d9907eb1300524f9f146430763f27fe1a4 b1f1177dcdb89c3330fe6137eecd4e3f9f613456e32db51fa3f334882640166f 507565d88391e24f45e3219a723a2201a02c40c5cd88b3e16e7ab8d36d3f0442 682acb51bb734a6c5a8184f716a0b2bc890dd14d80a268d9ca697cbad6d2aef5 d6b05b529283e658771e12cb8db03e805a6f377515d2ee11ce71bee3f22b3f58 a3b057a1933ed0381d0cd89166e7b2cc8c3613f9cdd92cd109d86b4ffb842490 bd337f0b6ac1634f12c067493bcc741b21581dbcd5eecfd1386b4b50803a5beb d46971f3d8b4f9fbb71a71cc887646ffdf1ac95e40c8374881aed95f4e60d6e6 8a3249e79c08af225720d75c7736a07fa6cf863eea4eaaad647788c43b8f4ba6 f25ff9ae361b090d43b63274a411a1b5d4536307bf4c2aa857c3d515ef26335c c306224eb6323a16ab68eac94a38649236d1124f55b7bdec67c8961e411f1fc5 fbdf0163bdbb52808c79b0670c091b39c906583b379eab71153db54af0fa64db 01834dbe726bb20cfd6e8304bd14ca1f2ad2a92c6e0676640ba2306661d886d8 163b7b5864dea3f7fa0a7205004588c2f58326f0a93ec6f4ed42db249b46552c d4c93ebeb66d6eafebd24ff97dd405b61dabb318122177de4b20916eed1ddf99 68800a101d30377b7cc7ec21e5e5ba8f31098a1426f09c5713c59838faa86a1e 697c8ff36a824ab70fdedf3b4890e0e4d767f4894c7a4fe02753715e4e5504b6 169089c3de0a304c16958b81bc597b22424e08e4b44effcbfdc53cfb7ca32592 f7f4aac8edd8081a5d88e1bf2a1d8af7fc495f279183c1cc8ae9c2a2559acd6e 102abb0de31c22bd64ca47ae288a2ec0945640c9d8736361c8e7e5c70193bd6a 44ad04132ef42640626b75cdb2e4aef4bd6f8cbf52b5097db6ed3d89cf980c5c d97cb6a5a0a76ded4893fe31663fb9e463d6734281adbff52fb386a34c14f60e 109 530bfd8a3a9d36fe1eb093d688bcc59d146c1d9c5de5eb6aeed0b43a5175fb033930f539b022addf12951d9819afcafd36c8a89fcdbf9c832119796f5b05230fbc63f975d8808dc3b6d3a9f69a876c5e4721dc88475262499f24588c9f1c600fb0cc5a0bd1c7a9b831f31a4d44a5ebeaac29e94bfa6099057d5ee05b7a54b40941a55703d40e45707f616c09ccf95a2785278d335f08b03df960ff97369d6800df3c1e27ee35295a2fa662b05e844d21f4dc319ca545d61be3732c01daf17b0503ddcb047282427c699c1fa5cb72b0d4c156dc2d5d31952e29b99de823516903714309ac840cc4a194abab1d4d370c0daaa26262a0c467398bf8c492f3fbba0a1e00adfa80b22310b65efcd6a8ca1f6070a12a74e9b3d1bb6cdc2e1e0cde5a0cfaba84a315ae1ad1aebc725205f10d925278abf3aaf053e88d95a924983cc10a20224df24ba4209ef7a2513eedd9a065e9ffebe3cae6d6c09fcf73c448c86a080b2936b8910e02b2c0ac9a0e19dcd35d8a2a8b72b1d0306f66aed67875f0b30e64bf180e8c9996622a50f41281a39b401b8c40cb00bc5ef2c8a13068a93da409b43f5f79a7d933efe6664837e1b7d92559b3b50847f71d87a127687b83f91b0619e3d2ad152cb0059277f9d221bbcc1e46d36757523b5b8256d7cf0c3f63ee0353b4f35e9c8b1b1ad97063370ff034e56623ff50ddafacd0d0a83140b88414087130eb92d138864af151615850ac692de53a0ccfa76d8b52bc5613a67d068e0d3c2a307ea6bdc7d3639b99971f94a0c7d7844ba75b796f1bb53f919d9dfb580f238817cdc2b4d5d6dde99114cced3fb5c780358d4463642ff0c656cc2fcf03025b95d9f07211d4c7618d7578b9626cf2d432bfa7e8a523a933ca0b5098e3dd00ba425c2d7ca098a63bbb29029a91a05c23d3e41dd136e586836c7e0f5c7b6008ae9d9843bd62ccbcdbe9ec51577b83de3355b8409f9df755c3d158dcb76d2f038a0e96db5c220cb20cba1064db7496301a0e876e563a385762b02afcff8e0f014268acad0c91b5df656be0af1054492995fdcb9d5030d4f3cec4b05e9cd3590d3b57aa520ed47149d5a39fdd0ae41168ff0db03eb46d4996fb41769691b24802fbcd8d03a02a3600ec20f151dc53acb43ce49cf35096d44340d18471216d4a0f339cb39677d5608f1788ba30190c000fda661d0df28b76966c1acd077109520cb236a7aafde0343af9693f4645c1b9b5b22f8dbb045af0c08a0e3661f8a3c1038cef8a1218be58327021838ddcfc032b6fbeaedf8476575acfeff95aed5d5f011154814b4ab400f7137eeddbb45f311b6e4dda472f609bba3633e69f99186305eeb293c0b19afd542f67efdd09e35efd3953bbc1999fc0444e94da0e232ac20572fe2abd5d3742135e85a7323f0d15540e890b26b1a9e3a426fc2aefeace4009ea9678e36c05c9ce9cdee9a1b31c39d6cb080f41e11b78d71f0e6365cec8c609b188a19dc5b6e3be592b0887d371da701886e5aa688438d98f0e38dc24f9ba052d1e422cc8093a23500de0945531edbf2026205a93d3c5a1be946dab7669710a8f183ec5df11d474411e952c138a26b0306ceebc71924efc8a1a1987c9af7e096db3d82e56fb4df40b862e455cd5276c7448bbe49f7d8f4c8a6df97168afa40b20e30e1ad005451fb88f7abcc5c80a115ec131376c868ad72302eb98641ea203610d3ca88d67e238833084249a04a5977285fa039ad1b2bc555aab1ac3ba2d077881438fb75f4322c2331bded7a94ced0620b0ecd0bc9333ed34a958555949017cac51cbc12842f17479432a460076cba44f79e7a2ac8214cb42c03ef4fda80f188c02823ad6e164e70340cf13bff49b17d81f8b324aa41835ea00add450f30e366f6906d09a32154cb4b4108c0adc78b29d0297636202a8093f4617a5be6b091d9c71004ff9a9d80ef062da4d2fc24dd3116b9d28a9b1493d92368f0cf5d60bf8237516b36d6cd93fd6f74cf93c1a8bfa5f07663bb494a53629e6958ffe1c0f692894052a574c6dd22ec996883c7aa4f6ba92f0fd16f970562d36994e3479014c3280402591a4778338e11b262d8092a03a95b834708a47d313297884993e0db4701d2b8d52c3b6801889bb3eb337683ed94c6d52430aa40359a743c7cb030dc26782a8f32e6c527b3a12ce6a3a3d95aec0d022e2d65408c45f022f1a16820c25cfb0aeaa122e45f7234360f8641ce35e3a9e556120cb8164f29edf8b249b0904e2342371e7acc03cb54891e23abb8ecf31934027720fd96879ec2f01fb3c0ef919b00a345ee590131c9c1c53a30afbcd37810679508af527342ade0821fd047130c26f8b4796f71794f829d007b33d821d6265a3c52256d89f838c160e900b534c8492855b9a3c396b732f6cebbeceea7bbd45d13f45e6583477a3ecd5f30fb5c537debb5e65849b51729c63c8b46308fbd2b0f9eb23968bc1e01408137e08c26717574be178e7b88491248b4bab741ac8648aeda84c788b6c7514cb74870ce6c73d249eadd3864c674d5ad07061757b71b8455386667ddf99f9cfd2012904a5ff2cf8e33895f81107b921a403bad6829eecead7759dd0848319cdf24d3c0f566f170146390e66944c7c30090f89772f26eccccffdf5a55329f4f0f6b99708dbc23cca128087e4614148dc6cb4d140881a447ab05b0a81eb990d478766be033e0e079f7e576c94938ac9a68dae164bfe02f2a9770437cfba8e577d0766a1087029e717883a2b8d086058c7982be9ac8a6b4285b9e129527d82a3e2cc2d510f03b5df875f98d64912902a2391f2130ccf51b38049dbf9c09dcb129e177c1409c535c25792b5a06d9c04bd3614a24678af538e62733eb8c84b96da4334cebe0b007e8920c587c90e23c016aeab216f4f2686a48420dd27535f6604f479062809c6bdf4c89ce740c5e4edb084c58606749fd8ae668c899b2baadcbfa04e872f0be5cb4fede713f963bfe26bb9306cb6eafd66cda1d59a9c01b03908e67fd6e6081e7e63b6c8c5365cd556412ceff9a960e0add9ff593d5c93ff7de119009d4d0be9f17982ae291f4a484bd008988845f684f2396e081026fc0e2e389864586605a220bd787a6d0af480f87ea13f9da4f80edc734556e98a18a0f5a00c0fee3f0daa4d10c2fc0086092f430f0685dc74fdf9a72eb9e7cbb9af2c00469d25d9c90aecd0531dca4da73c37ee32e7a29d8eec597971002fd9f1b848725f7df62c910edcb2ce33c0ce5fe29a870a8a3bc6cdb8cfecf69e6d2e6fdcb78995dc7b400a075d0cab2ddc617a2b5fb1fb4740602afa055bbde4e255cc6dd8c93264a185400c44392912856cb50c001e778577aae3cd5cc45ed4ba415d81b0ddb620de598f02511e40461b530d2d4528143d29c6539849104e1030b161f274d28f398bb9bf06f7e485f08dcbb24079d2891af11a4cee246cc00e39be0ecff091db9accc50406459d589a094d071a48777e0e67976f936f17d9d2c852af130fda59389c5556082efe0e1f82379993ba1b78ad9d74f3b5e3957864364ac1abfe375d53949f5e0d154ac58f75d82542c9a353506bbfb43222acd118919a50afed33e4479f0e3d0cb56aac4e16e3dfd1796ef25934311fed6c63a03aea63a15bdb90929819bf790e6a8392abdac98bbaf7190b5c23e0f9e6a1b17278f63c25bb3b4883eba7a72901337e45204e438fffa2f9432aa56358aacb1752b7762f107170f63e125743db0e71e347a6a372d42088437a82a3c8c822299cf53a2d232cb022abace3d9d10a0279cf36cb7f22992ad1c932332c4784b34b6dc6280fcc5d5450fb1ef83f06ba03b575cabca822f0f2619e6df17754abfb17bef65863ab7b9767559e030e7f2b09a495e432f207e86e35bf5511d275c24fcfa39e5c33a3bef45fa4850a5f754d0b05f8da75998716f11f2ab3e239d9b92414e68aa2514f4ce2c5432cbfa6b7ae0c88be1edcc887b83eb25a24fdb45444d7c2d80178657bc2965860314681bb6f0a47973946b7971f7c0fa30414ecb1ee0b831797029daf7ae7d2a9996f1c24150f01fe5c11c52156a489f24979cb6a9d81a602ab8d74f41049b2f910a2d7d7110472b0ecb0fff0977172881c3863a317430cf4b6f0587397ec0b551d485f448307fff4d0dd5a64b68a51246df2185d5e55a192c9beddf15f6e4c52c8af6cafc1082cf0822be81c3e6106f20347bbf89eef97977e3ff36b373ed5a5c240b0074a054739634ef47351f23b7f9b88e532de50daf2688afe8f3486d3f0436c12e6540ded35d9e359b031ab6fa5894587b310caa37c388f292898b59f5bdb188b2149042c102623e44458c0ee65da3eeecb8cc414fd2deba119a3d63d98cff3c5e7450e908805c2feecb5d4a38b30d064d634a826f30118c758d128f5ddb456412190013a113289a6f8288e8ffc52a57d76ee2773572f990fae8885f46236a501654c0c681ba1a96ebb0263413ccc676371bce12bea4aa571f75007e0c1bf2fbd5d070ff7365e06c22765b7180b4516c5943447ef4e995b56c47ec7cf2b8b6e4309480b10d6ee56da9e33b666f9aa1bcc65e77446778e36b90d73fac0ed234b99b7cb047e0823ef0810bc0df0d112fab6cb8adead50546c9ee51524303c5596c278ce09ca7bac9adb097a5107e7d12967b9282837c0b3a2fe23965995ed99e0afd03a020b8e4a2683799e9753bb75cd159e49cdd6a929c0392a993f862e9443fad8370ae48176d2520545458b1f97894cc12013e935c8d4c6b3753e722f89d27871c801fca3b2b28b74834ef660436aac8db385ddd527d56a0cd1281238ff12063c410f36d0e69d6c833e0c5506c3c6b74b7cc89ea809652ce34f8f0cc7f8716c1a650381d495392f65ceb0088a21db78c17b9a7071a4ab44f524a03f2a760b3c10a60c3cbeeb5917879f9e16576bb728c9ed1f81cadab42b7445a1c3717e814c413502f249cbfc3cce057f2b81ff8b8f2eb881b485e0b85117670150a6f27ae08140030b744d3360273cdd55a3b5b6e61f3a2558921f677c641226eccb0987311d4700fc18c5e054cdd2353278441734289f43eced9da67adda821906ab15e6d2a7a0331ae751bc9a24c0a7fcf50fd4a3b0869bf90f32ea74039028df67ca5f1a4af06c8e3b90654cc103164699e2557a0e70b08484d3710ec5eb464e09afb7acbdf06a2384ca8a5f7d20f8774f76f8953c9efa1499078cb5522dcbd388785c3dc6601e703316a8feb8248fcb4168ba07fbb84cc5f8c7a53fc216aa5fb8ff5326ec60ba8b52ec669604eecc21bb1d9196dcae9fa11be70ea815c810039c8dc7768750db9968b55a1cf80374cd1a040d3d905fe37795ff48540aa5cd692af0e5685cf042165352d19e2a18da963e2651176dd89338890a86e03142685a6e98c44a0a208e6b8ee605a78bb905de42ccb197b4e35a5aefec5971b7f30d31f782307d2820dfb1f8523fe42af6c77eb4c03d1d5750d808dfbe69eb66b2b3bde178f57200c04b4028fa57fb748566f32f86add2c36dc4fbf21a7466cd51c1e925d73f312d20d9054be0672bb3884618739ff3a1edae980b54984f3dcc5c242bb34b3a5db6c0bfeec3fec1eb158cbe99a5b1b20f3b074f209184bc6c1a9c37f510631132ba90410b7289be357be0d939988323fe8048efe4b5496f918acd999b993e9567b57005e6456035e81097aaf85ac6f79ca25de0eb12cc36a30153b53a085454386610c595638d714ea3b53e767055cb2baa80b2854307b68df1580d045d25ff54cc00cd21d8854289ea579e29ac0da7c0117958964e158ad936ae5489dcc44be9533074ed60b2b14d20e37a8d31c7006344dfe6025d2f4300315135d3c921a1230a9044f8e3d52a2db7ecd35541d49c94a1de24f669813dfe40fea5b54526c317c4701949ec3b4a38396bfa5f85c42040d1f5bb9c2bcb87db3072df7e2ed50111fc10b539eba3da5855afea320f3aeec539445a962570d9e234a7cf155bc207f97f60ff50f5fddbb337d8c24a3b8b9cced609eacafeb6b4ae7c981094437ed5c61e70b1b2f9186074046f43f58aeed909ff45d26ef4d5d8f8624ba8978ac93360f650d5cfb71737230c505816f1d1a819e7a7896a3fc0cf3c768ef6b3a1840dffd8d064e684c6e660369139da4ffcc213fbfdb0c400f80efc6a3d71a6da7d02c20200c624aa3e3a08dfc5bafdaff23c062ea5529b3c9d759224fd28bfc124bf7fe1b0a3b1db8a825b89888b305199a30ef93ada4d7328877d3214e2cf731a13f854200cad72002d8a072cacacb7c8c45d78f63d7781beef1f08f7d1a60962ee432ad02c8dd50bb503a47d1c7bcfb9c94fa17547add3fa2df94dd70b5e12b155443490c152e835ac2c957a667837dbd33229df6f66d94ea5737e538136ca0e34e8e130552eae92e412ca3a5a856140fdd874dc4a4d9f7ef2dc4654e6c5f4547001b5c0d2563ce91fafdfccb437ed12f537db1632c3c86e234e91baa7f90904ba3fb4b085484e5a1a5eebab7fe9c5ffebc40638130b616c1c0e871f738111a67c932250159a2df1eafcebde2368b6d7291b768c20a68f940daa05fcc367fb2a615f3bc09e016b624fb7e0efbe22821a599e850993b9bd5853cb3e8edcb72e4830dad1b086f509f733e9084ac1fd5a6504c4d6ccafd999423496e80859ba60f982bbf160d9af989c77712b05c7f140163bca32ee53f45cdc7e154f9e43d5bb42125f557035a5dd8a275f82824428f7cd221d5498c4eaa93d37ead3dccdf37fc0951b7a80b70dc09c027028983697b027804207ab17ae0d1e4e3f36bee0a114b0d3fd7f50be68928686d330670987b84b71a12c3245d2e1976309b5e669a9b05fb5601720f38ab2c3814697d6bd0b9aae8d91602cbd952274ce4fd09c96f6e7ce4496c6001a3a4e04163dd315434bace9ef858a959fc709e12256c62068a866eaeddbf8204da3e33586c417250b7bbdcec1a4e55f048af1262ff2cac949a8c2e060a55f60d137611ddadeb5812a3c965e39550d0e2ba2ba93b11c1127d99946bfb80b17c0e604987b7ee0533d46090cb0264fef70845ef779d667b7dbc4ea6b32340730d0c4b71db29fb59eff5b24e1d157fdd12b65e61e4874fcdf4a5246940c67be92a0c2ab539574397ba927cb3765db0725c77fcfa104ebc28f5cc7df9590017f50c0f048f860808c80c94df8580b5aac144831800c8756452c56bcab123ca6b7f2c06fb69b18235d8aac8dc6ae5633bdb798125e4563374ffe1101f3091e171c05b0696e527af64a6f26f5772c5e761662fd1be77b5a634cc90aac53560348d1ab406375a6c25a0637011115b19fbc97ff87915371a465a4e7b5c5cfc4ab93351d30d03a7f7eaf533d1a565b0466c6677447ddadf1917c7fd74d820680fc45ec44309b2eaa4afc831c86f5e0b6739f3ec3611406493bbd856bdee63bafbccd3d222079bdf9224f907b2932091c14b4966dca07037e2b940bad1f771349a8688019303e279500b5341d9bcde8b026ac5bf20adb4d863641b115f91cc05de3ff5b88c0137bd7fd2efb3bc9606480e18293b368199d97929b6319954169b4d1b4d6bb3067c17f1ebf944b3ffe9cbed76cb5e62c4024fd9edb4789dc5242a85024f10400bd5e08d49c0da204e8fa386f0684c76fa71a0e04e0e4c85d243c35bb862e92502430d93b77a7fa933d244a3d5e1454f5156eb33d0afee52d5cb6ecc0a789e8a0a1206cf0af1a2257460156ce57d259ff26d97de532a85cfae7a32d9ecf6146a0bd90b978e139942308728387b664956d3c0772cbfb0c21166227af1ed91d7be087c2d1db2d674c9b6c31228ce3bcdb7749eef5be76a58493f55e4e56f6fa86209fa93497763dd4d43bd51c9e297c1deb3c45bcd62816c07319ce6a570e741d805cf4330989568c73cb019078b783ed9cd28f9ff8eec3fa2fa593cc75367f0020b904cba6412c12559102dc94ee4aac8a6a213c7f6413f9999feb72beae3dcf40b3dc3e3b6629e25510225ce4d4be09b915a5061ea2914dc90156b99f7b66e76049c0abfdc7c1da5ad18b1b7b4a5342a0070861f9d5794cfedb180445b926404017d8538feb0d26d31f2e68642a05a9842a76676f24813c79de2e9aaaa7f77db01ea67345bc597f1eac0f31af9af264bb9a3de2776f40fb96c05ed451b86a41f0d4f93c16e63cb57ff22ecc58b68481bee10ec2941f07a46973d9953b1686b0400b99d8fc707d2bc99c664860edff5c4375a0a6ee51219d96d1aa5b55142992f0b8f50d63dd0c7e18009b0e00e4cc5ff0a7e56727f8881d67a26372b85a92cd808e986bb9f728d8a50c057eb28b76883e2a45d802e98f65881326b5f1f12a0ad0a2cea588a2e1fbf931f4511922d1bcc49bdcc334886a8cd2e4d42b30be533400df7ea0a629abe35b25d818f06e191de7576aa67604f087b3b593bc61ae30ab5023fd1d299aab64ac0005ba51e9adc25a0ccdfd61a9774aee4387ca5e08ccd940647db087089d6de9c507a5c370a9eca57014a955152166210061e6bef2fb5400a0d945ba0479d964e34a8321569d4ebbf86b5860f199a3c071a352097a3cc0003fe90986a13e3741408b5d2cc1d1e2dd4b3de0e6d943f7eea895af9731587b40df2d61cc1fea009fb5b4d71444a0062e54dd1a034820042a5a0c92329e4dd680508922657fcf444ab8cffa20ad32a2d9f9346c15e9113f51d93123eecf47f0802094660d26a37b1e572308238bcc0cd9d8fc6b76e85002912f36daf80ba94df0c41f1f63bacada9e91b96129f3bcc32ceb7fe39f8e3b35f038fbff14343617a05fdfcbdf0fd7c95321c2e30656d0dcadeaa0f123191959d4d3a940b75cb5e690c1da48b38f2663b210ab030841afae5faf3a051e7a384733805fba15aa474940e95f01b74b4e507fa357ea52cd17dd3909ad06738a5ed827ebda05bbb779b5d01a237b61232b70b62a386ee666a37e516da038d66796c5d1deb9925e7d38888076f7520ee2bfce81e1cff19db41e2e4b433a568ca2d020e63e9ec32da36b03b0670768237eb9f9f2a1e08823922726dba9e360876cbcf8d45392b3a069f3dfc061cae79024b95f0f0129f00ac7929a7d2af75c8934790c5cbcaf478ab9a1b280da048fd10f9b3bab48289c97c0b4338c0509e8f33f401519c19b2d6a91bb1a50c219d5d8696684568efd5d7832b7067280ce6de10de865cea3b9fa64fdcb5060a4d9eba424bd850bf3c3e3bdf86711b6717443fdb41a75a7be98b521ce4f3110e2f5e1923feafbb7c2f543b27465efe799991557b91aa02521e17903b3d9e890256d96357d42a8262e0753f78358d4e1d8f9ec870b721753c24c360ab7b96010435d4fe18f9033b4e01b90f2b474e0236e4d7e0f8fdeea3ec2ddf42c634b4f30eda2b4b57664a1feabeb6c5b830cd528d2faf4ffe6fbb5edc2df3083652f5300f00ce253bb745a9fa3e68f74c5c51bf76fed485f91e7ff87b189d5132853f7b0eb3731bedc94b8d44d5095a38cb5112615093af4a6c5c5a4055a1bea6c57dc40197fe3bd02cacc1fac9cc171b3d3c93d007c589d32c8ce39a6395bee54ab4c70c49ed8d98549c8ce7cd777195cb37734cb7c5e4906b93ee7dc030ca89cee72801b28ee0476a687aeb29892612ec89d4a49ad9113ae7d90e7f5104d1bbcb8db40cac1b7696e00f0ce928abf27843c3f510297d6e61771b54ff05df506a0b51250bc9f3452e6e306a124f847c351906d8c412cea8dfcf6dcf5842cc3055546128035cc7ec66a96eb6628c114b76772ce95720f8be86e70db1ad9043316189ea3a0427f34696bfd245a14fc0de21c86b45ade67b638f262e9db2f1a2c37e56e9e70ea56ef5ae5c9108410482782b110e83bac519dd0e634ce5f0fadc9c10b240d50f8f6a3142ea3789cb6761b140c15f28e64d674078854f5d38ed73d82100e2520da1dd4718a69db643e74434b2a95173adaa8b368a2c5be1eb9b8f9e30c55f5d00035c36f18ee46d4ac09e9c5eff16c4ada6ddcad77b4c8569ca691ff68555200a2b4dd099ceecee0b2b201f289009824e05ac1e4aea0ddb5326326300934c10039dc637d91ab8cf5a54e5913fa620681a3002434126622bf19e6a97b9e8a7b90f9e3f006b897d570cf860a0b5bd967e04e8e8495c9dbfbaa2a5ad3a02115a1f0459149d0f19bb5bf9a79923e6a5be882f96b4fd0bc7f82c560fdc003735cc5807a1625f508e67e150e7c941205d0028c7ebb1f2c092df8e4a68d68d3dba310e06e68c82cc25f595a4881994c1f80f38edfaa8c8375b2acbdf9c99808dab3cb30c000195641983bbf143df4d0fbdcaa368370be391a4dfb67608d9f708525a8d047607bfc17a6909a140fd052a2172ec5b4a09bf4753579af625c53ff427fc48000fac71c19397ac6f7b8d3b179bdd5b38930d6ed6b6041b6de57619aac418790162cb7b2b600d218fa80fb69c47f1cca0b24fe667a5fdb18549e342c9626c5209a33dc4bd4cf490d5cee8150294a62e6b47644fa1cf038e7d8bf410bed465b002628ac65dae09897921645d3babafc0b14e33cb1658c8b1d494eafe549b934d0f41d9d79effdd520a743978d95c1f45bf00b7937be5a5e11d8c150ecec6a5b2006cdb59a6f5d31fa6a9044b3481914678a4185ff2c8edeae59cc5f794ec14d200e0b02a5e054dd69d2e084ec1e3f19bd18045a3e7d23364dd701457684a1f5d0983aea621ce9b1d0d4c2bfd6f4a8aec14414a88e72b2b4e2e60586d15b31a8709ded344b9c349e4d81d78f8681899073a4e4b7af45a6627dadce0318119a5510ad5a543c2d40a68d425ac62eae33a1236fe2fa4db40100697c86e378afcb82706e4b0ccc115c947fff5b6a9d4509ab9d9100f06576e7b11e912be81dd6f3cf2096d2fb9a2a8f99b1471fdcf57e0b1b4579663504ce8f5defd276042bafcfff40e5085a85b84e62e90742369840db4b840b002ab0aaf9e3dfd66bfa2840292d70260a5ab3a2dd78cec36eae2a3fc75cfc7409a7f25b920c47e3636e51a2948430931737e2b54317ff7e7a39330b3de5d9ecd168a853604e4fad95b906cd20e4e066be7ea8dcbb55a7460234e2d25287bfc63a9e8d2b483d6c9f5b384fb1be43303b4e0f05965f251b27075d0092abf046a41f9293a546d055917046b6294dd8a0cfdea4e4849f947601b71cebd532d1122bf1168352aa3b47b804f4df5dcded80a9e4a7c5ec51ac379e5e6e44506a3b8d9a8ccd5edf4dfc29b8d45510287e1dd043bac541747509b337340c861b668a96b0caef40f839af9c3a2ec7d1a0b003d08e5a862203299a6b33c7e90245d6a97d826fc3ed8aa956f716102494f7da57c0c80e6e6444c7bfe81845b3d6585449ac12feb52be0540db7ed561969beeb86100899600da327d85edc873df996aabf3850d10837f2cd2cb1f52306d36828e4903ed07edb1335be9e2ca601c8a769df8c14544d570ef7b82c43e29c2c756b15907f748696e286230c1740a84b7b3109d71fa0c9a13beb7ee9d7a5cc7a22bb48a0af50e034e682e77eee558e3c4e0fc7d1997beaa8bb37e273e05d93137417ee407cc7d0f4e1eddff640966d0899cc9eb4a15cfaa5cf7ec4053b91a148fd9e2010185e55f18ccf8c6264735d7789995f424543e9cd6cf19a90a40e2e8b3b5d6970f60898d8ca9ee619e1ef72aa59801218e88706dd2f559128931868935f36bdf0f531bcc33d1415cd6b371987378bf6de11f38f21b517002990bd5c7a216ba660b281fe162661405551e7f22c522c03e4a84715f030189d04060b9562d7e02640bd7d46ccc62a1441086994d03de0f439908366f42063e9bcdc3923587853e010a8f4037807311f557e91492309dc1fd978029c129a93e6ab730ffefd4a7e36b09b6cc6e6cd55f5f66c103d73df7c0bd5529f5ced6db1b80ffa1e7e4a79809f405f0b941c1426aa3aa0b986eb9b2fe233d069374b6129bf43164a578f8bc97180abee95966f374a152519229421b10c4398fd75f81b7586e061028e453ce97e5048a6c30ff82cbb1bc5a3842d355b905ce071093ec72903008bf21bace5173740d0a91a24717dc2da92ba1566197170bf5ca68bcc983abf14c9f724ec11dd38d01e901fcc15e7a483eab8390166af3777b404a37702b30499b29b5b8face9d5505fa43654b5768e86c762f34dde105419eef3d278ac44b1ce94b8cfcccc9b870057a6cf3ff9bc832ac073eab16485d18339fd61c9e335848d7a67e00885538b70da4755c62c32bab34d733a42a378eae4065bc7468af477cadbc0a436883e3440a50ac5b87ca7d006ca26dc9d4f9f18fb69a4e9138746a619f7021f2a84182170874b35e32be8ffe2ce6ad40dbef331872c1e37cd8d5aafdbf52cc9ab03b4e9e08f7e01288c6fba50a7d796d421743e2f0f0aa69283995e497cd921e7ec4d03f0cd72315c5c6fab09ca88909d87fdfbcdcad94386fe69fd7bc597283961239c50f9bbf205898b551a48538f2c404573a41a472349ee0e0ccd6eba51b50834480001070c066202a9d067672b717e48c12b56084ec5f5a2604f1147ad7aecc08d3091d6c665fdcf6e6d753f8eda978b3a6ab1b0873946b221db0aa32eee159dccd019422f7dc42d7d8bda3536ceff5f5d309fe0e52072d175a409d2ceb5b01088c0ed8e0b6a5e1ae8108f7255dec01e61e9983cd8293bb90afeed229e3ecd5d412022535644b031ed2e8a600988b1eddc0aaff553c1a37665e34d48b1ae5d8855e067e54e07c65ac11abd632e0fb85ce84ab72582fa1706387d97e5c90b8402221000ccf28ca27d89062093cf40613e170aeb382d6fbc4e5f3c5766cadd962d40b0de10bb3e1f86f971f71d7221c2e327025f797ac2b172b08b45ab5979ae3e82408275ff1e57060049cb23434b8591505e4ff4f33b5ec7c52cac57188eb69511d0ce5bafe933ebe096290a54b2e6ff47b6793897f38bf3924043c20d1cbf0300e010a73bc31a7f986f6c24002e8f65c2a89bd124f799fea9eb2a5c960f14801800ea0d204a6734af867a177bc0e738a4763dab837f947a1b2607fc1aca9649d290fc4e9eb7e6250fbc914e68485cdfc5178cf307b2e7e7a1b92a7b99911bcf10d03ea77ba0ea2a89feefc228bfaf22e8beea2758c86bbd70c905f9108fe28c22006a86e06783f40bff0cf690c1d7f0dd04448c6eaee1046f80dbb1fe180e65971011cf4d8a00c1edd02b79420a85215896723d67f4040c9f64a90482cb2e727e20b20e741ba840f844881f0370fd093d978ee6b36106722d1e4bf3ce6b6ce804a005a79bb43bc9281d0c2905387fbab5759128c6c0729679b57daccba60655bcb06d706e0f3dc843ffed0493ba5808c9fd2ba910e8bbd10cacc89f98d6ea181be03f0a3285fc10160ed4c973b7fc8838252b89499e2053c748f6fcbb19bc8f8490e0c794a9ceea7f1234f7a1df4a04158099c2bd10366b75753a457f28bbdbf0b003ee45243bd69538c7808ae0fc2e6bc67ca08408e476beff3f31f96bfb12df504c5452fe546ea8c4fe2735e945da1a5ab9569cb0b458034155c70ad9721dc6a0a79b2806d32bed1dd14716b4420052bc4a31943e5781479ffa95d04d4569f380a0667b21486b9fe028ef0e38a3ccc336c148c6a8eb5abfcb0cf891cc202f98f0ac7bb2faf0ecb8c3fa04bdf3cf97a4840707816d55f176612969b4ae9ddadc9033cf2c771ccbb27caf62871ad28c362772702f0801e62a29c286480b15b0c0c05bfc9222afdc84b3a6b376643ccefd0cb20c0d3dceb4c71828f1d02eb1d52a20490ef35e86c034002a6c15ade69bd290697dff156c2d32757ef578ede89619b030622ee254a99c7e6b4c814db98ec2b840f9f855073728a46461cd20b5ebbcd0ba39a7e2f663702a896a2a8a9bbde904e3aa0109e771b589c233ca3298e2f780b5b7ae2690d9c83ef2bef308bfcc994b675e22f55ccb02d577e775af863c3d80c3aecb2f71a2d6d4150c6b2cc6230e735f8895d3758e8279f12b133e2f784a0027e884d42b6f29fd99a2e7a650a80c458c3492cfebf6882dc73c4241d2d3f270f6f86ad5580198c8747ba0350009d505a69f4219d0005175ad000cc8883f76a0c80563ee30dab3a1fba92df4ed28e23a43641b62224c741e4c854344a5608fd029eeff1ad9be6a3ead5536770b88c8d8e7d5b04773d24a249a3f3f086110aac00ad9ce3a2a45630d01c9563256de8941c17a980b8d8ee306d13f5ec2d4505ab0acfd5463d61ef55ddead0ac93e5127513115c5fabb393f292fc5af4711a6b2908c58dcbc1743285aca1b3213e368d7eb5270118a58e7a41cc3ffbb3a7266970060e1127ba0446330b28f5113760753c74783edaf4a7291ccded3627faf64f3100c3892e6e07ffdc4f2bd7836ecf1af568b56b20ca9fded07dde63bd1e98a0580f084d79ee2446356f43b5d6a39b13defa6baf4a444eb7b211d24913033e30900a73c1544e023ccaf14aecb16ff7933544d9f27f8ed0c1b3fc8014635d6db1660955957ab5687d79cda35d6b0c48f4ea3d504c07ad46ff79b29c6cf9d22238d60d3b3b3abece46a228f53b3b604c21a669cafd94db64bce6a6ade854a99f813c07bf0b4190910204a594416588e483bd382144c989be97dccee2fab64c29505d0a1637f003e553841345bc0738a4bfe6a0047fae501a2c4b765cb829bc415bd20f06669b32671e30562adb1804c9f06efd81a9791981f0e66669e481f06ab5e60ff1e3d5d04f77541a8b37189299e9732b2caad7ec05d6d539c6a973c72a061603ced3b45a9c30d479dc5e2abf04094e78444de011630bc14ef46ad9397afb010e2d631724971ed168c92395b2e4a4faa4eca1f1a4f4234e1d5ba1db3d38382a012bdc552d7a85abbb116e92c0904078179b9356574c0776ef995bca962de0f600d97b23a31c780efeeb90710faf591f55dbe4375719575b5ef47494a4236a1a0bbcb903928330b031ac9e26df4d1117391e58ee746bab3b9c001749f13a072904c861dbc8e98d6638e3f4c9bfdadd8dff8fbeb02319f139c662a0f64c238e53007b065b4e40476922dcf25d6b940fbd2d23d0960133bbe7f5e76c14d094ffcb038c8aa436a74ca1c794adc53a8f8d88b75f870dc58568c70b88803dd35944fb082015403989f8d613ff4c73cd3073472d2ecd5ffe05c62b17708b85d4cabc380debffb1dc47a2c5622ae60fde5d8b42a0b50ec65a96c3d0ff5c0566118936bb0a066218c4daae3a29ca18df18632675a1d41135f3674e06e4bba29700550af700ae35114614576580d38aeb360fd15f942f93a198fef462c9333a7dadf4a26a071af4a5f762c1761957889da5d2c46c4b765dce4878d0582cff3cbf785263e2031b81cc165de524226bb781c10e1fddd68ba07b7ba0b7afd34e43467f63bd1902e49f59e41848402a46e6612aa84209272305cad2cf163f507761438a356e2d08521fe168037e6e4f0551d5daa08e3fffa8e759dd7a15d05395344b4bddcbba0ff32c5a448d978b757b164e64bb13d06c1d191519282befc4e8c4e117d520dc050f8acf7d136c248a1e2737aa3e68a80a18c26a602b2bab791c66374723b81a0881ad60279ef5c9111ffd86b839ac13d65a6bd9fbe32eebc9c91b53947d4ce105e8a03f2f0c1f63b9a5f2a2879e44307e1b9349a03ee622e55d7d9b029dac070263c88ee4c3603bcaaad0744764488dcc5f546130fcbf9212edaea64ebcd28c037fbc310b1f84bdb801f6279b40827d384b818ba4bde847771bcbfb9749dece0f1fac2936633ec35da024fb8d5bf7870845fb5d5d981050e4b6267a4563af60050784cc9c1ca6e6666965aadfedd0850b0195f3730594294a72c6841b0fad7003b2ae869bf6f02baa394a5c4d5a254178c36c0f6c5e07275d78bd9e3bb8f35c0ef3ac447fd2bf1e93c8441a965a40210037156c0ad09c3e2cd5d6fb1db715e206d85147539c3a2a57f6e6cb24c172d4cb79d2bf69c4fbbb186619be3a3ee6d90685c668b6dbcee2cd48704aa5b983478fc00a273458e044b167706718b3bf3a0887a5aac2c299f84bfa50347a6f94298574404a89e17a6621177fdca18f3b950c81076e0d0ed7098f20bfcd9312bcbbf9194ee3b3893006a9d7f6e456ee8b880d9e4c0555f82b6871cc2001bc2013b6247b9a11bcc469d268660edcdc7310c4015a55d2c16f3e1e4456e02cfb15852ed8b08d69981bb5ddf2df9dbdb983cf8a07ddfd0d4304990050d185268b1b5a513cd30198a5d2119e719674f1f210b8e709db3b8852af23e1af2f80eee291136b3c8855741e85f1bfbb4feef6575f7afa08820ec92d1e25a0b3dcfad4a398952c1e308db2edd8c1f8b9ea5d431930231102f61d0561c6f156e78f2c8fdc8868ab80e27e9dc346b7a894a2290f5c0a25e701a468128b291447086e8c4cd8298fddbc5a6bfe724b30007cd6d5d96ef34a290271643027b820a2b5a421aa861c5ada4aef3d90f41fdeb9f90368635fed309b0c703aea81773b8d5bac2c149504c8e9e959da9f55635954eba89683aaced77b02f6ea5e532dd35717f9cf145ada78a03a0aa2fcf2f96339c3e566c3249350d704decb459da508308724aeb097fe49507db4c9a241546ebf0444a613055e91fb02f7d626a1349d84f547195cdcbf0e51ee6caac2b0c7addf3e6e9432e5502565035fed75f11eac2d11e38405d2bf322e097f82bba18b0242b7c0fa0300c0bd6705d8b2ef4602765ab42403c26e2f93fb9c1bcd5ca4eeb8a720181190062cfa8203314ac686cd33c7d17a0c2a4ee391f17a917b2550a918ddc3d48840f807e35604671d15f822bd84d6fdd478ec8e3883800189e5b9333120babb27c2d7f35549074d5533eb3e9effb39c34f1e48410144c8aad730667b327229d9bf7b34281a60fe692ced291ca32300721c703d2a5a05bd041dcb95ceec68fd38c5070933aba079902287954f15e3a267fcf74ca95d5ec5bd730da32970d3b54c398bfd8aba902e050ab0cb28e0cdf5a778a9555aee22bb282a19d947281e9e5ed4ebe51e62c09508fa400022b75eb2a053b3ea18793296a812ee39d545e524cef6ef5a9f9470fa8bd8b05e62bf92619c20e66b5c40646ca576add5f396a71ef2df6597a8569020ad23caca82e05abffb7202c57c9874eb11118efdd549bd99005db53d59f350a4c8098fc00aee7ac921a4910853a98ac739fc457a763cbc7fdbf30d6b84d9c03ef4c4e5264f6f4c40f2b4a96105f0a24bbf9f783efe180f321fa21ddfcc1f000aa92823ffc2b573735e1377e71c7e30681d7e4c5ad14df41d1d440167d4bae09fe1ead3fce7f59f5fa8204b7b4f4276a90eec6d9a84ff72a65d2bbfaf7429a066794987432996bf120959f9d11c73975d100261d4dbc79fb3a348512d4c0590724824e7524b124e893cb36ae31bfbad002bd992fc0872fcbd92fe4709291750e50da7cea4dacc882851dfe91d7c517bba5b37c444353f1fc375d81d8f885a0066086e8d7920a7ddd8ef8769ddd9494207569369b56db79b05158e8d83ccdc009f989019f354d7c430210a5ec0ef60cde654f923293ce5149f9d0653fb90bbb0199e30b2728c8d27d71fe48d75b8bb58e082930f13903d121a81032a3f36dcf0e0959c8854208af4723bf52cfcaf160ed079d2e3cee5ed394cce41fdc3e81c200efcf5ca866604c994c30fab3c2c1aaed963ef581c578fdd4f126602029d0db0ced3bffb66b0df1d42f2bcd206c12bc0963ec2a7b7998b7a77894f673970c9401d6843acbe4814d40e21c8e5b14d841b80c55db86830054c1a8136760b43ff90d34f21d25eb65c55c024a834b6775960ad36f65ff56d541e5a1eccd0744480d09bcee62840119d91648a2c284c72375064e43a18412d30e05847fa7bd4623cc0dd0a5aefd575e4bfa66c2a43a44c1d11688010ef167ad09f47d4ba99b5cf0c002e832384356159c23ed1c87a8d44164dbba2a6db51ed8f8e8c018aeb9daf2ad04aeaaa8a38bda081ac6bcf68b223e5607eea6c35675ac83dc1f63d3bc1b77da0ecc20c5d70c0455484921be7dbdcfaa1880d9b0cd42d61d044706de97b8540607f8dd6ce1b8d84a76d3be26feaccdb57f4975efe99b88aaa91c66d4e6cea9210270fcc25a96d56557195f108bcbaa312dd90b19d77139cce0d45884a22622920497ae02c4632a97f5297e3a348251c023cc7bc89d3920f23f9009b17782ce890bec77fa359f14cd967e85a9357157321e820713200555008b10929370506d22017850867feb8ba0f89989a2cc71810b612a72d7f7d4965911d9ca7828a5772f0a90dafcf4776117ca1a012c3a7445e2abf6620606414516d3e562a6925bf83a0e1baace214c0c762682513ad146d1e68386fe717135fbb4926c00120be1523500358c22d4f2672f1648818c3a35ae3009cbf3dbd34e7eecd69f5e9cc54d646a025faad585ff6dd20a83d2ef8433578277a4ff1ddd1b46f9f35b2e6909bb446d06987214fc9a1471ff80d0d37a4df04e285354263a2b9fc8a842ffe5e23c6ff207866c08cb7c9e7fafcbd881d4bce175e46a7f5341a2fa39b66f6bf3146cc7610cc0c031af56fc199028005e87628c897a0c6ad4254ed87643c9a5f4dcbf5dff09b432867bcd7ab1040cbdd17d72036313fc24f6fbffcfa6684896b92aef4dfb07c6c28b6b31315831fa6481c7d2b4d3ab3ddedcc0aaaa1bc79171eb8ef676590dda214657362641f440ad87e70854f6275a7b6da27918e4e8d16c8957c70d650d552424ccd52431060b64f996b319a91d4257531d8b96fec2abadda7c82f8ea0c0f2bc9c457cbbbccc2efe29f70adaaa233ec94f36a1cb08b9dca32e51ada68074d600f10b393db2ce0de4fcc6daaa2b9296488f8cf682e3a4d49f1faf8fba804acdf252174951031d14298d1254fc6c0afc7079582852cb27f2a9c763165b109351847f218c87dbe85687a363911441c9b558cc5ed454f9d9fcd263633f2870183e5ccce241e80ef14f1391cb0f8054797a5f4a69736844d034a950df205d8076f21ce184814856dab7936bb6e1501afa43657b5cdc69fe0fd51addfd812870bf81d159c3b19baf08a76aeea0fd6c0dc762e755309d1d6f3d2a1cff366a3e807cd549d514dec42bdc5e615d8ee5783983792c534dd9da4912a40ce57c6eda10ec29c4fdab90101123e07ebd65a35e91db03c107019c05811f919fa85e6cc6a0124f7eb2bf6e97e640d298ea69f1a651a98db3aaa6dab9cb3639508d7bec2f40a0da6f693315d4b3d3a1d0a17bc1be300a3ea90a8ce6350174fdfc9d74d4e970b8658e3f18877a0d52d234a809e62b04607c7183f44060110d65e3faaca400004427415c8d97ebc15320e4f79672e8f959a29376bb5546a8fe539bb5fe344df06498ecb20f169b2a6c7ffec9c48e23e95772f4e581d678351e51a37ae3c131d0cf3cb901d6f56040eeca76265dfd3a25d85d23cfcdc1ba042a27a2055f792d70dd939d9e23d0c47475314fab9b8e1745f383089c35ae4d3d8a47703a628a76e056b34bc57e918e55f2a5ecd35ade34eca1abaff86210254e51eaa6166506c490d85119b88d7726c17c38878a4c7bbb4b44375fa7ba85d47e345c97cd643e4dc0b +generate_ring_signature 7d40505d96cc9812cda2ecf10be26e35068ef21403fc13a3fe0374f1752bdc3b 58399ed2f6ab0ef8e44f8baabd4b7668e5d9e0ce163b55fde697dc1f57b65176 2 a0f1b1b883584dd18faeaee8affa2076a50d7783086353a9b7d4877f0c23bd55 9c840d84c4e8d918410d5efcfaad94d5be97b5a16738f9ba27c1301e30adc367 bc309edac23260a587d0ead081229e22a1a46029e24bfc99b8d264f0cb550e02 1 d3c0f07b5e4aba145fbb608b9f47c5511b83df8ae9730e92019b0d7d62672d053f904c8affb4366a819b1f057e1d0a165e065e7b23ed0832c0ce4e4379ed3e0a6defa5521d6a72acca6df52aa40e2880638a79b6a89909d86438cfadf2cd970fbdddc6378cc4d783be3cc4724cbb9a36c50d2a086c73253592bd5bac9860db04 +generate_ring_signature d2b1be9c36ccec50df11eb89ccada0a0d065fafc1a4b7296d85180598d0208fb 790464d130c590b15283bdb524cdc611c1dfe90ed958cfdd1c61f4a8c610e971 3 c760502af5bd01024b414aa8e6ec3291d24a119c069e5394023daa44cf502a35 304177d3c97d2ea31afd6600fab36a15763c2a7d6fd2b179aaf9adf4a6d93288 5c01ec55a849da5d9c141a1da2cadd189c7cc4302866056619bcf541c0ff4c0b 67567e52f4f0d0b41c1b633d3ff41eb5a3af5abbb4b71ea61b57dc39e433bd01 2 f6eff52b7d9b962013a6aca7dc9140c8ac8b2df68fb8b72fcab97803c280850ba3df73574602b238d826033bc1cd2072c572539b47f71bb221c2ba8da231fa0a76dc35001b0f3cd38cfc314fe79d78b2a1d18474c6bab85b426d08c1df8faf02a38f035ee4bffcc4babab8558fbb7733f273bd89f6bfb1498fca8ee78b9712000a82b4d043cd6d95314568a8c99cd273e08013e82e891e7fee58757cde825803d001f8a0fe95b789e91a1c8d3e1c935b2000b73b9215cfec31a611282190080e +generate_ring_signature 6a7157bd5b7abb0dbf254d14383ec158061c39beb909e371c736bc053821894e 319ca2b6bf52f500ee46fa4e19e07878690597cea71cfd1294d689377803b4bc 2 f579fbadb5fe3ef087c03567db8a70b8b40670bd8e77c9cc58a519e55fa91265 75e979d40c24323138df595858e567668aa8085c2adae4cd9a72ce08499f99f9 0e8eda5e20345e1e85b44af5a172c57f1b7ab0a80d4323bb560058b4bca9aa07 0 fd80ff060c2556e6d9fbc6cd4a2c201bf2cb045f4df0f0845d574928fd1a1200d65ade85ec207bd571317bd3b141cdc20f8db573d723f95ee87780e7eaa6a203b80ffc056e001e43bf73ed2587754c3fe62a1b0a597cb5c3f2788e41368d110082045b3ec5ff1cf549d2bc3a89b1ec602b1983966b618e74782a506c09c1a208 +generate_ring_signature 2ff3bc41c8184d849397465ce855f0331b2fb8089acd03896ecc40f13c26c6cb 3c0d534bad069aea7a7a99f476537d5fc7d744827fb3c5b1747bd5e628c0c060 1 6a2dd8917bb9f1afe10a52fdc06a7f1a8be194adfd27720f52dc6b68e41dac13 9a8ae3cce36c6a7ce062ca5008b7fbcec509d750a7af1755506ca42987e71f03 0 0078c0b416b2c63ee9bbe3df37387a4b4a62960555e17a188a7e56629e6d08078b8469eb6c8a14ddbb3f5e52b922dc34ab900c2d46c00b561dbb90398360b408 +generate_ring_signature 4de455ffd336973df436d8cc5400f445edaadc720ff6edeff859b49238aa2fce 0d974c6f5c5852046fbb400ecb528e463a9ac8ddb7e4f87290675000e4c29634 6 e119b88581dfc38a94fe46a82590cd9b062d321097017947ab2a039d77723a41 7ed1ae0e3e982d596cc6b1c33913cab5e0cac7b70a22d957ce5b51fb86ce1535 d74735989a7d20983a2adc2f7913caebf4fc12e1d33946c96d893ed9fcc41b12 af83956b543c7ab0878eda74010bcce3c086b748e6ab788628c78eef075c90d6 357399bcc7afcb24242b9a1eb62432c35ae6cedaefd732cc858d7eae7cb92e79 dae830a72a324ab3113ae4913a820523184bbdabacfe07bcbd687ade7d8659aa b5558d1400cf33fa98203b0af326975ef9038e19f64b41299a32a0f135bc2804 3 3e757e2e27abe42b28d20cc72da606aec4d895a6c6a2734f85b6e78444fa720cf30ab910d4fbf81a7ed6c858055f0655f4d0b4fb37041a37f97d5cea13dd0c007b8164d579b377bba70c9bbd4f98a9cef1fe3d7c5c9435daf70ecd1827b3fe06eb17f8d6c6ade0cd14f294b1fdf706208a5a70c0cd9f5990e87a052a4fae80037cfd63b611415417d20e85c1e89740592ccf4a59844d2ba40f2a21ea132205029b63b09ef8f5367272ccee350da5929b671dd56a30e4849eb54b369bb45d880dba19e4ed2e3255c72e8a23feb8df6687b6327d2845d7fc13921e6182f5f18f02e594efa77dc22c4fac2787a3d67877850ecb614f136af268b2b7eec86b04120f17a5ff725f3562cdb4f29a9f4bf5d0b51a3345ec6c7d4ded504a11076f379f051e539a6d9ab6d06cf26bdee71283cbf0e741ff8e10c9a1f5d53750a836e3c00ba31af2d74c3f2bf44a644aecf2510e21e0b4e0ab997ce26c82f2f699835521046edf1258b097204b62c1a070dfba6daf55c67c59c48b28d1a586cd493713e801 +generate_ring_signature c5a69d4c24d9ccccb2b7f24569071ae98acf6e03b925e5a9cc09752d801c62bb 3eabbfe3695bc2dbd5901d017eb2e54b9caba0945720164f342fbe3e6ce66b78 26 f43a0cea4cdc1617fdf4dc9aabd74562d8a295c3f85ed4acfe4042119367ea4c f4b250e120df0108ef321a0ada6f8d56a290ddc4164803d62d92e06524f4487a 60c9eaf854e68882287d9f28241a6ee63496342162b018d85c7e547bd65ff258 d3bbab657cf5184747c0ea16043192f6b16743a2aec28a35fb3e03c78114d01c 8b00e6cde3c562f5d9318949c9eac03c685090b8a3d57ba4ae5704ff3b5d1f02 4e16fef6f635620955f7ca6ff8f3077e146acc5591573501874e0f7ec8a6d84d 1ab363c7267a5f871fa39b58b3f77f132c832a148255fa43d488e145b12f24e6 93d47d397bd986ae573e04cfd6502baa9f86572d8fbe2fcfc29d35a6c55b5453 a61daf279f18faf9f001831bc06539fabea3cf733b10abef58a1523414ba2496 063029c65fe66102a1c47575d374692a459a416b9fac9450b3c102494b85b5bf 095fc13758f256b4550452a379a65f8d1c23e670cb5904394d153e4f9ef6f0e9 8be1d43efa5494cd054ba8f7a2ab134fa778dde275d453564e2794af637c26e8 146719dffba2869567d19999455814cedc3384b8a104dfb6512dd434041e8f04 8c21f868f215881a4d8f8ecef15a45ea9c5577c96edc5f2db93aaa97627267d3 c6cd4788daba63903038377a7748208b58351ec9384c96fb1c85558063ca8128 3e5881dcdd2d29e42ce5229d59c8427d266e9c0bfc48bd849a22f9c563ff572b f2ac153edf0d83e0b04bf20f7fb6c70881a8079af523710983e8aa89373700f6 0bd89db8a50f485379de7737d43b3f7899c254190c41f8ed38c1b82eeada7554 50313db4b4816e421de0dbb01a534131546b64539e2ff471e63e8b745b825c5f bed722400190efb4c21cdb4d76679ac6ddd56ef850ea89f5ab43f89d4f2c02e1 c81d8bb33ba874db043e3d8bf303b5790192fea6459adb800a76d48614937fcb 01563d30080870956b47ac0514635364e1c5f8d45e62af0895c405e96be1516e 909d70e1e8813e0ac85788146a836b2cf2d3ade2d2c5b18c9b08cc41b688990a 2da5ed39c9928eaf2ba35e5195ba789f8a01e99bd9bcc55c81c5b1c7bf6caa48 4668235408de2079f6d3111e96e502117b10d42a5c0e7790e9eebb7105a792bc e6574c97ebfd1ffb756f4b27c71b787e0c332b830086081ba57cfa1a699ea613 66dfd1881a176aaaf91af0a512636d9c0049460df4ccce64a4f7527e2d0e0702 16 901d6f1e717123b75b9ed1164ea44c07ca4131f1cd2799bee2cdf29735b0b80699ea5372d18635fe2bfa3c950e0482427df03d40c4adc50f1db42a3e94a7af0b0e479064193f147db1ab422c340c6e1a67389d89047f8c5dda887bc78600740094716c074059508d333eece20c516a628e3deb722df58009d96475639319ee03086d26db422c9b7fc7036d7406746a8f368b2e903609b930fceacaf151e9980f9c58de852374785c2d3a14194bd43f7c4833bda2260086c4c7e594be2326b60f05cd27166aa69154f1972251606ac671751623931dd32f433990ba44f4d1570c266e1c5464a21b0a3c5cf66a3a9c357b230ec6be5b1a6df533746482fc0344024ac8e69f61491a50861e7f17edd012377dab92f11283a46ba0540d21221e040fcea1ea30fc16bb84a302260211ad28067189996e5338433f3a181eecddff9c0ddf7f4ddf64149b69e73812adee8265368fed21b6c588d68067b4886c8fccdb04f304fea744c0fa2af7b23b111f839223ed247641b8c00262d506d7391bfa6a0c170c14f5602762f77f5251ae2367540d3f19b8b22eda0cc374fda732eb5330001fde81195a6d12e7e1e00c2148c308a11cbf26a65dd4666596933653b33adb0392f78f4ce83591266dfd94507280528be5d1554bcaf86ccd6ab62ca2004e39065a79b85ceb47600ba9cb33d71248bc42e3ce447db2b8090958704907b6871f0ed7a8e6c47d315c39389d75d7158bc79ec293c1d7725aed037f7940764d85740a8454e9f1de641e90033843d67a19938db831833a079a483dce50a34b89c4bd0f92061e9acaac00860aa556e3a6d77c1b4ea36477547b452dae3f4657f798af0cb14870e5a6af4a77aedcaacc8257299a3ef81682391dbefdc7282b4f052f550be7e0bd6dc73e9833c905f7db40c9773b4f658564bc277d683950f2ba6871270b34880168eb8805be7b5c90d0533c811bc406711dd529b77cae3ec93566adc407fa33cddc6703b16a4c1756ec5eaadfb52e8ece9af6faca60bc70a79ed295b601caeb1f563e667145f0dcf626425e721b1aef9321100d56d190f5451df30eef07ce08d22b213d1414c3682f8baf9cd4cd40a6aaec89e8a2e2e823c18eab98b90268308ce9b4e55a45fa6b9e67eafb71b9047a6730093f4d82ed71ac05d96e810dd79ca714eda18deeba0f7fa515725ff1c738c9095abd384044de3a27fc7b21006d15b40b55340a94c0e5724c85007ce4787d37e2e08a62456aed9b79ac8ec30224d011447b9ab3e45c36b2ea8c233cf4085c6d72b46f6dc461346d0f9e802104c99c0b44fff1f27e8b54c87b12bf8debca6c534c83e4bfc1229d25ea5b160208393f9210e4352ee401641b6836d47fab468c34b6d67ecf3cb35c92893a361b07d5ad4a9f3b8378a3f1326a7d0455073aafec91eea2343650614f5db977f5a50a4b4e5a64480b85c8fb125c99da530baf82b051877a0f0b4a935792590e2bd109b570aed7260c79852747d0a620312110fbc1d67d5a5add8dc393147375aa9c07532ed98fa2cd1ba0984b4ca47ec6c01376820985bfccbadc84feb67c5f10b50e801fe2c66f1a6d106dfa416b010fa353c656a18b3cc9e7b67ae64f32993d8806ff36c7d0fd9dde762f0832fd177ff6abdd023b79820470c4d06c4a216b4e1804d6e9ed4a9a30ffa11b933d89b92fdfbf8b1e7e103fc8899027046401d9e1f905652e03458b59fa8429d5c40feafe7c20cb5753efdc6aeccd55b0e32884188602f170783a4505fe8961a3fdb8698eaf5311b5d4cee97451db95ec6d835640eb07fae7a1d5b60721c2ebe336577fd0118b3f8cce7a6d279bff09a9259d66212408bbcce120a2ce1dd45d9c01605a6373e7a9ce7ac5e8f756b21b518eae1f128f05f95d22bfa3b4858e9dfe7adbba447ba09e90c7438b24c974c921b82b8cba310ba8f3b6a17c05aaf652604c15a2d17f47915b4548a577bfab8c5de6b9268e4d09cd6e5fc3b4d694d872392983d4c88647b53807c72129c03b429967bacab4620e38b7a6994d1451b0d1bcc3694ad5a98cbea059fcf9d26dfcd87c9b2611c5f30ed80f22363a671634f82ecee8a5e5735005c1fc9519b8d3cf79165654e21fe601339ada11894cf01e553a893e8cd3bcaabc870bfb20cf5bee731ca7d95f077e06be71d6c665af686b8940e4a5ce7d7460f70d35812b6dddd2ca1b92e3db3883094dbbbcb55a3bea997e9e60c4bd80499eba49067414f21156aed234b6c1a69b0f30cb8d708bf34a43fe2d1bf764e84a2e22d23995b89812fc6d1b2c47ba82720f7d97c279383135c3876c1173f021c312be46027a8228e931a71ba185f784590d +generate_ring_signature 1a25025edeed2967079043c06388cd736a6305609ead156e76233de39443e6db cdd7e9ef74c4f826659baa13cc7027e9d3c0b4379472adae05f36e99ff600541 32 18cbd5ae5ba735e8ff1df944cca9ae3bc07187947d3a0377811c7e0769cc46b5 2eee82ab6674d77e15df8e3fef6d52f2159d96f510aef9f03a7080cc1df8a551 8c891c48ea8e5578faad92012e8519be5415a6397f92493cf368598927562aaf bbeacd239001a036995137abcd6f6df6af0d42517532c177cee3d774a14bd50d 6a7439d1ffd71e17552bf2d14763bad542aef8690dec9a35e7033d941df846cf 99d0c3c86698a62548085e50acff48c7142218ab6c51308429e5d516d27c1e2f b14165aeef86209407ffbfdb583485fd28f6edfc09bfb30000a69af629660478 a1b08ad9ac3f2fab9ae1c44b64c1f8c0d89c7ccfe524220a63c8fd4b80355937 c6f39774cabc63d8a858d76d2b67968c57754f6cf539a0516ebdc2681bfba6c8 e1420e8805e8267cf5cf14ea411f178feae61416ea6097c2936cf255d6da75af 68f36553bc4e176554dcdb4d40911c9d2fc1ad535ed5de68e61214740973721f 36995a5cc2d48acf783e851bc82a7d33b39e8d5fa98a28d053496431f62f4e8e 6e5a2e1e833e72907908da061752783d2bae0fc8ab5a5269693ad104fa23a541 e4c81b9504412585a70f804cda57e30835428f9e7b33db9e303cd8280e1eb133 b21e9d751bc67faa423ebbec6bdeaae824d66f1fd287229d15202ecebf972ada d85dfd2d4c2d681e4da972cb7ac70b9472b5a9cf847e51c7257beaf9a71b6c7d cc68be54b9a80b51446d0588e46f314f0ccc495c7d51e3d271737e76a465c33c 34305ec1efbf3dfad30fcd4e67ec2b03687d889bc2cdc77ab48e0b5ff9cb56b8 c1e4997cb9f2baa62de3da1f39b14b0f81dbd2b5b1458d4017e9c3d78a24d406 bec351ba97f26bcb0b2e4c2dc0c42db166d281a7907971bf5087eda6fd4c7092 a61009cd579acb5a183f65cb11309fbfc2c83f15a8e9a3733dbcce4bcc9b68d0 4652edce62b97c125f033318f97c9d0b6af10e054d424e0054da0892856f8a5d 089d6c08e5cda9afc0e25c942156cba8e1154aa6e108f7157b21beb0b1dd9c71 7e67864cce0de6e0b36e85a0ffa8839316dd5257f1d43a4bdacb63723937940e b7924cc3f1eb11c19f0f92c4328e485ff5c087f7cd65d56266882437cf36b3f9 b90b30b4dfe9bb2e74a319d3fefdfe16dd466c5f304cbc7002c0b406c75f6cf0 f0e309314c00e1052b94b8b61b8215385eb018d80f7d416c7ffd3e303c8355f1 532eb3f1f94d0c51c3c2084e1459abfb07ef8735edfd8f0549904a66bea4a6ed 00c8322013b025dabc91953fc9d2c7558e2d482f7d3befba5ade16bcaf12e2ab e073df0ab60437b296be99f91cb20f4e2739ed54d3f387f1bef16935a75c1712 9e3706f7488756a2813420361f139e2e8478d77a52a07e0e3e0f2f85c873eab3 021fe8333e571ffc9e67b99ba619a42450b5c1fffd3139127f63412b9464a57a 1bbbc450a6b3450d412637420a225f94f6a76e0de606ccff43a749e255e7a803 28 9a09b789bafc6604a37b8a0b0b3090ed02a8c886f39f2bff159121b3ef0d4c07dde6fcda2c176638fd5d08c1678cc8becaf57c093fea4bfbf9f173918de49907f8d44fd477ef07fe43e5bd07c6cf2f8ba369c4656a5ff86389e928c62e346b02c7689696549f38ce7c9461a11405ff59b813b7fec52e8fb4e55fb6d3511ec5026378c74d73ea9a024af2c3b4fd885c017914ce7d0b0258d3fbab210541068a035cdccda0df52fe83ab758bc953e45096fe20cc575305acea52cf30aabe9d5d0fadedc5c16bc5be2e7a419f13a525ea2e482f20e14ea9236f59fc9bd5f8fa7a0dc7b7c8c45ccf354fa2467a91211dbdb2c73fbefb9a33fea234add273c2530009feaea50320102f4313301b882c080d2627bbe5556aa6b005044aac9f1008bc0abb7d0beac67928a451623eeb09db18332e60aefea1d07c6e719befd54da3a70f0e5d2b7ced91ae5c620e7e50dd109244bb2a49325972661b0084a3108969940197bc542f896909ff33c8eb4afa44019e9e28408ac3082bb41a53bad633f33109bc54c7dd2c34eb8f7d222d5ed688e313581673e26a294805b035e5eed2dc5d0cb3170db160fdfbca232a8f216c8e2c0adb9dc52d1411c42aa1b3de2746e4f90d6de5785c50f671b158f1c383829f4a9c7ff02b9db0deb49389e9a0d67d3f6608def932ae29df5d4e7ae793d3d37b31d59dae9817123f2263335fbd5d881f0e0029a5d982aa0f9f36bc8c43a7f73d745e198f90ab5522461b5135fba79249550ea9af2ec14a4412c0a36db71e72071e47ab14770c29cfac535b352920ee4e240c6674ca76be39e63846316afb101602e3f22d9776b7f965796cd310ebf1fcc7001092d4699182ad99b836b48704d663c493fb073643f30a50c3e982fd6342bb035f41c3d846aacfd45ea0584b656720fce0178cc9b55353fe89a0861b5e30280fce01d5ac0ed99428b2a20dc74d37f6036282eae971a2cec5dd4bab5f040aee0e4f4f2cf412baa3f94416c77c76e2e2d60287b5d4071dc88a185a83bfc60f010be5aacc7c4ecb42830f80d8e23e141c3c556335e0fe4ca52c9728a0d5554aa90fcbc533ebf11e39a5aa2e3c67180502f8b84972570e44e38a333ad9e25dce2302700eed4ca6d9536c579767a1d017ed28129d2c11d5d5d838dc73ea9406dbb50c18c2b2fdc8051a596fcae1b960a8a80b85ddf8b0b630a55dc9407d1cde281d076bc20384046a5dd2c0119c247a0f54bfa4aacff29218c46e6784e26962d0c50555775330e97640982447796a3c9c780e394111ce1257e50eaa8318b76598010e6458c00d070ea208ec2c6b84ab84c9b96056f2b6b806d13fa78e5fbef9fe9a057b77b3d089f06192eb0d97c1255642f2a101b7e2bea51e2c7243a257717f0203981213bb89ceba129d8b3da69b3bfa361e61d27d6b33b2ba5a9640365583fa056eb802e125c4d8b05207c11a6b63c1902ee8061d0acfafec338ec7ae405f500a0c948176248899ab96289407b2a0f9fdb7bde951c2f34c0959361d71e1dab30300ac4d93cd7c61bd2aa60cef26889a61de24f2cc526df392671d111f047ffb0a2e28074dfe315108c299dc32f2b528345cdcaa91553f580679ecb9338625800a862d1a96c86d14e25b920cb996b706875b5dcd58c2fe9079f3a229cd63506602c6ab0a980f23c860a21d720ab13bdb5dae34e4bbdd2b9c21855e113f8bdede0d63ba000e6c5f251525672b99176229dd7246d3238f89ed34d69d7cefd0198d03ea3658ac8fbad80f44b1602df08369705712cb7c5818f87d4063a27641eec90cadf9ca154871222da6e60815f2ec08ba3acc31c21ccd3357b348450ef0361c0925f849e754a914e6062c1d96176acda35fc6f639d967aab7a825023a036fa4004cd7d7cbfa9769491244409616bff61dbc200d250324eda3063a32c9502f8d06a26cc8eccd445d1a14abdafba0635cac7978fb18fa7fed7e2e31bb4ab9806606585d465766b44ea8fb5a516cee70fade1751baa5d4bc3066a8a90ee0b7cc7402ad642540990254b86f08ea45753ea104baff6b5fd57fbc2148f92226866f1a0ca782835dcb7599fb614f1c26030e7b2de088a2bf56a95adcfcb297f5ed63fe01aa5b95d13cc99579c7e6e8c4e502d6887bddb3b2c5f6fce7375e73070f91cc013085152f71b4763851aa83d6c829d7f72a2e778723d89ad3e77149047218ff077f3cd92091a1642c25b02c581a5cba5d047e7370add238abf3282ecd76946308dfee51a8914216eeb339d4978180dff40847869922f7f040344a77b76b5eb00333595a120c628c2ff92e4edc44b6e5efc359e0024b2d8459c1559149ce1de805e4f057399d93d47404178649356289c7d9fc29893bea6e90afb83ce184771b0b21764b1add18068b4e76fe8f73138c2a66fef7eed06d005b59adf5ddea68f60de53c378e744ea3bd6fdca265dbd761cc641698204937239991e1cddb49b89d0ad558fd3a776a9fec7b75c61fd39ef9c543749920a08dd8b89333ae168db1d6083af70866c03d031b7e574b477faa220492f0bceaa99a43d94f3b54ce79121d0bc1117ed2c1b3cfd2077fdc0ca0b3e60d9090d336e58c41090dad3e613889d40f98607e291246fdc9d0dfd5723e56f521f5c1ebf56ffc53e498869b74c40dd106018fac9787b0a836d25170986fb2a2830d6341ed45c99bbaf4b1a3893b30ca0f95b4962d0d5d966d4b06fb53a9cb0f99fe8f2f07f8175e9f16c6b74e3c5da700d650138abf1c55a43e69fbf2c9bf83eb24470ec1151baf6a1f829d51ae73030d9d5cfc6390d49932e4cb31974aaa2012d4a3a8c84588c01fe1147e463265990ff26720d70b4c6db955e2c1fd1915e47c7089820982c5bb81a844c8e012ebac08 +generate_ring_signature 06849cd0519c6f1009ff2038da647ab6c1db4158bfee7644db31219a32de2265 5ab33b23374071466d658f4931aa7ce87e2800bee54a77b3228c07a8d1f9e4f8 50 f4dca6918c240e30306a9f94dba59fc51a8ce538d71996d1b1ec029c9b32acc1 3cecaf5af6facb8438a4feef5999cb9fdc28fd4e4ea8ecd67ee057c70679a00c aac24c3bf580a2047cb608e7414c28ba41923980929864f3a59c5ddabe1813b1 178bc14f54597a5d3216a28b763597cd9593deea8e59a5e4ffab0d3af17dad67 cb648c43bb1ea79463c31058908db6360ae89bafede111ff9119f619ad40be49 645417d726d5f09e7c40ba1b1b1e17de4b445cc6b443f106ef8cdf20709de226 42fe478f50153993cb0de879050356c81055b30b04c773d07215ce8a05e86b9b 853818c89f9822f8b5bbd1116cb8fb192b49db52881a63fe91be8828843bf49f 43996d323e85feda01a303ec77bcad68cfbd45fa93ec6f52994fe002f3f63320 e80f906975529b458402d90e871d4fe391325a3506880ce3590640c1721663e3 249d745a3bed28cf3ab726767b78266a4f0df17ee72e4c228dd4ad9363198416 127e4eb44765ebb96929285776f2f28b8b506fd2f802709b960ba5122386e4da 47925a49af8a140f389594c6b0f58c0cc7d1e9b2ddf8b4b19caddcc3c191e66e f2c5333dd0c1fa1b3bbeb8f76e0c3ad4d90ae352e5491733577f53653a512a47 41ba603e6990ab4a6b38194ee651f33b1bd811ce9b415f738a999791e9ecc08c f0436f4175cc6fd250f1df326cacd940d9554ad61ea96fc21f7338b5314c0f0e 165eee723e8a32ed375e2454dd569b0a94ded444d743180b81883e7faa4a4f13 a85e2a7516551b743a2485984fb579351503bbf3ffe4a74a04877f9dcbb53690 e94ccff70bc813f73e14eae7093ce6610bd111710c284aec75a04b0f4e931fcc 03671c6ea4b79883673021463f66222b4aec7c91f5bc03ad7efc562fb6fe2db5 26a39e79ccb667024f0b7c90c1dd724da2e3754d810cd5491eac2b3cde14a1be a250f4bf3ac3e0b8aec3983660251dc7d6540d59ef7d05ee4b2c7412c166c98f 914bdb7c9c9b0735dd1be3f60171458b8e312e53ddbbb23e541660bb31fd1a24 a67f611b7bf550f4b54065c0c77352d3c49a3dbb71dde20777788eafcea84513 a9d5fd6f07e027beea34762f024615ff38a74cde6b009cb1795346607e5c1ec8 7c894a201bb20226d380c8b58342ab7b946782bed4532f9f25c40f639f1b1312 c360d3b1274db4fba04b8c6db76de6d52fd4326f7aa8ec955be3c62aed7188db 2be5959d53e19c49a6960aab64e2488996addfffb2419e3c8cef3db1f3c33a35 ba81236a84fbd39181eea3b60b1a85d6947007cf42c93ac15ef7a2cef6a4d3fb 8de16d964a3df51e199b8df767470f4508ccfccd27b7f6d78b62cb9398bc55ea ef9b3fbcbc282afb025ca8db586de74a30264588f5f1060d243c7865e4c18c6f 4dc1a34353b429f5e1811290d12d6e69f3485e6af9b389843aa1b5a067ced872 c8295f4fcc4620e37171d648e229e07c0ad3cf81fbdd49e895c08ca2dbb1a73e 39ed7eb8e5da66c5b688e7447508a283c96bba5c55ed8ec000e42ed4872ff145 4f7f18ec1e2eeee07658a986008ba1086b3a1f030a0dffb339099f94d4c8f3d1 c05959888131b964bb63476d63904d04bf1e62538e8c227fa6d6a84b1b2c5755 9af1683e1561c8c82aed8dc257a5f301eeeb76d538cb62c80d85825c3b58840d 58b9d74270c39051b00080c63d5d306b6c7e8ce773b912ae34cb528723b31409 75c8f3e9399c3e6a233b1f511138af008476a4fef6749b0d039ad92edfa6cfa3 8f2d018ba38454006905612074115d7f52f021de849afb63834f621b0cf86813 edb4523261f4218904cd2316470a97269c10b7dd6278345fad3c78508947624c 89ec4715a6d64497ee9825ec916c66ea04f4f6005f2c09737d094667e0e2c134 60d872a3dd8bc3dcd1e620a448fb2ab021b5dbd0e42bc54fd1a22173b7af796d 7aa0e6c13aa26d3cf23d32b3aac2f4b9f07dd8975532d326fbdce20cd978b271 f20616d23430a3a6df1fec7eb6ed20ac2f5f4d17cc4af342a92928939bc220c4 30ee3a8103f3b6869b385f54aa458acb4fbb8225b8cb1ba1ce0b313b9ab32830 d2c9241543810e19e2d25fb2b82f1bba7a7da6d72093f8aa35ccb1a7eb9d1271 e716caf713eb8e1c3f8c0c02dfcd682faec217cd7fb0a872f9a6e498be2d8f1c 8040cbcc375bb11d6c4da2de3035a95273695f28b3f766a966966a217af215df 118137a60cac4033aadd331dd3c1d1511d4c6b3ae41b929c05a31252f43d097c f28183b869e0e416647dfbdef14a6e7b1276ae55c760120cfb92d5333cbb870a 39 e1eb3a460f00a7a3435ca40f6a5c7ba8ad03411f4b2e3bfe94707e44f910cb010ba2b6c108d102c66f6a4783a06bba3797057881f17856a157b2e4a2393f3f0d1fa764523f363a7a8b59f10dd79275107acd1540775918677463e5a4a934c801ba7cf231688d9507794486dcab53bd886276e9ea13a2d7740ff0c387e2dcc104b532e24a39c6e3fabe529399597df3e85e23797eb505b17440c9f227625a6f0a8b73c9095082b50deb4c6c105a1e9b0bd4dbe0384eeddc8ca12f3091f1aa4203cdc7eff07ff6f27a0b825b192859be9fc6f381894fe98cfd1034325c04c59602e927d948965309ae621da6e167be1e3f2c9520067de4ff6cc2564691c6038507d3c8fb09b10e96e7a8847092765bb5d6d38aee5d39940608837be71cb24d8f0fb564e354e6accce164bd45273ce198c0d0c819e78e868f992170b0efce6bcd0ffdaf09c94b129113f8c6ecea34f30fcc76383ed9e7978e9806395ab15201590d40aed8a7bae628dbe33da05152d5be7d1d30b634bc98951bd7ff95ffdf018a004375f5eb45f5d940d98d8d1a22eb4485c7fd0857dbbe18805bd2a255f22dd60d83c26dc38179241aaaf3c92b23b61861c21d25ea5ad5ed07c471bf8b8f1bbc0118754e125914f3c2dc92e05af909dafc5436880f8f2f6e2a922a39cc54b241079a9a796de6fd96cfc3191c5429d8ded0136737462f822287c7730aa972155104a16f8ff9696a14cc8c83ba3dbe17fcc453910df037be3ed3829746ab68321c0743b9877196268c16f9f51d09f0f89f828f28ecb171aa13f15791399dcc8ee304fb20cc3b23e3330c442460b809468688b23133bb28a1b9c6d45ca8344ae7b70c40b7c480d4c1beed41d66326fc8b40356a285cf81876230fb2963cbf86da8b0e7f40fc5892b6103f2753b496fb65a4109fd004814ed6814fb8006922f6633c0e91bd34b4e2150f9ce1b5a264f3deaa5ee707126736cece2455eedccaff4a2406879de383ccaa21aa76bc38c9451746cd1de6152d3ffac10aa3d8d3880fdc3308b775283bacf20df252f0b77b1ce0dd87217f0c6045cf761e81733b3be4b55f08c14a19ffe72883161860d83ea44d0bde966e5bcd8cffa6b2d23cca316121ee0d544955a46cd3ddda4adaad6b3067c52e5cf27a47efc0ffd238f04d1725d00e0c6750a28bda45bd6a7230ff62057e561a0798aabb90297eb9e6ada53b5b91aa0aa24b49b81877ed9d0496fa6832b40d44872607d2f9f757c64e1b61e239f21502f7489c3fe2a5c4e0503a37b880f63348a81ffb1d9f47eafe2f281eccecbcb601e632dc45b91aa739939f67ed85218e974b0598c729a598770de520629d9ae80ed53c8be585b80456e19ba160fc9403f5444133b54ac109697bd25f524cdafb0d7cfc6579663cd67d01806becddd1e41f603cd71a852f443c66f5accd61c8c901d6574233f3729ce9dc23b251e345f3c9e26a9fde480c80a86196195d820c8d0edb3d643354db09efd245fec2b5fa87069bde57dea4385b6a30b8125e7576640629685fefe514a297bb3fc1705cf630540cda0be703efc10d14b3150deac7fa0dc2141ed9e2f0231bc073d74b7275991df189ba67a6a24136f11c0605bea51a0e88a87a4b0b732f296480abe26f8227febfc48469b0f25d5f89a23cf2c661fa0d363f0720339f76ef75d0ee7113b966b596c51326fc450d70e44450c6a623820f06991c7c896c7b0091ae83b4a4fc277e1daf6c26bf7b632fd97d8504f6536b0c4a0112c1ed00b9efcd4c088146a13b555c9c682a49a2aae35f0bff031ab070029a3d43f80ca442eec5a208dce3b9107ae2037c2ec22e8925f2308b0fbc69bb0c00ffe0135414b9608090d1bc165e7ef850d001a777bf7974b84e79170ee6630334a814a7eee3a48d127c1c4d0bb530eae9cb0bedeb64db6b7334b44c085fa601621177ba74c7f241f3a4bd8d6ed7e47bf25743b07dc9bff7702bb6b3a0714501d6afc4599903292281883fd892d8372b724bf3458c823300b6937bee4a4f370efafadcc48b857117b2a637573ac27b223f5d6771da6bf22d49ce4ad0253e230214ed4701fc50d479e6357cea6c893116486a7e892e184cb2f1a42b98b5cc3105b4a0ab8acdba36d8ad4fd610eed13b76770f511f8a1eeb988d6b69a25b647e0e4629d9ebd3e3aec6c364169cb13f4e4f10d4a98b81ccda56791a9d49041fe50528fea8203bdcb4e758fbca191e924dac1f617a5c0466496bc65109b36a921b051533fb23aada0dbd4510e2c8800cc147f99ff12bc5ee255e3412facb2987dd003244b4896f8dcfd352680d214f6e0dc60e85c0aaeaaf45c2dec9b9ce6a0fea0c1438990d3ce1b70560c2b97826ed1660a3430dcbdbcc57527cea62781072460bb1fd41765a53a113692f7c3f4b99535f799ad7cb830a09073b24f11964cf410cfec59953f4691ef03c31ba26e718982d229ee32101bb940242ed7df0d5a6cc0a38dabde82a143358d4a6044e678583b62509b28a7aabb864b550c2f0a332a808acea06519ae5cebd90099a96dd6b75d7885f4ae8b71c1f60d107b43d4a13910eaf9c35857b1b065cad69e683c4816456db4398c1c673035a008bbf7c8c029a01336a4b17d5c4a57ef76bf98255d20969d6843dfd2bad7f42aa8660a0e6f239089828de12fcc34680d55d5980c1a934437b48d0ab4875e8a76550cfff8eebb30093d65b7c311961b754df5e33ab418ef81920fe162ed0d23f3a610acf4d5e810a96c9fd97d8a936eb7412fb34580939efa836d249450ae728843632141c2ba103573c225945b429ae750996a64bbba856dcaf8d4ef290846220130f8e8349a5037168959bec73b4b5b5d82980b67af5ad9994ae31d0d37d79b015a788624fb60686b71a13ad639b7b3c7b6de3e8900b44b25187f7f614b27e14a99bcea2070c0b0505b4f1f922e00962683ab13e083bdd3a38f7ba35b0b3674b2b98db23ec9908c2372420594da0550d0066901936b886315c95a867b60a77966ba9cb2c876d0d5fce3bb2c38c39099655bf1673658a3f805e60c377a74df65d8731b88138cd01d92476d6e246736d9149686fe8bbbc0f551561742412ffcd02e24aba4ed1f006cff469eb95d3187a5b29dc863a936f16db138855d94d6c5db0ff7e88b8ef93038d3768aa6a3d964722c39b15f03c92888d547cdb9608eed212547a5fee785808df6dfe582e0cffc872fb55b29d512af69c88b90d381d28b4df087804cae76e04b927234355f252861eaed8cd00c07042e67614948492193ebc2d298e0a956902b3d6c9f965fcd053392481d2c3922a52bc779e321cd8c811e3157ea73c4908003b691a43646a250e06840a88121069912d81df4b8ecc2aa04231cea7390c580fb445db6236ba09baceb89f34686f1d900e1a4fc6a514a87b596963a6efa42304aa6e46a4e138bf34fdff9909d392113d4a117e6a6df512c3069a837f4d41550b6bb4ffae037948d1e4d5642b68528d8f5685e284067195b5de323cf1a9b1c706758c384d2234e66ccf7044f94e1f4f96a240600908a72f80115a7a715782cf00564d7737337b61120ed0fd22304921384d1736b512ed7ab0e287b25f4c48a307f2e4c1d76f26bcc19f92a910ae50e9078f24a10b3f029db1d59977baaa370f0128014b8f78ebc2bdc6c4509f94d959d041f6e8dafb06796be0ca8efca1030b0a76abc3c2f5be6f1311b9f2524ac6f452e57b374fe5ad4199b70482811035e009f2d4d6c97f53f26110c34a6a7a4ead4eaeb0aa8d6931cb3c9579f1b68b0e3b0dd7961eb5795a9e882739c2cd4220a5932af7e8522a6d58360ff91bb02823210d58c6779c4390b4c7c662243b24c2d79645c7c5b9d52e9a197dd2f4ab36ebc005dd5bffade647d1e77d127edff0e1f651589d483a8db87c7e5f002cf25db63b0b6ecd5140350d792ec8e509e4f6f301c8b7b238d8d21601e2c49c5ff854b3d4025902c35c1fb4fb9d0f1f246923f69e8b3eb2f86b7796714537357b3ebb92b504ac0dbbfb776cf9166c08abc1712329504be6b823add45ba92bb6f0deb289a003ba2f3d381a671e36fc1997532e07164238f8b99f4c8e765d3b5359ec9515be0c8dd6fbcb094ecc7d699f654cb6ec051474942756277f9ffd6bd2e47d0b5abb013e86940468337f70e113c1410c12019c6af7699710fc267956cc8278f8e256011d2a3a2ea94fd34d9a3c2dd7208e2026222d83f7c1a8393ecc66423cccf7b101d1f4db2822b707556bf76e3f91829f0fe28d25267b22228724b108bf95cb530f3f07b8e289fb12097301d2f2e68a18b9301c1aa90103e4f5a32c982aa7d0970db8703bbacaffc6d4a0d9204d362d1982cb42eb5b6659faa2abb2933a7295d50888ecfdffd1be43cbde1006bc5bef47b4ac17e5a0c4ff69d9965520820dcb1b0465f9c21fd07faea80669fbca108ef83f1abe51240fdf0f9685087930acb6e104d6d59d0d1df2a6c00b91fcc7448a8327b621d5556d5efee934b52559821eba08 +generate_ring_signature c3ed92bde9e8446efb5b9367d42ec2d1422a2cfa450a71141a7097a49688df52 f0dab29e41e047f2c8183acee8e0708501ef4ef0ddfa6e7f17a9024b6ccfe877 63 da39f87cd0110e4eeb4c3569349e603a31a6fb69dba927f28d18ccf5cd877d42 cd374e7929e2a64d6b9be6dbd9cd599d84d20f7612ac652f64372b73f1877408 fe80449785af9cf3ee7a75a83a43659c6f5ba4c18db3270b87fa75164c859f0b 1be6eb1f5707cc1869a2e6ebdaff53cf48ae08eae0a8b1e531b888542cf293d1 8ee5d44e7badf8d5c406edcdd8c530b16e4e0b3b9b9bb9355befaf2efc077aea 3d438a0fd481a0eed145afda32e3476b76ac92a006fb4fd7810ad329a29b858e 6b76c127a388232f2df579062c1bf1a28528be805489d4f2705d66123812b2c7 680039121a8a1928e28f3153cb1176401d5caeb5dba958c24fcfb0474cd4dea5 a19c49f7d41672de74e063361060426d2a8b882349b92bfc808ce1e61ef12f11 8303590b8103b58ae2c77bad446c90df388ab2ecf06baac717f0afc80695caf6 562b47c8f013f3b19c61ebacd5526acfeb1cb804935d358f7c91ae04c5837419 ef3a80c7141778f0a022a868bc60312f647d43431992d300194ef4ba3c35d438 79b1823612b9bf0aadaffb6084fd2227e72cd3d7e4e9c01ed1b49a6676ebaa48 0dc8aa6f08a8927375135075e0bb400446e6444e0e8417e6124520eddf59716c cf05ad9b936775c640987b5c24e97747a556d79438e8c971026419d7d570ed80 c2a77c27771f2b48d872ca607557e68b5209646fcd51d33bc2e7354ad20c64c6 bfb6f453161cf18160d66e15b35b0cec92e8ff6f280c7f8a3f7f6c8bd7c43ac6 f51fa0445e7aea779340cf8be0bbf08ebd182f58075bac9d6444c6ac43fc2f5f d007b1eda8d4fcf518760ac888c98c55e95d31a80f60dd2b4ac977b16ec218a6 621bad8defeb944bb8d45d8e639188f7126c575c99181c490020483bade02400 4be49d6eca74a476c53ea0b10339efd3f1c7b3b7ec94ed1231ed9d98d0c06535 e290e3eb2f3610c491129c37300cd2e06bf327604430c38e4ae74c2378521c01 404bac8f26b8258897596efeb6a2038769e5fdb5bd9f025879e62de6099b79c2 d831751f5e3f66c566623b38c3f42772de731327660ad06a3b074007ba95a0a1 1a82cd19f29451407682902139f2c23830fc162b921ae2ae5aa91829559610c2 15d0830fc658ae55e4dd3911034f674a1a401f09b7cc11c3cc6f4d2245dcaab9 f48765e2972e935d57885af53bca175a9f7900569120d1fcb7986c2f42b3a305 e3e2b896e6bc92b4079ddd3a3490f9c9fd0e759fd4fc2709e1d742a6196e49fc 02c733dd69cdf8e3890d006ea71c1555a8b6407fa2f3ac6d967f6e43db4df100 42d84b667f691ba1f585b49d1d5fa029da5520b0686d8798c592ea844137b3f6 5e6605e0f7abe6131b382e6a8759c4ca5fae77ec8291fa878935962ee02e7023 43db8314dbb6db17309b12392187087f92934bf63d7e052ff7df1dab66251e46 c1de18964d5b564728cda7674a983d85b9e61af2a5012489473cb9bd048de0a7 80972ee2f028d2f4b2a15800a16152d42bcc81ca3c2437145bfca495514cb69d 83ef50ea7e02045bbf4f158d32f4b942284e63e5f2b780280a87703e4f7036f6 87604dfb8212ad16d491f0d56d7b311b4b24888b360c36b5c1a1129db3f3a8aa a3a8d82b787407f866a125572c62efbc1da5db0ef23fc74562d4390c68aaff56 01fa56294a12caea1c202ced527940cc24c4a89dc8270f49ae2d18e842400b28 9f931197ff4f05f68b25ae4a86de55987756793579d84b5d62de550150164c44 b61458c7d49003834597f78d4a2592d1fb486482e8e7bc3c3a57f49ef2b8ac42 d554e6418a72f0331d05757d200c3692558f334802719b53927648acddcc3351 cbed989db9983b608bd23cf0f5dbb80015add6f0b67b1b6774dfa96e70d506d1 795ad4295025bd980139455246590abadab9203a035c50846e4e6bf5faefc729 75b50ce254809aae0d537d54c0156197e20dd20c84bbd9a62e2e301590ee0e3f dd8006d8f8db4216f3a0fdc27a872b9f2d16c511d450ef8ef9204f10860d8453 9903457d3aab9decd7c2e07202cbeeae6191259e348d0e05fc8439f06404ac53 40ef0206e8abb47158988a82b39fda91d6f52d592e49ddf90181bf959be81da7 baf85bdc56b8033c1e262e08e123e5353cb7f7ddb2b7ffd74ccfa529227853c1 a4d2ee759a828c54e976a8bbc55294e0f971cae0f30c4ff2fdb04f07de39c7c1 a0d47358f5ffe1d742a0eec679ec72cd1481f7190cb0aff2baaa141b68899de4 9cf118c576775697f38f65e1605978c2af376fc5e87e18c7438b07b48cb4ee82 9f022677e170ee00cb689b13de994c7ae9f5bac789ee8c8ae1e4c5e7e179e880 9064ec6c92d15bbed8c3ffa9c54dd4a71c3e940373033de467234d6ba7e863ce ed04f38c3d6180703052c3cb1150e685ce390ab1491c85cb317533f10fa252fb 5b71fdcbff0fd2e0d68d25f03b750e7ec3adf0c4455b0e06bfeae8437cf746af 7bd75fd7e511a36f6462dd1242f4c53db2b6fee823cb5bedc26bcc9c3bb52372 3b403b21997c01407a6af4215fd3c8e21b4badfba425c032623b182bb2e22ce7 2d15770f93f004a76c5e4ccc25dfabedf39e57420ec152fdfeecd75eea79d139 f1d12a64020bd216a36776fa960a50453b3a3cf398ac5cd64ede354e8b882868 f1ee519a49f43b4d6eeab6922191742c70adc22d3eb88f68f285114600740f06 8b48ebb921c0dcea1e01d7600fcb33fe4c50a1646de0f5feb775cb9bd87b8988 3485e0a10bfed2eba8dd4dd81e2ea42a7daba5c4ec4a035521004ef90274565c b6a73a9b1b67ebc907bfd0429107f58b7867a029b9e48bde23b821102f2b0ffc d5daaf0ec5ee5a4ae2a10fead09449b44b165bc5e657dbdb9aa0ae2a16824e02 58 6f085fd1adebf5514a126b402d28522edeb5fc4d76c5989c63859c58cf32370e6f320f35fdee15f7cd069776e4c0d82f5f91947e62e523dfb690c6596f0dca0171889ab5c7f4f44195c86c7e9421d20b455fa8019aa389014939e67d0cbe5606d9570d9e3c2c1c99827993738348e8b87bd47c23aac6b864a448d279b675870177c3d3e0e121c3aa2752a0c8c9db30cd31eb01ba9e64636f87360263120ee707c2a8b361c451213b2a980d54a2b621dcd25a40dc071066c8796796b11dd64f08a9e6698e7a747699321fdae86c979dd93e3bccc5f52bc5037d784de9d99a5f0c1eb213247b66a959c98c4cf4d6a1d42dd66e5f2a7e80dee0d1c716f9650a6e0628e3a3ff87288ffe89bb952a6db212ba96a1620cf14b056961d452381217f50244648ee4f8b501f0e23f76a57e27d2847b0bb0e9be8d4e86431c5d9602a56c0a373b3c97de97157fc2d0ee29e29deae82ee4c93c97a1fe990c1e9c0a963cac0df5e09a4d1ddb3414ca331c5335630dbfcfb07af8ca2e13889af0930232837302625041547613385102a211993380184b3c586bdb3ff43986a744325fcaff5a0f0a5812d074e1900d41ce24324b4f30cb31999f43f7df60d5231342f7a0744f0882f304fafdd5ba3ffdb52323a7418a4c1b246dcbfcc7e71d1f5f37682796c90bd65c2430e1dcc2ec73324b1a88321aaff72dda1e8bd78449984cbcedb1b5ba0fcf3c54662a1b4ea9d1a62a3cb612609ada804f9a9d7d70055c24860701b97f07ceb68e721193c8eaa9632b5b3f7b594be8a9930a9538c8f9ccf2b553aeccbc0cdda71a58b16d594d2df16fbc8829c7632b9b8687c518f2de428fe87e7935e709ad0d2583994cd6f7c9354cb38b4b6b511ab3ee8a3e544fecfa12886f91ec2f0ec772c9bf641bf57bd640ba5f36e57a685b4b548ded06f3d008f81210e02d420296ee50c102341a0ad38e23c312618fcbf11f960c75c9ec53c5ae5c113f0e8602a93b3ad40f211e1852311f85984e3ce48f112b3f469a8ddf5a6fc194b1dc310cfd5e567dd500b3b6800001668fb8cbf88e7abf0fbc38a432a2a3138f29e5440c5fcf5d543d455abe76e536d777331056b00517980f8e3a68b192f7a30f1b53045dd96369e04a442e917f63b0b22d8b21965977601b4f2d933633fabeaa01be066ce1dee3f1fdf05983257f5527964668720c122bf18f832e458efb1f3d4b4b08a1e190f557bc1efdb6cd4e3d347b97e55d491a7691a1199520c8e69054543800d8f1768ee12d514233b1547d56c4031a5d37ae7e54cf101470f525cd268e5f0d8212fcad8dff12e8d4bf23556e22dbbfdb9d41b0fc728ac40d78ca8ff0339f0836b4d5019b24a559679119dad490e9404c2728a054ec33b39185d2c7b1cb090e8a15ac74761f3c9b5e7a194ac4234513f33eddaa09a747fa2da116852a2dc8089f4fbb21fe317dcc242f6928e38eacf8cc0c802c3469959719304c7d0d27000517f4fc7cef3868427ba8724c6ad934bfa2dac6ebb445afa1cd832dcfafd8cf009907ffe2c8f32a6e6e68f6f8ee24f8f8eed6aecf2b4826d9ca7b0146ab5c3e0468cd93465a7b4102c3fc8b1c00b54561b078b422c06de9acc86c968d9cbe5206ac9ba0e08b899995f88306cc110937bc75af94cd24cdd252798348b7f28c8007890d45c4aa0e1c352e2a219d6b0002c82eeb2706f63a37587feba6a0ab34e40477000502380c2deda391802db2836f9a2ba46239be2daab6892440004b63700d98e22a6803e6123d6e131a463cf0a141b552626045e35d37a2e5bc4f2950b803eb8113514749979854f1895705aa90f8ab222b010d2ef466dfce6de455409606851b25e1100dea08363eaf37c155f5af2fafbec2fa52b0dcd9d26e04d8ade30f6442f7249620dcc6215dcf10a6d9ac491ee410a9dca05dc7a9e9fe4a47318f0e5423df12b719807196abab3e37ee861751f7392f3840ce7fd85353f3a8543708e60012672b91402e4c0204b73628e1771a4a8ede032d6a621fa039c5e4988a0c6d4844bf775efacfb70085feeb8eab5645aa5f5cd7dfe5a5bbd57ad7494ffb036a08defc8af797c9400fbebc84bf619269aaee66a134830f0938c4f40a6fbd0774faccc3fdc72a295f5f06a1c441f4b4db0e3f997e9fde22a80bb7744a8eb90c296414532d6df84824f801b60b40838630820fe6e1068bea5658514801875e0c3bedae519f7412ac666c7c4d5f8951b55525e7327b806b6e8d3e88cb380b4d03341fc62654d6d60f3697aaa49b5fe3e3df7f086d54f6e79d0fccd7404ac41006e83cc4d9a01e84c9db0df2ef1c593f20e64cf5f9545f5006ea0bfc8f0b51020d633cbfdee4a65f1a1cf8d5e3cad8dcefc75ea7cf8c546bb833efc3341898430e7835cfaf33428952622bfb8b6a02139d76ec4b11c75bf2a9d9cb3bfadbfeaf0d18beca3fad9111e8a53157d920d05f3996c0b5cf5d4b41e9cc11f4edc5143b0690451a6bfaa983bfec2e604cdf8ee1b3916889e348eb7ded95a5d4a969d48303c7fa4643c9b88527555b7653b203058415622b0d585a45e00984cd948f9dc40c01024675e29120a7cda2adfff6771244d6140083d76b266b8d9da4a78526880bd8b2e02dccbe6cd5f769c1c0e3f3e4db53ebccbeb881ac5a15afa6e492b1c20d39cca6e04f09150f2cffb7e632879b3860083ef6fdbb678837a5dd47388fd20eb250904c95a0e52ea7c7253e2ed2bc27a61533743c72343f978d1c348f42790e2faddf6964acf1e1e6e8b8f4274b8e877636483d49ad801ad7eed6a9a9891405e0cc5418493cddf8d07919fc110448dd61b7842ba9bb9b60305d3d142aa8240b9e8132722e926f6d002e79eb63fbbd68ee624d9c5fc516400499eb419ecd200ae6f8f7179433d3444a6c5656bc5ac978d92093d92bcf7d23263414f0294a6507760db07e4e1a7a0f5741c5a3fc75815e5a9d3b28b540cacef7515b8c512ef102ec8361a89b4e53014ef0dcd5f4ca7c51cd334f8856cc3a9d734f521753dd2a0557ac07e080678e2537400d67c61d1f0fc056b37ed8fd40fe64fb4d24d08a8808df316f5d45d6c45b1b7c0e897af619acfab1eb1e2f24fa04c96cca43680b100c5470793570392bd07220e0c79fcf83a0b69c17e6f4314261c76dccabcf7ab1014d96a776b6d93411eeffa82b4a44c49717fb7271b0a12748f8bf634f5be8ae0d9b0dd99ce695dbc4366af0986d12a0ad8efd629c0ca4851f5e054bdd673e210e31efe35636506a55ea2476395a88b8a44d4b4ee2cff71a76d3e295a8fb8ecb0c0d51d7b12b9036cfece89ec5e1adaf5da32e3c5a9f176c9e0e7d2295d905af095bbd4f0745be8e113f9e889f3e7f6dd83dbcfc2c9802817a9081ae23480f4203797e35117e38f789f6684bf24f0044fce1c357bd9a48a9f2189f1147761fd10184d98489b3752c9c754b87aebaa4f90d74dbc8470506d36f4dbcdf8b1686c6096af43a7099f47d58585c711527c41472b71b617ab69616da0b51802b0260410129a09819c7adba2caea93c856641569916654bff27d7cea29900f6276cb84b00bc985625879d686a4e74b612b2711ee37903e40739dd2c818ee56f9f85234d08da790491bbb94895008484824eb28d6d98db9afeca9a711298c49e72e1adc504673a91bdaaae29ae9dee0914de763c4bb98515d34e75e68441117d05535a3e0a85d23b61f2c886001ee4fb1ed079cc014e64950d7255318aa1fc85d4a78959055a7e80747b9c2ac60e1cf6cd77639463a949b109cda36379149b6777074ce10f4e7aa6dd3094477cffcc95e11df3f25fd9ec00af89a878a1d6f560d049fabf00e7ab50838f4d0bba7f76d021b16a1f897752490e5945e41bd748a2ff34b67f0285e33faa5120ffeabb69e584b30c857cf024ad6a6214b361c3132c94e1ce8707056c7eb13ce4ce9241c88f81738d38775a846b5c910e1ef3ccb0c1742a4f4e07b44baab077de377a0ba7a0fd25f5a44809f534d9086c7ae6b8175df74764a300db173c82115cca3f68e1eeca4ee4721fda53583b963a49ce082de78cda39020e33f448b8b5bfae1e8dffc235335e3dbf4c5f6fd6d51065235a86e7f654c7700f06281e2f87bcf94218dbceddbe704537825663f7dbd8cbc2215f05117be12807dc2afd452ba147fae7d265fea4b3d39954b5f3d2627d6a96470e47e3268b2f04a0c7eb05909dd3ef41e863643306682c10ad5c04f7af6d097bda99afd9ca4f0ae322e88cb5fcf73e3b2fc0aafb3d20b0d479dc0700138b1d93587c07ce8a000b60fe0465a999d792750b4c6f2eb782e173b8ad94b45cfdc8f0d9f09ab62e9e0a4b01893156ba67bb00089153289a9b015b2b6bca2cc4e2a047475b8ab59d88032d6acbff2a28f0c1e79f82429462c9404503bafe9478118a11c6dffe11d62b0ba4d7846dc3aa9b8ab6c96a8a10bf56ab68fb28502a6fa14597076265361f41094528d8b06288153cf8a0ad7a480f7142fed424a7a1c720948980ee4e10553904eb151e09515b3287ad6d61decad8198b66e85bb47d08a5da3977c36717e89e08e0fdb015aeb9ec358de6cad1cc9559bc0f399d7a42a1529f6a7affb18d568e0d26b9fbadaa2e3324b973b02e7735e6e19903d1cf5be12bac70c36af58a02e40125116de322656259e1d747103704391333f7440c96c5707fb2500732674a200fc80ae86019e26ccd6ac8e3969f5d87a005077f54a06f78fa5b87d78e9bc1a10dc1af47d2f98fd5fe5cfa1117654fa6eceb69ebf68cd243cc31c5642428244a084d49d48c54fcd80a79082c49f7c18f3b0f55bfb75d306e1c08b4b0c8a9940606a6f2063c5d084e6ee6870ea2a4b20d950f69dce8327507dff63cd770fb0490008228b6243865ede83c609e3f5e378b6f3945464c887f7e13f2e534df963dd90971a3c5c5c12c74dfca1b55c5c971c15a13e94c55d8dbabe9ac34fdd259122806a959de5db3b43abb79039c00e63cd8fd068b90597253be2709937b8c23eeb00d3121b82999c6ba02ada2aa745ce5a330af3a22a4ffaa238feef63a9b5ce0d901cb8e344252a95d4f67f6ca126def178655ac7addb2c49781b2d89b12bc626f0a48899e3268a123cf79df77561ec94220f0c27a770a80fb436a5480fb02095a0398ae5e5b841dda9e643553358243e4b02cd93f8bae7a8520ae5e52f8e3c90a0b9468f6e9f57452c324bd9e1b4e602abe8bc682fff54d6a1aaa0d069f156e42032fcf4753f95e137d0fee65fa4e7aa539a983fff8c199314b632cee81fd7bb50e3337feb8cd36d884ccfe66b140bb7d3481a9741fe633509cc05b8da5c97b350a4d252fb445737c4cfaf0214ea2dcc224ffd12e0631d7ed2cc08386092597bf0a65cfbc691249732fe95548ed68107bf2cfe6296294cb92451b01c016dfa8b10482ba6f72e18476ab8631b52a8d5cb5864e4d2fe43b47e0aa1950c60cc573e308dd0580487c2a09ef5e9d5c6ddeee1264cdf6a128f1b347a338185bded919ce09752b4323c15c9f582222c022907fcf4ab19c1a6b60feb67350ee4ab1ba8cb70b7032c0693c56e97b51d4746fbe0687eb6fea6d2df59f5251044f34fbe45b4d0dc39105be1a56a14db876bd8b0fab2079a4de9f3a8da13cbf47e79456b97549080d573aec171cf6a35789063f3c5a80b4b3180d2cfff2a1586f617a6392846604 +generate_ring_signature 33bcbd4c5fa68af0c24eec449ea51bb72ef7190c0ece9dffef244eb90d6765da 732fdd3197aa6855d284d6bc4749adbb4c265336b62300984ebcf74d03d31ca1 1 878686a68ba5dd5aa696fd1078dd94d627486bd9c11d6dd328ff0a9b6c091f0e 658be715fa02ba1b7f4eafc02f985fe14c9f7d25f6bb4663ff87ebdc2d63eb00 0 98418443782a594503816d6036765602b4d4f5731986c1a549e4514137fd4a06a1a96fd9549cfecab536f4b7be1c39413f627477c2b57d33e2d0909ad190b202 +generate_ring_signature 61c98e5693c58afb5c7311d89c4589e4669b52d8ff4571f293ff1f98af9b6224 68535c66978cc85a04be5b88c000167008d36429547ee67230abc78d01b3a07e 2 244b5e8cfbc8ef701d79624bb3499515be5273e45b73fbcfbde5b238121567b6 04cf46394b267c0bc9a5c9e2e5f88b07d59cd5a9bb056f94b46e816db4789be3 f0c934fded787d254b29eac8eea8e249d6217a82e8e9d5bd7565611394277404 0 4216818cfefd486e5cebf5a885e2593b3ca48db0d680be6e97303b7f711191010d60508349ab2027ed1455403b0e30e988596730e4d5d54800791300bcd96b0b8d0169e73915b440cf6d82db32ae5570fc830611a645d8c754686e4c8c2895077bf0fb0825537a787763f5d026f692fe6d2573d90010295042890d68f76b5008 +generate_ring_signature ccc5dbfe3a1cdf9f9c21948ae48b7cdf305a2054183d05654f6a5b8f1d93b6d0 406a3a1d1b23710ed1555c857e26bb6185a783eef109fbd102728811279bf26f 111 2921b78ba30bd3557e4b772b3d61ea6a16138631c4ba2d8613419171e99e66e3 ad1f075026848ef6bc1680d01e676f8fc292a83df780b49224e9b62c215f6eae f0ce3cecd97138d3819ee595d9c3edebaca5e340afe2f07183373d0dffa0f4be 26eb1a7d2b2c5bb31b7fb377b2523ca36648014dfd16c7f695bed93b718e8733 92523fa28a9a5267ed1801b4d606e0fa5c1b70d54c4634ad0365c6373de04a81 19c3b3475cdfe8c9e801d0be4def7e2d002cd570fb9d9234fca3a0011a8bc5ee 0e9593813368b47bb51b706641310e983b927386a90a00398108e8bb790ce5e1 5d46bdc035314dd0de17318cd46d886dea88c67150375407f34c0039989ecbfb 0e8d745fe8777c695390abcbdc771c9a7038cc327c417f3b754118c65b6d6f99 8b05713d11a953c083ede9778e7e7bbbf4410e35fea2af5b4320b0c7cc4ab789 13761d8cf0742d4b60bbfa4d205c49d4231eabf718ec4234f4036ca168d9d1ec cd2db44254c778fc88a7737e400abcce5d893219d8460c6ab3511b42512657fe b4534bc0527d545be7a0192467837a675c47e94db7ab43ea0773bb968a5740f8 ae5ce2355ada6f7780d89d1f33a249d4dfe7e079b5e102a82314bac3c04c3f6a 3838cd930a369c5458933289ba73002c71134a5e9dbe16c9dca9471987882c26 ff437f8c5906e641d47e53514ff94977417cab286d5f9998c0a7b8177173616d 9abfca55d6be1bc9d37e412d7c35202301060b89e1e700d5514d02d9011aab29 a3232deeb8e763d369ae5c94553aa0e264705dfe24f1fada06a1be1ebe93dfb5 8f51c13bfcf2486557450ea30970171fb0d428c02a823c7f9c704da2402b6594 1791024939f618fe68b0d73e853103b1fcbded7abdb65fa15aef63f55d8b7f44 ebebcd04a35a3eb5c9af95cd67d75289e616092523b8674c659d55209b9b2d61 4702673f7fa85237ae5f20e68b966f4fa9b68c8b6c900316e9f3864a2720b509 bba906384ec35445dd4fdfcd246f38654e8b9d1e3b24911e8c892687d1bff04f 1242ea27621579afd6aae5311988fda8be14600d5fc64352af2ebc4dd383f7d9 7dbe2bf95b8051ea0adaa52744aada889d89e6ad189ea26d8e8c355eb469571a 4cdf70da3f89ee7210375564f8d46aa5fc3a3c47e13866abac7c0d618346a042 dcd45fefa6e3de7ddcd3e105f8888bb6749cb565b2d1fe67bb1b56fef790fc5d 9507267fc1696e5c35cd3718490dc2a853cd2f6a249827f0235d6cb95ecea192 fdd370f6de86d821dd2ea27158eb2ba1a9f6bbe1626485fadd01a80a92a3ded1 575e8265225649995b947201fd76719e51e56f15eee0d5e6462331f69650bc7e c9c8a863860d12415cb601e9d8362990ee855a1e5d1d70cd75e15cc53c07e7e8 dcfe0f0f4119063c29577224bd9a7a1a3800d645e0db7547ea05389f45e73c95 76b4cabbccb35f51dd5f61dc72c717ea336795fc71d7510feba7a84e5beb5fab 0f12bd1eeaa102f5531d9fcb6b79423393fc47bf0e7f753c814f5c273a97ff75 86292224747d0e179c14b7a3e65e4561281b6a827aef6afa2dcb8f8b16571b92 1f9378d5cd626274da52a2b71027b5ee6335871550ffde522ff0a6f49986eaf2 6d5daac0ba917f50cbc0c8502489320d6130cc4a2b332b34556086c5084a5324 9ceb51482de662fe631eb9009212b401fe7138b667b7bd8cf94d9181f955f3ac 958663234c70ff625e10a10779504e267f643aede28861c4a0759eb01fadb1e3 d906f333761bfd3b113903255c1589cafdd20378d85665af168d0aad0367f83a 0635c64549ffedc46dab199ebf89fcc8ac599c8d89970f7133ab5e235e19f832 decccd21d763536ae6d62e9c56019358437f7176b0c6620b51bf59c5f2b0a518 62bfeccab34d00e92ccf718f671b735d07fc475bc5e4fd783e487666b0fff721 adaa55bfbbc3f50b640ccaec02646f4b152fef46d323065efda9bdadaef91ba8 82bfa52412799a07d83541bf6a3de59bc0af127e4310f09fea4d1971af8c6aa5 4c796345e19c94182721175a198a8ad535ec0f547558c7f07cbc3127a1ba0d87 ac9bc2d8ef7f43f80ac9d87142545ec4d14e54f64d4af3aae305bb5407a819fd 934000e2895878f33474fb25b205d5064c181277719336e36e688133a93bfda2 1b3ca7399c0b5508f80a645e083627435056ace47a15f8fe9f9fda75a0b17baa 9e360d75e54b81e3434caf08a8341bfd70abec216f1760388e7d68aebd87e6db ba19f181e552de8ee972884cf82d87abf397949851f9bf867a7bd57656fb8d80 a3d028b2702f532fb6dd99154d310afe882a709f8d0f194a3352f800b855c91d 6c477cc3e4587c389469d20bd69c196613bd50208f91324404ad58201f7549b2 a296d83f65b17869870485296b1df3bb2245aa4245abfad85e826dbd93ca4e21 23bea6d1f3a996575696f69c7b4f4e4b276707eed13cd00cbcc97bcd807b9bf9 5273d520380cd20c951e725147ee7aa942b743b8873b0ad756bdf30851dc2d98 85aca67f1e96a7e3fcf7ea9a64c5eb63dfddf35b692f631f89d179d5d3ecfcbe 84240ec649ba87138abdd7d1524c2b580c95d601395ecfa75ddcefb52fba547f d11fb311986843cef697a42c1db914979eccde0dae94d1fe7ebee7d4974be857 0af06bab640a620fbf4cc5c3a777e209c5421641b8ab610b3c5a2a1a47ae2a0e ba30f319070ffb436f5a5853d00f8013241fb6a6bc472d142f303623d33ad465 34132768b3b2dfb6d97c8eccffa9e4009230eedb6e84ddfed5dafcac3763adc3 c5903c815b8bd030162bbdae429032faca5d73777ce3a8248dd717578f0b8541 adc10a53868cc31d0d81395ff25be93e04212b1f45c00ab5f6eebc4c9b28aaad 1b841fd24b0e7929dabc30e700c6d312a8e3d976c95155a3bb3102fd9e0227b0 56f8165751e6767f607821a630ab9d2132661b4ac81e2f693e5c4f60114ffec2 6865e341bb94717a4de51578360b59cbb07a1660f70b0048d9475d28d7adbe3c 3dca83f1651d518dc458d331c9b9e7c2791ee1019cdf392cd722214e169ff2d3 72099a12ac2a7852c982c2345a5b13d7ee8fd6cfcecdf989430e365dbcd758cc 400f30e5179063981ae05f55129898e12a61dfd6379670fba47f4af1f1abd042 771d8631769dd24075741231cfda6e1bf41d075c4c60af3521546b2f6f2f7da9 1a4ac92b9c6b0ceca0a0914c21934774314b7610eb65d81639971606e2109a13 c6555b566f15c912b0fe560cfa4b5df5dea4e44624b49aa8a6209f9131109e25 944e66c093a3b22b7b64434839dffdfda5b8e0ddb6e93b3fb4e6c0d64ec80ccf 5682de8256dc5f421323d2e18e4fe8e86810eaa87c6d58535dd2c8d29d27d6f2 ac52d43a2c0fe2b23e73c67f6b0db159498140621b13b4bf2fa6311de6cfee8c 24db498c9238003c869f46647b4ec96fab351438cee5fe815ce700f5a83aba83 75ba2f7f38f6bbd6f58b59357fb99a74b46cb4252758fdd4e669d327a28fc693 74d45357edbaedf1f26950c22d83506ab3477bc671e685583418c81aa4c11f4f 61c814f9c7d4066bf95ecfc56a6b60d95eb63b0cdd4c0a45ff4d4f83367aff38 b58e16527d8d0435434e83e64c6ef570cf3717f79f8bb142a3510dce185286cc fb771fda4468b3ea8ee77c23ffa586486cbccd347fcac9fb97960262fc4ea76a a5147a14b7ef157b8288f781275b06f0b785c0888fab930c6a78d94ac818ff89 34557754636e143800091dd6e2a233b47f140010f36eb4ce27b48855b74076ba f95443b3d18f130e37fd03856ec8c6e38ac04dc9d528220481cbb49ed27a9f03 3a6d4cd695f507a0a8a404c02d1e0bd2ac93f4ef2e2056da9ef7a73e70d82f7e 5420f0d5dc5f296057a98a879fbc0d64605806aaad6b574da9a6e2ce9cab60b7 71b23bafb900f95b9d8d0deb139f0cb966ccc1078542b32dceef6849de962e8c 2255481001bf79311d76a4989dd98ff8c7f2660ab70f273b52024560d7d9fd66 2d291106b2157d69bc98c89e4034cfb9988d54b69a413a543074613cb1cf7923 3e38943714f3d3db34f5c12f375e73e99e6947fa661e86065c4041d15408eff8 7c79bb5943468383d769a23b260a0179587e9fbf3224349369bce1e5a49e4869 fe4fbdee077a24b3ac3fabed5c8708f4a5d9f844a8741cd6b2f539932d1a34ff b3fbe8f600c1f5a192bea732df2068edf5ffd6d766b2eba55ab7f6e8a8e4de8e 242b451f824ac319d890214a53a0c84ac7b2555ae80a02cb758343a607bfae93 2dcee51f5cf0cdcae0fca85c195a0f1ff1b3d4e468c9bf8d5dd6979b46bfac09 e35f7bf7fbeb114a5d73bc5450a2e24d171d947268a41c4da4ef322f387a20e2 5247a3383b66dbecec8e74af6fb621f5ae3fd77554c3c3f34f0de68a5802706f d727e6a804a5321ceca5df85777121c501e00cfc1beea4455cf94c68ca3beb8b 64a875b077447051c23ede119427c11392bc495a401d0631da8eb3ddc6af5c79 a1f1595cedc5db42062066166a58c45cf7065af7e52d54c26e16fd98c6defa16 91410d274f804c5ad21393f0f1758a43d0003d604e34fcc9ffd545a3d39b35b7 61ae9820c5041ff102e5232cd43fba22d68bdb5bfee2d997ed97f59edfefee53 d32a713c0ae6079b57526da8b355edd35cedd2ea55b9192e6e2bb97f2fe10119 9057bacde18dd4271192956ae16433cffdb67580478f52fb9e4145658f1fca55 8bae0275e1a2f5b71e2c2beb809e5a98d02b22abb8ade1c5356aeaf8587926bb cbb581f34ad405175e6b591d7f0d68272140a303a4044ad386bcd12489cead56 477283eb5796ec76bbdff973d760fb3a25f5a776d2cbb9f9d3a6e2391064dfb2 2096412af720af30473b8db41ee7c88ae13c2954d7078d28d284aa5bc76ae1f5 2086b5fff76bbc94f0df920c81d101e50708d68f0b18f3b2214c84b137126a8d 68b883a806d347d1ab2efb8d975c12725a68dbd162756d50454408fa12390cbc 427a77fa841f3dea30b971fdd3e26d79a6b1378674651569ce263b68e3654d05 27 64d1bd8ae10180168d64d8cfad5214418693ca10a40df601d7e952b1d46ceb083a09586ffe05b46449160e28a624b2dc797bb47a842d3565e5ad0ece0ce2690ede7ab2137bc02b59a84724e65fba52b58df548d9692c94655ce8b7aaa25a690a81aaf24bc11b3c68797e6531b8904e8cfb7a44f8487e4341c468f5322e438406e9b1eb76cd9f13c19b433342aea4b86129644e7710b4d3fcc19597979fba970f73c507869135a04b033909dcd70d1aac810931b1ee217e6193f1cc1f37896b03f27f1b6c1b40d2850ba70dd0e076ddc9d432d0d97241142b58575bcf5b55bd026a8d6ece53181a1507db92b858ffc1c04f90206e6cdeda67cca76c56b87c3f0ebad09b5994aec7550dad847dfe7424ee56be526a416b9d9584c0f5c5aff0f205f00a4bc23d5953c56373ddcb1b3ea1e2ac823486855bf51be4d8cd749014f40b4c9de33012ea589c6b0ef91a467abadda3d730286506c5d3561933652a61c804f211455aa4a7513f5a23d6f8a5df1833ce4257536711e2a7d5468269d2608c034b06cb5204f87340b6d7aff69d6eb005a09b596d3603e56e8756c736d084dc0e64dff02b8ff16329362c903f418f9f9314641a59ae97e81f9182f03abc41ce0b5db99eaa901a5a04cc216efbdca56844c7ad930966ff80e57962018f358ee8026c307a61725f67f0204ffdab2acb34d7c136d5f53cda20219b9b1b3ef838630f6ad77d15c509c8895a8f4a409ccc21874d998920cb6e48b57d3a3ef208c3500fc5a915da32251801483995f7d8a8ad39afee832afbf660feec4e5dde80540e05b707beaf5d649de8519df9d8194373236e7207785abcfba18cd8483686768202dd5d1f18f514d4ec8d5da347463fca3e712b6c166cb2cfe93f865deafb1ea405848cf98f22f14335529cb1ae5fc43e85137f92b9d151a8954a3f504133dd01097e9c8f2437c501a9a155926225cde9ee3b5943275ad8e3ab97401d967271de0a7c5881de2c8fe8fac5a89d1188291ccc98c24a68abe93ad6ae8a06b2d29c1005028a340bf2bf53798163010d20a84954ca8ae2d740cab432fb3d17a536e17801cdb744545814b97acd0842dcfa517a8cb73f0eff2b2b4ca6cc6951e810c8f5068dc6dd8dfd6b361fec24d99b8113ee8aaebd39ab36e201c933e45a1a83f6570896f30e71a0e6cc433a422c6734fbf090e178dffe67ff028c09e786a5f82b8b08b2a3ecf1065b8aa6b621b0b1285b716cdd8205728fabc26f389f37323aa39707073c7c1bb8b18a655c209ac8902f8e5e6154d57c0087f896bd11d2cecddf860fbcf0dfd485af263c0da6d8239af70d8e254d5f9d1c9760fd53939891c1f20e039b073b79d39707bbcb2d350d551b27d64b331770e6af8b91c8178168bd1bc10db48c661458f4b274eb7a275bc21d327eb7669c8df7901249b088133fc6c6200f5c4960708ecccaffebe4483259bf669da079d9fc2d82bec9db4c518a4113b70d6a2d7211f2d264064421d54a402023f70485e5f0ee2ce0d41190e0a041f86008096b0d4d212cb1dbdd66b04751ce6bf8e6b43656523197784abc39c4503d600dcbf44aa810617405e214d1190f77334f2a3e3adeecb59409dd2d588f7e5d320668e55a7f595e71678d0fbf0d3b2535a4aaac3af2553f866b572116003c8a7a0a2bd0e5f1e0f3e36e749e2ac1d5c0127c5355a7d61e34aa76a07370358ea997066f31106fe2c73d2c966896d819330f9fcd2ba14641a1b3d679782224ace51f0d7fea6bef41edaf29fc5af8035f427c00a64cc2a705acd5f9d9b64771c7805b0d8f9fc456ea5c91ccf3c20fb901939a53badd780d4261eec5de7bf8a8cfabac0de00085ae134d46f176afa458a4b42cda58435fde587a675d59e499d58368a8072f5488bae58a357b193b1112b6841bec5d9e9d17aea4afab799a820670aa930d29c69c83c332a403622e6cdb855ff57ad3c0189d76ee017fef6a45ab884c510e14abf06a1dafa299f293a05f66d06ede7b7c71bd4845edf55f0316aa2ce6e909df19ac96e071c85e029b8c9dc64822d141fe3d7a63f22448c6d9f97e21d70203106bde1fcc26a5403d7a1c42e3589e28b503f13fe4c828f4bdea405b37ce7c0293dcc9f934edc67b69ade5757b26c5a660bcfba263d53983a8c229a3a4bc1901cebfb65667a8cab32e86b079e750db5faf37f40d8506610866902d545b1bf4013f88936e69de6f1786a9e23455e6a4f9cc4d49b4310f876f22ba6714a61f7f06ab8ac4c222eb55381fd2eb44d38241aa29c42698911eb227b1f7bc8d061e55001651375eba227580f24cd8cae5984bb4bb09ee57f4e8c3f3c98d9b57f68e800ad07e3fcbf7d30c27f1cf3c4c47f87145b70a634f513b4aa3dbe7b75d80770e03f263abc6449a1e4ece70ffd79bad231dc7a0979f8b5654c4952bced59024d600a8b195b7e95052686a1bc3ffbf626035480252cc1924f71057610c1ab50495029a35f4380f27fe81378ff73e2d59f3ad0672b7d8e5091c78d85ace464aeccd01a34da56533dfe6d91719131e6a098d4c854e2be30d637891f3eefcce020a7908797bfa48d313842c20c4d51e8d7a35264b9ff7dcefcc1f87fcba5f7066437602b4778a70de196233e5b49030258ddc7b128cb843ed6bae6f2e0b6430d169100e9c5180102471b3f5e6804d8ccc14b8d4dcbc72832ea6ecfd7194a427a1707b03339ae7bb7a52805f721a512db6e257c48626262ac9ab7434d36d1e0de6d8ef0a13c87ce39b299e73bda10dc388cb10feb0e79516db6e3640a1d82917639d6b001fa4cb430b74602b0167812108cabbb9036b9160d2979f1d18caa2aa4ccd750b4286783f67d33100f0dca786181410b66fc8db3b9dc1356be6f8fd00adde80068ce364351e316e154a6d87cbfd0c9fc781cf0f6c8f98b7f7284577512abbdd0898ce4d23351ac5f1e812ff7b9c092bfaf5f099c88088873e1542f5c556dd8e0adb066f1340b301547349c04c95e320d9802e27597ad1fd449d7bb32159453d0c12f75e3e42803fbe4f2985444511a292b78e95df63de4c68a1c6fce18c827805614062805061cc1710d74569a66c31a9c76d4fe0908d206686b67caab0f469096a701bf228be9669fb31e22fdd9011472d23b23b7563a0c09ab45b4d396cd8093b7af9c52d5d254ad563c49b18dca0ccc9481f39ded5b726c7d32837a9fb3c0900853277728d2936e4d56b6b105e76b03a14b24b2a0eb73329b8b9dd306ee604de09549b6ed481df03345d8eb2ceba043bb262d42231cb82950edcaf47067608a583e89fb43afcdd5e5987dfccb123c297329a19aff3335a67089e7d2de322086f9e14e74198106ca353e31fa3426369f415a2bacf3160ea4e40aa3605e3660bdd62726dbd0706112964a0a28ceb7c11c26a0d0fe05c7090e600f9f801455d0d694fad13006c038eb6b63beafb831e1016c0a0791ad13ed4d17f0a3914bcba0da6320dc60715ecb36a910f0efdf446e01c96c0a9eba6569186263085b6777d09f45cd71fd6eeb60349298f944f6d4016cc72a25c5c071ebb8965946733dea30b870a4804a9ec89c052e8c42d168bf6d74ca8ed846240a904e4260db89d12a500a56cee04ab6c5b5c467220325189b29bd36d544a1d400d41d3b989fff7be38072f479c12f3da5992a926b36f30cea493d9dbd80b653a0915a721c5be1b7db605b7181d4ff939b629488b582cbfa8b55701b955fe7650ce9bf2907c9d87f62300811ee6dbe80a1883e316de9cc92d932d8a4f640aedf587f842d21d6105f7e50052f1b17b79af70064bde9a5657483c8ed1e175de4cd9d54759ce648e7b88d80250986e9dac1372b1571a4c18b0eab28f7f73246daf72468d4e8e34af03934f06eff2618127417c7254efdaf71c2e92ef962fe47e9bde2fb8bab49679681daf0c8eef29b1b1b08181631ee9357281cd5e58a771671e2006fa5389c1190d58c600f98b78cd75d697f132f4240009ccdb0fb39df1239cad5d84fe43272220d0eb01c8c8e989e12b766e784d3fb263b6be2e984e98ad1e4595f08c1652a38763b30e8e23d3092ff96cf7c40476684dd2193139600d0c8731528a0e9c536a8d26a10be91dc5671f96518896354360da84916eb057c9897347d40aa555c6921116e009016af319005cdaa2029196e31d59e782b62ac252fdaee47cbf0b2db408d673051f8db6fd7d6f2198d5c970b2cb39cbfe3717c55eee111f636d39dc1dd6691b0ba24d983c71f4bd1050b05b04509191c804a8729ad496f603532787dbf3672207526a28869130dd7b6d56accd6167c3563e8f1a02c81111ad152fe09db9d5960ce2a6c90bc560a43196f2399fe935c1ae69e0c90b9d72d6338d5db864ed2ef406f77030daa83dd3f906a9d8cb39d74874d14b022b8d96213b6fc29c06e8996409be201c650d2d65cad9a1e0a70ec11c480f15dc4ebdc69301184154867221c1081f41b2f5e34b7db14463af3c3b635e11eaea1cedca5bf0b2a689a8b0f127ad0c0b6bad404f396161b1510045a6767fa939f33752bdc619bdde7fecc4b76b6607166c34f75ed8d3e9b329ecb231ebb036bc4877e92c8a9a62d1b931768d7db703c4d92e4fdbae790fd5166841925d7e4c06f799698fda93f013a755f7c4c0990d55b4c37d89efe352da93cb83fbd039abc3b6ccc79db99fc5294b5f496df6b40eb83a720f00a0abc43ec32d9ba4c6d12d1f3686b454fa1fdb45bffb332f0c790e32cfb2ec8cec7f124a3b882b086dc571af66d0e766cd1c900d0b9b8dab5c000bd71650bbe3eeac4fb2706d3585aef87eb210b5a888145dedea45cc6ab71ed607443a6a21e1798450b6aadb733dd23bfea8ccf953acc30e16c5a5601cefbec008c6d2f2212879e706274902464fcd25ba7558f3eae26b94864855e53a031c540021391dafbd46b469485d397f4f6bdf7f8f9f217ed09f79245fd57af741cee90f6141935381ce7d6b92c50aab7ebfe848bbd275f17a16876998575487129eb807f51882700b56f162cb94ca3ab246dad56be04d6ff05c4d492922976be28bf30fc4b6eb7371e707c622fbe5820bfa4855cd1901a1847720a6cebbc8a0da338b08db493f6173432cc1adff4d55a7ca09f9be4db6f10b8397887d408e9e6eaa2d02435d33e0d33041e87988f06eb11523afa6222d8c559defdfcdd1cf9a3658340b3261abc4d08d803e254ac31e1cc52dac9570b2f78156bcd8a2229b8881ae5607921d704baf33e124e9da1e82b3debfbc92ffcd91b68a433aa9782a0f5a2c55033fdf8376d957fd308e066ff49461bbe78818b0ca22a89a1c7634206f80a47b05dc0c8f97374a31d3716a76ac6f0ec0a433acf4c1768421338c0f9b86977eb80d7a1fa3c8e5d4588b30d020c78f357b14050adf30eca1b62fc684e50ab0fef906bd3c4aea4caf4bdf8a0aa6ac8ab8637da6fd58bc58def2213f223ebbef60ed00cbb61af502d373c96313cd71dff0c25f046025e2322bb246b07298d640946d03477dad14ac010b83572a1ec3c9fda5b60e8badf4c60ed335edf0669f0ee4e20d1c4c3fded92ca9e97b63ed24a61fc4485d41f99503ccc9a404a9168e4046800aca24ed5c6b2dc4ed2b29e826e66b6f209d26f78d1c9d19a6053df5c4da314b017e084041132bc96b582eb10f4a8c8f51153e74fa254e8ecea59e57d354e85009c10a4c0ee75b67d256e6ed681a5419f0887f2819ddd6dcd1cbc6416a0149dd0bff4db9808c28451662e7f24f76f87ea514e90879e6ed1037bbaf87b285542c038d19079df94691856aae816d71a22c406176f66ed2e7a8a221571196ab0820047e42b5212d964876db1b9856d1e594e0c081d01934a37bc7b5ab5f999bd4a901079811146918eb45c5420dec19b201fcee8af4686d6a5a67ef0539117632ed07b4e9444fbe3700f5223932afef7f40771003c23976a1766c86a5e9531f249505e41e033cfe2369b04f86a4b63218ea396ed574393f76c0a6bd2a27876fe38e054f78d3fabef5fd3f1dd07b47d332c3521b891eb6f11895bdc89d10908a19ff0bf59166d2a9ef7157d002ddece97d2763a2894e27a5976f4b6c7db82fcdabca0fa2418ff53207671ef2b578a1db6db2ce3d6052ab898d858b96f2211086ca7e085a0e36d46a71831213482f8fc37d06cd6a28093f17f30502c9733c510b56670af649be55ba2dd11a8f411cd19c26814952c864ead414f349d5b65595f8c2e603f1bd02c9623a13b1ed294f8c91bc97df2649ae0931da9544b0948245e1d96508d9abc0dc043514a001f1b924300c19455b047d060864cc490d6e93e1ceb75e0d8ebcceb43d10d801561f8ea90f2392b2033cbd75a1fbd22336013f0465ebb906e59b7a98b8b61f14b82aea2cf6f91d6018f13cc2155fb8ccbc90ffe3cea927063f5e461fccd62400c5614874166bba9d3fdef284a7d4f2bad526723fa8c05c0d512a00105e27d32d61012a8b7ee4111a3b74d9a16573711e0f2ccc646dff380aef427722cea046e514b40d74436c05f191a121ed300bf29c6672e873bd4ef90b9c971385ba0d84600d9459a93d472cf9381ff8583c93d4625855cb4f112f0f0921d7585f1db20d2bf7e999d42dbf25e4e2200c7a1d1205543ee8bd3983446d07ae943a4ca1b663c9deefb77507ebd705730d32db113808e9cbaf9aa414fd700af4e54d6ac84981f816b6255e8292884d2095d0fe283fbac5283c211f107ce70fbd12a1b63f3b249ca2c9b0573f2e750865fce07f14f65b8e6bd33df9aa0a6b05304fe0a3d28ad6f14e5797ba91b9da7eb957f48dd4a51e3e163522bfb3cbab0c77526d63b91fb6b1061c7ab8016e2f77b214583f06d2f5de1404f4d798945801c55a13de477bbe1c15ae0e5eba7c36f2d8f78cedf96d48e2e9f7bb5ebcded30ccf2e7e35de62e5d658d48334750f53714695b61ce176c7f7a67b9b3fa86d750591c0421998c69ce9237ccf726488f9f1734da635493b472d2268363b44eb3304f91fd50cdb687a9f31a5f57418c84e40a9748d6f0234fc74f4faec80339cde037ed2bc4953fbd29a1ecfc9a60e25f1d218dafcee9b7753f5dd6fe0f231d0930ea384b470b0473139dec1ab627305d7bed373f94ec58946eabc9135fc12f8c70feb6df221ddfc743e4db05b60eb484da13cb74f0e8e8b1499a40861c16391830a52c7918a777c8362d2fc046ea88ab07a39e34bc8d3045f5594e541f64b52a301450896e7358311c635039cbaae02a6ec528ffb93c41ef228641d70c38337c409090beafbedde8e79bc04b67b91818125ca7b5e0065695d2b247b781aafd15e07f3b227baed57c9452e1cd9dddab46d8a0bf9811a172c4fee936307b3b340f009827297bfb4607ca47515e00d876c7e7bfe3da156011ded770bdd718ed53cf009b3f1461eafb79063088481fdd25db46ebf1689b3015f4b5bf2b4d62d27ef9b0ea993abbcb9c5abbc29f3c682862c7a86970756a058d56110c2cff31342739407cb03dd8cecfabbf23122487b6cff7baff24de7e6603992033877645364cf82034a03e86a9fad14c214fc747212449bec983bb55f7fd28dabb7fb7cbd380ea402104080b3442800a173f762f5bdcd092ad7f121ef1cf5e6c67d9bb5d214f4b808230e9c141c9303ad4ee1ca832472b0780ab425eb6eccb723d6e331e031926c0138e922c74b4f9f0cdd2ef7dd2bb997c2c0e6636350eea7aa021675694a4e3a0f5c6115fab39077cfb2fa06520886a05ce151fa9220fd9ef6562dcd8e5f9db908f41a350172ac6c8f0f52873a67f983e5e5f52d8c07d3e5f329681e36b1190701b217db4e14d0cffae5b8b3967f80dc13707f7665167b93e58f9aaa2c6e485a09a29bf525ffa72fb05b847792a95b03f704897e45ae76849292657641af827205cd8a6bb49a064b14a592efbabe073fe8da513396ef58497fde0a491446f4740331781a6df64bd145486ef305093324202516a0eb08ce7b895b7ec7162b25ab0e24215d6c6c7d2445ba8052280953b86a288ca16c95531e4f6e6e5a569ad80901b61de428146c311e16c566de1202536ffb30b4ac36f58065406d3db3a59e9a03d6ee9835109ef0fe7987845dd3e5ac50d1f673d203a4f69a3f15419d78a43809fc88feb21508e12b54df3fcf9f0dcd3ad180198ffc68e87896571bbf5cd2730917c7c3a8478a40ae6d98b0312b185d16b948504fa463bd312a5fb29a4c5fa406d4aad66171b71c116efda93c43e2c345a34463f581f8e85877b3168c7a17470cf7b5ebbc960eff67875b322b9e3f0046134478d0fd76308a60e4423875db440e01539c5a71db4ab4dbc1f1219b92cffe5adf5678dd4598c4574fd791275c110d26e993f75c8bef05e71e33314cad4c037b788b45c3da12574ecd66386a341d08714265ac4cd9ab644d884d76cc0bef7d904f5a58c46792dafd5cc22a9c8a9e000a501d4ca04ee0b82a59d750346c8f3bc34a7138d1f9603e30ecc830736d9b08d0cd2a3066995a912a6738e4d1ce09653d6672194ebfa50a93cf7a1d5891d504d31734d114b6463a61147d6e780d60b05ad17231dc475130c7929e5dd4366d0f5217add55bcc70210a8b816dd890a824ffe0ca5f4284a76b9ea5642a4e39e30a8f1602e638402c6929f9dfb4c5d5586a911246b7312452d074b3c59a02634a063a3c7eed78c6c349dfbc0417473be8cb95a11976f358015eef5a1e036b05430ec38972696115944e3d5c5b31329ece434d6d443be88a9fac015291e84692260a9ebd33c9e183100dadca7b76451c31d4286d827aa0be4abbd5dfc72f812fe80f6b886d9d1ed3e1d231f5ad743a10c5d5c2ba48b142ea885d1629e4edf2c94f0878a55b28d7268727a7598db6d3184f4584480018715aaf39cc8c19c7f393f90a5a6487efff93589b4b36019aced42bda34b0a5f109ad8c9a6fb17201c257090bede0ba9fca628369f712eb3e80d1132c39b3e854e5783f80699224a7c2dec009922df7ed1c9a332988e82b2d0dad551bd401d6905810da694e496ce90a41f6074fd5d05eef05ae6938dbf5b6b722ba07ade6f39c915e38a4e012f43654c19d08e5211fc6d65030dfb21ff0f9e8c95bb689a7a601d2a63306588d31b5e0ad7a01f0a05bacd19fb77dfe57a9241cccb784cb42f1861ca6e7fecf6b9767a812fd08750dc52b9c5f822067860dcb756702ce86e0ac1758e1cf2e5e5117544d8d1308efa70a858bbe8308f42f8a585e333fa25a70971de1752b235b52231667763b0dd9229e538abe0bf30d7cf05cf0d991454ef8461321209f1c3e5c462f352f2e0712e3e609dd7359a7a91b0aee20db249e851a0f475cda61daef225c3a56e1c50e317c82843320359fb85fe525dc936efc92b3af1d02a0298a42f5d2da20219b038ada0d7ea9808942b4e24d14290beb081283d3d46abc19fd6d455a623cc06b09c8c6ebff007c4dc05b363fbe8709e99b352c582fdf3cb4c9063cb95d3a8b830d24586a542218dfd3218ec750d7e5d233bd231271ca805db28da94b55752662008accf21f16dd7a29fb4c93d510cf33d424b4da8852761f4df5be34b54f6a6802c9792a474d8ed94f315117adb8db3de00b852f158492edaa1bf1a9345ce64d06954d455fe19f5736b7fcadfc1b8f44060356e97f3c043cd9053a650db3600e0274667f9b3b097c8a0070f4cdbff3b7a0cc413a6bf6083530674024289366ea08702e6e2c54decc8c7ff405955cb15fda9628751b81a3e47109b3af13a7126e098dd905b97657689eea887a041a51815abeb2d8a95a655c80d358d10c3efa83011b39c7d8dd6188d040b5b1c507f76f6f4297cbcecb291282a13a72890ec0d60518a9149904268ae6d5b9377405b9d65bec14db2827422d939cacaf30e7015600931d98dfe59a4b676b0a783c1a71e64c5ca7d9f1efc3e4ef3e67f5ba046feb09b017327d692d3519a24464b2faf8854909324b129bf46c2726a12f686e3d2107a6c45522347d38f960a1260147d6bc3c2ce6a7d43b2b81584d091943358e2802 +generate_ring_signature 15e1e8785c6672c8c9bd42e8b0b9dfa73ed16b7312e067bb23380a5662884e13 df79782b5900dfe6d529fffee15fbbc1768ed0a58b535232124ffcc89073fb56 1 5f5eedb18339d1ea19675e00cf95acdcfbfb71c56a86c51c473752b1a6eddf22 d2494b222ddf6a368339062b29abe06a0fffd651ca408844a13e8e9c1483c501 0 2a7dd1622d0bce1666b29a395cfe42507be6ab7f25847a913140413a1d3a1203ceec6f50ba962d4ba137eac5ddf398b393ead9efbbfcdaf6764cf0546c9e1308 +generate_ring_signature ca505427f45e3f3327ca3206a0b5da1292e0cd7361f6a625dd9345ea0d03da47 9fd40e7e4bc7f5c892c8000666b514a73ee6603def5babca6b51285c40f27309 2 839e8769b9dd788743f455e74f7253e687cca012d0ddad7cca0e4767e4bc79a5 72e824806950403597e8dd402e2913d458aa49d39982318165e32e5de46044ae 76e8413823935c97bcb8c388d61482247026874a53b8d31daa616b6f244d3c0b 1 663286613741470885912d1502946fbf9f44fef9fe2f9a983f36cfd16f7b360d28754ba2debba5ef534a566a34373e7830c5c23ec7f63bef8172b0e9d6ac8b0a07844b738e0f4d30ebd47768aa19e127f8691f939974b2de820797797658610fa1f5de8c3a8f59c4e9f84ad80ea86e9a6415f601edf6df22b0d73afe3f540709 +generate_ring_signature 640933f68d149a459fd5d65fa800ed1bc733ccbecf94b17a8a6678dc6e90e3d0 7b3e6f1907ad536d7bb54b41827c3d9483da8e87b5041faf6cc7fd235237f3a7 199 e1fd6f2f9381f01f62f9027760c178dd9f5716898395fa150bd04f9c1bcd7257 0c32219292a9a10f7902af43cb0b0ac820d2618dd2efd266e36d6f7bf375b546 44bb2452da68ccbad071ceddc6bfaa5a7a0471e67235b5db25ae0c401c848ba7 ef310ce28bfbb3868d079d856042448a1890589fd919796efcf0958a6fcdd49a 6138d4ccf0a22b094bf5f6f4bb25431c73194d4cd455225e9f90e890ecc258f8 6cd95891a2d29e19eb1f2368ca8c5bb0e17765269ebe249c2c9d6e4cfec2e701 518bad14481f59ca818908ca5290b892474cb9da9ae78dcb4684df6758a12fe6 486ef59a6090636d737f22e6f3c6786d600d7dd50084fd5a6802f4d1c14aa8fd 7b7ba8bfca6105c550f82244e8dc88a3d14f2573721cbca36bd2abeb5dda3361 6a8ab23a0f9a41e4f83b815e83295d23f704b0a46bd7787e017fb003111952c6 6e629c6afebae95df966e02ff8ca6589152dc2f1a8d79ea4ec806578a861bfe7 bc0df98eab5b4834f520506a48092bccb07099a8a57ab0d05d73a1b644ce5a9c 1bf49181176e9be00603b8697bd817908ffcaa7f5b6d3483e4a20f86a14ab144 2ee400bd275960a00316ecc4f4d62b0c07c77b5b39c9ba35aa21183e01ce7576 643c7057cf4f13239a05fb4daa429764b6c4812e6b31ff31990182c486537fa8 72c592d097ea1d533270ab38045068d9f8d3de716633a16cedbfa5de6ef1d94e 397c51ac125fa63b5872e6ab21da9a04edb4769975f0eb50fec6022b4163ad79 0ef1c332ec45f12caf7434918b5df896d7ff57dd0c5bfd517a99d1e45279dbd7 b85a252d88a0f3c910836ebbc1917a5fff542a8b6e193fb17de53aa7806a9df2 faa62a1b7452db23c98f0e36f6a9978640636d2a9a0f16713e5f10c09fe3e508 328f24953c88403511a3544cda71c6011fe7dfb9d56d3f24f0ff21368eac61b6 df1d055e26e6a5ef462e906fc0deb64d8b29598bc9bd85b5458082089bc96ddd 6c212c22396b7d5581bd5fb81bbb666ce3a184aec90d58d5e44ba1de5cde5bdb 4ce80f8e5c3097fd18e3bdaf8ba2c9319d2c038fe2133cb567699bf8ba797e32 da98eb0d9f2c7b7f966f5160a318169c9ac34176486af194cfbcb3d72860fde7 4660b2e8c795df61adb43ae7c80fd13d4f3291a55af278dbd315450a0c729656 b2847cd4e5196c29d705c1117de6dd6e517140346ec2331f88fff66555b453f8 74ae53ec473006db85d690d8241a46b390c62b66038ab2cc19fbb9557ac410f7 60b875aa3122e4bf0b8404ea36c488f0dda20ff1b4a4f3a23d13b03e1224456c 781f2e0c5693d7ffe37ea904f72274a969f3ca5362dff6587bd7731e3c8af2a1 2eedc4786cced370cc96010b5be504e587357a9fec120022c6f417874e885aab 57f3046490762d405a2cf33b0e42cc077f962b4762cad12d73acfc7812e53752 95d804b52fdee15815250d2de834ef304b6097fc5492370c2e2377290094ec84 91e241d7a90145bf0320818a46cee8638b8000afcf8a79c4573b9d63f1aa75be e5c1a8968de7e4e022a29419db34f969f0a32cad3262f609633f6fb7078f3836 da909ec5404bc643cdf16d774275fecf796a3f114c333dfe55b5b5f3cb605571 3977d5e358dffea1fb052a1cc7aea970ca6f1d5caba77633c8a019c099fd5c5a f4fc33716b9d5a9aaec8707fdc0ce658cd2f45b389c8d0e5cfe68bd986999389 816a38588fb04069185b53c8e7a152992387822555e6a42678a3aa09ed256b2f 73c235791b37af8bc5d20251695df714e2001301c2c42e6f4f0f6933b68b0b3c 37a1f5ffb8877b39e7f07c497b079918e528943284a86ce6ae4bbf92111c63ab 81127e3f9e51799349ca316fbbfc66b9615b81e34c1da957a2523cbede998e5f d022b7db7aab2fa830ca163e9db126143615a14d00784d029c1ee583b95d4ffa 518e4d4eb6c823e6ab52675ec57090510eb1cc5171e21ad531e97c1ebd9c84f4 bf18ff3b5f363af34722d8703b4ab8f6c20a5a5c00e70b29b4a0e2011248d830 09dd17995fba7f2bfcaa29dfefc5670acb38139f95c015604150699ede968e11 a10518e114803f176d8cac931c689a5b9aed0719fed4f3cf724121f2396e2bf2 840cb0975d20cf39161e599dffafd50389930787e3c9f9d5422818074490d30c e079333d9d7a6e99f86881d2383aa810fd16b12bea9762f98a3420f14fdddcc2 2d966bd97f8f3942e1943ece7b23d8125c4ba8899ce0f9f46784a2d1b38e4d7c d65bafd2b7fa8030414db8d167121da88d4e623e43aaf7aafed24ee837069785 672b6a10e067b982811c24a5e83cc2d70e92d45c2de5b499f906a8a9d695bb6d 508d519efbc059adc7eefe047c8eda930330c27d83fad61c2a41b502f07d4e64 239b57469cf421c31e765b9891a5d1a42251bc9e62c5dd55be426c500f5d7e41 bdf54bdc32858ebd44760b9afb27ec803fc6ccce43f5cb72290456c31a120312 4b42e3bf898e45da9dc4c59408e1f5aaf40bf11b8a4d09ccc66b4d626784fa04 0ac47f6b42bda3688fb69a48aa126d607e26ac91a443b2d6fc7669bc52a8dc2f 940f30149014968e9cc01fec132785bb6990f01ac0b7bc1ac97b3b5eb6256183 f81895b0ba67d9850828ccba479bec869c188f2c2d254b5612d627ef719a88a7 854708c1da30f9f98cb05ca5b8b8573321d4f729a7344921a9f376bef75c9e51 8e34b500038eae8d855ed7b80b3a81c05d6664958cda88e2518bc9e7eddce863 48fb26a2d6987b3b5dab2991b90e8a149dac0dc5021b198d2dac0a5900adef51 ff8ffee9e4ce4056d5bcefd56d6b06b481b72f96430ae41fe3320e165d122d7a 0f35e5b2d3736b6a9be73a6403c6ba0f98a938cb6e06be3f700bacf83e2540b3 07334dca089b23c1d1cd601e69b5d64c1af0347861f3754274d9b556ac9aacd0 e02f32c3a031698d5b6eb871798306b9f1f083947ef081e37e7f58226b6d09b8 6a0ce596efab1e09e24f83c816a2910c0f18d054a914fa059c41b1ea4c78f6b2 57f619f188c0a869d043f4c41cf43236c8e1ce80f12b6168233a6f452b667392 417484f00663dcaffd323823225e1cd0cf76ad7df724db16cce584492173e15e ea96e9c65d7759c4a0a9be30e0ad102f4dd0f47ab4dc75af9a8fcac816528d5c c01db326db562a48369f862a6057bf152e12154b9b71233f0b21b80462f56ef1 32050cf1fe635c0df1b584a947d370f1274e290fffc192dcd6126203518e47ce e6f2f9c2565ed7a334c0ca91efe58820010aa1ec1241e1b8827b23935f6895cb bc851af7e8ee6501beb8b4d03a7f0062442d6033ba37d5ac2f25144f7d01aa45 59cf88f92e93c57c35d98a382be18f17c24df3d41d8c8db2a91e6d5ebbb42e12 31f592ae1cae0de2e4f399339798f72f287afe968e9bc56aabc323a9ca0dd278 8ee9c8c5b1d3f378199fc47099e415fcd802651e208afece1284d94b5f45ce3d 508b5b43b6344dcd99d4c13df8d80f02055f094ff3e9d7f6db3447e01bc441a4 74872c9591414526444e50e95e0006a77b98c41b70c56a647772160c77603d7e 21302e5c387885ce1359e32b0988d0c3c0b645002d361994ee2031a1c9c06834 9bdf64b862161a2556140305b68339c517f6f54a247f96f238d45e1834596414 1693c3cd7e03df5737e6f206435972fa8c100d68e5d55b1efc6f26ae85e16942 1aa6e0e8cc7840effa32f20a21c92b48cf8a676f69e3465cd1594dbe800e798d 97598a6b0042768ffcd605725132c323250013d9ffdd4cbc46c71995803fd6a9 be9c2ff2e3b3e788f89547a687a2d8990d51c1cc70be9715840030715f14bb6c ad23094b33c609916ca52ccb48bfc68cfceb423ed2c230b4230d34409b1701ee 98a4bed71cc89baf62675cf7a8617177d6ce4f7c62014274ca9a574e974e7a26 c71c798ea3ec9ebf1f59b949e539a67aee11b8def522f6ed695f433b71b159f1 b298b7f06fe863b3ae360a12e6cca2d5ad1d105ae139fc01325be21f78d1aa70 57932261b773012ce60c5e26bd7a2a87fd66d47f0cc3b94437fea1cbe0b4145f ff35cb6db2c5ebdd155010503ff566ed6d1f5b1c9a36626ade48d76254250d3e dce0046a37851c5026c7a4ed303d870e9766d84a9e6d4e5976dcdcd60f34d4bc 6d37a87c1f685b27af20795e89487f790697b5c99b97dd0a19ef0711a1ac3733 f7266a5cf5b3f7ef28fb4e2428c5547c670a056b110bcb9188ac0a4452e39c25 a2b19116391e6ec0526b4af1f0fb04508fcd4306c91c7124196162424f6191cf cb1cb1c1dca53fa0e7b104127ab69699cc55aa750a9474b9517fb375faee3862 797b0ca02fed4c418405d88223eb001882e2665c0e631dea72205db3249a9903 104956bad09c091477f9906ed41f63490a62fe6e81898b8bc7d2044c02e7a620 74773d104bc903adb07c4e8bad78bbcdb44b5eea9bb052aff7ff96daa8f97365 8787c10d273fc7c43d5b055cbb631cd6fa24068ae2ba576049e029a174527222 6b13581a144e59149e402649568811cfe67d363c2466a272c2503861219156d2 61eb9b1780fc73fab757095aa7731b0a5da2c813cc36d1e607363a37fef27d0d 7672f3451e80b4fdbb01dc2efc655ac562defd71fe7e9c785f8e2d43b7ca09a8 2402e629ec86218046a59177c682f07d298a2327e203b9ba29aa3a7cda2a905f 2862572827a76baea9b113cd3057dae5021169ffaed7353a8e8a150d99914941 95eeb5f30313df74b524624cb8ca9397c97814698fd503eb80a915325d4bfd65 02d6feb3e2e024540b00f37b5b2198d2ff05d1398e1d1c1f3b73399befa20a15 5b52a566cbf14cd15d44df91d26109ffd242010a5bba7692d8b4e180e79aacf3 a713f0c2e91fb5e99e2974efa91de6fc0609c64a0e0cd2bf849cc0ee7fa3fd36 73ba4abac7c1325dd63f832e34dee41d4189d90e64f775f904bcce4bb3db793c 0a7857cd3c77a4c8449abea91b3d424fc8f7b1a39417efdf5f1b85827e65621c 0bf93c473af049f8496ccef2672503d3c75092f1dafcb7da8a60b7c6bbb1386a 5487792ffa3566c4c3c551abda71b6d5b461add6fd81c2db459017af4d3686c1 38bda5d44463d0841416199cd4c7b5a924b1965a77ece07a069763893a9cb75d cdd3fdaa97260218c247273eb293d4e90e4b0a0a2bfa1f72302fb98e1249fbf4 cddd8cb104fed1e429b08285ae0b471d5d0bf74fd1606887171f7176bc9e1253 eb84cafe4857855499e373ef983a2143e1b1d6155e94a3407e2e85f164ba421c 46f8579c8618a783fa34d0d6768d7dce4c4460b1ed21a24d4b48117ab148ec3c f8443fb9597919df3d9caba2a9836b1a3076b78675facdb890a574090c94cced d297d85af7014563f80c4f1a0d1886f17af1a43de53b34c449d660e35d08eaf6 1855741e4cb18127641e4d80db0e585c6f3d357eaa5ab9cf134e52f3023bc2cd b3eb8bb92cbbb2248a347b7d439d6e7deba3e11c0f491dc8dda1234d33ff7e59 48eac61ceea14de3a054bd98e687cdb860ac9dad6f3aa5732c1e2b8c2deb8c43 05a7638c5fe710f66a02e55b4cebb837de550a5ce2ebaf2f036b371f366baf47 6781eff113890f0dbe3421870aaf39fff6f64a9773208450e3c6cc5180fc0446 c52ef31914ef680e9940a5d04d6faa9d5a3bde6f30e4392a9614b4586ea11fca de390cf19cc66beb0772ceb5713fc9d06233071045499e7baa08f0aa398ce9ca 50a4b637e93b15704cb72105fc9e0a8c08e02d4bd386421aa42df597cf32cbb0 5e5cf1bede2ca83f83b04c7a3ceb54283e769734431060946b4092a6e3f1043a 90107a3e5bd79cf0c9d14f7c41afad86e1564a2bc87b96b78f64448278dc83d9 5c12fd730cfd26bc35831b953ef6c88ba99c02f2ed682fcdc5a7b45eb2995e4b 6a5534816c314480e918bdc6ace3924ec8b3ab9f7aabac1e46931df705d0d6b3 d33821f1f4480e39146c5d22c5f514d12de2acac4ffa1add24e94d0aef01e3b5 0ccfdf97d8f4d1c3bbc405a6c0a05cb10c84e68a9e828e91bc95613184285887 d8a28eda02b6054603560444fb09c586c851f6d7dc9cdbd62b627f04ffb1d125 91d7a87a8f597ccdb99c8a1fe05cc9c025aa267c63a8512f4ad9ede0f598817f 095efc60133065f2367438149bb8fe64be7a8e2ee6f0b6d66e7c1d44546863d3 096b3016663a9ba25eae4b5420d5dfd2752753526b5e300e6632a52f581246ff ce0f9ef751a001b0ea6c95c8f93f6065bf242b4ae69d8f0300243e223023235d ce21a3b1d7cc54cbee8c3b2042e2251d2476cd0e2f73119ecbee806492eab3dd 93976f4fe773f35761b0a3d56b5f260358c7b04960d6b01342c13319e11c1ffa 09c8b32547300288d6e9c3ecaeaba6dd15c7fa037a565b201c961f7aee9ef1c1 decb1e86aa6a956e687aeccc633c688d76fa9d76ca8d7b99961b1591b4c3acec 530080918050c0f671d21b63e4cce47b639790caf570a32c88700998b61f77d0 1cfe24c75253c93e025d78117ce0c2a1571e2e89e1687a28220c222b4582718c c4ff26336a238471bef959ffa39222ca67bb4eceaff89a104dbf99671b92d7d2 5a306e5353c2f33d596fe151c616ed716d9bfd68237038714b53dbeee2185f0d 7c0699c7e93eef0f2cbdec06332b23fd9804512d7b9a709790cad1291ee62329 07b843fbe8bb4882fd4bab8c042dc86a597985fe6c031bbaaf14a1ea7b1d2a8f ddcf47a01055394119fbfc49ba1dbe50eaa73f3cf19cf8b60a96326333bd13b9 6884f9ea3a3c4684547f6f8613d1087ce35d5b14dac078dcb27d63a5c3a7c626 c965f6b81f78472b1730b1444d9ec15c9fb6b54918b7b3f4993ea21c9610bd34 bc587636da4ec349e715ef7cedf3ede92d7a3cb0905b2dd35046f01dd12ffa6e 901f65b107e0217ce7569b32cf50ad1cfb72834ab06803f5b53db3464c6f05dc 51d7b3ee2af84871f009a8caddb325823200ae4462e88f3bc9c94b9bff00459d d85bf76e475ef81747d3eda905cce4fa6e876605e102b9b2e8f72cda9153c8b1 9d9a63453c0525302a0d2e7737449abd52163c08f24c1e37f15a527d6be68c95 b6e86edd5fb169911f22137f1111a253fe2bf493cfc989dda1f9a880f3d5ef9f 7d072839af8950bbaf6313a4cadc4a04f152f16ba3e84ca932d1445f9a34afc2 177f7508e693dd881969727b67446fa7eb9a47c26e826d43bd3a6e0ab5834bf2 901823191b933476f034f77d166cc5bda91b0c41daa76c781bf098e13983885b 922f40a9eab5b333199265429c4fcf25f459e09ebdc63d5e077bf733dc7412f2 319fa49be35d561499b802e63d8e2c61849b7639c623785266bf3d6dad7e3b18 07cc2c6e39549e7f76fdd89aa4a9becd09d31822781a94839007720107462159 dee6471f5a489e8ebe6ccc9b1e2b16a2f6d7df7919372b2115498781ff90efb6 29ec1cedd8d6ef03d011b35c17debfb9d4b202d964164928daafc80c66c0de08 9fa044d016d3ae76f4479d8831f4005efcc6daafcc78b0bb8cc8b65f63c8e9a5 768c5cc4a18fea67f416195fe7e1af269ad1829cb8861e19afa6384239e6894e 914752c736372d835b803db11dcd48156a4123895095f124b39279da0dd5236f cbeee34f7cef744beee1fda22bff75f4423bcf170e5abb20fc7bea38cea001e2 9023bf6f93a11c44043ce93f694351453c492f86feb6b44928d253473652f5a4 124a47bc0b26b26c43f6f23a65fc74d995bbd282e1044f56298b54b599c2cbb5 5c2774eaadbdb04a732dbcf9c23c77c335bdbcf686369e6e8223cb0274253508 f46bdd8fb828d1177c62dba969af55e292901774759d2c79cf8a5f1a57acbb9d f2af226a6a889ddc46eace4b559a7ce081395056ea030e1c0f4547b539c4b0a3 faf7b038e862ac015e8190049ff5cbde4f6b437adff070df511866e804ba4959 827f334cb6e5fbb142890c745e63905e0f0778d2a170369adb64658014ecb0ed 7b43a29455e7bf9bb18c406d8220c9dd42e737967e159697f38fd77174949c27 cb633ae5fd91d1769d88557b5a428183ef703293f5065b578e9fe3ff3b1bdf15 fc811147b7c40d424c287edbebaf6d8e7242d285103d1b6f60487287afb4d848 32ef999686ffbb7655c4aed56d167d2d7292f711448e8847a1a05e55e6424735 86389e37f7bc902081321af80c748af26665076e553bad8bdffae84b5cac4466 39033a1d13f762335650c32db88aa2c19a128014946330e09a0a03ebfb06f604 f47a687b06ac083c8511527bb3dcc57b8483e295ef2f24b3e84280e739f7baac f8451f5f9935a8713f1eccb30d1800915f83e0736b8c71c62cd34720c75dc7b5 227ac01feefc3da4c2e7b452e1cc37b20e7b5f4effe6061ecde47332385355aa ced1f5d61e7b24c1f65297ed6a611d92f233473862ad54d1a7134007ffc35e03 cda1925c58a914d8b11ebc4d2668340bd55565aff5c118f8a707141a4313af09 29932f04e23bf8c4e235571f2ec07cb384da86c855d2e3b45fec4051a6bda5b0 3fe044639d70fc0e5e6757db4e45df0af07385c3a25aa4dc8294a477bf39be08 bd398f48990e8183d93117a6162841d3df3eb78811f8a84e101e5931cbca754a 52b8ec2637c8327591ce0f41a81f77603ca5d508353486a5b380f3c495ac2b2b 2b6fac9a5788ac550314d5ef4a85d855ca12f866dcc8e55f63b7898ee77c1c17 880a74bd63ec12ffbadc16c57915d3098476cdf5b25c4e8cff1de4b35009b7f8 96bb3115b7d40842c7ea3631bfe35184e2d16af4261bd3fba60c2913e4911c9e b3d442f2d897e782bcab66f0a1de170fa5171f588761a3ed7f80a04f59644abf 4da03bce5db4b41ef54d3a5dfb5140fb1794332c20c2cc80e4184549ec38ff68 cd6abefacad45eaa7c9e295c08857910943d9b0e6945ba77199ad93b5668eda3 6eb4e11f2e62cfa6164dc0ae1ada61ebaa6949a07cd88303c633835fa98c1e53 e4705bda82e65f57781a596d1e54871dc587e1a614b77eab59911c1721d0090d 34 3ae4324c1e52bd475e2a6411c3d117bb21277bdaa719ba24ed8a9a88306a430e26d7c1aec0c3f5e0812f6774265ed99c3ca0cec7976bcfe2ed7998e52dbf320b54dc05806075cdf8121ef4fc9a98a2f8dadb05e209d9b3e361e6633a4453550700c3f30b909fc8604998ee6825bdf82bada7ff1b4ae822be338555cfd6c4960f4536bf38c4b3118a08e5bc312f4df0881e73947c5794835de5708b1e149dd30ce5731cc878081724f896144c4f17a20213f30fe49e20c8d2440e3bb0e8db35063b4f5a3491011032165704f56becfc4c7c01b053ffe8e984d3d6a22eed7c820183b16410989e896f9482997b29b7909cd5d43213175084d9bd40bba761cda303ab5caccd7fac7167b1b2f36ff1d67ebe0949c52e48f851c3e0c1a9fd20706902a5368771f4a3324204c2c1d379b45526bff6e0a21a8a7ddca4f9d4f59bc09a0e3cced0ac95786c93f3fef089a641017292797344347956557642f548ed720d0f768b0d50fb9b6bf723e87dff55e702e6678d07b98f4217dd47cd1e8754da5402ddd7dbef15375f0143030a9f48568fcca9ecb507463db9159b65ffc73937860a1252615fd5943ca9095c345ada8334fc8683ca3aec1ff0467d5f503eb56c6a0f586a7918ae4c07958e5f39a4a60f5ab5facc2acb9796775555f22e87a9947e028742ebe38181035551a6ef9cc822a562aade0c24ec78a4502c66720bea45ec0ea4485a61ee9da2a2d151a446e454b4219e84b4e16adfe962adb0c6ded6652e05e8bcdade1c95ce8ede4c77175fa576440a5ab051c75de6a75bcdb5c05bfa560daddd7c2fc2bd8aa8aa65b9bddd6d42bf679754d8b7d27925e7902ec7b1db76059c0da54e82be1c3a2ad50789c84113925717734098e3c2c9faa701392f76c107bd46b9bf99c40cabc36d03612ebec1537d240e0b6932ed021ee97575e98ea506cb439ffd5198266dc0438308bfa88328da606712bdd9e4fc10b91e0ad9a9f40c0974cc094cac423c8c9d974753e707a97cf5ae1e3112ccf4b83ea10cd94a2b076085ae1904701bfee5bc105135d3c5628c9e7692cf3c18312b4b7fee1db10a0dff74f42ea71820bd6e7e9217adc301556b9f4b5d385246bac3f273116b782e0f207b983842766e5fc9ac41720e4fbf2f5c61a6e48a36b0c6dcc5078c88e6ff0b4b0f69a644b6c0b0069d5973a171ede818c3c01a174476de8107772c55d8d80df0b86a14ac2dbe733cf6330a0c7d7548e78ca194b363bb64e2fa1dce1c9321064777cc3b0c65bc8def0c3a11a0fafa4c1ca36379bd35869e85da25d4a7b70707b5b09ab0e27e6bd14be600797eb2d43bf6dcd8d212b8f34f8bae2c949166ae046b36cfe12b3562e55f73cc5d890b5825a46bfe0b9f0ffa5df2e59fe57ac9f10863028b477c9168a75516affebcc52e08356af5157e836ad28f24c3cb38baef0c736dfcbbadcf46a3d722b8549a6edf470a4a4c838948f905188d7f727a1e850cb87356190a59fe5e973b581febb29ee36644b34ee6c5e660abcf96f15bae58066b2c5021551c52aef8ba74764f78826c77feac6b3999c5d7247d26f01926c70a4c738bab8706e625c4208f3216c19ebe672212c73fa946e9193875f7b24e360363211c894eabd7e6eef35773f528639b121864ab30be1e681fcadf1eeb3b330f2951a1e01cdb3a595fd2af1ccf09ca1c3518dcf5171bae0c9a8333db5a086005bae6646960a2f1d1ac25435f2d95dc2bcb5ae9b79c418a23acaa0649dbe4680628d92df7831b5b28ce03b984d429e9486b3c9ceffb045360b2fd8ca3387e300c712263a3c6daeaeb1548a52e581a5078082b9129cd63c23899659ba4bd205400a68c9583f4673bbfa613949b6e7464a4bfb801f546acbe4bd3325f944a316e0e9b6aae590b8f39595b07751f16e21fb4c93d5f3b6f501484e15fbc2134f5a902601e953d867b9cf1d9989192c772f7ef1f80fabc13cb17afa2e78d8e19b5b000c082177ea10d152b969837a1d773c83b42a8861142b48cfb889970bbfcd6ec09b01a0d633f3d2426314fc45c3ce964c1ec08792b54bea439a5e91d68b1fd800f5bfd03c3248329d548a51269bd9c68b410926980066e396059ec22501411090d4f0e93a9e35bd2ec88394c953868308bf5610bfb62b12a71c84b730c7b4c000ec4e61e6a2aa0a944eb65f6f208a35ea6ca146dc0092a2ac90ea9dd47af4b0b079799ce05938c7aeeb8bfffcebea7ad23d2c37a072eed6b5cbcfb55f77ec31803535960213ba8d2fe9a9dfb9c7963f92010789a5d4c45de2487f65d85f28c6d0c38fe761f3b6add1d0799cb918044aa041e545267d5a190c75fdb613fc67f3e0dab57e9f7e7e923f955364dbc9ec0df23a83e362d4bb7f2b687544fa5c4c14901e55441e0777b6515e4604ce3e21490c48a2642cdf94e4c2f1aa5bde5f4f9e903ddfcf2aad0bfcf67cb8f851d30af74ed495f1e6515f2fa89ae363e1ba7b1810c56c1495364922aba32f85deed3d107f083e2773bca35b9b158b78538a75082009349112467d4dff104df5cbe15382b84b82c9b698e94cdaab05603f45f139309b833fa81c9026a4898ede2169b2b962370371d16a4fca576811c4d1eab34a9029b9e2f23a418cee7208e9dc85b6cba50ca3c373c813f0dbb1612d67bda7ed00f511fad92b30ac627de73a83ac15a7389502898af551915c3ec0d0f90c4580c07086085ef82ee35b21656b6f79bc5e41dfbf4d9b12295fe3cf10ee27f7154f004b2204b698347180b69315ca862242b3cfd668ce7a566e65e01e90ed3d18d290d6101d5911a697a6630c1298ecefceda2daec744d9d489cbedc12711a0f6850095b289dfbc65540cbabe34f564bec71a3930d031500af0c08db812ccf2aa55102d3ed80206bcd317205bd52eb8e178bdafa2366f9127f0b5e8250208a20a5520b4ba3d45e68fe65f1569fe8a7802a226db0cc2cea9c39a74e665409a9a21cd90f6696b4a2848e7efc440928f9659c9e214eb428868180401fee2098ee17aabc08785d75db60cbf57cf664fed4dc2483dfa862e9b88fd36fabd9f3ca412d703307c2d1c11a677de31cd926cb9b276d644131c69ea6f7fbc155d9f71e40aa5dfa0c7fa84a3e66b139757fcc579fb32016b6db7cf35159a584cadaa2ba5e99af5101199650f58e4217c012cdb3a03733d0c6f0c7ff2a90d2d55606f17e2a1e521201e7ce83b87c08746f1097ad7468a8e7b9607c41b0fc9d42204f7139976576b10f1cb5889eb5d358351b98a30db38f38480b0b36b3650c6e0c2249b746ea01eb0fd82ad8ee894f4d14e29c08bfcb115fb314ce63b0319a14c0b3ac58140e30ec0fce3e717c2e2a22676effebb4b2dda1140c9a40bceea3750563d764b9ea470e041cde073ac25a270ee6850836552f128c5a4ffb8511c1910b36de9477aaf5e70bbdb8b6772b2ac29ac929d4b6b92d8d16d5477bd6d90b196a1c48314a72f95f0fddb9ca36af3d04cb96ca7c22d0156055724c0dc49f9517d2e65dd1b3c68125024c63791771d94c05cef483c6eac799fa1dd412b347ef47392a20dcb991415003d3260a4e561b2203ad94baf2d654138e162e0d7ade81a93ba7748bc548e4270293ab8fc15ec63d6d9d7b1d80681d1b58fbb36b0ea7f810e8f46f4d7b7b47040aa682fa8c17dc21c4b77425a6c6511d01effbebd4a94bcbba42e7ecfd0ddea50c850eb64e9828100cc57eff447173b770426af2ffdb222e4e8e2c3aaa1029c2066cdea26cc2d7ae3f3f95a407b95385ba9207190d36948814d2d58050f6aebc0f6025774dfbfb328dfe402b3523d67766fec93a061d7e0d9fd7c24d16f62c100bafee7bafbf6d8e9a0557e254eb56fd53fc10691a800b9267bf50ad88280c37049d9c2798b7ed9bca0fb999ea43c307a5f5dc499a1b0a4737572b1c00541c1b0c4c3ae48bfa48a79f54a56bf97fc69bf1ead3296e790ce0d6368a4337dbe757083dd376ee334adf5307e775d4d14872e9acfbb5887ea23d602c175677ad029f07b01b7a5c9cdae1e2efaeae791a6a0d59ae80abfd8270c594eae360ada371bc0074480cf49162aaf12e13318d46a2d9dfaf63024c5a1b6c29efee3f1302e6610d5f351c69a4f3f41bf0241e61f382e0ed5e08500e36136eed08e9723d7786f308f8d2053ef695e391095a5c2113d8c4b2955797c13d3b49ca93d9392de31e34079aff4edf22fba02e49bd82b667c6e53c6c32d666acb817bbe5ff941065dbca06eef17ff089bbd3e88245b0fe6db5ed4f0f36c20a156687f135b75e05b5403b0c628cecd89f0e682219e900cc157a0b2cd21d9814aa40667c17f23c1439ceba031517d78a9c5acc4a5f41bab12c31d4cef6ac61fb4f4da7347ef67c3da2c2f10722baaace312373fe3e6f0b393de9380478818ff63244dbe32460672c82e928064b536cfbe657f162c7250c47cf15d8a4a4ebb7532c24254b4d06f7b30196180a6f426311710a37a558c8475b725b49a69dd896df74fdcb55b9fa402b56b02d0dae8e4c1fc9de4cfef7e764be27be97e80984ef05ba3c71218a0babd22d835305fc7f432eae40a45ee9a426fccd42f5fd816afe46b6e89a2af7eb5f2c21fdcf0af8fb6e9faa6f8b45e249ae77e70b4c1f4a114cc80fad5ee6ce1606787ce1bc0c70d2b4cf9afb1ab48c58a38a3bfdf886b95ed1f0a9fac214398eb5b863d6f401f58ea3c5b267a5087904b03c87c511f7c477c226d3670f6b877e632b3306280564c7457f15d32124193c6f91836e38f1e51e09c670ff4169abf0f962eca96a0a809ef331f2813cc51cf5d5f601acc4a491fa910a788c778d69425db9a6797c0a4590241f68bdc0e2dd9a677e89a2cfd65b094ecf9952474ce9c83ce58bd0290f60ff3d728c1513869de646dec36b863be4d1b6a0608c9b46e304818fa2a44d0d9dd156209a299b840933229b15374ae08c74105694c78e515ad6adbd1b459206b538613fd322752d9de3fadd52e39a87e6a8e48803a1e858c6217841b37197005ec2b50b1ee8a78294d6a938a8a3fd35696e086840b8e2f85b388951068a6501e00d845f383bc3650533041ef0242dc73ceb8bb6c22df4d0af63ab4746f4cb0fa1e12b68b249ecd48047f5684b45c7e238a2f9a362fa8ab5ee8623f113a56c0e582e8fc3a3acb2fe86b18b26efda5ef34a7bb4364195fdcc2673112884f11a094c8ea5970cd1f9032b1d8dc7e1291c4dbcabf3237f8e4aa126f84f7b1d9ad500fb96369348a58f365688a763bde285aa67c4955367f024c841473461da445409c6cf4f167a2122528b68a02ead545186307e435c0b06db0c6a88da8a1a940b0696eeb1a2127c4aa1edfc86af82fc3fcaf9922ecd7e62b9a1524a732022c7d00cd5f53a678f1b8470981137145014568dbf4c141e5ff8e5e6671c2591f681730a394ee2aabc2f7248071870aabb4b8206d46311f0d0eb2bc81fda4998441b64013366ca32536a7df7d5d74dbf0f65116fb133fb29d5a5ac2db6ed501f1ffc5c0a7545a3d82b601d16a71cf81937565b0996d16a46ba53faea8289b419e815ea08d32cf244233a1ece6de45020cd38b0f8927dfc7fd909ee440c7dc1476f4732015ffe4201bfb80a505df9443164ee829833bb94acc64610f3d441d8d000626004379b94ba7124bbd1bf88171116263feaa514d1bd31446413212e2bb40b2731036106f4a6d14472304a134a022ffff9dc6c5153793ee7ef7adb46e35f99f1a3015ec639e975fb52acf24846e0b4b8852b2976ef046cb139f1fdcd337530c6250173163a19303d101a9198a5e86161fa34ad747a519984620eaf11d24edc6cc8089b1565affdc9ed903823c9692c1bb4cf60eeba36b39a49d00924f8c670e4190e398383539ca06358d0de2a2158f4c5745d09b4308bc89caac1448c512b2064090efcd0c8c7afca41d5c8fe5442154229c53753ea3e333b660c94457da36330081f41b0bece83a0f684c969cc905642e1628ff7e0ec1847328a1c3efdff3efe05f11b6a12c0fd6c1990816d9621f2c533b16ac2474e1eeaa87b8477c4a56c9b040feeaab08331e5be5e6c1f7ddde6baf7a6a50f9e37fceb04bf2bd588e79f3e01a9bb1016e88a66d44b0e38247ebf5fb1a23377c5b4927a8853fecf4fc3e3fa09177949bdf418f69fb761ef79f7a919f294ae06c0dd6895abfe7e6943972645015c06402483a5aeeceb05d7a36eb53e1bb9e818a39c49996c3e0e3af16d477e006cadfb03e5f25eee3ef1ae1da9175c09866b35832283067d98783f222478f30825f59b188d0f69f916946f8614ea06de4a2861a0eebf6b29b5046eb8a0751807077f9d1e896df8d0bf9445172113feb82295136046d9750a464bb808fe13470693b4137b5cdaca5bcfb07f9f0b77f05bfc9643ccd597c08ef4d462baec2d8f02859db7248167550fe484dbc955a4695080996a4901802f49714c956b6a68950e09fefba153447b8119f473570430c9ce4f3e527f3a830b602dbb00d75cfaba019b88177f871bae33931ee3b26c1d94c73eddce9fd9dc9f90436aef8b1eee2e06d126ba313a3e75103013972b2e1e30e6ed3ddcbe0d37479b2000e6126b70a402abf09b5745233f3966c8ce0bfa01551e1b3c2d4c6b82040e9a058202b678a20bf84faa5d510c8724938aa0b81612a02ad02dc6184ee3bfe3f051b1d3bac60506c1d96941ffb83b725fcc0b0d1f2dde1f5011007d47ab72376c300a872cc7280fc0e187bb5d9362e2481c03ad1da3e92f7edfc43f17301a7ba9105c42d2154904c91bfb95cca5d801dd9dd626c6163a936250f8704d36d7c94b84b46e2ad2b30ecb58e100d75b274d322f0057e9a48934e581f2e3702c6bc1e619b783d6e81d036cd4b2c4612c6d02927220af8014c185dc4250b1a3805e6ec7a9d3200ced7206478682d3cb52ee69193aa3d5fa750220dbd33d354899a848d562f6926b28910e9f790ca308a6b4d4c1a9d4eb0284318fca65eab48c13bfb8367a19719b7b9d0ed19f3de017fd75d0a8a73fba37cde8a46d7d1d4339aa4300d88f03129dc9c109e3d6696a7236925be8753c126aba9a65c03ae9da6ca19b8f35f1d95f4db28f0cccc61a33c4622e28e7a052a62847d9ea5b5d18c7fc2c9164e7ecc25f589f690bca6b103184be4a0bde9e0ac32bd57096ed602875a17e6a22cdcb910674f04008cad41d6980c53e5858c1fd4796ca7ad8d84aac462aaadab2408695b2a8b539085b20de2ca4968a85b8fc46f3e1a026f7c97b4c0c0e0d7a9d4af11a69d1cb260af5e292201b60955c328a7ddc74e2a3e31f5577864756636b85906e723042c2007ee2033612b209af788a359f96a0c6f88f8e391f6457caca778105fbd5c37c07f90fba02f89ff93c29aaca3740a8a97ba9f43ca764e9fb3a409215b5a708ec00449080bae2cb785e63dc3036178478fdaabf22599d65c6a3fb390aa22e285c09f219bc2c37c058373f37c9b8cd9ce7434759878b1ac4ba5defd4d185453c27002950769793484dd1726257176e110f3f6e86850e267015776a64c008668c52030de7ae99aa19826f71717fde57bb3b3e0b41872db27d215af9690093f945180a97340a943f4b60f358033438242d34a5f36fb82dca1bb9d73538f16123a3390eb524c35ccd3bcefea87838b9a983270b895a03f4f9371533df638e30d361ce07e6d1ee021f9976729f371ff08531b0787b89802fff05854e48761537f6db060c6f6e3e80783a3a5a8e3070319c7804eff9b99a47442dfec29f0185019831890abdb8962e60fa00ded0bc44bd838f595120d29dbb886083db7152938caa91da08fee5d2cfb40dd9a44b35e91fdbe204057d5e44e7cfb5ab62e8eac5483826ad0dd396f62b55de1a0c7585aeb79b95d9369d1db4aa71bc662a50736e1d5963720568bda234b54e68d25004f7fbbf7b55193e7f1ad3f2039c0dc194a977d0cd810a69515f1449c27a48b4406d624d63056ff514e759a55bfdd11041aee06cc5fc007262d75f700059177824fac664911899017c2eaa33e7349cd4b2e6d5b3d2ff06fa9c3b4d70288cb78ae3ce6f949fdb4aa4e55d08673e93f1c01ae69ceed3a20209050e37a9d62239c0cb772736084b6848ab0b151fbe15944345755fd8ee33005b7361ce325f1102640a3c2febaeb854741517126cb93574b61d0bb23939090e016b8da7878857e1c3e0fdfcc2eeec5f5b4ec67ee5e34c4c809acdbc9f6fea047ff1688c45416e3e57e9ffd984c15eda5d935bbca1b3fa79aa84debe2b60290122997dcd973bbba4ec74ef5f4d1bc96993bd23776a5da08e9bb8082361a464023a8a4263b83ca52a960c833fda184b3dacd111c44316ef4a8aec095919e7ea041e68081fc7606cbf2a91e0463b94356ae15202892c48bac0e0fa03e5e5437b0dd7d9a116fc9e4016996f4badca5af1b5e22712dd2d9af083c8f72fe746905d0a4dd0d54f26d73da6d941bf9874ab21e6ee408ce8afdf8e309ab0e711a1279c02ee67b0f76326ad7c61dce46e4e665852b8512c77f6c4549edf6e3fbfcf670c0495f72de17833715b80d8f94f999c5d828298373524620ae4fb7c005e992ff602665f38b113729a5d35b91f7eba475c4e8edc01a526d4ad2b6e55c45a8daf240fa53dc91697f479f49d8725bb16d25979247ea9e249ef22d814172264eedf8a01b775a40c27a551f95ee229f153449214f193b4c46fd2cbae881fda2733fa2805c48a2e2aff9dafb8a32fc33171ddcd876d155832b32fc5bc009415503d4d930f4c2da9e3150e66f2b82f7e217d15ff8e12376ac6b0fcdffb140930af1c9020000cc0001c7c93eda209c95d2d3460f02b200cb5a7e31778f9750f3bc6eb24930e19769b93c25152cb2e572e294f514c44eb67b42233fbbc1ecc0cc3709faa0b0861ab09f3e8b9f8d7faf9d42d80d5651bf74871f5edf4d5e48a85cb384ebba400d6de67f0926079f56ccd18d87487065cb4b7261b56d8012e1350b8ba4870fc0a1b61fcce38e9b10571b176b381176128a73e20206203d191cd15723008f00005cd318b58658615d6f34aa6999ff88de209944ec1d0a3466fcdd6f804d5ab0800003f4a6280aae95d97cbe9114a4c47d2615e611344be4052ee933bdfb4d2c50bb06f17858bb352e8f01d23b955532135e62a98876195551a44d42a92fd18030f108d1620139d0bf80cbb174fa5662dcb17131b88c230ed7d269c69a7e45e55096f0c88d4e5c83c4490402e00724c74fdd9edcf799190147e6711b8e9aaaec10b46e247353ba07e6d042b42acb6a46e0ec0846a87eadc9d53e4a72ec24c7fe00ac48e12afbfc4d3b96684025674e85253d88ec4ba7dc66a08d5f263fb290e2800188ea6488ac5701f4e8b491d03f7310a04839e6e00f8674d6c64dae8d5f39a0ede617a3f0d059608cd803c6097e272e726a348be174bac91d8c777a47c6a8e0f45715e79cf92e407f433a47df0bfcc17cfd1a9f4ceefe94ee4d09940ce7f5e0f2d05a5ed85876d3c62b21685e955c5a69643450725deb39462f63c6c64646708c61aa914fa2e95ca21c9364965a11f19f7fa44ce230bf591fd6b84ea91aa230b45c249fa3d6e7fa4ed826b54ba82dcb8e1b4006871d0bebcb626abde26bcaf04ac9af0a4167299c0a5b49f5f273aa947a06bb6aa492f910c6d807f07940889042322f819555f4d177835bfe6453d464b321501833e5b81169569a6ecbfc4630477d071de15c128b7334680234e608976847249eb38eb5d4530347d0f10abac0ea677e2125bcfc92258a8c8cbab325124d23a38a4bfd8431d595fe546e09c0202a14d7cb39b3b84af9cedf731ffa42f321805bacf31abfcc5235133a0d199c50e7d8905937c6146f8d5b9ea80d5e82e4aa3065fa33945fca1d496d9a95003010dfcf59bf5da37320935a71655427b8b175e1145b0771e6c8ee78c13b07ecc8906406c9f13c18a6fae156e901dcc5f160a5ca480a64ee4920a224f455dd5f5c50ae38c644e51909c5f101ab5b3c4916e52b527f82545e7fb27ce98822cfade59055a76f2642c986674292d35b911208374a76e5f4509735c372e2fb28a758ac708f3f2b851c1539bacdf93ecc6ec1677de99bad9720487b18a08a9c3072861270447f5bb2b5512a29d55cce31fdd206818b0b81530754b6c8d38c5a14b10a41108254bcaedbfdcba04fd66805b030c12d6adef005219aeeb251c4b0d422821ef03e492c9b8747cd195065e2fc6fef3f3381d040b7690e9f2c01739c68a86846303eab35b0eb7faac20a8c03d21a2d7eeb758c3161c14552c74fc1e4f516a31cc0569defbdc2be4ea96654398883db97bf8d389970c8140c9c708e416216cd1710f7ff4d65176f7566525498a285c6fc3522636958e1fbad9bce6fd3230253cb50266e804907319770fa0cef00cae6aae36fee7b2c08d7737d2df20a76d5627920d0a52c47e10047e6161be0f072e8d62008aefe7c2b2be8605fdb974b04bcf000582aa1d13b07305313319a2c5d85c61d3a75cce95ef64e5b389805ed9d05b5806f23e326a855b7aa0afde99b7ce3ac3ac8ceafd320570930f0bee91596eeb0c01d6b107e5ac724491ef9985797930594d9000e9b0e7dc7221083558474a7bed064b18513aed7d3d0a276e196c7cadc6bde6c71e8513a5e49e7406d0e05744fe0abe1618a0acc19b5c9489be7d2ba5a6269791b743d93ce9098060eac4eec41c0c98191ef618e472988e2f76410caa48128a31f14b6256f650b08ab24faebdb80a91bea2bab9c13baa04d5b779e67e055258b231ef14b71c717b0ae8041060f601597c9ef0124e61d978abebbdd4ace0acacfd7059ba1a606beda4d4d6eeefdc0fd6b18ca2e9390ef07869fad1d161ba24f522352820d2f1c24ea97c3f9e6746008f6b38984d68ded0aa1eb332b4e1b0fa76734ca7925cb329c6c95de76855e60bb6577c3ed4cab0aa65a2a6da4122bcc676ac5fefa9b86f06c822bcbe17ed1f080bcffb11c71504542ca054c0c6162da4da2d2b7c1484b2f3652acff2ca94a60001d303468f9163ee01abee90b964481321f1e3ae91940efac190b22daf8d3f0951404d365ef03f79b628ba3bfb710b193e63aad5d7f0f4bc69249c6d8e13ce08467cd348b5e89ada62b9e38ffaacd76736d13d3c063879667f19fc24f552260757df2f1f0b4849b0f31ec64e85804221a994276bd803f9390a53e87a04e8770f0534e0d0a44ab77ca558257fd22b4ef26b74efec0a43062aafafd1c7b0f78d0b1e043ac6a1275329cec962257ffd71ba6519aa7cb2ad4da9507dd8ea1f0cbf0bb7b5eb7f56017acd43533f07ebd90d736f0da9bba8e273c954e171e2bdf96b050255b4c28e773caea463d2061142fbef5dce37d0b8976e5d990a93cffae8d009dd6e3779ed5462d258649bc0da56f1ada0750bb31caaccbb50c0df4d063ede00b4c3a77421ec734358755befc2938b4199e2c683aa8fc924407fa151c32e190b62a1c6542c6fc39ee08c3e7fadf6847788d22632d917a372649a605ba662540b396197a097c6e5414a2d44742fccbcc9e991ba0d6b57c3494a60e65eaa6ef406c19aa0fe9f584587ad09dcd0c2e3c22b12313e54d3db36ca8e60a7b836f5470cc22791fb15394228bee5cbccc65472dc62eb4e6963733d04651b713ea8239c02a0ba61e5f1d61020180bc5633bfc536d9ca0d630c6ad3f5084573d7bc9766a0f2963b0f1adbcdcf9b53f5e1354b273e8c0d142a1c1c1b6429fcf7a8ba6322b009d2e2af428e841a5ac67a05dfe304d97476d37a968c5993cfa5cb62d09f3bd0d8dc5c94bdd0b81a02e53b728a5eaa24b96591f70abef1ae98b7ab07d0e48910a7f56f40ddf7919a6824a2f492f95a2458de211fa488cee1bd34c5698294e850ab454c7dd6e82d547d4f8f283c57ecee87584eebae6b177d9d3bd8d9c68d93d02046bd7d99d0f726fbccb4e24b762a01bad30aa9b525d537d878ce29ce38741045079e2a813f843832576efe11659baacc2510cb21e88759bbc772294ce4b41058a9b5acf73f73164228f76c9fc84f74fdb9fc0605306ada5991b090760c16205bc9b587093298d7f780e45780a0f969fb7abd3eea7f3a56335c6d9bdcb798b089cad88617d7578834ef1381dc38eb1bb98eda40c6d2fcdf1236acfad70dc510640fac254465a4d353b0619662471b6667e2398ce7a4a652cdcb9008d84cadb01dd57e070f179a8d0ab14f92b16f9599dee5647d7b67d46044d9120b46de2a00e6e52228e540c8753d8aac6068672abf64a7d499fb5ab4db53fe596b3dfc7eb0c4e03be965cd11ff438e36a67fff418ad7219053f5a017eb30a635d8119a0f601a9fa844b4b908a267f185690b875abfa621e9c124dd98d2755e8c0570522d80fa5a0fc1cd2395adeee91f2bb008afde5759b97ae23e7a45359d5a01881eaa108f371c4eb774a63f63f2d835dcb67a02eb55b153f467ca47aefafddbe49a8f4018c82adc0a7aa5caf94f5f86cce3bca731cc5d8b3168a92d007a55221c92b690dd99605bc6454b4a2a57529d2aed8fd7ac8402fa3c34f2219820f2f7818d17500b663cd13990fc3d19bd6e933d83abc531366bceccf6dc3f6e678271f8d5ae809fea97b731887dfab5e168bb0c95378445893260b85ba775c3f5f5e2f293ad20514c669cbf62624087651b323f2848076cf88393bc63c707b6357b12eecb94e0904644c9de8742f331756ac61accf7c37784dce1de81d399156e8d51d8c35c70a80c654a623cfb1e8c113f33ff274192dfc75be91365f6d7c1e32a327bc9bbf02d73268f24fd6d59d3e9cb29b46999b330ae8e47de2f9af526f7016979bcd27077b15dcc01fed4af99996a6a169d565306a573495e3602085d4064549bfc61501ec9a11cbc159efa2ab3f708f693684d657e8db33c645896cb644da4f661f3908f8d0228d5dbda20f6c6eab73b7b259571090bcb5cc4af91e180bbe44ac99550a15f6af4d16f80400e14ae4e007942257dc5ee8b444862392e6372815b2989a0e810881afb76f12265a0c72b3535a8e9ed7ab86c4b838ff7230a68905e54bc10c38bc5673e1458269614026fb9d635701af26def6241aae2efa00793e4a76850e2ad6b2035c90131792142d410f673147650a95738355189575cfa523a530ef0cc95c98920ea45f12ccfa512041d3142e30a7978c4c818f563f6dc21e9886ea046922aa220bf17ceebc84e6b65ac969aad536b43201e2ba5627e6a6b458eceb085f4472333da95697cb94da31688fad2de9bb1b0a3026d20609a91f6b1e220f0532c3192aa07b64daf0bc21716792564232fc295c36e99cc26767ed25d9c2eb04f2a1eaab5f9e1f50c061b6eed766f7023649fba0e05a4dcecb356cb3a0a4d503dca2414b253014bade51656835f41b64337624625605f488fe986c8c75a48404be3f9421d2b9ac1b9b2d63c9fbe377404d762a737c2b2b82c7905c96926d9c00938cee000e291be6b19ac6dea46c532762140c237ef55fbc22ea143601ceb1000434a6268f1fdcc484e2495aa0a2bcb2f46ae85f00e75d122852a9746b3c410c81d10645a1e1cd4d2fdb783418448e4fe6ac22dff3569a498584620fdacdab04c964b70e80d45c6d04036f9d6f50cf0e11d60c3dfa2abf1725f7863cb389bf0e6347a96bfbb67e81f379da165b020f5b58745d4a87035f7ca8b4a9a9bd4d6c0a58015f58779b921bfdbcd2ee1e7592d48512663c138913f4c04580a16778930e4a51ac2271a3942f79ca265a8df10af008501e129048b8b6575184d5544e5f0b0791c5a6fd02990a9193c2cda4743a48649807c679f349b20167842ee39e41004cadf3fa7c17771b008d396673f7de1f502fcae72ba1f73ad88614410108240bcb74f5731b254a9d69aa1074eca75a9af09979d2daae1d76c9718b6ab9aaf6091027d6c5ae4b91860d6a3d00580ce381058d23cec929fdc7b3e53ef25fa1b10b394dd906f1d778af8e310916fa8c09ccf4891773a36e6b0b8c0d45cca9f4d20ad9e4eb976ccfa1662e98558869cd6e2522321574649ca421843178c5808cde04e20cf93f41c81b51122c123ac74b67ed461a9a9dbd486d3a90ff62b48ff4b30fd49dc8343363ec35aeacd61f8541bed134246f08d09b295bcc9fbc7668e1090b151fa9c0aa8e4700db71aef6aed6a21fee34463edae033ed97a38124c13db80bfe522b83c56d38ab982e4fab8f8df10aebd284f4b14214b7d758613f5f37c80ee53831bde2d5a94c4063f5a329f0902ecb5b0bb38e599dd0d890da04516042022c2557b93ad33d5f5e80a16692d70d0b8d0bacf61b10a1ac12b668f16db3c70977554cc398d34422cdaa93d19b85fdeac17f337feb2cec7051d69b6d7267740928902ee3e253c307359d2ef17a8031ad472aa4aa4cc57bf5bf8d1b61826bd401b15588a414a35aa6349c69ab5c8866394a0f64e8ee36bdc83b4970b46ff63f02293d8f64ea8c0d05d5f9e859e48038cdf33944e037ff0a082e18b226fd4d7404a87cddbdebd154ca2b429c7e43217872ee687e29c42e2b6ade8ce2a6e98ec2022414bb10ed9b278d59af11a1b5df2849179c50f2ab410f3b17ec75ddc52c2f0e2f90b38642eb70118de75092cd5680117646733ded0a43a281066a27b5712f0da08efd9798f0c2c77e04525fdbf458260263bfe830550178d329f368ceebed0ce18c2c49211a5093d86e403db13452a7ec4ac04cd274a668e089bd80f1f61503501f0ff65cf5f963d81d55000b4235e9ade97214d06410e8949ae6de698f5308fbe56e7644dac995b943927cf9b62c44f8cca03b1ca8b0a9b9591c4daea5800f2b88612fb294b178b95a4ad96d1efeb6ab13dd42d75971239de80f2ee533750e17d92f3b15d7c922d72db517a0eaa91185ed4019f91e41fd39ee41ab58dc9f02561713db73c1e3afbf17bfff5bba516d4c9468e48f3eb67b4b8f4d562055ae0b223a016022b2b6378457f0f3e8dde196bc10d2e4c040d1983ead7c57039f1c0b755f8f63d86d084fc9601223e89bda043adc5839779d7fa408d625fcc450a3003ff5f4f8da4303c82ba03e8b5db1e5e1bb95fbaf18f6ea4b16856a862aaf14089da16d7c35f229c48ba351a449827889d84ef124e1bd811f87243ddeafe482080711354139a90591aca01bded11500981a4da6a6b81c894caec956e1a47aba0948f5745e7e15b88a7b80080443e342737ec997125713c9ad2411e5ea7d34c902d31a412291f52ad92793e3c4afa8f5d90a04366045ab5f975efb433e9f3a57002f5daf31730ab1b73257fb8389f7f260f94e318013a9398fc2506acf5a282806972c721ea1cec5bb5eab247e178119b6a8ff3e4fb5cb5a67a850a74220bb0c098592100f75b2103b3fac1a1629c7c26e5140059b7636b34f3048316a5f4e790aff6e2ee6b59b124cf7aa21a34d9697718495db4bdb65d7d25ea2712c26cf710d6fda52407236a749d455fc172e38d3448afb12de70c9cd18a319cd54d950430471af0367d944920028d0449952ea565e2d53f09fedcc7dc4c2940fe59a494a032b7833587757b7c5158f8ae87bd9d690f779a2dfd0139eda6de8036bb2509700391ee788e5088296effbd14e4953ef3eecd6c0a1ace19d7cfb1a0af28385950b9060b6dd2b875407d4a1a1e1fa3d07fe9b45a8cf841adb323baeff578f0c2c0e4d6a249a512d1236da0135d3f9408b5800d52ded63bb119e51f8fcca4118930ba8d90aec1ee1be2dfa5fb795814c4f7e333925b50747d6b0c686311d9bd0c00d9606b30b28f742426667daecb39197bc003191f92b7f0df945d3324eb2d7330b20729f32f7f182e782e13e570f0f0737e8cc810986986e490ebea392ac391f05f823c0056aac8314dedd570dc9a623d8254f0e782973c0f9685f5e6af5a2400e20e0effb97e2abd96c10c040f1ae0a99666ffb51550be723c2d30767c8fcba08867e75a900c35ef3ecc3c3a28c64526500a6736d250bd5433062e3a8f3443f03e996cab96aa0db6a50f6b33b9f221ae61f753321f85571956df108e1c368410074eea01fa289d85adee1a9e2b0d76705a4bff6efc11d97412d28bf8baef14f090755146a46bfaeba4ec6a10b27cca291f4016353a0fdaf9e5dc29afca2d85f01c6ecd40b5acd71f32b9e30e329b9e0dc055088c6de789518875f2b5c4219f30b0bcf43bf91fd20cb0150f7654a123e2f1a12a3d03be327e9610eae6c7fe7d90ea6f866f8cb4bf2772318dec86e4e70fa548321833e4c773da257d28160997802456ac823100705c0cf5a7c6a4aa8b7647a95ca59eadd1a02848f5ddddc8d96068e7a015756bd066721f758a438f041d915865dacb86ab3dfd07a5fa2b0060a0ddfd57bef8bdcff7a37153a69155b19c0b07d7ac2c9b54efec05f5d3cb21fa40905c03e21b8d203dc00044e562f571ca1ff6727ee585699977ff6d1b7d7e45c0f52766ca41327d51ebcbda56d92ba3f5e716aae70bf77c13210d82249b46099015f90aea7dac7bc56f83cc23ded6ee71b342310003f11690ee180cc9f4a6ba701cf52141890f067e5d5bb57aa2f4b07c04afc3e7c29ef6a4a157cea168ce09300f628df435ca397927dfa5df9bca36ebfe8615d43cbc90ba91d0b679f0f696d0905b76efc5b932f13ecffed9fe1a01155247459b02cc2ce60510d5cef99888b03be252493123e0403a42e73eb93b3c60dac8fac42eaab7ccafc0fe2d2f4c7b60a754663a499d5a3423ab74588658d5d35c8692f4e4165e2545a5878daeeccbf029d1d7bb94dc3f91535f262ee9a11de7326031510dce9f5624f6b03a5e30bd808cd35565ec0f1899014e66301469385ece1f93890301c4d1e33e31c37bd9ef20e3011b5cf5bc24a578ecbe6d4f7f5de29fd06e04cfe3e790172bdc63aa2bee30194e8434c378444997545d8bc01d0284f61ac7cc4bf5905bf452db14e2511ee0ec2a7efb5173417553d2f59389d73201b1d6536fbc963dff96e2b995be723d903e00619e3c920cfe455a4942a0a26fb4d43704a8d61fdfd8738b8ce7bb1b3840f669cedd06ce21de81c02039503e2edefd1c0ca55fe4c5a526ff489d720a6e10130d7539107110285fb6fa54532a00eb7eee2d3986652c348c188e821ecdaf00ac25d0bec27b0d9a4ddf5478676675afcacea9c8b9cc7f4c059b6c85a3909be0b39a740381d9008fabc6f7e04b50b6c925565f0a00cfc71af58881061d3e517056a4873a477f9562da01cd499c504d7f202c3535fb804b1765f7c7e2f6e00390ac86894a1e34a4b0913f315a9a9f18eb10c6f4a3051f7928ce78127413860cb06a183342f910339b8e1ad9805a14c1054f5b4fb2c9b25284363c56cbe32eeb80f736154daf9525492fc776484cb0c104def46f9456d1f1951e3dac1bb52d351088a97d5bd580e1ece6fcc3a8d3b79d591add4df080de958dd1b5c52fb7100c20725e748052672e3e0ae2241ac8c75f70654f0914d8338b03959fc317f95b7e801c21fa172cbc875a2a3b6d1e248344ab56eac7a0bfed84eb013ce4835ed9d12036e7c0b05d4c1afc1873bdbc588f489e961a4ecadd9455d5cd10b663679ce2d0d5075702878cabf3286c3838cd05283fbffe21d12b38f14ef6c331b9e7a4f6a06fdd451981d9b50e4757a68097b26b8db036f33aa889b865b0043aae77060cf0784979b396c263e528004d0dc5b665315e994605f21814c89d8428e1ea8b87e05125856a8d6ec3fa6f38ba862b65831463b5b8fac5888039500497043a930430d34b713457b3bccbd69f06f2ac936325441112a684ff67ffb6390927a8554a800f9a07b96f0026b59a2dd495a5dd13a8fa21bac340c0d78d79fa6c5f57db5f40264c5ff156f58d213b6c65ffc3040db37922d9d0bb9fe6c8ac64e2896cfdf620ec1b232cd5d23a5cfa7e9445cd8876828d3d840229dd7f1d9241aa9f60cc32d0fe7ae7ab4fbae1f572ab13e27f17346bb43280c5f59948e33fc66ad39fe80de02 +generate_ring_signature 7864676e28d3c4970b0debb0e6f97ea408580a98d226dafee3dd81f5a3487cc4 40d7c9c8b56934e5a5d9a07621a15fb22374327fec4fe966bb846f06dfb62cc6 1 494838f14fcf86139012741ad7732de1d344dec1773830005b3602c49e363c25 39dff26442e1ba3eda6724c73174a46d8fed8eca7523ced7781fe134b2f4400d 0 ee2886301f33d390b5ed86c7b474d9418c9eb1baee08bfbed5f082f75cd4690c1974c106b14bb58e8e8077d3f1479bca5c7c6f79620113391ecdf83af9d76a0d +generate_ring_signature 2e56246c092a45724cacd88af27591f7b36558138dc9f84551e343f3e5f9795d 79b775c12d319addb422da0e67ba0569d1d04cfd608980caecf219b65feb465d 1 fddbe3aec2ab6a7c85a14e5a631db26aeb656f75311c48450ee02dfa54a7a16f 2de3c5a26614cbfa5cebdcf016c9a87afb79822485174be1ae85544aa2a35003 0 c8b6cad60bae9ba2cf1ff82552c0ccfc777a07279b9d8c40a747f3bdab30bd01ee03fa1ceb5412e2c769506da01ef9a71c44abb551d7afd82376a9c101069c0f +generate_ring_signature 7815f1d4b85b2fa3d55770abd44e7d7111f47c5571935d9e5abaee6bee223095 bf1e67b2b20d957c19b829881f3654396a78adbcb44e985f7aafc4f5efe39231 61 d3399fe1944fc161ed48c41e5199c0d1543e1983de4cfd5d2c410f45311b9f00 e2166c3a7f15b793bd2403656e289d1689feb65f8e6fa9b7d46a34005b261107 b800cca2346900c2e4a7fffce98677cfc8847b83a5c69915a3ec51862f302555 8f8b9048a0d2f4d0e0de1d8aaf9760766ab3b9e7cc5fa3254ce88f951029b04f dd638dac03e070da9c635e93ae6250be11a35cfe9bcfdbe7df3fc5041b11e63e dd85beb9d51168206681cb9e2d07315dc15c19594df7e2119f073566d80d8d97 31e92e147b4549910056a05d60cb44d95c258ec13b82e57d1348265626c938d1 f6d071a1f002aa6b543a8f3c5749255d8c9e659e1ed46e4800976e953b82bb1f a6b081eb3489da635b97f6cb6878848c085a60d8b567f08b001ae48ee1591649 372ce8f0c084f59732aa7312dd360760dd3019a073c8cae656dfe2fe499004d3 01c7206287219eb32f90879dee2d05f3ea36557f828e4b91ba14d30c6c36273f e8220e44b09d67dd1ff189de878989324d964281b678e238dd5ef329332c1b19 226dc13531650b0de4c419171f4265fb88bf9b645396c39835a627424d3162ec 1eb7fdedfa46a4eb222797815aebb70e1f3c0a1bea9bfa3a5bf999d2c2588684 53f0fe809deee1f45adade3acf4b4514a1d437639c98cc9562951b7e95d28e3a aea6a6fc2cb0b865ebeb9047309270eaa1442c10796f8b2fced7458cfcd0e57e 214b0b9af85917ceac889de8f197bb26c5226a60aae4dcb812395e9830b7b185 59ab7c5277761232bb784b937b97290407dc2e22e0712270c18561322dd25297 5da1f62493bde6c3f398172047c7de1217bd463249a3279193df9b6068d2af55 dad11f986df301dfac98557bdcf96e36efb105e458cc3157494319b9eeb90cbc 6ed23b5f3108a39439aed73fc45b313eff7b198585a0de020ebc72543d4c735c 90936fc627704467602d21b46ac283168dc6a8081d0ba0330e0e08f7d8e9a255 0fd25a20ea3a833592279b5342ba758ed433a3997677e84f36a96bb0ed48c985 48b4d1123003cd14d0a9e7c323d1220824f536deec12a4b887a31a35fef9707d ea8c6a93e31262c5ba1be8c81129da5c5b38f4b60290a43bea72ba8a64bc498b 17b8f47c4b2ad10e21427ce62990cba355a82827add46abd01b0a9c6c2bfdea0 de19153bf9d58038d4f0c6cb613e037b2572c641f4bd71b1447aa4c93ac75438 26b47b7e2b2996a659afd62b05add0068672d724a4dc18a33ccb083bfe30d760 fc8adaf826a43673e332af17c4dcc381d69c5f05f82241202f799ab0c75bb58a c25fb0b991261f8b531bc5ad19e62b1b8055db902c2b1a435823c3e9856ed4e0 c46d960e1541d03db8b945297fb8cfb6febf632a151515ab0579595ab9977695 0411911de3280f44aaee23820e05effb2b3947eaba456a246a29d3c41814f5c7 1531a1532463934f0bc7ef7b30eb947a65f7b6770c55689506176dbf387553f4 f620c8e1ca6b327fd3646bf64dd2c94e9acfa3b4d52a008ccc2d0ad6db5ef896 82a462271505cb720f9f9234ac367fa6608edb83d33c1de3d92bbb3afba19b16 d443003d8fca22d127eb6acf6b26c0e8c4feaee60bbd276e19cdebfa2fe20acf 2b3faa1881c5ddd2d4f4b2d8a15872e4c74c1c6d3bbd54c5d1c2e334857369b6 8fec354ba1f19ded18abd2a28812c09019bad7f4ad07a12fe4c3a0f7e5be1521 ccd228e8336c7edbf92a2faf42c221b890a6c2f5d48ce59cb6858551f93429b5 c36dd157fad2ee13582891fb642397b4ab159de4b55fdc5fdaa6a01dd6763249 6d750ef9905133a6073dd0043fdcd7ef1f0d0e7b68aab2e79321b5f904a9da19 800055a80889361483f078c8be0f37c455919ee300b6fab6e637d31e00433b12 0ad3cb33206d1f80995b7ba6518a7626739c1b4057d5169db2f39e78de3e23a5 8e077ff44226cbee60be2f4d75befbcc46e691cfb60c664c1209e1bbb031f868 1d4fbec878de15c747915bc2942331ae6353e2ea2b1a562fc93b264730dd6c24 5681010b12e8f47019e0308f2952ffbe4daec078a63c6e8452bb95cd2ef6ef09 53f5417119d15c1df61673eb903a9509011c0ed83fb8a6863f8d972bd0f1c6c0 a5479f031a8eb4eae48c0881a6fee4fb3a3cc4e2a2d372594d5ff3277044fb02 c07652126a9845578a02a1512c77ac191d20a8318f1f1756d59d14e9f32f2830 8c8bafe3ce09e7721da3d52ee6d63013650d18c8482e57d501c3fd7ee5b0e9e4 f865858cdbb9fb50898b084136dae15caf9574aac8b712679d4318e9edf7b04d e318fb363e54aef5e7ddc0aa2340c2afdeab63e262688c95dd6efbc7f5a2aa1f 4b01b80161b7bcab306ccff59258d11e994258e9163bf55cc8e652416d817be8 5975d6d8d944547346f31768e953db73941c6bc1a189836012f77b200ed8bdfb f9e4bf1d84793e3702091f8ae0d833c7480e474666b169a0884cb84d6e5f55a1 3b161766200c121ce33a1ace049b0b65e29fdd307e1dcde2afa95a4dcac3750b 9e7861e59e8d3dc063aea6f094eefa2914851e0a96f12332a4dfed98f81e1f7b 363cc8d62506cd8dde52bdd490a3d3de6c3314303166ca2a8baf8e753fb3db0d a1e2e309a4dbecf825cc4dc6cb4a6675c1c4c66bc77cf63c5e19e9554de3e70f 282113e165a5ef3f6327066b407b464d2f23abdcc0a6366bfdbd10b1f65ecd07 7522f8708006f4930053063d182a25bfbeac061d4a499d1aa7687f910a2b6274 879fcaecfc2dd21a62fb8478768194e0efe3a920592237ecfad527bdd042880b 3 d9e5668d6f18f142b5a58614b132b1e323f32849547cf1c5efc29ff6c447210b748657658bcbfe7b306e750ccc2bb6a82aa41912e369bbe1602f22b65d3e7f07bd90fcffb2ae1fb2f73687585fdd4d947eebb0795596feab9e74473f04e37f0854b62134aeb671bbfcfdb696534c2c51315de51295041cbd42daa08dc9619d0aa39c897e3922c5fe8e45d59933c9c60e6f2bcc00c2b3c5e5c7e8f2411edbe1063624f26fa6763e09e9b5c35cecbb96df569e8b08628ff789813d198833b52a011c4e0497dfddc57d252e558edfab56edb2c908d5a4d222f975093dc10a4a36040995f5a8bbdad16703beb3881ee5a1928646ebeeeabf30ddcda9c236b213ee07ebfd7829abe1277cbc8adf2d7711df55834d9511b863d95aa49d6ac164ae4908d1be4a68793711ff9a69a31b49e72f54925403084aa6746ca35eb64cfb9b960fe2ca1c4dd9194f9542b9f321a7941330e6a2ddb4dbc03d4710adfc7d9d6c1d016edc722a77771a4487ceed1d8dc08535088d3f6e3d6cb0a176a29752f1ddff0a1718e2aa5bdd6c2370e0f4f5c73bc4d2b4a64382da48b968973bafa31f2ed00bf456612516b33b043aef2596c33bcfb77136923c7ca0b7820bd76fa3f520d30763d6f76bacf654b1cdcd7eef49dc82ecda4488ef4e3101514380994935ba70043e8f15bcd425915cb0d045d617f86f03b7a48725b1c074f0c6c8fb8739aeaa00b50702bad512597cf5d2010070b1b07595bdd1f251cf2ca0048ef5c3f0c326029f31d92a9be48bd82fa45373d51f78d330ee34b48ca41566f5d0d55a9a117e0c2d12b176b0db888da562ea3290c8144b8b7fb2b39c8ee8a2d7786e9eadfa8e09eae0957e2e385f64ebedf24f2b086528c74f4bf2d3907d73fa38d3e829db470be4f2dacf5cb0bf06c6f33f5016dce4a392419f5cbbed33cdae1893e1d0ec4f06cb3e7f5e3b9650ec9e2879595872adb8245bc57f323923ae088d5ba0554d540395a8a23097b145115fc1d7364509bafeddfc9ce4b7521e5c19f9c0d21b6f510a8f0c747b82a36fb921599e88d0d276a2d65468259572f8478f7794eab3314a074e5b43009b55d5c61369c35246e7fdb548d3dd6f2e55787babc64ef4f9c2de0864906a765a50b406ea7e157929bfa9a0c38d3d40776070942405ff5e52cc9900606e3c1c55702827f7d57bd2cd4f0a0f65b92297ccdc3f2b76cd93b918e540078279a2ff80f0b35023f06e61322ff75237cb0ac8d60e475341b17d3c92dbb80773ec441583f4d400c98ac6845e8542c81ec4b8fc8c89fbeaa1f860ad476e9b099e03e0114f58ceca9cbb1fc3ea5df9e002f6c32c9db1cb3d7939a066e06ac800ba4f9b587482ceaa7d3e498a756fc8aedb8fa89f8a34799daaf2c3bcf09e1702b713ac06e2ef92db96dcb37bcf2893e88d48d2838c28e4bb4e86b6aa66d0ee0a326075128cfbbf7625066751ab00fb4352a9d0492b454463dae298b9632b660722fd38245e8cc023ec6c134bdd40b1d029b3c6b724e7d9a5171b618ea69c7b0ec4bdbeb6930fb9dadbf4d65d9aba1e9a30253e4bc85903f4d3ce2dc85077290fb45b9f586848587f5c60694fbe34f9107fdd21edc71faf8403f563a4d3e4750c06eb4c82e30604b9645606cc9f3cee535ff147e5be9bedfbbb4a9cd64fa5fe0f4d980faae30d889907e1f67b8b400bcf3b0f2d55b9da858afbca8da48b57310e019c3dd275865c319105ce95d59db7284d205d242dd66028f94a4140fc1c500056b45bc6eb4a6962a567c2a3abc7777193b5a86376170d4f17c2401e2f91c300be2a042d78a7145dea718005dc73fff75132bfe013d9d8fbe39839d8cf6bd3086e0cae4af81591facd1dd20b304513b57986d533e1af38f4a2f23aba50be530d33becb5e996ba173fa189f45fb4df11657faad34f838ff2e2c07df9f569f3a0497300606284757373def299631f71706b8a8013a6cfccb1249a797817c098507963dd16286e02fe1feb3c4534642bcca05c6a3d35764cd166d603ff18d33be0d9deca603b0020bf9775d811547a3d5169c36d419f30036945954391762d1480123ef24cc93b9bd52458d55a274ff91b359457fe9188b77c2d5395eea53e42306bf8aa8a2ee36aa89e04029693f8d24dc31398e86210233cb8447c7da5214230ff416ac2cfd08fb31cb7bcc7407acef6b6d1ced36bff4a0a6e437aefb008e04043429c489392c40ac316bb950143b2f1b3b8ee1725fdc9fcbf91cce00a0fbb8084a28897842dc2a809d2b06a17756a622affd8abd40d2cff0bfa96841828eeb06169b780508e9d8c386fb8da4c2d63e03f53c060132c20aabd9a60dfd5c0d840dccd5e395f9395b42f15f698d5c4eebe8dbb517cbd089051bbab054917d9a60044bdf5b333dee103500f6af67208d5039d5e8b63ac6e67849af2b2c1b3665c403f6656f2be5c08a580f7f8e34af051e156ac0258daeb411759c39becd4211570d15f31d238ffbfa6dfc6157de0e7100d58ba5a4bccbb75a01367a9af9ce6f99034d92a5b879c73e25d81e3b736791f987029fc698fc2f40399f922c68caeb8f088f54dc16fdcb680238273f7b9ce618c6e3c1ad609d7781a0f80fa3116c83590c3df53f8e01df16c3b30f12df42633707f54f8b6c523ad1c3d3be548163905e0c8fbcdae5bcaa408c1f8d55156ebd664dc710ddce830685b4dfd4841fb34a7f004b4a75c783e2f0bc1e401d54c3057174e28d9c925891ae36ffbeee0184ca110a9eb6da3198a02db5e88e9f5fbd2c73810ce09d5ec7723fd6ec34f52100e03d0fe75c8a9f1cdad646942832fe70055688889edb86e8e0c18aecc5894e05472a04d155a070f9f05a87076a9457f8ab605c8363321df1f525bd19e561ece67e4105f58453971474653d49b70acb9c9b4d53d6ed6b2664a51c2d2e50d9b947bb8d063818286da859891dac3dcd492e1a86ef18ac0153b272feb3bb32c7d61b56be00e42856593ed9eabbdb70c3172d7d70026151a62e4115670935ad1605c211f80fad9f20a958135e787b36b92b9e057f999106de228d7543bd0a0e9506d0938e0e77b86cf4dd2f9faa3578f4377df649a47b7a19532e0fb64e10ae7aab51506a040b5094f995ef556bf9433c7f945504d72084a8ad64e73f40db40ca1e1a487a07efd843470138d56a1648c78f4092a79a3c52d46784f1f946fe14029b4a7ccb0dea49d395c64642eb8c35f35e55a497675b9a683f33a11e585ffea59b0fc8800980497cb8a4225b98fb25b9b928848f2a7438bb0fcfa0b1f71614fabd9fe7de0c5dfbc9ff9ff00a671494d9833ae8b0062c0dcf2135cee3b6a24234b7a7cb170a2acaa5f5378fc4e2030c8ced0496049120ed285cb57df2f6609a3583da40a50e08b9ad3f2535275e62362bce099f575d10c6fc066240ce70a91152afed87280079293a02f83f5b4d592329d29807a7596d86fc52aa6c8997023ea515dea81909afe4316d1e189fe9e6c0f49e165a6f77c7681534ee26ebee26ce12e437ff4c09b694b119794afc5d103b63f32b26ea40996ea076c4652370703101c005c0010f2a5844e6d261736acb580f210ab1da9e37f3497cbbfad97b2567b80430bf960db480076ce40396aa7d2a88104322af9ffa868065664c36b5ee277e068cbba30fe579925de2da9aae4c74efbb9e37638e8e5e5b6246a75623bf12fdbfc1c30d0c567b59303386beb0035690e34da91c76bc30866947cc1c327821456d731431063cb7f993f96c2a47ed70bc6e89d9e59f5209e767b7be3fe32c5b2bd15dba5b05765a2a50f860436f23bea831633f3f5e589a0e2dbe0f15f27f752dd7364356038f50f56a62cb35af38d1662d972278c7fa5998a430b5d6a15ee95a24974d120b4e6a03fd13c2b3bcc0229dc3d50385845bbb16d0945bc10557719ffe42b8510159e5eb0e14c591d83b31c955a3945282a69cb6d31dea5fd1e483a6da4226650e8e21569cd75a37d9b75687afc7d636d17bd0ce2241fb21a0a069388e331dcc04f3cc266394525534f55f6d7a4e508b0080520c31d5aaf03494877afdcdb05003f1a6d8d31ef8e07c66286cb260699f7f018c017b11c173f513fa1dcab063bd043dbd213a41db96f8fcc402b5ef4c5f4df947f481f3960b96e5b7c2045b56d20a7dbb9cdbe115bb40d27369287063941d551e891f752044b599130f94103a3f04e5c9a4d9e7e829fa35db4a7afe66e98d584ef7d73d3daa0e743b6b312d46110ad3bd699bf2935043ab2d7a41ab7c3d95a45db76a75c7e0d949c4bba5836cb5042cd4ecd74cd6be5f64b8aca6f0e36c90b9b946e9642971a5a18414c8d3063e01d8a906d49756da16f69f456d7b5e84c22802132572ad06efefc250c3a0fac50b2cb5d17c198773437047ec7452a8ad89dccae35c8c0ab4fc6ae43618072356099ce84afc5b1527e6fccde920f73b242e2e22e187f0c9134efded66c3c2030d08182af5b5d1637f214fa808f8354fe39a2f1c98a2595c78ebd758829d6558de0286d9a408e0ff7ba6951d1857d7f715dd1a1db015d046f79cf0d79b3363afe70fba5abc8475f4860fefe1450571d9c5c8ce04b72a79b2b139f5a1d6484b6a9f0f04b8add7a7d8bd8b35593f63b0e4128ec3b1623d5a602067fe78176ebfb8fd0e530d7c7e298d7dcae7fb3a1e24bd2345ee515dd30484a1b738b4bf11a07e27060b3385e73d29fccaa464e42bc0835e781f1ddc79d53e2a5cfba5e2f2be117903819ef7a93e9e37085d2fad2305b3d1f315a80db604fc467c00dc4fc8b462d30bf4145965ca6a511fc378e587daedfba4f9984f07b4b1293c16d91355d2c2070de485003e58bfc9b0ee39035731633c5745575bc4d3c66e6bb8986a4c7e143f0c7ff7b4efb46c95e36f10ca9443ad485766e9687350732626f1d38f4241f6a209757955ac6f81a4f8cc40b61ebcb88c97dca2774ca40132743a5196d23cdf6509d454e11bd66116aaeb70fe63ba577bdc6bf7f8d619a4990046e834eefc55320fd61da98686829f22b2e70e597b48de276904566ae221a8ce6352d93b35657a01a8a700fd041308c38ed8b35e940a16b7b481b51b14fa7d9cdc8123c8d78b8d0d3154fd72e954575f9add4ab23b5ef6d63fcfa923065a0e164f008eb4dabe320a51339013fb3fd8cf9fb1acb930a3018eb66c7038ec8e9f391342abbec60e830d0500479c6e825b59eb5965b6214d2a76e774ffc1a12dd0c78a141438bacd930da2f82809cccfa517f2e6a97e79f83c27067febb7460384a6094d38fcac97ed028d25bcd904234a9ab70683e0ce71a675846705994c8fa6caa9a2b23725f2c205825fd00c0b71da03e28eafefa2ef65bbb794f603b46ac425e1dcd060a86b210d01275d4bedbf3ba5c5131a539476da2b85b36f59eaaaa7ab00fda836bdf3d207ad690318a5b4b6dc22a1b02dcf3e08cb6bb005288a267acb880253cd91a85204ff6db55410b720c2075773eaf6f415b3cba32647a3ad70eeec2386e355c39803 +generate_ring_signature 4c4e0ad8f2598b7d77a053302d5a6795c381364241b30287731d6c7d063e5253 765d90a45d3bedd3c6ee22b5783e15ae9d418c64dabe2683135027a4e0f0d47f 1 f0aa7e8a8283be703ec37480d9a1e445ce42c58dad7d275752bd8ffaae075f9b ba659efc83361288e75dace789db89084f3970e73518e0e8d03f939eabb5fc0c 0 9e75473b1dd006a5b4ac7e448b900941be1dbe8dfd7961fc2ce8d43722823507f328a5882a6191dcf6daebece2463c36a9cfe197362f3656895695e112da7c0d +generate_ring_signature fa75d79801a1ec122a36862796c333cf133a930d25ba2faa3cfd8401b1ce65ec 64c40ebcf87adc0ce335ff113ed46236769df40a1bfa9dad5ef83d8babae3ffe 1 fa560cabefb92c708a2018c978743b79a348ec2c3908c74c44f619d4a437c2e5 c6f86b60576c5b4f0f03bc9fa8a52482cbdb490b0eb9d99b79e319609fd10c02 0 3045b88f6affd09e51c4b72538700cf7f90320b2c722f928648868df1a927e0918d9df6692ca5e71df57f2db0223a8815453c8817345ce71e3707f671e9c5a08 +generate_ring_signature 7c3fb9bb3ee4c42954cafd7b2b7e8cdc0bc46262e1c01911002435bdbbeaa392 bda43de0bab94f0f16ba11829801217e25b467af70657b746544a48667d928a7 196 3d7deb9d2aeb577cf0188c2f8e3226415e5c4f641e83da62745d8f20120a2c46 4f74fe1cb3af7a94a70f3c562eb98142765e1dfef4baf9f38bd368b922515c10 ba66623e0a2b563d871badd3c5dc6a1492d92839cd3aa5d2c0eb5f70c5f79581 71b8b0d917a73fe39ee5f2f3113a90b9b26abd07f56239ea5b326544359b19d3 eefaa73d07640c1019bd2f642cd443e98832f82d7890c49a2ea7d01abeb71455 f9d84b3c3389860cfd7499393b13ddb2d1d47ec3c782fccaebdf892d7341fa19 1b0bdd06f4dab389ea44e9d7d1005d501af618687d00940c2443b84e40e72136 3e1f0672ef333d52cab1cccdc601f87d010c82611598eea47c8b445823f5d489 a8aaf0ef9003a56517acab915650d7dbbdd51e702c2ee3d84f3e62fe0d8c86a8 eedd4498a0d2c6810091b0dcba02ab3e34e8dd4a4efcf2dff9ab5d91056181a9 d020f2842103fb4d38bc130cd14439b4a4957fcc666dfb3f8e9cea602d0648ba b2dd26957db1ee390466b7ff7b84cb95d8989a0aeaa0ddce683b254390d6a6cc 4b72466a6f2eb480af8715b9a37cde092b5beaa597f4e6e00746ad6d2d05b398 f921877891f56881d08de5ac9c2981c54e6c1d0ae2b16c7cd1a677b198864ccc 41f35918409892706539e055bb30aa7f2fa282f432e13fe769af5b0f636cd69b a2b4d259f5bcc195075eca1ff07997b0377cf25d771716bd4560238839867e8e bb4fc78175626e852e68fef83df8968ca5f5c4f81e1e6112ed6451b9d5f5bd7c 4edfcc81529f6e8888764bc5e41a1fede7049dd960f5a5d197f229a5bd1e707d 58e5461abca133964190a634685bd0e149628bb202ca0d5445c7a852765ae82d 0fed57463cca6186faf243735eb3ee19296afe91474c492f5489f8ca8a9028c3 7274df448dc3ec321473fd02de58ae70c0e27cb95f0720826d7853fdbdee48a9 add935fdef42a91396a34e19659a13fff9ee76b437d4084594e7575a3de21b4e 993ebeda5d18e86f53235876c9af7e6d10f3fb65068da9bf3afbb1abb464f52f 1099ba8fd7f7cc772abe480fa5f216bfe48f46e6340736f452f5e59b10182349 1c5a734cab5a13536221c7f83bebccdcf2660425b358a30a13d49b8a2c832cf3 c0d64b092dc9db3810f59e3fb8feec10188f38d31e227adf80496f775becd04f 755a97fd034508b8bc2702ba1250f5d71ce48e86c7e351e86790d2ef1ff825d9 e47db52ba46c257338824e3adf22706fa2445fe44805d4bb94b5e0ad8c47df7d 6b2ac3928ca6dc8ef22afaa034f66fff380bafa2674a4cf490960fc8b560dd9f 9c1aeea7d25156d1a58b0252704b71b3db0228f17c4cf95ea71b2b076a8b210d 74215b03d16e45d3b251a4c6367b54f8ae438f6965167fe5ec3bb645a5885bc2 013aea76e7a4f7c15338d048a54626c8fed0bd291661963e1f3567923b1eb93f 97c048ef8421b828ac7c75253c51ccd77e427a6ca6390d554abe773a947e24f1 a28841ac91711236b0e4e6f12fd88bbfdc09e269a6e26d7aaed06c3702efcfa0 e0b8ac3c9851b8aeba1edf45b2fcacc18d7834757c4df91b1e14b102b215982f cfa5948f6bb9b6e5678f5fe5c45d266c7c9fee864289ca3dfa79ce248f0c0b79 db95bdfd0f2429c78c806dd772fb934c3acad4fb5f7393e25b521afeb6a35c38 6200b0a613b936303102722aabeb1212b78f6af51735a3f0d96bd76058ce0267 79e29d799a6afaf916f0b5ce30feeb0eae2c9952c1ae4d7708a24067169b4ad5 20ff221440a6bb518535f42abdb4d5df26b3297a92b74eabe8d660510d103919 5fd147c22101ad4fbe7fbcd32931d55b13c55cbad3db68a1ab3f6996ea25d03e 01f4dac1ecd9e01bf1320587ec8674b88d34205ca51ba8483ecdd27b07f7ce02 73227647ce4ebde477e0cd99631d977fa049e3f0d016df930d38794d45ce7c32 829d500cb72ccce0680b7445877d1b1ad89f9d643f7429aa103a655670501f43 3f3f0ada3115f1dcf6079c0867c0c9e3c9ecd63c5d02920b12581cd67bf4251c 9c0a5f030055125abf6ef7631709f06c386af32ba9bc659647f6f6b349588245 56ef91cc69961d6b417ac27d82ab9a7c1b56fe8ed7935e6802487153eb8bba15 6943def5c2ada8d0d78fd8da01a138c16737992bae209444f4b02d5b96f3ac2c 3517f0a7f5203714e1b2da2723af527b99acddc6b48733b6cf23d0cfb6a4afdf fbec53b91419e5f4c1e9ea47c499808c27055492101ce15c85592dfc9f87fbbb ab7a6bc753aad0d5485f332d260f351b75115bcdeb32d1b0a1fd0d8421ee9ebb 6d24d7851f0cf26a3ee3d3c29f8e69f4546c90d9cbd3402fae623e07d69b022d 1ec91aa4a3898b66a1ca297e32940ef75bf5695d62408b11e46b831b25617f09 78b9628ae4e16b74d36ededb48245ffc70fb823ea0e2b7effffc34d4dfb8d308 e9980856b66d6e29bacc3272d8d94ebd638117ccbddce2f8357dd45ca32934c4 3263b10bce76a36c53c3ff5cb9d439c549c7de8d9e3e28cc29ed094ea304ba73 7d15f57f8ba68ad9c179b94a752888ca27480d439b13bf7b22e0f09c30890c29 76806ce6f9f66d86d585658d049c90b7a55d55035e7b9f426f2afa2c295fb6d3 da9630706b7b980a5e17e988ae2ff5dcdd0928d3baa3b736ff8fbcbf5e6acb4b f8da823972eefbcc84a68d34ec0cb69538491d96d8b9a1ad25ccd89bca2eb227 2649b2831326133828b22a7ae2dd1c2d794bc80fc92fb219bd038e20b4087aa1 41dbeab376c0cb3badd9ef47886d8e91a16bd176babd5c93d4222eee59583951 507d4bf45b583eecd177a2d7de1d303e908a9e7b326ee66c384074a19d0e8986 c4327912595750e1b4c774dab82ff6251392fd3f2901ed495552ab943896afb5 de7c4677a46a08361d3748d31c1df86d6a677a46b2fc52fb18cc8c17e631f9d8 bd154b97db57d73e1489e4aece2e891cc90689461a32ece0adc0252f2ea8cd0d f85947ac2d27b169e3f8668cb5d309c93474e2b4e39d303ef00cd22bfb516488 ce68cca1c16786b5f84d2d1b6be185c50bb7e06b6587fd12e88aa0ad55aef1c8 fbc677f0efe1e33b870182a6c970805dbd587f7341577ffaa2ee0c20e73e2b19 062ffbb81d55a0599d07c2e7cbafe69b85f1562731b7b6ff29aef4910c83f8e2 516bbc709109e873a712e6ee502612fd247531d24de032f46d104c6968887c99 147992743e4b8c1e638e6c84d6b426544cc49051f098366837217ec6839b2832 283508f305a819032db1b7d8e5ce5f6e9c7591fe555558d5e18c1d8308acc36f cf7ef848184c876862e4414cd556f787f880025486a4932ed1428ea22ea83e5b 688407f0f2c3c3e1d46c2481b4507245be9e897b6e6bf2f71ec42ff60cac5233 eb82880ae90623b8601b37b8f7f1cec0cf463aaa4c7041de35da6bb1e8dc3383 be18599f4111c5d903acb9dfb4c435768a3003f1ad1428472263292dfede4e14 036c74f81ef0d9812c60b77d4d996fa358d1059b806a17686115cb91fcb891a7 56c13bdcb883d9ba092af5ffab17ace1bfbb6a020c562e12cb02c8ac4aa56d78 c0e519110a5cff1bd58e27124f7c2ada7c4e337bb8c6df93c71ae1e01c862bf9 58596e856040f9da1b9996da7ecc4eb82201b92d45c7fd70f7a365b95bb58b1f 58c0e7620d52079f74559b40e84bd5206ff697e8a96a236017d646daff386596 7d9971daa6d9b033d5fb18153c941f468874eed0db6c31597e7339d0a6abb824 1266519036a3cb221de554081a56e9b6ab0da26dca30dd1cda0a08b8f3ae2e53 7ba8112ca27bf3efe3fe5d6f839dd7c60c904013e0c299fca13447cdf4046a17 23ccdadf7fb569338fe0880f84c81c843d32fc8e51d53d0e6cc06288441c12f9 89ca3b7d4bcbc5d983a9210c8e25a5e9ba55ccb51399dc4f87bf766e87a803db 8e1df71b815b60d01596821f77f20e8ba714e13680e4bb57c3650110994106f5 56b73066872d8a4b86f780e8d9833dfa56419e278da45ba5e2db20bffeb66577 2a47099d3dcf25018cfce34ceafe9d87438ee4186c41490fd403e6e42b5c2d3f c7a3399f0fa1dbbd9976ecc6b4f939c860b4af7115a14bbda763632b1427ffd4 9e95ec90e3f86a6ba34930bbba2f93439766b2c1aa20d6b372f53aa785ac6503 77790eee24121080bd6cd771633b58fea9554710b771021f74d7438a99bd783a 53a887bb398735352c8b991478de995167951f2a985275f2c38102937bfbae4c 1bca9a852dc22ba46db7b0bb5793346a905a6e45bf2a7373dbf8960b5b42594e f03ce64633d857f3d0f4b267c6ed14e3a82664338b26159ff25dee6ca784ebc6 7d8524ed9d52118efce4fadef93e0a0bc66011bbc9877e66c9649ecb16aedcb3 cf65cab677b16ce2e76b2e675de6f2b324c6cc4ea82fd3d9ef646876ffc59185 d84478e2ff8128c73b3afcef77cc2f1d9f08a49ff3f0587a3a6d473f55e0eb53 f59c7d8fa9eb320ed90a86c92a02ebdc016390dca8c9722170dd5198cd1e9579 f0b0ede5391b6c9c584a61db76a0cec84cb1640472365fd278f6cd5da4946921 07cd121ae416d38443e3f9ec281774098c4779d712b0b7a85d31721c41a7044b 6f33a40ba6f6c8fe17c1f387b2a15a440afc0b262628c8c189ec936c7ccce868 93d3c1f4187815f7afb9bfb316594bbb7bd194dabec5fb906b83fc2153b4e02a c0514cde62fbd830f585c13491d6567b0e287b3b4a7ee7b7816b1e91a34637de 175954ea8c7a717813d62813e3af15ccd10935e95aebcc4ac60473f3d24de202 7f3f6c6efbc30d15995a2e1ad3d5baf1685f65463d19b98d14d7c22edcd33cee 8da94cd37f45d0993b6f37761dfdbe699fb00446e37766f1a8d2fbed8919aa26 ca869ef84d9196b289c84b0a0f90281d787a4505157eaa91f4f1f6b9e7443a5e 2eda08be6e1d26b9d6bad8360c5f30c2f47af50e07b647d69e0cb6991fa37898 d3ddc6aaa0883e6c155f2118c5e2501ef8d182593518889792e50b1403a04246 944b3af2047b28c5bf51427b1c29242cf2a2a3f6882c6fc7060086bb72ce26e3 44579a1b2fb42179b9e10e232c9d11e1cba5e6268e3e27e22fb292c75b550732 6713824a19a140834e3181023eeea4d9090d4c0ef7938ca5ac760798807149e3 d99e86bba4142a398a2dc7852aeaea64d710e086b241fba4fb70ffbbfe4ebab0 241c2ea4ecd5936b02fadb15c2d763aabad2e7531892a1448c5cb1b2e540a928 6cf74d18fb775f01ceb9222574d0d704f7efe797b096dae146c2d7f860d6ae60 2df994259a9e9279a8c08f7f37d1333cfeff7d4c9b00e273ba0bffe67ee91d58 e1f86f218491f4e1297187b600e17dcae2d71362b0aa051bd241981b4b694902 a85a6b762a221ae263dc0f8568b23388879e94454b9b588752e1a16105e4efeb 213b0ee105d7df0ca3e893dde9f50a57d0ff2e91031cacf2cdfc6547c07d189e 9c5879307b9d6957f504d1b2cf3160fc787f747122abdc423526640327ac59a1 ec4fd6c81d070ee743061c36568f2e02cd1e1b75caef5aff94a6143005a49994 2002f3a945a9d21bb9949e8747df9e40c1bca2d793eaf979ddae2c712ee6f85c 3f0eb01b2613ed363e129a4dc8d820fe325c25911d8a480a62159c7c53ec845a a8164cd59ffdbe57cb56c7c049b1e95f3693b0536831c840febbce5a723fc5da 154861f7eb6cc3253cffbfc3bf8e5dbd79171aff9244a205454df5a827bed731 224f573206dbc13fa4621c6b73b036a241ea25619c14628b0d9767743b7c967f 6cdf85e92acd2dfd4399aa1bd45d803ea3d0794c76945e2d80274f53cdca5f88 0d3b885c422d389a29a0928d6b09883f4a309bde6573b495c6f6a268eec2d519 428e5249c0abb96946314d6ae67a91339e8920d2d6df6bd2ab01618a81d353fa d699f8d209ef4bbfbde089705716e162ef404983be581dfe016e0c49a9302029 9eda3826a1477e2c37e40e687959331664ededc5fc4937d03e81da5c0b34ed68 f8fe870c71cb553166802db6027fca173c85ebf0f9537430371b2ed7c3a3764a 622bbdaf0a8dbc4430d89147f9092de7a2637d0c5ca77d1a2269081d85c3dba0 5c372dc15cf0c42e3f359fd4c1a5c2e1a019414a400e0ebd18394b96a40c98dd 91bd292ce4c3c68a5f2579f90b4a8b09b8030e31f7e773ee844c720b079d1e2d b2c171f58346747d6ed7b02fc65873f33b7e08ac8183144fa59ef23f2bfc959e 5cf6a61116fe8d0b9f4804e6fbc07af6ae15207b392c8783e404d5fd293c543e c6646612e88da493c7944943e2e7bc2ec432410e0f43bf7dbe63545a306975b5 ac6f8b9b72dad35d5cc636b93a3b4c3dc77b0e2ddbfe90978be8a66e98e3957d 83171e32bb5e628424811c615deecd0f89377cb0279186374c36d874b5ce2a2d 474871f34e82e8aae1f236fd7ece6b9bb8d9627d12878e64fba94ee0ff917e90 697ac8f1dcbf582bef5162d945b2dc2d1a1969bf59161c5aa4e6ecdddbcc6b75 d0ac304d9ab860d7ad0ca2ba9b3a2c2b47d937051992b547426502dd8cb74fb7 775252729d52b7fbcb724b63ef65d2c14607fcd3f57cd9277a10c95aefcf682b 8e3523b50d6ced51c5a82b2aa13f2d498b504ecbaf220d23665f5c55645faaa9 40460ae2f74bde032b31812d53a373111a7f3551e0360aee6a0a5468324b29bb 2a971e6b6aea6dd849df590900fc1f2f527ef624b7f578ce23a43b7da1766ad8 64228053b65371ae1d0a5b3823c3bdde6ba4e9ab15cf84525efc24ab7810d7f7 673629e04c5f248c138f987e346d6f62aa8b70142768ed8faf516280f1ea6b56 aceea696d75ecf12f55924214699453432418a6ed52b2954dfdf1d6eb66028d4 124b855dc5d1164885ea22ebea5849b513932aa42feea0df88bc146b26062102 2043c7629e30d8554739ea06709d746206c08cd7998b6fe7278022855ec96f8a 114a9e312df49f4650807d8d568da9c868508d970437d843c14f23a5b5c745e0 c6150dab7d30c4a57bbf9f74bddc9775d86d26bb9c51496b5fb0887faf32ad8a 24335733fea5102fb726536a5f8ba5cdf20ff37b4f7640b116dc72952bd617c1 bbe2bb771c7bd5fd1c79eb68e58bf4d49e0c45c00e274bd5876df1049242a729 a3ecdcdf9b4af0bef4676d65a25188742351af5648c9233a76e1c75a24eb7a8d fa75c3f4785cb295c8e4b7cf4f95e9cdd80cbc14a9dd67a1ca7d544afd7ffc17 b02c22ee4e0d7d797a6d7212759fc99de5594b4075454495f786aca12e919678 c81e4d5e4af72bb134385df0131616470a16e312f2e660610045f5d3031884ca de87536fde596ff29d65922de1d67d596238e5a03ac0d4e5251dafda619a838c ad6c7c271779001b676bc498d945e90fbf6ba0db589c5b2d5a2e4a74bd6e8d01 4aec1dbdb3e2c9c272606ee77ecb170e6e64b0c857ef9790d612aec230a8475b 82ff743e813f4ef077ebfd66ec61c32a27afb8978bcdbfa69c8b8988998acec3 5a0af108c17d6deead0149accb66158f8246d4939930b8683bab88c17d30d30b 0f46615ccbf367984b3a89a7648d557ac69848376fc56e77ee796dd124ee47a2 5bf94829a2e3afb01140433a9d57e8a59899db28f19e64aa46ff28f5ef64cec1 3e01bf0f93e546cad6eb526f1249c1899a784808ae940f4d15e0a896191d4e42 86e649034e59402ab71e9e7e577d427d917a4257d65a283f1d7ac023ae559fef 29a914d0f4951aa24e2f0aa63b1f537c2081b11da7799a70258d7651a35c5747 3f0107399325655f8b6b33023796e8e55d75525a31a9e5c256f24cb23fdcadb1 6b052938fcf90cea2e5caea0d9cd4e183990f1efba477314b8ed59faa89bd34a 1c6a32ac2c6d3cc6541da9afedd51357bd81172dea1eb2d03a1264a988f761dc 889927d51a0256466f031595d94b95fdf732708efd504b9d22c6ff54aa3d4c59 c612ac8c54634bb1d7cc614a177c03d4b0bcd024ba4a74a43827af518ee607fb 6bd3a9348b3e2cf91c5e9431dce292fc2c2322d07ca626767479639c654365cd 9deebf112490cc58fe510d7aa8c804657bec78642f236df79317165cf2f206f0 dced935b8c9df181c26a925613e96193d52dd051f2001e74f9b7e6348d9148f0 3f6277b2f63bfbe985ece514161452767ade8331f20c40f3f0d0a1035419d533 4d95e5dcefe000349998e1a3130b7a7357453129503423a0f02683aa941cc0c0 84bf7e3dfac85642c1004cbf84ca5f9efead8b8fb4ca98b779e9e5f5445fbcd3 c3aad5fc72c27162dd18f9fa38b371cfd98b70b885ce7e0c428527ca7d8b7569 c716e52b55b748463b4aa1af2f5e178536b0ffb6bb62807d0a51960ad5cff40d f1bcbedc26b50cc7de6f7ef24a1f98d58564336922df6e6f4a70337e6670c6bd 65323b25016172bd154dd7336dd6beba5ef6f45a8713797a6ee39d0979c746f3 4204c9aae42fb89fedb2270e6e29eea1df504c91a9a50f33bb3cabb506412d56 6113d4806a2e2f16880724d125ec7fffe7c1292ff38cb6c6eb8a8f5af171d781 0f022e7bae4d0615d72527758f20be97b6ad2a574830b1d6f6c754ec4b478a1b 25178ef51a568a7d23ce35b29ab02a618185fc258aabf3ddd01fde845938aa64 f06519e655fa5a6ed6cc80efaad8db30ea29324c7dc983cbfe0f3e931bd5b558 46b4b06ccdb3670d7027499daddb0bf9a1269378856cec1700cee91ecda64497 2399b6d1babe50a8c854a3bec66c0e7cdcc7959b03d8ab9f7963063e3ee7cd25 a7a4f6c54713d2f6305c2800a582092aaca32c34b20aa07d0a968cc1ba45481d ed82f9e92b53a19571b5366ae203f7dcf90f19fa2edc87335726fc22dc09d81e d9787436fda6cd8e5791574b3aaa0283ca7c4a5ede9b2433376ff45160b58a0b 82 f6350240249269b83ba4ba4695f710ed6ec9b3b5b7b7e322883f24d712cd2403da325188651210c0a88b8b443257a8f760627b3633be35314cd936a3f09ea4073aeaa7d8baa8aa301e435981e035dc2d212b764fdcc9fb65a8c04c0b39086d09d42abd7a8d438dbf16bc1e4795091a00751d2e5b554bb0e9bfee1ac62f014f09593122988773fe35936b57d59db5476dccef7651fdaa0d273fcb0a5b106c3c08d9ed733e2fcee4c97f649380b5665f111047a6fa4736ca70bea8f3d1c3a4250bc8a3454f9a88323bc670d0b1ac52e8e08b66c92505adaa95ada0b80929d4d30475be64b13829d5a746d2b3f3ac654388bb8f63e722da0740fe77996751cefc06558d95bae44b05b1351c6852f8176e3475410c4adfa7923da407735a44b711067e8e076ddb29749028333eb2e7cd2d3ab0c0b886a97acfbeeef0a5f1edea3102c5cd8fd617cc727530c0982073b9c94b2b1d7ff80a7c2e93b80182514f2da10a122fb6efa275d9aa48a14803913174b2c5f947f88891adc4e0bf3878c9cdf90c18f1a17f51e8ae766efface1df8fa0483e35a72e95883d9efc4a443ffbcd350886c158ed55f7631af84dcf9974319a87bf6f8478f4bf23752271b2df831af700d9ed970afd8d9f0cf599b4e5ea3f7236a104ba3a82b54f5122441bf8158b550b86367ffbc6d71137dd9eb048ec554ce0c6a88fe2cb31157a94644f1550ae6603e12f853b47924103dc9b347126d47595afe689be4f36c3340aa07447987e4a06aff100f13c793b149c03abb26390e7a26a89a4a3321ff1164621fc7b87a999000208bf895c9193f87ad6d9649e9d6a37a642ea3e1b287729be0ee6bba6f9c90ca625305d4e2db3c2252a5b6a62e8ef322ccd2b2886e78a6ca3f6b5b3822b1804fbff9ba2c30b635a24001d013f1b67183d3ea49501c1e1cf2ca5bfd93b55dc05cdb228d98043d0e90a89406e0e591b2f90449a33860a2a7bd985b30497d1bd0545e402218e840ddb7b3f4fffb00a90295e611ff5ea05e4c0ccd8c7760f110705c88f997b8a3a43922e5df0b61be09b236feecb23c40dcaff412a81b5d1e1730291d0378e9fa5f737c4b8adb94ddf9acde32dc6954b2b3b8890254ba4123e18059082c254010e93de39be364e7f31f67cd0c7afd22b9cead93d7cfd7eb647a40dff518c859693cb36300d8f3f425fdb3895a407fac43228cdca6b431e551df50f874bfce2337677232892b364b8d846a6fb7417e85919680b2b7644a0eec22b00f7f2edb10a7075a67ce59acc52cb3c0296005df044afa5c49a538715cbd9ea037a14b1ef399072d8867f70441c1c12f4487b7795413f5c4b9390dd59eb58d706f6ea3212cfa6c883885bf17de4ce3b38788ab699becbfb6096111f74ba624c04fb34419b03daa19acdbe83d190b6772ef62a19a85a59c43422d87e9e92d59902fafe568ca0e9f65995d75dbb75afe39b7a04199be7a1d3eda5569be6da107c06f163728d3e70758c9824f8ab7c6dce1af9f7efb973d1bc92b6833a3b16e01e0f57199751c9c678221e4723436fdbc32622b44f9e70a77b1c461bf7a3709a900bf39d71a686e5aa8422a20341bdf318490377000e467e390d2d3fde090eeeb806abc8eb321a1f3473f93222da9f7cc423b8259a69ce1a7b897e8e000fda4a5f0d2f9f046bc6c32f4460c063a0ebf42052d50a3ff445f9fd1a7bd546da18100d05f2edcd3d4d8bbdb990d4ed11d66184515eb595e2a683e348d28a65cc8634de0b4df2a7229bfe47ff76f889330f05c8aa7d7bfb628b425f2e0ca9cc2c1171ab071a8cb373b96533d892896f7e19156bfd20197d0ea8419009b1cf22458dceea0dde173822faf0a3ac17da322c885782b3593e39f440742f72397e8e3f8c15a30494dd94997e13198abf3e37e7e401be4255646a942adbd493fa44f40c376c0d070b67eb0cbc008b598eee224a9698d99fc11e689ec51fcd35b9bd2a38a3628f0f8e5c0180ae8219774da53ac6c2e71ae9d84a083c3371bfd491497d80e6a682055b7840e0f61df614dd1e22e3a87432162888e7d15b9f64c5a3820584f9de310cd14a6dc3002c6fc5e2c80a19d360ca95b384ae33ee8f8dcaf29bf0e2e7a78d03a137f4632eab694b217cc3bfc5a1a0ec1baf4ce03f573b0b4d153936ab82730990c0aebf8e429444c26c7dd8038eb16e465d7d51545a9e6e7330cc4e9e9c720529e3da56bb8b077c013bfad5b86dbd52401fde7d9d9e1c3798652c6a1ee0c00ccc74087e3a9fed0cb78b2b6532c645658459239e2d410c4383f672f2977d3f09f63c608b3d76ef20f97c4b57edcef1b1ad6e982625a67907c214dd4d6151ad02d414b1c4abb3f442c1d91d42b83dbc5d64f55199f675d613e6c6cdd7644c80050551eae8b0d36d8809c1f4357ba3a4486c4f881b7cd8506bb0a8e3a86b57ca0a1c85fbf97e652972503ddbc33a8bade7e0859d9ae7fe1d54fc6d211db533050ef9f94c06737eeaf8399ae4d5f7a90ec6b1c5d87c6938a982d0951c90df7b2300d9d261bf1b60a962471a42014a443b0db313de41278f1b8a459c71fdd2c9500ac4222e8af0b157f89e5889fadfacb299f5dc5fa8ceedb5f392090633c8059a0a9c9e55da44295482fcd02412755951bc77f979c768344f436abae112227394067066199773c7ba88fb6fd73b7740358bb3efcd93c279edf9972f6067c8c1940074f4e5195b3deaab16528994bd9965148c39196ce2753cebba6bd464c4c8fd06b394b48323b1e68635e0faa4280cccf9f15571d96552c29cc5a6ab34516c030006a90c34de9ef01deb2a692da67e3c52bcc2463e9f5636bb38eb32812ca7ff038b211269e6eee27c1f64056defccfb589186580a0e02dab10c0a68fb86edf00e6207072eedbfeff52c58e90c62cc38bf33189703960d60d6fecc9e8891ca85028d464a4efdcb52f5ec9898b77c2adb62c0fb434505084ca0410e2c804cae3c00e69afc48bbae84f62969737ff395ca03b3b31bc2026ceddcf7bd92a3e67faa07ba8c65b2bb2258137f223e21ec362f95d39ee0c6acafec66362f0bc672fb280a6b883a662361413609978b11be0c3064c71f325988143042d9b807b47f268d076d5d4e807a5d0a6984df81737c479fcbac6f6dff715b2c5d6a343e495ff2b80f1719a721a9b8826939f8c4aa7b4b83f8fd6378f07407aa082aca25a64431f500e9852079d1380ea05fc47ca6b665339cb9256e3348ab0b78b7a74f0ca2c2250826497d92cb02405fd9dc99e4e1fa604aa57e11fbfe1f2aee3c58d2c0f141ab00b141c9cc659d693b4a764a298da0e80f797b88a8c6d0922a10ac650abd6ead08a3beb5df09ea0a2ffcfc03e02638ecc1014a31a1f26e763abe94a153d2f20507e5b3674585dab8a95a85351f6fa5466127c37ac57707f9d0587fd2ea7e6b8100d019d24274a0140619acd9c0ba0a20ef09702c4d873197d4db29288a586a9705948a71d9a4dc22d209f6481aa913f1a519ec0d881ac3c1ea07f5ae2b88691a0bd43618e093ca31c99ee842a2218a1effc76d826c42c11cc04d301983c4e3a10e21b101867a044da01ab1654670ef513f55ae830124058d814ecd359aa175ba06854920cf7e3b430f064f9920ac09f5c6167002aa65eae2e0af13985fb3329c0809719fdd73947bdf84ff5e935cbeaed2b6b931e7d477b314756e3d18b6facd043062c84ead44c834d4187620fcdff44c0f3a64120900775141fdba2855d7d00f25a6a7a1fc21261b51854cf6d159cf767d9e233f4a1e2018fdac49470cd18d0f36448ffee53314787c44607ba4b71128b9279f43eef41d614b2615fad9b8f70538d67496496365b1bd714da324212dd9c4c2e60ea34ba1579ea54f9620d8160a725c27bf2fe60bacb3130284ec9e3df41243be21240bcfc82d3e8cac43db8606d85b8c7309c7fd24e6fa047d1f5c40070c1928adcf55171a9fe5a95d46cebc0b9a05af9bd49c2d28ac5da32765b3e2e64f222f680fda5fbce47e3eac66cc510444375cf3823302b3cd5c97406896ca931d78b9696fa88a926ffab60ec9aeb80a42fbd6f741568cd1e2cfcecced70d8d2c93d1e4009003dfa49af4e844216f104f1335b65757bb822548525fa68fceab34ab40bbd5f925c1f94b53c32c17ea6097fb89cf9e7bd36fa3977797ba380843b08d479fa47b5857c152bd22e7977b80e65906fd18a5268546e081e85d6befdaaf3c4bdab8d03429c81f4bfaef06f110123804f7710fef85cb19800a5b5c381cb85fe81ce4fde5bf71747de61943f1a00b9befdacc4ede14e0f7836813830cbf9ed38a90cc7fbf21864991b85bfd12e0bb241875594de4aba4e9d6d0a1b36d5b139746d34e885e7f8e3bbf6678fb411074634b7b7557b66ae7571ac9cbc56951df39140b3ea7d64f9f9a89d4e0a4bcb0562f9eef689e1bf86c10868e952e06ebf619ae25dd39f65555aaf244c1481ab0dd995640e281ef327cb9532b2099c61856ced7d73904e0972efd9bae3c37b8f0c3a6747c3c8968622bbf2b2aba8b3ba85ba62c526a28e736143d61eaee37c95038cd1b43fff3ad42aaba089896678c1e29844cfd7a164418b7d34d7e540f1930630d68e1b62a2eed14924c1860dbfb0320848abaaa5badeb2ad70782428dc0f0362370be553d27ec964c927e75bd309e6870c7815b08ab4d3511b592eabf561080c2249ffbf238bf7ebdf73e301e9b3142ab71c42e374d9b51ac24fdc4bde9804a2b87a298e5384b8c73db89349f3b34ec0df8f2ac5b18bd83f10809eecf067063deb995376b3b92b51e9a4fbff1bf671afe97016895ca72bebd874688bc19009d8a14e268f80dfba9edfcf4f2e17a1dbd2233b79ddfc3d5c9388e10a830e1a0984c8c9e6487ec31c17c2403648e50cfef67b1d47825540d72f37da28f709cc012937fef6e2d5ce5905fdddff04bc275f8ec83494e51393d78144e1628512000a6dea0714254e3d68488a25143010ba0e1f5c42828e6c014158af4d3498a34d03bb5f7ff5326ddecfc3fdb8403e0462339d679a1904802626724b9be3a747c306a2c4057c30dc64937f75f3991d22688c919f4f6b993b4c40adffc49827de500b03d6920f2f0f578214b2ba8ca9c9ebac159fa3f4ec5702a8a956cad8a288b5018564f3f020f9add5627657e9cda655a725c1a81c1b065750ed35983ab73f9e0f543ddfc88c0c9587e47a8352d50daf3bf177d7b8258fb02c17097efde166a00978b627b8442ffff6478ad6fda3820dcdf8066edeb1223d7d76ad8bd32c48f40be9344499ef950180ebb5b19ff218c0b322f7ba009179625076f64fe087067f090a5b33c004add071f32e7f253086387632d822faebaff2d353b05e6310a5f508d14c5634d649c161938cdb4b55395e790217918fa145171ae53de2e225433709791f850c44c1606cf28ef2b9c0348f9c90d15f85c3a5693559ec9f9dacf51f05a01edee43644ac7820a7f6decb2171ebd5e03e8478f79054cbd10b3356a3c60f3f631726928147ed30c75af4bf2c5864eb04b4b4772738073367c36f6ffaa60111cbc61cd34915a4fb8a406e0fe4611ee28c99951ac18fb722e81602d3f74904326d17759dbb8dae65c3647044ccbf5f79477220b76b2cc219932fda9cd5340ca980505cf009be3c9a496d68f93ca3f52c99d49b3dfaaf71bdbf71d093efc20018ba9daf425c152a6cf945b30751ec9a964e179d67b01db5d40a1ec477de6001c19a0a336fc28a1cc77a433694bb1c543ff015bcc6d180c0ddc5d8b2723c1d0361a2f9aeb168f4b5d784caeb8b392783769c3445c5ae7f30233323563c2ef206b6d07b69dd7003980b555a2153841157721578804613e674b1c216c4465fc202cb97a390855f13363aadc55b096452d33b1d45c29bf2641a7ffee2c792c8990e2d87d138dec9ca8f1307b40253a7ca159828782e07a06a8cf9e15c09c5365e0502c104a4acef1df809f439d3d9ec7f4119132963b2e66e7dac6529102b84e60ea05e061f51e3e358b723810a962ad21e7e3c68b826321c18a676e3c8776d4e0ab854c34a34b2dba312a0b8a662126021825672fd6a8ec3b6ce1b2bdda2aacd0054ef2a648c52aa2c8c0e97284e8d43f6e51f5aef1c402ee14f7b34f5db7eb50b619cf0c513dbcfcfbbb54fb9206223e80fac9e5f351772ba39a465d262f6530f1fd9bec7beafcf5a6f236b62617ed41df044a59094961c4673a5026a6822bd0d6af74dd471e2d4a858ab15b8c6cee50e7edf2c36f8a5af990747c01807f8860c64d48093d3a8d6af04a92b881b6f2dcc017507a44b9986d27d9e0739c4478c045e3980693515dc3397a90628e1c1455ff74ec892121e650ba2ce6f64e2a461047abafcbfbacfe6ce332a684e9ab44ea75b117a109b1adcf202591793f2907408273ae83e5a3634715e13bbd0271ec1259827b7d3b8052e989a422ca7eda91c06f2b67d7d1893ac1180198a00b94265ce7ec5aaf3ffa3a9c4dcc013d256ed5606dee7686092feafb1a2628804b1fd5a840eb5a6f078c9d301862cbd1f5bdc3b02e5e6cba8797f50b49f573747bad9390d5e547c7f3670b05148461dffb513ec0b9d1f6e77320b11c883c4d2624f253f99c2793497659c5020c9c498e6a653e60b38aa00820f9b7bae02a3ff08ab17f89b48a3b59f2ced6ee22dfa478a3dea160c79836ea350fafd9dc40747d2769568aa93560e2f7e681e1ef0758640756d770405de9f99a838fcba6afe49b4b4a2d651f968318112c42bbfb26fd227a6a22e0fc5285ba21d30bdeb3d4da89888de0a0d648b249b54e8898341f6775e74ac3706690e44210447497794b2a69f58c6ad199829b4419d96ee56312c9e5ed3972f0e83d70c7e4a6bdba630c154cfd8b10a9800896c278735172c20c86e9d8f48a0070a7f29259a84268fa2ba607d8aa0de19d767599db6fb03ab4c0d7dfd2df97b0c98870dfedc4b69aba8d88cc436f84788fb2dc300783508cae34c999846553d0bff9c1a02ecc087cf45c2fd5d4c99045858cae3aae821216298da845265fb66061fe1b852627cd0d16463b320c6abcf36577fabbd0da0695edd51926ec7a21b0910cb5ac673662aa3a31d10f2f1582037d28332b30e0ae9c0bc39ab57a0bfdd0d8379de0a0edf65a9fbfa4cb6a4e707447603cc1087f6e7313a24f498cd61a608034c9516915e4c7a0fcb6ce200f131e2add85a1bcb2d77e2051a3347c8a50e0e9bbf6cadfad44c1062405c52bb8139280b51582b5660940f6ce9e70cce5d21027b0493e6f2645bd327a40b367243765c72fb1e9ee190628e14f7022c5d878e0a52442d0c380e43b550d713d68da1fb9d598f7a537ff8cbb9813bd2a41ad0cc0b1361a9f29a3a7589436e71983b8a3d23ae2f85f0476cd0248a946e0bef9f870a71eeebc0f80684bba294e8d6421f37a02f163841c2e59f2a02341dea4360ad0dd03651f3be607e8ca9b3f1286477fd943cf06fc352eb13fa54fef500f41a1b08b39206b8da73342d892360798344e321a60652adb7194690b9d7b3093f538d014a7767c5baeb5959e5fd81acec9973749547309c674086d1de9405d34c4f56049497667487ccbd19834293563b66d934db21d8ae5203139aca821207844f660ba8ce39b383c2ed9e7f62d98472268feab6427bc9bfa3543437b9fe14cd73680e6eb165d92672fd238a7cf1d64567d148e6c6fd100984cb4af69e05e5d9aa8e028d72ecd27ffc1fcbf35ef5675ae890c0788665c554e607307e0ee7b1c27ead00bb782fc919cf21e09d6ed17107384a602a26bf817bb4c9541cfac1b907173e09bcebbf86af32a79d2908224ddef1f43b3d4e2427942a9184bebd2904ecbfb00ab020d696bb077d174eccde7df10a6bd3784b78f1061e0af5f557a76858160b051f409fe1317ab73ee9febf68fe787b1227ed5ee50edb259397a1cf2188e4330981db66828fd7ebe5b42713ed7ce4780d488ebe7151e2e0061b46f94e532f4307a59e5470885cca0e8895d54a7182daf327e9cb3eba38896ba1503dccdb44c7094df1651e06370a2ba30c99a2fb6578b84fd36345e0d52188a3f1d88cc030be0792f17a6f68da8bb2bd0e7c86285dc359299e8f01dd0cef6926d847555a06db08c27e9e16ebeca374cbcd8dfc871a09f62bbaf292fca50c957004df6a04ccdc0bf661311325ab198825bb380840bd2e60ef5fb38ae030a077204aff3c7f52b80377fa4c8210e3a4a2f8b8a5d4e1472106733a6a48f0fb72f6e942ef30626fe90e0c15b3c9dd2ddad58b745c6b45e8bfeb479293df5a5a810f4545b80e118665064fb6d574db8c99f9c599c38629d38c45aae348d3d59a423935e7f105846759024af878e313ba20b23eb3115ef2dd9cea547bce638db4f1aa3772e5823221920700e76b84986685272ebb81fb7914365cae882dd0c99a9b872d718f76fec24f0f6bc85ee6b9c8377f94feffc047aa07741f748f09c2939d732b3dae046e0a68060d51e2f82ae09893cb904dd61610aca06b5e5f4abbaf2e626fc54bace3a9e4059113e6bb992dd30d58af07a8ef042b500f4fb6cc1cd8e55a833e761d083a3b0b07cc23c17ff527ed768cac05c498a1bf34d3ef9a6cb781127b326a9465f2cd04e2e6091f29b3dac0c422dd23ca54d1b992e844204d5394452bca2dd2f47c1e09592cd6c516b7f927a1ff09e873556ee8ae2de87efd8c5a2684ca42f8c980a9006e26d71717c42c2392ef59b60d912a766041415df373c535104dd402d290bb021fb9504471832ed06aedd6674638017bb5d714e88bfb6b5b473efd87c24dc50a93ec6626e695286de966932c299f20af54504dc1b3d7bf93ff0bd258a03038058c926928760a30db939cf2dd7cba2d8e3ea165b4ab9bc3de22e4f1bcc48d5205efc78394d64c654f345be89c9cf4dde2a0185833058d2136d0a5738fc380cf0fa623cc9ac6a3308ee2748b6503c26bf4176b08a3793673c78b7d09b86c100d06ee5ba85af63e8dfdda1ab9deea0e6b98a63dac2efa4baf37f61dfa5a12bd8e029c24505f8f7dc577842024f8acd427fc12ae0f4f091826ffc0dfdb0e3942b40fb15fa642ca4cd11669a5a39c90b3ae93524019e380f7df2cd89b62b5bbc61f0d11ef3299ded766472e37f8346ad29945ef635566dd663742279d950effc81d023fd458c5dbce160b8ce2f9c6e3ea270322938b1414a179a65caa8fc94d58bd060fea849a2819331d73bc4592142c17999e5353c2897b402340a7dc653491240693ac39ead4012645fa7c46ef91d53ebb7c17ffaed6c325f266a95e541737660df88f8838eb6f055c838d2990c3dd55eba13fe08f1d0f6c3dbe28f4ed74e67f08e4d79bd76def62b94cfdc06788c8ccdb796b3bf02706724e81b6f24f528f030c31a927a5b35477c67eb5eadc06bb822b018efbad736616246c4b395a9bb89a00d57f42bddc9f8ed6d258a99d3a8ffcf6b71e6e00db6c357734a51d0e2adeb30a317a039d03c1f31086f1eefbfa811261638c5388f86bf5ef7911006287b92d0208f6d4ca4c678e5ade8907b7f0ee13615d306bf43c16a04b38ed6bd5d4bfc40916f42dedf5d9005d5fee45f9208b4b0596154561347f909598723ad91930750b0274ace6998a031bbb1c124ebb481dbf6afc93b8871f8b76a15ce4a876a33d0fe5d182c78c599211af0ffe283074a8011a5531ffb4710628afcbf0b79da9090d27b817f12b1afd9d84400bce134e06ef872554e29c0fd4174fe6ddcf0dd0520e7da6e491846303b78cfca2ee55ebc7994b1960d8c82293b7efd053af4930800870c5cc62d7e2228f6a6bf10df0aa2b87eb842296ff9854d48040a9e7046c350fc9b60f435c30635941e9140913fd2c1b71cededf2fd42fac3c152ccf87d8010a68fce0f2d040b15fe97d25ab49997f8bec2735b61ee39569b40583f2da0086064e3836c427b16e2efddb78efb203ef3074a5c0d55640bcb6c1d925f225204207af9d8725bede99d3f7d8b2b17665718c50bdb49922e56132d7f20ee3b878230a8427acc57e2fe7d52f87d9e82391625bab2bef706cd62b95cfad1d9320f15905a207ff240a7b8098016fe8cdd1f88bbd8ce2f26ffa73754678e89ac17322270050fcfb359fa88971c7777b8190486908fb0973bd521aa6491e110eb9f173c90d23f2c7fe33daaf3e2c561fddc68dcffed8a1d6dff66c2d5f27243ffbf2aaeb0a6838bd775348c53a2ea251fa4c69d3c998aa1f18853bf30eab667cbe91d94200023470ab0ceb7ff5cce3fa4daee9ea4bc162baec38f1b6ff35b820a5df270000f5822ac7c1c00966763b45c9f5939cf1f9796417c299f76d67e14a399542240e058dd45e96e8aa5c8504fb567e3e30f7b88183bd42198df8528670e338a1b70ace2485524cdf704a80c73026f8c1a205c90fdcce66c073dc6d7ed0487f27b70e35b02e44f9f017afe49d0963b5f3de4211f0a0b85fc13de9314b9d05bd27a208a3c3154d127ab3ebdd84623cecafef0b63d27692bcebdae98abfaf342c8a3005c51ee09d0246502096d2a57994071ae266bcd091e766fec6b84193316e3e7e040ef4b2628f8cfbb7a3f96ee76c2adb53b0a0d024649e6c0493bb0d3b2ca9110994b562bbde4e7d2a47f2efc7d84d743622a80d663a6084daa58904c2cff5ea0902bf99ecd39cb6b0301975e8e66b6f704d1c0b2cb179614d98d2a10425853b04837ffac3267bc9555099566b7ea1553c9da7ee1d20a8d055b490d9521813440c6e089bd8bc4a7494ab74d9cd435f4691deeb7f653e18036cee654bca38df160e61d6f37252c317ad6ec1c3056afa60f7c98709df765224b271342916fd5f2f0dd020e56c68e20a0ef3e16597481b692faa22c8a0954698d89c5b6b624853fa054dfaf7564bbebd9eb56f7487314786febcd47ca77f6aba31fac285be00c1cb0949ee6b5965ef7b020ef8ab2f4c8c632b64fef41d3505e92e8c58609db0d88e092ac172befa54262d9696defd21e761da873f7208d656748c8418324db0fa8000764cb1bfe49d1e225ebbfbdcba65fe6f2dfaba654a8d36b0b0c30280b97fdc0e412ebcddf566bb785573d44e2aa58c5e6a9628b65ebf2424f8027ef62794fc07b6d11ac0c261dfe6163084448412eefa21a0192f933919618abc9b1b0c14ac0538fc99939060d17646ba8943c78253140adb7231eef26346b182eb6ca395e20fbfb68b2d262752a6b065b312e76b7db6c0b9b5e13eecbd0aa5fa3ad51c57f808c11e4847a74a4d058f69881c8919c3c916a39219b7d032139f5a4140693d22091cc2eefc1b7733ab74352d45cbbc891b2e85fbcedd2a13db08badbe20e762207b0165d6d61f222c1ca7bcbebe010f9a1070ffca89bf61f77a916563044cd1d02b8aae1bc8117798ba0fb2b702b25b5f9c78343626354e3e441cfadf491fd580e8a51754c85ac61272035dc6a68ba0a557531c3e6c6a83db2065fdbf684cb8008c3046b44e97c901b3466cdc59c3166a15f398c66264ca3faa98a5ad51fabb100e57620f66965bbacb87c972f387b8b0b39c3baca36184e7852e871c65f5c6509bc7944b68fbb6cad83db7cc1ba72d5b2ee986db7623e03e85869b1dfb259ee0482a8d8a015c2515fe9679b40fb802a373a5fe4211f73dd62a4711101493fc904ed91e4003c52a474f8ae17d46852cb5031d8893342d0c1b1cae31fb6e81d490bfc53b218aa0f57b2295e497298e2a148bcf219a114193a0267904fe39d8b410529ed7398186ae46ffb616e886706107c48de85cb0d0064b5eac81270a07aa403ce8028665fc1207d43f80af777c2cfcb74793a81a3a3225ecbfcb42a44490602fcd34898f7dcb96f0ad809ef44bd860411ea1840454957ea3e00dd8647894f016b6fee01616103e7e5beb0e1054cc759b16cea10a27e1d6a6b652de032425e07f1eb718ffd53c0cc1cfcce1131a0f72f9b0f51fc8ea5c0aa40ccc8780fed3b0262dd65e03960648f0a0f94f7ce368f1fe1a43a696382e75b8d30b864e5c88f047430a1323b2f333c864aa37ae4957237bbcb6350def725e7b377ea5d2e10d6028fe66d7c08edd6ccc2d37ac70da1cbfc66eb916d434ebbf7bd064d864c8193002f1c8b911915c3969ff4c0456063c55ad6b47583ed9a7f3240f6effe383eb6071c139d9b84c3ea59a5748b4de21aad17e739a02c2f502fd2c6d1256d6fa9b3025333d118353c7f3abae437350201ba79e80a85abe461e59a14c3367670932a0af05475b46f3f57a36150e7992ee3b77ff09643c203fcf5352628ee107f7cf008628a1cae518684eeb4921eac8aa687fe28d408fcd9639cbae20b3cb97b52230b6ab7b5648e58ebf225c28db3859a2eb5212067d83a9e6f9fcb47ea9974c0d20afcf09f3da6dde29e54a33d2377e661215402ef3239924edabf2b6fe3379b36095a20a4ea32e2484445cc3fc9c0cca14adbd810809c0a8ce8314f6d34e287d50feee6f2015e42f3d2b016079fd462715aea6cbf1e668a66858e2526edc5ef6906104ec309e2e0cb2bb445df9e36241d5933ac651c7e752641e1cd212902538502e06ce87f119514b1f2da2cf79bef1c2fe78a34497aa7332e11f13f06d8020c0a802677625b98d235204f156d26bb96da067530cded07865da982f2da44391d0434d47afff7e6ffb6b0b514769508798e7fec4140faa426d2ddfe5ea8e8c1f3061d84cc1bbe6537fee8a6698780139727476cde09a4d9f760dabca66a84acd2088f962bdd0b3d60114dafcfdd211682fc5adb32b2d103aba139abbfef76162a001159fef3826648dc2f71ad50084c13667d9fe58df188471753273b188238120aed066487e983acf19e1f2989a21d80fcbf0f3a635750e013b5b5d1026491250697c12e1177d9df39c38ccd0994ce0ebf4f5f0cf92a21d4413b73f27e8f64430b7d1fc40420bfcc4cfed689a98ac15faf97da41457320df6d0243a9cc7da4fe0411bd9eb65c600d80754e24dae239df11d1e1df8db4ae58a317538c39879bba0abcfc859636eb0752e46b463944be1610fc0b7b4d660b378fe963d1053592ab0d56d1b0726123f126fcdfa71bb8580c53b97e1dd1318690a3db66b335e7cece0031f06f64ba57c176c3623a3a126426bf3597cb8f153058a6a7d0e463e5c06b0e8d8a5d750dd8546136753e60d982ec1b8d85fad1dad1a90824776a525a09e10de8c3bc2ac53ba8e3c4955031d757e1a45d0ad85ef522d2d55ac77ce8183c950600bb6dae62a86841b01edb43ad2316c95a6146e710435ea0d9fc97eb44fd590f19d92b06e39f5cb524853fe5968a908704df1c1feabdad9be6a3124beddffd025f7ddf27722c14632e84c37828756d7e8070ba6cabdac7fdbc72b75a0af3cf0bc11b1dddda783e360edf4446387f8e2956d96f34e021cacc21dc19a366ac550f87503dabdb62323980bfa31d7d50aee4531a8a803c1fb226c7b5475ffb9f4e0dad6dc42021a1a38f200c46d5ab2d72f9746335816b53cd1107b780c11c41420acd57a582c94fdb4427f08610f2611dca98d640f3edb5df29ddda610c0527800ecacec251609f968891e8518fda3baa395a0f0c07ba31a44127e990f4c5809409010c7a0b30df7952e63195c2ac3f223202100af6e9178b25c1604b69e6707404aeb82a2080a7ec26ed640c7e60e7d016e02aff112ad0cc40000f4c8395143b0e863586522b75d2d17b1ffbd0b5187615acf1081bace08deecdb8f876eee2db0d7d88764f7121f85ebf449da2203f3d81ca0c421bf27d4aa0c513f487c363310df6f9f72946b98f03c293889f05807dfc9c9ef8b1ed63a4552b0a1aff4c9b46087a1c0200eeb7f8efa1dcea86270d2e4af7b9a0846d4c130b88efc55ab00e0d0a49dc57723b95676210a7917817b8d455310eb36fda6faa5feef4eacf73b162067fc9a42947cf34f8310ccdd4bf5a2ade82ede0856c1e17298cf894ba88edad0eaa28aa6949de6c74f0392f1c4d6a57faec8dcadf32e26f5ff72603b6cc943a0994262d59a82cf194fb93d3a0cce6a8f4b0cd984ed3c4d7d5ec4d295097baa10e5a3abba0a426d1c6fc251b31f87ba14fab8acc23ebc0bd31a0711a5b5ac9ca07cfe0b01819ebb04a3ed1221f8f7ccf1fd1b7323334e69319e740338cbda9db0c7116540bb42fe3acb1395da3701d97ba64701f7f5b01e9266ace35c9ea95470218367055639e99b33f2f552ac1754e38d407906980b94034ae796d95dd6c030f0cea9ada0c6bf305638ca222cab96add245eabe309c845c595f8f025d23e600023792304fe86010e89ca50b7472517a3ea33376a7a7fd3c0cec8cc7035d8090a0e71635c33f5b14aba950107551087f95e921b46da4195cc48da0ae3c0e5ce0311a0b9d7e12a54d12cc703bdd700532c3248f2724ca23c01041e2c63b9523e028b1cbd189664330c746b46ba81c7a469beb6a9372a3484b3b1a2c5c86bb50207bd12f7d66b346ded14e6db68deb4529d573f00e29d9c737343f48f9100a32802f83546bb43f2888dfc1ddb0694cff1b8e24a7e4f5fc36460564f96a7dfa2d9058d6a085ca04696a9d5e278c10c94e2df302995c785aec22061fdb66d87d14f02c9852578e3c75b2ec58e7909c25722b629bbe39c11189fc4dd7d0fcb9e737309985cbc207c57bb198e5a09e2b2e7a31a7346907b9d9835740c34b284e315d908b475c3c4eca4508e8a169a6f057fc28d6a5922990697963b3f896e571d2f310a58c386682ba374d597bbb80335d0c5fccf1c77ffa15f4b72c1684fe8f1cfaa02ebdef82fab044a6f8ca690f207f455ffab777cddc22e4a8ab29eb2af9ecf600579161f91e2e0cd5b769937066fbd66c762e45ec01985e0eb00267b82db081203166d6a41b9e1c03bdcca8d077a21384eda20ad684b4a5f553b644a6b8707b40f21d94c917e2d193911562905c29751ba5c95778c32a7e50ed52d29283d315a07a97b4bd4bf48eb4a7707911b41420dd8741563c705723c972f50dd6561920f038beca2d633c31c7788717f2972ab347e7ac2d7d8d7eaac5d206ac3069eb8f508def2568a9487775d61f3f3a085967cc4c086f36def4b4ae9fc486dcb9d145a088b135d8c59f6e5c07db2d994d69c8e033ce1bc46f14093fafa6dcfe7e31bee0d7bbd68a2f560105f5299c355b9f57ea6fe3b41f086bfbecf7072b90ad9a53e01b8bb2e6d51fb575cf0091a5953c74d6b1544b712bd3b5d6b6f629b993f71be0461cb0450b27e8025e736554c94822a0e246a7441700cdded75307e81508c7400fa13655c949ba22bcf757f5f6760dcab8a3afe55b4d36cdef505486f6ce38604f24944e063602d02adc4db3474edeaa7f108871275ee3b3142aed3896b61620aa16b9c3b9141da7a6e2f01244968a99c97f84c60a36c2ea1885d01ccaecbdf0b6835aabb7c099d2d0cb83e3212b39484a440b0b8a8b0b0e6b81fadbc2c155106807662b09e1d9271dfd335133ebf1a2ab1bbe74a6dc9e42214c45e0983251804f1a85b79b4a9427e8220f43f7f531548f0cbeb074a0a469856ee9ea3f10def03429bfa6ea5a638505796a86c37d292cf2229f7199583cba4a37cd30037499d05526344a79ed005dc1d1928e86a021f6913df7448aad9bb681f47c07f11ba1b0ca492e5a9452532f45ed466a52f2486f5f63d879f82c4880d5d40e03ec2f2440551839f4acb75bf239388e2925176230efb301e53efb13e500fe7acd4d2bdb2031faf0974eadd9ddb36f8f5132ffc9b43f90c3b3ffa5fcbc4d32ba32e1e31bc02948f3ab608d7dca6158e555a92ba95b1708358c0486b3b9bf68c166dcb65380678655371b5c2eda2c7b479ed9470a76c8e9b3c2bbf5c22f34270371d48e40e0ba19fc418674a4a9a5a5118956ef0fe35d98760b1861737ecc43f941205077205205cc6cf354daf3eae1173ec197296ed018227013ffdaf9db645a42da368470d6c53e736441fac1a7dda029e94f68d853eed9dc7c0548fb81024d45fb4657a055dbea743485ef4749780b01ea59987e335aa363491a0740d75705c8317c84504dfb6368cc8901e5ec7f69908a910fd5ee6636f96d2f25739f0c5b226cca5b407631290a67e79313176b469df0f41cfaecfd6032740a283da7d0d9d4db20f090edb1f2e139fab5b6f6984aa1a5e859f9be501d04661800bac31ad17668ed8a505550bf8b59ec7e464948b5601b7000441637e5e063ba5bb58d1bc30a6d2fc680ba2d7417fac4065f797372d99b4dc046e53de7d1f18e51638ceb0a98ed0a81b07181ca8124edd86bdf213640b57a907fa3d2d0432ba8f8494ca363227c1fc98068f8b96b7e46bd7548b68295a2a15c3b3b046371e3b5e14504b5d90616fb695015d41d821f1be56b5ab42e9d54cf085c24a2ba33154ebdb8d2517722538616d0db88f693e0f159274ade6a5a848c3145b8bb19f07fe4501f4f0cb336e202cd8016c3a9832592d8e7afcb7fb2c02cfbc0f1b21918c63d292c92c9f40c1c78dee01aa4fed30e8303c3e404fcc995bd7446464f180ea7ec9f975ce74e3968641e506c9474f56c0d5dacaaf52e9a29a88e5105992605b4abe44c428dacd75cc77360dc39705c50def1f5f3161a62c0c80e75dc3c0cd0b6b7e0df64e7305b844bbea0f275b74b9946b4acb5992b035d38b58d7a9f4e16941e6be1b8c2e29dc801eab029e0141b5abc0484e7fe0199bb727bb34aed76ff51eb7f794c550d3f89d55e70cb54cbe4b9b39c1b4853beb22fad7b8c13b5eacee435b8c0af7b2aa10d21fca09d8e2133173eb652e2ea2ee7c386398024c76b33d68c79946e774afaec4b45a0fd7af0526fce288de2b375946e65e7e7c29dedf403c27adddfddd2fc84747e90a96fefa397169bac7d0717ce49a450ba86232faae76e7c1121f36de845ad76a0eda51ab547f17c264c2a0748bef659d71d4dde4fe090947e54f864b97108bf10a6176571105cbccd03fe904acd9abfddd737b4ed4c778a8e02af6cc415c49690f505c73ef3ea3e3e31bf4444600667772af7f23731890774aadfe4c39a36a6507d7d619a28c41a94c17fc743411cfac54af5d6c13794cdf122928f20cb311e100a082a646239be51d8a0490d53b631823f488b4c74ed26295d66e4622d43a9e015f6a671bdd28c768bef1ca14b24e02c35f9a8e4237a677c1857444f6ef0a8c0bde6cf83fb5ccbf0d264d939ec88d434f60d78c682bdb1fe66ab02f0ee0e50f06c3a6cb85bd9858677900bda38fd541866f727bf04e04f14e22168b8b9500e8078d5b540dbb2e91bf1ed34d917954a74fbec2fbd78491673a4ec31bef383b82031ff314f852761de725d892380de08b842a49309edb4864edc26d8a6417603006373f3fd3682d9be4f8d69d310f1176d692b704640407cb43aecde4251c432d045e66da5bd8db763132c2943c8d419ec82a913778dbee2ec9b3e8ae52161bc20a27bbf6c8aff0a82475ed5071f8b5c7e3ccc549fee25825abc749bfd771d9ef0a2f35b5647b25b83371555ac8221d814573386b5a6b73019cf6a8c21176afe40fe38ea84f27ba81effed3447bfbbcb58672e35f55f6c7dfa5064bcb7078c364025ee24d2e7efee5906e966b8757dcf98bbf5e2936450a207c289ef47226d933017e15384c7d3d8f1d400787ce8f4c3da1471f05f18a2089d0757d856ab1e15d0f41453f431cfdff7b6783fae40673acbb8930002aa115d1b173557c8c4087b40a +generate_ring_signature 6a7c21315ea9a0794f13bcb92ebfe303e1b0713cbd6bfacb529134a6ed01585d c86de54a568c92e9d6a2be843389eea6397c740fb17949b70fb10a9a58842e2f 53 a4eab03193f38a2e0e982a87120a234f90b0bc7944f51cac35a25176fd58b777 d23b91b0325fe014f6e53b9fe832d97042b4aaa25cc5f1247658419c48a8bd49 1bbb20d6fae0893cf5485c19db5e675d999a0e9dba0e0718801dd3f2707793f3 2b515426826b80e919c7b7acf9616b6dd5572e0d5dba19bf9e4da51c00335487 7c9eefaa09435f31121d5e355e4fa554668f4837a61973d30dab74d464cde65b 37a99f9bd561f0192816187d019987c0ce5f147d4c418bc66f1a5cdfa0e883bb 8638bfc48383464f756c3664e864d326bca7855cd4f8741e29c74dba9cf4847a d67f42eaf5b97140183b520d329b27d245e46cd7bd00c2a171ffcf3cd8a0c9e0 4e7fb336b76b0304d312f2d75b8bf75834b7398db6526008a74874e4e56e6aac da1afcb4fc99dbd35c26712046956d72c6ae8500c3f916b34d670e6c9daf89c3 3d982fabf70524a3fbad979afbbcfb2d0bbf8c2aff2305bdec06e5c79d98b47a a5f8ac98cba7822b50a7073a987e71de73ab843465b744a3435b4bd005522a94 08ac7bba764eecbd335e48c24dd1c04c69e0f2b24165b12a408901dc5da87043 6a9232cec3fa118d16cd669e6dab9a8ec07a0141b3d3b5c97619289400168432 cdec045aa9b95db4758e28ef5d3375c539019188ed4994d59d932c5147e7d84c 76b7e24b615b9c211c78a8b212655d2abd2ba6da10ced987e827163c9f8d80c3 48da2559b00cab37fbb05f10ebcacf51650976a020f045d7dc83024afe1b2f6d 4f16a0f0e1da96f7b885a88f5d203502d5bd717ec8b5836707b2da85accb05c1 80d6ff504ab0531ce476105fc6c6ab4cd39e75f9ba0bd781b7f6e9ff483f71dd a063b2575fc0ed77466d817dadeb051e1264af664b3baf2fab0b3bf3c7b5cbec 69d8a67beb3f99a7844a980df676962f97b9971a1b084a5220bd830cf279e181 33acd6c839c0c7f438189e1f67a8313be209413fbc7b0b39d1265a6d757f879f 97d4d0006c8c7deb27813cb1235e7c914a20da278801c2f3697b462071114276 f006a40b43795409e5c5254e5486c9522ee2fd1a338ed26e9b40ec4e1c29cd52 eb6c0ccb68a8bcf2c2f322652f6e0635d46e214f0bc0961e6ee431d9a6e70e6d 10a6835fabd12b737505f67c3194489ad1f67e2f75982d50c5eccfac7661c050 2743f8484a17f4b8ab423d9a06e648638f50037a20b453f91da821bb8dd98737 ef6cbc0d2dfe502227ce198c737f0bf0f7a4931444bb8ddbbd82cc317449dade 574b33bf68724f9217faccbc97a60e2e6a7497582224aabd262dadf54a73453b 1996d428c975b3ebc9504f329e76d724f2716fd2d6b09436f0241d55525dfcf4 4aa00e19bbb3aa17adead7dc76b7c018a9afc20b424fd11b53514193ebb48568 346d12690e9ec83837d4f402b0fccf731d999b38cc66fa1bb8be1e46398b04d5 a819b6df989fdff76ab04fdd6cdcadfa2751c54e14c359b6dc63f285bb545ae6 8cae8ebd0995b16248b6f1e3c7c61d9f0de44b582ba27f155e1f315b81c0af60 0e6ab1fadc1ce2f6473481649a3a500a86e4aa00a042d0257506022a7abd0965 e2adb81732a12b14a36d92d5ac877c7eb23b4d365a31f6340ee0e85e3044cd83 557afb592dc585c70ad082f601b97ea5d53acc1e8c1cf2acbf73cb2e4ec8ed20 6e2edee22b8a26501186cf35808472bd9c5343c3763d921322398abd5b524e4b 321503c0c52728139ef0c745f510897978d1805ec1366245f14c12472c8f7926 1e94842bf9529a263ac929ac90835961315297f2d01d83fe9212cd0c5d86f662 f278b17bbe1236d3066be0bd4befb0e914a524f85bc146c117e05db1abc54126 d62acf5d9572433e91cfff5d9d319723e0d49dab4a3ff4a7a2c27597ef4171f9 1cb00476f95508a5c4b27c7de7ae82386bfed926cb335dc6bc934b21460abd7f 32855938be63eaf809c44abd425f69aa8699cbf7f8ddb2a94817692574f85b31 6d16cd460eef86fdd3a4ca2642866ce01e7f09d9c7e3158ab61fb4fe7b1f1866 1382892cc72eaad23a9d62e3dcddc3c8e093177fd99076db600ecfed64b60977 d7757e0cabd39e25dbe09943b20145c8ebe5710004c53ef8355b9e72027f6cca dbcccfcb3708dc00901fda1044bba7d6528a966dbbd4c7fdaad104817a37428d 42296a58204e10349dc6b9e58625ed27b7a0fc30d56f275e5c492c78faf3005c f40a1bd0bf96be276a9a65ce584aa7be335b762ac9adef807c31630587ec85f6 4e4e6efaa2112f4a9f3e2baadd787b09546ae6090f39bb2c2c701ea2bdd8a36c f74d02271c39444a3d2742e7e4015fe3f3a7f3abd96e4c04d719ab0324352638 1f89028e20b6bbd28eb46ce5a94886c0fd79ed9142147991750b7ea512c03be5 fdfa920e8a966735aa26527da416a60a777c4e9977d85417cadbf32400d27204 12 01c63a382123fc6f26b28ad75e53dde45f27e1425728108859a297815a4baf01b220b05e789fc09b9792090fe91986576cf0280a916c72bb8583ccf2faee5903972aef86002ecd629eff90548ac10c1a1284b7abcc438bbca0cd0f096857bb0f1067a764476c4f7c97011a8f1aa4eddcce8a396c0beadcde37fc15da2f108502206a9605ad7a54249fd1765b446442578f2ffaf7b4001c2071b03fe5be49e40583a9eab074267283475d9f4f494138e6b159a241bbdd4b7bf3f16bad4075800d13fd7ce92c25542ce9dfdca846946921590975e57da0e5d48cb49966d57b550543f1b3a0285d9c7230e62097d418fa79a4b7245378fdee40c3454d6eb213f00e5ab1169927e696da6f9b0d459026143a8a0713a31b55049e571e19c5ad427a0df839b4b5f783198889c955fdaef98b41430b0fa5bdf337a569d16b7e49e47e03660f450aa89032d433e3516a968a8d990a38b5db01af52898144edad8f39c304b4af83df4e2dfba14febc75d829f13349984d71bfa7bca7f0ceefb6315e7900c645268f5439c0800530d10f8ad07011e50aaa32f17a7f4b40881da9f31337f03562bb186bec0f06d1f94a90948806f3b0251a407b5aa2faa4704eeb0478342099254937360a83a3201fa4a2c6d8fb1f1601563c783d52966d4a988da36e73d0daffa9d7306adc6cde0aa51352a3bbbc594f61b5fa750441b293810d87bc5250361a231786bcf4d68b84243e58f9585cc2aa344dbd0529d64a045198269c6ed08337159fade850c8e86e1438f5ca51378e785faa1de0960f67940adb9fba8fe0cf563b3686163f759b8e95e5d09f93e6b62071360f77d6bad08a9f2db8d5a1d01fd406c2a9918e3319a9a2074950f7d40a5560f57e30dc1a8165ca70ff01ca404681e67524cc8827acf723df2b1c05445e0ae822b879f4f1689ed1dcf8748e909e37ae8013ea05607658d73506192601c905d88051a82f5a7c93cf2405013cb02913a16c9433500c21d87e4c619c4cd0779a133f86d9b6979ce41d2e74cebc7076261b041335337a3d1e5172819fc2ad48d54f82115737cc798789d5e510fae010141fae1131cd478b326da4da7ad124454fe345f4a99b8d1b137b2bcf9b25d08050772bbc427f203cb338e0e848abe1e3d3488de3d20d4f618765199278d48005d94dbf62dcc944890d573e4b8e551ed61dcff4f6a2474b71cc6b10d0f96fa0750462bcf57bfefab0dfadb15b5a2bb860dd3a00a3681c35a646b1f81001f820bc6bac2edf351d525ff409d03cec61db64d81e670740a2974195e156208b27d0e4de7540b8793618e2b94c8708bdcd5c274aeaf2c3ec1a37d00ce263fc6f91e0b272e75af30574ec0f7c1e23cbf98986c04b0813545ff7cf461972607d485f70ce7b7aadd0a64f7e6bf60f28cd1b3a1ba96c6dc55b16dfb1cdab3b13533bf250e5997a861c32a4f46fe3c463dabe4b4c574cbb618ae9f1e8ff78baebb6f4e570762f69be45b9c31eb090f0800f1a3b345ff03aa936973dc055f2fadc0e5dd25004274fcb701327c5c13d16fa15919f7939ee45f5d415e5f3b671cf9e7e7c8d90f1e0f703414c22cf144bc9b673a400f5af0a294a1b2081a28b5b4fc6cea86d10bfd485356bca17c9357ce3d7872736e8fc56df7bcbde4624bee3005e30be81808e07a6d18996f09c9a1166b271e94637dd3f916fd22bd79e2aadcb120d8be47001eadf8730a8d727be3988fc41cb267b7d03f37073c30a64c022da05626189107366853b358e57e6588c34492ca19d6dccd778c0035a992556397b281c44f48002fcfa2da45f58443108fcd1affb1bc5fbb305269c783096bc115de2f375f4408c1a9537fb2edb86265cf2da37cb7e53c4b1106733fd54a2c3bd9d1b1d1ebc206b2253289fd600e70c81e99f8df555a29775c670c23d805c892fc6c8463f1230ad5d14de955990559bff18c4a0434b031c655e38d8026ffb6dc0db2cc0c870e06fc10eaf9d43615a753860fc453022a8a8d784cee0eb52a2a71180efabef2380c3a6950c0522fad2da8fad28029c0a38a8108934929b70b870575228bb185f30cad8e6b4d67e59b48305d6a8764cc98a097f51bcbf8c7a6c80a207cc80058fd0cbe6b20b4cef59d8f5d54131fc238bf1d97a64412d817f86a21b22abbc17e890ec147beff79438a5f73c5446d110bc26275d769d06f464633262cbb12ee62cf01fd5c606d7b88f81d8d06897b5635e3ea363723c8f22d6a651464a903d5232d03bd2fd4c9ded992f30438cd41d9468d59c7121f7a5c581c41e1bcbfcfda9e1602f42554a5ada6275e69b8215b4bec1e55ab7aed6e2ff89d5ae7aafa0e6ea91404b3c7ed0066d088a8c0efd269de18ee8e48bb4d776f8f6932db0f939dcfb9fe0780c61e39d1c867a22c97df2d5eb4abf8129d5979c1347f33cc30921bad8e7f0f62d44641550da0e36c65158498b6cec865ccba214315c76c301e1a3135f33b0c062e1b38864fc2fb578c58a69a34271dccfb3f9f2c6c9d090c5290b87a7ecb078b157349fe1dffed51a64b10f27188feb86e3c677a298dd9b9ea867032af76093d2596b07b3bbddd67fffe2f287bb529ddfec683c42aa914adda638116bd5e0cfabd6459fb4bd7cc5c6e7c1764d838a1012b187e9cf8548886b1c7d593444801314db5d4550521b39bed0fc19eb1141f10ef694d164a2f21b5110b6294a3ad060cf5ffd035e81000f4a52574a7fc0265ab1f95bb73d996c0e6857ca53b073e0e405207a4dc5dfe5338c8ac19bfbd18b9cc8777cba715daa8639941d559dbb20727611e9b270b1d13e221b24984f36b8b4773ace4628a84a0a077884214d4c806c0b1e29e56afae63743f7747f1e6301d6292df851ec0d0ef40ef438c0b4214083fb941088bff92d1ff2c18347101b7101ca97d0fa91aae26e257a22daf40ed0f20e6fdf1c223b932850898d3e55f8378b97f040b833e51a8be4ee6be54a7400331f0a931793912ec77b55b7bb18c0354fc77bde55465933ae036ef6b83cd1208f99fab41724bb5dab9c53224c939a1d87896dd01d1c7ce92becec09f8df11a0bbfc92ef6658335cddc8f60d2bdf559d11847f86603ef1ebfa9db447d7e63000214d828a0014dfbaa1a89359a43d0f7c262d8fc6f263141779f9d72dda7793d0a795c26f876562d6f666320956119feee80af68e85278dd25ffe320f6894a800fa1449fbdde07184be07ec769163ec11a52162bd69b3675c32c3dba94b1c8a009f2327b4afedc18a722673117402ba9876c6ccc81e16b62ef06d91664b36d4601746f18ebf4f4b6a380c1c0717abb6d89c0a13ac5e2dd9c44319bc7dcb8698a008d86f97a4b4f28ebc8ad83d0f8d0a396d5e1811f5df2c43e041b782b73595d04c76fdb737ae9b247041f51b8d37f4f06211bebf470d93d8aa560c7a989f65d000c43dbe85fa194a36dc00246a3f4968501ba5745309870388b603aa865615102c8d5abc6c8da5fe814451d0b4cfc2e9bad59b198ccd7bd4ed80c2b36c509440dd8d9e593f86673302b2cc54111814dee6c3796693627ca43093631b138844203ceba574dffd835da3a5be309168750b242ffd44266a2b4667b9967dce3581a019a7ba4184e89a37386b715ba9c801368d243daa408e8fced9114dbc31ea4c40926fb1a2985277dcda4169bb0ce5bd9cef3c4301fd3de21c0d37638912d194e02b2a55e6dfe13b3685f0a945d6704dae1aa7392ac6db1eba5e88c2fe395cf9003b1402be4317accb61c411ccc0446964e0e3a9795605bfb129a34562dc4969a0425868ba0728c582192f02bfb3dd37196f256c6b32c96cd0a1509ed6dd965eb0b90dc350a736806acfade7c23055afb38e50b76afc0cdb1f324e79f852db4e00e0a89f1a0318739d50b8a1cd6ade01e3d6b4b63bcc244819ab9729ad0cdad4e04a865f04a59c69f7d82275ea317bbe574e5a42aa31a733e67d235be708eaacd0269fb932855f30524f44f034ddd95df4c19ead1fe50beb5a709d822a259b1590c830448303ac0b5c4f5a071d8da8dfbdc633f9425cf7909276eb81554b5b4fc0fc32216512d49d4edd7c0ed12a2506e79185bbd9e0dc0b2564379452f723ea503b4d30e235f124d92a6b49d5a56fd018e90e8dd3d169f3867da7e2e365b93840c027a875a1d0d9c881187771ac5bad1e6ee893d194a099109b0cecb34d6d7770c76bd3914b3318fe15769e7484a0712168583492ef40e5cdba5bbb8069620760edfbe37a8f42100eeb4e930569f3471b3492d29a1faaa696545c50c5a1bd62502e4b7c2c9c25efc4f10449a791d6bc71073986b8443c526c4f0551247bf5cbe090bc72feec42635343a63139963dfcce212f0d0563d2a3d4488515708721f02080be78c58ee2e40a014885eede95ad1cb825de8683392a9c7d0e0656028def30f6c89ce1c27c0342739c2a8dd9392f0a4306dd77547564c44f68987a0e6ac20008d23a013da1d12ee937bbdcee0b4ec4cf0bfaa13a4657fc5f3f694cda123a60dbb44b5b432fe2fec21aedc83fdd66e2a74023da8430ed92c347f247830cf3603631ea97446237ebcd26d508b62cf6d9e276b1e92360ba014e5467052b15030065df45fc5cd67a5baa144aa60e68d36855ee4998889604998336cfe85bede160a19fa485ebedbf33ab73fe3a373235e40baf7bcd1ba480b9aaf3f33904abdec003e1f1a9a13befeefc8d4c3c0c72e7c246f6597e2b22be2e5313e1d4265d99e0c7babb43c8fd6ad5edb2e959598d934dc1b0516d02b3e5aafa75817456bb67c01 +generate_ring_signature a4f99b4aebecdbfa9f6c5fd9d62aaf72bf17ba6b85b2b86f6857d4eb5aa4e2a4 903b964eaedb5d9df0b0b34f0b20d58cabf1279e7254300089735c55779f6d06 2 6312750888bfa7ded17884f70275bf7e07062a1df46e580cac091363ce7030b2 155806e2d34207010d5b4149cff7eb191ccd3d38dd5a1cfd9dcaf19891878dfa 04ffca61416f5b7b5d918e456a81e4f50a9d4bbae583602f8544be5a74c35006 1 c4dd6d38a37bb46c327c8514e14b0feb3c65c50f9c43f9562804f10e5f5ad70eaee1c9d3dc8562b2e09b6919c8251cbee6af2f122147a763f7e0e0fc7077b50df6a0ba56dbfca296ef0aa192a215434dc79a73068e1a887b2c915c938bd3d90937839328ac0713aa5ef2b277667fe3aec7e89d3b40a2237a0fb04a5398c5c205 +generate_ring_signature bdc62a6efca6c735edea0f7c692ebf61244271d37dcfbd89d538eec558460cef 615390da874162142d31fe5efe83e353bb9a35a06e861b6fa96012afb6606e53 3 f0927d23a105b3550d3aecfa2c3115c5730636fb068080dab5f9e2a91455fad5 c7a262ec1e963a0c885a967fea2e3da4374e2400ba9645db84ea1c785d13d07c 38a3d5ff005353ffd24da4db56e22636e1bd2c0182cf4e08a84cb50af7752351 a31a2f30ef28f312d39d35032878250a53ebf5c77325eba642299bf98b567d01 2 69636adc4760b89545e0a0a5f4fc75adb5b251b3a1b97501a6c4fe55e82e0307785d62b193e4ce8410c5b39d4861058a221b77f64a8477062c4a8a49bff7b50088039bcdb453ac577ab0952b07bf05572dbfeb1920110d172e11e266659f7305276e467f940101ad5c6ef06cffd3d9e8599aee30be02845eb1731ca2fc916c0351d5ec49d943bac0a7867a8011d21a008dc0ce649cf4c0acb865d13f9bfc7401b5f11146b91a28b57b93f4a7aedb329b6c9adc5a5c3486c31a9691ab4d2fe401 +generate_ring_signature eab1439501745d1a9b34fcaddb7a42e8d09aac0a4070e49c193b7eabe02f3e72 6ce8208563bcd24b2582f5c8686df7670c4046c6639d9948bd844d83c79e6813 1 df7698d71575f97f8c3699da593b61ed18f032140799e2e62c7a14e191ac49f3 004c41b8110c26fb97c3ceee44a7423e7ff0fdb3e7911af60bc4a03f2efd5f04 0 5ac5bfc46ae7b60a4e6e2a4b7b72db8cc31d51c5d0262c7cb8622c2bc2d0c307fae1a2340e2e7ac92fb344c6d40c73e43dfd118841eddfbb5bf16796f85f4b08 +generate_ring_signature 50d6c84602d35a33a30fd22db0d5cafff513d3947624c0185dbea80caee90c5b f686926e40d3a434315e5f4ec24c8282275b89773f52a4ed817025943ff7f022 25 9a30a185b0624525d23c91ea4f28bb3721a797a790c5cec3a7028bbb8e191027 8f4f792489a3796f4c9ae40b0e7fc91a7de29072db0dc09d3c5890a294d01a8c e12506e001bc9c5bafe5301f3df40bd27a974ae162d1446fcdb268a2406193e9 e250ded73cc3dbf89e920217cfca4c614d4eafb5a40c6e36bddc349a4c686597 b8b0831d43d7e2af8aa0d607b3ac194c33dfdf7d3efe78edf3cf6546011810a0 76423bd48cf42c31fc156e3091f20982c6bdc3db8e792893528279ecf06384c1 b7cc082b2637338f2530bb5340ae7c4fee3a2143bd2453970ebaaef83c997592 f85494d2c98b7f25681f18bc32565d0039015aa5266f65dd232dd731b09b4cbf 3ee9148bb2f4dbceb9c0869f3c4e7a5245e6e549f214bcfe4d91f78fcb15d6ea 1b250b0f1e2114b6cf2d6c7fdd4c7b9da1f7bf721172be560e2fcefdfd4813db 9d1b3233a32073ce52df7d1a307de6b836582afbf0474351e7f0a2b2a7a9213c 83c98a063202a587794fa6d5b0fa0d3c18a21b81a43cbb366d26a91735117f86 537697e77585c2097b51341c2811cda8fa3550cfbc44e529f0a63d63ed9290e7 5da85646a0b264efb575a635de1ef2c955a765dd336351e1b2ec3994d2c04e5e 832efcb83d53c171206dc4cfac510358bba39225f385a551897c4139e5eab6cf c6c37d8fa87c45ddd9c6d9f1b770c6faf56f903b59afaa8803ee7fc6ba5a3e14 6389ab8261b9771d991039e4aae4bcf58fa807749905593d1479a23dcf6d2e3d b6fc1558d3db2bd362d2aca176659835a3d7f34d5ee2c6da1209bb2355b4e954 91c3a0980db59fccc7a90a69af24680af4bdc0d17e0a72fa7fa5da59bfa997c0 e6f8b0e3e397895d57639d5aadc9881aaa3d8ee04d31fc68d2f5b9e411683075 0a4503f9c48b731d1dd8b2280a575c15e5fef2ecaca934969cffc5cd115dbf26 1327c1f73ffb2ac17233d79624a17b1aac6b645f16f2407de8a92077c19a3c7b 6dccf4059e35ab49f66e199315e763efe2815a503eab8bcf44f4d25534f9d200 f947e8966541a52c0835c2a064c2cb71bbc652dbcdb1dbd3314fcc8e75a01f3b ae46f670527d4c0d9f04e0a4ab67d23a4e4089107c3250ef62c9a43a27c1c4db ab8a1a9091fa8089477cb325d13df16b35fceccaea164250309204415733760c 21 cc961eaed416a4495985140d947180deb28e739c7a86e3ce80c6010add9ce20610865f9518f63f66456c587d138293d60e382c2532198b1e1b8e8c16dd9ab2077f76649dc6127bacdc33e1265759d48f93c39bbf798f1bf116cb5e82022c880e1d0f8968981cce1db021e00b78a4516eb360631466892d80c13141805f806b063a1f141ba661e8cd96a66a783d9d2bb762eb626eff7cb2ac20bb826b2aa1300f6203c331934eb93f64675f7d80fa540982a6db494cf240b40df950ea1d06ee0beb03d1b7304113671eca73e5ff0c128bfd75a10d840701dbd631feb64bcc7e0189c74eefc3d069b55d656ad81af5fdf846bf9c169ef81d790b98d9e886f2020386e2227bb587ce4f28f54b94c492d88f047a498d51e941b5e6223a047b1d9601c76eb05a8b14faf52011bf63e59dd950eee58e6ab9d14af92f23a13cfa06e40dc4338f6183f5e0281c341d051ba3e77e2c336e35db65efefd5bc829352de5008766801bb0cc50f410d1d3542ab471a40fdc78e1665cc04b70e0819dfd6817e0607a026e7bf6f215aa37e7a242d74a13ef46233212a85bb95a13296585c154201e659e3c7383a2781f7bff424a77eb91d60bcfcfa94ac057be5d2f53e88cb860e230de56079cb85b9d3915166929548feade266b69df96c9d1734f6a115396c0319cef2f5c36a6bfa34b2c0d4762dcecd618a27344eb8efb9e4960a92eb724d0339e06ec463ace0ed8d9f83c5fa5048321908ad619098a8b4fed5c823c7523d0b7df8411ae9b141dabed269568f289cd26c9181c6141916eaebab2af15fbb190869d2e1a68697d0d82d5f5bf72a7993995ec925da9181dcc7e70f4fc55f16fe013e07c6a74c39b454cac5dba654df4adb31849a7b981585183230329a97ceb30bee5a8a6ce2e996eea1cd29c20434e92eda6f6b0765756999652d3739fb0f0601aaed133843bb26454b15b5930883a5d46b0955ed313b090e6de8ba2790b76e003d2404bf066ab82bb7581d869ca53168a46aef1709ff96b682d0128187bf400ac2afefd4938645991c2222e453159bcf7f9df786d4da14b6699efdcb97df0701e16442d73100467afcc6cf64832a8651bc527c0f08c4476920977db779b5c60c8bec26cc1adec998a601a4e003beba0f3762bee2b70437130340d56cd7bd7c0948b394ca7f7aac147b77831819cde8c6db61efdf4df5cac7d6481656db3e2604afe2a85243666e26a6b52127ebaa9c4c8ce62e732ec86a9cebaa27d2ba1e5105bb511c1b56cc7aa42ad24e2c631dbbf8f2d3bf42422411494539ea9abb16aa0f899001604bda965bf3629be7eb7998e36cc9c7570a016290e29afb3d8c4d550366400f8c6e94b8d685daf9338acf4c2700cb13048fc6647de067e6740db47b0ae90e0232dac5803237ce1a738bd18a14ce1686307f29576ebc0acb2d5e9e2701c24aabe397db8d3401bf0e91ec8b55e55e115f3a993697f170a54f9a5cbd6a0abd3696d56f9eed21eb86bf6419d59ff35652552d4f90a0dca304b760fc5fd101235231cce4802982dc649f38b51a4ac5dcff07d99ec7610981a519e69826240d72aece8bb8879b86974c9d17d8d468bc7f40962b385a02c578297c56d0fd1a0646cbe64f6bf8ca304603ca241b938b06d6412e3f2aec081b999e9a13221bfb008c923d90900daa50720d96aa03aee4c80e8070d79c9ddb94ac40c23d9d265b067a0ae3a4a1b4fe26ced70ae972ae08e8684a6c02881420cf1886081a22d92808f7eb73be68ca0d1c5eef5edee6d264d335bad8d38675e6a071b5775f3f32700038b35e7b505937fa31a0e479a6e9fd0324dec74af6d13bdfe4f8e05565882609b7fafea7c3c45d7073e1bbdab18214627aa98292d93aa7b82adbb1cca6c0700cf43fc3f71cf315acfe4cca6323942e30dc1ef14659e203d232d75ac58dc18c011bb39e14662b6e1df3c82836767c99113f118de66b4a7e51d7044c5cb7ee230336ae5c857bd873e6193b12eacb2eaa7381d7082429b932e7cb58a0a4325c0809b8290d4a9b043ee69fad236c7921c67bedfb1ea976a9f3aadf7678c72910d007037df603be32385b2ebab08ce07fd4c51c66c88aa1b497d4b3bd2ae8a2270f01459a8811576fce6a8f477e9ce16222038cfde19dce9d64dbf019618efdca100857680d301b7f67079f39536e7857d750d2f5c6782246eee663f8a544d342620ddc1f1a88c9da38a67eb939cca8cc9bfbbec1f653d0964ebfa8b6373d6f988c01 +generate_ring_signature 24b6afb3f6d03575e6b84afb6fd3787b6554e649223fcc94f0c3f8aefabf9457 c34607c491db77c2a81d12dbf15e28e3fc50190e43560765d84b1d88a04d763a 5 37604db2c71da54aee4ffa062a3ce264f6f968bb22e195de69667a1f6d8346d7 92ff0f511601d6610f31f3a53c956265053e53a94a9b08e841da63cef7480fff bef1ec8aa444f5d1702357ccf31de57d9d08297471c8071ca27d3e9f30302f6b a0dc011ae5cdcdc8ea827b893a02feaa74556f6a50801ab1d5994f13ffffd133 77ce2aea1d830beb85788d5b9b9ea39a1aa0383807299c479547e16e7c633acd 1c5e52449305df865f8156ae662da8e69d334ee7d0c9ac835a359ce5ecb0eb05 4 6f2b673cbe3900f0094d3c160995450d54eebe0ba9850d48be2cae3d4984db03b3423116443ea6e82f04dab1fb425a1d077f927d75461eba92520b93ab74aa0bffb148c2ac06acc04f90b9cf4e282b4b5492c0232b74d381f99f457c6613df01ecb2f4e32427bde1ef6e1244aa61009990fc247e3f2e95231a3f8d709fd9250368c4316fba9074e6531594c9527f2395b78d77a07d29cd0cf607d3ae0ea98300001420684860a03dfe1ab6525ed46a93bc3207476143794f688492eafd1fc40d1a48c146e8a10d2464e4f9aa8f5d51b4bc369b85032df5fabb6c1283e476d70ce4a9c5b196577df55dd1d610fc3acc5b64b5dbaffe30b3790611922c304fdd009a41d86e9c4f5b72bcde5ff5bda27bbe0864f27b8c839ec2e3c792c53ec8e205cd62c106dbc518ea63f1127ed834dfd8e239d7b079ca29c98781b331e33ff007 +generate_ring_signature c5629b2c40a6c6c737f5dce8411acc9c276e6f0a56e707f8e57d60a7071557d2 743c872c39ccccc663ca95a73a624559e470c89275f9451e99b943a12c917508 4 7675eb1b2c42c881247435df509b540e8af4b715b863f696cf8f1d7def9492bc 2225eb2d6559955c01988be31a3dcedf4cfd6eab26c1b894f0c89b6625bc207e 287edf6cc3280b1a0e57115d79b5b762b3dd404ed1749aee4cdd0f3850bec996 601df0bfe882a553240b1bec04f63aa6592171d4f1da9f9bfd6509f1f76b463e fa9e98f42c3d58380216f5c6aa0ddeadf5a8f7f7e94b0f5e8d68215838d3bf02 1 660f5d8d336c6092f47c0a09639413edd484c38e2e8dc2770e6e1f95cfd85b067deb9de591fd90a2b433610b255b22d5d2598dd5896c35f2f3cd90d5061253042d72db5fba39aa722236224b4a336897b6ab165c76cddd756314010fd831300ce4c133dead1fb448194527b960a901cdec1caafe865f67744c333eff15ca2c06c131d5365395a83b91222e5440e6a696f57b39a1adea63677a452685e88dd203c799a4a792d1f1c16ca9239c7ec00107049d45b124b3f8888940c480bb968800d839779bac97e86c4e438dbaf48cd62471a227339c2d2c4d95de56f9327d080127e0a7adfd10fe06342a08f5182597376f114ba748c7490b49690fd9047a620f +generate_ring_signature 1a4197dee423f190acd42720cc120eacd6fdb3bf10b79234630b8858b6ca9767 b4267203b4ae75d442d1ff8149369c96273da7aa9eb82e40b905e1dd1637e96c 2 8dceed51a14035531d0438a6e2c16815792a3e4d76f0f7b34b5491e4da661fdd ae627eb1310b295996cba5c7958b1d0f0fe5bda5ebe6bfb1b4b75e8f82a46b10 0db7b3861e845b76870c76b143d4f559cf6449fdba4bc46075817caed034420e 1 5baa78f3494b392e645c5976af1d036e70ef7cb21e4d6ab3d702ee17881e72097fa5e0c1695bfec4f1a64eaaf3e9186b79a441282b7ec4d1917921aa0870130484356390e0512a064fc9a6c0bd205556583458c211105bdc6aac52177ba62007eeb0c2687a3d72056194090c7a787b90b5936ca2273ac8e7feed811fdd7e290a +generate_ring_signature aac5d207c0aa7557360adc8f06738e1881fe94b73f57368d845f5682f8096f4e 552caa3ef09555e0a216cf8a78b2e18271373231c4758eda7a270b2ddd71b92b 53 6fb3972559f40e71e36a3f1106a7866ee3ac12c0782692e78c10bca229640457 e883af1eeabbbb707cfe9f543956f5bacf9bbc169183279e901b91a1d2481e45 f1ffd5bf8f58e9e1474d1305a55939efef176dd58763423eb3dffa481e103c71 18e614da426c877dc72f4229ac2b4811fa69f56feeb194b73b1df699b14e79f7 a1ec96815246af7cd69ea904d218f724d15dbc0eb271235a4ff1be126925d4fa 344278387d740c1965901597ba56b536d71aa9354b209819bb4a0feeab3deca4 9ae49f5ae55a66e5b3a4b78e1c05756c13c7f7955805e984e8955bc79a7c8640 1c0fbea44d7ffe1a9f1145a760f6f42f7470e8e4037ba9cf4da94228b91d5380 57eb35ad73b6a99178453a88a91be2ce87e886f38b50167b7d121906883bb4d7 2a7b54e7aaad50e8c817096733116f69a22a72aeee7d23b9ea99a8a381015bf3 bedf676e37a4e4dd53b92a8a469184ddac922854327d98a02e5fc648232bac01 e46ca855f80c8e707cffbda4f0cb6ddc8875548aa8d30e1699facb2b1912ef43 89d6e8228d7a16faa1b37309835249ee56870557877394a039fac2d2dfaa27ef 2315f8280152ce6b1e5634e2419b22dd94aea973193b61d9296dd8bfd93b9ab2 12cc102744cfb58b2c388e52fa9c581c2f4ac84e34073a2a96376c6030880a62 f6974ceea92b921fd60f6cb02e275bfa7f500eaa682c915aec114068d6db88f5 138b0b7687520bc45d8bc3e6d9bfa314dcd1d8ad51c48c449afaaf7e7f056d71 e3cf7715e05664a5cd498f79055b41a5bc60b4b8827fc1fca083ee55822eebd5 4bfb5cd9dbfc3a1c41e59f9e1c00237a46819643fb967b5b92a80e7e386430c5 04e91cdeebb6eee9643337c58c719e44d401eec9b044969798f92fc2208d6ea4 f4be010794473e1db3049ecd4d84d05627dd33be6858ada87513d89bebd93778 b422e821b07ec1531698e2b89d50997475f78f8c59ddcf7452fa7094f8994412 8ff811aaa240794bd5e8cf5d21ead05fd755678656e8f0e7ae030a896ad0e3a1 b2b31ee946f51c58dc3d1e831acdacb308da4c894ada26d92cf885b466da12b5 2e333a1e3ee3b906b058bdb67db9884f2286d53bdc14c5143054b1e6348ecf63 cdbdc45b81cfcaa2bdde6d4319b0234ca2b8c464e6aac3ada947a181463a27c4 846eff34251f5c0076e4e2c854983c4044b6fdb836b1d6958084404c842541d0 31b2b6d776b38f559c1e8f6fbd7701a1f9cf067bedc98b91d5992579a6fb20a6 753c3a97d7a7ac59c3e32b3245b4c1e6a8c65fd79e2645dfec9e08196bf5c5c1 facda9cbfff0b7f8bdf25dd6af2534f64f3edf18109def802733de31abe06579 288da7b095382a572bf2b269f3d2f0dd91468d458a2c50be1139dbc014572af1 7edee65275e7cb061af3b8538a02dd1cb54c3a0049d722b3fc295af17c95814a 90f1b088ff7cef4436e5cad92502d21075b84748c2b4758dd0c1fdf0396c45a9 e1e9df368aacda8282da3cb81ea1758599413bff17aa9b3ed4d4c58a26c27857 7d59b675fcd945c0cafe8c2ba2623064e066fcca4127b04e1750baea465528f0 74e8a9e84b57bb4b07e46a3ed620a85ff609dfaee364bcb6c1dbb28de7f1c812 14fd789d3530800344d64a8b0173693bc41a2a909b18cfa88a5ca700acdc726a a6b766d16ee81a8570f6e7e3fefa39954260442b42c4f51a3b83ab6100292baf 6c8a23d96bd60ceb6b63e8353c2fc37efdd85b4af8ef9d6bbb6cbc56b78f3ce6 2fa3b181f1d202dd58d0b064348bdf31c1dad990dbbc8e059a1b414cd126e918 d2621e0e30d4c067701617544e8c86a0d4e2bcee498a419103b500868d9606ae e654d8b768ecbf0b688d3e513858f9c9515de599c70e4403236436768d1a1660 6e05f7f1c282324b171d197a03ab799a0bfdc371ec291a5dc8ab1407bc0d7e71 793bfc7db631e1edbd61fbec38831f9b5e324d2342ba6fb39e1f066e2d8a812a d869a67a0a327f783d72c395cc8b3319fc55edf427f2233b40200a7a76b3e4f1 823c7157e173f079713580b594edb97a0cc24feaa2356b5c493d927b25e576f9 1f52f97a0f224dae75244213223daead4262a21324f336affcb1186301dd851b dc4d8714ba6fb00db603e63b71095e0c0f48375647d8328bd1f1a98ddbd758b1 52b458e8782e3afb49cf0b4142fdb82de0b9a9b02bf66e396291581a96ac660e cc7a1833b75ca30eb566ec98a16e444022cd0537e9eeca574e5279c72cd402ba 492dc7df7860ada5e7fd7c076a7ac5515934d345ce74fc7f4461b19ee0c95dd1 a393560e00ef2187f5baf6338066076d72d170d8f3f76d53058804ff7ff76be0 77a6d6591d159cc018704b75d4ed14a0040fb8ab7924a6504e31263404ad7c20 d68bc46912d5f41a65b74c8cf7b0f387363e2d02b1cac744cd4bbf48c072e006 13 1dd697c05ceccf65ec1acfb57ed849d8a852cedcfe00ddb20e02f928ea1a90015eb5cef88cc8a88dfdc94707bc2f07d79519fa00cc9e387973116d56346673040d062b5460d2a20ea2e6fbe07372b981736f8ceba7807276b93932ef6b47a20a48b17209e58053afb7e1b4278eaa44900af892e5d163668e339821a0ab22a3076ae9da770c578c56cef6741ab2cc938ab6ef4b2b43f67f432710bc0a04cce60b5358714793a5a1c8ffd54993e6cd4a1a1630d697c82bde9275c0a8acb4e8210a45813e8e3e3984e259ad6f224d8a40e8c476b2f2595adda2a0bae542b11b9309c2a3fd7d38970674d0c38de2614dc651362df4103698aed29ccdcef88338340f62eea7b6ada86f78e7bdd34334230e9ba1e10852a965ea50c9a500aabd575c0cbc3e855dfed20de9c9ea1dedefefbe3790fd88ac21078cb61c919721ec050f094be144e6401b58936c1303cc43a4e3308f1ccf39b44d4a413dc4151afdbf050a0ea5e5aa4e4c3eb6ac9ba32221fb0d945a4e555128d03e591a029b6738d57a0b80518c3b6669b6bcbe3dfb49a7091515e2dc35359ccda1f089cb49ad9d0dc007cf7b8482032bf741b46dc0ca5fd8019cd721450bf5b5981378a39e6fa4a39b031cd4233aa74834bdfa15fdd65d798fe8f31fd13d428cf162c97100181a3d4d048dd9d4477e80d6ed85dce7496a23f1d4ab3fa28ccc9ca29e57552b4d1e49200e243de57989bd4f52d3bd59a00c7f71c090f743d84a16bdb6f673f34c065a3b0433f863405d24ac07a162bd6f7679049bdea8ccdee7930aa0911debec26de4a097b2604683cde6475217c3477615bb2a8d0dd136dad3142b65f5a79e923becd03ed30c2a9fc83b62d3eb7e24fcdeae4a11803a9a295dd4cb17e3b7d5f61781a0175c02031a60fa336e3896d497cedf922c5b93eb9b69f1bbfb7c8b8310773ab02e8f0dd96bfaca269e3e2e494a28a16cd2576b6a94e2d3392cc4e3bc1dc30400fcb46046f54332962e59f82b8dc3064f935626b06fe34b6101be66ca92b846b00dab39822424ca36445e17116067a60bd9bd65d1c1d06c59450e9e7d6028390007e11a696a8b3ed26e85ea873ee849feb11d3c7a794dc348609169ef852f20100402e537262d78915788d6ba8bf39bfac0cb7e8290c055469346eae64cd6a310560f0ea2ea24d6884bc6c7d5f232c13b173682de33a02c90983f3d711334612022c6d4b839e9b7d0a4be62336da93fa2da98571d3526ea5a6947154a33f519b04ee064c737323c1c3bb8eaa6a77e577c5a6d7ec353af451a29fde6f0555d679024e542d0e2e0c6256ef74006a9fdfb36591e357ca051294d38957677c1827030a7085d7c1f272bb302e6ee55b79012289afe9ba505896d234f31b3da41ee7480999c585c425953cb57973af592189e7d38c7112b3a81962294673957bde3c2006b52b9c2030646d43c276a03592e501fa7b590448c3d7fe44ef835c73162eb200795625997643762ae42dc7cec3729e4c32820a1880202c1a9ec439af5f1f880685bf74beeeddcdfc279a1526ccb0b82fa2d12f62e2c5b1338284e93b47be590abf0d73d4725c4933d265f397661ab8249200ce7d01675c6767a0e411ff1b460231242465e5295bd9fcfd5814b2766bacb1bdf8bb5fe5fbf6dda3a7943d925806c1737ce82e4400f4c7412d819a344488756a292cc5a27208197e8ed95e4beb089e627ffc5ae59671affe6f12d0cd62fc4f7888de36f4c22d9cdfbff3620a270357aa906800d87e3a3495deaa701999a652b82aa581651b0f3ad643e3d31a090f30486bf44c7703b78993482e8e0bb99faca2a99d108848711f79b32e37b10307e6c8b2041ec8e07aca9e6084edb35b0e641768fa8acbbf0bf3e20b047fa7ce03842632f0dee74e53bd04f4d2bb6259b97079b5ce04935021c0d79f06fae8c0004d11387b1b2d75cdc301e4c280c77d237219acc0719cd9dae4045d0e8f828306b91af39536d6f850273615b51aca9465e8b65eaa7b4eb05ef11ec9990161a60abed020e9d2770ecba25690a0651728110c2d0c27e3edaa7a1fdfc5415534b503c6439bc24d1c0fb445acf4b4cba99f4ddb40d5dd82657c518fa95a0433f4410c52492f96ffb7aa814ecdf0ec685a66170816217f125a4a1fa696cec1592fb701f8de55cb334e9f78915a1e3dd30bde5c66c9f6fe1435d7aa908e0245e3ba930bfb0f02960cb3cbd3d331c2be7e2dc1314a8bf67898cce0bfa63813522c4b900eac9f8fe14d31f2f71cfc42d5b0bd48ce3c6e8d5662ffc9db50e7ea7ed0b50200525c20ff6e6302585cc56b729891c98cd3dfafbd729f7e6dbd6338ed4a8a810230b8b373d84ee0b7a4d3c28614ac3f934c366a0487ba62c4ac23dd4224707a09ee2e2c66ed53f84b026c17fd2396a3be1be6aa6150c9dcebc8e13c02a4ef700edca35862b5ce285455adec1e581466254ae285c444b39decee060595ab85bc0434051aa473ae5df96a0a1cf114c094365893059039713a22640e872ed944da06b7afdf02b93c04aa84ea90c695774eef32f50b55e6c5ac2c6cefd0a42025d0073724cd5cb89ae42b26ee3ee9c0c3e2a8e7cec2457bcb77aa09d7e3c9593e7c0fd05492f53b24c297d0314a93d9ee73dd7393ff197f455e73f633edafd8d62b0b2324b4295d1b14d288f78d42cd87ade5aa2beaebf9a489c061956e851ee28001abd5bef6f8d865c620d3e29533fc149697c14a5d5d16e7142bf3dc413429680892c5be7ae973c0db15461c0a7eb4c7c6fa93e9eaa9bf635b3dbfa3bad5251a0ee943a16be6e9ca58ae2758c0e73ff6038e515a5a926e7617059e0867c85e89078d2e2ad5bcde287e8c860edbd671ab942778bdbcc1c7921ffc26d337d9af1a0e115f43fbd394b3cbb5915aa47eb3246af5ac1bb52a139b02388feee54eb47004e554bb49b97774c8097fa018a7d56a68f670b35161d2ff5bab6f53bbb009350ce62227e052748a445c4a4fd56d8e69c9a05b8a36bf049eb788ccfeb4e3385901912ec481893a01db0ec5a8a5b4e40a1d6120231e218018f8414a84f1befd8d0bd8de86c55c0ec8bd529dd4d3e7714c9a98c5ca227a337549a04268f00576860c1f75cc3f4e0ff770e84b6840459a884a3dd11a5c4a9f2bbd477dff1fdfb2e6053ec432a735095e59215e32cb897e9f12cca62bf65daf38660ab17dd3985cb900efbc7323d1ccea72299084ec1ce0b0d58a9419be4d65d09bc24a8a2dbb7a4606846b000c73ed19afc1fee71aa3c864cb51434158c696c9c76023b5961e2016041e7f621d17a76e6481cb8bf28ebf04c213c8647dab68667b9d596267215e2e0cc2c40f8101abf8e4b919755f51c8acc58da76fcf3ba38eea4003bac28f366e0360bf5e632a3538ab92e07fcc4f154660e3bdb270dac301f92e519d46c3461d045ba6befc421ab03f907849cc21563e05ece092e26d725c4974157cd55c1abd0f7e03fd550b18f75e96e22c9e159fe8dfd629363caf0ccdd57bf2f6aa93c31d0d8e4946b723824364b5e2898dee78031483954d094688bc0925e486c4b9102203dbbeeb14d648db7e79de18582de6c5c7bcc7a1fd11daa8586f685755a4d1b50acb47e43bf0f7b0a919c7bd4a9b6ac8b1df838dbd556eb327516a04a7276a5d006fc4e5e57eb933a93f7ac57f2143f3a4ecbb01bf894e50917e8cf04523c1470a141fe37ad231f9a074fbdc2adf2699ddb43afbaed1894c27a0e53db3ace41e0fe58676b2ad6198c31c31d4c2c9aee45f3a68d3085eef2e7748ee4dda795a390d4e17ce308d2059433eade2147578cbc92bf877179e62d11feb03448a00daf80e13323c0796bc7ca639c95034ed47310b5bf98fadf5aa628fad189294434fde0e1aa0f413ba0b9408aea01da087f516314d5b65ac164f6242fa44b664c0918405c5134d7e5a1880a358e4912d3d9402f55f2528e1007a2875f74359223b6e490b7cfb31f3b348fd689fc57ef24560f0b20f4aaea47f04994bf5c8cdaba93e9d0f4654a40603a34892e68b0bde87570a04f5d4252701738e1161abaa507b4bd40a7ca706b235a061dfe9caccc6ab7a77f3959d28e70d311a41ffa12c8e0553e208d4a9ec6d841063fef1b6a36d98bab2f3953b4cae4400c32a1dfe79ab97f51209517b630ba3c6e86bf110fd27fe5576fdb17ccd7742c48c2e5652cde4f4242206702e4e864f210442545db89086ed156c9e12e7312b6e6d55124ad65d7ad2910be9cbcf85655bae5c348a3059539ff1e13d69644cd6b2107fa1783ff16fbaf5042da2cd33c52c9737c80187d23b8ff82703becfa82a8436d113e7408e475c430d535079c531c3a632e5b66cffdbbaecaedcebc107b1c0ccc09922bb03ff619f085226648bd8ff368129e0721315cde38c6df25c8bf264faaa7170f9d36e2f8b0c6f2b09d6dc03d1a5ed21b9661d9dcd4070d79217af70769c03c29743929bb809ebdb5c0334231f372e0f241cbb60cab80b763a019cc65f40f466a4cb35b53d016badc7705a8751404ab345f139eccc757e4e226156a07340cfd03b6aacbfb10b362f1e874fa8da5609e479cf975600afcaf36fb8fd6b420d1a9e1da0afd5760b12d7e4a82e8b590e594a0c40b32994c2b2572904126df150a76d0393c2264f09fe8d7e57b1515326e70d09496f7411113b3f8084d224e4b2be5a7b18874e0507feb653c395a5353f1ee55ea1f134aa4c915632de158e6699af6e9b0c366b580e4e4e29f290eae1246dad892c9222b90c414bf12d7596354e55ed2e8f1f5a1502 +generate_ring_signature 1b9c52310cd90de6486e3c8b73ec646b0da895b54b118acff6ecfb69afa6e346 fee5ae2310a6b91390b1f30f2499faff560fb74ebe4b32f965af0edbb140a740 1 8084c2112b5d153f5086f416d6d1c059843bdee8f391f547ba9460225a69cadf 56320a68733e538cca2d2bc41b01634fc3b9e2ae3f9f83635b2574a4245ae00e 0 9e6c9eabf842016b6a3c949da8d02cf32997f4aad8332dd326aa5001ddc0540d5a352ba32fa6ffed3d6859805145d76e3d8631f60ea059fae8e485f29696340b +generate_ring_signature 65e7321376ed6c6696528042a65a9a4f6516d65fcef80214808c0c3230a52087 fcd664a50e9adb47d9ae24dc83f707e5178d3235b84c17e9a6f586c0bbc52a47 1 42fb2300246f28cb82112974815f4c14cdca8750a20b25ef861621effd08c45c 8e82a603216e1835a583482e580e635a34b431012284d709815768785245f800 0 78dc036f67afa2ca950f9474b5be60d764ea9eeafa4b69d1e201ec166d3df3060481f0501f38208d64b94a97599a5fd7dfe76bbfbd6e97496d2a511fef603607 +generate_ring_signature 5fc776eaeb6aa0303db0d4397d093d225c8cc0e00caa55d5e3110e9d7aba1aa7 5745637caf7435d578fb680de1416357dc6c6ef0ef7f00c0d98952cc3eca7d74 13 31b7451326d0a7a224f17225a19e946750d81bda5a2f56ef09fb60bb2172a44e 9665dbe2e581c02838c7406b1c2632ea5898f527c27aee64001f13fb69d4e46b 43522177f2705f00ce314e061859663485afe95dc1103d184dd8445b51f8d943 d087ba1e5c49e3d7f627e402554bf365e115eb049d1f7101bfcd02bbffc09d5a a76ea4f37eacc9214defe54f2d59017a3d410499c3a6c7784c538d480764e2ec 881632bf12f572675667d5b546c761b3448453c771872793058d7e74975d616e af5bd86056c8494bc987c9c38ca25b9479c98749dc1db4a352ddfa119c436afb bc6d378f748e912b350e8716c0965c91fd00798534a25148a9cc8308b0ef1922 bb0e87d6c20206a4237b34b1deeab57891c6750e4d152e28a6477400e4d5b3a8 3556aee9033cd19cac0d3aba95b99694deb7dec82a18509529836cb013f7e656 1b0e39b149d8425fb452857732ef05daab09881647d5e5e648f57650a5fcc5e6 c2a218592130b95abb79673da2204d97970894818e0cd4ed17569c973403aabb 3a3a3c9771727a8adb3029e1022d766357396ac444aa17f75ecd13b7dfc4f25a 20fd014c0ce804e8bc23ced4b22b168bc3c94b8f76b7b0f2366c91e4196e070e 7 93d772e28c853f001c864f4cb3a1c75c1ae34b180bbc3c638ecee1230d8be30777800b59cb4242b9c9fc859e506edd2f26ac3ed2d30c64c73be355e07810280bc7d4a8e3dcb26281dc6e95cfe861c5b138292809a77d0ca1dbffdd2e2066a00f8c7b79688e1d24aae7e18f65740b899b5e3767e27b36926faa7f1321791489077439c6132c8451f169ce7e96d0e05dbc9bd748ec339749478ef8756767fad10a87a271d42874b85c729f12d514e49c9cb26d6ce7c5abb96b3df4c99a1db81c026039b0ae079dec47b1b861c0caaa53ba18f050cc86214af7ebeffbfef8908b047498ceb0cfae3c52f7c5454d337f11322d0a9d261eb65aefff524a165e3cab0885091ae69aee81e0a85cfef61a0b596fbcc5b864c07dc2d7227a6cd95db426021d51e7a8f1c56c9f786a59f2ee768c1285e526c23d30f9f6f087b8115fde8a009cfbf59104be29e9dfdc982ff880f840bd4865aaa5147df8ba3d2739ceeed900a1069774454116bc7f689381a9fcf5aa2c6daca1b743c08ac40f2ec877b9e80ed491321eca693a74d6c5e19fc63f41bd9124891337dac863266b5d2cc629c80883757195377f736388e51c8e2581321112f3792a6d2b4f64af3502127c6e060edd4987b7c916bbb271c9601c08c6a985fcb0a7eaa76ce955e5d3ab06823d7007852984980aea4543ffe64479992f5c5ec145bce52f76245da21bbbbe723f47006d09a7c24f8409876937284b23279e91a3d67e775ba79c887ae150372f00dc073dabdc3d371b60eac924ea4802335b2814ef81fd3f740ecc243da8416cd6320eeb6cbf23ead4ed2d522c94c445c4ee3c1da584b71a7fa7f35151733d847ae80a48ee4f3883629fdebb7347295b7823115a24e70cbaae2879c3eabfbbaecaf50c38f9de0b60bfade3951de997742c4b997759a25951d08ed2b721eb1f4e8a7e091b871cb07485b3861957a21201f9372c5e21f06d0e12b6a0652c3745f5dbec0bda07cf43d0e9c70f40452d340738bbc883c5687bcfdf918840fc3553cc8ecb05c67c944791304f34f04bba487832fd9b04a1d48eb95320b820bea53d0bf5a50f624365a429a6e79aacb5055c298cfe4db2c23a0be3be9ac39010d71eb2cb3d0ce3db316df6852cfcffabe94172f31e513e25092d55b944f5535040080e6e920f +generate_ring_signature 1b670fa7fab988e6f8fdc8fb107b3d075faac101675f315f48f069d363482811 311bd02641a786377befcf7f879163d21693f0eb1bcb162e016f16961adf8cc1 6 81b31e3da8c61e445c7f342caa50aa1f14e957cbbca3d541c909dc5040436732 bd9772f8dcb21506b557824bb497ef50079ea93d74b936ae6ee40fa1ebc5e261 7a31c4a1f85d63b66d38c7656d27611f99b933c8b97c1181376acb149dbc2446 763dab71cdc26c00204f5d24ca3dc0bbf122401d0bab86246774d04adea8d2ef 19d20452c993dc154c00d764ea28ca9c18f92df93067a40b90cd98cf6055f2a2 0765b3b2cb35ad43f4b64d7da4ea43c89d080c201bdb45d5b546f1ede6eafe4b 8ac480f5d3827bccb2bcf68e45cb0ca7cf3e9c876d80405a7c455b22bf68b70a 0 57b4559598c54ee4843fdc277c20f1d0bd86b0193c87b2efcaf1afa73133e808b7ed6ce329b8cc4103501581290d9f2949fe5c7a5db53bdcf4d020a34a8680016b31e9ad695b778bf14f5ae3043da1d9d3901b798025b5e62c592decebdb6f044adf0117bbb960dd7ddcf9a5d4c0bbe338139c17bcc5c13974485487e6ebef0cd32c9f153acf7be0668b03ade1e3020b7cbacfb2738893731ce73f9dec630600143c099f919b16be1758eb553ad9c780c8290e2fe1ec44d2fa6a2d7758d8fc037ebd2dad517cac1f698052697e10abdf35948b0d23332f7c264f9e55f2903a0385f3b8a38b40be2f0e7a4bda96f81f60bcc82ae6884b28f01218dfa6e450b2062fbc622d4bd472aab4ff6c8da0c254370aaad03a0852fa5a2dfe58c31a0fd400aca0e93c7dc9150fe5ea164b773b5e5c1df27fb9d72b1beb82f1d11f0fea580bd6273c34033b08a16e927542941576198f359d8f0e5fde177bd1a6ccf4ec9e0492b56a484be513bf4f73390537dbce0dbd6d7e07c68c128376406b636e61ae08 +generate_ring_signature 89102300bdccd553ce352ef36bd8cfbdf13f099c02bd18596e3d42e6239bd3d2 66b67ac092566c2a368b9a8d9ca4c4cb6a98f299e5f410c662017950512d6031 116 aacf971f4cb8083e4592930c84c2f7e1e814359c56cf8595064261e3a57062d0 4286968d242a26dfb7144780c2399aa1b000e5c64a7fa136663d9e9129974657 914d0875d0b0ebb1a309ba776dfe0a0e0c3ccb37d57da02a0adff77911fa6cb1 25960a259e5be8f63b901767d00eaff9e3b5b920f6080b957913c065036357b9 f42f1612df591adbbd3075dd4d7e13f632262a2aa4692fb66a9b23f929520ecb 2cc95b8fccad32778dc53e534511749bb98138621eba4b49df2ee579c12970f8 b613d3379b8bc47e960b43003a77dee82913090ef169ec2c24bc1e2e849c6ac8 a2fdff8411483e8012060880b6b2ec460ab8c4092dc88b48bd2111593221a425 3991843e89714483490b27ae31f275df2dbe7e29b0adefd7cd2a985edc5e69e1 b04e1160aef718f8095f6d74b592429a04998db7e3235af0805e2393ab257f47 f56c8839ad45f551557e2f0207ef083f0c5ab8afd19a093106f079304bf1fd8c babf3b67c8a305bb3fb26c0a0cbdbf11bb96df24e36903e5d7931d8acd8a4def e08078fa1b5592ea810f70aa9ec2b5af155d158cfb72a4339b06953d203a98f6 24de47d9b5fb0f83502c076360a5f84ff10fae7c178dcb6899f0e54d04c6f744 bf656a168108aac05e7489c23b7d3aba492ddad0a7305b7a235cda9b5f28d62c 20df1335f52c54cd9a273021327eb3cde3927a2a1c589c0b8fd2bf5305542255 6932a7468388e9361a00b61444b2fb0eaf68d975c554ffe92d301fa54277a9e3 c6878450d4032a0a632133777ba8904cbb2e6034d05d5ccfe183d9189403d913 27780f528549913bcb7d6bcb96e14c6cfacdd4a229135dc06d7c5102219f0110 92a1e11e5d0a1e83ef1aa41bd739c136fe4d3b2585c0d8ad15061d93de77d704 7ca5bca6af948f20929a81e95d75faedc991ba2faccdc3f2d942265d0e60beec 7d0299200ec4b03843d3c9c78d33d9cb871902c2da6e871f5328e1330038991c 95250393ccfb12b976d783e295fe5a0986370b05c48e564c2aa5d5c3e3d49a05 81ca61171b3f4f312a2460fe2f3a9a3d70a4c648a1549ff47ac4f621ec00e8d8 8eb43535fe2ed47605beb83118cde781c3699ec00473a56ed1ebb72bf177b356 03e96f6ec4eea800c6f3ef8b2c43f31160570d383285ecb1738a481cd3d5c335 209433cb2ab66891b1f9dd9fb74a75dcdac2639611f72890bc4c2a90ee0ce990 1c64633169c011513bc0a3c23dbab9ebde0d84ec021815b5efd749122a316d39 aeb62e7eeca2e3d46381c5411a47372bd9ed18db465d99a055a3b47c8be3aa1b 8253bd9c2ed9610658e5e339f2531f19fd147d0782ef9f60917e9d06babb7e68 2728a4782111a52f676bee5c3baa8bfb5c346b560510f38921cad69f9de907ba 12d57f3b4d36edf9656e67cff6dbe2d6bae85ba9a4365d04c066cb9b85eb4c11 e458bfe87a0561429dab19d989c37b09726656c8a399d609559db03680c946d8 e273d0effd007e930b1e9e130b4f87497188c4ed48fedd3bc34d169f83735576 31b09e291aa05bcfd743b3da58eaa02c772b817aff414d469a1f668f41ca908a 2309b29e52ccaec650074476470410a53fe1091621881ed61e30240b7d8e5ad6 f26e0a3ae0c0b5d5c53b4fde3a017c0dfb1cc1e67c7e9201c484256c02fbf584 b2369e479b65aa0e85c6d61e312733724eb93cb2c5b34b088adfca7f61c939c2 bb0e268150f215a3fd664363b2ffda9c7f8cea4c47c1efc43a9118f09bb66cbb 71b08cb319616d98b8ecb176a8d109ca5adfc90e7d88838cbf6a92de3e8c7ecf b2e5781e43a4b2c32d0acd5c373cab979b290928728e9ba476791c4dbdd9be32 fa305e51d4f7c53dda4689c34856a691ae3d6a9d01ee043f2106b4bc3787dda4 fe970cb89108b4e704e492c8db64cacaf564956bb1c47610ee25245a842fb210 336aec71dbefc4e4e44fb1e19239080446d9299b0149e1fa171c48d601a41c8e 554b235718cac2e9355720d4acc8838de199b59ace20cd1ffd575f0f4631841d d3ff0d68b3ff9683e5bfecdb8e8377de4ec37ecb155cf39c4e63dcf94b1ee979 f2521d0a0e9836e48760f0dd9910f229175740e2c81ecdd62e3859a0f4e0c7ff 66b07816d226538b578bb1df23e408d754a8a07f3ca5b6374ec386c5134d2697 ca59cba4793221d8430c41eb4ef32de270d0c89eca0b858b420a57696eb30be5 c3c213d1c7effc9e43a51d247f0fda635055491dc26a23201ea60758a37feac7 cb0f2314b2ad60b4230957199a9de58698da7811f47847ca973102a66f0d9498 c928cf24971c0890606fdb0b56f17b9409595764e7e398a2e729ee7838b0de64 b189e86e14c4ca5ae8b4cb715feaac84b921f3bed879deac20c9b27799176d88 65e86d5133b1d43edb8213bd3bfa7babc6e23628e52e36cdec478d39759cd6a7 3407636c7c04d71490e9edb5839531aa4724402b3c663ba22857af6daf6c9353 3445879109faedda39c091848b3b49cdbf61ca6e95ffdde56bcfa28da6d9d27d de4c9c94c1a01d5d177b47355118f70a68e7cea90d162a4ab67d7f8b8be582c4 cd847f96764801152575a86b30e6ed3a75acedbfdb16a5576d3a094342f599bf 6890ecec3024edd3be7e67817ce51b8de27b7a4feb288d8c4aaf6c91e5e909ad 66e74b36d7939f347f0acd6699214d4a78a016d9c2ca512a6f6112fc4be6bf91 a582a1c2948b238fc1de90feb779b9d11e57b06410ef58e74f565b6e3dec4fd2 1d58c0007cbd13072dc8aca24abb0b6cccf238b47c897a1010a300cae267c021 a309f20016a70430700d55c29521f51dcbb9491ec7a7be3666a0183f7b5cc032 23c15ac582b1d2443eb5b4e1aab30a44f108491057a87064a1784eb64023e96b 49796b6bfbbcc45aee369a4cd875e73b43fc6e108ee97d813dbb82f4227cd821 008de8724f40b50fb90cc68e804d395de075bf3f40888741c98fcf0df2cff0a5 c974385a5536e2e7018885cf4efe028760084ba063279d848293ebf7cfb5cd90 827792ebb3a8a1283d94fb063877561a4e92c709e9541b7c87817b28ed185696 2fe3aa80686718b9739256c1a433a37109c903f0be3abb74819cabf35e0c1bb7 e93816be20d1dcdf6ee39748a1f08e06f8f39490991d3058abbda941329c3be8 ee1b391beb2319d8f67e7376e68c069c0011ccf712f5351a82c13552cc890047 eda56aa82c4e114c8aca9636db3b356e7ad9c76c63acfd1ee135d3a2ec3393ac 0bf5483e4bdcf85336f82b77f70b1623d201ff601877bbec4b7b4963268ab62e cd4567b648634eb4ac39958e55ef352570122929639894f31abc61adc2609fdd 0ad0e927718cf2b4eca2e50e0d27d99c75cb7cfd33fd63b7fa7ae38387d92e12 6735f7d46c51519a4a2d475899fcae2f47e08c91a08d28faf81ae21611548f64 210e26da394508be17607c6d23d08e6d93ad5009f0677228c754d212a8e084ec 70bf02ec4ffebbce2020b5db776c6eb463b24989f1f3f8aa2e1bca91f2a4e315 498b3093b6563f383502e489fde1cc1201acdfd6fe776bfd68f674c4e4a2e8a6 8e3a7e23a5cb28de1bd8e28ba5d87a1f1f234fa405dfb5f9206dcc1df31378e7 9dc0a522c312cc2a6c95d954ded0023fa95e049848cbd06cfa85798b91d9b96a e84f3f5612a23c4399389dfd2ed6972e6744b860a8f110ea83ebd77a3a7f632a 670b5b7c96794bf0300575edadeb40dc40f30c52ab560f55a8f7219c2c7be945 b97fdaf538c771ef870edac92c52b266eddf2f41a662c3b812483423f1172f32 34fcb11256ef3dc85e4100bdcecd84e89b64d9c39da22b7f7383ff1b172f0327 0d735c44c9c7c5502d4b4b280fba770b284e2eae7f9446d0964bb6ff7b4e2f92 08f03cdfdeb880a685504b775045779c86135aa6dcfc8e06899bda0391141946 865c08b9ca9748b72018189051fb1282dcd1662aef708c62593dce09981a45fe 647f241c0da6d5e897f96f709e4c5e0ee7db1a90026e297b2b98739662599656 b424e2d17bad69bee7e51c8c8c017a3689b4cdf52cd016af5e65e7084b3c380c 671455db63d8773c5e95e71d37c3bf7d3e6316a39cb2fe7d234899011458c83e 28119b556f4048b9b31e7946073be2b62f74b74a56cbd31fee1cfbc2f5f512a5 a8e89c237d06199bcd94c030f5c58ad1a3caa2ff5bda6d45de213262d038cd03 4d6ae40c4e28276d908f10fac7db8fb0954d0b09f3d516cb61bd9bf9b5df9fe1 7fccc1bfba50e840563c220d2520c9f77afef38d9dfab6e01892e6345ea100e2 28e363ec69911e6cd79ef7ec1c6899a531ca6aa1b00d00f68d71cfc038043847 21dc113dbc94fad84468f32b6b654976e9e2b7ba9caa011fc46394cc0efffab4 513c7e81377e13bd283918a5a44a5ea8bb253f54d554fa760e04523c27978471 f121a815f969fc0597b1b5c518cd49ba4589394fc511633cfeaf0554a33e5bf6 19b4883add93f293a640f138d0599d8abd50027f605f68e78e398568ea444647 ff453b1975ed16f8c0e1411dfde72046542032a9e753b4cf9bdce4cff141b734 4d39889a38efe6d22a0d3569d79b2d5d27a2caa9d40f8d4232db0ceae78c4980 471dd89be76b7673c0948e9dee567ba70acf98d02ef82b8ebcb73dba23a25aed 21a96e9e7fed5e8301c71774469297fa1d0e095ba317b916501070d18be35eff 1529c4c48b69e33c78314005df30b4e24f25341538b4e4784187f8492a95ca4c dd5756c71342bc91146a6d89dd9a1755b2e67297dd08ee2ad47da6f88712cfde e223d3cf9f77cb85b9443da601d925305edea5d1f86b69db533edc032dec4132 9df21b3f335c553a66f4c6b1dd7ac870dc885de1b3323e86dffb116d3012a430 c545e90cde0ea37de654dc7f6cab3042b9c53ebb923580740bad667891f34556 cf75bf6c505a45d0bb8b966e4fd8fcdb60caf6998310f13ef23d48e9f8da5f2c daa6ca75877e1e7eb9c9a1aa990f850d3748ed84dc65be4ef4509ac2dc15a50a 93d1f3b4a03ece48c32a6e8f2ca589953ea4eed5c53798a388e51f9a6a5b9e57 0e41584b69979e002262d1cca78e9ee773e22a4458ebaa53de795a9885051fae 4aba7f5eb551ee6c25d9290c10ec73cc2d1fbb0fb3646f2c3d2959e620d6543f 6e0fea29e401d70f40f8be4d865f7db6add2fab1ab82090f3b85e53bdf9a6cb9 743899b36c46807eae625a8fcb717ae77e7119dd436a6cfc11a7876a1201309e f8fc487e376e13bba04de3825648ddfe435e78b4bfebb43e5b0d40a33859a500 6 843bee3021f19fada8791ec911e9c879fec2ee6eb8903db50c2b9a71ae18b80fd5cc566d9493ab01078495f8163fedc996a4a13c7f1170436e14b1d914a76c04e0082e8b1ee2f2b3cdd86e8e3e0f68c123cc8c04bd517a25e80a681d9a75bb05b74eaa1b946d1b5af93d42bd4a1ab49f08d33a1bdf0516bf110a1f8a064fa507122c188f2500651d5f94b9d44b449d12303442153b042a4769ee3dd90b01580ee02c0e5036db23d70bebd4145f739da4a8c6b9b9682ecd36eb591e18947d00096f41de0c51e66430a23bfdb67378503c6a7b16e5b491efc4296254070b099d0039114200b61fcd7d070aa2f756fcd1d6674ebbeca19b7f04627afe036012a80949e6c9387941d518081094b9fd1cabea95e53289b62025e801c5a2cd1148160ca04c483d4d8a61ebf93dd9aae43ff8ad33c344a983f80babbbd2dab1a6644a0d54610f7a546840185ec5493490da7142788a19d6c4286fd32d3566e12166600fead97175768f8b81fb354f20e923fb92ab563d3b67e238b8c04cacaca8323b0e3047a1ccbce80e2dece176a279877c654f6d3640697f610622c8cb0741ff6f0b5eb0c327fc36df752ac73c1d037b197eef9d2df6b875c866763491d9977deb0d73b68b7f5b71e13ad41b5e9e8463da272b5363679616231b7c3200b21c09c4098a3e241e35c65a15fd6bd2cceb3e82e69fe4780b3c963af318c600e020cb9801b9277df3df1cb89ff13a6e51698ec34b6b968bce214fb26f26f9a1a05ed04801e822587da6d43312701ca6e950ec74a4a47404afd14b89834834a752afa50206128fd543095165f46e2fc25e94bf5ff6b7b8355e9e4788f26eef4dad0569e70e0099fa6f66143faad21af9abd709a65cfd00893296d54081c055078e8b6bb70dd791e00d92dca8073a4e6ebac2f3a663aff43ef9c1823cf2844b0173079fae0cde03ca32be734cf8c5f908517ee0473a548d7a7fa2e3f464d41910480dea390232942a44c00590373277eb80b03a6773331770e824e1fdd26d307003f9d3f008ca4bbd93084017ddf51053611ea8cfd4bec3c809e34481bfc835ad9ac1daeb0a1174640168b4dcfefdc6408f46a94add4275a18e3ba410fd5777b81d8bc1a808ffdc6b6a82680a0b684aee0d7b2814381d9e0f281f6f6d19ba5f22cda814930ffa0f447be2f7ff2cbee3c71a41726484b8816376c192e6d8818803c6633f580a4c31b11db80507911137920be7bc1e8e94a5ef5211c3dbcef02c17d6e0622204458e4489b6278c9bf4e6dc37f5b17ba5afa092b3fcff6f39794b8a5aeac3a0000687f690a5662ac5ce6cf563ac7602ad558c35e8618e2d0eb2ff8f2f5ad508083cc274de7ac5c1661f5865bc14316700d1bcda79622a6c521dcfdba2152f8f0f79c73e27aa5ba0a352b1899ce188868cfd959ba6cde3f1db5a82c2c61bec7e0e46b3bbc7ae294395187a3f6ab65fb646be3595609ae8190cd82af93602848307db1cfac6900d65e82ca93f66e0222bd254e017d1f23022f76b22dbc696fc0f0c699aebf95bfb2876072221a838242efa4c41e47f8cdb231b7b6a4aaf62b3fc0ceec422bcc71e68b4085de03bdd36f73bad8a715b975cd6ed87394a667552750f4a7c45065ff1da6e221006fb6176879e2e6cccf4da34abc5741f0e9c9bf7530b1a3ca21a5c8c7420712cdb0ed3336fd4cb70620e2037bcdfb3193d6feb4e070fc72c6314917a1c3a051781fd5c138706d0de066f959e03c3fdb32e84ba42cb00d1f4d50efbd76bfb78eb50017674e3fdb2536f7fe1d86cf79fabdcd9ea1c700b4438eec37af8005d7f22cebd13b496a0d90eadfbd4943b0d120b4fdf9e890d0aaf9dc3ca85c63c7fdf9a8463984f0a6411324214b7c889cad2f618f537a685095cfbd0666b116c22798bfe2b2ceb0f247de8756c3bd5f7ab17c158658b10cb09131f92300b295fab075796927244d23f1f04db1da1ed711d4e745d5bfbcb9a02ce1a22bf3002a1a4cab1dbcd49b606e40c53a91aeaf6bedf01fd53f34b31680fe11f46a5cfbb0996ea26efb4aeff5a8c4befc90a3e76edcfe4c5eb3cf7949e0b9a1ed381c8e6d0b844a7cd3d0566c43d3c87d1f0284833466d9d49c6d8cd9a067d2b3565141cd527de923ef38bfd5699d7f2366f44f809a40461678f1ed2cc0d4dae2bd409836c3620755efcad2ecdae3b87de6be8014352c9e8d989c66b3906165fecba7b494f11e40686c2d46a28602b3909990f2cb38534e37da6499db901a6e2489705eafc7aabe77a263b0126e36a288437d6b744777c9efb7c1814140944de7893ce7b02a50bbd9c3dacbd4d3a832a1849f4f690ef437ca32751b5280880e85f355e0e3a7e6fee12d74f3aaabbe3d800f76c7a99aee977da97c8168b07b34272b7fc1ecc8711a600a012ffd2747d1868e85e312c5fe46533e28ba6a60b61b094b1501cc2d7c873153b1b689f091eb471f6c7053d1ef0c14c61c50957085ab26191f2e43b6a3bd81b94778fe73a457ef7a092da38923cb7bc87f1ffe405fc1d3d74ec288436d39a11b7a937079d8802d53ee484de06071a3bf755ec22040fb335cbd138f7973b8eb5124f4f4408f7bf7061e728bded1a9dfff4c36c18036d3d7a2b8d3dd560aeb72c65eb5a180a04c3218fc3a9b25cacdd0bafb2e3d00bf7e9b7136c517c6da294ca2dc0fdd27ea0bb8c44a79cc72ee009cdfa9c80c307c3fdaf30c70efd881aa74f3ecb22edc67c63ac557491f5d4d037a14a17390f0df51b9cbd46cf4ba88d64edde5b0d31e781e270458dcdeb2586594c1a441c1b0f3d6e9fabb7d8098e2e576a4095d3d233512dc5a33bae8ced3c4b602dded84b012e5cd5a7d801c5a2248173380cb6d2044330f1b833397fe85a65ba0815d44f0bc5dcf3740089f0c2fe6c52ace47c649021404f123ca9db6a31c3eea5e1145607161e8eb5c7324c58061de7593e9cfc9cd82647147c9350cc9336e811df37760780e0ae17b711e8f9ad8ea550346520f8f5613e5393ce996044b61db4d467170e90d8ec6616021d40955aa6526a9159d1004f33ea9e480e3c1f70766a174fee08d71c7231079b69bfb86cd32eaf18d1e166e70e9ffe92f299cfd29625fb3c20077c0bb0a04e0164c0c442a95429964d062dda87f103805d71f273fbd98093a905f3bfd665c411530479d358d573ef0a60367ea9a39f3b14c478c6a6a3fa36760d50626f2f9535e6e341e26d1f5ccc0a69c7acffbd1d902c958b2bfe234002ca0fdd4ae97065ac8127445893f047b1e8b8ed1587e4380b504b6b7c3767552fd601b4dfada2756e35f729f0cdd6464e2a3b55eaf2f617bd1f2d22cf7b4c37588a02da8c071cd2d2def07efd3dff48f3ff417257899a74d6c4cdc9c77c1e0eca350f8d5678f02da637d26293afe30b11787abfcec61e305fe0f862642ec4f58aeb0bc5804070564dacde4cfa88fa549520d6357738130732ce6e36e40c036079e00138d7e752a8f849a9130a10f4995ddd5d6b56ecfb598964fc8b933f68a3d650046700e54281dc187872ec982a30f3206b1af2bb2d079ffb6db148e33da687660e7648bb70253c3e6ba1cfecfd4e7f803d70e76a8b68ad846434f043546e237809d2bf48116abba1d2948aec018e1178679133c4279776f7f23f980a2916a3bf0c06339e395be2bf4d55ce3dee813d12836183ccb9c4c15d3581187ac8d7a60d06d3365526e825344e7a6bcd490306d9c2c9bccdce0c64f65fb6ae948acd3b0e05fac2290d8a9ac6cf8efc42a45fb308815386c28afb146bbf4bbc95c1786bc30a26b70c634f23c8d67bda12b0a169653a89050928f9a9388264db675375cb2a0c5c047b9d0ee8f1d270d153276b591212daf6744ca9492d4ab4680ea4ffa9420851125ecc22e5f22741d1fd6fb438d0a54a2e2ab3180f12ea437e1bf339f35108ab89c2192d4d093374e5589759e21fa7155a88ebc1a45a49f780287fb31c3c0da1467c0aaf63817b7d9c8c3e9f8c4e913821cdf616b4b467c11cdaa86ad9a70fbb1e59c16bcabdd8cfb26e842c576dded2cd86dfe983e4ecfc5a09838227430c42faf2da6aff8095579656ddd5c073ffb6645ae2df3e20d93de1fb7ca21d5002082f4b0085237d42d51c484a704d3fbadc1c8b413440ae771685c97102f9bd0f548b5a270a5822ab2d23aafe270831d3e2717a1a6ed4d73b7559ec497532c700ff1f48e27ea86b838e5b73268f91e9d8321361f85adab7e907082f806f704503b2620155cdee7f98951233db488799372fad0fcdc3e14506c9b9ff085961f70c2db280b6fdfca72f4700df1a92f301d98fb9ccda81dd1cfce1c8950a790c8b0fae6ecf403b5f9d3499038c8277a11d05f58fa3a071345ad978ab75069ab6ae092d0e65398f8eb45299d33d282a91e7e5c34c849883552762352fe3a0017c340bc5b8ca7cd9b6773e35e38c6dc65efa4ced0ec4153b9e12985f8df1d6ec76f10f6a40b42669386ddaa15e6fabb47a5ef982f78a0cf243f0b26172cc5675cc7a05a9ac88d74ed7f3dbc34992b20ded541ab339b9fefd97f368003af8845316090f0ffa5e4656d7e3c789fce2138b683de1e7a10fc885b438d8d5911b70e2126a0793a3b950996b551e835a5f097bc5553c0fdf6ae6fa138257c490173b4004120fc534d58bfbb22293767d56e8bc866dcbaccda0fe473882f8cbff6ac34d2d9507de095f0e422dee20c57b104d3b08459377d37a2df822e21e64503f5b0b037f038f6ea6095e8f0ec3e33fd424c852ccd5775c822bb3992f16ab58a14674c50608a0d65154ff4edfd430fdac3c695adff068a0e4bad77ae75690e5636cc678cc074a3514acc4f135e7681c47cde9057e883c8d8f874703fa0033aeced1e9d7cf0dd9d844dfb47526eb72177eefa470d3b9547a1ab3afe41dd8a346bf3c28bbc20d0c317e0c0ddf9701a099cedbc245867ce2fd113726535a0552a5b88d5020650fc3bb6b7c835267e8225eb651b7fc81489b6da3dfa5286ef2727e7dee0ba4f608f4a0bd0900ab7d083b091810d761b9e632b8840fdfe4864a3d5e14b1869bc10c8200b248731ff36ef6478a91595246769f628b96a919b82c6b7c394056d87701f62bf204e948a8bd693d22aae3645a02727d950ddb752bb86f2acfe48ffbc407eb3315311243c8e54835c4a8019318748c61d64b3c6ddd62c6a1a0fcd9ff5f0ede06443e60856efefdabf99a6a1ac5638d69fb8395aa9e5457815b8191ef050017b050fe1288526e735b94d1934c18b3d9f7156a45d073d7cfcd58aff8e69c0afef5da22bbd78edc19cfd9e423386fde0eb62bad363cf2e27247654abe58040dc7a5d4faf95bc31ff5b54ae47ac079ac916f90bc47ad6c542edcaca72d86960cef4522713924447330672851f0e135c571b52eddd4fc327ab4599037dd7f0a05c6c58ec3e72f53556311265d817c769e655130155c6d583190333e0587929d07aeaaea316b3398679227068022b1720b327d4d0aea82985039ed20389d5b2901f2ae0c48d2832a9f516cbf49e53709d28e7237e148f788489b91daf4df267708c34d148683e551aa5af15bc23646f83a84d145ddf210d10373d0abce5d2b92081ca97b46a7276275c689ebec0972f557f802b31360ca7560511a55e1734430075532c6f8461db6b3ec444ded07fd430d20c0bd1aed791ca02c9a251289fdf60e0b67254803ee39204629265014fc6253c2db7a3d0a6c4acfb143dd8338f94507276760e484a3eff1bfda5572f7fd1c75da51f7504888853eccb1035a31e1a502ec4d65507121e736f6b49b523fa3c96b74bbed12cc01820e3147e025b3b96a01f4e3faed1810628b9296f127bac4994c0f7bc106a3a12779e40a8fd8ff31df0e072af4413d10941c8126d8c3fa0e927f60dd4377463fd434ef3896b23695de00a7011ec98f709db4e46e9502279f2474e548af10481a6411b730255d81909e08d17137beb1f1f93628afe1ad77ea7ee3184aed0078771629797403d6e9b51e0fd8102628c57d5b2474ff412e65cf0b6ce2d2f10177e1da21fae8c3123b4fc90099db6c53d78d5f5ca64bb6d171f9e6f59ab5857f97febc3bb13040a5b4cf3809dfe40327694e5b170720fd08ab091013b4645bee213f42e07fa7e95214631a08c186096778d2c6df8173915ef1bbe39437d6073e4409524fcbf5045ee260ec0d603a44976af15955995f75a773229bdd7889a5aa0184f292bd6b2ffe2bb86407a066477364b553440ac6cf9f7fe4636b48ca50e89d0d8891ef754134d7624c0db30d2a500c99277d90089542cc5058edc224f06b87af48853766926c03ef2206bf654cf9c3e0f6c74c42fd047c1c025a2709524087259ee25306bb861316cd08f77ecdb7a6b5434e32999aa66def3e980ed0729fca7743f95239d3e6eb0a260a83aacd68aa70217780e0cdfb64463cb38de519836613b8f6d6996402c45930041cdebe5e9af616c368e9f10a036c8ddaf3b6fc9d3fed325b52a59fdd5b83f80b0585841d04323b6b82186d988362ba8ed8c3c51e7da1aa4bbe58905b256c3a0d831fa6bfbd3623cb409fc9358e611013e7298607a0c4c9a9bd83fb4575c72c0f04fa15b33e5b64536294982e0e20b8d36a06605bd040de744171af0f020e2f0815f9e6c52c2345c8966b93324d503e40466335b79beb83d862b30e4e3e15370f29709581eb06f19d8def04d6b3c123a84a5cccd9246d683f157bea5f25f7e00e9b4465540565c021401ca6346c7452c106f4cb5101a0c8d92889bcf6ea18fa00d5a9eaa390fe15432dad58e0257a97c766446b1dcad9e224f70ef82b42dea309fc8c558fc5ee33829a3e671d023f144ce98220c7df65d490cfdb9152fb85af01a06310775435362854b0d8fe5d9c930bee46e91aa8df6efd91c9f8412e3c31011668a872c614ab34cf519a3a44e3945f5e693f83a0308417e3c35522a2f5290711c748c3d9236a119f322b8f70bab552d3725ee62ac7763ad19b1d111dd8610cb18a28ad3aa5086e98fd9c8417ae8000f5c30cf6ac2d1c2578cdc8f9363a760d63dec8b0e97e5a8e65405d1bbf17c11cf8dd86ffafef84511d61468c7d1d340527ed234c4f98f170d045aa30463d9425cf76b602d8480d27c394a5a3fc6ef902eabfef77815e10843d3e5fcdb5b4c68c9f5ffdc113cfd0fc47e7a80fbf91d90550d696cc326233f97a8e460f75dc229c0e962ea75b4183c75527d8d46ae6fb004cc12bb4cfcc6752c7ca98a006be5d5a440fa4451f3adae49de7e32cf495f9001ff1c785b3823f3689116cfeb584c3ac1ec8395dfe95aa695040411244e58801af3e7b94e372a2cd01edc5646267f7721a41834c7e67440819b5e9413f43010a5b07229c103aa6e71b18dcad9c92cd1793c72a9cf2f3217e53374cf48b7d820d511d417e577caac31677c3160a4f8001d30a28ff0c8fb9a92adfe3290409c7071f56f6d92e6b36b0303543a68c1170fec87d22dc517e2403f566b1df590bed0b87bb1fd985dc5596801a864f2b6da927d4bc47bc6fe74852825b2fb3f274e60c65d59eb1981502682ed9e3f6b66578a32ab8900e560aebb1bf9e5d2957a22a0f90106fd77e4a783f3657c3d4f312127a38100f49ee819ba3a5d1e621fcf9b80d8913f24f65fb90264f400302731b2eb959ec0e5530fc5a2eba157bc268516e0af351cca919bd51fb5327dffd52acf8e9e46711eb6fd0852b9bc6c5cb3350c50df4fd8cf11717ed66437aee867eac3179b071faa1c3278b1c350b0ed2d3fc8007e61d0c0d3e0507cca71e0c9adf0fd456b0646254e42c8143affcd4d630cf3b0f9611dce910d92189d4e0535bfeae31f2c2c41d376552eb6b1867cc701df9d10afb27eb66905421fffa2bdf2fea125ac0f132128a6856dacd2b450140b2f4a10b619ac214a953b719dc182035e2f2763b6caa4d0c85f924325c25258ceb936a09e58b7f8401cfc6b1b4359f4a01ebf03f683609cfef9e93a547ef3c76a56a240bb5b41b235b44d863695179091aad432fbab349e758e1456db4b195fa95deb20c3c6ba094317e9828c5d229b1f878899bb37238a35034902677efa451280f7709f8e08099470dbb55db6a56db92d0cd6a110465017d3d27c3d79f2617b0b5b60731682ad7801a6c3bca9c4942abff0242eb88df56e904bb820c6825158154900d4a6d2161c999870d541e59b5f21016b3c451efeb418c152aa3eb0edbd8205e0d4abef8f516c96fed0f6ca15ad47e1cba9043ebf98c104e6c28b1157eb99e260642d7372cbe316270b007f694e1fadbdb8d84d81082779be9e103de2b5c52a00c6c4ddd69be693b2fa5a70363325953799feb1a9cb94e03e2a0a506f4c0ce5a059aa16b454ab7bf602bf46d34b69ca3fcb1fc0ac9486a3914374120189b183407b99274011778613c4140d08096f03d8b8e161562f98e2ecbe36a1acca78f240703e7390ffbd248fb7cecdfa77cf4328e17412f6351342a3dce3b4814affe7307bdf318d457f330e83f3e7c4b3170cee1a14d9568405d71aa491fa763efa9570946bfbcebf5f5cf641d35124e05f2e61ef020cf59bc5786476561b409bb6dca0d97e051c0fe964e2734b6b4ba9cb92a5b78b186d4e17fe15c07b3e3ddfe4c5b026dd745862425cc1eecaa313a5bb29cf3e3733fde098dda9d18adde0eecd4b30373c3b908c685d7a56420fdd6fda2801be0b92d1a66139002ccd5ce7e63c7b80b745787494d576c732196e4a1473423a75465c8f85a1519711f87b796700be805e962167b4684336656e266d39c115ca2a7a0b768eaad2f1ed4598622a53f230df49b0460d8f5670377e01955cc805e1ed4b17f0d1f2f138bdfa8e4cd732517018b4e5ea984d85493b71223b52d01c9b0525fe69f92f38fbc1f3d6414b3066c079995ea42670a44c2ba4ec7a8ef8dd8a86053aad07fc94614b8656d347a49490bcfccc471878c894f758dbe4620a523ebdeb45d5a0d5ee4a6bd9d1c197197cb0a48d85bea6093cb5c7bc0c42b39342fce57e676638c52f64fd37016bf0e6e590f871d04072d18d8cec1f4e05e8018e7be7f4da767cd1bab7120d002ea6ac98c0f3d6cfe479877bb43cb50e94fe65ec59d33ecf5b2ba71744b94ac4143abbc2704bf0fac99b40fc525d81e943f06faaf1fec84d13da278c18bd43397490c076d050153778e2cb166009ec9cc1ebf954dd930c00815e87f4cee7f969dfcb1d94a0f7b378b94e35ca7c7a31ef10d4b5aa83192ab6ad240edefcb18e13a5c94bc7e006d8a66bfc1129d90b0378ff62b564ea62d26d92c1f14775e44e4f453c4d398065af33ce4b4c0a00b4b64665f91a7d7d84e361c284bb9cd274e85a306b9cde304c0c0393d8da71d7abbf2fde949934468a1425f19b99adabfebe1287d6ff9dc011b6824fce6974b2fe60cc84499328134d386508c92644e63b51405aced3ae20871f7aff4a88951f947517b69e1f0f465f78e8bce9379d90b90a3e2cad4bcbe0eaf67b513a582dacb284ef738384f41246d5938d75d4ee9a39719121a1e56720a0df68a0be66ac9c36e9837f0675ad1030727ca886fb510409ab1ce2e6e0ac208ea0de4d44eea670280248aafd93224a89926170760211f38c13edbf1e41b3a044b7f59d32468dd82865819245f9682df76bc82c44ebb8f451d23183f23e281033bd7f336311f658b9f27fa898249640cca89ad18aaa2eca5584dad9f4ba25005e1a995bb84d74ead317570faa7959b89cf0f130d96b34564d74bd3814f33c50061c30aa0a4d723189b726e89b7eb76aae0f069e4f0bdc9d6d0220a614f6fa100677f62769d2d2f5d9049d333559637a920e92f22e1e64c2b0491c7c3257fca05f68b640abf3a99c2c0462c66c5e29fdfe1811067dc2e786c07934a736ccd540f955a8ff46f4266bc634e2eb74a5f56078e85065fed455eb9a0a6624456a3d70dbb37d3238fef783782c547a2b5b8b91b2039cf6dc21b2d89a1a66417482d3a0f64d32411c2d58c9556a5aeb54af513b88bf26f0b857f17c5c21443a8641cd401d9622ecfd21180593228c9305219d6ce91bed1844b1c6775ca8114f4b960f500a4b54b2902ca36407ffea886a99f8940ea3dc9cfe5e24f451cb19585f6e68f02df59cc218cbea3e0ea45cc72e574d4f9cd113a429f44264f794fafb88da9560e2c9f439ca1fd76da39be85f2f6b953c9c92821ca5619084565cb920d23265903ccce9bc2e65b4cacb3910c66e99c63f89a967f20ee16849746a680b487dc1803539b5e8abf713a316e0e01ee758a19bb4f8882c9488f8f088bb7cd6f3b6a320b8cc38ba2ddd50598245b7cde53815e9e31809513273b1436b47c2115d5e7260a5e072e5b5d35f3587cc3a763333e712ca50b5189a9714473e061037ea8746e0b8c3350ec1e58c402996d6d4453ca673ee5a3bf59153ac2bdbb1ab24c9307c50f94047c5f9d7c99b849a09a1ac46aa80cc56848f1767f3af49d13ae66af9b5100 +generate_ring_signature 39fef726944368533d166d84ca3f2ed35aca6b33e5a3ddcbde30f1a60465c61b ba1907948b591f4d5e7faf586b3f95d9d259ec089ba3ab71ef4b57beb3e5830e 4 cfc47ad6292db3248c65c78fd2db49eea2c2a6ff4c6f50771decac7631c4f866 4454dc103cf1ca4cfba718e2d34dd2489b08e0b350523dc9477dea5f16743e73 74d30aed08a71ab71277adfc83d8e0de28b2eba61edb0e94acce6309973b6d45 2aefe7ed1319282d5e1e01220850c933406384c6a931b0acdcf4957de3891eac fca4d6c4cc291b9db974d09df386a7742740068d0db8e96b1b4faede19f06e01 3 a46e93d15cd4afe575a7489891a49a182b2cdfdac82780ca3a8ca17d52c6f40731cb42e8b48328dd749e2ce5e4ffaa70db997b2ad141ef5b5dae346996a75c0730611859c709c494f09e397bf5ad42380f5f1507278920abca2e5e71357cc2086704e7e93cfff3c27eb4e33ce02efab7ba1c02b7ca2ea6132c032429b937b309b0b0f7bd409dd7f7170c3509cb5d2c3dd42481652c071e44a177b64965ae980c8273f599d619cbee3e91e8deafc7aedd2707783b44ecf39ef3acdcca0c1b00047c22052f647a7e7bad40116615a41cd77125065b2aae3737bb5ee68ff31b80057ec7116c8fb01fb1441d0e567785b392c4caa8e9ef37a139f270798599b4d105 +generate_ring_signature 7f0c29493bb36b01a82a2f7077d547890e5bc7831ae6a35a2f31a0f902d4854a da08db64452bb864c36174d98a4e14781c78b7be3ff474071c0afc444fec84d6 1 bcf34b4ed58ed15b02861434750fb724d3101346d5e8303437f56bcd5eaa5c8c bfe6a1b8cb2a570d84f6df232db97b7a8e4fa3dd8e6af54d09fef6aa6f67d305 0 9630dc704115e4f8b83d1536533cce80761b9a3a17210f893f7cdbaf78565506b13fe19ad00f070459aa2e4567e6e44818ca6b268ec62576504fc0795412cd0a +generate_ring_signature dbde6983e1560c26fba2a47c5e5d55dc182a12985b43766992a1e1f119947126 bd9bd3dba5bf6a2d3e986daf25c2cad584ae7c3afa7c27f5a98bc220ed0378ad 14 0f89f5a886f94ae576b8e5d8b947f0e1c7430d6187b9cf04c718ef3e99d945e4 d3e5b4d8755f61d6ef6943f8ba572fca1041346eeabaaaf4d53b6820cda40b3b ef305918a04cf633ab888ed026dafce004b79e96ec7bdbed5ed73e7daa14f5d7 bca88a261ca9d4ac8b51c8880ec327b1893d829605be97a299e9f29998a90d42 aa06e88145690e0e77549fac710a5c2279c1cb330203a88efc44dcc81465ed19 a2b43e260b1ad8a55ee794ed03fad1cd4a7a80371fa5799451593dbd4d4c7685 2fcedda6848e5af9ddbb3c94e181c8153a6229dbc896098e1dd4892f4d4db92a 117ea92dd03c8e8d8b54d680f30ae97884be678c4a868e672c5f78de1012f66d 20b0511d897bcf5c00b6d601dadd3bc7a879fda5fd41644dc07015fb95de9a13 38ea2cf665769f2661fa7e08165a9bf94cb807ef946d2cae10fb0cf314919620 d7d6c773b3294e5aa5436ed9c61ed5f03213fab5b30b83ddd0e7c12a412533d1 de39e3972890dd3e175e30880c96f497e180070bb64d726b968970d2e3be7ff4 fec305745ffcfb15d1e52cb29377b9a029b555b6a4470f0b8165e32d7bc8afcd 623eac57d7111b44d17da4b98c585b59eace5b904538060f2e4527b2f08d7d96 7817e9be56a20810243bf96eed3585470b75dfb717ccd52bed820321c9999e0e 13 38abafab2f704fed4a09c8585c6eda454ef38b0ebbc1adfb6dd6c026200b7c04951c45ab5c63e8e355f1eb52d14d7281c211e5afbaa5a1c42a70ce602c8aeb0568f599d03b038f5679fc3ef0871faf4d5ea9bc33a3774cda9c025c0ae300b30319b4b046d467fc820bd77f9a6dd31ac7398c2f7fe769d7808671a997dbaf650c961b152c4f5b0f65dabc534bfd7901b36b5aa1d086fbd374b8ddd4e8ed8fec0ac5e637d0f302f972e8116fdfbe4efc3d71f2cdc23d65c21b73661f83845c2901f0395f0cfd7c7bb0d056f246431470914081f2b7f53be8bb39d4f366ab2f040541c8f1108135f5e2109331ef71924debf1507e41d6ddf137bf2447217f5e1c064b575cc4a93766c87f87a21fef76840f3b2c150e82643d066314be98df9c330a42ffcc9a483da1a136e2dc906cc081692703da848de6736cc27b8270052e390f7c06c837d56eb78ac18c4c5ac0cc20e1e4562da82a161755f79bd6035d720407d54c467f8bb1a70e23b75c06215828cd1a5888db8e6bd5bd43b9f368015a4d098cd41a2c9d60dbbaae059df4a0044636de663c65fa06a0661d6449f5275d200fab38e6c65f7e1af3a15afa1205b453daf1ffa2f621cfceded82dc359be74c30b5fba3e1a0edc73a7a134682b2f73d3fe6a0392e239f66803bd167600abb5650763d782f12f16b3deacc5f31b37e7a60aafc4afbe258b8860591a2c889c42ea0e786c6373a4faca9dcf6b0299db5531ea668f1c1dd81c482d605d34ce7c3bd3048f6ba7847127b72171e258cbe3483171d5c0b511fb054e2d9f5bea46bcc7590f3e9c216e8c96ce3c6bf86b478259593e6b6129b1d95547a97c67d615e8b7920b7c6ba4b2c7a093787ed6ada578221ba058bfc244251449693b240244328c97034b0b07423844159bb77ed30cb5f76c9e7d36f1f8ec864ff03a8e541c81cd540588e4622ae14f99258cbbe0c788984be3f04f066fc5799ab49cae51774fd37a0b5c8eddd8585ddd5a30017c91fcd9b217321def9551533e602861b3b9433d9c0f9181a8023ba5af0cfc964162dad655ea443010473e1cb524854e46f150bbc30b79933768d9a8a49f26058cab67b5ff540338605a193a70ad523b800e7a27d908c52e7fa60d5c9e746354373173862a9de283a764db7d0e4c16d1a88f570fbf0dea67863d5ccf685a86125c6ad5d099197c33c2434228e6d199c346f7f38fe60f872b9d54831c706521cafe5ac9320fc5a42198acb6b9389846320545b5004906 +generate_ring_signature 4a049f0f8bf7379759bafc11f3e9326258df0ac45b612ad5088bef00d28ddf03 d4eced528b619b2c7e3478ca4325d630c3e79822e59327310e1e58f2a5bf46a1 1 8e1cd22c64de56e319bddfd26af3e8a6213dabdd844ced186eba1fead7172564 ec5b99eb1d24eaf612b507786695acdf1d15372e3acf311457d7197159059d0d 0 f89f7594ac7a0dbb6eb7dc9ae4523b34fb5e25a58f0fef549350a07d61f76409542a4efc736abe78eb60cdbbb143b02f41276d0f0145ce4280817fcbe41d5805 +generate_ring_signature cc597b17e1ff8657ef92c92504a0a1638229a7960b286503597687714d90c0be 55ca58a01832cfe103ee846f2a11e237bd11e22a4baebed2548e75ceca9b2eec 11 02c74957d08764bb2dce5202466669f53b66570d75afaf9d6394bdf325be7c93 7aef7a7f9f34bd0716ea1483a0a4372b437ecdf14874b98455ebf4f6ea4a2314 affa26c8824fc326e2024d05c1286ad0797e4921c3c75009f80f965ad0b6061e e9946e75782aa240166ec2d39b2232303b0f79774d2a9a08310b9336eeec17d3 a7c3963b8e1784fd9004e58fc205b2de95f71d24ccb474cab1cdb15080931520 80a6a8dfde031883f53bf598dc1c47061487b20f393d0a630f0b15fbc71449d3 d9583c14e71f5d28c474e25fbd43336c5542ee1a4ce603f5299e0273f889f9d3 2ecca88312088b7e4e346b72d37ac2c9f312d4d82a842eb9393ebec65a0d7fe9 b59e25c663d8617339865199c0500feadbcc5e2b9d00819a3b91304266cc98ed 9acd42dc028bf3b39fe1fbd98d8eb7c7c3ad85acea86efecce7e41831e5a0e03 3304c25e953f30bdbfb6ed159e4a344c804dd52aea4d104bcab5a281d6a4a594 53cc0ff1070cb40bce44ecc8de8f6b189239790afb0545d951992845cdca0b05 6 d23fc7f13bec16e31897c9a74569e5378f83202001945ebd8a3956c6605592008209ff3ad2607322aa4969db900f1cb5b7428e9d1001956e22f8457c726e760bf13f64d4aa8e207d2e4e36e0bc8b47c619d3cc1214ec53f4854ce26fef3c680ffb3434a75358b5c9920475283acc64a141cd7a3cdada076c3c753c9fa307d901a118c34238801886893a205fb2175b7845cedc638b8f80a4f7a4a619fa6ce20d94ca17cdc5ef8402a585937083ba55f1c971933f293eeec4be5e1f5d2e7303086da6dbe3834bc8750bb1472438133d40b68fe2cbbc7fb83a7d4d65ac4fc476029c78beda3ff8482417349390807c669ad878c79ceb45d71726aef09649516700bb19a45f8503b7fc501643ca8889a9679694a288bea16febae581f2b2353d30c25cbad97b0e3b33f679604e3a50b43cd60b367605464cf53d79e3c2b24f17206d298e214c8bfd42b86b2f4a772c517d9363851c45c8e091e325e9c1d61f53a0dc64842ed4c9ee025d1caab83c3868946ea3449193fab6a766e0086b8920f880741a6accc085a48176f549c4d0014de06a5b7a803f754de752cbd3c6de48f990b32305db58e83090d857b8cc068d1d4b4b7ad75103b7745d0c1630e4ba9cab80e24f40f78624cee8559166b65ebdd8a69c0da92c82b0fffcbb776acc052fc900258f8330d168224a7a96770faf0e2a987a90c251d4e76d5bd11e11833e908f308d4d4f595af5b9a4f678c72e2b9deff2180f96e36abcc63a57a35e446c56230079bbd24892ff1d1fa29965f5670a37c558594515ffa64504b8ffe97a7cc180607064b55305d6a0d85cbe95d8921bfd208a1b6580a34a6286566f292f2d2f88e050349dda72250da36bb35fe0d2514b347cb522d817c7d76e43581bb3942165300dee52e6fd22237605beac81126ff4cc4ac1cc3023cffb5dfbd88a46e65130b0b93284a5bb0ea6883c336f28b0e2595a7a8f97a74404df21e2d9a6ad41689a805 +generate_ring_signature a1a10532175cdd68eeb82f0f46b27f97cc2bd57ecbdf6e339a92c61d67056fe7 a7cf65fccb2a26a3470e9f90ef57f9c406df48f0dcf5e9819553605479d54cb8 1 516241f236a13881ee4aeb786cf686b340ea9f3dc8f142ab7939b63ea948f9d8 0666045383e7f9829c827c19f36ab1b57e58988af20ce494064ec1783c85fc09 0 47dfca70942d66d5ee61d60ffa16076146a3af2c497ac841b5cdadbfa266d509e94c85763c2f5cdf4594802c4953632d23f92de69ddedc8678f41ef7aa033009 +generate_ring_signature 33b853afc4a8fff7ac0b290b524bc23068dc3bd098dad3cf44bb96470ceaea6a 13b719bda608fee66c92ed1f005cd7993c144ce764fbfaf5349de4ccf5b6c319 1 29b8fb819c8be48673c0b35b8d3dc52b81fa528cfc91ac486b7751b1ba88ea73 bb666988157a61e08efec906b79f3e1321fd06d1f401712c0b4c47a98de66907 0 34dc6f9fdc4343c15313ac5f504bb1773fc54bf27a452215235efec0ef6d2d0f164e1b31206a4307708ed0d3549f7175d6cf77ecf3021fb7a472d48cc8b77b05 +generate_ring_signature 7267598c7cab03345e1d5e770ea5cb4fb41d934079557f538b0fc4115962e89c 72a4cb672b3233588ce82c8aa41613f0e1f087a370871ed79b72983d414d2dc6 20 1fbc0e3d207a90cfb27c1619a4db82fa106454b554910381521071121f7cb756 3abb94f2520ef80147aed005c178e6ee800ab93d91c284f924871ad3baa3d607 180edd37508ccb4ae708ea73f0e56b0bf4d4a0367a7fabb67803e1e2937aaf70 947f5548e9c8937ecb3b13d7f8c17889369ab8302ffb140a742659c2d1744043 54c8e1ffb3f50f9e364aef8d7164980cec2ceaf36edce662252f53ead9a784db 613208344207cd0665eb79c8e0ce0fab7f8b864bd49014c7d3fd20552194b947 338aca305d87c2f8792b82136cbb7396bdb218213d5e282bb4d29cd713389a33 ebb825b0cb8392e3dbd324e009fa3cf5f22662c70b35ed38ad32155942d2e7fc 2a6a57bc6115aecea112235dfcb6908a5305b54c3e1be6ad47f4286b4d94fcd1 1e9131251f0cbe92c17dbf1cf67836afd9dadefeedd0011f3d53292e889e750c f1d339e8dabea718797095a461fb2eb70e536fa2690a639d486c027cf5af6cf5 4f4f6582bb2e5eea2f1b799635680b34e5c7aea6069dd434a5b1df7ec53eed40 00ef708cc6a3843468e01fb87abc5a78fb3e44d1adde671ef9f977652d93f0ad 1b441c5ffb9d966ebe924088aa42718a58e5f21129fbb5ab4710b30a5eab0bc1 28ea41a63a7c05801ffa3f0775114b52a07540e024e4f437d15c6f596999a54c 5d1635b9996f202342826c0462a030f4a3926923201d97e2e97782dde4232e8b d63ef61485f58a8a8421c1030c63b13e7f9285b3d17a97c0ec617156f3d87e12 22e861e8e46894f227e0c5aef38e7144d61ca338a95c8ac00c222afc157a012a dd99fb211e2e4fcfef75cb569e9a5f751bed5f29c5f69e4afebd558e1a915b1b a79ebc1f781ad0bc716d9d20ba4a4be3a41babd8b940b57419d4090ebab97ba0 77458fd9ec7a1208f2ebed73ce83b8910072cd2e4658cffdded55675eacaa305 8 76273d2678872f9d628f2bcf2fa4e55348c347f6d5cd5add5d16da2dcfbbad0a9969da01f59edcc8d997e9baef87fac85cb845d627a446494316f698c5156b0bc526d485ede63f84581f04554a7d4d49cf27ffc42eafa570130178eb0cd40d0324129af54a9663840a790dd79b4dbdf8ab468dfe88a09522c504b88d01193a0363112910a3870934632f8b0acdf667224cb464376e852f5908af32ad14376502a74dc88ad52de891fd4cfcec22815e175e9bf5718225e8d4a4e14d3effceb40dc40bf784b80e7ce3acdecff718860d31c099bb2d73cb5b10360558e32ae05a0cc98f34e4401846088db15ff27c4d615463bce242ab4cf24b42676b53d00141028a1f65e7ffeee54af647b8826ba60954d85dbab97e59d6087663a14c66fb7f0069cda230bcef707cca774c9bd6df0a0346b9d1eef1900c93118aabee9d2f740924339358b4d6e6f861c969b2ab455536d1b70b65f1ec9f16d960d106c1af4f0551e1e45a607472113b6b88e381156fdce641fc66d66539561c0ee6bcb539d40ebec532499c6a5c28966f69c43d6c25e0a208feffad242ddc800b8b9c58e1f80482b48ba52f661e0c28383b5daf6999fd1c6c1bab96d42b4da848b37d2f4bcc00bc788fa7863407b8b5aff00c1c50ce435e058496e0c353b264dc39ebe9a15b000b8026574ccbba296193f1ca37ad91ab5ed182d556aa674ba2449f2627bcda0c52caba61510c48ef69e578e615889e9f99e69359c6bc73f07047f8aa27fe7208e95317ccf056c35e1f2ee3a7f6a56c9aaf7bd7ef663c15ace114e31c0a97870891d847f551e4b81c697b6fbec00953fcef9062cfe894b070064a5d7deff78d08445b922062643e71ad988f4b34dadf5972345bc8b36697021b9b8868de765a06b6c8b5dd51ffeebae285546da26165e8c096cc90882c34f770552854e143d402c5d7f5c26285cecb7b3166b4dc5aca3462ac707a5229636339c26bdbdccbf40de5ef3fde8afe304d7a2aaa835b767f7cf0d669bcdd476bf3224355b622af3e0cc8759050ded35ed5076d552e4adf9348b56d2b027b5f2b27f127e99fcb77ec0b5ef4d4b596a421fc394a3bac5325df4988eccba27d17408b6cf59a13d8422e030a2c9f8621d8326d6b629c8480c748fa989dcb0ac5f448b7b69f8dc32628ca0ebb967d66aca42fd9662f9b35b541fa6e795a4459e8037c839f08f4acab9cae038c2b9c9cc878d358f95f10b179e1bbfb26d164ff665f4bd7f8de47f19ec45f03bd669aaa546df1756350eae70de45e4f66cdf3363bd4bb07e50be2b5223f920107258939ccbf401519f8ecf15461d182ec08fb955da0032f06d7bbf665a5410402c1a159c93098f3db5e6b7cbd47194980b40772311d0cf91345d01e212110069e472426cca9e9a72f773dc248a9ac58433cfa7752a431cf1d795a1e661d4a04ad9404068c21d6cbfa11058fa46ed4682ab4700e0afb893d1973c4a13624cf02e128a4730d146a4f894d1441f3b9fcb0721695bda019ace0a24d53bc19d82e0ba579adb76bd7837ce7243a0b46160862a729e73806fcddb4543e8c06ba88150a7cdfae57aa221fd68313cd9209947318324f7b3e55a11cb9a26602168ffd0100256de511cdb926b31868e1c9f9221f2abb4c799b8c1e1bf9cf67338b9e8c690763eed483e67dc7b9921d1c89de3af49e2dde297c06cd3d5464b58ec9db90600ccb892367604a44f4bb8872f116df58ae643771b357866874937de0d7bc26810efad047ec17247fa1f94bd5d7c8538e126ce3b60a5e89ea434fe879fb07447a02 +generate_ring_signature 43790ade135a2bf9ffbec0b7dbf243f6dcdd40eb7b420d849868ff7aaa0f67d5 f454c7087f82361c3912a9120b4226db15f0d752d96a5f445e19ab88d0bb32cd 2 5513355588936c635e602614de59f0dadd566c4e7a77fa151830e454838fb72c c00a25f291c66d07365ad72d80b0562e7a91e990305a0bf0e5b01a0db499c8e0 30c8fecaa0b7f217aacb273eeae2ae495f65dec9dd62c17965c5f46beeaaa705 1 6a4ea6a569c0dee9f677473c89950ebd2324839deaf287be71f3fcb450912b0a5cf752193bc2e19ff9044732eb4c46a6f5c1a1d25a2a1c281bb8868732fa800e3403afb281b29fdccfbe020240ade7bdc019aee7e322be81f4e3ca4311bf3502b5c27740c1d99521338059f9d083e85f3398504dd22ac6d243b2440cb3e88303 +generate_ring_signature f745e1b2dc48174a463a7b510ac8e6bffdc6303c2816ca05195d9ae739db3ff5 eb4669ddcb40d449366daba63b22670bf36b64ea51970a796f07fbe78b6e1af2 6 fcfa341e9f83038e4f10b48ea65db8dcc728b2324140891a0566c0cf16017af2 3abe9c454ab3179b0fdd96e644d37285582e54423cf27904fff0cd060e3522ec 202812d70556c9afedbfbcb2b863e47bbfa43a0f0e366d50fb369cfa9ba40496 f3280be164b445d7605ed53195350d332b6c4a0425993782d6f5974520f433e0 8b368764599684ce8308969573d800ea8e4e9dca46c25429005d23b8c2ef0f67 dac4c7e0b261d9ce63a7afd3a1e7f1d3c04682287567525b1696ba305a26b42c 40e0ef7967fa1af5d40c8e74b5aa2ce841b04634e8eef869a3e25b4920c6f405 0 13ebfbd2a82cb89a77afbfa6b183c7cd1e853902bc08bbda5095577a87e7830a2de1bbcd4468ee58ee8fc28ada60ebb63d80c10d807f8702325ccdf5f37cb804c99e3ffb1ce151b516b3c19950dc0c28065ae07e348ce1614de1ec87e36889091846a38162ba033526ad1f90e9a3c05d07c3d8dba8565122b3a99eca239fa30d9e9c618bba7bd6fd0098fcdbcd2e9f215eeca594b2d670b3fbde8081f146eb0a8a28f1750a980b595810f85c5f3f9654d167d71a703c20e8b2ef93c92ca9980a2316e37ddcecf447431b594f6dbce33c4b580f100b3450e08fc8e81a0bc6b403163584d050ac517fc2baffaa5c48d5b673f1b8574c745861f2774b04c5665d0cea4a3c6729236227d5ea8ecdf624579ff8abb604404ddc0f9a81c5aa3e0c3607025106ca3abc098c34a83945d020a0237fad8c73201f3dd751b282b4c3f62302ece2d7da870c5d88643a7eb4d9c9b8cc93d86c4774ce0c583ed98c71c8937903945294478a131f7b2fb66b5f6b3cf4a2c46fdf89e39219c493b4506987694400 +generate_ring_signature 0c1ca4f91f49d1955400c6f4f0ad43a05b4b8e5bdf8c10da751c98dd85c47d95 ed2b4cce5bd48fa761dc4b884062ebfcb4d51dfb6add0bcda77488c533ec29a2 1 c31c24cc6088cf25e09cab4318376a3202945099c5b2117ec04a0d03da2afea0 7297a530706b0a9826fee21337f341f3a7df6d4b53e78d21c0ce596ad2c4fb06 0 d93adb2ab195dba0942ab3510e046588d264aac31129e53ecd572627e3074f019a4b4f234d0f59ee934ada5b0528b3287536df05599f54d8680062aaa393bc0b +generate_ring_signature 31e7df067e668a7056c57de9230390b3b21832b0aaf26c1d136442f138615387 5a53279d3b67d60495984b9cb94036d0504971c6f5d582b8a9c868bfe5856de8 28 d8e11f5cb385732a6b28074afb6337a101560a0015e9772d3f30d3948adfa268 4da2764bb6febfd8c8e04b2cfa881095a1311336854fd54fc4b6b1d3b50b3087 03601a88b19d5bc27bab15b33d1ff472d91e7a928a3ac537c709dab71bd56e0b d18d584e85ebc1788a95a43f24242a33cc9fd281a25029934850752ba44d1cc0 a2696d71168201625a49011911d328a514d6ffc61600e807450349198bad3d8b b1c2f0574ff45ceeb330de965a3ccead9738da629a941436b22f709939a536bf 7d024af0ad9a8bc11b1870d4e4bb34a3aa7f615b47728c80e366899088627861 5e16ab4a356ca7682ef9b89c2ff32ab2233393c1f6f8ec39945fd8f68d29df5d a4b8a7093c7b2f11f4ef98f3e17f398c05daa7f6778c4ef6bfe8da1b6ad8638b 0f3c7ebdd53bfd02e2f96ef56487d96efcd6c6d791486816317d624ee90f31e4 744845074f17c4d32bf7b9c0d4580e94a1e86f2c5521c5f3a2c08a815a0978b9 31298b4531ac19393a103da8df5afdca6312fdc9bd467bd488e4aafe0336563c 9127fd630d5000a395971aa0d908cf98a5c7711e0f01d460a2a7684f52d059da ecae915500777293d1a23bdb2d70c17c964b9a63ff8dd4ce6dbcf45772c3ad3f b48d34c124988a550b37c4a1d823017ba38b39b790880379ab0d9cc0031f934c 2de61fe61cbcaf6ea1f10cb8ba8f4fb8df780cc00c0e517bcaf9284cef02bc46 040f04ae028b915b6d1c7d906be11eeb401f7e352def5f7613e211fb0fb558ce 551d509feb45aec8e434baea8603a2ff77b5e3a07cbb0d0b527c8558c4c64708 d073b898ab4ee3836c67d0e5d1dda2085f4c12aa416c81bc340b85c3b8b6ed52 435503b90a2d14f405096ebca6b42282534834fe9ea39a6bba5a4caa4f079766 2acd80251b3515fb625a9a18d2154b9ad1beb8d8b010d8be83ef0b749c5dd6e3 e5215f531238d2c273de542c6f4fcd444e82134080f087d0afc64f6fc8bd44af 22424ee51118bf80a8cef77b8e113cc0330e24a3844dfd84917fbead9b8c254e 2fd2a01313fc8b08b276862efe9a959c3b1a19c26615d90530bb81a9f7bb86d3 ed488f41eab4fe62d9ca4b7b84264cc8320704afcbaa174d0cce1799d577e741 4294f3ccfe624c32b9b2274e0c12e55daa840db023085c7e25ec32e00e889bc2 0f8c0b190eb31fd4afed7e449e6110176f33ccc4d986e948104607a326c879d6 3bdeb0e9895a3814004480cc6f992bb3d35b747f697bb88a04147e419c5c2c72 3e8890b916eea212d4e1e8c72b2775339eae8c42e4ae8b43ed09e24c694e7e0c 6 355d54afd091e0f6eece93deb42e5e513d569fc1f2a97722d9051e0374f9c80490096611769068f4e97b2416b7e1cabc9ec26e34f134c900539266afd1915d0408532b389b8eabc63af405bbf5971a3193b759aa2ec253d4a82de84ad487320271afe8dd2c60d07221f412f2be29a0779f7009a0bc6f62eb7a87bea43bdb3e0a22001e2c1d45ba5fcb6cf44b33f463b28398d7875099aae347283bcd1d37ee041ebdae7ed155a0d36fe41374c8923d9fdf6b70dd823063b08dc1012d803ea10a49305de83002b6027b0870647bb15c084c944e403e8a1ea638b29ebdb0fdf50509eb7b4aa6fea61e0300085682add4f1e63b10047e0e66ebaa4e9d73a47b380c180c8c318dc111b57893078c35663532693591bbebfa1064ec9614051c6b62091dba74033baf6534c26201aa3ee53f008122de11dec0c7961957c775137b68013736676ed025d98431e8342fb9dd50775d8fe535a194df29f10c02645c107f05ae45151697be0942c015c17b0026c14be13b9284b57baa749f1b4ce250c3680e83dcb2b3217a9a281470ad062aa2499e334b99b9cf30ebe838b17e543d285b005ab7549a312ccd71c4dffbb2746748c81842bd41e0e16db8af3635ade945f009a311585e4e9373a4d7d9d5b0bb056e24e2b18c473cfddc6dc8f3bf1147295f0302a999e2c615dc1aaa24e254f6bc36f04d58ef686ebd201b5dbd03df0655b20dcecdd14451a116deff2edf2c11f32e94068372c8cd1630c486089cdb2e18050e3cff40c25a3f70d9c53237a20c4507c65dfffda88e4c422b5235d25a0b2a050a2202deb50ef8d4e3215b6e378f982cf1f748c2250baaeb12317c62b13fd75009d1dc55126173b506c8e7fc92a6a6c9ebb4d1b8d41ff825827d311d4715163a083e01a141aabe248cbec1ff118c93ff930e2b1fccd590d6134ad4b5f3020ab406ec8dc82f6f0763b40c04705f322639f6ae44dd3c44c3bf83f6fb777110880e00336bcfa9675938990fb060cb7d668f9c3ee5bba02ffa6a672beb6e42c441f3080e361b068a1aa4a9292ebd37aad580e181cdd511355d039190491a7ffb70870d0d128f09f91e837f4b712fc4844a5ec90935b550645b4fc17cc8a03ea2c39f0b956b81a7260317cf7e39b3bf61fbcb68e497fa045831e143b0aa80aceada8b0ea7f3c20f7489305598e24403ff1a1ddc5481bb6bd77e2c6fad8a0c59846afa04943b4e22d05bc79cd58c81816011f6945b34c1b1d56f22751e6fe9b22ab3570bbaa850caf74561fa5b7715fbbfd9ae7adede713cc5b2a864cd2b397430c52c0d0c6b4a9191aa9791e25f56cbf26c48e3aea68352e9950a6343ca396b1ebe8704f5892cf44957dc513c7004e819620869cd40bd1f3c1e544a87f91d71e06f2f05716b742fd086ed3e7f58d9b2c09001762a96e3b7e7daa9bfd7b7bcf4ee722f0302cd9d25af1aea9cf4247c7c749280d6ab934a9d1247e978d3d3269e22e16c069ebed3661f043a257242969700885965f99b34444aa31accaa22ca51b19f010446012d08b61841b1940a2ecf0f9ddf1605109e6fee9a77bf81c16184722c3e059c49020686d6d63537fbdeb4ccdcd52ee5c74ce635fe6e8314bedeac4899cc0b46696f07422e14ec73b0e9517bbcd9ab4d5c300bdfd5243bc51ac4b98148020fb77b95c55470d9eb6d98ffe9a785f34ccb0c0e669027fa3578ad3643e9e0590d2c4a0631ef7beabcce3bea977686302702d629422d0f2efa8901acc221ea0709a4453799231d12e4caecac7d98ef5e0d0d1b75a4050a8447acc8740c92c8e009660e50274992c65cec4b9e9e8083a135262741845fbf8879b7ecc59659d5c90a096fe4c7f6bc15c715501f970a309a6ae9b302bd4303457e5d3db366e6910200cf685719c94311c9bd9010c2fe29d4bc7dac6610f41dc65570feafbd1e602a0df8820262a6ab3bdb886b1270abd83ea27540538a791dbab3618f49f2248ce609cf8e570e4572ef234d70588cc02b77464f4fa92ed2ede86bc5bc4ad42028c20866e6643b251b214cdb2c8769556dc9e87ffc74cb9ee70911692249b72b863a0fa8d80c39e6fe9fb98c526ab6dc71f6b076aa58758f78a3f1a8d34229ed996303d807e59e3ff468d510533b44d6b11d76fc01e74fc158ab8986b5780aac29a00525aa4e1fe57586132cf310fd250501a16a93d5fa4e96927989d65c42ac92b40849f5a5a5496806d3deb5c1764dc1a8e79006e5f96daba8756de10757c5fede006a5aca2408dd58f40a4347d0cec2e0c7cc073038d54eeb6566e77f0d969f3c0a58526f0fe78355f8e21fd37dbc9005c91a6040a9f231251da4ad375ef3467307b93863bdbf06a4e6536024492654206804132eec96fab2468a2674d8df5918005776c7ed12a9c2b9d3ba0da860fb35376a5b6c55e8b59bb26e26ed1f94edcd0486a8561e2fc910c4cb9a7c348a028b2d167b2acc54eb4d243db78c686bae720cbaf7990c189b4125039450f14403b318de0416e85b78e0f4aa2003efaf1bbe08 +generate_ring_signature 74cf9d48387f68da96ca0b0be54b436b877a40b8d7ce35afbae4930e435245d4 21d51622f92eecdf0071d205840bb1e1e1b8b26d69942dac91980a694df9641a 2 b06cabb47b0111da68931631552e977c5d0f7391abd7e835d4865ca6b6ddc4c1 c59a24c4128d29bfc1d676496b3b275e1623dd226ddb23f8b4852eaad13038b7 880eddb85bcd967112fc751b1512b490b11abe5d054e43126990afc8226dba0e 1 18fe1cf1ef04059288e527be172f4f154e60fec10084f577775d03d8d0911a0dcf08cf595b04d52d3f82925f8e6808eb60781a3ebc1c877a8108798c6e4be40b26a800947f513e7a1c472d996ed1ff457385a42b3fdcbf1649aadf6dd54dfd0ea1e086ab10ee713608b32953935dccbfdac554c5656681aa07c837ba8ad5290c +generate_ring_signature 9207d102e70ef77349166326193c037511f13940a761e9caa6276c186b3eb2dc b935299ab3141f7bcea9047224771b9eda89b7e65d5bbc85ca163f1d7defb31c 1 764d6791a583a488cdce815e5fa424f70de370e2669d6767ddd3d553c6199343 f6a1a2a2a3dbd61d40508ba1f192c0dc37fe3d297f3df14c9730a6f084779706 0 54525337257b67ce746fd584757ed62908bbf359bdd56de872872d330a1659027a9b63a75ec7da67b3a0d72f3814a1f703681029dce8bfea3351e286bf220202 +generate_ring_signature 8f1205e1c7bb2c98ace5d21dfc38b0e52715a4dd21f9b7de40d9dc516c0483fb d27c1b3252173f413b070231ffaa38185f412f2fc8af2dfa34c6923146cb8f8a 229 52c5784d3cda705f4866716a833c47355b5fa4898cdf0f719014b1e06da6e6a3 913c3f015930f8ef9ef6e7113a6d9dc22bd50d047517e2551fa3eaebaf108502 6492b0bc2eb958caa42949a7a154c2900bc56ed651135dbf4b1b170fe9cd7045 15511be2cebf723622ccd3683485b7643fc64514f97a882d9254f4dcba06aa96 30b0cebff8941b4295fceb60692aab5cc46f9080e27da45272545311f81f211d da45fd73c50d7fd41205025c634b92098ac9d86dc163fbc5cefb2bc8c50ffc5c a6bf60e5242b2fed9b365e5e30a7a400c1ed7a182291be8661340f03a63ee265 5bdeb86000a7c98f5c7c3570e807dc75355909ddf07ced6b1b8a49b8ab5b4b3b 7626e7ddfac740421e4ec2f375da22498175b569cb9ecb7cd0ff729f17c77cb9 33f89703bcc1f9888f44ac7af120c14ac800adf93302e7fc80b6953318683540 15ad941b050df08d5fbed5646703b809a4b2bf59fc806f68be975a1659a3d67c be0d234f31dee1eafdb19d6219a12ecfb0cba4b276dbcb9c7926588efe936bae d2c6f00ce060342eda234645e3302365ec1b1634b24a3b48bb83f7c2e38a4c58 68e9268bf9af8e28e84ca36852a5cdabf74996854218b84e62fe1821a6fd3be4 de95a3015e45e1e1a20ebe69066248bb6d9091c33fb5f0fd99c99e2c98b3d9f0 175f159b521035a0d4244455f49c292cb6a84c4820ddfe691d34c960ca04f7d7 8c2334a87ffbdda21fb11cb805d7a0ed8ce0bfe34974660956a8ea56395aa4d8 c7edbee25c17019b2202d4d0536bed6c36c0cfe9368be0a937587792be18a73c 42b6ab41fecf4dca2b1150f4d8952ff6d3e26c124d1abe310be551f2d5f5275a 3622119839daf53fd79c3d53a87e92b7174a4ca560cbad31f906b8def47149d9 1ae04ed9d6599aaa3208bb092eda99dfc9e9b48f08b73b45211d907038f38aeb 01cc92d5068bf3d9ccdcbd5622d574a02bfad10f4efd4b38d0f443da3ae00e5b e862f2cd0bcbd35d523302201f9bc7d15ba68b2c03076e475005de87bc5bac25 0a9d2fd156f8c3e893f28bab84b7b9384e31a15d1131386bd66fc345e5599653 ee3144f7734cb223aca913d15b02900aa7122f079f9a6247d37341be18c7493e 49630627a6644da9ee51fadd02c54dba4abd237db31d8e7f911f9b3669c71ac3 cc1323b8356b0134cee1836eb0e42282f78e46a54ec1406a7d28ea639fbdaa92 3cc0a3b7da1acc1ad6d518029595b1221e9b632ac78a707ed136b597d18ea8e5 1437804f33e10dcf092b2e2de5ff4b0e541b103b05b160e6416fe5d915b3d867 a28c48c79a7694db14669c83277b53961a86332fe3e488074b3b4b637e7b13a0 0f352d25647efb575677787de2e358d579be0aa9bea73dfbe3dc6217b12706c0 ad3a5789bf9a2500ca930d64d72f5166516c71f42534055b3a945447bb0a4fa2 0e338d303c59926ff532b127bfe7f5275b6ba6110631ad6932059d028fa49b7d b68a76cc12b0825d96739793cc5365d02bfe50ceff68afd0b9865f65cb60a3cd 018de5869e7859cb475b48aff57f07f0aa8c1e1ffba1e8e96e87fb32c6d684da 06888fe284c0789eb6e33303797f365dda3cc8f2a2f9ae7754ea1192ce14d435 7f41f8ccdc6f2b5194de7e0477302d1fdae0a74239d94d481d3147ebff4f635f 1ff844011bc0f9a1044b23ab46fc2acbe00d939a9fa90b32dafb8d52e25c14c3 1559cd22ca6b489236f6854add12fc7c2946c2f1f02d378539ecc9305700f87f a597e24bc6f4dba4354d97eec4ae6eaa44428e3f35ca2d060b85bdc2785b2dd2 861635da1e9955e43980a21e4885bb8998c8d86a047f382f94f93da569985c35 66f0dd261e45dedbe26d5e060b60cda4da5aee59cc0ae63387dbed7baff10649 447477689524d8c7a55799d1133c47352dbc5d1e71f1355822fc984ad6cdc2a3 1011c08840a7436f9366091864d940e93e6ccdf7b04189907ae27e4d7e00d96a 954b68b72e15fbb72d805b79c8137d01f8a83546848f8e84d99d7e049dfda8c5 160761e382b3dddeb20b285a60335140f03f3d75acf9005a8274769d3d2b3b60 3fb8f5f3539927e5c4d8d254d696ad4a2eee6d8850d9ceae857851a608dc11b8 5597e4539febf5cf5572df1ae65e58ceed3d7de6c7d8f48d4fb0f3b895951b5f 5c84d316987cb2949689b99adcff5ddb82e0c1996f9ff5e7ef510ad3b679ea1f 2721a20b915b3e8b3263055b1e819947c8b6acdea2fad1d5f2799ec3f52f5423 abbc7b0ba883501c0d07f898df3d11080eb4780ad610e1e8155b4b31458b93cc bd7254abfae24aa591fcc630b3fe13b2f6ecaffa09d1f220ec3d2cc166709ced fc3646f77bd6635c64a53e50517ce45fc7a60e84dd81e4ca06235693d8cfb44d 9fcfeb091e1bf9cefbf395dba14848ffba2714efeb80f49e9b00b00c9f05be86 8c6ef973374deb3c15fed4721bd7aab0e6cb36eb543146d69ba5b99cceeb2ee4 3313b1b042d283088915c9416960722098d20dc06e0805f9d20dc2eb7b31b46f 9e44b4c47f110e2940b199cf3ffceb69c711876cf5f44b3bcddc9424ddcc6218 52387c696e8b95cf5ab7f9fc26fe4a66bf8e1d84ab56b787591619fa2c3e44fd 47ecdc3e664495b46990625452266fc65083351cee005e2a769986e161788ad4 93b8097c3c625640736d2558ff7678691cde0fc00fc2a4e0c4e7bb1b54463447 d32b0abad912cbed3ea7bbeab6db254e521596bd6fd944df06144a2cd685a68b 6b2a2312bdf27d04cb2aacf4a26994dd4b307efe7dc2b1393e815fdea8ad6e9c ebced6a281ec97f6f7ce32d1e62da1ead1173bb873770ccb11f79f9d196b4307 16fdba74bd5741106023583564de63f5b77fd0ae433253ed67d554bd7bcb2826 f1ed0bf4e6f4ee1fd98a9a9b69645fdfbe81ee7908dd0c21cb1860b62e356ed7 681c3a1347ad98aaec4d7031be4969b3f26bc09b03e30497d58d30bbf95f036c 0b9211db6b2a938346aa0ff7df2800f81b4c45a58e5d7689503d584d0a0a5b0e 7dddd689afeea27ecb36fb61df6a0f088152a5694f4cf4155cce68fa2397f6a0 accaf8a5f922e97e1e740b5e8f60674563ef8efbd6daae9cecefa048705ca80a a79aec40aa552d5d2c8564d40860785bcd12aaa001bb3c1beb6846908eec519e 6a8c92275c25fbc29c8b9e9ea0a7eb2d50dfa48836a11890adb27053114e730d 604d081c0de894e8a44412d641baa0e067fc038347ac1302aa2a9b0baced9311 0ada2bc88e8648a5e0c01a90a72ef1ef334849a5cec553309769ab2ff98618b1 3e89073127cc5f0fb823d9d0c8b7ff60974413a12754775b9fba0efc19efbb3c 01e8a98e41bc850b040d84719c66b985e798a624749dcbf8b358ff7d64c233fc 6feb1c7182cf86bd80f8eab7f4681e6f4af6f2701a024d8dc6e581b4a4168ec7 f8e67fea54bffd71c8629a90d50c7f6134a0c2b0c4526795ca2a5e07f146f392 069158fb4cc625c32daf2608bf603d73b7cf3cd344670e218f3e8e54ee691ff8 4cd66ad442a4cfc55b4d0e913daaf33cdfe7dfd1da22b6c2e3a766d2e60d772d 1d0bf5a9a0c93b83efe4143b4cd759079984ee4b0b56512a3a0a5e2c0c9d287f 4df2310fb9561413b277873f38428cb814f221dcd694af87c2a9dfe676a64b2f 370a267d62385479b270e46f8578fd2f68325de6b7c71f7bd6ad678b061b71a5 7695614d59d1ee67af6684e4e9ba5e82cef5880b53b3070a9ed5520757fb2f2b 5c95cabe47f6be580ba29dfc59a01fe24985b9cca5ad1c8f11d94a3c231efed8 520519e1a10836f288b25c7c32c15469bbbe2618d5193df95381d1cc32860847 0d1f46f19b2ddf805247e8023194a97e1f25a23d7c9fbcd9ee75408b369f89ca 6691f76d727a9fcd6729798328728e9b4991ab4ea23a0e8fe35566985dddc1cd 1e9f2fafd07dbe67ce07bdc4b81cbe6bef69b5f69e90db19f37bc88680d67513 3a44c7187849ae084e96d6e6fc5607550a96d597a298c34804edef0c611718b1 f6d7535c498c428a792217943664569f4c301481b400c11a03c03fc156fdcd51 2dda0f1b716a5a96014d1ad7c0b0d6c3049be66082acc5760b70db40591b7059 97b342dff13f2ec104afa7ef42af22d053c0d4724d5c150a600309f679783c10 397b9cd9cf85af509978ee6324ea25ce2ad46f4b6fffd70fd31376c411c98b8f a38a93655b55c9ade39d4d1e6a81b5e3014066fde7e81e91ebf5b82f6dd139f8 1cf789d950a4748478226d2cdb521f75a63c6dc3edb4cb561c2e57b38750cba6 4644dbd47839e1e94946ace12b1dcf5c64920d0c0188649ef4a5fd11b5075599 765b04257c56f566bf8748ce698d77f0961dce48440c50673e838a9f24233a88 6b61314fef1282d0eae6088f5180abde895a022e6cca980cc193597a2854ac6f c2baadb820f257ca6f3d48de4c9768c4b07c7b8d4f7342acecfb12becc6879ff 0cb01561b6391e1493e1910575fed3fc3c314a417313256912de21c61610a077 1a1c943bfd4be26fc17ab352e30988407bc7de6de0899e3c8aac2a575b84705e 0b2dad0d3b05d15de7f035d82acac91d21c7772aab52cf2f35ee17cd1f7496b0 7e3b55ad8e3a235dac80156c46e290880d9568752fc2843851e6de584d7fce99 2cca8f02b11d269de7a1790ba4ab530b9f0d893d6a6cc8369d183170d940e860 3a7191d183ca61fab0c82a531ba5cf5b1da51ddeb1bccc8a3a5de1b3a08ed590 3ffaca59a42c9a06585132bc401078b1e69659c151876b562918cad9c66b080c 482a252d0b29f7fb876666d565f5f67aab6d39f29317d31236129632585b2ef4 4163654838c2ba6d17590d6710cfcd1bce67f76b7298bfcf92139d2502b899ea 94a1dc49c5222983b4d843765d51526f0f1cdb54bfc9a68f62bee58da9cd0270 4887f9ef5441a1f87896339506af632b7d8a9c2df05b002b42c5b6c32165fb66 c53bf4a93bdaf03f2e3c4561357ff9a247cf019dc38b621b916f2c4d3395f9b9 f94b686e0d068cae20b55014454f50fd950ac9f84ca705b629dfde4baecf1ffc 2a3f77e3c18ebe3ec45879fab23e3e3f1db348d06eb30666e3a185484a91b7a5 5a40ba01f1427edd310b9408c5ab2488e5d05ce48b8bc85f8c0081adf11cd9dc 75abf56aa4921954d4423e957f680abbf1e3f458a1717f21b69d26bc7132d484 08a037168f337c2f30ea5db11153eccf6f2cc847e052ef0f2dcca49e446f9c2e 9a88ed5ea6035728e6cfa1407aa9467df401bf31e5b9661160bdb48ff7e1fc36 5c86516712f28f11b0a96055acd38d7819d497ca2e4283fec8300da62612ece2 b8a0708e9b6d8586fc9bc3279a5f0a1bc99505f627bbba52d9cb4e313ebf4d4d ecc9011f2f931508fd5b199f76df7bb96ed8c1345a1787567d80b249d2b0eb55 18275f7f1472730153d052fe574b8e890a4a32b012934ea236b3663dfa28f14d eb3014649405efbf8bfc962ee7706e9bee6a4ef5625eedeafaae0051aa721917 59fee3ebc15513a0c63a4040bf31bb02b6c20c9246d006b4f026c1db2e7a49e1 8bbaf1b46137d97273c26d93918ac0baabba90084bcd209a0a172eb8e7ef9b65 8b2d7272c1ae09827cf434578ad48fe3ebba74ce2d3fa00a35c55523e3a9de89 e129b195950d493e3c9e498f8dbea5b3e1eb4dea65fc03ac2bdb26df885e4dff 3671edffe7f5fa8fe14ccd74930c7ef620eaec25534f1e0db04046db3a09d3ca b086b636c71efd2445cf6281fb3fe8c858c5a345a6ab38314e3d4cdbe962c34d 7ce5992f4a5b9ab21f3b00bd9d03eb3b508c4e8d472f6ce75a624bf69c489cdc f709e736c33fad590fc82f9a7147597d3ef86a3421c0cb90d4f7824f19ee0643 0a758da68465fe3fae79ad29012b6035395373d0959fb27bb440dc9468683f19 5de2ab6d01977528e26cb98e22ce5205b76ec3fe33dfc38beb8ed1ca8d65f41c 0718b9494418118f2da64066b22439acef7fdf1e0f8b14f0522d42d4ecff4722 52ef5deaf9620304b0dc480da5db8e3cf017484061de018008e2ca1c2a6f0675 cad10726ba8716d69051940c377ac5f6a82e2e9a23096597cebfcf06bf515e86 9fbbd93d71ad8ce532ab9b2024aa3cfc17b89137e91f4c3f93097e8a34a10e17 d7d5f11eacdc7cbb053d15d3f63bd0e33554b9dd2eb8c83800f8fb9d0b2dde42 07027433acea249fa21af4cc3df4123babd856022b1a6e7e972a80602a5292e4 4b1cdfb44d28cbe272563682d11e1489f4308bdb836b6ff4a61d0d38be1a959c 9f35f5c9b5b382dc4b6476d912351a322cf349d8b5861df181163a46cd393fab 210cec3467f2c3cc24ef57caf3f7fbf6b7a8b46edf272adbedd8cf67ee954aec 6d5fafbbdb69bd02b98a2b4bdf24065092c0fae33cf6c6279647675cf20f5fe8 6814c20e384dd2298476d630badd43d8834560a06e4a5d328268f03b9f65f714 d8bdec2059ee9a599affa6e3548a00524dc582cd194a66b5740332d8404e43a1 8e446db99e8e579e5059ac28440d2d2c6a6f1838bdfe074f2da61805b213c11e 50a06a07245c8319c6f64966086e439e8445382b79a31513ae10998537568235 5efecc1a91a9a9df0fffb1050cb1ac2905100ca92c0ef87074d50e17a54c4696 ddf9201bb6ae23f18cf6ef069d183d0ecd22b08e481d2f1c4760f3a029a17ba0 6c1f2b0a8ccce1c65d4f411584c4a4811894b96a7c74dfeb4ed9680e4edf0259 300e358000c2b357e48e532f4e02dd6901dea80a371cf4b4856cb5adf2f97c5d 49ca50fd86f5ee0445bf9292fd492e4140e550fc6a520024619501179732d503 026d202c9bcda09cef16ca1c5dcaaef6da40fe42e533562c530e1b4a69c17277 8e4d82f0a0384babe7fd0a4326952955832a37d1152d0fd189094fd0922cf106 c3ccae4f7e60f4fa82bdb101fbff68cd2071e2c7c5729b3e9d7a0a6a51809807 e2c970b2577d34c1a4849fd4ddcbf48bc88627d51c8640b3cb2ff73a0ca852c9 054bdbb5411a58a04ab389997a4c85e38f2c714137252db2e130b80947c505d4 491f4b4d406f6e89833f4255c2f53103a26e3fc9f2d3f04ab6f5e034f9d16a3e d7f7a5fe433888c71ba0567bb87dd28d719e92c71254a1f84238c7a78937ea2e bc03db54203b141f413912cb5a0cf4aca06609dedd432042f8775aa2524b0a58 5e59b2c9319d9e6b450020f4b19b4c30bc5b2be4d32f4f635005f3efdb40ddc6 4d3553533863be129705f3008ffb8cfc0477860675c0ab53a642a13445185e4f 10837c90037eee815d50df2304f1c5f35ccfe3bef086aac31ea953fdd5b1da1b 5783cfc3d851b2043684d08af46bcc28c218c4800aab3e4a25192990c3a3c143 a529b861e4f2a800c334f542c958072804073d33ef707a324ffb950a0d9a3bc7 533de95bee68271d28cf19a0cc1eb17310c12b00516d8cbdf11a0af3d90f6ae1 ec2a1aac02d4a55ff2436405b7e821d9612406a4e1aca17b3e05f54039d47fa1 cfbb9f0fe10486fb902b329603cef2d1ed5be4e50557e366dd2d9c75cf535129 b0fa5d3efcee11d9e75ae5e77cb0363f64278fc6958114c6896c77f776ea456b 92562a6e185be15eb3dba54eba90080b2b556ea4e3244195e97c289f2936c09c 1130fefded4d6afe6bd3c775c6f3d92e7c49dc0b5f4a64ab09dd94efe5bb27cd 03df5ab7e0e605c91b33709c625af020147fd3d14594ef3928524b3e41956276 fd4730ba5fb0ba5e26f9bbcb8e24be8e92768b874ba84bd850d3d6c6bf14fa36 ff340888ba7d0ec25503b411fba67bd3eab35e778299f4c7b8c4685ca0e4073c 7cc168482cc27ace90bc3f551975052082c33d3c74244cbf423910d00122e760 b67e965b6227c63f7b1f7c1cac3f4ffa044b499ef2126be0e1874db9fa24a6e8 af6ba3f29ce5b209c40565ca02b58dd4380715f6f10d872006488359e5a468b6 4267ae8273f14e15165df0be29aeb50a9a3bd312816a9be63bb554bd6b9f82df 4ea2aca4800d87b6df9995e3d83240df97e309fa4de089f8b12582cda536ca05 bbd135a87a41a425b196297e060306fc2ea93a1aa2b9f3e6ed2b66b52f4c1f89 d025ed814116ab93c9bac3f9a4272502bb77bf9f71562de1445129e041bd2831 12ab2d4d0972a1c1cb4edb0c36bb60f3c347a8a651b814325f85b326e48707e4 99f3623de0fedae77f920301cb2380861f9844fdbf0d71991b0942331449f9f0 a0f626fbf3ec494ab01485e7e9e49f97a28784231070d4ae3c6b7a9df94c37ed 580d0cb132d0204f173abefcf7caf3abb8bf62bf65bb4954b29f4451f642bbac 8c97a982ed265af9a10f3f8093be8fbf23bf583fbc5b85e8bceb0aabdef3217e 66c73b6297879ab3b614c47308a3e64501904c7bb61e2896121b3c97b12d3ba8 33c536581435b1ab7a93b47ad2e8ee25d3358873c7f13e07e38ea9d1fde9581d 358af96d40f33c96c703a54970c329d9cb71777c66a41b3706b161730e178e6c 973bd3b32a458e0b238a95feecc044bcc7640de370863919a517629d26a0c100 9d3ba5fac2f6aa8fc46a7558bc756ffed5a1a2ae57aebf71360681fed878424c 95898052706ffa6d889c76a090e8244d2e6e7d339610a429da38c3e454465e8f 6604e03547ecea591a6d36ed604582cd8abd72bdb2b49e0a917771da20bcbaaa 38d80837cb55b1ce698fdd5b8d44ce9bfccad9cdc19e0e3b7bb7de9f5eea7833 16bb6df962e6127a9b053dee33aa890210c2afb63058011a95d5d6217aad5d41 09d9cd42fbb4bcafa7fa122ee44a13b420e77c58add73a305eb3c2b89f81348c c5aa1333b34398e500bb2cf60376e5dd6126f4d3db3034259fdf55bdbd3ba7fb 3fae5690d4a5bc412b86d7618d36dc4ec01009aad3edb77ff776c673109c7548 b28456140a3e985c226ab99a6c80f87afa3519e955faf49227c7de36f93d6d5d fa133b36fbfadd578d68199bd44c7e2b902c2ace06dae2ec52034875ab963d0f 3a5c007a828fd572ff8e0e713c46fc41f5aee958e186c1b88198135b116e5ed8 de33d23fc56054e68f333fecfca5f4d576fec6c500a494b008591f99c2a6c126 8db348260e9bb621ed1ac68622e551e852db4e8bfefb048e31e606e71cd9ab0c d909650ed9a50ffcc7f26fafdcfe91064dd187eaea4dff027bfb66d734a94bc3 2275d3df45c2e67a1831735a9852351d1e40d7903380cf3b33ca7ad4700ad8e5 9bf506cd884abfccf74b7ac9899e5430ab9be9ce1dcf4339453cac9308ab4867 044e10b3a3f390f0e44eae198a8fc246162762c0303b9326cdf01ae02285cf27 3423e004c3572f016a2711b9b7d5cfa4be8327185f4782a83e719fbd880ec73f fd57d3173d91f4f5b7991b70a4a7c17993e783c9a250f909d8a452fa1d4a7d89 4fc2721e8e5cb0f0e49791437299a6969485895295be74f63d8253c3e0897622 c370fea6b41b19482dbffa79495759699404f5b8aec95c42d022e710feceb4ee d76fa502cf3ca64f27d37460208b45d8bdb4343aa09f60bc86b43b45b3bca456 63fa51cc0c431db9458fc137148bc7072aff9e34af67204b0646139b5efb43b9 09ea32768ee291622d18b4ab9b182a0158604757150dae3692016c830f19b508 f2b2230fb64c40711b943000fc9faee75bac64677635f21b141fd76aebb38dd2 5e39938c9c977fbf84df5ee0f5cddded6d55f17eee0a38dd5bf01798a9df1c01 855f49af12792ec85075cd22ce20f3bcdae31749371e906bd96504b1662c1466 4fd587a7358b0de45ad268c237bb9b299d6a9fb06414aa30300e237ba1a3f72c 3edf61a65c3ce5226dd877d6aa70d639d52ebea7fb4f3aa7259e0efc8a5ea1ac 5846a56eb003d2b04f8d8a5cd02391ce10bdc43ea6114b740f590070d28a7003 c842f7022d26e8f0c9b832102efa259ce5e77ac2453fce14a364c590d1ec5d50 f90822ac98bcb91651e1f71f9c0849f821e2810743ff1e8e7aeae5909908f451 45091874969d3c9792356d245536a8da824e9adff04698cebcb99ff8e9e20f3b 9542ecf82586fdcd124636a4ee4bf2c4bebfa0f98dd76b45adac28227b0f455e 38c69a23cfeb562bf698b66058d99e63b5ecd7b8ca0b1354a57e9d60140c35a1 f7f75d344ba6ecd4ee5072363a6eff47161aa6334c86e4f3ff832074fb064b16 f7815c6dd6946ccabff6b93effcd8cdb8dbc9630ec5274dd476b27c3344438f2 b21ed1f01a74f7cc86f2ebb376b203a82bdbcc17a6acc4700d314012d3ca7e6b 54655a6c6b8e909278c760828a75b2daf5bf809aa10d84fc68bf4e1d3636d5f6 6b69083974fab0a280295796253901ac813b657efea7348aae3e2eda762ff923 fbfdf4bf87b4bb696d14bc2b350acd77eae89f267666a092789e0af07a32740d 38 50530d5c1b50c1fcfc784350137eacc142220fa028419230e00ba4354d916204173fda0603a66f55c672c0155d049828ea04dfc387dfab5d5f9d331e1c4513091986d1a8a74fa3310b733c01ed012db2315a6dc68614d2135f1eeae13ff872027a6ba1a177de6d619f32bbfa41ac2e0f9c66fc5a565d717b40b4b0eae5649500a067a4d901b99011f862274de2c0fb6f8d3b450340920750a6b9f1eb5a29a70fc6d7512ab7c02147da9dd5512d4f4adddb5620ed8e356f34b5750aef2d309a02241ff73998ec9cdfa9709573b6f7b00f6252664770a90644aace5bc2e19472054a78e9f9e18d4a29d972db99875ad3630f8b8e9a947bf40da4ec1350a352d401f4d53782615dca11a635391692bffd0f4309411ad9b206a3c5c9adce5f57cc0631f38b4da1a6294b4c9956f9e34c2f7f02ea56b61639db7e67514ecff9bf2c0b98201351fb4ffc8230abccc674d3cd8dbbd959ccd8b77fb546634c028713b80af4d033fe66a787037fcf38e23b84c8f48b0d5f8a48c24b1992bb82f3c5cb1c0f0f1446f3ac899ad343c187ec29eef6328d6bd4a07c6636b5349faac50facd906f7ca340c935e677e08682f39fdc48da4d8cdbdab86ce696e3bb9458fd40d310b7e06f49e51e729ae2e705ce1b5cd7d0fdeebb8da7dae9ec721c77abf0fee2f05b051f9feb49496fdded0f87f2de1186773cb5fbf3f32f9de67aded3f2deab60469584cb1d1f696c5e1a994f19aaf5596d9cc7193f908a2d34391bee6f4711d02b093ec04e1a3b796798f1a65a656f38c43f851236eea699e610a87df289b42026e9eb4c5263d3693df9458ae32c85e0e778bf7dcad72cf194101dc2aac61540b8815ae39f5dce41cebfcf4f2ab4feaa55406c6db38d3ed6c2538c55f417c2f0d1fe2a0df06dd3351936e7ec94bbcfebe0476a4ebe090c7f34c3712f13eee0906b3918da84b9520b5c889c0924239afdafe9f49a39e8f40af461c8fdb6623ed0aee010f345fa58bd1442fcbb177cc23aeaa9a9c80aa311f92cd6754ceb3598409c071c7557df871a5bd8efd55a77a7fed0a2c2f0b4c4ead01769eac1825b8d40556f8609669c8d04cab252a288048beca6dbe72df33945f0b436d20740c3abb0548bbd93ba41cc3e5baf1fd825aed918eca542b21af05e284fa5d1940f5d95e01cb226242af359ed089f607ed380d46e947058228c818122851f880ab8e7171088bfe7206f84c6dc247981abeaa724ab4c34a21e9557ecf8ff6baeabe6991470b06365d278c4ac03fc5e9bc4a06f26acdc12294fc2bb7447d2d2bfc4ca058c707a26332713bf61117edfca06aa7110b9d036ff090c32556d671d0b4011d139801affcd2b3552ebb196f01210bc97c59b5783242a8b0bbbb046a05310e7bb2850e7f59eda6bb5216d9ed9187dc9536e51b8cc7a1b41c805e652eda91964631060622cb5b2e464bd5ada60d57952e787e2d535df78c812088d088b7f14c6df7df0ac69469c699541ae3863e80bbaa2ff686cd5d2ebafed1aa85c3fd1f5de51ae30d637a4c0f64840388b4b4df37b38f77bc22e9e37a8c03e027b748f0527e30af07a00b09c1cb6b59259f247db4e4c00524831d6d90f433cdbd3a37e7d6025ab200256c9411d1c1e1d688563a84f4fce67b5e11123f116dd78687baa72fff53100693d284374489d97bc23a32f52de697a219f945ef9d27d48c4c75275758e37e0aa8067054df6a037fb51329942667df79958c66512bfdce8ddf6be7b68c88be0ab509da2b26304cf0b9d3b50d02c2102266dbb90e1be618c9c61e9efc151099050b20d3818c5897615a9c99fe42de4c3002c91b3cdc456a825a0cc7df3ae6d503edefccaee3f2953ee2a4823c97dc1184f16f6780059dec788b502458795b4c04ad511550d60312a882177660adbe84ec7827c8313bfd67de62df338f391a13032ceb01b49d9a107c23e5a789eac36b77f900f66cf096093595ffabd43a6b9f0d68c2219740b0549fa2f1662b33f4999a542fee6aa7a3426c8bfdc862a61435013c298cab85f68773df3ca2d58c872c7d376db95faba7a910f1f7757f24618e046c299c43ce8c2977b96757063844cb76d2ad209badca2d2b701439704979ef078c02ffce66f8e49f6123b56cd83a9e645c882f5002fd268aa30ca99260258102efedc6e7529c0f3119bc75b1c4190b9cad55d4dbd4c2cbba314215d8762e6d005b1b5104e008331497c958512cd5a11a0e4209eca49b13862b25eeac38e403038acc74c8d9cd65ac2a5615ce141e156d32bb14620cc151a0aa2d798be6668901bfd8ea74df2a427e3d04560d3f5a82ed25773bd080f340cb7e34daa3ef631b0e24c1cdee1be4a9cec177f3b2d059d620c48729d53bcc7c26f7afdf64a3a0ca0c29949e2ed5c20585182ea370e5f2ba552c503b75211ae01aa44b3bc29fdf7c06213ab3844afdc91f09e598a5f08a2961758570c06f4e447d27307908016e51005c58bc9dc0affcceac613129029eca50059b527395aba3dbc678ace92de6b409c9f4cb5fe59d3db5aa8975a6bf44bd52a90f31bc8a6dd03e53e42c6957cee101551a54ac6e216bbf1a7b0e26186257975b7c727201eb1eca65a8e024f6aaa301f773b438c69b1bbf8b280ae10597a4c7657521d699c70d63019a0a708777fc0edd2b17461a87ee3e6b4a98b320b7a5fc9a041a75b2e551df26105edda35b7f0655166dfe0bfb1261e96a433b6da50c7e31e2d698599ddff6da02c6576f4bfa06fa2d834d299057616c4a98871313607f46fc0a1f1e419250582a885e75aa110c70a69066771cd2374b5f5ee40f2cec562d603f77f0d7aba9324ea584cf40fd04292e5db9aebc3e124174a57ea393c2f5a3f152a6fd83531b267216bd225a1706480b6320a639aca669faea87a5b95a8349f47a362620db4da044b2bac38d530d45605992eeee058fa956a79a9a22423ccb959aa4e0542c5e27814b3b9072a500f88e4579aa80e453528f22157cfa4fc90d2399169a00db3c054096813e024a04a358cd176ae873af48447eba47c679e01c292d17d6a743c4c65dd58537bbe40c584a8841d1334cf4a011d713114904ecf79d29690fd474e02e02a04886a08f051ca5a5a1423d16d952c6896187c66f809ae4eef226c0096d352724d69f76ab01e694e178bb95c20fe9f4d32a0166c2d12c7036e4b0f418354b0484e41ede20075347c11f7f2e88f9a017beef6bbe803bc9b1ec967e4f29b13323532fab2e8b032318c6d362e1926ceb1d1bd102e02e9022e44f4e09bbe64f6c84f454a7bca00536b1bcc4756f8b0e04cd2e4b69c4e7aa4f6d048a9ced8a79dcb59267b6823e0263236ddc08ca12c1185cddd8900aeb1615dda54ca2b361cc15e2f91ffdce1f0715f16ff14eeac60fef63ffeb0f7859963ed8a1f58f18414e932316cad3822906bd67264c5d65856608faed930b90950fa84e586322b6430bd10a9b0d4cab320dc7bbc7fd5bfce8f16cd7aa233b3c2fa62571927321a44e07043ccad2bd806903391100bb7599e3a5763544db299b6d84a6201dbf19c67d8b3a1ce167f9f69b0229e1568749e08ffdeb71ec520675d24a8d935118a4ceae340b0a7ac8cc0fd90b07bd9420bf2f79baf2c835300e4ca929f3b1221e210b1b03aaded917edfba4073f5c55d23f560e23815f3c190de64b1fee1027da74d36e1a166f043f8f492f07ef3c93544a02cf1e6ac713a1a33002ed2a7d847cf61e8cf990a72d31a66fb20244bd91dbf2d8d0b4ed5daa02a68c166235eaf29ef8da8403a63483137afad90682cb208b05f6a3ea93dbf61887b770daf247b1b13622299193b20fd313985f0e7f15f3f9c8000b257589217a445df19e2fdcd71cd01fec01ad71d79cc34eb70dc955c996f1d1a1ef973a009d28c4ce69d7b00d4b39cf17e9820286c8ad992206f9b268a512a5b909ee087fbc25516a14196ee12e88d7b3119fc4cd3c713dd20289247912b66dead861ad7cd73bb24c29b6b841ce7f81d8d1d87f4fa42e4ac0087073290c46b78366fb297c4a6fc0c3122368e67a94614dd7ee079afe9a2f620961ce78f19c11003f734bf48df0eec2cba8190c98bb7f40a9eeade6d2037ebc0dd21b41ba2fe453862bef3be436a0fab704144d85ce0a9204e36363339baa4507230b6c76b4401ab7871b63f4ef73d568f874ed6c89e21cc62822098141e16f0fd8f136d4464f016ec7abfa58cbc4e604e3d425e3adf2a94ed6cd1392d56f2704b14c86023284c029c04f3d2cf0fa2c4b57980e4a0f56cc5ce2191dd3068cfc0e8d97eecedaf9497470064cc0ce125b9b1e1bacde5a9587fdd97a6bf54fd3230b5523a3b5d85b3393b584b0c7ba2dbd6bc39f799de6d4130a4c830ac98c0c15083db6baf4e2a7f2df5440003b4e42a4e254492387b3de04890bdb7e3ef428c401322bd14ff8d538377d1800fb39623441aa01b9eb22a8c649b20614d04b591c0b2bce9770ea16458865649dc9e0fb34c1ec8c2db7cfbde9de58d3257c8953d00d66ddee0faed1397a7365d3c5d8c26edf42da2e7a64000e4d2f6e17d38d4b3d0b2fa196680e30856f7ba26055cb8db94f1f959f0b0c0e6c03fa6768b1f630e404768b1a88ee8bcd1863a044710449321166c9b5655c217306ad619e873b153b0ccf61eefff7482121646c59ef2973dcc57fa17560588500609e6da786e3e2350e923c7945ae5e9fa99315bbb6c99d294b42ffe48dc3bfcbf5a0b82cf4c2ee2607534bddca825e05f452b1b6412bb05a98363240aece15a731815d6f0f58dbb50b13cd03e2bbc9d2a6a9163c897e5dfa0a9c2e5f9052a64947891598dfd76c050a04cc7e2b6a2433344c4d2f0fa0bb38e7bcccb0ddee095a79371a3482c6622900816fe80f7ceeb7eab85876196c4925f04a192fe59175281f70c2a98afebf2300d28f5eec4c036dabdbc1c5c0438c6488d6e03b06813901a162e463f95966d40149e6058938c78fafce6fa11e77b5e095f4740a99ad4d61e270bcea34b2a3c60d5bf79ef4c99a97fd6cc9a6db88d23a0b0c6e075b983babb8e33a5cd1295314086042bebeaeeffafe09343825867243fff629fbfa5e43dd8c814d83b041394501f76517f7829ba6b27ade1da765539299cc3ec55aae8cadb9f388d11f5e46540b50c1253f2a74ec837ba6f0c889130a5fadee2c24aaac92b7dd7c96cf3d4764076757092486c32d401921756faf0ae54f671806f57baf20c7504ec27fc840090a345c46de1f0a213093e4feed0c2474e8d00751533eb38f21740f90a532cbab0938aeb2eab72983201e67fb8cfba1522a380c2f7e52c5ebf84d480312a305fb0267a32d20824dbce4d04c5bc81c962417ec834df5b5a2a20b9a64f312398ebe08c9fcc375724c81701a9c7571959d4e98738ed0b836fdde7e69f8bacbd718dd0b93dca81d3a01ab939eacc6c5290a227079d14a637451a4bc4ed6d95dbf1bd9024c1fa73c8c4748a7bece777ba89e5289a266edb0f190c0eb204a2fc00638150eeb167782f024f5bd2a675be480742265b5ecb2f0c7cb89ccb5e5302b6110b801e998f336f53fb681e6024940431dfc8f0760be8c0dac0f4573e2d4aa80a0b50012b36b36a95edfdb7172b4c12586f2e90f0ef029ad46d351879758dac532200d2c57ce3cbcb6d5b6abe275f8a98631c91126caf4928e2ffed2a8478c76f05506011093124331d057ae84a3054399cdd872c39170944434b270730c09d6b2360680d668677be702c0c4b14117d7a19051a950f20b8c742adc3eb29c2fb4883e0544291dc87e1397ad049d446638b55af62efad0585257be515bfc9f1a290f5f0cc6f20ecc2908782197af37edf432effc87bbde671438723f7da2e1133a7b95015b645c4a02a4aba14c370b23cc006a2ab5132f83edd6c59c7a633631b0279e0464741eb9e7d97234e4d859ca4fa9e828c502af81365d5895a45955ad814ec8036121bb5395aec20006b840b4b45bc2037e9a5c7d319923cd64059cd21139d80dedeafdab448b13bbf0ce43a0c51be7072e5d62a91354f5995644b88492723505cf077984d1994785d6acc1854941849b926995617de6aa21ffe87b4c0e897802e1600678cf650c009cf9279077202b4aa01f3534194af2f2299b426205a1a0030390f0eccbed460d9ff6961c475206db0339323d6af2665c97c56f4574a8a905bf80a7b8c169457211ab0ea0fc31b71fecca1ebbc2db659a3549e84f519d2a005d33fbe158caf318e557f39228c8ab0efedd6a6dce9e347fcb90b0d213b1c807a990953495688d41396f238720d2f942873e42bb2ff280298481f0e4f3f8c80f97a6669d384ce3f333f3f71e0d591f107266cf46388eba3f4d8b8bbe9e6ca804dbf782bff51a998792191cf6048add724e3c620083401b09c59104e482f759012e81e47bbeb8d6b4001cc272ec43248f3a03f77ca61a9a813d4b446be5ac750cd9662c7e398b7862de1dc0e9571394bf5ee5a3cd905f18a58afdbe5555cdf401ae6ca5da84581e9bc99393b68db21a6dde189a8fde88d89cbdb8a78be85f290bbb652b01b3fd963eea02d3c6fc92e9778d585a927da38fcee52db599b74e86014ed68a16b58be7b05b4827d65edfa0d52926b3074a3bd033ecd4bb11a5a2640122470a79aba24bd5ac5ad7dd1363403aa6d08ca05e3049e791f633015b6e6907299eee090468dce011cc5831e50b854ac27dee76168c481a6d5f5f343257ec0823517da298103681ee0b76eb36fa9546e6fdbe53c1f30470e8dc8d782c10f20e1f98c739d022bcaa1f3e9d39447368747b058b18c1ec82dfef49fa46f5a3e8040601bb0d7875d6d59436c781c80b0f563f77445936ea7bfa99c9b202b6fc7e0bbcd2fa2a656e280bf3e6ff4fcb7a447d050c4ce8926b556ab4d4963f7d1a330418594363d1e66674a09147cd3c0602923d38c6e59ae3015ce767ea7ff30f4c0adb30d9a00c10c1fcdacd8bdde252e42bb633e8cf1e802f37908ee2c6ae05f50a8bf771ea62e4ab6c691f65d66cbfc23cf18e7664f40e9a81b9cbb856cb33cf0f51926a1e0446b105f8387e8a80eee286b643c3ad48b2702164055d529ddb9504d4a44c6f357a21edf9e0b6c6d1c9a2f50aa700c0e9f448b1cb37d6be73263f093099b30fd1d3e7ffb2be8f008c494ac7ef7c662027000166e689edf9778a63063a144b0d69b382d2b3882497c3efcaf2016e1a16408a34d66a2e8241e16e930d3463075738ca95874e96b56055d3fb524fb6c8cb78f07b9c91ac4bf50c89e50ce386f717cfb29cbb94200b268fa2dac8ee7637deb69c61ffc4c457e223f17f076d39802bd6e4368217b7c660085b2a73fc81025606f0b7fbae61d078bbbb9a01996de5f69ab332095c3f69fa953133419e4e27c321ed8172f66a2aa28e9fa00285e5872de02a4a7fa8eb9c237cea623dfcaffd774a58dff81d605197a9f0a6069b8d89ceda19207d321a6babb00b14ba41c0f90556e6d528b40e87bcce10a803d208267c873dbdd050c6236dca754d516630b964903df0a9bdb1c16b74ebba03abd556a0ce6c12d0cc7a0aca849b9c2bf51a54c721257e1cae3b6c5091e9e00a2e0fcc498465126d4be33eb978c8bba17ead82de93212aa597eb70de5fcc920f6947781427e35107a86b4140807a11da8f8972af5ab00c0143a898d2e83a640d68b9c7267b29efefbd5aa6dba5d69552aaba10af6b00e28a448f86bf1b95bc08720ddc61d8816c59520e69e34703ee9cd4cd92b5b39450fc906cd864273fca080a1bcb6dc37b990723e056422e2897ca1b47005160b0c4f9896642526dba8a038bce61eabb2945ae1165ec98a7456dd91a89fc2647ad3d83c78a0db98fdeef02d8dfe1a2a69ead13134faeea677a5dc60056b979538758736e4c22be4d1c0b027dbc2f9780a49ec94860aa0fc0fd06a88df8c79c1e81b802e793b38da9ae8b066da8cd15937fcfedcc111a355cef7fcac20859b4e020660cc1d568290ab8880e1ebb71904a36f1fef6f19873a989b7c73ec96dee503c2ba3c5af2b81d41f5f08ee2c001ff5d1e278ac5584fdf4568fc29932e66e4b3fd36b441a8ff3a265e108d6cf11dff6571af308637cd7dd4143489760cd5e988c5de2dca986601368a50a6728bb4b4ec22ce0107d733923f74cfc6a3ad8b02946365f9945018f95460e01bc4c4d47cb1c21526b21ef7d855eb4aa5916cd514a73e7db3e02ef9c260b46001dbfac6929af65ab9dc15ef98777936d5fbe345977dcb7f14a65a2c130914b042d6f28c4cad41cec7a58cd817525c22236e1ac903f745c28a034eede566e5200197f53bd9d85170e47e09dbd33d44e9142c15d02b975271b08d7ea4f701d940e65b9181698cb3a9de7a119ad84468d2f496487cd5dd94bf0dbaf6260569f0a0735270f8632614d89a67666784cafb467c664ae0b31e27d4dca9ee596e05e05034988369abe99431eb9151b98edd9b52d7aeaaf58cc0f0a88483dc06b769985051532c1f167b0012b6520c997886c1941c54cdf7bad5c411345eca8d0dd7c9103999e154cc35456b21fa753d270edde105609432c546de1f38790ea4ab6b49c097ce2e2a83a0c67f98bf8c339065fa6409895be5e31c483171eff8de7e85aaa040678cae66e64a583ffd8d2e07332bcc18c8a21d913161eae717905e0fc94f10e79f96449ad1cc312d124f83e2435238456297d5637eb1a2bbe8a68c6d1fe490e06b05430d4eeb2c11b3d93afd95eee82e7591872f544d176b44267611c1bed0596f1e9f4b20603e00bff0f1701f672c987b1df0f673b994bd655d2a3ea9b3b042b249fcae65340f381137e34639cba49de5be81eb329153617f0ea0782f4d6048653e96c9b27491a074fe02749a33f0ee013488c9168b18c20582bd6e5e0320abf521fb4df547c4f7dbaf94c6178e2d0d8907ac33e77031e9bae1a80d017a3099e9ba3c16a18b9aedcbfc8d1924618f7ff58135ab0cb3f85295b8766ed0a1d08f680c539cd0ed7b1b24630bc9f5c0cba555f65c4e09f4712da4941a2c733ab0a08f79fa6b2681ac05035c24e300ab3ff54dad61eec6865cde08e69ec735209061379c693f85c3688f4a6e04601fda6cbd0c028a4a80839f743e79eabdcb9ba0c8c333ca605f3a16524d18e3a25dfaf2d06b4cf109e66e5d92f0af1822139460797383edb729bb35ea05156de7ef609ed5ab3890b5b80c46a278c392c3b75ff090c7b77f5b1f323e63fc3fd47d64b9a250ad6779099f6b9bb05073709c031110f66ab29734d77238e63859bfefc960953f83454bd809375ef259dca1dd030130194b4f11dca8767af16c5762fc0b8d300b9e603eed9b68dd52ec723029430840177c555e64fd7024244562b21f7aaf4bed96ca623b58b4e9cea3434401227a20445176bfe361f413d4194693a275502f6ccca6c2f8f36f9db94a6e7e6924e950d802e1e22428971f82e5d68275bd84096ce901ac53dc44ad6dcaa3836d9a8ed078be51c9de93333316eb59a5fcb86eaf42164df35a9a5d4221286210078d5cf0779f249d7bbbab6c7a77dae733acf47082617d8f76b899378d8200477d178a80fc69e37a4463af1e35b45ed62d09a2a53cbaf5e26bd752f0321f091f1e6d9ce00f0587954bc47c8fa1adfa8b6b4a8e89d593f2acb8782006fc12a50a2c9d8e60edb6744f4223436fddae4520d21dda1ff0c019cd674e8fa0f7d86e20502ef2c090480c96fc5f281c09bde6b836fcdf9cebebe890a9c7d32b2fcf51dd184245d02b5520ab2ea5dc52943ce647a24d0f460ca71d5becc99e6008f4e4b5504a3a4018548fe0df0b71550f15f6c780c7a46f2e58354080d83748a3833bea7295a47072c523fd2673646ab48d9e50e69fdf1b5b6a6268d339a2bd409db4dfb5a580608e6135516e2eb4f825d0aee624639c765f57aab35bec7fe6ccb544152edcecc042bf6919f45cedc9c638e15919d511fccda85ebe6c4b8f3ce48f98dc6114eef0951036f38723bc901ac2a27ae2f399e625e082a7da1e8fff3e53ce08ae798050b9cb7e886bfb14650b98cb2135f836f9991c714f88e647fcdf4737a8ab074f60d5eebb0d22f1a6a156f41d47817f8e71a213a2cda9fc9138b2141b6dd1bd1b40a789adb540f827cd2c481f27904ae6a3c2ecfcbf8871e4cc4886f2eee673d280954e12ab5491dd2505156a77d0b9ba4bc5da27fac8117d2df4417d082152a0603fd80070a826088f496277e838be997851aeb56d6d0684aeba687a62932b4e20621a646f3f799526760c39a0950ed7a43ba336212abfba34540c77a28d9f4bb06ff979e2a48aa1f7e3cf50353355c19fd124f0254c1efcb7fe6e96dcc99d36907fa24037bbb55bbdab52a3df1ee60a950ed23863f12388873f3a96294f21fcc0a7e0c619e8b8eaa2c92eda8c4ecbaf2e0e459d4f956f73b66b126cfb123a9c20b6671bb24889ae896edddac4dc7ecaf6ebe6384501ab97b6cff92b6aba5651b094a267676f9a5190a4df624e4418982725d5922e6097d7edcf9f4a0841d873809e2c3a8452222627a30dfe73f709fa4697ff5f024796fd5073ffd3fbf0d952102a6e76d299b2ce6dce9b27a5469ad875fec609126de610b0c1edf38bbd0f90d00d770dcd629b37a883b0feff338c0ff306c6e8752c2a821ef4e19eaefac504e08695977fc2405e2db07415ea9a157f5f27488a735c95066bcb050a1c583f58607b0796c0732709efa610c18f84eb8a90b754b8d2fac4e77ecd50c7211ffac870390405d9e281624d87cd12bcc435dfa080177aec9e6b5859887d1afe26898330e1fa4ec1473a73d78a4ed539a39972c61d87ffe78d915cc95cd3e1278d7177d030ad3e6a8d3cf1cf28b4d38a529356c58b78da55e5c48103a801505a040307c0207c05942b29ccaff175185691046060ae4ba48a4b7b804bd0506e361732a7b0bf71d2f6d35bc418b1fe686ca6d3915f0c5b270872611e3dea817f6c49b4d0502ff3ac42ee57dd553bb74b0463c1a04920c549c74b2acc966a262f55cbce7090eebf981b139f527c39b6aceb80905f186629919ea8444a89c8a3d8192b5bca702995636ab36940ef308beddeddfaedca013a37beb2fd41224b6fbbcfcf4453b05d946790e7fe83f4ac68ba7fbfb6db4f535eff2cf48dfb8ab991813a6b4d87f0eb5578faa8daf01b197582c51bfe2d6ce1050882fa96e56fa8fc968af560c7c081f02d4d3d0440581f59e940e9379ddbf4d41045cf5759539ce8f4288933c0b020f481749ec39ae928a674cc65f61f9aca32cbcec456b9a5ed457e900e772f40e1be35221de2c1351f192521c4d730620b37ed34d9ee4a3ac7c6e8bb783e87d01fad72001c6b344eb08fc5e1c2cd0be939ebbc1367c884e4e2d40267958d094068ec11eb28fdbb43b57cc5f70c05dfad08114acc8b8adaed02661b5cdb471b009b86de0e47d3e690fdaf72cdbf0c078a20ab46c68a68fca8c6b7db0198203030d8e24064caacd01a80b20aba98e3d3f3a51cc53c998fd9e13efc7f1764507eb0861ee72eaf6daec8b1f639d7314a972a0d2d28af36790b00f851f51b3c10a25019af906242d45b508dd511fdacb50df785908d4dadd67cd06246c1759a6f8870ed73acd6dedfab49a0132df0c22235bf78050c5c44f37067a317afcf493d1e80bc0a0e9890ad46377cf45286f31d0fbdab7c19862475899df9857d8ec2ee9c0046c9d596ab0b81b2929445028e3d6e80c3ee6cad52bceecafc83490fa49733f02e5066c618c555963e38b930d50be91312d8453dd719481714d3333148c86d507c081fee225310379d1b58a5542511a5d6be472cef944e9697ddf22e143b3100bfeaee842eeec4f27e344b381c00cecd86401f5c0b8c9d2a378963c0b7bc54d03e1ccc05965a4a084e0e90aaad1fb9a1813ec90452c85b86900674adbd1d9f50b7eb522e8a3386a259f12ce42ef2b5c7f3ee21a0602e7a8ad330a3a4f20bf5802e74bfc22706f6fc1d4e5e90b3fc0fac2fbc687f7d1a89e4aec13255b6c930309c9956d81b40cb57db49f7029e029b49de5b2eac7d0fef966cb01461636682801e55f60c2d380fe1826725718c28315f3f7d35aa542de5323337e47b6c7923505611676679c592eddde060f26b4f374410a4522eb35f064d071081e0e9e64b50dd800e9121b7db458900ba7c7fe160cbcce78eb3904fd27a05378b06b7d204502fda82f24f719a0e9adc6515cb75159eac217a523d7d91b7c258e8f72a97ee607ddbbfc0a421c37654716202841b428a9775447b70ce124f4cd92ddc3867ed006ccadf6b1749d19fbeeb11a13276a7d6351bef5696f107dbe5459a9116522e3095b0a31f0eccec9d707883517c4912fa89c14c69dd5314a12969b78bede7b90060c204a95ee2e62723425c5355dfbda1e6c0b3d56a8742d52a1ec6ad471e05f0a7d9e33d19a221ff68085df2ee5c562c6e0e204e1236a5b0ebf43584bc232690802c3bda6bd812ba50e521334c5f69c3bd36a5d10afa89493032b3756f2bf8300bba5a1266115337e3c30839b58a1babf35f687605b5ff5892a8ca5124f25fe09d7e05d56341323b9cf6862c9fe76b5cb9e23094591f32d5b9d82a6ffd139cd0e10a05627435f7ba82099355f1833b43184b15970748f4df35bab0cd0b3a927052f2674dbe1f68c5778e04eaeb07b0b8a5d032c4d143064b4f2ba6b02c0b34a03a6bd5fda8766a42ce12917dd4a8cebb0dc120148ce92d6fbc66a98b00728b60e773e83767cdea8576017744683dcf211d6687e174827445418536ea8f8277c0ea10606d2e2e78afc78d00253666ab905de8fffa97860c9ebb9ab72adc7e89d009725cc85ad44f0346f6fc67561a6854c13873516722d964e93d5f5e6b80b42032ab5e8a9db46dbfd6854d5f515eaeac6583437ddd6f8e17a5b662e748ea8b6072e7808c64bff2de2edc045e520eb6270dfe865e6a83de0a967d07e42d710a90e6846466cf9605d9199e4b18253ed635c453285f3a98aba257c01d98e845a2609d056cf8a13c17966e06cff17109e3089cbb2fe04e87308c64cb85e330b9922070a64733849d9a9816296966121e894724ae138d629051de2ade2b0ed3cdfad09d1cdcbc0acf0adbbf36caa14033944640b4d4e1da76f7e0cff7bff0940bef80f370109976f394cdf02f63bc042181edd629474f9ca4bd6e5f777108545ed690b11d5f4332e5b8a04a7984e526123a166a012d1cf02cd63b3d386ebefe90f120c774dd8d60dfb36da55f6f372a20466c103fa6c35f004deb76505f1177980a508d58f83d31c891160ab486496a1e58215274ddb3c9d147fa882ca76c3b50d690a8a06d03195369d0c99bc096fad0672333a0f56ac38b058edb707d7a9836cec00f3a2d1810d4b9f5d88f1f5e4cb9e6c0d6cf7ddcf68efdd0f5c922d301d951703b40171346860d16a37fe8b13ac2d17ee4f3e8596bf095d97f48dba9b453c1c0705397093259a98681b44ea99fa44c3c89346534cb813d82e5632aedf0de29f019c2eb2a126b57eb6e2ce33a5cef25cd2f55662a5cdda73aaf60c635b0ac3560cc28f84dd01010300e847a91562fe654a337797e40287f4f7e8c5ef44d730be0d4bd7be57c3229d49e44fc13bddcffb4c74ebd0171cd51e2b49582c9801450204ec07084defb841cd1dd52cfce5599a76d3a5aae0a7030300bb338002757afd0689341be22f73572c5e430a2fe8972cbade9bae152cfc1ad9da8a8fe82f28f401dccc75a32d68861f8fa4a07f653369d0f3621fc0bbe9d65997930aab8b25ac07cd079d43a0b9cdd4a1cf1cce46839d9ab0a26f9a02cb0307066521f4496f110cd74db73458078ccf6391be33cef79182859f04a93d9a92ea48d384c99e48b90282d775145370ad05c3b17dd9357f4149332efc4498bb4065b5b51b00de6314051ea6bc8f61492773ccef7df9b0310c210b65ff7d4dc41d79388b5c4cc6af7c01cfa3e0a5144414c00fe006fa1248891a86beed1b7ad5a055a8b85a33842aa80f8f8f91fc2b9c987ee245cc00799aab1984131d143527a0e1c1a123906c896c0d7807727c60af304bc2034c129cfe002e0a72ac225a2e13b050391d24c1629c0de86afaa0762d65404530585ffb1090fbe04e314ba18ad1001641e82babbabc01f5cfcc92ed3dea9c042b8bd12638d41b909fbec2cf471a6c275da8ff2c991a08ff2f8d0ef14a5f7005dce8a070ac41f9ca74d9f85e5cc7d54dc13277e43c28094a674e4ba96026785f00a1309a1f24291cbd0e1c6aec09b96b43aa18c0b25104208d63cc2cb2d7487aa4045e75e61b999ad56d7412f174ed2676b142523be20c67672c1c55632e379171744ae0ba9ed5127ec93457e9b240231ab97a6bb40e00be85e3ac91470e13ecebc8f2f711592e9f0a5c51fe87315ed53e61e182c3b7062cb071b7fd42976670a7c0e939cff16f3efeb9749550b9b4c3ee4628afb752042da7e1e318df6075336f018f6306365ecdd6e6da00ed1407663a123a81c9c30a7b9154344ebb0056cc69acce01391cdc68b3b7369333b56babac7ba8f12d3c0ea18c88c7ba103ea701691eae77b3ff399c1319c939135233c991371c48b3e9039503c2b3303655740b8ca7823021002d877ea64356e23ec30a3ddbed8f311c084a00102e40e3f03d16f56be99c746c4cec5d855c7ef77587bc9ae58ce52a1e0152305ad3871dd35f73f20cf0ed7af7786d5d9366cd55e03964bbbffc2f0adb0e8d47952b21c1585666487cef82d40d6a879f86c4e1bb39beba3f680122a00d0ad0453b5f68c46469ae6e8a555b0ac2fd1b5dc78f505080479f9100d1c936ca0ae56f610bbae7b68842bd33789faffd7c2ee4b83ee5af164ed694a44fe92e040a04bd30cdd64126a47d8ff7c8788afd5e1704091c20b9fbd5be399f0dea62850bdc0104759f7475654766b09b7788d3e1a090c59628ae2a3860647907a125270237defd9a5ba63674e0f2db561ceca2753a7a7223497823c368e6a813d56a250b379abd33cad922a6853c4021c13a420069b296f7793bfb46974a597baa87350d226ec6fc0c32130bc13519edc176fccbe844aa9aa9a3f31e2de4ab27d05a9701c30982ca75fe915fbe58bd1450ee11090e3fdbb6da76e2fef320bc0cef7cd807ba545ccae4b8bccdb79ddf4c0b30aa01e845250f16d2723358db03aaba996005e00ee290691f63ad61fb224198e56f410c348ff76841f3bc2b93c4454aaf080c4e4916fe9d734c952848135e1c63dcdfb26da0f8c4ad92ad2056df19184ba005d612e9ded7b8a67c6f857bca5e0aaf4947966b8f2591f40295cb429dbab62e07ff96cb9af45acc89b3b2f2f9c3a87de522b337c066f51897bebf4b9c2df5a60861ea24083d3997560705d03df070d9cabb381ff9828f94c3fc65af8a19e51104512b46c95855070e6a418b9348f1d78e76a9c290779c0327ad2c530a9813ce08b51dd2df8f341354ea419ea1fac81251a704bd29d347b79e12e33c8a4012040f74c28da721d51c73d200b7af8331e91d183710203d07d3326106895c85abb60cfb0f7a760044d58e2f7c4492894a9db17e659b303b15b57acd5c852b1e636a0e2665a972d2878f3d1999a8e64eb9236b9e08b39a5436bf69a86304e56ee0030cad6b4a9c82e8621fff95554c6ea8d6afb9b691a644d570c34c20d69b49511309f4a62245fc6204c41b8fb2f27f3227472ae1b5096bccbf981516719159ba810254cb039305ed4b83b8e97265203a049707a76f5d69b2f836e298f3f117f0290da44bc9a371bdea377d9274d50a5638fe2f77b0709b7bc9e22a02ada43f38bf06438828e6d4e0aedc22aff795df50934bdfb3235750fe8eadd9d4a542a0ead800a8b529269e00ab39d3b208884bdd3c1bb2d491a1eefda07e6886ff21f031b10d6a1cb099b670aacefc5bf51222b7b1643ecbdce466309653ddb712bc8a65fd0bdf09c79c0e125a2ee8cc038e9b185e0c9906fb76ac4188aeb760cc87ee1f500e1213d1af889a245231aa0adaa16865d2b372c8e90e39d6691859b9bf2e57c407c73c43f7b84856d6e26266916dc7c640ffc9fec6434b300b43871497f680600603bfead9c4d1959291851a4e8adf6d7ef6309b37e54a67a129cc14de00b39308b0bcc499ab843d969442cd1db6443bd0c5945c6d36c965af3125be4c4b93f704e2d7e8767dd7551249775e00b348af8badad1e67c95434ca871da77910e95f0c6cfe063b4a339e8bf0fda3b3a1a6a05391acd34f29e94589398d351c11ef6405f9403954b7a1dcd357b9114476ecaeba72b6f902856df1dbb07168161d64b70d68fda987397d4c11e3e4acb52e6b34d701da68eb7af0bc5dca60900c12354704ab2ce39f67e6a98338a6cd7ecce590e6d40f081403ec9a9889271bb4372a5e01b46c547bd3a667cd5e5c2f35d2aa517e73f7bf9eb55abea1efeace25b57b2a0cf700028c1237040867b6b60051a32e8729c0c4430478aa67ff8b6a93c627c40ea9d027fa2b30ee0e93b6f937e252955525c8091956bc31eb539b813cae5fbc04b93e4b8b045fc09ecf3fe79d1765c4d0160a1fffb36cb12e386d826e73807d004b3de97c96bd29829f5244bbe74f76118027ad699e2f66da9d318961e0a2490327f1d757fc9005ebc3008a4bec7946ef692ebbab986f7854fc2eb02773399106e21c6cd0af51a6da094df8b017fd943cc0c191df3b8dedbbafd0a7c3f74dbe0e4a538abaa4def3909ca7c6a5c93d37230237ab903e45eee6471c1e505fef8b0cf173108a6127e5017f75341f15d08947ea6b32ba5d91bed3861a83e6a7ed890c2ca1c4b1b48193b9b579e8ed409abd2b702d78d2ab03f5fb0c72758a28644e09f32a8099da4b6b05feb1bd7bf20720fbd5bb9a6dedeb822785dfca363f78ae0e53441b46d77d364118e5c3408f6a6675e1aef56bdb490f4dd95e91b7eccde5084bd5799f7904eebd47ea9f7500ef803a0ed0edce33921c8d64239c71115c61045b1507ab6b4c7efd8135ff7290f647a6fb6770dd44801386e542d6659ecfec0d301677a504c29389bea096f699ce3989174c4d576b1200737bf35187e6f0db0a0c16c7a6bc6b5ea500721d437e398b16bbc0818f28c47e90e2dd3d14a879ba0d14bf09dde41526f71019824eb410b74e43eb3a0e76677bc4fec5d20eedff200b91d506e90ed00adf535df746f22885715642bc425ad03894224160203d2bbb0ef70b23c6289161ad59419742f227294032a78ac7166ed7bd3fcf7faf7b2ed502074e4d17bad33bbfd31d8692238f43079d7d4fb5e92a2f74583ac0cf9be3df0e4c1c5ea6b5fb2fb41b6d0b2cfcb23fb0ccb316529ef6fb1feaa90025aa86b80069c1cc7a7a47dbd32d268f2afe7dc7135c9123a9a28ec8cb9db5b3fdfb2a820b7df382cc5b962c845ca92132d6e3effb5b7a78f12433a85e62693b2e81baa8078bf6df3ee9584ce4306668330bb7625b9f55e995640c839cff378855dc3e6605c376ba4201fe0c18777d2ff98c5f59f012d7471fdc94cb1f317e9b081e4b250eccced3c3e24f940d3c0e51f6fad44d9fbce3a3f41a8d44cea9c06a63a2c2ac03f24faf7af58de7d54f52c2db72d73a9f045aa940f8cf3e96718db99ead9ad40fc5ff99e00cc19fc20b0f4caae7f9f6b03c2ddd7c105d92157f094933c2d1b704a8a6cc52113a1b23b25f458097a0da568536995ef8e794a88ae3c88e49d92d08c5d0b2d4433ddc0b28e741c1da90876c81f77b4af7229ae53bd172433d45a501ad5c65ef8ec62c758a1044a8ef5d086ce9b4d7af97d69de2425ceea3c79939076ac2ae8cd9d2e178688fb1461bdfec2c4ded9b4d18509cd8d15a8eecfb8a730db1e6507ddd993c72f0e54a731fd6afa225e51c5c1483b4b0c6e9e51845cc1908b8ac8b8e4fa4f1bc62402c759b3c7b59cbd6d71baccbb1e883248f0db119640633994ace444814ab140804e9bde53c5a7d1279cf711f9c7eb3d47da09151e10d2addb78e055be6d7a1b303544a10a64ba02e2545eca5b6ff741d8bcc4db98b0de347f9a56997817eb627013a27c185ada17b4c027289263cc36af840f9adfe0d43b76ad46d668de9ec0ce7d4a1e33113786f935bc5c95e0d3d05c9f0e363b2055c416728b8e5439154becd4e8b3b4df2f418999fdefcab7ba80c3325a5c71003ddf1f3730eea84565b18cdce7fdaead6a99fc98bdfb182f90eb627b87ec28801f6c24b90d0d89ad5f90cf00da0ebe4bfd4840afc081c3aca0a661d93624e91023f5db6123e52eba574d76e0b1179f7e6f6c37cae330ab4dca30de6c6873ac402bdec1265399977d48cf0d3d47dbfe5ec96b0cd7c8b2fdca922390c653e49e70e73b23f64cdaeb13e7ead5e9b15d8122decea957f2aed9f71f7ae03d70c55640569c0f776e1f6bfcb4b27395157aa499b46b6121c570934ff0f2848cd1d1c2c0790ea267c661e816c465115e1a81d115d8bcbb950f7a375c530d7fb4a8c7b620c8bcfe4f4c63acd00fe30dece06f29c5371c536ac0caf91d6dc881e00d8b8f70cbcab1c3663e07803cea73f86ea461deab1c75b76248aea6152ee730a7d56fc0d0857a41e4c6c77cae72bc8b868202c7ca1dbfaf997294ca6313cdb6474688805ed1735c98048cc316fb16a78f1a51a39468f4c1ec850d8c1c30fdf1eff11a207271485b81d8959e3211efdc037220bd1de4000daf8c4982a6f1c53f7e4ce230ce35d08b8492ae14f2b3cbea4e34ef8daaeec3498c6d6a5f7de06ae79e6681f079d3a1767d5c7bb49724d349729669a91e3e8fabbe565c2d2f5808a658cb0a906fe52c1601e933c25864e76818fe7fd8ffb90c6d1ec9e94458fceb5db613ed00e9f770a283f1545bbb5de7374ce4a751c131cafb631e371245b7bf3003f6bae06fe0d685e00bb81ed7d66ad9d73afd5b7a05d7a0782b8164e211ca69af3d89e01fb52ac96b1f71372ae54db73244c24b8f68b33f03c150a032969e11fb5fda906eaef3dae2226bcd99a085d1cd509b51d08622edb5474ace3fbbc8faa88ca3e0c5e9263a188698f82635b82eb665a354329a3fdcddc591a8a6eb737ff944cdf0effd09e1922c744442037c7c07fb8bb92b97549f8dc2783c45aee4394c8ad4104cffed76cc280bf01847bab2579d92d57627a835e1dc2c52280fbca07f849500cc3f056a6fc89df39055578335737c013350f3650811ef998617d713bbc1c1107f5b4ab46fc11a4764d111762c17f18941f8b706fee54537f82c34efed627690f4e83ab5a6b17dc0471576b2e85894793e635b2ecd2c008beb6ce0e1b1a750d08a36b81e710a24ada5990d56900937db3e471adecb06f9c78b20d8c0848f18009ad7e0000d383b3214aded17407b2920e906fb0a2a762d423a8d961b1405f790b5c549cae9be9294d3456642c225f646f35848b9ab332422248b1512eedf4060a40bc57df6fbabe3a57dde3382296f5e5c45a62da753cf4a42d623fb67283d5087cec9a68bae7a9ffcd6a2af4cac2957a4d7d895a1f3e51a0ce24c6c035a75b01e1e51436943b6171811e43d91329718041b64b81bb0f4bcf49076a4b8f8cfd00e08800d7c3eb22cc46a6cf31172c20ff2a0085e7bdd508e2235d80e534c1d5067d6594bd937b6edfd0bd190baf6ab2a96679340c038ccd2f5194293a78e14d02aecbea3589f7a70296cece754f58d20d9fdd317d3b8b7455ae27e8c08df0e80afd423d75e311cb8364ab4404702b9f19d3be8c32cd61322309d929062871af0af6453c2a45dc99b3b723667358d64f205f23a4b9f60212f1464fafdf3a994e0f26efc55732992623186135bfb5ad53eb7b19c1c20ff80f1be94c0a68a2248108f16a1d9476e69b21d7b2e8e45237b4feea110347e266b5c12f904fcddd13810bc5799ee53c47cc9469fcc71d7c670c281dfdb37da2f3b590732fc8c1919697045d1a8f76003d5a7f1a0f519ed8a862cc4bf267a731151166089d74d1e033f00afece9ad7dccc774198a8dbafde6464f77498cd25f073dce5c6e302cb4cf23101590ff03ce9d837c1d853619f3c272e3f50868c7e4c7a756c251cd9c2a5d116071ca2012660b7498487d17fa2c79b98edf53b7fa77797271d76e057f050199909f343d5e9ffd52d98df35b722ae7a13c8b4f6c9c5e5371d0a988cfe7fa77a250cd7065407e6e7041668483bfc910af18c948543abeb6958928ab32ea024ab050fe0ac4f8e00b26e065dbd9bfad0f6bcaa43308f59b8e9ac87096073c5ddbdd90d14713af4b470054ee3bdd95cad78e22adb63f0bf7a4775ba69693ec23bbb7c05eb3dcce7e3ebdff42d0e828500bc6276a7ef75e61c992e7f573bc13004873907e7c620856a6d4d44aaf0e61520fbc2c59573cc01b7c5eb138d92b40da701980c1561351508a9a6f1a0289c2c2d547d63f294d01a4b4578dfea8b9649aa87b804998462bc5ab2d5313079f42f91ddb31f6eda7bcbbfffb024c04b25841b88780275fa3b145e089c079f41d6344fb73af2818316427e864438cf2b899b9a0c60062df1583e756a669d0dfa5f54f530ec2d6bb1777e4e90fe3c845794ff8c463b02cdf42992f50f6304df2bd9036fcb61b32abfd768033867ac46bfc08a90bd3f08c85b6dc30b63687948fcd4fe63391245ae8a83ff33d01a23c21d654f55d18a0256f2b28cc791159ed99f06869bd556621505805be8f151d00e6f0d89d970f50e +generate_ring_signature ff48702510ab57e5c8d1b6e81acafb78f7012e10e03ab15b0f1827ef34042dd0 82eddeb24599f7945a021098f99c7a1cb6edfeaa937e824e2f529708dc25ea77 1 c2e3e864568b78db05944261bb131881cd05c85c69033962d3c4f7e2a25a98f9 bcde8c2f9402b56df766a132e18cea4bd2d2c70cb537b599c2e829b7e3af4700 0 16083249d8eb17e3ec2e7e0402bcb40fef1063cc8803aaf787f0d93b86e2c50d4e5c106acf41a95a8e577c4d5072c253783e86cc6a33c85c02b1c2a77f2bf80e +generate_ring_signature 417b4e63adf44a5a461ff2ff030a1757fd342e7f93ebd920c25b4c8e6d740187 bf738513972b08c9078a1b21cd261df99f52670326ec59dc94279e626c0ce9b2 2 f204e5533b350d6232e62308092afee62c4ec0f4fce45938a2bc17ec3bcea3a6 50ccf363fd401389de4f584b3597c3893faa78316cb8799ec6c20a37596677c4 a1bffbc16adc7bbed80ece0ae2b4978a0b3325257a96a7e3c3c097584d096e00 1 4a55d011d013948e7923b11d22f762459970cd62fd289ab3edc1f631a546da0675c7f877d8fde2341b3374c9947aac2bbf72a2b6dd59668ee1995b5190973d0583d9e293651ad7e95861357cd9da16fb57d95114d943b16882eb1a699c905906fef023a873548b92e025c25ce2792a7c7809d4ad6176fef7699c6893fd23e407 +generate_ring_signature 030be7e561c24a2c9a75cf63dfe5bbb49addd7023362f7688d24ad9369967bea 158ff2e128941383275c97ac9c0206450dd2f16dbad1a0f30b045cbdbb76c2c9 5 205a0dc801db4e3d217a9a9e91bad16ab78ef983f799b8364ff9ab44add8ad6c 0a6f06fed33891ad9f778c3689968dfb1283af71b48c1690dc941ab115ef4d69 a19641d3c2804dd2450d313d1ab0ea95b16c4cc1cbdfebec643ba05604625dbe 93bbc7d65e865f094d1f7e7cb5b07f734016648b7d3e104db00b0d8899d19e8f 68e6e3257e7e974b4ceb1f7c4461950e60c1e5d1d8e935e02f4116bf89f5d842 0e0e85834add085696b849ea3ac806740cbbf7e2dba4002934fe022efe25f60b 1 56ebea714cfd75acf7d27038a622f8b8f319ec60bb44cdbc0a4e8e0e3b47560948a8d906f379380f1d4399c306bb0abf6473e368250307243c3fb62d6fbba50b1ac7230b005602fb7b6162023683bee640735bfba21da29b0e6f016fa60b4602c3e85d17c7f3726c9724f22720280668eeac8983c84c10a09cc8b5630b735d0df316c2981e3936b5518d4956ee8a460e33dcb4c40578a054ce0cf2c32a708c07edf12e999a005030e675a4a0d40760bea4f0dcc7e2cd65eaf582df22f521c50fda5e9236cd8f7d671ebb5edba063b5c6332e2284123c4ecf67760357be1af1079fb440483d44351b98c6ea0b3b72f44ad7618b252051e18ec27453e2994c4a05c9bcbc498113f6356c434f9f16a4f53c19993cfaa0a66bc2f2289abbe015500d1fdd04c048fb3c8400999d88919d369029cc97718a674f18741d9adfd1255002 +generate_ring_signature 676d9d3f03cde540717f27be188a1f3f089b9de4264a66906be09441f3910378 1b768edfb4e27ae223a513c913ca9122e2da6bd2f88efa1718149068e6a000b3 1 fdb9b839c1c33c111ee96c1fba14a6ba9aa851737e89a629ab9be2c215c1bc5a f8eec233f256d39bf9d6b86bc1858e47748c5686abed539c8c44457cada7b202 0 4c0ebd64b0d7d2d200b9458a34cb1d2b881025952a9ce79c6a3bc774ea1df10662726540a8adbb99aae71c5cb60fe28c62560fe135f37959785358032d5bf908 +generate_ring_signature 6b1284d1a3858c8b7932de78cb05afbd1d97616929e31905ba975dba70d7507a 4ace77c3426f132a65348c8f2abc450307cfbb5aab6b83748cdd9da470b37162 32 8e02737d48fdf2acb36863a26c00930d9d7022156fea101760d6f178ce30e0a1 7dd3e2d772fb4626db9ceea8dd6e455d147d86a4aacfe0f8dccba90937dbc2d0 413747d1db30c5a9e8723aca5d8e2b105c8be221c1bb4337222689f184c4a6de 297bb55ca843b5b0b291ec07dc6af5746d7348920fb57138bc2a6ae208239223 eebe4b70a7669dfd49a6996775053955a4f03e98b74f1ac029c1d3ce1579fceb 62d36715fafc49f61c615704f85ef67bf6c82c876abeb029e875c3847e9e8d21 c003d25e8b139b9a40b1e169a8006e335b7cae4a3736026a4ce0ae55f1d1154c f89a5190779476851312b1297ff0d8bdb0d6032edcbae23b48f9a8241fb7b54a 22a64918ea6e8d63399c1837210716fea97d6022c3657c869d7c9255cae853eb beb2dbcc8a4ebb25afade190a4b396df94791d873e34bf4fdd04f0a5a5f86753 51779058b46699ed76f017f551b53beb6259dce7238fcb23f62553638c0a0721 077f6bc7e6eeaf2cb3beb60ac00eca6412980fa9eddd932b9a408edca4981ec5 82da8fbbdd764bd252885dc704945be6465fc45cc30dbe3eb6fff9692ed05210 1df2acf66b87bdcec1b2d8e454e73fce0e6edf521d9c2ce5d8a0a5d8e7c4bfc4 c4107473cd90199469995334065883df47143ef21704bad6fef7db88a11e4799 616adf3cdbe84f04cf90b894eaf7a3e420d8606b3f5638feb3dc57684b63f606 180972d031b5a05c43c1822aa2f391de533069c98bcda58962e8f124a5816f79 e7fadf78edcf92bba764a30e00c626eecda4fe7b732e588f1fa668dfffae6425 369a2989333a13a52af60cc2400536292333a78735b5e2cf12ae1405d8e29c70 0036cac6d8ce1a2a5e623db6f9dd8eaa231184c85c1b7661e4a391288d82e7c5 dfdb8d0684014a38ddc314d51c948bb1fa93d2f491a0cb71c0119e7c3c677023 dae655dc8a1f360aae2d2f97cccfc95244703e91841372ad4e19ff62bf7ef77e 79d75438920510587b7e8935b56e1182a6b1736ec651a624385215d8d461820f bc7705ff99d0becd021efeb88f032a9f1a8afb6e4215da04aae62d4ccadf4b2a aa92d90286a2e97e8deb50bd1f88b0c17af828c037f5d6ca756461ce5aa50947 8a93227c8c929fe5668877d141cbd701b9221505a0af068d7de66651c3633b1d d8c07205cd999930a681e3df4c93d0f0306d2f7af98f54b7b0f9cc6c856164f6 71027190c84d4ecabdf9c6e5e4e441532b31da01076940d9ec6f8212674954c5 16fa8ffdb9437d1ea18c12820a0bdedc301adfbd3c525408784a5af4fc6334d2 dcf6a3df67483bc91e2cfcfdffbd967f1c50724d15754c08f46cb3afdd16775b 6982f0182fb32a36fe007faff4f28c411a686f97a40b44c4c825e865f9487400 2b4cc6cd754512d8987ebd79a820b681787aed6cb7bbd7cb5ef792ccf181b228 0b9d7dfac5f2c2203dccee3bf001cda08ef2b12f8cb201e2883df36f61970405 14 93830f08ae9942cba1f36cea473ca8265e87e3bd34de605e8344b6f92bcf970746ee8b5dd837f1ee0eacfc99f9811fe8ec8a2d7fb77852051148bf711dc6020c101d7ac16d28afa399868679d227eda9c93e5227f12309b17fd37e399ea743021b7ab1b6d2ee4f7fd4ad3af7dd2725fa6f4320c1464f428ee30abfbd8ec75c00ae16760df49b4d71f966be497734cde62bb7fe5bbba8507986b10bbc05a74304fdfc3e522287f38806308a06d86203aa211db6c1fd5696ff87ed7c3b759e86059eec15dbf66326e00a5aba1d140d17dd3a1f97e2dcaa5036d26c977d68aecb08d35b6aa339bc09d9e46625d1e7047649c3170a557b7751ce32b77ee74ea0d4050f1189a1de30dcbc65530960ba70d46c16e9ff73277786d8d2945dd193808b0fff56c591cb9f6462a6419318037aaa0bc356890f1f4042f7a1743bed497db100755dd5a649430e5799f055baf7eaff72019ebb3eb10d955c56fc6324a8278105b6a0ced3c827ff6bdac8693bb5d51926075053e957f5167850b7af7105f0a80fa69e67bb13f411d66f3f0ad07ddde933db701a1990f75b1fb5fbf1f93e08e606d369c896b30d39166544ae7e19c5b0a958de00cc2f403d7d4a722d2813f60a083dbed9cf6433c5338d10bde8d56699f89e4b75e255872e0b852de4a0d758ab0846301f7f3ef92152c9dfecaaab1e5b253ba9ce3beea6a333cedf9d6afbce19030ae3377565ea9293ad5885a62fcc86bd9ad24130f7cc2a21627f701b64d4fb0dbbc9a1917a2bdd7e386df85d054ea4b9fe82a71e52b7e9526b9c75a4f51e80070ebd5b8e999d6f1b11221f60971d1e8793d15fe9ab02823393a413018e36350fd4664808044c50c0c0bdfc87137bdd6b6afd6fe5d6527bd6308d31aec39a1409b7327171cf05654b9d80ece30e546d605e8a5092f1bcf7a6875ff29ed7f71a010a0032562bbcedc9a42626e3310a2faaeeef9d63f2d533858182e9d55ca3650726a767b8996965c873d09043b4fd2a26023981b8fc05a91ee91ca9bb790d6907415e9abffaa10eece461b2e4bc806ef718e6584f81053d4633cb53acfa151206af950a27a616b979b4dd67726c6992eff5a6b3b22ab7e14f6650f38d8618f5008e4c07207ed936ed87679fee574a64492e938dfd738813ef4ce02c01b0ea77087587bd2fe291dd0adc371abb2adeacd97de12856fdf3e4033a12bcae71388c0341b82dd9544ba729bea1c0f3e9908536c00ca6e08babbe1a82330eeeafa8d1060e1f11ea98f0522ba9957dc64bfc729803c94bfbcc387826f22d07886b9f430ae8c483f4128c3c395fb42edf8b895957a0cdbeaa2ddcefa4a091f3269772360639b4ff7bc87a3a459e1ec7367f31ade3b6e7cc9f3dfe8405a8758d2e631e9202a33e79ae93762cdbe2458636ffe68c17633d3b49192d062f26daa713e179d90a4eae783c5ad5268d734d65278b26653d564eeeb580ea2123c1789b0752e57d07a3a71d2ef9b0ca3a29c962537b086ae41936be90768a1398aa4c7732cd15e208536fc831c214d9863f224f4c4e46badae6766ec704bf54e4aef06a720900ab045c0628a1d3dfef4d6a60e442a1db1dbaadaf0440b7833d670a515df088949309ca6e9b7f92b8f7bd0af0e2ddcd682ea421c5cf7f93df7fcb750a8747a3ee0c09f46ec265095cb46305364d9357a2d3b1bb10993520b9c54b78649f6e65c8d90c5e700cb96ec53b09cd057cd4265be224025d4a5bde024144e0f47ee09e2af7079d6445e4a156ef9819ec938dde87990854fb2d66c7bbbe7c32f8ec316d0a6d0b945b43608a0bed2b8f792801f40b24b9126df545aab60b1c0b7cf63ed885ec0743995ff7bdb55a930d8896352084cef8b3b94313db1bbe966de158464ffe9905bc08f9b7bd261f437bd476ea4d4672847de617d7c1910f525562b1b17f13360496144ee3e8c93bce388cb9e823b98840994692ed5d249e5533a53a581888f20813f98c7bc7ebd3d35e9b481eea89106ed53e1c0957f6a6598ba7f1efc6917c092647d11360121667ea91f0839749ab84f346a7f35c3cfc06abaf7186ddf5fd0a3b75cd676d0dcd6f2f56a1ca7a495b625b72307443dd26c065eb41468e703a07aad60f535e2e7f060ad8a101788fa3a9d0c1fd19d3472cae3a7e50013354d407ec00b9168e6dd72a87ec7c241e8d8b4a87b3c5d5630a684eb5e2c6fcd928bc01f0c195550db9795091c85bed3c8d12aaad12d3477a9b157afab9be4fae8d40033f55dd084cc234609d3cf469aeeece5d1eec7cced0c4f27944b2d58a8b9cde0135925319d6ebcb54325a34def61354e8c17b03e1c28f7ad2f3fb4a83c0f7dc034fd27c1125cfa56c1c9e334750c2162221e2db0840512dcb4e089b768ae2fe060993029aede6a16eac47326a5c1daa83f3578b58e52fc36ffe0d6034c6124b0bb59fdad3bf63b3f731d0a17344e9a3a9301b983639e8626971dfb2700522e404454ef5e0a40a579fed42c423bcbdf43c08ecb4ddf0bb647986640eb7f6e57e0dd2a02b03521983621cce0be340bcdeb03db94adc99e9985534276f4379fd04026260bed58930f997a461f24c131d5e689c4ed4213e28af4c28afab4e0f672905470b7e5d4201ff7c393fb3df17d16a6a744abb39e334522f283d401869fd8504dc00937235c5ced65fc1722a3ee8e6518ea38ed597ca8507eefe6a420a5fd408657fef85d221e008b156af68d2070e40c8a447afa34c90edd9daf5f9b032490e1aa549cb38764022680fde63bb08f49c7ccf42513349ad93141543eb8723c4057492079215faa1e457e41cc85d004fdb4b749dbfba20aa639277ec5ef44c360f98793539d94f2efff4ff9379d0d53fc891fda3df5a15aeb1265c77c916e60705 +generate_ring_signature 2877c2572723b6c1de6b392c1fd7e6b9b06be04a33b2c87ee27559a4f440d301 6be1605f102b685abebe480e495a6ee11b401fc4f8ed2c7a9d59372677c51133 1 43f61d5fa46d25cff1552dbd032d2072537865b23f7631b16355d6e04f57406c 5768afeb110865a5c863b330dde198fee80d775dbd33ce56e639cc750825e60c 0 50ea7774a66bc24e5a10c9c343f543a0e44b881300472bfec54e78df078b73053693d9fe590bc17a8435820fe0eb46978d86b5b510ec558821b9803c0a2d2a0f +generate_ring_signature e85672e6b328687d087e5e27a222d908708082526eab8d9d1ed9c741309ae642 1f47514331ac0da99d0bb8bd185aa97b127a5c428ead41a49a1df9795df80a21 9 5d81cfee1bb4c5d9f12691a0a7f099897a6d2b5db92a5ad6e4f16ddf67ed1546 b352738407c3d1b642ec14ce4e87783c3461995d4a759f213a302d2f6644be10 2561702e1179fe38a61701da52b2c6470b7ae222b7be59aac2a4850a00a0517a b8e838d471c530f3c0eb2b6ef7a5e91cba4fe4ae976f5b9dd34d388678ac2c7d 00e859925f2b79171a96df93368feca24d15d9160aa005d86343f69b2147319f 59a3b6c9ca5cbca2b1b27ee13a158565ecff5267bfe407da2bf94c863afd94ed 51eb2c685edfba3a962e435aae95b54f866eb1429ddc3ffc79573c076cf4a638 bccd0d7379c0efd79aa6f27515826d350b8cbbb6713802a834a1f34f49a09fb9 5b7b1cc1529d4eebc5fec0b0d478591c0bb0bdae91eee28219841262036a8a60 4442e0c6b2c4e6cf3f5a1a33cfd304e8fd6c0c1cd8047061a19d83525b50b30a 2 bc7e5b7e533f53512c6269a99f0a01cf95eb0276eb6f2b962fc8bcae02b38f04b23b890dc9e0cfaf7e47d39dd6ada779f5fe59230957b6020b755fbe3efbad0ee1a9cb4f583bf144619ba1de7b8f6f02b83ec26f00847454a5e73660cdfbf2048b5dea88a03869d90bd2596cffb17a074871e553e0e67078fb21636e0e13e901bea1895f03b55187de499c69cefd898bba90d20d20858df84fb797a63d39e20b17da532aa921cb15b417274edf5b8d633648fa8970b795067a17a372915d6401d0babf744ab79d3dbf1475934fa02ded36b7b615f924d90388bedc13a8ebb201076da73121f10a3f12e13b41284a95469335592463f6712ec909f1a568a1fd045b2a46664fd42a8686cc65b674395e3c6104f8f072d6f08fbdec560cc0ccad0c27a6826bc5546059c982abd75c4764d1c5fa7d90a8ece40521564dd07f257e02554a8ec68376fd1646056b2887359c1001b621f0235fdcebcd58932f069a12012bacd07c0aa0b78da0fa952b9551e5e5223e542780dda3885a527d82a46399075ef0697fb6087b8b0530de6e85b87c430080ee788d965932028620aa95a7100392978c3880ae3412c57fd6bfc7f21821becfc968b4274398d4c08b95b1f9c909d3e63647833eaac1bdb43ab9598716aed4622477718aea434e56589038f7f20e4e65db948a69e60eb24ca706a932ab39aedec10ce00d7652c23cc7578f41d200c9082fec6d13f3d74b88cad2866cdcb8ceaf29d124516923bdd7ca930c2b110799a8601a0c066760d721bff206f60d317a9c4aba466021ab2a213c4e70a5f80b +generate_ring_signature c960ac5d7f59051cc139e56674bcc5a3f0fd10f6d4525685133ad71da9505d03 d110909f0a37135c260f7198ab302d84f9980cb0f9fcc863b9865e0fd76e88ec 1 5cf0e409f6ce507bb1d619490f4501603aa0b5b11c2040681f5691c5e7af95b2 1f795ee966c8a672d15337e590b1d327632928970274de649f6d9a6d71cf8906 0 3958e3eb485a86a0a14ea4f6b3939dad078484c516eadbbd49bd0c8c78865e0da096ceec6bbd827ca6adce49bc6b6d9a2cc34acf37ad95e233fa7efffe69270e +generate_ring_signature 00480a8cafb58dd4fac3d4e0b78615ed33fcea74c26ad9c23d6f610dd131879f 2b7fbd562eacf805c3bf9aad99e90ecbd22a521dddc5030c2777dc9f139219d8 2 66f670b720ad778de82b255292fce0291b5729b4dca211c9f0def3ecd0f8c143 58a740373134543502818f2326766e0188b935de010814e3e61fa44a2d1944bd 118bae54eddaa218b8e7b8c9079fa5e42666d38b08677a64606708e0d6574206 1 e7d27f0ddcc67edefb6f34b35a3543bbc04bf03c525536eae7afc7e231a84e02386a1e2c5905a97f5f76671cc23bb6e6045f757998c424b6a1ce77ba0f04ec0343d8060c52fc351b1534cadb6f807502778a05e6ffb5e54d2c664457d3eef60c9cf336864925074c2ba3f34ad84cf4c764ec05b7f2b0bea34768db9a9e32f10a +generate_ring_signature f92442a7a535e39ea68305835767d33b4a1527539ad2a184188fa473673e583b 9f256d40277f781e035f26613b45660acd3f41f24a8179b60a56999115a83d77 172 fd881da6976a55847f9590a313352a1a24a7c5b3c8c568bc37b920abf088ebb1 ce48a50c2e6ce8edf3a295368278eec1c9b73b56435783886ebada20591770af c1533314426eb66f59ad0f7469d65e8019f902b053502e03b0dac849b7262414 284bae0be6d2bd1ecdfb9ad7c6857ee9a9ff6ed1cf50233e87a997ee2bfdf909 060002eb3f3b22f173e4ba9a50b9140c3cd5c7f7c3c307369d3c87466ba9ed8d d7d32b301bf7bb28ead0c666282fd23a0cf445219423997f8dd30afdebaae9b1 56367289441de9f8938588385f9a3a9f4f9a63f871cd3b72c471702b8712cc2f ea5c570305cbb52ba6d889d8b7a340e9335972d520ae71c9c13832e491567464 071eac248b47a2c4653eaee65e55030239c5a4477254f12b2edf3b0be8367ac6 3f3b3221a7fe3ed062a4d5af91c3246cc7d2bc30e9d3f85947970944058d6be4 bf078413d79661eda1d8774f7003a5816a93632a8cfba1f3730d8a127e28a9c4 e889d95ae3eeee41f3debf69be540254c8e68347e8b2d19d96b7759dfc14e377 f5dc25d2ce32defa29030708ae1d6d36673f8b41146cf3d89c7aa2f0f2b6e313 5205a7793f0a4b0d6e1e9257dcad29614c9d0f886fdb16ef4dfb4fc36af632f1 cc33629b3226d7404851fef79927fbb90c1df81cd95b7703e38ffa526ddb206c a37b372b2c312ab90f771dff4b2053d95ef3099c76a6fba984bf916b29b56341 f4044c8f9a9801a5a04a157b7c9aafe40c019207ee66d29fbd3a356bf710fe84 dee92a17e7bf79f7ba3d6b6d01b213bdada853621087e1ae1247b115a98768ef 0d696f6a9682a9f2d4c0e582925281368f16efb8c3cdb700154db78114377dd8 57728b15184b062d286f942fd3a163ba68cfca54ae3304e739ad3076e1cffce3 82ab1f9afa7f7f77289c00080b1f98a487a14a2f073cf36bf96a30440f20ca52 139c5e0efbbf51c5af2b9117b7fa58d6412d46d67eab6997857701230a94825e 4e17a9d3cbfff868b70ba95e4243a2da2df968ae5b0b67fcde527eec0c084244 923577965437137b6a933bd61505ccfffd228aae996d25fc96ae9db5b98d07fb d841ba1679e8ace379aada4547333b839b808c514f6d19e38895ab48b1a9d16a 8175b06c4a1b58b90b367aec2e81dd9c41db930a96b3e08cf06ef9162a21415e 818abf49a345f93c1275d55321fa70cb50f458e9e67fe911aca8e9a4a253a34a 2f521138d0edd239f6e54d5e4d94efdd9749f6b520fea327e642a0fbee8a5c04 9f46910d6268a0dce884a538bec4a81c3bd550b739bf5d02035174d1b6707a17 64656d4ab3a6f1c92cd71ddf45659176f2f37789d0338dcf124b1e7ad1030f71 f8c3b190e7a9091781da8ace4b6019b2d68f05dc92e54960a1a8571702b9c77d 9ae039b7b615745cb4e51634a5b8a10484d73b81a9baf555e11efeebdb5fa24a 75157319ba0ed1b0208d3764bcfe60cddfac56aa2330c76439923d245bd7d81d f997a99abab99f25b18d72b7d5b73d4dbc1902dc3e1e01d1fd0d12b633772533 aad3267d5679ef6eae0d094ae5df02b5228d3257d9a1edc0af7aaeabdb9051b6 1eb4d7c23a4c40827eefad193fa0c778f1f84a07210edc51b42d3150048f90bf 70e8bc3d0136207ee441333ab2f463ce6a456198aa7d7acf7d1c966f89371bdd a6041cc33222e72a94710efa35235937f843d4372ac73668217849e4f908f4d0 265d61d8b2272325b2b69dae4f728d3fbb3073da4c4006d49867abd4c1575806 502d8c2e6f00025493227c87f12a1319199c3283503748b32b23af0bd20eedc6 41d762110cf7279095cd2116f10ec88f99520c860d3ced6ef72ae99c53468ec3 1270155b3fa3602160171da175a36e4ac52adf90a7f52ae84d8be8471863f681 f4bfee7e8374801d097ba1199f24c2750bee92f29f5f84174200dc8f25234023 ab8ece6cf4ff2b2bf545f8c57791685361182df280ff1bbd925e8fd3c16dc329 04f1c90fc0b45e41ed9cc34a012d623d0a71e726eb61a157f7e5fdd331cfea20 ee65cd19e5b17a0dca4706ebfaae7474929503051fb26bc70c7f78f986311e2e af5af1a0079b7b62f0b2eb03964ca314a21e0be6685c1c6e6dde44bd4346b943 96b9d0ba83bcd6f976a964068dafb5329d349380910d3eb67e550fbdc46ab884 1956cd083ace90b869f0fc7041362b47a5317164ff30d5d4597f205d53a71474 402f7334d4db7dba7b1ca8572cc893223cdd7fa520459080f70cefe98a093635 ba1053f093a6d06659433238b107558e26a28d2db942a84d9747fd05839f5056 89b8acb7bba7b8cfdb045e158837a4beb36c65fd064fdfb2715ac78a95dd76c7 7d16961a37a25cfd9020b44566facccb9ee31c8663c3917ad7ba0cfdee07ceef a731d84951d200dc2dd94d18c77712c558bc3704acda6085d6bb46d0021dc7b6 a14c510bab57377909416af4573bd3b8a551d2d35421a706758408880bc4871f 59f8144bd5690d28f29907c8a958a1a70f8bd305a36162e310f04f2413ac55c8 0d56e71a5e55aa4fb86756dad3e4edf87f3a01a81987d1e13f86099dfc3b9494 5510c0c357755864b4485fa43bf7dba01d33052918604ec41533610130dbdc35 9fb86f44b481cd504258ac3975fb5a8534673fd77765f5ed90cd310cdcb4006c 3298a93187801ec62be93c5aa5900916e6ef57a4e19331597b82dfb9c00b2d9c 55b2800d41d2641fd75a214efe80a565fd5f27410783232f2a44f7a774ef9f2d d6e2629ebc2413e4f294af37b2fd948a7d1d71728fb59794fce78ab173f32a5a 23617f3efb68af4f70ba8173f61b95356389c4cf788e3b8f72cadc0d681975ff b06347741f5fdb84b017a177fd904913a409c46e97cc00440ed449900e9ec2f7 e88ad1d059d2d9aed7150005189836ce56dec709b8a22f63ed2304ad6c42c87a 2fcb144f29caa8d4e19a07f977ae6917e8fa0c830c52b0d4748ebba4801edca9 a432e7e9cfa465440d9a0b1df528d974cc32d4c7f25ceb53440e5995e9b8feae 0b0428e6f8ed8c90dc54832b15a20fcbfc82cb5f908f36045b1dcf6b69cfbfca f4064a72fdc46314d3652c0bdfb6f08f1b248d5c747998fe412cb86e97c6d83f 45cba132b9b9a0ea8525b70a158d531e0f4bab123078906e1257558b9904a24e 9b41948b3d7e5d4636bb670ce3b66d16a939f34320f0317ce20ede8a9651b700 bbccf33c2d44e1e59f62fa8144edb353961bd522a1e380f95ed006172c88af0b c04093b6c7bcd3446060f9c18cb62503acd1b1d4d5c4e7818292065bb29bdb88 5cb1f7658f73d542bfe7b10a988f45db4bb9d777ed6b0f780049ae54e82b6ca5 3a8cdcff1cd09bf1ba1952514ffdb00b204322613221f6c6efb969a36c164adb 03f8c02d0dc951aac9f5b70c1c044c06c9c98b0f61c5b30d6045f9f1a12c1fe0 0e7c767d8a26d57dd35d5aea58aa9d3a6098dd8ef2732c66bdf0e183ba23f6b0 1d6d4190d0b0f4b8ce1c53f1ab48c601ac7f4c487834f2ed355255a62813e11f f609212468d6b96a6ed41dc61708c2521aa924711029801e8e994095bb5361da 87f496fc9296d7e4a2b3757e555083f89192935073915fc41bc8f4bf447bdecd 1741f6e096f8cf208f002cde14274303ddcb25c2533b9dd10f0728e41f28e501 b091eafa29e0509f605d0671e4c207a74acd926ca791824d0e8751f58124bfa3 8fef9d73d54f5984806448be3a3b469b1b9a50ddb6c7a69a53e404ef3940e32a 5da7b39dfc6181a2cf1ee4f9f2e695b5bed73909dfc32f575eb815f53c8c0484 5e95b7c9f65a53d07781d57093ab9cadc813bb153e669de752f5c97e05571507 505ed9f5b5ae645ef41900a40d7157b332290bdeae3d19f866af304989d5d8b1 1f8959af3cf6788fa599a3a00d84c323bcf29dee02bc65171681fe42c76d4b5c 443b4dbe3217e35f436a76531ee08d19024edc0453c2d341991a8390fddb1568 7f3f7aaa9da801b08f6cf47eb23b3cc7d098c5d21bef44ab449ac00fe6a19299 6ab9c1bcc391acb14e701e26b32b7a6f7fb09da69d51b289469f2a977bde80d4 8efffda82d82d52bb1cefad9ed90fd82956db431019d040f6100ac161eb7779d 59cf1ebb7bebd9e07338a515fa9882239bcc77f6e8709f4765b463388a7f99a8 efd4bb0ff013d411e7a7d220f792ec5ca72933777cf416c5d7f2a2945071beed d1daec9942d6cfe1c2a082609eefc2c9e831a0c07dd21570390e2cca7f2dff50 bd3adce1a245373d0073f2430729aa57954748d90e3718399028fdbdf68b05c3 b93a50fe5a835ce86b56209a1057babf323e6dd32fee9433db6ed3695de7d9ab 3a1f69a2825eb6d2c90b438e9d7fcb3fe8c1e38983aadb0126aefa31bcf6c645 ad8be22685e2be87180baf881ab3dc90cc636da658175b732adfdd968e6e0246 9d57a848b3079857b0bd7c7f961e61f3d6e3d52c2194e3c3bff28dd1bd5080db 1c1d0bcc04c7ce4cc76effd19c8469638abc1e044dc2a79c7eaa0613528c7c67 f562f27b6b50c3cad5d14e7c4f12ecae046dc008d5dc888a89d796aee7d92272 e292217b111114bcfb21cc886b628e57eafb81a6f15eb72478b3a58c9fae5ed7 0398e72c9373ab5a0e21048f4fa031532ba8fa7072d08fc12fdb9cadaf0f160a 77c364f1d84650d7cb383dbc06e3c4570dcc2a343ed4f0cac0bfd76e1a0d250a 067c1fd7d8f49457bfe82088af2622f1d164681e70865f2b317e8a39b5b1d5fb 32a06673ae1fafda5af9e2a4400b0b29397a71e148ab0a10588d97cd408ce759 5a9d9c4d860c661e86742c4b8a2f9f641a50abd42b85a702f0f9078e06189fa2 939d92a6c0bab1d47d8eca4c7a2637498e41d517bfe0af1578ca6ac2d43fac67 b95cee77da3467db2e20ef20a9b88a31f1160cd4c96b6f08cdd41f719471086f 36ec08a5ba086104f1e20aa1bee9d911e499d0a83ee3273a9afad03c64d86885 d322470a709b5aa95a584918ef8e80351984e58799fe061c8112dbb8ed166e00 3bf94b5d1bc3af26948c9e95f0efe70707b7567f6c868a26fe0b3736f4eb84c1 0dacec7f9473b709d9d08eb0ebc09f8d14153596afc5f2151e21ad5f4ffc552d 91f9062ae066dc0cdb72a9caa0dfa82230fa2db6df73e69d4ce042a8c060d813 689f47516ae100226f511b2491ce607d33565d036c270dd879f725fac4f85ae1 38a3a226fb70015ede495dcd52d5317c8f837ccf80ea444e0872742b959617d8 2698e10a18971e5533259238128acaae4db48bddde16b3671e0ca608ead5705d 41e5a86e6e2986ee7803d5302b70e50086b379232a81b8985de0c6d8072ba128 9904888e211357fc304f14a141953565774a8681ce718cd669ff3d0cf1b796d3 6f7470a432f921806f7704af9e2ae3db09e79a98691f5fbb355ee869c824a9a2 71ed2c4844a82c9bb02a0ed2ceaa1bfe5011364c90ee2bb86bf2eb9ea4656198 2872fb459f19143c897500e78f4e03ed59836d14c8a99abfe1c45c25fd9dc225 afc7929206da56bfecb5fcbd92f504b8840ba109f2bed578179316f2840b7493 740e6ac8f581a0d48d1c890ae1efa021348711b7668c2bf44ce4df9de3900d8e ab637889c7cf30038e81325a13de81d28096719222b2eab649ab4dc46e306abb dcbf3053b800c54b354f4ab29aa1470d2cb41f516deddfe7dcf37a32c6baa4b5 43f4f3389d1301a51d22acee3a2bc19d8ca3b4051279ffd1cdc43a81ef0f3120 d53a2897da965ed3ec8fe3a64c84e94148f5a44e26aa618f2d7d6ef6e1fd5f45 eb36446a8f29a00e560e3539910c595e84159b0097ca45e1251e211efd4457a6 fcfc38062f786c990627e08f21824cb2d7aaec22f7b375e5f9dd32d82fb9ce7e 8b639553c2a999201727a1b7402b65f75e048993ba308690ad9ba2b65361f507 e71aaa0bbc4e864ae50dc86b9ebb8644320d91352025fa0eb0f60d0a3d054334 3e0aaf77018f0588e9bbf0adb974a83cc2fa77f42cb4209c24766282cff72286 52d0a11678b725c2199ffbda6814f92e9033c2eb5c5ec249d4fd4fe94e23a726 8f8c15d5ed07695a7d4e815cdcf4dd3dda0926d0d752468948ed919e4729ccf0 aaedeb70ef549d4196dca9e3fbb7ae4bfb81e2908969840c384d2afd34af9f1d c4033ba752ae96463c8c6d0d2b84ff52be33f3215de8f78e89d817ea24f3dc76 f1207b72213b423c405c200a7a5fd7f8023e1f239c8e606ddf71031525f23b37 baf147ae7bdea07b53a5f5e75ba469179543dd0ca592095ff616ed04ef9beee6 2cce4a9d993261d22aacc8b5846160c0d1c4fbadca7a70479fcdeb550c959c83 c43edda5a8413902774b1f8b09b27182bec45f69e5889e202060dfceae6e9ce5 22832c1ec22d2ab3b19093ac102a7aa2b8b0bd38952b9e35e44c223de1269a0c ccaeb8254c1836cfe80e63c41f1b0b12cba744f6252a115ce428368eaa19f561 aa56531344d04ac78cde18fe6182151d31569d6989ee9d5dea5292b21da8a3d9 543c8af32bfbd127e6719470804887617c42fad95fcfcb653d49009ec615b5c2 9c54714b1fd93b07226cb356e7e29e1fe9c093a22f3832f3498b6fbac03f50af 0a1d9f709bac257082747850c6adca588a7497f5853cdf90843a4deefd0ebb61 1accb6cb2ad322986802e1cf695a77856dea76a0aca440e6f7ddf7c6995bf1d9 5a077b219652fe5072cd730e904db6069475fa9808c9cba94668189922e3fe14 90a84ec912c8cf108a484d9305d0435f6f8aeaf48bcc4e11f86f5bcfa27f1f94 0600ebf5b2f516607f12411699f6dd8c8817c88cb9f3382d854d4f943ad23f29 2559aba16f4afe15749840263fda8974a76f6ae4e5643e3f6b797a3c62cf58ca 7ab8110bfecb7a962ae0c9c503a44ca616d3fe93adbebb1daf376ea312adbf6d f43d50e52ee7398b0fa244e144ee76d30d39a41e7199424950d4f00e9796f9ec 78cf72929a258d65087e8568e2088a6ceb6cb807a9ed07f5af1af426a59c5eb7 1d840f3efef11524a202b6b4c55d15f152ebec1fbf543292464bb3de84a3c0ac 244cef52f6ecc47defa48791392fa2300201d8b10310aff5450038c14991948b 74779637e05d6c6634b4639a77837b33a5931db6fd4669514df6fa3a95db181e e2f711df4622b4ede67c2a90f1b54e90e8cd0a9e9233c30ca82ebab46c68d97f 935c0b5866b20fd6094efea23ec5a44d9f38dfc5f9e13aa13681f6c944903946 d7c11734281c2918cd59b872819af1fe6808b67aea22aebcbf9dae2289eeab50 b8496e518ba54a9047c61e1c88255305f43ddbb46ee1952e5261728f6f233950 a12f64dd6adcefacf9a68dda0307f97699f45069750fd2842dcf49d30d9ed03a 8f98e2e04a873d27b24032446f3802d96921a3d364c825e6b918f4e8f3e8ab85 03b646a54e208cd8e396a18b43b96fc1e742843437aa917e6cac0d706b0782d6 30322a0e721a026ef6ff39158e4315aa2a492cbf3763723e7bc78657d9573a3c 4060d5f0af5ce15d7a8d1479313e0fee2b20f8e968cebe186761d6c01317bd05 825b5e2c98d748a4749705d0ebd93e0baca50c01b072dba309b71e7c52be9e1b 57deecc6a18a1eb81dff5424b8da3f6a6e34ec7af818a361d583b905ff7296db d576304b302f68dc38dd385a2241268c204f6d0219615dc123b0753ba0cf35ef 13f693db38785ee165adf0cd566a7402f0d4181d2bd633c081e86c5509013fd9 258de71f92a436777203c25e57a22fc51750bfb959022cdbff5fc1d3eab8b41b ef87aead744d41649ade14650cf65f719a7a1083495fa3908acb756f84468a0b 99 a84568508239b5dad1d95d23b8927605345946bb7a295a6cd84152cfec6ece0527f3e2ef34311de76772b74096eedaf2b62dfc5967f0d061bd5f452bf94317076aa39f2e95dafc34cc7b550277db171d5131635b2285e24720478477189d030a16f939a1fb74c7597865734a88b0b749365506ab1d7a078f8ce2dc946ddd310fb5767372faebac4d4be7118bdb91fa58378a63eb703285118d2675aaae777c0e236ececdf98d69d79a414f99a98853f94a4ab5e13223cb1cfb519b3c127ee307c435745533cb3d016f9908f48bd6340c994a3626da9fa2bfcda0968d526ab60887446cc871a01451bbf7dcdc4c91f3d378a0a29d5e8f42bee345f83e5e6643046fe23634b2ff359783a20d94df69ab3a7c7d9b89719c59de941e9fb229e6e80cd71cc4c593792ce04fb9266ceacb39d758b06daffbbc42e4a247d75a0db17b01da1e94a2a1db829d58caab5d25c9c0f458c93df54314c7d7401a25e708d21c07aae4c196f17ce2ed2c9ed8fee96fb6c9b1240afcf52ff1666813dc06ca35b000715845bb324b326dd5163d08c23a028a790f444a79e51a4062df5b395ba339093e1449fc3afc61a5cafe3eba4bb86502cd0cc9232920319bd6b1a24de670a807dddc70ea9c5901e13567e5ea249f189b8bd9c4d91fb78d610c62cd55e567d007f6d2969409ccd8adde436d72d92462849eaaaa04fc0e24880060d63bfb96c90a672ddc3af84ac3fdb64a21f908ab54dfb7ff5d0148ab8c8617d331130274840b7570f4ca4d00ff5cab936951bf361e51fa00f0770d3a04a647718640892874015bb9ca4ddda77074f85060cccadedb0e8e424ab044bd34c9043d1c29cc904e0a40a07c7caff75383ad205f0c47a1b900b665f22cebdb653f506fac082fc606080db48a99756ec03acee8a9ba52f06fc4598a94e4cdda110562cd614d1a53fb0c9aa5a1939d3734fffb2b04dd91ff7212ae07473d4a8024f52f7f9d6c4de03b0e5239012fe1a1ae1c8fb53718065db6de23a8f0e3673897c5ccba578575528a0ad26be05b95b4b5a703841df12a1156a39e637fdb2204f4a6f0b8d940476f660f02b621bd71520e66c582b6c93a37c24fbf1e4076cbcc682fe2f122a12ac9ba0fd7560ea6b1c8d83a032e889a6a4a900435c1b9c6f7979ddbcb594da8b928b20767731943e701dcdc050c02fa46526165b82e33b37482355a3fe934996afc2d03830a73f490cbbe876a1db7f2b19216293f240f60654c50c94d3cd57e5b5668026919c7d8b3730af368b999e27b58a8cf2eaec6cb80116eed284952a288aee50d760f6f3b4ecfc82784961852fa301f8e71a668b2c5a9198cc302ca6599b0640cb07a49c71d3fa0ddb6997ec31ac9cefe4082433acc71c928d7f461123c4dd30d73a6feae954222c711c269af99ae40f048e6f6b1cb92db3ef82152028bec1b0e9618b5e0b0e107865e0012e3dab4676177e1f1c9a5f5e67238e6b77c9be2b60c5fd06ff0a2d07d388de31772ad047fe42ebeaa4a6d9e2814462a8627c4cc7e0458d4db2d36f47ae440dcebeb6b5dedeffb1eee50b97602e06f14c2cecc56570c3ebbe1256a85b1fa9682cafb31745cdab5278a1bf0c41cea863ee158e166bc004f39c387c0fd9a4716db7780979259d2db8458000df4db26c84d0784b970fa0cf5f7532bef0bb545665ee021172362e7c5a68e4abdc9e0376b323953b31daa0d0b0fc9ec34a2d31fc267fd3eda7fa743f15567ff331a22baf37acac1a1095604ea5a898cab0cf8dba8c20bfd8561827e8e4589e746b53dca0795e2c7c172a1005e4e552d34afbe881e9e0769735fdafe0d237b499b1b417313eb9df1fa13950e369238dec465e8a261ee192f7fecf80351b1567f5d8e5b5b9409127b9ffc18033e1551f94bd2c3d6c2b58943b0a335e78302dd72023ad77a4dd924ad5c55bf03b4036c03ffc45a5c76c4b6db45f2455a7f862e34638613fb85d024013225ea0df500f16cfe58e7948968079b5fcb588e4d9fe8a09afec7bb70294421d0b16206cd4f32eefd342a3d1742451fb286c629b4123c400b36add8914f3a3b0b17cb04307a6927d6731fe23432697368b166a9500697f50fe408ef56cc5fccd94a740a42b252edc5f4a8bea37eca9624bb286129aad3b3ba8ddfbc7f20dc1dcd0a43039f8097f4ab0deb1e79ac4f00b129980074ad1e5fdf54f298540f3b389937880a19f7ca59e8d4059d1d1d70284bb3c53a7dec8e2f3dc6f58a1f1e95ceb21ff90bdc35f9603b44baa41f6d137152bde5d994512882bdc71a80590ed2e3742a2c05394b5aff7c681300841a02330532ef13b29a536464afa7c1dfa9a19bdb4a51028022c0300523497f0941efb90d29a7d131d9bf4e75eb1cb7ca1960ee5657680741a309c9ba28fea23b0b5781994f3a7f68876dbe85604a09b3b34d3d604ec90a5172fa1ac29f3dc07a322752b8b5ea95e6ace26d18ffbfe4dc819018144c8c0dc49323427ffb782769c40a757f1f6253e40b15a4828b242032c7c877227bf0087e753e620da8dc3db98f83a1c76691ee92ec7785110bd86ea27f0df26da06f0f2c862d403ee510de00d36cf6c402b55474fa4e692a061dcd552f74e7c1277d053b26c1249d1979d541a66b5edbd387e0fedf21bb6c8b2500c774058cd4bd4a05174e3243ecc50a10ebbaa57704f28effd9f80ff94a4119f2ac8ff5b1e0f9df0381a4598ad2d3e3d0a017e01c7c6b45eff04546ed910027836e316c92541ed104d4f47faf3d3599dc33527e144e0f5c123b1d3fc565c488166f4d0de870f9530b54c4836a0ebe17db565526d226e44d4bb53d77df98bbba7cf8e672fb35ab6d0c1b797983dbead52feae83476ac04b62d1414939bfa03061550d6843b6d0a4d04ad2e73d359891b09a6a7ccaf99198b572808a7ef52df9601839a1fa6f1a70d04681311c2441e0fa321d5b4aa34548a9e1012254c434818cf076fb2ee5e03f50d086ae081b87f1deb29fcaa1c08c791fb24f7bd8223d211abf914aa04a325da00911ebec8f4d5ce3d127e733c6143817d814810689d68ed1cdd24eaa0eb3ab407d1b7e80f7af2a88489a65e435e08cfef4d6058fcc75f939a834fe3260c8b3d0594e7a76381e08be446ca51a6ede565c4ad05a2699d01cc6c7f393d8a846d850c20f44e5d6549f89df33e19ad41a1afa9dc7d59f776707e5f2b9364ea15d4ae098c57448f7d38a5e063778c2c7ee96c22a1fe0f4d6c9d56b62deffccc3dc5360ef84c8e04edf660cc59621d029c355e3b0aef571dff402eee7ed4cecfb5ab2308c5ac8c492ba2a355283a9d6661a657c496d97835c8d14da3286d04b2bc874f01ee2e9137f43fe3439f34bf811e0953783cb371be6a40fca756c18413afa6a70af73758dbc3f2bcbd93ee32fcd504fb13b2dbbb2f10519438a7fb7284edd4fd0daa36e280d6a3c9e9d3e5042e99d73d921b902f2a6c863b065f40d4552c47ee0cbc9ca97b741bfc8e2fac7a21707cc801e68e41609754879e776aa72bf6831f0ad2b8adb9d7c1d6887a34f38ec0617155001d1e911b943324d309bcf562e36309ca2a29605219369a382eeaf2a14baf763bf9d0e46ed1da7624787659fcb1d50e8c14a9e67dd40bc9ec9e23f0bc4847e8573317a3bb8fd1a052d6b2e2d3598d0ac2c6b0732524d496d6d093f36493c6a82d728dff3b164deb5de2577c11f07e053e323b7e6cd815c13cd07469bca408f02318ac8d5e540a6cd96be9a2e4498e04f8d4cecb62b0a228fd11b0ce98c5b18c4a52c01f3bdf58dfdf2cab2498e8180947980bd9f9dd5aa803dd03ac334028b0eb4396d750c5ce461dbc15f9fa2a2b06e9f5710384dec1ff5854f2abfa8181fb5a676b70526d72a666bcc2db617fec0f695ea529fc0663362c471ccfca81a9e829546fea5c035f7e8dc412e8344ec90484e175d0986347f0c02e8b9e3cad695a985fa31ed708682a8b834bbbd427ef04450fe37337fa55084487d0cc9c459b04fd14508991410ada48c546e0518c0801dbfc8ebf6396eeb8d66f7b55251c009d7474cba53d2a72a1fb9981a6e5ad5a09336a91c725f585d014ef164198e85c19eff83637b6933eaedb1cc12ac7c4de04620ae7c1d71b4b26a95989359a2bdb7a59b1c97d9bd4309d374acedd522f090cd356d695b800728b331cd32c37d9d6a709f34ae55a8c6fc7b97b7c506618b0062a4c349504beafb6cd3359ae2b27f8256c6597524e9cf287d9344ef9b790c804af530d75bca79d1965127ddcd1bb79f13077c0f37790fbe18122924817ad860ce1dff09feb06d942dac26d020a775f95fdd4726c762bcbb0d8c399216ce9190469540f82f88ea7cca298b721ccc3f0f5578aef36e9b4c6473fea33ca06e875012f2b7d40f2c48feb2e42009e8841f451db2a5f23ad6285ed2bac2792d77e540b58c337ded280fa8d728401a0d84c4d3186676c8f328a89fcb18c2086885b8705473dfabc0ac481a4bceb4c144187e1f3f2a3fcfd6840aaa08b389696fa4f5b0e0bf26019b574c5032994992e097c204fe2c1378b9f3b4c52e65a076b90dab6027f2f2ba47f96ddf5dea8f10b831a9f4ff092b020ba469178a7852caafa341d0a67004b91e4a92e5a18b9d0c962ed6893211ee012be6ce3a3c704ad4a70cb950d71c3481ebabb4125e224c612e4d6550c8ab47a7f34e1f10e5e27d8d6705bf30f41289c66ee07f12fb35c22161cb8d216e52c87bcbef8686038f31886e4e5d50627f78267978183f3aa5e315bd5e0642666cd56a1e759d1ca884d205c0083ff07beed3d67b4ee1c17ba52ca73d0ea56b967754f223b9ede1f3c57f439e41d930e04de6e29216be06691aef455607b2c17dd7537be9baec2529dedf1586f5e90039c8788fd3fc9649a081ec8dc583aaf7b1ae5146935eaa0b4c897be88ea5ded017a51fe4f4c3f7c2f8421108e0dd2a610a9a85ded2c4a0ac2894a6c7aafb4bd042baeb387495fb151484f6ed0200a2a5941dd7b2beb0140beca2592b75cfc7a0a69922de9e6f267bc6a69e05a4b00ac20bcbb6e6c986bd32dab570eb2a3051009c708b6dbff220c6e0e6e42c1abd42084ec51c4670ecb8e501d9f1e8e73a7380f973586555cf862845778794cb465481472141ca3fb45770a97d079e0d27fe10fd20500fe4c2386cc425bca4fd5a51202c38943b3be428753ba5daefbcd6a7e0580c20f4913eb9fad486cee11637ec662c254cf7d3f4e0ab008bb8bab1f59e306d2b417d3548975f449eeeb0e73038da0c7a051e24ccf268a3a646db8e734d80e6dc63ef3da73f8504976e10de048f599be95026ac79b1508b2c85b8a57875a0d8085190d2c0bd903dc7d3d8000ed22d33fdff4cc0b50741ab1078cf6a15e4a094a006cac29f11e33d799601103a19eb1517427793e90e5fe1a6c21a3758c120530ab91beb030ec86aae1438a1444d1353ce07c6d2af53609b186973c5cf05902acab15c573ae94611b0c4970f1ba03567a8bdee75e8ee200096dcf3bce228503c7b826a5685d4c50e06b6693683d9d3ba36ab89989accdb020793f9404598909ffff41a1892a40032be4b12b8dcbe7bd84f38c47a835e55d2a90961bd62c9805167e6f5b83e0cfb4547a3dcc7dc81c0a76716530a2ab6ec2cf6802cd09425303a03dade9f4810517b471d4f086132200b5445c590b241bba071afec7f86e0f006b29329e7e64905ab8bb32497d8d929e45ac441079d65a76971d5593dedb8004eb42eed62d4fcaff967dadeb4a53bc07d518d487aa04a367396c51861561090e4bdec5bafe793d4aa39ffe2fbc853552589f3f80acecccaa175cdd82940e45054a25e633ca7b62285553e1772670abf700b56d4dac532099b03d2aba407c660396cbdc37c5b1c3598af49da2e509f88657aae137b86153f5d296383917f39800bbd46d03c138269f3f45d3559ce8cce7671a0a4104fbc7c1b1ca1c1501a7ce06e37a89865aaac6ecba6211b2fc80841f0ec430cd9df75df232f65d59d853200473f878957243105f545b1bb91afec1bea9dcf0c84fb7e117582ea2271222520940600d19da80f5d36ed73970cbbef20fa99c2c8fd9552dc6db8b864df118840903d310a43c54c946776d6b822deacb72c3836ff77c14a198c091aa6c2e6fbe09a8e2286691a9128ef32786793f4ceb75cb5378aaf74c92faf7d1452ed95b9b0b4e9c19c1e2196426f4ef36add5586ed3d65fb7a7708ca8fd49465a769bb7e609ed86d9020b4d4246cb53c84b63f93f77d5eca34ead1666dc19e621c5543e88004842bc12327618386000a00967376ddd74ae0e5a6dce99da58221204e8e81108df55619be27c6a64f9c82f97f4b4004df09ec82c3fa4ba43a91f1770d2a9ac0c89e5a50295355ee8f53edb968f8729d4267c7c0fa0a1c7bfc929acde8f861c0d3d3d1265f66d0594738f7e1828bcb560bb232b539d3740e7dae5e32f024443095c0d1749704aff2324c39c4b09c7f3ed41b333ef027e60a69cf23e82c82a6d0ec4045d7c0a4da39e3e1fa88b1365677be76f3dfa2efaf137c4c541d5f00a8d004281b3b8b3ec3b41112165032fa59de3c026e67b0a8bf5ff2c9f44356c54620323da07f40a5020981133c933ffc6cb61bf3700e2e8b59edcb79ef28545ed8703fac42e6ea66625aa8e0b82eb39c974816818e7b0ad12b2647da24aa98ddd240a7f6b7322379951113a9341fbfaff02716fe2f08e50304aae89388aeff7d44106448e0820013de42853b9553577347bf570af530e26795971b1aee9a75de92c0a310af83a427ef41a55ef224a49d600dba2ccbf30095dd7f56149a3dc142de20c9d91dcb36b467b6ae0b1b12c5b47831307a7ddcc2967f5573b589c082497e709ba99cda53215192fdd626a09c1390e83e7215c0d3b467aec4d4e2b30e5874809cf27af46cb2708e9c81e0e5c18d41a5afe263985aa658ce7d285cbe7845ecf02d110edaa9a26bada55c9c10a5ef7bc00a66d1fef26df48961faef442566faa0f29929d3c2a2feb9d75b1853f63c8aafcd12148a674b0066d6f26c6f167770d0f6916d0480770ed37fa4d6ec3ef29ce67bed101124bab55b824686f7a5a963f06ef285dc6831330f62f45eb895896f15c3009f85efd45e06babc216e52962390b80eaa9f8b9d0005716305beea0852185c507b09d391090fb75a502a877981e00811f18d1ea1c75262a4f538345adf2b98b2617dcfc96eeb09d0c8c6d627c220140247df13d122376dfe1b764d15d6faf70dfc7bd2a124aae7629308e576bb00817c69ac56cc8c835435efd1d83df31d2d71e6c1884cb1e8b1de6459b3c83a306b35a1ce08b6f5f769360f06b2545bf9d4c4e946987b1f166cee68490a0879600a649ebf25fcca2f0df87c69eca7ac637d1c4747b13c8c4824a8b32129917f30a7f41cc4c91a43902ef40f8c6d1d7d9d6f175e5a70991c2968d1b62f31493a8001b697112f68956049c45fa5a56b01360ad1ab8ff8b446c77fe30fa7b3b643b08dc0790f8f8b87f79c5c1a3d9752c1ac373c93d24edc2523455629f22c8d6c20a52746944d3806f1f817d75e9f78f113c682426155b8a5efac535d4702ba35a09bec613ceb8b2cfa2f838701e22254d7a26cb144b5b697b3e2a740f238fdf450dccdaaebf4568d74349ff1e5e8c9d3a902d54f6937feb0b6ef8a13360a43fd9083abc2cfeccb41f575fe2a921dfa6f9a4110e7c37f1a1e78aca2f70602bbf9e0f4c5d82f67040c22f736cb0715559821b5f1587e664ffb12a1a8fe8b4a4c1c50b5aba150696176d6db6005c6287a1873d16d0d3db7b9d272d6da1adda52da3e0bed216fe5f7788255d123126f4fbdcc70a61a324773f3b5d0bf8549c706a0660c49e01203b84fc55375730cfa97e3d24ea648442482a3da4d6629dda8b9819e02ebb4a54385958db14a9c5de6636d06b13af64026912dc6bfd9b339c2b0c6e900ebadad69a2e0e312bfd422c36277e065ff78c4b55b1ee67ec903926c8df117002cbe83c440ab86a67c6ae8f1b15ecda6562daf53bc2bf22bae1308da10ddb4018c67f590471f05a10159410b47657618fc3e70917a9379c84b1aa1f96494e708e00fdd82fb759a83b4e741171ecb6a698b69779a17926a34ebb25a04dc7f48082b8cb38b75418689edc8c2de92630ff6227186edecd1f9f363407ecbfa84f40752224f589689e896ddde22cc8efbd9b5a5fde3387f7eb9f569d5e3234181e4062e57845ff75b0d095abf284e5038d14ddc0bc8ebba5637ea7a6068c99dd5e105e781c7cad2ff62c2a06c866509f773865328d8cc85a86221693c4a3df3698d0828411f79573139717be4a80f2cb7e4ded5a68e3a95eca8963552fc8425f696068106db04216602e3f197a14998db49266fd0087d0761b1efe35279d39a9ce0002e6f2807dc15ffd8e5bae2e38ab7fe08cac64a7698f849ec44d39b7829cbcc0abaf33881e0417f41fee7faecab25ff830fbf4355c2c125f3c2c0654dece8fe0a9fdbdc49bc8df5c8086fd7f6033e4f45a2ee6ddcf1417a6493229d666f324d03eef5dd9de38acee97a42a32000dc89586f5c8acd42519d27da5d6d41f355500cbdc78451ee62c158b09dc2b901c92785ed550ea7dee29b04fde0d5ec993bf00f4b1deecbe41987ff73b0320efc8488e8627ad075872eed9625ed128a3810520e5f90bad449f005dc829ccb99e00517620295bef693658ad2c792398ffb54360832564d65ad58b252dc06461e27ab42f3b94a6a60c63246ccddea4774b16313013314413406c79ae195ab6f421694b95f9dce505d18d2a1f8049e9c50212b2d058f09877ee1dc51f439d68e6aba3a9f53bb450c0c13a3dba5dcedfa7abb04ae00eb81a33070e893f6a3f66a9b01d4dfcc9384b5a58afc607e2f355a32b318e40b78f5c44671c1d834ebb7010a3716fd99eb74c612f34ca5049e9e43328af9060c5a9f83bae3827328ee5f8df6392f6de77ec3202f22fcbaa7241067d69728e30e5bdb2d4d6d2061c012612fdabfac2b116bcf81d375a86fabf47f85eb3cb2bc0825234c36f49f083c64032888eb4966a154b504547fae0c86a83b69ac44e0140bca02c44ce19e886cd19b33c3efad603b7a7bc527e0c7be1c2cd823d6e3c815069144b0854bd80f27fac1d308688b5340da080016139de9d0b43f263ba559cf08ab63ea84a85cc3fe52f91b04fcfb728c848888bf3878e5ed730226e370ea1a0c6cbda91b0c6fab36c4d95148c335f0d303a8b7ac42b0a20f80aceca2481ff50b68e1abecc9db05f21fb9fba04a47ebe94f78c51049fd9619db5998047525190a8e989f7b4ab4d003f0c6f6dc9107a1b03882b287c49259b0ec38998aef62830176d6524ae19c8e1a19d19c787a1305d2145831ba455a813b7e29692ff293170b918c8503d216b7f88f05cfbd7ef20bd78a90a1e353cd3bf78fa0eb66869fa8076e14e5084dc6bf07808f7eebf07ec1ba84bb965db016624411f415b71137f80192f28764d765f748746652f139a328deb75a7fb08b39512137f888eee5c70009a6129f5689b787054d399df0238a18429d60483304ecc85984f09b124de1e10d6cc0cb0e551109c6356d89914cfc84785a6d951fb094212973d76614b85fab0217d133567cd0e2b1751ece1da7352fa10a1a612c74e261d0cfde6a3d10a2a40e16f7ad1cf97673b032d92d9a5362326b790f5c45a0f9b5df9808385e1dd08502da953710f6c7e31ff5170333cf40d30022e04d68e917c459a9329c2c8a53c1071308733466e8ff9cf631d973c14838dd364a1e0fe77d6277351823d2e5b0ce047c6a1ce56ec2d126d47454ec6533ad0ea2b121793234ddf99595c80777144c07ade44f3d81b2e26634e69da5b29e52fd7863089d949ba5321b5faec4ab3a33097e440fdec037f97a6000acc5372b09011829a16dadf9eb7dbe5047a5ab28610c9a04267e2e622f742dcf884867316f4135335198e67a4ec034437b8e98e6c107cf0a537162d7a2f0249076be0c326f6a4b9c7c0bc050974287efcd3213509f0db11e2bf00c82e53f21bc25aa71a244204303ad78a271078c758d2e9db14362082743d2399bb3e987ca46593e90513faae5c032bdfb5219ca96919bcd57f3270524c74afadb045f3a36e0cd1cf68e3bd68d0efdafec8b0d56f11afdfb04cac704c7365a596c169fb44ed7d1fe690c7150c82aea164557d9bb05ee53ad3cc14107e18afcd2f153733f296f977ba883111db4bbe58b6724346cad4b7f720e191f06cc98cbf3a023206972f9f821579d997a16ce8dd7a77e04324aa8fa8fa65f6e0c8cdbd1c34f64ab225f2c1ce17615cb049c452882694934bb23a30dc722bb0903715370dcf2cef4cde7db004276474ae36bd6d244597c0adc6c794b3e1ed707000fa82bbc98558714f01c9ca28c855e1a3ac2ccbdcc090287455383a74022020d1be04a2b5f7abe2850295f49e7c180ac8e99ac4d7f8626b997bbeaf2db4b7c08e29b2787a70bad475ed5c172d2a476a666f6488c21c0123dfdec5b91f52dd80b9f445b634f4e51d96b9cfc37fc38fe84dbc2fb24c8372cfe04fdd54f03d45c0cb39d79222eef1e5706d32eae767b0a68d5915049a3c583e16243e0a5272a7002fa9759cb6bba23e2c2c041a87bebc1221dc3474d609803c213aec7866676ac0cd3b0bb9f4e360c5d0e9670e5e3ef6ebbe73fc5b9e82b28c0b1b53fc194d0940c08868b1150303566229a9192e32bb8bca9d3ff8e0d240d0745895f7e9a2d2401e2d70008ade16084f13278a6b22ad92af563fd9c709af36fb9cd8e7ebcb3110f7b2762d32a2c1e1d662486351ac820b32adcc80523db3405fe448843dfd59a0afeea65c2ef6271e374827ada480f037062b7a0316a1dc8952f6fc133199c280544a125457f9a001a45fa4760c763756fb9ba0acbbe777b429348d834f5866d0b367f5de43deabf8aca9682a161d204ee115daa3e22d1dc19c8a981b4eee804010b79937bef1f78f3cc581ffd9991f87b3349dcdc69c1d68bc673a6464e39710935b3eac50aa3b85fe3cb6ec793189cc617cd30eeb5cb57429281e69961121d09fcd189c24fdac0e7b9437a43a68c76a4f7529d86a872fc8a74b1b666044bba0deedbfacbbdc6811a305a8e985ffbcc4f75ef1aca4642b624aaddab2bca1be107a2c0717a976c1408a4a5967d14f369af318ef0e1e14e2056eb12324ef207310c863bd9421654e802e4a5f90f8899776a984de4dc88c43cd69cc4163fac367c0a3b3a1eda93a52457409e7c9898c3d63cdc7fc5684dfc68ae8b75f11c654cd60bd595e06bc45f6975304f88264cb6ea76fb05b4529c246feaaa28777dcbac6601f27af7b803bc7d2f66888772f4eda421d26979fe2c69304916c26e226a01cf080275ea176e0bd2df44e1bdcec64bc63141066b87d055ea85b5344c08c314d50b11359d2c67d80aceef4b984d08234a4b881895109df15412f1ebf5d4d4355d058352f701c11756a476fe8559f6b6b30307fa96ca39f8418bd8268550a1321108897aed8ab31fae2c6e1aee769577323e33bfddb05e4a066fc704c6fab6f3680b2ee0891f61e1d827659df21ac609340e17ebb73a2f7f270e54cc65a84fb8ef0de481f11a136fa80c18b7373add3037a737767c8cb30f314d9eded61b35813f098ac2c7ce53216a76c6ad0ac9c20c5c2f9a9aae534986f6efb26df8153f4ff102ba0ed11b06bef078241b195412f44bcee5633432ebfd0dfc8791c51dc16cc002553b32be3f0879e82607c58b38517c4dfb8acc6dbed3f63bb0805f7854f6200a63c90bfb0e86d61ea69130358a6a3d24e0cd991d448dab0a8e24f2cde55ac801765b7433b15b5cdc2e351861db5d4a01a31a8c76bc9177e01871e7fe832d540f82b1617ccdec3a44f6b2d610655b10c04063cc243af1ce1993e3545d82d02b0a9ee0795ba84d22eea67d6113cfc3e756fa80fb4dbbafebe7ad261b0c7d8c31045fc8ceb7061f6524ec7493231288e17461323690962649a1e19e557d236c46080059dcce443421cbb13458781836a8ab1d64451c83e987d7393abc34c198fd0a79af78da553af13701594af9f7d49d9908487cd2ab7326f2d267951293be9f045435d72558cdd5e817bd445285add0222b2030911a370bfdc369c3b94c0cc9057245e840918957f7a1d4ba91b88f77b7396af9d183a5f77add127919d78048033fac954ac51d5eafa75871446e071b1ed2c4cec22a87f3b764b1169fa52f8c012fce1b729872a3a8b675fd501e8c0a1907c4bd29d8d32c8a2cf812dbaee7790e7cc2d004c200dbe824bce46d848de436be9f4d776e628fe627741d1278292b03f8d325e471b62ebe4de12ee5adeec74333af0ce3ead8dca6678c41fd9c07240624a0b7cb1a7f0d12e76721976b45bafd3e73994edcad29cdaa468fc0b538a407fe640170b06423d575f761b003ec60fa058a896441456b112abb9f253abcf103f5519579906e0e0d11b7c4f2d93f8a5a7e50beb53424f04110c47caba39da10cf3bf5d06cdafe550fc5e9bfaf9f8b82257197b8bcac41f3186e36ae6f743e10f12b356fa7fe522fee7168b926100b57901e56dfe38fb0e153bcf48361f5739041e166cb3b92e27e5a7d5c1739e5bfc00970b2e6bc90c460f760b210185dd2d077ebefc4eb216ba10debe0feb86e5281506ef231fb49a4787a946eee0cf268c04bc000cbfecb4d2a9923d270ab34b9c8222f81e63aa73574937f59a8ad84194026ff39ccd97650182451124dc48a5f30a7f67187014e976438a01eb0881435307ce236da50436c9116e13d8bf8096abfa94cc7774fd8dfeac3e741e750a5f0d0fd6dc9d52d702eead69b11c32f46d3a739138f27b0533e9222a2238408c31c5063b3e64285824714097b78f9c9bfae30250add81c11677bb9f26be3a2833e5b075cc3f0559de4b3a415b0a971c36da19a7978e85d905d21a001bb19796130b100e5f3003f382a9b7a56ae251a95bc8dd31ddd20e4b8d1784573a089bf32f50e0142f258948cb600b75d719cd3ab341ba0529bf2ebe56658d3c787eff6619acf04e9df5ac6e5376a11f22d20246099d1a55e6d74af05c861a5292671d30a25d7088abf432cdcada442d32cb37852c6b5e885b460da78d2a19ea030f1ee4a018008bf9a5589a284c7c7055eff2757f905e6eda950a5dc337f2bca9fe04f28d7260e4f8574d3b62c2cd5fb3f6f720ec8101c18e1de5be99047836780751fafd41b01aa109292605e1913d02b300d263a7b1b7cb994d70e744feafd1b288ce8b94e05e69059e098b42900529473eb831354436c56a5431530b172c60647491ffa110a7476bd5a42e8cb412c0006df2ab7221cbfa371fa66c6b71f595675887d809c0291014a92c329c42e2f71ea18f45acfe18bf398c96be94e1a147e79f64c0030075e445a671121810b393481a7606b1fd2eb2d224db7d29b01bb63a2c9c865c70d98f598bcfc5853357362fe888b4a959d36494787e73327e5ab8d8b13db59d707d78c62d721f36d6ec561a17b651f52c24c0038533fe190ecd07cd3957aae5f0e81b559588532c205fdc48fde6134cda60fe1db5630015d3eeab0eab1e17ded01b3cc066608af7444e4c54c189682703b0a59ed8a89f4ba48059e5a0cf6e2d60bbc2706c04cb5ad2ee6f2e1a01606f45fbfdf7942490440e0afa00bb01cf5a500e28635216f752e78cf0f51c7958f8c8aed819379a3e839d4dadfb55171329401334a54ef085265bad5a5c17d283b6043dbcfd4c720859b167e704c90d6f7d30de187b76df69c5fb76c2f5722d119bf1fc9117ce46f3cac75c71704e9a41fac07b8264b498a1541d761c7ce5ae88f69fefe807e7afc836aa0a90e04e8a6005d074340a8016c934c2a6c3e6c86d1b755fcec5757db2093c95f1c9b805cb189920547a96286e94f1b99649bd611dbb061b585c214ec05c6f03bcacd5f1f402b5e091ca3e292394394845af85526efa20c8ff54f96510c2eb1c8fad2fd2071e3e208e80d5d6040a7c04c3747d8be2d744ba39f40a90aaff13290fce0023553398b0e36ca418ff92002973f3b5d520bf0bed2ca00ce79d8d89c0ba8394b10ae44290a397f3e50a86a64e47f29db45d3e3a642c4bc8b5ce2f8ba6ecf8dbef6f301140a1203fc5d9d76e9a82ab32dda8b4159c6b06b314f96fadc33ff58844f55f30e0f46ee7d6a0444ece436af6b74efcb447e50b48fa80c1805332dc67510b01a8e076a44fecab6831026cafda074bea61ee36ab18a6b2885faab940ff25dfbd5880a4fda7aa4c607d65ae4fcc91f98c8ef7fd31127059734c580faeae0c9a445f10df39229869914bece44d18bf3fc8b504a06c4076aa7054cea1449853c2f77eb0304800ab486a51aa7e789616ce7b1f2f4576d822e7f0a496d81c4e989dc169e04697a53388baaf75b8b505dfebdf2f348438355c026a6cef96574ae956da06f028f5717ec26c7f9c8794e808fc03ace6921865fa532074de6e2b86d531db04105a1f85b24cc8159b59043c558523f69c5773efdb35e4b165030c5494d345f5d0efbb419664861b37ac45d86936fb1417e25427f57fe63a34668c3ea856621bb038b79b1becf4dda51c1a7b9bbf561fe2fd47b49298afe0591d7b7fb4db026e30e1b5c76e3f9d62a3f0816f63177249bc85f6f53cc4316fe1aef1c987834c10c02ce8fac4b9cabe0cf5ccf69132f42a09414455b3241f34bc85aa4a3f06fa93c0f6589f5f2594172cc802fd03e04a8981c7282b6a5b9ad2cd932de76101582d406be03c9c20466bb6b3eb78560bfca6457e52520235dbc114268771431e8d499090522a2b60c769de3baab55c1f7ab1ba0f2e237f8b0780d47969b92b7f42d500e98aa2e11cc0ff955fabda4769b52627c178c9681bc8ccdcdf5983ac1cb851b05ea2d1619f8385e0a23e4ba3c993614017d74a7a01e16e68cadbd29c9c9d3fd0defb22b68bf6ab829aa1bf78ebc1f8d30e6067f005a8f75b0fc6e1e28bc0e29080fe70d937af655e3189dacba1528775183febcbae8b3fcd78523eb6f10537d0603c4ee2c39f1bf1bd379092d3cb42b4580c5ca3b278b018f8b3c9fa868055505363f9bf5d94e5b55575f35daac172617c9672ce781030246549e10f9a8449e095855b6c79285a1a220d6ac4ded198f3a8681a892a397aa337a4724c7f7031609a8259e5d76d90f681564a98baf2316925936cc73191059b478d80972d19bd60b6be7ea56a240e808513811ab998b13bd170282fc439b421f7c1da5f497c0230d3d440a2e1ba92d75361dd2127e2af67da8b280e3eb7dc8ba0e4f997f3eb3840129be81e9b091c1bcc8ac92beb571851fba0f23abb783b4b95b1e57e943f58e09c2745e303ed17c24d99e319996288e69c52e1683c98150dbedc3867f74333503ba6f2dd8e5b8999772fedbb111c5a6c4db6d4db757a6db807599e049f78dc20e2322b5859278d3347c663795a6e2f2042c6d1a3af3e2d7462de2d17d5310530ddde5a7a0304b4c0adb2d80b9200d143d90c3f5b93d7028b0ea9e2779fb0acd03 +generate_ring_signature e46bda5f8d177a85b3d4f3f9b52ec30d3675e7239eb271e9756c1b9dc52d0c1a fb375e609a51a1a54d30f6554e5481a94158e49705233340357ad15f25c832b5 1 d9515348ed96ba26f155412e0b4b7fb190f48c1adff6d6f7bb0d68c4ebe432fe 70df55444de1551ddad38c3ba80c388032316cba280052447947c02151dbef0e 0 e4b36b7bdae0946cb74d25f50e2b3db7990a295543e7f3ee042b6b6f0e230e096192f33bc07335ce95ffa7631adb557bc9bc8a4c15531c72a62374b2c5cc3406 +generate_ring_signature dd3a63be8c1d0e7c20341ff51de1ae69be6ad9a4af2494d969de4b5c5a3d1213 bed9efb67d0318e6e2fa22a32ab381332b38f0894f8a8ada7f8b4372ebfc967e 4 066b617b0fb79775aa1b548de8757a121e7d0e38459a6bc5dd0a587a88fa26b3 193a48039163ffb32e1ffa931abbee6e1aded29f388aa635a3ab148c8d5e2568 4732c8e3a686c813b657069e304e06df196259ac5f1634bfb7713438345f053f 4f1a2284c22c27aacd12d68d25ab10b1fcdef99a708fe2903c30e185fbc99c28 79be700bc846a57a8404540767e0c3f22a32f812668276c2d6b9a2938d286a06 1 bb40985067d244cbc7a03a5624fdc0a225c41eacf3276cb53ed5e6b108a0fa0c55bca1ce5b58ca218e994057375ee04eb95b589a3af45033316631483ffd620541cb926d3fa5128a692f4aaa923e599536dc4993fe69ce2c41d94d693ce0f706d7fda63b9d6391b79ba008c0a0d71ca405cd4f5d46205722e3b27af2cf67cc0540e55a08348992783fdb36c530cffc272c113d96146cb5d73ba2c5d4f25b290b8254b3f7dea97f6b749189f65501f8e44ad8a247b22f983fc79b924ac552200687b9a43d8cde69813cc46378e464c2ce1fcb3c63df8a26fc42b1fb79449356051e019daeb4533064bb7f62539e2f6e9631acf12a0049e19a5ac1dc49b5a4fc06 +generate_ring_signature 2a3fecc5533445d1ff407029a75e714626b2eaee3f6479c67a27201ed06b2b9d 01c8dbe8167151ed13b296b133178b80e39a38b0c0c38051040ade484b69e4d2 221 8b859d51a272d7d7f42eecf840f7fa9149a4dbd6a7724f6afa17c4a1bf26d8ae 8d6f021c956ea9a626077dd81b5fb6e459a370b56deb4f4fface816bfaf00668 815a27ec056a040e436e299d9632876500d90f3dde3eaf1107d9503788cc0c95 a24932b33273a1ceda8f5b32f270fc6e0df415fff64ad6b39017af5359242b6f 9a0adcc60772f3f654b3b417943433b00b6bfc27e8b8afe5fbbf844147e8c5ab 90f1e5a5891e354d4fd02205a33f49657a5de15d7ff4ddc31f39d8643d62aac5 450f08d11598d355afdb687cce5632cbbf31e3c1e0766765ac352dcaf036a67f 3866d2f6934814b1095f0e0a1f147b93c50f15716e254c9e7d74645a4df78e0e e3491b5ebfe4fd7c5bd3f1dd3a40ab2825bec7c4d827e0599f1145fe51629f3e 62c9bd5660490a1d2e9ff39215eb040f736051a242963bf2f272eb9d73fe132f 71e01607b9b65d973be08bf5983d862bd42ef2c3f0fd6be7ba33a54a5cb909e3 d561622d76d33431f0a7881258e4906e5774696441e93194eace2bede8d5d095 9e1c44bf1c3513bcf7ad2c087c0eb6f0d19ac9288a21838cbe629312543a7d32 506331bba887f11ea4ad8c9044e332c082302838c750f0b0ba8936b8dfc70715 a1f5054ef80b2ec1f151f60451f4c982280f128b8ed831095ff4479f2adc8b19 19dd0493e1890f8e0ac05b9fcdaaa0bd9688e2ec9f47e2432f02ca60992e4bb7 8d2df708b2ce601d1bf7d762da0d31681643d74b8ef61e061c1f67b8e88f3975 b765132b1552f0c34491edcb6d601849aee7557ca805edc5296334860376838c e4e824505152dc5a1800451cfa73437c266b6a0ddc9a6b13f8a82f2a308195e3 33be8cd89800df491acc53ec6eff8132fc3875a454d2fccd019fc6a3d4aca291 f7289f047047bfe7fb28bd1466daba43a6b73318aaf082fe49a9703f2650c54f 85aab4738840c444824cadc97dac6f1b738102ab8d495c5760ed553ec8cdb8c9 eb87a343835083e45d815729559c3e1de67d69ce50f0f1bee874a3e90b8701d8 c58861edf4520042f37e873b69016220706fed7fbb5f4ebc1e21b9fb5018e20b 9febb8ed1c5a2cae82c73149c0fd7accdac0451b8fdc81121b954dcc500e5dd0 c5825602f9ec7f01f1d7905c3b3e7a4671178863672f3ef2c83e1abee6788522 fb3fa6e53af880f0cf31f95a65b5181b4be788265f9a1c0cdd5c3f1718b5bf83 f6b63d638100268978d89ed0f6005d24a1b143c112c73a918ad27b6eed6f0f1d b4589104a456ee51feadfc532a6f52e5bca5eb9070d0a77a02624d774d4d2eca 1fc0934d38877661c56143af7277eb837868e8465f0a3351efe115d1f61a6b20 bc4f9dbd0695c462a8b57fd6b0af3974104bde49c6062b5c4bcd09615d028b7c bbefa2f48cf69f7b1df407805e23764ac7266494449ab9fdef0641b4f13fefc5 1c9ce4f9a6a3931a3ad753ba874fae5acd3e85351b3456dad3c0a8ef85028633 b378c247b6855244af5246a0760ddd1a703629716b16b50f2f7afa5ebfd57e01 88adc9c2bec405b609d7190671cd84ca990e3f2794ab3550fcf5ceb569009fa9 d063eb0d4f7ab5f0b7bff340644c61590014570207189c73846322e0b77f8392 7ef5e21552383ce730b75abb6de94d8623ef8987e2d382d85dca46d2f5b94324 7aeaacdda090144ac43de8e04075421917f57f1044871738ad85c3933a7b92ce 73efd0833c7b48c215c0f92d2e72c9584bf1a5d6f15c9d99e19a6f6808a9ed8b 65b8c07bc87e20fb9ca0fb72787720d7a6653b04e11d4d31f6c699fed3ceb5fd 250511f67f3ec1d43778d1c712a6b72090c1812349262f5aadbd37e6f3cb69ef dd168f71e4b227d7417b3a6d0c6dba72b1fe04e47b3816569f149ff7e736c6d4 e1837515ea0184def7085f6455cbb2ea7d957942dc3a67d8c6c2ac5c3f588528 be818f8f702a06b5757bf5620b6ed5c110924a73252d5818b4903036b38e9ac2 9348070e09c4ea8e76401842a74161a94a84a46143a1242e60bcd7769f8c10a4 775b7d0938679d7a173f56742e08d0eb911378e65e137548d9ccf8ac6f61e00a b44cbf01cc2e599fdce193fc44b58c7e9f67532e6aa0cc18a771823b7709f379 2452d9e33927a56350678ca3975ed7b2c8e6d4d567ad7ac0e4126b1a55dde552 75b837f6b344b941183721fb45bed9ea0f5ad890e05f465b4f0078b8c860ea30 626ef8f4b4cd5a655548792ac7acb47f3bd0f22411a91d39a6460773b8b962d8 173a57cf7b632d55ba237914c471a75ebd5023b471406501db025318dd7c5afb 6e50ed5141d31c5c71a32b01dff6c0f9b4d1fafcdb59e7363d0b0de0f6f22049 353c625beb6b2e3ad123fbb9a5b2596f38021bbeef7b5e0c10ecc9234f6d553f cd24fa2bd9e36f5ff131b44852c0bcb99d82f055792ed8dff48e837d1b4212ca 2388231a621404db213c4060ff612cb04291828799919be7462d9b0ce8db9642 f05660f72b5d1b9c8713a957604cb32b1713a8cbe32a3ed4c30f9f581d1b10e3 a9604a2437780030a1ea1fc481a7d2d538570c08f355696156a8313c4dd6e00c b759e38e1dc01c64386765f177c8aa429cee8ead275d6ceb7eb13ba22780e430 aca51ddfbfb7e7d73b15a6a620bd5be30c5ea0fff58d2952f2764ea5378f8e15 7bebea82546589b5992f3990f088c948ffc03551d65275de4cf379fe08a231f5 637176cb1b76867a4a07c3f4a7e972ae621c128cef42937a4f367b5b52688ae1 444b6b8d2ad3f031dc9884f1c8d33bf92b0f8e51a9decd2d1853a613a09ef956 d2cabe5ea3d6b8e8539f6ecea33ec239ece6b1d362ec8ace8900c7f5d32b18d0 12ce5eb61291c983d787b75b4bd4b4fcec42870f7f1cfe8d7864d955705863e3 ac72fb9e0b6a14fe14a7e4313c3df10a5f6a4e424c8467547d02ee590df993b1 8e7757946777d73b44177c05b9beae59bc440eac16f548c95931ef4d608401cd 649919d443bfae5542ed3d35dda9d4a8f5027f8529eb735ed8cb27ee408783c6 59510fe2efccbc9e071932f0a1d6295e9f0c23375af49e4e1ac630b6b2ef7ef9 bf061ea258631a7bf55c1bb2e782d745f8fef6433688f4a254b5871f1a59cc8c 37542533284da65b04bb83ee315c0aa265552d9583d7805f02a4d683ebe87846 b32389352f301af4a588d53580b2af6d76947705d45414e18c4ba8865dff0f33 a48963364cf079b4fe0128013c3d937ec06ac6d3cc4adb28165c39a37a0319fb cd8c533905d1ff627b1e69130e3758472c86174adb5002f0220369d51133aaeb 61e6c4d613863a1bfa1f9124380cce995ac5dd90732f5979bcb29d42de22eea4 2d0723b5254153eb2a17896b7fc15b8059d29e5005020ef172aa50a9ab7ea7a8 8a743f0297f0efe2ddca293984ee8d3db4f5cc461c2aa9a487f13494b59fa25b e6a3d6ec91a5aa85079c7580779b393a04c54782b61e0bceb0d0d0275cacea69 0df4d37fb5c42f35624003a5cfca5c3d6eb240758569b5d26f16616551574ab3 c9a427a5d45f34e865751f516eb16c1645fc0d34f9d3f29d15defd41c90d7c11 d611c94f2cfee7f21c74272603f669eed93b9be74f5e0aab045bdeaf02218e11 4a414aa192df1dc95b7bccd5352c3dacee9da798a587be8d0dcfc2d932e53c23 d432cc97206bf2028e875b0c39e098029670e2c499b88ef451b95c526bba2f09 bc4594e40b1c06c2dce163483efb045e8378d73d819d4364f8fa4519c8704a16 37e6a276d50c83cb2ab5a9f7ced57f2b229edd17e2c91fdacc32d93925f10145 290c5152fffc9d95e8dc1d815fe633cd79b1427d50c8db67e8b68aa2c4b98470 8fc57c77048100572648ff75b9c5d82dc1c0d0fa5e06f05afbf9f201712adc4c 0bded12b4809fc9ac013e84373728305e8464ef4c098932625294a4011450e25 b8e535e32f58d625101ff5318e30492fb54507ed21adce526c366d5d3e6830e0 f8377198a04f2faaeddb95b3184eda59ab45d52583bc8e6277e326817f5e288a 8ebb388549bac9d0bc58a50efa290860bafb5c558ac5ff97014e5e91e2b5d6f9 80d45483e6d7e55efe1cf485514b7c9b15ad69550f8228adab97dc7dedb77f2b 860df3068a952468d7de7b480ee3c114e2af6a1bfd57718ea70a532156d316ae 1e7db7133690a920da094aaf299bce31255ebbd1ff89019334069563d97a428f 4f729843f70c02f0aae4848573fa65fc44fea293545643effc5a0414cbd02b72 f6ea093551a609547278602ff331bffcd788474b51a4ccf1576e9c8f4c351d0f 0d1425e162b53dad767159eefcc68e3481e7aa15cf5dde15c560e86e9cb0735a 0e8af77622167bf235723615c1c3d7a819ff60db6c5ae32e80f5a2856a68daaa 1e59be38167c56a186e49d712150083c5c1dd7588433b428e23062de4221fff7 b43c12e5bf504f04fdc66bda7621af9e8660bdfccb2816075159ac2713ca3f2a 9f7666358a33abab5fd147f4adcdf57bb32e12150c079be07d8fdeaa9ea8beed 521a52a0b3e192d19c0d35c561f27f6eeca58ff13e58d529beee488b93d2f3d7 7e7effff3b399f59166c50e33f5cfc0bb5260ced230184bf8992be8dcf887b10 ace89f0a5fde0334b8c4cd6372814c0e424e477a71838d4ee4be6b063ef83011 c6f19f9441c7443c9aad2a73076f466f5e4f905ea61c3f294bc20b588959ee2a 2a635eef90451e80ada03b004c75fab54e15e69f569f9c3e9129f58c713f2b49 4e822815ba216e4e657c7a4ef7937215b6d62196d294345f321589d5dce6e209 a68901cbeaa21bee99f17aeda62c6fe986a2938494df1fac1741511c6c11cf88 43d3eb9c030f4d9964d5e9c6ecf2b639525d633187e11f0da531bab6e5ee8a01 c0f196df45d950b38ec8d2629d12aab07856874494c29f71c6881c1d9854d2f8 43c9afda75cb063deb114f4b37dbfb0078f8cbee6b8cb740b97227cddffa6a18 d75e8c0dd232a4a241372836d8f12afa216dc4aba3cca2f7238d37cdaa54e1a1 edf772170c15f645d820e11095642e27a7fcb2a1850d1fd08f3e8f17655cb480 9c47f9140a0615c1532835e0f0bd405ce7ccc8b385febd11a1bdf68f3e921259 9d73a698ad37b84a11b02b49d6bfc96929194baba505645ef1761bf23fedcf61 2643526f0e6e1cf9f2af821278710b7065f0cae0ab880e9058c329ffdfdf3ec0 bb76790fa787e08a246e71857c921d4748b40ac6e8451baad291e7424edb40c8 35e56823de9d39318ad185de2a1d4568835b54a6abcf49eecba5f97ec49006c1 9c705590ea073318a4982ce47e67d422e751ad1043f7510e6a104fa8a7a81496 1bdfd29ec07987e54e8c928f680cc028dd1672ee6415727a8d66639cef804fb6 6b389fe787a31b450f1818ffaa0db20cdfa9b6880fa0ce0a7c541a90dca40242 dde10545b8e2fd54ffdfd483c18afccd262e204605538588848ea1c0c99efb03 4a10e9ff89c15561cb1157fc992e6133281504420f132623b9de3174658d28c2 c05d11b8468a9c285d195c40436785b680eff853135ad3202d85fd6b3b71efbc 709d67e136f6fbb15c6b209d56c894a0012d3a3010bf785ec6cf46565c7ecaa1 a551208cb6283ff8ddab393d47d2f930957a931a70212bd55fb83eb03497ffb8 d65abbf9e83cd429a72a2c0b11e878e5a4de06c256eb6ae8b7ece0ee7ec4e899 a03de6ac90e23e9470213a21b251e70e74ecb35077f2485ebb2f7d150c1f79a1 83ce4ba9a0d68fe3ce9421f42b8a010c550fb6100c2f8ab008be42902f3c036c eb4edd84bbf5d3299f3dae898586c55b4483e0afe6f4c253a779b59a928d21a6 10b0465dbbc0d6ccdd8fefadffcdc6cf636c7d6eda6ba735718e6e0bd4ea0ee7 f77556104aa4371f176d65aa01c0c3dc4eda40e10df76beb443b450ba666c16b fceac9a002728d8175618729f289d0e9a1dfe429ef2b9758aeaa63990daf74ec 96ac9c4c4680607dd545ce646e48ba793e146988aada0c9c0581a41c7d9de1d3 0f749c6dfa9b9d40996764cca834443751a654c1396161ff146f145158946816 873a8cfa29fadc9e3fa66fcdba54a99c0d5a645880fe162ca31dab20003988f1 a9138ec342b3cfd9fb544a5e5c148d2ca59bc62f54d27fb22c39e6f6915ccae3 e43bae09f5a5fa2596ea6f61dce8d81f0b9d51ae509cdaae1a24b963c274ef40 7a1b36d60806fbc951852e66ef531c403925e9bfe7ac324fefbb2fd81a43baf8 fa159a7bfc0fd40d2bc5a70a1bc9852bf10bcdef8d8e2e1ca9c62161b719e1e6 ca533537e8ea96c9b417a07b70a3ec07d0ec256f1bd14e8b34e4d1c89f72097c ae8e7c402fa13e037cc53e9fcabf06811b17afa21020b647b1dd686834a2d67a 6ae329a6be8103e2e4976e0950fc024639b41b8bd921ce630667216be4359db5 1a5b42a72f462555ea91f9e43c52f9154feac0f2b4ecdedb450143d090b93d06 f6e54cd2ebb518244dc737487da1d95e303b6ab78196a6c9c59212f18824b0a3 51ad7925ae3bec9cb057ca255ae54aa59698c225e5a0293e09d0e83fc4e5b7df 4984ed3a617023b33947a5644cd4bbe8ca79b562a091f7f61080c96de880a9fb d9fae11e8d2eca9d88188fa1b5dd6088ce1875eedb85335003b8aae6abef11d4 e20fba3947dc0003f5b6ebc49a40df8c029408546659375d5b22974c7d3ed1fb ed2616df75233d9c496a1f0d130ec9bb0a318c0b05047fd8bdcf1ebc2edf62f8 482f06cb80351780d5d49898014ed4f4582348c4ad33f283311027382516c54d a59efdaecb58328250fe5e506a5afe36662a5265cb4782eac0b3839761e87b5c 8870807b28a03b28321d06f6296ecace564cdb01d7f37592168b2d2ca075c7c5 62a59c15f0a3dfb0c1949dbe156c2294db9f840dc3bdd4b342b8405aa28391fb db2b5cf69db1da72690f3d0bcdb621e1ab6bf2e8e40e1d0202dcf35d49904067 31aa4a6adbc872d7d62189c83880b5ff945bed0da282b75dad30a60790c0f33a c93ac37aea14d028789d9db7fa08d8ca9856c4b1c101e5f212a2bbd71e1784d3 c8bcacc9a538112417d16664b694a837e6d397b1a4de3aab80db4d326750cee7 a784aa9d92f6e9379e576afd1175a0e5b0540f3145173fb30784a830e85487dd 0a564ec2c2d56e504bbc96330a884de705a47a25142e44e039b915264d74fd3a eb71a630f352135a48b03da1d0cf9d96832fa4ce940e6bc1ff73c47371b47754 36431d4d8a33fc41e52add24baf42d705860d5cfc83aadfccc6e4fbe7c6c972c d16de71a298b57cb77398f2f0226415bf28602c439bffe0334b0d68905d219b5 4ffdcfe47d26d0d30ef567366c6eb46275989a8f847b39a8f30d89a74ebde012 1c1832371c681f58df93c6c4fe33292285a801c2e21ded6e8191f80800b981ea d5fe4823f4b5c221a275060de1d1bdf72672dce31bd3f2e0a474fa8c38c41077 bab9b85dda37d0559958c8cb542609ac95a5963fd4631ccf14a6535d320cf9e9 15f589eed4f67eb0593ac0cba466f4b1be1bed4fca49aca54536a4a24a77f29b ed67d0fca43e7dc33c83efb68375c3a19fa8eb4536c49acc27621ff542e86ab6 ca9dbdcd0b47bf2adb0e773a0cede86e7dd2aabc5e6b95c2fc3c1b8c5210ae59 5e915792e97503b657a9338082f5ba9b24e2fee4d1a2938a3acc902e3dbf4997 d633420dafefbf863766da7c63606d22239d6548894e26f06d891bd406b21524 cf4f849796892dd5adccde35a74999c25c6ad3c803718becb4243ba6d8c7e1d5 13167c9c0793e87af248369823f245d99592a43c60a15349d44fe233c5bb9f2a e183538bc5ee8ed570eb66d393fd056793e269b109043eae5c01a0a090d18b3e 276b50ea1b15f2a45809d0b22cc7834e406c1c1054c0c7f7d8525c32e10719b3 191e9285d6d24e95fd7934a09d44920af2042291e4c9a8586e0515225c8413e6 de9d26522bf6f4ed113d33a1443c5f160de08691daf09272e7ea03232f4e6e84 d333341edd7b38a52be376f8287d13f89bf6b01dea8a50aceb421d9f602b05a5 b8a0364136c444f623e4dda641be4088ffb270a4c455c6e941d341681c6d6d6e a62fee4d42bc6db241ff2513f2540f6c7db4fee24b129717e3004f95960397af c259025eb09f04eb7431f508b3c6999a3418bf6bab8af9b55cb7d3fa4eaedb87 c27c59ea6664edb562515b3f4c71715483aac96657e26017b054f01777db8887 f91c66dd8483e994c9ebaac3688076e8797d4d67668909cb2192d279d42379c9 9f58c7886468a9c83ba1642052eef85ed2a45ce928fcf10d173f4ff4c89dbb44 4653a8407b6d1cc000eb05b1b840dd14bb6188ef1d37a8ac72e12217fe166472 93b14e264350b8c30798d2a39b9072feee77dda2b52e375196b3106022b4bedb 5273509157346436df1b7f67faeed0c0a43e999f398466c246bc2e9286b87459 10017a84966ba1304aa896dc9fc5708330f19054d064687b39376a9fa3241fb4 9d9063ccd0d2d540300637243750c6bc652c1f119651f81140e5322879d82f8a 940eb9dccd7636d1f891ade6e4306521419fe57030ad7175cb3c8da0fc72304d a820c82bb599fdf8d086d39acd194e39f132a85f18d8300f4cc56a8220d225c2 233233f3420506c6227ac4a72e37a4bc907d63b291a4f32d4123a7da47159514 7fdbf1d6f41f271391410a05f7a8d30e55e1777905673b8d4b3d5219a47d656c 2661848fb57f9b929c0982fa6fb2ed2e504d2d44448cf087952dde5ef2ccd758 07621d43208d862972ab719f654d757ceccec4f52dd70f013e2cddbb6119219d 53f0af85e186307dcff8b2ae1b87f9dbfc5250f5bfdb77f36ef3aee934853150 34c6dc33ff9b31f5cb61c6cd9cac5a57494ac89ff81f6eb768373507e2331f2c 9dd70bd2b9228331231be2a27d40d3d9a1bcef5c5065ea456f091d554b47e5ce c3f51b37f9ca58c36acc1832c7a0ae39d1a4cff8fd2f754d3d05d546d2c0c561 362b58b15e123a74435aea20c0c96ddb5ffc7413018c4ff6540b6c3b941d1b88 445a8726a0fe3b24c15b4d1df672059bace0614e07f61b3984a624b9eff63aaf a49862a6574460fc5e9b3432da844458d407ab51632f28b02aec0d5af356937f c9d3ce57149377557ebb03ffd48b85bfe30f5fe44819e2d589ebeefd05f4da1c 9184c6dcbadcc0425ca7f69ed9a5f120cb9fbd9126c761cfb10307c90f75af22 e5b3ae461948bb71e5d5c1c408cb68a6fc1773b89f2a16fb06bddea5bebfe84d 78717fce1d774e4857b31faee17551a3c70a161f9733b9e82cae3839de372e16 5259c3ef1249d01c1b4270759a51df4e9c04dda8a96eebf5031b9b49ad47a5fc 2c0a3e8f2ac581b434d56652e75893f86395d8e34e0852ddec0496e35e286b91 3b2af796e8f040399653cc212a709de714d5259e74869c8ca1879c4c35b16acd db2975a318a9c2e4e8fa5be0c62e49d88c4b6606dc1d9cf71538c4508d68d92f d5c78555d2a2a033d104c868c3d908855247a0a9b31047b29d169b1be2eb20f1 d1a7e0ab08e9236ee740f2c42c57212be40213c9a5507d1f383d1c8371b47669 f7b7d65be98a75555b03e382aa01c790f1952dc8f8b019a34f2d6418ff64466d 773bc5294e59c1a624f89f25b359df72dc603234815e17abc0afe586dd33b264 25806ec523cb87bd4c3f5425a78fd7f879978b9d0a5bc2efc4db3a80340b91c5 1a0d6042f55fc72643c05ef6bd3a0c3a05589780b9c37779dedfabfa12be6926 4474180e2cb3f2d3ee664c63cda703a82d49c0da75dfdca8aa10c173a85d04f7 d2f254cb36b039214a42b95fdefdb45ccff86bcab3f38bbf3f06f7bd6bd6c324 fff1eb7b3a6e85f22fe1cf95d3d7e28b2c750a3e711b75ecda1f21279301cd7b d82cdfe7dbb0d0d15a39d392e5529b61256cf6dac2ca0c04f10c81feddcb5502 1f7426796d532dbe452a65916e56583c418ddab1d655dbfe9e4047d5fefa95c6 acd10a2211b93dbb4ab82079d09b712f7060980023a09ce67440af525afadf04 190 e0e5b56710c89b6905912b5d93190d46fd14c77416acd56177225901d85f160b6f772504adda9dfab49ea1aa2549be7f7357e8c57fd4b950a051f1a448bcc40db7bbf6f8668441411c756ae84cbf1932c95891859846cc181e28a746310b1807139982afc11cff2962a9410528a4c73144e56109b56fbc55f07075ad7dc91500982f633d6a0a1ac393ab8566266e05780f949dce7e3f438c29b63d6a05b69d0c6c52564680830cd0ab643cd730839bf5b4a3ee623e56dc927abb9a322146ef0670fb48c4adf0a26aedfe3a1441808421022c9974e3136097a07fbf2b37fc700a557b7a68d8fd39418b74499ddc7ffed68dd834da6af967e17672685a06f255017e2eab40f67985e0a3e787d352b75a1ab7ae983381ec1f42a40bebf0e107f80e5b42eab4b8669072a7bf00946d8890f2e1512645fd1885d845fd64060ca9e30c6179f5a48ae2377ad79765a25d458076862b56f06aebcf10bb15f634dbb86e0b79e0aea1c5fc1640436b9d33f97786ac3094feaeb2b76b138b722e978455c6085399c288f25a534bc40d39055c3f469780372589b8db96721adc65fbfae41c0df5e758369ddf3a20458883dca10e6fd1ef5fa3e19012869752c5c30a4d022b0247e585ecfe5458932c060abb6165803952efdce7c0333bcef3823947f00412028612597822cee407c27f4cbd902b8dde9cdbc8ae6630c6eaa7f154b9e9bea60319bf3e88892fe67a104217c1ecbd02648d861a44d6c128d55c56acadfa037b011dc6201b4a13909db017475218ac303130174040563a855978e883582c1ad8095aa23b1265d9e7eca8434455649d4e6770b445992fe368b15abbf5240f318c0adda4d6a8f06ff6f89328d45fd772e6804c4658567489b5818a1c16c241a4470dd500e3a9883982b9da0522853d835d7b411785c9eff31494bb89af48c279c505a2e2dfc13aeba8ee39aac52038bf1571c79c9033b269c8264b39a616a85a50076faae678254409888f5b1124866f01f3413dba6ef86e7b77d9336c0fe01794032b4f1e048288dc1dabbd94b0ee7729eab754a0d35eae60345b1ea197aa1e2608a352ea941ba65864c883e406e512f2befcf8faaaff3c29a8cb39277fc1bde70f59d45a6e7efdcca8f35395dadc7db8c91e869dfede900888518e62e652e9be061a5401571aec991859219bfdff864e6d10d0e74c81530c5c856a0cce0eeb0000c69242dd3f8918da327a46909db295eddfe4b42a3b0d229809186e57f4dcbe050818ae303c1e2124e11b949f6b0fdea7449776394447c7b807f46f89974dcc005291af72b726a8a2eca3e26b34dfbed3bfdc372de904ea64b88129a2fdbaf3002a8abe9a17038aa81120add5bbb856677ad40622d8a6a0126c4fb0313026cc0c055fbe3230ef2b290ac14c9ff173741b008a6fc17c8961a4a431bb583627010717403319af8a383ad64d0c68a2c28ed80a503ea473c199b49e06d1f5cc056005124ef390367e389a6ba862d827bfa0e64120322949f27d5d3522a4b039943b04f0bfd20b793442f74bd2781afd6bce8708e41ca6aa9b0894d4b4480e5c5929090874ac770c68b5ec20acc49077d1e512732b591ec7e7668d8acbdb5f4a3bce056720f06026fa04ed2a8d928a6a44417a983dcfbc28cfd8323861d2abb6a268018b3755d48c49df8e7fa8bc81c49618639aa6d9c0eabbbda56b5546e68bf0bb0aed05d51e59de326c34eab6e5ad0e6d6313b13a572f4e81bf49852b7c6583460c43153fb732723913d66f34f0fbf9f2197fa21c0b5736b845c3761f3ab6d71c0174b5949ee5b0e8e4c5139ba340ac0a874b3408b6ed930a097bb4c528706dbd0f2bdf376087d3ea5e565537fb91d89fb5c82e34edec4de79bdfb6295e1194630d3e4b16ffa9e030335fa329d3de09e3d61a151b7bb4f7865fea02847af6b58c017dd5d09ea2a92ae2c1d146793ec17d82990ed31270998ddd8f59b6220527d709b640e8102d3a1b3817d3d33e3de6d12a11f66ba893b266f68744463fdc21c209c49410774bb59de4fcb2b496cc5ac85bbb99a68844618b9b5068f2e1e2452208acf255c33d8a9be21eda174c1adcaf61a08a4cd19c9bc148a85f78e9ff24030d492017bc46d829aca317eee3f82f1a5d4f48e99aa17efe065c7215a656e1570b56088257b444966a3f257d692aca91be53b2871e6638440de6bb8f31e469b8084d9403a11f219d861c2cacdfc33b35938181d6efec16620b2f56662a4506540ebdb6638648361d0c46d03ca92969a087dad38f95c6155b3f0ba08f7f972f520fdfe68848f59bf320bac3f6fe8f0d4ba0ce1e9382694106a5ea6606794891ce052031e7cabef2b11d748f53b635db2f2ecc4e73f8563c55399daaa6bd2ea7570beffc472b5f4cb53231f2fb4e3f2676ff6c05eda6d80c76efafaa4d1c770b04072d8442b5dc23f3fefb0810861b1abddedb4a604f0381ac0ac3874401f1ae9f04a5480976abbdd70182ba817cef39fe7f0046fbc7c70f37570588affe7ffacd080c0741c25bcef17183d7cf83cbde7d8d0c9b0fededef7a924e07b75ddab0df08ab25994dcbc003c70d8b7c2d56613f8bc6f89bd2cc24f5302c87ad9acef6e0080eaa548b00d284a587a5d21cce894c750cb2487212b97a839c985eb0d71c720879227ef868aee3c57207a5cbc5b5b2cc7a9434bf665d11177c237f4b0f7bc2077b9d7f51e2edc55bf66480afd0777644c287e7c562dbbaa3682c868d2681ff00fef3abd2da8266689d3b005b1d812f55041a65345a783621b5e399b20a33c00719ea615e443fd40e431565115a0fda5aeafae37f2bf622e3f4e74c456cb3140b63dcb1523d841413687be06b0bdd2f783ca208672193e77d93245225286dd207820989c3783c5c8dcd6accd3731df33fc3da5a9ea4cb1888fd9691ad0818c00bf9313c27f4ba9af58209d6254bc7db105b3110963991be8d9fc2ef396028100c592ac03d67a52113ea59e9bee05d2cd55a3caf55aada1405582063357e82a7083b679d947f0308a9aa22fa78a348a0c84e1c27afaa0f49259ea86a72aefb4206fd3439199437e5219b9e3f626389fd19dd779e8f260646ebb4079fbf3f9dfe0e47c23d32e51eb553e411ed708d09844e9be693c97b1f8e04cfbcf8864502fe02778c725e20a52b1213b49b1908e63c5a01e04735a6aa633f149b53049785dd0fa341f0a72003e112a5a25622cbe636dd81bf5189616ef90197a1f5cee5ac0f0c8c09dbf72d2cf6942fde5e7395be69e97c9a9a831c3a90fb3020c39e2042170cb472ef176d7f94ba117c5f81fe49adc7fac348ba27fcb00d324197c7eca4a504ad10899c8e68949ffd867990984c1f333aa965feeba8ef4295a844968117e50c08025198fcb4e0f4c3c7c0bb804870e8c2d94c80898be9bcf9caa70e62a1a608801ee46dabb9844ea170fcd7d9d2e200cdfc337fc01b3fc8d45d95f39c11d203bd747c153b48a51d995ccc7c545c3c2eebe79e54b6a99c7c81b41809d748d209ddf36e98950b3b254f6e500524c276342764aaf754e54c3ffbb8cd3d6300b90c6f01bb503680db4822d764611dd19777244eb6f0e65326a514cb5a4594b45b07afb9dda3f4949842e244af88db19cbceac5236b29dae48853c19d2c5f7b7f601578f66701d4792af936a8f9d5af36ae32f66c04e19d2053c3c618144ca9c9d030468fe5be2d4bfd8946bda43ba42ad3297068854d532a5ae30d9069305ad7209c80b0ec1c3c555ecf8168c2d07c108ced64554e1dfba9c847e6637b36a3cdc05c6437a8aa793887cb3593cf64e58a32ccd3afe2a56b423bbe1316788084f6b0d5f592cb4f04dec5fcec736ed25c1e494452165d299deacfb053135d23940fc086f43906197d023f6af703835583127e316c4ded2dd4f2905da84f64273e9b5032db5cc85e3239b363305a401dd5ae7b45da846adcd7e9b242ef050e76639610e0dbbe40247ca29feb097d5da8cb83e3c7d5ca44a87daefe25467866282b0270ddb175d4d8b2d58a00e6d3a1be655ec920a47ad2a1b6838c20ae5fba3b85d800961b591fb9f656da834f2ed5cfc4513d3577d2a3e6dfeb06d9ea306642f99b405ad8d0ecc1d7b6feb92ef5a0990c6e2ac6cfa49b5f525bb65cad4fe7093d8b7072c3fedef9cd270e833289c67eec91e8cd36532306c84745b3bcba4e26f26a207e01c97da39f5c284271b83db54b7f6e0268b9700f83ee8b8923287499b31870621e7384e37b698a5a6e0e6340e8c46cdb0e7da7cd6b04042aee93e282bb63a0d86864900277b779f949f679806b8e84c62e5096206bad213c86480f7110d1d0d57a3ca9a97770a5be17a1ac97ad7c7bd033f7e672da3dfab63a65d3d8352e607a30bab6e4101306ae2b1aee6c6827e60aa2424f6ee7dec2ac75c419e0454690b1ce88c87510d00e01dcb69eac3808a3df2cd3c4fc166fd174cb3f505f1382408c3f644ac5ae85829b62a7743b7b927fc7e3ba77a8d3a9544da65055b07f85506ab72b78e733fc115733554fca7e1f72e6a2b6187513d8c2f8101dc5325488e08de8c01266473fb088de76fd9533412c321d5ea7fca27d3d7a86940e3f861950e3a3f7c20bafdecb6b1692be84bd7fab818b1e64537fac919bc3b4d8ed91f9604ea427e79a750cb7a62c5cea77077db02628a55700f3256d04c74c89a4eb8350798ad31fe813cc138760418bb7bb070d31a12e6f554ef0ff1001801074b6d510b49e6c4b7d5e5f148138e58ead90cf74a85d847e0028fe2acb386a1cd0e74f504647cce52c056f691445f8879e38ba016a394296a76c64696effe043d7574d00cb64887703991a0ad3e05c00fa9881aedf2a7dd42bb8fb0028c1d8bc80bd5100c07fabca514b38ad34372d732dcbefb185ce06086c0dbef0a29fcf19eedacd900a1432cbc35f0e70db1286d60b334b9c706156c43a79acbf6b60c686a4f32760c4fed0e56a784368df788c466ac3f42bd1183fc505e151754cd3dc0fef66be4083f76d7d1ee5c68301b0f4210e2112ac6e5160188808951ba1c27504a64db1e0704254e961905ce11326d87085d8a2b27e4fe2deb60ba6dd0a70f25641b52ae0cb27b2e81beba5a1059c51c232e84478b8d1fad044a1c2fd5913cc9d2f435590884eed1456a21f7a32a122ca17f77037e385e9bcfd26ab9f266b9f0e8ef25f502a8b078fc067a70996d3ec5979c9e866694940480324303e0ea9afe5fdbbb8700f0fd271e18f63b313c8926a825d8a7dc4e9264da9000d6f7c05470237b6cde0bcc33f51f5812444ba3a3effe65fb5bdc75d1a3025cf0a7d902af749000db4704f1fc3f994d991e9d7c898a19a9d03f9e265f34a8d7d503066d6bdce43922b90b753377ca54b7ff031f5f177f4aae7805dacdbd351998a27d4213078c568b8a0040ec2400665e35b4bfb9b713beac4592662535180d4275ed7659af611f876c0be7e8ebbf2d4eeb999a2dd6b4a5cd9cdab39f9088c30be0200a164892d5c157023ddc86653724953cef780dbbb150d76d8efd3f6bde2d26d3861b351f13b9e00efefd79f2424b143bfe7f0d3fdd346e27de4de2fbc6f4a12173ffa68449d97c0cbb859850e7303e597bccfc4b372c5ebb2d4b591088b98e6c9db0348712261b0cbc87167912ae868e85ff1008adf3ab59b4a54089294585cdee5117f486c83709faab527c50c415d26967101e613c25bf0c9238ce685f99c6f83611b72d073a054034dc2066fe5216a887e509784c9c6a794f86bba684e345d77b595ac37e320b482fd755886c6ce4f76c22e73c1c9cbb8e4633f6a97882d5764da0319905c20f5e110622a0157c689be8b35ec4484d903679981ed0ce293015a2ab6926a84d0b7d91695756124810049fd53fb6759c4c15302f49e75a73625ed1e43e4813060802d1aa9f4e02ad255a200ddfeb7aeebd1cbba209b4fb31ee77c25808df013507994a641b3b1397cbfb66c77e1a8b8308e35baf1bb57c4c66e687a2aa878b6707c2ff0024c004402bb0680fe16e27e020a33c4b8b63187db36260f379445d940afd90d0b209412b98d1ff3dd82504a85c1731e1ca912b23ea491062e441bdd70de74ad8b574cad5b38c740718938b54072c8cf1e6e2b0aa840acf4ef1e0beb104610b337feed6d95ec83ba94d557a09f808494eb73664518f47edddcf1ae90b0b3d07a15529b7f8f38489b57826d7393219a234365917755ef5bd58c5fce71504c584409e6b661441d8b1cfcd274214f448e92e3dc60cbb1c536f0463770852077e8605756a28056ed72ff446952a3634ede88d13f6cc3a0667757862b5d81b09d148d15844eb504df6a682e4e6cd76b8246c7edfc94c7b33e1d24c558b29510d5fbfb9eb946b4f9eda707226f3da34b365116cb953e1333d44219a94ef4d170e10ade3e9211d80eb971ca6b91b214ed168a8e29335228e6a91bab45ddf08790f6f4b428043d21eb53371544d2d7bcd6ff1a27ba7f384db5810003e7e73cfc107d40167f4b7992df9bc168d9abf68b495460769b84767869a36cab441c0255b0d48f2b51f729392c7eef43304cfe541605d34525d32ab013a3a7817f451bd4a056f7c4d21c56a1618da1ad2fc7e379372213ce4bea71c3f18f0f579273ca9640405d499b0d35d7d410b57e01182b59911ec9e2ca7fb87e6bd4d9bd079c631ed02484bd18ad90c09aaf8670506373e54544c2fac82e891b3324a1bd1ef996ed209be20a5a53da64d8b1c3962f268f96d08f4b81e3d3f3eb756560d4e468a50430736895be5ce333264aca116707b5d200182cf3325808b078b3f6dd1ee20429d09610efcff54684579bf487675dfcc11fec86f6559d1da7e8e12125fd5315b910700ca2573dfdc5beb5862db5f88ae4dd72016ca722218e9a0f13226448fea5a0c0bc7ca27e3ffdb2f2f7ff80a8952e0871b9070e2d3d0b4c466ed3b7e3e146c0ee97b85607a2bc7de7253fa5bf1d5518d7ca6bd92a246502710a0b3a13bcf7602c2df9791ed0744c75fc227bf74c38613e1177d9617904a244eca2726f824040ee1d2a7c3d8e5d430a43ac409ef65fdb4dfe4327d41f213601e8348384221220368593d426b0a33d4933458b1c207217f82cbcf8650cd0161482d69e66a0f810daa72f1efd3c03241dcd1e75c03da07b79cbda10e06d68798706c9145d82dce0fb5502b96ef3eb5b47d87e617174fbda95a7690b34928c8da1a182ef0933eed05261d3f1ce448f4f342038806ce3fc6428df94bd4325234a2f45299b4f168450ae38f81af1c5e128173891e021d7a87906811d2c688a8c188f10cff72e3324e0d22095d9a89a30ae570c3540621ce1f28e3b452fd31d410b7bb8a462e2839cb06fccf49086a4d22cda1e5f023d38e79cef825a4d7d9258e071bf0bdd8087dea034185ba7e86b85b5c902257d1616b7c94960b64ae8538880bb59edde568ef9208050f0a648bfebb15cf919e4bc29ad0ede506d8121d974c56b07b1f63759fb7073b7800e9e5bf895e7a825e61d4d1aedd0c9460acd391f2949ddefae69f451d0b3e986f5f8ae455df05a629f134e9f813c888765cf95046c6b16a6d505cc9170eee008feb0acf2498583a9be0d00d613a1fddc81b8e534185a79d7157bd50ab0447d0fdc6368944dad83e986f1e4906df1590d71b4067fcef8fa6fb613fa306042dab11a214f7cb3e9b2373233a7c7c1f9a1f1ee4f7158ef559ebd4965aaa3e0d5e47aa407383b97a8cd5f13e786038e92d900dc84c86321e19133c261af5df0e09a47015c1e5aceefcfbd9d757de9d873c2ae1be3c36c5db5cf3b797d8d760081ea48e1ee12f8b67dea697feb474fa182e2060c8b538e16eed5de732d251aa0a4aeb90e2eccca92b8778a0e1f504dd4d75ad6e518cf6906d60200879bff98907a2325b509ebd5e5cd3b53c6e2397b22b7021231ad881dbdbc03365058a572504b589295e9b33eee2f2b0dfbd2482b00a5fb6dfe9dc8d54ef775880dc11674400bde795a8e574808ca5ee84a936e01d4acbfb9cc0bbd86cf5bdad7f6e090b020085897d3f628c2534362f770a5d20350589be62f77efbc7da311ed79d9d2f490fefd460271a8addee32b9fd4e6692234beab893bc3a3613f350b091a84502e908ed83fc164ee45870e27675cd8df3707ca09a49c26b208fb5ee72b3891a9593082c2933aba8e0868cbd1af14e6fb72b36af8ba1ec6c10551272b036fc6fcbf302845b3440c8d8cf3c079f368a6e653b07045b026c54ef7fe987daceb4725f9c00f2bc1bd1233574f93914bb3c9042d7c4b0b0c272ddffa95e096eb7b64441320ce36904b5b07df38d1ef9a22ec5b183fbc3eaffcf999112a16c23b8fdce5c0704eac81e503110e59156fbf64561b857cf39e156476e29ef87f4a6c08640fec30b7faf06bf4a9eb1e19afa39057bf5d2d00ea352fa0681d64fb7b8325eba168d0dbdc5d283f631b0c55f575f76328ee49c99a65e3e7a0d329d3c16559d1de4a602cc02e44f1852631e7e190c10a70ce79fbbda7bd8915dcbdd7ac1e23f6e34a900ea22a7cab6b2d19a6802803fd9d1765b8067273eee4b80f603bd701c5957040f81e74c0eca1b183d7e564bdafc9d460cbeaa740241f72d0eada097aeb61fb9082be81ba42d22f3741b5c4d28cc59b99be2b9b0485acd3bff88dcaa5cbb15cd0e91f601ba0eec61f606d8c70b87a0fd02b769619da91d88d9509dde0cee67d900cdff92781a293fbf52d10fcd52ddf7089c3a9ef8be56837e52018a7d7b0d3c06c7fdb183a424ee4e38d59cc7ba522e8e2fb13dbd5a27d9e7b9df116e97bd860aa04f8045d8cdd10ff8a4a13176f5bea2ac1f4e699f12c2c6f6072d793fe99302a4376e7e54c5059ed5670197918eef15f657f6996cc3c668950e5ad9a96b840c836771cf1f28ae03393b150c12273cd6bea973cc4d7d8599a1aa84adf3bf4f0bbc81b38cb8b41dadc99dd5518c8692dc669defe87cdf3265fb97a8d364318a0fd4f37564cf4aab35de09f4305a3ccd12ca7c647278e5442a8ee61b1eaf9c5c0758d015bc699791c37508ff52835e1da95f55d55060c6dacb4350c32942e0cb0d2d8f0e1ddd6a393f8308f649c294f3c505d429582f66b676ee50873f174c630c221502d922650df39c8454ce394ccfb9df4e1b5563ad16f3ae2615fc6e33e508818e79b782b533d1eb5d79e929f1af9d38bc2452bd285e268e19bbfb2a99250d760a50920aed383fd1636e6ec4babb5c1e7f0e4beaedf75281180f1344934c056337040d33b411f4459b68020a9e8cc838af1e4f027b9f5cc17074418d082a0f525cb684a6a54c6d61fb3fd404e8b8c20a3c97802da110b58087253065cb150b93e3a27fdd2c89b7e269d708565354ba73b325d112bd1ebdaf5219e000742a03270205c6b573e06b3ce41c65bcd72574ca79c9a6193c6e98e98586672bcef50d731a79df35c40b007ea43a6494c804614f12b5c0a313ad9f575e6e1a13ed3a0f692ad2ab1b2b1805c074c4512049d58f8abd80dd76afba02fafd927ad593d60001bbe5e795fedb3d5687c879953046a9636b8da6e5679553b05871ab1394c0055e609a0dec32f69d360d96b1fcb7fcdb7be722232b34c51c618475bbe9059308de132a9e652a0cc1073146ad9be58c573d9a777b84329e1ad3ce61a256e05c0f8abc4803b2012bf2caa0e00048d9e3db981241d4802aa39c75103e8a91f3df03cc69e83b5f6beaf69834b28225c80a58ee43884ff524ae4b0b3dcc18f1f675082f12d55d2bddce8533317df105b4d8dae3b14371d13ff3c86669188ff2af39005fc3bf577463f492cb6a016d79312365111f9f3cc1cbcc14192891a2d6ed860064acd4d129e3f334c96074efd67c16863e93bda32fcd79d242a12d666a3da80b2937f1b966baa2a853a49e15a427f0ee0adc14913fcf7eb73743282c0a726402964b97d68c45baa50aa961508c3e430f44061260619c51ba9d7d0d67a434a4039ad6b26773669810af7b97fae7da6cec672a644dee16c308a692f4dc31906b058b1ef2d137e317a87761c18cfe7788af8de1038933f3f38232e18aa5165fa50aae0545aa4d5df4310bc004155527ff40dfbfa06a7ff79ca0c4978e058bc57f0f06ea87b9a75c95f34399a9300dd27b3bc379e1178caa6bd445a5b08f7e8cb106c6e81e9d06fd2057df5551e8235922f7a7cfe9095198d40b686a7971be7c7d058e65bce59cbd2e5ee05a8d45e7aec14cab8b5849ea0b6693094274a5303522089e2a4f6e79ec1868e5389206db372859777be80a10577a5d60a472b79874530a5c4bf979daa7c0ef9eed0a296681fc67d0a02419e6c9ae2053866b6ed7e92f054414e3a54c8df51ebc2ceeb10b26cb10683145af60f395bc19bf40436f128001e6af1a151caf5d1e9577a3b85573bd8ff2fff447f4a3ae99668a196dbc41070e1464a1a08b018c9763ad03ddeb6d6b0174844142ee62d803a7a3f07a68f58805c1c9404f12a06947e22fca1d0ad78e8b4480cb7b3b517a8900eaa66e525105051e4434976348d91fda947e0887d888968bcb4296776e8b5ed5885889da06e90b5dccc745e24ecd6de28a08ba6fef685ab2bee8aff6cb2538fc6f00a2fc08330fa6b357325eba9797023711e612c1e56f1387fae09db90a1b4a4054794e9f4d0ae0a9ec6e46587de68b6203eec353976246860faf9a60fc88b3f7c4191b6a3a056bfe51500bfb297c3e2f4c371aa9196de46c2684d1388a792b6e605034253c0c0c38e28498d5a6ff52d78c62e9d40e05485a0febaba0e2b37fd2b4afeab721001bc7d0925ba6417c141c5ccfb5a424438649cd70d8a35ffb40290365de01270551b7dfba790a506e528a8282165809648ad15ab29270d5297ff13497575b170e214f2fdd82d0d6c69cb32a457f2057cfab1d8f159aaa1a99c59ca17caf9da8070d5683120e5e9618cf9e7f03e198ecbae43127d67c7a31549b5c5c978f7704031f101fd1ab5bbbc68f5af2a782308fea09bfb51ce4d60a68714c920bf337340e3bca9745d417541b02c6042ab9049359a497bc5f48bb923e05748e1f8adbcc02875bd792eb643ffaa485ad848a32168c87a4d1d4f95414b07a7fb07c74bd51060a048d6932c2a38bc827d87d1c6d9cb479c2d1049665a51f39799f5ca0d15d0458ac85a2ec8235cf5077b049565a678eab6c523531a15d233d50c9c094d3ad07dfaffca637a72cd96ab46180a0ab47161bec7ed5951d21d60f800ccf1396b20b9e3d7bece0a9ad9673a77d95210a40117c8918ef62be61afc746626863bfdc0481c5c4b531d362a625da44120c0ad73bf452ffadbc1391d3c66d12a5aa0d430112ac8a6e2ab872bb81fbf3061167ed45979c76c391d9f7a7197b8c03825b660c8886c9888a17a622272997746905ade13e92774e5b1768e3875012a9d1f63d04e659c0bdeafe671f6ec4aaa80d810fa1d574fb7cdeb86e929e42f0f5f92b36018397e5a0ae76721d994a32da4a555a8e0464296f511a8f9e050e7d1804ee810afa5fccf2ec13bcff5cf7154fc7b5dddccdcb3cff4aa08e0fdeedf6d53cd24c05e3fecb18dc5526727c4202d669e7d6fbcd921a5cb6dba90887227f381fcb5f090ff84c43b7778e47bb5b8b1265321817f505997531471290766535624c2acc099fc28e97363d3766bbbda47638b958471c2075d48d84a863f930d457c5e76705fb2a6b243bc6929f2777a13c6994332a122f003eee154c24fd60f971e0fdd20ef2871ba9a3c7fb2bbf68d00388b6b26e89ad46acf4ae921a1a709ca9b431510634805f76b1cf51809b496a79fb7069a542c05d3fcb107e1f2632795503a6a30c5c1a28d9d2afc8b54591df4b6493b66384a4a6a0d900daa9dfa56b407891270824b52b6b2abb96d3cf61d4aae158bc62c00e0a87d2c2dc9a797b8edc1a3035024d4fb500efd5fe823a379a15593c8d67993cb7ba5be34656cbcca8035f7327003dc1ad2942463e474eb6b9389ad1e6803fdde73925e7f8fcf683d47d8157c00c50239a1c4d207d9621dff960aab87dd18bc365b36a13b80d937a809c3ecda4051a5e5df9329d89b03e771a98ccf4833615f5dac627c7e89aacffc0ba065eb7005d542d8d48ae30766bbe9043ccc0256f7552d474569bdb735778d81dae3e32046e3a710f970373195c12c728f437a76d075ffbf6779a0fe7271645f08e37e7094a13376737fbd66fcad8fb7e1cc2a9a02ae25c66e0f03d6b29207f5b9c51a5067ff044552a82caa4260a34df846f6927dbe34749af17cb57e7d3c1b26a683c01136d66869da3419345f3fa323d7cab08cc1ae43296cb217684cd0242acc41b04246ed22fa78c7b83200590e9d4ee706ac36d5b764cae469022ec200340df9202985a9775b2c6e449e7baa996f1c69015a7a4381061ca019807335ed8c017e206029731585d6976022f7108dff3caed5718bff56f53ba8f109f7ca24964c2df01f10a0061a61b58da384953ca0465f4f0c0a4b48ca78f8b2ba1649bbba84d540a7e9d0f19b1250de23973101c0e4257cf5b91f1d8433791b4a6450ce77de5d001a1cf52e0a728bbd2df289a353ae8aa2f4fcbe78877251bcddcf8c189d14a1400bd4a65d1546ba357d1c46199ff7e4429cb8b29ecd446cfb1ea59d7c693d1090eb01c67e6d7a2384835e71454bdd4f6b151b27388a21a536f9133bedbf805110e814081341022f0e2a38fb646f1d9307978cd395a3d69052a427f40dbd41af40e045a4475d035c0b03c1a0955a91d8f691979afa99dbb9c0cc0d5b632c62f1d0b3923d31a37e8dd56ee3b4e3a6fb8ba89ef135ab5b347eaaaa72834e61cac320f6e72013948678e2858f39d29a79a9f0d89abf766e8f73bc03d0dc48aaa5495048566650dea62bc8a382b921807b5e602ce1b81f60d7170c43141ff49b848b504b17c5ac85f789fd9abd0925622023b5f03ffd409b57f5f468d5411493294a001d2da9e4dd77a2f730bacbe9a3dfb624a5ac8faa39377a7a1eb8d3a2eb5742004886dc84967f90ab000bcc5132e7487ae0b13ba7c2b226d622505600be9800c0b32b4de820e742c3358e75ff4330a3498fecc808b31a255a1cd1413a478cd0e0e985dbf21b92fb2ae1cdc19a7240e3a646a53102ce10c5e4c71a24b637a47bb037ddacb3e84c98f2c9d6d9b26d02b3a270be18bbac13d58972fd8e1923aacea054f7a31db8d61778d49e284cb03eb1d82b0e6280848ee80e96e7e3cd915cba80e26c8c2a6af34ba6867940e58f7753e6d7181b1b6ec9d9bc5315108d43e0065041b5bca8b38b5d4c55cefd44a9dff77d440e76832fc951f376afc47e318452b0d1a0d71f26b57af2b7d2cc77c7f410b48f8bce9e265c6acf07d188d99554a4301a07aeea9d979ecf35398ac4fa3eedc02534c50f8e6841770818dff3225958602abdc21ad474ebdd429a6332d803aa9bd3fdcaf19bd57c58b1e7bab9fb9fa8f0f3806b837c259acc0d58c6012a75314054c84167a56e057515d1fa8b2fe5ed500cac8aa19ef1878d4dc5cd9f1b489a00754f60e95bfbb4d4aed701f70c9bc5202f19357d3ca4e51c2a58a163e2d715fff10b0d7288bc557d45bc82cafc2bbf10fcfc2986b6ed4e2113fc5aca9a8f1e7ff40edb017bd874757911d8f5c96a9fa01d7f464d72a2a80634f3655eaf675755fc0e81f96a55f994bc96d6e7100c9970fbbbd7e875fc3588162194519485476b1aba3329cceca66c7697a15a30c48b40eb85769620eec4ed22db9ab7a4beeb196a94e4358464c39d7df59e8fc4852f9012a68ac10414a132bbf5ed54c585a9ae8e5d69414b4e4d8c044170f369efc0402ef1067f385fa6a6522dc79f7e1272dc2d8817e326a5dbe8183e4abcf6b33a4083b7f531f641e90c9c418644e749c9c564078c0cae8150313f0056da5bade9a0bac34c6aacd391e5c7eba8f8b9c9f5f562ad8a16e3a92c0f1974575d0cd39a20d90f7efbfb164798ddc03c0312cd3f7fd69fcb880ad1b5e73b78f8e61442cca094322c42c27943df2f81ec138abd29ceb7cc59e703ee881328e50f26818f5870e7e9118cdfd85cc34e79965750d74f7e4de803690b346395fa634f865acce780c8aa5289c8bef404a33d345ec0df8fdd8668775ab09d7d057d05a1d8bc103ca096e53ed546bd808915900746e38ba9f118fdb43e768c0c02d9d21e1cffde0c00953dca684886c45b3d34ae06b11434ccec98b85b33195b9ea23a7d6fef2635107ea5a6d6c77ee61e33f22ca4fe9cd92e00656439aa3538710303c489fadc9a5043eb7ad717d4d81574ce32e83e775b67430ee26f5838ba1964b810315106d7f05c1bd4cb51f5c893fc78a92cf2e711e39ae74683138a0dcc0173ecb930e11c8005002a196a236963073c8e1026066e738883588bc04e5178a04a6d2dbd25696077a0e080df5d40e5d3cf26f894fb8eec246d7d0bf0f1d8730f64c887fec31fb0435d218d4cbc5c42e68a5f7bb2ba529dd8aca08bd3d3393f33a3940baae3e850495dfd762cc5feacd8c146567f2ec17f86ea12f79c342dc5f70bc5b123b6b2c0b28a339dd26fd6c5343537f788295a16f23e43063424f7353d2524714d5415b0f32a2f87c9bf591ba1dbfaefe2f0e06b60a7e8c73848216ab32a335ab09eab90ea86e35405a07ee026fc43d4c299b7d158665ef55b5e31fa9a31aa149144aea0324cf58148ff4122583868123edb10517799f9a31a3ac6e6191b17927c9037601da5059c37d2e37cddb48643f25af43b9ac5e78372343a50138477ab361c4e90f1d83c10bc23a31bb32e5a2859a32a323673c4d839bc457a941c52f5869b2a401f22b108839c2d02f51cc6779e577473969fdc0a9e1505dcdd45a57dcf9239700c2f7c0931953f2d6e3d958af895d71cc83bd2ada24a287574ab9ab7b030c8406e4d80dfc5fdb5b5af3cbdc0925926ae72e3d1f24111e2ce4b08105b852e6ad096077576bc8a9ca5be6f43e8566068b7f3620234f7a119bddb6b714262321eb0c39fed73029b828ae4a0fe92f6d0dddb49bf7afdbe251107ccc0f6e42b320aa0c3db8cfd704111ccf7fb57e33ed71fae0f866b90ecf090043a27c0630f2943f0d39cda3158180f1f8fbf77da142db4291fbf8657fbb0a2153b1b09d5d9a778307325c6eac179a1bb6a54db77f33f0a055ac57a91956b9f758d35a8ed6d5756e051eefdc28cfa90541c8894a25c6bac132f50880275097c9b402b9630006b33b0dc3904d2f1f0b5ca838725911b227674e9816012386093f8b0ecd771bb615f4027c5e21838b6817cbb3335f5815a44b349baae068d4e38ba52f57f9831c90860e1c449665183f7a22a0c54551831c02ca83876f9f57c860f4b681cf63793fb8098a518875d846dc4fc9e2a2304213893c9783dd8c743bdd80764743eba8b2350f2e59c48cdd26e6d8cf8e4d9adcbbe034eab37a1dc9ba9040a1a2c0102074010daf98f314c905bf3bedd1468c4ad576e234ff478412f1210a2ec2485113a3bf0d36552b1cbc7717179728abd519987c011b1318509dfeb60fd42c706a77023b05106fee3a72395c528f72f02fb3dcb0fd493f87c8a4b356989147ebe416784007a55457089945f608435aff0d0a2d18893fd810a4616d6030270545436adde809349ac3a9cf2c927ec40428614af36868d14d627cfdd93bbc5f6cf8a21cdcda0fe9874ac773be6eb5022ca057a7f1002b6b9e4dd67ae4029bd16d6bfa620e7e0ba70524683c7ea28c1128ccdc8ad7ca4e558027fdc195607aa1e2fa3cca509c02dce1864f338b8ae1a0e6c4d20b9261755c0649eed775ab774a8dbb56f82ea70b4f1d4ad58ab8aef1c222736d42b8a39be6353ca29981119a1a7404bb21d4050d0f698e272311dc1ba5eff85d07688f190e8709d008b047cd44ca90f716adfc00f345f2d648ef2acceed6499bc335082b1b7bed72b9989426f01f2112897a1d007af110ead6941cd742ef1a0dc4c90963407fdc5f10d8898acb21be43d275ee0d31737d92b33fdc5cac1ed8f5838055fb13f467c8eddaa2294900fc1cc808e40b94d6ed317a76f3788f0c857ba75a8fc6ce752ec3ada66dc4302f776e6cf8ed0aa7b383becdfedecbf608e3df224304f93444e6cdb936fb148ada29635087b70802eda172f60ec61a883b9fe424274396c919f50fd07ab0754c8170a7ccb62c081ed8bb96c8381cb68d3267bd6754b6995f69f896151f1633768ccdb30ce8ae082b337d82313bd5172b7cf3cdaae73a08fd4f2d26f1e96f140aa062362a02e2034246a8fbe2d5ee0b6b82b15b6caed6f8f54f539bc3b593b9d8ef43a7c1574302fb9872aee45ae0fe267a22aee24cdce5deab4e408919d91a8b53e75d95473b08c82ac4a5175aef72e5ce6d834c401c4f9413588702438e95775aebc970c95308912b3f2179764d1a6db3785f03df237b745f5356c5fb018801b42e74ef6b830258d46c290ce7f3d619d75e5110ce7602c32dc1541bb74e94aafea42f003110067513d74aecacae98cd1363871626668d3575bc70a8c05bafee08b5df24ccf30cc20cf10a3b9d9d01d88a2ec9dafb733c077fb90cfaa9848db658981188088b0208b3faf042e2b35918b6e8ab3f557b9eaa11f0726f6c4d672bc43c1f61be440f6989955bf71b92d45c9842666e9fa39f6bb7450355755ad2d05b6208b4ea540bf516d02a72db654b8ae882488d34c3772a63b121ad9881db19de51a6b11c0901874af81c08cd8de93d47cbcb3fc4faef512885293f23db9c1a78270b0f800f07a37f38297534629fb1e36eb31bb6457bfcee70aaae60d1f6c394e5848b12190c7598279433edd0e90eab560c442da49b8fa7c557a33db151b045ec941521f10fab4ad5a179924b52dae1fe703cd06eb83f1b4755f2f5436c8aa19117cd4419001b35c0b04653ad2308831326ade6b82bc05edd2b4ddf57001a3341fbec0f9307ed56a77740adc32cb20b7ff73a1ed20b0e11c860d857146de643ca1664a2b602edf49af1993946126b0523703f3d979f7ecb30a5f677b9a672118e07f1abdc08730940b4b8b2f7dd8851b3aa9a6c02894e90d3ddc1e4f20fe3be395144bdd00504ffc8dc8fcc6c3eed60d43eaea4dbf3f3614759b86457fad2ae4c3b0e031c0b54c44234dfd529514bbd76f952bf1512a6d84b5bab97a7f5390883de21aeda07583e5f699e8b105c3f1f499a86bf066509a0263be92c78d9794f3ad12e4b8e069924c7686278e35abe68e9e741749768f5ac5cff828cc74a13064c78c0556d0c76fee51b6bc5e7b8793c74d75e6bf10e86e2df13ea32ac61526f25e3ad85b80fe260f0e874edf0b7f62522209676512cb3f88bc31d17d1e9d981162da3651a047a8c006d50763acd5ff9ec0f83f34176055e198fa75442e9366999c5932c280f8ac50bef654479571ebe95c808f91410a61000508d1833b98eedcd29a96cda04ea28833917ac55f62be462c4a85a04033f6b8f1070fa746cf8299b0e75a1450fc20441c768a58a8297250b384d6bcaf2572d9a9b91c8b5cf3158c39b0fee800de9a77b7643fc79279a5dd6f25ca4490fe3706b8ea32fbfb4f98f0896a3e52f0140da4b5e7a9033b46a46ddce75d48f0c31a874c385e041647af11a47d659cd0518303b5047f6aa6fde079d652566fff924799293b768037db6850d64329c620805b0e826e4f171a61690cf92779b948b54f41a3943fc278ee0ab8479494f770086006ad414290d877ba9650d8782679246e0c53092c8b4120e1fe545206b0c0a73bcb1430e95a6ff86302047d5176196a24890d7d7c8e5da204ccd3554547b0abe99de271f9ce6b4256f733d744b282ab35ea86c6034c18ef395f7b9efeb290791dae06180a32c3b02c5218eae4ac14ce9c629a65fc44ff15afb2f50f30f6804649b4dbd8f7c746e09977f7ffb6b29b3a7e8b4aeec0cbc1f9db85a9124fd0809e3f3f05ea8400a4e4d58daf604b49cb51e901c8c7d9d0594fe20349ec633150b9dfa126d9f9335773b043a888f7438836e105cf31b5b35294403ebdab3f768046da009bdf6a9950e0735f88ac3d33bf1a8b38b109afa3830991e3e5a708ecc0b5dbe69c6be585527e96c50362a69404b81c18900aabd1512201207906036740129a050819c2932489c68976ebd621614b63797fec6dae88d1e80b5181fe66706ff0ec97149908b97947f5ccbd5298c2ed346176ab7adfd3788d277bb403b4d00caa8f3fbe5cffdb903d9ecceaf81e5db0b0151d6472b5e1c2e976ae5c89a040fffda036ed7228a7c40ccbb6918f270b227b1c821a73fca4edc92dc486d63ee004f1ef59f87f491ed3fb801d96b499bebd8a794f8c643e14ff0c0061486b58a0b100c33f4b94e26912b06a009bef9a6541b23311b865aaf1a719844e9f9761e010f77bf3f76c6c983f88d2e8d31d5af52899fc1c1d37cd879109781937b0a240ff7c791420a9efd7cdc75f2bd4bd78cdb73d81d3b08b0aee6228653bfc12cc908d72e70745e29e0f1c8e13067d1d02c2a680c83dbb8baf6a290fecb1e70bbea0bdaee2bcacf997408db98949ec0b36e29eaa190fce14a510ea2dbc6935daf0c01fdf17211ff4e74e7d0594f2d9ba5b99c29ca61a0a4b29c1fcb5035cebd171d0e43eb63200bfb73b7a2e01049a5f0935829c03e5333e158cfe40068f68ba4f80da446438ab8fc425f9a43844c085bb7fec6e5cd47f56b15d63ff26b3b79542206f24d3445cbc746207a9456d3626bef29bfcf8e67316fd2fdf65cc6864de8e000add13b62bbc801a87ff46a2a2773fdd883e463e1e34a73c6d7595f0a09b4ae06d96e3cf8fe1e4f3874c0df23324dea74668c5832403057eb4bbdc20c5cf81e073942ff1fb61d3479f0a1c0add58ca93edc7bda90e1198b5672802d9093c6a00905089c50c440f7dd9286bbdf24ef57a4714ac09cbabd7e6a19209b9311d6650ea87eafb06ae4a8125bb3c18c81a7519de252d0edb0651364a4499c19a2ce5602e6f101d0e1c5d4029e2cc94816fd3cc3c7cc19de8fb6795082d55e4b592d480f126b5ea3d12e0ca7b4856aff1707a19402580a79d5c241f59c0956c15c96a70fd3c54366537a68e5c689b934c1ba22955e65359ecd9f5812f9827f0507e7ae04fe6d660a566dca1d40b6e8c91adb94f3443320bd5394ecf9c41d4df136896d06f3068bd9ff11899e62cefbe32968127e19cb4bfe19e01d100c95f253315258084fcc6c02d5a9295b123712de898aac3db16be9ec5f4447ff6ba26558856cf80eb2b609cf6a37cb8ca0894e1c4e634e084113e536ad305134656bbdc57e0f74097101a8909d9933d8099b42a4a80ea1f1fd2dd65d1d78bdf67ea247a64f6cec05669d75f76c57afc346ddb6776ba53f41f65fe9a1f4dff9c8238710738121e006529e87db539acf9df114a9220e1e5f0e2ce25dc5ae4dff504ae3dd5aa7a81901a73e1e7b9d32c9c70d5b81efa57b6c259633b1ddc9ef98b9a00c7d341f5dab00fa30796922db48d929d731a713447927059093ca4f3bd9e2fc07f5fe4f873f0bbace7386c733fae904e0e2e528efa66c9e06ca5e3330fd980c41fd72bca38e06520d6f997f6f6bcaee71602b7a5ae2f35bb6078b6bf6d815179d32f723b8e40f4cbff32f1a32681aae630fcb454e9e0921921bcdac9b19dc814232040bf7ba0be80152bda1b084f32312ec0b33f0628101e5f2fde33ed1a47a170a233f988f0c5c4e7c069c66894d8ba3ed3cb9ec47d5d7010ff1c162484376d1595a09dc7e00d85454b69212351e18ba29afd9bd672867316b78b34192514ad87b858b2f5b03379979a57c3deb37fc8e437354956609ff384a5f9177f9f29899dc5cc3647403b4f3f92942633888f2ddfcdc636463a3dbe0a4bdf8b37682d4cfa2bc03a1af02b243b3ed11c7e56bcb8518d2af6dd97f52143b64d3e003ebd5da95c14bf5040d464ae3d976e7bbc143f87fd63928b1cdafcac2e65d5ab5a4e7c063dc576fab03 +generate_ring_signature 67011525aec41e62812368de7e56ad8ed15cd4845e00871483703bf9dcba31ab 819d1090586ddd1ec978a2f24412bab7d583208042f35c68d6ad5ec98752bba7 76 dd964f5695c3a008543c185f0a1b5fa461e9c22fea98d1af2a1e5d60cdaf6e01 d1b04957e8b634589c062eb1b743fa1cffb725a8e272703bc10f50d1f5a0642a 51dbe51747f44800638559a63971289e43845c9286aded8b96d55d982fa22637 81e0b148f3ce2bfb07971eb4acf845ea0b1cf1c08b27ace8b0714a78daab6097 9ba5b03511558e8edd7b3c0336d9086011c5adc81aaa03664841d8c62f1fd2f1 3ba3b659ae309970f3494eb0ec612a5cf70da1357bb5beecb2fe946a9913f6f1 882ea42f44c98b4e0d85af542d9de44ed889c3d65f9ed6d24c9eb751b640c12e ce38c8d666e28094548fdd3dd3a9e9706e0ab2c863d5e1c43fdbc0682bbc00e0 b0998d216a9ea0224668c114c8db2efa0a565838075f96126bfcaa22085b4745 c83836b02f86df9d9c8f753f52670cb80ec5b6b2720cb2b7fd7fbbfa2d838e5c a441967d23071f5a95a1f82f5e2ccecc2dd3d31bd1849d137c37e9415c01595f 0322b7bd2b2430e428c8f1273fcc36f45f97892b5572bc53163675abcd987753 b9e1f6ea22472fa3db4957c448172b1d58aa61f6048aaefb75d3c647743c87fb 348fea709d69b0752a6f382577e2149d83b39c5aeca6f0474d13b1d4627da825 50360c7ac3a214e93473db297a3e1211f08704061bc868bdf06976a95db2e6fd 8a5ed26f5ceb633b0e25b2a4410178c89ab38634fc36175aac9847fe666d6876 9b2690089def921d1d5cbf63ddd3f58b5d7fecb4ebb54b9b900032ef0d687084 4a22af02b3ee18ca29add712fb0890f5fe0bcd52848f1f8e38df6f38dd5c99b9 46b943bcc3d9183b473a4d522e4a1febbd713e23b7ec8959e5a4fccbaa9a4e58 ce8f7b05f9e53ca34b01d6a5bd63ee14991d7ba4e42a236a7248855832023dcd 851d02e432b19150a6f451c82eb384d730b0c3ce3c35a4505765785fc307ff36 7ede31e63912f8434bb525222bdc771cc1539e42cccf8953ed50b7bbd152f87a 989a4e49e2169c52b2e8ed72378944c7f4ca80cdfeede413096d6bc6bb481987 8d197160b9241e31906172e6e59aca990939999d13f247f5a5d63feb35218761 cf685e95fa014fbd3a7d23c9345b54c8918a82cf04519dcce9aba2064e703bf8 7b7bf2a667d37550e4e55a37e728ccc78cdb856082abaf80f30bf1b9c75805b7 b63b938e1ae69254a31620ed6db8a682a73755ed347b020e245209f4e5aa344c 8e1bc9db8e3ad41682859991867e2096c5e39067bc4157bdc5564c3391902df7 998290f1542e57fb84928d36aa67ceff21910f22bc8c3558a0ef10398fdfd3a0 f4aeaba0a6d239ea8a01d9cc7daf6ce0109d26aa92cb30f4456bc5eb2f3d6738 17833eee37eb03a9ae32973d56754808205ef421688f18fdce0caf0bc66e4f27 30b5341b038b23ae69a365a896790cd64aa19eb31864bb1638c08285909d764f de2d29581951dfc59b9c1d2a2a4ddf67919dbabf8a58651b2fa527c414a5cddd 0457828b3514870bd16433cba46f8738c173d41eb59ded65acc6c7dbaff5dff8 5c9fbb3ee625a4c846821074213e505f9c51d5358abaf3148a8d154cacc6d6f1 6c4485b140aaad6b9e994c7d2e5023d0940d85e19af437fd21f236ec3a4733af 88e354a64180b6869ca2192aff2b69fa306c43be47a4f110b85dcc5495f1c977 6a6a1de9b1c8cf8b9226fc675ddaf1a032205f897d0727829da84b8b89b22d51 78d1502e1cd1fe7c525ad2bb4f7c9f742d679f7908e1a2d4f7bde67ce95b8ecd b6ec7b175273d7aa77355401a3c36d43d003f6efbdc0a961564bad698e8264b0 b4cd90e8ea0018b56c069551b6c43f45d5e330633ef17161b85acd7418294d58 25281c2d7629d25ec3b0bfe45d621b5429c1588f2831611c642cc7dabcded2c6 6dfc0172238b46bc6b48fad9ffa9ca9e8dffd0667ef95611bdeaf212ecd6457f fd8836213387691d192f743f779da56e093a435d355426422afed7cd71d18cd3 d32a9bfe7024fd29b185a1308ac3fe06e21b181ac51ab2de211270a503ee93b5 d65eb25830cb331b5bf837c591ea4e4a350fe3cc01e795e0bed07d38b4f84991 c521b61b43eb63632c484c6955c669f84095d769f226524f2b324d4243398cef 3c282a717091456d676299112eee95a854a7a6a74e890c295071033c4086760e fdfa169265150e45aa61bb1450d7a1ff82a7f9f076bfd1ea5fcb3fc35b527299 139659524a57f89d2425bc842cb106d8c84c9dba73fcc03d61ef2b7e6ce2e002 dd28d9609d1d3e4f53c48cb86fb6af584275dc00ece5322836b89209bbbef2bb 139e90fd22032ea389767357866c7b2ee9a19d9637d5911e74556e89687e79ca f09a6690d27cd15743f95203885b89545c0d31d6f745a0c1830e9c2c613b94f4 b39a46aee128baa794634dfff8157c6d3971de45ea87b210a568b21d4cc2cd39 8f63b5d7703ce9dda77b5fe3a6569482d1a42a2a7e7aaa46e3f50a1bdd217e12 14f4e2881ee2111945d653a6c5ca978361671932c50890bb355f277be25d2a77 e29737b0d67fd4b843c907975e594978ece72d400eedeb0133e875cd65710c3b ea3cb93a2202ce93bce08902a70149ed46a56d01d646ba50c39f8fc122e19926 e60ded286f6ce5f73348f53cd99e4a3de0c5e60297be0d7f066fe19c08c13c16 77754e38b821d49702150ebf7f43fafb0889c05cdab297341578cfa18128133c 434012a532762ae1ebd6706d4740128e4f1c4b97488de8844115de50d7b9149f ce7d22e2a914b82c673b65b28172af3760540a84a45b62bde70bfd5833c33509 7af0361c2aed7982fef7782873dabd2d0ae6adc240dbaacbff15409d916a6a01 d3bebf533d2922ea2539d196acc3e160a8c776bd0305992eab11cee72b51576e 68bc2a53fe9f28b0fcac97ff57c0dd6469b3387741aab8bbe8779b72264f10bc 97741abb965b119d339269b289ad2b630e8152a2e8ab33bc9da9fd155a2453f2 a549ea88e7610bf3c7d92fb93bb175146bca920a8a566a6b8bbaca0c73fd31b3 fae4b40e6dfda8cd353c561aec3c9c8da0954d888b9c6adf883621b50c3c99b7 55e367e48d3ee954646ac39548bd79e9e56d216b1ffd07d7cefd5b99bf08ffa3 b5611452300c2159de3e38795fd01a3fc57dfaa516ade34e288e984ef3a0d604 3ac22c7a0b7ffc3611cbbb883cbb1fb6b4fecfbddf4d43c2c4fb484d4b103868 94d7a77a15bf45726606d1a5f4ebdb46609b7cf029cba1e51a629e1083f3d76d e7c85510fac98d7cdf17ff2c1103f65411fd196c37571a4d2002bedb00258cc3 023a807dfef8c1e036c0a55e6b1b09ba9aa0637caafee1fd1b283f48dcc7d88d 72638bdbc1acec76e018aa9671b04f82e1e0711f708f6424bd77f9c048060ab7 f19bab5af9575092387a5070f63e2c8bb72c14cc9f1ffd5944c2c24d82e880c5 b5a3a51d296fd23efced3457f883efcbdce7b2e7ac1514e5c51211be8c328604 24 920bf2310814694920ad659f87290b245340124e5a179ff7e8e1979383e5da069f7458dc3b9c47917d710b431d26c3884d37c188a9cb3f9638ba323fe8cbd70df3fff5bbeabb10ed8dea39176b3af732d56c2acffbe52226557b9d304b87c50d108cd063f135568fc32672dbd1986062ca90724e54adf7483e5206240df20f0588bb84c60859b8dbed44dacff238cda8b9ef478b6396e096485b8d52601395062578d736ba6ad91747d52e3f228f19b6eca84cd4c984190f5efcb3b3924ce60478df2e20845626432db7066dd9ebb07836ff742b8b349760e7392104231a8c010bb6e5db488566176e337aaabc15b4f9d963ec59f41f91567f7f5a2c5c625003a8ef2e0d4c0b471384d4ad6bb561e881992e74d317f8962cdfddd3afd1beb90d16264e0249ae0c139ad538be083aac8ccd8e6671f3972fc8942311692bf7ff06721a7e0c4ae333e78d366eedbd3378487a4f4f137ea6eb4f221f5f6720fbfe0f38f7bee76cbb6a997330bb4791e244fa6fb9a75731f90eb812c77dbbc15ffa04241cf3de420b6e8b2545998109ad3f35c5b5e50541116c609e6f1ec90a957001ffb794427dc5e4dc913a7043240c27a3350623bcae1f431f6f8c03ea41fd240aed5c0d77ef9b6785e866267d8b62599933da672ce47fcf2e9ef796b8a87fe50b1b8c4f1d30e4f6c103367e0a20a53e9ec0a1c1d90001f23fde8bac1363dd500e4d539461745554b8fa38a824bec0a9d356007afa7d7586fe0ad58547d6c65a0d8d0cde78240711c2154baabc630c5dfbedae5d1532d5fdfb78d2b7331791e6010c8497e941a0df1b9e6cabff3e2452717635f8354b6d07b92db6ed10ec286401eadd7a2f191e6061dce3f95c8490495d51b06f13cfb8a2086b0e3f89b8c8150d4bf14c576c1cc8cff43835f6154fe91b2df67686daad2201cb59669fede1ca0aa0d4f554cd58d7fc1ef49669dd20e24968ed73a92920482ae11df6c00eb6950437b49ca3c12db5d12bc2fce95900b99dfe45be731f9eb58c04b928d3b4a38809b709a3f34552904b14dae710ebc4f85274694ee77d7f721457904caf6a3c9308b4bd79276abeaf4ccc26c98173b96fd3fcbc905f787d2efbf9228ccb179c3306090e008d0d1b1e93a9414bf24c25d3311a7cae586d1f30c6bc5f4d2effca6a09941df962a5cb2a765663232e6720171174c0caba8772e8ee4b619e9025f106017e4fb3a6c32f7c902c41ddba24630f64b48ad42f57c778918b17eba9d2b853081f18f2c8d4e6b0fbe1d2ee08c1b6ceba4761a0e172ee2c940fdd6d0a7ea576060febc836130cd04f3e54dcbbfa430556e9fe547f59f394e565c96d5936dd760c02e61a15c1fed0d247bd33b57353977afd85f6322fac67292ba1a6b4f768e802a6f7b248d236f8abdf7ac55ee0a107eef738c31bdcd8d17e68520e151582bd0789baf0f9b6c37ac95c0dc75824b9b1d091fcf9e42b80c5fe8ec1c35d901ad506119e56966b63eee2e7ef053452bd66f5059ebf6aae5ce0eb0ecad8e1758989006f86ca3d8d1c85897a09eaf95f863c9200a6236305ec79ba2fe9434fbfeafd09fcb7d0fdfecd767a968099e1f755d085dbe36b8a247a009e654488438eb4f90ce71795b6e5dfc2e095ee0adf68338e8eafe6a7842bc8a9df00c945caf85676057d9c4cdab14a9776f1c4e765d30f637eb521710bc8462d568e6410e4eceb3608751877fbcb2b2bae5247f0094bfc1f675e160255919e21275ecbec348d21bf03dcb3d184870ea5b214abf0a59e8a81fe0971895b9fc8a9a4c1abfe7af2611308117f493eceb827cebfd2dac5a43fcf76db58ddd652ef7e6d9e224140726813033423db4470aa9c5f2a17744c1ae408eb84fc56e9988af5a3816c36446f041f00fc3d732dfb776c5ac88c82fd914641e608533461a6adeb90d22d6d20a3c9c90c4c2de90383313a37b5b743a93010dab0e2e51962bd8eb2619e8de8014cd2e00a98eda024ee8e7516b0bfaa4987662eb804672895cac25483960f010db61e5402cebd00a1c2a212d1077b33cf684d70804523349de60c69627d8d208b8ce0d20276d58c8ad7600b9d0ebc05a291fd9a7b697845e9a4db4123996414dd58ff130079941f0b3f6ea62ec2f7e6bd2bcf6d171a13965df1b78a63ffb9f7b4fb0778059b71640309b1651e5a72845b11b9c84c4083ffd322f7014b8ef4fed0449aeb099d604e154df80235438c6c8736476110f134db0a5345e1fffe57d70e1983000c9eb1915a7c15553f9f6f7322cd57334ac147ea26a32d3f8fe8b3eeb59df78600921d7f37df0a335bca22f6d6033036b3015df9b85c704a713959bf7b123ebb0b26ba2149f292eded1d35f269f1cc97f5ed39ad726d2ffcf39f01635ad365050e656ad4368c94afddef6625fb2af56682c1b75780445aed27cea5950c84b651062532822ccf263df7a37f57d4a92d398e3c69aeb2db4333a4ad8a31289e602c072f88c7deeb21faee4e2e5edc8d44e6726298971ed614f22a7d6e194ddf94380279678a5737bc6c66757c35ab471fdac89dbec0038a8ae1ffac103c922ac9d903ae97d68692aa51fa3b0a124b737949fe6b837446e14726fdb37aeb8d32d6700970c096ea9484c2e3faf3b588de149e1d777665d4e8a05e9769ad54f5be5d9c0532a8e3487de282779e4acdca23c5fc6d2815b04680a88ad77c7f107a60096e0d900094cc338ee81c6c9a31d3ebbf45e925ff3701297d0b2e6bba4a8a7b392306e588b702ecb907784bce3f0d3925499fec8ecabf6ab3a1835bb9c0e4665c850c399cec505ccee4f8d3e93a8885c481c4e49ae83dca2e8cbf4eb9c731717f740a0fd06f1923493c076647bf7e5f67aab18ecbd6e1c5aa7bfd11638f3e3f58c40da3e2503f4f2d6e42c6e59854f0908ffb1abb35f118ff05cd38efeb020236c90f06b4e18c247d2a4ee1aa5f56055957cf8150ab2c0dce289cb8a3e4e8574c14059dc9a121d7875d69c2cb42dbd7e39b514b699d8864310cabaccf365e3eb993048f84f60bcf2467035c7e093161e39db898470ae91c85da85db40ee85cad4d702ade9f6e8eb2afee1f69bafa935fc89fd79593da0203764b2e91846593d1aa6053fd0f087874fc1c3b810f28b23c2d16be52d9e84ea33fc49bb730731e283400a9fa73b07dd605bc5e3e630ead6c8367742050db8b73022953d4c36ea2f21aa00a2bfe7ae0f5beeda4dd96132a9a84aef3b6b37bebf67da239e9dcb420b014b078a692195f4fac817c5506ed9c57dca7c1299876a7fe6d6ef1a1e54df9a987e0ce083e5370a0e920a72138ae8d67ce08732389bbfcacf359264d6aa07345a47009c32ce14458e89d7a3dff9000ab78bdec0166e18537ecba2e6a452b4de451d062066fd747e158cc7e9250164e810c4d1ec397f63b68afe3a5877a81acae32603b0ab12da9e0242c2d641bcb3108bf6c99702d701cd371f6ec7361fe9d9cdeb0854967c62b2702f8d0012a9b3f4fd8b1ccd49112213d85f7160f59e17c5df080e8329dea18ef49c6f513e4c24b94e62fdd82f91373bad888326cc0f818a18e009c4fdf30e59278a2c2b0e8ffa1539aa8aa97305b52728d3f329f60b796c7c5f0fc8b49f9464658433767c31c544515d8a321a3eed8bbb16007f4c019fca010003b1e0219c70d54d3b38b2ee4778bb6b677dfa61bdc7148964d39eeb7fe42a3809d3b4ba22d276169ebcfaa3634592f6aa7affb18fdb31407ddfe25b7cd910c109c125f9a8be911fa2652fbb0160b1573bb50dc86700509984c57e3fa5b07bce08c8a5050a89325a27c77e746693ac5326d2d7e52a9483115aeb1097f9d3c15f04bbc1c22d855059f3ad26c336dddab464b54a6d551f07b6acc9f8d56b103f630a6bbe13fc8d0753efb2b0ae40beaa06b009df2a7f6ff59b3cfea5c7718989770cb0caa371d9bb42961e3cdb9df499f1b415e659c5c81da41cd115afea47474204044c863ad26602504db19ccb016a43a7e222626a16eb5a0ea55576dea2c2790fa6b2ca68b9383c8d1fd2162d3f15f1cfbd16fbb8171d2234d2ef54b7c81f5307bf8dd8c21e4e8f2cb0544dab8b8c38e01ea90e133a7b8bf3ecfedfd976517906c3b9a213ae7a1ff8f0727bdaae386f9f855b457ee7ab219904f644804d21100293cf4e30e650d8d4358afb5008f6b76294caf87d5f6f21b087fcab93bd4fc109ea50572841fec74ce6bb93d540aa9977f48b74b52d33cea53513033c3176e60866a8aa56d33a3ef01cdca4ec142667b7cf9f9bb91aeeca2db5d0942a80160e059ee319dab36e41269c2af1d95ee90c75a1905e274b9a46c78277fdb6d7fba30f13abb9f6a00204c22f1c478de8d537fbb7d7a419e992c9247654f13e64a11009bb2f2f58b705f7d3fcafd5f273d235ad22cd5a5f149e637624810f2a7d15c9065b65ebfcc0108bfb281377eaf0465a9d5f348283e06139e9c31692a88ebb6704867750e20b49f577fdf565ec146a8627deb36137ee3cd19af656c2fb6a8963080118048382a852fcf6d7b63883cf7324dde6e80cdeda1073582cd7f589498104521c71b8f12c6c9391b632a0288077ce2a17c039e517880f9ebf1a54fd58de0ce25c0baf8bdfc83b272864712bc50052317968ff8d0fd777eaeaa36d51cb1805690d394eb19b872274e73d95aa9e1f57d5af90afc8de74d10b4aa27b7e7520051bdfdb548d7b881831f49b9bba55b0f79030c99c53386d9303bba4323b5fda07fc5f61420c049abd3a4d8539276ef14c5b46022b531d4109ca4a2c972912e707925cf05655af9fa2efd347708d4fea96e5330a70a5add156e9908c657fb56909616570ff6d756e41c9c6470f19e7f6c1948f159442604ba5dffdedf32866850db1dedbf17f4ef852ff232c126c566de1e8e10fc0a4d544733135652e4b7be601d9eaf2a2b25146b92506074ecd91d3826606bf63cd80451ac851d95dd0c996076e8d9e8dc544e3ec75151926099169f9fef5fa3c962e534607fb810310c89f02c72e0f0997339ba959deec557397297cd4a17ada436145f150ad7ac638f0560f9c1aecf615c3315fcd0b8f5498f7f102bb207d19433ecaaece0c6531372edd0801885f9822ac577988db7674e4de1f2ba7966527f24b62c777f41e9be516210f10ad775cf62753d62ac12033d4a415f65d3fd56115ded54544deb37a9839ae00afa1f722391aca00dc92439b14a814d801de52c95262f51ebb82d07f6b6f1b075fcb52ee33f6cd9443ad27ec75cf685002e06c8cd1afbf0be2f7b35330fe5f0ba2cbee426af9a537498ff7f6bc8f28233e8fc3db1ffeb329a907eb650184a705dcefd32f6ff6dfead03dc1c8c7da9bb4eafa4cab334a22a11be3a47676f4280d83ca57ea87fb46f5882af55b56bcc5ee6025fa4760ed2e74e94e9e5ca56ed007828910f35ae0c35b4da2dc805f064501872c4e5a2906524bd33e79817b28f30e29d29f097340f2121e2f27c48fe55f3ed4d6e11411b744ec4aa8cf6ff0078809a4e92071957baa59ca1b7a8f80284ded272c12f2238921b61f591d6b5389e802c68ac968b598bedad618d5f47d692227607dff4d1a47ba61ff6ecfe74ddd200644e681f4c35a164d7f56070ae61ad807186ae9874f058c9fc5a2d8f53e0dc207b9fd3a106414a8a2daebecf36b5de9f057d20ed61143f52b1adde856d909350c1332ab7a5b5ebe7300d793ca298212347a538caa2aa174b11bd66844bdcfc90ad42c371483d832d49d2c17f2265628ff483e88bb416f2c0996638aeb365ff6051b56deae4a8ae8662d20b721cb81fd96b999d0fc594ef0a9bc05c1c98da3db0343966d2630a8a96480fa6d650061b636a576cda32602fb9d57b54ee9deaf4c036678e082851e7c88f14097a1bc2a87e0d624f4fc9cf7aaee648553fc1d94580e28479957dc158ba3de11bc0ef020600da1b6445658254a9affc3ae2e8987190df565fb436a2348da9e153e052d510c464e9adc72aa5beae76b65668c2f043807fe658432492c64c4eec2d55ccda78ea16a687ed971ef841a1dc6869bea84e603b3ab9cd290f41c3df348c0856b0b52feb35a9d2688cc7a3d3dc2f7f401275101d73828ac2a674b879e250a5248eb50ef970f8b76801113f2bff003ff5bd90904ba35c9c0f2928c3ec58adddc5e1c0ba50819ad468b0fc8bdcd76a53910568302c8eb13fd88367618391431bd63301441c55646ab6cc2eedc05e233de3a7ef40fc9f873b4d36d6a6839702f56af687f20d0139aa616052edf007edfbfbec0270febf061fd4cfca4e4cc5cedfdfa5ae3b0f60cd90219ba23b9963a5ae4bfba2d0a11e697d37f2ca997dc723c21551083690b0dadf3a8eb6cdfe80b675f57fa610f52994497ae1e7213332ec3a05049db58b825f9ac0df696e7567249dbe733e40e13b0bf7a574bc7f344a3bd75fb92acde599ef715373d7ea56ac16f74c00fe50c3bd54a9850ad2281a0de9629a8042feb256a0fb737ccf9e5e7223ad53c4e7f0f5e8ab2dc6720378ce8f8dcaee9d430462a813a6d73601d65425cf3c72ce83f087cb1100bc71a5e4863e44877b0db2eb83683468a5821fcf5623077eacacd2d000bbb5669ec44c80c47d44691f03ff5492430edd26ee9ed9c2610a235d36cd804e6aed960e4abd542c2393d149b4521ca2a11470dd8a660a8782f167e87d3d90846c330e39507ea7ad281acd0e0842c0615f953e476b6aba89ee157b904fa6105dd713307414d80018002cec2aaa20f70848e25c50cc1cbff7f890811b5397405d657b8f71ceac9da056102c67b1d506da30e8e703e367e4fd3f3b9866ae7a40aaa953272b2ab61d93e6573f2dbfa34a2c894a925ea42048a5f29d4c36531a20c +generate_ring_signature a204b66b297db8196c5e43fa172a9cf72d610c1a3e5c14a7e64b6e77a902a7cc 360b1eeeadccee686eabb2475524e968331807e166ea52bcbf4b806e056ee332 6 3a819e2a86cbb7c93d82eefb25ffc9d557aaba2e76ea0cd35ef017a0f9db4a74 fe4bd986465cd8d4684788e3abfeed9a49365cf9a40225a9b5f7a8f0d27d4992 ddb1f912f0733db94098ad017feee9b550c2afdae5e0bc4bdaab634dde7485e3 ea08a323f76486e70dcdc071e43b7458b070d5e15148e57268b4801fc0e9ecdf 16e7e2e0736b76d75013cf19835f4b3130b7761fa2f1243e513b1940deac38ed 9330ae8956224cc4732f9c94d92a3470737d4943400c6b837d8a5e4769e4c2bb 5f961793fa7fab2364e40a420192a778e214079b163c235d48364ed07808b50b 5 3846e8200f1e7908e143fb738ba457a39530bf3264969b56f21bcdc093863b01cd9ba4b97fae73c626bd1bebba775d9bd3410da5e7ff35c161d0433be4403600a9a0e1abdcf6d4d9fc2a54b7b7c2fbbd987efa816f3fa503b1783bec61acaa0d2f44a73bdb0ff675c6f9ebff8a541595e4dd1c3c8f9f77f6a46bb2e575e0b809bae12fd93ecf6ff452e58501bb76b902d2b437d528246e5e28fce833255cb30141c88bea17b67aa047171ec0723f2a6177f72e90442309100cd2daf3a8e91b060273fb31b6e0d6559c91c5210d6bf45058890c863b9a2e726982ce4a46b8b5036bf83e119430d3925bcdbb033651a31def9e0ef1b278b2134143d8d43257940cc70992704c00231bafa60a9ecdc893698c62653c6c5b147cdcdbcd23f7023407914c502e5111df0b196aa9c20ddca9512c3a66103597d1bb36b5c1abda9c9008b6a98728245b0efd0b1eb784384535a2759b56695b6843812b2fe1b455a6e00ed1469c943f21c99796518a88f02a33cbe2010473dde87ebc15909672ab761109 +generate_ring_signature ef79e077a9e0e9424ccd7c150414f789a14759ca964a0e13fc01fa5466c81e78 f946890532edc31375a4fc9e0201f16121acea77989bc1f44f7cc9e27d618cb5 7 7833f5dbe94cb5307fa63720d352767c4b1eeae31307e89dc2bab1b483eb07f7 a7c0d7cedf45355d0d2981b5f47390c3e452becb05e015647e39fc4221dc242f 883664c5618118cc6d2e9982d3e3b6d28916edb1db5db6db7bd5120dc2ed1f7c 1227bf14d8642d1ec64199c7b955557194a4bfd8962aac9236e38d25a8fb47f9 eb41c810fb20bed74105bc9cff98bff7526462572cf709a8b85ae65462769d60 55e0ae0f1a539622d44ef3ec8fa1565017e9f763c45f27eb5cbf6b926a414bfd 7af96fcfd866484934851fda064375a00816e21466ffa8fff2425997adb7c190 0b0c659145f52a67e664d6c06aa96452c552c1884cdd0f9e5b7819adcf173003 2 e58d912c22045e874c20d6671f6a23c1c76ae91f6972e98f5377da62bf305300f545d66894af9afe5339200927cc93abbb5ae09e4dee03850d141a8291d88c07527d5788bb4670a3016dcaaa1e31ea5cc8708d3ccc1bbd6620febfa220e8d509f9e134dc6a91ec51d62507dfe8e5dfa291c5234709e4177d4d1c13107035e50beb8f7811a5d5e633764883fa2cd8548b1ee44a7142ebe251d75795cbe285bc09637bb91aba76727808a890457ad1b027fceaf920076c6aa37c37f39f03502e02dfc2fc547a7d853068e2b43ee434b96097b42dbea15bcc3bfb85c25bc5160f0d53a226bd44f3c2f0aee264798307e579f1d8c9c16f51d353e7da0722066fd90dda88dff6a09b5bf860c998bef0063ae4c26112d434d72913a1ee89df7eb97606886116d9a213de523004a7e817cc52e61d978caa6b2cffcc43443ae190fac1010b6d73517196230af2c9bae8efaa0b440343edb7e02adbbb26a8065df63dd60e52d9f64fa4a107bfa05179fb551eb245f399dabb80816aca08266d0874f87b0a331275a71adb02c515eab7cdc96db6d83d5fff17151b1b7c87c210814ddd2002525d4eedba5640f0a3447ad10bbeaa22c75af875a6c9ed5b08f5a891138cdf07 +generate_ring_signature 78e0172eb16bc072a3b80ca90d93b44df26ffbc7dce563174f5865f7c16e5f43 7479b29e23de8cff8858d8ce192a594e7c4c3eddd8b898300b3994efab06c5a6 4 ca91beaf2146e06879051b0b1605935a0df5b5eb1ec9ea4f7f8d0c698e7c5bf3 567e5e858d217b840f50e66af8547d3bfd23c76290ee017b1c5ea07abe4ca354 c24d4eea3e2e636a9a24934ab8174a05043ad5612cef38bda772063f291d090e 7c51495c749ae14fd8ba542a7fc76aa77d4160b73f84e67811c1087eeca84d05 ae5a08468ae07c2ae86e46255bad3bec88fc27a5697b88a0a426c8ee41372102 3 0b33d2aac362a6a078e8ff0ee628810d5b1be449d06e0e0054c39a59aef01107ed271794920905eb30a376997a1cfa6671dc172e972e0e1d4ccbabc2d7994e0aa3aa7e65563f5546749671fc0d91789d75298312c45298cae158a30d5da4790da191ac1335ba4ceb5b4f649c093cd0c1929029bba3ea62613ac605578a400a04d9d88738e20a44c8518def60d06fbe6bc6d8820d724734dc7535de7bc95501045cfa791d55fcfafb5fb6796af6221f1ffeba3f0bfa2f867c8d773a15bbac6b05c24346e8d4e7fdab8e3bac1c164c3f57f7995236f0ea260b701c9706fc92570fc25726663bfed4034b30a1a3a5e3896c19304b68dd27658fabfcc11128766e06 +generate_ring_signature b2b17f8eddfd010da161593db87e352a00d0f915b3095657ce31bebcf0531d77 6d9765acd0f77a17564da4a5f7f07a0b918bc309f136baf6b452eff47b5278d4 7 0dec43ab1cb7836dd07b41edf9013b8f8565502509bd853b73a3fd7c2d8332d7 d007bdb3584697e80ca791a489452dfaaf96690715738e9d0a9772761622910b 3bb2e27057e5cb52140c29f86ec9072947931c4496da0bf88eadea327b45e13c 85fb98fe49d5489a55a67860a4dccda34a81b00bf5796565a5bca4258eae531f 3d1c25052eaeb43c950506c73a61cdc464c3718a502d77dab4bd23eb34093bbb 386800a322ab462444d1bdb3cd943642b4532cb4e6304270d56606daec593bec 898e99644703e2ab485bf42197618479f68c7767f1a81a754254194df7bb5541 8ccf33c9539ce3697c1ec001f6374d950dc0f5888fb2459f0bf5f1c12dd3fa09 5 f59233ff89bc30e2a8752163464db4817080711d54f58fcaf848fe2d0a5b380bb74bfc580a047bd6a46232a855907b41edaf556df8ce6de61102fb4db7813308e164104f51ccb9b8bb92a52c735e820ed703a709d19e77b04c5e3bba66d2a2030b305aea6ffe10438f6307efd7ff43ec44998bfb91a4f6c9dee1a826ba053707fdf442cbd7b24265544b42c3b242eb6580c3ce47ff8897648dbed8a187777b0b978a31f16975ccd0d0a6a44a6f6b63070da2dc155960dc5a906107101d3f8800794862402d395a0ca2de95d7e4d7a21ee69706e6ebf312e713d9b395080ea905b79c3845745d4c382826db160faf5d3d4bf87f8621a2f2983cd6f72961cdef0a26e216a99b499e351daf4b803db95c6bc72b57a54b17db5c4a28fdadb3d40d0d40780753d1f9e7fe7933ba267dc9399020e5c2e3c623d2a49a7a505135381804685ddbcfd7ad78cdd40d07c0f17c4ae06069d6209de00e9e163b8ec6c3b3a7039f3bd470670bc47deca9535c4a03c3c1a3a84ae08ba2593d565e94e61383b40630bbebbce8948af3aba9e2066e70b79afbcf5f3e036c5329ca4447c696e4af0c8cac596607a20a382beb88598e1c5544392b7eeb14734d1baff9c00e5d546a03 +generate_ring_signature dccd54de1c3418a71890e09bdaea34de43197a723146a148dfce50806f6351d6 beb57de477af6fff12b965fcaf9ff7229f670af5466c040902abbaf20ad26281 227 de6f898395f9d3acd113f747c1ec42b3c669642e954e078a84625e04f1203a86 50e0ec8c3ba80da72d39132072ab6db97355bb6d689df8d49323d6519f7afe76 33214ddb5c37f8ee1f71d77ef8f8389cc93ebe3e0750f0ca750067116ba6dfef 624036187cd190236a3af52cbd8ff1551af9ea618b7ad46c4039956372f2b37e 4d8a76e47fa3de3b4230220d19f147eb2ac1ea9cb0e815d5210310474bd722a2 9dbcb6dccb566b9b4ee3d9f4dd096ad64b86de505861a4b27007ed26bd7fa057 8afabd34bb7fd38de6ac26b47d135d6a0c05b890061de8e26e719e87d1140637 7d8f3290f75e81a26454b06a408ad4a5a025586eec86b8f1d4a20c006b0cfd5a a81cb1aa8dd7b2845b6f62cb21e37327eafab75c305977a487c7dd9cd9b72285 586bd7a57d35523981bb88c3a05c38f6ee6f7a4596a5c70d9bcbe72ff94d4230 75c17101e9fd6868e7881f5dede9b009aaf2e6945858c286ed121c3b912b9466 bcb29dbd41f103217282fa56c1c3e49a30bdc22c29f490a0c87f15979e90db94 10865f15bbda6df7774693537a26ffecf4f8ed4a9be5ea8256fc5128a5f5ab24 79c038c008b87d3425473433d22a6ee12f6a94912b2b473be3525df20304632a d2ea981b421db4622dac964447a5fa0f0b91253cd985fa651a0a94bb06a2e531 40360e8d38645b50466c06c3d2f366fd3b41ce9dfd854b4b6a6bbd91b9f2bbbb 85386e78ee0cfdb9c053759b698aee59487b080a3f9bb73636b6e9a36e80ff91 938980150ff4af1e402456d64d15e1a932f5b9ae0d03b7122c952a35456b6540 aa274c87ccf47e58a227424fbbee715aefb91a6a624217bd3ff62f4295200849 17ba30bae32dc6e3aff58c7b865f28ccfe8ad56d1779c3a40c9091dce280cd21 40182f7b319e2332eec3a96ae080935a8d06376a85d50e1dc1e54459b00538a4 18772a0589e44334bfdb0154f4ab5168e7cb33c2aa77a7603425da7c12d6479b efdce92d903112472849ff28ca2f7e312873d9325d28a378ed723d5fc2d0dde9 c874df6ef1ad049bc46597e1de0533c878070fda32bad8dd6139e98b04a44af6 09d67f7cc7632824c92065fea6024d80f1a5c9c2fc97d66150a5e77e9b069fb3 7bd68164f521e0c47994aa974693f8bd236564ad73add045a1f06d5d9a42da0a 7aab16af4335f06f3f24f38806b10c20ecd7f75e16905a97caae144393c880e2 096bfb98ae3213965acb6b92a60066b8f8ee495df93e5a2944de1646c369ac30 460ff31e4a5363b5aaca21aeedf944a713196ca387f438268f5817de68c22293 2ada78c71e899bf785980d8cd94f027e365684eae7c4292cc7da633e00edcb90 b84693047173cbc9166b0ad16a918d2dd5cd5b4dcb3175343e2a061f83623e23 7fcbf67da417193a649c5ca188c3c330d1615cf12e6854cb45a7899c24e08aca 1324e3c54e30e3276fad872fdcd7ba4f9ddc97cebe3f886658ac3767d60843d9 e555257d3f2fd95dc9555f8d89ef1dae813d0f6a9ab1e181f7d5ada73c8efd3a e52d868d66be401ee50f7eb5d74746beb94f677a4254eed91ab753f738e75da3 1d7355aa5837df46db3e8e8993e73c4a294d646d190a73ba40aa3ce841a83534 1aa2563077ff97e778155ceb8f8b4c98e45295e005fd9a5202e62ca462f995e2 f4e6929b34d3833940d5d30d2a7b0ccaa425360c42effb1940a10f49abb4b8d6 960da78fa1fdebf415e0baf03b63eff38b81ed99639186748188c348a3d5d8e5 ab52db15249b26d53920280b2b4686454ee211ef81bc330128e7c7365728d36d 72182c9dd9736c191efa97312318003e1550e18af5ae7b5f57b75a78d93462ae 8a822606f03874e68a1297636b7ddd645966b342ed11e826390a85d6fd13c3e8 77ccb0cb46f40123a379e014f46103cb21169b8f8dc376f34602a61ff3a164f6 c4a730fb9e281d183e142c9c6256183bba9f5871290071ead468d2b5479e2cf1 def7aa66dd688039565fa35a223d34c83aeefe5a6e702889eaee554028efd3e4 c42af9fb89579eb04d706df14cb7c47aeb844fa1c81cbec70512323ab608eecd 6f7331a1adfd3751d3edc5dfa02caed59d35813e646c474854241c7573beb1b6 91573e6964a2f69fba125d7897ee25fef6b0227a51a2deff1dc9205ed0778ee4 ad9bf2a571f586d8368024107021af31d5f5751999b25129f660f5b63cec9c97 0837392fd063e15910d2192b3c180228bfc7f12dd2428a81cfab8f91cdd44bb6 9c363f58e7440049d17164c4bb1f5a8a1ff444e1c26cc1ff97e43f4fd21f1b56 7e3e818d6a8d02896dcb8a7d53add542cf205c46108773495810bd8848d13e3f 4788b7a8e7b695a8ce58a3d61a7a3bd37d2c65cbcddf0f8402dfb8a5b1996394 fb352441f47b8f406cdb638ac64e7b6e8088eefdc9e757b9eacc7d3f72222a82 02ae8186d25a835ea25bdd979a85e2ed9681180b51fe5812ed67f431ae4c5f62 099abc67b8f2b1411f365db32e1973dcb2f6193e674f5ba9615cd3665a509a22 f140bb04214d43c84272acd28a16c49e2b6ac054ca2edb06542c2c2a764f6a80 cb2a56e6e72f1017eafb033c66e4899cc0da2b161c47265124ebaf0ce5c4fec2 838c1eb32105b74439cedb6bc89f68fd1e1359841994b58a4a7aaf5475c54323 e1c28c4e44817363335668ad1b2a38b2cf8ba1c0da151c1793d6cba07af2c5c7 3cf16340f818400832e2f4d3ac53242aa822b581835c701be359124fa74d51da bab07fc6e7065cfd2ef24b0d99bdd0a37b7c3a94890a0ba6e112ecefd9b59fcc 69ae4430e581881a5666177cfdfb2aa4d076e7c7fb1fc1df85f4a42631b652f8 5cb788dadfc2f0a8a71a53cdd95e973eb56ee8d8120caa38c9075b1e1e00620b d37ae42bacf5b181aff456091f7142680b056d866e1dc537b4c9c7c47d892068 5d8879fa016d9badd76b8e69f77bd6f02ad5b0ef79323306e8cdb3d3175fab59 b7e0d03bcc3356971ff4e5f2014a1ea9e2b8a3f87a170d25cd334ef440f1cca3 1cfcec5e2dfcb2243be40ae986f597f5e62efe2056fc0ae0f093a1ef8eac4c10 d45fa110c2c973d529ee68f8b00a7599c9e9821af0006cedc1ddd6ab3e37d9cb c003ff2afabb78406fdd999a1b48796082f4106fb616ba7afc27b475f2d78b79 6579d1ac0b18977a0b4a3184901abb13148a833b29445112fc0936e7710834e4 d2742bb8b5bd2fc348adb9121eb048b842ff428edb4cc0e628a51b235453c183 bf09a5bc58fa250176148cffd82244e614b0fadf94c23e53abf288facc92510e 6d37834e2372f26ad0ba8c510660ce2d74a495419065d78480332253bcb686e6 56d85f362ed1b9d5cf0cc9b22b58f704d7a1d18cd9799621c158e0cf6eac263f 15c8bd061f78d454c02a67973a18883014ea1a506740e3231a9b942b405099de 444188e9d62b697974066852422a091f9f9687895db8036c4baddf71bff17b63 652c85a355ae256e65bb47ff48c0f421633806c127bdbbc99b11131986a17476 d6d364039ad279968195fc81f2b2b1b8e315fdbf21618d05976eb434533ddbc5 082d940a66f319b4f2a26578dcb0277c9dd241d209f11059aeee396c767d6943 0f1ba3b82c1278d71c7ada842514e9e8a6b03d49925f279a55d84cecf4b8ea97 64369ba0557e73fdcb4fab986fdc58db1965fcb1b5fc2baaf7c86bea7e4cf0a8 c612977b21bde87630763327ba5fb9bda6312a03fcfed6aaf40b56e65c945789 8463e1860d65d4b64eac0d5367a5af5bcfa203c16dbfd083353917a61a74bc14 4ad00dd4cb6e7b62c74a36ce1ed7720b8c6453b570677012091f47ec7632759f d544467e93925c0fbe87a630d5a2471f3b67e79abfa1f8d3e89833e36d57c7cc 1127b1aa8e3d8bea0b4a9184720eae39e1879fa973d87966cd3fddc8901e0ed1 afae49672415a9cf1aa4f5d5a921e6ade299a517a5681c5888e1eafac35caefe 1bb1d53b9801aec3210277fb8af2e473a8d1026495c0900735d89f91adb54508 53c979d5d09084b0d729b5e740ef1ed51f99cc416b3096869eca4a287598cf60 42da4fc63c86c7a13bed979e30195d45e73d0b959865411465c2ccc30983a0e8 b0e4fa801af1c5c2834b31cf80831fb11d626020255f2b0bbc7c96465f523288 2af31e59dcb552b4eed7558885de0cffbb5cfa7217b423c590017588b488bac0 6acfb4632b22521dbe49fd353a239848f53e7265d43c89b59da8b0cc3f8303ff bd720e79385f6d2ca5f70fe141d02b50e21648cb3f959daa425de1e4392e5c75 0e3e941a4fb0c523195cc6a0dfdea6f52c566ab82c25483b9a10a9d11465589c 85fea41341b6d49d4823e309d4468d7cb655811177bb8a53e236c0747368b819 32593bb228b2f454aa091fe6b2e4716bfa29be3089306895310b1f2f57230c56 4ce8bbf39404f890dd296a2718624b8845eec23023437ad83dd3f90e3ebeb52d 434fc3946e64e1d07b97b77988cb48b8ae1133912dce2b9c1902bb9c83f63886 14354e897eba43297579a03878669d513e37ac42bee8e7c64e354ed39ba6ac3b 67c9bd298f98c083736bbd6511fed9fb238ba09634d630acf8c13fbf5d4139cc 3c77c3a7286acbf62237efbbae13eac56c4c0bd55f7fae2176550ae2f49b6b46 5149f6fe63a8042b6fe16c6e31b5b3da0245bb49ec249045152b1814e777efa0 e9c51ac176861a0c5e72275c34686f38e8fbcec99f81176a4f280e50c1d68de8 6d2de0ce0adf4eef46e9bc7a082ba9c45384c5e2766f7cd374f6e7ecf767afe4 7defec0b1eb6aad14ba9f886a3bf94df39f8f9beb4bf3dcfdd8e9f30c74b62fb f816bc04b50cb419e5342f8d9c1c45f61e8f7ee1021271cab7f60eb938619856 4e740f39bde4b3ca794f5218df1045ad330c992f198de22c7b1581e585f6e8bc 2a707dd3aa3ef5a0fb89987ef90884797e9b679941563b1b0f0fe482df2cb704 780363e8eaa858601ceb6eca2a95ff77caccf4a4ea15893dbfdf1d97f6196b5f 7954b47f519dd86d64fe572f5c83746dfc6cb7ffe388e257e5a27242705a615e cd730bc5c83ab9fd608d5e4d0ed2fb3a090e993da29c0341120f94940fb31a54 50eaa1323e56b6cb217c2728120f51cb7fa19c2dce41be26baef95c6f61cc276 811817719c3c0541dfeceff7756e80f7c868d56d6bd06feb1027a0c37b9957ef 1221701c18e4cb5e606260d2a85c99433c136549ffcbed18cf2e843ac4118695 972eada79c154e43a5ab8797202f266809d163f8a1496bf750667667d1f73659 812e63c9f4f8225eb94880915c2142e26163ef6d27d4a240b9bd3424ab0568ea 65e81550555313646f8ea5d2e1d998039c087825103a1cc9aec56489d6e7cb4e 4cadbcd7d0be79f6f32dfaa6a45a7479081bb824b5029c3efcfd51cd218791db 163486ea2d379505656ed6065a355a2b5a0bec2f1c56121047e2513708af0ba6 e312f142355698ac9e4705286e24ce259986cbf9c475be9908484ded0aba457d 5cf113e03b26cb54d5f672b6997a44b28af8421d258d1a51244abae07783dde4 7d19c56181925f1502281af675823ed9799978028ea3fde25e7cb7f2b2bf1af5 561035986edcd4fb791798407f9351b9d15ab7ddec1b1156211e4372f65c081f 9c2d63e65438f187250b110223fc1a41600f1516b75299d418698ebdb278972d 8db1c5443b19e83d124cfb93cb7659022e0ab39ca30fceb1cb7eebccc04c33e9 2a0beb68b0cc5fb35a2c8ad37619b7faec3a573380101bb1f3f8f930e22f17c5 95a8467c529e347700ead64b0f2b2520edba9317e1988a5e05b48d88de6b23c1 1f07b7c84d37af770b0d4dd3db8031fd8671db285db5fb292bb24b4cf03a66c6 03c26aa45c9344f2108ed8bceda99db51e2b3e476a9507305f4a264cfc22487a 7acc92bf21c416c9c2853cdbcb75912d17768c0558f45a4f9dd89f9950c04925 570dfcad88ccc890f989f6fa90445433718d1a33c2f6cb9d06f01e105641a0a0 0e703be79defbf776eacf28e3c02faafd451295528af4c3c5abb85af27a26c6c 8868637d4111edc6b9967b4817419116ff4e08342f94a1c919f88cfb66511db8 ad34c041687a0aac2d5b95645c3be9a092854f05ca306fc4df87b66fa20ac854 3c9c07cb12c72cdbd9c0a53a56dcf49286ccf1443bb46e389bc7f98e9c1e5a08 0a73a13db0b8a4cec8adb5f49b3ef1aff9116ea1b40907e7d7d53747914a6165 301e0e2771166d45a7db4739b33e67ded6f458140e03b77f7dc37dc10e5795c3 456ac73e71f674bd8436300971c616ba64522aad67f199c8600ddf5de5a5e6db cf529e39c63f83fdeaf309fe226248dd7c0d8ece4889249b1ee3f42802e84bb4 6c593de6e012579b5cc3f43dff7ac21e30433117f39e1dc5d17627dbcccfa8bb 2310887b528cdb93fbb62059afefb5dc8bb1ad99a6c26f470e8dafea9a21b75c c874e7cda400a4a7d347b9ccc3849b871083bbe16ba4a0961b5407d885d885e2 7d85f52aabe672cd6374d1f45ee2b8bb64bc9aa713c9d5b11164358dc0618043 ee63fc1d84e5ce7b6fea05733df25621d53e2f8b59d46cf6d3c206e0a1a4bbad 1945742ad66ed67a7e7be7619553eae5772137b123efc125cfa56771f73628c6 16578d8f6792c063ffefbb9fbda79cc2cf4a217a15d09fd6d9e8c6f5cb3edfb7 2fb93dbad3bdd8c52e66bdecb845ac12df250668987a46533e64ee1702586c11 0dc58bb319f98d536c5aba1b410dd591de2cfbbcb9626004e73f953989a09063 8146f9ce2ec99a5581481f5f9ccc32e31550925b16239c20adebe48c2a0dc8ed 6fbeeda5f57a6c3bec5266808f679ebe1786ad8556df24452d9465ba0163cbca 434b44876cfb72d61daf97f0bd8aa85d323af4320f52b880dc55a9c06c3c1463 c13ff3702adfcd8acc2a0b6e4e675b82f503549dcbf14a7c29f66393fad8093d fb76c1d8836c2a1f718106011e3dc5ad544b893726fa72b941d123ec6a20f785 2b7ecd72aea8643057327b542103b08e00b79ae6198592dde61cd62929089e83 e7cef581b5062606b00a2ae4310940b5f18d56d2742705a8bc3c7c54042c1694 d5e7cf1bfe62c0c6abb750e4b22fdc9b11355e4b34e884c94571163d21a81d6f ce69ce14cb9153cacea90c7fe3e8460653d27c61d239718a937f1fca35f99d17 b2aceb0c7bb9a7f865b3f01d06eac0cb1d3ef32a4e2145a49473135641276c0e e0dd5fac25d7e724aa18512adf237fc463676a5d1c9ce42c6a8b2c5cda013506 f7e58fe431e81b6fb8d5db68f69a7e4596c76227c0291f73f9754640085c7675 b63f41f757fe3f6eb32d2b7ecbcc86dd06d3ed3c68f22e41db42550913169a45 3a143a89e143e860c93e71c71f42a04ab75dac68baf92da4fefa6c6b173523c0 7417ca2eef7e735a3ab12e4a0e3b3c2439c71dfad03014200b8192206acc78f1 3e0136f18d38405b67567e2ea5165d8782a1ced39df60f06980d3d2ad9d24f3b 4a3e58971a4e4f94ce12c054249d1fdf9dc371bc7b5279f78197800c2a51bb46 269cfdf63c238721dba6bba1b839759bb0fe9e83d609a2901d141786090c3f94 95eb48427cb6c4f471c2ea568ed9df6633862be02cb2ca025c73b2d624b056af 421e453d565a46650ef8227bace09af7c068466da857357cd9b2873ad1ad09d6 822692d9465ea2005914901f30869c138f9a6351ee06b318ea9a26d443d97d39 fa23a03fc5b0b6e1150a54fe2bdfc78e925f4a66443f7e4a918a7c44cdcd05b4 c40394856093b0f40c439a94508d1fc3cdd5d6f1747d7a876c3250deb24261f8 0082526a179e1f85f2c456b87355d540144ae21c53cadf9783c5a00cc145fa73 91265fa8f7e3ccf55f695c17a0004ac429cfe485463c2dca00a116b84883ed3c 81dc3104800931b6a4c5cece8d3e2e8629722bdcc210bda4cbcbf9871a43edc8 aeeefded266eb59949423a42d3ebc1af07873e1e954696bf44daf3a75e7dd5cb 230cb94219fe81dec527b602ceb442d2fd047f5f69f94e5aa5229e18ce6eeb72 e495df1e8aa30144401c58dd35d99e206efef5800b42ae2c855ba2933ed1641f a3aea8193f840fd72d17b18a89b937a4892fd267a117d45db566c334763275f5 21df42a8e0ebc06d0be965488fa6bc6fd56e9a8071e7d7066e9c0be8a18715e9 199b10cff8216a7ee22ceb37cf3c03d219233e65a473f26fef00137d8c52be50 69375507f886578c829b830f5964a71243a21a88b9aea1555bb34b136776ae64 53d95e59a7cec2ea6ba975536d48a76fb560f61e988a7d5e27b2564c4e429450 9840ea72bdb5e374a4b515fdbaa737f50be2d4115e4192d0973a652c10d553d1 ad8d699290c6ea3864c6dbfe1fc2df282a7033a115ac050af16cadb47f1814fd b9b84b4655239182a7c635dfda8444a090d56ad9f36b39e49c9cc04c9b903d5b f5c7569fa43ac9d2639d582f90467c15ff8edac68da3368307be710019cc73af 52b6e9e81e004a4d350d87b19d86dcd759429a09954105b4b884508d0fd2b022 194d7708a9ae555cb73ddcd4b5e41ae54e297c19b03525eadf28030b16c47388 d158efb1f35f7d182a24ae62a543440c932d48b056d62a6bca8da8fa388fa251 6aecb71bb4a7ffcd6c4d15bccca66e1f2bc16d4a6c3d75990c3dbd30800d0282 5ecab698f65743d8bcd619885f36e574f28ec1f665847dd87ed66c7bf94bb8f9 5824367f4a299d2f99dba1bed610b421f6486ca776d3b1b3e8ac8e9c7567b4a1 9b1a0a3bd2e544a7cdec5b13861e77c8103386e57710ee6f31ee7ac777d3e089 eb852b565d9a9a4b1175a30bef0ed763c6d2d0369dee85bc28671c7d50dedc79 845b3fa7475922b76431644b6be2f0cd48c997ee497b0e3ad7ea8c3d1061e1da 1e9b85af0b4eaf78c85ff0089c6888b8cfcdd54f876d7111a3df6f96bcfcc678 d30f8e5b89b088b901c943e89f093b40838b706757139eff1b47c848edb286f9 aafba65a11d917cd3b38bc357cea2ea44cd018cffa6fcb9045e8b35501f93ed0 931de2cdda585f0ff0f7f81e747c1c5b20ae49a930a943871c8581dbfa5467e3 6f7ec2a05580bea3790eda02264a371e19ea10a05818b2d1f9df342945276a76 f5a08ffbb5b9317ba9281c7c77f3f6b91b02a17b8bcd51b7e0b2a84bfa0399de 4737d5fea3b9e3567f1c74a6527c2be489002195ec96c75fa7a412fa020c4b90 8693790f118dd039b89e51cfa8b7887c98bf8145dba4a981adabf3e354e45d77 b8a660ce9846d73f1ebb888faea6345ddc5c2124c9eecbab120d6ed427c59f2a 4b1ebf45036321b68ec8b0543203d21bf9a639027fe58261a98b44c0af19bfbe 24b6b654c8f53fbe508e669b3804cb9cd55056540a9b7df24ce403888b3999d3 fd9930b13d52c5d70440304d0e1e5b54a4b6b1ca2a435677c5f118f07e2b36ee 717eaf9e1090b2f1894e4678602a3cb5c0f374858af6b0d4fc723b396353ea1d de9c034a546b2c6cd88154fc9eb766f30b986dac38c3cfba22b16ddcdfe9dc7d 93c221d3be2d20f4ac343b9e67beb6dbe181acfa2e03e80b536b74bddd754f38 4e3424bb19b878f1845783d33117f315a972e4cb8a4f5f39218ad3eb081e07d8 4f254a776c0f3bd7779c82c22c33390aa41909c89ea9bdcad91dafc6e4003050 2050b427b2a2ec9dfba6c86014777901852742c5783c8e89d114938ab728ee18 9dbfb19eb52ca6e2ae0ee44e5df65bcc5e7439af40389244cb3b54fd9866aa26 d8b8df21ed6caea91e075382344d7e63de02addc86beb7d76be24ef2155e8353 22662c59ef7015896560440498100035ee0763c5a6de0b263dca397058523c34 0b8d591914e20c40bb5fa99de44ffbaa993b8fb08938e3f5033c07f9e4bfe881 55f9e2ad6e2699c81c525daa2008c323a0978a3b85dc99d591dda69f0aacba72 d228415156119c0157cccc74483b149d443a4ce32971461239627b7aa02a4b89 e8c7e96a2ffea407a803eadcf5ddc6254795c329a6f7332fe8220bd26cfc83f0 6e14ae550d01199ae9b978ace5b382fde0807d8671827a75f3951b18827527d3 259bd7e461198a3ca17d4336ed9f6214cfd10404d3a6f6a9a0bbd87c26bd9076 25e4b2650205fe5dd3c4b1ec5677389b00a4912b5810622b505d72125200a42c b38caff798124570938f7d2e92aa8b0a37229020355e0525069cb2c2ea16eebd f6d09aef0f9a2820f555475e44521817a2044a407da71731aac20fc4b6f2c26e 1fd4bd9f88327912a6c6ccf04009e9bd3cf0fe43fd654e1fe96382ff0ca4c509 35 4afdd9ce9b2cf891a521ca6030b377d52f1790a5b30f3bc06af012ec26df9108e67a76dc1e6c748a35fd06f8ba5947a4c4ee894896345c0bbd7dbf1b632bdf019bdc4d3741a904daceab5df2f650fb419c46483ef9b6fa02640c5c80aef11f0c1b36ff945cd67dc78da67e90e7003d95e1dd2f343a1a302ce7d91d1da7c26600d8e0bd5f66eb9fdf11ae5871908809afa33a4231a34a3ca603247cd623ce7d0ef8b62e82d48c5d258ee253f58dfc01b999e6de56666f6f9ec2356da0c9abbf0356a404246b99adff9e66f32e2e95bfef63a46e4e6caaa9fa7f1827beaa639209ee3c6b5ccd0f8df3d584f0519430ec449f7b669d34844b2a1746a688b040e60af90238674d8d2ec47900141a6bf470c823cdb36b717530b52df807b7fa01540279022b502bc50534603d38f8dad001d7378a960152454d9f0336e8df9cf38e087662ec9b90f4a43cb141ed49be79fbdbeae47791067673b01ab1cdf587886c0ac9d70a206f65754dea18586d384a9bca6f30db22b04f31d63bd919b6980fb006e106718f2f8b7d8f9f2c431b5d985cb36047000f8e99da44ce6ea50cceb46305097c447bd959ce3280c3f6bb4e455c172936d8b8bfca3c7baca17c63f8dcc20f55809e73a9b5e229019baaf9289ba419045981b6194a7b810c7c64698256f40bf4f5f64268a13847ab2aab05c587bb0a065ed3b2c2c98944a7736aeebf4ec60c9de4c7ef0aa74044abbd78800b848de18a5f33f48f74890895d985ba470d2c0a95921d22e5e88d7a746c84f0096c0543b2f400eddfe4b06a04b634b25f2c43060e4b00151e0862dfbd0c882a5baf3875e3dc574d849853992fca4e8edba64a0bf0c0913a5c2caefa9f1f372b543e2fad581659bf2b86d04c32b115b57042e601209fc0dd5ef64c69ba67d72ba389bf4dd478102e99957a1f8a518d27951343068473675f372ba861b895a3d102ed8f1c0c1daf15951b78d154228391f9427900aedf7311a79fa594acd29311df3327a4bd120f13cf5ffeff1633f98558f5090be19d0aed620e35626443a1d0fa9a652016852742d2ba5c8b9844b1a766fecc078e2af3a65c49bce92f6fbf377fb44d824ab944699d9d26929b76b8d42648e8039d42b6d87c1a57beb98ca6dbee45ea89876e6231774f9ea1e4e9820b4cfd5c0b68486b6d0818df67145d0568b5d8c87117fad3b7f94a3a3db2a6802a574ad804c8dde57905880fdc9938901acaac91fd867d2337fa278896e962a0e74bcac60746cbf9ad449a07ace8ba6df78305ee8e1e887733849b09d2df7c4feaba75920a7d82db7d14e3452c0ab08ed08a4d42d80aeaa56f41d656592966b6d6b542890efbd63209bb69a53561e6e28176c405a699d3dd358dd6e00228e824d5167ea406a9db3738fc77cfb68c8a57392186cf3ab6bba4de90493c4186f3f4cd06552d098393de955a26a0b1e600d2357c1bfdaa7e4d4002172f6f6695bfcd0aa752860579cb787b102c6531a07aae7bc8e379f3171be8076452f6948a47142216267405c14339ddbec39d2263c423a5c562e2d89daf434d3a8159300c14bbc3941ab00e4c21e550e23e9c5e6925e48e83eb1a9aca0026ed9f45ab4c03bad98cb110d40d11cc7e57610316979b40c94a91cbf83f6200ba942a8806ba71e5b82726d3ce04d68229f83db82468dc6ef41f40c50e4609d06dc817b3e26a751e6362994e6b0cc599c5b863f4d7ce46af7dd8168335207e08e6aaa0f364fa8140851bb2227e0e5cc3f3f2a3655040d32ca9e318f93b86955f67ca2a0acdba5a7ffb4580a66b08896cb3776b658aff6a7b2f1ebb25ab20d39d87a5f1d2a1ee7ac8b7dfb66848076ce1807db53c6b6e58740603094bcaf45a4f551baf0a2762069ea1aff3186c0b18dcef53704f18d4a278335f80b0b4a34c07a936456f4771d1c5a095a6f72b0fba9c665f52b552fbb94b064d161b4f1e173c9b99ad9beaac3017b73ef1d3590d0130db21fa48cd48cc974b963a2f1a0f143072747412921591ad23f0c3e672046f00914f388d1c0ace9a08982c0a85d7c2c855e846825632f329f627bf031b093db8f97e2cf66318fd1029c7b6f110697a2def0135084edee2e755bb8a38cf0115f7ed905d5e878d20e2bebd237cc6e1d8700675f6a745a9e031fa4826e2f7052d41aa8697b224afd4efc0afa3289f74f86cee9319cfe8a5b068c16bc96f740579d4a87ea6e20cc4894b491d85f3f6bd5f53adb07773fce2f6ce54392b5a44082b22fc424f2c0275604dfc881523347b8bbce18d304d943bbf47eaab8fbcd703bb6b3d653e40d81997058aa9c62317c42e788bbeb61b523fc774fe8bf675010778fa38987d06b237d0674b9f5d0ac8ca47fd9ad7c9f7f9a66b96a6e55ae3bc0ebbf96660a817de1f1448d2affa7a8459909055d5d66a7cb41b5ab4242693470875227f888012926f6843cfb89adef037fe8db4ba1d1e17953e8e9e6aca391606d08a3b43186112e735d00801b77e3a8f5c330e29db8dddcea26cb28409aca509faf1f4550c5d8ebf707cfcb40b4c8873e50e5b69c9bb214c100336474589d80d27a8b503f6c916f1e795b7193d2118c7a4157f8d198be9cf967eebea6a57a205ee133b0ccf95531313a693c2bd64995913a6625f6b83a91bdd7e83788d6b010dadaba59ed93c4902a23babd4f0246e87010618ffee1aa720d5587d9648c8150a7563e51f340de57f6218ac5e719dba15be03df5a144d52a6fae34b6d7f9c7d0c9b3c67f28b7fce791b2364827e41caca673afd3951b83a5eeced6e013d1e47094253c574029339e7461941ea2683c325e42f8a6a63b7998da597fb81532771056ac413f98c97ad74f263605a3228e081f0e93075ef8f09dd8fe863c964c92f0512face777d8d42478d812f33befb43aaffa3b03e801ebb7234afce6664bd230b96a7809bf6ad1dfd5462d5931ea61fb8e55daf4cd999e1231c2b4e068012c50ad1ff32c4b54efd4c281ee54a950b2bcc1cafa46bce884bdaabf277c8ef589606ae1c919a4185400c197f71a67c1c3b6a26a8baaff87a3c4329d5da13a6be2908e892daaaa07a9d785fde0992d089a2cbeede5e10fb1afabeaceb8c94ae3a3a08a3dfae81709bed09b3598086a4a0e1e586df8bf03e23a0a376a8c6db5246a30ff62e46a5a5f27b3f865437a4253b3efe4255fab3eb7d8fc7a1518479ffdd5f016deb73da5dd44c141cd1a74ce3df0e3fc01ea8a11bbe3afc708493affe98c505c14dd5ec3d784dd5bbb65489a926d794401af4464a81fdd41c10f5f01ccd7b0d45417765c89cafbfe6723745444159dbbca31e6ebb0fc637b34485a207f5f90bfbfca520d4bad2b4ff3b91bc29f0458c2c3b8612fe29ed6d3c0244c2d6c22f0fdb13e5088ccbc1603a9b1e2480a5a8b19b01898f8695ef57bb7205d8a9287a0783c121f221bb615036e9a333aed788d4ef208df46b1cac1fbf3273787e055c0cfcb259073ba9bb09310520808821f9280948ca4e2072662fabee3f062eae940ca0b0fe6d4e2db78fc1e29e18d9fbeb12d6d7dc4adfb72e90488d0e2c77478901af88cba160a94b41cfde1ccc7f22ab3332801628e6882830a6e42641ea02d90d0310238561fba2423b43ef75f003606fad9afdbf2a040831269f925715a0c3034027cc831aaa5cf28686c36b3b89389ebea5e0ee36457211756aa3dc9bb51c0603aac2a84f1ebc9e676c94e17dd0750ec5ec22fe87a5a30737d04899a627f00c74c7837892028f8091b4b7be653f9cc6ffaed035f7a253e1f21258c3e2698607541c66f1a6c42778e9d6b2b45e53329d4bea3cf04bc8b83ca250f148fe0b10034723e0e38513938a9f115a576d385a6dcc3251aba17237e57b5d214a5b19de04ce4af3634d5824bd124a48a727f318404a87b9dc9cdef7f6eba710e46e636d0763a5a5b5e482a3f46efe1c2cf410f5a77e0e7f3f8bf0fd361f3e7f956da3300800d47e4fcccb3e7d60cd4c9c224acd7942e693e7d7b807025e8d2f8dbe402c0db64e5713ce1aba30c0eeb1eb99cf680b4d6139c26abddcdeb94f796298038b0d0ba52832204a46bfa1f1721bcad8101b914e712bbc1ed58087024e514fe0e307a4416935c00a4b811ad4f78a77f915e25145108c1d683ec8ea85624a1fd94e070ef4a830e2f26f3cfff0bb139da3f4aeb31887d6dcfe7ed5ee337fb6fdb0b70b005a282bd0e9deefca82dee1c1bdafe99f3030a15860a5a33e2c043e3504c10da2cf11a1307a92754b5b807907913a35a9a3b2c3ff565c5183b38cedd4e5900acce4a01df53611bbfa8c9676e11ba13b1b5b4f44121916f87265fed991350503b9f090c3afa665baaa3b7669eb18403bd23a43d4bf247c3d9a3533eae3f3160dfa97bdf61160000b2f35291aa6c6847fa26d3071ebec8793c66dfe90999ba209b68b502eed1db8f075aba8ea7a02040253a5303709f41edaa33bde016882600c2791e4a42f8ae4a55ceba608ef1d0a9e75557ae8a66a6722d668fe29ba229800b5726c5dc041a76b2771a3d4781df098635c8271d5d0c5574a66ce24e56bd9040e321abb4fea1269df0333724ce8efd47b15e9cb8d80d2083c6768a96f4dd50a397fdc04ccf538839e255a8b38fe386f11bbe0d715ea61b8d27112623183dc0b4f958c0dee540104d3b61ea658c9fb05d371a03ba802dea9dde14b2dfb771b0cae45da2764f7f6ded818ee6d0a1032500f327f48335c3928f7fff6af469206011450dcf4f203ee43a6cc8b41d331383eadb09946cbf5956f79c8861ca610610599e858febd45996caad51720ac8d66e075ea0bd4821de154053337fdc4c3940e2e6acfaed305400f196820eeabbf17876ff2e1b9070a4f5c011f138bb0e797065ea529739b3ebcc2fb1a5563f62844562850ca9f994c235fc088c74ea9fe7d04200cddd1d260614e60de29323a0984bbf44522f14f9b032d8c8ebd1f9b06ed05f3d5958ef7fc1da6e2d01fc0f402e4be55b48d3e2448e5841ae2789f719ca004bc072814cafae46928669642c7b75add8d184f88c5ec2465a6c587a556bc110598033b7b6a9454754abda131ac45dcf195d300bd805244790035c644a77cfa039b26a7b87be7eaa3193b660cad41695bc6f7039aa2395dce1ad76434d25f6e010136a8b0d1190d59a2299bdf3955480344716df405d804affbd32b88db283b006187ec38fcb5a16f4aad00293a833e68eaf73a4ecf5afa60ffc507f709b0e309fa9c51aa73e4cc70f927ecc5675e839176ae7befcf86fe179378b4d1898ae400d20ca7c6c2c3e8967369e52e2ae41397572775ec2583386f3a1c2a3d30896e0d07ef5f31b4a04bfd61f08f76e02a9b04bc75a849155e8bdc9a3249308ddfc6007007ce7120d228bab4190ae42a121c69ffba72f122cea31eaf9068a1d355b1054a6295dec77ffbd824afe5590c8172fa12fc6ae6950785a101b6ea40418a2506b76b737479e09af0f99186d14de0f4513fc4b80457b7d80f3f7b95ed9d3b1e071499d968b1945b0f91bdb872acefe0be0f984be457ad3592ed052e958480ac03870ee50c593513b6296ac361fab232d53381d251ce8ec4090266fbe5adb8d7034302647da066e492641819592424f619a38abc7d12b2493823334cd3ef612a0bd8625da2d21a37dcecebf1d7a57c05a6e8bfb385708533b8084505257d888d02fe0f7fe52ffd1877cc357433a603d29ebe9c6b559b4ead2a3fa7bd7985d21a025bc4adac4aac357cd8b97546de4b24bc4a21c01e0467f61115ac3a948b73260ff8ece22080def2552d2f95aa2deff214ccfa02baa34dd42169260827215c320499ff5d6fe90b58c61cf0cb52c36e182f842976b1a361c12e61b859609b0a2b03c55a425106f31e4e344b1ca3e2464ad705964e89dfac1533b0c3f48e829d7e004dd12af36dcacb2945c1963aefba5d71888aeb65cc18926800f7a02485eed60b724cdb27b3ab18217911f2d347fdc35785e32c640425a8a41b30224dde40650fcab569576fad67a98a70bdc5a9911191bf731d855ada7d2ad19ea0b9588c800ba18e098bd0631f13e54a03d4b1c28210db97b607b74ed02c6f312e736169af0f3fa72678de72d720f86a198d378b6873581c186993cff37292bd66b2a2db75011b0dfdfa67ebd77e4c4e1387742e1157c5f9d0293066ebc5327fd010f9e2cf0859c111085eab993e42c8adbc819e19f54a315534d6c682900db08ba33a09b402bd0c0a3697346b315521c1cb49e8c8c551c57fad7f54fa61195708d6ed1ddb04a967d8e2c35a9addb5b0616650bf3256d11dc75a5c8d38559cbb8cdb37e6810b046b997049a2964da6fa2cdb506e79afff50230443f601257c90fd826dd1b4035b0b1652d980dd8d066b247dfe379d5c47c2b6779b5a8e9f9753f863287f7f0fe3892d67f4cc4aee6835b9d818b118d09fdfe827f5998ac704c1982a76fe9a0bc9369c4839f9fa92a10e26345cf3bdb893693f60cfe9e303f926b22e3726960043f71c42a900bd067598ecc4067f520f8c877186efb67b43dbf03690d14ba5072c618b198635473135e3964fe55638e5306eb8b89447a2bf33b7b5ded2dbe701ac43de1037c40455cd2bd0f4b92fced20560b4544e4623de1471b9b129323a077cefd2bda2156870c4d3d22a2b82952af40593d364a49e48beed8ffdcad2330d6ae95f9bf156b0931ff1bcef9d536ca619f31131ffbec74636d1158cc3f6d7059bf30901a2c96cd87e1a2c77267afebc5b75e4ed22255deb1b005a3cc1d2630ec6bedbe4467bb2ca4b73457f504dba5ca4bbd928b80798e770d77cd74831ba051f3e147741f0174c68029e0d59e03bdb3fd108a2acf8a96e4f422d537f03f2039d374c851216c433dc0a6091e68aef707eb76951e4772012fb6776e96366330c1399d6470c69d1b04dd51c2320fad7e804587e794a0fe56042bf729553e287088f226e05ce230b1214b3e605cdbc8c1060ffc130168d1775e7972a645b20ac039ba823b5de8eff9af4399ece8112f06a8b5c871918d5977f2879ade0d225d408bf66042a6b1e6cc847c9f7865228d3737a19238a20bac1dfff6e0ca9752d960c25a8301a0ed70ac6abdb61e1f987081d5e9b834626da0c176fa66681e2164f079f0a5e5a0f4a205b9603efbfaecbdb16ad34ca64df2bab9c754e087585cea304d159bf4ec959d6b56984fc59217fc42e97315ce059cab3623bf45e16a8d9920c751718d330337787a9ffc9556c5e2792383d915ed6fa92603fa49c4fcedb1606337db1f56172fad5496b4109c201ac8af858f75452bc4f56086b9b369ed6910887c62abd33b2501c81e0b263e1398a0ca031f10f91db4416987bc552470eaa0a69ffe32f38f7469c44b4e6cf669a25d9deae27a32d8af292d724b026508377012a24f8fd21ebe2183cb662b1aaaba8d79c1f35e4f016764dcd2a61e9a3e7c80bb9c337b526ec23aea6fc68fe9e80c6b055f331191fd317adb3d83cbbddb07002ee1bedb5829d89ac4651ac3ce3318cb794d5205fa1acb3ddc6033c60dd5c020cbcac5fb9315cc2b4424414f7a373a4da5843a7b438abec1d348d1a62198d240667cd78d6f67b8e28212db0adfd4ac29d544effed0bb6288cd009b257d6f76e0e5f10c57ba00f9148bdcd1616adc7204020124890bdf01652617d19f9676ed70c630094ebe0aec7c9ae2210eb6a03c169d04b693313758ece011070c08ec6fa08c50ff04f6929174d8f6bd76d52fdd9a19956347530318e5aadbfbecd8f6e430fe9bdf03019c9bda49b8b45dd6a593a137a6bb42fa12d00491e4653300a59370362dea01ec38a685b58406cce0500540595d79351adecad060bed02b404ad840628f43bd0a8f9b0428bad5eec1e392ef2a3de056c7677fe450d5d7bc5d7800d06b32ebd84b79176f5166ec633869702bf23fddcfa7538d69222a19fb4791d350c38239903d414792102828c8903232b4c2cbfec9d1d53c0fe33d8882be347a4095b7ae9a982082797c98b030be27bff6b4623a37d6b3396a4f56ac545e74a5501edbacd9cf5c98846a47c6af9e81fe9f622b260d3aac0b4342293df67a068750712712966dcad17f47bcad7360c90c06eb81cc61d1b16d840556c24fcdfbfed0c54274a05458a995d58bb0fa421964a54b3031fe9887f13c7f80a8418993caf0b06a3779dbb07846d617c645732809d3a286a0320a643b56a65ccce0e7788240793119a50d2d47d4113b72256a90d5feebd424dbcc58783b7ae029b533df9500ae60de13ec48d30d6681c94596223a3e6f31f67bfa1ee86e054006ebcdc813b012ccfa23615bef1196441766158b7b6e76b4789f29533ef80af3efeb200fa460348f4548ac1c448348f26878acf8f49e896a0aaa75e44c021eda842d35918d50b7e98049b4c636cf5c6c2f14361e071fd5cf082e0eb6f957cd02496390a212d06cfa640e1de08c15b308aa946e1119800d00d7978177212dc7520ac183e02970870aa22b16e29eb828361cdb104973a1942c0fa525cc955712c39dd56c3dcf10ce647ca40722adb7c18e1dc458d87724144b89f56a61036a610e54d7ad7eab506b2d0de75d48ee90a5af4d566b784b3d4fce7314e95564553ed6c4bb4b579a8050398b909d7330b33d28aac3ea097cc4ca4789f7401515afbc0d65896f4761601bc7037b40e746cd17aeee78fda1edc72011788403b33bdf5c1fdc7f01358c40a0656a64ae6f9e7e123f3feed0eb4dd9d972d258404a1dc8bbc05cef2090c3304db27144e75153f789feefa8f1115b020233906c8160bc0458e7d347ec404440f9f541aacbef1b9f640e60e1ed4e473242e47988fb941d3bcd863d160bbfea800f59cf1fe64d6e5d959c7aee993129887e520a0dd4b4f9b1598b97afd8e7b8c0dcf475f46b8c826f9c2aa6a643fd44d488f13d10208c16d7d881d5297f758870f033e0316414853e507699f407073af47fc524af1d2ecee69f20e3e2da789e80fd5cd632247aeb256c22d8b8a2e4d73503063d0012ce4eb967e1e4d08e9e4be0c886d77a351a4c3f82c22ed761eafeb87dbb09da667f6b01ce23d29892615c604fb25e9360bb86b7719638abba883b449ef20809bbd01d6ded1581c3fb855740a17ed02d464bef0aa12df75897f5d1cdac3f1535440435bf0b5a8824bfe6f0208976bca6f22edfebdd7cbcd1d94faecd549da31dee042de099bb57028b2b3e1039d0e072c7da8b7cf3463ea9caef7b0bce770d3f6a85cbfe153f58dc1fdaf8a09e5f2df380e2d27f5278a0d5a2b00410a563b1e6368d88b85ac38ee3411997d0134e538d976bb4b8aed41a527ce1b2926f101891b102800ca34aba4a3f623df0e92d6f0214b3f7bd4667e766e3e83a54efecb2e9b77c99db254b054f904b19d0a478649d93baaa36af9016c6e9a176877e9a7f9e04773a9f2c747beb190aa3c0cda49713d1e24ff4a6f200681f0c9c11c4463e37d2f366b15a54ce815341ea5073a12f34cdc89cfcca90f727d478b0d67d4dd755199b9493f16fdc9a50c373d07e08df1bd2d52a65524561b817828940eeec49aeaf86ee22f81271aa6640ff1032c342935c51b37f7f2bffd6686a050fe16241ee8fc836b174f08ab15131a2909cac1988f337211067071fbd0eab76de06eca02b7d7e77b65aeb641b0bf1a8f03523681161d5777cf307b6a3170fbc548f9cfdcd668b17d02fac72e4f5fca3f066a442d561e9439bc4f96af69467b27ca6de1e9d2c365548e99d7eb2f4087ca0ca14ed79531f112a033aefa64fa4ebf8248349be7c8b00a6d8414d28c681b1e082272b75753907c43d9c08d5d8d25b00778e2c0fdd4893c2d85770d09593cac05d50b61abc9c1e73bbc2abc8892975f70b2f7aa25d80e7e1962623021ce594408a0e02f3fd8e0c5dcf3b3deb43fa79062bb026aef3756bbdc2782eec9fdd97b050886c8fdef73601729ac93a00eade0026ea6e8bb73b651f66ca3877c812d600e790b691d20c2c30d695709bec05239afd15d5fb220279365714ba75c5f874b03b1e3ffe33ec7a2c170c14b635234c2d976f50db33c9d411027e4401c545e0b0b5135cf1ae832d4b62587ace504ff21b039a7ce70941633a7f5140674dcca3b0ae9c071d6960e92259fdd7a90cf28758b9305e6f1e4fb5c464ffbc81acd32b50603cf348028ec4961f8a0730478963f1145889fa28e9ba87c4cdc7dbf2310eb0a9d94ecb72a228a87706da1e4c9b3564f69172a39663219c68e4c7daa8ad4200beae6d421635f5f24274f3460968c091c6fd05efcf2130fe3e775bb14b40c6d0bdc77f81728b5982cf7b46c5951d2688168532e377ee2466e6295a96c35158206c3dd86e7d55e3c3760a9a9eb42a0aba66435463705f9fb747a4c944f7950d007e5347501b03293147a73bc7cc312427359d8edfca34557b70b4dc1895d80a40f753bd8c42b0d38cc3515c05f50ef12bc5d34cab0cf6820669abf85fe66871c07e344df3f74ac05bbb7270e51cdc2768725c5d50f458e7421c37de6bb9f2cbe00e66e46139a1f40742ad48310f09e98601ab636000bb2ba047df9f232e29ff7095d68e33256e5ff239ec680132d6d9e915bd563b3a0612edb829976f620e9ff07e8667295b7ab8a1ca7f21ac429c6742490492c53f4440920855cfbd132df500ac27676d1febdba85a616b17e5b2bd0d6a110b0d258c63f663f0922c907f66d052d2825b622ecd08a3be6a421db918bbc8a394e5b3fa21b1e9d6fa4ef6b4d5c0105ad1278836f10ee948fbd4cc7f33c76fe457a187b11fad96f07a5c3b4c61601c062a9484c258d37abd2da09bfa1f74aadd31c850138b69ca03ee2ac73362205eeae22f7de0fb04782cb2a27aea68a31f31b9639af77ed8cc7c6e7ff1fcfb307a9800c1c10dc42efbb2098df81096ff82b4ddd6fbc1c4f4972a252bb920a930965df63a2f6c3a554b571ff23ed60dd5d430f59c60761ee92883b27ba5cd3e60074227bcb9b44997e043addfab96cb438f4feacab64aa12e06684e6546bd8e608032ab771fc1b7433fa0fc42ccf837081151ba0346322c587c7df4aee726e620667d1b509d5b093a3c0f70b130d4be2ff196a16df768446c97bf9f7d6e0db4306921052ee334b04c29e790dabc94674bbf9b8192eb5586ff4aa0c94ee66121d078daa862be6e228085e30ff12e4d17b50b57448c0540d74b0da051a241c7b3005dd471b450c67fd80b59178d227ae0ec3cbadf7fed1f99e017c1098ff5542680bf6e54fcc2ab4e1cd20e0cffe22f12152ecff46aacd1482ea10f56eadc6363909e256d2beff3aa78337008ec9723abb40ce6a8dbe17d43d0ce8d5f062c999600176edc662c93a3ead2501295b827039bc5aa621ad1ecdbcbe0d132602cb9c2505482336c253cde5639ce3fe67a1d660a4aa080a74ac876d5497c448841434cd08d2fba2c6831f923b1634a09e599da97a7606d10baf902346a5c3b92ddca0d3078606e3986d24d5cd5c1bd1c7a3f9d44325f081c388ef8d2f76dfcc8339fbda00779486cc3f8f7becbdecffbb5a918d218c30ba32c2a822f63a8536bbb9fda2085dedb86ef1f3567b6d0b2555f9504fabbf5f889fb1a517109a71dc961f75de06d8519d69375d802b9cacd2dece92e6b837668b52ec32d41e093e184345b8f70f3f556d19653b10b681a67ec93c54af13f6e7608a17e688e65cb1300510baf805f64f49b9571a855318ae1ca8fb05824ca7f1492db8f6d835033184ecb185e804b17f4e55db4439eb2fa94095b7ae4228f7402452b3efd473afbe2460ea269a035df5ed266089f216b9d7e16acd0bc1155d92d3b174deaec3b3dd1f3e2b968b0a06db920045ac4fcd7bb6ef27f7f45ec8c1366aefa7170c3d404b293d6339c0054ca8a6ac94c9d75eb68ac095f9c7688cc3efd08573d0f72317f3e32f0e3a13028ae25edbfd3103c13f0d1b03e326566cf25266276a1b64a379aa775354d0190cdfd8f2c203ec0b1298f0fa835405589d55d4b4f08294e6324a5ebe1dc1ff6808a93822859fd379872b174f8eb2c20c2bd7c307c81aa37e1dcd5cd2a0d4de7b03e0c033161cd653ff9e52b7c13c9fd951af4642eb4ead0587e2ae20e37c6b640d2255b7abbb9ed77314dfa8dfd97792537c0e1e7e3ee70a0329666e49b88d8104baa00dc0e4b257d6183f5b063a25e9d305cd46d4440583d6e2bb3ca35c30c00faa5a78f62acc5c0d6386b32bcfcc1161321052403669bbc16d3bdc414385b60f394a2301b76e5c3f3f389ebfdb2e0af058a7090be2b7c93620dbe8106284900eecddceb90e821771977e29dbc75bdf1c9a4b16334ad85ba19b8c0ec932cf79007c43dbf91e4eb769e51361066b79f94de6c83ecb3f7bec8ccbabfbc2d266a30380f1eb10559aa693631a4233239f7b08ce11e3009e5b7caef714638755b5450374644432183abf91c2b05599e16d4ab248ea9e19115b810ad54c602ffc69e50b644caadb02d241a7f50e8073a0eeb8e784e12273a8d90d5d4f421c13a3d5f3017ee68883f64a6742444116f1c49cc5adb8cf8a3e294d81731d9278e3f881b406a266bf1cf9c14e5e48a6abe135cbba1174051be1d95bf6dbcc417ee654b8a50bc76968e04211bbca98e7289d418f09667afce3064622bb5733f6801d9e93d20a0e75d6dd6df5a612231bb868ea9ca70dff09bcd8327b81eef9077dfeb07fcd0b661cc09d8c28c94566888fa4e91a38ef9d809d8cbf7c2a96d42b81c8f32a5804ededd5fb230b3466dc5e1f56197a4c567e03192f1e461f52f18d525c80144e0c14c1372017135d52450c930ec01b4c0ed2075e24792f16f93c6a76139b9342026eb0b3106262dd92c7b55a90f9d3e884872126c69caf14a8d8014cc7f26bb20d3804aeeafa75268c4fa9ed9f5839b156cf54c79fec2f0d31fd9a117c780e1f0503562a24958a26822d8e338ad6b2d5ee88760cce0912cc83ec9d7bf150ff55069f968b62d6da1561c19154e3f0c37f157ca7c7e454248cf3aaa6ff74fa465108c27b0d60c09ba38e9373b014e91b39f1ac35276c7f136adeb73b43300371ee0d0f6a86726f2bd80e55766a4e2f9e1f9f2c25a81412d489b77708fccbfa7bfe030530f72653645f666d82c9674cf61aed175801a191fc1601a82ea0009044ba0dd14580081885e3ff96775735c7df0ed9b96cd3d8eb2daddd8d3f528143070605962084b919317a1dcfe053cfffa5293e76b4fdb8b08bfc2c0d9da67312a3530b6ed45d5445fcfe67ebae6da3e1c2a58de06e6255205a27fdd130e476c1aaf6026007952e4d2646c8521f761ad029083f2cadc38af0b4a9b1dac5c3fd36f7bd0a85067ae4b23b9ff85741ef3b53a729f492c0c8bec7f9049b4be7bf1949649f048eb45f80cf2a0ff4f28f5b0db8087e8fa748be298919f685655bdaf89f6b620664343951c0184d3aa11a2b7845a47da4ffeaf75514464466cb32eb921cd2680a0585f660a28d21da67eaad8bab25fa084f8a2d9f362a057bc4426077a261040b8cc31147dc38d10fa1e242b1d66aef3a370223944a615732861012ab156d5304d60ef10cfbffec1b6ab4aebce6f25dc127c4c03c07febf1d2939b675e3ac98078f40d37fc4611904476af631b7a2b77a356b4c45986a4e4ab9bb82c676115a008492833044d89a4630ef78b87b74233b1274c76ad59a9871e7e335ca1a0df90bf55ed0c80b84f6a8a43e645e1bca9ec5e22bbe0d8f065a3110ecc1fda7534f0cb0b3a408477e93d04402a9f19f64701114664ae871f6483d734bfb3f7074540d168de1e3aeb0f0d660592cdb24dac3731280b60372459cabcf3bfb1fed2bf2047173d1fede108ec244eb1216c75e8b0d82f856f3a05fae239533c70c657177014be0201fdef4c4376d35f7cea673d61c643430085b24c667d5d1e8142dc57802e16b847045db744b472f0d409f9710b571111d8d6062464f867e02a50271090bb7a80dbddaee992d3b09c9ef54facfc24680c9fcb3c32e3fe30f7fc8e4aa8b0f12fda6b1faade1deb60924fea0055d24a3a689698e794ab8e4cf32a42d5ccf04b84d4867e5a9555365fae23e70ec38a183cb74550c78cd7996f209c04f10bd0acb6eb3a27fbd90bb38e53a10b9eb2cd0c68a2b2a5ddb3df6c86696f52c9f0d0abe1be3f67a3fa92fb85b52cda4bbe874ad1882b9ef9223e2c1d333f65c92210ff63671d453db964ffba0f8239c02131e2910bb4c6a6874a45a32e3dbc558ed01b741228be039c14eca86427205fc5003c9a75bd8c908634f9dc5eacf3c98c60dd79a5dae7ec16b432cfe720158cdce0aace0b874aac03a75fcbc9d4f91cfae0ee84888b230ebf0dcede39fd808e8ff736059094da5ebea2dad746cf199b3550644b9d1daff0da7a1255ee669c44ebebd115788cab8d9baf09454323a507e0200dc75eb17af2659cb89f17faeaebfdf155363f5a0a4feb7400047715c9cd68f09cd659db80d8f77f40408efa50dc621eeb53c38e39fa7ed54d0033ead2101a4023b510d8c2b0ea7867cc566b2df81d226f690b179cd526439284a8261a8902f04b91dd0303410fe78cf091d87db65454ec11d9a4ecd78cbf51643838e3afaf405b620010e2a0c03711af3a53e463a1b4ceec7fea2ec8dce0c9f3a09ec2e9d1d02a5f42f16f729be1a25a2353917ea1c599d8b64b99827ce87cee56a7d364f0c08a65d7ce5ab9984cd84c401ae3cdadbca83f6624e6a4db2bb730a42a21075cc03205dcc9708260173b322c19eb7d53dd446314b0c5c333e990327d7293495e20003326be5a6cbba34555404a114883d26741d4318cc6859ffd88e1abdeb27200a470858b413ee882dbdb29fe9a6d974f3375e2ca484c32509f5c017d3c4058c0fb8fed6c5e209bb0e55935874dcaf290eebfa465347c1bdfd656898f67e8c240ff5484de93d094a7b10b1987845befebded340f90480f677c2d7ec1dc2bac3c0f2f8d1cedd8483edd8dc20039fb02261d375b788da6e4f88bd6bf95e1d4b25d08937515e629c155c74ae6242c02daaa1815dc4911ef0e160a13384897aab2360dd53cd21fed9dfe414cc82e279a47198bc0798e003260ee451bf0623c6975160af198bc474af3b7f97c606d96708e262f3bd9b6d124981893914d7a6ce59d0d0bf520f8850a1356c842e5b773b7c4fcc9968ebf4b8cbc35d7e49b61a18d783f024c252f5e5578762d67d90417560ffe5d3d43173a78394c4147b2afa5f803170b2b3f5c1fbe7906b0d383d5137d85fdae947ceba628632cd4a4b716c3587aca0a2d9ebf03b2fa4c9e1e62352648f5c2c5e88150f5d8e7a22c7fb10cdaa0e804048909cd988eaf170faa9cc9c81eea6779163ef4507027673c3c87f876392f270d1876fe7cf44709165611c7814a7d97aac477af52d92760be03fb9bb5a016b00d8a124238fcac4f782696a92bed8d97a35ee341930f5eb96bb83b441dcd968007a49231469ca502e0e39eb68792bab8200f5eefb2e9bc0e6540703ffac6459d02f7ec9e749e1ddcc134ea7afbd346e3109d7b746bac3c63ed47efdb9e74f61f0ef10ba3bafba5b9a53fbdd84c25ca5f1539a1baa68cd44529693d474c4a451101dd575853c8e7d149d6f350d834cfa4da694428955debd45008ed28e10d04f100250725a5940f56b6b66767f0118baa1a54642d610e9e4f58bb18a07e7ca5da08571a351763d9fa4065f9a36021fdff0d1042c405ef847c7303cdf092269a680cdc5c72437b75d71306fb0d7db99f6635d5f384e502901a9c23300c6cc8dae1037a85ec96a12538bdce0263eb4791822c4200984c28be68a82b0be79d02976d0cce3cc2e688a60b9d83caf08661445b07256d790d6982817f051b97f4a593f3093d2d135cf780f7d0ec4d58ecf201b9843d7281cfd0c6e203eaa0120b473fec0ec85328b811e67170eba7ec33388c1ecaf5885165b4b181690587f695e1001e0da014d2622285a91ac52181365aa1b4f4307dd6f1fe4f46cfa1b88bd883b79d02f65653716e90fe24ec9158b21d8a381f11ac073040771fbe18bf5b39f676db0198f7c3555e9192cafd175e351caebd8c96bf66a6164c97b6e2cbf2a8ceeb89068924b3907220711c688f94c001b59169a060634c4cb77e7dab09f7b1a768ac0c29599dd7c455b6748e2b72a7e96da20840ab54d2cc0d20c8ba9b2aa5a6d7490cc4f194c0dbf6f4018e087b6875b8a66215c52499ff22d10c71520ae9f4f9fb0f3223fc16d58d04d653b3ec085af7277f28b7f08e1472613788a1d43bffc2370909d3a1f88dce132ee2df2a839339cfc502a7201d89635ba243832722e5150f07749204cb85bc5cca30f4e177b578c9621660d17966fdd6386f4dea45e825a20f9b59b8b4de70bd3b7860b09e8167bf88377c79fadcdb40563b91ae3f7da6a709bf4922a7ac03682d7552e9d7396be0a5c728e57757acc5ed9923036facc6ed0645eeb7f2c6a07b602907a2ee4da70f292a219404a0997b7729a52b3f2f546604e2a3dc4b12f6ad6e2e9915120847c1c91d46a0e13b1715a92c7bfa48c7d8480add8b7431c884048c7e530bd1144b7b2626680f70a57a9c2ba5c73545c9f89f0173f8c3698228167fd686aea41c27e0ace3b81cbe69aa74bff26a324d6604d800c3ee9433b0979ead3696319102e3024c83bdbd35a5f34264becab480b4aac50414e2ef4368df08a5d1d3f7e895cf2830c1c09e8ff5ea527b966c75bb07a39a0b85dfee234520e39d237446f28ae1c7b2110aa098050fa3cb073b8ef9a215a609234129d7e9ce505b79b10db50ac955c6e8f757adf170f99a660a0f5b6317770c584da3a871196a929f2585bab2462508e40fbb87e0a7147c8b9b2f9a5c36b40e22adc78a4f782e75f160fecfaf866f34a92627965e0854ae75e1ed5e243fd1054f74cd38cb66bf0149ed7f98542bf8dd569e0daeeb9c46bdcef6d6fad12dc00fabb4cdf0515fead723efb704b3a4794aec05fab2cf19a4e80a0c93aba79854013de5a1bce843641c5b30184d2ca391f0a2230c62bd004f53ba2e4011566df1029400057a21803d83301aa02e9ac6381c5255bc0e05c0b44ab2dd94923aaf50046b06f0ab3471b0f8b235c5b51b90f4e84cb3ac101bd33fc3f21590a8fb70d40df846bef3b97de9960c9b32a9fb021c271652f2d0ffe5d0ac271ff1130123910adcca86dd60c54f2ea150842a7ad876e28ebeb7854785ac25ac83d1612d95200fa58ddadc4403459bf5c8de88bda35de29b8eccf8b6a2011317c70cd546a2b90ee6bfd5e96ea87c7845032ca252afa6629ce46b5782ad901c1f8fd7499fde80093c98a5098dcf97686e5ea7000ccd3894f4ff67ef913713c6333faa28e172a00aa7488aab58a6e00bb629cf5218308404f95d3a3a63d439e143eb5b255c57b30261b34d39ac6c7049c6aec81131a410d641244b6dc897ab5e40668a574315170ac818616056412516a73b94cf9f5a845c48b38be6775c0b76f27aeab53f45a10e2c546a8aa21d9641c6de187a60a987f39d5705a409eba6b5cef24a70a47322004b4a2555987f48a485255fb2f236b6f2ac959f057b032c8769473d0437d4c00bfa14c7037b331295954a883098122fc61a206d8c7641c15676175e6518e2c6000ebdb435c702cd60a4eaebabd9c28c600111bfe89aebb50a94eff36de842c60216b8b01fa94b5e50072f5cfe6d89956f7bbee1965c375edf9bee628c9f8d1702df04215204ef6894d814bfa0bd88b7157c26522b3788aff2dd58181bcf98440a6fb883ed81261dea166316e41193940f5930cb4b003d473a66b5e4c52102ef06438e5f7dd895c693a7c69b3acf0d4bd0690819ec7938a82df134e0b95da1210649e136ac162f85682c715ecc1bb947118d73aa13f217cffb58b80ad12dd8ee0760196ae2479c60932f404f2ed1579688c723d7bbd89055a8c6f6d75b370f7201adbbce7de6077976939268a9f64357e6f4718e1a09525401ae5602a8be9f2d078f3bbdb519481bcec7567811eec10a99fa214ddc339b13b5ff092911cb9188079f9c488877ea024fde8cb4fa37fa85dbb51003d7120c6cba58a17371f2d9200ae2667a2d3f8f5cbd84f45c42132606fb1746bcac8c0cc87ca7e94d8dca7eea040b984e9b6f135e316fc91a92042025183eda4acbebb37344f1f88e7ac60cde0ea24d93581b05d60d788d2c1febf98a6644e5d73607a9467d8c64f98a8adf2b02505e1ffc6e946b639855222642218d7eed129ac7a3b97bc18bbeb8824339330b01147768e63e31834ef9e0e892a5dac8fc67fbfbb8c9588c78675deccbca0300a5e0cdb626032e763179942404b9ef462fa24db12d6fbec69e30b71913ccb905a416304aee4e78ba5220f6a75f9a25fead1d2ba414822775de23e81d590dda0ea48e81668c4a2a937eb3919ec320ddad7e0745908515bad872b1967507a29400646f55505de0fb9d6b25e5f940166c858d88ccb324470683ea7e86bf28247805826fa9322eedd64e24dae1539267e425eb720cf3c28b82253bf73cd26bcf820c20c85c2f04a2412164830dc3c3f34bed213f2024a7d4d081dd9e070bc60d2500b2c193d0310a28bfadb3c726c54a0c196fe466c5e2c8ced323a322c488745e0ded55711281a776ac22a3bc7c5cef72fdb8bd81f6b51695a2cf53832fcd1e4600909691417e737b8f8994853277ef4b5035845c3a236293daf42608213992090c110cf0352e38fbd28bde85b126e3229beec14be1418531a4a17ae7e6eb0c110d4920593cf0aad3c73475c522e8504fa64bd84082572d11dc33bc51f7877d94083ede2ae1e97872ab4efb93aa448cf67d6bbc8dfa918465ddd93174369e82910da68d261379b8c536247121899a3db0c4399be8282d0c2114b72aa8e3eeaa4208a75a6d45d091b7a0f73471080c10599dc9f0948f1d485168cd5bf862650f3f06d3af954e97692ce6ef56031e87e3afab01677769865b1543756554e1b5c88e0eb5a6d40f4e5b1a0dd3093cc20f4d9e4259238d3de00cf9f13fd5c32a702b590fe3e0d624540afe8aab9c71cddfa39c40217fad437432654c75d409f0243352032f6747b5eb02ff0871895f86048955c2a895e3226397003b17acdf4e6fcee10b6ba7a94de7600c5451b8b964911d82de6175b90ae72ce80d043fcfdcdeb6bb04e94e9ecf96317298990c34db47c7cd0e72fd480fdfaa6bb25bc2d1c2866fcc035937e40270c9c8730c7c053ffe96e02567b64b496198c6b1bec8f36dd8800e0cba0e8a8001f274105017c73fa0c634b556431f24e3e72b6c82df468aa14c7903bf9c0a7568cc59b6f5196af8059fac305516b7d8ca5a636e5de7a04d30dfbf0fabf8b0b9baca85c3672f674e94c3b1d1cf2de1ff1b5ad03d5105aed3e3940e0ba2855887982bb0012ffef9bcc1b788143b9300d36e3155548d4d5483141ba40cb24957de0db51ee90786ae295112d8f7a14201b30aa0fa5c1ea3c00b45a254055d55b880fe032c4b5f0a7066573260b4b2db33ff8ae15b325d6ad85a82629d0f1ebf913a3bd8ef76332ef71a5ea7e60e4a7e1d2434e243ef70ff00c27f4a610f3aafe8a597d791d503bcae33120755248d622a5fcc9c815646ea105e0342e10e2d0f4b54539a67203ad6a65963083f6daa7b3f9d80edf5dd910ab2fda07e7b05435154cb94c1ab236bb3c9f898a51046a9bb2f4a8542b4dc30115ece5a7327072b79e76d8604a424ee065a9fddb2ee17e85091339bd5aedcaee25e606ce07908876d81f643ce0cc276d1d6a24b9b162cce63c39a9f868aa9c631e4247a2d6804a449286340b2a0cab47f915c301765496bad6eb1059cb6d7fea2c8dd461277085a539877cdd601cb6bb4e15aacd0cf83c3991c29a5aaa63ae98cdf12be23a408f22753d6d5e809e0fc39ee3d7f384b5ae57a03901df297d019c74ebb464216001b9f7ed7cc932e405c3684fb9538851bdabe896dd4c013d2d437abd6c52f980db5fdc3a18713428e7b3134d5dce455c6418b0aca3f16d8a1b8f099de21515a028508ed1f398e780f0b53cda46a45561ad37d3bef7e2b8f0d56e643b318523f0b7e308f9108d9f3921d5962875e9b937cddd86c67f606ea8492b5f38c1502a103121e3a84f0121bd0af0304d84533bb273d4d7c58550988ead7c9303ba7360d08eb80d12d809290ec94c35edff2b817b85473f87e4b58e26a356017bdad08a60ba11aa5148c5501c75abee978b9c04b5fcd775977f8455e76e52044c51d6ea503ccab1c00cfbadb06876ae1e0c9263e459188aee53da19294f5ddf5f72b94350f52d3c66d8394111a8e6924df6e039569394f84dfc973130a06155f5803024b043e69295f2637c10e5ec54538cd77f85077c1b0c097cac40f437270c289b5350dd3cddd37bea0ad347c914fd540de6e52fcb0bc585e045ed0dc1c92de785c570e9eea914820ad7346e3cf1b1ac5f6b4569797fa85602321442b6856f8325ae70751cfb870eabcdbf06feaea72edbf929ba302a5f658031b6234b5261adb534a06 +generate_ring_signature 511e40c694c62adccd1cd78dc759ad6f7e6dfc6f65c233300edd092548e6837b 1f100b5863c7a8cecbca53896f81bfb37129724d1ac0482da5e3c041ff57c7dc 1 25fe770727b3555d648a78a3e962b1bf25f9733c0fffed8837ced7e2b3214a07 805b31538ebb4de57afbd5abba894511ef846fa6c94906865803092d7302fe0a 0 cb8ee33cb70cf743d9035e38a44e1fab872103e2ebdbd12961da96e465b05f02c5d28969c44759654085b1e8e39f1f05b7eb49f3335fd41a84780511b0c1de02 +generate_ring_signature fd1acd2eea26d5ea5f10a0b1e26b68e4e89fb9760cb610bcee9a0e6903a25904 984b195e9d1b1019e51a1a00ef3fead39de4a03464838690084b5b1f5b49eb4a 26 bed2706f07b60e6e4ffa986002009646f5ea5fabd1b164bab9644b690893d9e6 a4fb36914b96a15fd407598ec221d6d6a06bad7c21bfd0079e91db6b64048f91 ac5d1a9778bcb740b0a04211516231df150d714a929520183c6dd54153b07eb0 c82a344033f5709ae3398a87d909ae637b24c0b355d25b76a04fd436c0c01488 56bfdeae80cd043158fd30b06ebc4605b4dcafcdf90db2982f1cfeb7e5494ddb f6d0da924cabf77083bc07058088dd8c4074e33c1839d7137b59cd665ed18637 d4897cf986fc87998d75784803bf73a1e9854612b9cabb0d5a91754a9587c83a d0261ce00fb5edefe2cfc4b2f1152a5802e64bd8bed7ca4d5ce7ae8e6be671ad d5d7d51abe48bb01095b73590a322c95d0c2d8aac5a30cd9c071d42fd222bca9 f6fdb08161059d27e267b29730e633e405365951a79a9af34e64b2831510bc5a 5dc9b05fb96aff4ba4d6589ad93d086044a0e6483528706bd65550ed154657e6 a7766d3acd6e9abc9c2925c3d1060214894f6b084d42907bcd8ce486a100348c d681d22b74d9caab9d5f8a39c2072a899fe687e74c04beef4dc50c856eb18407 45151bebf7146e8115b238efdd4a62e9902d8ec3b2d17bee96e177e6d7aa19ed 9f0f1064920ea54f315688deb2138113e19a1ca6ce6401fe778a01749a2a8736 e862feb485334845db565de4af0b91d08cb2e4488553054918a4fcc5cdb99ea7 c07283ca458cd202e47d486a82345801fd8351b6d511a7bb5f4ada80d3c6109a 7ba6f60b39d016bf3ab7752a09d9a9bd72b34af8dfa098fdb1f75e565e5323d4 75a784cfc3245741caf9e5d074b516bdcecc8e27784230f92084850460c6986a 1b852eb3b1840382e80d8beb354567e7d6ac15e5abf7e450358b70f067388d68 9b06b82abcd972ffd5bb38d7c6bda7a4e50e79350d0d22e9ba34231db2c69d6c 0a65a8633f5152b3b3514e838be2c0a6cb63c686f3be5163044812ba73372779 e535860453422af9b12fad4a50fcb1c233b303c4c1cd254923b7594ec0e1e95f 5c70df9727b368bcdf48d910dd9bbe2d113d468e22eddd03bf268c6194f5e46e 934174a3b129c761d997d0df693377e44c5303aeb2036e6b26fb0b6f7c688146 a3a8c856c73b2c20025a9db3d15ed505a5a3cb453c7dd31f318a76fe5098f59f e7def0f0562dfcdf304d8ee8b7483c1292e92a39a2b6616e0763719aeeb4c10f 9 1b4ed62c34da2f877a8686282e905842ae30654d7f4e1ce7aacca0ba8c1f780f69e34c2de0a9839ae12627afff7280c23e0db49b5d101c8b342f3282edd917052e466dd7b71f2e1f35dd6f33952527c28bbe51138a21abb702b5a24f4425400bc6c99eb14e735541b99090d1d88c7db8c2ee45c9d81bd0375f829c0a9d9b3e0fdce16ede18534548eead784e03ce7e331114f7ba36c5ae3d4a91f481861b0701ce3d999e29e2c14378a1fa7064abb54cbca5e6df0d3f290245edeedd8ff3910d54335d57b7b23e0d1d35c4e1612e6488d3fddd32b91cd6311ce5f715298dd20a6a7848483b0f8b623b1fc92abc4630e71a333558d846e86e53a73f14abba4409c23cbf48ce04822d7db21d501594aa59251a5e932cb75c968b5c752c3709c4064aeeb62813a9c66c3d67f9f55329bd2dad112b6fdcef7e58a9f18a9dde4ef30ccafa71f2e7132ac62217a4b837ece3ed2f858ba5f244a1c05fdbaea5b7cf6801e18c1902c1a4c4beeeea4ba074e786471c553252e479805dda2d97edfe7f16032be1eeb66f54cc6c4dcb8c8cebc0ed6f41e41c496f6fbe96a92cf0d53aa19f003d3ebdb9c86fa3eea9c9ba992d0af03e98643ecf495bfd6f7a7c15143510e70e833719e3713de590e6837552da1ea891034447e232818b7eb8dbe9732a4a4905996b121cd8fa375f761c31cf6e3371c98f71bb30c7765bcdc9a4aa6f959b0000f3cdaf5d4d2a12e9cea4bb5428429a1b0446f1b6749461b18d28260a494aee064f8a1f6f92d8be76dd9056ed37bf0dd0b8362c026c90ce49a85a52bda6f7340a741c9b4e112d6d51b86e9f93910cf36b7e7d904d8c416817af3c416d02c64a01ee33905572415c2608ac5afed137200aa0293bf1812ed157a44c217fe3081e04900a44e7a673b10b42e398a64c8ad309026acd9866d0df03a46ba1866077490eba015528244082dd00cd98fe0811fbc9ea543ba296826632ba3d2b40ef79be0a1f5839d89034afca07073f656d3742322b0545c5f2c8aa300bc1868a7569190c4fe1e9ccfd5bac13fd99b797c9b0571a08bf73ab8adcddcac3654c24f08200024b5a82ef4b1b99539b7dc11e57a18dd2cc744e12bb19b3060355dd0540018f05b1894c3ba57c3df94d78367bf49e795d3d3918a3b71e1ddc31684939fe49380706ffa3267749183987d9dfa191a30c79468ca27b4e48ce7ea29ad6619a35b2046442e99e8772a5478c5e2668322825267cc60e71259e1ee21b0fd71990911a02b3c0eeb2e32f589d42a9d7e15bcd18aec78f11f6d3bc31f0f422cc2f0afcc501f2932391447ba4afe5da0bbe7d783936b8b70bf758f3b28641f07316c69af60782dd9f89a4d75596ecb66e27bafed76186a56156b2c98fdc54f96272233f4207fabe4a1f5e647add04ec9cb25de359774f844a86b69f8833be44beacefcb200c143534426aa64d28068e1a84ec617a0ad31995873e47fa91180460090e4c8206123d39777fb7d83ea96d4d13a78b6e45076e625a227c79a6fe0410d2e42d080ee386f376655611fb1e9ea539c991cc372140183fa45a9761bec5de4da7353c031a4a6463ced9af2c960b6bc31dc43750350032be1a509fe1a9f6b3a7b0522c0ce9340b12f8678452c36b065757c05c4a2690c4d47048d05cc49d92d667e8ff05e4f85f84864125e740ba20552831db5af4e928c6730399c1d3b9f8c1f4c30501df1fe2c5dc184b4793a8b2552b4123a35c93f661d7fe78441cc377fb50c3dd06e6021e4a1b84db5463e801ebac2cbda82904bc076e33a78746d94378b1fa330475b3f01b6523524816c3967e57af098b681e46f23c34fa9af4191b6fd5f74d08ce536a5d770924f875d886ae885695ff96cb6d1a4252a0d381fa4dc11b92c9049ae652656d075638dbf7549155f66c6202e2b0645dee068945b621316231a605553adb374fac14369cc66101aadf70317ef7d0946d9b5b6019aa85681d49bf02433b99b625f001c652bbf0c1256b4a83938410297f8467a6b035c8c24cba2d0afec80bb6dbc6caa0a4ca980298e03a2c047913f23ef07b26cdca76705ec3650d630fa66f40c088e4a82f517e9ff3c26cca3518953405d194e54726a3324a8a0cee9257d0c63f1f6f07119a5a3b400c1d7d9fcb0d3fc4470c84eb40b74d14710400ff55fdb4e27e4be497c28c96f20f962901fd8b1a9a25d2f93b6861a1afb403855923ca02f030e18d9b8409f7ed72a020fefc0e59941662bfcb5285382f420119f6bfe18d79b078c30c4d2639001f578805abd1f5b022df991525acde1abf08fc3435eb89634ec41b40baf9a4c6b547d2142cebb06ec2ad2f9e0af044655f03 +generate_ring_signature 5a353b51a4b8e73c0ea6abc2c657c04a77fdff66fc6725ef72f3ad627f1d475c c9900fb41f580b8dbfe112df57f0c06e1ce7fb0130751cd61c16c51120cf2805 31 2082c30f4fb72d624bb617a6a03dd887a65bc0f54d266caf486fbac1aae5442a 0eda2f4ef08a2c05146633a297314ad90dd695da99f4f765c18c22c0c808812c 749d789d269f4a8d46b4af5ddb19cf5ee7f86c54e0c08c6d93691d96a1fb348d edd996b1ac97ae752bb02bdd19976097ddcba82f6039f8115498fbe93cfc09fc b7123f37c63a6ba41ec9b4868113d6f66cbbbaa8d80427ee6964172265fc2e8b bf64bcfb1fd3669ac6664c3e9352e5f30ed3060fd80c97bf43105bb062b9beee 991ec7c190a8f7b9accadd7c2b7ed031fe9c579f343499814c4c4d898855a3f5 420ef41f473736a88f14d32ff6416ee906a4bd91c3a573c9299a4f1c054471f3 236a9c9deb53c00f8668443a9515510ab1e9b968f4be278a94c3bd17c1bf06e8 0d96fc77c34ffa4ef13b0de637055983224a12c1dcd206b283c797aab5e7127b f11026dc54d653154b6f4a9c29d188e9c8d4633605e689426cc3fea64d70b114 04b312d50cda555c47641f109b9f4c6778d9a7c52e2ebd57bbbbd32c459b5c96 b1ba5c22e1a25e62c099f43f3e62f3bd082fe17b2e0334d30b45bddb8290fda6 b69eb8187d101ca4ebb8d84c72592d77f57d04943f3ed6ce93f56bc370b915d8 a0c257d7c690866b100211a0555956726db6192ef9ddb6569b0a452e7764b0ee 99cd8de3617f2f157fcf318c43e96e37df9ed7303c27b039bad46a8ee337b684 e929eebe671159239cb71bd63468e94a2cb90b756400a389b49376f84aa269ce 8f56d7e009725f04ea1ac3521329a1572dbce6fa2098cbe4ed7132fa5ccea342 47e13fc70a64213fe9d3278cd08ca6aeb844bbaf556df1c57d927c30f6d2c75e e4632e8650a1bd477c11dfb50e3c48c77c0bb5ffbcc2de1c41ea85ef50214b7f c8d0e4cac23ffa3b13127de2c01f885d110620b899d6d1ed4fa014c809642284 33873c867e6a07e795638df98011dae680049a28824194c4c7d5924d0f7bc18a 7930cbe3c49a2067ca5a88383454ada7c1529ac75665be86acc0906a63d0b84a 803714d531449220c83f1582d3a0b96462e6a1c4fac0f73bf486fbecfd99a216 6ab2e19ea796cd40a1973def711a3c370edd3afbc452afec3a27375c468232a5 5f58473e62a5a53727b3556b0d537a5210c77effa6ef4c00a00e6ab328f29133 228455d4ae9f1cb40781b45e6bbb26562bc01fa91b81f16f46e018f299454b57 5ebd456bc28a81eb4399fbce59a8c8ce0d765543e7d1dc876ccf2401da38e483 edb57f628c6675f649fb2947c2b7abb9504e00aac31d3be494b258daffff3935 44d94d6c395ebbd88bd008d0ff9802d38e06bac61a71f3afd0c1d6b75f5517cc db1c12682ce582aecb3ae22f6040d3c804d0d000de0b051c8739587850534ecc fa0835dfecc3c5213938d2abc5b71467e1412c22cb0cd2c42011a03f15af9f0f 3 23aa305767a24c301ef4eac4b87f9724fe9d9d7c5427cb07b12b1270decfd9062976a2b8e5873082d95774cc0292d361d863d96915f61be5e02898255c050f04bbf0f8bb3b2bfdebbcea259fe15cec09a274fd29de9bb8c38f0e4dc108bf1b01e6e7a89f7664ec47899e55497deb3fae8c9dcda6dd8d07587c65c78cbc93db0cd65dfb91859a94d9ce830a9c84ef7b52ede7e721a1c0c3dea72e26fb7582ff04c2fb7a7ed8445136ef37ba1b91945adf2bec1f3da834e3a3157037db7634800d726d91904e27ebb7e5ea143711a5546ce87f9fde522874afbfe1f249488bf705b18e862816d0f6a718e8bf06e0703025498bbdf1cd1e54cf7dc5a9166e530a072a37c09be69c54830efa33c46b00f07816e7a845494896f96d8f6cec3faa8d0c222b00da1784ccf31019e409eec43e81f865d5767e35fdd9ac90725766321e0e02cc4335d67be30c859e2e66d09f651148e3250f3bb15bf444b87b503f20210bf56db8e94a8a8c3892bef31d4cdd5e942fae892e53430d59c7f9559bdbac710d44e03a233322db8ce32829a58982b70439c56130214c7bda9fbe3d40eed7fb0e0c3a9e68c02d43a26683cf166521745c1935b039b3728ab51a9893e361057a0214856779e655b319dedcfd7ce2844c104adbac030e0322d9216d2bd4c02509070cd3e9e91a11b8c0df4bf8ab8f6c8b0dc15d62506868e1ea72d919de2b0f920ef3722c910032a1a4cf27870a7a0ed6decaadfdd529d75a4e742bd66ea4a5ba048e9d1d8cdee7f0dcf3299bfb01032aaaed959a6f7b7b2b2738ca3f4788eaac099ce98b80d4a4e2e86a45bef0fd34bde2449e7d7605a3cc52f117ee295893ec0042c335d028a33ad9ddfe2fd4daa8a25cd2a5d8c213fc69eacbe64a97044dd00a5434a5d3e20e771530c72065c2a86f2353d0600e308a8855377da0c64f6bb90e9017294660d13ed05302631086e5c2faa87263ab8a85f620c6835898c6b86a0941843df16dffac01d9c926ba0f02ef85a58dfea5a99533c95b110c4e4c220b0d9945080986bbadfd9e8fed9c8ba93060f9e0f0e24bbd3f3cc02586404929f0075cff8e6cf98bf37debaedf04cd25bd591368ce6ceb39a635f695ddca1a69ac00fd3b1668e9e9a851da6fb6975c5f01cc30b8a4ad91c13daeebab72f15bcdde0e2a2728a60369eb1dc7e96f3b5ded85ba47b89bc0b080cd7638f64b2c1992e205f8170aa457f84c5d831b2a6bed84085ad9899ed0a09b1a88aa0a24d069c98d07426dbafd82b30ff6baff6964d0f41fddd6719ff4f13387a89acaa62e3bd2b501dcb0681bd3c08d9b6682e044dd30bd25086191c2e184a5780279fa69a79f550d773bdfad65b31b7520749a0a2362c3347c0263a13313f7dcb9ed079786e8a503e678fb740aebd8a8f0995b8ef1f87ea9e83e3a459167a7ee260d52e6f0ab940ea8f018fa2c052bb5ae444fb5844dcb914426750fe05e99e71566c9e44f0cd906bb2c20c508e1bea70ea03310b53a87dfab6884530f76b64390954ac9512788013a876cf8fccedd160493c05ef77474050f19570b732fc23c14573f9ed628d00a4abec2fc43ca826b7a77e40e4bc38af18d04c1fbc695465923526cf047947c016901b45f6cae865346f580da8fba6d38731d95155e111f0f5ab4b18377056d00289a8d9a00fe70082e1e7dbeff8b8e5c8827b30a8dab4a2f93ce7526dac5950e9303a08e6c200cd95b25758e5cb4caa5dc3d3ea8814a06338e8e753d6847a10b41810df0afdfe1b4564b148867ae98dc136b0bf3d6e7ce78b59e38e885d3c600e71766dc2fc59fdc28f5f07bc50a7f204222d2f73aeeb2b909a7dde5095d29081de7186f6024b5d1df8a5798af652345e84d99540c7c03425aa59add7b10560b2466bdca353f3148eac51f0f020223d1241091426709afbe8681cc9a378c9c0f54648c49751971e97294f14d4b80075a2876911b80b373a1f181e9113f2fff038c893ada1cee7b7d1910f1701374aa255e5d991cccfd3b6c3936495fb37b7201935d6a83487b882e62198ee84a75fe9f6ad09086d998b0f2dd72067365db8c096030cfde05b6243df956207ba3f41dd20b518d16ebe94e17248df8d6ed365406d631186aa29f261244caa02a856f8e07b931872637d8d84688ad92887b8f10089861e13c4da96cc3d96d67d609e01bfa42c7c2bdc6106b918ef7190f20cc730592bd1fcf0a338df807152712c4f88695d202de2c132c4d00696a6f6113bbf1090d3b9cdb98cce551dd3780e259c9be821b0d71defb7f5ca9747372f589257807a18fd5cda88586cf49ecc6a019293cfb04e7d542106c74c79791d3cc763a660539cb649c4cfc8593bf95e131609cdcb3b51dba3022d429949d3ef92112b9c709e815a19337ebad3156fb2fc0d93bb4f2042a012da19b7c6baef7b6fcfc6ef7050a0c56c816b314472a28c30739fe5f5c34b3199b316a34625ec2a916278122085b4a5901e753908c60fad34fd5309d2897d942063d748d73c2c34fbbe3cc5f0f562af9a32ff51222487ad1a63c6e1094f459e648ae2ded85c8cccbfe8f301d0a9c9c2bb1b3d99c2f48bb4b0c35848f4bc1835cd5df5343b17b65eb51dd47c2018fe1ec2f4dec7ce976bee8a5f10a0d41669c773ae5f01d503b8118fff4a0b90fcd4e610588fc781edae15c8339b8889e2df6ebf78ecd3a8d4677043a69a2fd017a083507c67396cd1fdd8630408d71c16db0254b5ce5714e141ab8b4652d34087e5a2a1fb9be9688e1bed67e2bfb38c9ae4fe14b394daeefb790625c7a5b7103 +generate_ring_signature 6b0442ce1572d31322392813b8f2796600e1bd3a7f311cfcd5d18db158df3f83 005e98bbb03be89de4e329438452ac2466eb114dea37227d0583067af043e361 26 d753796ecd8caefcdb6d6eaa0b9f198541cad7e68ac70b436a7c5fa4db496797 6ae845a62d710002eb816996c34cfc5534d1323ec9b664c20151c4571617185d faae0348d8ea4771b6855852bb5fcc60d992a0fd9ccbb4418f9f205fb3eae956 c51b9891e997455b4b220b2f1b57dd8cac0e35489e9f8156cbac6b7792a35f42 eacb4149226b18cf68ef4cf0bf21d09d3e094002482accc00b213d0c32010d25 da150b24184ad0285d59eade87dcb51e73222301f46a0b8cff4007576858e11b 731a7cfd4b0bfc6824e2b5cb6e3fd66a77981d707259a9d2dd332168310e0128 b8ed9d7e6d9db63cd5dd9f5effb80843648c18a5c63c32b67ef6232240707fcc e611578ee243322c9d2e3210f57abd215388bb1e79319fccea9944dc8640e294 410acc54a3f9762e069471c4686a7e9e3e92c61811900962d6ca2400188a6ddb 87d32d08f2338c3da64bfbdba28f6b75d46dc4ad7fa2f018f033d09b608871b1 bd0219ef9562064deaed5e8d494ab9a22c32f468c5573258d6f8963c5402c62d a56e1100ea7ada74f25f46b738a3101732b7fb2a6a24e860fc42a122cb295603 b5c1a4655e455a01e68e3f6002e6aeaf2ce8e84d610c5e4542fe2d3100a01df6 6d324750ad63ef0f6be7ba47418601b0a5b5c1317d151a9449c53d27425131fa 46deeb14d5108ae9456f2b8375dee122dd93f8c03fb326866e6409586b44c7a0 ff9057e3940c9016f2dc47f986425e3d4f7f751ef2f54ee3aa1aae85a2f32460 9598db5a457095e3d02ea5c1ea0c56f8d43a2febcc64a089907219992cc8e4e5 afba70859ebbf598a74ae65fcd63fce51abdd43b5b6485bc8dcf9a2eeff66d7f f0d872a622b64de9d6f5db16745b7b7dcbce78595217317b73637e9c253cced1 c244cc03d464699ce9f3c9fcebc80a8131104d03f2fff938dfa1f76dcaeb6ec2 ca824fa57901f10a1ad1fa6c51a444064870adb920046153246b60176229569c 2ca019b674ed3157c84475298abad0f6b62dc1cda2a11a92fab49f9743567a24 68903516c9127c4e405d1583738f2329755d88724995901269931006aed07600 0f8e7fb6d91889ad5a0aa80048a6a1d941f666e2f5c9ef5b322bd3729de419a9 6f87fe92f681328fca521963539878521e1549077f5851e04b200e3e5d9859fb e4887420585b1f8449d75da7de40507abcae71e73652b880ecfab2d43a9fad0f 20 a834e60402dd63e5ed217f5d4d7466ac13b4890e8fc807563b3ebf818242770e845bdec58e32783ab54ae7d1890e1c01fc7d0ec9062441d7bd9249dd53dcd102e9f19db13d5019e20271d38b2b9ce8b57d321722a6c6a986233937ed4d82ef0a0589077a5ce9980c4c5f64e5f8886f6744ef0392cdf81c89e5e14d4ba81f950001200a976bdd964481ffdc47e0e5a28a931c95b5d8734df7f93c3875c57c44058974d666a4f3077e13103b52b3228670b8149c4a0e44d07809e35342d1269608eba69190adec9a4ca1b3b525a3b1a743e161bcfb19b12b025b689a085863450063c3afe8e2dd5fb7940c235e5bf535cbcf1cdc6facf0d37de514ca07c4f6b80a47fbae6c5aa7824d8ed2fbce81fdc6a3bf2191c5d6e82d5d95bf76dfc1764a08d57e57cf80499db6ac9d0abc5c3b282a0bcc65f642ab02358f12dcd0f89a1109d08cbe79c92cbe430f3d04232196d27c59507ff5fb63c79e8fac140c534784013e808f6470ae209d9bdf9f3e1c5246d9669383183cc94e8f7d339d48adc7ea08b428bc89d26cfec99921488bce8eed207d9856f1cefa9ba69ec9316d04fff50e4a297db97f966152fdf849724e979a529e2fcc98acb39434bdf16059095133003f05ff953c3128d2157134f539f4c68e61a09d60a0b188f28e05acb6b5b22c071f9c54958c3ef46c281ca99ed65c7ad6fd4304b8a2d927bc1e0cb4c82208a204fdea679cb68cbc95382598358b4e8036ad186ea9db67285f590d6acebf3c910fc3acfdf8dc27a6360b3c095f6bbb14e77c593f834d237f45725477ce97177f0c61aacffcb65509e3d5a2cf96c17df6c53938a51efbca665c4b0cc9383ead280f2384427a134de05aff0df35a3fdef397e84d4f9c46de86b1282c24523b6cdf0e0db46e5b33160606f2f1d06a20076ec5020701705110f9193dca35e480a91d0389b3da164d94668702a5db7fb9fb11960da05f77d112278747ec9dd7dc725c0deb8808ed2a1dc37c24ee6dda732fb9a57cded01dbdb174522cc8f48637ecda09fd23953c1ab7f7cbc47ce6dc66806f14dc8ad6db2e44a0cf7522f51a8b8771022e80cc3188012f3d388ae648b47756970d7e74c1aeb1350e1abe2c876fc915025368a506ba5dbbf56ab8a019d6b63fd03b4a526e475cb0cb0aabbfd2348fd30e2280bf99e868d2220bd936f954037adcb6d93ad2e6578d24872db2afbf25b40aff7e03d45003dea6bfd76767aa4cb287b5d8b8dc0571d7aea2c9be170aa7f8096338f26eecd554b123e6334887034dbd86523c6ae1db1458f13d152d6d066a0be1a6add88ad6a2d7e02fdcfde77392735e686ea8b198059b5e364d72204db70aac27b4f6239537699bc716c988a8e0efc0cdf883b967aa84592093c2e3c96a0373d1525cbd7f3aacc02908e0a5151aaf6629f0b639e4fdb44e9e6ff5932e0b000e9e22a6a3b8b575b1923089ad83891a0a698cb226fa9af785823cf4f3b12a01569420e28a5eeb46c008dd64204ae81b66f094010c7ce6d47be254f704c2340f3c3d2a13e09a13f77e4a8dd49e5d518f72ea69bbf7491bd7a25ce27d896df608ac898387dc5f4236173f4caeff65954f6bfbc90ca1224c1e7ea2b41e7f3eb20547fccb087c7295adc740881090273371979a2202250f8615bc4b843b629be90413cbeca3532b7e02db4baa9c8cb2ff62b7a8f17c96da0de8a96135b01313740758a0e52e1d12b4e077f3307b8be96239df0628b3c47e152bb986d2e228de4502f761e22a2b74f011c8040f55b56728e508a5c0d4fc8946492f8152d9d88ae60473601f67374c85ceea306449a37ba293286b806e25a72b555396b1d4d48cdf001a110f70d4f5133e1f17a365ae7caf395da13a9c22073a5674bd1979f04ce70437ba19ce48482fb55387611ead9e83cc89555dd188c0357e48ffab30287204085f0a81d9fe7d661ecdfddf37f19c1d2f2f4343fb6e8bfb3739e96cb92c4ae40ab8896b390e8a3f5b936508206799d96483423d2bacdc2cc6af73e8baed1fae0d6769886aa370f610f38b028746887caae32598b41c800571f1994bf3b490ff018655235dd40585dedb9aea2611949f8680c75bc2f5a27ccb162a95a68e95ae0f788361ef0e67e927db71a2bc083eb94d6fa3064bb287b6197a725197cee0f502382c3a1e9ea372d0416185dcfdde027d512c796f25114e5d216aedaf0d41400b4e9ae7f7c97b68e53871cc90130749bc38646d9a031cead7bbed1706d1abd606b9389d13c1895910e520e86fa5bc8a37bc60b92181fb5e83324029eeda6aca0647e812c5e3688ac421cc919126a2324b345049418769507170d324cf131c5d05 +generate_ring_signature fa878e98be7bf61dc3b35153e4ab01e1c444b109729d4428288fa17dd1277318 c711a0a27fef0f9531030c710cc93fcd675120c2b44fda635fc3fe58ce68d5ac 2 8705fbfb8783ab5987596b73b4d1ff0156795c951e0c58b8091724678d9d4174 aeccdc9ec7f198a2f4824e33ed96d40215f9b22fd69cd2f0f84546093fa44c67 128378ed23f8edc0dd5305fa3e39996706a4c77d63b5ffda8b294b057c3c580e 0 eb0c48a87e20f1371074413de07e52488f53350cacc8e51bb0784f83782fd80f1cc94915efcaf4e3e25748bc4da43c22aa94a36d1f07216d044e62e79f2b640487ac1bc6c2f7856e493e3dfb28b57e60ba62a7fb3e9a779cc34e3ceeadbf9304ec7c441b6a5c3cf2a51b45c80b4b4d39c7f76d74f3c39dab5514cfb5eb562405 +generate_ring_signature 4dc38ce91c25bd6cb03ed5c0900c0b3a3d171ecd3f441d8bf38218214082d784 4279f878215bf5455ef8c99444b6a8cf0484d54010b0d1e8fe7adfe97af85f10 114 bd939bb5f29b32bd0b9b884328cb04da64b7a77b9d0f1975a42d74c06222906b bd64c1eef3f01b60b4c60765795c12d9d1e67df40fee313bc7b0cd7d769bb669 85917e8366e4bb6557e7e3d6d0851f0bb72cdb2f5605cf80287dc503333cb4ff ae20b8d284cc742818c38e284160f244b157b868fbb088f6125b8d039623d282 67676a9bcf5d35194deed9e14a41c23c81f5e96a82ec4aa28d9c54574c27cae1 c57610fd10ed400b939d00134aede8e719c8e4a8abcd18ff1b32f7fc96b9c937 494be2c5d57bab3e0e2df1715d4ab09912124a8eb65d44745b6ed4bd50718f10 d89243a7661e6cf3d7ef223ad3eac26916c71f9337e02d56db8bb710a41dca38 bedd21b55f596d4fc3ca8577ab48af6d0e4caf878bdbc81c61368799da91b655 c18ae2c77b4f87e07ed8564e9eec44a7fa841b9deaf425ea1be3ea7c800cfb94 789dbe3cb82cb86630f62da491b353336731b43ecacd9d76841917db6e265b50 d983f5d7e4980efdc335a6eef84da9d194aea20eadaa2b1cb2a2dcf6e510cb24 3fe08f0aa6a0bd5ffacc337d8cfb3ffa5ed8b1ad2717d0d51b929d9a33806790 686623a9f75270dacfeb22784afb28c0fea864434b32fc76a7c7908c378f1e8a eb2af211b6ec6d3a7583c0a264a4de602ebef841008dca26e06a659e933994f5 1e02ac91f995e252099050a2f9bf0a107500f689fc785f1f9d221deff9354e50 526b8e6185f40078ccdf886172921d276549dfbdd3a0cd27fc65f172d3bdc2a0 8e3d39b85ed3a05da76c47c8f70b76020002823548f8d45fdc27df8e6852af9d dc0915a34b7411206b4694438d629c17f84c2c07aa745284b1e84133765b88b2 6f1070b084301889189f7db4aefba51bbf037bee8dbdb086ddba9e9fa316502d 8cc4b03df9b06722f19ee747f5e4adb7b6a5ba3f9473bb33e98f06afca0155ff 7913b5b2a3371c554ee75295492f120e4fcc1761a2e90c163800e0bc8566530f 62a6ae54a6ad56bf2cc867132bdc8ed65858367d7ad38ffba12b25bc2f44a923 afd37d8701510ff65f36a4089d28a116e923640d83cd013c9ce2bd232f689cf6 d2b980b075ad35c7080194272558a004747fa3d4f54d52e54d7eb15a55fe47b5 09dde581825bb3e27bf96127461b67b50bb5258077ab5773544f29fdaa4cc48a d5e555016be782538646c00925362d280850ecf85d9c1975254025187739a0b4 099ec3a3c0b2448c583d9279b8df8d6f9d95a524d7ce35e5ad796619559a3c6a b1785018039ed1f0e47624cb17cba05a29daa6af97847e985c77b5de9e2f90b6 4d1d4f2af470f9e5c21fdf9985312b217dc866cd3c99798322d4332a75ed3f83 61bac09466cc764cd2549229e63b21ec0e00125211c18d2e784c027edd279198 e401492fd7498a1281617bbb9750fa4dea2f7ac1c05d4601d7b4d6f93f05e05b 4b50fa467dd7bf98cfdfa98b87a924aa5122b6b09fa19e2c6b1aa82e4f109d71 e4c8c681e9f7f7f0607ba801fa44816a802e81e8fc42faa343d0c0ebfe8e3e78 8a0e62086feaa8a6259ce3a21c4063fd4cc807c1beb371372f45e7aef1493123 fa7a5f9e71d90eb5ba18bdb7a9337243ba0d8735c4721bb9b65be880ac4ec6e7 8c89e1ad8f42ee19016d782eb7489ccb70b72be6ed9bc3861647bd742fba528f 339678b140b4c32ab322905eec017d40d039ed7e02c9ae57c68d2fa7e8584626 6c2ceb1dfd2d66f045bd54bfd7e5908a57bdd5216b7b17ea808e479c0e4d7d22 f36e853ea751ae307fdbe0837663f823106aded803139620a9cbf8d65d85c1ca 1514c9820ad846074be5e94a1a460efdd832703bab36dad44b04f6723e6b983f 4734b3ad5971011990b5ef237b152c24a493f304b337f556afba97607746fd81 8cb350acbb7d6e8c4da9dd936645a8750b052a29414c450a9bc886f1ee58bc9b 90f095ac9e66ecd8c321797c8cac0b6ead2328db02968466cb2a813a14b97b63 b6ca5b532fb55b28dd5788905f16a4c017617e45bd9b59ddb916a910c004c906 2acfe7532da865b1eb9f8ac6d344f535c2dc9e6f38be192986a02ff8968af635 e656d87021f86642a2f147b58869913e1721e8c8320ab77acdf32775ae4b816d 45847b41ac23724e860ede8d7fabed5982702ce646484b7c008a603b48b08a4d 6d0c71b58f122a7c9e6f4a71c0a8ef6ab1d598d67118486fb704c4d1fa8adc99 abc2b523b9f0111245bfa413450546125e29293df013aaf0783cfda5db038302 8649564fc912d27de342dec94c8dee22da9d5953045463bcecd0cf111a2e19e4 7ebc13266c2d167736b1fe30b6447f51d915a09cedccaf68726bc2dfde668816 dfe2f28066a7b2489f85153ad3fe8c42301efc3792e55764241f0377b1df59ae 53337434326613e0a7e86c87effea7123084637fd9ec488d6a5cf79678418d71 2fd4edb314b9ed004f702e97673312cd247ca6cec9dfdcae83ad266cf70d70ef 63627d0e850e46d81bcc136ad2aa095ad1a451f2d40d0954b5b9a99009c9c723 22d3231a1d7e59481188324c8a300004065010feb4408ae9bb8c5a6028099005 00452956c834dc93e022257d4ccf84a981262c21e5b0e364d606e5b8b35ef10b 8e4624bafddf2d0659dd9568425153ce7249d66dd796ae9d9a3958c207ff621e 18e20d7be6b02026da91d6a20212fb83271ca5c0e41d31f7742f9e5d7c9f75c8 0321996b983d708e35a7062ec8de17bdb8eed2aa44a33da0d2d1fdad633641ce bd8512058818d365f2a894bbae3fb48a8d8f683cb5b7ea86c20876fbcfacfd4a aa8206c4c30cae28a9539f34a06071b321f3bfbb1192890beb6cfcea066224b0 208e753a3e2ad56bc88aaeda6673781d74c4adea302c24bb430bc9d2ad99ffeb bd60707aab510cfe9aea55674eab81065c4481d823653bfdf4ceed5aa4cbd2fd d73cf31ffb213b78260d22a47f44376c188231f066635944f43b5ae6c487fdfc 04acf87b977a025d7aed687524a1612b4ec58f042f570dd537e2b7bf1f374819 b67f9de08052c5fb3a0b62ccb6ba87ea75cd0ceaa251708b40844267f6b014e2 1ba734db12fcc7ec682fd0ba5e50f8e201f88bacb180be02ca298bc501a04334 36d7d9519faf0b07448f93704963f2d5be500feaa60567408b62c2feee1abf63 fb64b38df7b1c5c931251663722e1bbcf64aeb53da9d97989e62eef5b3d33185 9934ed813d8e4ff4b96ea8691f2a73701443643065025a46b8d0ffed57deedcf ab16da45b8ff0732acd013c1a3574d7a76d4f8c510dc7dddf54723f8bc9235d0 cb6174b0170ef65b191639424a1d18c67ecb6366d5e789d006718ff192b74e31 5e832d1026ceafa167e47f8e846d6f9fe37d337fcd63cfcb0327ae049c79c253 7495dff8ec7290d6672d5ee2f5bef600fe0b573dd51d4d356dc3093801b1db78 218cd5ae20837d084034d5de568343e729c8c38c4c1aa9a7129eadc0b155b2b7 9466ad947fce451cf7fadbe9aaa5c7b6084f72b3421abe042121d6947579543f d221ba32ba6279137c88c55d22a192535e263d14033e775ef45eea9c076d0dc1 c428cfb99f6446666b862996418db4a552bb045ed8d8fcfd2b08deae775398af 8769dc3e2d1ad107d4f0d96cb5823602daf842cb159238809a9088752d340ca7 4083c2735f5c674f4896893952c90d275c6d28ec96961b36e75164b42d63469f c1f161d41107d3e258a1f77985876fcf02f05d98183ad77c5c943944024c4ce4 7e5fc3484d81d4f393f6fafe0dd4357ce73d08b6db85aa6d4a5dfdf61586f9ca 5c39e84b62a9dbcbe60d5625491f3cd44621d990ee50b74b7b3a7862057c7c48 34383e8918f547536c4a41cc91b2f02f642905325140b52f53589bba81f16383 1cddf5fc7808b6466f75be06c1b19b90025a85e241a44512e65a94b275058179 141df5ff0158ea408546d801ba6f0c6af9bcdefdf23ac134cf932835091c2f22 d476b262891e7be025046acc191b5950f598d26b940d883be486e5032cb31445 522d85cc71685afa492a62c6f4508207849af1987a7116cfc90e4c19ebe400df 1d1f78f69b6744335233e84495b0511e32d3c720e2373bf669c27a4990668e74 905d0a24578ba9bd1bc7b3686d1b08848b470ab12f4956824cf3b0fcf056c0fb 8a3768805b5a09ebc505e5c0d4242b617adb9e0f3865199bbd2a7be7579024c2 e7c80d659dbdd822a62c5a2b5580932a55c9baa6dc0818e8ae5467869f87375b 1b32d833d664cf3338c60fa3205c119e1ddc8c9cbeb44702459a97512575c832 12abc274c96f4ad4062309fa3060d05df560ec075cb78a80251cecacc9000a8e a01aff1dc079be3335a3efec1cb3be3eaffb22080688a4fea7ef634f8b1e5bac 1f4f6d983fe92cbac643d9a027741439190bd7d911d90c50a86bde27d8f4dabd 5d82c7c2e907bc4f46744c8bf1d8e441c21e28d3e194ad9acabad465022496bc 8177edd83f79988face73a00ead13d6c0ecb1db4f365b2e3f9568ad58cd8c641 2ef4116bf37982032ec32ceb4e1415331f4fb988957d113425ae1268975157b7 58a2d5b81c539077063bb9d470cc5aaa3f5014295ea7790b6bb002ba368ec551 452b24765b3f19045dd8c1b37e16823a6ea3c91d9500709ea0aa83880ba159f1 2e967b6d4aff868718cb1a7b26f50384b1012eb02313e8947d8e9a7c52d28a7e f160277b45b9b94f4004260a2dd9b2682f13081a1490cb873207ff96874d296a 6b9995916945798f3a838a968e36a030e64a240737da517d6c29fcb56e2e033a 3d3a67122086e15daa676f97a1f704008dbc47084ddd75bff2d52de83ef44377 e2c2c8614448e6b57b2bff177ce01d466211ec6cc80e26e9c4a9238657b13932 240762e3135c801ffc2df0714bc22bac52f23089d3698fa14c4f9dfd1eb09377 1b120c957aea684c4babc04fd213fcf10b20f56f8873a808dd707982c73b349b 468c72e026931e245e55e916ac2d0235c52e4255826f113b35c9857e171b2901 4dbd62c32aa0b9fa0cbb9c7f8174387957f23d2b7cf1386f39400b48f40802c8 a3d0a864f3226f810e69cfa3c6fe0e2967af9d69f12d39f85ce0c2c43e6310d3 3e7bf7715c43237de8eee1de54dc56b73f8e7982b30c1f93bfecedd49be9adbe 00ee10408bd89c7576d8e154cac0c7f743aad43ec26fe575f3a005ffadf84606 44 747ea911ca5a0243b6b0df4deb8cd31d0c268267ddb82b25029fa52c9cd32905bb531375a95b2987f5ea4e6c82db0205fb74ec3801ee725a5869829fe5abaf0caa5412a2fba581aebd07ac193bce1be8a6ecdc06ce7fc1d53005ddb72c7ff0048a8214009150d6e364cf07243016bd5fd7236c179011af2c29f09b5cb2abbd0738c4d84430f1f330c16d09653d88c396bbdad126ffcad281eb210821baa4de063fb32f803cd07afcce7f83227b3556276d113d6b4c0aa41b06d766334524d805726132fa59e479aa3fca261fb1b0dbf7bac6d3040a8cae36a50628daa91e940a6a8730b5ebcbb8b384f5ca886e9ca9767c567be0bb3bb6049c0f8b4b95ef8c031c80add827b75e22373a80161ed441ebccefb66d1b023a7a6cbfec90f9c96b0a5a2d5b1dba2b308816f2906073cc5cb9c3244306c728afdf17f3a6269e8a540d69f8a60aa4c2f62368a5cd7cb9d9d44b6a8e1e9910f53f8cfc49df4a4c26920ea41b4f0f862a909d6bab09abb805c11444f56d4faa59d53bbc747e4c06ed6e074bb074e98e01a0e7c53edf0e8e9f93c560ef0568ccdfd127abe44b41b3d5fc09717802848b79a5e843f3b055e66be9238b089b22de3313826f91d85ff5165c035efc463e802fb9e6e74784086c0fd8796ba99f30ac047cf418f40056862b9b0ef7f31f409e03731f97a72b1c0fc7a438d8baf79f8d0e837ef6a47de55e2dc10ab691298735030640e91277a872bad7a760454ec7a8f8cb5b6b6dec410468a30d9cd3c5ebe00ddcec846681133a97950893bf4034145fb79b5cae017e643b5d0467e73f5763149a1c614b03026a030dd5724e0b8f51ae5de9dcc989109c9897046c24f40ec49c9d3271d9a9047b477954955065bdf3a4c0690cbdb5c79d6a6f087c8ac80e88876e8dd21af696a0a625e6c8367bcdfa66acfecfb352a2fc926403a5278e77c75781da2cd38525b339a255dbce80e4ff72215333e5f72969593501a2f0cea5a24458ffd04ae63c5e60331a5f49bac3bc404acfc065b26afe8d9a08b3bec0d08b594ba2bf8a984540cd1d073236fffef040711a7d8360e59e5e7e0b1a6216fbc82a015fca730ba100fc9fbaa9c19970378d8735769d644dcc19450d19601d5a90783afd8787e13c78d39499e1f2aba14b0b89935613049242956507a3b2146549e776d9f9ac61f0024d4e0dfa14618030d758c2edbbc618c9ca8b0ebb121a9e5bad7410f82a49f4cb8b7c6fb14edb276453318703f5253d595dbe0c181ec17dd0cb73381342ca4cb4d5f81e0721f7d2d1e44539b04b1ccabeffc90e91620d25d659b1addcdbef9a851b0057b0552fdda4bc0193e86bf409ce65a10a310a9d68ddbfe3fc33b95394823e74af06880920ffa47b3457669368089be10bc7db22c5f64025c24fbfacec410d6db94bf0e20f6cde7e593d33ca980ac2ad07c0098f7479cd461f3de966a6fb63907a705f3828030a1a37714001eb4b68420a3c032988b60b4967bbd86906397734af2a9f1acf28e6415fc855278904fc0e08e272f2f150e42082e9d9c64af3df5376aee6c4e9c65f01c6f67c1fdcf7515f035995db08f465bac830f57e8768d0d504f713f26488087e1b4f4d83bb4a959a036e5377118a3029df6a8aad18d79915da351166bce1626ad3b70fb103d7187b0040f36a7cd387c2cbf75ca42e1f3753cd914b9b17a2d7468bfe6cdc26556a5e08bb0169076e6bd98244d58a87472540a28c8e3624b71335c02c08381049140e07e7064668a6f7250cba79241beae7bf761b6fc6620353a6de8f827b75b2f92e0ccbe815f1c48d41fd29dd3e1f57275bbdb06275c7255fda1db2da004ac6afc70b43aba531b3d40e8172eee3c3af0cfed3d0b3a4d4e7da00d5fe4679d37ad38908ba924197b8d3dc5036178151ceca051283187965f381e7fe2ec8a14fdfa53300a343cf5ea93ca310702762d788480acf1c31cc35d56aef708c6a5c800bdba006ee5d983eff36b87ffbe125b87e9c16df9012c147b6a58e2e395a182159c3da04bddcb5df1231ec7f3638ee1412e3d06859f55abb79af3b0e760fff6805d2b30f5dbf1b1ac3435c6aed26200a01a412ed3a0e904c8bd1d33b80b8ae819b93ac086901838ba2166f9ae285d296e5313a36b9739901d682345c2933ef7eacbf80069490d86f9f5a4f44f7e73a2c7d0a1e486f56bec7d1fd67475bba6c1f0a5813089cef1f2de4f3ab7924ddd0e730b48101ba69240ff0a99ed04bc1a999c8dc1d0b117883381cac06ab840ba890c65aa93103aa29045aee55e393d3084f42b14201353194b6e05a91a886c22292d11199b82e1bbf3e30b329b2e03e544df0e92b0ca3c203f3675ce8197900f960d0d1c23f547b88d3a8ecb9022355609239c56d04fe8ba00b10bd656a62b56509f26ec99769515f9c58864c483b33149890c27504fc7e041da197e7065a40e938bbacb8e98dc180daab05ea379904536bff5cf409145e2a06de08b3701ce310bd2a90af0befd4fbdcf9f9ed1218bab1d2f7971a0c2956d4e98415bd16f3ce7c2a92aba3098d8793e174db9603eac09cd22c2704061159aed9f3d9a2be8f00d73d8e73691fdec4fdb875b4be159b378494dbc10405c3421ada30693f88ab3ae0d34a9e8ddc4f6bbacb564aa0d8886bdce425c9560cf6be7d4028238ee8608efd4b5c3443b8bca5cc897f6ea173e1cee38de66b02020d2882379f321af12062f9b21c022cf0b32a94ed95236deba98f1946ad4b260ffc6edc4eda7c0e49bc16937d0d3d370d3c6ef8e52a4dfeff525dbd6d3f9dcf06f13710590fde31f571e3863dd71dbe6adfddf81aecb8781ea482466ec30997010bf656cc40d44509840be524f976965f1f5a84a9e71ca305c4d33737c7702d0067362b7b1c25d23d50eb211c94a5745f941ad46ff21803261b8150236de0b60367f00942be190e9b6d5120b8498820b5c464947596a30d691586baca77adab0ac1c342e42fcb0dcc28bea9fb9235bfbcbc210da8d424e25b3022b8feb70dcb0223e6965a516bd68002b0efa7c81f3d06d83b9249c9db8e79122d7ea899a5a20a4bbae76c694bf2680e31ceee22f19bf82ee098d5a52f5870380e7373ad395501fb9a422857d1fa766ed2332be2a69c9d8aed47d6137193409e2c83308bd48d054ff0de9fd763701e6159ff7a315f3050ebde11692dd4d64e901569ef2c68680b0a02f78e5f65cb748d0b75d533837feebe3dedb8f4263871abbfa2bef3ba1105708c0e4757badde88f56f5ddca7db74d7738045b95a6795ee7a31cea52340b0aa8a62fb37cd0260fb5ed73a45ecf4fc6f10749cde7e52d50ecbdc69eb6005a003685180b2ec910ae26216e2c0a1e96a4fe69a19d6db41847fcfab992532d5c0dc233f5ee18756651209ef425e2acf995dd44390acbea1a23ba52e0a540650e0a041ccf345ef07c4fb5d78888cf248e758be46d3ec90d0a69ab3af5d85fe91e054370a75edc76c5b20bdaef3a4ed654356c9d01f5c9fd53e30f7404f5f9d5d706a699b87e59b6e524c88e62fb4d28753ca6c53b96813a1fab2890c10da873e30eca4fa370c22a8acf94d004d78add522c4c462aec16d5c8488f39cfed78b8bf0715d40f78cb86c849ce67c80e851b4d621d0241c0fc33b8b7b976f139ac4a6206d8ac43f84c33ec00bbec205a7742e8098bce09f542f116b9da3bd36347d6e60a8745cf842d82ad31b0cf60153777615ffc03708612186b0edefa9f1e4c877a0f18cfa7e9899cf5f70670a9474f6b1eb19cc5adf6b30297530408ac61ee3f4807b003a0dfe5a05a6c7105c279a09ad2c3a2c50822f0faac88de8bf622bdd9b0043cfd1f878da326a384b563ee5beeff0af89be348310f6e1b81f1e8b972eb6401969730d4acf9a806bfe10b040389a7cd994de800be734a27dd691d7194d1040de3f0260119c21e8a8f764944bf95987ca37c20fe7956d483a45f3a4dfeb1be0029d4b546d859dd03aa583158f00bcfb1a66e41a68e8466f9ed66af4789e15407617f252f2ecfee3f2db811a0bf2b600ab81f44b918cb70054c366fc9e00d7801c30b0d00a68c6ea654e56f2a348ee3380cb709ccf20a299295fee486df40e5096569eaa86d7c7d0223deed5b9bc94763ba679f8a0d7329bb5b3fdf0aa660360de7901d2957944038147802fd36b1c5f0b24eb07dc4d208bf4ffde380953f5c07a7eccad3f39c4e1911b5ed640745e71f052c712c6ddb401fd8ebefc9a3aaf408759a209970780cf39c62312c8690bd77cbbc95b87f3fffbbdb3c1f95efcc150ae9947588bb019af154967b04a70453df16f2eeac93e85c3c11e1bfe15269d90e9dcd37d73af6bf1ec4328e63f5ba6c52e3f2da253f96608f0531ed366ff5290d58e2c24bf594ca78f0e791f20172aaaa838f61b22dfaa332f36e18767b018f06f34b1ac8ce7ba882e996d6086c3a264e636ad16721e8f008b5b19f06a2de3e0abad7dc659d340d8e36c2fc68097e99e994255589db459cb17c361ffc303d420eac111e8d1601d6bed243a4d2db89fd5cf3883308181b3f0b3f07a5489544ce06a27647744548a2f25e27ec1651de0e962a00812039b5c6fedf998076f57a880e9519651dbfd803d4d723e17b7040c05101e3b69421dba0f7ee6589ae8a696d071aad4a8faeb258be7ec6c456ca96944c502b8ffd6a97e421599e8f94d5780904d8871304f8835e74c2480892b54e9f8e237efa1dcdbf4f1fb8ac81f3dcc25c0219efbd2fd1ef67b23c7da232b5bf2bd681686005bec01b283efbfd524198570d62e58e9f8e39d383e0caf88ea81b1566ea75ddcbbccd90adcfb8ed63a9b40c0c8bfcf34a3f194081585d90f35a79082bed4ea74f739650349ddb49595a980d01a74df4f85ca9bc47401bac53dca8548ebc97207663fb0c5fa62dfe948b98a10a56e78f138c5d07c13b6a7252058b3209b4f178e010dbc39e62caea46f49dcd0fb53eed92d2f554c26e7362a6b1d8ddbf0e121639b9eee14b202e485a2ac3e209e90581243dae633c655743ccdfb9c34ce1e901a30d9095097caf2bcb475ad301efdc4540e55f7bc810a02aec6d3948dae0b760237999476f2a272afc1fafd0046fddf000e67a2d3efca78ac096e9dbf05acd314e099c9382ed9d1592c2335708ee198a353e639a57ed639bc33dce4c0646ab8d3541eba898856738ef00411e09370a0423451a64bf09004a8b5cba32f7d94fff9eeeffe91df31a5728da88ab0b8914c1b7ac795f90d6fe9245c4ef1f542788e7ce30262685a2e6593e573e920a581c7dac573d08ff551501f7c975527e58bb7be29a9f459343dd05c29773ea08732a6da07be781d0cf8d6549cce95647f8fc977872f541415ad6d442a01c360eac8345d49b0278b83bc4fe3563632992e4538092c28ecb22d0606acbf07f20027362375eb2ddb9cd367b77fb6f909f4998fec5911fe39bc7b28f6c2a6e56a701dbc21dae903e89b9d12656f1dff6f1f67416223012ec32878b8c680d69017a048117a926c571cc9406ba3c9787891809437736ffbf1d29c6463d00e458e1c60f89e1b9498d1321c2dfa55c63822499e4cd4529a033f382da6580fa8818b79d08e49ae3eace2cde08fa76fe0a496750c89dc682c278062f597bc618ee6b079204551a3e33a5023076ab12fdaee5da6db11d3eac4df762d8f7c752f4b3d3439d03de70f5d58193b9e343d4413d45ed52fb06d22408411c4eb2b77134376e444e0c8e93087b2cb4788c3fb6ba616a82c910bef9dd3a1c953946b8a8bc07f876410e69ba45634dc5334ca1bd8ed1461254c134d03d2b3f79201810ebf0e2baaf730e3aa6ae820b6e2707b84d7bd150809b3b17d9848111facad44822750b39d0c9006cd6a570ed1243d09600cd85f745be9713e3dc6abc45a28461b3e66afd88c20a7f6ce4e8884b4b0d84ceb6abb4549c6f21e3082bf2153f4a28b07bc5b658c5008b7a5740185cd455bb201f6f6f9335bb0ace02ddb89b409d5268ef929ce63702324c558ed020218018ea3d28ea64f69d5ad843f3edd19924066d271d6e44e10d67e20711496f92d51d9cba8837df6255170de993ae0b3278c70c520cd4246606f04de5444492e23ffba975cd4aaea44f6c1b2e3e6051e7e62485fdbc68515205a9645802b9bd82839085424b0ee46a8878b1f402849f56e6b9bedf2415fa3c0c0829d07b65cc44a6610a0da42d3e1920754ce696e8644af377ce57489c2cda081356c1b8629a9488a208bc0811b0bdf79d5ce0977995ba728e759ce8fe84830c6a7dd2cc155157982e8905386f50bc3462e94c2353b7d32ecd0026679c279307f7b996258b07fd6de1403f3168baf815b3a189f6d83034f3866f6e7d3bc20503c4400620c75d9164efa7b721016f8cc101252b879e630eeef8554ab186ce550af6075198fe8fc899cafd03786ea7c490f57b44469598c1f5d137d767855a0607cc99afaf6e203542bfcf48b1fd7d8cea2f183f6ee47213067efe336c9017170c2b25b21554710c31a2bc76dc3780a036c1c1c6e05ef351e29133cb3d1428f808301b50b062bf0a34fc525fe7736dfe2711969650fe2910704d9ea74e9955700756ee0193549b09764a1d17ff01035094300590b48e2b461582fcb97f89e3b9088380d720e4b762a8f54184abe5558e2ed8f2291a0399147202ca852dbed31e0bffb652976cac42df3a41bb80a9205b8d7531ef91117661012aebc21422a4c007c14dc4a0a14043d3c9bc2ba41883ba53a5fc0a4f7cf95a9df9cd54752710f7095f2f4ac4b6e3f855a08603f1d75c412918d7f3dc155b3936dd137a6d266f20093a02241cabbbb78818a1af24075ee409c2dc7790ef7d0448c05ef964daa8f40780404c0b6f4fc1a46c3420ff2ba8970d365a3dc6986b5af4d33c57e2a78a970109ca32d1ed14f99c1cc87a15a323e6f2341f4720360681bf9da6d932f2d77f01a56f946d5256e4219baa0434f1a2a26824d3d21b105b6ecb1ae32a50eeca2708dc6f5064eec90df5a4e586a2ed0e56189aaab152bc823c881e7baf3a8dbd9105cad4538e70d403db8fd633067ff869d5439f9ce186fe5a6ae5d4161e22e5050f00f1eaee484f60428731b9df5f0585becea0fcd1101e50a0dfb20d64f47bc506a42c7e512784f414a8c93ee1bb4db9217d7bc60a263429935cd8d78615973603d8c90911c72f85b2b0db4212df4136e56e6929b75464e0485de93f5955b59509fbe6a66ef75d35f7f5a48205e743cb7df54ea681f6b3555323562a0b2be17d028e4d43bf6d5cd4107b9cf3368da9f7b9ef75d27a2bcaefacf45ffa53f4cf0208fa1e45ea0151fecb0ca5ae78123bbc374c120524ba487b08515016a49323130210ac4777678f6a757533e55644462cb9d2c371c032cdeb5df1b3ff03bf2c5f07a64843da23bae5931a77a306f816d7a0034e06ff77b1bf3b4bcccb40819e860e1d6866df65c00006257a80c8e366a827658f9cd4531c51f51b92845a5d41ee06bf0cc8a7b41a93ab19ba40f6a24af1642f297b222ff5c0ba7d53658a62322006e187298e0fb1107ca216ca491a0c8d9201b00384c08a9932711bf5b082b3c40defaf5eed1dc613b70ee379cb9db6efafe068e4691c950bb5bcc4bff4929aa70e8b51aef378bbdb8c91c6ddfd366c9c2dc1d002c2efe1b275b8032167dfd1a606fa30af25f40f655c5c5c256ee850acb325952ce59fbd71c8e63ab81dfd89b301649bb53e38f01bcd2c9d2cd2bb7b6f8e1c3f590ca020e0093d49da2abf4af70212047b62352183e196df1083a1c5b164bb7f0ba00ec4a6b7f1e7ded07c6dea09ec2b6172ab6c3f0f8f79ec8583ecadd38c54e0f9eed122f4daa1294698ca7d0a1669b616f2d2bcafdeb73117ca1cfb9c40e8b32cbbce61e7f1cb478ec2aa570a8387c3bf9ce73315dc907b9134b4c2a842799d0cc9e54405d7a42db32a64f00322129969e226463e13f1ff4a7b055d12c718e1b6c67d1afd8a3d2be623b7e403762f4c890ff6e40fe71a6bfdcdf9301fa4de66733429bf135e9c0697bf507106b1278afbd8f4576bc6ea5dfdf9789890501a0c392b578632983f6b1c7a67fa08b1e13a6000c576f8f4c549b5308ed1f3123c22eb1edc9d2ab13942e86e9b6f058899a673624eee13326330126b7cf121c35951dafaea8444af2f547c91a2b2076ea0a791ff0c7718aaad3e759266825d9efa222b9d5684baf9b51f1c015c4e0248f28d1b309097b09d0929c5580b108319274819b6db42953ebb07b9b7c1530509502f194b66f34a982bea46e37619ebced90ff09ddd23ce0511f20cef02120edba8bb7aa844199b034bf02475e022902e18029789ff4e15e880b55b9ced560a824bfa6e7ad37f5102ffa70022b2bb061fd6aea7f038f8d01c9384097109b50bdb3fd5a3a0e1ccfbc7429a3e1d9ee8b07db2af1b0aad79d8f1f8fe675bf14e079f0330f116655f14a2c2c3572a69f4ea3ac8e7750c91501e435d059aee3ebe07e2fe3661bf5186b7c4c5bc0dad0954f17514a8de8a4bd022511d89c645e2170906c347ae4e4fc77e40c73786bc1ca49f6c37185f4b18c98296712a248c23c909becb1a4251b343b5f5804ef4643bc442d96246244b3a5e39edaf929f9675df03456d8f0dd9124656fa8615ba5d679e6423a4a5adde1b2836434c65591b6bc20bc3c002559725125416492b2d937cdd895eb4eab2eba576e3a996de79dffbb10cf6c48562c604c78388d135c88f856721dfe5448a4c88d6727d973ba577418000a3a9c7a260276daa1ec7514d7cbe7d47aea92ecf4ac014e3828f86cc2080de089babded2773d64dbfb5043b5ac220e37d3aa6401324f107a29a1ab5e919b2b078a5f31b00e931d9f6bf5ee96a5df1064c5d96291da01dbbb135bdec9a9897f09ee1c2d98fcb2df112fe3cc53c7679215cc0b937cef825b5bf9ba2823f673030d7a7e86ab41c616d58ddcf22c58ba5f58948d745ed31b6f92d8eb2d31e95adc0a66b5df50fa836da39787433f70ff7de475c6cb36db478cebef0f2ef8873bfd0208e3db699097ae772ed80958d43c17428c2a09602dd260670a7e57a8d9774006f3cbd405764d7a9d33017dd7dc41503a90cc6ff984263b7aa5ad8a7ff7d8400e31cdec158c95ca6a389e4cdab231510a76160514c24ba73d7cb65a5ca1c57d0481471330e55c072406520c26c22c8f7aea87b8303e1a417181dfbaf1cb4ca10c8776e513a6e4412c3c21032e9d6e43e77b41f4a8835ec5b0093bc84f4df5140b0d7f02e6de0072d7ce8f8935279cec4a40e365d296f838ae92cabc6004234b0e2a5a38a8a2ebedf1d6f61161f0bcfb875ac3dc2a11a29c71a6af852b863f3a0e19e3e15f2182a31d68e8e194f42ed97b870eebc6ed86eb4a7520615a23fda302f4d7d756ca6c22494029dbf44312a860106cbda5c88a737aabaa359b51ff9f0d86616e89f1e292fe3a3dc27c47d717e68617e083c262fa3b697c07d04806730b4a966a6e57618e33f278364d919a5ce60a8f174e0321802e07938601ac3f070f948accde198526df578b916c2e1e02bc562128818c70f5d48e077b5171057c077f4b36854b01bbb63430635b5aaefbb06461449af9c4bc5bec48b708effb3a007a1c02d16b40dcb20b11e166381efe6dfcce5cda786354d66a6ec9bae8c58e0f7955b6fb065662ef797e961afa00538259a737d32671e5b34d85e9c3a748fc047cfb99874fd6e6c82a5b9272cf2cf29c7e05750da17819c0cb37e8ba7c00f50b14f1302cb747858b197026b77309c2bc88b0dad05693ee76551abee1e6d1c709578ff4a26e5689d8e9a7fb03d7dc0409b205196061b2edd08a6ce6e73774b40200098dc823f5c393d9183f751fc71b3aef4a6bb2bebe5f54345f7de75e39c8096c12bd7e7ac994a4cbd70903d54c817bf758cbc5a00861d60ab1432873ab7c0c957249d43dca6ee291143297e28db4ce7aae348458b9268d8208ecee87999d0df0eaee097736bc5aad5c7915afd956b439ddd4548b11cc2530955e5cd9af2505b36017c712c32b7e6d9cf74daf01bfdbcd85f06c949ac1979609c7a427c9160a35090f7a102bbc8057f85c8bd21acf0d688c45a95f886f066745944cc6f67b010de33898ffc94e879db9f2162c564acde3089fcef5d446f004d23ad0f116310481bd07fdc4f06bf0ef0636d6d64755825c35246c6719de4a4b0abcccb088410fb18af86edaa62f20856211b700ee1708f2d65dbfd2cfd6ca995ee5c108c5740c36b92d8e65b4959008b7a726e0d9aca1acd7a6f5e7c543061eda54c4beed3306 +generate_ring_signature 52ab38afe83396f4a7abbbe478732d39260c8086f1b221f37018ad83a56295e8 52144c557bee42c63d3943a1da22bf53c53cf47e18e39ec7618060fdfa2a056f 113 2667878a43543a286946b627dba44ad682f51a0ec0dd5885c0c17706e1f27962 56070ef37ef631a894808bf2998c18079451cab0c36076b94ba7046928c7a1b7 09d61f61e982a1e500603b81bc56fc727b7ab3822a8af511a213619776bc559e 3d16d0811f8d8b1ff564124ac0735d77c2b62ed3339a2542deb20167ac8d78dc 8d1e73a58271f020e21201b77b3e1a0d5802960cef0da9c30f88331d1e53b7b8 b15c3a90c6d12c50c12ad1f1cd4f5cbca77abfbe26dd1003c5f92d256b7d9a2a fce4121de27006176bafef1742169e769f3a25a075b413ef0c3f7affd5150561 23e7a79af125e2cc0907abc7b61ebb2b6d712691a6c556474ca79c1cd2873c0e 1806559d6d2e9812dfa4d97efabbd8ecafb8331b2a836e8b94212ae21d9e935d 3d825ca551aeb017319626965977dac767f90994d4574ea52b0600086f745c96 9e5d692b5e82670f6284fef4efb6a3dce320ca9644126d52782f922e2f1cb62d b04d172573b99e1b8074d362c5f6b997b05ca74a5aa4d35c957853f9078ea90a 451d26bdd2e49368fca9c893cbf9a2551aa0bdb1df3d919a75f16da4615ac00f 39123ff66a0a8cf47643625b997f3febe9b843028a733213f920fcb0fb224321 2e344a31a1f4abb279b66741e22b00d72342c93ddd66332b9480f38ad010dbf4 174dad431a88a820abab2a8ddf30bacd6c0720da30f98ccab7c821f2ce13570a 8fd6651dd86f87c6e9baa019e3222459a3495c481b5983bc1f3bdd43600538d4 70bd22972b4e8c895d93c5313a2c4de3f096b2832401b34492d8b63e4c828b93 724ac189a333456efd5ea95bbd6c3174180204a0541f56c9d3c4e82c1b9a9430 ee0295b969e97cfbe6d47f879da1a789cb27f9bb2af514fd549b4c8d8db67ea8 6516c5de7a016a83efaeb52dc3810e147831cc6a7bf6d556aa711e3f26ae74f1 3f8aa4e1f31046011579a2efb2ff2e98c2e94868bf61f529b28e0c9dc922e65a 40f502a1fc081d3e0870baf688962eaeb053998be9e61627a99ea713bedb7d33 f5de2cbe86740e20007d273d240495720423986ad67d13022a69f67eb65c9db9 a72c9067245c4f1f7d3c6411578b2e02e18c373f78957b0c011d1ba817d5fcfa 2a6f3ed6fe0238355cf8a1bbab5f2c1f5674c2432cd8c964f4bde62d1f84089f a1be89b4e0cd03a7080617e61c08203f935e63429a34f8ae637c6290a5f855a9 dc25b39ea362c07b69225919ec631e1de3e539a0c99076421a03888c136040de c42663cb57da0d67ce5e40583deafdab17af7f37f4e9cacac7be29510b77ae60 57e85e3d5bb0bfb51da145a2345412b3d4f1bef0a9ff125c2533e3a76c2fb261 51d82c0f6c42340c6f37f6a4d24eeddde756700a2a674f0e5745b35b0493cf47 d6239694cf291b89f96ca1f448b250193f738547b70c7627a72d6d8c3edef2a8 3e3ef92d08a0f37954c6ada45ef29204a7dacc626c7181d7613f2fa613a7d515 479b439e8cc29f89d9dbbc3acd98fac89603d262b014a2f876ae4646ada1d401 ac9c823e44caa76f6d0d2ae5da6684a6fe0fc4c623cb28bb0ffedab0de052047 bd47406403e535790864051db0c1c2d09b0e7a0fa9df52d23036106cc5eb915e 19abfae288790ac7d77c0581167f78415897eacc19ae8d537d118c47fa2b1553 b5a5323c096b566ec9e91eb9f4c870869872499205d05cf3d7b58811d60b1f58 36d46886bca44dcd834fa408bc22275efd1264644143dc3fde9814b06d1ca44b 758aaa67d8781b59b605c687e993e1d43913e3693a351d9addaff6a21cf196f0 32c1e4af3a5597eef97f47511fd377a961dcf5e3ed4c1b8ffe981abe8ce31a84 ec3619a0a1928ce8907e344b0c7a8e87e15fd53ba8693341138a5714442e2c34 c6bd33f733cbaff02918e91d14855be7966fbe29a04b6149833010293b04920a 6b8a52bf35bf9bcdb80f5c22fa241b7342b4f6bbe085f78e5096f0647f0298ba 3222525c78358332d2571622030a2c91873a8912f815486ae7f1b38418cccb2b 5ffc8527b801db0fd1f9fa568e6fd8915afd4d2a12c9e760f8a0e3f65c13fee6 65cd0954290ad98ee8fc0ade4ca8931b1f6832199e69496a57d71b25e0c4da6d 519dd0ce81bfb0e2e2aed5c4404fd08cc5dfc32cdebd2c4eac8e8341a99593dd 028506245b4782ff02e531921ad41f33458316d2a04701949c9a1130170f667a 65479d0e3b8c5e054a42165784f71979e44c5ec86edbd9360d364bec827c38ad b056a8e8159cb51dd0ff6854f0b5cb7d73eb6af8a5056f7a157000fe950956a9 121bab1f2128a355510aff20a2348ca726015f44b14247ce79d17147a4c4060c 67b7dd5f52f743bf4421b83e735123e1c17173c562bebbc92f241f748c45c682 1e8a956e74016f5431b773efa5ecb58a388e94f91b9a5a4d928c90b987298361 928bca6f3ef639fb399620438c215df701e992d50c32a7d519c9c4029777d8a9 4b928f62407d82ea63fe4ff44757b0e2c99b3d809f473a2d9856a4e7915a4048 2638494a4220023d7abb69e0663e321321a39f10ed88170cad8c098adc2b5762 b4e4fb545e5daad738d95438f9caafa048eb5b7b6cda4984f0d98b218f8ecab9 4e1da4093412883d96f6080929c73604c9b918e4b4eb3263edd50d07cd370ac2 1825bd87bd4c8621c5d585e3a150b810734249c5f8b98826d4fca4fe17a479c0 afba4add454e41364dfb8f3f10e613f4379125837d809f9617c0c59557d8ba06 be6c78f9549f725e77166b209a3a62be24858c6fa6730b751b454d8db46513f8 d72dae253fef5793e2c4b21266aa5fd4bd4bb298ac12024c3136bacc698bb5b7 3cccdd128fb4279cdb8782fe4e891c92b9212ca7df38c1281a7c23bf0488c7bd 38d08fe57e0103171b5000d5f7d6f741eb51c405dbcc7ba351ced61d610b5e0d 7877f90fcf24f4cf3fca2f456e942af7cbb7c713aa9a26a437870bb0dff5adec ec4175d3e5369062603878430224568d1d9f6b01b48a0377dc82f66f9ca309c7 eb039d354117c3df90c53e9eb0492b4cf2e4f6b4b57bfb6cc809c22621edf016 6ceb1f244447e6c4fb2f4b7abebdd227271ae511383515241e146e3401b47afc 65cb32f44ecb9ce88d08bac89c7b36eeac2e52fbafae9c104b65851d0d3fb689 7ae2db115d82335fe2d49f12ef8248994b958fbedfde2f99dcae864daadadbfd e547d52a6eeb118a7cd1147895f5a75dd0dfca914d50706f6a7033235e3ba07f 6ab9a84a88f767a1cbc69b2740c05acaa9fc9d84ae58cdc77e01b0900cb19ed7 601efbbfc5cb40884d47fba0a5e1e5750685788e4225ad69319f0dedc8053cd8 151f98617ce7dca0ac7bdc7910fcf5afceea4b8acb144110377b458ee877eab7 7dc10dc41ad38ef8c879ac0c434ba7dfe0c4ee458285bfc0b07be3127c970bca 171ce0ef0830d4ed99f42e0c20d063ccc3c6ca50b747b36f24572535d28c77ae 3f55caf66c98c9d773a90472b2432e6bcdd84a286b518a3f3f95e2ff104847f0 98ac27c0ec172728c1284698b62bf64afcc2cd1535ab245b6ef9e39e1a1ee486 38a2e914fce5327a0510a04ebfc2843e5ca25b995ec616bc1e214df14cdbb2d8 6ede2050ccc3ce325d911fc26311bc74f9f09f51096acb61ffff4b751705e68f 527e46d547a1337da68feb3065d017f1fc3d37fea430714287ac3bf13de5e43f 8208fe6fa4af1af795df8b07f36dcb08d87f9b5fdbb06059b6ccebb6cea006ce 41cc08d2fa9ca82df528502af00d1d8ab83840d3961cce3a000909322c393b15 c04393bc4ae049524110432433f0290f23a88dc4b0bed266bd3c139180f4571b e41fde50f6a402f874e366619f17cf3af987c3641720207c0e48b30f3321d462 d680c870d7b43c6ae308b818dd6d14b29758aec61195bbd3e354221c897f5458 352582605e62e595ee051a22e47c7f10c86a0ea59896c907afd853c418893bcf 39b1b721c361d5cc7d3455cfeb782aec7f122348a62caa0d67f4fdbc98665503 66e0b54e8d0d0b0b61a6641685c1f3acbe72bc1e55254d88a2cbcf50399fad68 c3afda12547982d6afa86121d0eae5a549d22192469079100f272ab6d8831338 3d8021bf75cd1a3b64192e532e21744cca23a32a7a50b459b635d8cfdd6e28be 176079f943ba5100d89a6f665864407996e7ea1d554cfa79322d38300fbf3cb6 735b236716beb3d302ad5518901b9a523d51fac099752fa0c1714fa342ecc944 d6d69f6f715109ba6b81cc303efda0abd38a312f526b49bc058ccc44d076be32 1e2dd2d69008482d8e7a02b3d026e549d61455b1add013bc88e131dbae129b16 e02a717aae13cc6ec9a9ae8bfeb952c42d8fc709233cfd1ef7ccbc1bbf912bbf 009b16fd9effa67a4c4c8eda2b15fcc67adfb5aec379d592c6b80c63d3750a9f 40cba48b6fe177037c2d2f652e35278d5aa779ec0e37a01a3338154b0109c7d5 fa94d28721ee024c3920937cc597b6e440056d873da9183a4121740079e91c52 b87e5b2635dd6e4bbe8d22f83e55a9b088fc79bfc79b3866ce37aa3be95a4783 705a31ef4484a1d621a26ff0b0250ab8f4b4feed12227f709c41b03aef4739a3 5c424ca54db641680633f09638dccf23657a0fc34f09c2ed97b3ff0193c330c4 f00e3bff74143e2eb81b5aa10d4f8e110d6f82c12a6ecd870f786d120a863ce5 2b57a48a8c801c44fb5a193e27b51a3b63ba879a68a603f282ee29bb06dc7b20 b02e81daa731f503076e99d7e975ccd900b8a4f4a20768e03225a74ee7769c6b 2dbc9cba9c5b1c99149fff8f97be8faa42fc16d0be7bc5ac77dcf8886a290e32 0027178fea8c180bdedbfcb2bb7e394185b37a42977afbb70ef8cc3f5b2b1997 c417798c3869d3bef770f05c7eb4e89948bd4c76031dc429d6a52fb199e16b02 829c84deef8ab603af8fd2a06bdd0daeb12820c32345488d074fdf0c8f004879 4be25ba569f1b4aa9c55a3e6dcf232d3b3ce4cf9990a6f8698b24db41ca20eb8 37165ce3e5c4b593456f947b9813754b44e40e8407b81178db50716df2352374 b899ab2a46ba764730d7a822b6e24afd6d6d276c4a73ee66fc39eaaf2ed57b84 fae3ccc4f506532923a8f03d2d227f60d7870ca2c855098aedabd89abfebfb04 108 a39b9cb892025d99ba58a8fad99dedc462ece235b74b8d1f90cad933e8f4260994b6b5146c97887dd30ecf0618d8cdf40f30a05b4043bf5412fc56336e34250f7346743d52ef21865344c531731bb9fa25993567171d56f453edac0a2af25003a08e35eebbd71d6bc371df17ad61918a0ad6ff7586909a4986a818d3adc0a30f478b0f261a7f0276d35a93106a7cabbe33680e71e92f3baa6f8ada72234c860dab3d4f698d02723ed8ddb0bb6dc43da792fdc70802652ac1137bb651879e560b51ea5156cc57a6e8390e1fe5f1ddebb858ac12d67518af739d17bbea1492c80246ebca7dbd471690e9d9eb4f0d7693899923aab1731184e127f819bfe65a6e02c9f8121f44d9dbc902ce64468fbe38cfcd0217f0937aec4307d2641163b98e022f1aff8e5f0f3a9ea9aab568e6bc0a0bc009e7d1a358f6d6c13e50b7a78532037f9958900545259239ed40383365dd0ec19b422a18009f1d6ffc260a129a7b09781f054f4701b19241340cff7033c00605b12e7c831443274dd25c4608e17501731c9001cdc97f378cf9e49c9f064735639e9e7cfea635e953b728f4ad77c20ab387ec5510beedca6fe2ddbd0f830b84ef360b683fc0423c8d660b477977a808ed31c3b980bdea7897c7b53161db0efe895beb770bfa603a9470f8b90169f40e6dd4dd9bb2485e166dd18f5df7d73d938bdcea271321c32660db40053485ba0bd598192e3dd41dee39252cb89c5a3e09e2721d9be1d11b5a1cf63d3cea1eed0088e9664de8f95d2ecb59162473cf3e3a72331038a307f13a563ede0bc13d0e023a680e9111151d5327e902b2ae9349dd1f92aca2ea562a92ac857c6fb875f40624ec08548a7397df8ec1de57daf97037c37aba167f663b81528c91ffbab9ec0922452216c801cda812daac6f354527241d573122cd856fe597d46e8941b23702abf12c53f4bce240553016f1c782cbdf505fe8520b924f6d7574394b3bdc3b0734ca8ddd85183cdfeae42b60b4d8fe54303ee2d369c6d1faf539c194dc2bfe0652ca395f7b8b8d6493d5efa117e990d47368f417cbb219dcbf5e8ded6e252c0bbc4d25485705136d017dcd54da69832a2eeba5c9b95cdd24a2faae543d9b48097bfc26598dc19a44410c9fc550c81475d0aad3dd3b57046a7b8e4a166449360593e29628f0384e5c10f422fb0a49094a7c05da11f1bcd453cf76c92d680ecc01854b091280acfdf0ea45dade294e843afc1eab373c45d44a2a7e8cf50273c008d270fb42a8c85e99cc7c04c631f5f784582b27b0e500bad48f6faf9173caea033ddf2269e6d08b9e228279bf77ce7ee597e838abf73fd5d4f30b67215f9fc406816d76174974ad7d9079cd22ade3b8f8999084d7e5cf088e4f2e699ffbd11704a8f610318e7ca6dcf4caa241a6650f9146d13a7440405682d2422a9c7c03de04f38692e460096e2770c20967b2075b4adba508cd872128acb869d12528b91b0752965cd9a8756c5c994bd2759695eade0745b9fe16bb3470ebe68687a8bd49062a78f0bf36e46c99205374a21cde0fe9c1d1613354546e4a9aa9c0fd9e03b30f8888fd33152a35693d5c8e1a9f87587885058b1c8b02934baba31d06cb62720263bee8d91f98e2cf5daeaa95cf572024ea9ee551083c88bd5dd7cb26b3b52403bd17043722db40515a966dd2bff59698ae17a92b35e12b8df651d6801bd28d0d242718a2915569874fa517abbd2ac08098fa3f17484c146a8b2fd5972ae9e50e7631612788326ebf00a7e523b72e02b570e7b2c1bd01878fd2705760c4b3e902614f0d004943ed7a36d9cf58224e8ac2c3783cd4db22674815af9d0eac2a860f398dbdb8037e3df5c43830998287e5393bac6cbdb852087934725dbabbc931034dde1d50549434ee6b3ef41770f67fba6313b970155a8a8f58c474136e896f005ccfb28127af4ccf76a42190e2f4a30c891bf29a42263f265728b7a6523d6b0f6c7bbdf1a9a3bdf7a3c252c9b591060399aec1598fed9dfdfb6581e37b72990f589162219d1a5d8acf4ea8ff42b168e3919cee23ca365a6f5060c2fabf75240b7dae95b17b4b3dd58e91aa05ebf1da0d99ad5b9813278d3b9e3caf40288daa0d34302f040ac7db9f659e13593661ff7568218f60c26cf00c36b979743ecd2803cbb9bcc007cb4042dba333f23db423c0f31e1f3e5ad34a6041fc4a0b6bb2110c9e76530befb7bce295cd79d3226314fc148aec202da8d7992d6c058f6fc4d90ba411f5ef862e9127ddf48c42c27464d5aac0aaabd127f774f218111a8c308902ca0c0f4f7e2006097bc189fa3b6fcb4b7c75cdb7910c9d72af30cb034a7ed2076b223716865f71d2a54c362ed2086ec2ddbdfe8953467daccbcc9337d9e42c00d63252c8be531070b638b5d2ff2ae2fd307d481d7b679c14bc1f862f26ecb302c56620df8ed8b24c031f8806918b2e5dc83f82f2bc8604d62039219e527b4f0bafea9ab8f880bc36aaff06649fdbe1a84baa029362bf6112d69022c3bb4a40045f2cc48aeb932ea562742eef2ff63a5a8213c365acde7e59db54f48c6dc0c50cbd04c187e68661400719187095e8ebf509c8caf823a9d0cc932fc267025bf4071488645280c04ff840361366fe3110f0a765b03306ca84a51a30cfa0f722fe0b661002a6e210783b9cb4929466ec6f72749137f4b88b6952943228e4ba7e75007e6d472952dc010460c52fd0ef1c5909cd625733f6495fc3c2a99441aad4e90fb78c88850708079e1b8467efe3f38791e3e6e494e7b7d52fb6b0c6f89859e70bec7621d076e3849a527b5faf963f1e19cd3a53e6a5b0f1d03e8ddba6f08cce056f14b474ebef3e7473ebea0b078a4481679669b54da1c15ad1245df986857404a73c2e8ac84408272110efdeca8aa91c394ee911535228eb9d2ebae9cf2baf0ce19baa2b708c009bb8beacc220cd91e2848da73cf6debc8714572e76063b05020f72e4eb4e9a991b8d0a1ecb30955e4342e026910ac49c4885ba75039e01400f5b9e012af76691e0dfd584e14e2f534e209877bebffda30f66948713f2516a0f5cbb65c96db3481cfa7eb35f9170d068d81129a24b82cda3c4c04ce76a3195067bb653df0c40741cdb45b76e33d842061b64c4141416f5af7e78f391fe90e402a864419bb8a128afd13c2eb82b4d303cfe7931e7acbfcb60cd88141c08c2bc047481cd80a1209a2aff8cecc9ce26e1f51509b2af8c54b9f6322553d36317ff0f270c0f4675aed8064f88b5b40636ec95090c32605adb0f1a0d67b951f16f0f06db419d6dcdadfd0284d080623ed5cb70038086dfafe87278683f3b92ffbfc1094567ef31d92ee9ad621052bab4cb36b3e7abc242b844d391aa06832973d5d10b3be7e386257c2d98d5b838af5d8a9decfc07c2b46bdc8beb4d4972f69b8140013a589f32386732af2917d8e367b18f3ddf73722727b72e927ba2fb74c4bd3d0bdb36a7d302f6f2cc1b849d36bc1291c1deb3a68c51fce69f8bc1a87e6192ba01af34971c4c031c662a2c6d4552d1ff849b6abd49d0272dd1367844ad73994203d1946e548e369f6c6f5258a117c8a6ace7cc5c2474ab80b33f6508e16d42b00d571c03b1c0b906c49cd0fd39c155c0f4a0cfae524b0509f03cad4bbc10ebdf08c6822c67eb2249f3a442dbdc2633297b11f2fa073bb076086c0ae5344b40d6093277f11ac09b6775117872f9bd66ea450b148effe47d8a3d4f28f6a252d8860db528078987e3a16da014a935a111c73cfeb23bd53b6740501f862359fd43090bcaa76038c8e479d9e4cb303c4fbcfa06ba26b53a035dae3ae36fa6efa1b8a9005ec1b16f9d3f17dcec0a089ebaf66c41e335bd4469aa3369d95a7277f5388c0d2b28ba53c01d7deb416d950646221f976bb29c3992d0ba9bd4f0267dad66db018da778c841e9ea9c1be1f2639191b9ceaaae935eec6be8ee68219a125c063f026e79123c8982509f46e1d72694f45cc0678ea1fe396db43a715e0f7883cbaa0450af1a7f190fa8844a7ba235dfe4d01b5571c07111cf491d5f2801b83b4e370bc3aab84513bf9cdc84bfa6cdc7af852054ae9fa8b2ef1776729246059e18b902341b373e955ea3127d0d5461895e6f4c7c35cbc86f4921066fa62a59d3f00c0d6129a8b39b45bcd3fa0e4ec29a5f0a2440ae7592518e5464068744e3ef547c0ad2b9ebe218f5f8cebe2ffe16a98c335bf7e09367ce4f7e38f606223d9179280a124af3437f031d41bdb44e70f5a1c8dd14467aa0742ae0a12c55dae83a39a80bb3bb951bbd367bbbfeebf304349dd228844ff9dae04a4e8a359cb9eb89084e0885b1164ba7f5393ae319ef93caee9891ce3f3aec1d5edf2f57315d0f06c8790357ae1b362c443729ea1f990a1549bce040de2c8e94dbb369853a412ef3779403bde7d11335a54ef4f6f25a44ef72f39bbc2d1bd190b1650f9eabe67da372740b2a1538cb8ee4f637786af56d3e1f2998f15f1076b7dee2b55ebce5bd1f026801d4c12c5515a2600b11778af74641174dfddf99c3a0acf1809fd63078dad8d60ff94d6855459e6fb901980164cf0e477a22ce5898a9dfb030998e3895967a5b024f4289c4bdc663c62f00349a22eb9e80586ceda2fd20378082f12d54a92d2301023833fb85903e1e376d45a1df47f94cb33865e23bf120b88faafffa5b76b30332c0b1fcc7a76d735d33104d4ceb00b3ff5390b1283e9f9bfee5300200a4d706bd8e03f46373fb9fff891decfb39d58905f82fc53124275ad2719eb121814402cce6dc9d62af0cd1e78483e4a6dbf72f6aee3da68329210713500853900f0a0660bf007998ceac9cbfaf6cc3c93acbef21ac3e84522b4177262be97f4b7fbe0a4fb4ab9f79f84c214b6e0f306e51abf05763323635ba6c3ad407641d4cc0180dbb6340d0e7c7ffe42f64ddbf87fab6880b044a38a7f0f49ede342fa6622c5e039c127a2a115a35aca8bac60fb060dadfc7f68000582baa638a861e5f7dbac50a5db8503d00cd91662bfb55714701d38d812d88e091c6b63ecbffb866b7e798001f4f3aec6b3713781b99527b36871e5520e87a6c0109e3adf92a7189d301a40a7894cde0b740db61d37e53fab2adb066c00a62500231d2a3a47a6c053da69e0c234847784ddbfc80603b16d1463b7226d3b61f457baf9ea0521b09f92f350f088ad7ba2d43507c70ce1126af4883e7d43876cb701968818c448b4f65f8cc150cdbaaeeb5b3ef7164c6c5954ef8dcd9a553b86aa4c4676685304f2fc45707010a50b2b431c3ca8a390e54aa03c6a593c5def5cc821f1e66a569957f5a68ae390f7bcae0e4037afe17e17a02e19fd80a199b167594c1aa50d0a8447b0318c74806e8f9315b78fdaaf486a03a7edd1efe9c309c63de886afab3accf5e3b5ed03d0d7d8fe554623e2a4df5f22f7ec9bc22f62a0b2a716568749a7ad3e2b7a7c01e05831ab5a8811a11de433b39fefea883eeb995eba8e00ce4ae308c6fff7251f20fbeec3d1a72219b74bfa609fbca57b6af8d553b8e823141e3bccaf76f553e9904402879f25c086fbbde800f4477b8501312b3c7c8b9581fd805508dd6ebe4ac00dea99795b05efc8886a02dc69a5f44bf4ee87a9ac851dd4b572b6aaa5a996c07af9fa35c15ef7e974c7d5f8af3e509a3636f0212e2fd7fc5c08cc91f4a89880984b33e066abc1d5fb6f1f07c5424b782764b3507735cc9d4af89a534a57cda0573fc73b32d1b2d3d1d2f3c072db3b43fae0573b4513f11912411c1d41bdd4b0af0987aeba25237b47c5cc352747906fcfe8ad1a563d6a4026006c5101d1e4c07bd20e360305f569b8b7afa09273c1dc1f8ee570f78d93112c938b7021deb640c86f1dfb9bba6356b8dd01b05bb40a61b29b4579fc92d92143727548f980d05004690d4c4f3542503bf483b122fe98b98a105bb9e09a49756b62dc3ebe029ee0cb83626a67cbe182476f78411e8b20fcdbf4f5756e0e608cc7590e0dac0e17d066237f3845f78fea87a312b6f6b6f518bdbaa014586fab04d0f400241884d880ee215ba9e24b20d6cdec807d21d72ba39308aa2313bce08b0550894de2589330d1f6b31d8dd215cf7682605026da9589b10913996da5ce613de86228dc20f5a03c37c7fb6e87a004b2f619e89b682dd2b0fcddaf4e2b1ddc7374e49ad2236d00300806a887ccbb198f7a5d483fb2fab1e76d5367e4139c0830bcf343c808dd3043560c44abde6517a4d19f2ad0c34a21e4b442a002e3e3de01b1ef2ac5921030d44e36711cdcd8696af838815455e49a852a8e48ce6012abdc89214f555a974013a9e625cd686887a071428caa54f7e41dec52190ab3e76372064ed35982b5a06c562db689e42e295ddad75ef892aa9589ff69f01a537d0cd33c06726e7272f098ee6e3575e6da89287b4e9237b71140a0619643fff27f55c957265ac62dd5303478d594e8639e56e4fe1da783bb733a74f5a27027c85193c0ee2aa5248a5590c96ae810ba38cc3997649dc90be361bdb84e711af41091a9dc8a3536657b71500679c30461f74b4246b0e18d00c0e0526bcef750a3b4fe50c87e6414484c7280c12e3530b236684545f3596c0b32bf83efed4ae47c209f6f83c257c7d787d080a474dbae2c47d543fbb0ceca72dc7cb1eb0271a6b4ab1f38304df705d76f2330e199dab48d4c985de0b3f85de47e09e0dbe95b4e2d1beb1bde359f59253bec50fa4e389b5345611f537aa154140c33f86c306b7250406f57261e194eedc5f2f0c8e3735a618ce013f0158ea52d2aa0415427f83f1e1251882fe6a6bad068cd10c4905eef24a059aff58b51b87f3280417aabcf15c3df498c1587e7d20da0e3e0003528982d52fb5585c5c2ff24c974d5b4c358a3f472c366132aa10c9b342fd0d089bb3ab6c161ad99081d05a84da9654b43bf20f012e4df7a8fe7b352abbbf0834910b6736160ab726a10216d06988610bfe43a7eb48051f348f819730730f0687f9b960b4b956411416c0e97a950fd0b556296520d480c8dd5454fdae9f9804ee643c30d81a88f34a48c1704f4a993e371edcdc194e0b69022d014b87849e06400056cf375c4e9c4c62400653cd27b2f21dd1d2e41ed7f4bbf7ba1455a9c200acdeb86611cea04de14cb3004e53148bc5c9f195e027b2b0e6e5fbeac886ba05d823e57d600e6cefff70d38aaa02e0231326386f910904551535319bd249b508b5efcc7dca76ddf1983457b797a50770dcc3460774e34c590ee373d30e3f6d0b1b4592d5576ff1735ee7af8c7388aa5bd96709350ffba4f25195297a706601067cbfb228af229e363b0883f4cf87da258af323a14e007cd050f82ec0abe8030be3c5ba50c52f7e729aee539a73ae989137caa58fbc900495c0aa5a8a72b3c4044c153c7d659c440d8986bea7c03a62b83006d55ace4ea136dda030cbb94f7308c02aa29d52a558f3e798fd45c6c1a9ccf731382afd37ff62dec5066c01535b0683c1fa1afbf94a0dee0bb0984afa3ce320d1eb345efb9eb30392675e93ed3004fe53085482ef7a0ae2244d9bc7529e98204e03ed3474fc7a5f8533055e303c02f34ca8f726b18295a1c077d1831e51ac0b20ef7cffbc5b84f3d5a3f6c58ae7033924258dd46d6c86e469006076c5c100407c80efa4ad693bc76fc17b44ad620dbdf240c5a2d9be42a01b08ba23a6aea28bce991cfe4638496f46b2f24527bb01f51054cb47037e7cb01838eb585cdf4edfc79504de2a7dd98ee7dae95071300be73430d5b23cf0bd463aba726e2c05919e8fb8b736063164fde649fe5a5e1c0d2faa33c957418c9e6cd229bbdc581f149c5a48e53b904abebfdabfbe35d4e9017f2af353a51b6b9694ead1dce4e0050c871f22183281e9508a1633b873ba4802e6cb728d118264d848fedf0b6e0fd32cef9f4c9dbcff7e46f7cc1477419d8a0ec097a9e2aff6586a7eb9704d931de04b4f19fb2ee125071357cdd18b97ac3c0badac0b1f35dc882ac42cb94ce34725d27af65876e042d9e25a1cfcff0479540fa9fd42b736a94371fdf4058cf5425108f5e3e4c3fc14f28a0fecff1608367000907aba24099bb53285d235bd7dd24ee976904dd301733fcbc0a0f9dc4db79607dbaf2a06f9a48248cd59ef6a29bf829ce8eb5e58fe78da2cdb8f00ab96def10d95e38e3b5a105ef1d4092329d7d56cf6c16a25fc3644aa72e52fab26ba6ccb0daff69b73eeb0a3b1f315b52961e865b9be70ff95c38a18d73dc86b6771dfca029132a46cda910a4826a3142e72a1aecae7646d241139c2ae99c63a6f094b72003dc42510ef14d0c3c0c1dfa901b9e6871d97bafaebfaf529be2308f3e36cb105575f85c40eec91f18823b92e5079d3ec5c15a8fc7179ef282c6cec79fb9c8c0eae6a5ff61aec22889b283ce3372e73509dbf156db41987cba704e4ea30de8f08897e76171433589b58d35a74d608e2ed4e458052df033ccabc53cbc99842600d063d4d774cc494c204e570eccac5706a05ea33960a8f6fdcb98f4fac9cd16e0cff82ce221fa5f2cdb6ed8b1b368cdffd6632b612d4e6361cd3109d84be576f08d5fe4f350fde798530a61dd297d75422694f0045f6eb4c6807cce283de4c8a04fa065735846b93cb6cd220609783a15adb964bf7dd6c8aab1add54ba4deaa40bc5092e22ab8d0586c5e2482917370bb1106094dc3a21c2df7357f45c4a06a305cc9ca5641760cbf7b7320d3d353362c71b05f171f510cc3235862be20f19110196e97a157077da3c35d25ac91ff452d9855874b4c7694189e6765e70017dd40f9d86592dd045301c51fabcad41d3c0b43ce137092ae87eb7edb32c9e8542be01a2df9b2d0a3bd8610d75eb90adddcdcba1b6f4450fc149dc01ce57cdcd00e8026e8f019fbb00badac170329fe83306ac7d244963ff31bd2b7169436c384a960b2475e50f2678ffe9e0143374f8e29b2d9475e25c638301fc2daa72a5e506210660eb7e094078aec0b907a4e8be2b84b4a51c1357593bf7cc94c24de5b21b020aee576c484d1d340d0c7dc94bf02ee222397b9e933be7bcbfbd9227b3fea6820244e5f6918868486a070eaad080ff566c18fd7c40de6e15cfabacaf2f32977d06ded6acf6deb45b1c8837c42e0872b2393027a3fc8df8f98f3ec3af145f185502cd807d306adae9f1005e441b80bd2b5afb8a5536397e2ec269ffc9646889df00f759dd4dfc3335ba9f866f118c4c60af808bb6ecc6fd03ee05efbfd5a867b90573ea38606265a5c902b961ebe0cdbe99d8c22c014d8a641a4b8b807eb7cede0bd7bcd41af4ac0e3fc4925ef0aa7a34181ec477d55ceb294d91e647cd35f0e0038419102bb6b448c89e66512e700a1a979876f65b5c409a97878f4f54d6f8490fdf211c368cc4d27d905f80926e52afd618beeffd666e8b82ba957a0745c95b02939ef192eaf2e1462a8f785d7978945f0b6b66792fd1c7104339a044ae94ef055cf84f84b4f2d2ac397550122800ebd533b904b861587fdd7a7ed21ba1ce950185031d63371483c4a3b725f685a004c6c50d248a70c84aad172adeb42312da01b9f19df0fff7071223422e86494cf0d3c6b32dfbdbe9b5e44342d1b1a05af70ddf792526495e999f6830d95f5fb1c042e6c3ab8f3281e46e94635846f5fd1c03714448126c5096a9b0a16b35c2999d0e35bf1aad48428967669160654038fe0b50972a6e4f9327950ed21c6daaa3163695c909f83faa4c72766f10d5e89c0d09ab5b2499169df84ad0c324df131c59446ede25708ee726cccd0a8cae3eceb40c04d752c84c98ba13ba594a12d227045ab26f9a7bdbdf10c2435f81276ac7a0046ad94ea0f676ad968077e30950d4d67b88ce7232236b4a5817191623cff2080f85d72b67cd24a5b1f1dcf7b201ea7e7737587f67266a2f224e50e40d169aa10b055f7bb7b0c742b07fe62b1d72da02e667fa5ae60bc27a5c93ff4a7fac1ee00137c17561e4c1741c7403b5352999d560737003c3c6d7529957bd669126ea950e0021bf7b8396f7bc845ea2c88d41503bb8e5a73550ce30359c6c1208fcfca40e9e53b8bcec5912304979ddc9a4483a0393941c3324b01bcb175bf9567fc59c0ddc66d9b9c3eaf9615d0160428a6151320675e104b047be7e4d20739758704d0a7541543d0f86a5da0e42bb18a7248e3413b0e51a184291b2226de651ecfd420d +generate_ring_signature 869c7247a0b0a1fb90d96cfbb2e3d17682be83f64ea5718718768a7ff74bdd41 cd4c787f13645ac936e8e981b3c725d1bf042959eab770f7c28179f6f37f959a 50 64a0db89add71f4dacf4b369f61a58504533ede4d3740cecca23b82f3a75f4ef b37094f708fb5ffce04e866a09c3aa4ae4e9693abdde06a7b22dff21330d2865 e80522d9eaf7c40d7ebd82c81d893dae50ccc030c67eae697aa0f7df696b87d3 603bf0a35d784c9788ce83017240d87603463c6c41f61896f24ff3a54e5183bd 2879ca5cbcd35d10079dd6b0875cec28e8f177fd12a7e89a597d4041c60b28e6 8f16a24090b39a5295926e9f7e1e73b85fd8f12f96e58862d5a70b5b8067fa74 af4aed48e14d50dd553871225b4cb21dc488041ec203ae1f4140c3a46595ba69 91619b1d38238a1d1ce66c10b3c474893e0484a3af833c55a346d08df4520b2b 4fcd543fdd76e2a7d5a31e6d0d39ec96e4d70b14ecf3419d8f59f8a87e31a26d 84fb3a395fc0fe8ac6bb8fa697cb62d3d56cbfe568c24ed6f6c973cc674e63e6 2c9215c4af4e6ba4447a7ff5c92670eaca21bcc140aae3ad637a93ffa4e0c707 5384ef060ba220e21f947772eb03716fb79aee73e70298e74003d6150f4fe609 ef2500d30e8ece7ae0a2ca2026d4f176ace683edef6326a16f631e42029de6c2 4702a38b0795b6c71a0b8934c2b43b0420c29436fcdeb1ff04c5d44470e900a7 bcf1c1b26acbb158b8d4444fc9951fb62388c9e2e37cfaba70a676a0166bdb04 32d56ab2539721810ada48acaf38e0d28a2dd6e4eedc2dd9c82cac01786111e5 9c3321f4e62d07e104398e1e405823e9e1ed36032ffb78ead40900ec52779db9 8b732bf86cb9368873cbc81c0390eec33c88cefdc66d4063c54b09ba3aa2e78d 2dc91d83ec7a70f8fa663e49c70102671b43eff575c4ec9dd2ebf3abd7c03d61 f982af7048136c5075ba09d0d82da852eb1ff9879a49967cffdb181dbe08d576 a603ae7cd1acd05ddae21bb0803fd7d4af1a4fd56f995abc3343b3d86827093b 296b597866b55b7eea58f80d58ddaa00b7be7058e07b24563c4732a3de2c23d1 1a0a0ebf748500c4c58716300a78bc636d8616b3c6d4cd9d27224ffc6ffd21a6 2a2ccb830271f5438d1a91b5255ba79c629261eda2dc9237d63963c2650e7038 5644c2851b08f83e350bd96db37015bafa8100cdfaafe8d9576b7e7b5d52f76e a69c652a4d177a681277d5c1b4ad6b9ef22737b1d49789380dc890069aaa3046 3e7facbdea7a85a4b59ff60f20d441179fe72d3e07719597b11f544341fbc223 55358eef614502ef018661f3092b076e66612215a378f67e060fdd3a625bd41a 92db53a847ef3a1e0786d07ec6dff53daf37c2358d1b76174406a75bece6a192 6c248b764ac844f7f20317da6ee0ade57c3addf491a6309e19954df847fe0b57 c2b3c177d65920dc7e16352f4d4a1cfa82da74c45c502ffec6148240b048da0a eae88428fa3f81f6a565c6f71a34a1a282550b543d7af367ae568cde957a5adb 347737d568850ab93cdaba356e2f2c932df6a8b41fdec8b05b9104950766e803 40473c24f38722a5db64db65f713d670ca27db39726f32a728eb93774a89ad9d afb366a426ce9f1906a9d3673aba98e048aac4e4e668dbcb17054378cd42ab44 e9f3ab459f7194bc370c362fd7d8a6a75ca199b9ab4eecab528939d27dedc48a 0d4b4bca9ded630b3612d80d468b504d0c180d0cb434c46ca42c68ee98dc2cbc e1a6e0b00675b3b136853539a4e7f82877bee50b7ecc3fa26b8bef50ea81e5f9 7ffb6985097f8b43c0b1d8e645e7a7a92c44f72d6fea1ac90e429ad8cd6bc389 9cc6611070bee3ad7b9ee4cb7019a55e89981fb33689d4ff7a876d543d0e9112 e7bc5ed886bf0e3d7f6f5c226f8fcabf9d1935e7e2d609932ba263f451cd9d27 1c65db720c1282e65a0e49d0b06ef80fb5bdef56c74df1989eb020a3568bd4ab 7cc29b80758c5528e9534bf02225fe899ce2d91bcda453e046f275cbe0f1f9ae 51094251b02255d1ee35ef29afce78bcd0082a50dc86a164aababac34c46bf55 ff25ef58937eac01fffb1d4c73935ae9852a5612d26c13174f7199bf48a6c25f 69c882e768b9f5ab02710125377cd5f2e137d22b25ab1e8e3ea7421e262391b4 c4fda0318a3ddcfe2017ebf0bd023beac85fe29e49ea175b75846fe20a97d461 49eee66a75a60155e24d1337eac7f8110bcd122a9a25309383135d43b37a80ee df2a2fd55bdf1941f0a7d40293da98f050c253827d420f6e020d11e81613a0a3 d56f97969fd7002aa6a3b9448bcb83354ddc3cf148c4e9a5e36c050fbaa7df42 b61e797b24014d16d9cab92b84a7a057a1a05054a1f1de374cd61c040fcc7c00 2 b854df5273bfebbece2e31741311008c3540af4c1e157d0a09d65ae3ee6e5c06436b2d82e8c8a348bec14601db213fa843c43887576b1655b8f522c326f9a60c1feeea5cbd128c8397cdf3c63f97b843a02dd368de1f919a6c7534d0d21bbe075099dff3aa75ec9474646a6883da266a550fa9b439cf4d1b9313ed3242875407f643f5f1c5b008d8df2a3801a32accbdb4609e9f3b8c887483efd232ae14050c4f58302ebe83d8821b8773e6faabcb4ae101ae948f0313cef255ccfae084800de04b85325eaa695071cad0b0c8b685e053a06edd4ed8d82e921b6a6e6ae92909404f36a771f95ea10e3910e5da87a79bdd09f68c42f2ab581a79114023d64908d3e2f95c81ecff1bec33dbc54adb8ab66c1bf1ad9b4def708d077623462c5d0f56298adaeb0066c19cdd2ab8a3c12a01791091690e50d0a39087ee09856bfb066726258fcfe26d43046fcaddac4880ed85d2e4e125586474c43553620b37d10a3e81f6137a17356ca613263a387c1060a855f175678ced2e1b2f0487e9d4a00300aade217e03de51a135f0f5678f32e0307027a02fc8ad219a29e86c495f0a0a282a92d4cd6358ae229073e6164481451b48fd5820ad04637d48f23d8cb346069af074eb9a672d4fe5177270bb64646dc24c3ec03aca505f8a9cc4bb21e8590cfe66f8f59d313cf86bed554bd8c8b63f06b8e92bea191cc37a8339fccb2e51051632f84c0f9870d2968b9ec55bbf06c566186ab36bc43122340c99d74a07700b512718a6f701f1b172eb6cc44435a2d4120c66282733fa7a22e116391e51ab06ed314b106cac92ac73b7bef6962d156e7f9ec23cd2b241536fd8dcc8138baa0b11e3044a60fc69f10523ffb84ae1a435a583ac3e028c588edbdb75e138f035047a2bf137d9b47cb8105d3423fe4ff2e2fa96a1b2ce7f2604cd650b4f3b658e016271ffb7e88d9ec23873c16fcc1d92798235496f182b2745bd4df1028074840e06a1d443907f515d834f79fab8082273ffca1c8703706ec07302d4a1dc5b6901e19b2623e26ccc30b4ab5109c817f7163a00e2bc6f6e5955e75316410c195f01422d8dec1a27c664cce707530e85dc1c302e3922cf4ffa82ec1a80151a44ca09c3579ef56cb563c8329bcc739005bd7d057a2e6ffef889e543d6d20c0c69ba05a907c05bd2ef258f61649daec2202b531104fb1193431d8f12b6464ac5cf520b809f0b496e0e34308a65a25585cd8d4eeb6dcb7d73b3389464862fc9164f48092f74b81a2c99a57f306055f8bb8338b82627b19c35877cd578ffa6447564c704912b88dc9be5fd8703cf96ff3643c7c8334d61f89077f62eae126d8a69c0b4065d4a00809e6be1aecbee4fb53b1f756c1c79feb82bf44de8c833c8931aca6e01298cef11bd378804830e958ceca623ad97172d5f2ca7b5353eeb6c2d9cff0f067d1c7c29b5fbc0fba774220384a7009e7f8ca0e8db8b7bf73992afedb534080ea6df6a7b0d1e71145a7117a5e16f607d132f2c2512c3436580e809c45f797b0f585409a4a5b5026b901067c2d130d66bbcef1bd85b66576315cb870751aa870ab65d3a9e6ec44fbde5ff8bd520b9b29cc572e413a428f9005cad9df8183f0601f23537d7c3054c51e4a95cab5acc4f1aec7db14b6febb75e3266367d5d596e0dea036c798a978bf880423093d4929c6d4b244a9874b5cd3500095b319b8e0107b33b4d9c60dccd53ff651565a687a88dd5f960efdcf29467b5d04e321c2773083602313ddff9a642f4ffe8f147d99a9eee00aa5ebd66764d172f2ba22c038c08538ff944d6ab9d4796e5479b0b3339c5c78690f8db36aaaf75eceea4367ddb0b11bdecdd5788bbee3e4363bada8ad8830d75d2465b4070f4312716c5ace376096929f6ca8867557eb0ce0f9012e6aa397be54579234001dc96fc2c7cd03250098ff6e080b7228981376b93b6af674bfe0d39a6c9df262a2a80f73b9531430301d262e638a96aa55fff4a00f5ceaac44159ed85ed37e475c524406dd096643d0735797f98e52db02565ba93388fd3e75c38b0c4d3276327dbbb12a51c1569c601723682178103a17295b81a060c099371a837569f362984a722e7a4914fe046050f9a015a2f91a2fbd6a0a76ae4c1b072ad3fb9a6122d25375033ada429b4fc0f0d38870229bcbdf4f94adad372b332ce787a3ceae44e84514bf26c6eaad4ca0437ad225442c2974481568fbd539f9059be80fbe69fad56263f0f427689f6910828e4679dd1ca75430b69897fa58065811e1e6b1b2a89ed7226b17f31a7a59a091aab5c83e2fdeb2c8b7bc52d84af68d4e3d57e0e540d38a3751ecc3cf5111e0c24e6744b8b6e2b61da27d8d2c40d90e01740a900df55f5f5dd2d21f1e2d4210d097c362d0d66f38894b4997df63b06d41c8094231167439afed0c23349b8b0095573ba5c72bc1238ff91d11a7fbd1bf5f03d64b72f7ec8e8d0330cdee9ecd5026f23829ff8175b81f03066bb2b7589c37b88230e7e18840504bfee024148d10d0d3e246b90670c04aa1981bac67ccf1c0e81debb920c53e3e4bc31d56da46c015c4039eea9c3bd4e9653ae03c2e634490f2693dde90aa6a38525eade34478508e4d8a6062c07b2020eaf40b43c8216f4d0730388879518c31f72bfed2ab8e105fb78a06f9ad875e380f32ac2ee353cd5a293047d14272c9c8d046bb297be680286075eab2fe1a5ef43ef61e338bb7b487a788c609e8b74cedf4fe3eee9c2650444887aebff0c33649a23f25281f7393eeb6e4ffd211edb57a0a6a0d2ccc2eb007dc237cbec050a28d1ea6a4badb286e1a6fd8fa2d9726194307bcae809bb6d09509427ad297934ead29d912b86d7e47f52b975957dec8a3aec22018f93248e0771094b471b590fc011cfc6681d8d5bb21ce58f87e355cf0fdc27443b05ad60084c7fce3e3d401865e252df9725c49875cf206b9c15f91269bc68ba2b9095e80beadf571b5e016cd8ebd2aaddd3bf3cd512787a6fde7e3d89033cc1f30e5bf20718f0f1a0e4a585950a3c32088672bae07dc8e06c419c553123344cfb08fac30049985686c20c612c7348e2f37fb9356d292337d90f0c9a495ad8b447e031fc040dfcb46edf034cdcd7f7317b2625396dcf70f384ab683c8480a6254308fdd307c8c962a133d2b685fcb688d7e0d6764f4efe37b1e3dd6f6aed7bc247e1a0060c061811214a3d0dcc33e7a9519ac4d7f1ddb1334c1bd28307eafea3e3131b6f07caad0dfd03da229cabf9a0c504edda6d3bce3ef3e754d5d4cf078c9361a5fc03d8a1b49ef905630712609ecde497c1b54e008b3b38527177df60d7e0ce88ae0287b34dfffb0daa0f9ff632c779dbabd8edda87d779ca6c11296c168f6cd49105ab86b375369ce70a82a0c1e65338584db8d2c2f007dd9e28ff816c95d9c4c10bc439acad9a05fc90a4190a10b1a3400f97b1c34f2bf6630bf279183567ab24018d461817de728b1b6727fd62228fe3702e67310bf6f820483b24a43dcaf226005761495e717fe30e5292f051f82281a9e85a8cfa0d9a52680a056ffb53c53f049e47e80d9aab19243c2ea1d8cba4874d073dc8d1adcad742671b68802ff0c9051a7da1db4830a5b14899fe12c8337f1a00f88796541e37a85c7a8b6ae8cd2c0db4064eab0f390d15608123eecf7cb49292021d53a28016238073073f743de50e6e01f71dc1c36244724857c176c7340a8907a195b2b19f76a0287d42a9b5b407b570bf698b40386c641bae6f0c2cc72afcf7706fe65d9b78192fef1da8ee690f93ec8ecf413c06bead493b824e90dc8c5c958f888c11a6873764751d17f9e301998d47b7e4d1e00ad610eb772150bb77fb498ef0f3a844c7d45187e9d04db3075e6b9ac21daef6e3df7314c6627cf646c71c85b05dc0a0ec2ff4a0ef58331001d32d3124beeb1212538450443bc5ae823506d4077eabaafa414509e302e03b08335ea3964782b982c078dac2c271edca8cb798a68b974962753935a822c72307774fbca91cd19be4bd8b5ab1a81eb007c2498db6ca94e74ed61b1bb5f556cb0419c28bc88e8be8b8877745e4d050794b2b1a5c549585afb5e4022872b614c8084473c65e0e2d4e18e4f790c556976f79dd5c2ffedc5d3a467c5f8e4dccddd6038f05ebfd7cace6e82f9d069d74a04b67e083e58b1514cbe89bf5cac23593590ab744712f1c80ed6499e25ae85dc636c965872c7441b96af6b9e1766a4502d60436adcae780f3ee4d097c6b26591c62c10eecf62dcfe8b9faed1cc3436181f9096fd3e6546b8a683d04ae9d35a5246e26c0c3d67a85523552ac135e8fd74c4e08916a7051715b4a6d8bab0c88cafb55ccfd4d77684292993ef1a62ddf3ef00f0ff1f3ca2be5631066766d923ea5d45c04513b645471d2c698fee5c11449cdad0420191d2f6f7130db95889daeefff4b2307f441ce2e1d2c7b9dffe373a6db4e058796df6a4a485647a8f9f660adb6f35a9298c0f427f3cf212757ab50a2d47000 +generate_ring_signature 1d0b119ca5e69bfccdec2771ff272dc2c8638c187733438939525ccb8005f4ff 8645d7285139bdfe550241936b2fc8a26757ffcaa7bac9084ff8a85cebbd1e89 3 8a075c4f74b5520b511ee1a487cb31c4cc93befa5af0887414efe0115fbac0cd 5407168cd81fe9cba6a34e7e30cbca17f448f2226131ea57acd74420675830fc 2ffe6b476ff37d8d6487cc1c7e5e89ba0295a5d0128e6ecf8ae64165d6d14d84 ffacc2cc992ccbdc7cb0cc1a695ca31a92f6d6a2563b03349688a63a86f8ed01 0 4aa5ec4893a41bd3429a0edeed4721d10dafd3f1b2fe3fd144a2691a9a16e205f3b6b3f8a988790fda53f53cfeea236dadc61047372def8407f7fbef7c5fd206db64c092b429a9921c904aee753ad3fcc1c5779c2117fda0ed10a6ebf77d310470bcfcf55f6f2d3304b91f29d99099e284c8d1f5032efa5456af2bbc4304430114ef8c089866f7125f66b759d1059ca1c98ce2ae4d90e68d7af18d7931a5ee0ab40c025e00fd8c2bd450aaed9d6d16ed08c4e499ebcae6a45cebfba66656260c +generate_ring_signature dc0159dbf72ca06954ef706baaf14f7c84f6d6e96aa47cb0fc100a73f21506ca b0dce2128797d8cd8ea9c4dc13be5ffa76e3c68278d8721835f7bea75d42621e 155 55bdfd958e609cf23a67c353b313cbbfef67c4b9f888aa9ff96473fc9c841e61 5cc2a7c274ee46db7d80a45e09bcb4701ca0b6c651a3f71698df8681f565da1b e1ec3ba3b9a5c1f93867401ad6f4b8f45a81fc04b057bf48198f93507db0c45e d26059d3c9f334e298bc08d99e64060f1c68e8cb0db08904f3f2e989c8eb0da2 06ff2aa52649f6205f044d65d691168f3bf22da79ae46a294a362c23fd674cad 376f687a817012087ac04320874b9e169806b3a384ecbab35aa7caabe11af3af e6f0454df292cffbefc002e20637b78ae2c52993e2f1d7801fefe9894babaf97 965c5c80ee12cdc2b0ef724fea7b86aba5ffa1ef7a8d371f634177fe0c77ed69 5cf4164fd1d89da37159323130a12b0dc4d22b6743f7a14f37d85675cb7711df f43d9857a3c0ced069e7e66e99416b0f4969010ae1ec4f90568fd9bf1ab42d27 15a59c1dd5958f6b8b48471d7fd113b4a761808409dd5606b5eedbb3fce5749d 4e8c6e51b88c5df9d2f41a66e80a5b443be79333309de74e36d26867c5260f29 84d85995ab7fba0a34a50d10c6c9ba52922b7f5f1541e7740dfa7fceba592b3b 04409b6c2ee649e8836796a2d0413dae06f8f41cc352fb00bfb108a6f608ca32 9c207be7f2a862125bc38aba0e04e98d16d0d2ff2d36461c0480716a95ec0a36 39fce2d41d67bf1f182d1ccf23e140ceaba4322266b10ca3c950055b6be5b023 fef4c2cf18221ce0ff9810a1928d9168e1b88db9f42f668d74c2876286c848e3 d1ebeccee551e9c25aaf9c6f1f6334fb5471e5e94f46bbb1d80729919c5691e6 9e7ab6f141bd4ecaa6d7885514127168338ff935f1ffc56eaad972e19b807aff 4aa10541e1a9124fcac6896b065066ed0d73150c9e9455f914e4d96ec07adf5e 32ca3c20ab39e82821420d6e2e37cb9a72f19d1df5908fa1fedb306b16c3273e 8172aa6cda51efb16683412242fc3b90a3a37c9381e4109c1f00521546fced9d 2f9f8b8f71b8e1d5cb244c0f0a14caea1c5d77b13ceb6a2ebf2b600ca2d236e6 719b3d1256ab1d9f286f9cb4bac235da7131be9dd47388b91c6eac952c53312a 14a64d08f79f8b03c321fdc291fc633fbeac766125cd5bb1293028bb7e86e4cc cdb1c8bbcea2d12159ca80158b1d8944d839d82f94e1c0c3ec84c71d00a67659 aface084d21571606d8b913198308fc78bbfe4fef087b2cfdceb8c335d756d43 66502a8fcec2e95a45c10fd5288ecd64e1f880a6b7757a3921e14f432ece527a 63b5a9153f88dc28a9dd325a81edb0bb2fd73fa2ad2aca31839f9350412b357d 7c377465d285f2f6d31834ff61cb8a9cd5ab2fe99b4ff9c330e0d59d305b21a1 ced17c63ce3dba87598c1adc062f3a8af81d4937447ff8991f7c780bfd9d9631 d33c03c444dad20c86164d634bcc2689be5724091a95c006723756544a5f88b3 42c6f935724c8f7f3f5c53645123165daf862cb05366a895eca0a7656cebe76c 4950dc9ec99c46c3d605d97f96407e9de344b0bfb7b19f976c9c84a18b3d26ab c813aa66c11bb66586ad3af0cadcfd7f4678f9ce71478135b8cfaf3f7fccff8a 91f5b936445454387f607698a4b2ca38191ae36dc6fd0ab0580ca4b80b058d8f 9b8741d58259a1f279a397da968d1df565d8e133ee7c69e1ab72448660625adb 4a7c1752977c764cd5ac091751bff8dcd41500f38b7c3baf458963f2133defc3 2d2b4a25e1f4a864c3f45e0bb5808484bb51d2e35b9180e3565dd2945c13a9ed aaa64e83a28293a0f8759ed4cac37082552d3935c827293da30bbb5d3403613d 66452a289e79a5361719e68d911741f20163ef4896f0257585bb9c832069c5c3 a14e0b54b0e02a7e81fa314190c273653926c342c819d74e9aa461300f238855 1c1322db318ccf855d7a5ce5a6aed6bface24d4086cc7b26ac67733be55570ca e10ddca61c7790d94984c7ed066543eb76623923557c610ec2f8de6f79f635b2 7588b14ea4f0e4a215d9a982eda345bdbc6aa4cfd04511867c40f9011556434d 409d5ba7c80a7e1c29df9bf923ce011ef47e284c0bc462533245be82213b4b7a d5e6a2f9f50e6e03b514ed898ac28f24fbec78eefbb3dbe0a6a00627f72aaf4a bf34dbc0ff7eb6c8a74f111324fd8eeb2be93a93c8975635ee06b526ff1b3ad2 ebf00d242b0667a638b7c93c397e45f92d1e8acf73bb2abf73b03490b3b6c3ae f3688388ec40d6680cdee04c6f11d79bcf9eb4e5ac2ae253b4ba852826b76bf9 0ee543f8b280ba4381bca59e1fcfff6dd425851c4119e251a7732c99b8d2f168 d872db69074557c2102d81103935a90bdea94ed0b0200c3ab0847b526e29b4f7 d4db1b4947a3e86d998ff66601af2f2da8cc84e9d724441b464143b171d7c89e 746e4cd0a3813757876862505807b4b78270c0a170b888a3bb9e3e60c0101908 8d3606dc0218adf7c809f52dc2c19c9aea02db66e95b21a4dffe48680bad98c7 721174c2d937c62637e492c02253ee2a7defa7d4a3b6d5bd47bc6f0addf40ef6 97bc500b936ed73732b36010e062fbdf149dc2a5cce48c1a648828156c956c28 f3422baed25571091c63261b7f854a389392cfee89a8aafe67b0986c77f933f9 3224b4758162f6a3ad94d76f096029d96b8542e883a50f48d189fc9ddd927efa 8634ba86f40b66d5e0ae98089bec9c49bf7ed348c857e9a8ae6af9fc1a1f1f66 afdc97548124eefa649ccc49f8c4895e1503a0b86e4732dfe073b18a14c6e0bc 5290911f8661f9a415535158f2effbe89a9178c4194c922b660645825e234584 a79798f044fe2f369b259d2b59c949a0cff4c4eb54b5464a1c07862032180273 36f35784685306db02646934f6ee0734226dd836ee407bc83ca232539af2a10b 1a689224e864e3777accd246ff0ec5ed4f78b44c7af8f551bc9b411f8614693e 37d040f52915b13ecff79dd88fb88f109f091a5716ec289f72adbcd9d1877221 3f8d7c20a19fc64d9ca3b1eff52f6c4c062d6ad85e9b242b3d16618cc5c359b7 8fb07bf7455db11b402bf9548947b6af97803a02577e538e9ad452fe51960335 e457e7a04242381202e5ec2f466205ab1cc3fe0fa20a97eed9cb1afa6f04664c b8b4761d93cb707c2e0fc386991643d770ce3309203f1b0c8f182ef797fcbd3a 4a5c2e5aec7e342cb8e6cf16f17678553cfd06e5359be5994ee6e50bf572fb22 5c16c16457e9fee9a62c4ba6b6dd7e46aa1f3f85d68f08629ec7a96b85118869 b7299ed0fb01db3149fcf30dd1d4ba8c99b9384bed66d70c3580e042a6f7833c e6c61646a287ad0a53d582ffb300a86755164342b83f3a6e5fddd5471068658b 21e7442030bf8d818a79e5674f1b5798a576fbb1ef84472add4689990f62e8a4 7755f1fcb777b9182eebd528bf3f60f6a2da148514e90695acf897196bd897ac 8f50370b4a8db13017296ae820c4b728f46e8244824e590293dfd9db7888ba5c 2fa322e48e87ea8a9ce4f70ac45fec4bad47ed617817a39c6ebb38bf007f3802 add067ff6442a2d93b12b6de049b5fa78541e45dd0a0f74c2223ad7c61a7198e 46554f1886d0ba1aa9e264a5f29b08c957b5c2896974664b222e60b6e47e1b0d 4e0a19f0b65791c9b0b510e49c670cc06bbb324d67f657a4028ed9f03784d8d8 386291c7799ecd8cdcef7fe30f6021f42ea2918b06be3fe8df2f47a0fcf2d616 2f4a5d75e0868c2b8e523ae394049ed0ca390bc64f641ba203b9b4dbc8d41d2f 841f17f2584641774eb003c751d0ccae29152dbbadbba8eb066a2ded5dabfe98 d96e7157019eb21d9ca9ddad08a2c2cfdd752fa10cf6aaf11089c180f6dfb13e 7360d3476419e86cb99abd0a33264327b0b5171557d812bdcf6800ea0bab7131 463083e7e2a8fd2d956876dec974816bde097827365b94a1494d5f7169f69686 44e8016a6c0e2e21241c67bcc8b844793daea164b5e2a14baea66c3c7a3cc773 22764f41e82557790205bc987553010dfc5bda9d8b9cab62def4c881e4ffabd9 9a52e8f5b5cf1bcde87297728a79c4154c272fab3a31505e9de06cc22db12b20 45ecf071a3f952a3886e388374de59aaca3109b8a9424b7cc4e9844de93f3c38 04bfda9f253526a91d0a4eaae8e8449bdfa9d200346509ad9fdcc90c9bf72046 baec1c08bd22e1d1803758f86de1dab9d472cefa662c4db5da8bdc3efbc19b34 efc658768831e5dd37bc425c2d958f14c088f72d86651f63099b6a59fc956aad 8bb4c7e070bdbdcf0bc3ccb85a7f636c40f6855886fb9e5afe3663c78ad39122 a4a4c96cd0361853a0f6e464a2ef681a05d30501c71191f74883a7071b38ca74 1f0eaf33404e3d3acd817f9f8a49200f6610b730b03dcad2c75829a92b67d8f2 693c7c1e884e24acb6ea9f79df9a4d82343d2f00a36617e80b2341f4a7061e9c 57e1c0ebdb580b1ef2eefe50d96d81d9182b45cec86b8c2bd8bc70d8094c3a64 10b2ab4153737474de7bc454ad5f247fd398761bb29f665cba97dd2a446f7397 e80e6ca216d809401268bea920b105ae5bdd440577b7a9ae2981b3d75b39bb55 967fcc98c8830d8374f52e19229f50d39ae130caf369227941c97f432d6a381f 6276cd137870acf4b11d90c2aef851894fd6aa8d8ef3a2e6a7464e0daed09c87 43a43751bf077cba57e404a62eff1082d66777f1e304f0251dc884a472d65730 199e70fda56e43e548af5bff218590ec49467db20d932ba7b93490df9bb79ed2 75621e025d68155e2501289bf8caba1ee9b89e6389fd2ee595e72baf5ea3b34d 6b7e8ef6c4575bac539bc5fb12d6428cec7286e03ffe1f00d4996ac4214051df 3a28bc22d5954f34ab59bca16162feecad41b3a25ed1008b21729917e9e4dc4a 8996cb1d13337dcffb5f57547eb7c6be1cc2af29d9bce46c4dac048159a15920 7b0eb9a962b2673a4c3385f17fdf9059f202421a36ec76a2190eec8ac1680d1e 6da2909d4ba18f512cbc36a2221a2e16d99a78c6e526e802fd9fbd38b8608ddd d989d9c710f84bd67771a252197af590b63b424ad1c89d623181ee4dbcc2acb2 5f03667eafa7c0eee3c741d4aaea93246d9085248002cf92b4fbf664da891e5a 4e885b308279d51ff6ba3294f150cfa79de993e4b4fe240e0383926eb1b72a7e 48310fea7a0b977e646e440ccfd050663c1567bbcf716c18738631be861bd928 2d8e36d9c5d5bf1a121d9b86dfd22dd40df765a8640383aa3a98e86815f6b202 c2ba1b76ef0a6ffad1f816d5a195b4290ea5bc5a195c21b99a2205a0fb0fe19f 9264af1aff612cf6c5423fba55d78357d35f1507961a1e5777cf9324496ce5e2 ce48bc8a5d073ebbb81bf4e4a31d30b43869d47aead425642fd590c2c6b71ad9 415ba1f571226cc741585b624b408f244b226a3ef68112a787b7f4ab117b7070 02690c0871b65ed57af53dfe9ad9af13fffa80e17d559e3ad8bcebec4e3244ae 28a00ccffd9aa89903398ac9b36e8bf24e77b25000ec0cc05435f70caec00f5f d71cf74bd0d2adb4721b4c2a2601109e25b41290716c2dbd20cd71416560042d e497022d82a79c271eda123b3f2cb12d7e050eec55c7ba3ca5c18f90456df49e 5593d828a1c1ad5fffaff2fc0e4b82c488a4a4fcdee2bee513fa7f2b8ca21402 ba7deb31a63aa2c63a9b6de0d018061bfd5bd1305de6cf9be496f2d0b38f0f1d 2fdf34d6c3f7b14cbf3d6dc057f68f9238a01843bd79f65c343f8888c57e032f 0ec1bebdae697a17fd38bd3c2b1d466c6a2cd62b221663dc14450fa9645f8f0d 64b8735f9e0d05b013bd5649e8df55455cf90d0a32e01a5bd47ce4bae2e2663e d3210f6fb94b6157defd835fbf6e24f335a53470ccc876c8f24b9cabc4bdc689 854d5cc84664a763d4ba94fab9f6826e4b6795b2c4c4644d1272fafcfb38dfe2 ef8341e4a2cffca78f4f85382c1bb11231f98e313e8b4b158f1f3858e0f1bd89 721bcfd1800fb89d20114f080b739a7f577466853064735708dca02bce6c08bc 2b21a17310ff3da504aba575ec91cbeabfe7cc7ddba637140f2b7ea4a087e5d2 c9002d13dbd6d60da440acd4e2fa2a96a51cb18380b2304c45f8d55d9e3a23c1 9c5743359406a6ff0228502eea24354ea96edfeb39d4149612a9f84320c3752b 4fc972f7883ce8ea63a0579fc59ba3626876688dc96b3b2774202f8c2d514671 8262e5c2fdc9e2d7add1a28b1ab3acdd49dcbbd4ad6b733de427fdca8bbdd9bd bcb2d7f1ab48ae12ac447d9006b14953c7b75a45eba9d7c28f3b596ae67d90d9 26cc343804e2dca0d14ab0d2a2d7d03b794576bf270c1e79b737191a78dc02b8 9acf167693db32fe00a5d4ec090129992b39c269833c6666c1946d8523bb054a 507659e6454ec03bc6228a469717eb91adc0c14f2dafa266c56522e037bd543f 6dc86ba6eb6846cf225842fdfcdbce3b702041a03dede7e5003cc402df63335b 1677fad719b74408ed9e1b6e2f9e5e6f0d5d54f850f8e4a4307c3fd2a5db6d0c b27e2f7563d41739dcc27a208e3c1cc6d7925820e953b36906bd63456c23a4c7 46a4d38ba7ef139dd455ed55953279c6229eb792833d5360a449e84d31ec9d8e 97d72d2c3deeb90f2b7bbea277bb5409dc5e185443550599dee57628b152cbe1 06cbf2f8915bad92aa93e100fca048910bac864de2e67b786924a5dfa47ed2dd 5f01c55600492999cd80dca1fb95bdaccc1ccc4807e274f5e2d76780a62ab9d3 bf3525ee75abe62a109fb35e297a6ae6ab51d430756ca8a44c2aa0dc558623a3 3a78768168d7f695ea5c96485f80efadd5d0434c256732af421ca95921cbee87 1748b5a61482181b63b42252c7663b9c19d3386000b73e440d49a2602801a192 47fd23ed6490a2ca505a01a2a0216e518b5459c621df2fdf9f4090575ecc0819 22cfab5bcf95785b94f17529b1292b25b8ef368d6d3bad17438c9fc4c41709ee 3309c171ccd892d173085fce4bda373b15bbd84719baa5c86b00bcf2c04191f5 6e8a6e2009014f498970673b8c94765896e485f7f5fdc8d36c97e1daf93c6003 77 8d5dbb7a0466e3ec9f214327c2746c946af769bff38da55dd5ed308424691d0c1ffdca53753ddb7be6ea081da0b9596fa8ea4d72ff582b86c3778012f5005207f7f8dd669e9108306d477530008c43cc8cf08c6497210532f729e3f084fae80a8bba574ef14b654f503d66585eb2acf19e2bf1a67fcaa554786452e0bfd75f0d276a0ed021f30fbed1848276149da319ff4e768a0fe08ebb9921af853c8d46010bf6b10c066f6de7081bb5a8b9c4e613a3391abad46bd9cec3317f15c8d12100d4549ae6bf63dbbf3937a6278b31bf80a987a10f7000de1d6181a7a6bc633d01fbae3e7a41b8346027170806b2920c6cd8e36e62eba3103947fd45988ac07d05fcda94ab8708da35c84fa5e4cfeb59be89cb51afb39a3a6ac28af8172a547008076d47811ec74c8627c4d0306ecf1f51ca03229d63f31de267e7ffd299557f027cb8b4b8fced8d08561aafb32970bc3dca5e617a91861d1243ee53e639712b07ac23e107742b7316f184ca6ff68efed2ca5bf76edb065a0080df2443e488350278f065b68920db1efd041dc7663befe068b35da3c3e94e04dbd13d929dafa10fd164072166633844f21423095e878fe5902418c2a2152e8b78a009482492ee0fb521d632ffe50ed732d0005f9d47b57181f7312b1a41b1c877ca02162d66d601aecf6ca866dc9c8f2a4430a9b9bdb740f24014593c53eb244651b0b09c605501e3228f67b83ab6ff8fe70eb5a14b66a44e2ac7180a4976fc37dc8227d20a2007fb88b9ca14c8d298fbe9d7eb42ec2f53f10c8a1c54cbac585d169a11ae73c50899d6db4823745089b89bf31b65a10c597651f8acfda4c2a0b5ab2bb7f554050eb097333ca2830915f8b90673ca90ce52d2c3fa9a8a4d756a2919d3c22a67ee0d0478f2776bfb69796417ece78a52c998492d7832080f5f52ca5691e16b0eb305f0bf5994284bf5a83cdf3f14cbcd4a5ada94b0b2760f3a865ce2e736a527b108e1757d33ed4c7f13fea7e327207bbd437f2f9372f941b53874ee6249c8dd46066659d4b020e1edd486de54d73fe49a1e030d9374f0139a0a544239f5716d7a089b2f72002b6bf05886408c2064f7c9e82bee35e0d2946c91805c26276f01830e000ae9b9532053074c0fa5949c2fce2603b1ad334387f0bc64959c167da5860f1d8fe87a047d577be053459554790e247b46a2e0c537bac03440b9a0b49dd50168654f834fc64604a3fa582198f8cb184a1619c5ce144ce86033b14f520ef30f8e0abff37da82b3cf379a752203ad9485960fe653e5ad4a7dcb835edb09ad10ee4502989de3aef26a3b77340ac584cff691d3628ca727bed46be34d186fa19064aef1892b3eb4e5265ad2c14815dabfca977c57412c2ef14cc22dffb4e975a0654b43d8c4b288d4ab7b7a8766a6d37f5f534dc3c61063bc3d2e01ecb534de806e6bc1e003ae518dca85db46d1f3f5b1a76ac554cadbe80d83ad96a1ef524880baf80746fb9253e0f56c7eac147fa9ebb98c66cf6292806b83d75ea8bba8efc0f3859c2632502cba122db858f09e24d8b2332e0166848be6c09007dbeeea91e08c61cc0482496f4faabb08ee57b1826843f13bc204cd1d5d30fcfee699b0eb50603658426fb52f84b934e900fece01c168a5f0870a1cea2c7387f9ae232ddc40b611915a52f77d95e2eb5892bc2d1646387a1428f36981b44e38987040d0ab10ebb9887b6f0fb63cea6debc60a9c569bd56f38f5f68a57d51adb6fc0aa3a0e700b9051a49969343ae55625fd1be7d46f9c1c399aa82b6c57e8fe808222dff0e01876c2f88fc8cd84670af0ff6a854bec10951a833e98855376bdad2d5224da4044e15879a6fc3c0e3d49c6ca77da12568f78ec40a3dd05e466035dbf987046a03bd1c7973f04d98c8bc232fc8cc5b65583f3944bfbe212dd961caaa0fe48c9d00f0857609e752c014e83a79631e866f86e7796b8b2501f82c81b513a4b59bd10c6f506c4bd45457f45a332132c9faf89ed50bf74faca6f79ce2ccc63d7e528e0703fa736d74f47d96d0166e7bff8736b37c7da7f781232490eb360dacace781017874de714a5862df11a193a8a2397928ad3399a6d812511bcbd579b40871c60af3e87e9fcde3f04031f8ef4e1581b3792c20df1480730f57c9e0b56ab8bea30b6fe472c524ee3096a5ac53830d39659254108d2d31ee7700ce45f8566b394b03269a3fb390646237f7de1d9c6bb9f916a2353608d354d0ca77478f29557bda02fa89154b3184b63fd62d953d52f4e1866b72e38fb1129c5a7ec57532f0be18051861c0b9ca8bbe81d60a3f931b731603a2f2faa76b447ba2502f000c1e11460e8925203532a0b182b2287696a430ab2102e919f6c38142c685207c4d7119d00b6ce2da2cf249fff1298c815fd41768873677207c28e25bc4373f1cc32463c00540812b690dc1fbc38cae1573176b9e08c59c0ff6c4717d459f234ab209b426058b25e7789188f29ef01def5ad20237a3d0d2197da121ed2920b2852288d6bf0ce025da3764337443243d16c5d2dd1a6557df48c4ef8b6ab1a2c215fbd904b308be4ca810aa8a0f9acdd70fb3ffe66d2430d1ee242c4e1fe2c3dd06a7567f930d3119aa0e6843460f72e60c67844a2f5d1e501dea1c9d3892dba51f6bd7b55803560212c751244f0b3f97c2100b93b0f096d0194aa51d2f865179dc00e6a2ae058f9563a46c8ab11dedf879f079f6469056a0f9e193f60f4dc6e803dcc7e97f03d292472a20b336971cc84c0df1ad14384433ec148b0c78e1d349e78cf00e440c9a7afc466cc762d9656973ae25e19886182b5320bc468d71c955ed6013d38c04cca336b21e593e0b5574a6a5037ce4c8a9d7faa3141e5e57f6399acd04a6cc00e6a28ecdf54f8fe5be58832ef1e2a0e9705974253550c1a05e69c3c72e3d65068814395436df1459c78c8acfe7f4db4afb9d1346ef8b72c2e137535c5c6fe30dd4431b8b57769412492b1a9c9fcb7190d551d7199d023ff5d858ef92cda2c705fe68eb8c4454291a16f1071e5d9eb29ddffcd621e6a34f9a4462f32f6906d00e51e3b7a0f4a32cb83b5ca111e4cd1005616d8464446c9e70d32cc39dac3d3c06753f849ae0d60d8655418e94c88ebfbedf0051727936cbf825928a16b0b33d07e08ab155ac6b7d07daa2a83d61548c3a27ea09fe450de550f0ee29cec2f03a08388a63328c23b1a0b66eeb266f837abf657b9a649eb35462b461bb533e3c2d0b69af1ee57a6b1df990a4574a6bfccca5da1c23eed9fef2a9225663b63034190008f42234965875731dcc76803da1a58290fe2e5e8f88f44d9bcc035809627e0bf6632a71eda38b9e4abf69fc3cdde99a897920136517cc5be2f3db7043925304ac27ac718da62b7c550e3e8a908831cbd0a5811b211d9f0f25b612811b7c7a08c51e7fd191b966d012f22b75d950e2ee11eae12de1dddb061e504c2bde0cf50499a643b46797e190635206a51da4a9e124a6c0fa78827db20d6e21df67441a0223e7cfa5ea7a34a76823550084d3b8a6a41ded0b321b6f795a792d4098d9960580d89db4dfca3eba564e4c0743d4e117e8abb7366fd0bd6a175dcd020770930bd8e6dc9856b3ebca486f38190f5a3d8e9bdaef525a4275b2cc6411cb06f2c30700c365101ce07900cc0b580556c0afcd0ee7ad0e3c42273ea776662081b9030c5e1f1c3d5163a6674eb12368e6b34f0be4d4a2e69fab2732c2cb8dea9ebb3e0e94a8e15630532444e843927db3b78cb90771d3b411e19bf7ab09dcbd3ee3ef0a6075746f5afce41aa48e77b6680310ae06fd532ca3b1121e8a1392a55bb9cd04c8ea8bfa4153b24acf1be0d25a9c1371f43d3fa02d6ee4546524a9e3a1c8610694519c16fabcc3940f665816ebad79c5891f68a357b8cc36b8fb3950ef42780bf0954598c888d722527f0bd89c856e2e9a99868082ce00d22136172aa2201f05a3d812e20cace6d618d8f27af5727117fe13159d7b66ece3eab1926e5a22f50bbfa6f0026ff592ae7f2860f78579057db8f7d95ec685b07e47e1505cf7674c0f33117901aae02d8cfe31dc75f74bab3acf5d708e725333a586810e5cf70647030998795eb6bc85216ceb58f1f3e966c52e1fd1e641dfd6a389f2f3baeeebe50eb464d2e5b3b0586aab42d33449638378d83c5e33b68a72c581a3f73f851e20094e0fa8196166d1a392f6318ce7571c3052398b82cc427ed01a1084074b85f40a592d22f3afa34d5b87c09804f4d261b491b8eccb3ad0b6ddf6bb30b83e611c02d018f85e808bfcb5e85388e1a80dd63252bd7e995ecaf60b800235386ee7660a0ebea6cddf9442751cb6f8a1560f1f387a432f14aabc887dd64aab57925f04084cd6c4c48adb98cb487994c4bae683b0c4d21e0e6b297d4404a13c7ae74c030f54825445da3ccdea4916d52c9da05c0e209838597688fbe1fb71c0f17d1cf00a07c909ddf3515bbd8a971999f0927f1748b535031249d1e5c61a97475c09b60d13b2c49ac131d2fe8c5b20681921adcfcfe0bead3db0cbbe9600966bcab0e309e0da71d8b3f00a0dfdb0a74f0337101c7b7666acaee601ae248daa8c6555060ead14fc3a792d05e35b99321429b1b81a392faf52e7edb4ffb0375b7f6c39e606df5df228ee7cc06b285a2556de8e5b7fac02fa1dbad6a1d49f89f23087fb2902b46ad07e8b68857543002ef7a69a65fbcd04886a944f8fc358fc5cd1bd8aa5087dc0d8e5a44fcf955d989504e837c846173cc1966cec776108c991f097c9970db3d8cefe26db33fc78cd1556b6966f420c6405f8ae4082c62151af10e7ea730fa5222fe431d116e0848870687747954a5929a4e06bcf942b38fa366675dcf80fb1e7e63373d0e9a8e0ff77bd4a2bf63f85a2536ff3402997b7dffb20af408409c391bc20afc1f86f1aceb81b04ef5c22106a24900cbae576340c191dcde898018989906d357aa9c9bdad2b288bee23d9c77ecff2bbf35d7f2a6f097bbadaf50a6f1ed67abef43550ac80267233ff930e9d7b313d1c21745854e16813fcae820e8f4cc8725769aac0e6f00b1c862ffdb37dd3f8fbc62532bcf667e1ec86845207077d85868156d541ccb0e1ebc9b96d63660b0b1a966e994e61b572302f88e908f85092fce00b56e943c54d861705ce9e0e5531f1a66bda27078eacc36ef3590eaf00c63b5c1675ae6ada64cf378e777e7956074b04a813b34fd929d0acab10030b8f4d6a68cd8d7b835a83a6b0ba7a6afde6862bf328b62d67a8218dc20f190ce764c0bd424f78da54c700632aab6b9c19cf6ef945dba74af420c85a57bd710ff57e0b5e0f18ddea2cfc96293d235e5943ed9f8afe5c33746df67e6ec905050a6024e1d446198252bbf4014b5794b8a7d9fb74873c1dee6c60330ec1d31bc60d113ab95ea4e0376a715fc16022a5a965606afaf3807726bc78b6a27207994305ee15c391b205f71a62da15f7dbbdf52463932a191884388609d9bfdbcad0d0055f334ae6822bced23d07d303cd35841aeffc65388ab80fcedc66461261a81b0dd8cd34270be2542eb2d4eabcc42a9e083a07846efa79b351db493c5174d84900bbf4e442660c051ebe059550e2c67a7a8434faab10c6bfb2802b1e2f04cced0519d047ce3c502abe2ff94dc8be59f6e415016d2c77f1b54bfbffe179c9ead70aec1fa21d4a814abd41f7d5d19bc6b77f4ab14298a1bb14f7483197fff2b6390e2c3230700ff95ef79681078d49f0ff3a8f51138771989c3c1a8e18e13e49a90a3fcbc0a7b8b2458c0b6beca04c5dbdfe86b538e57850222a24eca0476c73b70654f3b8e29e227998fd54ffe06ad51f831fb4dd0b07258a17a9d27595738c060b4a087c00389c596fb33c838f4c7581663317da9a3a941056d6aa000734e380005b7c550677862c23689eef181e8107d1ff986c9491de02d8f6c001789ab222032674a1feb232ae673757ef59122249400cc1ba29a953815aa8da9e8275b3de0f3361574f998cb95a4087c880c87c8593c24d82772867ed9f2437f8b2465a0b0e13e36a5a15f56e04f4e861c1dcec4913c789a96e556e1b8155fa9e472c8007024f2f0d7978a7aeac7fa54d59acc6bea8758dd0b215a240e8fda5b05440fa1c0c933d982cbcb6e543ae08fb82f33f97a0509da0f42ae235f42698b1244e6c9902b5cf3fbf95688c9ed3ac06b5073a03faf591624f4f4e76cec2a590da26d60306e34eeff5b3f27e6d8cf3ad0dabf417397883c7505b911519b5bd8aa9f9d9930c720d534e362a1582b9e3e008c339d43e1e1c3a9f26cd7800cf4752491fb2c70a3e49b2bc960a44168bd04284b7abf6506672a29e5a5b42f27ffb1596c544af00709b832bcb891e9fff8511d0c75481db9e7a0392f16db45873fa1d45d7a85f075d05b31ff42bc26cfa9c87a6400939e9f32a7388a867877de470cdcc39e21302b44d9f40e1810367505372ace106cfe324ba16f8cc6c6f9602bbd0541761ff0f9cd6eabcd67af7bc6af0a981d403f2cd2a863fcb3d31cab9e34f60115f5be00a5fa74fcdb120cfe179703ba26691838145a2c0b1043d0296d27e88f173f3160cfd1fb21cad26e39ad6dede6aa15b40cce45443a1d9b1de22b9e9c63ac9dbf4084e1204fc9cd654f68572705fa66eafb934e924f64682ccd4c65cc38cfdb956079dc39002ae8995c740352ddd5b84795b53a7262d231d2c309ad1888301920408173699be4eb061febb4351ca82398893cfeacbe55e41d7c2a7046846ec0b940bc6d1df129c92618fc7da7d8c7a4f5b100faaa52ab538d592e7d38f446fb005032356d8a445faa7d655efe6d6459d05480d88472df5ed85d3b34ff34ba6a59906ef7b7849802186ba3c50abe2f2995456da2803cd96d0d97478e640942a63e603e22f2e89274c79e8fb3f5bbdcf3c91426510988730ac9637ffffb660ea1cb70e3c95c6d927be58f5d32e8c7340d47b55ff103f474d9dbe55d405401543e0f609b458d2e0dc06f9b0962ead5b5bc088e9ae3884bf803470993f761630cbb80108a70b30678e11e349bcd4d5b5a80ba85e80162dd08470eb7faba473c3f4605d0c92cedeea55f423c1cccb7c0a9a2479671b96c9ea1a3329c1df5a71b9011a3a0941c3b646395255e0e65ad84ab2de4a8e04c54995dba0a8c36bcc4d56971e350b71d7baed141a9ee90672ab3eccd024095a354915bd6a3e8e6aaba2194ecb8202c3b328b8fbc2b8587b75d0ddae8edc5b9129f883e1ed4210358a99b74fd72205065f05f72ec6f988ca2a5d713761c56b85f808d36638bab082c7e670f578b50f60cc3cba90bfbd2868ff15909e92da705f38a6e4ba0b0cc904a4815930232600e9cb9de5031f79dfb98ef497742d176b5d431e98bc992779bd97ad4bdae26e0f592483bd5d4b550eeefd8a1567c9e349159adcd3c41adbac9c55ba8ce494c10569410b9aea3b33dd25b887d26a618e5f3bd323672a56f4cbcff3ae8889e7ce0aa2b86431718210ef6a49cc5083fbfa5ee538ff6b95fe4dd7cc6d565228f8bc0f9e3ab33a4bcdc3c49f8f15a8084b9526e874b71989ec8be22245e65944588c016cc701f511e4c5445eb7e40dae1011769be29ab8cc5a3a67cbbdc60e35f48c06ee90b79a7d05059137d32fc78ea7e7d302c616a5bd1d88fda2a6ae963f7a5f02cd71f007a49718f0afc04f7b0edde7c517cefd623b89bd6b5644d4afe353ff0be7ef516ff029ad53713ff564b808388cbad04d200f396d69ddc9f152856d930fb36ffcd1919822df876c3a43c9e867f69efd77b5698cd7af8d63d1ea7a5b0d030f6fd54945de51a6a99aacf4c1be292bb858fb44c2e018f62f66d78fbb5a5a0ffc518e75614cd6f291128b5dc97a539ffe273a3579bf7e5515a325ea42e5b20f5f8bf74e1cec27f4fc3236560949fc57577978c2f06284e4dc5bf61b501e650f09a314026731c54b61d84adf4d9be32e88ffbeffd4f094df5c5f0068d13fd40c35ba9e69a21063aed2846db36ba7b0c6653c1771f53710927bf5b58faf7fa103ab86d54d4d8b58fd7271845a874ac1cbfeee7bfcf0b3e69c466d80dedc808c0d8d704b4a52025b34668b8bfd8d50f00eb2b4229f7c4a4c6a03d36771b093170f1a5ef4371a62115c4738ed0475f022b26564a53f4fe400828c73b632922d9409e44ce61e1f42eb35de0279c8f2cce7576937a66ebf13c317ab8d1e16497bb50e8cf4a3ac4f69616aaf24bc2a788032fc98a81e31eac6a15e7d51a1c356e9fc06953254e68fd218334d1425d4428a417f5a787d848f95df4fab09b3a46abca60db13ed3720b4ead6923126bd5d801fec0ce795ce4f2e7d65cfe67045f1aadf20e713db4ace52a22c88d526dd37bbdb2debeebee5cf8a526bcb6307812d6da5105f8f5271fb36bb1aa9ecad5639d5d1890125a34025bec5fd3d3d347defad9050ba5d4cd28c1b83afafc7383601a631efc3d3e7b8d9324d9ef2e482af55b4d00005e2f48dfad0757d0ea6868f66fee9c06a75f8097c417bd52b5f3fb8688d55b0142128c7814c5770e39e0808fc5e683953a2c4de55d481802e53cb005c16374036821eb2b0ad3cccfe05880b051e6ef461ed4c99a155152dd5e4d2976837575024572406d50d62bf88713cd45406e59b96fcbb227f41b9e1d738300a9ab504b02745dc8be0d830b453eb453dfc1351396ae66e50f65468cc40e93d58b5be92805a11c74feff56deba891431fe7e9e07f585a266c506fd79c8f7f6011375a5220991a3967e449e92a90b1d8c823170401d49e721e433af0a58d7e46d3cc534880fb8757cc001a8870309ae882f765d248f7df51d9ee0d0f0e3338d01c20fdaf4007c83cbe4188f767a8118e855a805b386ab766529b86f7abc3bb216412b94d60bca2d0be6bedb4a38627cab5c3b23576be308c2d6c261588e4f2f97907a472c028345c9fa3e9994cb617cbf788faf4c60efe7b4d97f9a88635d99b1e9874ae4017eaaf6bb0e0a508a7012a4aaff74f55fb306af3c06ba731f53f5e3496c40810f93afd0a700d9635c74afdfcf71d3256ecedcfb42752d2409a43d8c2fb1462c0f920e94edc3e3fe99994a5f81cdcba5cd20216d6898e76de5d92d708a8756bf0e93737aa75140672b92cb03cd32b276101f3f11fbf4a99f643ffd111f7269e409d9d3410d720b7781d23815d4cff5567b6d4e46228465888386e462f0a00b4308f55cc7c7ce54342b6cbd055d91d1a8f4e9439d34365feb193873ac8dc89154083081c38859913fb9061b781a877c7edf186ea6b0f2d2e52d8580e908b39c8b0621fe0ddd6977d109f9e7358485f9f9a52c3809611c646e518ad1bb8ac253db0253a673802ba860474747927f8aa745c74a4f952073f58dc44d89a0153dce4f0f9fef063d2da49d4c2fdefc611fd41c286e3adebc3c10a6638e2d1417015dae0257dedc7b75644fef8954191eed5fcb84b2609cdd906d0e819a3fc1ce5041d20e360435c0f0f4030b2928eb9cb1bc466e9b180c2c5c496b6c738df0d3060bb10ed00cf5a74fd092bf8eaf5405f8b6dab8e769247a172293b4c031c062506bba0e3aa9ba72e1711c53ffb12e91ee280e357ee4d8f3cf7cbe521b804c7024903b0dd84f7a2c7b35c4d40ccbf07265b043fcbe60c5575af5febe457d1b03e0317d004c6b19add44caadf00f38c32f4fafee7e79ce7e1d88ac7df1ca9222e0975d70d36b8e303755f8281563cd489395db91ff6e79ad496088aed0f69e3a75a24350e775c1a72650811879c928071a4be511c3b037fe11217f80bef3a134c658b84002880261e04996a0ae38048ab94f55e7cfd2b8b626f0e974064603f9de1cfa10bb16e4d30c7ebed0467cc763f9a11bc5c365b95e32afe92ee602f1bd087ec8f0779cdd310ea136433fdaaef52c8b1790aab02901cfdb877f23aa9d550c031bf0cdef09414654a92a941f7b2d508075d5786e075f73ccbc3575262af3bccb0140fcefa6ae93f5a7f5fa29874fa7db6dd9cf7491021fe0d0d44f7d2ef908af2fa0b017f313b514f949751a675513883c675c3906750901ae6b6c8b606519b33b60367172af2f0b766b1265e316d48a29c5de48efecc9ecc0e6133cf71eade4e2e00425999bb393e414d361f2eca1f78ccaeb595a90ad0f54736dd52f33428eba701cde108345e261414b0894a2dde8e608749dad05b53ac95a9971e9b1558d365054ec6b2c2bc3c0f19e626d7b28a1827972565c4a27528ffa288a5ea2475115e0b42cfce09fce1c3f9c3af277a5178e1ec17056ef7503d61d3eb6eb8a7ab30430d0a38f3b6cbe966b85b0b15528393406ccf15276e9dcbc0ca6f064aa46f20560851569d165736fcdb0e2e132ea336e01e4cf4c601ea6e8e87dc3fe2317c461105f05d8f942bb6a985cc6683447ec6e24d7f19dd2d1602eb0935e4ede46c1b3a072208c9db676af426791d8a7cde51314ee458551b02f44e54960d55e4def9f406b01f278c3b6088ca6f1b0e941d673c7dbc466546136e00537a9093d8fbd16f0feb755aac9bbde7fdd13d8c697149247150f8f6601c27b4f44653662a102b9201edeae8fdc3f7db4ac6e486b7647a496f1f3733163af928202e87fcc2bb6352036b47ad21fe16846877e873f082dedeb92698a418db3ba6f66468d9f7a9d9420266661dae496379488b67bff119c0bf8bf76c3e1c11b3e5ac4e28b77ed9f99c0f79232081f2e49cf483d098e55b4c4c0063e44f16ff7ee79ca87e0791f9966e057dc60dac78e8033b4c5112f7d1fc487663394d60725f485885961a5979a3ac0122204b36d47c0721e1e0f02cfa75c9dccb1d336fa120eeb10b4d4f4f68e5730e3f8bed25f378742697bc7b88a50469414d42973f02f49d16a2d83a7b9c998c0768ae277e57ec5cb2f9f8be5e9247bfc66be5e8187902d0d96571e2980ccf3c0768e52ad84d08fdbb6c8f48df91fb5d7a3cd7434f260a2a3c8d9b43c9a77ff909ce44c4edc26b5cc82ae22bca3e4231044a22b31e396b803fb2a03e9e3de05d0c75384c67725a0d962540169f348309d00a501d6d05f9f1e44acfebe40e21240087813f2649e03707797db2871da837d1d5f187179e8fcdb7bb1d7618dffb2605bfade539bfcc9c06c8167cddaca619fc400f0412b4db4ccf34cc51ccdb3d1505e0b96631a802f937e3aa2507afb006e5dd996880279e6c9b32a6d27454394b06ba8a4050e39371697b019a2dba3c494c579be7879071552ebd6f19a947663b05cd40ce1eeafb7c8d0a374735c4aaa6611c3abe4cd648219416eb2df625b4950adaf0b3891a8286dc9e732fd88da59f3a4510777da9bb8021c2e947520a716c05dc033297b8b210cbfa750e12489a3ec71bcf3a77aec537825a591d1c40a6e30bc471ccd02ce021a7b03fb5394d4dcc437ad55e6c88c9b7a441cb085da3583c058beb975d8b5544ac8f3c24a73010b3918cf3359425773f99e787c1060a0f3e099750919f5c779fa46882b9a2fc60bda1713ef87be883986e44e6e3f7efd8e60aa97970164451054f3a8c7b09100cd62d0c28de11d28a012866dc81beddc2e30a1a98c6eacfb7a6629e2027649268de17236194551e249b6a5864be33fde6ad068a832b22ce00ca7659a6e4497fead24f915eecdd73358dc87b2d10049afe46082289a6d84121a1e9f91048dd0c54949d9762e38775e71faaa3c1b433859266062b633f727cf2578d255f1e593650db51b64d4fa94aa63744978a6330290fd0025f2929ed346288aa5f405b3160ab1381ec6b16d1c7137187e9c603e14c89140bfb6402e8e72e90aeaa3f04a13d9109d81b19bc171a08b95658d2e4f83e9bab0c04863365dda1a0b560876a02cafb4ea5899a4346e54fdd6109e995a43cbbfc053cf7871d77ff40b8b5062b1b7b3742489fab3f4f8b14ef0f8372232e5a559c0d393293d9d70518b6b2cd4c9d969876c239ad55c349ec8976e9c03fd21c42c90f4bc9a240d5bfb043b3d68acb5a928ac67ca230ae94d8db2adbc989e62da305047663a834846650d7d7b143706bf8a03c247800806b5ebecbe81927f6459bdd0a50effd74f5d581692b6aff20a94e7046dab342a266a5c9259d8100cf9b4446046c6b4e747a1e2fdacd9e454f6059a35b4bc8ded4912f0001afc3e0be9882b702099343a596c45317c8944fee535abba364fd13910bda3a07781ee2f671ee0a01b531e3cd32413787e48785c887bc7ccc8aa85c18af5d4cbc8629dd3eb97806098789c054a723e5a0b7106d0a7ecc0755608c74f2423a701984009bbd30f625076beb2dc8b99e3d60701191992e125e635c0153403ef6437d6cafdd84070a610bc1ad231726f7aa0e608914509da1cd68f20f71d2cbb3d13bcb502dfeaa65fd081a837d233a2fb9809e8971baaa1466f189be405df4f402e26d0f28974547290857a6cc7879193e54f8ac2dbc42f3acb16e24f3b6fb8eae215a439b1bdf6e240723a1dcd6a8a1f088979fe7076481c6a3a54c1841c67abb4873613858a09f1a0e8129523d6cb0a1d2aef5a7f3b090390a12b4796ed1ceda834f9c4997750a3d00eba2f76a66ba699d20560ff27f855edb77152c8551c5bb90bac6461105801703687107edee576299ab5e468f230cd84309b5db5ca0818911d91c7fe988272a08c52ac5a291e45e6890a52849fdc5ef90dbc3d93b3c0ef67305712a56af8c2203e337eb974bba80feb7706799e77cb5e3738c9c424fc820277055274e24f5c4016f165ccdd3e46eca7e328164369bdc95c68600adaefe90cba45ef24465740508a2f8a712541d6fd3631d1ef1c96223d5d74210836256ad28fb247d110b4270034c404bb4248c5b0279a8d0ef775bdda7118925f25bf1622bd952bbce96522b047091e52481d2857532c2d30e0224c27b15c9c8a9532695a54680bd387c7f650d4f7d0af3912fc3ad0abfc81e4a505d728b8d2d9b1013002cc9f99bf5a969ac07c92c62cda3271262c731461cdf2d9d5da07081c6ddb6f4fc4b19c43ad0127a032389eeade1076c02c9d10cb2cbfac86b81a005fc89441a58613c2016b25cb30694c775d142c6923094462751cbf5a74367d11eead92bd18b3fe39e0ea2d63c0f9b7108c4641d977daaf015558a577bc700a6260a62d1fb6e13909d829ad54d0b7904ab823fb89816d93dd8c5985da32a7d91154a1222259ec29f8882fb1e850c5f1ab0e571da4661ea79ccd08cf91a43c3cf6b4841298760e89c4e8bbcb12a00cb9eab296e40c90404c8180efd65d1fa65ada931f3d9670977d4a4533d0875032d9744af2e148a05737bca54ef678dcecc8777a42c0fb5e9a519717f05bacf0761595651ffa03809c719163add0365451bf4e12dc9466ea51f80e8029f61af0b5c57abf94b1366fa3d686abeaf57f7d8d97f817e0986a4c97a59d5150bca3108271177fc026a66d9bd5961a2ad61bce9d1800c41e6ec76e5080d9d2472e7210d828a23a7a784bec681829377af5fcf6d232bacc453b50b13eae91b7d8283c4010212bc8b01b7ee7be48b32849d980c615343a50c367f0308fc1f954ff77cd00d97ec181c777c907e3dc5aaa61e581e037a66e6996266f2b14d48c108587ea007d3a118bd8e4fa7658f86d6a73468907d98c6c5c6b4148ad6aa1c082867df0b041b4ed7eb08ca6ba4b92f11b54c3c2a1d176c4d135d020338d125f659555ad706d50ca12df34eee61b990997a26fdf2df2f003226694097c431dd0f1e12f2110c1bcfa523c55dcf3ccc9b6d93bc6e1a99ab46d1bc57e239a11ed3f20f3ee72407bd6adf6b9e51af59ffa0a104b4b8ea8f171eeb4afe94280806c22b4f789fdc07beaee8aa6e515dc08268e0a8354ab4606269ff6435f6720ddbbcadee5720ee01fe0e6299167fd1b6da222b59ff7f375efa176f3de30e736b703e0975c191e604b8bca01e671dad21b36271e63e8c7203fcb83b61069c82190728537df4f8570bc8262feff976aa76967e86b8749e1dbe68ef63cae342f4f11ea9ea1d5d38210e +generate_ring_signature 62d9fdf998799ad5f0044a6a7c5bcbb9deaaf78ac678f0ca3750aee518456f70 e85e6d34b7a44c497b42634e4e8be3a56e714cf5c44792c29204903769a21091 16 21b726d56ecad082666ffe11fa61408df99af9e47500483421b47a34bcc410c0 314d10d957af2e4da98a98de73d201ce595460aa9f842237ccb0347e9493d3cf 9cbf055445a0e7121e267b3a73e7449e9eb5915b70922f47437dad8f4ec9417b fefea43cf649abf557a369c3f7a11d4812394d1def349ab23e7c2e44d019288a 48360109d5b6da985b6701f47397ecbdc59d97383be7e9b067b318acdd109ecc a97375453701cf393e2a8e95e5b6c2ce365c461eaa62cf1449633e850b5a889d 7ca32230254a075dd5d166b06603ed21cfc4f5215c94bf84ce9ccf3721c44df8 14beb62093b4712b058f10f8232884896e4e85d2ed8ffc1fc6e03b6e1080e2b1 a453729d18b0d90e519e241710b05547b2569dc07ed744dbe3a5c97942e3ca75 5c9896d9305a062aa12eee2bb9b6c8e3be3641528c6d19847081172694d3594c 574a15a29cb4e3c1b244fbb9e2bcaf0cc6c964d19047d3dd44efe0d5bb5ea1ce 0980b5584b6a796bcd0d1e87ecf3be9f1615413e3f1ea237bcaa17108c11a5c8 653a3daca423c001f6f69ecc804ed50a82d0d95cdbdb35c7483fdaeca547d807 18f5a832f606874954b4316e40f69c9df460e505c0a7506f0d42af45c58b483c cee4f248d50d0302575d03de3e792cc5ca9a2cdf58abe5b47be7e1e4a801771a d63afdc5a0cfc9a531b4f099fe8b74b2f9c758c39c1cfa3fda7def01abb4e422 7c23e8fc27aeb5d238494829871baa490fabe516081fa922f3a4161ed0e7c502 10 f49c88ea1e4608925a207fc998700851a1ad3b50909c5cb1c5ccd2988ef6f7044e9e9c8713df6823ab7607ede709000305930dab1f297793f54ac2ab4b8397003b48e6ee50adf19e27d928d63c0792fa2362e3e278115bdc0a528676af00820a6d39e813ed3898c4b0c52fcd5bd91a0ad9b3606cdacf9635080348fa074b6d0dd9d1cc3fe3b0c4d606739d490986fa3b20e6e17ebc208f3a2136de78b9bec308b74148b7ddaf59d650ff0906fcea41d7c04ccf60834612867a24bc4b7c3013036d3d501b933c9aa8bfc4fd92db6ea8982d10011c7b48cf8d463466264ff2c904cd04cf8a2a684eb6358636883cb47b7a17e5202bfdae039f29fc5157cbebcc0d24d47c1d64213fa64a17d69b25dfeb926a7a30a5ba4a69c3dc0d86226cd4e400cf06fb56af9342d892d9c60c395b7e2359fe40a16006af791fc8d8f1d4ae7205d63f6e5f2589c30bf87a12d86ce777428eb0be137ef96b46df6a545726983904a78a62aa9a53e10f685fbd8a72c77dcb99b5db596adfeafb86a66d2ab48cb908ac2419dd4bdb9743a56effda54f9d202763cd4bccbf974600ba9774b410c0c01e60f890b8d5cd85fbd72deb9dce592dd0f629ee6d47e22ebfd65188a59147e0a5138ae8abf6962dc5a617e9a8ff173f51098ba5052d2416accf7625d5d94df029c38119eeb6f19efa18debcfc986ee9b8d4e01d5a6d68d953594f3bfc4cdf30bfdb5964d7265a52b3937fa49d6b6d971b6fa93389c0ca677180211a0301c190272b833b16d18bd9ea3bfcc675a65170ae6fb487bc1be44f2547a0422867bb20e295d27df2e8658b30de1ce16d8949ddd3b27d04251411fca62e2b52230d3e408444729a1ba2e421c4145865d8b99f9b873694453d629233cea095c4c2e1f100dbddca3c638e4efe324eeb69f9c133b2db7a4ab3a05201692c0c32aa595bd5c0a84e0e7134a4c65b946b442c8ad551382c7d5a81b4cf3d924a2c6f97ca7f77b0511b4989d7c5201be8db3dee9631f94c41629867759e8ec2bb67af7b2a8b1000360b7b0750e6ce880ddf12d50738651c02d08b5fae1e78ea4618d39ec4ec97f06be1d7eeffcb6d87e9a219dde2e3a9f5ea4811ef5e02baf4cc69f43dddfc2240a4a51ff4671d4826d2a3f592d3795e0d49ccdd1e19b30e44343f635544d70e60996b705f44208da03bc4c1339692ca62a3dabf16c054ac323295653c496976f0f5ab184356778cc98b32816756e0dcc4a3e9b0ad18cbef30aa93d27c242c601084a22d0c9d061c25350ef592e209b03a5f4edad466d45166d54a7fb02294d6a0fbf70279b0149927673d629cff01f95669d7c66a3e52b39b62433f2f81fcad00045fd498037b5fc8da56ded0216a66d8cbfcddf2adf0baa6ad45913fb8c6b0b009ec2ebdb6bbdbc704e634288ed6f644e9eedd23c9afc74fe20037a27016fdb05 +generate_ring_signature 10f3730c2f6a2514d89bc556346e2a37fc87d7e83757248623edeb9fab8ac049 0add4f035c3579eea25efb66d55d73cc4df230ee3a749cb5ec8ed434d913e41a 1 8c906227edaf3c2236ca3f0e9b6aaf208bcf39ff871919427a7719ff092d26f5 1fd2d19d0945cd70b8a3104d3f0a7548a9acc311648967d48ba83203f213a50f 0 1ac82f142f35bcd80feca8aa2a31daaa4a774d68ad7370407282fc017cb2180287d83d815aa8919177e2453f31aee5908891342fba2e41ed289fe7ad8316170e +generate_ring_signature f4555c7a94cab7fa9604d4c106969a84ad66a469599db08ba49bc24b1e1488bb d97745abc00bb8228a40b47a0344c9556d8dd15964fcf20f0fd82341d6f18670 171 cfc4bef5eaf34348f327005dc7f809d94e495153532aa7bd337e1230de4974da 79b0a445dd281c6c167dbc468a4946bc46d7ea55b1393895ff1e8008f2f53a3e 411bad6789b69ae4a955d7d5bee956a5b7c73ccd3481b6e331fffa049a45d625 c5e8959c5e9f915e5af54c1f77a81fe3cff041c001bf163243bf59c206b9b78f 401660dc495dcb63633517cdfb4c83f7c1bbc39b510caa85d77861bc3f221618 6ca7e36b209d1e1c5b20b78e26ca678325502c504c43e51fe2c9e12d2ef289e4 b295c552bd1495b7b21d99ae5bb7b8025e507a3042b6fbd81cc78f2ab2878927 1a10dd6423205ce8dee6302ca9a40f2257aacc9e954bb6fce5cf4a14889b00fe 4def6357385d6989e58e06f38a50fa51503aaf3b13c2bffa146d2824f93f2fe0 8efd4d3019835bec1143624705a46b9f54dd4f410ad23a75e39f868728585a6a 448e81181967b7c2f211960907184ccc9dc01effeaf2bf0ff3246cf715fa7841 e12006e490cd7037403ccb0cd61a54d6016103d8228ff5bc5b178a4be5b3d5a2 24c6dd871e6f6d1e466e1c2be9779ccf2567abe7533a6d0868b3497346d6f08c 4dc82719c9af722b68c9c9b7a6eff94600c7e6a08240307ec343762f9d24b142 f72f1ebd2e930a6f94e2b3487e3bdc9f8f82c78e8a890635c76455bd12d2620c 508410277ea62390fb737108acb07efd9b4afa8cc3f0492f1735381a6bdd85a8 f98a6c409991d99882a77806afee0f5a4f479f4840279447945b2873c992d257 32e736d6c58b4a36c91cf1b8d9719430db9182e15184a30797eee75dcd966b22 f6fdd93cdc90c61980ea7069933b6dbbd53075b5a0dfea2079a461b4b4ea6658 38cdf0a99c31eda7fc48ffd68989add1a6224503e269a5536701b11e4028708b b1aa00ffa31d1d3d7e645dc9ab2c9d724b3a699702c877e7ea610808dd2ea9f5 1a8f990377f923a024effcf088cc532ea6049d533d59563e398140dbfef2790f 635710a81651e374f11c728151a6b81f8904f4036f70cb3be54c4a1f394780f1 d4c8b7a93f5aa7b5029651f44bfb87ed4e762e9a166e37db35eed7ceded15017 5abde930e31af431a396c120cfb04ae6377f2f653214c76278b5e93ee91c7989 596acbd0cee6be1584cd61634edd8ed33f0fadd3918e8b3398ddee785be0d2e7 197c6902281fba43aa82e272c5cbeec79ba8b28dc0bbb1f1ddecff2c91cf0444 88176dadd11bb0920f9dcb0b53cd9bc720919e663588317d6d48c38fb4c6a795 c623bab71256ec174614b343d79a6b1c7c29f89a94ae9758f60cd5f2c01fc40f bbc6e9dd8b262f08d09935723bd9ceca95667d6000b64d6566985d8ceb02f5f0 e8d10be27173f52da0711d82d8a2ae34925fdec44dc232eacfd24932b2e459c0 05d9f54e15501e036c89fc60c8fdf9ce39a7520d0585e02e852f944a76cc47b4 bb548d6df5b6e342173a857f8378450dfd1c1526399648a05f81b7e72cc5676c 037311c8983b23d7ed1450a24706026c1930d7ead062b4c5806127061c96fa18 41d01eb664f28f27bd51f1959a06120d7eddfeeed185fc4b5b9c67593ca3f750 ee1fa901b60359598a0985f6642fac8604aa654e78f1f3d18a1fc4ecc87536cd 7d69624959350420a65aec671b481fa6ca9f3406e9080acafc5b6dc251e50a56 5315e45e258217f50b9bc1d79bc411aa350d3427080b3cdf5c97ac703861d519 a4ab5d6d62a398b29a4bb0ff983060158e2ed7a81488fb1271e380839d5ab8b5 05985546d407861f5c113c25061eb90876cb7b1d043a74af25c62b406b821337 bad614857399ac6ec7ed7887bd1e9e0fc0320b595233fa0c6797f8dce599ebda eebe0e178a0e1fe78e6233eb6d86d3bd701173831d7f40da0d64cbc572826cea b329aeb55ae0de7a468892cdb57aecde5268829aeb84bb88245111fb438dffa6 fdac8651768a15a0f8e502319d3cf2dc41fd737e975c2b4a428b42db1dcede27 88d3032b6384a94593f5c782e3e7bdb027414c3fd99d8730b2252a7ca744a3a7 1771480e847547152d3aa315218a1c2cfd01cd45ba95182a1b6447103ccceb38 37b27bdf4740b2a100f54f9a289cf58c2b66a4e4f1b13e577095403c46ecdf86 427e01a2c9473a771c6fa5e2dbeccb62d351e429e2947b2cf8c228bb10da4649 15762146795864be7e50a35b2f3155b05eaef4d1b01c61347db5f11d5baff073 69e02c54680cc7205966f38e4a27a37a4a7357026fc38b1b40bce7e7edd5d1ee ef15e86c3ef859f2c85cdf828cd425d4f4ab7aefc11f5a0f4a16daa2b5fd22b0 618cdf2644069de2e8275619d923ead50f93789a4ae2ee8b68b625cfb206b39c 50d292bb61966a8d4beb864d9c120170f1570bd63cf07b41332797b6c29f5c9e 0a34b827392ba1883b610d1422c11696cc281c8b6ae75e961c42be3a3671acca 14730187723cc634969ee839ecf45452304a07bca280f7dae82ab0edc0877c29 699a24951c6ad1649350086f99a1854c89bda4228cb4a472179987daf854cc10 09aeb8ef6645a359661d04a03b7b7ca72c17a0a31b88997264cb49a38b8d7c13 85b0ab6229bbfa7a3b5b7b23811785805f63909500c94fb51d89e8f0554e70bb 3b665ca09ac6eb886900c99aec8e95270ff3ea73c1d8654cafe3acfd40019003 cdadbf8b0087a497967578999992fdb5f6dfbca21531dc5430e41151e051ae56 0ac2710ec9c037dfc4c3f93c5191a14737c0349a2c0ad7f6a44796b7bab64ea0 ceb2bda229231c4d5dc7c031cf687e6bee4d94c75313fc3d662dc8a6925dde77 a530dd5485fb70af577f782c9bb1b3694f832fea5555309dc992c65950137c14 2f55b2ee169c1115ff9eb53e24e1a240b7759af68bf305667fedce45e3f0d619 69aeafa707c68a9aa05a273a1b3fc158a5fdc20e143523cb6362f6ca37f99c25 2e03f9603c7abff4fe2766d73c4a39fdef14d144b0019896ec3685684443f1cb ca18d53a70086097a653b294e2eb531efee7c58909c0a35c80202224e4dee356 aa7b68adc147c53ae015e29d898a72b94ef6b6661f3a54c88a03075c73fbcc81 70cd2e3c6c589814e7a3191ec0f20839bbb2849436b9108110d689abb9e0027a 7dc0f441b3f1c7b35cb5ea301dffdb55e8bfdbf0acad34bd01786107d43a847a 3f08b78c2866d0779ef8a6751b7814b6a509a091c99e96fd3734886a6d001ac5 11db0478b0e9a19dc38723770b1798e3addbf704cf8f5d2e2764d139f0d22a51 f961c491a8525f841bac81919a905dfbd249a7a60ee6257584dfe276ae7950f5 28565b59bedbe41949595332aeb2711fc67733117120b1822c2e9a327e977125 022098f9fa98ecdfcba9ed3a8bf49c72ccd8117cc14d21dfbd22db54deda7ba0 68f94489f7a92f0a9222e201d2b4bba8ad6daa41b4de2c57c7ce9b393dc9365f 34b9147b28a3ac76dbb13ecaeb3f6d6e722a4a8c7d0bfd8efcbeeb187b096a79 b9a3ddbedc533698050e8c4dd60e05919b70fc5d487df6d01b62858b05f48aa4 c214edc1e0d6f77f18a3beb648b37a547ad6f929f3bab96586ec3034d6f9fd8d d04804e17f6bbd4fc3248c049895d3911d3953ac98790ed97f7d038928be2946 08ff0bf8daf6b3b57164169c2b48ca64ada70baa106e164513ff2a5e00a29ce0 bfc8cefb5c9e90ac9e5a1d92e9b7529d813f8041e519fa527b224151af806e26 a07f1068e09111fb6b58ad945f1c7705afe8ffd16cbe5488bc8fb94b8d53caf0 a451ae9ea7500d137403e431646e4ffdf6292489608606042f1a0e373e062f7d 6fe4867afa167c2f14ef976eb6403af05c632057e0d0c5884a3b83052a9cf32d 2e2e29ff51678630871baf5f4b1c97d6474647749de46d887ff31916194cd43f 662a6698e43f9c15cbeb38c3a8f31fb131e1d9b476f0f3a0bf40ee0e8e6a11a2 8421394abfa917e685bec5736e2492f825e7b259b587d6e568929d4a05ae2a0c 442f5c4e472fe9a9ae50496e3ee4281a7945d55a585aa296873e28e2cae5fea9 d09852ea077348503bd7995157301f844afc568e86d3cd18f0b6d415643e3e20 268d7e0eb3a46a11012dabeaba1f7e3574d1e32074bf4a2feded65c12b97453b c122fc5e2fc268e3b10f328e4ec77edc375399c1100dd10acc894903da97b9cd 9f39d95d583cbd4cb2779e0820d40954218f009a7cc5df92e030ecd9329564c3 98e17aee11922bdb6a06e8d5b639097fe9127dbf79d12aa9a8adcab208b36a2e 43250a86c378ab45ea8f34e71a451cdcc8e0709574d40f77357ac3693db3d4b2 f33bf249022052081d7b5da52abccd5fec3330fa176306958c7e3d6291f52984 020c044368cad6dd0e1d4b563ff0d334233c6a875a652e551d0bf8855c9d772b d32198328b301c208a8ffc272e512f909012a07cc99488ca9bf08c8fd8940f83 0832c436f450191a0e0ae66be14f07ed5081fc53f66a0df1315d7358cb299d52 fd2d84f1afa4fbe177b4da82bf8f6a5b46406e4dcd6f308fc54c6eccd018d707 c650a776d5e5f98ca43507f46d2d28048a3e21a5196dbaba699ba1904ff2571a c87c053930f772f7cb317a3191c3c114fc323220d74216c28b9d1354c35a866e 7dc1e344d66e726db6bd8d296dee9d3f04ef75862bf5380e445ecd43b0932618 af34f348b06c894e95c02c6a16fd7e52da6b45663df93c6a2533141959729f7b f99155d1317e0a8014da9f95ef688226097cf59080edfb3cb1a7d8c40c236517 bcffc6582fb43aa29d9df01cfd043166191ae64ea184af279d3c4cec05e46572 dd56ac7d1111f96f932fc79f3b9a33bbe5ab7c2dfac8d3c082c1d07c31147ba1 af799d2346c63aa6e1aff8bcd560b70bf99b3a84780a2186b018f87cdc7becb8 a613368b100cc5a85c4955ad5bc04e5f8d95fafbcd62908e699b984210e548a7 f6db5e3e8cf9f271b129038c706e1a335baed4cb85a23934722e22ed7aad4814 f94f6840a410db8d7809f6f9b6e4898bebe6ee6c792f046cf08a0e192f0af4f6 cb6060273be798eb8e209509a44301993613f4fd5b56b4535c515fa55b4d746a ed1c8d689f250b24e66c8153558ba88317c26cc02ddd3fcc6c9c59cca038dcf8 68d13f2e581dcaa275f2523c351febf927fb6c93e65ca9a293ad6e812b413663 46f723ca4398207c1531aab36297a3f437f1a26458eaa3b7cae6dede9fe8bf91 d74bd9c293e3022a7f7a2a5cb8c22f6da99c4dfac585bc91d4a54428af645a8f 30eb33b7a3cfcb4ce2bb552ff92711152b2b114b9a3dd027a7367b8e8426d5b5 f61968957b10bd9c877b50c5f7c8beb6791800f1f3bbe9b78648d3fa0faeccb8 04e2acbd42014dcb32cc1e7690cf83bc017378fcd3b391ebebc56efb6b515ac7 bbd85c1e26423b3c6c68739a82bb5bb1bb045b8e154ea69faec0c4e55400b79d 87e234d1e02dc9a3cd8167b7fc36b72b2cc9a510dfc160b87e4c3f08046e504c ef996edd8c19019436b04acd173b66a4a881ddffefad2180c07abb7534021af3 2b75cedb11ccebc4875dfbf2f4b18ef82ed352924567cf9b8bedba2e79ac9b3c d012005892ad3bceeddb59a34f98d1717cb85daf2e9fdff671d38d29919c3306 5934b1dac37b15070667de83bfcbfb87386f93dadd3d90abe02115ee1be25511 8c8367ac396509cd1bdc3baf341cb8659e85e597c321cd8b07d8e349cffb27fd db8e1c01cbe556892ea60baabf545c1944bcaca8b03c22d4a1f5e9167b5749c2 26929b9a191e52549d227b3798974397b97d8127911f6945dcd52c3a915df871 bdf06c632061e00b667dff7405eb578912508ce8b1cda679ba90637a6ec9d755 adec4d15f5b6858c8c7fba49da88d68d4bf31cd5b92b34ff29ac5d7cde88cc98 d70026fa9b5753d42069c8def719a909d34d6cab3b1949b5398564bf6f408d70 ace300fd54f1eb3b6428c46feee6824f1ad1b7519c099800f8c90c92f4c52833 0d6a0b164b9b02a3877f8326ba35cce82e4eced1d21af4d2e80f86ca1d8b72ad 3e65edc9aa0612700e957251c766be20acdad8ecbcbac2a645e286c3c42889c1 c797cc975661a163376b9d0ac24abd415490f5c0e5f70f87c40cdbf7f0c76c29 40f0a14168aa61ba89b9e5c40a8548858efaae2990b1a102a6810cd4e14a4200 422dc367b19a024000e1f102653a2db986d13c1be3b0fd9a6d9882f6456cac0b e69f2c3a2328b8546bc48b72f489d782e4f669f0a95e759e2c58101b10fbbbb0 d0b9df63e05f270697be34988384816f750d2cc5f1b058637cc4e494bcb247d1 cf39c2a55ff7c176830e14298088e6482b864bdc9935a880e54000ba2eb0ce9c 47b5d892a0a98abe0646e21bf3acc9414e08e74d9d7ebc46b8c5127e5aecb478 df6c351cef6ffc4738adf60d0f7f995a651e93dfa38d18b5e8be83d6cf7ef6e7 9f765118726950e8cf8f2f6f6db2cae8e49556b0432fe6960fb31f4262acac57 9cf2fdb6049dfecd5cca8e4a5fd17364a89ad0ef7524a28f48aaf7b32309b4cf 13cfdc82e184d2626825ccd9f10915fd95ec8adf1f11dfca92f5fb5771f48a0e a3aa71ccdf007f88880835328b070265dcbec5b8b537af52774d99cad5aeee68 3cc2fa6550177b8354d2f5e241f8b721d2a5366c34494df2cab5ba99864cc23d f2ce0c858dc7639de0154418fdc0f5f36ad7237830b43a200eb1e5b8ed713345 08ed90cab1be91c643d1d9c620c3bf85f3cfe1173c7cadc37ad2dead9b5ad176 00ad99b49db1adf38784bd79bf313794e45de8dfbeadb26dd8a2d475b49a6b71 37f121e481a9282144254a27b07b031e44bf34a47e2ef1bcf7b9b8524db52050 e00d06925df9f5236e69fa2a78b78de859780ca4e8345d50efa819179759e483 a4f6188c99012e9f26fa24abbb7dc87a3057d1bb4b99f4f384e5decf9c17c36c b5a5e73fb99c679b4357da96990a6d19a881e12922f1466958a4daf9af573e88 4a52f4bd038ea6c046ac554f842f88a654a055e5e9bc18bd6172f78dd2c97cb8 8efa9ea083dc3fa22da94cd582a3fee99d6a51db50444b1aea61c4bccc2b6140 ce192be6d8d50cc467a4b520368ab399e880b3eb4d93b8a96bbec74070e7c155 f22f7b08626b947b8d6017b846b258c75d55e1098d381bcfa7440d5ae51ff91d f11cea7db46c8bce9a69f5eef55f9a4131bcb465ea1153c9144fb698b1d936e5 b134a75ebcc9fa5b09567511151c8cc6cadb20af14e11c040f19519a6f613102 59b74ee6ed5cd8ac35f7dcd3b5b3497d391a3cfcbd21afa40a72713cd45b23f1 7f31beff2c2c80200ba74222781dc3b00d1cb9c2a92d0a0deae2032b8b6ebe43 acf94b13dfb0480f64d8f24874daba01d93f09d2d29be66a48e076dcfda39980 d31da219eb8bd9b7ebb4d6723adad876c59e1cb458f66a11b8b1e494940ec782 0f493b671148ad3cc994506460ee02bf973068429f9f1073d2cb09096918a5c4 e7464582dac622f032f7964927c94b2d801f92464c59ee34d533b474e0c4c568 faf9274a260a3f80554a0e9b6015ead2b3c7e39d9d5041cd63f834c5af85f398 424919660a91ecbdf5dc035759894703844ee28939b9eb004ced281901dce148 08df61a2481c760147c0a65a188ebbb4c0a3394bbc7850117317032ba5f7a31a cb51316d6b4d9797ef4ae30b6ea1314aff7d91e1e8b0cdc3d56649b9037cb193 fb517591cd9ed330972170eb93c3563848becb59e905f421afe8fd8f37e5a1e2 9cd6bf970dd2da5899dc28da8446cad1bc6881cea822a9a20debf919b23ab20a 103 0d00051ac182fc638afadc3cd95edb75c80c38680d43307b7288461ab76f3909f9d8fe414fb7fb68fa84612fa4c266962469dbf8745f76bc0d2e0ba83b60b00ff78c9ef8be32f69036bfb0849ef9f32d742b3693d102905b43df77bbe5ab070e52df078746535674b4ee2589f79e819f85dda9dafa3b4101beeda425c283870a95767577553260ee68cdbfe364e63c0b3372847e00fb5bf3c817dc3ea7182009bbcfbbf9930323993a38df5030a9f258c7f41ab5b803eca4f1245c3ae5aa9903fbd7c374940a246715a20633310b85ef6812d96af1182c14a3f967332f599b0c8cfc0cb289f1b73b4ea0edc6b52a4acb24fa2e1bfb5c401d27d8e0b70ee1b80dc9698285867cbf7b1d1ad5bd23b3a1d135070e2fe8198b9c3eef7c3e1badc50c1ce39ea8251ef0dc91e53724b4e84e0183ef753df684c4b460754dacfc8b160b8f34c1c4bb1de364353e8efcccdfb26d4340a1a5bc5a238e75571eb96e43030b8358de0d88327d6f112cd6ce0f640a95b564ea7999888466638b0bda438b580e950c9b8fa38899f091783f5c0e686a1d306b0c764a78cc7c0a679e744d813e0238b4c0562c66affcd5fe659c27c8ca29f0c5759ae6115069575e924ce7843d02df8412079582e12e2be58019fc6f22456b6367c9c9216eca68d7bc392f08170fd0f67f9a142e98ef570dfbf4a7abf9d01ec40dcb7fe1594b4edcaabf04e27b08b239643e5d28923166119665407586cd44926bca5671710be5cf7bfc6173c30ae8a8d5ff90f8e78fe98fb2003a04a43a7d3ea311e808546edb2bd978ee349f0bf1cae6bdd5989a93a55878ff144836a56dc7325834b1f06f0d74a85ff138670770e0b67a92942ac86936dd20b1dfac9ad4dc1b2c5dc459805f6afd2b973d160ab0ecf481faf1bf274a6978f9dcb1acf32061005f8d38329ff1ccd828c107c50674c77fd7f4c784a81c6a6b4b31e22e7be4148bbfded3cfd478f8706acfbf8f0428b6b0c3c6645380a8e6044bba8371c18d895572e17b204d8e54a6e687f9380236eac290d84acfd089ae8fa82c995b292950aea1c188a0fa35223e38d73085091a8c8862a6936b34a3723744e1da6f2fa41dcd912b14061b8de4b19e5b032309d0e99c5462575b3c7343b6a4aebac0ac2072ef31b9ea8bd47fc089e862b04e007fabb3f0cb4e6e1217fec843ab8f4507666aff47e882d4d367f14c17d0d2e90c7ed7048186db17dab048e1d9bbb9efc950382828ad6a5417f19f615aa500b101baee484c5c2ae6db821dd8954a31a9374093a8d61f93b5de70dd8a78d747080f9e3f9c55e5c094bbfeb3bb49ebeacfd00c9961801fee73ded65e132a60e8fd033a74f19de4ba291127b50303982034bda3ad174c95c72a019e6b4c6ad439bd071b4bf2b16c4e24cfa7b2f696815338b733c6b441390c0fa621f742d90e6cb90457ad1bdb020bb07a019992a750750dd35c9c14342ea53139eaffbcbe83dd840f01135546a59793bbf054327e3ef6849fb60386542f306be1d92ce0374387cc0155a0ecab065a16b7566ef9809009c4e02eab1bb11e65d56efc370d5707ebd301719bc91212305a3c1e15262fcab95565b2851a42a8efb02491fc0ae62f933a0890878f126acfbdc21181a05538446a8512bc94272ac18c21aa152ff0e1105f0ea96806de8ba2bc3bfd2e8e398c0d4b559f34805fda6e6d64cd9f35c5064c5e0872edb30bda7f405d4ce2c823e70656089e011783d77f1e95231185eeaeff210bad42992af578d07741e57b21981dd04121001f7290c5fd85bd96b1bbae2c5a031df82843f2b4a2b0535c38f415ec0e93cf9785c4e1ab9cb25ba793a2811d460ee6de6289ad56de3f8bb9cbe628ef301e1acbf394d1373461cc8318049ce86607f6ba953dadd15fb6dea6aabbfb119687ce8f5cd36f905d6d9059535efe8d7502c9384a0f5e4ef7645b36a5e516187b20192cbd1c6d3278142c2e829a9980a105e31324a47998c1d0c6885f2e7edbe71913ba6dd8b79e651566f402a2ecb6070b12b2c7e93b62384efd51a792edf02b63f0096994754e846c07a6dd804023680285f9654c0fe2f3cda3a9e52b8e9474a00b8e15f959cd151da93d1f11c40c540b918b68a189e5a4a102fce3731e12ec0dce5184d3544ea659ea5211caed66430616a965962cfeb1c38e31c3948809b1fd8c77082165afc8cc6da51893a3d4b901aac0afa2471ab5576f183514902f39d6779a9a54e3ba5816bef557ca0c35dd0273ba3797375490799ecab1f196edec4f6165e998f83d8e97362559fc8cb5bc0b040b940347b0990dc18bae453b2a705013f5d2a1b9725746ebbc5b409f508f06230ce0876b73f8b82a2d07e09a3ee8987f66516739c78b5335c163c55d257f0c5cf569d28acd154de447d970b9e3320b7022ffb2c8fe88460d9f81a03f62260e27c916bf72b86d8f07185e3f72216557c616d5251c7a8f4cfe45c349fceff305438c8e334ead6e2c5794529edbb9087b4a3ae57f68fd01d979da3d065e704c0e6a2780bbcf62f286bea2d8f78c7a63b967b629d00a16a5731cfd5cf1339f6702c61561248f1e4651d6914eb5db9228f1cff0f04c9a173e910ddf0607b3a4860393e3acae2a0eb728b335edbec7c6aceff8c8f1bf13aef20c589203eb786e0b03fca6e770f42a008d5a4d239957332f887ca4db45e082861aecf991bfc9bf060b11e036d9caa2ae07af619eb1f5ba082f0e20ec6f24279ce752ec7a6d75e6b301d21affd3b23ce9082fb68ae24ce9a04614c072371025c820b4014d530267dd069a8e81315d2117daa4f5b1156c6f5dce53978c7c999d739cb8c3bfc3e71084042baa78a8e6e8987357d9197fea662b9ea2e6b9f4bf986287d987d92e7315c00a827066c843697d7646186cd6e446efb1a1c36a259cad3f94a279ab044c12e50dd418b145fa43296d01baf33c3ed9ae9a402cea0ee2a4175937c8800496375c09bbe92756b2f40c0dfee7ea8cd0a318c209a76b00c0ae42b0787be71fb87e5800efa4ab8f1dd9850e7873de963a5ac14ef40348909255dcd3c90b18053f617a0b50f90298a404163a9c3bc1c041de2e3caecde9bcf4f75faa854a543e0dab060bfd1127c40f03b73526dc16f68522ebcb9fcac9bc1ee66d80cf95e5cc43b4730db44fefce108ca64be56ba53cff6077cb222703d9f28e4a7cab64a8097eb4ab08776b22eeb9e4b724cf731096c1961dacd1b42a0c966343c9805df4c9248dfa036605945617b3933d10997f1756bae444bfb8abf4a29778ef446198455ad53600b6a228a112866ec025e3768f6d5f88ee84184ee17275aca2880c2864f35ebe0e6b13f572d951c83a2988f97d6e702a81b789f566a4c8675ecc4fc9cd7906280d1f474dcd6b1a12a1ec7c5e09202e17453952518393443a60a3f500a29fad820237729e5a805314c114580684b4cf8b80fa2effbe48c43bbdb4d07d831a2412050acf1762ccf5e96d6a8db03dc05b21e3adcd498d0cd7bdbfb196e5ebf33ea6074890ae4330cfb01beec7751cec929ca36c53b46b92bdfff1a9c26907c9bde9089fd18dfd62c528b04ca7637e3c291a0e24ca9565c90ca6cdfce6a241ae68b709cfa503146b532d8b4c44c906d56df944624cd79f8880bdac10102d8d63b05d09dd363ae9fec9282cb42ad80e1fcb364f7582ae97654f232b17345b723880a9044183e534383995cfdba1e890eecb79fb62785c085c966b0a95226f526cb10a046e693fb62cd3a2c16acd4692ce06ab668a178f0af8f4f126878fc69863a89a094214f094d6c89d48f7b743bc9766706c7457e64e53b1e1020bb79e9b0c691406f2f62e0671f61ba2e18f2aabae91e692170b953ae2e177e73c638bcd726ade014272ced1232f9103f922f2dd663941eb9ddea9d13103b622a2f94309bc7e5a0085a172bf87bb0f6b9a9b9cf3d861577fe87570e459710dd0ccec0bbb0adc040f9bdf17fca8217cee35bdad24e41e4619ac237010f54c60686b252c2e4afab30ce89281b0f28e1afadb7af51fc2b61d51adf7432a6acca555c3575558e6f413032488720c172e0865a0eaa3cf8a11b7bf714d76c9eb068f4edfa3270df2139500281e570a4ddbf51d5e487bbb9962c1f6a8d552c4df717ee1ae3b3e4ceb05f00afe1eec5bcee035b1ff64b5e5508c162ce396daa938a952a29cbbc2968dba390ca0f5f97c23a2c9fc2d22ad49d1cf470d3426c5bdc5531b503c11576a9ac52c0cbdba208e30e2d2727a7fe893aa6b1df6e327c64123dac1a08ceeeaf860f04a064769c973e446e0b50b55693c0128ce7d42e3963115070cccb077a2eda5cc5e035ecc8bb77be23e6e15bbc040d6d2275af2e1a419cf6b3175aa0d914e0fdb6201d8e5642a492887bfb713b5a8c75d182ef883020b3ba0936a445685b46cbb74089c605f786e8226fd38f3c92ff91a55322639584ea423df71bb84d7aa87181e02424f4fefcdb1a546cb6bc78c9979e546fcc43bf2468a85a60e69603ac7aea30265b5abcaa22f9cf79e01f6f15cea4ea9fdc9e3c450a5d4f16d8f907325ac680ef926a619d8c4cc94e847305c1b20ab3069fafe02c798e0a79319a84080ba390cafce1ae8e8d098803eeb4fc6164e459660431bf74effe1af78319d94b1cea607bb4ab06c0bdfeb70f02beef2bd04be87eded76532aee885a4e24b23dba353601932d9de456be8ffbe9733c3a7745c8ead188e02cc5e8f98b8e1f7a2024a58004a5dcc790aee025bc3b4cb5f1072027c58f2f639cdfe2d1b8a6b58b7084d22c033320199435aed1e54ecd0f57f639e0216b93c8582a6754a00f7a89ce4f688f0e497b954a725b3a62b7561649e53e73671d61cb4e0318a905f08b4ffe002a5e02307015b2c3fa9e6da1625620a925d2ff3727296172466e9a345f8bd2122c82098be157bb4cd58e9edf0fde10e652ab93d05980762ccf6a3c7a4d6745a6dfe80f3f4d3f2a610ce844cea7887a7b14d9a105099b7e39140b90c17b3990c9401c094aa5c6e1e8c107dd1af7e2958e2715494265d81ca7e362cd9c6bd61ea538ad0c4ffa4d8d38245533454c9180d0eb9e34fd68f1c877540ba5b6c7d8fdc4cb2c02f5faef49567f8cfba896f7bcde64d7e90bdb27c1fb88b114f3ba0794ef40120686b79edceda4109f272c54b6f36bbdbd496ea8f32aea7c86f9a78113423e9708a0672b473b873b6c91327f77fff2af210885074571dab80b69467d639cca150453571082a4229478d23fab17558550efa02a0da8793d1d9f9a06c77ffdcf77091afd82034b8e46db301b248545cba23feb78ef3591853ed78b657c0e714c9c079d65ee2124bfe03f2230d2828d659a6d0424e1cff8d5780f8a3a3a7f59a41c03b493d0a6d22820f98b2c58d1ecee371b5d123dd3247ade42645162c9ba9ed109bf8db464ce6cfe11d9a8234ac3b34c97b16bc2325d0430f5cdab7e5a7d6a760385e186dd93e84e57b2ca645a2eef108d259e2f273f273632ac20e0152e04140b0f3ed6e69d6af352f1a06b49a83002256dcf99ea88a4a1ef175ccd96ce93820177fa68184d3e9cbda09d8b7a9027b0776460b61492951941715588c29bd01101fddc9a4e183b4fc33af3d2d43d33927987889f6bd545d3ddda6fda16262fa30a6c2649fa2cc30df112a0476fbfcc909c272989001b17fe1c184eabfa355a1e06e558656cbb731dabf8d483fb9c49ea74e49555d25a910c1fbddc301cf3c4ef09e1d7941fb5f2a5276040aea5e4ae54361101de06e3139e94e24eca95640cbd0dbfb8e289d335b3d1daad70b96710019d98aa7f74b61f7adc99993f20abd949093b03bc3ef2987b73425df926586b3e83a7230371653d60a9a5f7582bd6c0380aacf28742e3909997d16b7b528f25fa475c188aae67e51b152c2e245744578806c359ab4773595a8a2230b9979cc37267322892233db91a4b3b7c781a179c93004957ad257a75f44ee0988b6e787750abe90c3095be44aa35ece10cedf5472c0df33dabdbfe526a133d0b07798898babbcd3f09a3efed5c91f901cfb8adeb0b0274836bbaf074d7caa22163b722a1e65c8cd301b0e69a8eb232cc7107fa6da60e3886c480ddc25600f698b4b4ea246f22411cedb5a3ae4ffff9597fd9c027170f2f75f65b87979894bd9cf3a4461970a7afe77f105c120abdabdea1889fb8820d5b4cc2bb8011f12123f89ed074a19f89bd7e19e5730ca1d95ed5f4b1ff38b80eaffa55c2a2c05b76745a3cb58db2f0d6f0db013b4c457228786953e5a13bbe048d954b4728e6bdba9f8fab277e1a008b676cb36ccea12e45739c3e00ed031a02e8cff03b7ea8beb9035b98d2459a250b69fdc9785635b42d1db42b81231d820ea5eb39aa9c4767f6b43b7269597c6462c193760333d821da73e6935327fbac084528e307cda0bda9947fcd732ef3c54246e3ee3b394ab06608b87626bc97e30ed23632a6ced000af0c795a1c9b5722417f17ccfb58bd80b3c1a854a9326668099e433639b9b28552da5b1b832afa54c83859971db2887d94d33201e31b694509f7f4d23d772dec780b7148a28d958ac2b4afb248434b9ec5792ad0b04045130c473e8bf72eece12bfab252fb6187f88999472581c8d7ee5730faf7c161492005ac2345fbf684e1e755e88b23b8fe3d0dda4bed52da6837cdf637b2afb8e78105dfc2ce19c4021f908d5b5f42938f367dc6b7a8bc8781ef28dd9b80b0230e420fa925e61db3ab214b0358012d1ae09d7b8ecfd4c9c5b66f5533c4e657c7f5370ac6016a6c9ff46c9f1d29be4d14b77813f3764da1360f7a85ecbebdb6f2607f0524a000f731aaa124ab2bc9775582d91a8ec11b227fca7876bb2233e4fb8a1603154272974ea41da40244cdd3108e182bb1175acc9e245ecf0120c9e335fcbf06990b8655896a4d047f0f63ccdb30a9b99d76a62560fdf7a7c8730380f67823096e0b1d698367eb78832f5230e6688755a549498e971b8c3b335f57c9d3122a08124849cd8acf22e5b6193eb778f29e013ef6f20ce309bcdc4cde42450739020b7bea62f7e877d2d962341c7c04a9cb371c8920d8bcaf3b5b1577f357d4eeb30550d2a7b035b5bd3134026d84f6792524b66ad775da003e06c018b9e6e0622002eb6c77aced104b6a71c9fd2fd5e6dcff4997deb79436bdbcf5f243c9fa613d061f9f1c57de7ff728244ee73744722b3b1f8eff492814f3369b08e1b51c51590afbd8ea37cf8a23a8c4419fc39b64e049dd740523bd6b7690b32d8bdce0bf9f07c75050c4b2c509cb3e4e84643394c978d600c7ce1985805bf8d40839de3588070cd1d0ab303c185889ef8904d2affaa1f1aa42b071b6d48add35e27876939e0100f999941e5b8b32a22208f7a09d753c03bb4d7e5023ddd422ab3848728bca0d864bbca616d7b434f3bc10d86e069074bd618754a5d42b931abc861a45ff7f09f9c737c602e7787bfea644bd33d22ada14a7ed5cdbda889949ea3f2d6ec8ef013360324ef7a6764d7e4d41993d457873f40280327c990536adde132605d60e025f0fc8038c2c577a16b9f7868269a998f238abc1b1dcd86d91d15f9c6b0b63011ee69dd5191c43ebc5ea6b414226d1fff0fc98b5859fe31063c0fc7f15dd5605875a262a79986bd5371db23fd2f6fa7b2e033a28d8665e46fff39c1895ea8608cd0aa32ebe39e8b0eb8e96cf594419901c73de9b168756a3a49722d83c2d470e916b2aade568434d123bb37aa30211a9c972164efb900a152891c20768839f0d2afd2fabaeac299405a92d8941731d86f5aeb7149d140f17a6e950d92361ab0a1244341273ea65ded016704cff76d539a5560258ac11e612e3c2fb8dc2b23b0b2caf309e545b679cefa2200cb5ca63956b7c58e7e7ed79827fbac2cdb188d7030388cd2ccd1dfc41349bb5aa2356ec4c44adbf68bd29f06210717f6b6a854d0182278ccaccfe43b2aeb66d58ad9e4d9575f89b1929635cc7824c1210a377850c605c943293f9c15a890d384bf42f03ac233f2d92028b02900f8cafbdfb9d0a06a7f2784c0c2a3df0ed61e9c4c48d570bec77599f3fb3c4cd60d27c0b41f8b10e087e8c4a9119c724a918ebc5638be8c94a0d88151434af46320031bae18862018b5c0a5cea422ecccfa854d0c9ae4fa41ddc2cf5d1ee646c7ff652de27acd703ca5b9afbcbe46a9f046a3b4711b8dbd272b46b5b2e9860604a8a4ec7175caa0b9e791b3e35c72de0ee3aae342eb420ccf1e67a925ac619ba5a91f9837ac82d06ea9f783db129eee6e93d51f679dbd6e90c39510b6e390c4b764c218df059330dec562368f8e82c244fa6c727ecaf42cbbbb26e3e677af63c80622ed55352710b538273db07c6fb56c99f429c7a1af2913a4ffdb2f52db11f8aa6878c6c5b2b0b1df9555977dbf4ff5fa5c4f5da52e2b44415f541a1395cc880878af1f72bea0e4603069ca1eaaec9792b0e1bdb87d12b8e5136b08143072c6eaf12a0120181085ea01c5864525b04f2face202dd1dc4f4cdeeeaae228a37e6e93dd62fcc8e507ff8361c115df826824707075c8ce3863a5ed56bca381b16a29d3ef839d4d8c0588a634fb9f19698bde67c2f85960b546196b2d503f941561af764750f2ccbd0f964f1aba91908bc5383a86e42b1d12a626767ae2266a7d51abebd85e71adc90a5eacb3cb318b4cf9841c622a66267e4f849c1da7c1918f34b30b981051925c0c303cbd855169d8e3929bf41ceba6ac9083a0e4497eca9f90d858324dcbe1cc0197e01e96953944ec5eae98109276c148dbd8454b843b3ccea46930324053a80f382faf1f50a9a2cbdd9c9fc5eaa9c4dc2b9ef491aa510478d9792c72bc805d0653fa7ce8847905357dced42de31f37569666981abd8ad79fd134817609cad30838cae4119e243621403f3ad0e648f8c554e70c50a573a9b12de6d063a0ea710c6b7295caecc2467b14e6c23d119cdd6a683988e27a427d641097d9b3289b60082c07a17d3d2ce85e2d81a51e6fb6e72564c2d83b3fdd8e13858303f211f82202a25825d8f5df3bf90b4c44476cb27b5afbc2e1332fa5d5407280d268e6d745018b48606119d478b0a6c17211004a141a9d260d762340eda14d37db218fd5e00e9379b467e3d48fc6362f66b7f96d30f0c9f54a56c7bdc925fc19a0305b30420f1abb7c6cd5b70b2f6aa4f7c93e3db7475e7eaa85fd89d35ac303089d4076b5068e60b1df9495bfa5189ce85e9e488cd50dfd8396563710225c182f167d882d08479cb30929b8f8483976a25c39308e4aad44202c71891f0293b6e6cce8b7810f96e54e952b82a733c3dedde9eabf3170e5f2522a4b43dd0654251b8428bae0074e836ff3e3005fb1a1c82233e00be804fd53803bd7759d2d2b805135a0f17f08f5d88eb9a6a07da23ca431e20c5735a6a125e056e645c23aa7d9f98fc64e9a027102f05de3cbcf931ebf190edd0faadac44aad21fb6460121557ee79ad3e9603392dee0d10ea6add93eaf2a61730f0c23697156b41b172197c811186efa0c60cf2792c23c8669dfc57b631f4c255312c01c5581333abb0050509503ad762e00690c6f7b793994e837934fa5a570073fd26fe4461e57387ccec4828ecae56500671adc03ed8ccc6965ca95d5fadeaf27ac9b16c877eeca9e4cd0151c3d279420c70b55f6dd6717129d8cad62d1accbb33b5008676d153a755db1ac8a13ec3d7029351c1a17c4eb10d2f675677233af56bd5330b76b4ab3da25ef737fe3f79e1011d8dbb369786c10854a06dff14982cacbb665c6cc959f073d4794ede912e1d0537dea0239cf59cf01c02a85686812dc24af2a1c526bf2e82918e92b3b6f6a00c024775c6fec3893dba077e51269e15ec47b961819f925726e0d18f8d528b7800b0093e7418641f60c58213f6005f7656afbf0070c794fa8cbebbc411a8b9700f060e12667176ed4dbd6646412034c7751d42ca838db3c567a75303c5d275df054956937bed403331db088a89c148ac06fafe19ef37615a3de03fd3271db2b508166f1f1c8ed6124529297535a7c469eb27dbd934465f02b563205c553c442600f7046e75ff841607086eee7cc70d93ba74ab011909e678bb6b45ae91ddb0670ebf3289cccbeb98df119488e228cd0f9d7c1a0be9010075faf4d9e2ea0b72e90d7d6ed0fd8e8e369992465f83c04a49609656e41afb7478140ed58966bf365401007d466c688664c9fa2061ed008f4a3614b9f651ef631b429d660488b0b30e033002a699da2408dbb877ad5b1d8e0a7a651df2996d9d41b10e0234c78169e009f3ede121b2c80b7832496679fc1219a7cdb048ab06be00839eb740cf7164110c11e1ef1b2aa204e8064272ce4c142744a20e93595bdb9d0964577244ce79bd0addfddd902f85eb5ee839809647cb34e13a0f01c3cc1a2dfbd7ec8ea477e02409e44617e6fe7981c882919114241212aab2013754b7b66656e892d63cb3be82096c421f809ce17560595af57e4578345602d0c448b40d32c6192a05a8dc377309f6c03c86334397306f231c3b63e3ef152b06f6bd2553ef10fd02e89d9c0e0a014489afdc53cd7445d3ff3ae1babefaeb5c580b9cefd1fe27322848854756d5070f831a406ee809cca27bb1ce73027879a639abe6433489845c8d8060c8af0a01f2b9f04025daf6cdbc4639cc9a20031148d73ca3640c2b96d7cfff9044320b07446fe1af874ff741fb7a626c9a87bd99b4964c1f52e2876c6756f78ab8172704d0afdfc4d93531c9ff839b120346cd31352c31e4d1ed6b4e41f4d59a1bc3450cb0e6ca3c711e3efc8bf73065f8ad6566b9d4d7ec6e8dc1a74733ef5d5a7eb20b73a11159b4552d6149a5cef62161f389893d9bee70792c1dc4328166aec37c0f24bf63a1a1cf456576aebb89f405fa1d5e96e3d3fe44c07dfbd583ec4fd6c40ed080485d844f35be2b856bfc9d9c444beb68c3243180da045afadf292d41250f01a0ee65f73131fd931184dd272b957f6d01c7f1fab57539cdbac06bef0a1d0afead4e2f8b46d2e3183fb001ea2467889e20c5f666e2f5d47589eb6518dece031b0db48e33e87a21d6d258bdcd24d9dd353226563948c40208656f889852910a07002b8bbb7bde2093466d5700f124fd6d9ca7f8d00acad20356b10439fd410e9e050614364268d392d975da9d837fe281e4b78d81a0a56cff2c30dc844ed40254d3c73aa2d51437e276c56d6497583bf42c6ffc7a8596428a49f5743976450b2e3b4823f670d038e2ae12db8c650ba4ec7962192f479b859956bd952168a808159691b912f197fb98217426a8b6b5385d93d6627ddd6be0c3df6aa944bff10fa32f7c60444f6bcc460417acbbff9adef5280360e32f61c35efaaca7f36a0401b50ae6673e0646ce64f527876024381a2a096d3b3d2d3d739dfea7afd6940604c27db89c4af12437451ef23cf034ac05944617a19234b9107eb65bdf54d7c702e2b88a4ab3fc44c7b0d797e70bbcd73205c20a0d982c3e04c8b2d65ef2e8f2045192b64f637d296e789ba72d4bdd13ef6621a97eb692030daf6fe37b4191340883761b28ac501f2196173af058be041bf42ed2dccbe507005edefdef1db7000ee6a70f3751b08a66a30f8fa6e1676636a33e2776e7d3281760013d1fd4e1980df6ab95dd154908254b248efaea1e63d279b465319e5f9312133da9f157496500b2ec32ade20caef68905719e9d186b613228a69bcb7028ec04ae818c2d601408ff5c4f1c88461d1a03392dc1c7103ea33e25976ed50fc2d1d0df87337bc95d0d4b8372735aa002bb94838436b278979bd3e37d46e7e98b31e8b2eb00dfaa080f47061c6e5275960c9a1b27b54e6fc391ada47d3b86c5a1d03eccd8084a136b0eec50ee903679978e29693964d5ce1c6142bf60dede0129917898c2b5cc811b01ed4f06b713bb059de3a4ce2c563cfe76218d7fab39d39eff5fc224ca43d5e703c0c2a17e22738baca1cf014df191de8eab17680d8971b7c3f8ef79245dd66f072ae39c4d1f2688267a053c604a5b761a30a904fc4526af5274b25cce9b206d06cc840f5ff52531a81bcd786762117cc0bf3f8d32b79d602747f02b8834a6e809f5bdc841e92e7ea9106852a785d0e8b4b06a953d44117854efa30a18b3600000f51a8b106bba84b3da14393c9016b8fb1d2c2b14f8e9056a183de02b4963130948e5228ae038d24ee2804995fa2269a447e56de83b42e24589d11a4b191f0205174e7610a308d8d7935912ab83c63e6047055a590b374767f6dd59bb210dca0e33dd7f8738946f59c2a33436c51f71558354b51fe9ff0e4ba465fc2b71e9790172478ce72ddb2779aa3fdc7b6a650b0fb0b515342b4a553c2ac8a717a526c40873a2c912ff23ef5f4c3a3318f51a369ce989c167688fa2ad999744bbf28b730780af0836b2e88138165511bd17f1e77465e9785d7f4cb814e683396e20da37040ce5e77b5ff607fa38e769d448450bd30adcb397d6771ad0c20b0e9cc4ac29063499d2bd323094aa3ee406184c6fddeac2469ba61095483f61be306003a6dc0a41e0a1b14d6b65ad9c2477a884062dafb551e42464928898606d702858fee8027cc101e683e444645f62c4509d6cffe43c9cf259850b2bc02b6b9be3b92de409d4ddda3755e8fdb0ddec68267876166a6613c64320a992f020606c87ec88a20e295338805b407327e8e2b0ab7d40869257b2675f1d8937cd486746aa431de20c7185be7e083eeb8a6283929cbb52a7b0b53c21f5f706d1c36f93cbe4d3975908425b7869a85374e0895d226099e3907405cd1858ffff3b5e0857d488b2a7b40a4fd5f14a480fad18d1efbdf2aa0ccd269083577debf4b1eb73a76c93f517440abdd1e3935edc2fc5143e73a908c8ee75e5c78b1c6b4a38db93a81e4a0f9778050be6f9e19682af9b9da65ec9fa09807549bdf0d12b66b4e55fc82bfe0bc7320c3d5c4a82fb9384c7565cfb542712f6190ec3bbd89b5f0abdbee8c6d1bb065b09f5135f9f0246b5f42d664da17164b9cefa3ed421a45170ebd28b2856f6ac4b0c5dccbc052b3c4a406046f5dcabe0b0c444ecc48e61a2cc0a93ef022dc25ee5041f439cd52bf536e8d05e469e2ab59772a44f3b5597e23d7dbd36db4d616f6701887da077d80fb9c8c7eea8f21eb699dd4b7206fcdcc696c5cf947fa41794ed0fb88a914c9cd7ccc54a03faaae73dafc3ef44cd0cbe9334135a0c919cf7103c00ef004e7611dcd5444005e9770abfd4b9dd8b779b2152216f9351d7e7ae10ca0935f03a04d7f5a6433892200c2967f7cf95714b1d3ed7a855550d0891a5920c052607a40df1ceb2306f3c7afda563f481155738cc7eb0fd4a2e8e297d02b5f8095e72d4f7673ca79d84cef4c54d8232a948f01d2fa232ee25ca68cca823d5340c641acf44703e17db8f7d3c91ede3316e2374681f4a8ce817733c10e575415609761f1dbb03ad6d761f7e10201b8f067aee6d84c3b84de6d64d401c0a94348f05551b0d811205aee855c2d675fe4bc2f1991a80f8e39baa7ec1ca9e1b8a48c10c189004b1f52541c38f654436f971199cbf0fbc5548a39ae3d0058caf64eac501166bd366cc08fd9f12622683ff7de0d4c3acc33685b4a18b398bbc13f32aad0eb513b4944dd4b006adab78178f71ffa9e8d9d3217afe19283ca5fbb1d4383609078010b21954e09edb71a5ecbbd72d0603e6395ce25d2c066a97b94d6732280f9c5c807e912751bf5b36ba1dae4a3f725feccedb740e7d4a273da96934b3a7030b40d0a18fd3932b14fe482906af27924395f28763b4e068cf86ce8ecd20eb0207147da79d85c77281ba79ffc0876441ab14b5d23e13db04ae459e752a1a3b0b2c41fc23499f98a3aeb0d31c2e56300301363d932a8c48e6582ed07cac65e1063d9c66a028f873cfe96cf581fc4d0fbea94358fccbd87b29f21e25231cac19011a7a76a8b7cea34f5b2ecd278a86acf3e7658343fb93c354a5c7f0e141fa050c0f516dee7bea5c35b398524f3e49c8b0f078b35a79831d5c548e4a56f6f0dc0830ea88cdb22d257608c149574e7e39404bf0e75fbc2dd2dc72cab53d9c099c04621f903d7d7bb08531ab702f05fc8d984f122dbb6cb5237288252d84c3d56b01186481ea688728b45b91224e10fd2a3e68c798689916e27d270777b1926dd90df186c265427948c67bf1b46097ba9725a899d61b987ca70574e240962f02050032cf47b1b7589575edea7c1bd9e6a055c087a9109b358526480f6470a24be406c145337c0f2205b861920dda31f6bbd57f17bb7dc5091cd4473b5adb6aac590f7cb7e8e2a005f4b8a54252ef4fe3768568ce894b56174d685abf80fce209c20820b1c9a4776d04d9781fd3b49a4717ea8426f4d5ab8675790fa1d3537c41410e41f2b1e573691311a1e1ceceebca95155566a3d154865a03b429a2e2a579b70e95eead357d6178b5c2ed300feafe558f40de06079780f7028e7923b0279e0d0b2c2f2aa06c1bf9f2838b7d4b6e5da99daa9068029595d572248a9ccb7b58970751f3d506c6b8e8fe436b562a28656cff90b56df696b178eb56f102a63145720eebe88e2f471d7fbcb6b9f367d05b653ca0da921dc52654593b53d3b44fe2e50cb98a3154befedebe973f33f9ab36c8b79cf8d2645f6ff7aed1c739406549700ceea093c34a5a040e39740546cd9b36d43c0b4883154e74f5a2ace94ceee2610160d133137b6e00b8d075b7156033ed718c4d7375ac4cf35280726acd13d91503ffa7d46c887c2fc764b39a184496eba03113b49dcd57b5ad99032a0749736b0d3ecdf282068ed5efbd52a5ff5408f6a5a5ffef062886c8f1a7d67190faafb30d2219fd70594e2e4c371bbc77510f10de9c517ed66b313758afca036487ff18023696161642bf57f5a2aac821db1d5949810a0a1b722e0436deb2dc94a67c6001da71a2bee39a5441a9b90cf30f7f30e675375e422542bf6d217d7987432bcc03ccb5c143e3140dd0c1636e93801b1e7d7d361734c884145973f019760099df095c8b19af1c46e8cba8428492c46ff802b720a11b97f6d55f3157718ac1b47b0f7742ab6886c5ee0b5de548ea69b2a451b31d39d641bf451fddf5e98685d6a40258cff4c156a85e292b6f452df7cf4cc0946edf0e9268839e4175f2481665e2088abb68739d1b9f93c348298ec38ae8c4b325e2ab7c0469f4e91bde5b9aae06063cd23003e3b8f1d8da11730cc83eb8357f4d1312cfea06dc47240171e469e805cd472b8269b22ec99c1792594436fafd734b265b4338567d6054888727e9d708f297035ca0e4aa2a3f2bf5960d5012d01d0f7e934da0c25de43487aa42605d0cb9f5504205005030429ef943c5e4880189aa6d96b9e3641faf7d19a7dc6fcb067fd1b6182e38316e2d8351ca1ce92945b63541e51405b224fdb9f1e0b6c1670e +generate_ring_signature c4f2c0a39e10a7d2881c8753890aaac6ac136088a60bfdb67d9f4b17f5670464 8330fbc47f5ef2d5bcf3109e30396640363dab70a4760847e1ede29a99a660e2 112 e47318b68aa07faa35ef3ae5b8ac38acbb0fbaf3def0a4bcfff10395650c1eb3 0059e481fdff16f9b76cd2203d707be8ac31d48614585fccdc232a1e89841d2a b16f9868b930ad0f327f095f2f58eee17344ef219e5119c2a4d23078f3288af2 d375e7d98e7f286e38459c0a90a742c0e9db87f173609b3e088d6b0d1eb3a71b b8aeaad7d8293c44eba8932b8d7ebdc654d48ee3bb99a2ce9320b5356c363aca 016437d4ce1c2ec84d278ad1151e86005ba0757cdb110145110fa26b74038590 70bbd94b724d740a11d7d4a816c4c87f3eb7bcb2b17004c0ca11afd3671d6bf0 8346fdf809bce48d576e43e8005cbd6290633094eb508f7b1b9c2a8225a169b7 d66885f9ef8d956b5f7f65641651e31302075ce1b5865c5a241fe9eea9b29d22 8314224a7d53944a56d8ca1db88ce4f86a63b4663eefb779cf8153e2561fe761 bbaaa6e1e54ca7dbf72e37893f2154e027f4fa16175be97626efbbdc69586e6c 113bd03d75850ac055e90308da8b18f336ce2f7a33a88403dd26f5252506311a 652f8202dd69d0d86855a85f39f01eb961046f598588d8082ff4b4bea0501af0 24342da5fe22d17ca26d88f21357e5d375dea5e293ea81bd8ddcd5727213f196 392997ed25a6b43ff9f800a04131a81fdd28b839bbcf017337d407b145856907 abcae499ea45e168aa579ca1d7f5cde47450cf61bdb768b74f38684227be9a20 41793fa54c828bd0eb3a127ccbe27c4400055b2e7199ea5c918b88ee0d983314 c57bcceec6f61fb7de098dd8ee46f9d5cfc42cf864306601ab6dac9229efe5d4 2f9c53e941b12af7d6d1f49617aa8984be45080837d56c015ef28041d70bbb6e 16beac4e9d17d70a7eb517f110d8e7ab5cf60b4234f0064a884330e7af8f8264 fbdd6593adcbedb594273cdb4f71d628e6922f572ae7b94d560ad1ee543e17e8 3cc322f308c404283bc3a5476b262744a2d23402b4c8edd729b963a1417f08a6 1cd533cdf11e6c7078cce85de6290555fd75411a556c128a9962b7dd6f4d5c09 51ecac75cd208d0dd87edbafff96006fdcc82eaafc0a6a4084444802ad396758 1409b737766520c2d4789b6d59c26341c7aa1ed815f5674e794d9d0af5d83647 ff676950fa8d97c9c227cec21314b2970fd0e7fd688c64737a246eee1ce7167f ee4cef48d1ecb46fbceb2db3768f26d5d41424c5c7263e5df8dc206642445681 635b8c1a27a5ad357b46ed43f5911da74f20df7538ccab7afe574a4ec152b4c7 8f1ed5f84e8d913a43bf95b9f1566cfc3a067d30a28194f3fca347d2b937851e 1b97e52ae79c274e456980c2930f47174197392c8576111136cb6354d3f844a5 114069c991bde006821a4bf4d7981168046477d02fd97826d812d9f16f77907a b655863086ef74aa89ec9dbeb65bd766bc59e600f2fb91c06de972d813eb2ce0 a0f739a4e1b36c8cc807d771c84e081ea83a49ba98e194b0604d2d516665b778 58fb8b5f2a78e34edf2caec39b3f382d5c5af7e6efc6260e55d139a7371d25d3 821efd6c97ca6faa08c9b14624e4e143e52cde411d27c05cd1ad1c4b126f6e67 b20135c3919f7efc856e39bab403fb72d11970c31660a418b8aa67a427a0b303 fefdcd2c9f62d2788aed73c44a3632486974a7c146d5814229132b136e21e413 db6b3594fc8d6e4086aaac78f29e786d988d88818f175d2ffda41d5a472711c7 07b7567fb88ce0f9f467d8c42f1432224fff4629e4e46ab69026340c6b2a7e43 a1644980560aa39caf5deec4a899557be279f3d9503b973450f08c4dd6e82fae 74eb226428afdbcc7558ada5d79a5b6bfdcc9bb8338e75b750a27ccc083d919c b9239985dec6a42c08999efa4e7817fdab6b49301f04559daa1b80adf42b355e 10113e293205f5847c7c8d8f41083e82f3a525b5f56de04359706e5b473babe7 62156ff12c819c07a5b399c6a5694cf81b3cad3182d9c89fe9493399df4d7fcb 0d88e5842e2a1507642926ee6f947772799e27c4276e84a0a377f28e8787766d da17155ac68b93a53b3450cff5c3627b633b2b7464d3f0a74d9f47bc7a9b63fd 027b33143e2bc160670358bd4f206b7650ab3ca6120f163eaf7d52b1f6419ad0 38dcf4d9c7f711c8c3f333ecd93ec97e22abc07670a8c8658e6277d579923d7b e88323c9eaddcd13f3348bf85f5251f593f034ade1ac3b5309b0d542443a5e37 fa14c4f0c750ce4b52d4811caa95d3c9a7ad209966683cbf083e24d00e1765f8 838365eac90f707d698b76078f9ec1d896590f9adfeb5c0dc219d80b42929d63 e599e66bf44e7fccb2722f3a97df6fe782e80811e957a6d79108dd700d46deaf 63e1ac5f9583e1463db1c0dbea5f5780eea86c3863fb7f2f7144b094d52713c0 f30040af05b4f9d55c2cbe1de1c2be3d0a464a3c2a07590ca5248eaa76947ed8 f293f6c22c31534998d09fcc9f83c6711345ef9acb4799297af1a30ba50889e7 6adf66f4a7655d9e80dd691cbb6ba6b0c34ddf01e00bef4020f5fdd993bfa62e 7413a65c5acb2f77d01af852e1d17d05544d767f62761ec4bba98ebeab91db0a a604ccb842e25ad2739ada5513fa55c922e98a1cb47db75cd945fd1821a8a84b 760744f0d42cb0fbb23e3be8e04860eeff2c489c93e39cb7fd3e644313376de4 d3319b1bfc438b6c652aec300eaba43a8d1ecb0b10ce7b1456732042b0d6c219 f8a118056a6be318825229a5bbc9800b6002522e577ddef8382d272513672bad bc8b2c33bb713ed0d4cbd68d7c118e312fcad230965cb8086dcc60e0f725ce87 1d7bf114a3d4e60c584046097d308779be8ebfd81bc4cc2ed7434f700d0f877c ad197648d3c14b3a127c04a02b1bea5b8dc8e369096a3dd74c6eb1ffd9686449 6d70e6bd2eb0a115f51c5f4a1a5fcfb052340110b9db067916687ec8e5f581d0 5708259e096e2c0f23724fc835626eafbc11e98a8672e36360ab580e8abc23f8 64959a011b73de580cf315a0799bd7f4e24538dc149ffe9fa582fe16a1389a4b 987abdaa231434b344aaf7dbe32040701e1c7a5bd7cde69e92aa17800574d6b7 71e094387ec015e8bdd1684f778d544e0868a27a2cce16e3245863e279d36858 297bca83ea72ace5734a5b734743de740fe5d17f2d9a61afc28b5d3174a4d899 4b676cd079de1a9222bb783ea75c17ae3e16bc08b3618e4ff201a9fe849c042e e927115998105d74e0f76b3ce1c3a46b0fef5c68f6061949c3b3fe7f21a321c6 95438c8204ca32bd089293645bc0eafe8552fe14ec29a232fb1ad4fc619932af 5b8d7ffb49e11b950d8cafae7bc3319c9b399dae92de13a01ec7226a7821fa65 a1a64ad18b13235f8ac0c33eef61c152050f8ec646f4f92d7fc9848ad59ecdc0 d728a9547eb0391c9009cfc4ac05409c42fdf5609034354004f0fa4d5441878a 784d8afaad3bf0437067961f1ee47c71e609cada0b97c636aa87a4da8cd640da 9e562362de224ee6178006c3f3eaa0e211fa9c0b9e4a909659e7e0d2a28cdd48 b31b9f4a7e801c99f98d1b4523457a434d623afd8631163f7ee99e14a7b6870f 9164ca548303e3f8e0ff9614e064af82f8cedcec6fd76541210bdef127eb450c 82d00ea6aa39897b10534f681a739925d179e3c25f87d582982988ff8813d4c2 c3cc009f53709178261b9fd064f31bc3397e4652f37db77717cac41430f68b0b 3c6a52119f73f153c9b740b10e160e88a878a15a685c6e426494c1a190bf2ba8 787fd20038a29141633d628f1d9be6c78486db3256641d4c5bff7bc6ddf3787f 5f8f4faa52b8c26f56831f67bed9ceb8a3e98243b5d89944a0e21e78c523be86 ff57fef6e81f2c1d5ea0f39579e30a2330b7b30058d65cb5100f6fb91db481e4 6faad18ac496e6517c09c2ffb99ef74ee010f1a70016477aa92f04f1272cd725 8bf8561b844beb409a9603af71af4ffca292776452278995a2768f924069132f 0573f9fc57a663c0ff3b2cc35f4301465e2c5737ce086d2159adb28100a9d8b1 44273327e8d0447d0205ca23ea4991f0167f82a7dc7dc9a30040eada716d2855 223250e4ee92ab39a66d8c5d10cc1d859f8e129522763c03afd9c1ff982c8d3d efc9951e85bd27fddc0dad8179ad34a125b21a24f0f27dee656e5be965f54dad 2b31f9237409aa3ddd3789f095404bb3bf9557c7d1919227a4d812f2eb7afb9d b4d5426a38af058bed01586fd89efc34fc60cfbc95d4a3a34c722e05c5457c1b fa9cdd9ee59c34fa855609a835d3bde169d545ea653702f0a9b7645c8a8d472f 377077cbe71528c3b0bcf76ab93ba7451da7adea27d31234099289b05b22ab1f 8e5ea1ca5ff58b0d5e73e959b45a650332202e6e2c182672bdfc365b4b19f550 260285a632a4ead67966de806b34a32cbdc69352ccc25299509a3415bd6ac2f5 16c8b44afca503068a5117a0fa39b1cb5866672ced428ae720393e8140e4958d 6ec0f1ba70f1bfd60018d9780c58c09ef50ce619155a91fed21516d72fa65549 112cbf0a773da739a875b800bca212c1dfcaa8a7b006bb0b1cdd72e197dd5fb4 909c49c403a2533b78a8dc96119852c226b8a93e302d9a34ae910a8c61ecbc7a e16ca70091bf8859d39a34dc2fbec79f00888832d287e0bd0a3442a6067b7206 b41959aaaccedd4c7b4ad47f694421a9ab5f18365cf45a88d85dc4076e87c649 aa268761491f71d9cc3b911ea0db0efdc35676bcdd5194952d9d0f1dd82d18c1 cb1fd93f7c9844cfd85a4c0f89f5a41793bbc43cee813fe3d5ca10fca6538b04 9aeabddccfe892522302b7bf3e04f4b94773b0bb89b28f6f19e24220495c0caa 81cdf2134d20d8536596154521f3cc259e5b917c9cc4373c6f7adfa0b19e8009 78154a42cc5ebc11375cc35cab1a274f024301edee8fff0c08b5cdb0dae37c22 c29ad72588fc320a019c6c102e50ed32ff4123df1f8417e0e3ae315b8a6a31bb 5e053eb278d157296654a846a072b83224dfd088d8db57abdcfd8de00b2a8bee 5804ac28bb2465a4a879f07c063dfa4cfb2762fb9015da611c9b36fa62b10c5c 314e8a0ff8ce6e952150be70fa590a975e6629b6d6905e3f6233672af926dc0b 46 1fcf7ef19f675733f91dd4a8207d5c617fb263438f64803e8ec140d75cc0d00c6018706f263c7e8ae43a221e41f664d1641bd89b2697dc4ee8b2d6b9974c400c26c9520d0a995370e7b2d52759b8dc92ef93943fe5df0098155cc454d03a660f354b7b5d556faf76d5b926da008aab440545514256d1867de6ebb672e7cef10ef07666c0c575228094bfb5016fb28553042eb1bd880228a48401d0bd6793560b0186bb00e0c8ba9f9960d068633025609e6709289c6c6ffbbec50cc20adf7409b6ee102bed2e085cfd1d9160d1d3ea7581f1023ead418c24d89d4a933a4f110585a54513357e8ac7cbd234a835bcd53a9d1c5f4832f07aa5ec213c9a6736d70419bb412431cd4dd4c5ca857f939677188560c4c2fafc6053bab6e4361d7350064b2473e267d14f4b2bfd87aa2ace397ca1ca62ae417b35e497795573a175370ef3ee8626412dcbce1593f6e1f7f310b550bd8b54c4f12296c87b1fbc6ec3ff0d2948789a4ca19105d67235fa749467ac14682d5a5680b6267bf38bd4c589e9082759940fdb611a47eab4a2b92e5c51d63ae2f17f1aad8df975125a4d43e79c0c00421cc83193dc826cf90572e57da088a0262e9b0851c5e974643263e445c900d60f3d55608f081b106facb4d20b1bd93f80fd95a29e5fbdd0886487a2dfdf0f1355cd98bfaedf9e0d65ceaf59fb575b346b70d74f4a099966f8bb77b86ad60991767c375bab84e221f5b11bb693114a2f03439f2b52939fa7f50bbb4f804302ab40e17a7f04a23ca0890b6e77cda648c3dade5ff6debd9bd6ba2edee9fb660d018aa4aca839165b06f1c3377501718eb80f4b49a49a5702bfa871d8f2c285056828665e0f5c0ea785f39f6c18a29aaf7b58a0124483c9c33a474b597e317f0bce0b6562387280e261d94eb5070f2a05c8794612f1ff1dfc3b7b9b39963aab07367f70d908e4bcdffb3c9f0a2dbe3fb3d4209c7e046bc780163e628edd68650104052ffd574e9bbb478f73e558e70206c04ff0fb625b4a537ab0062bab7ffe084948c3db15498f5d4a5421d7a7ca5f4ed6d6d8c13969ba65e50ddafa1e3c4f073ad0be8e94a7156b642fd05393d502ea31c7fc6de80c540e4410793e5496bc06b2d74fbb6acd6877085df8174566d60bce9c74a055a1b4260e65379f2ceab908076f5d551c5f5b7d8c272f573a0e0e77163fca0d908e48b6394da44811c1e20731635492dffdbb3f625f0d73cc968a1d31aac3e4d7b0d62148d1240d0cf691041ff63f9bc0117374591a7cbf75e940756ca26f9ec2cadff72800b50af95b1b0625215639d06ebf0b502e6fcd9cd3cd9ea58b899c5ab9cc11fefbf2d82914e10acb7a5d88d2976df2fcd6e76b5777a3035a7a0ef8d862363bc1c352e32a3b25082a6046fde3ef65e4057d0d5b49f5a99cb7c9372469cdc8c52514e57acb4ff20efae06293f2650cd3cd15bf74edcfef458f16b9f258955674d5cf336606400b0fc363c21ce6e1665bb8b3394ae3ea1a9b5e36678da68b8956ac0ad9f904f2780bf03b6db98470ef4561777798009e858b53d83d5111a90f58550e73dea455480d6476d06b41182c229fb44296b8b3e06040ca154f31a41c8350532c6118eab30585c145aa3252bae17c5600132412e6bea08d343815e172d7f10386c9270fae0deb06ba0e995671c98fa279141aa7329970d53e762d1308303726e1f067a39d0509f30be03a052e9ea26e93843cbf5c5fb4b925ea78a5c8ec7cf00b925d079e02dc525662e01e6facb323ddbef6f6a47e30d4f2839b4d5813683a35bee0035907320f0affe129cfa6640645a12ea630445351d62ddc626d7ee9b9ba57a4005f0a890595e1f2c4a61c22e3a8cba39f4e8e7dda7d59f5ae9a1b440129898d6f3208b5502afdff42029b20a98ba62723c16fa2f07291a6d769bb062fd08e054390094cb4b87e0fb32100ea4dfbab06f8564e4bb4fa5d9454bcc6ac4e1cfe615d8e0446d904249e7ff62315742268d35e23bea759fc0f0122e3fd9d820d429a505a008d833bdff859f59493e5e47e7fbf0d1b4012fbfa778314a7fc88b00fd23bc1004f7957cd14d2f28852305c2c7db50e7fb90446a72e49f886251e20dfa48c850b01cf1212d2d1c97d983942c004326fa8657d78c0eb6fc7cbba32dcdae3bb41074ce6594a35d33578adb837431ef0ca16303bf35431dadd8e0481aceb151df700821d546f0902b173475c562d65e85312266370b97aa05e5ef6c9b5b5d043ff0249d051fe80450c1cd6289db78ea29638611fac124253fab2b453d099fcd973082bf12b9e810cf5587bd43b2cd50c53a1bb544df0a674792fee29c925ba04d40bb9caf043f967ba9ef9c03a668fcfeb9774c5914d231985a794d10aae7837b702adc438a2cebb9afd2dc7aa9ee4d8fb448b16a8e963015674eadf289338215c07d66e5ed23f72d02bc316e861960c414a45eb69309fedf7685506a07add025b0c671b5b5883a7d42f9aacfeedce9ffc579aa19e14c08d4a9b21489b238c065500d64e3d9605d9ec7106ba103b34a498c03b10cb0e2ef5565c45a72b7db6b1d5067f711f044b2aba03b6a78bd42b07ef71431c462e8034a5672682a0619c5a6301f55f096a3cdd405e538e92b10387369412497586769fe931cd64939cc5760c0c7bd12b399546a1f7140795b6ac8ce0652d5b2f8ed6c44baae2d61e4a216239073661df3a8121dced23831728e2cc697963aea388fb2bb21dc86a8a34a09d300591c9336f48ec25cadfa0a74e66d190b7cf9f4b825f67ee9a5db593481aca2a0672481feff11eefc7b1db169173b591efc318c1348fccb31f21e71faa9e575e02f862ffbc8578bcda117226a5acca369bbfbc50be304ef0e774e9488b1279b305541012ffab6a015b55f22748158a72ec38c6292135e52620a9b8c19b30b1e907dd6862505652b018ec29a4f268a538c26ac2b07c76fdd58e6f02e83356b1d100b5327f65a24e2d4c19603f4506f2262893e6b29bf2e5ea6b17bd816388693805be4756ab75f2538e89a4bc196395069a0354112284730ca74f83c4fa2859820d2999c33aeaed013c54c1c6b53404a4ada1416060a6625fc23a2f31482dea97028839136beb71935c1441e7736e0cd282ddc65ac21a4364c5a15573f4eb33290c5e3cdbb84c0f4e79009a41c7a10de61dd6a65076c4e239ee947226be924f6f0a5cf8caafd27772b248a4eec556c7e7f490ac9d614dfc8128a6d1de5078137e0e824d7061e7303327224970670c613b843a5b0d48c9692c18d6554baaf6c6cb00c82c0d3882fcadd5dd73859c8203d0465d2f1cfc0d33801beac9ac4f3645c700690bd497f538d820299dc08b509dbf9914547286f558b18ad6ad38b0c90b4c0e7d3405053a5cc3963c487d6674b3f493a2c933727b78146d9a80c2b4702cfd0810014d5a975b1ad6096425157ea0a970c5096366c5e03284e07a4d2dcb1d3605f141e7f0d3c0f823844cb20de86266da8d5471ea51541f84fe73fa7c02bd5b042d56ff58815b8951f723f63b635ea4d2db7d1284a613c71cbeef9c0c13675b0079a22f520eb7ecd63b819dafd026ba49b442646067e1705ae967f46612b029045d5f2b3542afe57d3455afb5656329df7ecd80632205fb3b6de3bf8433c3800bae113de16afe42173241cff67f4ff1b634c8fac81d9a14dc736b99669b27d80072f987a19626b820dcd22bc88f7914f1a9fb10a456b106aeee1e9263cb1c6c079a8180ddcba1ac6a1b411967b9fd716defb6f456e1508bbf7d2808d4ba559000c2b57f62ae16283ff7e2e144ef2847b5dec2803323210d78b80b5cd798e3910d6813363c7c3b3476d0105f21591cdba1eb5df9cc1c051e34c7c6ae7c186f2908f12abbbfaf07b6fa0184ec60f18ab4f5ff5cd331029c7c632bd2bd9aefaa480c6a59bd4acf9300ecd5f2b352ad905c3fb0b2b1ae1e9aca717caf9f5dbbb3750393de2d421f373bbaaa6d2772839d8e5dc45fbe3d5bda2f544ab58d578351b10e14416add30cf23ad95370d404e703c4f64b042c04d355671666b3a90e631ce0f11e7007027a514823f7354976272f14191acc00c6a937a518057d79b4ed26d0aa7880bb71fe6938cf3c3911e83a066a658fc653c97b8fa00795c4a95d4c5ea04f73918c007c9a758104147e4562550cdf3203d3eede59deccfdf8f56057a980d29efe8a5efc5c6870a76b66b504d5cb9b848c545e73bb5daba695401e440c201f631478bf56ffa954e772987ca8c2336f8855b8dac6a547090bcca325c5b020f29bee4240c111025ea6e87e97301fbe0f23b81095902eb6ff7528c89abb37d037796c1def752c3c6e48e5addc2f8294deca3a27e5b02c016facad87eeee52c034678fa092605ab1bd8b1bc246f2012acb4b10b8be70a6cf0210adf5542d377041af11e95833b3d069bb9f56584b64ea09f7d8e0c81a9843b18c902ee5d544b0c1a3cab85cc41071a96f3a532f9c1ee0f4739e2c6644e8db17211aa94e658e5084b3d7d096b543340307467d13cbb2f19f4c29f9907decc7a1506e707af264d0528a7b66bbee451c535ab394691f9a960e85cbdbaa98515f9322ca3dfb11d8f0f954e8179164072f90cd9e3607001ed790fdfe7da2f0be821142bfaa5246fcf0f2bfc7950dffba24847f0f6dcf83f15f6bb03436e5e4f7c90aae0205255e6c30642d1c5dc71ca6895df222a16ad4ba71aea8d3e08e50003c96e3fff9566a6670a6debdd28130b43a6c4ba97aebdaee39703ae4901742d254808026affb5fecf0d182d9a0accddf7becd0561d3b09ac1935845037d618690788b54c032e9583a00380bbbfe6c327309c65a62e1d52d9b310e217f66b795e39830bce54dde81f800d981a58c89f0fd73c5360e984779d4912137f1ac3db98351bf31815ee69f51034a0f34050d57b1713b95ff0e04f80b5c90c98e62c284d5d163f0834611209f09a8f439e5fef0b8e1700922c27ed3f735e04c4b44a7e112b9a93e0d7a35eb3d0c9271818541b912251e336327a90351b94234f1e68b16f2d5e8a449fbbcb50d0179cc4c1262f883c1d8ec610de1f78a51964278aa5cc682501e6cc26d069d070d372946e3e647e44448160273a0a0cc576e3ec9f094eca6c2803a025eab8cf50573d48e9714c4c13ebd8603dd4c1c5b790bf24afb8a477cea592fd278e9d07d05b75ed86d75bb89e340f620be4d7eee61402703916a3dfd88285af2a5c3ec370a20941c79f0ffceef712d922c9501d61a774cec163e54a54cb804c5f9982f210d00489a33637c59daf1b428e3c4a42d25c54a917d206e5ba5fed7fe572748b50c8549df20fa19c5bb041e303ec58f4fa2489b4ca4426b6dc340ff5a6636d53806e05d03998b5bb41eccde4794040a564a76590068578f2315f9eb5013b417b909d62856e9ec7226cded9ee1b9e3a0b138aa06eedfe4ba0ef50676d6a4dc4aec0d1d14e4c0ff95d02372a3d86e00e720e5da906e54b8d1171ea45a7f457229ae0da1c298f4bb2dcb65ca2b6ce6314f329b1bceb8d537d193441d2177ae5f6a760830b77a027fc52de393d317696b1e5f5539e8c03f827c0bea22c3ae55b5707c0914b6b46e29e8ff49b1a1d587f862cd923e02a21930c056ab1981dea1e9220f089f66de862a1b847433f09c8660d1fc0410e96d1d8b56ba5955213f64a0fb120e9d0f1931cf023f7e30a83d035a19fad0eeb7a094e0631025046b25da73508e0ff9a6a84a4e9b43dab3a1a81c412e554e99f5c5907161f0ae6abea1ef20068e060bf9f999e47a021db0c1160ea8c51710c10f934b1b923c4c8f80a4adda58280e161cc8901dee6ba80f8a88c96921f3896ef991d935a2ce8cad2a7c9b410ef1086d1efe57b8477a8b9a9419dde319bf226d0a2c2716fb414bc0feb40e940aa50d274b052d390123c3daa4f7559bce7be2b14ed0915a5fad3a6a6cb8fe4e76220c4e0612201d877a5c1533ae62bd2a0f6573220285d7ad4d04eebf9c35d91cf607c0c28010248a208cda7a3bb44774f6129c7edcbc73339c1410bbde758ea21b0a5ca8d90a720f2f1a8f3935f353abfed8331b50fa5c6bd296c9b4a43a5d451c0fe35da26963bd5ed54cf9ae77c7393e7f734465ae293cfb945a061184bd5548003d4bddf991e00bb3ac577796d32f28c2e2c874e7dfb732bb51fb5fd4764ad103e9bb2976c05b09e0767ceb05982efeaba1cda179613d5dad0383fa3eece52209a244874b963d33fec20d1d9baaf5ebe99c7ca445e0b86747e16544e1486b260bb43e048046520161c2da0408fddd4c5f164040fc5cf3e51f8f1d687f00f260005e22593815324593c150e274c2fcf2d53cdc96c81bff8729b06a82ff76e2800756a33a7ace3e03275473dff91df6e5822abca60a960a2faf4ac5d60a1fb7750cc2e6a542eb7e25790007cd2c472fb73ff102b0a8037416420408bac0535ef807dacb574c20d9ed95cbbf7854e2038e160df701886ba0622b21dba4f1cc901a0b74c8be25e7a0dcb23b729f669e0f2edb78d90c9660b05e11efec9199bde9960414dd33c318df89222adf62e0220a31ac169c7234685e72f47e45ce1f82c3d30b867c8b132ac8589d9f2bbb6e8889c7a822abd4a6bfebfce22e3b7b2acc797507dd25e810eae298e73ffd99eac44f9869f1947c39b1ea43ecde0397a9e3b49b0f48c1ee4b1fca0049c1a71192b18debd481b3a2fa3f6306e4c6f08c96555dd7031a3b8766a34c777701debf292c2087c0478e1ae7b9d21fa69f76fbac53aa590185056b7b86993601c2870615d23364e1cbd851f47f67eb72881c31319f97ac06636bc1fae90fdf67e44c614438e446a3f1d404d722cea1a3a4549499c0d4c80505e5913484cd245b170c4d7875b3cc7adc40d1f639f92075709abcd0afd07b0d9354f108b09191ce0765383accf5eeb0a3e28a8496de4801f3fd2e458280fa02d53d4b41a36f85eee29805b18d0906ac7e23af7c713dfd7c72810aaa8942a706e946bb57686c22465993bbd160af52445e1d35fcefe4bb50bcc84d7f0c5f5404fa189ca866ce2cd5f131340e507106c25a6954c8f5696b00d4d6f5e094a3fa073e41b50296530a0300fac26f8e4c0a8f36fcfa95487d0b9a06804aadfdffa20fb6a78dcd773f5c7f091e655384b570b948d13c9db744d2ad2be61762fd528304a55c93cf9683dc11f35d683165ddd1e708f5373ebaa137e40731d331688f4a03272586e31cb38f45bc4a51b5b48e2ad29c6650bfaec726412cd045e73f78880b37deb590d996bea622d8c6456a92f0b1cee5cb9f24906fbab482eb69782fe0062584bcddb549a91c20d1271e7b38fb40f59bc2840ef42670d7454f048822d207631934280442fae0b4a69ecf5cbf087d9d00de490fa703413c2476abecc5a10d975e8fae0a1870300fffcb9e8ad44afca4b85ba3e42cb54a998683991025ad0d7f32d641cc5452e2e78739d63e07e328dc19d1aa8c9fed56e8dbb79d9e29880ea2a01a3135ab8321e8276caed898db03008b0d2a1994b2c7cc8078244b58fa0b8e5a9cd24184e2fcc23e8549c68a0351a47da14028b5ececfb82f366e76e2e03c75bb8bf36e3983fe07b2f10f73bf6746c11b19798a1cad183d7c3cc36ae52013c9074b9755a6e95f619abbac953d6626071883ec5aa91af02c41c93adf6bb017acbbcf94505d6271f02f445f78fc3f081e707ccdb4c6f271953df358b6f1008ae795c6401cd319271cbcc0b1e773d7d89f5cc4c20a270c1cd687a72cee8f702863e40bcd13e274e4c00019da0aa6e42b25b7b52935765100f63f04694dae6010c129969c7b8cbece68d081f1712b23e4dd7f4eea2cbbc4e7ecb6d851f084a03f88007f4301ab7f6d14eb3707e2fd65bc1ac9d61bb264c96d38c066016eefd02ece5d026f6a23c78aeed4c515d18c85dc3c98a208ff9c31a87338355a1ddd4032d233fb3aa34d66634b910e7b0daffc06e582d91bdf0bf43581eeb34f60943091cd79aa5979bf310cbef72a315ad6cc311ee8d570501da0c3d01d1a09e72eb01d175620df6e35279c642ae384f090c2cccd0d2e7427c7c3f72d8d6c531f3720b1348d9e3bce76471479a6dcf62b52be88deec325685f484985e04848b8d8f8087eecaa711fc0c71e1f359a12e0ece74e7892ad33a53cadedaf896d914f91790905881846ec67d764935d6c040dba760a8e40161753de1639dcc9cb740a2db107fa10da97ed599be265dce75afc232372e3a8df3c822682f146be03e780f1300fd24e1e33d1ef5967386c9d20b4a360bfeb1b4767c2dc13a35338aef71c312b0d5c176d26abb5b92aa80122339200c4b7cc03963c34bff697f72ba071385c1903ef2b9010affcc4c76356e6228c3c76047c70905d987fff388239eb48c25cc4012af1d7c05f3b33dee915065392d0ed1c5265935350cdcaf638a86db2d14cd00360fd0e6e2ae2fc172c73c1ec090beb344b3f54db1d0824f52d7488a9fc214b00dcce1443dbaef00ecb07bcffd9ecf8ca45cf4e3569e703c94b2540655e13110dc04bedadf1c305c8b27e7392bcde501d294f75cd47bc58855b1318739c7e290cc87beed32932ea4a48293e47f367cded0a1c3a4db4e9c99daecfd1d1097cba05af0ed127ad8f4a3ebf7784ac7477994bc3ae303b79d1875947ddc6c0da61fa07d12bdea9a7b17b5fad1fdf13ee4ce7b056d7f0373dc4bd4c154146a49adfff030a8e59831c3cffb03488ae53cfce79bf4e6b7bc98f71e274c44520140bd3130c45e28fe5cf6bb4289649fa3538c476cd294c53bb40d715448e43d0bed5fad50754aa52ec4d686e75400e7286dfb39d6150a7272719d89ea1f922657bc5dfac0f365c468633f0679343b8e625e36b18bf9f2c5d2d6feb156945e862948f6d2603719f830650ec2c2bb194059821c9b6c74a1f064abae3c72ca801439b03d16605506e7524c75c84b97b6e7112c48a1aff4bd563fa3163b6fa4bd1e415cd9d480d98be4d519ab047b3259c1cb99c1e199142942b2ec1fac20e457419d27310b20ef5822441014a18cbb9aae5b75f5c07e73e17f90cf1ead39bda032a8424ee0807375e9f69fc74723b1b11737fb37e06e1419faa9c7b55dce9a3593d215d6a29041f0b57ef096097ef424de85ac80347ec09ef8c2317eff31008b0ca089a67f404cbe5bc089fa687dde3091ad9bb81a9a52e0de14eb6fd680966d3e5c55e29c5011607feb7514a3f436b31bb480e7a2681c708833dff97a8bff685a1b15b2fa405b14c398582cc7d730493235e38a1c3bfa5705ec909a36d66309dcb87347d3d0ca8c784a70d4608ffa0f35ea3bb2d134cd55caf098c6dc2c44d4379a3b78e4c0877464d1f3ef97e1359f005e5c095fc9649a17ef962e3f3d6c9aa0caaa6fee5072fd4d47675fc1b2377f599dfdc0d6cdc5e5e54919f1d0c9fbe24c896b8984101abac9e1fb9e2cb88c502e5d60f46771195903411a48fb4545a451453871e4c0ccf001f74c8c3054b604a9fd097da2adcfebc84980f9aa493a8d57acf959310016ac9c84254f0af0c75cff4b9708bc1081ab6e9928f6108f736b1b49812ab7d005af0de23d2e687ed153e43b176f0f2c7a95f14ccc2ee1082ee73f6b97c86ff0ad21c534ff329000799d7c4c721d72f3af6880767be30955ccee7535f1256a50ac4005b7f84099af86d99f4c4d4d88c86b1d309cfb457d2836b09d6507427e205584ceb61fb1c4c6a9f09042fc06342c94f48ccb65f3b33af21a5ff4f00a0bf03cbdd1e1bd5097866968e0e30ea8f5178ce5240c22c8a974e08adf8d0fe3ae40af32492a7a679b89978cb637ce74e7cf2f71f206d504b0f531ee50bdab1960209c90d925d11af2ca2e502fe88c91e8765d05ff658fb23e51acd408e3ed5542b0a38ae390169c8e8926503662f2a7255ef2741d0d4fc4da24b22e24d4e4409a20533cee9a8b287eb4e7c1d868f8efe97cdc470401b0991df22ec7e46644b3efb0dab6b5db335ad574f218251cdf9f2416f7150781ae7b5697b4064290a284606005d93dcac56034235ff268ea7848befebfcff1084b1016505f65dbc9da14a180b92a0025050d4a89db2dabaeab5ec1751e3dc2508068e22f4d213afd6d590b809 +generate_ring_signature 2f70a3f91cd8002c712cc239ee2488b0046d4b010eab9d35554f4e1bd9d61b0b ad0464c09da58d6bdb00c070a4e094c0883cfb6eec67fb6532093193b4b1f6dc 55 e19f645f9ee2bd327eb51f336a58e2c9db7b1a64a97d6803d6b56ed4fa9cd560 2c9c1ca72314e021d1cebff7509cd33dec503d78b8249bf712cdfd55895a3726 7bc59484c4005b65ef76ca020046038e3a524db3c456d48bd6e6dd45f38a252e b849fae09d21667f19fab5fd6c7d0544b59bfbed39a524a942d48828106cb12e e16fbdc60de52e405d04eeb52b3b00c0bd1fbed1d5af08f4f35be6428bf4fd8d 9ab4b2beb1c9a8ae1c4c33c53fe19581488a836bb933b45bce4884604a5ffc96 0fc8e4a7ac769f9a692ec80395494b43e581f82aa65b4247ffd7a67291fa3b58 af9c421abc18053d52ef23a6846ddefcd717a96fd90f2b04ec33eab670dbd39a fe83d20f114e98d35389b23ec392dce2f3a110c45710f0c61905bc5c77bd6389 ca5eca751a73bb5c81f1f85ee75c0b6933f364579189a9e010b470495d1d4166 678ddee968928b2be7d171f934fe13a681556c03c4471d2b3a0c106aeb16e0af 58a41d7ce0ed959b012e587542acf3031f7a878d0668573f296e95e79c0c56e3 1f98f698a97f4bf968d8c4a073d62bc50ab6768dc8f0253a6ee7d0a9b8e400e9 203f2fdce050523df6932b625fcd99c5d592853d69594c6ced0d8d1a27e2df4e e682c2bb504201724f9d6831aeda7347efc2aafc78397f237ee36a2d609adafc ded39b9bc8849e513f81e5c22bb93f4d7009522eb63f580e9e5ef369a009f63a 9b4566467f59a6f545fd438293c87f895979da6151e0f713f0535a6a78edf605 b16fd389f7553736eab1855cb33822999b4c4a28ad1aff92776724fb2935e91c 1ce0853c4f87e754e05213c1804225ee1a2120494b20fe23bcf133b33166504f 0ba8b536b491f31696ab1d4dedba8164f7a1774d648b6aaf571e7a1dc00962d5 df770c4d34b57b2cf0dfd5e59b8778e4f6821796acf3e5a8a1ef2b6c6caea12b 000cd6a7b4564634f50f4cb3bb34040bbbea725ba56a0226603e12f16c75c838 68d4b4453419af5172aa54d1ff68004e0e8bc785605c87dbff1522a69fcf5eb6 6ea42b2fdfabfdac3d4e4dd520fd923c25ce1d66c5c536f21ac14af60a3a69b0 e6c455a08d17e0f82fdb4b1d2952d1f03bb66fd20b40d80f693e6f1621261b8d aa4da257da007f817e0ef63694a46500b56c649b6430cd8ad2cd98cdc92fd5b9 ae71c3343d5a2b6124deb034ac1c381121b80e9cdd3c1a3f8ca645be245c802f 55c97d73d6cc3e432460456234d554ebc88f4153ed753ae69a42ed8827906384 7d2d00d52c811603b99a492a7a2459d1c88cb5113a22ebec3ca09ffc126c8757 b487a8889eb4a2e809205aba036adb11473ec68efe7bacb6df6fb0ab34b5f563 636fd45289cc28718d29ec8dc3a53ab20f2dbdb81aa8a7e73ab256f16b075484 d4c8554683898da5f9da8a64c30fb3e343cfb19eb88f7fbf567901a9a98d2d71 160b282251304e7cccc5682a64e5f4a4c7356e199b023ff9818c25defd1e47f6 1a5b8c9c7a12ac3f2470048f0642602e07796a4bc7bc095aa88a30976fd3b873 d39fa1a932a00e90d0c2339bf81666c08f299234846b54124942a9db33365b48 d023e5c5466fbb9f9f4210e53965f90b51efaf8181b4eb11f28081781f618fac 441afbbf59c3ce187d0ea4b7f0b8c886509b32f762f84b64d64cb1ae7e4661b4 7d9521c9c0476cb8deb152527709412eab60f79e6dcae7d6a8bc764b521552fb 7753e5dd3407847543c066c3be5b57568a00ce2156029dced77a7d7f84b51c49 643c25c296ff6b10e044502b84bbbb8066dd60c13642ed568cd3a127269cc683 af193ec7757ba760a786e1bd6dea91abf67e0d09fe1dde9636ee740cb64b3881 97c86f4eb82cf9b008f34d77be77474a9dccbc7a28f8af3135804c4be4d7f4c1 42a123aee5df04b5d770c8d135ce837ca7b61f889afbfe459affc364683bbaf4 82443654c82e18c320f5c5a91eddaf6a009a9ab264e19fd377094165553a5798 1c281ae660b2a48d4ce02e48fc43d19d9506fe50ce98be6138ce2a12f104171b e697e05d9ca04429039d3b472766d362bdd5bc09edc5749244a4e1631d24fb19 c64794f14532943a569709acc96c45ad352cdfe950a925f2d6d5d15461e24dd7 215eaa77ce36cb9da74e8b4cbe60fa4effe632bc03b8ac2972219d7197b1755d e45863fc78b8e87aa3e1527121dc382a16697e8febcebf7371975d02a8c019d6 64c05b4302440eebf39e77dfee67a5c556c28240d204684197c36c61add87089 c56a0a14c23839ebb8fdcbbd53da469670546a8a4d9b988e4283fecba308ad63 ecce137e698c68abfc86d1e30b88654aca22c4a81c5badfbe97db39fd069f4cf 0261b71c4342e73833ef38913e92cf7acd43191b63872739d4c419daaf35d193 978a3653c3a18d1f58e3a222176e34d37ffbc816b80d11625e0ea049554b1778 bd8028231d3f30ef112db35443850f73b573f8a9dc3345ca5744bb7b97ff4009 8eedeeb643d1d8dbc79f2f62d47dad575e1965b8cf1c25dede6f010cb2e50300 24 59a971ff2425fad3bcd13992dbbdf9785305cea18f04ce131770b4d2e28dba02a00e3b05de5d0fde61e63bdb786ade3ca58947073bb488764606b3ebd0600609e9d9c0adaf2373024a69ea44acdbbd9a84ae74f25488df0be2738eec3409b70045ac54ff862a8c4ddf5d13e95ad3898925280c2485bbf589dcf12a02e1b20a08f8699b92354e4a384cb2ca5fa840c04accaaf7542b9c049fba901a262c96b60beec5d4c2c6022cfa86c49f9e35532a66c94aee232245fc805bd3001cdf05f107994eaf7c4d48018ddcddf742f73dc706880354e36761736e78592a62b72916009e2f80ce8ade0dacfe8655dab6e1d8d4b239e6779620c613778c93c066c6cc0fa61c8d21e5285eccadfa5726dcd0f2c56fb7188ff06b0245ce0579ca01d5e004a9ccfbe6c1e1950cffa962a60c61910dcf778ee4ca227011cc7d7d9c0cc8380ef863d563834a3ebf8cd61a2a399d83ec0f5eaa5c2ec8f10fe068e72892b74a0d88a34a33380f16bdea2437889accfda618f3838ccfa393df5af6d97c7939c20c2224da8f7eeac5107361b7083caf977c138dcc8bade5f68d2aa2caa075b486091d7969480ad8b535041b2c93c29dd893bbb39ec6eca8242120795e6a8aedc10245c83d9f15b0916326980003eb60cecbc47751ce56bc9df1dfb63959170726008cfff9ea58554992292c866075f5778ef572dfabc69a82325949eeb8177a5f09e9aeef78e9fe58d6658d18275548fd0e147b8d60d6e6a577265994b741a3cc06f3c03da8f869a69778d9327d094a5a41c06e25e49901aa8b194aa08660a19603767b339e28050f4bf2d2dae2010e8ab75fa99a1c3b29253a8601009e17168c092ebcf4151f2d51070fb2fef8943cfe456c78cdc4bb7dda09fa768984a88fa30593198e31fea0a546d46f22c7de0c0f6dfe11226f27691deb01b169adf587530f3bc9270b9e1d78a8a67e4b9d70d9e12d9b5f692bd108a2644b1be761729ca40cabcab2cb7bfdea3362563452db9ef7c884a82d0dede94538f5145f313c7f1a0c73cc2eba8c27fcc0b8955d920de6bc8c92ff3e4e7ecc0d1a1221d9407e5aa1034ec66fc12a939a0f2767f2af343a46852fd64c850d5fe82af921eb60840f320319de593ba7c3ec9508c70172aa3b9e0bf2927b778b42b413920fc22d4439d505b6e6469fd6448fd92284c69f912ecb7f4ef0ca274ea68983cf4859d7bd847803fbe21297cb423a6dae7a947384944fcd0c510e149ee52a751ebd4ce214b829086a7c494f58b31ae1e2e1b412d52873b81aa0b794909758a1c124b28edbd56c06aac302a458417da82e3af5234c925df0551312615016a197ca727adf6e83740282039557233a0e92788b195d13bd59a6b348960dcfddf971274d913d7166a1083dc6ee8c25f1510d056ac0106932d9fb49b48cb1a059e70ab14feb18a15455084252a94d7520172a28ff09631273278741808316fbee95fa1a975e5070281705867fe68506de1bd459805414cf2ad43ced52da167396d1a5104297eb19cce901dff29dfe5a9de9e56f1276d842727b6a1fc2a3b003865b4be8b6fcc44b31990bd9f4c703a5c1081168c57ccb92e969272294d354050d3533b51ceb1c9444a009c09932b58c6c798abd336c413de060aa96c22f32baa3dabb0187d5b45d69270359f61a9832c53ad31dbd9aa5010c9d6954a95128fcb64ee5c4d7d2397188eb0d685d5e4b123494f6094f3fbe103c6ff75e07ff6899b1cf82e17420840e049e03ccd1e9e3849d1e02c9dd3d17b6db737f56b669bc67038befc638b1699bf7240fd7f62de49cf873da500655c3380865c72905f4e58036802850d5d2ae883d220c7c222f15cc7437d37d44dd891c20f361b7d0e1653b3b359d504affab6c25940089be406d304b3cfcfbece51cb505bda6b4488fcc9c9fc7da6babc9bdaf0d2d09447adce0101b60bbd76e1c0bff8b9ebb25932907c9e5e21e88ede4a884e01e0354013f067ec762031c18d4ecacce884da16b7ec49d753e636c9bee70e6b83700542bcab59413799295e58cd0615a9bde3e1425699633b92082f4e2266d63a909892d437b7b6970de0cb3f632a92dba085ba5bbd8b2c02b09a94787daa3e0c90ee4015aae33f414223c3f5823b4500b517bc050dce4b6d8f8f17ed44d8322300b2ca9a26962b7e89cef388d39fed0d76ae9a53977a5d65bec353f54126c9618061af5016f8e93e51f88df3eb1f13a133ea6fedbe101dc10d18e6bafa4bc2b59057002048f4bc81912fd84ceaa12fcae1957d91fc9195b995ebea3487a88997903eb932a8bbaa1df1c7940ccb6369eb47737e95d1ffd34ce9c51488db480471c08fbe050b02dbda9253163237f14ae7533e1b29a39fca035fc1f102a80a7d4160d3feae8f5739ee867d319648fea77094c707846e4541b607e4dbd4f0bcfecd801a76606bd1cc2edd04bc51217f693e873ac4be75595853a402e1686bb5726790a0c7da0075291bcf33214bf86c9dc3fda63861373713abb9490d6c3c7b2a1970a38edc410272942f4758aed302f2184945bfeb26e538788116bca0133e5a79300be3170da7e3544577de06f0ae3746a9e5fabecfe62e80d7c830ab081dba8c1078ce784d26e7850149dcfa5c4be93a6c8621ee9fe02029de2813c5263ec4ba70bd04b6a9f852ec02e101de57b8a7ddb6e7b4e812722c9e1ae1845ef8ab78b800351b4acc41e2160825036c58af2cd05166fa597281eef026c647c3acc69b9170a408783f88f183cff88549c049f3c54f2a29e3b7891aac7d1839346378139c505781b02293baab8a2eaf7c40d11e684e7c3e8e7bd04b5b376c663d506a42faa0f6703f10992af9e6f6639453f8daad4e476c0d49085fc3bf2146357e357956b092352dc209c724c6b45bc1c7491065864df2c7948b7cacd0abbfec7597f930a08046a347b7e18089e75f50b65878e59a3ec8158e8d2623828215dcb45a97b410d519421f7367efad7d0b6590c23b495b9d5f751ae444e98153e7929cb75c6ba0b0907731c8b5b36b6f3812cec4f4b73301813e7574344c948fd3d087f9628c30e86c63e16abdc3eb2b0f802d8834dd66c36a2b899448edab4df2d42bee0ed7001c952706f90052599beae04b67833a556254b6c505a92052d4bc653f1b7ed2c0e2268b852cdfb990a561d98d1d621245b3ba56f2317dffc97b2a548ac66df3305e51c094fa1ae62ba9cf528bc831439e17c2c707dd900982a1e873217b1ad9d04684ed87c7a14bed1b3e314cc1ce27098d38a9f985c2d575e1d73e5128467ce081fd1841d5dc5d495f2e559032f7136d1f9c2c31c17f9f54ddd0419eeeb9f6e04656f902111736211a21d464a249cff3c7f60bedda472df2525783e2711571a05315cf172b4c1fed6dd6551a068d17bf71ca0e65a945de61530619134b3b0740d335c10e604d5b69dfe3b4cfae2f75f0e915221a7a944243dfdbb7c160f6e350d1a9982b0c172a914480550e778bf345729014189a2e2a7fee38e8db067cec807f74a0e070faf4df92404372367b2538fad8eaafcb797432393351dec2b85f606222476bc4ebc7b409915b9fcaa2dfd3e3011dc50443bc110053c7e434fa47b0649f66b5780e8c9e07f8cf1eecb416d6a078dc6b5e7b6649195552612b91fb603738a109038de305169b62869f27422102d29743d725e72ac6c38db98d5b54304c0d774cfdc66d69cf60d375b54fb8faffc3b6de7a3cb408b734f919643a62e040ef47039dfa2b7301efa06ca348c69f3d0a1343cb2b81a9bce0e5e7812631302545adcd437356948ed0440efb14476623608bd17fca782b8df715a18a80a8502203555efb76dcaace047db7b58e14a340a4c40df976cdf1a611a800204eb3d06dde5af61a81c2bdedf93a0982cec2d7e7a2e06f05b0b29df58d22ac0e3f96b0c5312fe5e33fc0a2d8889612765d7a461c26ddc95b6344d6af2f1f6f47b895100ef828d42b6947fd3584b41ceb237c7e9d58b81d95c2e8fda86829e7caf89390d8b7bb98c8064b32e244461952823a7b486d4c77c84f6f97cc871bb397369b40cee4ad48760a76d265c236fa2033fdf937d7cade969de7a63c93363834e1f9f00a5f37a535dd2c1ec403f2475d91464f7bc5caa3f4d7474a30c2cd67c22515007173f2aa863ca6e44adc2edf8b70f6b0112a4134a4c23eb2a06baea34befb4d0e4c6f879a3fc297ff740310327926312228c24e3bda34b1f6d96c97cb415192092338896c126659933003f85bcdff65572fd081c4507cb56b138c488b9a41540835a8368cfd81e73988b1a68c7b1c3f02d3f6f2cf3f72fa4c2a5cb312deb6a10555075870dcf71bd3445f6f234ec13c07fda4ab1b5601c7de4710e9d9de7ed10d0c4ef6661650815e44ad4ac7cad3fa30906caaa9eca732c7fb834fc06f768204039be141561016e8811a36244dafd4477637fac2d8739c4b65527cb540a2980dcde5a701d76c311809cb780271fa03abe32d6a29194432e19d1ccc5c3b821e0617ad6a028ac4897c698d730a9dc9e6b1f5214fca28b2962be458c5f282809a030b0055344efe8483fbe7357e9e77fd2451a524e2f5013c86bde4c748acc6d60525c22e8581a116629e190c6f216d42e47b2deae5bbf9e21aa58066c11865a30df9951ca6d8a4b94745b8042ff09d3cccd4fd5048dcbae803334f81849e07510980a959409402ec713d84edf4cdbafe8dfac332615004ab6e63bb82273101b2027785f7d4014a0b380deca671056d4fb9fff4f637e900ed312a2896fb0efda004b6abac0a75694b3cca9cdd6240edd6b90d338bd752880f4d66e6a29f0c47940053100625e39ae536797a4fe909f5b779a4ea7fcd23e6ce9b345f2d86a4d69f01829b711cd78ff96036fbc6082b74a41de3953080fa7fe34976b0e3318a6ae3075063081e3f7d69721b08b18fcd9bec5374cc049a12018f726d150087b36cd60d +generate_ring_signature 0ba30de6e6d2dbc9fd491a2874728fd0280ced45df74f8c8d32cf2e77768e7ae 0b3de9afe1359f3f83c005c7631c44a73ffba60a836ae3839d8d5927c4471ed0 152 3a358906b6ff770b1f077ae12d8f7f3cde8cc6d3c19a8c9f5bb6ee11b7388e7e 472a126f08b58e6cbcd37f5ac22cfc6c662546bac3da95da2ba09452f2437a91 fb4235440693b05fc30539541b237b7f9b5c7ad68df2955361c3de5dfad8e9b3 591196da4d50c814756f5ba064cbd38d1af3671a407d8a05cd8d0e056a4e012f ae702c49cb7fb2dc5df1a5e1c39f2b2ce402f2945ddf59241dc047b4ef81cefc a585b5a0091db1a7ab1bedf09903aab8d0b0a844d74e67de526bc99cfc23534e e092843c820545862a9e6bb15ded38458d654c953971ecb97fd1bf7603c1dc6e 322a688cc9f928b3499215ac5bcf36b55b781ea58280315c156841adff146995 ed842c7281842528fa2b17d4d910377ba970d1443a686d3742a18816b10b4ad9 daef8362af755e7e0d52afabe3a44575242405bc654d2c5c59517b98090bf334 1c3845699b373abc21d24b1107ce31e3d3eef3e7ea96c491f7c6652ce57465fb 14261e794ec119d49af09f570dabf6729560be83464419a93d02f0da7ddbbad4 33b42eae69927195bc2de2fb5206054ecf6511312a2528d4929a4a9867002813 c796fcbba62668d7a0d2348a1d9b5f06ab4fe9b157b0d846443b41edb9a1b6fa c95fc5d08879a12aa68952ae167d17993a7e2623297f75fd2272c82e32bfc37a 6cb2f098c37f9b3c863566c29d56939ebdbad47aa24cceb566cb410223a846b5 b77692430504c280106fdb7f639c495d8ef0d7d558dcbd75b23c03f51f8a3e11 4159b10f826b40b00b32991efab391e8f6c0222bcd8ebc0ca35f01c56023e0bf 3fd351cd999eea1dae072737a2352de6e0d35cc5c03b7b66784aae6b5e8a1466 9a184ac51ae37b7fd07b91e062c085b6cfbe8d9fdb539da59cae447ae7a7b78e f3542561f7f3543004b64d6f2d45dddca1fcc7ce148f8fcc240e5dd1edd2c294 f84af966409541a111985fa9c0ce4d203fb6d449f337eac3527765a7026ebe87 c85ae572e78401bbc3594bc748bca1f6996461cbaf8c28064aa3ec6814bb41c2 4aa24501b140eee2efa6fb6980eb6bb4b703eff58c9069b54f1b30dee82f46a2 c61f9c3d781903381bbc07c6ccd2631e6592130c7ef184d2936fbb156ec1560d 146811dcf8fc8172c1504d032ddec6798525e1d8a9429517ad73641cf2ee7499 556394b1a728ad1d4cd280e5e56837702e029da3d8718f50e5bbaf6f0b05ae17 0d694f1df6ef6156bc6f8c5cc2a3668801e9a9f29f8436d22d25feb331afe929 7ba9d54920fa1923eb772fc59f73ec3578fa08866b226d11e983cc8d2f8ca631 9e4f1d09796df5a2f21b9732dd9a217ff85b1acfa8ad071c0f8cacd16c875025 77c0433220a41feb333fe7a4c76ebee4dc10a08644bf530426b99b21dd0ce3c1 1a34d9fe7d8b4503fae56512bfb90821147cbd0d276ced49d456da3aa59f50a7 70f6147b9a07429cda27dbac9f7b69ff87293a49a6bb32e48e5bd7c6cece8bc2 52a798ffabffe412acd4e9afe2e21ca0f5c3b7b83481d18e98c03f916fad4f3a 6fafe4e0b6a1bc4e19439958d5b5eb6300e5685637a976595748955ac294450b c9081e39f92a9d9c0b0d120d3bd1bb757133873ee5e02dd6e428083df7c89cac dfe7f7dd98096fc79fab45669c8173226c19f953bd7d7eb2bb2739d79dfa2e23 3c3856df0f874c2d766f14ec708a366d23128ad13df93d73db9156b7d9f074e6 866dc59ada75d08e8c8ef670218581db6a3fcff78d62d8f058c4d7f767ce6e21 53ec7e68ff4f0c96dedaee098825b83a4bf56c3f6e590f787e4fb02a0491a035 66d5a0aa788d312053b71aa88fdb961ef22eb238afa1eb8a50f739d821a27e1f f5cfeeb51f1238168ad45c7daa7caf2632caeccfe64a2d97e6f93bea5fe902ea 575fcfea8d4bc586a780acaaf079f4a75d4bcaae1ff008cd5e961fe7f7a7bbfc feccc7e6f4619532c2b5cfb43a0e7240b35e7824b94086a93c2f8ff76bd01392 7fefcab77bd3284fa4867c63503d1ff728ebdac0574632be0dd8afbe601f3b81 6bc526e9c885d131970279cfc139e14d213ca40384f021541e669b8b64cf6c5c 864abdb33355465fd8d918c99bb840169d53e1532c8d82aaff27ccd1197f8ba2 2d37d9199ec4b6660639f4419cf184293ecddf90f8d40420e61bea0971ea583e 45890e5e880c0607c04b9e703270355c33cf305485b9c15b9927ff2728bfa5f3 e5778f46b5adb329727cc6f39685138b0ed0932b3542873dd4448909f7d3acdb 7fbc3ad923ce86f8ff5c34ea3b657fff3eeaafe9be2e00ad74e02ab4a579e558 a247dd3e76847f4c67f98e8c62ea0552c69410b57f59bdfcb820dec44fe377cc 31f23aeac4ecdd85e4833c8adf88b7c481a6f687b45920ae746796c878ef16df 7a812f1702f24563d4af961789b40be3d5b7836277f5c96096385d5d373c02a8 6ed55998c547fde1f832dbd3c496c4b1de1d17cb2e2cace2cf188d38ecab1df4 f24160e9814de8c26b075a6f11f085788ee84abdca189158a0420aacdd9b6eee 8255924674ecbcd958d0ecbd07c8962bef86f2d2a5aef852ec8f1fb7f9d69c35 129592cd3a9d7e1463862b37909bfe1d74927582b6c45a6afd259ab07ebccbbc 5d44bbe0a39fb5f35dab4e900a447a42ec4153eb53f69eaa47bdb9bac8137d8c 18eceddf52dd350616f472d7d1b8fed16d25297913d7f2f8ed04883b999ea48f 3af3a5785978cc66e200dba17afaa3b40ce4a5783fa9333a6424c4ba06fb7618 901492fdad5dfded52b21c9959a178334f8e7168b6609bfb077e2a7b93ede507 f7d79b9ec77df91be73f405bd30216aaacd3a055c63efc4f86f0b109798655f5 dbd4023695ec11a04d54740936f407fb2c2b6cbe979e6ef5bf0767a801d0a178 93ab27f15725d4630ba876f4a58ba78e4e560a410aad53a8a735911b7196c6b6 545578c450ac54f055d46e661d12d4d0adbe0849a61e2ea03d6e3d4638da7cd9 3391411ea286cfb663d15115d113f7b2f0826a840395939e8d5b611433f2d113 57ef1e875e3855b4ab4670fed3d630e6cda0e1b9341891e59b7e2f9914c947db 658ef5e1bcf430e63a0b9122ee3a84ecc9f47388f7b31c6fcf366eb8fa9d93bb 2658013beab0b98888d1eb2f795f64f16c8855dfc503d3a66239a0047c5593f5 69277ea51de11f6fba8a43c41fd968d901c4e31e822f4b5b3553318f847b1995 77257767504d0df2a8e5500dede46246b257f7a9f304d5e034268bb02c23e209 06dffc165810f727b325edf311d3839cda6b71ef24fefbc3f455121092d7a938 a50590f3d9193887a33303080421388a59ad10c546a1311e16c8a7313dacebe9 4a0796e932306dda67589cb37f697c2810df1c1747e2b2fcfa293b4f4c40c4b9 c5adb0180758e9a960558496d341d36e639d8355e36e076ad31859371c8b1014 9ee7528e99713c1bd54457781063dea844349199756561b4be6c384b779ed8ef ec8f58b2e690c33d2d30384142e787c7b380d422bc7845a99b9d36ea5e1a3c55 151a797a15a5814ba694cf2bb077185e5b1977d9d18eac0ad772e1282ba5ce80 c08da9572b6395a5cc018f6cbd1da4883d6a2f7d3d76502fdfd72b655e1bb5f7 30ab619fe844790c294a0434cf663d887898c694b1bd374b2558216eda20e683 196b383d573af3795f23d86ed51dd55d9c3ae875464825141be817a0be662345 153ef73d1f5b17c75c970bc76c29e4f2ff6bb212d76e0ae7ac38445c7c76ce97 4c8e7f367d39ea4210f0154dfb333766531983d43313240753483e00d1b72116 dd237005fb83081cc37a3d4a781426d7c72fbf170ef7973256d3b9cf212f2f8e 8baa3f9913024928a3c62e82d8d89ef7438fe7110b9dea1dc138c7618837403f ee427f72f1736d9a3b416b363db0e19250a04cdeab9969f6209bfa556cf79e5f c7662161c5e8c2390fb938a10669beb2713a9eb58fdf61d4476bfef7284db7c9 f9bac6ab6e3346e88b5e48eca67640c104dbf8520f08e240416c3ef27a29a332 cbc2ee1e4553e90b60ff83126c636bb047e95d7d17e941aba994bc5e122280ec 601b1aee63a11d7e9f930d9a64f672280b17787131d12e46ab0140ff01a3372e 90fa3f00483356004def6de99c8dddf30d340b96a065d0e14e7e2ab5929771a0 5f762e1c5d76870eb65f31f7751070f2664a90dfa7ae1df568f64f8478368bb4 8c03cfc38212d86cd7002a0947128016a0131983552f6826d20a84bbb052aea6 b188375c340056fb061f1ae3bb28ad17beefca3e7b5473fc9087e257cae927c5 dffe5382445976ae1ec4261b3370718468ba4ecab5cbfb50a22ab6d38285d3c1 7283670d3cb766afff7f34486489d5a1a5b0bb823fab655657166de815abec24 f7713361d1b7b267f55265c03a458233b8d077007687308da0ae02a3075c0d4f 202b3150f80754ff14b48f57a8cb648ac372fabdd91874e45fd0b6b5b26b2de8 cc9ad47925cae8fadc55c3005147ea9f93b4b8d7b1517104c24bf0b893ab46e8 be42bf5d3f94f09be5042260f359c8292dfc6f7c27ab8da88e9d4d80ee553e78 3bd3222539be23ca0edc229d3eeeedc1b41b8ce296978d73c86e762af79aa887 4080704d2b09ab7ed4eb0b8b0e439ec8a2ead3a06f817ce3f9363563950fdb0b ad35286dbd79ecb282fc7cb18c5dc7e7abd53bbcb26c824a3141fc054b8d15db 4aad9cc0beaeef5482e2950883d7253a309ef56e7fcff5df54f5f8e654868cad f1d06103cbc99b6de6494047a63fd690a1feb3082c52eed0b2ecffd5872629d9 3b55b2070aa5fbbc6f5a160de4e9f3c2f4655278f38d02bc6f5c5987f6a5d46b 8b6e16fef782152e2e03abe8a72a0c961bbcd899b27e62fc1c59a9b703aabc87 a027b3419222bce3f34445a08da62ff76712b33bbb62ff25436046e8c97ab78e 07fc0c70593b4023360ba9026368f270ee095f65cc7ea57f459b739f73328ae1 a2bcde7f605fd6d05046e0eb333081e8491057e369713b9f9adec7af1b55665d 687186ddddfd074aeffc932f3d56fa3edcf50479c2c537dc81c712c80f0d60a4 868475fcfd1604aa98ef79866cd4a7ce314850928880af9bc2e31eda14f07f79 c46158842eded9c26dedcc934ca9d51ce3eebe5a6173710225bdded6ffba7643 1ad3ae4a9d113f8991939e7396f74fb0d96280837f67caad692d18722a93a200 0ef7b4ff25837eedf897a910f445eaefe4520042bce6d666cac0b206db3dd1e5 7734da981ddc91893bc8a527007f824a22b40844d0b4537209e5d3d37bb27091 ebe6fbdbe09907e6fdf2c89841e1c57cf69c6e324f8934559cef075f7ac0e2cb a5ffed386db8174633ba84fb978f88f8217a6afda3ca2e5acf6e70249b0b4937 c130b6a8e631f76d972d0f155949fb7fa8d093e576abdafa584cfd192cabdef6 7edde741298c76767208089c421fc96b728a108eb96e60618ed458a25e62a0af 3453fa314a63d4eb7ddd4ed46dccefbc3c939afb8eb2d00a3ccb4592e5e1c39b 001057c3d37ee9247b99984c5e5270911227156b442a879a9ffa598b9c0521ee 337b79ce5ca206ad07c09efd4b90abf7d907be87cc842cf4c33a4a75f40d03b0 13f81b423b6fd6096ae3e0877ceb6fe79453a2714d72f43b8779d3f9ba7bdc56 a6f2f646deb6f9e205ddaee63ef8a76b3a5b54a928ad4f3e1dd88d16fcff3992 65cd6f6ebc27c988cf885cf88705f1868f66350f1aa9eef50e3e9d032f8f110f bfe7674163556b3b7f7d14d706cb97df910eaeac8e523d564aee31a02d63ced8 5fd0212e3214b85eea338d564a336905fa46275d7207da69f5c94cd76444b66c 0745a8db9d2d5d2cd987cf39a9719da6ccc397dc8a979bfe1b98755b83ced8c4 8d47529f198578fea2df336e62bb2509a978e570d20a03d0cc5ddee398b90489 3c7ab76d75cfe7c749c818939a4d00b22cd55d17811f1e81a2943368acbc9930 9032f04a5cfc16c2560a30ddde438fd67b34f03d067266cc1ed0d6b1f21a8da1 82a319462f7be63add5b1de1c57d4f96b7d01a37d37c1402b8ecfc7c23172e45 5fff19c41880b0ab48f57fc5453d4a14d5c22d78f10ae7bc7a5ec7c6d397be7c 7b4bf36034148946aa80874237e075fb735b3a5ba7a25a085fbf70fc19f63f16 5e367870d1be56519048148b998a8da59d93b302f8136ca1ce8265d92653398e b1ae44dcdc1345606bc107fc2ff729fab2c215540b088e6117fb82f8469bcc49 09a26ba5b7ec87989a79d47fdec7febf79647abfd0efe6ba1078a15b345116e7 a53e112ca9565d939ac0f80c9218438e05ee1cbb40e094c4c6b9778cd1ee9341 4480d3899ff9742956f8ea9686d3d52082cef3e150fd8d647768701d99805550 0cc2d147b225cdeb658984df806fb6121de31e3c43d73cbe979c8fb8251b0bfb 42ed973a0b53fd57c7676e9facffdba1fa56437683ea6839537af906ed04733a 00ff260d90022060b2c237a1a68251d150fc53b94af0b47adc9d661dea155785 8d7bd44609717a19a777658dc0aad8ff10c8fac87e643378a683f0097e916057 47f05593d2ee2b00a2ddaf85ba6a7bb9c56e09cf8ade444503dc4d426e2af9b2 caa9a7998ed8f932e5d50c969b38da96ba85fa5501bb63af160fdeb987f6bee8 8629cfd12d7e4d98962a96d5359d6df658740b02e3178842da6536006b11b20a c181bd65f77ad32482f0e347091f9e6469d6867b55ee420a6201fc7eaa5f9c4d cf322dd9ef25987283f3e35f9f457c5a8b04e8ff506851d068ae1b0ff35ad2cd 7a1257577ef572707447108a254387944435ae9e37eee8b92ac3b21164599e0f 57bb7764c06e796b8594ef58c605ea682790abf86cad7c3fbb6842f18c70cdd7 1358110fd9c8fca453bfe9f048ce071eaf67dd7f1cce4f4d52e9cad916554c03 107 513af6a58e3466c8545e650e4bbb7a6e95a7596a8651cf3742291ccfbe21a909321081dc89d0f26b1815c04c437d304e03629370974f109a536237ab3764db0491c8251627f14250e62c371c5037eeff2b264ad4b720ace967dcd3fd7a0bf70ce9f0910bbd16387cbdf2927bab2f77859d94c5988b6d740d2315f9823d987d00b4dda02907ca773852999cdddaa3e74069aba75eabf06602ed315c341993d3090dc2b077196cecc070d99f374f271535cddbcc4de93addcedb3ad21d370359084bdd3df0b5ec967e2684c24dba4e50fc38a6d14aa8fa4490341b4c993824b402ca3313022f243268d53a5628b894cea7e8021ce4312717d649241319f04fe90a5e91f8ad38e9795bed6536655af386e299b45e7afc3198b090d4d04ef203cf09ff64995ebacf67b4f40b6cd16f023c2836b72e30700fc9bd276163227fd1aa0062fe4f494a855aff9fee649872bd75d4a254c1a1849a0f6792ecbee041ddb60feefbe60689e883ce19568ede10a402d483e1a0b4915534811d1c778f43195d0fb8f42600e9bc83a81a88b36f55fc966fbd86dfc2b8b9876811c36d830c29a606e6f79f67842ec1eda59a530180eff96872eed57d50d5c223d7b10b07d70ed702a9e7612b81bc298554a16b86ea9fbf491f0d03ec4c63e9d88570d10ce78e6600adcf7ee7d949cb8a51277f7aee1f9925c1327870161ee54de401a89b0e54e30bfddffe9f5a45e8e5ce2c406d8251f8ee8519c0cd949f189e2a805c8681e05808ccde4e1f20d48efacb9f407bcc2f30130e0d94add8c89aa02badb1463560ff00aa5faa03df299ae067bf972a1910c7ac2273281b3cdaa76263eea27f432d710b206c19c819848bc0293a4f1b57efb5c5d14cd075772c3e2c0f3bc86551137c062c9588a7e2f98bb2f6328cda55577be501450a737ecae4155d031b5b4c616a09e4b596583962a7ec631e28c49e4da6c7c6d45260d74f128ed7364a54fe82b205ae56b8c6fd079cac3886abd2260aa96ea1850320c3eb90c90e52b79d4bfd4506417b8cb3335540665a6c35131f5fa5695a6ba89e821ee622419f9c3448172906ab19c61f2f190166419a79a00020691da7eba2414531c5d94f6c2b9eec972d044b85f586e75ef500d84accfba411397d141b5c0898dd0b5f4e240106e4b01901ec315d143fee6cb7ef1129cd814d4baacd49e6ad380c58bce567209ab449af03e7a1d392ea99c810ebcf757915f601383fdcda4ea584a05e0b6085fec3305e0ff11e11e298e7bd2db47d8c40558bb3568fba5503703222b8947ca69b8c63300c72e257655b8a905cbbbac6fe9646c61fa59193358037201064e84ed8ba6af408aa9bf4eefe93893d7190153524640d8ed37adb397f0b14067dd1b1f98cd65b0898cbeb0cf2a32b2c95ecca2a1b454469d6d46d03c4c4ea25b8df0a9faa8652065e53f66f5d597d4c467651826b72d5e391c7066a6559375271d7fa6f12ceca0be2af57575eb9d729185ee66d07072507f37e84f20954d722402f3d6a88418704d78ed32097d3a1868f3dd38b1690c6dccfe7c18085626dffd8899e860f19ee07d8650453643ba6728997c9228077e63225ff99019d6645fbcab469039e5a1a07c63e611ab552b6ddd6d6bcacf5bbdcfa08a2a0a02d11ac2ee35dbccfedb46c0c1022ff7873a3a3ce1cbb5c7633005f85c352ebe3fa1e6aeca55564334b776f0e75bac8f1366d8debb23d36d306a47a6c4c1a5e6caba7d07d0c07fb7c90729401d59bdaf85069f33765a1a6fa2f8a44b4c6434d6802d94c8aa78d1529ed5ce208a28a7001e1be3ef09b597fad5bbeffa10cca5871ab50797bf58bd31425d40e05cc7035ebcc23cd214f9a0882016a798cf99473f1a63bc1d650f5da3bd2211107779497a9ddc7bf0801413e600fe02c520cc9647dc48917bd2e0d4c6514d678071415954a00064f863c6ef5981bad9a0361f77d0920616150c67c9db21b5f7601af40b4432d6ffa8df4a1a1a421e0b90dc29d10ecd50821235fcf8d0077da0f06313ba80169ea4fac296376a43ce092d73d597292548e3c9f0cdba6daa27c230453b4c83109c9a38b468a99185261adef5f60c4521e168bc092108abd38531308548187595217a1b1bf20d39b0add80fe3513a1ca2d4ae118f69dc8c2433b230583783c168825bd87feb84a40cdb826a66d85ce7ff06ae07b828e315da385a903ab156ad4c68ccddf1cee86e01c9b7c0144b311dc09737d57089cdebaaf7905056e809f18ee6130f241ef59ebd6252fbbcc1048efba218c877c1a55ac7945640671e0ebd27ecab1ceba6501b8878c9a7c77ab06704d665cad597f0a4ad6bc4e046bd3a59fbace06d3f1067b18307d42b278c6ddb961ff168c205c0f4f872dd10fa077b7daa4fc25e2bf234c1d94848689cf4febeb7dd0817d56bfbd034524590d3cd69e780fca7f49ba6ceb76e8c1889a3c2cbbb0762bc8b434854c0ffdfe04085adc5d7fd8341e1d3f5ecfbbdab41cd4c80c72b3c6dcd8ea9630c2221c0be80795e46530e21a828f26cb0fa55880c127b059b968d910dcfb59b044176d82ca0f2c67ff28e56218d44896a861228e769cf2616d0b1ef07a598cd8a940ac4f4f05d62461c9c14241b9f5072490b9bd09a4e95b9f3e26afee603ea6a4ec95dded0f1a2538b0efcff1ec808e73669c36df0bb9de2a7b25470b28580807fd7b685d0e1cedc076ac99dcbb166c2349a6046c4d3e8aa7ccad710a15d6f7ddc461b7710182402da2d909cf2eee7427ab38b0cdcc9505928a76a2ece24261b9e47ffdd60c824e4ec14caa27c5f185878a5b5832bee3cb90b8966f08e1df25d3913c72c505d5936b43051cba251785724f3f21a114e0f184d3ecf62807260b2ac86fa8840f4f0861554966445394c4082905e5d8da75a3f1e068366e98312088a93e111602ae3bf228c29390ee8aa0706245750336bf77ae40935c24d92e5c4815cc36560d7926e77238bb7aa2605ccfcf4d2399d36d9c0e9b8c1b9e973efba8e1fbf0c502c6964a26ba707ba9e743c5f05dcffd4f2f13b2bf187cb12f92a2a96c68be9506b79903e78dbc97b451f113f9827377a33f96a89e05cac3d5d7b9e2f3a68e610dba78a42da14b4ae6d4de224f535933875e98cac72a322d83897a46de2601a70a0935b938a07b2f1b3c3229b8c213d7a8454645de3b4c0abea05b177cb346520b4b57ab4dbd112c902c295d5e9c70caece6e848c26250e1bde26147370089a1028a385f4cbcf446f2e4eb50a640a6f754cdde3154066d657939d89560173120045229b5ad986b8da057649afadc8e7d32a01974e40ac0aae49b81f823dc3fbb0fcaad924dfd5d020df02dea72c5d9d6d4e1301f7c9031ea2115e88003b210b60f0e1557463125960487477583bf42678c1e36b5cc21a57f562a42e8759d2f06098bd1a87edd22860919dabd94a782a2edd71dd92b90e672243c8ee039d6498b071efdde3575f9fdf02edbbaf1b7e4543814aa416e4005d3367dba7777f05494083f0810582d9b2ade551ff4408c94f95598164210037884811d6bb62f7ce8520bfdb65bbf538c332c39f64ccbe47526d3d525a4e5e8418660be5a68c29308660cbfc3728d762977266701f873128e8c0a1be969c1bf3a6d0146b76d79d6bfd70940917542a0807e6ae41122e41568e9defb378aa97e81563357a811ade702d60adf978f3cbaaa5281b50dc36f6cbaf422dd3782fd2fb6ceea22dfc1daa58df00be4590cf1ae0fbdb601913511ba4001340c859bdea4c27b2019534f3bed911c0ba285b4c2e86cb7bb6d7a63d42225082ab086da5330ffdc31c7110cd803fffa05a67f387eed0e2c8ad597bc70dbc6256581a71fc783c6a46482aa337e19a18007c04f68c0287b8d67bff5d2df2328bd990afc5f06ba5bf1860f0f4b367464a400bf9254a3cea617f52c0914ab8c55b3ca424797fbef9dec1b1968f8d3be700d044c01602a7fa630cb1333d93bbb46a06bacf33b41fd9536f75c9e2cab52a99e074bb54808ce4936be1e834caccfc22f1b6fe95ca3dc9fe61110d7044b90dd560e7983cca9680c838d16ace80671ff7a8187996ab3ba728967f0be634ae4452905ea248df97cbecae786d8de14787c6d9bb50cfbf0a0941e181d2f8b2988538d080196b32319c11afa12a1fd670dbe1da4da1d512f16163c1cc24c81a2a683e109c0855489e7339e0c02501454958273b551db94e2416e30585059f36f7abe75060b55f4195ead79c0b47ad74b3358a05f4b8f48dae88d363aaaafdc639ee36c000b634b766f638a92137dc1bd35e515f4faa738285f6e62aee8e1f0b479172b099dd621fbe820165aca0c4cab402cde02666486f51a137b8c9b5bddf4c1a70c06e3f7f2fd618b0223788db4958f5ee3d034c720b08df79b189561e97d10d47105798bb0b105c5e4ca6669cd74886cf41542419b4012567fdd220d488c4bf6d30d8c8682347026a1aac889c74c31a6c23287ab862f7174ca0b95982fbf465d810755724204c9f242eaa8574afa905861933721bf29db791fd5405fde784b506502780d3f92cac1621d3a78edd3027a191759792ed3e2c3346132892040a1f48b01cae02f2fc732fc280cfd11f7307020bdc5bf3fad212919d49cdfa886eb104c0c914f91ae618347b43ea84a7dabdb4673f9a779e61137640c8581552e5bc14f0dc71b08f4ae16ae354b00939ff9b9a934a96872c4ac5752f0a4558322364efb08a8d6e8d473a28efcc482b5ecb8e82fae20ff6dba6be658e31a1ae789de6e6702f5bcdd837241599f6b674bdd17e07c1b5bd3a9f49b8ee4e8a071a1f3049fd5095f799898915b64bdf7b95bf6107af49108e37dfc27f5b97f9dbb97d36cf39605a870cf8ab5b2d6355c39ec8d707fc4e0be499c743d7ccc657a3b6abe096b7a0f11cabea39c3a61cb1a42c88d3bc36d26206b13d7c53e497726e65f454f18ae06acd7ebf0d8bb2f63ae1537373ce69ed9c65b4c46b4d582cd5471dcc9d285d504fb0a8063614fc87335ef44f77020a158b53097d72ee85b19239fb866aa86150ae870cd6c1f8bb86d443111d4248b945eebfe6246e3cf17fd5b13fee1ff35b50eb5f58f2c8a36c13e8447075929ce82e509ed19e63d347c144a2dbce87d33e40bb53e7cc2a31154de4ec5ae76146db2c3ae5bc8b53a472625a047b986ec305007ace4e9309794e215a708dd43932692dcc6364c5f94961221fa978ceab4725c004925f96dc6405ee85d0f7722b3075360f2aa08994947add14ce08ce2cf69d4043db4c8dac58fa3e3e13a80e9e1087cea9adb228b87b40922e8bf626b50a531056b7c29a9dbc7f2c3c34cafda827cfb8d1a29608f96b629bdc612ba08d3c1f407b769ab8f820adca00226bf94f10572c09c16dabef485574b27d4a592302ba40ecefdf9e16472596adddd35539fc41e042574071fd579ab487ab58d4fd8d8b80673e3809fb09e00ab1cdff8d628df800723db33095e9644ea0b078848051a6c0815a597990c66ed58a49c1a882f6fb98beb92c5a4f95263f99acbaafb310ea10eb572d3e7fa254af28cba87fc73d12696d52a9666e1d74b2618c61f61427e4e0ab06a53fc1aa297e1c1cc97b85b3bf0a76e1ea2d84d46a7929cd9c33f4ee5220a689c0b99f1b77bb677a89d4919f85e152302050ca441234d6668294e8554bf0bf05c12a024dad03956c3e71b77679dfe44e049f105dc37be6f07f7ce034d8a038cea4756c5574ab2b08af331da602b123e4c48ec5fbc79a1e1619de988bece0ebc8d4699a8342d74aa51de0297ae4c0bc372ac8d5ddf682cb6e8c575d9ed7407db7c382dc2fe7d005e7f28b64131183dbafe80f016fee800f8154ea6fddfb10d071e6c4844fb93ab55b8216d67c329ecba25361f54ce5f129ca7d95af68f5907c7dd785f2d44b735078d9d85c23d24fefc3c2fbfb73428b37065ed2a37e4140745810f5d09c84029572ed5808d5328bf647a9267493fe6bb986d5dbdab587e08c82f7d0541f61927688af3640983368df5fc4dd7f12406f526622688477c5a0a7a999bd74539b9d8394039574adc34481b8baa55194ae8ba963c7aae52f92809cf7c4e08bf94fcf0519aae4b0d9018de1c5d54b74093fad40a1e379c6d8bad01524c0a1c6140b8d0980c6cf202bb9600dfa914e025fe1e60c7a4e81af8bd3b05c3892b87bb58aeaaf3664d01e6cad4c1e699f50842b2235c94877be821f6af08ec653bdfaa75288bd30eb8951ea1d3f1c07e6d95e153185a0183b9519d0e690f5226a0abebd8426f3c8b81a2ef59adabb9acd956fbfdf1687c2b8858d4624707c642a5b942c628a46be04015703a0ecc6160b94a68ac59eb428c7e730c04410240a1dfc9c2d4f86ff0b515fa433e607ea565f3316e356867dd6f284dba6ef700b7bf16825d6e87ae1da1d260421ca3a2402da69511cd4b55dc0e276b4ca61504c483e022b236f9ccfa169e20c7b799631a1a8144245453b8dfd0c415cf5c0301d7c954434150048d2db19e665c4eea2620bd8c92eb4a6f0de4be567b2621670ecf253972dc86805691f520559edeaa18a3158c145f73a4174b9c5e14b9139e07fc680cf861aab7e581ddeadc5bfc25f3abdfef060341138449aeaa4bf8844c010c8aa2db9345a4673488ada9b03c0732d796f74fe391ee337b9d391d126c45035a9c6285292c9dbc262c43d6ea067ad9b78af6ea05d7e372a0c3395199fa300669de109c9b3a4f73d6feead31dfd04aff779d51afc58815c3eaea53fc631220257a74a0e20cd18f75c1e816fdf701181f589ff711207ea6d31d8214d2459fd0be8244fe07792aeb38a23d267cb90f0b62f871d63010ff351804cee6a4245c009d5559882150c1d490fd72c5ceafadb96518bb31dcd23ec7b3e67110e815ba905f8ba7354b08e9a9442552637d6196ea82d89d85c55f8a2aba141883e6dbf22043fce50360c6841f821550820e178d7aa65044cf8df8d2e3ed0165ec0f17c280d55ea15df2f7591c9b9b5430bc48154966caa556b124e9c82a259b54417afca04024a2d4a8404b72b42ed3aa8a0d58681ded9b4c79f9e2f7b0f65fc0f067c290ed7a813cc3ccacbc6ad3b3beff2e5db5923245ab9ce5980e04fec4afcebc3e5086db8917d93c4c36ea6dd8fb832b42ce017d242979f9622f4dfb56f19b710c50c86fee367f08ae398fa12350cf6ea9e3f02304ea51a4480f0b6e5db879912d70a5083d7fbfa72d9c2312282dcd14bf6d7df02d76217a57057c6c018f00b720406d586aea6b712aed2153b71495f6788566efd13ef83af4b97703852722696450aa5c95eeaf7d233e09a7494a622e976a36b2024152e3d2550e3c16481cfa51f0a0251dce06d84a8356c4f44ec2aac2ec3a6e318f91154262b9a66cc83d4524e039bf7661be91a8a74ee96e6667db9ba9b5a5b77fb3fe01c942441027e6cdd010ca2f460d1a7b80b60fe866bc4de84d14891360afc0ebac9b712f851a420b2510c8f6932c05d2664e86ef37604ce4095a2c24e4920298e31188f0b22621b7b010e41267131b3f298f2d620318611d41836ff7f2a9c5bf12ebd55d2bb8b53706a08b751a484c683ae034c334c912cbfc31f06cd1492b03210ada53fc03c871cb6020548c5e502ac55873bc1234ba8424057407f418de57b5e1e65e65e84befa2b071ebf7b5b205791a716c24d60fd7165e25ebe34e523eefbd6a7dd55e6334d0209644f2a77e4326dc4d650e42e2734ec12f943c5554d480e206d4c02b326ca3401cc34f1812ec0c138a8a1adc4ff3f0900d0a6e3ac781e514739816fd9b3ebaa01a043d3722064168745ed1e7abd00d13db2f817f341699e1d95a057020c9c9a0c8471f8be4d78c21e0b8fcc4d6988683e399cbc46a77460f899d698e2769ada0ed6304f05cca2cd8d492107ce32328f6454e150e1c568b9cb9ae76e9fcd74da0f7bcd4fa301d47e6ceeb64b513d203cfec1a14b1b0ce766ca2b7b76344d01e00328dc94f66262f4d4e9f05d7ed3f631b71980242a3fc3390f5b2a17931cf0e6020dd405ab2f6dba054f20c15ae5b2530e20fc9d3b5cff6ae03bb2099ad8b6650d47e551e29840c7a3af5592b12bee0ec826c88743cf17319b9237933544fc30040f467150422d493469a8bc7b290419a3b5ac0e12dd82b185a3d689b51308d20660471b6e27392468e6bdfbe1c8e97e71d28f217f4766b392e7966ff2e734ed05e9434ed3865228e9bb974410a30a4db297c1bf6acd48116b9ba6c463a673ed0472ed8fe305c4da3befebf5d502f8c3cfa79ad7d88b45fa3977085332116c69066610918f4fb74eecc1befd3f2e69d3f5e198097bd0c9842a541779f92eb27506494ae483fa7afdc1191f394ed70cddcede4819039dd3d6ef388858050cadb40e5d7354ff9d1ee52674e8660b815551415dc266568213832ff3244ab70292b108d448ee52eb8b47fb5b71aa5eec54f09d595cbeb021e56baae6609348ed70d40d234d65a1bf4f409c859a5f2365083398fd5463ee536ecc9755b4754d69692505551ab8ce1f18cf3c1c176ccd3ed94c53964801cc9f45857303fbc1ee56d8ad00ba6aa5a54245715de37762ea80e11561fdab977b48f489526c7eacb11de664077c831ff5716a56dfb371a87926be80c339ec86f37bce3f4b319b339b7271f70a928ebef14e8bd021f30b9a2627ecce281a0d2c21c240231a77f0a78d8642630d5bb20dae55979d6b6551e3d8892008e2af2f4da750d9ff2bf0e097f6d0f80109fe016be02b7279117d12ac833bcf63c46bb274d71dc3582540d0264924a3ed0b88e1b27c02dc0c2c6b94a01ec02ba6ceee7c0d74cfaa0315d607f4dd38112d00ae7c1091daaadf9581a1d95e8fa16a51690ca9a32cd1067bd4bdee63cbc8f304e3cb7e2e70ea30f9cc30dba0d50c26318bbc3c4353501636a4fd092dcb52030dfb10033569457345cea511310b8ac9c3f7638dd36484040f9fd358568679af08d8c7355b9855d515f47999eab3b135b39e611f94887e1bcc278a8084b57e8e0fb2892c956ccc0e008e84e167a228508b03f49bad890e1c7ed0791cce3a7d5d00c9b20a71b62843ca34c3445aec9c6e76c4adfca122e998a82323ea609a462600cff9a28597e67d3d19a1d9ba47395286ff836bf343a042a6e3012fbf0622bb079cb5ea29833e6282fff347a12b32ccecb2a7330dbe6512be4d4203ae9ce9110de41d3681cf43d0a80fd83709028441ac63bcb940d33fb7bb0c01bcab116f2f0428eecbf87993a73bc55b93912ac8ebc23631c44a4a9006653d035d196a0a2d09dd0dfbcd4f07c56cf47da88e48c2f67d5396364920b8ea9b3ae86a7df54238077a4458cc51589ef2078b3ebe4f8b078b00c8156d637672a719edf12e5fe9ec067b2972b80ec2f9747270748f042dd2a9b2a35e96c64407b4a345b6301f590407d45d9828fd360043d9d0757b8d32a5a5fae234bb43645e5ce4d95146c910e4072b9bdf9d3f555abd7438c20821630e72a62e640a374033a971d52c8612053705262560181519e2822f9bc350514442c58b0761f7f67c5726025e23550c9da80c97ecd16e9c56a8e8e2b33d4a076973d7cab7b9c119052a20cba15f3305007e068da9c16fa464531a47021c6ed27ac7a6c28caa472a306214fd2d6bcfa9d8c50ff75521a377551480206b18183b27921dc37e27aa686b581f49b02439daf4e40cb4b73141ee0ebd6c10291285e0225d863c04cb3dbb6d4bca348beea5b16b4a0b2a6c3913903791b2c841fea4321776b647d3c367a710b68ff661de9518646b0834021bf0119e4b2e55bcf4d5e7b04626a92a3e124c8296382883ecd9a9cdc70a058760d77b227eb5cbff83c631346572a04cbf4ea5f101ec578ad598f2cc0b01396f8f7ef41ad3e6a0610436f71ce32a80d2272426a91be8b6899a4fcc6af107da520e92f43a0c0b2617918f2da88e50724a69fb8f08ec39e43ebc86355c0c071ee9deecf828f27f22c72b9dfa50a330cc16637722dfc94869c8a03395131201f61ff990e94e936579d0965a7ed235b093eb1239d9303f3216726153e4b4e90f4a864aabc64cedb7cde7309bb29e8f1679e1d35dddd89914632f28b789747f07c23604cf99a7aea96bdca25a7c0eb1048f0d0ed7c538b3ddd7b17b9d663cc806a822b1b6c20c748743511e29bd6bbb63a92905be2fcbb9cd59141cff7c001800bb621adc38dde844a40136d2e792c775833bc280c2b830b68ea2de1b8b833a07d7a7ed08645bda54ba4301f26e0f29e0576b66c854bb2b61e11f4332fcf7a90f03346f16fc29d2190bd5c3c1e5c3eacb0b1acf38a5d805901173199e6e4fc608da64284c5a840423aa43a32e39615cb717c924176b7954da847eb619fa6a6609f39281f51130ec5175209361935ad03f32f59038234b0555d87eead6fb5a2c0db639121d4e4003caf2e27058e5709f78a53f6cc4b5b0fecd7266e528f0d70e0515845c905b136b752975689bc8604a188405fed19ce6d003b829c601bb6b17020c4ed76cf1c5e2b2c13b36011f308a7cce19b2ffd8bdefd0297fdd2ed96de705e8b5e0fbdadcd83fdb76cf89c23c73b325585a17772a5bfdf8ac93b94480660c2919ea069fa4b0288669633a3d54b930f5fa395f321cccd058a2d7981253000940eb471834db32fe7700dba57426c23dabc2499ada11aa8e5bcfe568c4f5cb0b0c2a9baff4957d45964a7ec37e97450681308f270dfa347943e3691e8faf8e0ca196a02732a75d0400fee8681d2a49cf83e04b162f25bdd825fe058d4493f102a609181b56261bf5d4a195c1f14c07dadd9a9d3ad47e20fbedcca3d66a6a300528ea3d8c5f1bbf699fce60869ef4ba24c3a92952ef89b25de660884ff5145404d6e87dae6a06b41a16cc020fa0840653bc7a40cbb248c58383940856809c9e0c09add695143f7ad570b8c5190355e5ebe8b2a576b98d7968b67ea0e5df6a5803a263ad7b3a2a9d9f0e51bc97de6d7ba61410fe17652da30e76ba09048c90b20369e091ea6c9627e01d0c8eb87be56123cb8c8643ffcbeffc5bd0c10078cb950e13699713db4efca2b055c2ebf3bb08d7b1f2e6911b58668ff34e81b1e44dd2023e494438fa17cc3d7e2573149a648f81fa190ffc06b9c38a4694d49723181104520c5496eed090e0cba75fda6813b95ce5a07803e1cb7666246783ab3702e30a10bffe445304ee6250edfa9c96193f35763b9ab21eebfd78d75a146ef7b27c09e6394e928331aea229cc0957ed93bbb3828370e7276674876bcbe9a40a4a0c0f9678a22ff7a4d289555b0b3a46d0a2923b1d99d06228f0da93c15813de6cdd0a99da82ba4172a6fe8896b605e0eb8f5b156be2af8afe083258ce2a38cc263305b521b2dca996c041fe1e999884f467aaaf2e5678941074c05a7844a88f619507f49e0a88ea12128d9ff573280d65470d385a4a14c3b461257d1e6611fff34f0b5a61d745cd34f6566089203452498690faaf2392bc11c376e0078eb16d09d3003f2b9d4937fa80a9f2b41f09f0ab08d46d84dc51080639f4d8e08195b03de20b0a9db2cb9a8b33ea4bc495f90ce781563298ff78ce77bef39ea96e37d66abc0e2985c5bdfc1782d4b6a351da491ae3303ea79278d07deb9053c3ca5bcb05a00a7fda7e61e1f56bc195926b9fd767a7813b3eb0e96d3e4d8c7b44945ee58a8b0f6c421ddc255d106541ff9505fcc73ce198c5db718426d207e9965c2f665a2e0faefdcca951c06887238fe0f63c67f38d3caab59cf140942336522b7476f7670977d1d2aaf6ad40b177ecbbc3e52d4686401c4125c5fc8afb2e7fd21ebbe746050dbde8816924913ad82e86ee96e655ce994bf9303785712248812282670d0e06e4a93bef51eddb447f7a65791d64616658a5ed0db0322e0f2b24a1e1b8dcce0583e6e0f87b7f93a5c2011b65f9924f59bbcb2060a92e36923c6a9d13b2e665089eb0dc6114a685fe05c1023ae256353f7c1ee96b04a578704dcab7c5e17acb086368a420ee63ac374ce0f02dc1c185ee507dc96515cb2ad8b2460ffe6f17e6077fe1ae38602bc3ce37d402421589baff5dd56d0a0a66f3df855697f4c888c20cbc3172f2cfc795560c3e5c3823c7ef1ed9e1515ba368ebff408bf6c3371a1406255d67bc70ec69b86e9738c3c26c4967fb1efc493c8cc04bb45558a6ee8d470f4fb720eff24573dcbafb75c533b54a4729912f067a35823a56c790f457f6da08deceea2354bf5a99afa8960afcfd733d9e24dd15d4df748a24bfd11bd7461808c8f9ef61efebef8ba60a37e484ff0ef885381970d0747b10ab3e5f21f6b3f10df360511359874da7781cd6f6fddec53a4f912a724f74106e96a8b115f43346047f466110795487bfb85104501a7de6ae76784f1a1880f63d40b553e3fccae90e658ce849b4ec0008dbc5e1a2a0791a19ca0aeec6896b9e8c152ac4452d72d903ac9894a9a3049daceb988d6ca4a6808f9079b58ede63dfbc262f94531c4d2705397b08ca15958b64f52b19bf1e98ccc2326c688c10831e269303e6b22b47ca0dd0934de1902e302230a452904d545bcca1e181ba797f977ef43d8a1f9a627708e3354eab711c3da5bdeb95d32db8802d93d221e6571feac35d71c331c4aa2902ff185d232615ef6e67ea63cec0521322d7667daaca6d5fd7d2344afd79e4e407fb90e8749944eba5ea59f39414c20befedaf6c08852c54dbc22e033c98e137087d1a853b3f61a59bbcfe03f5fa54828d13208d84458ad9b0f46180179a7bcc0ae482f2e0b3c10d9b2aaf1cf665d0dcdc74b304a738638650417e6a411f05030faa75f1305409d50bfcbdfdec8fdf1dbf618a5db620a276c55e8b222240373703400f79bdf32a7f392503007743c75f513b645e9e0b1e59427a22690faa284608bfd12a949e1c3f5bb01b8d2ae604e84f2f4c5f75ae996f49bff9778622c9940c33d27fcd39d754a1c961b4dee0375cbfd42d89ce9a57efb356bbce3d5e1b2c0978894b43385366888bece77f0160fc3efe1e9b5a7570a74add6cd94cc2899501a9bc57514ac9dfa1f0f72eed1334476e7559a6c7709a2e9199df8b4e4978060e14f535bae6776868a9b2d207cbc16c1c8fcc0a72963dc4b1dfe1a3e23df8d9025fa3558683789af8d126282de4d6ca9b3f1a61644a10332fb068b34bcd9e210fe0e202e03fcd5efc4b1e77db443f75f7ab7e09cd4f111df691d71a11aa67040e993d6c61413c6a8980f15077322ae80f6a2b715b8ac75017ce5c2afddf9fab0bc535d9e90a43770669be198029c656c412c2a923483e6e6daad77344a2819a059cf8f651c1519ef1fe38b55dbd718f99195a3e36c5d1527061ac851378a3aa0dd6af9658355a1ac6c15a1e45fef3a511de69aabbd6bc083f554dc8786d5a190469d7b057fb58727fa537aeb0b5ca9efe4f5d89f210a8198e6a9081963ccc480b701c8caa3885521503c80512f575cba6494017b740b55deeb3be7c49002c3e0dc25b51b2cb9b2f89bc37baf2d71339d430b0f9f0c5c7bd4f52cf031ca74b77062d889a598f66f35ec5108185a96c839e852afc9aba532cc55bdc3b514532810caf7d4dfd2ecf2bf07aa4765266c1e7db0d7cf946425d77d918161affd5f22d02d18fba12a1f8cdc5b7b1a57515c4edbe26d3c11e386f9313458ffa8dfe11210edc973608a9b164a2fcff94fece921e7e52b69b08b16a5d79584129924e47e709 +generate_ring_signature 70b22a0dad712049354729ee250c77812c8d958133d282a7ccb5c7ce404a7b18 fe481951dcfd22fe788830a0710c2d3aed379ba59ce250ae71ea46a62487f0cf 1 00677c75a2b2cb1c5b946207d8f6a4ad98d9adacea1e5481fb84af52da4c48e5 e6bd4c1635814c70cd87d7b1e256e002a954c151ac5e0e6cbbecb0ab7cf74801 0 2d59bf4419fb732ab7ef42fb96d657b32efb1380f772e3ade4f4ecfe0aee7508595b80e87eb9415397c93bd25ad85a328cf851df9efa5b080b5563bcdb680c0f +generate_ring_signature 70c5e47ad8fea9638668fa564ba969be479001f55eadc7869b102d77b5cc5214 af2e9cbb09401113f23399722b7118d38070d67810cbae3c18e82e7b7da6f472 206 a18422fb54e5bc1a14964fcf02ea976ad907a99c9de543aaeaf998f049f92d4b b51182e7531087236c2fcd70ba1c99604a790e6543540582a638652844f962a1 6f8b2edda8e71567c9961ca07b818c8dae7288aec02c148da0bcf1fc96bdf146 7839c78aaf168c645786b1e646261a865ad44b0a83024f719271bd08e9db5cc6 664a1b716e8f6d679237984ca759520ab88db8dad87bf34d581c61137f93044e 3c3120134830ece01517323ab484002429a65eeb0bb17481bffa97774d5aee7f 933409cfefb692004ee42f1c281c0451050964c115bcd81f79dea07885f44948 ad6921f89907b538f3f08e34c1d253cb0d5b8ff668f335a9cc6e718e9964d170 1c2aa68c5a31e9fe7bbb8623570e67bab36522659cd88321c3f1f1dc468cd8a5 66078deab22c8b165e30dbcdf5b9ffd5734f7df003ca3fd515e535107e0deb0c 4330428b7fd83c847e0949054d66632dbb03bf17f9255126175d6d551b8acfae c6a01a0bf78fa2b335f0d5eba32fb0fed7a5453ecc58042f97a57abb4ab93768 36637941f031cf30f9abad0afa6ac9df869c4dc7239035f00b2536c83eb07f6e e582018b9874cb88b09f9e30ec9b471cd154ffe4b79685342f4959c653c7e30e 9d988d6cbacba45ce8492043930ada21ee880e0f9f7c2bcfe34a978787b7317b be59db2de9e9c18d852a319b8021db050c8684e9b4fd27cdcc70d7972f262235 701f1b7057adb0d1a917d09f9703ca267c6523a15a42d4b2903d1c68bc9dbdf3 ccd77941930d74002e7be55e8cf6b51b2efbb534250352f9841cab4b09d6101a 9bb62944367b74b680de5a491449c5f4c89922e21999e4749c63bddb96757b45 d4f1497d5e1602dc78b67cc2410369ca0b65ca3f43269d48a505f35794abb7a8 545c4f9c95f6387f51da3938bf05d6e1dfe24ce7f47d00673047c6d15b775b85 9e16b849e343e81d0a8e4856e5b61f24e5839b3aae6e3bed70218a831b7ae4b9 6b85c3a6f57abef1ee42b1929198b80bcfd590d2927465bf86a708a8d8935016 45702a3b1ea9f88fa318ca774673c9f5305b70f81eabb243ebc51a4d456a39ff 14e17ab717131d76747f7f89529456ed4b7e0b4bfdbefa66b479a1cdfc2cf1c8 b28bbaebe6e5a2082179a5d40f82b858120207ca642c4bd208f86d1981e968ac fde5f21d0002ee5fb68b31f336de025dca70152f8fefc8c96d9369c8657bb45f 6b6250e0a9e114274c470d03b3a09adb267eddd060fd7180a3d59f2b4c84a255 3f912637b519de978a550c9ce554c4cd8aea77b7c67bbb4b61b7d35dfcd6cfe4 2030f7603dea048af9075f8d5f94503afc5e9de977d18606915200f186c7cc91 b243f8f735967e6d2fef727ce4ddbb07260033eb06e22b2c2a6e7c573ea8b957 a4d61eeda78d420b95dff8eafe85b94c4876872f1da29b344d213ceefc4913d0 c6f13c6b465dfac7794fdc9b025461ee81a20a5e12aa1bec8169cfe89c66ef29 a4653ef4b4897b15a9842bff5c3dc1511ee1fd1a4709eb9bc11d479628b5a49b c5b6f5f523e94d64365318fca8eec9f9b69d7517b9d0516273478024c07a552e f9e16f2e35b355a188119e38acf7bc16afc4e0e239fdaa97dc487b9789ad2093 8e32ed09461857c41eee4eb6f4697107373ac5bfd404b2aac521aba53c1e4606 da10a8ca7ec34b14f75c82fda8a295339db1a95dc0ce4d51c92dade911a7118e 00ba18bc2201748eb0ac8a35142ef36a1f054f45cf328ce6d4c3a7399434b51c 77e93d9c6e4d0fa69e03244504cf715ccb6e1771a68c5a4bd01e7a7ce1e89ba3 cdfdff3ba97751f3181e617d7303bf4d8dcd5163bcc40af5390ee071a729370f 36e2b4cfaa03c516f3833c40d9d8ed3e5f123f81fa878aa7f56ee21c687a1c2d b978726e14c14fd17ed98d7124feeae05714a8109e1dbc14f503a7f04178ac0c 29dcd31fd684e980ae3fc46e5da92f7f5e78babdb806087ca36ad8703734af60 c1c96cbb9e90a08dafab66240ec2a427c9427dd6cbe2c649aadddfe2f2cac4d5 ddf16af1d9f17774b2d18a01f21633c88e676729fb92ad1ba2f5a6e4515faf3e 7f3bfe385a2aa865c30d151d1d70f7780b9404c3ce0b80cd8d76ae0bfa2d48c2 33cd6755c2a765f34711898c5cdcaa2ab13ccb4035809cc714b8ff1b49e312db 6a9c43c587cbc9040a1bbbb5ee5b31b5fb15c8e8bcad62df9ee4234c0137b330 ed1e2acb487769fcdabedc3a39a4a33f00689ba64d2853c247f1a82ab5d9c900 a7d0aed414d50b92555e68b1627c84600850b2f67023eb90124900fb54dda7e1 9c6072d044134b540033a836fd2cc23887a8a82559355d7e05934c46257ddfca ff51af4f58762907aa34f99131a60a7d303391b059d05fae1d4d4e52e9a7ee12 ef073664eff8f23d2db789333d15a35901822b75146bc40e6a9a60bc348b0f43 b20fc9ed263e55e0d3b17ee113d43547e373cdd165339861d1a5f00824954bf9 da9614943a2b671472b15a1da5689b2a22503e468857e9fcd5a80c8fa0616927 b1d8c70e8927f0a0949c7ccabe0f8420c7ed3db8d0fdef72cfae6c34ce3c236b d2229f0f33a9fa68062a177416c13ec529f36dc95429c6b3e2030e7998137076 1cd7d9d9e748868bc060399946dc24d156a1b392d6894bc1ecdc54874eb26d64 870f6592e775e7787cc3141ec39a4bf8fc40216d768b215ed2c37c020663f568 c7ccdd2c40f607a04ea79b14cc3f0e1087c1c1aec9cba9be3e9a040153878cd1 4efb40425203e4db182ce267e1ce8f9fddcc7dea9c231875dd2b477f6160f93c c77b8ff4f3990a4a716d9ce40d9dea47d675d4e2e8032eb4e158e71e22b593fa fafea62add24d9bfe2c5678c3dc32c3460686350cac842c68ee8c9dce4befd5a 0d9d2879be4d5c54549a652a0332937b19a171d08b87355a057c155a49dd3511 7d37429fbd0fe2b2188a93c537c7e55283ef4fccf2ef28dbf393039edae68d1c 9647f768f127751f8d2e8df5bdfcfe0b50ab08d002b859a36be77319d2f626b2 25a2befe5ecd5c5afcbee35e708c513e9980d217b9982a894decf8e723f4267c 49fb16a530ab7e69538cd98e63e69dc602ecf3422f854d0bd9dc0c56a6fe435e 9a4cc03169cccbe20d82c8d1c88cdad2c0e16bb32c22fed4a5325ba24c5da531 92ad7fd060ff5e009df4146670aa6f3c48add5eb9201ae12c3545334efc68d8c 5508d45bb35097749c0bddd3041a15f3f30a73f8d8e6bc26edca97d2b3ed8eb3 085d6e6167b1711a5f0246198a0ce033445c56c308caf2ffb67ed4bf492a760e 814453420ac7935b45302b60a76d60ceb3d875e27d84181cd995ab0cf7c194a4 66a3b5485bdc1fe37020e728315e4952d501260adcfa287640a15e4ff29934bc 7d3202c71e8352547fef9f2effa942969472ba6b2b711685f1e7ddf767bde373 c603687740f6353258d1563ed82e5e72b7ef7738425b829089e044312668092f 78a2b79ae83a3be22e894c61f9dd9944816749201ebede34760f0d97bea2d29d 77915a974635dc324949da90d5a6414a9abfff9c6a1fd2de2397ad515ed1ec4d 394e0b6cb9bca29f0889e679730af807cc382f846291fecc23ef891bf4303352 03bf7c7d9e5f15da8d8f4d8c8242eb7d0f8173f66c9a709273e1803a073e7c0f 885a745d8efe981be6a9aa5eba6b24b23032229968d62ba2f680a437051957e4 bbbbcf28ff988f39a47cb062cebac6a1ca52cca0b7553f1e64503c9508ee27e6 e4253508f5c9313bb1519bae17f58c685d49e4194ea7b369ad0e079878d253be 33e5fd5640355e45d35e4dd7f1e8e7cfa2998a9ef48c9c887fc31046881663f7 8aab3224dd2af87ddcb5831c1282df4fd90b353f4269d4da50252aba203d19d4 be6533d22fa31ffe42c80e1a1eb71d65bb27a8e63c7f382d1a00c254dcd986f8 9c0652aa08322b9db87e6b8bea6868994d9ac10fb89a5c1fd79e6cf05e4f451d 99c615eab0941b915fcdd01beba0f79d841455986f64b211a74631c04adc45f0 eb7436280e32dd1c1fd11ec57d44e4b960630debe8fd3fe9b16f7e25455153b3 dab838aa3a6646ae8523bc8e612c5f76bb5cd21bae09af8ae035434a9545e1bc 6b0f31df949cbd288c5d24063851ff1c107938cba425be209728e09dbaad55bf 83eb14b50510e48cb3f336fc4aa0003c1fd2dff27e26484c9e3cdaae21e0f73a 37be5905a9f43cd4e56d974dcfd016461c3d066d3fb227d6411e942705892208 53963e49426946c393d7051247a746783821d70b04889a094a5c99f0107a48fa 3797c08b0780ab73b9f4897a12174a42871968f024f767ad713f8a7c35995b5e 35f08b38741b662a557627dbb793e4ddab03a144afe29f15d2582f528e3f54a9 34087c19447afa9ebfba8b6312969741fa38dce9a3c52294c08d949180cc79e4 7352d684d1137c23c8d55ff9c01d6dac19ab3d6b8f8a30ebf5f9789920e5c3b1 d20ac3c612b2b275c0c708040418562f80ec07fa66d5b8bb166d115e059421d1 b34a94287b0b92f980fdca1985f12feffbc33e22194cf83f7c64f9db3959d810 b00a64ca8cd8d6dd32d2e23e22fcbbda2c979e3a25e095d9bc984b75e29263ff 88905bf24369828cb2d7789ede9efa19100e1de11fb23fff84c5f919caf1b200 f550c0946b5bd38adf6cb83ee5f66a33f6df9a352920a0608cd6973294b88591 b84f4771e6e6bbfde0ba6380d7b0adfd745782a79c665ef158b35f30c3f2aeb1 6125547278feb8959d7b44022834dcefacaee1821a2b9ad98072409fdd1fc5a4 2bca5c5a2f52ddf22fb94c1ed7f094beedec8423c2d908530d56bdc6dd8b33f7 d122a98f60cbfc9afb08c4a3665a4297bea2e28e1aa14248e534792ac9be9754 0cda988e49b35ca39b860348c499ace34a1cedef1fe980bd791e85cc9a3eeaae 50405db106877d4f79bba49df0d00186a34ba02918f38d10a6772a08516f358c db86f45ea5f440499c1fcbddb9e094ec839219d90ff65ec5db7f00e401f294aa f2b186741767357374da23c9c787b00f7715f6ddd2bc13f4ce0c7fd8af871af9 45dcbdacb01ebee6cdb99b6bf299a327d7600f911eb178b6b040e427045893a5 7c665eb4e236b8fc0df26b4e7006ea202158ffcde5bcb01bdc76955332dc4612 e5c13f2bf51221881634a0b3be87f9bd3b77ca6fc5190ba73496de2025d9e9d3 0f5d6fed8deb031948aa27118f24575d129130cf4d54e8c03bdd5b403077b9bc f5f83a10c8257b8571056cf96b205d6e29ae49a045165f9e81661ea60d210b46 04f5cb095be125daa3ad6f020c25bc4eb60fc210c3c55ea6975579c57704e006 310a935599ac84e860c339e951c795d560a8ec6d0a9915e36f54bf7476bcf7a5 02e7263c5f19528ab4807e5cb0de10f754189d00768dfbd2ef200aa1ae8bb2f3 9e7512f100cd122c07927b9797b694e4829ecea344c5aab64c31e2dd4fcd1f4c d5882dbd1118a46af3c624b569edb0f66d6c7eebbbc822a09e74210605d05858 02fa26513c2e35299ec5634188849a3c7876f283d8e6d95875184dcfe90f928c 7c1af6f4bfe0fd44c10c6e17382585eb1f6f6e1c0c0774fe66517839b4e74a69 ee9ccd0b683adee6b6c99eb80bfc58d50ee650f2089b23aad6a53d1514204d3c 7a61e0013420f41c550184125d3810897caee0c1cdb035921461d30c070f5c44 5a3b4acb99a292959c09c3bc164aa35b8cfcd80e8dd1e5a0ac18b4b20c1f7beb bfccdcc76e71a16d7cb5cd68daf3c83ea71ff3ded30cefc9a574d75cd7ed3cd4 0d37450354f70f3421920262eedbb2388b4b3025fa7b7872045e108cfe506a69 7769a6ff5204841123e2f786b9c438157a68763baa0ef3b911b1918dfc908fdb 0281057269fd5c1792dafeb5dc50f5fffc205ef14b1a1b495335d41fe9833641 eb74bddbde03604204f8d59400fe0977d2fadbc4591d25092dd8507ff1ca0441 4984c0958c38d0bf7db3e714963b7ae2be3f14e4247aaf8625a1206bfcff624e 416cc5e0618e97a86a868b745b730cf5082736b28f158a15fe97d46dd18943d7 c0334b9b1bf615d4e50d93c5cd68c004f616c96448640f96abf15a05f259b6f0 72228197fbf42e0bc7fb85daef82dbe1f5e2dd7b173e80b590bc6cf985d5a065 74d54c40ba88a6e7e407696c81045f77b7ca443b5f676f9e7ed2133004c220d1 da36cc9ea4368c90ccbb962bb33c447d7f1c57c6b5f0073f1bc1f453a58a51a3 29346ae924c2c641fddc37b5db1a1cd7d8e8dc6c10f397ac1e4296fc50f957e4 8ad915dcc38e22bbe3808ddd979703b55641e5e082d922ddf95eb55bc1dd1c99 f9602f4dc07dbcae954cb895a266f64f0ae8067aa0de93c828b0b63ef77b1286 127806dd2c84355c8f15d5542dbca5d2f545bf7aeb97da34d09ee353c18df7b4 4808e46928e4e630867cb664a2a594e847858220ca93bfb33e06ebb991816f60 f45ea82edbdbb2b01bf809edd7ed8c7b5d5bcd610eea3e3e65fd3d2d0813e738 b9cfcc23cc02517f75dd3ba4f05fc727681786f67e6cf81edb3bd39e945bce60 e1f5bdd3eb6c2b9315ebc2fc8a2265b6103a68a966992ffc100e00a6bfff91a9 b0fe946f3fd0a6f8005f857f84d96a929971c37caa1b7ec7f8f76781c10f7659 a0f943eaa5354a75121d68f221d7f4fd05820d142c52ba0e613ce40a94ad410a fd4885348d62e21aa70db54056909d38d6a0434958a88bba1b00bde9b5d9a68a 81f0f73e215de76d5d9395ece7e348290fc38c254660d07e5bc1ee3af399c349 6299a39b5110643b1ca175cb95efa2384e6ef5b097e104d538f791b61c934039 7107d2209213b68af9ddf368bc4cb14a7eb4a97c2d12cb016fc4829df353b976 26e8f097cb2958051fab454439aa1dedb6929a218d6ebc0ad71ed240288d5848 03c22c8c6e0d2b2566d02486f8d281d47757cfb55aaddf6e9ce709d0b434bdd3 8f0851c2b5c3ca4878ad6c9b7bc5d0961d2c10181b346bd04024a1c3c01ca783 49ea1b8ca3b5ff382ae05817952efe5b1e492dfad8864520909211db36d9a3a7 4b4077f01a37c8c41d0a841b197f90bd81b0f60e8e85ca241ebcdc75ae7ca7b0 c5094f0a05bfe6bf19b8aa4294ad8ad350c9da12fb2349e19f20fa468a8d767f 28620428aa5d407387a24846a62cfe882bc32222b1e0a11e0878e69142f927c1 469a5d58cda2b621561089a5a84923527f3a7c34d73429b32f12165453fe919c deb95037d7f978e00a400165cdcd21698a9abc5543a86a39c2a3a2a38df0147a ac3a1f62cb0e149eab665764aaed0d534c57bdcdd61badfef5e8fbbff284da4f bb95b6728b9d49af7954d46440c0a38d95f97dc319a6bc801fbbab115a9d227d 51e67336b6a0277b50c6b8c9461b570341de5c545d6ec069133fa52fe0fbe96b bf9ecee1e808820f1733fef3d8b26831fadce90ef25c2c9e69f4ae1583c2ff8d c2ccafa1afd4a0fca65c04cf04372a9949fc82e1cd44d3fb62372c4eef025d49 e6df379d0ae015a6e363eaddabc993ac719fbcbf0c426923cef3ce181bdf91b5 715a8e056a708fcc73e370b8c8189b95be07c48f029aad9d78318bb4211fafc9 33086563a891bd5405a0c4e6c823aa6caa40baae56af2987d0fd5926b7bcf92d f135dfaf3da2b5b44d02fa04f5808eb9d2f98908d5be6084694a0d0f0bef6b6e 86646a742b57067e655ae93ebb181f34b2bf68226725b7fd49b7891c5e8d3c0e 76f0c9c066cdaea4caad46ca803c9aefc3a11dc75aed472479652f6d75f9f593 4d1772db532f9da6aa39d0644867e5ef9c429cd7986f97eea7c253a0e43e8698 57bba026eab4e72dc1a9636f0c63988275e9eb38f66c56b0f6d998fc281f2f0c 628eb7273a6845f9636813f810760b69248603686f21ab695f41304a5259d7ca a83d219f94eaddaf2bbc68d387db7d477f41a6fffd90b307c45342703c987a8e 149807be6e62c586c635336a9cc8e1e12491ff04528da3ccbf4c62dd49014d72 5fced27002f066f6e03292c55c36bc9f4e5be911738f25f889f12d0ab576d690 619142a6d9c825b6b874b1619f723382fb91d1cd94d59814adb195b2df8e2afc 46a9f5f54f2c432884e00bdcf31a1f2832aa3fc08ec5d613e39be20a8e82ace7 1da5ab4a03bb087697f2149715033ecc9fa93f3503c0323f2a4763ee8c8bd51f 481a87a30c3af37b339dc69250c71ca8a922e816667443710671a80dbe52ce92 6f72c0e69adfdaf0dec11ab94da9e9607fe1af9b19d2942499f2f3b06c70ec10 3f27220ae74e591ed12f7a5e53d9899dc3016346747d8ec9fb0d8c6bbb7c2a62 7be4d47e1841078f34a8bceb4a63e3dd7d84a7a15d2180fac6c8cf9409ce02d9 4d781a55176f8de667ae70e9c1bc7cd4fd263930b82f4a4f32ee12957d19b4e1 b5df86e2b91c3dc2f216c0c9ab065ffa1ede0d6ba02fc98df834c77d65b2ee60 448250ef38aa56bbd00f29317b2a48212acd247ea64550dc411ad9b176f129de 90899eabd9f089088d271399c46d2550c63e6b3eee2ca47eb1a1b01220abdbba c3e9bce3229262c0e60a477d16d898464d78d94b5ca4ecb64f769ddf20c5c440 67ca124eedbfd419a80a46588fcda09e2f5fe336b8e69d989c210e613a82e724 f36f0c8aa0d5711221f96dc637e34e8d4cb79166136b77637401449596507b8e 5a7b4baba7c4e9e7b0bf849d2f3734a5b822f30f132d3dd1f262ed9551a2bc58 a528422be9a71e011bb84759bae4421c53e306cd112a78ea9f79f1a2b19b6c17 8a80facacc40636b9445e719bef3a4f732e26fecf9fe9754919ac27b9c53fdee f9cd67c569544c62a0c73392289fb7682d727ffd10e7a79697a4433e06470f8c de9b69ec46282341a42fbbf8de6b54a232ef50cae2ad1d774f3630e60c8eea92 929033db79ef727eb67d6dc4aeea650c01271b3d2a6873afe6e6b0bce5650cba 301ce60b3754afae20c3deb7ca76dc542dcf5e3c37650258e11f666bd9b978e1 b07597015e477302bd1f044c547f7246b74de2e4b069de91314e7023383e891c f6be00b7a0bff65e3d29bee8a690717c412bedad30eeb1b9623dbb7744fb72f6 afefd6e6e0544512a57fd39de6adedbf6d980abc65de3ec99f89ed06315ee378 43dd0a609beb44b6df8459e8dcd769d48e892897872efcdf1783a5a913f5dadb f765aea469de90af8389652bc1cd0aa52fc42e57bb8e546e1618d818128264cb 51e47a387db4adeab17ef6abacc440cea6fc47e00dd448619843ee6aca670536 b2027fd7a8e36de5a752b54332d2449ead5213aebf6c678c54c91e35e6d87fd9 fbc0102c91c3c12182445681f5f123ceebc65bed2d9932afc8a2f6fa78a04b0f 48 2281692d047d66754f0b51c4be4c173edeca1f7cadf6f4e2e4c93a52911495031c88faf5cb264423ac02309eb316b2b378ecbeca2ba385c97bf82d249706d704cd0b12a9e5804a9ad7f3614151744a12126bdf6f720dc16c5e60bcdadca7f00ef615b9c50a0a6fb224acc72cc04d3dd91372d6622023cf81a7b0d815122a680ab92390263a254424a9d30d0a58475a5e48b73538394128a66bfd944a7379b70ed5f8a69eb4f9f22f076a845e9113d8c46689235a337fb4cdcf33a2be85c3ec04da67c5d4d809a5f690c7abef43d4f8d4115b0f8503d424432b2ee26c1cbeca05621a7f2c60072e4d79e2fab3246dd26d7b4d00ec1dee1f149d3365a64cc0510caf4e6449a3c7fe5ef8e485eb5e7abd30c60965d149819ed4d172bf1b53c6ee06c10294716b69ab466964b50a18eccfc4f21a03618f305194dbd168b999a8390937b7a506aa191904fc8b55ecee44bc54fbe4598b0a47f96fbce77bb858ef600b9753cd7f97fe1cfa747a7b4e0a35d7d154f88cbb58d0f6e219cb9849ea43d102035d6ea4cfd79e8c4df0c9b0251f07882dd0bf56d845d4e77d3cb5e06cac41074eca8ca8be72041a753c682776286c0f1a5cb1e462f61c4fe34b8e1b3a5d520f6cf7db15cc7fa7147d9a4aeb9bc9a12d32967d6d731eedd9284dc356cb0f2d01a59c9d3f3fb1738dd939549acb35cdab7ec2687028b46e90384009797eeeb305ac0eaf530329635c0b3127fb9f19d8e7499618276306e2b3a529b2af617d9c02d8db455b024365ccf402d0dd052d0e783ae945edd981d9b2474b8a8ba31e880199ed518935f6f58fa8fb13626e1aa9d24e534568dd5a24084c74c39d9bd8d20b19cbce0fec136ea8f70471fbef448a1bfcc99258c7d11523094fbbc7d4bce90af19e38700a546e80b1a9c2f59c0be7947201f26f50bf8f2dcec61e9b331e1803dc0cfd5e7900b9048680f3929880bb250152e15d269f387de0a8609efed38f0cf80faa4738d2ad80e41dade9f4a10a86e2cb5c53b075c4414a5c646d64e7e40e01cbdfa9e743f7a020ee7138dc8fc3179b9953cb1e6e889632f0006bbe10bb00b7dfa2c47840936f11021dbba0b20909dab77852c7f91149e348c2cbf36a880aeb52d41fa689d20e9e693d9f3300f0e46547c63f06533f41cc84c8243ce7dc099fcb2715177e2bd057fae0f563d17daab53569be0c0835110b41ad804840d20971fa941f4eb15039725d160e085da2ed5f1e392e2464d0a02e1479cc21e3d508ad3fce67fed18525be8fe5bc1be202ecb79e7635484b35fafcbf19fe806e410a5bb070d0dc93d28bcc6c754f0a6ac66d0dee99f64bdb77abd4dfc413873a2c016aafe7abe0dd2c8b3d5314d39258d61cd6a82a3dc810462da092eca95737530780d4597c9b255564eca32c4c1296a6aff4f5a7da83b7fdcc65364845e25c4e06ed6435808edb9344e6d231d354835301f342d32446984f76a73b36c01984c8028c7002f0ed1f1c19b5972e41cac78625cbc18e6d207ee7769d955a4167ceac023dc0b85f4ea3b0d7df0925d5ab7dc79d9f862a68b49bf7c3926454da6751fe0ae558adf2c6e0f685a5f248413b31269d552b887ef939f4305cd1a0b0d72f9302b257215ba01b4ce5c4a138475d95d6ba7e16aa70ff64f680526b4f5289afd0070d8f426774567163b38b76fbdbd4cd23c363170fbe0bfda6fdba5f6b85d17705cd366946e23af146249884017d934c78aad10f4af1c782980a5067bdbfef8702092be91187b89aa2fedcceb1518f9b71210aaa487c810ee62c1f4696348596030f4895cd2d8fbc904d43dc3493a913d2fe17b897db84c180874a8a0fa4962f03d39f2b8dbde2b3c699594d83eaa986933f13ac6c6fa10b3f42b47a516892450a2f6cfedfaad75ad0f24094fec0d41f6aa7c6e5b7458ede51f0961d58a160ed0db6a7935df9d522f23268e4fe8fb500e4aece2a3d240a9cd83239d1f11f368803e2aa58163bd9fdbe880c470f633225cb64d61617cf97b017bcedfcbc6a64170e2100039fefdc23f7af3544d651daad7dc1c3baae68519a014dc737a5fc0fc30e251515605904d9129e97040c503fe92a73b3d9768571aff0c8ec8a322179e603263652ccff21c655d5a205df7dae200975ae9646ff95e1a1d913a6c74842ef0da92a1a981ddba4d5deea5a49d9a0e2e9a143e57a12e5589a1b93e6f0df5efc05c7d472f63c0a177e028f1716c9c6d3a22373d5a8eedf83dc185bc23b90426c0635116f61518ad605d53c2ce996244ed2c6a57f522040ed31aef386a1a3b3ef0e6d45be7c1be2082fd7e498825a0a8260f16f4ba6c70c9482a1b061e6cc310c0962f48d9fbda1577b892da605bccc93808771ea1d51192b309714f4f2341e1d0764b406047e1337ec83af00448df774ea0634e5dd556f20af18d69b163db9c60cba79ee212d8c54a3696646f9bf15bea4100b18170aee86bd35da56777c51e10d1ea56c440e99142cacae9bdf8e83478cf4f7d1b340932d2f816e26ea20151f01a67f139c9b861ffb300a3736d6593775dcb6a124d6b6301cef32fd21485d3f034163e355e1c237a905357f5b66347ed3e27f063f2b0b0b9ed924cb0075c6f5090d1be47f095557390873e0bd46b07d7ae94c9e8667eddb2252a57df76b3f9804b8ec8dfaa02396ed96f177bb0a41a02917428b8d601ec2d6b95e4d27e754150f2cbf42b99ca47ea9c322d10526314d565096248f202554270fa93e842628850506abb962b3101b35c8791cc806f0f075279793d207c528a6c3d6f0d3cfbe4d084e3902aed389cd0c55930341fd213d3ecc937c0a22690a8579e1724e5354e90951c5d8800df8be11c8673c1b8746ef18f33f87d284007ecea582aba0d85be30581e4187e29c2888d457b4ec0854ecdef24401115db8c16f106e07cdf427fcf092d567284f44083a7bcbd6c35eb03ab7fe90e791290d4d5c055d466aeeb2dac0d9c0e917857d695d444e202988e0e198f15f8e15ffeb7eecaac3f950405249503752d9d1b1f86077d9827edb660dde77d0d893eb025e2dffea829a510d12eb409cc44ac404f310b36af86a7f7611815231491b64bc8d2f2c3aaebf9cd736ab50147b0a8d2ff62bb9ae013b5995695d19b80397b6b4fb23446a2b34f9da2a495013a7203f86af6ee795e68ffc16ac60bbfa684ba1a5f73eb67be117788a41b0d00c3bd6fd1d8aa55b0dfb6dd1af20d9e02f8a58859002cb5c409fc82788af65702d9c12ee62845d4f3cf4992ea253e0ec0749cdb421646aa7f0b532d97d8bfd5023ad501a901d6acb77fa267b675b323c1e0e0086d9b38b909ef402ca7f94f5e0286f6cde162bbb220496f01711d1f5692b8d5f50cbcd8035706f812eacf3ea50aff453d6585a06684b181e410fe1ee6a935d1f20212e52f84d2aa1a3d2b3c1700eb739c413817378ea22fef8d031ec12157b88e973d13412c08b95c3711df570f77b99391ada37d4596bbadb80396d2e715782c014ea7cce672b6bcab8482a005d3726cc8cb2139d498a5ab05f44d6d61cb4e84570dc73c6c55658a34d77c63091feb693618eae5467d91da37e5fe68c044336c31ee89e5224e0a7a8477fa31068a6526189705ac7ae14bf99caa56262b564568e3e396715ad90e36ac1777b1037ff41ee261e4a1ed11c0b3819587523dcfd36611ae9872ad51b8506212172d049ee4a1f0a13035968eb60e4bfe87c46e1d7083a2b564962e11bf8cd790349a0e0c370320723de812873dc4ad195491310d1a877d6855414b994f7fdb6f56de0fa27677ce0980485cc189c477915b5b8c1bfebb850bba88434031bfdb29cbd10568bb1c70f25b127cf2dad52068976d99bdb46e2b349578b7e533a7d2a2fd53028ef41438439928dfcc1da355ab9c21b908c45b3e1678cdb403da9e5797d1bd090c5ca2e5a5b5a332951eb2feab1039cef63876fa208f9bc49badb1792f79c8064eb3b02ba1c662a5563e44d63c954f44d0e1f1eb86842a6fdd9b8015b67387021af5ca34f2170c85fd3ce4f26b2bd79721c13ff069ff53e6c6694c6973f22d023661f063749e120ce23800e47eebac0054d8b262248f17bb9f3437792d725d091f4f319a2f631788bf75d16f33889c502c5c51a469868ab591cae2c73834000d9f2ccc60973c99a4aaedffc0df9025eedc50e8fd3000bc51051f4b04e4eb5b0eb6560f9ac97fec4a83e36e98bd49da09afc2a464972df66320ffa5138d237c0434029e6b51d2fcae707ebff362d7ade57290c2dd591acdea5e71c1a05475b70ea7bcfad566e8b55af8687aa76b5da905b0970048b92aee41b5981ded4dc8d5006a7ce07b268ba658f6dea170062405932081c1fab6e60c1fbae67971012af6008cfba0c3ac982508986ba9a62f7bc125b8400fe2c54e4b08fa61b0a27ebe67070a800456a64222251b4e5efd4e9be2f715bdecb37b64d9c194bcabac6b01a70ed88fac5880bd593f2ad2f0de9132687ccdb7aaf6b69cae0ce88ae817cf542400dee0dba2c2dc53651eb79c894951893c3e76a05764020383b906fabc799691069f94103d9074f2f7a1ae8b369bbf532d6eef8361bcee1f482b01452ec064e40c05f39e295a074eda84b5eb646e84f09765ddf8a4770e091bda48dcfd26a85a0ab075ae6bf2231fce46d25a87e520d379da873b3bfc23620f611140b8022fc30ce73ef3378e3dfc5aef08510c39637442f9dd24c7044d44a00ff4120cd0266e0e9d63fa1720e8f6bd80b5bf3b376e9e585deb5276cafb3fec26296cdb12123c0f31cc7a09e5b2a4d5087f0287e7f53a0cf2b590fe8a96e31d7941dc41d105f20b92214b21a916c6dd7889d634d5c0f8fee2d3a307dc82eeea1c1f6606472a2e08d8d0092d118676582d44639a7dec783126eeb889bb74fc4218642808cb016a05769d0117ff78e8ceced59e8a65570f26d589efce998c4256491b86b77b2391037bcc66d903766c6e894615180370b419af30f5798921faf9707662a995b6b801e4e1e044fa5eb81ee59d70619ec428bf36ae5416b678e61f62016b9fc0c04e0417325862c9eb59b3bcc338cf9a8d639b77977e3a75b81d31f6c7ec8f47b9320d97b01f87571705eb3f3e544af04e300f2cd02e60efcece137e0aed7d19d2e00604eb7419d6d7a59cd6449c3b8cdde052cda100382e5e9014ce71763b74bf8106a0f659a8b0f03c18c2a2a51b516e8ce7fa028c30c0980c02cf2e6f0a4abba208f4d99858bda95cd632236fef30a7a94223771a91f7c79b842cb4c6917db14f0b4dc01258f1eee375c1de7f203e87d148ba893dbaf2415e7bd7412345a02c630f04778e72b1c93f5a3ab2a0355bada13ce60bf92bac252bd9f7e2208aaf3d8e092c77d4a1e7a9ce39b0434cefbf36a7efb6ab9d79fdc8abb0e43810cb75f7930c20d5ca6b6b5f83921a33d91d4836c1802a2b5973df88285aa21074158d24c500bcae16a86ef6c7aca08e6685d8a06c5db9bb30e893796a366428c81151b01a0e018d0f6ec7686819abc26ee15d646fa441981db9b0a0b97151a629590dca5000a612a24a2617927bb95c0b579eb0e383355d6b913625f90116bf1e216649040d59ee6c9f806151878117aea663053041f60618830ed344845a9abea0e57cd90c4737572fa1773f2e71746beaa3787a9efc487756f2fa3d336fdd3506af1c0a02c0bc8eba12f315926a0bb9f81927f23544c1529e58800b954f4d02426917a10af2bbf3e675c41baeb8ee6016a6885c6cc869446669a5eb0e9a0b252ada0562035379ececaa47f0f81e61fd74bd1fb378e12091fe1c8bf5c13d2d100d9a59620c35e7478f178ac8eb770488a2db4152188e05dacf8c544e4cd48d7b24b2275008986e38451610aa74f9f9e5aa366392a1789d7366de07204abb5aa274ebb8b1001e1ab30b156b3be2c74e77e47e3a1d8fd48da9ea4b4fb2acc2db2bc10e519602c850b2ef5ae991febfa7a60f83805c4aaa91f5fbac4071b3f9d62bb43847810051a04e88f96e8e259d775895d0ce6c4b0491c9856bcb1dd95cf599a895694b09f348f64c643bed85c714550aa6b134956b22f785f990dbd227e4be2d32646e0a994326eee8b82509d9bcd6db7320aa61f62cb4f2d98a48c9085c6deb96f6d508cb91ae1ca2586879def704ec86066b2cf446bc4490332a4746028788a10eff084d44ffcf25af752d22d010d3c293bf5e7961bd8fcd1872484c2bd4206793230643c13fc7aec0cb2f2b544188c505b4b35f7a111c3efc977b26e7f79284fe20093b41591fb2cd741acca1a73372b22291f1549308d02b652a4501d9043545320661d35e132677ff12b0248703307a545cb8e6475e324635609c824d1cf74bc50e33f1e855370b4cf7be8f92e9f5efd7f235670098e351b31f4bf66a6237dd3601d3cf305d2067d6056812c7d71c7809febfe24e9ef206ec37533f77dae26eb00b92b41f1bd57e194fcf21a85c6adf084ff0300e7387230975cc932aa5ed1e9f03c3bc7f5d643b93b7f4aefa2c768e4b1f9e8c45eecae224cf79efef73f533420a07305fed8c760bd5bbbd929301d43aec516f0f47483312469b24772027c11a0e021efb35fca3c8338c8bd1834df5cd9163dc6356e1a0fbd6e209140284657901a842edd438d192266db750f52de081c8afe3d2eb15ed02863fe2fef41fa035002c92322e3dc56d3be24fa75d7a43ec4cec48d75ca3a5e822ea9625a42feaf900996d37d4d41f488c263651414c5584168adcab739b52b7688a2c3510881059035447894945033fa3b2a5b6f04cc7ca4be59e7053d99ae8f7d38aec60265dbe07556e60b3fd66e879755df3f1dad0ddddc46a9da8699240db2819b5ed5aed1908e848c84cf5276d8b5df2e564ab36d5f1a9e80f847ffe9dceaa852577b53d110ab5cefa3e9ee90e72ab73775b1b7428269d171b3ad8ba6a1696a77c895b8fc90049dd1b5dd899c125486a8545d957d1b20f97e3be534f066c4661bd4da4a1bb0dc1d7a31b2a2733402919105bcd1f2310b7c1ed4bc9f2ea3ca5b05da71da82e0d749b2bf513ae3c6983691001ac60ba7a99b42cc7d5d4665a15456cde340a600c3951559e615638adbad7f9eefd6e5b97462a8f805016bd6cc28b1942b569630a5c71325e319533c92b343eaa009823f8503b2d73cb24038fee9462c55c7d920ea11b0c1fe35e0d8cf0d1c54494a50511470c991a295b8f1409fd7bc43c3c050df37179f8d27a8e8056be89f5dc6d575769a2665a11e1e5bd93f0bacdef9ed70b96c97f833342aa5c9e6464d2a559be3f480093d690004dcb2de4060999c79a086e30663e4bcae4a68ce731f408e890af7ff4a9e7f0abded0d9f1541ce7e7f80935db3ac5fd8f627687524da4868b0a9deef9eb046c476bdecc7e3f5dacaf0f0420c03e125cfc30b2558fa7ba6f6248f7c3746eb33329c772fa7ae63ef791350c95cfa856de5c30636e5d2a5b2d083e4692ae2c6e6f6526b62ea2499a7770ba03fa930941e51ee7171d234b39242dab32b7a95f28eff35b652234fbd0629080091cb3816dda3d746ce0046410fce064614ca6066273bb234772e639e7f6ff4d0f34130af8da8958233076001e84c540683d1845b82e99a98cea14136e611ee609f62b7247d849f89f9d8f99dac928a5a8744f843fe07fa4bd5e263615dff5a90e0ecfdd8feeb33180e0f72e5a687228b82e0fbab979226a0f01b53672db4a740daf6e233407840063542bbbb6fbe176fbd48da75c12a86f0f87c7915857ab6b04ce70351953dd52cddd9325b9a045f9ee0a4f8dcae5e902cc25165838df9dfe0bd79400496b81bba078a2ed8e2e4eaf612831e75b7003a0638d25ec52fefe0a0400660bbb895ffa8cfeef54bd26607e006a6539769eb7fedbecc3a44ba23aab06a893c527d0044ed859ede45adef7b2971a49838459a2188c3320c1755f80220a71b26e7f8a12e08fe0cee83fdc6f629810be178764cb1a6971fd297b9e9a5e018a97737061b5442d8e7cd9754cad2e10e603b1ff30e4255db81c1c8a62c8c900f91133d0c5686ec65379052c1bf04d61b997a217ceded857fe4756ca27c9c204eaae9417376a2727ae456952b6c77c5f38b9c92e4fd3efb869a6cdc502010b0396267d2697d38dc2c0d3f9885fb0d4379aeeac7984dcb723528c058fe7fccf0a0b2469ff87c8432d74ff080dd77539d351c7d993bf8f591629c9bbacb607080ccdd9ffdd8405dd4d9dc00ffa6ec3672a19d8f6ad2d0458eca37b2e6c7c6f89083e9aa5422f51edd7809b07cfe04e6efacc67fe42fc77787bcf1b2d59ba0d7c06859a2e4232a0171db5d55785d4b9a48fe62455761947763acdb568b2b099f007aa22f02dff0da9dd7b90862fe231b57a967ed78c686ec94e2797767b1d36db01222ebd062911c5dba991ee6faf24229925290dce2d25b3eb8b878009b5332a066ba4354a86a64ca714063bb14589b6b12c891c3532375334fe553feb4b32b6056340bb3305d8b1fbd423656b6bf75af44f3d386a3f141166bb65c566c8bf80007ee029310b82e1f2efd028caaed863b6b4c649992c5462e82ade7ce699191f06f152c4014d8f79d15e18e0f6bebb9701c14d261f3b799329066b5253cd64370af0cb6924072f3fb7dc1cd392eb78e71e33c7772ef6393563d12e73509c9c040617306dc1a88f66e3e7f988647a810efa4e4a264781504020fb8b7c4072ce8b0b89c17f947b2b17c92f71e9476244be865262ee2407fa3339ba5975ea97f98d0c883e2e7e9539a06cf25880341e885e84d572c14acfa266319b0d37ca83c5e00a1ae825930d8c9f0f6f52e475222e0190541602df549615130b9cef3867117b02a6a2195c5b439764938e418a0e08ff17ba4aecf71a15a48a3d34028c897b000fdf8d73d8977464ddda4eb7eb4768d8ac75193e56d05b6b66d16537985bfe3402cc900fccaa26b56a1edb0193f1db92dd70daff618243bd88ee1b859fc7a8fa018bfedc642ac92a75f45897d164442d937801482f835012377acdb6013e6c9607036d14fab4bd810ec7c52f3076e2c3312eae3a6c9d9455bbb5e03272f7abc805fe0c7930ff94027f0045cf34444969e84289270c63000977de2d1eccbc4c660bbea06d523eeb97cae1bcc36ad1258c23dfa3f98a15593db287f89905e587aa040d16849607f0c55658d96f82b6216a62911594347fe259b81b1707cc963bbc06a8c0a3a2667e0b011d448f931fb5351aad6dd3579578328aadb9b6e711531208de59fed8be1677896b15a46e9685fe60529f7c19c1209175c87cacf4d5ba1a0dc47ee237063d4a8275d60ca98e9e2e873094df5be087464536ceadc366e9f80a3257ef387248eb702b85ab4cff32c55c25bf52ec106c744e6f482d5431e9b709468be2303ab951539c1007988523cf47f32263347de210296975c7183848a202aa110f4d57801a1cee8b8f80809ef5a4c620e1f66b148b8445301409157ad203809df456ed951adf7e693d046b372b3a106f5d162348e290f650762d793b850109951f7d11cf72284e937ea6bb27510bac5eb19a722956917553e369e3ba21033abe573cc9ce4a7cbd71e4addc33fe2c3b434aeb3cf3911e63f6c41bbfb8cb077c52cc32506c4e4e54d2f68ce4a36639cb5b8161eda46329e0f6ff9337b8a20b4c82529001e10e9faf5520aa8a489b93f5b6b70d96e63df35c85acfa5ab48d07e161ae389dc35429a9cde17e258e7d83acf149190505d13db631216ab9e67b095e818687fe94cd4df7c6e0b0ff177d71aa27f8874f320ab0d599f635ddc0c70227fb81b00f38f23633ca1a5c43d672ae793be916ddb2a671cce2a57fbde01b0921611c08e2f0559e197b3a6a98fb2172b81e3f50b2dd9119c13a3ecd0df960010857730c8997a3839de95ce0e0681c4f0458ebb22ad2b9ee6bbbd2f6849ef206a716bffd9de5e6fb898393c3806a80c000b20069bd363d940fd3538a58bda70db5aa711c0f919b7118ea3346c2a796f41425d126a8189dad387d72bd7612150c350c51ae9446846bd499a6a1f53db916f633c273275cfb7260732a949a4e5a0141e7ebd4ed46c0db37a87a66c8ebe624ecaea0f2c3c954ef090322504544840e52857fee8bee484f3f633dfaf1aa06b2bd654f42899d551c317534b8c13597021e736bbb6329e9fb48aeea0ae1c8fb5a1e7f79f2b1e68d80412ff757c848b80483b19b8e3d350b37a140e8ac974fc6414223993d77ee9868456e09db2f9b880833519221072db682a5c6ee98b6efc523abaaf3290654a399674729d6cb2ad80fed946c2ef29c0cff0e30198fab09d6683487d1cdb7c0a26ed6685eb96ba8d50026ef657defee88d10604eb63e650dcafd8a612f507556fde6485da4691c60d0093ac5dc0c5c36aecf65b197de594b686e33cf9d3882b5586e31ff54dacd9ad00e3d57d7e9a7671f761fe15bef823b48b3de7d07e4e51a0b2a5d30bdd2fac5c01315fec3dc7ce5a002168920c597bb91a4da2024b507a05d29a75fd82def5470531c558cd909cd07ed0cbec68bd3a41d745b663f35b962bf92d1912218482da01dae9d9e7d38b5d5a6d76a8b2bbb242466d91a9b3593f4683b83f2671ab2f1302957e815c7ed1562e22fcfb19b7f59853691ee93b9fcdffa9ae42780a01f3430c0f58ba37d5ad4286c5c755048350d01d3f5f8f02ec83205d1effadad700d6b0eb93ca6c851e711f1201c4a5c60f166cd81133093409a6072437c0a9a778e910cf7c392bf13dc9475b203d02225d98220435904a145f7645532a24663efaddf05dc2bd08ca29b6cbd79308f9e79fa50938b16b91db53ccec2acca982c7960200af4b2740914d9f8cb018a451e4abe62d7834e450a8b99d16c8017a5127d2b4708bb062672f28ed0a90250c4cd754e04ef17e7956bebc857600835621a854ad506165904f04af967c5cdd86b276d908eb510c29e4b35726e40ba2660994debf70385c1995a4fe36601451926d0a76c7beabbeac83fb9e56bff487dd07840774e0e4f71a849e6bf3c91463b7f7974b10a2d227e28ade756a0178c48f60bc52d9f077ad674b8a7fc89cf6d26f14546f7373fae88cdd4e6faf8015f9ac31996fe2908a3dc9dcd66a34a025fdf4eee018de32f3bc674eda88923da8afd6744d7e02306494a919600f4805c30604adb0a530fd08d281248fcd1bcc9ab1899a225c6e10074d3e99b93d4affb1c9a9bfdcae352556e44b0a4b478a2c8c2a809b27c9202056a40cddfacffd4c4d31804aa715771c022ceb6b1b55ecaa78a8d84d6c0600805e2eb0a8177d9080ad7be1cfe3b1f6cfee69ade4341eebdf3dbeb5976295deb07ccd24ab4c7a4809ef48cfeb8c39db8f931a9fdda4ae52e9ae6b4e91a075a19090b84d65381051e02d0dca60ecc80a315698608207a8963fcdb341af8242ece0e3b75341340e2feed3f74de77d21f0b2befa50f8f7e3d5059ce39b5aac6958d06c4cce49425dd6b3a9df40e9c1fabc82529d5733306332ed0d111670fed29ea07ad6251d7f6e3d5937c865fd319e230f236b29c8882c8d68b310e45d79ca5e209bdce9813668c97d0689b58433a73ba8f3bb563025350cdd2c64c0632d5d86a0fef746c43766309dccd9ccf9e5cd5a793055dca574597bda93935f62b96d1630a7bdf953d3594587085e7a77acbc10e83c10b40b78928c98cda3c172b455a01085d198bb2560d587910055135a549b0b73b488d30389b055238fed433b6f3e303135d34e9796498ed2891a9ef78d9dffe0c68ee911eacf947d2e1c216d2e0740739543419d67c80e479b1904d751438578b73d9a50b63b8563553eee59f45570c95e10187bbdea4f19814e1859119077e23147ae8cdcc0e2ebad55222b3c1d7054214a3e7a34f6e161521cffa00e93d09a1e339f9b2f22844ee1124f5334bc5003360185c32550c06eb51ad2a01db6fcb59bb9b39cf4f46f52e146dfd6b6655038f241b7397d5240f9c2942c95e6499cfeff0a5eb3e602a9d56b305689d7cdd03a8bc9e601a41f080c7e1010e2c7301ceb2c8bcf2bc5eb0daf9726bc815aecb068009ffbd354ce575f0372b1dde38c125559f61bc7dc8903b991fd67165d75c0fa2c7443145836fe814858b5fdb77b7f096bd461e2a1687045ad1fc9036baa10631afd54699063ef52f9de8c2dc11e15223e0f01af2258cb6b404f665e04ec60b1d6e2acbd2193d702d63358604e6ad9d9c9f1bcd0a3601232cb970258e56490a810aa07de8c58f212fd098db0818d13ca4bfed5d0b44a78a34c5dc625d045a0137789e1cc12c5d2ff583f3e2c7f5b845ea29231a23fcc79e8b80450c6c86840257606a73f3cefd3027d88c23ccbf6a2303d490ef5ed6e1af0a849f74364256099e7774784153f26129a683fe15a87970a9698faf4bac120754fee7218248d30d6e7c39562d4fb59bf693dd22d89c248d18b90938358f4eb49a2e3633422add03f45ab17b8761f12bcca8c064d3ac1252a6ec66442dd9bf7bf19364521444db0b85f735d14008fba4fa218c2a834ae780f360030b0a1ff5952ecb8207b304de07a896c255f098da3fc9701e89dca066ea3c72692a8fd0e8027b34bb0b191ebe0da61a9c1f4fa43a993a4d37b6d33fc80b5e841f9aa39ab459c4fba269c9cb0601b55df58b2a5b3b22844ecef8d8d1ca84d73c325955264197f2b142983f503c0b33271571b3232a23b53744ea341ce5c2ab2b5c877c669d95dd52ea8a486cb90f16f97c1bc644280cf6c86263f0ad2589ce731e5265c22bb29b7930d84bec8306e213ea4ccb67ddfb51311796f5db70fb7b8eef92de905828c349075e91eb3b0a84b2b8f91d23913ef090620b0d9e5bf504d9cffbee67c48189f6d38e92e82c05ae13a4e54d0ed852411ae332dcadb4ee45409187e19fd812eaa4b614edefcc03cbe27612876f3a106173f1c4d948da216876826b4096be5ae2b8320bc915ee0fa80240fc1843348ff92025f48d6259fcaf2984b2c46c9b631cafd366de874a03a32661d79b918a2a09c89161a749c0f6eb6fa4f7fc9eff4a53cf6b0a9349530e70fd5e519256dff86302bece079e9b1e9bc1f3f063d47b41b24bbfad6a44790007e7afbcd892175bbf1a106895490070ba5dd74a4ba6e97bd9c5f1dc5e6d9d03344384259cf3984160cbb7ee78765bba146647e994fe29569c8ee9e551bd3302f4a7945d157b128ad73c8658d060b750c404f89a868e4a7113b4e8026fe9f7006918ca80ff6544497771ceaab420caab0ec60d9cb59a44fec7a75914a88fd7028180a20fb2d9c91e20abf0bc4b1d9b1c1caafdf9aaf0580631d64ede07422507848c0fdacf662cc9fd1cc58adc663b801b1819348c70d7050c3ffa19f2818a0f7733bbb9dbb547894d181c6d11baace57991c8de120fbe53ec12033b6e773807a84d6c6bf1f117ddd8d1f610501e24da2fe67de79ec69e485b383239b43b1804df1450cb1f2176aba74e58ac5b30a123318aec9cb1b8e0108d246bc489577a005ce98fbd69220bf9e31c989be15d0a3f4bf9574c4e3dd7d6f61f7e69ad7d5d061af789ff84cfe334e2dc01e9be3b6fe460160fe76c7fabc88a76a5c0f357cd0c85e6f175494476ea8a54b4fc342510e6a0f90a699c53c732477a91c97f908e0b7d48672e301bedcb5c8b0130e158894c1e14ab2df94268ee9567539247f4f30465cd47c5688b42e321e8692c9d6a6646b83db2ddc67de0f75e2aefe06e49b10d29b9af0f392611e6ffab054f62ecb84317f2a907dcf3232dea6c3430f67f77000beecf89388987705dc8c9328c0e2d00100073cc0d299cd22d824e5fe1e7250db49a56fe3fc9279b7467e1201f75fe684de2ae1113ee0800a385356853f36303ba7b9d43f8887aa372e86828778d8ab0b7be04b31b8c028907d9e808c3cffd094fcd5149150f914109e69b0104d3b4ad23aa2085625d721028a11c460da9370b517fd19f66b72626c9df5f8ad35a4492826f9cd57f35f0ff65912dca47bb8104db074ff6df7701c2ffade238672332bb92a46c2c6799fb1a63904818cb654b09a62e96e9a9c362d0bba455474bf7e9d6e8f20891b85aa6171d389b0f4425a10a670f82788cef79b731d35383387ed525698b923960871ccee2eaf214fb4c510caddd1edf31c1c08e2d6364f718258e928cf1ce118aa8d7b722502fa20fb97801f227fe677f35841dc50ad6eaac30fa74fe3da2bfd5bbc2fc85e40d26f54bbd092ef1830d9db570d1672e2de8a6c6e10be99e3038c00b0db4fb77c6be170a8801abfe3c6747b04920be58a8a0dae9ca15193c5a551e6506d541d4209ca859bb0abe6acf7ad5e7af8df47f4d77e59dd550a95a1c0cc0b751a1935fa9a8deb45b09ae093ff9057c2b34cec119e6c343c6735a9f8faee189fd4941eee4900c782200c2685a5091dd2278cc27f89fddfec1ce6ab2081f5f38f3fe855c8dfd2916560cdcc7ce9238bfc44b21e8a22412be4a95b8068b5fe9dbd6f7ae61ae80a475830eb7e91aeadd28bfe333ac9eb06bbf7753240df35940b8326524a929745164f605d6d04196efdb0b93867b4ff6a91e533d11214e31d0262bb0099e373451c8f403cb3885c4556b71b1b0143c1ab3bee5d18efe396bb9c2087ffe6f1092c7fd620119a5b3f2db9ac9b5ac608f46c427b6bacde43e05707e0e46ecfa3bd72f26dd0dcd0487db71c83a7275348cae3ecbd2255263860cb6063b7f80ae1189f966030ca1da4e230e18156838fc7e674144b09660f775233502277d8586f2d81582a1074287bd247e03420160c2b3e9a7542e0ffa7be9c199df46c3e8c04e2cbb49990e9cc08c8dafdd5cc550eb8c181ebf01c64102165c4b042b1b540832554b64dd07735cf17afa86dfecdc9d4ffffccfa5e2808656eb5bde2c9b613b86133d21640748258e09e0695e14adb92c9c48bf81f3dc5b6cc638f240242ebd66752a6c9c09726bd5b92093ceed83c4e2c0462caa3a9f9437ad942c33126f76731ff9398c0049275f1d186e86594b8cdca8b7248ab82094869daf35db534c980de8df27fe04d72955c77389f8a12d887a578b312d024a154a1b92b87e854d50134b9e29e50eb5bb752789d1993dbdbda5160c02bcf3a49489f959c9d101ba657aff043d7b0801683ca20213b780edfe4b5edb32e5c1433375092cd808f3aff7d8db94e81309b1a300a7abb258e417124a2e902d2790ad17d31b534de0475f74d998628b6f05504da3910e262cb27c35f06a724802a321262f2be26e6d2dac4f959b2d928008a22631cc8d75341e48393789a9527ce77452da8519fa394726b17d83fdeaa0008f7125d0e787c9ea999f0c9a9622bb6de26b7ad69466a0ca88075741d3296b01be696ad70d0b09102664ee007e4b92473134f9f3f5163d2cb84780360b9130015da065b467b731096ff052b2d22b332a811f5a35a13de51e363afc12355bca0fe065881920f117eace034e78c7345bd8fe56397816630fa43efd7227d67f4707df8293b1cfd588f0db13eeb16edd25e264e292121b25ec031c95cd6f8cec120cb79b0caee5b2fc39ab83bd6dc69d2deb40babd36b5ad647889252eb0f911af080a6608f0d76e5f4c5984e6dd2869f5ada91cfa0faa23182350f423879ecef8057486c664ae11b6f7a578b48e2bd4f68cb857716f3dee5470d08c9068d2c6c7095bd09bf5c48b5fb8bae4b14ab1eb28527935cc4f9684b0b3bcb443344d00f3008a08659135815cb29cc1c975c9af8c5c3631ab47700a5606a237835bac9cf2058f87e6478a060e1e30215538d9a174d0c9247104ddd4a1953639d693b49eb90e84ba9175388fd9ffa986d0b9eea4829bab68f3d347be37a9bd762d2a57afa9065e45e91e44114f5485dfa154b2dfd11328fe88da51883c65c3be1b248a32610f8050ad7f30c41b0e73db7fd3b1b7b89ddd25cf804e33f6bc23b774284ff6e60eb572041fa3f26ebb4474514b8744a41022a57d47858bd7169342eacfad90630d219b8df25a1869713d23439b9d5661fd30ae9176a3bd4f7afa04933dd5feca0102834c162776b07649845a0bc8f03e2ec923097fa1e1c0f64831e2a19357370256e423d20fd5dd117f20e8082c8859ce148653c2694604e93a8821d309c6c903bf82b5becc8a905dcd923ac964882a119a2de0ae2a31f31707059bb8f11c530944228c218ad16fbd11285833887d04a627bf8eaf44c4f55d9671e72378bd55015b03192e5ca22a6c048d9b888def6a63653ba6a5a286e2981217b333e3bee403538d475bc8c9a03ca32a229433ce877093a3099886bff0bcff17a7bb3f4ba205e6bb3579533fa92154aed047cf14579ced6ae4a550a065419264e7f793595e0ffdbd40119ba7bcd8f144fc8fafdb1a57919fb207950bb842a8d0781d08cbf70a2430dc76d9213eba9da4d6c927afdd2b65cea338f2a50deaf1592374b76dea07e827f1f6d41cda7b176be2987c09ae7ae6bbcd95132d075cae27dee168d6060f6a8876b9ea0a7c5c241c8b5bdea491ed694f7c2eade495819740fca07ae590048da949a90964e43ad5be2adf0cbe2e865fad6f48c2b8e5cebc81d21b974a060f2bdfb5ca38bcaf8a40beba5c36a898721a74549e570ea3bf7e3516a7062c9406aab469e0448096d52a0b1a84066db8780746c01c30659132f8ff264122b1b008b029e49dfe3569bbbb92998c409326d98bd3acf5f11c4f2d647845ce2e54b0012d619f4428abca24f5255cbec003c91c70b72767b467728fed9daaa229fef60c7e182a0acb350cc1a6cbb3e9ddfa4854ffe7473a7d0f9e8cd1881871ff69990c0b20f40090db92a22c768d50dc8c5fcccdb7dfc18f96d2acf6c3e1374533dd0d1d65302779242fbf2406d92812818405caa3ce9ac3df979fc078a40e2ae9700c5549087db7dd29aea603784ee2e5105a689ab84857be90ac2e02caba82df080a3b91183bad553d7461409be7535a4392af205380fbd9c933c60e3b3870a89403387b5d6f33131b3c5fc251495045f7fc4cca8d61d386f10811ff97249e5ea70a1c0b5962332c27ce1d6f378cda113b9dff1bc08283b8493d13757f308889eb0968347fcd23b9fec4cd28394eab2ece663392bbc7b8df9f6f7c12462b1986640f044b820929807bf0d107807580bc6db0d52b4dd89b9395457079ff982961630d25a1e8ac911ab8bbeb1b61f7537ea0c48bfc6e20d6207e3add7374a29dda490c92a429ff6d174d086f7ba00ea22d9d14f3159d48948a696d5a86bacea90e2800d32dd0f193935273f09c498e25ef22512bfde8d8be17a0d87cf85086c76a010e0b5a4135cca42f2b6d053f16480a25653f9ff921513087028b40bb759e2de90197e55599327a5eeba01dc4cf5ef8c88be88bec1364d011e4ad9b860fa4490a079676b17c296a0d0e40780954c3dbff491c398b456ff13eebfa7d1a8994b953038371a5b1017d7c3c0ed07ac256025084b63c1bf8478c8dfdb7bea8bba6abb30d180d07b23f6c055668146dd5524819ad3e3919fb7932c401fa54e76724ae2800c297191d86b1a9740d872eba9b27259b870c9432545d9bc1994ee6b7bb84d60eba61b3080600ab778e08564b76d5a2788cb874e1d2a176b907e205564422be0a3c1f9e07648bfac95a698763c42d34d345b389dbc629a34a61b62776b7eac80ae5ec26fe23eb62a833d3faa5cd49bc39992e849f522dba394b004d3b93b99f02d18840dc7424e930a4de4697c0b26a3a2cba81feb6e3784b88131dedad5fad0e1f5922daf2506c4cb6bfb0d2c60afce34f546cafd14522edb79dfd2077501f0612645d312235698c37f1e4ffaf36eb4c9bec18f89283bbf022b943f644c19d0652a413ad6ab38947a8e2bbb1a9b8ada2fc2fcda51ee8d163fc7cca74ab99e3073f84024893edb8dc5280720a729cc8773f93d93ccbe0a2506f68da2b213578070b693c9aa896bfb690550dcd5ab9494be35e14a7d814b7b4d399bda90c438008e4416081d79c33826ac671167939594560b3e56b23cf319942e68ac86163c9058f4336dba7ea8131b5d83d0635fd2221623be55421831dc232c1f82a6fc35b0d3e5a9a5a38537eccdd797f2ce5650e204d447ac152d6cefe448c95d54efaef07ee67b976ba89009d9c7da32150fbbf7e2b57c60cc2b77edcf68964f0dff1e404eeb5463f7e0a6e612b13407922e2b69c30f3b59358d52f31ce60f37e40798701f251d2376c87a530bd497e2b4a4bfe80c839280888ff1bda10541c7dd182bb025177deb3b9bf6c4597c8551cce3a5cff2b8b6332bc4677c48e0a589d0f46f5074ec287bacc81357cc1982b06c7c1975debd9ef4ba815c93dd1cd25a7ec985908e59d7b7d353f0c19b16131011275cc5d139de46510f3c64f6e9514f51368830ced8e08b9c33d0e56e0cd2dbf6eda68eb0b67126df0e0224b20e03c8ed5a5980f4436207c314a9af8ad76868c6dd0be717e8010162eb491b16bff18b3454e7f0c2a54c20609b403c3d52b09795c38286d8f08d8ca6665c1c9f1eb2d7a6871750d6a0faf7e45e3cca6b7e58a81124ea6378ce39b3e350780abeaa01e5b190393045a7558fce19385867f17b50429321ff5bf229a2889ec96bdadf7db29b2c3940e +generate_ring_signature c9ab4560e364fa2cbf7ec6e3aa25251bcaf2beb043ccacb0a4624058fd88d86a 5d6b8e078641aea54474ec65784fccb1e95d3a0f6c1b97005d6fb65077c145ac 3 56d63e1b1a487fd4d7975e66dc212e3ba4180fcbc6ba777bda12c4a957f359d1 0ba8218820fc5a47b7449974d27f4cf1fd7ba6698f087a6b007a15637c8c56ca eec4a9fdb41c38be9bbb62e86f0d497b8adef1c297306cc3a209a700f1c76981 461bd2cc07b9c8310b74820b6b7fafb962605dc168cdb11be6995b344bf95305 1 6d6b10e0b4a58890ffcbef0cc87fc466d36105f6614619f254b6fe895bd15f0048864b3502d902ca02d03002a2f2e91cd5c5258268185a43e51ba911419a7f05a929dd17e07286e0f4324df0903263dbd0451d33bf6761f1feeb5e664feaae0dc73d927bd4f3bfc1a82f8b63490efaef3069262bd9788b1483baf5f7b645bd07a4c0795ebe9cddcc95acdce55b9a36faa9870bcb4fad7dd694216ce07973040aac8d509dc86fd616cf50b96e0cc30bc9bd0a48a400fe3a16abf875ec30173f07 +generate_ring_signature f075672344407c2c73e656aab50a5308cde472718afb873c3813cd31eb083a22 725e94903757033d3ebc8b23440f0c208e60f8932872536ed80050f0ce714156 1 ac769e6e63bf28b0e214931c70c958bdfcb8b7e662f280fe1699e9570b3e4df9 34a4a033910e6aee4adaf7aa902b9bffaf1ec93c9949bd358485f59c45538306 0 2ba9a0b11de0274310ab0cebf49ac3ca88fec2e495670d57a2b1d0b9cb7f8f010dc3103844c4ac508e4f30c4e50fe3021674605335b452f03f55f7a3a66c7c0d +generate_ring_signature b5c9ef14b9a08e78925da00b25dbfc501c26ecafbfd1710539c0ebf3f0c4fbd6 bfc8153ee1fa82bc2a51d1e74adb672f8354f3e11591ffa0253c4a3a7843d759 42 3cd53e438e8c719ef4087516c7124def7dd74122134893d56fc52917f9ee9b38 f2e4139786fbd913a4f13e19d484bf62929447b8afc84612094b0f5bd043df10 5fdc376b219fe822837a4ad8c483f1f392a1cd70141368a20b0d47ad9fe1e009 727d639f4fd6588673837ba332ad5c0ccfd832155dc7e194dfa9a8aac128869d c98ec078ac512c8be32d11374bed3fd019d46dbfab909f65b47052a0b36a20db 272da4cf42e06b9645cba73ecbef4c57b64b99e481cf3a1867ee7c971364904f dfe696e5068344bf3f7994528217694b8b3d359eb52a362423ab2dd23aa0ca5b a2b507f5f68b776722b35012a49fa542f58eb1f6f5a4f2b03ea507e306dcda46 4235b88c24272d62ebfb9b75e6af06479fdac9f26ee03560b53e33d48a820c78 74f869ecf9de6d2b0835ed98600e6c24794e549725eb338f0243025d9945c008 daac00d7a944c053a2e423047c1ae410cf73d0b3e3a0c57072016552cf940313 a6fdb201cf3f7b8aaee0409bc5098d0fbb791e4da2e87846e69c0387ee917048 c556403a2899b4a1288c81e6f4e7f1d2b8adc7e1baacb2272f87407a5987ce4c f9f08e37a0c25ecf700e25fc7107c9bd008931cf9fdc39c454bdc4a8f66b716e f2c59ddd77f72881f42cea3a1a1053f9b4e4723e20e65a1874fbaceb9fced0be e7ea591828e31b78bc7634275bc73436b23dc1eaeafb86775428e5c0a10cfd39 7c15e3d04367d0638758a8c9ab73eabbcd356909c04377d9c3008e70e1ed0eee 3bde3e1aeb47353edb3a007ac3fb658471f6b3c33a67b8224534301d41d55769 8b778667d673944e41147d049effd5e9197f6943b65d8bc47e251c7d66e614a2 a566e5cb64d80f5eb09d5d8fb0c7bd67fa50b2847b7594a6817c94a2d2cfac40 44d95857ad4b5254b378db5d6f5de3aa0c866e0eba2757695e5b955a71ad425b f95a8c8f71f794894ead92d7a8343b7f92c2902376c1ae65891b425d7beda62a 29ec148b76bad44d003a3e53c1cde766fa9918b011696402b0b5546f6cc98c82 2321dee77fd3f088ff04ef45e65fe6baf7cc57a2e8d5887efde37d4e5dd5940d b094fa11e71f396fd2c5efa723ae3b86784d3b4a3ca28c454807c379fd36d8e2 62b4e2eb263e0af951102eb0db0ac934087876431fb5169f04e3033ed66473c2 3043bd14d14c669e87fa0c47de6b5253e1c9b660a200b528d5d008dc6372ec28 81837f200ac02a8ff2949baf7e2046dd41475237d01dbf8a7ba05435d4f56542 b374ce8d68abd8d30d806eeb3133692abff1cc03ce765f4d2189f420792d0fa2 8aa45f186737fc070eae52127397db1c1d921b3716233a76538ef4cf8d98b006 094a03faca408ec2ef2422d1e8e93ea9ebd15ed6793f909327515725fd245d8b ddb31f1ca2d6a32e1d6ce1e3c7935d2afdf32bc00b185ed92832441705af8ca5 cf9923a125eb3d3c6a27bc8abb15c9a1f5ecd97d6bfd4101395c547c92f53b62 70b0954b770448de581288b0c28ef89c95ba5bc78d70302f1b3f8befda378e63 f32f02453664f8af919094f4d8ddfa34781c83f4e8b8e76bbbdda82120444532 a1c063c2be3b23be7ce11920ad588cff49f0be61f4cf1d86961ad990e0d74af4 c9ccbb04e610a9d99373863ff6334908a4218f3a27fbd9e97b76d5a1db666609 7352167d50a292ad64b7c1911ff0d3245ea58be061baae00519af17ec81f1cea 7340ce3795cbaeeef9586e1be8bb45d0e11835db89cb991b1b9042dccca58de0 8486d053b5274d48b7174efe4246d6e0ce405a90d0101558f9ff98fb0f1f684c bdbba1f4e3c95f849d2cada4a88daba5965c9b1707e3b963cb130bf9ecbad8bc d077d77756520959c488b842f761257f35fcd5190e86c4676a4a44d6ce2c6bec 553cc6b8ef0b6b67e23e2a5474a2c74b954bbea6e8e8e8c6e1fabbf5ae4e3e0f 10 eaa9985c37648cdefc6abf1ccd6ac210118555593561c1a7e2ac49512239170bc76cd1ea7bbea3601035d9e173884f722d2657f3026552646a259498b4dfe60c1b8a9823a3b92c1d6c169cdd654bb5255066ab180effc1893383211fab1a2e0a2e65904c3797293925a3d4c32ee26194e6429c778313c518f9bb64ec4394340c2caab05339016465f9476c7eaaa201ac8ade7c480a059d6f291f32521d389d0402da00b01d55f131ceda07ff92c2fe2229943f40e31ae1c9a95a6e5a4bd3180d393115f29d77e5a11fb448c180fa7f6ef89666e114bac616f2001a2322fd5f022b389a6c55d1dd49efeeb32eeb338ba3f3f8358dcc0fccc17579ee9cc4b70a0ae9e22c8afc08814689e6b189bcc84aa61c025b2fb79f32b7a01327a1880f9e0eda47fe156c120395cc19e9a4bed46938519a4b274d413e96a4d631ed291ef70ce61d08a712b10bd11f78d9faec82e69609251ace440636fb17377ec6e2dfe2061fac9d0fe80c04d9e3b4d4681baea3c5288a06b007d933ec2216ed81c73add01c106acc2067d07b54306ac7e4d024ccf257e5647b62583fc6adf52ee766ebc041af8897f7ed6e09b4c9cbaacc99ee3d3a1f34d034a3934e9105d3bf623cba40a1f1caafd46b77b36fbc63d85aed99a97955f76b539be43c8a2d36738a5b7670a07b17514d664a009067740b9fa2e0e4e7a8014ede8a6e71477e2a5c77ed8840374bdc3ba5d844abffe8989f450dbc7256ecffc2a85b6217179c74628ffddb40c052b3193929fccb11941d18cd50ff1e47876dc7d10e4d48bd4261d696a8e3a007f245f9c48a8365bd64b6975f0a8abb994559df4b3213df56ae7ef13bbcf0104ba6408a7f3047b718a83ddfea0a76d954dbb2dd747ce3e07eb1bef9aef5cb000a7cb183fa82d2baab8a8014062bb0d70d618c77da2d45f285e6e708ae57b8d0ded93b169594a79b00b182d86ae54536925ea3af28708ca80a9a6bcf6d922c10cb0251f9cb82413f510c32d06068b942021f12cd116e61275b97505e752af7008b1b55dff950dba575586e08c3817b8c98496b3c8d6e04ada9e2c25b4b75b810ff7741130fa6455307291ef8ac8f13ed9b3c93d6478cc42370288855beac3fe04235fa66f970906acdfdd7756594a388feed42505390eb0ebba94e80daf126a0b2ad37cfdaee56cbec5148a6998e5c95c7bc4e7332f5aba91f443b80f29055e0b78b226c5fb7da592058f9e624b5131167a4f45b30041028643ceda71f75fd20689077661b4f48801fb6dbc9a17437565d3250a85266bd0f3b92130677970ec0d8dacc889e16dac31063d2f8c0a64b3bc258485ec7acfef24d21325dd6a8be409daf37555212f985769de8aabd4f4987208524a08b310fb82bd2d77a49e32ee0d08e4289345dcc750558297dbb5ce64508b70493735f96c9edc10ee45b0fc6c0a117ca42176f288e75c599bc66b1b2f7c678d17fb9f649e49c1cd6efba829dc024246e2ea3db76fe309420f69e7c62ab339f205f3993f9e75713422e25806bb057b53eedb05630e1213d0fee48cdf09f9d6a561dfe3c856eb2ad5e19a7d5fc70064cce0d3a57732c6c6bdaf95605080f190d13592f22cfe82da94d0a634a8080090fe251b1147b8f3583c3af98d09b8c8f427fbf92e0baeb37872628bf5ff8f06663e2696b3130a46d05d6d850be0a781d16596d87c5f3324398b83cba840a102a12597bee995a49c7809b9a0e8912764b0963352a63b95fcf630dd2400b1bd0d206fadf62077f953d5dc3fa2415e49cfaa29f07ab2cada25a5bd82ea0afa8b0039896b299b694da5dfe5c9e0888eb7913bb4a19e361bc634da386a41b5eab5057fe9aa84b4a4888c82f737d5bdfaea35e25438ff1f5a7228491d93e2f684d809ff8b1dc1708f01054aeddc46efd52254f76d39967e5a7b746c4574050bf2640d875bde27cae3870448a70abd01ad15c15cc253496e4d225b18d30387e7b9d9032ace4fb89513c8a3bbbd8c95b071fffd7527364330a4c4c73312a6b0b48ef90b0166747924511a378511cfdb9c1db2878e1b822aaefd2551a0652a85a2e18f041d23afff2c809d6ff422e455e1f40227d753c86e1878c98430e62bf9ed28850ec9f8b5760276bf4e4bba1b416b55dad0de5309713b067a723f8b0b741c68f00d2f3ae8a07a555d77b90bbb70e6fdd03f1460d2805a5167cc53668ab705fc9d057c5485138a89fbb6d88c52c89cb324a727134b10fef2bff959dbe3a29326360b90891be9325c73a69cbf8d918b634198de770c793728edd27cacadb5c0642e04f203107e109616e1602eb9f91b6f0cee7716387615f9eaf2c4a2cdac13a35009fa2603c2e7df7749f64d901b6a6b356c341d44a8c8489436cd1f0150afc00b0708ebeeb6c1272e3f840b2c3bd0a97dc54fc5e58fae98241c43f53f0a5a934a093d77f09cd0f2c31d5c993868e31062ad2698921b2f6f53d4cc7601246a04b308edc13e44b851af90a072063daeea8ff46825401d4b54ac9ec68ccee5536352006f65ce2e21ae1775007a18e117184cf079477dacd79d8c12fdc4512947f343026b8fd5f85a5e2678ed3ec8c574841c9a5cfd582b8d7f1794f097ff3e04a8e00c544c654895cb5c3c783e69a33ba223c3fa73a878a4721d7f535f1c7a6ef058021b05c9ea3a145c80ff849ccb8ecff88aacf3dfff6f37bfddfec05457167cb108b137eaac5e17922ceb0f564e06e19eabda5e99ecac8c5f1e0681026ecd3021027102d0fdf04c0593b69854b0468adc2b0f230fa0eec90a59ad74a95cb71210080d1c2155eb51154dc6b372523c7fd8d7a95a98724ce60ffc3ab5bd7511a86702475bb2267c4f632521692ca34d2c2972376708da2805fdc72b8f5cf6baf3490e2fe6a70a09d4ff4bec406bd1ca11e6d48313819dc488196d0796b7901c3ab8040048f6212b847707491c04445d6a5b6a25b6f6a6fc85b7800a5ed1d413b75c0a9a1e0664c9a8b2660b238d30c20b872d1c65b724ce6c82775611571fb67e850de732d8ab1751ac2f9b308f6e758263997cb12bfa83ff18d2a54f51335ce71108394233b6b5cf0924cebd82fd5e1ab5bc8f45a090fab15c09bb0f375ae127480d61e0223d001a07086c7a28a2daaf95255293285a56e414b594ad4bae49a21a0038be0ca7732e64364015ce9a271c570d8ee9f5893a50b77ab04e0b9fdb34b3069831eec9619b63d1525d16f2a70914ba5011bd92c586c9129790006ee91b6f0094b8f0c2d75aa3ea399dba73e54c73ac7afd02de9a6ce6a81d5d13bb1ca5f0078bb9ec2b63ba79b96df33aa64514184aca3a2816569103ffd38274fb9fc47f04289bab6adea974e2aa6877950c3df0103b302da4ec181892e78fbbf4483aca0f782da5a872afef5468d09635c24aacdf676d840554c22109e781eb2d09097e057a21b25ab8bc39c281ebc21480e6d9bc972e2b1ffadfa450051dcb1e5d637c01d0391506787a9cf0bed6273a3efda0a0321dbf48c5fe4a1e5a470ce108d5480ee24c3eed1330051c46874a674ffa202cd545f5b22354a9f91576dfc7024dba0642080a5e080c5b9454d3a0025fc80fd315e32d83f9677cfbd8e0ee5039011307d1d3a9e90184a12be9b390fac3f9d8242d4e8fdcdd50caedbfe6b73c9401dd0e95d112b28a604b94bec5dd802eb0db53d8ed849f81a85190de6e71d9894d3c04782e2923c57977b30aa55a58f7d73334537c90d263838279b939777a657b570a7e1d02c2debd71258676e57c804ced408187a2453f9732b811e862eac282690f +generate_ring_signature 9b329f5487db8fa896767fe45c6f92096dfe0be8251e4cd36d07c0919a9c7dfd cc275cc9d60c681f4d00425e16466fa9f2c36f5bb333ec2c95aec837a40d0fd9 4 67e340c96d30549d1dcea389f686423bf0eadc42fda684a21ffde3fe2a0a1d3d 118888e22f09ffecb3a079f729e993bbe3a2f8ad9b3ab98943d7b6d039c93e3d 1c03bb98ac40e56b9101b0a628aff1f3ae682a026cba93ede7304f942261f04b 9f8aab00b42766e651e5d5c5a71ea8399f52bab563afa059823ec0ebef99ae32 52b84d2cd9edce72b79d4fe908f59290c7231b8fd6479b11b81e990097c65707 2 6061e39d80b0ee493d603780a60e97398fc7ba7b97b853b74dc58e38ba53a104bbf6143269c99075bf7bb68be828857ab394b0427925bba7b7c881003ec7e50cf3706763308aec4c363ae11d6ebd93955a06105611d3fa76cdce4184e81e45067ca70f26da138c87643fbaa0cee071b69e01d589d18d2326392853d2a867d60583810b131e3969ad44f24f01a0ff2d56a9b6a895cb5d76b15c421c9858adee094031dc7a986d7f497fd63292482035e27971f6ac43e57d2f20f77e37d8f58e0288a6d787a62f27d363fa91c911215bdd9980c08974b720d66b05da3ea0a45d076e6914b5237006a05d3fa7319f690d22f9e08cbc81088a171159db5159627304 +generate_ring_signature 3e8b6dc355913127e29a053eeaa791f7b9b25c96292970b20617a77f2941ce66 864ec9b981e85f596e34ef440cdab5ac85878584c8bf0990753d53c281430f5b 4 f04f4e042649c5340e6decd37d44afc47a81e00907733866c866d853e10616d4 d7ac266b6fd14bb55b3a4c3fff28017b1f2d51591a8a9a38a921b55e0ecb2b36 c579e81fea2b7202b881513f371e0b5cc4ba5e941e7378cedbf2355e5df74ec2 16cd14d1087769260824246f32fbe6c6e8068e7efed639574c837904fa186e57 1d46be9f88c8a3546d8107e6460b04cde2821f0c815fdeea2a5ee8556b36f006 2 63d08020499a59cbb41d88ddb1d301cdedb7491340fe0cf9656daa03f2e24f0e36dbf1874d8f54ababf888410598518797d4b1c96358b9be3b9a4bb39b4787086605bf5b4e91879818f499f7648688738cc417269be27ae828c1d9edfac8040fd409057fcd2b97d8f024a6b9573cf9e5cd69346ca6fa60f9b9574d23b8d0de0729e83e3e1d0ba63f025eeeaf217693fea4894aeb407fe0af4ee7f640a456b20d22b0ff39997648ee5393540ba3ef81f7928e11b56271fe2542e8140cd3ee170c2447e309497fecd794f5b430c0226c429ce6b5fcd586954569366b0bef476b07e4c1d6049dbe4838a212421c97496b9d2920d96f6ff94dff6fcc965551d91706 +generate_ring_signature 4eea1e52284c31388c572c77b4dd6bc18080d356f22b220fb2cf4a1706bc0f86 0317dcfb34494772c5bdabcc64b2661b8f061614472914a82ed7860376853ec3 4 35ac95d061c9f478a387f5b33ee56b5c962c622cb8335e3f5b05a50dcccb9567 eff1403b48ec6986a6f3a436fa75390d47df6b75578cf7c00c9545fadbf9f9f8 bafee79e294e0d60c0cc2c4ce4fae48df86d0ba91e46e4a2e6cd033b34c74414 1161986f00835114de03ed5ac2f9b94de944c1a2b9f3a237c8e3bf9f0b048349 324b8b23740f0a95fb37e26b28ade940114335d0c395200e3df2dbccf3fe0b02 3 733325b3c48a360fac99bc2436f612447a7e74655acaf68d6c0327829067770da573351126a1db507326ba31404357c53208386916f9ef8d7662e8f6b3471b01da0e2886169c9215ed1cb9fafd1da3a2cf00917aa7007f8779ec4455277ecc059d933e2afce960bd9f7222e7d1f4dde040fb4558f69048bf3a1b46cb78237801f91a5a0676436ec0b7b0f10b1bd89b790214bb54f64f5867ef8deafc51004d0f3cd89555d9312a768c49031940ee3e108ba6448eb24fe02bdd04d0916ba0b2034b60987756fc0efd29274dd2f5a5c7de6f40c4ef02a35943f6152c590ba5f4001eb4cfe7998dbf59ef1638e9089b532a46f68271beb1b5f6ba39f82aeb50a403 +generate_ring_signature 3897f3826263809665c0f79b257b70f91ae560f59533df45c79aa481f14ba6aa ed20816021e0870adcae45b087cb64978c27a36fb94967dcddeba7878c43065a 6 1f644846ce53a77e39b3e5ccc0c578bba7b854b801815d27e4a279b929752d66 9370f055164552e621e0ea348794778e844013451447dac7420ba92789bf23a1 58d1f500efc8f928bae7a56d15ffc8b07b65139f4578ec2ee706ccc992d46cf7 07c2dfc3e82dd1625eaf9edff3a2fbc75e6e641a41a2ba56ed0c939d2b81768e c2156a0f6cb0f9d9c49abab8124b36784d2f0c676c9af313d1215357e5203e4d 016c41e09ae240dccf9ec65d33ee94ccc74845a9c8c233c2a2bfa220be6dd093 2753960b51aa5cef3e1fe2f7766429bf0bf3cd91268e609c44e5e1779005d20c 2 636ae7e564274559f211d61f15528b646b5e0354ee792528d224351d4f5ef30c6e98b14998f971ad67dbddc6204bd4b4df9d556202d1124ac342ec5061b1060014b9ac25d1e90d4188aa783f6bf5d11e2bd288136f8567e30a8662de1fdd7d020aa61bd50a63777c9099965b1825951321f901c8f513ff03b5cd9f2132a63b0f8653fc37c9ec3f990b2918c35c13032219c24cf6becd9d710a80b8e42871a70a5c39cfbfc5005d5db7c7566036626f801d4a2943ae9536f6a2c0592765613f0ea8cbe091dd4b4a632052488e0b843bd302d8ecb77a39684b49564364205bcb09d4b2d55b6f2610c309f4f4153defa51a2c71f7dd40408faec0ff3397295a750891f4a3d203ea2ea6d10d7258f159a569851e6eff80e980d94ca3d53245a4fd00f75d9085940f7cc626c1b342f73f056885db0060ec6816467cb60b885486160d7e2e775b6254b3e13310a4229ce0d52ae7af95792a7bf11d956f6b77cf58eb0fe937453c16ae218429f759b617fd1c9a1b656b4dfb6e37d0590af9e76d5ddc04 +generate_ring_signature deecd93de3b6635a3900d9205d9a0445035451960faea63acfb11ab858265584 1157cad423a531df089ff8735908f4cd8d8804ca6f796839239c18df3adb224d 1 888be7fe0293f3f66e8d0bf3606153505f274fb692431e258bea9ca2d140d85b f8dcafb41c048b60f3119e9537c0e2d44cb10b1e6734aae324e473769a63870a 0 5694400a4d63d8600fc80f713b24f3345e6241a7077eed86fd2850d674c8bb043e3358823fa0dd05dcc53447acd2c5fe2c7154f7ac2162f5418a2ae35c6f7301 +generate_ring_signature 81b47dad14107e991eabca238e4b65e7367928f92b795b2f3a2508c79608dc19 c05b11bf0e1c3e0f47038d6aa49ab4daddab874de20e0886bd6f6c578ff6db01 2 b7f3a03806831c048c28fa2ae2f5fcac0e6d4a76cf9fbccdf2ba1d0e3229b35e 6638087a63c6d537b87a22004cf2364162a52246a68162872931a6c0c35aa5ba 75c26eb3a2b4a277a7cd508b08879524621a628ed9ba713b276161ceccd2ba07 1 623ef1e32a089ef4bbfe12e3220759754d7fe25c90f5aa2dad65b678c4ba3f03f8fa9bfde7093f88aeeedab8968fd0b5a1d0bf9ce57d042853b3c79767c4c90eaebef5afee1ed2cbffab2acdae94aa10446484ef59495520d1d3fffa13981909818b180f4d206b575ae8bf131b7dc860c783acd935e1d919e17d30d130882704 +generate_ring_signature 27f39c66f6a61282ebe04d3c6116a57e4133798f01a738294eb6a52c813585a6 a9750add17565f877b2b22823ddcc5ee4b89f41c728972523d61a69b78e9088d 1 b02c8a057b5cea1652d1a4db956781e6ce07e6f86e747ce176a7eefa3d4d652f 8ea9d363e3c1ed113f8b6ae7672511441cad4877336596e940db05efd1e9ec0a 0 5e6f4558481fb53ee5fc6cc4e3e9761b95081eb3c5d32f28bd7f4935ac3e8402d1a2f63993b4c5a49dc3b438e9a0fb19b6a4d627f1fbba6767d2d0550a934703 +generate_ring_signature 5c37b80efb792c48e4c99395fb19626df810aae0e617e587415938611e35b37f 952470262e4fc8ea399640c866cef6d408edeb219511cd4eca7006622386f661 14 84cbe808524c622505b5e67783989b593fee2b939b5932b8c3fd77bad3b4647d 60f3e7f2287a85996d5aac23f440ea64c6354fcb1f7013a5b25b081f2dbd991b 3f4a81047e7b9b1b8d642f8586c6f98bc20baad33f75a7504afa7c109b0fe612 40d1ab12988d9c1766f01559399c0e0c3451da872781825364a1c42b46760ff6 549564baaec6c44ad533e9f01a4a133f2ba260da2127a78ce876c30e97c6cd58 82961348f57fc20143b0d6b30e6bafc63b8575bd0a55a28df76af8629cdccd00 119798a0eb51777954ca108539337f40554d4b2df47a4d21de97e135b66b9dd7 ed90cf28e5407fab6b8d8fb70a98d9fd064cc0beb47c61ac94845dcdeca4eceb d4b35cec0f367a4e133cb2c6d4e63c07ee92247ea1033cce6bfdc7a67474e224 d37a52f026a589b51064eeeb64a14aea05420c2bc70d5a04ab1d5128819ad3b0 968604beed7f12f1058ac5312bca68c811064117b189e0e4333db93e603c8be6 b3793cc7a8a5daa5f02ca4496390157df25213562e7d4fcecca72e9a23119e71 9ecb2e012c207c1d09124479bdc8ba9fc10cba28ee51301b5c7eaf9ef68fa355 011a63f6c9050800a210f1015674fa555ae80133dbea7e709dffcefce488f691 ee5d09e9d9167824d3ae704ba8c5b4d2236f69da9002c28cc0faa08954caf708 8 0f13a3f63e0658ef31f2d9b6831062ccbd95c1077fb366ee5cd482d6f199b10be0da73b421074bb95b7d4d4fc77b27741a21891f4070ca8b6c47bad24410c40f51cef80f07ac0b736b81c72f489bf1c98d5452c9b1a376acc02f316c3d6ca2054d9ffd7b9a43309e90b0252719090073389dc92fb283bae482e10b00bdf9740b623f154f2e73c1012463c5d1bc1fc0821d4b73a70cf147c3044462b0ee8fac02ce9082e7552eace098a227cfa2bb3e770abf744b8b7c5a92358c853bb434b906d0636040b6a5ebae45bc63876faac43885febabf59c8d3c3f1ed817a158e7606e522b17a5f61b2a5828561f52ba7eb9c15917f07ef58dc330fcc27e9097e390a171854443c37d603f1f3d1e263accc5b219d9a79b7b415c8d860b9c3624b210a89dffcc7aab06f1d13cae86c3476f037f5209968e471e888bffb41f016d1c50ccd68610e402564580a4bd54d55dd850ee19ddd482b5d0994460691a62de36c0dc175138b7ae6a5ec21d0f2aa78393081ef7d638517e9a11cfed9cebb1dffd60386eb62ba5a7622d5bdc7414ef4ee279492488e8a06996c85a7cb27c06cc88e0318ea764c56bbcdff92b07747de1d96f12d29d3b0802182d7ace3c08f7fc9890bb84b992cecee38f8d251aa7ffea6071b1d2d5310ef322ba96f577997ec15260e11c20204aced8235b879d4bcfe9304a4032768324549c77726474ef6574a530a7900ab384801ed3add08b9326978bc98d6c43a2ba64b3af02ca50fabe0bc3e038c3dbd5c940b8a64034dda01f96af84c7b776e02172479da44e0b6ab6b9f9c0c2390e9424e0f119b0940f3bcda29686f25fbb9b929545fdd075346eb88941c00105726fbb36b4fa9115c0359bd227aec612f9253188377f8db9751ba768076071881159d2f5d1bba54825afbe244665e4dfe7ea562b62f28b746ce0f32623601682ce2c3229a5ff14dcba206cf7d8ddca1e6cb93a29a414b2cc65826faa7e706fd37c4063cc19776c4996c87bccc37538d45a0c80b907b3720ce606c42c5850f0f8c3d34016dd6ec1762663ef3e83013b2c1655e4e4c20e72b1be7f069b2bf0ee1d4858c37ea52c3a2980ed79e78cfb6e86aa8f85686299f23bc1fc62c55570108992b33d13df169abd62a25595453e57b5c6e1c2724965c06b7aa506fb4ec095e0c86a73db10b2b16a3667e045fbd8c4ae66b636156be75418be3f71904c1089056026d2c527458eb04744b316018d3bb17b8acec3a0f8e919bb259fb88c703 +generate_ring_signature c5541e41e2a344f29ecadb669ec32474ec245ef6b2b5f06717612477a8bda42d 62feedb1dd4c876f4e95c2733a958adadf85d278f946e4c82f222a7711ca10ec 125 2804aafd5a6c29ddff9968bc1d6cdb3ab4d346e83d219fc96abcc5729f6b703a 77521a9631c275477f4163fea03d7c3fd704fb7b5efd379320e005840539af0c ff24636208c5422a9c83e3e8de964ed134553e54fb7e8205d2e77b325c66ecd0 f21c08aebe88ad781e69ab0026121fd5f134720075db02bc436db5b6ce9b459e 0535f689f85a203f3612be098e8fa8f06ef477af3395c6a116bf9e3280f57975 ee82c0276beaf45e6efc7ce81696bfff42f557029ecd81e0df7887b7dca4b0d9 f760789ca02a9fbad11532b7cda06619a32e2e6e66973039f4f843a1cfcfb906 f41125c6fec4783f9578aa8fe9fbaad0dfa5612a829c9d5b055c778f404a120a 0e9f1968af9a915ce9c88c0ee9a1c42bc4d6f606262509404362a80feba773a8 43d5caeaae639a65004bcf71222e8ca2d3b2851b152dbf7bb89aca28c456e63f 16a37a71f407d8ed2bd161cb9d3f45a9f2801ecbe67ccfddcaa31cede4cf986d ef3ad750bcb7779904bd2bcb5561d505c1c1891d8c78e46e94cf1d63a0fadcb6 746178b33c58f06a6685b550b789d398862762f14ba9a3f8331b2ba82e971322 ef470ff38a5ef2fe7edfc030a77d086100965fae7a341afd095867a411eb2745 e8d43a2b8c03e0eb1d64109b1bae138ee4578dd584480fbf9becd4e63ddc3ef2 f466151692eaca15a47af224b8b220c1f71c51a78b581b1f12695947c790d09c 0b064774a419a807e737a7ef8ff52dac16c372448243428c321c024aac33078b c4763dbe8d39b3f2eadfd70260acef19c336a1fd80a89d68bf8d5564c467ccfd 141ba183ad8dbde5e67f98828c4adc018d53db2cae83c34e3be314bacf438591 41927644356a7633faefd0bea5fdbd25bae1e720819d2fe79500a950e3fe6d98 90d8b56157bc869813104b12784c4e88e908186e10435d50add8746552f89c08 9f3eef380682bb8b6e0998bf7e3391992492183d624b829ed3340c34a6affa7a 87397030797108c58eb9502a52a18973db5700f8bdd610dadbc80aa3b6c66e59 2c5d5a0d6bbeffb62f288ee87e5762d6fc9074bf787d5df4dcde7a49e6efd1bf 8b2c1a8dd258009d5751dfabecc24fa5935f0d252871dd66d293ad0185915a5b b1ed2a53a023134c2023a77c7725817a9205f831864acbca58f9c79d417bea85 7f55eb07859de3dfc0a8e206d1d1cf4a0342488987a6d2c18f41e97df0df014e 5188fac915e9e03a83302e4ae128f1dbe63b89767b91a4ff3b9295c30dd3bdd7 10ff7af1f258091927149854228a5c7d5ff271bf4bda55a9ff34d1ba7675c509 38e38959c1ebde70dff0a5bf2257a13281b11d432bf7f51d3737c500c36f7f2c a19df45899c18cc81122cbad000567f8fcda8e789293f9b77b5845c03892fc4a 4c33aed9b6b8ac36afa9892e7183675ae5490d21449940b207aa6466cc1e97c7 4292a31b6bed65a201ee83b71915c238b72072ee439b25a2cd496d2cbba85250 77891645ef71973768d5086c5e666eee6cd22c18c9c027277154c294e58c1107 761de841439e26204b01290f022bb014db8df120501179be8f3764d7c19fd8f5 9e711d6b3abaf3a78048a9e1276e6d70aa4ba0df9673d7b7cd653494125e585f f9ea96c8410b15246095f15b1a080578a3d2e4a42c9ca7ac41d713d69c674237 13b6fcf0056f59eede98eadae5e9308012083fe4986c744e1fd93db114c389c2 1ab06de0e3067113d4ecc48c5321fd6928e8f30c0b4c073e21a629f794c930e7 f2ab29c1b2669413a71b096379c3fc8b21889ba95518e3fc576215fce0033592 9463f8f4a96d456025108c444132060d710a6895da60ba9ed29cb0e7fcd222e8 f17341125a334c6f5ba759ad278014e62bab511f9c7fa45890f99a07463d5299 0cc4709fefdfb59de0bd4bce0f3833a2e3e9bcfe56ea4469f9c1bd9e219ad662 cf4a202bf367be4e41350c1b3a49a90f1faaf925a73af48224b02d8679577d84 844e462cb86c7906ba82a2cc5eb86828fb9bfc790abd59c2cf54ca6c0524b6f7 265cb58a1b994444fd2a9ec13e22f1e219ee2910b87bcc809e592d4597cda731 eab37e95ed3dcd62f3ef9d98ded850d36c437ae0f7c4d360eb9bc0006567dbf2 a9bc3a316ee0f0dd1245d33aa0f2b0a65dab787db3b093b583c1d183cdd87388 c593bcdf8162435ede45858a3f82d18b6e08ec9917574bb630b019885eadc15b 519bd964446b95b440fd2832a2bdafa6fe9dd41f91bf9c190827ecd014817149 3c97c72e64b47cf79222f987d41a9f95959b05f60aaa714ecdb375f6bea2f24c d0799a853482f9870120c87d82efe93f6ba672e361244a1308f5a918f8026204 3fbcbe6e8afab9f595dfc5d42092b46ced57cf4a77a7732010e5435df18bcb35 e7058fd902caf725e1937654bcf210f8a08bbcf4c174127a19bd013a4ed13c5a 3538cc278efd09cfcd347eec14091864878040351f128a36f17f8eb1bc1bca93 d023d792c2ecf5e174d78de4f4eb1c972f05fdc31510ddf285ddac03edb6e9a8 e3bbc9156f2d1efe4b0e5bf24370772956146201f29458902f299e7b178e2926 5ce05d70587d6f961dadb852cbf573da00d3575902e5a111204c29c1c1995524 13d6a79c71d331cc44369af63eebe06208038fc18a35d65a9bcddfeac86799d3 f0c18a55b75b0e8b8e34b5efe0d3227be9898fbaf8b270b82a347c477442331e 6d941ae5057bd14443053028bcc66e3df3ef8002ed4889722a619d27af42c4c0 65d401e25158b5d536010e71ddd6face36fa59159c7147f57f98dbabd6c03e3b 236e3b8f14794886df4b2e6cfbe93af86a369ed36ac7a163855fc84a917d132b 2e731fad62c827847155d6b543e91656f33a37f29802b99c74b5ac3113bcd3d1 8bb40fa5f58d155cb5875f14fd8f540b6c5dd7fb230707cfa3914d8bb76a9985 b741f627de1025788557446cd8571f965165f3b87ba70bf9593d2543060b1dce 40c2cddadde1a33d787f42ff20b897470f6c49a2d1cd8fd9c9aabd4ed20d8098 b9b5944a94b1024f7f03d858245bc7085f2bf8c9b559040404b2a8f1f5668353 229f8995a931a23752d3617ba4c06a74e48a6f3c8bfd40aaeacfa2fcbc6ac031 c97156ee1235c56564ba3bcfb12c645e5eef9bcfa4173c3f7efa755646a3999c 207cdf552b7744e8e68e4721ee56795e2ea0d00db61156784b351b07d91d9b16 cde0f0dc90d52b59a17d9f220a8993787153aa1b2dfb30074633219be62ed6ca 2f8add5d450bd257117c2c40f33b50dd4a85ca2946b10217344e842ad29a3f57 198859b069a1c48e96f6420b89dc69cbfff32867574717ff02965e9ca9aaa9f6 da1c7cfc3af582887f4bf517548956f949e1a83a8b82a5c8a396df15e1ad1fc8 a0e5719a9ffc23af748a8e062ce1ffb8c82da607c2ee26993b0e76c01c1951a3 05776203a11284fcd79016b18d7b4544d10b9ec88a861baaa3b980ded424da0f fcf8075b1a91c94ec4b6f8412da739dd74d749a3cc55e9574e0b9d982d21ef43 d567383f5e2709fd6b640dad0915a229e62d8e44880cbc20b71637ef6c883cc2 8c1c065394f4585c0244671370ec3aa9f31c73c109f50ef0290e82b41730f3d4 a6def36e17f8f1bf3225f801454d57b2f8792adc94b1933fa01af88fe8e967ea f61371c2ab70bc8012c87fcd14f12d7ab1a2eb51875c764acfdfe3a2b3f69487 aa0303635376c96892e2e2f609d626b6dcca8b19879f5add956927ecfb6112ec 327a716cbd42649a2f59d3fcaa59cdd2049e94a96bcf21d77249246ffeacabe8 50123f75a9558eb33656757408e2ab0250269a03f9ec1dfa8386b38ffda39f6c e38b6d66bb8d6be42b76fd58aa01f118da2cd9249adf097f1b3b4f46a54d7ac4 108e7f9b934c25bc12d2fbb18031bb211dc7cdf996f111d0a2e56d6cb9c6a9dd 5074acee3dc35437abbeb588f5d942f968d449823b29942c21c79a16f05e65b0 6038f6d5324fa1e378010bf8ce3bbe897d5165b764396e25c243db6906951f3f 969d74fcf9d04f95b465ca2b88e50a79c678cbb3d1e1cfda6002379972eb676f deebec572142847258635858b17708930be57b82404f14d028005099d670675a cc8970b85874d414e741607ab7a9692512cd3108c0622c89128101fde33eaf83 ddc7f19c8d9a83c0c32191ca81a3430383c6de882741218a4150c9779559cae4 2270ff3129579cbaa7f986cf738785ce9fbfe70ad863cb39de9418905775758a 6e2a1ad2fa32e93d2dca5ba62643116e6b0788fcda52a9dd74c656e358370580 0d7b0a579493f836a031ba1a45411f36e4174ce06998bd231bb22e4a61aa4340 eb7759d06fe9444869c5fbfb6276a97ca4c8fa577030618ee50c7684900237da 799b5c7306c3f2882eade612689f64b84e03fd984f229b2bad363e9d22b33895 271ece66610fdafde9ca4766cf6e9cbec05386e840d0b161e5508b1a21d025fc 74528f7ec8243c31361f0c20b8fbdc3001c7af4a03e156d907048fa5b02a939a 5cc57310e3f8c9b79dba293353aebb162d7bac8535ac93366bea3d4ba7421dac d0440a843f298088834e32c073f27400c239423b5964cd4cedfccfccb0dbdf7e 61b3bd3533f547cf1c95755751fed8295be21372be495dbd6adefc160bd8e3a8 0a62af61f1c5b60e5d8e0a73ce851c3118b3a33b3e98248508ffc21c12e1aa30 a2bd0986f7ba0a3e2acd06c9e5bdefd96e901ba630b261ab1dce6ba18628513b 87a2209b6183ebbf569c618925fa782208943c0c4ba3be4ccd1dbbfe4fbefa80 c929225b641df9a42614b29645ce066050299b51fa4f76e4d5badfa50f62faf6 02b21e01e28dd2f197fbd265b82c6febf31d13c357e2e524fe770938786c0898 e224e1d91b7f496640c10c658c983faab5626f2ef388c8573262a5c2b803d737 f7648874f6126260ba9b85d3dd99419ebd95a187a00f113efa1cd8e26e24e66f 70d018da92f36ced31cc9298b8be6e9b7d1873b3443c604e0911be4cd6bdb190 0f7966f0a07e18ff4715fb0d0cd23d99c8c6aefb0295670e86eba3100458c466 327b0f4c5a5d34c9e69b5f3c2d9e32191ea616f31afe0cbad2db717b29cc4e15 e6c620872b328204fc60016bfd6ffcd5f475868e9eafdad2f90fbe8fb604653e 367bc5e3d53ecfb280d5bcdd33d37c3995a9ea2f4488f20b0bcb8bfd84b9e655 b826533c6aeee95b7f8aa6b9bafd6604065aa3f048e55acddee13898e7f9d41f e7a4ad5121e7e60dca0ebf2f49de2f19f622ac3bfccaf7c59d548e08f5176156 7b9802431d797c61656841f32129f8a99e23f625013df856eead6a0eb63709d3 6a7337412e7935d128dc296c0e35be814127f25ffeb42b64ec0dbd294985ef11 2ad88ddc9f00c78f253edf8a26757c38f2ad59cbfe69ce6cc0cb6577dc863782 d8e3961134ba49851ed743543d66a85dae08fa786e859ced4d4f1e9eae6c148b 157eab6090d78b4383f756450430cb3a4a993ba3f19e73d04fde2425aeea0703 614ac9e9b8f848877e3ee8e24d1b4c597f6afc2e518175b02b209a69c0a565c0 0a4d2290321b8aeace3965cf602c32739a0beeaa91567df3ca40efa915973447 8ef0ef41d7e977f0c0f2b955e8868d331a2cc5e1aea3cc014a64d96f1653b80f 6f8dbbfadd4f875bd3fb7860e6b522be87b40a36a9aeffb7d8de0115f2047b0b 66 03c51da13dcfc9a1fa8e5c13ad5332b30ab297f8c71020d17d97d4bde7fef30c3478d97a496667c876cb4575e00868ed4eadcf01138450aa42287afd5f188000d15fd50f43e01508a008c4f306831995dcd88cd80a3bb9d133f7eb7112b10b0bc5ff61a955864b72309d25dc0e45053bdb884ed93e6bb9f0779a225680648b0039bf608b39c3da583f5ad7670077afbe3f41279c9df7760126e74ff5055cf20e0de870fb41126b9da1e1e789fa09dfb2cae73a5f3ec2c9c0ebf563e7502f560e0b24fbda4ce29f8805c24d20303e9a3a94772a57b6668c12340391b0a466f60ad6970ea7a5813f77a20b3620b9e1e433f003ac04988c2454b776da39ff89920d9d5a20fa2b3d4bfedb40d4c384a91b719483af62ddfcb141230cf957de6b5e06cdf17130a8c3233aaab69a4c47b3b67ce20d47a39df64f424e160e6cc52e210f9409c8ec5a6711b97f20096ec1c3f102d4a9daaba9c3f867dae846cf11583e028fe0738057a8a0dcef9a6ccf88510b7dca83260001e3253092f3ff25791d6400880359ecdd45c79f48a2acb125552dac5e5926faf0e7262f795704a61d3ae2088570a4dbedc6ac3b6f58639144faa2eb8fb09a6f1f5a8ceb42e1173bba76ce071082d4d96a654472963ad7a36890388074953b20279f2aa2630d5587599a970fbbe535d782d42d4ef315ccc95b92cf8f8dbb9e8322b17730cf7e055898b9f0096e5dc2ee04ae051dce7e9a85547f7a85f7fd17d2285d3b49ee0695b95ee6f401440f8529ca8f2503f2169e95de3fa059a82b7eb175555d4c057e18487c97f6022b2ea08c73223f41b5179cf0a5ad89314f12463041e13d3f1812bbff34e923008e684d70a5d6887fe9648ca59172d1ecdd396f278476b9913e029037dd9d9806e3c6e1031cf6e76251c7dd239b9110ea6fed10572bfd402d8e4505c8c689ef06decd16ca06e9ddd74422d0105720b7e89fd94037b3ad11118690d0c7cb66310f2b6b355304dd4f37f41e78c02385bc2acbcefd4639f73cb867af108cd9d5e400195513de1fa4dea7ef9587ed647c3907f7406fc270b782fcdf2e05149063db07127f2c204dc5aad3e7e71f8d7eaaa4ce6a82fa6ee58d2300dcd3c5b2d09a4309b0b5e937f272ccd9c3de375257c9af37bfecf4c5b6ddeac00753832379f1ea069be2e7eaa2f85e53afeb9a2c104b1058bf862d99c0c0fcc7861fdc35ef5a710e45c496802905c34e71a170f60c0d5ff5afd149ae285bdbc1c61f1777c71fb001bc4f0d378204307ef691a36e5b3bca6ca2e1c45b9059a7d2a54b7245a6ed540839976d0cd7c33dab7d617c22dfebc09372cc3afd54e6e4b9ea78ef9d5a797e07fe5689dae6a53c2a790612d53ef918df706e4a120906f56f7bdf446d84733c06319a7cc3712dfcf83eb43ed60cc79ce7c8a6f1e1949d7ae10ba40cb6ecfb290bc7f1766e342f4dc52b59ca2430293e94ee1fd8e6a49467b7d05f13e7d7940709a8198613d885e9cb81535d1bb823f37a751871ff44b2635dc251736eeb868606d9cd5092278d9daadc0d7d43885f0017124a46047229cf11359c992f616b7a0cfeaa52ae24e14c231b641ae1c7e224f3c415a4acb46a1d358bd1c17307ebea0ac5c8906563c63678c95075d9bb9ea4a12f9df4368658bcf4b84cd0e895a0f30245d08fd3ec4cee9d667f562f79e2a9f8e0b76c3ea2d34eee094422b561772e0da7dbde0f80df0414b68c1797b5cbf208a2c1cb900005e79ad08632247e501509ff883b41224ef381d7fb414e58e6e4796e5da5e449baaec1af97b14a1e44880d658d571ae26ca9b0b9f9df5ef7cc5ee161a4c19768619ebd95c5c0451895a90912b02449d9f1242f7d57c56a07dd6cf7f261954ebc33837fdb3ec53b443c9901716a062563ddf9f93fbd7f9bf1b91efa7c0adae3399d2a34d388202572b19e0128e79f778b674e83ba210e8b2f0146d6529b348a2a884fba193a00746894de092e7c6a084202f8c9ee82eeb40de38d1c92ceac085b032345b00dee21f4f3c10066f6462ef5ba113918dcc1d785c6cb095590df263ae60660f9704862554ec80e2bfd353b7ce3ee7fee8c27e46fb3307c4ca19ccb3a76da4c242fc2d2850fac0961e358bcec2d97118c1b5337eb35746c0f13156682d5b20eedcc23f421da6001ea5cf0aeb54ca01f457e9e5f8007a24be9a5e6c6f644030c55134d25c704af0b2575a5d4df990c460edeecd8eaf7e18ecda4c710824cebd9492a0d4398a0790d4660854973ad4311eb71a9cc41c97f6c3cea1ebebaafc7adfa53bf9736444c06ad9d63355882eb4079d88c79422a1b363b555a413803dafb9844cd7716ba3c0136bdd402eef6669328cab8e858fc24d831d44f6f35b6ac15abe4925615fa570fa60203ae74cd04e55d3940dc223bcede1984ef9b9c9bccdc3a6d020201381200fd7545ece2194e831dce2ffed5221372ee9fce6e74063e7256b6681e3ca2f00b9a4622b907ce1f05c280fa192d943511354e29146854331554707539f4066d0c09899617d3bbe68d3d881ad6826a592e972ced5c50aac80818df150180e31b040b04c1f6bde965839e3c96e0795288d122fd3c56781d14fbd9879fa2c06fb204c752903af2e755bc1e8858338e2b765c44f9404e4a18fd0f91b3596c9271f406bb75e09fdfd2f4904455c4ea70b054b751fff7136a773d93839d4f02edfbce0e56cadeb1b6f6eb529d8242cfc8994a58337c28d01228a29d31099263f05e8c09b807a97e73e015d71e133b986f5df9282833690c6536db7e7379a747f0b48b0a84e235c633b3331840483b7a8702a6fc3c0fedc43d4ff22e0da1d018d389700ef635d82e8b5f530984a6af95acd2d0d0808d698fe1538a9355b141a9da3b760c8535800cfb9711700786653cec731f1774a04c4a950a6a109db39e2d6510930210e6345da5412f62cf67a775b3d62822e78c32c9cac5cab36f3f30d3edc1d30f00324828b0ef0be92bdaadc599097bfcefc43e4e4c3624ddd7fda7b52a22d70241e3153bf2b6acfa043eb9eaeb98054bf9fee0c0f634c18a3ba9eb34f1279a0bf6bda8447a37ffb5964d43d7dc3746e680f9971147fc988db3a90e80f0a5c10d1ab067fba00d24c912db2a7d9efde5b169130f124c609b2c2994334d9ae6130632dc2b46a184502a09c2968d07ef8dfd6097ef014091ff2ebddea5e3c09efd087a2ddc5cebe7f41f8f54bbd5d26b651291aa5f53a3a825bb8ae21e9a6f78c501fdfc728e3591fabfe66f1f3a00295abec0179d9cdd9e6d875621ee870c14dc041ea89f7056e2344698cf7dc4617f694284d8b6a2d206bf96febbb49438df6c0dac8e19f37e64d0e792713f28e771dc631a01ad06289af2de182282f6a1e89305beba4711b86a82a4ec54ea0fc3226f07c2966cf908c8351ad685228913863900b0d35d17cfd369c374d1395d5a364527d53524fa4f96707a7b3a0597acb78b0bcd07e8f7ca27e1e5c2117bda6be169dc92a392031af88c52811d169ae2137f0d1639c41b4739d4691f8f57d467945b0b1dc7d82459fa678f75debb643b2be20fbb18612a84583618eb0ecbd99799e5a23a05cddc8a2ce6f3275415681ca8e20ae216298b5d526c1f74b503514ddb32b4c243c10813339db16eb91c578ed1670db4a9d2e7fa13450b67e317ced5ed3eb4edb3b848985dccd452ab6be85d29c70b7624eae5b56f44084b576eea89dc6fc85c6454b2bce98b19d9724fa1fad5c30edcb1d40b3646753c393d0a93c2dcbe9b05772f3a2b2701c30828c541ebe0a50b54df2180afa9bd2ba0d8925c8dd9f4fc6cf66bc6698cab1d5a522d57f90afc0e250ce977176561b3e909d7a2a2383edc06df64490e39b503d5f6ac44a3778104ba7674715304b05df024969725c024c55f01ef131fb9a0048af416d68cf8130dec17a7afb80bc03912e4b31d2eadc915de1bba343c007b5b5373cc32267ce806dbe00adf2c2b714332b3eb9e9102f20f2aad2f232a9b46d251fac0cedd64bc0764fc6a982a589d68e0a8f787e41335ae22e6a1a5930870ce413ca7cf86d13f05c90b6d8fdbcc74c35a34c24c73b936f918d5ed83b4b7f61794b033a5f5e81a0fe189252508a23582fbb86b071d0de463923eb699fef4e9d7140b4a68787f0f054a4d81f7498be4010ea8514782d16b8a486d0ed80a7948bda0e0273bc14b2403496513885c5417bf007a5f93fbdfba9fa3ecd5a3a871a77670a5ca794d7c950a50d316b3350a4f1f0ca2ffb150fb729c88905dcdc89fdd6a5d13d0f84a8e7e0b94d15f66d5270f4dba5b7e809bbfdb8cdf50e0d61311a1d6e5b5c94f5901a60e9ff691632f0aebd28c989ec35f7e2f4e234f7cb9f2961b6211c5563ea95ed5063e8ef823f742950b2b93107ed3fdca7e259225c1a5d6d8915f2af9da37feae0df0f4a7f3efd4cd358529cb0aca99ae40b8220cd0b14491914a5ec266556bce0d571605481662abed71571f011d6e7c9f31f94bb2a2d48126d8478e0d49ae2004e5559a482a01ae8c1ad0c736969891b07d17a878535c1bdb572bd08b23acd20a88128123843c4db57b52f18c880f3e3983f93df6e1b60548cd65246cffb118095bba492204d33367da8cdea1439d5f5b70376d689f42b1ad7aae4ae71e658a09d2db14fb0473d848dda3980149322d3d0581ef979b723ac8cbeee5ef6e6ef30c99ba14abff111fd533e31dac4efeb7e95a52883e9b2e17dab4765ae77b6eef01e1ff2abbc2bec91e033c5957b53255182492a6e95e4c3705c0c95b4e37eb3b0aa88e306e3e2e25dae952b8302f0ad9a635655884e2edf73735864b0814299f0743a6db7d478219fbf168fe10905c6f868300b7d976c04cde92da5f82318f440bc3fc2328e9c4eb7f4c27994cd45916939c0b4eaf610f1670fad5ed0fde56520f8ba2093199fa7050e6dc397c68a1628ad0526ea89313c4c83a52fc6165934302d82568e965ba51758994554e5ae32169189aa0295d3452487ed949b2eed4c10bf63222b64e8a7662643c115a0ed6abad31df5b8f59ce6d1da8ecf4a7cadc1e0b1a799e0f7c3290e2338c17674a51e150f609de86a99ef91b1006297b4c37f80ca43711dcf7b6180204e14ce0b147753d55047729df62bec151d2cdd24dfc540f14bf6e1bde71d4eeb25ba6d6b652a8f3f9618787cc665be918f1759601720c0ef386e71627c954605269024bcc08dc713edc59ecc568edb6af317cbd9448c30b5329b0c0d950b0e1a59a994aeae062f6385d638ef3af45b9ed1b121cba975105a042b692c82b722cf674ebe787980a4776b150337ece086d26d13a74b04b4205b2003c5819029659d532ab6328924429cfd9432965e9caff62d5daadd2d5ad004d78fc7d1900cfbaeba3e3779f60ad8e3fb142ffc5b4bceedb7f3399dcfa0103b95f6f1284a1a276dfb75791d92cd4dfb75a497a84979a18380d17ac4a4e290691032287351059ebc30a157a7828bc987a271aa2a094be0d6d27e3b5248e9a035e1365092ffdecc6c35b5d6180c32b3f9ee5f98a3b87e0744d598534b5c4980d2320c9b07c0294d8fa323585f7d61f1eef8fe92f027af2687277949222566606305c8ddbcf05bca04a157a9513642789db3dd674f56225569870ee3cbe18e707580ff74a3cb72af39acb31f8b2d61c5eea4daf3f164b88609161f4254fa5a30ba4f9a5507aa45f53af37f707bd03bf1ecd90eaf80d59d7d6d7452db69578e20f0caf1c15368b7796c18b5d911a317563b4ba02e0b877e1a94e403301b27c8b01570e149da07f3b5e5e1b3b8af933cfab70d971b15dd5ea2fd804ef0b07622800d4c62080d3eb8731aef44c554f8d21227e986edfc023e05125c58d6a9122120c11765913e60ee2972b05e81fb1a17ecf00ac29c4842ba264ef3703395eb62909f32b5d852a39234cb8a77ef60db93cf564f9a9dd2aa2c736b63f79e8e5508609c88db5b11f7e5666ce5e3d26df60657db2b3ae1c19d72a031616a390ce055b0bb67afc359e637fc6de1fe8062997dc5c6e8fdc92042bc899a6b775f6d3dd390ae2e83a44fe167cbffb1903c3ac24649221375a7be3730959c96d66928f907509c00e242ef536f72adb1244230cd0c9111f0464e98a1d8db78005ec45aff6df01ce2e4ed27770c7b1393c05dad6a7196d2adcf18e7f89d819a4e077973fd3030306faddec05a6b0aeae8a196d5407de08c7a65af9b6646b5930168646465516000a81c7cd41620892da603cfb9c5b69a0a5519c0e88d33da4db2fe4f7c8e19d00be28025e2be62a58997d7071cbdd29169b0040f1714ead974dfbb4944abcd40b03ca39e49153973d4b5db8c9086523897bda887fa239c6f0276aaee5a594280215cb987d1a9fe3531256be856c094393222f2ae31239e7c6a57fd751f4fe22045849b5eada89f216fbbd0e70126029a265bf0b1331ff5c9739f5f89d31a07a088ea8c1561a5eeb0822f66a29dc3398736fac23c4a233e961b946af8e33a43409d9eb922701ffd0a098ef09ccc4ef2c8672d29432c21bdd9bd1b418ebbcf55509d65d16ea319219082ec9d7313f71c78502da080e78465ad72cf983a8da8cc10b2aadc39e74d5db35b65c5fcf169fc2ec82799f5dafdeeb9edbb234095b3a5b0cacc31fc48e0add8b664bf64a462e21d68e1a7121036af4160c09034676bbe50b1067847250f781adb2cc354ed0e1b16d1a8eb0609f3a5d824d547cc5ba0891008e57232028a283ac84d511918728b20f8b06e0f7f000526ebc2bc92878ede8002f0a3b2c179609a23548130bd942991b2fa4b63ecd135ac04cdb685ca41d50084167a81b3bc3a772eb1e8f55deea9cc055e7acfedaaf7dd5fd31ff7a0fd9bd0425029fe6942d785963d0b7b37045de8598cd19d12be5133162a004f2afcb5e050ffe67939f08ce4bbc9249eff06d402cc814c36ca12a0e7dc8ef95af6b5f4b0ce3ba69f5abea7cc5e74dccbf9cf7bc431ec562dc8d0fb6c36bf6d64fba64c004d0889af8395674f043c732256d1993ab6f861aac75330ef56921449260ab95097a92c1b75cee135ea8d0e63d1a0d5b5121f082f8fc4ffa2308521a584ccb2309541cb395ae243bb1fcd8be306d1e2ec6042ebad97b12126098e52be84fca7509f003888a248256796f038f30c59dfb2133e9f42f6febf4af86dd0e815e73cd06f6adde63c7c603ea806bb9012a5105d5204b29b23e16bb21efd96a43dd4c2c0a7548ce93303cb226edfe90d637d13f06d73f70f5d2f407fd60a45a6e01bf32085a83e9b88fbae058442a96c6843bad1529156b5144cf36f40fba36d5c5e8cb0af59028e1d629b55166c7bfa0fbb65354970ca7eb18ef7053d0e6629a7a75d30a7067b93a34057d8a3b788c12b245434180e1cfbe507faa270f1809c14fe79407a3f7ff59d3188f25f92c0376f26d0d322e7d65038b96b9436aa1b95cc4677503be84e6cd5f01359e5eb862c11ebd8b45cc18872518326b1f9141515b8747f60665015cfca5c04715d9168663a2b190cfcf198b9f43fe0e39b48d9e458502d80b3dc0b49a9dd3fe77820f7714270715656acd8047d28d4a30ca4f5285d9e19c099209bbd3a50c3cbf58d9620b725a249a9c849d0cb8388dcc7f2a90502e5efa0dac0f9eaea95b72a31afef787d38b3d1b12753cd5427a3549f97545918acbc10a81ab5eb0bb841eab9100a7c81028a32c8d3826b836136942234e829ed2212a059e9ebd5f7b37417b4fcbe5c29ad97256d4c8839b18f930e88492ca2c24b59b070460826fcd5185c05edee9740e6c10733a047276de769b55d352d080a7227d05ffdef14076d65e5f51ca1aeca5611f7628714a2be8e482cf9533ef9040fd0e03ce97908629bb2ff05b40da50d3e52729793df7b1af7b80ade2da610e1cd3af0c171b287af79e054a9586ee6dd208c1236c301c7fba9edff173f423187c49f500b0c405ec84e1d116cebe34083e7875a80a3cc273111239dd088a137a3e4166058ef6682eba4ddd344d3a2ef203fa59213ee8931e73faa6d87ce98d19737c76009445ae9f4d31bb8a708127ac42daff428b8479416f7770e26297890b292a9d072e087239be25a2377479481aaf3554cf74850e77afebe01f64edfbe5b9147101aa8afadbf7a7bd80d25b2dde042a67dd45b2b39e99897473e858a41e5ada7b08431fccf2e2545bff71f76b763bbc44014718498a0bb4af8c55d2f63233192e05bae827de253f07ef551c81a3e75db3986e281619696da63b623e29edf998170f069f1c4702977d87d5914e6491d5876af99b0b70c778b53123fbcf92bf10030df768f6b40eaa62f06ac2450e8cfe45d57cacffba3f938c3152c3980fd178650d80532104b9e0f031d0e9fa15d679df8264762bacfd18c6839e77b8349b462f09ccfc92c01f5520694ff846cee53e587870710c32d99e25b2b11fcdcfa096520b1a244add0e6ecf2d69ac0c9f661efcc88b72ff8148e5c4669f8b9c7e6321580bdf1c857c0af522d1c14b86274fdccac009a83ccb0171e449af8451505f80d000eeac91ab84eb89f5827f0081a3ad92f5eeac3176e65d29ab4bc2e061a5305a049698c66c0ebdaa2b3605696fa1132f19737297404d2748eaa1bbcaa4547c2d0d22441a8a146fa6da58f08557df4fbcc7d227d5bde237ee5d4fba7baf132cac0e9f5d3db2ec2f5e42776b8d455ec93046ffe391fec70f3c327f09eaefda8799026dbccd6c442d31efb4e47488acdcf8b7cd15c35de959f181a083c0c02b759e07667805be536e47ed617a228dc9a57cc50c1b732ffd44b0d912845ff3835a0608ae492f7981a6ea57c20c310813f675056fde9bb1b96dde014acef3fdd137e20a961766d4410711df70bd5ac52f24138425d77358a3b43aaa5edcad2518dffd0c6bf1394b8432be78275acb5593582872617baffc7aea38dbbba3c9beb4dad706497932f99a04b978341b8e411abe55c1d675906db732e938b5476e4d94aa48081b2cbc664ac0ec7dadb6b1ba3e234672c8b5ff79f15e2804036a88f2545b70098946bf46e0bbc30c7bf95fc42d4fbc6b0acbf3139086cfb21dbaf85875d78502feed91b22662f5c8740e393e8bbe0bf3d3bbd44bfcb1fa0eb6de4e2780c33706346cded5a4368d68824ca575f98ef74369846de062c0fb855fa1758685758203f3e3604e55eab54d33d3e238d54238a7a3871e6309f0278b62ace6b8b9990408c977b6eeea0310dfd37bd1f3ef165340ec8bc0c962682ec53b66b615f72b16048537d80633ed368d483199d79100c10e1185a79c30afdb47e1d202a25655d004ada330ace94a5640419af934888f83e6a60bc88249d44a791589e41d9cf1170d2066d8dddb16be86c17b64de3276bfed708bd8824ec15408eb4fdb87d5f8a107305ff5d4c21ca34c26dee48f22d06998b03829d6fcea08a1fcdf0750bb2a510fdad8ff1429b48cc4f233422badf7eed7db6f04f32d711e77a4a1a21ff122430dffbe9577805a689770fad814643d398aa5c8f82799a1b45e2d001b8ffd0981004ed12065d46d0c4613844cbff8bee75c592e89f1b3da4383f8ba00ae6d28f60fe27054487ec5c8c1c0f351db0c0cd8bb7ed5c8d8140fa5781e62b8db65d2d803423488b01fd96996f66590941d325d4fa202b04c3f073f74c441533d16be1606afc3ee9824dada8d7f5b0d27b66d2f300d0396a1834f8f4d5f0f56ce61c4c50a6b12f9a2a4930f9cab4dc40d1976c727f8d785963711a5a7545149fbb7ce5b050ed4f6792acaa88f182a5f6dfbd3fb2c0d565e7ffb3827bf31a03d1ef7f6f900c7a83cb2be7d1c3c3d3bd3d5a4b68fc2d8727796572758fb1c53e082e3b60b0e8e829bc6916e2e31ef802e1228e5b095f9cde3d9cecede8d5c1e846a40e0650811f0d6b1668b97b9fa401a70efd3313e8a86cc47f0a4a6f090d99ed659b6800d7e657486f4aed9e00d05e643df3545855361b5c556f8cc13477f2925c1c8dc0af4c73a0432f416f04c0907b89f5fac84c1ec8cba4f3dfc58617dbcafb512d601282031f9badf79999bf015c30511cff54b12f6859b0c5f66ff0fb186a5c5d70ed0edcc5898e1124364fd41b1d513dbf5131ea81b7d27fb89e2a69b4b8ba35f0789d53d4a69a43885473a7d194ad09489592b194df1c93dc8419ca0313fbce20bceda08d246145d6b272f20a46434ed0e7a5ede9210599837b66a8df158ab690ba9d129288ece220a546a28e524a9c19331bee27c73e98873786044d9aa60460bd04e1adc411385b7a0894ef821068143d8cad40ec0cd901b514aee6d9b2fc401b83675f28c36a4b181958e46254059e0df2838f2a2975a18dce87a34ecdd100db72c4dcc5c9e09f7b57b707b91886ebfefb8c938928f8927268d50d61270720b476b046ba1023cad1a2e3b6643e1204a229d82077b5db9299722aed9496a970ca3bbcc18da52f0f56008df59948ffe24f9cb95a132e60a1ed30c4c952134a90fa130f15be236c08f7c612590ebe317bd911d71ab56e27f573eb22b66f3e3500431f87fde1d4b85a9e63dbbae60b8c4979608f669d367f382e523b8dcd1787b0235f9eaed9a352c3f1a95b8423696eea1115865794f0e68c869072c07518c9301187d9143dd7c9abe19ce1ce324fb321ce260e1365603f0a36ab0138d9c40b7035869608a42c31e3fbf9a53a17f3ba787a63dfe964b25fe1a7c242af20d65440d044da61d7b167467676cbc69174fb78c9ef1cb0fe813771ace6ef039fbf7b40cce1cef74723c9db28219cb0f609860e8fd6c0bc4dba65b9208969f6c7e665e045dba93fac738a92747a46a1562404f312e281d50c40ba0244e8f0ed23932140c50ed8ea343568a74d063bf7d8d02efc81a840a65595436952ef934a65443fb0a273454e67654237a99c75db85819442ebcb0e01ccf6bb4e7135882c796aff80b1b137e7f2e4e1e17c73fd95e40d8ba75484b5c9293f70e44b0f22c531af1f7036445cda03cf6e253a0179d4392669ff380ca9a632349e04613f5cc1f171e5008bacd84dc144ba4b3951ac8975498dc129f8c76155da3982e8bc1001a07ace3085c7320402035732865dea7630a66ffc43ee7a8ce372fe00a04c3ac557e86950bfc40e4a28a9aa2c83fc323cdfa3100570fe1c811e9cef28229b8c571e7fdb2033c0b580468a97e52a6450c469299870d80fcb8c22051bf15f225fbc254141608f4c0b673da5402d2619cd1e0114fbabf6997a9f111f7dc56482d398cdab907024fced55a0b847be69eff5d491eafcd34f1e29fec721375da9153dd3267ea9d0c +generate_ring_signature 6050b724afab4e33fc1adf423689add9c50984ef8df4b93034fe824ccbff6d6a d1588a8bc29b2df0d555b8b0632841b68cbbb4623ee615c9499862aa5b732cff 120 ef87037b6f29edae32f566f14ea179ecf470fbbcabf36b62d252b85af30f35e0 0adbf81b03429adb494d54bad3a726c919e81df09046e8f237eb9b5f82882a4c 3539a4bd1b04de484d125456d2135230caea0bcd47d76beea4d4521918f7e769 e01e642c44600e167c640013562d0d83d62e9eb696b70e9ca217725dd0b46ad4 9e748c90a8fc9e8839f770f94ed282e08b2d0319dc1287bb2fb3b240d84dd055 43001d2b2c696470582c98b0410879d5f5afd93d7bb90cccca625d289bc6a3a0 dbe579be20a891db9f42ecaa18e45ccbc4abd200ab7258131365a4d5c93e776c 69f3b6889f8d6d446374ae878b38da35741eaa4e4a88ef220d16163703c244d9 f8b561dded476626a7c0ba1adea2c1dfd69d96f0dfcace37406cb1bb25b19d14 881e289bc813db26f4a718a6d790d553c8edd952fdc6e70f1434e0cc81918098 0a56f9f412b6080c9bbd334a6a0ef92ae6a61b038d6a09b0b4f1fb38611bcd21 2b8c0b64bab89e760d5a74376f331ff45e5a5756619836c00ce5882b08e3c164 fb0b6c65d3823cffc4fd3fbdf0f7d867017a8c2d2070af7f69753836a10b3346 d58bf0a33db172ad6cd6460498f3fce85fb3a138694c3beb46d48bb5fec7fe26 0ab01cf3ce2a8d87dfbadb6477bcba61ef64981fea057acc295aec2dcc9ad983 3936b64dd7d013462f90d1969d8c601bf6a4f0bf45b443534eb2ccfb03998ede 412213951f3a59b88d161c057933de94085d7fff3fd1e370b22b16855b6df2bd 5d6a58959691c9c8a62c09bd015b84d4fd7a4c8ab3826100512dda43cbbb4ff9 7eb29652b5ed2b1080aca08c6eb2bf7a41e30df6c229fd56b5d5096905b2acba 732bddd78f3cfcfd342547d80cd2010ae931a76500c9fc073cc193a843839d02 e48eb1544264647008711c351e81c32f81e41f5a7c85a7141056c253e8e856e3 7288c441a034b859a5056dfe0fa9911c24775637c5f707c2eb28c90961747514 f06413f8ed307215eea8842d1f6aca79eb1846c21e1c4c243b665ba195a1781b 01b8892d2d57f8bf7cca182387a6d0872a92fb1a72df366d873307ea3927fa12 d729b986311d8c0da3ca87ac439c5fccad8128a5be990850a3f5820846147146 4b4d2bc6efb964a9581dd77f183a55567cfebf196353ded9b03bab6649ecd10f 4f12843900a11cb9802ad2474cfd41edd21405a2bdd740e9739c66a5bbe50223 d93acfa9256172f808b269ae6faf49ef9cc3c86026efba2002d2eb6a1da504bc b869d5d99868014123044ba4808bb55ff416a7590cc23bec4d46f7a517ba5b41 35b15cfed88fbc7ad9034d47394fae12fa8287dc371b78a7970c51c0697d483e 24c214c701ad51ac08e0a57a8da5c0459c2499910cf99a6a851cd435b47627ac a92fb963867312988139b9e3c29d2076d1a7f051513b6f2c3c24d74f377f2e37 7e4054200f5643f3437795f83bdcf506d97221196ea711b1d69afa5e4b345978 a9c74fd8309fffb5f589dd48f55e29653b8d0638473139eea572bcf4356bfa13 c8ffc26337fe41548090425edcfbb21989da5914326fa7ea776b9214252ec87e 3e25940afec823a365f77fb1f13aed2180167df85773ac52edc44f2346ff7c59 ddd96cfccaf4b991b9fd6f819ca1ecf8e0996e42862b9d0ee71f0a446620822a 50090e56c723895660648958c033bde07fddbbe89149b1a8c8656a6a35a4bbd5 74912093994ca326fb15659020de86bbb291c425b8937324a0070ca4c60a9165 68fd8c81aabe602c09f4b37046db3fd2c15267d974c3697c5f6f35f25efc8183 44f490d63003625443747a7243287d28b3f5fe4ec2a273131d8bc7e09c461d05 6359841de569f54123834720941eaaaaa59b8f9c2671fc7c83ded6f831e9fcc6 ea75a676b28568f3330e7d9421ea92f3ad3cb9ea67ca6a1662c45eadfb67182e a6c073f05c29b0ce08c57cf36d71490d5d40187d51d382e09fbbb2a54114a376 f4b02dbfd2253dbe13ada78a09b53ee799fd347705c07a9538cca79fe719845f 4535ea3b1f6aa90a6b98e0b940c164b31f4787b6a847758e88ffc7fee05d5cd6 8772138c613d9fc8f3f62a0373d7040616a372212328d0acdc333d62fabf9a40 a8f52f5a71eb7d703f5478f4f382b2cf3b0efd6538b24ecd632178dd432dae5a 97215a35ff24137d83c793bd9057ebe8ee7e310964ebc5d41eab6d15342479a7 e7ee87976fac8c0a016d83d26fcbaea4c18103ce971a1d695333632432cfa1ca 00d81de15264f2c13b567041ed3a8cd5331cec6305e4ce7772138be437d62437 d452fc10e867349bcd7f3a783012d1b4e26befbc821b29f8fda8cf5e5b8c9b03 96eee1a1c980b51b51a3e47cd893a4930acc2a61ecda8beda97ba416fdbd6f15 a828686eaf20007dd299f869ef52fae1785b0f25810a8b8ef5b47d2294b77349 bcd0fce555fc44ba05a71cde39e36de093d44149f803781cec70a1c040d4e114 e5d10dd7877e7feb14ff7ff3074abbce9b62b7845de46880a1a3e400269e23fb cf4cdb04cb0158c1397ebf413b9940424f4eefc3aeb315b134bf1c012de95886 e99a22d2d2d0bec068aec00b14213757b498140f6a637b3d14244f4a83391640 e1d337d2a691a786e85f0f2b6be24fbbc6e3b0916986de5551f2e04eab58bbcd 1dc8e63527bf9ab5699fe1b2e6d6aac976da2fe8c0f3ba390239067f19168d03 f32db1edd95b03ec7a6cc707383072b2f1d9bd7553ca3213a76246ed050e5ab6 9b6a260abe2e997d96e528bee60949c7c79d22e88e3f97ff9740a40c030e05d2 ba6364da3293a9a53a9068e15387444500d6d5a91c45760b30b900cc5a44cdb9 5739ad6c057d2c16e74312a7123fd6dbc46a1f34067f1a6d3f8751f801fad433 e6bab56a1a5f21bfb687e77f65d0ec0cc0063576708f02b204a6fe716f8db773 97407b2907cb431220e5553ee94eee4b92fcbd7d3d26f11e48399f5c9ee85be3 861f72244c53d41969ca914dd57e15b82158270e5f133876664d689d541ed165 0de2abf3741bc18b0f5937818f3a1e33523630ad8e1cda086b955c2b13c4817d cfc22f7d43b1effacd87521b5463772e8453a2798f2d48c2918d264f15c563b2 26c67ae82b3c310202c1908af303e69ca94c0ba83a8176d54d509781da183e86 75b7727257ace3fdaf28bd08606f4e8d471a805858a4de3af7ff1b295a8877b5 8cb68849c5752cc92d8c718605eb5390ba07013bab0659429981cb8249148107 10e3bc9b4705f0a5545deffd9d83f16268f4963d4d12225086432dce3f317d82 994e0e93851f6565314a797059f6a68746533f6d8afd7164538c279e3983bce0 0026807556960aa20611418df4ad89b46722bb4273305ab5beaee0518bf90b4a 5df227da750dc157f18d54abfe7ea27bb64eee746b41315c843b42ebb60a93af 6c3d71b333bd43ecfd9bed9e9c6a3438ee386283c9de2bf1d9cc6fe63eb3c7a9 5862c6323eeaa240cbc96df54754b7f9b38a982646dd58b29b7674fe280afa47 e59d1cbd91f6a1da83f811068d2c32d3a4dce1cc87e12dc670b3c8f4cd837668 e01b48407c6cc98e591279d215168d45f8a9b0bdce74294c863520a96c1f3578 d123f51fc47d4a4a23d2589b3ea6afacac1523ae68c9c3e987efde55960da989 b24cbf3af9e8598a861c21eb667e305b275091ff54d619de94427c4e8107b4b1 ce5ece118e978a798e3bec113fcb05480ca08761b12f4d82de6aa20c351dfd4a 881e216b3ed4aef9321ea28d6f31d565356bfc4b1df72b877881273b55de257c f086d77f57298e4001ff99f30c1ce019cd81a74fb77dc8d3b5e286a972b52b78 22359fd9b4985eb46e587cf19bb5094ff334ea115c12eedc31b14a0c1a883306 f4ca0ca1c5e64f8143edccbe63ce32279457871600f83f726706a37274f0dd82 2aab22b4fd2e8d361accdb45a9035695c109d53d69ca0bc4b6cbec1c5a5f73e0 d4b27fc292dfe7d49fa7cf49901503f5c6fa53812247a99d09fe2b540c1393f4 136c4bcec68a9a9d675f590e35ed6a9e51a85be74c269c66345265620f5f196e 2903ea5a2ff85ea2f92a411467005858b3a315b6b6b9c7a7b406fcf2906a69cb e86aae3208c02700d40ea0c63ec911ad32aa67fb4d5c7b055ea2161a877cced9 c2523fc728db90b1111de2a58e424cabb7dc53810094cbfed40e0f1899b31cf0 239b2dd969fdffe6ae68c2a227fd066b0d08fa305c8daa807db24925d2a7ba90 2d64e595338b931b929125659133566e25fc56874ef3eae77760e289a4e12227 64ccdc4872ce46da3421e9404faf86eb8abd0046239c71b8fcd9f2b7e1fa88bd 85c0e8646ac761449a58f53106cf8406af576c36b70c76410a038d36762d4abc ea4dbf46f8653be48bd964ccc8171a87d06c3ee1874eea739df7de9c8f06553f 31ef879b28137a2252476dc796f988465ef7640224a5d9b8b75bb890e8a8544a ab061319d1d6fb3a9cc5c2cde8201a208b82e5eea782b88ee93aad24785c2d6a 43d69deec91854813f55eff328db76ca3f1973eb498eb0f1951a15c806b8d9e9 f9f07b9a4f5969d9ced711bc842836faf8049d5feb3f4cbcf094089d0213a079 efb00a7103ff7a73f36a3b7daad7efbd296feae5fbcfc8aea798a5921fd4982c 9ea8ac862144b79a9715c672a206686f4ba6790c08159746b2707a8f20845b44 22f9d130216831692a2fb6be1a5956179794cf0d3ab5240aba781add81c8eb7a 2e43959ec556d13a5e30403182b5d786a6c90b1271333bfd306b15525c3838cd 7dd14d4250642888be66c827013a345b63ef223c79e5410d86fedbe880f49511 075559e15ccdaa2be0d98e6a4e9248189168612117f1161da8523fcdbf62e725 c071bc9b096c8e911e9f6a4b05726b4bd8cd54ee358d611c0bf0caf467764aac 82126c2b00980c6bbca68944706644332100586eae865a99391c3a99fe206209 955eea558bb0799db9ee5994a88e8d323e27a63075def3cd6d16a2ed82359240 23a79218069842b0364c4ca540ca80374492d6d3bdb578515eb5c62935e5cdec 8c107f0b24dcb8d4d5bd749b90782be058a6665c71e09052e6521a5a23148ecf e55ae85f46ff1d248b12fb35c35df934f4da4865c9969504dfe03eac92112b11 9d4c8f0d6c14ccc7a9c9438cd11224344b31f50c9231fab574d3923bd2ab6276 6475135f9644ca979ce611a47ce21f55f16da6edc548a88b7f1f2d5cb828abab 3f78644f896131d3080551c3b5dc669884f66a90069fc02d0980c8cba93dab5d 032cb098c9caae12e929023e907322382e07864e0e03891710413bc9baa6f628 03f519eb0e73ebfdfc8791829aacaa3c10fbf4a69505e658abf61ee87b6d101f 8dccaddabddeba280e5df36ba751d672ed25f786c12a8c725f972b6f11e3c638 e567bc025376197432a4459ab346d82730c42b604ed31e27b6f16fdc61351b01 16 afc991254c1f0a3923d5fb8d63e1dfd41ea1fa27c0b3c400335758880c55ae0ae4cd55795145706c3ec04a3bc87eb8e46858fc600cc6415539ae986a60adfd024fd93d63e1f3fc5d32e5bccd21630aa700aee62f7b1863b468e33243a7fe2c0be096261e629dd6a788fbc4b3c2474d444c0e2a3353a3193d8a66c73806abc60f224ae8f031adfbb93fc59d36bff07adc81a53a0bfcaca429584080d8627b4f0cd66dd0556186af14de6c18456c7ed31c4e3cdddec608b3c37cf182bf885e910237f2281ef98c41e0b54120c5cd0a71a6aa89c3a007b02b3165b23ec22d22c5032d0a64fa68b4d7f79d927e10c056a5762ce492817547594a8b12c81ffdcb3408a0ce9459ae1f59f6ceeaf05d5907accffcb926b8bd89fa082bb2a0636ab6f20dfc7b22627300cd52fab7f48bf5ec2ec66a97ddce816bacccc21cca27fb26fe03bd234330960b687fefe0fec7d80cd0f3a2ed84c61825ef3f7816768797aa7a021c76b6c42ea236fd4c50d80b4254f472079bf1009cfb106b10b69b79480beb03572ce46761b729efef9374690df2c6d274d74b122bb8aaf2c8653cb1cdc3b4045369edb043d9a4b9f21dd2625de372c74d21395df7753e97f910459e6064d20021216ac20a1dfb58a0e3abb7db16dd001609ba833ff20f63ca6aa0fe221db305cde31f51fe24c0d049045dfe7669216576a9a1fa9e5a266093264c39dcacaa0a5bff93c4fe782c44149171f03b3265f9011bff9888081b8537baa41b436c180f21532ef05670c2ab267367369443630c67a61ebceda6534ad52e06e2a5dd560215db5849fe97ebc71214e896d017ffc327593fa538cfd85a80fef032ee8e7d09009f066ce7993dac042d11fe630da9b36a570bd13c7a9069ea3f8da33fa4a50fc1e5c215d8ef5a08c1c3f7bcb3604a86062f52792f71e17b6df3f95b116bc60fe793674c39e9613e6fd530678decc35f6bd0c534ea90eb024826086e8be69005e26970fe98f04dda93d17ffd17af637a780d44a0d414aeee146b7cc03ff14f06235146d4d9fe85cff4442743bb3bcbde3e313caf7c6fe38f496411e1ec9b2009b28c043af665bb91f4d67c0a23a55ed28cacc344b57c7b93f1f3220592b07e08282790a2f4d4da49d5f393dc6374f3de747d6764d066a401be349994bf99c90a18a739574b1ab0727fd79a09e11e33e5b1c53eaa17c97965dcd5078b9ddb2a0bb380cd9faf8e2e5fbfaa036dab63b132d5618efce5bd8d8738b313dd91086006a809d52e85800e9f098afce1955ddb561ee71e4e966789b8de8ca46921b41a0e80095d88ca7b7957825e7d0a5313f383f9b0168e675f6767dd9cde055576190202db994c851ea2a5e7f5b05dff3828b994a6fda52928ed1e1709506a4762280d983941fee08ccfee9da56404c73cef174a122c468aa7277f31cbcf20170bf70eb7d0dabc06a7c96af13df7fe2cf73c0007e26034b964e3d71071fe2827548d09dc89de2ccd7846faa236b12ca3feaf181b199c391a9d4f658e7a836b4057800ca99342bd100c5100c958ab9b986c6a5eb926dfc30dfc7e4afa8ea4d1a5b3850e6e0cb85aaa97374ef0ac6b863ca699040520edb93fd26f3e1e2d5e9153ef4605210a626d5a6d87e27e76de71b1b6795439e020dfe0e1318dffa1f912f6e9490fbcc0a3c3715a06e43a70da3a46511212fc94f32e9224697df4bfd023f334110388d86bbd40fa09a1c0ea819a617a294f6d7234ff959fe6e4b6f34b85d02da709375a67fe558ffe4340d5a44606fd206a287e2812abf1f30aee96d3940d0f9c02dc044eb15670fbad317a2801a38ae9b572c31f5409ef67f20955dd747fd021059403b3417ca0b6fe1d6656848dd23be31e945a4fc864d3064151bcfcd4df5f02e8b7519886fc6d990d08f923e712d3e971f590ea0cda3d4194fab45012c4cf06fb090b6b6bbebd887782d56cdf7e92f6a42bc8defaef6a7879594e1b4b9d1f06deb368ed5576ab75ed61eb9ba4927195c683184c9bf52cd4704ef11c31fd7b073cb1148f263d70d0c20c79b624de1696b13376badc92eaa859e8b59aca7fd6067d6a310064c6369df31ef634bb0832d1655a5cc7d8b4def43d7670148bf05f030248b0c189466b497d6474f421317b520d5bab86af02733210a0293d5dad20089e4fe71dcb644146747adf422d8f279d2508dd1781909ae042799798d3c7db004667384b154f3891ca8a155e41837ad53088b0ff8e9e8e63b7a00a5ec26c910dd7e213093cb5f9d65578a11e909f26d9c21108da3b07fba09ed7b57d69f34a0b5a56ebcc650f624477c4994ac53b9e328394265e72b612184be8bdd57fbb7c09cc31440d264eb63767ce0383dbb6ca2aadff4fa245fce1b956b01015e5bf51012c15c3622ee553b32696b78fd789ae40f50bfdaed583c129b7cd5d098a3f6f01e1a6e1272ad7e47a2f9253a313655b409bd9182992010f5454f20b07014ede016d4bfd954dc5e98462bc560e4233b71a35cda8bb502ef582e09971389375720c3c730af280c9c3d1ca4f960240dd1424b1ff29b82363a6d81d93c4561e69d00cb32b005ae3800f2e90b1495dbb31b609d784928d1ca9a1817ad515d640651e020404da4123c876e291a61012a3d7fcc4b3c257ed1c3c36e368a7316b6fff840ba7526f19dee288d1fabd20a86e426c1b94812f1501dae2e3143e5a723b7a59068f42717f3c53bb509822a200c026caddda75b5f45611ae0427c68f0883063c021a084c394fe33e5fbf73c3eb4d7ede5137fd1a3dceacde6f049ea2826b7b2f09dc2cbb78d55a0faa675465e28c01a0eced7914b700de2470e805cf23ff350302567db7f518aaf8a1190cfe2207175fd6b475bacb7e7e27c851c9e7f9d702b20fc934bb7992540f75d7e5f43d9a48a0ed0f554ccf4e86f5f9cc3f92b8d725e405dd47a0129226965b6c127007718e78366d1c19d3d7fa4e9afdeed5ba822ec90cae5faa72a5f69a6d360970d76ac37fc458cad791278ef874208ed91ff4c2c50c8897b25d9ccfc208245cd5b4e0f428f5e49d8286c2f862ecc2674cbae2a29903152a389262076882bb2a227ed5a3a14a7e09d9a291da498cd7e8295c77b66c022d2319a3354c453ec3ed86413fb267e2b8cd3ea7b9c69a898423544537e20800e48e8f72b7e7ce02aac099db89fd629e0bfd312d1b06eefe3a15648654ffc20c74513478a8ab14e76eae374162da10679c907f482fa4a9bb1714921a457039020a651493e796719ce61bdc19eb2ee5968655dd6698919e169f595f76296d4001d8307bce3b2abe36c4b630eb7e21a4c3c47e3dfa8dbc82d2c10288aa750be10ce9e247e5037c4c21893db408c5daf1026cba8012c8a717f4ba6346bc58dfe102fd2dc28e50207139418c6171f130aca3aaad5069b2de5ee91cae621bdbe1a60dab4c22fa1ba8353c9bdbd2987b021b55a97e45a52f7c391849d8387459dfcc01424fe127902932560127aa7b853a31bcc3148aa9686dd54a6781be7e92744209d337f929a0a5e66083bc5739f56537663a0311163eb1a6be7b81f1681dad45006ce86e013b66909e5be7f487f0d249e70a8e272dd4ff2b25e473ee8cb7596402b4b2cc077fef5bec3c816552f8651ad1f29196b6ba7fe202cb4c43d5055d4209482c2b19d03b179f9a40fbab0f364ade937c47375151805bc2bcead236c8cd0681d6a8285e4e88864975b358c4164c2ef6da9d4441444ee25e2d23da9847820ccd4dcb064d69c5bcbcf23b5823c423f0f8a4f97ce2405705e848dbd67dfd1d0f9d79542127e6cafa454b807db865c810fb6734248ef1d12d7fe79495b5be7602d901b23f20ebb0b997fd22d581c28c92207b56966f84f8560b8a2f2188b7db063c903fc578c2e36f6ea58399153575961df232839b30ec1ec2d9327b5a91a80f255bc0e671d85a3edc916009e94a362ad8f57fb9d9e8039a53ef7e6033ba9407685dee8de695a409352bb98e09958c0a78728732884f4503c891a7ba224c6d0a488425e05265d745f55c30dd2a67ffd410a9ff3b4ecf38f422f8faf9fbc6ab0413d489af4a20a553f0fd9ad69cac2d09f21eb74256a90d4061d6f4d33468ee007491c9201545b58dbf7c6e630790946c1b5fbe127f36982e081052a1e0f54c0c52facab270b821cdae76c49715bddf5a1bc27f9ba3f3370c838f0cd280807f003496f81b9171ac3672158e9cb9cc23a03a260f2482538e7c243063a4bacb3f00f364faf3ff9410c8f88a1ba981a37d17595fdbd2a965907aa91269b58f933d01ec2726f38973aa54247d11781a0f34b4cd893fb278c2a98c37515b8fcb74990dcd01bed6c0808b2c4cbefa9e7ea7ad06f6e7653a74cf357e5bc18e5de7363e00f9f27fa65cd15840ef409b87df54f208f00758f7113e53570bb3f151ea33100ffa86a9ccd268422c4acadd62909f11f49f9dc60ae24f97d511a80e7040aa7502f1eff2615252a09a5310cec8afe79c35074e8d8c425cb509041bb165edaa9a0b85e8bc589536783a9f0b39cb2c3efb983cfe09603e7b518d2feed707a525e300b48adbd37cdb77846747a84e3566fa5cf06354a9cf189b20ed289bc3edff2f041e8e0ac5d81f24bfebd536b90432c4ad2e4e275fa9f8167289796208be628204dbbf38ec58cd78baae7943acb57b843ed0af82ddf40ed88edcd569074256d00f73253fae72d8da95ae49e3c3e6b6380c7a24260a9562a03c0699a6907fbb9e0c4b1103b503d8e33a13754eaf9e133282c737fdf59b0584b6ae7741424a3626029b705618d345d6caa6c85f3905d8e0b786cb40fd5df8c7fac652487fd88bd40053647419e772f38176af89a43482a0f7ea57300b047581b22600e1c7d960bd0b530bfc4bb4fc0c999e6c01b60bb9db93d813fe69472e06e6d4d30229f761650d37b1a08ff1d6b06cc3e30ec9b712b15a85b0d403c2f67f2d185159c87abb0d0577071d668fc7458d0b9c87b893eb000b5e36b4a8897878f59afd3c718db6b60838abe11728c29fe9ca7438b5ccf8db1cf472a7437bf3bf7156f033cd238b7206e428b653399946d64fe8253298e0bb825ab9420b91bd6fa03a2e05f3fcc4bd09621d40ab275f971b3d559ba4b7f6d9a41ee00652e63d0e1a55aac6fc5f454d0de9fa69b91333924270d2403b42b3cd6c4f2ea20a5e895b46152b2d6c463bd60d903d692db29fa7ef438a8921d05b1ca38a650e3bd91a78ed66c96de3176cb60daa8b9ccc2953e59aec1925d9f51e1671fb6c40454d0e3d1c4e122849be16920ee7b9e5b541643b577a83788b74161bf9909ed6e72f767925aef9e07826e7b7003a100c97416d39ff3465a92b4582c3050f3988756c80160b7efc697f1da042026e985495f173066f69bb1bde2c413c010b9510cc1a2e8b6a85f3983e443fba0c1334bba7df3a859af711978d365c0b03cd8e5eb8a92bfc52f0c5cc931306370038d5ac79d9b0357de65b1789113d65b1f53a30241fd4397fd32e16f3031c590a3a3c94c75758f19b9dfc25851d148413316d822eac18fd698dd2887e5cfc6e064ea07a82c35b7d5b30bff932fa089105e8e4b1cfc41f4653bdb5d992404c7005abb38828b80c5abaef84d8b5907e55166b02875600788c11adf4a674f8104c000922af0d0f735a179e92d39d3422c6f8b4ce3c8aa1f99bf320c1960a38cf840901f26a631433301d1f2c8aff8907fb860a3326b8ffc816d3a71af8f7ba93dd0251bcbb26367890002ebbb81eed772790d554525bbe6ac2aa5e717ad5e56ea5034d573c49e9b0fffe2468e5f410e84582dd360d4ec50e29955121db4844389c075198aa1fb3b8dc7a43fbb1eb9811dfaa344f1eaf21d2e037b3769675562ae00899064076cdebf34fef46ceceadef79acfe34b1b170282d8f26208d84f125f70c6d88114cbd2763289ce31cf7b5ffc8dbc523fd10426b68aad03cc34bb91d020c722bb047814ed3f2d07d748f112a8e085290e21d1e56e809f330717b0e7bee09827991a35d04dcc9bab009da86c1c3a3b98d237771ade3a79d7bebca3678f80bbba4ce3e2a349bb6c9735235fe88658426c46b464ab5db2a70783287b2fc7307390d9ef9cfeaf1732aa297de775f22ed56c4e8547e66f55617654716a652fe0d066306458d6aaacdad6c9d0c9e91986e4022bb310dce8d3e3d649915556f63042f0bdcdb49f62fb0be06df83aafd3cce1288caac6d471ab3b2fd14e40b947207cba62596b423d7839292f383b1c6b32cfeedd33e83095213c466e48836f12c0377e0c6c58ce67d266b33a8496e237da5c7ab2f5816327b9b65a09315ef4846006d26e1f407494b9a28c887e351fde02141bd93355ecc9a35e8485b3358c8c50757b56d582157cac6bbe507956b8a249519adafcc9249153489e9664094122d08554e98cd6b0a0c236126c8b30430522e94268d57b92d19e0f1626eaeb88f180c47381f988477ee7ebf4fe78b56c6b44053cd10ae8c0fff2cb462396a5832a403a701da48f0b1c8ae3120e35f13973e9ba93ef1be1fc2138ba0fa60d8b3db6f0ce006234c894491cda1992b85219f7c44ff7f9f8170b49effa4dc8f3d2f200308b06d8c6e9517ebae3318113e8ea894b19b73c1ea3c5a73db5af999a012651f008fd4cae064b076b0bbbd04f501e8e2afd6ac0b7308aadd18b37cb86d04e1b606c8cb1d27040522b195d3718cfc9fc81acdb4f0390db61f2ae62a852b84b6db0bdf29c7cf92449afe6854fa0f2a9bfcd27daa41f061d881ec0dcda6bca5136c01a2407deb420b56f4a5e299c116ad6b28abfcf7b1b93a6dfc7146a29665a89b0268993a701d4bfe97ac264fd4c8c1fbf5b08520e801bd433ee3291868d40bad07fb00f5fc513485c6fb7867dfb34d5bce47cffa3c890223a8589ab8a104d90b06adba2d7e86265e0b9ab00a26ccf55616c92d0fac50a241fb8f09c3707955be080a81923f3a3e333cf7fd98156cd6023ba2fe981603a7dd3e324dc2d8e1a8490fca9c589e532b2bf928bd54268149f09c210431b1c719fcf2785517d86348da0103ec98a9944b92546f4ee0d4982c8356b4c58d72086f60438d8615db634dd80bff4209c07add031c5ef00e238c17e36717cc636c787c6ce609946ce21f62530f9ca43317fb531c1c050654cf901aab3bab440b05a50acdb96013038bd2cb5b032c75b8cefb1ed5a28c989efcf0c8fd2d27900c17bb3bc87770d1132a1f74b80c4b691bad4d3e2e165d35e9e53b8ceb891ad903f030777b484ea7cfaf0a18ed0a510e1f7b2c3dd46bd292d29127ad191466cd0f3595ff9a0979bd4af445cc7a0d4d9411617c2bc0efcf025c13386eb7c0167bd90f5c6e1dfcef9c3a028888970786be01ad670910470c4537a05b25d5fae80f1e8e5fd544fc151f79babdf43f0f6f20db2885a2eb017bebc02a2080e25655ce8d63caaca184a6cde01c9273d00e34c7783cad2a21f57cd423393a587df3f7ae9a191b7fd3998c2859723ca428073439f8b6ef77f7f3e6bc894712e595595c6a9362b9fb159151310237e8c2a80a771bea5352efcb34d37f9cc378d71061d605239be13d53aefdf15eec2101950d60a78109c64c8ad13096d074d9e8c571e374d456e638fb31c700ab64d7fba305c85d95cfbb18f2e364027a26b24d013209a0ed549a74083f0ac2128b4be294051675448af081286cc57f185def44fa5cc92518bcf26240b6623de279d1575f0f7d2ad23dc8bf69b7f3e13fc28b8a497f51eec09f311dd0521e4f33b722e1bc0025800a3fc6ab930d32aa4b4dd7c514425143f3690781eef513557d1058c2f0055693aaa2ffe77332bde0a50c175324047770da0243d87015667a58d48fbfa403c5004f7e538fe639bb0cca21f3ac21b63194bdd7a991b8697d02ae3de88acc0bdf021483c58dbc328c607c474eb638b234768e9c6092cc5b44c4b6b41a99fa0bea35e53be5f1f925dc09b520037a14b4d85fb163f59a8e3a9217c16b8edd0509887df1fcf3c6d2d2569702be90958c8a5f2cb2e8d5e177a48ae46a1026088709aa3aa5aa0e8f8b9bd41aec8ea33bf0de416b91a6833c9e6794840aa2d59fbe071ae86a3e884002f7ca2480f2ccd0c8d1a50343a61eeb649f30f79c2a5039fb0f4ea7bd1f1b633cdb06df788387ee870b2747bb74405fce0e7e43bc89a432b800f3ff885a43f2898ff2e9dac151ecc8e1fef69fc3357ed8af8f5594064e3dd20cf888a2239fe4b31c5d835d6729716171924a171ebab8d6edf94afeddd700c403626e5d6150848529423071b15abd0b642746d5946de05a0db3142ce6683dea037e454e9814b67280ced1cfac9c2ba145a600bafff7fb96cee795ad5e910f3a0aafb5b426bf0faeba4baf5d625d0095e3f5844694129c43fae7724afdd84ac00f10c9bf35aa1feeca09ca43787593efc6f3e9ecce378e084a7359c564b1a66b0c23f83f0d4567d5d6feb806bc689f9e105a4a501b5f2db7fba02eb37a435c76042397c92ea3c0a60b596766eff99eb82a83fb0543ee2ac766c6680aed4808c90bda954de7e4b897cf7d937b2226ce78f5fe6486ea9dd0ff7c66cb6060cf638d0bf3157b43ecbe6a28cb8397f62bb861a605b168bd4722da0490d4f0bc31aafe02d67bfd2cea74e01500943382551189d50f0c15039545e24b7d8a5305e5fe530efd5f7a275dd7a474642724cd319a5aa33ffe0e5924900d66edba8b86cb0a18031095f6c22b983d91cf022de992be4972b987996e4b347d4a4ea8457298179c0aa96c79efa689232d28c83364226cd8fd14db16792c9968f8632b8530b87f030b2e7185fae3a4219f0e552585c5a74542b8c41ac2c5a34e4fa3d19dd5c8288d0ee06f56d6f72ec343ef5de521d8e75f2aaee739e5b56ba3e6d9a89b37fb9e800a6848d3720be1f6adb4070106eccb16e6c86a3fc2e18af2bc3b2ec83a737ff80527335dcd593839163840cd451c94383eb1b7779dca10cda136387e5768570c0ea0c6242630b25dae727ced70f35e9385f6f78aaf65743d4c0aea6045bd59b004bc76261c573629f23e889f4be4c50c2dd6bd42e8e5b34f5ef5938203ad28a10b80706883cff45a206d00a9ab21a3b784fcc4517753ae2f1e2fb1a364ed33d10d6b99dbd07000bb490d08039c7d3a0dbb9763a4158cea2cd7c5eb24bb57570f0163a7f01a9534bff3d6c9fa1e4c82c4440277a9ba25adf7f6c9260be16247790cb65d629c89757a353935c999c51fd8461e44ceab8495d75504a54c71d09d1f00097fd82a0d9b3b115759d1f0fd76ec9d452779316ca09c158418be280154710968597ac3fa1f7d8dbeb521691d9b993f7d70aef2b1d06a48cd20fb8c57d60904d853824dce3011bfbc07878aff4dddd8d6c0c6462287aa2cd128821a547c4e0ee3cc7b6f4c629f06d532df3cbf76ef4612158f3e19d1c9db7a1ca6a32aef320964f498a1b4ac7eb1527e3944c04fd472383129737d425522cfad128c98985903f1d3cdd52aca595d2b9e54b5eaf58ea97cdb1ecaa2b22270a7127ffd96163805801ad6ddc2346f146ff99d4d160f0cf8f933c715263c69387c695b28aa6ee2043a0b4239df708840080ad08e5ae177340fcf91420a1f0f6a8195f3b2fcc79f064de6b2503105c98a69b64b512b3e2e4179af1ace089f135d7cf06c5424e34f0fc8c67d10648589fb37c31e151b18bb121dc40cea2abc88e21cf512a411f46105083599f08f8ade88564cea215ac27f4592a556cde38d407972baa5d8e21f5a0df73e2d0943a79640b004d6eb1b29072106613d4ba8b5af8c10af96ad2059f4027bb5be10aa600b16b77c8628db747cd898a11f017f9badbf8db345323a67f20a6bc8d24ce2480a8469ae192b12da5efcf79eb97ebb627ba0e69c35371d0be40b03d2a10d9d91829b145797b42a9b4fabdb6623ac2e2d1faed5b48ffc8964a6021b2ed8a0bf6d39a217bc47e2ec052639b96529ca89891a0b0977417d0d0bfb02b0372195543e6be33359528cee86f5738ca51db216599280a8d81cb54a5be80031343edc6af0c80fe21e32907c8350e166cf0ec7611c58beb253e231d88d7c0930b87e9bf63dc74c60fbfb75566b9a56cdcaf12fa2bd2fd2c03ab3925b31b50e633377272ac262eb63ca1d9d5bd3f5e455d4752db11ccca6ce28e153b75c2b0fbb8be1083ed1d220c92ddc2be37f8706f8231cd194df98033fcfd64ad59565082c286bd5ac7fcd1712fc5357e05cc0508e4f9f24a810931bd6e7e4ca8b7225040cac9f93b1b87a8a703466a09001ecfe2228e2fef0da29e33c131122f167870a4b21a47649e3db8c415c7dc82c3e601f1faa3af3789b3e9a71c7a72a01425f0eaa520a6b751a71d5f0ff7f0287a9522f6471faa5abf93570d09e65b7d1782806d8160db43c7fbb5af25b04ed2fe7746fecca5eedecd66b51ecfed54f62852b0a9b84960fe643835c6918c0744a248f8cd9551f712021d6af55ce62135e314a002a7fe8f6c00db3f6b91cdfd9f0d4b00b931e6f419bb656dcbbfaac2ba876460f3127543a4bf2fb4a74afc81a30744a9f4b72eabe45652970c11379c995023203bda96f2936c21fcb7ebeacd50a4a0427f2bbdd8f0724260f59e2460ac9f31c0e879cc29ec9da960488c964a8e28ab4a0484d8110137ccd30832a9644f8a3a8080bdad28be182a714fb0f8a13fbe7e200826e44899d10e9c4193d3192d605be03edbb18fde21d8f7ee69dae1ce5aae5949451c0f5efafa900a28959cb7bf2c00df4decbc27fcbc9fc884a7e33c33d2e999fd6c25c111fe0fa1ad6464315276a0488e1e906792f06cf78dbfab354541314abab22aa4af14d49320f196bf1dcca02 +generate_ring_signature 9dd907994196494b8125e30882384a88166cc89ab3d0c91f6fde9e8ecefb5e01 90dbda83e0a10b0070bde90aec62e4dc65910a922c21ce75f37c425cba693876 1 b8780402a1279c031fd5dd4cb89f48bfd15b5a8dfc140d9fe0eae67a51d18df5 5631b1eed32e0c03cc92739379fefaffc2389bc6059c623a2df77b13f41e3209 0 686fd73d1d2f2cdbd77e043143b03a30696f1e4ee3b1f3577787439e7678b40d8236294b31e3213ead0e10a46aac08d2917a80dab82936497324bc9ad57f6704 +generate_ring_signature dc57ae672f6691bb5adeef2f47e8eb0cf0c5af0d06d479a1b1294e5b3d41c06d 3025fa55a862d8127dd4c6c3d757a2e555e7d50356c3028f6a0027aa786c751b 2 e6ca090d9d3cad0b144faed63a19b9f263b793259f0a84d3465d9567893893f1 ae20f4f7f9038da72a39b4618b1a9032d77bf1787ae00632e19b50051db8de04 0c51d9cdc712d60c0fa724e226ba82e337451ff47b69da5acf95fd41db1d8c09 0 0aaba160dd5e2aed6ade146e31a374725bf9c99fd8aa8b4e20f53a0e9f54180c78bcb635cab685bfc0aad9abc67766f894b0f34f1b0b9cf12b09cac74b541507793e60045a0e7c7336eba35db6e1597ec5809ade6e1a043bad9713c20f549b04e38197c186524f08eee4faf1d1e8968c4a6c5ed4b2c87a178e64e496d77f4408 +generate_ring_signature 2e3eccf3241ad759efd0759054ad22f235e33e882f8b3a0b8dcc2bad44cc13b0 46083a5c582071ccf8cd1c9f33bddd7b02328a1d68cef46e82f41baa171a2fd2 2 ea16dfeebbb925edc360cc2034ed4a7c45dbd9a2ac5c2699cea5d822a31b6187 64c01e0efec188fdb60bbf359b073f613efa84fb51d764cc716938a6e3cbbac7 1742543972b8075a2df32275289d6be641db11e78bd88f72215a5d2376203901 1 55b2967846296961ff4d19da5b4d269a2fbd52060ca13cfaf5618ee600b1ea08c6718b761eca00d1bc84320e14d81371112ef60045ed6eeee7e0515dea378e0d22792eeb413da87c423a6813990662402a40ef04b86b576bdab40c0127c7710258dc1ceb280148757bd82d2c8e2aa629008619966b20725defe80d78e4653c00 +generate_ring_signature 5324c8a8ad37bb76e86edb6127499fcbf161e173ec4cba0d426e25a26aa7c2e5 a3d91b89186b32fb14d79601878d9b5cf0289b0ac68d19b474c86f4288c8422f 1 ab633b5c1c80ad6bf183d24f4f37eaba1c4776e8383047290503e920ccf3ef59 a7afdd093c3a4e7f64896c7fee5b6db5f6a57a6f14c04f48df5c4a4a1219cb06 0 37182c23e8e5ba154766f587ccf563244ac14bcfb8cb75a0ea0c0c022543b10f4076babdf429fe04a17cba13b500fa6ba72ddd63770dfc622ed648a446ce3c0b +generate_ring_signature 9ac64eb93443ad9fbc9abc23cd255253d3f61fbebe818fb9436f2f12397ab717 0d24970317c011442316da2d75ce0bb19dbe3c33ce6efe6495dd185ff0b77a26 3 29316431df8bca23ff0440601e07bac7ca40edc4fb0f948d1cb5a4e62153a1b1 3af36e933fd1d37481499f583155fedbec8bd3e131d278a28b209d5d6938ed7f 5ade6caeaaf734a3f6435719f88871b4094ece978b37ddf6b0554aba023ad626 94c2c27e71b84624c2f55761fd6b36a2f0f835a67fd12bf7b892dfae86c03d0f 2 fb851cd56a21494805c54cf6b839eb7b4be17334385ee614a90876d4dbd84e03f3e23ffe0d26aef43be2d0b475f864df3c2cd9e291b279728d3bd2a6970a6c00c9089ae1c90f5d9b8f66281e981c67c69f915e3c24d45dc0b05106042e07130690bff522ba91492aa07256ccbcd8b880d86424dacba5b69fa8dc403c1c527b0b5cd298bee50a5ae03ca5cee52f3ffc82010f53380568103286ce5ec88bbf190face288fa5d37b0684b391dfc22b9935dcfe669a968028fe5ae2e3f5de050410e +generate_ring_signature 733c77641afbcb27266517b8f24262be24609a4729609ac2bec71b4aa86f67cd c2474848d390a19e55e10d80834f6426d2421d3c0d9348e81312b9c644072e79 2 5eadf01c62bb7ce2e1cdf66e49982fccd9965769d41846e1526cb9eaf2d15395 ba67c3a77e88593f8ecf2c137cb0fac4ce7d2259010854396991fcbefde294f5 c3c9808a7c62b5542b5c054bc98c5f140820efb26aa6adc2f48f4d0550536b08 0 f6b8f49f5e6e0282ec410cbc1146d872104af9d277a542821068426734e1a80e9aa080bb86d922a2abe26cae68f82ef996a4e7cea62f0d95cea63b662b2082075c73b2a96346cd8e12ea502d2de1010afe37a84921aac4c95ea197437e7f8d03687d79d057dbd907fc253419bfd9e93a1621cceb02389c9f017e3847e56ba903 +generate_ring_signature 1792c680135fb955b884861240d9c55b6ceae3a130059fe1b3ed2e9ad5a9de97 ba66d0c9bddf46eca50a3205269fd2c427e40a9ca780d09d68b953511561b396 5 bbbc21787e57aaf4928a2de53ad1d1c7527586f1f8337b1f3f8d4d37fecb35ff b00f959de68120641ae043cc9640ea2a5fa040ee950858c1e9f16b427128a212 c5d5354bfad3251768658323c092fceb2918d8a700557c79ea5fa638c18fe516 e731fbb5657553ddf378c3d60e0b12b1a5e375ba1154c6b79ad6d276e68f75ea 1936e101b0e7612be29fadb4d8a17e3dc20c44a7f065fcccb1f372bd8e3b0c19 747205001f75da90ded4f907a518903090940432f8959d8ddf25413fa3381304 0 887297c00cd126e70eb70dce237b24e4d082f2579c28a7e08c8b524e8f3e240fc969f69ffbd359a6f070cfd2e57d127bf16a324d53b1fd758a9f0842b760e205beed00c63713327f01cf4118e067f6e8da3812b2426f37a8aec32f9e059d8b0dfb7bb054165b6817880153c8634d9c4e44e7a2d63080009636cac2b4a91ef80d4df8d42fa575ef8d2f8791c00868d126a13611e2e723f2de9e0a65c52938ff04a54f81690e76e29f41c629d02cea978cd1bb318813524db6cfff08f25592da08866695532e0475f2cc9766fd13220c7ce2bc3cd953bf867e5947f0ddbd33760bcba6f9bc070fc52f371cb4a7cefe0cc2eabc0f59a26faf80c2592adeaa367b0648693b7d5f23623a820c98d527a2d6e911251d0f23f8ca8eab944be874211b0e787bcfb21b6b448a1139bef04470c9b975e6c6b52ba2ea384f2de51ccbbe8704 +generate_ring_signature d432ceeb293d5ea12e583f0e6a858000ee675b292ace24d2ad53d641988ff416 03776cc64fcfaf6b9a23a85f9577bbdd429b653f732b43a0e8f43d67efa63c87 4 200d574e962228bc5b74f2785ca3aed1662541f7300c818a4907279fb763f5f9 4c89d8fa1e9ce61a0f8ccb300b598c795baa7dc1aed979f22fd19958412c6fc8 287c5c314c26c80e50266bfeba4676980391addedde9789756dfaf37f5d5c120 0c294071658273a2f36d3bf44863fc65c8732d46e5f907c4b68ec8aa0ede4643 4ccc770ed00b9eed0888d3d6346bf28324e98418aff1d7903daf2ed68548090c 3 da6b16811b36552a25c4d3ce5d12b537959c985fcc0ef435d3aa66994bd2890d0b24d1956a2c2997acb633018f38a362c53418701d2ce92bee50666636b0f30607dc1a48d7396d8ca51b6b07ddc35ef2627c8c5231f3faf6e7db34dcba78ff095ac35abefc2b33edf1608cca98631634a04914d296b8b74aafa442a1a0e38f0168a90b49c74f9b03b4b08bac28e985c0f121ad175932f202ee027c63e842180be407571b4db2ab7ef165e33a4b6afb1a77dc042482d734009d47d629ea4705057cc3bbaabdcd310ab252b17b7feac5ac93d4d2e8f6fe8b0330183ee6f7ab1508233b2da86acf3ce6f73dfbfe2878407f1ae0c0c411617728bd017278fda5c702 +generate_ring_signature 5de707be4d1ceba4a9991eda597450d72c752ffbf85f714725b7a53086c2d611 ad59d7abf4332dbb03984884318194af02d41844ade4e9f91dccc65be8467abb 1 a2103cde970a000902d7bcd98045d5a688cea83d1a0cdd1bdbca4c087249f11f 6eee07f5e11d2601d7175b90135c8328c797d5f4952a125a96f934aa82aeb006 0 65427eb202868f11f21b47ee4986db17a9c56d862d6c8630e049cd1142eb590ccd16472e787b06bd2742e19828f09eae672e759d90d8e6a1d81c46fda0da6a08 +generate_ring_signature 1516afdad27c8277ff77ebe9cc6b9889061ab85d504c7678b51d1b453e656f4a 77eeb7c8666282fa03e473416d2b495c62e9b8423bd30aa054cdac22f0173b75 4 294897bcd480edc6eb3f302bec49fa75aa38de4ea0a5b3ce458e284477a4df2f d7b3729fc615104085544ea277352df10f43e24e8b7f47469893f34df7197e95 9e4ab024bd6a82f3544a941c0dd88b2985601df873cfb6c82c669562d0ccb2c1 768ca9255bad7ce9db858bf51271f6321620424e7e24e5422395b10e3fe48a04 7328152575f971140847e181482617d1220f0cd933535cbd8a5492e8c857030d 2 ca5cdfb40573b257d12ba020402ec3af7ffa5a0152af83b45142ce727c3d590929dd1d4ca76f258364c46150c8f8301512afb6fec95af1336f74a7537b3ce000fe1ab9605ef0696950caea46af9fd3fa10aaee09f1bceb8ad8b2b07a0e53fa0678f5f36ee6d02c65ee966c0e8d06b263c044a59ee0bc5bcd3e46cfbbb4fd20045bb535bddd1c196a5c0530f7be5fa0e4d42fd16892103c6ff7e754d503801e038b1562ae50e775b20df563e49eaa8d6698991e6ec225307d8806c39a5ac2bd02d085647604450989480445bbe222d8bcd0fb84d01a20b328dfe8c1363ac6e40dc3165b4057e621d42108cd35d1ef1bda1b5c82e925d4e61ed8bf5bca2b096102 +generate_ring_signature fba004537aa4d6db41185d8c4b12b3256048480c65d99eac669d156968585162 ede0caa2b77adaa6a0ff9cb917a79b644d2587b7d3821e5632955403ba4c9d68 8 62c27334f32b1587534ea7810f3356eec492bbacd7c6bfd2cc91b084dcc38643 c6dcdc2bfcfd81b385864f46dad8d1a1f608eb4d5615debaa651bad8eeab6588 806806bbbe254061c70ae2ab54ccb2c10886f792d9b3d0569fa9753e4c44562c 9e6598f7e3b174857ae16fea6948156db8093c3f991bff0da5a2d3c8f302c9cf b4df03f1889ac5a3b21134d6be2deb378a4c228af70a3317218fb34acb3ec86b 00262c572aceb403da34678e64368766aa091be815bd42bef5db81946488b366 c77fd924daaab02e4f95d653c8689ef3d3b5c71fc70825483bf36eab26280ffb 9af7b0c4033b7f3a8de1ce74f80b1ec03fcc3b92f2212ab8ee5a9c68869b5995 404d2882583e961947d1903d8bf28d3ee2fd467f01ccefac2d2ac3431ae30506 7 a7ceff744a1bfcaa750927065654f105433516f6b533592fe90ebe34b78ecd014a665f54e3189b24de6c2c8d2231840d0606e4e75ec0e961255c7ec554c9fa0d988c57a0f2952ae8bd47aef4308fbb0b915551487ecf272462d6b6319243f30a19ea5b978602cd52803ce2b66f8d2e530746b57a35da4bcd96c2fe88be7b33041208a6445854ff23897751f4fda6172098d872b46f62e0df23f1ff933b832f036481068286e50a28ebf24f5e47da0f9b1bbb51f8ad7975111170cc24c89ef806aba96b08d4a274ab16fbfa4a1528d72b6a9b9e20c58ab2783d8103247295030ac0a4ca63103c146df9615277392c6379012f692bc957a015200f6ab669cd630ac7c0500b67dcdc9a8f62bf2b64197f394eab6cc01d8d68d9de76d82defc3a80c5655dac9e556f3a009212cf0204f244bb35d8d61eb56fc33896ab75d1d2d7e0b7c02a8d08b6716fc5e5e19feaa2c9c2d9d5fdce6041c583236d5cbe8e4a40e05911219b6e21efc65e1c950c350c98867e7c329342996d7398c9036b4a0f56502bead90b8dc0bf3e88e8071f10f4112f319905256b9b0d9c2c68acb731493fb04fc4101d6af21735c3f6c9de0c653e362e48dcbd6e5e27438bd4d3ebf4e44bd0562cef95f26f102903508ca28e4ff2a896887c49ddc586dd192f4666db13a5308e713a593cf6770cceb80db87654536ed80bee44afc2bad63af7372c489dc1201 +generate_ring_signature 3ae3d9ef2c81e08c3b85166fd60d74ae81f1be0abcac3690480a1ad0186e68f5 27e97d10d75e9108d8c4e89e43ba75a843f4b250b3873aabf158a0379f55986f 7 1ef624898a6fb35a3396c517f90afe615d97e1beb426a8bd63de31e3f4725ff9 db1b81edd507d1773035e62c458b4597707bd5f7abe52f6503c7e94372699daf 2d0b80fa6db1598fa34207dfdbdded71c01c7a6e0c48a9895d103ea21f88a765 b75766e4545583a57ba86801b55b63f7f7c875f6ccf2e567048d191230901a02 6c633936bec4bc1d110cd8c1ef9ad728d43871ed5c25496493cd5f853e871b42 37426776747c40a3e60b35730a93d3aa3753431e7d8dbb2b61a5aad33ad8f6e1 79ecff41ce5665dcbadad024b05d9e26f9e6c7b9a7d10d6d608ff0fd4364895d dff0e02f69750454db98ea1c0486d8a27a105362ba9b8679b0bf2725967dae07 2 b21bb74d5216f6351a72c666d4feec708fdaa4c77360fd160502b115ef06d20e17513476aea0368a9e6eb863d2ded816a01894a511d33f22ba5c2addead5300f184c4414172e0ad247f7ff57aad66ca351daff60e2c4e13f5e4c8403c4b3b3085f0ff40145530ac4343b5b13e3ccd47b1ae9f645f90ce0909ed7e37ce5d1180b1e44599a85d2c114246574c512c352f2853c44d4ab14d0a8b5fedb5e759d310f1c803ffcaaaeb0ba87e09809c3f6afa6716d79e491e337a06bcb0199c169650efe9b3e311ffcbffaa381c08e3f8bcd6d130c58c0730f56e9dc7293f9e2a859057c3ceb3e812822f8aee6f9acd2c34809c5fec7b9e6ebb0331f012ac29dffb00492741e3aa236116a98e25be43effa716ac850021cc5be6339903591dd2ff670d27b9dbfe288a978bd1af98bea75ef4b5ef1bf0d829260dd8181650052ba7bb0d00bf17525fdd4967a0db619773e0370aebaac15d1651cfa3a3c2d76139fffa0788208ad71eddd5490d0569f324aba907cdc6a2bc3d5f8799545d36e56d46760d3b68318850b8169be86170f9e59c37627cae33bb6595f510e5bd0fe525ae1204c15b89fd37b1f7fc89a190623386712363f3e96b39d108bda90da8458d32cb09 +generate_ring_signature 5ee5ba303fba3f86f72178eaf964147c0f4728181baf66d0d69886ea57304f1d b252f1d78bbe4179682604610e30821f9a161cd81ce886689a1aa9199ce14c19 1 cf0e634f021ae9a7741780f42dccdb7ffb0c5dd00e99b208872e604210ad5926 ed4ecd59c016635ae665994d6a1698ba6b39989fc31bee19aa099bca24bca605 0 7329b03861da35923c9295791da8a2f48306f7324336e88ca0da8002150ebd0737df3895850ed04942a53d7c844905a6d9a4b33e2ef72e7ab19ae8c2396b1d01 +generate_ring_signature a168acafc3f3634fb3afb2fa4c85302807788e0b1d6db633457c343b83d4ba02 2ed829909d69d8bff8cb268783d3e844ad3539ff90fc6bed846c15406c237801 3 8eb8412c40ec4e21cd9a4fbd2d7298232370a4ba83ac5b0d1e316888b817b511 badaf9725b5d536b6f3a66ec3ef108a4b3115025df7f43e7ab249f3f58059f6a e30cb9b423f01c24bdc64c739a31419cada83692fe0c4d64fcef6bcb1776d592 7e76841378d75e2606cf68cbf75f3f3db2cde74b2faa01a701a3c432ea191c0b 0 b69f17ab6be454009e01387f477d057c3ca530b458c3a70f46e6e0d894828e01edc580eb6aaf7cc7b231b6f60e4785162233d5a00dd91fa1a54a30a1391dd6024b4100a5c9754df4d1f52992b7529db545b03444332b5b3b4afe384919c64608e04f6defc8b34bbdf283b3b7b9b1e8642b9de2c4d9498a068094ba6563d89007863de6d863feaf14d71405610fda0d21a05f5e1ec6edeac4d4a742e73794fb0a585fb55e3f8e2ccd0438b86b4843b7ad6a9d39591e219087541448fe15a1f10e +generate_ring_signature 76c197a029a4979d0388b7ec7e68ee11ccbc66a8c504502adb011f718f46ea51 b1490f09967c786694043061f7b498ddb9b064091bc8437ce133d71fabac2f6c 31 72b83a2fcfa3b21c53d07c617a198a000d5c4446bc05f142f8d42e94938d3c89 bda3e253729f9b6b189e923f6a5cb3b36163b06e8e2e5c1c6d685cd272ca0ed4 a706710f83c922e3aab795c56883186dfa8dda3408ee314f04816e9b8fb92f90 5c0813cb343b1cb1825ea4378014a6953f5884a8250d5766b23a9af5b43d2dfe 506171faaf0ee551b7c2dad4b1a4edac2239be4e7fe76479cfaac59551deb880 9e58cd307a14f6482f0c00d9342fc0b08776deb879c567b53c9a5ecd58d96364 aaeb56ebb9049870bbf2c68ed50c74c74f738c282da33a5897213c7e305826b1 d0e9a71c4f7b38d6e32f455107a814d6ccab122a3d027f320ca36f072f58caf0 a7672d1d2178777608f058280cbe24887cf1c80d54e4d3ce8c6572600887238b e550d5bef941cea97a3ca5587654794b2cc95e4ace85043435632d6fc9e6c239 08eb2a4f3d70a4e70bfb340724fe58e71cf75de3952c474abd8270a1a4145c67 4c69a16fa040d1904535fc5addfa4ea00c6478c97fcf349094fa4cf540fba173 844f1c9064fd2337d653c08109662a533ca1ba3adf29f71ad52fec1d581df0d6 1f38c8da9e1841686caccb994796b0d462db73db8bd5b023f47438da226212d5 b0a333c00453586b2585617916edfb5d7c1e0e41a06638e8131960b9058e0486 3e1ef31dbed8e4ea54aa290b70cbeb6674f2edd2f1e187634e7489fa52bc1d68 cd5dacb09ceae7dbfd47d22450197370322d2e34c5061ff0f886147a4cb3b444 90b87ee3103b7ca76cb968e6781417438fa9603507b72d5bb1abf1157576bcfb a8977f55e6aff38511520a9f07959dfed92a336e0aefcc6d10d21bc9937ce18f 5efc3ee01e44a3a68febc8ab9dbde69bc47f943de706c9e1309718b18b66d7fc cf1fa8638d4113d43b9a9795c37891dc10d1f6bd71919b014529f59cf2bc0d6b f03eb4af7517879ea5d377e49ddaaf2533e4a1e61935ea0d857fdd1387ebc899 763c9a6765b4b5ac7f6e53789392a1ffb43a0bc850707018f6419ee7fe8c7d0b 8a524e3d21eaa69a18ec74a26dc2d2c6f93689abd3fa4ec469a9effa1cd4bc89 8b6581e491580aead5297d1415c29bd12e15c79ed2d0bc2a25246b97a8337604 7540c5547acdd0df7dea03b49e2cc77d255385817bf3cf6ffce2caebff519074 45dd05c9ed02f168c67efadfbffd2a95f0f1cc84f4292826083bd10dcab56b14 bbe9ed0564e875b45735b0fcbb7e52ea810e54fc1f8fc7e1c704a16183f48d12 0d8c787d3134f90078bd47ec104b03ad4fac9bc6d61673c780fb445bed6c6da3 83cac99ef2be476a29ed5a3af28a348574576a086eb5ce2afb477f5ce5aac752 d5d7bcd8f0c14dcf5f621aa4f9dc1670efbe7b82dae76b43698fc21a2c05b2cf 019a7f813771d3f579ed6e3bf4722ae6ec129e1efc8854fcdff5c992a8e9710a 14 39e1486b0456765dd2de72cd67228305c2bee2eac0f1bc5a3ce923d04df96c093efcac14b790cb60628a1be3b9a63ea4ea98673f2b5145a0390e0a73f1cd6e081034fd0e787ff0d5f2e0b55f19c0bbf333886509ba577a5d13101f70bda15a0ee60d1589ecc0818fc9c00e67b35e306fdf237637461c91f69e05f694052d5e04ecac599c8a07ace93cef69d604849bd621259e1ab16ebaa2d26a7c8a9441900e46c36e14a31de227253abf35f826a1b0772ce60d98a9758d000a7f8a94026e03949af06d2528a82d7ce9f9153ee0d03dde08c8f4d532f9cd3804c5eca63eda0a149ce7afc28ab4ca5c7b0ec53597dd311f1e507de2c86c7f4cc47ae22270b401ceb4e7b4a7dc92120bbd5f18864578c3179c607212bfeb7b258836d81b342d04ba37ac88879c8354674aa021e8109f372e06fe9d948974f5e526d85767966d0a7b6b572c1f9548f2c024c29dc5d7536eea97b0bfdc938d8cccc2769db32eeb00921d03c41c62fc6c0a222ef6e6b4684ab505f18e7f08ec6733b2dc5115ad3703f2a1e817e53114919e606b1c71b0ef595673c52e8ed0f4f6b17d09e08e72d40cf69a68a179d1fe04af217ec526afb028906d1b5dfa73c847d95b9bcb4683cd0914a20c8b15684d4f8f91dc4287122942a5d0cad863e6a9c100649d57878cbc0e914fd43bda744193ba84437d30afc9e3941125c71a448476d63211389939c50fab39878d8962743382673c015c0adc18d9aace62eb3eeb6964831e322f7ea8037f67eb8eee9131e950dc4f259adc15f4c300979668b7bf6aa69eb4fbefbb8d00e1edb3fe5c07d6bcdea9597b6623468ab0f816110ad72ea5bf2f167ffb96b909e9e7a704496600992ebf26928f200867e4b74b2c93b8a4ee0a2a8216ba5c210a9cffbaae6229f10392f0cef218a9d99582f0abb1c2e180fcedd3137302b6b101c13953d4bab22f7c00cab61809563cbc7363530c63cdbb922e0836c2a179580ded4a7c9f91809fedbd1e4d12ac684defc9a8f58580cd6a532a03960185568203bc0e2c75755f9796e13d21364c86c7591bc63018dc2ce7122a2231eb667b4d0189a1e9ebb297b91ad182c654b475905bb456f251f92b67ed7663e3be2e89a701263413be601e1b24cbb683676c977d7a998f64eb05aaae5504065ccc2bb421055a93468ee30986ea0f71c933cf1d0de159534c1bc113f664c83c6460285aaf062610f9bc21467827b64c0eece7684c1bbf1fffe9889da6b2c7e21a03bd3d73069e23d2fdd84b58aea2edc39dabd072fc722ead19b104abba815684e79533de024c3360c28e3b47e4ab438cf6f49e9625b948839cfb7ad31445ba817c8d1f4308ad9322f3de86b09f7747aa3f44d7b5d1b0b4d26c77bda4381e26c6c5bfb3160f6629328bc8d959b627b288849ce7e721758150f6d5daac9937303953de38f2061e75ac8a363a209b5c37d5f93466c48cd84d120ff94e112fd763fbdccfb0b10d1b2a95f43f2f92ca47d4e08aca57da5e2adc13c103071ac95daa4491c0866e030acb0756abcfadd58c09dd00983ba0d6871b9d3264753d12e426f688858e640b46c70f5b81e2291c49be1ffa2964df355781f5466e1eab2f388382c31c98ba04c788690f3bf5356fceec3009f56dfce2a137dd7ec8ffe93007953dc048ff580ec1581d9540631ce2966868d0fec7e4c676906887b94a4d16679852b625fa9d01a07990ba0a3c457e78dc493427e406afb97b002669959fb1c768c55a7f7b96016c8da559c0500fdfff680212db4b9c30243aaf27271dfc5046e9a313aa40560ed3d3c3f7a9eaebfe556f6f5d16194ff1106fc89ac48841abc1cc69bbee2b0e0eeb8849c6a58f0617dc59b1bfba8429979784cf63bc1e7bdec89bcc44eb3e090575ea9d98f2c12a6cb9a41ff4dcdc2e390ad5f8a314cbe33683034a8908470400c8fe286d93a6b1053b5e6f5e478cbf2dd10c258e1c34fd4b431e56581afbd1084e71e97da3c2a4511a2dbdcd8cf7d8ccb89c264714f941f49da7941fb1c11a0b83a241054339ca83784a712e34d3a686d829e50de74576380be5ae1c1cc99e077fe486fd923a435a19d9b6555ad855734add72fbe7e719b9578f63e0f1ff16032abfb77cee40451dc6e1d9d5611b7f54daf589966824cb3227d55bc4f2b9fe0960acbbf64e522ef2f738b7fbc8f3cfb4ec092e7608e6f56c0a94afb881f5700361f7375c98c8aafb25f1a52df900f252c7b6292dd85f1ff53a8869ceaecf430438bec3b830e9da4dfef7d9d0316d4803ada80812518d6db64438dc4204b6f20f5fd2f368d3def5df71cb8f882b283393b0965637d8ac400dbf971471975034072c5102599b8dfb098bc41dadad8553f3d8de80b97f7f825be031e4993720ca019c0f6710615bdc607281574c5649551bb4f157162a71f89b5cf57b4f83b4c00d11a5cae362cc831a2fe2886cf766382700b8cb3b63e667ef57c99c1f7e964e02acf16e1b4858d5aa24afd02c8117f8841013476bac8bda6a87731fda498efd02b77b439f4258f3d88a9cacd23fca62eea7cf6bebc9e74448f2979b92662fa9039f751f3d49138cf3c9f996627b3654828bc118c761e2e674d6acf2b4e892820b9eb34e9c5f3755f0d84ea3a75e57947cfaa2c1d2aaf9e391906a92f5aa25c00355f7ece91ff9e71722cac1395e0c0b1829612d1d901360272122a5e4ee592b03f738c0489fb544b24d02a8d28e231378f0dc1cf12814571c0aac80f14eb04a0ac7f5632475083d272560ac01537389afd92a8e8890de713a0d2bcf6566c72d00 +generate_ring_signature 98a0d34ecad7f93a524bdd5bbf05df6aabf1edc86e6dfd555409eb9f12b970b6 9472ff1d85dc16f590c6b034169850cd9a7c61aa49991a2abdaf9924eefc483a 17 808f7fe95ba368b13e3b5f5bf8375263e293056dd4c0b946b15b6811047a07fb 8b7a69805ccfcea3c45182def8cc422c1f23959e156bfb4b94c5fa3392589454 d56fa8d5afe511e42e28e98f7e24aaed8fd49280766b40781ac01b14e402a94f f9e92df33c3a7deaef73480de48f287a96692bb05f964f331e86a7314ac3cefa 44bd6fe3e84c4b5e931d1751bdb85138dbc2497dd25ee0acb2340adabcc3d2ac 5e0e3b4e8610b3ad968c0776fe185d5821b440b1044c3cad601377b92792dfc8 701052782deb012f529a3fe3636fc4e6d4f3c5319c5727db36b7a76a634b983d f37e7aeda09d6da3b5f9d06dab4c809a9a70c58c9cddbfc81178cdb90be5d511 ed952f75fc7fbe0c454228ba22ee45bc4d54438d88e5544cd74d33c5e851e80d 60efc3617bdbc7957d8511526338e57e34f9a8502a8911684d24de2f76a87ce6 17323e276a428b2b238a5c03460a881ecae3ea9622f35add96322ef1b42c1ee7 9e51a49147a1029412502883ee90d0b683925d611e354913af570c8be1549f3a 0ea8252e6e1faf7fbbc70ed8040f9c57c87bb42876ffd8564032776d81dd5dea a7924de7fe171159aa57a7d22a5647f2e968396e6a4e424afbb070b98d0b12f4 f985bff093c1fd67d92b225d00bc4ae37ca856075940461b790c17c1a73debe4 247c72d2e0916bdfdaab6abd05ef5a877b7f175bcfecb8a499b59c6577b93e43 12ec03a07f4f01fcece9465d8967580241577df2d6da53db9a23f5257a62ea6f 4b9071d06ca415811ebf1038c1bad7d7a2338f82b500a3a61c0312d48b09040c 13 229672b135459b0b00e180db7feb3f48084c6b03eeeac5e2de4c97102a366c0d645c9f2df8df0d6bd74eb96020d532c00be3ea652572d0f7be8eada19a1a81068c330832a4b560014aa9c6c5bf20f5f45cd0e3055aa66ad992db93317a907a01e52135d38bd7d7622e928f7fd375aac38e5fe379d04efadfe315feecb25f05065591cc787da2607eba658a5b596b3854a805ed8aca657cf1ee00a9fc9c0fca05485eea448330e740647790c3a658040d486c79cfe4d7d59365099e680b0d160399c8b4ab33083b1f9b2d30edbc4eaa7cbc9ddec02dff44055920da09cf69a90893fc900c2703aaaaba3cf9dfe505cb8173272660dac59cd86aa15e1ab8816d05e842d89aa868173666c2e077b942f733052c7f2c971ab720599be50fdfba3b08a4888c33ecb9a556c153dbbe515ba7927ddb0051e88c965c647fe4f1def8870cf386cc4ba9b309500795c7a56dd86fc359a7a099d5af9076c0944c08422d4503815597721bfac1b774e9b0ceace12ca6f8bddc8dbf57c5cdb37047b924cd25041541e38ce85de6d5b06770c2703b6ea4e856b524d3431b2b9f1890a490d9db0d532d1e7a2828f340165b7ead6f0b855787e18cbdd68c3d631dd814a300dcb006a77f66c1bf3aee949169c2fe8d9acb131e32f9c2ffd23ec56a2829908d562601f3eea6208bccecd66f31f596fa9f00d18a0a15691560866ef26d3d430f618b028916cfdc1fbfd1afae37da8f99a13edf81a0ff3760af4be77e4240afd58ee80bc7127b404467ff65bbd3a8f9e4b642d471461279091675c66ff651d1c42efb094148b87d094133ba314676a5caae8dae51aa4733ff8974b913d8b0c4186e3d0ab3291402abc9f06317057cb2c430f6b2a4908d8f0337a4e562219ccc1d086f0343fb1a2d4f512e98973760206403f6315a59cf5f57272449d04507f4c7330106bac1d2c1c406fdf5d3b0014390cb71a0e4392ee27e22854e09cf6047b048e005d3704033300f50e6224fbe3b62dcd74bf951b8a7fa0957ecf4a226b331ce7f087bf4b8d4e15b2f4d5d0b1b82b967405b9aba037efc362198157f61c4646c1b0e2c398117c269d0777bf546cdbd438a9d820bbd538d2028cdbd8d9cd481184a0e65edc53e29ef19d014298a3ca602259eb53099265c4bf08368466ea5ac9f87084579f421630418d081c3a82bbb32f3ac79bcee4d717e4f6c01a8811e84fcaa01f70de472b5c5bb02810bf03da9adf93823b3d08198aab3fea3359afe1195630579e6a2077f7d711cc50a9c70ae1a111764445ef92e677f1008ea236dfe232d03e4b26105b27f1ac86c2a31ac87cb176e415b529438607dbe899c8d0e1c2dcf07369f9e4c1914b03389c910d8ab43b81ef611b35b518a8854e7be3daa14451605378aefad4ccff7dc639326ac778ca35538671bc8825a110d5339a6054690920abf57a686516f77a23169c4926019b6edaa8b27266bbeef9d29cdc97e8650290e26d7aa625bb405717826284e791bb9cf4deb47fbd0ccb9ed636e296a28882002 +generate_ring_signature ffaebe5a64fa21b3cae1f3c067113c4cf958a4287801c6a10ba0d63a82a16c0c c44d1d1d668711d4ccc3cfa257b24e2ee233322f078e4e6f82a17753e4f5c8b5 1 47d76caa6562600ac2e06caa3b861cccc17d73eb25df6c34308e21876aaa54b7 005c86d012510a55a17ba3300ea863c7179c91e876ee0138f8e987fb4d7e0d0c 0 45016cff505b460747ebcbef4c9cdf8a4b9d12a79a27f3d91c704ee0623d6503d98cad08df6687b15791ea22fef85b50f3cb608d444eae76be8b9b1f6116e10c +generate_ring_signature 73733ec5b86946952811b69e7bf7ba5c37587c91f2d34cb2370d2b9be05a22f7 d8e042940c4c896d6b06bdc0fa35e077a07e6a39cb15c2cbf9ff5c6df99b1cce 15 9161d68233642bfe6035afaba1645dae605eb345093bc46f03d8f142d5fb9fb1 128cc56981de785308fc47e1edbaeee881f818971d65a8a2c0100ff1921288cb e3ddb25dea20e2fae36efcf1c8cfe7d3514b371157a39108f4b0e7609928c716 d08ed18856d466f1ef3f50e49d0986e0cc3718be4a5dfc99311070f7b135891c 56cd77bf457f0ace98ecf454913a8b6940424c0e12e183b5bd084c34122084a8 232fb54db3f3758c5329d8d37726dc8bbdbf44403192b6dccfa8d3783f657969 554b67d108192723af618ec6024f9167100dd252cfff52cc9288da122350b548 049a7ded353d5a0ab8661f1342bcfb123ae5c8369c8921b5589a0ee96ae49512 ea95222a7f00d7c6aff189b8fc0c3844e24375a511ea4d62386dd6d5599f165f 558cad957191b01fae7f274202d8faa3b5b71348c1ef4600302a84f112bd2f37 c046f7d11f1a0eb7dd6f25dc1e83c2ee571e43241fea595625b06b18180e64a5 2f203ef33ccbcdf41813ddd96364179f00d04ab0c475744f66eac76524dcccaa 1f427b4add5faad3d81832802e23ae646a2ae862c51575d6e753b35fc87c51fe 9fa5579abfc3a715e844651c5985800b8f77f4b2d2c1ec4168e6817fd97cec7a 56144586fedbe763b3b0f3676cae4e49507c79fb23b7ea08a8b755e70c46e634 5e508d8a5e99e8ce441898c87d40de7b4a0cfb2bffda4b57c89d20065c9c3604 6 3396a42f0704bbc70dd830a500ce3d1484c41c475af49ae31c0e8e8f4248a5087a77d595078a1035042868f98373f7966456cd8109711ec1cf4f6a9f44dd010a58ed9578348fcd9d1c8cb569d81b95146d6b42909d46fc52fb06528344f3160c0b2388c2ed0ceaf32da74a5547feeda44f08bbaa7d04628109e73649b080120192c9ea78a19e3c1cde8ce487d07f25e2c4df78ad19f41fefcfd245eb03af380c097add1152d58c51e97d830ef654b15a93afa0fcc0f319e7304f7e8fd331aa047a23a03e544c33471cc97d6ef893b61cc74295d03d1dbfa105e5a02207ac47035663503fda553319ab0d830737ecaf8ec2b27e23cf5034bb003176e98319f40a970c7c7d865e7e287f028cfb8a1bac0a0565c0cef9fd319296597dbdd44b320d4dc0004a909b0ea093f3f5a7bf9395f1836f675f63225958ca66db4244c0610824c5ac795e078a3ff4d70c021ccdc506e522f9b57d6ccd5c61a14e84bf7bec07c458a7ffe6df15fbdeee0f44e89a762e47229748764fd8c2f653927dc65bd700adcab9dfee11bdc83fdf0945a1ba0a237dfd7c7f1ce75117fd5eb17536e45d06f0b765e219588b62f336147036fa13d0df44b8f1535c740f8b9110068b4bb30c78a5fba36f3953aa802e48cbb5b420f34bc3d29445f7099462637dd769c1dd0a3a01e6d2cfce3b2bf86dab33d4167d307d1d25339d10854dd345ab54cef52600d6e377bf9c92ac9528a4ff2bd4931a5860e7387523cfc7cb8f70e95ef5752106eeb98b8985bb71d16d0809ed483d87cec578e795f1d6e650abb548d59de9640c299d63a321d9bbebdadc554982029f0663e8392a53829475308fa7ce53b431082023ee77764f52e79ba7cdb268deb5d8a13cae9921bad88597cac23a33d931025a12788a0254eeafc6f242646e7723ba124c3d30476ee70097114a7147599605eb282bc86b5c012e08176e40cf3930fa956b0f340af172933bc824c21c177903c70f70185bc2fe378e6279e1588a25f178ef000ad8a6008647d53742cca5ae0871fb150c700cc649c70d19262811dea8912ec0900429e554e56a6412cd492606e2bdb0b2ca12269727ca3e3b2e8cdfa738f8730914c63725af4c7a8f7b8c100b414794dda4fd2477d63a74f167131088cc070bb9aaf9f14173fc02db48a2ff0a8d6a78a7d3ed7ce29b4869fbb4fc1404fb4a9d2530d4e0cfba4afb242758f5004b7b1efa019ea7e30ab61f25a8ab5bf92c0721e99700eae5ebeb9a9fdd3e66081ff4ba01ac9bafe46db58d196c11cb9304b14ae3d7a3385f6cdb7faee3dc1905722eacd1e4d770923bcad99a45aebcc75b602151d36a2a83e7c625cd8fca480e +generate_ring_signature 778bceb8b14ed5f0f51e4483bca6ed959b851aa5ff880a59320d30d0e9c9ad06 d8508c6187929646cc7231cb18a959d0c181e5f40e550c32ebf11dab277cb36e 2 dd0e07b025b266a88482d72d7d3122d2513b11966b5f86b0b3190402e30343db 903bff8f1f2a393c6f31dc6672be383ae112737329059fc46090bb1a9d684c1e 4a41c53827294ab03c79424b60a5faec6f44fbade5e01d59228680eeb4a00203 1 2b969e32a19ca91c13b003e1d038f1b1b61dcf5c03a44d46932157d48e77d50430d0294cc416ac98a306fb9adc248d16cd6674baa2b24998860f366e3f2b4f07898b4093a049b9e6a79b56fd94a82f8446e7dd273d4471d7867ccfd5c091f40199c88f0c2d935e9257de150ff0a23cb57efa149828022869df20d7a1f5b04205 +generate_ring_signature d0af946a7168a2b284854db904594d1ad9055cf337f25b80c56f218d3c0d8dc8 ce5be5577913123659cbafc8e7fe1b31227e976ec379307282605e229aba392e 28 b5bdd837cc0aac94b3fbcfc27fb023126a3ca0308d6f9ba22d24322d5eb01d57 e1b478d03240bee41ee60b3ce5c2875637836588696ea197fa4cc240d9f6ed44 4edf95184182c3034efaed72684168f95ad8ec6250c25b075f0912e2d4f63459 1e9cadb7f1407262d64ef55add50c2f6875160c2fb645c8c8ac96d2605915e77 4d888e5b483d209c20b60c5e5601c42deba7f05592ec656cd665576a0bd3f742 bda224dc161b87b51264856b6478fd355106105b80380ceb36d2e18e5981e6a9 37f0548f3545620b3ef1515fc5b522bccfd8b8a068598495c4d2cebabff9cc02 e386b54fa02234238ca51c9939d2e3fdee81abcb94853f033c615422babc8f63 4247bd9140878709938dcb0432c0ad95159a00c66902c004d2ea09e2335cdee3 93ce5626ab90afc7f16c8fdd870e9d3640407fbc4c29cc12c11108a852208774 a68380092c8ba661fb24f30b21d7df4b840dbe8fce869394986c7ae4e2edc0de 25be966db483ab07c610bdd83a5e26019a21c9cf65d582d3aab40025284dd4c1 ecaf62457619b1c1362b35dece2209aa5eb9ee8cdaf59ae1d5be775fdfa987ab 5ba393fc4c396411dd2e3e93455b31b17037e14f9564d9896de7455a1994ba29 16b37fbc30dce9dffe460ded732c588b2a863563c0d724cd909cca18df6ab50d 4e97f0370be8608c175717e554f34aa4610daba6cb16fb96c4136a57ba230b9f 86244bdecbd028e5be7c98b2d347e79647333e71784a584eacc308c094cafd2b 25839bc668849bfe64fc9be62997fb93d1188108f7565498c33f3f4ea4e1bb23 429f58dbe7f3eec5dc1bf30ecef4293381c828c5bfe7b7d7bdb6acf942cf858b 2e852cde3f41675c3a1aa901653dd6f91229fb5349b59b95a86669b806ad6d49 b0a6aa5f72e0b404b831b691b29c810c533abad713128efc640b78b46b8bbd6d 8bc504d0ccfc3d825a1de31f005b02e5d7e8396b213ae78dee2745ba94af12c1 4653b7942cb6e119836df837d39f7b43339ae6faba28856d987a50ad2794b292 454a9c491f09eba303fd689918d9ae6ad662f8adf029fb687dee072d8809d26b 861cd74d161b68c89564fe7621ed75778aa4fa86bcfd60b981b8fb5dfdcddd16 da9d169079abbd9219fc9d1ce674378a218433cd3058d7f48e71891724f8e3cf 90e52c87f417b04bdb42a50cd4344c89d2ef87d722ac451d8ce89cd148b9a69c 6226f09a0649ce2c264d056d3dfdc74cc8edca34091423845c78bb691bb6d89c d06d01f7eaea9f5aa3836471b07f66cb15f9abf5191f03245633a73abac12609 25 e1b1b68b33b8abf7dee5dc42cf1fa130b9c9e5108330d374b37f154599ba290f9b983c7942f66d50bea032c6ee85b9472c186e50c53689d09a97cc657697e9018a3207bf4d9859058074682b23d7603fab003e18b19c0975174be4267f275e01284d7ffb807527bde515b9161f7708a30ef256ab7dceac24044a918d8ec01701282e8d37142e45b9d6ba348e23e2aa37cdc6c0a4f882f48fbd93ef0d843f6405af3f86e986e4fff771a532d0f1def54ccc8c7fd0a7ecc6196069ed5235d35505c24d5e3e1a1c9b01d130f1eade76aa4643ac0434ded63599bae730532253d605fecb9834d901326f3928733a002d9110d737a79f397630af8be30a5e4fa027046414c36e1bf9188419d80ae52f9d309e1fb1f45ab402aa2b4544d805a5c19207fe45708f8f01111502742e23a63b61763e3e81f48f88344771d8b50740fe940edfafa34d2da5d0312d68a5de4fe27f95b40382eb048b5d17edc58c5117b21c0478b40196daeb4e191b9438401d51b96a43664e8425b1ed3f3a834b7866a5d00dc584f7e68c4a54a1d02819f4855ee91a8639513fdb94ce05d936461013a4420c8b14ffd7189c6e88124bdb69855e23790cbaff2daec526ff7daea1cfda670c086c269540d92290dba760b24ee696ff5e061e6d18b25da69a6cef3af82e08f708c027bea729340395a89a239d4636e7079f95c768fba29b7d2c97c7b57d54590dffb96fe728d290829f9382e50bc3fdb14b832fab00ce44c5cf2bcc9382c3c303d806a497cd1e8d1c5bc13183b9c0b3581f63fc3617f1c6bca6d3a953a54e7f0d8fb4e05aa99d90b3e82c8d097279eedd21317307779aa1217d4b9a65349cf209394a10b7fbbe87dcd98f9defad1c39f2e59d2b5b511b0a3cef72846f339cab0a79e2b8fc82666e6d6164ee050ff1a74a586e9bd7796acdb337e154cc35feff013ee0337cd1b6291902f904a3a0c0463723ea2efa0b530b2748d30367dc51b7081cdff2cdec7083a3df621271930200412fdfae3a1cb69ab25074ac6a2b260209f185c74313eb8986e4d99154cf0011ef57778013d80205d8bcd4ec3f3d602f00ec4d597d9c207b6ea53062a36e0f7688de8641cfd09c418ce217e08b7f31a10ca52afc702255a518e24f2d9d78fea290ae9eb780ae51f5f75073948960fce00d906fc4e84a7670bc547708d2a52b3ab3381e7a540a77c1ebfed53de87dfc81046e8c74e582d864379050c61296d720be792823eabee0fc7a2f783a902ea020099c1e090e13bc4b92db2cc059b32a9bc73cd598bcc023509f6191728f353c93069d637c734d5f860f801b4855c9328bab9b9d59eb5902fdfd2a67e1d26ce7a30d5cc693fc0bb0100f246b24d59c784e029eb490518402fda1cea9d3cb5bc62f02a179ec62f872374a1061d9a0c05eae1bab485edbe8e53ef85ce34b89de471d0888133bd506de75de0b29d3c5c40ae4c78f15ed4168edec1a0d931fb30b457e01bbc51160e27c79bd01a440bc79d0b6f49e58bfcf3c1e2da8e11b7b4354ea9f067d3de16fc82e9066d181744203447c84bad3bcaeb81a737ddca73e29405f7404a30b2cdcf2eccb50365d45036f902abcbedf6e3b9781993f3d64880ffb65b5066fe8c78c26c1f4090059db8feb5aeea02ccc86cec3a6f18379465f18fb80b40745dbffd654db4ef3fa0ada21f542204df570ad54696309bb331dcd6456fa9b013c6fcd99601c397911d4a053407c4fcc742b6043289d036bfab167ba1d85840d9cc14e881e52f68e7ef84f895986f5a5c4c82c4d1974acfcf348bd382e6cd1034300598fb6b438164d7997c93f3be5a3c94c4856523209a9768a5ebac7866406e2a3fa1b21d7684afa80afa7ae616143631f6344264be67968e4ace35cd8850b11b94c9711f89b43172d0fe7c4119e1480b50900399417976abf1096ac278e08af40882eb26f137446c107b5af3de323287915b727741afeb8685ce8a0c5160761b2397955cfea1e719fde1eb180bfe4375eb36d4d358e4ccb3f05e1e955ec01b5e663b12629cc8d5afc25735a97c1a8c53d53deb8c49014423f6f953fa86f0ba4dd5194abe5f6a1ade9ec84a152e4bcd95c909a84e709cc015ee37999a4ea08d904ca5be74aedccd50b45a2a892a36edf5868cd87a282677c091b21c91e010f91151e185cf33c68854011540159a89ccf582ae4b605e168ff3d7c447242dd07cd19610631cb4e817e879ba59c637d5e0eedb8d37f75f90274fbca5d8da2d605d872cd1792494d2ee321c83c7cf73ff5e63cd74c1cb8d59f2810fb7808de050d166c9761d5a28ac38f7e71c9ef0216d833ff601bfb201205e5e740e97078a40f6b207101bb41f5bfbbef12d043281d6763881c7f58dd3a0ce83f102cb1e24f07afe7e3132a83eb35d7506b842192b407041a219b8ff76757a5acbcdee2847700696796152b4e794fcd481520fa47fa9ffa14da0eb305fcb5c8c71de1446f370679bcffde16f2c7a30979e1047855e8116a4738a82ae4fb2dfc3fffe893b8830a +generate_ring_signature 32fda91fee7ffd86261cfba41936bb2a57a1babc1e41c93bcce51132d3e9b507 faf0d72af0aa2f1935f9dec6852902445190d777581a778a7e1a92c1bda327bf 7 6e89346180becc891a370752d14a032274b762a037103e634b45bfac60809226 97d5764cdab85f5c11a86546a36a96bae26bd080c451ef596daaaf5ea684086c b60d1e57e2d34be3512e8a2be4b3d00ae2fae3632b5f9b0ee1c71705600b1c0d 3f10de099eee553e1b45f8f68a760d6516e2690ab993b9bb75d57a09a0556f70 070eb1a8fcd9732847154aea827954d8eac4034de82aa78cd391ae8c8f395e19 49ffd7d2431e8db5626d37d6220b630a431cf592bd72faecd522feebba1f2291 cc1127fab637befc072b81cfc583d2d93180133cd0ead88efecddd89bc46e4d6 84eb3ef42330604ceff1a7e02b17de15c717f1a904f34db6c724f1702876480a 2 63b1587bf7fd448a3cfca6ee640552adfa52bb32620a8b01fc0544f6473d3602bdb97235dcfb7d12dda8a85dcb4d346b3933873948f3929ac8f54fdefb486d00e471508918c1f7426d654c32d1ca8d2517dfa246060670e7ee56b2e9d7a532073f356fec3a2c9d94824f3f702c036b58e71a76ba9f309736076357cae37be606413e0a5bb7681fbdd6511b74eb77edf3c7a10616e26d53c91bf5edf267c48c046de27ed709a02fe8dc879f3b37c08dfb8137e6b035db8f8d5d92411a03ad7a06a5de9268dfddd7cf39a8105c375d4e5ab6003602a66d33e63d99f966ea569e0a1f221471be8cba8575f049ead182886c339e7af1c4ad797906faad46997fb5084c7164b1985a1c2cc2c903200d2257124a9c2067c2e19a24549fee85eed6240cf5d288d87511e5963fd23c89575990e7f8eb9cc237b14740a16987b99cb4220caca046d3644e742f8d099657cf68945c4e1e502195c382daf2429bed25dd1b0761d9141faf6256ead78c7aee9dcc42a505796bdb35d94ee565e468bc7ea00006ca58e6f4c6dd591dbfd6f3306d2d765522f68bef4ede7405190da56d2f3f4907eb98883dbeb5f022bd4190e8a34b2d77f7c626e75675ee08b51f1693af8cfe03 +generate_ring_signature f95f58b92f28fe5c8cca37b620c77466d564444b3c4b27bda95be071949ee7f5 0c7a668470179291b39470d30af192a98277cd4c831bf6fe9f3501489ddfd8fd 57 9e703ea6f7136edcb82cedcbf43a68c6782e8f1e57c5c2303b4131d1d5215b78 2ca97ab38e7199a22fa6cc16ceb84711e9f2a41a8f997386ce5b60332e752d32 15cf730ebedadf9fff98d1d7c42b42328259a37645d31ac52a1ef24d67208fb5 bd41a6107e68625847b3eecad7c295f58cf612069e47eb4a713f707452687a1b d95b91d6438f3fb2cbafc7977aa1c84e9869cd78915800f9560d3f0274b2accb 698db750d8b5ca89f0a4b0d0ced5f429626aed733dfa772cfa6833c2b97fdae1 4cadc08cdee26ac4f3f3cb932ceffa9ed5e936df7bf97492fd07bea007d5aaf7 5ff610158afae988f8cc3731b3f0613d1300df97a6822626f845d8a73f14d839 46aa6f92903c682a2897c8d73559f1db32dab440f857d9d3bee4e098fff99656 20bdf6ed73381e3da67d3a8dd62e82bd51afe2f750b7ae124b08583c450365b2 640e7073de6dbb3a8d8497c4a3f9c60332b485645519b2283a238742da08fe46 c9572e5cab158cc073919303f7c3288c2245c49de1129aae637b38c5bb62303e cfbb23c7d6ddab8a249ef1e1dcc823e9bece5a2eada6ab13fbc1242131daa612 8afa786d231a0c09a13d63170fd8226330f81f708147a1f47fee7557ba27d3ed b365ca81fdd650d274f1ea94c492a4a65e64adb7b0fbb6de9528782a3650118c 10984b7aa7bc1ac1e54125f0cf87f802a9d64ef28ebdf3dc91913a4eaf8aafb3 cc880b625163e20cbfc65210c127325e4b3a17af070d1b74126c7227bfb6d065 0344b564c3e1b91487e607626636a5027dd2a9590951f55647853ab8a886bfbf 5c568bf19007ceeac73bbb66d53c2cb062eea710fa82590f76da6845517aa8f8 cf31e75da46ee36623789933c452326df8b33609ff549b6709bd747f3a973ea7 c3af861bc2c38f2d94889e5738e4a756b231fd38b39d9fdfa307a6af6a26fdf9 3dec71fe173c27b02df24e8989cf7075342710a13976e63bb740be0ed3cf8881 71221e35d8ff84de6fbe8a8a23a6c94d4a2e5c9dd5e0806c337b78b40972d968 7433536546f072ddf45b1cb55fbe68a3c3b6154b4f43540974df4c96c16df3ab 62b2725a6f4ddd4e5bbb0728efa86630b41d8ab848d746f73af95767fd57fcec b6f19566fde140878f4f1fdd0f64ec397ffc1ecd66d780af774b35ec329d2139 b1e53d524b31832419ca5ef8960245fcaab7cf1ee7211f3de92737fcd32a2256 156c9189c4541514c557e3767e3ee07429b397cf1cefdb7eee9c978d1b11f6c4 4babe5081a6e5a1dd8ea579522c8f6a44db64bd48229ab6ba7f9758990c333e4 3f6ee7d39a89bb34bc68f02557cde48c0f1e4ec9654d65bd704637d16fd26721 4512838f751891c81732898eb640c8512b6a77cc7d63a3e61ae0827b7f710627 a69bb4cd67c4d72585a8d3db4000429a3ca1691f2da44e01ec0113ff72dc895e b737c73f3572c71efe7cb285879ade2b174b65cea150f180eaa17014d7320cd0 7d472b19d3b3a828dd67749dab91cc686c0aefeac6f10b021914362e9e3db34b f6158b35f8a7d3bbc700d39c43483aa57ce38c8e6ca1195eeb87c673e5c2eb48 5dff3f8b3f1d276f142d69dab6f425f4f78b1a18182d1ad207c027114a694b13 e54fb93a3f15286ce5f2284f284927eab88e48b8213c1a41eeb586ed999404a5 7dfe099cad93d510b49cd166c192e813c3e512a62dc4ed11d9b9f781f0194c80 773cab6f26db9c2c36da474052966fcafa308ad649ceda337f764428948abaf6 aa7946ff4d12c48703c04f6bfeec86d18c4a854109739b3863e4b5007999e2c2 b386b4f889db963dc4daeb14893a3b0b6c3b09a3920a1bcfd04dedd217e573b1 0726fc793b0c7493cc0439189bd82d5d0c3dd117b9aa33d6a0d9553de873e88a 93f9a1f0eda3a75467c99b01005ff16a4c8b006649f77533cef185c4f7a30b08 377b1b8895d0b601d45a5dd36b93eb460ffd9e30a0cf1d96b91f7f189f4d8732 5730b7cb7e779695bd04de02fbc180f8a7b1a7e6d0fcc066bb28d4c94b3aa021 548678ab5e2fb6d870d6c05e39b3267101011c0a842f373b0c9735313fe7c207 788e4bca36e99ac9bd39e7e4188f88b78dce485fd8ce7408ab5d5588a82eea61 c7fa217234c15a4b2af0aca4ea58af4d3762dcd3b17a7be13ad882ea91539416 12c3585925770b39fe686a8d04ee10251d504c94a6c16853f7ef1a6f0a525556 f8748feb866849ac48f120940c6731791a593e5973c9228693397d8e3ad65268 8bb30ab41ee5221c65b21919b8a784de26c55983fdaa5bf635a1156b51213f77 70a7fe521146c4e2c6d0673e9e31f9931c0fc79d516439422dfcb86cd52d6497 a85e0367501bb37a0c8d92a311d3b6ced453790b116246c9c5b7a3f6022d7dc3 062fc5d81991c3e216ddb0caed6fdfff321d73c2c5fe54b9d956d18cbf7b7c53 fca490659c54bb57d385e33e8b67773bc912ed88506ad07f33e4acd1444ff692 1cfa1c7683fd787875092bb812b4064d0beecac871e1af17e72d359f27e2d87a f2a11397ecaf7753c18f0a6970da0573a2804bfc4f48c099b29e71ec6397c2d7 25a78e09d6d2bbf9431db8891b048d8429eb7768ad2fe5f2482a76e03683e908 5 f9ea10294467d8c136243972b0fdbb2196005c6a1f3a1d6b303fbc3e4ab33c0cbe87d5165649e1443007d03c41b97b01d19fa6d490cc96dc93ecbe64e164e008977bdae51d5731bdad097ef740c6b12ebc634fdecb82090eee099b72966356006c89681f1fd2776c29950605daa95cb1a0fea3ec6be1a7dc91f8dd626c570506fdea4eb5cb013ca970c104e0a39096d5e4a7388740ce44ca0d64c2c6c7448e0aa867712abc79beb2d5366f368a75edf4f6870094cc06c091b0f188cbdab84d0e2838297f675506a5914845b282bdf3c6e1120a64a64fe0ad2ded2f5f4e86970d09db1b11d7420b2aa592ea3ba101390beef50d42596a44042abba6b7d8f2f40d7c7468f1048c90df151aab5192309ad06a3674ca1514d92236d7b22e1181ce0f4eefb20b710fc0dcccdd141b012f9142ab7a35051242fb329a56e499d1dbb80e0bc1a3d5b28e344e0dd4640f16e56f976a82f31b06bdca058f9b4c5a18d18d0d94a18c531034d4ec97312705c893313cbf3395e1e071681e468c43cc39b69f02630d6934a0036bb8d3ddaf6b5bbec7c7635bd64c633a8a1b784b0652df984a021ea14af799a5e00d5245115081686a6e16d867a37c7a0ac864b4fadd125e0c02b80dafda7e91ffb641ff964384b9201c0697e3fd500babda58839f453735eb0c872bf6dab726fb303ff13cf7b2730c0bb8b13d96333bb01e5e27caaf4b6f760fbecfc17f0359b7e33745774dbf05fecd45519acaf6d0204d582dd60df48ca30946e6f2ad7e28ec2e62d3ef75a32b7434c723c28bc15ff71d0f7f8a6e2f6151021b4502ba6ee6042d56cf7d8ddf9d7a45728ad1be62aa7078e9baaa6cf2cbe80d336d7422f29c6354f73292f3ef0d19dff321bdcfafab53ac9afb5ee924e31f07dfdb9c832f2eefd29d1e44435e8ad9fa64b41b612382b1362a3509d78632e104167cb17bb645a4e261db047ea86e6261373e1d67844fcd7e87d8d6b24b0b99083569d0497527fa36c53bc9fe0bc4ab94347fd9879a79501cd19565d81718970d9b863026e088463a9181254bba9bf233fcdc22c5a5e3379ddfc33e950b11c8040ab1557a7753244175a3d434862bb07844dbd2da6087f9c1524e83c7e8c9d60ee56b8f55cf0b61cf122aae9592d0af9a9abcaa9dc366862018892676cb9645027c17e9493bdbc53e5ae8bc29ab6751f25c0e041a4f8acef0a2b6534b5036420e2bb54030f05959225d4c9d0ddf2f9fe0ce1841d4380e52ba5fadb5e586582a06e68acfa26cc07bd8b918d4d59bc9900cbf525586b2da060fcfc9dcae2fef270e25a28ff4f1fb449aab653b865061d4a3789c3201d6bcf00c13f8678c934d0009b27a5990281b6a3f977f7e2bd656aaab4cda8911da87b1499b17116b202f5b02fea4ddaa82e5cb4b270b6142572eba727903033a45267567a39c86a3199a1c0eb2a97f5c5e1dac1fd99a88eff6f1930f3b5d32261ef6ce8bb3f721ec58d2d10dbc8b669044368af81efde77025e31ee9c5e226acf4856341a47bff97e97d34093db3b6d11d4c3a892fded7f9ae333749e1003df3878707f371444dec9f756804e3414a70d52120cfedd38aea649fe4d36ef2dbb4ad8b7e9c58a2dc586a416500f817d95d36dd89214f5df4783f08ecef3b7dfe391a5044b77d7b70e9507c3c0bc8f96d8716eafc2310c17f6760b6b3451dea0a31194dc4fd84a877b6b9c41700d2ed8eb936139d08bd0548dce4a7437e6aa6a17b528f5a9d8f7f01882dce8402e7b19d53c2f8ff1e2738dc2bb6680c1be82abd48ba09f9452ece81d35d1ff9058c38d2dfc00b6b4dde5df1b0e59f48302332e35b5227398497aa80ce4dd891019879a820f92e3ef301842993ec1d810016d21148543991549c110598d2c7860d2b4a459e75f8a5a3bf8aed975bc4335c4f6aa2da40b9b4e9875125fa7146c60d535f56953630fdee6df568aa32c406e0fefe6fceee7129353103cd37d977790fb18da1650495640fd49772dfcd2f8397a3d01b9a76ca4139ee8e9f6fb4399e0d3d14412fe02a78c0cd3873ad2f3a32c31ae05a98427bfa42916223fdac912b0190111e7ad6fd7c3a438594e9a2b2264abea9639773bd22be11039634a35d3b00e9daa8823e4fa8b077df4379f1ee1f32f31c384db9da7590b3e30d1cf7225b02272a19e373cae8c7a1d9e7310183af3dbd3ae19a6060d3b23071dc8008487905a0ef9011dcd39badf5e1e4092ef2bbd51a247d84d77820686d38c26e7630970fcea629f12bb7983c28be7d73a92fcc401435095ac66bd40350056445867d7b0f37aef3134b3d01444c9e670a6615557c283a7592ee5f446480c40fb6ae9611061588c8b6528b0f8b6037e60f4272258e886105e2d48eba295348abf04816450f6e0ff1dd7a9cd8f278b76170b8092ecbd63236ccea97539c4708fb212a4ee5001f67a175b927bc5951c9c0d708340248e59103b86b7d32c591251b55f753e30d404bf8dbf40f0800348b301ec6b6b9b508667c8548cecac573ff2b9a2247630c51f58d56e558e39fe65d104cc73749d686bf8071da26a2abce09f5e91cb39c0c4f28da63b74a1f27a4eb9fbaf27732a3341daa872f87e74ab38a694f54da55095bb5c9b4ff005615c7cfbac47a55a79000f3765b20b8e409a664214ec86e5c021412f6505b59d347d98c644f289dc752b97a20767744864d6c85e46fdf543b07290aecd9cf9afd8dfd2cde0fef08fc85c3b5fb1c59f3725869b1e883162a2406e3aef1e43e983f1fa91d03d6b24cb8100717138a64561bb2582884d7c0cd3b017f135a7ba72ff7fd095cea52906040ae914499567342d59908544a8d1e7c830af8892a55f6408f8c45cad16a23a1ed76262a3a5dfeb8b174996263a5a39f2b0aa458079ac018f048658716bad78a2f263fb041d322a8a437468b5292ed835a0ac46664fdb56173d4922102414e37cf65473b0754ee0112ab49cfe997323a61001f581875efd538f8c6ce3bd16d191a3515c8248f4f5c4b6e7961dcda9286b503dc7d69eec532902a8137c71355671d69ac71645a5742f1dd8c19b1801549830550b600ca80e76d21db282c2706e5dad59a1d8067b20ac564bf5bd6fcece0fa02e00ea91e70f1ef97f0109987761778ab1ee5dba88db2cf88c652941b5ea0ea03c0d41a55845fdac42fdbbb91b4c78ca2fac40f2786d3445a116f50cc22519606ec742626a24257239c18640c7d548b08cde9ba01c2890eeb975bc1d7e90f57088ca8e24a5261cefd62eb6cca15415a23c050d27da45cc0066a80db5bbef4d90928178802434ace8553ff4f54a12b8f11981f757be7776010e6436e0c72820509673ebf236897067cb144ab2a14e7c1a38815c08d7ad3809d86f4e4ba3f12060faa3cc3d56eeeb259feb81c1e19dc6ff6dbdc19b59c8005dc530915453362880aec9cdeaa0f720c2e1f6d9dcf8b2c805a42779ff93f4f0281e9191573cb29ae005b0b3f61fb7a1a8672ab2386a2a258cd3d579cceb7b81cdb78c82030a497f80876b4293ab137f0459119092701cbb0491306e89753a8fecd8356d068f843a500b3924e55199d1de3be74c9d722864018f47ace0b74dd4f9d639ce80d04abad00347ae4583990c44454d7331cf8cccea96427a4de05d34a3a955c4f1e746a430c834de19404a77fd33796ba12600690bb4441f7620a7b21e5252021d6f460cb0c59b0b022c97236c98550f26499beb6a80c8c5c6c2ab487d15d0d285b366c1d039378e2710597941242a64a2a9d3a3b9eb72118ed58b18a2c5bb1572e91806d0cebb74327e2e0760e1e81a2e61792e39f4f6e8e374fb015d57219078f8e8dcf00ded933de3cea84f7ce9f47210d43cea3ef0971b5e428f141ad0dc58ade09ec0eb36602bddb295784497b2ff23d589a72900b1c5bd596f7f3eb9805c9ab6a7e0dbe75f936497cbb41a029ee027086da196ab971cceb28662a1f0a7ef8b5fa1d0b1824e3a45f9619b5c6beddaf9d6de9fe6247094e73667d6fb0f86340e9bd4b080ac02f73abc702cdd9ff4cdebcc512621fe0e6e533c77e96f15dab1766a4b00a15e5d2219fd4339ac2ce51b33d8c93994f80b6940812a97d6ef5d75f079e3406397254b1aa03d06464fcf4b1175e2986f76f87eca47bf9a12526d750084f75026ce571abe2aef2dcaac4d34fb704fac8a375b71cb24696e51d783cfce68b6b072775589a34609c5644e1282b6df5b59365fd7b6e67e2a1c38dd3746eb2d56800ef7fec80fba750ac12e0e6253b0f47011d422b73796ba53ab7f47b3ffb5cd300b0b8ad0aa2f416c22257e6214159935a352efe87bf1407856784f23fd1014f02d44bfd5e56094eaddb138e33d79fad843507acb4df64dccb9a3e08cc7f352700fadd7c8fdee2868d5f6ecb32da1d12d4d782e788ebe071b78fed25bdb41589017fbb07f0dfdf9b45512c972adae139476da1af30eb19c34dc1b09f846e4b9e0d435cd56833b2d7a053e5e3475dd788e6f184b75ed53c9223012fc7385856a6038239e38e3b15169a049ecd44a04c3dd7c4493ef64bbe1a4c248eaa47917be80833b2ee25df9ee4f2748fd67439e41309304a8a8c797d14849d939bdbaef2c706065600ea940ad7952238c7fc512d9cea31e927a2b6799aed1c2a89a5d13e8607b49aac43bda321e980538c88bc5698c7141fe6feff5bac98fea8633aa96d3808606d545f289dd6741ef12fab862b4ae4e50400e140e930540dd81cf78b2f110123dbb0dd77cd6481a245e16d242f2d9aee4d1c132beb8ea45ff2e33e6ad8a407e20028074f1a89b6eaf6c9e2c76b8fffa8096603a1f41843de0130d968cf2c0b9ce0b54af3c39ae5dfbef615e8a437aefd3b4e8f276edb6a79b802238d995602ad64f4c7dfbbe2803be40cb3ac3af6a67ac86e1bd115500ffc95af0c68885102223c21be4e38ba59c440d3ad778ea28a312d93b8da981b615ec14eb638ede802f6f8b7a9659a178a5d0e428710b407ea1f0f981806bba9dc7850b15c3371c3092b299dd69bcb72f2167a50ebfe66d75b4f256fe16254792f5518e1389585d80c141d1cc2116189e70f5d790e58fdbe1055e59d4670810169d19ad0f5eb40af0dddc7f10e86786ae05cbfd4a5897d00e3f175203ecf39d6e33e252972d10ebf07 +generate_ring_signature 3f9d1e8c4afb644f791e59e8cd50f02ffc79029629d27d18462bbf999fa28537 6b176e3de6c8e0d8fabe2e2552415c02af6c8d2352d99c208af199dff42d0a89 1 0870ddc2441fb0194765bef22a548260bca7fe9f8480a961f108f601798dc115 8c1cf72b82661277cadbbedec6b11e35f7bf100ecee3cf1fa9dc46de1d9d1201 0 449bbdd7fbec125afeb0d6efe02138156af9ed187ebc6c87ba1effc72b58bf0caa111a98e845e31297cea029579906a9bf19de85350f6a3a932107f8a752b909 +generate_ring_signature 21021e77c02b18cfd15c18a1a6a46fec6a5473f49fe7f17f408c9c5ad3bf9f77 b34901b76b34408675b429898c4c3adb74859e3577bc512353ce51e338bc1b80 7 bf6aeb0c9fdc617e09d5fb43f98f77c0e4db61b442411f17612d8f7d68030d8c e3134f86f35ff93536f816ee7c71a22658f0922dd6d977141fd3251117adad6d 0351df4a97e005521dfdae09511278b8789a7f095ef151c134fda3d71db05438 31ca8ba0c25b68cc7b0cfb6d37ceed81333f87469880e8a4dc431daf9b3213cb 12887b59f4b5af889620abf19a52a7bd1973e04d7b949ff683dd3da922457017 2aaba178d00eb926ecc67d750d6ce2f10402e59cacf46e1d60cd2bd2dacc7c06 ab66bb5959bfea90f0ab7f1d6c78b6e380a8c91ae5b77ee24357abfe128fc625 9e682a0ce957782de7ecaef9010f9bf77483a51ad18b513a8acf4ef2c7562600 1 db0d2dd9515c7577b2ada453627af0762ea281cfec691f15daff0f52394fca0e15e77e76f95291c4fe218a781fc5e59c686395abaf78919f65a89c907a29ad06a8dc140b4baeb9a4d79114bf3ce5f64cda97565cd0b7f77336e3185a41b40b08ee72d407cda37652362a57e961bc03f855a43bd11a4154c2706bb445a004a10c6a8be05349fd52b13382abe407f08fcd76e82f874a380be55e02c92827783405c5a4c201e4136d37527fdc5450fa536dfc5c029b85fbe2df2a451c65fcd1520a73d6d78fff06142f260eff5f68f6c6d6a473985d4259a50ff54a15a7ce0d9a053621fbe346a8fd4535137cfd5198a1ecc5cb2ec7e8bf4f7e48b4d4fe8abf9f05ed467dab5a373d91ed646b7183fdf5263dfc4ad173714ae7b236d1fe6ea45e08e422649589ace8c836c991b959155a8a61f658a60f4c7b1771a0b56bb37833065e3712f8a4d0f41adf484a03a2db86cf3ba761759581e9f339c8723133f2e707f5415c1e6d32c65f6917497a739dcb633a51be56c46baa4196e0057feaaa2f09efa4138cce6f362fa26d4664700fa5e7dba5a9068dd05e75279d7862b628e503d6999aeb7c7455fc1b6ef2bbe1605e0cd615f80c17ad4f50f568c75b8b34d40e +generate_ring_signature 953630fc2044cd653f7a0ba4d3413eb62f6e17867d2a24283d65c3ce6318f887 e901f99a3e172a3f7de853302c9ca5a1ef79f5762c13ae654c4259e8cee66e33 4 de453f88739ae0d8339d5176fee305db6c4558ba79cf29f7cd9d578ed6f93f73 2662a16a5721e8273db4eba161eed7f6c55372ce1b57ffab8347f94ea937ba77 9b838a770120cd96e91ea3753b6728aa50c8428a6bda2dd55ebb2ace9abefb72 7555929a6e983390a6d99b9fb21340bdea436104456f7345e6319e772f17fa82 47baf5064573771c80e48605222461043b10259e4b1d5ab00ee4c5109eaf0509 0 4fef6525cff944181de55e1156888fa0dd29361788b954996b257049c1df030f9319222ff4d5bff80c67f42e1c30eb5990db306329b006babffc92a0a373ee0d95bb38dfe42b9095c27d51a8d4ab6504f2b3a4e88912dc7d44636ed6dd1d7c081ae6b0f0c7ae51fbea62feb5c5598265532b4a6c361fc45346f02fb9fcdaee0bd094a11ba49e0a635affecc691e01515bdc139a5761e263e87cd26bbb4480a04535d7a04daa59b88e7b7772c450059531b7aad08f1e313792eb5f106ce52000a078ff032112aaba4b6b461b650be2607f562e1f00578e0043952cd49db32100e1401f313f1f56ee68795aeb76b51a09d8effb36343b73bdbaec36ee10aa6a909 +generate_ring_signature 389429172b85700ef011454e82461e30d9df22fd1db8366c615775683e19d8e0 999b6e454561a04399883e2349d36225d208c3426018c8a5140b5ed688601c1a 1 ae6e602587bb3d69854b8ddc46a38071e6987cd8dbd8e5317bc22ba20e568653 1202a2169995970dbfd88fd2d6bf35111e1a13e6b0f32b8bc93d3b0afd206804 0 d2af7e29bd8d3288524d7bac412c2959dc4e92bb892d571dc8c4e814e7648e049d6f48f5abfe2f9b1568533ecf245067b65bd67ee501374d150b0ff0b43e3c04 +generate_ring_signature c37605f12deb3cc9723bfbba6a865ebf42b6f55ce8f3d6f097771ab2b354f3b0 fae39afd0699fbd2a2ab9403bcde544420b66224cba89e6ef4bada7ec9f7ddf2 3 7f973eb742664756b20e8dfe86e1e12a62ea6cce44e987474d3f0d5fd115da7e af79056ddd39d65eba37874cb91566c6ce0a60305380f8007535d2e7dfd7d8fe 720f9e325b8d682a17ec1e8c9d118535f2102e9e03b570f5cc74b50e8ee1d12c b57bfcf6db2519fcc43ca7650cf3248d8385e5cfa280d6d90dad4b2154e90b09 2 4452b9de98fc99dd355196ef6b6b8aa9a26b5c59fbed448a1a707d7f2fd94703631477ba215bedb1a9aedaf0f0ea6a8d187ccaa3394394599da419e914822903704dcfbbb267b0b11bf370666c3cd3e085fbe36af49e6c108cfb729635a4b306399a12a1ef3fd6669961ccff82ae74bdb2d35e6dcff068c2646c78847e69e60403c6abfe66b905581659e8ea71b1c43b490783f28b8aadfc83145d5d38d7190e453394ceab615f2873151441fc0ef3ce963aff9446eaa8e34e609d8648ee2b02 +generate_ring_signature 37671a1d249e1788e7c6193b699c5e54f6be1ca35714a3a3e4bb5f45572c01d2 e0e86972cdf25612d13ee325998e27afe1f5ca20cc558021bfeee6b8437062d7 14 f41482621e6d7291ee1837f511206f285589e82270c54f3528e9188dc39cf6f2 ba41a5e2f314fc05a8f86861c2e3a05318556814c9d209e4363e363476591f6c da784f4125cab4638fa28b78a7e1040b68b0f4b782bc53403d9b311d1c8418ee 1c765f10d5c8315d648a1ffe3d3c81bfdfa6ffd0e32935b3f4e63f3f563afbc6 107c411025027e2d443aef21a7587dee87fcd8d0201a679dbfdaf4f2ab19a2a0 67d5eda8542bd0951035449c08bae062f28a7eae03acbd4a85c70e98321df6f0 90ea7de0f1638302ba6017957756f60b85de1f18d415b613c0c7c9d1fee2b020 714f410e3f73569ba9dbb4b8f973b1432cd573d33d24ef35ae7cd2f7ad385cee f2c0ee9553a23167d0e49f274abc2dd3976c1cfb91ec2254c8c88477f46ab46a fd5de7201cc934f74b307d5f5f63758e633e9d60fb9d3eaee47f2fd2856fe9d8 086925bfc05dcc018b7b0494d8743328f5dc9b7d6078c3db4f03e2d8be5e2aca 8fd012389212df1806ac761c9a70f1e358870177b639ba7431cf708279e1b77c ae9c72de597032207f452e71b0f4885a4ec933c9908068b41e9a1a5d08eddef6 668fd60e30f1bdeb678c538fb5d94a1836b1e038f20409c6608f857dd3c47bcf 1a128edd23b4090c4eb20ad7ed850a3bbfdbb4bf0ec91066d34d3b8d90433b00 4 145e0ca5bf43956d33a228e877ed4f5fdeb3a618bed80c5e67c12f59c3c9c0008de90e6b9e3444ed6b027d8bf42798002622eff383220f1bb884fe3552040c0c6687e2bf0a5da43ece1eed36784a3e31a7c6e8e57643b4e84f7896e7104468041185477047411afbd7c0aa103b812ce0fe91b8e141c0b9812bf0400275fcaf0a8cbfa14f2758539eb3c62041cb9c511eaee8feb4581e0c31b1f56791986c1e09cae2c1ab6a4158e83016d29542da36fc7fc15fcb13285badeb45b462c231f10513667d398826b6910dfe0d65e57da0eb687a31893228f85a81b61a08e77af30aa24302fe34a3cf2b4c200209c63737209619bcee377a31f436036767d0cafc08adf3fdb638127149f4f0fb68cb23593f42de7119449047a436e76757e610f10a297fa47f77f6a196e67ace0616283390e4468587874bf4729d37f3a7eef2e207bf069190607b448d0e948a18cd75bc5028b8f8fd3d8500fa70c5aed87ecae003dcbb694ab60cae0e074b98ff0631059f01df0b83ce70a12d2178d4c3e31b8001e10772fc6cc5da07abb8ade5c72d25307a7edafcada5745637398666e0538c0243ccab54690242751b84a34f0f7cbf6793eb7c1978d57f2c4648eda51929040ca1dc2e63351b213ba5b9242d1bb82fe5ff347629f73dd7e846533790de4a0c0ff0061c9ff5f3962ab4fcf50329c457bdca5e1fcc102da89869b98d4ca0661701a51576c8b5939c98a57896052f84e4158ca39d8456a3d6ef78ec751a910ae70a1ee20227067149ed052e4add5edb294875b5b8a27206ecc2036df19cd42160028b23d984d1900112711083a1465576eff6b0bdb8184479079ddadf3ba6da74058118583787c7994d247562a67ecd8e54e316890cddc8698562a909d74e48390d4bdc3489613e9b6a5e48006163f1b0d8f93c4baabb54d3b19e557f93580a1f00d1d9999d7f6c510a267758ea5f4e5155d364c47ad0405589272c5ce3b0af8908b129947c63c6de0658084111b8e82daa500f2c0d487f4e46f7780699d9be4806e77798a83d366511797b48294a314ec53345db6e10ba721cb3070c2d3b17e90b31ff22e89ce20bee3e7f0b665b3cc60a8ed58a413b13b7368da5ff89b32ab104612f092203d286f9e8339a998035946b6a165f9182a6eb66d2f7478abe8b660eba8a3cb19164ce0f43cb350b3547bea194fd120bd5110a100fe6d9738207ee0a2d8447a7a9de8187e2b303003a246b0a7697e4bc3e3ab082601b9edf190bf40d +generate_ring_signature abd653f7ad7fd63df48ac6a7e0192cd459c65a8240f9d2cea3676a8bf574d3a3 4ec5408a2203919766e99425df38cf93d465ee4e7179d4140f743b780950f7ff 127 f7cc7ad1d5529686cb49d9a39db137ad1c4c480bad36c87e9874db399df6e2cc e5603615a39caf26768f8f006d8e7fbf75559d8f20f1d668603021cbf5b06ede 35c398f97a395727bb667e2d1b2e4ee561ade6cdce28decb955ea40e3dc81b19 39f7d4ce324bfa1d831a3ac2a26784f4d9858a1a7ee913f0d753560f0fa205c7 3a70187619d98252083118eeb6c43d9f23a441d081c06da5cf1538ae2f514b46 324c0c07fbbec5cf493db7a55f4f2944a88d66fa29495c17d2ddd7beb799a981 8246ceac0bfbc5c3bf76bf1e188c6edfcbe7e8a8b92c0930a0e9f6d7d9bc4efd 46510f6fe33f0246f667ecfac5e9d01e62f9a675b3c9b43267f7ac6e9e055eaa f0531b6c747fa76dba4a033571b5adc2affec1d8ee7c3172aa25a35643118ac6 c3080d681c17a1a9294552e508fbc3a0183a17a1e2708fe32552580a44ea4cd8 dc56dfdce49cdb9221d288b78779a6bd84c4cf8e3c5d7dffb0c5ad507715f174 24b46cc382e88800f74d1eb5cd8f81cf60002b5ee67fdb2f7fe69aa3f921bfe5 28672e60cb393cd812809e8092b639c73e81cc2e0f8867bb2f136cde06c4876f e1ade5706d9f09a98f177a310cd7b8cda1bb72141283205abd27a1db228d8778 be543df678f6169184ce2ad48e7830679ccccfc52a63f8feab3d5ee79f0c1054 b464788b8d9c73bfc942547d9a2726e2c63106f18e322f9b690efc813c68afb5 4fa40c9e5d07a7143203d40e401198a589eb8751b4dda6bc8cd1681586150590 ae63e7c95e21ee618a4bf5f51a9bb1349a56e13045c1ffa54d51fa732dc88a98 788277e0c0c724a631417c4caa078aadd6dd7ccce6a2dd3b6126490f9cb29b03 0e7f64e648f936b92ba48d50f642d4c59bb972837a8d0c698ebd3f6e8d834756 8c9b5957c1c93c492cf3ec80493c9a2ca5c8a296b47c45a6ece5a9c1829249a0 8fba8d91bec11803d4c5762227b2a87248ba4f468c2ab1b4b99d37f605b6c942 25d297ff24cce2c8763580a2cac0775cd77a4625f877b6ac39bf8b8ca06eb121 983429fa4e9308edf662f138a32bb5b5d667fbad795482bba974751c1cddb922 a4bf3f834a9f55d2667a98958618e667207557636d212af92a5f6334c812f8a7 f35b326077681c3290f31fe27fc154716e58b7c9913a77cced12a56d1c492a9f 323c052da3cc1228ee8c14a16ad1c955df440e58cb9811673f49acadcd111954 f01661bdeab726471018f51d247c872624fae8e5cbf34071c49a269957756e0c b67dcde3c5613b2b3b599264f06050fc144b0beae6e234830548b6af5a4ccdf8 38af053d20c292fa28d084721700d138b911430c73f397ad67cb878e115b2beb 27c40d8f771f20559e775713e71ae041abe5e1814e6631ef3eee841539e2d262 7a3a321751c2b1e32dd96af7d33d60184d8d42aedcede75d4e22c9572771d6c0 166a8a31964185aab43c20927b122f4457005786e3d8594cc5512ff88f6778fc 37d33c370f64d5c21fcd80739846a39fad9eba9803afd1e3c3db6c933f600845 68ec83d3d23e6054492ea161ac9fffe1a42ca54f28038bb1c42d3b7e29fd333d 5384f0857de1ed5fa5db5755828eb4e117465dde04a15a90215612782e29f4c6 f93dfef70ec9bced9b7830d6dbce44c1aa4eb671d5ebb3b815d654224db90f1c e386cfd691fd2c855bf8a5c86ccf228af6d9481e38acf5cfbaa9d8accdb6f740 9380536decd8199c51bb9cfeccae5b75d423f18e1de0651bf9419cb9b484084b 4689cb44017f37ef8ef63fe68d4ad26018e7beaf3486bdab9c6c3c459c3e7835 2baa6a00de237bf2bb2fcd3d106601e57f33c27a1214ebb317f53f20052bcd73 c874b88ba9c5e2ce74996d132be4879f2a589b21efc30314e80d0cfade1958bd f1485b76e02c7e9b402c577182a4a941fd4c60618c6f92f7ece4f1f03caf723c fbd10d5ffeacac15e16cefc049ae124ee6dec10c66b6f5b62bf21d8aa41acad2 f48ca5d7ac83ea3e8d9adde2745fd6f50fa7639824db2b311bab61adbcce559d 0d676c1b31a9de877928419723a5eba5d37b2378d2b3d9e846b6cdc865cf6f6e b45daf0605d323651751912084602a122b3a39611bff1cdc36fd74d882e17201 0ef240390d83a22f5e7b9685545e94434d7f667141e55e9344d50753986d4a2d 138d36e66f4cb547e3d2e2e32856a10f04877cc45a4d94d2853c7ccb9c29a05d 3c616b6f7303de221b0eab85a33ce48ee0fb91b125b69c2bc13bde24316fb146 c808d1403d310e1128b18554da2a8dff773e7711104495ae2d4c4cef80c4adef f00a9429229a6c135aeee20efa63cfadc5cb264f965d5bb99c2622283688953c 9f763318111c6e949abe452fc29319e2c0cd855ef13973355a458a3b7abfa5cd addc8266481e686f0120ac9537d03348a12823ba9ad2064c8fd3c056af87d9c2 9e3163d748d6fea21146280df9c2b01c6b46300a40d5d313749982b94caf43b6 09daab1f1bcb2f17d13c0ce3024631c834b845ffc29656cac32decbdceb36dbf 966e9351376dd899b4b8dbcae80f03764a7f67ee0d1c883ef1ecdf62af6a4c70 526b72a160bda748d06fcba2acc635f2370c642defd1c19280ce29111912adad aa37714f944dc1ebb4a5bff29adde7ba2b3bcf1298930cce423ec8bb71b46301 7cb9588e9e2a967a8a5a1486864106a667a277e28f65db460b3086772f06a97e 94fb2fa586d2e0850db9c6d30b4cdabc2f2f12f59bd1c9536fab74d9766f134c 1c466c9515e20c18a56bfdd03bb7478c965a7d12d702d7d4957a63a560d10266 609ef0ebd972a504c818929e4b680aa2d3af312353eb0f8c886fce2c2ca18434 102793b591bdf4a26d4277b57abb796e5066edfc167aea07ffa014cb58960e75 4a94b3f8577cbcaec6b0dbee10ff8f66320707497543846b19aec8f922b4293b 6697ad22d8ce84fdb5fc90cc5af87d5d2d962de46cb53ca843e9938681484d68 18457de093c4a00394f250ae8db6cf086e33f6d5d14dcb3040efb86b3a3f0b04 aa9742d9efbda738d62ab7625bbabcf258ce112757d0278d160b15936abf3cf1 76bc78c14142bd24d386b391e510a448084640fee6d5c89e78912563a30c6082 5a31d2c3cee04912b6bdee1af63d791bbbb9895c97d40f64268d429c29142985 575e2c99943ee27d8962991068c135d934f8d7189ec84114dec86da9f8c86d7b 653841686d1dd0025aa32fc36e2ec002f602d6063433722d32b438d5e2d75319 522bc0507407007529b25d2eec729a59c0f60e98373a0f539aef20df7f53ea69 67feb74336641ee284e032ecba89e02428d2829af28d12b0e5bd9376a60be00f 3c865f1ca2bab0db4eff5eb9e4108ac90e1b33362d43910ed423cc670035f039 d6e4c9d3d9d3d99471e5973327d308fe8faf93727fabfbb0f748c4fc9fba82d6 d70a8e2810c33e4f5ce14666e6282b71db8ba4b4f25984761a61bf21a80098e2 20202ef0bd6bf3faa3dd42b314b620e15fa03cf525b7fafbce8b1f620c86bfba 8576732069b9c355cf922f86b90041baf4d4d44f79055afdb35a958f8ad9fcc8 5844694e707777f2f9499d4bdf209fafaf286fde9040b51657217add3e345bd9 ebbe61e0c6be3cee2b8f2825806423b8368a7f649a0c842fe537873674061d8a eb676ad1281e502de98d3e9c4732a095ad28db45c164ce7893547fe6d776cbb3 ee6ae2f3a4e2c55c347644afd43a8e3ba646259a4939ca5d0acba778d05249bb 377de8f232e2702372e38e93858ae04ee8729ff65a76fefd8b961e64c4eba497 a0ed9e2d5ff4c1c414edb066792f9026692e49855bd6f0c3d69a1ac2413feb9e 1f6c215399aec9b4d8bf289d372c2467f560896aac9b941d3e3c0646c4611f79 a98c0268b55e338c26e4494a4c8a8022d9653647103a0f1bcf91dcd8b3ccffe2 7ee898c856b5fdcbf8003dee310084f8e5d7553febd3d4d9a4dae9728ce7d8f6 e549734c11a2460d6a99322b248dc590074d36a74635666c0167f4d6455a9b10 801df1a8e2aa0edf9d00861bd977b0e4f5d369c182765852539e4eb98d58d9d1 6b17009bc4da1114f1c40a128a5f9238a5ff65e43b0e3ea6be474245c63be927 86a0be9a8e802b0a83eb58fb288e8eb0169085599fabe73df43d98dd538e4fe3 b52cc90b3082ea8bb1c16f4398fa9852f1d7bb3984701dcc59403ed0ea11756c 2e093e60b05a8dffaab61ee017987832247f2980afac47bdf83cf4d4c9b09fdb 23ccba870003d977788fb87907888460551f208dec112af68b493e339324fba9 4336845a60783d068c0693181b155ff8a927e2ade96175842294c5269bba5fc0 885cd6d8605166246c5ab4add221064ac3307ad12cf0d76961527722baff5b45 3640a825842e15ab7fdbd183861ccde121bd494d55dec192307f6e26d7257d42 51e8532b10c76386fa77df1f0972770a67b83d877946387075f159f58814e449 775cba5f1da6c8d6ec21a32821189f46c4acbf8248b236b1e14ae4550607ae54 6a4853aff074835c48ac5fc3288f185e2ec3888ac41ce5fdc6d7b50abfad9f66 73789c6ec8839b4003a2045a47c9602f75a55fd2632f4c1e9361f939178ec98b a548db9423100cba74cacb67611f617ba18b0b65d29fd55ccab9f81a0613f653 e89c5116940301a8cbab6c88056e479ef6ab6605b0ca9cbdffda08dfafeb697e 77ca3397a46092795d90efa06d1bd62da05986b7fb733ef666fd387e39dccb91 b16e1b9bce92e7e72834af0964871ff655d94223f297e2a916bc054b0c4f8a73 b53dc02837033e165910df5ba952c0ba9c3afeef594666dc0994bc8cef844cef 16e983fa02cf9303daa709d001566d3683db349cbca4ecc9677f7da6a6a873f9 dd6a59c284336c7248af37f73cd62638b4a1126028f3088f7809093bb9f40c43 ccebd0ec548dfe03c8576949bff066db53c08d2ea96fb7bc3eaa2e35bc4e549b bad4168d208766a192af7fbeb089e80fcf3ba50783063be866b2afe7f2bb392e d45a233a18f241b0ebc2e27342eb7d059257a766409c5471a725cb13c4331ae4 99dfd92c18d6d113ade3042702cbbd6bb9a79c0c7aa934fcba3382c31dbd7e7e 62ff7d2b14a161f835ba981bf2d97470a2df1438e2c1252441555a898e1e0aa7 ae772f8ce1d252f53aebb7307c17c4e0e7d62f419acc3a5ef596ac80f5f95d49 95abfd3722194601a8532044be8d20e29d027d0e2f4055bfcb51c000439dbfe8 3ea674df981be77af3101ee05efbada58aa84c552b85528c9e96137b0b317f9a fce584e406a2166a6e4bc7576e45ef838e4ce27d2065a45a19320a7baf163f01 0def1fecee4b5330a6488df6f248df22ba92af84bea002746a0b647e50941c07 d692409668548677cd700cda6f35836b205eec4de848518b6a883dd1270839ae 7e1d912ebaaa566002057b1a6a174539ea9029cbd86342a8aa3ffc748a36da7b c281754f3176f07b395486b21e69fe80cbd853438f37a25af70b8becfab39fc5 d0accbde0cfb2474ea79917c09a5c6948393d625f29affecf51bdc3578e93e67 0a299a649e18631ec310b45ee09d737eda2cd32e6ba100ed3bf0984ff88b18f5 e27bee21a8a3745f69a8bb71b60907892f43bdb270af1d4e682a28112c80e4c1 d8f5963afb20f917aaad9e0d0f329df6daa524c0c4d80627c537eaeb7c6eb783 0828b7115e8631dcaf475cebb907e6aac977d9938b7739617e480846e8b0833f 4c50e16ebc84e90f0d414526b053d3db85e9c0f1273217537805d9d7fc03d001 82 fd084c664397d05a644a5445e4619ab71b75c8878be8b638f6a0f4d09e81f70887323fd93acb4b0584e3ab1aba68655e90427c9a1a2694ecd77a80d6071a8f0e811fdef1faf5a2cd2809eeaea033e3f918fd51157d0538d61a22571313ed560a33971875c62813364e8aa6be1fcbfdb3cf8e261994ff2f3916ee0ec7ac9ac70dcd70a95bddb71fd57da3c4e3d22d657b8ba8b97017d531c64817b6c659f51309bc5fb75e2223f5becc6c1addec9bd113e8b662fb66a66e8e7ada31c6529c4508b00fb5794c7086b8b9fd719af69074d01f3329ccebecef1479ffbf02abb6860b1c6b4af6bdbb7dee15c2e5e824f1a3d8a19fc8e8fdbf17cb541ee6c8edc6fc0972034bfcdcd8e0cf6568c5dd8815f3821d254a8fdac4c4c9f59075fff2b3fe02f371efc09679bde59822a4d17f37383bb528fb0722018ed742823aff2944830b818312cd8553bac764c2aca009f760d68a45c0e9b6e8ad23cbda753edadf030ed290d87a9d7d91c864655460fe6b91f412f51263baad7a3fb9cc564556cfd403d41395a65b03e13a2a5a0d318f728c494548e83f0d603ef6eba9f0b30965f8021d9c1559dd557050df1aa1a2b7e2c2bacf2aafd842645871d058e273d4802e01e5af08974742816515cf26a531e17834ec7e774c7dd42b1b7e327c0f1617f70a9e5aad969b628dd5549c859d76f097e0b047a025beefd8f12ca947df02c6070ea06f65a7253a736ce1056ccb5e3ff713e451a85f97ce54185b54019708b8930ac2ac265b7559df6d5b13354b015c967c0227816e7a881cfe073f2d30d72b2901b3a82df48bc56331241e096f22957609358ae75b9cb5b2be7396cd960b2541084953ce2e87f4f7deebdb9d415a050e8f891e8059bd9c923d861b4c479386b4012c0f25b5c00c54989d377d4560ee99d973e57f90009766834f0765768e0f480f414f29416dd53d1da19c27cb4b18fa8e6c0af16d34e787a463e7fee3118ca90377b1a51daae573b5073d0ef6af05640f78317823f11be982016e042905da000d598ed64481bd862d689b25815e27a97674cb25f4cabe85803be0239bc1b2b909828417a1cbb97af967a3245657318f5c73cdc436738734bb9c0954a63879330f26551a552f0d9bd2fc9ef55778df4f610fa08297103a2163dfbe15486e908a05de50591c9e5c000e3c89e45d37dca7fb7d81e42d213b0f8ca6f1b57329f7390cb92efe1bda555d83f484ec9ba003df8e94e16a58b88de6b475362d28e6c2e305e9cbd35fbde879713d5f3185ba07f460f0ba71977fae4e1e8a78033da53bda062288073960b02eda2a5188b919342da3947017523a52ab1a4da5b421ea726e036c75a5801a209bfc18bc4e010076e51f68f557c14e4f41cfaa25a163bb4916034a0904e7d2f01f9203fe6e89c0a90158fc1cc11d9eec28f931916cd4e906ac09caf1c454d72a44f52e141d3a7448da808061b59c2b7f7fae9cb96f762377ad085994f63970df0559ad8f8a84ef17ff34d3646a87641935fe667459b8744a3f081238a79b3e399de8560f4f7f86e29ccb2285b5e66119ce55df50b8d86c926a01f8d2fcaee416919827b2fce6b6193d912d282b35d4176224da640ec70f61c406af8dc514634f852cd5364cc625b9b3bc6158a1ab8281030b5c8555f85cef4f0bf32ad788cc4e7bb0d506a8e612e439854ff7e8725779d89041806cd73596ed0a29fcc805e9a15aad6b8595f2c74b48191fd71996460563df11a8b16a6d047b096d6eb0dcf97c901246b2405d6e081aef854ee7513886e9ad523c8e9876f03c0da84e4da9a01b4a577168ea293f6cdc8da22637d5dcb9f33e3bbfce6fb42cab0bcf20181635163dae25f47754e7d45b246bc422ab9726594cfbbf9a15b1b04a006fc84ada32d366fbde8a5c3a2275ad295a5dee6b9e69195f04f197c817c1470a4544d9183502d01ba36abd8e90208131c4957cb08bb7994aa66a61caa798940e1f30dabf6a797301bcdfb04b9ebdbb6bf2289646278e90fb185a2700ba1d8403eb3404002393c1530f63991f175afc675a20cd4402ec1a1bada9e6e4853d5f068300314303312a498fc5a2eed1f5257b5d9425da7a40ba93283fed77766b20069453839299fec4eda12df847453a20f26d2c614e39c8c0d2f36dc08af23c470d0805c49a1a04a71fd0eecb1c1a39f95003892f1101daa46738247362efb94a06bfa2a610e52f7e798b3a98d0953f76cd8b27a58e492b1e78aa64abf30a316307292a39812a6a3206e8bbf7787d4a5cd42e60e9d0bf59e949c058f954efcb57040ccdabf5ff5244083e4074156bfed2640b75fc694b3e1d17f52ee37512d0e20802cdaad46c3d7fa436d6d8b6323566b6cba14a504983eaa6d0fcf0dddb53ab00ea2f5c7269b6a8b5796b55566c51de992166ecfbda52e138e7acf1dda84dff050cf4c6e9ec192d9d774d7d6f51491b676e8afa592e9a9dc52586d0bd7686f302695188350e3620e559ecae000c698292d7e81ec8bad50239c0c0c8863a85c00d286115c7de5139ccb1c889e2a5e6235bea499c04e5ec369e0e761e2683d5b8060036916866dea40d1860c5f6ce5980e8dab61aaa406b1fac146300847a089806d91be7394c54312dcb6bf0e218d88bab339298d8c69f6ee1ac1b24614784bd01c387dbfdfbb056e35a5fbd387a6a7f9ab5b2c24aab2732e4f25dec1d3f31fd03aa358c1a72587296086743bcef616d05fbb25292c2d3cd3a9da32b2db6a1700986338e5c280c660724497eb0f0751b42ff55f2a2fce5e6a93ef68d348e20f40386aaa3dde23a7936a74a47b632f8ac89bb7f32a3279d00022932f9c0ff32d707cc91c527a5c507cae8b4c904eac893879e9a09854f1688c7143fe688a35ef8091857a0ca007123d5c69efc6de8d43ea3e477c9af18512403ad5a426533e89f0abfc914f8a2dd6a54da70eaba6ee9440639458ab12234aab3e44ef953419b6c0460474681609cca8a841ba607c2ea91fd20976e421a2d340085680bbaac63350e02f48fd6a8c6799218416f698f13ba3e14ca3115aa96abb4b7e89f000115180d9584c1480b2616b9e7ec0d37c2eaf7eb0a2ba7e838de84281aa980bf67e60804f4955648d75dd6511642fd1ff772230bdb65f8e755f719789d1701c30360bf02e20b60e20b26f7208ded3bcf7776075d97462e268448db8adf1bbc649acb9a0df1a23132dc981fb0b897f2c93ed635311de94829f024a7e8060cc74714ca010ff8ae0413cf0f4a378bfd07323e318ad186f7bc25a833c5ed36d3209032d9170c1cc3a5f7953fc3e8edefbfa835258e322967ccb0f8241034fa6a36b93d3e5e0a7270c01c6dd4a3d576d464dc368c41e96ef686d31d846365e18b0822ee7afa084d4c276bf832f306d69fdefdc419dc4239b94019e8d108543f4668078050820ba13f469fdf41b088325fe90e1eedc2c03158fd8cc324feceb27aa745b7bca70eabb4cffe24e0b5fa2814e7f0b625eb0f5d4a005d7cd7528b9cb007af74917e0f20d1d9d2a1003962d3a6c9e33d1e36d15e81a26b8b540b35c944774a5109f7037e28452ade490c56e08c14e7d9f481ffb49f7146dff13b2e48533e54deb6040a8e710cce12434b5d11e4f7abdc7963528f42741b6b7a52bc1eaf81d0d6b833084eb864c30a01c60fbe36c4bb90410e0915c121386c735a7b59d5eabb350e0403b7489d204d5bfe8be85e75436566607c8ecd0bc8bc53cc41890d7b20b1db950982aee4933865a304aec8c81a83e49bf6a1330d0b0beffb133add63fcd9ecc400a2719348f1000cb9b296ed33ea306dc9fe1b5e639c689b9959640b70b47d9906198c4e3eba23cfb7f2fa45c9e5cd4d7b163aac12b139658795274d33a7f8e90281f78b40762225e68cc880f4434890df6c44020027325a8266ec379c984d8f09cf5319b419fc2bdbc518abe7740236853e691540a85717f061a218821f235e09936e427dcb532fcc9a9708c569587b7701652622ede9bb16efc9820e82cc2804d976a866ac237d4955f39e27c3f0cff435173e9fbd778237b64460e8d502dd013c97b8b863b888b40ec9dc946d2e58ffc8e554b0f4459c772962387d416c6405ecd6a9b167198aa5bf4e92075f00a93fc33905846d50182a5500253c9d93750d8d72ea9d2eef4b12a570ce1ae838dc902693efedb9c8deba1a24e4ff43f79906983b3b062563a6b359eb8ccca0eba1767eee2fb3a66642d8fd058d47c9b42301ce5d9ca6db48b8929e222b81b2faab5a428cc8b8f477e1e464ad62998173240a03795ae90f21c6dfd19bbc9676ff9bf07d97dedd957fb71b96c4eef3c2b3a50bed78ad13967b339f1565f682da373ed3496d82be68be00e59c81e495cb089501621e2e2675a2a2d2f335808ea4cdc236823cd6a3868de639d5869d18a34fb307d9ac341f8360e85fc130bc6b8755fe475c9ac6c88854d7f2a33298c2d9892b043d1d66008622f34065e4d837c2444356fadd2e26ece01a6259f9b6e0c4e1010033ffb401f4056724a07769565531a0a6cbd73f66bff3ebad1cd6431d33ddc90cd904696c52c71178dde83a27601fd186916d781a44f552c957ef234b11378005e293310e3194c0ae6e0dfb764085f9a2372da945e01b72bce7033238e15cf20f62c4c9134b9187de1c5a263f542f238328842b4e126972e6a3886385582d980306e07ea52c75618229ca83279865fe5e5a1d611f2efd285deb8480033ba8170fed74e9ead22c04d1412bfd610bd320369105bca72a00b4bedbadd16a50d58c07c84018e0da506ec11ef232079fd2f9ac8d62bf4d875f2dc8bfe62dad52c7a70bc7d6b6c7eb6160a6d069c3c596a33d5647a14b5d1461b41c550d74a9744da90b783030dd3c2d2f904261caa44b26064b223e071c99ea2b5439edc42521f1ed0d52c7beb8027334a223af2d5e2055c1c6b9eb43c75389ec246b82835677d6970e121646e85f7401e30bc65266ea2164eb6d8534ab3827a9360a8848344ea27c048aec4cdd1a1719ff4cf8075ba4f4c3cc522385e319157f444a00ea63d16a470ab0fa44153fa0d211371acfd5c28d85470ede784da43ab32e79e335176ecff101968e9e0d77e45f9efd4d54368c33773adf9f189a0d4b80a2dbff98b1f67b5404aaeb17d533d9e0957adaec0e8a3c8a34b168d437485c9478e7c4b19c51e6810c86497f36d454de4d95413c6d02efe3fe638c65b4c5204660f7366f2358ef7202b49feacf535dffd2090db55a1f3351825d94b528db11b4f681bfa7b3df42f80d4502f7e864021ba6707cadbee6746e0b2641ee0692098ab45e509b37d795550e0c3effffc44daa7227b6b708a3d628498cbfcfe13eb0549e098de5ac9ed9d10f01a3877be157ba860ed9001ad4a68b2cc035d2c2234810225c57ab774bb1f006fd438ba034cf055e278b86cf584109f85854ffba9de710b68c3f835de3ca1a0b9aed77ae64f4d752fcaf489036d5cedff31036a40f3b4f16304c6a31b4b0d80c77b7b5ba7f8656556a21a856538cfe251baf3a5dd820dfb2c1fb453f0caaec020e0dc0fadd1683b92eccaff087dc9fc6977b6f639ed2eed120e6ecdcf2880c050296865c9cd088ee84bd52917fdba4bddee173346df948cdc89c64a4b861d5084559666c6ff130422d0caf43db44c3427a4d5657d4226ec51158b690ec6cd50b6ff6a016dc880a687a10f20fcb2057317faac6506dee3f906b5fe32b8b416c0fde397c8750a61388c798b2641d09f0bfacf8a071bd0e32db64458464967fe101a3cb5bae64b48558e1bcb6d01517915de66c140f287ad79c39a957b20ba6f30fc36303c1cffe0f37606eea932732a7cce7d0c8a3e7f56cd254c429f8634f730d850f7f706f8e08531a4252984012c7cbcf51e6025b095a9872e4aa4fefe187032652c9a6d4088ce17470bf650649691b980533e671301cf7a5ddf4dacd2c3c0bb972445be569528ac53f5bb3e0b426af8ae8fbc2c1b0ff626d7670085fc75a098a5c7d5cfead3ab28a57ef6ecd3aea2b66c79b6bee959288a9f6e3967a06010070e3433961e33ad162ea260dc5235cb51233e52ffb770dc922b361b47d80e6073c1c5cccb26c0a4e5d7af61ba1d6ea820cf3859595c3d994f16fecf1826eb90b23a191ab80ed8f6225282ca112f6518649916fcfef9254d542e30ef2d76b1800361d6fb6a9c559096314875aec90b1baec12d4bd8e8a5eafe2902ae146396404bb7523b81d55d59ea7cd196065b89ca9256f34c0878048bc7baa53763fb07b066f59bd9397a9ddafb4986fb5bdb5c6a68b52ee9d88412f506917a69d7a098303de15e1349801e828be2f3d46cfc51f4acc60d3a56ebf7690f6ec6e62b97be202922c4ee870906dec7a7d81a7575f62309d191979eaee2dd8868e338a17b18908ac26cef563b488c775efb792bcfe501898e4b1081f5d8168f43851ad7571800cbcb790963072d28accef73fd99da11a180a5e73c3b136e088dd3109a87e9170cfa181cb228d01588467846faf834fa8caecb758f39910d8e96c21432f2d61901560d6084e30073bb63008b25323457828048a351e39d23577ba982c29dc2c9084dd3de94dd6fd57d3713969b059398bf7ed6f3088688424e03bf0f5ce056110dbf84cb9eef5996e1a2a934a158c23a8bf009fa8ec1e6463ffd67db8628bd8b0e997f21300fc9aed4008581b1d67e2a5afe9742dc2e4179cdcb6df9701af3fb067a801c702a073db4cdadb8e9b5b64988306b1de13744f77b4ac6e71477e2da0ba28007220cd9f8b83c811b86f8ee31b9f8ec9decfee25ec0cec47465978c8f0035a90e7e02cf8ad1ddcc3e41af3f90a78b0f5867a3ffb620aa412217dc56360621b5dad0cf8413adfd47202e83410edaeeec32615e333fd394ec754bfb694a0642d86ccc9a0d98b0d9495f150086f5fc5f20dc09b734aa640faaf2f6b6247a0ef18ab7835892733f1716de94ebfda388a9390eef1f9338238a131aef374ae00df939552ef1075de5a600f917b0f13e69186a075629cee5a3ebd4da6081166908330a6288f1a53ce312275226f5aa382168f5ec6444ac8444048ce859df7e820b4f50d9ce8ef300012b227075e6e2a2cb818a3eec6c6d6a5dae8038ca3f9bf504267d9ce0c37e69498039124891027c11278fe22774524a0932ed77f99d8bac0afa523094a8d0ecbf6b3930ad084f7cbf5dfacaefab0e56c27a1becff63260208bc624befb99b5b5f3e63960941f370d70afd8d513552640978100cfc150b0601e584d598e632cd2fdc39ab91a0d5cd9cba480d6682eff3389a2d3e168eb76b0bcb2eadd8b48e43df853309d2ad2783c76e50e6e1710d48ff8446a7640073b7008de79c263d439f7caef45091d8321b4de087c8509032f5c05482c67da36bf20594edb584a927264069a1c31bd65ae81de03dff445acb8677feeda57c6a97b102ce3c43e8bdc8be3e366eafa21682583fee68b831b4344d9e4d55dc792a5be60d9cc25a374eb88386ed47152f1a6d67fd1be74d91a23693eb7aa6d5bbfcd8fb0ea587266278f698a6500c4c23fe8668d0827c05de246d2f0f59bb005a3933680d214d1bb3cb624b226c21e803ef2f69cd15bdea593ecd27a2ce550b1046eeb50d7f4e7fd5948dd78a109eac27a0ad76be9d25aa229dbc0469eeb57a170c9c6e0700d7414dac696c2e043877cafe52d3f3e396c64134b1dda19c85e8541a73540b45b068cb3644afebce94a24ab65888834476fae8d6830eb7416e09787a9b1000fa17e7d99b2a3ba5322bf100cba7285accb4076dbac9585908b044a925101c0dffe83e945403fdee3a8aa97f5acf6939e6f9893244e4459df32dab4ce705150e0fe137b81018cf05add94ff99d166b3dff07e9481bc7d36045985f5ea6a71d01e5dc8a06d65801c31352095ef736c453456700b51e5334f7697398ba1f9d450ae40892eefff78d676cc3b291aeba484d7c6f101747326401931a1fffd9adc00aa637ae784ad424e1b1a912f3a7267706d68370761f3fec379a2c4f1f2bcd3704db2d17db46ffb2bdfb82c0e6d6e742cb368b39a4e76f663d801004c31ecdec0da4eb79ceb526fe005ff8cbcb33a7cf3c8fbb6792fb5fa7d3878357221997d8034e126bfcd4884f8a08557637bfd2546b7cc55a3f76cc4b1f7ddeec632fef7c0a16c92bb4c42dd3cc93dd211f1e44e42ee309ab5f1be2b33872a3e46df8671a091cc70bd9db3be7248a4acc2c39193d448cc29d63be907a10041a25dc4a7dc508fcd71212c351039fb80200b7d031b0e098a3dfcb19627ef047ca56cc5ba21303a0279587c03a87cd1de30e44fb3d3baad883e7d5381d469669b19d38800f7e033e1e78dca43f7ef5b2e58a13cd9df531b79d6d8855eb711bdc52258a9ef810093ac7eaa4d6969380a2fb89250d204550544860aef20dde610f352e887d1d3402c07df9bb9fc3a582bdf0609cf76853581dfbc7c068ac8e47b9be82e7cfaa050f44438fb534c08e84bc3052e130e10f555b85ad1e191f180042920f9e4a55cf0e2a6403c5eb62502835dd66bdbefea6892bc4758d6c505dfe60756d276493ce0c1a4304b4bf614ddb558a841ab7c08db5b4a6d721b0009b03adcec52e79b90f03c14caa0ce55af33efa8cbd9cea826a4df383b0d5fdcf0702a640e99b41a077015b84d7415d461e0c3f1be93b544f5195f775968221df61b9d686f14c35552b0e8a1beaf68827c429e578b5b73a3c5e2c5f0882cd3502e93b51d960eabcf73107f030c1e9dda3ab056dce89f86a5b246edc40392bbc1384368fb7da7a988ae50d3a63dc437e03e96727f43337775dccba1e38c70af957b94290fa2985231f4d065582a14a05614ad207b9defcabc16628cc04afd522a1d7d3d8321ae9c6311a0541bc7af99c98196fddfe1eaee74c3b1c9cb033b77fbfbda3342059439fe9e9029801c15743af9341d3e49e55c4bbbdd818b1b126763bb899cd1c78c62d7e220ddd1231220465d7ab33e66168338a9939ce17d415877437531b3424b201c0d4065828734aab0415dbcc08f019b55f5b82215bfb81077e38831dadf4441ae0c504394b7e89836939ff2d594880612f3dbb076be9970bfc037c88eeacfd25a8c003873383523633818340e4783327cb55ee182e056535816c4fb1dff9a42997e308d09bd6d61cf01e6523c92b59400c0adbe6d45b0f9fb7a13871e5779981e94b044de402494b200fb31f51b1cd555f68797897412188036726f125ee0b64da13056d7e80484f5f1611479fc592033512ddf3d0469d104ac149b0968c3ca4849f04b2ee55599d73b29b911175b69e2acf20bdffaccd1ac62336a67755f107a4cf00b117924e05e5ad2ca073e9fa719003f6c0c325575d4b6b99c469284d1dbe54091d16dd8e654f02be709b8e6f58e9f90a58c5c53710ec4dc8dfc136abf38ef3033c1cbe049f2361fbc192a8a3467159b9a3403bc608592ffc1fbac9eea8148d043cce444bfa2716cdab74bbdf2e43918e20731ed1b6f2eea45676227552a69e0d5e32ae384626bea99ed6c775943850d9883730248c7fe65cc6e862e7093db9082168a910480bb246aa6c08e47ef9376eb9b6ea0b471a8da411eda068c79af5097d8928fd852770e8afc67936024f1eb489e610da2a816f5c4230082290104b0f023a2c810981c26d9576146d44ab5cffdc414a92e309a31a4cb78e2da6720a0e4eb3318e6f58b44716885449175d8fdb343ea1d96bc884505a4636d49214a00f3dab8c48730000c4037d77e15ecdf1153fddb97e3e14938b17858aff4b892b056d28fb3284f370b94fe9af7a319d333eb13196aa4055d529a855d9976048130c24203da087b5e91aa9df7290fc93892512cab93309e76020391ed56c0641df015c008a9dc2b95fc44f8cf0ac69be7c4603948f1cc4b5072d19cfaa966f8cf507bf9ba5604d6247d7b897d0f32a8fb035b7dfb5cf276e42fe0b7442d1f092a1049a541fdb1761bb9f54662e80b786c027da502c39146bfc5287b5abae1721b204b2b9af8a8c178937d6155fc88faf7f1404e91cb7e4d295d8a8287c21383366058f0306529dde88ab8a8c9b6ca3647e0a4539c9f479287bbea67c29fc352f5b0097fd382d4e0d68d10d85eab181c7a1f43dfb5709f5bae0adf30b245fcf25f407086822d2ec8cea0e72ed4fc1dd1615e5dd2b79b28d0e9a97914f24ddf85d930c3fd0335954ed4c83bcc9662297b6c32dbec08fc303ca208eea1790252c838d0bd3fe76c6a91165051a39d775c01d65c2d926bf6f250f6abaceff71ed884c0304fe05a755cc465b3bfb04afd6eecf0966db8cacd64953969ba2f688f5858a32045692d0b393cc68de8f9e014c9f8cfc82d18cdce0e2184345a4b3eb9f0992d80966b9551d5a8aa0866beab77120d7b18c3b8eb943f9a5bf79a5441983bcc3250d9a095ce348b263c67488719795c6a0261f0296d85b5bf9374040274da4ae320a5d50ad8f96ca0a5f644f837d55fc1f11ed451576ed2feb697814c5e72f48340d813e48912c75000b35cd14a0459e90b63ba21e2a7fc4f3cf69078931ecad0a0ae5fb72e7f4c4453ef29ae2133ac7c0aad70afad5707b28e657a76bd8daacad08f58c4382a374887182e8e167ba2c09a945ed23c3938a4e806026d53b01186508ebb44636a3a81c6d88974154bcb981544893275401958de595867d81b13f730ddc9446e1f18ece26e0ea789caa8e9725a4aaa277dee3be0d6d3cbd65bbef7c06c973dfaca004dadb831abca4aa30e7d318fd1dc05690242f880aa9949bceeb0d5bf0620ccefb0e50f709f99874abd55deb8432ecfbb91426d4eebb959ced4609ad0681d81c230df81f5719e8086acb122f1cacc1a2c679a2d27aac5acb26a909c986a83890a49b1bbbeb67f1aeb77208b00a201d70ecd60f3d5e51f87173410ab3442e056fb2845748e308daf5dd7b60aa6ea973784c08913d5e970f32e3160f65947585d6abc7034ebe1baa675487c236e718a0e4f92f586a7d415b46c52e05f46be003058d4fa734836c07b660024ddb827e0d81b7f836bb3deec6015eaa004d4808475ee9b1835c172801a0190cfe4b8ef3279e4ac3dca3a95f5b13884006c52169cc390a884c45c7fe6666ed9a068c90856c94ffc8cbac0530ef5e672e0637625cb8b9e2d40766485f8547cc4756e29fcb5c9dff0f0d6031d7d3014bba062682d76d6d6335a0abecfba8a82ba69b7b5a26a2816b8673967d8d56fa5b110c5a0d59a5981273aec80b7d020e55a448a7b7cee91dee0e78933cc6ee1f41090f8caa25915905e576aeaa3e8f2ea92cd989ec2e18787996f772e119703d80b40943abf8d7135ccde0324292c3b576014ed5289c6e3c7b5a77e8711c4433a3aa06e9f5719f2a7e09631a05a4ff817db2d0546c4ae6217810dda47e5f6309928405ea9ee251f0a32f4510db2329585ff35810babf02f3c7dfb6fc22d68f5540b008 +generate_ring_signature 1bd71e0dbcfead5409660f7e8c3127a3d0c42fabf9ad6c0d82d88016ea624ff1 df36b0898025fc214088fd247b1b2e2c49d8b7112158410e9c2bfe9922c43dd3 6 c2c9a12dbbd3c694aa2dedff6015a22fd8a849edf52bcc7bbf8a05cd53f6ff23 437bbaa4c47db3859620b779dd5d2a04962d006d16f9bd04a7a6593ba645794a 4a32f65c25000f76a5af3a41ba8bd88d0acecdf6aaa2c55688deeb75625e2de5 f3a5c02e272bf7d1028f201e33c3549631c89ba801b5ad1cc7981424cd1c7c25 ae2910331e8e8b26f7ab2075d3bb9608f666fbf0c4c71063c58c2bfaefd7c055 889845375f869b5bb75e58c169cb19db753ca9f558f806b47c160f1dfff52084 4cd9b90686b4bca0ae584a8c6aa7aedc6d22993c380e03c55abfa047978b4a0e 0 ca79682655c0b7f0e249ede6c8014abd78365d372348532c43faed60f3f47c00f687f1cb7be07f019bfcad498bab613ad11a843533eaaf372d94e98079e1b80817c00512526b92515e0600797a57c38998142709815c46e36315a77e6e689d0e3b8ac6d2bbb06f1a0a4a99c45dd4033b7ed8d04fa3b1e84f66ba63c73ba6a40e549b5384204930e89dcb0bd0f41a3ae7f937076fca2050a65b4e4b4319625c0cadb73a9f83389bebd8ebd89134c027cc197a55c29224de9e07a284f539fa1705db8da16b637f7264f3acfc0bf4005bdbf492b970d619ec7155492f8f904d2f05c0848a739fec2ca92543777cdc1dddd7d5b7d870f0ee8c2e1cdcfec75abdcd013523a1c5880ac9d7edc3732dc0206835b0a56a9200f6cefe8040c9aa0d44380b664ee8f4b84679550e782481c4a897e9ef91ce092cc93318fa74e2c0a31048068bc761f6310790597b44ca6002fffd55b413d2a710098843b3cda9254260c80a1f0ab50e81523e8ac6e85a8d39f7bf193129c7f261584ac4f14342c7ba39ce02 +generate_ring_signature e424f4b58872a89f8d5cba2367a5c518d6f33932e829f1b82ea361005fd789a7 3881cd8adb14d6d3486a135de75961ed9ed7cb92294019d81adb8eb07344173a 4 08af7a24d14e395c22d134f5db91bce9bbbd0e05625a0817e7925c7f5d3a288f a0833c919f066178070650518818c09a464418c0579ad6b6f834b377035dad02 72d51c7e3fd7f62b2c2e108749a71a52e59072f9fe9d9eb78a9bb7c7d04b5358 33f26e4db6b10c1ec6205159c9292d852a39d8d4eff9b34147f9021e15cd4021 b0d7f260e8c049f4773e7a7957656bdb3426b3ed2b77a92963e25351afbab80e 2 43ece5700864c385d093520a063053219629bcfffee6c2d16f1aca8f5cc35c01edbf3e94a71b4a4315653b12d9afbeef726beabf0f29b6b76d1321743832350036c7583ed471c8612970da5033e94d7e40b8ea94504f48b0d552f4e9548efe08b76eaeab49c551f6e4d318e1888bc33cdbe394d0214c41b6f2a9373544f39e0f14ef86df269209f745a1f9f8d3fbdc1bffb304c550117460b63f8b314e286408896992abbd0f10e3935688e03e7e2d835cb775a372426216a3ea6a1e4d4ca702f2d1cc1d885a31626a957b4be30ec2dfae693da2d24944d6b5cdbe3076751008c03cc94609c87875401da849432e482449ae53ebb44940966d4f65793069dc02 +generate_ring_signature 5bbe504919c3ec458ead546da6a26736d2502e6a05308cb5625c39d40c3b0c12 af979abd7a21ec14aec19f65b49717be0e48846576d34c27404b0df29997b81f 13 d151a6ab8f224c3b9b2bdb438d5fee09275d134d320af24552c67af777d396bf 5627dd6d295bc9bd10039aa49cd520ce164dfdb98b5586dcf38ede1cab9e41dd 1e7968c3440d4a9994dfd89fda5d7f399bd609d7d428562aad4956efddbe85ba 49058138f3cfa317adc8439d479aa62adc11ba92670252048020952d91a949e7 206246ed0a40f7d750ec28040df4d659ae116b1dd5863f4372eda67025952d13 d3c2e54b9ae6962ea49eca392655fbf8027346ac63dcc309a0899e62420dd273 2b13248ac56f9f3bc37b3d49b2898fd783668af84f8a9738e610931e59e66842 b76331e8fac5e727716cd2fa1b24a6c5703bcee81afcd923b5efa452b0d6a869 87f3a2689803f1d825b4b7d9591c4631e2319b9b9582666d23b15e738939a03c 83179f1ced3540b0293518d30fad1b29d8227da77f1a32a19132b638d72d6d8a 7d1ff46774fae090d9433060c2c38b3e0543d2f9291f469f4ac0d75d6b8e4f90 e293f3c438ee82c0bf1d5b7d0cf27f9daede67da84f44436e395aac9021c1d3d 49541a49b7d1df796b7def54c7b0f8fccfb1a6674680d18f67f5e7124bf0a8dd 579e68d5f338d7c7e0e07ac07e0e592c9dcd18b87291a19316e66e57513d690a 8 7fa23a5ebb603a1729a86e964e5febf65677d38e7de8ef3947c156608101dd0b867f4a9fd07e535a152f6db6ee3488bdf6245444c52d7682b146b3da79aef204344a97940018fbe3eda7a3f395c85a2ee989c3c86b206ae8f0a10232c2da4b0f9591a5d7eb40194f84d019e18dd626d367d7890f59b8aa7e2d4d8ff4fe284d0053c3b6f7c6b06b9bfcc4e51018a057961feedf48e18ae3b2b15148bee630460e78ffcc973ccb130e664cdd23fe7778c45bfff32dc96389e43d2baac6ed5479081199dee6b9c040c9de8e48bbdc5d9b5fe703d401d75ab92702f83fd3b7c54c0c4a7f5388c1dcef3e9605c9cc0166fa830695a6304383e3e2a17b233186e5680a55be3ed1d7912d6bd01afa7f4a4bc921f1d7bcfd5ad3ba48537d4361a5649808ab9371f126709020928a85fe94102f8508b1641922f5a5b8657bd2322fb7eb04b98cff5d4e0129f5b3e66213a42f42f50e45e414018ed5e2005722e7222f180fa2d4e4eceb08b7516322209a8fa61a533e7c327a542ca7ddd0eb0cc51b65dd07b468e2ec7be89f8d61d3216b4d533b69ab8bc7308d224708f7ec713ed89de90a5cbc132c5dfa27d4d253ef77afe3d2935284840d019ff78b6172eea4a8ca5909435e89e3fc34b6a35f9f017e2c91d0405fbe77b99df628be43cebf79361d9909469ddcf9e6729df07da7024731f0d6d1fc35ba29b0fc6e265cf8119801d6c90d13159994497724d9b873f75833cb3491c896275abdfcf2006587e1a21bfc6d0fd1933b1c1d2978d27d1bdad8774c709435c269cfa93bdba3345af27252070c0c29dc937dcba837e605950f9e441cf7dacecd515f8a3df4920d57559910dffd059f47828358ebb5cf25a78ea3af7248fc6f615641a23252ddeffef9f3eb96ea031f94ec7883d39ea6edcb6d63afd8efed2d39e17ac655b81ff63209cc456b3603fa4727205e613fe4f2e09761bd0e2e7fec4acca582a17ef208f250556ea930081adf925e36f15f031ed1a91ff880b33077d7dfb69d0a0dde81ff3bf8b265df087d8d8d3f76a993480a00dfa24d3baf594df68a136cb341c4cfb5fe057b413f09f0b81f66807d9e3a59732f29f5a18e2b6455a87cf0aceced2af694db8cc50e0ad4f34d48db994a8b5ba154ad100ae1bd85a365f68eef11abf89b3a57d9698308 +generate_ring_signature a0859f82b14dfa14725b605a1f66c4e7e3c5703096a3cfa833f996b5e105ea0d cc280d0af6534604494020baf8cb120bffdec0339aadcf45b3d97a2b7965d393 1 13f43d83ff833e7b6ed996c43f01724dde26aa6d0ff8bee55b7f2a7ae080c893 1693e87cf56c39b63aad39216be18df69feef1484ef89a9d20eccf59584e360f 0 6e8eec0112b3c9fda911937a840fe56f09087d0014a7aeccab40d190b30b930dc2086587ee4860f3bf59a326dd32d3065bc4ebfa5e217da809bf274921e06101 +generate_ring_signature 0b93af27e122fda8495915b5038a4135290d292040914c8322751c075859176c d874a4e8dbce217f1f0d503daa4d2833dd8457d2d29022c39b63c2ff18295462 1 fb104b44d767fe74de8cb1c133c2e1d253857eb003c6c63528d7cc67b3f7e49b f39fa6fd80c7a487e67ce834393545ccc91891b663b57ae879ce6ba4079d600a 0 e51e452b0feccd0f42add0a613e7da9e5ac7027b25cfcf7b3f795a1bea26a7085ea32714f6f8daba1d632709f678bdf01ba1d4f22a6b59415b93a91654d9f30c +generate_ring_signature 88ec571618126ea8a58e3d9d6d3415253a7a9e2a4833ab4c68869ccf369a1aa1 0fa962ae711149af59f3923efb7e275ecd525f065faddc47b94d5b77a1d058bd 3 147eef3259895073beefedcdbc2d0373150bb6dfdc255b9c58326fb443cf76cf 48b26ceede8691804ed3476374d36b2e820ee44c0792361a511080de5fab88f0 2798e56eff7a2f665c67a1b615e19834f2095580047490ed3998cfa1a5ea645b 55597215ee22059246d3b0ca241f9fbeea9ab39876dc981ff1fe7f70ea10cd0b 1 fab54c65e1111b735cfee5a90b76e429dc84206547a3a73d42ae322f39bf3d0529a8aae101f990028b54615d20b957556feec904aac2aa2bd2cdcc7064be2a09d98b1b6079fb0f5c22019518269f8ceacab202c0a6bbd9da4374a2bfe705ab03a0b3a41038132743114db4f9f55792fa3a71f94089a563ec707627f676ffb00bc54aefb383d4036d11f800c03404a0f0196929bdcc009b3ed3b05297a99c970ebf9322f5371c9f6009688ba1600d1789fea18c18a197e1a850a4c9f3df7a7a04 +generate_ring_signature 7d0e728c17d84fa5a6380b5d42c2874053f090ca9619c72e8f856aa878300a78 406841c5266b911d87602974d79515ee3cdefdc52ee5e319c934adbcbc964735 26 e134815b7a0d32a792a347b2f87e63aab51b90b3e63b281f4832e2e18470e452 7b5604ae9a247eff5c40efa5965808a9294586dfbd6d96ac962167a4134acf4e e7040750c32328c80ac5608f9a0bf591ebef09d0630f5022e3628558c91ab2e9 3c358a038783a448cf3286470996f23f42d97e3ca2cc4bf4fe62ae3a4f974466 5ce0c0f74280351838242547aa953e6ef42f3830c9763f1f201c95e116a9cbf6 6db16ea86470b1e4bc1aba22ffd2f6a309b8c442cbc82c9ae1d7503ff44ba235 996b564c3f75320433dcb6e93ae0c45f1d1bd63268b2dcf97feccdc2ce8fcb02 2a7ea0d2ab7ecd8275ff6c47dca7ec0fa796fa4abad68d20384a1314ff83db12 31ecb4133c92846d600a7004a619e5867595ff17fd4b4c4b6685a34b0a64a212 e065a64af314013132daf153c9d7079292340724999e9b9e0e2ac22c4be269cd 44ae0fd0320230f57bbe081415831d4da5ff3a4c6fe4ae24a2f9721df86e8de8 8ea50a4f60f0c9d389940d28fe7bda03dc202a8187b2287c5609d0a86d80b970 b0fdab7d002af49b410025b061cd946fa32b4a4fbaa400d8b05ad89ec6c102cf f39415c4eff393fecd622a777af99d13d6ada020e6b5f154b0721f91bd36e005 99df2ae81c858e669d81270858e66861a9bebc89abd36b232d2fc70415f32a66 a3bf1ab959108e6eb6044b8d3cc8ef0fac7c56d326b225948974c66003a7a49b 3296005eac9a12ea9a9160bd2d224e549a050f37f7b8a6643007f953cda3964c ad1056f0da4946f740394f3c044438c016bc31ef30bd1ebeb4da58fedf91096b c69e1a9512027cbd747a968593c936fab41dfc0de4189316f49d731dd5032cd5 cfa3c7a19dc9fe7654556910f7e6332cff284d94787d3f1c142213b9ab388aa1 a8a526d7c0cbf45988a265d3f3874bfc16fd3e972b5c6a02151a47aa44f07394 1b4b025ca5776cb79a04b73e1cd207f7bf18f388bb9732279f870a94c20eb53d 9223e36a7f6fac4560bd6a82a48d6af7bc91bfcebee656f6f014673a0289b485 9a8601702f972d76ff9959082fd1ab159e5607070cc24c09633ece1f65d65aba 73980dab5271d0312d3736daa5ccffe20edc58040be9a59d833ab288f2623b38 9838bf0467b2e4c5b42c2558fe202360778154a2df347fb3cbe950d677b7caed 9da3143e8f9e479e66ebb78947eccfd708b8dc2af91e13ddbccda8beab2b560a 20 b891e40617e15e645922532567b31be47337c1140bff027b36a5eeb8c7962707bd7910a0bc2f158cebdafb9ae577daadab7160d5a3a4bed0f63064308cb8e802f59be263479ec1b4a7cc3514dcda06954513737b7d6afd30544cfcc548ec7d08a32249e0411f75234533459be0534b730f82ef24cf49b54b8d0e67358d1c2c0a050a87e88e745b0aef72da495d72afb76397b15bd6608002802fad039657c70baca8dfeb3ae93709762eedad733ac13521d5da1aac34833b2d4ff42a10be6b0e514f401001f1c4319bf29f40f117c66b0c5788fa2de6a9830ed970c8719f23062ec54f1ab3d47ebfa50cfc9895669390d1c09cb553e2babf43c3db252c929a0fcfc2a1c406c2a8574b9a49c728e4701934a91e59d3ea050d0c6a34c785d6e30cfb9e9c523a53b777f2113f228a732f53dce35456ea54b5d5787e05b2189c4304695bf977007100a2013474c7e46366171697b7bbd3e049917f86a2fa59888e01148d356627024a4ceb657bd5562f87492845f234f09db51f19c5e67d4ba61f0179a23e12fdb34b15a16d39b9f9de4e4a03353a9e9480a782cb984cfb3d815e031d8b648a8d2dd103f9db0b321d2d43a8d1fe84f98a1cd7fd36e74381c6e38e0717331f4379c732c010d0deb02cc63893f973ab5cca9a2f01c97a3dd76764200ff3a5f78a3910710c4e34c41230fcfa77b0683f164122baf65a43ce3a5cc58d0a2d6d465fc647510dd95ad1cbf67bbdd4d5f9bcd6f81f240b810b73bb3c8e9c02deccb28ff9032a67a1aac0c4c0e3e0b4bc56a86fd342d36024ad8c72398c580acc594cfd86f3828b70dbe6a20269617537b43115d0681488847674610d2a23007ee786cc4a1d833d446047fcd6aa100030158d60f1e35eb6e3cb64acd7756e0411a2e376a1d1c6d055885bea1717aad45dbd3c25340833187d6710f04261a502d91d5935d939043cd3d37329e968b20c00cb0c8080e9f21baf5eb3ba6e2be60aa200f94ec13a6a4c5fe059d682cc61f8606cbc370ad52060b3297e664adec70160f308b42d4ab41c82c26dad8f57686251cbcd5fb4b4b64c631c857e9ff9710599dee2fa471b50ba8981dfcc5c270a67df5acc2dedf010318127ae7bd358550e0570b3ea79b85615c44edb393d3191216f6ae6bc6de1a03fdd00a097cf24950bd88ae2ca1bba92ee207186040eb76ec6c6da2f877e7081b0e48cc661096bc003a3f4778e2f20e34ee38093f9ddf03f7c1589549c606606f01e912fd2705e2d0882583a8c640a3daf385ac7995b7d0f6e790af5e16f7aec2564a2516d8dc2700fa69930edc68cc4bfe8b15a94016fabc986e5535d4846faa0f6b6fd6f89f5f700ea1e784b8e09a57b078e3666fabbf808cf930e7e9b81e04f162b35ff9ba95a034f7afa2a736229465536e67c68ea63a36b1e45037fa97d9b03842f89d346e1042e82da4609f47eb803f442a3a6375c9e0b6797f0b9b858e4c1f46f3ab491620d115b42ab21664127e12dce3ce6da36f7bcd00b137500a9c36aee81dc4449570da5918134b6462c76ba245711478f74b3c5d2b5419b4c7bd795b4cb86861a8b09be7ae7af1b12d704d53055587eb26a20d9fd0f10e69144a746e4e44086ab0704a6e35db50defd44043da303dce83cbd5a0f4ff3c05a17bbf1bd5c9bdfc191104adf136de1b8662cfd4e8570c738e86632f26ff93d63863283b89722439f86f0376d5f5c9d9bf4a97e7acdad3ee41cbc69dd76c7e75cdd166e181c1ab58ab9e096fe849972d98bf267a00c7e6eb86750323e935fb28d99e1b7a36650ea89f3100682b2f45f3d8d34a872dc11562feb5e30d624b764e57244d27d4488db92f830b2e8dd770787cc48c89eff0e32502df12e69de441eda5b8abd4984888112f330bc07eac1361a586e306a911a0bdc8ca70eb0fbb599c462d6f6a4de41751c98d0afa3acf3a707acde0c669fc91d4465af29c41eb3ea365f6bfc653019d91797b044e186d097cce7ebd06c4721c06d8d5a20828eeb11ba707417de1d328ddf36a031fb1de95f29dbce1d377dd7e5c8f1e2e3ca3136209e624b588effa2997856904f4e1937397b743050215c000b227d110bfe5263e96830aee34dc321b58b49107400a45ef4344c27aa292170c52cd93a11b9ca7325094d17a414fe00080676e06097625b0d262a2142d29e9da764eb52c7d5b78825815e02e070872ca62b5020048b1221dd9c23d7bee7af6083a267cc0c200b220c8d959ff32e4100421a1ee0b787c29f0422d7c1bf657c777d4240c811982cd1ee38d3fb3cdf6cae4fd6225073d45caaad9ff3975a5e561c728a2987f3763ec609a224079114b61661044670c +generate_ring_signature 65915976dad0b79ec2f06123198fc3a123c37fc4a73773487e857da9a5c54c71 7073df50257577f8b723f2b7268b6d026d172b9fa7bafe8e0d1712112b026e09 31 a2b0a4460179b04820d33421d1ed3c5f984c770738e6e734176c1b69ba5be705 d172bfb94a40f6d5e3a512f19094f112ee1ab673f54ee9968556b127169952cc 39c3b2c50c49fbb9ea7f49ebd3ff1b21c6120fecedd83b6fbc88013dbbebdbf9 36fc7e7fe71707ca4777c30c05d7020fc7da04b333676201efe55e93a1468797 7f1971181921ffd10a03172ec2726b7c95b714214b94d040e914a4a549d24623 2013ffd455d969d46ef413e6cc8114d2b6a25de9e5fff6aabd4f1c84fe28bf58 61e67258e2b7ffab772eb1e68d832f940bfed8d3ea7e56f9fcfe7ea26a8d1999 64e5efb93d450926f1e6115688897055b968f93a6972172719d69cdc21e93e9a 1db41b3664617a8e5dd42df836433ccda0abc7ff1d94590706d506cd83da9c79 722a3ca714a18f10ad273c30a5ed6104a00bed41e5c6bf5f955ab7dd41a49f1f 32367aced52479bf495d60e499e1671537ac2d8711fcd83ef8874c667fb94fd6 d4052c8067abf0112c03f4ef277f7ee5e0c351b245c1f922d2c82bb855d6c341 537ff1cd54e1e52430585be5197b3da8954b631e423beba3e39dd3dc6be19004 66b18195c83e626b6a9c315854420167a09bdb29c02175eeca18bb1996893a0f 6c8982d18575be7948224abf0157ef3f788de7294ab6ff5c1cc6d9bdd5647527 6251ef481c86959a70d700a5ec9892ba415d370dd1fb140eac186f74090b59ae 25642851c0c2540c4a43e28ef53f2923af198546242bbec704db549933ad6498 002c5ef9e09a9065372ec5c408cdf6975b9673d416d78204ca1ff40b3a2307bf abc5cb8512bb1db9b640c4890e68dc3a7e0b492202e48c68c26ab68501150352 0b8a2f4c3e989eedaa91cf4c308738a050b33d28cf23940ed9070a576b07aaba 97d865b7b22cd95e121679e94c5cfb4b1870e4779058d308d565151de1ffc754 b3ed05b092a76c0031c064d505430a15b1bb4071bb70991dfd111e04ec77ad44 99096c10ba7981946e06381fb36672c611c0aa8ea97a3bd8d171a4a188d70e91 fcef5abfcf89053360ceba60f1a0742f6a209b4e9bd3ba23a0491b25b18d12f8 61c1720dc08e8fd3241ed1c0f59562dc5a39337f2a33443ab4f041ec9842e216 cd5d30ee14c6d6a6ac28024b55fbe2c306a0dda2790957ec2ea3806615e9965b 65c60d4eff3add02a38c4876bd36586378abd726502755756b1ce232b7fe06eb 3e7c1ec3ef42e896cc5564c1881ccaa0ba7a5b5e616f069a185cbd6c6643aea9 521bea98888e7645384b810063c034332ea7fd15cbf091ecc24a9b2d629af997 5293f3c7583d6e5f28b5a08282ee05a2aa2e8afc2b35db0ed6c4aab167de4273 a18fc87524c2eee2b4cf3c001ae2969837e11bfdc9e135cdc09a3c5854b3a04e ffbadfc27c1d36aa85dd83ce4143d666a9fef3b90820373fb96f7976117db103 1 1c9cda2f3a9320e833db5beba7d82ea79a3ab41fc14596280244c5bb75b63d038f2bd7d605f0be1e8a7823d17b2a4451c2073d617026848f6efcfd8643ee7d0c96ccd49d92737bf30ea42f890f60edb1c1449f4294ec8d36f01926678b4b3300ba15b9d734dca7c5a56a0dd66c5860efe1c76bade8069bdbed8d9722d580a10032781675d3e1854509e4f02f05f54c5aa0004726802b4b6203a5a45002c299033b1ed32c6ffc69c87192d4b15c02a7ed7900f697a6c0d9afc0cd4fda8cf1e5018e01fcb365827eee1c9bcfdba067587131e395338be94ff70a5e1af23c41300da5cbe9ef39047fa4f15caa7b8a48937790faa1d9ecc003c592335e11b753e00c85799bb114b2d77586f32f085a202b31d1820794470c7086745cc047b028d90e14a0a74df45f4d325443841b1418e58e98edec29512a6b365dbf26f4b790260bcd51db03ae50601b12221043f02a374015cd69d7255976e4fa164b83f980740f368be694f43183dc2ff469e6cccb08f7abfac8b02b3addccf6a7383151fc750af1cb3591e95b12e58db370f245d60038df752bbd70f65139ae11ba9d4a6fc802242d15c70d96c5b307cf673c67a53aa3b885b36356fc55d5301f6a9a66deb20e1a5c563d1714f3afaf951fce1c57cf0a35d9928ff45166bc21492f0dc95ab7032982239837eff586ca787a9716b35112f0e8a1085c0a9c8f39c998472c69040f915174193bf367ddc9c5ba00df82dd92d23076df3889c6c1505083bf8fcb700b4f24baf11a07a194a1ac2b0d6557c075ba82fa4298e19d07972910fcd6f595066e5b499b5c032ebd316150b8feac64837d47d6cb48f1e62ca369de4878fe850cf46cd3ae66a658c9c4ca7286cd1db79e854679e9c4959154bb642886a8cc23062a62d11b0458974feb9ad40df7e9147f2d4cb984082d64017c0d826d7d57d2058bad8bcae5b0ade7f4d2cee051a97e04f5e9f8272a3cdd068141ade0c28164006aeaabc6866162bfd6f2df30fffb2832af336bc8e0eb9197ab5f6c3e8f95d803865707f4bcb1db7dd7cd89e856b6a10005a020e514024b55805da63c833c1d0a32d3ecbc38cb9851db07f183046a8784fa9b6700798fa293d942879748d3a004035a93080dcb8b93c9e656434ce3b759615d9ef6375c9ef65da4e469a3e13d0be93ce82a99940245640b8dd1343adc3c902d13550d0009076610f9fd5ae2a20f9521cd1042a23a5d7125fe6f6b168aa15038e303d0bb2e30f26e123cfcff2b0cf6cb10576ec43074cca73e39a108eb8be2b771df9478c1bef13e018bed8a350e53a3eb816116f57123414f791a170b83298f1f60dca8fa84d8c1083d200a910bfc8131cb692b924a41ead51d512bd64dc7f925a7cdc5d789b4ca860e17a618087018bb410997e4cecec255d1fb709b9fab7598b02f56ddcdb603e170e054f10e43d5455e673cd080a167344cf5896bec713f814b2de7839b69acb53e5e35c10b77200e508b9fdaacab4ee81b0ac42491d3b48179b17f4034a94ec80505a2ff00c3249e6bb42c067949a4ed48b06842415449512775e53c07f156793d4dc0ab004fd86e64fa97c8353b696c6a81b294ae66e0336a8aee21d301a1c2cb19ee920cc58a234e65fc8ba21605d397278a5960e3f86693d6283127e3ffed187bfbbc073b9913ab6a4aacfef80ff3bca9d6de3299f6783db8146047338d113159b81f06bd62fa90a77f4dee7bca61ebb3ba6e963cd9c5c64c938b2a0ff56bcfadbca90215a310ac4ea3f2d37ce487acdbf5a70c0341d257bf30d5b75b30cc033c07570debe16ec26eb5539dacf6a06e71b609cac5bb41bee606a7dcfe26ec973c412701b90abf86a434ce93f80f14c1fb739b645413c4146252a1a6c965416ca6a4b7054e1c3ed057106e6f41901cbbf8759fa02495797692e744c2256b77322ef12d0345c9b32693250b5fe75a4d25b82a04e3c7349356c998e822fe30e92b2367d408f3643e2617c6f022acdc349fda1003d1d904a1d611cf47578a02fe55db4b09039497d45d503eab3f736b8de10c744e469bf8a205e281f82168531563542aa9047a5cafe4b5ff237877488e9bfbb29ad53764eb80264d92966f305cf515842101343236e743efda7022cfada2ae9ce027b9d07c7edb6e0f4fe09b4338a111db0366e2a32b0f7c3e240eadd98b09c85d706388094a8cf1434fee740843e257cb03859c1e1c5cbda894530f44e8ded45304fa80f98800fdd4d428af310f674c510b466d49d63c20a0e17cf5b0f9064b01e366fe8200a7dc9ccb8d0b8f78a168b409699898a81b8754dc6e21d0b8d3ac6480a75e36928468f66332e1e4f816e89c02e177b57ec9a0a8e5162d25c28579c79cfd0bf52cc100d75d5d2513344cb2af0dd10a614ffd7682bfd04e879696dcd667764045a30b2f0be6b4ea82dca29f5e0c1a7f27c3ceaf4d095f7c52ad678002f3cd1064f2571255166115e0bf92cce90139e446e1467a363e81f8246e019eb0e0d8bd4f7eb08e8fa06e14bff30fb5f20b2cffc01b01cabe03605404835509413100dd34d4190ac0482879091aedc89e08d1e80e76ba33679093aa6dcf1520da84592ce7555ccf219ab3e169f58945d60d0f9503d5d048419bb9d45e0ceaf5a7580e6c5375cd938210be5b509e5522d207b1eb21ed8b6ae3abe7c1898d29c458dfcdfab4181e40d4cc9d234ca203c23d0ab620bfc3729eeffaa1568695feb283fcb0cd822eaa8b22fb1a0b59178d2fc306571263f570a357ed0aa7d2e2cc2f1983ecaaff08323ea18daf7e850f2a26ac08 +generate_ring_signature ac3e3b092c1e38aa072cc4210d9bb2e4ce013216d06cba6049ca7b679bf4822c 317219905c41dfc83057f3a6a0d538f47add4d4931b087b6d488aee4b93ff7f4 1 a957ab83e1decda794a51d72c6b411a526b193dfcdcb97941651ca61fb40a066 cafd4da25098d3a9667da2474b43613aa07f58c8cec919c02067c70b02f18902 0 03efb0bb6bd63fbacd5cf4af3ebabbda590c37dcec4e67b224609205ae9a090999103995adc2d71fffea1274c993e62683e0a1759bacfecd94cd77c817d04800 +generate_ring_signature 6107adc2ed0507e69710223b8f25ad15872db003a33231b30b13e693977a9a04 28c0cb6ee37eee1a2282c28676b5b9b3ab652ecfd46c4cc29b8b6f4ba8cbf514 1 ddfb74774075531723882e838680b6087bc40a394ddfb6ad1235e233491f4a7a c4aae45c82526a21a7eedc6caab8558a2bd4f54cbbd11e95e72bfe372bd1bb09 0 4b75cb0a560855d74db19851a1add2323364d748c0980a690a3c0ac8bad04b01e14be709b127b791cb2b0837d68b7d3b56dc61e65746b5da7dd04b79ee55ad09 +generate_ring_signature 2100a5f4e410e5dc62f19a0c5e87184e1d148a8dd98b2466964959ff2381da66 d6d869463615fbf48380f9d11e7461f8cd5bc1fe91fb99f685743aba5ae70101 28 793ef8c181dd024839f3212f25ae757cb80bec1c219db4246c6ee2fa052b6f17 2aba2b63e6491a35ef32789b0c7a6274377cc205254e9bf2f69cb0286dae5523 3fa876cce9035a3e6a4d5d1b7a8c09e2df305701c39878bab4da23bc717f2eed c9e31cf9d3551e3fb2af4fd1608af9627f0a417d3cdaae50424f7ae0ff69783b d0c8613048181c9a3694afd2a02589c944bb3eef2b82b77c250775536449aee1 b9ebf2faf7aa48e64ad3dd91d6c719ac4f765c31b03bcb8fafc869c8c7636f91 e9f8b1b1cac9b84936264a4752dc97d3028a209be61bbe6bde32f8faf5463a72 2e66c163b5b33bc4c675b99736bd883533d8cf58ff92a6c6632cd47898a8060e a993576fd3d80b0925a900c3072aff58cd46dff9d10f166ab52fc57182b7afa5 da828df878c59586d46a4c49696661c3bdbdfa9f36a053d5806692c42e3f36f4 d2f4e92d5e5d70fce216e8b6d243fb32014d8abae37bebbee14ea9b8a3d84b8a 2f1f24c37d5eee2ae72a2dcd3b1b6ad30f5d34734a762fc4ab7b8956ff49dd02 cc7d90c3c1c794b6406cb1fa52f61a0045a2fdfe1bd83636c848f18a100bd98d 481a170059326d8eaf5a6885213b915fac4cb27a8eb5318489a75fed10cf50e2 dd2cd3093ca1a6169b55964de9003e308736bfb3903f3c8f8d6b02f252df474f 7dc1c9c1fb20dbf1c50b213b43df2fc31dba76b9afbee67b8b0faff9a7d269ed 50e9f8e8964df954a2c522a8d373e52d2187bbc76919287a59d9b506249ef137 8902dad485e7e3a7b9cbb784074a11b2f75686b7bd6d613027a0bd91f0a96c2e 33f2908d1fd2d85c821734bfee6f1f08a1b70b82b95a7b592d02a3d09e858cea c70aa472bbea4d8d55b345cc2948ffcbff1f91294eb23062088332a5d78b22dc cf57313df41a81100e9abfe9d50146d4bd97dee3a4633db1d47cda8874de978e f661b18e8576915d55a6a23e8d201ed5286a576f2c81c2fbf4ec7945e9b54f7a bcbfc29481405ef6941a8865ff80219df8f16f972e6db14ce5ae1f05aa3e04ff 1c5b932bed93ed0c1b0cdd46325126b6049b4def7a3bad6b7edbb8c6adb628cb 089949b4a4c24c2131989b09abdd0aefdc12c1c1f62ae87e04fbe0e9da567544 0da2fd4af5735a1b90aa73c411039d41dc2f61aa1d1ff7bbb76ad39e8e2c5082 81073bb6dd2b22897882b3c9d865bec7f568b80ff23cdfcc22421fe9771c3395 5b87191663fc83c7ac5c0a1936e414fa357bd8c83323c08c5aec0c65be085458 6a668a19b1bc83605e4aa62bc2a17c45bcd68210cf2b05980ff205babdeb8508 27 b55dd140afc90f4b6e06679defd00098ed175ad20bc15b8295c076479fe09c0a3ec14584145f2ce63210f0447890e01642e738626a19beeb6dacd55ca322d20e4c9fdfd9595b4683e4e3e3a602911d629f7a119b201f131916475532f7839a01ba69e15f4ff7a681152175ec1ebfbbd40e2d8ce5f27aed2274608ba0e63b8b05530c9c1b048587436bb8c9bdecfb34abcf8b20b977ef06de844bc7234abcd40102831bd109af0f1ba440aa64d780ff619e74cbdb031efda3e7f9ba8323c9c804086653aaa4bba433347c4693e062565efbdc280b313b4f29370bf495b887b50d3b4bce34bb4ea94088ef2e073d358765e4daf994689c6107fcf7e5dba13b2f08db464a06ed3ce6bfabd11cb5cc0b2897446b38c774f1fdb59cbb8c97dadc100e296a999880693ba6a27bf84a0ac07d6485fc803cd52887f293e2e7e5ead4df011f36a05078ed3baf55eeff5d1a3197ad465741c36c51157d8f4674d5abdaa10c549383d969cf139abcda55a6bae19649db8ced58a54ec748bd3d3d0324a33603d04675451479f3bd2ee43ab3e9f7a574cea3558babf4602e34210b0ca9cf680ea08ca3f5fe73a030066f67c8904e5ace16a590da9f0557ceccc2012f4946a10b3bbe2193be51b8811dd02e03db8169c7c7fa854c122f74410c9573a1c8f30702b3421d70c2b86ed644632535ae1102da43661347756f0a95bb29967e5db59e0ac20ce20f5370fe18456c59bbd29159733d89645a0e2e2be82985891dbff9fb07e31f82dc4d660cc440733bc6ada51b4e2073a18820b3cecd3b1a9e8024a77509acbfa539afdf4da2a4fcd6b15135038f33046cbd005032adf44842a9ea12680cc9b88c1e3af379575e40899d2adec5cb83e5a3a5f640486422aaa7cbf07a94008fae45d390690f1f8c63805df6a21cfcdcfbb23382a28eb8fad0cedfa1255704c2832878889af865bf940af113aa4537f5d8c134fd3dd949dca384de2e31d30ab3c351e57cea5c5759ca0b57884fabe028dc9c6185cc8b4d287dcd69c6eef20d0d3f000bf5b9f494261e1baccf24c395c3847ec397343c46958cc67efca1bb0e0cf2d6dcd53918d59651a6de5b0998d1bd6b7b36a2139d37536e20a10618ab0e18a2c1c53670c77f096c3a6076fadd9dca4c463c546d682bb39161fd41b2d101fdb829e55148ccd65804db07eed4e90bd86f7bb8710cec2526c0abbf9d3c620227574f5d1be708450be128368789ac3ac393c052d9975c22e81eca28fd02eb0faee0231339208ed1ead0b1e6c9af1a5d13078045cf2a31d3f9229fae9ad9c806d9c80784748bc18cf6773fbf3ac595b926d2242897016cb2dce8532c0ad2610008998882fc468b2eaded12d9c4f7b488a434d9a9ce7a609a7031348a904eac05170cf699f45ae5352eaa2ca804fe6b8f68f83f2df8a08f7298bfbbf630b4b10ca42aa71bdb7ae231490fcb5a65733afe50acebfdf526a2a5139cb63e21c2b9018684d7137bfc9d3da812b8321a6840f957f4a71a629f52881bcbb6c495133c0619d6daeffa7ad7de74627e30b2e4e709d73fd166f7d0c1b6f7755f455c90b006e50fdbef8d14398e33df891ea4cf25bbaf32612a0c8a5bd8c03bd428fd5abe0bbace413f9217636270a3b6b83036b7d98ecbb0d0ccdf64978feb7f3afac426085788f852b8f4cae321dfc104c6a1daab5cece5f65360de4fe5fcb82c89d45906e4059ace4b54f91825912db55afe71f2d4734db085b01aca8f2c0e6a3af02805ca764ca6ac38bbc3aaf92addb8d918d269c0d6da32931836a4cd41c755dc4903b1355049469fdc2ac6251c16392b21962614c446650d1472ce5404b6bdb0a100ddb8b582bd55b883dda3521d61ee48718d2fb924f74ec600308c3130b4d1d807aa92e305c02b74466647d3f7c39ee8368a3fe2a8a73b6743b5c1d5239f4db60e349b10ae9902208c0bca72576fed11d77d2cd51e120febabcfbbb2093e7e0c053e9ed7b820acb48f2b95fe94cf61e95ca729e0a7d03019c65d6e6ab01e6fc10d5191425ef7421c768ec83a4afa8be811da71a2ef50aef054e8b4999012fb9b04d148881fdd57b3294b09d115da884326eb0839e96fda767053b1eefc5716e901da5b580d3ad2288dfb6eef87037b868b8eab66b406a12d8a84aa56e4bc5349038a61796bb8436d12c1a7e629f0ced6006e5b722c31d914431f61d6552f11210af88519be0c5db6ccb80d7f064b998d29dbdda9bf32e77d5b1e677a055744f20cfaec6be4bd9dc4fe43336217cf393024e656d98b1a9dea814174b0bad1a3090bdba8c7a7a284dd56bfd115516bbffbfbecde1401cf62692c4c0110cf1f05ec06860ddad8565df50b6cd6d246331c76a3f9ab5153910cf7f91c4e5852789af006e504ae33ebe172677529875fc95bc74d874578b9d37472016895a31a5f950801ec54f77cb4d5db7a0e88f23528b8457d149c0b5cf33755e5368fec71f50cdb09d757378ed351b6a23d426aef8ce73c8b35e5fa44168cc2f33aaa17394fa8860c +generate_ring_signature af71df96a0ce2242af718f9f36bf7b1e2e54bf52afe541127dc2b87f6ae259aa ca8423fd0e91f8872e0a32bd20940a577b0fe5939ff8c684a67587c699407cec 22 1258734c3315b929610f4c8215a30412459f294317eaa246b5d8867d12c69363 aab540da690563ec5971895052762a8e8998c754f895201694a17c476dd598b1 74990fc817461aa56686cfd5f453b41a69f8aa901fd4e8d4d1f1a0b5f6ff8c91 be833b7186433e5d760182cd8ab18751036d1b0e968396cf61ef0d903633991a c174deb550d5ce869ebe3ec05bb8fb582cf4e27c35d5c190b7da5de77f28dcdf 8dec97e5c5778667e0ddd106fa037ed7c45eca6ae55c3bcf25503d41bd807fcf ebb1afb03b050a92b8e559eb8e067b545f63d8099821e50b1c99799b7d1991eb 7e9eca4eac87463a8a0706af0dff91dd9000aaea65a691255bf3d4891b55244e 51b1223f80ed22dd385f378a33a14111d712ca3018ba967a587bed35d8b798cd a1c58243c6446bfc860b55045ec7607dbb23081b72265365fa90cb90eba8ee23 41910bf9644f3f9fe19c5a219d3b7f96ea6168133c19629a2889a73de302b5fd ac0940d8dce80710286d7b2122907d0c2a8cbebea6493e44d3ebc88b131e98e8 e10b2dceb0a93a22d8c62e6e4f8c7d766c0733316e6d9af29f6a70293b2ee109 f2553f4c0fd73109c497c05a0524430184d0995adaf87df4b35b48dd589db98e ac12b75019ecd593017d62134be2c9145b530b80d0c604f3beedde286fbca385 366ffcc3d9f2704507e10f7945ca35af510d4917ca6a0f320b22bb9bf2d126e4 6e8396f83467caf6c90a774f7a35dcba54461a71e2ee2990df127a5271b8093c 3d1e476c5c17a2a9d875e8e23243deaa16b8ce4bcdc2dda4f11d60a521fd83f8 c52b53a7882356adf4167237b853e0605029776fae814d3fc7fb9f5bb8722f2d 524c073fa8195ef0ca8eeba82d2182b8cb026a4f0759296cfc02e5a240e6b65a 0214ccd35e3432dba23b879df17a8efcc9e2e56826be4d0ed4cad87bafd20c9e 9e572b2ccc41e5595b3c74f6b1a571dad53ce4efdd29e04fb953b7e8e5abd8c4 c379fc436343668d69d752ee38c2e4f989cc013b9926f3c54568eafdaaef8603 7 7bd2c4792bc05abc21bcaebf0923eab275c9144c80ae806a781653c40abaa003f8b3b085c8c653338f002969b223f61317162fe0cfb7c02df508e539adf1bb090afba55f7be6fbe3d73164429c946a29acfaba89321368fbf1ec88fa0f7cfe05ccaa3169e9e9249298b47730a1ee026f0a1ce38e84f94b8d79152f6fc505ac098a89f2f816b1510e983a61e6fff890275a5d4b3f7e6cca6856ae85d21c0dcc08acfa7a0323ce302ebe7c597773fb3876c37143e175572990de6b0e66038d8607a52cc9e23ca724149ca49689b65b9f877f0910ba87a3468fbd1f089fa9a66104f8fb2087a7ddace7d2817e91dba58878fbab281f4a69d273fed6cc4f556c480ae527a2b69c2887844217c6381065f1da64d5ca2fcf42dd4193b2f156adf5810a601b5184473ed3b918353f29c1fc70b2f2ad706f1367d17cc2a8fee13199d70d986a1583b2033055a81581688a8f4ae84d676e160e3cb0d77481446bc41f5907f39d9cadd88a421412342994a8226ddee48256c08ba37b7849e824f885c5ac08ae60dc153b19c11a605f7f4f00dc48749dc447e507aa593065b2d4da1299670b89c882589707c218a5def3ed377575fb159160fbe2059bf32c0ce988cc43500db56863d8876ad64bdb832ca649e13c88ab71a0719e9e0daee872338d82f2b30da27579e341adef0abcee6d61701d690bd1ff00fd547660be245f9d6a6c62480ce9e16a145e17a7e4b2470d6c95a063198579bdf344800676b67e02e40318f20b9beb98709c039a2cc7642f921e367bed48c3c5b03e6be8b056a8e7726752900e5b0a139ec47ff32a493d12adb3ae7c184a2e88dbb7212818c2c580dc465aba03eb465a4c9e17853e6a6e617f87cf294094135ec48b4717c0abce8a61eda5140d506eee7d820553c62aae4dd01b404e85feb69b61ed525e0e8a5b288211ae0b0b89b3e4f9a3c042f5dbf09ea88cee77e359207ef17fc2767b6a7cec02d3fcbd0a88c6fd3fac1f8617686fd61caac6ac90bfcb4362fded2da360701b319f1f7605e3669e59a1d191a63c991bdf24c50d3f970d648c763cf50769192cf72489ee0079946d777a82adfcfbe5954b7ee6c2cb334c8bb2b03e2cbc7f23f73fbd8adf01752c5faa7c0a3fcdd6e591a5fbd3c516edb919f4229e413bc3c50fe9baf25b0535098eb0e315fd63671c5b248d203a71a1057f5688fc8213817f3fcc98b0db0fe15983768c57a5d4c28aff5f7240c484fc1f2763d9c2e36664ca3f0b4d2e3d0cfdc0c42f84deb8597b9c223d4ce75587447e8a7b3f08827704874c65e0db6e06742caa0c741e16dbe4c60e9554ea42c8ec97f899a4b79a934fdab4db1cefc40be3fb1e63b85abf67b1b6fa6293bb93ca95a6e4b13ded72255426d2721cd2960a72d62c0bb5431dc77d79d2da0a47bcb922b638ea10574426a83d9056f7ea5d0e322e2548caf7286f2d7badac6d42ac61b2b1d0524bbe11a81edf6c98dbc2060174a3b5330a88c0b046bb042e6ec11b746f03c35305889ce2152439a01afdb60696bbf66b6699c03686d161d9eadd1605f7ea34302a4a798fc6ed5f71e09d8e0f69628629566261f40f41f753dce47533d4dab4d3359af56719e4eb8c108beb0849fdc0e99041c2a64b8444809c7737152c749742d3745e3733493c9c1c768508c94103969397af990202b3d5e45354a7766fabd502e8ac65cb9d95e7766221055b497766a15c0fdd8ced4664a0f178631315600fbeb8bf88d4fe5676c372970ad15387638228f0fb751ae7a675e206f9efc31b3783b5809ddf508c840d4d11035e44f6c3cb140162c3e606b222fb22ea62f15be1b4d3d228778c71993b0b9c016ed70b6bdb0885febea0eef3589c83863d45e5143c243f33921a1db96a11d00214682dfe8b8d097ff779e4d05279182433fc84d884afe6d59bf1fb3f904f3c02fc35df786e57a2f2bb97d979b60026b49d7765378b394d854c70ab016e8b170e +generate_ring_signature 4512063bb19daa1ba31eb0145d48f7dc9d06fd806ca89540becd2b5e6826c1e0 89a8e66b8da6a4002c7cbd2dd887bdad6a08e3412d1972c1ddba31be29ad779d 4 104de0915736fd728e8c86d2458441d876f459f995c7256a276534f438a614f8 05fd6561503fcb29ce98b70157dc619b5c4aba732aabb0b0d10b8298d1f5e155 2c1a30eb5f25ca5ef00b7806c934794f03dcc5ac58606aec2081f9522a216749 133392cef31dc52c7de272824c7d487327b3320b2eaf1caa827083872002b16b efcc49cb457ad9394617b802a3be8b9292ed7e88c782508899ba39a152e73200 3 3450a20dbc46af7995de979e4c776a2abda7b657f2a7a606c76cf534258da806ca1ec84d596b13b1cd199a120e5ee866e240dd51a23b2dd184cf96a09c181d0ff215f5148823f9d86431d7e02735ad4d553da0778fddf8faeb8cacf52f765b0a3d9a873a4f490afe2167ea8298824647ec7b50b945af20b36ef9dce700ef740ff132ff39df353020c8f547580f4b7e10275a1f8a29733c7eb60e9bfb5ae84002eeb384abe845a2d71d3efcd97f83f6c51bb4b5b66f25b9f78aaa2e76897e1309c86d009489a09871c49681ccf781a828a7770b80d4b0762101b70cd64c7e140ba8d5cafcdbde2bab42e0e1008ce9c06c90ad560da6c34fa70b57a28a11a80001 +generate_ring_signature d4a171b6e8c5a7881d47e5e059129c134a12972e6cf446760d5ea3f5d9d8a526 4995bf794e131c0a54e6b94218aadb1062967bc29978ef1e04a5ec2a622bed38 2 8a1e660522cfbc1da70e1f269efb738b9efe941435fe7ced85bcb8b2b445015a 52a0a2d5f69efa6666a07a177725d76f60a1fa078e7328132ea81e2a1aead5f3 7799976d0f2f488ab7dfce43781bbf6d8f64f4c17c6dc5e357a98ad83f23a20b 0 3ec3c9fe0e6f78489dae5d773b5df03bcf11c23d369864c1acf5413cf72ce6092c1cce0d50f02675c4821ab74045f478a5508a475a76c9deaa86e37f94906a03d133db7c07cc91c9d6f7caef4bf7716f2ec6281438dd86e751bd943b145c1409672aec2f673359ab6e61fa2a2a61dd16f2951d6b8e0a900016e1828e2a79b50b +generate_ring_signature af87b1fedee0e438f10aae5befa088392ff4dfae1546b8c8f779fc50ad51da97 90e690a1501ff3a3fb14070bd46a4b4a7175bb31f57956486ca61f2e49cd4ab0 9 35f64d12ad8dd1c71084709e6e868e68b934eb407aae5fcda2505773503025a7 c80dc8d3d3abb474b56a5c054df286f87f5c58881451feb22cdbdae8f3f900a7 3461ab681e2e79261f83d37d82d4982892320fa386cc6e6f5bed308bc1c13d12 46d70d6342274b6fa1e9e3d5b5de51c4627000ba00432606cb4aeb362e25da6a 2b561ad67dd4aa1e38a5fe1c8454218127a73881a206b24862e136cf2b255780 31053ebff5a815b078f8e94d9f0f044f1688b0133cd883d479a94ed066a1d1c2 968233788adaae5fc7466d8390d94d6198074b114e2ec9b52c4d851ba6832166 f668b4ef8431e75554c02db408f359ef81436a0891490210bd509923a7e0a749 fa4407f6eb50927d9c17ca60b21e8129a19684ddb746e4c186b88ad5113dcc86 020bec4c5ca87f397f5c0919116c54fc4fa83c1a650b045ee4385a3d7bb40d03 5 8104e4aab9cebdb578e8bbba5a6e9ed3236b655173667faac09dcdae12fc6c08b3a69129ea3b203634a00a5aaaed6fa6f14e73795bf0f5e00f1df33020e90a08489d9b6007165452979e4ecea64fb5764b1c0488a6b9489c5691e4575caef50fa42e51e1adeb9763c5a84edccd78da62e5ccea264d4f84bfb38d70981b713a0969d670a07bdfd488eeeb02f722e281dcf1d83c29d5fb5cb1b47b54fc37bff00b1610b6e0e3497928d87ba95d0aff4d661c376359d0b3e187df603adc697f070a340ebb0aa0d0e7b2b067e9030dc66646caef70b451c68fa40ff7c7f3f3ebbd0edb120cf829039726302d18f8e656bbed04bbc5ffe3e48ad29f578bd116ab600d9f220b487d36e27564de0a7795f89f842e8cca4d8667d9a2fb2703ef54c78700cafdfae9e01d8fcb90555f5dd7f0e7a05230b871d2935dbe51dfe4dab2e0fb0d03fc206aee60dec9deccdcf2fc911c1f2f9098dcb40d4e1a425659768dc6710e4b9ed203fba05bd91354a8582726c80253bdfc21f414c432b50e036afd4a910931bb54ae13620964cb242989baae1902e2b74c1b353d51d084610194963d83042bb8af66a24a2e43864dc4a7730585acd16f2a43bb50d8532446937b77cd2d013ddd668f53c94a12956543ce125b4237cac099d8bc8bd50414cebefec3c0ad01f7dba7104bdc71bcdfd275aa9e55980dcc3bae1b33ab776fa84df2bdf0886208a112a3f59ae23ad19d1590e5df17a93d145c8372c6875a3dfaf134d86c721d0b27f7d788a2035ccc721b7c41d23a974ca4d742e1125e99a1d4a4d36f449f1b08 +generate_ring_signature 2d2c11cf68486227b17b80fe6353ae59abf53ba0b8bc75f32a61007b557c53ad 35e426514c73e7c66880bc26f90365d770c31fb753926f24d23848697bfc5a88 57 a9919f6a2b7acaa39c078c3b24e3487a89c1e98f72c6deb29190efe461915b1a 97078cc1079d661dd75108310685f568c813d7486064fc35b2f69b38fe6e94b4 d6e58715307fdae6fcf242a4413b3264d0550ada54a02a233e3488d63dac0307 a6eb853278a50ed8333786b3b15dcddd3d8d784cc647adda671f710a3cd46bce 204fc5f49b9e9247b2245d0b29b32a697e6cd5b1c4347430cc2002ee6682551a 19f3496ab38450705b89e6ed8fd2c725a72ab9e04daf81b3e84f762c6f368858 cdaab9c4d1337195fa27a23ae9beb85e1b421162b0175b01e5022a8a6693a2b5 da64b3606b987f43092b844e787c73e147dd3f1401e5d4de45e6defeebc4c542 8636da56ced937d7942364cf6791b38e8819902811b071d88a7e9f31c9158318 24d943b065ad70668edf4dd47bfc174333547e92fe95a4840aa1cbe343ad8c7f 4f633908fe98dfeaf22d5f82a7bc5c591f68f0a5c087df556804a218ce5b539b 550f1304058b94162f04b4435bfc4190006262a55c326750c93aadc37c491239 2a8a480ff6b6e352d3eaf025a8a2d5f7170e8c72063216af052d14050d68573b a7a57ab7e2dbb84b1b687ce4e37c966aad92e8d63a80a67b399c3bc1775bc570 b1b394508b9e74e44ed21f6f38aa8844ff16856e1179b51b73968582babe8570 fd472fe1eb884e3f57567ba1ec8d6a3839e944cab78e639146224f141aecdcfb 5ca43dd0779bc74b551aa2279219b77dd51b6d84698949e23a5fd73663f5e5f5 4e4bbaa324ff9e710ddd5f5e837df67df9ba854afba57819b919b3481fe374ff f9f50fbf4109310755f80a3920a2bb47e297e4420b5859045819b8f2ab68bad6 4bc0b971d4bab80d4013dd6cf6363ed852b6b95a94b83380eceee179611f0126 9f5fb2b0052f5a19f77eaf98b94282e1f62d09b3dfd090074ba6a99d7d3cf0c7 570157fbaf6de931c767a74e01be9e00dd875477c18a556c93187cc9aa9f51b3 5b581778bb48ba15fa9bb4e03524b5206268bd2ad52aaed0ce60ac577cd3ef94 00c51c2ea99904e5d933635fb1715c3abb4ff07642aea7dbd46624fd9f1d825c d50322d0ead2433b3977ba3dce5964d17979cddda84a13acf5f5c824f110cbf1 bf25117e9f951431febfa8370999db5159ca8511fde188cb0166624773c5f0f9 4c64494a15cda58cc9def65c4746cf88207d32d24b90a0c134dd0ae01b09c144 01032bfc7e3f6e6b0a30a3d265e01e385771c8886aeaa6948965c05728b056a8 060f286518d97854b81a3ab11a55141d4edc97bbbc93fb0bfde999234b05400c abc25998dea92a2cc721cb7bca4ec8890b18d31fe9b130e706e9f9c231933aee 0d462aee190385626534a8642fd579da27e8cd596f90b3d5f5e7fe55407bb405 04042ded8ccaebe4ba0dc9d67fb8bfd9f51c6a43f4dc154f3858b2f34624573e a8c3ee54ed71f11805178141dc71af46d0218cafd72ea5adb3b54ebfbd2ba1eb c4d103d2800689c4b1f2b984ee4b0565f5e214b8209fe622db3f0b8b8fbd3dfc baa5fb8c4beb0204f6be08ef6aa3ea2351a81ed5f169315206a089e7abe8e691 9f69cd4518c0732b7e0d34fdf8fca4f449b391d66986f4032568375532a596bb 2703488d85817e84c47dbfab7d96a741c5603fd38674f9a14dbf9c0785f90a3e 18271655568f950df4aeeef27efb9c59680db900232da356f3bc940e77a661b1 a260915c171adc5c17beb5b39d073adb213ec6206ca8cd6656ad0207f0675c0a f3304b1d21a211a6a64be267512752e8fe7e4682fdcfd42676d4fd7fc49605f3 7910d249a3c64181a1737103cb8a0cc85dec99873b99169cc3995662ad1c2397 b19fac0f51b23d973d0b7daf502321f1abcebd28f6f8abfd75b377db40a0b99b cf3cde5e0b30b42c5b610d5087e32d21bbc819a6ee816bcaf928ba20c45670d0 64f3ce114419a4bc0eec99a1f65fcb2096c94c65ef8a5ca44209c48b6f364045 7f757bdfd7cef93d1ad0b987041bc741ec64dc3cd78293506a5bad36e0926d2f b0155d119bc08df378af49e9eaaa856742bf7783f3ee16d9d545a6064f12acb6 5b29fa21c990c8b43186c1d6d842152a2fa26eb6c0866c10014baac7c82367f4 6fa38c3bea221425d2fb979f5666b6b0214f9fe9ce0abd6d39427dd22bc20c6c a6f86b5127deee81e39532d897d37407dd7348beee9685f0cbece5decfa5d3da 1827e21b68bf6d7348b325fc74d8f65289f30a38ec4f84bfb126d3d94a6e8cef ba62987bca2eb991d123ef903a7377415eb9ac77f244042a87b0538e233fdac1 be54b474591ad0d58a89c6a696abdfa17005717e6b9360c34cd95634210eb8da 78ddfa3c43d923ca2037e3774c9c4c8260cdad274487ff71dc1f5b739e0ece84 6a2184d5c8013feb86b8fdcb6885ce3331320d53f4c02245bcc90c34f4f29bc5 db01a4dfb62b753a5ce1109a566604b9eaeca13064b13ac2fa24b4bbdb859236 de3e15f15f67e61b3f05022c387ba7029432f417c1ed96377d0fbaa9b9474f4e da88bf9678f4d1594a8899ebbe347e7039f5e23287dfa64d5809f0e70a359f2e 098a6833175b819831237a5b135e325dc1e10b0d348a07ca831c60ca31862806 1 46485922e0663680e42c890bd46625c70fe5fd55e1c7cb02e59c9951a53a94005ef3caf7a7ce2c650b2113c33e25b30957c94cf0538b7bd53436ad2041156c0da9de9bfbdc1e36391bd8435d85adebaf594edd05f2280cfe7463c9be8a6e530cca9ccd79cb09401e7871965d536929f14befe23389d08ab13166175293a0610374ec0d7fb08c28f14b03d2f3a5316ac1de3b87c0daa4d1447e911de5d284fc0d7b0f0f77824313a58d624c2f261349ebcd23ce34351685260126f2720fbf89064629c7b7b28e84c4d3d9f0aaeecb9b44c7443c569d88f860506ed8f09cba35055f9ba5151b934d4c0243448fa1d72ee4a1460043da70349a534d7f6f3825390a1348788daabe964f014cca277e72f27145875e19b13f9bc94d2be573ea1ad208587204c10528a5a43198bb0a94b990ee680c3bd6016e14ef2b789d6ef61c1d03f56361651df09bb97a8412a338ef64901790155f65a9325f4c2221c9a385d00402253888463cc64f1902c8790a0acfc653f5abbb1becead628556c770affac04a6987fc9cb4eb55179b5f5f36b13b1b4dea9003db2202ee5a7e6c6bbe6fd4e010034f1a7ad53bac65dc590436e12ee080ad65d819b143e005de20c58c746730f04ffcb50c4af2dd78352ec4639da2e152988e9b03318b33f39c736e62b2afb0cbd3ae7759629f2d11304551ba72801ebd8a2adc7c071174feb134ccdd46c720b554e0c00d6421274ea7f45613cf1753fe5385181bb4584fc96fbcd5ba16f79000f7a49f2bf7529d577f8118a3ad60aa200c4e54d9b5fcfdf0144db9639bfc80c93e80bb7ca79a00371291cc19a289e3ec9413e7c835f631c70bc684b8e0fb60839c99d618d969a8df3321ce8da00692e7bf7adf30bc28936e3abc5d89deb11037c38e1534b617e9eafb9020cc1febe2cc619db3573ee3239f3d571999b198802ca6bfa30637a1d3794050f9dd9176121c3d373a45cb54c08b706617dbc4f4b0a5abf5cfa37149a54b30475d95f1481ac287375f46c9e186bdf0307ac3d2a5f07e7dee417e5a48834b45f26b5745bc09bccd92ed1c37c765347e648b401321600869411d9629f5c141edfdf1a9e1187cf1fcf505c22a12e5e1925b5828d2e0f03f849fcdbd78579deba7ea616e5eba425697f5904b660027ec4731a481dbd1e0443591eaa4bc33f189d3483cc3ae12ce0b1dbee50c0c5508398f6ccdb21e3190e4c64d2d920654ac28695e84fd6f7124d0faaa1d772bdc09b88d9f115f642c7072f8a819c67c7dce0ef8a391cdac7f49ce97eb072a91e294f8bcd563607b0f30bee2118d042fd17dd46eb049fcceafd63421216b301d099222407d642dc64330dbf96b9dfbc4616c8ea6654b0bc4f51c8e08fedf0a0ad25a08bb9a544ceca520540c2d4af6eb450c69e0b720ece8f6a8dfce12103e58461f7a34df2aba942a50996f0526524d39c46c2372f88e19bdd0c56ce41e6d4ec3913fcb72649131df8001768bd61adef1d3259d2edceabe0e53ba004da1c518b61119d29d4db0f16d2024631bd556963760c923aa51579921e746fd669aca97eb77bd3f478e82adaa30f7ae3a2f0a55f83061b6eddf5339c72c79bf8d5b00942db42b70fecc6b947630461bfaf14649d0dfb627231678c10c9a85fd661d078640bc88e03ac4b1c71cc0cf90306ef97b4817c368f1fa9c4dcd66fec7c272fe89a2446a8fc93e28d81a60c86a730b89e24f970987c83d4b7c364f0c905516858c5f750b3fcf894f55cfa030281eecee1b921e2d0cb62d63d58f5797cb42be3b65adab953508824438dd104642f8df0fe521048882440b71f168518b27e87da854b3b056d6b45b2e04dbd06ac7d4f733aa0994993e0e598695835ef31d45197e699b35060ca6e4cf807a602c835666bcb80b84ffe021e5425a3b20aed0f73a46697a0e0244e88a06cf45501457f6f748c499bf4e3d948f7969d34f06086a58bf3ffae84300bb431c5a5cc0807e2ec78af5c240b856de50e260a3a4732539f9f70187a64b6841aa12dc5630ec694ff2f617cb7c6b4b7cb858230afffad0dae587d0185aff1dfd43077cc640ec912bd629e5455a905f11fee1f7054a2dc1852bc6ada4e933197d75e5837240dff6c2c100507a72fe847a8ab6318358d307409c36e0ac8b0600e0f8c555c8b07b0b329ede640adadb5ddb7aa2389bba1dcb42ea0d4c62221b604bdd10a20950cf9c65f05236dd3f3176bc06b0a54b1e6475d3d8da7e3f6791a8811ee49c30b05368c7f6a8a1b99251357a8421878936e06723cac41eb4af36f4b2aaddf233c02a7359f323817f3a46266f4fd0eb6ad1dc6091569182dae3566a22672e360af0c02df92f3aeb714b2bfd08407b307cc14b12562492c6e891baddabd8d6b20de0aec5e6028f3a84c7da60d69a928bc9c89c15840c3000c076f484dee413caac605501a2f3a307d1dc44c53672290c4d2973c1bbe63503bbf617baf643021434a06add90c9c16ddbb49bab63ba8890a546d3e6d8f86528951f9a0d00bd20335f004a6ff52b2a95cfa75960cf61edf80b71d38b2c866efcad95b13a2ef78cefcd00b6a236aeec96e01295664a8e8bd04285918790206ecce3259bc89e5d766e3930fd351e8d68955a6a8190651687356170b93257292a99627e4b520e9c85d43c00462ff0a0b6cdd481a0d8268ab5602b663f9804b18ef5fe3b1b9f4fdf361f3fc0b0800ab519b986c77d595ea12c4dc4a78d71adf643cfca4cefc68bd038e2dfd0c50874db65c84b38f98a4116639922bf29272c36f32363b850a19145fc9faff01c8d3e98a3fea838ef9c0e36aeb36813968c34ba1f078f61c0b4c00b1b3a83f0fcc0bfa12ac950d2b2240a83110a080c58bfe27c57f4939b7bdce9a4badfb28097e1b30ca6701fe17171369e1a318ac031bead9140d80bfa4f8e52434418e7c0fe81f9d0d5bbe329bf34e25d08ebe114aab7b9fc1fe131636d977a083ca1e8700b4d52cbfd16e0edf3e90a6363e88229d9ae295322a2e22804b3bbd8da25aa80e768658fa643a52599c5abc54566dd568f1f487a614ab9f074a0e29a8dff5be06de85d8b0199a6dbb3f3cb0abfc92248605831fbcc1be9592baf51b528ec65e0245b8ea254a8fdcd84d13ba4707f48ed4c4439091bef66d82ea19feeea757b90f29cc9d07e235245d0f78fbe7496ded41621cdffbd675cce3518f0ac63c1479012b06b14737724f71ad074017728d3d55cd9655519d9156e9878d3a07104d69035a5b770ba5c95657caba8380288ebcbec239cb19f5faa8328cb2f9282efd24005d5a7eb7439a6cc20313a7bec7171a9a5163cc34682d84feec5a1400c590ac0c68bfdc88ead31c34bb1001cb945ef21b36fedcefcf9a522ba8231fb9ac7f4807d17c3abc706da669722eee6cf82fa9e534fb54c87c0808fb158186b3442be8064b3c32b257885c4f20eac683f0f0adddb1398866464a6f9655814164857b7c028064072d475acbd8ce2b18f9950701de75ecdbcec3c1754369773c3c4cdd9908008184866c5972acd64bb760da5a351fec2f04f16f50a041da0e68032f2987067b1a101732a847430a698cd39bdbdbd59e6f173956be1fb7852e0519238e1f0d113de9b1c3e6d0a4992100006a02ad18f9ff6188930f58f98ce8a88712ba48026f0d2321b20c9c84bddab2a649fdab94bd4c0eefe7a9b4cf97ea80ac7868b10ea48287edee1171db95e66fdfae79435846d93156508d2b1cd34ddcfc788bbb0894112564fb6567caab167d4f99bcc7c58dfb0f744a966760212f22c73df873042165f9c59d69212120d250ecf5ab45dfd634debfec9270800a009921cebcc900ae88fe11dbb0f072adbc34e02b408c41624e2b05ffbe91b1cd7b784a9e12a00d34350602c97d6f12a5078709d34c0092516400371d56d54938ff8208921b9a0f805a2d106a901f11880cee0fb0904242cfe8a281b0ecae01b7a0f564e77c0f0bcd7d27f21c15ca2f6a54737e9f5711d34ed2bd2ca9e5c9fcbad896f5ea6c6f0e8c0bc8c74bcceb6d19ec1259a5bd3e931c06fbf835afdbb865f79120fe38ad0a7687500f22e5066ebc51982e5468f80d9afdcc9c8d15110933ba465859bbcb00203f9f06ea6377eb73471f746fd58befa74dfcc0c86a361aa56f3d5808942b062d624eff791432b4a68a453d7dcd1d76c76e4616e3fd7144bf50f764d0cf990b9ca9d60ab5e3ba2476af33af73e5eda3b05c69df5628f4d06e0fffccaaff4c050e511841e5e4ba66890f31b7b4d650b15461e96d3a054cd8800e569fdd695503719e17b9e5c1405fa9478c0e594bf656fa5517a2e1315b499dd57ced95ee8d0a9f41ec2c66e7462613a93a0aa29cd136d89ebfa4e7b2fbb4bdb81c0b6b4ee30bd8bea453f78199758a8dae88be26a4d6056169c19f0b8a669c4c01a20ac5aa0809c03f9b77f4cbaba79b0b3bb267226ab457a98f3db49b5ddd76938e173045036ab73092f038ba4b8137ce824bb678eb566522f17a5b5906e56591cec091990267877d5d53d959c777548d2a324260e1b02a6544214bc0079088937967720405760e32111d911d6d74675bd5da8cfcf23971dc2f569bff43d9118f9229921d0b01f9371cf50e56699e72d4ffdf8bc0fde7b4cae29afe7e628894a2d1a83c160c4ef81f235b8055c2cd10171c86f14ab13d85a075586af8cf950554eaf102c403ca8e8b480087aa11efc9c07042c50b29a66069e574a38ccbe4196233ae7ee807c875e24b8f056e7b25bc7a16bb539ca72c8c9cbe9454e1ad0417417b0d40e109542e18e3cfc116ab87f05eba020e1218554d63e841feaa7cb2063beb9664dc0b366bef65a49456e91f554f627d51315df7fb8bcbb97000443e071ea82959140bd7b9d11d8cb9535a5afc47a08a87de27e18291a803326693a4f576dde707ca02b5ee92a30fd8cd837ff094060b75490f8848136453ac603a5508f9de19044101404a5f45fb167be250b9dbe50bb51217dfeabfb2f36fd402a9e9373fbac8d30c50eb415cc80e5b3ddda0861bb015afeee835a346a755d06fb0f83a043462650fc2062dd8c0b7dfd44a713e1f9d9010bce783b0b22ab4b00d5689ecb7cd944d09c543dad3d20d18264afeefd857a7313a9e1e2c70f26a50564efc233838aa8000 +generate_ring_signature 5f538e02b1461f07c98077400e3cf540b0626000edcc0ee4f184bd1f72604f9a 6be7e0a471ad7fee54ade0d6615b22f36f5ef44992917c3981ef7a0a4d561da8 7 4648fa9cfdae1cc58f504ea9031acb69b269d148e581e01294730540f5ff6930 9dfca258921efd4ea725792ba16046d64831fc572f3dc72ee4fca1100b966ddb d7f734b73581e4fcd011a57ccf613b25ab32c3e61dcc3aee5b65ee0d118f3858 e11aadc5cd441dd0c48f584c8f92cd17d1aff66ddbb7f86e4ee6deae1870f0c2 9893530b57c30c46ffd006059cc5ccf20a35632060107d8ee0272da1cf219b7c 0f5a132da48e2f8dd651651c00026b9ae41d909503fc5e7b64c808201a06d2fc 69fcfadb6d9b4b296d86583324f22d124fd6764ce0dc6331efe72adef2ba1a26 02043a6d949d79e1a8c63c231e3db3bf406bf741d9e4a35b14c41e9813e90a00 4 549bcb0b353d421de2d171d9bff3dbfe67bad88fca379752a76d7687c987920e008aa8258703d05597dc0b0aee74d0e7f7e24552e62650383f794d422a92da0163f1b9586ee00f40d8ad8ca406d892b06cf4325fb2f8e1c319b4e1ac3ff43b0765c29d68efb289478a52694144a02fe0607bfc467af3d67dcfc7f3f3421be10bec530ae754f26939473c33f1cafba3964e6ab44be00212d0f2340f7fe3912b0bd8c348471ef8d6c046e4f372910f1c6f86f667901c9b7f6145852f392f2243074b74e1ea3922197654bfbb8f3e7ecd737c2a3c54966942874062f165a226a70ae64bc92d4079e5d2badfc83089ffc9768ff5f8e7d17fff1394edb4aadc9e9207cbdd2874fc1ad9facce162eaf91b36e7170c58e5fbf5f570c2d55be200d1740d208f4c0762a1e8f28c235c914dadb73715d4017b11145bc2d764595d5c5a12067b15eeb08e1b995e5e4c4ac6702cb3b447659c229a56dd221eb8d13de35e6201f50e23bb16b8396412f0f2dc55b5b1d7673b914f284091265ac2ea5d163ee605b84e4b2a4b3c7620863d63acf8f2b62e6dc236b590bc8912b651a840d628d808a0983263e06be3a710fd7c6c2072657daf66861adb5c766c7f58169fb9364905 +generate_ring_signature 39bd24a7043973bd7a7f4c8e448e7787d1e5aab98f333b25391f624a492c1d35 e7551719351d2f367292d49a58da045bc9af52dd31cb565a2020f5fd3333728d 253 87211d95088dcae2b1d8db1e408b1eea3050193114a62975b8228d69364c2e16 d8080eb5ff65b1e88262670190e68166ea45b3b603b37b6d7373cfaa636f165e 241d5a6bf98df975d09c3fd82f28daf686e24e8d577632498cf47a80742fa4a9 28044c0d9ec190be78c95859877fc3c71a5eafb944d87af6657986098d5df61c b5380a0808e51b9bb85a54c703ebec3c1dd5d00ca4c776d3a4d88a3b15fe13a9 9b02e613f1bd267ab8ba9f17e1f44dd254e539ac7c4ae51db640be7d43aee526 b4dbc4c9bfc1aaad51e0a4605f40f3e4d86bc7483e9034c60412b099eb36dacc 94af0b33beeef150aac1ed32fab925099b688b72a6d854115507e893d15663c2 7e8e137d9e882f0fb9e8eeb29e607cdb113578b086780a0728e926492017232b c025e69848c70343a238a934746b0dc0edcd2d545af7003bc00e647cee2f635a 80c47dbf387d8fc014051cdb257585f7b85b08e41364e3a6267652db57967cf8 2f16c481aaaedc8ba701a58363f4574d21fbe7d9d3b225c200b7fa9725f15948 8ea6e2ff9ce7991d7764a92cba2c865f5e2a5e258588a43f76e17e41de039808 75ba819f4fbfdff82330cc7afac927e72fdd861cd9243693ba5745891311fc15 23a6a044fc33f8f2e7f8aff58e94283ea45fc8202f1e84c71914073fefea600b 3662f25a290356e28770c62bd711ad6ad929ecab25da3f515a3c053e6212311a 192b423ccbb0d6d329e42a8d0317c8a00db0e5a2decb455b79f6b7d9c0737c06 054535dfacb051e8f8cca57c43ea456cbf563fc80371b8ab750b4d9e660270a4 892ebd8b4d5abca1c059e3aac1cb2543194108cd999debe33e7a65d112760d2c 6627c6b5e09c06f9329edb030089e2bd60212ec499bf9e00ead218f3b4c4a94d 0323b5f1c8df8fc71595693abe6a5423cafcc7379ea59a1fbae58b1c820f5f66 8cc337574d514c3b74794eeb2cfd25a2b0c10768e631bee1c08499ba34b08c50 42ac8825861673461fc8e350040863a663fc03a8c7822d4d97ed3d80369be618 c4e5dc0e598b2d75087c6fe50b411588803f1924ac9cf53882d952b6bf175e20 f93279c53d909208453b55837953825d8fce2dd9d88d98917e7946016653d867 094f2ff19126af7b3c0f71d8f0d569edb7d4ab103a7b541a85defdfd3f516473 00da2bfcf3f2f224fc3536837ca93891dba7254213f5314015eb6be4f365c6aa 99fd38e293ec436c242c51b8d7688a1e0da8b8d9498f568c8c67b7fc7b03ff35 e8b3c064790b8c6004889e828c28e4a13c94de102ff868c0cb30fa7a1b310455 e009ac341d6cc6f400c9aa812eae38b983ff1c3950ec0ad32f54a637714207c8 41d6e1013a167657aca3c785b428458a53751a3ce4f55e7f69d1c2a732dfceae cb6646dda6cc5d0f46c3eb31b6956d944b63687ffd3ad12f0d58fe4a1412021e 989c30d40c19570e0fce6aba0e6789ec2351436a0deac9f4bf373a5621583610 ddc4aaea5c7d9b2619c2cc955076f63ad67beff0f57cd678a43b6aaa935629c5 7137f11a9d15db1ecab14ea167381fa81bd092040a9bce49661788380f5deeeb 92670f852c2cd07a97830d3f4db9577d9e933d72b46524dd05c65325d9a2f875 d053e29032574a0eb93bd7f30dba833b1da8181e2093e3b50d28a5f39dca6b6f f4e4496e938470ad634e61275427d3fb275fcdef514164975d93a320f5882e88 80e0024f178225cb0b40aedf698b8f4b85e413a34e29df3e3adf9bab15296e0d caa36897b70299c47f3a0cc3aa7433b9c5273e803dabd5986f84763492a41a90 7cd4fe492d5fa570d769270c0cc8ebba8fb7253dedb141801482ecc5fea15a98 a2a76ff27b18546a18b6bbbeafb1ece1272a03e2f147e9bce94da684f018e322 90a46155d50366bdf2a2d7e398aea33224a295aa3455fad40dfb4515d365d60d d08d9934354be29e663f1c8e50b6e3610d8fa15c10e12feb4cbfb1d64a07a059 8555cde97863f54e6b9ed852e3c2a26c2ea56d815d710939d89eff70153d579e 265617d3c9973965a217a9cefc0200f5300f3ecacb1487add5e1bb141400147c 6a45afcb5a02430e57bf0af7572dfff1841b9740f5e5052540db3dbd64fe3cc0 1744e17c9cd258c8a7396e7d704bbc4c6c13b06cb92bfd176ba1567772cd4ae5 9236692660d3647d50a61c688725baefcaa8675ee933596c42886d7e57d81b46 a8a82c4536579ebd4e35bf3030b1d18b00f3b56befdcaa94e6e6dddb00b3aef1 eccdd01e8deee3c4ea8fffc8b41bcfe089da7fc2d0d250c8d81c790a0a340127 a1b33761a1691e68c0ddadae7965b912a0a2578b27eb6e5234cfd1f6bc175dd5 65df31e331e3a1f31f01892821485c796e18ae9be5046c567c2f9f31edea5eb4 f51b804b2b4c7047e874db85fc1f827c78facacef789bba5ae0bac362a8719c3 cefbc7087a409c7bfeaf01713590ab8ba8cf200f0e253ee24513acadd14eace3 5e24afee28b5670695148232ff4ac7aeb6d028430f7e52ec699bb8d2fa423543 75ca63027ccfa35301adda5929b6d34db711287e8c20049f04682940a2574cbf 6d028deeb9f33d1e2dc9a7815aed9d46c8bc5ef5a24ee180c51b992d02447feb 0b4862a56199782c3db8d96e126b52966d8857c030bcd12bf78dcff477de2dee 6f5a8b4bee8f6d5f58db280c52b72b24011168a95f856460018c6e942cb93018 a8866f4e5582371713d07a92b9db45fb32f27fabdb45fe78b71175af9b9abb05 f9abffce30898c135700ac2872cce9820f6771733cfa615a271529c04e619c2f eee59d2fd563cd83bf665f28f327f28f2236e0d7c72e034da73634668014a050 22a58e501959e7d96664eea0a947f95643cadab389bd257649bbcb75e52b101b df4b5654604c4fa3b69a9303047f39149a9ff62e5294ce1cb9f03a6599128759 892b52449e8ade9f51e72afa4d16cfd61025a0b852b4aaf0b62771ca4e4d1c03 e922bf917ec5c7181a864a2b0fab689c90593a47591b3ad0ad442ce8b632aabf 09622f5fb64ce4b5d67a424a280af79dbaee60a557cada15a72045f27c839617 791107d5f62b4de6c11a38bcd84a836ac5799cdb4fc736f1f5e14f8d4bb56d9d 3279edd7b2fcacab9810ac647eac12ce8b16994829f283d3eb83f8e13a4d1429 739d415d0f7d8d65675ab5dee02fba52b22baf769fa331477768e8fa21341d4f b6bfcd65dbdb070b48930d06a83706aebcd7be61fd7595c8402da87f09e8303d 9007464c9c734c561094b29f09be140baa1565ed3c09196f399e82b489ca5aa5 be6092d1c575e39349a043b24d3df2a8e9b4938e8ba67796428ad0a7309f808f 2ab34408c3bacd9bfea53991e4a6e69ef5c6bb25978bc25f62244b143f9bf52a 76a04b6675637ac67513c8e6f9d89243f059e3407a7d04d534597bd0c7e46897 06325655016660438374f04b5190d361b605d30a2df103fc4f1acb7e28b7e6f0 5f3db63b41e7c2789a9839ad5fa73abf78d9ee2a6ed7fffa7ab485ae0b1c21c0 481f3b51347b46eabc5136721c1edf697febbaefadbd1d0d72f8f792de0fabf0 a8fb1a7241ea84c5e50739aaea6761d538a042bb33341adcf37e088aa1d51477 10c28dfb14d43cb7914d21f8a5875d6825d44258b8cd578706befdb2bda02092 5fb5866fb47158c2a5bf8abcb52a9c53a2d4f9c0c549d352394aa96b79e406ce d3c12eeb91a1d62781ebb072b272ab1bff02d2fc5c0c8f2af0db54696121c282 d7cf291e972479633e7b3b3c6fb8019fbcc26888e6048d9e78eb63058a5ea47d bf0a952706bbd8adf29a83b681560fb826be849ffbc69d7fada22e90fe83f9de 83b805751bcde4a3174a5f6531efa627a47b849f702d6e7ff871ab792f0cc818 c3e23cbc18a7aacac51625ba45f180e4fac85c76df506553f5c1864fe17c086c 3abf67694d6026a2cdc3ac07aa3281e527594c551319a2e6d9b257b7d46b58a1 382f03e3d26afee7c3f7a8aff90539d0b05521bb3458161f05e472717e050924 1a2dcee09581ba083f476068b6e59c97b3179c2a9759fd5062f6ed03854e6738 a1837059296356f67330d65f3ec669a77140310381bd40b8833038da9ac9467e b786b45a36f1fb2ab17fa149b179db91ac9214a6331990f899b9c9d607a96948 b3d9d31002353f99de917df0fe2a29bbf9468cb39f3ac6f15cd3693b9bec9500 afd4d04039fc38e10cc42211f091880c106b4ee88cf223d674b72691ef04f0e2 779fe4986cc325cf9d43a81527667f5477bc6b0dab67b5deaecc1c78c3b41228 6967bc33b9751da2b33cb217691bbd5fdbc87db54e360c236725df760271a6d2 20b279076b9092772984b180cfa0984e2ed6588a2c970d6a406b2773170a25a0 26109422f9bc97fb8374c815a93ed9634c8140a8d0e14975d7f1e52b66580946 7eeac26b1ba8efe87bb115537a60ee5a2bf6b16a269a518d7c60c3a17b3f89bc 98e996ba603332be1162e011edfec5b73a3a6184918c34d81a6d8b43a4badbe2 f92c62570ca18eccc7778273b29c5c817e19aa5559a5104cef66e4e89dc9ff9a 96ac32b1c4d83ef670005b00e79131cc3bea1253d5c082475e6da0b399b8ec46 13bae2d12ee9264bb9291453cd7019a57958f502960c34e42017c13a0d428ca8 a344ba1158f690728f90eff634b62ef5f2ce2b8485e7ec9eb3d523df9ce9e20f 540926e4afd31183a2d64de8c688960ee63aad78f8265810d24750989af3703a 827ca48a9bb00510c9c89c629ab3e7fa5870fe2493bc6e07b86390c7f893e91f 7724d7adaba57802ed1509ec9030323f08e6408afb4cdfdc05655161b75e1525 a21c61d032a71dc127b1677cdf43876169493bea45add0649cc9d059bfd20f9e 62ed1053385cf71c7f25369a405d4b30b726588341ed822fab9949216c55269b 0012dfb8eebda069b6b178448c062b01df2580dfa68356ea9b18c466d7c21a1a 2b87f48472bb3c7e4d2cce4ff0cb1ae568db0c8731e6c368252a791bf83836bd fb4c4da5dc1f72661f0af5db7637f1b7940d849103c430d63968c320a02eb9b0 9c52d72249c8cfb3821117962383eda8c12b6e3f8d23986ab4afd3a488437fa3 d2aac9ca8f90feecac1c737690c26d3b3ca4cd90841001749b22b16bdc1e43e9 0392f0abb11ce9258379706f9da4703e71454fc7d8d67536b1f607f207751b32 78a67470664d2bbc9b352cb4b14ef9009d9e90bd4cbb0026803d1968a3cd1078 d3c46ae28f78b2aed09cbc451d9923667908a5d43a8e092b630ee4be84394500 89e4bd7046c2f12dd51effb3d2c800f3ab40ea4da49977c1d552a12d7f8aa959 1989bff2c64645d0364924db8586d990ae186ae9f313e91dfb4b3b70d05c8f93 42a1b41f6cca4410e04819ed22f20c9182e7a9b9bd7e8408c41a2eaf48f1d192 35b4663612712abbe565843fc2c8761a1b3b0fff08fbaaffdeb0e3a7f6853638 3daf4ec635bb2e54e527afe6df275c297f7fe217724781c4f8739ccc9c9b2fb6 5afe40899e4e7b817bad34de512ee2077227facc7bf4d3af21ae52fd13526623 b0ff07a6f55a5b48ac6112e029541eadeae416e99a88a2805f230346eed89629 7d6a9122f5df8c327f111e1b3bb934067ef9d0070c3e34681560145cd3544e61 bba9cf327cbd4ab53e16379ff673f14ff209e9f69fa78507504290352b22f1e0 09f78a7968c74f519ec07af117b5937a93445fd1292b3f4c6c048ed3f2ad473e 4cceabfe35704957d8d765ebfbb4b25e7aef813bbb69918e328a82ccc9cd0ef5 4d072bdb1e147c44696d3ac42708497cd14bd5b2053c4231e0f269ab68801d38 fe2581a5df60a4be668cf4eeff3b81b1535b62a8a073663d25cc40ae7e727bd7 201d6fc7a1d66b80f66b2a7e7461ff48c2f7f494ce75cf6a8fd07994315c7d3d 4aacc4df48cb39506847e47fd1de8aecd36644ea9a0a3715aa6410f90bdd17cf 391c4953fe0d607a5fe0757d3b6d89f377eef9472e2df092d84330dab9032c3b 7c0b6f25bcbdfddef802981b87e2d2602e796ddfbf38c612a1ee279575b473a8 7d918f300409fb5498d22dab33d5fabb006e03884a8120342e8c7de43a0ddb41 b472faed5d81a2ad3b5abad7bc4254a17e227e6cf61dd85f808343ca66d04dcd 56046d829e429c14ca5bdf58c1735f9ed29b23d82dc82b865f4404bf2164f78a a777c375406fa5771e7014fd9c7f1195047cd7afafbfbc4e90895f07b25bed88 2c8a6d21d61e1f11e8c4384310f41a6d170302ed1693767879280e6d26a2745a a629e4931c6b0932c982fa5204e98907abe2f36060e8571eac9faf4214983ec6 26b14959b10a5fc133763c1042da576408b7ef0722e74c7c990771ecb67194b9 33c948c923205f23965294543804368f8d60f355d0b812d7ae49100ac3dec0dc 5e334c7ded8a7fc203e7b4b7e03842582d3f87bc6d2e975091c5b1749dc3afbc 9ede74c0b6637781bd4d617a1ce397c5c6d985f56ae0a3a55292139e636e343e 397a7f6cc7cdd493461ed52f39dad6f3250a8463472c95ed9d549d6d64c934dc 0faba779da6568d7f50b60ac6c02ad0abbc93f4da87aa61940a159b4ae7f96bc ff1543693433d98347ca081f5450abdaa0cf029ccd8654b31920dc8630feddde 333f205783f49fa3f0c93d9b97791d7d477a9731131559f998bdfe978885604b 1d3b152f9ab2ede0ca97f01ae350441b4aab27bc99419b2b65a499785c4fb523 67d42536c5b0cae6b6613aa4f2a9655fb108a1cb9c741ec0e8acc47b0f50bb5b 0ac2a1b9379b4eb1c63d742bf7789f3f9ebac237ca1890cfd527762b7f24324e 8ab3bbc165e4d338e6a06221aa9d7bfe583a02ea4e411c5712f7890e87206362 2b570b3e4505e0c909b0b5c8516bf506c47f4a46857255536e9ed48cb668c8f6 c88352c9f9f561fd50106608d456a26849fc9b94b9b6fe630685500c8c0a9374 2909dfaaeafaa66a84f68336277f17295af6f389bad3ef74fa3f998fe1d7aabe b59fe7f21465882cb99b1005442917cccd40b9991f9ddd1c0a5caaec633e2a7b 0af13e35bb870c074cfd20e6488817842223336da2ac422534a4e47b5096e14d 31c21003ebf459dcf7d60d87619e953d60cbac7f3e56a3006f783d7409231169 58bbdc210a0a3c9e6d7a6c1a739e71385d260acb26caf1971161f1ff129cc850 a0c1fea9d30b676be2bacab78a0a803cb91d0d5b7dca34e7414b623d31d760cd cfd7370b09766f1129c6f7154fa9173b72b15492c96ff5423112d81aaab345a3 1c4adf684aa1c063c88f48b6d755b6ff3e234b4abfaf11e669b4b049f3990ed3 10287329dc43f6f23651cb0b6ad8cf44a733a6d62f5e3bf0bb564f9411b8fc53 eae584bd1ed75c877553e40e71f0fd3368fcf5ab466f60fd51fdeef2f3163ad9 20723ccd0d19029a89eb5a705cbf37c67fa7482b2b5cf32b269713f9e82df0a3 4b435f890fbec0afa63df7ff10216141b4cc91f2ef43e45960fa369643490fae 3946de1a98c9dbb03306cb44d1345352df983c0ee81c599642037ec826a95a73 81f7954e59f94dab306d170894f26a64a3ed794e58da478fa4b1c97d3e7f702a 7b17975da7e5f925b7c00c9e49ab67c1c05fdc84ecf7c7c2bd9052fc1792a993 c2a95abe1d8563d55b71d31539718f81588180dbc1e7613ced3cc8883b7a4ddb 4151bc7f598d8b5f51c06994350ce4992cbd5cf5f0af19131f2eeb2930e0c2a6 e6d57d2471095e9557746dc61e51d13d23a13a39c21cb2f5a025e6e57d955319 f6948af76dfc38f9f369132e61275d3931f73cab4be55e0c74c26f3ac06f25ce cac3b7a21c358d6bfa6b75afe0ac3ccb00c8e1e42c1cad83652d21f687953df0 7f80969a5e9ae3b12dae228ec1a9705e9e5cf8822df26f4b2e01d33d01960ffe 7f88d5c21dd827ae7133e3685fef7cd74348ede6ce206f77c48bfbc3ca8d9d16 0137e2fc4336f7f6e5c58ac7de684ed6fbb80b94f9a566e462397185a83aa3bc 4e09fed75cc9ac94c945f26099179285d323109b9e5e6495a5fe70352b47b23d 014decd861cb19767ad2aabb1df2a1a9c39a90ebfb0f9bba99336101a3caeb04 6a8e6bba480e9236ff6d502a670c9c3214af2c2c54d68ae26373be3ea9f6ba54 ee16d722d807012e9b33dc391fa877d6ff8b4c28d8a60bef98407d595fd84bea 01c76b8e1b6a8bc8da70dce6e764ae7c3a5e84299e5ee08cd549452e8a7ad5f7 7f9dddc19df35383423444f76b977bf5f9f8aeca8ee4d12b1c2d47050c4865aa e31923c564f3331aca4266e02a02fbb38082782bf920c73266dafcf5c584a938 e9e1a4257cf560e8c93178a567c31cc5b0da317989e91fbba69e19fdc144a850 d99b31b583a68991c6a729ab3e014e8efa878f4d73428a3e7837b4ccfb7ec34d c43dd8c6692eba9517699c6ecdcb4addf074078005e66901131e3a951c1b0edc 0c4adff4116912e3cddbd8af1e0a54252745b18c8069f00848c86fcaaf1b7aee 71435bb799f392c3fac1d37fbf25cc104123c4200deb7027a25c83f14e1376a6 106deb263504c267e112d2a5a0119a2c56801262370e5005798474cef408ba50 39287346a77fee5f44ad898f17384b4f8d6fd9f579ae9b1a247c0092ea61a459 2227d6bb59ff0f8b917decbf5da7fed3e6b1da14d1acff753c24e8900dc37e74 2bb4d37ed8a566931a8b423ec83989cbf87d6c95be7348060bece8b781706a01 08092eda20bdacec2c77841cd6cc4c5520f526f95d6f0826633bdf037d9d4113 1c2c619b184d8283b6fae53673fb2b157236ac33610eb5b83071d2ed211e0ff0 8e2cfee0c3e50d555312177c397f1f7e28c137f8c66adf4480320f809feae542 91e29a009e5a842cc42d63fb6df9c01d2d166b97128e9e30747cdd6bcf40b27b 015e3a83975191cee58d69290ca0bb424a728c4016bf7d7a8446045f3b445038 0e1644bbae9ce4dcd9310271cdef3f5a1e50dded3fc252ea67d4622f6cdb3f59 15fabd6e07c43e4fd09387cab79bd79d2d1167254e03ea4b2bc14a79d86d3d73 334c7d74257b32240e6cd3715e5ccad9c9854e499e2a0a9a965b05e66a158c11 931728bb4e71c9ea18ca56741a4ce91611399a12d55d24c5e926d860c9671f67 e8f5006539db80c08d514de53e33c196fedfd6cc69d25cd854ec7e620689f605 184a126faeaf6f5a815c95c47e9c04a7944eb0bf9a333ad1e170cc214e0c89de 30243847c6842b53d49252ec2cb97d6ed6380c6a1b51f38aa5c22734eb444af4 4fbbf2cee95f7dcec26c8dbc0c6cd3933761707f72014b23754cff935cb17bc8 9bf3ae0a3eac604de96bcd6601a8c7cf6e4b50a0cdb19564202c56ca566f7e76 1db713ff96be67e88510ada404d59b61cb72233bc848a67a49218fa7eb03c580 11e055e9d596101236c23c1ec2547d92d43609e34f9df12b5921a1eb86d94c6d d2577aa223e766db511f51c5b2e682bcf7bbeac3f1d62688676eae35d0676a18 94249cd701e10680522711d48a1c700f65ee7a588eabd3d14db6b00d3b0a883a d3565a2fe13cac9f6fdb94345fadbc56e9925d6f4e23a724aad18f2ac389d0c0 e76aefa00ff29a4f6b79828490b0cf6a5f1d80ba4b6a261c1e92db2ca2394f53 ccafb3c0c1bf893669cca0d35e6cca664e3506dfeedbe2e9ad03118c97101ccb 2f93eca42748aca9b75b35d4e93605dbeae6899aff2243302b90dc708b619b91 9c5f644fffc89210281ed0be8025d6f26bc199f6acef6c7d540db9074d22d480 73d571d06399b629e06ddb6570e0f8c2bbd1f77e533ae6299f52d41a5f7338c4 15ce90fd29a054ba8b0b6d055618880b09bbd224a023ea05ab2351fea9d803fa a989382b2acb0beba1b03b963333f871eeb5d33d3fe7458d5070ac2792197585 42f972b27b415208048a2b9a889b68da93265253bab8e0734d669999e1aa6d0d f26d0f678d98edbf10d7cc58d02907283cfb4af65ee711da72d049ca2d45db46 bbe344aad8977ce28c67cb5a68877a4d7c17f386a04a5c17616d1180b46e02bf e0ab79f061bf88fdd6779df98d9dda293f3d2795aa708a2de6490ba655834006 88c2506e0a3f6e896df79e698020e240452b32c6fac52bc977b25747f19f64af b4f594a10eede8ad967bdfdc073119224909381b7dccd689adab2fcec0144fde 397a775a0664b932a0d72a7c98af6d08319dcad3c32db270120b0e277d79a3c3 7d63131afe8aea760a530344ee8deb6b7da613e3ddab7f85c3e1f7035769f782 8a71c81729b06a8c8627850015e8d7561040187685d648d0e4099ac2dc090836 5d17966c6bd9faa7cda178016d03b4aa310d78bdf42eee6de5dbf5466a9d6c58 9791a2b9abca7e54d48afe35fcceac3910298b64474e867a3a3a8593201ec83f 65829bef7805b4703ad88fc1a7cf848b38c3e40c0b16a7b7104bcd0741403e56 60512fca2ed6efb77f8299b3b33688bb3e81f71265a1c4fbe1abdb266ead5057 2021516ef3799cc02c7a956d40f0efa1d4f95ff405ce8dfead6989b80b32bdb4 8fd4b02b418427901456dad0cd1ca52671894ad62abfa6f227ee2fb62538aa27 1be3f560260be66bac669acbe18472ee86b338e045fe6821d0de255ea839cb09 d5ae308119a926b3a4a359e0114ee2f938158c61f484dd750f4489cf0e535e10 e218771ec2e03708e7f885f14d9b233e1b7a7d262c56d4f009311a2aeda0ea4c c01c9457d1ad329799a61fb10620f4c0c0b500ba26ff7cc5c3351f1ce90f5b3b fa27dd75891242297128724a5e0f2cb0050f3952b21c3028e29be67af64d7747 8342bfdd59eb7d54a07830ae4d09a0d15064fc19fd4f174427cc33e46d8e535a 0ced66b48f6efd8b0230dd1bbd8c5550e0019d8def29913096e02d39292961df e98da021b8ec628abb0ecc8a6897f3324d7680db16ffb46dbe762c30779e038c 258d59f6081f46450745f0f7c8a5fdb41fbb4ddc0a0149ddc267630c2e677d9f 0f86b6e98a65ab1d035e0fd02262daf83b21c8ff9cab3cdf7afec0c43af3d1a2 25442f835deb1d4ea6333eff39668c84a94e30c57298169935b5afcfc36d7adb 303b1075bc6b497650521361175962ec0385dd87bf864def6f575c2adc3cd0a2 8ad76afc1d698b50abbdb1d94a5c86ccb49b17d49af4d36f39dfbc66ea953fe3 b5218d6cee39e1d1a74e500958b718e406a89c7f468754a744ffa8a39b54e8e1 fdb05ef89c3a19d21e8bb34783a60888a9be53e1e7672d1a3e6f5c6cda4e7029 1aa8d8d92f535ac3bcc0af296a3ffb2c977953ac1e75a25cb65c4f4456bf5ca9 890d1506854a507ae800a7edefe094979769b1e9ef9e376296e7b328830de625 7017bf0e6491806767601a30572fc2dca74d1de7cd3e0ddcf42d15373a04bc8d 71c0cd41b5e501771a72c57a6868c082d4076256afba7569814caec598602f27 376b3198ab6e5849451279c39453c528081337f80ef80e882e954f60b1410600 22 92d9e5b11118cae16a1bc26cbf8e6f9968920a0254c3b2c5877db6932ceaa20f66a9aadc5e070cdab52d60b41132087a2bc1892d8f5294515c595ec5306c68035378b35c912f0dddade9dfd15c93ce31aacfe54c61f8ca69c8441897ac46370b9dbe7e31917c7235b2b81fd805bb907677a52de7a1a10db66bd5b0fa822fb1073c5894288e20a392c5e3d0db38f295917f71b0d98af0786df5392169889ad104ac1bfdb0a87c41268fbb66995d7f6f0adc60f945e70283c2ea49b8eeef38b4051faa9c714b412dd76c2f7ea358d7637f1c295ec920e7630f705ba7f7373fe50b82ebfa730640a9f28b5136d14420304a62f19187cafaf131aecd7fd3bdf1eb0eb667b357b2e0f97a7bce57bb99975b44921c3aa43a23332a0babcd73e4365308cb3d66bab782c87ddab879ba13665afca42cdfc474956db57724faa7371cf90cabaf45e8f873564b4b606031bdc55980811dc78a2094bff14c28199403d57f0640fd5cce993495cc33ddb536d551af26a2af7912a8d93489960c2eb349df970a85e2f7f261ea564f03739ef54262203c36ba258948dbe4941b7d91ac075e560a148e003d9aa5839d2d4e17da5ea6a026eee750de02a7047b10154f30ed812407993dd3ae451da38187863c025e0d3a8f0ed98b20a1200c355174e76b46e2d908b7d480d1368262f147f71cbfd8f5e723a739677a316b6d9322cce582026b1c05d2a335ae99c77389ad718df8601f70ddde0e5d323aebfc709d27e6574193a00ebab62c02fee131511399a7c06ca2c630616aa1f626feb21a0eb98a9de5d471076fe3b331438fa57f4e0b84fbccc55b598874ef3e85bd11bdfdf6b15353442a09f780d182f57595f7f493fc9ac06c458d8a7f89a19e721e2d5f1e721d1967ae0f9b0756729ca85252b80699d68190879b0dcc3bedd62628032b5df0437b04ba0ce941f1d40b98d3c51508b6007946b6c74686241369caf65df71fc64c970cc503e9e09d2069a8cb439db5c3397e4dbbcd0ef7fe8093a2a8ab28fd8ad40412d3050ea154ae7995347014e8e2b7d44f5243092155ae20cc1b7079ef14b970d6ab068aa31719f15b87b524aad0f62440396e3ccfcdba087756a3875ad552aff1290c23e5f4060305fe03b4d492d0373a2fedb9484be4184b84471b5ce2aba441570082d6a4c0113e3a5361666e2ec206b3ac148d7b6482bbc5e74fbae3841169eb06496f4b5f529d78fac4f1a59f575b5ba3fb14279f87d5a37f78d9278ef89a5a05792932728e7b18c071827b9559f5d235486ca241b493c0e565fac373dce9af00b70b72afca797e9eb1300ad9c8728f48045bfe249a1ee8d9ec772dc5e99a8000041605e39d17ea3b20b2844e0efaf113f5574092b35da1e338aacc48c5dd6105691990852e49b5052c83821156ee2f8cfbcdb30c7d95d2f2af56c47f67a96a028955e7051907435e74216e0a14770ebbef577155afa6c30d881df0ed046252042c7ba13631f01d54527ec6cc2664933b57d7bff192066c9aaf9c53898695f70c2069adbed7a8ff2d8531f5a2139422794fb07ffd9c6bab272b322925a4148e08f3dc866d6c34296225cdc71cf328f04119c468e73a9be25d6fe611635ad62e0719b9ab36102bd1f17d3e70f80ba7098e146d80a510b576f5a0c1c7e21c6f38046543f6d0aa261413de0199f12ea05bf03f0412395e91c63a48485845e362360fa5d41f56e746084c8ef2c6e6631c2f938760d42c18bfe8ce2b830701e1a3ca09a8493e9a7dc9ac1db6e2c0bb8ad6bb5ea0c95faa2dd81e563a28bab999fad70735c14ae16d5e117e7e31e0eda7139dc3f3a5b75f78cfe2a898ff148b67a99c00740982d7425b45b2e572fa458dc5c965c5f3551b3a89ecb2b281faaaea6d320c0e9333342bae47309030877b42c6ebd3f798289ad26560dc2348637122630e0e39797c36c61d362444e3001ea8c214d6e343b15b0b071e2445dd8374adc6790bfcb3092a38d9f659f49862f5b4cc7e6659eec3e9b2396b64c3182e73dfd3720a85340d4462077f2d2c87ecff60523e8d5c78cfd55bba5ff34b738574e59e4b08c680e37dbc3b2060459ba126c845ba1b0a14d3185a06875a596b737de3f0ac084bee93168d5e6a7308c8b1debc3f79a94f374110cda4c3b367fd500401f6e50bf20677c99968b8630258e19c531aec38818cdc490e1790abd76eed78194fdb078d4e3327576cef04c1e2ebd2e8ccff0669e305da2d76387de39f0886590cfe02aeb9004ca8702de77a68b87327cb5baccbb773350617b87c853baef120a41e0bc666e3c3b50e914195085ccda3214dad2b79e2a855191c6c2eb8e50713a108092ce90c7fac40636e542c1957e97ecd5d83a5db1c56d4c6b09dd1d0e880c97e0fcfa64a73c3394fffb771ffac7b429ee67b97d81d041975f9ae0d925800a5370dd0cabb9447631c59067c5656d09ee3badc392c06963f817317a84132048d1100f833db1780685eb559d8b88e804e597d9d3f178eb46f7ea3da4980798291560fdcda32f5070f1f3462d1fba3b8662955ffc6b1249912a84519ed6993d1d5920a459c587bb6be73a73dabf95e3cb87f608ce8ff0d26f62aa02f8469c284f7af0bf15dab84d8eed10d1152f500cf24a358a7c52f703f078cd5fe9fba6a328a3b0988ab8d821c5c0538d14224a6829c88d43194254f8447ec922ade8aaedac07e03939440b9d880f5b78bd216df10dffdfb4562378fa48ae2a9309bd887b0cb1d03f995828e5eba9fe7e2db66da3322b19f035b2f4c79f995523d5c6dadda9ba20ba1eabce9b12cd26c80df1e7724c96371655dd0c5772a0a652e762ba79407290dab34b4331aba23d95dc8b61eebc132eddca71e16f9241b6435906b97e4cf54084977bd6e420fcd253edf6cb11ec2e7aa189b3d02163fd94c3ee64552f6c9230dd16639ef54a3f205ef719abd460f6d4d6667eaae7edb6e87d717977eeb950e04a3eae2c2fe832d539ec3ae4de06cc8bfaf2054597f56623e466190d0fb930b042bf2dff9d73d31b3b8b2d6a713495248597fd4362245d645c3248dee19a5970bc0dba8a070cab0a0b478fe2053c4faa28bf295baa80688954250a2907f8db20d649ed40a435d09a746822024fa785e1efe531a77b60369f32322e76fdf517d01218ef14fb0035fa4f663cbac04d8b78d314bb0d6ab1b3e7a53a7f760048f0c069069188e313020720bbd60614a5d68a2d0805c64d73d3762e44ae6a7b83f79036e1025915823feaaff5696db8cab673ca6ecd4707386202f2d1cb68016b0650517f7cb4fcab6f58bea3c53ee72d245bc6e27075aa2027f020f976f73f76688081ea64d9ff6b730503800af08ceb57529b0ea04e07e994a81d5a4b9e51480a10e08326a66df552b572401e2b2f441a67f71e1fd0771d204981d619351ca0e1e0c0d41636e99f7e3f3007c8fe8b75c0b84e2756b2b86f7785ce81edd3a7c75c507f46b3a6fb8b9c993225254bf4f848f71121baff7ee788e808de4e9222282000cd2dbce4ec700c4b572ede4794dc4ac19dbb0181c698134cb06acc69c0f6abd073b9a139e444cb973a7d0b1954fa10286c28c9b7847d831573c2c247be3ebc9064ac329185935cbf456a567bc6752c8a86e5d89ecc2e82e4913ea4c73d4fe9005b65db02ce67ae8e2080362c4e41bed6228e9fc0ce4095c3290650a053ecc9c0404ff3d1799b999470aa61fd44d06a60c34c08ea4705afcd7d5b86bda1cfa57070ab4c723ff193653c5bee763d016b952bac17ab62062bd60bfcf024f5ff92806eab845e34c83b374a37d962442784bb061defca3f86bb92d3f89fae544ea6f076f15fa92be0903d1dbef149574c9976297e5f0db8c67a2b380c4b25573f7b30bc472f90b8deb89846be7e4458356c5c65a97008f34bbbbb7101ac47aa75b9f089755a98602a5bc148cbc882783f4a9c72c5ca4a9e6209585898497b1f7488200bfea01fa829fb56834b0e0944e4d3491c56948447e038efb74b9530f73205a04c577a85047bcfbf3f870c0c907e9fa279608b77b1761320c075dfb10e837170764ff8ce80092e9a4edd49f578e697b14f38b675e829ad1ed5312887c5d644707a8d5f8784c1b59a6239c6d155f12b82d08dc79c14b1cbb83476ca859365de00e9553c678d595507f2aaefb206e8950eac162d40e342c07c20a2b3a3e272a720fb82d333c3e66aba6411df3a35f3d1f7e2c2181901aaf53872aec11b4d2d678083859e17b45758983b2d3f6d2c143c3f5c40250428a3d5610d73471884a309607a5ce1ba1db3ae9a61ee61b992e594b66277f7172dff0a517c96e245c2156ef04d3b14f2838e9351ff58de60b80114ac7acc6cb66bc3a7d8285d5fd57bab3fc08f75b51697dc2d3d55c017906ba365db248774833538edb58a3a9031de8b2150b2cb1f8f9c8aec9e7514e0bb38fd7e7dc5e429f4d10d3a8591b8d4200a813e107f08aea7cb9539c59d03ece09abf6997de541c4e81d0ccf68e3058d5849fdd6038a6e0af5d7f8e5487997b62b5c4298ef4c1e7a99c8acc37ae898074d0a6137049cf07a1aa9004bc2600b5a239d79048b835f86cd51906a95a4e0a336ae5a330f1f72f2f1c14b9bc192241d78afde9b8a225bbc0a720f468690ddc95450e1cd08e026d96b15011a4b03a514c66687aeaa0c13fe27e3b21ea3d04bf98bbd088502b79dfe29fed5f5650664223344d3812906078576fd9143e31928847d513e2a0ff47326e436baac4a794f1ac63d8657ed66de24c852d6e7db6f548ad4e1b8bf0f2e29604974b48be27fe24034f8609edc3a481d3b298b4bc5dd67e5493258a80a667e230413426e0d82f6a29d240977fd49b506603bb766643f5a23791a57c3093b5aad541b63860cae683927e6c642afa861403d106e55fc1d97f5ca8f80590d6da2cd340900d6ea679aded9df38c442a19501f24a4ffc4ebcb8cad7cbf4760bdb4b6a1ec37f60ead4963aa29cf94a8a817df929d39b161454025db9ddddd70362a33d3733aa874f0daeb710796b67d898064da0a20e8ba0f69e21fa376f220a6462b340c0e40ddc53357245ec302f85822f65d839ab7e3737876f2a488d5b0ca65fd2be689044f32ec0376f14b1a8e991ba8691534f6ef03708670033beb00e1c8cb1df6caba8886e7c616352bfd4dec0f7736dd60a7c9572e1d9a75a2b210029bbd260ceb701b099acfde721285fb12c0264873f43d6d22bab3e3acfb86706d51db3b945b88cc2bdbe7f79322abf30242e551c588d53090fca816f2cc84a05149ead4160ddbc28ff63ba1db4e1379b534653528045fa03c329bf0497f1210715a37e457b737a053bb4bbf79b8467c4f08d1a4b710f26c450db42d2ea5cff03647c8e59d58e92ee4a069ac47b2a2b417a345dffd439e317f258219eab3f4b08ba431e34c9d7a500d188d827eff15effb5adb21d393539dbe558b3917bb30b082722fdc84249d1450bee8feed23b350448c9c81c3a55b096c165468f3edfca00d52c67cf526ecf33935770cb960cef064b9fa5daadfeecd2071a3bb944af540b776b93777f1511db5bae6777180f2c950ab7156cc5dc1801b4384ab57781090997fdc8da59c33248fb118f6f0ce73b9f0d704a037ad7f461a0f41a55174f0f02445325e596340a6d4eddcac5cd7611b561f66e899cba04b555d2397275c0fb08d61bb1a125c6aef7960495b75016937711df3f3cc0988638c1244f256b307104283ef6e1fb0ae4f410a2c008cbd11f7c83bc605f1a944b7d55732fcc175e5f07f61e47a48f7ba75f56af869ec4c35ee8c6221388a6fe8cf903a7f8222673010866fcc59f17a45bc05bcaf8f9b3766ca7eccb9b6987aebab657694afc99208a03faf674db9c9933fb7f472401e2b0710516c42cf225b4b92dc9179b5ccde34f0ba8eb0100076d1f0e4985eb3899cdec838d2b77b211077754a829ba5b00bab306192bc41e6c54f6f2a9d60adfb2166d932cf1ae0e1342e3e0927df54165dafa0ab2533aa2cd9161453ca0ebd2c3b01c8e84699d3202c5e366c6e00af61f9b1108e75897ac1c112ae93f583e6a400bf8657d05ffe4cb3a2ff0da2bf46bff35a1039a14ef51b95e8f69191b66ede5cf9761928041a325e5b005165947c4c03bae068343da0b8d6787cbb76a210c6993270be23f9bda32420df5ce92ed71e1ca5907ba11d83d6f78a4cf82ba4bba67f680fbfa8f21d3d21542c1a64d096133b50a0263c13e3c9a0089de524433171382181a2382bab9b843c4b6bfd90fecf2558e00051658a22508c135be89e332ff4e9501f859e83cd96332e9244fa4a03a0f8e03d798975f109c75a588b5d17bc9c95e19dc09f94d922a931af2ccdec06cd21c0b2739d739bbe1db39e2da9d6ad72042eb236ad1b9d6c9cb7331e15cd81b728600a0511670c8f1d0f9498e36146a8bfad5bd565ded10c2e4350fca85e19807750ec7433f0862b35e7136183e5cadd1bb3789fa9c45c62f4841c7cf6cf8ac1b5a0c466909467fe3b1b22c7fdd4c2ef03ca789ddf4eb18f0a42c3b4454b280d90100b3216cea185191b2697e15c38c11816ecc530b2cbb067d70588c2a1a469b460865fa32d45b876588e90d397557a83a30ab657b6e7e97478fca81e3175414e20d3f223d3df672806509b676d369bf8095221d2e83cf05852887b8562f27e85600a52534ece6ffec518585369e603468a0bf0e5267291dd2d2f8e1e568aebe2a0c9dcc608708066c80e39112a5f2a8898f8adc590d9063a851d1be1f1771d5ae01aa61fb93ef39d9deb337c570a3626a2f3f4931365376a56743a22ddd35f47d016e12bed89505a3db2396c45e23cd1dfb7adaccfa1a25d9d6afec6a6d1c10df09ea56357ee470be6a4f5126c638db97c4f2a389ddaf883ec020c619562f003b0efeef365236c9ff90c259d2cc29570bf31afc3d39d680a30dc2551f6ceec9d607a973ed9b8c888a43e77edd806ae982cefa1cdea0a0308946627767595ced46001791e317ef3f5f7c2de8f8233bad1bf1532abc1b27d75d27a8023f8b85305203c6ffc6c0beb7c107c45e032ecd135fb9304d7ba9fc145225cbd26bfcd33c5602e86d8a55948bdba71338d5e2891504376c4993f5c428666b77546b0ee6804b08d2de92c2f5cfea0a0be135979305bfd753b840f201b3d4c657571c787b61680910e7e79063b81d6e23261b62694492ba077eff11b21e24d36b12e979d907880b668547f23450358144c02310a16c7ba2cc3d5ae40f21ed1afe6ec48412bf8d00f8d047ab9e959cb708fa5732e59d826c2769c8d39115acb794499bd720094703ddcdf0b0085a0715c8678df6e17ae5d335df411640de5cd830d7fb83fd8b44092121275dc2aed3e52576953e672bc6e8008408c881b61205d29ccc377431900a863ce783e08854cb296570dbcd915c83278a5c06614f447d56df8e934b5e5a09b1fbafe326ac97e45fe2cfecffab74f64d7ae75e31f750f4470b80dfbcedc703af0ed97a656689ad2253af666e385c04e2254184e294e93fc55459e0c8cfde0f035ae9173b1483918a4642dbe07dc581240a820e9fd87f2e2487c00a52c44c0eaa7bef377453960b8bd144e4a67143aafeb93f3895314bc6eec354665d74c30b536717010ed0e006d614d7ea4f66a72872cd91cf5cbe4f96f4005890ce513c0d342935d01316c7c203e3917cf7a0ad1bf1750ff37f146a94c5015712dc08470b8266473b5ff202a9d7b4f317381686d2d58af6f7c8693cd5b517eda879603105c307c62e00a6047378319c5cc013cfde28950f8b7804e126ae71801fee0a5c00004e7ff8f81b803bfd6f47578e1125ad2db84493095f34ed602297991e3ff207bd54419cd577cb09ae5b6316e25ac1c2cfd4f89a3af6e7ccd2a6f91a187d2d0cd6ccf6e0e98928855bc6cf1e0c78abe76a64d9f300a78b06748a9a79d2832808f8826881c1f4de1c981acacce079bce2425207961420e88787108ceb2254a10db33f7ed9abd936ec22b5fa09103c20864c9645c48f459499e2b8de99fec29407c858c429b2495de220fe24113ad3f1979fb6f15cba254c6bbb452a6b6638f90f8f388018205042d7c26a1385115b6d97305e51649f88415ffefda5e9801b7c0b280127828fc62f771da09670078bd53b7a1c0f7ab483c72dc881c43c59c20b0bf443f077861ed6cd8169ee62b95b0ab5d8fcba335a2e738de0202d7bcfcb8f0f665a25bf643d59c84c16b6de2ab3158a64a56e607a9419936b8bdb316937030576c90b147ba80251ff45acd9362deef2347a46e30d16740b7d6f44263c1c1c0b6540e2983ac304aa152a28690dcf573107989b3c7e72bb0dce9d9ca747ff1c07b98c8caba334ac334c77ac11a35ac3dd042f4a682697b7e21a461fef0f584004b8b77c4f84fe8901f7ef7b1b469faa05e1850c3de75c389561b70e03ae8b25087d60095c59766172690ab5984edc8d09aa26858b9a7515eb67f12c75d6071b061182c4a3db65334ea499f5c0314f794fab84a4c9ba692745fcb93726666702070975c3fbd68abf7d829e8f011821090233e298fbfddc3771c4a32e7789d1870cae55bb6725c3fe23ac65a3021590e3c873db6eb499b13a4a5b9e1ec6c986e70e48008b04e7fe7604aca5ad73680b55515f32935b2a9af9dd3e4929587f65df0de6223ceab75eaa447f1a5a710b6acdfdd84f85ed4555035471600b9e4ec5e404861b299295881bbc77cc931fe0433f5b7f3bf59c247e63968820e1e9f601360a6b032bc837e53a6f362f0de6fb58a2631e3a5747ec8dad4e263b5e8349f2c50019158309cd5f64239982f6b70167bc5091919879289bd552afffaaa14f43030fb924ee73fa988566e6c47dc603e1a6e88ccf2a0a80ed55302b6bdf94a605fe0f32f7ed12028f5de76c9ca6dcbde595222e594c6b93c8c8be341d6c49f513480ffba7e0ac8ed96ff53aa6276702b9709dedcf24f4c2540a5efa8f5125012a1d0361c0f92b7e14f49f7fd3011a5ca3d1be0e9e03be1ff94da43268ed12af4cf906a836a2bc495bb653ac4dbc8c11e79ad34a9ef66126536f38b7ff40b6d725140ae85396b4b26fb72f19b30e805c89f0094898878fc48c926e71c13e204321600759fc9546bf3eb84ccc62e20c1949aa5ee092c1897ec237ad1027ec2e576a9700693301c0b8d4e9e831bc050d1ee8f415c0ce32a24fff5ef50b79c6060f4a2e07785956c6189a19763bcbecacb60026d3776af94fb88d76186d569f6011bea60d932c0b09186301d34c3a39e1ed256bd7ae5c05b3c13a42773c3f37eea372a40e8f189c5aded55276bf1006cc0d0a37257056619f92995f24ac70f6e1d7c4a304c9cca712ed6ec96bae5a17b3a3dbf089dfdca2dac6e23b0b5da1230374c5a10ede5032e7de45ace504e48811673f485007470633b160ffcc0de92a5268ce4904446ba2fd351bf5ecded525b8de01e002382ca32200224e47f2997cc9de7e9b0721fab6ab692e58fe49bac57b8951420774dc85baa0f214fe681d5c6e34d8b6063a64d70dc6bf4245bcfee926c5cf78d3defd7f8cf6e6e3941bdd4cf220f27202cf18f1bbf87f6f21abb7daff15222df52b62856e7f7b2baa284687a2dbf62f04fa376b5d93a0663854dc3390b505001368a54755ed4a3cc91f2564bcc1d63207651def03eb6a2b6fb686a1ac11c6ea08e8f02383b21b0bea3c57051f2246c50de206142f69ad7a051b23aff871904a74f971bb016d482a99c30ac8889d0768010d6d9e94a1ff0eacb229ddcf9ef67b5f54c9c04b3af634e1ea46831afec0e001fee3fd46cc10d02225d04d26b34d9599d74298b272aaa1abbd8a1e338935ea0f1cffb6518a9e7549762f5c5b4addc6576a77302c7ca826d49b517b1947d2cc09e2a82a758d7acc912d9de4795c2bd51b988648d7b11b6a1a221a2b70b412120de8ed32cb44db2c43c6016b3767acf4dc57cc27d10aeeda01a2669bb69c9671078be05f0081a399727a500508717896c24b7d92416c673d24f30336a4841ca60a449e81ce615855b3487386aafd427a87b020c8d35688c95bb290adf336394d0edc8e12c888064c120c1f5f8edee0932374ae62a8e0634188bea1267580746106c715d88b86401ba01b5818c63e52107e24a1239effd48a3afae452b050322e0a30dd158b7d1617e54b9c4f239eaa722f670f1233ecec04d5c223af3417966f03e107ead4bc0dfd6a931a6eb99c2d5d6cb7d9f3797902b06e50049da9e31b3f02963cde5e1d06885c6da542ba65f332e09708a8e3bc2ebd5571e9cc45f3ff4c055a41ecd74a6fb856049637c6ea6b50b7272cb023d805f179296b642c9b22450a285218c637538eda7c62bce016474b6ddde8f3f5a2cbab94dc99ce982d47240dc667433e0024d2ebca00edb6df360459cbc45b8504125dddbe0060f752ab7e0121e4409817f342e283f2e3f1551f3b58077fa8cbd949093946cee5fa0256ac0a2406137fbef3aa02a430201edf4315dfd91ee7f768bbd0ea66e8f35c04d816019555159ad25039f0eab12b2a244b9a496f689426d224c33ea1ffb57b3564a20739920a5d7cf66ac54d47c45877c86896dd96d8b530a9edbd7035c1722069010cff98fedfbe615ab12dfdc89b954d3b9bf0bf8d3fd7bc9df1f7c1dd3688771508f8d4d30d7be6a66116789500efe21b1e92ca03e7b1358291b32de3d0dd9f90031f1a5d6e105ecb8804395cd65b4134b28b2b7d8116118fc8c05b95581a0a4b0ecf90224a639824bddac80460d55ebf8e62917feec850d9c5fed88371708c090ec874c63a31d958df041b9ca37f6bd128dd7480bd26bb33e9d97ab028ba736d03cf471e66634bae963a8c34fe471e8259fb17e6965ff2ebbb279c40a55cf0720b78ea31ec64cb4e9a3242652b016eb308a34ab9b779fd9a3862221698de03380f6cf16e297fb217dea0cb6ac8eb8f22ef55951bbab237407f84c748c1ec6b750ef55677fea0e0b2559b14a367ff06a9ecaa9d7fd4596fc980e4a1c75ad1d0250bc708a2f5505b8f48f1de0625fd58d4498620345bca8952d3e24067eda38cef0fbf70315d79da519acd58cfcdd0b29ac016bf744bc86b9967c6ffbdf7cf14b206516130088b246c0caf2f4592dddff59800e121e32338839f50d0ab708dd01600366adbdc79a39969f231bae6e76d9a6dde4bb01517d1ee41e0c01efb4b3ad0019354130b9cac0b34dcfd2078af5b1ee29e0f7a04162835cc88ee3631fb170c0c77eaf9213eccda45c0ed9f486369d40a989052ad711708d1935388d99212af01c452c1dbe2b3043e8c32a82f868cb4a2169847ef3cdf231c9e5029776e2cb00f98dd11063242e7c3383454acdeabe2f22b824c908cf8d5f3e62070d696390a0c417e7964cbffa03657bb37e42ffcc64d5a0b42ecaf103af00d3df1a33fab10070d9bf58a728c05e274264966234c25a33a876a87b02075a1c7ab00620e6110030c7231c34312960938d9526b3c949ee0534082d332cd459ee4649a1fbdaffa07e2719e544af834b1eec3eb1c64fd29d4e1c49c9e80910344c9f5f342075b2308efa5a3d2e7da8644a1637bc872d2f5b8abc5deda5f52cfbcdd13d1a5b2f87d05e3926b6344069ab238da7a62638abed9cbcbe7751e15099459ca9c874468120d7acde5ff0a32ab0bc631a11a11820f8c23009b6c3d123493cc37d2a111a1620d3197fc09d0bfaf39b50937eee037703d9de092c0724eb6ea761566893317100a6900ec0eec169462e681798c160f3097d94216b0bb8ba238b112e2593499d70de840aa37c673b2e1fdf7b9e654dff07a556ab53b26201ff2eefcb46dd20f040a395fbf2e10c26be9721b08e67b7757827a0a67b889dbd0960decd7227458120847953fdf2bcc35215d3af046bb92142ec4dea4d008d824926180608f8e281000d02722f6a884f947267d2c896c437fae2f92ac73f2af64d900c5b63d9102f50f7aa5cb7974f26f3ebdfeb28fb7792a2ef9f1ea313c5f3f81222a49bd1da0840a0236391a96c281d360e866a5c72e477bebe911c2a06ac338e1c57d4b2aec8e02b8e0b2e8610ba747e7495c3540d2ea35d673bcbe707fa87df23b016d58b0eb0042ea961ed2d75c79d35083b9567e358ef5c069942456282b1476cc4a4cab660b4eed05dcd63f6e6149f290e830d1197e621ac604830d5ee81c281f443d6b7f0db8b64e870f915ef180522381195de0ed5a26f259592dc693f5a835e6f5899505b73af986f032b559932dda202a583b388fbd4e140485ba385e43b755d7a8350508adf1317b6f16ac0779e095bb4fe4397abb1f0e68ade8e37954272a57910e036488be0f17434b1aeb76473d0fa69f4c7b524db6e1943290d57b935ce3eae405bece6e6f469e8d09e843054a94ed848fb34d5956bbf721baa011f91fb4c66207e2c76d7ffc59104022d43069035679855419a7a7984e397edd38289ed536f50c3ff95bc7305bca5e56967136af04d92f11dd9eae0b3b27f76f719f71309b3f00762d9b3ff2bf30db3e455044cd4d7d570ee35872411a40a7939556f965bc030704cb9b6e07e00246febe622536f6a5a5f0794b292a8b80ebdcf95ebf52e00a07a09ebc9db5847e97ee4a993d5f505f90b659a9d0203470d240771bbf2ef9530d27d57830d62e55cfc7d31e9884e915c5b7cfde2b78c0bbbb50ca4401ac6e790878d3296e91825409f2aef37bfc3dd4b74ce5f2784ab829ff2bd8170e110cb10a93c206549033f45f6a06c7df431d4c4c7e92bed0886ef8a9bee5134ea9a68804317f612e0da0f23075b53d3310000648d33dda6aa770153b0a0c04179e501f02de7b76c59b663785e74eaacf61762a7a1cb6b74d7728016c74742cca5e78de0162fa65a69e56504181391c6545e00ff37a622bcc8f013882dcd9010bab7e9f098085d0ae0d80f43301ed3be3ecb7b6e1a1083faddf87fabab3c78851c26974083067ced06590376a3f2b02d300151b8f20fd50387c930d1221129162f025d20fe3c60fff2148f9bab332925e6949cac9ba74ab260813e69749ad24d18d378105021b7d1aed602be328141dfc8cf8ff982dda82a5a96c40b4f3ce751a3cc8790e5cbf929b4e43cad2e1af6245cb700a265b3bec4b61d1a8776020a5560a070d0969f33f8629c5f66489e3f8f90dac2f692864b752bdeca029221592d88e6b7409c3f15f25c5768b0edc343dcba5939f3c94565589e8077713b9a0de02d79d0b093cc4b4ce1db3eebe85eb1a44efe2d31ca2ffd9fd780d6182d0f830393522620bdb771278798511bd8f8eed10209b0fb8234e59f1e68888eb2ada93d10113bc04342c1e7c0b11b7bed70fb9298c351a8d80d35d38ab5e33c0bf819987baea2903b1b723a36c961469377e4ad65aeb950c46f7dcb9d74f83f3540859e64728f40f283a331bc661e6ba172d75d2e0f1aefa8a7ec5a394b77fe75691e40486cb46057cdc2ea549c85fd72c81886698799115b19d689109a9d338d33f817016577b0815c5cb3d44f452310dd20621d45903c838acc2f9ddc2770b2fff7cf1139e2802c13865ce1f4fc2497200f9bb495500c3bc2b9b9cc1f86fd9a9c0af1c8e4a630031c3b9fdef6a1da4790ea9f05b99df5bb2b065590926d263a530aca8c2b0480ab50647b857f0bc80ee5e0fffe473f59a6c6d4b685fadae2a1b7611f719660f0a1f4413a52bd14663414bc0257002d2fa7e2e86526e3ad5790ee3690f14d75b0abae817c13196e5ec62fed760709a3e579b66863b8a39cbb0ebfa063f0a7cbd06eae1df4ed46c98b756353b18af6b7eef296863a3d31d619d34ee80e3953232011a3fd3fd645aad0e9f2d38af95d27f5c976a5816f76ef6e20dc70c80d31733092402858b465c1cd7fe50b744af8a58e11388e3a5708ab3834ff9d3506d0ea304851928b1e2d5d05677015fb3ec4e9beda7b97b4d2586a34840629c26f637e4052a9bf782c974650c2be26fb96282180550a6d90ddab197e9fe4d91952c934c0d87280b5ca14e21550837643977b73ad24d9cb03b3ee618794cd2e5a3155b6a02b93d5c2053f2a17df7092788bcab93a731c93a831984125ccecaa14dffd0c807df6ba0a49cd5d4c0caec4d670bfe168e4d923741367b3f50a03c1ea1ef863b08b25e4d118fcab730177457e73b5cf4faf3f4443c8521bd07a7df18329a71a001b56ea45a1663e3f1f11d8511aa49e15e3cb137254e954b7ca7f0fc610d26b20aec60f8f5ee3e9d462039798c8d0f140242096f0cf75c66232f22a32cf7ec5507c9b5c43c58fce8e4e28687a1805af6334a9a11b736a2402cad89712dac6e440572a993e0f4d4455a6863e6c6b10a5bd58d5d9172fa603acdd3c488bba9a2640ec71d62f8665458e564cb4523c1a895e096b320c5d5f34aa19a048ccab96fe20f33e81cef258db44894281d2c15becc43f0e2a37ad2da905aad7cea25b77d9a0350aae4942be9343cb68633d88f0bdd5ecfdebcdf347a55553a4b97816ee1ef0ae78786d7dc7502f778542b163fefbcb15cdac5cf9c6accfa0daee47d9f6b1b0d233da8e62abbf61c666044dd77eb68f3c5a79eb3ea99755fff33e5085a5aab0ef462fcd4d67f9c7b40ea18434c01390e6d3cda376622a166e0d7b7072e3f2503f8bf57615e0285773469cb6612a76d92328da1a010ca2d0794ea126e80f2bd00d78d02fc2c60a3f64a287de1d5653bb05d2da6f32f1b86b14772693d4e12060ea80457812ca2554ed9e44c8332b80a7383337f81e2a94ef97afa9a83451e38049e9d3c04b41bdebe0e1516a2ab8ca0674c707d01c82ced0a7c3102d0254e77072a3ee20f7271356652afae3208093a7000cb4fcb68e3230a787cc42e6d326d05433c34e8ed59ee83e2d6abf144beb911ae28c47143405b67bb2d8d9084c5ad01b5e212f418fe441b5a353876d6278c135e7d9aa33fac3a93733b93c61194e40688bf935c1ab6162866348fb2740f8e67bc33e26abb12b825d24798319303b400e66adddd953ff91481b7d0c4c983b29e5fc2d373096b87749130097d6e43660cffed5daa25e306074f5a09f88b20fa6bc9ea86615d9e98c2c0a82078eed95003498a2fc1933fc2996c73d82e1ac955948f3e8460947779bcb5257b0fc41c6f08b03c2d4e9f9a437d2437325ba78782bb0de3442c907a27b7f2a7ffc761fda50407d99b7bbceaaea57875e80f26126ad88c8d09789941228a3ec34800d7791906e34f21727ecc3ff11534ef1c7843946469e33d0c385644a082cdf94077d76c00977d380552c2e6629a96cb8793ae9c58a3f88e04312b2760f91ac5d692bd1e0b766a667dbb9771d955572648b7dcca0a75305d3bb370df1e577b291698cfd1073cc2bbfc7ed47c0b84402f29b0634632eefe188e7debfca0cbe7cb48b3cfaf0bc768e20c4014cac3e3ca15da53537c05c5429e04a43060c988663e4fe1aacf085f53f0a55bef34192913942db876affbb3fae1ed5f17e1ebc9f9d353ab1c08014a0c3646e77b5f25ead7ccad33c4d40c75c09fb2208886d077c2bd625eb8430e77477cb5f331e95ac9a8be3c55c2b3d6d73a1eac470434c41d8eaea970138e075f35a65d3ab8ef7ffdede43a840919ff9cdeb17de0e60ed393712ab339d7e9089d2ddfef4864cc21d7ed3f96880e214c0f8ac8c7319ed7dd5d8152075e95da0faf368c7d42a787fd8127cdc6a412c802524fa4329af935876cf1599831c42c051d3f64edba02a5a7a0672f492e5cd248df3bafdc66da693a394a29bafd56cd08e8ff5e0b65c6e46d3826cb2d5534b419ee2c6b2a2672ee1d92b42ce70f2e7207fc91498fa72cc44ae7913f8a6d42e1a2db89deef51c1b41fee656718b82c71081526fe4675e2fcd87be90a1287e71258e618bcbb7db48f0ab80498801aff4a02e2c2a16a5a177ff3aaf50096023b686c547d084d0056b10e49d4fa85864d650a7ea0e3e0a7861c49d673ac53266e9043a6eef45357cd268695f8216e26e97106f79c7cdb585246396d27f3f65a0be7542470769028255c0dd621b189e835080357591250da746d276f4b932fa218964df6227b6b1145868d23c19b4dbce34d001aaa83afdbef5eb9e86714ed8d8465eacd04ae11992f3aa8ecb1ac9efa8b4206153255d219812b70490ef8de637d6dec98f19d87b330b614fbf011ee6c0f3a02ad49d1178157061716615182ba12c949d379991bd6eed839e105fb88233d660bd20f42b5a1555298475c0270f2b7b47015bb29585442ecc71e7a929153ce7e004380838f0c02a7d3e06b6639fba7d250e1c975d89c1cc3bb38d0b19845d6e504e6ba41fe5436090affbddca7707fc95f60ca3955109bdc77601f06367bad4c04f4eeed9fdeffb79cd8e36fdbb3038c4c624715fd07a48dcae04356b577bdb60dd5fc6d0816a9cb4c9c7195d88569f4a6ba4b1f62dd2375f7add1b85dea22530937b9dc7ab6169a0c897454476bf10e120fb5840219344c4de65d0aca7e55e808573b62424a6057c0c323d7fcac81f4ae0a0b7b1b31cac790b5e625eb062f6209300b125c8efcf418b312d23e4f15293ac3194af866a7b4709baee85de56c6408a2fdd94d0c7797818c2d2309209a6e32724c9f1e3c8828bcb52155982ae6a0000ee4211f48be5f0b2b869729a575068549063d97f0728e197831c3525df51c0d2d87ea585677e743812498b33595786860050bf07602795102fe20f05a42bb0301c2a59d3d6df160c3c7fcd44ac27b6a4acca59a3163f30295efaecbb3ad3603e09cbac03b271e2fec9b5598f612c1d093233cd790ed9c5228ec5e6edfa61607dfd29302f33b84786c3fb17b6febad25767f153aa2755aa3fb0c3e06d11dee0dd9a22a3bf73afdc14a126872384c64f2f636aac5d8a87db1f9837662f6154f0038c222331adc4ac8a8c4ae677af76688e6145615a8ccf8501699939776b4650fdf8c97f558e9f95b13948d5c9d60e900cbc2055347dc4b41ffa966b7f70bc50437494fa889ad5c6686b0a69f0a573538f5a7b86543dd67d8b960d04eaab1920341adea81b6b7d4e0cb2eee4d56db0317556f98aa9c266c0f4d6ab6d09ab18e00b95306896f0dc6396486c3cebaec3c997a3e1ec433a17264c640c419156eff0ed34ec919165432c50f2dac7e1ad1cc7f2dc387df81457153ab759b27740a04043f958618e8fbe419a0161f0ccabecb820943243fec18856b0979f1691e40d404325e456c11776379052eec1f5e5bc3c6f7e44ec5d292e651f5dce3111b1d4008e6f9ba7a184ae6a60adc9d8393b43ad77385ba8117555d316e2d7dc3fceaa409c9147b5727d66b95eea2ce3ea847e60ad0be4a1a455072fda76748fbc424170ef92aa6ba23b9a63f6ff892f5c536b1e9b09291e7cb1b47cac2657ce7cb1bfd0fc900c4a89f8a6e676fa39538ddf0c8c572181dc12ca66862bf13b0c8db29e10792dd4c1de50e0b3ff1d7cef0a4446c52bea00d794e999ea5b244c87d459ba906b944fba8dd559b2df25967379be7e631f382f8d05659ddf1aa61c06231077a090261bb6cda9b85227b3803db6ef276da6a412cbd524ac3403bde9396e401870b7b851474faa7b4ecf51087fc5a6b56f39812914a47961c8fdfa0cdedfa59e3075b1687f01292c8344c7ac61d2c4cdac3c02ff7269f9f6c1ca5eedb58c1260b0101d6b9814538b5018f5e5ccf0b30292d73560d63c380fb73fcee38788ec378047a6c994ceceea7ef000241d7d9f2cc2cd74b87dbad5d3d66f32fc1a37749fe0148690210f553511464e003e689ac33b8e1c8690d330f66df4a48fcbc52b92f0b3fedd7376e970be7b1f4288ee94fb9c0711008b032fbddd8bd8086ec5d088c0383740ac83379df53bfefaf7803224b8951e1849c31ffacaa16465cded123090108a76ff73d8fbb9d567abbb89cf8166e6ecbb8ae98f282193fe587eb2ab0cc033de4716df0c6c245c5c31dd4be2c89a2d9bde699c0d7c95516dcc9e409c7ad0c37614ca66964cb196d4fea602bac76a60c75937de567a26796a53be834a017013a4600971dcd8df3c4758f6bd21f8ead8ee17ac0f7890b19a0bcc377b172080815c16f420f0b4d55884210f6f153b5ef5ea6f1f663a3991fcf0f25966d044502214b8484ebbb88db6bb1662d80bcbacc70de501e34ef4b1052fadf0e12783e05d06727812ff7f40ddabe2d2d6fcb83d2ce79a65f4883f21990683a037272140911a5462221a79dab36eaf72c1b6cdb249990b52a0fe7e4bfabbbb12fdd1c22076a88f39ce2ff234cdd70e97048dc40fe21154e09df562b78e0768e3c2ee2fa02d774b997d2b49245b13983d2494bf7705a1ecca7dbf7aacea87daf47bd813408b3db0c6a004c47075ede405cf729874015299950c5bf825c7908336ef0d5e6071de715da2e8a476f73bc84f89e39cabf496b41794002d5f32b4dbf5a9289f40938b32016eb8a665e10c59f989cf089e9d05d3d286b2c83c5092d613e56abff006419bfc0d0009cd5c3ba8477a02abb4c9cc6ea0cf7482daa6f3935b896139b01f5cd778d413aa5b83ae4af0566e02135c2e7293b75d7866a273c6aa8f1ee8606058c6992e269c9b51116da6d6d3250d79c18675c2b5697dc3eee68f617420d0be76ec01a40aac669cc29f290745181ed264b9446e08c0e99969d46b1556861091df36faf365387a864db8ba7154a5453de2137a8d2cfbede31418522f709e60fac6b782e3075fba83a26c6c001610e043e7fb2f4d979597bc50f0c87a291b901946aef3ac851e81d0d06bd324f1843e3a3b9993d94e24714e49b24f48ae5960d6eec300c43e9ed42a5dfab3d0df7664552657ea1bc52dfa00874572480f7a904f58162df0792be94dea4ff03a5586e0041156b7fa54a6a52a25f6eef830511062365eceecc8929d9dcb3517da5603b2b01b501c090a871c2fbc55c76e7f380011b86838d1f2e96b18c35634188154f310917caf7c243ae1f6b437e9141c8330e8d60b08f5a9536796602137d18834176aa48fbdc616a27081a61fee25b317d08b6a0d9ad0f238e0af6294b066623a782d26cd6de9d1de327e8d6188090439d0ab76e73b2c2d3e3a582de7c332d80f7cf537c80d93db1d0399329c0c7bcda3401469891c447c680aebecd3f92ca45b838cf8fa7b2dae97749fa0aa33d5a6df00e645dfb81149422dcd4018550464042f2ff4b6d948cf6bbd15548d39b3af061055f2e0dec36f6ce8295a11b21a1fb2ed745172343e6e188f07817ee0dea892d0c9f3a4ed82d641749cbb54decffec80798916b3fac25cdfb33d772bff165fd70fd2b11d7b465c4ceadf498f7869acd2253d694e4c88aca2f5f1ad426d920b7b0efac57d3e87c7f39d442459f7563d70f5191fe57e582c635fb90ae1bb580ead0c9ca4bb61b06221858253526ae3683b9ca03f69f91a85c806324f56a56d4dce0a4ebc72bda5955302fda39f081180030a79acce778fd0a881642bd2beca36ac0fbb83309b5f5113800d51783a244730d6b642ce3957a9ac1c8ccef5fbdf156c0291d178d45b21ee8c2ae7030bced30324b3304534924284468ff909e35c08e303241b1140a84f7312a9aea26de5fe87cd7eb189822191f8635e7514cf69fa0803e1e031054aba103d309ff23c6e1ef6ebfea2ecaa4d134b3c5a26e750bb5e750bd1ec3efba130c1c0e764ae0541877432a9a134890daf637c6b7aaef90bc5c70e20a8c95c94ec2e6fefb21f1113f207f53078098d11e4009d9f56c86773000e0319d3e595b90e8a646c8e1e5005ab83ef9284d499d6420e069cd8b78138137b03762e1d0582f50946687193b607f292c96c38ed1903914cd771f45b0da5514a0a6c1e04ffa86e04595a71dba9072dd5906b0656f886a2e80db8c6705a50891a021ce1c47fa9f5745168d44cad0b642c7f2d922bfab2dd1b502cbc85d4584b1d078d9890022f7b1962400bee36f4624944f0b7f995ccc87a6dd96dc6a430e65a035cce61b1991b4de039865a7b8fa678ea585da815b26baf5d60ee4649c8e89506ecf7af186c82e68333db8b420769bf255bee1e53b5abc0784724672225658707449a66e0d7eb33bc9cd9f6b913077bbe76c3c1ef2f649fc6846900a9e4fae30a333186d3af0819e34bec9f01f278bdc83debb2251c456df0b84cde6d16a2ab032811f6d0c95fab9b7ffbec0f53147971bb28d5e70927dc1b595c86baaa15c80cfcd3c79b16e0095e76466d482300294cace148270a790a337a7dcbaa430ed105ca79b271a80af7dd38e28bc97adfa8155bed333c671461d9666e2db21d64d003a7126f26c022d3c1b36804fa0eddc311dd84a7bfc4a0848c701b1bcc9076160646c69abac06425953fa682947113f33720dab0c7e3622e7dcf5a520cc7f56301be0cfb1ee90854f8bddbcb6d63bc83e6db96b0296c2ac97905b1c0ac9188f80e6d6acf6a20a80a24dd41c8c1cad0303200999abdf2b6efd9816ae29b812ede013d5a17e3e2653e9fa4b9f5ad693fe06152b9314ecc9e8b8e32d8331cb09ad90c4b1ee92bc59d66741bcd904e6b204d87ef3cbf135e19a7438970b571109a8702e399026238b71f5a5a458e1cd26b32b1daf994a48a327afa5aad003918e3740dd9dd391ace6d134f1003c570c059241f8c0683a303411c7644d86209fb99ef05878e6643f3f474276b5ce4c1d76037fe50bf7321e995cdcd8f07994e02389e0e5fb41ced19511837ee37e7502fba3fb5e4ab33d2caecc2dd8372cd346e0de50458105d8e32bc180992a6231a3192a686878e4ee35037a2f36d583d7dc910bf0ca197fb5318b07b41a6174b7a18c0e1e9eee3f9df46dc0e69266fbe71e90ede07b09a6072a5987797255cb632ce8de054bd9c2abe3a772c0eb2198bfbada57604e62f177c630b9cf78dd72f85ae17b5584946e8d929e418fa9cddef5366ecb00926457109c1d6a920a7c053472b5d54e0c410af44ce0c562dcdb194c02fefea01513244b2838132196da731943a1bce14fece3ec9bf22e704a2cdf259c918d40115fbfae1b09bbcca0f753519b5f7e017a26572d9d53ffa376bd3508595c41f08a10612f500efd9e10ce26dd9545d696bf411b98e4afb80e84505f09b8cee0c021f03aad100e80bc15ee1fbf3d849e1d092be486415da42f0a25ab82f7df576076cc138d9ca6ad45ad33239f334c242aa5b750d5b5595ed6d6d9c94bd7ab91602c6512e7216bd8c8a990a7cc0e5f9a2866f07e3b6e4df0a001e5dd7ace49c91058af2771e18f7672d8b48615989e2c7bd47574983d57aa46f9fd562d8d7442100779eb8d30ad91f5e9543ce5915b47e0b9f8c4a4eb294af64da76348d29a6ca074a7d1d89d95f82866a40a85bb5039a597269d3ce9ebd8f3d6c71e4430382bc0f4eda4ea3b3616a1b94636c7d492a82e9ff868e7f261881fdd241faa5b38f6102e22a5c69f6b69be687e52bd487bbe64a75abb66a58368aa0b4652c133f9a3b00c92c2306c3cb5b8f5f6c60f26c7ed101738c5328ee0cd1c46c10462d2b4dd0078f2ab1d86be6fd2710be0fbd1bf5c148b574802a9aa0038b38f2d82b264a87050b770885637958546d679eed705b972dc9ca0395ee8fdf43c3b1d76d76f3b10785a6f6a9611f43d2f09a82c544f462c982dfcbc32429080e2d4f05a3984bf9075b8516307ed325cf84371a50350c86b1e155c480578a1130d5df634f55cdd20c79df9b61f6c2cfa3e038db159e8b36afd4a5ec5b012c52340edfc63255e0e30f564f6e12c478b0989d45029a975f5c3e5a7397ec3b41408b4bd212735ea697082650d2f76929631250221697812d9f29dfb2f221f868b84b23477f35b8c9dd086113c3ef243b321be5b7a07b1f1256075229db03f5b4fc15aa00b08effa3c402b941652073ae0d3a3f56c68e1f0b373ba5099bf7df8a718913a858f33f71710590dfa937e90acde15289865763c9ef197367bf716a79b47c598105b08708700099743822bd5a049bb8be2afa285d8bfeff7992c95a5f28f2749751570cb3320ce4e62649f9389b222370dfb832cef4664f19e1c5dec59bd0549ecc7900aa430b625be6b684221e26509d038d58f25c6d3f32db09efa6afaf9f0313a12cc659063e89654c1d590cff19daccdc5f94675f1d1375999fa39f3dedbdaf2d65fec105903ea624e0d9009e1f14af218f669cf9a97a12672ed5ecc52b746f1fab78b005ed4dd717042c4a7300baffed39eb2c2fb5584d7d1f442ab3d741e61b63b8c90d87f896723315285a01cdc8c237ab2b558caf27883b2250978cec131c8eb4bf0fef06125b4bd986b095a07e4d756b9dc2b8a9e8b7ec749f75e7b030026a6d0b075ec4aab8c3808112cdee7c16fb29154a1ea143a57f45d468fea51171cba8d504075849f5d72b9c8e395ce119889c0b5293e493b59d7ba85cf95963d03989890586d31e41ae2affb2e6a2b96a372866ce646fcee6c2740bb0e4726d601d25af0e56ec3e33fc94286c0c2102cf35a98e8a2153a31cfa5e9f7bdf522255790090068388f65ec318bcafaecf2f234a3f3b0bccf7e6bec7876b8339e66d584742e707cea273791611ddf0ac02d5c9d193b7068da88c717742731ba91e84c2b1681000648d6924a11369fc5d2bade59f09b707b28e90f1dcf89e52276d069679ab940dccd995fc380e89ad64efa795a244ef66441b633d286b3855a4a70ea63dcb8e0372cc9ab9bb112dd6b596eee8d52ab50cbd5dd7fe62c03fcb14554ab4482cb60466c9a0565eca33f75423ee4ee506df9de4d2ae801070b8bc3bbd0298bddfc300ff7fe6ec8a3ddacea1ee7c658f08bb97e264a944cc17bfb2e5212be4ee9ce10e329f88c610d9205a43f5b690fc17ba426971197ba0b7ab6717503c9b50af8804 +generate_ring_signature bdd604377d0adea441787f06cbd71142cf15d55ee20ec1bea85f573661c71201 2487ddcc1b463edd2a754b037e4ebce25e5fef7d87a227d6eafa588404345543 35 b089255060a9bfb64276f30e0f156291d61ed74376751cef0c3c519bf97005cb 71344fa8a1d99b228c90f79d26b10a0936b43185ae7e456376f6bb527bff449e 2798b734c642a9ec401cf1a33c14c239c2a59c5f48eab81450f06b8824589ae3 3aed394a9a3077b2b7b6254d4ff647bb96b7438be120fac9204096b5f169673f fc7b91dad8e497665fad7726ccff5cbb5c611400743fa25dc395bc95f09c7704 d4a4dcb19764fe5a25a5963903ff0c0b9134c4b8be2cf7f33e7bbf42b59618d1 38b63cbc455b1340fbaf53d4284ed6a0db201b4e4aacfcdafeb6273b1a307e3f 0b8d7c46fc803560f12ef832de6079adcbc1ef9f787b45de01a737ff5e95836d c50649bf69d8dcaf869c7e42948ab1784cfdf39af5a7e4f710726f439b25fe51 cb559abd56c267e40422864b8e58cd27ee9f9076f08da0fdca1939d20e6500e0 83c0c2a194cb48bd356a96e50ba828121c00be8e57a8ffe7460fcdbd543ec13f 1cf20a067afc5e04c12b6ec9ed5dcd70d647fb13e390b35b24503db9590faa9e 77ccbfb6fdc8e4fdc4b9f1d5b642a0be86ecb52d0760e9d37949c2a08be3b17f 95061c0154e8feeefdc01a2d1fed47012266721d0fc3b81f1e39ecce4f8b2850 2009e95362d812eb87acd566bc521ad12249dc3b1919d8af30ae0aabb389b6ea 7a8431ca2ce7199d1db1a3c786fc9405dde6d0628952cf95174f0d35267a41fb 8fe2a2d24ca5a08caca966a0a5175b9807f3030631c5b93b7ac35eccf25724e6 67a3edbba5207baf306674a7c24b2093a4bc41c3d43bbfbd545175c8c6028bab eaa0962f91596cee665815b64415daf81bfca68eb9cf5eaedb9ac0a0c4ed0900 2f87c482a8ebfa50f9776869b0899613d31302f61bb9a8066182782b738db442 1fb8bfaed14ae9aa8f13807f7a07d705d47cd642349fb1787734233b7c85a3ed 35be0bfdf4b725f01e4f05af0bb9f02ff0dd4f612ded87a2340a1574b3aae315 4a0fda512f352146f7859748681ba33d2760bcc72a20db5922691856f7e603ca 33f200d19c8fad76478d2eec8699beee2061ea1f31a6f28e764cb7025691266f c968d9bc39daba4299986612b3467ac3d45ef0b830dd62b497e52219b175fccb 22460e30d871caf28c49e4cb59cf296682dc386f5eec6279ffcc150faca7cfde 1f92d5efa075ca1f939b27e1b0aad182cbca7f20d2da676ba1992d5696dd17d8 73cf74bf162bae040c2523789a0bb3a96cdf781f1a766754a2e2ee1bb200791a e669e339a639b731135147dabc872053f458a767ca38c35d3e9a3049f7e8908e f8183272074d1f826677ac9d576cb3f558fec821f5e32cb337107ad6cc85e63b caf86118b2e212f253e171c428e935c8eb6cf6ad085f25af4a0a93f9ec59ace5 9dd75314c1d8ba4bc81914778dd77308015880272b65bc37c402b1cb832a69d0 bf83ed74a2f7948e17828735a98afdb798a3f8793d1df6d4d1d8069ae7403dc2 b553310ecd79a8254d329a3d6e2002c89a501c06b6ae3b7feb152293dd26397e 7d483ab7dcec571e016a577f74301a6614a34734be679ecbc324170dddf06aa6 57993693eb82f28e7ec9a7bb128da07bd30e23fe4522dbc460d96cbc87482706 9 07d3702cd16495adf74d067f3c67b51d29f3a130cddf8ef785af58dc7598830a0a7fda5bba20a94a7dea5262be385b66d999070b3e542ae9b4cecb64793c8c0d06cc61b0aed2017c34529ed19fc056500db5030dcf684c5582aaa353be6e3c01fe0424fac328567920ce05662c2b8e7ab7ef3e4acdccebee49f3e00e8bc9f70e30b829d77645f9f76db0d2da4d1d6343a3b31cb1338a7655f0161127b74c4b066de546b7a96b09f516c2b71e0cdb3b955ca4795d385eb223f3f8a0650491020683edacc93b3ff82c8b14e468615d09f702a6a82de37d0e09300c2c8228513b0d10225a10d16b92d46b1116dc764eced1ebe164eed912b2dcf835866038f6be075b5fbd663b44e16b44c864585b74c199f8c960dda5a456a7c471233d806fa8096e556b365384956823c73ee290c0c9fa4f41cece32cd60b72c26e9e9c7b6b00cb9c01f645e586a2112cbac077143f9f10fd1323fe5612ad0fd868ad4fbfa0a00b5db0edd482c8f70ac15dc3ccaabab86f23f389101524a48b93270901e0a59052f727a159f763909ff10073ebe1b612eb5f5d4c128a13285c30fc4551626d706e514e3fe8c0c410a003ec217ae1188e3c8b317030c21cce978ac9193caf00a0501a22913a76d47f5c6e6e53a88a792336053e7e0337d3d405845de0ce32d5b06f7ab9fdb839cdd10956f59e9a4b595de1558aa57dc305e80a13a781b209987088f2606e5bd4caad396fa2852a5479a5b7068fedac6e79d8c68c97512315ccf0faca367b8616b7e954cb434afbc335e7e01ea7d552e77178b1fc800c771b7990371ac859b54d208e308b9349f1aa0c7dd0914f52b87f315c0f80f6e8e5709310b84edde2055279f3d92ef61a48f490a307fd8d3833f78418b9cfee2d68aa49102cb79ff0bb0901fe0321fc41c70325a33b702c56375d9bd85945bc7b833c32200b249c959d814f048be4c7c5414e5fe801324fdb2a660240da411172197bf4b0880627bd74f967b4c15b2d289740fe4362777e7beba012ba7e3c4ac088ea35802a5e42e695d86be71d3c48f85598e2d547c6adc27925b16a6e6b6c51dd29f960ca5574519107556c3a36722dfa78fe6f217c9d15b5494455ca7b7813b3f49e60b126e9e535115da83522a0c81aab9fe2e5cf64e8273440fd2d00388e9363d7b0019dd6f01184120ee68f4bb80cb7b3c043b32ddf0c8e67d70e1afbf48c0252a033b410a7913402b87054b54b759ca6bed3acda99335218077ce1d5549b81491089343c05de444ab18a470761a6a19e918fc8538d7b5b23c2d6c76568e65edbb09abe66447a7261b689a8c5165faa9dbc6fe384980148adfe99a4e7222ed929407b71dbaa0a5c448a79d80a085ff5035a9ece54ea89a353f5ca61a0bf57703720e6e4ac852485bf9a029e6c24fb973f3f3840a6b532a597a91595f89441cf4b609329906ee8f4698bb37be0a0e0c0e558e284b72c6da31e10afe0feb3cb3b24e0679b40cc1d175558e2da2995769b12ca6bb22444730601acf9f2d575baaa91008912e2592f41b4916a9f9627c1421864e3f19ca0eaca32870e47e402c4f114503e2d0f6dfc6d1733d090cc82aaf8ae82b892fe1da599acb3e8db2d3eecc14d207ed7cb97aed6d6aa0fea3d7b6f314639e092a91199c9b77eaa9d1c363c0857d06f572aaaaa4991f7a55a9f036c373fe299e3b8a85cad50c4d55e01738acfb1703ef4f1bcaca154e2767f56f40b6b01f60e76072fe91b1e8a0405beae04e103c0f5afd1f9667e1ff33bb40fcf6bcf3c760201e39912dd8149cd73abde8723d2406952d166c303c7f822195f59d48e42e1926b6a35085ec3317c1e9d024498ee90d3d45f5c3aeb18a6275cf207caa116136dba92954a85adcdc5d35bb2a4877e609ab43fb2f51792862dd6e42d2ff5215f8599774d346dabe90f1b1e6ec30f9180cdcfd1d2c93e3a88a48ceaee0bc5f231be2976529be0fea473c6c9b0e7910a70308c2cce6d49d0441b6d0869724f81c7cd4e565a55db9738cf4d52aedda5d6502ebb03e4d191fa13c70901e22daa333c2eb7c259b44798303ae974375d0b46f0e11b516dbdc8fef6b5eefc4923aff4483becdd5d2b8bf34ce8cfd8d0cec5e15063a5d0109e704ef7dba81eb3d27798cd9ca77d607cf18251302eeb0096795b205ca8e9ed46f8ef0d81eab9316bbdb265d7d25610239e52e6bfcafb598a01d950ad2116b4fa5c436676734d9a46b3a483f6ed2163237bdcfc30f343e74bab739015938ea48af543ec234debe7061a50fae07698d0592606b768d1368b9d9ee970d59e6e5508c3e58ef952cfdf29cb29ada5ddd254b43620a165942ac42b3636e0d9992f500931ff7832bbb50126894780e600da117a548d25e7e98850715e9b1073ff3d31215f1d22f9ed6d8ed3c501585a35a2a15abac89cccf016100842b53044852569b4ef3ebe7f616721151bb96f8b7c0605e2f6db08da5fd9028ffb77b0fa7933c55e7ec5aab3f476f89dd578895365a15434b54ea22923185ea4195ba0d9c1ab9b4b168dc8e619b811129c12d6fa3bbf34684fb8b3a2e26e42ea6ace309368fe6a2f687df4ce7e55fcc992d54891bb9b158a9bde23924206fbc198e090649da00f34ae324111749b7a87b1d932bcef1bc86d5262f79abd7dccf6e2600025dc1f187309f39d5d22617803a0c332b11ddced9bc6a0e6f13ab3b6cd40a970c2211ce4663ac96be5a19065020e41d5c9ade02ef4092f178d507e8fc5e05740587a23d310036c02d3fb3f6ebb3b0f89e338d521edff6cb43ca48d9fef7fe7003e1cd86995864359357325a63826f23bcee9261e32429e638ca44c7a26832420f6e9a25520c0658a24f727383e0fe4ce6ef8f664364ceb69b67982119764f0e03d51936fcfa210351d569193956f06fe0945a6c206d56b5f88f466364efbe6c0e32b7ac71c8c912c7fd291d4a30ab857d9fdc38471d0a43d97ea4c98da064f60fdd4b2271de9213aa4b957b4926ef7cee611d31c6b58efe9c3d17432db1eab9034ed19720d9ea9ed4b56e41ecf233ffe45b2b5fa0c560fdb493c8ce21a4327d033a0b44c105f1f453cca7526c80f726a77eb3ce460d4ac74a10053d0385517a0836b69d48e9c3622e883d1805c3ec2eb867d34cf15e27cf5a06e203907bdbb50c +generate_ring_signature aca251d46d3b5ba026de943e11da65c82565e3661f1aca050799c330dfad3d41 b26123f9a3df78771873063ddd286b3b07e8c622f56903f2efce7499de4478dc 1 332885ac700a35ecfcfd9d4b87366e052b2b64a9b71cede41d1498ebe448e3e6 e884832fa0894e874fc141dd3685f50e5db4e63743002c9005997948552f4b0b 0 8c0f522b270f65db7c71050982fd3d32b0ced0e5effefed89fc3967241a13c0546873e6f7476e32fa5d1fab62092353e1b70a3f0a4ee672a01b2a4891973a40e +generate_ring_signature b775c2fa902004d9f8b68d5e8d02f5a8c1a5368339cd1e9a5509f9cb5bde73d5 7d0c3bbc111cee95905b30f237c3a948d90feaa912301831d3b3d0d765056b96 1 8594f65a950e1a254db53911145528dc04b86702259d209172a832b7886d2d2e 007a01d31abf4ef99af89f0054b67b885b1964b5ecbbcc2f1c9e5dd1157c2a04 0 59ecc7db6aaf18ebc47637a70364ce3729860008353a5d45ea9f6ba60ef30b071c5798ba34f54ca91380c82bbc813bed45c308f081297d0deb1a30b6d01a8c0d +generate_ring_signature 677e5736293b69b1f168f92d1f194d05ced68b431ea47d89d1805e285a75aa96 c7fe04a57b9bc912b8b51cb394ed14d95cf3175482764e5a9e311b79669acd05 3 bd2dd96a9f97843f6f9f67291f4ac742f7dd142e4bc174d83242322ea2ed6578 519650d18da99601cfe0ddff1cb8a6c3c5a3166ed0f4b4d5df08e731f1794af2 cb6f9d2a06625c1ca76b07ed61ec15de053473ddd9b648ce6c912a3d176ed6e3 7fdad023cd867a5981be27fac5ded58a4fad51750510df61abceb2cb6cefbe0a 0 f49bfe07a2a130c32c43a0f50a689653d0237baa6724342b5b22447dfd5dc103885c905d22e6b0514e5d68012a93225e02f450e4f457bfe8f6d80568343ab509a62f5f2934f86c9f102d7dcf8b8542fb1b0e1007e4ed5f2ca7114beedbed8307df73093abd8d1fdc654d4d054f0f97a669312f086ec8f921f88fc80870d6c20309a5ff593f5ff3295f4ead0aded9ae766692912b8781c31d4839d407be330b0eba7413611ca47f2f2f5850eb1bd405418f32e0afdfa938a3161868312b13640b +generate_ring_signature 2c56d0869d611a98a8583af24571d794a9f7d657bc9bb2668f54c91339377b9c fc4553cdaedadf367b81758a5b277e20625549411a88c7325bd8a7129bcc476a 2 e82c7b44deef558ac8ca40818f843d27480d89f5851c2e0ead9e2fe58eebbf9d 66b8b0b3f84eac9fc67ef4d8f5324dc53526d1906f0f830d21bcea089e0c42b7 7e23cc4cdb791d7b4ec1ceb2ef16f65d29edc81cfe5786a62f4abc457c4a600c 1 8c2d85e64db452f7f8abee33c16e41d3ff5f5ee6fc92dcfdef484ec26db23d0ef99aac0ea09e2510cf67ba9026c35d25afb3df4622f0e9fd90c83820a710b30559d5858dd33a3aa7fce35434ce2d3d2518f16efea32dbcc142f20aa4abde16057328909662f1090424514c7f8ca726d47f23f631816b60784e8b634bbe5aee0c +generate_ring_signature 8446c895fa03164711b6289f8dca06a575ccd8558fb070d1f07a053b6ac62a88 8f78a27ab3ca15e3a040e4ac2fe32902d418b7317666becf389f520169099aa5 1 05291f379474a1eb448d27e66553fbe9df2a5133b15392f6a966ace067a870fc 7bb9bf9cf9611b1609b19513734454f28f49f896fcf29e689a937abcc07b4803 0 9052555dd0c07c6c34f6abee431d9a5d54d3e735ad367d8e926f3d3fa169b1047f24af04bf43e4a302de49c8a402ad1cb742db6c74a47c132d0068f40151d408 +generate_ring_signature 89919f2cddcd14dbbd6510687053b794e5b8f01565250bc8968b781f24739668 0279f476b72eebe10b6db1264b63b2b074144dcdd4c0c2764cd4d1815acaf713 2 f82d16dd5f4d75904b7b9195713ee68a1595aa8d9de4fed48c72c0529734f52e 04569b72e8939aec2b6db3b83ee34c21326a951c83915e1f318435f609445b41 824dcfe5b0051072ef13f8c9e183c0d1ff5f0dc9a2c6f397bcf1bc3d626a1803 0 83123a75381c606bc3ec2b0ae44fc4a82b653963329738cdeccc068951f91806f12bcaa4ed51da12e414b650e698a99e60b5fbe46553ee0fec6f414abdd6e603b08cf0e2280dc1df8c959689b318c85bf26f36d4bbbc4af33ea8a1ffaaa7970dadcc791450f3af3e2c47e5f314913b78aec586ba5fb69da2a8d43f3468ae930d +generate_ring_signature 0d45a262ba21084d5b11dc6f7df59cbed5aa38f517ca4a814ff490923916a652 9b86f1081957871b7575cd304b3cbaaa38d3f00703de9cf9e8e184128f864375 197 d0f935d39c2c505fd5a8121bd2d4a741896f55ee2470792d924dc0101a6a0366 460c54ce55f47189b9ef38efcd9c2677e4f3b7705893761ca701d0fcbb17530f 497b4f9949d0021f02b66a82223c4cee3471218d072dd5bdfd3cd75f22e7c0a2 86234ee6f836e7a59fa06e91798301543347f89beb69c47a5610cd6a48b80f81 eb8da56b6a7011e63dabec4649def5c6b1e90ed607e3526ed329dad2d201681a 2d35789d85de0049e0b401689d2cb627e88d8fffa91cc98cb6a0cf41d596c936 c3789543dfba6c37c771f175366171d91d5ee954e9a0626df83d371e1c03600e 19096bc68e91305027ce6f497869c5187fe39a606064f01dc45f696486fa24ac 59731aa199ccc0a87aa746c6e6cbb76295bedcb6269906637ebf0ec791706b0d 608f849ac3d56aa72ea3d80df7d5a784dc4210ef2196ac1fd972e9bc7828b378 e98d5ee1f8de14bccf0abab68e749a9c3288d3d0fe6406d668f912ce03e5830f 3ecbf97e70c7bb254d6afe53e6c15a9e240f26fb732e9c8ad1f13e6f5bdbd766 bb32c560feb78ac2da03f973c620b99dcc06c726b2a570b732c862c95a1a8f79 7475630c541b4b636a91e93ee3610ab5ababc266db827320a8814c3a347b9b8e d0a372bb94a4850540f4796fea6bb38d394a47a935d2dd4a5326d86d501c767c 74c6fec0ee4b8931974d59e8dfdc4ee0e28e3d1418d23428234b48ba8ad5393b b688d964bdd826f6748bf989babd2c55febea15e51bc92d3ae961509c0e336c1 90d569472bc45d3201db602f71869cbf528b2b6b3fe19105278ab39bbe1073b4 d9a36d7afe645436cd02b3e61a86cae1999ab5242d039b4097508861a5e247a8 22c1541a04cbc85028279c5acf1b54b6151b82d09c16513b646a14c6944c3f5c 958bd65d4e627939b83672a58ca806770be18562c061c2a225dad1efe0d95b6a bb3f7a2da142d46d520a1741c9c24d85804ea3da66c691d836cc3b8c5f7421bf b5194a6a410afccb0a4963cb71afe4c177cf90f9a7a3a3d4e9660f1ff064ea93 4f7fd2fe01c9dae2050735a578bbfe329a5f8e6153816ac53dbd34762f87a8a2 6596a10ef0c3eaca356f8873042150216ea7df61d25e36136df4746fa77ec0b4 860402d1f5982aafdee30a7f8830a0f4fa64877255abf4a5e2abde02f43e5c2c b86ef36d3b6873de1efe9e02e2083d3bfbde0ab81aaa37af162f1a6fa0eba0b4 5e7000837abddf0711071b6d205f999327522033d068d3d029187b083b5d88dc e7c7f0e549f921bfc164a4b724f261759c46b147b8bbf0e6e68c39503c198b64 fa55f966b88976d859d6e4776ed2dff074761de70d589a1c89c86279f0005a3b 75900a1d92813d506556b6484ca988d165f54008f1a8b50e0ba8c7b23bdf871c e7bf33d171b351a061ed80d3c52029b6627c7b73167d04f11e339da701cd0d6e 655314b8659808e3993409e198dbfe6555053ed5e0c195d39808ea0f45972b49 89f9d50a6e5f312d92da3763101453a9ecbaf69e84f47c21d855a05bb54a219f a64975ef5c421dad5eda121b3c3e58f818443428a05263053be3da99fbe77df2 5fc852cc8337bd39173274d4f3cf102e0c2fab30ed3d8ad70c753e3c344ed4f0 61298f604f8dd7e109b8600eb9a84001c3d7dca3efae6229dd12adaa4e3d2c6a 07f4d5b629d9c2efe6705f2adb2b9342822e615a9fe6d98897d74fdd89fe2878 5e0f62fd8243023c0b9e7dde96d4ef14e3079f4b0d7e76630c670c76847aad5a 345e572794d11d758bc761d9f22f977c827ed332108e810b21b85cd6378518a0 8afce8fc456c8724baa05516fd9af2ef8ddd29e538aaf1214756f683188a1bef 08897543e204208ef1c1118b4153c450245dcfb2b7746e48e8e389275e816861 d10372082654fb3dec772174e0967ea577c5d93d4ecdef99c70867d84829a10a 4e038f0e07dfc7e752952b19ec3ab3355b2b92e7a39182d3eca79b6a3f87d14d ad67fe5388f5e92e911157fb2b034c1a615a1eeccd3858f255bed067540dc711 c40a5eff26f7221382959ee22dacab3668894870fad1934eb8ad8a7e40ed36c6 ca5f7f63c498e39e135e8c18f0262702de1348dc79f8204c216651a9c7f9ac71 e578f8694a5e92e48f06d5709ccfb681b9c8a738abf20861aa51d61872cd3348 b238cc4ba4613e26d7d41996e6a15b7a1b4314aa7157a984eea7d2bc0706d00f 52a762e09b64943a304ac9c2e110ec56493d00ca752ab2c715ba72f7d69bec89 b5b444ec26de88fc6ff295056545fd65ae83c012a45683b9a22a379efbf186dc b74cde735535b0acd9ac6da6a18c10c4d3e889f1b0ee33aabbfe3e942e9f6321 ae98eaed6da71726406457e4fb6b8fd6577e545ccd7aab51e0c5d51a44265e4c eb366a542d43efe8e33f1f3733dfaacfcd97e215a44ab0cd2aea54a485bc1ee2 710ad4431c43814d664c3a3e0507f59cb8c65c3fd6f6b9382e84bb308ce2dc52 957765427ab11d99da32c054c7c68f888f02f1f2fc10c5883fb866661b3f0e1a a453b57b38dbd7b2af86ba28379d8832e3bd57f3b54897875b458ddff98e7949 a9b5ddbc6bce04caac06aec9761a507f0e09a8bc3f2e7fde93304b5fe5e08673 e8854e7fb42255389b91010d08f90ac2d86da53d338f914fa5edecd400d29a7d 15dd3f808f9f9a4e1f0f68a6157d3541dd24f5e3e51b0eb51543c1ba1dc7aba0 4f187a0886930e1129c97e3373b873c4be9875f745ee91a2e20c2e713a133d8e 64f4cc62ffefdd1b73eba2cd09c5f7ee3f2433faf82129ab79ba6884040fc3ef 735c79657390255c9072a9852bb1d92425811a7703b9809eb3793f4119e00463 5b88ede31e1f160f65b6063921a2bf8b5a28788ec8c3f54089aed3d5aabb983f 7c1252e028be184bdf13a60a46bd476217cceb393d19db69ef94bfe6f3380ac5 d2ca845fc232693381d777903751160a1fb43e818d548f0a0151dbd23815b260 d8ac75db6b5eb39e3f5e23d0b88f82a51552150419ad35e2ce1eeca110df4552 71686dbbe145ed2962b857fe2b732bb0006638ec2c91889f6ff79e1729554bba 2ded0ecdba64251aa08c39c52d0ddc5eed4c85df82f9856cda1e0309315ed47b 765d23651313db76c45f44ae14865aae2c57f8c67beaf102cf299f8f4f5d856a 998456c7aec76489456b3700990c3bf373bc85eb5643a15225819c91c8642b85 340176f1b4bb47be1b0108d75f4f06adc449a28a3ee4b3b7cd58d186cd6c7c19 7d3293cc9c72bbb56250a56c44225e67c63cbaaaf65f2e3671e3a8553889ce75 ac9752288d33835a09b7110f63c38c74a3f24b97fa26471a9d5adef3c4d27373 e152f9eef552515995ef4e9ce1257287e5c7848da1a19697c0ab30f14f7d1d97 08ffc32c3134d2e1ab2f57d3b287a8f6ed83231e756905136b42c1b3aa69928f a5180c65796b1a1ab3bb051ba05483f31e84f49c1eb893392fe987187ef85c63 8e5e0df1415bfe86c31d128a94174622e9323cba4e1adb06805e92e7b0620fcb 8bd78cb19bfc01c452f2ebf461670e00a3c3aff5f7ac6a1a789aa8e697ac324e 4b65071088cac227ca7b21538cfbc0cb09d0be5736b852e44f1b02508900cea1 6256b8a6eb474cb662edf8e72d45598a24d9c9a5d0c2d4ce90e6e4eb4ab72f8d af391f1ce7788799623687e8f64fb61e742273a8a7344a6098dce9f6e1b1d78e e74249c6cc363e2017d459fe26ff53745f0ebb45716b03f735b00889811935ca 5010e69034d8eb2d46d1a8b4f0982c2ff82f6a13e81c61ed262939eeac5cf3b3 88f249e548a21b58d4e63560be38b14054257f5b88adb0c1b2db7248035dac78 925e9971e38bab4865df5368cfe172581a48e8d8a25ffcbbca2dc00a1c765488 e3087dd5dd33f774691f876a3755f311cf77bb6972eb2c6f56928346973d8e99 56c686807919352f157d48805535d6824158026d99dadb72a104764d7983c2e4 46800e442d18322d36f6978aaa3790dd145716459f3ad4a04be9597725af3767 b32dd7aa461a9faeef0e495886d5d8c7fce4f8f5b7fb3fa42ac513bae5903793 b58508c057d5cfe3a4c4524c96c74f5af636b5a9a56cf1bc4cd816e6d4d465bc 5cfc9b6f9b677441ae34f285fe46e36c922c15ae955a9169d490984bc8b626a6 d58b254b005b382262704dac32494e11e867b30a92df690ad0883a5bf7bebedf b582fd6d2430c8651f15e80ce36cec80b79d7283bdcd5168420fcbd779d1e9f5 3e684f24aaba4b871045cc521acb81cc4a9068e06ae0a7c0c4a7d5ec7e2947db 809eac13d43c2fff1e919d0d5a93a17f97775858fd822b787cfd9d040cb0d341 6a6b279a65c84c83083de47e5ddf51965103b6cfdc39be8f0920cabc138d1b32 11753c1fd004eb63e3296009423f7a455019ea6122a22e06d300edd724dc054d 2d32f6d1bb3a0761755bea3b77ad838c0e0b3fa3fb52186d2ff037fdc3c0f198 3f8fc50bbfa21a30257686beda5a777728a60a3d187fcdeb5e361ea75b4d66bb fa0d4059819fe819544297431c1f676d1b486f9daf8611012e8ffaba1030d767 1902609eecfabcb88c566f1f737acc62b41c4131178eb8a8ff68506e2972875e e536199a16fbc159054a32e48cc24c7f151500e82c2757353cca78df3b72d22e 659e8fa4ee5592b00551f6f3c6e8746e6c05a31a46332717964ffbcf24643b30 594bf8cb04d93a3811fe7bd3d3f1a413775aeaeb28d3b06f7f29617e58e3c3ef 7c9d7a620d47d9512b5dad55a6f4fe98940264b316372b74c0d39a1f7b355663 ce45006b86dd046a5a18e70801360901b0d6174a695418bfef4dd036c55753a1 f199d0a10dcf37bf413f9f8617863577ee4a3b86454e3e4c34833dc51ce8a9cc f65ea29258bd880c7924c54f538b08a9795212830240e606e5d751b3ec8e3b6c 4a4093fcf50674032c280905fdcbd285a626d87b9d077973eaa690ca95c4c71f 2f660dc6c1a14877e7898aa47ec02bdfd278385536fd9fff368f5303f1b82f70 05821ee0f3ba4a2451eb951afd27a624348f9bba4561b1fb9089e974aafd1316 af33925039e6797a7d7663d6c693ecdee23099b60ff22c559635b5aad147814e c3662d59040b9dd071b53edf6e41943332e09c6ea7e4e2ac12062992376e7b9e 67c71e230ff2274b8fa392df01d43ee6958d4e6f07da64410754ec5c5ba52881 e2d574143ce458f5886f24d031c49180b3f78c55309365715ebcc8a3620a7575 63160938651dd3ff9cd282fc5aee479afa13705c7a2c43cf8fbaa286cb216070 e51f74ea5fc1d517a12b69e5451f3f6780c5640e5d590c380f18a3300b935c3f ea8345cf437c679dbdc05fe2bf30dba47b35fe45c5ab2e5b68592c4adb6167ba 893d54c1b4fdca988943c4d51d87f37185139c9cb4f8e3626501e36d6a6c5042 92722b7d3ecd3d5f6a50ee26efb4de32b26545b58d83886da5d09518880a6ac1 59255ac1b983a2a2e1551a8f19b4ce70575d7ecf4970a250e6ce5634e7220a39 e76d98db55a8444e9d82f28903072fa1b9a940298b2a2b892ec198bbf576cd39 0c624da5122c9be9c1f0f3d6cc3caec5eb3c5666d13241be0e7f2e0f8ec303d8 0b5b10255ceecca806ddb8fd3f09448367cc961d2cbb6e544811e5a6adbb7b82 264ab5191f54099261d36b2fa67fbf503042ffad98ded6ac5caeee9e638b5a6f 1ea24d52a43ed3fddf2a4fde7d13fc73c8757e7446cf7e95bcfeb16008895c22 a369d000754b106a32c7105623859a2b44d167ba2662474d64ec90103cae1782 87e6981a1eb1f422b6c482628204998236f01cd4f563d9b883decea4781b65c4 26fd4292f36e22a31a5f17f918364d0fa1af7038b64eaf2c77abc071ff324911 1f7fba3490dede66a8ca0b0f67f069f48943844c22261c69d586cafe9d3f86f3 ff7c7a106db23bf12f87313679605e6b9e0c5364c20da190d5af82b5b6b22fab e33cfd1d5133ba44214fb24911a44deb554830bec522fd27f0d9c3084613f413 6dc71dc6f84a8bcbdef2d72e2fe12f21b29fe1253a015fd95dfbfe755486313d 6ba2e3f0a288699640458bb12023f8b1c6cf01e292f386730a7e04ebb17462ae a1ffb0a73d396083ce3a51fae7a47c10bce4e53f736331ea791393d9149ad791 fdb87a472b97c99a0c6bfa8c8ce93c6327dd796f3d71a3a34c4c81926d6da322 91990deb0c917e2d68ab5177ea86fe68552736ac7b702fb81dd994581f60bea2 093beb91bd66615026efeafe94795e5749797c8339f61f9ee3ae96e8a1a5e0fa d1bbd9eb353fbe9bc9c78fcdb9f49d56d310e287dc698ba37f5943660f2294b4 8342b638e5299018ecacc42e496e7b476d7f35ffd6568600573da43331f36226 0de99f05c276e608427855984b17f6a513383f9f3fc5914d59b85a214dfb85e2 c9013f923e6e641de8a15f21c821c00ce771fa5f36a35d6eb1cf71fb4e633296 a3ad1f8872b82cbf7e2607b5058ae202ed1c4f6b5a963db93c46722295f31479 f82e25aecb0428d36f2cd0ddd00a066b4d1dde1150f0a098c2df2c51003a180e 0b12cb3bcac1c79d798fc51b32de043cba54ead89ca92ebacba60d25c1eeb057 d5a7bf94666cca12b65df7241020dadb44f4321f8a1784a3467c2a2950723465 c439609c1bf47a2dd78f7bc5fec76ea959e61a531ecce3c5132f0470a438ca75 88fc504b3792b714cf1a6f09ea06a61fe625aeb30656061e42d839f0edb32db6 e2564ee229515f37c70214195c39dc123f988a4ac285f53676330739b5622da2 ffea0d4088d7364d36827ffd49a02c5e67d2d1fffda60e2268a361f9e665dc41 b90a124da487e998d333f782e11a2811d686dbb2387fdcdda8a5d03197bcb4d1 de4c52684e022e46d24b2085c15ee9750192427e7a9802e1a36cc1ed2dffbdd7 656f299420103185383c44c5962ec031388ed71a73bb7044d789f2ef6cbaf1c3 970fe375583d4dbcc1c7ee1ab99dbb0c96090524ba6eb8824b1675c15dd88d60 aab8f210d3a0fba5dfacb2d7173f21afa3ee0d6a987478f4feb3ad810c5f6f03 3396e08c2650fe809a12b5f44bd599b2cd4fb31c4f46ef409a49fd84a77a01c7 834731fb9db2e5f2d6e27bd7301e26be7bdb804bf83c1937c3c4161aedc86cb7 f242fc60275cc30369ead9e735952f6d7dd15057595ca6f609dc5d58a4791703 14323f5c93b07305aac7426b3799b4887a475c67dba4765ec8a70fd80606a399 dbab57d98c775867d49d6c52423c0b126804542d411ce9ce25cf3318c4550e13 d3311c559c2b7a814beb3beadaf4222bfb4cac71bcd7983d7217cdaa543228f3 de0667f53f27ec36fc2ae62706e8f1f31ad40b7c41c75ed114bf0e330fe87e2d b69135ff45f3dc2aa4801bc87c3fef26d331bc43ab564b85c13b068af18108fc 661d565d6920d157cde619ef12b6a1c5423c76626316733b2f292d9cfda997e7 a0c95af16260540b15491b134d8edba37295aea89c0100a1d48ed2c2b6d48503 92526a2a4f3a617b69c8f512790a038a7108df70693170aad803110083c50aa3 721bdf17a1d57f5d868618a0ecc1b30e5c7c532d4313586febd088aaf1e6e0a5 b15a677ed4b10725e31febc960832c10fe1aa4fdef17c7689dfe10b4ed11981e c556abb91a48f5bf50334d76d1a57a158b724738b1958de18091dd10c248388b e15542597c42cd9489f86844ee28eff51fedc25230ba60b8cc0e4364bcf91b98 d393f580e3514fb5b11a65053286acb28f27681933179ea9a876b5f8f93b438d 89e823eb75765537b62451caf9716647134188fd01e20fcf5d64c91de0fba39f 3c2aa2372f3c532107f71b749d27c2e6ff868b6284ba697cdd6248a2282f7abb 47cfaaa08546aa6c24db8b82a782231827772af391cece7b743e458a4d2f27b8 022d88759e90a3a7fe32cf5d4a589e2b6945a534e72ab513416410d5e062d0fa b1fd2452b06dd3cf267af2838907ad6e951be731fbcc7948b41d4ab9e2f3e6ec 1e669738dd5d5b4c60d68ea7b83543be705962589858f38abf7c94154778340c 6704174c51e37e606f2f59bb4b0d81e7911a7106f067275bccc9cc728167969f 27c3e5582d3ae50115cc2ed27ec69dee741d220be586a8e4a15bf11061f5199a b3e3cff7095f52fd5d429ae13feb9bb07d950421ce8d6eb5545c9c9f37ad7e98 c4e86321804d5a694a9cb01c250da8cd8ad9ed0018a57f753a9727939ef43636 8747be44c733b2774d99dc0ebbadd1d5a265990f3a89ec700cf2c2bda2432055 f3fb7830410b46dbbba5df560109114c9b93cc53944cf66657bbf67a4a3dab16 211000e8e40923d62ce5d876540ef386e9bf3d3198644fcd5aa5e3e82066424f 2013502249fd648ff99a93f5c1084e6b241d048348cacf5c07ca96323798e31d 3785d7050ec2fab2bdd440022329e791610e489f9a295b71e241730578e270bb b58f968cded07722e0ba9b26a0be633d011797d1a98328b938aeda8a43815466 81b119e54b8a5c218ea177dcbe332564e8f58c22c08965e6a697595b708dd9f2 569f521c520a7bf7c26733a9cafa9f68da5c35b0c66f737e632a1e8789e68dcf 20f86cb06e37a7a714ac5dfec96d1b59babed0d5635e700fe647de211074cb96 7878ef11b7770e557684f57ca6bf19292f8249ecf579320baf1cb189643f0b9e 80b6f868d06af612f5b7607126bce9df1dd538b90bd227074a493bd0f5a3f67e 33cd0f98b0d746b94675d42147c90b1bcafe16bf4234388af6cb8cf5672e372e 96bfdaa51f13ab4b987db32c5f6c2091c930e2141eddea4e71060d78da517726 f04903094be80d8fadbc7adfa9dfa821aec0e7d7c412c43752b1b18fc02b2f59 1e1a145654e2c3a77663dd13c2e83bf42b183438281cc5ee4f9496120dea8405 50c1b066abade654288d38684a982d38a346295559d77e14eb87ab9c951b7300 69 21807f71e356534536607e1552c248c5a2059837c4ef95f0a68b93e98896d8042c4021f37da1bd996516b681d2e318c812d535e44e0bcf03a3182e5f3f9b130d0e3563b5e520c269ba709823784272b111a0aecdd7fe8c716a94acca97f41e0e8fc81ee5ca1e91977ddbbf4c74e1ba5a5f4fa6e88c7eb572633ae3b626c19c09a55f2a44da0fedff7dc0d24c27bffaeb448e90079e22d9827526a05e6d119d086ca98786b81bab9a975ecb97186697af5d18261bd74334e6c153512a3c9fbc07eb9463ea338eef67fda11fcdcf32693066e3b40dac22b12da3bd874aea89ee0676090323d26a5dbfde226b4c7958d9055e8151bae38d0cc23f443d169f49bd0ab79ccff97b47b3660b41db58351cec4bb1bd93589d1520788344bcd0a0c81f0f270679613d96a024e27530e45e96456833fdcfbc4045c2a93f8c43798f82e000ccb6f485d88d592765ed40c5775a9f6c8e59636d2181ab5bd07657c856bb9a05fa70638081a029acd938887391d4140cf35218382de51b58dfd6549244ead60a33382bcf3d1a98d7d644216b59005880927f4f4b592199ed9365b0feec5cff0fc3b6e40232ba8c6aff262f04bf4d4668dd28ff8356bb8615fae335445cdc4e08fa93846c258f76146d969c380a591c88dfabf9e201969a94f250e4035c66db046a373ead5283dfd066616f1295961a55d24af8b7a75384ce05ee3c67103da309ec4c2a1c47d5f92aa278b1ee3bc55ec919e394b69687e756299aeae435c20b0869c137c6c933f2bd088d5180e580f497e25ede00d2c4785efe9c6a5eada7900087147e6b6cce9666513bf97536a2ef3014a63b5a18ecaab4f7f28d98f56185023d2fee7d6178df4df31ee0ef47542967d890eb7e10820c072a10a685bdc39b08a6ce5b8f37811c08ef1bd01a1be1fcae84b6e249431fac0e323b0c51fe95c90fa8715857d597305120950eddcacbebd54a2bb7f0287dade22a9be012eb84f70dfc1a0b459b65d6c14ae09ed0e7b1ab4466f5a7623ba746335357ff157d670f0e4bce93bbecfbabc33366568fc36693b46840df702626579e62e9516f0e4b9e0202ab6b9d8a6f2823b05023fac29ace4b15b5ed8b8c6bcac06da835c106aeed0c1638e5b29b5572b72acc82611047287fc2b3a59093cfd318331e6504950437094dbeb8961078eab9c99d8fa5bbe5bfbffdd8527c621daa6e7dd5a1a0442e240cfcd5b5868bfa2da07b12bca77492675dc42cfcb566b34a359091d12a1056b70a7d6e449a11ea39fa27d7d7a696411c95f10b6551896611b1d4c4869ca2ece20364266d3b028c0435a1f136ff012de7c1360d18111aa87e7f12aaa71f78d2d8010fc6b0c6e87def7a42d42dbed8834da0e67aa29d84b721396fae096582c822068ff9378df71cafa852910d5011168379fcafa138bf745931c08de73a89a81806c8b08f388321ec17a80a34c88fcb33d38760d46cd3facd1d8f22d511be64af04cbe1e759fe03dc712f0f2c43219cb0d18f318626b27774b9639b90e8dd9a2603b121222acfbfd61222a230256ec665ae0f9d83f85694ae5b48bf2d0c2f801d020865529a730c1385a82dcbb77e7fe21421f5dd0b15340bf446945f101b3cc50fc17e4620dcda2c8f46db467c3e3ebbacbdd9758a4f6f81d1343449e7535de20dcb984a9958e1ca69466298ad207e287fc363c19a60779bc9e6b1cdffec47dd0c8e4255bfd134463693bfdd636398d148923dc78a1d53248df34db30bd8524500f91f907930cbd92a38fffa0452d7bc42ad1a025aa8e8b6372617ae071f3e82044199f8d6909ef38d2e8b6cc6802dfd1ec0d06517d3cd0293c18281e2b81a0b0513f67bc2b37fa38522d657843b27a6cf762ad6ccdde704a1fcb18e18047d370a1900afa71baae8b8750a2f3088edab7602ac0bd09e2ab536f1468d8f82b57701ad263b58586f566f7fb002c90ae537386e7df7ad41d8398fb7ad0b7ce6b64b0d52832acad8c5e6f25599d4e90079c0f0be88548cd09ffb976b3c4e87c7d9ae0e84302423f8f346d048a3d877752331b70e841c7608f89c1172d507734655e606e1d14675476b15cf87290a5d1a0bc7e2376c7c594b727d1d0d156996bbcb280d69c3353a6eaa79815ebfd2c49c52df4bf46fc256005023845e4bba0750ca80070c32ce3d6549cb135f05bdaa3de1730cb3d1793a3907b5ce1bda53a317ed3c0b05f780848676d2df00a812b2065e02910bab2660ea9525aae9fbce20eff9a306e80a54b06297c79ee6e29d574d8d5d5a6a8e6e7f32a74e13bd007d9cf247f40a116a086c86fbe1e7bc2e0afd9c3e0bbc3ae7b04379ea77cc55c93c279903d10d3ff3ed6aaddbacc75779fa9146892a2d1ea3901f19fbe83c191e634e62e8660dab541971156a0d7e483471a40e5040a3b75f1ba6df46abfef2ff2ce075db8d09065452a13ac563ca9a828f945308a74efb39a0505090f01e02d396eb82af320b14eb31120fcab6efe256c00896cf4b84ef1b6ba19b5691430b6cede209d6a90c63f9810f550e346999233ad550f5f7fee6a55364b26e51fda1cc5804b41c0c0ea52909a62b7f00677cfb7cba9c18f14336c61436a458478b7ec4e06c41f828013f4f7559115967432a4878bce9f861c377d4243162c601177255b30ed8f2720915581c12ce5f7a3f63c4ae2eba416abd39523e85279ade2532a00437f6130c03e4ebf29b8aafcaeff4908aa9b587459670a70623b9d56ba6c1ffe800e17bc90cf5a926310d71f4cff9c117bf5361c6229f960b46245554437b31a8bd26de2802f47f15cdcadeeae51516fc0cbf7d8de2b3bbde11b45c3c7c5cb6a58cc564a70d377cb8c1dfe198a2f3bd9569c6a01ff94d71f3944a779cff6ef278dbe363800073cfbb463b0fdae43f7005111fd85183028ee8644250c9670be031939da7840d08e63e81378429db095cab27595802554754996bdd2fe580350800781d64b80c59b87a01fad1383a5467c719a1c0ac0c0217401b10153b476c3ac6a818e80c0228b215a75ef433a597ae3292dae49d532d1b6812ee9291f87f248ddf47e99a01dff46bc464540adbc59cf39fb12e4b2dba9c64e0cc207a78d50d676e37d9630a575d1c5a8bd171d7211f76f00121bffc602ec6cb132a431c1281ab99e9cca00f8b653d76ef6c38b81a2f3e8092d1de12e02ef2d866aaabfb59811921651df206daa281d1a854b77958b955c68fd4d536f3090a8dca403ece38154f812940d101cbb9e4d85b2d796e84c2124b4bf364b2eb88bd80daf50300e011ee05fb132808493ed12c5752d9834f3281da388bcc75ba5f9b8cfd709ceabf63d50e700d8107828c6f5304fb6fdbffcf874cf05dfe16a7b2a606d739abe3f991dc68a60fb7074f32a9cb2affa4c1dc92a95b19bb4868f3ae4bbf4a0629e3cc5a46dc259c0d00ae4fd36e28036f1e947391326644498e1ca4e7a8f043057d455e53dbf31d0f0c741407b5adb73509e838bb156f801809be25478a7c7433283bae5a5a3864dd0bd49a0a8e9d0cf8d2d0005d6354758e91a66f45ec95e74861195a156dd0b36f07b8076a14281fe3f5c42a18928bcaf61cd064b36465aea87b7aa0ec75ab38f10a4e6fbf370031a9428baa85d5553e3ac0a1b1e4f56ade90d7709cd4f12a4c9b05c02511ac95010885b8a6001ad6c46497ef1c1810d80839662242fbc65c2b700a82b2b06afdcd6c4d253f3a06850ff1c1c04a4879711d9cb7ac593f53c04f5303f03c625938d81cb575e74921534fc7023792bbaca28c85f456d6c85bfccdba08080c7787f2e66a5dd660f21356a281863eaa5f1424312739a65560895fc0bf057df124c8962b5ec2dde027f46c986ffc1a427dd428c73f2575a3dd8288cefc08a062a3c67240e41978f3ff93436f89777b58fc58805e60f683f9bd6faa864e0126ee980fd563b7c0442a0cae99d33565a28a7372469b371befca5466a3846000d87056007539b2f0d79b5f413a182af5794ac9b950f1981b36bb9ba50c3766049eaa8bd5aabb317c91f3e32d9b93dd84ce5184f474ac43f61592ee70e424f509f2b7bb892a1317ec666afe4b2eeec32cbb7a4044e944d718296e74c1426d940d7997512871325ed1fe29718800fcbbc009a5d93da5c910e60dfe1b2430eb0a04e558e4f769f93616b079c1b5efd25db61c5f71d44f0346db37380a7f7846c60daa9c1f594832822207caaf744cfd28c8bdad9189165cbc2a676ad5e509e6680029362665b7474cc37f2657dc2b7d8e495fc0e8c75158c44a365dd7deebe16b02e36a6dc11b4fbaf9e2d74497ab1978052e26360f8a07c3ddb70f7d73b11f0407fc1d285bb960a853f3d7db8fa5a9b2dd07a664fb62de2ef4c4a5ca9ad79f30046fecf542355b6dbcd761e55eb3ae9ec32b969f64eccfc5ba41c1415d0763b90703eb78735c7ff5372a65236e1043ff08e77170487acd3476b82e5f0f21dc1f040c8460813abf7ace96d536e4061b64d08208dd3855d3d77ff296a0193cfd010dfafc42748e8f44bb89c2c385ead56cb0a6500fbfa83aa2d33c184ab37f2ea307ee97f0ad6899e1080396c7f5244d85a972b7b5dd7b101a8c601ea71e9a56ec08efc784bd9e755bfc7081763b2bfc3241b2382cc2dcc746c1c2adf2e0cddc4e00748b299641fb1749e4444e1df71397a96219d4f371edf754ae1c3bb859130d031b73e213d4d533cdbc6dc0702476273ce93127c95e87f27303436d3ec424870d58ddb13cff028723a2b211d5ab87acb3031f0ea5a03b3ce14199283dc2fafb0a8b8944d0bb6a8ba068b6848bae939569b554099a8f69b8e8aed526a4227dd90f2526c3bf44a34b679a9eb033df96a0815bbbe911c1171b4777f6ff6bf516c707dd2c8e59bdf6110df9f8ba3349e23deb479199b5d9998e61b3e78a57032f6b0ce3750f544215a14a72b434246083314684946b2dad612fcf550ae0e7a60343028c3384754a692098c30d38ea2100a4319067f73349a8156b94dee3a8a6baa50c8a02a684cfc3882cff7548cb608fe0f7652e7c1502370656656552a0ca7ef708d3ede8f5e472606a2b92de0c19d40f5fa4bdd6e24cd15bb0319ee66b13c8560f8f915d065f3542d6fc7fa4b28aec3ee3a92113b75c7bbee2ce7b85caf9e6fb0cf78c919c2c372535678f426be28a1b6aa4c878589e985b62494f52a20be773058c5a4319b5c107d135151e0567345139a3592e071fb96a035d8364416f8353085a4da7d68093a2a6adee33f8951f8697095ea093b5a28d6a2a6a1c09115c920ed3b40825ab8755e2b1a3bf2a57662aa3dc78404731ea989d385b674ac4ae8f0cf3ef4b51b8c82cb3bfec29c12934f0922e45b5ae2641788c82f9378abdd21506f5196b1f1c4e15f36c8268c2865681f266e98a616b69a19882d816dc5ece43027afb68d919b9514e18ed47fa5f9906686ac07b6e15642d7acb17f1ceb03bb309d70be29ffc8f714169e214148b628e014e9ba5a840ebcd48cb45361332f57109f69a1ee2cbd31ae2b1dab0a86269758f5ee68828b9a506539e83157be2d078001a88662286d5ddb163f5fcff2a305132f1c643d644bf829fe5fb2d1d87fe5b0df774f29b7ddfbacbfa290d638eec8e4087ab74f0d0acfc9ca6e19cee44ca9703c39867bde6b7ac6a654acd8f799e81f551b7df6353f844708ad15026a330410202cde7fb5eaafd7141150e89af6e23c31cddf13f430cb7f5e174d121cd94c10a2bcca9f833521405ec35f9bca031710c4405542d936a4588295ad30536aa8608c63f55adb8bf1b5478bf3c8990d9e367786925a2ade306ff8a4f44310bcc7501594cd0dfe9bcee0d004127f13c125ac774fdff5ac9d975e1b773a236dd02fc0ebe3e81b7c3fa5e104dedb761831388b49a08559700eb53be744cfee4ae2fca056e78c14495b1a1be73b257bfcafb3fcef8ec6410bcce57457355ba470b23f30add59d49217b65f06b77dd23bb70a3bf4cace891a1c8ab20dce3eb5889c56db0ad46120222ce9721cd1dda0e95474aba3e4ee98c5c0a9be3337cb812e4dc86b07912b27a53c3e04191fa516cd32f3d48f64d61e316dc4d751d4e042c07f48ce0de119e10d7ee53a6468d03ee9acaf0bd36219c1c83f693090c039cc53befce1065901717de9718811ba5ca6ebfa807ac0936a5d552670fdb141c12f19911bcf0afd846f5e476747374a516af468711dcd39fbfa923f061489352b4763fc554702e68876caa56b45de0b68c2fd801ae0571c602adbe322db947b2438bb484aec0df622adf8b4719045973ee18cb5215f2c4fd9d2d95a8c83844cbbcd07689d670406712d01f2d14ce08ab83389ac5dc8798277622fcc698bf97a36e13cd4cb950649193b7ed63a830159a33585539f1d22d1516659fa3891f67c281deaaa888f08583bab5e9d2f4eca6cc65b1eae7e7935f6e4e85f709207d4cac1d5394964930e37874c30b18591a9b420c5e0b460e50f8cf27e929800b8006f2be503763b8b092825230a2e884d2d9a26c140605fb7953096f59968af356b661acb65cae151071996777d3160cf40204cf833cb24aba69d73127cd5a6f53e101b293c3f07df0127c4d55cccd4d1749cc5c9612e128b8e108a0c34011687ec96b5e2cde281e60d776aa652d7f08e73b93512eeb14e14a60a93995fb4518592f1eb197a879dfd02afb1de3926e2c3141742236ad35c5e920d4549a2a8d6a970b1e1ae00db9bcf04f5e0f08df98c8f7432b531492c926590ac6136fd56306f8efe7043614ebd17044deb383d2e219fbee78f9cdf63825ae6f757561654ecadd5b8e28bc4e7dc3f0589b26005d4b159a6a5d8d27caf9a5507eb7aa3f2374c0e2656a3d1c088341106d1c38c1217e72b155babd4a67d301b3a109cf932ea59fdc27811d7c0c4762a0956eb4940c88d113ccc65681de6c854102d77a82c6075f53b7966d5421c424b0a8a85f1e5fc72a59338b59cddb3daa816bf6445f0ef0c02ce87c273d2e5de770751328d4b5869440e0fb58292223886ae2de3dcd35a0350eca38ec536df24330d83204fb31746c6283c8c61efbfa7d99e4cb17c27f3c9572014384610236dbc0b90ebb3c4c6be726dec00443f7e7424abb4edda50598e658b52235eeeed9293022ff1b37eb77c41161ad4554270d6ec2d11c25dbd4b36d6d943429fc4e4b529020081bc873782c804fb7a89f55199245ee0b55d839f0ab9866a9bdecb9b416902b081e532196f8597b497d578828e3f579a97962d7ebe328da5b636628bfc5c0be8dc2d0db4e88cccc1e0cef2c384e43c835f58cf2219dd3de0870129082847090804eca45c7059e07b9e4febd2c77c3bf02f5126e8921ec357a36d5fd46ec30e245865416799024c61adba5501d1e6a1810e27ab3baf2013da31949566b397035ddb58031ffdb585eea0fb301b0b67d739bc80c86ad4ec3c809bd49dec27100381761103e82118cb185f2c07a2a64f9676de0e0534205fd79b0c9a968ad9af08ecc95a4d782cbf56b15b7213082c25fba5a16f3999574dfff4cc9703792d6a00f061b05e8a8efc4aa087e6dddb15128df91f92355c4c335300ac3501fbe18b03dab3016868bb2e68f0c0d2aee37174665d2783a95f94cafcfb448d095c89d9098ec327a99e893a1ddd6b52e7861ac018fe68a2dbb8e7e1a03fea94af644e1201843f3476fd359442764609d39add779589f3a2c8da5e848d860f67e4555d300c601450698ed41e83ee306bfbe5859c66d028082c4554eb6b4394c5fb9ee06102a8f4e4497abb3af7b99264021ca8d2de203b37464284f672299b3442c7a2ad030c9e8f9068544fee2c1de301bca8fdb4e7a996ad3958c2335078618d8ffa470ba59652c9cdadcb367d706b0010e0a38d88d1ba52ba4ebc93322c2a8c54bb010f2d2499194362cf761d0d594464aa63d8d3590b5c4c3f8a0228ec02e9a0012f01b7233fa6f1bc29dd211463fd4224ebe57dc2c557e7b3fc50a8ea38721513f90906485d1ab6cdcfabdd4d599016aa9a0fa8eec1bcf49943292320ca7bc4a1eb0b34f786e8a4dc7b81ca378ccd121c09f2dbba0ec20f7145aefd4868211a3c52040649c49d07147be02435df395ed18daae787448d07671bcfc8697e624b12f1023c5710b65b7de9f62c9e253427b4819e17dee5d7aa2a509854a7d399ef1d5a0d35c55358fee19b9cd6836b3b5f1a456decac07abff363831ce3148f406533e0a08e01fc2fd942804c21a5ed4271d98ce3686ff2f7c1278dc15cbdccc80168b052ed1a0cba1e14c8f7ba7b065953d6ba340cd56dd8d886a9f8b1e1ef2ac3a3d0bd5009b9e2bb20feab1e0440c989a2e94829ea0f01ffc5dae5b45b2eaa25bc5054467aeaccf6170db78aefafab5aebe9bb3cb240141365a933c2194c40ef38c0eb6707ca694719713d74468eb24887851174a44a53cb8a7a4bfd61e8e051daa0ddeb397c87d204400958cf72841ddfc9cd71268ad962a8b815f030d057348b40d14a599aabd3dcd85755f4fa178b80a97d3275c1cdae97d26f45d57810e75f70cc0ac9ee4eefbd522f56890b3332216c642a7eac26345d1c4572d3146b2b4f50aff57f73e97d7ccfa54e3c276affb495ff39244d1eea42efacd11c3970ba90508281c67476d48e73048674f6fbda6c80c208df1d01c460ed8728947260dc7a80e8bb0ab568e1096c736f9f3740f69ab311c0f2d8f7054f6bc3b6883e83e921c0125e9fd2c2c8155f0fa5dab86d9c2b91c417f18e8e24845646ba5fbc96293d4016dcaf2621d40fd3c3d38e5cbee5ef24ad15c26175152031622b4a7f81bd015081f425e3716c2f940f7d1b73490274fc656243526180f8be43be13c2579caad0c98472be6173becc70707554feb37925c96ff08b9b97de1ee95eb976f915ccb0bcc0ae009b6e8d0f116394e7af83a1ed6070eccda664f038cb4f6779dc20a690ff9296e1f0694ea293627acba003dc4dd884d2f4189997be8ba66988e51057b07b6c9471cf71d25ef0d756f3dc76c4baf1c9de13958174af259d92c47e186cc07e18ade94d882809dd2c577a14f043fffe5fcd7b6944e56cbc675b88f1c77420c92c625567a08ede95f185735389f4ddfc9853e7543bdc483d878eec058860d0c537d8134777b23cec6bb11b7cccdc9092d446cca977dba95c830a4c484a70f0ee3917f751b966cbb6ff9edab3467f7900417512fc574331ae790196ea8d1ff0398e6ea670901800b150877188330eba41c382b14c62b9ca58a65a8a11486d5009d2a01a67f54328c3b009a156b44f27f7ffed30e0b04d38cfbcf091b91e0770e53201f3d5e841bc8677146e61ce3bc647a0acba5d3a940568e0c0e74027a800a1b83c25be06ba0269d9d52cfc16e511303071116c4841965c0de3105e5405e09e7b2dac14b9fdf7fbba45eb948b280d73c83bb8d4266062f4951034e1bd14301e510fb1c7db15e79dc064757d4f4d2a4747f8bbd5fc9d60404bab83c7b34420c5909c6463632342ed87cb3b00d0f18707e37bf1139d1c6e854089e09421522036ddf534c2e5080a60883538b31aef144660e92daafa3daad44503ae5d231a003b56e45558694782cea04c8949dea9ae1866514a84efdf850b1500926feca2e08d4a7c7a7aa1c3a354ad7ac1926087185c19d41df381836df5b743e646c24190f7fa5e3a1c404d8f27655c65a989f97764cb61ffcd0624387af29ed0374b6df064de9a91efda7e504435d7ed70cc65a47b3c5c2e5532453211846d7e607844c0f42ed7301493ddaaf2cae5ed67b5dd1e05bdaf6af277a4001641c19787d6e780d4742ce77456da93ffefbf7feec6a2102063797715ad167cf0d9e5e90f5d4f503ccaa7bb17bd10015444b0a31395fc23d5f62028240593fe2ab9c0c3492dc4f087dc0f63e66a6abd11070ba69045ef80a1f2c4f2cd44e8662330f56ef5801090310adf3daa186a524ae82e468ff8765d882e865de8c28d229f7f96adbd2abae0d8179923c4137a24202c0b4ff9e682a5aa158e8172d1efdc523d021ec9f37ef02f16c5dd35ed1b7484920b33ae4ff69655b89f6f30eb1f6864090736761fe4e0aaf8d5cc7dfcac9802da71934f8a9b50520badf21e07dc4571dde08cdfad57a0c9d9dfc0e74ef75b7333fa7349a07485ddd0580ac240e3ac2c78a86d47377700ed83b138fbb77ccbbc17ceba465280bf9f3836f91f8c7eebed4d419a6a2fca40282da21586766aca928221900268c2a6436a1c0d64e18fef0024688402d86ab0abca985171f1a977f8270cc60b8dd5ce454a3b3b3e9231aac844a984a4f151e01d601870e91a3faa786aad7a028f5b3e5e12409838e2b2168e46e17e57f583603404e9327ae88b00b94b914dfec1e54088af2df9d8596a814ba44a0875aebba0217c3c44498321f07149801983b87cf162ab702a3e36d9484a908b477d3a74e0390602e11a52f55913c0d20926c23715b97eb54acfdb4032e3b36531567d3cd0d8b18909ddd7000586c003b4ea027095d12d3bd9abb19561f1295bce26cd2e609c2c36d424ab894da43c47cc2deb4f76dc570d67dae88ad482d8d0f93b31a1d0efb1b94e0700d54deb6c840d2db4de8ae397195e29d819a5d4ececd13a5f23f0a5bfb181ffa6fcee5d86333abfd95b691d0d951f8bbe36297ec8607eb00e6110153cd7ad846c15003e8c9e2171a979b956196c175dbee74fec593f106a0219302786f7fd0b4d9e5a9dfd944a1513899a0414ee184ef86af17c5ef7b5dac02ec0d820906640c1acd6464103255ec96f7a76dc90f1bfa65dbcdb5893b979444e609ccbb165a73dc70f88abffae3e1a3786e9cc75840d5243afa0de2d835683ada048524bceef407639edbab890c6fd2695bb8faf801e960c21bb1b5e23aa2e94f05e915c1d88f296a34c4fb88eaa3fee002b9247234a65d2b3d485ef784d7bce806a7db21f14adbf56dcc3615d95a0671ce0e973754924c5ac891e4029523e9e10693321f0ba9f668bfe1ad83bd2ce768001dd002a46ac589937ee805bc8b75220f7183a9dc17e6bee01da9e400db37139d5d5f2bfab567f56c5971f5a45e300304f00f39ad39afcf02ecc843d9e93c229249ad547f1f6680e2ca27581e9d621a0df918ae6366e80b367edf6e97cc59773d7bb9e3691e167f3bc34cd95b1a8e7d0d800012c97cd4e67942d5cf249ae36192acb01d8b48d04e0cf4475636c7f8dd0dfb09890bb32b263c0cb964d703edd21b4e3a509697d7d2589074b930f4c5de00a9a347be56e0f4c4f59d62e091ba7057ac9520437bcb0b26aa9c66c668273c0ac75cdd7f3fe72d8d0792a87077a018744c02292c603a6475201886ce83d8a300e89304190ead434e518057f5d8dcbdb277c65d247b2d2c668bdaff700e783703ed92ba2a2e276265a5c58e70fe1b418c1219d2411a15c79d936263e26164520df441948bef0404d3d1c8797c84811978021d296b6926d648ca8b28463a840801e7888dfc2c5c21a6cf6e28aa0a74101703469b0af8086caaa0f3bddff0581d03acedba8da5e59f57dd354d413cf35f65479031aa5bd508f845de206d7d20c209755c12bf15a924e6a3a40f4f7846c5d02750bc219a5ae2130b8385ea8449850b3055ee15e2f3d62eb4c98d180b0a7aeefdf485c06623c17768007620bdc69804331a91cc1757b48410556b2e604803c6c67d35ce3a560602311b828c9757b90b4508f5c444bff75e8e4ac71094f0d380b616b74ff5e813b033cc18c7f2d91006c64b36366f0ff561df09cdf0713d8792c98f6dd17395ac14447381c5b90c4b04d555124609db930c7dfe231909a766ab3ecd0eb32c4d4cfc073a60ba5ab07c0eb52c10613d3ac397920830c13e7e8f6d09293ec1f86b5ed1772115352ac98d0896a47a17992c10ce9ef5e496e3003733353feec37de5105539242e4efb4519021ddb78396bbeec0eac62ec54cddc4865a468c1ad02c07f5d0c23c1b9a85a5200efb9c51d6e3ea194c5c8bf9f4c8d770b92035582679893cff995898821129b02b9630e7419b4166d3969654d8fbdba6f0572849d30d8bedcef91e0b472b21c0d4a896f9e0a080434df790f9284411d11ed1ff5da21552a6f979dd7d738d9cc0865fb58d781eef2cba1b0fba6cbc06932de1b19d1d0e4904118453dc746c1d1079828f0d4ffb5ff1e1c6c3449c230777dd0cc5519dc702ec8720852c63280380871985c651fafef2a7ba123b4a864affd6cce48fc2810e667d30722ff9f19b10115151b154f7c7983deee1e9666976308ff77ed799a9acb7588c2cce94db8b50c61f5a839202c67803ec7370dfc5f3d0619d578fa9aaefe2c6ce58a3eb1729a0eee287dc746f0db3f6131f5aa42a98df719604721ae0ef13a0f0c20775c53d00540d049c1e70e203b672331e66c1e25ec54cf53f46e324671af11422ac599820a72c1fbb59c53fb7b578bb0b1f504c03f7f22bbf83f5b7ff045c32e84ef67ce0c9f858b8fcb4e4644cfff99a776ce4a06938a12d1fd04d76bcad793335509080e34534fcc2839197ddf8512b1bc7c14aa01e137fbf0db058762250271e7bed10d183517e6972bab43084f640d0895d03d06675997063cc373a0e055508cafbe0661ff6fc761ddfb10063b2c61b37ff53de1e8cd04f6ac8b4fac4f70a5e9f3fe0da33d92fb6c714e3ca71bf5244f8ff0ee9d51256a0656e1869f28e9f36cf2b700cbd0a10cc20f92baddaca5689dd096fa4168e0b59945cdd4219a5854dc2fde009fc31a0a742be28dcdb8de52ef5cc20e6026bbbc01a5f742f251f189ad853806aad1adb69c2cbd12cc0328d76c696e88eac8e252e873f738b5031efd1002d40817e9282d946dfcebf2af541d66e7ce645cfdc8b693b4653206ca88f2bfb81e0a74a916617923709274e8e787707f81295603f196d1a1c11ae23d10ddec60920f1c86607fb381d156f909d9aa607a6589fb4f6d810e51694f826e56ddbc49280228090e40fb7e170fec7092ccb40c23267ffd1045b21b58aed25734b6a62d47057941a0aadd627c4507bbe86b1eab8f4787bfa62a2dc480a6dc255b94094c210a26b69430aacbd7d4ca446aa2dc8320719fd765c8a616dcff7456c925c7cea70cdf6eff6cc7480f53ac9a183d7653be427ea4addde8cea992f41a90f01d1ae209f3116b823a70cf5d25967c7dad95c637c1b81fbc0ae77ab23684af73fd70740e7b92490dd2d02e5ecc03f0d2c31a3514795668ff3cb4c785665f063ac8e3f506a6a1de911d46e32fc713c5fca9782c9041b4a771dfa4a7e000fc4ee27e89e10b3fe9ec821cd7631f108a1e7a1980ba068b73810829c5768de2441131567ff70b4b5e6138a341055bba7fb16599f8704024761931c694f6974ffce9af0f8f5a061aaa06ffa631b457f3d67b17d946195ed83019826b435cf65aaaf4cf3164e405fe9cc4b25ae704d682823f77b9a6a3e242e26341c51bff6db15ad61945a085030a9a482c41e312a5077a80da1e9b1a760644b3d5afb303a9072bde673c73a60b0b93baf4397ef103e48e97001b3fbbab794154ff7151f369c24b7d737241ef0d3f88797a365a787a916151cd9392e76f06c0fdd2da2e85ff8e3013168db9e20cace5780c92a6e85bbba2655af55341010fd0f43b76c513b630257a1eb2c20c09bc1cd68c5015a5f73b1254bad08bc84c7100350c7cafd43b4e10c402835fea0f6edef05971f166899baf1ebfb4e0096f3db17dd8f9387e45ed58ed642b3b5007aae7a88dc8491a07f33505c6c850662b292a3f2c3a65c1c9ecfb8cbcaa8be100db4ba3c506df12a9ac8c19595b0c86add4c3fbac7d45df6e91a2046bfc5fd20a52ab6073f99023c58c25bb97aaa51f21b8b94012893b3038b25c93d050c135036d57cd81557832dbd01d2b230b220d0dc7a84e682edca4a48690a017b3fcc504a41c40fd023e565aa6d15e99d504232a1c0f349555b8370d3421acea2e66af0eb82d84175c4f98da1fcb80fb7f56b4f32836275bf0bf790f1c4ad3b17423d704a5062fc8c654285e6917bfc9bffd80e68e665dbd74b5727e1fce02c6cf060e06dd479fed34c5a6e6c085729ba45a34d09c94ac9d7bb648a6b42d679134e8bc08f32ffff623557c9b53960cfcf2cd78fb00f0b4e9262814b24799d9a44b56b002834c91db8d6fc45e36df359360d452aafaff806d588223bcbe0fcddfca51790beb54068dd340bb4dab0f345e3d2ea30c20151d37966e546c23f6bf8d73738a09dc9c1d8f43c6831d30349221018ebb901b44bff7b43d2a174c4adc08d28e9b0240aecc1374723aae047fe34fb5763cf0aad158b50b6525a27d18f6c06d6d7804e2e9d39f7aab825de64278ec9ff183f08b7d20333944401c6b5b7a6ca2a5fd0e862a0cefa42047a48203d4ae4e427e9a6ce04c23c5dfe00fac0fce5eddcf76024f610cad126018f8bc319bb82459216a36928d964d6916170191ca9a8323dd0b141a872419db99500dc6cff91528d582ceddcc245290868a69b7f6d9d812a509a28323917bf9e0786dd77dc22af6bcffa6d6fa908dfb9789eceb96426739aa04e1971c48c9ce5418df5a0fdeb2ae84da5d5d053ce5eec9324ec49930892637064b60a44e917c25a61958de20d994d123bb71c9729d48a8704e1ed199d842500fc8325252a751c9b426433fedf63344ad62e7517e567a9edca5ba06ac1c3e4005c28a08894f1103ecebac79a7308d69023a995c2ff73bf9648235b78e22b2c1064def363aad8282e74f15cbec4c82e705d64bad9188b0dad43e3b4a7a495b1c09c612d228793d7a4ce11cb08a320163dc7d177ddc52fc7750b9ead57371b23c01a1201eaa5c3aa0e23628051a8b2cd91f170c69a56a4c12862d60351018462f0abcb0513fbdde9caeca851a6973f96e7aaa31f86efe9ff92d6cff98b1bfe2800308fe607f53430000f104c31035f9dd9f21a6baeb63dae98fa0d957accec70102313f304bc6cd2248b7f7dd9b497b8741c0fd746fb10811781591082fad2b570ac869ef721bc8909a06a9d08a665579164c374cb839594c0c1a0fcc4697be6f0e038a34aa3ce5356f99c75403de0f4d18e5524c406aef8b6ca85aad16a6ecf30f9e74b05d12286f3b7000877eba9e9987dc920d7588bdd0fe9cacee871bc3290979947d782eb432f4fbf2d6b0bdd2184ef72a04cd2316ec824e6500d788ba7e079e5cc224a7201e4083cc231b00fb6549627cab00b6d493ebba1cce0d463c99042792d05bbac96cdbb416604809e1fe1b27f7998685d5e49c27d04f04efac3b06442e57d01632c869c5a48d16e09ccfd1df6c306e63376c90762d35a7f244cf09061697e839712f098f9596d0cf32f6bd1ff76b7659d1b627fe7b3209078ffd0bf1aa7c7d48bcf4a0f98409e8b01dbf329855783838b78906feacc8ec6d0758091aaa46d79f13766e433b0dfbe42ca183797aabc3c738353cb9181a09cadd2805d9c46e32de01c6725b550e3cad4ab4d5e047716cdab08cae95b953ebf3da130b06cecadc0db4601a60b44f9847379a5d961926095e6c16f9789eae958ece700cf6acae2143dce6ce2ded99e546386630ed94ce2879cbf2082dee3a949d38040f0902a37ffd8627cc9445d11b05e473b11921f958a83f2694d85f6aa6d5bbc60811c26449569217e887bedb2c21672b5bffe6e2bd4842a4787f96dfcccd06f3007aa8ffebbdbd67c54aa5394ef8a3b05c37c0acb19542c87bed99be92a7845004dca73a6be6de8f03d86f78f887610a45c22b375be60c89e3bce66025a7070d00cef35cc273425624bf81552d0c8cdebf21853e1f27d7ec30ce58f0baa63372098fee116ae8e719b0fe6ec51fbf254ce382e47cfbc768d12c6d9bca35809e8308e46579c1fb9b931f1326cf8cf58bc4ccb06ac4ca00727aaea1ec496b6a94ff0caf2a051331bf31151433ea38536c9a64b84e856efa7651ded40817e78bbb6009660cc8e1ccc97167702577ce3c3efbcaf5fff94745cf737379b42e9fc2ddd504080c3abf9c6ec85867adcf51656531833343d33f341d2eb725e3c5e80a3d6b00b90c38c93951fa8d635e24b0b0a48b2777e13e28e58ceba05c7f9bc8f3322305b530f6a457319701669c0b135583c05a2c44c029988bfbd50827e57dd002130517f4b8c06b1b64d89f44708f9f9be94d0e74905a8103dfd8f82f5dcaa168d40a7da2839690c9beda2cc82f8b81467f92c3c478aac981ceb429b3a6a3e5d842016361fcef9522e3fb1f8511a9ce446a37ad86972948629d783ec7df5767abd20625fcaa37690501207d502eb1729f30394f4e1688e7e7e266b85c317ad2ff340ca56d7a23ee1df0c73fb27bb22965c03c5cf5cb04cdce8fd3b1e7e5e05578410a9579eb3ec10a76b94236f2920efdcc72d3af010f6fa23612808f660bf2ce7c0a87af607bea164a8765703af3d16f4df4d20e69f987efc9edb5a555124db54a096572308654574d0612f64676c3b223c8e0eb2007363d134609c8d6a742968d0522b2252f8cc03a25aab5774a64c5df7a7f6711546092487af84c4607483a2d0b5bde96f702df14927b9907ccf6eba88977ec7b24834253dfb6657c351321660848d68c7fd33113431c12089375bd914a449bc95526aa4ae83e3d165217dc020cd6e3f60c57897ba86e29c056396c0d27b491c6aba1fcbc2f402a0f2a1033a8052781d57ffb025d3648e7ea938514e5ed5f8cda1a0eeaef177772353aeb6eed0e7867ecf746a1157ea58891e314ef9a4652d59e415ad82b9b887c19d138f9650753286821bbf782f934a0064178ffe6bda710f4031456b48cecfe4aca1bc42602e918aa46e512d94adf4f218b68f955c158794a54a13e69375c0e48d0c43b710ac30118ee440d7a501e724c79bc9d8f08dfaafa4b658aa47cb624990fcf6dbe0e26611ff81f1804487257c6094b228c555d5330f9eb617294b9614574e2df9c081905a187b7333629bf71f1bc89aee43494b1b44746716d301f1b0fb4bc9d15025b2686b4a6fc04d026196a8ed9f4444864fe4d0aea2a8b38a3d2dc39019c060f27cd572411b5979e3b4519aea6540c21ba95d55e11b9b3ceb26d522458bc7e022d6b0359b12ec355dea821c82ddf888473554083d0f36dbba8e9cca999effa0c8b0f4e13e9559ee50fe42d7bd071ca97f3455a348985c25872593b0071e196059bb82db11518a38ff6b91fbb263f46728ddda2f357c992b2d803d5faecbc0e0891ef4f92ab23d624dfd30a9efa25fd88456c4e84abf1bb8c11f268b5e0ba4209844e72fc68a6c3a26564d0fbbfc88431da1d765c292a81391ec5daba34d31507ceb8fb3044d9d1d1f99b4941a126e0580a255fc98fa2bb7ab58259d23d0a5d0a0df0bea82ecaab07f30d97d769d5b40c7605b11735ec813875e61a551a192304f8fb030c02c6f182bdda1d6ae85b8271260bedc2cedc301947bcd8a19470eb02969cb572cb4290989b0a9cc0938bd92218c65bdbdf97101cf7da93be851d3d03debda376dfddbb869e2f0c669c287e71de604a4682b30931f8178c70b8ecb8032401da4c9474ee1693df584e3bc074c6b3284b0962871a784d94012f0ca2890923f0f4be09049085029f38dfc47bfb9f289202b5d00a3d19de77ab0d07d88f0ff220751f75ab02690e02afc8bc3472c502ebabc9a09a20e636ac86ffc96529013c74764728f136711f653a8c9c3c2b08e6c9822691973a41a7bce053b5002a0a84e5e6a91e15ac847330943855a0aaf01b9a20e4da42cda63404e9d1a15b1005daee460c950776a19cdc223b1fc6fba907a787790f598ca5d0034c8bdd9c5a0e +generate_ring_signature 4b99442b09d30a94e40a661547b075704ff7076e432907efa34f28fbf8f98c3a 8c817e1ac218112b22b9f47fd1d7de82f36e48900926febdb32adcfc4e4503e1 2 3596cb2eedbe31599dd34e5577a038571b60800da674802f977182ddebf3cefb b290773d661bfaa5cc2d1db49b3633963e714e9c1f3cc8d843fb7f9e57e199a8 1452ad276c1cff34d8ed225e848e5f61080b131f2500d6bd06b7e85f85d08106 1 ed4b0070fbf1e81b572499d71fa5595907ca348b232a56b19e5f14031f08ce0707dab2b3e7c7056ccd9b259f4401c6533a4a18b9f6bedbdac04445db0e4b4a09577f217d98681e6fcc7e24c3f85aef8b224ed3ab2531e3aec9f74d8c5bff940b6e372fe9bdc18dc0c8f088886ee30a37f59e8d2b4033055f810b060ab09e1a04 +generate_ring_signature 308bdb322aa460f8c564d2134e23833f4e567150599cc3cb027083c5172a9ea4 6025c2ce5f391b371ceab7d0e0a0718eca1a660603056c887e0117f0c838737e 72 2c78fa1ff09188a0c4c26871aa125e9078025ac1e49d77593ca0dfa1989421a6 4dfd23fb69675f8836f93cda1dca1f2c55b68a16435d8ed3902c45709f11ce2f 9562994d83657b04fc8a26ec25ec00dd6c2650781d81e866a1e4ebf78e0ee80c f5d9c7e8ad13a830598f6f587c48910b3cd4b8d338c90a3194f3cc6b8c0a1a89 515aef296c04dc838810cd2dcfbf8f4f42ebfe4942679929cd3247f333a7734b 0f0f309eacd4b8ac55489aafba5cacd746a1643399130d56ddfc77e826feebaa 464b240b5fa690508441a556a6e7043451d269ba3fd38658a46dcd78d449b781 662cf10b9f6900077f61ef37bf3b21a4b42e0d65ecabfcd5c593ec23da6ef016 48095d922127ad39050e52130b408147dced83e557b81f237e67864e68e2103f 55916c66cb8086f42769ccf831cd4356d31eb6c3411db78d8b8a8e5d7e26849c b72e843b6a252e3d6cb4f98bccc29bc494779c107d385cb52fc9d90fdbbdd43d a9264eb06d803e5d7c9f5054678d649d87dbfc7a2c826a2346e6ac929b55fc48 df7e5cb855d1dd60f6efd88d00d9439c92fde3b71567cb46aef17c76fe53e51b 5c3b976629247f7f10f4c6d6c52846b9b4de1d7a258d84ee0811d0a417939e98 55a366725e671d0136fd0b711fdce650b77559adf56753f07c4130f18a36f024 c205931d4633f04b17c931fb00f2b47b8b7fe2c9de6def25496ebe5f61640bca c3baf879f8a858219e26c7983a5cc8618af19acaf1871fdecdaba355d6ed48d3 5fb47938e220e2eca446595c8396c75c28d0e180c2b3b6c3b4d9c9c1593d3ee5 ba26eded826bab30fba456ea53f5c34c97a1b5a985dccfaf6718d1231db6bf50 d5c01cedcbd2bdde6d37011766d8a864ca0f53943501cc0ab3933a86ac0a530f 434dd5d7e49617695b786c2ed3e851b1b76a5140369e2a390168d21cb99864e2 f87c29400d724f5c0c8e4e48049538035a4eec610a74d2730ed76176efbd84b1 ff55b2cd5d13d4d8da692ca9a0d1af104e1df3414e4b0b1821a56ccf676d3562 0cd6a4a67367d58f553ed01253582a17d8c85d8434b790fe0d79157e8b7a3fd4 27f75211c127aa1f68a3d22dcd83200fc3916cd1c7951e538600a31d19202599 3a23e03dc653bdc805cad9a038cdec71a0bb2bcb6de17e2db4467df3639ca7ee a1b76ca66834623b4a2445c7f369cedb1b2c0e884f938598bdadd245822ae52d 57e14ac6cf4de2ba8f4a4fa39131a40ff02398b2a2217a0747b4645c5eeb5703 0bd34d6cc73e4824f3b96cdc68b939fcbb3f3d64f85db0e2f404189d39e1e3e1 abc73c606cffe36f175a81afd69e3bdd7a6d534aa8da4541aebae0c5d78ff2a6 23eec063528ed18f12792077cda4ddcd7623b8ad7a8d9f194c0b25495d6d774f de751896b52bc0803437ec63efe0f68e6c25b17211373a6c083e9233cecd4c2f 88be6f30459ff0a926a4d7f57b9a71b55250e9387208b19b235f4e69511ce887 d64854d2a0eedab738b9155b41b30d425512d2165fdf68b60963e6586f0a75f8 29a1235869a8d737511ef31916570692eaadc9fc5a836419d91157959bc7408d 0cfe56ba540437788376011d248c5b0addffd76c8f183e1798a81a05995eca77 1d14bbdc17e017020d8c6696a1780ed8a92c1d25192da8ca83a0835ae6ceeecc 603114e5f9220594cd088b81a0cfe4b54fd73e0ef821e478fa1b3ce7dceb32ac a0fc34ac08c99f31ed89e5e310ff55208bc3fa2b53a750383992d460c2a12dde 83ed317fa3bcfcd2d5119593bd6e24db04b15669af5384d08d1bf32ca220a60a edb90d89c6618a866613546e755fb50ae469b95fc7148baddae72f531090e1c9 d29ec006b1a758c0cc9c6d3d9f2493295c574f90d6c6ee53840ad28c629fba2a 1e8eaaca2c4a8ff4f4dc13490237a0a97f061741c0b06dc5ce2a6acff153bf94 68df50cb1ef38e0ae678b4d449ead853d4845db7c1d41fad198cf2aecc594d13 e01e97370bb7b5364dff8611ae6da78f1a38102bd5e03c5e24eebf24f4ec00cb 5553e29cbf778f6b48e8c5e2a68c9bf3d81442e1ed81f25ec700a9e23a662466 23024f3db8eaf17fb8e0e05330a1c9760245afcadf2efcc400df7c7d17a2f767 a827106be8428f79cea92551f543d37393685ea2a286c66cae92f8182cd39605 0b08a56085462887c422032a20128ae61ed97dbf5e74fd8d93cdb1b6e19461e3 3b2c211dbd384da555d95b648a128b56ab6643b216cd0ef0b3efd2383428be80 ef5b4344e2685e2cd651042f3f03dec4e83d3fae8e19f7390756aac1252e8a2c 6874856045a450c60b61ff651b69e34e2c834ccc28f7f483485e43f2b7e619df 397019b0b5f8892c9cd5578270342cc060f29908a62afc7833f2132ddd890492 56cab4e804d55cfb63ce4827aad3984945bf52a980afc5fe65fe9724217bc007 516eeb63ea2cbd880702aef419a1a7d6b300523168bb60c1c2f2176cf1d3f0d9 0e9ae60794a835ce5d1988d7c461eb18f25ec62fdabce76be9ba7937a9f69217 0cb857ba94a45956dbe3c9634d5c0cfd5bf97ad640af92d0b99fb0544d7693bb 2095075d719d82294627cabd25b7dfaf7158c62ad211c771d3644c29a5e8bc7f d45ef2f378c76e52ff85f3586e3fab64411eb387e3a68d54834160e9d4f75195 28e2f30d2847b093194a3effa41f01428cdad1f2d20cfba6eb5139f9e0163a5f 65bbcc93001475bf68096f7cae42be2412e3ab74a60ee2f4926fe5c6aef83f0d a61e3661ee0c48cd01ada4fd2be409d2b461acddb708ec548cc1bb0f7eae6f97 d26de17c60cdb8043f11c087f8cbdf014dd27c7ca80a08fe8aeef96d0a1745d5 1a493350e3b85b91b47d8f1f52effbe30f2927f6f86b73d13a1bba57eeb6c67d abd5c231d76667750f9d4a234631bf3f72d067f1b88a4b0926dca81004b3d0cc 1a3e09ab6047738e738872c6cb46d5157a0978ba84af4e4f4436fcec61ab6e6b 29231906cfdd684e0f4f21ff0b0ab42e9969bf6d6064b38b735f90428296105a b4f5a4ed8d4169c040d9a4c387eed3e2a2282668f95e5d59cda02b9282432bb3 321e8195768e71c1859bc220da29d8b7315671615da6f7a1c70a65c08a1c435d b8b37d590b0147ab67db2bd9cade249369b44bca21e7027d56b61f375e643a7c 4f8253bd0d973bd1079b792ce8e714edfafc5b30625babd7c6c2a9f04a25f074 466cd2e93630e51c245a31c4d6cd4c6918374d45324eb23022b87214a23d4fd4 990b405d17c5c8583ba5b4967781da5456316385111c52d4c1761d44062eff05 19 1548d90dcce9ab43e248bfc01034d28a19ec4355d927849f7553b4b289830303e8c82b37157120c61216e4711c7ef107591bfecb5e24ca746e0f992984082205043ef8b7fd5e3d1dce67cec39463a3859de87010a75e964cf2c09ea2d983710b055fb8000c5f8ee2f7c10d973d010548ceefb1699c5c80a24f9fd264fded960f0a5b9fabafca8659199e076a78283d6f291d79890dd0568cd654fed2d0eeb600bf96b8306a3df449b2bca6d176073a106e53e3a02eb1d5b12ccbfe00341b3b0034d3a83e9aff008d2f029b1252e62d55b5a7c0b6b30aea3b78ab92131fca1b032535f849044c6567bbe70ebc388d898fa231bc2e61fe387ba4df0070fee797005c443acf774e192170bbf7df813650dff1c4f38374e4b5a6cd4c1636103b220c93405333c6622beae311836860a889aab363eac2610166c6912619b6b4b69e061a720f5f99dc9556b8031b385f732db873767b09d7f550d8ba348a686393d208589bf347f450bd4a3e5e02936cecfbf85f3d23828f3faff9d94f3092c13d640e33b4ff8e7cce8e22f86ef223c3d20685137817bb092f3e9253b3fbab2431a20c33d7999bef831c3a149bf9378113389e162d3a8229fe5194e8c5f72aec836309cbe02c4bf37b6804d6f261b6d0d982a7fc3b6652b6834ff24e01925dab1a100474ef064fc1efbedc116ba9164398db2b55f59289575a9a30184895c0caa13c09b271afa9455ba5ad23295ce604c2623600266922eb11d82b0a3fc7ff5700ce08fa19ce5e0869a99279011ff7e58dd46f0728d2c481d4e25d7298b2a57755d40b9800794d6eadc7cd744fb28d72c6fc830e84b71e5552af17b083120c6d688c037be9909484db11efb31fe69490e694c903f1bf93671c8b92995347ce56d5a70400779af62ded79b10fadfd9ac1764a8077d7c8949c477847701459f10baeab0da8367608f69e61e6c14ae2ab3280042697e03717e6b734d70280bbc9e60dd309b4167d9e058328db60916d48bc61c35511f642fc84bce5c485dfcbe6b130f60beca65c2b424da75a24a4d3866368561520f8c315f67a14d38a47b60147de8a0000957e317d015fbee9c1721829939c95b1387b72023e209bf4ad3dc55d7cd202fc77ed7f91c81d1b55e74c6d56f5584b68161cfc978f406898e5ad417eafad0813aa416e5b34cc21838ddcebfb8d276bd77fb0d6a6abacdc774c676be02510001aa2ff2387d1b6abd1baa58c6a14668797a57d5487381dcc4abba1cfe90574000ae7ef1a808cdd6efa461b36b54c30881bd975f619772d5fc9d0b3c503637209ffe76828bb602a12106f1d39944b45b8fb516ba7ffd6769c5a56900a4e9f130f5a64a5173f468db7f56fc52d189616825dc996e3b4b2ccc097b3b858c12511067a1de9746514eb68226090f4962047091ce5cef53db00719840cc23687cd730ce0dc8d665150ce694a3277abf1183d62a7a0730e16286e0476bfcb32e287e60b61ddf6724578e6f6ed5b221c565fae8df2efc90bf5b90ebdbc1a3fadc6f0540f9f01b0865da5c8b83bce5e7d2b1c1c3361cebc685d97445af9eb36596867a0084b483c0011f0f414443909d368fa8e1d3578a88e69b07dab67c7f2ccc9eb0e0df783767bdb2ba1cf25d60c2bc544d40fa98389e22fa65879321dd289405ffe07237bbb2b33aab42aca679795161727fe472232597323df6ec7e7d424ea4df60453dd5a3eaac1934222299210d9521dd44813fd68428cfd6ff558c418b6f81b00e2e2fd072f6dd56b9b9a159f6fa592e36467b310bd9c2c36a37f3d8f3572e90fe437583baa28679f2eee32d4f6b492f7645c9586bcd19c2745e32e7dc7320b0337512313842faaba7e55fdd51b419c2ae5c805d8288e66852ab2c331fff8af0c7b48c92c63fa84b288da54cf08ad2da2934c14b7f3f0935e76db8e5f1afe1d0748dddb147aadf4943e045e0448b5d2bab786a85a11c710c51828d6ed5ee9b504a53a08e5f472dccb019ab8875e76e73572efa1d6c88c849a8b975750a334b301ca185e101a3f41b90ac8b4a9db4da764c432141f9a7c86a522fd6c46e228610cc7d6414ae413ff6dea7778142dd808998cf3ae2495668a74326dae1136052a0101f1da16df5fd0bbd321d7f5f693801757d6f12a20c251791d6391e3e833a5099960b5329c02e88c48608a68af41d360ea63c73c95ebe52fe624ad1cfd3a1d0216a1de5fe4fd39d0b9521fbbfd78eed2d3703f9cee0208f78250880c9b0346090992b95bc551edd606545654e3f4859b7b1a1e023c4fd7f86d251c282504870e56b3ae705d3b3537d4b1cd114c215ceb56731451aba5db7fd8edc0a2f9caf50f17af24fcc8cb6af0f0c06559efae18fd8e38fd51ae7a6b03db906783a9e8c10e1da5d4e0e37d3548c9bd14134761a8688dcdedd7d9ca6cd03e9ce2ede7d0ef0865f6d77b4e472ace4126cce423f878a2581bc6d799433f67e7ae39862f59a103d0c5fc5a9a86ca2cca982dd0212bda765a48a0a4551252ea2630388b235ca30498b517fa80fee0f458d9b1640b6441fc6078100d268b7e473054834c52f3040591b845f08f5473419c1d234a4a83c57aeb9db06e65ed3b5a42dad007bbda6001d5af19e170d61e1874484a07d414f9d8ae8a9cb78ca5eb82e19112de940d9c0446a923187dd64de324a1cfe85df417742db730ab15f6ce4d48c28b59872e8b0ceb4e906ca27dc1badf99679376535e315499d9abe3f1ae766ad155ab40d76103efdbb3b375db760f2f114fea19520e9c30edd14ed8ba79af1909838af3300a0a4de783e5b06b5619450211de457d07bb6cb5a1ce107d05042146a8a2982bfa08fb231f18b59f2a416840b92cc8480657ef2df2226a170bb5f25dd9e4ceae7a0856ffe10f5ef5dc1fd449decf21b5fb558e556197d53827a2ed88c8976e713a024cfc5643398b158365216dbfc9c1f657c4a81d999d63df9dc2259126f4ef3e0503432b9ea78865b14b8413c6721ae7290535869ceda71d97a5665459ed9e1806a9968e6361c1164968dd4cf4bcc9ba28c482ea38c8ddbee268d3d9d50163b4013dd5cbc611b7f6879b068672ce19abd9bdd572753bc5b5a61a291ac6f4afb202016d2c80b2bf39952739796afe4fa699b762c85587d11f18163dd3f0f3088705174480b27eedd0ccdb59194c21ac8416c63b6c38ff3b85ff32b972fb6a84930714f4306968e96aaa4212bbd29ff39ae075f02b2a0ade1031b30bdaec186e9401b615ef26c4badce802cc714ad5b808913e41ecfb72163702b97bb18a14a93101bc9dae3936df14e170670545e460acbe1e51ec756fd5542bcf1ecd9e50088a085f1923bee4dd73d1a82a3abb492d4ab7287b9f33c548cb0be80e6382a4782f083e9a5be2709986a52a4212e235438bc28ba4e8d74d5e3cb0ac122869a42f460e3bffb1f0022f7e8c8947b2fd677d3c148876bc11bc0d1ff48ab02744736f50020df4e2b738984dab9ece08d4cb6b2244466a8005cd381f766f888d6edd123204a4bbf94a5a92add61655834a959e9516afeb5ba4f949464713c9bb721c64ee06c607705803724ec1aaf927352d90145dad07f1dade956c0d4f934a0df14b0005c5e46891291084d940792cb48fa2f0b985308a941e9e83375a1fa8330157530652a878cdb1a0747f1517321424181a5243c0786474c331b85a2c599b1a1c250698223770a4a461af7897491005c71be734d55572bd22fd8fb4fc4e2fc3f6c7024144b0a950d58e2b0452333a903b694c24e5a668e0627f24435f66c3aceb2d03fa326f7d5f96939c333003c14b2e1b076162cf9d1beb1136a0245bd225ca910f0627d8392c1b13a2d8ca1bdae05ae4c4668965133b15c88f9b1bf76897d5900290996237522e41abc39bd1c71dd8aa8377989d5ac6215f23a274b7c1bedee40cea49f60cd0fcec773b3dd99827f3460b12f759e592b817a4211bb6cd3282bb0c47388cfee71250a08ac6191c7ca93e67220a25cc7b787f8e48093e3dedcf030eaae63a1e03cace1f807e7b6a3bc4cb4d102ae93eb8309e5e4aeeb08a18af730a045a4b83a00a258300f506a99963773439c9c0bff4bdedfcb4718298645455072ce38c10cd912f31e78d6d0508ac19bc5daf3c00273dce16293b5bffcd2b8c0c936a4941d7f411a0420039da80c8d0677730d4e2d4b8435ed7fbfe4ec042be08d70ebf121fcd9581437ad540c0483fe1b7d012a9d76e9e0f7b202ee521027d07181d57b7ddb57cb608c41653b3c79b2359d4c7783bc2f8624e72691a26d35f0df01c422a9c744e8267110efacf09ed678fbb539743ad19251a82eae5a2cd840e15652a22e6d60f9636df5c168c34841c5aa475f8e2007fcee564b5a445387c07303fd415b2ea74aab647febcf5c73c974c6f321e52df8bb827357beb42e60e0f43dd97c23b8e73a45f9f3046b08290008066d4bfe5e8c770ba4af256fcd0f003288074bd65529500ad54ddfb45a6e2ec5cfaffd7716f9ba14550602f3de7d801dae6f1a480f7836b51381be68a178f41997a8bd98cc54afda137c96003bc320cea216eaa45da57536df1b7f1ea84a51f96bf0f4f0cfae77f631990a6741abc080bd4df74e3929b66a2a3603737a6274e1b03dbf7abe7a814a0c1a91ac6933701328b142ae5247f5b91d5f1d6d5e676bbb0b9a790da87a4571b2d7f69074ff60c57665303e316dbfa008c8e9e7686fc2c4b7e76813def0a2d007e7a4c0bd2e104a2edf7b87ce28011b78debc7a7770f0f493674f93dbe8b6cdde89aff353e07095a6a14090f9c4215bfe23b3867843b6370319c9beb1b67af02fe44f3806e500b1c2dcbc93a3e741c8414e073b2290fa3bc21aa90ea80357d73a8ea5bff5be5012ea41b72dd7572afa81d7b09845b9d5318f7c4197359f0c4f40925b5fdab8e076b81407b1006d0de544fc16908fe6b59ea2e11c621bcee70d9f9cfaf21e44800e2d57e48c6daccdb6001bd23693e36bab674cca1eaa27e3abd617ce2cca9040de7a7fc5fd0549484847b142bd217ec43959e092d950bd4956582886d84689f0c8d8b75a4abec6ec3f6beb0d82084d018c33a57704107457961a2bf8f079ea209f90f391cb50dae3c2f1611263576448d70449fe30645af946c86851ddbdf18019727f506a36b00ed5e35abff78ffeb2d106a8da732d64917614ac4d5556cb6022e8aef8fefa505fe293dc89dbca410d7c22a524358c6351f7619c6ed93ab840f5b971b11a5308eb18bd74aabb8270b7ed94d68858020ea416c2075997475830a3ecaa554b812c104f67472a94b02ce9b5c766a90f40ff0d35950e862ee3cf50cb1556e62bc04f564a13045255c7a80fbbcbf8c8ebdf82af3fb8233ec80c4c705539d5e6ae645766c50e0905266b734484e032c529e1747242d5b76bf03ca9209cfd5d35ebd5a4b311a6d3bb7e7afcf340c18afb53145556a8cf78933360d380963aaa2a945c47a134d5183d31bcaa204c83af7458b9a357e0adf1f2f63a9010c7903f3a4fc2189b3374a7c27a9d95c5405fb787af01864db6986713a25ea770db16de51d9bdc11eff18ff26f871fd4e9920c57eb7ab537f56898a368e615b603d13b5cb665164fc6696b5667a2ebf828b37993fc9d3865f4ac7cdf8d97e52507afe89e453cdc359402aed1f7f4b45000def9c1167ab4723288849645af72fd0a146ae27c0699864e20224c6f5e9914425d2599032aff78ed9f27c240ef218304d9992cab0170546dc6b6b0ab40aa5ee824e14169a5bde5a42dad7107a3d08e0f8c2048d9581cd470d10049f3769683634f9e6b387070b54f2985e3a87fbe920f9f89de81ae8b54a62021089c9e2518bbbfce05be6214d3cfeb59b74b8acea001814b7d09d8b0cf8bf103ce787daf009ac204582622b7959384d7973d6da30f028d64255fe55e2563943421848e7db99beddc76ac1415089c639e21f56f7d8707cf9dc0187da082d52347469bf04fd685b69960f2736d6198a710c8ac92c3fb07f73dd4aeb85751f6a4c52d8498de49087d717a994e2005b34e9360f1471e700b523258b3667f2a9b43caf46eca4e48d78ad8e46750b175fb4e034581c7937c0159f135606df3e174563b8c4e7d6c90fb6025308d68651363e0adb803c42f0e03aef76901f55ec1512cbfc47dee2c871339bd6136eb99d95d7198bd790b3cd903a8f0be754fd553c7431efbe59c54a3fefe653978c5e49b4e1018d9c0633aef0e6e8a7bf90586fa8cf36180f9badbae530cb946375ea286472093964dc539c007c05345a441903a7c55c92b92d932afee86e8b7d6f923a7cff3559dc2da358c0fbe2dfaedfa165adfc03598f176fd1e420c281001727eef40452725b7888cdc004c3a65fd912abbcd4b200f174401149092562370405267bdfc04a7e130d1cc05b8abc3791ad17c7f29ad6f1e360d87ea0ab2349f33f049e26b7154a5eaeaef017c06ee0f4fc43d0bf8fcaea7628b42a44d95b2c0ae979c37cc57733c45ef1b0b +generate_ring_signature 19b59e689992b33510cb3a4641a0487214eb16b6c4ec84f17a725a64d241eb02 35404cf6de872e69cc675a0c374d23085e607a63b198b243575c3aaccba5110f 55 96bfedb655e4ad5ae088e1170a9f8b58daafeddb84a3d8922c7a695e82e66963 b57b0dd3ab098d2e5a1cb9df3b9ecdc5e05a5a79a40c818eaddeb80ef3502be5 679c0ec82de8ea3d0fbbd4f41f61cfe30d486e8d2807a7b2da091c4e79cd7efe 1859dc973eb9798fbf6c7cd07db81acce29d707577dd72eee64a3d5373103ff6 4749c0e2a2595df0cc7608c1893942e2bf72f1ceb3e4609d0a12737f3268378f 34980ffb911d8f163de0a1f2da648b989d2b38f41e9525b4fe2e1f5647a15263 8bb5ae4cda78148b7b0333f2ea45fb88eaf413880cce4254f6e84dcea809ce23 e02027de37ca5fa4a7490a35a52f26d3e1bc1a462f8357c3ed9db7f7ec1bf6e2 dc30251df7d13fb90afb8a6360bab069a15a7f18b2db02a80da3a3906b96b479 e72e6290ad812eb649b4a722ffa13919d2e4fd49ff4a44d5e1f8d80976553fb3 70caa5ef3d0766d48c6d9a6728d0d539290b4a68452876c9b8554590429da46c f57560f08f8f42d053aad27a4387fb09c0c914d921b518f1d158dfdf16a4e17e 4655b554d751125b469451635239474a94238a5e59aa3ed0766083e11ebfb9a7 259012fdbf5f5453f7aa98d6b021c4da9214f34f297b11e32f1effed4bb13909 81b66e8895b7411a2c7b8390c0b20673b1ae12dffcbda94d5561646a6d461dee c62b504e655ebf52bf1d72cf50cfe4b4e25a67cf6e968c939e8baa09fe05ada9 139ed705b0a5f440be69de7d7d3ad3a2996d36f8f809f02bfd42398b02e91dc5 392f1fe7ff4620ae2a3757acdae3b4ab011418e843153628c3193db731eca520 33c7d48405a1112a28fe51de2e6a9a9fc35669d246f23b66b88fd65480c23a80 2ecd2cff507a023c57776b03ceb8e75536c3ba9d734bb08672b7aca0a81dff95 4608c674e9421be71e557739fc5b2b868c8e46717d893c499a575d6d1f6af1a4 5e89a1023aca7015745d6e134222ae6d36574605626c816396755f0be2d68bd8 004f0204e19cfaad23af654dc4ac284fa89229c2ef95b9bbd5aec1e0e106fe02 20087f0163ca1244c5d52b7d9c343f4859f0fb8151f0ca44fd58e9fc28622cd3 d061d7f431bfcafe091420eb0bf49476ee690e680f63d2cd8e57ee04ec315d43 17107bccfcef930b408cbaefb05256f430fea8dc5fd4250ac9dd5f4ed3fc2547 ca339d35f0fe605eb0587ec378bf0f3bdb44e11438f999c1bb1c3c5637b7e2f5 2f89eddb111d0b6d8ef5a6a5c265c2ffc491d0e9d09d0017f494df2dbc360f0f 88719f6e415b781c843652d420fd554d291f2c0e8cbebd6d7e0fad80c04c809a 6e0b292ee1b4bf0767a7163b61c7d9cb7d4cd45276a78913b07b73f1c3c1d64a 4eabea46bd0266491d0228719fc8073a404fd25a4e837accd5251a61cc87c02b 2017496e7854f8fc95f17df21bfae2c0afeb43aa28c245ffa1b5047dc149e89d 24589b406b4a7698016ffb8aeaf5185993b8502e46d46e3bf37bb078623496c9 e8e32089aa49dfe331db6fc798abbd5c3f2daf7e1c98263cc7869e66dc7adacb 05b2f33d72ae54e95169937adc6420774768db8a9e3d7236edea05e4455e18b0 6a4b66dae9632c12f4daab911b0d8b9945fb75c25360549c23291cd63744c3ac 8f423495dbf74ee63563293a66074bf15713e1d3f3a16ee4d5f33d414bff6fa8 3f53e929272cc0ec8e023cc81acdfd50852273ee9274732acc069ff56e8644f0 f0a84ced9df533d2a34935af3d0bd07d544b2e84ada7c3143df14a529302f09c 39100e3dee3d1985e04694de6a49a082df90f3f14e07837dfd96298ee8019252 895b06215ade6268bddf5c5accbada81af003ff3a8326701c7c4b2e4af1f3f66 a99ddc831aed70624f0e17f8e1ccfcf5add0d30ff448ce7bdc08d6464fce9e1a 60ce654405b62dfea39278dfa49bf1cd1cb640f60d3bf18fe3dab964832bb7b3 0f755692573303969da6af7969a990f4fb4d0d5a22c1c446338c7aa394b8ea55 10da39906513a827a213081f345f6253bf93cbe5717b8add690d76499580ddf7 b363a3561c0abb151066a5a39b623c9c1b7745333dc16e3ff858b227588b525c 6436c0ca822aa4068aa89054638665f1936cd2a00ef7c68c18a200d5731748f6 310c66d3a506da4d1cab6649b14f875c07df63139e6c4424791fdd7ededca9eb 5137254f8aacf7b9b331927b27c932f68a248df87954633c08bcd84cf7bdffb7 1616d9caa4875622e3802ec550aea5d449b648ebf710a77d3c22eeecbbee6f76 56441b786f910a220f80a7312b1f58175bea544c2831d29af67671af474cc5f3 dbb337b8152363bdca9c7c7b15992fbf2797f113c6e035310becf222ab12b970 36fd79a1708fe543dd785cb28caa54f1d2224b6959821294d8bad2ba982ad4ab 745e169e91122070e4320302d73305504072395f02a6eff85884be79abce0c07 870a32a647e2db50d03544860d834c1c4666eba0e351ff13c0198bbfcfc89c46 f708b34270bb6c3817c74e5227e43d13c13ce3dce8162b8ac558da1c2e81be01 54 a9dadc8c65a1061b423ed1e53d5828d6e63125105723cc2388bdb794d590da07a50778792bb4005601deebe971214179db3df008fe5d97a49b6365d916950406e02159a59b1104f72a08cece141b7df1391a81e5e62e92102ec74c93083f7c02bb126758ce4b5072468a8c9e78633252a49f94b9ef0045696ecdc31cc9306c036317b48dda42e4445f307ff910d29bfebc640800c6709ca062645489fbfdad046d4c364ec980070944cd1eafbd222c8357e2256f4606718377b72fc42e80df07f6314dc9a9fbdf81956e69969e4c4a8155bc1618f21f5745b6e95dc232462806028b302d7dbb6163fa3788c14ae1b69ab43853f463364a93360cbe7b8422410c861f4d13a3c445665d33a87da86405287fbf82a45e26e5c10b5550469e9f3507cc1d15fc4f5276f22e03ad4155e8ba56f56bf802e3927efe024a0258227232049cf306e84ed23c7419ba12fa48d05fc4cd7d0f9fc5e0e5919dde94aea775aa0d5a698236d3f3653499ddf3e6e32246e7e4a0d4bdebe50dd99ac8cc47d6e3910c5b724397812b387b0151d9c7fe1db04555b7bccbb6cea9fa037d70673105b500b65f726d2b3928dab55afbc5870e25cd176aadbbc845e1883ec1d8b14dd780080a5756b7a820455393cdbf75aa6da11648dff063dc1182915db97f0cda33cb00ec955b8109ff41e7e2fd519859f72ee2199d73d831a66f4d0919c637c630d30e0a8e967514c71d2736fc49418283d979f969c39a503c84b607f737ac1aba8e0b0e4909d1e2e842f295a73059d7579137dfb931f2362a1e1d497446da51d0bf0848aafb11ad41e963a0c7f45211bc524503ed2017b97b553448d69f343ad248050ca9669c8be7ca1615c1d4836d8e2a4d424cc2c2be302b236c8a46d8bd30960949fe54726ad3c3871624cd94ede0b6aa69804ba9f55544171db4de0d007319019abccc7dbf158a8e628708bc25b80ffdeb4fa1932436f50ae7cdde5bdcfda602db79108872caf07874914b459b246417f8aac5d7a563351731d29c21c4d6d30605008961747a04d20db8fd7c6189811d672a8a948ba843e6db59d439c0809e0e69e4cb7ea493121893fcbc58ea3afa7e9d4909e6a364f536d1fa1255f0f5580636dcdad4d6ba5e15f2dfaaad2f8f05345a02ac39d715a5c22cc5817ff9a72a0d0bcadc40c6ef13ce30bddd72f146e5ddf77e8f6d3b5ef001d9f87bdd160fca0618ae77ec6cca2f7ff437cc78934157f495184a7952a69d0581749dbb4a4f82045f2f1f55776cda7fd275ac14d1e62abfa868038106ca8151173df33f33ee9c014792395c1d3fce9a58a6da428f660fe6758999fdd52475bfd5e1dff9b387200b753006c904c3de0bba37650a66004660f51444cf0ff8addf1987abf051d4df0eb3cf2c718f12decb2d3eccd95a53e087ae0b85e47f1da52966f9e0f34a7812028b0a7ca592c7df6ba343f2321c2217fdaa7a659a9bc6a701123176be3f2177086025e5bc3111ff6fc3141c9355b1cb1cc8be1df7afe29256203a98385110aa0cf5610c1a996c4db92024a57a63707b81574e40f31f4a7bf92b3954f217f69c0dd997ce5d1e55600501058b578566330d00301001ae2b74aafde3a6798c40110438d5c2a30d41d6a1e2206bfbc3610990e92aa9e986bead84d686f9a9e475720b848785acd1001ab13021700a4fbde4ffa0212a751bfc13654dda23ca9a1c25022fbf9cd04f95fa2af4331419aad79afe7407f653619a85e3619e624aa6565404876651dde1474db090ecc6e7977d8bb622c286c9dcaaef4c240c0fae20f34904e4da7cc4015e260406c87f9745a2cb6eee401630d8287954a1c91d0843a1f702ee57a0900a7f948bb21396f406e350496f5afe83d9abcf8eb2483d0af453c30a5226324449b92f753aef636ec185edfd3264f7d2e0bf08d31caad1dc418de50579661eccaca9d2d300a1752ea1b3001e2be58a6aaad0249334629882ac5d390db863a3863a6045e98426924dde4ca7eb9aacf94af1808c4cef5152372cb0590585a3b668edd6dde937426552c854899115ba031f3e411cc3147e4b4593a5bc0ef9d0d24e4d20916927c6a0e494934fcbbafa48ee568dd31f920f48b45deca200a08c9ba8f441c2056c25e8ecf0019e39a8a0580ffcc21f3f73e5bf10d620150c54d0b9adbaa036096e2df0431d983a8c059f2920195ee805b85a4e8d24885c053b0c16025bfc6ca3d0ca125530b1d945a3b03bf149d2cf3fbb625ebd29795d0e098bb366a8dec65ace29acf8aadcb959e02b77796cf0db16877adf112383ca0964420dbb01ac01aadce2502e597e147c5373eb38191f76cb547182ea56bc870451409b30f88225e808ad47a6bf20dd902ba982ee34d87103387638d11e3e0b0a9c5f61b28897074c4f551b3e1dc62f30b020b39b6805c270ea68ed9cc4127b0eefb2fe1cbfaa9ad1086bcf8abd803a02059c02481fbb8bedc26bac402449d9080b5e7cd3a15789e2c5149cae4cbe88d3e0608697c22fd0428073bb2bd015250d98672264535b2ee62e559ef43e375256da548fdd7321187d1456feaa1339af0cac9aa8f6d149ba336e027d1b8a4d6819c70b3b2a04e38731d95f583fe4cd360bca59351eaa03712316ee6645a0bcee7ceebef343758744eb14c51499600ca600a6edc358e4a6b166fe2daedcf8988d4666083da05522287d98f555dbd0945d05ddd34e4dbbf0014d122a14892b0c9bc0f35d79e2770bc0ccc6203b60b11a32029e42a4fd5fa07efea52d7b50aae093d08fe2bfecb9fae99ab33c8ea08f9097016253e5d9f991a21b01ae845133556d1cfb3585d0855979d40c3e7de4c97d5e09e20cd0481706d2307b7626c5a593008198f758efeac817c985495ad96eb0ba05ba6a8a1a1a9e8dede26043d6c3f549d0f6164c11f8953030f24621eabad6880a96a6fc846a2a662fb0eaf75d89609d7b5362ca39f7898f07dbba0ccdb82b7109349e150a88afc4131efcd2a4153af304dad2dda0937197194b2ee7544af2e4070d91f697c71e237d0966b7785570c1e548ab63d374cb26743d4d540e299abc0250fbadfeb4982ce690d2283e56481d809f6f0413b79f5b0a6f98bd6c7b237e0da579e1ae7fb621ffcb814a3fc1f99689b78a8f992f47bde4d355944a47e5760f9c17ab5596f5a2a51082b08c5ddec7d55256ad5071379654e937f141fe5f810177628c21daa2af136e195d0ad690faadb41debd67ab93eb1e514293fc94bc407a234183739693564be9b873f7a7f788969c1da1883336569a00fea857dd9810e7604f45c6cb96865ad1503dd4b512d703bf16f652bbc0a653bdd62b4b11873003536265839659acc1d2da7c0f27a9ef4db7c59e87c6b69ff41035dcd132c9b0d294c876f11c30210d95ce56d61f362aeced7ecefd73a9ffa2ed6396e8d98030917aaffc8b00c94135dd010a80fe8e8f8164c86fa28b18bd9caa5e5fb608e7d08749c8def35aefd86a151db658ad13008dd7eef3ed3d78260aa090a680a7d000220a5b95fc0af46442d1071a87be783d477cc43dd93f80d0a8e577e0c51a4180c5f633b3ecf5124a5ba1ee16f98eae99544a37e17e3397617ee364b54ea7faa0a0e21be35940d43ba8cff07abc1eb338b9dd1804259686c9fc344310b93956a07b67c6f9c393d99884b6bd0578e394637f8f4bd330f67826d57e095869ab0ca035235b55d90b1f0811c2c941f03ccfa51f3d0546e1c3ccd955d4ed8521a40cd0e97e331f03ca75fa8019486cf625cc2c8c5751b896ffffa4b932b5a8ae619c805add99024dda69d3d8839c7347e899f2a70658ecc9ac06c8fff77a7a4355fb005edbd0802061ccbfbaba8490565952350eabcd9573fba632b0f6edda7430f920c972d48ce4ffbef02840029d55ed21dcab4a0c090a438cd30ca1405d7b878060ce36b2fe99c9544d27e2cce11e9e3d55470cad45df05840f29861f189869b2c0c0a665ba6807d5b10028d49ed0ddd2c7ac0d411375164217251f9f2853d46740121fdf5642e94d76a7b46515293e98b9ccf1f6f79c4b1e338b616d1ed8d72890e9adfb4bc23678e19917268a07b698ed65946946458ad598ba604000978e0dd05872599ed4a8f8ad2d0a190eec904161d21b69095f9070f12cfcac3755441a906d247128a6f3246f7f72b119e8abf9ea022c946181dd86f04dfc85e871299fe083d31c7866ab001f247a6a56b1fd5341dde2dd660c463fba4a72dd3b06f99d2096918904767a9d31d0296d8d043d87b0ee73a3c5be6dc2abf871adafb6393230ecddd27402000636724d249809ed419f4e7dbbea5c4d2cca76150b016309c2b0734f866b46fe186b18e0616facb1e4db47cb070366839fcae651a172145e3790ae96113f75419fbe836769161e9eb59e768f6b449fe84a68742d88ef103fb690cf37fc41843cdf1c3aae75bb779742d178c426eb1c41b0b89fd35b01621e8f003f5b71fb9d881c5bae0e414f4b869052d53af1e1188cb6b3fd40cdf5c953181061e4f3299f4ce6aa95bef8bc523af17c64e16504bedd089d736ab771f7e7cbe0c1b9eebff64bf0337010609833cef60ddee4abc46d34d4f2a2d76086006f509029e7a04822798a49af80dfc53fe3cb120ab5e7db0384ade1178e04a8100631a0cf4252d454735765dd6ed2909af333f4f51bba560aa7fe3a078c8c6aa8c2900024cbcdc41fb28934b1134364df1d8f5b49cc964db7bf6d2cdcb61514f8b540a0b2a55e214743312ea901c96d0ae4d6e0ab7243149d0de09495b150ffc921a9b0bceb7f71fde4b917b7fb625df5eca84d9418b7639b3a6505d98bb3b047b2aeb06bd6ea0f6cc677a48c79d4626658e5a4c9f2caf520b60b2d99788b79bf6886808008954345ec618d85c386b0e074a93d8dd57b3e6072d20909780f9fc4956bf021f8e874641c8940f0cc05ff35a2f0e628178477e461250e74601519b2d522d0b +generate_ring_signature 4672f905fb42dc0475983eb5a48f658a06268290809009c1c1b1d2c76fa8b2e7 1e95cfc5d2d71497ba502d461fcb7ea9f9768e229b9c268e3f8341a4f254990c 1 3d62ad8a5671f7bb33e660429e1a8d65f454ca409bded90f89d659e10284641f 303c8f50c931bd0703582650568fd4d3382117c521de689e1630abaae8a4440e 0 9af776dea0e4b4f55930e52bb8c9a2779f73e23dd3641d4c2bf4d1ae7c7ab004750b543424f64c68e91176784b0d020274c1e134444233ce83614783734aca0d +generate_ring_signature 3c562fe4a9bf99c509f2e8d4032d42128a7e92b168392d9538b06fe8f57ffe7b bbf7fab81426cd9d7f448815014c590fdfdf9438021002f067664326bf4c972d 21 f766397c6872286d33fd553796dbfaa31e7b3d1fca071ec0a4460dbf140a9f1a a6a61c5ffe6b77455c458b3cb3e48ec99c70963f3d0b7476f2f3fd49d798bdbe 23ef9c123a46aba32944da02867fe6dbb83456fd81e57ac37c04fb398d748e2e fb3870eee10bc2a23e5cf82e0023c95d7de15ac76adc77a78e4a4c15a743b52d b433b1ea0f29c61a863315d234cfe7abf86106226da82d4de2a59efee82d4a97 90c7cc26068024a6a5ea19303358a9768440254c5fd395e60770dd114003b12f ebdfdeffcad6e468659e5e55c0702cebf169e0b6505bb802101acfa7936b5971 b558862930e7fded4b64474777d7e762f252d7e5ef32cc0862f259ffa0cc074a d10554549af45f28017be503d7f9d838d8c20afa543b0da9cd28f45089401038 939c678793f7619c5985f478b78f90ffc9b3de93a2aaa1674ea9a3b3f5b4f369 e639f8e4a4196ea5094fe047663e959e329ed2a9c5b205d4192213c5f3c45201 1b4d75055248504ac3c2821e2cab34035c4c28941960bbd177208e10499f0bc6 1b6e7a5ea11b4e4a042e2e197f06dbeba08163e681566fd83737e209ecb69678 209c34fe577f2dd7cc74706175211a65b87489553fb104c9da0ff1cf5e5c402d b1ce728ee6de1e258471179dd350967dcba276f7faf60a9659756e1d545811fe 8b8d736c76157dcc516cef83ac9318921e9d6adfc0e4a40cf686fe71504d79a5 96331d76ac46063e2d60b4bd809c91e77b893c8d9e2c53d27a1c378c20ab40be be0d16a05eaa11ec4ef8283d5f1b3ad1cae73883c059d855c7d0355c1249dd23 ae7000fefca7f670daeed94a5f3ad56697fef12313821842c0981271f2142d77 8c5fcddd3f4b3855f1d17487883f33fc05eaec1c7949aa9effcf1e17eccf8895 8900e46d54a0a2ead552e74ba2753b5c8d2b15e381daaeb48569029433d6e8f1 edbafd6cb274f37b1783450c83485489e72afb99e07afebd2853f9997418980d 19 65948e78b6cfe916913c45cb1908b918b15a9ad22d811da44084fe94a2284d0fa53cc2267b53e5d3329ef53d4531d60e51688a45e1556cc38d6dd787305d180779323e6d837cec5fb2c3dd20089c2537a95523bd12bc1d46e4a985f64fd19b0aa7ee84e9e2a0cfd4438b4949e44e637e432e3fda35b6d3610948adb37ed84d0deb4012fd79bf4b34c0d3b5eecd20a3a9beefedb224afa0ea1d61f5fe288f8f0540f4515f2fd4731ce06be5a1e3fe319b97bdce5c9162a4269efaa55f40be760eb711b1f0770f7a8f3851f5eed2403193c57859ef3a9c0dde5c7ccfcea87607079bafeacc171cd555b4683cac10174e7691b6050447811315245ffbdec8b10707a61551307b1731e08501c4c77d8705f69a0fb5f51d44bea7a1757ab49f04ce0ce5934ce66033bb3ee52ed145c02852f0c817579ce9bbfd0dde35dfdeed34720c73713c4d18a4620608747f8ba3d7350e5e607ca0d53efebd3521373a49cc02063164c9bca26f0837f32c1aa1fc5c60bb118d1e760bfc7cad26aa58a106a30e0ef26a4376f7c81394c686869e80dec659aeb631521e643f8a561fea7b2274750982c647d92cbd9ff751b37249af2d628290ede9b6c9bcddb4ee2b64d977d2680e765ebd18e7aa240fa0e7033a84c644ef686ec0c92bd4b5aee22cdc307b2b2608ce0a6937eb4caa3624251e940bff10e433e85dc72752f4fbc020ac9ca0ae160ed6e345bd91b7f27fbd398d1a271b2a910025ef90186d48e134ffca401f6a6a0beb675c4c8f54d2b1008c103147f1a034486a05e7150ed3fda45987430573fc0ba4bac3352a84342f17d1da8eaa5b81014fac3e5cd165e4659beb9ab6c2b4910b3d906e77f7a8b34c20de1348bc9cfbf1ab4c6f824f3fe4dada7169cff734600733abd9bbf0137aef6901d11163ef1a0e130f918fac0c90e8e1c280031c070f0bb38d38fe55b844581e6677d313fc84f0bec9feb764b9f3dd3f6e63b8ceb84f0e2f8501f8bec43dac4416bf2af1246b4acbd734abbc7404905feb8bb936420d01c24f0058a5019696d548db36ca2a8e8c30d66a6887f37972f90305aaddbe5d06ea9bad064434673aa92466a2bc13b729f1a5acfe1e33bea1552b283b2eadae095aed91a34452ffd064e1033868b7869829061046022bc7ee97882c1233536105f25125c17a81fcb43bb1d3ef920cc4abcac82328f6f01c8fa1fa763e2d13490756b4346b89e664b2666a99faeeb06a7087108c65c7ded2079890252907a7f4094a244833a3b184f08fc04d8d4bda3d5c157bcac3f1f6138b5cb8eb989a9f3c029f2cb8c010fbe3743cb906c5ff6bf93a881914c930b06f0322f1f98bd94c08050fe98befb6c4776b52f7a58a114181e511108fbc262c1af6d6a922d82e4f9505e59fb9b74428c3e06570930482f82b2e2fbf1ea57e149d828f251ed2c470110f89a0593b75e411f597b5d8cf22a24f207dc4147e2cfbbc520e75bfaa6f1b0d0d31477637e850ebdb2bb7e44aa3f09cb379c23e2f77a4d2a803b9ea99e0a8620868eda97cfdd52858f3859c7bab7069776194d714bf7de7416d50749754de3f087703207495a373c29f096a00ae5bd03daf4510392578bd580d77b355d0a9b40cc9a2a25c03770192bb645d4cc61499b58f46b8c0ed3eef8e855a2fc7deefaf0983430ad06d6c7800f435e4c8b928646c43be78f8632a093cdd5943a43f8d060860a86024d44d79173706c6b26b951cda065c75b1de3c9a6af87466cb685ea00d8d07171562747e9370ad98542f99500c9fb01712cb24c5574f9a45ad1555400aaf4baa2a140231fdcab55106e91db93933164d60d76e67f5b100a4404dda420e56f5bbd0d49c92ca07515e74edd629030f8d2bd2ee0a2deba9146dc5a7dac409 +generate_ring_signature f429996a2c98e465ed627943859394523fd3edaf7ab2a919b0d190789e34546a cb0dcebde38589ac7c0859ec7885c7e6952debb5cd69e6b0922e73eb31ba5068 14 7d8cf927a4b89584ee99103b0203a51636d454aeac07e163065565f37a22449f e5390b19dc3e87c8f2e8adf3fae09348f3abd92f936d3a4c1ea8700b29e31dd0 af63ef0b26af519197bc1c456474274d00301c68111a6552a781de6b5989d86e ad8a74a8276ce918dcfe1f633b4212d61782b38396082709eec6547be24cbba4 dddfd48e4e8599d65a87c7ffd853402dd1dd945df61402d6f5d1ee5997bc6f04 f5ed6a7ca50fa4352ef8b230f8f043d2109bdc4d914492233a24bd51571c7bb3 e9c6b1dbb79c841d2f39407336f0536b30466d76dd96c30634acfb4132718d99 06d9d6ab1381c4b6e3f914ca3273965aacb4479a575468aa84dcc088f675009c 4bca1da62da88b5c0194f893878425959abba8f1b59f70f8fc35e90fcf04607c 537de67dcb5b0515251c02ceefe5edebf29f96c3dfdc4cbdd80f677aa7a86feb 81979c19b915ed61382c40953dc698d9d2ab5487d8cf9de2958dae69d165f8ec 939b33105fbc1114c67916681405b0dffd88c6b9d265023f1889a0ded6b6f715 1da95472cf9ea935e2225531660b0340f21d8db50b8d7f9ef889ea69386884b5 bc01a282651fb0e13e062be2ae48dab4533bac2bfa7a069d281db33ece3bbef8 9f70dd260c97b39abfe0fd3a4773ea3e31f4d4a440c3f998f68482a6a580910e 6 d089823c8685ce1bdd7672ddaad2330541e7c69c224b4bc560f50c2e6fc0c70a8ebe3d2117c03e88a9edb8b4e322c1684c0890797e8b0cc8feab549f1b9b3e0bb83c49bc82addb1a69f533e69c40e3ad71cbefb61fcc4e49ec1c0cac07a943052c640b76fdb3d2bd7e921e932bf28432beb3763614530a9d641122150a19270e6ccf518acb0175eef69879627637d8a762ccb87404d391d4dd217eaab0189f079ff8a990ce72431af49d4d5badd7462b78a606fca18f52a23a56d5ecc7c7c80999bbd1564c9b43b04efc80a665141ae0824d3e3e071153bb7af1e7bfc2b8b30996b9aede9c29d8f2f1c7694c9c465f62a441051f581fd0e8b9dd2efba7543e0920958cfa74b569da0fd27e9837637589d66e272f4ec4b8c251998dc10f8c90094e817a1a4f64047bd4e89abdc3856a30bca6465a0107ebebe62156803c6751040f4da71043cefb5d0e2f8c72f8f9dcfa7fd25f7338a8806edd293fbcf25b630d1a3297eb00c3bfde2a32a45f3c8ec85c83799d8651d5673a5990564e6b46590cc8d12210d441d6b9a606d6011e5b62d455f239ae4b1b7172f5886633a724f10eae87f990010d90d15abb312bfe8a7eea7307c127405fdec8cbbb46147a16fd0131aaef5a786c2890ad36af71099c8531f57cbbc75e15c505b262d05216b77903fcacd0689630d18a4422bde8c9d929a0efbfa2478c6e7ea6d825c12fcd815c09f6527c09a43545b6eca924d94369d33df092f97760ba1ecfbe962a5f224c1a03be6324db6faea93ac57b2c7de581f74f8b729ff4a6e1d8de33ef0f24471e6408b3ce4b12361cfbaebb894a6687be08a04544b9037690c6648c9d29d58030e102d40df79ae96091c3370832a4ba2ee7166c65c5f74731cb4fa53f795c6c6ba10eeb06eef8d06cc359802f960c4475585f95a05a1ae87a181d896c034d996a45073b8113fc80dabce86ea4c26b3d943a64fff78f54c96eb417b487097ce8885b0132640f58830f6b2c4ad3ce132b4ab0b6c8737546eb45a08585bebd3ef20c0d01da3a2798cdf9d1a734394f3900953f43a1c62eaa2d26949e2f9651774f61a8094ffa30a399ef4204687a3464f8e64ea56aea2f06e17e2ce9d24403c8f1c23b0a6cf0fcd37c5836d9d37a50eeee6058790f745d7333e0196a6f12788deed3f5078fb201ff59faf7a567f09a9f03548b5725982268c6398ed0c67580fe7df5ce034f7de93e3bbd7001dc6298ff56b885b21a1ac6cfd4fd3f750775bdad9f913d08 +generate_ring_signature 8d14132e5e17124e6b09f1e79ae09c053856be240e8c7382f8d6c420f7904300 96eff4358f28c028507c8e7f0972dedb4330b822653b2b313f4730d46839464d 6 c616538cd58a741d5b8a384cdbb2d17de994f61c90f280f7977f1a485c2cd43f bcce7c9de27adf25424520a824e71ed3e00652f7a08dbab59ef38b690bc64d3a d2ac5e67b93192d3714c302e2c933c9a303d484ce2c69c242fba97913dddb0dc ea7b1655ef60438b22f1e7d37324d92b4b548aeb12d7b870f2cfceb28d0977c0 2cb5bfe3c0d79f7fb25ef00316e250bec871a4ad7f103b6a9d0c0ac7cea10375 0d5bbb4a6990c3e1c33f173354732dceb862bf580770829ee361569a8d77b44c 1196427b5966709ba0a1ca8b6cfbf60f91fd6a2c7c7e7a6609a63ee0f801300b 0 2ecf7fcaa5b59876adff7f152ccc676e1fb140091f945530762f22071769160ca199dbd70c877a88d6845b30ab0e3f4cd55d74a15c9c2dc608a697da567e230062e12f0e6867cdfff1a3f03e8f99dae3db4fbc5b90ed791d109bcb296876dc0f1344309c66fa2fc37c2d5aec0e831c44cc4acf3937fcafbf0266103f007dbf0c2de154e44e94d8cfc797b53e05dc2d3d61115dd154ab15610d74c3424fc78202008398463eaa06bf687780829df04b59120def1b22a915d0790e664f5d8531030243b54f4824c77418aedb58eacca8b850d5006a8943193349c3d74811c6570dc7a3b596d308d44fd2e3e64f804160fc2f797c5c15ff4fe1083a4861cdc50e0f07b331b4f32e15a5aabc617c74eea170c6214b2786876d670a5e989e4c006f027c8bf25459e7cd40d2f9834a2eec52c3dae4b933fe39703e8b69ab46c871a2029b96c2e758cda159248b93543dd85e3efa10a003f5bed993532f43dc0179cb041e5ef9cb37a0a5f10dcdc255523228cde8d3d3636d93f906e302db718c415e07 +generate_ring_signature 05b70ec90350d0aacf2b9be53ea0a7e0912c456b4e0fe7f3efa8c7fabb1de215 db3f8c06eef1a728af728ce5e028068dbca392543d43b30e3408e70a5301e4bc 19 fd5ead415e17536a9a92d5d8063001eeeaf376a4408d3e4d3f3351200f2fcb0e 05fcda741a631937b631b745ef0451150d9442798aa28288c6a2f7c02d12b08b 04bca31da70f07d8e34aa70087d78dc097c478267db53798b1a4003ca02d9e1b 8001d9b613fc98a7a3fbeaa7382d71a02f566f02e1281abfb83888e3c83c6825 36b1a72a97fe92fff3d983c734cdbd2187f34763c98e85e9bd42f90541aae212 11d585e09e6cf199be4e13c2bac0b996a4addaad8b1451e5b0afe18b53cd941a 766650cb51e01a611c1724099e9999c60634e1a3cb36295c58ed35d4d8ce6860 8151bbc0a9b80790b44e6ce4542749de5e5b91171f0240b2a029d27e51c56836 a84a16dfad85aae7dc4f86f3576d396cf2867961b129eefba1c40a9b87895f0e eeb46b381844257d051435756aeb6e235b92545095136c1db72d84edb64fef7f 78ca044b9a1ef85d91d558d71046768bb5ca5e4cf1a97a693a51e25c3590d514 953c11498b63f8cbcd143d0515aba347c20c61fe47168e7c862466ebe875908d f11936b0f1dc77561fda254ce5ea7998bb1c00f4e3e69e6c63b0795d99961d93 5de09f7a41ebe987ea155d42edd7283ce1548191db18aede9bf0ade990936baf 26d83642ee3546f8334921be1fe4c766739b3e92dd8a29894e1e348bb6fb428b 48a98894200a4b25f10741c42195b8700bcb390b790ba0e01000d3477b36549a 4bf26521935253ad92ba75a2cc88b931d82b3c79fef8e5971f228e34168ada07 6a5cd2c7a32cecd50514eb46046ad996bcb2d1eda668a7f82ea105756cac2b08 63b28fc379bba0a51da8d41d1d89ab5cd2d5e514fd7ce0830963bbadbbbb236a 15af32d28b4350f74faba181075479bb3c5527f1ed7172c00ac28b703127b307 8 0c305769b45a1959b81ca8854ae7db67e702a533e9d76053381696462b0a8303a4c9db1137fe5cc1b444e04d3a82a3f1d5b7fade44c00dde807f54b2c1298b0b0da9dc829b9c7798e10688bc8554c1bc148c67f53d10eb956d9e9f7ef1fea50e8714a884d446f8f1802d8defd2f111202dfe8b9fa577a834f611795c14cfc908d0e6c3831d3edabb06228742c3ad6d314926f2eb73a5efc9cb396b98a2b3bb0344cfe7779f78f142888510b4b5768fb7d76d34e782f9b8ed43a30460d9c2720922ee2b557e15c6ed4782306930181901075dc6c15ab0ea994af85c854145c00ae848bd4e98e46920b99512ddaf48d6476d59d199180f389c57a08eaad39bb009568daa0b4a6a7998f0ff6675f207309db1d30a565bf50c03b4063c3ae0319601faeb5b69d5b7cb0f5cb7cd7b7de440538651286be6825d087270759649b2e50260c794e1f45bf0837934fb0c565e51d659af70a8133c0e9cc734e856758bbd0b3cffd96234a40c5fa95595d5f421c947db1054066909e409a24c13ffecbfb50e70337af56c5282ee673c0dea3af3599667482563d53f174a32a2528ea4c90c0b04c6d9d06aa373e0efc6d4eade376aa52da5119410b62505da31be7e37954805ca39989ecaae27e8f36fcadd566acdfb8fb28e0bf8b728a8b16bb7c0fd5186079b6c7ea2d57dd12cf0e7d2377ca1bd3188a96f163b05e1a4c7f2a9a648f5f204bf6394b3e9e3e8a3da1c38b0c81678b210c2186817720e503572317600acf0018fd58c52ef94cccccb47de1808925ae0cbf6884d886a46508a7936d951015e0fe7fa9c30c0d623f858d4c009c9454929cd853c62ee265186513c3524457b1b035b3a6d1ca267be5a651b7024306c525c27d298985280d8137af5cc583d59e50980846774b9977597328e2e565d65a206be18ed5b4fabcd32f830986ea5b806079969bc84d4ae99a7a5821ac2f0fa11f045de8295765ba588cb38eecd98b35e046b51a9c9a7865574cbe1911d04ddccf73c410265f21efb25883bf2ff99860e0805f26a7c36026ed5fbdfa85faed307b08e05c85bd1db3811b4838ed24882ea001a647b6c993b623181b2cd84414433932091252ffac639edafe643e05ffd2a0f2fb4632a5dc478e705b1b0d6248067e9face7cee3eae57c0952ae5d895fc19048d989393e3fd9c83440127e5fac6bed1e629289fa4f290f1abc711dc9f74e203508cb5427d5f283618ef311364245897092a8369be7550d4064ed8bd283d630690f25e786f726a96bfee1a7d062c7086a5ff2d38e01a863c44c8a2b3d88bca019c3e5c6e8399f5f58eac9890ff36dc343cfd89d59aa0e0bed2faa718a988bb040e5bb8c301f30b9b26ce46974ce6c6fdb0679eed96abf6c46d60fddb4cd37807376c5ec9e6d541d11d3d91eb341ac86256fac22f59ec73ac87306b5d6b1f9c0c0b44f4a886f0c929d79186e9c38ef0350bf15987a98584521b17fc7d89f0770c7c787ab6492c292fbb25bc3c097c844ff40c5089c9ab1e014660b1055022500097e42dbca4eac4c90596fea1b1201a1053611d088f3a2e524d53705365911e016cc3303b6aee6ade52cc6f88f54a8d103945a2e17250cf49d252c34a7c00a9003842fbfe5a664d518aabf0c8147c870d8fc0b70b4ab432cf968398d52494be022fb016dace2e2520fb832491e793a19ba8c7da4b4bfed3c1f19f86e2dba44b01 +generate_ring_signature 8e7b0209eaf2cb145700a9519b958e08189855775468f752b72dd90602caebf8 e128395e02c5766dfe8e16fa00f544a0f7e6cb5e698dc00de3683278a33a9d11 1 702d51626defaab76f436ecbbefb7a1bcd953735c1b585149a4e6ae7c87ddfec 9caeec6bceb4ad72ca57dbf1562d53ea0a93feb6a69236f8166f061d93f2c901 0 564878b94e45a813bc05a1d5ab2980eb3e2aeb75e38207b24c73e800f9780004fc4f7b4f3db98674fe192cfd3435d3342de9d391ff9e724318b8255b40c17200 +generate_ring_signature 8bd72f709b787215cd41c9f7953b0287d08f4e6a73780b558ccd03c3934b5918 8d3904d2c864c2c47ded0b25d5e6b125a902b075738deceafb17fe51df3357bf 2 ad75d42030a6426aeab0303392ce21c936df265c5557d800604aa7c3c0e390e7 7d31f2eea57234b7d6d6f09d4ff58cc4bb7d4be04d9c40fa925706466bb82f0f 807012275c28d219e1b973c1ffc62aaf43bda1b49fe3ff094879f9676253b102 1 2f9c5bf84fbfdca94340a9731223e44e03c7936d7e71314a6ecb00599d3c4e0ee15c6a4efe7f11f08220da2d758b4d444db3892674632a28934c29952a1435053eab5c3173fb6b601209b2ce1891418a298f3f87832583ea728d14eba615020a6ad221568f045d8516beb64a77fff5fec2a246f34dfad80c5ce45eeb062d420d +generate_ring_signature 21a6e77355023a1ca3e620126ee0d7f247f9a814e40125e2586bc9c6773928cb e2814a98af57ee26de7edb7da847473008e128902c90ae796a9980863d8e1df6 11 c81a625349ab970763589667eb533b4fcc76f764e525ed10673e5bae74bf508d b2143c5206e143dd966e38b3d6e785a87b631bc5dab23df88c2e357ce6753e35 44f17cfc3e461746a121975377a05cf87c6bd41ec84a480fffbf045a4b17c0d1 737dd0a57c5bbb2c5038fc3d084871aa281d087040d47b5cc36c381fb25e0620 8bd71c4407f1a7e9048cecb474fdbb527f15964bcd2f9a06b540050b372bc542 f31ddccd182e3b9530ac13eec95786b47013c9566499c20fcb8b31d6b3dd91cb 074096dcf159f4a52337d58bca2b695a95f087125676381832a4547ae6161d4b 8536caa0bb2488682c0c5eff2a2cc7723dbe1741e3ee28194228235a363e7f6a 3ad9404f37be6a07cc9de38bdc1893c172c9674cb186070af90ca0cba027035c 9cbf183bc430c397a8eea5cf733dbf92f7ce3a0365319b979dd32cfcc3285cfb 817989a15ed0e35bbaf2d62d94a2a6bb3025c795f830753679c3773a8dcf6cdb f84b370bd4c5aee8c383a1cde66c5ccdb497c2266a6b15e53d4bb6cdc0535305 0 ff2c13b3347bf3abe96cf74febc4f52f0c52daada62056ed4cbc4fab2f5be10025cb69df3f7fcd3969d94f737ff3c89febb69ba6cbf3782e8be4630580d78d0dcc92d981b05200c9eb0057484a8550be48d8be660fdd90b827c38a551b6ecc089c2d4ab9a37ebd9fed1c9656880accdcae9db7b4a183c8618a5b7b3f07a6740eb0ac7ed2276bb1980e9957da5813af0fcf3bf453338192050720ded52a56a3034155bb47c3592b977a46062d4ba1e5468d30b7223c8953bf1a3af50ce29d4d08f0654388f8735b2ae0f12cc96a7f71e1acaa12e0f2fa29a99dfb48076e549d0384e55f60d9c18e407b2cb1acf2bc34f06c7fc49fa3f6e79911bf885764a34f01a568e48ea4b09382d815a20a50574c760b99efb97a0a43665b2d81944e878d0699717339a18cf15d5059abbdeac15b86f2534048c0de4f079ab6b0a6bfad0e02ac86b85b009b38b11e865c9cb031babdc4932fdd8392425bb7b90c372250410c4f9be3b8b986da1f4b05f85604b38514bf69360f9632cc373ec1128942338d0e92413236517d8e95a65f04b8aec29c06b0345ab0f90c1876bc0b4fbf52024f04f64ff19458af3e94b8eef46ce2559c1250f0b0b12e3561d86106c284fac78d006e91430a08c5112f5067ba27fb996410cf6b3766f2c7408831f3023e96150904f4770b73eac0f83bb16f4b6b77aeaefd0dd2ed2f1511a1bd1233780dcad5490f4e3989a1f646f1555fae4561333e6cd098429523d84ecf3bf20aa27ff41a980004de4646e64263261c47aa352427122ae5164d1dcb51e24677b30e8f6bc9d90c2efd4e65f9d6ed01a9a90bfc930c21f25e3eb552439aa008577f1be28039e008c7b227d1eeffcbaf9da77057ebc1bf2e991ded6c69f29a5725a9c9bdfac4d20d1d1831136846d7ed5b5a03dadcf02b22060f2c1a07cb51eb1012dd711edfdd0f02158ac98b5e16ae4655164293bdad41a73e49fa919cb18b3540dabfaeea6006 +generate_ring_signature 267687c04961afdc4dd6108ce46e82c9c5a60a6db9b613cb071a86dea4069053 1f8b194a5dcd594d315df03b43f6e6c6d8ae58b51fcc06678c93ef532a7c2ace 1 6bddec7e07d75ea37fd6fcb93b7094d1e73a56715a904647d5b0d572a6d2413a 83e12ef8c17ad0741746e1edde68f34fc3a241e9d612df464f282e449ecc600e 0 878ea47006bf91cc6ff9c68e77a23604179854711863d909668e2d3ef936a1052b54eb8df9c2ecdb6b56e3244fe7c979e3168c8ef94de1859028317a78d52b00 +generate_ring_signature 9310255e1d6492094731550312eec4fed0f54ea5d706cb2a611434e94de2f67b a6b30c2ab8c81f1e2b826258e3800503430825f7f111a5643034ddaefb4a9fe6 40 41d5a10bee397e496d882d7dd820e3854bb222240d4fde670ec2340931bd16d4 dad6b13bc8910f762c3badfbef8a686fb787b6511eb20e7f6fbe390036b2d3a6 100588d38cca1dc9c852d2f89a1f11a246fdb955ec2f73b8d5f8ff3e915beac1 b8b3ed919d7d75574bad2a81c5057b596f62796d96acea022784cb653bd4ad0e a0e37438540bd74fca28cddf345ef304b579ca4fb60457b02a9438dab0cf09f5 e5971444bb60d3b5cf42a088f0e318d2b3c22e356f97457dbfb7362d91f866e5 a159413d63e3466dc7cf257d0e5c094d0d5f87003c9bca4a00e0898fb137a364 38af861b033f1aef2c996952cd44a0120bc4d04d179e74b2ad9d21596a9b8730 bd0a348d3280f6b986c74e6d3703729374158bf1a069701f4aba16f73254c0aa c857108270e21b98d602fef841b56f0411c12305f38ff38d14372c8718f7d2eb aecae0afc964ab34c2f5564c220cda5182896dbca21635ac81026d3c29d0feb0 7bbdfc571e773e399b569f4ff2830e8d9b518d0b5d113c66c78b7f4286a97848 d4e44123919ce55ef7753229180e82335c43c064d515bbfdd16af5459e6fce9a e9454bb7137d5562ce7fd1a5d387441792a1d252af2e7f8249da3c143e7ca5f9 f8ba4561c33a80e40c7ca29743aaa5f9ff45831ea41af4e1835af4843311b5e7 562e1b85aca51bfe7fc455fdbc0060a3107836ccb8e72f197ebbd4cae6c38240 314d5ca7078b9cf66cc9a127a84346dc7a07934357ebff1f03ebb46a71c3b29e 15899bb4e5e5f5ac515e9202d3be847d7bc4a5d2f8b10606a5c25d8deea117fd b088006581f476a6f974389367a8d751e3e77db254b44c37007512fceaaabea2 ce728b492bc3de4934263add7ec0e8b8e8a9befcbf6d303804e8d543bd00935a b18e46b791bf0f12877386efa7dbbadf980d2f090fa2e59826a8eca0417cbb8b b1367e0a7711b395a74cbce696bb16102f8bfcb550ada075fdd28ae6f91ee820 f9d2d946be5236f38240e7750defb8a2e6f9c938dabc5773e8fdd8debc50d6fd 4423c9426972d73a83f73448e6bca19ed2edf3230ea018ea9cad3b1f1d76c4bf 97522c269b46cf817c95aa9fd186b9c09f9ab2da7af3ab55bd562d3cf3b51ed4 9f9380bdfa737fa00f31bfaa185c89ddb4a431e18f4a17c12afc3233e236483f e9544769948638dd359c9fee61317f0c93e4d476c457238c7f8741d4fdd20ffe 033aede3e0d40659cef397ad6250db441c97dea5c26ae4771a9f4dbb1941cc87 a436a016cfcddaf1b1f1fd9dbbfacd6979269f0772526309cbbbf7f40fe45920 d40b3ec269a31e412505088853b395405ab22a54d2fc0b7cf8f36d3746ba3e22 5127aeb4f206ecb11b191437e41ae3ce2e8734066ac559921a470bd656d56c24 89b0e22e57e9182a1a7d02ed51a5e6811f3ac915de5164664b296033124ec6ee 5937d8236f189d12fd6cd9a2ba68df4d5ba2bf77e72ed9449bce95cc0105c974 bccdc7108b06aae759d5b4d0e7e03ae74d5dbdacb6c5ae6935289e231a1bcb7b 15b5ddbcf9014f7f31170a82731321e9cbaa1d3b1d374ac8e9baba8e301fa1fc ece21182404939af7ec56ffbc46d8a72ae114945aa53baf864c7dfb925506b47 fec8930653c39e756c9ead78a921a02bbf3cd46e48053766a3ea05efce314417 bc04ea71c8a2d6774c477819f1e2be43fb4e8990b064e6c752756cd8fecd2ded 373a4b62cacd09fd6ed9e3b8354445b8316d15c11dcf334b28c2bb7e3458e848 c81605bf680bc260a7090ccb84ffc13e1f1e78fd94c2320642a3786f3fb2bcbc 299563329c5dc60a5689618c81e1e4edbfbfe96dd877c27c30e96258ed287405 10 a8370bfce21c305439b37c109e59eca60f93ab9638d47ac0c2d1f72fe19141053d3efbed657c2f625b44d21d3390d2faf56052b71e2431583191725e902eae0a12756cc894a7341dd1ed147d7e57b9ad6daf653d921558bdb14afd0ef601330edbada0a36445fc6840ee49f39bb3ede767d76552e84ca2df0960a735f97e3c07459e774701d39363dfb3b2d108064e9c3b2be6f8d1c5cd1b13673dc4aa11c907aa116b94792f691ae1d91c860873edaba43e5180340141c0fa05012ad5e21e08eec566ff54ad190212f115478e7584c4ee7bb51c0165287f484ffd846f335307bb675c3e0bdcb3b0feae1a2b66ce58aea59fedbc7637a0e1cb8ae06314c55d0cc400dcd66c9ce4ce86685cc8acde97f62a69f516225283c4aff3db88a91cf305197f9b94edcfe8a28a4e78b14e4399564c04cffcd13d253eb700317fe6e0c8063261649161e04ae8179cb4413e0078462c2cf275683e24fd045e28a0fa505f092eac2d0c056a2366ff5c8112351b25e24d34deaf2b6a949f0d7acba177b5bc083634802751410c043f6243854921c7497d229ee0969c589c2fee4f7b3bf61203688d748e066e7caf88d71369c6a683e34a8da494e17ce743d8b159a10d7ea80ab05f4d852046c5cb70efb4aa0426d602e5dce0432656eda9863becca0f523c02fa44d25787c83770581fe7e4368a3e3a4b30160847b701631a10593634888c038b142816d58f2a7e43adab6759e7551c085a6bb3111abda63cdb21f4bbabde03c79fbeaf3ef2ca3626b21d96f7ced93fff24ea7bb0d6da1c5133e480baa44c0afc2b61f593ecddd2a22ecd7405f87798a70aa3e7f2ef617c1170f850f7d0180c95314569b0316695fa565e5f47ee7bc4d26cb691f0d78f480fdaebab3f5f6705ea128f78e6af33ac28ecc3f78a91fef6bfb926ba473eb203e1b2ca89326d160347afff7f8907fd3892380d7c9025632f83f8a5eaa128fe345bb2b850b94d530e4bb1ff9801bf4066c3233428a9f7f968cef7ef5ed9c3bee97758651005b4250385ec154a1aa38deba0def3d5088cd98b49ed9d7b689fadad356fa00eadf59a0218c9d90b4adf0ef371b4b0e46ed9ece4d7a6e2c7de2070476f871aef20bb1a08b983bf4cf20e174971ac7bd35312ee8104d7ecdd2f95d429dfc51520d90fb0059218483beb000c8506c10c7f89b0f818dd47181a52b2d09dfc3c001c9159780d0f257285050863192edc696178fbfd8aab58b01e0a93e28d472a7a64a33b8000ee37d2d8cf1697180a1829335b603d06adcf68f746074853c9644c55dd9d0c039108ff7a9bc98a6e8d4035a05f0f9011157d9172ed1353b418d34eb29bbd860abbb7cb38ef8c2d28d103ea47ec924b3a90321ccd44f948bc6d547aa4a4d68a0540a2b07205e3ee3d5a4007ec92d1e4696ee1ec735b8a567c68b8775dcfd1880adeb5694be4844d0776aad0ea3f05eda75ce3bc4a9673c18caf57cd761f95b40255de7869b3e4d33a87290ff6b647d57b9dff39ea65e8339f773d7e57295bb90f96f4f4701156eaecf778ad2a9d2813699f6f16488c073efb5b637e4e725c2d00f9f5c2040868a7705fe6b395356cdadb21ee9ebb8504f6a4730050a4672dca0cd4e3aa473ffed04bd6e13b36f6602e7f5da33b7683eb5b20de30ecd754eecf061bc1752eb0179724c1dcc45e84bd62c92d430ef2eef0f3afdf6832a4c8946304f8252eae0dc9bed1b7f971b13975459da031b81a7f68834da247660b7390ff0afe5a4b2f319201771e5d97b9c4eb01505511ef60860019ddc7168bcd3054b6014c3eb6364ecdcfbf4fe2a2e928105a4d9e0596f029f708356ebc8f7170cf2203f7994c32101ac0f20a49796c3522ee0d6c87946b0aa54eebcf66a87b411f4c0e92fbcc534837ea3755beb2338cf05651e06827206c8531524621c96eeba0fb06eb99f61d215d8e873043cc0a39dfcf4a917c94fa00515c4a60d79f8504e5140b29081e52b82cd2d9da48321c45aea6667dcaf982583dc1f46cfa052fff911003f2be27cf931a1890f9484d8044bc65e4ec9386c24ee17e95c00512aa9b1e40048302e62066d4f64364f8c03f783a3c21f37fe8e6838d3ed31c90df360fee930710453502ff90f05fbfbb3d09815b086c082b385ac0affda9a2b10b670fde6f0bda34038f56622c38ef7cb0ddf88c1a62960cb344df5ddbc4e9e7c8a4a7669d07eee0492dbb5b9b3fd1fa48047e24974c6991bd2a98791a340a0effcd42cb8a0dca0cf4a41f2a8b7fd62b8015ca06f2eb68d2c9671b0a59cc9c6a78814397c0022fb2f61948dc917375ef30ebf8f8c8d65dcc9b744c68e20c0504d294ee1093060fa8adde244a30a558a8f48fcbb0d928ff8cf446315ccb460159fd1b53dad90a59bab26b208f99019054952c65d4c70e80d58129034b01697e7dbb72345a310840143dc46d5f19b4bb14692e852edfe2b0aec265ea67dc08a7eda3c01d05fd0d3b05f28eb2dad0194ead51efc141a77be97135f38406e932ab1e3257b731000adaca02e1faf3bb7e5501fc7b327df9af47925e33de27a5179b60e4338ac8d505ee41ac311b80df97716990e121b9f8e1433f341cc70d086bdd781a527b1e870bf27d43c38485aae5757903b479de59124cd255323f0a549f64a78a5f23969809deb5daa333afec4042da25df55f5d132dc2a7f941738c7fd196dfcfa76db1a0bb9216ab4d6f366f9c68e17153e36076f9ca8c5084343b5925e19af128ccb210e4292fe29190c5ad23d35352704e0c1de097a60914e1958253dc885aac2e4b108906b103f38ea55ba036a60595e09213237211181bfa4b3e002fef08749b1740f516f1226477565830de455436ef4ba2b15af376ccb0a8d090d2204780b30da01f728ca300c5c07af81422a25ec86946630cd61a2ed574e9b42f11720e937b3037e6154a3d38f08c400d519756360142750accc313f0ca989c747a8488c17fc0cf2603c61a31311e83843ef53f9cad5034b35e19328db0b2c9338dc61afb340031fca9bd320a3d5cce21e300aa4510198d84ff85a4d211799b16d021a8f4e1f02f345ad7fc08566bfe20edf13f4b113107b20a78cca1bb99c70f06b8304f6bd088e2b380418007535e18173c1c7e85ddf5d5816f23c36480f4607cf4e0881a409b81d11f310ae7aefa24ffd06ba45f812043b76fde0887d256452bb8d6822740540918ecb1c669e373ebd518a40e8ccc6e2772657a03dff6ac7d6b9dbc42aa00b6c294cdb557cf06ca1962f365d5d94706cac4e9ae3a5dbd0c653f356a753330f4f75e995e211fe14583db569569ad5ed133d1f2df69e34b83e5b25be881da507a0c65f5fdfe4db0b964038cebc1fe9aaaec0e9df0225743a1e1354152cd5590e04f1014158d4048db661a701241793f54debd8693d671cb9d09b883762e4d20e5f0e9f72136447d2205bd1e465b29dfd37664a5b4b3bf3803f6076487352a30e69c9371c8bc8611f446145ea9163a9810bc63abbf8d6b3548fd2ebc3ba19650b3528eec70b1c92e39124272b41a3b023e87fe10826154668f6566950341c4c0ae5c00cf4a94441321a7c3acf5a6058ec455f299acf966942bfe74cbe75e5d207 +generate_ring_signature 657d817c2d4d9a393b1d9eda25141349f6daf11cc4d6fd7b8a1f279c12187e46 e8c62926823b84157dcb6145174f85084425a5c705ed0d37842430352451a394 1 6ca123601f7636c3372154cbba220818162efea295240761066b059d12148b96 eeb5e6852d87a410c402eae49f12242b35702470b648824c817adb253d119704 0 83c370f6cbef1de5f0dff9b17c3b7a4a7707e5e5269f5407f18d9bac4cfeab0c5192830362e58681439839d0b6fba76a01df1668997160e63e07a265d6dcca01 +generate_ring_signature d2ee8055b5e9fd86cd9e9cdeb95450590ea7a48e976337e8af699593d15d6232 cd23327ec509965ac07ac7f31509833e47fc56b96dda646223d917c02c292ba6 1 ae47e38a341217de4e0562f9b146299c0f7cdb971d314ace7522fd4fa3e20fb0 8c4730239e8f96e53ec2e30b856645506887527e7b6ea24fbff166a727a74007 0 20e08c6ef9a3ac8b90ede10ea7c833ecb39c0efef679d716c1a91ce5854bd1064600f4fbce02b45c9d3b69124f435739690b202ac327b2b034d51a430293ad09 +generate_ring_signature 5f6aaa96065ebdadb91cfc017ad7e81c1052623a75dc979d052c43d025572d46 e96c5739f208695639dd5120c89672abd6148f185164bbf7e17c643f2441ed18 1 71b0a7ac6902754f376699e4526f3c9882787ef2ec7477dc2cca2b5d6791af28 0dacdc8c2bab9aa153b91ff96e7dcdd6736e8829632e92506efa068500ab4b04 0 d247b01cd1efbb2f680f876852f349370c3fe8731e15fec25664f55b48e56b099f860dbc8a7f8cf406cfb94e9d2454a265e1fa9993ad2aa7abc32021c54f3909 +generate_ring_signature 210c782e19ba6264cb91745cb5cda5ce13b234ed030561310e88f6dc2722b483 b439238aacc9b192a9d7d1b73f845d24f574997d88c949d54d372549419baa7f 225 385d632938755662d282c477ad5bfb19bb1d153e5815077216a4c4e2b394e211 5e180c966833b87b3003a824abf2c6966441760258c201f7002e823306a9c2c3 5d040395bd315a7bde66937473a7d96ea72829d2f2f868258edb4cb265aec30d b5f068ea2e517abcd21e927c7ea996c5974cb5c681805ceeb348244594a342b3 327d212984e2e84026317ccd150142a955be2dfe34d19e194a4357bdd221a9bc 752baf4d17686fa71a2534a6864477b608ded4d869f847586fc6ff4e4d266255 05c39810cc10183513ab20ae2caab4448a0429f50673b239286c2843b43a9aa8 f60a2de08e5a77b7e56777d6ef84a5b6b546e6934cfc3a635378c9af0b7e2f1b 9986783276b0dd845c3b0c56ecda1b29b28bd7a3ea155ae6fab4376012a40180 0b3b7b391bb34851c1720cea7925614177f278deb37b16d52197fe8aa45118be 9d41a41006737c046947fc176183ee7baf4c37114479616e89d7517b9d039a56 d057fc6060c31d7629edc250791ea60c9f49bc5ca3872c6146030bc08ceec217 64bfe3083a136ae58ba33130ffd58a60b338245e368db66335d4844bbb0e25a4 4b7b5ae9dc755559da17de7325ad60d80b4f442bf508e304d2dfdfd9a3853a5e 2a5d589b910884a9267458678333e8a3818a71a45005a7aa3a9e088ebfcb88b3 eebb3f88b3f7951e243d7d8db9003edca9f0d9001244476efd46074fbd904fd7 a9ae02114f76adcf94ad23a9079337f1d1edfd74fff2438ddb47d0cabc1f5af3 db9edc21464cb1d6a8001bbb2933214b5ed3a2824935d63c05a55bc5b681953b 5afb033005752c35a5e1fcc95d2341640f532d11107b3a54c212b610845db203 8ec392866d432fa11d21b4293ad1ebd421fa859ef7c5311eec1acf73da95f366 2d372298a9b62e508013d83cbf0d95ffd72fc8fc6fb0d623db98a47e30640eb9 7d13621c703d7bbe44a196819e192422a9211e53a8a6ee138d99f541c7c45bbb 00a26e1fbae064a1c2c800ee32ff1bec58ea68e6bc3d9319d0e6f7d9d4858943 b3e674609b98360bbb85e1f9b80b779453ae635c894adda35bd100e7c6b1879c b9fd8cbae04c271e2e59702508d371c15f180310b6b5b16bf78f92c30cdf3451 c7d7b20e3bac8ac1a104ce0015b29d6ca9f66e4b921f3b7760d14b0d0ef806bd 50771023cdca6100850cdd2f60cabab98e9e42e6de099a1f399b898efc79321a 736e41ab6937e217c5ecaaf37eb7bb7136e8f9e29089c8be9567e8ec9bc76002 52cddd12cdf1c506e78e29591392fc24e7be5b9c5b6136ac2ecff15a94fe0768 132e70b68a7ef9c5f401b812458745e73c3b1143fbd6d548266d446e8ed21800 a5c2587a5b911408527f894750bc2fd3ad05d88fea96eb3fc521ce217cea614b c36d3589279a9754ed703a49e284243064bb8201823bb1e6c65ce1f1cefeb3a1 f352384e3ee128d4d9f5e53677e193699a4a0b32b87b494abbe9affe461c8224 623cb978cbe7068f3f5573abdbf77acb50f1fa21456d6c426ee7d14d925cee2c fe4eaf7646a7d7edc86ffc7bb3e02ba1bddac47517eb676a73c8cba574ac7583 74564c30fe7b0bed8908ed7550c7a1c0e81dcf9972c5588223a5c31f7c5f24f4 3957b9c95219d16df5af553548404c6ed013feaaaf3311ccc19767979f81afac 52e16912389caae211a4ca91048df9d62541505cae921b1adecd1c92a5e3106f 9aeb3d3ba84f8209795c9fe000a920d7b58e361c2ad09856e4b385cf1d3fabcf 52e159e9ed060899121461c71906a83f47c1337db4d487590c1d7b742e23e29d f4042896920a3546282ce109658cf0f1535d98aec685a54bb653bbb52f71f560 81581d762fda656949b3c072d157a16cbc96e27f73410fed70cc2d816ab76632 01d2d48e6e5599d24534f3482427eb4501508b8c1068583f7354074462aacbfb 323f4706c986053428d87e580cb45426eb38eee25fb956345607f71f76e2af64 c48ae1b3e86ab129abbf82123f987b773cda8a621313d5e3e12a82504aa4b4a2 764a1d46d895c15e2de0d8953d4a349c11ce203bd8fd5853c7acb497040e818e e59cb3843b69071cc6a876a73433694f55ad77999c1f2d9840cad6f4d46cddf0 f0c7fa7f51616a2b8b3d3c66fcd1b999ce236223d899a24d21d7cf51c7f8320b cf103bbd9665692f9ebb884207be5d48260d16cc118184ddeebaf8db303372d5 718247883127bebcf88b3c0aa856d2c82f205f0ad882cb40fc308f011d4c6ed9 12d01937773a71a29ddc4fcbe01005e7f3736b25414dbeb69f1f15504d65cd2b 8570f352e0cce7b04f9a415cbf78ea49b22c6dd1e199d2bf2a36eb7863b52633 72f0a2aaa9927f98bb985d97aec6cc91caa36f06ae37fd49e717f3c465943bf6 955bf1291296b84cc7f1a01a9d38145a747cac1f901739405cc358c1a791543c fdc3c3ca79c46bbd3226776dc9ed47e1ff3f3a7748860b9894812132f0290cce 5277b50bcf94ad33066eaa0d94885009bb6a2ccecc8748ab5d2b053d1c4bc8de c82775ef48623297b0ff928b04c03b1502645d58ed9c111478bb9c158a6245d7 88cd50b04d60e049714273798db1af00d5106280849efed1a9c1e37ffd97e253 d77f732b38cb637343ec20a66da97c5b39c9c3e8ff4b84a444e3d32164612791 cab9c3f461b91e213008d896bba641ad24993f98d1c7c872626c00a8b7c001ca 4c62c3d7c18c0b5f240c56b6f20e1ca4eca30b7d8806f66aa43f5df8a8d382ac a70644c6f3c2cabee8ad982a2994c09e81962abdc94f0fe48a5de7b155941de9 3ca3119e94ef5a78db0e52495b37ee42d3d87917cdee4cbf8ff53989939b626e 0df77c493d22bd419c3db7ee8f464acaeb6d83d607a7bd5a6083a62c1b318f94 011fdb5a7173b0924e03210b8ce3d0bf52a5982574faf3b110739d0288e5da3b 3361b6da469b5eacd5a5699ec7474c764d1426ae5caab18133252b5d186c1f41 eb68f27174f1dd2d342ea6be42eb3635fdd597031142086cef689f143e149faf 1fd91c95a7eb4552eec9f60fa61d3f904ae3c8799fa1a80926dea02cb8b69fcc 2fdce58215cfcd09250e3830dd355ef53090b5f8ea6c65ea7db805d720ead1a2 b1678ccc6288b290116e3d8341eb32e9bdc0e7c4bf1872afee58eb9430f82c45 48eb663ff455d22f43f0b6cb27f7d5eb0943d4b60b683717198002d7b76bf4c3 2423895ba50880058a75cbbdf5a535ce176ec31a14ab32d9de4dc3855156b8b9 06043986a58cfa6a7fa0668668b39f297b00dc2aa7470bc9608021165442b15a b881c686c1b423b1c36eca6246e5bd5d0dbf2f1bcba8462a008ca7dad7b8ce9d 1222668bc17ed0be0cd5f58172d8c058cf3869d26b201b850e46e2d1ba6528d1 1bfa0e56cb48d57372b35238c12746ee52345d52526ebabc79bdc28ac559f1ac 0dbd0538a75a12d385837b24a7948bfb3e7b9ec2e10b05516396041628e18a30 99910ae1888e67a0de161a454574c19c1f008f1c8559204e575db88dc80f1a94 1f000d2ec665610f784536730adf1b3e5b9b9db13a42165d9544acb881b0b92c d4970792197ccc02b043555225c505be8495eaf84b6a70d55139dda4c63b20fb e4601ad66a30cd9bf6e8a7291282a91731c303b459843c73d6b3538bfc3656ef 256a7723b4f9b7d7f652579beb53a1de9e7bee8e75833cf73b259452783f3251 613669fc70b3044796ae69b20a3fb3ad32f2e0e64d789a00dd880ec974e6afe3 0a9baa39c17da06593fefecc921b5a56b08e41dfae48234f0fc23d8f6a376ac3 8fed0f0e6b4d42105fff2fa2f5d17c1a29959a58edb4fa15f137f0676c6ea7be 630f0d0f9a93c58f56d9cd2f6ffd88e32588b5a2ceb0e1888429d3a38db25ce0 7e12a83617a864086ecf6b8c440f108ecbffc96bcbc8df5f3eb3c72cbd5b4e12 d267956f1075d0755c7f76b498b63e7f51c6e1c4902067d3be637346db102f1d d2e828db06faf7dd49e7e5223c21231a3b4cc4c3342cd71a3a92f06d727214de ce057552eb966328262a82f0106561632761a1872a5348c00e2fea537a946a4a e44b42268cb4127b29e47f5cc6499e67b618afd48462eaccc13477423abebca8 f9f0712fed976ab4091ac0dc3c15b3c2dee4bd54eda3e08c6dd2f9a4e4acdaa1 0e65a84589f21ec3b9ab6aee1d24ac1be5cbf7734450bef6f523b1052601341a 12fee1a3b1c49e1bea073a7bfb93c8879582febb9c28354ed5ce0268077fd176 4bc6cd65e4451fae3817ae3210d8a59303520d0eb1fe4c5d5e43d8466dd784d5 b96717f2b1ff0ae2dde66343dc8e00268366d0e8688337f6ab7d18508968ac8e e095f6be7cfbd4143c065b3f74abd9efac0552e91bd66c50fafb075534539ecc 750c8f66ef049a2f66dc488f51a125873b97569c692b9c31040bd4da3759d5ad 7612ac603bee6381390e5a20fff937b2532f880a0e51671f2641b67d58135f60 ebf737d8044309797fb792a059b02311943c12876d3a5c1384166ee2b717820f 80849e5e6d4f4415d77a52dcd6fa289881920aee19735d03fcf5979c2b001993 c97589e7bce4464b1f1921265e8ec6af29040970f09f2c3475487e7ecf1d489d 9c407322aac4ed8983ca8b8dad19b211f0c0770195119157fe44f1bfda932ed9 31fbe13706f2716dc25b149e77da52c1ecc55b70053e41e8bd92221b1b385783 b6ff14454cad1917030df735d2c9220b40880d7fb0ed08841e9e705eedf9036a 06afd0020bd36ca861f8202223febfb21eb5c7e338c907a4ce654e457cb2a315 be6f0907e14047eb8fbfdd1f878550770c6fc2630832b81948927b6f80617372 163f776a0f464f59a6fa6bbbd7e93aca17ab04b04b7ce06d34226bb7e900d7e7 f1550dd1d473f38ec080a6dc9872442350179aa80ebca3afe3286c61b1ab732a 38610d484a664c9cced181b42bb7a96a076921437de67f6be1c922e07ca7ca25 5990aa4aa00146e517e50f3eb04336597f2f8deab18ca1439ee5a90863d4a72a 50d7699edcd2730a75ba37516553f0548e2abb5044ea89cac8851026aa73221f aac4a77bad6ea4d5dab8b739c0a01c9fe78dac1fec94fdfb1168bd5cd59798a9 95022a31b8a904b2ad50d8f60e7779fbed16e47c07b1ea65c19a13ad8ada2291 439ad6b57a6dc6b7dbe15edaf44d7582f0d6b316ff823d41cf85ab381d35b860 38bfbeae4653dd4124de9a1a3e53bfa3066d4e5961412d6c1de12cf77d27fbbe 55992a2b6cb34982966bd89eae451b39c701e79de02d7f3440a8c493a51d9d9c 4b44ea04e75b073a4d597f4d1fdd414179bb7bc5b8ee9001a3c3313eb7a81028 3bd990d568b7f23824d36b441004334bc7d88d6552e1b1b9a6f772ccd02af8cc c6c13ff416fe823ac2ebdd4d8c96c9dcdeb678ce832218eaecaac26d227aeb93 384e2915e3003d1e5a32fe42c165eb31ce1cf2f7972ab7080ff9dc95146cb44c fa79bb9d556b1063924ff37f150aecc8590ebf234550f4c90135fd301010d020 a06676590cf910a41cbe07a80e3711ecea47d43343eeefb106027aeb81f11fc1 359028b96cf085cdd4156f29c48aacfcab8974c51e31d1920648d844c48503b9 f4fe5c34eef748c3c9a3dde8eb3a7cf88f894f7f29275ea11ad9d21c2da7487a 7af8f1af63e71d95ea3c26f57a9a3818945cde9f1f70c77678e501a6ed2c5b53 df5347afdd221d101381ca39489ae4a40c79e60dfe914640b7c9a15695bde31d 9d68f3e4e218ed9d94181094ec24d78d8e5e74462edf23a04e0f5c309a488ea4 f69e67c58bf7d1f54daff66bcb9f967f4a800bedde5c1e8e3388594d2b6162e1 4e522383f361282994b5df8bec583c9076915963c387cd8fb79c5a143b77b20f 649473a9ea78b524cd53b328a181eb40c0cc67f93545baed9fb9d013a218829e d3dc41dd315b7390789ae65cef880d5b87f4b6f1806877f4828313e8842fcd02 2f14539f942ee6e9b0f10fcedffc6c2928737cdefb8ec12d1100296ceb74d56b 9dcb4fea73d041a83daf21a9cf9cecc054749ce61b55161cea8533d0ffcfd66a 8683d13dab4da5823053c4623a860b0f30990ee197a528780a276f81c70b639b 700509bd43a4d2b1cbe027b952587f67bf07617ef194f1d182718bb1761ea6d2 41a1e9fda04748ddd5fec36d1178167776ad5fad58ca7ae08c83ca4fba14897f add8a8ea5b584b8eab15afe856925109b90a73f21700ebe6be92064968ba8766 e6db06fece8a3c69c8345ebcd3a5448348e3ba7b3fd15a05f2eea0ee45783d5d d4c104b6d17052fc549dcc764684c848cd7aa470cd478752b814010cfe8c31d4 e36fc9e9c235d35b997678b41b2702b931498e32d22911d3c47aea55503b0033 c46fd9dc260f6d25ceed78280b2d5bbc08657efa5f7b962e30e22ed3893f768f eeb7a4cde0a142ea63cb9580b0938366b3b95324d18b17da2be4d2249106d3ff e4987600c30314a65d4ba41c132d4aaa6df9bfdf43c9f6edb4aee71afb3b1ca9 26c450378bd5de84dace9354dc11164c0df30a66c2623ffc9821e9ba96573474 3768619c48bbe89a1929c7d18f71dc321ae9147c29e1e9823e15389af035f1fb 65c1b6c65fab69cf2026b344b5b0cf7e312d8554c8ca9fae97b87edcdea5f945 79be69b0f629b18930c38f588b76f86a2c91dd5f8ff77265c58004639539767c f5c4443cae1a272c3ea900331bc349b72f7537e4ebc4ec9995a5388ef922731c b71e544eaa0ba427d75dfa2bbbe03694da48ef2be247833d143f81812c7b5a47 b7b2457939c77d5ea8ffd71ea933e4efe986ca8b455e53e0ae148b4ebc4d48e0 10fca1960f5ef371ad93ba33f223b7d2851ec0c9c76f70b2d629d6581368f90d 988f89d0727350eb720066e198c14229bd9c6b30a4313419df9ed31503ddb02c 2f81e060a1c1d035ff2cd062ff432329b6b16dd5e5ccd3ba1e3c64907ca41351 335def56d9aadb5e550a9a87df2c4171ddedc3257cebd96a88ba2ed0b13f7301 1bfe59272dc577a9821aa956551cb1f045c67d4b9228a04e0b4449a8b462ff0f 444cb383eb3f041882c5ece5a3ba472f6cc4586f101c63050d8ca8e4ccd0106f 39c3b97d1ffcca4f7d0560f49f5d913f67a3903789743d29b49b902b897774c2 fae6d7e20a850723d9764e97e3856dd3863329e66705d8d0ee2b9889aa70c327 1a095ec877ccfd6ad7c098273feb65d098e1f573626e57de6ae4dc61784c9042 d562adedb07334daa8ef9cbbefaf98d64b24c9741dd72445a9e120e183a63c6d e80223440a373cda0d6adad064bc2148b320113d7406e1eada98f1de93472b0e d15f5a5ad17f8a3ba30847d4e85424da0114eb96dd2eff269cccb4b3cc364591 48707fd6560c4cf9cc8222491d45810fe75673ebe2a7de749e9dac85242c1104 8963b85abad1a6a24eed9b63dd46902766eb8b0dc13d02b9e80e3ed5dbfe6511 49d0fe1c91a4037348cd6f6b8c2bddb8da46214c33e8c39c0630483b83305ab6 f803ac0ae722d368b352c47dfae3852904bdfe9bc7d4642e1126a482d5e9eb72 1084f0fbc396d6ac455df3ca4fdc22a24b8d1e00b3f904ffcb6d2fab8729590e 4314465a59abb470b1e05dd90925da4035ba6aa20ea102c56b6365865cd05a9e bdb6292729ed47f044cdc357e7081b249a275ba904149ce9719b57dcc144269b 033d157a3b91aead40b8a4f029b61f2a40d39b86ff38e8e57b52c03c426dd10f 138d03ed71356e08e6ea8d29c57dd8b2802054587a058c80773a75bf74bfe371 397cfd01c6fe89a34b41873338fbb70b4dfbe8a954d895e88cc87ab2cbcd7686 292e2bc17fee6d9bc8f09c710411605ba57f8534063ee3608ef39bdf73ec4db7 8150e00b1e5b68586aff8612515be21fc803344d3d0329badb0cdc99b80a1362 c92a75c221dde7e996ae5f8ea43a5760563a0532dcc6360a60dcb0c7abc13f75 49bebc69b650aeb8ad9b8382ecdd65a74d640153ee047060db71536e5166e58d c03564454188c4a7c29c94b4d128246140bad2a621e400137c7c8566c3ed98a3 75ccedb4415bd43430e80668831b2f667bcb086778ca43805d9b67ebceb0138e 47a95d5a7fa6e64dfd42fc92f103795e8bb26d8548cda156075d528f4ea79c37 ddb756c6e856bf99b3cda839a95509f50a9b18b87c8d9e07395eb1f37f351004 d813a9adfed2a92f4d2a386acba181fd12d26e8cd139e67e39bb1a45775b3f8f 36c6a368a17becb5b42b43855f1a1ec9b958077077a9ae8b2b6186854f2d7b96 5698732e696ef2306c582231ff9d48f919f4f5d59bf11df3e2bc7a9511a8555f e0f7dd725ddf4ca25c9bc32f1d6b33a562325573cb3bd9f9bdd03eb15a568bc7 6ecd766abcb6ddf79fbc0b7b510d7be17bf5497fe57e54fbab5f44b59522eff9 29ccff9bd63867d4bdd3d99c79bc54b9503d3312ce4792f280da7435e22c773f cee276d9b36560b5078d93780431258456ad73e47b98741c4d885b7e917a620d c465fbc35b063492301a77e868a24728a238335f1eaee5e7c94173fd1d5c2bbd b8a805da943c09244669e79bb62d6a58e80e0cdf069445905b332e0ff2e0a4b9 081c277c8956e574ce8315e55a4d1bc6a1f7084e3ef79c565c5c920e09ccf7d7 62a0c62da5b16a818926f5568bdb97370612c34391831bb8fe60ddef00333e80 e98bb0d8e342ff5ec1041a49101a848ca973b3143373404be9a6c2045244abc6 4667b53e81243320173ad6bc9564fc5249fc89410c70b915602a8c164529342c 26c435b82bd68d0b7ce15e81bc391b2b60b554be5867e72a9566a101e27ea9e1 9587ffe6e571e2e36fe80649da50c303ce81f852ccaf2b2e9b7b4664260c00c9 3b28089761856fa94678ff62ef7f5ff7bbdeba5ab033f2bd71ba5850bccdea9a 5d92b8261d4196ba81ffef7859087b51a05ae5121554181cc687a4a9490699d6 297f21664140a48c0cb3fd6e6581ff9acafceb152c76fc4bc9d31d946e2aa54d 87d4a74090844bd351a393e7c9fcd7fdde317947281a8680ef950e5b71cbf8f7 1435d4678846561678bf9d5600b2a787b6339d4ea3b8187a47ab5c6541817741 a8ddb8e7b2ad56a9d46dfda69e17472e610b68af71a3f7ea908c9bd69d68ac62 d8fb6e4733f9fb707f77994667f77e4e3ca818bf8a6af8120e50ddb02ee65f83 8c34552da56de382f71218a665199132e665a1aacf11cdfbc77e5ffaee59c61e 098d1aceb9cc5ef48ba98dd731a3440ee771b9ba272a2c3f07cccdd192f76661 eec48d281e1ced0a49fe3259dd5b4834e22da58794495cb12b14c50f42f352ad 01bf24b8be84788a89206619db1cce831a71cdc4fe0b51c61d0d93144557c19f 53559b4a639f13b09851a396796fc6ca68211f3ecc2a00a687dc3a526bc1efcc efd8b29f481a043c230077c3eb0afa01d99835eb6c5432bfb855b4edfe57b43d 69cc325592eee89f8c1219ae28e35e515f167491b1a1a99987fc1091e3d7ec8f ee3e949a0957a440e8dcf6401c7483a97e78c90248ebb05bf3fa2259fa3b767d bda559c3325659ebeb39078896a9076f66ebdb69b95d21477d422bf892c81c0c 435a8f4867263036f7af465acc3c052161c75a6e983e8ada56e5a88fe04b4a6d 2fdfe818559614cbdc8428e814aef218181d888820c66e8e92b3d0e038b7d971 9f7a9fb7d041cb442104b1042a85085541b4e19332f837a6bf8200ecce1e4e70 97b37dd1b11cbd70c90f0c80fc8c4ae8ee7dffb1ddb1a9b89fbbf94a53152516 238255be5a20745de3541e9e269b10bdc886707eb44e6b7677ad29f57c2e5380 18203e567a0bc5b74c253fe7f3f55632d245f17e1acdd0efbd806129e2c57ce1 183149a1e04a061ccd380488268820b73083007d21f9f09ed559d49b2ed9cbe5 2dc198fb09f54ff3988d9c90d94238ddc96197722d71cb1793164eb352ff4a5d 45cbacaea04505e21430785d1b66aa47eddb172a078a578fb976b018b3902743 98938e4ccb446cfe999b091234c844e2a6648de95ea4b7408320b1b37a0157a9 d6b18d00da6c1f1585aaecfb15f8913fa65cf12466dd5ebc55fa84b28cf39e2a 56fda2b69037627ee3958547c0eafc7f376c0941ee1dbb2dd5885b63727392c0 b3538a6ac0908d7f225b58f2185a1735e2f0013d66a5fc5c7371158fe97b1005 4f4ea02eb4ab62fdfd94367557069356661e2d2032e9ad94b04e9ff3a7c44a01 204 a905229f448bcbd6b4df58e365ef53b3116e842f5d51de1367a74e64d9d1320c2598b8ec8a28210d5ba475d17f7abbcfde3d04b3e81dae851e32ec9a7cec2c0620ecb02a2f1c3067199071a533cb967809e4fbedf147c2a38c82f5c40193250af758eb6cc4b732d92ef6cae9c7e4d3f733ab49f89fe923be0c3a8cc8cbaacc08a4c1ed0f185f6d66a8e6e7438536e9fec766a89d8ca89e16e14c58ef9f25220e43436f539a30ac553a50a81cb2d87e832dbef46467eb62049e7f80e87ec0a208ecbecc910f6ba99e929096293accfb020b843cf006737d0c69e6b4bf10b17e07f68bad5fe751a7e6c015d46c042eb0f100e6df524d05b2f123a3cad2ff203009b7f7cb2d44bfa4951ab3010bf4b5db4d377e33573e04abdb06de0511e2ea6e096ffd9cf62314fd6a88382e2e0466ce91d929614fe0ca6b64aee977618e65ff09fa70d7deca54c729c009ea73105b9e6475017f680cb34bb5f2a255f868400c0193c376f7ba24e556641cdcd64afdfaa6f3f0d378903f2f841b320b85fce9960abf3c360a659951a5afbd16fa9d4a0fee00534831f5ecddef5788bbf260936602d88f893ae8a8165ba80fcfabb0e29338e077b7f71e2ad8c29dc02416608f790667966454f49f775500f05b3a5321bfc2e7c6f06474b50cc565940becf78796058e20ed7ff0b0d721dbc3bd3e4bd71ac6b7db831e4bee48762f9081de1603280b7027e9364c446689540f1dd3e7d0ea034e7e76357468dfae80b4cc2b353bae0d2ddf35ebf0252f281cfd23e61d6b289508ee40ce1ce453c2e1e2cc9e3a580d08978a2c48997703991a3922f68d36a147c817fd9d6eab228aa1509b4b850de207afa40fd1d99148dc62b44678d959154478b5cff99a6cb4208c65f3d514a1f9078c2415df86a1af7d953a31f97f7ef99bb9bf43a8a2c1a2d553f4b016a694410c2d13b0c04f6aee390c4ac7f360351ff086323b907a48d0e97aca9858949dd2084867a0744c3ed5e2a7ca4aa77f727ba23ad4c8430bc6d0eb7e674ee493271e0f29d882710b6bf73c07c1fec97bc84b7816b3189e9a57cb7518178079514b780105578f6a783ad337ef71e2b90134d504b6bca5446b57d357f4b1a6ced2813309aae612c5bcb48f71e538a22ba88b2793955c71b3bbb75de05f10941e83ffec03303d7a221e4ab1c09bc767f66fce1b0b165c8d6fc4590bc047252c53a7eadf08a64c357d5e442298cbb39d7e744d6365ae4869efe258d3a775af25282e175203db96e2c8901365cf0f37521f166f6aa35d71810bd4a5bc3baacb9eb3fec75701839bbf432e8bd02da0616216dd1b3b1bc1f4a87acce1ee084e00c4388d487a0b906ff188b5aff18b9282a16385bd5d8b27f929e9f2bf4eda70b5b40a3a067901b336a0f76eff2e4aa73dfbed34fd46dc7e8c025a631d7274a5c7f82c1eca1f02219d29daf36fcf86d2794a90eb45af57bb06d9b079ea80a2edb9000d9b2eb700dd32bc8a5acfdf2c59ac7c0ba116e362f2d8a8291c11fe7ab43c14d1c995eb00b89ada2f48000c4137b330688739f5bfa8fee35d2264c4bc512e078fdfb28809be3938b9e91a0576ae7fdba0f03b04a73250bcebf651e98aaeb19657101a7e0f523f96b01f89a52055c5173ae44af145a36b779ad1acb70ee397a2834a240401f8448e04061d0017e6b6d34d4b96305f238db7ac6dedca3d5643a648f672b506430da4ce89c8a33e8be5b60d2dcf6ef0a204f6a6e0867d0c3344effed8505706bab25c7a54a57891c97429ec649b7978d4c415d29662dd2147f37ea97513b20edef5d683963c3145fa73a33c6ebd93b6c9dace9ddd69191321cb5c2ed406fd07fdf31309530bdb101fd46ce639ebd158f2e8ba786fb1fd62983d22276f167301ebcb33991d030eff9acf56497021a14620978773e3ce6f281aa56ac154af590829745ff1d02317709745b2e50474050d2807ce953dbfefce566673a7249b17034621026a6df859880f9c4871745ce520b89b7cc2ff558f17f93f73cd2f21870d609a4d8bb34ab213dad81c15bd3c84eca4f6a70f548fe6f4387585a04a6e7f0b5207b947be5d6bc4c223c614422331cf9ed419c2759cd0bc7bc24b2ee5f38009f8c22893be48b1f8b84159303d7f8fb70a4d155f9c9155fdd3383b0d933e6108dcc17735c3a90f667f29cb7db6789e9f0862b89b51e9503dfa76e300bf4cb402b5c147e306994904b828f39e11679e41ea3abfd3afef0f813ba98c9654d62c0501edb686e7c22bc923c250e312569e102b721a55bc72f61255fbdc19b23c640813d9b35005aa8536d0cf7ada2342a6c034d44d180e2a4b78ab986ccb64a79007e75cfae974cc46e90c3ebd3e492ab761c437b914e53f6f9b399a30b42c03640f48d7f779f49d6d2f2315f7ce6a40ea1db9a59e4d69a058abe94e7708ab8d9308da8de9c07cc5b0b79086d5f9d2611fa4c383b80b62dae2b7012148a694d138035e22d55503a71a5a8c51ef6cb730b84b694b9d640f302a767513f3647716090140dc7e9ab86b73d4d72ea069275a168667bebf74bf634b7fcf38c29a7645fc0e11ce0e5c7bf32e418228bdf5114efe80538fbea9c54dcf9d0b38aeb0de60ae0a201dd20af7297d3701c3369ee9ab69fd437ee170e3e03328942403bd67ca6d0ff833986e2839904c00888a2a61e74dfb3fe7b9dda80aa5b8e31f1fd2ec154e00fa93819f02e63bbc267a621df251f1c69a3404d497921a3b0cad359562a1d801e2dffedf404be7c36caab6526ccf425bcf939406e815f85802e7e5f9a3830d002834feebcc0cb68c56615b3d5c69733b63756a5238193297a1c15efe969c6405f0b82683b2201a9bfe3c452b67788bfecea92a4511a45f812e639b7ca78aa40d77ce773666518034887b921756a5aed92b79d8032fec550e90c5002a5db1dc023d39359979ae9dc880e715d2f25f592be9510aad4c52cf33f18d9cc800a51d04c20b8bd80b4b1ecce94c453fd73083812f05ba69c5258ff5b192c6639c717503166d197a5c02174eb0422e58ac1d2461b5e8bd0f722178565f879d723888530b543ac81a6c07e4a62422f128252636525b7a78f3a60745e5cdb1783338be5f0ed17d0189f1fe9488cf7a1051fed0c9c9ecf96df176092c47c609651c35094904b70c53c769c047fe8e479326b8a121d4e4e3b4d42fc833ec65b6e6bd7109f303ded7aa4e749c68a8a8b05a7765c172ae4f0b541402817ebf08cac386a228d3078aaea9acf4d000a9c4d78856734c80123439a46c781927c4f5dc4f8d2563af04e5673cf2b206faeea1aedd05fca70453210515cd09c0dfebdb0d0b532e8cbb002578e18b6620adcb39cecbc79d67a7ddfbbd9eff51bcecc8ae5693cc632425037e2e097ed14702da2dd08f154d2c6df01c8de2f496fb4a1050db0df38f4d8a00a9c8e79cb3bda8ba286177d77fdacce0011d48bbb7f9ed536789b5402624f1066656b32073904cf868da4f3b5984807ab70d77f5db8468fc56a368a4348b0d03fde6caab2d466884f4389275af6bb4ed4a5e845b37ccffa5c2ce8930c1169409ae87c54edb44d322c07bf7cd485804f843a6f2aa93074c077762f45572e02501dbc53906d0d056785637d272572d9bdaddc5218f3a42a39a2ddd7f092352e2057628cf83613674832e56c552aae03e85074bb6191100a0c110c627ddc76b2b06ce9258be5b2efc22be6867f2ec28083c210a886c9d3d02874b9dd986029fe90ca7e2ad951dbeb8933785d70ed6ad7f2deaa3da8f0266a196714f7e7ec72cfc02ee14a6c48780d75db575610591b23a5039cc5a88f3116b2c9d619976c399f30092c2a906fc2d60624614123496df4916dabaf7bc7a108c4cdf4ee1fd5acf590589a394546d5d930d6921ad89059e928c84c1d831b9dd84837a0b1c0e218da6094a5f9e784c0d288d3fb08a26dcaf7b4fa01a04cb96019f9c7d67dac56eeadd0471058cfe766cb95a438036679e2389081963f536312115991b956e92631853019a85eef2ea4a792e0bf9940f254dd411ac7856960a8c46a5d4ce16746e443a08ca4e288772180219ac48cb649f284d875a6d26b09876d54b23d3eca0fc04c5080af9741a9408fc857c9dabd101fabf6765cc4dcfff839386f9347ddab7d85b0d7b12b438ec95d130c169cebc81f0460961d124447c06257f7b016d3537799a0f9317ad97415da98570f615923fda90f5a12ad6999d0bb1b08903c366f0ff1405bfbd5dffca22012908ac68f964ea9ce9bbc8af6da6c00a35fd7970c6c70c40003e4e6ebdc99fb03928b98e49bd25a2f5b727809ace603f46c76b6f75f15f2700f418d88eb5a195267a536db9ec99a6855aee5c120cc198c67113b1f9619c1e0a2062052349043b5b3ae6053ef75b19a2505bf19c2f3ce6dd9b6cd38e52bf3a0ef4f63a9575290b71dc0efbdad543dbc6d8334e4c85b47d8c82009bc477243000b0a5a27b25f6ea31a3ff0303e53ce4b46ecf3030ff3e47849478882e8d4c560983ee5001be5c72112ec58a0fc19087ccb075f83244d38fbe7082ec952b2d4a0a19b1cca4d5844638c28e1b6c6c57fa4203fb6677184c750d04242b8e216a1b067bc6ea154d72c86215cfb6eaa611d3d7afbed45308cf1aaea4bf4fb463ac1b0064d4a9ece22700d79c0ef69f28cfadb95110b187b8e8f7337c50d5f51ec6fc0d85e69751c968ebcab44dcd37971e732c889802b1d3e1796e66c23da159f5f8015ff85875b6e43318d59042601e668f5c1fe995dc6ebddcd46e5d65b2efc26501842244f5fe28e998ec051274013966785f76a39dc7be21121c1a27c1a1988f0f6bc3a157319c2a129b1e5eb220e01873b5b3eedc88739a34cb157434a4655b0adfa7fd337dd235e44e062fc75ec7dee81fe39169777fc246cf29bceddd2f5603488472441d6329d7a7a94af8fa7d01804d66fae0b1c9c987928c025ca7b7b203522ff96496fb70086aa61f3f12a9d94fbdc20ff88a3dd524bb0c369804c3120ba47d6d0d22e2c28a65b7c376e51f4bca00e307f4d2a5be7692fdfab601b24a00690eb26cf2a3f305fc565edb277c019340f5cb4ae72062cee928f40e3905f30ed58bf864a824bf8d9bb68b46b1e1286883b3a974e54a8b77f0dd5a4476949403a1b0da02e687e6d1a21d39a0f67cc8c5c85c0f299e8db42a5b247825787f180a6ff2feb3b8ed46743af099d36aed6f9a34fefd9be2c788b765ae6b2e07523b0518047035f9411699c63e8595a4b9320a481207e487332a87d736c91cf4eb2d091eb33911b92fe4678e1d8f13c19ca251bb8f7b20441d47e6c799dcf4096e6109a05327c481e6f79df04c2ccc993ca297b824baa8470e4498cf43b826aabbb302c43fdef413815ff25fadf84e9e3fa3086705b62cb710c74da79895f723a7330324816407de1271b60e731977d1af4d86e3d034a14a3e352f05f88885766cc3068ddb16ae7985db7b4edcbdb70de4cd4eb5f94654908c4039c3e317049be20c0f448022c0e3e9f04ad699e15a8df6da8ee0d9caa317cb489dc5ec276e77a05a089b0a0861d69e58788266f7d1bdcff298ab878ea92172354f08314b2a3fa10b0bc46225b16593630a998e044ec5a9bb1643056e3dee4c6f2f5ccf9daecf6a5e0894d7030025f9fcd40e05c9e73f6a54d730362e1b01cc9f2d5db45c336b18b0014fb89a21a8eac0e6dc0403b9d032022d44af3671489b8b6bfa7fafd3df8e6b081116778f7f3ef02a386730b643bde59acc2db7ac753c7270cc46d141b7ce900c4cdf521aa68da139a00b59bd361cc3ea376cae14253c7b8e320f52b0ea6e6807a1b2b6ea29934ec3b0cf15f8f5cdf964f373156581e78eb6848092953b24de03cc082ae4cdc71501b994c65c48b9df73f2c44c7092bd53532b287dbe7faef701adb22b822c36c19af77916590bc395d298d4e6ddb1e7ccd7bb7933bf8ffd7b0f682527db4141308c3af779b5e2558b66ce7d5916ea7af096418fe3d65ce4300a2a1328d5ebaf7647c6ab32399151e1b8adc25e0b12d4ff960a440d11f5b0d005eef67c33ed3f4b4744d7d98f1d1f50d368dab30c16b1f7b112d9c212fb2c6a0c69cb437b2b248f68eff447ba39e4a7ac8d97273f2c507378b168ae88779e6b053455e7b29e1294a4c8a93124532701c84541d428a84de5acc2b2e9d7eae8dc0b87289445352cd29aceb27b5a6f0a9623e127f84b28160642ecc003aff0035b0ecb224e50bf2a10a84e9268b6417b7bfd12c33bd27d7c5a48d6960c01d8eeb40ee0431f73805bc6b3516780c77b8a70cf5b003354c529f39f45c1211647a2d30c8a810c54c0909d6482a5aa2e4a9f0218ae551a20a93980eb11c80d584342b30e0ffb450e790246bb91bf0b7ead7161666c2cc64f79b745c646f514b45c0b1b0f39facb1925899eb80c2a50467f301e7f1b7789b6206d9658222b85400b19990e819c1ca5106625324e25a890e46ae8b5817b0d81a47f775f06160844ae8d0d087779a213a0d6e111a2fc71f860b9f80df3c38e08ac7b71fb3af53172f20d2004cea1ac76af87c6117749bfa054a3f69521b34b31d58ae8456c4ae33e36f31d0d4a5f3429a5c926b4fa2e356fffa44d9ad1a562d6d9be6a1896fff390d5194e0b94c2375aab43f8a147c5825f054932bd9b88decb2a01a65a4618d26a4862440d3e3a8cd082c654ee9e011c3531f2f4ff33b1a9eeeb2025075e3b9fa6ff3d290168844df4da8d258a829b5c36a9a46b0afd356b9ad5bab1d8c4d7a7c215f4a800a2cce1d29cf64329d601f836731b2574ddea2d89738016949a36539a4969b4032b29b12c8e1029a8875f675ef74ddcbc88773b3fac28b6cd53fee4cb47821c03d4968eb2ac0e2e91b41e38dab10cb030389e28a7f73cafcd567429a8418e2105ef8ac5c2ad71f3f7e2f7682a73c66d087f67409f736d04d4b60f68754e1dc90c2412d86bad431dfcc8a869c5ee1bf96ea7b4d2a35ae1d3e0d3bb6bf76cc8e605241a67f2379d7aefd2bf3abb9a987c565d88847ee6f8c2e64eb15d1e3b2d0205b922d042c20bc376cf61a1386cd9b079d0e504169794682fddfaf877807cfe02eea07662ca5bb79def9b0edbf6b15af6455c3ff18f81896ef039f28c195cb50565e96c47072171879bea8eac4e0766a59d1d19d404366d897bcfc55c06fa100a72bd1b764d04cb5d94ddd553eab51caecb49380fd56c3bf23c40dd8ff37f7e0e852a6f78760a395324b1deaa1ca0c4a52033bce9d37665cbccfb0d628a0e4c0ad05e7c5d37e387d90cea4890d7e9c8f976fa4ddb60b3de212469569db616a607901fd285abb313c11a8959080b3d756450b1448a494642d5bfd29c1d92175506014ce8419cd49ceb69b9e0cc11e78001d4387c16536dd0fe697e4f49f3d7d0009722e254ab780d37fecf8bf58a2b5674a152b4ca68e3fe7e5baf6a959395f60ce3eaa6ecee3bb34c47c84f825433cba087b8c9e2f0de0f62eb2c755d0861f30a05c065a53439be2e794c3d5cc197b5e25b5124d743d9468db8484c65bad12503699c33d0306b5e71df09cc4cf50cb9bfec7f8ada291d7ba77d079a0291e09e034e8da6a27347d48f5b9975ad92a4d84c7939fab99e89f319719e3487ec8fe409629eec569c9928663cd4f4efe35ae8b6876122ea4b1e8fc4869fb16ae58ba2052f4ce344eb030d2f5b958b8a0a230639b6025ff6091db523d74654bf6c22830a77663dc6e3c17992edcf882fec55407772a403a66d74aaf7c4c8cdeaff486f0056cd005772903c3e8873c2d98b7abbcd35b7d154795c2f73dcad880f390eb3035fb9fceb50a8e82dbb11e9e27b7d08d5ccba4b7b825be799c14204c9c3cd990f0182abd0675108e7641219e2e287aee0e453026618b384f7ec0475b6c2761d06539dda33e894dabefd0595654ae0fc1fb42c70077309fcc58a221630bbb86d0dac4048b707e179797fc99600c758edf10ff549089f7d66bc096bccc7859115013be21833ffb1169cc1df865999c6e18428da149ba4cec11e46beb5cd8fbaf40412b4b42362bcd36b56b78b04c882f99ddafc0e41e0675fd463919fb10cacf80fed5d87efa5703abf57d8d366a2bfa5f1fb796eee7ddbcabd9ba35cf7c9fd9204fb45859b8726ec0ee70fb2ab873caf5b333ea273ac9d6bcc59e4f15d63891e08c27fd61a9f583b90d4cd19d8c06b91a78c6bc6eef4f81920d4b001be90a9520eccd8b2018d1c62a38d27388bf91c620739055d174c16f36f0abef8828a58ae02c7390070c03ac9a3bd18f1b3996322ee0d6a883cf009cd234dc936454fbdb70a1b174de9664d41dfeb6206c394f5a0ac2bad288d4951ef45d09028c07aae8d01a02416f42a3ff840b7b37c9073e5df67b32ae5e2c0ba9d79394782f33b25940a6444bf52187752e9686e8d07a51a906ffb72f0a653870e29c38148fdd60da50771b65d9c65eb306efd0e6037d1f4356baa6f0af7858e1827d6cc7e0fb7a6970217f6f1ab34254a47a80c9d615c5b1de576350345eadc68cb05eff8fdb7140d014dfd4977d85ddbbcbabcd33844ccfe1465a180c22d844ac250c6a36cd33d5505411ce0f59e32b073e75fb11426cfdca258f37fe8ac4f86b237910ac6b7bd6b0790f2ae95924abb00c3adb71eda0f96828f0a548c9b54f4b521067ec61d643607f235075a90e1d6605c3342c26173f941234cbce6a6bc94f8110b35b5d58b7f008d232250f6a4fd39831a5ef6f11245f3a36cacc3d8f21acd7e113a5363ee91095c4000176df4c981dfb3f976b7d3f90fcbb7f9180578278ed148bc10eed10d0b663b50375760ab577f84172d0c7aba9c8f85c0e805f123fe1bbd8b4b3438640bb3b1de5d94203dba6a929b33c4a7e8694c7394522a48337af5cbe1e0c203cd017ab565f66f3ee9a2892b2da2f4b905a5d715f0cae5b66576a592830a5c8c6301c445e5bc769310893c33340a4ef72aec9ede613f6ee25c58068427c7727ea104d163ac5a8441b156593059b6670ded5bdbf51a1a9a8490fc680eff9bf6630504a09e0497de94419c2862d9a70441634557ab3706f3b3a57377c8bf9327d2ba0a8ff2959855984b7f85a425ca73f83c1504a76c9aabfde872c1de60089a873b009327195aac6b916980434bcde81fdb1a2d635e29bf77d0430d27b2a47ab6250c422fa9fbca49e7f9c3ef8cee46522ea6f1b1dee38e91b7261c28e48576c9ad0302ea8fb85c0389060582e699a416dc503562826c63fd18b96aad2f7150356f01e962db92adae0cfd872f3e14216606ade6df351dbd477751dea063d05438390d9ba2b48519031a0ef8bab9560f27fa18add5aa559e5b7f3b4660c121a990b30f1605ffcd2907796dc5a83a2967474e33ba236f4460b519bfc5eb1ef9dcaa22056460a35cc59469e3d11b5562a3311f8dee918dadb629186b7fa80354ad09920bf1192144a9d3b55c4f617623cd705b8f87df12701c740e59d608620d051cdb0a681240f5c3d0738eda7b6992e6b17d314a46b99467c56e7dc3cbce4421d7a40f6f22eb6f5530c938b11387aaeb7e86295dcff2bf89049611a2df51d26077020ab668a47ba24e2746018dd3e0a95376045c187064a5c09fbe38796fa5beeca80335dc4c3b71385101bbd8366eff8240bf7043b7452a515807d7976d754e1dcf0dc3f86be7e2b5f7767c2f5454cda40b2514ab9d7fd1b2c2ed4ed832021481c8028c3525173166789b5b6e85a9900bec2bb826f6c469ae68033dee628716cf0b05213960bf87f65fa170cf3981bc53d9f0e4e7060190bfcdebf7047f881b1bfa0df4e4868b44120186524cbc65f081ba132c094d990c36c53e78a386b89d89920ae39525506af0312d9a8d53e3288d69fa44f5b7efb80fca91f71354da3fcd0001d1cc5efff003db4208994c93b1c8ba8752611ced0b6be64385dff3e3a1d99603c3cd630aec5182a7628a9bd583d3e0307e130c3c056ce53df7823781b2be6f0e68366929e3db1d15163699cb52edcff18c7754746628ee8b83c94e32fcbb540433a3e1ce3ab88fef6f402090efccc0704f767fbc8c37e697445ac53c3c7c500d01c921f64145178fe1482a7811ca416a917f8c708317df8f6933c586740b5301ef611168662eb451e3c87cd9cdf97ae0ed16c7e8833f617af3fb288eea9b3009e40130c467268992e59afb94063f500bc1d5773fe816fa6dadcf0607a2e4960edf80cc7c9f3e0d87d11ef867474a9f5e2344a777db658d4befb424c855ec0c081461edbcae88127fd292f7b1cf535a0fda7690d37b6945161e8ecc35f638f40bd0832f4aad78ebb0e584cc30bfe8ccc2e75c99c0777443b8959fa21385b75d0958a141b7fb96b9f2685848d287ca4304bb4390b3758b856ae6995d7e67a0340db71239a5993911ef80895ccf5b36338fce1f4fe234b7c722d912ee0e94daa80abfe2ab020437f57271af71cdb218d2fbc924008b5e667a062fd08c6db388430c30863da329717b25acc19f33581fcab0e3bac58c208fde5ab9498dbf22ea1e0d3f5247b9e1549e55bf786de6e1a61ba4f12f19ccfc53e01d79fe76d180b657092297fcbddd5e079cd763b3fc25c9a65fb0a7e03061e14340e60de0751285d705dea83c50aac7de7821758e4b754e5fa8449c7a7630addd177721a8a51634bd0a08e4497e610d8ea8a63d317e9ed4534bd7773e6596ec0ea2b4c2927a17e50f0241f0bbb40456d859a1278340f9e48f7bf9bb7b588a290acb19c9ba56a63ba20ab053242b08f86eb28b911f313913979f06f912ca40681ac2b01066410108ac06eec23df6c2dc40e18871c41f6e071c795a40ed49e37eb156a632ad13a4926e03390582609857a672acb93cc2c50ba103cce1b4558430c8fe3741909bf5b011010847f0b8083577cedcd5282bdf132d8c3f556b3d3e31e0e2220b7e87742ea2017a5667f2c7754777123e60f783f2c3ff712b88a46462bf36db6265469cd85a004473e6ac6729bf13285a046d8149f2ca793b39e90531f02e56f7989c2671df024c7084bc037d70b28959d8a6a9b8263ba30325d42348a4a066c9b2d56fa60c0e522c097187ba5909b5025d3c578746b97e411ead24a457108e905d2835bc2f0298717ca1e9a337529fa274d92c8b5812806f9da9885ac354200f9f289f33d603643b05e0eeaa0fca529ab7f8234a9ba3c67bfd146f29922422da65310b8fdb0bbafc5901aae1573ea8b37eb05e4c0d09be63d4b62dc13f3077d58dd3ac43cc0a9de82e3c83f8a234c1f2b72621b09c190f828219c364653ced8621e3172c57063cf45e40a2a82069da9f3b4182ac71eefb4a5b05950760c715809ea3b625030a2f32e42e8134da67e1191e8b995d06cd6dbf8a7e8188edbd7157e8f1043fee06629fe0c7e045a3c28cdc99caf78f6e27bdd8273ba5ad7eda480b1da10a87c9057020e229e972f294612b0e4ece4035f6db503d6b5822b12e82359d82e2bac301d8d04cf2ae0b891e2cd4a98136d211184bfb5bf5ba1b9de0f93ccbe270fc1a073d3be3d8b76c7faa81e6f585bd87535fc65420920d81eadc6b79401f65b7bf017759ff27515ecc89d960ea73be9e55ad57bcd4cdd032e08359a5046eb8d13d0042daed43382c0c4724a6aee3eec70fbaec9f28f3f35d8130e2cc738a04bd0e0bb49ebd470ecb7fbf8fa434f848e8aed33a1fd7cb65fc2415e3ad3bc57bb50a09747bdade1e58fd31e5fdb498b8f110bdfa390fa7651779521d1fdc9c3279ee0a68921330e26177d3e50ec20cc2efdcd3bc0a3fbf42e1f85eb184559d6674280ee849f04313627801561868e3228b1d3e5a020190131c914c9a02f9c60155fe0cfdadc634aa3f052555b2d9b0352d5710cdfea7d838104daa0d1b9467f06b070dcb39dfeff739f04f5224e9d4bcb820fe5c2f1771731bfe4b759140b7956fae0e7978415117745d1d57c5a944119c62b02321f549cb8d1a487078d3bfcb278907491c1808e65df0fa1e365f7d8a571fc6b43c8756093126af5454d4468c66f70dbff9a48615d7a41e4f32b4320b30df1c23d7f03c5632c40167a5ad8f293a150b56f91b38928d56132d78f0a28e1f01829dd27cf6cb3f75f00770c68df9fde10b3872e42581fa87ed4407c088feaf408d9933cd7e1ed859894df3c1de211c1408d151cfefff4295e50c46b4f7f7d3747bd5983a25a616ec095134663747079504bfe13d03791da920f5c23091338a07ef5970e76fd98e76f0f241d4aec484960523ce18f4a28f19c68699aa26d312e1a8407b7282fd5dd7ee4641a391eee7f20b5e693f4ae9cef7219688d70adb14bf582ad3ac36c9cd678e257c52aa27f3ba034c06408ec109986dc525c3fd0569f4c87af71e2e6998fd52a369cb7ca52eb10732dd4a18f1a76bf1a0216db6d3e89b5f29aa661570328708c84ede69aac65b0249544addcc3fb1898d33b9f87272f45a9b797f97880f77c03d16e84195041400c071c2d900cbbfb08a5ffd7183846726884e77a506321dd0c395271943a9dc08f4b5c8cf5872eb64d25c34d3830c4c45f7b1565647b985834be21a00fdd2dd0bb6116d0621d3eef312029dcd6e67941a0d13a3132c2db6f307eaaec88b5d6b0f8b9c1489ad9792006052c0305d03e1ca72d6e0bfd00254a7bdb0b5616c3b5f0bc70b8c47f6abf58511cb7d7474435f839812d0db83bb5152fc7c1bfdca2cc40d7db234280d0d07672e262533d9be4e7cb4fa38d266991f892c723d579404aa05306cbad97e179acb8ded8be33e87c3c1b7f0d13c63515170a5888135280c360b6554a5e57920a494728709709780766406988305edc2d3e70e36a335a2290908f749e424585f34db52bb23f266cfc21ed7fbc3d8c361c5c841ad52c05203e40d987e8c9f277efed398f9c02ba060a8b368794d9441f003b5485709680b5f1506758dece3a5456739e3a169f54f2a297d1f4b7179b996277f306db2fdbbe76e03041956fd992ea5a6282b81bd84b7fa17275bae847cbf6b7b0ce38b3b91548e02abd2b9c4d3ef42762a6fd8df382124e0bf149e3e9d4323d0142e898fba78830ca0344c27f1616ffb7f8353d0dac1cd7d8b172f8daa7128ed7f79770fa476ff06f23b93f506f4908ac698d7bda1351c54f97249be14b744a943de8a03999e630bb059302079a6c3f1a6d8e56e8a48b7f76e05dded7e538be30271c4d5b3bc880fbbca179510f742078390c7ec3a99a1e982775a035c071b653075f4e2a378f0016fa0dd3a0c9bed8ebde17c9d1d117b8a3aa9a4ede663fd4cd91314905df06b0a83ab5aff06ccb5e4569053161cb76e74be5749cc50c3fc26238b6245e580350cb966b1d375f2060409cb0b25620c4564b5736a12443d83c7b7720cc6e2cf600827f9a6fc1b221e59fa1e14c827dd1513b512dcdc743cafc375cdbda5bd788d0fa0b5faf78407eb59b77c34eef638e28e155b21cd6a744c0089792c54e0647a0459540d8c6c880aa75840e85a791de8a0cd107d6aa802584b6ff25025020b490b3a178c1ac3adbbd4d024efcac5e6f7507ac9c4955317492d53a6c1a5bb070f05aaeb989b5dfafbcd0b676b973057e7d683d1c891cbf5be02dcf1177caddebf0225bd57aaefaf5a1723887aedca0d8e55696222a81822995fc4ca91001c7dda0604603fd3275542b104240752c8cc50bd45453f606c0afa49cb627e302088c70a6f403e7ee6837a9b0c3d8ca404ca829b777f7d5eb3efc96481f05ececed3370fad98b4da97e7b8e5381e82ee4f3d07a1682a0405b68d7d8b46ccb2d106c1550ab4f31226e835e7ad9d5d63e17a57a6bb14358fbba10e6fba89fcb419e8211208796d43e190523a9268300660eff7d12dfdd8ba737fa36ac8665c831cc3600b034f217121de144660f41447151067229610227ced84f8829cbcd7059c074cfc0f9f604b5b8ddc02f03a371d55167953557700fba084a7e7a6fa5046b905642e031366d52706175e0b09027adc84d6b799b305ab984e7b86c3def7d873d8798603655ec65a5ca477d64c9e5cdc589091d702a047b28d48f4bf97d748717af65f0eb7bb81fcbb1f6e56f3df0fb12f89f21df6b0127479f84b75f9f360bc76f4de0c087e5f8883c9039939842369c948e29597e8230d6e6c9243544c4c00b90a270f12e1db4cb5bcb25c35beccfceccaa5855d5a86cb0c232536d3cbaf64f7ff3e0ccfefcace88864fedf39ca25ab28bdbdeffc63c9d8bc84a846f91ac9cf4ca7f0f5d4b48cdc30af64ca7ce116a5d7db2d02bec3897583d786a9956af7482cca70858604b9f6defc7c609670acdbd2923cf0baa48fc9c4e3512ef0de03597a095054069beb01b72bfba5803c427da8cf7a650fbc45be786c999e9982c35bb1b020f988496b7966daadb50c9182f0a96251b2139bce6d2157d257c113c0bd96893071757a8ff890aff9dc94cc152691b8487705855b61e21ee22a30fa1ac3b43ce08949226151c2b8d82de0073d0187c2580a53f0eb32147385829bcf4442901500378cfc3f49c16c4d7f226a0d3b7fc6098a5c7a5b510babf68795360b4402a1b09647bad1da714a07b8bfc08627543480b4cb96ec61a3de329c17d5f055159550839db697d633308a8aeb50f5099ea78bd484457ec9e2e81224365ac78adb1e605b76436fb16950e4e13e9d9b9308f0cdbddab1e8e68efc62084849cab91ca820eb787605021ce1322da05638501cbe914ed3ced9ba73115e4746f06f055c65102313765aea37ae8349f2b22715484ebb6d05461ea394a79f02398eb668aa1ae0bb7a3dda94881db130b704cd288641321a90681a54034165a43ddfbeb2cfa9f01669eef11b225bef91b379b88968bde6bc8a638a574a3e3300ac8143b13f5f902d5dcf331c168304a7668a783e21cda7ddf3286bdb88ab40d3630b1bf9527e708e05c02ba08fec5b4ae0771d173c97b81773c3ed55e900579d1f4abc01ca7d405cfd9d5b877f8b29a2c3eec59d652f5f2b16ca729fef248812e65a7d45a3a4c0f5b0c6233e9c2287956411be9075be3b05bd6610979eeb54683fd473e44efd509e52e64df862b1a7d5512b26d4fcae2f3ae4dccdca15603ff2c46a71be6a3e806aac3227c5479e067e410931975f5cab25bdd9e52ff3be6a99b94b6263f6ef60a8fd8ea935c62f6b11386ad8da37f288fb137d985c199d4f9a296573da418ca0ecd01b2418499e64c5244cd9c254decc386f8b6ae4e03d0ab17a78cbb2e89c702ce1e1c9b637e12079f655ce79dbbdeb45714ca40275606a80b9402c83ae71809eabcdb609928911740ca5e1443a99a4b69cb544dfc05a9bfd335a8e73957e20f0b88a62ab868cd2837f6f667e3a865896c323d9389b91834da434bf71a2704055ca01a63e73106ec6b84aa638b502e858b97a13b857c19f5940a1e206d304d03600040018f6a0bf6fbc6788463acf5b25f37711abdcfe77cc2059d7d06af2b082dfd7409e505447cbdaebedaba426b7562485034a2a7472f934a41c2ca902d0ce2b501cd7d0686a838e640da652b738ad74ec10381cc0bbf43130ecbf202be05d0e2a4e1987d0b31cfc1e6736d6825b974545a03d23f58655ffef5304c0cda00ff42d40ce3341a1f7eb10a553f7c455e95be138f52dee26a90ffb9ea63c0170f47b66bb3d477a5b04351efc2172d60e25d7de574c6a9092d9dad8c760bbcb60816898fca364efc2d7b09d4021b2bd01d5b2cddde228aecb8ba95b0f42e31d50618fc5ec26512bf8286d0b6df805b2cecac7d721822138e6cd628e7823b221b079569d5acb33d57c44c3c95b473c81f9e6c508ae6d47ca4549df79ee1a4ae4005493383663459c326666e639f292946a242c6652ed0d804a0bdd9496a6b4f29050202ca0bd475463aa1894cb91ffb176e466df39c0e11d34ce231bd247484cb08559c28ee8fd0bdb4e94db9d995e3b6dee8ebdf263fc9515d99a555cbbf0bc806fa115a233832d759195a91fcb37d2aee13ba510e5c10dde8a4d33296d97b730a0fd97ea4073ae4cf8e0bb8ebda36bb58baf695d888cd3076d7eb5ee42ca28a0448c3663455792061b2b95556d3d6b087f20902aea04a2327704a8aaf07abce0269343896743263ee03f888c2c538de426e5d96e33de2d478f0949e1324fcee0df6ede249618b5a567d430d9b6a2a2f51c0d887b55ef4f9364c69b7f29284d7081ec2290ebda8dfcfb61c9dc988db38035afbf437ab63b4b3abba168a58bfc702cbcff10e2d992f1f3caeaba8376de5b8e0a73b20b2fae09f396a9960a95fa00248a774092660bd99afeb6b0907d372be6973a97646a4f7866eadda42bf5a40097ed147f36a3c8238f2ca734a153e2e52def8dd344dfafebdfd35bc78569cc706134cb21608532df9083c4e2df48c74f6e731558e6a244cd50b221ce298d3d1066f46091248718fcd9c45a6534b37a37cae75ea2a4591323263020d076f6efa0026a17ef5d04ba3073f34599d61d83460dbab88efe06bb128484b973d276a990f643b4a4463fca29efcddd070ee4816713eec42b57f059b7f4c556ae65991b709a1e7566b7b493d70d40c6b7813bfe2c127bb75165e31ddbd108f4dd9af0a5e06ef4b1fe7a9dabbd69e38b5816090dc63eaee84362a759a171c6ba361d17fc907cb5fc186931c35085b4a3879d54b4046b20e943dcc03dfdf764ed5ddf31db40eb912568d855607502149716b3209638e4827fe6ffe17322217df4b915d9adf03e2b87a48bbba16e3f430dcd02c83b453a9c18d98a00097a8b76ad7eace043c09d3148435b866df326835049461c52a0a027b7c1986a425608f98325e9b3f27067f96b197442a48120993cd96f8df852b343b1961a72397728e4bd398d623c30b5e9cd054d1bd6bb93f9208529e7ecd4474d250b811132a3f0f0d67fd40fc07013feea269de72c83f7bac8c7578807457e199c56b6352d3cbe1ace5ba28e3570467d51b1f2127a5cc528ecc35894d253d026d5706577f0f50521b77690cb0e90fb94137bee9c728c07265bdcb8db835f35ddf935b6d6bc03a1535f6df38a2f60f269bd871f3e5bcb9b53ae3dcb046cb53e09415a679a953739ce31e965bd40601b5a703e07f4c013c9c2cda116effd81e06f2d39bb698653c716d1108c6e47c0c1eda383a446e6811a28c07af7b42b8f5a43f91874ea2f5358f0c12f245bbbb003eb9d288b53632b31114a332b26abd81593258fdc4eb6c31f51e36eb8cf98c00850e45e356eb37afa936140ad2f4a7ba0bed11433c8d798ef9d4b06b3aa038010eafcaeef2b942d5a5ef611d6b8e8739105035f4c3ef069b3692209e66c06e00fa07bb8b0e2b5578a396202d2e3117add3c9aeca2c95c742a1f161c3abac3f07a5d6cf09323a2ab782873af2efe5276a25c08d401c5ce1d9a2c7590f1b848f0bef3c40837da48d04ba3005efb74f7a6de2c689a210d46124f32691829443a3066c10190929c43912e0f042ecebb33f0a2b384a07d8757f62371845e5eac8d609b2a899b896454ed1bcd76969b40674b9618d7c7439c5c4833f13c21d84bff800a002f17a6c4adb748657dfb832d43a0c412376cb44bb363bf0cd0ef205c66d05c722d2ea1780617b156e531ee0fca7845a6a8b66ed0af7ba056a6d230e5f23012d3df0a011fb91f74f1d14487eaf3e77b0c2eceb7533c6ebe2f2a3dc6d655e01083e4d139f9856434847b06ed41c12d474b4816673a439d3bcdb6dacf992de0e18b11fb316f432a72b5c3a28faaea2a14bfbcd3e5e391c3b625b7ecbda2cf802744996d081263741d9821dc1533ddf2ebe767dd8bf697ff023ae7f63c5b3630ea71e254a5eac99d0c172324b8f06824890222833c4a3a6d3f7127aed56480007702d4644b67c18d92906dbc980efcf693ec31dfea9ce1f9061ed3808578d7b07889f27633c4bebf62154fef766bb523effd4aeabad0d950b86095e91ebbcce0b99fbbd5ffae06c4a2afaa187cb9bf488b95a7bd32d5f1f9d08e04eb80852510df4c6da68c332a727f927a31fefc1b1f0c63344218c2a4f416cfa2fd7acf90d0c3a0289e0631060516e08910abb896325cb39e2a9293339b7fd450da494f14c0cc6282ddea0cc43ff2955f1437cce7c72617d85a3853f88b9814daa33039c940e2dc9de37dd680039e18754161b906f0cda88bd8a4f4753a802a90663cf10e80ad1229306fac3408bfc346a6f53a9a32487119980e8db237b5f9cf206d32016054c6278eb767f894f546ffd94c6d391c85c8d9e00dbec79ada19fc151a2e6610a39f60c64b7a73e24595856caaa1d0d5a2dc693038289a4369ee0b8bd6514910598e10cf6f68e4bd551466d0aa4640981b05739e4ec2d52bd545e3082775bdd0d2eadcc3db115a0fe7a2de0c583a4c769a3631f55d0ca147a17d3c4ef2d866c0f31bab53e282a113a55b7548db5ec963e4dc7ce58fe0ed0aae72902d8ca358c0fa10fbd0d82f1b1b75a792b63473d4247556eee79de1a1b0c114175af6b79c40bcf20afca171ddb744ea85f3fe8e10852595e42b89593e5782de681dddc05a0082c61662fa38267b1a36df9fac97cac0b8d39737aa86c9ec3703d4469851794066784f7243d37261b24952f5bb5a8ff0add82b0e017e247db0073e2482e00ef0e0b1127618077fd0bbfb20ef0eea0699ee9f9c9b84bb100120b76fe27fd89a6088e9ee3cef1a8efca20c6928c0cb498d387515c65c1b77de63a93381a1f642b07902d2533502d99b112422ae5147bc3cb014b77077a25f56586e215756dcf540e726f0c16f688faa65092d3c319767ac5c15f932d6af960e4a288de367731210a42fdd27b6e17dd7989b7fc208c597a8464eaabca174de591024a83d6313f250e9f30cab3a53e963e32cb7feb40dc1fbd5cfac3cbc03e6ffe816025798c63ca0d388747a2308c5a4506e27da110a1994df51f9dff4c3078ec266df02358e96506e32587a317ae37dafc381b18dee2440aa58ebed74ed9982cb003fb679cd7f60a60b87d161a706128cc8c3bd934749659170a9d682a500de195abda568e96350da68c2a3af5b120807775a44837406f4f2548a8f05897f196ad156611ce2d2a0ea7fd03b5e19df312eb8688c7d86730bc9cbcdb307936633eb4b2d000047e660e19733502e2e9de1b4474ab78de43caa7069b5e8a87b9fe942c14fb0d495d55045799df8f285a28770e44e27a20e7ca80c06c764136c4d6f7e21c7fb11303fc0fb26eae64a3cdc9771d8cf8a19a012e18c8a06921b00fcd71fb5e110045c2220f508703113e8e3bda762379065e243807d8f04a1ee82a5bfdd10ee3cbe7ae3f0c0377f69c949717afd959f7a7dd38f964d9b18759645f2efcdcb32a22c808ba0d13c679743b13d056b0b405cdad60b222d2cbf6f8c926e5055c1af6110b0e860a90a83ff0a852923ce25e3d41511c5c0c36aeffb7b26cd05f24804e82efc4f605434c53a239d12fb53df1a997d68d8cfc7604f86aaed1f6d7a6031db005a552094f390c4cf98475007b2c319681fa9f1b0564cdbd3be881a596167cdc8d76f70a11680bd2a47ea2daeb85d4dac3ebaafe5c190ca369501cbe21623e207e3ede0a6f0adc0d89d967c6a7fbffc4f257649d8d1d6e1b6623a18549147207040fe30bbc6c05f5a8c8df8d59e9de86a2228562378243d79aba40d79d7ca1e50f78670662f447e6561d2ff8493b8e66b9b24c4685ed5669b721c744534dd04e3916a208a02ef829dbf6a1ff4b4c538f39984b34828c0f1b38445e7faa071c16fe8ea50256904880545091d19695c58a9302de787eed93dd68689276222745e0d6b66f0855f58f712298de9c45593df3356842d9a5a15e9da10a2c6d59d759d64e1c0a01c401a226dd32222bfec5e6804f2f2bfda6e5cfdfe1341c27729ed480fac36505d0aa2725dae3346b63f56fea6b929faf34bb144bcdf66daa426cb3ebe2a8d90598fda9d458925b4a07b194f8839d50ece5fa2b2580ec851a4fe70692ab21d20e372f364be7424754755d9d2e4d384c8af4f046a082a3eeafa0ba68bb9aa6d705827f7eff085c961805eac36882cf54fa46f65138115fd99fd0424caefbc599077beca0a2a7060be90dd380ee4f49232b4a0d98e89f6e864c4aa8d4cb6407bd078cd6ff873dac15902655cd0890953ac2d0147b6c65a2663a589df32123a8b4091c1baa35d69d6f99cf70512e62b1b27c872122d0d5444342dd57b63ae0992d0621095bb7dcc56899ebf6d47d536b8341eb28db0806cb5c4b480e97c56a6195033de18ec030acb08b6453b562e8ddad0bbcab5b4bf5e3a2a91f367dfbab0951031d219a2423dafea825e61e118259f5bcc1f46e51133b217b102ec8b3ce159202 +generate_ring_signature c94de04cde5cb97d00fc58347c9d43c5e2ff19eea4318f3ce8eeede86e590181 dd0cd265cb990195247bcff884ae711d6d6e27a552da53c3871f9f1ef313119a 41 fa1a0c591987eca8d52794fdfcb34be618b911c96bf95dac8383b875200f160b 5df51ed1944ca217cb3aabdf05f9e84481cbfba603863402a3b0bc3032345285 aab3f16311418e85c428006e09f6ab4cb938193554381b480c2ef44782ab092d fa2d0b52bf1b930a1f0f482813498bd527ce090daa3f4285477aab43cd1c45c0 954e9aeee405d40869ae3c3ac2ffddae7c00e08574f8e28528d9efc994885f51 1ff131463c64c2c3a1b7af41435e737718ceb11fc26df0e48f192cc78b49606a 05defdd1653a375ee7b758c3d7bc997beff91c486ef9acd1f7407599ef9eb509 4db7cf256f884cbde45e0e580207029b4f5fbaa85ad7464b20e36100b70083a1 581797147df7936051e989bb4881f330344d5733220ab185292371438a9a7660 606177bdf0e2889440bc7c26fcbd0f17c5a204d1c20205dce16be9eb561361b1 94a10e3d908cac01a162fa1087d08145fb3db70cacf955bb70d51bd0c0cc2915 a40e979b15e9b750a010c4f924a6da214bd0a0356aec01f392e676c9f9d42a4b e4e6ba7de1cb51d81bf38ad8e9bc3ba7e3059b170566e6188e5dcdc197f424cc 0bb2355397a31022e06f6edbea574aa43c63a063f8287fe3de58a438193cddbc 1994a036f18f48016570cd7ce9c56458df51f532738e2fd45fc8e09dd53ef228 0ab9a2f03402f2b2d2d7e850d682cbdf1a82b34566059897b754188b09f84477 338c19bd0bcbabee027bddd1b819ad49d5c37fc77c7cb6d6ad64862e23ef632c e91daf7d75e20f04693b719c473fc61a15d3076e1e372068316e0ef056be00a3 f547d83a8f49db95529bfb04880da8d1856c39a35ae73070dc84b3694e49c82d a3f131eff367e0c26660772e1598e71a430daa4d7ff274e3c8da018a40b7f765 205875d30d48dbfd44277116bb0d80b478d1f8a0a83ab789ac0404fdbc03617a 92c1fe822b784d0dbfbb2ec60c8c8e9e9a797b807648554bb5105b951a3d9d6d ff72f514b8adfc2861faf09820604be178b7daf92202088a85d2d0e66bd57edb 223c5ca852fabf656ceb333b5bf01d7f1f37f2566b54b7c4c29a86f18d1b8ae8 14a1430cd78fd2039ac5d7df2e3069b24cb36b12f2576771da0c1911f49a273b 69df315a3694a8a96963810c498a765f95eb4f84478bcb38a8775797de768e86 e94d3267a6fbec3acddc3ef82a82bab6ee8b0fd4a7defb1332b936a5f1013fb8 ccf5a0bbf7e6addeed9cdfc3599a5a94e47c5faa02a3ba4671a4c008bd403f49 45f5cd60d7af60fcade4173bc0ed1a36091f206dc28e13844a16c675422ee1af b1907d4d139c71715303888acb530cf952f70e777b3801a0f402f2f0585d6930 ca8744c0d9e716417589570bf615e2f97e01a83992b36558af37d7ff28714523 fc201c54527f971e930f3d35bdc20ced6a666ac12d851398a7fa9f26ca509f53 6efe0cfea913636b65c218d9eb260c3b9ac83018e18623135af56184a418b565 e35ed29a37ee3eaf9d9c8133a0ffe4d0b7b4b0358d5889ac0b3dce4bf4bf12b7 4f336bedd6f2044ffb6866bf87bd50c3bc3f0d814b18be816f51beec2f29f1b0 eb40611822d44136ae6779a5586246a2ded4d005c83334cbeda1aed834b7f888 64b8c042003d59c3e9e1c4ae0d2ef6c1d151e02c4c5e7c36c7aa5b59b7a74357 e7ebbdc1ca40f25c085c1d573b4951e9d0e15cb0c0ad0026492cb6b815ec2595 2148ddd29c481bb590a19dc5091847a0a86d1a1f6cde6a8df6f594be8dea66c8 96c16cd72fb2150c2371c2313d1d24520bd660b248c5cf3ee368f95d7257cc0d 9cf1735d67ccbd6c29457094d09bbe4b18c3824e97168d9fad6c3b97a177f9c4 25cd26b4ae877a3fc7251903d65736fd36329f066ef5043d715032b1244cc803 0 94531be2aaf28336b8add2e441b779123046ed1f8897056c4b13390ae64d090fcdcf3ff0669aca8894e424082ebdcbdde21812b17577cd441934a7e9cad2ce06c205b3a610dbf6cfbc4e379bc2269f4d7389cd8c6cf2e0bf1c8fecb4f1551a0b56c6b87307a788205800d9c371bfed642968eac96d323c530e49f92a7a43bb0ad23bed8e642670ea2ac1b70e74fac28f5f4e8a9c8356a259b1992a44f7e6860ca2022c7731c6d34640857ccb54ec53c85e051c67d49e87fc5ba6a35bb268e10f9e917958c72c091a170f222bd8eace45113b78e12edee412883f6d59c8b021033e9f00e6d569311d549ae95edbe49768651f16c36791faa6dcf346f223a31b0496d6d39a82b8495960aa56958f423cecb69a3dc5229ef2da50d002a57567bd0e411ef01549190a9dea6bc2849e143784a8469e928dda2db82a5dcce15bf02a0800a6c55186a68f6e94329dd05b8a4621f1a8ef0f87ac79a4d8f3e3462833ac05eaba87aba0ae563789d17823ca6d3395e42fd8d704110f8f0014b0119645500f0956463f5f19ff420bfaf94574297304a86e943958567f6f2398911e4ea3530fa0de3bad6c83de2c3a9838e3852ec17941c1a907905ffe3dc9ae6cf05116d409211d397bda1831ae225402f07e8ac3ac910493f7fc5f4c13adc64e17ba6f5b0cceaacb9344dc593c57bb5ed14123e75c8930a2470c240f079d6c002eb973a60ab868bd8ba803f66c889b64a2935f3044500a5aa39d65a6521289d3327a10bd01235316b8f29897b372f7911fc4ea88edc38ec2aa3678a3890f2bb38da4e253016af8b5c9b1764d4c7d40dec931326696b353c706ffabccc8051795e332efdb0f85d50fff295938fbc91fde7a2c2f8d8e13ed9bb05eddfc83aa8ef48c17366609150686186cf718416602f3ab5714a3217be8c101488689bbd42e57825905fd05d4f02e53a21b7aea582c2349e750370406e97b3ce971d561691876c0c992950db16cd05c786618cffaa8e9f3a63b9ced6782cc69f8425274a5c236513eaf830fd370b4460e5b0ccdd066e50ddce13779391d210cf4bf524152e92569d8ce830c1f0f29a1c5c9ea885301fd0abe33ac10858cd6849181f9982fe2c8f169da90047d61c3cef15c70784541d5275ac99402dea98a8471955f974d09761f55e4bc0150bc778c4889bd89842d8b563313d597a1c9f8f89ce41205be2257a45217f7079007efe31adeec3e1cdc479780cb46a8a61cbf88de3394edf91c0c368f424904f8e7748274b801e514ef3a9df2bd74b60c2d4553ede7acc29a6e18734e182906722e39106b6b406c5928286a94340b70661d1055d0ad9205a2e71839981f330ea0d62f0f80c155b9e20606e180119bcc506c98cd5e06867b3a8acdbf8c3ea009901f82662380e9240cba964e79a82ea5bea8d56fffe907fe994d07f23e9762051cb0aa362ca0f716d473c18239a38bba97624b04d537b95710b260b12dcbb1010030b4537d1f7e4996b14ec7991fb0d9e5a9263863c2c1d72fcc4e38777ebf0f86bec748b36497b57459eb23bbeca2fbcea0ab111ceb0994462c7e74e54fd106eb725875c68ac9647d543d6c9421bfd12ff9d5538bfc26f4fd234bedbb1a0701916b9385013a0dbb26087b32a74df8ba2fcbb706844d1c308675f742bdee1c04ee644952565eadbb1458593694682c1ad9f7cde09c02ee6a5c3dba92de016c0c0affdf6bf4b2ca84d0ffc8cc8753f4f3142557161a02c43d38124c04a9c2c50325cc9bf144cd479c764b44781e441702cefea42c6977aca1410a003ba9685e008240546f3fc9b3956ae459856803a27193179e8e108bcabccd2c38bc25a66d00f0ac9f5191b7b215d695f3b0c43f8af577b26e4f9c1426a43051d22e5a4cbe0912b27395b6d9785c822060d710b2e2cb87b41ebd70cf1bcb8ac66928452f030dab293cbf2c1ac966d311a79711a9f1c9df8e6848dcf5c60424cf4f7aad0265002555686be44257961baeb71087560aee3d842b84970bfd5125010d5891f85a0065471bedc4db39ba93ea8b729e62a72c02fa78d4fcb23c106ac4431e6e9adf08122717cdbd02c1be09b34a45f0314756109e0849ea60102e39599376e7f57c0b5bbf2c0e71fccc029139ab6bea7ceceb47935c3ef3f844c48a204f476d99ee0b19589c40beab8fa1bc474b5a85fb8177e42665ffcc21cf614f320f2212fc370451e9cc7cbfed55774cc19ba8e4eeba40b4fcdfff8027721e25ff1ab11ea6fc0696aeab993b2ba87dffa35c2620687f1081c79fc15b8964fe58636102c6225c04538ba9df4b089bde96357f1b7529167f606f1d22a191fdb698b6688cffe0aa07478ab4632eedc8a6fd73d532dc708c5993c2fdf629ab1ad39878b070daeef50161174141368451014097f5488c7e03181d0853f54d02e24e93267f54ca9bcd0d2298af2f543c5ef6e9850549e739f5e0ab0ad11392db95443f0dda8d3a94850df8f0198d55ee345c55a2db1c85320390e2154ecc7df786efd7f483b96b3a170de973d0ad93a6cce8c6642261b08a1f99da721d8d0b1aeeaaec4f26ce2a54db00cca418c80877ff5c5d391b38bc71f70755d197322c10c72536cb69c5d1d21b00e3df4614b4443d28db24d6e8a03e95cdf2467cd6741f017d76ce34f46585c30fc0ba9a74fcc1f3a4330a8945e848465f74a52beec2a4445773f4e651bd1f140ac6eb0cd47c6ad6c7d0a356b5315cea8dd2030b220e70b8d64d1096c19b3e8f0ed655ffe8ed4115ee277e974881aa42076731b4198c9c32a5673b86d4a3574103ea525dc4e406d75869d30a3803c5d2b0c86c5be4236ff534a84e5aad8162240d35b62e36979566e75d6793b21aa4602a8e8b2173624e62989f409a914b97b50ac872b72eebbe0aacd4d044fdfc0961c9d79644cd13d6b87ba4b8cb3452e9a90d0f779527631294f7a6f630f9c6065090f59eef2f5d6f65ed1545c78c7107e601f8538cff77718952c0b4d14d222604c649b238fa9dc3201487fabc4dc768130d22ba9103b06c5584c3bfde2ebc8b9c71f339e99c33ca0f45d4b0ee69a3797603d9a8009c7e4d29c7ea7ff3b21d46e8d64cb40c59dabd2e72e48d282f5b6acd08f307760cad0f368e9ee84404acc11ef5384ecabf20dc1b244f99754a63b2fc06b18fe28e7c26b2cba2ca8adf264598c2ee67857ffde4d1ed34080bbb36a54801bd3cc4c04016950a8768a4649e05c941b17cc790b9615d04bd69a8a05540420a16561d7010e9f1baf7cb230b30c07bfc93d7fcc7a6c0be20cf62c4ff3ac12d01537a9ba21209f62ef0d5f26373f3ee755a4a7d7ed51f4cbbc0433c0950ed930bcf65fc192a962cf9c5dfcf8d62969adc89045750a6a1010f0ec41f9027ffc40a7019a9ec3cacc52bfcdc3f0ffa16c27074295a6464c4bd2c49c075e15b0052099499a34575d4b7445b023d7802f010d081662ad83ca601599f15ed6c8140fd02ca088b91b9aa0135dd250a7e412b7a59cee80d9c1c9adb96c8c6177d17a64407c51b3c410c8c3c5962b855ba7cfef7868ea34cc132dab4258bf4dd6b45e7220cd718317c0c411199df2fb6716d8e870c582e299072021ed25769a60e3774f40a4f240fe027b405d6958241118d69224e408c29c6c538b4268125ee56e818f70f20dfec82fdc6879fa3e699ed77537ddefe769101dc6e9228cac80c7607bcf50e +generate_ring_signature d6ebf139a45ef1fa4b4be6a9def628430e9a0ece08b67e60646b44d7581f62fd 009fd2354de45de4f50f4171609d01f3af1da8c4a966e6613656ab7ba547bb4d 2 6f791430dc6089fe5726dcba4999c2f277ed2515275152c3615756c85ca7fce3 fb5ed1804e5b50fd5e0faea33284e7b91d9d881a69067cc49560ca97e1828d7b 689799e5ac8fae88a1fa82a03c0dec56a2ffa69f983d929bfcda6ea30e735f00 0 9a283b6603e26b20dbe9b1ccfb48e6bf5f601e73d61f57afd6b1bc43c9fb990d1737527183a77bf6011395e6aa9f6a15ea5a1d3fe58530f20436664f4ff46304c462be3389c4f8ae83c0f6cc1f614437547811a29abcc49f9ce5a6132b781e04d2c2fd70e4d109d76e235bd755759ec32d2b858bbad926f7d389dd64ed971b0d +generate_ring_signature 0ec44a65dc63613b9be298c6039f1c13f58071e1b8f642259fad92b82e70ea6b 9c1b7841db55bf2d432d77363bf8f7871946d1028ac185176bd270b9b1c8245a 22 0ac06d71a291bc9cb8a65166f6e267aa94f67345780fcc3a88990a1fa5ddbbae f4dc2655b78a75a5c02d1da6fadc0e7df8d10024cd2acdfa75d6b926d9df26f2 b898a4bc2437e9c7aca7ae42b40b9f55801251be6a22e06b35cfdb9b519fbb07 5fad8c5d05bbf6e210b426f1c85cdf65c610189659bc1dee66cb48e155b4848e 06c86b3de6e5b17a6b18bcc3bea96bba1f0e53011abdfc8d983e7128686aa565 1981523626d0ebc509c4975cfe7855f5935518d53bfbd8020103875bb04c54cf 71c3b087b4ac47bbf525fbe96f827dbb8ff8cf477ec15e188df1f7468f12319d 76ab284bb2b33394e49a7b9b6ab4eb0fa9175f8cc0549099afef7a425cfdee73 035ae37614df557e2879632c43dcb2a6e731f45ed71d4c24813a2fd0b33e27fe 4619e99967c91eddc5bbb3326d8bafb2d91a69e99120dd426dfdbf0fece87b2f 6c25581aef5948e25e70cacf2143b8aa5d09c4f0f71d6aa72b8f97acf504c9a1 035708993aa26e4a03188068a9053b2c4daaf5e7fb6fb5b63e4c5b3d5fced851 7046c37e9f70f71033fd835f432553e867df9ce1f42bf67e883fd1eaaf6fcd91 fc9c1fa387d94e42b000d5196cd36b4d9ae84e1a4cf2de91dd15a8e9c655995c 80c9ac968a6a22f994705609c7f829f80e9c56786ea24c1048814c9bd8ae39a8 4d7eee539bdc72685a3e4239f78c67756fe3972e1b88ec61f5ce7de6e90cbca2 9f080a14a10352a412dc2067d03db1e23a55fc00e1c90ebe2e3edf4c9067f8ea f124e82150067aef668390263f418cf8bd5ce9a01fb2907e6847231178f4cedc d28e3033f536023ea168cc178fd5c9a9e3ed795f8598f7cfbb973122a860e43c b501e956550a6e563a89978557633f9d61ede3fdcb4f5f73b10fd98961369c19 a7c9d688441935ef112dd091d6f934bd055fdc508e39f78035da02849b5e374e 5585b13645eb85ee005065fdebdd45914a3afbefcfa4d5aac0b2807c21bfe0d5 ef411cbe80bc6bdf3cf2c9ac140669cccc0c9355fbd42d32c545a67b3bc96a02 0 acb7f8d139d3f0606e0bc120333cfa89b0525e00cfc0817e65a40cb476d3ab022594bc42a7ddd5aca68f91402b3b8103d452554703bfb40fbd1a5bd901a36e01fcfec231b79083ae656e0eed52934f0e16f078e32b222e745df0c29cf5dfd205146c72d81ef244508d263053f8e9d5d78d9fabc095c970ec8901764f4b44590ce37a3d763ac1dbad2adc2b03f7cf2e71366648e9f33a83bc83145fb0ce145c0d5deff855c81d8d6db6fafa8d8d0dfd41a4cc90eccc66c0a78be5f2f7ced8fe0f1bc84892f4a08a764e2b240fcadec69dcded216d15ac6e006436d7474b0b1a007ff1f544c52886bf6592c3457753a60c7767a983876579ab60e02460a6c217062efe9cfa3cb22e648c370c7182b779241316033b7421bb25377af4e81465d80a7b4a5d151428bd4a6c4284df63d3f5faad79c310e26f31ddc8ac02c998eb7a086dd6e13ccda11d5d90961ac81859be8143ffd28224ce332460d134f1d702cf0c4d64014eecdf7a881522ec826decc97ac32eddb4edc8ce0f57b3e0c1d6a14d0f414bc7fc28ed4a2aacfab900d476242eaa0015fd95f7a89d2ee1e562f91f6b0776eac2f17711261f9a820b200cadd522b11e4bc19a31e443a884a4edece40e0c658c9be300378b1108fa6ca8c178e65eae8c4e8881486d802705cb10f81bac01d60e58499a2e62435c653a41a1892addc2638756977196050ca196c64fe7980d36408819b9c71fbc83b1e3b753b155e2c4376738567da7f31fb75f26fdccb503edc4bd88f3701b6cf4e0503bffc578c5d112c6151bcc51da22e186380470900b95685260a428d08978cadf34b8c0c6499bae140b5a9bc5ab29dfd59ded9eae0cd3f1e02ae5a54143af1e23e12d8786febc69ae01730b1771947b0f4f280c5b0025363a6f88bbe38a2a13dab3ac92ff1b95002a81fe1c60a4272fd4ab22bbcb033dfd63cda389f41f75d968502efe105fd20d80676061835331ac556ab47bd6071188ac78b4cbd545c11f4d6a9ddb4df28382677e5da30301bca9fb95ee68a70b1acee24fef8fa2855b1ed26b80cdeb860087cbec760dba1dc911b161ac04d9074059bdfd8a0e2739c22032b92d6912f1617387027d3b677df2ad8ae660ae0c0dfacbe6bfdef0ee9fc15b73f3b462652b9a28a46c9ebd1df5c5e8d386a43b59003c808fa828c76f556d5fca6a30e06f518194f87edeeb7cd6a8b91d437121da04a7147e686741cfe5dc9f49711d3227b12395bafce9def7b98319888ca1b85d04ccf772ac7af47434d132f15d201b95508a57cf531a69643d03edcc115444c3031ec3d5a5482d1b2943e433a5d3213287ddc363f64203c920049e6b98c433bd0b0f818535d7f72d565c5e474f4450087f490db02366339963859d937f85782502bc24604022f7a3380e707644219bf7216d42f85f9d10db65d44ad53d37bc430a336f5546c949e67aaf8c55d74d165f7bb3096123702872f96dd5935d8e147709f3b9934a9f54a2562d151eb58f603f0cc86afbc69358175a38ea8ca3a38f8706317bf73b80204c0e010eeaac2f076259ef551b1bbe8a6dce11aff53072aa2e08a25348b750163413ee37eb5653b53b255c8ab6f9c024d8b07dbcbf31e835740fd5ee986edc0638378198aab5eb3096571ffd9bb5c38422fad73eb96767ef980a47f0b27e1caab297ba2df2e4901f6e509f8cc583c74394b6e15fde83816e2e013baa1cca3e88572af1e1a2d627bc0046ba83614ed490bd3133ec82458445fa0d366928a1fe6246745361f2090e329ac938e171108ed3816e9eeb3d2a247fa80e539911235a92d83c2cf52409dde29bf433e6471439470db6a1dbe2513ad9db0b0d8d5db3c70c26351fff2399d927a5124dc9d6eb818dbc773bfcf7bc94b370044d185865a3db9b23b42ac3b85dbc26b70260f6eb677571fcc7cc0e7def9c3107d223a558aad17d12b6a38b8b2d2fafa1751606a0524b0e5e4b78324298c2d100 +generate_ring_signature 93493d23fb44416ee09be43865923d0e1dc1066d56f5a3d0de81813af3ba0dbd 7748c0a5f3f87b8261d37a2fcf576f3641db82845d8c9baab1255a025e947fa2 27 2d160aedc903d73ca9290d7a6ab88c0e9f34d8d1eb85ff0c2123e1e619aab827 7bb969822d0b461e0f71f8582559be4532c9f9ba4b98a3bd8037f3ae2cc99086 3973974ed9fab1a8bfa2bd46370a22e6aa9ee366333379802af5647a360dd8c1 d6b7ed18d47a17ddb488bbf95b3af8435f2beecd767e1cef3bc7fc0a4ef7c4a2 0b30df77534bf83ade55d67c5525d900a8a7a4e45d22af5d3fa8036ab3ec3a28 7fbc57d8de03940bc6ee8cf3f5598cf04672d58cc53793c1f62e0f193a2cf264 300807e7fa48d0b41448f7307d2e53d080ca6da2dbe0b49856acbfb5ed7e126b b8caad285713bf9beda3f580194fd57152f67aec0e70a93c28928f5f486f59c6 a07af8e32900154e837d3b7b74d5eed2eea6b5fa431e09f4927256459a381021 73aa4e3f354b1059b14d1cd48f52502231c181cd06d3e377e93ff7d7e4493348 ce6d6cd79f36aa44b40bd05e6a087cde2fda0d82d4cdaa6e2b0cd36b0b3803a2 038030ecee860ada3ba07323cfef9534ea0a90d2b5d30f2af5cbadc1bef72e60 dea728c4b55cd4db70c5c69438fd75ea5180675a5b9d66c02fe1a7199e9bdb69 f27326d480de4e3b79274bd526dd79d24f11ebd046b9a8c5b376c44061d23a9f 136ba04808b1ddb0227d549f18e8322002c8f9e75600a3abd5c73559ce0260ea 601eed6733db13d4cb81bc73faf4f9f5af74ce56405f4bca0a048333bbc439e9 3c0c6f776ae4ee29c1c691d0b57309ed56b96a31a945079f41d3ea89e80f32d0 7249c2ae5af899b9283262f07f522602c6d1bf3d88ed92e0c1b9996d76f0447c 9518aee525652a3dcfa5a37d3844e7b2bc051db5335ea438673b94b01a04f8c9 2843e804e929b6da93b04026f44ee744eb364f20610f686d5a73fbc43b92663d 6b5281b6cd170d9cc7ba7bb56af507aa3dc21e816f0acd1659a908ea9a416366 ffe9b0f145fbc6ea4dce6273b27e9287654cfaf82accf33885fae7b152fca9ce d1bb3f05ab3be0fdee933d8492e1c5cc24e7488e44c4258dec75b4b650a9892d 8203602357f52070b4bcfc3c5240704ed87ecb93a0b4812512e3db7d9d6ed061 1568a18b7fc569e1cef7a211eabd5da6fd118cff94cd726d1b4d988598a51117 0080b6bfcc6e68ab3bd176987ec4f0ebfa08e2d5bd6901cbf24586bed9ecb4b6 6b608f086d9d4c67f3f1daf684659a389c5c9140c7b6e5ca71ab5b107cefdd79 dd1d64ade0adf36ab84cb537861c99b79a6d012ae1f994be411baa76db298b03 0 8b6d8b001f70d1cdea070a0526f5faee4f3e91bf70d8cd5a44625021631d1b0473e9eb251480a1f3abe25269431098399233c79ac1a0c7d6308a1202aa48d70a9ca77e496fe4ae604295d158a2ef4d1acbf51c991e1fefa33502c0434aac6d0a3545e701d7baa15c1d9bbfaf4881ec9edf22ec7a99d61f55dae9f8f44565e8044610f3791e03249cdcc28f52f23473dfe7bf651c53de381a64ba8c17592de408794478970be2bb5fa5e77f2aa3e2080e6bec490e5d2ba96d4e24c207adc4b30cbc7d9c6956c132dcf6123fffff763b9c7487ed53732b028b5b771650ba857e0f1a739d7343ab7ef4fb55a530450fd11d04eaa9ebd533815fa4a8e8210ad4800641337172b7f34c356ce0c7ef0fea248d2f5b92ecbad795faf3f6e30e4eba270cf0bf21e571523fa346935f0966ac8c1cf8b870ae7dffd6f8cbde8cf4fd126a07c91c38d7de3aea2f5246b6c6e2d45baeb3dfbaf6caaab1271c57b5c0097eeb0e6523d34601abf5bad92957419365dfa53dd33c1694f91a255ee5a6451fd3470b3498d34eacc0132518f74ade75deadb89c2c0e92004ce7349bdf0ef4077ea90ac40cf369d214d75adac60b0c7b03b18174e9ad037268d32bcb3f68eaa3f85e0770630cdfae50efb715a95739c01735c1ef461fa66f41f337450db30abcd75105d3cc27692f04d44d9b02b387e55e61ed7a936221c0ca532310dbf9dac51b2f006cf2249aa834c5e2d59b264e077dadb62367851b8e9cf8f67e1905f01ad98501f03220a4fbc719d29e963b2f4bb0c08441c68b30f6aba6eb8a946bbfedbf3c05471adb66caac242c0bc6763e09cc61458d50af2a71edf7a86d055f72f5b870029aad9f7320ff3124119ebfd9cc8f5c1cb3d9d7a77b8903dc03b4e68c5dd9e20ffef22824cac4e54d3cd025d472686bb3314373266f54ce21ba4ad4ffcf3fc50c86434358faf8671fe1039260497fb9d2b83b5519d6bcd235b9e44305c472f1028b6fcfc369349d2bef66664e6f66d5ddb5786cd1f436c5d2307300815481c603c63a95c24ea4ac6909ee1276459466758e52a74a0d6dbe374047e2ef0ec88c025e18e97152dfa259dc82b43525584a37f0f9984d31c287c9c4194ef1adbadb07f8e9e5525f40dc1fc2eb66c982b851c5c18f1673d9f50b4fa6b3178802ca62030aeb6a71a17dfa87d2ca5d0fd3994a404ad861d0e7f7c275c4d24fb555da49017decb2e1e7245c31ca84fb836b44b345c0a31ddd21773db5e423bac0fa438807820b849c9ba5eb3ea9d3cc7cbe945e22739bff621b23fbeec57cccc1486760043d92b3c66b3f97988de4477128b596af6115533d319bde580786202a0d5a2b0151cb97530bc5a5482818f88938825065e19d5c17a3d644f8f97d035c6618410b7a4ff378477a10c53a9a7b1268a5f8387a038bd4a7e9f51fc14daaf7ef8c830cec7e033717b9f8fe567754813ab4d253db95dd3b756d63491c5d6ecb4d203a0b24e629b047f0ecd9085457ddafba19230f8957005c2c35c8aec41145cd812b01887e37ff98d3191246d9918ffd8b6e1912e285d5650d0b2a38a3bda948502a0358fb25098336ac96d055dc0322ee7ca39f7544148523242148d9542f7c7dd90b3ea9a70098548621c098d7e055bc4199066bb0ff476358612388c9406d964200a9c1879892bb6c4196d9c8f51c66bdb0f465cb4c4dfbe3db874694104fe0c0089c568dde8721f75516688093312523400955ebc2d3f51d1485035555b131890f831edcf7fceaa7c628ce50bec8d3d83b9109b64398b633f6bdf1a9f20b552003b9c0adcd5860bab47e8961d5c4ae05d02819c8a761bdbe761e57bfadfe61a509ccbba8e53ab443f30c9d0b3d67f68281abae4e7e51bf11263f80b1c80d9eeb06642ef595a042a788b106ce3e3b7d3f8c88bb05e746434ce1dbd525c5a65a6006998c597fadafa011815319d24942132c3bdcc700d4ab3d6af8d249a567f6bd0062e5a8af71825f21b31cc2e343c8fbd927f212b9769cbdd202fe8de4e6eeb705c8fded12288c91aedc16249ff6737a32cc97b3190659649601f12e6044d56f02a01687e4a9df3afbbc3ea3f6c154a4d847a14aad1c856f8d119b38ae408c0a0028a4103a17afd564556db21fe908591b7dd6af25760089fc249da1e4e2cefd00989137f09498c2ee3a5b56638b7796511367a6f114f3893b843bcff886e4ad030975073c6a76e3e38f0753ed2960b5f7f85ece867f584df5d164cd86150b8805e3667802880c08f55681c311fcad9fd2fea62d2193590354e0aa451312fe4708acfad1a9a8e492cf3a03418f7613220f662fd56d4889936eaf6e0e2e3c401f0d88812640f02576d42f00235c72bf7a5fa6fc051e58b75d5bd07e98ed501d210598567534918f09ee5a554df4958b7a0979054905c0b378f61709977f5b2bbc08 +generate_ring_signature 47a17eef9aa4f45e349474251fa7db40f4dc3b2d9cb74a0f63628730e4f087fa 67206377b4229f14b901ee725025ad9067500f80708603d343cde543f6220070 16 b9d6ae2e4275913b39ded0e04f23f415e21d0755e6f5f15398ce24db187b80be 7443760d12012330c27c0d958be3f5bf3c543ea087b2e710f1df3aea40a3597d d58c7950dfa2cd1e5d5958b03665844c4ec07d3266238af73c026b2df9c9d749 22c2ac91231c9618673ee8d8cd01a3fbfc915f6da971019e5ed1c04cb848eaf6 bf376c083d6e0298d8e0bee38fc8a296ff9db39661ff0acac51a7cae47a528ba 4d84063c54fc35ef611c69679d6bf8252aff307262372e127ceb6ce28a4eb57a 191d335866139d9a9d70172dcb623d9d0d589bcfaf29d639db9408105350a6e7 16d1388ac89e6e7be13d79c597bab902ae51c727f4003e0e32d6a9944155f5eb 7161b56af7340827dceb9215c1ee38f21479d7deb8ca9aad4cadad12dd190762 eadf42743e1b0f76badbde7f1b5600c599eff2855e4af3ba1e832b56c745121f 0113294d12228749802c3502457a95df6875abd7dd63631232e319cda4ea7176 517b3628d5b1f6a781dc2a65084e515d45f745a55acf671b478d2553006ae2fe d1a16b1897c1379411ea2bad7ff86ac19c519141174182de83c372da21e04676 9c6e206bf2d21ec5f36fdc489e51f9748d748a8064e5ec0ed21e7210b6075d71 fa2fe7158dfba5f1083ff8db88ca7510fa17a08da7da486f3194297ed4ed0afd d467bdfb269e9f5e8c72cf6c27b5c7eb4c2beccacd7c0fa37d22cdbe2d15807b 02584ea85f70c3ef4912ee658341e6b6dba3f2b9e3cef23d99ced95aa50d680b 8 9b503abf4e563414238704dc7027c02e116c6880f42513268ea30673efbadb07021964f0f109e9d3d50b4c9ec2033e0b3bfec3bb6e7d4c5bd943105087755507556460633cc8f4b4876f2d1b7a37ce5da9787e09b842f2f2f461846840439a0fbd7491c068b496fd622d9bb3e4d64161d944c60322f3db0ddecb6b459707260000155b55012d89eb61c830a798e591bdfd7ff132302023bd89d77896a4ccf30017d244663479f9bcf7b38261fd8bd2888560997efe5bf6a5ae665b9dc803590b35c3e36d2b24f398ff7c75607088095d86fe956d26ce9aee10749c734c83b40abf88a1ab1ece77643c87851229645cad07b04a029c01b977a203ea9841bb1c077ba9a319937d126de3828e1fd3a43d33f7f925a4ee2487f81dfe4bb6d7e2a5092fb00260d1b6d95a26596edff3da72011a923132b3b8e7cb743947750e1a29061dd223b2432bb5dc6a91a3efb5a887907c201d2402f5479267a56bb356bbf50b8906c1550f06534c2ded19c9991b12dde68594d8787085b37c51e70728ac300754c61a0e658d279a102a6b9ae398d8804be338c518e9da6a9a641b998aefcb074213a1e45a946c4a9cb136d27e02c72be54be5468da5f522e73f52b43266f20e6845b6b2a2594f5239abf145b596b68455d8430a90785c940ee91b06dcee0a071c2870d3a2df188daf811a19ae31342efa8f1d5b7ffb8f72e056d5dfbe52430e966f24b3a0c910af2aad898be8e0b5f6afdc829a1fc3dbadc0ceeea9efefcd012924525f78bd6b5e484257a13fd6c7db0a1356374dadf98a21438c140a16b8043212a928fa682499556f8b39497f31244ee070f128bcd0016cd8eb35d97bc10d9d117d43edf0748ca7c1f7d7c6cbfb43fa47b216aa166a5b2e8c6ab4da27df0190e0faff082ec92633eca71f1f53b61d4dec4828502841588eb1bd3d31c5560b563297de39119549e7d71a5656e24c80ebdd2380a7d435fe4e55b136345065001de98a6d10ca53244f09be66917bd3c9d89f682faef90652981274fb4912c309474f72d315ac5ac95591ed34d4c78c7ffb9c74de7e299d3c9515bb5bf2ce7a0832168d20fc064ce000f715560de40a22c3ecf058be09fd7d56d5a0dfe7df43076b9f5161d34119f4d3300b8e9a670d810681eae268b8c88f2f70b2d4d428b2074b60718054e8e943ee4db73faf8b5794175140242b890298ddc98e5ba9032405e1a2b6be0449f24468121c5c152a44bc3ab199bacf1eac52487db43ee73bed0aa81e8a795062d13cc1f27ae64ee03c8b863a77c53bf392b796690b2bdca1540442c85917d0dab2184777c22e29ce4e53eb0df6c9838f6d3ee0af00993a8f45074d719fa5e4b8971b942d584033b5d67af1357be6a835ae3f06bc26455ba1b60e6b891d71de1f5336c9b92747585de5c9dd005113cd54687d19eb71c1b5eb360b +generate_ring_signature 9a7086a8ef73356a7a113ae91f791ea57a32bf63cb15766556218bcb2f7582fe 9eb46385239b056e240bc6a1180c51a025b0837da355c5d60055da138d05cf09 75 d716b1fb66fc9eb0d801d13e48a4795600b8afd004cb31bd7bb69ac7bf26a5b8 facaa9a563716b9d22e3e8204e8c38ab90e4329812d9bf33dcf49322105ef8bb 092504da28d4089cf7fe33cbd6cdcfa5ebf9ece75d42805975564bdcccf34497 893aac484591284d041d6cab92ad3a0e187834520d90b3a6686276070ac380a7 4e8670484c0779ba9975ac8bf55da8ae29d278a0334b23e670fac6eff5bc133a 7fe32c5fc50564dd44faae596d56dcb817232c1e50385f248e6dabcf382d0d20 5560a7525b10a96fb989ffffaf9c2a20291d0d28be117be81cf7292344c26a89 7abc90f8cf53e7b31349994334ab3388d209b38b0a1747d656b064255933b80d 4b5931de92f69f1272ace927b49149cc942276296ae99a42e52d7d83de3fd414 a2e67069563571d1eb1f16694869da1a9582511ea81b005eecaf1f63adcd779d 23c1f904a58e6ca0d91c4caa3dbd7cd5baae653299b339f9859ce870200db57c 19d779d89ba045aa643e717f548b247156a41d35e7be773d5775eb452587d44d 68e999d5a013b3d945434a46adacd5a2ef18ced42ee0259d949aaea9f06479d4 f8c85c1538d967eb627ec666563cfa8203b978a8f88a236f4fbe179fa791c596 19909e7839625dbff33ff26ed186110acd7b34179b0e55fafc84a71b7a3f7e31 53312455ea259a3056ef247f4b25e63a807fd2fb463b0ffe1417cff46ff4dfdc 3380930deba9c016b036d71ca525f349ddbf582ecc80ed127da3272f6aeea4ac dbde3d341c05dd02ad333c64a9bf35866b1f44da659a07135875cc65b4d65439 06577af36ac3f0ac371d8a2167f637c741b9d18a79afedafed8e943a081733d7 944ef7b3bab2fd90c5a46437b068c23d4a5074ccf0d0a6fcb960ed9763c0b880 01cd86208d37c5512d16d459a4581904d13eb862fbf9e34c24e8a1c3233da0eb 8a10901b4799674e91e6c496deca1f904b720dca6c3a81364bc2ff03a7e57897 339b3dac7330ec98d76e59a6e414629a1cd465d14402c6404c9655036cb5a010 c0f2a7cacf16277a4c84b115d21db048e8180a15b013af3f295739cb0e5e8c2b 9bb9566b6ebb7e992a2e6138c810873127f3406860cec5d3e867f676f23cbb73 3a31153daf08deaee78252f49b658c875d195343bae2a187c16465b4615b64a8 5047c33140837bc18466fc3d1e178179768860faeb7ff166f65450d53c7dd290 bc66bc18c1d6601746fdbf34d4f9c4e7213079d15eb054cf570edbb66ad70591 fdfdd237e667d70a530fc4f387af0473f99a1f2988dc480ac1606df7798798d3 d982e4a92446e08a2e9dd91b155e580f3d181bde1d5f5e69a6cf5e82d35607c5 f8b566a67779c1ece46c432d20b280c5cb8da1184f7eb242c33759434d7fcbc1 6fcc2b37559dd4cfc22abb8b5fa07eedca9d56ade5372f2e4a038ad1d0b92c0b 15ba3fe0679958f07c6deb5369d356b48b668ec67d4832ad33ff37b0abcc629c bc70e3eae472a511ab3e547f91aefd53e40dccb0b030fe7f0f5c382f41337be6 e74bbca25c9180dbf84ee1feacbb9e6d0514cb6acadfc7fe9269d8f1441c4004 0a681694b9181b8b0eee8388c9714f2920c63b46ddc955643d9ec4781c2a820b 377e5e05a7d1fda80259beb3d2d4067cf955e08089a74a45b460799745a52532 198981c0b8d166af3bff21e762bd85cfaa43cd7241d7fa611905b4fcc67d6236 1c533cc6c0bb0f4eeb97e36be210351f39b1a4ab10b7a339e92aa187a6e08679 955803e9b32dbf08ea6a31dcb5bfa460976d058cd5d35fa2cdbd446dd112b3b6 b6eff72cacb508187b555d67f22abb6e73ef2528404ddbf17411ac4510c44fa6 f55361c4f5dda43876eca6af215d10fab43d1fa4f029a370a08996e08fe5610c 38d3a3e8c7d263cf018d96a9c81737c34aea0b2d3092b63d79ca96a2289046e7 5a6424e37427e45b539c65f46272da6b578868ba83a76e6a0ac240c3004ccba0 80d9e5a24fa55306544acb007c88c87a5c65b6f3f814d3138b56d914a7c1f046 7635087c2fbf829094a693322f3f3ade9c719ffb418e5265c0186f4604a294f9 00ce84cfb403560546340864755a5e399e5b2c061645bb7c12a7c99b6098ae82 dcf0053ad01d6cb871a35dd4f255956e50f487ed81b0135862982eb4843df3e1 ae49f8be1790b697b4e8dadcfdcb5786ae07e2623798d494c18f22181ee1d4f2 36cf774f577b9ed47efc0b0b8d38dffc78e6372ff1c3326e336b71600789f487 e30e26abc6a8adc3d0e8a226fee82efdb0462b698b98a6a1878c640cb1cbed61 11ecb444c49b4a7571625fd56f8e30c9008cdb5ca45acbb8d8b716480ca9bd6a b89f7406af985457210d4e406755e995cc07515a414c0c6f751c4269ecf03eaf 7456e8c3d729f835db509c26a5b20a9349d73cd5a59c2f09978df89650f7db88 378ad7b8655c6acb0309dee66ac1cbf99454869528c5cd45b17cae0386db60c8 86cd4f6a7bdd988a848508a368343b0fadc1193111fa561687db3cac67e2696e b62cc61b3a0e5cc7b99bf1107caf5d810d95ddd4102ed701d862426639bc3735 01b43550d9efc0413fededc10d91ae556a36c29151bf36e94c391f162ca325ca 75c92f49af4ba741ad911d40d2c4e021927009b71602601d9432468069caaed0 4952e76de4f8e470060e655a18c697523d7ae0563ed61dc70a54cc3830e3c988 562ef593d5dd96f709bb82c1b59ae43ea34b365a05026a7344010abd6c419fbd 4699941b91092f0cf560e7f9f35db0295f952f11c5821635676538495f663201 ea0d5276b2de554255c56fc0589e3795fcc9f39234b354ad8cecc554b7b8f6bd 685df7e6f427618f22fd206f70ae5b44d71f408de837502005777fdd92bbb4bf f364ed496716cd88da9cd2b9b404f620868e8195de8d2f367f2c4c80e708a16a c7c368adbe6409674f1a2fc603ae4fdc1a7cb31ccead9b6e1443a6bd90fb35be 248ec8d63f6c5cf526819521d866ab1bbdb6cfa84b0e87dd8a7ad7d1a47a841a 4dc9ca992f062b7a011c372b3e99f87d54de6883627d42cc97bc7a576a551d1b 3dcfa728ad76ab77e634add3524207937374f3eeee11a566b5babddedb647fd7 77fb0e227ad84997ef3a5f33c800b7ad6033aeb0f3eca8d688abc8008dba62c8 b8990b5dfe159a6db927724d9572270071593aadbed7d4e9b9acc4a1458afb70 72886ce644da16d21a4eab8b7d00dd65f639a40a5dea3579ad7923cede155007 6ce176ac239a4afd1d9f61f7cea9286ee6b5a6c3f5d8c76c02a3e6c59600deaf bb67aec3e0b94a73aea72380edf585f9c47232f471b81ba1725beb05d6352cdd bd4034fd4c3d280c619ab29786e48bcf7bcc8e839941cac09d57eee9a67f0cf1 2b9967f5aca1eb4e0601d393b1989438c1934a0b0dbdf515637c2fc7c9709a02 17 49f32c1a4ea7f629cbd25ce611e62f3b1f15afb097b741be253699e7af38b208f4ceecdcaf0859bc947bfd19bca13eb0c4a79edbe179dadfe60ce6925365ac041554d97ca72204756d8094425d7fcc47ecb56159b9ba6ac3013f56c9ad7ff50e28abb30e9fb96eef331d2c30cf01a2e01228743fd427c161e4c406c952979d03683d5a04f98be9270e2d72a7e7508d47393d59cc245a4e4375836f5ebb50220fde69ddc39b6620e6fb367220226a779f9d48c43c2341aea8f494335ea6fdee0228b59bac066663f519221be8243748e672d8869ed6c4854bb55c285d984889028b4f3922d68ea29a4a78d70c7cf6fc5e0870595d6a1b637a0a0282574146bf0ef2262fa30bfbd262496b950705c1f45a0721ca59cc99b759650aa806edf7750e5936ebd37c845bd38b24de7e811148c4056b75a073afa81a5b300ec67430940cc3d689e46669af53d9005e5ded7d4540e98d52ac6f8b94d8ca8b748de3b8bd06132f3b781124bdfbf03f3a15723c07b834c5e8e2e9781b9b130f4a284e850b073ed20dd04344d534a3f034cbb5071e827412c0dcf88af33319352fc128f0ee037bfc2b3b010b1f8b1aa340d9716b8ba9a819c0eb220776ef2ef67ea60567e008717d735093ed42396a08f82d55681f35968bd891c3a02460a46e6a090260500bdc01337b050806ee9565f9cfcb1321da58c19fded8de7dbfac35c15f7287b90ee07e538d31607c23bd31a9c53972dcbc824deab45342d41b5c3fc28dfe731f0dd161b6440fd69ab03d6a7dcf8dc47ffea48cf0624f8160a1c817d0b4d2054a087275210884126315d5b537490a67f851da27834a40d0289593c5cccfff3fdb0510721ae833298b06b1be4b23d77dc510255d7cd6284d5d67e585d0071ee3090ebcad8c7deb1ff30e3a04c4f0455f559351277b977c2d6d926a0c7b9c55bcbc067d4664557df0bb5f48e30873f29005b53a1ba5e36ce7541aad33712e92642505b5454547505d08a28b3f944721f4db925328997e45d25757e6b1afc18454270e71aebdbc5497ea2ca6e11da434c39954828fbcb0cfebc32662af2b37faa74a02c4e313a0ea1b35a94e77ed6fe0123a39516d7d7317937c1b1e6f2cc3895d6c0f93a983f7c0ba910d408aeaf7d4e9c0b775d57cc2a81af7cc1aec102fc786580b7a75238b2bd01a3f164ac617e58b9673280131b43240e850ace213c2c08ed6094bc7fd379452ba684f1e2ed6184aef38751c53d70f3b11180e1452a91ac24b0591c2a27746d6c020d3d156b9dc1fc5633bad202cdb67fcf20ac65ec6061c470c537b092b97f25e9617f54b31357f009265eae4bfd2a8dd2d8586b0517470d301456358a1b1ef9ac19879b6c781ab2ed5931760b1da7c6045cbfa8ac216a4130fef6f678681b13a8b38a5c34aafc4bd70fcfe996d29d00b7754c4b4a014abab0e037536370b390b09838efea5d673f1ae4dd5dfc832fdcf51303259ecfe7a6a0dbbebdac8e4e06ff5cfaebd35934c4ea095c6a7d9301c99a4db0f335daecfba0bd12de94aff1a34e342d3f473d9d5fbe2334182fb83df80f61a679b06b1d1060978b857bcbeb5840c8986f2c5b9acea2ce3f2a3dd7ee5c2488d3e521f7a7f5006281b3440a6552f990db9c07409b5813d1b15169b771d50a6e68077f3eddd6f08454a80f3321a3e77a56d091ec57547e95509cfbf41fd3ae364387073549162056ac4bfb296d50dc52ab4a73e32cba3f99d1bfa92fd0dba27fd3f7fa25089fc0b9f4233b85c338e10e96a0c755ad6d8f5073e19882753ddc2eff46d1eb295060661339564afe2b84312162545e634784144063d993112f8ed45f4084d24affa083c5b72117d290735121aec439f4626046066e9dc30106c8b682aedce0e2c410d552e53a83e5aead11edfc421e2778e09500bae95687fac02b68facc7c5dcde02d996fcfb0c95c49b627d1bd3493c5648135ba09ae031a8303b87ed82ae746f01eb396ad97b5c6e971e63f425581d4d41ddf58f2d5d834e7c7c6229dd7f01fa0df7cec1c568d6b1efec96bd5267a061291b1bd4b85196f77232a235bc94fefb0cc4b53a739609476371765654b03bc91086ce943cb6e41ada1200237034cbbe08982830f70395059a92f1fccc3152c3fa18573c0936f7bf927907046f71a1260ff9fe34d659ebc980e5680ee03ef8228c76beacf36013427e6e21a6eafca14101d7fe079ff665a57ee84780411543981440343c4a9860a34a30d057eb98d44a0de3f58f1ff763ceac4f192de936719ea5015e604680db535f8299939aa1a03c0866fdb1d6b276babc2589bd7c009bf0f46c96dba0e24d0e2113046fa39c55aa003f61275c78362fafc8f1f880622f1f638f03bc176e4c20892b9a6829b43d9b0d791c5b1f356a31e2a7b727d38fd61fbe7a2ec653c3ce835fa8ced27381bff604663d86e9c855285d9b35213cda849efab1fba58f162a3db7ef7f7b7e95a90803afbb23f999bb012b0b9ac0728032fd6e35c55eb23856075eeb520ad9f71d710c7de491ab9c2cb5d6a034206d60c0249715f369e75b14c019ad452aff4d478a059295984afdd6ed2dae90a055443106586a2c97736c60de180b1030ef84d90f0522d47943d6c8673425e1acba41b062c97277d69efc842e4e46690a69e1a4020f5acc288c518416192370aac4da4e984ca9fb8ebb1cf23fac7412daed53a2060272b2124f671fd583527c2c52de057718264fe3aed450e3050a62a488dc7c6301630c13e084d3f15362a4618f3f6393d888664d4865f32bb63834c71d74c9fa07bec0314d8b6f1d925094c4a7cf98ac84464bad2793009c723128e07fab983b0e5d2b3bad4a8e28b23de778e32d5e3fff8c813097ecfe8d1ee777c10753481f0d2053e080463c670ea38112dd3494f8a6d9c01d6bcc10a20a6665a99ebcc3100559a0fd1e43e29b7b4a922dcf8ed35ceaa7e04f3cf144e90db0d758cc5107600c86b289b70b54f87dca3cd81437f5e9f45d06a332cbb107d8ec01f3d1b3b74801eaffa35f18bb3fcd209f55d5f255ca4275c39deec9dc29dfd7a9831884acb000506b821849582959ab0279ae00513476088d532960b778e627a8a79c7d0dd60ba676bafea5c6e6260b2f38953d847f560b4661c0cb91f8b5bd113c297d35800e0a6ffcf2ceb318a25b93d7acc2057d5edfc955e5856fd5555683b5c181f9290b4da2ac34e6fca822dcf8e406b769cb85615f02c82baacebfb7c24bf868e5dc0fc8f8f9c5f371b2060b7a1707aba634581626fe4566874e6a5dbb5b34f85ac300f3ea63612aab3df71c36cabcecde86dc36d241d84e102871a4060018bb825e0bcd5ab727c7adb2c2e5042a41a7b5e7fc32033405534fcce627f67d9a61853000a09dcc2a6f7ca3b885ccde29fd51160ae7e778f42581a62d806c71ffea308c03d475b6e100379fa04a22f1887c94c71856a9eb2f1f0cb0529d10bf3a25afdf0b7c9baa29a2c1a4290e48a947fe52eae74755df1db952b83b3f0e0eeda7d7cc081e58a96cfdb229ff1067364ab778527299238252c2f5b6b54ce52b404a58b00d3f116e0a92e439730e43afb362be35141f8ebe7e836104938d345ac9bdeea204e0721b64f3ee540b6b48acd683a79fef79d4e1419dc82e58a64b4882afcccf01b16a446559d396cd81becc7561d45db06dba2b2b1664eea21de556ba5a51680b09a89d80c6d52c9102633385707b48794e962821ef884462782c972e964d5a062b1550a9d385743ba10ce6c1c01e222334a61bd6a6eb03df16bd3941e61fe601632191d08465fd08baaf22f7b0f9741c2043c805f58a1481db63931d5b12ae0838c8911d1a3fad7d6721b7bc02ffb0af8f5a163bafc414f7acc54d145e4f430a58abdb5b50a7d944a0f154e50869438526d10c987577134689ac8d2a98b90c01a9d705db5056137206bf5d2854631769970e979efd3d23154f7be752ec3cec009d4a6284e4852830837f908fcd21fe7b8f2d7b9675841d14e91e35e98320bd0339c36f4423cdfc798d01950e5ccb547dc5b45ee27f578e4d22f808ffb37039092b188f0fd24eea22ef91cb20a57d8ef85db92591929af96e47641ee8c478140b148f56e466b5ad3ace2e200a8d900c77bca53936944de83f1e99e4eab716440ff391d234ce1f0505750548df78274166b79a7430d0d3d79a589e9f9f71bcd507c75a0b61bcae847371a9d38a21e129eef8ce0ca16bd069e142e88cbfe829ab080cc2a7d2e8fdc80d3fe0a9026183c2e1c87c4a47dbdd82a92be831b4117fda028701ef5c1103fdba5ef685de1c36750bb7324b12e1816457b329027bdfb1ca0525dbd465789a2fc8213f01b30c3533e03449afb949158683f9f864517584b905ae46072de09885fd53a7c56f03b596988014d0797724e3404216daa4bdc8970715a79457ff653aeb5f8eada9a45bdb12ce7077d1b8bc4f8b00a83e08198c290022b242d7b1f2325acb8cd457774ac9f54fd12233a8006171175e588af6e75d090c8f4c9d44ee19d95e418d6801841e5a74d16ceb29313e8d6413caa28ec23905c05f3022e84c1ff7c14603540f56bec6f2c21c90f5af510dfbc8c6a55045850671536c95a00fa68817b05f2828342a862b5cdf318869bd45ee4146324ba4c508603c1eb26e97e995e1377e2f6cc06ec03d430a3bb12090e5934ad653bd4c1a0150fcdf100b7459fb95e7047090e824922b2ad57a8a0539a7a2e2ce9d87f88506f972372b99f49bb3462f34e400b3b115555be7ea58c9e5c0b203c80846e2ee0c35dcf7c756aee357a62bd3f7118f36e1ad541635cd4c3746c7f11fc4ee9a9b01ba4fe13dc2997be0114fc7c69aa617bb39c6ef84a1f1839b3210e3f128de1e0f8d4a4520ce53fc5eceb402d1de6230ccb68b2de43071137aa36c592a6bfe62058bdf57169725ab6f2c925e75431cbe3d992d463e7d6d2d3f60dcd8717d9deb026556ba6d14b5cc4f73d844cbf9f8ef77536334ca7984a7bd0947bde09361870fa25a6ce41f7ab117188545b5cf311b1d88415f2f053443b6c6285a3def1df603d692eaa79c0e5389674dae2669063595257db7e3ce2598c0c9c8440553ec4305ce3d0bda6f4c05f9daf2214858b8b39d2f28df1949941b651b77e1128c0e89099440b8b0e843f0a29bacbe0118749993f1fb801f72c22280b4dcfce350c93b012f9867af2a83abe72d9e6ab2ad0e597c7b9e45d2b46f46588eda644ccab2a8020f6ac108ffd076f6c0516bcd0d550fff5da3b14aabd29f4c27335b0ab4932d00478058792e7e8da0cd843c3798a3521f8c270f0ec3bad76b20c7918622c18b0ef7f7c65665b2fc39192bdfdd372d7377019215546fd7d5499d67095c2f094909c77238e7301ea963097ee3f11fe8f4615c72db8f0fd8a550bd406a4500383b0698026ec113bef8a794dd4ffd6f08c8752850abf701adca05aac2a34e7daaa001eb1bf39d35d136bcc60c8e6b836234b79a86c72fac01b1b4013b3c6d81358c01c95fcb809430c91c9bfffa3c61e2bb89c06a80a9055303e59316191a6751d00f99465e18f23680454247d470366084811d7e1f464ca01912bc94f2cc624e0909346e8948f995d9e59f7d30dc5b6e40f3ae2c60668d5a79c91b1cd66b97d9800b438ca0e5ce3ba16da904fe02cfce462ceb9e8aab7a90e7bdc045204e6e84d60c6c17b4a3b647b6e446f19c8fb0c6e24a74e03ea8deb9e57999a532fddc9b8a044f0097570277084dd23fe2ab782a19dd0934a4b0f7378af215e345c7da9cd90a6d17b682725c1f50ebe09f2674368f725d6bdb7612f5a154f4c1fc2c97545a03e001eaf937778ed931dcdfd549732c5698a8b92e0fcd2fef8e328f73a9634c00c11ba0239ca60a4ba2cd69b37607845ec81ada0d0701a5ec53055ce3cf36c40650bb26f85eae2548b791cee8e606a99827ba4cba107de05c84afdd24b522fc0251a40917fa846e8de5341f3fb24714329b21263577bbcdce2ebbfd8125fca002e56d4485c57caf8748ae6462840f01bc159f011d18c752475836e6376a548307771afc8c206fc57833b8bbc5888d8cbc484525575afb66e70ab5021323743309ef0b032c1e16b039ead8d87b411f24a4e0be3fa47c21f610840736a7c7c32f071e062cb729d351b92311c2cd074ecc7179c374a0015b5ee84b493e897cff9d0a53b69db8049c710282db431a3bb00ff4fc04e30d4d745248812cdd4de9baaf07af2384d96b8c48e3254c65e646a2b9ae57476ba0c7eb75730a471ca6e997eb0d289e832fc38f939fe53162f7f39f22ae71c6b26a13baa1baf00f1519220ba004bfe9c21488408f71b1e9b05c9d69ecda5152a6400815b12d8b4db63b90375a0ba7bce7dd7ee54ae6f80d7e501ab6d6ba32b61e4062d0f3cd73b22b8fe9734e02403e509f00431f8cad1a39e140abc67236fa1ee226b62c54c8b729e0ac434b03f2d52417546006503061a950364141174ef086fa164bed502dbd1e11692bc60ae209d78244595bb90950056f0ed002ed9b7910f5ee350de05443ba6beab04e087706dfeb36744085f5202772d91dc13704390e887f7cd416048ff1151342090d6b665088d1c17286cdb3fc21430f00c06a1852b201c9e8f892f76a5c2a68740a57cd673d402e34bff1f2b6ba4332230175a4ebd0a497b022a14736d5d6d73203c9bcdcaf79cd226fb84c88295176715e7f717ae8fb623f9921eea1a30620070ba4db3b822878b6105d31818f8188993d31716893327e1ae9159b3efad6b0f702 +generate_ring_signature 9d3b5964da0a0dafb5a05244d09c44cc052535bf41794549aec9b85b7a238998 b7d7b25d39766c953ee51d4057e3746b82c88731470657ffce7f90bba173c870 1 a0dd3438cf05d1a81fcb179ce77f0a61b8a47525d2faf17808662ae3d2127d4a 9046bd3b0ee4f3b9f6874301485110cc7a5af1806eb93acef62718598a837d08 0 e47a594a30afb2de7cf2dc87ccdbcd957092e9a5b5b98f73b987349149aa6a0efcda77cc3bb88651c4bd6fae17a7e444d00029db1d93734ed31d159dabea8c09 +generate_ring_signature 36c2cbcbb47f9040cb04a7e8e1bd128dd73240ae49d6a91e793c624f9a1b5680 faec603891f4e9da1cdd3e6d205a4173aa202867a1eb5959cbc9e93f353f36ed 2 2d266206059c398811fc3ec5e99c37dc51af758c47f094bcc5354884fcf0e83b b76f2f51b0e270cebb200499466d003705c19d8589e62d6f2abce941455aad68 5b96fa1f454618fba2f17fffe6a26238269fbc0cc08b9361aa03c451d1fed50f 0 5e167afa304f84ff0d7940faebde8dace2a4d125d9c3ce4ef3c6cd98e8ea090999e8891bc94cba5b6e57ad4638be74413a0f4b6edfc93f392a17b38ae1b9750184f233618bd9f0105c19f44f67f8ae9e0f2284745935266a0a13f0be535efd09b2ba718e4fb68e05db1b9f1080411f3cbaf0e70cd0e3ec29242fba231c91f804 +generate_ring_signature 8ec53a5456f968a3c524f9096eaefd8df6bac7b84fa3a94d0bea97696520c66c e55faa7e53c815159484d92d413bcda25408c57907fb97debcc43b82bb72454c 81 4e6ec9a062da749b4c7ce55c1771c5be74038853f1ec913878dc89b65a98ff82 76f3b513e21d14585ad5489f7e7614a99d0f565ba1d9c74d7d3ac6f4de6cd0be ba18df4320a46e06f5092c242c34b1e5780ed295992b8988d8b58fb4f4af8fb6 038270478eba4e0041656da105b78146c1934c9e8c43e54e37e4103dd9b2d84a 1d88989753b94a019df5b0d526a04355b5caca57eb89dafc2ddfd166cc2d9b16 a18d580f24cbc8814533279e50c1b76c62a3f84c8149a5f28985330342a5510c 8c97df0e0ed3cd10d4546368038ac04a493fdb4f776b71f2883da82cfca39ba0 d51124bf2d485048464f1292f68ac4f2f2d381ee4d776f442df04470341e5dc4 76e1c7489de49f7c029256219e12049f8868c0326dcd9fe7030d4cac30567dfa 0481ffdc653eb171f9f868759e310680c494d060876e40ff8259129ea672e213 c086abc24ec1e5d6c8f1a88fc25aa1b8ded0665a092d57678eeefc7a8fed692e 63068e1ba561aa0b5b7e3338583da9dfae07af9198024cc19636f2ab864ca457 2523398a630cd53e849a151ace8438fddefc758d65a0983556ebe4849cec0e69 05715bfef2fb4e3d04fcd6caf880db87df20232d9e6c22f3e536a051833eb97f 79afae0b5d5f0340259f26c0ae49f2d7b2efcf451f721386bcbd3db0cc516cc2 32dc102cb33a1dffc0551aa87f896bfa4ca364d7e473855053ddcedad08f6192 28772c16ef4767cc67a4802d334e62b3cc366320ddcac5db6ed6da4a3a518e48 ae62ab17972d879f26be0dc8a4e0e72cfabbeb48a1f6c862a566744c73f0d5b3 95757dc182b24d6e76f770585632e8e2293ecb0b2e068b1415c5aa6b8844bbf4 a9a01f9794d39e2135c734007c792c0170211b2f31d22ec855de6c2c9bb099ce 809e8daa0bdbf2845adfbff43ed329bfad793cb1d50964b76b0d2dde834cafba ded1d37ef497ee99ab7f47ba850d3d1592469939b25fa0dcccfeffeb6e487256 5f0c974156a91fda39cf74c75e8418f5dd735a30c3e1be7fccc6e66ebaf51bb3 df40904f087cdb5c2d375fd9e9cc1b8fccbaddd8f8b366bfd6c4c73d548c89ca 86786f17d9de29de6a6f3a96c1f15efd1b795d97f316b2945ea60497416c4d22 571c315699e3f94b0b49c31c9603c952c1cb7b4f501ebc975e7cb9aa76d22486 6b37e86d97644021c4aa8a7570dc5c75697d8a5f48c3d1dc40c5d3bc534ab466 37bbbb806e9bb6515f1b506b554a53590b078f3fcdcce8accb1503d83b4fef11 ad7c90bfb7c44aa341d1e52e1b2ca429e29ba4db54dfa33b061cf6ea1eb00f73 a6562d8af16e8877bc51ff84af564dc25a03f7d0d7535bd2c5bbd130b3e762fc 15baabaae493637cc08130ebdcc165d6250f863613d137e986c3318eb1e1d53d 30bdc4dc44932cda6b13dd13848dbde684a136dc36ddf053c021a789875fa5e7 486d213c4c340c96fab9ceeeb238077f728c3ace8b01862fceda8d0552b285d0 31b5688c7137a1f2caa7356b494db41e82e22dd6a8fbbc1d6f7f2c95bede6e04 df368afc6547d8757f35c2f1e925b2401a976e3dd656790c29df95da30aa7d41 5b457edbef079804a73b9b2c07ddf6795cba1dc7b2b378efd4e090206556c0e5 1de5856c9fbcc5b9320f75715d93718fc5ef2e131c202c109ec452576ad8c0af 57efee62fcef10bf49fa000f0859bbb6bd87601ddb2fb92c8d5b7155ac088d37 a66b2987ed02312dd641783d5e0667e1962271ff175d92a9cabd9f8ee5d7efe6 1d295ca90c2ca4e776946f589090f8b29ed75cfd4912b24335d1d3af6904a165 5836d1b2feb67fed61162fc0547a2989cff2b8223a54e304731ddb1777a53ce1 cf6c69da62ae9770b7cb9258bb6e8b14ecfc4d48157742c2795dbbc757bd9730 15d960a1be78b06fef10a1d748f4b7102e16e7b9b515b85e4ed56366bbf4d347 6f4084468f6082c7a3a09ba66ece708020d294c17718c8a15ecd4c84cea2e269 db08f8b2fd5a814a77a829c3d81da8aba108bb0a30d0ec79095f6272fabb29e9 bc4658cb9532f7dd50cbbe9cc3b69c42c684f1baed27c68f7bc61a3d0ac6fbfc 70118d671fc682d4ba0975f6db97a1e946ae1523a1602e980be873b3bdf7e262 100e6a645828abdf759c25a993f3176bfd861ec8b14fee0bf2257c6942f3cfe8 dd4a958e1f2f175763bc424c37138f7f6ace5505b4730c1474f13e29e9fee09e 981f550230c2ecc818356c5978e09c7bdaba2bfa50a18cb65861c757fd2a92b9 c4591c778f2af35031f0b8c10a9ff2076ebf4743dfcc683b135af85c68692734 ae21b69401e1dd790b5d0ab2a9f302f9596d399233c527fb03a3c04c174e9deb 1a479edea2a50f19902e1a6f7222d5dca50220170d3031d932aabbb2a2c60a50 ae222e075a61554b2931b48ffcced2deeb724b0931e57a7fa42c83dffea81be4 e09cffe50c23b6b501416c062abb82c4350142d512fa7718a1a7b4bb0c9c89b2 140813e40064d016f31c79e98732ff2488d8f5203755206ae3ff8b43ae29c2b8 755adafd8cbfe400bc7f0d6c7e9a6745f805094fa56411511cf71a842dcf5e95 d2f4bd013ff2a5614a0f46d8f8291a727974291ef07d6304cf43a3cdfc8020cb 77b077c9ab0974d0f7feda41526b5f3c49aa93128c71c20a32210320e1f64b76 5658127bad4fe2d7f6dd4dbc60a73717d42208a526b6d0cc42a0cfc72dc5de47 d990e1f4737f9389eb33c7ffa8b5ace778993c3e7843fd8eff9e5716d7dc3823 258ea018c125c4f407fff137b02e0b3f5746d2ea37e5b3f0ca6adcafd4d9fc36 15df78d82effc9eacf8420cc9e11aacac8d5e909cc54acc9c5bd7c4c871451d4 dcae2deb75447b6ea9fe455cb32c80b6d9e756547da0645f13d1089069238913 fe5566a38dbd13b5a0a18c0e6b39c5774a98c588d2fb2fbf062a6a313dca8e15 852091726bb4ee8baa253c406c0faf71cfc6214aaaede4a378465c6b8bac7d21 927934f87ed1e0f415b63e60145baa00c5974030a1c5eb8f24b7188f10fea32c c7db3aecfd44d679199341764a02c3be4a87466677263b8b7d05acdb694c124e 3afb101244955d7befd723e91c81a286ac7f651be08f9895d2273e2d0c49a300 2500f40884d6d748b93f852b6ee47039fec6db5d3377d28569d0fe76455ea246 e1b1e24c5a4eb749ae2e2cab6fcf3b86634b426c3743aa9ccb25f50d99ead1b0 c344cab976b25c1785abeca1946186a1b4d5c1ff5bbab59d8dfb6ad5db01a8af fe2baccb1e96d61f5be322ff3e39d5b72d5cc799a758de1313d4793b5a723fc6 83779c08a1567d1ead921064d6637784b8b85a2d18bb3facbb48e530cd5830fe 5560502b0c56672bcea621eb22343c543696ff0f25b230409984d89b083e399e f78636ef78c66c68d74165c4635fae714db6ddd9ad3b7ad51c7c45f125189b37 f344bdedb278b1a78067d1effad38e71921c10c7ee69b0f597e4a481b4a9b014 164d7bf3bfec8c94798fe21c02efb54b58665b26d7796d0ffa3679b5cfa02b31 a7e4fc8d4ec066b75bbe98d9cbcc648ea13bf6b32646dcb58e5590edec327232 03220e9496c7b992044f1d2f3dd65b6d0e8dbd4bf2f5fa7036508d9fcb28a147 6610c3995d2a266a69d4205027ac22196f9817d66da97a547a2d9a41ae582d22 e95249c7c9018c9c5e46e59b620e8e412eab445e832cc46cc99b4c9178100504 68 fb9e3b21b18825fe552b55c8ae3e5f0f6f532724dd3df9606470327875f3250f5291d72160354ac92566794736330d61a2fb53dc2836ef05ccffa11fbfa11a0c29cf06602914637d6703a422bf2cc012267eaf10f9c0f1e5a2f29200c5ffc1031d2ea4acd4e94d6085a370a6291b2d6c03e85ec7b53b37f33dddf5bdee99fe008442fb0bf43c8510c46135a14e7692c986875187c18970822168c4636d4f4b0f7b27ad184bfc24d0a439c3914d4c4f1654b0d785fead82fe14dd65faf3e7550de49dbfb75ab8f61e7deaaee7aecfd8987e3331036b64d846f6c181547a68ff09498e8f3072b3dcc9cdc75e0dab3d53e0ec4ff9b9b8cbcc8f13b955dac8cceb082f235a784fa6f15912094941b823f6420667c83d663518558957908f617dee0e785b718f95cccb7a4e5a57cb9f6f93e1790b122a39cefd67d5e35455f62bb5031a44232614a697ee369bf5a2b17a287d7b2c112399b98f536b59aa0c1aa2ec0ed6f3e6cf419d072485fe69bebcd5987cc8240b40ef0d4b0c289ac5c27aa291018553cb7d3b29b866e1210fe18888fd30f9a750532f7c5fa040ad69d3f2327f09497b6670dd35e3ce93001795a469e97fed68e7382791c9f38be295aca11c5b06b6169e068b723faab33e667d09aed5a11ddd397b5ff36a7630bf7a3c4e6d4509773876dd25089c008c2f7050c063541bda43a85f9a3c0f6dbe057f28917cfc07b8727f8e56dde493df3d34a701f3d85bd480a57bd0f764bd7d3e9351e20e900b12b66263897e006da183828805d9a32ef7458651bdad8e30f0896398d5d36d0a88257c70b4921e21a9d87611c2a4bdad9980ca7c07f44d7cfb605e85d4d8540944a8890690ba252fb02466ad3d78aa12d69ab0c0da23b04f5bd1caa3d91e5c0529de3830893598202484452cfde5416fef5741780b43fbd1a0dce7abe3540a0a8ae162cc27d3dadc7d24b4a2eddd9946b360cf9b50de985789479206d5129308f8dbe1c001fd74d6489feb42f632aa4bf10c84c1b403c32f1f7595839bdca209a611945f3813b3a46f951f6fceb9026118a47677e8fd6d4b7979cc014ea3e40abefb3822d551cb89570772fa1e508191c0a7e43018b61d5c2e0fe30ab028d40689d43b92c48a35790d39f63581a59319708ff42dff56751ca44b928dc870af0a71e982212222c3df945172c35b3986608e78fd187a774fb661dcfb18923c67091872163646b8fc9f7020f981b840781946d0b1c4f900e5fbf965ce0f1c5df8009935ccca54fb8e56bfff385e4b54d75bd64e4658ef54fa00a2de908d4a315606f308c5d94340fa52139cfbb6dfb0c459e050bd6869a7b51efd0fe2ba27f9e801464d0eab476d705c01819d0e61cc2af785bf5529c2275f6e163fb261c650250d5119e7418f44032be271a5ca6174454040427b6562bac5518d0004bc9a493c0c286f5445e0612188606243acb2d8220bc77dab3e8475813c9490d7e7c98b3208775b850b39580dfd45981a761b12408afff711907df44c2286bd18fa0a64d6081393638431da4888aadb3547285c544bd54b27a9d9b5f5136e28476fc789a30bffefcbd9d67e9963e47f8f711a9af038e992699dc63fa1b3a895b6eaa84478036b6908e55abc08f1bc2afe6bcf6f4b11e68d0ee5eac4c8017897f9226e27a60ead22365f569e8bd15fb85985ee8e51e0e8bb6dd70a4c5f0b87484ac37e4e6b0bb80b579ce4df91ffc56c3479b9151159e92f56316918499e1a950ce596539c0e3ecd955894e288a9c23e14ba741dad99ccf57d5bd03e5b6411bc64a1c447560380a11752171e4763e2fc44cc4bb5218b87deac9ff497e72ae67bdedd2b66cc04e7fe6e67ea0b3e71f0e390a5fa5b7839bcfbd0c7e31c34be6ab0da7dd230b4051281f7dc128e6e9b17cba45071181d4998e330cd72c79f240f7689a896ee750516e419538ca50ed94680f3a13ecea37c21e8cdaa4fec621163935c2218035a0abb8be68930c603159fd635f62592c22c842742eb2d845a2a0f00a2da754f2d05babc0534b9b2c908de5138142cfddb036796d4a7c057ef56de4200294f886f09cbeff39b1a0f2b98c59d33b9709095aa7b26245c8d7b28fce123677db92f0c04db68a76cd0477edffff397b7e8ad1b7a91008ed41d8c1ad2a5f32d32ead9800ad8327c39a72f33cbe342a0b610e6b5f66a0f083ecee31b2d40b9951a9333910f0a7f5a91a5ace137adacdfe969fd95fa101acb192ed6f1873b2764c39c85570679075806bcf626ddf4c9966e103379ac6d5ce8d8b1b00d97b6d4494fe917c706a8aa2265fd1c60e9302a903a1e7b198e1e879d7e828da04ca9b914cc8751980e2e60ecd022a942ae3b0db4832c3530259c3e4c9aece72992070e5c925d6f730235591fe2f287622d881e3d8d80684a40b4045c3cedfa4773e22c39d1ea602c020b0f42a08162e91ae14fc0d66217c9178b008925ad08cb4aa956ce8de60ad50e954c1e7fdac604e1783c138a6ad51f295b8d85ae91c1f5c4b35028e1a372da00f84137eea8de263dcbca6935810e7d69e714f778db75701bc03f99246f24d009dc56f481cf8ae2c2ef03c8143acb6fdc8b3e70348b5efc205ab8e407b76cd30294ab8cdab4dc1f4cab806c9763827f29dc4fb41f04a2416690720e0fdcf6320151b53ad2cc7b2a18aaa5e40647b4c1dea3cd9634747e006f8d3c7def6645e100e34347b44b1a5383f014faa710d4f7a361e8868b76939858df4d51b2ce16230439332446cf298d178a590ffc82018468da7d853b65ed5d1bf835e0cb7e429b035fd3065dec39648af3973f551c40638a61268f4ad4fae0303a26ebe12d554406afa92575ac82e732965c1352e3ba0ddd7e5512f8fffe614a3014cd095f2c5405b9689ba74d1811762645ddb3fe09b3d8bcc5ce45cc63b945adc45c2e6a6e8e0a2dfb265ab77590c4636f2975b5c9eedef807c7dd50915caa2896931ff9e43d000136cf77bb49e4214490a19ba0b883e93ca056f108f3b0c7b3f83c9b0291dd05aed430c3fbcfeac1fb2a0356759d1fd18e08f2eba1f9527d5d8ba2235f648b0ebb636b067b469fb9474bd9a180fbe1d132fee3563cb10771faaa4ae8eaa76f0ba555d2f723903bd0390fb518e724060b6877840a523932fc3349e739dfba7805e26d5aaacac5b0bcf57ab48971bcaedbd3553c474e809bde1adf9b2e24d254000f87d3bbf4c15c31b886a5e94694e3f3b737670e7dacdad2a257cf3ce7a1a609ff0a1316ab8e5942b2acfbf93bfa7853c5396e301d126927a16e283eaf35ab04fef2a1a9fee695a3d503d9c27e677b2282768a7a9717ff1fdada9fd2399a41060d93e02b05c28a19f8223a2bc4e9c24732252b79154656191b2baf8abc61980920c7cc5e425339f24d3ee8440208b0d9018289cf7816996ddf40dbd17b15d3093832f025b9e2a828a5f8e68ab0090e896d8b8d132da4972fa06bb77d6f146202e41d09f2dfb81bceeb916a5adee209f2e7486d7b53c9f07bba4273166cd1af061d8ed64260353e65ddf8c04e47968d2deb6ac37113eba1066d5ba933c3b5e50c253e5cdcbfa765c33b079fb5a21abb809c14d4f8ec3154e1066e2ecc6a56f10c18f2c71af5f566239552628c37ebc1b472e1350fa9609ef7ae1f57de662251024913a9de56b950c37909c31b58595634cf69a377243620a8b3e7f566ad6e500a9c8bf63b1b1a935bc7762b2e56826bd8e14c2f4376fff3ca49a9542dfd6e6b0e6e15ec91fa4d1b2ed4c282e65e333aac5446408ceacceb14bfded640f760720c37851b04d6618da04172b0c001e2f21f16d9c94d56494ffbdce8881223e5c302a14febd128995b8dd1d3c367d82bd4c348bd0817ada2d50e92b3107d688bca08ec1c9edd4e6a64506ea264b7378b3a87251bf39aa53342fba1d680dd5bff270d5d04535e058a3378598f2f6c23c571da0ca0686821ef9d3627a08d561cc82b078612bd5a0abebb9213f319f2cd0584bd1780a14643e90b22f2cc2e3e41b0a30040ac76fad246d6a21db47d97f5dbacd06610bb088f2db94256c129a38f6b960145185dd690b8493eeb55f859d2232ca1fbaf02e939b78151cfe75b59f99af701d8090c4f96743f581de28b9bb821cea6708573663111f34365203ba7bb08b2094c08d93f664d2c735778f384d51a87787f17ed27ea8bf6fa4d26408d03050705c7444ab7f1ecd2b5358aa70770c53f1dd90089b0aa442e0e6cc01045b12c2b07397211ec466f29a556dcb75623a2982c57d5925ebd9a69f4dde7b4842f26d30db278f6c4bf53699a75aaae3663e6ba25ab2c5bdb3179018311d128818ffac3078a394e13e5328a3c6e517adbfbbfff80bc4b9455dd86f6ff59cc5713f0f3bc042c6d4b02a0263e296b2877c1808a7ee2635f762c07019c490618dbdeef741105ae62e6fb6ece1b60518eb1d66ed80bbc085ac0f354a74705b3fa325dec07b907d6d4caded00e9aae8aca23f722fc6070120711f9e20b97ea5aa8d2d2f119b9054a710b6432a940f208ea1cdb44690246cef0516d811ad544afbbc4e0b067160d2f814b5e6ee49492142c6fce17e855f256982f919cc492b50fe57349c156db0baac808c51f0c9b54be9978eae208506fdf04f01233e8af1074f7becae18a620c7a8065d9b9648c28186247c0371df92c8c5a5e5f6b19d80016864bfd61efb608af98f7a7eae861aa6d7212f2586dffc8443f4103a9f0d8ad2f8dcda496a5690275b6759e25f8b41d5f5c5112007860cbe40f5608a507aa86ed8a1078224e34041c974ca360398fadb5d4b0581e1af384e6fd86c1a68815044a86ea731c2564078c17e1a257270d1c84157d1025bfc807a022e6b54ce9bead496ddced6debf00177708c1e6fcb221442c417c27bf8e7cb3ffdbd48e0ae023695498bed036808091df16463fdf882bf8377ad04b4141de04dcdd242c932b6a0932116385a9275062fb5835a8c1e0d85d5da4772b7c73a43c6b540f099c0cfecfaa08d12c521b0043c51f707e8873c6701eb49b613601dd8dbc5b3ce3a9c5423747d840b2fcd190d4aaad085d3e99eaa28d37e1597b8e3e0414dddcc5da32a98839e68cc572745047655b5938b6993adf0f0b1d5f7c870e56b375a2be5185e7ed119e9e0c1e197033970730639e383b0cf5a799faeef906d4d29866eb453e5102b8eada101671f00da6d7a4b36633220c311546e8952c71834ee7def771346f2f09bad8141cd4c02e40da9d005566d44a514dc8cebc4f70d9b410b6cc13830d2b2c3558f1dacec0256c1e492aa3a72026a08012658f29ee26e0c4605b4b768283b373336066f3f01062ab4cd91a941f84abf7f20680261b22893b0ffd524afaa9abf35cdc66a880a83c4b60c404a7e398ac61899b9210df8a4606d0dbdd3619cda46af803a03500428cdff39b4af0435cfeef4c21246b7e017810255b36d19bdaf09a500165dd702cf05d9a51e916a88f148206cccfc4a57337fc7ef3f3542381fdc6299a56d4a02a21dc849cbfa65c7dd2f79ae66f85bed57ea1baee35f5e3821faa5c6b6837a0afc686b2e3345dbe76a055c8adc1a40612fdb090759f0666cf9dd707405e08005fe933768e0eeec889f8d74596463cadee4f4be1ea919cad166953f9ccb8ca90ae4ae642f224e7b93a7221b44324cd31268c5e48aba977aad1149f543edfb490edeef8d06d1629d30fc4608019d29d71508422d0ca83e49aaff40428707e1670e4a4fac31834ebbd978f4f243ee7097b064ec400a486e5c897a0855980764e806923857cc350a97feec97b9108821d4b12e9f60f6e16e433b89c4bc59f33479039ae9390e27d395c014fb68d603acd915a0ddff2dc9a2d3e7021ce08d363efc031a9c64597b1d076fb2c74cecd61d1e8861e11f8e72897ab0ba27676ae6ae3b042e9a83eea905b02b5cdc10e76645c8ce41cc99893351d707eda9f782d50d12087886d5ca78408ac81785a60f2bdc2e6079ce07ca4c1d921b6bee6fa1913b430511672278a08ee394d6f39eb13281a2b8fe6ecc6118b4fd3617827c911363130990fef13c790f6f82f3f0607e9abffa36918c7ac64dc4982788aff25ac85fea029e4c8f6c7600ccc74fd4da2b2840774327604582f5f80c0767a0caf6cd02660e710891991359298f6ea73f4cb2787788eca1c7297e6dc85d94acc0f53098db0630cbda99d1085eb66b0d846bb3e5d92a273c5bdc53f34957cb95df6962815508fd6a8927774bb9e423c3e6fc1994f921bc77d48b4cdef75c4cfd2c5bb2be2d0615c8917b0df429844f796b2c56b05e4c19be253cea6ed8e30495dcb65c78410f94b1e102e05945643e3c20a729e040f35388aa0c18dade36068436a9268e980def3dee9143b1616c035b591f762cb208239fcaae1165feb808bd9ae739458409e882a944f826ee5f18469a21357ccaebd0a935d25c954ae969890edce7dad40eb3a716a957a339944d8f5e99ebc3ad4eafd992e1759071f3c0307fb33a595400c08099b22cfc8f4133cb18d3f4743ea56bb989cf1b1d603970895ffd54817f0b04cb00b6589a6475a35d42316c9bceba6d64f76d242ec8951e900cf86141d70bca9d1558b29144724f97117571a7528a291322a2ac29ab4a4927631510b0c2045be7e45cf0feecbd36dc6c95c78f2259e0629dddc62decdc5a1078c9a34ffb0d73280fc992cc3b0593e8316118e3ba44975a082840fb7aa5d5528c372ea5430e7a391f6ca74fecda3dfd6f7d1755f66c1a5b91cb39b5f18f7d842cc02aabe206cc75ce799e3aa5cf603b5c226c4e139c84512221c35609e6b97a0e0a54792806f4b37740f15b3e185dcfc89a4a1849c272a3507f0044440dfbeaa4a46921d50822f69a3a6feb27c6f9f1dadefec8f967d92fe0031152b220199822320072ad0ce836b6d24ccef0af6db6a7a6b1a1f4fa2d1350207a50d84db55688a4e4f0b50ae35035298490fe503f3ca00b04feed7f3bf008f4e49eea7078cb81fd9c4e6201dfb2713f2f57913d36fa6f2a95fa2ac072432383135c62ae31d97e3ff0352407504cab906bb799179b5af1809f82904b5531a02c5b5f7a02cfcc1a07ad9509015d29949c67d4588999ce67c403aeb48b289157486e83632571a737bcd9abe80298d8e140de8ffd0268d8cd2725cdf2ee78d854869b67758609a59e544e35790f036a3b1ad729a0372bdf1d6d001073f9cc702bc9d009e4fe66f9b61389ce920b6d3ca0bcf2569b98f0171bcaa81f6114e9600a5c59e808c5b8ace5c750fa610a56fd72c8304607a2a9fc664cc1a63a49f26196222286258661edc07af5ec5c04 +generate_ring_signature 713c4c8803fc00e35bf91007f00baba57486e0d70d332c9608d09b6682d5a554 794244933962c44feef2118a18c784f069ef776f495f075dcc22870eef06fe25 4 d6ed7494656d90bc14042ae12e0dbbd7b9f890d2347417b21830785a32fb3b25 dacf20780d08fb684de5ac429259951b81deaf781a6e31dbcda6ad07f10ff0d2 d0e455ae02f0be142f67090ae09f49ef67d65c5ca67a2edd0e612bff1b04245f 61b716ef95d86ea4d50645d4ecd717cce494c2d4d42aa30fd13a45e3370a7f34 da2cc4f0ebc2d7f6020c9dfe8ede0c1b216075c6c2eda1a9e10969a69a35d10a 1 e89a53c9d17738531e3806ae60d38f25fc9dac1857e3141b0b353d81b358490a61497fa3216e66589607eff192818d6fb27616ffc69f91b4b57b46d37564e3056ed16f995b3b5c019a18251dbf4cbca1937189fe29d0d8e0d9ba9648aa364101a54933b16ee9e9060a63e5665950318e79e03802463ae96ab95d112eb522c808b198a832f91eaf8967af31de0e3b04c5057ad3e33ab68f7bc80358971602290718825ce1dd00a6d71f6557bab44e983ae84728f2c817cb269e486c061043de08f31a676e5a20d53dd661568f75442cf4c9ad103467a3004f2938155efb883b03e9e5d96539468dfd90bd0364179f101f338f49d2455eb7c10830fa65d28f5007 +generate_ring_signature c24d45d5956e2ff05c5900e0e3cc833784a44af630d7aad0beaa57fd879825eb 359095816f878d15ab2b0a2186ffbf5d95c777c21ca2cb72d2266d083a4a5809 6 bb3e5f6ae5df3c4a43310d86dad1560abc1f6626a09d5a76eddfb78d652654d5 7a4ac8d3528abde1baf73df38ad03a857739fa446862d1e28c4ac3c83edfd15a ecc674b53bb166ac2be28a7d51e66edfc9f95a324277a2d9d1b908e0f65c5e9c 5841a26c6b246992664b8d8b58769db51a4f7a194fadf2c7a15046a0e2770023 75ee9e843ab7975fbbdd99bb16392452a10f7e7b38144f29b9d706bfa22cfd65 3de3c211b808c4235fba01b016862f8305f0deaab1fb58823f1be27f151acbc8 9b8031370c48fb1cbb36f400453a00108841cfc20f4ef9d9d206f33159cef60c 4 404f56e864763a6217e54b5b8e915a84026f83c596be8c6d03cc4f9790c54d02e98f44e7e069679157101c0cd7f4898e7497ed566f95b9d9373a5ce3e0535d031e4d733c5cac601da4b6ce79d092eacddc7814dd10e8cccd2f18f2c0f3ec2b0695019cda44e95c70808803953e8fc9373817a492f4837d6fa45fcd5d1bc2af016ed3f2b39659ea8b3a5d948b01b506b618befb251e6f2b664892629b235aa60d6dc9bb820018ec47a97b5766ccc4276bba11aa66cb1b77d014916557d43d1507cd61e671d7034943b5b8dba7543645cf8dc6df3268625c312b70d42303d137019be8d84be1bc63af870ce559417eddcfe146854bf2d771508d2399534629100a374e727d62787b81d2599ec925d7abe9f4eb06ba7a517b196c2a3f98828164002f3f86645fa22d125dd014e5df254a7185cdc687a5f786b75b6e03f945ddeb00e010f8d9ab5cc12c1a1e90dfb6c93c76e799e10f340cc907a053bfc30465020905ad33ca6b9680d5ea7592808b8a47f7d0554347cd0cfb4a2dd10d9916853202 +generate_ring_signature 9b9e3fcd3f946f6741e6186234eccbf58f97379d2138b6ff985ede15f22f113d c3b4076a7f669ba7d594c52e660da73a565bde7416c9bb99e06e9259d325724c 31 c468ab3f7c3268f7a1839d256c30bed690689a39994e4afe3512c765f2b21ae9 9e8f235c70243e95b84ed1e454c1c86b10446b699f059f66c46e5a1b37dd396e 5742fde4cbfc7422341b8a5449fdd125d30e41357237e469bf0fe77246577442 5402efe6137d07e5b71f0a1192ff212ea8959e080d32b012f937fefd0edbf337 2331347eb9271235c789b6d2be8c6dc18713e878968d77f72b9d644b16f26dc6 0bd583c1d0d8b4b2f8b19609c8d928eb4f4196a491ca45fb1f7d27e9b1a92c5f e1841b87e3d529490cd95098d08b589f51f0c84321b656ba6e0820c41bff4574 aa142732d8b972888a704821a22d62e92eb09b620f0de227981a7d9612333591 d80b7da6a8b938599b5dbba5941781b1217e4ae5a90efd8fae4a65c49c30ca3d b6d8d385a96ab10a4cb6a82df537be9ed042c8f6c42ae261baa487f7e927a384 131a2aeaa5db3afb87093ce9ece283ff1c3c8f7ae83f016612a4d3c866035a54 23cd7cbfd909e99e696f371875e9d7d6fe1f99a0a091dec26dc79fbcec6c29fc e496229d63f8c8b61f17f330dfc30417adc1306275b55adebd8b2a2863f72c8d da2db6040913049257b6ced576f35e4bfa431f25ae137c6cdfb562118bde066f b658b88b06cf34f1897ed99a26a20b0eef67ab280913d42a688b3d862cba3d09 da1f11e2eccd81c0de8498f22659197dbb66bb0512188ec900d44e8036bed70b 9fbf6c6e3f494eede2ca88646a1f3c3e3c40cbc0216227e0c474f5ed7355275d 5246031ef62f9bb1399d8c444a9e3ef99b0da76ce5cd7ecc882dbe9847d1d62e 8e4c0645b710436a74749e8d5531ed8719df52a68293713e61fbc45d5fe8a68f 18a5567a6e61cf2d3bdd2fb0a58a39783afabe754ef59dbd81e370e35954f1f3 a4c53ef53fd1c7dc628b6cac4bf35a5b84ce3703c07040924d2db4ca523cdf73 7ade9695fadb65c56bdbeda04307b304a04b9d5876a1ebdc9dfe35f7484a3ea8 82c7f5919c40ac93fe9c30b9b50b6353dac39748634b5afb52eda0aca5c00c68 12ba3162a1d7ea4b0c110e4b2f7b6f83a3874ebc983698258e9bdb187efd2ec6 62f6e20381447fe7c19968f5eeaa00c04df07ad01f96cdbfed367b6d34fe7b33 f27a9baac0ee08596f753298a41c704066556ceec1afe5a3c300e339a709225e 6b15357d01194d299f5dac3ac8dd4ad6cd02588038c3d9a8fc4cf322cca7be66 bcda402e1596fcea31ed893a65251f5fd60cc2b3571c5fcb1c8418bb6ff390cb f04d671aa35a499ff64ac633e18ced492f00946d4e3bda70d6274350ad1c313e 6b11299411b8c35cf07ee188537faaec524cb4ab00b28055554673450183d288 cc0b067ef56c580d768f01f0efc8bf7293e408d0369d5c3f0666a2ee4c70c553 28c2450bf0e73521266c017a04fff88bd10933d316572e91a497ffe3e9a48f0f 15 ca75b6d3a8d6eeda543bbf14ba9b71c7c0193f3d7b615aff09fba7fc4c21e30a1bdb20ff5348185c7d153af260a7e9ab1fa61f44078c0a0d02a28e58e6b91309779a1eaa221a031182c59af732ec9409a760b525ff4677829a80fb761eae8d0ef70e0aa96c6329814b8ed3c42ae6c027406bd0bf4ef1a3ab5ab52fc1d98695000292597d4f4d4dc054a0ca200c03f38fc4163d1ca9b442e707411c12907d9f0effb4ce25fbfb2253fe45a575d9a2ee41e3427a84dce1c32a50cde3d2a853ff0071f0314b2916526c034e23452b64123f11199b771ee2b1af0e486c083a801f0096a0dc342c13f29c1a53af8b4d732f18e1c4adfe04c83c3694023b0a0949ef0b35ca14f33d67090c0c01d516110f26151ee2fd405335ccfd8dfb8561507dc30027c3af61d6ca935f241305b40034ad8720db6c1b38fd10eaf34165e12fab2c090717094149c49aa7e28be60c99b5f7d9a66a09a91d0098c081ff825a28c270048db1c81ace94678cb22e908bc69451b77aeedeb2f9890571049fdd39d9ad280eb63751f0b4963666afb016c990707252900715688790c642026fca2f086b6e05d354d275945a32db0451303766c2d7c3cacfafcc37a5ca057af8c1a12e92730473e4c42cd654a9c8f606cfd80d9199301b8440644db32a411f9c9ace3b55e1084b65af44e1ae299f808d0c21453e99491b439bf5759b10723ff74d22ec9680063ce4e984a7b2f630ca95303dfff9729ae09113af350c36ad2aa53e3ad5641908972949bddbf204e3138c42dff2a4fc32d16b54a0831fc5241c89be25d8efda0c069884d2f1fe5f5690e65e111a713e4ae6340f4d7387c3975bf7f09318f6b10cb1b77ab274697410c3b4ea5ced839a0c642cd89e0d0a9da6bb1a958a93d5d70951363f4555cc34e2645279ba3e20352a0d87ef386279649ea44dd2cd9d758c0a8d8de625cd4b227c4bc29fb6b0b36b186f375c4ca65af35ff85c3039501a050e85c72fdbe25dd3bdc67ffbddfcb3b83c4231d6f1781dae5fe365b2062ef4330850063729202e507f2680f2a0019d06125d24bd0c728ef8b73f36858892c53101926f6c1c068a356be4d67a73131d6e4ad7c34db092a873f6adc20bec57e308012a22b6734f091ab85850990e75148eb913cb2a2d14cf843bb3b5705cc431de0ed9fbe3e1cd15a57a22a63fbd69e3e380b8c00bfe3ba89a4838123b2bff9b7701f2e8a65ab158a7d37ef52fdafe2cf69afc13ff79694fe320b6f81642f20d8108b7e54ea1da597e65539f39b505f67b053e701ae6f978e839030df1e0e4da4108163ac5425ef2ca7d15f8d65aaca655f35465561c3078b27e6b401a9841e79c054d863779b26c75f29dddd3d82d52e04a3b45123316309aa1146f91aed722a90eb0cc74cbedc90c6b98319e6c5939ff7db08b4a06b838b8ed68a896d0a9dd4705742c06317eb859e5f3f0ea5ddada506232216d1857f94c0e88b952ed47a6910430ab3d73a770c5331fbf93950d202fac53ee7be8eecd6ec3eb1ce7ca60321c0e21dca91a3f95e7626bbe8cc99bb6f32e00a5c4cb021108f0665dcddf6e51e70de5d6773c5653196e53065c0a55db5150db641795eba6f4dc6a75d9e262646803f3b42dfc878ab46ca75c5c972240a55e09a5213bd818b002125b2187379620002625592b59e844c46cbe065caea8435306cdec8fd75a06297555df76bd22bd09bf86cf07f53e7d65b63dfdde8f850e704a96e19e6eb8bbd883070f00a5f1af03e2cba2b7d4ec4b79b00c26fd1ade75cae58db490a969873fe04fd8495aa6820b34d48a59d16d0b62c6542109b5d0f028febeaaff7be514b2a11bc9295eefa200fc1eb14982cf794bc8356f140925c3ccd6d56a3df7aed7abd5990a2b448fd40f35c8f88073099f8a4bacd22a4b6842ea6c1ce5009a16b66e0ca62532b5ca0f09f8250cc4ec1c0d17cfab374af2641252df977b6266aebe6a1f8e4873ef20bb00a2115674c94d3ad83e39a7ecdfacc69b6f41f982926b83e45fcc6cc84713050a67a28576ee08599431bd7bed74a451ff793aff50ff17338405f3d8d114521806a28c8b27c64871b1baa08cea4a7386d36d3a8d27e21e9ae541a1becb55504203bbbf53a14c550d5842a7d83c304493e9d2a194b4bfd25996761b634f669a120e0155fe83c0f9c5abcd68c235cbe6d7a1142c9aac98c87fd6a0beceb5e4e39c095ea2784e03cea3ab9d0349f33f07aec785838cd41fe6d9979a3b36b02b1b1406892939af622a34a2ef570ebb7a4b2bd8289e9560592ed2fe324e844a5be1190c12b6cddafe21b50f2fbe21c31cfaac26675c5d1a1cf76baf9feec4f1f1ce0708b98c321d6e881024ff6afec0f6a3251167e91817d904fad589039dd8db14be0817986f8191143abca5dffdd49f09a4cd9e9d5455c23a47d35f84d0569b379e09580f0346aa8d8837e7aed4f0fec7cf67b80fd3f5f2e2df48125ca6b98c518101b5d8d187ab91bbbd1accea50ba1bb92de1665325dde6d7ff89165d882ccc78025dbcfbb5795d83b797899206ff8ee04821ce86cbbf53077fc1441f9184ab440925ba058387019eb286594c2a38152548e027e8c2ee9109da30c60d8b2c42a60d961e09657811c6df334d66aef12fc5cf88e273569cf44cb9406258496044e808af22dd00a1561b4ef75343dd2052735ca8d4a8c81c27447cb0be59447da28108ce08d22221e90162c875dec610c5710b40375928af94d7d59296754f52050107d0ba13af9e404b0b055a76149e9cba9d2826c24a5ea34886b015abbfed3fe603 +generate_ring_signature f31b094e55ac7802f86f73c3525a6cd8e769ee880a8c63aa2f5992c255149a75 b17df6621687c3f37b1a5ce5ac09d95546679313dff994083f079b5cb462c930 2 9085d0eae6ab0e2b4c9289ddd4b4405dd818dc5218188920df043336d491f918 5048e0efec6bc401a73f920934508f4d9b99ef5ecbb439f178b7e65c7d08f410 39467bd15083c5504b8e2a161aa69a6e54d336270f30814bae129f1a53972306 1 fdc0002c6a2df1f274f0766ff9dc5f5579aa59290948163146ee04855285f90d24e07459daa71a6b1ecf7b1be2147696655f18a23e7c70bab4b8c63368aef10d5ca545e8b2126f3c2328d8fc3027e830ca5e359ff4c5b5f83fd9f7bf219be50912d71b77d41b29ae380d3612ad1f9f2f89deb343999d029f6ebd83a5e3416705 +generate_ring_signature 2b1ed38fd7d880a817ba1a9e09166f4ebf6db200b718496e4b0e1611b699a71e 71dddaf7adc360a91390f506b386c69a1cfdd89e948d803863f742d9fb9c5453 11 7670293c88d3439719814fa939089f77b31911384f46863e903d78704c004a4d b0bf008c1c329ea891d807a1d7ef55316b480c19dd90a00233ed6027d566aad1 726dd2e15afbd42415d0bfa6886d84e1f3eb9442f71b4088b04c79747aeab11a df4a86c0db52362d3beb62ee1083fbb3c400553a99b793ffd20d4afd8cea6a14 ff9ca05329874655d5be3d882099ef375017081b3b16a0b7da8cac1ed3820988 88baad2c438cb5208973869e561254d3a38f4848039b445b9a4e8edd2522c325 7285ad0124a73272138c91e701daa44aa52cc647a73351f1f842bd7454efe5ca 0cd91d92f61536a7fe4f7c9a6dc04e2dd74dacc2f2f6a93af216dce3f69ebcec 0062e10dc722a08dbaa346d3c2070973ccfbd511dad2db35f77e8c31eb95b913 a9290ab629ce61f962650140314ba4e6e23ad47e1196460bc33f6ee92b18eeea 6cd25a8510ff4112e5f2c4d15801b7a006db3092e24e2db9cc216c7834fafcdc a5d6b5f4dcfc705c6f42f3ce3854458503a5b82742a2857903bf9a57f297e703 4 9b438c43c904171802140e18f56f857d97eb2835117aeaf602a4d1ae0c72550108880489a007c1ddf1c37aa56f23d0f80bf2b1d012f4d6fa0eded13178240503753627914817389eb02480e1bea1997ad0e0684f1a319b0e3760e91b392ea5041679da09728a98ecd59742fef00d6cdb70ea08a2a2535d35c092937445551f09ea87b4410d08ef6a2c9441c8b32e410edd3a9e2317ab32e8d3b6a2374e00420f64c01ec37ec2ecdd5aa92b48628c11dd30209092d9f9a26c4d42a4e414b34f0f0005a349f8c7b6946313ec69ba63bae3c2013442c64d27b31c4763eed7f24709a6103a39fa57d94446263735995cadfb4275d3ba4f069b414dae108a4ac8860ca0c56fee65c88bb65a6a7ae7bd3bb0c4ac0f5fedcbe61d1c5f45ca1acca8ae078e6a69979ceec3d2b92a33ec7fc7169d129e2b14d22f79336b3306d0ac93d10f1594d733958ffe22a8a35a5c9230ac051b7267873f62e2ff056103c2730bf50821a36d8cb15978ec745a936a357326e66b324db4815cf3e775837ea458860109b0f07beb8a52512b6c3d2c2983956f808a1f3eb161b1674f48e27f97ebdef60f2aaeba48671e01b83808096d3d3cf5c8ad96dab79090b2a55a219d868fa45c05d8ed0b962a61af84132aa99cb9fd028149d458c0426fdc8da30147c9e39c1002a02730a489224bf62e20d431dd92350c1fa9df17b21588cd3e1eb7b238e07c0d9b9c834008a434112d33db8b1bf23c0920dc450c10b9f219dd42ca3541025406f62b3fe1e5a7bbb24d97febf1241311300631ff4a31c33871c0063d37339780e8669563ebfeb03027c736ef3e4140b3c3c86bf3f7228586e71f43612200f090875abcbba5cee99ee79d3ac7a68c91393e1e63eba4bd2f33846b21263a84bfc0f8f7da3a5adb861e4b0e4642bc14c269eed41312cfab11a8bb449d84e81cae90199e589e375e8732dae771246923a64e92baffe199abed788577f4fd525c7e40a +generate_ring_signature c5093dd27848ac8566eaedc9052fcd6197c28bf93c33afebaa7ce2dda5876a00 323871a9a595066bce864c151cafd777f14afb4943761ad5a574eeadf5cccb1c 251 39937708a3b6828e2561d337b4bb62a05329a677891317ef1fbf021efde4a32a 7d853d909949ca86efe934596d599e2d77997f771aa4877b6f66c9ef477cf18e e983c352edee9ff1590f1b148b377e4a2d5acf31be2fe34df8f6a8d4c4e4ba05 7e1871074a0892900d96e11c48249bf88d429d7bbfc5f0a1029dd27ae601cce6 053b105d261dafab41652fd16d5f6fd87e2c3802d396097745891a1f41af5c99 3499b5ae6a1a148a140469d676a0f42c4422cb71202196215c5e35b35dda93b0 cb14c49d87d8a24cfb716a35bfc392ed5ae3695ce41a9c65c34834f3c820ab4e d4d25b34a483a582179cb1c171532f16f1672c5350950936984484e8f2256c1b e657b8c50f8c92b530cbb8d9e77f3db440326b397a54ba56e3cc6a0b123a092d 4dd6a6ef7f8d5bc9f80693c0a886b0c4432eeab776c17346ac19504f46d622b5 130e7e54142cad6b55cf6143df37f9d206ecab657e1fce433ae278806ca1d26e dc7113def3d74145205db0179dd574c8b00b0e708ae5eb7510c2624500274532 7335c7e6cbd4e8153b9c1ee028e3412f1681058440bf04e6613c49fe1f1bc75e ae3948dd1c2d07972a05cb741943e766fa304e2fea461cf909eea69de08d4e79 28408929a927aee09552e8a162937eed73757ad37ba996365672f1204ad92717 2fdfbeda21d8293c0594ba9671d0110421fc35888908603226cd7eb0995e1ac8 3cfad5a488402caf59a69746fb5cd739782d61d6e2cf71c57bc8c1127055fa19 cea1d4b9951267cdba6ebe4c6bcfb7ff7f465582a73f3b8d60726ee1ee4564ef 36049feba81aabdb2b44241538d15f60fb851dfbc19d13a6e4de240be745fac3 d4225c2a253f2cbd16bb65b500ff4369240284ef1a4cb1f77e6e8143300d0fae e8ce1f23cf3ec0f98eecdd9c744f9894a5c8b3dcb0b29431c4bda4658b4ae24f c1456aae2024890d5c914e9e0a2926e7f004c2e2532572ce815a9ab91f8e282c 959ff70881dbc4521285d80eb7941cd4ba94418da70f041ffcc7479c7c513ccc 7aeb49c0c8b4256f7a718665b575b13cc0688964a8c947edb78eddee1b32656c bc18deebf265b86a577800c425c3c194a390b295063a87aebdf816315e04a0d1 e3e9862b6b0ef2c15cd2058dc35e03c2aafed007e25ac3aa59cbe2654afc8d42 00d1abb0f3f487207b0d764a621ec24265e35ef7d5256647b7404976a8bee167 9db522d9789398ae5954197c166cbd9f876c342836c100364716a3d23a3cf885 30a63e8040c68e413514de31646a18847214d70363850e40bcf5e9f5c3d92ca3 2a78fde45151e821a1a924b5241a2403ccfc6ed62d2463b09e7070b7279c5a9d a26c48fc6e7e82af42fa3cfbad13812bb6395aded22dd250b79f47ee7dfb6581 a801330fb5930a20ac3e1d928ad94a70faa44e7d8319cd6c781d3d5e0b7ce0fc 7ced5508691face4c0630aeee6dd57f510911092109720982102ade5361833b9 2232855cb3aac2d84031685bbe847619d358070216d20f681104f55bd100c2e9 6e49a809ca9d63a6a21496ece8bc4bcf3768b8588456ec1d909c2925f60e995e d3a035827bad58e5a65284003b112160f3d2f45c103bbbc13f00a92f101b2e7b 3abf72f7ac3ffb7fc6113fc42e26031badc02e4d8fdb3f7f08b436514c3f5a31 2d3b155aa6fb6ea05e5a8367039d257fa0b334039d5ea0ef11fc56b8117c306d c7fb3b153d7b88597b92ae7d79f0e3185716dba81744ddd9ac1fcad2e0543fe8 1325b0d92f95fa8ff2cd42384cdef4d2976e61e76ff73072bbcac1369013789f d8cb35a8558bec532c929325f135119af32d6e46a2aa9931a7464c6d469cdaac 7a7c8d38b604d98886d467cd766c40abc920d751c2c94574fad91bbd6a8aef5d be6b00d3cb61d3032253e1a7347e346dca1bb51a92a3eb8b071af0c891665b37 0717cb474be340eaaf4a144b64dac510b6e8e63e3bf9e8bcbb3c46fed88b0f52 3542fe85a3882427abfe0b026c12ae8d195a3db1534254a8d1af81d25992489f ac694745614759040e5dd4608aa75f9b570f77d2e166364d6912cbb21fea0823 5cd8f7e533b3c94444c897c8c91212f0a228fc719ceb8bd55a5c033700dbeac2 872355c0b5f2347b4d2d7fca96f8df10e41b45b9ffb72043a531274409494385 ff1d9017025185587a0a4cdd7c884cc70cece019b26ebc15b09f716560dedeef 186ba15a08137c92659ef620170b35f6b1bc9ecf4c029732451394adfc999590 9c97f115d584768d386ce5204bd2d3629aa63ae97628146498ca37b61918a442 79f55796b89a671a06bfebdca61971130b23fb61eae4704d2fac27538ac499e7 b90a0e602d78353e01b476c3a2c35a5f0c7e5f415bf78d46ede20024a1d3c4d3 a670570bf89098e8eab1fd7afe3e00b01c07049c3722d66dff9b359fa895d70d fd465b0756c76e548d9ca89b6523c153bbfefb1ed9f32774f9e114b60a551d1f 936420d78379ac16945964a1dee95d71940e87c2e88c0a309f70ea710ef1a1f1 7821936f936122f4adfe274bbd32089d4cec99401014b67f0cffbdfd3253bc51 30d8216ea0f746b13f5e4a43f6c4d297d7e6db7e19445749d595eb028ab09ccf 7681954cc4cbb792670aa9a21e64b4723d86495e4d6d27b3cd606b00114c4397 e9663cfa339901c3282b9d2b4560015f5ebd7fbbd4827023b224e54109933369 dbe9eaee8efc256b605483f862a3dc3d5f70a4053ccf29fa4a52d008441decdc 633da88c3db244e95ef373d027a09cbb954ad208fe938f44c3dee2a5c9bd7a23 a1aebb8588a8f423fd40bf80763d0a0e5c4b1bfd3fbe76f14f9b307561ed03a6 9863117a21bc64aa4a8a301aa9798ff1fe6febaffb1e2958b259e1783fb5737f 2eec0edee3b2793b4c35d7759054e7b0c15d4d450b06a5025fb65d8a60f15372 8ca8ed9269c7c5c7ffa431228e9f688716fcc4133c8f4394513251ec26fe3159 f345a4005a7c36b6b78d97a2a030e84cc8157daf4a5a02d63675ede07529dde0 f7cfca247ffb2ea7cef102761986bb68b2e5032245960d800d5fc65d3947f9b4 59afcb1a6088b9530d8bb2435c23a885ee82c3aa0c2e685631a21a228cf07e16 1f4b5692aebadd8d08e8f4468b568a94b2eb59d5d4bf26d5343cabeb20f80f01 897ecba2d98f671188525193fe5dceed743d68886ab7fbaf5be3d09564b619e7 bad4d546d08f62c1e14500f7e340a2eeb8d39e5aa99b9f66d329dd0bce311a37 a6fedfca7f611f99b505df971c935955e2a5179efba1f0323340d67962c4d84a a30f18f05878d4815c755fbe7569d5c042331ef9d94571b0e506892355bf79b4 fd7bbe962d8a76822895dc0b08c9fd60cf1e66e7d91cbb4d88a32d876941463b 5cfd62b1a9c70f8805539e08134011fda0bd8512815c04a96ab1b79f84c6ca91 07477415ae11c9974543d373acceaa7ffac870868220a69e06c3b733939b383f 947545a4a8315cfe81d61a3caf96f5f1a41d7cf354d7095891525ba18d5edc7a ae3e36b290fa5663f46cb37b4d8785778b6e7a5b5da34f28f7a1771ccefdc084 54d701879193377dc59c6df056652fd546b4f3accb0fabc6b6019320cb5ea088 18e49c30704466549121a72ef2756c34a208791ffee54ba4838225c88bb8a8e6 e569ad0ca542a034679e69d502cb792d6a6f8c864c6abdee43efbd188008e9d2 54d46ad00303564a2ee8ff3360eef10952abe041485f2876016994dec2f7ac1b 612af9076950f88f166138e7bd7eba7ce342273816ce9f701b6a3ce6a385159c 527ad33d10c16c2164c4493cb5ac7e3322cb31282c6abc8fc60d5c19395c291f 85b25c37f30e9b1e6afac878fb0fdef3b5145443bd9ba551bca138ba379817d8 f1ecc0173843823f14104228e526537ba7d80aafa2a3f048cdacb2c0640001ae 1cde7b25a4c9b20d6915df9262e220cf42af1620dbe43fe677f97a1c5e021194 40ccb1739d05ddd2e883bfe67a4b4b00807aeaf758a68251b109049674eb627e 541beec1429c13d96d5daea9a75dda434c11c07f445326b1132582caae01d282 646f5b9cc6746bcdc22265a54b7c8043cbcbeebb1ae15b9dee7f6d7c7ec0ff2d 71471b752190bd2ced82c80b406ae39ccc88db4ccc597a24cc1cebfe24e42e63 8cc5a53f3eea773c29cd1a51706f768177f0f388b88d74b2b705fdca00d41cb2 dfe6ec108b198bf4ab586bf12f3f337d341c1a727348582b02e9649a2efd9ae7 02a64a9f14c464086daac1deae485eec3b15b57ba73a3cb6829a45942df6c885 7993899437d2e12076f3311006ec58cbdc36498810766a5825434b3cea5a9fd4 8a99015c83d9788fd6890af7711c4a3701a18cb6691d01c2911daf81a7036e52 6bc9edf373af0048417561db069c88110da2929002ea335ec61b7869501066ba 212d2fa1f059a592c9698bebfa9fde4e1a42a7e5cd3ebcc5dfe4cff5e61150f4 eb4d38583d7578738306c3d8d49bdf9536403765691d7397dfb870b056806878 5086416d7d2681eaef90d4767bc362717d1f75738fe63fdbf399fe987db43485 164a5a51776f41979c044ae85dbfe48a718dc637e7f77602ac4745da472cff14 93cd3e88854c1f515ecdb84622489175df9af084ad7f7072cda98f3b4ea76031 83a6a90b6cba9599afdded09ca949ff11c59f21d715dbfe1b7bb69e8cd30e39e 1411f05a4deebdcb245c7fc6e1260963e848d2d5093bf81567abcf9f84823b36 0d7a2530fb5a816088760d2ff8268e77dae532a31d162fe4538f75faf017560d 0fb071cf8e1fe1698f021a4817531138967476a396b75b3cc904856f2d973caf 030725321cc53ccb7f6131762f53c5e56e9267c4b43acf557be855e62ac1555e e552bf40c1ffaf28e1c467817ef78ef8364d939f5005f981e3c15af2a8e1f68f 7e11b3ad88eb6d844a993b9de6b85439e897ecec629a27e001482461dd40d0ea 16f0aa77b2720fc1c010682081d6cf1a4dc995ba28641cf5bb3ca92be1396083 0d74a5621aa39cd36575996f46f70cb2e17a65a48544ef8153fa3ebd092cd2ac b07a03f471b407eae66bbd77e5f32d7df9ba2bd53600644a6c43ccf2da37380e 2453ee3f5ebc0f2c92ec0b9e520fdc85a5bfd1094774e89dbcf7c47f6a3c8e44 d251b156ff019aae5893d61e0a97d790a6f906970232452c420c2accc10150c0 c5a56fd86a0fd7b6a17cc704b241840b377d47f47eeec7a46b966354655f8b11 9cc2e36eafb7a43d447577fd8ebd8daaed083338bce3c7d150a562e5c77c56c7 1f496612b8a0a887f9fe7ba917df15a26c2a0e5367528b7dc207bfc731bb0ccc 9e53a519f3f0743b9b02d1869a9acde5ebc122f764facec61fbb1b344d71c26f 090a467d9f5b6e63f1d891861ed4b07ca59c39957c94a58b61bc31b0a5fd7ae0 0ef42450c94af1bc08b782b5fd13d4def2d845f1340e911cb3cc1a99deef0987 f56cf062d1975b7d2325078fccf313b0ca0f9bf47fb81a1f9496c59a56c38c9e 72a867219b50935dfe77b3b1df2962f2693f35d1786e586c1106f275e2fd43d1 0ce8053f27a719156673574afa97f4d4472cc0e61508e5e46f34d60349d70a24 7ae67af34879c1a48f48338f3c6a43e28b7adea79209cfe307929d2c213d18bd e8bc556cf00960590987c6b8e92c034421699462da965550e042b0394ce5f3c4 006ee6f0266bc3d1ee4de3c4dbabfd237dad65f3d8d1883be6e8261800a3f504 8bef60f2f5d2a4b6f92113fc6e6644e25786cde40c5b5756f0f0c00660905ee4 d5ddc9afb5043d875d63729e1ff1454e33b625c4b8809d6cc78a722fbad332eb eb9e35797e0754a91ce08ea61798563703fe8209ff6689c88b36d6a30440cc23 32e5d527199ef20bcd81d4a66bdc2ec4e06cc46ef4fce8f047267313b26b7739 bcd9bd9ca2c136832d0469c6e38b8df493072badc35ed90bb6063f10f565ef95 587cf5785217edbd9406ee2210fee0b0963001048943eb4360da7a922e09a6ca 66faac7543d5e35abc41ff40e93d26dc3dbbc0225b283e6803a8586a25d8f83e 481a8dd416ea4cb95da47d4df364da485d7733bf9544956082673aa41655fcd8 87cfab1ca3d4e95aa3419c6c42fd3eba0ec55dbbb4c1dcb2f344a10e0a4c1fe5 66925ceed101bb8ba62b1023f59f4caf1f30ce11d98971e5d26ea31419849e5c d5b3eebf8aa62351c4b731531ef851d9fe093327fb7a4598a5e3b0fa11d1aefa 144b857e9e1fd993dc9bc2677345d5cb721171e42eb90749675e782664e6c002 b93b6b29b90149848c2452df74b309a91ba6b67db97f03d85f0337ebc8cc624d b7b8343623c8331428a87549b7b93cf932dc3008a94920a95987226883bcecb2 7b4538e82a894a23770c06c18aadd0806ba8a9d2a001c07cfaf4be39913c7d2f 925df872ca181459906b39330b8238642f0c2ee8455e79378077064b41615316 c474eda94a395af8db75357c336d7093c996d9d73572fb646bafd395c8ee14e7 0d73fbfc77b3fe3a5750280e8d5a5427417a5474180efd55342fdf464a286cd9 64be1caece30b05367337ba9e0cae59a8b5fca0f872a256a2ca529db87bb56f8 8be33680a41f61ac87e8834991bf5bb066d6761d6b8dc1ee3261ab31d9489e2d 181df403060aaa9e45775e7711244a3eee977179a1a8b265ca469ca37ea8b918 c75c38b856bcd1aba8f0b39da20e99454a829a64aeda962211501db1f44fea65 ade8b070d01fa4825714e9cff03d3888b8e4494aa106ece1b5f19ac93ad662a2 be8cb23501c41cc8da43ff11695b06f03a7bfcb556eebefb3ef756a4d3adf39e 635c8c8532405004c47ec4f598eaf557d0f190101f2dc9092cbfb5d2c194a632 9f0303816b3161b9a54ed048c58f2aebd4b50dd1fd74759b77668d55aeca6966 6913a5aa387f5fc1df608509e94efeabbe1e645a7a0b2ea1731f5c2b76c4487b 3439a5b52138d8489f8e4ea2b6eed403591ffe406c92517fd92a0b94699f4338 e37f7d0be2eda682c76b916dde8e1f59e49a3f728d0e6a384a70617d20d67b1d b608f6462485000a57441392ba0a6f3e080a5702a2e1c2f0ee076c89396e626e f45b075f8bacc04dc537a68e3152714f9ec771cca01e8dedbc8ca0556bae3e50 ab88abef0ba0c0f80019a69d3edf35bb3acaec3b4cabbb999c2811fe6c0e5201 4131146f23703cd3a999104ad4de0e6cbe70617a18db2f12fe205ac0145c9939 7533bddc26066cbd331ea83cd47bed0803dcddf53e44efc8d08bb25622008642 bd4128d7a66046ea22c24c4113bb19550e8190873148ae4f8f7c2ce5088813db 97b798c7b5bee39cfbdcdd05ee03cc41531902fbf2510e2173fdf65f77d33e4b dcf940ab3e938ebb84705381e9bd8fbbf28d38147333027c931969294a73c5fb 8847b8216ce4370e2d6af86d9078e9076dd0d1254d39fdf268fe77fd555ac582 1164bc0ceb2209a98e74b2c8b0208d87e7b22faddfb85264d193b9e635bbfa0c 6389eededbc5d132c379c19f329b97a17504c1d15a0d1394f0c5424d71afed2e 23a1015cccd6b3e32b86be91c5cc6e284728e7361365492b643695ca569bec6f 994c0fa0662667820fe339e742f682eadb7363c2f32bbedb3c0581eac6103130 a07f149f06097c6867f376efdae00a1eadd77df6dd962fef3ff384aa62dbd994 132ee638838203d2f73abf3d2d6565a2b66a49182fd1efcf4e74f337dcef8abf 6e88dc3ef36edcc0a0855b50c9b74918c2a97c143158015391435bcd51cd997c 2f667583cbb5396449332bbc7eadd753dbfddbd858aef3427c1a612d5816233c cea8feb61dce52b4289a8d5340b4309b1c5eb6faf13f1119cd4b2bede2a197d9 784b19bb39c04a0c64d8e03cfa02cccf2bc421b68641a909367d0328917b707c 75beda4e48f1475b63c4e878d44172bb4c4c8223af792ea94ff990bca6b3069b 7faf00a416bd08e6e4ab4ddbfdda1a081a00a64fa0392b95e9230eee21b652d0 2fb7d868c25ff5d388eac019245e59490a83650409c4e6e577df9b3c42f0bed2 b69baad2f76263308d1f073fbeb2f1e8c00bc4bd11191eb5def5a9fdcac60ff2 666f450028ac20329193551b9c2610d8b25dcb9fa8f5940f43e7a60e9c47b7f0 84a1f6033685833b55eac7e04c741af9f760a50d56f209d222b0d2b46d42c532 f227596d9f75206ce74f2f6078986571acf0eba96100e76e01333836cca5828f 787811688624da50922ef60b9fed8899a3734ed64a553ea174255c02b775059e 0c38b0514b4afa77993790fd35a2313f70e2a5c7f2e57504e86b15b5be965b92 33e697073eedc087320c51d1ecf61b54d5578e355c636418b217f98ad8fbdc53 4b64e1c35f9eb38ad17344a4165489e69ea3407101b409465ad17f8b47dec206 7d6d53cbf0c498461fe18a8e68a19ab04374d171951c61bebd95dc4564ce1c98 17066d4063e136e4ca567b7a2dd79aa7aca40634f87793656fb38fde733dd759 fb9ac5740f31fff598eee443097156f3790a54ddb14cfe02aa6cf2e5b3263f46 b7f18404b45acafc9bb8cb10a186a696ae7ffb84126e5458f50a2f1794e15469 929ec779dabe57da88b1281ce1792624c729187e4fdc0d50585ab8528702db2d 8cb2b46ffcad9dcf356bb089b8beb2a461e4ad8cbd5d2532780350e37e41d0b8 25125b917658538e1d422595b7c4a5917d81e47d397b74baa5d713d1babbd730 6ab9febd5a2f5836ad625968e702dacc6babc7fe52d428d7bbae17ea7c3ad61f 4874db513848b00dfa108c53669ebc17506166896af48e3c68148d4bf8b99939 89872f5084d9875e216519bf5a6c0b9211d3ae603683f411ecb84b381895e863 6cd5ae3853e0abe7ab6854b6d0b0926d92c144293ef4dd4f9972fef1ce5d6e37 ab3475fb2007c87278a0b7a48147d165640842d5f67169dc0f3ae2fd305db10b 1ff198d5440e28b78e48d730cb6c210adbe74d42862b41c5ed42515b967b3c68 9cb44a35a2a15d1aa9a94b71d06fcba5758f1f691b48d1817a063b4be7f2ac5e 3b1552d31a70d241c3c33b5ef0358cc846b2565427d7c66094f08fdf0dc553de 5ab0a9edd5f873c69035024f5ce6dcea1fcbae7c75a35ddd3212783d0c692957 ad1d6326f5f0f122e8702a8494a3cefda9c5ecacc61daa3b3fc1dd94fd58e5e1 f8545840aa70b10af0918892800f095aaa228d8cc94527e93b1e5040d1413c88 9209e8b69e44f10f57fd5b60ffd7ea319ee94679bda2321cce9251f1260486ae ef5b023e360229edfcb38c0357ccb791b337630d5296f505763d48ca6d332114 641bf6bd2b1b1356498ea723e0018eba1c4126bfaaf51cedcb144ab2d0362398 6f3e871c296ece9eddc5461072ef8566bb4ffb9071eb591eea8eedfd0fa8a97f 2487fa37a0778bcd6560c8a445d09d5c7fa8ea4f5682c04cc446cdaf1af9f579 7126fd6c1918ff3feb8cf821b586142a07680460d7dfe528aced2980dc403076 d4d6a5d8655a8119d0686d5a82f74606037da1a7a2996ed25e9f3fdf77ca391f 8cc8830b2213ec306c201584340f4b4f5cbd3b3fa3eb36f75786c822e22e594c 32d173fabfc93d005f3ac3ad2366703b38907a2672b5bc724241ed87289d48d3 231ea19cb8dfd1538ad9358c94f381884e5eb671ebee6de0032dfbcdb53740c8 81346e00b86ca5b96febb07161273ebe427f374522df2ebac148d83fb1c84b65 6622ad031e4a255114ffcc1b993d8d3e31c6d65e40a59574905b216a352a37dc d74f5f46d0a64f9dfa77600f3f30b38d2a0143a770e664119f9f77ab9d2f09c6 8e653adfaf5064ea5eeb09d51dd65dd8655fa7fcba239f4ec6ff41379d44768d dd71ac0050e36750b6e33c90efa389b856cc6d06b4a88a2ec732bc505886f114 005dbef9166eb9d661b8954120bc5d3a26f0019ce3f3ed9323c45319e1820551 f5b548cc9af5856e732b3d2d40d73ed801d809611be1b95d3a0a4578974b2d20 defc412b437cad2fdc7299390a68aacb97e78252f38a80d9edabbcd3a4abf3ce e0af56e16a967fb94821b853f6197c6d9994ee0aab2b081ec16ee21f228ec0fb 2853b9be2f74e4731ad0d77b05fae3297fa7087f6e9a6a61dc8295035ed081fd 4d6a31f13950296e764b25be58d3f379d79b3bbd6b38f01fb6d3e473a4f51712 623c7cf4765775246300ad80136a7362cf5c9d862708aa7a19f57f56cc06dcad 548e4d75e578610781844f271035461009b327954bb099a2c1e45ac9b08c6df5 191e63823ab830693cf02c68e19d49e9307c7a4f044b7698dd94d476df250862 e8fd2c2f0a999138fcb936569f3f470c4d683580d72cbc1bd1e4b36faea2d972 925689b1d36695f45765184f979dff2f2bc70efe57e37d9226319b50b90dd469 8fd4bb24b442796650c1f0d825f468b955a34c5b61189a1c44b6df47feb40d8d 0361ed2dcb2a570f577b35cc6d7eeac9b617d70b7967fd4d30f94da25ffffc39 d732a6674821514311651afe2142d14be9526596e9f45e9c8cb658cca6fe53bd fa76c7ea0f87084e9543939eba23cad3d3ea2a0df03733044e73fa25859b8cd5 c8ddeb6e648cfc07f8ebf7a677ff6a038e747e37923849c15c45342afbef8493 5c431e3a46089c0b41ba2136374f0e42ba6f9555b0019995b690e02d1800d814 93dbc3d9dd0820e634aeebdc808ccabfea052b77199ae04fda47859928a371c8 df9e4ec31e852616503784e26457faad8ecd94428884bda9ee6d81c575394867 f9707e1b484adfcde45bd506627735685e0b3663ef00a41c48bb1c11cbc266bb b123a4cb6366790bac198d2d3d400b201c75cc4af7a3430c8a546b9891ae05dc ef38093824a8b47c138e20844f923934f435dc18e84cce31a679a61fe68917c0 e81b5c08382f256377a7fde70b56421e7962d4b9ee90378b201666a0bf88c6ab 256e4f321a15a634355690e49bd7ad6b92cd51c8b1629f85c49260c0c3431f63 ca7b3fdca514a286c7e8f440e8353af4a030c31e5f27ecdbde302a1f8269c482 e6a199df5a488561924613397989d00c595ab63019c2bd1d8b5df37fb1ac7ada 4610c481631c285a6acc307c9439f8f8acfc306f47194e2343cd74efb7007e4f bb2d60b1aeeaba077329246a5a406c310a98f94f3c51ee6dbd102f9e6677198a 834ef843a1b0508f4862ed8013d6555cc21a23c52fb5609c2698c20db9912922 f624c48256355e29be4357a424d2d5d6d9d0d4d8ed3045fd28957d1b747d1867 6168d8666d802eab8552a1908420575c4cc9b5dcda2bbad9c10df93d7b665931 206b2c718e2f34a009b1f1ee7df74b9e813831f175ad5d06e520af8d0012d178 61981a77843ffebd3625d850e0419fbeaaa7a08688d92266f33100d7b6c40d0a 141 ab1fb215d64fbc7e4c3c4f01ea4376de6e31f6a8a010cc658420d009d36e55089e7f0d30a81d737cdca49a0a1008ff69fde6ffd1cf2e611859ca1ef81819ae0ebc557327f33e38f2750b6d66ffcde96ae11e625dd3b3380a8dc199f68a063c0380e8cf746b154b0bb32e13746498492cfea4f64e08225f1b48fcd49bb570be02ddc692fbc8525e38ffdf80ee1cd3e206349542e04597273f7263816959b7970e31eb189c37c67dbf4b840b984497102c88d0400de78ed3a39b52aaada390d20bed22fed65744eafaf541661470a0be697eda136be7ddec44bde772810e147f0791b2096138a56c0db00acf93054f91ac6bc0e3657e203410ccaf656cec06480820791ba583c3c89fe6ed44d39cab0fcb871ac47007b942bd974ab5b80e8f9304350f5cdc608bcd4e9f65c6f8ca48bdd3d17eb196971729627183f4a485b872032adb03ef61807b494560353a65b3091d41baa37955cc7cd4756505c38f4671085a84845566bc7b1d19ea1c23a08d9ff0f2db5cc6d6858e9e1e969075dc941106f3027c7e26cc38c8b8df447d6bd209ec9cbf963b2b60e2030440d0801c4e110358f7c55801369b23f8e4559af84ccc9d87aa14e9f00a1fe65ab1c85d0c070e05b2f2d7f553e032af8ad700937d5830f650880c51033c2c1a8b89e0659f2f3c0a7b937bda0acd91d2c1cfa3ec862bc2bd49666464a4b71b86d0321daa0139be093bd15251aad8f6af5e026abe6a3063f7fdf76453429265d136fa0eb62acff00a08fa64f9e63377f34644e8ff27db06357cd0e5fa31b3d1da7f02091abefeee075e0850908296d9daf74f360c9af16474572c38d928aeb7e7e3b1d299a0ec570fc63989ca6ce05c42df06e6409e37c3fa263ce92d361ea315940dee71267d9b0a09bb21390b2e37d7fc0e2ff5b93f3dc93997cf0dbafd27bbe0a999a56bd71906e618f93df4f53e309147b58b3978a1b7af931d884cc2df5fe18a2f5f6256640e2498eeed0874cd59217c7a5a2e2572006a2c3eafb08da01f31291ab69cd4b80a0d46f6cbb5b05f15bbeb931567e3c1d47f57902cbbdd9708cd8f9311e6fec00f266989a39d73ed9450205aeb878c05af1314c8d062060355d241af56145c590fa24335ee26b5b010b3494b93a37bee088c0185bc1751c6e01f4ca5d233fc2c056a08ce6cc681323982281d32804b50f510191c6147517775135c118415e4d40463f5bfb4f36152d050f908fabf5d714441d08474f5facb7432972fefa599ca0496f314d1737d48dbbba0ea092930261298e51f98dd2bf2eb0c3c2e9f84f0c308197a7ea2738129dd2773d1309e3871b01f9bec883508fb69ce6fe5a162fcb80ce851e11aca3f4cc918367e40a10bc1bab765c4f8872eb60f34ff5d282fe00f0bfd5d3e5168ae90fb4200851850d5a88aaf650bb3c0f356490c7c8cb5dcdc18026645d891455c98be8b0abe0067d0f1e9aaa6930fc8e3c3155fd0d8748c79170b5cf8e73c8ef4bd03851a485ee0e8c5e4ddaf24555673a06debc8c1a47818520b088becbd7099212c1c4ff64382c63d9ed1f2cf25f3ffeb7f31b9bd589e7010024e3f99eb151c294198dc98f04d08596da3460659f82ba21369a62c0304371102e903fd4bd2d78d06f97564307566642b50147fe28934cf197ecc8400bc4eac0065f1d2e53672e229ec0bbdd45e26e3598c523f4c1e7255329fa501e64d70580ef7efac180f95770585486b8c620cf724edd86d367519f7273deda6efb33ddb071df3cee79d69811fd4f8075bc03f8619b21c276fce3bfc45a92e0baef02e240b8be54dddd9d8ca16a49d7894823b6b22e61a0194210c5bf5f643e946421c40067c2c3614a2e7d6abaf14de09f720a8ed5e038eafffb178910f66f2a275588e0a1df2abc2014d2db73259eaf18ac722d94ddde567805370d1f860baf274fc9d0e0914f64755376fd53152a3b13847b714f1b86f68bc90795d9130b726cbc99a012b0763d44270c11f5873e9f8109a4f83eb83b2e4e7681923e8f30467c6ca9803fdc5905d54b66383e3cfc1b7c5e7de5db9985eb335c997fe2e920da250b7af0034e34b01add89f24a0cc92e82bad0e4328bcaddda232c05062895c0db6cd520afc82f3ec771a90a6cb9762e7421e39632c8beeb223a46a9391b34f1214c1050bb377874ed8e6a08b0634da87ec1fa1afc76460111bb7b607dcca77b3e7d9fe0d791be4d72409292ff9bad34e67cb21cf5d6ba6b220d77d768546b977f4c02800c502fff761a4384d1fda556dbcf16ed0fce9213eb1ae4ecc11cb50a16f60db09f0705eebe70056716413da666920d7ab0fdde72f90fa49774449d4c2701c9e054573968cc5719a63bc4b7ad50b7ca9efd6b1d3b19a1f135c6128aa42c6a0230f1f398b45b88f69c9c148c8bfd072dade1a7789fe25f64a4525b4e6d75761c50be2e8f0ad01d966ac7599b409a101c0959f22870db5f8d330cf2fdec26b34be0ea54801cc0d685f89904924cba6af5cbd7bf2c5865caa6c893886f3f1d6d8200668948c0855a018e3fd0c74a58799aaf7207704448408cd3ee314cdfad49dfe0e19fb7b86e762f94d35081e6512252f3055216bba4460b96fba0e75ece44bba0705f5b413a6b88a2fde10630675aa49cb926b48ad1ac79452937f49cece871c050b39d3a54fe0a8504984e68f751524120b3aded08ea867d8faac8c688e6f880a5accad604206897b8a8b067e15a41aa053aae606ca5963ffa6e538093ad8280913e56105e7c11e9a3335033fd03472d76f50082cdabe05fae23b1c8a5196a10b8ec387b1dc87902010f25caf6e0af83fe5299396c85c866f436b5fd9cf20390bfc10c7a0056bb78d6537b918a826d2f8b80520bb8f61d3186fa6b57685e2a2061f1e2dd90687c2317a5fdbc1c5d88c99eeeb4d03ff66096803ca14669e2bcf03ccb4722719db4928c9fbe97298999a21abb195cbc6fa331c851faab9b6f84c029dc9702d89dfeb143bc11c80a89929f10d4dab8c7fe04bce0defa51c3f815306c75497a6ac01116e1110d500a855243a8c932625969aa74f504571619e8f7f00e5269170c817c605aadd7ea60dcf5bd2bda783e7bc763832d0b158bfa4ad9c057cdcd027d39817ea1d9451f7e35a469b5808c909423818656e6e49ab7df8590c71716df02908ebe5b114151206c27c4e6b4faff654c9541498d49830d33f150abf6a39883e365e33cee6f69f9d7389459a1a27e9a959b534ee5d9734d03bb30d9300842d31a83f480f481927f9aac0b45addc03eaec44043bc3ec45d19f5e3043cac7f300058406bc9acbace495c8682c7bdb74c7bdf07359f84c378bd21b905a08d7507cb76385a0c8d15926e3cb29cfac7d02cb52e2ecb0681c960f6de3c0ae985f5e5447f204d3d8a1b53e607029dace2bc74c42deba777a34239b99c080a61648582e6e068b153542c3cd5af2f5adbcf29dfea5b16e28a1d6be47cc82b0c616acbb4c79b149df5c052d80e4a7ac4add12a48a22edcccebe2657cca5a2f000ac7300f55ca56bdede30cb3283756a0c11c01a607d9c762f5826862beb83b039366c5504e2c70ae0f5b18c1b27e49033b0f30dcbeaedf2afd6e9425f90b46065dfcc0f351fc6064406514ffc2b3a93174d80ac67161531a4b39538e6e2fbb0e0d1e2049d8d11d58f0893a8a913857baa7afe9dfa0c4c615439e654283b37d01aa0cc42f90b06903e4fc4e18ad8fd003b19709e5fbd8f643a86b8d083d4e07045002fb6a8aa4f414ac973ef3741fcc28437a6ad79605beef16bcec0f9807580b77566ee36360e7e3ddbf7163435bb1e98d2ada225777f05ca84226f770fffd042b12e0ae3594fc3e8ed3a290322c223f553d334211c0f381d43836752042480f8926171845044a4abd09e895bfe3ad2c9d01574ea3d008a228ff827c5cf25e017e8b969f07b04d0eee2edd6f2e80d5a7395021156fcfc7227d4f4ea224ef05089dcb07f8e0ef02136cfacafad31cdb783dc31502809b9486963a1bb24eb7e60c2d43b048080affa018b7913a847432318fe68e6247942975f7ced72039e860043bcfc9a2bf30b3214b1240ea03959071729984df735129eb9b1d16c956e30e0ae52509f2c205a82eaecd0b7970ff6ade2bc081979cf06ae21af052ba2816420634098c4fdc8c22817843e2c08af864a261319877e35f06b54542c4d80ada710fee11d3030ba1df9d61c572b58154ae658a052ff4cfb7ac3bc5939d4f27deeb053417eb3c93927f8fd7dc5e20961a09a5d544c10efc076e5a25507406bd29ed0f5963fba069d4dadd406b143e8b361f535ea3697aeed4b8973c63834b50293e0340e2bc94f77eb38647ec0ca02122dab1a3205a538b12bc5911448e9e4da9950fb3457dd06d06b3c284e3ea6dba3f183d2be52d9263f1103fe009613637930307dff5b15208a7cb76aabd1434210cba0e734ef02a3f8ce36764f593acf23ff50cf237540a827b14744f3482cae9ff2928a5de660920655fe42c87e9e22631d10f5e16190481dce7cb7599481c27d9b1ce20046b99e3697992f12e599695d9bb0c8149a988e7c93eaa91fb3228b151cd26cf1dc7667817364c53d8460d307a930e028cd29407453b8ae839f0989171dcf70ff519e5e9e4fa15e6ea3b6b0b41dc07d45cc09a08a09da72561f891e79492130f00b2d3faf1d2e5f220f1dec531800100d1d82016385933fe98190f32c81422ed36a26391f966f10fcaf91f762cc8047f9ce379c2f2962c9d1a38308d6e10c95d540699b1310bc041d1f888b4229e02c19c760b77b37d9985459eeecb12273a423ae0d5605c39a7abb0ce71841eca08a006b9579c1c08d2a29bf8f2bd14f51abbe0e8ac9e8755951377171b13164c091998712b360b8742d0102e67372fa53f9da9220e09c8881f9926fbf7e6cba608235130abb062e209a5891733e8202e8058f78370bca5ced0e4aa0185b63edb03b54c372a90f21f75613585a4fcc20f08e2c00aa5ff0da55dc4955c5e7361de0aa561488997704865724a8581a010a9ffe74560c24880b2b4a0c8996920c29d0b4460cbe6da64df6c538eb828f16bb3f82438ee3b594a9cf4bbfe13e5bd297d0c7cc4920319ccb0efbe3afceaed02787515d82d99ef73093aff2079e8db8ed20f75fa33dc24a823c6665a249d4dc661516a4128fdad663f0ee72d092bd41a1b0b645805dfc2dcf35530ee735b9f3590343fd965dadd952b3cff6d5df705e99904a5ee01abe290f1d86ede37ceb70933d790ff53669f9b4e5117ae03172cca9b06afcca3a72786e5d5b19851935ae54e5659540adbdf7c9c0a9d38952cd9f5f10da48a4b0930a5dc83887af02f56438334a77c8db3a683358a558e715c0e56c402c78a7d09a78e765033a9d42e5f20ef551d980fc7770ec0643cbb7762d9504505fa41eeae028b235fc636d46041d772d2c180df0ced81559a8bcfac842999850f203aacd834622c2260ecd19d825951ef1ee1aa34a2a31f4ec23287de0eb74104efbc3ed8ca4c5033f0d95f427ef3a483417edff1fcbb29271d0edcf154765f0fec9bfe606453d3c77b23905cfab13cb8044b2776c4c5d13bc73c60b025b48b047f35fca842ae3d3316ae2a0aa61eca434fc79507124081505404b8b32ad4070f635717d289cef521b4d6e1a0e3801c3ad12c5204776cd4f9ede04f45265bc5063c7bb74da601392c99199097c21dd43a1c39fdf9966f65ccca7f3e866981060468f423d4896043bdf4dbe69e656dec113e86a0af6dd8acf8030482c527d3d501f430b0e1cce73c91a626a5209b74e79a508d8836870fc85c28e4e4dae58a5900f3fb4b28223aef935f783b2aa1be5670cfed32dfcfbec9fe4956ee9a5742710164340b658a2d51760a3f6b992ac0d611566ff537fff9c85568432298abe6900001adf3372e0bc5c8adf10877c652ddf6c0c7b3b7e1a55330cc61990101868b0819b60ae89ce1ca35d0f91c88737b29cff8cfff5fefd7fbf6760aa66c0373a704d9ee152dd3ac8a45b1feef865f60269d71fdcdb5e64312bad7ed57f09cb9fa029319eb67a1860dcc6dc9293f649030bc0b72723b6719e1d8ee1a16ba8bbcae013e6b209056ec2c0d71e72a29679f2e09b0e9d96c5a73c113304fb2041c15af0a719c570209fc35b1cd2e4045395cf71126068f850b2ff54bfed7d7df10537c0beea4a69b798139b9d7091da9735b8a026bb0ca2d75cc2a2417ffd14ac3ba430e628666c91d88d920cf5e1d672cc732c8e0180fc0653bd48d344e67abbf56e8044f1ebc12e05cc48dfb0a0614eec0fb4a4369fe1f764a28979e43c6c0bcb1a30b654861cd62197b0035a40da110f4239b07339d06d8b9cf3d918ec7fd905a7708173d5fa3addcfad525ea1301e08e8a2e4e3ab5c84b33a8a9bc861443eab6ba00596719ff66e957915f0fdde36c96ca0127ae10468de11ed30e0d9b3245626404aa958e3214f8c2a577ae9a66a4156d0180e1c955b36f9d32e046cb9d33553803215e93ceac4afe88d91caef2efb0768bf3cefc109c1e9a12073d22d46e2e520336ded892066f76566383550f04089f2a3fc23594bcb903b580aff57b8fa1d104241b3e18b2441eccbf09710c15f59f41c1e606622ebd803560cc2f0b22a5e80d8322bda71d6d5562dec57f57961e7ba2186f39812f391e08fd21fe6115836f07722a88e2d2f06d16ccaa65f1d9406711d6e8f9cf1f9cf28e9b65e3eda094a50c96a9dfef4dbd0ef94cb5327694d071b17a17fdd86f81c55a25e2268f3af4670a32e8873e35c7320aaa8b793f060011fc8e71dd3594f106f340d86f3990d2b00b3d016f8778025b2f2c241507179ed165b8bce6001157368a7e5ea50f7475e60e91f4cb2b41d23fecf2835993295f2c0e871be5d8dddf7cb2552c61afade985055d95a8e6dc6f5e69ed49c9b05f4a0cf54e969c9b10cca60da19158f5812c6c04cead56dfee889193ab86e803dac1c5ef2e187c6edb3dc88ab775c1e422ddd3090fb7933dbe284baeb1de3f10e999a9a745cce17da2d7c0a360bc69564f92860a284949f29c9c396c61249119cb6e8a38c0f921475962f4cda6fc42c718e9ec046fce947c3d284b6d4139fe0c7bfaa26f75492e25d7a132154719e5319e6a0107bc81424c0c2d0590ec5e2332c7168a1b260ef2a95f2820148541c343d52cf809638581ce07c9f12efaceec80548043fd7d47f4d25e57dbf80ba55054b6890201fbb4ec8d061ba553d9b099999875a6fdce15af3335db0651c98c30e68295de03c31d8dfc059e94a24ee022951981a11ab491d99f01d2fbbfc21e5ea2babd290ebd0107cc4d770dac328077e71f7aab62001184301dd86ba403e0dd66a9fdbf07509b6600fc7a32cb6f36013b77987599b0adbdd507786c55729b284a83bf4d027bbcf3ca4315634ea40863ba60439a0933440178f052288610b22a73220ce9012ef08b538b46fb24f06c7895ca84901e14bf0797494b3ca0c3363b078ae9960b0ec81b0bc89762e50acd775cbc1f27f5f1199792b937287d5ee8d2e33bd1e6003969b29a36333b16931c5c409258833440e662301533c678b4f2bdb63345070bc3d233d1a8e60b6162e790a3b683ce55cf3acb7a3603d199cda5dad2ba9dea050512dff61ab32e0957d69a87a5d2eac76d2d045040a73db8bd1515122fe4d601611014aaf1235d5611371d9e8a4e5200c7806f591c48168fc4a86d37adac2809ce0bbcb537a5b469e131e79a08f8ef9cf8348b11ee2ce2e938d76f1bbc59220d459d4bf756ed9867fa30fbcd369c9aabe2e99482d39b340823fa7ea24a63470379a61e196366c5cac51d8b1bbab1a93b0804edca1879a7a3631d6e110597e70756b7e2ea006e4a17542bf1593fda7dd0cd84eebcc497425f355c217cc222ce015f29bd08cc1d5cfa95481bf2029320ac9fb99c2e5334eba5f1699115c28a89066f5307f2735c48604281d0df91a43144baba41ffe1d6660cb24f6373ed1afa0b928892a95b235b78d4eebdba762b96c688436216fb48e5e34eba10281254c20dd37cf56aa24f97fe7c55bed85714e5a0307018262885191f8481d5a0bf8abc0d92978c85a8587e855e86bd5a92b8b96ee44add6754f181fe1bf129109cb283018c34846fc01c2b681befeec173e0ebbbbd9262ab19a9c1395a8a363018b47a0799edbc9472040266ffe8af19c1e0402c290eb718a86ef0387e397c945f44e800935b4137144d742899d3111bbcb06b548a8b41425474148da36d9ce21feaac0abf7260624331a4b6d97cc26cf652b9c51f94f23d933ad8acf2854b579bb6ef00dd709624c761ea2d706fd817faf74c88058ba08e568741b599937d14c2183107bbb012884340ab18c6a17ecacea91826ec7a32f282912126eb0e78c468723f0c1a481eb4b702f6241baa0167aa8ca7ba150ccec658bde2aa9496c69963dabd004525a6749d3618908fe941203ed085122563bd69b49e10f4c390469802a3240a14485da825b22e44d685ee6e3fe170c554b8cf2960101b6be98b3534fa00c4038839667928405113ea086fff8818f0bf9a45ab0ef3b7dab8f8bab618af7df704d19f569168ef56e22a0e203d9e693a65c28e0e5445dcc90f4691e397ffceb30ef2e3c57bcc069902f708f2e9980c2a06c7bec2c0153785864288010f539bff0cd801ca46e1d153b2474b4a92815d32eaa47c3d07d359b1d71bd2e1b5459dea08fd3c78385cf7abbad28030e9b2673636a108ba2ac1e98ab85d1632292399d80fdb0b60208bded3a97ca63fff65cf13d6d482f0d8cef1cfb588d677664cafee064ab4a739c5cf99cb51bf7fe4e0d4fc36670605b2c59dacf9f5164a66ac119c0f4038ba38ea06b8843c3040a3a2d9e836b4eac9929d60ca82ff81f339f80b7a0a3beb37d2685f08cb48aa99a6e7812f501ae3a12e3df224c1695580b6452e5f0aa81dc77eca85b503203956106c545c062f3634d017f3860e870352aa0ca03f046e1f9d609a155633bc25f9515212a3f4349cc9da21b8b2fc3a4c57537c30800b4bb5c597ed3d7530dc29b6381627ccb0e8145b8391d277800b5ee5386fd8ab092b6d5098bed8341a7f062d58e5381e610792828ebb3973b00c7b941361ab2b0759a21dcb35c31aa15833bdbb3abdb6e1a24a28c0b78a42cc2fe113665130f302f728aea92d6de30655708ef3fe32a296628dbdbfa5521de8dd716911ee6f34058210f60c294910a3711d3bf5f060ce1ef07b9947f9ed243ba94f4f97414c060f5a805c1ed5fff948823615638384054bd322fb128967e8e77ac1e8e0b589e908b086434b474c628656cdaa2fff803d50f688eb4f2de41da0679401d81253a1028e1b826083b0cfaf4bb527d38051e4214dd492d3fcd93c0296aea777c0d3690efebe7ee1104a6ca67e109b35d21ddd615a4e4f4db02ba694d254158413be0605b57dc9d2157741fb603ae5839c1f4f67fb4bac8575dbee59287310d625462f04856041895f99cf1e73a6ea4065902307f0b5c9bac6b0ee90cbd65f06471e340324727ea47c3a45f1666bbb7913b15d8a92e49303d12c9e811b8d1f895bddbf04ec2d3b87fb2e70cb87a0431d5d7a887116c45968fb79a870dfe3e297a00439063dac09f303f07bcb73665552a4342490cb60c30aff178319406cb9d97de7aa0c3e5e5254171a11e813bdf5494e13e65d2c08d81371815b9abf1f31031c185f075df543c58df35b20a16c7b5f1a23d7dab9a8e81007db9a292dc63a320d5b880f17874be10ed6e8735c298f0f1696608168247fd2c070bdc670019880ecfc7803e45303ec93afc4941e2a6a90cdbc9b463114eb5f4eb9ef3b4c24909eb96b510dba51f59eca3351844cb30be921d1ee3e2520418322e68751c7e6aa7c9cb8ea0e3e551da1008be9ff608b80a3e7ca60512a03dbc46946f29fc83be4c681f7200a9b80e6721aaa118a2810fc9459114ee073eaf6601d3d88b40806b9ba4edfca0240c9fa671f6c5445716df024c5a0d2459bc366b61b8b19e4922c959dac5a600bf63c8e69182b0feaab2f55dfec2847e0dceb5e8c52802c43edc34824190a6c0417f52008748cbf0c252a7436d62eb974bd2b0a33a15456373d7c743b952a680e23f3897f22904cef840efcbe6abe093b900ebaac721194b20ba3666eeaeeb8084cdb8b2c180d7d0a6a9191a2a3eae9fc1eb8d386caea56277c1eefc8cdee7605efe28cd8d32d53c4a98f6459979683fac109a73a52c878a029775bca072fff0e2840b88533ad7b08ce0ccf3676673d794d76f16cfab5346634f0b6548e32c5051bae29a08c2a0bc46dd7232dd60b69bfa66ddb1afa271371ba83ce725ee5700df87bf68c623005c2052751188fee58c03b1c38dc92e98ca4a68d1d32e6799601fdc87e24d70f9cafc2d99746063de2da2b8ac64e7b01048fa48cd25fc0fd3407e1f66da0234dd99b8ca21b7760734910879051f46bbe2ac46d4afad0150c360470938abf0eecb5cd0a0619c8bd65b47594d6de315671a26df314fc7f10a7cb04939c89117a4815f2fa4373793462a8c71607044e567bfee8b619816cd204d80d72e9155da7a82671400d1a2cbe26bf87756951dc57a893aff7a1569951c7c90f4971a8e5231e2ff5a28b9368ec4f0bab8d270c6f00f8896deca5347b91fe4a075a7af8387414cd330967b59238e872b218f6de2a0329d8fcb38e5005554fd40b211a91870557d6d647cf244344f4878ea1fd4f5d56b21f9bd2616e83bf587b0889cef9f631f8c69554d521aa9008883a33deb082cbf40548ef2d4f4ce166bc0ed3c868014c78c5be6c4070b28a864966311976b4f6705787861f53e5379f840d1d96ed4b7b2aea312b1f34214ac5663a8f243f288152023c85ddcda052660f0b3bdd3e48a69e7703d53d579fc5225e7ea0ab9c716ff8a693494ee85e46ba9b0a2383b2dcb87aebd42f300971e57c03c963b20f8b59c0ce68dca2892da8d8780272253bb9f3960a1d18e10a5cc272deeb2458e7fa3293cb4356e8559e2cf64d023441b0a116a4b3822b086bc560e1f4670ab2993ba57aa4b2db1a55bcd25fd40e34f4661cba161df71ac414fb46e3f9ec4660bf11d207afea16eedf9cf9ab4702da879313767d399577c103c1559452e8087690df929694b1b417d4a5b367e70852d731399b7765897567418f9f52cc41ab6af07487c0538a5a080a81d9bbe80b089f98c52bfc59ffc3977c0550a832d670b695c5a7eaf880b6f951568365e508889cae5e25da80836ba5da46112b7bc90f23ae0be21ad84c303e19c450d54c081fa3b220460418281559d0eb7c6b6ca0129bdd16b2299cac74c27ad4f02f7904a3d39e398903e19ccccbff8f07db4d0eaaca8568baa49c056bb6139b594b8c02f9e4972c86e5ac463e0ade613585c2e546c6c3ae9e293ef9124143c19c97aa030f93af932fccca44286143691d9d9f6e1b0c7f9ee73822104dd0e4251b7b400a0ab3bdae3a10605db30b88244fb252405767da5035ccf292cffcf09fdde12809e94b96f541aa6324260333c6914afc72ca1bff2a3dd5a057ffb20b94d008850ee2ce3173aa08037204d4ae3cf825cac17e32f284cdb0f6d8388abc6a8e9f4a0a6d7343303dd58f09d36e2985707e0f41768613efc107e1ce3f911372b6dfe603921aebba2bb8c0cf6febd785b6ebfb1ed5a1452f2067ddff8bfabb38c9427b0db0b64ef0702e53c995fe7084407a681e692b689b43b900c6c7980ac934a5bc017f0fcea68b71c2f398911ad990faf998746ecb3a7139074a4edb72f0da158b06ac869e7dfbce2c0bbb59392dec43df6ca1005fe55070641cb4e2f9a098449c0d206121a48e1df26cc3d39cbb3a5c97252a6f7c020047e60b9f34d3a444a0ce0218db46278dd1dd0687d8c8df5e9b4c8f2b28cd304b32b60fbe144b9655ad1508391655bae6fbbc044aca2768605dd2fd29950eadb9d2595b0c85348eebf80001a975eb9cd8c1d7280d8eb4fff5a8583e85ec03b864211d518e038c28b04df70e08e3e5847c10a6c389ae0fac0bb738948c702df6643509541cbc6d72b0512705c026cbf1958a90f5a5c2afa868f4977aa0654af18efd20f6c23eba86a8cd3207bf8f599c70dcfa6c62822bc97a84f3e2c555027c444e63448f7e390bf9d9400023df81a5f7286047cf7ef6f599331360559011e6d223ebd7494e3f685bda7e03d527abda260db3e74c8d3dda5dec5f6fc86d0e06b689df5d5f33fa47b49ad40ab62897bd66b9dff75cdb66477a23b99e6e6fce13329a25e4f8963c5482efeb06f3f211a5ca7e3197c6959e56256444ca318b52b25547161a750b754663a0df073922158d2201a3c238fd558cad934d7dfb8b5841d2b08ceaf24f6d3f3f8d1d0235053c2dbb961661cd49ab3ea072c7d5323b73b9742d0a413296fafbc54f860fd6041859f72d9ac9335529aa0f120a55f56c41df4573ecc017c3f293a4219f09ad36ca72d7c9565730dbd4ec15c3493166bb8bd24ecd05ba50e19baae305c405688eb9c2b5ae213a998658aef5ecf98344d9ce29d1a4ba79a58aa118929c830b4a0331b6dd837dbb8820b33e3417e3479225dccdb72898e80b2a0b0f02a241097db0a9848553024adfd5a6ae0dded46db0bf8664e3119da60523b707481a830e60bbccdaf4fec18e87e5594c4e725d9c86b30f42d33a6871da9592c49284a10d39c97e208850b25043dcf5fb57c85e024e55361c163b0a0d31374ac92e40db08ef827a970d3da95ee27e1ab3d7a06b075e87c18934f0aef35e466c1df14cb3086a71d257a5db4172645e149f3b66f25573d373cb7102dbee41759fbbb7825c03adad0f8b2868fd06e7c5dc20fa1e8877eeab6f65a4c73edcce23921c989e0a029ba93869de4f2574823f1e1de909c5e1f773a2dbe5102223e61fa84ff70d120ce4a46a450a62eb338f2fad947ee40b66d3586c0fc9b760677cb0a64a6c128a074f5e5138f2cb639a12269f9326ac484334879a31d1f11fb7f7a6b6ee46c6a7053d2d30f871231942d5660c3d3f114406c683126b64918c19d5f90f951710f6024708e2f569ceaf166845ca952c093ce5b138ae6c2f33d4f307b22c156cdea00063404304e0bfb586a5d4b88f70a3a271e47de6c0455f330e0c7b94ac93d35c0309e9cda85387821c6ba956bd39b1c2721aee2ca0fc1251339f2f0a45d0e38c02faee3b54e3ccf5e6770560b80308395d10355dfc3caa120abe0004ae628c1e09c88f6e10d0431b1a3e90762d8f138ff7e54e97561a0f31ee9420a13f17cda1044efd7bfb956c812bc6ef656077557b7fea2d5d0d47bf18e2faa5fe3f393458096271979951f4307cc8a69d1f2eca3e0e386f159c9b43cd28729900c0646b5204f9959387c4d54bf4ae40b3bccfe312673530d2aac16ebbd4455f675bbd660403a6a8cb841afc7c01f28536762bc1c3bc57f0fde5ea248822a88034acf68c3f0ea42d352e5bcf51c5ef62c17bf0ade7321dee5de65a95c0161751c08b2444f00e9039b031eb277fbc08e45b8c232e87bc668b0493bdcf0d7b1498552c82fa6f050772eb6e20a111d6bf6caba0eac1958e084dd819396f08056e5141ea74cc2b08ffcbb6f7b2f19d8aba02fe0b09f6befacbd2308fb3fa91115975b6923c006e02dcbaf76b7a4c2efada4eb81d37c6a2be4b3f271eed710fd13e4170dcb9fab80af5f2bfe6cab56d5ed2528edea9256b34620c363f4ca9f064e4f937e26a801d08866b616762d65445b4d11be160928afae8559bff299222dc532486e8bc20a7008928b10428fa49b2b6d03a9914f9bf0021f649bfd62b32b4bbb8a85acf68390997440d34c1bc8b7a3bad0c145433ddfad62bb5dc5a79ec7990ba1ff36024a60988c05ac4cdc7830ec062934b4e4c60b1664006f1a82e75da8c87a8f92eebd10dceeca0df6af81a48656e888b98b5e18108e5d5d9424f448089dd4feb9a3b91025687f6584d810484620302c7cd27e1b17cfb3675de2f5977887307a0e8ff73021e663c816ce008cf979eedc9eefc8a252d577bd187c96883dcd50da771e09f02a4e9d6a7df61958926d317afdccf83f32d92155320d718f6b2f3230249ed73018308ca28a0a5d0c517ca50682310cca727252f82bb852c1fb79c883f4ceab20a2b3b8c3421b94125c16ba95a9e551e9e688a263023435b2c66db2446046a5404bad0e48ab943e93d754013ee4de57a38dee3a9777367dada4158ecbea6b4200431a81c33fee4bfef863a9335b086af8872e06dfc691c04e10cc15b1727344709e2b77db6f6ef04dd4aefaae96cf568b56dad2ef0ee91ba79a8800d8caacadd07f8ddb4c72519a34b5688eae4d6c99517489a0acd3094ff34466664664ab0060f92e76bcb678b52ba78309b89b5b27e082a02745f60adc650fa104b66fc2f5205b2b87f5cb966a73ac301dffa8c50723b433cf222bcdddf4c62bafee78c3cd30866216d56570464d76f2c45ad4d262363489887589cdf787dc66f405bc6a66c0b85c9dd5d3418d79ac8ba6e6ba49cc2a45807a4fbd78183cc3538b2a93ca9e50d964e451f9ff097c9ba6ecde12ba25d18ee0917fb5a337278d085fbfd17466f01aa5e0a018f804e6cd08e22835b48a11ab8d08a10dbae6a511f4e1c5ee7c87e015755356f9fac2f75cfea0012ff04fdfeb4853ff200d34ccdc7ee62ef3b39f50471ecbdcc0cbf1fcbf7233de10b141a0b4fa167932a22e7c94712ad0ca9b00f08f5cb26e11765c1075a0608afd6e10671dd944cad6eacf44834ed32bd1dc5e9039cdf55334debbf5af0b5d7716afe8885c4e447538a051e8f355c3768af32a50821cdb3dd16c6cf865e278883281ab0813c8184f7bb373b09a8a416bfa9dfe8052f1c2391a0152c43c0b16fde0e74a0c8f574457128a9a311421fe485621fd80969e4d2b8886de5954dbb5ee85c8d212c74e8da355dca69405d8c3e006d509c030fda69ca35bbdc55f8466aa5d91ec91602afc111c2896e9758240df684cad008a7aa75bad2414cbbde2a0b1203fa27eeb7e121e726db0d5116d67a87ba12a90c8405a3fcac5e66e154fe1e4df99f2cfb5ab1356f005310739049f47181d4fd06e1f7987587327d506f10de7931e5c08954d80848b53c4f5986e501d3ab59760ffecd7189f0063699e6dba205d9a90b7a91be2badf2e693b2f77f4314bca2160b1633d4f09375d36925856556cebf3353a477d6085e2f3aecd4381773bbbba70735480e0dbd1ef69760c627e2eb9dd0a772111c4d2c9cab14e53a41d832443d03f3f6c3a5a6ec67c3286b230e406820d245dc4490a9007ae7a92455ea4c80b900305450261262855560526a06bc63cdd5a4f3b0546c5f0038b2c24607acdfc30e416ecd95a60c405dfbc05331d385ae1b53cc06fff212db6b55f57a7e3ea5ca04fd0be79230ba3b22bda44012df65f57183a9718aff784cabacd38184884c300960bd7452e3c278f58784b39812eb2863ecfdba5a5fc00efc1c2c7c4d9c9caf0fd4e45a89681a1b412eb5d4f7a4ed374ce365282cc3856898961fa6427029040e451a45d6508293158dda5d7aef9be79f430e126d35efda48a96c2d5187e67709e84148a8092391f6c6dc16114bc190201db6b0e4eb4ea7f026a123bc5ad7d6007527728328fce97d5699090ac1deee665a9f0bdc73d5fbb2f68db89c35e0120beb46178a1098248a484f2a8bec5d839953069258ea50db97bbcd347f206e1d04c92421b273f6a14cb45804b6cc406cfb16460c96d29d070d1e77a30cff01020b4845641c2cd5a4289f86dba16dacddfc05fb087454199a68d9c4ad448aad9f00ad13aac6955b148ca394237ca8d607a168ab1e9788357f6b8c71abcb5afab009ea2573a4fffdd9c77bdc93847645bf39fc08f02936efdd737c6ef9f9fa697505f42530421a12759843125ab0ce55f491780690a697bef1078afe63a7ae782d02e33b4cabef2472e6e3b32839e4cfa3d6b2b62b7a52b139f257a2e1715925e10c3a310c941fe87d9df0a4da465403b88d10e228cc540578247c7dee57fb598e0f5482a1b00f1f071ed43e5a9b5bcd165a8af1ce8fdbde6dbc2b90d2c8c97ce00da2cb537811b79827ecee21ebeb477c1f0fd0301817f28774ca21c812e6f3140c0b651f08439eb7e5d68fc6aa52fe5c3466103d65f65f2e60a303b004ed8a6f0b3e2bbc4e9c43ce1873a0c6f15898f9b1bd465c06ce483763f862c45a7122c100d37196d463d4a511d3323490961b9aa6a98379b5c4fd2de367229e800c45e201a08778d829f529d38bc23e1af382f8cb8b5b4b2f8f9e2b1b954ce99c4ec2ee07536f04960af4ca938700b84e8bf3663dd80f4e6eb4e5543535e417c0b690330ab3e91f0700390fad60e4168f9a766a54cebd1f254baf1d60c3f1441d247d960fb17413468dfb6258f2d6b8f6c5ae26ab902655e92ca3c795745b5799a0436d0a0a67e9f8a786f608669b7f2f3ada944e9db979512b3f89b64842533545f611094f319e0f8c434de546649b8213b241630335201e350cc9c49374c4127b96dc07a1a86c5fa76dde1863292eb7bf7fa33d9f254c63865afcbb0060c6189553980b247168b2cab577006578ee58b1cc93c53f31a8197de6b908734a84be32a17c09dc585f7a441016186ddc8b4026b89ed255a48c5df17a5257d83c75799b69660e64873f3576357d10656e0f399d334a5ce77dd7633498535ddb91c399050f63082bef1f63d923bf9fe6fe2ec7767aaa8dc1d01c9d396228e2bd318db4c1b4490e91d767ab0f89af08a85ec0b1a5e5f5b664ecc53f7a67bbc9861f4cacbab7b40ab93ce0780b42ade40849e8f51560becb47aaa2c5c97c30f9defea8d4e86d110e62603bc1ae24d41171c1a63af6f18edfa7949af088d12971059156975be9c40e00933b9cf66f8c691d29045070532d0a75d3c155cc6c5c89c811158fe4392500652fee0df43e37ca8565c814d8d6cb0d8efb346a7e6abea2c7f133f2d3413f0c38ada81184e0f15ec57bdd814b10915ac3dae65f57a47ddd875e8d9ea35c80094b89a38907ebd6b5c28a2aa87a64e4994a4799b1ad48206c64c9ed62393d510fbb102a4060b0e25f64956b9b3883b21df88500577b2632f3b8cf53aec2f6ce0f576aebc66d943b71deaac22d455136e024d318f6c6aff081a091ff6dea200406098006c1359b4d8613cb8ccdd87a6da93d7b5ef7031d54ddc8afab77d134260cd7f8292836dd49b8f0d44acbc7ccf829a229577b928adafe8331010e0b90e70017dc056496d6b1caa56929300911f9ca8ed42544fd7a51f48f777573e54e060370f005a3801734c8a3e4cd783f55a6204573a282ddd0c3957d49bc30d7134209374e29e04080020ac2c09f372119903955c916fafecadc51aa07e7b7ec03230f8899eae59702de18275d2a6ca2e9d6e2e0c9102b87ba2bf8c09cc57ed18c43092940725f044a0d73c7dfd04ae5c5c12efa99c47ed0942df7dfee0e811e4a8906015a70b6d9b4f94b6fa3f35468433d1117ee04937575a6cdd1e9be0a11303c0e95dc7ff3f792cef5922a19d35b90228d2c020830ca174a38772648d4ee11f800a8464ce9505e48de3c5653bfbac11fc405c20354d34e745f90738bb5bb200b0c7f82f57f0073813a41187603a213ce9957f7be693f353472582428cf5e7fde06de569a5578ea72513528aba714f00676622847818800c503e370e41b8150be056269f26db3dd87047b8a482963712f15089f2ba6c1aad59b44947fe4e3f25604a9e448fc52f5a50f47c0b00991addfe9c2b1e41ae2eb813065c181a54148ba02695efa2cb55dc8d84d1f574d03d9e0295229e6dcb3b9eed50f130e91065daf047b4c8350a02077a31d59c5cf73cb70b8d65a5efd6b66beea277032a4482ed60f946d7986e1e1793a5cfe16723af882b0e467d2d8d8c68bacae688dc5b5ff2a0378f1bc70212177d569df9b94acd4a0c4ff632ad452871fe2e1712a61426068071c40f15a610b84b55b443ae6f7bdc463e839688724f9ca5764b5822f2b4ded0ec9ae74df3ae9b855d8da7c0af915023f81155bd602c55ccd76643b79ee6ee804f51c0dff6a13b4396a551ddddf56f87a908a356aa2084c241dab30ad1f5cfa0a5932eb1e3f268d29e8b70c2d67d3a100800e602818ed1582378b7494e5226b0d439ef4bf56d18abb171730090fe6a8e2077baca9588eb8b4568c4acdeb09cc0b9ba9da84092772b73a02c10d68904e1df5211e2314787cef870eaa08590c010c28cbf16fdd74e52d987b67b580a3b756c9bfe9f972874814ff00d9123c774c0a1499ad079044c423a4063a746c221b7196e46d0ee3cd727dbbb2b54a98def806ad3b71731873ff59f9322d2aa05f8d3f4bcab6b4274253f3892375941c72c00de58e2c2b80002ba3c8bba8bc240d9b9f2c28a9467406011604037c2609cd830cd4edd516946affbbb55050c4e263654d1d766d4f391f2ca76eafb48cdc3e5f0666f058e007d9c96558e3837991d42d69528900f804780d8a7ccdae4772cfc90bfc13ffe902b0312568990758a5e4526f4a623449b1c51aca4f98dbd21fbd740a665ca4c724bfac62d510bf0fee0daebb042cbe407b7c9d4bb45f82b17a849803090209a422ae6b579dd14e015ad5b4730fad267b0490165398ca0538d638d70988527e1fc8967c07bfb39ea958836c5df6854cad6f478e468ebdccd9c7abdd0cf5b1f9dc95a15158375113c4ead6777b000b47187628f7f7f9e118dc7e031401db41fb600c43750a50d369c502ed069954e1f3eac8379079d2f73846b6464e0f7711c2df43adfa9a4b5e72d39bf1c965963748e489bf0ec0827ea2a43c74530492c34d31c156eda9fe249cf663d391d977f93d07577b5ee9102c9b6718ab0e0c91fb16c6e2be9986fe301f09e163eb13e583bfb7c660f31017d2e5b67337910b18955c54d4368a441cb3832bdcc064804129a587d2e1f5f1aa3fc48c1877e305793fc5a7b6e1b3c46a5b6dd2e1a3cef701e08d06d6620009ff17d9f5f8bd310a3915fc9620a214b44ca4330ee16bb3547d21c9071afb83007181ff63ba16cf00451edb71ed22be3347ebdc53a1f01880c7d3fb4a57df73bcdf213cb557b41a007e1a65e5aa0f627124158d05d41afaa1c6045519b6bdae349a41d06709827507fd2fc6d14a37a8199962cc9ee0ba3cc30739e7a6d468bd48b1d1f72cfd55fe06ef59db9cffd08bd51e635879e6ae1bb66cd53995bd84181e52b3041afb4a170d8882369e2aae3edaf892e6e12cafde26862447a91e518d4b1ae26dd68d5c6a0060af9e33e4e890dea8a4a791ccb27bc575b6661b57f70434e070c9c410757102d62a4222d814429914e376099387c35a613b2b84f5b88bc68a10d00b88cb3805af4eec30920b5361d68fe562109d816f08a3ae2f48292f0c37f72eae0a9c610c91182ce834cb8640e4ad8658fdb0ab0c5607e93eabcfbf6c44997ed09d004f07afe14f05f5b8defabe47244bcc50a0db18ca66db64deb8f6672a5047bb76060ab1d0ac9aa708743f36936433f98c18dbac3545d36f4cfddb0455951ccd0db1090fc8adb82b12308243fadb52961b2068669e12b73d931a34d7f9b1b95d22c00e6243c01d061dd6bb1f9b49b59decabed3fc2fe9c643d61e8b90f839a4949e90f35032aab34dca4c06e4e3832186a8dfe05551a7c85eaecdf4a0388a69efd2204787609978f997649e6938c1572ba589c551d2f63f65e9132053fa5039216280c59210de7fe150f42777ba6453fe65ee80c9082e8079a202d90d2090278dab6032be9732402408d8498f471ede447e4a08bf543500e0850b8946f949450a2230d882ab915e7b45730af26f843949f22f2f42ffb28ee075358c3bac90ea2f9710d99e913986f3bd7302a8c843dbfadd16352df4aea22da55e79379457fce72fb04b2898ea995c9b73d03a152f5c4c41940ac13fb049600ef29445cd812d8196d023f24143e72586d4cfdd1273cc8d73d9f829df19938a81bf5c3b5cb5d1266910b1d0c65d258e7f23e0742a53d470cd859d82ed831c32c946ba7df9e8a956707031e3a5480623301c3f92bb429e5aa8b25b24125c2526b17afab1b74493353c90d3616d12f19a38cb37db8e0bc5b55b89617b85b3343c8d805f3e102b5366bcc04a40c1041eaefc3bb058d049d1d27265f5010428ff77c469fb1540e8c616a380993741a55f853fffe8f63159741fe6472bdefcbffdff49467008c3b45f45fda024a4cdd813b7f9a83328746863f0acb9dfad4f0a79e493166ba0e81f95ea98f040ab85093b2a1f18d58ba5d3388a50a7be32ce5e13b9087b9c1c1cab2be98f6056ac7bc41b7795ab46858ce7250ce69cf567d17acf4e0d885e898a26bc0d8600ed21c79a9642e20db569f02a5e5762ad5fa7dbc1de01cab8c5dad06e0c327e4065ed4063b87d629759d18e5bff5eb23b184e118e06326f0ebc015aa8c082e30032da8a9f908689b28b4d68a2faeca724b8a3fa4a19400064e88120aaa001c780ad640ab4299b7e72f813675a48936ea220c973af013e2418861e2802bf3363405c0290d023b71be7a6164e50bf3b40b048514cf780ceeb1b959d0c5419e017008be397a37e7125e3e0fe28dceef3d0530ecda6853ff8e8c9ae8be5aba50a48f01cf4050224dcf77ce8c641bbd98b61ce2034753619ca96b99ab206474932ff2091ca4b0ee5efb1e722e22efab6392bccc03436434ee6f554ffbef847d841fe701bbb7b4fd8b3f669c2c463f290f492cb406d05651d0d7ad5d5d0c59f21d07510a0ac2f7d8aa9f17868a69aa9c27f0aec811de5126ef073737d1b3d9e202842b009ec25bea5b571ddbcf5ae407ba4b5fbae1b25323b17bc58fc17ea4bb439e6800a661f99fbbc3a4b8f0b5c7df581e59ae1a48fc5bf973548af75da1891d05a800a0703ce3d607a100085174237ce57d0e1a991e7aa58f4364837d053ae1d3b80bde48bef6ed248ef54510ed8063158cabcb1cd1d831e24371e1d8dae3ee25380d068e61dab85ad6e96c412941806ba9f3cac029f3f4877c904636443063c716077073c657eef097f972a81cd7e663dcc05538b6a3dffb5f17c10478603e06b60a2dd44dc881914bd8ee321e3dbef5005724abcbde7bbc013244f32463b1edce02eaf0d30a408b981ed8f58abc6e3e353737e7e9d4ce242e6c3b9caeeea9bbac0efaca348789ac733b1cf0009a18518f7bbd9cdb14a29cf7400387b395e0512f0be046d4e16b3f796f938016a263535c43d973e6c687405b52370627af9a11a304990e02474b54a2d999aeb53a10ae7f0b167ba9aab3064d8f5c89384f71296803cd71aaaa0d408ac449ad47f424fc15b6c63eab1211a1e3949e1a1c575047e604590299ce3e4ccd17484998e3dd92570d9487abd52213eba890b810ce1f16a60763df8d0229fe69d9f44a4e789ea00f84dc23256de7075f989a9dbeac670334072e1ecf44994057baed72f5c5920de737ec3b23b37397a1df52dd22e2c0ddbd045285597c9cd52f090d24fad10769ecfaf5db73322598a765f0383bd0efcd3b07bbb10a48b14fc757eb4b95f72fd4a6f6191a950a3d2bf83aabeca10f7b8fb80a461d1d663d78e579e24123448beaf5dbe636fbc5de1876234e84eada0fb2290af87b7b46f6e5064ac532e94bfa9383a8081623314eb3a47cc7bba298674a740fd8060cdb214b0ae414845662fc04687cd879a6d38d42b47ec048898145e68002a54a79ac4362ce06f7d2277648302a21e34382b7cc2c0e023bc0df5bf8e1dd07f4dde27a71085f9168c403c8f939a304c5f38e527cdcdcf7012eb4d64a7d11078277dfa8c57763271caff630e4bb2879c50f4e6bcd09619624b1372ae8f074044f2504c4d7d74449e37497cd23aa1961685d41e8ae35e8d4768c95749f1f9308fd35da60b895712f72e4fcfb4a0e5d53864513183afcd5c43d4ac9807d56d80da444c730e68ddcf048a78a8f162a268d872e84b053771ea6257a94549efdc000d1dc52f005443989cc609443e4fee4a2afbf420b8c5b0b44bc0da731cdc1d80358a4c1b47c9b4351752d760b45025aa4721b934a407f1ea096f88a6693932304f4514001a93582899773a68e6c63cb844ec4dbbcf2f5492d31ce3daccbc42b0aaa4ad238e08cb446c88839ab5889bd1ad120142393e8c55f04532f3f38060f0c40356f9e0d2769564eba415012d1aa74391dd2c54a45ea3bc995968608a0960568ccac49e52825d585a07c288c628ff1d943f30cb7aa5b652bedd37c4499040f49d83f8b3aa4d4f6c65356212d0b26ed2c6a16a2d786e4be3afadf70bdebd407d615b41c41a80c9d339580a17daae33501fb42b5e3b695f579c2bb3527818305592976625bcdb79d2965fb5cb240c46ba8a40506151fd9bbff4c472ff421c60408df1984e029617398296eb1e9678e7416d0209084d3301e1d45487d51f74b0b316d6bf5afc988d7402bcf14549c740f477799cd6a0dbfc67de22fdf8e9a0207722af14b29f364a2a54e922f98491db250379c73ced139bcd2ce176d7f3c93037e81bdf026f0c71a366b6c903030a72d60dfdc7b442b53e57cd60ab912ba6305becd9dc1e5207b68db934f1c0bb6ef7be553e94c517423214d9811048c97db0f7a58392b53522c938ec81016d4ae7735b6d9de1cdc049b57edb67855ce8e7c0f +generate_ring_signature 590a993f96a147077c4f5cc32f91a7c802e654a5c8586e9598d0b6e4fde6f48c cb9194bc59756cdca58afcda4960aefc51304bc4f6ed04c6c2440488f42b6a40 2 87fde03c5506336f088a68e7bdbaca0f4f0e8a9bc143808305a7047194242664 8b7ef8a586c5feec0dbb2e72d8036764ff5da197557bf179192e31aad1d5beff ec722447735f098024e362a928af39663703309bdb68b3a5e7e42ef7601c720d 0 5d5b228897ca33216bdee5473aa2dd7bec42e0d0068062b978f3648dbc45410bac1eb34b1c5945f29e7ca90cb529d841a0435996db66a416e1411e9979f9770fba66f9735c20627728c2a9129efca42bab42a2bf9edb62f43d3bcda433fe47025fedf02136fdc76223c2c8b054d1289df02a0e2a7b83edc97890d4c4de42cb0c +generate_ring_signature 0f36247a48d15d7ef13a065c7b1a1ee40149510d7297677108020d7b2a36375f b0226919b26683f35354c80532460ae47ac0c70b2f18a7155f9582904058976a 1 24c4f1e4a4d51bab40ff04be8bed03b4337518300c1a0073bd59011b3a0fdbc2 3dba1bb1ca997825b955163be13e92cb7f823c13db9de96570a1b539c4c1f607 0 f716cedbb1d448a313111ea25377169b21ea4b74c6eb331093ced4a26bfe390d5eb9280fdd4bbe33d8194feff077804ef2a1f4483ab0839f7af79d4158938a0a +generate_ring_signature a1831c2903f8c324b1693adecbe806b81454c15fef50af931c62204b3e135bc1 261a8904b68f5185ce1657f266f83cbbb7cda81229e313d568a15cc55cc86521 40 9f6fcf297cc2dee2ae973a51eeaa6e940388bbb75065504f449cca9b25267d00 98e264b0c531599a8a579074b2d927f85e0c8b62c437f6372267a275c4e10169 42549d197b01ea3544e172e32b102414187e707f3bcabb6b175a48e280ffbdad 5cbc67dad405316a18ff8863ff2bc32fbe25e08f8c291e790733e39e83c42947 dfab77a0540c2ae33cc85efc014987cdc19cc435463a61152c70336fbaae0715 3cfd622fd739f3eb972eaac07aebf78208c6558bfad030afe4b6b7af38ac5581 2625cf12b9544cbbb8cfb6c43d176cc0722487328d7ea20a7ea435c7beeaa4d7 aaf3c52ed69792553ce48eb77242d350a8cd10ca9cfef151c4634a284338c15c abfb16ed19fe8aba99218c2734a87c62966e7450f66592a47199615d8f08073f bddb2d703ab7e9b711ae268e900ce8ffc7ea314ba16f69a839c8784a22dc1cde a4a71e5b7225ca67a6ca194f5f650cee3ab4aad608f27e05c2a72d5ab1417399 397f57af3d7105156e93c47c7e75205ce4b199d3d668e701414f62bed9f30a35 aa89fc94e7ab1e9c47e2be820e4b852e2841333375c7181534c1d9f6338b2b22 12574a1031709e503b7b55dca440095b5ae32e7e49960df3f13af0e3545856da 2cd8ad3c4c1479a755671fc233c1ca15da6d1319d58ee7fb46634cf5e6d2c2b3 f72d700961cde8dd195b2bd34b8b33c27cd38ead4ff52cd65f32597d9f845c25 dc50f2aeb860353b9c32c9163d2f371cc9b1966ea4099b783db95615d1b347db 02f9a85d776b326a14e3c116f3e34706f831e81ccfdc4323500d28e525c22fde e46577f6631c36676b6cc24d154902c03d8cdc427b8eaa9a16bdf1600dbacc5c f61fb173a89280c2c853240f345f1f4acba37c73fab295c6153d5b0c84ff5f29 78d202b46b95461b1f0850005668ddc15e38757853e48bbe6f531deeffb3c5b7 b4fbd260b5c4d963f1f1467eaeebdf072c22141d53d5b87d4b8b98a162977021 496e1bc0694a985482d5e4deb35a8f5b048d3f6af8d0d12a8c7e8d1425c16cec 5ac968233f0006c4849db8c0134ff928fc34212a6955d2fbe6c4741f29ddd1a1 4d29eb966788a97031479b226bbc1fa8b8124ecda89c039add1f48095bbbc2ef 3c721246302983cfc7fc5505fe05ba883e54bb4b50e87eebe01a44322e356cba 634e670cd777a9df99da4be3ab467801b4d87a42d93e057fa798e08fb239c91e 7f972510a0eefdc9ceb8ad6bc4051d8743642c8899ad90f81882a972042aa120 431c662db985b69fdb1492a243237c0b6575a137065ffd665be0df48773453cd f0c20daf7e7d96d1c1282bfc919af7cee54795c544bac82adc854040ce5bb142 b7429f8a8bda10eb7e6993550f7e967b69a4ae04bcf1be65bdb252f0bde3f097 ed293f21c6a1269a56ca8335e5c492d520fbde3f70b877f22a6a780eb1109bea 0a6b31f4ca18c8cd4e1b6860664e445c32a314b3cfab312f7485b35701b8d033 ede2afc010140fb9b7e89924c1a3696d1883d3255da5aecb64f64ba3d471a90e 1dba5ccec546590f8a9ea6e91170f0d0c505ddfc6793b150feaf64637ab6dc2e 9398df9e62ad755ce37bf7d03b555316e7a3a54714031fc3630e997e8ca226c5 39cd3807d39ae3de4b43789511593e57e3ca4f44d3ab87de4dc50fe8c79fbf79 678358781b39ff78d276740e2c8c2cc482c5bad694edaa12b194bbe9dba7f78d f965175ebd6af2efde584f6bec3180fe5da01682603e7b9a54a610da393fda68 793abfdb3fffc1c2669b18d68e3239c0662766c5745a0f715fe9af5530fa6d2e 07a99a07a14dcffe0a3dbd5e14332410dbb5a035cce4a0171ed6540eb09b1505 2 981a0bec2238bca76abe80f07d4752f50d3d7f5b9c7c9dae58281445f8aeb30dc84de88c4f138eae11bdf4feb9b4c3296319e774034c9ebadf14e25175a6a709b00b10fed2d75a40293b2777dcabbbcec7d713d4ce2db9a64b929dd65189010544cfe03c8f3f3979b30a3d5ce17815e36f03f6e7b9552f96cf98dab4ed0c830643420b77faa6c7994b9d2635f02afc86f1b6a71516ca863cd28edb4d95de1400347c32bfdb655241af0cab53ba973c0a1a5113f3790f0c4588898feb0a53a5072a3495a39b7a15a425675260595af6deef4bfa8695cda4f42ec444d4d4868b09749f06d7d2acf0f537ded7d32bccc4f17f6c34aede942b8fb9be350abab5ab0a6c328515db6a0cf897f59839d85be6506e0a4197ceb3ceac9c6043e6e9593708b2d2257072c46d0846e46848d28f7c99b0361a76e0fd22ef7642ba95f8dd1804408f511092b4c4085c4f0afe5baa4defa260e724b0dabb09a54ff494c6cb540758044a274a9edafcc314d902c680fa885e308d17f254d07cb95a41961df7be0b5d65b6743c46258091c9842ff6232dcd61317ffd1414bcc425f5f4f61ea4b802d7d10c1eab703e50c3fada009bff2d054524315132b27d660a97aa600ab4710dbc7c1a7f94420ddeca4cc070b45402fa5f267016749fa471f457f87e647058009ef22567f7be3d81bd72f6db6aa065eda0cb7a2fdc46d2d82830c16f13fa8b0a2e213790d8f0e403e93d1e8ae047dcbf51486f647e73da8ff397c72e53c2060cf00dd30028133ffcda0de47bf91672398a56df656a917d32ae07414d19f8b7023eb05c88b60997835f4143f82f750881ac768230af688d267bc200338f4ad903faa91a93d60f5206a6cfbfcd8597419d0a87923fcbb66bbbdc139bc6495a710297514767c4ff3e2fe52dfd0b0570873bcba18c2cc36990253817d388056e79096b1691ce2473d4ac32445449a8e686b62b9a373aea9cc44d5a46c2ff5adb960cc218a24e1a85526a751d7dbf273f28ac238998e253d35c537f665482658d460b6faf6bc51c0012e301af13c403429077009257765df26178c4b1ad69698a7101a77df2fa2682ac01269a0fc4eef569cc277b1d13b4c1b04efb86823b2d8d1e0d58ecca65966c492929667efb801256d8a26996c8193aeaa54e8c4c026b7b990af04889d455f84a4fe96ea86e2a96ea38e44871414b53d126391183ebf9f06509e0709044435b885b1b866047f6cbad74e0fc269064c974a98bc591e4d7272f04a48ad884e29c877d5a478914e7eb5fa1afc14315e2794e9ddd9001d29156ec0dced39d640528f2898daf4c71ee72f96a424cb060688b1a1809d06d41dc76af07f7be406d259f320e56643780865cbee72021d9627d6cd0d034af82a52233eb0ed1a0d0c8fd052a443bc924639d729baf64357455012142f5ef279b72122ca30be5697b1115d24eb30fe7190484ecbf1eb8e313f491947c82a337af83a15a910df11d9bfe4d9c454630a9d834f5f2c96b01e905217a305e2ce48b2531b75a7f0bd8747543eeee305ff059fa87d151b8570ca7cb5f2e4aa8c13bfc77567d9c190cbb4aa17b661ae327db5c069b93c156876fdf2cb3deddce4b1f5788cf5c8c6d0cb93a5ffccb8cea902cf2df27d1802bf8ee44efb0a57eb167f433228c84d9bc008a1540fc4a2aa84b6f62cb958ef83d2258cc091f19228ec36ca7795b0b5ca40efacea614ae0bbb12c8b97da05cc4993372828e745ee902ff8deb05a2c7652a0e851c75fb96f8f70c67e2d165a22c96e50f3d57900833d372f5fa0db94a0d82075b9e9f2be1a03a45409bcb580c138eb322b1e5e2bdb87a908b363fc9f3debc0d5ef894eaf3b5415eb418c627a85a4a04dbaf209aa73218213e6eaffecb144e096d3cbe84cd6c48777a0872ff4f77ec3576037c8a7ba87cf795e263afbee65d06a58726459942930171d204d554e3673f9711763e13b0e05d586e5e144663f10900e52b6de41be11d57a840c602df5b3eac5cbc2dcb63328ef6d58768a6ba370b971265d07ba7969e3ded0dedd059400459b61dc14bdd60968741453eb74dca0decdf36a2037496588c818296d4b3733d5230cb5db82cf5c3c52d32f194696c0888a30efae07630542add79fe1761f41a72e7bf4044d930cf8d94b4650f8cae0e6c80aba538396988092503a7b53e1baa96e3b520ae1295acb358f5bcd855c501f67365790f7b568c85b5fa8b360f9dcef14871a659b5fad199f3f4a265d30004b278e109980eaa8f6caaf471348b3815517722064e8e31db372717c9dd76110fd13883bb23ee54c145ad851177d7eda3aa79bb5d72e85124618044a1945475038805f65c66cbe58d5db81870d62382580345a4e2c0c4c72d9aae34779f29340e5324b085ab8f20c022a1878c568bbee8e36ef0407759ea4471e1927e7eb80e0f68d15657ecf4cdde557117d4274b5e027f6164afe349637a52a437adc8bf630a9f0019817b317c275a55d4095793bd27b6b08eae54357eeecfd28887c8857c0ea717f35f69df80d3db21b68aaf15a4133e76b8d898c12d84464efbc2b3525903dbd971b570c4f9a45aa21a1dbc37ef54411ad261a377f2c450d590c4dd630b0e96e581eacafb7dbf115038ed36015aa1829b8460042b781b4114d84e88b7940dde26decb1d75b6ee4a1db215b4a75503947c7cc98bd04a367b0342b7877d960b2be7ef1facd457e6e1a93d58dd4b96e3c2faf684498284bb2f2cd7d499e1cf080bf975859169e7b4dbb1c20b26b807cb65547facc7dbf3d1f15b50f1436396049418c0b1c4cd74202bad8aa97a74e9d77458ce39c9149fc5f2c0a599438a010ea615b5460e4c4705a6a222657d83080b07e331a5cacd71e1fa96afee87ace3081788db28b2f3f35e75ce4ff4e87fdbb3b3a457abd4fb9497c29663164d6218069e65d4032de080a624053e9ebf7279f5fade0af4fad8750a44d825be50fe6e06dfc73597178491e4fce8e7a70114eab887cfecf620586d519bd383e3975b2107466bbb16301686e958f38a71a3ce774b4fe1498afd0816e4c23c2a638e308500463ce694f3ba203669a149282b09f2e5f1289eff160e598953f6f8ab9f7dc30e147a1e18ba1418f379f59705610ad814a507878cbd31ef0ad99cbbafe3c83800ac952d05194e52410678e3b5a5fba1edabc351afedf1e3a1eaad92ad6f4eef0eaf7cad976cf571b9a176c27cdc0e461c3a2afdcad5f7140761037bada6fe1208a53c485241f9f3118e0f8f6d93128e4965a1fddb2a08fde668b321dcb889dc0aafad0dff23e303493e6b7e62f320fb87d211282828fb3875832cde7606e3810b955c88cbf796ad42a8273faafe8d89f9fb4f8c12c900802d303b9376b82513078a63de112d845f7eb3d832de837bf9be5ff110d8eb80fb4b01a03b2d3d82b20c9686aea679d687f024698f60146629c43232af3d8b0e674fcc3800b60083240983f128bc61116b00274a8cf1eca764a54e73d7ea64459ffabf94b810c9eef80da761dd781ab8bd551cadc6de9b855fa4897dc790e15d04defb4609397d5e3401424b60672ad99f626b66abf6db7116f79d979099f4160ed85fb53e6b8d659d09 +generate_ring_signature b9ab0b10afa5860d279a36ca738754958805e5bd3290604096442b8a4dcf9730 425b3eda70237941990ea68b9889230fb9badbbccf08a74b7c4a59ce552adb78 1 a15898bb31142d00cea651b265aa4ee7e73189b383c616eb0f6e37ed23ef7eff 84c6db89df73d478f2725b0ce36218d81fa68d1cd5ceaa8a1f8f2f10892c3005 0 7720960bc7faa3faeca50b02ee312fe1e40a8307102c4aaf9491bdd45661340f2bfe4af9a7eb7a887270c6cd900939e9043d81e62ef898ae59eb0c223fe5e502 +generate_ring_signature 77f35661d7757d7d03c765f11350f4df7bd28708a37163e7520bd8c8fc622503 2b129b323db1ce4fdb915ebf2ddd4be3ac0798b4f9fc3e7188846d9993764fd8 10 ab241334d6e35fd6340511d9c72d5a18159c125336c76cc06eeda30ab23ca126 80832676389cb95756b34c8df992e88936b79d89114dfcc8da1e6eccf6320239 c5316c4465366ab16e1922a835d9a8b2f2ac89aa43e2f6c254c703b7cdc88519 74ea52737c3db895c2b38faaa7d1b47496998d4defde5cc72d070a15d622908d b1d7742def18ea0edf1a0bb3931b3777d85902d969b9a2a4ac30d997773de9b5 8f6ed97932d63ac5c74dcd60385536f4d3905e5697e6c27ee1fbe15edb0bf7e8 be4a83371f1d524c26abdfa898e92049d4f3471acbd5fe9b03cdce08d7fb3f4d 3d1d9750e65c8d279fde02c4c08a86c781880d3747b0d1a9ee063bd978c0921a e9d2250439b623b02e4915ba6f3eceb0a74bb7af6bd8ca70ba49e477f69f5cbe 09aef8291d5f369067a350126fb37112cd0550cf6534ce110100066ba0a8a7e6 7d6228970998328079db9ffb11b5340bd972a39cb5fd090f400fc2f4d9261008 4 2b91bfd3691f1adbf3901d7dec727d1cf4e1542440695bdecb589c8b2c75ce047280c0a085ba396c34b30d16d4b3af5c413b333a1842fa0a0678984168c0d40f28c4fb8361b59e9b616871e82c280b7c17addd59487987ec023303e2a962e1061059a97043e3914ad1fe31423d9d0a30b052eb9672332128c8ee5e74ee2b990b8ba0de941f522131225e11739a26ba0a494831e19fbf2e6289d21c3815202c029c99b2fc9a7a492066d722b43e2e3fffa9b8c711d0b9368d2ba19b87226385042f8869a00538995b5d028981c0faee06cdca8b4c6308a1d56efa7fd772ed4807f28ed6bf6fe2d61056055b7587ddfdf37bd31ede1d31e78b3a1dae0b415b6a0ad0098b7050a54d397fa3058698d94f6d5e342ffe8cf3abca59384012a848a40f947a0f338911acf1b4cfd8ed0ad2a73ff67163456d8abb81c851c8c449b76e00718e075a0fecb603a3da413d9da00020e8db194408447d173503a67655dddf03f8f87cef2d55d4518cf4be3d6fbb224d08c74750f5c6ceafe7eb44e4e70c34012a7d1ffb9f929562c234c39f5300a41e9fc5abc95455cb8c14e1238ccb107b0a720e9be6307b844e9e8226b9a5a6de44c87faf7f1bb1279e97c8a63e4124f903e1b811655f719835dab14596612590344f854fff143f27e57bbc588f945fe30f6890abfd567cb38ebbdc01da0d393ada5cbb98f753374f33792843758298d001268973b9c4d71679d7969d4453e7e2f35b824ae86a0f94c7f6663f7052b8e40ba36495e9a3b5d5ac05ce1eaf11b766052b13b03bfa7faadfb3b0714e652ca005da1aaff1d9ab4ab18e213980e015ba6fb176e4fb281ba0645e229b4b30897003e47d5da289311db7677a754a1b6672087b54adc902b537c6366ef7b06fbcab02 +generate_ring_signature fe0e906842bbf5445baa4ecabe98dd6ef27226ecc2b5ad9834e08c046d0f578a 0e4320a4732e89ca8a53cea98b44492a77168316f9e8ce95fbdb5197e9d14d62 30 b7258c82c9b3e0adfadc33a81ca09cd307e70ba065c16144f46f552414a9a629 54d94e93bfa71f1ea7c1d64902bd8df7602d7687e43b983ec3ff6ab88b4765f4 637d164f669b1111c41a5533ec52a4b05a515ee5f46f74ab8e50cc481157edc7 66fe4748dc0ea965eee04b152c0dcbe2a5fea11333951d568491103296730f02 acb9c2c966da9415c9b09bdce3492e7962d10ac1c2a5e46938a6651e09fb5516 c2d7dfd2f498249427aa0eea3b192f4802a3d212f7fda58c6961e231ec0ddad8 c830fcb8aa75343dd3722955964dcdb9864c7922b872c1a209f477e923944379 bb23e3cba803bb4750739171050a55f31d479ec0ba27f4c0fd5e8a207257fd29 a6afa3958567c006ac5a61fe4d40b5e34aa0f796aea13c2ed3866a1f12fb3e0a ad6765229bf2c6c88971c254e49acbe6cfe0310049673e1cdf346cf09da3f56d 0edae6331033f401c42e320be461975a86552f196f495b3a4e3b0f547617e963 a920e55b3c7f3a4a8653d75ed1cc731e31ac880e175959f6e267fcfcecd22955 b4d509383bdcfd434c407c5070f125c46a46733da20a3f0fe9979df94c31d5bc 25daaf9f93a6284f702e02395babcc7598031c6940e7914a93a8e9ec722e7fc1 2f1c7e264b1262b445f4178156119b3fecc1f458f7044cc4de63304e14c97587 a487d33950e0d251e92691a34a583e13aee472b4c841843ba5fec5b3c7656ccd 48971038b01b0fbb7ed269349420385f1231bca51ce21913572ddb33162c9619 f222a85417740af8ea34ac66322a4f494ca309d7722ffca6713400e4be4fef81 696f40d88004d1a539495a61f35483afbde4fb225483f1bbf7c2f82c7e59dfde 9a3b1cf51901567f6e504c0c8b08a503b8cd633d6f542c9020cc4d238e6c00f5 3fc2d5cd18e5788a96f016cbff76554765afc0428cb260219b81dcead3edc978 ab62d587fb8948401a9a3cf874a024457791d3f2532803e39e00b7e522cac759 3dedef9bd6efc320fa1bc5dbf0b0313244efaec9a7fedc18667bed47a1e97af7 b0342dc39ce8e3f1b5053747f6fd3a450319d7568779dbad042e76b31dc816f3 8b3365279c15fbb8c7d2758fd0de17503c48d87aa9802593c22ad59a27d845d4 4566ca51354a7da4bcaf7f457bf803c6e32ee109a939a69c342ad61534831c44 da8e0ff478e7c6b7fe83467d5a5cf90fc769db0d8230eeab2e79424c262229a3 2cea0bf3ddc9e0ad18757bd6efc37a6e5923dbbb76d22a06463f25177223f3cd ebdecc636abbbd1fa2b166bbfd742ade21ad6a22dfc49fd667cc807b8f51db2e 5293d8afca91f70430a90bb0d05bcc4e6062b20578ff2cae721c767b6f929fd8 33c6805f3997d25bc420affd33636816459bc9b8cbc2f2c65fe2ebca22879b0d 8 f50d9ef2cc95db03f5a46375d92cfb01fa364f6364a1c2305b2d4197ae5175074379c17d0e2b0c624487044896ac762afd7293d4b6dc02ac78d41e23dfd192056288522512eca4c1b7390e15592159b1781ec042b136a5f62b674222f52e2f02b0d151e275b3e4560a7eef14bfbdac4728e4e66f85ffe9f88dc765212413910e262d430180bfced320cd67d7a3458eb53f6d289bec1b6b2e19c70de043a8f702013cb6165d9f858ac9ecc2b78d6886e6323a44e5d3f16766fb9a88e098584c0b53d3594d4ae3cf974f16f40563d27bc35b31f343dd2dc5437357197b4e4456015d309d774d032f219f205ddb140949432420a4936cd0956fe9b94aac518b180e6019acfa875a441fa41ee165a6dcaa0189c314ea519ad916d5579387a9e22a07e62fe791688f540c2b41646ee634529803da6e1879fd4832c2f8d6d02ead0c007dd46f925a8dca5f3326f100cd72a41d1bc250d008a9a443b8ba968e59ce440be6217b354b3f00dc97848bbe5d60a03c42127056f2a7993e58185bc75d755d05b9e9301d0a8e7dc7fcf030e5784a06da6ad16afd03eab8286ccd73e299a61f0fa37bb6584934cb6fe79202892743083dfe59bc1a739a33ff9f3703cecc690e05f0841f210613d5c62393d39fc76f57a581202f5b57aa5e48fc20c5b85061f3091f4e75699944dd669e8a465766afe1a8c3dd40509ca18bb9149c4f9d48572a02841da72c95cdfb0348d701212d73d02b34cceee1c0ffda07178c68c158493b008871bb0ad74a0d6d39d58c895402b6d2534cfad0fb217b39a1ee3560a0f2910e58950c202879c830b131c3c7b74e3a5dbde2989cd9c980150c7bfc4da920660f3695f1c8852ca4e8b1f8be6e1fca07ad9e06a88683a1d65f23fad09ea52f6c00b1696494519fdedaffbc464ccecede3f32eb1d4aa3d2be87766b678b46c1430dd0a178d47bdbdfdbbc9cb5b84864f4294b1a8ed13366dde262c771cd222c6c05916c1524299d1585e8747f1fcac2e87f52743291c3cdc4fe1204c59eaef1090887d3882bc8b7c49fbb388e505e03dc48527da85ebd8da0f293cc28f32ab2cd049d4d2586859a5a44ed576907ab092fcaa3b3cfd3f0eec54183f699edbafc420619baa20598b2bf4d4381ddd69b65f24fd73a1ac075c9be73995e69a08b35fb0808fba65c0e09c1c678eb8dac6aeba3008dd83e2031a3a22102b85c56b0c98a0cbea48333548a44cbdacb81af646c4692d3a2fafaecf1d557ae38d44c1e5f4106afe4715b5f6e5ad018cf2cfac81b996c0641a94101cc2bce4b2cee501bb3fd0bf908ad106a6550cc254f4a62691202555a3835f2f0708db355d5229a9b2033098756a5114a3c9770306fd51b710b14e1c0b695502673417dc87ca34c7f74d8057e39b2e738f74812729a4a5f0fc6b1e17a40bb20cd1034647eba953139267e0c0a9ac35f04fe82dc4b770dfb1e20b594688ab9e837856249bbc094b67ac2220b2dee4b4ee3e28fef5743704e3bd11d96d9eb529b402d7e98d149959ea386f800c5828fdd058cae6f2b405a1eda3a08338c129ceeffd3114ea4b64c10eff59901bc593718659361be70036e376b9056fad105dcc21f338af9a5099a3267b6a707c905f794e8d5182505868ddd36061fdb019451c2bff2a3160c095efeeb5d5903fa4217e5a11f414b678737b3844ebc995a6e58715b9f44db071ac3e041766f0ddc051e01a95bf5090b0c6a81a5c30ee2230bd6c946ae9cc01cc5b4d4e494be0ca5be00c329853f08d4e520acfd6cb627b307dc20600eca8a0d916d5cab538b0c851b7de41f13a230d6b174cbe94f6f45481d2f9a40171d6eb51a1c0603a6fe02d7a3d24f008b91c428bdbe4398a2aeb93997ac4cb0d8ecd3d6608f194894990892fc48cee0729b6c31813e360b7bdbba134ea93ffb24fe41b075ae8687ce400ed2524d8623d36e8312998eb41dba8ca73cf605f3df6be3bc0ceccd673dd57b0c4fb9efcc44e7d099aeafa41c03905fe212c26a90d0877d64ab9cc0790871f602cca960f5852539df513f152de3ffd5827978dc351288c5e0138b54e697ad0d03a5cfebb4d71cfa6d49dc358f4309a3d47d19a8d0d38908795a470350604eb20c57ab4d0b9896945f4a88dd69bf135aee3ccd44c777dac24379adc149f3915a0a12bb4c02b908c5e932397d6030aca3df38937a37d8f4883081662b762707e000693c2709362f1134d960efd0c121be47b599a3117244b207ca673bf6f777be0b8cd811a6b50498ca92a92a19d141a8ed0e5343fb87c41e99b3444d103c679d0aade2b9501ce663decacd5ae8478999631bb00803da5a40e81c8ec7ad896b4d0b90924d4b1987a19ca15bb9c5f5b10b79f3260d6d64cc7c928ad759098d69550c3ac2df95fc5659a6a61e1ee20cb54f9fb72ec19f85bf8a056e2b0732b42db702ef76cdf48529074860baf484958e088636eed5c2b6dd0abcc0e506c778dd710a4026f766ab1c8081b98ace78f5e53a147869c761c5bf759fbf820f40b7f1d40663219feec371b348319f4d674a50be827987373d8563f4c9e36d35cfef442c05caf61e094ee2c07729ebbec7a2b95e38a13018e1be9b22fde45acdabbbda55026e7fc9ee1cf27b424364e5411aff1f6ce1b0bcd404145498fc7acaabd15ad30e2e4fb61d4acb3a6ee0ffaf1939a0be06677978fa7aba04d973e4687d7ac2fa02 +generate_ring_signature d44366265cc5e6e8a13f3d2f55c79baae89befbc5ac99d063e6e0dec1d5f8665 345925032111e3150664a24dc32937232ef1bb457d164c57c24ac1160fb7d6bf 1 0875f733705d14f8dc398a529ca0bb633914572d8849d084dde057b82a946fa3 f51168e99b11a987b49c956969326d5ab4bdcaa1313fa7ad146b24932208d607 0 9f27213883bc1408571580309cefff9488ab66e41b104b9f92f8d16ffef2f60241d5a17beec564185514ed16a3dc1112664ba3099abd7627573640c494c2620d +generate_ring_signature 9b43013f26543ae86b8194bef3e6999d7a787ac7d9e69375e117de5f6a963a83 ff20e8a898dfc4e8c3ee2864418a757fc9c52a5a6941bab0c745d23846a4a0f6 5 551ababef74f99377fd5d68b2ad3a2bfe14ec6b4afee4c39686508184f87cc8e 25d84441eef268e987eb34f24749b4a13044e341e6399a5cd6b155a87e6ff0b6 c3850ee99faa03c06b6574ec52d2916e32b399aa82286fabf4936b39a916e431 42dd11fe00b24c64342341eeb6643d229ad1673a51f09192b3f9ab4b93256cbc af4d559997026bb67abd8ee6e469dd35577dd529ced3a657726d0c02edc3d5d6 b34b5b99dbb26140e778ba6fb61449721e4c7c8c1bda6e70eddbb6c0672e9e0a 3 82d1143abb5c3706c629c1b9739f7814366ec1fd406bbabfa25e52a30ca9fe06c3358826e931d82e30e12d2eaba19bc5d82c53cce0ca3548793ab16128a8650863df1ed605e009122eb2ca75c962bb5c4108961ce12ee957ecc212bba620ab046f96502dfc1693325ed41f38331c83ffe959cad53734f5c20bb9a9953bc7c6092c6ec30294c4caa14aa1a41e0b4d7d68c41f0cddfb2350c07a78908af8edab050b6f2c46859002f874ba8f8aad578ff72b472129b4bbedad565ec4294961a60ff4db8294a9a024906e9996e0598740fe23ab1307c9ae6169caedeb85190d9c00b3d76199180674e395573e4835ff2d45bf490df20207b5fe9579454c07e6ef068cb9767c904744b20cf80a8af5fd60de771c4df2dea19d648289300e2d87d907fadcc3a4f482720b1e0b7b21e4e392dc8032719e12bfaf19aea01bdbc1f7d006 +generate_ring_signature 3c53431eba5c16dbdd1fb2f98d6388541b7b85cd5dceac9238d6aa7dd5e8ea8d e9c7b96e13b6225b03c9c774e90d2e3f71b7be553dfbd22cf1767888c4f83b45 16 70b95fdf50d68749f55586737433d11e0506886dfcfe08a82b5b01c173dea87f 9a8bf41dbb914dc402d80b6f1510e0318fb908181d765b578776b034d6359ceb 87a438d3d769aef03c070d3853f17c4e470a4a7ec5e4a118a8f00bb0966490e3 0ff0242f7714946d5b872f9fe6873d237f1f4fe501b88119795f64f744e8428b be4c9bd9231f25e1743419a2858c014ced12708662761fb36083aa29710d1120 94b62bad7ce972fc7ee236c909cf19203dcd46fa48552d86f85a2dd6379e4e6f af00070c55a10d7ea2ddd2953e593fd5cfb29eab6c6b676472d029152120fd95 e839d97bfcee72acce077bbf20856232fc8e31d27fd71da70a6f0600f85dc9a6 f35bd0d64ca12db2c9548255626727e57b4eaf37b42caeb5c4160e5053b54903 7b23cd36d3ef9cb17d8afa4346fa949be91492241c2eec7d02329f4ef37be3cc 7f5580e1ca9c5ad57941951ed0de16dce9a031761dc476b0a3f4733bfa76eb75 6bc5897d154e23c56cb1216534610d1d75241d7462aa11b8a80d6191dfed1354 99884db56a229765f038e573fb018af4e2c4e75e9750908a6dfcab2d4ec7a821 656458506acc81d90dfe82decfbfbb6b5cc2462b6e18ec798b486add559ae372 0d165d983a91a7228b44dd25f84ef8246fca9530a6f7e15924c102d4f4b9c038 83169f90ec014494f2e8dda969dc1dd5fb60f47e3a4386945b787928e56817d4 580db78b04c538d490ba0e0cabd646e357f6bbbf3752f0ea350165b3fae41a0f 15 e1cc33ce14cde0d3823504d6326fee43b0fc44ae7e7fd38fa6a38b60fceec3016bf6b5044296316914ab57bec34f848557a611a8dd8025cce87468b7690a5609d2e9b824c501556fb498abcd38cc1c9afd68b0bf1c01e11d380c985f45ac9408a5e9466e9ef02cb335052f2b1073215b1a16f8ccbc46aa01ec42b1defad4d800a22582ba59e41451bc28f3a8632fe3761926fd2d82dd56374616dae1a8cc120e8efde44aa4f379e92dd694586e52004daf0f35c10fb41ae10751adca30c61d0a8ab0382ce938ee6e457925abf489d4291bd43c4314c6cf30ddbc5a16a2e75b0713d49666c219e12e0c52d1bee6e74f0723eeb16e8d17f95d91cdace8ee6cc0011bae00a02c129f3815b84a9742b80c1940cb322d58e639f70d36efbfcd23470399d0c661f39021ca96be4d3053f566ccd11451749bd77ef5654d53c96177180a5b62159b0b111e8bdc29bb5cf3a866f70636d341de3a2b15562bf8139cb9c301c937cd6e0a5b29c471c9c22ee2395170eb4946f45f6c7bcf03f0e69ccbc5280b4d632f1b7e8aeb92e6dcc6f4c59adc99c7711531daa387e6a5197b11b35b2f0d9a1abbe7a2be58f15d3648de884a855e8be284da30078167e4f41ecf561943048881ce4157372bf7b143941be18ceca6dec78cef133545d1b458f04365cc57004083642de348abb049f2e0c7ae8ae5edeac583574aee572c9692e2e089fd9501622cf6346acb8bc59aed648ebcc19248f0399a286ba21cff0232808d39ded00bccc01d8cfea2777629ab002462bb54712cf0dbd7f7d5679676104a77c7b2890af00796c798a78a3a4e9e1ff9e3433231c02efe3056304f8b02ef93b85873110c66ffab4ff083839e7f5b7d2daf7db0a9c35c7f0aefa3b6b230a5f5aab4a6a0036005746dd22e1a4134075d1d9ee24c72aa7c9843c1c9562611644a9aea236303b1191478faf4e4f7a99c3741cd294c1d9e61d18fc9ffb16853640f402d3884016668b76f1d0ea844212f43b649c016a4e30a33b471da161cfeffab4ab2a59b0f9e66692763f3797b64037adb5a57396ed94a3f5373a0c5b782f1eea94006d10ceaf552c3c68b7068b2bd223abca4977ec6a6ee180ed881c9c09c0114b9ad820b66042feeb500eee8b7f5b57205b5e4be05d2e2ac9973b74e733df1cf3aace509f3c752e828f3af4d9d94e054540c5bb96887ac025a4802285e72733b64d4120e1c23aa5f9cd78a3d6165ec6b463c2f9fc67f3887f1394d7a8ff7b0a779483d0f01024c99f84dc88a3a5694f222edf9ca1701dffc16b88332a3db56e06a90410c602b51c512f29d16726f4c0dbda42b69bb909eb85c8d3680cad54d6d322d4304330144385bf839c27df0e764be7612a70c3c6412d8bdca8a2656e065bc33be0a7b4f335634ea361abc94585c567977693bad5d0cfb533f409a61852103549805 +generate_ring_signature a2848f3b36302284378d0f599c7fc0b7911a94281e33d4eb4d47f7554971f7c4 dbfbd2c763cc046137ae025d4fb22914e8e7182f3cc727d2177e5670ae95aaae 2 184816f5034af3d627d6d835e5c2913953f985f597b11370c779ca34048fbce2 85172db3a5e5418276b0b57bab2962b26cb6bb159f3608bd31d5eb6fd507b799 d598ef43cbb2381ebc6a1585786dc1afd6f3b2fab6ec7c7c020b934bd44f8405 1 04f45468810eb03f0305054b9c771748595686d9ca26af4562ba93490f993608eded0bcb2d17354bf8b08a62bd8313b3dc1d6befe8ba9931bdc337520a36160c7ab8797a0ff358e5550c9f90399a86f841bbe679abf9cac8a802c46d1ae7480d738d6680d82e7e5a3f0556ebf2dd8244351501a80556b7f966a872df557d5408 +generate_ring_signature 3bfa708865ea752237a4797383b6ae2d4d3c41482ab53d42ac8feba9f80b62a2 2e4d774207848de566e97eea6262bb6ea87dddf04f5dcf911ce9914e9c97f528 91 38f7acdbe90c284805550b51f1e9929022e7d533bfe75bea0dc33f717a523e4b 817262805accdb4febe4c20a8037f50aa1fa7a00592836fcd3d213fcaf022f45 13c55f83cf89d5272dd1d638e2b3048653167468c11a8bebd335cdd72e7158fc 71d23b3dd7bcc81b2fe24b32e9d3e57d638c21ebd4039a73c15bb2d6455f3b8a 2d30c73750bf6f92c3bb59771cdddc7d0d6e0dee4d4ef60b443224b418b5689a 5c0eea3463693e350aea35b9b75c918ab6782971cd4aa830180fff60f6084ea4 72d4ba8604da472325d32ddee00e21d844275c3896911c26f94038f30a59b305 2d09540ad3704fb6791fe07161f12cf7c634b173b4348dc2fa2b2d91b5716d6e 6cdd2f19ce8037aa1d0a1b933e2c619c65947fc732acd11f115c62d40e77ea41 db94bbbe5548ba971a53006889f253361d986258b9a641737b88d6d1c1a83aba c23eff960fa6ce574cfe1f74248c29b10843be110f8321641c2147baeccdda1a 47b9f3ad5306af76c7045c93b914dd0a90370ff9e3888129421ed8b78d5b6725 b749de5034ccf7c0c1bcc7a76fc3b71a90b395f8ce74189101c8b78dd08f176a 9c5c2afa18b46a710f5c2bc387d61eec6eba8c59a00781486a5504fb35b8e46d ea7a0f8cc1e7896b4de7ad84a8f2223c1f4cc320e466ddf80112b01e9f7383e4 8408d1626f6123a3c60271334ab9ce1568f4361c9e8961a9813b2f82947dd0bd 5ffb6207a836af8db9df4bde8a1fe896f864adb6d4a1699cebdd134250d3627b c719c4908860d4a4204a100951cd6f6a5f523fb1c25af8f4e1a7d91566042583 1588e45a7cd74dbe403112737070ea504804a951b592d86308aadca05e19bf97 2bc329ac65a06e56aa65e146f5ca37b15c707a1a856681f1b6438b876a257a62 6cb6add63061cb03876eee0de9733db3cfd194b529b92638d74ad1e3197ca55e 869f1663036ae025ed0696a551fd6153fd0eab16437e4f5ebb740975530cb4ea 565c6df71b17757170cfd147fc7ef7ef5442d60f420779de08537e4854242034 ee1361f2ed36794b34210f0084ed1e853d79403287ac7e422a9e2acef5d43171 396a2402d29083eb8514421bfb8b841f699d607565131c44aa72680c47ae6cd4 bf3e71778ff37c494d0d588e4c240a7a1fa28dec561e5fc95ce52d0795d30b0f 0b1c1afd2a06b982d99df49358e06aba690af29ddc2f1c6d5003e6b44b670eb9 0967624c84307d50ff5823e51dde55c2a0f4dd3e54c478bebfc74567bd7a1b27 12a0cf3cca9791f9d9188e21f8d5c4c5632ea737e4a548a29c6116009b1f583a c26300d73a72480a4553d325bdacc91f0c3b10b7dd53d490cb3d9a69ceab15da f91ce4b28de23f339c6ffdd175ece69c2718e2a3d9b22a80961f6aebd990f09b a5b04ed97ddd4a071ad49063c159c6e1e5bd53be621c2103efa9944a504c430f dd1ce2e7ad25135322058928f7e0176d77097063eb384130b19e30357b4d1a31 2987d815b4fa2b7214a0eca3d95b3dc8bb79ba433ac66aad97f3e5ecf8a67575 dec31d9c26307ed50d5ad50c5698b88d561b3732303d0ab09f2ceb106b2f0af5 7ca0b73fd6dc919d8380b22294516103ac26831cacfb46e0907223290d5ecab8 aa97e38b8e804aecb8e2c1da77776e3d0582341b2d39be6a45f8c513c99cd5f7 cb32604020edae3f1ac43f49a50364a09e9277ad9481f1632c7e446ff24d376c dc66cd2e4cbdd497703f1bd0953033cfb6550f46412250c4b62a8e44f20ff7e5 d713e361ad8a2b8715bc3a8bd0a64cdf4fe19d6f4bf2c20970572a5aed81c14a 1952d47e81a3e2a02d59f47090da2972b89d8952d498bd95d24256efbcf56fa8 33ffff80dbb6a235d0d6c0cc52dc0dcacb4a692cf09cce398423eb8742080853 1e328b3b594f3748288881bf6df6c2400fbad42e1dc53099f98945d0776357e6 e45396f2fc37f54c9ca3af36970ca0ddf908dbec065b232678d5fdf9cdc115cc 369cf4a82d27c83b295dce181970028fe53d6265380cf87c01819e36d1563ce4 7a68d3887599907fc20337d5f7b7fc4cb4f88dfc6759a0a35bf7a2a077659f20 0e579c35139219430eb86bc0da2d37f269893ffb6d10834a6d04f0a7f283dab4 52101d8a151e0a535bd2f5fe4db6d1826c47e6ccbd8a06d1ea8e05537fbf421d 3cc96ec853bf5d99eb69530adecd6aafbe9a95ba14ad506d33772c9de1cfe1f4 0ebb479ce8e81559493318897f9829cbf14499fb639f99c5a6432900b955befb da8e0e7f63a5f460d64b71a38263100afca62d6ddfeea1ac0db0a657f1296af3 119a595ecfa1514529f07f7a90cd78d4b7a98544fc4ed0141ce1c7fe2a9acee5 d9faf2641c4726c711f0d4136ac280e2972425b5ae7a3c27bf5b1171b378909a e807cdc919a38d3c286abd67ae7f0dd3376ac8e9ac55716b52b5c44fca5cb5c1 59c7d6c794047bcc905097d7fd8569a5e4888aa751065b66de32d8d00890bc79 39a5852394a61ea8ef293b244f4a2e3251939dd6dc383940e4b0e5f49b963c10 7fec3deb4d91e02f59d75bd1e7ac2e92b5f8030d696bf88463288e7a12bd4ede 9b1364077d41d4c6fc13b5e4a77e52c27b2fd1dc7765c1696ebde46a07c4abb0 2f6956d8fa81990810a2ed1f01e04b4484c0fd3a76cd84ddd2a9619e332e1d88 5e135779ff608a35cf5da0e30656090fe76c2cac427604b732fdc826b7265198 8c27f69dc560a8e8a68310264d9e65e7ac88beb237a9abbb7708266844057ce0 efcd93dca818910bceecf477ea4334af40922e56c9f4db357cdfc3321abb0f68 fdba4d25f7845c2858ca4e6c1b077a2b7f0f54bfba930670c255f254c277ac2e 04a7e77ee772a997a8954b709cfe1f660576adccc0bc828042a6bf9978594618 917d17c135e20aea7caea1dd22cc6752e4fd5ce05098d2836a146c793eac61a8 b45cb5ba80a17e6c0646adaf9cbea1f7dc5f3799a826c9f088722a5cd527b82d 29a200178549a18673e0eb92e8bd384dfaa695e130c54dab6cc44e826e7c3112 c8303040ae302f47eb64d676166d16b6e38b8996a88e49a0cf1a679da3dc40de d0dd2eb2093b1bb607c5b53a9df148a7af9ab8eaf50e56a9a19c4366a4b873a2 54107acc6285e280bebd6c5314d9ada8311dce901bc812d700a3e938fa628ee9 5b6748e854d70e02a91b3642e600fd32898279ece0f10729127de0bafc7db0ed ecd46f5a0d0719d60a71739ea93af27400770994db68ebe7fa98f9cbb97f5e86 9a5cdd5c941a30e5840598daf4e0e4dc646775ed5080ddc03bc88e74e57dd7b3 b76f4e14bc66012de09bd3fb98724759e0fb3cfe68b17ee2f236ca35fcbc058a fb0f82776ca7899d5cd705d784cb0a30d9a705fb5817497d66c048709392fd8c 6ced641a3d4c402a7bdf2de0d000f9512d6245cd946971429ce8abbf13ff56a8 fef493c6555d43b0f2303815f086b7de4c13eca9fad0bc7cdb75a118bd433392 3ab468a92ec76d5acd3837aa83b7ca933984be9c167dda2a43e86f5b5a70cd01 155bd8eb9c7efba4b2e45083e6e70539f68b0cc02a23aa1ef40d94424b0cf257 e51320a48a75b74d223cbda29a41403195300d6da467c8d636bc909bba0d36b8 3fffc66702b2144fdf6fcc3be9545f7f5ee034e399e4db1c44f7d7960cf0580b cee707102ff886386a1afc90ac11eb95ff796df190068216737f85da4a99a85d 27a33317c49c01e292d23a085ebc2af814f406665c43f9f8e064234bb9022a08 de53a7f01a8b11731298523138bc022a5def631ada76f4c513a71e5398674225 585c96a449f6712c701e231b526202e28a7e7878d8c09c79f103f2c734b106ac 55dc8746379b0c79871bb5c3fdcd4d07f16567e4ae20fb7ec2e794d27785facb e9a7d69695afde442c58da9339bb8d00fe3a90a74f813f00880f28784797a309 6ba12954a1b735fd842fd5534d28304d9819625ac0425b128850b5369e2c88f4 d41537e1b86925fd180c1dc27f7c6491813f3344d4b9e7bf26062f3c93a4ca24 d04d80299b1f40f97eef90c85799b317154baea13dee98f721fba3d6736e99ff 9629084c2f4e36d15a963af5ae25a8ae70a20d6d4029b19f313cecae8c810163 5eb7b30d7c80f24c1cd46f488db56a4b930cf1c595878682025f0315d1595401 43 fcdb87dd42aee80293b1e89e3182ce4e1d5f1b7c5cf5cbfdbb4f4bc7af8a900ca562c974e870a3c2a05e8d15316c3f83edb7b9043e2503373c12765363bedf01c01f138f6700c9021da1d362d0dd000cc7514e78a0146bdfed847c8f64edbd01280a10add0f573bc2278154db5078ef688cd91df0eb1bc82bc6616c7207574012fd09b489ac09ad2544e7aa922328f67e1e2b0bfade3e03cf09656d7658a0402f14c10de89893b007b15c52e8bea8ea89c8e15c47067eee237079a08c0adba05391a806b5e329dfacba975e6c6f0c639845c72ec2a8f11872a5ac3007f09980cc730aa193cd66ef63bf52f9bad55d68366cb07421f2403d71db514556e2fe900af988b561771ad0c71af192b18969de454111ce873f62145370aa9bb7dfd7709f6591f4843a2539a91b0cece37f5185c8b3718fc49b076f05bccdcb3d475d807e602ab60a086453f204622a9ac975c263e048b3f3210dff5908aa3dfd46d770f3a2780ddf4ef203225bddd898024b05123aae2779887fcf4289c53b35fcf3701a625ee97863203f076147f221e7184273a30e99929aaf28d2ca6ba90b6f3330dfaba4fcae3072b388cf57b32cca39b06d634dcbf24f14971d954f96472081908f7be2c7122e71c77c39c89af70b7f93ee6fd833be132d8621fe4d92f79a6bd000aa9262c492cea68922ceccbcaa0829e62a61ecf1a83d95cac3e08f4dbc7a209f295530b04cdbb0eb8448c3679b0e0a2a8c8a1d99de043919c1f7430aa85670a65d965a150ae47e3f50596f43b148afe03729c91b04632e4c6c94b6bfba7cc06a0dee47db68e89349d8b3d3cc889e5852c55e17209a71c1ae598e7167fe2bf02493ac42f91afc58588343a20c1482421488686ff456f1aa74cda2a103f38220d3a6448890cc050abad4dafbb17c0e1fb0db9443fee995b1d574287046ec3570d2208e6cc6ebd0c864fe47ac692a06eec096f4e0f9fd469355001cb864262510a46e15ef5f45f3370a9521f402369ba5692e081678ca073c7e89c0f5f840b32039a6a13ada04107cc722c36b1f66e66e3d9443f228e186c407a67a958fc19110b576585e343613674b3f63c1082e9f928f7eeb4aabab3a09d115936f7dc2fd80a70e8d45db877ae0a62ae2067bd2b9bf2c94a3e8eb32831e22c591054c804f50dbe2f59a7d8c62420c2eb8f3b73df5d328d9a87cae2dfee1eb88299769ab6700907cbe9b7274b904fa727b886487a6cc8006e419fe53c20cc858f9b59f5b67d00fe80d668e875704c86f1c59b2c228d057b627ca54e346bf79f5a56f1d623310dd187731be8b0803022d94898a7cad048dca68256976969650c9633f4aa09330b7cd80871c0f15ccf3311f83ae7b00af38cecab64a04319fb5a4aaa9377e5c502f2d64e1db85603b1241666998caa97026f71d57b891afbb4474331477d17540519d23ae42ec8181efb0a1441e48bc0aea6037c8b77d0b0933e06b20d9d36d70dc13e95f1cbaa6518e59c1a91c42f81ce38b518d526b02dd8dc7daea6db0e9003e8efaaf5c8f7fc780e5010069670d7e5702748b4b7816a76dce6a3cdb4899e0ed29708444f7d200da63740fb7c6537f6f80feedc6aaa8d4d754c75e0fb30e50f6ed9e46cc0085ce15097d5bbe5573c157b067a0a9ac489e8dd0ccaf947ce7701eb39660c0ca3dd107f1771c5663edd23636590677f5e9be06152acbaf383690a640b14986093df332005f0f3f60a69905f19d1a9cbc5540542108a2c199f830f3a2d1c5e28c80e720185cd62d43ccd697f0ba0c95a770751b6091590709585032b0cc3a0a913db766c5ff09e036722a61f15b8648dcaa864fe361f69ae6a5d002fe49cdbdbaa26ee7982755f756f4faea78040447df2832f49e1f0957bb4ef02a1b6b560bb2d2b474b71e16c9e4cd2d5a1df41d2df68fcaa78ebdb70badf340ad9de775504910be3cae4fa2e2ba919617667e591fc6ca7c6d3de32aaec3cca07b5b9637b95adc97dc6499641a1a31780b4dff64ac5aa8f9a7d02db722c577707a4ad72d6e5e4adb18325cceb4fd6cc492510e0323def59e62d968a870c6575090666f02b6df7baae501833f8822f785190d4a4231abb04a83ba8f39bfb7b7205d9a981bb8fb491f8d37f8ff4314d5c1bcf6245c296c9fd4a9d754573bd5d5e0d2bbca164ad824025f644588b431985d18a35f0cec6bf7ca6631ce825e670b3068943b553c4891fa50a9af200122bc97a262a1e6d7366c2329284cacd8387ac0dd196d9d8f996630d2ad1ef99bf5d171317caa0a0fb94c86115162c5b9e76750e4f5a081df272eedabec854c4dc9efc2aa3acc03e5cbd52980ea8be434830730098b71bf670b5ca1c50e7228f09ee5760e9fcaca30e80568e5a2d560c5a49b704353cd3982be53a945d9bd28db2e2628e47c3eeb3e2b07781fb98ac01958ed90a77de31823c9cc5b7aa1f0481edb215cdee65bf4f5c9e0f1310870b4204706001b5b9bcceaf34d56abc9a111125ec04a542938af9f39b20c8a2a7d9c78dfbbe0c4a41e595be489c4dbe92d040817144e9d49b4e32e125f7609ed031763150e80c29244ebcf8833f2c9afc31385504c302cde0101519e823c8000725ed290091094f9ea1152c164a25e6785958e304a6b72a986672ceef6996261ca7595cdb4d007ed36dd9775b2b20f74c5a15afd8257ef16b66dbb842a6c80538649b58aa13015d5fdc609d88b949f4d628b53c8d3c5a3ca33dd8b7f742e4f5f32e82a63f130485e9924b86256a14474d87a1a670b672daf38f5d7c7ef1f7b9c45d67354f1604528528ddd60c139ca0833ec2853c3b847cdcfb1195b0929a8be690e7d627f701980cd6fe9250a84a92790222193217deb7c423256b9dc795e1a61cdbeccd780d3be9d2c0117f47cd278f87e9683673f6dbcb6c412e5921060c72f691c61540049b96068ffb5129c7038d7498d14ee6be5ae2db581399a3e5d5b97fdabdfb770ad5623fb28b4e5dd1012af0edf9f3bdf8ee2ac859db2cc56bad939c2b237e4a05d08a44534277230f4e144a462738e6bba05fa08ffc42335e3cba52431d62450cfd734241def81f5301a1a360d3bd8fb361267e76b7921d554a0a18be8ea13d0b2f37f7167b7047cba35e810975e574e63fa109ddbc304aaf543a7f250c4cf10b4ef39fbd2125e53aed3bebc387d98b82b31b786cfd8a489254aa6a4c29ac5f05063f77bb3d8f5130d2750129eb1fd1beaf7c9700a8814662d79cf0ce6440bf08c1b5312711bf66bdfa6f7e31b6f3146302a5bc13f876fd4dac1ddb50a462eb05a292b799f071a599c8f509f14357ab9e2d64f0bb4ff963a3f0bea411c24e5b07880a720d52e1a9fbba609a86e57149dc1f4cb9a9e98c7e42936f1eac1c63930a9c9602e214e422408c6620069839fa1f2490df1eb01d57085baf334cba8d79057d0aea419f503a57874fa55a9cf13b1dd9599a644fcb90d859e97d888ceff20cfe2ac392b3fcffbbb69a3cf91a5c7e611e5dcc0822b6bf453fb9303bf96bd5011cce1be35dce1f2837c80df1374800811bcc9f0170588b3f8cf012bc7151eb0a0e22dddc7d5f0508afa8a4352fb218295d78f021bd54c704d9de4874f258ec0a451e83ed9ccd023c0d35ddca5acb2a91de6586c617d942e97a34cdd2f9533f0bf9d319a8965e7ba75cdee818b4c0c1f5bbf2f46e97808ed3b498f79a6857fc016d35d1b126fd1f1fdc6e9afcda7a184eebac4d82aeb9ca3106777d5e98d3460a23ac4da841430f2936337645611bba2d65ba1481be6959ab29d0901ea471780f8d15a5e151d55015c22576856bf054bc3d69755e253a4a34051dffd6dd45650c3e2154f5bae965173ca2f360c44eda8dcc07771266aa132db5bd6ee67ad27e09d8cbc2a350615d3af6f58a2cc3c425b9b613e4c9b553dbc20c2b6859173d8600f6943bff3eeec49794c56210e7632f5a63f8fc6bc7cfe8882093c28a35927c01e4e35cfcbe7e60026b4590f2fc098f80fe3b410bd050e957f27ceb56ce187a0bc0ebcff5d60750ac1daef3458a9ba47a52738e87d585073975078b0144d06d06546f15f137b3919b5d5ecec0e0b9dd5b892a25b3dfb429462c9cf4d1ca005c0eb21a2d9a298940ed8a9f46d13bcd36908946e563034e21ce01eddaa8ed112f09e293e5e7cf56f2853849f16502e76a124e2ff78573dfa8a999468c1b67b8e501970df04b863aae3f558331f4f953bb357d8392ab66fa82b528fbf897da00f2094879e82a71ed2baf5f8ba0b8f616cb50b435ed56a7c9aeb6fe9d3d06e01cee0ba16650b6b53f852cc229d46a7dcab1f6a9bf0475ec8d98acaed8a916d061340c8fce4968583717b7673046ad6c2b402687e5f0cf86832d05db989da49431b00663f599718e68040631d86af65ebc2455ecb13db4678bc4db94e8c07ad07f0703096298328c559f8ad15896177b9ae22ac5300df758c5f5c6e0470071ce7e2403e7b9acddfa7a8b26dfe474b09367d55432de66b45203f75bda06c58f5eafac07ede66027b54bd81ae0b376523bebad5b392bfee557512abfe94dfd9a916ea4005b76627163172cdc7874b118f219cef35f8fb9e24f7280e5d72d4c0fd1adfe01d6a43c90b85f2acd859bb242fb1f9ab325d50092f755df8a37092da21bd8d701776267c1a9f824b3bb52edb31a55346c1b036fd9fbfc441e1cf1896d9a52330b6f046e6b0804f8b92e1c58bf80d0d9a6824c534c1b00b6a7dedee98c3ece7604dc52ac61f6c8cdca14e30c49570bfc7b983f6a86636b451ec9317d5630174e055e72e58852998b0022d72bfd9bb7bb924cc4189f33f8178873735e4e6190160c7d43d52e958605e89fbd223c62a75c8b9190a2e07775511b2983e65423cc4000e458bca4cf0713aeeb81a73ba918dbab9631e1cbf5914ce74af2c629758859046f1b8ebe057c8ccdab02361c0b26ce0e5d625d19741e8b5300423aa0eea6e20ec67f938ed8c01a4ac90c61578ec6519e85f87e7419124fa2284c84be6ccd860b7f519abf1b744b8571038bc38589f17fb3770e224ff19617c4f710a5a5f6280b5e8cbf78a2e7fc08027582b163891e642a82a38672f5b7ce688a36fc20e0d50ee4e38c85afb5bdd0e9ac8def5c39a8849aa7f66753f1112616f768e1026ec40aa8e91cd0897d37c39a50914949c3ec95fca4267cca2bafc6bfe908616d4e1101490c71beb31686b6df5c3e51ccb38cf41ef403fc0725cf625ea6094ea0a74b00b874d65bddfc11d9034f5d4a273c870bd75cd07bd421f4f915095b6aea434b064088ae3e2265d345d4a7564c35a19e5c9e3b5a3fe6932687695197b419c4140be00928fd5354edf7a4975f8b17e9478171c80dd808a9aa5cc197cd94a44be90ee40e12215380a43ee0e23e586666eec332bab42e5baf42c3c300b1547df5f902fb47e77e7a0b0675d58fa7cdf28d7efd405075587dde50f1ee1a4e4d08b8c001f52078f936f4fc464275c8d7efdaaf204fae9f54859d771b611bb4f40726660d6ae2c5c5c817c1dba6aecb9c68ca7920f9419f01778a45d4a780016faf22040852e1bf7374585a6c2daf275bcf53bae45862f7fe0e7e06425f0c1580f3bc09042af1873c896a9e38d3b3b9a0bec1b5c2834b5a34d1768d1c72cf0f7407c99f03dec7fc38b807e0e71fe9130516a44319a94138ebe4c5454dbe2ae97ff84487099c7e4dde05266349d93a277490350f5344662695ee32e25d2bf81ba9810ec8065f6bf1042e41fcbf0d39243528fdf6dc2c49a75440f1d4f2d5ca5c63eeff400b2a5fb4a6d99e264ce6a3377ddb452fe77b6d4de1e311c35b776f1af20afbac0c005cfb8f3e85e7a4087627faf6f5d6e2dc23954395da31f6db131208587403096f83912d456a82c29cd8217a5104bd5b9f3c447c21cbc325a3069e93fdee3209e31576a78f0f255e6f22bf9d344f4deb823ac8ad341383d82a07b8bffe1dbc08628facdc6992579262679cf2248740f43714e830be162832a93d90cff1eaaa009f3c15f0d7bd2ce540f18b8715d85111b86cc79c857079d33e97f4bf1b502507bc2eedb85db6f4b28f3eb957a8d21fbff8234773e43e697b01dfb8f8c075d60cc7bf48cf9fcc106d5ccfd308e777130b151e23c2eeb5c1408e9783fb0e82aa0cab6755bdbc5a231defecec0c9f19ae0b552a941be878feb41abb94e0e3beac024e4b606268058a85049ff0096ea006bb62e912a1512dbabf8f7ccca41d8d960ceb3a7188b2ace696ae4827156dcc91f064e95eb254941805b77ae2650f00f70a78b4c200c690bc0a0dd392169dd3fd3453d91700e87a104b9dd051018850f7042e30f42cd278f1efb1d57ca40bb0c9be1fb959fa6205527cc325885862272c098357c3012fd27494789a50b4aafb9d490b7b8da00480add7c400c820477f0d03dc121832e5e9b2f4699cb8885556645f839d639547438199ca87f14b23b423041a8fc77436154d6633695aa4916bb0eeb77927d31fb7389ad042b5cf7da6f40b16da701a7c5228786c9aa5fe0c3b2b2abbc973404cf4bf0cf77c2ce992f88901ca7910708b537aad408351b23ee31e8a71f2b154f39ee7ec071e7d8617843e050c173fdaf5104de4ec725f98088fa8aaf6b2ad1b5e1cf535f5e45b303bac3a0e49f324fcfa06ca00b195f994e0d5187d5b07482d9bcaea296b272c506f99ea01dc6cad19632c034a01a546ecbe8d1be78a733653e8dbc61a721deb5eeab57f0115ccbf2e2a70e6cadefe5eb104b6809d78ecf8727519d1716588a792819d72085603969d35d2230ca826a8cc760414c7ca768b384dcffa708bb14abc3db10703bcd0ad1b0279336fd7a7540f547731e18a38085b21080c124df0a95063ef5c08fcae305a8afe1e0b6e2c4d568b43ffe828d0c40927cd5353db9a4f13b8d14901be99ffba31fbf182cc90b9e3f1a9f1b501cdd26e47b28ab2154b27247339460759ef84a8b47fd2d856f190c384f0265a0058937e2220c3a9a10fc81547b77d0d64b7de49084402573aac65f3990f2384f7652ee883265e34dd808d8bc08a2400a8887f50b80d5a92185ef165a9eb170f477b13b95922debee1b43516ef599a07f54c880d5fed6a106e018792ef76be1abaab1467bd9f2d313b1189e63fb7a30a2fd0ea02746858d87111eb70680406c86f4027b03174c08e7cfa20b9e266aa02b258ada57a843ff0036cf1a881c4fba860dbff1d244eca76d114b6ab636d720574d0feaa8dc4fda1eabfc723de785b97d2c616508927f17b28e571d98a83ec0dcf4d22101ab265aca302b87dacbaed23387bf69ba86d5e5b5bd4ee97671da907db6240529de4708fe305d067720456dbc945a219f0c40a953d5a23be5cb1de0decf6e7947312214bc60666791e71fd801112c2352c9bcf3031ee5f821bd35008a58d8eef0d6221083fe92ab0481d7d0deec9800c923dfd04c1a8a0c152ee140ae56e7eea817a7d4a63e721833d72c9ffd3bc6babb949a5bdff4eec8c612e44038e3d59d95ff73bfec83024964f4456af52bea09c35a07774290ade9c77d7600c24e3638ba1896157bc0effea656ffb444ab75f36777ba94f158335f10ec9ea07576148d0d880a3f568ec361689e86aa9eb4b0ca1942f11e28fca9178c352ae09e0332d2cca0c9177f907a38ce644bcd499d3d0a6bf45b8fcf4a6ea90c98f01019df4cdfd3986023dedaf1f12a2d6c83e3eb223afb9b7577db81f12b0ed094901eb84118895d379330ba82914aa3bbf884f8ebf703c99a7bdddc9d65604b43f0fd1d54026db98106a6e2b5f05dc7213ac689ff0561f1940622f2b50fc5460e705cd15d45703bc316772cef60e91a7463f7ea85a68b77de5e387c2b592d708d800cf16d0150f33e8b535a2a66d37077a6fff7d4fe21a70e1a64589d22a3f7e680f0f856029d69a1861820727e5006e6423c7a009cf28edf3827ee67c38b4cccb0d5a0d323f53f3831d71127d98946e3ba4882913c8039c7999aa505e7285ea7c076a6dc4ee6639d14b2038d1332a7632283ac9116ea7fb823bc37ff901de5a020494f9b96618b9da1b7794407b91d97464690be79eec33480aa378f31bd2ccf002156493a41e1f852c89a2e57eb878b95c87cafe8a0401e2897856ea853acc9e0bb855e2a398128226aa239206fa5d638de4656f7507b4d226d20866bb889a040cb88e6805d0673d552e46fd9e436ff53334d3fb5423774b0a3f799fd83114e80d +generate_ring_signature d9e4680311d1cb9dfb01254b08fe8cd0dcb7b3c503d6859c59dc47e25615024c b8f8617d742f1a96ab3b79e141b52c76545b39f00fd00729ed4325c2e8e839ee 35 c67863dc9547745b0a1835bd2bdd3c7f866df86486e79b5b23c60dbf330dafbd e837d13ecbb2a5a0bd620f59334475bdb332d44f44ae4220e5b30f4134416a87 c00aa3422f0a2f3a5a89edb8eb7c99d642bb33d2b5d4ae42a228ef3dcf990ade a132684aae1af1909b7d32035c7853359deec9fd5ad14e5548cc2e241223988d 39b05e702beb2ef5e6f02b67d46fe13923ba2629cf6dd4d813f92b88ce94e049 d3f8732f7598b1e7d51388a77e409698b1490ba56ff02df6c9091fc3723ace03 54929c78aa0ac34a796a230b66ae375a443a067a07954cdfdf4f34dee0a08a23 81ef7d3eb1c810cc7fdec70dae0a75ca8084651cd9a446005f7af0a37ff68c8c 06081276051edd0d6f829948a77a1ac5c8eae9e492880cc52c3a3b7e9442ea86 6b8762b54c942dfc5a701842fa7cb5ecb20ead13d5e72ed2475e00ad78f292a6 031c76372a7bb536cac58af77c11daa08cfb59f990cbc8c2eeaefc4ac1a598ee 660ca8b174d65ee995bceaa95e922dbb701ddb6c4df76e7cb46a75fc19b13eeb 81822888504f072acfca4da1c30575bf5efd17a769aac9cd0ba165003de67927 ffb833d7a1d148e158172ee880a850d83304eef3c6746427a1ef1370fe147474 23234646c19c1022929d56a62e494ec2d45e7188863f375cc2ffe681701112d0 227bed9c4db50f0e54a70edcdd4be63ea2f6e7b3563af10e01c9a9c5997fdba1 d5aff7375658c886005af953d831d6e4e2898b1490cad7d44a1b428e87e8c2fe 546e5da5f5599dfd4cf7139f92c80d70e6361249ddfcc36a64a4a6a4579ed6f3 4617768b70f157ad344866c8f0d9575c7a8029b2f693ebd75202fe238c9f334d c505334dbf2103cb24205e0ae4a660a592921c5132906cd7f634b78ebe0ced01 688761e17fbe6024f8e51c6faf00112256440cc4a2c7d37888ed4f5692e26b71 fc48bb2d96d7ade8b51a189f4ea1a1a427669fff066da86108899e2e069c37f4 e80d7e7298e64f4f273548bf0e2bdfe3cf8ff76f86f1291d28249052605dbf46 23345ed9e65ee1d2f9f0df21ec20f4fc506f837f54dd024ef770a81b12588302 b572cec572366f8fbaf5ad8ca07cd1cce301e56ada7e15fe2ceb03d6f7c615b0 ad65e69ee23a2d216d437521cbc41a946f1a0e2e350eb1c242e53d845e664a19 9d652c7154706b781272ea4ff1a5b3b84d83b2ab86389e927b6b0bbe611a207e 05bd82e4dd30ef4ae5041ba77652e173ed932b90ae8f26f0c7a1c924dbc16bbf 95f6523d233a5180cad52fa70fea8ba013a1f565f9fa5d56e6099b99018a5948 e376bffabf575bcc111d39638e0559a8d7e6055e594f5f43fefbc5add7904697 022b90003abcdffe3444bf899962aa0173489163c45d6f3a1c023fd8f089cd6f fbacd6c6ba0fe19d9d48ecb207c66a7578648a77e9c7e4475721510afbaab99e 88f5947ac68fa3b16c1c768a9bc70ebf76ccd2800e6acba428ef6d8be78b8b34 c4a86247b999e7c8460f3784603080ad902ffe2e71c986278c7a26db99c32447 1b83e2998db2167cb4f9133ec411f62fcb2246a84ec1ea4acaed25ba5fd60d52 a3480165d43b66938e0169faeb99f7d09b4c918620775b48e699b9e3e78dfe02 29 d392363e5095333117c49a962825fd195062e800bf70c56113bc43552e51860d238d88360ab32c01657adae7ef564af13f9fd4204866dd7da5f17853bf1a23000415bbdcd6d894bc056d377cc4ab1f08d694bdf62c572f6c359a914c1351a50b6d861bbe3106919d2f7d88c9edb1de76b8b6d12807811967ad84e42d04abed08d378f1467bcea70b154150670ff65547891b4857a5db01ad7b57ce416bef16037fa3a58c66852874e413bf557bb47045588b279e56517f12c1e123cc9b4fb9004e922754f84435d2a6bba08b1d08cb54b84cc3d64c36bd893039466aebb48b06cadf8468100b5e921eb05ba1ca821fa8e6754988db9033e3af252e66def1d308a5c106a7964734bab8c5678acba8f5b07a39bcd04126d2d05a1936e7e1ad570fa8b326e66fbcb73b86636b12e7cc728139e4d62197f428fece963074cff7b30df7fb235cf50a9e6a2c7816e0d21880c2191a94319a998c32fbb01174cf3f660ae8ececaca971b44fc0a19944eb7b48348f8da3b0c9b4e7e9ce4f0abc618f4601a815be934cff18e9662771b0039a39168c29e2ab2b05baa3fd8659657f5f4c00c32894a8cc1754f7ab3c13d06dcc53276ef617c6382fd1b4bb4b601f688aee0c23890314d6f4509ec1b00bd8a3df2775a5032c54ebe6d1aa6c9f129f4214bb0f6accbbbfb7b9b376ca44c6f72df7c03c5fe7718292c59853f6fef6c91600cf0c9c1476b2027ef8f475e15da2a8df91929ca0f4ce687590d95ac9e6e2d0878a0ad8c38092dbc484a278b1a0ae2da6e9b1fd105f3454dbdabc11c39bccf3302000ec9ad809de0b247f86d4e38e644ff7b29d52cdc5dfb23567232955dc7171690219f164a52b7c37b2d59973e1e58cf76cfb38d02b59a206d212093492442f4705e8969b8450a74c0dc4d9681d43ecf433c4e72ae8041f2fa757c562715ee5470aaf2ba2c951a8c7d6bbf6fae29b2c6ffc193c0af2fd419c2e87d6703f47465008353fd035ba7d3e29b40642d905a47235045fac3bd0d65c186fc83543e82fd5059f02cf54a9998ba3ef2e3ce397fa72260c64ea56674e8e9ef96b34d3d05e1e01373c5ad927623a312281fad2b6d8d0c4292917304c147df8e1aa37e6517cca09fda6af031fa6ee63e0a6692c9cef71143458ad073e3bc956d27c3fac68358606b1efcaaf2118bf88255ffe8318c1472d7ab5f4175c1266514a06e0b8233387025c3f5e3c9d46429925800d464a5dbb306cf680b5a8c6fa047fd6cf299fca380f4e67a7ac6cd1300b79a1790f1fbc535ec33969dfd79f9b6311e93f8dd35da50ad98a60c96327e1d573de75d5197ac694ac66774fb4a5f011b3442462a9c658022d687fb6ea3dae9c33505085ba037046155851720940686e88085f18311f5b0a8ee3858eb99cc087b95950f355f980a4f96d85f360e43762d7047d27c4e7dd0d046782e297b0ec579aa52b0002c62cf368259710878f3b5661af152457f28b001e1e243964635c80b1721b4bf11e5515c972743b9b33906c36f9c89cfd7c1b056871b6ad76d71cedfb11fa2d29a5ba3d309c160f90c4f48f1718ffb2310ba7067d769e772a0f82734091062885b0f379af0dde6090751de1f179853e48752603b03671cded7081c873b4de5a61082e9bd1235f7abff43f052b5457cbfcb8fa074555384a9cc8722d3ce79599b56777238aa78c09009ce198a57a9160206bfd08bf4fd8a1d4f2488174c67c49b39fbeae0ee42f2323a232b0bf380514b120960212e1483a0db6dda991f2c8736b9df9e7d31c981d7c928daf12bce0b1fdd49a0728f9e00861dcdf5fd9b15455132a5240d5389ba594f15438bdac5e4136181806c422e629d74126f876a0a5d14b0f7544980a085ec1d119cb9fc8d6379577d40ca62b66182b2e1790f55dc0e10fdfb3a8cd7b28da07e78512d640a1668eca950c5fb61dc96ba0d777ef2287b042a10c8eb9c526a31927200dd1097bef7c25ca01eb78c3f71630b880ac4cd2625ad980c0d2169671c8efc42e016ee7e5508a9000ac015e392716b3b9e7151013725e06745076684abe6441cd18e554799d12f70d9f52ea2355df3546b3835c7f9705c6ff7c9ffb41ff3b3263a9ae32eb7039f70b9b4bb332647fb99d7d61d05d38132aa68261f94bb6ed18bf100ecf9646dba1017ccf1e9aabdb9ead1974eb1d1c1fc9a059ab9788c0b201d8e5de85d76642e30ea6f7353b99dc6dd80ab0a740c50e0ea1e7151e318007dbb6d51fe42409f5b40a7f6e01bfabc19fd3149b4248e25b016219b99d340acfb3d9bb716afd8731570dac6bdf299bdb7e4515d8e0eb1fd251506074e08db975a975e620a5a31f9d7b0aa6296ebcd0d3799784a3a151143349e977dc8bc09ed6d89d0d62b93d143af50f2b6800dd90aa984bf33da5e6881b8e2e541f3bc8d8fc2f789319314f8ce8850ed369009d20ffb10daf1634e68c3c011dc8d8d9d2817f759b6e9603d13f4b740f71280430425ede8c579f344964dd22cb449aa31f64537414a80633c1cb02010328a7b38b703bcc632979024c62e92715e8f2adada6dabc007b8c78675991d703b2b935754000274bbeeeece5afac04415974fa15434575878f3006ad4cd389031832adffab26b1cd6d155a7ddbde35e3b0a62efc05ab8684ba374204f2336c0d00d1c192cb0a6d854b764ed8d3f83e2068ee49bd07e128ba615777ee2b7f4a0090a42dd8186a0d1ab8b906b554aa4ad21129411deebdbdcb856c177981cfa400cf9571db8d53f7ec1b29a9a9bd79f5df1139b42e2a2584776d23d4b43ca23209a5ff9d6a7ed14b42885adb3c17f3da044059a23fabc7e7208de27365612cdd0bd18c042cdb282a3f7f3f16bbb79113df7b4d2182f1bcad7d695882cf0607410cbb3791961c3ca45b5ee8a18d8ab6d14f7aeff98294769cfc0e75b6c03ebe9102234c1eb380868c0f247f5abfedca6a071fab84bc88dbb249bad56fa54e3b5408453873905b0137da3d5e29d778f37a989d6480d13e4386fedbdfed3578426a0619393d81047fe2b099ff81b61f41ced2e97ff14477fa6f506e1aeb8fc9fbfc001d77fe4252427d74a8e231a95546861d0c1490923e6aa0458b24e2559d95e60c9ba5d9c8533713c8fc975f18c534283b95974571a78352d467ca2775f64e2308 +generate_ring_signature baf768d9e42fae2c59729a1d2b8c1c90345ba669b954316a5674d1de6220a76e 4d7d7d439f8fea0f37accc6bb4b1d9b3d2c6a8f1de4c4080d058aa9aaddbdbb0 1 782b450055215a1e6bc07b6003187d9560cee16e6f5262521df26ece593c89b4 388fafcce2f0aa9c54a2cdeabc47075bc4c6dcfafcf8bac5a99cda75c3e61b01 0 02c92f3789d4e579646a7d07750ee92f9ca4317517437726849c661714511004014c8289905755fcba47c89bbe6cc437f0d54b346ca4c961c5f68bc311c54f02 +generate_ring_signature 01916e70c1a168a97075a7d873849f7ac03e65094ee16f44ae66ad4a3e0a9a61 19388d755023e1604e7a357953c2692ebf537afcd3c5b175b18b4480c1770ed4 53 242cfc91401e49fbe717545536ff1288d9929ecb3fc5fcf431ed7e457bbc4b39 5720bfc3064c22d0bcb46686feb470b9483cdec909c4b70c63a8144c603de1c5 3144d1064b96500b9fa8ee6ac4f95292f6d92e7c6ca34b5974511c380807739b 90aa403c95a6a5ea6866ce56913201a0aa37d5c09e3e0c0150b8f23e6a8f7074 29a6167a49da7587f4a4a05e534e194f7fb0858c8b187e681104939611678220 47a0530a95b6e548ed923ddaa08d8cec94bb9afd43a716602c73f7a563485426 d7792a71e94ddef9dfc4300d460fa547d0318c04a0f43513e98fec2389fd03a1 84ef4f5e3c509f8110ea7239b531431da9420ef98bef880020ef1565915a9a10 1a32deba9900d23de6dca4cfab511391e4bd2ecc9d153f2cec3a4ce4fa4c54df 371d5595b2a49bf72d7824711067e3248f2429e086383d77de2035a5da111c25 68bf85ebcae2c713975dcafc9200acca99d4d740f05bca766d7eb82391767f66 837dad3d55f3fafc1717b116c0f7e7121ce70b4cfb542b5452a08f92dc59ad55 d66ab88fe75167e16bc30dc727e2862128b938292e5bc60a5eb03390edf0b133 32f67f886be12e0bd1c98eeeb1690acae74e7c6e321ad14bf6dfe7bd843176a6 572c46495fc92467f1bf1142893e0edf08ae501f806402ed1b8ba58cc767f8ec b845884ca93520147dbfcc37a28b5d899faed2f821235f2b055ff34d2ce0ca2e 00617cc79d54d01e4bf16be365e673c3cc6bec51a5c337fb736a885ba0acd573 6a8278978430c6e99cb857f4e4512e8278aef7fe70e4b2cbdb0aea6d5baaf14f b0177887294e8717c7c4300e3daf4e5d35721a54fb85aefb99954ad1cf15228e 5d0a7d23e13f32762fe5e66ab9be9aa73336f9c385772269046132a11795caad b229f7653c5630d1b2ac5605a5b2419d7c8ad9879d6d296eddd14a1ed788eda4 97e01654bef78ba3a81c4bcb36b07c6dfd723a867bb2d1e1009574ca5a4a4444 05a16ea1786cec3520ded847a0addf9ac3dfe96c3315e785927d75834f73b5a5 995498a9ea1ba686429c39efe8fc92cc2bfe7a4a3da23891b302d56b18c61075 e4f2da569effe699c788939b58b7271eb5b63347382d7b9fa650e0c16caf7dbd 62b2acfa240fdd2888dcd3ae4b3b60d9aacf48d627ad4909f0ee3614d2a1d8bd 6d52a488709aa858e528c36acdd35cac6cc7a1e0d612ea4dc68a4393ef095a56 da6aceaf9e86d64910f2f90e7e7d30a3459260c9dfbcf427b0704f5d848ddac3 84c11c1ddf0fbe484e80c375f0bbe985133c72d3be646e4a62691b3ddf02c93b 132529a4a7796849c67315835700cacd444e85a11b9e71afb5ca41fa81dc8373 69d1d9c94d25a965f96c89bdfe92d5bd5a10a145312a46d990d8747d9b54ce72 a77f3f1c05b8b70a0203e373031adf0596eed28b5a434cdf0abaa7c2221c5a93 a611e3a402a42a33cd8f832dd46c794c6adc9df2bc32747686ac7614a72d0c2a e8f60d367002f5fda4a024992dba1b161fe1802e680977b2e2f55a9c3deb27d4 e887fde0d2c22351ec22b8aa8889a80c0917613ab397c978b2871af576c76fbf 9a90e74a7e602b11e1085681ef5e08a59827b7976ecd8e14f5c7a81312a9b84f e35b60b7564725d9a7dd34a6ff0dfb5d488e3cebd79f8260a7057b2b2089c233 71a3cdd1e3f0840ce0cbbc9049efc87f376938c4a6fefda0e4364684176e6bac 6dabcb53034fa276be2e73851d9a39aadd74b3ac7ecb0eec92b43608cbf4b02d 13477a1e0cfb62bdeb1a30889489446cdb3e37bed0e868906ee91da6ee4de0aa 1bdb6348c889278979c6064c256adb5e3bc76b61e332b2981a49fbd79a14dfe0 e3c6ad547fcb3afd9dbce9593931897d8f736b5044f543475757c7775a58168d 379dfebd4d07db29982a708e4a03ef03a583e869e704f36dd62da8fc41867cb2 4249977ebd5483286439256595142ce68bf2a88d40fa2a0e204bb8918b2ab1f3 08710fec6ed8004733e622281bf186975b7c7ec6e2a85d9cd7fd146f9386908c 0ea6d13d9634d24f6aa872f341b2bfaba220045e6dfd752f0746cc1b5e9732b5 201ac295b1b5a10a4317748a29f94ea0e5b6b069a8759eccf5bff9067bc1c829 623ea94a4d6de8a3db5e0c19026fa2623559fa878030c98495111ee9b2951247 f1382fca2d626d971af139e3873405119843d0b0237b48d08287d893e7f003b9 5e536ecd5570c8ae9165916d9f14688dbc8c82130e1c589e23ac5ecfa8a39435 f18095254b9f272c5e5f10322cbee3cb62cb40e75a900bfb69b1557c254060e5 d1c0c862d42a95b7008215dcd1cf6c4516df9830ea11c217042d0f3df720814e 4eed5da8eda2ca0a8436e337346fc54f3dd38b90748bd4023580e7fbfe271dba 8b58a9e402a6a320edb50825a07279ffdb434d5bba8c2b93bdf568144e098a01 0 c99a49941b816b7b2d4d812ac29998cec68644d61c7b9d00447960fe098a8c0f1f7b2ae405c3b784f9aad17963df6a69a20db822e1ddb2711612b0295d7c0f0cf40fc9778cfba5f0520b2b6c19e88c67f63f36a1d2d3455b40051d095ecd360a0516cbb8a7c86ecf4bd9987c6bd981c3abed6db965815b0ec20571c90016380500bae8ee2cc3dfa744dcde36255aac1e5fb795617e1e67d0fc9e8d766ce26905dd1b6c455f095435ec412e7a93637c734e810eef1c862a51ab12370662fba80762117a0a65481b98d7f92528be2469bc9b7b67779ffdd06b7b953f29fa040807840913ee988b4974920bfa08e71a8a09d43c38c532fd9db09344390a610e3e01d8d27573f8d6f0618b79ba6cef66470c646b6bba1b8501b57127c7837b24c40ad3002e6740418aebf28bcb3d236c8fa312a0dced5fb5e30aa451a5179da783006c7801089b53a6539188bda423be7af7c42a56216cd8f74d9a2878246d7867026086e912efd959ef41b6974a413e6d5ec54fb39d0caf03e76d6e52f2ddc1b401737b847da8c1ea3a26ea79437875b582fd22c2a8c8a1982f9925c70a088fd00f5a60373e26344f28860ccf8e6ab361676e918cb73d73c3e1ff18f29574459b076522d6b6a7d7292fb7bbf143a8c651c14cf179ac582a532803749a465f8cce096064409c944aaac8933c344b2fb714645dd9dda31306ee2b17960929beeb13032e058aad5888297a1fca21041e51cdabb38157f157458d3a546480d1ebea0c0c218bae518c72ad1f096c1ae339a02933c110121f1d28f53c7d89729bdbf2e103d117eac5f5f7a0408bd9d1a702e25142fcd9ec521883c11f818ac58e54f00001093d6e0daa39a484336c4aca8c444a093794d9abc3c9bd3c4f89d46d64af040c8cbf27dec713ae9d8342a9c5979735e2726fe7668377ee37ac80cc7a605afa0704983e5ce71c0ebf5069575eda2f54396cd2d1100adfce24796c7ddb5632ae01f3cdffb60309e4748f4b5c8ec7eca298757c9bc9f1e73a843bf65477fedd0e0e5a61765bdcc15bf9cae2df7be445b91dd80855d7570ddadeb318c1786d9e4404ebc558369b09a0cda3939cd9e7ee6eaf4e22e4dc3336e4b7650f347c8642df065f29c55e9e40320639aa11206ecd5de22297c69bcd781b3f42e565d3198f6607bf192fe5f4aa21abd106ac2e093d107b243d2ad48a2aab1b3b5832d8e472530845e573874cab57742cc31fb0b1194f7e6d77d491dd377b55fd2a780bf8e3cc0791b6529f5897038e98e2d33d3f4465800c67671c5cdedcb21c2e792ddfaa250aa4eea5e7f81b56e4ffb717bf0228679cae32ff073e0f0a7fc6a7799de52e49026738d6cd7d1598c840360cb5c168d98b86365817c50427f30d5e12d60280dd00c25bf5199c86a8f58c7681754e6dc737707a6f55347ffe409a04bed4b577bd06af96d8545c8c0cd7968f9be5f7c0bd9775aaeab7161604d7e36d0b0c848de30002807e8ee3a5045d591489e0b33169c5882ee700cbf2570a17ee7cf142ca13036d816b9aadd06fd77f43a9e0b40b7d229375a7eb5f6186453d6668fc18683609a28007d19f1d56a08630da9a7e4e3ad2e70fe9247ffce23eaaa6b74a33346503623cd0a1811c329a25b6850a8c2efb3001da50fd433e20a85dccab521d9e6204acec3558784ebfccf56114aa3ae7f69311fbbcb8d6d6b0bc5fbaa281e2abff078f6c12c4a289d0211eb036c8b036f306b9b6c32c88d3731f27c7e45ef27f2b0b1d975a5a614ab986d65586339115121dac6fef2dfe50431b6d1b023b50808002eb36beab4532eafdfb967675647e4b6d7310e902210d22d125173a817146e20145bdfad526ad1e57b57dd6bcc86fbc9e8b44051f5d0b69128aa3a965200d81036b26ce7f4dfa4994acb50003f8ed28913cff52a9bca774535ce8f78045688400169e42475e1174d5da6412e0879aad9fb77add817afcf783d9ab4ee83aadc501eeaa4939036df968ded9a4755b2d757999c262e85745fbbae746e5e50165b60503f670cf0f7792d56112de68aad7c1fef98fa01ed39375d0e9855ce7c032010a902b51cafd56d37364eab9c3ab21f648b159e3cc082d652cc8bd21754e2cac040f838be62e832e995c50b0610e5e4b3e767f8521c1745a3e63791a36f8475d0181507f31220558f352c2acd8be5beb2bf71c0c20ecb2439b94d3db429797eb0058e4e7894d0aa8313688b5ec6df0823783bf606bcb14de2f040805c9231d7e020c66b902affc191fc9fc25bce65c42facf62f6a76892c054a5531b48b0d932057430e5f81cebcd9bf6c33161f21b5fdade5647792c9271bc592fd173a363a00a0460f0c19dbbf29ba107026cf0cf172b4c7ed9cee9e9890863c65d666f205302bb6af64a9698ec2b1dadb2eac59438d6d3c6a4e6fcf79ff765cc9b28efbd8d0b8034de405dac0ca7688a1bb9e6cf5d84c4f9865fc86add46a2ea4717be88d00ad099635bb30467bea5a53e6918f3d9a9616e26ae2553cb08c44c4332bae35a0cc151842be36c98515c3c08719740651784ec93d2e2a47c7be9cbd8c71473d90f21c803384233e07bbcf10964985b38d7d935f74749cad59251153f4a7d27ed0c4ec1276018522f6f2ce46a97ca1b25989ad5fe9b92bc6b1396112df79ffb220833d75be1a6b9d0e6d17314396d0320188ded49127b721e113d51d2eb3ca6fc021f91c52554ce98b54bcfd5d4f3329fab0e03b5984aa08f19dfc9615f5ef2630aca7cc525de7bfd0ae0ba54aae895fbbc260cd2fead7b5055e0664c2f0ded3501c78b6a530857967ba3d942fd0ed3d0796a11c03c1f86778b8195fcfd84d952057a533d04d748c77c0d365b77f1c0dc0dfc69cf461580562322567f8b0f460504007832dfa354ed18ada7f0abf2ea030e4c8681452c063df17597f74130884e0798a29529b624df50c49a3ec4102e2db13848631a78777ed6df003afa0206d70a9e5d31e059dddd0206dac32bf083c8e51f26cd53006ff2aa45edddc588a8c103c611025b883f9597fd32e1a6c9d2b99db3ff6c43bee87c0b55194b0b927cd909da8475b2a19a24843a492df9d46dce549db892c5aad79653469b732b794d1a07b52f572b6f6abf3a1a9fc4489bcc30ef9f3444261b944a92d5fb482d50ce4d088a79c14cc07953072d778b0b9dc7f6d91b4aec1c1a1218d3700a8be986d4f20171e51855cbe709567090f2ac9f50b98b990a66b25ae90bf30c8260898c3ae10fa033c17f463ad520bf60d18d159c1017fd1ecbfc4159fd97f7dbe94ef5e63e009fb47b0844893954c5edfd40f4c11840117a275b76a19f484a0943422b6a8e09742bd7745bf312b40324ecf9cdde61c7b2b7da2dd461e5f585853eab3556870f81ed26b26954c4e70c4ada951c3a81913cb5e01816f554c9c3ddd9c50480e40382a826235cb02887af2c37c9f8f983788713b04f3f3b8bc8c1b3af3ae98afb07e07c19c47559130fd03356d8ff2e766a0af558ec090b3fd13c6e4cecf1be6a0e37d02e193613c3b4df12a4d7229bb6bba87caa16d21e0bb6a447cde94a5b210fe1bf41d1237318d7cf841c2c1e88afca135b46ac4d3e48df0972841434165106264494106387b6d693bb7579e71daf6e4d220d22961e7acd111255401300c4039a0ccaca5a4eaa7d2483f41df06cabb1d6e35d4ac321bd13f45a0360a3415506811ab32cc6c32430c426ac484d12e6125ae53160747d76a2620582c14410d30d4eabc1ca13e0966898282f9493457b1472fbbb57a7120ba8af7acf811426340f658a0ca5911e76974125cac2006093b71626d6883d8dc82c23ab38531f17d802000375368f07a5994a9b4c8c651cce873d4cb192328f6d7ffc3dc32c292d47048e6739a3faeec4a0d99c4ddabfd70442a47756ee6897ba86b0ee4bab30dcfc0422af7400a5bb9ed4a57062bae7b3e9f94194ffc76087f6e986252a68c2eb520d8b4d8fea463992312272ae6dc503053bd8bd58cafbfc6cf0e778a05636b93d04de19bbb8a764f2663d63247bb7cba7392170f097795ad3e20205479b9d6c3d0c1e3d1aadaabc580112dae35ecb5d57addc00dd52d547741ea58fd017ba4e5b09f8da838d473aa5aed7fe26b054b217982a7bc79b6f1177b930c36eb5a0c4360c58cffb85abfd55a9ac6f3a9fc521022f18a377a5f874c6fa01ebd6ac2202150fcfbd787aec86c540fc64a636ca92636fcd290cbb109e206deaefc4e65b0f2e00b0190fe0dda4353d4e2a929ed9e4efac45a9bc93dcca748970460355b9632307cca776d7c41d8a77dc84396c4bbf5a9772c40b007a0df203531d2884e83bb50343a39bea1e436b3717c29c5dd29a36c208c22293fe75ac1e0ced941cf2d96103739401ba57d1c7d908d7388dd8d4daf08c1c9b678e8393614496ce46a1bd870c64912cba2d64c528955ee84e35dd9f85476de412c22056c1546561cb1f113e0f511e0216f12e7751b661832a52586fd908fd42e2584a658edb5158514f13040a77010defb6bdb5db11067f12430c30a28d2518c01ffb670e439f730aeade4c073c6537abb4fad0d09c8cb41546cf8a03e1fbd0595e71f75d034de629c6a0600b3cf372cbfeb570f0d196bc51a938f92252e661ccc023ec062117b3d28d163e02d41a651b463a5dcece0669b9278e3132ea18402986aeeca50008f5dfe9eeee09a377ffbb4e897e0af1c7d5a48c2cf87c4a30b1e56dedb1d25687520152c2b108dc3d4a7f7105c2cd2816830f0b27c7795ef4d58a947bc6476b10e6e1d883cc00 +generate_ring_signature 0fdd09cc2513c1d346346db21cfda3bf3da4d65bf66a91dd87c83fda9b9d407b f96cf77c4abc31236ec66ab0e3c56d25860cb2027897a5fae40622a7908ab147 16 a5f687daffa9505005c73221669b1a728930db51d4d40b78ae6a821e9b319440 07a6ddc48dfad0383024bd700f46f56c88129345439bda5e7bb7f356ce11c8f5 585bb3d7f53e474d75cead5d1ebd12c68757bfe231091adacb877b09e34cf3d3 bd06689f501f10bb2f4d5e3cdda3954a6d55ad18c3ad485fa98a4e83b163fa15 0908f88df8ad262a40203ba72994f9bc919d79acf8f3847c5ae72205f8ec29e7 6ada309079d68bab1123ec762dd8ac04166d8ea824acd9408f502731dda08a8d 80d9f89d04bdf2c205d6955cd4f285790a4282a6d75bb555b1342138b42c9364 77e56ccbd4d1ad228e32d2be7837e3871495df21e94dd1328428251d7bc3729e 6387f7309b7005b5073e718b24b2b7fef3fd84749fa98e91dd1f83912487cf56 6b7e693dd187ad5c38f73079c040366abbf2990dd30eee6f685f0360ba22eeaa 819f739bedd0589653cba095292e436678a1fd38767d4a6e9449f3d4246f12c8 5b0d6f09b5ad89d7cd8ae24e8ba3c2c4440cb8858c2483ff1363037491953fd5 e3a579a3dcaf3ba92fb5f57cc4469d1ac00cf048f0f3950819875b1805e43a46 0b650b9de62dd063d88549fb717d3ebb8fc15b3bf7338457c53b7cb4c2b1350e e8ce03b5b89b392e9d2de32cd39416b7c189f5ba81954c37bf64586dbe707ea6 feb0d23239826ed5813f0d76ebc0934a4ad72186f9e43a7895c54e5e03c55b32 e01f9713a2feb7d4f9a06fc9b89f1f438d990269f8650286146ccb2df251e309 13 7dbc3cd7dd0cdee2f4ea6b023d54fa01a5201268e80372d7279dae38d1b0060a5d364891627ae4972ef69051d8b80289dceeed2554f0eaeb7efe0fc75173dc0cdaad9c881b5cc5da881594eb3aa35439e80ebe3cab22c99a5e927ecafc0b7d050981d651ad100f90c81ad9fa1d8a3c9e55162253d80626712b5076c34cd82e02ff2cfa0a9b141bd1e60c83f390415aa89c6f45e543e1141d60ff7292a240b00cb2042dac5e2f90f800855b1f46ad67789bb2c15a413376202a7481a31bea9b064966cf5b5c8b88ef8149963cc3d486b8f2b67d568b3eae8dee0cfb88ada5470d0f7a843eb7aa0f241f6ec12cafc3605ec246c3f53e4e9eacb3fabc1e2669e20a756d5b85a68e09988d3ba58fb2891208fec425c32e143f0eacd1ab18c45c4b04ad6836757009e32b1e2aabc93ccbf920141645ecb84d8529b1e80ae4738ceb0c88e5edd988e61de07166ef5fa497f62246508e2390271f14603b2a5682c60b09fcb7053857cb24b29bcca1e9a1bbb372dc55436871199caf0c06ef087b08f40531b28ca60402baeceb90602f7b013f450d153d770fef8434b881f0ac3ac6410d038395d459aa07594c3b18151ee930f4e608faa998739da5502ba8d10ab66a052f72ab36b4ebf52ce3aaf1cfecdd99001448c2b2705a53babcd9a4a303ffc8057cf9e6482be3c3bbb1225e1eec07412028f9d7e00cc59f8fd800c4623023f2051c2ffe5b0bc18048ed78196bd0b152269f379b23eff10e6cff2a367ec69bcc0e6df5880402e32cfef89622781316ab6a7d918ac84ba7c2849ee0a02fe1f9c40dd2a5af52b04426b3b7588469d11c2b9ea8d113025e4b2846dd998b9629ce4808c8dc582dd2af193f027e4b1207f71fc34ee81586813d950275dc33610b38aa03524cbd0e5fee5b3ad6f6ed4ff907233c7ae14b3849782e3ac2b5bc87dce5680ca5146536f323c0d7e213f15cda539b7ffaf6eb1707077edbb9e21112a615160d52df5b4866199da8303824879cf62bd300ee15362078d7996c14ba490e90ec0f42cc321dcd24b226a1312113bbd2b7e9044d20be8c3a505989f10ac9ed30190e8596691a8638d247700928d36db4d5d3b3fe2e5b39ace8c9263891bee7e43e0d172ed241411c3b52b4867601a27bae38d141c6f34dbfc4630fd6f523b7ff430c3ce8d644fccac1997895e22b03f12710e9418d03155df042e2aca01d3d3ba00bb96524f479e199caec5aeaa03ceaad9500945df5a59f579412f5630c725fd80d8adc31ae1a5556e2f1758c5bd4e5f43618dc135c997be33dfd07c9fe3b7ee70ca45429d0bf98375d90e5f5f682ab1bb3eace739d95f875090c43ddf426765f0583ed1ea01096f30dc3ed33b88a7ae6a88cad49e9885fc74501ff1325dbb02f05ce5070dd2854c66ec0a03b256d4598b26bc073f9161c62668f16fe25a641880d +generate_ring_signature e3495fc2ad438c8128a655249d4d6b69b6c17ee1dbf96ed1acea6380d07fb724 2adc1d2a97ca897fe0342c2165b8ac198eceb0374a7320da794d1bd38a45c962 5 dada0d56d195390308e9eb9304b877ed930a1c15de8c0a28ab04066cb0cb379b 85e9284587832bd7e59fbd7d3fb9b37b4b63e801522f187fd5bf45cc4f5454b9 ca11ed3ebfa24d358807b3ccdcf4c623af574b8c07c825b4b4b6f68cfbe9bef9 f77887b1191a51859ab5bfba228a360ce889059a60aaa740a5de8e9d0a7a1e57 c445962d9a4260693d6307b3305fcfd65807ffaeaab251349e02ec4dcb4f2d02 5d0db6d9854ffe9d26d8df64f9a0f5ac01aaa30ff2819e6377724f76d5c8d802 2 5b14de234b81eaf22a7a339e9cbc1525f160555014f1ad0ed92aa4f2ea70660f91e5d586c0e2b8055396813a707f4e336fc1f29ebf23bfd43e87af0930ae000080d19fda872b57d018fad4d217fe1b2f30104b9db0f7643c649ede742bb33e082cc736c78f5c52f8158c149e06496e58065aca611b9233398c03a4fd4de2ec04ccac11085fc697e60df919fe95c2f405ae3ca5e443d50d3c5d56619c86c0b70f15bc31f4faa970f4ce75c8278dbdeb50fdb56e1112caa684d486084a3d2667051981a48729a575eefbcd9180bd2530cb4f554880031cbec9c250f4b736bac10888c97e0c0c673dea61e28b45cf86909937351d8791a49f389e05e612dee52f0b61b053206a9c88ff9e9bdec0c69c14b408268edaaf6d04a21917bf05848d2d0f0c3dd76a5067219510a382ef3414512994004bbc36c28098bbca81f7e798a903 +generate_ring_signature 923a11626de7b0b613c07f95a600a1ae1b62ef2b0bff338120f4aca08a518f0f f12c207e70b2dd9bbfc6988a0ebe5f07f915b5bf2b1f1a8ba23c5db2475e8d26 4 eab9a7d80ad2d7e9060076bda332b1e2b31860426710e7071e5f34afeccd51e8 8d48bb5e57d8c7b0950384093d6187ccd03b0c535a0edd6da35a6f015f928cc6 079e64c24eee1da696543bfb274ead55ee33e3633b9601d9e3512371194dd4f9 6348488a0884315336961ace563bf106bef9f2c5e745e5204531a3e00695d5cf 2d4e461db0a3b63a7c05d4699b1c790f86351dc1fc96d1f8365ebace89d9db0e 3 b04dba2e3c4a03a63d72d1220c6da3073303afe4f792b4416907b31d8697230672887657cd684132bacc87314a2ea693ff6f55f006175965793f10dff6e3960b60764dfdc141284bbe78d2ae1c2b1431a16470a1cd563992918b565980c6450c40d957ca80dd7a275c5069392a5f1de2094ff2e0b5154798afa64e199cae9107d34183134c6675041a3364fed3b7cceaa5253b9bc99f8d7c3be09bfdd130ea0c59582971248b038956be5aa44e9437c54f2342c6127d312e39281af5dcf2020657e5f1d0891eb5a46b0b651405ed053a4ab0a13b0e8da31ec2439899e0f2300f7172125dc4294a93d4e6527c7ecca987951512aefee33a27b9a7a10eea2ed702 +generate_ring_signature f7eeba3fcbdc35c01d69774f6d1e4ca270680e87e1af40b2773dac9e9b0a56a0 6f1f04eabe8e9213c7c5d4c4a4df1b5564e63b054d3e09ad81eb6c4d930ef44f 7 ecbc3d0e415e2c5360b40747cf604fcd6e4751e125ce2ed8f7922a317bebe282 60887705484f05a205b5e6c98ec1be1df8cdd4115f0f3edf9b545370d81b2283 4310521f1828d60b124567720d381d203d81d07b8c66434011e29bda436bf7d9 4280fb5b19e595916a02527cb77814868af83620174dcb21bc8da271ef680004 f2f8b898e39ad301c969cc191c11b162995a5e1f813dd63aae200a718c7cdb86 867556a39a4dbde3c7fabd5180eb0e7ce275ea211db2fe90e13c4c040c0cb28d f6c1111d0bdc89c508faad4ef2015ea98e1f5416324def724136e030ae647603 f01ce7ed8d9dca5629b5666899c8aff6ce0b58ad2fe04b77ae75c2ed6b9de30f 4 02736e71e54f4fbd27e02289817995f7ad15b50fc8a2a27be822acf7ecfb78003e829d6bf32e4aa8b64cbe3a66de7e76ec40f0666776e5e863c48d4115fbbd0d5ddb3a0adb8558054102c34a9e9b5c0a6b4721064c7d11aa00080bb9ec3d11040dd6d8ed85c0fa1633ee2c1a7688b8fab9a16ec6bfc42243c0a80c662d20b40e7ca9322bf94591cb392d2f1affb4ecb2b3f0512d94571088a775af791e4603051a4707901a550c3104de4741078ca43b456fc9acbcb67aebf46856f2a062440846450e6af6e635d6e91cdccd8b7d97b95ec5fe0a6fe0616d2192c27f43786f04df12ce29b07e57d97f354a2377a29fed4c63bbd9dc2cc8ece9b07d8c0501070b4c9189be08957fd4d3fe173ba27906ae16bbe07a5630435c04abcf280f18be0b72a12fa67edaffc7b8aae244354e04cdb18dc478e4bf804e70c2278010ea8d021ef154b28e8452c201db3f644282edb7d04330a2633c42917bc07bb1980b550d4ce481c6134e6ce3805de3a78fcae1cb255624d164349b8995ba13c3bc1aaf0a3398bbb2ad49070286db8f9a46f17f6ed2fc9fc1518156dc6c1395685a0609023a9f21b809008e97832399710ea9e0d1c12f4456a878b2c5086e915166c11c05 +generate_ring_signature c4dee30ab0373ada5dee59e1362d805d6a2eb83f1a878dc9e82e1e9dda566d60 66250b52e8ea438a69f00cec532367c889ac73727ec2b41f696bfcec68b5788c 169 f2f2b60d7675c181f68eabb0946ecb76d12c2ae176c8d7f2b2f5cd7e5390b732 743abfcb7c102fcde3281f0989bdf8aa23a38aa28370e9caca172f5befcd9fb1 6925c7c3b98df3427d346866a2063fe956404bf6dd718ad32176af5f55fa4932 e13d9ea26cfc623476efdc90ae5e746d5c01ece1c662c1b93e9cf19a70830b7d 05d454706821b18b611d100393ec3307138bc84b0c0958cea0bc4eb1e9341236 cce2645b7ce500aa4fb89088e6ba8b90d488e7adc3403850b6fd762500c3317d df18cc2a78fbe16f092a779a7f0503cd26e45a8258379e4535d97a01e4f3af7e a9d9437498986268c6e2594b4932f8ac8798254bbbb9f17e9fc5be12aa35beda 9a8f7b61754085f10a5ddb5a7bc293ad28e8365f8b51e94a18e974b7b536a883 3295ada3db3830f1a2887c4f1403eee920a836108a42a748414da663e7fe9108 80545b9db676fd424f717135f3978b39415e045225c2aac5f340af7169e68b06 3d9d86d009b0be89558db01f5ab33c2bfa32242167d6a7afe06ac195f6c500a0 37662037db5136dbe094d1eac43c18c9239969f9d3e0039df9679cfdb4f3d18a 3bea446ef619e281d5fc24c358629581d78385b0556d72fb244c66d97e2c6483 a28b168bb36453c5fc8ac19c55ed117077e0f450eab8972815682e1fbad7327f 6e28882488bb1a06b52d716767a61584235e7351b839b84fb2d340230dbb75c7 52a7690a81457f57c9e79fcea48a019f3bd405bb6b66e61a6f63d9151d719a7f 9a80b4b0f25383a2c2f70d94ae5a4da7b15857d3242d05669e84f02d66e92edc a801c63fcd5ae02c607cc766f3ed531cf1e94316ef2d2989ebc611b200a300a8 34e4278c824bf5d404204de86aaa1a806ce70fd062eac373faf51353e9fa6202 4b6ff12fb5ee85425a5ef473502e22bb78aaadcf63ad4d93ed327bfa691d5a40 27dfd189f1ba9b2fc2b0d48bc6086b1a4f242130ceeead85acefa3a92ef86ce1 37b68e0abc377dc3c967591e5615c78e49ebb08f1b77952b5bbc8d9696f2aa7f 6bdc04cd1004485d5c703f63811fda02ed60d3e90e0dcbbc4e870027807f7db5 e106081b39c6bb35c5f5607e57b1eeedfba6a6060f21ed360faa2a8ca5af8d1d fbfb46345f0bd2e1711359b292ebf8bd6a7e39e531f986591954979fb09fbb2c 37b5b8821be52009c9e93c3ff1010474644f5b2872a8736650134e21b387ab31 c83fe1124f489b72f050473d52932fd5d68f2dfaa2dcc21136599cb918b135b1 cecc1ee7fdfb34c04581d11c61b300decf57a42e280edbe70320993be72af60a f2b51106e9a9d3d144df1c152a80a090331df2efc91e310403f0a05b9eee87be e17858155d4549918dea501437c1148710a310d0f90620fea070e83e02e53d12 46b090083bd9288b6acc4812e363601ddc83c03ea67b482eb55d2a790b77e4f8 bbfbcc1daff1f526118950dd3e2709c845523e19026123c5369a13c4d59939cf e075c3c1ea09cc5b80799103ec90cb60576c33c3b93a73c24b0d1ceb45d295f6 af65af5d55b0d0d15fcba45eaaf58ab174f985bd4d41c2d20b7a4526247c9714 80bb8136d6e99bd8016b594c39e7b137326ddb543eac6fedd100dff6d6b4b5b1 5339bc37bfce89a3dde58c7fa4db847a4158508bb50bafcacf80255382ac7671 c81b42947cc5f69eaa5c203eae4df3b3d020dd3a0c5018f8a9f5e34348624d0b 6d510f05dbe39d83d6640cac35d0a60068331cad220b17c520f23fa4552fcc87 d25684244293b811b3551f6dab2a5050fe9dc2bce52c15740a2467009222ad5b 9d87b136332a86b9c5bedc40467ccfb345ca8baeea24b84ca338928ec5eda14c f2e3f7adad9febf75773a3c857d62d3263120100e2022ae9b77c58b3c7b894c4 d7d5c94f7e248cbf878428a2b144af620e4681da88e766ae43b1d6dea2169967 30efeb1e0e068b2c359373c7add7676425c35d7c7fbf3bdb90b82dd824008ddf a761d5d202b96868e878551709c2b5ab877d7fe56995504bc9052ae0f0200bcf 55aa7d97269864c26740e4be038efc3b0c5befd21ee78be97639826f5f0f28ba beaa56cb576bc41b36f5751c3c7251a70398e4db0a3c09ca35791e8981ea1f91 83d7a2343fbe5165a375540f07a124513fcdc269f32c397a138f8af02a44ded7 26efe640c840ea1a9322378e8e90f84a17627e792190d3753a4d87bd730b18b4 f1a97f521feecc1049496d25dd3648443c601de11949d575b0c550d8baba51c0 d3a56987efaf33ad73e78557598c79892562eccd7a36207e1b4a8f2580b77bcb 5d74020c3d7c93bbb99d658b8fcf1cb18497208ad1c44bcfbd8a8b956646051c 0a0c3a31f1d7cdacaebb20388dab4a8ee07765177f8cbb039719985a5a942e74 e8fa5aa4fc7d0821c6c013cbafeea5d28eb6a24c9db8779e0be56f87383a3acf 78c803ef1f3df340b6cdd5855289dfde75746ae4fb6e6f79b902da3e6edcb510 5bde60d09e8518e581b09e441d9e9ee10fe68d206984d8da451be2969dc6351b 9f0d4380fe0bfb2639c08219c7c83b8d0dd4f8a37b9e7f956315cbcc91df1fb3 ee57237455930f2d6a13257fdbc4010bd95502b156592117befc744e68b6a4bc 05845a51848c70ac4ca85654d54962644005fab916c14b572137bfd21c90e893 cefc22e0ae4f7bdb5981e9af5226769bbfb4117a75bbfe9d0c0aaead403ad02f 79df1350f974a43e500894a2ef4d069347a199579a198bfbfa7309818f11cd3d 0043576d4b1652fe8a3006e223fe2907221a22c73ae9dfffb4af218b970c422b 178c74199c65363956e4c567def65f8ae5fc0ddb7aa7cf04873db552a18631bd ec64d4014cdce524f52ebaec746e4cb7985db07efee7f230f7a45ba0ebaf2667 61b2796c0984c0d366679762172eceaf6cacea13c448f10c01d9b95a3c0bd932 78b5e5e15304a5d7deaa7fb92d1dca0db3e8caf4a621596fb5efc9e45b56763a 8307fa27a58136ddaab152cb540dd912a5f9cc467f87529fccca7be31e3360b1 bce231185b0032910b1983e47b427164c87735004579b80ff136b48dca7dcaa8 14b20e79d48470cadb4c806df5af9b034d8df8097a3be1f2d7928fe34d2b3cef 4247fdc58e6f242735df1b4fe9ea9fa87c0f93ec02b7a1f57314325ffb849cad 45c59346b2e561a5ce513d69d53d60b0513d0c34fca33d1a82147187c05da5f9 5f7f797554373b48cc0b9fc19fe98fdf53fd7dac6895a3f7a7e773f616501c35 aa2bbb46678024ccfc9a9087a282316f6af2bb04c3757ef7228831d9833dbb83 dc399f899363da744d9d4153065b6b684bd658c393efd3590ecdff3d9c2ed52b ef6d0d6e6176b560256198b8e349dee4afaba86decd2bcce727dbe95e2b70636 d32ebfc10103684af4d1d40e94d595541aa541f4580b1f772655c653a02051f6 7eba6d64022bd3abc8a5c86a8895bc2997d77dc9a1945ce761b65b15da53c6f4 cff49af65ffe5745dca4cb015ff2c06015337e90d3eee87ed64de0490989c684 4c63ffd66a6dadb52cdf67ddb33d710dbccb0207c1e5566ac61f22a21bdb129a ce65e5281d0168a505605fdb278f1898e30d0a941f8b0f9764f13179b3401aae dd903cde97be0cbe8286e4fcb959f165ade330f86c365bc5373cc5d6921082f4 38346157f200df62a9caa5b51c0b39266e67e8c8b22f31876a0737ecb3924c64 7a59f714f0e320e8decd66d486b6aed8285ae48f77cdbf8db8496e43cf327dc6 43549791da8e97a5b368cde89d57dde61b4c7b1826d2bdca03c98547d6c1f210 5a8087729bcba594809cf4a31b6d2a66713d11f44282fe19fee4555fa38f33a7 80de3bf86da79e5ae3c839ab0a723291d3495dd3903c8f64a1aec3c388e774d9 2953ca5866e7d6eda4551ca518fdd1d8b88f5621483cbb9ecaf29097ef6876d3 bb9814f0e3957bd5dc7ce3fadea884545594cc240806159fe497ba5888164654 335a5909bb1f4401db26d7e55f726b209d154f632e713e1bda691e1346fb1fcf 34865ced6ca72c9e1ccd2635f6ff2a3689d3b968bdca016b9775e4df7e58377e cdf0adceffeb97350dd41a3b6ace99484a4f4f7980ba08c3c22bad35b2d25622 1095dd8c0c3555876d83821ef96927bcdeb3c33f5785b7eff6b23012c84e6bc7 e04fa71c25a0e3484b479825afbaed78bcca81d1ec16d0e02b0ed26732ab70db 383dd6af2e2638681fb025379fe1a58438a5b71ccfb44f62082fb711937dd982 3dbe16c9dacc2a7c3d4f2a7aadd7323208de7e81cc92dd4f8958768574ba8d0e 1b724b508e6f688287fc9adb44f097748eebc537b8a9ef2e5ffcd5eb0db256e6 a8ed0d0d34c15f7b9bd8539eb4f0db1a73703f7b1a684590737870417889d75c a1ab8437038769c0677d621cf9bcf25c02fd4ea19386e18e7a1cc4e27ac591e3 d8263c35b3be8a8054c7bae2c394b6c6dbbab44c0a3a8ecf0d705dd1fc8cba16 b903646b44e144d83b2a5338054fb318a0cbbaf2d4ae28f4e683241c72a45645 f66868f006d85e3cbea999cc1de9aab9dab8ed420e887eb6d8b8bd5cab1094e7 f062cc8d8b539657ee47edd97f11ff9b863157b951af13a736b8d16c82a84363 8c9fcc3758b9ab2b89d1ee8457b0d666087169ac25bbac716414ba5587047695 bfd1f74391c25ee492570d813899533797a11e321ea9e3461bec229061206ab0 455a4eba5a915e05782377566b72dab647d9c633dac408aaea6f8ba97581f044 e0c54ce9fe9341fafc56791db8d5ea68e8cbbaf222ff855120afd8dec9f62695 377c0338298c3f0f80e509182a3fd5a40c81f40d15b46b762c0e5598eed8ca41 1c107e944813fe33970f7d5cf7e1b12539e36364181450a0f60631c3b10f9813 8997bf02ac05b9491cd822642d487c72084752b5e4150b0c703e032418d754ca df6b6f5942e2fd0d674c709dc04297c935b33fb597edc3a5734af6196318a617 ab7f3b2f0558840dc839c5f281a190d8277534848c9f64534d9b47f887a54809 78ea46342cb94abffecc038e4e00ea9a5699dc34f89dcb625b35c09c31c20090 209130802e9d6511db53dd050c8c20963375343d5e47823fa970c40ffc553c3a a7b4251d7524597f481c5a5308d1bd3b3c0254d352a9a5b78f18336eb123acbc 7dc00f08f1299dbf52bb029f19685213291c8cc9508a1b2143d5e7b7b18b1882 ea3bc724e789117a5c484b5b84441b515b758399062f2bd98209706bc9a8c3fb cb0f6539b7b8dc5dc22b2301428b9e9a2f6ab08787853b664f6845ea9e85d9fa 315c2d8496df14859b7901592b800da7ae4db9f8c92653dc6d0c108e8b4e0eb2 980a0e7e3ee524d557a0bd194d2b81c5c305aba1c8c195691e4d4ddb4d257f52 41763dbab5729980d1bc0f26643e657852421493e1bdde3b0ecc756780fb6c57 3ba8348fe5bfb2577e997df67623e132d5a290c2eb381144bd42470baaeed8c4 6d9b9089ee7f98aae119f6c6b622bdba6955abe4e874a085245635b8f2b94393 4c82127d23c46e3053c6490ddb8aca8eb567ce3bb1c3972e26a4d47dde728567 5a9a20efb7bf87a0e7c91eeffd12efbbf72d7ae4c0a8c25b9d9c3a4ff8411c67 1d186b6ce7892fa7a1c3f4f00a8b1fdafb82d97e9e7aeebe18750f45d114069b f367f4b4179fa6d22d6d22a828c33de8ed0298cde5fb5d17863509f9417af0a7 2811d5b3ed14736137126bfbf65204cfe201c5298f70aba9d4d3d0470d729ae5 cf4b99cc377fcff0b7ddc95b4f4b523c2310394ca5bf1a17e802afdeb906cf32 e6a2fe52632d2367d577c8092a66c377ad37ec2cb209c7786886e5704bdbbb25 4ced786fa5fe2666af4caf36d267efc92906c327c06d449b601005fe4ecf2ba3 c742a00349add70c374c25e31f1e192e97705e210bcbe81294380e8df3802b51 6bca8c87b78e364890984fddbccc8c407e512a5e691f8c9e4e04f526eb27db32 33f41b7270fcceab849b60835b0c5b8f3d0a3362db18f6428ac8d6cc629a52e9 f6ccb30dba421f16c87224c1fc6c910c28d928947aa80a4aadebe14b5472e3a5 b75d3c6321f144f78bd014df235649645bf6eb8d15a0b412c8791f97dba6ce96 1ee09cd0a0c0ffe069acb5feba91b0d1eb78b810977acc83131b03415a3fa2cc aa4181c9559f8e935047ce91591939a372dac2b5daee47209472f919c6035a02 147b4551c883e364fe17ec3d1e9a40592cc27f8052e1fc7d17a6c5ec24e2494e c9e70b872854e07077e80d0f66a062e15c46c3e108d7cf656a5acbb5097d38cf 32e3e959f6dfb3e9e3bf1b1fb3c822bd8bb584966fa11dc1e444d2ce4cade8c8 842e0eee7a3fe30185d75956878c418aaf33dbadf2d95223031edcd9334a14fb ce6acedb380e0506c71df56f08e37c9cf6a6a7419a0a84d67421f8c56524ce82 c4dffae7b32ae71007f42fefa57835500094a53a7e59b4003da57be6d752a1df 7c3a885c38d04251b942ae97223b302ea5f38383fd01eab497709c42d5798a6c cb5531c9cae36b968f165726c3918cb6f0904779851d8ed46fd9504a90a39776 7229ff35e6048fb93a2ddf96abfd5642442d91ed4ee2928e2dd2d9819604b79b b79d0f869bab272133ec14603e03fe4cbbe5fafcd859b8751d5cffa2f13067b1 49759392147b1f835d36bb592ab1b387a433067c4ff3ef27c65046c868028b51 466ad6d9a84e0662e61b8a8abb0bf5df7beda276d68909d944a28ffdeaa8dbd1 a3b15d7dc0194fb2f1aab6a358d2b113f8e82a5effc3640a89671c76e6a70dce 43b84023745566f7628a0c30be507cb86dfd35b80dc8eba994cd1d2e0af685f7 b8fdd27e4e758747e0d303623d0eb13a255037f5154e0d869fcd2912436d0036 1070a72d4d5efdbef3183f5a0ac7699ca4f91f3d17883f44bf7fbc51b1fae1cb d4506ec0ae0bb3ebcb7d481bf5ca8a68e19125b15df4c15d955dbee943294e55 858799a67150b2fedf3544b0f1e6e3c3d26f7f886926f57f53dbca535c8f6cd5 3c2938a328956c51e42ba52e7bbbbdd2d85cd9ab1807ca2451822ae8002a9f47 2a1e89c0299dee6608a8652416d21ee18b71282dd0772c65d6d2af8174de8890 c710afe34c83d62d6fa2713f8ecd695ba6f7c3556f7a84f9f9e817abde3fcd14 9195d5d5b1a96ddb0f8366a2ff9e0ba783767d0bccc52449136ed07c05eb1729 8324d8cfbceba7901fb55976c5fa6acb13005fbd838dbb42e77495c867adbffd 5dc1c7243b65a5a6de9bfe9493ee49c369548a1b0f7a707c50b49fd0f9273e5b 8cd9443a7faa14d017876ac6116a4de3633059124c9f55e71687317ec4a4d4c9 0749cce6e8c6eb0b30cc9599b9d1e2e8f536af0a52e3883c9b1a0eaf60bc61ec 1900841d34f0df254a39a2d586c69ea435e108c903e9dada712075752da3c470 72fba31c4645db0e3fdfd6c4bd4bec66dccda0e3b1f39ca0963dd6ca237eed10 00de416b0503948f1eedb67da56b1b6b529f648c69f43bcf1a91bfe88a8c0e15 40b401518efa872876a64431ac7463a297acce71697edbf95bd5a4584385a91f 6300f0d82da691b8d88cfe2e2766f7547cf1f60ffbe70ab984cf1b8946334fd5 fa3eafab921b0a825cbf4841a68094cdae5734aee61725ae8c598ed4f66d43ad 45f89106066fb315ab9004ae5031b1f914599c3b0d9f59cd27ae15c9289dee0d 51 eba04041b199c295583b3d5c47f5f3a4f37a5e6ddda05be78a1c0a7e3e3bd5006c59410330fbbeeaa2024d495e77d3dd82d7c629eeff41d06ecfb2f6f0bbbd0bdff3987882f06455af41a7ae98aee1bda15b1803e25919632fcaba14f391ab05538366e6a2c971abee38a12580f1c28a313aa08b80347edf8bc9ba97cc3dec09c9307b37fe6bd16b31b5aec3c432ddea0083c6250758b0ab710d550e797fbf02f563e3296231ac35a26a85bdc40d50f8a71d7b0a04fcac8d58e721eedb1c850e5014485ac3eba8a1f838aee68eda74474a3835424994098f369992aa8e31520beaf70a8ae3a29f6cfd38f3c3d692df3d03274efb0f2522e0234927bd327d470513c21e34f7d0797211f35b17a276796322201fec51c251b160ed49a833922b0e14f251616a6930236f3075d222c5c1fe7c5fe3c904eb4f2cda39cdbe4bf3b80f3d99e00b12fc167fdec761dad45d7878ce6191eb440fdf8d3b590e5a791a3d0902734b97c6acf63d4a03a3f4fa923f634cd31f6eb5043aa302466cdeb56179034aedd4195541e6aa0ce4b14ed48f494f9cac4d5900de8faea15958302f8aad0a6a97adeb3d61f8c0a66e97dab9b6c3014bde6855d6ddd4f27a61d78cf8804206159ed3316425aa5a8da34c30f798ecd5e0b8deae2dd89e62a50f2a093a33d80d246ad0e707fb290a05aae2299232860027aa27dcecea6b8cef8fbb46ec56f405fa85d97b631e3944bc29d606f4a897f53144c66bc6236a4ca0df3a996d2c900d2d9007bb220bd4b6b8ac7b15793eec9f9e474af8d085407ebddedda4b60b9b0b9e7d05e2e4f6744b072c228a963642907e44dac5960c41b7da56354b9ae5490945796143ab74cdf9227dffddb856ce63dd1705d57802d7bf5cb9b85c6e2eea0c837730139446d602414c94f0798911d8ccbd3d1ea4a878f799d8aff09a49e00d160841e884251827f78dace87c09fd5e69754bd50e16a4a7246d808f0e6b640f097aed72e68d63408dbe6fbe687bae732d1223fabc1804fba9c1ddfe40d65d07328496fca25a94c4d255330f20b685600525c111147dd5064c0df65a0eb3dd0a2b1614251710381d1ce49d2f9e4c02d2dda4d9445c6bbe420552a7140ad16f07eb8b90901bd2f43fb72d575539bbd0f1079645d9dd7a410097eeba6868396708e131f344e3139bbb83b3acf692389d5ccf9933f5a4f07d972dabedb5142aea0924baab9baab4a16d6bac38589b43a9a8a631b7e84d0490ecd65505750624060626485ac46d4cad98d0bfc1a22e05faa93c4f19a898cd76a76295582331e589065a3120d5586a65206e9d54c529532262556a6f0f4c41821d69532094ba2a2c0f2c1150e4d9c829cee1f48279d208a1aa18cce4fb0757e50cb7721b1b57f77406e8d96c180fe8d245d67246a51e0e3ce476644fac65857c6ac29e26af06b760006b90f1e5a0ad8e3d069aaf50b7cac0b71a8e336f989cf0633bc42bc1d2c9a9009ae9bfd5d540873a99af3a8bc06f35b31b69ba58d759fd6129bd3df27c85ae0668f4e695c867992aa30b113456b1b5b3f6ead1ebe744f4c478583da28a1f83059e71be14e7e223b04809d097a3bb04a16612891b5dce672bf026480ffc23be03ec7cecaeda733e8d94709de1452711bb89bcf2064c5fbf54f8dbc42a09a72904904dc6f61cab975f5e6285872067a4b2dc81db55622cb349a952d0b8423c960400291dace75da275d0190c3ae01f5f7a23929c912c2da7e7df5547dfdf1f7c05bcc1a56e1de7b800b1be637ce2f1993edf2dd69f5062377363d37e8452831403b2dd54d6d7170bb0debd886a6a50fb28ff77634c4f8e8b0c949a989b4f0a1d00b1df6169e54bb450ece8282b6fab5931e825d26cab9c75afc68cf58f03d2ae05e556b28634e895b7c1adf29a5939cc8dd1dcc323bd2377e4d26ffca0bf1d1604de6df4f31a0df8a31b8b68261f2c5a28c10ab713040b99954917a75ad31e400bce87e455c3e39d6c5ca11bcdef5df0b0d066531561ca27558d346ec3ee57b7097e2811824cf7d441c42538fec40f6783c695f6cfb698361d21ccf30cac708602bb18a5e2a627d2609e9c5533efc346a2a6bae08b1ff1d340cb67264e0adc8401ee9c24454dbca98653c19ec92e7def47d9dd398fe2da283ab466f2ca5162b1027db2d7aa3a85ac5786d1869caaf84a3c715d108121abb7ebd91f1e127c0c880364aae15de3901be5e0b6fa1f6181f0586f9df59dab88db9af0aa289ada08ce0ee773cfb2917358248594de7b0fa9be0d0699f54c16ab3ec581c2c9fc7154d703fde9316c838244f1a9dd3c6009f4f6f7bdc4e2826a0d400ebe322758e218980cef3044b754a86e113023cf973b55649e482e2ab18182bd34b38ca33e544e72023fbe2f23ebca510c1d257dcbf2a68d38a1fe1f9bfd0af64f8c8a5ae6afb3a60ecb3f8a43f0a6f589f0e178110bc6f2fb9e46ef810ffaebf9e1956a1a8f82c601a73e6f4d6b05740a1a8941ad4a53465041a8cb0082e1104d26e992842178940b4d621815c75ecebf31e96d0a34a5782d4bc4c8b5d4ecac55898d033e267dba0dff69d3bd914133a7afd4be25d20b0d8585ca5852b4f5caa14c1099a4430e4901668c040437235625422b6a3ce0ccddb961f3002bff5a7e95e69ca9f208ad950f2d96d5de67fe4a3efa801431a12a274eebb021816ed9a32f40245471e34c5d0cf127148d3867c8f98a223d8c30589a0a55d3ce32a0b07d5bbde6453d77d62c04e7c881a4ab2f2f8eec40df0fbab2e7f223f21da20ca329fc78bd1a8b082b5e0aeaaa110225d0bb065e40b269beb295b16126c04c095b58be9d4621f6995e7103b3fddf74f9e26ad5a5a1bf7884be7573d29a04baa7e52452717502314725050017d4e6f91e8822704460820f700bb33f1f51464f4cdbe9081612e7e5c907790c481acb9c159636920132508dee063e943d3df139dd4b5db4fb07dc82394cef00ba923a769ebfd0b8752c3c7b8e0265e0506b410dbb886f3c99071a15cd192a030a41b11ac3ac39a8eb39c0e7a5d326c590d33a3038f97ba28a0c61c787047e04c0815a502e89fa1d7a3cbb0b5b233c1fada19c6799991a4f922309f9201d7106ddeb0bbddd8310085f7c7d5e948621e2b3283379b3a9cfadb94834f9fb73760edce404bc17cd91c29e7f0ad88af434fdea071660c59e5c46c028f232844bac03964aee4678eaffacc944b06db61b66d716c3749077164af13c539ee41be4450d974522ea59219c73064da294b70f3f6bcd08d00c49840f28c31aea56afd25b0d772cef073acf5bf95d23e678203446583093621840d854cb02c8cd1a52ced50e2390465b5e521d937f6bd58d43ac6731241bd84c085c3e29d7335765d28e3106648be285aeb932d5b069ffe21341c1845cf964a74044a6bde2842a63e7a5ae0f8c36f434f1e7c525fb3ea5b3f3033dd54e25d179c9d524cd5e7c3ccffcc694050a719728aa8f54e1a3a889663569c0e65d603d7331a82ec3fa97814ed8a08e056a5b3867f45759823ba8dbe86d4b60102d026605d1ab8ff9eb8cf81ba561cc0aebd01b5fa5b046adf96ba75949fda75da1a3ff3d091307f721ad4b4fdfb62b06a69b6f6bb6fb32311484835290e384c17f58f1c36e235d41aecd8aab78503c0750746e3fe4dc9a9ce5e7bb6ce59df759554a9b057dcb3f9af95be4178c34640e7ee8c1b32bd50ce661528a209c3b4967dc0abecde33701c9b7f806b31dd91d0f6f4e76dabbfef290721bf93e0c3e9a2250edfe5fcbead26e0cc7804900945707eb7dabdba53b56fd428e52c01612a04f48c8077a0fff421aee3baf2c23cc400c129f98f17900c53560520570a59b5a3ae121cabd2edb7e19cf482daeb30e7d08f57afefe104efd55ab162d2ed7e9c171f78f25a54272cebfe57cb2a20e2d43067136b89a1a81be21727e7486cc2afa40886acfb7f3b04aa24332b2e91f38b505b27c0e2690ac0000e8da87a7ee66d8cf7ab7e324837f9c8cba33c6e404edda010735f5d925fa66d849a4cd2d7cd56f5ac1228951244a27e542ffc6b0161e9b044fee3e4c4b22bd328a4e6e74add87f77b1c9d69531e5d43380f7dad20ff3eb073ecb0a8227ea7fbcb85c314497700277105e705f8daa79b3d4fe7323c3dcda09753b9f99208f3622fdbb826165bdcc4508df12e5cab71ce52b1fcdcb218f1c0c6123ce1c73032a6d63b0847cf75ae949d8f9ebb14c2ca1b9a9d832a8cc1e770950a726e16a2bd5778d27ec46ecf4274757649301194cf8c486328b705e1d6e01f195a1d4660e78f5c9facdfd09606835fd764f7e21e6d48ace420eed20dc9e0787b6a8bff2cc9aff530e5b63396db5c0c479330577f3780529a2e7890366670cc2ff7f3466955b86b47a72300df87bf43f2c9cd2be23163151d041765f1b2103584a3f90beb3876fbbc41b7de9ab9aec34ea2cb81f535de36e1759a97d35850d62107620b2bf856b5e76c8792d86e362eeb7c033082c24c7ea4e745452c4c209bf5a053e576c4f2fc07417352903c345be0d167022ab65ced737904e2e2c010a9bfe31f13120de87534497a80e8a822f422dc69a38e246e329ef1f935d15840c97ce000cf8a3f714a6422447b2106298de42caf512f8aab40d3ee3adb1556e027e3e254ce0a8072c314f10a56f2b9f9ae4393948965f1464d16b2e0311b00b0a99d213ed84011f2b749885ce2be1b4c6999cf3dd11ce329fb6672e614a82910906c0e65ce0a49194badf352d05d505fac38cc9ae4eb8f246d245328467459f0e405a98b2cde688114de7e73f2cd6b26359b30b185af35b9fe33ddbaeb355870a5ae0a86d3deb581df03c0a26f8d95327c9e10f35aad9b811a05a3829b81fbc02bd36781af9873dc983a8618e27790665619d9d0dd2efb6b838588ace940852005f615367bdcb7e3a6025c49470395a674bbc113728a0ef35583ebc5bdc8191008a6adfd195a2741ede9a6a9a0e114f66a695124c5b1e28125df3a98d4407c40d2f0439d43ea5110ad5a0e9ce0dcbe747906f83e6b020b63f65be447c30081c0a29a66b074825584c67f3eab551fb82d2cd16e1977fe60e7ba4554ef1d986530774d005fe259ab622fdec78fffb379e8ca7b16c538b1185cd81390fc297125006a216108fa1e51fb73b1b0d715ddb573e5219f3145948aecf9219a9e67049f801208af0e06e8bb93d70f2aaf6b3102453ed4c73cf0842a598825f7a3d4d3d040d2ccb60ce93c393392d11f60da7b06d786db98ba632726f2cc73654073fbbf10aace74a3ae6ff693b3325b7efe95b5f057b6b05629eb4e230faa1a776a01abd09adea1a20e7e1e2ca29b497cab9c752406619a1d4e0f67e1166be08a1b57a7f0dbba02cee84e977cafd3005a8360a4f9f3807dfcd973bcb0f60c6f6aafbd6e802772fb78e769a009c627b665059ad3c7734504e391f7e091a64ac8abbeeba840a871f7510529a982f8ed0ef0bf5e2211f19284d900b894c39379d72c0e54bc8018d7dcb9216681bfd8ed84842125041efca0add66d7dfe5c720c0d7a4b01c1c0c9087309b98c3213897d0159864d53c70b326d5d786f17fc62e2431c8060db901018315b20162ca127d03f5fdf689a57c7a542f05add01f500a25a0450ab922096c71e4c631d27d2ca09049dc81bcbf61009ab17ffe167fb8f10023352997850f3ee3a87f8fa039e9a1b22449f08d531cd0e248e41774653d56d99156b41b56094f85187f72bb0de2056af17b99d67185e5618d0b8fbd77f8785fab75358eeb0cff9f481d2e3b7e968f4107068b36ff4a5da44ed60a88f77b7b70f55a690b590c749fb6a4a3180db6a127f548b96171bdd8fdd124766d5fc22a5ddfd48dc07c086eaa9034a7a74f62a648b7a4f8d09ab2fd59fb428540f0ff1ddad1313f415506114e23f574ca17ac664e0c09fae3091489f8426be2b983044a5c7b922b47750a64a4ccb21000e31c85d92b335a419086ba5e256acb7335901e467c52f6e7a70db25ad7cb48d7ff1981a3b22264b3d67090d656599a46d80e05a886fdcea140069df7eed27c380cad9464b071f61c51e54eb35d977743bb49188745b8c8628c0174ef53502575389133f4d28828ada9b76cbbc82f419217f6395f88063763ec09f9e2d04ff0162e45b264b263e591f07580305f8f2bda16eaba552ed6212e9406ed4f6e57aee271e9599d98176f5c6b3fcc2e3fe6ceeebe48a38e3b8a6104540109af1233a481e61c1d8f7190c69b49c7d18f39bc3811398323bc664c57573309f5bac76a5b51b63cd2c284287884c2857dd9f2ab87d306e67c8a637adc085b06812f76f41dc1527101783a2b942e88d694dbbf5df525b22d5793ae7f7d7e8807113350e14bf7b58b7a4e11a052d8c2a9bd01d320ebbc50c94fce8d859af908081857d11c50ea4f97f7b357fefbf64a9365004ff3abf7ca7bdfcc4bbc2aaabf097d068434d843dbfaaddcffa3c183731f5484060467a732b61a0f15192800020fae925adea64a6f658fcf392fb2b4bf5ddd64467dbe8c08f460f5062da8055d0615e9807d6ea828ef8595b182aab943f04d65f267cd61a50244ca27a03c4c4f0bc4d69be239988132591adbdd8dd76dcd85647d768cfdcd5842ebe8cca88d5b04e6a1ec26e637cc6a14dfb02ca0001c4ce9d95d12305ff3f0c11ed6b967623e08f20a465d4bdad27813d1c00ca527b2cd3fcdafa14e21d9423da6cc7c04c87f07a1454eb92c178d7ace7c1ba90156d2c550a49843ec9d13132a817065f8c5a3030067d24e1299a0fced20b8e62fa4811c2e2dd2dbc1d7a87f91939a057a570405ac9df5afe55691f94c79afece38c030f6f355ce4ed4784bead56a3d21c6f76074e3299a8cf37ed66c9f76e055fea6dc08d02d8a4051df482907f6425cb478c06433553a8d1da6b3e35f395403dfc1ff6302109e1b6395030934f32dfae4ce20536892f029cdfab6227b5a3e527cd0be1e7339ba449a900f1529fd654f8ae3d04f209afbf4b1b1c519852a6296ead7d150525389761baedf44ac6071f878d71050beaed1e520f631a0bde169a230038011f07f2f990ad52f07625021a254b4e018a4ef75b6014042aef20a9225d3617838ff1b37b18afe43aa449e3b49f9e2f05bd89598a4eac23c70b3145b1cd1577d8dc2bec5551165956f7eccfa5a507da03a65f6ace76a6057cb4064998eb3f960a5f91f4bfcb40a9a1e0806d22781d2d05da58e2aa1b183b5782e65442511b5ce6dd776e3f4a2ce95e8a3cf900df1783041477cc1dd735ead1425381e8a80d123c979b1a835e0928cb2b84984adb60f4082b8ca50f332cdcd6dab039cb9fad1ef2759f7cdd0dfa44103195d862b6f3bc0e61b5ff393a7558baa72dffd3c6ad39f7dc023509004a12722353db205e431606c9d85a7265c08c38744ed948c8d526aa252a7bb8971498faf318fc9652955802aac1a9b15cb8da39c54e367a3f3ea782c2b52f532f04fe2bc4c6e81bdcae0108d3e3393b7a7179da8e6fa02d219050d13ed3968a392200abf8df9b2139f5b50640d7f253f8ceca1c7296cd353559f850c5e5d5403eaf13192d3715f9c6db0d076a2bf0fa3228a45a3fa3bc4bee114dfda6565d38e9a70d975a0bf649dd4bdd0261bac65cca7b569ada19ddfefa126517ac5e2ac3441ea014b3f84ec7ce69a104bae63da0ec377e02bc209fe08262aeb2a3182aadaf87337937d93cf0265f880ec4fa6937027da17ba90de29893fdf65d0705e4c1c3c3ae497a6b8f702fb3df0dc2cd00e48ceddbd668f4e4bc5eec6d332500515bea352e8d897ef7d49e76390a5e298ec66a838f2216d3f1f0547199f40c4c4a453c8cf014c09a0a1b25e96c064f6367eab9c1173194917c15fa15b3b7a7be00dcaceff71c265f2ece50e67e0550689f939b8792e202a490c0a6060a0a452185097974c96c4e990ea59d0a140b23ed8bb2422b96cb27a662bef936f5e2f7ff3ab4b019a4d6f6051e334f4fe30f5c932fa98971075c6483fe6a2ce29810318ce787c101d4b1ba15a457dca7cd02629b7227721981f7851f10186de5da4a98edac7701b797264edb30fe1c9a21094ac225a28ea9962f807387c24298ca8700e8b609d58bb4703906488856388a0fbb2f4f7678d41bd32114202f7492548957f6c711754aa348d02143681a5f4905b82ef496b1535f848bcd4ecdccc4c8427bf2403d3dc0a681767d0f7ed7491b002d11c85ba1b8352887f981f5247d6fb62e23d034c22b202618512408638b1b03339c7867ec8ea70d7694b5ead1372b657595fb3530a9738a9908fb0e24fcd306d09f666aaab90d4a6e223d2862174b96dfcf7365cd3e6cb6affbb66d9372e602370324f7993150b96215ff3a8fec8f1158f91712ce37086dc7a59d4bddb588054f22284152d2b3b1e591c6b27b1b6af1f92ec615cf5ef9076fb90089541da5026f651189cdc770cfb0cf6abbcdc9af8e36128173ccbd3a35afe1829f9cacdb0b6a20ec5035a4a4cffc2e85aa284e4dc6829e553906f8d39b3a3142289e1c080b5da4e5c489185ca64ade170750f0c3c872b899a6e89b62b644713c56e035b804f90d95a071ec6e1f8805edd90f8c57e8d886b90ca362d6416fb34063cf928609b5fb77b6d87135e2a5eb160fa79f24761aaac4e7bb740bbbf03e5a3f42a6ea0ab76627b7d3a582b71c174fcdb535662c6170977dbb818ffade87b39b268a4e06432d348c279af085217c0bf2fc05bce508dbf1408a211d025ea73adae34cfb0968c223974583cd36b178aa748751209e29a0aa9602f20a2d7cc2cc9411fce3032a742deab670b9550cb7be44d47b8c5cde9362a3642baa92d48bb3be29defb0000d4ab583104e1ace4703894a88c9283d6e1340682377f3d7414a6a002a3e705a8aacf50be94c1c56b699aa95f7e6fd92d8cbd4397490e3751605a62f97c1909fb71ff9c5115abadad7e79cc844c4891df22a869e47febdab14da164c7715c077ce0d312b79887f8dd7a6606557409c654bba0929a98815bacdf90df9c42a202d10bd228f4634f8a5ada3e9c147cdcb65c509594db62959b5c891aa4f363b40f58d6b8c547a85b527bff20d067a12eb2571f7dd98e857e0861f62fdfee5540020d590a06d9397c973a289e3d9a7cb95bb3f807db306158c931f2c30872471d0c33c49e15b70bfcd3c238bf2594e222a33676c0259ac6637668704546e142ae09066ec39540d45f0042c514467abc24eb943ae12a4f19d1b54204e2e881b37a0161cde5a4ee3a56c584bf8c86f232aacf5b6758d2f15c36b7ac7c9f05e8f7bd04e4913b31ab651e05efeb7052dd8277765ff5c23bde4ca362f603a2463a6417053bb56338e1ee29bba920fcb86526a3fb849f57b5622ba1bfd8223f21f947d8014ec16d5f7f4bcd96c63d40e4a39d81367791bc23cd423fe84bd1fd43c706140498c0dc70e966a36d60c97c3081e84a495fa9c184515162d7fb0379293b192c0a2e9ac7ab23089ade0e4daebf4eca54c658ac16e79f0562f206e1bc8f927b190c79f87d86dbca24d116c086fd3eb0a2247aaba0ba1d3982bf92ec30841fb12a0715431b678af7be6e71f7ded583a46e85e0a5d6a99c7872e4028c2b682ee17c0306c1c22266bc4a9ec7aa5bdaa65d3c78d0ef04d4e7d31bf4f0df889a57bb18002b3e7004e9f7f67d39a89b72f44141bfb7beb00018536c5c06b6e2ebac45d50a3ef547cac7806d02ef06a328fe818ee70a906d4c0a462267406eb280c8b1be0bc734ced8874e46c5eaedcb4eda3f5065fe668e58bfdec19dc44099739303f70871bcc8729f9a49915edff6b381eab8caafca01b506a8ad4258ad326f1c164f0cbb311e72e172c8500c093e336afcca4228e12d14e7e669b578fb9e03898abf0599a079a0cf3c8bf5bcfde3feed2744edf02918c1712c6b39961a27162311350cff9289e670d576c142498b8f9b3bade0c250d6511fdcf2ac65398845bcd308043315ffc67ae233a28178f1de692281cb749c8867de7ba9115c04ac1e67709e0e137eb48d3b09ae04b0e8692c344ba6e8b6ae3306a1918d778845d3264d6e660c10442576b87edd5e1ed80115a39d5c66510b4287af5bfc2b329239e8a0ffeb0a518977958b11b0376329e59ce00fe2689bc4d2b9b67b98e0ead436ebcb94fb065db636235c8b94a11a249c14c4f26c4d137af658cbc19ab0ca250961fa3d1d05b87bf1047bee2a882a3c8b14c8ba50f32742081a1c135b3c19ebdb5a445b730ac123613196b99b058962108e5ed86356c103bfc4de82f16c18580b7d9a27bb00ac153402cd742a1259a8db7e88bdc367119c81b204187204944c2e42601637019c9c50fd7ec020d1451809b687f3f068db50b4e8a39475f8662c6c76c86ba607ca94b40b3e1079fd3f321f4cac7ae196d0301327c7dbe6dae5dad63d4d16390b5a66ede93167fb71b1bff91d17d19baed6a5df2694375856c99eb1e7a7b0d1022cc27072ef7179bc10ab3ec2db193de33447f3ad92263db9bf0a5582ee2bc50aa947768c343d11eeadd55a5b258dd1b8209d2a40441e61a82dc80be68958af00c1973befcd6b6e150f190231fc286af21956f9b517a2fa4b1c8d614dae821d0d8059a3aee9c2313983975c472bf015831f0d3a9a6c0b8e75e6b9002421bf9c0d7d4a3ad73c95fc6ef174066adf6da2dc78edb5646e5fa4f4b54ff330014e7401947ec253ac9c2965ea0114ec9970a8678e137a31ecc79d230820f7d66a926c06bcde6d63da5fbb76ea477d86c5f9442b6fad3bc6c04be7552845618b709a350a1005be0514567e4b48d8cc1937f32f962fa8450541b1a6b97cf7a8197bb604077a0fc5e75da43f626d866fc72bc3c045b0e728eae2b5cd7c25c6171504e070081b591420d8251683d91b69ef95eeb24efcd2ea4888eca0dcd1e1606c35575504776e54e00e3682c416a25bb0e650889e75c395b5d3745fd76b003cceb78b100574f900ba6b907313b8e1a3e3911ee034323efe369cb42fb4f10dc9ddefd3e70dc2d7675edec4a31783422069eda787f42735733bd5e885bf5cc91598385b2f0cda66e63e0ba6b0b079e0f9eef7b281df74514de287853e3183b8926b8ab0050a4d1eda80b80bf7a8663eef022cdef9b49b5359cbbbba4bec5a8e755fb7a8e304d2ea4b9285fb762e8de825ef8989b6c79d80f9ccf627992d3bebe30911abed07562c3ed0df19d8078196705dc2e530d6168872697fe9ec82318f494d81b7dd00e89510f37fb753e6e919836f12619ac5f42922292bf68d04d0f669fc745706056de262d3ea76467ff4f4b299e0bdd5421f908c7312c2b5310e6bc37819009d0def1952e3c7d7cb4b5f230af4e4098e42c3fd1d0cbbff9c3d66aed4d385601f04912a00071e446191f8a576dc54b4a1843634de988cb5518c64e2c484eac5b409214f3aec60478be5655297390fe4ce4c569822c0d349cfb818148a5d3ad0930808f40d8faddb86bbee303eeecd980d70a6d326bfdaca3234f24cd1d5e94ea80ad49888b14ec6768dc5f1f2666dc1d0da9da22099bc33b2b64c5d757292399d057ef6cab0e919bcc72806b6be6c2de83f4a83f52f5bf390e69bc7c279c53ca20ba002ae8b978751d01388b2454a1027730b62e0f50b1052edecb9d4bfef2ea6075a93d40b3b241d26d26221e4c6ac078d3faa5e20267c5b90735fa2bc6a036200561bd27e254bc28a15e05ab65686e2ba2c87141f90606ed2085e8794e59a730eab983538ddec3eca8182d44d93212aa0385b7212a48d34199b9657a39cb6f9030e476432d2acd77834b896fa2667a0517e2cbb90804d3da16647255738e06c000c44f606d9cde9c614f02c8b39bd3de77239fbf71fd26abbf65aa459a20c340ac807fa8efbc36c879611fc9448c7b288be230156478b6a5fe9612297932dfb006d2258b39e6347e7a560d29c9aed2a9cab6ed1c4c02730f411be183e8746cc08badde9636618d17541656c2d3b29adb73be7a1d946372a385605581f7089930dd44d04a4b4630320d67b8300ad2abf82a09c49ce1dde83f5021a5bef2ef069079d019caf190f32e9b9ba45abd4819bfc0a2a088fa11f77bb34a051a041bb4004d082cd3a4a1ae2669eed564c0a5e1d1421617ee62ba61e97cea0f24f19572e021bd3283d07627d59673b929bf5a706bcdb54901d238c8a8871fb5deb842de60218d891f5250e89d63c78932d72c9996f017b528cb0a0659d95df403e12b88a0d11f2b408054408c6089f2618cfbec6358e8b50e321d41651993841cf11994407f09608a97ebeb79a1a02c4e92b8313c24e70a0e9ba5ccbedf26bd18d9d8152091e905ce9baba0dfd43c13d28bcac56cb659dcd5f853d465b9e065bd6d9f2420bf0979e663c432addb78f6b01f55b99860822e6ff78a697efab397ddf82a05804be12fc595918933a8cc6f0f02a13777c777ecb8732f8977abd928f4688764b0f4490153fda77c4802e00bc720b85ab986936bedac95e3ae1bc5c72fb83d8ee02b18b774c56d1b5e8a0137d153bdf16e39f239c31141a79962c079a664a47650e2a5a48e23e2a20a82f958714ba4b6ebefa43edf8284a66b7e9d950f9a38d5d0bed183ba5f6b3f328bc50c8a995ce513cf922a8ea65b9d62109cc35955f82de03bc498deaa2f0f013b64841c317dae3f2797eb0b5033f7c591de9c001fde03a055946124caab7773263d9d499b71d924e3cccfb713865fc12f88d7bbcc1d1a50bd01b25055b53a67fe44d11ab7809ca6c9841026b05e282c9ffb28c02335a6c0b33267fdb96528a4313a7a19a54ab6af0de42950b908236367b30180cbccb830f8445a270ba8696f91c51b3f99ab2b37a48c36bacc8b60e168ef7a348ede5ba0c6a7b0c86d53de5d3af4919d40e7fdc9a7214bbdd97abbe4eb5eccad9d758fa0c5fec0f076dcd067a1d699e7d5f71581ef1cfc9b810ec1ecee98a3542e8c949088419f3c95b2c73252d7ed2fbd13aaa3adbb10a6ed9f9e9fecee741f3ebb3890d8cbf83eb83b5351bbfa4787793e75c11d51b80ef8afb608d4857b1d6faebe408c8ed5f08cb77df07725e63fecd9ed721d16c3c2f6277f800434dad54b833d2070fb1b8d69b094203f78cc2312aacee53986cdf23c96d3181ca6b108c10dfde062ae92258e0929d0ee3038ab0fbb70b0df02f78fe203249b86a4e483a54929f07a1088a5d4a400efd984742efa00cda5d0a5e396076e1d0a4c7a906fdfbaf820623f29772932b9f26c63e3e78aae4fa79cf4b569f4ece90658c82eb9aa0659b0f839c86032aaf72ee5e834277308162f19985729af0fc21f96a3ecfc91a1dd10dfa511155eca952b071550c0e067d15d75a19775ac744ebb4ce7e913532ffde0226183be8fa5fe5fae160604e5c73ce26734541db9dce026f87370e1b1a18640c0d37bd305c5f0a63cc7ad2f5bb714a3f73fb6c18946e49272abdd3513babfe03c9fad33965b53dcb506dfc7dbf325c308cd83e360b3361cb156998dc79900a02fa852db74398d27038e6a805a7942dea87c529bf7c97857b61c81d02b638480b897d8b8ee5d6b06e1e0aaa3375f28b9b11998f9644f973345d6f0931d26c410e8b2d06ccb2d7be1f80ff41a1aeef00255c9511b731a86e845eb05164d3046802636d2d7f59c2b909328d574e8f84238db71010d421ea03d79b353f0c5ad32a06cc132f21d5c983d38cf59707d9df5bf44223a5eed0f6d93d3dd8280ae801ef019b271d4ae78beb72e3afdf4403f376c197fb3814a18c135e22e4cb7032166d0c6034d6aa3b26049375eac910719e377d9c73cb91fa0dc72fb3c2936daa74260e1d81b82ab1bf9261be6ee42f1039fa785071cb474b540771953048feabfc700782f355e06a5e57fa4cf64679b53cf3f917c58c0d3e7c9b7dfd23624baf9784027b53b77f3183510393ab91b8820eea9523dba15c9e6ced65e586c878ee379a0d7f1b58ea05e54a386f40f3621430d0d39454e120f7fd426e32a69a94c5e77f0e2dc1a019eaead12bd794e4eba898d203bb264c3e8af1b5ceee4872eab16e7a0c7d4f1ae1c1a1e97ab4d2b2e2b9f9862810fffc6c406baa895372c7bf252c1e09bac4f20651131dc487fd60489dab1aecb3a1a1fc628e5777aebc4d33ae6f70068c27f5e80c3c0cdac9f9a3d7d5dd423a23dae655222b37e00aecc00d39e40f06db09f83590612bc8a0c23ace281018fcb84125229d05849ce22fa24266852108109ebde425b24807c09a4be7889f9f6bfd151a82ccad7fcc71a73fa6f7b22b0fb2ae4d7930ab842ce89c6286f72ca3edb2e5517f8a2b10740df2aa4ff500510afc4e7b75c1f6211896b68aaf84f83f371b04b440bc0030c4df498b2ea56193035800bb9d69c677ac42400fac66b716b40672337786cd61ffef2f854a1b2e6a030688ff39c18ea36e684034fcd31037eb9d7ce15f23e9b750a6dcf7bdcf6158061a5ae95770ee24b4631f5856b019db3ced63ecbd912ac4f4418c3275631b9a0fada3885ffbc3e77d411831ed026fb4acda588f776ea5425930c5a347e8906804ed18dc5967f9c02ec6211df275518215909b3500327c298a34e3278b94ea5d0c92d47f8a911ff48af2bc8840803bf86f8723f496f59595adbdea5517fca80308581e5283f0fbd1bbe2b781e7921fc8fb528ad5907ba7d4c47cdbd4c67dddb90c29cc743d57a5f6ccf5386026b46670ee547e8a5a572706050aaee31cd7307f04298c18a0f994499bd398ead0fb966e9a2a92c8a1ce1069dd51175f8ed9e9130526af789bc4583bb52a30be1983bbcaf0477a9ff96d4bc4bd6574a2ed4dd17c0da471b0ad1a2fba6c4ed923ddae0e548aafa53228e298b6186008c0ea61dc850e29894f98dc0c244d7535873b4303d9cbda241380f2079b056f914447a946e405d0bb72d1286dccbfebc5231cfe4c60f86cf20aeed368d1b589d4758c5743990e100f0f00b23da52a24917fecaae9780ebf8968df5d2394e74763da1a4892ee033c9c73be5e52a9d77f12ec675216c33b58f29289978ba8d8c0074c26f11a1e0844f2eda5a7ee8d3913d3a22dfafe5d150791d3cfc8f5f5b8592a1c644bbfce012382d59f1f5cebd915c9e04e1304e2f083e45d42c5b67f00fb40d99bcdb41109e698966696873e672b60425d5cac9d8a8e5d77ae5476950afe5c635dc08c2905049761c0596aa2c5f320555e77bb411c835373aed3a8d64f988ce5d216e8c50e02111a005a1a7776bf2a76a11c9e47c1969cc670646745e7bad5cfed1d42430c +generate_ring_signature 23427a37b63e16df40a94a569475e086585f88042e3c6933cf1c7c089b041a45 a680bcc8da17f610deaae851e49a1153a41cf2a0e7b4df452b0cb0e06fed4fc5 27 76ef12c44e822d190d8591a0606a6246e3e339c3c0d09a3e4ef9b9f3e537c783 dfd8e238c9d58b0b26de9d6d577cb9ee7fb353d5e624577d8a7089dc7ba75c2c 75d01aa0ad629f58acb073ef222e20cf3ed9cc497ca0c0faed5328da617201eb 039651d4977f68975edd0044b8c69aef742b79b553550bbb89d03996940f6a65 2e20014f619c3253a8ef76ada8eb6b6a90b48d80a0442afb5c49bc677ca30633 b6172170bbe84ae154fc5815d4736578dd9d30e1e0436501dd36b09da27bfa7e 8cb4fd174df4dc7db427d121a19bf938b4e8aaad7830e9e638f653ef0011ab54 049bb382150f20b7205ee71165393d59fd4cc0d1a690a088387f8039867f5b08 a3ea8ea364079b4a02debc3fdf1b6d7a3791981efb1688a876b97008d3aa27a9 ffa5fa37dceb7570f33e6c757291119270ac62a9caf79c61a1f73347996b42cd f3d0898f92b979728369d0d9abe73aa690292800f3e4efb66de9544e95fd11b1 29291a457166cc60ec27368846b57c40876d1f573ec9af3f0adf44e478c1d561 195ea8899ba4e879cbbdcb3946d8158b0db04d9f147a07342e73089a7aaf5c53 0cfc96a760e8bba3e0a54bd8f64868fb7230f025603bc1d7517d5238f309223e 0beb14a39881918f54137e99f016277f66d022faf43dd0a4312de3611ef62167 87e660091dfe3e75636c7dd731a7aceef1aa6509a4ed0a5d9dd70b5ff02fbba5 4e1dac7838a601b8bf319f75d333a2488feb652d3f2c1610fb24bb66b5dbd727 6e2959ddd5b086f020527fb71362b3d902b030a27a873654c22794471312ee44 2c75319035311b2f58593503dc6f6f0f5c9d6a5b414586818f48452f5e7846e5 f204e336a2710b43975568558f6845fc099637b05cc8921e0557ef5121672e06 83210f8e7869415f49bb722f02ecca7b1a39aa5cb90ee5f62015c26b5aa7e23a 3ccbee824b4d6ba42ed29e238af0d36fb4213986ce70582e2b52e8e519b23c1c e42edb639429562afb90c78af73c0966e4a38f892a13e29c778af7febd30267f 1bb904406811cbe96212e0b34bea451f8fc119a7559e7bfe57eca21b43eb55c1 8414918482c2104b258f9095e099492c5da1aab97173d21dac8c083206ab739d 65bc67958d05c81c80d48855041e321e1e74aec3006c2cbdf0d9c6adc0549271 12fd3cd5de5c0cea07d92c01871687c5dabee74bd383fe58a0721e5c0658127b e3bdd2c25f7b73bd7570bb5c6d3dc6469dac206a4483a6d8dc173bd2b23a6f05 2 ac4b55c7d32736e7918787a24f0b0380964a919a33d017ee0da4eb2046890e02306bbedb2716fb5503048ec1f3182b65f1919a42dad2a46d7de705998b0d8c00b4dcc465764a0782f41c636643bf767bf325039517be584d7a65f12553844705ccdb3ef266c02c285ad0a0cda8aeb3adbaf94f2c28007454f51b69f414aed40c69787ae300e7ccffac14f771dd32946d99bd37c481fcf2c974c8500b71e5e90924ba74f82e40750c8f14ff624c54558a9358e82d0c29f8731135a296c526bf0d07028c723eb884b800117a2558da1f66d34753f43d3c81a074644ccb7206e60bc8b7a4e79c37c20e21a880642c9164aa532b95ece108f018a39ded89e48ab500b832b4ad82163649b2bda47002ac8054e36831a8da997ea155860697ed2c2f02e91d69f842ffeed056a28be2156a542d458d59edb1346b95ed1d78e7c04d7a0174d369a7fe03425f97b8bce2bb3b0d7f38477f05fb39a3bb1fc9e2f28ca27b03ea9ae5b3dabceb342afd551006a735a2dd1ca5e4cbb34918c52f20a5629dae05b3af34d91d73878dc794e19927e7ad88c70e3512d6e6874f4620acbd150ca70c3f8581df9f682aa7b4ae93db7a6b2a194a7ebde275e04c13702c26df94ee7308fc006408eebf1c39968a0cd7dc6df99933b0ca7724e9dfc7130cfb26f758120e9cd51382c2a509927845f7807fd06b5d5b0f6c2baefcd032971ecf225a4be00a55d4cda55ee6ef81e77cbb78c4246b30b794351980f591e71fe98f8355b08902a9c93fece9febe0488b9d1d0ad3b46c32e1b11d02bb83a45f56f1fb8b1089a0f8558c1e58ab09760751a63a9f1a34eb1265edeca6516bd35983f8deaa3c14309300a1b6d671a73261fc260963e438cd174b3b10883097c371a6c5b534a6c890f0107111e98069aed13e5dcd8ff3e8033a22b20a58356eaa173e3b4c5629b54049a8390897e74f28cb71ef57a166865f97ba50431f9e007a1b219d3169d0d09058faa8c4564ebdec1a86a22e8efec9a7aaccbdf30b5ceb73dc19a913e4157aa07be61fb36087ca3d5b3c4338254ae693b9be72125579e4be6887a675deb034e0fa0f93342171f10a6b301d12591347138a4452f08310d511d77b2c86db8d3b50ba8bc1022f59b3f0debb920dcfa6ad32829f1874e9440aa3eab0f49bdb7a1260b645e56fc93792ce94a830a2f2455161fffc7434bf6415a30311e1aeb2f908108f3886d6dd8fee7f359b241cefab4276258ef836503d7077e7de9595b474c500b1703ec7dd1bac6b0aebb57a5b7fa75270706d21b955685b4c50e36a2da0f290263b76de4df0c17114a3e909b8e4fbf2a37cb0560ff3a76ca8df82972582e790f1d67a5f7399d4cbe094d2dc252de3163fee75bd9ab17a838d6992bc3ca0e9405f81050902f13feb35d50de7d1e3ea9ff282b78df49a32c70410849465a91d00c2b86fde9714474081f9a37327faf111240ff7ff81dc5bdcd3d800348325e0d0d281385d2a07936cc43f3ee625e977005f82ab07cb404156902dc8de21f5871052210f5a15bdb2b8f6224d7cc88980631bec59f756427a180242becabe1dff400b838df84724e49938facd892481f18384aff124d065f15a4a16ef3eac27b630e3a76cf606855bbd0e2eb097ac4360bd004ec2feb7ef9e12cb7047ceaee19a70106fad75757b02894f017dfb2328fdc72fc2c64a1feee065cb781f4aa5a549208c1081f6a903abb309db7dde9a1fb4be5db81b80e93885499cfcf20e263813800ea2c9efdda98b3323a6eee42676b4b7577f4eef37cc8b06fbd7c855472f2e305b5fb29e247ceca85b4198e563310093713f6101bb1b2f03b49181f8d857a350fd197d6f216250b3775ba694794d122a6277f0e1b91cb28d853e9af4edb7f6e0950c6d97a4d8646baf4667c78827caf90e9f948a131e051b95dc31a1479eba70b3e9664e415344fdf746e8fc115261777209f8208a0892167e6e47383776c110aa66e892ba130e61b3744e719cf4b503f81920537169b8a9cb9797516d80bec0ab00c7813e985a970815ced3cf3d154426870a2e1f9f1a1c5151f25f54474080c8304bfcaf73d5d2e600b49e16a027f92830c7383ab309397b31f77c52a007e0f81ce850ebb25accc0a9d456f5cc5577469b8653a1ab5f2625d906f1ff88a410332c99e06696f5037068f8db6c96efd4d8d54df5b1096b06ec2f85ba1b1101500391ef6956d235c8b5c37aba56f7446f67bce0a370492ecf0883c9949cac27c021230ec9bb86491716fb65fa7aea9c6e2deecdc85d9074b7c875839f359288f037a9e339186e2e690298ad57c95662e2e2899e7f33ec32f06b74ea2c3fda22801f773f2ce3070338ace22c3feffa8cdb198c844fdf4e2bdc7ccb546f0405e6f0308f7ccc5facc037727078d9645a6c8193cde45dfadc2ebfe661b030d76bbf905 +generate_ring_signature 4a79e4858cf84189b1a787a39042ea6be11532c2bc4c1e87937546cf467d05d1 e6e7e9b18970c931eda06ce590197f03131062d3d1b5a6ad71ca1e5abcd18e3d 12 2fb5a4364780ab0fac68569071652025fd8b48e74aa242c27adef75f375d94e4 3c46616f02f3b6d9fcc70f74f9319547b6114a4b2af80da69a665b9edaf6a4d9 1c4d1f6b0f21a6b2a975e2a76e7c4c735bcad9430ae693d69b99db3623944c1c 2be94122483ecf69df0dd4920519ea1ce71af7036f03e27466ba7272421deaac 8eb99f028055ca92189f78726acd9b93ab452227084fe718ac4dab9e8f4389dc 226f4f356c64efa694b8311ad07d4ed444d940d98a54920d2d1f9fa687cf20a8 4bbfe2a4a7b779fa5336295fff9be27b165d9a01920c3fc3ff28d4861997c3a9 3c50a93bcc8b0ac7cb7904cb370d850c81750e2de67ac9d4a769f551bd28727c fa5880974ca82b96b3b64d419674cc7efba182815d23f8424e7e354cc3e63dc4 710410134c629f3565f3706878f34c8b4b049625d520c4efe0b8d0ff9ee02fe7 b0d02c0f6de3ec3774101beb260500d636a2a48c5be06472d17723377f82e0c9 7fc89baf0a1f51aec3e249259b8f22194c406956d27410ce5ffebca081fdca89 1d8156cb422b95597ac3b4ae844934b8e729b09dc95d288c4826fb2b3b8cc90f 4 a2c1ace08199c5dbc5ca671f9b7026b0f6804f4aa77c87efa62fe61eafb49a0ae78e136cd1b24d73c7e94d872134165f6cea6582fe278d425bd2df9050b8c802c2e713cc06358da730fdbba92dcbdfaa0f72154cfe48979f5d5e1afd3b64d7042ef74e7f63fe2b2b25a039e1351caaa91512d827c763f7445a8610b900a5890e70bfb237d7b5341090da9a954cf9010e26031bb8248327173b15b82cf87bb708e446461c1e7d1198f02dc08ee0408c7dbe648eded6846960745d619c0b2264091ad8ea498dfed073dedf9afdd884f807a82350b4b38722a4b2f80e692cd7de021ba71ca5760deffa13cf5b07cd93960a07ffad6f0dac9c6595c92115f2740b0ad0e32bfe715d7c0d8a8e13b421c60d247893ccedb9ac2601348c0c253865a206c86bee784044d1c92648f942b786fc7309143a9f7550c20f4ff0fb75cd3989046a054dd740c021aa622816b2546e565f05f78fdd7b295db143a81a038403ac08158cb03c7f741e32235316141a659e06a0f7da5ce18f6055e9f2fcd109735b09b3fdfbfffc6c634c7d76bd0be666466cba7b35113e561c0cc445ebc016c37901062ae0e1a5db644250e32b5817e4cbfb9cce8e5995af01905a95b23808b9da088f22ed2488cf7f64779ed7dfc1bc2f4fcca4bba8934bd9f17be1d26898f54803be0317bda7e600adeb9b4a2971c828b379243676a23947fca75580c3c8667400e922a8ff1356832ab1563287495bfe1242a4b2f5de56f02b9e2af1173e59570bcf4a2677469a88b8de816253633f344ba7239a8a628754821998322fb01ae30e2957a883295145ad4b7e3651cc008e6473d00f0af5ecbea249db9e7b10622505dad2eeaedd3481852521cfb0d61dad9ef797697f918e8744d2ecf37948a2a003e18b4fb6d0dd1e5c6c45b058178ef75d4a6467ea2013bdf7d5a903366f382906482bef4bc476b834ed11a854307d3a09d0d06937d07f47a11fb38a5120f6c00ea507eb0593162071fc570e1210769acdf2d19e4e7864c188286de498336c4503600beecbb9669fc81176c471900c0508c63980690f623035b380b942db27bf0d +generate_ring_signature b3a73b35af9a583cec3e5ae026c0974460a27f514a1e57863ab5b18303b49bfd e40c3300a1834002a79553eedb269f35f9d7041cb646e3e6db9fca5f153dda0d 10 ebcb749e504392105e194230671d48c40a2122d199dc6ae4e0ba73efba82bdad 7859f6fd20abfa37a3c77079adacf378ca5e510c878c37c588a9646e93fe8388 427f5fbb4eab74be396e86079a6f7efa6cf50d058e9f050686421abf936cb4c1 0d6be5c57e41a58ef7f994a0face6c73c93aede764b54cb3666453d57c2ff4ca 7d0a29ac592eccd5310a9f6ef5cc6ebcfd854b437325c38254981fa83a35e333 67d601df6e64415b9113817e05b525ecde13d8f59020680a700768f9df9ce0ab ca677493bf0e87103de142670672bab6afe534f83fbf59bb7efbead953f2ae79 0146d12aa7dbdf94c4c6c9fba9ed250f5edb2042dc0a6b6d143155f2f5352452 2dbac320da8dc26a2e4483bcd01eae0ad7748c28e4bb6793f182f976f80108ed 87db7f074e179a7998b44b864409980beee78a7ea3befcc7fd1fa697735562bf 35b98eef6f934ddf49deebc1786ae2a7c69d7c061a108ea63be86644aa65510c 1 a5e1c34d5a2386b9f585191ff59d95d5f2ab94ab453e71a94d79074eb9bfa00c448c4fd19738c9b4eaea1e1cca372b10fe7de4bb7ab6d9c14bf58f1ac47a3509cd174bedbb310aed91cd93e6fc8c71affcd769b5b9936c8a5849f93ea6d68a003252088c9f1a4fd27d4d6089584d61a8ac92eee77c52eae0ff3326c9151e2f004bcb4dd8d264e5cb021f604155df24f375f693e406e88f4e9e3fa935eebcac0cd5c2a37cc33c741bb494676c710484f00d5513828da123360e1d4b6427294900f746e74e783a85650b27957a0f1fa6ec828e13b5b702eea4372655ed569b0d0fc71791f51410632e304d3f4c9112fe821a62d836b7ce6ea81c2aa85b4f00d7040282f2d11eafb5783b01a0be4b591a1ad7c24b62274502ab446d896fb59ac60ba5c2101e9d0cf5854cf942a41cce01e2863a492b750c773d6a71e589201eb80d2e0141c0e5eda8f65c5a56b2c3c44fe2eeee59cdd8a2440c69a1f9340f98fb09c3b380fd7acb101c204a652d001d7329d154eb37ec91cc481b94db779f21eb049fc23a444f23fd53566653036f4392f1c33d4aa1e22455254ca4ea81f89d230f7a74c1e49016f06b0d3bd5a622c8e097dda9ddab02f6da4c650c499f8d1c820f1f25ae532208e46194c83b7e76a27668b87765863ec88c59542311d389327a002c3d6a3451c7ce0426a5704f312990c040758d0ff1309ae349208d4e412cba047145b28c3fc4a1219031df5b4f265215c2bea50861760319b34c0f3b73c78b0c89826b18249320d04cdad4fff35841220e9c5c26ec03ce569e9cc796bec87d01dc2abb4de17cb29d7f1c09a6796949e8a752a660b3c723bf709afabc9df91e04babf5af074d528775d583e0b247943be9c582251c902e973fa7297b4de0ce002 +generate_ring_signature ba2cf253cfce5ac728cc5bdfa9e02621cac271d7d640ef00c7bad3612f7ea07f ebfa9411cd1911793cd8c3c3751fefb1a4f26ca477020409303e36d624ba31f3 5 a8dd3253a8e5d1aed79cd6b19d6f8f006a6f755c8da0b20e2c9187fe203f6a18 f7cadf8be983339ac7aa073402ff1d5152c7db7a2b1ab927aff355d4ccb97538 beb5217ff6e8217cfa07a89e132171eaae685ffc7155e1349bc2ccb6289970da 6f7165dcfd5f3310abed2c531905a5c69b118b2d708ade5721f0a452248e458d 04201b3245cbf0b7225718ecf5aa71db813eb5ea54b21423f93fc9ba202399f0 d5d5570a52159caf96724aafa56bb402e9340bc605d665b4fb283c87f3404306 4 a0f7561f9de2ac6941281dbb0be67b4dde21383b79588c6994f0912e1ada5c0571bef6138c8db1fa6741de3dacff01d93093e3acb8f2fd218104c5fd461b750d80bfa0fc47d230bb2e0dc16de4a585d64e8210dde3bfbf4c57748f9bac2e2609253b7d0c83901c38aec080b2751733cc2c70da2e0a60bf38860f0a643c9f0a04ef65990acc1a94470a719b3db9f37eb8bbfb87a0cec86bc827c16c606abad10a813caf397627ffa3d70ec8729e7b8f194c8e79cb63e72a628204a7dd96db0b0fbe815ca9dfd827e343c845b6aadbc2e99a7567bb22f3c408b8fe33722dba3209b04a80d481492ca510e76afa5b83f5d826abb5b16ed1e286e5d3d9dacb6b250d35f9829ae220c46dc8b9d3bc08660886249660aa5b21da6719cc00121e223d0fae7b6882d6d319ccbbb7dc82b774fdf02766689dc65abff4523932c6ddd0d808 +generate_ring_signature 07d546720ad955c6ac191f0ea16e5f7d843651501ec655bb7352f0cbfc540cb7 9c58ecdc8162a18d21f31152b55b98c21529b1b918752e05d6440062b320e36b 34 c5de097b79f066a3bae592e76e4f681f9f9cbddf2d1bb9819d8988f12e657b28 3f2e00f51eaf8c766ea0a750a72aa798c3323c630bc6bbf66796d9eb1dcd7814 0108036469ee5180b39ab609a7cb4a4ba3f4c6ee1399f5a5475cd7abf56f6562 c607fb7f081e02725de17473c3358cc3863eebc8306403fc097d8a8cdf56bc5a 358cc0d47937f554bdfd5cad829d6d4c04bd7b4bdeb8138387409f51f5bbd233 c585912932e94cedab2246ef0f35ba86de9ac8816c72f39fac1b74ec849513fa b967097a873c451309dcc10c8a7716802451bcb666e7aef43c23c4886e958f59 edf0437f0789c2f1280f946e027b9e9d3cd7a7450b77c0c29cc2d1a3a4048e59 6e96fcc4fc42ec8113e21380f6e34dbe8a1fc39c8ec605413a6e4ebcef95a5ee 3540744c779e95429aedc594ca3e7f144fb36f2354c82a215ee3921eee58a687 eb13aa3f8418b863153d0ffa0a185060ea881d0bf26ec385511efd7d5af9a9f2 7f6f7a45c10655b51e31b6d8a8d718eb55be8263be321b959bd31d8175067833 403cf1c69bd4293df454439957b0b82b4fbc10de841225dff9bfe9f09d0b1e00 8e79d62b1b537f5eb4f56e876c25c5419aaffe859b9a281792009937d39e3a18 2c69ecd10e42299b2775570761c7f256084b0ae90d49e77a84f1f728e1ed9eff a15aa70042d5321cb041d4bd2204a7b3cda423ecbadc33aa57c4bbdf22e9464c 8a01554c542f2df7d9ca32dbad3ee49473c064baa299d3b659f30fc0cbf19b9d f2a1a19157f813c81ba6633b903d7c63ef3aafdbd1001206e1b6b1334260eeef 577dc9dae2487db1bb743f56d1a016e8352d2eef75b8439f7a0b06d2484e6329 c405f1ded4cad4af0084231859177e971c5525f1bb1854113ad7c5bc62e21dda db561e3f680279ff441e82c2a7251ac1e763ca0624ea03eb64fa2a6ac72b7b93 a38531b78756f9ba162cff58ca174efc1d776e9c3375d43a366c5da19e854a6a b12e648ca8bec1098b935a509295024ca47bb4f2f0f458cc371afd286ded5bfb e0e9d1e673bbfb6785f45e855cacbfc081725ee4f9572b152976c70166ff6f92 be23ab7ffbe883d989c97c708ce49e6b7847a4b42a78d7a2a122de8e7ed05aa0 d5a6a40407c2aff52cb6d80fe6fcee2cb265517744e655dfcaa45e9b5b159e3f 90e4110de219aa004caa3aed8b67108ff8888aa4857c54ce6d7191e8b11365cb 9dff22c48301e57a059f72497b5b04695bc6f69ed27d12650b8b5e48c9f8f8ad d056b92a46fb72170ff6d95012156b3c75abc6b19a70969ec132f93e628ccc4f c6bfa573278be6a28702532d85b56cf3ecac307a53474f1599bf35ce0c39f692 cbd889739f84e045de92495e182e47d1b87f2979de5eda0e24eb15078756ef4a b56c32d9e333345df49bcf546e4b7555e5820429035314341146e2eb555b736e 1fabeea644179badca2d5a7660fe89f61ef1e1975046490dcc8ab54a35b2ea12 8576859fe085697bb50b74c16b8f8d75e00ec1f6bb6207ce506df6b1bf8dd8ef 85c20f8768a15b7819a3885ecf1c285cfb1b5609a93007fde3e1964d014f5405 13 cc87ef949cdacfda5be3b111ecaf02725c1c901f807bbba49257b4b8d1e35800f34241a9f56106971af83837b87c6f95dfcf7cd2e55ba0d644d41d614a0d5f0647246a1617529e4af6adc449c7d3fd0b599d23fd0a84ff680090a51eb821ad0c61b2cddef7e79ec969b4c4802e709b4bde35d392d3557cbf6e6de2502156b90f627082ece6744258443f92e5a055577754bea0b6a0c0b44bf2c4d7464882f80a9eb3443b52a8d541fc0dd9b4f1d0807b9bc5e9cf6c79672f3a53960fd84ca10445788f3acaa4df522b14b7bbab50e7b0581e1c297bcd0f7f06d43712be225c0fa86af386e1117928a5df4e73746cebc724db20ba3a1a835fd96419ecb851b10076b614f68cd89e3a180b9162482a7def161f48fcaca082749bc0a496ff3f670e645980ab312a193b5c480a684edc9febf18000bc7306b02f0a2671db4bfdb400aefa3d9e221e000cc0c121daf7977005286baedf84fe482faa6276f9816e1300ed0c574792c6539bbb812e0e9dd9414ec9fe7e752b1ed20f4e8e886e29514406ef65cb982a9e2bd4227779549121d5c1863bea2eb2877e656199315cefbe6305c2689dec309efa27d6bbdb5b9bde168e1b747b7606215bdcfba9950f31a6850ad45d4b623b30b09a350cb710adb7b7df91fd7ef369c0f5ff8103d1d645e9c60c2920bf74cfc36d58fafe73077cbfa74c2fa20361a8b24b44835f0f6d2426e90beee9356d310392f802c1a34ab52ccfb3995504546eb3a9d8b6c5b8f977f3180813188bc206e33c2ea3f8e4b43937f8d6d93c0f3db397b0486fe70cea90b3710200b001e292a25cf9de46b3462dab637e9a296d16d034d5ed8d6b5d039ea2670c071982eafd2ae5e692b0b1a66ecf016193e2a7b5b7ef114cf2524a4623251c06acf4780972215c5c375a2a2e4bbe637f9ce7858a6fe084451389be7e4c6f58081ba550c3287f36974dccb9433379067e7626b7ffe231f2469f299dc645185404dd04adb1228d303c459ab917d1237d0838d2171b7b26161b8d7188ff18d8bb0b148295c0fae15b386324599069654d55f03cde21268631b84bed7e1430f7ed03919a1b2d2aa3caf0ef5f99ecf58ac6d68e7f4bb5d2de8daee1a66c9d7c445e07af684f2ef03ec401f9f0029a52dbcfe37bc846eec343eed97ae8862a3d385c095cf04e5d79027b3c44ae288f3ff11b66c480388048a49739bbd9e99f059dd30f1f923b110e21d7e0ddcbc7f92a68337fb39012ca8d6948aaada206ceabe3020021b83759262d5e3a12489d405b2775e072d59a47fa5c7d63fdb9731a77438a07298e3093753353ac9223827fca0599a725cad6c8a1f90630fbf50dc480211100240c4706354ccaa03d4d750caa9785276ef60115331dd1818bad57b45e7461015d3e97ea37e852dfd8202f17b5d9b4ebe72c5d3bf75080536407786a042a1c026a4032e5d1b95f849bef992006147750295329c7892cde5c8938992f0835c40164722b07b4f22823d2fca62826430024f05a251e38cd26dd201e9aaecffe2508440ca1d438be15b7d3187d4dc7e8cb8c6798a0086a81d8129d13b8be9087ef0e8ad5db0979ba37249f8bbe11b22a7ab8320550cd7713c94d36c8520dd8758305797ae9cb593219b6fc8ebbaeb3a1f3860d041e79b9f733756f463cd8c8adb10e8d7a1b9ca71fd87c0294d7f69bf2167356e10c5b5c2f490b4ec1d68449a0b80438b1e5d00f74c31d342aead66ac2a17578493674b9507f76a234ba886666ad029355355fb95489b89682e35e9ad288de2beaf33701e7bd078ec8e9e0061fd20b2e730218958e2479f256030c68c1b97b8deb2a3e2d77bccac3cdd07d8fbcef09eb980a87aca6032ebb34dec70846acf258dbc147c0071def9da0aeb3920ce90ea268ecd38514f199b34574f1af45ab62279d70e15a9b2007fec23c4ce336440c965fe7e2728bb8657ecfa8a13751c8bb611c01d11e9023695e87a199e862270b58b536ee8d2f801a9dca33d7f48854fb432cad6923bdf95af59d4d3fb35b24018a181b272c6206044168b0719da3f473d05c2a7fe2c0254cbb3a0ac51990700fb2d2633f7bcd569ae7146c0f1ecc9db593c4c37cd2954357a469967e393d8b00c555a071c6c057530a56474bae4f3e500d188d3b13f6143181b61cda4baac90d59d0f147a4b628a606d96323f4784e9b1e923478741d06d3cd2a13a709755501511fc6df12c2f735db6ec40edd9f3e34ac653d2710b2d256baffda360c98350be6eeb698a242e14693bf8a7450f7a8774d7aa957f7cbf01660c636141c657d06c19dbe686b547ba6d62dc3c59ae3f5f1d967218aec23fe6b494def21115d5a05bd1301a86442a380152c30bae6d623eb85f0dd33634e231de178738a5eab10024f03f7700efb88122be88b7e23e5bae2d7d9ac36cac0bf308612892c2a09fc066c396ddee1752737b7a820bcd35a681ec7759a3ccc9c0b70f47249fb9bf3640870abfd5345feb1f034cc276f3d3c8b250905f1931bcc0703a66bf6018787520d6894b58beeff41777a509076b68216c09cd6f4a5a8fbe9045500ecf3487f1a003779919555001e5c1749736bb73095a1277ac1acb9859ad4031ead3c7c16b30bfe9e82574e5736a0baacf56cbd6dfd3fcdc2aa3a0d4010baf1d22fe4bfcb4b04dfae9b5e7e49ef88ddd959c23327ded194035f07406a21830f34049db6fcdf0047236fbf76defeca1573450e8bb6f519937d105520b854825a04d150301033009a17f81175d24703c3e5da295be2ef589a46c1adcf3dd0e493af7900241ed805b9f4f8ddb3250abd1c698b769f00624e85c48f95dd5fe0726f19e2c1f0515f08aa59068ffe6eec5f3862536f41e69bd7bd53a5cbf6fd67f5dbbfbeb7e790e3040dc97c7cbc0e3f2d574ddb27628de755d6f7af504f0fb9ce7b2dbb7b6b45cb04ee948cd0faabcea3e2d23a119c4625abaa4231814c25ed15e16d893076e2530a5fc5b2daf9a492dffdc587555068c8efa9c36d64476872e70be513b06638600fef12a4a8682b371ee60ca22b60669e462371d04ef987b4d2092cf44f15126c03 +generate_ring_signature 0b1546a77f70112073985f8a806f191e83e9d3cb9f19e41f15a07671a7d7d173 0ac40cb07f49b58cc8d7c8f9eda97937c5cfb83fdaa0e897e4aa8507cf55f2dc 56 fb504b3a12b0e8237788b91816e395f566762e9d85e276592b9f42eb46b932b2 2de49b9fed2a1379e27ff9df1387c5704728bd81a61381ce8da4d0190e310cba 4b42cb7ed8b175b0d82c6f6577b3c2ab65bf0e0cbec21bf00a6d0a532d508383 6996b0dc98d6d30c76d3424d16591495301a6981e8a4afe7b0b7b0f1682c0c8e 8c3d881ee3a14b52541c96ea7ba2200a17a9ae2123c15774efff022603808f16 c7bc7b4e978d13bc12a50ce744db1c964ea624d53d57ed21aedd761fa9e5dc56 84230301ac02a11ef46e5e7dd0e2e73faf19b28e3bb579374a805a3a07feb800 0f5062ed757ea9f3a1d9680003733cd69ac3077de75e715da4c72774fddbcb78 da0d99811f920053e451849dcb82c6f9c5f6f2e1e0435a52265dfa695289bacd aaaf445a2d1bcf716480c3f552efb0335d76633b4ab6b71ee6bd9460fc24a1ec 1f7884f7dbce7ede89ebec68fbd6765b2cc92ffa7f8c819b1a98c86c0166f142 5e1592fd36141e29de682b90ffd13d68aea02e67c8c75e872ba66690187fa6a4 78f92af4fc0e7fe8c117f1dec9d225cc1a690abe427f626466ad5768a1578932 5f34d7ff989f08ff0839d1df276a0905c9ddee265d652e2be032f59b86686d95 7525ee1aba8272b610df2377c0c275202fe06cc0589b9213e70a127a849e198b 43170ab7baf0ffdd0fe6bd391dbdb7e2dcd1b89707bb00dca9bf12e5025b0fda 8b84df55edbb4cef851c9674e435639a0b147456333b55f2ef083e572313e347 fe394491438a5a7860e525035b70555caced54a2ee0d0d7b23a85abb6b8c6a49 682a71ed5b369424431725db66925de5175d081d65f6a4fe049a5748836737fe 45ad3881f548cedcef87954b2c02363e0a4571bba5c5462d16bf5ae4f2dae423 8c3947dd8a8959059543c20250f6e83bbe841ea641759cf6f6cf0da204dc3fc4 11f235ed2bf9246af8ba9fc067374693fd3150529e05b138dd6b2942b7b8ef0c 57a1abfacc42c7ec4f8933bd93b59fe01f0e49690621379993bbb03ac100f036 0ed848133ef3457e15f0977f10a76d7e535b26eebf9dbdf97264a964214a05d2 fe282180e425a1bd0a8b7479be259067de45fc6e89243f0d22c7565148acd310 81857e68f244e6d5bbb6cde0c7ca8e5b8662870472f78cda46ee11f21ed85b48 545ec00ff569c1ed27563608e09719ec282f47e8517ed95bd1398ccc0f7f0404 015730048da5e50a112a705cd3110fa035634991efda22bcedc9ca6eb6940979 0e3c5354e7e30d619cf5f89a31817892507e64ef94dd9ddc36296da893c60389 9f16a094d0358ef39bbfae78c967677fcb3b701245f66de2faac17fbd9a50b3e 3ea3265959f6857f1b3de569fe573268e3ccd4f4fbb3fafe0b63524c13df0375 1b510080c98faffca1796b5937611e55d2d91ce491789a65df068ba7a06e2fdf 2d1f63f2b1f54755b4a3d328552f79708a3fa6db59dd970eb068845d8536c432 d9f5b0a6f2f92e8be14dd2ce300333d20579d3fb6f8719ef8a8d5ba5a3e72cfa e4d7b759782849314e443316bf297f0ea63370eacb54839ad9a377a54f72be32 be3341838a0f2bc12009e688ca22178361db99df8060bb520740138ddf5645f8 11d8b6d32bc7a35768648d631e4d320ea8b56e932f1a2cbff0d40095fc29f80a af23e51d53106147d060673d7fb4f2a47bb2ab72b2cc255797fcd818f5135220 3ae4c97b960f65673fd5e01d2c92b3220f6b285b748a546027a8b1bf59e8ac6e 76d0434adcad2173c4259116139ad0fde9b2ae309c1c6e332571794cb588d152 08d4b2c32fca4c8879102396427058c720babe1e39b90279bf202931a0f621ae e8e3cc79d0e05cfe951707e1c903524084e4aac4c30f9179c0be50b7beefd7ee bc9c3ed0a2c25b1e59d70d738dd9da4ae3fbf7d535fe58846d523faf47bc42a5 127d126506b2c410e2c8ac9ce472b1d261932e3fe361dd90880864d003812be3 c894d10d2ee5c17280e3e53c026084229c4e9e875cff20d608a1ef71e4573320 02b412846cfa7350e024294beb8fdc1e2587557e8a7b4452d4e9e9eed14c9702 c5fea97e5de8cdfe09a77f6f78a302e9db64df2137117a848c46c79079b6cdcf d7d0505214530ea4cd905270f664d9ffa08585a7ad28123156f52bed892f5055 fc78f270e04a04f38ecc4b754ed926fab86d7fd5cdb959e91b9ad15ab5677b6c 6fa027b87aca6842606c8f8bc4159221eb056c00d56553ff60884ade17445fae bf5e94eef26a03762c215cee6dd95140ef2ec454c2faa05d2964095992a47705 29440b94f3701df4472ac2647af0de415c2fd191c23dbb8b8f096d3f7ff490ff 6d6ef06df5e113380e27e8ea590b8548c81a715de4b54dc9a5d35d1dc45dfee5 c8ead1f8291072758ec6bf2c68a6d31e5f18a9906b3f22b9de6541f4d09f4068 179c75dd86d37ae7ad1ba856d31f36d6a0089ee1a363c540f0676e8dc727be9d ff186cd81fc7dffae43c919c60cc0da74e722d269741c3694c8ea925ead329e5 901ab283171d866625e3b45fd7fe13b6b829a44224a37795a61683e6addd1704 25 775a837f4028bad9749b4ac9eacdd99bd4b7996d953b5dc21ac15e6566665d0e7e90b7de500076c68ec74ca34305f6156e7e34017145320cca59c686eb52cf05c4d60d3d980bdee60897feecd6a57f0bedc8d0b7d7ceda22ab06a4a9ec0e2e074865be89e5c547bb98da5822bcdb92a85c98a2c9aa2eff5bf21f212f8cfeed09c1633b725947aa069c4d0842ed007f9aa8f3fcd3df6a232fd20dbad713f47604fa2b387df7262aa30456394ddd3d4972d7901ba8c54d830f005a87daf3988b00733bedaa0a03868b7cff21b9f7a7b2c5eff79fe08c249f7a119868236874510ea0f2e7e0383c97a7e78f43555d4ed5f1a81191fda0533c0d0215bb225886af097d07312ee870127d57dba00a4bd7e5ec165a53f7c916430e6e4ea1f715a2e3018f326baea21cc4cf50dd031e588bb4d6ba5f819b46b4d23cf609ef0facfd3c04650c7acb0249389c70087564d0340eb8cd8341920ac71ef7101c5d05b76a0d04d6c9e3b7c4a609a6066c408127693181288fe3740758bf4c12c0d7802188a40d8c8a00287044c0b083c3137365ab72cd2611abec4b7a7b800cb473250ab92e0da194616bf7f76bf5848b0f64b800bc320cd05a8273cb58c0f06dc1a117f39c032114dfb4ba47c7fae3bc8fe012a9fe0876322330aaf5f60068ad44c4beac2b0d7110adf9b21a3e5f7ac271fd17fb4440c83a81f16be440215643977861891b0f34da67ae15b691339c51bf8fc0ab06eb480de096ba989ec346b98ca5b7cdd101d0371834480b2ab2aa759fbcaba7c21c44983d51998fd76782e4c3bacab6a409a9ad50b32f69987e9cb1e273117d2f1c64f540f93477c346135a7cc8256446073126e28ff988849a5127405768b21128399f0d4bf018e3db08508393a1ed8705378e795153c9fc6e99cdc69f0cad6efa1b932fa7a317e6737e9d63e17b87c1063577dfeef974e82a74cafbfa84f1c67b0b1055ae2fa613659ff34a088990ae035345fe0285439137c03c72e234872875bb31baaa249df56f14ca0bf0b9066a0b1af02147f735bfe562d207ff3b2a88e91f8f11dc5ee6c0fd769f2b736159bb0fe36805a6780ec62a4dfd121fad7a6ef5f4303302f61cd421bc54b58dc986010c550ad6ff49a1a4b818547f5550e1b132ad887f856a60b913aaeb866e22cbc60f31649c227a5597dcb65a7e391fd2996e87c72affe6d5693a3de90ec5f8d64c0167ea55430816f1f85ad67274c235bd428b0f1b3b76185167c4b7b314f39b3c0160b63afaf1d130495e677b1de3865bdcdc06d22dd14612a764ce8dc71cfe8f0ba4026efd5f13da7368d45a8879a4c0f06c2a290ce3eff4fb79465d3a0bce0d0591abeec4615e0005edf47bd6f73ed4935478d8a52974665429f0d464c610270bcd1485a8b67f7141723e9d4605fe047925e8374d6306fc977c41c1473250230b25a480cb1a318be8cec94783d0d9e8c5c1ddebc1ce78b84728fb36488ff2020d1d4d08554d757b7595fa336d0cde8191f50e471d219e08df5c33293cccdd680eb34f81b5da93e1a450f0a3059f238e4643ec59ea19513d733800acd83dcd0302259cda4a13932e4123550795d63770af8a6533568c78d288ac95626d77d4060e0bdee47ae8db7a3d2336b495f9a3611d69659d5ca7e83c3e40dd1286e6cd040388a27da7674891c4ba96d02186c6ff01564c927c691e37e0056d45523ea1db06765f8b6900b11aa51fc24ee8580496a5718ff21ae9f5169575808d917bb0b10a2076f2f5ea019cf7602051e9bba0c0c4fd18a5ac79b1741fd5f823f6de57ab0e5d8356634c1202c3c1f8ca50fa8a7bb64a286a46f8e322c92fa64c93c739080942f261516a1c7d6c97037d1dcb5f7744a6fd68776309e23fcc4a8d354a658d0d67c88778ad32280f0681db5101493cfe2b73b9e12160b73ed918012cd076ab0dafec70b48a4e8f8a3ed9e46b7a4d24f3995354247eccfe86f1515da97d226605e5e814fd7e2aa476364a67c34b32928e97fb3184e1bffa18327a6f3f087de000ba044964f187570aaaae6ecab02dd2155a4b35d9b45afe2d228fa6b69d72e50eac80b385cc01315ab733e6851cf7168af46bf7ddb8f97d2203d2c7efa057a9092e3362efb0ae7787b86fde7aa61d7248ddedfe906259efae02953250c2502207f336d6453c3a30ec8367a796c44102f82869fb1d8cd15a83878925b976db46082ce45feceb7e5ff50cc41f78318bfcdfde684c1e5529ecd390b15bb3b2e6b402ea4a5ec73a782a90dc80e8aea5fa5a91b0c72a1227672413704551d4ddd0b003ae1d54c355890e21725ee6e8052cfeb69e49dcd512a78a41fb6bf36b920e0c019e80d45da5d6378508cc8dc70a6a332abba95313f3b19d245e5087f51bc4af08542317f9339a58e606aa2333f20faeb15ed86e20afc3044889b1a09904939c099525771d94d84068ed1527f387fa7171d84a83d4ef28aa6a035ea5062e942c0805a161f960a680cbe7fae0411b82699c4787ab597daf6b280cab430921d1970dd17f87fc1626c904d558f2cfbc74e8d9686ff3a0d05d8fbe693934f26b9aa000fb5ef204151bea5a8b39cbdc9150f3551d3d2cd9de389d4fd4db7e7aed3e8d063f461d8162d15bbbe55909cb9da4e4702413471cbc1d957357e6b29748a10608d021f14dcb7ac9761740e169b9aadfcd4727b4c99ccf66c00b41f6c189ac4a06eb96d0f2f3f97825811f42ce789c07c9bc02b24e554aa471559f15f87dbfd601753fa7cce2dc4403e22e365c9bfabb11ad394d041d11c4fda96031199c83160bdef56df1066ccf8bd41cfe5eea2affbd596ff975dc1eb55d9524f0417efebe007a15ef47e71e9a8abbace6b33cb89c40e532219d8f76f51092e9005ff5332f0ada57076e7d0900234eaa853c5fff469e14cad4c404a721900f8d9095c2cb2b00c6e0574787237836ebd0ce53d898d55a967015cc3625d5ff8e76662593264a0bd5b3b0889e1f949569adac819f53044c912827e9eabbd2ccba473532aab815065fce040587740f7f56f79aaf4cd31bd12d692aebcd2a6a9ee9ad5407e65eb50d722d9091d301b49f0eefbdfa3ae7ba7e6e516e940970e125e8a963e3bfd44e0b0b2ef05799af592610f66866f375da52e0fbf9f9bd75d979d6ab7e95e9a71706469fa2e5a12ab5d788fba235a2ecf8f6cd7d3ca4d4b99aaecc73ddedd60afc0932a62d81433e4f91f9fe6c197749334f37fba39fef3bcbc4ba72c036639dc309fd48db5a8a41b4fa128929ff9685a9a0a6036b816590ae7358eb10af8ecb9e05e0a311397c03b0bc5594a6f1a94fc2ab3cec20ea4c7cbf2dc462aeb6372e9903392e9798e81b7ee3aadc603195cc5ffd7b764a0e133e22b9288026877196e909c7718317e03a76cee16b341f769d3666c438184bbc22387f6273e2d7329b460de2a40c00b52d11dd30058a9d49aa70b84eabc97cfe8a6c4c7424b52a4cec8309d8e11866f197c0b36cf097a5dd1cba984074194b2859fde7afd8bcfb8e757c04a203ea3dc101a6aad375bb151eac1b36461b7d9ed359e47e462d1083b4eed709fe77aa12c6e98be0368fab0fd09b0c7e8c4690ce708a9dbe60f2fe6ec10532025f48755e1c8696373602b7918f54c954e1dc8ba0ab1aa3cf78e7f87906d47705d8c4e378a3bcdd3216c944c0d4fc7631a31bc54103481955d59f20bfffd882014bf07b8a5600ffa46baf94023413edde9ae24b2e2b92fcc67714795230405f00473fbe45dd324a7c748e845cdbc87729b8c85631221e83e77bfd96dc23549c011ac0c9cf6b52d63b0e4dc2322ff9dd4c17f300737279bdf178f4a9bea330c30dc7f6b3470d81b338fbf73d53585612128adb9d86df73703dc8b57b6def5fdc0395b4a8861604f7b927fd6f19c7f54b3801c62ea0e282c25901cdd447fc2b790612a1ad62947946ce9aa32cdc4906557a1d88610a078635b3d7cd88df2589880aecebd228623fbcaa4a5b8dd86c1b4b54cb6107e1408b2493c9eeda4b89ddde0e96f832972cbb1a69998799514dfe2d21ae7a00de1cf19c6f19b30652857d4102937914e5536175e3430c9cde0e742439313a6ff6acfc8a3e399899261291a805a95e054eb0d0305cfa398606e3ba6388038780c32dd2a9e34629f77133a4f50437c1a8296128ff523fbef6f67f0a0388d43839b17bc711fd19cfee42d8d2fb02890f8f617fff8c0c74c7edd7c9f1757588038f0d7f2320fa65cc02c5eb18440049f58cf564456958601bf33af35e802b244c2e0733d11c5d25d2c4a11844a808495a978df82c24ab7374b0cb95fc2cdc22efe70744472340dc650d7ba81fa90ea0fe27c6d86aac4ba162f80c81dd51a5b957db08b0ccb2147077e5fc39f5c805db08788241aeec6ba497a8b09416395b327343ad86a4e186a811adae66dd970ac3e9323f2f639b5d9fa9ac20beeb3c2e4596ec5855c5fa0ec13db230ff51a900f4bd76b44541c835a95d52a1d97732773a735b343450a013333e068fe8853b07111a2ea5e48ef04b635609b6d44701c8d63de27bd64f28101053e3de0e65a5073bae8c01bd3e89782df7d5da58774f07c919c27ac21dc7727eac824d2491a00419c18764ff35e2fcec387bdbfab71b50c11bd1863c535ea9593a2f9048dd1c0751f48f2d62ab8a3d383cdc7eff66a83eb88d3865a037f5b6416a63f6cba554061e5f076f31e448933e066821069ffeac74a8b1dcd20dcc4e095f4ec29ffaf20d3ee9ede95ff2ae4bd9dc8a615334ca5f27655c455655776147baa73f58ac1509d659e1c7b3800df8871f17db7a440e87353efc1cf0dd4e13d7381177a2b3b50f9dcb17d6c3d178450f4f8369cec8d2e41584cb9396a95e2cacedca6cdf45da0fb37d8ac92775c798407d588661cb3e1848da4196d29d6a1e39c7bdbb1fcfd20f20e648a526f184c339e5ba2fcb97ccce3746d45bdde60f98ec978c5dbb81fe03a91e1e7ce570c555f270c390767b8680eef6171c1d247728936d1d407699ea0296eb0b6d2f1d11e0004fd9b918efcbe13a16bc5c0ccb9a1b4468eccba6a7760e +generate_ring_signature 246b0d03ba218cdcb8142ceb5233708fdc1f453f1a492cd962e0dd9b191ea00b a58a14933f07ddd1da220d588cd863ac95d4745b46b0bc581c8b608b8aa48f94 198 0db161f672914d04b3da1ef8fffcdb431771826fc0c197e8825f2b16f0e1ff2b 6a77d52d3688f88f6b8bdaafba20b78065cd9a9fbd05213a96a125ca224b8eb9 e2ea7c4d07361af255042d42a83fa1ed8c49a9f454c7a8f6953ac4a25efd7803 fd944b028ccf14871fa10383afe9e30660a86635d20503c3001960c37de2e69a 61410433de0ad6345b5991355caf4432ae0f6d68de7b8b92fbee59a8ef3461e6 b2a13b81e18cce2587150264dcc9a4b7d5db29199601ce267381f912f3950b7a 54e61f2d4c10373f8ea74c8c262df8130e8d9b92529d9f4010822561ddf431f4 243e9f19ea8dfe283cb4cd1532744223a9530328854c771da9815e411c3ca262 f7c880c639b2cdb9f5bedc069e18dd5891428036391b3872de804d2347e06bfc 7899ec95851d38036fa9305b0d06221227ed571c0f488f99357e74d0e97f2f41 b55d7ec0e21c1b1c397775e2877af9d50328d9bc1d1d9b41075b2c82e59c60d1 86da65cf84ffa0efc5d851f16927ae1ce5cfe37f22ca1ba6bcb0b0d0eb64e5f3 a0baec146642142849fc2bbe21b3fb8dd36c5bbf4c4c8400a9a718732be93895 18e7f44ad2e9dbae18937a04deaf986794c4d1de98423a0ffe9e838505218ea2 085fed1c67c7a5446f05ca2258b1dfaf302d2c357c5626db2f6ff1472be6b789 9a7a89bb811e02200372ebe05f263dfea069011f51616bfeaeeef4d06b471123 c7da7fabfc31f636d3357b0153cfb6dffd2ea29dbcaf79a51b0c8d2b9ee141cc b49528744efb738fbd350f830e7c94d14fcf929490b1a7a7ebef9cd3c0506bec d392408898105d4fa4ef7a113c5e51fc9ec12e73d3a3b681895c5d1f8d64f9ec 14c14d0a7083d434ee67caee6b865733aad7e1c6b53260adc4a67ae429001630 d0a66f5a0b9f256542b2f47d20d9ef0c9f0ce902762e794cb6bfd0223bb03309 53640b7ae4b07158cad551f8b87080c9665cad08ffae960cd538eeb202566880 333c1d4edb0dfa89b137577862e6280720ba32d5a8985cba281e9708a350cdc1 9c85633679a683c2c00a653dd983fd6e6939e9a5b3fb1a91857226ec720de3e1 bb1f5067b77b9f97c17f4a94e9f23c83ce86290fa675f0af3a88bdde6a41c8c6 0237b12838ac6dbae1e8129a75907939d6995c2823fad75c981dcd983ada76d9 5222c496e1219b242efb961b9061080da46d7184ea84ba3f443e5f56e6f8e301 1a73f7cfd54d0c215775175106a40c23a418861b0ecb10d9aac208b5cb917dfc b2ff34e86f46a9e541444950f04822c87a9f3fed09eda2e354ee00a25adaf5cc a37896dbb89f13cbab665ebe009de96110741380b6ba0fa3303d025eb2328c60 2e123842381b4b15f6c08d5dce5b7cd375414fd0309fef0f0234da74f928545a e594b714f1b8cb58d6c1784233d1889c036ac446f4a109db65ad3e504759d3b8 44e1e6f6481996fdccbc53330ec84315b23f2e6177a04a838557bdb2bd967ede 21c095ad1a318b4cb437bc85018015dd88f7be33d7a8dd9866c1748e68586c5d 91c5ccad9252630c2ea99586a87420e8af58c3348ee379c2d1ddf0a221b8a6bd 406ec1214dd65890ee515b98612c06de1fb192b5a9c79a62a30119622a63fc92 b97aebdab39ded19b5151cde77e0dba3bf884b20d76e989f998555379e279d1b 2ce46bb4e29bf4020c8f89041a0127191c91609c94d10167a8bfd0a05436c7af 38dcc4af14c5ef185e7eb728ebd9856666ff74e289176bde4e13f2e89c916b64 22858147faef84673a869220da42dd7ffa011b3b344c2e78d5f060207d030e34 efada635606131016da8e1cae06032667d020a4647fb543730168e424a5b8e3b 267cc134a2557c011b156d1b9d5f9d12d8cc1ceebf5e3071fa96878aefb28980 ec9eaa5c03b8773659309ddabdaa3cdcd7007935175ea3a6f5b3b929b4b45994 1832d72a8c8092c0d1edff25076be448986ffb145a7eac5f4bc99665bc4e636d 76ec16c44937efe375a1923cb4cea0d42a394233d00ed4e77461c0bb19591f46 387cce667b9796139aeffe1f79997a0d019299a8129b946a849f5f537cdbdf6b 79106c954375fcd054ee0e558d8959a6e8b100c1b2475c1f2a30e12e936125e6 7bd06529c0c944410e0027d4b418105022bbd479d2791066aea07763a2beca9d dba510ea42aa855ccdebbbb71fcadeffece08803f3c2760c1822a1e5f4fa9663 c98f55e44c5213fd51b52e869c7096ea5fe9620f8b183d484d447ff8a7779778 1dad1ca1319f93621fba40e4b7ffa7c939cb43525421df2b096361be1b8b1ddd 4ba49cac2da0d71749bd8a99f60bc231bfdc4304ba7d33323a362ba045003af8 823f604a64a454fae850acd9267ab3a7eb96deec9476d2ac69c2f013023ff2b3 afcc56fb9d925c8c7172178f518f795dd100013a35a22469aa01850d5a2b8933 3b02bee1a2346707050462002b2e0624a7f3beb0f61199e8082503ef7bbeea13 a5bf9d17c3fcdd0678cd6e02a3a44cb04127c3cbe0905dee1ec3e2f207e8247b 167cf06f2b5cb320d5d8e82a25312c768d176c1a8d7c2a978b0a6b355d9291a5 121f682795848f0cf6881bfaf24cd161e1d48ec64861a2d3d6f5bd5bdd8d1c19 1c9bc3a726a992f1e7afff27ee6cccfcea8f103dc018161d0bc3cd3367bb8424 4a5293fdea6a697bc9a561326b2ecb6a364a38d28d5b51ac517f8ac36a598633 fdb2f1a88848466354fb68e56ce89e17026ca60d049e063a67d85f6a29848aa2 8056d4516ab1042b2f40fe62a3e1df9eebc350f4cd39d7c60744a25f127f9f24 43039a60e66e80cd341f0d915608485413cf54abd00d6f74ae7d6b400740d82f efaccf178d84c5bffbe97a3f0af3f982049f3b4f7ae981e80b5caa19d887b606 532b117cf74e4766c38d3bc22de22b831a81496e04eb9f30cad2cf1a08c2e371 517fa25d585f74a45202d4583f084d656ea1854b0d03219792f4f3a28f23d58c f472a57780e1b0043e1c06079f64664ae911603ea663becd80d6ee23009000b0 eb6d7f2c9d42f5e9c5341ae6df2213ca5aff31308b8e56b6d9600fa2b3283a29 224239609d5215fe464286cedd2a04cf5be2b4865f3867df83543b1b9405ff32 54ce5094e3d07710882a35a255de37cdf2cd31d9b9c121b3bf91ab7bdf65c887 a0665b64ee617e93e6b2656ec248f99b568dce3cdb93d0cde77a59a63153f467 7a87b59a3b81eed644cce872635415ac56b14e8ce1d57758e3b17d208f802ae9 cd266d34ce426739a6817e41b47da687da15d0fccb44bcb63afe3e6d21808304 91d6a6f62da7e9086bb77ef0fa1652678f4112bf8c58966141812a41bde21014 7f11b0dce98b3b5b753547cad841a49bc70c50979cb7e26931188b2805562c11 6ebddd1fa58696e991018d0bd21507c9fac9670dfc09b83460831d0f99d6dbc9 d2a836ad7b06d482f0473cfcca188e2667241e2fac3c61446616d85e2873209a 0787949bbb01720189c8c681c73df597e5604d268f8ce00990f2e3d5f21e2cbd f7597c8aa7d943292de1f422b9ab12ae9fa26e6b029a8001fd6af739b8c5d16e 3938d0588802af284691bacc09e8924d5339abcc01664cfbec99510906fb8f06 0655a083cfc8ced5a2428b8ac6d90fac5492cb59d492b50c82ec706a9ad621f6 e67d8a5f62c1d7284daaf885e438f79fdd3f473cbbbcd7a12955ef107ee85c17 fe075fc6115e419d5e51a05bea69b864323bb3223ffd2cecef6e0c34e5b4e1fa 231b075c7044e047993781d57fa4d637f5df0cee973f033702ba468fd11a1e6e 63be8069804734a71417e6c6492aac78377a94411dc38f6ca94c1687fc0fa574 a71d86fb97b927e7a2c37baa95b6be30bff078fcdea7cee9ffb2ae3032631867 2868f924fa5a7633a1465a19064f6fcf49236cc893570256b5ff83def889dfee 9327dcd74a80d8e531c1355dab619058fd5cdc4fe01a06f037818a975a7fe74e 2499435cfc2c9432b3888b7a2807f192ba19b62b45d7d273d7f5b0a204d3202b ed5ddcc23a3f887ce3fdb9bb25d758312e7909f9535f9a3ff1624cd86f903a3a c32b0e534cbe8a00ebb0576d80b80dfb4b75f5b17f8f38c9c84e3fe49d13bb2a 9fca90b55ba9b6c5f2c050d2b2f1dbb3bd9e5e51e7b8476cf1355421b0f59b7c e7f9c9aef7db8e8dd1078201a3abe2293b43236ab521c8b0792d56bdd3008246 c09f4b85470610997c9946ed24c314d56689990756c11f8544420cc87826e683 38323e53c2f779eb1622324be8a5fb006af48ea5ef5e3ec00148a147b00f6542 1a06eb4466f0ceb6a73be5b1d799835e86f6ff309a8397a7738351b9f5cd1a00 f1ce554ee1a5e1bb952ae702018ad3ca23eb11ddcd1bfa11b0c1c55256b1547d ddacdb96a51e0a4627d47459904ac22d64a220a1d1bdbffe16f5aed08820fec8 0022e83f981f666993ce52fef3d82566be91bfc1954673b07b7ca3ff5fc58e49 4fb2031fc2197a88d496854b3935daf08373093ccce1f3e69486e4b35c35961a 12f5aad6c8f040e99b9426b95229f2498481a69f4555fc2a593768b68d8df433 a356628599009e89718a1d1d6966f28da025984e3b7dbb310de1e39945606db8 35a5213725ccf4d076a71e87981b63097228bc1043506e1c2ec429f13355ad88 1ec779087cfb19cc36ade2d26f64f9bda01057e81ff21fba482aeeed4ac12335 a913b76c8eac995496954feb8bcc402d6d6f4e800789ba28bdeff1025d818bff a75d75ee55b992b952ca5cd226383b3964322796819dba518628f2b4ffb2fd84 e2ea18fbd119c70d15f4223d594d40202614979a08ca9177ed2c8c76862b2e22 a89ec97b2fe0a79fe66416bc90f396a54b020381e2c7942fede9d56c8b2a36e1 1d27f8fda1fb4ebb4ac24e91716e636398c702214188591e68243123e126186c 02cb80704c2f7e0b1776e82a7e16258f0a8a4c3ebdfac82423e3f72b7ee56d4d 9a524da42a15229a1c21aa734d09e4a71c0ee202f5334ba972c59e00b0871fd3 04cb489a417d31f07b8aef25651e7ecd6ca857605c720860839562f66bdf19d0 d543251d5e015dddb344028dc9325456e3a65d33c5de7d3ffa0bc4f77bfac4fd 70f3c9efd0122620789d9b67d84eba1218f40df0ee75fc6e82566e83e888b990 643ac4025e417233bdbc3b1de190bbb1a0cd3c2dc8de7b9ced271bf3a067e391 1f86b62e7e9a8373f99508c94eae94e160610de9a2885dc75a4704f05d569b37 a329f36a793abf7b70440a1b88da55f88de436e4b65e75cee79957eccc454d94 20493dd3fc860598e2953e9fd29ef927ecfc661443285e7f44b55aa2853b8b40 dec1cc91cbbead300e627e6aff37467273a6deccfd077b4f016bb2ac7f54e591 49bf801c8c676cfe1313e692b56aebedd91546d3e3037d72cbb957253777313b de0168b980e3e80133c7dfa74f76a8eaf7a6e39c4c6f5e2bb9cdf9bc8ad94055 26385c685bb1bd0e809fc3732de977a3fdaf31e8547a78e18225b83120bc6e69 90dae46f81b1994f6952fe1639f741006af0c02c44c72aada46d6fe9aab0b879 cab19752deb880f776f0fe03f32025fd7cf1258638a487698a90302727eb80ce 90a85fe21ccee828f07c00b4c85f6d54516cd4bafd16a8b6a6ae81b2b5466785 430fc038dd9548be476dba2c31c6748c7a354e3c67966d3250900dd39c809e28 49a2d2e4c02a5b94b5ce54609f7dac32aca0a4b2051b784ade638db8df4eecac 4d6ae1194d575bfea52a4e09266ba41676b7a552346100db98908e927ebf18f4 1b0bb0578845f9c1f7c568ba36e7936729fa907f6c1a8c1899229cedaa1aa306 10d3aa986969f9c45562adacff4f74cf399294ae3d6a56c6ae72135f51ea90ed ac69f664f096b6a8684c5f014a8b984f1eb9ebc2cd7edd2b28b37ef25e3147e7 d348cffc84741e63d73f26e953e1a0c6dd8349008edaa22da7612600561a5ff6 d8513404ebf2c16f6f7775d3586391168e0846a973f2229d569a12a06429eb93 fdb70b83761ac9e5f0efaa7af09e44edb8ffa0183b75ce52ecb5f6dbd0abbede 595b8d61b840a5659888c9bc06ab291df02f4cf5199157e0a5b509c382198067 54cf1293b535ff5aacc99099974dd99a1261249472edd6e6b4086fc1cba8c90d 97c875ce3d7224207c85371c7b1def7eeeb5b4690762b719f9f04bcfb866c479 c348eb0c65094a6127d7fc807507c8302a1c69895a97364a2f01ca5482a20cd7 b1308b60a84a9941dd55462ede7f9a54be15fbbb3cc28d7d3479252d0aff9563 c9517163ac2088e0387a0cc36e85345d134fa4104cce020658fd8f542e836edc f64f3d2a5ee687e1f8dfdafc7b06b3b62f1829d4510ae5c41d1e637141b45f80 44a63864b9473c34d0eaf8ae941eb127d8413c6629b8c36fb08b0ca4dc214df9 93a1bdf9e791ee990bf5508b6bf513409cbfb4ebcb16852a38fcb493810ae898 fc94f570800fb6581c4eb3d277be0a34891423b22e1f3391fdae809f9e75729b 12122063caa42018d7beb28e347bf7c0831da5a492b9faa318f822ba6b543008 0a6544c33ea85db30ab5ef3944aa87b5243c6e44a3001f64ef98b86edceb9520 64b6729a7871fc0f35ec31ec51dc6e444b2a5afd02c508e336305f46d5eaee60 bfb50061c92ceda81088d5698f56c4ec4c554354060a38711460332d38e21a33 d77091a9d6d90d9e8d68d1070ae328ce59f7a6a6599068e7520af35fee82fc94 d09449e1b37600ad3a249b93f2decb928c3ab1916c38cca9b56c38e93ef4d987 bd1feb8de1340e0ea9a15ba6ea5bc4f897f0b3d86ab4b882743620cf7133754f 00ced965f3ad93b796fc644f608b94f5851bb8be1559a5ef9a072f07437cfadd 610ac68246075026e34fe339cfd31c413a4fdb201bd7cfe8773875ec5f08bb79 10ef02726da9fa5618dbabbe19e3670ee4167b7c9228f0c9367a53cd8c5cdd95 0137deb5bc0cc9a05e0f12f261da6584942c556c0598db0bbaba1704362a8fc0 f69de7e6f5116310708c3395a3e4e95e4e58aee0cf39670a69d167cb308daa8a 17a8dc951c11526dc54a5b877c78de0267a21841a3fb137f0037666f996795ed 28d84bbb485761c0177ff4b35cf8982f98f7092339e8d2fc448aefc8845c0876 540ffb7709f0e719fb5125e443b79c3bfcdac655671d33187b750bb3a27cdd59 b7e545642adf2ca59cbb67c5161f4fa5c82d42307201b55b55dced00107de1d1 bbabd53bb8454036ad63cddf866f7e6942a5e65d740efdbaf15393a46cc2a5b2 6a19d37e31a1d58fb394814334fd5a85f9fcd1bfc3b816990292f8d81eb2519d 98393707ff0ee3143d5c0e397dad545f7edce40c334f791df327ea542d31cd75 1f71b20fe2cd0064eb2688696006fcb45d5ad0b722891bd7c8077d9717914e01 3751f297c85b2064882e0afab06492cf1b103824cce5c111e2d841d8e8f459b6 ca24b137032418a3d5805e53f04ed21bf57524caaa5cabfcfa19d48061f5f612 b89270edc915387df517efe1ef3d13561001ac485733775779affb438e71d8cd 051a33c231cdddc05ee22d707ee5e8eff9d1ea12dcee617f7dad29210e08f6c4 9119b076330d06caee1dada7957dbea09e03144a122ab049bc0564818823ccaf 04e925699f33291b1ec77269f0f3a02ac1013939697ef6e1ed1c040ab59d213e 9ffcdfcbc96482ce62bcfff69719e9a6ced6e3cf172d0cd234ed9c7e8e5586da 9c35c55dd07ce906697dd1b1951fe1149ebd04cd7f31a0261804db2990cb6274 359f282831d45f2d2f915dffe9daeb8acfb4d99c5e2f1be17ff3671b378a80ec 1c10d7e23136bbdc3b829338ec8a11bca411d5faed84fce332ce88f7426c43d4 6a60603aeef94a7bacc061cb070fda7db41fbdcbb654a4999637af158acdf1c1 97ba1f621d285ca0222a2e31ad7685dc7aa40921f8e9dc0925bd84854d05dce8 6ac558b2d25d64f6b3ec05a6f1515f50474071883e3212cca0f1a84deb99ecd0 e490ef89f3ae5442e0e7afe3bab29cc80801fd82f32b65237208a5644ba942f2 7a904de44a30db4455676a6cea297a0bf420f5e9cfb9fdfae8aa833995fd7c65 58f12241a8a1ba839dad5b602c6ba45cde6113125e59f9052da4c832d14a66ca adef9f1a7a83deb20af284ab72e4fff3c573bd3dbb991106d40f4f725f289c8c bc3930af2f43c34e6d904ee625bd2beebd315d583eb7523c18f6958526bb131d 86e0d0697f039e5271ef13abaa28ff801f90e6bbb79ea905df55801bd0462286 c239f19f3a2be47fcd0a23cf95ec09547157010d8e6efc3315651f23e2655176 99e7300e40160c9b8dea5c066c1578aedbb8c9e35eebcc8ca77f2b2ea2b02dff a1a55f538026ddafba163ba90b5f348df85a214e35420cb11477d8391f62031d a298dec7818c28ffb74b359efcc38eec4b32c1e73e0e8881febd3bd04da66040 b2bf08059fff6b7ac09b835bcfebf106c4386ffab05295dddf769653be295a54 6a3938a46207a8cc52ff35a881a7ff5fee19b918c8d354ec074a2943636508c6 4e96f1e895e106ca0ee4fb9ccee77b63ebbb14c1d514269affa917a0249413b7 4f1e9be08fcee2d1ad61a1fd15c8f0c9a1aae378a763e1a953c96254f38b99de 8321c7ce4d12a3ea5003bb741646c771079875712d53d9ad50068e88b7de9fb1 d0a5e457c374b7d188834afff3ab9aae45c86f7db697b26b21b7a8220563c46f 4565759b70e9741178085228485c81e1bcfd8a8ed97b9e80850b7052c3bc7ee8 133d0f3f9a55c3e714eecc0d582a6efef92f78eb607e03bd505970d631714a63 83a5bcbd0ffae9cb2f11def4f599cbec5bfb02705415ab68dde796c121523a06 c9028b0ade18d0b941882b9ad366b079ff9b1074c0dc2b9b1286f2e3b8e6d15d 3fdeb6bfffd1afb9ebfd745e369eb82c8a328985803ae385f265e7c7c5417370 e24eb8851842daeacb0f9710304506f90d32cf3c49ea8a8463779a8ebb7add0d 25 8f3b87c88dc4adca7f8b85eff3e3d64bbd2278c92b513e89f269598ac170440730ab0f83a0975a57bbf932d596e72233156255bb8f75aac45c173168ba29e40230c5003896cf9b2a1a1b1e594ba2742435c9cedfe576714d248e995cbcdb13069391bacef5055fb0562796f076c46ca59124ef5cfc5f68a720233296b3991b038232555fd81cbd09423c05f7cc34e2a802e5767cfa62d4d134391aac04629c053ea0a240c5fa0e08bb31f222d841cffaf0dd98ea58ff9b9f98924c84e2664d07120ab9995ed6972b6199343cb7bcd6ed511d5f051e4d9ac07885ff57d9ce6602fc2de5447578204e2802c4b08ee2d70a756be0e1500a2aa0b0d25d72eceb780383d9840d4332ca8f56aa43e08e31ed5fcbf982f6fadc4b75e2d7bb8fd3c8ea0bd261d27c8edc81bfe94cb431e3a56646d7c73158312b058f1928b165c75d2a055e56621219f274c79e48fdab342c9a4efd2e50414d2deaeb62cc7b57facb9906d7df14f39d7f0c9e5cbb966be6907fb04826875fbb5e12d18fb6bee5cf37ba02f259769ee50bc6597d95bd5b7d79f20d31196717698d920778f83c3dec8195033b7099d88cea6f0478164ae6c9001511989ae3acd759b34082de19c0dd28390d68ca78138739dd60cb5284c095f50504a470a6239689b3882864d6cc042331099a2a1669e55046b106df947a28091601618525b34f3f383c9549f9e74b0a5407cbb875f7a1fae649c93cf48528f25bb5713f79372e636d827037f343496202033e5a4aabadf8f5bdb2f1bac693b51b558db8eb6cd933515c0675f90676b6ad073e05b8b4ee373a098547391b31ad1dea62b001a9432df65ee2328a3ba8637b07b22a4086b14ebdb70937f51ec7ee20f85e9b2935d8e4791c8aac31dcb5d4a1077a995c779baae86691a9b89517c14044d9e84ef9b2770363569ee6c0520ae30136f6c6940e6a089d74d2a91a7c5bb7016e986f538109540256da1aa436002006e3e5ed41a940863253c4eacb25faa05a0170d987913b2a51ca8f29a4fdd99e023479c0cb0478b11c5ee4b9fd63963117715d9efde2de988812542f301a56880c791fe8c487dfc6fa229c511d0338b1e81926fba688c09e722efc7dec6b9c990b5e40ab8a9175a7e91f53ece9b991cc6913642217332afed0217a57b40fe62d0eb511378d1cfe76a316f8c36339a647aa0bc71fc916e5b9d334bf0cc1bf7adf0e7bc27c7f9ef44603eae38eae83d420e9ddf9fb5bb5dda168fc4af47cbab1370d0900a1ee0ba632f8889d6cdcdf44df62c3162c9de25340d2b406330a472c7e00c598783110bcc6cb69bb606005bbeef6b043cb7ba652fed4c012e987ef26120bbc025592cf475bf37e8fe615282cace19588a8f396f1dc88a02b29db0d74fe0b18b2c0a63314725044334594f8766827ed7279b31e16dae36f682497036c5e06e096e63a2b5115eb72985e9c798571272952bd9276a4b242fa5d23c83e0ed20afe4d88cc69c7de6145d4963d87711ada259b3cde77df8db5c0a3679c0be1e608e68e09bfb25f17a82864d44a5b01394bb2bd47e9bf0c4d5bb06ba6854937490d51eeb4952a307e1484a285a24a9d83426395bd991c9c2d1704f41ff355b298060454a052881c0f6a133cc9d38146512f6b1a8218a3bc7b32b062e41451f8c70d8d6dea558bf1c8dbfaa1842758a5f05f0377f35b8ed567b7bf48da58e9dc540180efd88fab346c66b87f14955c8c12dfdc63c9069353a3f731ab91daa0831103813f6ac0c1c8f74f86366afd925913c08b017306499d507c72472d3d10ecbf0e72cee2a2425d35170221e056aecedd3081a155377fbb51cd901f0d2f02b8df0c898207071f47ece88b1dd2c10a3f5f0ac904fed7fc48563474eeda726b84b20e79428b1204d475d74eb519bb6598d976455bcc90634afbaf0d965cbbd42fca0650d2abb6e6a4443e052bbd787efcbea78a1e3ad85f0fd841e2152f23f45cc5047fc8488359b43b4aa998e41d44ca2ca1125d1f4d63d7766cf73104d4915d8f0db8befe72f1f64871081577787f8f1db19ff0067459513742d1c6e4c5d08e3d0bb938ebbae33ede5946d1e40f985a7f452fb76a614c8c1bd5556cf6f8d712ae005ac78ce150c16538e5914c85a852da119cc0388f6168f894b9530b6440fcc905fd9419e0c5bf1ed2673b41dc533a80b6462eefd42aebda44226bc58325539c08f9306b5bd00952bd77075afbfafd124999cd637bab85f3c63976618c7b3568004fe43689306e467b9b9d273b5042d2926280cb978fa3efcb707e5b4f983a5809c1582f02de7022aae6c9519a5f8918a0707c2119d3569e8b4203e11cde96210529ddc5b573a48ca3f85d10e920eb24b944c0e15ae88add3e1e81ed7c6361290464f8dac9ccedc60746000107a47699a5473dfdf53700381751957e02a07b0308405223a6afcf95e949ce29612aa7ddb67b5ca47edf272728420100c2ac5bc2059fafd5d5dabdfcb1d31bb71b56de855c1d8402dcf38ba04cf6a8841849622806be820a42e871d70b840ae945945df131eeeee530f290b8f89e5fb518d107ea016265ef77bb910c3cbda66bd7381bd60801c70549386cc06498411081cc891409a7e99ce434e2481d5824e9ec0f7c4e93732695710bb852de917a62be9ffb040b0ceef880075d3318cb028b47f59363ecd9925b27fd987c256eca83f868426b0e8a5d3b408940d94195201b9e3676ebbdedeaf30b4fe3a35abf36d4e0f2a1dd07d4cf8cd7df4a68b5c2de1f65db90d8ced5039ed98c6d5b54cab8e26e739e7608a2c0061a8ac18a8c06da340a6cbd2868fddabdfa3e0c709ce304e651e8b08e08d38f570f572a681e0452cfce934cc4be342cd030ac2db872581945d5d0b446029610934d35e9ebb44629d42b14e1d227b3cc717cebf39b1c382b63d11e930603734922d22d03e0d72c2060259e1b71d2e35712feb498a2b4919314d5aa467103144494cfdffaf66bcc705937fab5a477bae868d0671017cbabb4e7fdd861990c29afa675c3fbfafed07c5e6df79ba67b6932f82bd5d266833acd4505166dbc03e325fa2c51b420a6281c0c17ac57970b5a3b2407b4287f8490fc47bc50dd430b544b4e78a670bdde495896ea2a5ac55f5b16d28bd00bc847e618c015719df90c22e0347e83b937bae5cab1411f54ee6a88b81749a84659c338a6d3dbfc51bc0613390a5c44f3155f20c4c8b7d7ee8723fbad528b0374d00274592d9d1cb4f0042356010188bbd30e40f5b63068871de6aee6ca4ef164443257e0539e9322b00bf9638e41b96fb93f429fcf4b15d2846f79e71caa2a3e3e348050fdf5d9122d0a182cce663aaa350ab818b7dfbf23ce4b373b004cf28011818bc1592dd9047505719b94a330fe25192d4e566df7ac855108fd15695eb0625895d679a370ef390d718a7da5ecf43449d3284c5fa53af73efb2caeccbfea0268339c03645dc22305f4dce933d41184afe2a2d8fd8cfa18483c7a90e188d8274f4d89cf796d70b108c57411d249f1607847595566a20b430b36011bf74f0801863ca1028dd0489702569a85e98c6e12ddc382f3d097519d0551116d94757666c638f478038ac0f5002158da5d14b8bd9349e6aa9f24e6f9f68166bce65d61dbede0fccc153011cd0f583e3f0af65e0aa84b1426fd7c20cc95a72acdfab8a09a5ab1ec1275981ce7045170fe75a50a7b3c1de6ce10a017ed9d11d675c4d9bd86615131beb646b944076ce8557fdbb9614b40ed8b63dfcae03fc2f3ac6247c6a5f967be9f4b23f59f0b68849d1434be889efae22768e7f3c3b572a9afa58d1f824079c4689212b26f0af5dbe233618c5a46f7b575dd7fcb22b51c2c668fe35d4d9b124a64a0929c05053faa00c2ad98e97e27805aff9d3c23c66b74e342ccf48d5a44a2c25f2b791403dff4c04d40b30be1cb10b607a399ceac6a1d567c7201b5cb1d68fcffcca31c05f2aec81512a05c54db85972cca2412211c7473704e9e06ec856afd77b8a4350019a9487e723dbb104c165e2ef621b1dab018564fbcac873c81b5997543d63206505e1fc04c4ec67942039f399c5dd9ecdd99c1cad6d85a978813d1b2c824180b752f4e9ba6951a89a5ada46bd08fa901ad06beb5fdd841d9012eaea30a00d20be0ac0cdf675a023c94f0361f11464dae390be8ec77da865fd4fead09e29da70bb41a4cb873c4ca9a1226c6bd825dac965f705a9f2bfdf91791d6b49d5f9e4401ae5f56f1b39ebec11e0bc52bd19adab9f33d4350ef32eda32697ee371a5f8905a17370a57b9ddb10c3a59d0f33105dcab393b615549aaf791d1cbe92e920a80b020970969a06b3afce3de607e4118db0169e988f542e43d76a03c5ed9ecd2a0a20c66da2407dcbbd069b179b800f29c482126ebe32e7174cdccf063beb6d190efde1df5884020e4c30c4d8fa83807bffa5199314f05b2b1fb8d3842fd15c4e047f2dac875263b2de232dff225f24573f7ec0994f7cbfbe3e50cfbebbc61a5f01ba5a8056d4721bed705564935fd080114c8b37873742e416c04c6d897b6aaa0f638e0d96c809f8e46b3ab671e37e8cbc73ada9f4ceaabfebf7c32c50a415e60f309c0605b88875e377e3d6af881e703823b36133c2a1d173e64bc998102df70005f648583b0024d9c56a45689a711f89590f7d7e3e1f7cc906c776ba3a278304402c66967ff338497897cef8f4756b533acfd2614afde3d8dc7677411484590cae3e747189445add323b37794eab1b61ddf952ae72c0d7e47f81560da4a9c40221f4e5848cabbfcfd67fa623954481857436131ae689b1c61831bbb9bf17770f7627a53926097f618ae0e6e7106209abdf0164e51264dcf198b5ef6a08f4c10faa9d8ac222f6fda070612a9e8dd051c20d1258142c67d3ae50d4101bf58a4a0a9d1ee2a14c48ee45369f2a10688d7da8cd331d5765a7f5567cde91188875b500ce6c9bbe42be21af3c550215b24d08cac0e09a259faf427d3b8cf2a990b18d04aaba48404a896bc92c5aeafd067fc325a2d5460b50dc92d26069af2e57cac40b4a8d2dfff81716189ef2a8febb6f06ac428edf6d1a0cc342b7d3389d405445059eec86bb56dd4034459dd9d6bdd2ee63797efbcd0a21e74f5509cd3cccf0a10ed96c0942a02804e8c7300e8d094bcfde46a64c12d87c62552ca5df1730c3b0066c98067fc7c50a810f819b16ace8e841ed6aee6091b1977028a992df92ee5200ec9ff4919e8106fd9815e7f9ceb91b1cef4f37dca1962ce297f43f2fd5ebc90cdfe99c8de010085ff020fd2f53e5ff9c5a693aa856c9913b2d6266f0f74d4500e5139b5e5076c94c66d7ed42260fb5d4f93e0910b6ce736d28ae1baef6c5ef098cc3fe587d87a211ed80067354a31aa33eac4e6eecc58e7aecb385fa4d03090873a9b28baa5fd69810e0e7dbdf1880c1e9faba696bc17d355babca58b0635a0d2eb6bbc0b544dddb849f42b92ac6f02b5d94df207f4d68e584918ac1f85d800152f7a8a21af8382749d23a78375d30bdf760ecde8e77bce87a0f640f41778b07f8cac4ae1d0d1247ea8cd675ab94ec057666dd5325b6c8f898553e8ab5a88f0ca3ed47ed793c289e7a198c5b115f6075c1cd170a0717f31d4a14150532c601070caee6aae8f7f756ccee18b28afb327f0902800c5648a23551358bf3e661630a5ed926ca2fac1a2b872cae900e19eec41e565a52a9243e6bae3852e263ae2703689c2aa49f520ae01616e2adc70aee9bd62a78ad63545e1a268661aa9485ea0179e99ade674e0e698da1c8015b284cedf8bcc6cab49e964c34c84713537bb50e495cf0e0476664ce5542a660133495c993ab363f39bd9eb288825b764e309e024f3672b263968374fa854ed0e6fdeb25a11a81943cff7d96308b68a8834dbe088f5e3bfe4a6b1477d9248d89eedfff7d71805bd1cdf81c18475e3a6255532806c14bfed5be4f67fcbb24fda0298dbcb1d12a3ac8e64c582dfd2a359d8c64fc0d5193a40ca88f0f760a28205950655c2cb6dd3e3445926c274b7bfb92f6923203d49b53ba5e591169998293ac19f23c1bb579ba937f5c843ba7f5124cfe12cf005d1ee3005f897c0d03091144207e3fb353c37c6855f12c96f60dc4b895480f06e84876bf871e1b0a08e6075bf0c2387e8f9210abef5e51b76e1c236cad84da0d5e63e03d36b55771ab22c0537003128bdd9ce9ff6ad2605aebc9f8952509bf0caa108acce225571004155a51f8f3ea552af7a10647d4a8b3563e4f6b95329d08fc091404508d87aba2246088504539c113fdc815e78f41b5edbe18523a944d087213bbca7ffb46514164e849bf0837b04838809d5e4bf96b06487c560f074b0ef9222f8f8e2a1889bf8b49053d23581376ac4abe4c45934b6aa93fee1d6e2e01e1db0f08cfb5cccbfa4face2b012daa3cd84903f7cb107f44938385554dfbe024e9ce2e3d9d000f53affae715e90279a78435e33127377bd7fbaa9054be6d00c7097737056e7565fca7792edbc2984740099575ea3c1f663d3ce237ac6e18e017aa614fbe5bce8c90ef9a5d58d4d5bd3084c7d4c290a8f4ddbd9add898732e02ed16945294cfbd77500ec361e148f3124213d8f8476a43b803bc377919880d0f16cc6cdee8f4512136b030c0335acb862fafacce525b977b3484d591b2c93f0396a45f559f0bcdd53a3d98bcdab020baf7917363e5ae4ece87d159e5d49aca015bb7df31a82a8ff485e3f41ed1a4dd45fd0c023b3ebe67ef5886dd73168089066f253aa431556dff8d113cbf16d8fed89d6db3baf83099b77083901bee90dc037e49e74d2a66f3e2d93db8607ccb946423f4c6b49833e814ad384bdd2ec6a9020369cd7c1717ed59c7a526e60d01cc166ad8f3b000095b79ee8f90cc4c272600d3aaafca34afcf7131975e0517ffa74f1e0ef8e51db38d0fc0aa90a1662d880ab31a000deade65f92768c6a7419f59ff25ab952f375f42ea249184075aecf50ea6ca7a1cb9e374d1d7acc7d7be78344876bbd8cb87fca86b2709145098764f0a0e823e57d69ace750ea58bf6f0db549531a655f23a85d2a05334dbbb55d6a00a1c3a1dd6472017f97436898075557af1dbe64e01e5ad22bf81be33ff0e2ec00784afa88a698128fbcd3580ea2303611f9337f1736fb2e23b8398f8908b179708342f3d6f586a14bd0cac0ed841098bd9104d99519b290dab5a8d90d3174ace0fd2e870cf2b903b73d614d4195e7f8dccf4308d3f4268349c13b6d02093429802665e2111af809831d8acdfd81aae3fe4ff196f33a4d0cbc93eb32335979cb40efa5f14a8120c102c979e98fd02d12d9a064a93d0c9bde6bc09d4ef2a22258c0b93c772e3fcbca3545d533c88d58f83b1e015b00526ef92b76f24d3061c4c7c0a106f248383955392e8f45fbee1503ea8e6a7c0e40dd1f451fe700ca88501a007280f337d5eab54185287ad30b921c454dd20b351d606ce7688cece3b3016a30db7c2a3b7e28aadca9cb50864fdabb82a7b4c14f072cf0ebbc90ae94b9299db0d490e612214bddd78c57459d55be8f40dc815c6a23f12f9e3172e9482d2f07209eeebd59d5d8c2aa1ebd5455541f26246b5a85212bcf309c51d8cc571811e290ac2def5cca70ce7be0259f2d1e3cfe5657df05e776c24910ccf04b589cc6f1105f06f200ce863e672703e6040e1aa61143ebf30b4f8d06bb951b8037241c21309aa6e5f7e5a505b817c965fb5230a8cc8db94bae06c282225eae54b8fa3bf6402a4ed950351406a7b1fd6ff2ea14de9bdb5530deb1bbf88bf0a72ec0e31ab3d09c937c367f3e97d7f20ef150c6be10f03acb0c89e63d46d64f73c31508eff5e059e29380453d02c27c28c51949ee208a8433ab61bd1e776996835511f0937e401ec63add00fcbab6594ae38cd63518436a5533b06c9593282335ecc0a18f242004fbf2354935e1af783c20c2789d01e480b3255dc9342bd76065a28e904e0780caff08eff84bc92e0cfdcd324870a227c9008fd7396274b33679aaced513a670e5b6cbfb0b480eda0d3a1001e0936534ee564e63160c413524280a9345b66d90a9b542d786457984d97bcfb82f0e7ace032598b9ceb2eed153ba1a599c7b452014c509d49f481ec1774a896890723100352c78275c4b4a053a50ee0440e93770156065bbc1c2d9e14ef6a021816db11bf6b60cb95dcb0d11891815cccdf034e012a8c0e4425aa83c2206ec7d7792522bd394d176ddd859316f5f5e9ba51f6cb052be14312239919f1abf04fd8d993330f59b6bc202379695f0950aece7cb3110b1f6e540fc240b9011d9ffefc208018993d5df994986c3374e2bfbeecca834d0fbc804017f8c86d2526b37aa833cd053d3eb1bf63564aad4d7aa653b5c139b109f3f7f2013c9a70b5a609c9c0960ca43c42e8c4327748d33aae982850da4f690fe788cc121d73eb71ea878d2f5f746174c46b49a486ed1f0e02ec5c7c17f9bb0f51c31a90e16362e4327211e8f33804fa36803f298d8196c0e708d699ceab7304bf62b94dd15a657949fe1f67d10ef6d2fd710aee911ef0570aa599317d45f40660bb1a8cfa5b25f51a93d746660d8095070f1310ce72ac7ca0c0d282991b080aea67892f63c0315b3717044251923f018e4ea71cd73aee1ea535fe4b97bdb50e42d06ae16bc94209d0d3e317b4f06330bf7a5050dd79b631c3ef5404b9608d09a95ba6e87b631150a661b63a82bb514f201c0b0fcd670ced919023c7c15fb2062d2deaf0ff3a6c5c003bdd29c0322497322c1e126f3693dd9b5348f032d4200b68d984be379698bdbac416c8c3c91cb7cdf5d816320888ac4f6668b4faaee30f7c4a12bc65fa1db5f9bc9c8c3ea8cb93c016463b1d05b3bcc1671fda8528840cf5689195a227306035e6291af75514012a3b21667128821f6a28e26a7528800c1d83be7e41d78340bb4fe140b5df70f37d62b551185965fb8afde3d986a1dd0374f942d95b77eb9b56e3af94e93996951ccc64ec67c27388ef6c035d055c1202741f59f8b8e0b8703992e086a3bba8adaa3f916ce0ad5e6750bdcff2518dc807373c468728183e05f46bcdea1470d8d798ae7cbc2955671bb8cede79fd764209736717295f7f10ce1e7db42e1928d8f0f4711537b19702078d2579f9ff3169072d9d086c7428e89be222068391ddd57225d6908e0283f6981539b926e95b540035be300781440a1ffb0dadb4ee1dcbdce0b3f5e596ec29116a81a0eb6622470d98d8cea11b261dea4996a426bf66eb63820706bae1ad65e864dd117156e71202c4a51bccaa7c50d4651b2a6a18ce1f73aa5d443f531066f494e950f8771e900729d5f4b5d8bd37e2e947f55fe0e109bb67db3a2335add8dc49467ad71468ac0f1c6c2552edb05d797ec2e275cb3653d0089eadfff5a1822c94c39b331bba7f0bf5b6b92904c9fc6ba8029bd61b18c540aa2f6fda19d9dba901a11fa43033730aa8f9976f1b307299007eca72e21a0b9f1cdc559081321bd18acfac996ce0150795611f1ea27d57db1eb548b9b83e9875dd38f1481d6c14cc19ac880d7d2508059d2889b0c4a4932be1a6aadf7999b300e8fb5aac17354b57ba74fc5b39f5860aaf71f5b076631468b644e3e8efe2d2f6b5fd009d35420cd4eee378e4113ed608b5ea394af859c4008674311a478ad64ccdd6b9f240ac896ba5e1431c45c2e905de4e9f4fc6b6b22804a817ae2f65c71f2739236b0f452ce622bb1db320b2690e6513f1c3cf8382d264b01a9fe8175b9f22f4752fd7934d37c8d203ef3af7fa0415759656ec966e46e8661f6d80a05205ea095fba0a5e1183ff625e116d9f4c0a3e3112bd160f7fb0a42df58c96664a0fde979e1c1a6cba1643f1f48895d4a607a92d715b31b68bd5ce1c2a8bee3f34b6dbcebc56bac2e480f6caa66b816348096602e3c9f49f2a43a526c7c252513c6b6289731c6fe2e9f29f3f428d608db0040d182278959804dab293cb5d201d49fd032557a54188c555b70dae8a6b36ea08314fcef701c2edc503fba49ac0304e00bb512fbf05ab2f7117682129d87c030e700155ec01b8407a6ec7f40ee1add38ba72dba2f971c95a06c257df268545d0ee7420822d8f62bafc20d3c41852a22784408caf29d87b1e8743a43d146fd450143435f388d53d027bec2f3ff634aea41b98d3fd0a91fdc5843f5aec3ea4fc607986bf868c4839d767efdcb4cd47fcb9854a0c8a464aae9a569c54efd413e8c08de1136a11a22e37c9638f8bc144b5e477cf7aa521c4bd2c3c4200cea51796e03c54c9f30baa7b1530a5d0f19b96f9f731cef39469ca82e8fb943f7b72289db057bd851012bd066f97037ec08c16a5e5bdd3ed6d58e06d6cf62d14e304253f0061ad2a67ace7909d5453ca332b89fbd4e73304778061b36f024d9f68f738a050c7df2bcbfe93d5768a80b5647341a7e377be710eb154a5d7cf1f836d3e2948401c38df5776f04bac6bf7ef95c4ea4afaa066602fc2bfd2ec7966f215fa576590f2b12ed60a54017e42d4df0874c7ca833e4c74c6b104439ec14f6c0889f713f0676fed40e35319ff57b391d5cb1c707307b614eaeff8141e44232bccd4ea7140853185f2384ad78fc0ecd4262137d4a4520639e3d8475facbb0e8540f7aff7705a03f7055b72013db93d3626c84b9a2e95db0bf6cb6296d82225e722ef38f310cfea17f5dc23a132d96d949dec90b9094296826ec34b4e915baaf3b6610df5a0d8fd3334b810f00e1e06e63618adb3bfd8a9e7e1478d1c3b1b2e4d5388248bc0ff2bc6b13a057effc9ac818f3e0b4bcc46f9bd36f3a913e28b76a31aeb33fc50839420d269f799394d86efa1006a61581f464b099e1cb3810993cd789adffbd09087504bca594d90ebaf0d18648de5f80cda5d8a64018cd8919b5e021a3d5e005c00da6b225b8315e1b6961f0b2fab2aced687df2a3c0dd14463579703fefa90cd45313ae0ec87915ee9a2fd71e47c155c0efd00efb49c88e1b5a193946bd8c0d086046114c15739c423919beb362923e4386139aeb1f889146a79fee23de0f01d4176198988ced521653793347cf193de33b42a904aef79e6bbd02bb485ab30eb4da829d4139bc32636d2e39a865f058ad516777a96171d567d07cc245041501f4f82bff8674c47f5b21c9fbb9a4974ff1516c005c02b94a4a27a50536926609bc0ae6633d3a7ff01ddb28b0a2b687617801b9d3ff42d709f7bc009d9b4f9e06f0fa7f66ed13a8f49e4d97dc6fcc8b95c7d7a88f6af9741d7c6c3542ecb84a090e4bd9f5496c11b669bb3be377223909d374af355cc579b9a129b5108477bd02c8369ac5f22a9fc44899890e55f8b59bb42a98c3ab61ab260d5da7f3eb98ef0f8cf10688274b7d6f81a89679ff0e6556d8aeda589ecee73b3e5a62e494a92f0af36e805eed96c63b69c6c1e139a11b98c330711075bc90d55ad592fa8fb6eb0c96c2277fc1cb2390cc3b2cf48b9251af71a9c81465517cc650b1b9c69fc2e60d6c20c17da0cff1ae874af430a43ec8f697af597ff731221dcaf7fceb974dc702900f58393d6101ece747af3a341d0c3ca4c9b75193f037623b86d231acfa43083eb7f24ee11db68345ad5c9ed7f5365038a1d5df9c304ffe9f5163a296492601c75ae6284c268dc1ca4f858ad7617fda4d8d509df0671fd2ede7faee5f68510767af721bf06ab3ae1c4bcc512bb8b3cdbb721a86295620c8ee21d096a5734e04862c1a054a708241aea5ed4bdf4f7a45094692397b5971db4566a26dbe42a10bdedf116f64ff38cf29e5d773955c87374bcec4e45b3fa12ee8dca9a201415b00b562e112cffb04c20e10da981c2ed18c33b1fb111b64f077d1d8db8b865e5e06c391f60a44c16186fd1473d2e7d684e0138859e927076802777107fa5a345b02bfd7cc4c97bc236a7db2931c3ad4581ac7d7e17f5cdcefd78956002354696a008c17c6a4f316dbf6b9a42ee397b434458097ab9043507e1941359c9c59d96306c53449864e6d6e58caed20f3f86cbbe254952b2bd1c697b1a117851e5efea80535bcc44db680809aee50f100b75fee39c0d6806c5c2562c605f125163618b70f4068859c266336476aa69d13bbf13e352f6ec58751f59226550c3e229eec9200df314c83e93d70ab8675ce0210f7d05b85da4574d55d293c47ce0e4b8c07a60020d00fd6ed703803d28ba3d86392d615449dd5db7a6ef8ba5fd39ff24cc453036306583174a75bf83a08fb6c1a3637e4c26551a9cdd28ed61226c6c8555de103373e3ce46c71a4a3fc6fb0aa806970844c7616b03c88f6306a4abd48d7f3d9062d8bb2228e968c3ef155f78ae43ba48350b957efd15e117ca1fa022b4481890eb66afaa914ed9da87b14ea4e4188decf45e1af8f78dc082d72f2b3fb3ba6410105cb789f8fd317b65396ba8b34d742d2581917b6de1bfba41c33d35a947ba008acfc83bde74e9a37cbda481a8c3930b0c6d369540bfbdc72c244e09b9996d00cb6886e6c07266c24cdea522b68fdde31f458e7d2990b848c448b69dec0ce450c339752b261c678415850f0f5e19b95aa2c126cc65db8feaa78cdbe80c31ced0418f436fd805bbc202f28e11ed0538df15698dbd98d2d44ec3e8001819c398a0d6c239b9c3b46497659fe5ecf96f5b33ca09f709c5f61b4b8800d3ee1e1c7980cdebb148b0b3808362e8599ba1fdd2666735ab8a9fcd5be987cbdbb5b6f863e0f82fecb7435a67c29a6e5a20310fdc85fec8989f6220575ef6e2834d58989da07b144bf7199498823325701ef1dcb166b1875b0dbf1f92f8d4b108696f71a660d84a1b6315269f8d6bd0eb2c9a161305a3f1f4269cfb5e85d4a332e6b99e7c4081bd128376494bc1cf308d5835f8251f72acd55228761997fa2ab38fc0e02e305d20a8d2181f121d4af43547f5fd324c147bbce81a371d643e93c66dd98196406ed9b7f9711d057f9f2d967e2d96c04abaf0ffeb56769a66763955b9933ff7e002191a2ad9851a6cd5cb7ebf52c166f271552f283344f2c70a5ff7403f72d1504c5dc792ecdb6dd615c467e5017649ad82e40e4c0eb0ce2caf6284ddd6ff83802fe6d8a6d41d16027c15d2661654ce416e5ac0936a4a95a011b1221a5b38bb6044733c983ea0d7c11c488dfb577ef1379a625f594461b9ba24783f062d96f7e0b084af26ad7f8d28b87c85bdb7ca6c1eb35dae7510f6f5d0ab9680eaf2c1f0804e6f4145016371f8aa151f97e3fede60d85ee890020873847869e42cacd7ef906e63abbd55b87acdc4aaa1cc70a08c214a37b95098414298672853b8859cc24017fd21045fc2412fd78f1b69af0f08bb1e4be003dbd785ceeb843f651ddd0a906b17d52f06a635cf6d692f4b331fa850687d0e541baa7ee2fbc7669becfd3d3027a218a18b6c0814f04143a64d9a4d37391a006c79eb148b3f4d12e70876b010827fb7d1dcdc006dd3d55876f3fce3a90f62b7bc5c9ccd84aa3d28a94dce3350b76f0cb5a5d4048198a506dcdc1ed301609c04a011b4b7b89535a72ba207eed01594380d224173317d071e9fd4d3d6f72cd2e5918624bb4a9b10c60f6c30b9d0d7d5944eb974216c48bbdaf4a7f69ef92b20e03ad824ea78f41bd7ceeae73c00373139435c7841cb737304ec5add479f140fc06e28f67cf92de2044f4c23c0400c39ad413285f51e76c087ba5d927a300d44a7e0317e35be94d41cb89f66af40c6994d0df763b00428f7e94af71aa00c71227853f1a1f5878d02c85108bcf8803ff9e8c036495bb118eb35979fded8c3f8dc8c331278d0f3942d1752a2b79ac0aeb3d994f8c4204fa96787571924379697db8ebe0fb1688b15c6853f85c20110dd972052874cafd60b866bd163e83a975fa19f1eaf5daaa1c84de7acb867a880cc64aac0160eca5641070631d5f85e0845430f1fe3349fdb95e27dfd1e095330fd8e531d764605587b36e7481708978bec8d6a0f5efc188e729f6475ef9170c05b2510dde7fc1dca90eea757593daa442cc156a7bc09ffc40837e9395ffcc640350057acce214e5c3ecfc7d5834a9ef1029b32d84ac1f93784c279cc72bbd8204fd9b5af30c739d5d0ef8b5121394ea1a9fac2d1972366291a47213173e26d2010ed400128c775e733af9ab3fcf090707eed20b3b477fa3753a3455444333c805ac46e2c235c79fa20c71beb420c3ed022561076d8fa8c5a96e480d09562951020f24a4bb49abc3cbaeb0e0b7eabcc700b1af7e6af785213fb11636868f1dff071009389888f42b29f8dced9208ea91f9c5444ae02a261efe1c907e55ad90a209ecb57980bdbb5ef293359718719bba53ece5c16e9e91037d5500c599c5836c08473940755b3f465558847f606c162022f061e9daeb0742876661b5bd7302850402e70e07fc11e3751db57cbbcb77ac087e5d1666af91e3dabe4acd04ec67fb0c4f4e0939f8803bdef7d6339c5f37e310e21ae217dd5a553873cd01ab1db3a10d0676f5bcff5da1c3c13049dc3faec02267424e7bbac0fa645b5c5dc6b06d1f0034a89cae995d08ac91aa59b7e81e476f6b250b1c14e6c42f4603e50c8dcb5e08d6d054b4b5c93ffa94c1889a20bdfa7893c4550c1547ca7ad21067a06d1ed908eeedf44e0fda2a91b1606e16f382aba74b1cc2bcb4226a7e5eb2d0cc0192820c1d2a97e21c46e453719e4f4ab4c55d3844f4c50d1a63bfa4783b9bd1f6a20c0ded2d41ee2218e45e74daaeaa11838e518cdf4c7f95e94727a15a6e5ec21a3108ae5a098b3988da0fd6bd4601fb84434d5d110eaffbf5fdb3fff485f8936a6805b748c44b1f5e610ecd6abc4f7fbdd2240802099d3413fa9aeb2089f7a8971905ef5b397667e8877c878ffe2991390de1b23e075b39d85eefa1d6948390b033064c6f966d30df6e2270794a671b3185b23180e01b48288c12c1cb56aec222a7032c1840c0b0a8a93702996416103907888902b9c41f470b70cd309ac8802e5a0357968ccf1f821e109543af1d90e9675d07feb78a5c39d7f1007f5c8c1d9fa9090ed07cf5eacb033f2375fc6364c3bc30744ca66e20386109b28842ad0994170e0423fb67e47ec48e0407f3627ffecea725494de2dfd8e2e061240f4578310a048229189adccef6f4896be90658f3a3dd338a838f484803d6f7d5997794c12c0fe0dcaddd85375ad1e365807f9568c18d06c02e0b38fe77a3500c2cfd472b91062c39d7b3a61b969a3645f2fadb91f06c2da7b717e74ae8d48e8355360aed24009413e4917e37f291c842ae6158b62b06c6582fb37bbdf6537bf40a58b3ea5d0f69fe43dd4ac5de3f4bc23f45e098d6000c3a68a4867e7adab640a5baf4a68f0aa81f0be7c04e0d8d7a9813caa3f64e08d72c3e8093fbd4edc536297b5388c10729346d4ca87bcc2eef0710ce23a884ddde40efd7ecee8efa34ad191184eb6c01d95e852d866e4c531db1a7a63c8291353fec37f838037a9bf4c4b21e97b87e088698f4352edde777b66f41c7bb1cb0711704e7f6a85e173fac67bf4c19c73b09e8f2369999f12b625e3c5f85d62c9e2336216b693606879b1cda7e015abf470c031d7b81c90247318988012e509a31a8cff4a1a1b55769eef3a8d2c83d9d9a0f8d754c8f129ed8919bfa9ce0e9c4f7baa02666a352be858a0a789021ea1aef0f04d78e33db565b093ab0bd5ec9e73ed57d9274f851af5d90605a2727986be006821a1c54b0918fc1256fec86bb33bb9ee767cbe01bccf04868625908bd71ae0cb18711fac846b4c9e3740513373e910025facbe7c9633b09ec51d5e12c9aa30da1189c040c5b3d0a0b91befd7e1fae61e9157cb9487e2d99251922f65443a80a97c7838592566c5d10adfe8f483cb7ec4aa4bc9c7b3dcdf17196a19beb6975014f0da6a6bbc81ca6d64c2cacc3aa95b5e25d928dd72c53c386ab3cfe011dcf00703a1f34594a0de03e2b18b6d61c449d742b4ec98228ec23638da3788e2be401ce7501b18a656fc5192a6dd2eb05345a89d2fa0872e470539840f2f10d9c5d02ee60b0fcfcbb8b4459d73a094ecf2407e19deb71f58325b13d037dd3dc74430141b8f2ecc3cdeedcacb087c659d186f36de2e9c643d8f8283daf4cd95631170b7325ae6c41730b79e34d127212f2834a6c62b1dfc56fdc676fca7f6b1b42630d92dd1e5eeaec7c20840ff55d6de5bb7cd1cf0b6f1b941011aec0fe842902d504149136b3398da5144fe1cc8c71e9cb0d6794bac16e3abcea9792915fc21a060267a8c0b46b7c60e5d9ffe12ca4fc69625488c723e96ea2faf0af2b430fc828059d46a2b02f047b9b92a4f2f9ce514487e0a97192702570314e61d1ed0d76dc0908eb5fef74e1aa83c93d60c289dfa174cac28778dc0f5e50ca8193c18f33f5049fd2d5982a2c5d3007625159d873a71ca0f1012d3e4200a6ae396bb12c41f00d4b95d8cbc1e4b0d887c14084b5d795d528211efd24d8b0909669a32083d6a7004f572503d0dafe8190c4f5960e151fa549f5363f6fdccc6939dbd597cf5db60ec50d0ebe8a92436ef20456bfbd2b91f21df5f061757e6297cbc1cbeaced3cb0753dda12de97ee3bcda38122ac08ef4465e52313835f4e5ac60a2d5c84a21140d48bd38427129f7319fd2d37d8fb0d0ad65d319fb11e36d13b8c3b1fe79156f0cfea6d38a94ec9ed6d301584220ab9fb1210ee635af52b726618c3f1b0ff42b08045bd637569c4e3d8a32c67cce796175805d472778cd43a278993c94f7af7f0435bc0a9ca41bb6cb123e7d652d2a70d3e8dca0db564247ea617d29c25810d20c760d20ff6671634e8704a6bbf0082d27ead116ab120f642dd7b1aff9cd01ba097a49dc7b8bf73358eb0bcb5024db650e740f79bb3b62de674715c29ba2887a0a1e341701d54bdb791d96252bbf7f2125ae29d52549cbd7800e72ff25b0014e0f1bec3dac65f1d1324aa604ad53260f2ee3a8d6ede607394e5df1a3a80b8cdc075f39840ef73502adb13e607f61945dab75a0274cd07d22d41e3b534294d073027b9e3829affd4b8e0a7814a64ac8ed6957dd377aeded35987c412046e6b22601bbfd8ffe1c94c54adc310e7d9d1300d37e9b8c326a57f8f788c9680a8e14530e344648c9f776501cca0f7055af58c9b553862731e05d2d6951b1e10acafde109be17aeac17d7b7be8fa4620b93a633df9b0b087fc89a927d280e39bfeed79f0ba6c6b08a540fd5926cccff9184cb0e39258bc531c06011cead0479d19a9bf107926eb9b87122eb9aa4bfe85ed709e72c386fdc6a195efdb3c2c7c789a511a00bda737613ee8e1dbccc0d977a1512b9177fb04553c1891223fc352c91951b140e9bfb3b1847aa182ef05adbebb105a769160e2bd63cb2335d4db6947876d8b50f0e68fde6d3c687e114bf72cebbb8ce98c9def1c85bcc579294a119410c3c58018a55ceb9f0e00dd9d5a9106057529fdacf61750413e3f538e82fa367f19e5b04550f2cfe247143d73b8ca8c94230aea263cbdea6941240298a96522a699ef30295e4ac9fa151405a5e545c475d6f6d864d4722d248b5e9fad5500c0c46e8b8085305395183db1f246fae4c0ea40e76be4491e0e5a888415a0b15b2dcbf17b80fb3a3248300cb1d61c8418929ee52227e8dc819d4f5a332db61956e17ad428b0cff00324b1ac30ca7993b73942f9e9dc1b3775414071f066a531ae765e49b250a6c21fa9ca1f051603e0eb0c49d2b1b637a022db29f19cfa983474bd62a0fe901aee7329c23f522f01473f5d764a78589b97f24f06e82acfd38c07d368ba02607e0d0653463b71d8d9d228946999b6487bc271b0f285876705cd02b2032b501086a45fce6019dfd7e8c43db56ef03f298ed1843d8af0b8da285231c65a8e1900d +generate_ring_signature adcdb7399ebe573a2e88f555775d8df55ceef28b0f51d1256cae8f109668a24c 447ef2e9196b56579e07bf5411b980ed2bff6414024f79852d9d4af70a449870 10 9152fedaec98aba200129eab484df0a6366db49094419265a78ddc2b57380e30 b79be97e80cfdd3ab77c6855acab29c655e3d1a07a6e2b53462aa7b9ef83bcbd 69864e7c079c36ee8698ad1b54f66bfd101bf1c835670beca641f9ed37736f99 ca0520d1c7e07f43082c5a46ad0dbb4aacb97e88d7246a60a8ba3667f2458c9e 053d4ad64725e121493158de54d28826417808669ce746b807d65b1f11839a6f c299d75d2531a842aad42ff488b2b76112caf12bbd500d503f43dfd9708746bc 04dc77ff1925242237f287d9ccee87c27ebd75ac6e3a45c357d9bceecd937f63 ca3895a0d1560e67451efdfd8cafdc838c7f1fbc22a4504323ee47e7436ee49b 3a8185cf5f418483607610c29acc87d2b5b0eb5705a25252dc151d28168f65a5 7ccbafce5134beffb22c2906ab4008a62e784b46bd74ffe5bcb2386a990adcb9 f00cac90c0731f2c2eda3e61a2bbfa99255c22990d697a1008ce4a53d42a8600 3 c8a3a2bc427c23b03e1efaba87938fbc06d549b12ed7072cbf9f8c10edcc9c04748044f3a359c9842357ad9150b225764d1296060b68cbe961fbf7e8768ed106df5d32528e9acdce410cafc9b6b0a90682238e3848a9d679a481de980123870fefe61d4d2e7703bf1b26ee9d01aef6e0889d5a0a587c82f736e49ec248e8c505abcade97aa5a018b78d3fea96c17f79f6db7e0b9a729646fec2752eb5e9e460f1d540c660f483f26ccf2afb61cca612f85533a0834b523931802fba1bb5bd80666471106041da4d7785d6ff183697c1b63789dfcfa1c033135da103523c5490154505061d91a00b8362537bead8147e40e7568b0c914646b224862195e9b270b35a85a5988bc00ee6b59368bf520703cb69112d61d957e0c044b48228ed06a028b3ffc9e4aad4ec9a02295581c5b6859b6e1c6398cbcdb00b48608be1ce546035d2b98386520fb3fb03bdf36f58ae11423821cb80e18f9073ec7f1ae14f98b0e016d2fb7d6fc2be583fc4271eb65456630b5665d913f5334456d65ffa5425709aa68bcda0225092eae513213c35390aaff684cbd0c2646c3a20415800ebb3f060ef700a8b17e146adfeaae163489f3b738166518c5aa0555c3a2cfbc2a1a020553836ae870433f1c3c0e507d12ee7d80a2d9ff7b2dd0efd78a0a1ae7f788170ccb8b65cccb581c3a0a9391b7a52a2faf7536c3ef816e40ba7c4ea75b688b240a14cc3ab6dbb2209bc81182df55b68623c28293e8888ff25a790ba8daad713a0ad47e978ad935d2be4b07bd281c0ab2a252038d227cf9bcc2d64f4193cfd3b601b0ead5123b91e69b2afc0c85539f7083ab4cbe0a99fe7a414723bcbb7063430687a18661b7fa620de67e978454f87f634cf14a28c684756cef67135d60982706 +generate_ring_signature 8603bb45eb1799e1bc1fe6038b19a52bfb9ad7cc9cf44446f164acc3ca40da1d eea6a1e1451c63be7dc890f76883f56b1fe5b41921b4e9a5f7757b92df2b6a49 10 278124100668fd040c4da5aa9dcc1961b3e51c0c1c427757e00938d4584b9a72 1e7124d6beceb0fc37efdfb8e2098b24518f431bf8c0b1d5ebe553402c025b99 73f76c9a7b6cb8d8bc3996ce6fdaa00ef47e3c4c0af2fa6cbc794153d4ee0fc5 59bcb04b3649b9d1c6a697ef8465128d7ffa20ad00d4bc2f3651ea8893e75e17 8a996eeddd61f604a74434deba30fdc66efe9a92ec938471b11f88ded7365510 124691caca477ab72bd7b69ab0e991ed6f6d30e910f34997a8c826e5f9c3e502 e80acfb48cd74ae789a6591ec70de50049fdb9319c8b4cbd814e463113e97c23 4be725c440060881b1dfc58d506b5c16bdfdd539bfcf02ad5dea50748ca0350b 57f42b791c1ae5f5dbf20e303be722ea94fd993241ef302e94da5cb017802ba9 dcb72d6d16b55946296733c9638c60dff04002c51c55234878846bc1b6dd1864 f0dacee794abc5fbf38c017120a19c7e1c65637c65b4103fcb8c148af9aad201 4 082007a751f92b55a693a77c0e6199cdbbb85e0827348951f9da840d07cc6001c95cf68230c8c6115668e682cc483e76a3af70bc770eb464d3627bc041daa9016ed9ddaa9205295e272bb3b3ef3172b52ce39338929f7f944e5258798249a30b57a3397b680d70ba5ef5d6f30d1a5c829a82d7119a7f80a578a64e1bfb5f85081b82124e8ea116d5bccb8ab41379a52ab317e51ce76506db98f224c138a845088dc6d6c74ae16e330e74aefb3b358679f570b3c758b46aa733e51b29f3c8f205284b749f1135ec3ab8383c160d33039e4e48aafbb607fdd7152895d3a00a860fac7ac7056cc9820db1f5991de300159dc2975ed101345e8e715230b27ee8ae0806137d03d58dc8c87797498d19fc878a09284ad29bd75d9daa893255ff88e4057ecba8f25a988abf91956cdf876e1c63912e50abfe9dabcad136c8107fce140ec959b8bb60bdacc774764deb71d000e5fcd2bf47f888519e10b5524d15df38090655c1ec3a231268f8ef87929fa92e2362d33e566f7c5046b78518232304bc04909b52c267d5e41e36f998254074c97a56ea2008631156dae267581e93da140d17c03b20f2f53919d896a0b52454bb8e19adf1314f5a9d9ae5aeaa8349d7580fd4c08c1da9209cb9f3a17583af0cffb89c75df70080f6653ddcb20b4b326d308c337636d587f90fd2c0eb13060617a4e16d9ed033bceab9f22bf2697dea10a0ff1a186837eff11209cbc4de5b035274f907ef87d5b8e6f1b3adc9eb8fdf8200aa04a2c0d28000e0feadff43a9ad985f0b8e16a124e4993254ee22869ded70f0044e26562145d155ef60f77aced604aad361b8eb573f7c504398b83ac25b25c0d6ec98c238860462d488e8369ab2a737835d476dc3e7b7f811d04858a9f346c0d +generate_ring_signature 393bd6d7e3d18366b43c7f64f94c7f3e803c05a357fd4c9ccab41f235545ee23 ad175b4b3efde3db1a37d5860d51d8c1e71bbdef25a0d88f29fdd6ed07309665 4 beb4f6cb5a63cfd76a7e66a78ed35d788947613b90e7bdc25ada761b87ea430a 0e37fe8e05c17b8ff67e4fb79a6849eae9631a41fccecca28618418746d0a9ea 087f54a5745a7fd38e45534fa0a878b582fd7d16e6705e0bd195d9c11ffe9638 3c91a5cb9c704c6f70c36cf483ff5dfb14b45958ee50b244de2f8c60e3931a82 c022e13c7fad4dc5e547e92919763f8276ba20a839ff4790f7423bfdbf60eb03 2 ed28c9ab941d91f81abf983fccc22841db7c0171e40b8aa59d41b3cc4f618108d7c91b213f6eb2d5c8aebbc41d5c3c2571b3b155cd1f2d85d0c961e7e6d51d0a10a393ad2198fc4407b86de23ded44f86b607f094cba653aed28f6ea4137eb04ab06a5b215940fb4b0547b4e1e416d6925a1d94b58d76b46c31ea3ee082f400c501945b17f12f39108739e2996f8913e45f46c5950fcc13a150fc9936e723207b7f38c9d3059a34efc08cf3c74e4c618acbbd4166e5cd29a3841f1184260e40372b948a54818660920bcd731cb00880dd6daf963b51b708bb02eede2336f7f07ba366256992311d522ec0c9e5e86c8ec1bc48278e33490670c90daad6970db01 +generate_ring_signature e565abb91e365168b01e67f4a3c568d32789fdc1e6dab2f448e9f38d091a3d4e 9408b7dd8b3cad102645c2011cbfb0edf8b012cfde69948fa41745d9b2e0d375 1 353fd42821e9933571099b5e6cf214b0f20f3b6d012e753818641c618d20defa cbbbb2095f92ef10834a02629b43f7cdb9c44d638c6972b9030d793472a63207 0 dd5534feb2f07e62aed0f9fb8ac5abbaee801bead110e3433199b70804132c0b2527d683576fa89542b92bee0a68613d7da2446feacc3529244ca12087f23501 +generate_ring_signature 7ca2998d7e7f0adc4ce35e5b474e47699d5460663aa16ab449609fbb00d500f8 7fc9c544c3e934782785e9f73fe032d96973111f922034ca48bc825cc27736c7 64 483168d096ddf7c014a148e53be2d680079bdf94ddccd86b087f8b33d357b7fd 86bdeea8a6d0fb1d4c7d5a173d5468a55f6787f612cc997039307ad56f2ec6f3 c60a76b900e4670551e578ad687fba402b25416433d72af5291ed69e630337ea caaacc734c97093c58dd64102a4246b2a5840c1c813c4e338394bbac7dbebc75 1ef3e4e217cce535fc694fd8c6d3e113914f0a519038690ce7324d06e4d1f8be 916111041980474203cd2afa9043cf5d9c3d636fec3498e1f6b06115b668bd1f a09a5afac52945be31fc7a2e7046862c8d4806649359fbf8b5f7c69628b4f167 6b5d1fdc1a962a295746d49938647583ea088de7f8e241497fc6b41bc9121e95 0400eb6d12f71290e0bf2e6af6ea14c37198a7e686b63e0569f6741844f0db28 235285f1e1e1a1bb6daf5820e3f9db2f5ebcf8a3c1dff2a988953d60d77c2c90 847ebd598b288b0052a82a35c58fee0e477556cad2bbf15dc60208ed14712b08 a8a54162f2a6e4fd177b3c727dcd2034643d6a43970f2bb9804cbb5f24366b5c 37fcdd2a3b035e5f5f4951d3ea120dabbdc219d9a6c385d03643a7037eb26af4 c3c68afaa4e7cc9c7cda7569cd3b0e880fc2b39e418383c4f7d6aede362acaee a14729c01c6f8c78c68f4863e384162a863d304e780cf1d87835369d6c0f4c42 ba8de594a2ebd411901147db78dce4c6b5dd35dd2c85ae6ce2e2f48f3e47c122 f9d0351c1c41aedcc6899d3c963c410b3e33510f747c1a3cce8ab1823a3414ac d8f4ac38ce7be2c45d3b74880338bba84e256042ddcdf30ff25089726edbcee9 4b33b840fe9c767bc2fc9b8f989aed3d8012edc993776b1d2476f6542ae15cdf a7b39b9014bde6702c0c7baf60a3245a70f8db5abac0fad30bf98c36d8b201a6 3905a5aa52ca2d61f49e770c53c6663687c2b6565736b51527b8332730d283b5 e9932651c0ae63cf3738fe067908dd46644f1d2235972e027beec99724d013f0 804f17dc20b27060756c20a2e3b1bcbc8fa01db1672c7aed51c20a868b0360bc 138c0ac27cb68eac5dd51b8f75d1c31a215f26b14445bcb3642fd00a176a6639 0f283696b934fbcf7ce30f78f8664d8f28c986a202a0c340b2d7d5782cc7bcee 36415f082045b4c3b1b0f805d2e6ade990723d39db0a53cdd723d10a255d41d6 16a9cec53d35cc2861126070e57d228c20f3350b93ea4b46d8b62d01f3209f20 a5c9112fefba9ad954476e87f670af6dd626898b3953a31cf3ffb57886f10d11 ca10bbb819ce71f840e69d53750dd43adf4cdc2c785e517c7edde54034a3baba b1f8df73868c6f39af5d6a71b16fae7c882cfa335115f62f0b093873c308d06d 557f9033e27f5ffa3268c4a3f3fc94dc73c2e3bc70908888c3644143503796b3 f2cad5e736f9839d99cf515859fb8464fece1f85450d5bd5558ce750e60e2942 9becaea439d210fd173a374e51586d125508c458ffb0eea0257416ae62813a81 a56164a43e06f4da3d2cfec1db2c1fbc2641b9f86dc4869e06e55f0a9ffcf751 c8a1abd87b2837247515c9736804424e235d61d6c41f90e748e72859446633e6 210074f11031e5ce0d2ac8cf51e9fc7981155fd1011745ef1e74b342672b453b 8fae668e0c5e30c77823534bb06c660867896f016907fa8b05f5a18452c7714f 0e1a32d6619800d7c2fd5873d124c917752f058318d43b66b2512aa1667c5151 6803d84d7b5a3574328f3403b2f4a79b5262aa31e6f1c971c179c4ec2c8560ab 44c61ae79c631193c330a66d64dab906514cbfec06c8bdf57d5cdb1844b2f789 4000fa60a283326fdf215ceffc849f6f58cd74b60bf19798cd6e54b6e3d8d06a a78e6293da0c64e26b788e55a0afbd8e4273a226a514cab6e3b4beb8d214c9ea 12980cd87170fc759380afa60290915389ff1929ab5de05f1e8549f014fb2b44 a1b83d3f280f162cc37feafd51e90cf6e1da6c82d25ca9ae12b4acbee46a949f 6cded174a9447758824a50352936a539d97c0977ecde4eb9df83c0244a44b89c e6a9d7f60b9d6f367befcfb0578605aefffe810cd55081232176f23cd4901ba1 13fa490eadd0b74699af3be95e1f5b2e488826a104779877768d5e10a506bdd5 5ee53f2482db49cd3373a9195fe2067e018a081189e9e0ba2500357fee9f11d7 0cec68b56dbdd3f61c5b5f242928af02ac0f1bdffc5461b5855fe58b582f54cc 6c17365304dfcf711494cfeeed75cf5e121ee6b59cd67219edaae7ea07db4250 6e801ef00a31b59ad896ab91c68c67ec24aac24b03436c7013c18df945666875 8326bacb4818132db5ca61d73897c1ec0769d9833b9ab677c42bfb9b177e0f09 2b460dbd272c99ec3ee39938db0555df0f23e8090da8e115a912e15d51aa913e c3cb48550117623033617d274569087b44d1da2dbc0257bbf45f35e3581b536d 110d6abeaf7b20668c3891fc61240bb2c3d67736f5e80361217156a55e142f5b 79e8696c0f50252f81b5f218eac91894ea15a95a48ca68594de8aafcae37d7e1 7308404a054e1005bdf9eddda7a5d0ac0b395efdeb81a655f4b8e8819bdc583a 38239bcf14fe4fdcb14d6943c464ca8cf401cdc46d3d212b4f1ee52161e3b4be 3a8bf4e796f2cd92a78240df064c3520f4966257b89e49ffa2ac6b4f70322a97 b4310494a59ac8bc4ccf06b17f70ddb789b387215fe2b73d180b3dc1779350ed 51275e0ac22a92d506d12e7969e28f060ee7fcdcaf100134af046d622ef7c8f9 d509aa1c71eaf0259e74c41d404c25ed03fbf75f249214b7525570eee1eefda7 388f6ce354823892c1cbe49d7fd79d8c62627308a48291af34e16bca292a6ac9 7831cb8fc935ac1eea198f48f6632475f8feebf686406eb00d43f4054299f8f3 d62eb32ea87d2f0efc686cf9d79d111ee668a65a48c3ae2606bee28d8dc54202 15 2ee2dd1fe11afca75e34f980ef12a9b69c39a5046dc15febb1e53cda653fbc0cd8b779b0bef68e2e07caf8793434f36fc5b8908e2919a6231728454a7c31670f0660affe8cc0e6dbce8a1c5f08b1a9ad15b0c25e635c4ad1a59a37120300b60522527f5b4907cbbef7a739cac0edd54cfba16b42ac201fc22b94b9e2848e790ff15b0613a0382ada2cfe671f6929b7bfb032ffec6a4e6f389ebdf8c1167dab0baa5397e4725165e21644189c43e61f95768a1a4cd72cd4a299b6036f5325ba043fb0be698ea3a76126e222f5f656f858bd1c5c3a4fcef55d55a81839072af5044529efb2b8fe57875d426615768435b89da774045cfa7c43ab48256282547b0afbca3cd889d897b42daf38680b5e505bbea6975fd83a5f92c6ceca2c2ad48d04b5be5f14882c82f255d8265238237899a22c4e8d2fbd94ff7e8ec7f7ca12af03c13c28a3a5e94979e158482cf922259c27c96c7d0e3113af8b699a74960ecc09a62cd9c86c313000cbdee1411d69c3d28e12e067ccfbc78514277294a80cc7084680a24893e6ca23c9c43e31c7d3d3817933a029bb236230de0e0b918a44a603069c7cf48ae8432f23a44c75c38161168723e302e9b22c9d7157b7f00ac7970be3eb599d09df2313c0c0bdbcb6bc77e8de48246d7a4598ace9df80e055e96e09b4b6a4a1c826b5876c5965873647b7daa63665058c45dd5ba651a4b518b0270f018c5d10476e7ced2b9f6eebd132b37bf3d2a13a55d338d7c5dcacc72f4dd208182ccab9910f078a31be6eed73fcfb5d95fc8a0ecc66a806a7d0bd8e3b0197077d39621969d2183ba266f22b145758c4d82df8b54c7a2c82135021a8dbfc4c036d4b51f51011b1a51a5a0cebd6a511fce1838d330288d02d0f5149adec4e060a0f644b7ec30aed5a9268e1ebb46f52649ae96ed48ec947f32238d6802b0b890bd664500ea6eca23f74fd4063a41ce356c287345a04de842bebe17658de4c33049fbe98bd5bd68ad9d744dba870066d96658384e21c5a27284055c74b40463b0faf29c0dda1106b762c2d4633ac7fe6d55783f563c51807fc1b725d8d5d92450ef05c97a28f49dadd28d260696fc8e3bb4cac4217a5665de8041c0efcda79420f9f58b372437efc4a96afe6a2dcce3e642648a270cae90fa4117e75c605189e027fc84bffbcf3cb6c0304fbdab524f50afde3d0e760a15f82e35eabfff4dcef0595384c3dd73815be1902860070a92f9582c372440e45c4061fae081f1738dc0679214a61bae33a47a1cf4ab43049d271c142b02c4db231d6407d7f047d5c120a099c81deadabf1bbb59ddf3a7b30bca9be16b98c92e28390c5212c188d719607348bff51578cd8bb113b685fa15e809aff2922daa9249634608982a6bc60670eeeb4693cc5f4f2954e31f6c1a9db4b359e91af70a3d6a859181ecc14651396047a319df55fcac89d55f28bef3f33da75314ef9d782c617502694b7d517d7530361aed5ef08203e4ac01aba6fcb8c1404d41f52f8e746a0c91fd8d89f00df810b9ee016de75a61ef80111752a39b413ec940266a9fb120be4512f30a3a622a80d0d8a266c9be3fe8b742ed8d8fe2c54e8eaef06677a754798d6ed807516f9940306264a77c824b064f0ab1a47bbe197bbebfc5e0277dcb54c4415e35338d21e0c933080097a69ba5329e52f9c927c1c143c68e0eac2a3f139b37ce8cfc761be0aafa2a799b8bef98c2aff2de4d30606d031e490851894d9d74b984127734b19024d0058c98d86a530e49c8beeb714ed3197eb09eae8bd8b9fcfc0d6cf74b11300a1d043ba357927ff94392bf01e60e021a540f962afb1141902858efb36877c0c11dda8fe449cec909372f5f4784ba23d8661db1bd8ac1e30e0c6b1427a60e00a7eb33b1e0851b354f5cfbcc0d561ebc9cc53a443ba6240cb15fd7d56ade3130032f5038d8529d014d10eebf3e3cd57e23a0da2d672b3556b10fd89c1cc37ef0c6b6181e3dd4fea0d276084744e53bba3e68b81f851d1d9e5bd7130d3579faa0d977c914c6aeaff3354a6a197057b6737c757533ecd9b7f0142e097304c014a04ffdce71633d8f8bc0547a3ead1cfd5e16e23767079cf80b12fde1348b586430d6b207f71b66bf200a9dd7f4c9d16293500c8a967242beb8952a2932f2baef20772b02152f6baf2cfb847e1d311bfdde2da511d9e27f84ce338fe73e70f16fe0331d87c54e615cd3e7bcb10b58dc8d8d211734c952e6380a3e06fb3a2486afa040893af010bc2b1449b316f28e0f7918f5946d561188fe264a131b06faed5750f90aa4ff1d6bcf0ce5ff9347ef0b4fb53d8df66bd4cbbc0072fce0b9193b2560908077e7b9ee0a18011947ee0a1143b87ec52c87d418b909a7ce5f11e0d3e410e8104337d687c3b53d3eb821d6c7065cca45f358739a7adb4f8b99fb4d40b6c0c833581c60fdf04bd10a03dfeecd68511da41456941672a447ffacd7c75651d020553f976558cb338977e0aa2a1b26a722357dc86fe7381b77cdda3cb164cce0aaff5d0d9e21c46fd188b378b2a9f460cb3db1d934c18fb75302effe56c8b570e8450ac5d48b3bc4e5be818c1bdece9f28c295ffa790a6d29656e0aaa3f3624023428ad317749322b4fac899e4fac6e1e36ddc42083579bbd7db80b750a94e0054fb39cfef6b40b1ad03fae31f385170143c684c3925d4c25c6e8d0cf2ad8a9012733018daa75a0dfe6dba31d2d5af36833dd33521219c0e5fd921ce6b728af096284cdaff9e192b9ad120192f0a628f8f399438ef6275542a30681622eddbd0f19d08b98ee131d1303ec5637996f5b83f0e3748b84fdf5bbaf7d3f911a60fd06ada63a5b60d779af22bc9b336cd3adb42305e62c761af2957f184104073cd303f64db90d5228769ecec72c4301a7b1f68ef14e0f6533d8e2f4e460d2cbb79a0a8512b7a6ce9991355b53fedc05b1c696a10bb0c69fa99a287de4243e0ec8490322c214baccd20bc146fbf5f8e5ee85db2afc2d8b834bd2b086e28632988356058790993c85d636c00f1512a80eab31eaf1bf0fd481ed8a9b8f5945b04289860a42f561b24ce53793d69934a0fbe93a837e18f4041ec6e38b620db9f7dbe4ea0a408d4187eb198a3eb31cfcf7d20f1446f78a412538ebdc74c9174ffe99f2180eb179a255caa1fcd072bcc938a17024e799be4276fb9964e60aa9d4e465a0e20b58eee935f84c3e2abf731bd6968a39bba4af52fb30f8b4129f187bfcbe9a4107986c5acf2327597eedcab62924bea6087aaa162807b77f73f1271f338571f703baece5d2c84b9ce9effa23f40c6562933a9e63d4aefa71bac4b201f1229ce80905ea987f725f3818cf3112cd7ccd835059932337fb688feec3e24fe9160cee0360122164528078155d3473196d63da43da105a3edb8dd56126f5c00407290e0600d511250ee64f9308ff23d92bbf720e16cb8099cc1b414293625ec06edc63093c84b10735ddb806fc425f1126ae5c0b8abcc0567193c9d55cc2b619e842ad0bc4a519ad35ac88bd3dbc3e704614d8582d0fb0bef4533cfc15127d8cda67590155cf69ed41aaf9a71dbf83545991766cd7d68e47368f7e20af227106f1bf2b0216a8e199f9f0fa3067d9387e0c5399f0bb6e6bec51749d489a6489d8de4e360df1cc51722fcd8fa18775e05b9a5793961fe0db7b5826278aa91ddfbe15ecfc0864cb0b50dd8991b554bbfb029c72b0fabce898943c603d50eda6270bbdab7d0bbc2cadea95f67911aab0e151cef5de98ef9f0e3696b52926f26b24e2fa9be40cc1cb5e8b2114b38421e53d083fa62d7bc9bdb8be7b17bd8323685628caa53005c93d4f4a0daff10d166df97bcb632b9ba2a234df630792002aace248971eba0144275a3e15b7fb41ef51365a635231291e4b6ff2ade0b4123c99555f644351003ba557cd4d0b47cf8a1bb91378e0fe2c90c680d812816b46aaf6f8ca7456bb0599f4a76cfefc5cc954534fb0eb1a2bfb6ccc719099c669c49bf02ce660e0b90ffef7308ee778a7a4acd9f561e983417123ab7eb62f97f4b89d48f6150420850eebe38a49dbace14c90e531d837d994697023678c5d6ebcc49e1715cf03dfbd0bedb26f477e970356e58d4b5ac533ea479b0668d19c4e6a39b9fdcc709063e60e1b16a9604a70fc71afe4b33177c372085663a941a6c6f6077e5e521be105fb09d5fe92b1650cb1d8cad7ebd2df6a5993fb236a080d38b6398f82e36ab01c28005392a00ff0423fceca664a23b529de58994426b98eb259e49aa85435b392f50c2ca96a3142fe083696ba54979a168cac9a9e89a254c8ac610c938bb897050e0a5c7808e94dd1867a34d2222e6797f053b1ba32c4157c0f040ee2b46b19c9d80f5bdc8788bb88c3ddd5975ace0881d39fb23ded2300c62b0bf2793a36fd92e3006aec70a7159ed709ad70f3654273c2b88ac5a6b73ed11b0e34aaafec01e3bf0aadbe3b7958dc71bb07a18efd4a939ee3b65d16129777bab2db1f9575d126a707b398971b3afe8fb5d8e4aa830bf7ebd7348155fdd7ea62d0c4e1d21ad810950eac247cd29ee894e2061cbd3113465d2ae91052b61499cbfbc75622157b9d200487aee91a452d767553fd1eedb11027c612e2494b23a1a52d52e91fc4b8fd8d0492c0dac6c0b1d67dcb28385a8cd72e85b5919d1728293ee29e711d60bfc7c400ce7c2a0cb0ae9e4093d11955007dafd6ce1eadeb1a24377c830bce37036636069d93925aa38f2a6d8b6f1f48f999842010f5e01a395726c9e5734866e71ae004d444a58acabca849265f18f749a967b7fc17e56a49e46dd3253f5bb16cd3bd0ca6fe23661851068f0f4fe15f998e2ba86857bbb451caac8387f77effffb22a0caed422b23792a5bdd681bd97283c2b59df097b7fa4ceb89df1ff0ea7a1de080d1ac6122ea8130cf9d5b4ee15cf3ac689fd019ae9a0500cd6d7b17ef8d8215d00b0a40d2d85288f0aa3c94caba8deb695c4dbd6066daf8eeeff9081e262e3640eaa5aab602329b4c7a42aabf380eb804fd56dc475d58cf9f6eb6106582569c80719ae734362b450fbd0d51740fe2c44f871cb7221b9a9b6a7a4f281a2a28fc60345e9850557bdac3a93fab7f302106ad2ed2cd0e4717c79456b69865959f98b094331860bbf05393e18d43038446c185b728480035e1ad29652eb35dda43b330b361b63f34847448c5db957bede36c009115bbe7151b8ce5c7b7a72bea3b9a2042e0a907d9137581f3341b77a92b4089b01031097f40b1e23db8818686e1d6a0c827e44ed159fffdc44b65cf56509cb539bef77ffd8bedbbb0fa8f2cc846385001ef1adc1bc5fe6cd7f31b7409b3b07b40ee67b02f2c90d8de7c4dba372065104df9a1666c09d7bfd08b14a21f7cfb758137e296386ea1ed73eee68fd359f1e03cf94507002645e17c60182ae3462d44a9d9aa522156d7c2173d6731bcdcd3e0a17ef4c8ceabb347fd59001815fd3cec24005cb9aeca0a24bcf16b5b799c14d0747aa7485aab69c7914d40e765b7296556b470feea7e497b32e26387a6134fb01f170aa9899aa178219359b0d182e14cff5f7fdd3810b8f17fdc4c88a15364a0db47dba15577acf0e1d01e9f45029364d224bb671a616398f9c101855991a4604547649a901b4006a34f5b46321a2f6ee5f2df205c3f957414e39604b9272380760f39f1a44f9beafc65a88c853c0c9ec755dd9dd0eadb572d00e994a48e95a04d95caf2b6416bb39dbeb4ca5165d6b7a6578d96f13d922c1a3801c03cfc5e30c +generate_ring_signature 4e6823185a8771ea63ca39e1aa4dbb7578a6fba3da566cea485c82b3c0efc115 0f0ffd85c0de4993b1c69e5b0c21a3811b49f6c0f4097a81e33448c1149afaef 2 cad1c5a19a24b36c12ba2812652309ba4234f04ca20812ef2d76405d7740afe2 4ff3063f234d5d7b4717b2c2b9e4c09966f69f1c1083099420e37baf84d8c9ba 071ae9ca27b1072d58abfc39b4bf143d61845202748c25189525172e1d081b0e 0 32b8857f070d7285b3c2b43e307d593fb35142f4111325d1f98f83074523f20455d68c886a802d0f84a38aa10bc4af10679147da4979eba30464ab408afa6a0dd2957b1f99f00cd63dc7eeed770a51912350d65c58013d159125bc2f350413047bd081ef106e72c2f19538c9b7bb0202800bf3302d9cc095da9021d5836eb20a +generate_ring_signature 91cbc9e360956a6ea91cb542dc70d6cec885b8e2a1b763a3e352389041be1aa4 077849a2880ed750d786a1f1f0a17cc01c256f896373c465779e5154b3f7d955 113 1f4bdae2817d67f27dc0ceaf976717df975fb8353daf7fa01fff17ae37770146 fdd656b891d9e758069f81b2b2daf433fc84b3dc8eeefd2d295b7f5ab9d75c92 0859b4fd8c367923f1061c416b7e8e9ebb99e3829e50d8d21fa659ca4078330f 7e2da8ad81daf87ae03a1f42ccad8d8f3c8e1d4f75dd89416119b1c71e8a2416 c32fdedfa2464148a62c7630da8f598ff39f13628b221142f7f2d5b61fd5efaf 76737534abb4dfdc6ba72c40f3b406f9cec3a51fba44b56284fc62db05394e79 e4e6d4b71a8e963a256a300e4ecd265dadfc6ffb1fb7c46a68b9badf18e3e4d0 b4ce84dd04dc6ccc4b85eee0925825b4436237ec68b324f66f5f9684de4f66ba b1c379e91b9b72abdf75c4d25174bf029926cfb3498beb075586c9ae3cc105e7 2267492b2a3131012bdf68b84d681f03824aafbba96b2b9735343abbd74b8cb6 00ae5b1f0c470c254facb1b12ba8ac6a31191cdd01709d2ee6b37c1caee74cd5 bd859849cad2bb97e2da3908dd8b90578455599705674fc0016ff46c3549fea7 bb21060aae8d8503dd930512d7a080a6828223b5fcabda0c161ec3ecaae3fefc 66bb5f6b1c081e0393d2224ea522cf385115dc03b1970c2e7e3a5c72d8e7bc50 33cc10d158229e8603dc7895a81a8b6c55edecf6f43a506625074261d402fdc9 43a36814c2c72f6a273fac7094f9b00124e0bbd332b9ee61ca41ea231b018816 1d7f55694bfd332d08164d331fd276e56d365f2a7c5fcd3824c77a6809b65385 c2eefbf1779a3dc88c493b6512ac8d2393b2a65f195681baa41bb05e2e58f689 981ed644db662419628670953465ebe2ac3997ebb4d8292b24d2ddff64fdcd86 123dd138e0f62fad60aa9656d484aec34ad87fa623b69ca4f77f36b5b903d71d 29976e6ae7245518b0565907725a15e1445bd1c0730b2c98433c47e13f34e4ad 541da01c43ea8445433cf70817e7fccefa13245c1d6ff34ada5324ed8dad3d04 82a982aae68c079dedcd3455d266bcef67e4d39028afa0df883dfdb4b36ce979 7561dd51d46d617281a314a4e112228a6abc1ec8ae26f59f5d691cccbd96663e afa6f7640f981e3d4ed2b85b74151061aed19ff5390c0ab3791cccc1a21ede59 54c84b57a8749841027cdb4555f8a8c03dd9763957d801efe96096ce6a58a1ee d4186b3f8ab4c4fb9256161f632c9fbae63912909c22636357b001ff34486de3 e845c35b7b319a99099f122f0acea3e39e82bf536c9dc0a7fe5875d6b0739c74 faf9769154330257db19465d7ae0f2f211f730e4c83220c48d7648596e91aba5 3bd9d7b85fc7ae7d7bece942d0ead33b882d0c35d96d491eaebcd6fce5f345da 1cdbd85712849004e3e4069802d7387ef46a4816ffb84fc626d5c1b9b7a60a23 d284ec26e2df785de3542990946152a736f729a787913dafe58f8745b0d263db d23bd5efd82b47c8d982ed9e1b4dedd39a3ee3badfee703dcc33d3c32b2075d0 f1eb248b21f1a5abe42327d1f203121a0a113fb1cfd61281e639aca1a15798a7 dc124febbf2b8b75ad19408ba905ec2660f8641608048d4053b137c18118285f ff0a4193b301b36562d8c1ffab7bbb9c62834b0116a48b992a85c1ebea6b0a81 8ebd75cea0d1f657d506b3cbdb855a9db21f87d8e0cbef42849383f093b7eaa0 9e254d83820a33fd0971a3c7efe2ca64c35a3d5d50ac3884bbdf7d6f77d7dd5f d2a69bb8e79306446c6b2cda1ad9c3d1bb571720b43e55491abb505b6aed4f6d b60e0fb3e1013aa0c1d196cb9d2fce8c1f201c0a013e6634fde756f6023c36a7 262e4efc7730fa981014d1567de9d429eda860bfee22ede1c9941ef94aaf2159 8a40d305e04999c66638cfb169b2a991c0995c814ad978eb9f1e951bb3465c40 088739da61d37ad97d467ec8bd7ca4139b311f7f8b6cea6469aa4e8a4972ba51 6874ee85e75171ed81a59ee5030a8f5af72373719a8faec2680c00af75fbe0ee 0ec37865399810603eeb33048982fd83b25e9e1fcbeaa91c0ed06fccc47c3f80 5ea898f527a48bf7a4fddf87f26d13637557b6214da4eb1b0e4e0495111f9f36 b1dae8a393bf959ccf33a86ce48214f4250458661892fd8bcbcbed768f792548 25a26575e7b663bc898396ed9ccb961e9bcb00d107f0662edba72d2697a78d4d 927ac8a2052c2635eabe2602f67d1e024ff0f04dda5a09a1abe3475cff9b4f78 ef6c5437357e0b31c6a3ddc3474ce246f49a669ce06acc00232a4cb170e0b7ac fa6ec2d86e6f12a008a2415f6bb6d62d8f4530b7ee264e6335421b464033b6f1 77027c9a7b41d51ec9851dd80e52221d541330c4aa65ae9604bb924874dce56b 6229ab07ca58513e9a5fc1db57e4c1bef34e4007206e440b26707594b3cf88c7 137f3bea2550d2307d8a733cd7ccc1c6f80fd9b4f307b75583193c826805588d 48f0e380c36fa89f755054572914efcc6ccb415d77348416a33d5512f8376380 4f5e4429027959682771f088b7101e1cb07d0183052627e44191ab65a4d11e98 d40ec8d941cc48b7edf706b778d736183e05856d03fd929adac4eebf3268cb68 8bfda414414ad854211292ba745ee96f200c80e3623506fb8525f20fb2271fc3 1a615b1e5534445656037b118b8b7bd13ae02ae23436b4ee338d92d37c3d4896 9bc163d094d4f87154cf3593c1edb359a903dfe7509524d9335623a29c656663 eac712841f6694776756f6a89d043bb062861a4378cfb13af76d301c71cbc6ed 7ef33bfc75e86390bc080acbe35bbbad5ddc51da165009bd0daa10db962ba996 054e6574ef55c9f9e94207ec107352a0dccc240ef20c033dd6d1182f7f62909b c32880fa3ce928700ac24530ead3571072e12b03a49b26ef6b3d35d0f4c59d53 b98d0748298b80029c901eb4ec4cd8a436e83c7e01f440c786fd16e15107ac49 ebb0e6eacbc9c5f35484faf27ecacac6cc694ef4b09d807029ae6f510efdcda0 b490c1cb481c3ae08a73f63a1102b316d286d93852adf5648cb0dcb6818126d0 d4f4da3f84eec9c39a4c7cfe27a3bcd39cb9f291005614f1487e0f45a4f1ec00 aebf45b322432696c7b67d3cfc0aee50ff8e40c51ce8cae8a626812449d3a54d f606c7fa4e43f558fdcd80fe7a73dcbb249056b368f5a44f3dbcb9324b503c34 106f85af18b3197ad3351fa8fd8425d260e7aa0c05918af757bbd3361bfd69d8 32256ba58423e1ea10dd4f501a5d238f31499624872406fe500f498e66d008b4 d292d6bd8bf7432aaf9c61d7dc96c73f0dae8132673e85fd3aac5fac1a93431e 52b24f1118795f20e46b90c1b047ee5ee2d3652ab2bce842a9cec609fa62aefa 449fe393def04380cea7cc4c3db08cebf0482f2ff57127ffcd987d92c3fa6163 7a8d3c4d6eb1516f2fe284e496574b3f189b5015a489fc50e54a9f414dcc1b0b b39077b6c733c1de5db7ff8fb3873f024b40fca72f9cac1fc08af28b18e1b932 0b203cae9dada2034024effca44f99b52a36ed00a2f4b67f03a4e0ef5b7c5dcb bb56bf108fb574114fc98c31e713ddc445cbbeab6b01d4e568dff3bd169fbe2a 059819ee8457141574c6dda4edeecda5e62993b800bb2148b16529bf6c0a7d13 79688125c0e6b72e56ea7872416735714947f586635bea64120945fa37cc59e4 5adb1c6cc2ad2431222dd58e8a6f3571063b3ef20bb123e3a1071fcc60ecc9b1 cc3dc8f1ffec0e5f45293b94a5a7bf3ae58a4d4f572a9f5ba8ec3b4f34138369 3999f2af3d71f38b14e98dc84e2530f037e83e7394bced0b6d681ac73029e563 67652807e4ed16e014757a70974b4f8db9e6f6bfae39fd0614ad1f06e525c081 d3813e43348dd6866a69fe1755162f250e8e3de00b7ff0060cf6475d2445836d 438dc90ded371c003c1038b170b925cecb796c3074ccbebcb98e3168af4d39c9 740cdd1999f72c2a29922d6ea744085a4d9f104636ea931643943ffe673fb15e 585fbe2386bebe0fa46eca5ac524a3cf4665498c01929b98854634b0a0d113c3 de3706dcad8c44c755bd56ec422c99168fb5f8683866a13d7cf8c2145a49ba38 da4157d8f82d2a54744daed564c987c996120211b30d482fc0bed011e8f94fd9 721a665b9b6194d077108c0797031347aa6b2d6183812e890ead8f7ede04fa99 67cd0f8158601c13c2f85d32fca57f324985a20c4766cca265693c77d87d5143 9e8803213f74d6e5869fb505e8309245889a807a48327769b8476f16992a8198 eb535a0a39ee12899ad81b8327bb17194757e760e134a0bd34a38ca32b7fc9cd 1a94815e92a49195b30d5c6d08c6011f615a876d9f84562d13e20c8796bbf944 ac695022107503164727535604598ba50cfe2c8d2555345948521d94851caf8c 4819c71ab8e12d613df31c3e4302178dafdc21ae33baf2ee3a9f2b161aadd640 c5b8a211af4dc95ca5ddb7554270215cbdec87c34dde54c484e1bf8ed7a5500e 375f63727c1dedae8a51f8424156f97f9ee3b54301423c8be049492638476587 cbe6d761149db8e9231283d7d3e27b6bb107068127e49ab0ea318ae47a52d941 d78379ac13a4e4e05a8ff9cf451d22fcc16d17bf4b06f66c55f8da8584b08fab 6edfaf19c5dc75df69facf72eb82bc2c016b2650f32c4afb731f7f3fdcdadaff 44d3fe29429f69fec91ae3e2e33b227fdc00a8ab78e3510b2bf03322f20683a1 a859389fd668d9b8a8a5fcbd3ad6c49c751032078462fdf6aac4e5600b8bd37e 6808fb004f96d638abd7d164167fff230265214723a7b65344011667814ef5c1 6c3654047c65a6312b5f785a9246e064f1d223913b7e11db00bba27b8d6e728f f1383ffe364151b77625632904f34bda76a33df6561c30d591c30cc492b0fc41 6cabd4505bd1a98ff5f7c1c75d7d4e328d691510f4a23ab74098c3b54a08ac03 c4818a93d8e531cd4534694bf915b7bd5d65baf66976fea183e3df1ba69b65ed 4ed680ad71299f0ee843625af25736e020ed7bcf82ee266c0e34537b1cbbecdb c8a92d1bf5112f1781f2a7a528b196a3bf4ad426a48c30174be6ded892be8be5 6ad5557af5108ef9b99735168c733031ca356c49e9a4cd26bc6f3a332d9ec219 5c0ecde6ffe8646985d4552fc134f6d013b984474f29e62d6626dc941c23cb0a 31 e09678c6286e175d47fafa72f5d04fd5833415510d8994a41037084e99f7eb01942be52177cbe5938cca162d353d5531c612be695f341b69458f04e6f277360cda48b874dd0f216161c87af81f450c42d3118f9734a61c30c0f73fa572ddfd099feb99360e6b20c8ff24609393b6b825132b67df31cc042e0f7290ee727f4802ef838bbeac63fad050ae612dc8d7f5ee20a0dae75dc43cba61d88f19c6e1fa0eda384f429d11996ef2a7e669ebe126593d4efd86e83d1591a3fddaa42544f2025d0f33e31240ccd925cf1985abc614ba50ec49430e89daff56640ac587f3420686af1ced9944741c869ba7ea50cd6bf22ac3a1647f015d471aee150fdfc0560468f23a0040d40ff7a49d54320bf02c3361fd8d0188aa979de8a5856d593c9908d726cf0acd5e84a59b168b20ba2a7994b778fe5affca0322ae2a6111bca20201dec905b7d3317b33319cbe00d4718c64db3a11d6f365ef311816936ca3052d0716b57c354fee3c06d90eed59577bee10758538e4ce2a323a09446811dbe02f0c9aaa76595555977fb03073f1d30df66429472749ba11366497ff6a658715f504abe5f5ff5845117efe1057a6c6321c8f851ceae6af9f4641c0e965cdd5970005d32a920722b2da3b8ae5d178f5d3fdc5697543934d7be637d52aee1e06ddbc0a6aa682c560baae8de3ed965a5ab9d8dd337f60c9e577db9bba0f1e6f7a87a204cdb74687b04c7dddbdf079d29b92cb3d21182bff1d83fbb72b54ae7a598d44023e25e1abbedac7a2dc20c303b29d931ac18d80bed543803d65dd469febd9900a12ca20491c6c1c6925f70accfc25481f1ab619ca358a2ff8aeb0ddd7c465890244ab0eeb0b4b6ff35c0a198d25b679dc8000e15a098d02ad71dfa42b39839607213b35cd7d28f47b0d15eec12c1d1d6e0cffadad7ea636834f244e954079860985b1cb9aaf8465729623250c669c481737411bf4ae4c9eadb78355c6f976ba0d4d5cc6d696186780bc7f53941d7fc6c50715c4c975ba2d414ed065db028fbd0e44102c3004cd41ec297c997ba05cf556fa077e1e3eb27d661ccb9479da859c0c6e62cd1642d5a8418dee5c5ca3bb72d9d9e77351957f74022c914c58dfc0ef0d136dc631d7a2f9419be922277cfa8a99444ec946f071899dd973340954612b08ba99d140aa2b3b0ebffe0e0858157b33fa8aa635d1120cd5b4f091b2f7d4d70d752d34ea69b42f9c24f8e20db8f8b48d6018fa3c1f68e82945c3d2d7c75c4001ac6070e9f37649995d18f823f2a387910959c7e4b45b52f20960f42ff335f40f6b29eca81894b8327dc340d2e349f3b89ac3d81b899f4b3c871f8dedf0d32103eb819e999fb0d90a75d71b9dc4fbc7fd9c8091dfd0dd9ac9e0f26e39dc9b280ff60fd6702c6dd246e21c7c0bf8edc4c2875cc3d0fbe5cb880ed1f4a95b8c3401c1ec866e1313dd20d1863b3c223c62cf13f5ec1289a88503fd4d93f8d7cd4c06a16b811bda412dab1d3a0ebac89c0c63b2e686ff274e3ed11fb81e5a8c817e0536e5088246a51c1888ab3396623933240e1e51b332d8162cd39c3414fddc3509ae1d46107d16e66db727d4e6833c9bc37b41518131ce274a20b2919c36ba0007dad9f262c3915c174d286808d9d69df6db283fbd916b4984d986ecef6cc24708893ae279bfb5346570e0903df8893b08d81e6c08072d6fcd1b5e1201c652fe027302b5121880483d2dc87365af6ebcd76096419116c79ef3323b43176913450fbb1260896e75a0be1d17801a8bdd6d8bcbbe0d2bbae720e384b728aac4f2970cfd590820118787936bbdcceaed78df2e8736628545804baf900780de46c09b0e66b035d6f5b5e6d185ddaf2919963447c18ac502837d5b4f51c6ed68bbd3bd0799589499c41e62779817ea25f19561e02082754e88c931c50c24f61869e5e101ea4b9ff34272e508cab3c9c07cd7b800883c33a065f2ce490bdf12ff3342400efd68d22c3e83a43303daa633b9b6500ebf8e42d3d58d1723bf72bd98294310077514f10278cca3f2caa7a8d4d2451502e598f37332da1c9bf86805f422d5bd08d97c2a12c7a72a5516962d966ff44a5b45c1b12bbf6da474813504d1edc693062fd1551b84bf4ad7e7b2ce18892905b5fdec551d38b0837f55caa56910c5cb0327188e15872b48970d75661c5b76f7d0f07dfce3fae5a7d28417d0648d383d097bdf15b78995995f581cfbe9fabd1fd7097a27c2d15e980d1e2bf6d70ccc5b089ad000145a4c0f78a1667258e695cc3b46f82c596f9807f81d1343d787001f0c3343dfed759eb42e66164dc0bb494d99b55a8748e1886cef1e47b15af0a6ed0b69aee2ad1511f8b1ef1d688fea65238939dcbb397c8f2fef42c896ffcef95907b5e101af3cca741d52a6f2807266ebef644b9b7396652c2cd6ae773795c4ec0a852ec1b9be565a37bba45219f3b06e8a0b00a8cf0503a25297068e9840c4f509d21c4ef0b03c08f3f8117f4fbf703a60b8be2a75736cf387f33dc4655bb57109c84a6fc95aae017894a502be8a823f469c6aee9556e3bd75dd2803c99869ff024007359e8ba6d1e3c0d8053086587e01a735339b7d2dfcf466177faa64c19e0410eea74d493684b06c376e695ecbf7e04ea14f5f0c046307d874228259254a047bb1263e30ea1fa1f8d89b1178e83941e192350f8284aa75b38c3541ba0097094ef1d1e002adffe4e4fe29cd2f56845e6cd3c11e4ecdeaa96effb41852de400f1bd2e9a712e7cfc601cf62051ecac58d86dada2acfe7e789b511e840883e5506d86fa92ae4cc1c46f7dc857527919f471668da9191251a60e7713f5c49d8e704bd710a58b6737b3cc607081810e09e8f5e58a72003d22f232133e7b031c42c062154a51d4cbfbbeeef4c0eedca5c73379adb1d743c5ba66b28dfd7d6f8691405581650bea1b09f5e9eb95484eb2358392d43e0afeb1b8cab2422e8212f09700a84fafdc7bb6c33557620356d4890c925470e30f31e7cc431fa72e5f39e03f604cb21362b3a19e9e4b85700323fbdbdbe2111aed6cdab45d805ceb8c715c35507b141bb3e093c69f9de6212822dbb5318e02eb40a171cbbf949e31407b5c9ee04d1a483e85fbda27a92287d85a52490c3eb86718f139837c060f802a3c4b807064c25bf40a9e047f826d1f4af5fff76d04bfa99a54ced5d80cf8d28c73a181c0ba3f4aa59a28a38b387ebb46e2b7b94e8c960446891445b54aa52857c29c47905f41d6c00b711ea92d667e4ee6abf0295f4104527440e4df9b9e238196988c00eae5517d7f41723ac9eac1647d02b500db7e7eb60d5612e2e89173562133cd80959314f95ade28704cb9cc63c8967354789581142611b8a63421a56bece799705b929bdf292b065015b4f6b6e582a29a4f175a13ad69ef897ea2012fe2b7c3e02009be466bb14c8a39af33edfd6ca5907a2888c0b8d8e5e1b99857060fd4e74059e773513b2e4820e8b76e1273baa0589b126521293a9649a8ee412ffe1dddb083375370318927df8608de41b6f37416ad920e8813f4dcb47f01339557f986402451207edb5b442248aa34ca5c4946bbbd1ff93bf51b9001f0d735a0c4857880deec9b59ff810cc193b8ed503f8b8d5697f2d736030e61cd40cd1cba00aee4102eaffecaf7f49e224a3425ca9cd850c3fb71a1dafcd52a948eb0e3dc57bd0510c1ed101d7d6dc0ee5116ce9ae3ec0734826cfa6f9ff8271248c72acb807072906f90ce457871f922b8d74c95ae7cc6ac98ed610f65fa986b5d49523e4f31e1b019cef1ca0f8219ebaea071b116fbf8c1d38c239de5341fe9f899420d777183309020d86a19d9dfd2fb0caddb266c8e7d9f3207267405e554df1b33dfd519ae400d05c70332278841d8d62d554ab9e43db5bf634c0aba75047fdf8f11bb40443026107eb3c7e401ebb07d3b298e8d335e5fa84f8d835495521f044fb2927a625068216ba33efe82b36f6bdc29b1acef4d7b999887cd5d944d639cbbc3b38eacc01508e8bccf0be4687ff8b58098a87ae2b1978960378ba23e6a0052fd105f0f403ce969dc2be880f589bcaef738cc90722883c2b0c48556f14b85b9a14542f97081cd63c3545928a261ebb19757b0a57cf4b3667c2702b96649eb2d9d18e8c8209663abe8e91098cf1bb1cbc79399651cd7cde887ae192306ee0b97a88aa501806e96a4c23263c3b51033f733f540d4da59c9ea30e1f9724c745b7ab421d63a10dac08488ae51975071cb8c4ae6df0553fa349d62e5a7eba57912979f4efc3050f33ba087decab2a48d22a1f26bb48467fdf54eb986fab3e74270c0d89bcae0502c32cf0a88992ab7bd11184b638de5b1dec393d0fe0121f31216fc26ffadd17050d76fcdb239d02b2e70e5954cb19ecbb84f422e583a81868c63a2b13b782d7064c506f463adc2809de72e51cd718fe686e17e30fedb13481318d739d19fd260173a5cd8a3d00742e3653150bcf677800bed17ee6b2e1a63903ec7959b5a1f30b9b9ce010dce7908da3ebbc3c1ec8c4a1003bf4e21eb779a42f5d03e04b934704d1e9eb5ee8dcf795cc8a13376c96fc4294d8647af67f590abc4a18955e5c71078d918556d56b9b07a00e435b9647021a9264bfa93092fd76857529772f87a307fb93efa2190e897f97880d259697c22e8cb8a5880f1408a299ccf3368854b709e9118c03384911690a5a0bfb49364b2401fd306eab29e0804e6b28e03852f408c9247325ef760ecbb6e2cf29f4cdd1112c7188ce37803cd9cde6e1ba3c26850a7c11ff91063972535830d78907bc5d8ee2f1ccd53179cab41870cf1959861d03c1a76b3b7106e8232f842327f7836e52bd59c45f260a81576f67cb875e5b1601ecbe45035d21fb76cee0f1cea49e2fe599b06a8b9c5de4805aa3b7f66ce3d502822a2927fe05f67c48d029d01fec72ff9ea3709c7c117659d8b168d22abf0600133bcf37195ceff135d7503da655409949cff3f3b6c80e3c73fb533d618dd30c15cec98d5abade4d78b4761efc4b608f1db13d243d94431abc8988269d3a2804b308b2ff7968b0c9e5e8e4e1cdc9735884fa5ef6ea86e11a208687268aeeba0a7a4b33e8f7fb7cf3b038208d986743ba37eaaa41e3ad9c22c59998f2b23abe0f5f741dce25dfde2e8fb69a3f11d51efe4f3217586b42eff8e1a8969e473a6f09ed6090ebc709ad61783d512c80d51d08e1e701b26aa2e152a55020518ba7330653d5893d0b43c18bff7ad05fe3655c8895c5c9d3184d5ab221699b81fa875209b3cacc6685ac2aab99df5fa3f53fb439fd294a2599b2a12a8c4a6548bbba8801835671a3a21d39dfb6323d8af9cbb3c44bbc7559f9946e7f4103d7b0a9605a051e76976d328ab3dc2ce4a5a1350e80db20826b285f2dfae02f969c020b59a60771a85eeae68b12e65a7c1bb9647c34587145143fc3a5b505d140261f48de2f07910f8fb905b22379217907253ddb8632299b57aba1197c910b47cc4551b13c066c104577849af237093a4b536677b3f80962023c4d89b98f9b55402594dd2d033df05e049efde3f7516f100f441003159cc23d0deb5b79b456d07a4a80652d08f05db319131f5ba0f54f9c083e445ade70fd3785ebf31b3e651af7aebf9f1e02b9ab7b2e98fbde30042f1cd7f616aa96eef195dcc93b78f9cd4ac974796a78060e88d404416c567e1345dbe50ce213ae900719dfb0a4737999432a01fe8c9f0e12227668445d56215fbe2521780a07c2e33a16adaa6a77c13c74c5cfcc4a42005697d8aacec53ffb0b85ecac45fce1330bd8bc4899fa91cc88e1a1caa223b2031fac5120d7bc8aed9c1e59d52ae28239aca69d6cd52c3950715e27695db8980ab38afe5ab51a4252b157c3ffe7c6bd2b75b8c93aa80c2c733920b2ca6d52320248483f109d327f79d4e78d5b1d6952efe147979c5e73a15af1bc6789fbf4b80c9cc7c364013dbe522cc37f8e86df62dec8e4a12d80b5ea89cf8f45569a54c50e385651561acd3eddafa2db494d257b66bce2be2feeb5ef0cbd74aa0c550ca70f1d51e324c4987c8895ec75046756eb8cf7d59f2d8ed32a247edaf432ac7d5e03baeb9ddd615057e6b863d453b00c1a617bcd4d9340c196b496bf39506ff7b602586b6466a92ea148e4910f7a106c002277da66f3c2e2e88cd816bbecc6aab20274f248fc73f02d3dd2efcafdf2309c24132f231949ef3555049be4f08dbafb0eaed210c99cdd6648ebe093a93ad20cab93dadb8246b096f32eb2516f6a89ab091595cfdcb3f252108e1271d8f6ebbe0ea5277047a9c4f666599fa31a1e8f3104ebe636221d96653821f54b1d745a1e448cf61f6acf62bf22592f6523eb0c900e46b0547622d76d8481585cf909c4e04c2e2850f3915ca7946984a21b180cae0c4dfdd44310e8035a6cb70dbd7274eb53b44911651f04886e8de3d8feb58eeb0bdc3f6a63b550d96840a0f931d4600fa423371d88466669fc0caa70c5bb45a1079448a0141fb8dbbe36887c708258705ae13b131a5a5fd3df759fcea11caadd051402d001350e4c08d67e239b8b3515dcb1bdda1e1df691f38e6132cb9e80fc0bf81dcc709c7e29b01b4a1ee43ad9f24c11cc804f26c133bfda04f1eafb16c60ce7f9ea345fbea54f7397466cdea5814fa843dd372d30ac49393b9cc74d3f3b0ecef4bd90217b8c231ad0462c29387b039529033130ba34279986de7e5489640354ba38d1bea2f146684424bce21e4006be1c1cbad51e889a94ff5c37992f7b03c0a898f1f2c0976b16359339b27e0ad286d3da23337c49d79d8305bea06ff40b56c250391317c0ad3dea1f3be23be5bac9ed6ade9e8c75df7659a3a00b1c1d0f7b013fb839d66ef102174096fec84c50af0362ad372e4423915d36f3d812e808a2467598e952e516a29e16e92f1cd6ec18e57523fc2d4b70a0552d1f46cfe70e403517d598eb3d1a20b87c03a909bbe9ff5f233c88c0124e7ad7a4d2ff7e4005b9d354ad6069acaf7eda802b4b28914728b5898968796baac9cfd9c2606d090a590cb064f53e5f892504049716f98d846fdb1bcbef0c61f83732f7820f7f21048f6ae1a3a4450b897397569222ffc2893d4fa280d45d23e2027c8f6314b0080d733dd234e58e67b4a31b5324d7033672a19620eee270e85d6a0eae3b921f0d01582151e29a844d0cdf6c1da9023c4941b189ca2c049a69ceb8a868837cbf5c0cc90361e66c6c1b0ef7df5fdef097d5a447c0e471f145d29b43bfeaf66f0509049ce9619ec2ff8b7b457f6af9ee79a9cb07033bb6ca1da962c2d6b678f7065b051e0147e5ef60e9aaf51b8cee66580daf2c69c67a9744741fe6e96c19195b0e0c99d0f070a45dbd0af6bfa3bbd210e50d2382d858f9144104942272321bdc5a0ee42e5d9315ebe848c16ffd85dc464194bdfc0f09278aed72beac0e8ec1940505cfd3d874594f4ac636fbe6729014f239e43b471a3ae3012debfc944a8fc0780142daeac8f8cbab98058f94d97a5f000634c7d77d797c4fbb23eec8747b1d8e04cec159026526333304c9a869c5360bfa1532f4b2b658a269cb56da17da943204c43e3583fc9c6785b4fdce469c32ff433da659f91ecff57c9c207151bf36fe0a06de682d18630614b99c15c3ea1347c293b8f183f7ae3e009e88d2fda0109f0f54b80b9a2df089c1d0c6a5494e6044c5003fee21ef217c8d802e1b6310cf6601611d8ffed3ec3a29c786b5bce3bc8e4d3d975dc674fa1f17c15180f5148b6a0121082e9d9b30aeb18a4058c2ecefce3f3e9631d5e93099c9d47f87f374630d0b29526cc2a15c76fbbe774d26f0ed70b86e7ff67463e3204ec8a482ae4dbe2309276eb20fda1f7a5b3d0707bfcc2beabb0dda477ba68f39071bae70425e7fab0ccdd5efc4fd6284c76481b8e7365a4438b0d9463b8163411962a12ee301d9020d5b31d8d17a3252d70fd56159cbbe3cf5145c1e7e4659098ec58ad38fbfc1770173db35636560e3f7fb12b6785f62289c3f2aeeb81937823cdff2abd377c9540e57296b558d7f67f49b4478b464f98bb08d1461a58c7f62e719fc47ca08e4f508370cb139663c12712dc7b7cf5f0acbdc08cb8145739f1ca19440fa9e26c99a0bd38c1e0983cafe1c81314f3ed8445aea70d354127b17fd6ca0598ea50808b907c0ee41e6bc76831017fcea66103f153666aaabffc496084a43417b0c3a84630f3b97ed8847d94f5b8053dc3c00acffa1544aad18b4fd56c78b3c9949990bf2013f529b5044beb9d23a55096ed9d7865435c1024abd2ca87dc213e1dc7807af018031661841d36b72687a7151283d7de42ced84444949bc004b5edfba099e800f193f09b542b30aa3f4edf5bcca516f05c8ae00943a413bf8c2c5845a9dd5a70d16576f2c229c8e8d8a7a38e317b72adc83c1d53bb9c39af06addcf33b297ff02756b9e69cdebaf6a035d79a9f06370aa8c86d549109c642b418245f097e20b04b01c166a16f69ee2a02ed8df23c6eb9f5edd974abbe9cf83cad6ade9e65afb00ee4ae28ac9c73f16e24e95d3cc2ad92e79fe02be99a7b66ac7c19fa8f4873000e2591eda2565967f65f23cfc082d5c26a1ae09927aea39467ed67e53200bef0318792d9432d39360f2b267422a9dd6f2bb38a971d905ecd0c548647a532ffe0c792f689e257f6760e79e00d92cffaaf2cae573627b4577c597703ad95ba83602147a0cbeb3b7109b3df7fb487636e58826dfea0b4f100cea7b8a7b797eb6e204800445d108e4b2cb3d750294989de215a9f801db9e13141bdb7066178de30b00f54c66e912f66fe472a06c106f1d9d581edb74b02d51a780fcf4ffed6daae0055d3944393edca64e8ea3afe95b640957398153348bf55c94c03ab1c6105f950a6028cf6d69ebd747f4ad241ad908d54eb83544276a0a8a3143803b89bd2cc80a00959bda8845088916105ba8dd1b7627601d43533bb3890bf85e41463055230829a5b5a1245a0b1e944adedc436e254be2366ec5e76d2d4b9318a86b5f7f490da0bc882d1d13477d11d17c91f7b63cebb1fb10bc92967014bd16c8a5965e5f08dc5f2369ce95b34d6c8d2d2d25b2f7f75b03c41fb002307a906d29610931ac03c4b800978d38de16cc23ab92d4caa800b2add89cb119d0a7194d50d1ef5287003c4fccbdcdeb8176dbbc1e964e8d6bf9a9be4401c1cf0072c2de2e9515d6f700a78d470677986c418e43dfc6f0f08245d3b8c301a28044c418625fb06ca4bd0472bd0fd4b01137217866b2f4d5273444ffa94b1c43e156add2dc3bc22546f903cd80d6a9e05548c4672e70a93a1a797c6015358efeebb6807d283ff856204705f428a3a06a011e07c8daeccd0fd63373b437611e457094f7b2d13172374ac70a8161f389fbb6421bea26b092a4bf8a3b9b888eb47ba82ef780358efac8503307094c7e0145f366ff3a4b9fb3c94d46e0927907579a7a1133ba345ad79d4dcc00a7834cc6e376a78c35ab7133f1e482b4c4d4effae5facf4a9838d89c072d7e00f4b7effda8e257ac2eeb46ea82dc6396d52ed9607504ff6b852432f318a83e0bcde26e122971c7dd9d92a1690ce70b5aa7b947645a49dd3262bd2c1d988c3f092f61bf555795d4dbb539ef593b3c2f5ccae19087a9f0cad6a47d70fbed6d5a0fec7f88d6652c529835167095c69be53ad2224e3b28369bbe3ab42045398fa70738b9f0756d9b0beede437616e33d65b9cc09f306d6983737d5e62491e0a1090ac852e6c83f13dca5d4a9e08c76d82f774760352ada589ee0a9bbcb756ee71701a02dc3df690218e664ebfeec6b782fd06376edc57d10513676b4054cfbd08801135356067624bb2030cd2f644616e5526bef25df44b8e3b573b0c2603b514d063e7d5fb0170fae65aa37be832ed397206f333e76094a44e7be4ae4f14a175a02163fbb4363f672b2e47628d86355363660a02c02a0d1f7ffcd496126451c0d017cafd06bdda34dc028d834d77c6e7ea508f1720b360c86fa296f37330548920b48482c0594a8cc8f0db2d250d1d1a95c11f27ab5a61099b7a949490db45fd90f677bc646723f405f29076ddc98c3a8f40e1c82f85d3ca5565de539baff9cd50f0c9c0cc6c764dc1657defcfb6ff11b86e483ab4a46507f08af763dd40fb4bf0aea5ca36c9750a32814f5d3df4a27e6b14e8155675e593785f25efb2d4db44b0e +generate_ring_signature 5354b346c632fb825e3d4facd91c90176cef0f333295379b68ae2f3d646796ab b232e87202790b0f139b5abf06b5ed9dea9fa10b45137abb748bcedec49957d4 2 1d18a97357d5c6f3660f9cbaf1d05bec77fc60225b96c643af06888b41d560f3 e4943e8f3d243545a50e1fece9b992cbee891844f6920144321f5026d51c010f f0967d0fca4c4f72d2cbf40eeffac2483d32aec3c862f83c0e938e8788e95602 0 731ba2db3ec9cca7a13892123a0f613d248868f529cdcc11c2d64945fcf82a04a1147104050f4bd63c6bbba07f7ce19cf0318e5d18114722b5a7ffc1940ae804294d74260d8b51569d7ec2c49903e8c6904078b324dded75463a1e285a95510c56d30bfafad9b6a184ef93147cece263b21542f048415f8867a580d8a02b1300 +generate_ring_signature a3e9f19a6821d12b0cae5d619f939c5d9beaeb9a361f9d7bf374b3814ff23ac3 5be7e561234f7b6bdefeebaf9a512ac4d97b77dff058f832100634dae4b222f3 4 1385acd49f2edcd1d00c3e69d899ff286d76ba1ee5e7684e480d914d31f57a58 5c43220a60eec00974abc48b6dd95e001d4b1e362aaeda5b712d8b6aca644304 02365a93fcab00824d25ce4e4bb58a49b9f68ff7b1748d1f03f8e5edd6fff416 a1abd0f71d7c05bc675f911d3c4e54c1b8618c603951a1de691eb38f030199ea dd3b77701253a786191b9d59b556f2b96336027fe8b0de80bd2b059acf610d0a 2 b9c1fe225cf0ca30b89d0c03d118e46f91f513356976a89df306a988aa96cc055d1a221eaf3a00fe25989d14dc40ebdf6cfa48bc50fe8c676a9380147d7ea70387287c180bddb6ced712597189b093966ed73589fd9de75c8872069deeedf000e7e68164c5d5dc4ef5c8e052e86617e198c9d90eb775d7a91978a035c2873c0cd7fb8fa6a64ca4e2a3f770713fe89196d420e493a97431b97155cd3589873f0c354ddb18292dd0ea8274395ec921476a7edcc498d46abdc4ad58bac0bd61370e492f2dc757db6b2dd42bb80550371202d9a293d9ea1d966efadc35d2d4f7370e33c9d6685c207a5e11cba02bfd5b45970d76fdaed73ef87e7e52575e17693f09 +generate_ring_signature 095549371963a6086a8533f8182dac9a9288863a9816d457484aae549c10c454 034fecfec4129e575a8f79e4a2f329ed0d6e804db3c6b30178a298acce98562a 2 80e4e87a2b4f800d02c125ad32c11b8d667705547775533c48d73f12f39e1c41 c404cb7233f19eac34d072f1f24f9edef448ab74024a8ddd8d72639079be844c 47b6e374cfb1b6ec4adcd3183f7b9f54812d7bb6d87adab41e1713b89a918c0a 1 d0fa58d03cb64798a267b6ca4fe130c967674224a2ef5af6db8e2fbb8cb0470c2e7e8d73ecdbbcb32f187272690e238b48c5607639fd05f0f44e2a7f59adab0e52b128213a44c07f5f100cbea92167372aa358bf893500130cb4b51ddb2f3e0ad517ca1489c322916f009800a3e46b6668eccea24d98a9ab1dcbad560234f305 +generate_ring_signature 765d32e597159be85cd75d6f4a26b6afaf0eb7618a10bdbd64c3629d5f37829c e5729996355a7f45566941b16bbbdc3e9645df09d11c7a4fe0297d9046d80876 4 ab4b00cd5d57edf83bb7e34a17d61fa41fd2bb00eeb8b182e03e05fed0d63047 1f121a1695b39b438807592a0364e537459d9ecaedd7be6ecdb71b9534ef74a9 02849420f9ae2c064385bf53eb6a49ef1ced6fa4b2a386ccfbbc279633f3a4d7 4ae5357926a9a9e9204bc6f2c8d3c05bceffaded63d44c2e52792fbe87964b89 fbb4fa26dd9caef9daa40f9f451d01747fb79eb9bef92b680df1b0f52588fa04 0 8c714abb919fbb5502c3b9d0c22cdfadeab6241a4ec8e0d4ed01bfd062bafe0886996adf18aa2a1792a5d7c57d63844d6a033ef1f2d5da39ec6ac6330908590149dd2b84dca274f08d870e857b375837b6c00e269d73c73e20393bc45fb5fa058edeeaf8585e261c854675d89852599914862f451867ea48852c5bf87757b40b955eb8a953b2a9180795f69485e0eedc9ef69d534d7aee0d38878f69be585404defa08da1eb84e80ceb58623ac4ca7a6de61a786080162ae2a8e134c942b5b0f3a79729276cd16a02aa859369b5cf79911d123067b456247ae1c1cbe06bb990424a406104bf2557d86f6238dae70a5b6096fd81581bb7727df228adbfe885605 +generate_ring_signature 5607d2ce8ec511c95bb7a1ea452f4574df04bd5bdbaeea20ac0e508f13feea37 66293e624f791235be8fcd3944938a213b1449de9b64b840f46ab25c244e2eb8 15 252083243400c45f44300a90a5a72cf124586df2fdd358c3f2d8a10a9d3b9f84 dbfc0f9a60359bdb13b1eeec597ce25a39019303f53e214106c1b6efa039f41b 1799c271c54721cb9b9aff31e770a27db2016d3462e8f7cdf97e58f5d957d1ea 0f3e0b58254ed1f16875206a5fa71d8b2bd3051cadd875a85c064fd43fe972a6 db61f4aaef31bec7a76142570228880bc1ba95aa3e130efb815c392daf1f648d 69a59d101b46108998f7147556e1a489d50a8de2822cc4b616fb316ba6308890 3216e6c0b89b0e9dc558f22b0646340901d4bff8dd6d0d37b428de10e1fb3d8f efad5c615373f47a5391c88bd7fd426915d61f772f8813fa1998007ee605b032 aad7a20871ffcf814a70a4964aadea64a2ca043ea3ed90c04bc0c581ace03197 ea57e5ceb849204391f7896c7cc93ac01645bb955a35e7295a6201d3888072b7 ef11e7753f6973b1f3c06b2b7dbc596db6f16120715339b17824086c12a18686 8a8442aa38bc171b5fede9ed5892c4c6f8b7834f6d84d6818134a2f61125f86a 12fded8c7ee0e7511d3f37d7fecb900ee662c3f7dc36756d64fd7bd5221ff607 97e073bf305e6e79f257fdeb5d5b126f12f818edbf3502a129a71948afe94933 71827fa61bc9bdf166378ae6e479ab50eff55f8498804bf26e92fce047f1a76e e1c93e67a747dff61c24a89817f62f6f84ebe585721cbb46d0139ff7d410250a 10 b2f03ed1ff9b3847c77ba6b73eb273751c0e01975d52b04d3b5f05d965cd410d45910e07b6cd217ce9080fcd84191d92c7a1c55dcf6f77511e90ae8649fdc6054eff8ee200f61047ca8575dfa779f6facc4f74ad75d1efaf171f9c004afb460740ebb29de09dceacbce81b590061e213e62d2dfe4761bf97ed9d7ccc2391d803ccc58e5aa54cb8641209ed899b1913d9b692142d7f45632915000ce507874d0b3bf38e7e7034c8e60975fb6bcfa2e0882c8681e62859acdd5c339dd27f664003b33d730635b7a262e5c97dabdaf00753925ee57920f83582e93fefd1d5073205e610d05cf7cb3c32af73feaece4a84596feafe08c714d8c24bd7bc1e436d710492790bb7320a56df42f165e47891fbd10a804a9278266c260e1c348cfa5747098c0f37d780402e08638c7fd2a0e0cced7ab579cf7f935d91db49dcd20499ef0a61e9962b7c454e9e8308e9a4ebc0187eb60b00a2fd007ff4ae11c53c29804c013148a67e294e754b2765bc8611df29d95c3fb468e6844f724136dc47bb082d0a453e6a8ee62bcfee6d9602766ffa50497cc4c278e7207d3ed115a132a4999d00139b6725e139131f4bb276f823bb823dfe6be7902caa8f2811fd136362589b00ae178b3160c8256cc7c74100a5c9bd14cd7081862c85cf68f4ad664b618320057dc07e4f19e5e16230c49429f7d1fa97b534829a48c78b79a34f13b8de38e60016a6f1a8df7d004710a22e2f28816d0ec76a7f8e76639f702690a3bd87653408c134abdd131a057ffeb9bd9138f65ef2aac6f1df3245841c3e253c7744d7ec05e81d092d0aad6d215c4495729db9db88cb41dc8a2ebe480ae5d102a97b90a10dec8aa10235d82775b5e46d324726c06a5fb1606df32c27c1579acca543a87b0ab3951fc67a38ddfc2ddb9fa88967c8cc28ca1ce52dcfa86a28a91d0fab700d05a1d7fe50b13af92124a6bc06c3f92c022bab544a5d3d37577876bdc57588220b56cb694ec36c8bdc928e634a6a8ce4121debeb7c97db6dd7641b5d404e5c9e0a0300d423011fcf8367eca32333eca36931efe6f15cb8cb6ce9e2c352189cd50c6b2a1474c267f5f7abf4f49e2284d03c019006a9615bbe11949743eb64bf710f0ac80c7219ed024a9297c882be1087ff02ffbac14b4b2da87ec9ce583ada0e0018a1db64a022e9669b537a4b30ccdcbab69705dc7ae85cbe81009279796fa901cc4b3fc14b24ce61c8c8e7e0921f7ae468611a0dbcaa5c68cbd30a2df1e39b0ce8594d2a8fc7ba0ed48c09550682c648cbe6ca8302a2d16617a6a9bb30bcc201b123a72aef2c024cb24a8ed16a0d839b801b0432c21d6ae0db155f045ad81209 +generate_ring_signature 781f7aae6916cb86442605ca1c1329f08692a28c5cdfe0cfa95d964ecbe6f9e7 dabd3b459a7ca4562fe5ca42191a402e51cdfe567079cffaf7ebe599459490c0 49 6d5bbeae1447773bb341d4f6253c84a563d776c81124bab9c82b8b55966ef9ee 5f3582036521d737a102ab0878f3482825882d869168795c8d29501d25741e71 52f9596059344eaf95f52e49d2ec46868c01a0612413866c50b351c5628fc8e6 57d139af17d71882f6a385f6a87caa657970517595baf226175248877f5ddff9 a9f4ebc8be21b9348b62361a712c90841c4ff4c595b36049efe2d101c4efd321 e8415eca3390ef846f46259c0e0fba6dd7427a7b5f1af4cbdae1cdd34abf7569 4f3e26a193a4cd0e338fe5ac6b4451217ff963c1ffc7ed2afb8c86d534b0bdcf 438597f124d1177200a93dfa9d8d0a9cbcceb2cbae2d93640147ff1eef7a2233 f5339ce166c0ceeb926acfe4a017911f9e1e3650e4eec6457e20cf52355ac4f4 4c59880049b15c24b6f54586a69b8935b5d8893a7dafa1550c5123cf0a253cdb 18a2d68bcf6db85d092a4362767cffbc973fdeb9eaff23d260c78777f8f57681 0d45035f8548a968acc0b0815518ea3024360446580b0e5878d75b0610911340 724763763a18220ca705b3221d8ee2231cce67235d0a75ddf076e8ad5523cf60 65236dfaaf133b1dce26fb478a81305bab5dd30e426ada4a399e29a7c02ba4ca eb7717bf4521a54e02db70fcb96c20d9d4d6673378f6b2c196bfc22a3d5dffe0 6752e1853091c6b4aa5f88df39696cdf2b7ab14d3e4b140d1c3905a6f86766a9 ffaf2190b741ec45ad0bb7070e66235764c44449ab408c6769eff87ee92df421 9a3fa68c3398397d68f7eac4a19e7235bd4256243bf8667082c4bf5bbc06b386 c0cbfc6b9c6b2e6c7c696cd7a89e4c79c7dcbb394199494981a6342fcffdea0e 661f99232ae0a6143a994c679146f2756cb4dcc6c465145b05d7bcf57aebaace bcd4b4ba71a309874300bbc35f9a9a541a7ee8055f798a4ca4dc546ab021f25a 8937c53d0262f927ef14ae7dda2cbd277363612e98669e58604547676a862af7 bf01ff591c0a495d7938ee4e3aefb0e20b121823451b1d902dfb8f2b460aabd4 6e0d51f94745295a30af485bbd46ef078fc52172f142f1f73e68d5d0d9ab1222 36fd64b91f05287e299200deb9fb4cb0e3de6f0240e2dbf274912579a8a75eac e72d9f05f370aac2cc5e0777bad931d6e4e193179e822583f02983f8c6bb54b7 d589025c70a128d8fd4b6364ada77612879a039ff49a61b710ea5ba856eb4cde 78c4e48f85675999752f7e6114460354ef4615e3ec05d2f6f96b85a0feb2093e 50f02e55a8df0e9bf0c44c66403ac989c630d21c2389ae73f3413f1d07f8fd2f 9d4879da832b2027ebd9dfe0d5d361a4dbf32158d039d6951d20d353d0129c6d 9e5f72821c4ac472b040e19d85234f46aca39613013ac66788c3143eff365d85 9d1d9d4be6eb21f591cef27b387545a22dfa1882a7dcb30fd5f2141b398fd2e9 35147ca542ae0c529dc9818dd8e4bcdc300a867254b9dd0317bf74a860a66817 17c55d7febc20cff8ad649ba7ae32318cac1750da6b4ec47c31c0f49ee1643f1 f24f5e7d6a9a0e9c457a0ebec586b4bb0a3a5564559cbf377138f64562df2484 952e0f4633631f3eb0135819fdb7aae7742ac5b2024c5f15ca1cc65e1cd08f24 7b9adc5c33bcd0feca677012599cd9a7fad17de30a30efc0981029e15c4fafbf 11e437c3869f6f8d822ee753c68b860560480a37eb57706082d076e0f601e54d 7b89c9df6a8a9f261d298a996c3a1947a6f676c2c7be4ae7643c2ac5d8e89c62 ff3e6f71a711fb28a24a2ea10170ec29d4fc3f3e2f2de6a8698783b0664d941a 919436ecb0dd620ea94686ac3fa07bc17a777d48f9c0224dde1703d7a2ebac63 7088fc87666f3a284b325d1a50ef179e47ce44f546bee688bbee3377b5462e3b c4bf7d3009908f391b0a36d2768394e429a4afb69d789d64bbf1377402f39b01 5626037572ef6d5c06636c8d2c7a56e19e4eaf2bfd6229204d4a60157b7c3d9e 94c3d100bdf06065c3d6c21e4d743d925065888271afb52777926c1bb49698d1 45591bae02bab2a3b21a83305a149c0f1ad63d16f23501ac471fc5195eb4c934 fe5634f879e4007ac0ffe322a23d96d428ad09df6eafd123e67009c77d3826a7 e131ff9519e778753cbb08ea67fad0bf1349f8bcdd3e2a9e30556d11ead0dd5b eaccdcec9531361e1b4559e4a9ee176075cc22a6770864c21abab1a28fa062e9 b6b0a5cb6c1acf7b032fe3401997b9be29ae854fa7d6c1dff8228a85426e8e05 35 9355d3a3afe5002a6098010220f31343a446361aadb2788e1b7b49aff354fa0ccede8d1e5d53e011df1242eec4d74c03c5f5eb4685822cbe6a497ba52da04000e9f88ed91565f4fc65b3fbfd8351f82fe4d960e22f71942755cf2d3641271407fa5921ffd803e00d3068bc1fc0941eaaa6685f08552bcd83b8482ba63379ac07087893e4888f32d8f1f618aee2e3c35ea84b5940e5ca3e580d12793981955b0f9975ee6006450b5681c94afbf08be38f518c296f4df53b70e338fb358c1a7302590162dac7eeb32c4eacc6d6c4f4f7749d9c9687c020f8a9ae13a84b7780500389af7f6a3e191273e7722963de426fc974226463a5e34af4d73a64a258d5490f929ff430f05f9e01197cdb143fb85ed5461fcb8be21eec1872f5bde0fd192c0cddd2c4c12711f5b3968e72e5cd309a22f492eeb13dd8db744bd764a9cf6d10027691aba258214eea546dc949e9c372c30b17ad10a9a56b755915036d09d8e2022da76c422207094cb3e37e2856b2fe51107fc15958d841cb78a99c39cdb5dc071f4cba99574d24cd898c297362ed91389cc5b148965934330720809b50cb010c022f36cb9eff3868f43acd5b3af37945c5bc51c61a95c0532fb2cbd59bbe310da48d59c57f620c43eb9f7e2d9e278903978813c3f5b6582eeb7d7790d0a81602b8e30b363626d50390b9afcec243a348535a3515ee78a9d3b81720c93fb75c0aafb09829315d933c3ce46e71017128ed0ebcccaabaebc4c4c281ad4279a813024c2af572af2615bccfe88571dcd9aa4df64b6218f2c083889407513625761300e8bcbef06be6bc034510979835818ba827e9336dd2670a5be31c6031a05c1005a45de60e7811890278f6c7c744f8ba59cc98b24b3b4ab69b0d8c9801780a9003623259e3fcb8a0df87be2d5035081cf934a97982927a2489b6283969f24ce8080f3b07076f61cf859b2dc96d76fd39e85b16f891059100ae9072be37d7a9c404d1eead8761e72e927a85728606da45c876ae03fa2d0ee79eecbe70d8450a41089b735448461c9be0f6b5a1b7b156e718f7520a06720b4ae337d6f6d4bf4d18097c9517da14d2a782374d94626fb388a2a8aa13969d0a59e067704617b010b503e6a008855d9e5c54700ca8852cf2f424ebb0f795466afe212f9c7e11fed252010e4c983c216964c2e08ca015b4d0f61c338f6663450576cb583ee542a1b1830ddb9902e74707af9380a894d1d14f72b3ee8b3411a8f76a90545b48e2f6e20507938a8febab8d207d6d680caa72226cc0a6d23f29fe67b61db2602da3fdca4b096af3a7693aa5bf1b7064ffa0fd762a670d79f577cc9eb551625df85f647434039afcd243d85920e39f59197fae25ae38f1655e8111e4b3204ee96c9af96db80cf2e7f470dbc808bf6c3b7138a2d902ab02331cb8696f5cdf42df15f001a33c09068f93d56df4a57f5a35ca0aaeacf4e5044f07799a47aba5741c81982142b60d33d6afe1e8cc4ab0500b98a4abc3d26f70fda407df8d606918dafdb089dc80085e37e658ba320d15e9692f022aaf7b6e2e282dc6d192cb409c5d1085099b130cfe71f476300be43345f81005dc3d7b651f6080253835837c428a9e702d63d805e64c67d5ace65cbfb6255f8ea6f7f19fbb3644a3dd92a3b7e6fb6ab3c78156027b471ad6a218ca5fce83c3ccc671f7b2518a9dfa230cee43489b35fac8ba7108a2363460caaa18bf63c7a1a8acc6e2773598a308ec4a349546580ac1ee363205c5473eb275878ec49f2cd30064ab14e7aaf69e7bb690fb11c2e17327c04edf0236090edbf9506aaadc3c793c97f36b8d1125ff24b872371e4586d75b1276cc02abb2cdc21e1dc603f9506f501d369875eb982ac684769b90a42eea5797230d0818de88160ba2415c5bdc5da9b15fe698dcd0358fac44a62fa3a6bed7c3dc9001cf122a1eb07c9842d4edce098ab99c8c1511070dcce0c3f0aabc854876d7100a1225c10cd4c82e63e1b0db5267f2c4649bc575df99353634f09a3cd7af0bea00f746ea235bda7b2cf01fa0d83c7bfd3b33fffc2776c1da9c74215dc84ac9fc006cc51c2d26938e32ed3e7fe2f6dec46e76401f76548a541612cab694c6dc510d655e36951521f74488f73f8c748b97225642a6eee956e6217fb561e9f20bf606778a2ea44f9365ceac5d1a2df14ddb71f455fd52233817a942db0c9aaab9aa0e563490341af8381e7c74a6d7c5824b3d2d1975fea8231aafcba1a2ef5265eb0636e6cb6fd842042e496355b13dc4fcf1ff6706b18c63b30c374e2c5bb3110c059c5325f9cc53074351b3805ee1135d015d8f1e9e272c7b9cd663815b27ccf200c66f3db1b7f037ef149082e1157df89338b987456e8c31459e6a62967c58600c886400b24e5ed151eb1d02da908381b569963c1c30b3e24f99a19bc8482d300481d17ac2310bb527bfaa80f502b317d63da6b0b5d12cbc28d690f1a239af20049cbaf8276a36ddf2dc5713b86cb89fd80c24797a3442b495f6238389f466c30250179e19a7a30a5557c6c34e3d4be18eb7be5774cf0dcf528970676caf8f3c0b8dc2e8b2b459d978366af8da0206126142271ca3065e591b7953808ba039360c981dfeea37cc01a88326c80cb579ea64de423b3bd9f37fe60e5be036f76b2a0c63207787a8585ac866756d347e52fb7dc329cc96ed47c5756815d0ee698571048890df269e9eecb7923d93f62e8ab3d019542ce074587ad5f8f5055aeee57804611755646f644a62601fa8bcf0fafa12d7a4f82f1b331826fbd217640acad80107fd50320de0c3f43f3c4efcb2ce606c14d68e95854d241a5b275181a2ce760bf904803119422dd9d1a39a4fc8770498ebb652338e23cb93ce7889d9809a0f0d0f78b08f8dc593bd107a1d7e185932f9640f66814fd0948b8719444c9a317f090778b29287f2b1a9787191f90d46590e28ebd40d83db74de4c08ff6a1f437006d1514db193ff0124393df6191b56463a083a7afdc3726c0cb3f5382ca4bf2000367500d770f5e6e9805ddbdcf58745905463230fb8d6020a6c169a34f5609a0cebf78f364216dbe1a85144a5bc0e08ab21e22dfc88c78eada90c27b97e8d1e083393a188396e32e5618b3c0904d1c3c9d9d3613b3ba8d921fc7f7e419953510278f18bd6a72ad32c056ec8c2777d1d85914a388d431a664c744f62c62f22770b26ceaf0ce30eb45f8107fcfa666971644017deaebd8fa707881cc7396f2912048dcad1523dcac5399446fb24387cb5d1480a8652c5ce87ccc10cc4dd0c412c05da55f3ff8b591188eb5c3c6364b75ac7752ba1c4df5359e7a7695eedd50e80052d65574b6ce59b0e07d30255a1119cfc5e43d5f0e97eb92a9a2253e391d9a105e42c41debca573a6e0421037d451bf13473b80a06d2be6212650d0c244db59071542e9477a14cd35ab47744ea265f45d9d103e82c2828baa471ed9376a4bf10d5745a1caa0f634f3326552f56edfb4b44c6a4317642b6c1ab39cc19f7012f705ebd6f5afe9fa18c64d7a5b5748a5c39429fa9c53c310d74643cbc516f237b203fa66ac08c73c2b8c8a0c4ae8792c73e2ed2850af287268abe8080da8da91b0028c598da48d0e8544eb3449d1de52b7b83dd6a6f86acfd42bbc99d680a2569d06412e0de04d11509efe404aee94d466df845258599c70e6cd2c5a621c2404c00efac5594132ac56b4fd04f35e2b0c0178c571de1059dd4bf5642c7bf0416c820b9f5a2eba303de9e8c2c2c6e894155640713c80be86310441812cfe262b389009fdb8bd46e9fefdc1dbe2f6560b56f29cd706879ed5ae794bdc79ecaf57a7450a9c15235e55cc262c02b2edcc2ebf795ec9a37f039741ec3435ec5e9ca49fe00a8facb37d6d69565ff7bb35aaa7a1933b988f6e5fcb3e44f73df4d6a31e3d650c1e999bf28cd34556770d0086f9897c9f07e755ee7eb73f5a4d18a6114f651b0ed07afd371cea2d8d831cfc62228a38bd1762aec43bc018fff038449126e81f07ea7f6a21a379e80b5d22a00bff43605a5c641ec986b955d75cd6983a03e5a40a15ce6134535179a72be5ba5c83502a052abf50cd6da4aacfa25ee7ba1361930821c2a9224299c1c3268131866e4f9f304bd406276e04a369818dd4924445ac085b5c6fa5174fc4b659b57102548835432cc67cc9c182b853b0c235c4ff93cb07bbde23d68de1117a7983807ab4362bee70f01eb91d90ca5f788e196df69c32041d2e0a8e37006bea18932b84ca68a744ebdb6aa6c077ef32d5f38b8aa91b5203280d0dc71dea4cd0ce59dc3df4239706a24ec314f08c64dc3a9ad70d4102a90b0a0445306210dc1a7e2e13b8acfc87a5783810e8e1114c3653e57a8c6fd92a0ff00861a19b6c95e8488e5b941c734c04b3ee61a6c8d8bd96b256989eb9aab506 +generate_ring_signature 0ca6be8442f73c39adb0992700397888777437a7de0da8c8f80ac7397c51f46b c2483ecf2cb22c97aed88f73d621ddeb6f26c3417ed263dc17a486ff77274806 1 03b86aade5f2eb2b8868d951161eda03d4a47efe945a9dff7d630de9089a94f2 ef2190e473cce3732bb33ea3a62a4d8172a78336453c32f4419d1437ad3e950e 0 fa2b4488e06308a905c5e20581b8508c5374f4f75410269da03ce838cd2979028e69fb7ffcf10007636e913d3502f68021b4994fa959ddcce562ca8c02b6d10f +generate_ring_signature 79c5aa9e030b432d9269f7e35bb8c1ed8eca493372526a02ea1d6ebcfa52b385 9b9a27939cd35b70d56eefed6bf3473df1bf5e1cf3ebaf7de8ea58ef212170fc 16 1e6c15fded661246e31217f773daa1c73c6b5a535dd39cdeece6347aa89981ed a2dac02c4e2ecf3f06cf95a267f7e1db2bc014bfde9b8d5e4698279429848a43 ca579ff489a24164edf6a84c9bdc87374b47efd80c3f994184b770fcce575296 2e247431d695436c7107a280e966eaaaa4d90440d824f93d1c96ae49bc16d4d9 f2609d17a8633257d1cdfbae1690892339e49f9a46eefa9e7b97d5b0570f9cf1 bdca7e43816c3c6fd0013557b1e0f2dce14ead44b6ceaa381986cf6c3055adbe de1b17fe16b510a67e0fc049a5cf4f801a9d9550e7ab8a5e4f74ada85a4057b4 2e9281c688bb6d020dd8a4079acab780a40329c6c54e1947f0e0e7d90b614aea 51dc71ffbdba2913ec8e578384e4d25fd2bed760dda9649155d4ef3f49898c63 88d6f98dd9e6c2a990aa0120152a372da00c455c97b22767803121f55b9d3433 6ce4fc262a6b4837e5f7baea0a512112a2d72dc6d36570cae857511197eee9d0 ddf3cf1ccbed6da657be10b878abde58598a07eb6be65cb870978429bd6550f3 5469427cb812861380add098f93b02d766d7b9de7b5c34d17bf8b2105bb99cc8 ae7c7772243d1b1a0939390bf562946634fb8f3a3df045c15f622aec4a19b05c 62f43f2a86d3cc9a59a82ce2c087c9442c8f0de3d648e319793e8c7ae4158735 1fa26c1a7685ce10b3734969ee68b9d8f441b0ff1beeda2a6a5ce41d7857727a 7f846dc0a1378202cd26c66f6d80322192979c9191c9bad913accbe8f3b4030f 7 69b1e5fe87a16a5a7a54b64d7b8d83ecd508fa0d91e8ccf3571b88d7a9cc970349a6b0ea41d60015b39185d4e73839963cf7c928787f045a393ef4520d423a027269151204c961c41870863c88252587b2f65b63c6cd31b14afeb15af93aa70b68d6ee70e252b2dbf7621662a0ff8a4b50f65972f3401145103f3ef39020c109445ffe882b116927ec96214d4eaed46118840473f03e657fac317417168da50ab497f018501f319826c069349a509da85e91a14af7ceb39bbb03e9076bea4104a25c119e34b58e1f06bfbe89137e74337dd379be370fbd71502a44e28abf9704b174c466b962ea73d91faaf09a520ace560918d3db0ae4f596817195b552f405cf375ab3cdb82edce5cdabfc4f718a0e33fab7d735616c6f83ffa4271cad66019d33be509eba4363766bf685a64938b21666d786f36ae8303b8cfe82c3296a044e9a33293d814e7cfe7f324d10bbd55e4b3a4f8e7ac5f3b42e019e485f3eb9073694a88531baafe2aa47621519a2396a48b5d38b6695f5a99eacc3a627e66205fac696ddfee0f963f1435f29f0c59d8099db7b25e61c44d5aeb3f6751b675c0ad4a0e77ed524a5e8d30c5131aedaae113bbfc2d9e10c777f46708af2ebc9eb0cf12d48dfff1ea1ab3eefb3531882660f349420fe23ef158807c947b6d647bd0c55ba39948d308000311d00ebedd89457f40c482d43616ae203322577ad00320a78beb707e00602e7e2a78fdff44d2785f665864c752e9baa744b0b717d4f39083e393498d5fc5d5137617b251f135a792cf1538c8114d20e5ffe90a9955e9903cc2a1182faf69702d5d0eb2a3ac119ec6fd00ad6bcaefd2c1a975b00fb5004068afc023901f8a968248deb99cec8a1d00d457eb5232f7cc58d4432da1c6c930a4def75a5b6850c1eb02b133a6c74dc07d81995c45120ef6cb09cc4842fdbaf0d4043e912a7e9c6d8d8aaa44358efdb8f5ef0d440599b02271cf8259d518e490a178aa3fea33f6487f8bf19b8754d28d9188c104a0b537d546ecd2970a9a70805ebf353a68c761acaa7a7382abd45675d8ee1e3ea2ef18410cb690a94067ba804a2f752adef8c7887bb10128755eda3ee220fbeb54f46dd17c0b0a4d22067d7024393fc9af0ae196f660d9bb91b2235b88606a8d2663d5c2393f785de1dec5204744fd9d0efa7d2807912dd2178103353116954d9a84683e660b9e4dad076590fd6dfd45c9bf67a3bb8f768caaa66755103e7a14ea32ca2ad47449e01ba98c6030834a5ea66ac038aa6f97f1af2978c9c6e167d99ceecf6d03960eb16040b910eb83e84062eefbf3983d775eddad5fc7cd18d60cc146c7439c86f186d950b060e83fa8f838cc0f37cdc964e364bfc78b73492fbc6e599033a745558a27ffd2b061bb9dca3fd448bc00e37df84b03548bbb774c0b4fbf877a3c3b7cec2c0013804 +generate_ring_signature 80e43999bc762b25fa9d83c6fd111032ce5582d573d6f0f3e82e0f989fb6b8cb 1437aa8974c08719cc58a5334921c3201e9f45a5290f2581b26345a659b86475 17 40584e3d53899817ece3a763d73fbd5d5a09cd75ff713d3e06c3eba2fc853319 e4b87de4b1b936bf74e0bf1aef738e47297e65f2df50c566a9d5177c75917534 98449fead3b69ec5ab2d9f524ce8aa5e6d84f14917b3b03575aad4d4c291a634 23619e368d268b186bef7a5fa0165c31ad8a3bf4b0220bc262f484067d9ad4c7 06268863803442caa2e603b4791a097a8f33c45683ef55befb3c0d7e3055c36a 61ae5b158be8ad2e489dd7b0b78da17166a97f09629f96342e8712788c1dfc0b 4852fd68ec5cd1231f48b79a8bca21277e347d39af3349bce848312452874d59 5446a777840c6f3f75aaf437d1f84addbd22b5d5c848ffc3b7fe16e1157e4819 29984210fb9b7462a3cc598bfcfaed8a0d704d02851f1ea7d3e7e3cb8d73d3fe 93d4243ca3db959b7f3d7af72a0bc85e27226d1c716dcbe1c5b3743ef5c289ff 7efab8e221de5b67335eb804564b60b5be37ea20497329bceae89822d9e530fa fa084a1bf2c317822a31681302722b4a8001fa2aa9a0190e45b68346298b9aa7 bc15f7855be9e63f35567ed80b629c270a1cb1de56ed670714645261edd0acb9 17749387d62c8c3f725f1acd5b44c955bd4b35ece33bb5b3fb121ae52dd07750 7b83783748d179d74bf5f8a177a8e400ff4783ba1a24b7966d99b587f6e4210f 235b414cf437b43c611267566496a834045ba01b3f16c7b83a838aedb35e5863 0e326a30399705ca5c7fc10ba5f072fe06656388cbbf8f2417032c6d3e1f1519 0be7d9b1884397738bd4495a62ef77233f1b262363539f6bc3834b3430c84408 2 01a14d4bb61696da5d5e4e6f5557cf6f0049ef4af152477d630226da11dd0101f736e1a7507fe045e5cf71b4ab749da4b9a88982716441f33f2a1b2ad286640a2acfa1e05244be97251e9e01da118edd4258a1d57e35b06ec83a02b7293e4a0c6d3a8e7b1191a5786ad83c068a3996c937d5ebfa236b9bb75b0a5408a61e160ee89fe7f15ac9341205aa2d22a6115fc621548c92c35d64946125a945be7d300f2556116cc4f7bc3498f3bb5e54c95970a573d75a70b865215e21f9625c53f604b25d6cd86fe8556607f55e4f63292b5e63de758094ddbd4e119cb1baff0776034b0e8ca5ee48b4dabd9c06d89ca726382868180b0960a96b974c4acdfec55e0e3a895d58eb158543b5f70c30a07f4cf1933e498edd6f1594bcc7f269cd2b670526f5cad7cda88db8dce8629d8b03059f44946aca76a2b8aad3ff50547f97aa0301ebb366b542e75cac85550a01edc5d9f124b87b65a26928b7237529c270b809b93d85eeea3393abef724784b8307d8f71e5d11d5d57c1fe67f65f507eccaa063bd1c7509f618d6dbeaf3b697188402db41b353736b0761d08d88d883a2f0b090bf2d6f121f9d28b4d616d6ac91e28c4edb6877f5fc5557294056784a798230f3cb06fa88ce1d814ab01fc5b8c67160b1cf4a4e1ac45a169e3889798e36f750a4c390ab3aa66299a3714fa0b8a91901ed65c701580398186d7ce24ae1354290c8f397c93a409beab93e6091dd842f454ee3e62cbdfcc29492bb503eccb2a1c0c885bf070d23238ec149c2daf3c5be9a9991a9ba255ee891799743f394f93520b2d20498495f8c3b5069d904914429b35c416057b71ffa2bade0b6f81e3beb40bb54a716ccf419c65d20fdf51eee71415e68ef134d1b7315b2ee62dc770229408d3db59c0e6c2c9efb19da96bd7f84c7fcc703388835911e2083e29accb9c8305ba311334ee6761e3b92a399dbdc882af86497e5e9e9d80bf00f6bcf53ea21208f7dc2ddfe35d3fdd46517cf64dbf149f5ef2d02cac8bbc5f43b8fb3cde77c20553732322ed3db3e4686b1c57a1b900a7e7a3e3af6c4c0e907f2274df6343b80a7f6988670bc7a0a8f0675eb99a89d861d8834c6bdf28abf44ef694d549e239041ea31c10ba19b0369a11f61fb839f328a39b9fd833f64d740e74241f4db4850f22382cdc8654fa088d55ee6b62ed8023dc363aeac53e95653d6fb9434e27ef0f275db1298d0d11fd767ca64b9919273b075dd4e15a109465ed413ea59a6cdc0948c46562055cfd7e6acd121b63fea957186f778c8ec1eb0bc12ca80ec318330ee996963b34a4646e82a38437af659a18d0c19cfaaaf5e119cb1dc50f21f3b700822986d743b03cbb34a36ce09178976ab9cfe8c12b26e2f0336f07edf471de00d25d772c234c55be41166c12d83a4b51aaab4b3ab77b54ddeeff4b68b003fc08337395a34260b92f7656e6208524b1217bff180f25e8eabb136f0b1e51d4e605d298558b39c34ea22f04654a158527ef85889d7a9d41551d8639ec5eafae5e08 +generate_ring_signature a1cae1149c356e23100ecd1987c21504dfd7eb93e828bd3e67010b2618b67d12 41ef904648e97ded7bb6cdb675c0eb7c3e265920e0ed3a43f6d9d52b9cac4950 4 8af0cd069e8ed1cd5b5210d4f3b1234fba3701a51f71223a642afc1ab7c2ccf8 68a106230168fb6b767c70b9bc50adae625609a4deaa23607eb431a125d62acd 7f77b78b984a927a28d64f19feb63bab35f545c419003dc6804fcbbd0a6a7d89 02e2ec9fbcd3da1d36c4426320a0ae5dded7b03e360e801c842d6da1479119fe 9bc45f2111dce6e47ce84bce74bdb15374994ccfdcc33c497c8c6d02621cc402 2 796345c40c31b7c28da1b765be18d5599137915fecef65eaca9c7c4c78c50b077836cecb6c522209d310a0c07f170b09c642f951325adb402d595e53d73f5e0748ba180db3b070039fa090302f73271b2dfe937180327ed3b1405ac9df198104dda7f8c8ea6f31b3e92eee292451a9a5dfe3af2703580707221a9ff8291a220881febe9d0f91d9e7b421f2e278b8ba89275cb7ae5f7855287cfdd49fdb0a9704a6998a766f27586c40504c4b318ac90cf42d73fcaa731a9ea34a7ae61418900a8a6a7dbf27512441a5999d362b628e634fd3febc7fc5eb862122aeb1ac6bfa080ea30c00eb18d5c047656bc3a796ced004759f1ad04f1973901b10cf4ef4490f +generate_ring_signature a4a40107ca42f136d1a311b9dba01b70ab6e9b488c9d4bb1f82137a8de045b0a afaa82207be08f51be6d9e58d2029c812fca1215e9a4c9ce7614fa9b1ead0e33 1 21eb68d3bc8a11f481c4dd1aa33a7eb137bc0266faa99ad02035de1940e96ba9 4fbd33282caac0fb0207af80c006ae9da1993618bf7f2eff764426fd0fe7f204 0 8b3618108d922e7d6d2d3640d58230d1bf725bddc7bf107711e16ebb8f89160d5d4d5758542a5fc57892f704dc6b4b927e2e0af722e6ab04d600cc9b6e517e0d +generate_ring_signature 856142e92819b975d12e0fe26c75ae88da078d345246b985f3d2ee7b2c2f7c5f a7869efbf3517f7b3217cc10c8d34aa79dffcbcc332863aeb9dab30c14f2c216 4 c2fe2e312d4d1b483105304f0790bd832bc2bb194157b215d265ff8f1fe5688b b6db213bdac6431e7a58bcd7bcad322ac7dc04a2ac609b2e2c1942aaa2acbe7b a9a365b5196b89e4ead8efe752dec4900446c49a93fcaf75c6e01d6155adaab7 ecbc8dbe05d49b8c9ab8538a959747d3c965abe9bcdb090829e109aa9af410bc 4ab503c0deb81e5867cb8acfe683c713eecdeb27bb51b5cabe78fc3d287ad507 3 58001d1592f13fa33037e4bc22ecf72a2970db98d1e8d82722f1d443c2501f0e4f423eb7767c41cf4e1bd34bf17a1440d6ac28053b8b0df157aba27547b94d02e0b288abf2efa8b4348025fb47e1fcff1fda0cab0d6eee78607b7fc25ee93d0fa96a93fe64890827b8c31d267027aedcb2a9747285aa83eef4d557ddc1ba93011ad0823ce50a6ab1906b1c2797fc7cf576736b3ee7b918544535c0679c656c0d182a693681682afc8f7b34f9c3f2b681a61437b2435cb89d1fbd264ecaa8140555a82893d0857a97b81b5581c722763b1398ff8f9d8f153020d179a1a8c78a0bab7281b56141fc77ef8109aed5aa8cde7bcb698e317c8e945ed245f86e172802 +generate_ring_signature d43a0443d5c3325225fa5986febb44adbda0422c3198fefc5c6f004c12eac890 48a03abbc676e7c1e6595e7871d3e47c65756783745b6c83beb3966c83b24426 7 54c73df736d858d956c6ecc78f71bfa621f1668529798aae794a2ebcc0cfe8a3 48f1b704c8549362e4bd701890d49bbc96f029f5b51740cd9af23be3c8e330f8 34d8123aa5765ff6fab0fc962b525c8ae0bce152a59c8336155f62b5f8261ca3 82f6e242df725fa43241d94c88c371eff160c9bca40e27c1ccdfd5465f8d1195 75ea08cb3c88c0dff75d744fe587524a5b3cb012c2f4e7299673dd24a07cd85d 8a3935e33e50f5f9ff79a529f18ed5c88340893bb688622625bf7007e15d5fe5 3261bc8eac8e3f468997a2bcded49c910fdf5b97056bdb5e81535665a23cd13f 55cd53ed388daa9179037a611a241494ad182883ba0fa337a9d6608a6f554309 2 b132af9651833f08264a742dda198646c69fce8c133a70c1846bb1a3f8e10803a07b25e8ef629b1216aa3e2e188daaf8a7a00e7375b4c12aa8607d73222272025a1facc8bc5e1112e42f836c8c9306e2d90f98ade556f4f321a7774dbf68620b6d0c72b20732f83ec7a355a4510508ff5ac223a87c193ae2598f53eceb0eef07260f6773c777bb10c6ef9f3e20017ae99520192733b399cb884773dd7dcf3b09fea3e961efee6502c887bd2430de22b87ca1aac863ab9eb048b4bc11bf71f3075b20c6787939d349627a821fb78aa4e51f4858434c15d9d2e82fdaba90bf820eea738ceaf81093afd3ad7d1ca9a9d7e940741c8497b5f174c8c28e490987970580842bcb73c62e0b98695d43d6c877ba243cc8e13be27224f46b457d18770508e2893c030f58c0aba39f192bdb04885f8f7b28f84ccff9d774e14944e40ea60d5ce1e4c48ec8c425fd377331ee7c495585ccfa993d598da0639d597194aba207891ca46b7f5994f5c4a6a067c76bad2f3ace342a3de56c99a58218699e41580ce9bcb060b69561c29bdb39ac0a32c2ac38547b36c1605380cad8e584c28d0b054c1e110d0c9ed87410151da57a5865806828386ea8401f84f1e864ca80d2d607 +generate_ring_signature b2a6d43314f32e184b6224f1d237368213d205e110a06eab248d8ce14dc66b35 589f05748c0e2fe74f2d93e8bf91c6dd062456acad1a8c440151142a36e37366 5 d1015e1ee08e69977d25d7e23692136b840a702afaa2ca04c7a1ff3d844ebcd8 bbde63dda3a37b486f0f12757b2e5a560d47a041f4bbba732ecf55846ef97288 839a000be768e6908d46ee701b19eebc912e346f98cc523c2d8ae63caf9c6c57 10191593db747ecf070bf88ee12a5f8c16f8ef506c6f5caac4fc0e0d0dd2b6d3 446c82636100dabcd3fd93bd36eecd42dc10ded9be9b37fd1c868ded993a8bd0 c188a0b57f2c52f5ab7ad0e98f85d05d317a631d20a45f2ff8d65e4dd9d00f09 4 67f6fd35d9965b2e5498a282606b90e0f07305a94753a919fabc1e12376ff204b155f53c6b4aa36fdf67ad2c306b1e1e7c109f9c521f25545ac6af4e1336ee091e7ce65b3f281c57294a76caee5280283c98429ba6191e6dcc3cc7b0870b9f0ad76748877be793830ae2a96c8ea1d4a2ae74556e0d692498b296533d4cd27404fd67a698fc96c43eb7d40eaa3787f3897466945ce1ab3a825a9c45e40a783003a5cdd368a6879e842982403b9988881c3b1b0201def49e6699f98d9be55e4105c2c879c99f93da90caf1f004d30f20fff6af22273ccf4f03f9aabec9042db802e0774ce4695d25c33e1b4ba73dc91bae60fa77dbf524c25904bdfafff210a20e36ac00388a75eb42a777f0476d8a23d49866547dbfce1c6eebb9ab59d22b900079fe2ab9940a33b0e071c8f2a43ab902a56e4726fbf3c84ec72bdf41b107e10f +generate_ring_signature 2cc6d7e8e09924db18a5cb8454d9d4651e68b7c40c10f2124c4178819c166731 8a63ba236ee52e329eb0cec80e8281fec17a4553aacee39d386e7023e8c49f1f 211 402a41ca99cd815d081c2481ea92da40524a3c0d1115380d080643fa3d9b0261 c643929caa547433cd00d1e0cd02b77e37f5de0bd28fd21018040431e47ceac9 989a1efbfa052314e3f222b0d6a1870ad23a7759208790c4fe0dfe84f9a682dd 28478f7385c01c31b55f667e04f71eaafc7615d65f818155e4da35a160cc4d85 94a4beeecbef6b573e9fd9b3e959fe53b47de4d0da7b9030066e8ffbb31357da 090eb96dfb16bdeed9a02046eff14c0b3da54c8d93d4708d336ad86355ba116f 7b77baa8eac66b27d8c3a7b0156fcd94d958be2551351438d075b91b63c33a53 a58ed775d98bc67e07945599536d3d2b4cc06cc37e4f230396218bfae0fb1c44 29320ad589c514098a8ec72b9349f0940737f2bfa36e9330f2d42c00a0120386 3a9a27201c7bb4bccef5dc6806d040c30a8230d34eec0d685a6c4e40751f49a0 13f2730c4bf1c2b5a9d35cbefc51d4f74de8ca3271033c7c4a9d0944dde08c1f 73872a978fb3ec59c4f7f86f5c008cd9ee9f54736b180ec968930983176bb464 a657bf78310f970b68ee552e2bda5ebe5da601b5245d2454a30c0c276ec18efc 5ec07e85ab34bcac5d5172a700868c29b9ccbfaad9858276df0f2eb75e142996 9af36317b0c92e94bf9f88b3cb32eac5e2022d67c5843fd761328eb8d2f85783 65631374959a3b4b0fa5538d510a13edd1f616c1a7b304f1916004695dea5eb2 3ec33d71054ad6db9e4c10680d2dd4dccaa408e8796ad3f04f63e6fb6c9436c5 eb7fcde5107a6e168a6bc7bfd51604c60aacf0ffbc41c4ee9f004eec92a441d5 4d6264610c118362726519f09cc02b1dadc926ad5d3a1d334d3203c42a7eaa9f 0053001c4a77bbf3aebd3956aac9b8420bc67d84947128c32d651d78634f2890 98c403100bc3d1135388a8899718c67ae72d86fbe32bf4ad6430eb0e5a871f59 1f0f066060620f72fc27072fb04d111d7890db94da780753c565114b28fc61d6 c7bc71f7658a142c8c2f32112d5f2fa2bda5140632b577c4ded16dc0e0324bc0 e1d43f558301785c73f060139a114b59ba8260645d3a42256fa1554b50d7d1e2 92b26634220d7b331cf050d947fc8610d91f129412b01c29969001e7eaa9b770 a6db9435005617130ddcfbacd5f432cd688054c7bbd5d6c74e35a6b87b36fcbb 8fab60e3f2b22ee77a896e2f244f683e0215baa6c22124227548473e8226ea42 231600af4603f92dceee9d17187e0218993f6f60a91205a361a63a1edd23f31d 41065d0f4a7c7bc1e0e1921ae8db77c6e8bc58ed6eea0cfcbcd8ed75b7a657e9 83db2a59e355af551f2385866b1479993bbf3297877032f14a53893db3c0eacc 1f286383aa01569fb3cee63a35dd32f41c575261cb68afbbb832b3c4eb9135d8 14157b52262d16b5b2b03e0bfb051211b111776d2a777872dfae2a1f3dfbf780 8b499e0ded8c18e93b012b857352ca03f7c53d19a7b662b45bd6f0ce0fea230f be7c42646fbf9d17dc2c09924595162813a1cd92b41ca029465240f105ff498d 5bcc54cfc168b31f99d15c2e23cbbb33889107430b86dbedae47393121c7e575 9115043833cbe33d775d09472a193354f00cca070b86e2f2c55a3c587ecdefaf 62dae9a21077417d38636f9766d2b23e8f31c11d371b941600a68123a0e5852b 2f880dd8a7785fb4f6584ca858b7a9df171393eb1dbb997e049c3c74bd5c28ad 36f51e55611ed2b0ad671bc3b5a67dc32e9424baa52b7dcfa886fd16e32a4a92 cb88c24731f1c01341bb16711cccf0262711c5d538b5ee3d91e618a0aa94ae57 774ee3b25c731b1563ab1a4023808d1083f4d019438eae312a071e0963320b73 80f340dcd9f0edfa672adf1028fbe5e7933a5f412609062951c9c13984477c1f 846e5bb742c7d2c5e540a41b112dc6743c145a76dac105de39dfec0c03804934 6c3731202f8f8da0d540616f32fb1261187b0d5dbb8f4f86e36324adb50379c6 304c36bc24065978e078b24b16bf8811bece463f53926d8bf4cf14141a8accae 6e39e3ac724c929ed1fe8d2a1bba08b81b757f32ee119a07f8feb66c990cb484 2fe3718912375419fde8d55acc1e9f7ae8f3e8e4f7e8c253f180967ce9f6e277 1422514f9fd55ec628162935e2736205f7b19562e10f09a24b671fd3d363e807 ba7c20056d4c14ffd0f93dda7c39233f2b9f005dcff265a9ec774d54a16f21c6 4e024cfd9598522249b0940e0a532f8a2e460a06db24702c8503a7b88d8ba9a4 8bb41c4698f0c4e1afe52d5110012d704e063471ae568e4ec407fd6a7f97f179 1e5665d9af4551c32f5b41cb376f6ec70dea9a3b7c8568a8ce24c525ab1cc65b bd2af664fa7374d46ed3a2d6de4295b2b6d6d241af3df086abe5b9ee7765c8bf b598e6304c34c0a3158998db4877c8025e27de6ea53305f445f774a7235c60b2 7817004add781de4c2bb6afdcbcc88165a8620994a27e96e822c8b599cf9181d 402afa1c572a3e19f68ec8dd5c3f2ad4af9ec3dd0685019da41b208fe624dfd4 e1ff43e5787e2832904d4f55ccc3f24a896a673b5841cfb82590ca8889074818 2b8afb146c7f80094e3201010ff0f6c0503d4c916219e7f08523b6eaabac0627 671fac59f003840250f4070a05f89d73abbfefa4f105bfd8f85f377db6409f70 bd10a4554ebcf405fad8a7371ea2aff0589d0c554e83dda524a04663a7d3d694 81c03045a55f1c812b2280e0cb4fb6e122466d47ec60e10975eca58ab9a13d1b 1b3dae6b495af9875c3705bf1ea6695e7bdaf39c20947e061eaeb820026e9920 d89c734cb3226910d0ce056da4cae31648f5f7689f4879cb440afe2b783cc92d dfe58bd956769ab6129bc2966895f3eecf98d596a15b90dce4d41fda89f51908 90b7690e81a72de5b64b94be37faf4cf2e053c0560786cd3832ac0a887b6c2a0 97f5440f4fd3fe8928b1da92089fa2f2a2cfa22ee836e84bfe6f0f0ac2c0b639 78733374ee6577eb4aa1fe1dbe30fc49a26ca6709515f9fb9ca30ecec256e525 0cc4bceca6c04f76c8cb0579cf41869b523ed6dcbd0a7cca939040b113c5ecb1 a77eb08d2f61991f1c5d2d5c17ecff62b6f355f2c184586458aaf6f8fe1b0c47 e904d9ccc3b5d0727786b623997e85477572f374a157dea7a2ec15ac0b8cbd5b 529a15c0030b5ef91a2b172d050ee093b6a008726a5f30b4ada8e51272c89e61 e484b6e14d164bde5bdf5e11ea0360f4b3751add6dcb86e0609060903db9508c 678782c7d1fec628538b8bb22e04867e1cd37b82fc3bd27af6376049edd52027 7b75224203be14b0d9648feb66944b90a538a38977dc9def8515fae3d3b65446 471cfb7f41e6716e40f1e43fd3bf60619bb61cfe9647bae7bdcf159c6c011b6f 9a9abab7e9ba931b3e07d8b5f2a6b367e83b7fd27b876efdc15ab64c56bb3e99 3907d8e20f2cbdcb064d0f0b5a9cc39869aca703fea645e487ae7569fca87229 46a2823791014ddc54c9d56e45f469871e4f14f33443f7dc337d5910beb1aa19 b30317411d74a37484df3768877d0e92b5522e5622cdeb19db037688633a764c d63f7433d699967d4dea7f0353aa6d83a1db6688bcf05eac535d806f2861136b 05e06df4e9ab60de679307e3b12231ef37c0a7e8bdcdbeb792a140e0236486ad 543583fa163484a44a43267e2ba34db90e9ed2cc570b7a25c0e45b22b9ca1eac ca42a5c54f83e796076e644a436af8565132ddc1d075b95ed355c7f2b0b556b3 f3f5efe8d35449192c8795d2cb1059b720d0e1044a3ca69b1f57962fc819d56a 89bfa2c9258aa739d4fdbc65fd3b6f0a544d9cf4b22de25be25b036e9d9ab2a2 092f55c0c3bc3a275c4ac457c0b15d676c8d5b2a072472b624f1874fbef6c372 11595707e8ea5783c592db350724d7463f8cc141fcbc60a9863ff890d783131e a381570de73bc10fb7f4b4ea3876d21bde840c0cd5aaed2c2521cebbff77d258 c629d6bfd1ea0dcdd46444c5a560a4f386735c7e1dfba644e119da62637e6edf 4605ed8bc6ad9c8734a4f9fa61964edb5b73f1746b568a3525b088b7e4e28738 d2bd09313210ff4eb285b2c1171af27b1f7d3a14cdd78da5d36d5df33821097e ddecb9ee20cc01e3e00884943e5b284088589501ac73c033648430b6403bb0bd f983fa409d58a694b709658f79f488051a5a3627a1f5d3d8ab4f29b88ec85591 11b78bd153835d415546c093859342a069c2f9179fd77487ddd724441f8a1d00 a1be919083bb6e1f0e9ec941588f3ff0cc80bef966f32c1e57fe51f9d740ece1 c5c5a8d4f9a3c212c8ab2cb9bea0fe4a2d1b28ef78d0d92948ae2bc58d879924 2ba7ee31adfff772224a6915cc1026733d9750ed78b0a6003090d01b5e4800c7 f31856d475a6d9e542a8c52940e65ae76aaf239c58c7af445011118479ba2e97 d8ee080399b781b97e9e320be1efed8826ab3332ccdac22c412b96b587e49f3e 046b156f6016b3e8c4e28ec46e6a894a14bd085fb98d272f954646aa6a68cd8f 67da4100ca141d0b335ce9aa673c8ccb1c241d54e8e2d33cefd70fceb5b1d25f 93bf5b1c7a6ea142589637ede192df057328a7ff99325e8d143f5933e0e8d094 2013b7002841ce5b4c162f543ce204e55ca37c290241703cd444638b7e1956e6 e1fb9f7f35c2ce2f064db2652a494fc4132d67f1435d2c422651282e2d5936be 6b84444a3fc1f4ed71f07fe9807aeedc3d765e86a308cbddb549e41a07637540 6922d129dffb6f2e7e05e25d67a675deacf4a3eb7591cb8f411b4bb02fb352fd 807fa83b273eca0d0cdc181b9839f806164eac0da439354f27bd4c999fa3e112 ddea38959723583da1265d5bbbc23fd21d95912ab44ab7bdd388b8f816d7c950 d31e177c298fe21d24cda414ce09ab970fe07a16247afc1931831858f682913f bf212d78a9d4b8141c0e1e03b553c0be1bdfeb5b17cf2b9b109a1dd2f08fdc19 07bbe8caa6552571cd4fd46f165a89c5eca829e04ababadc29fc862625ef3415 43339e682fea6b57b40f7c199386f392488077429cb491f0380d649a87324411 46c1cd36c1efec4e065c8e64700fa8e225fdee7e7b5d0455184acb459785e924 09ea83b1d7d5a7b580b54e8e95f3204374bae8ed92e1271a8573d9d72baab4a1 8a7700e99533fcbbf456c9b22d519a25f1f5bc3099c71aabefee801277d0c19d 909b328ce25f2e5f270ed63fe45b43d242c03c3f74f3ff6001978ebeb418cc61 f8e3cf614e7bbee6985e1d5604dba2374630bbdb9651e91dcdad5ab53409630d d6052da08cf10157fb2a2e3b44c85cf76df325fc919c588086972c6bbfe193af 9a3b931fd12fd1f0d68fd76d68e44ac2de9f0d82fa476f50f0dacafcbc38dbf6 f07f3e41a4f7f79500cebf1b351ac5cf8346b068ab1cbef602fd60136b20cb6b 15f00a7af5e20b918db78299f63ab9ff993ffd9b9c3e50d32b89d8d68f1cb6ea 0402a24ffea45621cb1916265651c80c570a55e8ef7aa8577afbe2b15f3560ab 0ca97cc9867b2edec0d46d9d0fd14071c4c904025fd0c5ceaa5b13391a7303c9 755f312086e70719cf72de5564ccdb28288ac1fea0f41611f5437754cbbb360c 4f865ab94b9747b5219e5b62f06c8acd434b8ba71f9a49656bafef74e0fbe000 eacabe372936f28985b08db8fb4c2f8c9b4a48c261d698e77ecae3c3232c69ba 66e7c6a7231b1d3fa2771babe3dbbd42f1d32cf5f80e8112dc65ac08ea630c05 826d230e93e779a6e5d9ba481cead54668b91f7934e5a47f6d2067356d88a432 38d8c90a333e211e35d18433fc8e97a2912a4481a31007dcc3cfd7d02d58e445 b89ce70d879d1748dc572e179124da2922b52a9561c4e5d4ef787e65c3092bc3 dcd03083e2edc956b86afac0b6b72eccf61a96c0b9b1d8c49ed5fead44c22164 0b3b6bc0b8dd2aa42ab1909e31585da82da55ace9c06a9075120bb1584c4e65a bd7ded926e2626afadb1ba4c6a6484b02fbb328965bee31607f24c1f11cf35bb 1b822414e321327b717a8b73a68759528b22454ae4f1efbbea4acf27553bc7b7 6fd47667514ba98fb485a526bfd3eabb680eec1e6f6ee9b0ea242f051dfcb62f 10eaafcf82fb0ed3f7ac45f994eebacf1794bc7b484c3bcf1d4385534a7ac8cd 2f874b03e03e634e4afd81c541ec6061d956fdffa59940360b6be44ba08baa39 415a29f704999c6a1d57de28f50b6c8a28aa3a45e0f090971f0350169cf10442 65cc454756fe9f0afcacd7ebf5ecef668966b8d0a689ee9f8a80a0d672662d93 ae853a35374a5f088e4811ce400ca24b7292209f5591fb286a840f811a1a3a4c 13fce6e2bbc79ebe247e21edb3221333aea417be828cbf7dec641711cd009c0e d0d1c7e0df37e6dff7fca30ed034b174b6395667162b7ec2f81922433700006b 11700c2375553b574770775e7ecf74d356024c6cdb73a12040f899b072febab7 9f2045dfcf371ec181d645514a7200019f6917be67e5b29b27a690fdf434720e 9211009b28ae0d38739fda0cbc46242b9c091dc864e3849463f7d5cb6eb4224e acf85b5927f99c616e7f0119c2635f9e56689f1cf771e94c29d2bc6af30e3b73 fd8889f59b8fdd47277bea541ef26a7861a890898a15fa55f19df6eaac438054 dc0db4e6550ec7cd5ce876416f54c3bc59447b83c02415fae918adea6df7a4f1 29f5752cd58eb91887c95c38cd3691a9b404c99c5022e3e644947c58a62405a5 90d99d090249b1fb30482224f3c6d3dbc285c8a2f35a417a1a4559479a5af592 81c3b1b54dce4e4bea68f2cf57d5d10c6eaa396eb66880590a9d68409ee96e3e 8e78d814062170b9da0f2df53876e50d658e8abf5175be44f1a847b25456adc8 5c8e9a46ff66124cb418455a16ae3b78eded90d8d0e6b87f69e0d2dae684553b bd3bd4c32d490375d0652524597c89eb2a8dcda40278e9e17eaa0895fcf3cd63 37d269d95abc4010b6dbb4a1f13b0081701a625fb496a554ef50fa790503a0ae 069716673161003f4f4e03e91e61e4c35307be3285e9b9f4f96fc87d9256a112 3bf47bfecfcb4f13b54c8a5388623355fff92f4e991b1439ef4bd2c04c25abd6 8322ec655d08bd2297a67c7d218f22caa679139679f03a0bab4c0c07e74f5a59 4c7ffce48eeb64c719286644bd4dc84a6ca8a09976cec4365ed8a9f1e52020c3 1e66c12636713e73ad0661b537ec450f53bef1f34c814f53c780068c3cee944b 3606237da7504cdb79140ae14dac5e9970763a3c9f4a7d74a3721c93c7e1431f 1fa3ee9449761613dd39dd33e274e6d86709e83d747edc926b115659fab77121 14c7b97c2462aa3bec7a691242367004906e2b2daeed42dd2ea8c0aac75dbed4 70c011309fac84d7b2e71fa0702b49b21fbfd75204605c5bacc0c74dc5e8dc33 189ee51670246ce6697a111207191553afd5580f11cdf6859746993754e7ff80 1131c9a3307f39ca82c6232e5852e4461d0ef37afdecfa26b5022a93a7e5abff 9b130da9fae53bc05605bd8b9b7a015713699c5be1f02227a9c74da3db31a018 2ae654a588871677c065d03dd1d1d4232e1fb67b191a32573926c62d70ac7b34 a1a6f173a6aebf9df3f322eeb425a596e71bfbd8b75148232328b593059397b9 deb8c6156bb9b1cdaddcdcd83b0286e746c59d2888dbfefcf77e5fe1cfcd72e3 d007cf856dad279f734fb20b0e5c97839ac2ed18a607f8581b43fc5b0dd076c8 990073943718cc401ddb885dd082049c0168da46cb6568a56a4b287c9d959fa1 dd5c9a93ea17ec7174a091e41d91f3a03666ebd3411e5ea74938ca33b05dc162 c182c5d4f1d8be0d7a5f1b6ee8d126b397fc5d7fb52dd58b0f42f9b1d65e5d55 7adaeb0744f933f647464c92a39bf738067f6e1b4b996b4f2919289f2fa5ce9a f9adbfd726b517b55912e28c83f19d182b8df969ad4b12347131fbd7e0a8a2ee 0073fe5086e6319f79cb9243f359519f00da4d3be8855fa79f4233f8a50c5874 a6c4da072b6da74e25de82ee19976195b8483fa6a054856792d89b1e1f68ee44 d5f638f1b8eaea5e2da06bb90bdd5466830f036e6093df3bf23d03145d315529 1483358755a29f66d238dde5e62a125d50f6d22ce820553e207172859000a96c 72e77707ee4d52fd0ccfa34c57ed5d2339a69f39e6aa6f371080a3d414e25505 b7e9e15b52f4402ca9fe210563a213e1dba8bd02d0da57440a03a98980550a80 4b1480c24c531e5bee169e7ce570d89ecb0f0c7b3d041fd93f04abd1ca8bdae5 682b6bfc350ad26c92b229abbf92aa82a3d1c789f124c63c166555f4f960f592 94e2a267bcc92476200aa4d5fd0b3d6fca8f2c6a2c69623a22931b8e9242ea40 8d09501afab7b3142a44a531971cdefe348be311412347c0ed1ff2be5bae723f c6a8695fdc9043bf1a698ee3e2df46bc211ece66e0a5215bc16c8db3c676ae26 fece9504781b2b510ce438143395d3ce8f6ed69dd1179b9615680746b4260b56 7e43c14f5b5b2dbe7e3b37f9726c32a04244507d09cc1056084afbe29b97ea99 feff4768795d67539c95ebd9a89f8482775f595d8ef3852a988ab14fe95f5227 e88d342f5edbee919220c56ce616a548f801da5186ecf00a3fae3ce4eb163fe8 bf8e39152682f122fdf93ec892eda4246911194534a15293badb5e09e3c110a3 6654a6a6b4a0f3111b9c1bc051403c3d0a519a5721718e370190d833f63a8b88 a05d4624f0b50a2d8cbd071ad862012b3b01e98b69969f9e5d17b7660396eb4c 4bc3d8c6921685728332e49f6f4ceecb659504966c1d3bc49e57a79422c1e35e 3c497afa0fefe3026683a39fb701024d7ba204a2077ec0daf03d6bb1ba9ec005 3034c04416c57bedd19c75e57f79e9330bca3456c43418e5a6bf002d969b425c 5aab8831da8f0c192081f901a62c8768ea530c96a3b249aa9cd0cc83d404eaf6 ec54beb7e480aa22d6fd6f0c785e45c7f2a191bafdfab0bb07d5b48f092d9c5f 9839537dc7da3a8141b41c159b91c769d0cf2ba6e68b94217c602ebdbfc6a599 ed86db43e4a4868efa115d887acf04af9da75527a4fcd3c6244b6c4d61be22cb 8c507a0484046c536423e8d9af4e35f74ab7d30e0f3d41378b95da880febd57e ffd1a7b87836064b4247e77e6d4e88e944ab3aeea5c6e069161227e0d540482a 038230489f2a54958eba36e8609ead2529e3932f8b89c4204fc21ad19920adc1 ef2b4e6713f76a01478aa2d01d3caa45467de753c810df2f9b61caf0a526b699 abbdf138313206132f30d3c3376318e3c2a1754d95ea64e14b22e60d8dd8af54 5d3dae9e4dd24dff168fcbe2b21ff0313df82a1180b7d7aa0cd4aa1d9584bb5f 1df861a9b7b185f0eaff1c005d7c2bb89fed2ebe0f32f478d2f345bd678b49b5 829a61bcd2a3ad8a8d419c5c8b7fc26f2d13428fd97b7c54d6f888255640762e e6fe97cc23d26246af4db30b1860072a4841915bea8e575ddc5d7739a6b96954 7e5dde760460e9b910e216540728b4ccfd67182c070c6ec2afff9d1bcd0674c9 cce6c582a6b4d913727ed464544472bf199cabdd81769de5009814887650660d 170 5fda7276bffe02f353d71d6362085e3d537b8bc068d8d6857d2b3cdbe0feb9036c97fd04a9047927e652a7759ba47e518c527ad8660ba9a5a3deab787e1a6a0022765a4a0645d610b6d7b7ffbc165c39189f92d160345b2250b657ebd5d7810dba07b24e98aa686c455d96b35d7df3883bb4befbe54e8d512600052f7062fd014e0f925af398c80d8ab546c8c144f40d8f6e4225ff13609d0d95f1ab7db8580f6785f5562ac2c50d8fa5153c3e151c46952adf2027fdee5bbc378642efd0790f403292110b08702f9495037099dfacf8378301df774ae05b16e29c37a606d20d91699a92fe43e1ea98614ba322db6376e183cd772112f41bfc616da7b5a8ae0345487ba93cbd8cd70e4991486634eb4033e06769be384223ce423f2dfabcb8046b6526a18d3fad7866bd596a389ffaa41859f65f92f73f882c77b3037aaa16070aa6a4532d034356090206306fe901645c5d779ca039645f7ab61908be7d9d082bac632faf9d769705c528d02af6b67ee1a36b15f12c5778a49c37735614b00039d8e5e29b4c411dff1ae7d0a92a60fe069e84ae4583a5099103768b82c80f02e264ed40e4d965cdaa9af657a8b5be12975c453cd9db34f0aaf434fdc170ae03c73df3fa30ce5847ca5c44b5d67997e3deaf0fb1ba9fc5acc0b1acb685600d0b1dd56bf8bff45af73fee3540a57a55830e3e7aa63f450b11f9b5421ffbf0af0d62e5f7904e585485f939f8fd99fbc93ba3ea0555ee2880b5379741f166cd800f7d4cb49847f030c49b0876f04003ce065442df33f687cc6fbdf82310e6d08a0fea04dac85fa056c299547df83280d996309ee2420fe199510c346a3850e79b01f6ee83abd38802bcdd6a7e6ae198946fc6d706cab83598f3f84299becfa1e00daafc7f4d96701b59a97645858c802b54256abca1ca596d22f18c9f440fd5d10e542ec3066c2412b7aebd229a9693c2751070d55ff1e248f11966c03ef3c6ee043456f3593f6eb82d1b99c21a73c826ca0570e8a93790c5250cb3935b6b62e00075c09978b2a287b93f30211ff1dc8a06e4d44b240d3676ebe738fb7c498b2a06e61bce59d9c6aa2e90be4cf1e62de6a3d4e329b978eae0284a7bf36228aee903884cb456c866b6ba662d621f153b30fc1fab7527bc8d3f12336f9d4afb6c3e0eae495ed06f2ac43ab52eb144e148ffedaabd544e16bd99d10951172f1b573c0b08d5fa8afca8e3c5a4ee46ca1a9b8f3107a1326e09cca06e83590e63e4b9e70d94d629b1203c54bae9f329105e9eeb447aed1f9ee2466b918bb36039e69d52075a0fe42273b217acfcd403af135c49b585e88bf3a8a984ae22ef4567878aab0e9fbf012e019f54ab863da4430b62ef2c924f2c1fe6be41d86303462e24e2bf092b3638c9d65968d158122b9d6a964a94d4d93e1a4b066f24341c95c5a8a9540b73ccf7b95826840f3dbce8cdbfed75e3ecb3857fbdd3ee13f69a110a96ac280bb52e6fd2500f35e6d619ba24f47a50346efa87a3ee50e97fda50c6003252e10101bea9ed9723b3d5faf71d1a573c5f1f588ea06ee149519d68ab52d8b70124055d056221d9c5c72e8c08375ab3c960ae6820e47ec0cab360b9b98049b8c7cc07a599802a7658fe6ed6527619040db407226db299ee03733be5d92037690cc202f0a828e072a47936ee3ea94b5fdd515e7ad58980b85c79c0338a44cbd52ace018984eda632b3e182c2baa576243ec05f772d07b3623dc54611ed9062f352cd036ec60d28f08380130b90ca023188aa4b3b833612664fb027a9cb69510e698d0457df021827ceb55bdd7c11321f6663264acd85856ed7dda036241e6db096a30ac7d7b0ace5df2e568f9fd56d2d2071ddde4805d2cfc005ecf74d99c5e5ef5b0175a54799f9368c2f4ac6d22352dd6b0ef765f19dbddac7e1864173f70a19680891fe7d839c70444320d0004c0de49f69eb0e19095355d1add6768e0a6ab8bb07d8f9e8c83bb088a182906fd0f0a5fb211f0ff5e578181f0d2a5241a8aab4e30141226e5afe62f7c524df3b447ebd2cd0512eee099da450382c9d46266755cc06ebe0e10980f777c7204b50628b09dbb44d04d2a5066cef4c03830178b2b3f40923fe5a13c683dca11086152475e9e3c78320ca0baef3b20742a15225522fdb004d8e0d330d35e128ef26897f39f49f8b33cfe292209ad6409e6f0bad0a30bf06b5ebf734a0774fd388611125b9e74c2943b5bc33409356c9af1965bbb4ed5008a0232979193615dd2ad14065515f34b77890c28de49cdb2e8642fbf7ac251605438ec69cd43f0161f913de67729f2e5390d84d6606164dd30917ca54b9a84c0efc9bb6254cd6aea48fec1efc9a2335bf6cb232b406e925df5853630fe8d7ef06a75bfc6b9417f76e4d22052655da4b0f177df410310efa14ddd089f904aa8c0c459c0b20d106a92902828c2fa62dd8f44a03de236870f6b2c7c649713607b104d9d83fc4624c793c933a806a5fbdcc49d26094025e09fa63970f436d56bad60f4af3a66c5729237f613fa7947521e66425d4ade98222514c6a373bfef59a0b098a0fb50c7f72116ef28d4d488e4ddc92a66ce2e508954364f2f1107a6ccc2b03c079c461f77a8624a632cd446e65c848ce0dda4e650254155a7622e535ca7d07af089ed739c7482f430981a29ba377b7734465f767c8773bdf5719856c241c00d229d4e42b4f5f780b74412e712de26c63a5c1b88bf09a631fb47aa6e802470db9b3db90666412281b13f42c3447f94d620d016dce157954c331c7c4d777d10b7d3f81375c0e0c8f8635d2a8bed3c93c0828b6b59a0c426a22e1889744e96f0a1a799dfcb662c9bd87e7b51eed8fc0fa28719328843e443ec2f5187ed5e58102dc22a0e0b41e98ce363fda693cd1c0ba1a2aa00b513260ad8943efc6f566cc085ad495aa0dc142edda93a60a7f0eed7e62c46dd0d0f8598f8c14e6f7d3e1a005832b30d50a8dc2cd075aaf50af96a494c077462fdd283e2a8ae7462582ff1f0eccd12217f9d4bff5e99be831d4d21e0a5629ab5b12238d9cb8600f4b101ad80dc4d0d62047fb5986ce9f714a831f39a32b9b90d9aa74276658b5eca31b894905af0c026d4ff041bd351cda99703ac9bcf057a3ed5c4a7c6de834c8f72e60ca0291f8e60efc5db2bd0bc02c47cdb5dca7a0a14d6f5d24650e78dd885dcd4b5c0fa478327ad6e358d54b4e5f6eaa8e7f0ce2d9922e7a1a15b221f6bb9f3174ec0b91e81ecf00d141212b3e51e3b2e8fde71c1e77c3208f2b3681841bb6b87b8a0fb903333460153249e52af97a82270ced5e2f3dea8718d1665ce9e611130faa0c0cc166cf79ce59616237806eff24a9bda63bf2ac5e6d48979623cc736d227608ac09742106f5bc93a9cce7d166260b313da04e19b31bde9c3887a3180206660e6ce6d1c3fc94117196c4c890ac73c6694f9b414a337cfd264aa864d6571aad04ba86c688d497372732be3b6b6e80a1600e9bc70fef1dfb1ad72bc66c077ed60ea9a03b83160cb86e6d4cdb41c3c3e82f163d2507b182aa8191777eb9c79eac032974bffe70736c81411750a08781c2c97620fdd26a8e39fec4fd19000461cc04d26ac78bad1a7e508c940b0b7e5dd88b3851af0aea68041d07989ac02516410b8350babc394bbd84de52f0ab1cb5f0fbaa30680e304fd470fb7c6a2144a8310433f9f7972fe04b84acf7bd7bdb112115a9abb0a357692c7731d8490faed54a09ef095859ec566a698dd88ea4412bf3d62059acbc2a694f639436dd60aaba4f0fd6a180c5ef697a299af6e6a18461bbae187fae4762307ff28c29df48ed647b0f7b486e6966565f4cb03b0f988d520e2bc9465daa7f97b914e0678cd064751b00aca3dba380883c0353195bd7f261d67331708c4f602e2edfc43ca987b47b60056ce116ffb54ce23c2c767b261fa90d1821bb3efc63af74e9ee21ef793fcaf506303d3afd833f825ffbb8117047e75370441703085d86a4a64e654d9d261d0a063645166d6ad995240f0c1830b3fc3a485410da57403b9cdc591d9db2283d000bfd47c4d17cc2e024a77d45a897c779473c7e225cae0436527b70bc782db5000609fcab48484c8ac9a513e86aba1295d7dd7b2bd97cac26b192211386c5d97a040f2bc0b48ef85c5021c348b98b35b69cbd6a2eb8feb54949b7c70a7158e16e078ff54dbbb6abc552f0559746754e4d5feabffc5e6ba3affef36cc01c78def10ea4ce6de40fa498f21c22d434494eed600df26622d06a54bcb5c8cecb425edb069b4111dc66939082ff71822086fd4e5fc6bc4d1fa6ec15575385be72e084490ddc1be5fb2d581d5d909ca46a0796461b6effe82963d0d571a62e9086278e8307d94f0e9d952413a08b4a0733f704e4ef56486e8971ca01967305e9f9b6f32b0f7cff615d1f584cf5a72d99aba4c401f609a6e495891200e6163b9fe38ecfdd064dcf11f5576ade2bb1db21ef18e953724d7a598d94e071932b4b52333480f90ee69258f73492a51f5ead29c642de50a4853839c78e5bcd9987f02d43ea8605016eb51065dad2d45633b7fc1a9b694bf47661177a532593eb261de041cb9cd10fc5932f115f12738783db8ec19ae959a8825698def6fc56b1424dddfb781e4201176973e8c028ccff37f00c8466f7443bf24f0443f39b02b8310d55d7d3f22e0615a097e8968450698eaf33978b6abaf7309a8b3ef120db57b25db0d60a32d40c98224cdaefc5fd58c28fe63810303b3bd856d511da92cebb5de3f5967463280d4d8b57539813f692e3566d1981e35e85d891c41430f4b73c0bc58ce2290a480c26fba04bf8b1a34ac21a0976b9d7741e5b6de64c40949a3f3cf27cc10807ef01063f22490855680a800a0642e982ba524ab62d972960d90fcc14a798268500041bba9fdc494310f11411b5c6114ba48f8d796395075b7faee63712ff463f010dc5ed204f68692373dae4d5da1399aa47d4dd5b5395defbfbdde24786bbb28b066b46e351b6155761543790ce065804e5264f0ccbd6fb952a032df317b0d9620cc78530ed46ac12c9162a4c840b8dcee4958c8a334820f6070db308e811174d01915d4bdb08ce81dc320ff528d0ba126f3ec28cf95c8d83dcca655cb225edfd000a939089c26eb8b8968d3064d3af109b4a2ad71a05cd167720f280a2a8777b09a909c1d571a79e045ef7db071134d26aa241483c205e667e77a4c406a5d3f1032f3a121cd8cee8fd985473b6eca2b397233a395aebe4982aac52c05416ca9a048b0895da9052b2c2fa00b26772529e7487069a48c277e294bf0ec4db923ef000bda2f96f9df6bbf3eb15fe17b077a8c96db63db56af40745a6902520ee797f0fab36b30a38754e47a88e379299af6ef2510bed802b110ddc9872d52513c2ac0486ce5c202dcfeaa326a67ae6c1195531eb828f7f605fb5be826ecd4ebe1b4c067e0e8ec0d4a8c2429a4314d58dbe983b78e0bc88bec77bd585dfeecd6753b807f280f75aba2c23201e555173e9d19e081b23fc18f561567d1fab358c9be7d508f96b353c5fd1bd9aa31283b4a2157ee947f095eafac073aceb2239ea96fd590d2bedecac821958188dd1ed47c7a5aa9ff74ae83d7e00e60b4f618f0647e6980d20c42b9e8765c19f739c505ca33158596e4445efe28298c9ddea8bfe174bc30a18432db20369124bf7f24c78110e7ff04b35a2c8fb76c992f7d820d526d04400ae9ec601545269d6ea40c6ffe90be99a4605b91f1f2f2c6da4b2eb1856bd030944a278e887c80d63446c74aeb34c3209ae9bd165ce39e2b5097b4f2d0b175b067512b7373c4431ff39e448675481311888ea483e4f4363f28776ebb8fae743035bcd5d91a985d0c4372b14af50f9c26b92cee26887c30d20a822a8404c99db0728170d61d523f20bdd125f3c4903301f4ba2f957f2b11e487cc73b81ac28d609589975c0c87cda5c8bfa9b0ef1ea60a8f5dcf2b8c892018a419f5596a804380571e674a8422e8e688ed2690ae968b95b4b291d16515febcb0fb4ca6767113d0b3884ec38be277792183667eaddcf424645b12684ff816bd1d4482abfc49d480f2f038784dffdb22b7d60d6b15449b44d4b2ab4443bee8de5a5c22694f695ce0abbffa1d34e3aa8427b91d21fb4bbb43deee1d6fef21f0136575dd7c7484b670d24726ff39b10dcb41798923b2fc1489d22a2e5a95e1a394b8c89be2beb1985073056ceb10c9900ee4a23b3dba2658db70cea0f2a14576a17a3d4c46dcf63c902ce1b74ebf3fee12c55ba9e61c88b58303d89e918d12b96842f9cd686a70bb20ceef9cbbfb0d7f20c0ce6e95e93d2ca69b382132252b5c76c5c588ac07b98f7014324a11c1de768084f4c11c09ecaafb84fe934055fe0c455d81e0429fa74510d31f9fefd65f25cf573c588baa13a36ade0c005931802243b8e45895fb5f733022f1c3039bf0e353d63c790787b32e963a5b64a6a055abb7fd65a02dbd862eb07a3c25bf473bd7b0c6a319da7b36d7a24b280610bf26439f860c6915c1d72d5033930162f0a344bebae233bc3440159a5517a8f3eea84b5911d2099d20c099b092fd75579b2cd71c0221e31d55cae5afaeb221cf80bf24029e10496f1bea42307cfcd880562756bc12138edef7f8f256ef7906068832e4db67c1c71684d0dea05d6e8441102a9fae0e1e1e01854354a00b2e510c677e37f7928657b28f900ed080fc8120d61fb0ab548d74ad1ceb030b271ac6a6fbc4957b7c74b326ffed88306625f47d9307327492a2f89e05ed78422d300eece87787bc09fbb159462bc250b9be0e5f0b90fa26f3e902e09265848ec3742de640b908edab1250b67b108fa0425b472c34dec48a8a5e6fc563e535295843cd6940c28f8fa58c5b4a0874e0f02ed9dba6ec9830f937e83f3c712066e7ae0f657da48744433d015ea2296b4d40eba46e4cf25b9cc162e9d24d192fb1d0da60e75d7f769085aa3503342c8eed90d68f81aa54f62969d9fef3f50608ed0c1d9266516b2d459e7730590d9fca7b70e2cd630d0b43e216264eac09d2e53eb7bb27b9b708b8e4f20d9b5f2b92917c90e1c13d02ff1c6af4fb468848844e1479997816a71c57f617b7a699170409a580fb78cf0b8eab962351f253dcac75aec34891da2c1f428e4dcad12ad85e72f2d0f54d0b139b858daecf9bc75dd77baf10cb699e29e982b8c8deab2075388cde601739751a4e4e44084cd919b3bc0a45dc32c9d5734c688e0d9a6e693b7d12fbf02756a38aed80dc085ed934ca0755785751f4d3859f06375dda170b6a088e5ec080935fa52e35c7b927784938958b34aaf78c14ead3f7a265d2bb968d4ad242a00de6a7c4683d356c58a385b20efe7d3f2bbde5c7c9c7ed795db106cecd680070fed9afb5162564594c40d2f398b2734b41d24274023d11efe08fa540166e4a303af233f140a410f77f53beed9e8bbb971cdf2fa15bd1fc1029664f7592b25b20f20f08ce5750fd8ff3efb2016ec90a2461faaefc8ca4ca38bcc5f47f39d11340beb19222c43d0aafb2ade825e7125a967c2e9907432816db7c3bcfc418f577e04b1b8f631b6cc7c691798de7713787d557cd05933f44ab2818b6a24fe9791e80d2eb8d7a8d449a23a821efa8c5421b20895e97b34985c13f18d185ea359061b03e3ac695997ec614f82fe8d59a99dec3ed2984df4286f0e5b39e7998c29a8cf0b8a863ef5c6607c57d2d1996a7311a6aa6102342d3951243b8fd5262da46fb80241b55647a2e01dd6aa1de77b929d9fb0d51ed6c862d39c282d1832c0f8c11b06fca7cc3822478816988c6cc5c9dfdb009a642cdd1de78c243fa39c6df308b7085da84a1a17996bcf1a47586e76b9d149c32bbebf618fcf5be646ec708a64b406c57c90a490a0e5c58a41c48312db76fcd1a16e3528293770749cc40c2bb0b90fa9522183881a324a971c112b3dad96c3cb6052ae2adaa03d7496a1caacea7a0aab8031919839edb89e7e8c749a654cba13f8cb8344659859a379aa6a832f700667e12099ea769a1e8ea8e7108cdb07bed50ad60601513791216b4c2382135b06cb44ce9f5d4a6f36e668145d9788495c310217423d859ece5c014e9de01c430e89e3689272ea2d7badb629cb77dae0eac2fd6b695aa4d7ed7a0e3c7659096b0df7717c27cbbd2a7bb777440d3f1b29a3b2eb2aca6f6acc1991af0cbb1458d806b1753696c05755befb92d3f23c5d42aac1975242dc2049c4d4504b462e65410707623ce04282c159cd1dc0eb2776d797e40691526052f717ea0ffec3353fa405e95fb65467eaab4fb248497508c41d0ea71554fb026634fc76960e78ae0f0b04c8f836bd30f29305a8cfa2c51bc411656e98a2c42d02bc742392ec0f967f8b0c32e799657f175738a62f8f08afe00c43e442c93fbf643d4c2633831a4f5fbf099f3149fe42194f5aa19168b4b5f1640d0a4e9a484464f365914a6251d1720b018579ae58b287f58f8961922c015417790ccc5f3196b81a0140f2a9f2b3a12a037f6730eef1b164d9331ac13ad44ff1db00dab5564d2b57729184312d0808910c38c6247da6dd22c1926d957ee1607527ec835042b47b078628e2c0e154fbc3092a8bd2b26ee18f3be850b06f74754ec40a1a4f0a9683c626eb541d2edbab130521b1c980bf71acc5e3360b90bf62507b9cfbc2e668b2e0b95ba229fd6f7dd9095e8899b6bb49866d68a7d6475c6c305997fd823e265106b3622bda831b24630e5fb4d95d66cdb8e5624f09df6e95ede68934779e663da92d646691108df27b04ad9ce0e62f411613fd0f25602db2337ada3be3194dfed48168db6ff5aadf06083af04660fbe93f64a5ca9217df9aaf8ce9f609ed7a31716e31a3aa825f9ae803b878e66008ba56da671c1d58515fbd5b48d2fe120dd0745f6c64115848e0ab061c9f9f8ee13d13614331f628c3f5730bba0af989d86912e31f80c2eb8ade5e08043c58345d18f20cc71393414f8e052c987d3421519b665e328146bfe115ce0cc1daec25fec9bf801417ddd038c9f01bd37f38ddc35189f61d8b97aecc5356002394aa1175b5b234208ec7f639c5891c2dcad8c773cb4e65cd9c75ba9599290158c7bca37c78ccaf3e4fb04955d7afefd7a8b17d13b1fde83dbfd7e8d747e402f2bb6f5c271e44787e787d5be836a83f699341c07e1d515c4d22cccd9bce2f0781d35ffbb0e35ca9aaa5d8ab6d27c566b42f74ceff822a200a96f17a216b240ba0da2a917a34d256ed6cb2af3e8c4172eee2263cd85220e9d677af879d31da0991965eee5e81fb7bc547c4735201d798a3bc3e8dd2851102a7138ffec5892d0c2aca6d8610104621f0f54c78e68750f2e873f20831ff4e982d5efff258decf0b298e4538d1c55a6bb163985b207156d492617add7381c7b387a6f01adf576e09f32106f50e4ec47311b9103152bb2c18d8355cb6a301e2759131090a57cd2202d36d62e80bc62d77f8ab56b6fca813b24e9a0ebad9a44723eb8657bff211c50c0b7e6ac8d63ea9cbf971160e857dfff9835e49c47bdb930a4920958e6abfa70cbd1a09ca61c9974151d9fffd8e627e90702ae4b99c2f9e7e37b5cc0aa84dc30812d5290f2e7307942b22db66b6673a5cb3b96109b43ea5ce59acdab85ba6e30525800e98d5cfa6c5f5070bdb0109b37fe14c68c21310659beec8ee27f62aa50c31d0a0aa2d21ee33f4723870633ff89f776f40ae7ff9f1425ee32516dec4f200613837fbe1f00179b58cd6a678b53560362c5e07b770211f1bd297033b22d40698e78bcd093dbaed71f67fe0b1f8057ec3a0daea31fe76be6387f3f66d4f7506bf30d809b9fc3c234587f6b31c8d2c07b3d88b7f8fc232259de33be47463510cfb87f3afa13b67e44e650c26610672c5f067740cd54201984b07bc8cee2f0e02277b0a72c46b8042da256e57d1c3eb66b124335bf97dd06ee20f91cb83e48f02d163b34b853c800b507da8505be1a7e558ae5e8f8c7e7858d4403ab8311c3208de79c79f235612f6137730ea4df4c4e0e518578e3bb6b24c84d5f56ff05ba30586c035278711cc00b1bf0fe22f2cf15552639614a240b65e298dac7986d9ed0c5ee38f93d578422ca60e6ee9d893a95b378096ee8a164b6c7e403cd5e754df03075fc826b87553a67f33e93e6dfe51e4d2f242f4222cb8bb456119ab5ecef600a2745642e57ae63e62c2544720d3658b9534f6ffa010cf4032f5b3f16a7c4108bb7ff357b070c8b83554fc06d1ba2881278d28be137471c72a4e6af08c4afd0b91fbf9395ebc06572c7875b43d06ab3e4d9da771c1fa3e5ab78962d27d93b50713fae3be2a4a59675eda0104dd1b84cb42aba8c501b05c489f10a744acf02f0250bc3d744c76e474d5f2ca2d49cc87589e8ebfb3611a9d370227d41e65df200f729d4e28f0f6d07612ba9de438fee323e61e1ba275b4fbe2cb24940feed4240ce05e7a5c6a00b8ee8bee51fedbac4ddecff70c4c9eb061d724bf4353c7ac68099ac8dc498e8bc9cad3c3baf1bc36cf10dae1bd63f91e705df6a28b945969ab06831e4e6a4e7ef4a486c2253810870b1b6281a7e5191c4efb5fbb3e0d4c3f16016a06c9cc206c116496e4ddd85c7302599c1345dd7a01c5f5aea3adee1cc88005e343b5c5f0856b2908f6b7176cbef56123e07d666cb61b407ac310cb41cca804cf21ca1c32f816df08f12f4860f8a79ece4f62d2851250b1553769e5002f2d061a11515f6b66ec23110ba4126d96ecf2822fae8bfa98ba130e7d467ed93f280bbc20802a528bce488e9e48893597f20377076f0d134effc68e343584c404f703a846c5adcb5db29e56f4b6d58e74296f6c38ca4e113bc6f175994666b3f57a01a99f216f4465a29c195fd991165fa0a50e3dc5840999d214792d4d368c15150f5142626b85461ce3f0e6b4829b2e121f64c733a60226530244fad41014774e020f6939a396182bc6f638cbb64814d4bdf6742a2798f4da39bc9472cfc6755c0a911504078a3544e44d602087654928e84d85aa7e4087f9e831c05737bf29a108012c93376641f9e78a3059bb38d9e12c11a69e4a87ed0255869c94b02bacc70e7622d1c84df9ea82c4ac6d31de599b2af9f7ca9fd5b3c345b350b9b01c21dd0c8c101bc11725cac963e593d3bec607cfb393e09695300453574b23e01fdc490838ad57e08dc6b852d02abb7e4588dc708ec9d5fb21db2d70a64b4d19437a5e0439d95da018acff669c5fe760591541eb0dce5d7104920202718501cd69bcba0fefd7e9ad7857bfa1b004f41ae97e1352fd9c8f5833660dc677a78bd15243a90225d48df8c28bd5faf7a8cd221f95b6d38103ea4c3520cc4ebb8156f5450b680c1e4ab0c38e9de2d871ec17bd7916eac380f4900f965ed4e6e4a76c2ff7f3ee013eb5bcff371183857c03f62ab73a6cebafd705cf2d476a9ec82689d898181f0e52896642ec8b96ff482b9d1dad4f311feb5fd9755dff1f26ec9f755e910737008d00817779f5ad405700f00d2337e38dd5793d0578f7fb26ef62f8363c86a604c9258905bbe52992be611ef36d7814296aaa2fb124411dffd2084e88a4663204f3b616831b05c07602125487d4225cff288993ed26a1588f16324dabe05df5086554604dc57863e7e6c02ad8ebfa9b7b58f24ed0e7495eaee3eaa23f60decc03a5d9870aaaf3168e2c4c5b28169e8d34fa8ccbac6932a277db6dc628051de20c54289e5b922debd3029cfa453232b592e182b8429ad33504ec30021a48c5da04779395912260137868a57d6359f63be0c4f7c80c742ad73e2a7518d337f4b60c2c6a5371852d8d3db84093354c7d82f6e7192da0bd27c383ec20be13fa90140d132215ad5160a3c245ae9d9272aac87454de860e2fdb08bdf9a6961021c90a01b8b0a7c2b27674d231e5766f9f206284a2ebc4c72e792d8fee29caa5eca92e04bfcdd858480311131ba4ad3305e5b4e2f8370eb3f1dfd3d14a49f8eb783492028343860414a5ab3cf645da149b623a9c25a5ac2eb16f6aa761e2436426b003024818656bf12dac5a2dccf67be874458e3f28764ab48861c13c03efd1f79ff202fc362b2911f386d0f7ab062599b881399a7405eb0ffcc3ae90d611bd92e8bf04f5062c399cd6008b46c7f5a72e608acf21cbe708ed72d7eb82889ce8edce1203add0f76ae8c2920cca192afdcf8dc2cfa027b3954a6b135be14c7916da25e70e8502306fd3c50a9e0477ba7d8b91730e77c643182ad3aa4a48f9725f28ec330b6b8f2d0cc4a571729dca34099d4cf199a0661244c17a02ae4d9e552d13faad09710a3b6b42ccbea9f615b001f09b02dbd2b9ddff9a923dbff04aa61954a9be0ff1b8500c9e0796a17b36f447191f5a5159a03e2670cd0e8cee6f4f13d6591a080f84dc2b10c5d663d90133f86caf52f7f751efd7c46806bf006acdc21a1b07073aacc92f5db917564312fc8d068e5dc4225cb1230454bf989d3c3c9b87ace00c11d019878d3cf1c267965e4ac2e381f8d3bc33bf6a31b684b9e4d3c5491bbe0e5d368965739cba92380d58f7eed8703eaed23cd0a2fe7f6656e05449604ef505923f50e2dd5050c1b9691344a6bd2b63312cdae32385a4153c72890baacee102df13e3fb2231fa2ff47701ee7b4842fa4d894dda1104f2a591d23ee767e7290bda41085079ea544bf09e228f186d8b94d6fa7615980459c9ceaf0a933e4ff7002a1e70eca0a47c0cc08170738ee286c873d77c77a4d78a1bd65c7203d179c405f5156103452b7466d8d3c9d0d686ea0fb5e39feee4511abe3c73fbefb0612506140e9cde797fc0cbcee9fe01396a294064a11188fba3d8c749e4909623840607eccf527978073b6206db80253333e427b65c03075fda4c9901bf9df87387510ce7f4abcdac44d4d2b240a859ed839bda0dc6abaf175b30de9fedb3c99097910ecc96b4bbd92023af202f886a350b6d128599bdbba366af266582c92cd661630b939fa1604c47882585ade45814f94d717e6d67a52f5fe13aa9d69a78fe41a00669bade2d2ec8ed0197606ca09ea291881c061b3ce8b4f2b1f5088d839787730f4f232402962f89d359776f81e82fdb8d90d25c0f396a70a572760e69165d2201671b0b19f69a641bbfa7e420348347301397ee1206e6da5bf67642c7c830a50676813e6251feffab2e587956de1cdf619efc0b0dfe85bc2653e335ce1e7d010b15adc24eb4ba2a064ec2b78724bb586524788386a1b0ada4f8dcf24303993f0a4b0049d7385396d764d62d87de60b2f20e7bfeb771b4f42fadbb975770f4630eeb73381744e3c6d8fc62c2fec1d6b7ab4e9fbffdf00fc27b705ad6679af98b09a1cc86ed9825950a78e9a5c27e5e7983c61182fcda36e75423d87ac9041d320abeda411ab96dc4bedded92752b0ca4ba8860bfb8279fd4d17374c606d4e09d0bc85ba45eed1dd4f869af265f032cf89058adc0a8538ce8d3243f96123a430c0309f8a5ccc588dc9d7cf08824d789aeaa0a405e687ba7b97b711da0912e35fc0f9c946ab6cebb64b0133e441d8d02bbf061fb77cb36ca452be312e0ae699eaa0425ed7d62876d0c945545ae1a3c9a1a4f7cd5b3764bbeeece90afc6a629c8f90f5097b9e767a4486d4f8607ddf40dd25c2df16c961b0ad1f75764df8a72aa6907f97e1fc0c11c5627b95f68378ea65d726f56708780cbf0f076009783f6031a060d8b3a0675842049af91b419402d0e7fe286a3e51dd094731e585960634eff05f3711c43044c89e9076f1d9c57c5aaee2d921a6b32e2e88ed91b7d800229b60d26abdab5ada93edefb924150504c95ca0162c49d45e234c1f4e26a2a96d7ac01e0e529d97f78e0aa1985b31c3750e4c7810e99416d7a09660732f5671848e909bc42fb8784dce3728579d04a09615c6a09fd49e1a4f985009672037e34722b0c77c53229832750befd88f80aeffa6f1475d873613695181a7c9fe64e9e8f3c002aa73e0eb6a21a85a512ccabb93ac0e1b089b2ad900f717bd60841008e97400968ee123a39eadf024077247a56cba05f8ea1e4a8b0d616498709041d3971960d068487d9a6d3386c56574c6ae9b1fee0aa09c33b24b206c3ffc8e6739ceb910dd7c12d235ccc5007e1c91b91e9100040c75eaf3bcd41e5f5e8c62e84809a050effcd505448b3adcc70db588b9782c09dab732db476aa483366927f76515f9502e3012971da123c8ea5e5901382da58cd269bba30a9682737bbdef4bf901a0903119f98c732aa2eaaa9f8094be64793bc38a16d503cddbfbc1984c87480883704224e938adf0ea5b6927456fc08132f6d52411f53f5d41df9eac9f109f8d39207439b510a791516de6986a5d008364f2e006313af6ef60438ed42415d791bb70b090d06eb3dc7f597f9b582426277de4b7eabb260e647366adbefa72f45d50f047f0be10fc5e9064885c66ed598e460311140db05615192e5e059c55403777a062878628b75db81fff138611fda011a062ada194cb80e2bb635e657d4c5773f03b973fed6428fd0932f21a7ac026b8b2c94dde1cd3182d525211982a75867360b36f819f2359665c774e72bd24091fd7c4d9160c62fd92367eefd9115ccf57c07c5a61ad23406fbc369ae3e1f23f635a9126fcf89a525d7f28e1df60a83d0f9063d0c7b57befa8b2ab425b43301d639f7579af638eff5ac1cebfb0f1dc053240dd018221c868ecf390722d013b346f21a755592049eb55cc066bdf9a613ac18013c95771c8e97d38cc2591a8e6d053145d3a4c15b71effb726bdac189af1b7f0f0e4eefe9f006c78ceb33f4f48fdbd3567dcd9a28ac763beb1a3e2e74edf43e02b6a0e08599d58ea64f8732a8fd2cb39e76ec80ced1212ed5e35cc7d18c2d770eb4d7e24db9e651bfcfc9e5e22c329c846dbc42712a3009ac257cb6cef04d760bd9c23a51f09ed2080f72b3e34f71bab5a601458f2106d3e6e6d6d7e6c03e0b0997daf163144ed852c2200e2450be39258ececd5c117becc500ac5b79e2d52707f43b12090c31e2bf9abcc692263047243c61dac20ba4212079d5a2bc364f1001966a85595fb413102a7aa056cfa886363d55cbd9ab0f58a365e36e7a219cf10b45ced70c0b5f81793fc5639d7dba3bbb06a53a81d54e9a33a26b8b005a82fc083a84478e289b3fd24d3dbbb5e5730bf9cba3dac4ca5dbc362935b971af50920c9eb46475734f7d42f176e78c8a226baf097f74fab4a82ba0dd721c50b8e01809718cab67b368ecc007ea05783cf80c464d7acce98a44a6eb2053393d3acaea04c7c88c0ddc657b807ca68ea0953823186097c0c8b304ca516a1a39cb35d4c20fc38e66d62a01d74eaab3bcae1d80af89219a025e56a8b34959098bd35b71bf0d995fa176d00efae0d8765976a1fc4176b6c67ef895eb64e30c3ebfda14c2850bea6f068a4dc113e8c6e412b14e347c073643f347daf88cacfb668a309462600d257fe62504693dd813d55bd5e5b6d75c6f24dec3d10fa1ca8b062deef38a910611399ef2ba65ce299a088ce2f6760861000703c9a8b63819f8cebdd87e0a56062956224d5f08319ec945f50f57fd42fc98af19a02075e0ec217a747dfb3fd70e80a57b31812e9995f4780870d81457ad3723a0ff1b065f3606482f2b64d8090e3231f373245376f49e25a2a925f058b632c699c32e4d85f28db31bdef4786f0e1432c8af25e8a66fd924feb1bc49ee1c418b0dfae82991edbf9ed677c82bef09456942f258efcd9b203a140a0352cc84fbf276c94323d37410a474130d4ecb0f76814c9830408ab5764f1bcbb6a7048a4bbf515826bfe8282697a4cfbe7eaa0c65895648d1378164a9bc787d3e6ccfd3dabd7d87d9053b53f8cc6fe88279f80c2df6eff62c7bdfe626507a67ee295100fca8b1df2e6887516177e727d08d5a0332dc8be98862c587d2c44cffd94cefc7aaea5cf195643be9b0f855747e44c50b45fa6057d9e3dec3a7af9e63b8650f598504243d1a7439b5af646692f1de92008e23015b9f5057f78ff5aee33d946cb38bef508caccf03965d1e16042223950209de0d44c16488482a4bca2c4e882952d57f22f0d58ad488e5fa25395a4d6309ffc01afdd5d134a9423f102254304d5339a2d0db63711bf27e00622373e11c0d35161da5f4915e520aeff3e0d644437335e18d9145c38078714f964fe9774702ea31907cb1e0443338640bdd07bfba58edbb8b3f3d4a0c2d87a503360c78b20890dd8457408b712fe95b93c8eeb81224ce7373ff394857d4c0b220ab8b10ca086a3e33782e7b6b1fa84ea82b97061f430980be5595d0493e80f3c20f2cd10b0539f97352b592d42e00091fc312860d6506609f68e682eea30c972b50a53ae70e31678b1d2888198445363ad0b571fafb63c95178a38db0d6a3e6ae15d30c640be812265cdeb9e136f953e95930d367989c2fdf7a1a8c01aa07fea12ce29339062c1d01eb0997b7e606b9112c48556e7254cc986cace4980c2f285d492383d30e7d7ae722a97ce9ad41231865fb3ef762fe5402e83b4c41076c4a2b377c36f406b452e83cc6c4a1f04d469d86d8618c180bc92b722ff0fc66aa4db222c0bfa00c36d9e980a6558d3aa912c3c198e30ba06610a2cf53421565974678b4035d4805973c939dfcdb7a35d325b4f22449d1e83308b98c4be1351c4647dad678563d0e471f2fa769a615ee51be5bd56810f54e964eda3a4a02593e1dfe729a0925a00c3ae32a9513eb8577d59c9ca6ec8dc574cb5e6ced89e376453480dcad563f350cc5079a8f55c252c8f5982ffb66fac1bee2988a0ef6f14fc22a0c7a5f2e2a2107f7f5d0209f0bbbf09da9e844b1a3f59c4c12655664b146051d557ecfb3f12905a7bf8d0bdd033edb4ea70b35d1df5bf723f970a8717d83df9cd4db3760832c0218a302de4a350540914ff78ad63cfd5e311a6edd172084d15fe7cc30a3b59005cfa82e2a744966b85244b494b51d913d3e3a461d72da466d2e44092d451eac0614e3c3f981e56a488dd7c2655048e283197a3cb4223d6b38df3262e20add570aeed51819e04b645a538e476442782310782df1e971dd61d7b77014a464198f04711e3a7c9bd6f62b37048446db74080b6637ce264bfb901a583ecdb3f01ef404c2ecb491e10a299eb20be84fbbd929d833674177915b88338a80aa4654114d08a8390ccc731fc443454b005c2fb964a51b220cca7e6442df825b812369e6960d434f9debfeb29a574595e8126795bb75babb700f79d0813e91a144b2fe33d90a6b80d03374c62edf0a6b3b56d7b577b6a8e515e1fb0c079012cde87a8b6bae0e493b7649d9c76f48a7cb30a477c77eac6689e5d7db9512e3275378fa9a6bc2079aa5d456da9a504906428de84f9b34e0df23c264b789d79aaea6c16db4f81c041bc90fb45facb255dfbb0e5d2521ae74fe4dbcc7b056ff6a2e19085455cc9c0dd18a86e5d1ab695905dc95f39f653d6db128a281d3d08af9ba20691aadc4170a6e4a3ea298fc04c45fa7a2f5b8007ae277847128180a2e7375b9498fd517130baa931b6826a0a20faae159a1087c4e2ba26034652c0b38a53b0aeb96e9ca0b0f700ce71e41249cb4182997dd04393d81131d579902335abb5a177c138865c20d0663ad8395afdb154c78578c58e4428907bd3569b9d7cf58c0e1c53f3e57e305d2d79d0f023944cd466d16212f4501e2ba289883b151dd553908141c28f3cf00cf4894d8e3bd4f0bd4ad4403a72fce4822ca9eadd4acb82cc086c164735131035b46c652f905f48bf2509b2a78150860f4ab8a8d65dc610dab2d453f12bb69036119f0746eadcf8a4a03b1de0f8c22f0801884334a4b027a0a47d3c4bbe0460938a3f002061ffa01dabda2f96a786a4a19fcd0d0099d1a498c8b76004732ac0f5f518d07a775fed793d45508a063c3fe77135cf68e512d2093bea41bf7d9ad0e67466088f4f5bb5614b7f3822993b66e88fd118b3c48f9e3452314db4f8842087198bdec5f69eb8ae73453c8af3dad211a25b2c83157749b14ac9228d7e5ff02e7622c37caf11e219f09cbb503a00f7f2ea0d7d1b0147ea225411ac039b88a0b4989287f23603a1a32a19823f0e238c7418e3002d7c8a5ce212b25108e738101db82fe2d88d5ef70ed231804e1716333e3cd21f8976c556e2af79643ae21100755ac97b7a5cf7e660ec162a4dc7e6661d31a804a51abe6178f7ff2b4a5cb0f0716401d87bca54f1d10b3ca6a44457469fb4052cf008cbf4ea6a0292de65b7b085e630d2f9f57e0917ba530095a5f512c236e4888077c64eb3fa43b70974eb6041e71305259839f7cf236d1e66e488204c108c9f60da7369562bd9439f9d1a10bb3dfaea30e7b1eacdaabba3696a4ab5df990fd7332c58776b4a87eef1f98ce0efd45bad2d47965c955aa7a2e4a93a6cde28e67ef7d6be89ca917b77c4d7ac30d42fb9bd5f1e5e42bbb6ea6efa84ee8a0e119a751f6be839882eaabda76b2ce0c8bb85b16b5a77cd0849040b48e63a96551a1416e712e09713acaf1f6bddd1a063870aab5ac79b04243ff3b1a23d265c8bc6c151d9ba5ce38175a736a38b63001aa96f91071849fb26e1f4293419874c887b0541ba0a84706f56f6793d1119f067a410d679e909e93fdb99b04a42fc7d1bbe873f0a6ddd23f511992c81de5d50a43d105d6db074c03cafbc6905323a0ec9109501c41231e913a9cb5ecb01efc0357c6a24a6286d8833857e19195cb37f5e18148852757b19b3ea136d2b544560513e8094a108ad8b50136d7ea1a5689f748830d234e7d6b4e4b9fd505af622a024ea25c55dcfe2503b095e085957a30b9ee55e79023c85f7a82fb1be5260f9908b3b02054d1438726c80b2cf030b132f59d08fb3d578316165f846af287eea00e246845c15987f0b84bd05899dbbf348e52be505ab29f67fa261e564d625cdd02d63305e4267448fea8efab8c2c6c124b3a1e763b28685280c37d1adb002f680fc17a7e3632dbe18c430f4b799c3a4660faf039ad7e67d724bbb2fb9b10e5dc0e +generate_ring_signature 68d6b1b9d874d51227c9f0a28cda04d8172e7cae79e06b58aca4cb5fdabf36c0 3438eed5f743a6715d57581bf005efe4fc4cce9447e6d296bd602250d8c16fa2 32 97f5294c86120cfa379c931f3616ae5853e5650dccf93877dd3d22a949deae47 20456187ac5551528a6c1e1e6e6ae511a8835a1e17c2d8376fb0d3d68f8ff749 0b8117871c4b8a996719c93ec132408c440b9f0a73d36ce619e4ea5099f46f3d b258d44bd5ecceec9ea3b4fc8e95496d997234a63c7b6e5128b39bbefbbc4bed 7c68933b4898deb06963efe87110b3a446466a13b18660dd8f550622629d0c0c c2a30c8b5167c28eb95b91920dc363bb6f933bcd64986c3434ed89fc077536ee 3c98ea89ace2a89cee7bc6e06f63cf5ba9297273b9044d4c6ae81b9e411bb440 cfbad8a65a06a22fe90f2bc11ffc003601392504fb1b3f85177f769e22481b8d 8085897e5683d6b33672d6e2e7532337eaf1a6c47d6eab0832460f5460f9f495 a4545f4596f607250e5de364cc2e0e070e37d1c330a6ac6eabb3d03af415209a 9febfeb7e7bbcfc9ed9d663b81671638f03555fdbad469150cb649f765d60d6b b7240cef5233c3eaf7ea031d7ed769d65a7362c90448afe2fb5fba997ceb6d45 0343b7459f99c7ac11c3d9f829f5615c5bc60e35569dfd97b78e6ae2e00f5d8b da9d89d8f350d85ce47516472c0e73dbe1114aa92de1e847d7c6a1903651671e 3bc5140c5ca0e3df7a69f91bdb268dcfa28550c35d64da809ba9940c03dc842c 85eb184236053a9e30b23e189e446a46c11be28359c0b4f70fa696e88a425b39 d08857473602cf81bbc7b1b17766fc09a7fd97c3994f8bc4de94c6aee03f3e99 7edfc9671443ccc734800a1479d6e7840689872b8652be4e97c9ea7aa9971e3f fe172c59587584facc28059d30713ecbdb2f38d75e967899cce7043592ad3e02 d073e626d083698553eb8c7d60e5a526e06f9e288f2c0216cc71535b443a3528 6621abc1d1d77e0e65d363b1dbea5a4651a810fead6d57fb116791f83824d668 fdffaf67600740168caec34bfd37b0fbb16275b155de1eb1bd7b39f1150d0c88 dfddcde4c8ac2861c78bc7df7cf627f7bf8d115c825a10d470ee665ab72bbb22 4d93013a13fb22587ebfdb63e9d8ccb1f7c04bd51c199346461fcf3314094887 f008c557a5edfa69389b32f0da30d88b2e6b91a4e37600c14772ffd578a67930 d1ee1579b8fe43e9faf76280b8c012985465491c20bfbc29d9b2f2e69083e16c 5056cb0d29f7e3fff91d6f9ddacb545f2266163f049ae76407df41c25101658f 80b56590de9d2826db4bc73ca7f18407fd0e5c5c03dd9841c9ded9b8dafbbea6 62571c909782c70567e9968ded1c05a4226a3e04a07ae9db48e0153a56b2a468 fa3243902d44e63e78a23d70a6706efb6e453d3ed42d087cabc7655fb83ca3e4 976c32ea460a47aa3596d836d70bb40a84e6f3d9ff1e2e01a516cfeec938a2f1 04d573623956db0dd22344c139dc7170d8ddf6296ccf558cc2bacf230485aa05 041c83987a208dcb9b7658b567e4baa29c0fd278a85a7ed885d26e4356250908 11 f671094a78aa561b3b55a7f48d64975d9e9affbe91004b50d9d94e2244434702613f71f6a4d2ff96c5ec14b35f04e7d6634aa8df18f7cb354e3410b9273eee0a215bdfca3db61b3961e6efc29e6036a8dcee279d82a32008081cfa4c8b7cab076f34223f8563f8f4406f0cb88c8b5f9e30ff3f37c48eb80814682828dfb77c0129c1ceafcc2da0bf24133b80bade09fb6651fbd68f2872a44d4fb47be30db0006670be7bc4f0f6d3b0cdb31276fe0deb50cd17bdab908d3f931cfb0870dadd0ef87fc22eb156412208e4296f221bab9bd2b9f628207ac5b5e2daa40257892d05d7a32e8b8cfd84db9a2c048b417b474f859ad2110ec0d7cc99a1431224acdf0c102423cd4cae34298f6d5bbe999359bf1641d9b184fc4c65ad363cb3ef8f410c5a696eae469985d0bfbbc0aab3ff609c8e88e6defc0c1a2f73d2b98e93a5ea08718d856f35b6b54f8eac38a3da0014adea4be6c5570d79aa1abee1d66603280f1890eee8cb87780032722203f865fc149f197f69a597409535b11986a9fb9a06116e557210f7b19b3bd48428b6b5a0bf560e27c196c5c3caa0b2017bd6278807c34e5cc292e6a45929650b358c9e4e06cdf8720eefcc1652e6e05f7d10fbdd06ab7fe0cf50b4de1cee7995ae250da3af0256ecde371d60098ed174437a2776064fe895c89b8e1cef0964745eb829dde215303abf16bd38d6152b195c89764f06a9443387b5a5a993d8a92afa46f8ad2dd6ecc9c71b0379ac7fe21e6a20163e0ee949ce04e30101f2a4776df1dc04af4a6d4665ad9469a54f2b9fcb948b696a024b6b6e0f1b60e7424b74b0b735e9232678c87274fd931ac18f7fc0064ad82a0c4a794bd5e298b43ac5491edab3b39df33135617d5762acd9a8664ccf8ad4ac09994bea862bae4298b8fcf1bc25cf5f5501d6dd6f1d9aff1a9a642dbe001e45029f0248687977fc591ed17a30e572a019c5c939a892a76cc54be41d9bbf378106673983da3124c04caf0f84a00dfa2df71d8ef63c5326cc9777bca134ab209204358791c4940772f91e31b6b2293e4df1d419b56f75145c93f4377edbe50e5d088ec88ba7a5a015c08d2f9f67b89b43e7f19fb1a6608d86e3094c48196d479d06653e53cfd9e2b415c0e2c39fb7b60ab1f72b6d682aa85981ff6bec05e36e8a0334c707b7c92e0b42aba055030938cb1e60c9c61e24a3ee2568b394da6ef1af0ac20735443c6033777ae285d8e239ec9009c0c1c18412889ea5a064c30359a5032701a66d6cdca2756438765e401205d833e397aab5b68da09ec63d3958994407116e2f18371d2493ee497c8add0e58e3c2a08389b0ff709eb6790a01c16ec40e4f93c285b621db15f45be3acdaa81eb61afa47b4b95017c303f7b1122f38630eb0c5d5694630f5533c8bd88d33c0aaa299f67e7c30ee766565208c428f51560fe55c533ea708f10a98fce6d7924e0c95ad8ab7f32975f2e36aa2bc69cd0ee200f8b64affdb7ece73942627162170aded7a030ce093d1c90f986786156bda4b0a698140b00b2c50b8aa0b7fd5a8aaa2e3aaccb227093cc68d2ac59eceb0ce4d080744629f0b40bc96ddff73b312512b3e3886462fdc2c4efcfde9e74e0eff8f05b0a3ed6de5ec750cb68bef1a99c4dffd2bc3fa90a76496c3e890ec1fb54a1d043481b7d5961827c355e4e27dd79b67f03ce7d92075a368a0716dff52a35bec0bb54f00fcf2f317a7cf26243ec4764a90277d1396a9bf175e4503bad41d34900a954bd362cf87d97282c5662ec5c2c34db7ef9a2707cee69477956ea7ac99c408772cd1f86a4546d3716b7c0019a12398c06422da7f743c51d45bbde7f500af0721513c9d844ec9b6bcd984787df569351407eb354d78eaad431f4e1082813104c049144c88327388bbe0088fb8af619213eee94c43b79c0a6cc6a330eba89a090a17a2a79a01313e701fc5d89bc5820462baf7233403117644bb167567ca930dce25f0a4dd3fd82f31bb60942cd73e9e49e17fb8e200acf57fec304b7509080f74bfea3a21626125b4b784610900713739222f7e73a2a547b6e26c991dbdfd0622f96f5e516a97a909d983ae811ad66a9a95134295d4765f83860ade5866b409098d5ceebfbf682dc288417b7aa06018f87fcf84bc9657af0289a1f4c99e240822b07d9ece9dd6134e026de189194270547847d85a323e48106aaba573230d0e6305e5674707af1ecd706d61fb3ec9d00adb297c70a7e49c1df9f36702c38b04956a41b2403a02c455691495ac183640746df4b3034182ddbf92c874fcc063011213dc8ad9a4bfd250103e98c73053de4d014ba146e8b92dfdcb0d2c7b49a5003a72a16440938e6dfb3fd9eb25859abc57b5dad2128a7d835bdfc78b131d840d968bc131d314a50a92cd05748666dce92f899bda29d8d80dd4590d792b42b00277cf912bd58034b9ee25d633da6958e3fbb312d129fccac46602aac8e7a1c202aedfb4a68eccc527813534ac26e1cc4df49281949e7dd8887cde8b4474ce3409bf03302bbd01bf8f145f5b5c5babe2066cacce207c028e773cad4888d5b9ac0823bb6f5db6d4d89a2d8f78395b6961f972a47daf5af9bbfebe75f233e64ae106dd0199b0e92ef32e9b2a8d34e932000f06045674bd3c9a5f48b85ccf894e050da36b4bb21c0391fc30a7834fba854dd1985dbb8917fed66e53d589fdd7d4e70fa333632662b109c834144757de67974f272b3aba9d1859bc2ef8c1134c63a502760c7379af1bafb7040420eabebbbd57d62386df763ed154f8083607473aac03dc69a24901d3a18d451fee65fe0f7a8164824e50405ee161280a6df87e817b054fabc2dbefed3afe6bed3115e43a7e5df306505b5ada2f1ab5b3187117ef990c check_ring_signature 1eb5a959cfc7a40b661728d352c6287e9dc310fc65e4c0a918c20139d7607d08 7184fb63c48a07960785cb450cc4515b97e7b659c762758189a4cbdfd15ecbb9 200 543feea2b11f4c1999fd15f1968c57a31d76969ec3c84c25573b3cf2bf9b5311 f7acd82c5348165412cbff4df40ddd3ed317575cf11e2b47e2ac6b5b7934008b 2442c5df338c960d7c9dc1b3bc18c6a3a532617265d74eb65bc3d2f8c6bedf3c ec2e1890adc73628519c41299c4056b5a517b614401ca8eb9d1e891f48805adf d49719b604a8c4e7733a9a8581956224e5e9e814562df70c5c25a0a7b5dca7ce 7805c4fb95bc45642f87d5abb0c407f519b5664010294cf57ac09f59ac28867c dbed4e7268bbdc7dad3f866d8ad59b98ba855ab32834e6df09ca0f9710067d4e 3eb3cb8fe7eeea2ff5ba051e32fa3a2e3b7c711b828d8a76165cdf7b2cc176f9 94df9911db6f6d46b49115349ed48e8fa4fe32fed831c9b31ebb0c85faca116c 992c65174fa348e589d5f550a95d83ca76514fecc90d0f351d92d9a6c0df320e 225712dad2f04909e6d79d01ce8fb7d33047f972375a0ef54e8a5fcf3d875f7f a9a86880f9e0078a4947421c95987187dfc52d96adf363c502f30edc6fcb7aed 504fc767e317ee865214245e1410a400133f52b83badd645c3d2f64e48568bb4 a820199ede446a1c1b7ba39c3158128193d1e6285a1c546d7b8dd9bcca63e17a 8c9badfaca415bc7848f951f197edfd7e4da91cb0fd14d177b26b82c94063d37 eb82e30bf22d75c12cb67589bdcd56f9c2ee3486a1818e54e7b0e64bc23e00ea 489cfb0a793daeaa0bd62951309ea439d363bc1ab2ae5f08aaf8d6a0db866d87 db74a07a7f44f6262e709b945c4d8a21ab57201e3bf0e0571c022b8d7c927226 f347a3bf74560dee12fb32bce67c02e2ef61dcade637fb7666567d8e8f689376 0422c442ed56f7210722910a5138b93f885928562641477ceec412f895a6e48e f725928a066f1205d0a3c7c06124b1060d782dbcd5faa053ebb4c3fa376d83be eb0ebdf89be52235d34584264893dfed22d15019d0d0e6046f8bf35afa1a0f90 1578e3bb1eebd60e77205a9cc7ddce1d8cd994d8c0a57c683444622b895c6c42 d5e7af099509b7e97265f92f9a232c2c14183886209f9e5e6a9f04369cf87136 849b1e0918fc19c02871083ac8637df704b1179f03ae7fe0fb535bff7aff3d9e d258100a0f9aa386969c161ec81f8ba8eb5c33db0a2b6318dfdaf1e212652c0c 6f4e276c39fe940712737352d9aac0d872a14bfb0e20e8adb912ea9820a772af 1ebe7ba5c7d9787b4d448ee29f0b5a7df81886be38817a75cf099c1cffc8e806 a68e5c9414aff70b56a736ee21682beef2c8a446773abd14fa65ff4d9939efc8 76a2a0ba74d5786f9fd32f0e0e4ca5ec9c2d915878f629564b967bead19d03a5 e5d25bcb19a15506ffb8ebda3168a847cb1a4c3e1c883593963d0815926601ca b189a35b6584635e2ca225b99d10f935e5e2d9aacad94deb7159cf41c8057ddc e6af85777638abd5e11c416ea03972b4a874358e2b3d64dca64ddd456535d128 7c9ec2eed774675459197aaf4e2180bf8e9bf80ec7a0442d5f5659c71fa2c897 49b298e3e80b9c0003008b62cd6c5a3cc7f4229850300fd5c48fd2e24dd81650 f95d9552653ba4d6f3bff5859d6ad269cd7291b2892001080888bc619dc4a046 3680c7fefe69716f04f4314f8b409677dfa82557e994d65693dc7d7f8ae5614c 6e506622814f5d90ec809cf2302d5cdc275f29665009656fb1f941754c649b3a 667ae2afbe462c765f0b8cf5d2e9bb5b68560aa570efecdad57784585a1c7720 2890a34891ecbd6788bb47aa0681962cabe54f06ebc27b019408d7bdcc6ba637 87bb518c7d847f9fc2aca39317dab6b1a0c75c091b9f22a7f1848364b34509d6 0c838af55d313fa455f90656f7c7d97a1ef503417a97a74f395dce0524d789d5 6ec977027721921b53909973a702f24a8d366a45f0436a0113d7cce6993f53ad 71ba0e015bb7be397b27dd1789c94c4c017f1ab681a0d3846f0353a85d057293 64190fa9576c454a48d898256f23ae30fe7a500d09c68634366b5544a5ecbdcb a32298df698677db8fc50477a0043ff3bb022d27d63fce6de0c913fcb14ac637 98a1529478e55bf403fed1a009503bcaa71391dfa83e5bd0d1c3c74783c0208d ea14c8daecea208f07aee92f4edc7e00fc5397bdaea6c5a486036c517099cd9c 7dafd59d7f155aa634a26971c4106a24c172a30cbae48bf6ea1b1d72b6dba1fa bc7967c001f400998e18fbc53da60ba187a1480b731af2e09762979f7c4362d7 6ac5b82e87191fce298281d4b3520025b0eba743aeecc42ac9d218b30a781d91 e8c482ed4b62e158ae0dcdddcb794110c4f73c5abe540ec3fb7ccf8a9dab3a74 f7d52dcf00ebb2810331d1278b4ba03b0384f47c0f89f92e8e037ddfa03054f3 f123cca20321b7e73faa51c8255ffa7bd8266b1a39991bbbf8115570c667a306 ed8980dcd37a86d7f7d1e720548fb6ed8bdf33da32d8e33f681a93f6f5459883 c85766a5858537a13822419b9af7136875a760e4dcafdfe13176755499e5fbdf a3a94fb862d0e1c1867d46b6ec614a9bd6ac5164e8de19e29b8fb38e2a6436da b1fd45e36bc57e1c8672f4076dbffcf2bf09035dc5e4c47f14a6c01eaf3caba2 ce5e1669abc0c7f060ea303f41aa1197b90facb7a2fa2204726e7c57ebde9665 cfd271968a4967ffec462606fd7b195b03f2af58de867bc5278657a33f710d06 e3d31e9be390c1164c32441ac1fcfebf65db22c2d3b7b29f4e054894d854b217 809148d336d7f80f701a80e5eacab1de50f1165d03e5e352a7113d6f1efc6a2a 971ed240b6a0298187f27d4dec358f401040830673b07839f74e5ce7a7c82ff6 29618c8aaa9af5c68825a87aa8be826876e1814e854c86b7d76933b449d2d279 26353026e1c4758ebc3c432643a7f611590ab32bd7710529f2947d47f98cb072 0cc7ca72f7c934e5334af028989c07a49229fb00e4096c070dd24b87c2e2f690 c814ab0bd6e63cc3b2d7a6695dcb30b7710f7f2a7421ab2871ea2a4b5a9d5f8e a0b3bad37c0e765c22ffb2a526d0c2b8111f16e59c7cc3be7e6a1b07e8de767e 3902bb51c23ee908ab925e56fe904b5e661fd795b4d0ae83ee111dbbca78a819 10546ef941398f600a29d7eb6036bf371b11e4ebbc953b9c5329b219edad3b68 7323fcd88ba69cd500283550e1fc13813d73c6c8927e3705eccc63866bfce833 5bf49f286f1292c986bed0f67522965665a461c38852786845ba46ed654268a1 97d85e168d5987d1fc79b0e887aa7611bc298143795e9bc346b235bed1f08f46 ab25f12c7063b2483515c5b907dcd34fa0db8e94ab1b06bca82a0a4d7a21f166 b3f3794b37a43820d44a689b2b7b1a8df269777a21957f91b4308d2cee66e45f 1117043a0689cabd1960d1ef742bad37c510731992d024a7ebe7a2d63667e7ed d5525971a5cc476083846c310e77ceb540ef037fe181acd00afa81e29b67ae14 5db681990d100c409663fbf6e24b487bd7e4e915704fd0d841508630a613f097 56d7cceae4ef1712eb95a782dcbcdff8d959861b9a5ff56d395f0b2d34f4f12a 017ae0cc1fedfdd445b4fef15938cae99abdcd0583a188e07557fe2a65842233 4860520043dbe07024a476c4091dfbb17e8398ef825f665aaa11ed63a62a4b34 99913d4fa4a13a2a3732215ce2adef2951d4c4fa31191c62cc8cfb5555e27d23 d2d029ee0272ef1ea773fedbd8c464a1858bb4238198d2d4121393e6c8326b3c 87fde213731057da37cf5701dbdde968af42a328079e52157e56a4b2b6c95cf3 7b416abb2b0081c4205a614f1de524752cb5729e4431f28086ca888d7cc87860 38244112fd3bdbd05733434fd81a649419f413c0a8c14bb52263a589f0a73af7 63f4dfb6c5fe738cdf13cd432954f590bf00b4e81893ccdd5b1696e0049c8d58 723e7e3418302ea27987ccdf280ec556ad72cde3cb2a596f3349098f27a794c4 05e2cac03c8b1b049c9bc473f0894b8ba9b34aa506831e06723b416140308133 59d4102257ee505cf877a216aa22c840b7214764532b920bf8260a7f58a5dfca c4f3578faef7a4ff279ef30a71a06f56b037e0231c3fbe5ba778c53d524b101c 73822d96d4f8c8dfb37e8eed6aad9f19e9466b0871c917303957330d900dcbc6 ab3220323c42fac9e304170080e2f77315dc5ac77d46892308fc76a721c5773e 8ebf74406a07554db8abcc8713a23b3031c99dd6d232d478ccc5ab9fa54e4a53 59681c4bd7a5cb32ed92a2b1d8cc6156199921a4f7696d03eee11d3dd64b86cc 2a5e0eae43611e232226409667f02d74412cf4ff1f4b7135458040d935462a31 0bcd81359754353596e7e67369dd00812a607c48d6e4db6141ea84a70b9e9d30 14331aeb9f5a2f2128d40fc738cca6346ce2cb2df0df94d068c5ba0623cf7c65 fd07c4ae1e3fc78b95d7ac73a73ba18f6c1df405d28e3661ba860b3030a835a4 39f3ef8784ff04275a988836c1fe13467a3b2c5c538e34cab88ab03745531cc9 7144f8f4e2ac33178eecec7ff144123605c1f1303773d049dbda8b2aa34f163f c40805c5886287f2d2d7246128b5f64eb4f3d1428d3605a06ecdfb7ef66c43ef 059c6721140ae041d768b7384689981a13f998f5e19c4bbc03df95f8d4fa866c 9e995343b7855a18d916a9913aa7598b7b108ffd35e00d2510f17e842e71f45a 7d8a69025c207109fc0529fd604b76c4523ab439b00907445e2424ffffa98f46 6c568bc59db99d2ae9f78813e483de0039137628715e481870a42215502e241b 9972c9e517db11d6ea09e48c7db0222e64da5e7df3f78cebae553d4f8a0d05f1 3238ce4c1788fe041a12cae1fece1b6e6854f9a8499b3c09a4a08365dd89ab74 1eb6ebd1d5fca4b4caa9a1e08734c15057daf9aa135cd9b5c81b6b9838b690e1 5f84f99c4afa11c9d03111be7077f386a34869930f1c620619b296f297928728 ce6d586bbba966c28d152af4644e21f519d51a074e9d51d14aadeb500b4171f8 979a86122b24dbf7787836445630666d28710f38622b2372687724884de8feab 2d31c50d507f0340f5428678b5339f43e677c4a46dd9c3981a1a96cbaa251895 528d682b3782df90300fbccc325f62f11997f660a168444d69718ccfdfa616fb a819364888e956816954687dd809ccd3a1aecc32000e7a699a6c24771dfd6cc7 6a720408f24f4a5c0bfa9865513f41fd9e55c5bac0d3e7cdac06a7ee66fb50d0 4fce289cb8c7b82f183ba968834165040eec796434551de337d05b5f44bf0e7f 027cc53385b6d116160036a9ecddb3434b2562efd45abd0823839b70508912a2 636e066a5802d9504b86f1022b5a8b93474918be38a2962de9660d358e0094e4 c509514ef85df5dd5a66f619c963b5684878d1290ec2976e1702963eb97c794e f11e96dcb3820fc332652e6e973399f6f0a990ee88ea90b1e3710161bffe6797 c5e990f41806973d30789a085ec52291431d3f66e926c9a7b0fa50bf7b234f87 281463828afa2de23dbb757568330562b49c3b290aea675e47363fa3918b45fe cca71fda3caf010902660bdcbf6dc15faf08b04b345bfb6d4414f3e019a7bcee bf57436908bee12116fc88573ef538ae22699e66fd7d10304c13f5c83c2f0a1a ddc2e241acac4159fc35e49a8937afcda1c85e92ab47e1f659e7df2053ee006e dfa9a0dbeaebfc6948f685d905180e3dbe629df2973c961e49224cf1a104c351 846ca19fbf02abad29916e23ccfa80d7a89ede3f441ee77becd2a67bef045094 4849b1f596c629fa04976f04f1d1b797da43f6ea4130aeecc07bab8b37ff047a a3d2d94b33f852cd770fb595ca4c6b1fea367dd5c0fc1553f9168b94a5220fbb 7add63f8fadbeb00c6be6be1c36605d026c141cf4c3cd38bf83bfc296d99db48 6ee0daf17d82d17d4d53bc0b54c255e746592caf7ecec281bf0a44dcb2b5fc49 3bc952eda5f4687969715739a6892d53975a0f6dbffa12f8adc2eab46ee29d6d e5fdcde88c9d6175f3ba2c3e9a499033ab1a5419a70f3b6ed2d017c56da92f36 e309c4d435010678c8750dc2b3d8d4e250ba9fed2a2d7f46668878e31282853b 391f19c85ff9bd2e38d97998a41446207b044c6c5293f50a7e47e4f11edc2cbb f1f226812c18c243bb7caa6420e56b85378842dda40b358566f6dfed0dd0d48f 1c38d642cd012bbb3fcfc6e3ae33f0d3a9ea0f5dd6f7d9f55b6b51dde70d3379 0e7ac1cabce9bbb6012f866b5457ac146ecf181df31a0adfed70cc551f33ad3a ce6d87fa0d789679f90f97bc0ba84b149907f73e90d31af191cd666b008851b9 d70c3fa70104ac187ff11e87c88c3d439c32e1bd545e9fd03f0f623b3de40742 7a3b502186e37eecabcd2f59c5245787f4c9b9eee5fa5d41a1d9685bfdfba5cf 29ab3109d98ae17b91b533884a50a6ce720ff3559b94991d9a47542a7898253b 6401b466925bd5e24859cb228f22e0c2d3d1cede7a595daec5b53a1abeef7c80 3d084b70ca1190fbf49fb87a7256fa81af5dccdda264bd0fc4e44e9652c2144e 8739ad8e09f754b81cd511d55fdc96f09ffe2d036c7389014eec318a4090e7ff 1d0b13a09908544823ef82bcc0ee0702f771677396887243e9b855ae10fa0d3f a0dabc3212ac005f328af1b67f3bc40bfd568a3bfe23ff1d17724a2fc0a9b3f7 47ab08ea0a5d1ece88d24e91608cb486d48e78b428eef7d5af5b70b804458124 532dde7062bb3ac560c7341edad20cc4b3d1e55ed5446189ac3ff1bfd6e1b375 c26be2d6baadc86a620565d30fdb3e3875c04679f5cc462fc090c2ad1e9993d6 3f24a8cec1584ddcf6a36f82e21f5d5d3d14bf5fa6693d770bed98206227b5a5 8c44348acb609a7de70b5b3967fcc350f2a6e6fbcc3bc8e5b3c0d3d85668299a b0fc060e8f9c61cf0a65d96e40fc1eff32f878e8efdf2b9aee1802900c9bb812 a7f1cc91a33b8236620adb78493b4986f6baa36a9fc0ca39de708aee8b03a9d3 d9f2debb03723b5a5fb3f1eaca7d757cc53e6a1add37fbe97a44c6c995697d43 6c8b6bc336ed423fd2b8c688355252219f100ae0b3f7dd49715d2fa54f4d2dc4 0a5a6089033274496bac02a0be66293bc08a6323a7a9197cb5ee8b881b79b91a c6c7e200df00a5c8bb5cebd4819272446669b1a55dec6b46f9dc395ee5122518 317183a7f4bfebf4b8eb9d45abaae34053d561a29d4a18fffe4ac424aadf9a1c ed2719e979b9f9120a9fb07010dc60d4801a2a7d0988a7f2cb185693ff6dae22 93bf867715511b241f220390ea0ddc19bb430ebcf0197a23bcc866174c9ea270 f811165233275b8883df148c4e0aea9d344c557c965592546fb35057b73c9941 6413323625835409b033e73cecb47c414b7313d74c0cea45b231b9fe4371095a 4b3494d57d4a869dd7ada0b5c02b52c6eef052a60160b6a61d05872914569732 b8f46e5c2b7e79fdcc92aae87d8fd60c8178dd6e771ff15094a9da652f8269f9 195b6edada34c6068401ccb49b9f9caabcd9b51d59877ea6a7987253a7c570a4 944627fc5a0a7390208233ef15712c2e4f4fb0fa4f13f356e6d5d226a4b028c0 12e4921b4627591f5d4b9c3a704271175ae915d0ae8d9e350a225ca2ddb6902e b0614048fe6f4850dc31eb26140d577f367d022ad4fc13681245152ae1775deb 55406f7f4b2325cf39a34f916591155d671605c4519e4329a0f8e468805820ff 9e9a6827fffc2b03b4dfb4a018616c99e3bfd904857fa557316c2a16190ee91f 827fe24e9360186380c8373f428be2974709770be3040afb6f165f55cde6cd98 035b5472508d2b6599df3f46484bd5e2b798b8f2407074c2b3c5deb7ef7d1bab 36b3c7034a6a0bce5fffb24e89f5b34f52ec666b6593566d2bb019f12dd1d5e3 bd3fb3283305397254bdff0a0b7eea8edb3b8eb624a59a05d379d7b9a75d9482 5e3646050305256601ffdd954d32c7aa26bd1ce5cd64bca3ab158392d6f39813 3ba181568063a0920047f366be38f8e73eee814cb48b90a01f9496ad8bc63bbd 572777dffa2d63a4078122cc97fba3e8f9eaf0da391d436aa82d93374a6fafed d3d57c5790d18da0df8b9f8fb2be00eb3ee2adac775e5117d86c0084f3ea7787 73b4509bf34adcc3e4bbf20f60eb771c173f906c4a4cf5e59a1f74c29d4054ae 822e5fd7ecb36e2280d3fb300f9e2d4221385f421231937bfd634c5873892457 0ec5b38fbccdb87a07423ee0d7e787ab152e61cd4ce85820c22706f6d899a63c a5b289d4e5a8afcbc5fe71209e9af3b0922e1a1dea7fab4029932d1e2359fd34 dca27c0e7c2a877dc3ef1e000b0bd9d74eeb9defb48fe33ea19b185eaa551fbf ab288d412f5928b81ea2d7c772cc91414280a9e4ec5c7c3c8bed0ad0aa446fb9 e6399d35535d7fb50125496965791e751a906159d92920e21c147ff920a7f33f 265c6d002d46892c66a22a3ccc496f74983690e9d7a00096274ca4be1ec7356a 0d0d5998b3c2bcd15787e24d2c7325aa7c76155632d68dbf57461665b61253b9 0c3691ae2daabbdcb784ea3c0f6f68560ab548a12ad28e2bd252910ba78ea4d8 344bee7a3fa92be3e138402eb69ced23c34a94d7a60b6bbec466b156f286a013 9c76424430af08b8f5ac377c84660321dac7c87b02f55dd69f4151142ce7aca2 197ea5230ad5b754b94de2da099a842892266157d403b498a542c93a642d38e5 42ceb7329b551a2ecf1c3377ca2490dae242e8d92359f6b5d619002bacce37b1 7d24dab492d677ce092a87be26a245482b7e89b79205dbf76c8ed491a5c66518 5465ff982b133a9e1281c0ea318f7801915ee2da70f269735a0e17c7259b641c 3e31784aca01ea6728de1110ac04eaa9099348d0bfee82f920fbc0d53e1a587b 54dc85b634b92b67b53a63b955f5560c600c0921e7a788a826f0109e4db5013b ef2117c227b0f301500939f2d255bc805f066fed8117f16431ec7865015b3502 6bdbdde97c4c53684f7733531bd5ff5a0e6a0b5bb786fa193d1b81714ca99470 6574c7ea90add532ab17e8218f992a4be754579159c77e1b1e0c025c90e6c607d79db51a387cc6d1cbbba32f914ec953317c8915da7a28ebdb88169d99f8e70c388bf3d7d4fad9c11dd2c46f3944f378ab3d2bc0e1db0393b2a611f74de32e0b7a7198a0165d43e0fffb81c2034e5a032a9214c90197d25a021054498727ca07dac763ba6c2b47a30ac3e221521ec6864226cea23a054367fd67daa2b3fad901d98a68284aae665c19f9342c8293da2a1b2efafdc071032e2eb88dcbe587a8015125eadeb0fad1a0d3f8b5e71c477c96f0d2c97a56bd819f96748514b97205041eb159a2e4c861ecbc012ee6ba2c30ac02e7759ac2d932076a2d9713ec049e041eac3039f8a2965860515e700c9cbd71d72ff8665dfaa41e676e319783d9a9094bd0b6a4e560247d344dd5b006f091da0fe91bbb672295d82cedb8750ac3610dcc704382a3b118d1846af03cf3b699c9b240d6e8f9efe1a32cb088032d15580ce8c4d5813e6008cb3742e9fcc76223b966f2a1a64792cefcaa361e5b691c05024d0ed6bb607d67fb22d853ae72d9bdefdc2804683042a9dc35af5972f015870c36cf7cdc43228d717e79deef034cad0b8a30aba230dd5497ba18f533f101890ae402d83420ee249fd9f988649e2f58ec5843f245e6dfb229893fee2fff4e260395fbe236ccf24bc01b937efdd49b1adda3a86d9a8a8c34b00c29f679ec65950a47d23b47a80d42c49fc0af826c6f0d549ffe1971017848fd8148008b060048077ade0b66788c3dcef70ca09a6731f97ccce3207a9637f1ac5befd5bd34a546087966d4a800cb5ee22a92a6749cbb941e2bdcd078857dc003e33a1a51c0986b0d527efdc308a03b3a0ed935b329750f093759df3cbb5fc680d61a1198ab61a30cf3a7a1b6778498e8e74d42e5b1f138dfdc50069ccd566a42966955fb4abda302d611addf86710e8e1d7acb0216250658cb22634cd0a6caadf3a700f042c93b0364c9ef395cd3da91ab3074771f0b781498e78db9dfb875c6b371f88fae58ed00a6907684aa6475bc07809ad920ede6c548a36dd02ade6b69bef5679ee60ec00634b403e5b14020e4b271328de7eefe5aa442381343b871282c19cf4a90c9fa0ee15a8a53c96741363d05f736f9bb56185759c9f0728134a3789265eee027cc04b2bef44210f7ccc2850661e1e54f60c097949acd0e834e79efe7fb380983b205d90dab9d57e6a0dae8b7b8e639612473d5f8712c45a6854bf685412d6e08e00304e57c661176367151002b4bf745ed242a0e0b8d8201669db82534449138ef020c760c9555438f48abc355f070c9c746054cb0391508197525268667365fa807105ac420f6dffd08d7bcd2c3e6cd4ce2be13b5235e977f23e51a02fa9ac56b0b8e6695b1568e83575b4ce420486b6ef3b4bdfab73afc75bed1cf03ba895ac302eed9dd63db96745a44efda268188e96ae9a124fa24ca4a4aa66f8faea80d820336976be54bdd1002d5f42548fe8bc7f127c4b059a6744467aafe95266814840ed2ec7fc9aea084f1b4b48d1305b15fd0bbed6d4a6cd7bf7441a8bbfcb5942c07ce43b6cddf00ca0724faf4fae8fa2f959d3d63867531526f90bb5afcb3638c042d308a1700deaf49ab0bf0344633b860afdfe47b41102925e8a0eff12bf9ec02380b16a5c8b6b7bae6dba854829bab8651e41753e0800eafdc3466a08244710ed8b2c3dbc5592244d3a88d09d8124b85b39627ba45ff1c35568b2c27ae4939050a6472203e93318550d792c43e92aca7e5eca4439d617a49c430bf224126f30818b76536524d119c81823f5907c370deeab707eecccf22dfb7d90657c5ba530493c224f6500eb65b0b02585a1ddff1ca326bad311b768fef98de94d1b8241e07cc5e901331c3e085b3803335236aac9a312d347adc82aebd41e66391ebdd670d438303bf8efa18391d1cac0ebbe0303417acefa53870e4d7d56928a79f5b18022a42bafd1cd66e0bfa621652d599f0d55747d16eac71a914ffc978df21ccce01758fea0311913ab3c8e2ba9f22f860cd7132e2c43c6975874a3c187f2eda39034d63ee0baa78894f4d24714699eac0039ac7a50e6ad215b26b9ff029c6b320028b7e45d6dce6574a2ea91ec4f1fa5c6af9fcb8dbadb0592f7f1b99b522d34207c53b55ac96003831b79a768cdbb1b976be4c955cbf65f1bfc7fe94f65af2be0e85ed5c627428ec93aa4b0742960ff34c915c9c5dee8056e5698720abfc316108b272fd538f2c25e25e16608ac6747c9d21d077fd0196e81ad002ef9d9a87380ee1297c7f0b3b875fcabcbbdf1dbc8774dc12855c1e750a08077b3df6a87b5608680ca8af2ee893ca260213ab79be2722c8c5cf409410bb170c62cb4a7421c008c890c27a318c2ae8f2b62f1c0ccb0bbdf839672776026df3a79bced8a4f0f20ec84e1d47bf32e7aca64e43c2ff8349d00af3f2d61a8ce8bead082d4cd2fa750b80ee56bf43bb16701b50f3544b77dcb0ea88d631c3009b952bd099574848a70dd98cf7ed7dbc893621be5bd97e9701ba50f0e157b862fe9f7e4b5fb4098f7302ffdf7ccb310c42b05328ace52cee383c8d1fa4c921492bf005699537c40c8307bb3cc354268589e7d226ae2db76dfa1381539e66e072cc00fb3d7ed6fa78ff0e05f566103f3789c8b338603d5bd7ba193f35b8a4f58011c8631bf8ed7717460a985c0083eeb41939d3d19f286e0e424802f4763e1ce8bb0630cd04ab5d001101523f7e5b1ac7390483afe3c5d89053d84a2f48e2f34fbae7f7b7bf441c21fa0c6079f6becad400b6009b718aa2d335e3baad5d9e5ae22282cebb702f18bd5b0cdaced8d888d5b06db79fbeca7ff596034782c19c8f269cfd9c456bbe67b9fd05b0228415623dafe4bab86d378e42e60c713a26819ae2067817eab824e9ddd2039730c10fe7ad67896ca8336273ee601b46f3f4f5315df0f10207c45b1e0a1408a4d99d26d2e57682320a085dc673b15c863239f3dc17602988add7a3eb05ee0f148917ca318a6bfd6e926bd7d72235bb8b7923d71c28cc50a05ad04471adc20181e8d718c211125307e32cfeb604e668fbd16c36fe3238ae93ea9bafe1a49b0058b7179b1264155bd3efd5a3f66b357831662ba9fd703f52ad87af43ce03f9051442c8965674bf02fe138d3c7a18b39a6d174ecdb858547131e974702985e20f26b95a51bfbe529a0b3ab1f95a0099a284c8a8c60a2b710f18fb8f3d1d689b042e9f4a1fa9d3728971178fed2b71130a1d8299061307e1431723b68278339c01cee39b08b9d1b543d7a8c69ef725208578ff893475b967b862c04d63e8400c040b4b2ac25fc44464e312d86c004f68c1ff83fae64c694b3699cf8be0041a380ef42edf932377186d2499d4b07e37fd61321164a9b89c7d4d7f9a453f736ff60b5b9274fb356fa7eace6c21da9028228d5f6e0f61cf91fe2de19a3235fe28ff05fc077223df51d769a1acc61bca6a36e06c6968d85f0240858b7e54a8ef42aa0bb0b547783eb805256bf2a8b6e0bacc0e8881eb20abb630488d1168bc76ba460b0c20d82cd57326b419c066b0d316f8f8d88b73a864d3db84384e4950299e6e076b3e5c48410274e82e39d4b7eed1a00f8bc8501b5ffdd93169ac6e5fae9a860196c7b843de2f086e627bd40a775ce42d76b108faf1a066b010263c10c173430148e4f97394be97bcdf5c5029cc8d70bc39629812e7766ff73c37652c10af360677a9b8ee5550788d46a02150d965a78c1b9095da18283d732635715f78c9690df6879f44ee2a8145b71a8306d1bc27acebc8cdade42013c072777683d8b7d00617663009d1178aba6d544c2dbf741b4f6f2dca51b171d906e5ff09920c21090fa43bdab26e65c4a8e8aa6d053f14008a6d50709bedac1a28c33157877620410917d5760480e0d2007c9e4871b947349cbcae81bf5bf1f66829304fa5013af90e48b698fe10d1b9f4974ec20c78eb86d15129a70f879b54ff91cf4bf424f9b2040985daf3493898006790036ad7cc033c73490826faa507a2407f3ef9b994ed0cb13ee39397da2ece753b5e82cc79b9199154e0361c0a026ebfa66efc7f8c5909f35bf4296dd75a5c37c194521d2a268f95ded22535c51d04c6da379afaab9604bb17be7d4e5ab4e81ce51a19f397c6230c0c9d6404ca9472e4842bce00bccc0af4cc8a6a20bab6a551db552c0592838660db7dc4e9e85093f3c0cd90a6ad6109b65f99958e002873b7a163685738d9edddcde3f9a412b31510730ad6673d46014fc3a3f5927482e9d3cc1fee75a782465a09837dfff95baed79bea641dc31f09629c50efb1705cdf2919451b2434579136c6d456f72d0d40f5c9e7035a9ba20aa09ab5c6634539480e7d92fc630406bffc031298b93ecae9f1b89b72a6979a09318bf4c00f30533ee6737dfa86d998fef55a2f83a7bbd67d3371086ec94d150a4222f7f26f895a8a9ee8aeaee9a5267e825d0081f1e5c9883a720cd13a40680cf8015c0f317f6ea10d0b08c558706697b6feec3f48990ba5ff837116b1cefa04d3994fe4739f42769801532a616e7a50412feefd93c459b2acec97499c07ce0f8b47b89191e729f27e30ceafe883d1f436a181e1699a0ce9cc18777b601fe008e0ff102a75a6ec2c5003a2583fee0c5e56987d3f4c4f8bb5b5177729b9bad002351016c31db3ebe62b151664fe5fc2e5fa4d4c3116e8ef4f77108025986240079aedd3f12f49c0658216671ce28424e644168eebdbf2408e6ed94f640a7b7d0a48e981e9030fc1de0409026cb59a19a82854f0eefa2d280d4b60a46e358d680f331ae69c5191053d94dd92c6f851aa9e04428e615adaea0c00b2e6d375d79b0e43b8bfdab96deb1c354a702f8e0383dacd3e941fa921dd035e1dbbb91283440dc060ca229a49601e45c933dc1ca82a26ed2f6ebb7f4069ebfaa142ebe26cc30dbd10ed88d8704da012a7bff2beaa177bdbbe65aecc14317c5fd4f30b215d630a05893c062aa7592ef0a1924ea74fb108c65789067d1349a0399df6c1dd802a01c4392e48f72f1c10b4ef35c1bb66f2f3369f1168f23d425a8babde13e9e86400f2c31b9fa07aad174139153e07c23a43820fb1f1b621e9820bc9bc3d4bd5be09dbc0870c73715ba44158cb1589fafcfaa8bbe23f1e0216c658054965adb6e60ab92521e01f558d577067134015beada31dd6898f24419508d59bb8c09190080a9cfeec60827d6025bdd30be33eef8cadf55bd5499d93bc697fa4a0cb3faa260528d3be04734a3a772877a42ca56838a5d9b3f848a311daeeb373d958f37e63078a30c18cbb0430a5c1655b8202bf613b72a62472d8798b85babcc20b5ddde0067e78c74b85fc7224617b354771e5eaab6f4fd4c94d189d2bac244d4e7eec6a04522c6cc1a4e5cbeab8b0ac70f843c43ffd73e35be4a7ef0d971612e89a760605e2b4cd237933e4692db1642747d38541fc77d8ffaaad2bca53c7ad7fcdd39e075e39208d9a431863a3ebaa112d567bec95fc14dd96b553c71907a7f086a7210fb3968719ac7d86f9e1c6625399ce0388fed307df64caf5c4600aaa5956165b0702e3cc1ab0532b39e43fbe1961a2de0968d947244dd7ee990f247ea0d3730c006f670649bbd4d5cf38be5799375a922443dd81fbff8ee615d55deb58d56c00062688a9a67a6520c49bfe2a441351a57918c6ac9b8dc6ab3687a8563e6106630e32f07186c57e2d9160ac42afdd875a4ef45fce978551e7cab19f328cf99ebc09d69e1b70349d9ea9c22aded1bcdea723cafbb8e3f143ceeb1204ffcebc6e6b092009cc4720a3be9ac89387c4e73b4057089d021d7cfdc07a15ed1b8f71dc160356332520174dd9990ab8d34161292dde08e6f3c77961108c20d09dfe3b52c60875bc437be1db6822e0102bad0d908f7ebfb172c724ff5cd1559e4bde8e22d505f251a919a44618480fc6dbac82c3fdb340fa042e010d9dc6dc59344bc379a909881face2d2a544eecf1b7f1f0c6f267d6003b93a41e3ba21e85525b031b28f0572295bd7ce62fe348524e2136a542ce60ee7e81a666822b3dabb6cfac498b70dede6fc207764d734a64d0f7b68c9c5722651f917990cb447915332b9cc9c71076065f580cedc3010a4b2e4bdc0593718edc01bd6aa1396698f634051166dcd02cdec8627e61f211ef1d377a00a7f846fe999fd26134cf3f566d9aae5211fdc072e768ef12202809056ff599abbafd4c5b7a25a592fc0c16a6a50e4e737bbd3036400564f08050e1aca147b33ce3c30de48bea1881b471b311a7776bce851d500f9248afb156f3871644de712d40d901334ae2d3c043a740a5c21717cd6afbc08c712f544f2884f4d82af848a15761ccc259380589063526a93b2fedc4543ef0f89993a7f5adcae5b6e1202047b482c9c61646924c70c8464575158963eb3cc0956ec2ed8878b2c838de760dc3863fffdc9b2058087ff4838fcbbf50ac49e4405f16cc0659638797432ff56f010d8dbbc3dab0149f424f01a29a95a2a6fcf6c04864c45496b0fb865512cee810296a5525a085aa05f4e241f9d0e132e0f3692093dfbb5cb614a0c4fc4cbf009ea0b99438b4f4f3c1f617bbe58a4c6b578b9f9049656e5548f58e2613d75cc01715c14bf4e7dd28c48df6a7ffc726bcca360d50d68314ad6f5120cc3f5ec2cb578e4993627908513fa061f9b6faf3f018c596b08d5586693b2e5c5754582875714975e9aff44613c0f462d56feefd246eb703408c3ce415c5f244cbf1b1d12e7c6c585a01bdbfcac1c386c1b17829dffe13aa80fd3e357f44195de100e9d3f695b349ddd6477e25cfbc7e6aa3dded90f2137eb0e9b839c049353ee34152e70313212e007e688c15c72029f09b6a4b41d46295209f0c2432a0e762b3dd266565ad4d6d15b27e50ec831882ec85f64d6524e7ec10586a00d662160071535e89168dc248cf4ae7cc31f18e21487b6a03c90e738520f2a577a52d5b89af200a22ac083879437bb510ba626320a912a6425e087171c0df98ed7436a027f9482370440340a67b22d519676e5b181ff644e909a14c30904afc293a9f5374c67a93160c0cb9ff5ba0476d7d915b47e4d453edca6357a340d0050cdd26b45d8d81c453a079c8db3d3b6e5287269031ea01d4523276d142f005d1c0f78fc5c44bd546bf0ce0a8ffc514f0e7a942733e32b514b49bfd60a770aa7658a6627b81aefa120f574f51fbef466d85fbbcfb060f75e9ed8419a55700cdd948e9a791fd92eeda363137dfb4a46290b7827e0bea07aa5b93b431375b804261273bdd2a843fb62c110fccf9395aea73f6cdc6c4ec74411df35b4fa7289024adf4a719dae9a539569a4db6599d787e02cbded7b61205e0f4c17c23bb85e0f24394ab01eb41f1660c00662754e35c3c4f64bb9cf225ceb448c135051243809fd469571f4f155a880094e6dab6f79cfffc375c2ee2edbac1b4081ebf833ad0de996708d7b0c8d312c1781081cad3791def4bc6d61c1e9ca554e2483ed907804b457e1301a912279625ac841ada755c1b8652f65f8a229c8c695402d6414e5046159ba45de88a3fb0399e5bab9d46fbc97e283de4a9cb18d353a7d1df57a3b0a37c45d5985c930ae9a61d9e3c3308574b8db6ae781385543bbf212545c9b0a08af7a613378033f86caf2a598f4a8185e5a724fa6d6a1c49e3672179698e85f01d4f31b0041b96c59c1a7fe185c43f6c7f79ea943e402d0013f49dda188108b0ca140335892e9e67ae887a97700ee7ea2384540cdac804f9eba866fa4e7344a09c771167e0810c7d240ca11dc83c30f65354160b2bf67eb2d585fe5088f60a90b523bda2922281dbf1073a91746624d50ca04ba5fb753714fc724cb9f08201508214f86d04290578ef0ffa9bcf386a74de796f7adfca6a91389a15a9db0fdc40ba54d9d546a5da435d9aed5ba17d3aa0527210dbeadaa9771b7537a1880eb24013176c17d0f347b0603f4ceaa58d31538e4a2570d79efe90f3de72b520fd96601c549452372bbde8c8ed5754fb4b1b4fee09bb9270f7526de9cccb204abd8d10369375db73e85860bf4daee9ea65d58307c01ec44dd71ef2c90decbcf22c5430aae2c215755da71a8c36356730f08a312ea949144b42dcc494ffb37f426d749045910c212189f8be0cf63b842ef8379f440cfd4e13eb1a34426859a85469d4c00fe826d071e54c81afb90f13326057be51ebfd7fa2f72273a77a551fc580f210fc01feecfec5c226062b5ab043984852381bf5642cf3f0568132c87b358ee3a02cb28a3b2fa0b0d90cdcb22380888943e3d8744c0c8f1104d4831035937af9f0b2c010065e23090e4f96f6d662ccf4b2d23e6a3994e41da69f0b6d7403dc44f007d4dc1b3ea89d5142263391da3935a801a3b666f74bf7733335cd55b7f98c7007aa493d5364e0e330e93c195f6eb79fda9aee1840f6fdefa2ad9409910617b0842442dce288f46d04ad4e72929c1374d0025cbb971e59ec20e5b06abde36550ba333026c323c840f877596e0965924dfcd184ed799d2882c4bdc23a880776000274e28a5c8599ea62e91f1caab43e35d69676f6e513d5df265ad4d6a53980701d46e1b49bbf875602580ef3ffc38311d0d6942154fba52a1cfd888aea545700fc55d791f9da456e26a0665b5e9fa37d165fce814d1bbe7c3bd4b17ca08bc460f9bcc8f479ab8776379f43b4e182015cdb269a193434dac91f5353de7c90e7e0d49f2075b6c4c39a9e268be24338b5c6a45d3dbc178b80834e788e32079d1810d015804bca3566f28f4362da9df42986e15ca892ff1a0bb9c03cd3e6021e74c03f88548103c7a4afb6cb4df706f26ad412c5e35eb6b83e7e479cd37aab750b409eb04f68b746b722fc18b3b971c40553d9af49f61360a8fb0ca36751a726c6f0a7bc20208e9d2bc31d130d5ad2cf8213d140ba0ea095215fcadb33d2f2c209a0702600305ab97405225caff0c7ddd93bf04b3c36c37bfb0309b3c050b8e471100cb4d52cd59f022e0276b28c2d61a0952121d24f54d8e5fabbd00295f51fbf30d03523784a0dc69fb7e5af7e763eb58081c4ea583745bbc552ef56e0be040b80ffd384198b84d28f81ba72cc18abd2c7ec1931200c08062c1c2806242b5d8f2012c1ac370ca74498e79a4765cee7a29de977fa6012301d6da16f4f499d6f0b509923d4b8b09aea483ff261635696a1e3ce7948ea8865377bf78dd27f01f7cbd0e215f5f837a1ab10ae26d50b3764bfbb29e5ce93b9658ce7c569eccf13a16050c6e811df9731add88dc5b5bda5fd17d6dcc334838eb9e3945eeff8e36826192052528f032a00ba355f13bfac34cee4b84146940bef6c23c63963488ae620b42070430b50a7b03eac908f19259c428b08d10d8b4f5e67144949c1be6549fa282092c9153a399e6ec749df58466da26c95707b48d2801046c5c7aa1c7af31e85e0f56f41cfdb983b6080b612bf408a4176abc4dae9385654c7a7975ef5440b3a107268495c6d79d99a1334e9029a5e316838ff5055f4323d29abae6e42ba0f484095f5559c5317904b04e6d0ffe1f7805dc75b987476582354f642f79e124017b0f9d28f8f6fe0a610779a9ae25a53eb5cdcbbbc1085eedc3313847a41b15e1a9077ba6045bde9ab6a9fb576d985ac0dbdf54d2bd4549954c63bcd1f6d07c0c7f0add9fb398a7a5b72f57e4e831e4aed867db3953adf7518208e7fb0ba60ff9cd06b7df3eda50db9e0f4d8ca467ae6381cefa34ac0cba0bdd554dee41fb4ac69207430ac7a574894281088aef716227b2493065a062b937546bb2ffb2c43e52aa0ef05cbae45bf64b454ca6ab57c7ed808d479356477c903fe8c8b3d099037254099cf6776315a05682d86ebe596a345e40ae3d22d275d8074dae2633dd8460ef0f86e91f62ca6e98952f6f00e09d2d1a6036d01db043a5033bcd846081b625970e64fb9b188fc289ee61c2b4696ff3b4119e9f188dd595e9060e735ecf77d4a30d345820d1991debcb1823a9f0eb3c9a7bb257493fb6cd7262a13eb2fd747dc50f6fc6bc5a336ad0f5040f67890ffd9a7e0362825a0aa80f8eaea6352678873c00c379d4f2f3f805ff643d7426fbe1e19fac34254e75513690d343c57bfdfd7b015de9e262b25ce8e3c47136c17d90fe1cf959645f834fc2fa65c7727f0dbd4e0616d01eb262d97e5776e7dd3e4a7e783ef3263c2a9e59e73d41dd160c36adbb01836dde27aca1d62741f183599b85f6f7ade0557b554f03b14f71a8e5979e200275c2a81dc80b64f3f53e7769cfcbdfd4c9ad1b8dfee5f174f3fa4d7ee25a900ca8364125ba7141093077305afa3842b41c6ee0fef32b91c9b655f855e9dcc501c5e77e0e1b38c8a9b71dc8a03f389253d9a97193a3a5472257ec6305aa1e700aca4a7e23237d77f0b919edf0e380d8a3e9d7c03042abfed4fc4595443df6470f5db6410ef1a46bb45319f00171314d73c5565c3c85dc66d0eed7a52c3ec7d00d9c4eaab76640c384e8cc65fa21278b89c7da0cbee25f9667baf8be1a5fabdf037364ca7d4f347d890415bdfc151fb430c40d8a4aa228ef3f020ab5e384739f0c294fa36e411ac08f72fce3b3c2e9a5cb2f5a8c5fdb6d5898f4e87d85eac5370b129e1aa7039ba99ef81ea34b7b9549d5d2546ed70dc64f19a5fb26f7eaf8840e5ea513bbf9aca8462d0db78119baba8ad995f1978ffec53dd4edc93f5b18330214e4cd6b34d91f9201b9f457df457422c8b444bcd45d52107f32093797e0c4007d26cfbc521458e6601356a45554d456c66f0f399c75a7dc84a49d2f113ed1032e1bf7b7a30394fca199ab7c16ab7e3e78890d32c66f665ad89c4f033fe66a01456e8958e347a28dfd8995d1c5bf2568d01893a21b4e71dd85a8ba6f5f924d01a084a5ed00a25e2366d0e8c4a8aa934fcc73e2ee7d1228c716c47b67660c3b00168123c4e63ad3234e8e281274d8bc7e8a0be2c525d86a9639d21d852910210d4455d0e001b2da13869131c38972076403bb3574fe270ecfd47b60d385bc2009441bea4710edd0ca90b671f89513db5b2d46b2900ce42e95c9d8367e50d2e6035342e3e702b714862a212948a82de93f56e5bd2c3fe0b4bb9e853e7af3fac605e4ea52a275eef49866183f533eb8b3403eaa675cf262d46b88690bafd18d930038bcf4fad829e014929f4934481eacf943eb077374553ec5b5624be3a0ebbb06ec222e6833d4d6dca6eaf90ee766b0cda5fd040f5c77f9c4d594bd53f8bcaf0b0e01475dfadb034c0d9b9721b77f38e9513037eebc09d158616e80089d169105737daeaa26366b89ea7b9ec31407cc23ae312d2001ea5937377e49661d8aa2027c78c2f236db0d42460d273e4edf5a7b45c3a6f60313f655ae458c90973d5e01bac1c3d47d8db627f7f1f85f1bda702969165df95d2f66d6758147c0552c3600324e841e30ca18bace5e0c01313402a12603e95064eaeccd6fa870490c690e01355f79a532fec13cdd09187169aafe4c623102cf4cc39e747ab60db88252710d0881de3dc70026dbee1dc3c785332c15418ef65af21c6fb17e26f9c9a875bc09795db2488677347d7ba2ff7c0c738a95171a0b5f62308a249b8bec203cb44409a4faa09a0ab7262d864d4b15d967901c147c989a7640d3f6d2cbbb24de6c7f0cdca451ae4732c326be5beaaba2a5ffd471f1f024c5c09548a58f13f3818dcb0678b8139f96c7320947784beec04ad9b24e72146b58f70d56d71d5233e344d80f57cf44049eb3801625d09e538b1957dec8a233ae63ab30c138204cdaba0fd90513955109554d538266f0a1c3d24bf9bac6762c5aa51823af8054356a17cd0b04550211e34f39114c051c784222e00dc2141d7add8480f80f5698c68690311c0f0017465b91da26079b1f17aa6372a8bdf8d67932f3baad1f64b6fcb0a57f990e443f0cb35cb85073312baab8312066e3ee6d14644b42f9b8d06fb32e850b920d54920ec420a9fddc519c43e8d1704b1bd72e1f407022c09abc2103f43327250b5ca0cd69cbdb8536a1402e846cc0deb8ae23432d4c9f9c75a474ae3819b56405ab236d541be5582294894a3c691234fdf5289cc721c608801094a2b4181ec6026060a68da102f1f60e1120a0e0f74eb95cac771d87b5c23b1ab20a621cfa93033d738973ce692f6fca0d6f3b891c790574dc8add1efb6c5726afbb2b756feb0d8b3b87d1b7a452dfd18f0d648b210085f43be2b5673db4ec1ed077fb7a818e04a748cd211de5f551c10343f00e19ad53ec95b8267d1169319b4b82104f40ec00c6b5b51f08411600c56ba3c9968394d22535071f3d9964b6d9687f572cfb9b094b39adfff37cb229494564df2bff906b735db59d247278f5cdc0b06aede2ac0f677b34d8f2f63ee9a5b4ed7968d02b62396a1b6211e0ddb0cbc43fec41954b0beb1d5bce7c9e5806bda84130e5b97e2bccbc6ed68861ab42cbbd3e2e3af5e3061b44ff05520dd1baf283ec9695fb847d8db8ccbf536c6fc9eddc145f28e61d020a74d6bfb726e7c6f6e4c3160cc6f144d7d05fc6e99d20d92a369eb8f207b20efc4ad28230ca2711faf4ccc75937d9553c46987c2f9d0656e439d206165cef0b5555a13003dc93921e28555a66b2e0bb74e7ba8330ad10957581b9e57049b0038b2aea15eed52d716068c93bcc5241dc0c360e6d8c3f6818d36738939789750e455a10bdd1b2853a61d295db2d61b61103b56b3dbb7a9410feb4c565280f60008c410fc9d261e25af33902ceeb694c0e902ec5970159a57cdafec01e4c46270486b938592ce986333121315fe7ab5bbd6cbe5427e59328679bb7c01f3f9b620bddb31cb73b49fd8ea3b66e683c1bc96fc440b7973616bf21cb5f3d6aeeb1540143cffcf2ac64abd10d2141e7f3e89f16f0d2a5668ebdcee8f19d23d4d9b1800be112f4af82c92ff48e7b65b86aa07afaa0986c77b39602188084957bd25d5c0711418b73b54e1a7a3183816a979cb88dff90317f015d3e2c6e27cb08baed8e03135a480d87ccc31d2a9627c1baa2e53dd8f38c1782a119cedf877576b1162f066b75bfd854709a57b9f8b12466cb16d72635e71bfe38802614d74d4583ed72003273a48143211fb3be169415775efacaf0f9b3aa65fb866fd423a1014fc38b0f50dbc61fd603519c61dc4efd6d367e4bd8e5b05abf4fc594167aad6bb82a510d66017b8b1f5cc625f0b37aa75b6b413e5239ebc70235481addf60bc01957880d70edb5076e84a69b6ef16b06c473b51e98fa013657835a977fca84f737289f069635c0f029bf697abcdb0d5354608868edea778a5d33adf255651ac158ce7303457c2aae48d6d75df7552058e76870254e8c14c8abe3bf96160fdede8175ad00f77d9523b10b338934f79d3baee5c5814cdef1b764f296e9745bb591504de50a3af63516059aefb2af8d64f0211406007ebf6a88c4819768d777d629356c7e0b6c9c8ec227b3a418fb9dd6da12253bd9bed14880f35f76e3564be69de44ef501ed9e28af287005436778dff8893f1a2c1007fccaa05461e6ec7b4ac53dcdf30e2b43bfc4019f66fa27f9ca3eb1b715a383f5fb7102473ae44b459c0c563cc306780d3374f9c52e476dfa372602c4a8e88aaa71280679cd3a2d6e2b380071640e81d32b8dc5d3134ad8bb5f22898318c40e485fb758ac2b49fd647d306d305200ebab9f03f96fc52d4f2425806c4b47c3f44b36ef5fc564b0191e3274ccc93b0bca96d799dad028ba32ce93f0a38e08816830b8a10ad029daace6d825a450390b0dd302b6f4b58f261189bb11cdeae8e269778ad120e3d1f1e4adfda1386bb00cb8efb97b4737b05fb9e6b83725cabf5d6fdce10613db96055ebd23467066d80ba53e15ba5b821857808318820b959b943b24ce5eccd642fef8749a1fe0c88d012e9a0044a05223c0566094d2e7557ee273380b12c78610bbf840b313223c1a08961c2b3056e58ec2901629dccfe070b01dba0edb0d0492c14705dd37b242e207e4941ea4d4f7b99641d1c1d7c0dd825f4ddddc4ae90d5adb684985ca63490f04d83a7de60069a69674e34e9190b21dc2edf29d301aac1420e52006e19cdc4b04a2084335d9b4ede83a0ec2b508cddd131078a0eb345113e75216b793293aa9006be87039c822621e37906147efbf3ac4668f6d5cd7f8523b7cd387f036e73006ad5c935ba8d75696941a11506b86f3cb4713cda59e20660bf1f0008c65dac804d99e60db0369bb9530f9179d0f1e340b99cb50d7a23374fe00bff47d7a94840bb54d81bb6501307f8ff9d1cceec3a7e79eeb849d90b6448b3a04bf5790aab9090d63db0350ae9dba81b6e69a359d314d97cfe4f4fa5f7dbeaa3c22b5b28cf50507f3731146c60e3ee0a2bd894e2180f30b82e6b7006d1e9867dc531bd5370a0d79da1a1fb334e39e37a9ad33d5d9de3c501c96a3c6f6e3659665b8c664c08107703e25913bc36054d0bd4d2831ee876315c45bb58a47feb849ec50fa359d71024520e326b8a97729bb6083659e9a6c934bcb3d256c72f508f0457b1afd14b405d1d9618d80f008b65ee4477490b60902e33928a42bbebdc8ab7fb8e7c7b3f70c8a9c9dbbef93eeacf0304fba3e1f7e86ff73d12ce4961f264dbfc28d69be2e006aa96993296cb3edd72b84a66baab093660a38dec8fbc578021853228e4fdc0e0c7792c82bd52f57bdddc2ce177af2a325c2f225884b7fd02ba80e38bfc485037f2e1307c012442668b1ff7c73af07fec7be794a9460c03085adfa04ead6b60847f7ec625322dadf787ea430d04d88f591c66d2310b3f8cd7aefc4e983dfa301b26b88ff3459f2dafca3237be06c09dd61a9557c4aa316360d2ea0b7a3fead0107b41709c93841f164f649912f1276cd055f9e20031666ffa0a5efdffd5f2800830248963e7e2fe330db56aab8944d352cea075ddf16d8cc3fdc4a4bdd49390435ae91ddd2d5e9e7c338748a68cec114175992563b809877e658ad8a5dc61004d71f84ae6819dd0cfd62c6b5e8f8c72db877faab2b24dc84f269d0fad372ef0f795806dff9f95790d1ea4c0b5efb1ae79df105af676b77774aa7dcde40940d014dfa7f9e8545a34fabec7df6088e44657d9e28ca5f67fe966cb299f3ae9fbc0ddcdd263ae5910210378c2a4e312e58c7ac4cc4c747ec01d3c474fc998651d107e798498bcfc2cf97b647beb5f133ca878bebeab876a6d8d65ec28374254d5b04a086d230dfc448b9d635cfa59f4ffcaab74c324c88bd1fa0b96b1cf14787a60266a4b3c15ea0828f0bd6794cce450b368b01cb1da066b40f66b6a5e9529eec05b06b3e218a611dcd2a0338fdb7db2ffaace2fa31ac5f40bba888aac1203121026ce685a6888de03d5452ee603e8113beefc30dc14cbf25958a81a8a4a840240cdfe1ec9042309ed91235799be2f902003ce3b4976c18b8ed73b18678794dea0f4dc0d8b0ff2938a4d29ec756e1306fb018c24b47ac8ae1da1a9fd547769cad08d57a5451fb6b275e718db32c44427d9b0a7fcd0a50dd5807cc817b9f9f1f0d0e4ad827bffc12d8a4eb60dceb98ff5a01550485ac4ebb60b0a7ca661e1dbbd30871bb1df8ee106a6e3893e11ec46d9d188ff9f0359922c70ea794a665e7f2a50f2128d3e63d9e5173ac7e9d83638c53e9de0d0ddb50daad2cc303cdd6489ddf0d7fd8e1a8d59a8e5d70a909dfd24d2c9d742de5df0c1f390011f65906a189370d0e9f6405a716ea316006a2526a78cba0bfed5de0b30ce20e4264c8a4b94e7705388a445a24b84c8bb89da7c59bc5ee0a0edebd87be980dc1f0871b1a7e069b01cbd8ebc3923b1a4a15f487ce47415f5276ae2dd3417609c2325f334330308d0a95e529dcdd6bca69a5e6286668e078c2a6e624c870dd7de23afdcacbe6074408db145c0e6b20a878e713a5476b2aa009ade4efb006983567694e55f2f695e3034d372dcbd7ed79e7a0d78274786d2505e59d7383ba0879d0f1934fa87241ac03a66b57accc64db643108ac2093ab18ee76234e262c73585b40f148db7115c707de5cb98d48b7769a4ef62262bf72a953446a3a79c2fbbc3f26a72070cdb1190ec6cce473a90450c794b91434e6e78f9b47cf54810300672753ede88e98c8dd0a3d9ce938da4cab3a3228cfc9e9a8d253906e7ab45ab1cad794da355b197cc00b0aebc5a1bf6defd40683f1caec954eb81e889bdbbdfe724fbd3beeb5bf372301611791f51dfd3cdf2b93e2d8a72ac63a459961b017778827e49e5c0e26b62806641629d374055b4c9ca9b4786ebcebf3012f792ad4181d4747e59ae6fcd027014f66cd168735947c1572c3deb712efcabca9955eedc877f3e580f4373e4a6f06c62a28262ef8d0c88d4cbcc2dbcb1bc8a867001ee63274371475ccef161db00c9c4e19636f611ff203be5bdf0d2366a4ffb659acfa67940bba39b016fd92ac01e5da21a1ca7dfd457db0a2ad2838aae11107ff4b6d32e5de364f300cf1014409f6aae39fc36ea8cf70d34ed1e11f3433f52796f42336c80d32b195b273e0e200ed6217e30e2585580600d3ad67869361346010cd23dceec4d38c3eb2b759200963cb4452dc9fed21f6f60d209a0176358f049583c85e9354e55cce488d9cdb0ce45abc53b6a9333df888e2d3e7c63ededd44f57257c6e502f22c30e3a29dc40fcff1bebdc3e672746dc325136fec244649d5b144ab0b90220d842ff9bf8d940bd2025cf7b0ab9c0f4f0575eb2085b3cdb7a037fa9cbf171090ca58d3cb5e520b7d519ff9d24de471d2a1a880957b5c89e3d581148a28fcf403e125b1bcd0390f22b974b9938a6e987c096aaffda697d2f50b487097a05cd447110f1c24474f0ab007d30ecf352f078741584f2e53c2bc1eb01204ee416b76120858c8c33d7b04d16858a989432e7d5af36b2ecdc07535f4f895fa078d929fc971cf39627edd0f9c1b76a0ff81b0e872eada750d0a9e32a6b0f8ab8d71e4e913b122cf9f9c090686dcb3a2b162ada6f92a1a5e4dca935e734c0b7ea62deaf190f64cf80cee5b0726acfb18ae407ea619e78c5195a46ca8059efc586d618780982a87ab5d0c88034c59f947f8f92e69145fe788838e574ab159edd184e0706e804e050d1f3ec7087a9e4f4d0ab12c0066f2829ebdd87503ddca3090b15f37ec3ba9c0c5036579063427f51743df6bdaac5468dafc87e02983ad981600198e67797649ac56598d08099f6299dfb68bf9891c966eba1a9ec6e0ed33c0f012a4e49de28ce45b92b80a987ba942328772dfd0267b7a10401c53a8204773136c6008cff98ab79c75990ebbb7bd00709bde6fcaf8e45d509a8305f1fb1f447997194ab3933b1cb88ee00baa5e31cf5f7838fb62e2191a94e35f5618be7d42d02f645f071408ed13fa780f44de54d83862bec967cf3c1f00f6a69afa2851965b2570a7fc6557ab331ca00bfe3794286f279e92d2844ace81c8bbceb89ac2b1ddb6d824dfa17274193e0a038cd6b514fa84a19594f772ff72f6c4a488df37801d2eb9d29f9e13bfdf0260053a6f937f07413fd607adec071ca80f8625569f3953fa6f4c05eb092f910aa40378aa0ae264c4b0f471a83a64898f77e1719ddde3d930210013d9403d2ed8a60fddfaa4eb6841e4f75622a5576b6f16a962344e325cd8f5ddfeb879169fb1180fc9bc9f81a4b9dd06848107497c6d4b478148a3f8f915d1c014a9b7d09d92c30619cdf4c62407a3bd1aa4d68e4984b3b54fb8b70575977440b70d2705341293038cb06cd66c7a01c074cd22786a032303d7040ac15a1bbe2940409fdbdeede3042558ee40c033aed008fd7c6d6b6ddf36569699e2f5fd0921921105aaef3e3e0b578bb2c8a141fa52c6b395ff1e0ec247e78760317089e491abb282a7dae0b30e396761d9be46c452b5f3506e069f1cf5e633855e845ab5e706af6d00cdade40c7588b30d8086039cfbaf7a7e341bba0c0f39c24ab5b7ef8c7b6c3134d0f5c201591f285672d937a284b71abac38ef299866c4976798b82958e6bca727377db03 true check_ring_signature c70652ca5f06255dc529bc0924491754f5fad28552f4c9cd7e396f1582cecdca 89d2e649616ccdf1680e0a3f316dcbd59f0c7f20eba96e86500aa68f123f9ecd 1 9cc7f48f7a41d634397102d46b71dd46e6accd6465b903cb83e1c2cd0c41744e 3e292a748b8814564f4f393b6c4bd2eaaface741b37fd7ac39c06ab41f1b700db548462601351a1226e8247fea67df6f49ea8f7d952a66b9ec9456a99ce7b90b true check_ring_signature 3da5300a7aca651dc3a85016824b0620a19973eae4af8910cc177faee499358d 8d39d8d877d74cccbd5fad872b8297eec3f4b3f5187486f8c98a2ff27f994800 16 7af6983daaecec1bff70b05c7369fe1636270f8dd606d39eba974b8c1d5f6091 78c2676d12505b7d5a63ce29f736124b48a02dc78bd0ccd1e9901344811bbd3d 957251408e9a8b255adcabc52bf15cb8a05501e2892d7cdda22ce672adc0cd12 73e433a1668d056d9b651aead47658476cf23c39cfeb4fd23625ed94af439677 f58dd3a46e0a07000ef6e978cff87604c32cd3df487acd220b53b5ccc46d0bd5 3480f2437e01073133ee9cad9651665277b09bc0a46618f975a746500f9f34bf 0ed796ebd217fcae58272ed7c4c0b058f558961f95a68ea52f59e4e6f0374d73 729e5938a034b2f50b583d4ada5541a12de09aa1776653a821da7d6c6b057716 1a9855ca5dc19cf11f49a87b182695451e83952f2df4ee9009bf0f72d6e25194 c609386fb270c7af8dc4ee102fd33ed3c836e7ea493dc79655c9feb1cfb9c869 94ce9f9d3fbe4848aeb59231d749c6c9f7bfaaf99c31e4317c2b5b1b335f20f9 9331dabe4d6f230a7c45417dbbb28b0808b4b6bdcba83b774583d784413ddb4b 437652a047fe0872b264c094440625cb5cca5cb8d10cda950138c97dd09e5943 5c470fa7fc2684336917a626e2dd4a09d68b4ef9499857c11e8ef0aff77a9262 df3ccf869b0262bd33952ace2b1320f809e227949ce6a2e89a245ce5da75e250 4ec480b685a07d091057e73954c8bd5e4646b2bd0bb0a46a38f74cc44e0c940b 4e7ee4fb45708e2bd97b2ee4e134224714c2fe1c0b679b7de838d715e281f10e4da8a6455bf4683a91e6e56be119fde36ccd61f35aa4867e9725dda7c18bb40afa097778cdcec51fe2d205db00ab9a00dd5f6f0a2a3b8393a7ae2bc03d6ff5073b25f72be8166bdf2e21a4745841bbff68a54d6fa3c77577a6b5c0f0b7ce0a120d07d704e9a30f306a63d5b137534ac4b60953abeb420d526f4b501008bd0a01790d806d86cf96391953e8b630833a3d6a01f023da720e26015287dee5c9b9060f43fa3574626a3673628b6b82c4b0251b5251173b59177fdc8f79974205090da7a3d93d1d274ed39654c348870da21ac66ee7a0e072e504aa01ece4051c3f0c93eb6065ae0e4d05e49144b986b33ee741c406860d39c953062531f2e993140c43aeeef6b144cc42bd13c2aafb10402edddebdfe0b426730163fd530f7b2c3009d4b98d761131c5ccbdcb737b4a1e9fc72c1313fb45eef13ce4d31c40ec94c0466cfcba1771229b0d2e014d716c9a2c10f6191eb67c1bb05e72f291d1d98290c2b4542d196df41410992428e36c62965a2d68f1fb99d6d4058b7e0360dae2c0c56d7980a546f547d2944c3bb6672fdb7d40378d47b0bcd9ca9ce55abce22ee0d94a7906ff1e31d37578a919dd92b4615ee0930fb33cb494548e13b731b33320193bc5b88e5864fe2d5d9a1b2bfb343177ff10d647337205f22a37b5cb385090bd2a050d055cfb9fbe01cc88ffa3fad76eb5b7baa50cf5f379ac6cf2636da8308c945c859cae02e772e432b177238a6e3d75b5cc6e676d1f3989d78493d86440cbb5552586755101dc4dd43bf51cab188fcc919012f7d9b19db5189fad02362059c8b3cacc8f80413e9a43f41cbe154ad55acac98287edef98240b6d8a04fcd0a020c8481164041bd7165a6b90b8560593c8848d29995634392dd22f3e993bd05580e41349091cb125eb2048c53dd230808019482f4b22c83ca678d9b01931b03f84ff3b4a78c138fbe36d013c9a423e0233962be5f278b535b20f05c829624043641b8386f7cd01a77c4077a5a6da0eec912042c60d9b6bcb56cac9713dc7002ab69ee15e0013204f68d6093b158c21f3a09e205b15e627b3fa33d9a0cd0300d9287b5d75ab7dd914ed95c8ed3db0bf05f68792be1492c41854a2e36c123d30427ff0fdf706d5a01f6cf6b1c02e2140f7b165b2259904528f12b527a7de76a0d8f90c3c027bb2f71cb1b345fafee0628b3f15049cb577b780890b9b96c53160b9ec99cea512304c94325b2c894b6dbc926c9b2b8f497a765a180f70425174802033d83980e2b28cfaf767d6a4da871d6adee474fb093e89df7eab67255b1b70821e64bee166892d1e5ea9ff2dae7b65231427e01c63164b5f3f84626511d2f077de2741fb75e3d940646480b595338c6043cdda23f0fb48d875498675e686eaf false From 539debc477a756907a82a45fa3d414e4b1a3b300 Mon Sep 17 00:00:00 2001 From: sanecito Date: Sun, 7 Oct 2018 14:27:41 -0700 Subject: [PATCH 0537/1404] Finish all contexts but simple_wallet, translate 23% of simple_wallet --- translations/monero_ja.ts | 600 +++++++++++++++++++------------------- 1 file changed, 303 insertions(+), 297 deletions(-) diff --git a/translations/monero_ja.ts b/translations/monero_ja.ts index 939f9188fcb..17815d982be 100644 --- a/translations/monero_ja.ts +++ b/translations/monero_ja.ts @@ -34,12 +34,12 @@ Failed to write transaction(s) to file - ファイルに取引を書き出せなかった + 取引をファイルに書き込めませんでした daemon is busy. Please try again later. - デーモン忙しいです。後でもう一度試してください。 + デーモンは忙しいです。後でもう一度試してください。 @@ -72,53 +72,53 @@ This is a watch only wallet - + これは閲覧専用ウォレットです Failed to sign transaction - + 取引を署名できませんでした Claimed change does not go to a paid address - + 請求したお釣りはもうお金に送ったアドレス送りません Claimed change is larger than payment to the change address - + 請求したお釣りはお釣りのアドレスに送ったペイメントより大きいです Change goes to more than one address - + お釣りは複数のアドレスに送ります sending %s to %s - + %s を %s に送ってます with no destinations - + 目的地なし %s change to %s - + %s のお釣り %s に no change - + お釣りありません Loaded %lu transactions, for %s, fee %s, %s, %s, with min ring size %lu. %s - + 取引は %lu ロードした、 %s に、%s のの手数料、 %s 、 %s 、最小リングサイズ %lu 。%s @@ -126,219 +126,219 @@ payment id has invalid format, expected 16 or 64 character hex string: - + ペイメントIDのフォーマットは不正です。16文字または64文字の16進数の文字列が必要で: Failed to add short payment id: - + 短いペイメントIDの追加に失敗しました: daemon is busy. Please try again later. - + デーモンは忙しいです。後でもう一度試してください。 no connection to daemon. Please make sure daemon is running. - + デーモンの接続が確立ありません。デーモンが実行中になっていることを確認してください。 RPC error: - + RPCエラー: not enough outputs for specified ring size - + 指定したリングサイズのアウトプットが不十分です found outputs to use - + 使うためにアウトプットを見つかれました Please sweep unmixable outputs. - + ミックス不能なアウトプットをスイープしてください。 failed to get random outputs to mix - + ランダムなアウトプットをミックスすることに失敗しました not enough money to transfer, available only %s, sent amount %s - + 振替でMoneroを受け取ることできません。利用可能な金額: %s, 取引の金額: %s failed to parse address - + アドレスの解析に失敗しました failed to parse secret spend key - + 秘密なスペンドキーの解析に失敗しました No view key supplied, cancelled - + ビューキーをもらいませんでしたのでキャンセルしました failed to parse secret view key - + 秘密なビューキーの解析に失敗しました failed to verify secret spend key - + 秘密なスペンドキーの検証に失敗しました spend key does not match address - + スペンドキーがアドレスと一致しませんでした failed to verify secret view key - + 秘密なビューキーの検証に失敗しました view key does not match address - + ビューキーがアドレスと一致しませんでした failed to generate new wallet: - + 新しいウォレットの生成に失敗しました: Failed to send import wallet request - + インポートウォレットリクエストの送信に失敗しました Failed to load unsigned transactions - + 未署名の取引を読み込めませんでした Failed to load transaction from file - + ファイルからの取引のロードに失敗しました Wallet is view only - + 閲覧専用ウォレットです failed to save file - + ファイルを保存できませんでした Key images can only be imported with a trusted daemon - + 信頼できるデーモンしかでキーイメージをインポートしません Failed to import key images: - + キーイメージをインポートできませんでした: Failed to get subaddress label: - + サブアドレスラベルを取得できませんでした: Failed to set subaddress label: - + サブアドレスラベルをセットできませんでした: failed to get random outputs to mix: %s - + ランダムなアウトプットをミックスすることに失敗しました: %s not enough money to transfer, overall balance only %s, sent amount %s - + 振替でMoneroを受け取ることできません。利用可能な金額: %s, 取引の金額: %s not enough money to transfer, available only %s, transaction amount %s = %s + %s (fee) - + 取引は無理です。利用可能な金額 %s、 取引の金額 %s = %s + %s (手数料) output amount - + アウトプットの金額 transaction was not constructed - + 取引を作りませんでした transaction %s was rejected by daemon with status: - + 取引 %s がデーモンによって拒否しました。ステータス: one of destinations is zero - + 宛先の1つはゼロです failed to find a suitable way to split transactions - + 取引を分割する適切な方法を見つけることができませんでした unknown transfer error: - + 不明な転送エラー: internal error: - + 内部エラー: unexpected error: - + 予期せぬエラー: unknown error - + 不明なエラー @@ -348,18 +348,18 @@ Failed to parse txid - + txidの解析に失敗しました no tx keys found for this txid - + このtxidのためにtxキーを見つかれませんでした Failed to parse tx key - + txキーの解析に失敗しました @@ -367,17 +367,17 @@ Failed to parse address - + アドレスの解析に失敗しました Address must not be a subaddress - + アドレスはサブアドレスであってはならないです Rescan spent can only be used with a trusted daemon - + 信頼できるデーモンしかで再スキャンしません @@ -385,17 +385,17 @@ Failed to parse address - アドレスを解析できませんでした + アドレスの解析に失敗しました Failed to parse key - キーを解析できませんでした + キーの解析に失敗しました failed to verify key - キーを検証できませんでした + キーの検証に失敗しました @@ -421,48 +421,48 @@ Specify IP to bind RPC server - + RPCサーバに接続するIPを指定してください Specify username[:password] required for RPC server - + RPCサーバを使うためにユーザー名[:パスワード]を指定してください Confirm rpc-bind-ip value is NOT a loopback (local) IP - + rpc-bind-ipの価はループバック(ローカル)IPじゃないことを確認してください Specify a comma separated list of origins to allow cross origin resource sharing - + クロスオリジンリソース共同をできるためにコンマ区切りリストを指定してください Invalid IP address given for -- - + このRPCサーバーのIPはだめです -- permits inbound unencrypted external connections. Consider SSH tunnel or SSL proxy instead. Override with -- - + は暗号化されていない外部接続をできますがSSHトンネルやSSLプロキシの方がいいです。これでオーバーライド -- Username specified with -- - + このRPCサーバのユーザー名につて -- cannot be empty - + 入力する必要があります requires RPC server password -- - + のRPCサーバのパスワードありません -- @@ -480,7 +480,7 @@ invalid password - + 不正なパスワード @@ -524,18 +524,18 @@ 0 or 1 - + 0や1 0, 1, 2, 3, or 4 - + 0、1、2、3、や 4 unsigned integer - + 符号無しの整数 @@ -595,7 +595,7 @@ failed to generate new wallet: - + 新しいウォレットの生成に失敗しました: @@ -622,7 +622,7 @@ failed to load wallet: - + ウォレットをロードできませんでした: @@ -670,7 +670,7 @@ transaction - + 取引 @@ -751,7 +751,8 @@ Transaction - + +取引 @@ -790,7 +791,7 @@ Transaction . - + @@ -850,7 +851,7 @@ This transaction will unlock on block %llu, in approximately %s days (assuming 2 failed to parse key image - + キーイメージの解析に失敗しました @@ -900,7 +901,7 @@ This transaction will unlock on block %llu, in approximately %s days (assuming 2 sending %s to %s - + %s を %s に送ってます @@ -910,12 +911,12 @@ This transaction will unlock on block %llu, in approximately %s days (assuming 2 with no destinations - + 目的地なし Loaded %lu transactions, for %s, fee %s, %s, %s, with min ring size %lu, %s. %sIs this okay? (Y/Yes/N/No): - + 取引は %lu ロードした、 %s に、%s のの手数料、 %s 、 %s 、最小リングサイズ %lu 、%s。これは大丈夫ですか? はい (Y) いいえ (N): @@ -930,12 +931,12 @@ This transaction will unlock on block %llu, in approximately %s days (assuming 2 Failed to sign transaction - + 取引を署名できませんでした Failed to sign transaction: - + 取引を署名できませんでした: @@ -945,13 +946,13 @@ This transaction will unlock on block %llu, in approximately %s days (assuming 2 Failed to load transaction from file - + ファイルからの取引のロードに失敗しました RPC error: - + RPCエラー: @@ -984,7 +985,7 @@ This transaction will unlock on block %llu, in approximately %s days (assuming 2 invalid unit - + 不正なユニット @@ -995,7 +996,7 @@ This transaction will unlock on block %llu, in approximately %s days (assuming 2 invalid value - + 不正な金額 @@ -1005,7 +1006,7 @@ This transaction will unlock on block %llu, in approximately %s days (assuming 2 (Y/Yes/N/No): - + (はい (Y) いいえ (N)): @@ -1027,12 +1028,12 @@ This transaction will unlock on block %llu, in approximately %s days (assuming 2 Is this okay? (Y/Yes/N/No): - + これは大丈夫ですか? (はい (Y) いいえ (N)): Daemon is local, assuming trusted - + デーモンはローカルです。信頼できるデーモン予期してます @@ -1047,14 +1048,14 @@ This transaction will unlock on block %llu, in approximately %s days (assuming 2 internal error: - + 内部エラー: unexpected error: - + 予期せぬエラー: @@ -1067,7 +1068,7 @@ This transaction will unlock on block %llu, in approximately %s days (assuming 2 unknown error - + 不明なエラー @@ -1082,14 +1083,14 @@ This transaction will unlock on block %llu, in approximately %s days (assuming 2 unlocked balance: - + ロック解除された残高: amount - + 金額 @@ -1099,17 +1100,17 @@ This transaction will unlock on block %llu, in approximately %s days (assuming 2 Unknown command: - + 未知のコマンド: Command usage: - + コマンドの使用: Command description: - + コマンドの記述: @@ -1237,14 +1238,14 @@ This transaction will unlock on block %llu, in approximately %s days (assuming 2 multisig address: - + マルチサインアドレス: This wallet is not multisig - + これはマルチシッグウォレットではありません @@ -1320,7 +1321,7 @@ This transaction will unlock on block %llu, in approximately %s days (assuming 2 This is not a multisig wallet - + これはマルチシッグウォレットではありません @@ -1335,7 +1336,7 @@ This transaction will unlock on block %llu, in approximately %s days (assuming 2 Multisig error: - + マルチサインエラー: @@ -2085,12 +2086,12 @@ Otherwise, you prove the reserve of the smallest possible amount above <amoun Error: expected M/N, but got: - + エラー: N/Mを欲しかったでもこれを貰いました: Error: expected N > 1 and N <= M, but got: - + エラー: N > 1 と N <= M のこと欲しかったでもこれを貰いました: @@ -2105,12 +2106,12 @@ Otherwise, you prove the reserve of the smallest possible amount above <amoun failed to parse secret view key - + 秘密なビューキーの解析に失敗しました failed to verify secret view key - + 秘密なビューキーの検証に失敗しました @@ -2157,7 +2158,7 @@ your wallet again (your wallet keys are NOT at risk in any case). failed to generate new mutlisig wallet - + 新しいマルチシッグウォレットの生成に失敗しました @@ -2199,13 +2200,13 @@ your wallet again (your wallet keys are NOT at risk in any case). txid - + txid idx - + idx @@ -2220,12 +2221,12 @@ your wallet again (your wallet keys are NOT at risk in any case). ] - + ] Tag: - + タグ: @@ -2240,30 +2241,30 @@ your wallet again (your wallet keys are NOT at risk in any case). Address - + アドレス Balance - + 残高 Unlocked balance - + ロック解除された残高 Outputs - + アウトプット Label - + ラベル @@ -2294,7 +2295,7 @@ your wallet again (your wallet keys are NOT at risk in any case). tx id - + tx id @@ -2325,12 +2326,12 @@ your wallet again (your wallet keys are NOT at risk in any case). payment - + ペイメント transaction - + 取引 @@ -2395,14 +2396,16 @@ Originating block heights: | - + +| | - + | + @@ -2454,7 +2457,7 @@ Warning: Some input keys being spent are from , txid - + 、txid @@ -2482,7 +2485,7 @@ Warning: Some input keys being spent are from This is a watch only wallet - + これは閲覧専用ウォレットです @@ -2565,12 +2568,12 @@ Warning: Some input keys being spent are from Generating new wallet... - + 新しいウォレットを生じてます... Electrum-style word list failed verification - + Electrumな単語表の検証に失敗しました @@ -2584,7 +2587,7 @@ Warning: Some input keys being spent are from No data supplied, cancelled - + データをもらいませんでしたのでキャンセルしました @@ -2600,26 +2603,26 @@ Warning: Some input keys being spent are from failed to parse address - + アドレスの解析に失敗しました failed to parse view key secret key - + 秘密なビューキーの解析に失敗しました failed to verify view key secret key - + 秘密なビューキーの検証に失敗しました view key does not match standard address - + ビューキーが一般的なアドレスと一致しませんでした @@ -2635,19 +2638,19 @@ Warning: Some input keys being spent are from failed to parse spend key secret key - + 秘密なスペンドキーの解析に失敗しました failed to verify spend key secret key - + 秘密なスペンドキーの検証に失敗しました spend key does not match standard address - + スペンドキーが一般的なアドレスと一致しませんでした @@ -2673,7 +2676,7 @@ Warning: Some input keys being spent are from View key: - + ビューキー: @@ -2701,13 +2704,13 @@ Warning: Some input keys being spent are from daemon is busy. Please try again later. - + デーモンは忙しいです。後でもう一度試してください。 no connection to daemon. Please make sure daemon is running. - + デーモンの接続が確立ありません。デーモンが実行中になっていることを確認してください。 @@ -2717,7 +2720,7 @@ Warning: Some input keys being spent are from Balance: - + 残高: @@ -2727,28 +2730,28 @@ Warning: Some input keys being spent are from key image - + キーイメージ unlocked - + ringct - + ringct T - + T F - + F @@ -2758,12 +2761,12 @@ Warning: Some input keys being spent are from RingCT - + RingCT - - + - @@ -2778,7 +2781,7 @@ Warning: Some input keys being spent are from the same transaction - + 同じ取引 @@ -2841,24 +2844,24 @@ Warning: Some input keys being spent are from Index: - + インデックス: Address: - + アドレス: Payment ID: - + ペイメントID: Description: - + 記述: @@ -2886,7 +2889,7 @@ Warning: Some input keys being spent are from failed to read file - + ファイルの読み込みに失敗しました @@ -2933,7 +2936,7 @@ Warning: Some input keys being spent are from Address must not be a subaddress - + アドレスはサブアドレスであってはならないです @@ -2963,7 +2966,7 @@ Warning: Some input keys being spent are from (no daemon) - + (デーモンありません) @@ -2983,7 +2986,7 @@ Warning: Some input keys being spent are from failed to parse index: - + インデックスの解析に失敗しました: @@ -3013,7 +3016,7 @@ Grand total: , unlocked balance: - + 、ロック解除された残高: @@ -3028,12 +3031,12 @@ Grand total: Accounts with tag: - + タグを持ってるアカウント: Tag's description: - + タグの記述: @@ -3043,22 +3046,22 @@ Grand total: %c%8u %6s %21s %21s %21s - + %c%8u %6s %21s %21s %21s ---------------------------------------------------------------------------------- - + ---------------------------------------------------------------------------------- %15s %21s %21s - + %15s %21s %21s Primary address - + プライマリアドレス @@ -3099,7 +3102,7 @@ Grand total: Subaddress: - + サブアドレス: @@ -3109,52 +3112,52 @@ Grand total: no description found - + 記述を見つかれませんでした description found: - + 記述を見つかれました: Filename: - + ファイル名: Watch only - + 閲覧専用 %u/%u multisig%s - + %u/%u マルチサイン%s Normal - + ノーマル Type: - + タイプ: Testnet: - + テストネット: Yes - + はい No - + いいえ @@ -3191,7 +3194,7 @@ Grand total: failed to save file - + ファイルを保存できませんでした @@ -3249,12 +3252,12 @@ Grand total: %s change to %s - + %s のお釣り %s に no change - + お釣りありません @@ -3279,12 +3282,12 @@ Grand total: failed to parse txid - + txidの解析に失敗しました Tx key: - + txキー: @@ -3319,26 +3322,26 @@ Grand total: failed to parse tx key - + txキーの解析に失敗しました error: - + エラー: received - + 貰いました in txid - + txidに @@ -3388,7 +3391,7 @@ Grand total: failed - + 失敗 @@ -3404,17 +3407,18 @@ Grand total: Amount: - + +金額: , number of keys: - + 、キーの数: - + @@ -3468,45 +3472,47 @@ Outputs per *: | - + | + - + + +--> block height - + +--> ブロック高 + ^ - + ^ ^ - + ^ + - + wallet - + ウォレット Random payment ID: - + ランダムなペイメントID: @@ -3519,83 +3525,83 @@ Outputs per *: Base filename (-1, -2, etc suffixes will be appended as needed) - + ベースファイル名(必要があれば-1、-2、とかのサフィックスを追加します) Give threshold and participants at once as M/N - + M/Nってのフォーマットで同じ時に閾値と参加者をください How many participants will share parts of the multisig wallet - + マルチサインウォレットを分ける人はいくついますか How many signers are required to sign a valid transaction - + 有効な取引を署名するために必要な人いくついますか Create testnet multisig wallets - + テストネットのマルチサインウォレットを作る Generating %u %u/%u multisig wallets - + %u %u/%u マルチサインウォレットを生じてます Error verifying multisig extra info - + マルチサインの追加情報を検証中にエラーありました Error finalizing multisig - + マルチサイン締結中にエラーありました Generated multisig wallets for address - + アドレスのためにマルチサインウォレットを生じなかった Error creating multisig wallets: - + マルチサインウォレットを樹立中にエラーありました: This program generates a set of multisig wallets - use this simpler scheme only if all the participants trust each other - + このプログラムはマルチサインウォレットのセットを生じます - みんながみんなを信用する場合にだけこの単純なスキームを使ってください Error: expected N/M, but got: - + エラー: N/Mを欲しかったでもこれを貰いました: Error: either --scheme or both of --threshold and --participants may be given - + エラー: --scheme あるいは--threshold と --participants を上げられる Error: expected N > 1 and N <= M, but got N==%u and M==%d - + エラー: N > 1 と N <= M のこと欲しかったでも N==%u と M==%d を貰いました Error: --filename-base is required - + エラー: --filename-baseを使う必要だがあります Error: unsupported scheme: only N/N and N-1/N are supported - + エラー: 不正なスキーム: N/N や N-1/N`のことをできます @@ -3603,98 +3609,98 @@ Outputs per *: Generate new wallet and save it to <arg> - + 新しいウォレットを生じろ <arg> をこっちにセーブしてください Generate incoming-only wallet from view key - + ビューキーで閲覧専用ウォレットを生じろください Generate deterministic wallet from spend key - + スペンドキーで決定論的なウォレットを生じろください Generate wallet from private keys - + 秘密なキーでウォレットを生じろください Generate a master wallet from multisig wallet keys - + マルチシガーウォレットキーでマスターウォレットを生じろください Language for mnemonic - + ニーモニックのための言語 Specify Electrum seed for wallet recovery/creation - + ウォレットの回収や作成のためにElectrumなニーモニックシードを指定してください Recover wallet using Electrum-style mnemonic seed - + Electrumなニーモニックシードでウォレットを復元してください Recover multisig wallet using Electrum-style mnemonic seed - + Electrumなニーモニックシードでマルチシガーウォレットを復元してください Generate non-deterministic view and spend keys - + 非決定論的のビューとスペンドキーを生じろください Enable commands which rely on a trusted daemon - + 必要な信用できるデーモンのコマンドをエネーブルしてください Allow communicating with a daemon that uses a different RPC version - + 別のRPCバージョンを使用してるデーモンとの通信を許可してください Restore from specific blockchain height - + 特定ブロックチェイン高で復元してください The newly created transaction will not be relayed to the monero network - + 新しい取引をネットワークに中継しません daemon is busy. Please try again later. - + デーモンは忙しいです。後でもう一度試してください。 possibly lost connection to daemon - + デモンの接続が切れましたかもしりません Error: - + エラー: This is the command line monero wallet. It needs to connect to a monero daemon to work correctly. - + これはMoneroのコマンドラインウォレットです。別のMoneroデモンと接続する必要があります。 Failed to initialize wallet - + ウォレットを初期化できませんでした @@ -3702,151 +3708,151 @@ daemon to work correctly. Use daemon instance at <host>:<port> - + <host>:<port>でデーモンインスタンスを使ってください Use daemon instance at host <arg> instead of localhost - + localhostの代わりにホスト <arg>でデーモンインスタンスを使ってください Wallet password file - + ウォレットのパスワードファイル Use daemon instance at port <arg> instead of 18081 - + 18081の代わりにポート <arg>でデーモンインスタンスを使ってください For testnet. Daemon must also be launched with --testnet flag - + テストネットのためにデーモンは --testnet のフラグで開始する必要があります Restricts to view-only commands - + 閲覧専用コマンドに限ります can't specify daemon host or port more than once - + デーモンのホストやポートを複数回指定することはできません can't specify more than one of --password and --password-file - + --password と --passwordfile を1つしか指定しません the password file specified could not be read - + 指定されたパスワードファイルを読み取れません Failed to load file - + ファイルのロードに失敗しました Wallet password (escape/quote as needed) - + ウォレットのパスワード(随時にエスケープ文字を使ってください) Specify username[:password] for daemon RPC client - + デーモンのRPCクライアントを使うためにユーザー名[:パスワード]を指定してください no password specified; use --prompt-for-password to prompt for a password - + パスワードを指定しませんでした。パスワードプロンプトを見るために--prompt-for-password を使ってください Failed to parse JSON - + JSONを解析に失敗しました Version %u too new, we can only grok up to %u - + バージョン %u 新しすぎるです。%u までグロークできます failed to parse view key secret key - + 秘密なビューキーの解析に失敗しました failed to verify view key secret key - + 秘密なビューキーの検証に失敗しました failed to parse spend key secret key - + 秘密なスペンドキーの解析に失敗しました failed to verify spend key secret key - + 秘密なスペンドキーの検証に失敗しました Electrum-style word list failed verification - + Electrumな単語表の検証に失敗しました At least one of Electrum-style word list and private view key and private spend key must be specified - + Electrumな単語表と秘密なビューキーと秘密なスペンドキーの少なくとも1つを指定する必要があります Both Electrum-style word list and private key(s) specified - + Electrumな単語表と秘密なキーを指定しました invalid address - + 不正なアドレス view key does not match standard address - + ビューキーが一般的なアドレスと一致しませんでした spend key does not match standard address - + スペンドキーが一般的なアドレスと一致しませんでした Cannot generate deprecated wallets from JSON - + JSONで非推奨のウォレットを生成することはできません failed to parse address: - + アドレスの解析に失敗しました: Address must be specified in order to create watch-only wallet - + 閲覧専用ウォレットを作るためにアドレスを指定する必要があります failed to generate new wallet: - + 新しいウォレットの生成に失敗しました: @@ -3858,17 +3864,17 @@ daemon to work correctly. Primary account - + プライマリア カウント No funds received in this tx. - + この取引から資金貰いませんでした。 failed to read file - + ファイルの読み込みに失敗しました @@ -3876,125 +3882,125 @@ daemon to work correctly. Daemon is local, assuming trusted - + デーモンはローカルです。信頼できるデーモン予期してます Failed to create directory - + ディレクトリの作成に失敗しました Failed to create directory %s: %s - + %s %s ディレクトリの作成に失敗しました Cannot specify -- - + これを指定しません: -- and -- - + と -- Failed to create file - + ファイルの作成に失敗しました . Check permissions or remove file - + 。 パーミッションを確認するか、ファイルを削除してください Error writing to file - + ファイルへの書き込みエラー RPC username/password is stored in file - + RPCユーザー名/パスワードはファイルに保存しました Tag %s is unregistered. - + タグ %s を登録してません。 Transaction not possible. Available only %s, transaction amount %s = %s + %s (fee) - + 取引は無理です。利用可能な金額 %s、 取引の金額 %s = %s + %s (手数料) This is the RPC monero wallet. It needs to connect to a monero daemon to work correctly. - + これはMoneroのコマンドラインウォレットです。別のMoneroデモンと接続する必要があります。 Can't specify more than one of --wallet-file and --generate-from-json - + --wallet-file と --generate-from-json を1つしか指定しません Must specify --wallet-file or --generate-from-json or --wallet-dir - + --wallet-file や --generate-from-json や --wallet-dir を指定する必要があります Loading wallet... - + ウォレットをロードしてます... Saving wallet... - + ウォレットを保存してます... Successfully saved - + 保存しました Successfully loaded - + ロードしました Wallet initialization failed: - + ウォレットを初期化できませんでした: Failed to initialize wallet RPC server - + ウォレットのRPCサーバを初期化できませんでした Starting wallet RPC server - + ウォレットのRPCサーバを開始してます Failed to run wallet: - + ウォレットを起動することできませんでした: Stopped wallet RPC server - + ウォレットのRPCサーバを停止しました Failed to save wallet: - + ウォレットを保存することできませんでした: @@ -4004,63 +4010,63 @@ daemon to work correctly. Wallet options - + ウォレットのオプション Generate wallet from JSON format file - + JSONフォーマットファイルでウォレットを生じてください Use wallet <arg> - + ウォレットの <arg> を使てください Max number of threads to use for a parallel job - + 並列ジョブのために最大スレッドの数 Specify log file - + ログファイルを指定してください Config file - + 設定ファイル General options - + ジェネラルオプション This is the command line monero wallet. It needs to connect to a monero daemon to work correctly. - + これはMoneroのコマンドラインウォレットです。別のMoneroデモンと接続する必要があります。 Can't find config file - + 設定ファイルを見つかりませんでした Logging to: - + こっちにログをしてます: Logging to %s - + %s にログをしてます Usage: - + 使用: From 85e58cb24aed02b3fa6cf26850e334dd571be04b Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 7 Oct 2018 22:20:27 +0000 Subject: [PATCH 0538/1404] blockchain_blackball: fix stats double counting --- .../blockchain_blackball.cpp | 33 ++++++++++--------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/src/blockchain_utilities/blockchain_blackball.cpp b/src/blockchain_utilities/blockchain_blackball.cpp index 5eb2acc79c1..00baad7f08e 100644 --- a/src/blockchain_utilities/blockchain_blackball.cpp +++ b/src/blockchain_utilities/blockchain_blackball.cpp @@ -536,12 +536,15 @@ static uint64_t get_num_spent_outputs() return count; } -static void add_spent_output(MDB_cursor *cur, const output_data &od) +static bool add_spent_output(MDB_cursor *cur, const output_data &od) { MDB_val k = {sizeof(od.amount), (void*)&od.amount}; MDB_val v = {sizeof(od.offset), (void*)&od.offset}; - int dbr = mdb_cursor_put(cur, &k, &v, 0); - CHECK_AND_ASSERT_THROW_MES(!dbr || dbr == MDB_KEYEXIST, "Failed to add spent output: " + std::string(mdb_strerror(dbr))); + int dbr = mdb_cursor_put(cur, &k, &v, MDB_NODUPDATA); + if (dbr == MDB_KEYEXIST) + return false; + CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to add spent output: " + std::string(mdb_strerror(dbr))); + return true; } static bool is_output_spent(MDB_cursor *cur, const output_data &od) @@ -1153,8 +1156,8 @@ int main(int argc, char* argv[]) if (!is_output_spent(cur, output_data(output.first, output.second))) { blackballs.push_back(output); - add_spent_output(cur, output_data(output.first, output.second)); - inc_stat(txn, output.first ? "pre-rct-extra" : "rct-ring-extra"); + if (add_spent_output(cur, output_data(output.first, output.second))) + inc_stat(txn, output.first ? "pre-rct-extra" : "rct-ring-extra"); } } if (!blackballs.empty()) @@ -1216,8 +1219,8 @@ int main(int argc, char* argv[]) std::cout << "\r" << start_idx << "/" << n_txes << " \r" << std::flush; } blackballs.push_back(output); - add_spent_output(cur, output_data(txin.amount, absolute[0])); - inc_stat(txn, txin.amount ? "pre-rct-ring-size-1" : "rct-ring-size-1"); + if (add_spent_output(cur, output_data(txin.amount, absolute[0]))) + inc_stat(txn, txin.amount ? "pre-rct-ring-size-1" : "rct-ring-size-1"); } else if (n == 0 && instances == new_ring.size()) { @@ -1230,8 +1233,8 @@ int main(int argc, char* argv[]) std::cout << "\r" << start_idx << "/" << n_txes << " \r" << std::flush; } blackballs.push_back(output); - add_spent_output(cur, output_data(txin.amount, absolute[o])); - inc_stat(txn, txin.amount ? "pre-rct-duplicate-rings" : "rct-duplicate-rings"); + if (!add_spent_output(cur, output_data(txin.amount, absolute[o]))) + inc_stat(txn, txin.amount ? "pre-rct-duplicate-rings" : "rct-duplicate-rings"); } } else if (n == 0 && opt_check_subsets && get_ring_subset_instances(txn, txin.amount, new_ring) >= new_ring.size()) @@ -1245,8 +1248,8 @@ int main(int argc, char* argv[]) std::cout << "\r" << start_idx << "/" << n_txes << " \r" << std::flush; } blackballs.push_back(output); - add_spent_output(cur, output_data(txin.amount, absolute[o])); - inc_stat(txn, txin.amount ? "pre-rct-subset-rings" : "rct-subset-rings"); + if (add_spent_output(cur, output_data(txin.amount, absolute[o]))) + inc_stat(txn, txin.amount ? "pre-rct-subset-rings" : "rct-subset-rings"); } } else if (n > 0 && get_relative_ring(txn, txin.k_image, relative_ring)) @@ -1281,8 +1284,8 @@ int main(int argc, char* argv[]) std::cout << "\r" << start_idx << "/" << n_txes << " \r" << std::flush; } blackballs.push_back(output); - add_spent_output(cur, output_data(txin.amount, common[0])); - inc_stat(txn, txin.amount ? "pre-rct-key-image-attack" : "rct-key-image-attack"); + if (add_spent_output(cur, output_data(txin.amount, common[0]))) + inc_stat(txn, txin.amount ? "pre-rct-key-image-attack" : "rct-key-image-attack"); } else { @@ -1393,9 +1396,9 @@ int main(int argc, char* argv[]) absolute.size() << "-ring where all other outputs are known to be spent"); } blackballs.push_back(output); - add_spent_output(cur, output_data(od.amount, last_unknown)); + if (add_spent_output(cur, output_data(od.amount, last_unknown))) + inc_stat(txn, od.amount ? "pre-rct-chain-reaction" : "rct-chain-reaction"); work_spent.push_back(output_data(od.amount, last_unknown)); - inc_stat(txn, od.amount ? "pre-rct-chain-reaction" : "rct-chain-reaction"); } } From 6844ae1b8d6b9fcbe4fda5ac5cb0e5a4242d850a Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 16 Sep 2018 18:30:39 +0000 Subject: [PATCH 0539/1404] tx_pool: avoid parsing a whole tx if only the prefix is needed --- .../cryptonote_format_utils.cpp | 10 ++++ .../cryptonote_format_utils.h | 1 + src/cryptonote_core/tx_pool.cpp | 51 +++++++++---------- src/cryptonote_core/tx_pool.h | 13 ++--- src/serialization/serialization.h | 23 ++++++--- 5 files changed, 60 insertions(+), 38 deletions(-) diff --git a/src/cryptonote_basic/cryptonote_format_utils.cpp b/src/cryptonote_basic/cryptonote_format_utils.cpp index 5fcfa33f6d2..9e9c126054e 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.cpp +++ b/src/cryptonote_basic/cryptonote_format_utils.cpp @@ -199,6 +199,16 @@ namespace cryptonote return true; } //--------------------------------------------------------------- + bool parse_and_validate_tx_prefix_from_blob(const blobdata& tx_blob, transaction_prefix& tx) + { + std::stringstream ss; + ss << tx_blob; + binary_archive ba(ss); + bool r = ::serialization::serialize_noeof(ba, tx); + CHECK_AND_ASSERT_MES(r, false, "Failed to parse transaction prefix from blob"); + return true; + } + //--------------------------------------------------------------- bool parse_and_validate_tx_from_blob(const blobdata& tx_blob, transaction& tx, crypto::hash& tx_hash, crypto::hash& tx_prefix_hash) { std::stringstream ss; diff --git a/src/cryptonote_basic/cryptonote_format_utils.h b/src/cryptonote_basic/cryptonote_format_utils.h index bf71eb59189..725c75f4e99 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.h +++ b/src/cryptonote_basic/cryptonote_format_utils.h @@ -48,6 +48,7 @@ namespace cryptonote //--------------------------------------------------------------- void get_transaction_prefix_hash(const transaction_prefix& tx, crypto::hash& h); crypto::hash get_transaction_prefix_hash(const transaction_prefix& tx); + bool parse_and_validate_tx_prefix_from_blob(const blobdata& tx_blob, transaction_prefix& tx); bool parse_and_validate_tx_from_blob(const blobdata& tx_blob, transaction& tx, crypto::hash& tx_hash, crypto::hash& tx_prefix_hash); bool parse_and_validate_tx_from_blob(const blobdata& tx_blob, transaction& tx); bool parse_and_validate_tx_base_from_blob(const blobdata& tx_blob, transaction& tx); diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index a725eac6e0d..35797d2ac75 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -250,7 +250,7 @@ namespace cryptonote CRITICAL_REGION_LOCAL1(m_blockchain); LockedTXN lock(m_blockchain); m_blockchain.add_txpool_tx(tx, meta); - if (!insert_key_images(tx, kept_by_block)) + if (!insert_key_images(tx, id, kept_by_block)) return false; m_txs_by_fee_and_receive_time.emplace(std::pair(fee / (double)tx_weight, receive_time), id); } @@ -290,9 +290,10 @@ namespace cryptonote { CRITICAL_REGION_LOCAL1(m_blockchain); LockedTXN lock(m_blockchain); - m_blockchain.remove_txpool_tx(get_transaction_hash(tx)); + const crypto::hash txid = get_transaction_hash(tx); + m_blockchain.remove_txpool_tx(txid); m_blockchain.add_txpool_tx(tx, meta); - if (!insert_key_images(tx, kept_by_block)) + if (!insert_key_images(tx, txid, kept_by_block)) return false; m_txs_by_fee_and_receive_time.emplace(std::pair(fee / (double)tx_weight, receive_time), id); } @@ -371,8 +372,8 @@ namespace cryptonote continue; } cryptonote::blobdata txblob = m_blockchain.get_txpool_tx_blob(txid); - cryptonote::transaction tx; - if (!parse_and_validate_tx_from_blob(txblob, tx)) + cryptonote::transaction_prefix tx; + if (!parse_and_validate_tx_prefix_from_blob(txblob, tx)) { MERROR("Failed to parse tx from txpool"); return; @@ -381,7 +382,7 @@ namespace cryptonote MINFO("Pruning tx " << txid << " from txpool: weight: " << it->first.second << ", fee/byte: " << it->first.first); m_blockchain.remove_txpool_tx(txid); m_txpool_weight -= it->first.second; - remove_transaction_keyimages(tx); + remove_transaction_keyimages(tx, txid); MINFO("Pruned tx " << txid << " from txpool: weight: " << it->first.second << ", fee/byte: " << it->first.first); m_txs_by_fee_and_receive_time.erase(it--); changed = true; @@ -398,11 +399,10 @@ namespace cryptonote MINFO("Pool weight after pruning is larger than limit: " << m_txpool_weight << "/" << bytes); } //--------------------------------------------------------------------------------- - bool tx_memory_pool::insert_key_images(const transaction &tx, bool kept_by_block) + bool tx_memory_pool::insert_key_images(const transaction_prefix &tx, const crypto::hash &id, bool kept_by_block) { for(const auto& in: tx.vin) { - const crypto::hash id = get_transaction_hash(tx); CHECKED_GET_SPECIFIC_VARIANT(in, const txin_to_key, txin, false); std::unordered_set& kei_image_set = m_spent_key_images[txin.k_image]; CHECK_AND_ASSERT_MES(kept_by_block || kei_image_set.size() == 0, false, "internal error: kept_by_block=" << kept_by_block @@ -418,19 +418,17 @@ namespace cryptonote //FIXME: Can return early before removal of all of the key images. // At the least, need to make sure that a false return here // is treated properly. Should probably not return early, however. - bool tx_memory_pool::remove_transaction_keyimages(const transaction& tx) + bool tx_memory_pool::remove_transaction_keyimages(const transaction_prefix& tx, const crypto::hash &actual_hash) { CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL1(m_blockchain); // ND: Speedup - // 1. Move transaction hash calcuation outside of loop. ._. - crypto::hash actual_hash = get_transaction_hash(tx); for(const txin_v& vi: tx.vin) { CHECKED_GET_SPECIFIC_VARIANT(vi, const txin_to_key, txin, false); auto it = m_spent_key_images.find(txin.k_image); CHECK_AND_ASSERT_MES(it != m_spent_key_images.end(), false, "failed to find transaction input in key images. img=" << txin.k_image << ENDL - << "transaction id = " << get_transaction_hash(tx)); + << "transaction id = " << actual_hash); std::unordered_set& key_image_set = it->second; CHECK_AND_ASSERT_MES(key_image_set.size(), false, "empty key_image set, img=" << txin.k_image << ENDL << "transaction id = " << actual_hash); @@ -483,7 +481,7 @@ namespace cryptonote // remove first, in case this throws, so key images aren't removed m_blockchain.remove_txpool_tx(id); m_txpool_weight -= tx_weight; - remove_transaction_keyimages(tx); + remove_transaction_keyimages(tx, id); } catch (const std::exception &e) { @@ -515,7 +513,7 @@ namespace cryptonote { CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL1(m_blockchain); - std::unordered_set remove; + std::list> remove; m_blockchain.for_all_txpool_txes([this, &remove](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata*) { uint64_t tx_age = time(nullptr) - meta.receive_time; @@ -533,7 +531,7 @@ namespace cryptonote m_txs_by_fee_and_receive_time.erase(sorted_it); } m_timed_out_transactions.insert(txid); - remove.insert(txid); + remove.push_back(std::make_pair(txid, meta.weight)); } return true; }, false); @@ -541,13 +539,14 @@ namespace cryptonote if (!remove.empty()) { LockedTXN lock(m_blockchain); - for (const crypto::hash &txid: remove) + for (const std::pair &entry: remove) { + const crypto::hash &txid = entry.first; try { cryptonote::blobdata bd = m_blockchain.get_txpool_tx_blob(txid); - cryptonote::transaction tx; - if (!parse_and_validate_tx_from_blob(bd, tx)) + cryptonote::transaction_prefix tx; + if (!parse_and_validate_tx_prefix_from_blob(bd, tx)) { MERROR("Failed to parse tx from txpool"); // continue @@ -556,8 +555,8 @@ namespace cryptonote { // remove first, so we only remove key images if the tx removal succeeds m_blockchain.remove_txpool_tx(txid); - m_txpool_weight -= get_transaction_weight(tx, bd.size()); - remove_transaction_keyimages(tx); + m_txpool_weight -= entry.second; + remove_transaction_keyimages(tx, txid); } } catch (const std::exception &e) @@ -1041,7 +1040,7 @@ namespace cryptonote return true; } //--------------------------------------------------------------------------------- - bool tx_memory_pool::have_key_images(const std::unordered_set& k_images, const transaction& tx) + bool tx_memory_pool::have_key_images(const std::unordered_set& k_images, const transaction_prefix& tx) { for(size_t i = 0; i!= tx.vin.size(); i++) { @@ -1052,7 +1051,7 @@ namespace cryptonote return false; } //--------------------------------------------------------------------------------- - bool tx_memory_pool::append_key_images(std::unordered_set& k_images, const transaction& tx) + bool tx_memory_pool::append_key_images(std::unordered_set& k_images, const transaction_prefix& tx) { for(size_t i = 0; i!= tx.vin.size(); i++) { @@ -1301,7 +1300,7 @@ namespace cryptonote // remove tx from db first m_blockchain.remove_txpool_tx(txid); m_txpool_weight -= get_transaction_weight(tx, txblob.size()); - remove_transaction_keyimages(tx); + remove_transaction_keyimages(tx, txid); auto sorted_it = find_tx_in_sorted_container(txid); if (sorted_it == m_txs_by_fee_and_receive_time.end()) { @@ -1344,13 +1343,13 @@ namespace cryptonote bool r = m_blockchain.for_all_txpool_txes([this, &remove, kept](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd) { if (!!kept != !!meta.kept_by_block) return true; - cryptonote::transaction tx; - if (!parse_and_validate_tx_from_blob(*bd, tx)) + cryptonote::transaction_prefix tx; + if (!parse_and_validate_tx_prefix_from_blob(*bd, tx)) { MWARNING("Failed to parse tx from txpool, removing"); remove.push_back(txid); } - if (!insert_key_images(tx, meta.kept_by_block)) + if (!insert_key_images(tx, txid, meta.kept_by_block)) { MFATAL("Failed to insert key images from txpool tx"); return false; diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h index 892cadc6916..7a0cc23bf17 100644 --- a/src/cryptonote_core/tx_pool.h +++ b/src/cryptonote_core/tx_pool.h @@ -434,7 +434,7 @@ namespace cryptonote * * @return true on success, false on error */ - bool insert_key_images(const transaction &tx, bool kept_by_block); + bool insert_key_images(const transaction_prefix &tx, const crypto::hash &txid, bool kept_by_block); /** * @brief remove old transactions from the pool @@ -478,10 +478,11 @@ namespace cryptonote * a transaction from the pool. * * @param tx the transaction + * @param txid the transaction's hash * * @return false if any key images to be removed cannot be found, otherwise true */ - bool remove_transaction_keyimages(const transaction& tx); + bool remove_transaction_keyimages(const transaction_prefix& tx, const crypto::hash &txid); /** * @brief check if any of a transaction's spent key images are present in a given set @@ -491,7 +492,7 @@ namespace cryptonote * * @return true if any key images present in the set, otherwise false */ - static bool have_key_images(const std::unordered_set& kic, const transaction& tx); + static bool have_key_images(const std::unordered_set& kic, const transaction_prefix& tx); /** * @brief append the key images from a transaction to the given set @@ -501,7 +502,7 @@ namespace cryptonote * * @return false if any append fails, otherwise true */ - static bool append_key_images(std::unordered_set& kic, const transaction& tx); + static bool append_key_images(std::unordered_set& kic, const transaction_prefix& tx); /** * @brief check if a transaction is a valid candidate for inclusion in a block @@ -509,11 +510,11 @@ namespace cryptonote * @param txd the transaction to check (and info about it) * @param txid the txid of the transaction to check * @param txblob the transaction blob to check - * @param tx the parsed transaction, if successful + * @param tx the parsed transaction prefix, if successful * * @return true if the transaction is good to go, otherwise false */ - bool is_transaction_ready_to_go(txpool_tx_meta_t& txd, const crypto::hash &txid, const cryptonote::blobdata &txblob, transaction &tx) const; + bool is_transaction_ready_to_go(txpool_tx_meta_t& txd, const crypto::hash &txid, const cryptonote::blobdata &txblob, transaction&tx) const; /** * @brief mark all transactions double spending the one passed diff --git a/src/serialization/serialization.h b/src/serialization/serialization.h index 5fc382a1e62..2050e88e263 100644 --- a/src/serialization/serialization.h +++ b/src/serialization/serialization.h @@ -318,7 +318,7 @@ namespace serialization { * \brief self explanatory */ template - bool do_check_stream_state(Stream& s, boost::mpl::bool_) + bool do_check_stream_state(Stream& s, boost::mpl::bool_, bool noeof) { return s.good(); } @@ -329,13 +329,13 @@ namespace serialization { * \detailed Also checks to make sure that the stream is not at EOF */ template - bool do_check_stream_state(Stream& s, boost::mpl::bool_) + bool do_check_stream_state(Stream& s, boost::mpl::bool_, bool noeof) { bool result = false; if (s.good()) { std::ios_base::iostate state = s.rdstate(); - result = EOF == s.peek(); + result = noeof || EOF == s.peek(); s.clear(state); } return result; @@ -347,9 +347,9 @@ namespace serialization { * \brief calls detail::do_check_stream_state for ar */ template - bool check_stream_state(Archive& ar) + bool check_stream_state(Archive& ar, bool noeof = false) { - return detail::do_check_stream_state(ar.stream(), typename Archive::is_saving()); + return detail::do_check_stream_state(ar.stream(), typename Archive::is_saving(), noeof); } /*! \fn serialize @@ -360,6 +360,17 @@ namespace serialization { inline bool serialize(Archive &ar, T &v) { bool r = do_serialize(ar, v); - return r && check_stream_state(ar); + return r && check_stream_state(ar, false); + } + + /*! \fn serialize + * + * \brief serializes \a v into \a ar + */ + template + inline bool serialize_noeof(Archive &ar, T &v) + { + bool r = do_serialize(ar, v); + return r && check_stream_state(ar, true); } } From 17701864c41f5716c5252632a79f5f41b6f27bf3 Mon Sep 17 00:00:00 2001 From: iDunk5400 Date: Mon, 8 Oct 2018 12:25:45 +0200 Subject: [PATCH 0540/1404] Depends: build hidapi with -fPIC --- contrib/depends/packages/hidapi.mk | 1 + 1 file changed, 1 insertion(+) diff --git a/contrib/depends/packages/hidapi.mk b/contrib/depends/packages/hidapi.mk index d4dd80e2262..1c43e525ae8 100644 --- a/contrib/depends/packages/hidapi.mk +++ b/contrib/depends/packages/hidapi.mk @@ -13,6 +13,7 @@ $(package)_config_opts_linux+=libudev_LIBS="-L$(host_prefix)/lib -ludev" $(package)_config_opts_linux+=libudev_CFLAGS=-I$(host_prefix)/include $(package)_config_opts_linux+=libusb_LIBS="-L$(host_prefix)/lib -lusb-1.0" $(package)_config_opts_linux+=libusb_CFLAGS=-I$(host_prefix)/include/libusb-1.0 +$(package)_config_opts_linux+=--with-pic endef define $(package)_config_cmds From 6653062e6135e37fd36d81c575e70ef33b02609b Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 8 Oct 2018 10:44:51 +0000 Subject: [PATCH 0541/1404] unit_tests: add unit test for alloc alignment being a power of 2 --- tests/unit_tests/aligned.cpp | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/unit_tests/aligned.cpp b/tests/unit_tests/aligned.cpp index ad4837becde..2b733faf230 100644 --- a/tests/unit_tests/aligned.cpp +++ b/tests/unit_tests/aligned.cpp @@ -84,3 +84,24 @@ TEST(aligned, contents_smaller) aligned_free(ptr2); } +TEST(aligned, alignment) +{ + static const size_t good_alignments[] = {1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192}; + for (size_t a = 0; a <= 8192; ++a) + { + bool good = false; + for (const auto t: good_alignments) if (a == t) good = true; + void *ptr = aligned_malloc(1, a); + if (good) + { + ASSERT_TRUE(ptr != NULL); + aligned_free(ptr); + } + else + { + ASSERT_TRUE(ptr == NULL); + } + } + + ASSERT_TRUE(aligned_malloc(1, ~0) == NULL); +} From a39c0358466e3816885ea0786b3a0e997fdde673 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 8 Oct 2018 10:34:24 +0000 Subject: [PATCH 0542/1404] blockchain: add check test options are given for fakechain mode Coverity 188616 --- src/cryptonote_core/blockchain.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 6c6e024e4ad..96f3bc7e5fb 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -341,6 +341,9 @@ uint64_t Blockchain::get_current_blockchain_height() const bool Blockchain::init(BlockchainDB* db, const network_type nettype, bool offline, const cryptonote::test_options *test_options, difficulty_type fixed_difficulty, const GetCheckpointsCallback get_checkpoints/* = nullptr*/) { LOG_PRINT_L3("Blockchain::" << __func__); + + CHECK_AND_ASSERT_MES(nettype != FAKECHAIN || test_options, false, "fake chain network type used without options"); + CRITICAL_REGION_LOCAL(m_tx_pool); CRITICAL_REGION_LOCAL1(m_blockchain_lock); From 6ca00b6d75fbd4a06456e23e5982e72ca8e5ba17 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 8 Oct 2018 12:09:44 +0000 Subject: [PATCH 0543/1404] miner: really reset flags/precision on std::cout --- src/cryptonote_basic/miner.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cryptonote_basic/miner.cpp b/src/cryptonote_basic/miner.cpp index 2bd43de9417..d0b03593ed6 100644 --- a/src/cryptonote_basic/miner.cpp +++ b/src/cryptonote_basic/miner.cpp @@ -201,7 +201,7 @@ namespace cryptonote float hr = static_cast(total_hr)/static_cast(m_last_hash_rates.size()); const auto flags = std::cout.flags(); const auto precision = std::cout.precision(); - std::cout << "hashrate: " << std::setprecision(4) << std::fixed << hr << flags << precision << ENDL; + std::cout << "hashrate: " << std::setprecision(4) << std::fixed << hr << std::setiosflags(flags) << std::setprecision(precision) << ENDL; } } m_last_hr_merge_time = misc_utils::get_tick_count(); From 2e2daebcc9c2e664070670efb3cad81618567f0b Mon Sep 17 00:00:00 2001 From: iDunk5400 Date: Thu, 9 Nov 2017 21:11:20 +0100 Subject: [PATCH 0544/1404] ANSI colors in Windows 10 --- contrib/epee/src/mlog.cpp | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/contrib/epee/src/mlog.cpp b/contrib/epee/src/mlog.cpp index 61d853ef484..638155b6be3 100644 --- a/contrib/epee/src/mlog.cpp +++ b/contrib/epee/src/mlog.cpp @@ -28,6 +28,13 @@ #ifndef _MLOG_H_ #define _MLOG_H_ +#ifdef _WIN32 +#include +#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING +#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004 +#endif +#endif + #include #include #include @@ -117,6 +124,31 @@ static const char *get_default_categories(int level) return categories; } +#ifdef WIN32 +bool EnableVTMode() +{ + // Set output mode to handle virtual terminal sequences + HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); + if (hOut == INVALID_HANDLE_VALUE) + { + return false; + } + + DWORD dwMode = 0; + if (!GetConsoleMode(hOut, &dwMode)) + { + return false; + } + + dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; + if (!SetConsoleMode(hOut, dwMode)) + { + return false; + } + return true; +} +#endif + void mlog_configure(const std::string &filename_base, bool console, const std::size_t max_log_file_size, const std::size_t max_log_files) { el::Configurations c; @@ -202,6 +234,9 @@ void mlog_configure(const std::string &filename_base, bool console, const std::s monero_log = get_default_categories(0); } mlog_set_log(monero_log); +#ifdef WIN32 + EnableVTMode(); +#endif } void mlog_set_categories(const char *categories) From 6d0ca4e25ef2a0962583697d6afe1fbf22aa44c8 Mon Sep 17 00:00:00 2001 From: TheCharlatan Date: Mon, 8 Oct 2018 15:53:22 +0200 Subject: [PATCH 0545/1404] Prepare Depends Packages for Gitian Scripts The gitian environment does not treat whitespaces in configure lines, like most other systems. The solution is to just remove them. --- contrib/depends/packages/libiconv.mk | 15 ++++++++++++++- contrib/depends/packages/sodium.mk | 2 ++ .../depends/patches/libiconv/fix-whitespace.patch | 13 +++++++++++++ .../depends/patches/sodium/fix-whitespace.patch | 13 +++++++++++++ 4 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 contrib/depends/patches/libiconv/fix-whitespace.patch create mode 100644 contrib/depends/patches/sodium/fix-whitespace.patch diff --git a/contrib/depends/packages/libiconv.mk b/contrib/depends/packages/libiconv.mk index 87e30b208ad..dbcb2814112 100644 --- a/contrib/depends/packages/libiconv.mk +++ b/contrib/depends/packages/libiconv.mk @@ -3,9 +3,22 @@ $(package)_version=1.15 $(package)_download_path=https://ftp.gnu.org/gnu/libiconv $(package)_file_name=libiconv-$($(package)_version).tar.gz $(package)_sha256_hash=ccf536620a45458d26ba83887a983b96827001e92a13847b45e4925cc8913178 +$(package)_patches=fix-whitespace.patch + +define $(package)_set_vars + $(package)_config_opts=--disable-nls + $(package)_config_opts=--enable-static + $(package)_config_opts=--disable-shared + $(package)_config_opts_linux=--with-pic +endef + +define $(package)_preprocess_cmds + cp -f $(BASEDIR)/config.guess $(BASEDIR)/config.sub build-aux/ &&\ + patch -p1 < $($(package)_patch_dir)/fix-whitespace.patch +endef define $(package)_config_cmds - $($(package)_autoconf) --disable-nls --enable-static --disable-shared + $($(package)_autoconf) endef define $(package)_build_cmds diff --git a/contrib/depends/packages/sodium.mk b/contrib/depends/packages/sodium.mk index c38121bf730..35f444fd5c3 100644 --- a/contrib/depends/packages/sodium.mk +++ b/contrib/depends/packages/sodium.mk @@ -3,6 +3,7 @@ $(package)_version=1.0.15 $(package)_download_path=https://download.libsodium.org/libsodium/releases/ $(package)_file_name=libsodium-$($(package)_version).tar.gz $(package)_sha256_hash=fb6a9e879a2f674592e4328c5d9f79f082405ee4bb05cb6e679b90afe9e178f4 +$(package)_patches=fix-whitespace.patch define $(package)_set_vars $(package)_config_opts=--enable-static --disable-shared @@ -11,6 +12,7 @@ endef define $(package)_config_cmds ./autogen.sh &&\ + patch -p1 < $($(package)_patch_dir)/fix-whitespace.patch &&\ $($(package)_autoconf) $($(package)_config_opts) endef diff --git a/contrib/depends/patches/libiconv/fix-whitespace.patch b/contrib/depends/patches/libiconv/fix-whitespace.patch new file mode 100644 index 00000000000..531364b45f2 --- /dev/null +++ b/contrib/depends/patches/libiconv/fix-whitespace.patch @@ -0,0 +1,13 @@ +diff --git a/preload/configure b/preload/configure +index aab5c77..e20b8f0 100755 +--- a/preload/configure ++++ b/preload/configure +@@ -588,7 +588,7 @@ MAKEFLAGS= + PACKAGE_NAME='libiconv' + PACKAGE_TARNAME='libiconv' + PACKAGE_VERSION='0' +-PACKAGE_STRING='libiconv 0' ++PACKAGE_STRING='libiconv0' + PACKAGE_BUGREPORT='' + PACKAGE_URL='' + diff --git a/contrib/depends/patches/sodium/fix-whitespace.patch b/contrib/depends/patches/sodium/fix-whitespace.patch new file mode 100644 index 00000000000..c118386118a --- /dev/null +++ b/contrib/depends/patches/sodium/fix-whitespace.patch @@ -0,0 +1,13 @@ +diff --git a/configure b/configure +index b29f769..ca008ae 100755 +--- a/configure ++++ b/configure +@@ -591,7 +591,7 @@ MAKEFLAGS= + PACKAGE_NAME='libsodium' + PACKAGE_TARNAME='libsodium' + PACKAGE_VERSION='1.0.15' +-PACKAGE_STRING='libsodium 1.0.15' ++PACKAGE_STRING='libsodium' + PACKAGE_BUGREPORT='https://github.com/jedisct1/libsodium/issues' + PACKAGE_URL='https://github.com/jedisct1/libsodium' + From c5a97315eede7588d0cb1e27375c72d044417cb6 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 8 Oct 2018 14:17:41 +0000 Subject: [PATCH 0546/1404] Remove last traces of libpcsc-lite --- README.md | 3 +-- src/device/device_ledger.cpp | 9 --------- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/README.md b/README.md index bce41f5cfdb..85c64ff35eb 100644 --- a/README.md +++ b/README.md @@ -150,14 +150,13 @@ library archives (`.a`). | GTest | 1.5 | YES | `libgtest-dev`^ | `gtest` | `gtest-devel` | YES | Test suite | | Doxygen | any | NO | `doxygen` | `doxygen` | `doxygen` | YES | Documentation | | Graphviz | any | NO | `graphviz` | `graphviz` | `graphviz` | YES | Documentation | -| pcsclite | ? | NO | `libpcsclite-dev` | ? | `pcsc-lite pcsc-lite-devel` | NO | Ledger | [^] On Debian/Ubuntu `libgtest-dev` only includes sources and headers. You must build the library binary manually. This can be done with the following command ```sudo apt-get install libgtest-dev && cd /usr/src/gtest && sudo cmake . && sudo make && sudo mv libg* /usr/lib/ ``` Debian / Ubuntu one liner for all dependencies -``` sudo apt update && sudo apt install build-essential cmake pkg-config libboost-all-dev libssl-dev libzmq3-dev libunbound-dev libsodium-dev libunwind8-dev liblzma-dev libreadline6-dev libldns-dev libexpat1-dev doxygen graphviz libpcsclite-dev libpgm-dev``` +``` sudo apt update && sudo apt install build-essential cmake pkg-config libboost-all-dev libssl-dev libzmq3-dev libunbound-dev libsodium-dev libunwind8-dev liblzma-dev libreadline6-dev libldns-dev libexpat1-dev doxygen graphviz libpgm-dev``` ### Cloning the repository diff --git a/src/device/device_ledger.cpp b/src/device/device_ledger.cpp index 456eda739f1..464cb0d9039 100644 --- a/src/device/device_ledger.cpp +++ b/src/device/device_ledger.cpp @@ -48,21 +48,12 @@ namespace hw { /* ===================================================================== */ /* === Debug ==== */ /* ===================================================================== */ - #ifdef WIN32 - static char *pcsc_stringify_error(LONG rv) { - static __thread char out[20]; - sprintf_s(out, sizeof(out), "0x%08lX", rv); - - return out; - } - #endif void set_apdu_verbose(bool verbose) { apdu_verbose = verbose; } #define TRACKD MTRACE("hw") - #define ASSERT_RV(rv) CHECK_AND_ASSERT_THROW_MES((rv)==SCARD_S_SUCCESS, "Fail SCard API : (" << (rv) << ") "<< pcsc_stringify_error(rv)<<" Device="<id<<", hCard="< Date: Mon, 8 Oct 2018 15:55:38 +0200 Subject: [PATCH 0547/1404] Add gitian build script This adds a build script to run gitian builds for linux. The build script was copied from bitcoin and then adapted for monero. Build step documentation is outlined in the README in the contrib/gitian directory. --- contrib/gitian/README.md | 158 ++++++++++++++++++++++ contrib/gitian/gitian-build.py | 229 ++++++++++++++++++++++++++++++++ contrib/gitian/gitian-linux.yml | 162 ++++++++++++++++++++++ contrib/gitian/symbol-check.py | 163 +++++++++++++++++++++++ 4 files changed, 712 insertions(+) create mode 100644 contrib/gitian/README.md create mode 100755 contrib/gitian/gitian-build.py create mode 100644 contrib/gitian/gitian-linux.yml create mode 100755 contrib/gitian/symbol-check.py diff --git a/contrib/gitian/README.md b/contrib/gitian/README.md new file mode 100644 index 00000000000..1f7d158eb45 --- /dev/null +++ b/contrib/gitian/README.md @@ -0,0 +1,158 @@ +Gitian building +================ + +*Setup instructions for a Gitian build of Monero using a VM or physical system.* + +Gitian is the deterministic build process that is used to build the Bitcoin +Core executables. It provides a way to be reasonably sure that the +executables are really built from the git source. It also makes sure that +the same, tested dependencies are used and statically built into the executable. + +Multiple developers build the source code by following a specific descriptor +("recipe"), cryptographically sign the result, and upload the resulting signature. +These results are compared and only if they match, the build is accepted and provided +for download. + +More independent Gitian builders are needed, which is why this guide exists. +It is preferred you follow these steps yourself instead of using someone else's +VM image to avoid 'contaminating' the build. + +Table of Contents +------------------ + +Please note that these instructions have been forked from bitcoin's gitian build +instructions. Please also consult their documentation, when running into problems. +The signing is left as inherited from bitcoin at the moment, since building currently +still fails with libiconv. + +- [Preparing the Gitian builder host](#preparing-the-gitian-builder-host) +- [Getting and building the inputs](#getting-and-building-the-inputs) +- [Building Binaries](#building-bitcoin-core) +- [Signing externally](#signing-externally) +- [Uploading signatures](#uploading-signatures) + +Preparing the Gitian builder host +--------------------------------- + +The first step is to prepare the host environment that will be used to perform the Gitian builds. +This guide explains how to set up the environment, and how to start the builds. + +Gitian builds are for now executed on Ubuntu 18.04 "Bionic Beaver". Please run Ubuntu in either a VM, or on your physical machine. +You need to be logged in as the `gitianuser` in order to build gitian builds. If this user does not exist yet on your system, +create him. + +Note that a version of `lxc-execute` higher or equal to 2.1.1 is required. +You can check the version with `lxc-execute --version`. + +First we need to set up dependencies. Type/paste the following in the terminal: + +```bash +sudo apt-get install git ruby apt-cacher-ng qemu-utils debootstrap lxc python-cheetah parted kpartx bridge-utils make ubuntu-archive-keyring curl firewalld +``` + +Then set up LXC and the rest with the following, which is a complex jumble of settings and workarounds: + +```bash +sudo -s +# the version of lxc-start in Debian needs to run as root, so make sure +# that the build script can execute it without providing a password +echo "%sudo ALL=NOPASSWD: /usr/bin/lxc-start" > /etc/sudoers.d/gitian-lxc +echo "%sudo ALL=NOPASSWD: /usr/bin/lxc-execute" >> /etc/sudoers.d/gitian-lxc +# make /etc/rc.local script that sets up bridge between guest and host +echo '#!/bin/sh -e' > /etc/rc.local +echo 'brctl addbr br0' >> /etc/rc.local +echo 'ip addr add 10.0.3.1/24 broadcast 10.0.3.255 dev br0' >> /etc/rc.local +echo 'ip link set br0 up' >> /etc/rc.local +echo 'firewall-cmd --zone=trusted --add-interface=br0' >> /etc/rc.local +echo 'exit 0' >> /etc/rc.local +chmod +x /etc/rc.local +# make sure that USE_LXC is always set when logging in as gitianuser, +# and configure LXC IP addresses +echo 'export USE_LXC=1' >> /home/gitianuser/.profile +echo 'export GITIAN_HOST_IP=10.0.3.1' >> /home/gitianuser/.profile +echo 'export LXC_GUEST_IP=10.0.3.5' >> /home/gitianuser/.profile +reboot +``` + +This setup is required to enable networking in the container. + + +Manual and Building +------------------- +The instructions below use the automated script [gitian-build.py](https://github.com/betcoin/bitcoin/blob/master/contrib/gitian-build.py) which only works in Ubuntu. For manual steps and instructions for fully offline signing, see [this guide](./gitian-building/gitian-building-manual.md). + +MacOS code signing +------------------ +In order to sign builds for MacOS, you need to download the free SDK and extract a file. The steps are described [here](./gitian-building/gitian-building-mac-os-sdk.md). Alternatively, you can skip the OSX build by adding `--os=lw` below. + +Initial Gitian Setup +-------------------- +The `gitian-build.py` script will checkout different release tags, so it's best to copy it: + +```bash +cp monero/contrib/gitian/gitian-build.py . +``` + +You only need to do this once: + +``` +./gitian-build.py --setup fluffypony 0.0.20 +``` + +Where `fluffypony` is your Github name and `0.0.20` is the most recent tag (without `v`). + +In order to sign gitian builds on your host machine, which has your PGP key, fork the gitian.sigs repository and clone it on your host machine: + +``` +git clone git@github.com:bitcoin-core/gitian.sigs.git +git remote add satoshi git@github.com:satoshi/gitian.sigs.git +``` + +Build Binaries +----------------------------- +Windows and OSX have code signed binaries, but those won't be available until a few developers have gitian signed the non-codesigned binaries. + +To build the most recent tag: + + `./gitian-build.py --detach-sign --no-commit -b fluffypony 0.0.20` + +To speed up the build, use `-j 5 -m 5000` as the first arguments, where `5` is the number of CPU's you allocated to the VM plus one, and 5000 is a little bit less than then the MB's of RAM you allocated. If there is memory corruption on your machine, try to tweak these values. + +If all went well, this produces a number of (uncommited) `.assert` files in the gitian.sigs repository. + +You need to copy these uncommited changes to your host machine, where you can sign them: + +``` +export NAME=satoshi +gpg --output $VERSION-linux/$NAME/bitcoin-linux-0.16-build.assert.sig --detach-sign 0.16.0rc1-linux/$NAME/bitcoin-linux-0.16-build.assert +gpg --output $VERSION-osx-unsigned/$NAME/bitcoin-osx-0.16-build.assert.sig --detach-sign 0.16.0rc1-osx-unsigned/$NAME/bitcoin-osx-0.16-build.assert +gpg --output $VERSION-win-unsigned/$NAME/bitcoin-win-0.16-build.assert.sig --detach-sign 0.16.0rc1-win-unsigned/$NAME/bitcoin-win-0.16-build.assert +``` + +Make a PR (both the `.assert` and `.assert.sig` files) to the +[bitcoin-core/gitian.sigs](https://github.com/bitcoin-core/gitian.sigs/) repository: + +``` +git checkout -b 0.0.20-not-codesigned +git commit -S -a -m "Add $NAME 0.0.20 non-code signed signatures" +git push --set-upstream $NAME 0.0.20 +``` + +You can also mail the files to Wladimir (laanwj@gmail.com) and he will commit them. + +```bash + gpg --detach-sign ${VERSION}-linux/${SIGNER}/bitcoin-linux-*-build.assert + gpg --detach-sign ${VERSION}-win-unsigned/${SIGNER}/bitcoin-win-*-build.assert + gpg --detach-sign ${VERSION}-osx-unsigned/${SIGNER}/bitcoin-osx-*-build.assert +``` + +You may have other .assert files as well (e.g. `signed` ones), in which case you should sign them too. You can see all of them by doing `ls ${VERSION}-*/${SIGNER}`. + +This will create the `.sig` files that can be committed together with the `.assert` files to assert your +Gitian build. + + + `./gitian-build.py --detach-sign -s satoshi 0.16.0rc1 --nocommit` + +Make another pull request for these. + diff --git a/contrib/gitian/gitian-build.py b/contrib/gitian/gitian-build.py new file mode 100755 index 00000000000..bd7e70a715f --- /dev/null +++ b/contrib/gitian/gitian-build.py @@ -0,0 +1,229 @@ +#!/usr/bin/env python3 + +import argparse +import os +import subprocess +import sys + +def setup(): + global args, workdir + programs = ['ruby', 'git', 'apt-cacher-ng', 'make', 'wget'] + if args.kvm: + programs += ['python-vm-builder', 'qemu-kvm', 'qemu-utils'] + elif args.docker: + dockers = ['docker.io', 'docker-ce'] + for i in dockers: + return_code = subprocess.call(['sudo', 'apt-get', 'install', '-qq', i]) + if return_code == 0: + break + if return_code != 0: + print('Cannot find any way to install docker', file=sys.stderr) + exit(1) + else: + programs += ['lxc', 'debootstrap'] + subprocess.check_call(['sudo', 'apt-get', 'install', '-qq'] + programs) + if not os.path.isdir('gitian.sigs'): + subprocess.check_call(['git', 'clone', 'https://github.com/bitcoin-core/gitian.sigs.git']) + if not os.path.isdir('monero-detached-sigs'): + subprocess.check_call(['git', 'clone', 'https://github.com/bitcoin-core/bitcoin-detached-sigs.git']) + if not os.path.isdir('gitian-builder'): + subprocess.check_call(['git', 'clone', 'https://github.com/devrandom/gitian-builder.git']) + if not os.path.isdir('monero'): + subprocess.check_call(['git', 'clone', 'https://github.com/bitcoin-core/monero.git']) + os.chdir('gitian-builder') + make_image_prog = ['bin/make-base-vm', '--suite', 'bionic', '--arch', 'amd64'] + if args.docker: + make_image_prog += ['--docker'] + elif not args.kvm: + make_image_prog += ['--lxc'] + subprocess.check_call(make_image_prog) + os.chdir(workdir) + if args.is_bionic and not args.kvm and not args.docker: + subprocess.check_call(['sudo', 'sed', '-i', 's/lxcbr0/br0/', '/etc/default/lxc-net']) + print('Reboot is required') + exit(0) + +def build(): + global args, workdir + + os.makedirs('monero-binaries/' + args.version, exist_ok=True) + print('\nBuilding Dependencies\n') + os.chdir('gitian-builder') + os.makedirs('inputs', exist_ok=True) + + subprocess.check_call(['wget', '-N', '-P', 'inputs', 'http://downloads.sourceforge.net/project/osslsigncode/osslsigncode/osslsigncode-1.7.1.tar.gz']) + subprocess.check_call(['wget', '-N', '-P', 'inputs', 'https://bitcoincore.org/cfields/osslsigncode-Backports-to-1.7.1.patch']) + subprocess.check_call(['make', '-C', '../monero/contrib/depends', 'download', 'SOURCES_PATH=' + os.getcwd() + '/cache/common']) + + if args.linux: + print('\nCompiling ' + args.version + ' Linux') + subprocess.check_call(['bin/gbuild', '-j', args.jobs, '-m', args.memory, '--commit', 'monero='+args.commit, '--url', 'monero='+args.url, '../monero/contrib/gitian/gitian-linux.yml']) + subprocess.check_call(['bin/gsign', '-p', args.sign_prog, '--signer', args.signer, '--release', args.version+'-linux', '--destination', '../gitian.sigs/', '../monero/contrib/gitian/gitian-linux.yml']) + subprocess.check_call('mv build/out/monero-*.tar.gz build/out/monero-*.tar.gz ../monero-binaries/'+args.version, shell=True) + + if args.windows: + print('\nCompiling ' + args.version + ' Windows') + subprocess.check_call(['bin/gbuild', '-j', args.jobs, '-m', args.memory, '--commit', 'monero='+args.commit, '--url', 'monero='+args.url, '../monero/contrib/gitian/gitian-win.yml']) + subprocess.check_call(['bin/gsign', '-p', args.sign_prog, '--signer', args.signer, '--release', args.version+'-win-unsigned', '--destination', '../gitian.sigs/', '../monero/contrib/gitian/gitian-win.yml']) + subprocess.check_call('mv build/out/monero*-win-unsigned.tar.gz inputs/monerowin-unsigned.tar.gz', shell=True) + subprocess.check_call('mv build/out/monero*.zip build/out/monero*.exe ../monerobinaries/'+args.version, shell=True) + + if args.macos: + print('\nCompiling ' + args.version + ' MacOS') + subprocess.check_call(['bin/gbuild', '-j', args.jobs, '-m', args.memory, '--commit', 'monero'+args.commit, '--url', 'monero'+args.url, '../monero/contrib/gitian/gitian-osx.yml']) + subprocess.check_call(['bin/gsign', '-p', args.sign_prog, '--signer', args.signer, '--release', args.version+'-osx-unsigned', '--destination', '../gitian.sigs/', '../monero/contrib/gitian/gitian-osx.yml']) + subprocess.check_call('mv build/out/monero*-osx-unsigned.tar.gz inputs/moneroosx-unsigned.tar.gz', shell=True) + subprocess.check_call('mv build/out/monero*.tar.gz build/out/monero*.dmg ../monerobinaries/'+args.version, shell=True) + + os.chdir(workdir) + + if args.commit_files: + print('\nCommitting '+args.version+' Unsigned Sigs\n') + os.chdir('gitian.sigs') + subprocess.check_call(['git', 'add', args.version+'-linux/'+args.signer]) + subprocess.check_call(['git', 'add', args.version+'-win-unsigned/'+args.signer]) + subprocess.check_call(['git', 'add', args.version+'-osx-unsigned/'+args.signer]) + subprocess.check_call(['git', 'commit', '-m', 'Add '+args.version+' unsigned sigs for '+args.signer]) + os.chdir(workdir) + +def sign(): + global args, workdir + os.chdir('gitian-builder') + + if args.windows: + print('\nSigning ' + args.version + ' Windows') + subprocess.check_call(['bin/gbuild', '-i', '--commit', 'signature='+args.commit, '../monero/contrib/gitian/gitian-win-signer.yml']) + subprocess.check_call(['bin/gsign', '-p', args.sign_prog, '--signer', args.signer, '--release', args.version+'-win-signed', '--destination', '../gitian.sigs/', '../monero/contrib/gitian/gitian-win-signer.yml']) + subprocess.check_call('mv build/out/monero*win64-setup.exe ../monerobinaries/'+args.version, shell=True) + subprocess.check_call('mv build/out/monero*win32-setup.exe ../monerobinaries/'+args.version, shell=True) + + if args.macos: + print('\nSigning ' + args.version + ' MacOS') + subprocess.check_call(['bin/gbuild', '-i', '--commit', 'signature='+args.commit, '../monero/contrib/gitian/gitian-osx-signer.yml']) + subprocess.check_call(['bin/gsign', '-p', args.sign_prog, '--signer', args.signer, '--release', args.version+'-osx-signed', '--destination', '../gitian.sigs/', '../monero/contrib/gitian/gitian-osx-signer.yml']) + subprocess.check_call('mv build/out/moneroosx-signed.dmg ../monerobinaries/'+args.version+'/monero'+args.version+'-osx.dmg', shell=True) + + os.chdir(workdir) + + if args.commit_files: + print('\nCommitting '+args.version+' Signed Sigs\n') + os.chdir('gitian.sigs') + subprocess.check_call(['git', 'add', args.version+'-win-signed/'+args.signer]) + subprocess.check_call(['git', 'add', args.version+'-osx-signed/'+args.signer]) + subprocess.check_call(['git', 'commit', '-a', '-m', 'Add '+args.version+' signed binary sigs for '+args.signer]) + os.chdir(workdir) + +def verify(): + global args, workdir + os.chdir('gitian-builder') + + print('\nVerifying v'+args.version+' Linux\n') + subprocess.check_call(['bin/gverify', '-v', '-d', '../gitian.sigs/', '-r', args.version+'-linux', '../monero/contrib/gitian/gitian-linux.yml']) + print('\nVerifying v'+args.version+' Windows\n') + subprocess.check_call(['bin/gverify', '-v', '-d', '../gitian.sigs/', '-r', args.version+'-win-unsigned', '../monero/contrib/gitian/gitian-win.yml']) + print('\nVerifying v'+args.version+' MacOS\n') + subprocess.check_call(['bin/gverify', '-v', '-d', '../gitian.sigs/', '-r', args.version+'-osx-unsigned', '../monero/contrib/gitian/gitian-osx.yml']) + print('\nVerifying v'+args.version+' Signed Windows\n') + subprocess.check_call(['bin/gverify', '-v', '-d', '../gitian.sigs/', '-r', args.version+'-win-signed', '../monero/contrib/gitian/gitian-win-signer.yml']) + print('\nVerifying v'+args.version+' Signed MacOS\n') + subprocess.check_call(['bin/gverify', '-v', '-d', '../gitian.sigs/', '-r', args.version+'-osx-signed', '../monero/contrib/gitian/gitian-osx-signer.yml']) + + os.chdir(workdir) + +def main(): + global args, workdir + + parser = argparse.ArgumentParser(usage='%(prog)s [options] signer version') + parser.add_argument('-c', '--commit', action='store_true', dest='commit', help='Indicate that the version argument is for a commit or branch') + parser.add_argument('-p', '--pull', action='store_true', dest='pull', help='Indicate that the version argument is the number of a github repository pull request') + parser.add_argument('-u', '--url', dest='url', default='https://github.com/TheCharlatan/monero', help='Specify the URL of the repository. Default is %(default)s') + parser.add_argument('-v', '--verify', action='store_true', dest='verify', help='Verify the Gitian build') + parser.add_argument('-b', '--build', action='store_true', dest='build', help='Do a Gitian build') + parser.add_argument('-s', '--sign', action='store_true', dest='sign', help='Make signed binaries for Windows and MacOS') + parser.add_argument('-B', '--buildsign', action='store_true', dest='buildsign', help='Build both signed and unsigned binaries') + parser.add_argument('-o', '--os', dest='os', default='lwm', help='Specify which Operating Systems the build is for. Default is %(default)s. l for Linux, w for Windows, m for MacOS') + parser.add_argument('-j', '--jobs', dest='jobs', default='2', help='Number of processes to use. Default %(default)s') + parser.add_argument('-m', '--memory', dest='memory', default='2000', help='Memory to allocate in MiB. Default %(default)s') + parser.add_argument('-k', '--kvm', action='store_true', dest='kvm', help='Use KVM instead of LXC') + parser.add_argument('-d', '--docker', action='store_true', dest='docker', help='Use Docker instead of LXC') + parser.add_argument('-S', '--setup', action='store_true', dest='setup', help='Set up the Gitian building environment. Uses LXC. If you want to use KVM, use the --kvm option. Only works on Debian-based systems (Ubuntu, Debian)') + parser.add_argument('-D', '--detach-sign', action='store_true', dest='detach_sign', help='Create the assert file for detached signing. Will not commit anything.') + parser.add_argument('-n', '--no-commit', action='store_false', dest='commit_files', help='Do not commit anything to git') + parser.add_argument('signer', help='GPG signer to sign each build assert file') + parser.add_argument('version', help='Version number, commit, or branch to build. If building a commit or branch, the -c option must be specified') + + args = parser.parse_args() + workdir = os.getcwd() + + args.linux = 'l' in args.os + args.windows = 'w' in args.os + args.macos = 'm' in args.os + + args.is_bionic = b'bionic' in subprocess.check_output(['lsb_release', '-cs']) + + if args.buildsign: + args.build=True + args.sign=True + + if args.kvm and args.docker: + raise Exception('Error: cannot have both kvm and docker') + + args.sign_prog = 'true' if args.detach_sign else 'gpg --detach-sign' + + # Set enviroment variable USE_LXC or USE_DOCKER, let gitian-builder know that we use lxc or docker + if args.docker: + os.environ['USE_DOCKER'] = '1' + elif not args.kvm: + os.environ['USE_LXC'] = '1' + if not 'GITIAN_HOST_IP' in os.environ.keys(): + os.environ['GITIAN_HOST_IP'] = '10.0.3.1' + if not 'LXC_GUEST_IP' in os.environ.keys(): + os.environ['LXC_GUEST_IP'] = '10.0.3.5' + + # Disable for MacOS if no SDK found + if args.macos and not os.path.isfile('gitian-builder/inputs/MacOSX10.11.sdk.tar.gz'): + print('Cannot build for MacOS, SDK does not exist. Will build for other OSes') + args.macos = False + + script_name = os.path.basename(sys.argv[0]) + # Signer and version shouldn't be empty + if args.signer == '': + print(script_name+': Missing signer.') + print('Try '+script_name+' --help for more information') + exit(1) + if args.version == '': + print(script_name+': Missing version.') + print('Try '+script_name+' --help for more information') + exit(1) + + # Add leading 'v' for tags + if args.commit and args.pull: + raise Exception('Cannot have both commit and pull') + args.commit = ('' if args.commit else 'v') + args.version + + if args.setup: + setup() + + os.chdir('monero') + if args.pull: + subprocess.check_call(['git', 'fetch', args.url, 'refs/pull/'+args.version+'/merge']) + os.chdir('../gitian-builder/inputs/monero') + subprocess.check_call(['git', 'fetch', args.url, 'refs/pull/'+args.version+'/merge']) + args.commit = subprocess.check_output(['git', 'show', '-s', '--format=%H', 'FETCH_HEAD'], universal_newlines=True).strip() + args.version = 'pull-' + args.version + print(args.commit) + subprocess.check_call(['git', 'fetch']) + subprocess.check_call(['git', 'checkout', args.commit]) + os.chdir(workdir) + + if args.build: + build() + + if args.sign: + sign() + + if args.verify: + verify() + +if __name__ == '__main__': + main() diff --git a/contrib/gitian/gitian-linux.yml b/contrib/gitian/gitian-linux.yml new file mode 100644 index 00000000000..751fbadd32a --- /dev/null +++ b/contrib/gitian/gitian-linux.yml @@ -0,0 +1,162 @@ +--- +name: "monero-linux-0.18" +enable_cache: true +suites: +- "bionic" +architectures: +- "amd64" +packages: +- "curl" +- "gperf" +- "gcc-7" +- "g++-7" +- "gcc" +- "g++" +- "gcc-7-aarch64-linux-gnu" +- "g++-7-aarch64-linux-gnu" +- "gcc-aarch64-linux-gnu" +- "g++-aarch64-linux-gnu" +- "binutils-aarch64-linux-gnu" +- "gcc-7-arm-linux-gnueabihf" +- "g++-7-arm-linux-gnueabihf" +- "gcc-arm-linux-gnueabihf" +- "g++-arm-linux-gnueabihf" +- "g++-7-multilib" +- "gcc-7-multilib" +- "binutils-arm-linux-gnueabihf" +- "binutils-gold" +- "git" +- "pkg-config" +- "build-essential" +- "autoconf" +- "libtool" +- "automake" +- "faketime" +- "bsdmainutils" +- "ca-certificates" +- "python" +- "cmake" +- "ccache" +- "protobuf-compiler" +- "libdbus-1-dev" +- "libharfbuzz-dev" +- "libprotobuf-dev" +- "python3-zmq" +remotes: +- "url": "https://github.com/TheCharlatan/monero.git" + "dir": "monero" +files: [] +script: | + + WRAP_DIR=$HOME/wrapped + HOSTS="x86_64-linux-gnu arm-linux-gnueabihf aarch64-linux-gnu i686-linux-gnu" + FAKETIME_HOST_PROGS="gcc g++" + FAKETIME_PROGS="date ar ranlib nm" + HOST_CFLAGS="-O2 -g" + HOST_CXXFLAGS="-O2 -g" + HOST_LDFLAGS=-static-libstdc++ + + export GZIP="-9n" + export TAR_OPTIONS="--mtime="$REFERENCE_DATE\\\ $REFERENCE_TIME"" + export TZ="UTC" + export BUILD_DIR=`pwd` + mkdir -p ${WRAP_DIR} + if test -n "$GBUILD_CACHE_ENABLED"; then + export SOURCES_PATH=${GBUILD_COMMON_CACHE} + export BASE_CACHE=${GBUILD_PACKAGE_CACHE} + mkdir -p ${BASE_CACHE} ${SOURCES_PATH} + fi + + function create_global_faketime_wrappers { + for prog in ${FAKETIME_PROGS}; do + echo '#!/usr/bin/env bash' > ${WRAP_DIR}/${prog} + echo "REAL=\`which -a ${prog} | grep -v ${WRAP_DIR}/${prog} | head -1\`" >> ${WRAP_DIR}/${prog} + echo 'export LD_PRELOAD=/usr/lib/x86_64-linux-gnu/faketime/libfaketime.so.1' >> ${WRAP_DIR}/${prog} + echo "export FAKETIME=\"$1\"" >> ${WRAP_DIR}/${prog} + echo "\$REAL \$@" >> $WRAP_DIR/${prog} + chmod +x ${WRAP_DIR}/${prog} + done + } + + function create_per-host_faketime_wrappers { + for i in $HOSTS; do + for prog in ${FAKETIME_HOST_PROGS}; do + if which ${i}-${prog}-7 + then + echo '#!/usr/bin/env bash' > ${WRAP_DIR}/${i}-${prog} + echo "REAL=\`which -a ${i}-${prog}-7 | grep -v ${WRAP_DIR}/${i}-${prog} | head -1\`" >> ${WRAP_DIR}/${i}-${prog} + echo 'export LD_PRELOAD=/usr/lib/x86_64-linux-gnu/faketime/libfaketime.so.1' >> ${WRAP_DIR}/${i}-${prog} + echo "export FAKETIME=\"$1\"" >> ${WRAP_DIR}/${i}-${prog} + echo "\$REAL \$@" >> $WRAP_DIR/${i}-${prog} + chmod +x ${WRAP_DIR}/${i}-${prog} + fi + done + done + } + + # Faketime for depends so intermediate results are comparable + export PATH_orig=${PATH} + create_global_faketime_wrappers "2000-01-01 12:00:00" + create_per-host_faketime_wrappers "2000-01-01 12:00:00" + export PATH=${WRAP_DIR}:${PATH} + + EXTRA_INCLUDES_BASE=$WRAP_DIR/extra_includes + mkdir -p $EXTRA_INCLUDES_BASE + + # x86 needs /usr/include/i386-linux-gnu/asm pointed to /usr/include/x86_64-linux-gnu/asm, + # but we can't write there. Instead, create a link here and force it to be included in the + # search paths by wrapping gcc/g++. + + mkdir -p $EXTRA_INCLUDES_BASE/i686-pc-linux-gnu + rm -f $WRAP_DIR/extra_includes/i686-pc-linux-gnu/asm + ln -s /usr/include/x86_64-linux-gnu/asm $EXTRA_INCLUDES_BASE/i686-pc-linux-gnu/asm + + for prog in gcc g++; do + rm -f ${WRAP_DIR}/${prog} + cat << EOF > ${WRAP_DIR}/${prog} + #!/usr/bin/env bash + REAL="`which -a ${prog}-7 | grep -v ${WRAP_DIR}/${prog} | head -1`" + for var in "\$@" + do + if [ "\$var" = "-m32" ]; then + export C_INCLUDE_PATH="$EXTRA_INCLUDES_BASE/i686-pc-linux-gnu" + export CPLUS_INCLUDE_PATH="$EXTRA_INCLUDES_BASE/i686-pc-linux-gnu" + break + fi + done + \$REAL \$@ + EOF + chmod +x ${WRAP_DIR}/${prog} + done + + cd monero + BASEPREFIX=`pwd`/contrib/depends + # Build dependencies for each host + for i in $HOSTS; do + EXTRA_INCLUDES="$EXTRA_INCLUDES_BASE/$i" + if [ -d "$EXTRA_INCLUDES" ]; then + export HOST_ID_SALT="$EXTRA_INCLUDES" + fi + make ${MAKEOPTS} -C ${BASEPREFIX} HOST="${i}" -j 4 V=1 + unset HOST_ID_SALT + done + + # Faketime for binaries + export PATH=${PATH_orig} + create_global_faketime_wrappers "${REFERENCE_DATETIME}" + create_per-host_faketime_wrappers "${REFERENCE_DATETIME}" + export PATH=${WRAP_DIR}:${PATH} + + ORIGPATH="$PATH" + # Extract the release tarball into a dir for each host and build + for i in ${HOSTS}; do + export PATH=${BASEPREFIX}/${i}/native/bin:${ORIGPATH} + mkdir build && cd build + cmake .. -DCMAKE_TOOLCHAIN_FILE=${BASEPREFIX}/${i}/share/toolchain.cmake + make + DISTNAME=monero-${i}.tar.gz + tar -cvzf $DISTNAME bin/* + cp $DISTNAME $OUTDIR/ + cd .. + rm -rf build + done diff --git a/contrib/gitian/symbol-check.py b/contrib/gitian/symbol-check.py new file mode 100755 index 00000000000..6808e77da73 --- /dev/null +++ b/contrib/gitian/symbol-check.py @@ -0,0 +1,163 @@ +#!/usr/bin/env python3 +# 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. +''' +A script to check that the (Linux) executables produced by gitian only contain +allowed gcc, glibc and libstdc++ version symbols. This makes sure they are +still compatible with the minimum supported Linux distribution versions. + +Example usage: + + find ../gitian-builder/build -type f -executable | xargs python contrib/devtools/symbol-check.py +''' +import subprocess +import re +import sys +import os + +# Debian 6.0.9 (Squeeze) has: +# +# - g++ version 4.4.5 (https://packages.debian.org/search?suite=default§ion=all&arch=any&searchon=names&keywords=g%2B%2B) +# - libc version 2.11.3 (https://packages.debian.org/search?suite=default§ion=all&arch=any&searchon=names&keywords=libc6) +# - libstdc++ version 4.4.5 (https://packages.debian.org/search?suite=default§ion=all&arch=any&searchon=names&keywords=libstdc%2B%2B6) +# +# Ubuntu 10.04.4 (Lucid Lynx) has: +# +# - g++ version 4.4.3 (http://packages.ubuntu.com/search?keywords=g%2B%2B&searchon=names&suite=lucid§ion=all) +# - libc version 2.11.1 (http://packages.ubuntu.com/search?keywords=libc6&searchon=names&suite=lucid§ion=all) +# - libstdc++ version 4.4.3 (http://packages.ubuntu.com/search?suite=lucid§ion=all&arch=any&keywords=libstdc%2B%2B&searchon=names) +# +# Taking the minimum of these as our target. +# +# According to GNU ABI document (http://gcc.gnu.org/onlinedocs/libstdc++/manual/abi.html) this corresponds to: +# GCC 4.4.0: GCC_4.4.0 +# GCC 4.4.2: GLIBCXX_3.4.13, CXXABI_1.3.3 +# (glibc) GLIBC_2_11 +# +MAX_VERSIONS = { +'GCC': (4,4,0), +'CXXABI': (1,3,3), +'GLIBCXX': (3,4,13), +'GLIBC': (2,11) +} +# See here for a description of _IO_stdin_used: +# https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=634261#109 + +# Ignore symbols that are exported as part of every executable +IGNORE_EXPORTS = { +'_edata', '_end', '_init', '__bss_start', '_fini', '_IO_stdin_used', 'stdin', 'stdout', 'stderr' +} +READELF_CMD = os.getenv('READELF', '/usr/bin/readelf') +CPPFILT_CMD = os.getenv('CPPFILT', '/usr/bin/c++filt') +# Allowed NEEDED libraries +ALLOWED_LIBRARIES = { +# bitcoind and bitcoin-qt +'libgcc_s.so.1', # GCC base support +'libc.so.6', # C library +'libpthread.so.0', # threading +'libanl.so.1', # DNS resolve +'libm.so.6', # math library +'librt.so.1', # real-time (clock) +'ld-linux-x86-64.so.2', # 64-bit dynamic linker +'ld-linux.so.2', # 32-bit dynamic linker +# bitcoin-qt only +'libX11-xcb.so.1', # part of X11 +'libX11.so.6', # part of X11 +'libxcb.so.1', # part of X11 +'libfontconfig.so.1', # font support +'libfreetype.so.6', # font parsing +'libdl.so.2' # programming interface to dynamic linker +} + +class CPPFilt(object): + ''' + Demangle C++ symbol names. + + Use a pipe to the 'c++filt' command. + ''' + def __init__(self): + self.proc = subprocess.Popen(CPPFILT_CMD, stdin=subprocess.PIPE, stdout=subprocess.PIPE, universal_newlines=True) + + def __call__(self, mangled): + self.proc.stdin.write(mangled + '\n') + self.proc.stdin.flush() + return self.proc.stdout.readline().rstrip() + + def close(self): + self.proc.stdin.close() + self.proc.stdout.close() + self.proc.wait() + +def read_symbols(executable, imports=True): + ''' + Parse an ELF executable and return a list of (symbol,version) tuples + for dynamic, imported symbols. + ''' + p = subprocess.Popen([READELF_CMD, '--dyn-syms', '-W', executable], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE, universal_newlines=True) + (stdout, stderr) = p.communicate() + if p.returncode: + raise IOError('Could not read symbols for %s: %s' % (executable, stderr.strip())) + syms = [] + for line in stdout.splitlines(): + line = line.split() + if len(line)>7 and re.match('[0-9]+:$', line[0]): + (sym, _, version) = line[7].partition('@') + is_import = line[6] == 'UND' + if version.startswith('@'): + version = version[1:] + if is_import == imports: + syms.append((sym, version)) + return syms + +def check_version(max_versions, version): + if '_' in version: + (lib, _, ver) = version.rpartition('_') + else: + lib = version + ver = '0' + ver = tuple([int(x) for x in ver.split('.')]) + if not lib in max_versions: + return False + return ver <= max_versions[lib] + +def read_libraries(filename): + p = subprocess.Popen([READELF_CMD, '-d', '-W', filename], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE, universal_newlines=True) + (stdout, stderr) = p.communicate() + if p.returncode: + raise IOError('Error opening file') + libraries = [] + for line in stdout.splitlines(): + tokens = line.split() + if len(tokens)>2 and tokens[1] == '(NEEDED)': + match = re.match('^Shared library: \[(.*)\]$', ' '.join(tokens[2:])) + if match: + libraries.append(match.group(1)) + else: + raise ValueError('Unparseable (NEEDED) specification') + return libraries + +if __name__ == '__main__': + cppfilt = CPPFilt() + retval = 0 + for filename in sys.argv[1:]: + # Check imported symbols + for sym,version in read_symbols(filename, True): + if version and not check_version(MAX_VERSIONS, version): + print('%s: symbol %s from unsupported version %s' % (filename, cppfilt(sym), version)) + retval = 1 + # Check exported symbols + for sym,version in read_symbols(filename, False): + if sym in IGNORE_EXPORTS: + continue + print('%s: export of symbol %s not allowed' % (filename, cppfilt(sym))) + retval = 1 + # Check dependency libraries + for library_name in read_libraries(filename): + if library_name not in ALLOWED_LIBRARIES: + print('%s: NEEDED library %s is not allowed' % (filename, library_name)) + retval = 1 + + sys.exit(retval) + + From c95a14088c9105e9be793889128425981e2a3bd1 Mon Sep 17 00:00:00 2001 From: erciccione Date: Mon, 8 Oct 2018 18:33:16 +0100 Subject: [PATCH 0548/1404] CMakeLists.txt: Fix typo --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ade7a290de3..ae125100746 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -186,7 +186,7 @@ if(NOT MANUAL_SUBMODULES) string(COMPARE EQUAL "${unboundLocalHead}" "${unboundCheckedHead}" unboundUpToDate) string(COMPARE EQUAL "${rapidjsonLocalHead}" "${rapidjsonCheckedHead}" rapidjsonUpToDate) if (NOT miniupnpUpToDate OR NOT unboundUpToDate OR NOT rapidjsonUpToDate) - message(FATAL_ERROR "Submodules not up to date. Please update with git subdmoule init && git submodule update, or run cmake with -DMANUAL_SUBMODULES=1") + message(FATAL_ERROR "Submodules not up to date. Please update with git submodule init && git submodule update, or run cmake with -DMANUAL_SUBMODULES=1") endif() endif() endif() From 77471e23bd20fa90249adb2ba735467c02ebb837 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 8 Oct 2018 17:36:51 +0000 Subject: [PATCH 0549/1404] blockchain_blackball: fix stray ! --- src/blockchain_utilities/blockchain_blackball.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/blockchain_utilities/blockchain_blackball.cpp b/src/blockchain_utilities/blockchain_blackball.cpp index 00baad7f08e..03ff3cdcd8e 100644 --- a/src/blockchain_utilities/blockchain_blackball.cpp +++ b/src/blockchain_utilities/blockchain_blackball.cpp @@ -1233,7 +1233,7 @@ int main(int argc, char* argv[]) std::cout << "\r" << start_idx << "/" << n_txes << " \r" << std::flush; } blackballs.push_back(output); - if (!add_spent_output(cur, output_data(txin.amount, absolute[o]))) + if (add_spent_output(cur, output_data(txin.amount, absolute[o]))) inc_stat(txn, txin.amount ? "pre-rct-duplicate-rings" : "rct-duplicate-rings"); } } From 8f22e808650a0d599913f608441515f9d985cced Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 8 Oct 2018 17:12:35 +0000 Subject: [PATCH 0550/1404] device: destroy device objects on exit --- src/device/device.cpp | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/src/device/device.cpp b/src/device/device.cpp index 50041baef57..d5e3031ffcb 100644 --- a/src/device/device.cpp +++ b/src/device/device.cpp @@ -41,13 +41,26 @@ namespace hw { /* SETUP */ /* ======================================================================= */ - static std::unique_ptr registry; + static device_registry *get_device_registry(bool clear = false){ + static device_registry *registry = new device_registry(); + if (clear) + { + delete registry; + registry = NULL; + } + return registry; + } + + static void clear_device_registry(){ + get_device_registry(true); + } device_registry::device_registry(){ hw::core::register_all(registry); #ifdef WITH_DEVICE_LEDGER hw::ledger::register_all(registry); #endif + atexit(clear_device_registry); } bool device_registry::register_device(const std::string & device_name, device * hw_device){ @@ -80,18 +93,12 @@ namespace hw { } device& get_device(const std::string & device_descriptor) { - if (!registry){ - registry.reset(new device_registry()); - } - + device_registry *registry = get_device_registry(); return registry->get_device(device_descriptor); } bool register_device(const std::string & device_name, device * hw_device){ - if (!registry){ - registry.reset(new device_registry()); - } - + device_registry *registry = get_device_registry(); return registry->register_device(device_name, hw_device); } From ac5674524a485c069800912f79b1b44840e391fc Mon Sep 17 00:00:00 2001 From: Riccardo Spagni Date: Mon, 8 Oct 2018 21:39:54 +0200 Subject: [PATCH 0551/1404] Revert "Merge pull request #4472" This reverts commit 79d46c4d551a9b1261801960095bf4d24967211a, reversing changes made to c9fc61dbb56cca442c775faa2554a7460879b637. --- CMakeLists.txt | 5 +- src/blockchain_utilities/CMakeLists.txt | 20 ++++- .../blockchain_import.cpp | 8 +- src/blocks/CMakeLists.txt | 35 ++++---- src/blocks/blockexports.c | 87 +++++++++++++++++++ src/blocks/blocks.cpp | 31 ------- src/blocks/blocks.dat | 0 src/blocks/blocks.h | 14 +-- src/cryptonote_config.h | 1 - src/cryptonote_core/CMakeLists.txt | 7 ++ src/cryptonote_core/blockchain.cpp | 29 ++++--- src/cryptonote_core/blockchain.h | 18 +--- src/cryptonote_core/cryptonote_core.cpp | 4 +- src/cryptonote_core/cryptonote_core.h | 3 +- src/daemon/CMakeLists.txt | 21 ++++- src/daemon/core.h | 8 +- src/device/CMakeLists.txt | 7 ++ 17 files changed, 185 insertions(+), 113 deletions(-) create mode 100644 src/blocks/blockexports.c delete mode 100644 src/blocks/blocks.cpp create mode 100644 src/blocks/blocks.dat diff --git a/CMakeLists.txt b/CMakeLists.txt index ade7a290de3..b890965f66d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -199,9 +199,6 @@ set(PER_BLOCK_CHECKPOINT 1) if(PER_BLOCK_CHECKPOINT) add_definitions("-DPER_BLOCK_CHECKPOINT") - set(Blocks "blocks") -else() - set(Blocks "") endif() list(INSERT CMAKE_MODULE_PATH 0 @@ -677,10 +674,12 @@ else() add_linker_flag_if_supported(-Wl,-z,noexecstack noexecstack_SUPPORTED) if (noexecstack_SUPPORTED) set(LD_SECURITY_FLAGS "${LD_SECURITY_FLAGS} -Wl,-z,noexecstack") + set(LD_RAW_FLAGS ${LD_RAW_FLAGS} -z noexecstack) endif() add_linker_flag_if_supported(-Wl,-z,noexecheap noexecheap_SUPPORTED) if (noexecheap_SUPPORTED) set(LD_SECURITY_FLAGS "${LD_SECURITY_FLAGS} -Wl,-z,noexecheap") + set(LD_RAW_FLAGS ${LD_RAW_FLAGS} -z noexecheap) endif() # some windows linker bits diff --git a/src/blockchain_utilities/CMakeLists.txt b/src/blockchain_utilities/CMakeLists.txt index 873bf552cd2..ecd7b754c91 100644 --- a/src/blockchain_utilities/CMakeLists.txt +++ b/src/blockchain_utilities/CMakeLists.txt @@ -26,6 +26,20 @@ # 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. +set(blocksdat "") +if(PER_BLOCK_CHECKPOINT) + if(APPLE AND DEPENDS) + add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && touch stub.c && ${CMAKE_C_COMPILER} --target=x86_64-apple-darwin11 -o stub.o -c stub.c COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} -r -sectcreate __DATA __blocks_dat ../blocks/checkpoints.dat -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o stub.o && rm -f stub.*) + elseif(APPLE AND NOT DEPENDS) + add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && touch stub.c && ${CMAKE_C_COMPILER} -o stub.o -c stub.c COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} ${LD_RAW_FLAGS} -r -sectcreate __DATA __blocks_dat ../blocks/checkpoints.dat -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o stub.o && rm -f stub.*) + elseif(LINUX_32) + add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && cp ../blocks/checkpoints.dat blocks.dat && ${CMAKE_LINKER} -m elf_i386 ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o blocks.dat && rm -f blocks.dat) + else() + add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && cp ../blocks/checkpoints.dat blocks.dat && ${CMAKE_LINKER} ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o blocks.dat && rm -f blocks.dat) + endif() + set(blocksdat "blocksdat.o") +endif() + set(blockchain_import_sources blockchain_import.cpp bootstrap_file.cpp @@ -105,7 +119,8 @@ monero_private_headers(blockchain_depth monero_add_executable(blockchain_import ${blockchain_import_sources} - ${blockchain_import_private_headers}) + ${blockchain_import_private_headers} + ${blocksdat}) target_link_libraries(blockchain_import PRIVATE @@ -117,8 +132,7 @@ target_link_libraries(blockchain_import ${Boost_SYSTEM_LIBRARY} ${Boost_THREAD_LIBRARY} ${CMAKE_THREAD_LIBS_INIT} - ${EXTRA_LIBRARIES} - ${Blocks}) + ${EXTRA_LIBRARIES}) if(ARCH_WIDTH) target_compile_definitions(blockchain_import diff --git a/src/blockchain_utilities/blockchain_import.cpp b/src/blockchain_utilities/blockchain_import.cpp index 134969dc46e..9ec768d2604 100644 --- a/src/blockchain_utilities/blockchain_import.cpp +++ b/src/blockchain_utilities/blockchain_import.cpp @@ -37,7 +37,6 @@ #include "misc_log_ex.h" #include "bootstrap_file.h" #include "bootstrap_serialization.h" -#include "blocks/blocks.h" #include "cryptonote_basic/cryptonote_format_utils.h" #include "serialization/binary_utils.h" // dump_binary(), parse_binary() #include "serialization/json_utils.h" // dump_json() @@ -759,12 +758,7 @@ int main(int argc, char* argv[]) { core.disable_dns_checkpoints(true); -#if defined(PER_BLOCK_CHECKPOINT) - GetCheckpointsCallback get_checkpoints = blocks::GetCheckpointsData; -#else - GetCheckpointsCallback get_checkpoints = nullptr; -#endif - if (!core.init(vm, nullptr, nullptr, get_checkpoints)) + if (!core.init(vm, NULL)) { std::cerr << "Failed to initialize core" << ENDL; return 1; diff --git a/src/blocks/CMakeLists.txt b/src/blocks/CMakeLists.txt index bedda3b880b..ebb5408cc2c 100644 --- a/src/blocks/CMakeLists.txt +++ b/src/blocks/CMakeLists.txt @@ -26,23 +26,20 @@ # 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. -set(GENERATED_SOURCES "") +if(APPLE) + add_library(blocks STATIC blockexports.c) + set_target_properties(blocks PROPERTIES LINKER_LANGUAGE C) +else() + if(LINUX_32) + add_custom_command(OUTPUT blocks.o MAIN_DEPENDENCY blocks.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} -m elf_i386 ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/blocks.o blocks.dat) + add_custom_command(OUTPUT testnet_blocks.o MAIN_DEPENDENCY testnet_blocks.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} -m elf_i386 ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/testnet_blocks.o testnet_blocks.dat) + add_custom_command(OUTPUT stagenet_blocks.o MAIN_DEPENDENCY stagenet_blocks.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} -m elf_i386 ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/stagenet_blocks.o stagenet_blocks.dat) + else() + add_custom_command(OUTPUT blocks.o MAIN_DEPENDENCY blocks.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/blocks.o blocks.dat) + add_custom_command(OUTPUT testnet_blocks.o MAIN_DEPENDENCY testnet_blocks.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/testnet_blocks.o testnet_blocks.dat) + add_custom_command(OUTPUT stagenet_blocks.o MAIN_DEPENDENCY stagenet_blocks.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/stagenet_blocks.o stagenet_blocks.dat) + endif() + add_library(blocks STATIC blocks.o testnet_blocks.o stagenet_blocks.o blockexports.c) + set_target_properties(blocks PROPERTIES LINKER_LANGUAGE C) +endif() -foreach(BLOB_NAME checkpoints testnet_blocks stagenet_blocks) - set(OUTPUT_C_SOURCE "generated_${BLOB_NAME}.c") - list(APPEND GENERATED_SOURCES ${OUTPUT_C_SOURCE}) - set(INPUT_DAT_FILE "${BLOB_NAME}.dat") - add_custom_command( - OUTPUT ${OUTPUT_C_SOURCE} - MAIN_DEPENDENCY ${INPUT_DAT_FILE} - COMMAND - cd ${CMAKE_CURRENT_BINARY_DIR} && - echo "'#include\t'" > ${OUTPUT_C_SOURCE} && - echo -n "'const\tunsigned\tchar\t${BLOB_NAME}[]={'" >> ${OUTPUT_C_SOURCE} && - od -v -An -w1 -tu1 ${CMAKE_CURRENT_SOURCE_DIR}/${INPUT_DAT_FILE} | sed -e "':a;N;$$!ba;s/\\n/,/g'" >> ${OUTPUT_C_SOURCE} && - echo "'};'" >> ${OUTPUT_C_SOURCE} && - echo "'const\tsize_t\t${BLOB_NAME}_len\t=\tsizeof(${BLOB_NAME});'" >> ${OUTPUT_C_SOURCE} - ) -endforeach() - -add_library(blocks STATIC blocks.cpp ${GENERATED_SOURCES}) diff --git a/src/blocks/blockexports.c b/src/blocks/blockexports.c new file mode 100644 index 00000000000..0154b041351 --- /dev/null +++ b/src/blocks/blockexports.c @@ -0,0 +1,87 @@ +#include + +#if defined(__APPLE__) +#include +#ifdef BUILD_SHARED_LIBS +#if !defined(__LP64__) +const struct mach_header _mh_execute_header; +#else +const struct mach_header_64 _mh_execute_header; +#endif +#else +#if !defined(__LP64__) +extern const struct mach_header _mh_execute_header; +#else +extern const struct mach_header_64 _mh_execute_header; +#endif +#endif + +const unsigned char *get_blocks_dat_start(int testnet, int stagenet) +{ + size_t size; + if (testnet) + return getsectiondata(&_mh_execute_header, "__DATA", "__testnet_blocks_dat", &size); + else if (stagenet) + return getsectiondata(&_mh_execute_header, "__DATA", "__stagenet_blocks_dat", &size); + else + return getsectiondata(&_mh_execute_header, "__DATA", "__blocks_dat", &size); +} + +size_t get_blocks_dat_size(int testnet, int stagenet) +{ + size_t size; + if (testnet) + getsectiondata(&_mh_execute_header, "__DATA", "__testnet_blocks_dat", &size); + else if (stagenet) + getsectiondata(&_mh_execute_header, "__DATA", "__stagenet_blocks_dat", &size); + else + getsectiondata(&_mh_execute_header, "__DATA", "__blocks_dat", &size); + return size; +} + +#else + +#if defined(_WIN32) && !defined(_WIN64) +#define _binary_blocks_start binary_blocks_dat_start +#define _binary_blocks_end binary_blocks_dat_end +#define _binary_testnet_blocks_start binary_testnet_blocks_dat_start +#define _binary_testnet_blocks_end binary_testnet_blocks_dat_end +#define _binary_stagenet_blocks_start binary_stagenet_blocks_dat_start +#define _binary_stagenet_blocks_end binary_stagenet_blocks_dat_end +#else +#define _binary_blocks_start _binary_blocks_dat_start +#define _binary_blocks_end _binary_blocks_dat_end +#define _binary_testnet_blocks_start _binary_testnet_blocks_dat_start +#define _binary_testnet_blocks_end _binary_testnet_blocks_dat_end +#define _binary_stagenet_blocks_start _binary_stagenet_blocks_dat_start +#define _binary_stagenet_blocks_end _binary_stagenet_blocks_dat_end +#endif + +extern const unsigned char _binary_blocks_start[]; +extern const unsigned char _binary_blocks_end[]; +extern const unsigned char _binary_testnet_blocks_start[]; +extern const unsigned char _binary_testnet_blocks_end[]; +extern const unsigned char _binary_stagenet_blocks_start[]; +extern const unsigned char _binary_stagenet_blocks_end[]; + +const unsigned char *get_blocks_dat_start(int testnet, int stagenet) +{ + if (testnet) + return _binary_testnet_blocks_start; + else if (stagenet) + return _binary_stagenet_blocks_start; + else + return _binary_blocks_start; +} + +size_t get_blocks_dat_size(int testnet, int stagenet) +{ + if (testnet) + return (size_t) (_binary_testnet_blocks_end - _binary_testnet_blocks_start); + else if (stagenet) + return (size_t) (_binary_stagenet_blocks_end - _binary_stagenet_blocks_start); + else + return (size_t) (_binary_blocks_end - _binary_blocks_start); +} + +#endif diff --git a/src/blocks/blocks.cpp b/src/blocks/blocks.cpp deleted file mode 100644 index 0661f8448bd..00000000000 --- a/src/blocks/blocks.cpp +++ /dev/null @@ -1,31 +0,0 @@ -#include "blocks.h" - -#include - -extern const unsigned char checkpoints[]; -extern const size_t checkpoints_len; -extern const unsigned char stagenet_blocks[]; -extern const size_t stagenet_blocks_len; -extern const unsigned char testnet_blocks[]; -extern const size_t testnet_blocks_len; - -namespace blocks -{ - - const std::unordered_map, std::hash> CheckpointsByNetwork = { - {cryptonote::network_type::MAINNET, {checkpoints, checkpoints_len}}, - {cryptonote::network_type::STAGENET, {stagenet_blocks, stagenet_blocks_len}}, - {cryptonote::network_type::TESTNET, {testnet_blocks, testnet_blocks_len}} - }; - - const epee::span GetCheckpointsData(cryptonote::network_type network) - { - const auto it = CheckpointsByNetwork.find(network); - if (it != CheckpointsByNetwork.end()) - { - return it->second; - } - return nullptr; - } - -} diff --git a/src/blocks/blocks.dat b/src/blocks/blocks.dat new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/blocks/blocks.h b/src/blocks/blocks.h index 14e391319f6..ec683f47ef6 100644 --- a/src/blocks/blocks.h +++ b/src/blocks/blocks.h @@ -1,12 +1,16 @@ #ifndef SRC_BLOCKS_BLOCKS_H_ #define SRC_BLOCKS_BLOCKS_H_ -#include "cryptonote_config.h" -#include "span.h" +#ifdef __cplusplus +extern "C" { +#endif -namespace blocks -{ - const epee::span GetCheckpointsData(cryptonote::network_type network); +const unsigned char *get_blocks_dat_start(int testnet, int stagenet); +size_t get_blocks_dat_size(int testnet, int stagenet); + +#ifdef __cplusplus } +#endif + #endif /* SRC_BLOCKS_BLOCKS_H_ */ diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index c62eeb73828..a6858ce7cce 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -30,7 +30,6 @@ #pragma once -#include #include #include diff --git a/src/cryptonote_core/CMakeLists.txt b/src/cryptonote_core/CMakeLists.txt index 231489fdbc5..72844db66e0 100644 --- a/src/cryptonote_core/CMakeLists.txt +++ b/src/cryptonote_core/CMakeLists.txt @@ -41,6 +41,12 @@ set(cryptonote_core_private_headers tx_pool.h cryptonote_tx_utils.h) +if(PER_BLOCK_CHECKPOINT) + set(Blocks "blocks") +else() + set(Blocks "") +endif() + monero_private_headers(cryptonote_core ${cryptonote_core_private_headers}) monero_add_library(cryptonote_core @@ -63,4 +69,5 @@ target_link_libraries(cryptonote_core ${Boost_SYSTEM_LIBRARY} ${Boost_THREAD_LIBRARY} PRIVATE + ${Blocks} ${EXTRA_LIBRARIES}) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 6c6e024e4ad..eb869b79561 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -53,6 +53,9 @@ #include "ringct/rctSigs.h" #include "common/perf_timer.h" #include "common/notify.h" +#if defined(PER_BLOCK_CHECKPOINT) +#include "blocks/blocks.h" +#endif #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "blockchain" @@ -338,7 +341,7 @@ uint64_t Blockchain::get_current_blockchain_height() const //------------------------------------------------------------------ //FIXME: possibly move this into the constructor, to avoid accidentally // dereferencing a null BlockchainDB pointer -bool Blockchain::init(BlockchainDB* db, const network_type nettype, bool offline, const cryptonote::test_options *test_options, difficulty_type fixed_difficulty, const GetCheckpointsCallback get_checkpoints/* = nullptr*/) +bool Blockchain::init(BlockchainDB* db, const network_type nettype, bool offline, const cryptonote::test_options *test_options, difficulty_type fixed_difficulty) { LOG_PRINT_L3("Blockchain::" << __func__); CRITICAL_REGION_LOCAL(m_tx_pool); @@ -436,7 +439,7 @@ bool Blockchain::init(BlockchainDB* db, const network_type nettype, bool offline #if defined(PER_BLOCK_CHECKPOINT) if (m_nettype != FAKECHAIN) - load_compiled_in_block_hashes(get_checkpoints); + load_compiled_in_block_hashes(); #endif MINFO("Blockchain initialized. last block: " << m_db->height() - 1 << ", " << epee::misc_utils::get_time_interval_string(timestamp_diff) << " time ago, current difficulty: " << get_difficulty_for_next_block()); @@ -4401,21 +4404,19 @@ void Blockchain::cancel() #if defined(PER_BLOCK_CHECKPOINT) static const char expected_block_hashes_hash[] = "954cb2bbfa2fe6f74b2cdd22a1a4c767aea249ad47ad4f7c9445f0f03260f511"; -void Blockchain::load_compiled_in_block_hashes(const GetCheckpointsCallback get_checkpoints) +void Blockchain::load_compiled_in_block_hashes() { - if (get_checkpoints == nullptr || !m_fast_sync) - { - return; - } - const epee::span checkpoints = get_checkpoints(m_nettype); - if (!checkpoints.empty()) + const bool testnet = m_nettype == TESTNET; + const bool stagenet = m_nettype == STAGENET; + if (m_fast_sync && get_blocks_dat_start(testnet, stagenet) != nullptr && get_blocks_dat_size(testnet, stagenet) > 0) { - MINFO("Loading precomputed blocks (" << checkpoints.size() << " bytes)"); + MINFO("Loading precomputed blocks (" << get_blocks_dat_size(testnet, stagenet) << " bytes)"); + if (m_nettype == MAINNET) { // first check hash crypto::hash hash; - if (!tools::sha256sum(checkpoints.data(), checkpoints.size(), hash)) + if (!tools::sha256sum(get_blocks_dat_start(testnet, stagenet), get_blocks_dat_size(testnet, stagenet), hash)) { MERROR("Failed to hash precomputed blocks data"); return; @@ -4435,9 +4436,9 @@ void Blockchain::load_compiled_in_block_hashes(const GetCheckpointsCallback get_ } } - if (checkpoints.size() > 4) + if (get_blocks_dat_size(testnet, stagenet) > 4) { - const unsigned char *p = checkpoints.data(); + const unsigned char *p = get_blocks_dat_start(testnet, stagenet); const uint32_t nblocks = *p | ((*(p+1))<<8) | ((*(p+2))<<16) | ((*(p+3))<<24); if (nblocks > (std::numeric_limits::max() - 4) / sizeof(hash)) { @@ -4445,7 +4446,7 @@ void Blockchain::load_compiled_in_block_hashes(const GetCheckpointsCallback get_ return; } const size_t size_needed = 4 + nblocks * sizeof(crypto::hash); - if(nblocks > 0 && nblocks > (m_db->height() + HASH_OF_HASHES_STEP - 1) / HASH_OF_HASHES_STEP && checkpoints.size() >= size_needed) + if(nblocks > 0 && nblocks > (m_db->height() + HASH_OF_HASHES_STEP - 1) / HASH_OF_HASHES_STEP && get_blocks_dat_size(testnet, stagenet) >= size_needed) { p += sizeof(uint32_t); m_blocks_hash_of_hashes.reserve(nblocks); diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index 9b67765b5ab..ab66fac8b8e 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -38,11 +38,9 @@ #include #include #include -#include #include #include -#include "span.h" #include "syncobj.h" #include "string_tools.h" #include "cryptonote_basic/cryptonote_basic.h" @@ -75,15 +73,6 @@ namespace cryptonote db_nosync //!< Leave syncing up to the backing db (safest, but slowest because of disk I/O) }; - /** - * @brief Callback routine that returns checkpoints data for specific network type - * - * @param network network type - * - * @return checkpoints data, empty span if there ain't any checkpoints for specific network type - */ - typedef std::function(cryptonote::network_type network)> GetCheckpointsCallback; - /************************************************************************/ /* */ /************************************************************************/ @@ -128,11 +117,10 @@ namespace cryptonote * @param offline true if running offline, else false * @param test_options test parameters * @param fixed_difficulty fixed difficulty for testing purposes; 0 means disabled - * @param get_checkpoints if set, will be called to get checkpoints data * * @return true on success, false if any initialization steps fail */ - bool init(BlockchainDB* db, const network_type nettype = MAINNET, bool offline = false, const cryptonote::test_options *test_options = NULL, difficulty_type fixed_difficulty = 0, const GetCheckpointsCallback get_checkpoints = nullptr); + bool init(BlockchainDB* db, const network_type nettype = MAINNET, bool offline = false, const cryptonote::test_options *test_options = NULL, difficulty_type fixed_difficulty = 0); /** * @brief Initialize the Blockchain state @@ -1381,10 +1369,8 @@ namespace cryptonote * A (possibly empty) set of block hashes can be compiled into the * monero daemon binary. This function loads those hashes into * a useful state. - * - * @param get_checkpoints if set, will be called to get checkpoints data */ - void load_compiled_in_block_hashes(const GetCheckpointsCallback get_checkpoints); + void load_compiled_in_block_hashes(); /** * @brief expands v2 transaction data from blockchain diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 3882a14ae62..69e3c708b7a 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -389,7 +389,7 @@ namespace cryptonote return m_blockchain_storage.get_alternative_blocks_count(); } //----------------------------------------------------------------------------------------------- - bool core::init(const boost::program_options::variables_map& vm, const char *config_subdir, const cryptonote::test_options *test_options, const GetCheckpointsCallback get_checkpoints/* = nullptr */) + bool core::init(const boost::program_options::variables_map& vm, const char *config_subdir, const cryptonote::test_options *test_options) { start_time = std::time(nullptr); @@ -567,7 +567,7 @@ namespace cryptonote regtest_hard_forks }; const difficulty_type fixed_difficulty = command_line::get_arg(vm, arg_fixed_difficulty); - r = m_blockchain_storage.init(db.release(), m_nettype, m_offline, regtest ? ®test_test_options : test_options, fixed_difficulty, get_checkpoints); + r = m_blockchain_storage.init(db.release(), m_nettype, m_offline, regtest ? ®test_test_options : test_options, fixed_difficulty); r = m_mempool.init(max_txpool_weight); CHECK_AND_ASSERT_MES(r, false, "Failed to initialize memory pool"); diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index 88256833000..58fe5b7b56e 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -244,11 +244,10 @@ namespace cryptonote * @param vm command line parameters * @param config_subdir subdirectory for config storage * @param test_options configuration options for testing - * @param get_checkpoints if set, will be called to get checkpoints data, must return checkpoints data pointer and size or nullptr if there ain't any checkpoints for specific network type * * @return false if one of the init steps fails, otherwise true */ - bool init(const boost::program_options::variables_map& vm, const char *config_subdir = NULL, const test_options *test_options = NULL, const GetCheckpointsCallback get_checkpoints = nullptr); + bool init(const boost::program_options::variables_map& vm, const char *config_subdir = NULL, const test_options *test_options = NULL); /** * @copydoc Blockchain::reset_and_set_genesis_block diff --git a/src/daemon/CMakeLists.txt b/src/daemon/CMakeLists.txt index 1177904557d..f645836a4b2 100644 --- a/src/daemon/CMakeLists.txt +++ b/src/daemon/CMakeLists.txt @@ -26,6 +26,20 @@ # 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. +set(blocksdat "") +if(PER_BLOCK_CHECKPOINT) + if(APPLE AND DEPENDS) + add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && touch stub.c && ${CMAKE_C_COMPILER} --target=x86_64-apple-darwin11 -o stub.o -c stub.c COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} -r -sectcreate __DATA __blocks_dat ../blocks/checkpoints.dat -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o stub.o && rm -f stub.*) + elseif(APPLE AND NOT DEPENDS) + add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && touch stub.c && ${CMAKE_C_COMPILER} -o stub.o -c stub.c COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} ${LD_RAW_FLAGS} -r -sectcreate __DATA __blocks_dat ../blocks/checkpoints.dat -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o stub.o && rm -f stub.*) + elseif(LINUX_32) + add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && cp ../blocks/checkpoints.dat blocks.dat && ${CMAKE_LINKER} -m elf_i386 ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o blocks.dat && rm -f blocks.dat) + else() + add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && cp ../blocks/checkpoints.dat blocks.dat && ${CMAKE_LINKER} ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o blocks.dat && rm -f blocks.dat) + endif() + set(blocksdat "blocksdat.o") +endif() + set(daemon_sources command_parser_executor.cpp command_server.cpp @@ -67,7 +81,9 @@ monero_private_headers(daemon monero_add_executable(daemon ${daemon_sources} ${daemon_headers} - ${daemon_private_headers}) + ${daemon_private_headers} + ${blocksdat} +) target_link_libraries(daemon PRIVATE rpc @@ -90,8 +106,7 @@ target_link_libraries(daemon ${CMAKE_THREAD_LIBS_INIT} ${ZMQ_LIB} ${GNU_READLINE_LIBRARY} - ${EXTRA_LIBRARIES} - ${Blocks}) + ${EXTRA_LIBRARIES}) set_property(TARGET daemon PROPERTY OUTPUT_NAME "monerod") diff --git a/src/daemon/core.h b/src/daemon/core.h index eda8894a6fc..475f418d650 100644 --- a/src/daemon/core.h +++ b/src/daemon/core.h @@ -28,7 +28,6 @@ #pragma once -#include "blocks/blocks.h" #include "cryptonote_core/cryptonote_core.h" #include "cryptonote_protocol/cryptonote_protocol_handler.h" #include "misc_log_ex.h" @@ -86,12 +85,7 @@ class t_core final //initialize core here MGINFO("Initializing core..."); std::string config_subdir = get_config_subdir(); -#if defined(PER_BLOCK_CHECKPOINT) - cryptonote::GetCheckpointsCallback get_checkpoints = blocks::GetCheckpointsData; -#else - cryptonote::GetCheckpointsCallback get_checkpoints = nullptr; -#endif - if (!m_core.init(m_vm_HACK, config_subdir.empty() ? NULL : config_subdir.c_str(), nullptr, get_checkpoints)) + if (!m_core.init(m_vm_HACK, config_subdir.empty() ? NULL : config_subdir.c_str())) { return false; } diff --git a/src/device/CMakeLists.txt b/src/device/CMakeLists.txt index 727134f75e0..8f446f42a24 100644 --- a/src/device/CMakeLists.txt +++ b/src/device/CMakeLists.txt @@ -58,6 +58,12 @@ endif() set(device_private_headers) +if(PER_BLOCK_CHECKPOINT) + set(Blocks "blocks") +else() + set(Blocks "") +endif() + monero_private_headers(device ${device_private_headers}) @@ -73,4 +79,5 @@ target_link_libraries(device ringct_basic ${OPENSSL_CRYPTO_LIBRARIES} PRIVATE + ${Blocks} ${EXTRA_LIBRARIES}) From f2127f9dca29c8b1e0a51bebeee5e8e49e6f4876 Mon Sep 17 00:00:00 2001 From: TheCharlatan Date: Mon, 8 Oct 2018 23:14:46 +0200 Subject: [PATCH 0552/1404] Add checksums for download tools The signature prepare tool and the gitian-builder git repo should be checked for their content. For this purpose, checkout the gitian-builder repo at a specific commit and take the sha256sum of the osslsigncode tool. --- contrib/gitian/gitian-build.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/contrib/gitian/gitian-build.py b/contrib/gitian/gitian-build.py index bd7e70a715f..2f4c93625d4 100755 --- a/contrib/gitian/gitian-build.py +++ b/contrib/gitian/gitian-build.py @@ -24,13 +24,12 @@ def setup(): subprocess.check_call(['sudo', 'apt-get', 'install', '-qq'] + programs) if not os.path.isdir('gitian.sigs'): subprocess.check_call(['git', 'clone', 'https://github.com/bitcoin-core/gitian.sigs.git']) - if not os.path.isdir('monero-detached-sigs'): - subprocess.check_call(['git', 'clone', 'https://github.com/bitcoin-core/bitcoin-detached-sigs.git']) if not os.path.isdir('gitian-builder'): subprocess.check_call(['git', 'clone', 'https://github.com/devrandom/gitian-builder.git']) if not os.path.isdir('monero'): - subprocess.check_call(['git', 'clone', 'https://github.com/bitcoin-core/monero.git']) + subprocess.check_call(['git', 'clone', 'https://github.com/monero-project/monero.git']) os.chdir('gitian-builder') + subprocess.check_call(['git', 'checkout', '963322de8420c50502c4cc33d4d7c0d84437b576']) make_image_prog = ['bin/make-base-vm', '--suite', 'bionic', '--arch', 'amd64'] if args.docker: make_image_prog += ['--docker'] @@ -51,8 +50,10 @@ def build(): os.chdir('gitian-builder') os.makedirs('inputs', exist_ok=True) - subprocess.check_call(['wget', '-N', '-P', 'inputs', 'http://downloads.sourceforge.net/project/osslsigncode/osslsigncode/osslsigncode-1.7.1.tar.gz']) + subprocess.check_call(['wget', '-N', '-P', 'inputs', 'https://downloads.sourceforge.net/project/osslsigncode/osslsigncode/osslsigncode-1.7.1.tar.gz']) subprocess.check_call(['wget', '-N', '-P', 'inputs', 'https://bitcoincore.org/cfields/osslsigncode-Backports-to-1.7.1.patch']) + subprocess.check_output(["echo 'a8c4e9cafba922f89de0df1f2152e7be286aba73f78505169bc351a7938dd911 inputs/osslsigncode-Backports-to-1.7.1.patch' | sha256sum -c"], shell=True) + subprocess.check_output(["echo 'f9a8cdb38b9c309326764ebc937cba1523a3a751a7ab05df3ecc99d18ae466c9 inputs/osslsigncode-1.7.1.tar.gz' | sha256sum -c"], shell=True) subprocess.check_call(['make', '-C', '../monero/contrib/depends', 'download', 'SOURCES_PATH=' + os.getcwd() + '/cache/common']) if args.linux: From c716a331f34da4ce501674b33bdc0995cd599122 Mon Sep 17 00:00:00 2001 From: selsta Date: Tue, 9 Oct 2018 02:17:40 +0200 Subject: [PATCH 0553/1404] device: increase ledger timeout to 2 minutes --- src/device/device_ledger.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/device/device_ledger.cpp b/src/device/device_ledger.cpp index 464cb0d9039..a1778496007 100644 --- a/src/device/device_ledger.cpp +++ b/src/device/device_ledger.cpp @@ -176,7 +176,7 @@ namespace hw { #define INS_GET_RESPONSE 0xc0 - device_ledger::device_ledger(): hw_device(0x0101, 0x05, 64, 10000) { + device_ledger::device_ledger(): hw_device(0x0101, 0x05, 64, 120000) { this->id = device_id++; this->reset_buffer(); this->mode = NONE; From bd7b800f0a0851220e033ba5b0c4b8608bfff3e7 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 9 Oct 2018 08:13:58 +0000 Subject: [PATCH 0554/1404] device_io_hid: fix DEFAULT_* type (too short) and init time --- src/device/device_io_hid.hpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/device/device_io_hid.hpp b/src/device/device_io_hid.hpp index 560208c77d7..6fd15a1d112 100644 --- a/src/device/device_io_hid.hpp +++ b/src/device/device_io_hid.hpp @@ -86,13 +86,13 @@ namespace hw { public: bool hid_verbose = false; - const unsigned int OR_SELECT = 1; - const unsigned int AND_SELECT = 2; + static const unsigned int OR_SELECT = 1; + static const unsigned int AND_SELECT = 2; - const unsigned char DEFAULT_CHANNEL = 0x0001; - const unsigned char DEFAULT_TAG = 0x01; - const unsigned int DEFAULT_PACKET_SIZE = 64; - const unsigned int DEFAULT_TIMEOUT = 120000; + static const unsigned short DEFAULT_CHANNEL = 0x0001; + static const unsigned char DEFAULT_TAG = 0x01; + static const unsigned int DEFAULT_PACKET_SIZE = 64; + static const unsigned int DEFAULT_TIMEOUT = 120000; device_io_hid(unsigned short channel, unsigned char tag, unsigned int packet_zize, unsigned int timeout); device_io_hid(); From fed4e59886c3410429f9625caff0298ac7523bcb Mon Sep 17 00:00:00 2001 From: TheCharlatan Date: Tue, 9 Oct 2018 11:12:32 +0200 Subject: [PATCH 0555/1404] Change gitian.sigs repo from bitcoin-core to monero-project remote host --- contrib/gitian/gitian-build.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/gitian/gitian-build.py b/contrib/gitian/gitian-build.py index 2f4c93625d4..0a93dbf5b82 100755 --- a/contrib/gitian/gitian-build.py +++ b/contrib/gitian/gitian-build.py @@ -23,7 +23,7 @@ def setup(): programs += ['lxc', 'debootstrap'] subprocess.check_call(['sudo', 'apt-get', 'install', '-qq'] + programs) if not os.path.isdir('gitian.sigs'): - subprocess.check_call(['git', 'clone', 'https://github.com/bitcoin-core/gitian.sigs.git']) + subprocess.check_call(['git', 'clone', 'https://github.com/monero-project/gitian.sigs.git']) if not os.path.isdir('gitian-builder'): subprocess.check_call(['git', 'clone', 'https://github.com/devrandom/gitian-builder.git']) if not os.path.isdir('monero'): From 977fc1bceb3eae6cdc1226313fc8e447b18fd764 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 9 Oct 2018 08:36:06 +0000 Subject: [PATCH 0556/1404] wallet_rpc_server: add describe_transfer RPC for unsigned tx sets using a view only wallet --- src/wallet/wallet_rpc_server.cpp | 165 +++++++++++++++++++ src/wallet/wallet_rpc_server.h | 2 + src/wallet/wallet_rpc_server_commands_defs.h | 63 ++++++- 3 files changed, 229 insertions(+), 1 deletion(-) diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index e0b631aafb4..aa00157b02d 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -994,6 +994,171 @@ namespace tools return true; } //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::on_describe_transfer(const wallet_rpc::COMMAND_RPC_DESCRIBE_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_DESCRIBE_TRANSFER::response& res, epee::json_rpc::error& er) + { + if (!m_wallet) return not_open(er); + if (m_restricted) + { + er.code = WALLET_RPC_ERROR_CODE_DENIED; + er.message = "Command unavailable in restricted mode."; + return false; + } + if (m_wallet->key_on_device()) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "command not supported by HW wallet"; + return false; + } + if(m_wallet->watch_only()) + { + er.code = WALLET_RPC_ERROR_CODE_WATCH_ONLY; + er.message = "command not supported by watch-only wallet"; + return false; + } + + tools::wallet2::unsigned_tx_set exported_txs; + try + { + cryptonote::blobdata blob; + if (!epee::string_tools::parse_hexstr_to_binbuff(req.unsigned_txset, blob)) + { + er.code = WALLET_RPC_ERROR_CODE_BAD_HEX; + er.message = "Failed to parse hex."; + return false; + } + if(!m_wallet->parse_unsigned_tx_from_str(blob, exported_txs)) + { + er.code = WALLET_RPC_ERROR_CODE_BAD_UNSIGNED_TX_DATA; + er.message = "cannot load unsigned_txset"; + return false; + } + } + catch (const std::exception &e) + { + er.code = WALLET_RPC_ERROR_CODE_BAD_UNSIGNED_TX_DATA; + er.message = "failed to parse unsigned transfers: " + std::string(e.what()); + return false; + } + + std::vector ptx; + try + { + // gather info to ask the user + std::unordered_map> dests; + int first_known_non_zero_change_index = -1; + for (size_t n = 0; n < exported_txs.txes.size(); ++n) + { + const tools::wallet2::tx_construction_data &cd = exported_txs.txes[n]; + res.desc.push_back({0, 0, std::numeric_limits::max(), 0, {}, "", 0, "", 0, 0, ""}); + wallet_rpc::COMMAND_RPC_DESCRIBE_TRANSFER::transfer_description &desc = res.desc.back(); + + std::vector tx_extra_fields; + bool has_encrypted_payment_id = false; + crypto::hash8 payment_id8 = crypto::null_hash8; + if (cryptonote::parse_tx_extra(cd.extra, tx_extra_fields)) + { + cryptonote::tx_extra_nonce extra_nonce; + if (find_tx_extra_field_by_type(tx_extra_fields, extra_nonce)) + { + crypto::hash payment_id; + if(cryptonote::get_encrypted_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id8)) + { + desc.payment_id = epee::string_tools::pod_to_hex(payment_id8); + has_encrypted_payment_id = true; + } + else if (cryptonote::get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id)) + { + desc.payment_id = epee::string_tools::pod_to_hex(payment_id); + } + } + } + + for (size_t s = 0; s < cd.sources.size(); ++s) + { + desc.amount_in += cd.sources[s].amount; + size_t ring_size = cd.sources[s].outputs.size(); + if (ring_size < desc.ring_size) + desc.ring_size = ring_size; + } + for (size_t d = 0; d < cd.splitted_dsts.size(); ++d) + { + const cryptonote::tx_destination_entry &entry = cd.splitted_dsts[d]; + std::string address = cryptonote::get_account_address_as_str(m_wallet->nettype(), entry.is_subaddress, entry.addr); + if (has_encrypted_payment_id && !entry.is_subaddress) + address = cryptonote::get_account_integrated_address_as_str(m_wallet->nettype(), entry.addr, payment_id8); + auto i = dests.find(entry.addr); + if (i == dests.end()) + dests.insert(std::make_pair(entry.addr, std::make_pair(address, entry.amount))); + else + i->second.second += entry.amount; + desc.amount_out += entry.amount; + } + if (cd.change_dts.amount > 0) + { + auto it = dests.find(cd.change_dts.addr); + if (it == dests.end()) + { + er.code = WALLET_RPC_ERROR_CODE_BAD_UNSIGNED_TX_DATA; + er.message = "Claimed change does not go to a paid address"; + return false; + } + if (it->second.second < cd.change_dts.amount) + { + er.code = WALLET_RPC_ERROR_CODE_BAD_UNSIGNED_TX_DATA; + er.message = "Claimed change is larger than payment to the change address"; + return false; + } + if (cd.change_dts.amount > 0) + { + if (first_known_non_zero_change_index == -1) + first_known_non_zero_change_index = n; + const tools::wallet2::tx_construction_data &cdn = exported_txs.txes[first_known_non_zero_change_index]; + if (memcmp(&cd.change_dts.addr, &cdn.change_dts.addr, sizeof(cd.change_dts.addr))) + { + er.code = WALLET_RPC_ERROR_CODE_BAD_UNSIGNED_TX_DATA; + er.message = "Change goes to more than one address"; + return false; + } + } + desc.change_amount += cd.change_dts.amount; + it->second.second -= cd.change_dts.amount; + if (it->second.second == 0) + dests.erase(cd.change_dts.addr); + } + + size_t n_dummy_outputs = 0; + for (auto i = dests.begin(); i != dests.end(); ) + { + if (i->second.second > 0) + { + desc.recipients.push_back({i->second.first, i->second.second}); + } + else + ++desc.dummy_outputs; + ++i; + } + + if (desc.change_amount > 0) + { + const tools::wallet2::tx_construction_data &cd0 = exported_txs.txes[0]; + desc.change_address = get_account_address_as_str(m_wallet->nettype(), cd0.subaddr_account > 0, cd0.change_dts.addr); + } + + desc.fee = desc.amount_in - desc.amount_out; + desc.unlock_time = cd.unlock_time; + desc.extra = epee::to_hex::string({cd.extra.data(), cd.extra.size()}); + } + } + catch (const std::exception &e) + { + er.code = WALLET_RPC_ERROR_CODE_BAD_UNSIGNED_TX_DATA; + er.message = "failed to parse unsigned transfers"; + return false; + } + + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_submit_transfer(const wallet_rpc::COMMAND_RPC_SUBMIT_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_SUBMIT_TRANSFER::response& res, epee::json_rpc::error& er) { if (!m_wallet) return not_open(er); diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h index ab896aa3ba3..69066cfd9a8 100644 --- a/src/wallet/wallet_rpc_server.h +++ b/src/wallet/wallet_rpc_server.h @@ -86,6 +86,7 @@ namespace tools MAP_JON_RPC_WE("transfer", on_transfer, wallet_rpc::COMMAND_RPC_TRANSFER) MAP_JON_RPC_WE("transfer_split", on_transfer_split, wallet_rpc::COMMAND_RPC_TRANSFER_SPLIT) MAP_JON_RPC_WE("sign_transfer", on_sign_transfer, wallet_rpc::COMMAND_RPC_SIGN_TRANSFER) + MAP_JON_RPC_WE("describe_transfer", on_describe_transfer, wallet_rpc::COMMAND_RPC_DESCRIBE_TRANSFER) MAP_JON_RPC_WE("submit_transfer", on_submit_transfer, wallet_rpc::COMMAND_RPC_SUBMIT_TRANSFER) MAP_JON_RPC_WE("sweep_dust", on_sweep_dust, wallet_rpc::COMMAND_RPC_SWEEP_DUST) MAP_JON_RPC_WE("sweep_unmixable", on_sweep_dust, wallet_rpc::COMMAND_RPC_SWEEP_DUST) @@ -166,6 +167,7 @@ namespace tools bool on_transfer(const wallet_rpc::COMMAND_RPC_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_TRANSFER::response& res, epee::json_rpc::error& er); bool on_transfer_split(const wallet_rpc::COMMAND_RPC_TRANSFER_SPLIT::request& req, wallet_rpc::COMMAND_RPC_TRANSFER_SPLIT::response& res, epee::json_rpc::error& er); bool on_sign_transfer(const wallet_rpc::COMMAND_RPC_SIGN_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_SIGN_TRANSFER::response& res, epee::json_rpc::error& er); + bool on_describe_transfer(const wallet_rpc::COMMAND_RPC_DESCRIBE_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_DESCRIBE_TRANSFER::response& res, epee::json_rpc::error& er); bool on_submit_transfer(const wallet_rpc::COMMAND_RPC_SUBMIT_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_SUBMIT_TRANSFER::response& res, epee::json_rpc::error& er); bool on_sweep_dust(const wallet_rpc::COMMAND_RPC_SWEEP_DUST::request& req, wallet_rpc::COMMAND_RPC_SWEEP_DUST::response& res, epee::json_rpc::error& er); bool on_sweep_all(const wallet_rpc::COMMAND_RPC_SWEEP_ALL::request& req, wallet_rpc::COMMAND_RPC_SWEEP_ALL::response& res, epee::json_rpc::error& er); diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h index 2377b69e369..c65c7e9b541 100644 --- a/src/wallet/wallet_rpc_server_commands_defs.h +++ b/src/wallet/wallet_rpc_server_commands_defs.h @@ -47,7 +47,7 @@ // advance which version they will stop working with // Don't go over 32767 for any of these #define WALLET_RPC_VERSION_MAJOR 1 -#define WALLET_RPC_VERSION_MINOR 4 +#define WALLET_RPC_VERSION_MINOR 5 #define MAKE_WALLET_RPC_VERSION(major,minor) (((major)<<16)|(minor)) #define WALLET_RPC_VERSION MAKE_WALLET_RPC_VERSION(WALLET_RPC_VERSION_MAJOR, WALLET_RPC_VERSION_MINOR) namespace tools @@ -531,6 +531,67 @@ namespace wallet_rpc }; }; + struct COMMAND_RPC_DESCRIBE_TRANSFER + { + struct recipient + { + std::string address; + uint64_t amount; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(address) + KV_SERIALIZE(amount) + END_KV_SERIALIZE_MAP() + }; + + struct transfer_description + { + uint64_t amount_in; + uint64_t amount_out; + uint32_t ring_size; + uint64_t unlock_time; + std::list recipients; + std::string payment_id; + uint64_t change_amount; + std::string change_address; + uint64_t fee; + uint32_t dummy_outputs; + std::string extra; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(amount_in) + KV_SERIALIZE(amount_out) + KV_SERIALIZE(ring_size) + KV_SERIALIZE(unlock_time) + KV_SERIALIZE(recipients) + KV_SERIALIZE(payment_id) + KV_SERIALIZE(change_amount) + KV_SERIALIZE(change_address) + KV_SERIALIZE(fee) + KV_SERIALIZE(dummy_outputs) + KV_SERIALIZE(extra) + END_KV_SERIALIZE_MAP() + }; + + struct request + { + std::string unsigned_txset; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(unsigned_txset) + END_KV_SERIALIZE_MAP() + }; + + struct response + { + std::list desc; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(desc) + END_KV_SERIALIZE_MAP() + }; + }; + struct COMMAND_RPC_SIGN_TRANSFER { struct request From 02c2b43a72c941c7eac7d8f8a9ed4b61597a3fdf Mon Sep 17 00:00:00 2001 From: Gregory Lemercier Date: Sun, 7 Oct 2018 19:05:13 +0200 Subject: [PATCH 0557/1404] Utils: Add Dockerfile for android 64-bit build --- Makefile | 9 +- README.md | 6 +- utils/build_scripts/android32.Dockerfile | 2 +- utils/build_scripts/android64.Dockerfile | 142 +++++++++++++++++++++++ 4 files changed, 154 insertions(+), 5 deletions(-) create mode 100644 utils/build_scripts/android64.Dockerfile diff --git a/Makefile b/Makefile index a0bf86c0e90..40b8839ccea 100644 --- a/Makefile +++ b/Makefile @@ -103,10 +103,15 @@ release-static-linux-armv7: mkdir -p $(builddir)/release cd $(builddir)/release && cmake -D BUILD_TESTS=OFF -D ARCH="armv7-a" -D STATIC=ON -D BUILD_64=OFF -D CMAKE_BUILD_TYPE=release -D BUILD_TAG="linux-armv7" $(topdir) && $(MAKE) -release-static-android: +release-static-android-armv7: mkdir -p $(builddir)/release/translations cd $(builddir)/release/translations && cmake ../../../translations && $(MAKE) - cd $(builddir)/release && CC=arm-linux-androideabi-clang CXX=arm-linux-androideabi-clang++ cmake -D BUILD_TESTS=OFF -D ARCH="armv7-a" -D STATIC=ON -D BUILD_64=OFF -D CMAKE_BUILD_TYPE=release -D ANDROID=true -D INSTALL_VENDORED_LIBUNBOUND=ON -D BUILD_TAG="android" -D CMAKE_SYSTEM_NAME="Android" -D CMAKE_ANDROID_STANDALONE_TOOLCHAIN="${ANDROID_STANDALONE_TOOLCHAIN_PATH}" -D CMAKE_ANDROID_ARM_MODE=ON -D CMAKE_ANDROID_ARCH_ABI="armeabi-v7a" ../.. && $(MAKE) + cd $(builddir)/release && CC=arm-linux-androideabi-clang CXX=arm-linux-androideabi-clang++ cmake -D BUILD_TESTS=OFF -D ARCH="armv7-a" -D STATIC=ON -D BUILD_64=OFF -D CMAKE_BUILD_TYPE=release -D ANDROID=true -D INSTALL_VENDORED_LIBUNBOUND=ON -D BUILD_TAG="android-armv7" -D CMAKE_SYSTEM_NAME="Android" -D CMAKE_ANDROID_STANDALONE_TOOLCHAIN="${ANDROID_STANDALONE_TOOLCHAIN_PATH}" -D CMAKE_ANDROID_ARM_MODE=ON -D CMAKE_ANDROID_ARCH_ABI="armeabi-v7a" ../.. && $(MAKE) + +release-static-android-armv8: + mkdir -p $(builddir)/release/translations + cd $(builddir)/release/translations && cmake ../../../translations && $(MAKE) + cd $(builddir)/release && CC=aarch64-linux-android-clang CXX=aarch64-linux-android-clang++ cmake -D BUILD_TESTS=OFF -D ARCH="armv8-a" -D STATIC=ON -D BUILD_64=ON -D CMAKE_BUILD_TYPE=release -D ANDROID=true -D INSTALL_VENDORED_LIBUNBOUND=ON -D BUILD_TAG="android-armv8" -D CMAKE_SYSTEM_NAME="Android" -D CMAKE_ANDROID_STANDALONE_TOOLCHAIN="${ANDROID_STANDALONE_TOOLCHAIN_PATH}" -D CMAKE_ANDROID_ARCH_ABI="arm64-v8a" ../.. && $(MAKE) release-static-linux-armv8: mkdir -p $(builddir)/release diff --git a/README.md b/README.md index bce41f5cfdb..ab33b10dd1b 100644 --- a/README.md +++ b/README.md @@ -472,12 +472,14 @@ Then you can run make as usual. ### On Linux for Android (using docker): - # Build image + # Build image (for ARM 32-bit) docker build -f utils/build_scripts/android32.Dockerfile -t monero-android . + # Build image (for ARM 64-bit) + docker build -f utils/build_scripts/android64.Dockerfile -t monero-android . # Create container docker create -it --name monero-android monero-android bash # Get binaries - docker cp monero-android:/opt/android/monero/build/release/bin . + docker cp monero-android:/src/build/release/bin . ### Building portable statically linked binaries (Cross Compiling) diff --git a/utils/build_scripts/android32.Dockerfile b/utils/build_scripts/android32.Dockerfile index 8647341cb7c..e49bdc65205 100644 --- a/utils/build_scripts/android32.Dockerfile +++ b/utils/build_scripts/android32.Dockerfile @@ -139,4 +139,4 @@ RUN cd /src \ CMAKE_LIBRARY_PATH="${PREFIX}/lib" \ ANDROID_STANDALONE_TOOLCHAIN_PATH=${TOOLCHAIN_DIR} \ USE_SINGLE_BUILDDIR=1 \ - PATH=${HOST_PATH} make release-static-android -j${NPROC} + PATH=${HOST_PATH} make release-static-android-armv7 -j${NPROC} diff --git a/utils/build_scripts/android64.Dockerfile b/utils/build_scripts/android64.Dockerfile new file mode 100644 index 00000000000..c4464fa84ac --- /dev/null +++ b/utils/build_scripts/android64.Dockerfile @@ -0,0 +1,142 @@ +FROM debian:stable + +RUN apt-get update && apt-get install -y unzip automake build-essential curl file pkg-config git python libtool + +WORKDIR /opt/android +## INSTALL ANDROID SDK +ENV ANDROID_SDK_REVISION 4333796 +ENV ANDROID_SDK_HASH 92ffee5a1d98d856634e8b71132e8a95d96c83a63fde1099be3d86df3106def9 +RUN curl -s -O https://dl.google.com/android/repository/sdk-tools-linux-${ANDROID_SDK_REVISION}.zip \ + && echo "${ANDROID_SDK_HASH} sdk-tools-linux-${ANDROID_SDK_REVISION}.zip" | sha256sum -c \ + && unzip sdk-tools-linux-${ANDROID_SDK_REVISION}.zip \ + && rm -f sdk-tools-linux-${ANDROID_SDK_REVISION}.zip + +## INSTALL ANDROID NDK +ENV ANDROID_NDK_REVISION 17b +ENV ANDROID_NDK_HASH 5dfbbdc2d3ba859fed90d0e978af87c71a91a5be1f6e1c40ba697503d48ccecd +RUN curl -s -O https://dl.google.com/android/repository/android-ndk-r${ANDROID_NDK_REVISION}-linux-x86_64.zip \ + && echo "${ANDROID_NDK_HASH} android-ndk-r${ANDROID_NDK_REVISION}-linux-x86_64.zip" | sha256sum -c \ + && unzip android-ndk-r${ANDROID_NDK_REVISION}-linux-x86_64.zip \ + && rm -f android-ndk-r${ANDROID_NDK_REVISION}-linux-x86_64.zip + +ENV WORKDIR /opt/android +ENV ANDROID_SDK_ROOT ${WORKDIR}/tools +ENV ANDROID_NDK_ROOT ${WORKDIR}/android-ndk-r${ANDROID_NDK_REVISION} +ENV PREFIX /opt/android/prefix + +ENV TOOLCHAIN_DIR ${WORKDIR}/toolchain-arm +RUN ${ANDROID_NDK_ROOT}/build/tools/make_standalone_toolchain.py \ + --arch arm64 \ + --api 21 \ + --install-dir ${TOOLCHAIN_DIR} \ + --stl=libc++ + +#INSTALL cmake +ENV CMAKE_VERSION 3.12.1 +RUN cd /usr \ + && curl -s -O https://cmake.org/files/v3.12/cmake-${CMAKE_VERSION}-Linux-x86_64.tar.gz \ + && tar -xzf /usr/cmake-${CMAKE_VERSION}-Linux-x86_64.tar.gz \ + && rm -f /usr/cmake-${CMAKE_VERSION}-Linux-x86_64.tar.gz +ENV PATH /usr/cmake-${CMAKE_VERSION}-Linux-x86_64/bin:$PATH + +## Boost +ARG BOOST_VERSION=1_68_0 +ARG BOOST_VERSION_DOT=1.68.0 +ARG BOOST_HASH=7f6130bc3cf65f56a618888ce9d5ea704fa10b462be126ad053e80e553d6d8b7 +RUN set -ex \ + && curl -s -L -o boost_${BOOST_VERSION}.tar.bz2 https://dl.bintray.com/boostorg/release/${BOOST_VERSION_DOT}/source/boost_${BOOST_VERSION}.tar.bz2 \ + && echo "${BOOST_HASH} boost_${BOOST_VERSION}.tar.bz2" | sha256sum -c \ + && tar -xvf boost_${BOOST_VERSION}.tar.bz2 \ + && rm -f boost_${BOOST_VERSION}.tar.bz2 \ + && cd boost_${BOOST_VERSION} \ + && ./bootstrap.sh --prefix=${PREFIX} + +ENV HOST_PATH $PATH +ENV PATH $TOOLCHAIN_DIR/aarch64-linux-android/bin:$TOOLCHAIN_DIR/bin:$PATH + +ARG NPROC=1 + +# Build iconv for lib boost locale +ENV ICONV_VERSION 1.15 +ENV ICONV_HASH ccf536620a45458d26ba83887a983b96827001e92a13847b45e4925cc8913178 +RUN curl -s -O http://ftp.gnu.org/pub/gnu/libiconv/libiconv-${ICONV_VERSION}.tar.gz \ + && echo "${ICONV_HASH} libiconv-${ICONV_VERSION}.tar.gz" | sha256sum -c \ + && tar -xzf libiconv-${ICONV_VERSION}.tar.gz \ + && rm -f libiconv-${ICONV_VERSION}.tar.gz \ + && cd libiconv-${ICONV_VERSION} \ + && CC=aarch64-linux-android-clang CXX=aarch64-linux-android-clang++ ./configure --build=x86_64-linux-gnu --host=arm-eabi --prefix=${PREFIX} --disable-rpath \ + && make -j${NPROC} && make install + +## Build BOOST +RUN cd boost_${BOOST_VERSION} \ + && ./b2 --build-type=minimal link=static runtime-link=static --with-chrono --with-date_time --with-filesystem --with-program_options --with-regex --with-serialization --with-system --with-thread --with-locale --build-dir=android --stagedir=android toolset=clang threading=multi threadapi=pthread target-os=android -sICONV_PATH=${PREFIX} install -j${NPROC} + +#Note : we build openssl because the default lacks DSA1 + +# download, configure and make Zlib +ENV ZLIB_VERSION 1.2.11 +ENV ZLIB_HASH c3e5e9fdd5004dcb542feda5ee4f0ff0744628baf8ed2dd5d66f8ca1197cb1a1 +RUN curl -s -O https://zlib.net/zlib-${ZLIB_VERSION}.tar.gz \ + && echo "${ZLIB_HASH} zlib-${ZLIB_VERSION}.tar.gz" | sha256sum -c \ + && tar -xzf zlib-${ZLIB_VERSION}.tar.gz \ + && rm zlib-${ZLIB_VERSION}.tar.gz \ + && mv zlib-${ZLIB_VERSION} zlib \ + && cd zlib && CC=clang CXX=clang++ ./configure --static \ + && make -j${NPROC} + +# open ssl +ARG OPENSSL_VERSION=1.0.2p +ARG OPENSSL_HASH=50a98e07b1a89eb8f6a99477f262df71c6fa7bef77df4dc83025a2845c827d00 +RUN curl -s -O https://www.openssl.org/source/openssl-${OPENSSL_VERSION}.tar.gz \ + && echo "${OPENSSL_HASH} openssl-${OPENSSL_VERSION}.tar.gz" | sha256sum -c \ + && tar -xzf openssl-${OPENSSL_VERSION}.tar.gz \ + && rm openssl-${OPENSSL_VERSION}.tar.gz \ + && cd openssl-${OPENSSL_VERSION} \ + && sed -i -e "s/mandroid/target\ aarch64\-linux\-android/" Configure \ + && CC=clang CXX=clang++ \ + ./Configure android \ + no-asm \ + no-shared --static \ + --with-zlib-include=${WORKDIR}/zlib/include --with-zlib-lib=${WORKDIR}/zlib/lib \ + --prefix=${PREFIX} --openssldir=${PREFIX} \ + && make -j${NPROC} \ + && make install + +# ZMQ +ARG ZMQ_VERSION=master +ARG ZMQ_HASH=501d0815bf2b0abb93be8214fc66519918ef6c40 +RUN git clone https://github.com/zeromq/libzmq.git -b ${ZMQ_VERSION} \ + && cd libzmq \ + && git checkout ${ZMQ_HASH} \ + && ./autogen.sh \ + && CC=clang CXX=clang++ ./configure --prefix=${PREFIX} --host=aarch64-linux-android --enable-static --disable-shared \ + && make -j${NPROC} \ + && make install + +# zmq.hpp +ARG CPPZMQ_VERSION=v4.2.3 +ARG CPPZMQ_HASH=6aa3ab686e916cb0e62df7fa7d12e0b13ae9fae6 +RUN git clone https://github.com/zeromq/cppzmq.git -b ${CPPZMQ_VERSION} \ + && cd cppzmq \ + && test `git rev-parse HEAD` = ${CPPZMQ_HASH} || exit 1 \ + && cp *.hpp ${PREFIX}/include + +# Sodium +ARG SODIUM_VERSION=1.0.16 +ARG SODIUM_HASH=675149b9b8b66ff44152553fb3ebf9858128363d +RUN set -ex \ + && git clone https://github.com/jedisct1/libsodium.git -b ${SODIUM_VERSION} \ + && cd libsodium \ + && test `git rev-parse HEAD` = ${SODIUM_HASH} || exit 1 \ + && ./autogen.sh \ + && CC=clang CXX=clang++ ./configure --prefix=${PREFIX} --host=aarch64-linux-android --enable-static --disable-shared \ + && make -j${NPROC} \ + && make install + +ADD . /src +RUN cd /src \ + && CMAKE_INCLUDE_PATH="${PREFIX}/include" \ + CMAKE_LIBRARY_PATH="${PREFIX}/lib" \ + ANDROID_STANDALONE_TOOLCHAIN_PATH=${TOOLCHAIN_DIR} \ + USE_SINGLE_BUILDDIR=1 \ + PATH=${HOST_PATH} make release-static-android-armv8 -j${NPROC} From 8833aec08331131c2eb69a1ac3709837f63a14d8 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 9 Oct 2018 14:31:21 +0000 Subject: [PATCH 0558/1404] wallet2: fix cold signing using non padded bulletproofs This code was deciding which bulletproof configuration to use based on ptx which weren't created yet. --- src/wallet/wallet2.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 7871fe99c9f..f87edf506f8 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -5563,10 +5563,7 @@ bool wallet2::sign_tx(unsigned_tx_set &exported_txs, std::vector 1) - range_proof_type = rct::RangeProofPaddedBulletproof; + range_proof_type = rct::RangeProofPaddedBulletproof; } crypto::secret_key tx_key; std::vector additional_tx_keys; From 93a88d7323ed72dfc08e45b15f996d94d96bb106 Mon Sep 17 00:00:00 2001 From: Gregory Lemercier Date: Tue, 9 Oct 2018 19:17:56 +0200 Subject: [PATCH 0559/1404] Utils: add support for newer Windows versions detection --- src/common/util.cpp | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/src/common/util.cpp b/src/common/util.cpp index 2a1d49af0f1..35e885fa381 100644 --- a/src/common/util.cpp +++ b/src/common/util.cpp @@ -307,10 +307,19 @@ namespace tools StringCchCopy(pszOS, BUFSIZE, TEXT("Microsoft ")); // Test for the specific product. + if ( osvi.dwMajorVersion == 10 ) + { + if ( osvi.dwMinorVersion == 0 ) + { + if( osvi.wProductType == VER_NT_WORKSTATION ) + StringCchCat(pszOS, BUFSIZE, TEXT("Windows 10 ")); + else StringCchCat(pszOS, BUFSIZE, TEXT("Windows Server 2016 " )); + } + } if ( osvi.dwMajorVersion == 6 ) { - if( osvi.dwMinorVersion == 0 ) + if ( osvi.dwMinorVersion == 0 ) { if( osvi.wProductType == VER_NT_WORKSTATION ) StringCchCat(pszOS, BUFSIZE, TEXT("Windows Vista ")); @@ -324,6 +333,20 @@ namespace tools else StringCchCat(pszOS, BUFSIZE, TEXT("Windows Server 2008 R2 " )); } + if ( osvi.dwMinorVersion == 2 ) + { + if( osvi.wProductType == VER_NT_WORKSTATION ) + StringCchCat(pszOS, BUFSIZE, TEXT("Windows 8 ")); + else StringCchCat(pszOS, BUFSIZE, TEXT("Windows Server 2012 " )); + } + + if ( osvi.dwMinorVersion == 3 ) + { + if( osvi.wProductType == VER_NT_WORKSTATION ) + StringCchCat(pszOS, BUFSIZE, TEXT("Windows 8.1 ")); + else StringCchCat(pszOS, BUFSIZE, TEXT("Windows Server 2012 R2 " )); + } + pGPI = (PGPI) GetProcAddress( GetModuleHandle(TEXT("kernel32.dll")), "GetProductInfo"); From d147d24058ef4d4e25ff0603b6bc89f2d4095874 Mon Sep 17 00:00:00 2001 From: TheCharlatan Date: Tue, 9 Oct 2018 22:24:49 +0200 Subject: [PATCH 0560/1404] Add windows descriptor to gitian descriptors Windows is built with a seperate descriptor to handle additional changes that need to be done to the end binary. Consolidate the gitian-build script for this change. --- contrib/gitian/gitian-build.py | 8 +- contrib/gitian/gitian-linux.yml | 6 +- contrib/gitian/gitian-win.yml | 135 ++++++++++++++++++++++++++++++++ 3 files changed, 142 insertions(+), 7 deletions(-) create mode 100644 contrib/gitian/gitian-win.yml diff --git a/contrib/gitian/gitian-build.py b/contrib/gitian/gitian-build.py index 0a93dbf5b82..feb193a3213 100755 --- a/contrib/gitian/gitian-build.py +++ b/contrib/gitian/gitian-build.py @@ -60,20 +60,20 @@ def build(): print('\nCompiling ' + args.version + ' Linux') subprocess.check_call(['bin/gbuild', '-j', args.jobs, '-m', args.memory, '--commit', 'monero='+args.commit, '--url', 'monero='+args.url, '../monero/contrib/gitian/gitian-linux.yml']) subprocess.check_call(['bin/gsign', '-p', args.sign_prog, '--signer', args.signer, '--release', args.version+'-linux', '--destination', '../gitian.sigs/', '../monero/contrib/gitian/gitian-linux.yml']) - subprocess.check_call('mv build/out/monero-*.tar.gz build/out/monero-*.tar.gz ../monero-binaries/'+args.version, shell=True) + subprocess.check_call('mv build/out/monero-*.tar.gz ../monero-binaries/'+args.version, shell=True) if args.windows: print('\nCompiling ' + args.version + ' Windows') subprocess.check_call(['bin/gbuild', '-j', args.jobs, '-m', args.memory, '--commit', 'monero='+args.commit, '--url', 'monero='+args.url, '../monero/contrib/gitian/gitian-win.yml']) subprocess.check_call(['bin/gsign', '-p', args.sign_prog, '--signer', args.signer, '--release', args.version+'-win-unsigned', '--destination', '../gitian.sigs/', '../monero/contrib/gitian/gitian-win.yml']) - subprocess.check_call('mv build/out/monero*-win-unsigned.tar.gz inputs/monerowin-unsigned.tar.gz', shell=True) - subprocess.check_call('mv build/out/monero*.zip build/out/monero*.exe ../monerobinaries/'+args.version, shell=True) + subprocess.check_call('mv build/out/monero*-unsigned.tar.gz inputs/', shell=True) + subprocess.check_call('mv build/out/monero*.zip ../monerobinaries/'+args.version, shell=True) if args.macos: print('\nCompiling ' + args.version + ' MacOS') subprocess.check_call(['bin/gbuild', '-j', args.jobs, '-m', args.memory, '--commit', 'monero'+args.commit, '--url', 'monero'+args.url, '../monero/contrib/gitian/gitian-osx.yml']) subprocess.check_call(['bin/gsign', '-p', args.sign_prog, '--signer', args.signer, '--release', args.version+'-osx-unsigned', '--destination', '../gitian.sigs/', '../monero/contrib/gitian/gitian-osx.yml']) - subprocess.check_call('mv build/out/monero*-osx-unsigned.tar.gz inputs/moneroosx-unsigned.tar.gz', shell=True) + subprocess.check_call('mv build/out/monero*-osx-unsigned.tar.gz inputs/', shell=True) subprocess.check_call('mv build/out/monero*.tar.gz build/out/monero*.dmg ../monerobinaries/'+args.version, shell=True) os.chdir(workdir) diff --git a/contrib/gitian/gitian-linux.yml b/contrib/gitian/gitian-linux.yml index 751fbadd32a..e64e28bbc07 100644 --- a/contrib/gitian/gitian-linux.yml +++ b/contrib/gitian/gitian-linux.yml @@ -154,9 +154,9 @@ script: | mkdir build && cd build cmake .. -DCMAKE_TOOLCHAIN_FILE=${BASEPREFIX}/${i}/share/toolchain.cmake make - DISTNAME=monero-${i}.tar.gz - tar -cvzf $DISTNAME bin/* - cp $DISTNAME $OUTDIR/ + DISTNAME=monero-${i} + mv bin ${DISTNAME} + find ${DISTNAME}/ | sort | tar --no-recursion --mode='u+rw,go+r-w,a+X' --owner=0 --group=0 -c -T - | gzip -9n > ${OUTDIR}/${DISTNAME}.tar.gz cd .. rm -rf build done diff --git a/contrib/gitian/gitian-win.yml b/contrib/gitian/gitian-win.yml new file mode 100644 index 00000000000..7ae8c58f426 --- /dev/null +++ b/contrib/gitian/gitian-win.yml @@ -0,0 +1,135 @@ +--- +name: "bitcoin-win-0.18" +enable_cache: true +suites: +- "bionic" +architectures: +- "amd64" +packages: +- "curl" +- "g++" +- "git" +- "pkg-config" +- "autoconf" +- "libtool" +- "automake" +- "faketime" +- "bsdmainutils" +- "mingw-w64" +- "g++-mingw-w64" +- "zip" +- "ca-certificates" +- "python" +- "rename" +- "cmake" +remotes: +- "url": "https://github.com/monero-project/monero.git" + "dir": "monero" +files: [] +script: | + WRAP_DIR=$HOME/wrapped + HOSTS="i686-w64-mingw32 x86_64-w64-mingw32" + FAKETIME_HOST_PROGS="ar ranlib nm windres strip objcopy" + FAKETIME_PROGS="date zip" + HOST_CFLAGS="-O2 -g" + HOST_CXXFLAGS="-O2 -g" + + export GZIP="-9n" + export TAR_OPTIONS="--mtime="$REFERENCE_DATE\\\ $REFERENCE_TIME"" + export TZ="UTC" + export BUILD_DIR=`pwd` + mkdir -p ${WRAP_DIR} + if test -n "$GBUILD_CACHE_ENABLED"; then + export SOURCES_PATH=${GBUILD_COMMON_CACHE} + export BASE_CACHE=${GBUILD_PACKAGE_CACHE} + mkdir -p ${BASE_CACHE} ${SOURCES_PATH} + fi + + function create_global_faketime_wrappers { + for prog in ${FAKETIME_PROGS}; do + echo '#!/usr/bin/env bash' > ${WRAP_DIR}/${prog} + echo "REAL=\`which -a ${prog} | grep -v ${WRAP_DIR}/${prog} | head -1\`" >> ${WRAP_DIR}/${prog} + echo 'export LD_PRELOAD=/usr/lib/x86_64-linux-gnu/faketime/libfaketime.so.1' >> ${WRAP_DIR}/${prog} + echo "export FAKETIME=\"$1\"" >> ${WRAP_DIR}/${prog} + echo "\$REAL \$@" >> $WRAP_DIR/${prog} + chmod +x ${WRAP_DIR}/${prog} + done + } + + function create_per-host_faketime_wrappers { + for i in $HOSTS; do + for prog in ${FAKETIME_HOST_PROGS}; do + echo '#!/usr/bin/env bash' > ${WRAP_DIR}/${i}-${prog} + echo "REAL=\`which -a ${i}-${prog} | grep -v ${WRAP_DIR}/${i}-${prog} | head -1\`" >> ${WRAP_DIR}/${i}-${prog} + echo 'export LD_PRELOAD=/usr/lib/x86_64-linux-gnu/faketime/libfaketime.so.1' >> ${WRAP_DIR}/${i}-${prog} + echo "export FAKETIME=\"$1\"" >> ${WRAP_DIR}/${i}-${prog} + echo "\$REAL \$@" >> $WRAP_DIR/${i}-${prog} + chmod +x ${WRAP_DIR}/${i}-${prog} + done + done + } + + function create_per-host_linker_wrapper { + # This is only needed for trusty, as the mingw linker leaks a few bytes of + # heap, causing non-determinism. See discussion in https://github.com/bitcoin/bitcoin/pull/6900 + for i in $HOSTS; do + mkdir -p ${WRAP_DIR}/${i} + for prog in collect2; do + echo '#!/usr/bin/env bash' > ${WRAP_DIR}/${i}/${prog} + REAL=$(${i}-gcc -print-prog-name=${prog}) + echo "export MALLOC_PERTURB_=255" >> ${WRAP_DIR}/${i}/${prog} + echo "${REAL} \$@" >> $WRAP_DIR/${i}/${prog} + chmod +x ${WRAP_DIR}/${i}/${prog} + done + for prog in gcc g++; do + echo '#!/usr/bin/env bash' > ${WRAP_DIR}/${i}-${prog} + echo "REAL=\`which -a ${i}-${prog}-posix | grep -v ${WRAP_DIR}/${i}-${prog} | head -1\`" >> ${WRAP_DIR}/${i}-${prog} + echo 'export LD_PRELOAD=/usr/lib/x86_64-linux-gnu/faketime/libfaketime.so.1' >> ${WRAP_DIR}/${i}-${prog} + echo "export FAKETIME=\"$1\"" >> ${WRAP_DIR}/${i}-${prog} + echo "export COMPILER_PATH=${WRAP_DIR}/${i}" >> ${WRAP_DIR}/${i}-${prog} + echo "\$REAL \$@" >> $WRAP_DIR/${i}-${prog} + chmod +x ${WRAP_DIR}/${i}-${prog} + done + done + } + + # Faketime for depends so intermediate results are comparable + export PATH_orig=${PATH} + create_global_faketime_wrappers "2000-01-01 12:00:00" + create_per-host_faketime_wrappers "2000-01-01 12:00:00" + create_per-host_linker_wrapper "2000-01-01 12:00:00" + export PATH=${WRAP_DIR}:${PATH} + + cd monero + BASEPREFIX=`pwd`/contrib/depends + # Build dependencies for each host + for i in $HOSTS; do + EXTRA_INCLUDES="$EXTRA_INCLUDES_BASE/$i" + if [ -d "$EXTRA_INCLUDES" ]; then + export HOST_ID_SALT="$EXTRA_INCLUDES" + fi + make ${MAKEOPTS} -C ${BASEPREFIX} HOST="${i}" -j 4 V=1 + unset HOST_ID_SALT + done + + # Faketime for binaries + export PATH=${PATH_orig} + create_global_faketime_wrappers "${REFERENCE_DATETIME}" + create_per-host_faketime_wrappers "${REFERENCE_DATETIME}" + export PATH=${WRAP_DIR}:${PATH} + + ORIGPATH="$PATH" + # Run cmake and make, for each create a new build/ directory, + # compile from there, archive, export and delete the archive again + for i in ${HOSTS}; do + export PATH=${BASEPREFIX}/${i}/native/bin:${ORIGPATH} + mkdir build && cd build + cmake .. -DCMAKE_TOOLCHAIN_FILE=${BASEPREFIX}/${i}/share/toolchain.cmake + make + DISTNAME=monero-${i} + mv bin ${DISTNAME} + find ${DISTNAME}/ | sort | zip -X@ ${OUTDIR}/${DISTNAME}.zip + find ${DISTNAME}/ | sort | tar --no-recursion --mode='u+rw,go+r-w,a+X' --owner=0 --group=0 -c -T - | gzip -9n > ${OUTDIR}/${DISTNAME}-unsigned.tar.gz + cd .. && rm -rf build + done + From bf60e98abc07dd569902279e7879b5198944f66c Mon Sep 17 00:00:00 2001 From: Riccardo Spagni Date: Tue, 9 Oct 2018 22:36:44 +0200 Subject: [PATCH 0561/1404] fix version changes on master --- src/version.cpp.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/version.cpp.in b/src/version.cpp.in index 55075a06405..ff24058111c 100644 --- a/src/version.cpp.in +++ b/src/version.cpp.in @@ -1,5 +1,5 @@ #define DEF_MONERO_VERSION_TAG "@VERSIONTAG@" -#define DEF_MONERO_VERSION "0.13.0.1-rc" +#define DEF_MONERO_VERSION "0.13.0.0-master" #define DEF_MONERO_RELEASE_NAME "Beryllium Bullet" #define DEF_MONERO_VERSION_FULL DEF_MONERO_VERSION "-" DEF_MONERO_VERSION_TAG From 92a0827eea45d06729d78fd3013c007829903f60 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 8 Oct 2018 10:25:16 +0000 Subject: [PATCH 0562/1404] wallet2: make fake out selection messages less spammy --- src/wallet/wallet2.cpp | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 7871fe99c9f..f005d85c6c8 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -37,6 +37,8 @@ #include #include #include +#include +#include #include "include_base_utils.h" using namespace epee; @@ -6968,6 +6970,8 @@ void wallet2::get_outs(std::vector> LOG_PRINT_L1("Selecting real output: " << td.m_global_output_index << " for " << print_money(amount)); } + std::unordered_map> picks; + // while we still need more mixins uint64_t num_usable_outs = num_outs; bool allow_blackballed = false; @@ -7066,11 +7070,15 @@ void wallet2::get_outs(std::vector> } seen_indices.emplace(i); - LOG_PRINT_L2("picking " << i << " as " << type); + picks[type].insert(i); req.outputs.push_back({amount, i}); ++num_found; } + for (const auto &pick: picks) + MDEBUG("picking " << pick.first << " outputs: " << + boost::join(pick.second | boost::adaptors::transformed([](uint64_t out){return std::to_string(out);}), " ")); + // if we had enough unusable outputs, we might fall off here and still // have too few outputs, so we stuff with one to keep counts good, and // we'll error out later @@ -7086,8 +7094,15 @@ void wallet2::get_outs(std::vector> [](const get_outputs_out &a, const get_outputs_out &b) { return a.index < b.index; }); } - for (auto i: req.outputs) - LOG_PRINT_L1("asking for output " << i.index << " for " << print_money(i.amount)); + if (ELPP->vRegistry()->allowed(el::Level::Debug, MONERO_DEFAULT_LOG_CATEGORY)) + { + std::map> outs; + for (const auto &i: req.outputs) + outs[i.amount].insert(i.index); + for (const auto &o: outs) + MDEBUG("asking for outputs with amount " << print_money(o.first) << ": " << + boost::join(o.second | boost::adaptors::transformed([](uint64_t out){return std::to_string(out);}), " ")); + } // get the keys for those m_daemon_rpc_mutex.lock(); From dc8f6924b2c4e97f742a49942815e3b5ef50d675 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 10 Oct 2018 08:57:41 +0000 Subject: [PATCH 0563/1404] password: fix backspace outputting ^? on linux on echoing secure input --- src/common/password.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/common/password.cpp b/src/common/password.cpp index a8d5141dcb3..e41c75088a5 100644 --- a/src/common/password.cpp +++ b/src/common/password.cpp @@ -158,6 +158,13 @@ namespace if (!aPass.empty()) { aPass.pop_back(); + if (!hide_input) + std::cout << "\b\b\b \b\b\b" << std::flush; + } + else + { + if (!hide_input) + std::cout << "\b\b \b\b" << std::flush; } } else From 0e33cf89d2e351b2306e66a7d23ede880208fbee Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 10 Oct 2018 12:15:20 +0000 Subject: [PATCH 0564/1404] password: fix secure input with echo on windows Thanks to iDunk for the testing back and forth --- src/common/password.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/common/password.cpp b/src/common/password.cpp index a8d5141dcb3..7ba3fcb4230 100644 --- a/src/common/password.cpp +++ b/src/common/password.cpp @@ -60,7 +60,7 @@ namespace DWORD mode_old; ::GetConsoleMode(h_cin, &mode_old); - DWORD mode_new = mode_old & ~((hide_input ? ENABLE_ECHO_INPUT : 0) | ENABLE_LINE_INPUT); + DWORD mode_new = mode_old & ~(hide_input ? ENABLE_ECHO_INPUT : 0); ::SetConsoleMode(h_cin, mode_new); bool r = true; @@ -78,7 +78,11 @@ namespace { break; } - else if (ucs2_ch == L'\n' || ucs2_ch == L'\r') + else if (ucs2_ch == L'\r') + { + continue; + } + else if (ucs2_ch == L'\n') { std::cout << std::endl; break; From c0822fdd301a41711dca69576449046b55cf9bca Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 10 Oct 2018 15:03:38 +0000 Subject: [PATCH 0565/1404] simplewallet: mark default-ring-size setting as obsolete --- src/simplewallet/simplewallet.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 4ec7e3eb4bc..b63eb597411 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -2403,7 +2403,7 @@ simple_wallet::simple_wallet() "store-tx-info <1|0>\n " " Whether to store outgoing tx info (destination address, payment ID, tx secret key) for future reference.\n " "default-ring-size \n " - " Set the default ring size (default and minimum is 5).\n " + " Set the default ring size (obsolete).\n " "auto-refresh <1|0>\n " " Whether to automatically synchronize new blocks from the daemon.\n " "refresh-type \n " From 67e76aa06c3db2a82f98fa6a74fff5feb4f4199d Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 10 Oct 2018 15:54:59 +0000 Subject: [PATCH 0566/1404] wallet_rpc_server: optionally return tx keys in sign_transfer --- src/wallet/wallet2.cpp | 4 ++++ src/wallet/wallet_rpc_server.cpp | 2 ++ src/wallet/wallet_rpc_server_commands_defs.h | 4 ++++ 3 files changed, 10 insertions(+) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index f87edf506f8..680f3a21474 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -5607,6 +5607,10 @@ bool wallet2::sign_tx(unsigned_tx_set &exported_txs, std::vector tx_hash_list; std::list tx_raw_list; + std::list tx_key_list; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(signed_txset) KV_SERIALIZE(tx_hash_list) KV_SERIALIZE(tx_raw_list) + KV_SERIALIZE(tx_key_list) END_KV_SERIALIZE_MAP() }; }; From d46c765dcad76ecf0a8b90bf08b1d5e103278d5c Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 10 Oct 2018 16:46:10 +0000 Subject: [PATCH 0567/1404] README: mention max_usb_current setting on Raspberry Pi --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 5da96371a2c..36fdfff8a75 100644 --- a/README.md +++ b/README.md @@ -237,6 +237,8 @@ Tested on a Raspberry Pi Zero with a clean install of minimal Raspbian Stretch ( CONF_SWAPSIZE=1024 sudo /etc/init.d/dphys-swapfile start ``` +* If using an external hard disk without an external power supply, ensure it gets enough power to avoid hardware issues when syncing, by adding the line "max_usb_current=1" to /boot/config.txt + * Clone monero and checkout most recent release version: ``` git clone https://github.com/monero-project/monero.git From f2c2c47a4ba40a00d5c6b9d7ea3df4c6cb654c83 Mon Sep 17 00:00:00 2001 From: xiphon Date: Wed, 10 Oct 2018 19:39:51 +0000 Subject: [PATCH 0568/1404] simplewallet: fixed deadlock if a user hits CTRL+C twice Co-authored-by: moneromooo-monero --- src/simplewallet/simplewallet.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 4ec7e3eb4bc..625ab52bc56 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -7050,12 +7050,6 @@ bool simple_wallet::run() void simple_wallet::stop() { m_cmd_binder.stop_handling(); - - m_idle_run.store(false, std::memory_order_relaxed); - m_wallet->stop(); - // make the background refresh thread quit - boost::unique_lock lock(m_idle_mutex); - m_idle_cond.notify_one(); } //---------------------------------------------------------------------------------------------------- bool simple_wallet::account(const std::vector &args/* = std::vector()*/) From 68e9744d4465cf809b1b68234876ade8705de9e3 Mon Sep 17 00:00:00 2001 From: xiphon Date: Wed, 10 Oct 2018 20:01:04 +0000 Subject: [PATCH 0569/1404] simplewallet: perform trivial error checks before password prompt --- src/simplewallet/simplewallet.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 4ec7e3eb4bc..8d24bb9fbd2 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -4715,8 +4715,6 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector local_args = args_; std::set subaddr_indices; @@ -4936,6 +4934,8 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector Date: Wed, 10 Oct 2018 00:16:55 +0200 Subject: [PATCH 0570/1404] Add OSX gitian descriptor --- contrib/gitian/gitian-build.py | 24 ++----- contrib/gitian/gitian-linux.yml | 4 +- contrib/gitian/gitian-osx.yml | 114 ++++++++++++++++++++++++++++++++ contrib/gitian/gitian-win.yml | 1 - 4 files changed, 120 insertions(+), 23 deletions(-) create mode 100644 contrib/gitian/gitian-osx.yml diff --git a/contrib/gitian/gitian-build.py b/contrib/gitian/gitian-build.py index feb193a3213..eb138e41a2c 100755 --- a/contrib/gitian/gitian-build.py +++ b/contrib/gitian/gitian-build.py @@ -66,15 +66,13 @@ def build(): print('\nCompiling ' + args.version + ' Windows') subprocess.check_call(['bin/gbuild', '-j', args.jobs, '-m', args.memory, '--commit', 'monero='+args.commit, '--url', 'monero='+args.url, '../monero/contrib/gitian/gitian-win.yml']) subprocess.check_call(['bin/gsign', '-p', args.sign_prog, '--signer', args.signer, '--release', args.version+'-win-unsigned', '--destination', '../gitian.sigs/', '../monero/contrib/gitian/gitian-win.yml']) - subprocess.check_call('mv build/out/monero*-unsigned.tar.gz inputs/', shell=True) - subprocess.check_call('mv build/out/monero*.zip ../monerobinaries/'+args.version, shell=True) + subprocess.check_call('mv build/out/monero*.zip ../monero-binaries/'+args.version, shell=True) if args.macos: print('\nCompiling ' + args.version + ' MacOS') - subprocess.check_call(['bin/gbuild', '-j', args.jobs, '-m', args.memory, '--commit', 'monero'+args.commit, '--url', 'monero'+args.url, '../monero/contrib/gitian/gitian-osx.yml']) + subprocess.check_call(['bin/gbuild', '-j', args.jobs, '-m', args.memory, '--commit', 'monero='+args.commit, '--url', 'monero'+args.url, '../monero/contrib/gitian/gitian-osx.yml']) subprocess.check_call(['bin/gsign', '-p', args.sign_prog, '--signer', args.signer, '--release', args.version+'-osx-unsigned', '--destination', '../gitian.sigs/', '../monero/contrib/gitian/gitian-osx.yml']) - subprocess.check_call('mv build/out/monero*-osx-unsigned.tar.gz inputs/', shell=True) - subprocess.check_call('mv build/out/monero*.tar.gz build/out/monero*.dmg ../monerobinaries/'+args.version, shell=True) + subprocess.check_call('mv build/out/monero*.tar.gz ../monero-binaries/'+args.version, shell=True) os.chdir(workdir) @@ -90,20 +88,6 @@ def build(): def sign(): global args, workdir os.chdir('gitian-builder') - - if args.windows: - print('\nSigning ' + args.version + ' Windows') - subprocess.check_call(['bin/gbuild', '-i', '--commit', 'signature='+args.commit, '../monero/contrib/gitian/gitian-win-signer.yml']) - subprocess.check_call(['bin/gsign', '-p', args.sign_prog, '--signer', args.signer, '--release', args.version+'-win-signed', '--destination', '../gitian.sigs/', '../monero/contrib/gitian/gitian-win-signer.yml']) - subprocess.check_call('mv build/out/monero*win64-setup.exe ../monerobinaries/'+args.version, shell=True) - subprocess.check_call('mv build/out/monero*win32-setup.exe ../monerobinaries/'+args.version, shell=True) - - if args.macos: - print('\nSigning ' + args.version + ' MacOS') - subprocess.check_call(['bin/gbuild', '-i', '--commit', 'signature='+args.commit, '../monero/contrib/gitian/gitian-osx-signer.yml']) - subprocess.check_call(['bin/gsign', '-p', args.sign_prog, '--signer', args.signer, '--release', args.version+'-osx-signed', '--destination', '../gitian.sigs/', '../monero/contrib/gitian/gitian-osx-signer.yml']) - subprocess.check_call('mv build/out/moneroosx-signed.dmg ../monerobinaries/'+args.version+'/monero'+args.version+'-osx.dmg', shell=True) - os.chdir(workdir) if args.commit_files: @@ -137,7 +121,7 @@ def main(): parser = argparse.ArgumentParser(usage='%(prog)s [options] signer version') parser.add_argument('-c', '--commit', action='store_true', dest='commit', help='Indicate that the version argument is for a commit or branch') parser.add_argument('-p', '--pull', action='store_true', dest='pull', help='Indicate that the version argument is the number of a github repository pull request') - parser.add_argument('-u', '--url', dest='url', default='https://github.com/TheCharlatan/monero', help='Specify the URL of the repository. Default is %(default)s') + parser.add_argument('-u', '--url', dest='url', default='https://github.com/monero-project/monero', help='Specify the URL of the repository. Default is %(default)s') parser.add_argument('-v', '--verify', action='store_true', dest='verify', help='Verify the Gitian build') parser.add_argument('-b', '--build', action='store_true', dest='build', help='Do a Gitian build') parser.add_argument('-s', '--sign', action='store_true', dest='sign', help='Make signed binaries for Windows and MacOS') diff --git a/contrib/gitian/gitian-linux.yml b/contrib/gitian/gitian-linux.yml index e64e28bbc07..6c338f1da0a 100644 --- a/contrib/gitian/gitian-linux.yml +++ b/contrib/gitian/gitian-linux.yml @@ -43,7 +43,7 @@ packages: - "libprotobuf-dev" - "python3-zmq" remotes: -- "url": "https://github.com/TheCharlatan/monero.git" +- "url": "https://github.com/monero-project/monero.git" "dir": "monero" files: [] script: | @@ -148,7 +148,7 @@ script: | export PATH=${WRAP_DIR}:${PATH} ORIGPATH="$PATH" - # Extract the release tarball into a dir for each host and build + # Build in a new dir for each host for i in ${HOSTS}; do export PATH=${BASEPREFIX}/${i}/native/bin:${ORIGPATH} mkdir build && cd build diff --git a/contrib/gitian/gitian-osx.yml b/contrib/gitian/gitian-osx.yml new file mode 100644 index 00000000000..d9ebf95d3ee --- /dev/null +++ b/contrib/gitian/gitian-osx.yml @@ -0,0 +1,114 @@ +--- +name: "bitcoin-osx-0.18" +enable_cache: true +suites: +- "bionic" +architectures: +- "amd64" +packages: +- "ca-certificates" +- "curl" +- "g++" +- "git" +- "pkg-config" +- "autoconf" +- "librsvg2-bin" +- "libtiff-tools" +- "libtool" +- "automake" +- "faketime" +- "bsdmainutils" +- "cmake" +- "imagemagick" +- "libcap-dev" +- "libz-dev" +- "libbz2-dev" +- "python" +- "python-dev" +- "python-setuptools" +- "fonts-tuffy" +remotes: +- "url": "https://github.com/monero-project/monero.git" + "dir": "monero" +files: +- "MacOSX10.11.sdk.tar.gz" +script: | + WRAP_DIR=$HOME/wrapped + HOSTS="x86_64-apple-darwin11" + FAKETIME_HOST_PROGS="" + FAKETIME_PROGS="ar ranlib date dmg genisoimage" + + export GZIP="-9n" + export TAR_OPTIONS="--mtime="$REFERENCE_DATE\\\ $REFERENCE_TIME"" + export TZ="UTC" + export BUILD_DIR=`pwd` + mkdir -p ${WRAP_DIR} + if test -n "$GBUILD_CACHE_ENABLED"; then + export SOURCES_PATH=${GBUILD_COMMON_CACHE} + export BASE_CACHE=${GBUILD_PACKAGE_CACHE} + mkdir -p ${BASE_CACHE} ${SOURCES_PATH} + fi + + export ZERO_AR_DATE=1 + + function create_global_faketime_wrappers { + for prog in ${FAKETIME_PROGS}; do + echo '#!/usr/bin/env bash' > ${WRAP_DIR}/${prog} + echo "REAL=\`which -a ${prog} | grep -v ${WRAP_DIR}/${prog} | head -1\`" >> ${WRAP_DIR}/${prog} + echo 'export LD_PRELOAD=/usr/lib/x86_64-linux-gnu/faketime/libfaketime.so.1' >> ${WRAP_DIR}/${prog} + echo "export FAKETIME=\"$1\"" >> ${WRAP_DIR}/${prog} + echo "\$REAL \$@" >> $WRAP_DIR/${prog} + chmod +x ${WRAP_DIR}/${prog} + done + } + + function create_per-host_faketime_wrappers { + for i in $HOSTS; do + for prog in ${FAKETIME_HOST_PROGS}; do + echo '#!/usr/bin/env bash' > ${WRAP_DIR}/${i}-${prog} + echo "REAL=\`which -a ${i}-${prog} | grep -v ${WRAP_DIR}/${i}-${prog} | head -1\`" >> ${WRAP_DIR}/${i}-${prog} + echo 'export LD_PRELOAD=/usr/lib/x86_64-linux-gnu/faketime/libfaketime.so.1' >> ${WRAP_DIR}/${i}-${prog} + echo "export FAKETIME=\"$1\"" >> ${WRAP_DIR}/${i}-${prog} + echo "\$REAL \$@" >> $WRAP_DIR/${i}-${prog} + chmod +x ${WRAP_DIR}/${i}-${prog} + done + done + } + + # Faketime for depends so intermediate results are comparable + export PATH_orig=${PATH} + create_global_faketime_wrappers "2000-01-01 12:00:00" + create_per-host_faketime_wrappers "2000-01-01 12:00:00" + export PATH=${WRAP_DIR}:${PATH} + + cd monero + BASEPREFIX=`pwd`/contrib/depends + + mkdir -p ${BASEPREFIX}/SDKs + tar -C ${BASEPREFIX}/SDKs -xf ${BUILD_DIR}/MacOSX10.11.sdk.tar.gz + + # Build dependencies for each host + for i in $HOSTS; do + make ${MAKEOPTS} -C ${BASEPREFIX} HOST="${i}" + done + + # Faketime for binaries + export PATH=${PATH_orig} + create_global_faketime_wrappers "${REFERENCE_DATETIME}" + create_per-host_faketime_wrappers "${REFERENCE_DATETIME}" + export PATH=${WRAP_DIR}:${PATH} + + ORIGPATH="$PATH" + # Build in a new dir for each host + for i in ${HOSTS}; do + export PATH=${BASEPREFIX}/${i}/native/bin:${ORIGPATH} + mkdir build && cd build + cmake .. -DCMAKE_TOOLCHAIN_FILE=${BASEPREFIX}/${i}/share/toolchain.cmake + make + DISTNAME=monero-${i} + mv bin ${DISTNAME} + find ${DISTNAME}/ | sort | tar --no-recursion --mode='u+rw,go+r-w,a+X' --owner=0 --group=0 -c -T - | gzip -9n > ${OUTDIR}/${DISTNAME}.tar.gz + cd .. + rm -rf build + done + diff --git a/contrib/gitian/gitian-win.yml b/contrib/gitian/gitian-win.yml index 7ae8c58f426..74c757c11fe 100644 --- a/contrib/gitian/gitian-win.yml +++ b/contrib/gitian/gitian-win.yml @@ -129,7 +129,6 @@ script: | DISTNAME=monero-${i} mv bin ${DISTNAME} find ${DISTNAME}/ | sort | zip -X@ ${OUTDIR}/${DISTNAME}.zip - find ${DISTNAME}/ | sort | tar --no-recursion --mode='u+rw,go+r-w,a+X' --owner=0 --group=0 -c -T - | gzip -9n > ${OUTDIR}/${DISTNAME}-unsigned.tar.gz cd .. && rm -rf build done From 04ddf02e3a6fb5ee4f4777e14625bd2d701add5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Sa=C5=82aban?= Date: Thu, 11 Oct 2018 18:12:17 +0200 Subject: [PATCH 0571/1404] Return appropriate RPC error code when key image signature check fails --- src/wallet/wallet2.cpp | 2 +- src/wallet/wallet_errors.h | 9 +++++++++ src/wallet/wallet_rpc_server.cpp | 5 +++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index f87edf506f8..b505c13ee83 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -10542,7 +10542,7 @@ uint64_t wallet2::import_key_images(const std::vector(signed_key_images.size()) + ", key image " + epee::string_tools::pod_to_hex(key_image)); THROW_WALLET_EXCEPTION_IF(!crypto::check_ring_signature((const crypto::hash&)key_image, key_image, pkeys, &signature), - error::wallet_internal_error, "Signature check failed: input " + boost::lexical_cast(n) + "/" + error::signature_check_failed, boost::lexical_cast(n) + "/" + boost::lexical_cast(signed_key_images.size()) + ", key image " + epee::string_tools::pod_to_hex(key_image) + ", signature " + epee::string_tools::pod_to_hex(signature) + ", pubkey " + epee::string_tools::pod_to_hex(*pkeys[0])); diff --git a/src/wallet/wallet_errors.h b/src/wallet/wallet_errors.h index bc518d04a13..b3141985dae 100644 --- a/src/wallet/wallet_errors.h +++ b/src/wallet/wallet_errors.h @@ -72,6 +72,7 @@ namespace tools // tx_parse_error // get_tx_pool_error // out_of_hashchain_bounds_error + // signature_check_failed // transfer_error * // get_outs_general_error // not_enough_unlocked_money @@ -418,6 +419,14 @@ namespace tools std::string to_string() const { return refresh_error::to_string(); } }; //---------------------------------------------------------------------------------------------------- + struct signature_check_failed : public wallet_logic_error + { + explicit signature_check_failed(std::string&& loc, const std::string& message) + : wallet_logic_error(std::move(loc), "Signature check failed " + message) + { + } + }; + //---------------------------------------------------------------------------------------------------- struct transfer_error : public wallet_logic_error { protected: diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index e0b631aafb4..87ee20902a3 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -2906,6 +2906,11 @@ namespace tools er.code = WALLET_RPC_ERROR_CODE_ADDRESS_INDEX_OUT_OF_BOUNDS; er.message = e.what(); } + catch (const error::signature_check_failed& e) + { + er.code = WALLET_RPC_ERROR_CODE_WRONG_SIGNATURE; + er.message = e.what(); + } catch (const std::exception& e) { er.code = default_error_code; From f5f7c2ac245523b5a78a9aff57f05077ff352c1f Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 8 Oct 2018 14:03:08 +0000 Subject: [PATCH 0572/1404] rpc: blanket initialize 0MQ request and response structures --- src/rpc/daemon_messages.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rpc/daemon_messages.h b/src/rpc/daemon_messages.h index 8fff369df4f..be3138e3b6d 100644 --- a/src/rpc/daemon_messages.h +++ b/src/rpc/daemon_messages.h @@ -67,7 +67,7 @@ class classname \ // NOTE: when using a type with multiple template parameters, // replace any comma in the template specifier with the macro // above, or the preprocessor will eat the comma in a bad way. -#define RPC_MESSAGE_MEMBER(type, name) type name; +#define RPC_MESSAGE_MEMBER(type, name) type name = type{} namespace cryptonote From 14ed029b241d00f6a711b9362de4ab094ec30ea2 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 12 Oct 2018 12:54:17 +0000 Subject: [PATCH 0573/1404] simplewallet: fix view key parsing in --generate-from-view-key --- src/simplewallet/simplewallet.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 4ec7e3eb4bc..e1a98135ca8 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -3032,7 +3032,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) return false; } crypto::secret_key viewkey; - if (viewkey_string.hex_to_pod(unwrap(unwrap(viewkey)))) + if (!viewkey_string.hex_to_pod(unwrap(unwrap(viewkey)))) { fail_msg_writer() << tr("failed to parse view key secret key"); return false; From e736964a0cd37d887d4da2ef362e760d1cb7474f Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 12 Oct 2018 15:20:42 +0000 Subject: [PATCH 0574/1404] Remove epee header dependency on cryptonote_core --- contrib/epee/include/net/abstract_tcp_server2.inl | 10 ++++------ contrib/epee/include/net/http_protocol_handler.inl | 1 + src/debug_utilities/object_sizes.cpp | 1 + src/p2p/net_node.cpp | 1 + src/wallet/wallet_rpc_server.h | 1 + tests/net_load_tests/clt.cpp | 1 + tests/net_load_tests/srv.cpp | 1 + 7 files changed, 10 insertions(+), 6 deletions(-) diff --git a/contrib/epee/include/net/abstract_tcp_server2.inl b/contrib/epee/include/net/abstract_tcp_server2.inl index 3a5c830179d..5dbb7a47831 100644 --- a/contrib/epee/include/net/abstract_tcp_server2.inl +++ b/contrib/epee/include/net/abstract_tcp_server2.inl @@ -43,6 +43,8 @@ #include // TODO #include // TODO #include // TODO +#include "warnings.h" +#include "string_tools.h" #include "misc_language.h" #include "net/local_ip.h" #include "pragma_comp_defs.h" @@ -51,8 +53,6 @@ #include #include -#include "../../../../src/cryptonote_core/cryptonote_core.h" // e.g. for the send_stop_signal() - #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "net" @@ -149,10 +149,8 @@ PRAGMA_WARNING_DISABLE_VS(4355) const unsigned long ip_{boost::asio::detail::socket_ops::host_to_network_long(remote_ep.address().to_v4().to_ulong())}; m_local = epee::net_utils::is_ip_loopback(ip_) || epee::net_utils::is_ip_local(ip_); - // create a random uuid - boost::uuids::uuid random_uuid; - // that stuff turns out to be included, even though it's from src... Taking advantage - random_uuid = crypto::rand(); + // create a random uuid, we don't need crypto strength here + const boost::uuids::uuid random_uuid = boost::uuids::random_generator()(); context.set_details(random_uuid, epee::net_utils::ipv4_network_address(ip_, remote_ep.port()), is_income); _dbg3("[sock " << socket_.native_handle() << "] new connection from " << print_connection_context_short(context) << diff --git a/contrib/epee/include/net/http_protocol_handler.inl b/contrib/epee/include/net/http_protocol_handler.inl index 76db5346f2d..b53bdc200b4 100644 --- a/contrib/epee/include/net/http_protocol_handler.inl +++ b/contrib/epee/include/net/http_protocol_handler.inl @@ -32,6 +32,7 @@ #include "string_tools.h" #include "file_io_utils.h" #include "net_parse_helpers.h" +#include "time_helper.h" #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "net.http" diff --git a/src/debug_utilities/object_sizes.cpp b/src/debug_utilities/object_sizes.cpp index ab883963688..2281d073489 100644 --- a/src/debug_utilities/object_sizes.cpp +++ b/src/debug_utilities/object_sizes.cpp @@ -29,6 +29,7 @@ #include #include "cryptonote_basic/cryptonote_basic.h" #include "cryptonote_basic/tx_extra.h" +#include "cryptonote_core/cryptonote_core.h" #include "cryptonote_core/blockchain.h" #include "p2p/p2p_protocol_defs.h" #include "net/connection_basic.hpp" diff --git a/src/p2p/net_node.cpp b/src/p2p/net_node.cpp index c9ca63f4337..e9d2061e864 100644 --- a/src/p2p/net_node.cpp +++ b/src/p2p/net_node.cpp @@ -29,6 +29,7 @@ // Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers #include "common/command_line.h" +#include "cryptonote_core/cryptonote_core.h" #include "net_node.h" namespace nodetool diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h index ab896aa3ba3..35ea119020f 100644 --- a/src/wallet/wallet_rpc_server.h +++ b/src/wallet/wallet_rpc_server.h @@ -35,6 +35,7 @@ #include #include "common/util.h" #include "net/http_server_impl_base.h" +#include "math_helper.h" #include "wallet_rpc_server_commands_defs.h" #include "wallet2.h" diff --git a/tests/net_load_tests/clt.cpp b/tests/net_load_tests/clt.cpp index a65e02cab2e..275044045e3 100644 --- a/tests/net_load_tests/clt.cpp +++ b/tests/net_load_tests/clt.cpp @@ -41,6 +41,7 @@ #include "misc_language.h" #include "misc_log_ex.h" #include "storages/levin_abstract_invoke2.h" +#include "common/util.h" #include "net_load_tests.h" diff --git a/tests/net_load_tests/srv.cpp b/tests/net_load_tests/srv.cpp index 6518d911705..54110fc1e7f 100644 --- a/tests/net_load_tests/srv.cpp +++ b/tests/net_load_tests/srv.cpp @@ -34,6 +34,7 @@ #include "include_base_utils.h" #include "misc_log_ex.h" #include "storages/levin_abstract_invoke2.h" +#include "common/util.h" #include "net_load_tests.h" From c3b8328cd3ca7e5052b6cb2f746b19137421d268 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 12 Oct 2018 14:09:30 +0000 Subject: [PATCH 0575/1404] daemon: do not run complex code in a signal handler instead, delegate the work to a one off thread and notify it from the signal handler --- src/daemon/daemon.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/daemon/daemon.cpp b/src/daemon/daemon.cpp index ea24e32eb7d..f536495182b 100644 --- a/src/daemon/daemon.cpp +++ b/src/daemon/daemon.cpp @@ -136,7 +136,14 @@ bool t_daemon::run(bool interactive) { throw std::runtime_error{"Can't run stopped daemon"}; } - tools::signal_handler::install(std::bind(&daemonize::t_daemon::stop_p2p, this)); + + std::atomic stop(false); + boost::thread([&stop, this] { + while (!stop) + epee::misc_utils::sleep_no_w(100); + this->stop_p2p(); + }).detach(); + tools::signal_handler::install([&stop](int){ stop = true; }); try { From 8f3c79374900ca77c126976036290d10f9999598 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 12 Oct 2018 21:02:59 +0000 Subject: [PATCH 0576/1404] readline_buffer: fix "cursor in prompt" bug It happens when readline displays a prompt just before switching to a shorter one --- contrib/epee/include/readline_buffer.h | 1 + contrib/epee/src/readline_buffer.cpp | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/contrib/epee/include/readline_buffer.h b/contrib/epee/include/readline_buffer.h index 87c8826cbd8..5968d243d5d 100644 --- a/contrib/epee/include/readline_buffer.h +++ b/contrib/epee/include/readline_buffer.h @@ -27,6 +27,7 @@ namespace rdln private: std::streambuf* m_cout_buf; + size_t m_prompt_length; static std::vector& completion_commands(); }; diff --git a/contrib/epee/src/readline_buffer.cpp b/contrib/epee/src/readline_buffer.cpp index 076a63612ce..da264471f39 100644 --- a/contrib/epee/src/readline_buffer.cpp +++ b/contrib/epee/src/readline_buffer.cpp @@ -44,7 +44,7 @@ std::vector& rdln::readline_buffer::completion_commands() } rdln::readline_buffer::readline_buffer() -: std::stringbuf(), m_cout_buf(NULL) +: std::stringbuf(), m_cout_buf(NULL), m_prompt_length(0) { current = this; } @@ -86,8 +86,11 @@ void rdln::readline_buffer::set_prompt(const std::string& prompt) if(m_cout_buf == NULL) return; boost::lock_guard lock(sync_mutex); + rl_set_prompt(std::string(m_prompt_length, ' ').c_str()); + rl_redisplay(); rl_set_prompt(prompt.c_str()); rl_redisplay(); + m_prompt_length = prompt.size(); } void rdln::readline_buffer::add_completion(const std::string& command) From d886b97fe216846647aea2560d423c138e0d67d4 Mon Sep 17 00:00:00 2001 From: m2049r Date: Fri, 12 Oct 2018 20:58:27 +0200 Subject: [PATCH 0577/1404] SOFTWARE is the default wallet device --- src/wallet/wallet2.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index f87edf506f8..d774c54c062 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -3611,6 +3611,7 @@ bool wallet2::query_device(hw::device::device_type& device_type, const std::stri if (json.Parse(account_data.c_str()).HasParseError() || !json.IsObject()) crypto::chacha8(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]); + device_type = hw::device::device_type::SOFTWARE; // The contents should be JSON if the wallet follows the new format. if (json.Parse(account_data.c_str()).HasParseError()) { From bf842a6a1e1e0a983d7c37ea7564b7f19d8544b1 Mon Sep 17 00:00:00 2001 From: xiphon Date: Sat, 13 Oct 2018 09:46:19 +0000 Subject: [PATCH 0578/1404] build: use ARCH 'native' by default, allow to configure and override it --- .travis.yml | 2 +- CMakeLists.txt | 8 +++----- contrib/depends/toolchain.cmake.in | 2 ++ 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index ca3a24ae82b..6a9b9b94b92 100644 --- a/.travis.yml +++ b/.travis.yml @@ -58,7 +58,7 @@ script: - export TRAVIS_COMMIT_LOG=`git log --format=fuller -1` - OUTDIR=$BASE_OUTDIR/$TRAVIS_PULL_REQUEST/$TRAVIS_JOB_NUMBER-$HOST - if [ -z "$NO_DEPENDS" ]; then $DOCKER_EXEC ccache --max-size=$CCACHE_SIZE; fi - - $DOCKER_EXEC bash -c "mkdir build && cd build && cmake -DCMAKE_TOOLCHAIN_FILE=$TRAVIS_BUILD_DIR/contrib/depends/$HOST/share/toolchain.cmake .. && make $MAKEJOBS" + - $DOCKER_EXEC bash -c "mkdir build && cd build && cmake -DCMAKE_TOOLCHAIN_FILE=$TRAVIS_BUILD_DIR/contrib/depends/$HOST/share/toolchain.cmake -DTRAVIS=true .. && make $MAKEJOBS" - export LD_LIBRARY_PATH=$TRAVIS_BUILD_DIR/contrib/depends/$HOST/lib after_script: - echo $TRAVIS_COMMIT_RANGE diff --git a/CMakeLists.txt b/CMakeLists.txt index 1bffd29b60d..78d16b2add9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,6 +40,7 @@ if (IOS) endif() cmake_minimum_required(VERSION 2.8.7) +message(STATUS "CMake version ${CMAKE_VERSION}") project(monero) @@ -139,7 +140,6 @@ if(ARCH_ID STREQUAL "ppc64le") set(PPC64LE 1) set(PPC64 0) set(PPC 0) - endif() if(ARCH_ID STREQUAL "powerpc64" OR ARCH_ID STREQUAL "ppc64") @@ -517,10 +517,8 @@ if(MSVC) include_directories(SYSTEM src/platform/msc) else() include(TestCXXAcceptsFlag) - if (NOT ARM6) - if(NOT DEPENDS OR DEPENDS AND NOT ARM) - set(ARCH native CACHE STRING "CPU to build for: -march value or 'default' to not pass -march at all") - endif() + if (NOT ARCH) + set(ARCH native CACHE STRING "CPU to build for: -march value or 'default' to not pass -march at all") endif() message(STATUS "Building on ${CMAKE_SYSTEM_PROCESSOR} for ${ARCH}") if(ARCH STREQUAL "default") diff --git a/contrib/depends/toolchain.cmake.in b/contrib/depends/toolchain.cmake.in index f8548a72462..66168facb41 100644 --- a/contrib/depends/toolchain.cmake.in +++ b/contrib/depends/toolchain.cmake.in @@ -51,7 +51,9 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Darwin") SET(APPLE True) SET(BUILD_TAG "mac-x64") SET(BUILD_64 ON) + if(NOT TRAVIS) SET(ARCH "x86_64") + endif() SET(BREW OFF) SET(PORT OFF) SET(CMAKE_OSX_SYSROOT "@sdk@/MacOSX10.11.sdk/") From 76d6d832d2a43ff03acf9b4ce7b5f0f9e34231e2 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 13 Oct 2018 10:19:17 +0000 Subject: [PATCH 0579/1404] Revert "p2p: connect via the bound ip, if any" This reverts commit 909398efc79cb1fa92e330e9a50a316ca5858953. It looks like it's causing trouble with tor on some setups --- src/p2p/net_node.inl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index 74924e4f437..9390626a8f7 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -937,7 +937,7 @@ namespace nodetool bool res = m_net_server.connect(epee::string_tools::get_ip_string_from_int32(ipv4.ip()), epee::string_tools::num_to_string_fast(ipv4.port()), m_config.m_net_config.connection_timeout, - con, m_bind_ip.empty() ? "0.0.0.0" : m_bind_ip); + con); if(!res) { @@ -1002,7 +1002,7 @@ namespace nodetool bool res = m_net_server.connect(epee::string_tools::get_ip_string_from_int32(ipv4.ip()), epee::string_tools::num_to_string_fast(ipv4.port()), m_config.m_net_config.connection_timeout, - con, m_bind_ip.empty() ? "0.0.0.0" : m_bind_ip); + con); if (!res) { bool is_priority = is_priority_node(na); @@ -1617,7 +1617,7 @@ namespace nodetool return false; } return true; - }, m_bind_ip.empty() ? "0.0.0.0" : m_bind_ip); + }); if(!r) { LOG_WARNING_CC(context, "Failed to call connect_async, network error."); From 938476c9a59e04c245fac7707c6baf2183964e60 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 13 Oct 2018 18:11:09 +0000 Subject: [PATCH 0580/1404] CMakeLists.txt: detect and use -pthread compiler flag The cmake thread detection just ain't enough to always work --- CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1bffd29b60d..d96dd649e69 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -428,6 +428,8 @@ if (UNIX AND NOT APPLE) # Note that at the time of this writing the -Wstrict-prototypes flag added below will make this fail set(THREADS_PREFER_PTHREAD_FLAG ON) find_package(Threads) + add_c_flag_if_supported(-pthread CMAKE_C_FLAGS) + add_cxx_flag_if_supported(-pthread CMAKE_CXX_FLAGS) endif() # Handle OpenSSL, used for sha256sum on binary updates and light wallet ssl http From 5dff61d4c322734f62422df8dcf181ee73f952f9 Mon Sep 17 00:00:00 2001 From: xiphon Date: Sun, 14 Oct 2018 10:51:52 +0000 Subject: [PATCH 0581/1404] readme: update OSX build badges --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5da96371a2c..d6515cfc770 100644 --- a/README.md +++ b/README.md @@ -32,9 +32,9 @@ These builds are of the master branch, which is used for active development and | Ubuntu 16.04 | amd64 | [![Ubuntu 16.04 amd64](https://build.getmonero.org/png?builder=monero-static-ubuntu-amd64)](https://build.getmonero.org/builders/monero-static-ubuntu-amd64) | Ubuntu 16.04 | armv7 | [![Ubuntu 16.04 armv7](https://build.getmonero.org/png?builder=monero-static-ubuntu-arm7)](https://build.getmonero.org/builders/monero-static-ubuntu-arm7) | Debian Stable | armv8 | [![Debian armv8](https://build.getmonero.org/png?builder=monero-static-debian-armv8)](https://build.getmonero.org/builders/monero-static-debian-armv8) -| OSX 10.10 | amd64 | [![OSX 10.10 amd64](https://build.getmonero.org/png?builder=monero-static-osx-10.10)](https://build.getmonero.org/builders/monero-static-osx-10.10) | OSX 10.11 | amd64 | [![OSX 10.11 amd64](https://build.getmonero.org/png?builder=monero-static-osx-10.11)](https://build.getmonero.org/builders/monero-static-osx-10.11) | OSX 10.12 | amd64 | [![OSX 10.12 amd64](https://build.getmonero.org/png?builder=monero-static-osx-10.12)](https://build.getmonero.org/builders/monero-static-osx-10.12) +| OSX 10.13 | amd64 | [![OSX 10.13 amd64](https://build.getmonero.org/png?builder=monero-static-osx-10.13)](https://build.getmonero.org/builders/monero-static-osx-10.13) | FreeBSD 11 | amd64 | [![FreeBSD 11 amd64](https://build.getmonero.org/png?builder=monero-static-freebsd64)](https://build.getmonero.org/builders/monero-static-freebsd64) | DragonFly BSD 4.6 | amd64 | [![DragonFly BSD amd64](https://build.getmonero.org/png?builder=monero-static-dragonflybsd-amd64)](https://build.getmonero.org/builders/monero-static-dragonflybsd-amd64) | Windows (MSYS2/MinGW) | i686 | [![Windows (MSYS2/MinGW) i686](https://build.getmonero.org/png?builder=monero-static-win32)](https://build.getmonero.org/builders/monero-static-win32) From 991613f88a6aa789610bd2f646b94f0958f7a497 Mon Sep 17 00:00:00 2001 From: Tyler Baker Date: Sat, 13 Oct 2018 14:13:43 -0700 Subject: [PATCH 0582/1404] Dockerfile: init and update submodules The Docker image is failing to build, as the submodules are not being explicitly initialized and updated. Fixes: https://github.com/monero-project/monero/issues/4582 Signed-off-by: Tyler Baker --- Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Dockerfile b/Dockerfile index 40ba81d9c5b..cd3e7df70af 100644 --- a/Dockerfile +++ b/Dockerfile @@ -115,6 +115,7 @@ COPY . . ENV USE_SINGLE_BUILDDIR=1 ARG NPROC RUN set -ex && \ + git submodule init && git submodule update && \ rm -rf build && \ if [ -z "$NPROC" ] ; \ then make -j$(nproc) release-static ; \ From a677492f1be6c0ae791775577f9edccc25f7201c Mon Sep 17 00:00:00 2001 From: stoffu Date: Mon, 15 Oct 2018 11:57:02 +0900 Subject: [PATCH 0583/1404] tx_pool: store hex string instead of raw binary to tx_blob of get_transaction_pool RPC Inspired by https://github.com/masari-project/masari/issues/93 --- src/cryptonote_core/tx_pool.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index c31a2073be9..c8b8a678674 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -766,7 +766,7 @@ namespace cryptonote m_blockchain.for_all_txpool_txes([&tx_infos, key_image_infos, include_sensitive_data](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd){ tx_info txi; txi.id_hash = epee::string_tools::pod_to_hex(txid); - txi.tx_blob = *bd; + txi.tx_blob = epee::string_tools::buff_to_hex_nodelimer(*bd); transaction tx; if (!parse_and_validate_tx_from_blob(*bd, tx)) { From 9ea507812e4a996885272bb8541195762f0cd5be Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 15 Oct 2018 13:00:32 +0000 Subject: [PATCH 0584/1404] README: add libnorm to the dependency list --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4b5dd06292a..dde8d3beb97 100644 --- a/README.md +++ b/README.md @@ -140,6 +140,7 @@ library archives (`.a`). | OpenSSL | basically any | NO | `libssl-dev` | `openssl` | `openssl-devel` | NO | sha256 sum | | libzmq | 3.0.0 | NO | `libzmq3-dev` | `zeromq` | `cppzmq-devel` | NO | ZeroMQ library | | OpenPGM | ? | NO | `libpgm-dev` | `libpgm` | `openpgm-devel` | NO | For ZeroMQ | +| libnorm[2] | ? | NO | `libnorm-dev` | | ` | YES | For ZeroMQ | | libunbound | 1.4.16 | YES | `libunbound-dev` | `unbound` | `unbound-devel` | NO | DNS resolver | | libsodium | ? | NO | `libsodium-dev` | `libsodium` | `libsodium-devel` | NO | cryptography | | libunwind | any | NO | `libunwind8-dev` | `libunwind` | `libunwind-devel` | YES | Stack traces | @@ -147,13 +148,14 @@ library archives (`.a`). | libreadline | 6.3.0 | NO | `libreadline6-dev` | `readline` | `readline-devel` | YES | Input editing | | ldns | 1.6.17 | NO | `libldns-dev` | `ldns` | `ldns-devel` | YES | SSL toolkit | | expat | 1.1 | NO | `libexpat1-dev` | `expat` | `expat-devel` | YES | XML parsing | -| GTest | 1.5 | YES | `libgtest-dev`^ | `gtest` | `gtest-devel` | YES | Test suite | +| GTest | 1.5 | YES | `libgtest-dev`[1] | `gtest` | `gtest-devel` | YES | Test suite | | Doxygen | any | NO | `doxygen` | `doxygen` | `doxygen` | YES | Documentation | | Graphviz | any | NO | `graphviz` | `graphviz` | `graphviz` | YES | Documentation | -[^] On Debian/Ubuntu `libgtest-dev` only includes sources and headers. You must +[1] On Debian/Ubuntu `libgtest-dev` only includes sources and headers. You must build the library binary manually. This can be done with the following command ```sudo apt-get install libgtest-dev && cd /usr/src/gtest && sudo cmake . && sudo make && sudo mv libg* /usr/lib/ ``` +[2] libnorm-dev is needed if your zmq library was built with libnorm, and not needed otherwise Debian / Ubuntu one liner for all dependencies ``` sudo apt update && sudo apt install build-essential cmake pkg-config libboost-all-dev libssl-dev libzmq3-dev libunbound-dev libsodium-dev libunwind8-dev liblzma-dev libreadline6-dev libldns-dev libexpat1-dev doxygen graphviz libpgm-dev``` From 6f28667aa3aea43b6bc697a9896d8e44fd12a7da Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 15 Oct 2018 22:10:49 +0000 Subject: [PATCH 0585/1404] daemon: fix reading past stack on exit --- src/daemon/daemon.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/daemon/daemon.cpp b/src/daemon/daemon.cpp index f536495182b..49d6d49cf71 100644 --- a/src/daemon/daemon.cpp +++ b/src/daemon/daemon.cpp @@ -137,13 +137,18 @@ bool t_daemon::run(bool interactive) throw std::runtime_error{"Can't run stopped daemon"}; } - std::atomic stop(false); - boost::thread([&stop, this] { + std::atomic stop(false), shutdown(false); + boost::thread stop_thread = boost::thread([&stop, &shutdown, this] { while (!stop) epee::misc_utils::sleep_no_w(100); - this->stop_p2p(); - }).detach(); - tools::signal_handler::install([&stop](int){ stop = true; }); + if (shutdown) + this->stop_p2p(); + }); + epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){ + stop = true; + stop_thread.join(); + }); + tools::signal_handler::install([&stop, &shutdown](int){ stop = shutdown = true; }); try { From 0fbbb065d4d384f6f412d866f45b5583ea6098ac Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 15 Oct 2018 22:39:51 +0000 Subject: [PATCH 0586/1404] p2p: a negative result from UPNP_GetValidIGD is an error as per the source documentation --- src/p2p/net_node.inl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index 9390626a8f7..705c4da4e28 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -2042,7 +2042,7 @@ namespace nodetool char lanAddress[64]; result = UPNP_GetValidIGD(deviceList, &urls, &igdData, lanAddress, sizeof lanAddress); freeUPNPDevlist(deviceList); - if (result != 0) { + if (result > 0) { if (result == 1) { std::ostringstream portString; portString << port; @@ -2088,7 +2088,7 @@ namespace nodetool char lanAddress[64]; result = UPNP_GetValidIGD(deviceList, &urls, &igdData, lanAddress, sizeof lanAddress); freeUPNPDevlist(deviceList); - if (result != 0) { + if (result > 0) { if (result == 1) { std::ostringstream portString; portString << port; From e7f24850d56d70c18930da97bffad20710451d4a Mon Sep 17 00:00:00 2001 From: iDunk5400 Date: Fri, 12 Oct 2018 15:20:42 +0000 Subject: [PATCH 0587/1404] Fix Windows build after epee dependency change --- CMakeLists.txt | 2 +- src/daemonizer/windows_daemonizer.inl | 1 + tests/functional_tests/CMakeLists.txt | 4 ---- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 78d16b2add9..8baac02e9b0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -880,7 +880,7 @@ endif() include_directories(SYSTEM ${Boost_INCLUDE_DIRS}) if(MINGW) set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wa,-mbig-obj") - set(EXTRA_LIBRARIES mswsock;ws2_32;iphlpapi;crypt32) + set(EXTRA_LIBRARIES mswsock;ws2_32;iphlpapi;crypt32;bcrypt) if(DEPENDS) set(ICU_LIBRARIES ${Boost_LOCALE_LIBRARY} sicuio sicuin sicuuc sicudt sicutu iconv) else() diff --git a/src/daemonizer/windows_daemonizer.inl b/src/daemonizer/windows_daemonizer.inl index 8077f29fb60..7e61e360367 100644 --- a/src/daemonizer/windows_daemonizer.inl +++ b/src/daemonizer/windows_daemonizer.inl @@ -31,6 +31,7 @@ #include "common/util.h" #include "daemonizer/windows_service.h" #include "daemonizer/windows_service_runner.h" +#include "cryptonote_core/cryptonote_core.h" #include #include diff --git a/tests/functional_tests/CMakeLists.txt b/tests/functional_tests/CMakeLists.txt index 4b21b945cd4..7a2a7fbd18a 100644 --- a/tests/functional_tests/CMakeLists.txt +++ b/tests/functional_tests/CMakeLists.txt @@ -26,10 +26,6 @@ # 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. -if(WIN32) - set(EXTRA_LIBRARIES "${EXTRA_LIBRARIES};bcrypt") -endif() - set(functional_tests_sources main.cpp transactions_flow_test.cpp From e623f2b225106933cf26330fc4814d50a5d72bfb Mon Sep 17 00:00:00 2001 From: TheCharlatan Date: Tue, 16 Oct 2018 03:39:17 +0200 Subject: [PATCH 0588/1404] Add building with depends to the Makefile Depends can now be compiled with `make depends target=$triple`, where $triple is one of the supported build targets. Adapt the Makefile for this change, remove not needed windows deps from depends setup description. --- Makefile | 4 ++++ README.md | 21 +++++++++------------ 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/Makefile b/Makefile index 40b8839ccea..c858412e629 100644 --- a/Makefile +++ b/Makefile @@ -41,6 +41,10 @@ endif all: release-all +depends: + cd contrib/depends && $(MAKE) HOST=$(target) && cd ../.. && mkdir -p build/$(target)/release + cd build/$(target)/release && cmake -DCMAKE_TOOLCHAIN_FILE=$(CURDIR)/contrib/depends/$(target)/share/toolchain.cmake ../../.. && $(MAKE) + cmake-debug: mkdir -p $(builddir)/debug cd $(builddir)/debug && cmake -D CMAKE_BUILD_TYPE=Debug $(topdir) diff --git a/README.md b/README.md index 4b5dd06292a..c9418f81cd8 100644 --- a/README.md +++ b/README.md @@ -494,22 +494,19 @@ By default, in either dynamically or statically linked builds, binaries target t ### Cross Compiling -You can also cross-compile static binaries on Linux for Windows and macOS with the `depends` system. Go to `contrib/depends` and type: +You can also cross-compile static binaries on Linux for Windows and macOS with the `depends` system. -* ```make HOST=x86_64-linux-gnu``` for 64-bit linux binaries. -* ```make HOST=x86_64-w64-mingw32``` for 64-bit windows binaries. Requires: python3 nsis g++-mingw-w64-x86-64 wine1.6 bc -* ```make HOST=x86_64-apple-darwin11``` for darwin binaries. Requires: cmake imagemagick libcap-dev librsvg2-bin libz-dev libbz2-dev libtiff-tools python-dev -* ```make HOST=i686-linux-gnu``` for 32-bit linux binaries. Requires: g++-multilib bc -* ```make HOST=i686-w64-mingw32``` for 32-bit windows binaries. Requires: python3 nsis g++-mingw-w64-i686 -* ```make HOST=arm-linux-gnueabihf``` for armv6 binaries. Requires: g++-arm-linux-gnueabihf +* ```make depends target=x86_64-linux-gnu``` for 64-bit linux binaries. +* ```make depends target=x86_64-w64-mingw32``` for 64-bit windows binaries. Requires: python3 g++-mingw-w64-x86-64 wine1.6 bc +* ```make depends target=x86_64-apple-darwin11``` for macOS binaries. Requires: cmake imagemagick libcap-dev librsvg2-bin libz-dev libbz2-dev libtiff-tools python-dev +* ```make depends target=i686-linux-gnu``` for 32-bit linux binaries. Requires: g++-multilib bc +* ```make depends target=i686-w64-mingw32``` for 32-bit windows binaries. Requires: python3 g++-mingw-w64-i686 +* ```make depends target=arm-linux-gnueabihf``` for armv7 binaries. Requires: g++-arm-linux-gnueabihf +* ```make depends target=aarch64-linux-gnu``` for armv8 binaries. Requires: g++-aarch64-linux-gnu The required packages are the names for each toolchain on apt. Depending on your distro, they may have different names. -Then go back to the source dir and type: -* ```cmake -DCMAKE_TOOLCHAIN_FILE=`pwd`/contrib/depends//share/toolchain.cmake``` -Where is one of the above mentioned targets. - -Using `depends` might also be easier to compile Monero on Windows than using MSys. Activate Windows Subsystem for Linux (WSL) with a distro (for example Ubuntu), install the apt build-essentials and follow the `depends` steps as depicted above. +Using `depends` might also be easier to compile Monero on Windows than using MSYS. Activate Windows Subsystem for Linux (WSL) with a distro (for example Ubuntu), install the apt build-essentials and follow the `depends` steps as depicted above. ## Installing Monero from a package From 9b6dd9348c6447c57e8c03675949871ab21b1f9a Mon Sep 17 00:00:00 2001 From: Lee Clagett Date: Mon, 15 Oct 2018 23:42:29 -0400 Subject: [PATCH 0589/1404] Providing user supplied default constructor for expect --- src/common/expect.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/common/expect.h b/src/common/expect.h index 32624250244..72e4060a7a9 100644 --- a/src/common/expect.h +++ b/src/common/expect.h @@ -350,7 +350,9 @@ class expect using error_type = std::error_code; //! Create a successful object. - expect() = default; + expect() noexcept + : code_() + {} expect(std::error_code const& code) noexcept : code_(code) From 45a6880d789414b85028c8e7b56f920f162d2d04 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 16 Oct 2018 09:16:36 +0000 Subject: [PATCH 0590/1404] unit_tests: call umask before mkstemp Coverity 188788 --- tests/unit_tests/notify.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/unit_tests/notify.cpp b/tests/unit_tests/notify.cpp index d6811c6bd5d..e2477a76e7e 100644 --- a/tests/unit_tests/notify.cpp +++ b/tests/unit_tests/notify.cpp @@ -26,6 +26,10 @@ // 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. +#ifdef __GLIBC__ +#include +#endif + #include "gtest/gtest.h" #include @@ -37,8 +41,14 @@ TEST(notify, works) { +#ifdef __GLIBC__ + mode_t prevmode = umask(077); +#endif char name_template[] = "/tmp/monero-notify-unit-test-XXXXXX"; int fd = mkstemp(name_template); +#ifdef __GLIBC__ + umask(prevmode); +#endif ASSERT_TRUE(fd >= 0); close(fd); From 2d48861db78f6495a1b4bf07b6fe653c818a066f Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 16 Oct 2018 09:17:21 +0000 Subject: [PATCH 0591/1404] p2p: only deinitialize what's been initialized in offline mode --- src/p2p/net_node.inl | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index 9390626a8f7..17de1897215 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -655,10 +655,14 @@ namespace nodetool { kill(); m_peerlist.deinit(); - m_net_server.deinit_server(); - // remove UPnP port mapping - if(!m_no_igd) - delete_upnp_port_mapping(m_listening_port); + + if (!m_offline) + { + m_net_server.deinit_server(); + // remove UPnP port mapping + if(!m_no_igd) + delete_upnp_port_mapping(m_listening_port); + } return store_config(); } //----------------------------------------------------------------------------------- From a7960542a9f8ea0ee0e90a24c411bb98f452c53b Mon Sep 17 00:00:00 2001 From: mmitkevich Date: Tue, 16 Oct 2018 14:58:22 +0300 Subject: [PATCH 0592/1404] WalletAPI: rescanBlockchain, rescanBlockchainAsync --- src/wallet/api/wallet.cpp | 26 ++++++++++++++++++++++++-- src/wallet/api/wallet.h | 3 +++ src/wallet/api/wallet2_api.h | 11 +++++++++++ 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index de1bfdae177..da6ee5d75d8 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -381,6 +381,7 @@ WalletImpl::WalletImpl(NetworkType nettype, uint64_t kdf_rounds) , m_synchronized(false) , m_rebuildWalletCache(false) , m_is_connected(false) + , m_refreshShouldRescan(false) { m_wallet.reset(new tools::wallet2(static_cast(nettype), kdf_rounds, true)); m_history.reset(new TransactionHistoryImpl(this)); @@ -1011,6 +1012,20 @@ void WalletImpl::refreshAsync() m_refreshCV.notify_one(); } +bool WalletImpl::rescanBlockchain() +{ + clearStatus(); + m_refreshShouldRescan = true; + doRefresh(); + return status() == Status_Ok; +} + +void WalletImpl::rescanBlockchainAsync() +{ + m_refreshShouldRescan = true; + refreshAsync(); +} + void WalletImpl::setAutoRefreshInterval(int millis) { if (millis > MAX_REFRESH_INTERVAL_MILLIS) { @@ -1984,6 +1999,7 @@ void WalletImpl::refreshThreadFunc() LOG_PRINT_L3(__FUNCTION__ << ": refresh lock acquired..."); LOG_PRINT_L3(__FUNCTION__ << ": m_refreshEnabled: " << m_refreshEnabled); LOG_PRINT_L3(__FUNCTION__ << ": m_status: " << status()); + LOG_PRINT_L3(__FUNCTION__ << ": m_refreshShouldRescan: " << m_refreshShouldRescan); if (m_refreshEnabled) { LOG_PRINT_L3(__FUNCTION__ << ": refreshing..."); doRefresh(); @@ -1994,12 +2010,16 @@ void WalletImpl::refreshThreadFunc() void WalletImpl::doRefresh() { + bool rescan = m_refreshShouldRescan.exchange(false); // synchronizing async and sync refresh calls boost::lock_guard guarg(m_refreshMutex2); - try { + do try { + LOG_PRINT_L3(__FUNCTION__ << ": doRefresh, rescan = "<light_wallet() || daemonSynced()) { + if(rescan) + m_wallet->rescan_blockchain(false); m_wallet->refresh(trustedDaemon()); if (!m_synchronized) { m_synchronized = true; @@ -2016,7 +2036,9 @@ void WalletImpl::doRefresh() } } catch (const std::exception &e) { setStatusError(e.what()); - } + break; + }while(!rescan && (rescan=m_refreshShouldRescan.exchange(false))); // repeat if not rescanned and rescan was requested + if (m_wallet2Callback->getListener()) { m_wallet2Callback->getListener()->refreshed(); } diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h index 6d343888bb1..b4637b8e645 100644 --- a/src/wallet/api/wallet.h +++ b/src/wallet/api/wallet.h @@ -114,6 +114,8 @@ class WalletImpl : public Wallet bool synchronized() const override; bool refresh() override; void refreshAsync() override; + bool rescanBlockchain() override; + void rescanBlockchainAsync() override; void setAutoRefreshInterval(int millis) override; int autoRefreshInterval() const override; void setRefreshFromBlockHeight(uint64_t refresh_from_block_height) override; @@ -232,6 +234,7 @@ class WalletImpl : public Wallet std::atomic m_refreshEnabled; std::atomic m_refreshThreadDone; std::atomic m_refreshIntervalMillis; + std::atomic m_refreshShouldRescan; // synchronizing refresh loop; boost::mutex m_refreshMutex; diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h index ec1a84877f4..82627de296a 100644 --- a/src/wallet/api/wallet2_api.h +++ b/src/wallet/api/wallet2_api.h @@ -643,6 +643,17 @@ struct Wallet */ virtual void refreshAsync() = 0; + /** + * @brief rescanBlockchain - rescans the wallet, updating transactions from daemon + * @return - true if refreshed successfully; + */ + virtual bool rescanBlockchain() = 0; + + /** + * @brief rescanBlockchainAsync - rescans wallet asynchronously, starting from genesys + */ + virtual void rescanBlockchainAsync() = 0; + /** * @brief setAutoRefreshInterval - setup interval for automatic refresh. * @param seconds - interval in millis. if zero or less than zero - automatic refresh disabled; From 4eca42b2411c7ff11fea7b9b64881509d90932e3 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 16 Oct 2018 10:09:57 +0000 Subject: [PATCH 0593/1404] blockchain_db: initialize m_hardfork in ctor just in case Coverity 136568 --- src/blockchain_db/blockchain_db.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/blockchain_db/blockchain_db.h b/src/blockchain_db/blockchain_db.h index 396ae754476..71c46d76b33 100644 --- a/src/blockchain_db/blockchain_db.h +++ b/src/blockchain_db/blockchain_db.h @@ -544,7 +544,7 @@ class BlockchainDB /** * @brief An empty constructor. */ - BlockchainDB(): m_open(false) { } + BlockchainDB(): m_hardfork(NULL), m_open(false) { } /** * @brief An empty destructor. From 32123789129372f1a4cb46a8aa289843e15a4b37 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 16 Oct 2018 10:14:27 +0000 Subject: [PATCH 0594/1404] wallet2: initialize some scalar fields in ctor where appropriate Coverity 188336 --- src/wallet/wallet2.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index d774c54c062..fae972cf8f8 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -809,6 +809,7 @@ wallet_keys_unlocker::~wallet_keys_unlocker() wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended): m_multisig_rescan_info(NULL), m_multisig_rescan_k(NULL), + m_upper_transaction_weight_limit(0), m_run(true), m_callback(0), m_trusted_daemon(false), @@ -841,6 +842,9 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended): m_is_initialized(false), m_kdf_rounds(kdf_rounds), is_old_file_format(false), + m_watch_only(false), + m_multisig(false), + m_multisig_threshold(0), m_node_rpc_proxy(m_http_client, m_daemon_rpc_mutex), m_subaddress_lookahead_major(SUBADDRESS_LOOKAHEAD_MAJOR), m_subaddress_lookahead_minor(SUBADDRESS_LOOKAHEAD_MINOR), From 7cc39845be1cfa9286dec589d4f1c94381b140d9 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 16 Oct 2018 10:22:24 +0000 Subject: [PATCH 0595/1404] account: init creation timestamp to 0 Never actually used uninitialized Coverity 136615 --- src/cryptonote_basic/account.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cryptonote_basic/account.cpp b/src/cryptonote_basic/account.cpp index e891a748db2..1dc1ad71d04 100644 --- a/src/cryptonote_basic/account.cpp +++ b/src/cryptonote_basic/account.cpp @@ -136,6 +136,7 @@ DISABLE_VS_WARNINGS(4244 4345) void account_base::set_null() { m_keys = account_keys(); + m_creation_timestamp = 0; } //----------------------------------------------------------------- void account_base::forget_spend_key() From bfa2dce171e2a04096c07f204037ccd13dc2fae6 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 16 Oct 2018 10:35:34 +0000 Subject: [PATCH 0596/1404] rpc: remove unused ctors Also prevents coverity from moaning about them not initializing fields --- src/rpc/core_rpc_server_commands_defs.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index 3b654d4cb4a..b229841d6df 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -782,9 +782,6 @@ namespace cryptonote std::string tx_as_hex; bool do_not_relay; - request() {} - explicit request(const transaction &); - BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(tx_as_hex) KV_SERIALIZE_OPT(do_not_relay, false) From 3ffbec1556f8817cb014fcea83bccc41747cfcf5 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 16 Oct 2018 10:36:49 +0000 Subject: [PATCH 0597/1404] rpc: init m_rpc_version in Message ctor Coverity 182501 --- src/rpc/message.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rpc/message.h b/src/rpc/message.h index 16b8e92fc9a..56087b99822 100644 --- a/src/rpc/message.h +++ b/src/rpc/message.h @@ -65,7 +65,7 @@ namespace rpc static const char* STATUS_BAD_REQUEST; static const char* STATUS_BAD_JSON; - Message() : status(STATUS_OK) { } + Message() : status(STATUS_OK), rpc_version(0) { } virtual ~Message() { } From ad4cce930b3d969feaf9225e07f9f13688f21fe7 Mon Sep 17 00:00:00 2001 From: xiphon Date: Tue, 16 Oct 2018 14:43:56 +0000 Subject: [PATCH 0598/1404] build: fixed submodule check --- CMakeLists.txt | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8baac02e9b0..72aec521a9e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -175,19 +175,21 @@ endif() if(NOT MANUAL_SUBMODULES) find_package(Git) if(GIT_FOUND) + function (check_submodule relative_path) + execute_process(COMMAND git -C ${relative_path} rev-parse "HEAD" WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} OUTPUT_VARIABLE localHead) + execute_process(COMMAND git rev-parse "HEAD:${relative_path}" WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} OUTPUT_VARIABLE checkedHead) + string(COMPARE EQUAL "${localHead}" "${checkedHead}" upToDate) + if (upToDate) + message(STATUS "Submodule '${relative_path}' is up-to-date") + else() + message(FATAL_ERROR "Submodule '${relative_path}' is not up-to-date. Please update with\ngit submodule update --init --force ${relative_path}\nor run cmake with -DMANUAL_SUBMODULES=1") + endif() + endfunction () + message(STATUS "Checking submodules") - execute_process(COMMAND bash -c "cd ${CMAKE_CURRENT_SOURCE_DIR}/external/miniupnp && git rev-parse HEAD" OUTPUT_VARIABLE miniupnpLocalHead WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) - execute_process(COMMAND bash -c "cd ${CMAKE_CURRENT_SOURCE_DIR}/external/unbound && git rev-parse HEAD" OUTPUT_VARIABLE unboundLocalHead WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) - execute_process(COMMAND bash -c "cd ${CMAKE_CURRENT_SOURCE_DIR}/external/rapidjson && git rev-parse HEAD" OUTPUT_VARIABLE rapidjsonLocalHead WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) - execute_process(COMMAND bash -c "git ls-tree HEAD ${CMAKE_CURRENT_SOURCE_DIR}/external/miniupnp | awk '{print $3}'" OUTPUT_VARIABLE miniupnpCheckedHead WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) - execute_process(COMMAND bash -c "git ls-tree HEAD ${CMAKE_CURRENT_SOURCE_DIR}/external/unbound | awk '{print $3}'" OUTPUT_VARIABLE unboundCheckedHead WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) - execute_process(COMMAND bash -c "git ls-tree HEAD ${CMAKE_CURRENT_SOURCE_DIR}/external/rapidjson | awk '{print $3}'" OUTPUT_VARIABLE rapidjsonCheckedHead WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) - string(COMPARE EQUAL "${miniupnpLocalHead}" "${miniupnpCheckedHead}" miniupnpUpToDate) - string(COMPARE EQUAL "${unboundLocalHead}" "${unboundCheckedHead}" unboundUpToDate) - string(COMPARE EQUAL "${rapidjsonLocalHead}" "${rapidjsonCheckedHead}" rapidjsonUpToDate) - if (NOT miniupnpUpToDate OR NOT unboundUpToDate OR NOT rapidjsonUpToDate) - message(FATAL_ERROR "Submodules not up to date. Please update with git submodule init && git submodule update, or run cmake with -DMANUAL_SUBMODULES=1") - endif() + check_submodule(external/miniupnp) + check_submodule(external/unbound) + check_submodule(external/rapidjson) endif() endif() From 0cdd4b074ebab612d7d0a6ebaf3efffb1f9435ef Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 16 Oct 2018 14:46:17 +0000 Subject: [PATCH 0599/1404] wallet2_api: fix generating new wallet in the GUI It was creating a new wallet without a password first (this should be fixed), then not changing the password correctly --- src/wallet/api/wallet.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index de1bfdae177..bb1ba83caf1 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -776,7 +776,7 @@ bool WalletImpl::setPassword(const std::string &password) { clearStatus(); try { - m_wallet->rewrite(m_wallet->get_wallet_file(), password); + m_wallet->change_password(m_wallet->get_wallet_file(), m_password, password); m_password = password; } catch (const std::exception &e) { setStatusError(e.what()); From 99d45a95787010fd9e38390b6ab350df8a738287 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 16 Oct 2018 15:16:32 +0000 Subject: [PATCH 0600/1404] wallet_rpc_server: fix change_wallet_password RPC --- src/wallet/wallet_rpc_server.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index e0b631aafb4..1b63d65b63b 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -2822,8 +2822,7 @@ namespace tools { try { - m_wallet->rewrite(m_wallet->get_wallet_file(), req.new_password); - m_wallet->store(); + m_wallet->change_password(m_wallet->get_wallet_file(), req.old_password, req.new_password); LOG_PRINT_L0("Wallet password changed."); } catch (const std::exception& e) From a69fc05a0cf8eb9d3247bbb0006546ba4994a310 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 16 Oct 2018 17:20:11 +0000 Subject: [PATCH 0601/1404] util: close keys file lock on exec --- src/common/util.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/util.cpp b/src/common/util.cpp index 2a1d49af0f1..7d8c9aa997a 100644 --- a/src/common/util.cpp +++ b/src/common/util.cpp @@ -233,7 +233,7 @@ namespace tools MERROR("Failed to open " << filename << ": " << std::error_code(GetLastError(), std::system_category())); } #else - m_fd = open(filename.c_str(), O_RDONLY | O_CREAT, 0666); + m_fd = open(filename.c_str(), O_RDONLY | O_CREAT | O_CLOEXEC, 0666); if (m_fd != -1) { if (flock(m_fd, LOCK_EX | LOCK_NB) == -1) From c77439298591792601f8c3f4c94950ef6e2c542a Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 16 Oct 2018 18:08:36 +0000 Subject: [PATCH 0602/1404] spawn: close all file descriptors before execve No need to give whatever we're calling access to what we use --- src/common/spawn.cpp | 3 +++ src/common/util.cpp | 20 ++++++++++++++++++++ src/common/util.h | 2 ++ 3 files changed, 25 insertions(+) diff --git a/src/common/spawn.cpp b/src/common/spawn.cpp index 59f11675cc5..0a2ce83872b 100644 --- a/src/common/spawn.cpp +++ b/src/common/spawn.cpp @@ -38,6 +38,7 @@ #endif #include "misc_log_ex.h" +#include "util.h" #include "spawn.h" namespace tools @@ -101,6 +102,8 @@ int spawn(const char *filename, const std::vector& args, bool wait) // child if (pid == 0) { + tools::closefrom(3); + close(0); char *envp[] = {NULL}; execve(filename, argv, envp); MERROR("Failed to execve: " << strerror(errno)); diff --git a/src/common/util.cpp b/src/common/util.cpp index 2a1d49af0f1..9afeb2607d9 100644 --- a/src/common/util.cpp +++ b/src/common/util.cpp @@ -28,6 +28,7 @@ // // Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers +#include #include #ifdef __GLIBC__ @@ -967,4 +968,23 @@ std::string get_nix_version_display_string() } #endif + void closefrom(int fd) + { +#if defined __FreeBSD__ || defined __OpenBSD__ || defined __NetBSD__ || defined __DragonFly__ + ::closefrom(fd); +#else +#if defined __GLIBC__ + const int sc_open_max = sysconf(_SC_OPEN_MAX); + const int MAX_FDS = std::min(65536, sc_open_max); +#else + const int MAX_FDS = 65536; +#endif + while (fd < MAX_FDS) + { + close(fd); + ++fd; + } +#endif + } + } diff --git a/src/common/util.h b/src/common/util.h index ce773bd386e..e793a42b552 100644 --- a/src/common/util.h +++ b/src/common/util.h @@ -238,4 +238,6 @@ namespace tools #ifdef _WIN32 std::string input_line_win(); #endif + + void closefrom(int fd); } From c39e0a1706500068bb58477e40b92ae411d47e8f Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 17 Oct 2018 16:22:27 +0000 Subject: [PATCH 0603/1404] core: don't verify range proofs multiple times --- src/cryptonote_core/cryptonote_core.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 69e3c708b7a..2fec6b613a9 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -856,16 +856,19 @@ namespace cryptonote } waiter.wait(&tpool); it = tx_blobs.begin(); + std::vector already_have(tx_blobs.size(), false); for (size_t i = 0; i < tx_blobs.size(); i++, ++it) { if (!results[i].res) continue; if(m_mempool.have_tx(results[i].hash)) { LOG_PRINT_L2("tx " << results[i].hash << "already have transaction in tx_pool"); + already_have[i] = true; } else if(m_blockchain_storage.have_tx(results[i].hash)) { LOG_PRINT_L2("tx " << results[i].hash << " already have transaction in blockchain"); + already_have[i] = true; } else { @@ -887,7 +890,7 @@ namespace cryptonote std::vector tx_info; tx_info.reserve(tx_blobs.size()); for (size_t i = 0; i < tx_blobs.size(); i++) { - if (!results[i].res) + if (!results[i].res || already_have[i]) continue; tx_info.push_back({&results[i].tx, results[i].hash, tvc[i], results[i].res}); } @@ -897,6 +900,8 @@ namespace cryptonote bool ok = true; it = tx_blobs.begin(); for (size_t i = 0; i < tx_blobs.size(); i++, ++it) { + if (already_have[i]) + continue; if (!results[i].res) { ok = false; From ec3013903b06768a04d5f33eabf38777977dd5f6 Mon Sep 17 00:00:00 2001 From: Cactii1 <37589158+Cactii1@users.noreply.github.com> Date: Wed, 17 Oct 2018 21:36:10 +0200 Subject: [PATCH 0604/1404] Update db_lmdb.cpp --- src/blockchain_db/lmdb/db_lmdb.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp index bd91f308ac5..d9c849cca39 100644 --- a/src/blockchain_db/lmdb/db_lmdb.cpp +++ b/src/blockchain_db/lmdb/db_lmdb.cpp @@ -1208,7 +1208,7 @@ void BlockchainLMDB::open(const std::string& filename, const int db_flags) if (is_hdd_result) { if (is_hdd_result.value()) - MCLOG_RED(el::Level::Warning, "global", "The blockchain is on a rotating drive: this will be very slow, use a SSD if possible"); + MCLOG_RED(el::Level::Warning, "global", "The blockchain is on a rotating drive: this will be very slow, use an SSD if possible"); } m_folder = filename; From 11415128118396a8e99774f4c9cc4aafbf319c53 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 17 Oct 2018 21:12:11 +0000 Subject: [PATCH 0605/1404] unit_tests: fix notify test when run from make *test --- tests/unit_tests/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit_tests/CMakeLists.txt b/tests/unit_tests/CMakeLists.txt index e248ed96510..7687e3c52fd 100644 --- a/tests/unit_tests/CMakeLists.txt +++ b/tests/unit_tests/CMakeLists.txt @@ -123,7 +123,7 @@ SET_PROPERTY(SOURCE memwipe.cpp PROPERTY COMPILE_FLAGS -Ofast) add_test( NAME unit_tests - COMMAND unit_tests --data-dir "${TEST_DATA_DIR} --binary-dir ${CMAKE_BINARY_DIR}") + COMMAND unit_tests --data-dir "${TEST_DATA_DIR}") add_executable(test_notifier test_notifier.cpp) target_link_libraries(test_notifier ${EXTRA_LIBRARIES}) From 776aefdac1f8e011d3e1fcc99cceb3bfb6e7d8f9 Mon Sep 17 00:00:00 2001 From: stoffu Date: Thu, 18 Oct 2018 08:01:56 +0900 Subject: [PATCH 0606/1404] tx_pool: revert #4592 and move bin2hex conversion to on_get_transaction_pool --- src/cryptonote_core/tx_pool.cpp | 2 +- src/rpc/core_rpc_server.cpp | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index 5a9fcf67e65..553a22298e4 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -765,7 +765,7 @@ namespace cryptonote m_blockchain.for_all_txpool_txes([&tx_infos, key_image_infos, include_sensitive_data](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd){ tx_info txi; txi.id_hash = epee::string_tools::pod_to_hex(txid); - txi.tx_blob = epee::string_tools::buff_to_hex_nodelimer(*bd); + txi.tx_blob = *bd; transaction tx; if (!parse_and_validate_tx_from_blob(*bd, tx)) { diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index d8f556d3e22..263ddf1bc63 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -924,6 +924,8 @@ namespace cryptonote return r; m_core.get_pool_transactions_and_spent_keys_info(res.transactions, res.spent_key_images, !request_has_rpc_origin || !m_restricted); + for (tx_info& txi : res.transactions) + txi.tx_blob = epee::string_tools::buff_to_hex_nodelimer(txi.tx_blob); res.status = CORE_RPC_STATUS_OK; return true; } From 825d836f9f6ee724cf5b44ce720ad0bc26730ba0 Mon Sep 17 00:00:00 2001 From: xiphon Date: Thu, 18 Oct 2018 04:37:30 +0000 Subject: [PATCH 0607/1404] device: fixed Ledger Nano S device selection --- src/device/device_io_hid.cpp | 6 +++--- src/device/device_io_hid.hpp | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/device/device_io_hid.cpp b/src/device/device_io_hid.cpp index 562aca8b8c7..0296914e12a 100644 --- a/src/device/device_io_hid.cpp +++ b/src/device/device_io_hid.cpp @@ -72,7 +72,7 @@ namespace hw { this->connect(p->vid, p->pid, p->interface_number, p->usage_page, p->interface_OR_page); } - void device_io_hid::connect(unsigned int vid, unsigned int pid, unsigned int interface_number, unsigned int usage_page, bool interface_OR_page ) { + void device_io_hid::connect(unsigned int vid, unsigned int pid, int interface_number, unsigned short usage_page, bool interface_OR_page ) { hid_device_info *hwdev_info, *hwdev_info_list; hid_device *hwdev; @@ -83,8 +83,8 @@ namespace hw { hwdev = NULL; hwdev_info = hwdev_info_list; while (hwdev_info) { - if ((interface_OR_page && ((usage_page == 0xffa0) || (interface_number == 0))) || - ((usage_page == 0xffa0) && (interface_number == 0)) ) { + if ((interface_OR_page && ((hwdev_info->usage_page == usage_page) || (hwdev_info->interface_number == interface_number))) || + ((hwdev_info->usage_page == usage_page) && (hwdev_info->interface_number == interface_number))) { MDEBUG("HID Device found: " << safe_hid_path(hwdev_info)); hwdev = hid_open_path(hwdev_info->path); break; diff --git a/src/device/device_io_hid.hpp b/src/device/device_io_hid.hpp index 6fd15a1d112..ffd3f534fe3 100644 --- a/src/device/device_io_hid.hpp +++ b/src/device/device_io_hid.hpp @@ -52,8 +52,8 @@ namespace hw { struct hid_conn_params { unsigned int vid; unsigned int pid; - unsigned int interface_number; - unsigned int usage_page; + int interface_number; + unsigned short usage_page; bool interface_OR_page ; }; @@ -100,7 +100,7 @@ namespace hw { void init(); void connect(void *params); - void connect(unsigned int vid, unsigned int pid, unsigned int interface_number, unsigned int usage_page, bool interface_OR_page ); + void connect(unsigned int vid, unsigned int pid, int interface_number, unsigned short usage_page, bool interface_OR_page ); bool connected() const; int exchange(unsigned char *command, unsigned int cmd_len, unsigned char *response, unsigned int max_resp_len); void disconnect(); From e54e5668a85fc5139077351e0035ef1318f2ef7f Mon Sep 17 00:00:00 2001 From: Ricardo de Vries Date: Thu, 18 Oct 2018 10:45:42 +0200 Subject: [PATCH 0608/1404] daemon: Show mining address --- src/daemon/rpc_command_executor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp index 6464d372fcd..9a0603a1009 100644 --- a/src/daemon/rpc_command_executor.cpp +++ b/src/daemon/rpc_command_executor.cpp @@ -449,7 +449,7 @@ bool t_rpc_command_executor::show_status() { % get_sync_percentage(ires) % (ires.testnet ? "testnet" : ires.stagenet ? "stagenet" : "mainnet") % bootstrap_msg - % (!has_mining_info ? "mining info unavailable" : mining_busy ? "syncing" : mres.active ? ( ( mres.is_background_mining_enabled ? "smart " : "" ) + std::string("mining at ") + get_mining_speed(mres.speed) ) : "not mining") + % (!has_mining_info ? "mining info unavailable" : mining_busy ? "syncing" : mres.active ? ( ( mres.is_background_mining_enabled ? "smart " : "" ) + std::string("mining at ") + get_mining_speed(mres.speed) + std::string(" to ") + mres.address ) : "not mining") % get_mining_speed(ires.difficulty / ires.target) % (unsigned)hfres.version % get_fork_extra_info(hfres.earliest_height, net_height, ires.target) From 6f88c03dfc2f2318a13e9c0177536aeeec44ea24 Mon Sep 17 00:00:00 2001 From: Ted Moravec Date: Thu, 18 Oct 2018 09:14:15 +0000 Subject: [PATCH 0609/1404] Simplewallet: update help text for show_transfers. Describe the output format. --- src/simplewallet/simplewallet.cpp | 10 +++++++++- translations/monero.ts | 11 ++++++++++- translations/monero_fr.ts | 11 ++++++++++- translations/monero_it.ts | 11 ++++++++++- translations/monero_sv.ts | 11 ++++++++++- 5 files changed, 49 insertions(+), 5 deletions(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 47b77abc691..7e0900edcbb 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -2484,7 +2484,15 @@ simple_wallet::simple_wallet() m_cmd_binder.set_handler("show_transfers", boost::bind(&simple_wallet::show_transfers, this, _1), tr("show_transfers [in|out|pending|failed|pool|coinbase] [index=[,,...]] [ []]"), - tr("Show the incoming/outgoing transfers within an optional height range.")); + // Seemingly broken formatting to compensate for the backslash before the quotes. + tr("Show the incoming/outgoing transfers within an optional height range.\n\n" + "Output format:\n" + "In or Coinbase: Block Number, \"block\"|\"in\", Time, Amount, Transaction Hash, Payment ID, Subaddress Index, \"-\", Note\n" + "Out: Block Number, \"out\", Time, Amount*, Transaction Hash, Payment ID, Fee, Destinations, Input addresses**, \"-\", Note\n" + "Pool: \"pool\", \"in\", Time, Amount, Transaction Hash, Payment Id, Subaddress Index, \"-\", Note, Double Spend Note\n" + "Pending or Failed: \"failed\"|\"pending\", \"out\", Time, Amount*, Transaction Hash, Payment ID, Fee, Input addresses**, \"-\", Note\n\n" + "* Excluding change and fee.\n" + "** Set of address indices used as inputs in this transfer.")); m_cmd_binder.set_handler("unspent_outputs", boost::bind(&simple_wallet::unspent_outputs, this, _1), tr("unspent_outputs [index=[,,...]] [ []]"), diff --git a/translations/monero.ts b/translations/monero.ts index 8d4ee7ab885..e28a2a05802 100644 --- a/translations/monero.ts +++ b/translations/monero.ts @@ -1727,7 +1727,16 @@ Otherwise, you prove the reserve of the smallest possible amount above <amoun - Show the incoming/outgoing transfers within an optional height range. + Show the incoming/outgoing transfers within an optional height range. + +Output format: +In or Coinbase: Block Number, "block"|"in", Time, Amount, Transaction Hash, Payment ID, Subaddress Index, "-", Note\ +Out: Block Number, "out", Time, Amount*, Transaction Hash, Payment ID, Fee, Destinations, Input addresses**, "-", Note +Pool: "pool", "in", Time, Amount, Transaction Hash, Payment Id, Subaddress Index, "-", Note, Double Spend Note\ +Pending or Failed: "failed"|"pending", "out", Time, Amount*, Transaction Hash, Payment ID, Fee, Input addresses**, "-", Note + +* Excluding change and fee. +** Set of address indices used as inputs in this transfer. diff --git a/translations/monero_fr.ts b/translations/monero_fr.ts index bac742c56dc..27c38ee1011 100644 --- a/translations/monero_fr.ts +++ b/translations/monero_fr.ts @@ -1789,7 +1789,16 @@ Sinon, vous prouvez le plus petit solde supérieur à <montant> dans votre - Show the incoming/outgoing transfers within an optional height range. + Show the incoming/outgoing transfers within an optional height range. + +Output format: +In or Coinbase: Block Number, "block"|"in", Time, Amount, Transaction Hash, Payment ID, Subaddress Index, "-", Note\ +Out: Block Number, "out", Time, Amount*, Transaction Hash, Payment ID, Fee, Destinations, Input addresses**, "-", Note +Pool: "pool", "in", Time, Amount, Transaction Hash, Payment Id, Subaddress Index, "-", Note, Double Spend Note\ +Pending or Failed: "failed"|"pending", "out", Time, Amount*, Transaction Hash, Payment ID, Fee, Input addresses**, "-", Note + +* Excluding change and fee. +** Set of address indices used as inputs in this transfer. Afficher les transferts entrants/sortants dans un interval de hauteurs facultatif. diff --git a/translations/monero_it.ts b/translations/monero_it.ts index d4b1695f30b..5ab96d7dc3d 100644 --- a/translations/monero_it.ts +++ b/translations/monero_it.ts @@ -1702,7 +1702,16 @@ Otherwise, you prove the reserve of the smallest possible amount above <amoun - Show the incoming/outgoing transfers within an optional height range. + Show the incoming/outgoing transfers within an optional height range. + +Output format: +In or Coinbase: Block Number, "block"|"in", Time, Amount, Transaction Hash, Payment ID, Subaddress Index, "-", Note\ +Out: Block Number, "out", Time, Amount*, Transaction Hash, Payment ID, Fee, Destinations, Input addresses**, "-", Note +Pool: "pool", "in", Time, Amount, Transaction Hash, Payment Id, Subaddress Index, "-", Note, Double Spend Note\ +Pending or Failed: "failed"|"pending", "out", Time, Amount*, Transaction Hash, Payment ID, Fee, Input addresses**, "-", Note + +* Excluding change and fee. +** Set of address indices used as inputs in this transfer. diff --git a/translations/monero_sv.ts b/translations/monero_sv.ts index 3c5ddf9afc8..b2387cdb196 100644 --- a/translations/monero_sv.ts +++ b/translations/monero_sv.ts @@ -1855,7 +1855,16 @@ Annars bevisar du reserven för det minsta möjliga belopp över <belopp> - Show the incoming/outgoing transfers within an optional height range. + Show the incoming/outgoing transfers within an optional height range. + +Output format: +In or Coinbase: Block Number, "block"|"in", Time, Amount, Transaction Hash, Payment ID, Subaddress Index, "-", Note\ +Out: Block Number, "out", Time, Amount*, Transaction Hash, Payment ID, Fee, Destinations, Input addresses**, "-", Note +Pool: "pool", "in", Time, Amount, Transaction Hash, Payment Id, Subaddress Index, "-", Note, Double Spend Note\ +Pending or Failed: "failed"|"pending", "out", Time, Amount*, Transaction Hash, Payment ID, Fee, Input addresses**, "-", Note + +* Excluding change and fee. +** Set of address indices used as inputs in this transfer. Visa inkommande/utgående överföringar inom ett valfritt höjdintervall. From 109717a5fdfc9251997cab4933af6c8608d2bf9e Mon Sep 17 00:00:00 2001 From: TheCharlatan Date: Wed, 17 Oct 2018 13:56:01 +0200 Subject: [PATCH 0610/1404] Remove Travis check in depends toolchain file The architecture for darwin is now detected correctly, remove the override for it. --- .travis.yml | 2 +- contrib/depends/toolchain.cmake.in | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6a9b9b94b92..ca3a24ae82b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -58,7 +58,7 @@ script: - export TRAVIS_COMMIT_LOG=`git log --format=fuller -1` - OUTDIR=$BASE_OUTDIR/$TRAVIS_PULL_REQUEST/$TRAVIS_JOB_NUMBER-$HOST - if [ -z "$NO_DEPENDS" ]; then $DOCKER_EXEC ccache --max-size=$CCACHE_SIZE; fi - - $DOCKER_EXEC bash -c "mkdir build && cd build && cmake -DCMAKE_TOOLCHAIN_FILE=$TRAVIS_BUILD_DIR/contrib/depends/$HOST/share/toolchain.cmake -DTRAVIS=true .. && make $MAKEJOBS" + - $DOCKER_EXEC bash -c "mkdir build && cd build && cmake -DCMAKE_TOOLCHAIN_FILE=$TRAVIS_BUILD_DIR/contrib/depends/$HOST/share/toolchain.cmake .. && make $MAKEJOBS" - export LD_LIBRARY_PATH=$TRAVIS_BUILD_DIR/contrib/depends/$HOST/lib after_script: - echo $TRAVIS_COMMIT_RANGE diff --git a/contrib/depends/toolchain.cmake.in b/contrib/depends/toolchain.cmake.in index 66168facb41..f59b7b5eed4 100644 --- a/contrib/depends/toolchain.cmake.in +++ b/contrib/depends/toolchain.cmake.in @@ -51,9 +51,6 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Darwin") SET(APPLE True) SET(BUILD_TAG "mac-x64") SET(BUILD_64 ON) - if(NOT TRAVIS) - SET(ARCH "x86_64") - endif() SET(BREW OFF) SET(PORT OFF) SET(CMAKE_OSX_SYSROOT "@sdk@/MacOSX10.11.sdk/") From 8f96c718bc5b3844904b04868d5605778af41c39 Mon Sep 17 00:00:00 2001 From: TheCharlatan Date: Thu, 11 Oct 2018 02:05:00 +0200 Subject: [PATCH 0611/1404] Adapt Readme and script to monero gitian build signing The sigs should be produced in a seperate step by default. Remove windows and osx sig options that are not needed for monero. --- contrib/gitian/README.md | 78 ++++++++++++++------------------- contrib/gitian/gitian-build.py | 36 +++------------ contrib/gitian/gitian-linux.yml | 2 +- contrib/gitian/gitian-osx.yml | 2 +- contrib/gitian/gitian-win.yml | 2 +- 5 files changed, 43 insertions(+), 77 deletions(-) diff --git a/contrib/gitian/README.md b/contrib/gitian/README.md index 1f7d158eb45..4bd326f229f 100644 --- a/contrib/gitian/README.md +++ b/contrib/gitian/README.md @@ -3,8 +3,8 @@ Gitian building *Setup instructions for a Gitian build of Monero using a VM or physical system.* -Gitian is the deterministic build process that is used to build the Bitcoin -Core executables. It provides a way to be reasonably sure that the +Gitian is the deterministic build process that is used to build the Monero CLI +executables. It provides a way to be reasonably sure that the executables are really built from the git source. It also makes sure that the same, tested dependencies are used and statically built into the executable. @@ -22,8 +22,7 @@ Table of Contents Please note that these instructions have been forked from bitcoin's gitian build instructions. Please also consult their documentation, when running into problems. -The signing is left as inherited from bitcoin at the moment, since building currently -still fails with libiconv. +The signing is left as inherited from bitcoin at the moment. - [Preparing the Gitian builder host](#preparing-the-gitian-builder-host) - [Getting and building the inputs](#getting-and-building-the-inputs) @@ -37,9 +36,10 @@ Preparing the Gitian builder host The first step is to prepare the host environment that will be used to perform the Gitian builds. This guide explains how to set up the environment, and how to start the builds. -Gitian builds are for now executed on Ubuntu 18.04 "Bionic Beaver". Please run Ubuntu in either a VM, or on your physical machine. +Gitian builds are for now executed on Ubuntu 18.04 "Bionic Beaver". A solution is being worked on to run +it in docker in the future. Please run Ubuntu in either a VM, or on your physical machine. You need to be logged in as the `gitianuser` in order to build gitian builds. If this user does not exist yet on your system, -create him. +create it. Note that a version of `lxc-execute` higher or equal to 2.1.1 is required. You can check the version with `lxc-execute --version`. @@ -79,11 +79,8 @@ This setup is required to enable networking in the container. Manual and Building ------------------- -The instructions below use the automated script [gitian-build.py](https://github.com/betcoin/bitcoin/blob/master/contrib/gitian-build.py) which only works in Ubuntu. For manual steps and instructions for fully offline signing, see [this guide](./gitian-building/gitian-building-manual.md). - -MacOS code signing ------------------- -In order to sign builds for MacOS, you need to download the free SDK and extract a file. The steps are described [here](./gitian-building/gitian-building-mac-os-sdk.md). Alternatively, you can skip the OSX build by adding `--os=lw` below. +The instructions below use the automated script [gitian-build.py](https://github.com/betcoin/bitcoin/blob/master/contrib/gitian-build.py) which only works in Ubuntu. +It calls all available descriptors. Help for the build steps taken can be accessed with `./gitian-build.py --help`. Initial Gitian Setup -------------------- @@ -93,66 +90,57 @@ The `gitian-build.py` script will checkout different release tags, so it's best cp monero/contrib/gitian/gitian-build.py . ``` -You only need to do this once: +Setup the required environment, you only need to do this once: ``` -./gitian-build.py --setup fluffypony 0.0.20 +./gitian-build.py --setup fluffypony 0.14.0 ``` -Where `fluffypony` is your Github name and `0.0.20` is the most recent tag (without `v`). +Where `fluffypony` is your Github name and `0.14.0` is the version tag you want to build (without `v`). -In order to sign gitian builds on your host machine, which has your PGP key, fork the gitian.sigs repository and clone it on your host machine: +While gitian and this build script does provide a way for you to sign the build directly, it is recommended to sign in a seperate step. +This script is only there for convenience. Seperate steps for building can still be taken. +In order to sign gitian builds on your host machine, which has your PGP key, +fork the gitian.sigs repository and clone it on your host machine, +or pass the signed assert file back to your build machine. ``` -git clone git@github.com:bitcoin-core/gitian.sigs.git -git remote add satoshi git@github.com:satoshi/gitian.sigs.git +git clone git@github.com:monero-project/gitian.sigs.git +git remote add fluffypony git@github.com:fluffypony/gitian.sigs.git ``` Build Binaries ----------------------------- -Windows and OSX have code signed binaries, but those won't be available until a few developers have gitian signed the non-codesigned binaries. - To build the most recent tag: - `./gitian-build.py --detach-sign --no-commit -b fluffypony 0.0.20` + `./gitian-build.py --detach-sign --no-commit -b fluffypony 0.14.0` To speed up the build, use `-j 5 -m 5000` as the first arguments, where `5` is the number of CPU's you allocated to the VM plus one, and 5000 is a little bit less than then the MB's of RAM you allocated. If there is memory corruption on your machine, try to tweak these values. If all went well, this produces a number of (uncommited) `.assert` files in the gitian.sigs repository. -You need to copy these uncommited changes to your host machine, where you can sign them: +If you do detached, offline signing, you need to copy these uncommited changes to your host machine, where you can sign them. For example: ``` -export NAME=satoshi -gpg --output $VERSION-linux/$NAME/bitcoin-linux-0.16-build.assert.sig --detach-sign 0.16.0rc1-linux/$NAME/bitcoin-linux-0.16-build.assert -gpg --output $VERSION-osx-unsigned/$NAME/bitcoin-osx-0.16-build.assert.sig --detach-sign 0.16.0rc1-osx-unsigned/$NAME/bitcoin-osx-0.16-build.assert -gpg --output $VERSION-win-unsigned/$NAME/bitcoin-win-0.16-build.assert.sig --detach-sign 0.16.0rc1-win-unsigned/$NAME/bitcoin-win-0.16-build.assert +export NAME=fluffypony +export VERSION=0.14 +gpg --output $VERSION-linux/$NAME/monero-linux-$VERSION-build.assert.sig --detach-sign $VERSION-linux/$NAME/monero-linux-$VERSION-build.assert +gpg --output $VERSION-osx-unsigned/$NAME/monero-osx-$VERSION-build.assert.sig --detach-sign $VERSION-osx-unsigned/$NAME/monero-osx-$VERSION-build.assert +gpg --output $VERSION-win-unsigned/$NAME/monero-win-$VERSION-build.assert.sig --detach-sign $VERSION-win-unsigned/$NAME/monero-win-$VERSION-build.assert ``` -Make a PR (both the `.assert` and `.assert.sig` files) to the -[bitcoin-core/gitian.sigs](https://github.com/bitcoin-core/gitian.sigs/) repository: +Make a pull request (both the `.assert` and `.assert.sig` files) to the +[monero-project/gitian.sigs](https://github.com/monero-project/gitian.sigs/) repository: ``` -git checkout -b 0.0.20-not-codesigned -git commit -S -a -m "Add $NAME 0.0.20 non-code signed signatures" -git push --set-upstream $NAME 0.0.20 +git checkout -b 0.14.0 +git commit -S -a -m "Add $NAME 0.14.0" +git push --set-upstream $NAME 0.14.0 ``` -You can also mail the files to Wladimir (laanwj@gmail.com) and he will commit them. - ```bash - gpg --detach-sign ${VERSION}-linux/${SIGNER}/bitcoin-linux-*-build.assert - gpg --detach-sign ${VERSION}-win-unsigned/${SIGNER}/bitcoin-win-*-build.assert - gpg --detach-sign ${VERSION}-osx-unsigned/${SIGNER}/bitcoin-osx-*-build.assert + gpg --detach-sign ${VERSION}-linux/${SIGNER}/monero-linux-*-build.assert + gpg --detach-sign ${VERSION}-win-unsigned/${SIGNER}/monero-win-*-build.assert + gpg --detach-sign ${VERSION}-osx-unsigned/${SIGNER}/monero-osx-*-build.assert ``` -You may have other .assert files as well (e.g. `signed` ones), in which case you should sign them too. You can see all of them by doing `ls ${VERSION}-*/${SIGNER}`. - -This will create the `.sig` files that can be committed together with the `.assert` files to assert your -Gitian build. - - - `./gitian-build.py --detach-sign -s satoshi 0.16.0rc1 --nocommit` - -Make another pull request for these. - diff --git a/contrib/gitian/gitian-build.py b/contrib/gitian/gitian-build.py index eb138e41a2c..99c64e9dd02 100755 --- a/contrib/gitian/gitian-build.py +++ b/contrib/gitian/gitian-build.py @@ -65,13 +65,13 @@ def build(): if args.windows: print('\nCompiling ' + args.version + ' Windows') subprocess.check_call(['bin/gbuild', '-j', args.jobs, '-m', args.memory, '--commit', 'monero='+args.commit, '--url', 'monero='+args.url, '../monero/contrib/gitian/gitian-win.yml']) - subprocess.check_call(['bin/gsign', '-p', args.sign_prog, '--signer', args.signer, '--release', args.version+'-win-unsigned', '--destination', '../gitian.sigs/', '../monero/contrib/gitian/gitian-win.yml']) + subprocess.check_call(['bin/gsign', '-p', args.sign_prog, '--signer', args.signer, '--release', args.version+'-win', '--destination', '../gitian.sigs/', '../monero/contrib/gitian/gitian-win.yml']) subprocess.check_call('mv build/out/monero*.zip ../monero-binaries/'+args.version, shell=True) if args.macos: print('\nCompiling ' + args.version + ' MacOS') subprocess.check_call(['bin/gbuild', '-j', args.jobs, '-m', args.memory, '--commit', 'monero='+args.commit, '--url', 'monero'+args.url, '../monero/contrib/gitian/gitian-osx.yml']) - subprocess.check_call(['bin/gsign', '-p', args.sign_prog, '--signer', args.signer, '--release', args.version+'-osx-unsigned', '--destination', '../gitian.sigs/', '../monero/contrib/gitian/gitian-osx.yml']) + subprocess.check_call(['bin/gsign', '-p', args.sign_prog, '--signer', args.signer, '--release', args.version+'-osx', '--destination', '../gitian.sigs/', '../monero/contrib/gitian/gitian-osx.yml']) subprocess.check_call('mv build/out/monero*.tar.gz ../monero-binaries/'+args.version, shell=True) os.chdir(workdir) @@ -80,24 +80,11 @@ def build(): print('\nCommitting '+args.version+' Unsigned Sigs\n') os.chdir('gitian.sigs') subprocess.check_call(['git', 'add', args.version+'-linux/'+args.signer]) - subprocess.check_call(['git', 'add', args.version+'-win-unsigned/'+args.signer]) - subprocess.check_call(['git', 'add', args.version+'-osx-unsigned/'+args.signer]) + subprocess.check_call(['git', 'add', args.version+'-win/'+args.signer]) + subprocess.check_call(['git', 'add', args.version+'-osx/'+args.signer]) subprocess.check_call(['git', 'commit', '-m', 'Add '+args.version+' unsigned sigs for '+args.signer]) os.chdir(workdir) -def sign(): - global args, workdir - os.chdir('gitian-builder') - os.chdir(workdir) - - if args.commit_files: - print('\nCommitting '+args.version+' Signed Sigs\n') - os.chdir('gitian.sigs') - subprocess.check_call(['git', 'add', args.version+'-win-signed/'+args.signer]) - subprocess.check_call(['git', 'add', args.version+'-osx-signed/'+args.signer]) - subprocess.check_call(['git', 'commit', '-a', '-m', 'Add '+args.version+' signed binary sigs for '+args.signer]) - os.chdir(workdir) - def verify(): global args, workdir os.chdir('gitian-builder') @@ -105,14 +92,9 @@ def verify(): print('\nVerifying v'+args.version+' Linux\n') subprocess.check_call(['bin/gverify', '-v', '-d', '../gitian.sigs/', '-r', args.version+'-linux', '../monero/contrib/gitian/gitian-linux.yml']) print('\nVerifying v'+args.version+' Windows\n') - subprocess.check_call(['bin/gverify', '-v', '-d', '../gitian.sigs/', '-r', args.version+'-win-unsigned', '../monero/contrib/gitian/gitian-win.yml']) + subprocess.check_call(['bin/gverify', '-v', '-d', '../gitian.sigs/', '-r', args.version+'-win', '../monero/contrib/gitian/gitian-win.yml']) print('\nVerifying v'+args.version+' MacOS\n') - subprocess.check_call(['bin/gverify', '-v', '-d', '../gitian.sigs/', '-r', args.version+'-osx-unsigned', '../monero/contrib/gitian/gitian-osx.yml']) - print('\nVerifying v'+args.version+' Signed Windows\n') - subprocess.check_call(['bin/gverify', '-v', '-d', '../gitian.sigs/', '-r', args.version+'-win-signed', '../monero/contrib/gitian/gitian-win-signer.yml']) - print('\nVerifying v'+args.version+' Signed MacOS\n') - subprocess.check_call(['bin/gverify', '-v', '-d', '../gitian.sigs/', '-r', args.version+'-osx-signed', '../monero/contrib/gitian/gitian-osx-signer.yml']) - + subprocess.check_call(['bin/gverify', '-v', '-d', '../gitian.sigs/', '-r', args.version+'-osx', '../monero/contrib/gitian/gitian-osx.yml']) os.chdir(workdir) def main(): @@ -124,7 +106,6 @@ def main(): parser.add_argument('-u', '--url', dest='url', default='https://github.com/monero-project/monero', help='Specify the URL of the repository. Default is %(default)s') parser.add_argument('-v', '--verify', action='store_true', dest='verify', help='Verify the Gitian build') parser.add_argument('-b', '--build', action='store_true', dest='build', help='Do a Gitian build') - parser.add_argument('-s', '--sign', action='store_true', dest='sign', help='Make signed binaries for Windows and MacOS') parser.add_argument('-B', '--buildsign', action='store_true', dest='buildsign', help='Build both signed and unsigned binaries') parser.add_argument('-o', '--os', dest='os', default='lwm', help='Specify which Operating Systems the build is for. Default is %(default)s. l for Linux, w for Windows, m for MacOS') parser.add_argument('-j', '--jobs', dest='jobs', default='2', help='Number of processes to use. Default %(default)s') @@ -184,7 +165,7 @@ def main(): # Add leading 'v' for tags if args.commit and args.pull: raise Exception('Cannot have both commit and pull') - args.commit = ('' if args.commit else 'v') + args.version + args.commit = ('' if args.commit else) + args.version if args.setup: setup() @@ -204,9 +185,6 @@ def main(): if args.build: build() - if args.sign: - sign() - if args.verify: verify() diff --git a/contrib/gitian/gitian-linux.yml b/contrib/gitian/gitian-linux.yml index 6c338f1da0a..473a7720df2 100644 --- a/contrib/gitian/gitian-linux.yml +++ b/contrib/gitian/gitian-linux.yml @@ -1,5 +1,5 @@ --- -name: "monero-linux-0.18" +name: "monero-linux-0.14" enable_cache: true suites: - "bionic" diff --git a/contrib/gitian/gitian-osx.yml b/contrib/gitian/gitian-osx.yml index d9ebf95d3ee..a6fcff0dadd 100644 --- a/contrib/gitian/gitian-osx.yml +++ b/contrib/gitian/gitian-osx.yml @@ -1,5 +1,5 @@ --- -name: "bitcoin-osx-0.18" +name: "monero-osx-0.14" enable_cache: true suites: - "bionic" diff --git a/contrib/gitian/gitian-win.yml b/contrib/gitian/gitian-win.yml index 74c757c11fe..fef5567f9f7 100644 --- a/contrib/gitian/gitian-win.yml +++ b/contrib/gitian/gitian-win.yml @@ -1,5 +1,5 @@ --- -name: "bitcoin-win-0.18" +name: "monero-win-0.14" enable_cache: true suites: - "bionic" From 25d327e796e60b2dceab4aeab26fbc4edf7a5537 Mon Sep 17 00:00:00 2001 From: xiphon Date: Thu, 18 Oct 2018 03:33:42 +0000 Subject: [PATCH 0612/1404] device: extended logging, refactored device selection code --- src/device/device_io_hid.cpp | 56 +++++++++++++++++++++++++++--------- src/device/device_io_hid.hpp | 8 ++---- src/device/device_ledger.cpp | 2 +- 3 files changed, 46 insertions(+), 20 deletions(-) diff --git a/src/device/device_io_hid.cpp b/src/device/device_io_hid.cpp index 0296914e12a..666255cb31a 100644 --- a/src/device/device_io_hid.cpp +++ b/src/device/device_io_hid.cpp @@ -13,6 +13,7 @@ // #if defined(HAVE_HIDAPI) +#include #include "log.hpp" #include "device_io_hid.hpp" @@ -69,11 +70,47 @@ namespace hw { void device_io_hid::connect(void *params) { hid_conn_params *p = (struct hid_conn_params*)params; - this->connect(p->vid, p->pid, p->interface_number, p->usage_page, p->interface_OR_page); + this->connect(p->vid, p->pid, p->interface_number, p->usage_page); } - void device_io_hid::connect(unsigned int vid, unsigned int pid, int interface_number, unsigned short usage_page, bool interface_OR_page ) { - hid_device_info *hwdev_info, *hwdev_info_list; + hid_device_info *device_io_hid::find_device(hid_device_info *devices_list, boost::optional interface_number, boost::optional usage_page) { + bool select_any = !interface_number && !usage_page; + + MDEBUG( "Looking for " << + (select_any ? "any HID Device" : "HID Device with") << + (interface_number ? (" interface_number " + std::to_string(interface_number.value())) : "") << + ((interface_number && usage_page) ? " or" : "") << + (usage_page ? (" usage_page " + std::to_string(usage_page.value())) : "")); + + hid_device_info *result = nullptr; + for (; devices_list != nullptr; devices_list = devices_list->next) { + BOOST_SCOPE_EXIT(&devices_list, &result) { + MDEBUG( (result == devices_list ? "SELECTED" : "SKIPPED ") << + " HID Device" << + " path " << safe_hid_path(devices_list) << + " interface_number " << devices_list->interface_number << + " usage_page " << devices_list->usage_page); + } + BOOST_SCOPE_EXIT_END + + if (result != nullptr) { + continue; + } + + if (select_any) { + result = devices_list; + } else if (interface_number && devices_list->interface_number == interface_number.value()) { + result = devices_list; + } else if (usage_page && devices_list->usage_page == usage_page.value()) { + result = devices_list; + } + } + + return result; + } + + void device_io_hid::connect(unsigned int vid, unsigned int pid, boost::optional interface_number, boost::optional usage_page) { + hid_device_info *hwdev_info_list; hid_device *hwdev; this->disconnect(); @@ -81,17 +118,8 @@ namespace hw { hwdev_info_list = hid_enumerate(vid, pid); ASSERT_X(hwdev_info_list, "Unable to enumerate device "+std::to_string(vid)+":"+std::to_string(vid)+ ": "+ safe_hid_error(this->usb_device)); hwdev = NULL; - hwdev_info = hwdev_info_list; - while (hwdev_info) { - if ((interface_OR_page && ((hwdev_info->usage_page == usage_page) || (hwdev_info->interface_number == interface_number))) || - ((hwdev_info->usage_page == usage_page) && (hwdev_info->interface_number == interface_number))) { - MDEBUG("HID Device found: " << safe_hid_path(hwdev_info)); - hwdev = hid_open_path(hwdev_info->path); - break; - } else { - MDEBUG("HID Device discard: " << safe_hid_path(hwdev_info) << "("+std::to_string(hwdev_info->usage_page) << "," << std::to_string(hwdev_info->interface_number) << ")"); - } - hwdev_info = hwdev_info->next; + if (hid_device_info *device = find_device(hwdev_info_list, interface_number, usage_page)) { + hwdev = hid_open_path(device->path); } hid_free_enumeration(hwdev_info_list); ASSERT_X(hwdev, "Unable to open device "+std::to_string(pid)+":"+std::to_string(vid)); diff --git a/src/device/device_io_hid.hpp b/src/device/device_io_hid.hpp index ffd3f534fe3..bb0f0a8148e 100644 --- a/src/device/device_io_hid.hpp +++ b/src/device/device_io_hid.hpp @@ -29,6 +29,7 @@ #if defined(HAVE_HIDAPI) +#include #include #include "device_io.hpp" @@ -54,7 +55,6 @@ namespace hw { unsigned int pid; int interface_number; unsigned short usage_page; - bool interface_OR_page ; }; @@ -82,13 +82,11 @@ namespace hw { unsigned int wrapCommand(const unsigned char *command, size_t command_len, unsigned char *out, size_t out_len); unsigned int unwrapReponse(const unsigned char *data, size_t data_len, unsigned char *out, size_t out_len); + hid_device_info *find_device(hid_device_info *devices_list, boost::optional interface_number, boost::optional usage_page); public: bool hid_verbose = false; - static const unsigned int OR_SELECT = 1; - static const unsigned int AND_SELECT = 2; - static const unsigned short DEFAULT_CHANNEL = 0x0001; static const unsigned char DEFAULT_TAG = 0x01; static const unsigned int DEFAULT_PACKET_SIZE = 64; @@ -100,7 +98,7 @@ namespace hw { void init(); void connect(void *params); - void connect(unsigned int vid, unsigned int pid, int interface_number, unsigned short usage_page, bool interface_OR_page ); + void connect(unsigned int vid, unsigned int pid, boost::optional interface_number, boost::optional usage_page); bool connected() const; int exchange(unsigned char *command, unsigned int cmd_len, unsigned char *response, unsigned int max_resp_len); void disconnect(); diff --git a/src/device/device_ledger.cpp b/src/device/device_ledger.cpp index a1778496007..d879ee95a0d 100644 --- a/src/device/device_ledger.cpp +++ b/src/device/device_ledger.cpp @@ -340,7 +340,7 @@ namespace hw { bool device_ledger::connect(void) { this->disconnect(); - hw_device.connect(0x2c97,0x0001, 0, 0xffa0, hw_device.OR_SELECT); + hw_device.connect(0x2c97, 0x0001, 0, 0xffa0); this->reset(); #ifdef DEBUG_HWDEVICE cryptonote::account_public_address pubkey; From 99cd6f961a15d5a16668be44c200b5f78024e618 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 18 Oct 2018 18:05:51 +0000 Subject: [PATCH 0613/1404] Rename "blackball" for clarity Apparently some people seem to think it's a censorship list... --- src/blockchain_utilities/CMakeLists.txt | 2 +- .../blockchain_blackball.cpp | 24 ++++++------- src/simplewallet/simplewallet.cpp | 34 +++++++++---------- src/wallet/api/wallet.cpp | 6 ++-- src/wallet/ringdb.cpp | 4 +-- src/wallet/wallet2.cpp | 2 +- tests/unit_tests/ringdb.cpp | 10 +++--- 7 files changed, 41 insertions(+), 41 deletions(-) diff --git a/src/blockchain_utilities/CMakeLists.txt b/src/blockchain_utilities/CMakeLists.txt index ecd7b754c91..24a750eb03d 100644 --- a/src/blockchain_utilities/CMakeLists.txt +++ b/src/blockchain_utilities/CMakeLists.txt @@ -184,7 +184,7 @@ target_link_libraries(blockchain_blackball set_property(TARGET blockchain_blackball PROPERTY - OUTPUT_NAME "monero-blockchain-blackball") + OUTPUT_NAME "monero-blockchain-mark-spent-outputs") install(TARGETS blockchain_blackball DESTINATION bin) diff --git a/src/blockchain_utilities/blockchain_blackball.cpp b/src/blockchain_utilities/blockchain_blackball.cpp index 03ff3cdcd8e..d2ce5cf7672 100644 --- a/src/blockchain_utilities/blockchain_blackball.cpp +++ b/src/blockchain_utilities/blockchain_blackball.cpp @@ -226,7 +226,7 @@ static void init(std::string cache_filename) bool tx_active = false; int dbr; - MINFO("Creating blackball cache in " << cache_filename); + MINFO("Creating spent output cache in " << cache_filename); tools::create_directories_if_necessary(cache_filename); @@ -1019,7 +1019,7 @@ int main(int argc, char* argv[]) po::options_description desc_cmd_only("Command line options"); po::options_description desc_cmd_sett("Command line options and settings options"); const command_line::arg_descriptor arg_blackball_db_dir = { - "blackball-db-dir", "Specify blackball database directory", + "spent-output-db-dir", "Specify spent output database directory", get_default_db_path(), }; const command_line::arg_descriptor arg_log_level = {"log-level", "0-4 or categories", ""}; @@ -1076,7 +1076,7 @@ int main(int argc, char* argv[]) return 1; } - mlog_configure(mlog_get_default_log_path("monero-blockchain-blackball.log"), true); + mlog_configure(mlog_get_default_log_path("monero-blockchain-find-spent-outputs.log"), true); if (!command_line::is_arg_defaulted(vm, arg_log_level)) mlog_set_log(command_line::get_arg(vm, arg_log_level).c_str()); else @@ -1114,10 +1114,10 @@ int main(int argc, char* argv[]) return 1; } - const std::string cache_dir = (output_file_path / "blackball-cache").string(); + const std::string cache_dir = (output_file_path / "spent-outputs-cache").string(); init(cache_dir); - LOG_PRINT_L0("Scanning for blackballable outputs..."); + LOG_PRINT_L0("Scanning for spent outputs..."); size_t done = 0; @@ -1215,7 +1215,7 @@ int main(int argc, char* argv[]) const std::pair output = std::make_pair(txin.amount, absolute[0]); if (opt_verbose) { - MINFO("Blackballing output " << output.first << "/" << output.second << ", due to being used in a 1-ring"); + MINFO("Marking output " << output.first << "/" << output.second << " as spent, due to being used in a 1-ring"); std::cout << "\r" << start_idx << "/" << n_txes << " \r" << std::flush; } blackballs.push_back(output); @@ -1229,7 +1229,7 @@ int main(int argc, char* argv[]) const std::pair output = std::make_pair(txin.amount, absolute[o]); if (opt_verbose) { - MINFO("Blackballing output " << output.first << "/" << output.second << ", due to being used in " << new_ring.size() << " identical " << new_ring.size() << "-rings"); + MINFO("Marking output " << output.first << "/" << output.second << " as spent, due to being used in " << new_ring.size() << " identical " << new_ring.size() << "-rings"); std::cout << "\r" << start_idx << "/" << n_txes << " \r" << std::flush; } blackballs.push_back(output); @@ -1244,7 +1244,7 @@ int main(int argc, char* argv[]) const std::pair output = std::make_pair(txin.amount, absolute[o]); if (opt_verbose) { - MINFO("Blackballing output " << output.first << "/" << output.second << ", due to being used in " << new_ring.size() << " subsets of " << new_ring.size() << "-rings"); + MINFO("Marking output " << output.first << "/" << output.second << " as spent, due to being used in " << new_ring.size() << " subsets of " << new_ring.size() << "-rings"); std::cout << "\r" << start_idx << "/" << n_txes << " \r" << std::flush; } blackballs.push_back(output); @@ -1280,7 +1280,7 @@ int main(int argc, char* argv[]) const std::pair output = std::make_pair(txin.amount, common[0]); if (opt_verbose) { - MINFO("Blackballing output " << output.first << "/" << output.second << ", due to being used in rings with a single common element"); + MINFO("Marking output " << output.first << "/" << output.second << " as spent, due to being used in rings with a single common element"); std::cout << "\r" << start_idx << "/" << n_txes << " \r" << std::flush; } blackballs.push_back(output); @@ -1392,7 +1392,7 @@ int main(int argc, char* argv[]) const std::pair output = std::make_pair(od.amount, last_unknown); if (opt_verbose) { - MINFO("Blackballing output " << output.first << "/" << output.second << ", due to being used in a " << + MINFO("Marking output " << output.first << "/" << output.second << " as spent, due to being used in a " << absolute.size() << "-ring where all other outputs are known to be spent"); } blackballs.push_back(output); @@ -1420,7 +1420,7 @@ int main(int argc, char* argv[]) skip_secondary_passes: uint64_t diff = get_num_spent_outputs() - start_blackballed_outputs; - LOG_PRINT_L0(std::to_string(diff) << " new outputs blackballed, " << get_num_spent_outputs() << " total outputs blackballed"); + LOG_PRINT_L0(std::to_string(diff) << " new outputs marked as spent, " << get_num_spent_outputs() << " total outputs marked as spent"); MDB_txn *txn; dbr = mdb_txn_begin(env, NULL, MDB_RDONLY, &txn); @@ -1460,7 +1460,7 @@ int main(int argc, char* argv[]) mdb_txn_abort(txn); } - LOG_PRINT_L0("Blockchain blackball data exported OK"); + LOG_PRINT_L0("Blockchain spent output data exported OK"); close_db(env0, txn0, cur0, dbi0); close(); return 0; diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 47b77abc691..18b596662d7 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -1640,7 +1640,7 @@ bool simple_wallet::blackball(const std::vector &args) uint64_t amount = std::numeric_limits::max(), offset, num_offsets; if (args.size() == 0) { - fail_msg_writer() << tr("usage: blackball / | [add]"); + fail_msg_writer() << tr("usage: mark_output_spent / | [add]"); return true; } @@ -1718,7 +1718,7 @@ bool simple_wallet::blackball(const std::vector &args) } catch (const std::exception &e) { - fail_msg_writer() << tr("Failed to blackball output: ") << e.what(); + fail_msg_writer() << tr("Failed to mark output spent: ") << e.what(); } return true; @@ -1729,7 +1729,7 @@ bool simple_wallet::unblackball(const std::vector &args) std::pair output; if (args.size() != 1) { - fail_msg_writer() << tr("usage: unblackball /"); + fail_msg_writer() << tr("usage: mark_output_unspent /"); return true; } @@ -1745,7 +1745,7 @@ bool simple_wallet::unblackball(const std::vector &args) } catch (const std::exception &e) { - fail_msg_writer() << tr("Failed to unblackball output: ") << e.what(); + fail_msg_writer() << tr("Failed to mark output unspent: ") << e.what(); } return true; @@ -1756,7 +1756,7 @@ bool simple_wallet::blackballed(const std::vector &args) std::pair output; if (args.size() != 1) { - fail_msg_writer() << tr("usage: blackballed /"); + fail_msg_writer() << tr("usage: is_output_spent /"); return true; } @@ -1769,13 +1769,13 @@ bool simple_wallet::blackballed(const std::vector &args) try { if (m_wallet->is_output_blackballed(output)) - message_writer() << tr("Blackballed: ") << output.first << "/" << output.second; + message_writer() << tr("Spent: ") << output.first << "/" << output.second; else - message_writer() << tr("not blackballed: ") << output.first << "/" << output.second; + message_writer() << tr("Not spent: ") << output.first << "/" << output.second; } catch (const std::exception &e) { - fail_msg_writer() << tr("Failed to unblackball output: ") << e.what(); + fail_msg_writer() << tr("Failed to check whether output is spent: ") << e.what(); } return true; @@ -2599,18 +2599,18 @@ simple_wallet::simple_wallet() boost::bind(&simple_wallet::save_known_rings, this, _1), tr("save_known_rings"), tr("Save known rings to the shared rings database")); - m_cmd_binder.set_handler("blackball", + m_cmd_binder.set_handler("mark_output_spent", boost::bind(&simple_wallet::blackball, this, _1), - tr("blackball / | [add]"), - tr("Blackball output(s) so they never get selected as fake outputs in a ring")); - m_cmd_binder.set_handler("unblackball", + tr("mark_output_spent / | [add]"), + tr("Mark output(s) as spent so they never get selected as fake outputs in a ring")); + m_cmd_binder.set_handler("mark_output_unspent", boost::bind(&simple_wallet::unblackball, this, _1), - tr("unblackball /"), - tr("Unblackballs an output so it may get selected as a fake output in a ring")); - m_cmd_binder.set_handler("blackballed", + tr("mark_output_unspent /"), + tr("Marks an output as unspent so it may get selected as a fake output in a ring")); + m_cmd_binder.set_handler("is_output_spent", boost::bind(&simple_wallet::blackballed, this, _1), - tr("blackballed /"), - tr("Checks whether an output is blackballed")); + tr("is_output_spent /"), + tr("Checks whether an output is marked as spent")); m_cmd_binder.set_handler("version", boost::bind(&simple_wallet::version, this, _1), tr("version"), diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index bb1ba83caf1..1b4370c366a 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -2161,7 +2161,7 @@ bool WalletImpl::blackballOutputs(const std::vector &outputs, bool bool ret = m_wallet->set_blackballed_outputs(raw_outputs, add); if (!ret) { - setStatusError(tr("Failed to set blackballed outputs")); + setStatusError(tr("Failed to mark outputs as spent")); return false; } return true; @@ -2183,7 +2183,7 @@ bool WalletImpl::blackballOutput(const std::string &amount, const std::string &o bool ret = m_wallet->blackball_output(std::make_pair(raw_amount, raw_offset)); if (!ret) { - setStatusError(tr("Failed to blackball output")); + setStatusError(tr("Failed to mark output as spent")); return false; } return true; @@ -2205,7 +2205,7 @@ bool WalletImpl::unblackballOutput(const std::string &amount, const std::string bool ret = m_wallet->unblackball_output(std::make_pair(raw_amount, raw_offset)); if (!ret) { - setStatusError(tr("Failed to unblackball output")); + setStatusError(tr("Failed to mark output as unspent")); return false; } return true; diff --git a/src/wallet/ringdb.cpp b/src/wallet/ringdb.cpp index e5995e7fb50..f562d6c0687 100644 --- a/src/wallet/ringdb.cpp +++ b/src/wallet/ringdb.cpp @@ -412,13 +412,13 @@ bool ringdb::blackball_worker(const std::vector> & switch (op) { case BLACKBALL_BLACKBALL: - MDEBUG("Blackballing output " << output.first << "/" << output.second); + MDEBUG("Marking output " << output.first << "/" << output.second << " as spent"); dbr = mdb_cursor_put(cursor, &key, &data, MDB_APPENDDUP); if (dbr == MDB_KEYEXIST) dbr = 0; break; case BLACKBALL_UNBLACKBALL: - MDEBUG("Unblackballing output " << output.first << "/" << output.second); + MDEBUG("Marking output " << output.first << "/" << output.second << " as unspent"); dbr = mdb_cursor_get(cursor, &key, &data, MDB_GET_BOTH); if (dbr == 0) dbr = mdb_cursor_del(cursor, 0); diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index d774c54c062..893642af71c 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -6980,7 +6980,7 @@ void wallet2::get_outs(std::vector> // outputs, we still need to reach the minimum ring size) if (allow_blackballed) break; - MINFO("Not enough non blackballed outputs, we'll allow blackballed ones"); + MINFO("Not enough output not marked as spent, we'll allow outputs marked as spent"); allow_blackballed = true; num_usable_outs = num_outs; } diff --git a/tests/unit_tests/ringdb.cpp b/tests/unit_tests/ringdb.cpp index 0d92049ac91..ab634ea8231 100644 --- a/tests/unit_tests/ringdb.cpp +++ b/tests/unit_tests/ringdb.cpp @@ -136,21 +136,21 @@ TEST(ringdb, different_genesis) ASSERT_FALSE(ringdb.get_ring(KEY_2, KEY_IMAGE_1, outs2)); } -TEST(blackball, not_found) +TEST(spent_outputs, not_found) { RingDB ringdb; ASSERT_TRUE(ringdb.blackball(OUTPUT_1)); ASSERT_FALSE(ringdb.blackballed(OUTPUT_2)); } -TEST(blackball, found) +TEST(spent_outputs, found) { RingDB ringdb; ASSERT_TRUE(ringdb.blackball(OUTPUT_1)); ASSERT_TRUE(ringdb.blackballed(OUTPUT_1)); } -TEST(blackball, vector) +TEST(spent_outputs, vector) { RingDB ringdb; std::vector> outputs; @@ -174,7 +174,7 @@ TEST(blackball, vector) ASSERT_TRUE(ringdb.blackballed(std::make_pair(30, 5))); } -TEST(blackball, unblackball) +TEST(spent_outputs, mark_as_unspent) { RingDB ringdb; ASSERT_TRUE(ringdb.blackball(OUTPUT_1)); @@ -182,7 +182,7 @@ TEST(blackball, unblackball) ASSERT_FALSE(ringdb.blackballed(OUTPUT_1)); } -TEST(blackball, clear) +TEST(spent_outputs, clear) { RingDB ringdb; ASSERT_TRUE(ringdb.blackball(OUTPUT_1)); From ade369f96fd0decbce1489f12d50d741f7c1b5a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Sa=C5=82aban?= Date: Thu, 18 Oct 2018 23:14:06 +0200 Subject: [PATCH 0614/1404] Add RPC error code for non-deterministic wallet --- src/wallet/wallet_rpc_server.cpp | 1 + src/wallet/wallet_rpc_server_error_codes.h | 1 + 2 files changed, 2 insertions(+) diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index 1b63d65b63b..8baa2251e1c 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -1577,6 +1577,7 @@ namespace tools epee::wipeable_string seed; if (!m_wallet->get_seed(seed)) { + er.code = WALLET_RPC_ERROR_CODE_NON_DETERMINISTIC; er.message = "The wallet is non-deterministic. Cannot display seed."; return false; } diff --git a/src/wallet/wallet_rpc_server_error_codes.h b/src/wallet/wallet_rpc_server_error_codes.h index f127ae24033..9b3a2847d71 100644 --- a/src/wallet/wallet_rpc_server_error_codes.h +++ b/src/wallet/wallet_rpc_server_error_codes.h @@ -73,3 +73,4 @@ #define WALLET_RPC_ERROR_CODE_BAD_SIGNED_TX_DATA -40 #define WALLET_RPC_ERROR_CODE_SIGNED_SUBMISSION -41 #define WALLET_RPC_ERROR_CODE_SIGN_UNSIGNED -42 +#define WALLET_RPC_ERROR_CODE_NON_DETERMINISTIC -43 From a7bffead9e5c0f1943f1459c9d1ab581b4ab11d6 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 18 Oct 2018 13:18:49 +0000 Subject: [PATCH 0615/1404] daemon: fix base fee stating /kB even when it is per byte --- src/daemon/rpc_command_executor.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp index 6464d372fcd..fb2cc15f81d 100644 --- a/src/daemon/rpc_command_executor.cpp +++ b/src/daemon/rpc_command_executor.cpp @@ -1717,11 +1717,14 @@ bool t_rpc_command_executor::print_blockchain_dynamic_stats(uint64_t nblocks) cryptonote::COMMAND_RPC_GET_BLOCK_HEADERS_RANGE::response bhres; cryptonote::COMMAND_RPC_GET_BASE_FEE_ESTIMATE::request fereq; cryptonote::COMMAND_RPC_GET_BASE_FEE_ESTIMATE::response feres; + cryptonote::COMMAND_RPC_HARD_FORK_INFO::request hfreq; + cryptonote::COMMAND_RPC_HARD_FORK_INFO::response hfres; epee::json_rpc::error error_resp; std::string fail_message = "Problem fetching info"; fereq.grace_blocks = 0; + hfreq.version = HF_VERSION_PER_BYTE_FEE; if (m_is_rpc) { if (!m_rpc_client->rpc_request(ireq, ires, "/getinfo", fail_message.c_str())) @@ -1732,6 +1735,10 @@ bool t_rpc_command_executor::print_blockchain_dynamic_stats(uint64_t nblocks) { return true; } + if (!m_rpc_client->json_rpc_request(hfreq, hfres, "hard_fork_info", fail_message.c_str())) + { + return true; + } } else { @@ -1745,10 +1752,15 @@ bool t_rpc_command_executor::print_blockchain_dynamic_stats(uint64_t nblocks) tools::fail_msg_writer() << make_error(fail_message, feres.status); return true; } + if (!m_rpc_server->on_hard_fork_info(hfreq, hfres, error_resp) || hfres.status != CORE_RPC_STATUS_OK) + { + tools::fail_msg_writer() << make_error(fail_message, hfres.status); + return true; + } } tools::msg_writer() << "Height: " << ires.height << ", diff " << ires.difficulty << ", cum. diff " << ires.cumulative_difficulty - << ", target " << ires.target << " sec" << ", dyn fee " << cryptonote::print_money(feres.fee) << "/kB"; + << ", target " << ires.target << " sec" << ", dyn fee " << cryptonote::print_money(feres.fee) << "/" << (hfres.enabled ? "byte" : "kB"); if (nblocks > 0) { From ca9b996dcb03d3516b4e86a9c89f451467ccb9a6 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 19 Oct 2018 08:57:28 +0000 Subject: [PATCH 0616/1404] perf_timer: separate log categories based on caller categories Also default to microseconds, for homogeneity Makes it easier to enable what we need --- src/common/perf_timer.cpp | 8 ++++---- src/common/perf_timer.h | 18 ++++++++---------- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/src/common/perf_timer.cpp b/src/common/perf_timer.cpp index 6910ebdd4f7..47f01de6576 100644 --- a/src/common/perf_timer.cpp +++ b/src/common/perf_timer.cpp @@ -104,11 +104,11 @@ PerformanceTimer::PerformanceTimer(bool paused): started(true), paused(paused) ticks = get_tick_count(); } -LoggingPerformanceTimer::LoggingPerformanceTimer(const std::string &s, uint64_t unit, el::Level l): PerformanceTimer(), name(s), unit(unit), level(l) +LoggingPerformanceTimer::LoggingPerformanceTimer(const std::string &s, const std::string &cat, uint64_t unit, el::Level l): PerformanceTimer(), name(s), cat(cat), unit(unit), level(l) { if (!performance_timers) { - MLOG(level, "PERF ----------"); + MCLOG(level, cat.c_str(), "PERF ----------"); performance_timers = new std::vector(); } else @@ -117,7 +117,7 @@ LoggingPerformanceTimer::LoggingPerformanceTimer(const std::string &s, uint64_t if (!pt->started && !pt->paused) { size_t size = 0; for (const auto *tmp: *performance_timers) if (!tmp->paused) ++size; - MLOG(pt->level, "PERF " << std::string((size-1) * 2, ' ') << " " << pt->name); + MCLOG(pt->level, cat.c_str(), "PERF " << std::string((size-1) * 2, ' ') << " " << pt->name); pt->started = true; } } @@ -137,7 +137,7 @@ LoggingPerformanceTimer::~LoggingPerformanceTimer() char s[12]; snprintf(s, sizeof(s), "%8llu ", (unsigned long long)(ticks_to_ns(ticks) / (1000000000 / unit))); size_t size = 0; for (const auto *tmp: *performance_timers) if (!tmp->paused || tmp==this) ++size; - MLOG(level, "PERF " << s << std::string(size * 2, ' ') << " " << name); + MCLOG(level, cat.c_str(), "PERF " << s << std::string(size * 2, ' ') << " " << name); if (performance_timers->empty()) { delete performance_timers; diff --git a/src/common/perf_timer.h b/src/common/perf_timer.h index 675d6234d5b..1d4dee5b553 100644 --- a/src/common/perf_timer.h +++ b/src/common/perf_timer.h @@ -33,9 +33,6 @@ #include #include "misc_log_ex.h" -#undef MONERO_DEFAULT_LOG_CATEGORY -#define MONERO_DEFAULT_LOG_CATEGORY "perf" - namespace tools { @@ -67,23 +64,24 @@ void set(uint64_t v){ticks=v;} class LoggingPerformanceTimer: public PerformanceTimer { public: - LoggingPerformanceTimer(const std::string &s, uint64_t unit, el::Level l = el::Level::Debug); + LoggingPerformanceTimer(const std::string &s, const std::string &cat, uint64_t unit, el::Level l = el::Level::Debug); ~LoggingPerformanceTimer(); private: std::string name; + std::string cat; uint64_t unit; el::Level level; }; void set_performance_timer_log_level(el::Level level); -#define PERF_TIMER_UNIT(name, unit) tools::LoggingPerformanceTimer pt_##name(#name, unit, tools::performance_timer_log_level) -#define PERF_TIMER_UNIT_L(name, unit, l) tools::LoggingPerformanceTimer pt_##name(#name, unit, l) -#define PERF_TIMER(name) PERF_TIMER_UNIT(name, 1000) -#define PERF_TIMER_L(name, l) PERF_TIMER_UNIT_L(name, 1000, l) -#define PERF_TIMER_START_UNIT(name, unit) std::unique_ptr pt_##name(new tools::LoggingPerformanceTimer(#name, unit, el::Level::Info)) -#define PERF_TIMER_START(name) PERF_TIMER_START_UNIT(name, 1000) +#define PERF_TIMER_UNIT(name, unit) tools::LoggingPerformanceTimer pt_##name(#name, "perf." MONERO_DEFAULT_LOG_CATEGORY, unit, tools::performance_timer_log_level) +#define PERF_TIMER_UNIT_L(name, unit, l) tools::LoggingPerformanceTimer pt_##name(#name, "perf." MONERO_DEFAULT_LOG_CATEGORY, unit, l) +#define PERF_TIMER(name) PERF_TIMER_UNIT(name, 1000000) +#define PERF_TIMER_L(name, l) PERF_TIMER_UNIT_L(name, 1000000, l) +#define PERF_TIMER_START_UNIT(name, unit) std::unique_ptr pt_##name(new tools::LoggingPerformanceTimer(#name, "perf." MONERO_DEFAULT_LOG_CATEGORY, unit, el::Level::Info)) +#define PERF_TIMER_START(name) PERF_TIMER_START_UNIT(name, 1000000) #define PERF_TIMER_STOP(name) do { pt_##name.reset(NULL); } while(0) #define PERF_TIMER_PAUSE(name) pt_##name->pause() #define PERF_TIMER_RESUME(name) pt_##name->resume() From b916ca63a8b28a38bd791f0317ec5b5765bb8686 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 19 Oct 2018 09:20:03 +0000 Subject: [PATCH 0617/1404] rpc: fix output distribution caching ignoring chain changes 0 is placeholder for whole chain, so we should compare chain height changes rather than chain-height-or-zero. Even this isn't totally foolproof if a blocks are popped and the same number added again, but it is much better as it prevents the data from slowly going out of sync. --- src/rpc/core_rpc_server.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index d8f556d3e22..f5ec1194ee4 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -2118,6 +2118,8 @@ namespace cryptonote try { + // 0 is placeholder for the whole chain + const uint64_t req_to_height = req.to_height ? req.to_height : (m_core.get_current_blockchain_height() - 1); for (uint64_t amount: req.amounts) { static struct D @@ -2130,7 +2132,7 @@ namespace cryptonote } d; boost::unique_lock lock(d.mutex); - if (d.cached && amount == 0 && d.cached_from == req.from_height && d.cached_to == req.to_height) + if (d.cached && amount == 0 && d.cached_from == req.from_height && d.cached_to == req_to_height) { res.distributions.push_back({amount, d.cached_start_height, req.binary, d.cached_distribution, d.cached_base}); if (!req.cumulative) @@ -2145,23 +2147,23 @@ namespace cryptonote std::vector distribution; uint64_t start_height, base; - if (!m_core.get_output_distribution(amount, req.from_height, req.to_height, start_height, distribution, base)) + if (!m_core.get_output_distribution(amount, req.from_height, req_to_height, start_height, distribution, base)) { error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR; error_resp.message = "Failed to get rct distribution"; return false; } - if (req.to_height > 0 && req.to_height >= req.from_height) + if (req_to_height > 0 && req_to_height >= req.from_height) { uint64_t offset = std::max(req.from_height, start_height); - if (offset <= req.to_height && req.to_height - offset + 1 < distribution.size()) - distribution.resize(req.to_height - offset + 1); + if (offset <= req_to_height && req_to_height - offset + 1 < distribution.size()) + distribution.resize(req_to_height - offset + 1); } if (amount == 0) { d.cached_from = req.from_height; - d.cached_to = req.to_height; + d.cached_to = req_to_height; d.cached_distribution = distribution; d.cached_start_height = start_height; d.cached_base = base; From 61304151b45b8adf14ed1822971ce59dc57b6f5c Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 19 Oct 2018 16:46:52 +0000 Subject: [PATCH 0618/1404] db_lmdb: use MDB_MULTIPLE_NEXT where possible for some speedup --- src/blockchain_db/lmdb/db_lmdb.cpp | 32 +++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp index bd91f308ac5..0afadcd8d49 100644 --- a/src/blockchain_db/lmdb/db_lmdb.cpp +++ b/src/blockchain_db/lmdb/db_lmdb.cpp @@ -1980,22 +1980,36 @@ std::vector BlockchainLMDB::get_block_cumulative_rct_outputs(const std MDB_val v; uint64_t prev_height = heights[0]; + uint64_t range_begin = 0, range_end = 0; for (uint64_t height: heights) { - if (height == prev_height + 1) + if (height >= range_begin && height < range_end) { - MDB_val k2; - result = mdb_cursor_get(m_cur_block_info, &k2, &v, MDB_NEXT); + // nohting to do } else { - v.mv_size = sizeof(uint64_t); - v.mv_data = (void*)&height; - result = mdb_cursor_get(m_cur_block_info, (MDB_val *)&zerokval, &v, MDB_GET_BOTH); + if (height == prev_height + 1) + { + MDB_val k2; + result = mdb_cursor_get(m_cur_block_info, &k2, &v, MDB_NEXT_MULTIPLE); + range_begin = ((const mdb_block_info*)v.mv_data)->bi_height; + range_end = range_begin + v.mv_size / sizeof(mdb_block_info); // whole records please + if (height < range_begin || height >= range_end) + throw0(DB_ERROR(("Height " + std::to_string(height) + " not included in multuple record range: " + std::to_string(range_begin) + "-" + std::to_string(range_end)).c_str())); + } + else + { + v.mv_size = sizeof(uint64_t); + v.mv_data = (void*)&height; + result = mdb_cursor_get(m_cur_block_info, (MDB_val *)&zerokval, &v, MDB_GET_BOTH); + range_begin = height; + range_end = range_begin + 1; + } + if (result) + throw0(DB_ERROR(lmdb_error("Error attempting to retrieve rct distribution from the db: ", result).c_str())); } - if (result) - throw0(DB_ERROR(lmdb_error("Error attempting to retrieve rct distribution from the db: ", result).c_str())); - const mdb_block_info *bi = (const mdb_block_info *)v.mv_data; + const mdb_block_info *bi = ((const mdb_block_info *)v.mv_data) + (height - range_begin); res.push_back(bi->bi_cum_rct); prev_height = height; } From cf75ee722a896a0fee2f5c33b2f0dbf051e66ae2 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 19 Oct 2018 21:10:19 +0000 Subject: [PATCH 0619/1404] blockchain: move two new verification errors to the verify category Lest we get people get scared again --- src/cryptonote_core/blockchain.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index eb869b79561..fa23b6bd200 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -2364,7 +2364,7 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context const bool bulletproof = rct::is_rct_bulletproof(tx.rct_signatures.type); if (bulletproof || !tx.rct_signatures.p.bulletproofs.empty()) { - MERROR("Bulletproofs are not allowed before v8"); + MERROR_VER("Bulletproofs are not allowed before v8"); tvc.m_invalid_output = true; return false; } @@ -2377,7 +2377,7 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context const bool borromean = rct::is_rct_borromean(tx.rct_signatures.type); if (borromean) { - MERROR("Borromean range proofs are not allowed after v8"); + MERROR_VER("Borromean range proofs are not allowed after v8"); tvc.m_invalid_output = true; return false; } From d3cda5ad393098235637d8a5f8fa52cac96358df Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 20 Oct 2018 09:12:55 +0000 Subject: [PATCH 0620/1404] console_handler: add a global log when exiting via EOF It's a common confusion point for users which run monerod without stdin and with --detach --- contrib/epee/include/console_handler.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/contrib/epee/include/console_handler.h b/contrib/epee/include/console_handler.h index 2ccf5b095a0..6e7efd1d788 100644 --- a/contrib/epee/include/console_handler.h +++ b/contrib/epee/include/console_handler.h @@ -352,8 +352,11 @@ namespace epee std::string command; bool get_line_ret = m_stdin_reader.get_line(command); - if (!m_running || m_stdin_reader.eos()) + if (!m_running) + break; + if (m_stdin_reader.eos()) { + MGINFO("EOF on stdin, exiting"); break; } if (!get_line_ret) From 62f94e1b9d10ba94228bf772b30ac08798b41d95 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 20 Oct 2018 10:29:00 +0000 Subject: [PATCH 0621/1404] device_io_hid.cpp: fix copyright header --- src/device/device_io_hid.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/device/device_io_hid.cpp b/src/device/device_io_hid.cpp index 562aca8b8c7..4dd8277388e 100644 --- a/src/device/device_io_hid.cpp +++ b/src/device/device_io_hid.cpp @@ -1,3 +1,18 @@ +// Copyright (c) 2017-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be // used to endorse or promote products derived from this software without specific // prior written permission. // From 7d21c9b5738e8d3cb1b021ce8b5be88ee1e07e85 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 20 Oct 2018 21:36:51 +0000 Subject: [PATCH 0622/1404] CMakeLists.txt: only use libatomic when found --- CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8baac02e9b0..a9fd6fd459d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -937,7 +937,9 @@ if(ANDROID) endif() if(CMAKE_C_COMPILER_ID STREQUAL "Clang" AND ARCH_WIDTH EQUAL "32" AND NOT IOS AND NOT FREEBSD) find_library(ATOMIC atomic) - list(APPEND EXTRA_LIBRARIES ${ATOMIC}) + if (ATOMIC_FOUND) + list(APPEND EXTRA_LIBRARIES ${ATOMIC}) + endif() endif() find_path(ZMQ_INCLUDE_PATH zmq.hpp) From 3a85af403dd92263d3715886a3e12c5f84f19d6f Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 21 Oct 2018 17:09:21 +0000 Subject: [PATCH 0623/1404] core: fix handle_incoming_tx* comment about return value --- src/cryptonote_core/cryptonote_core.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index 58fe5b7b56e..f43d593dedf 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -117,7 +117,7 @@ namespace cryptonote * @param relayed whether or not the transaction was relayed to us * @param do_not_relay whether to prevent the transaction from being relayed * - * @return true if the transaction made it to the transaction pool, otherwise false + * @return true if the transaction was accepted, false otherwise */ bool handle_incoming_tx(const blobdata& tx_blob, tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay); @@ -133,7 +133,7 @@ namespace cryptonote * @param relayed whether or not the transactions were relayed to us * @param do_not_relay whether to prevent the transactions from being relayed * - * @return true if the transactions made it to the transaction pool, otherwise false + * @return true if the transactions were accepted, false otherwise */ bool handle_incoming_txs(const std::vector& tx_blobs, std::vector& tvc, bool keeped_by_block, bool relayed, bool do_not_relay); From cf646e3afe89fccb6a5f7bded2eae499b8fb19df Mon Sep 17 00:00:00 2001 From: Paul Shapiro Date: Sun, 21 Oct 2018 12:43:31 -0500 Subject: [PATCH 0624/1404] wallet2/create_transactions_2: removed extraneous shuffle before sort of unused_*_indices_per_subaddr --- src/wallet/wallet2.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 6b3a8533e3e..04888d03542 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -8404,12 +8404,8 @@ std::vector wallet2::create_transactions_2(std::vector>& x, const std::pair>& y) { return unlocked_balance_per_subaddr[x.first] > unlocked_balance_per_subaddr[y.first]; From 76681b9bdae63c31fabc83f32390fcab7069ada9 Mon Sep 17 00:00:00 2001 From: Paul Shapiro Date: Sun, 21 Oct 2018 12:44:22 -0500 Subject: [PATCH 0625/1404] wallet2/create_transactions_2: fixed typo in try_tx=true's estimate_fee args --- src/wallet/wallet2.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 6b3a8533e3e..0f6911822d1 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -8601,7 +8601,7 @@ std::vector wallet2::create_transactions_2(std::vector Date: Sun, 21 Oct 2018 12:46:13 -0500 Subject: [PATCH 0626/1404] mnemonics/electrum-words/create_checksum_index(): updated to work with non fixed word list length mnemonic --- src/mnemonics/electrum-words.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mnemonics/electrum-words.cpp b/src/mnemonics/electrum-words.cpp index 3d6338856d9..496e26858ed 100644 --- a/src/mnemonics/electrum-words.cpp +++ b/src/mnemonics/electrum-words.cpp @@ -215,7 +215,7 @@ namespace } boost::crc_32_type result; result.process_bytes(trimmed_words.data(), trimmed_words.length()); - return result.checksum() % crypto::ElectrumWords::seed_length; + return result.checksum() % word_list.size(); } /*! From fd62b6e79f87503bf27a3c8709aaf9cc4ae36146 Mon Sep 17 00:00:00 2001 From: xiphon Date: Tue, 9 Oct 2018 12:33:39 +0000 Subject: [PATCH 0627/1404] blocks: use auto-generated .c files instead of 'LD -r -b binary' --- CMakeLists.txt | 5 +- src/blockchain_utilities/CMakeLists.txt | 20 +---- .../blockchain_import.cpp | 8 +- src/blocks/CMakeLists.txt | 35 ++++---- src/blocks/blockexports.c | 87 ------------------- src/blocks/blocks.cpp | 31 +++++++ src/blocks/blocks.dat | 0 src/blocks/blocks.h | 14 ++- src/cryptonote_config.h | 1 + src/cryptonote_core/CMakeLists.txt | 7 -- src/cryptonote_core/blockchain.cpp | 29 +++---- src/cryptonote_core/blockchain.h | 18 +++- src/cryptonote_core/cryptonote_core.cpp | 4 +- src/cryptonote_core/cryptonote_core.h | 3 +- src/daemon/CMakeLists.txt | 21 +---- src/daemon/core.h | 8 +- src/device/CMakeLists.txt | 7 -- 17 files changed, 113 insertions(+), 185 deletions(-) delete mode 100644 src/blocks/blockexports.c create mode 100644 src/blocks/blocks.cpp delete mode 100644 src/blocks/blocks.dat diff --git a/CMakeLists.txt b/CMakeLists.txt index 1bffd29b60d..ae125100746 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -199,6 +199,9 @@ set(PER_BLOCK_CHECKPOINT 1) if(PER_BLOCK_CHECKPOINT) add_definitions("-DPER_BLOCK_CHECKPOINT") + set(Blocks "blocks") +else() + set(Blocks "") endif() list(INSERT CMAKE_MODULE_PATH 0 @@ -674,12 +677,10 @@ else() add_linker_flag_if_supported(-Wl,-z,noexecstack noexecstack_SUPPORTED) if (noexecstack_SUPPORTED) set(LD_SECURITY_FLAGS "${LD_SECURITY_FLAGS} -Wl,-z,noexecstack") - set(LD_RAW_FLAGS ${LD_RAW_FLAGS} -z noexecstack) endif() add_linker_flag_if_supported(-Wl,-z,noexecheap noexecheap_SUPPORTED) if (noexecheap_SUPPORTED) set(LD_SECURITY_FLAGS "${LD_SECURITY_FLAGS} -Wl,-z,noexecheap") - set(LD_RAW_FLAGS ${LD_RAW_FLAGS} -z noexecheap) endif() # some windows linker bits diff --git a/src/blockchain_utilities/CMakeLists.txt b/src/blockchain_utilities/CMakeLists.txt index ecd7b754c91..873bf552cd2 100644 --- a/src/blockchain_utilities/CMakeLists.txt +++ b/src/blockchain_utilities/CMakeLists.txt @@ -26,20 +26,6 @@ # 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. -set(blocksdat "") -if(PER_BLOCK_CHECKPOINT) - if(APPLE AND DEPENDS) - add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && touch stub.c && ${CMAKE_C_COMPILER} --target=x86_64-apple-darwin11 -o stub.o -c stub.c COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} -r -sectcreate __DATA __blocks_dat ../blocks/checkpoints.dat -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o stub.o && rm -f stub.*) - elseif(APPLE AND NOT DEPENDS) - add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && touch stub.c && ${CMAKE_C_COMPILER} -o stub.o -c stub.c COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} ${LD_RAW_FLAGS} -r -sectcreate __DATA __blocks_dat ../blocks/checkpoints.dat -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o stub.o && rm -f stub.*) - elseif(LINUX_32) - add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && cp ../blocks/checkpoints.dat blocks.dat && ${CMAKE_LINKER} -m elf_i386 ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o blocks.dat && rm -f blocks.dat) - else() - add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && cp ../blocks/checkpoints.dat blocks.dat && ${CMAKE_LINKER} ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o blocks.dat && rm -f blocks.dat) - endif() - set(blocksdat "blocksdat.o") -endif() - set(blockchain_import_sources blockchain_import.cpp bootstrap_file.cpp @@ -119,8 +105,7 @@ monero_private_headers(blockchain_depth monero_add_executable(blockchain_import ${blockchain_import_sources} - ${blockchain_import_private_headers} - ${blocksdat}) + ${blockchain_import_private_headers}) target_link_libraries(blockchain_import PRIVATE @@ -132,7 +117,8 @@ target_link_libraries(blockchain_import ${Boost_SYSTEM_LIBRARY} ${Boost_THREAD_LIBRARY} ${CMAKE_THREAD_LIBS_INIT} - ${EXTRA_LIBRARIES}) + ${EXTRA_LIBRARIES} + ${Blocks}) if(ARCH_WIDTH) target_compile_definitions(blockchain_import diff --git a/src/blockchain_utilities/blockchain_import.cpp b/src/blockchain_utilities/blockchain_import.cpp index 9ec768d2604..7f92ecd87b1 100644 --- a/src/blockchain_utilities/blockchain_import.cpp +++ b/src/blockchain_utilities/blockchain_import.cpp @@ -37,6 +37,7 @@ #include "misc_log_ex.h" #include "bootstrap_file.h" #include "bootstrap_serialization.h" +#include "blocks/blocks.h" #include "cryptonote_basic/cryptonote_format_utils.h" #include "serialization/binary_utils.h" // dump_binary(), parse_binary() #include "serialization/json_utils.h" // dump_json() @@ -758,7 +759,12 @@ int main(int argc, char* argv[]) { core.disable_dns_checkpoints(true); - if (!core.init(vm, NULL)) +#if defined(PER_BLOCK_CHECKPOINT) + const GetCheckpointsCallback& get_checkpoints = blocks::GetCheckpointsData; +#else + const GetCheckpointsCallback& get_checkpoints = nullptr; +#endif + if (!core.init(vm, nullptr, nullptr, get_checkpoints)) { std::cerr << "Failed to initialize core" << ENDL; return 1; diff --git a/src/blocks/CMakeLists.txt b/src/blocks/CMakeLists.txt index ebb5408cc2c..30d85adbfaf 100644 --- a/src/blocks/CMakeLists.txt +++ b/src/blocks/CMakeLists.txt @@ -26,20 +26,23 @@ # 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. -if(APPLE) - add_library(blocks STATIC blockexports.c) - set_target_properties(blocks PROPERTIES LINKER_LANGUAGE C) -else() - if(LINUX_32) - add_custom_command(OUTPUT blocks.o MAIN_DEPENDENCY blocks.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} -m elf_i386 ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/blocks.o blocks.dat) - add_custom_command(OUTPUT testnet_blocks.o MAIN_DEPENDENCY testnet_blocks.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} -m elf_i386 ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/testnet_blocks.o testnet_blocks.dat) - add_custom_command(OUTPUT stagenet_blocks.o MAIN_DEPENDENCY stagenet_blocks.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} -m elf_i386 ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/stagenet_blocks.o stagenet_blocks.dat) - else() - add_custom_command(OUTPUT blocks.o MAIN_DEPENDENCY blocks.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/blocks.o blocks.dat) - add_custom_command(OUTPUT testnet_blocks.o MAIN_DEPENDENCY testnet_blocks.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/testnet_blocks.o testnet_blocks.dat) - add_custom_command(OUTPUT stagenet_blocks.o MAIN_DEPENDENCY stagenet_blocks.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/stagenet_blocks.o stagenet_blocks.dat) - endif() - add_library(blocks STATIC blocks.o testnet_blocks.o stagenet_blocks.o blockexports.c) - set_target_properties(blocks PROPERTIES LINKER_LANGUAGE C) -endif() +set(GENERATED_SOURCES "") +foreach(BLOB_NAME checkpoints testnet_blocks stagenet_blocks) + set(OUTPUT_C_SOURCE "generated_${BLOB_NAME}.c") + list(APPEND GENERATED_SOURCES ${OUTPUT_C_SOURCE}) + set(INPUT_DAT_FILE "${BLOB_NAME}.dat") + add_custom_command( + OUTPUT ${OUTPUT_C_SOURCE} + MAIN_DEPENDENCY ${INPUT_DAT_FILE} + COMMAND + cd ${CMAKE_CURRENT_BINARY_DIR} && + echo "'#include\t'" > ${OUTPUT_C_SOURCE} && + echo "'const\tunsigned\tchar\t${BLOB_NAME}[]={'" >> ${OUTPUT_C_SOURCE} && + od -v -An -tu1 ${CMAKE_CURRENT_SOURCE_DIR}/${INPUT_DAT_FILE} | sed -e "'s/[0-9]\\{1,\\}/&,/g'" -e "'$$s/.$$//'" >> ${OUTPUT_C_SOURCE} && + echo "'};'" >> ${OUTPUT_C_SOURCE} && + echo "'const\tsize_t\t${BLOB_NAME}_len\t=\tsizeof(${BLOB_NAME});'" >> ${OUTPUT_C_SOURCE} + ) +endforeach() + +add_library(blocks STATIC blocks.cpp ${GENERATED_SOURCES}) diff --git a/src/blocks/blockexports.c b/src/blocks/blockexports.c deleted file mode 100644 index 0154b041351..00000000000 --- a/src/blocks/blockexports.c +++ /dev/null @@ -1,87 +0,0 @@ -#include - -#if defined(__APPLE__) -#include -#ifdef BUILD_SHARED_LIBS -#if !defined(__LP64__) -const struct mach_header _mh_execute_header; -#else -const struct mach_header_64 _mh_execute_header; -#endif -#else -#if !defined(__LP64__) -extern const struct mach_header _mh_execute_header; -#else -extern const struct mach_header_64 _mh_execute_header; -#endif -#endif - -const unsigned char *get_blocks_dat_start(int testnet, int stagenet) -{ - size_t size; - if (testnet) - return getsectiondata(&_mh_execute_header, "__DATA", "__testnet_blocks_dat", &size); - else if (stagenet) - return getsectiondata(&_mh_execute_header, "__DATA", "__stagenet_blocks_dat", &size); - else - return getsectiondata(&_mh_execute_header, "__DATA", "__blocks_dat", &size); -} - -size_t get_blocks_dat_size(int testnet, int stagenet) -{ - size_t size; - if (testnet) - getsectiondata(&_mh_execute_header, "__DATA", "__testnet_blocks_dat", &size); - else if (stagenet) - getsectiondata(&_mh_execute_header, "__DATA", "__stagenet_blocks_dat", &size); - else - getsectiondata(&_mh_execute_header, "__DATA", "__blocks_dat", &size); - return size; -} - -#else - -#if defined(_WIN32) && !defined(_WIN64) -#define _binary_blocks_start binary_blocks_dat_start -#define _binary_blocks_end binary_blocks_dat_end -#define _binary_testnet_blocks_start binary_testnet_blocks_dat_start -#define _binary_testnet_blocks_end binary_testnet_blocks_dat_end -#define _binary_stagenet_blocks_start binary_stagenet_blocks_dat_start -#define _binary_stagenet_blocks_end binary_stagenet_blocks_dat_end -#else -#define _binary_blocks_start _binary_blocks_dat_start -#define _binary_blocks_end _binary_blocks_dat_end -#define _binary_testnet_blocks_start _binary_testnet_blocks_dat_start -#define _binary_testnet_blocks_end _binary_testnet_blocks_dat_end -#define _binary_stagenet_blocks_start _binary_stagenet_blocks_dat_start -#define _binary_stagenet_blocks_end _binary_stagenet_blocks_dat_end -#endif - -extern const unsigned char _binary_blocks_start[]; -extern const unsigned char _binary_blocks_end[]; -extern const unsigned char _binary_testnet_blocks_start[]; -extern const unsigned char _binary_testnet_blocks_end[]; -extern const unsigned char _binary_stagenet_blocks_start[]; -extern const unsigned char _binary_stagenet_blocks_end[]; - -const unsigned char *get_blocks_dat_start(int testnet, int stagenet) -{ - if (testnet) - return _binary_testnet_blocks_start; - else if (stagenet) - return _binary_stagenet_blocks_start; - else - return _binary_blocks_start; -} - -size_t get_blocks_dat_size(int testnet, int stagenet) -{ - if (testnet) - return (size_t) (_binary_testnet_blocks_end - _binary_testnet_blocks_start); - else if (stagenet) - return (size_t) (_binary_stagenet_blocks_end - _binary_stagenet_blocks_start); - else - return (size_t) (_binary_blocks_end - _binary_blocks_start); -} - -#endif diff --git a/src/blocks/blocks.cpp b/src/blocks/blocks.cpp new file mode 100644 index 00000000000..0661f8448bd --- /dev/null +++ b/src/blocks/blocks.cpp @@ -0,0 +1,31 @@ +#include "blocks.h" + +#include + +extern const unsigned char checkpoints[]; +extern const size_t checkpoints_len; +extern const unsigned char stagenet_blocks[]; +extern const size_t stagenet_blocks_len; +extern const unsigned char testnet_blocks[]; +extern const size_t testnet_blocks_len; + +namespace blocks +{ + + const std::unordered_map, std::hash> CheckpointsByNetwork = { + {cryptonote::network_type::MAINNET, {checkpoints, checkpoints_len}}, + {cryptonote::network_type::STAGENET, {stagenet_blocks, stagenet_blocks_len}}, + {cryptonote::network_type::TESTNET, {testnet_blocks, testnet_blocks_len}} + }; + + const epee::span GetCheckpointsData(cryptonote::network_type network) + { + const auto it = CheckpointsByNetwork.find(network); + if (it != CheckpointsByNetwork.end()) + { + return it->second; + } + return nullptr; + } + +} diff --git a/src/blocks/blocks.dat b/src/blocks/blocks.dat deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/src/blocks/blocks.h b/src/blocks/blocks.h index ec683f47ef6..14e391319f6 100644 --- a/src/blocks/blocks.h +++ b/src/blocks/blocks.h @@ -1,16 +1,12 @@ #ifndef SRC_BLOCKS_BLOCKS_H_ #define SRC_BLOCKS_BLOCKS_H_ -#ifdef __cplusplus -extern "C" { -#endif +#include "cryptonote_config.h" +#include "span.h" -const unsigned char *get_blocks_dat_start(int testnet, int stagenet); -size_t get_blocks_dat_size(int testnet, int stagenet); - -#ifdef __cplusplus +namespace blocks +{ + const epee::span GetCheckpointsData(cryptonote::network_type network); } -#endif - #endif /* SRC_BLOCKS_BLOCKS_H_ */ diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index a6858ce7cce..c62eeb73828 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -30,6 +30,7 @@ #pragma once +#include #include #include diff --git a/src/cryptonote_core/CMakeLists.txt b/src/cryptonote_core/CMakeLists.txt index 72844db66e0..231489fdbc5 100644 --- a/src/cryptonote_core/CMakeLists.txt +++ b/src/cryptonote_core/CMakeLists.txt @@ -41,12 +41,6 @@ set(cryptonote_core_private_headers tx_pool.h cryptonote_tx_utils.h) -if(PER_BLOCK_CHECKPOINT) - set(Blocks "blocks") -else() - set(Blocks "") -endif() - monero_private_headers(cryptonote_core ${cryptonote_core_private_headers}) monero_add_library(cryptonote_core @@ -69,5 +63,4 @@ target_link_libraries(cryptonote_core ${Boost_SYSTEM_LIBRARY} ${Boost_THREAD_LIBRARY} PRIVATE - ${Blocks} ${EXTRA_LIBRARIES}) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index eb869b79561..955c6c73146 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -53,9 +53,6 @@ #include "ringct/rctSigs.h" #include "common/perf_timer.h" #include "common/notify.h" -#if defined(PER_BLOCK_CHECKPOINT) -#include "blocks/blocks.h" -#endif #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "blockchain" @@ -341,7 +338,7 @@ uint64_t Blockchain::get_current_blockchain_height() const //------------------------------------------------------------------ //FIXME: possibly move this into the constructor, to avoid accidentally // dereferencing a null BlockchainDB pointer -bool Blockchain::init(BlockchainDB* db, const network_type nettype, bool offline, const cryptonote::test_options *test_options, difficulty_type fixed_difficulty) +bool Blockchain::init(BlockchainDB* db, const network_type nettype, bool offline, const cryptonote::test_options *test_options, difficulty_type fixed_difficulty, const GetCheckpointsCallback& get_checkpoints/* = nullptr*/) { LOG_PRINT_L3("Blockchain::" << __func__); CRITICAL_REGION_LOCAL(m_tx_pool); @@ -439,7 +436,7 @@ bool Blockchain::init(BlockchainDB* db, const network_type nettype, bool offline #if defined(PER_BLOCK_CHECKPOINT) if (m_nettype != FAKECHAIN) - load_compiled_in_block_hashes(); + load_compiled_in_block_hashes(get_checkpoints); #endif MINFO("Blockchain initialized. last block: " << m_db->height() - 1 << ", " << epee::misc_utils::get_time_interval_string(timestamp_diff) << " time ago, current difficulty: " << get_difficulty_for_next_block()); @@ -4404,19 +4401,21 @@ void Blockchain::cancel() #if defined(PER_BLOCK_CHECKPOINT) static const char expected_block_hashes_hash[] = "954cb2bbfa2fe6f74b2cdd22a1a4c767aea249ad47ad4f7c9445f0f03260f511"; -void Blockchain::load_compiled_in_block_hashes() +void Blockchain::load_compiled_in_block_hashes(const GetCheckpointsCallback& get_checkpoints) { - const bool testnet = m_nettype == TESTNET; - const bool stagenet = m_nettype == STAGENET; - if (m_fast_sync && get_blocks_dat_start(testnet, stagenet) != nullptr && get_blocks_dat_size(testnet, stagenet) > 0) + if (get_checkpoints == nullptr || !m_fast_sync) { - MINFO("Loading precomputed blocks (" << get_blocks_dat_size(testnet, stagenet) << " bytes)"); - + return; + } + const epee::span &checkpoints = get_checkpoints(m_nettype); + if (!checkpoints.empty()) + { + MINFO("Loading precomputed blocks (" << checkpoints.size() << " bytes)"); if (m_nettype == MAINNET) { // first check hash crypto::hash hash; - if (!tools::sha256sum(get_blocks_dat_start(testnet, stagenet), get_blocks_dat_size(testnet, stagenet), hash)) + if (!tools::sha256sum(checkpoints.data(), checkpoints.size(), hash)) { MERROR("Failed to hash precomputed blocks data"); return; @@ -4436,9 +4435,9 @@ void Blockchain::load_compiled_in_block_hashes() } } - if (get_blocks_dat_size(testnet, stagenet) > 4) + if (checkpoints.size() > 4) { - const unsigned char *p = get_blocks_dat_start(testnet, stagenet); + const unsigned char *p = checkpoints.data(); const uint32_t nblocks = *p | ((*(p+1))<<8) | ((*(p+2))<<16) | ((*(p+3))<<24); if (nblocks > (std::numeric_limits::max() - 4) / sizeof(hash)) { @@ -4446,7 +4445,7 @@ void Blockchain::load_compiled_in_block_hashes() return; } const size_t size_needed = 4 + nblocks * sizeof(crypto::hash); - if(nblocks > 0 && nblocks > (m_db->height() + HASH_OF_HASHES_STEP - 1) / HASH_OF_HASHES_STEP && get_blocks_dat_size(testnet, stagenet) >= size_needed) + if(nblocks > 0 && nblocks > (m_db->height() + HASH_OF_HASHES_STEP - 1) / HASH_OF_HASHES_STEP && checkpoints.size() >= size_needed) { p += sizeof(uint32_t); m_blocks_hash_of_hashes.reserve(nblocks); diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index ab66fac8b8e..5863d9eb15a 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -38,9 +38,11 @@ #include #include #include +#include #include #include +#include "span.h" #include "syncobj.h" #include "string_tools.h" #include "cryptonote_basic/cryptonote_basic.h" @@ -73,6 +75,15 @@ namespace cryptonote db_nosync //!< Leave syncing up to the backing db (safest, but slowest because of disk I/O) }; + /** + * @brief Callback routine that returns checkpoints data for specific network type + * + * @param network network type + * + * @return checkpoints data, empty span if there ain't any checkpoints for specific network type + */ + typedef std::function(cryptonote::network_type network)> GetCheckpointsCallback; + /************************************************************************/ /* */ /************************************************************************/ @@ -117,10 +128,11 @@ namespace cryptonote * @param offline true if running offline, else false * @param test_options test parameters * @param fixed_difficulty fixed difficulty for testing purposes; 0 means disabled + * @param get_checkpoints if set, will be called to get checkpoints data * * @return true on success, false if any initialization steps fail */ - bool init(BlockchainDB* db, const network_type nettype = MAINNET, bool offline = false, const cryptonote::test_options *test_options = NULL, difficulty_type fixed_difficulty = 0); + bool init(BlockchainDB* db, const network_type nettype = MAINNET, bool offline = false, const cryptonote::test_options *test_options = NULL, difficulty_type fixed_difficulty = 0, const GetCheckpointsCallback& get_checkpoints = nullptr); /** * @brief Initialize the Blockchain state @@ -1369,8 +1381,10 @@ namespace cryptonote * A (possibly empty) set of block hashes can be compiled into the * monero daemon binary. This function loads those hashes into * a useful state. + * + * @param get_checkpoints if set, will be called to get checkpoints data */ - void load_compiled_in_block_hashes(); + void load_compiled_in_block_hashes(const GetCheckpointsCallback& get_checkpoints); /** * @brief expands v2 transaction data from blockchain diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 69e3c708b7a..82b45ee764f 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -389,7 +389,7 @@ namespace cryptonote return m_blockchain_storage.get_alternative_blocks_count(); } //----------------------------------------------------------------------------------------------- - bool core::init(const boost::program_options::variables_map& vm, const char *config_subdir, const cryptonote::test_options *test_options) + bool core::init(const boost::program_options::variables_map& vm, const char *config_subdir, const cryptonote::test_options *test_options, const GetCheckpointsCallback& get_checkpoints/* = nullptr */) { start_time = std::time(nullptr); @@ -567,7 +567,7 @@ namespace cryptonote regtest_hard_forks }; const difficulty_type fixed_difficulty = command_line::get_arg(vm, arg_fixed_difficulty); - r = m_blockchain_storage.init(db.release(), m_nettype, m_offline, regtest ? ®test_test_options : test_options, fixed_difficulty); + r = m_blockchain_storage.init(db.release(), m_nettype, m_offline, regtest ? ®test_test_options : test_options, fixed_difficulty, get_checkpoints); r = m_mempool.init(max_txpool_weight); CHECK_AND_ASSERT_MES(r, false, "Failed to initialize memory pool"); diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index 58fe5b7b56e..8ab7d3126c2 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -244,10 +244,11 @@ namespace cryptonote * @param vm command line parameters * @param config_subdir subdirectory for config storage * @param test_options configuration options for testing + * @param get_checkpoints if set, will be called to get checkpoints data, must return checkpoints data pointer and size or nullptr if there ain't any checkpoints for specific network type * * @return false if one of the init steps fails, otherwise true */ - bool init(const boost::program_options::variables_map& vm, const char *config_subdir = NULL, const test_options *test_options = NULL); + bool init(const boost::program_options::variables_map& vm, const char *config_subdir = NULL, const test_options *test_options = NULL, const GetCheckpointsCallback& get_checkpoints = nullptr); /** * @copydoc Blockchain::reset_and_set_genesis_block diff --git a/src/daemon/CMakeLists.txt b/src/daemon/CMakeLists.txt index f645836a4b2..1177904557d 100644 --- a/src/daemon/CMakeLists.txt +++ b/src/daemon/CMakeLists.txt @@ -26,20 +26,6 @@ # 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. -set(blocksdat "") -if(PER_BLOCK_CHECKPOINT) - if(APPLE AND DEPENDS) - add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && touch stub.c && ${CMAKE_C_COMPILER} --target=x86_64-apple-darwin11 -o stub.o -c stub.c COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} -r -sectcreate __DATA __blocks_dat ../blocks/checkpoints.dat -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o stub.o && rm -f stub.*) - elseif(APPLE AND NOT DEPENDS) - add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && touch stub.c && ${CMAKE_C_COMPILER} -o stub.o -c stub.c COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} ${LD_RAW_FLAGS} -r -sectcreate __DATA __blocks_dat ../blocks/checkpoints.dat -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o stub.o && rm -f stub.*) - elseif(LINUX_32) - add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && cp ../blocks/checkpoints.dat blocks.dat && ${CMAKE_LINKER} -m elf_i386 ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o blocks.dat && rm -f blocks.dat) - else() - add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && cp ../blocks/checkpoints.dat blocks.dat && ${CMAKE_LINKER} ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o blocks.dat && rm -f blocks.dat) - endif() - set(blocksdat "blocksdat.o") -endif() - set(daemon_sources command_parser_executor.cpp command_server.cpp @@ -81,9 +67,7 @@ monero_private_headers(daemon monero_add_executable(daemon ${daemon_sources} ${daemon_headers} - ${daemon_private_headers} - ${blocksdat} -) + ${daemon_private_headers}) target_link_libraries(daemon PRIVATE rpc @@ -106,7 +90,8 @@ target_link_libraries(daemon ${CMAKE_THREAD_LIBS_INIT} ${ZMQ_LIB} ${GNU_READLINE_LIBRARY} - ${EXTRA_LIBRARIES}) + ${EXTRA_LIBRARIES} + ${Blocks}) set_property(TARGET daemon PROPERTY OUTPUT_NAME "monerod") diff --git a/src/daemon/core.h b/src/daemon/core.h index 475f418d650..d1defd573c6 100644 --- a/src/daemon/core.h +++ b/src/daemon/core.h @@ -28,6 +28,7 @@ #pragma once +#include "blocks/blocks.h" #include "cryptonote_core/cryptonote_core.h" #include "cryptonote_protocol/cryptonote_protocol_handler.h" #include "misc_log_ex.h" @@ -85,7 +86,12 @@ class t_core final //initialize core here MGINFO("Initializing core..."); std::string config_subdir = get_config_subdir(); - if (!m_core.init(m_vm_HACK, config_subdir.empty() ? NULL : config_subdir.c_str())) +#if defined(PER_BLOCK_CHECKPOINT) + const cryptonote::GetCheckpointsCallback& get_checkpoints = blocks::GetCheckpointsData; +#else + const cryptonote::GetCheckpointsCallback& get_checkpoints = nullptr; +#endif + if (!m_core.init(m_vm_HACK, config_subdir.empty() ? NULL : config_subdir.c_str(), nullptr, get_checkpoints)) { return false; } diff --git a/src/device/CMakeLists.txt b/src/device/CMakeLists.txt index 8f446f42a24..727134f75e0 100644 --- a/src/device/CMakeLists.txt +++ b/src/device/CMakeLists.txt @@ -58,12 +58,6 @@ endif() set(device_private_headers) -if(PER_BLOCK_CHECKPOINT) - set(Blocks "blocks") -else() - set(Blocks "") -endif() - monero_private_headers(device ${device_private_headers}) @@ -79,5 +73,4 @@ target_link_libraries(device ringct_basic ${OPENSSL_CRYPTO_LIBRARIES} PRIVATE - ${Blocks} ${EXTRA_LIBRARIES}) From e51c978770c25cf866981ff3227e1dcb9d00d2fa Mon Sep 17 00:00:00 2001 From: stoffu Date: Sun, 14 Oct 2018 16:54:07 +0900 Subject: [PATCH 0628/1404] rpc: fix wrongly formatted JSON for pruned tx Fix for #4399. Also unifies code for serializing pruned tx to binary/json into one. --- src/rpc/core_rpc_server.cpp | 31 ++++++++++++------------------- 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index 55ee66a79b0..aa9d3d64bf2 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -212,23 +212,15 @@ namespace cryptonote return true; } //------------------------------------------------------------------------------------------------------------------------------ - static cryptonote::blobdata get_pruned_tx_blob(cryptonote::transaction &tx) - { - std::stringstream ss; - binary_archive ba(ss); - bool r = tx.serialize_base(ba); - CHECK_AND_ASSERT_MES(r, cryptonote::blobdata(), "Failed to serialize rct signatures base"); - return ss.str(); - } - //------------------------------------------------------------------------------------------------------------------------------ - static cryptonote::blobdata get_pruned_tx_json(cryptonote::transaction &tx) - { - std::stringstream ss; - json_archive ar(ss); - bool r = tx.serialize_base(ar); - CHECK_AND_ASSERT_MES(r, cryptonote::blobdata(), "Failed to serialize rct signatures base"); - return ss.str(); - } + class pruned_transaction { + transaction& tx; + public: + pruned_transaction(transaction& tx) : tx(tx) {} + BEGIN_SERIALIZE_OBJECT() + bool r = tx.serialize_base(ar); + if (!r) return false; + END_SERIALIZE() + }; //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_get_blocks(const COMMAND_RPC_GET_BLOCKS_FAST::request& req, COMMAND_RPC_GET_BLOCKS_FAST::response& res) { @@ -564,10 +556,11 @@ namespace cryptonote crypto::hash tx_hash = *vhi++; e.tx_hash = *txhi++; - blobdata blob = req.prune ? get_pruned_tx_blob(tx) : t_serializable_object_to_blob(tx); + pruned_transaction pruned_tx{tx}; + blobdata blob = req.prune ? t_serializable_object_to_blob(pruned_tx) : t_serializable_object_to_blob(tx); e.as_hex = string_tools::buff_to_hex_nodelimer(blob); if (req.decode_as_json) - e.as_json = req.prune ? get_pruned_tx_json(tx) : obj_to_json_str(tx); + e.as_json = req.prune ? obj_to_json_str(pruned_tx) : obj_to_json_str(tx); e.in_pool = pool_tx_hashes.find(tx_hash) != pool_tx_hashes.end(); if (e.in_pool) { From 7f0dd094e79d2b0bf536329475ff44adebe09233 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 22 Oct 2018 12:31:42 +0000 Subject: [PATCH 0629/1404] wallet2: sanity check rct output distribution from the daemon --- src/wallet/wallet2.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 6b3a8533e3e..51d15c6b250 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -6683,15 +6683,21 @@ void wallet2::get_outs(std::vector> uint64_t rct_start_height; std::vector rct_offsets; bool has_rct = false; + uint64_t max_rct_index = 0; for (size_t idx: selected_transfers) if (m_transfers[idx].is_rct()) - { has_rct = true; break; } + { + has_rct = true; + max_rct_index = std::max(max_rct_index, m_transfers[idx].m_global_output_index); + } const bool has_rct_distribution = has_rct && get_rct_distribution(rct_start_height, rct_offsets); if (has_rct_distribution) { // check we're clear enough of rct start, to avoid corner cases below THROW_WALLET_EXCEPTION_IF(rct_offsets.size() <= CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE, error::get_output_distribution, "Not enough rct outputs"); + THROW_WALLET_EXCEPTION_IF(rct_offsets.back() <= max_rct_index, + error::get_output_distribution, "Daemon reports suspicious number of rct outputs"); } // get histogram for the amounts we need From ed36335c969a1061e555724467263e22d1738cce Mon Sep 17 00:00:00 2001 From: xiphon Date: Mon, 22 Oct 2018 06:48:54 +0000 Subject: [PATCH 0630/1404] crypto: fixed incremental keccak API on big-endian platforms --- src/crypto/keccak.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/crypto/keccak.c b/src/crypto/keccak.c index b5946036ee6..b095b5ce2ec 100644 --- a/src/crypto/keccak.c +++ b/src/crypto/keccak.c @@ -145,7 +145,7 @@ void keccak1600(const uint8_t *in, size_t inlen, uint8_t *md) #define IS_ALIGNED_64(p) (0 == (7 & ((const char*)(p) - (const char*)0))) #define KECCAK_PROCESS_BLOCK(st, block) { \ for (int i_ = 0; i_ < KECCAK_WORDS; i_++){ \ - ((st))[i_] ^= ((block))[i_]; \ + ((st))[i_] ^= swap64le(((block))[i_]); \ }; \ keccakf(st, KECCAK_ROUNDS); } @@ -207,7 +207,8 @@ void keccak_finish(KECCAK_CTX * ctx, uint8_t *md){ } static_assert(KECCAK_BLOCKLEN > KECCAK_DIGESTSIZE, ""); + static_assert(KECCAK_DIGESTSIZE % sizeof(uint64_t) == 0, ""); if (md) { - memcpy(md, ctx->hash, KECCAK_DIGESTSIZE); + memcpy_swap64le(md, ctx->hash, KECCAK_DIGESTSIZE / sizeof(uint64_t)); } } From cb4aafd27ebe452ca49aa5142520a40064c2fcca Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 22 Oct 2018 16:03:57 +0000 Subject: [PATCH 0631/1404] blockchain_utilities: simplify getting block blob from height --- src/blockchain_utilities/blockchain_ancestry.cpp | 12 ++++-------- src/blockchain_utilities/blockchain_depth.cpp | 3 +-- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/src/blockchain_utilities/blockchain_ancestry.cpp b/src/blockchain_utilities/blockchain_ancestry.cpp index 2f0bbffd64e..e01a8892cdc 100644 --- a/src/blockchain_utilities/blockchain_ancestry.cpp +++ b/src/blockchain_utilities/blockchain_ancestry.cpp @@ -407,8 +407,7 @@ int main(int argc, char* argv[]) for (uint64_t h = state.height; h < db_height; ++h) { size_t block_ancestry_size = 0; - const crypto::hash block_hash = db->get_block_hash_from_height(h); - const cryptonote::blobdata bd = db->get_block_blob(block_hash); + const cryptonote::blobdata bd = db->get_block_blob_from_height(h); ++total_blocks; cryptonote::block b; if (!cryptonote::parse_and_validate_block_from_blob(bd, b)) @@ -482,8 +481,7 @@ int main(int argc, char* argv[]) } else { - const crypto::hash block_hash = db->get_block_hash_from_height(od.height); - cryptonote::blobdata bd = db->get_block_blob(block_hash); + cryptonote::blobdata bd = db->get_block_blob_from_height(od.height); if (!cryptonote::parse_and_validate_block_from_blob(bd, b)) { LOG_PRINT_L0("Bad block from db"); @@ -620,8 +618,7 @@ int main(int argc, char* argv[]) } else { - const crypto::hash block_hash = db->get_block_hash_from_height(opt_height); - const cryptonote::blobdata bd = db->get_block_blob(block_hash); + const cryptonote::blobdata bd = db->get_block_blob_from_height(opt_height); cryptonote::block b; if (!cryptonote::parse_and_validate_block_from_blob(bd, b)) { @@ -678,8 +675,7 @@ int main(int argc, char* argv[]) { add_ancestor(ancestry, amount, offset); const output_data_t od = db->get_output_key(amount, offset); - const crypto::hash block_hash = db->get_block_hash_from_height(od.height); - bd = db->get_block_blob(block_hash); + bd = db->get_block_blob_from_height(od.height); cryptonote::block b; if (!cryptonote::parse_and_validate_block_from_blob(bd, b)) { diff --git a/src/blockchain_utilities/blockchain_depth.cpp b/src/blockchain_utilities/blockchain_depth.cpp index dd2387e5b96..8060b0de4d9 100644 --- a/src/blockchain_utilities/blockchain_depth.cpp +++ b/src/blockchain_utilities/blockchain_depth.cpp @@ -187,8 +187,7 @@ int main(int argc, char* argv[]) } else { - const crypto::hash block_hash = db->get_block_hash_from_height(opt_height); - const cryptonote::blobdata bd = db->get_block_blob(block_hash); + const cryptonote::blobdata bd = db->get_block_blob_from_height(opt_height); cryptonote::block b; if (!cryptonote::parse_and_validate_block_from_blob(bd, b)) { From 4564a5d17b4c1a4507ac14dc96ad2bcaa675712c Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 7 Aug 2018 08:02:42 +0000 Subject: [PATCH 0632/1404] bulletproofs: speedup PROVE --- src/ringct/bulletproofs.cc | 122 ++++++++++++++++++------------------- 1 file changed, 58 insertions(+), 64 deletions(-) diff --git a/src/ringct/bulletproofs.cc b/src/ringct/bulletproofs.cc index 381f5087211..0e5b3b55f85 100644 --- a/src/ringct/bulletproofs.cc +++ b/src/ringct/bulletproofs.cc @@ -127,15 +127,6 @@ static void sub_acc_p3(ge_p3 *acc_p3, const rct::key &point) ge_p1p1_to_p3(acc_p3, &p1); } -static rct::key scalarmultKey(const ge_p3 &P, const rct::key &a) -{ - ge_p2 R; - ge_scalarmult(&R, a.bytes, &P); - rct::key aP; - ge_tobytes(aP.bytes, &R); - return aP; -} - static rct::key get_exponent(const rct::key &base, size_t idx) { static const std::string salt("bulletproof"); @@ -193,23 +184,28 @@ static rct::key vector_exponent(const rct::keyV &a, const rct::keyV &b) } /* Compute a custom vector-scalar commitment */ -static rct::key vector_exponent_custom(const rct::keyV &A, const rct::keyV &B, const rct::keyV &a, const rct::keyV &b) +static rct::key cross_vector_exponent8(size_t size, const std::vector &A, size_t Ao, const std::vector &B, size_t Bo, const rct::keyV &a, size_t ao, const rct::keyV &b, size_t bo, const ge_p3 *extra_point, const rct::key *extra_scalar) { - CHECK_AND_ASSERT_THROW_MES(A.size() == B.size(), "Incompatible sizes of A and B"); - CHECK_AND_ASSERT_THROW_MES(a.size() == b.size(), "Incompatible sizes of a and b"); - CHECK_AND_ASSERT_THROW_MES(a.size() == A.size(), "Incompatible sizes of a and A"); - CHECK_AND_ASSERT_THROW_MES(a.size() <= maxN*maxM, "Incompatible sizes of a and maxN"); + CHECK_AND_ASSERT_THROW_MES(size + Ao <= A.size(), "Incompatible size for A"); + CHECK_AND_ASSERT_THROW_MES(size + Bo <= B.size(), "Incompatible size for B"); + CHECK_AND_ASSERT_THROW_MES(size + ao <= a.size(), "Incompatible size for a"); + CHECK_AND_ASSERT_THROW_MES(size + bo <= b.size(), "Incompatible size for b"); + CHECK_AND_ASSERT_THROW_MES(size <= maxN*maxM, "size is too large"); + CHECK_AND_ASSERT_THROW_MES(!!extra_point == !!extra_scalar, "only one of extra point/scalar present"); std::vector multiexp_data; - multiexp_data.reserve(a.size()*2); - for (size_t i = 0; i < a.size(); ++i) + multiexp_data.resize(size*2 + (!!extra_point)); + for (size_t i = 0; i < size; ++i) { - multiexp_data.resize(multiexp_data.size() + 1); - multiexp_data.back().scalar = a[i]; - CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&multiexp_data.back().point, A[i].bytes) == 0, "ge_frombytes_vartime failed"); - multiexp_data.resize(multiexp_data.size() + 1); - multiexp_data.back().scalar = b[i]; - CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&multiexp_data.back().point, B[i].bytes) == 0, "ge_frombytes_vartime failed"); + sc_mul(multiexp_data[i*2].scalar.bytes, a[ao+i].bytes, INV_EIGHT.bytes);; + multiexp_data[i*2].point = A[Ao+i]; + sc_mul(multiexp_data[i*2+1].scalar.bytes, b[bo+i].bytes, INV_EIGHT.bytes); + multiexp_data[i*2+1].point = B[Bo+i]; + } + if (extra_point) + { + sc_mul(multiexp_data.back().scalar.bytes, extra_scalar->bytes, INV_EIGHT.bytes); + multiexp_data.back().point = *extra_point; } return multiexp(multiexp_data, false); } @@ -273,16 +269,19 @@ static rct::keyV hadamard(const rct::keyV &a, const rct::keyV &b) return res; } -/* Given two curvepoint arrays, construct the Hadamard product */ -static rct::keyV hadamard2(const rct::keyV &a, const rct::keyV &b) +/* folds a curvepoint array using a two way scaled Hadamard product */ +static void hadamard_fold(std::vector &v, const rct::key &a, const rct::key &b) { - CHECK_AND_ASSERT_THROW_MES(a.size() == b.size(), "Incompatible sizes of a and b"); - rct::keyV res(a.size()); - for (size_t i = 0; i < a.size(); ++i) + CHECK_AND_ASSERT_THROW_MES((v.size() & 1) == 0, "Vector size should be even"); + const size_t sz = v.size() / 2; + for (size_t n = 0; n < sz; ++n) { - rct::addKeys(res[i], a[i], b[i]); + ge_dsmp c[2]; + ge_dsm_precomp(c[0], &v[n]); + ge_dsm_precomp(c[1], &v[sz + n]); + ge_double_scalarmult_precomp_vartime2_p3(&v[n], a.bytes, c[0], b.bytes, c[1]); } - return res; + v.resize(sz); } /* Add two vectors */ @@ -326,17 +325,6 @@ static rct::keyV vector_dup(const rct::key &x, size_t N) return rct::keyV(N, x); } -/* Exponentiate a curve vector by a scalar */ -static rct::keyV vector_scalar2(const rct::keyV &a, const rct::key &x) -{ - rct::keyV res(a.size()); - for (size_t i = 0; i < a.size(); ++i) - { - rct::scalarmultKey(res[i], a[i], x); - } - return res; -} - /* Get the sum of a vector's elements */ static rct::key vector_sum(const rct::keyV &a) { @@ -620,16 +608,16 @@ Bulletproof bulletproof_PROVE(const rct::key &sv, const rct::key &gamma) // These are used in the inner product rounds size_t nprime = N; - rct::keyV Gprime(N); - rct::keyV Hprime(N); + std::vector Gprime(N); + std::vector Hprime(N); rct::keyV aprime(N); rct::keyV bprime(N); const rct::key yinv = invert(y); rct::key yinvpow = rct::identity(); for (size_t i = 0; i < N; ++i) { - Gprime[i] = Gi[i]; - Hprime[i] = scalarmultKey(Hi_p3[i], yinvpow); + Gprime[i] = Gi_p3[i]; + ge_scalarmult_p3(&Hprime[i], yinvpow.bytes, &Hi_p3[i]); sc_mul(yinvpow.bytes, yinvpow.bytes, yinv.bytes); aprime[i] = l[i]; bprime[i] = r[i]; @@ -652,14 +640,10 @@ Bulletproof bulletproof_PROVE(const rct::key &sv, const rct::key &gamma) rct::key cR = inner_product(slice(aprime, nprime, aprime.size()), slice(bprime, 0, nprime)); // PAPER LINES 18-19 - L[round] = vector_exponent_custom(slice(Gprime, nprime, Gprime.size()), slice(Hprime, 0, nprime), slice(aprime, 0, nprime), slice(bprime, nprime, bprime.size())); sc_mul(tmp.bytes, cL.bytes, x_ip.bytes); - rct::addKeys(L[round], L[round], rct::scalarmultH(tmp)); - L[round] = rct::scalarmultKey(L[round], INV_EIGHT); - R[round] = vector_exponent_custom(slice(Gprime, 0, nprime), slice(Hprime, nprime, Hprime.size()), slice(aprime, nprime, aprime.size()), slice(bprime, 0, nprime)); + L[round] = cross_vector_exponent8(nprime, Gprime, nprime, Hprime, 0, aprime, 0, bprime, nprime, &ge_p3_H, &tmp); sc_mul(tmp.bytes, cR.bytes, x_ip.bytes); - rct::addKeys(R[round], R[round], rct::scalarmultH(tmp)); - R[round] = rct::scalarmultKey(R[round], INV_EIGHT); + R[round] = cross_vector_exponent8(nprime, Gprime, 0, Hprime, nprime, aprime, nprime, bprime, 0, &ge_p3_H, &tmp); // PAPER LINES 21-22 w[round] = hash_cache_mash(hash_cache, L[round], R[round]); @@ -672,8 +656,11 @@ Bulletproof bulletproof_PROVE(const rct::key &sv, const rct::key &gamma) // PAPER LINES 24-25 const rct::key winv = invert(w[round]); - Gprime = hadamard2(vector_scalar2(slice(Gprime, 0, nprime), winv), vector_scalar2(slice(Gprime, nprime, Gprime.size()), w[round])); - Hprime = hadamard2(vector_scalar2(slice(Hprime, 0, nprime), w[round]), vector_scalar2(slice(Hprime, nprime, Hprime.size()), winv)); + if (nprime > 1) + { + hadamard_fold(Gprime, winv, w[round]); + hadamard_fold(Hprime, w[round], winv); + } // PAPER LINES 28-29 aprime = vector_add(vector_scalar(slice(aprime, 0, nprime), w[round]), vector_scalar(slice(aprime, nprime, aprime.size()), winv)); @@ -914,16 +901,16 @@ Bulletproof bulletproof_PROVE(const rct::keyV &sv, const rct::keyV &gamma) // These are used in the inner product rounds size_t nprime = MN; - rct::keyV Gprime(MN); - rct::keyV Hprime(MN); + std::vector Gprime(MN); + std::vector Hprime(MN); rct::keyV aprime(MN); rct::keyV bprime(MN); const rct::key yinv = invert(y); rct::key yinvpow = rct::identity(); for (size_t i = 0; i < MN; ++i) { - Gprime[i] = Gi[i]; - Hprime[i] = scalarmultKey(Hi_p3[i], yinvpow); + Gprime[i] = Gi_p3[i]; + ge_scalarmult_p3(&Hprime[i], yinvpow.bytes, &Hi_p3[i]); sc_mul(yinvpow.bytes, yinvpow.bytes, yinv.bytes); aprime[i] = l[i]; bprime[i] = r[i]; @@ -942,18 +929,18 @@ Bulletproof bulletproof_PROVE(const rct::keyV &sv, const rct::keyV &gamma) nprime /= 2; // PAPER LINES 16-17 + PERF_TIMER_START_BP(PROVE_inner_product); rct::key cL = inner_product(slice(aprime, 0, nprime), slice(bprime, nprime, bprime.size())); rct::key cR = inner_product(slice(aprime, nprime, aprime.size()), slice(bprime, 0, nprime)); + PERF_TIMER_STOP(PROVE_inner_product); // PAPER LINES 18-19 - L[round] = vector_exponent_custom(slice(Gprime, nprime, Gprime.size()), slice(Hprime, 0, nprime), slice(aprime, 0, nprime), slice(bprime, nprime, bprime.size())); + PERF_TIMER_START_BP(PROVE_LR); sc_mul(tmp.bytes, cL.bytes, x_ip.bytes); - rct::addKeys(L[round], L[round], rct::scalarmultH(tmp)); - L[round] = rct::scalarmultKey(L[round], INV_EIGHT); - R[round] = vector_exponent_custom(slice(Gprime, 0, nprime), slice(Hprime, nprime, Hprime.size()), slice(aprime, nprime, aprime.size()), slice(bprime, 0, nprime)); + L[round] = cross_vector_exponent8(nprime, Gprime, nprime, Hprime, 0, aprime, 0, bprime, nprime, &ge_p3_H, &tmp); sc_mul(tmp.bytes, cR.bytes, x_ip.bytes); - rct::addKeys(R[round], R[round], rct::scalarmultH(tmp)); - R[round] = rct::scalarmultKey(R[round], INV_EIGHT); + R[round] = cross_vector_exponent8(nprime, Gprime, 0, Hprime, nprime, aprime, nprime, bprime, 0, &ge_p3_H, &tmp); + PERF_TIMER_STOP(PROVE_LR); // PAPER LINES 21-22 w[round] = hash_cache_mash(hash_cache, L[round], R[round]); @@ -966,12 +953,19 @@ Bulletproof bulletproof_PROVE(const rct::keyV &sv, const rct::keyV &gamma) // PAPER LINES 24-25 const rct::key winv = invert(w[round]); - Gprime = hadamard2(vector_scalar2(slice(Gprime, 0, nprime), winv), vector_scalar2(slice(Gprime, nprime, Gprime.size()), w[round])); - Hprime = hadamard2(vector_scalar2(slice(Hprime, 0, nprime), w[round]), vector_scalar2(slice(Hprime, nprime, Hprime.size()), winv)); + if (nprime > 1) + { + PERF_TIMER_START_BP(PROVE_hadamard2); + hadamard_fold(Gprime, winv, w[round]); + hadamard_fold(Hprime, w[round], winv); + PERF_TIMER_STOP(PROVE_hadamard2); + } // PAPER LINES 28-29 + PERF_TIMER_START_BP(PROVE_prime); aprime = vector_add(vector_scalar(slice(aprime, 0, nprime), w[round]), vector_scalar(slice(aprime, nprime, aprime.size()), winv)); bprime = vector_add(vector_scalar(slice(bprime, 0, nprime), winv), vector_scalar(slice(bprime, nprime, bprime.size()), w[round])); + PERF_TIMER_STOP(PROVE_prime); ++round; } From a49a17618fcb4c734248dcc06954c86dcc3fc5cd Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 7 Aug 2018 09:59:14 +0000 Subject: [PATCH 0633/1404] bulletproofs: shave off a lot of scalar muls from the g/h construction --- src/ringct/bulletproofs.cc | 59 +++++++++++++++++++++++++------------- 1 file changed, 39 insertions(+), 20 deletions(-) diff --git a/src/ringct/bulletproofs.cc b/src/ringct/bulletproofs.cc index 0e5b3b55f85..8a6ea2dce47 100644 --- a/src/ringct/bulletproofs.cc +++ b/src/ringct/bulletproofs.cc @@ -1138,41 +1138,60 @@ bool bulletproof_VERIFY(const std::vector &proofs) winv[i] = invert(w[i]); PERF_TIMER_STOP(VERIFY_line_24_25_invert); + // precalc + PERF_TIMER_START_BP(VERIFY_line_24_25_precalc); + rct::keyV w_cache(1< 0; --s) + { + sc_mul(w_cache[s].bytes, w_cache[s/2].bytes, w[j].bytes); + sc_mul(w_cache[s-1].bytes, w_cache[s/2].bytes, winv[j].bytes); + } + } + PERF_TIMER_STOP(VERIFY_line_24_25_precalc); + for (size_t i = 0; i < MN; ++i) { - // Convert the index to binary IN REVERSE and construct the scalar exponent rct::key g_scalar = proof.a; rct::key h_scalar; - sc_mul(h_scalar.bytes, proof.b.bytes, yinvpow.bytes); + if (i == 0) + h_scalar = proof.b; + else + sc_mul(h_scalar.bytes, proof.b.bytes, yinvpow.bytes); - for (size_t j = rounds; j-- > 0; ) - { - size_t J = w.size() - j - 1; - - if ((i & (((size_t)1)< Date: Wed, 8 Aug 2018 12:14:13 +0000 Subject: [PATCH 0634/1404] bulletproofs: random minor speedups --- src/ringct/bulletproofs.cc | 78 +++++++++++++++++++------------------- 1 file changed, 38 insertions(+), 40 deletions(-) diff --git a/src/ringct/bulletproofs.cc b/src/ringct/bulletproofs.cc index 8a6ea2dce47..2d2e6af36d4 100644 --- a/src/ringct/bulletproofs.cc +++ b/src/ringct/bulletproofs.cc @@ -93,18 +93,6 @@ static bool is_reduced(const rct::key &scalar) return scalar == reduced; } -static void addKeys_acc_p3(ge_p3 *acc_p3, const rct::key &a, const rct::key &point) -{ - ge_p3 p3; - CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&p3, point.bytes) == 0, "ge_frombytes_vartime failed"); - ge_scalarmult_p3(&p3, a.bytes, &p3); - ge_cached cached; - ge_p3_to_cached(&cached, acc_p3); - ge_p1p1 p1; - ge_add(&p1, &p3, &cached); - ge_p1p1_to_p3(acc_p3, &p1); -} - static void add_acc_p3(ge_p3 *acc_p3, const rct::key &point) { ge_p3 p3; @@ -435,10 +423,13 @@ Bulletproof bulletproof_PROVE(const rct::key &sv, const rct::key &gamma) rct::key V; rct::keyV aL(N), aR(N); + rct::key tmp, tmp2; PERF_TIMER_START_BP(PROVE_v); - rct::addKeys2(V, gamma, sv, rct::H); - V = rct::scalarmultKey(V, INV_EIGHT); + rct::key gamma8, sv8; + sc_mul(gamma8.bytes, gamma.bytes, INV_EIGHT.bytes); + sc_mul(sv8.bytes, sv.bytes, INV_EIGHT.bytes); + rct::addKeys2(V, gamma8, sv8, rct::H); PERF_TIMER_STOP(PROVE_v); PERF_TIMER_START_BP(PROVE_aLaR); @@ -515,7 +506,6 @@ Bulletproof bulletproof_PROVE(const rct::key &sv, const rct::key &gamma) const auto yN = vector_powers(y, N); rct::key ip1y = vector_sum(yN); - rct::key tmp; sc_muladd(t0.bytes, z.bytes, ip1y.bytes, t0.bytes); rct::key zsq; @@ -563,10 +553,16 @@ Bulletproof bulletproof_PROVE(const rct::key &sv, const rct::key &gamma) // PAPER LINES 47-48 rct::key tau1 = rct::skGen(), tau2 = rct::skGen(); - rct::key T1 = rct::addKeys(rct::scalarmultH(t1), rct::scalarmultBase(tau1)); - T1 = rct::scalarmultKey(T1, INV_EIGHT); - rct::key T2 = rct::addKeys(rct::scalarmultH(t2), rct::scalarmultBase(tau2)); - T2 = rct::scalarmultKey(T2, INV_EIGHT); + rct::key T1, T2; + ge_p3 p3; + sc_mul(tmp.bytes, t1.bytes, INV_EIGHT.bytes); + sc_mul(tmp2.bytes, tau1.bytes, INV_EIGHT.bytes); + ge_double_scalarmult_base_vartime_p3(&p3, tmp.bytes, &ge_p3_H, tmp2.bytes); + ge_p3_tobytes(T1.bytes, &p3); + sc_mul(tmp.bytes, t2.bytes, INV_EIGHT.bytes); + sc_mul(tmp2.bytes, tau2.bytes, INV_EIGHT.bytes); + ge_double_scalarmult_base_vartime_p3(&p3, tmp.bytes, &ge_p3_H, tmp2.bytes); + ge_p3_tobytes(T2.bytes, &p3); // PAPER LINES 49-51 rct::key x = hash_cache_mash(hash_cache, z, T1, T2); @@ -671,7 +667,7 @@ Bulletproof bulletproof_PROVE(const rct::key &sv, const rct::key &gamma) PERF_TIMER_STOP(PROVE_step4); // PAPER LINE 58 (with inclusions from PAPER LINE 8 and PAPER LINE 20) - return Bulletproof(V, A, S, T1, T2, taux, mu, L, R, aprime[0], bprime[0], t); + return Bulletproof(V, A, S, T1, T2, taux, mu, std::move(L), std::move(R), aprime[0], bprime[0], t); } Bulletproof bulletproof_PROVE(uint64_t v, const rct::key &gamma) @@ -715,13 +711,15 @@ Bulletproof bulletproof_PROVE(const rct::keyV &sv, const rct::keyV &gamma) rct::keyV V(sv.size()); rct::keyV aL(MN), aR(MN); - rct::key tmp; + rct::key tmp, tmp2; PERF_TIMER_START_BP(PROVE_v); for (size_t i = 0; i < sv.size(); ++i) { - rct::addKeys2(V[i], gamma[i], sv[i], rct::H); - V[i] = rct::scalarmultKey(V[i], INV_EIGHT); + rct::key gamma8, sv8; + sc_mul(gamma8.bytes, gamma[i].bytes, INV_EIGHT.bytes); + sc_mul(sv8.bytes, sv[i].bytes, INV_EIGHT.bytes); + rct::addKeys2(V[i], gamma8, sv8, rct::H); } PERF_TIMER_STOP(PROVE_v); @@ -843,10 +841,16 @@ Bulletproof bulletproof_PROVE(const rct::keyV &sv, const rct::keyV &gamma) // PAPER LINES 47-48 rct::key tau1 = rct::skGen(), tau2 = rct::skGen(); - rct::key T1 = rct::addKeys(rct::scalarmultH(t1), rct::scalarmultBase(tau1)); - T1 = rct::scalarmultKey(T1, INV_EIGHT); - rct::key T2 = rct::addKeys(rct::scalarmultH(t2), rct::scalarmultBase(tau2)); - T2 = rct::scalarmultKey(T2, INV_EIGHT); + rct::key T1, T2; + ge_p3 p3; + sc_mul(tmp.bytes, t1.bytes, INV_EIGHT.bytes); + sc_mul(tmp2.bytes, tau1.bytes, INV_EIGHT.bytes); + ge_double_scalarmult_base_vartime_p3(&p3, tmp.bytes, &ge_p3_H, tmp2.bytes); + ge_p3_tobytes(T1.bytes, &p3); + sc_mul(tmp.bytes, t2.bytes, INV_EIGHT.bytes); + sc_mul(tmp2.bytes, tau2.bytes, INV_EIGHT.bytes); + ge_double_scalarmult_base_vartime_p3(&p3, tmp.bytes, &ge_p3_H, tmp2.bytes); + ge_p3_tobytes(T2.bytes, &p3); // PAPER LINES 49-51 rct::key x = hash_cache_mash(hash_cache, z, T1, T2); @@ -972,7 +976,7 @@ Bulletproof bulletproof_PROVE(const rct::keyV &sv, const rct::keyV &gamma) PERF_TIMER_STOP(PROVE_step4); // PAPER LINE 58 (with inclusions from PAPER LINE 8 and PAPER LINE 20) - return Bulletproof(V, A, S, T1, T2, taux, mu, L, R, aprime[0], bprime[0], t); + return Bulletproof(std::move(V), A, S, T1, T2, taux, mu, std::move(L), std::move(R), aprime[0], bprime[0], t); } Bulletproof bulletproof_PROVE(const std::vector &v, const rct::keyV &gamma) @@ -1034,10 +1038,10 @@ bool bulletproof_VERIFY(const std::vector &proofs) // setup weighted aggregates rct::key Z0 = rct::identity(); rct::key z1 = rct::zero(); - rct::key Z2 = rct::identity(); + rct::key &Z2 = Z0; rct::key z3 = rct::zero(); rct::keyV z4(maxMN, rct::zero()), z5(maxMN, rct::zero()); - rct::key Y2 = rct::identity(), Y3 = rct::identity(), Y4 = rct::identity(); + rct::key Y2 = rct::identity(), &Y3 = Y2, &Y4 = Y2; rct::key y0 = rct::zero(), y1 = rct::zero(); for (const Bulletproof *p: proofs) { @@ -1224,11 +1228,8 @@ bool bulletproof_VERIFY(const std::vector &proofs) // now check all proofs at once PERF_TIMER_START_BP(VERIFY_step2_check); ge_p3 check1; - ge_scalarmult_base(&check1, y0.bytes); - addKeys_acc_p3(&check1, y1, rct::H); + ge_double_scalarmult_base_vartime_p3(&check1, y1.bytes, &ge_p3_H, y0.bytes); sub_acc_p3(&check1, Y2); - sub_acc_p3(&check1, Y3); - sub_acc_p3(&check1, Y4); if (!ge_p3_is_point_at_infinity(&check1)) { MERROR("Verification failure at step 1"); @@ -1238,18 +1239,15 @@ bool bulletproof_VERIFY(const std::vector &proofs) sc_sub(tmp.bytes, rct::zero().bytes, z1.bytes); ge_double_scalarmult_base_vartime_p3(&check2, z3.bytes, &ge_p3_H, tmp.bytes); add_acc_p3(&check2, Z0); - add_acc_p3(&check2, Z2); std::vector multiexp_data; multiexp_data.reserve(2 * maxMN); for (size_t i = 0; i < maxMN; ++i) { - sc_sub(tmp.bytes, rct::zero().bytes, z4[i].bytes); - multiexp_data.emplace_back(tmp, Gi_p3[i]); - sc_sub(tmp.bytes, rct::zero().bytes, z5[i].bytes); - multiexp_data.emplace_back(tmp, Hi_p3[i]); + multiexp_data.emplace_back(z4[i], Gi_p3[i]); + multiexp_data.emplace_back(z5[i], Hi_p3[i]); } - add_acc_p3(&check2, multiexp(multiexp_data, true)); + sub_acc_p3(&check2, multiexp(multiexp_data, true)); PERF_TIMER_STOP(VERIFY_step2_check); if (!ge_p3_is_point_at_infinity(&check2)) From 484155d043d661ae1a3d7a196b2354719b90485a Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 8 Aug 2018 15:01:41 +0000 Subject: [PATCH 0635/1404] bulletproofs: some more speedup --- src/ringct/bulletproofs.cc | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/src/ringct/bulletproofs.cc b/src/ringct/bulletproofs.cc index 2d2e6af36d4..5c75e641848 100644 --- a/src/ringct/bulletproofs.cc +++ b/src/ringct/bulletproofs.cc @@ -423,6 +423,7 @@ Bulletproof bulletproof_PROVE(const rct::key &sv, const rct::key &gamma) rct::key V; rct::keyV aL(N), aR(N); + rct::keyV aL8(N), aR8(N); rct::key tmp, tmp2; PERF_TIMER_START_BP(PROVE_v); @@ -438,12 +439,15 @@ Bulletproof bulletproof_PROVE(const rct::key &sv, const rct::key &gamma) if (sv[i/8] & (((uint64_t)1)<<(i%8))) { aL[i] = rct::identity(); + aL8[i] = INV_EIGHT; + aR[i] = aR8[i] = rct::zero(); } else { - aL[i] = rct::zero(); + aL[i] = aL8[i] = rct::zero(); + aR[i] = MINUS_ONE; + aR8[i] = MINUS_INV_EIGHT; } - sc_sub(aR[i].bytes, aL[i].bytes, rct::identity().bytes); } PERF_TIMER_STOP(PROVE_aLaR); @@ -469,10 +473,10 @@ Bulletproof bulletproof_PROVE(const rct::key &sv, const rct::key &gamma) PERF_TIMER_START_BP(PROVE_step1); // PAPER LINES 38-39 rct::key alpha = rct::skGen(); - rct::key ve = vector_exponent(aL, aR); + rct::key ve = vector_exponent(aL8, aR8); rct::key A; - rct::addKeys(A, ve, rct::scalarmultBase(alpha)); - A = rct::scalarmultKey(A, INV_EIGHT); + sc_mul(tmp.bytes, alpha.bytes, INV_EIGHT.bytes); + rct::addKeys(A, ve, rct::scalarmultBase(tmp)); // PAPER LINES 40-42 rct::keyV sL = rct::skvGen(N), sR = rct::skvGen(N); @@ -711,6 +715,7 @@ Bulletproof bulletproof_PROVE(const rct::keyV &sv, const rct::keyV &gamma) rct::keyV V(sv.size()); rct::keyV aL(MN), aR(MN); + rct::keyV aL8(MN), aR8(MN); rct::key tmp, tmp2; PERF_TIMER_START_BP(PROVE_v); @@ -728,19 +733,18 @@ Bulletproof bulletproof_PROVE(const rct::keyV &sv, const rct::keyV &gamma) { for (size_t i = N; i-- > 0; ) { - if (j >= sv.size()) - { - aL[j*N+i] = rct::zero(); - } - else if (sv[j][i/8] & (((uint64_t)1)<<(i%8))) + if (j < sv.size() && (sv[j][i/8] & (((uint64_t)1)<<(i%8)))) { aL[j*N+i] = rct::identity(); + aL8[j*N+i] = INV_EIGHT; + aR[j*N+i] = aR8[j*N+i] = rct::zero(); } else { - aL[j*N+i] = rct::zero(); + aL[j*N+i] = aL8[j*N+i] = rct::zero(); + aR[j*N+i] = MINUS_ONE; + aR8[j*N+i] = MINUS_INV_EIGHT; } - sc_sub(aR[j*N+i].bytes, aL[j*N+i].bytes, rct::identity().bytes); } } PERF_TIMER_STOP(PROVE_aLaR); @@ -771,10 +775,10 @@ Bulletproof bulletproof_PROVE(const rct::keyV &sv, const rct::keyV &gamma) PERF_TIMER_START_BP(PROVE_step1); // PAPER LINES 38-39 rct::key alpha = rct::skGen(); - rct::key ve = vector_exponent(aL, aR); + rct::key ve = vector_exponent(aL8, aR8); rct::key A; - rct::addKeys(A, ve, rct::scalarmultBase(alpha)); - A = rct::scalarmultKey(A, INV_EIGHT); + sc_mul(tmp.bytes, alpha.bytes, INV_EIGHT.bytes); + rct::addKeys(A, ve, rct::scalarmultBase(tmp)); // PAPER LINES 40-42 rct::keyV sL = rct::skvGen(MN), sR = rct::skvGen(MN); From a281b950bff73fc715554d00ec32292ef97b56ec Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 8 Aug 2018 18:39:31 +0000 Subject: [PATCH 0636/1404] bulletproofs: remove single value prover It is now expressed in terms of the array prover --- src/ringct/bulletproofs.cc | 284 +------------------------------------ src/ringct/rctSigs.cpp | 9 -- 2 files changed, 2 insertions(+), 291 deletions(-) diff --git a/src/ringct/bulletproofs.cc b/src/ringct/bulletproofs.cc index 5c75e641848..09d22c6d1d6 100644 --- a/src/ringct/bulletproofs.cc +++ b/src/ringct/bulletproofs.cc @@ -313,17 +313,6 @@ static rct::keyV vector_dup(const rct::key &x, size_t N) return rct::keyV(N, x); } -/* Get the sum of a vector's elements */ -static rct::key vector_sum(const rct::keyV &a) -{ - rct::key res = rct::zero(); - for (size_t i = 0; i < a.size(); ++i) - { - sc_add(res.bytes, res.bytes, a[i].bytes); - } - return res; -} - static rct::key switch_endianness(rct::key k) { std::reverse(k.bytes, k.bytes + sizeof(k)); @@ -414,281 +403,12 @@ static rct::key hash_cache_mash(rct::key &hash_cache, const rct::key &mash0, con /* Given a value v (0..2^N-1) and a mask gamma, construct a range proof */ Bulletproof bulletproof_PROVE(const rct::key &sv, const rct::key &gamma) { - init_exponents(); - - PERF_TIMER_UNIT(PROVE, 1000000); - - constexpr size_t logN = 6; // log2(64) - constexpr size_t N = 1< 0; ) - { - if (sv[i/8] & (((uint64_t)1)<<(i%8))) - { - aL[i] = rct::identity(); - aL8[i] = INV_EIGHT; - aR[i] = aR8[i] = rct::zero(); - } - else - { - aL[i] = aL8[i] = rct::zero(); - aR[i] = MINUS_ONE; - aR8[i] = MINUS_INV_EIGHT; - } - } - PERF_TIMER_STOP(PROVE_aLaR); - - rct::key hash_cache = rct::hash_to_scalar(V); - - // DEBUG: Test to ensure this recovers the value -#ifdef DEBUG_BP - uint64_t test_aL = 0, test_aR = 0; - for (size_t i = 0; i < N; ++i) - { - if (aL[i] == rct::identity()) - test_aL += ((uint64_t)1)< Gprime(N); - std::vector Hprime(N); - rct::keyV aprime(N); - rct::keyV bprime(N); - const rct::key yinv = invert(y); - rct::key yinvpow = rct::identity(); - for (size_t i = 0; i < N; ++i) - { - Gprime[i] = Gi_p3[i]; - ge_scalarmult_p3(&Hprime[i], yinvpow.bytes, &Hi_p3[i]); - sc_mul(yinvpow.bytes, yinvpow.bytes, yinv.bytes); - aprime[i] = l[i]; - bprime[i] = r[i]; - } - rct::keyV L(logN); - rct::keyV R(logN); - int round = 0; - rct::keyV w(logN); // this is the challenge x in the inner product protocol - PERF_TIMER_STOP(PROVE_step3); - - PERF_TIMER_START_BP(PROVE_step4); - // PAPER LINE 13 - while (nprime > 1) - { - // PAPER LINE 15 - nprime /= 2; - - // PAPER LINES 16-17 - rct::key cL = inner_product(slice(aprime, 0, nprime), slice(bprime, nprime, bprime.size())); - rct::key cR = inner_product(slice(aprime, nprime, aprime.size()), slice(bprime, 0, nprime)); - - // PAPER LINES 18-19 - sc_mul(tmp.bytes, cL.bytes, x_ip.bytes); - L[round] = cross_vector_exponent8(nprime, Gprime, nprime, Hprime, 0, aprime, 0, bprime, nprime, &ge_p3_H, &tmp); - sc_mul(tmp.bytes, cR.bytes, x_ip.bytes); - R[round] = cross_vector_exponent8(nprime, Gprime, 0, Hprime, nprime, aprime, nprime, bprime, 0, &ge_p3_H, &tmp); - - // PAPER LINES 21-22 - w[round] = hash_cache_mash(hash_cache, L[round], R[round]); - if (w[round] == rct::zero()) - { - PERF_TIMER_STOP(PROVE_step4); - MINFO("w[round] is 0, trying again"); - goto try_again; - } - - // PAPER LINES 24-25 - const rct::key winv = invert(w[round]); - if (nprime > 1) - { - hadamard_fold(Gprime, winv, w[round]); - hadamard_fold(Hprime, w[round], winv); - } - - // PAPER LINES 28-29 - aprime = vector_add(vector_scalar(slice(aprime, 0, nprime), w[round]), vector_scalar(slice(aprime, nprime, aprime.size()), winv)); - bprime = vector_add(vector_scalar(slice(bprime, 0, nprime), winv), vector_scalar(slice(bprime, nprime, bprime.size()), w[round])); - - ++round; - } - PERF_TIMER_STOP(PROVE_step4); - - // PAPER LINE 58 (with inclusions from PAPER LINE 8 and PAPER LINE 20) - return Bulletproof(V, A, S, T1, T2, taux, mu, std::move(L), std::move(R), aprime[0], bprime[0], t); + return bulletproof_PROVE(rct::keyV(1, sv), rct::keyV(1, gamma)); } Bulletproof bulletproof_PROVE(uint64_t v, const rct::key &gamma) { - // vG + gammaH - PERF_TIMER_START_BP(PROVE_v); - rct::key sv = rct::zero(); - sv.bytes[0] = v & 255; - sv.bytes[1] = (v >> 8) & 255; - sv.bytes[2] = (v >> 16) & 255; - sv.bytes[3] = (v >> 24) & 255; - sv.bytes[4] = (v >> 32) & 255; - sv.bytes[5] = (v >> 40) & 255; - sv.bytes[6] = (v >> 48) & 255; - sv.bytes[7] = (v >> 56) & 255; - PERF_TIMER_STOP(PROVE_v); - return bulletproof_PROVE(sv, gamma); + return bulletproof_PROVE(std::vector(1, v), rct::keyV(1, gamma)); } /* Given a set of values v (0..2^N-1) and masks gamma, construct a range proof */ diff --git a/src/ringct/rctSigs.cpp b/src/ringct/rctSigs.cpp index 0d1789a3886..5ec9a1750d3 100644 --- a/src/ringct/rctSigs.cpp +++ b/src/ringct/rctSigs.cpp @@ -45,15 +45,6 @@ using namespace std; #define CHECK_AND_ASSERT_MES_L1(expr, ret, message) {if(!(expr)) {MCERROR("verify", message); return ret;}} namespace rct { - Bulletproof proveRangeBulletproof(key &C, key &mask, uint64_t amount) - { - mask = rct::skGen(); - Bulletproof proof = bulletproof_PROVE(amount, mask); - CHECK_AND_ASSERT_THROW_MES(proof.V.size() == 1, "V has not exactly one element"); - C = proof.V[0]; - return proof; - } - Bulletproof proveRangeBulletproof(keyV &C, keyV &masks, const std::vector &amounts) { masks = rct::skvGen(amounts.size()); From c415df97bd20b2bcc999c0c0056bb32e9b102a5b Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 8 Aug 2018 21:19:57 +0000 Subject: [PATCH 0637/1404] performance_tests: sc_check and ge_dsm_precomp --- tests/performance_tests/CMakeLists.txt | 2 + tests/performance_tests/crypto_ops.h | 3 ++ tests/performance_tests/main.cpp | 3 ++ tests/performance_tests/sc_check.h | 52 ++++++++++++++++++++++++++ 4 files changed, 60 insertions(+) create mode 100644 tests/performance_tests/sc_check.h diff --git a/tests/performance_tests/CMakeLists.txt b/tests/performance_tests/CMakeLists.txt index 837d39bd3d6..5cd054d8671 100644 --- a/tests/performance_tests/CMakeLists.txt +++ b/tests/performance_tests/CMakeLists.txt @@ -46,6 +46,8 @@ set(performance_tests_headers range_proof.h bulletproof.h crypto_ops.h + sc_reduce32.h + sc_check.h multiexp.h multi_tx_test_base.h performance_tests.h diff --git a/tests/performance_tests/crypto_ops.h b/tests/performance_tests/crypto_ops.h index 3c68583c5a6..4766a120510 100644 --- a/tests/performance_tests/crypto_ops.h +++ b/tests/performance_tests/crypto_ops.h @@ -47,6 +47,7 @@ enum test_op op_scalarmultKey, op_scalarmultH, op_scalarmult8, + op_ge_dsm_precomp, op_ge_double_scalarmult_base_vartime, op_ge_double_scalarmult_precomp_vartime, op_ge_double_scalarmult_precomp_vartime2, @@ -84,6 +85,7 @@ class test_crypto_ops ge_cached tmp_cached; ge_p1p1 tmp_p1p1; ge_p2 tmp_p2; + ge_dsmp dsmp; switch (op) { case op_sc_add: sc_add(key.bytes, scalar0.bytes, scalar1.bytes); break; @@ -101,6 +103,7 @@ class test_crypto_ops case op_scalarmultKey: rct::scalarmultKey(point0, scalar0); break; case op_scalarmultH: rct::scalarmultH(scalar0); break; case op_scalarmult8: rct::scalarmult8(point0); break; + case op_ge_dsm_precomp: ge_dsm_precomp(dsmp, &p3_0); break; case op_ge_double_scalarmult_base_vartime: ge_double_scalarmult_base_vartime(&tmp_p2, scalar0.bytes, &p3_0, scalar1.bytes); break; case op_ge_double_scalarmult_precomp_vartime: ge_double_scalarmult_precomp_vartime(&tmp_p2, scalar0.bytes, &p3_0, scalar1.bytes, precomp0); break; case op_ge_double_scalarmult_precomp_vartime2: ge_double_scalarmult_precomp_vartime2(&tmp_p2, scalar0.bytes, precomp0, scalar1.bytes, precomp1); break; diff --git a/tests/performance_tests/main.cpp b/tests/performance_tests/main.cpp index 87a1573c2c8..3765d124906 100644 --- a/tests/performance_tests/main.cpp +++ b/tests/performance_tests/main.cpp @@ -50,6 +50,7 @@ #include "is_out_to_acc.h" #include "subaddress_expand.h" #include "sc_reduce32.h" +#include "sc_check.h" #include "cn_fast_hash.h" #include "rct_mlsag.h" #include "equality.h" @@ -184,6 +185,7 @@ int main(int argc, char** argv) TEST_PERFORMANCE0(filter, p, test_ge_frombytes_vartime); TEST_PERFORMANCE0(filter, p, test_generate_keypair); TEST_PERFORMANCE0(filter, p, test_sc_reduce32); + TEST_PERFORMANCE0(filter, p, test_sc_check); TEST_PERFORMANCE1(filter, p, test_signature, false); TEST_PERFORMANCE1(filter, p, test_signature, true); @@ -249,6 +251,7 @@ int main(int argc, char** argv) TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_scalarmultKey); TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_scalarmultH); TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_scalarmult8); + TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_ge_dsm_precomp); TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_ge_double_scalarmult_base_vartime); TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_ge_double_scalarmult_precomp_vartime); TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_ge_double_scalarmult_precomp_vartime2); diff --git a/tests/performance_tests/sc_check.h b/tests/performance_tests/sc_check.h new file mode 100644 index 00000000000..036abf12db6 --- /dev/null +++ b/tests/performance_tests/sc_check.h @@ -0,0 +1,52 @@ +// Copyright (c) 2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#pragma once + +#include "crypto/crypto.h" + +class test_sc_check +{ +public: + static const size_t loop_count = 10000000; + + bool init() + { + m_scalar = crypto::rand(); + return true; + } + + bool test() + { + sc_check((unsigned char*)m_scalar.data); + return true; + } + +private: + crypto::ec_scalar m_scalar; +}; From bf8e4b98709ffcf133ea875f3fbf7f99c28e52db Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 8 Aug 2018 21:20:50 +0000 Subject: [PATCH 0638/1404] bulletproofs: some more minor speedup --- src/ringct/bulletproofs.cc | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/ringct/bulletproofs.cc b/src/ringct/bulletproofs.cc index 09d22c6d1d6..f22a109e9e1 100644 --- a/src/ringct/bulletproofs.cc +++ b/src/ringct/bulletproofs.cc @@ -86,11 +86,9 @@ static inline rct::key multiexp(const std::vector &data, bool HiGi return data.size() <= 64 ? straus(data, NULL, 0) : pippenger(data, NULL, get_pippenger_c(data.size())); } -static bool is_reduced(const rct::key &scalar) +static inline bool is_reduced(const rct::key &scalar) { - rct::key reduced = scalar; - sc_reduce32(reduced.bytes); - return scalar == reduced; + return sc_check(scalar.bytes) == 0; } static void add_acc_p3(ge_p3 *acc_p3, const rct::key &point) @@ -139,8 +137,8 @@ static void init_exponents() Gi[i] = get_exponent(rct::H, i * 2 + 1); CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&Gi_p3[i], Gi[i].bytes) == 0, "ge_frombytes_vartime failed"); - data.push_back({rct::zero(), Gi[i]}); - data.push_back({rct::zero(), Hi[i]}); + data.push_back({rct::zero(), Gi_p3[i]}); + data.push_back({rct::zero(), Hi_p3[i]}); } straus_HiGi_cache = straus_init_cache(data, STRAUS_SIZE_LIMIT); From 4061960a16d27f803ca54b8887f46185c29c336e Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 13 Aug 2018 11:18:54 +0000 Subject: [PATCH 0639/1404] multiexp: pack the digits table when STRAUS_C is 4 Spotted by stoffu --- src/ringct/multiexp.cc | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/ringct/multiexp.cc b/src/ringct/multiexp.cc index 21957b94ce5..fb2f1855163 100644 --- a/src/ringct/multiexp.cc +++ b/src/ringct/multiexp.cc @@ -320,7 +320,7 @@ rct::key bos_coster_heap_conv_robust(std::vector data) return res; } -static constexpr unsigned int STRAUS_C = 4; +#define STRAUS_C 4 struct straus_cached_data { @@ -447,26 +447,23 @@ rct::key straus(const std::vector &data, const std::shared_ptr digits{new uint8_t[64 * data.size()]}; +#else std::unique_ptr digits{new uint8_t[256 * data.size()]}; +#endif for (size_t j = 0; j < data.size(); ++j) { unsigned char bytes33[33]; memcpy(bytes33, data[j].scalar.bytes, 32); bytes33[32] = 0; const unsigned char *bytes = bytes33; -#if 1 - static_assert(STRAUS_C == 4, "optimized version needs STRAUS_C == 4"); +#if STRAUS_C==4 unsigned int i; - for (i = 0; i < 256; i += 8, bytes++) + for (i = 0; i < 64; i += 2, bytes++) { - digits[j*256+i] = bytes[0] & 0xf; - digits[j*256+i+1] = (bytes[0] >> 1) & 0xf; - digits[j*256+i+2] = (bytes[0] >> 2) & 0xf; - digits[j*256+i+3] = (bytes[0] >> 3) & 0xf; - digits[j*256+i+4] = ((bytes[0] >> 4) | (bytes[1]<<4)) & 0xf; - digits[j*256+i+5] = ((bytes[0] >> 5) | (bytes[1]<<3)) & 0xf; - digits[j*256+i+6] = ((bytes[0] >> 6) | (bytes[1]<<2)) & 0xf; - digits[j*256+i+7] = ((bytes[0] >> 7) | (bytes[1]<<1)) & 0xf; + digits[j*64+i] = bytes[0] & 0xf; + digits[j*64+i+1] = bytes[0] >> 4; } #elif 1 for (size_t i = 0; i < 256; ++i) @@ -521,7 +518,11 @@ rct::key straus(const std::vector &data, const std::shared_ptr Date: Thu, 23 Aug 2018 18:52:05 +0000 Subject: [PATCH 0640/1404] bulletproofs: merge multiexps as per sarang's new python code --- src/ringct/bulletproofs.cc | 106 +++++++++++++------------------------ 1 file changed, 38 insertions(+), 68 deletions(-) diff --git a/src/ringct/bulletproofs.cc b/src/ringct/bulletproofs.cc index f22a109e9e1..549e5229602 100644 --- a/src/ringct/bulletproofs.cc +++ b/src/ringct/bulletproofs.cc @@ -91,28 +91,6 @@ static inline bool is_reduced(const rct::key &scalar) return sc_check(scalar.bytes) == 0; } -static void add_acc_p3(ge_p3 *acc_p3, const rct::key &point) -{ - ge_p3 p3; - CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&p3, point.bytes) == 0, "ge_frombytes_vartime failed"); - ge_cached cached; - ge_p3_to_cached(&cached, &p3); - ge_p1p1 p1; - ge_add(&p1, acc_p3, &cached); - ge_p1p1_to_p3(acc_p3, &p1); -} - -static void sub_acc_p3(ge_p3 *acc_p3, const rct::key &point) -{ - ge_p3 p3; - CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&p3, point.bytes) == 0, "ge_frombytes_vartime failed"); - ge_cached cached; - ge_p3_to_cached(&cached, &p3); - ge_p1p1 p1; - ge_sub(&p1, acc_p3, &cached); - ge_p1p1_to_p3(acc_p3, &p1); -} - static rct::key get_exponent(const rct::key &base, size_t idx) { static const std::string salt("bulletproof"); @@ -733,6 +711,7 @@ bool bulletproof_VERIFY(const std::vector &proofs) // sanity and figure out which proof is longest size_t max_length = 0; + size_t nV = 0; for (const Bulletproof *p: proofs) { const Bulletproof &proof = *p; @@ -749,6 +728,7 @@ bool bulletproof_VERIFY(const std::vector &proofs) CHECK_AND_ASSERT_MES(proof.L.size() > 0, false, "Empty proof"); max_length = std::max(max_length, proof.L.size()); + nV += proof.V.size(); } CHECK_AND_ASSERT_MES(max_length < 32, false, "At least one proof is too large"); size_t maxMN = 1u << max_length; @@ -757,13 +737,13 @@ bool bulletproof_VERIFY(const std::vector &proofs) const size_t N = 1 << logN; rct::key tmp; + std::vector multiexp_data; + multiexp_data.reserve(nV + (2 * (10/*logM*/ + logN) + 4) * proofs.size() + 2 * maxMN); + // setup weighted aggregates - rct::key Z0 = rct::identity(); rct::key z1 = rct::zero(); - rct::key &Z2 = Z0; rct::key z3 = rct::zero(); rct::keyV z4(maxMN, rct::zero()), z5(maxMN, rct::zero()); - rct::key Y2 = rct::identity(), &Y3 = Y2, &Y4 = Y2; rct::key y0 = rct::zero(), y1 = rct::zero(); for (const Bulletproof *p: proofs) { @@ -773,7 +753,8 @@ bool bulletproof_VERIFY(const std::vector &proofs) for (logM = 0; (M = 1< &proofs) rct::key proof8_T1 = rct::scalarmult8(proof.T1); rct::key proof8_T2 = rct::scalarmult8(proof.T2); rct::key proof8_S = rct::scalarmult8(proof.S); + rct::key proof8_A = rct::scalarmult8(proof.A); PERF_TIMER_START_BP(VERIFY_line_61); // PAPER LINE 61 - sc_muladd(y0.bytes, proof.taux.bytes, weight.bytes, y0.bytes); + sc_muladd(y0.bytes, proof.taux.bytes, weight_y.bytes, y0.bytes); const rct::keyV zpow = vector_powers(z, M+3); @@ -814,26 +796,26 @@ bool bulletproof_VERIFY(const std::vector &proofs) PERF_TIMER_START_BP(VERIFY_line_61rl_new); sc_muladd(tmp.bytes, z.bytes, ip1y.bytes, k.bytes); - std::vector multiexp_data; - multiexp_data.reserve(proof.V.size()); sc_sub(tmp.bytes, proof.t.bytes, tmp.bytes); - sc_muladd(y1.bytes, tmp.bytes, weight.bytes, y1.bytes); + sc_muladd(y1.bytes, tmp.bytes, weight_y.bytes, y1.bytes); for (size_t j = 0; j < proof8_V.size(); j++) { - multiexp_data.emplace_back(zpow[j+2], proof8_V[j]); + sc_mul(tmp.bytes, zpow[j+2].bytes, weight_y.bytes); + multiexp_data.emplace_back(tmp, proof8_V[j]); } - rct::addKeys(Y2, Y2, rct::scalarmultKey(multiexp(multiexp_data, false), weight)); - sc_mul(tmp.bytes, x.bytes, weight.bytes); - rct::addKeys(Y3, Y3, rct::scalarmultKey(proof8_T1, tmp)); + sc_mul(tmp.bytes, x.bytes, weight_y.bytes); + multiexp_data.emplace_back(tmp, proof8_T1); rct::key xsq; sc_mul(xsq.bytes, x.bytes, x.bytes); - sc_mul(tmp.bytes, xsq.bytes, weight.bytes); - rct::addKeys(Y4, Y4, rct::scalarmultKey(proof8_T2, tmp)); + sc_mul(tmp.bytes, xsq.bytes, weight_y.bytes); + multiexp_data.emplace_back(tmp, proof8_T2); PERF_TIMER_STOP(VERIFY_line_61rl_new); PERF_TIMER_START_BP(VERIFY_line_62); // PAPER LINE 62 - rct::addKeys(Z0, Z0, rct::scalarmultKey(rct::addKeys(rct::scalarmult8(proof.A), rct::scalarmultKey(proof8_S, x)), weight)); + multiexp_data.emplace_back(weight_z, proof8_A); + sc_mul(tmp.bytes, x.bytes, weight_z.bytes); + multiexp_data.emplace_back(tmp, proof8_S); PERF_TIMER_STOP(VERIFY_line_62); // Compute the number of rounds for the inner product @@ -909,8 +891,8 @@ bool bulletproof_VERIFY(const std::vector &proofs) sc_mulsub(h_scalar.bytes, tmp.bytes, yinvpow.bytes, h_scalar.bytes); } - sc_muladd(z4[i].bytes, g_scalar.bytes, weight.bytes, z4[i].bytes); - sc_muladd(z5[i].bytes, h_scalar.bytes, weight.bytes, z5[i].bytes); + sc_muladd(z4[i].bytes, g_scalar.bytes, weight_z.bytes, z4[i].bytes); + sc_muladd(z5[i].bytes, h_scalar.bytes, weight_z.bytes, z5[i].bytes); if (i == 0) { @@ -928,55 +910,43 @@ bool bulletproof_VERIFY(const std::vector &proofs) // PAPER LINE 26 PERF_TIMER_START_BP(VERIFY_line_26_new); - multiexp_data.clear(); - multiexp_data.reserve(2*rounds); - - sc_muladd(z1.bytes, proof.mu.bytes, weight.bytes, z1.bytes); + sc_muladd(z1.bytes, proof.mu.bytes, weight_z.bytes, z1.bytes); for (size_t i = 0; i < rounds; ++i) { sc_mul(tmp.bytes, w[i].bytes, w[i].bytes); + sc_mul(tmp.bytes, tmp.bytes, weight_z.bytes); multiexp_data.emplace_back(tmp, proof8_L[i]); sc_mul(tmp.bytes, winv[i].bytes, winv[i].bytes); + sc_mul(tmp.bytes, tmp.bytes, weight_z.bytes); multiexp_data.emplace_back(tmp, proof8_R[i]); } - rct::key acc = multiexp(multiexp_data, false); - rct::addKeys(Z2, Z2, rct::scalarmultKey(acc, weight)); sc_mulsub(tmp.bytes, proof.a.bytes, proof.b.bytes, proof.t.bytes); sc_mul(tmp.bytes, tmp.bytes, x_ip.bytes); - sc_muladd(z3.bytes, tmp.bytes, weight.bytes, z3.bytes); + sc_muladd(z3.bytes, tmp.bytes, weight_z.bytes, z3.bytes); PERF_TIMER_STOP(VERIFY_line_26_new); } // now check all proofs at once PERF_TIMER_START_BP(VERIFY_step2_check); - ge_p3 check1; - ge_double_scalarmult_base_vartime_p3(&check1, y1.bytes, &ge_p3_H, y0.bytes); - sub_acc_p3(&check1, Y2); - if (!ge_p3_is_point_at_infinity(&check1)) - { - MERROR("Verification failure at step 1"); - return false; - } - ge_p3 check2; - sc_sub(tmp.bytes, rct::zero().bytes, z1.bytes); - ge_double_scalarmult_base_vartime_p3(&check2, z3.bytes, &ge_p3_H, tmp.bytes); - add_acc_p3(&check2, Z0); - - std::vector multiexp_data; - multiexp_data.reserve(2 * maxMN); + sc_sub(tmp.bytes, rct::zero().bytes, y0.bytes); + sc_sub(tmp.bytes, tmp.bytes, z1.bytes); + multiexp_data.emplace_back(tmp, rct::G); + sc_sub(tmp.bytes, z3.bytes, y1.bytes); + multiexp_data.emplace_back(tmp, rct::H); for (size_t i = 0; i < maxMN; ++i) { - multiexp_data.emplace_back(z4[i], Gi_p3[i]); - multiexp_data.emplace_back(z5[i], Hi_p3[i]); + sc_sub(tmp.bytes, rct::zero().bytes, z4[i].bytes); + multiexp_data.emplace_back(tmp, Gi_p3[i]); + sc_sub(tmp.bytes, rct::zero().bytes, z5[i].bytes); + multiexp_data.emplace_back(tmp, Hi_p3[i]); } - sub_acc_p3(&check2, multiexp(multiexp_data, true)); - PERF_TIMER_STOP(VERIFY_step2_check); - - if (!ge_p3_is_point_at_infinity(&check2)) + if (!(multiexp(multiexp_data, false) == rct::identity())) { - MERROR("Verification failure at step 2"); + PERF_TIMER_STOP(VERIFY_step2_check); + MERROR("Verification failure"); return false; } + PERF_TIMER_STOP(VERIFY_step2_check); PERF_TIMER_STOP(VERIFY); return true; From 8629a42cf6e4650b552925f7637761b8e7ee66e3 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 22 Aug 2018 22:30:14 +0000 Subject: [PATCH 0641/1404] bulletproofs: rework flow to use sarang's fast batch inversion code --- src/ringct/bulletproofs.cc | 233 ++++++++++++++++++++++++------------- 1 file changed, 155 insertions(+), 78 deletions(-) diff --git a/src/ringct/bulletproofs.cc b/src/ringct/bulletproofs.cc index 549e5229602..d9961cb2044 100644 --- a/src/ringct/bulletproofs.cc +++ b/src/ringct/bulletproofs.cc @@ -29,8 +29,6 @@ // Adapted from Java code by Sarang Noether #include -#include -#include #include #include "misc_log_ex.h" #include "common/perf_timer.h" @@ -289,37 +287,59 @@ static rct::keyV vector_dup(const rct::key &x, size_t N) return rct::keyV(N, x); } -static rct::key switch_endianness(rct::key k) +static rct::key sm(rct::key y, int n, const rct::key &x) { - std::reverse(k.bytes, k.bytes + sizeof(k)); - return k; + while (n--) + sc_mul(y.bytes, y.bytes, y.bytes); + sc_mul(y.bytes, y.bytes, x.bytes); + return y; } -/* Compute the inverse of a scalar, the stupid way */ +/* Compute the inverse of a scalar, the clever way */ static rct::key invert(const rct::key &x) { - rct::key inv; - - BN_CTX *ctx = BN_CTX_new(); - BIGNUM *X = BN_new(); - BIGNUM *L = BN_new(); - BIGNUM *I = BN_new(); - - BN_bin2bn(switch_endianness(x).bytes, sizeof(rct::key), X); - BN_bin2bn(switch_endianness(rct::curveOrder()).bytes, sizeof(rct::key), L); - - CHECK_AND_ASSERT_THROW_MES(BN_mod_inverse(I, X, L, ctx), "Failed to invert"); + rct::key _1, _10, _100, _11, _101, _111, _1001, _1011, _1111; + + _1 = x; + sc_mul(_10.bytes, _1.bytes, _1.bytes); + sc_mul(_100.bytes, _10.bytes, _10.bytes); + sc_mul(_11.bytes, _10.bytes, _1.bytes); + sc_mul(_101.bytes, _10.bytes, _11.bytes); + sc_mul(_111.bytes, _10.bytes, _101.bytes); + sc_mul(_1001.bytes, _10.bytes, _111.bytes); + sc_mul(_1011.bytes, _10.bytes, _1001.bytes); + sc_mul(_1111.bytes, _100.bytes, _1011.bytes); - const int len = BN_num_bytes(I); - CHECK_AND_ASSERT_THROW_MES((size_t)len <= sizeof(rct::key), "Invalid number length"); - inv = rct::zero(); - BN_bn2bin(I, inv.bytes); - std::reverse(inv.bytes, inv.bytes + len); - - BN_free(I); - BN_free(L); - BN_free(X); - BN_CTX_free(ctx); + rct::key inv; + sc_mul(inv.bytes, _1111.bytes, _1.bytes); + + inv = sm(inv, 123 + 3, _101); + inv = sm(inv, 2 + 2, _11); + inv = sm(inv, 1 + 4, _1111); + inv = sm(inv, 1 + 4, _1111); + inv = sm(inv, 4, _1001); + inv = sm(inv, 2, _11); + inv = sm(inv, 1 + 4, _1111); + inv = sm(inv, 1 + 3, _101); + inv = sm(inv, 3 + 3, _101); + inv = sm(inv, 3, _111); + inv = sm(inv, 1 + 4, _1111); + inv = sm(inv, 2 + 3, _111); + inv = sm(inv, 2 + 2, _11); + inv = sm(inv, 1 + 4, _1011); + inv = sm(inv, 2 + 4, _1011); + inv = sm(inv, 6 + 4, _1001); + inv = sm(inv, 2 + 2, _11); + inv = sm(inv, 3 + 2, _11); + inv = sm(inv, 3 + 2, _11); + inv = sm(inv, 1 + 4, _1001); + inv = sm(inv, 1 + 3, _111); + inv = sm(inv, 2 + 4, _1111); + inv = sm(inv, 1 + 4, _1011); + inv = sm(inv, 3, _101); + inv = sm(inv, 2 + 4, _1111); + inv = sm(inv, 3, _101); + inv = sm(inv, 1 + 2, _11); #ifdef DEBUG_BP rct::key tmp; @@ -329,6 +349,34 @@ static rct::key invert(const rct::key &x) return inv; } +static rct::keyV invert(rct::keyV x) +{ + rct::keyV scratch; + scratch.reserve(x.size()); + + rct::key acc = rct::identity(); + for (size_t n = 0; n < x.size(); ++n) + { + scratch.push_back(acc); + if (n == 0) + acc = x[0]; + else + sc_mul(acc.bytes, acc.bytes, x[n].bytes); + } + + acc = invert(acc); + + rct::key tmp; + for (int i = x.size(); i-- > 0; ) + { + sc_mul(tmp.bytes, acc.bytes, x[i].bytes); + sc_mul(x[i].bytes, acc.bytes, scratch[i].bytes); + acc = tmp; + } + + return x; +} + /* Compute the slice of a vector */ static rct::keyV slice(const rct::keyV &a, size_t start, size_t stop) { @@ -702,6 +750,13 @@ Bulletproof bulletproof_PROVE(const std::vector &v, const rct::keyV &g return bulletproof_PROVE(sv, gamma); } +struct proof_data_t +{ + rct::key x, y, z, x_ip; + std::vector w; + size_t logM, inv_offset; +}; + /* Given a range proof, determine if it is valid */ bool bulletproof_VERIFY(const std::vector &proofs) { @@ -709,9 +764,17 @@ bool bulletproof_VERIFY(const std::vector &proofs) PERF_TIMER_START_BP(VERIFY); + const size_t logN = 6; + const size_t N = 1 << logN; + // sanity and figure out which proof is longest size_t max_length = 0; size_t nV = 0; + std::vector proof_data; + proof_data.reserve(proofs.size()); + size_t inv_offset = 0; + std::vector to_invert; + to_invert.reserve(11 * sizeof(proofs)); for (const Bulletproof *p: proofs) { const Bulletproof &proof = *p; @@ -729,46 +792,75 @@ bool bulletproof_VERIFY(const std::vector &proofs) max_length = std::max(max_length, proof.L.size()); nV += proof.V.size(); + + // Reconstruct the challenges + PERF_TIMER_START_BP(VERIFY_start); + proof_data.resize(proof_data.size() + 1); + proof_data_t &pd = proof_data.back(); + rct::key hash_cache = rct::hash_to_scalar(proof.V); + pd.y = hash_cache_mash(hash_cache, proof.A, proof.S); + CHECK_AND_ASSERT_MES(!(pd.y == rct::zero()), false, "y == 0"); + pd.z = hash_cache = rct::hash_to_scalar(pd.y); + CHECK_AND_ASSERT_MES(!(pd.z == rct::zero()), false, "z == 0"); + pd.x = hash_cache_mash(hash_cache, pd.z, proof.T1, proof.T2); + CHECK_AND_ASSERT_MES(!(pd.x == rct::zero()), false, "x == 0"); + pd.x_ip = hash_cache_mash(hash_cache, pd.x, proof.taux, proof.mu, proof.t); + CHECK_AND_ASSERT_MES(!(pd.x_ip == rct::zero()), false, "x_ip == 0"); + PERF_TIMER_STOP(VERIFY_start); + + size_t M; + for (pd.logM = 0; (M = 1< 0, false, "Zero rounds"); + + PERF_TIMER_START_BP(VERIFY_line_21_22); + // PAPER LINES 21-22 + // The inner product challenges are computed per round + pd.w.resize(rounds); + for (size_t i = 0; i < rounds; ++i) + { + pd.w[i] = hash_cache_mash(hash_cache, proof.L[i], proof.R[i]); + CHECK_AND_ASSERT_MES(!(pd.w[i] == rct::zero()), false, "w[i] == 0"); + } + PERF_TIMER_STOP(VERIFY_line_21_22); + + pd.inv_offset = inv_offset; + for (size_t i = 0; i < rounds; ++i) + to_invert.push_back(pd.w[i]); + to_invert.push_back(pd.y); + inv_offset += rounds + 1; } CHECK_AND_ASSERT_MES(max_length < 32, false, "At least one proof is too large"); size_t maxMN = 1u << max_length; - const size_t logN = 6; - const size_t N = 1 << logN; rct::key tmp; std::vector multiexp_data; multiexp_data.reserve(nV + (2 * (10/*logM*/ + logN) + 4) * proofs.size() + 2 * maxMN); + PERF_TIMER_START_BP(VERIFY_line_24_25_invert); + const std::vector inverses = invert(to_invert); + PERF_TIMER_STOP(VERIFY_line_24_25_invert); + // setup weighted aggregates rct::key z1 = rct::zero(); rct::key z3 = rct::zero(); rct::keyV z4(maxMN, rct::zero()), z5(maxMN, rct::zero()); rct::key y0 = rct::zero(), y1 = rct::zero(); + int proof_data_index = 0; for (const Bulletproof *p: proofs) { const Bulletproof &proof = *p; + const proof_data_t &pd = proof_data[proof_data_index++]; - size_t M, logM; - for (logM = 0; (M = 1< &proofs) // PAPER LINE 61 sc_muladd(y0.bytes, proof.taux.bytes, weight_y.bytes, y0.bytes); - const rct::keyV zpow = vector_powers(z, M+3); + const rct::keyV zpow = vector_powers(pd.z, M+3); rct::key k; - const rct::key ip1y = vector_power_sum(y, MN); + const rct::key ip1y = vector_power_sum(pd.y, MN); sc_mulsub(k.bytes, zpow[2].bytes, ip1y.bytes, rct::zero().bytes); for (size_t j = 1; j <= M; ++j) { @@ -795,7 +887,7 @@ bool bulletproof_VERIFY(const std::vector &proofs) PERF_TIMER_STOP(VERIFY_line_61); PERF_TIMER_START_BP(VERIFY_line_61rl_new); - sc_muladd(tmp.bytes, z.bytes, ip1y.bytes, k.bytes); + sc_muladd(tmp.bytes, pd.z.bytes, ip1y.bytes, k.bytes); sc_sub(tmp.bytes, proof.t.bytes, tmp.bytes); sc_muladd(y1.bytes, tmp.bytes, weight_y.bytes, y1.bytes); for (size_t j = 0; j < proof8_V.size(); j++) @@ -803,10 +895,10 @@ bool bulletproof_VERIFY(const std::vector &proofs) sc_mul(tmp.bytes, zpow[j+2].bytes, weight_y.bytes); multiexp_data.emplace_back(tmp, proof8_V[j]); } - sc_mul(tmp.bytes, x.bytes, weight_y.bytes); + sc_mul(tmp.bytes, pd.x.bytes, weight_y.bytes); multiexp_data.emplace_back(tmp, proof8_T1); rct::key xsq; - sc_mul(xsq.bytes, x.bytes, x.bytes); + sc_mul(xsq.bytes, pd.x.bytes, pd.x.bytes); sc_mul(tmp.bytes, xsq.bytes, weight_y.bytes); multiexp_data.emplace_back(tmp, proof8_T2); PERF_TIMER_STOP(VERIFY_line_61rl_new); @@ -814,49 +906,34 @@ bool bulletproof_VERIFY(const std::vector &proofs) PERF_TIMER_START_BP(VERIFY_line_62); // PAPER LINE 62 multiexp_data.emplace_back(weight_z, proof8_A); - sc_mul(tmp.bytes, x.bytes, weight_z.bytes); + sc_mul(tmp.bytes, pd.x.bytes, weight_z.bytes); multiexp_data.emplace_back(tmp, proof8_S); PERF_TIMER_STOP(VERIFY_line_62); // Compute the number of rounds for the inner product - const size_t rounds = logM+logN; + const size_t rounds = pd.logM+logN; CHECK_AND_ASSERT_MES(rounds > 0, false, "Zero rounds"); - PERF_TIMER_START_BP(VERIFY_line_21_22); - // PAPER LINES 21-22 - // The inner product challenges are computed per round - rct::keyV w(rounds); - for (size_t i = 0; i < rounds; ++i) - { - w[i] = hash_cache_mash(hash_cache, proof.L[i], proof.R[i]); - CHECK_AND_ASSERT_MES(!(w[i] == rct::zero()), false, "w[i] == 0"); - } - PERF_TIMER_STOP(VERIFY_line_21_22); - PERF_TIMER_START_BP(VERIFY_line_24_25); // Basically PAPER LINES 24-25 // Compute the curvepoints from G[i] and H[i] rct::key yinvpow = rct::identity(); rct::key ypow = rct::identity(); - PERF_TIMER_START_BP(VERIFY_line_24_25_invert); - const rct::key yinv = invert(y); - rct::keyV winv(rounds); - for (size_t i = 0; i < rounds; ++i) - winv[i] = invert(w[i]); - PERF_TIMER_STOP(VERIFY_line_24_25_invert); + const rct::key *winv = &inverses[pd.inv_offset]; + const rct::key yinv = inverses[pd.inv_offset + rounds]; // precalc PERF_TIMER_START_BP(VERIFY_line_24_25_precalc); rct::keyV w_cache(1< 0; --s) { - sc_mul(w_cache[s].bytes, w_cache[s/2].bytes, w[j].bytes); + sc_mul(w_cache[s].bytes, w_cache[s/2].bytes, pd.w[j].bytes); sc_mul(w_cache[s-1].bytes, w_cache[s/2].bytes, winv[j].bytes); } } @@ -876,18 +953,18 @@ bool bulletproof_VERIFY(const std::vector &proofs) sc_mul(h_scalar.bytes, h_scalar.bytes, w_cache[(~i) & (MN-1)].bytes); // Adjust the scalars using the exponents from PAPER LINE 62 - sc_add(g_scalar.bytes, g_scalar.bytes, z.bytes); + sc_add(g_scalar.bytes, g_scalar.bytes, pd.z.bytes); CHECK_AND_ASSERT_MES(2+i/N < zpow.size(), false, "invalid zpow index"); CHECK_AND_ASSERT_MES(i%N < twoN.size(), false, "invalid twoN index"); sc_mul(tmp.bytes, zpow[2+i/N].bytes, twoN[i%N].bytes); if (i == 0) { - sc_add(tmp.bytes, tmp.bytes, z.bytes); + sc_add(tmp.bytes, tmp.bytes, pd.z.bytes); sc_sub(h_scalar.bytes, h_scalar.bytes, tmp.bytes); } else { - sc_muladd(tmp.bytes, z.bytes, ypow.bytes, tmp.bytes); + sc_muladd(tmp.bytes, pd.z.bytes, ypow.bytes, tmp.bytes); sc_mulsub(h_scalar.bytes, tmp.bytes, yinvpow.bytes, h_scalar.bytes); } @@ -897,12 +974,12 @@ bool bulletproof_VERIFY(const std::vector &proofs) if (i == 0) { yinvpow = yinv; - ypow = y; + ypow = pd.y; } else if (i != MN-1) { sc_mul(yinvpow.bytes, yinvpow.bytes, yinv.bytes); - sc_mul(ypow.bytes, ypow.bytes, y.bytes); + sc_mul(ypow.bytes, ypow.bytes, pd.y.bytes); } } @@ -913,7 +990,7 @@ bool bulletproof_VERIFY(const std::vector &proofs) sc_muladd(z1.bytes, proof.mu.bytes, weight_z.bytes, z1.bytes); for (size_t i = 0; i < rounds; ++i) { - sc_mul(tmp.bytes, w[i].bytes, w[i].bytes); + sc_mul(tmp.bytes, pd.w[i].bytes, pd.w[i].bytes); sc_mul(tmp.bytes, tmp.bytes, weight_z.bytes); multiexp_data.emplace_back(tmp, proof8_L[i]); sc_mul(tmp.bytes, winv[i].bytes, winv[i].bytes); @@ -921,7 +998,7 @@ bool bulletproof_VERIFY(const std::vector &proofs) multiexp_data.emplace_back(tmp, proof8_R[i]); } sc_mulsub(tmp.bytes, proof.a.bytes, proof.b.bytes, proof.t.bytes); - sc_mul(tmp.bytes, tmp.bytes, x_ip.bytes); + sc_mul(tmp.bytes, tmp.bytes, pd.x_ip.bytes); sc_muladd(z3.bytes, tmp.bytes, weight_z.bytes, z3.bytes); PERF_TIMER_STOP(VERIFY_line_26_new); } From 10e5a9276953ece1f96d6801fe6d91d550c5dfae Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 24 Aug 2018 18:51:14 +0000 Subject: [PATCH 0642/1404] bulletproofs: maintain -z4, -z5, and -y0 to avoid subtractions --- src/ringct/bulletproofs.cc | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/ringct/bulletproofs.cc b/src/ringct/bulletproofs.cc index d9961cb2044..9e4d855349e 100644 --- a/src/ringct/bulletproofs.cc +++ b/src/ringct/bulletproofs.cc @@ -847,8 +847,8 @@ bool bulletproof_VERIFY(const std::vector &proofs) // setup weighted aggregates rct::key z1 = rct::zero(); rct::key z3 = rct::zero(); - rct::keyV z4(maxMN, rct::zero()), z5(maxMN, rct::zero()); - rct::key y0 = rct::zero(), y1 = rct::zero(); + rct::keyV m_z4(maxMN, rct::zero()), m_z5(maxMN, rct::zero()); + rct::key m_y0 = rct::zero(), y1 = rct::zero(); int proof_data_index = 0; for (const Bulletproof *p: proofs) { @@ -872,7 +872,7 @@ bool bulletproof_VERIFY(const std::vector &proofs) PERF_TIMER_START_BP(VERIFY_line_61); // PAPER LINE 61 - sc_muladd(y0.bytes, proof.taux.bytes, weight_y.bytes, y0.bytes); + sc_mulsub(m_y0.bytes, proof.taux.bytes, weight_y.bytes, m_y0.bytes); const rct::keyV zpow = vector_powers(pd.z, M+3); @@ -968,8 +968,8 @@ bool bulletproof_VERIFY(const std::vector &proofs) sc_mulsub(h_scalar.bytes, tmp.bytes, yinvpow.bytes, h_scalar.bytes); } - sc_muladd(z4[i].bytes, g_scalar.bytes, weight_z.bytes, z4[i].bytes); - sc_muladd(z5[i].bytes, h_scalar.bytes, weight_z.bytes, z5[i].bytes); + sc_mulsub(m_z4[i].bytes, g_scalar.bytes, weight_z.bytes, m_z4[i].bytes); + sc_mulsub(m_z5[i].bytes, h_scalar.bytes, weight_z.bytes, m_z5[i].bytes); if (i == 0) { @@ -1005,17 +1005,14 @@ bool bulletproof_VERIFY(const std::vector &proofs) // now check all proofs at once PERF_TIMER_START_BP(VERIFY_step2_check); - sc_sub(tmp.bytes, rct::zero().bytes, y0.bytes); - sc_sub(tmp.bytes, tmp.bytes, z1.bytes); + sc_sub(tmp.bytes, m_y0.bytes, z1.bytes); multiexp_data.emplace_back(tmp, rct::G); sc_sub(tmp.bytes, z3.bytes, y1.bytes); multiexp_data.emplace_back(tmp, rct::H); for (size_t i = 0; i < maxMN; ++i) { - sc_sub(tmp.bytes, rct::zero().bytes, z4[i].bytes); - multiexp_data.emplace_back(tmp, Gi_p3[i]); - sc_sub(tmp.bytes, rct::zero().bytes, z5[i].bytes); - multiexp_data.emplace_back(tmp, Hi_p3[i]); + multiexp_data.emplace_back(m_z4[i], Gi_p3[i]); + multiexp_data.emplace_back(m_z5[i], Hi_p3[i]); } if (!(multiexp(multiexp_data, false) == rct::identity())) { From 6f9ae5b6eb535fb748722ed1c81f981474422ead Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 24 Aug 2018 21:20:56 +0000 Subject: [PATCH 0643/1404] multiexp: handle pippenger multiexps with part precalc --- src/ringct/bulletproofs.cc | 21 +++++++++++---------- src/ringct/multiexp.cc | 21 ++++++++++++++------- src/ringct/multiexp.h | 4 ++-- tests/performance_tests/multiexp.h | 4 ++-- 4 files changed, 29 insertions(+), 21 deletions(-) diff --git a/src/ringct/bulletproofs.cc b/src/ringct/bulletproofs.cc index 9e4d855349e..a3af9264e1a 100644 --- a/src/ringct/bulletproofs.cc +++ b/src/ringct/bulletproofs.cc @@ -73,15 +73,15 @@ static const rct::keyV twoN = vector_powers(TWO, maxN); static const rct::key ip12 = inner_product(oneN, twoN); static boost::mutex init_mutex; -static inline rct::key multiexp(const std::vector &data, bool HiGi) +static inline rct::key multiexp(const std::vector &data, size_t HiGi_size) { - if (HiGi) + if (HiGi_size > 0) { static_assert(128 <= STRAUS_SIZE_LIMIT, "Straus in precalc mode can only be calculated till STRAUS_SIZE_LIMIT"); - return data.size() <= 128 ? straus(data, straus_HiGi_cache, 0) : pippenger(data, pippenger_HiGi_cache, get_pippenger_c(data.size())); + return HiGi_size <= 128 && data.size() == HiGi_size ? straus(data, straus_HiGi_cache, 0) : pippenger(data, pippenger_HiGi_cache, HiGi_size, get_pippenger_c(data.size())); } else - return data.size() <= 64 ? straus(data, NULL, 0) : pippenger(data, NULL, get_pippenger_c(data.size())); + return data.size() <= 64 ? straus(data, NULL, 0) : pippenger(data, NULL, 0, get_pippenger_c(data.size())); } static inline bool is_reduced(const rct::key &scalar) @@ -118,7 +118,7 @@ static void init_exponents() } straus_HiGi_cache = straus_init_cache(data, STRAUS_SIZE_LIMIT); - pippenger_HiGi_cache = pippenger_init_cache(data, PIPPENGER_SIZE_LIMIT); + pippenger_HiGi_cache = pippenger_init_cache(data, 0, PIPPENGER_SIZE_LIMIT); MINFO("Hi/Gi cache size: " << (sizeof(Hi)+sizeof(Gi))/1024 << " kB"); MINFO("Hi_p3/Gi_p3 cache size: " << (sizeof(Hi_p3)+sizeof(Gi_p3))/1024 << " kB"); @@ -142,7 +142,7 @@ static rct::key vector_exponent(const rct::keyV &a, const rct::keyV &b) multiexp_data.emplace_back(a[i], Gi_p3[i]); multiexp_data.emplace_back(b[i], Hi_p3[i]); } - return multiexp(multiexp_data, true); + return multiexp(multiexp_data, 2 * a.size()); } /* Compute a custom vector-scalar commitment */ @@ -169,7 +169,7 @@ static rct::key cross_vector_exponent8(size_t size, const std::vector &A, sc_mul(multiexp_data.back().scalar.bytes, extra_scalar->bytes, INV_EIGHT.bytes); multiexp_data.back().point = *extra_point; } - return multiexp(multiexp_data, false); + return multiexp(multiexp_data, 0); } /* Given a scalar, construct a vector of powers */ @@ -839,6 +839,7 @@ bool bulletproof_VERIFY(const std::vector &proofs) std::vector multiexp_data; multiexp_data.reserve(nV + (2 * (10/*logM*/ + logN) + 4) * proofs.size() + 2 * maxMN); + multiexp_data.resize(2 * maxMN); PERF_TIMER_START_BP(VERIFY_line_24_25_invert); const std::vector inverses = invert(to_invert); @@ -1011,10 +1012,10 @@ bool bulletproof_VERIFY(const std::vector &proofs) multiexp_data.emplace_back(tmp, rct::H); for (size_t i = 0; i < maxMN; ++i) { - multiexp_data.emplace_back(m_z4[i], Gi_p3[i]); - multiexp_data.emplace_back(m_z5[i], Hi_p3[i]); + multiexp_data[i * 2] = {m_z4[i], Gi_p3[i]}; + multiexp_data[i * 2 + 1] = {m_z5[i], Hi_p3[i]}; } - if (!(multiexp(multiexp_data, false) == rct::identity())) + if (!(multiexp(multiexp_data, 2 * maxMN) == rct::identity())) { PERF_TIMER_STOP(VERIFY_step2_check); MERROR("Verification failure"); diff --git a/src/ringct/multiexp.cc b/src/ringct/multiexp.cc index fb2f1855163..178f922676e 100644 --- a/src/ringct/multiexp.cc +++ b/src/ringct/multiexp.cc @@ -564,12 +564,13 @@ struct pippenger_cached_data ~pippenger_cached_data() { aligned_free(cached); } }; -std::shared_ptr pippenger_init_cache(const std::vector &data, size_t N) +std::shared_ptr pippenger_init_cache(const std::vector &data, size_t start_offset, size_t N) { MULTIEXP_PERF(PERF_TIMER_START_UNIT(pippenger_init_cache, 1000000)); + CHECK_AND_ASSERT_THROW_MES(start_offset <= data.size(), "Bad cache base data"); if (N == 0) - N = data.size(); - CHECK_AND_ASSERT_THROW_MES(N <= data.size(), "Bad cache base data"); + N = data.size() - start_offset; + CHECK_AND_ASSERT_THROW_MES(N <= data.size() - start_offset, "Bad cache base data"); ge_cached cached; std::shared_ptr cache(new pippenger_cached_data()); @@ -577,7 +578,7 @@ std::shared_ptr pippenger_init_cache(const std::vectorcached = (ge_cached*)aligned_realloc(cache->cached, N * sizeof(ge_cached), 4096); CHECK_AND_ASSERT_THROW_MES(cache->cached, "Out of memory"); for (size_t i = 0; i < N; ++i) - ge_p3_to_cached(&cache->cached[i], &data[i].point); + ge_p3_to_cached(&cache->cached[i], &data[i+start_offset].point); MULTIEXP_PERF(PERF_TIMER_STOP(pippenger_init_cache)); return cache; @@ -588,9 +589,11 @@ size_t pippenger_get_cache_size(const std::shared_ptr &ca return cache->size * sizeof(*cache->cached); } -rct::key pippenger(const std::vector &data, const std::shared_ptr &cache, size_t c) +rct::key pippenger(const std::vector &data, const std::shared_ptr &cache, size_t cache_size, size_t c) { - CHECK_AND_ASSERT_THROW_MES(cache == NULL || cache->size >= data.size(), "Cache is too small"); + if (cache != NULL && cache_size == 0) + cache_size = cache->size; + CHECK_AND_ASSERT_THROW_MES(cache == NULL || cache_size <= cache->size, "Cache is too small"); if (c == 0) c = get_pippenger_c(data.size()); CHECK_AND_ASSERT_THROW_MES(c <= 9, "c is too large"); @@ -598,6 +601,7 @@ rct::key pippenger(const std::vector &data, const std::shared_ptr< ge_p3 result = ge_p3_identity; std::unique_ptr buckets{new ge_p3[1< local_cache = cache == NULL ? pippenger_init_cache(data) : cache; + std::shared_ptr local_cache_2 = data.size() > cache_size ? pippenger_init_cache(data, cache_size) : NULL; rct::key maxscalar = rct::zero(); for (size_t i = 0; i < data.size(); ++i) @@ -641,7 +645,10 @@ rct::key pippenger(const std::vector &data, const std::shared_ptr< CHECK_AND_ASSERT_THROW_MES(bucket < (1u<cached[i]); + if (i < cache_size) + add(buckets[bucket], local_cache->cached[i]); + else + add(buckets[bucket], local_cache_2->cached[i - cache_size]); } else buckets[bucket] = data[i].point; diff --git a/src/ringct/multiexp.h b/src/ringct/multiexp.h index 559ab664a94..b5270793343 100644 --- a/src/ringct/multiexp.h +++ b/src/ringct/multiexp.h @@ -61,10 +61,10 @@ rct::key bos_coster_heap_conv_robust(std::vector data); std::shared_ptr straus_init_cache(const std::vector &data, size_t N =0); size_t straus_get_cache_size(const std::shared_ptr &cache); rct::key straus(const std::vector &data, const std::shared_ptr &cache = NULL, size_t STEP = 0); -std::shared_ptr pippenger_init_cache(const std::vector &data, size_t N =0); +std::shared_ptr pippenger_init_cache(const std::vector &data, size_t start_offset = 0, size_t N =0); size_t pippenger_get_cache_size(const std::shared_ptr &cache); size_t get_pippenger_c(size_t N); -rct::key pippenger(const std::vector &data, const std::shared_ptr &cache = NULL, size_t c = 0); +rct::key pippenger(const std::vector &data, const std::shared_ptr &cache = NULL, size_t cache_size = 0, size_t c = 0); } diff --git a/tests/performance_tests/multiexp.h b/tests/performance_tests/multiexp.h index b8b87b3a68c..b6e86ebd45d 100644 --- a/tests/performance_tests/multiexp.h +++ b/tests/performance_tests/multiexp.h @@ -78,9 +78,9 @@ class test_multiexp case multiexp_straus_cached: return res == straus(data, straus_cache); case multiexp_pippenger: - return res == pippenger(data, NULL, c); + return res == pippenger(data, NULL, 0, c); case multiexp_pippenger_cached: - return res == pippenger(data, pippenger_cache, c); + return res == pippenger(data, pippenger_cache, 0, c); default: return false; } From 8b4767221c9b0ff3015229b167e5be1331a16c12 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 25 Aug 2018 18:37:21 +0000 Subject: [PATCH 0644/1404] bulletproofs: speedup prover --- src/ringct/bulletproofs.cc | 54 ++++++++++++++++++++++++++------------ 1 file changed, 37 insertions(+), 17 deletions(-) diff --git a/src/ringct/bulletproofs.cc b/src/ringct/bulletproofs.cc index a3af9264e1a..f1b82197801 100644 --- a/src/ringct/bulletproofs.cc +++ b/src/ringct/bulletproofs.cc @@ -146,13 +146,14 @@ static rct::key vector_exponent(const rct::keyV &a, const rct::keyV &b) } /* Compute a custom vector-scalar commitment */ -static rct::key cross_vector_exponent8(size_t size, const std::vector &A, size_t Ao, const std::vector &B, size_t Bo, const rct::keyV &a, size_t ao, const rct::keyV &b, size_t bo, const ge_p3 *extra_point, const rct::key *extra_scalar) +static rct::key cross_vector_exponent8(size_t size, const std::vector &A, size_t Ao, const std::vector &B, size_t Bo, const rct::keyV &a, size_t ao, const rct::keyV &b, size_t bo, const rct::keyV *scale, const ge_p3 *extra_point, const rct::key *extra_scalar) { CHECK_AND_ASSERT_THROW_MES(size + Ao <= A.size(), "Incompatible size for A"); CHECK_AND_ASSERT_THROW_MES(size + Bo <= B.size(), "Incompatible size for B"); CHECK_AND_ASSERT_THROW_MES(size + ao <= a.size(), "Incompatible size for a"); CHECK_AND_ASSERT_THROW_MES(size + bo <= b.size(), "Incompatible size for b"); CHECK_AND_ASSERT_THROW_MES(size <= maxN*maxM, "size is too large"); + CHECK_AND_ASSERT_THROW_MES(!scale || size == scale->size() / 2, "Incompatible size for scale"); CHECK_AND_ASSERT_THROW_MES(!!extra_point == !!extra_scalar, "only one of extra point/scalar present"); std::vector multiexp_data; @@ -162,6 +163,8 @@ static rct::key cross_vector_exponent8(size_t size, const std::vector &A, sc_mul(multiexp_data[i*2].scalar.bytes, a[ao+i].bytes, INV_EIGHT.bytes);; multiexp_data[i*2].point = A[Ao+i]; sc_mul(multiexp_data[i*2+1].scalar.bytes, b[bo+i].bytes, INV_EIGHT.bytes); + if (scale) + sc_mul(multiexp_data[i*2+1].scalar.bytes, multiexp_data[i*2+1].scalar.bytes, (*scale)[Bo+i].bytes); multiexp_data[i*2+1].point = B[Bo+i]; } if (extra_point) @@ -232,7 +235,7 @@ static rct::keyV hadamard(const rct::keyV &a, const rct::keyV &b) } /* folds a curvepoint array using a two way scaled Hadamard product */ -static void hadamard_fold(std::vector &v, const rct::key &a, const rct::key &b) +static void hadamard_fold(std::vector &v, const rct::keyV *scale, const rct::key &a, const rct::key &b) { CHECK_AND_ASSERT_THROW_MES((v.size() & 1) == 0, "Vector size should be even"); const size_t sz = v.size() / 2; @@ -241,7 +244,10 @@ static void hadamard_fold(std::vector &v, const rct::key &a, const rct::k ge_dsmp c[2]; ge_dsm_precomp(c[0], &v[n]); ge_dsm_precomp(c[1], &v[sz + n]); - ge_double_scalarmult_precomp_vartime2_p3(&v[n], a.bytes, c[0], b.bytes, c[1]); + rct::key sa, sb; + if (scale) sc_mul(sa.bytes, a.bytes, (*scale)[n].bytes); else sa = a; + if (scale) sc_mul(sb.bytes, b.bytes, (*scale)[sz + n].bytes); else sb = b; + ge_double_scalarmult_precomp_vartime2_p3(&v[n], sa.bytes, c[0], sb.bytes, c[1]); } v.resize(sz); } @@ -258,14 +264,24 @@ static rct::keyV vector_add(const rct::keyV &a, const rct::keyV &b) return res; } -/* Subtract two vectors */ -static rct::keyV vector_subtract(const rct::keyV &a, const rct::keyV &b) +/* Add a scalar to all elements of a vector */ +static rct::keyV vector_add(const rct::keyV &a, const rct::key &b) +{ + rct::keyV res(a.size()); + for (size_t i = 0; i < a.size(); ++i) + { + sc_add(res[i].bytes, a[i].bytes, b.bytes); + } + return res; +} + +/* Subtract a scalar from all elements of a vector */ +static rct::keyV vector_subtract(const rct::keyV &a, const rct::key &b) { - CHECK_AND_ASSERT_THROW_MES(a.size() == b.size(), "Incompatible sizes of a and b"); rct::keyV res(a.size()); for (size_t i = 0; i < a.size(); ++i) { - sc_sub(res[i].bytes, a[i].bytes, b[i].bytes); + sc_sub(res[i].bytes, a[i].bytes, b.bytes); } return res; } @@ -549,8 +565,7 @@ Bulletproof bulletproof_PROVE(const rct::keyV &sv, const rct::keyV &gamma) } // Polynomial construction by coefficients - const auto zMN = vector_dup(z, MN); - rct::keyV l0 = vector_subtract(aL, zMN); + rct::keyV l0 = vector_subtract(aL, z); const rct::keyV &l1 = sL; // This computes the ugly sum/concatenation from PAPER LINE 65 @@ -570,7 +585,7 @@ Bulletproof bulletproof_PROVE(const rct::keyV &sv, const rct::keyV &gamma) } } - rct::keyV r0 = vector_add(aR, zMN); + rct::keyV r0 = vector_add(aR, z); const auto yMN = vector_powers(y, MN); r0 = hadamard(r0, yMN); r0 = vector_add(r0, zero_twos); @@ -658,12 +673,15 @@ Bulletproof bulletproof_PROVE(const rct::keyV &sv, const rct::keyV &gamma) rct::keyV aprime(MN); rct::keyV bprime(MN); const rct::key yinv = invert(y); - rct::key yinvpow = rct::identity(); + rct::keyV yinvpow(MN); + yinvpow[0] = rct::identity(); + yinvpow[1] = yinv; for (size_t i = 0; i < MN; ++i) { Gprime[i] = Gi_p3[i]; - ge_scalarmult_p3(&Hprime[i], yinvpow.bytes, &Hi_p3[i]); - sc_mul(yinvpow.bytes, yinvpow.bytes, yinv.bytes); + Hprime[i] = Hi_p3[i]; + if (i > 1) + sc_mul(yinvpow[i].bytes, yinvpow[i-1].bytes, yinv.bytes); aprime[i] = l[i]; bprime[i] = r[i]; } @@ -675,6 +693,7 @@ Bulletproof bulletproof_PROVE(const rct::keyV &sv, const rct::keyV &gamma) PERF_TIMER_START_BP(PROVE_step4); // PAPER LINE 13 + const rct::keyV *scale = &yinvpow; while (nprime > 1) { // PAPER LINE 15 @@ -689,9 +708,9 @@ Bulletproof bulletproof_PROVE(const rct::keyV &sv, const rct::keyV &gamma) // PAPER LINES 18-19 PERF_TIMER_START_BP(PROVE_LR); sc_mul(tmp.bytes, cL.bytes, x_ip.bytes); - L[round] = cross_vector_exponent8(nprime, Gprime, nprime, Hprime, 0, aprime, 0, bprime, nprime, &ge_p3_H, &tmp); + L[round] = cross_vector_exponent8(nprime, Gprime, nprime, Hprime, 0, aprime, 0, bprime, nprime, scale, &ge_p3_H, &tmp); sc_mul(tmp.bytes, cR.bytes, x_ip.bytes); - R[round] = cross_vector_exponent8(nprime, Gprime, 0, Hprime, nprime, aprime, nprime, bprime, 0, &ge_p3_H, &tmp); + R[round] = cross_vector_exponent8(nprime, Gprime, 0, Hprime, nprime, aprime, nprime, bprime, 0, scale, &ge_p3_H, &tmp); PERF_TIMER_STOP(PROVE_LR); // PAPER LINES 21-22 @@ -708,8 +727,8 @@ Bulletproof bulletproof_PROVE(const rct::keyV &sv, const rct::keyV &gamma) if (nprime > 1) { PERF_TIMER_START_BP(PROVE_hadamard2); - hadamard_fold(Gprime, winv, w[round]); - hadamard_fold(Hprime, w[round], winv); + hadamard_fold(Gprime, NULL, winv, w[round]); + hadamard_fold(Hprime, scale, w[round], winv); PERF_TIMER_STOP(PROVE_hadamard2); } @@ -719,6 +738,7 @@ Bulletproof bulletproof_PROVE(const rct::keyV &sv, const rct::keyV &gamma) bprime = vector_add(vector_scalar(slice(bprime, 0, nprime), winv), vector_scalar(slice(bprime, nprime, bprime.size()), w[round])); PERF_TIMER_STOP(PROVE_prime); + scale = NULL; ++round; } PERF_TIMER_STOP(PROVE_step4); From a110e6aa18928fb0bc13d0e39c854aea9e99e96d Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 27 Aug 2018 13:23:57 +0000 Subject: [PATCH 0645/1404] multiexp: tune which variants to use for which number of points --- src/ringct/bulletproofs.cc | 8 ++++---- src/ringct/multiexp.cc | 36 ++++++++++++++++++++++++++---------- 2 files changed, 30 insertions(+), 14 deletions(-) diff --git a/src/ringct/bulletproofs.cc b/src/ringct/bulletproofs.cc index f1b82197801..4e1c940d1b2 100644 --- a/src/ringct/bulletproofs.cc +++ b/src/ringct/bulletproofs.cc @@ -48,7 +48,7 @@ extern "C" #define PERF_TIMER_START_BP(x) PERF_TIMER_START_UNIT(x, 1000000) -#define STRAUS_SIZE_LIMIT 128 +#define STRAUS_SIZE_LIMIT 232 #define PIPPENGER_SIZE_LIMIT 0 namespace rct @@ -77,11 +77,11 @@ static inline rct::key multiexp(const std::vector &data, size_t Hi { if (HiGi_size > 0) { - static_assert(128 <= STRAUS_SIZE_LIMIT, "Straus in precalc mode can only be calculated till STRAUS_SIZE_LIMIT"); - return HiGi_size <= 128 && data.size() == HiGi_size ? straus(data, straus_HiGi_cache, 0) : pippenger(data, pippenger_HiGi_cache, HiGi_size, get_pippenger_c(data.size())); + static_assert(232 <= STRAUS_SIZE_LIMIT, "Straus in precalc mode can only be calculated till STRAUS_SIZE_LIMIT"); + return HiGi_size <= 232 && data.size() == HiGi_size ? straus(data, straus_HiGi_cache, 0) : pippenger(data, pippenger_HiGi_cache, HiGi_size, get_pippenger_c(data.size())); } else - return data.size() <= 64 ? straus(data, NULL, 0) : pippenger(data, NULL, 0, get_pippenger_c(data.size())); + return data.size() <= 95 ? straus(data, NULL, 0) : pippenger(data, NULL, 0, get_pippenger_c(data.size())); } static inline bool is_reduced(const rct::key &scalar) diff --git a/src/ringct/multiexp.cc b/src/ringct/multiexp.cc index 178f922676e..85b1dfed4e5 100644 --- a/src/ringct/multiexp.cc +++ b/src/ringct/multiexp.cc @@ -79,6 +79,25 @@ extern "C" // Best/cached Straus Straus Straus Straus Straus Straus Straus Straus Pip Pip Pip Pip // Best/uncached Straus Straus Straus Straus Straus Straus Pip Pip Pip Pip Pip Pip +// New timings: +// Pippenger: +// 2/1 always +// 3/2 at ~13 +// 4/3 at ~29 +// 5/4 at ~83 +// 6/5 < 200 +// 7/6 at ~470 +// 8/7 at ~1180 +// 9/8 at ~2290 +// Cached Pippenger: +// 6/5 < 200 +// 7/6 at 460 +// 8/7 at 1180 +// 9/8 at 2300 +// +// Cached Straus/Pippenger cross at 232 +// + namespace rct { @@ -543,16 +562,13 @@ rct::key straus(const std::vector &data, const std::shared_ptr Date: Sat, 1 Sep 2018 17:46:34 +0000 Subject: [PATCH 0646/1404] bulletproofs: only enable profiling on request --- src/ringct/bulletproofs.cc | 62 +++++++++++++++++++++----------------- 1 file changed, 34 insertions(+), 28 deletions(-) diff --git a/src/ringct/bulletproofs.cc b/src/ringct/bulletproofs.cc index 4e1c940d1b2..bed48769a37 100644 --- a/src/ringct/bulletproofs.cc +++ b/src/ringct/bulletproofs.cc @@ -46,7 +46,13 @@ extern "C" //#define DEBUG_BP +#if 1 #define PERF_TIMER_START_BP(x) PERF_TIMER_START_UNIT(x, 1000000) +#define PERF_TIMER_STOP_BP(x) PERF_TIMER_STOP(x) +#else +#define PERF_TIMER_START_BP(x) ((void*)0) +#define PERF_TIMER_STOP_BP(x) ((void*)0) +#endif #define STRAUS_SIZE_LIMIT 232 #define PIPPENGER_SIZE_LIMIT 0 @@ -486,7 +492,7 @@ Bulletproof bulletproof_PROVE(const rct::keyV &sv, const rct::keyV &gamma) sc_mul(sv8.bytes, sv[i].bytes, INV_EIGHT.bytes); rct::addKeys2(V[i], gamma8, sv8, rct::H); } - PERF_TIMER_STOP(PROVE_v); + PERF_TIMER_STOP_BP(PROVE_v); PERF_TIMER_START_BP(PROVE_aLaR); for (size_t j = 0; j < M; ++j) @@ -507,7 +513,7 @@ Bulletproof bulletproof_PROVE(const rct::keyV &sv, const rct::keyV &gamma) } } } - PERF_TIMER_STOP(PROVE_aLaR); + PERF_TIMER_STOP_BP(PROVE_aLaR); // DEBUG: Test to ensure this recovers the value #ifdef DEBUG_BP @@ -552,14 +558,14 @@ Bulletproof bulletproof_PROVE(const rct::keyV &sv, const rct::keyV &gamma) rct::key y = hash_cache_mash(hash_cache, A, S); if (y == rct::zero()) { - PERF_TIMER_STOP(PROVE_step1); + PERF_TIMER_STOP_BP(PROVE_step1); MINFO("y is 0, trying again"); goto try_again; } rct::key z = hash_cache = rct::hash_to_scalar(y); if (z == rct::zero()) { - PERF_TIMER_STOP(PROVE_step1); + PERF_TIMER_STOP_BP(PROVE_step1); MINFO("z is 0, trying again"); goto try_again; } @@ -598,7 +604,7 @@ Bulletproof bulletproof_PROVE(const rct::keyV &sv, const rct::keyV &gamma) sc_add(t1.bytes, t1_1.bytes, t1_2.bytes); rct::key t2 = inner_product(l1, r1); - PERF_TIMER_STOP(PROVE_step1); + PERF_TIMER_STOP_BP(PROVE_step1); PERF_TIMER_START_BP(PROVE_step2); // PAPER LINES 47-48 @@ -619,7 +625,7 @@ Bulletproof bulletproof_PROVE(const rct::keyV &sv, const rct::keyV &gamma) rct::key x = hash_cache_mash(hash_cache, z, T1, T2); if (x == rct::zero()) { - PERF_TIMER_STOP(PROVE_step2); + PERF_TIMER_STOP_BP(PROVE_step2); MINFO("x is 0, trying again"); goto try_again; } @@ -643,7 +649,7 @@ Bulletproof bulletproof_PROVE(const rct::keyV &sv, const rct::keyV &gamma) l = vector_add(l, vector_scalar(l1, x)); rct::keyV r = r0; r = vector_add(r, vector_scalar(r1, x)); - PERF_TIMER_STOP(PROVE_step2); + PERF_TIMER_STOP_BP(PROVE_step2); PERF_TIMER_START_BP(PROVE_step3); rct::key t = inner_product(l, r); @@ -661,7 +667,7 @@ Bulletproof bulletproof_PROVE(const rct::keyV &sv, const rct::keyV &gamma) rct::key x_ip = hash_cache_mash(hash_cache, x, taux, mu, t); if (x_ip == rct::zero()) { - PERF_TIMER_STOP(PROVE_step3); + PERF_TIMER_STOP_BP(PROVE_step3); MINFO("x_ip is 0, trying again"); goto try_again; } @@ -689,7 +695,7 @@ Bulletproof bulletproof_PROVE(const rct::keyV &sv, const rct::keyV &gamma) rct::keyV R(logMN); int round = 0; rct::keyV w(logMN); // this is the challenge x in the inner product protocol - PERF_TIMER_STOP(PROVE_step3); + PERF_TIMER_STOP_BP(PROVE_step3); PERF_TIMER_START_BP(PROVE_step4); // PAPER LINE 13 @@ -703,7 +709,7 @@ Bulletproof bulletproof_PROVE(const rct::keyV &sv, const rct::keyV &gamma) PERF_TIMER_START_BP(PROVE_inner_product); rct::key cL = inner_product(slice(aprime, 0, nprime), slice(bprime, nprime, bprime.size())); rct::key cR = inner_product(slice(aprime, nprime, aprime.size()), slice(bprime, 0, nprime)); - PERF_TIMER_STOP(PROVE_inner_product); + PERF_TIMER_STOP_BP(PROVE_inner_product); // PAPER LINES 18-19 PERF_TIMER_START_BP(PROVE_LR); @@ -711,13 +717,13 @@ Bulletproof bulletproof_PROVE(const rct::keyV &sv, const rct::keyV &gamma) L[round] = cross_vector_exponent8(nprime, Gprime, nprime, Hprime, 0, aprime, 0, bprime, nprime, scale, &ge_p3_H, &tmp); sc_mul(tmp.bytes, cR.bytes, x_ip.bytes); R[round] = cross_vector_exponent8(nprime, Gprime, 0, Hprime, nprime, aprime, nprime, bprime, 0, scale, &ge_p3_H, &tmp); - PERF_TIMER_STOP(PROVE_LR); + PERF_TIMER_STOP_BP(PROVE_LR); // PAPER LINES 21-22 w[round] = hash_cache_mash(hash_cache, L[round], R[round]); if (w[round] == rct::zero()) { - PERF_TIMER_STOP(PROVE_step4); + PERF_TIMER_STOP_BP(PROVE_step4); MINFO("w[round] is 0, trying again"); goto try_again; } @@ -729,19 +735,19 @@ Bulletproof bulletproof_PROVE(const rct::keyV &sv, const rct::keyV &gamma) PERF_TIMER_START_BP(PROVE_hadamard2); hadamard_fold(Gprime, NULL, winv, w[round]); hadamard_fold(Hprime, scale, w[round], winv); - PERF_TIMER_STOP(PROVE_hadamard2); + PERF_TIMER_STOP_BP(PROVE_hadamard2); } // PAPER LINES 28-29 PERF_TIMER_START_BP(PROVE_prime); aprime = vector_add(vector_scalar(slice(aprime, 0, nprime), w[round]), vector_scalar(slice(aprime, nprime, aprime.size()), winv)); bprime = vector_add(vector_scalar(slice(bprime, 0, nprime), winv), vector_scalar(slice(bprime, nprime, bprime.size()), w[round])); - PERF_TIMER_STOP(PROVE_prime); + PERF_TIMER_STOP_BP(PROVE_prime); scale = NULL; ++round; } - PERF_TIMER_STOP(PROVE_step4); + PERF_TIMER_STOP_BP(PROVE_step4); // PAPER LINE 58 (with inclusions from PAPER LINE 8 and PAPER LINE 20) return Bulletproof(std::move(V), A, S, T1, T2, taux, mu, std::move(L), std::move(R), aprime[0], bprime[0], t); @@ -766,7 +772,7 @@ Bulletproof bulletproof_PROVE(const std::vector &v, const rct::keyV &g sv[i].bytes[6] = (v[i] >> 48) & 255; sv[i].bytes[7] = (v[i] >> 56) & 255; } - PERF_TIMER_STOP(PROVE_v); + PERF_TIMER_STOP_BP(PROVE_v); return bulletproof_PROVE(sv, gamma); } @@ -826,7 +832,7 @@ bool bulletproof_VERIFY(const std::vector &proofs) CHECK_AND_ASSERT_MES(!(pd.x == rct::zero()), false, "x == 0"); pd.x_ip = hash_cache_mash(hash_cache, pd.x, proof.taux, proof.mu, proof.t); CHECK_AND_ASSERT_MES(!(pd.x_ip == rct::zero()), false, "x_ip == 0"); - PERF_TIMER_STOP(VERIFY_start); + PERF_TIMER_STOP_BP(VERIFY_start); size_t M; for (pd.logM = 0; (M = 1< &proofs) pd.w[i] = hash_cache_mash(hash_cache, proof.L[i], proof.R[i]); CHECK_AND_ASSERT_MES(!(pd.w[i] == rct::zero()), false, "w[i] == 0"); } - PERF_TIMER_STOP(VERIFY_line_21_22); + PERF_TIMER_STOP_BP(VERIFY_line_21_22); pd.inv_offset = inv_offset; for (size_t i = 0; i < rounds; ++i) @@ -863,7 +869,7 @@ bool bulletproof_VERIFY(const std::vector &proofs) PERF_TIMER_START_BP(VERIFY_line_24_25_invert); const std::vector inverses = invert(to_invert); - PERF_TIMER_STOP(VERIFY_line_24_25_invert); + PERF_TIMER_STOP_BP(VERIFY_line_24_25_invert); // setup weighted aggregates rct::key z1 = rct::zero(); @@ -905,7 +911,7 @@ bool bulletproof_VERIFY(const std::vector &proofs) CHECK_AND_ASSERT_MES(j+2 < zpow.size(), false, "invalid zpow index"); sc_mulsub(k.bytes, zpow[j+2].bytes, ip12.bytes, k.bytes); } - PERF_TIMER_STOP(VERIFY_line_61); + PERF_TIMER_STOP_BP(VERIFY_line_61); PERF_TIMER_START_BP(VERIFY_line_61rl_new); sc_muladd(tmp.bytes, pd.z.bytes, ip1y.bytes, k.bytes); @@ -922,14 +928,14 @@ bool bulletproof_VERIFY(const std::vector &proofs) sc_mul(xsq.bytes, pd.x.bytes, pd.x.bytes); sc_mul(tmp.bytes, xsq.bytes, weight_y.bytes); multiexp_data.emplace_back(tmp, proof8_T2); - PERF_TIMER_STOP(VERIFY_line_61rl_new); + PERF_TIMER_STOP_BP(VERIFY_line_61rl_new); PERF_TIMER_START_BP(VERIFY_line_62); // PAPER LINE 62 multiexp_data.emplace_back(weight_z, proof8_A); sc_mul(tmp.bytes, pd.x.bytes, weight_z.bytes); multiexp_data.emplace_back(tmp, proof8_S); - PERF_TIMER_STOP(VERIFY_line_62); + PERF_TIMER_STOP_BP(VERIFY_line_62); // Compute the number of rounds for the inner product const size_t rounds = pd.logM+logN; @@ -958,7 +964,7 @@ bool bulletproof_VERIFY(const std::vector &proofs) sc_mul(w_cache[s-1].bytes, w_cache[s/2].bytes, winv[j].bytes); } } - PERF_TIMER_STOP(VERIFY_line_24_25_precalc); + PERF_TIMER_STOP_BP(VERIFY_line_24_25_precalc); for (size_t i = 0; i < MN; ++i) { @@ -1004,7 +1010,7 @@ bool bulletproof_VERIFY(const std::vector &proofs) } } - PERF_TIMER_STOP(VERIFY_line_24_25); + PERF_TIMER_STOP_BP(VERIFY_line_24_25); // PAPER LINE 26 PERF_TIMER_START_BP(VERIFY_line_26_new); @@ -1021,7 +1027,7 @@ bool bulletproof_VERIFY(const std::vector &proofs) sc_mulsub(tmp.bytes, proof.a.bytes, proof.b.bytes, proof.t.bytes); sc_mul(tmp.bytes, tmp.bytes, pd.x_ip.bytes); sc_muladd(z3.bytes, tmp.bytes, weight_z.bytes, z3.bytes); - PERF_TIMER_STOP(VERIFY_line_26_new); + PERF_TIMER_STOP_BP(VERIFY_line_26_new); } // now check all proofs at once @@ -1037,13 +1043,13 @@ bool bulletproof_VERIFY(const std::vector &proofs) } if (!(multiexp(multiexp_data, 2 * maxMN) == rct::identity())) { - PERF_TIMER_STOP(VERIFY_step2_check); + PERF_TIMER_STOP_BP(VERIFY_step2_check); MERROR("Verification failure"); return false; } - PERF_TIMER_STOP(VERIFY_step2_check); + PERF_TIMER_STOP_BP(VERIFY_step2_check); - PERF_TIMER_STOP(VERIFY); + PERF_TIMER_STOP_BP(VERIFY); return true; } From 74fb3d882c9a50798795367ca8cd5dac85138c78 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 22 Oct 2018 11:56:45 +0000 Subject: [PATCH 0647/1404] multiexp: some minor speedups --- src/ringct/multiexp.cc | 48 ++++++++++++++++++++++++++++++------------ 1 file changed, 35 insertions(+), 13 deletions(-) diff --git a/src/ringct/multiexp.cc b/src/ringct/multiexp.cc index 85b1dfed4e5..6f77fed3450 100644 --- a/src/ringct/multiexp.cc +++ b/src/ringct/multiexp.cc @@ -473,10 +473,7 @@ rct::key straus(const std::vector &data, const std::shared_ptr &data, const std::shared_ptr> 4; } #elif 1 + unsigned char bytes33[33]; + memcpy(bytes33, data[j].scalar.bytes, 32); + bytes33[32] = 0; + bytes = bytes33; for (size_t i = 0; i < 256; ++i) digits[j*256+i] = ((bytes[i>>3] | (bytes[(i>>3)+1]<<8)) >> (i&7)) & mask; #else @@ -615,7 +616,9 @@ rct::key pippenger(const std::vector &data, const std::shared_ptr< CHECK_AND_ASSERT_THROW_MES(c <= 9, "c is too large"); ge_p3 result = ge_p3_identity; + bool result_init = false; std::unique_ptr buckets{new ge_p3[1< local_cache = cache == NULL ? pippenger_init_cache(data) : cache; std::shared_ptr local_cache_2 = data.size() > cache_size ? pippenger_init_cache(data, cache_size) : NULL; @@ -632,7 +635,7 @@ rct::key pippenger(const std::vector &data, const std::shared_ptr< for (size_t k = groups; k-- > 0; ) { - if (!ge_p3_is_point_at_infinity(&result)) + if (result_init) { ge_p2 p2; ge_p3_to_p2(&p2, &result); @@ -646,8 +649,7 @@ rct::key pippenger(const std::vector &data, const std::shared_ptr< ge_p1p1_to_p2(&p2, &p1); } } - for (size_t i = 0; i < (1u< &data, const std::shared_ptr< if (bucket == 0) continue; CHECK_AND_ASSERT_THROW_MES(bucket < (1u<cached[i]); @@ -667,17 +669,37 @@ rct::key pippenger(const std::vector &data, const std::shared_ptr< add(buckets[bucket], local_cache_2->cached[i - cache_size]); } else + { buckets[bucket] = data[i].point; + buckets_init[bucket] = true; + } } // sum the buckets - ge_p3 pail = ge_p3_identity; + ge_p3 pail; + bool pail_init = false; for (size_t i = (1< 0; --i) { - if (!ge_p3_is_point_at_infinity(&buckets[i])) - add(pail, buckets[i]); - if (!ge_p3_is_point_at_infinity(&pail)) - add(result, pail); + if (buckets_init[i]) + { + if (pail_init) + add(pail, buckets[i]); + else + { + pail = buckets[i]; + pail_init = true; + } + } + if (pail_init) + { + if (result_init) + add(result, pail); + else + { + result = pail; + result_init = true; + } + } } } From 9da67502d54ce9568d6d4878eaec98a7447b8a17 Mon Sep 17 00:00:00 2001 From: Riccardo Spagni Date: Mon, 22 Oct 2018 23:12:23 +0200 Subject: [PATCH 0648/1404] update readme for 0.13.0.4 --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 5da96371a2c..601cce54409 100644 --- a/README.md +++ b/README.md @@ -179,7 +179,7 @@ invokes cmake commands as needed. * Change to the root of the source code directory, change to the most recent release branch, and build: cd monero - git checkout v0.13.0.0 + git checkout v0.13.0.4 make *Optional*: If your machine has several cores and enough memory, enable @@ -241,7 +241,7 @@ Tested on a Raspberry Pi Zero with a clean install of minimal Raspbian Stretch ( ``` git clone https://github.com/monero-project/monero.git cd monero - git checkout tags/v0.13.0.0 + git checkout tags/v0.13.0.4 ``` * Build: ``` @@ -340,7 +340,7 @@ application. * If you would like a specific [version/tag](https://github.com/monero-project/monero/tags), do a git checkout for that version. eg. 'v0.13.0.0'. If you dont care about the version and just want binaries from master, skip this step: - git checkout v0.13.0.0 + git checkout v0.13.0.4 * If you are on a 64-bit system, run: From 3bb4b0d41f76483c4ae4d8d62faa4531049badeb Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 22 Oct 2018 22:17:15 +0000 Subject: [PATCH 0649/1404] miner: fix build with boost 1.69 --- src/cryptonote_basic/miner.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cryptonote_basic/miner.cpp b/src/cryptonote_basic/miner.cpp index d0b03593ed6..d8ca2dd3576 100644 --- a/src/cryptonote_basic/miner.cpp +++ b/src/cryptonote_basic/miner.cpp @@ -637,7 +637,7 @@ namespace cryptonote boost::tribool battery_powered(on_battery_power()); if(!indeterminate( battery_powered )) { - on_ac_power = !battery_powered; + on_ac_power = !(bool)battery_powered; } } From 5a2e54a1cb184b4aeca68ce275f169a1f17e8614 Mon Sep 17 00:00:00 2001 From: doy-lee Date: Mon, 22 Oct 2018 14:02:44 +1100 Subject: [PATCH 0650/1404] Fix prune using receive time as tx_weight --- src/cryptonote_core/tx_pool.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index 5a9fcf67e65..df41b13e265 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -379,11 +379,11 @@ namespace cryptonote return; } // remove first, in case this throws, so key images aren't removed - MINFO("Pruning tx " << txid << " from txpool: weight: " << it->first.second << ", fee/byte: " << it->first.first); + MINFO("Pruning tx " << txid << " from txpool: weight: " << meta.weight << ", fee/byte: " << it->first.first); m_blockchain.remove_txpool_tx(txid); - m_txpool_weight -= it->first.second; + m_txpool_weight -= meta.weight; remove_transaction_keyimages(tx, txid); - MINFO("Pruned tx " << txid << " from txpool: weight: " << it->first.second << ", fee/byte: " << it->first.first); + MINFO("Pruned tx " << txid << " from txpool: weight: " << meta.weight << ", fee/byte: " << it->first.first); m_txs_by_fee_and_receive_time.erase(it--); changed = true; } From 70877b1d911eee5e379113931ba87993e5d06880 Mon Sep 17 00:00:00 2001 From: Paul Shapiro Date: Mon, 22 Oct 2018 18:51:45 -0500 Subject: [PATCH 0651/1404] wallet2/create_transactions_2: removed obsolete '/ 1024' on min_fee calc --- src/wallet/wallet2.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 6b3a8533e3e..a85a356f5d6 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -8336,7 +8336,7 @@ std::vector wallet2::create_transactions_2(std::vector Date: Mon, 22 Oct 2018 20:57:58 +0000 Subject: [PATCH 0652/1404] wallet2: extend fake out picks away from the gamma pick in order to unbias selection from blocks with few txes --- src/wallet/wallet2.cpp | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 6b3a8533e3e..c5618375eb6 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -123,6 +123,8 @@ using namespace cryptonote; #define FIRST_REFRESH_GRANULARITY 1024 +#define GAMMA_PICK_HALF_WINDOW 5 + static const std::string MULTISIG_SIGNATURE_MAGIC = "SigMultisigPkV1"; static const std::string MULTISIG_EXTRA_INFO_MAGIC = "MultisigxV1"; @@ -6795,10 +6797,29 @@ void wallet2::get_outs(std::vector> error::get_output_distribution, "Decreasing offsets in rct distribution: " + std::to_string(block_offset) + ": " + std::to_string(rct_offsets[block_offset]) + ", " + std::to_string(block_offset + 1) + ": " + std::to_string(rct_offsets[block_offset + 1])); - uint64_t n_rct = rct_offsets[block_offset + 1] - rct_offsets[block_offset]; + uint64_t first_block_offset = block_offset, last_block_offset = block_offset; + for (size_t half_window = 0; half_window < GAMMA_PICK_HALF_WINDOW; ++half_window) + { + // end when we have a non empty block + uint64_t cum0 = first_block_offset > 0 ? rct_offsets[first_block_offset] - rct_offsets[first_block_offset - 1] : rct_offsets[0]; + if (cum0 > 1) + break; + uint64_t cum1 = last_block_offset > 0 ? rct_offsets[last_block_offset] - rct_offsets[last_block_offset - 1] : rct_offsets[0]; + if (cum1 > 1) + break; + if (first_block_offset == 0 && last_block_offset >= rct_offsets.size() - 2) + break; + // expand up to bounds + if (first_block_offset > 0) + --first_block_offset; + if (last_block_offset < rct_offsets.size() - 1) + ++last_block_offset; + } + const uint64_t n_rct = rct_offsets[last_block_offset] - (first_block_offset == 0 ? 0 : rct_offsets[first_block_offset - 1]); if (n_rct == 0) return rct_offsets[block_offset] ? rct_offsets[block_offset] - 1 : 0; - return rct_offsets[block_offset] + crypto::rand() % n_rct; + MDEBUG("Picking 1/" << n_rct << " in " << (last_block_offset - first_block_offset + 1) << " blocks centered around " << block_offset); + return rct_offsets[first_block_offset] + crypto::rand() % n_rct; }; size_t num_selected_transfers = 0; From a43daebf57560aef4e83b3cce7e92bab3f2070dc Mon Sep 17 00:00:00 2001 From: Howard Chu Date: Mon, 22 Oct 2018 17:31:48 +0100 Subject: [PATCH 0653/1404] Add stats utility Report statistics from a blockchain DB --- src/blockchain_utilities/CMakeLists.txt | 28 ++ src/blockchain_utilities/blockchain_stats.cpp | 337 ++++++++++++++++++ 2 files changed, 365 insertions(+) create mode 100644 src/blockchain_utilities/blockchain_stats.cpp diff --git a/src/blockchain_utilities/CMakeLists.txt b/src/blockchain_utilities/CMakeLists.txt index 24a750eb03d..c6a1d43a9f2 100644 --- a/src/blockchain_utilities/CMakeLists.txt +++ b/src/blockchain_utilities/CMakeLists.txt @@ -115,6 +115,14 @@ set(blockchain_depth_private_headers) monero_private_headers(blockchain_depth ${blockchain_depth_private_headers}) +set(blockchain_stats_sources + blockchain_stats.cpp + ) + +set(blockchain_stats_private_headers) + +monero_private_headers(blockchain_stats + ${blockchain_stats_private_headers}) monero_add_executable(blockchain_import @@ -251,3 +259,23 @@ set_property(TARGET blockchain_depth OUTPUT_NAME "monero-blockchain-depth") install(TARGETS blockchain_depth DESTINATION bin) +monero_add_executable(blockchain_stats + ${blockchain_stats_sources} + ${blockchain_stats_private_headers}) + +target_link_libraries(blockchain_stats + PRIVATE + cryptonote_core + blockchain_db + version + epee + ${Boost_FILESYSTEM_LIBRARY} + ${Boost_SYSTEM_LIBRARY} + ${Boost_THREAD_LIBRARY} + ${CMAKE_THREAD_LIBS_INIT} + ${EXTRA_LIBRARIES}) + +set_property(TARGET blockchain_stats + PROPERTY + OUTPUT_NAME "monero-blockchain-stats") +install(TARGETS blockchain_stats DESTINATION bin) diff --git a/src/blockchain_utilities/blockchain_stats.cpp b/src/blockchain_utilities/blockchain_stats.cpp new file mode 100644 index 00000000000..716b33cae08 --- /dev/null +++ b/src/blockchain_utilities/blockchain_stats.cpp @@ -0,0 +1,337 @@ +// Copyright (c) 2014-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include +#include "common/command_line.h" +#include "common/varint.h" +#include "cryptonote_basic/cryptonote_boost_serialization.h" +#include "cryptonote_core/tx_pool.h" +#include "cryptonote_core/cryptonote_core.h" +#include "cryptonote_core/blockchain.h" +#include "blockchain_db/blockchain_db.h" +#include "blockchain_db/db_types.h" +#include "version.h" + +#undef MONERO_DEFAULT_LOG_CATEGORY +#define MONERO_DEFAULT_LOG_CATEGORY "bcutil" + +namespace po = boost::program_options; +using namespace epee; +using namespace cryptonote; + +static bool stop_requested = false; + +int main(int argc, char* argv[]) +{ + TRY_ENTRY(); + + epee::string_tools::set_module_name_and_folder(argv[0]); + + std::string default_db_type = "lmdb"; + + std::string available_dbs = cryptonote::blockchain_db_types(", "); + available_dbs = "available: " + available_dbs; + + uint32_t log_level = 0; + uint64_t block_start = 0; + uint64_t block_stop = 0; + + tools::on_startup(); + + boost::filesystem::path output_file_path; + + po::options_description desc_cmd_only("Command line options"); + po::options_description desc_cmd_sett("Command line options and settings options"); + const command_line::arg_descriptor arg_log_level = {"log-level", "0-4 or categories", ""}; + const command_line::arg_descriptor arg_database = { + "database", available_dbs.c_str(), default_db_type + }; + const command_line::arg_descriptor arg_block_start = {"block-start", "start at block number", block_start}; + const command_line::arg_descriptor arg_block_stop = {"block-stop", "Stop at block number", block_stop}; + const command_line::arg_descriptor arg_inputs = {"with-inputs", "with input stats", false}; + const command_line::arg_descriptor arg_outputs = {"with-outputs", "with output stats", false}; + const command_line::arg_descriptor arg_ringsize = {"with-ringsize", "with ringsize stats", false}; + const command_line::arg_descriptor arg_hours = {"with-hours", "with txns per hour", false}; + + command_line::add_arg(desc_cmd_sett, cryptonote::arg_data_dir); + command_line::add_arg(desc_cmd_sett, cryptonote::arg_testnet_on); + command_line::add_arg(desc_cmd_sett, cryptonote::arg_stagenet_on); + command_line::add_arg(desc_cmd_sett, arg_log_level); + command_line::add_arg(desc_cmd_sett, arg_database); + command_line::add_arg(desc_cmd_sett, arg_block_start); + command_line::add_arg(desc_cmd_sett, arg_block_stop); + command_line::add_arg(desc_cmd_sett, arg_inputs); + command_line::add_arg(desc_cmd_sett, arg_outputs); + command_line::add_arg(desc_cmd_sett, arg_ringsize); + command_line::add_arg(desc_cmd_sett, arg_hours); + command_line::add_arg(desc_cmd_only, command_line::arg_help); + + po::options_description desc_options("Allowed options"); + desc_options.add(desc_cmd_only).add(desc_cmd_sett); + + po::variables_map vm; + bool r = command_line::handle_error_helper(desc_options, [&]() + { + auto parser = po::command_line_parser(argc, argv).options(desc_options); + po::store(parser.run(), vm); + po::notify(vm); + return true; + }); + if (! r) + return 1; + + if (command_line::get_arg(vm, command_line::arg_help)) + { + std::cout << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")" << ENDL << ENDL; + std::cout << desc_options << std::endl; + return 1; + } + + mlog_configure(mlog_get_default_log_path("monero-blockchain-stats.log"), true); + if (!command_line::is_arg_defaulted(vm, arg_log_level)) + mlog_set_log(command_line::get_arg(vm, arg_log_level).c_str()); + else + mlog_set_log(std::string(std::to_string(log_level) + ",bcutil:INFO").c_str()); + + LOG_PRINT_L0("Starting..."); + + std::string opt_data_dir = command_line::get_arg(vm, cryptonote::arg_data_dir); + bool opt_testnet = command_line::get_arg(vm, cryptonote::arg_testnet_on); + bool opt_stagenet = command_line::get_arg(vm, cryptonote::arg_stagenet_on); + network_type net_type = opt_testnet ? TESTNET : opt_stagenet ? STAGENET : MAINNET; + block_start = command_line::get_arg(vm, arg_block_start); + block_stop = command_line::get_arg(vm, arg_block_stop); + bool do_inputs = command_line::get_arg(vm, arg_inputs); + bool do_outputs = command_line::get_arg(vm, arg_outputs); + bool do_ringsize = command_line::get_arg(vm, arg_ringsize); + bool do_hours = command_line::get_arg(vm, arg_hours); + + std::string db_type = command_line::get_arg(vm, arg_database); + if (!cryptonote::blockchain_valid_db_type(db_type)) + { + std::cerr << "Invalid database type: " << db_type << std::endl; + return 1; + } + + LOG_PRINT_L0("Initializing source blockchain (BlockchainDB)"); + std::unique_ptr core_storage; + tx_memory_pool m_mempool(*core_storage); + core_storage.reset(new Blockchain(m_mempool)); + BlockchainDB *db = new_db(db_type); + if (db == NULL) + { + LOG_ERROR("Attempted to use non-existent database type: " << db_type); + throw std::runtime_error("Attempting to use non-existent database type"); + } + LOG_PRINT_L0("database: " << db_type); + + const std::string filename = (boost::filesystem::path(opt_data_dir) / db->get_db_name()).string(); + LOG_PRINT_L0("Loading blockchain from folder " << filename << " ..."); + + try + { + db->open(filename, DBF_RDONLY); + } + catch (const std::exception& e) + { + LOG_PRINT_L0("Error opening database: " << e.what()); + return 1; + } + r = core_storage->init(db, net_type); + + CHECK_AND_ASSERT_MES(r, 1, "Failed to initialize source blockchain storage"); + LOG_PRINT_L0("Source blockchain storage initialized OK"); + + tools::signal_handler::install([](int type) { + stop_requested = true; + }); + + const uint64_t db_height = db->height(); + if (!block_stop) + block_stop = db_height; + MINFO("Starting from height " << block_start << ", stopping at height " << block_stop); + +/* + * The default output can be plotted with GnuPlot using these commands: +set key autotitle columnhead +set title "Monero Blockchain Growth" +set timefmt "%Y-%m-%d" +set xdata time +set xrange ["2014-04-17":*] +set format x "%Y-%m-%d" +set yrange [0:*] +set y2range [0:*] +set ylabel "Txs/Day" +set y2label "Bytes" +set y2tics nomirror +plot 'stats.csv' index "DATA" using (timecolumn(1,"%Y-%m-%d")):4 with lines, '' using (timecolumn(1,"%Y-%m-%d")):7 axes x1y2 with lines + */ + + // spit out a comment that GnuPlot can use as an index + std::cout << ENDL << "# DATA" << ENDL; + std::cout << "Date\tBlocks/day\tBlocks\tTxs/Day\tTxs\tBytes/Day\tBytes"; + if (do_inputs) + std::cout << "\tInMin\tInMax\tInAvg"; + if (do_outputs) + std::cout << "\tOutMin\tOutMax\tOutAvg"; + if (do_ringsize) + std::cout << "\tRingMin\tRingMax\tRingAvg"; + if (do_hours) { + char buf[8]; + unsigned int i; + for (i=0; i<24; i++) { + sprintf(buf, "\t%02d:00", i); + std::cout << buf; + } + } + std::cout << ENDL; + + struct tm prevtm = {0}, currtm; + uint64_t prevsz = 0, currsz = 0; + uint64_t prevtxs = 0, currtxs = 0; + uint64_t currblks = 0; + uint64_t totins = 0, totouts = 0, totrings = 0; + uint32_t minins = 10, maxins = 0; + uint32_t minouts = 10, maxouts = 0; + uint32_t minrings = 50, maxrings = 0; + uint32_t io, tottxs = 0; + uint32_t txhr[24] = {0}; + unsigned int i; + + for (uint64_t h = block_start; h < block_stop; ++h) + { + cryptonote::blobdata bd = db->get_block_blob_from_height(h); + cryptonote::block blk; + if (!cryptonote::parse_and_validate_block_from_blob(bd, blk)) + { + LOG_PRINT_L0("Bad block from db"); + return 1; + } + time_t tt = blk.timestamp; + char timebuf[64]; + gmtime_r(&tt, &currtm); + if (!prevtm.tm_year) + prevtm = currtm; + // catch change of day + if (currtm.tm_mday > prevtm.tm_mday || (currtm.tm_mday == 1 && prevtm.tm_mday > 27)) + { + // check for timestamp fudging around month ends + if (prevtm.tm_mday == 1 && currtm.tm_mday > 27) + goto skip; + strftime(timebuf, sizeof(timebuf), "%Y-%m-%d", &prevtm); + prevtm = currtm; + std::cout << timebuf << "\t" << currblks << "\t" << h << "\t" << currtxs << "\t" << prevtxs + currtxs << "\t" << currsz << "\t" << prevsz + currsz; + prevsz += currsz; + currsz = 0; + currblks = 0; + prevtxs += currtxs; + currtxs = 0; + if (!tottxs) + tottxs = 1; + if (do_inputs) { + std::cout << "\t" << (maxins ? minins : 0) << "\t" << maxins << "\t" << totins / tottxs; + minins = 10; maxins = 0; totins = 0; + } + if (do_outputs) { + std::cout << "\t" << (maxouts ? minouts : 0) << "\t" << maxouts << "\t" << totouts / tottxs; + minouts = 10; maxouts = 0; totouts = 0; + } + if (do_ringsize) { + std::cout << "\t" << (maxrings ? minrings : 0) << "\t" << maxrings << "\t" << totrings / tottxs; + minrings = 50; maxrings = 0; totrings = 0; + } + tottxs = 0; + if (do_hours) { + for (i=0; i<24; i++) { + std::cout << "\t" << txhr[i]; + txhr[i] = 0; + } + } + std::cout << ENDL; + } +skip: + currsz += bd.size(); + for (const auto& tx_id : blk.tx_hashes) + { + if (tx_id == crypto::null_hash) + { + throw std::runtime_error("Aborting: tx == null_hash"); + } + if (!db->get_tx_blob(tx_id, bd)) + { + throw std::runtime_error("Aborting: tx not found"); + } + transaction tx; + if (!parse_and_validate_tx_from_blob(bd, tx)) + { + LOG_PRINT_L0("Bad txn from db"); + return 1; + } + currsz += bd.size(); + currtxs++; + if (do_hours) + txhr[currtm.tm_hour]++; + if (do_inputs) { + io = tx.vin.size(); + if (io < minins) + minins = io; + else if (io > maxins) + maxins = io; + totins += io; + } + if (do_ringsize) { + const cryptonote::txin_to_key& tx_in_to_key + = boost::get(tx.vin[0]); + io = tx_in_to_key.key_offsets.size(); + if (io < minrings) + minrings = io; + else if (io > maxrings) + maxrings = io; + totrings += io; + } + if (do_outputs) { + io = tx.vout.size(); + if (io < minouts) + minouts = io; + else if (io > maxouts) + maxouts = io; + totouts += io; + } + tottxs++; + } + currblks++; + + if (stop_requested) + break; + } + + core_storage->deinit(); + return 0; + + CATCH_ENTRY("Stats reporting error", 1); +} From 6cd929eaa3fd2df4eaa3c9e1be747e400426e38a Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 31 May 2018 20:22:37 +0100 Subject: [PATCH 0654/1404] easylogging++: remove std::deque usage It's not actually needed for this use, and saves a STL header --- external/easylogging++/easylogging++.cc | 8 +++++++- external/easylogging++/easylogging++.h | 3 +-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/external/easylogging++/easylogging++.cc b/external/easylogging++/easylogging++.cc index a4bdad4cf73..1139008e237 100644 --- a/external/easylogging++/easylogging++.cc +++ b/external/easylogging++/easylogging++.cc @@ -1981,6 +1981,12 @@ void VRegistry::setCategories(const char* categories, bool clear) { m_categoriesString += ","; m_categoriesString += categories; + size_t n_fields = m_categories.size() + 1; + for (const char *ptr = categories; *ptr; ++ptr) + if (*ptr == ',') + ++n_fields; + m_categories.reserve(n_fields); + bool isCat = true; bool isLevel = false; std::stringstream ss; @@ -2042,7 +2048,7 @@ bool VRegistry::allowed(Level level, const char* category) { if (m_categories.empty() || category == nullptr) { return false; } else { - std::deque>::const_reverse_iterator it = m_categories.rbegin(); + std::vector>::const_reverse_iterator it = m_categories.rbegin(); for (; it != m_categories.rend(); ++it) { if (base::utils::Str::wildCardMatch(category, it->first.c_str())) { const int p = priority(it->second); diff --git a/external/easylogging++/easylogging++.h b/external/easylogging++/easylogging++.h index 8e8b7094b19..8bf94c546b2 100644 --- a/external/easylogging++/easylogging++.h +++ b/external/easylogging++/easylogging++.h @@ -386,7 +386,6 @@ ELPP_INTERNAL_DEBUGGING_OUT_INFO << ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStre #include #include #include -#include #include #include #include @@ -2531,7 +2530,7 @@ class VRegistry : base::NoCopy, public base::threading::ThreadSafe { base::type::VerboseLevel m_level; base::type::EnumType* m_pFlags; std::map m_modules; - std::deque> m_categories; + std::vector> m_categories; std::map m_cached_allowed_categories; std::string m_categoriesString; std::string m_filenameCommonPrefix; From d6937e373b32628fff414c7d8a07e4323593c6a0 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 19 Oct 2018 11:15:31 +0000 Subject: [PATCH 0655/1404] ringct: use dummy bulletproofs when in fake mode, for speed --- src/device/device.hpp | 8 ++++-- src/device/device_default.cpp | 2 +- src/device/device_ledger.cpp | 2 +- src/ringct/rctSigs.cpp | 49 +++++++++++++++++++++++++++++------ 4 files changed, 49 insertions(+), 12 deletions(-) diff --git a/src/device/device.hpp b/src/device/device.hpp index cb91176503a..815a0ab9363 100644 --- a/src/device/device.hpp +++ b/src/device/device.hpp @@ -85,7 +85,7 @@ namespace hw { public: - device() {} + device(): mode(NONE) {} device(const device &hwdev) {} virtual ~device() {} @@ -115,7 +115,8 @@ namespace hw { virtual bool connect(void) = 0; virtual bool disconnect(void) = 0; - virtual bool set_mode(device_mode mode) = 0; + virtual bool set_mode(device_mode mode) { this->mode = mode; return true; } + virtual device_mode get_mode() const { return mode; } virtual device_type get_type() const = 0; @@ -202,6 +203,9 @@ namespace hw { virtual bool mlsag_sign(const rct::key &c, const rct::keyV &xx, const rct::keyV &alpha, const size_t rows, const size_t dsRows, rct::keyV &ss) = 0; virtual bool close_tx(void) = 0; + + protected: + device_mode mode; } ; struct reset_mode { diff --git a/src/device/device_default.cpp b/src/device/device_default.cpp index a4f40e04137..68f40d91e1d 100644 --- a/src/device/device_default.cpp +++ b/src/device/device_default.cpp @@ -83,7 +83,7 @@ namespace hw { } bool device_default::set_mode(device_mode mode) { - return true; + return device::set_mode(mode); } /* ======================================================================= */ diff --git a/src/device/device_ledger.cpp b/src/device/device_ledger.cpp index d879ee95a0d..0a86e6987ca 100644 --- a/src/device/device_ledger.cpp +++ b/src/device/device_ledger.cpp @@ -396,7 +396,7 @@ namespace hw { CHECK_AND_ASSERT_THROW_MES(false, " device_ledger::set_mode(unsigned int mode): invalid mode: "< batch_amounts(batch_size); for (i = 0; i < batch_size; ++i) batch_amounts[i] = outamounts[i + amounts_proved]; - rv.p.bulletproofs.push_back(proveRangeBulletproof(C, masks, batch_amounts)); - #ifdef DBG - CHECK_AND_ASSERT_THROW_MES(verBulletproof(rv.p.bulletproofs.back()), "verBulletproof failed on newly created proof"); - #endif + if (hwdev.get_mode() == hw::device::TRANSACTION_CREATE_FAKE) + { + // use a fake bulletproof for speed + rv.p.bulletproofs.push_back(make_dummy_bulletproof(batch_amounts.size())); + C = rct::keyV(batch_amounts.size(), I); + masks = rct::keyV(batch_amounts.size(), I); + } + else + { + rv.p.bulletproofs.push_back(proveRangeBulletproof(C, masks, batch_amounts)); + #ifdef DBG + CHECK_AND_ASSERT_THROW_MES(verBulletproof(rv.p.bulletproofs.back()), "verBulletproof failed on newly created proof"); + #endif + } for (i = 0; i < batch_size; ++i) { rv.outPk[i + amounts_proved].mask = rct::scalarmult8(C[i]); From 6097472a195fe81b34c2e5ab83ff2a1786c2a5e3 Mon Sep 17 00:00:00 2001 From: Lee Clagett Date: Fri, 19 Oct 2018 22:06:03 -0400 Subject: [PATCH 0656/1404] Update ZMQ fee estimate and add ZMQ output distribution --- src/rpc/CMakeLists.txt | 7 +-- src/rpc/core_rpc_server.cpp | 56 ++------------------ src/rpc/core_rpc_server_commands_defs.h | 13 +++-- src/rpc/daemon_handler.cpp | 48 +++++++++++++++-- src/rpc/daemon_handler.h | 4 +- src/rpc/daemon_messages.cpp | 58 ++++++++++++++++++--- src/rpc/daemon_messages.h | 27 ++++++++-- src/rpc/message_data_structs.h | 7 +++ src/rpc/rpc_handler.cpp | 69 +++++++++++++++++++++++++ src/rpc/rpc_handler.h | 17 ++++-- src/serialization/json_object.cpp | 23 +++++++++ src/serialization/json_object.h | 3 ++ src/wallet/wallet2.cpp | 16 +++--- 13 files changed, 260 insertions(+), 88 deletions(-) create mode 100644 src/rpc/rpc_handler.cpp diff --git a/src/rpc/CMakeLists.txt b/src/rpc/CMakeLists.txt index 4f8f96524fd..8b4c27e3ec4 100644 --- a/src/rpc/CMakeLists.txt +++ b/src/rpc/CMakeLists.txt @@ -27,7 +27,8 @@ # THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. set(rpc_base_sources - rpc_args.cpp) + rpc_args.cpp + rpc_handler.cpp) set(rpc_sources core_rpc_server.cpp @@ -43,7 +44,8 @@ set(daemon_rpc_server_sources set(rpc_base_headers - rpc_args.h) + rpc_args.h + rpc_handler.h) set(rpc_headers) @@ -63,7 +65,6 @@ set(daemon_rpc_server_private_headers message.h daemon_messages.h daemon_handler.h - rpc_handler.h zmq_server.h) diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index 55ee66a79b0..dc3860d6007 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -45,6 +45,7 @@ using namespace epee; #include "storages/http_abstract_invoke.h" #include "crypto/hash.h" #include "rpc/rpc_args.h" +#include "rpc/rpc_handler.h" #include "core_rpc_server_error_codes.h" #include "p2p/net_node.h" #include "version.h" @@ -2124,62 +2125,15 @@ namespace cryptonote const uint64_t req_to_height = req.to_height ? req.to_height : (m_core.get_current_blockchain_height() - 1); for (uint64_t amount: req.amounts) { - static struct D - { - boost::mutex mutex; - std::vector cached_distribution; - uint64_t cached_from, cached_to, cached_start_height, cached_base; - bool cached; - D(): cached_from(0), cached_to(0), cached_start_height(0), cached_base(0), cached(false) {} - } d; - boost::unique_lock lock(d.mutex); - - if (d.cached && amount == 0 && d.cached_from == req.from_height && d.cached_to == req_to_height) - { - res.distributions.push_back({amount, d.cached_start_height, req.binary, d.cached_distribution, d.cached_base}); - if (!req.cumulative) - { - auto &distribution = res.distributions.back().distribution; - for (size_t n = distribution.size() - 1; n > 0; --n) - distribution[n] -= distribution[n-1]; - distribution[0] -= d.cached_base; - } - continue; - } - - std::vector distribution; - uint64_t start_height, base; - if (!m_core.get_output_distribution(amount, req.from_height, req_to_height, start_height, distribution, base)) + auto data = rpc::RpcHandler::get_output_distribution(m_core, amount, req.from_height, req_to_height, req.cumulative); + if (!data) { error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR; - error_resp.message = "Failed to get rct distribution"; + error_resp.message = "Failed to get output distribution"; return false; } - if (req_to_height > 0 && req_to_height >= req.from_height) - { - uint64_t offset = std::max(req.from_height, start_height); - if (offset <= req_to_height && req_to_height - offset + 1 < distribution.size()) - distribution.resize(req_to_height - offset + 1); - } - - if (amount == 0) - { - d.cached_from = req.from_height; - d.cached_to = req_to_height; - d.cached_distribution = distribution; - d.cached_start_height = start_height; - d.cached_base = base; - d.cached = true; - } - - if (!req.cumulative) - { - for (size_t n = distribution.size() - 1; n > 0; --n) - distribution[n] -= distribution[n-1]; - distribution[0] -= base; - } - res.distributions.push_back({amount, start_height, req.binary, std::move(distribution), base}); + res.distributions.push_back({std::move(*data), amount, req.binary}); } } catch (const std::exception &e) diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index 3b654d4cb4a..e8720cb1455 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -33,6 +33,7 @@ #include "cryptonote_basic/cryptonote_basic.h" #include "cryptonote_basic/difficulty.h" #include "crypto/hash.h" +#include "rpc/rpc_handler.h" namespace cryptonote { @@ -2236,21 +2237,19 @@ namespace cryptonote struct distribution { + rpc::output_distribution_data data; uint64_t amount; - uint64_t start_height; bool binary; - std::vector distribution; - uint64_t base; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(amount) - KV_SERIALIZE(start_height) + KV_SERIALIZE_N(data.start_height, "start_height") KV_SERIALIZE(binary) if (this_ref.binary) - KV_SERIALIZE_CONTAINER_POD_AS_BLOB(distribution) + KV_SERIALIZE_CONTAINER_POD_AS_BLOB_N(data.distribution, "distribution") else - KV_SERIALIZE(distribution) - KV_SERIALIZE(base) + KV_SERIALIZE_N(data.distribution, "distribution") + KV_SERIALIZE_N(data.base, "base") END_KV_SERIALIZE_MAP() }; diff --git a/src/rpc/daemon_handler.cpp b/src/rpc/daemon_handler.cpp index 9d3b09b68b2..8822bd378fc 100644 --- a/src/rpc/daemon_handler.cpp +++ b/src/rpc/daemon_handler.cpp @@ -724,12 +724,53 @@ namespace rpc res.status = Message::STATUS_OK; } - void DaemonHandler::handle(const GetPerKBFeeEstimate::Request& req, GetPerKBFeeEstimate::Response& res) + void DaemonHandler::handle(const GetFeeEstimate::Request& req, GetFeeEstimate::Response& res) { - res.estimated_fee_per_kb = m_core.get_blockchain_storage().get_dynamic_base_fee_estimate(req.num_grace_blocks); + res.hard_fork_version = m_core.get_blockchain_storage().get_current_hard_fork_version(); + res.estimated_base_fee = m_core.get_blockchain_storage().get_dynamic_base_fee_estimate(req.num_grace_blocks); + + if (res.hard_fork_version < HF_VERSION_PER_BYTE_FEE) + { + res.size_scale = 1024; // per KiB fee + res.fee_mask = 1; + } + else + { + res.size_scale = 1; // per byte fee + res.fee_mask = Blockchain::get_fee_quantization_mask(); + } res.status = Message::STATUS_OK; } + void DaemonHandler::handle(const GetOutputDistribution::Request& req, GetOutputDistribution::Response& res) + { + try + { + res.distributions.reserve(req.amounts.size()); + + const uint64_t req_to_height = req.to_height ? req.to_height : (m_core.get_current_blockchain_height() - 1); + for (std::uint64_t amount : req.amounts) + { + auto data = get_output_distribution(m_core, amount, req.from_height, req_to_height, req.cumulative); + if (!data) + { + res.distributions.clear(); + res.status = Message::STATUS_FAILED; + res.error_details = "Failed to get output distribution"; + return; + } + res.distributions.push_back(output_distribution{std::move(*data), amount, req.cumulative}); + } + res.status = Message::STATUS_OK; + } + catch (const std::exception& e) + { + res.distributions.clear(); + res.status = Message::STATUS_FAILED; + res.error_details = e.what(); + } + } + bool DaemonHandler::getBlockHeaderByHash(const crypto::hash& hash_in, cryptonote::rpc::BlockHeaderResponse& header) { block b; @@ -804,7 +845,8 @@ namespace rpc REQ_RESP_TYPES_MACRO(request_type, GetOutputHistogram, req_json, resp_message, handle); REQ_RESP_TYPES_MACRO(request_type, GetOutputKeys, req_json, resp_message, handle); REQ_RESP_TYPES_MACRO(request_type, GetRPCVersion, req_json, resp_message, handle); - REQ_RESP_TYPES_MACRO(request_type, GetPerKBFeeEstimate, req_json, resp_message, handle); + REQ_RESP_TYPES_MACRO(request_type, GetFeeEstimate, req_json, resp_message, handle); + REQ_RESP_TYPES_MACRO(request_type, GetOutputDistribution, req_json, resp_message, handle); // if none of the request types matches if (resp_message == NULL) diff --git a/src/rpc/daemon_handler.h b/src/rpc/daemon_handler.h index 5f968751132..2c8ac3867e1 100644 --- a/src/rpc/daemon_handler.h +++ b/src/rpc/daemon_handler.h @@ -126,7 +126,9 @@ class DaemonHandler : public RpcHandler void handle(const GetRPCVersion::Request& req, GetRPCVersion::Response& res); - void handle(const GetPerKBFeeEstimate::Request& req, GetPerKBFeeEstimate::Response& res); + void handle(const GetFeeEstimate::Request& req, GetFeeEstimate::Response& res); + + void handle(const GetOutputDistribution::Request& req, GetOutputDistribution::Response& res); std::string handle(const std::string& request); diff --git a/src/rpc/daemon_messages.cpp b/src/rpc/daemon_messages.cpp index 56f6f6a8c1d..fa848cff46c 100644 --- a/src/rpc/daemon_messages.cpp +++ b/src/rpc/daemon_messages.cpp @@ -59,7 +59,8 @@ const char* const HardForkInfo::name = "hard_fork_info"; const char* const GetOutputHistogram::name = "get_output_histogram"; const char* const GetOutputKeys::name = "get_output_keys"; const char* const GetRPCVersion::name = "get_rpc_version"; -const char* const GetPerKBFeeEstimate::name = "get_dynamic_per_kb_fee_estimate"; +const char* const GetFeeEstimate::name = "get_dynamic_fee_estimate"; +const char* const GetOutputDistribution::name = "get_output_distribution"; @@ -825,7 +826,7 @@ void GetRPCVersion::Response::fromJson(rapidjson::Value& val) GET_FROM_JSON_OBJECT(val, version, version); } -rapidjson::Value GetPerKBFeeEstimate::Request::toJson(rapidjson::Document& doc) const +rapidjson::Value GetFeeEstimate::Request::toJson(rapidjson::Document& doc) const { auto val = Message::toJson(doc); @@ -836,27 +837,70 @@ rapidjson::Value GetPerKBFeeEstimate::Request::toJson(rapidjson::Document& doc) return val; } -void GetPerKBFeeEstimate::Request::fromJson(rapidjson::Value& val) +void GetFeeEstimate::Request::fromJson(rapidjson::Value& val) { GET_FROM_JSON_OBJECT(val, num_grace_blocks, num_grace_blocks); } -rapidjson::Value GetPerKBFeeEstimate::Response::toJson(rapidjson::Document& doc) const +rapidjson::Value GetFeeEstimate::Response::toJson(rapidjson::Document& doc) const { auto val = Message::toJson(doc); auto& al = doc.GetAllocator(); - INSERT_INTO_JSON_OBJECT(val, doc, estimated_fee_per_kb, estimated_fee_per_kb); + INSERT_INTO_JSON_OBJECT(val, doc, estimated_base_fee, estimated_base_fee); + INSERT_INTO_JSON_OBJECT(val, doc, fee_mask, fee_mask); + INSERT_INTO_JSON_OBJECT(val, doc, size_scale, size_scale); + INSERT_INTO_JSON_OBJECT(val, doc, hard_fork_version, hard_fork_version); return val; } -void GetPerKBFeeEstimate::Response::fromJson(rapidjson::Value& val) +void GetFeeEstimate::Response::fromJson(rapidjson::Value& val) { - GET_FROM_JSON_OBJECT(val, estimated_fee_per_kb, estimated_fee_per_kb); + GET_FROM_JSON_OBJECT(val, estimated_base_fee, estimated_base_fee); + GET_FROM_JSON_OBJECT(val, fee_mask, fee_mask); + GET_FROM_JSON_OBJECT(val, size_scale, size_scale); + GET_FROM_JSON_OBJECT(val, hard_fork_version, hard_fork_version); } +rapidjson::Value GetOutputDistribution::Request::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + auto& al = doc.GetAllocator(); + + INSERT_INTO_JSON_OBJECT(val, doc, amounts, amounts); + INSERT_INTO_JSON_OBJECT(val, doc, from_height, from_height); + INSERT_INTO_JSON_OBJECT(val, doc, to_height, to_height); + INSERT_INTO_JSON_OBJECT(val, doc, cumulative, cumulative); + + return val; +} + +void GetOutputDistribution::Request::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, amounts, amounts); + GET_FROM_JSON_OBJECT(val, from_height, from_height); + GET_FROM_JSON_OBJECT(val, to_height, to_height); + GET_FROM_JSON_OBJECT(val, cumulative, cumulative); +} + +rapidjson::Value GetOutputDistribution::Response::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + auto& al = doc.GetAllocator(); + + INSERT_INTO_JSON_OBJECT(val, doc, status, status); + INSERT_INTO_JSON_OBJECT(val, doc, distributions, distributions); + + return val; +} + +void GetOutputDistribution::Response::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, status, status); + GET_FROM_JSON_OBJECT(val, distributions, distributions); +} } // namespace rpc diff --git a/src/rpc/daemon_messages.h b/src/rpc/daemon_messages.h index be3138e3b6d..70f4c50f1d7 100644 --- a/src/rpc/daemon_messages.h +++ b/src/rpc/daemon_messages.h @@ -28,6 +28,9 @@ #pragma once +#include +#include + #include "message.h" #include "cryptonote_protocol/cryptonote_protocol_defs.h" #include "rpc/message_data_structs.h" @@ -62,8 +65,6 @@ class classname \ #define END_RPC_MESSAGE_RESPONSE }; #define END_RPC_MESSAGE_CLASS }; -#define COMMA() , - // NOTE: when using a type with multiple template parameters, // replace any comma in the template specifier with the macro // above, or the preprocessor will eat the comma in a bad way. @@ -118,7 +119,8 @@ BEGIN_RPC_MESSAGE_CLASS(GetTransactions); RPC_MESSAGE_MEMBER(std::vector, tx_hashes); END_RPC_MESSAGE_REQUEST; BEGIN_RPC_MESSAGE_RESPONSE; - RPC_MESSAGE_MEMBER(std::unordered_map, txs); + using txes_map = std::unordered_map; + RPC_MESSAGE_MEMBER(txes_map, txs); RPC_MESSAGE_MEMBER(std::vector, missed_hashes); END_RPC_MESSAGE_RESPONSE; END_RPC_MESSAGE_CLASS; @@ -407,12 +409,27 @@ BEGIN_RPC_MESSAGE_CLASS(GetRPCVersion); END_RPC_MESSAGE_RESPONSE; END_RPC_MESSAGE_CLASS; -BEGIN_RPC_MESSAGE_CLASS(GetPerKBFeeEstimate); +BEGIN_RPC_MESSAGE_CLASS(GetFeeEstimate); BEGIN_RPC_MESSAGE_REQUEST; RPC_MESSAGE_MEMBER(uint64_t, num_grace_blocks); END_RPC_MESSAGE_REQUEST; BEGIN_RPC_MESSAGE_RESPONSE; - RPC_MESSAGE_MEMBER(uint64_t, estimated_fee_per_kb); + RPC_MESSAGE_MEMBER(uint64_t, estimated_base_fee); + RPC_MESSAGE_MEMBER(uint64_t, fee_mask); + RPC_MESSAGE_MEMBER(uint32_t, size_scale); + RPC_MESSAGE_MEMBER(uint8_t, hard_fork_version); + END_RPC_MESSAGE_RESPONSE; +END_RPC_MESSAGE_CLASS; + +BEGIN_RPC_MESSAGE_CLASS(GetOutputDistribution); + BEGIN_RPC_MESSAGE_REQUEST; + RPC_MESSAGE_MEMBER(std::vector, amounts); + RPC_MESSAGE_MEMBER(uint64_t, from_height); + RPC_MESSAGE_MEMBER(uint64_t, to_height); + RPC_MESSAGE_MEMBER(bool, cumulative); + END_RPC_MESSAGE_REQUEST; + BEGIN_RPC_MESSAGE_RESPONSE; + RPC_MESSAGE_MEMBER(std::vector, distributions); END_RPC_MESSAGE_RESPONSE; END_RPC_MESSAGE_CLASS; diff --git a/src/rpc/message_data_structs.h b/src/rpc/message_data_structs.h index cf15ade1cf8..3b56aff1509 100644 --- a/src/rpc/message_data_structs.h +++ b/src/rpc/message_data_structs.h @@ -31,6 +31,7 @@ #include "crypto/hash.h" #include "cryptonote_basic/cryptonote_basic.h" #include "ringct/rctSigs.h" +#include "rpc/rpc_handler.h" #include #include @@ -192,6 +193,12 @@ namespace rpc uint64_t start_time; }; + struct output_distribution + { + output_distribution_data data; + uint64_t amount; + bool cumulative; + }; } // namespace rpc } // namespace cryptonote diff --git a/src/rpc/rpc_handler.cpp b/src/rpc/rpc_handler.cpp new file mode 100644 index 00000000000..d4beb192858 --- /dev/null +++ b/src/rpc/rpc_handler.cpp @@ -0,0 +1,69 @@ + +#include +#include +#include + +#include "cryptonote_core/cryptonote_core.h" + +namespace cryptonote +{ +namespace rpc +{ + namespace + { + output_distribution_data + process_distribution(bool cumulative, std::uint64_t start_height, std::vector distribution, std::uint64_t base) + { + if (!cumulative && !distribution.empty()) + { + for (std::size_t n = distribution.size() - 1; 0 < n; --n) + distribution[n] -= distribution[n - 1]; + distribution[0] -= base; + } + + return {std::move(distribution), start_height, base}; + } + } + + boost::optional + RpcHandler::get_output_distribution(core& src, std::uint64_t amount, std::uint64_t from_height, std::uint64_t to_height, bool cumulative) + { + static struct D + { + boost::mutex mutex; + std::vector cached_distribution; + std::uint64_t cached_from, cached_to, cached_start_height, cached_base; + bool cached; + D(): cached_from(0), cached_to(0), cached_start_height(0), cached_base(0), cached(false) {} + } d; + const boost::unique_lock lock(d.mutex); + + if (d.cached && amount == 0 && d.cached_from == from_height && d.cached_to == to_height) + return process_distribution(cumulative, d.cached_start_height, d.cached_distribution, d.cached_base); + + std::vector distribution; + std::uint64_t start_height, base; + if (!src.get_output_distribution(amount, from_height, to_height, start_height, distribution, base)) + return boost::none; + + if (to_height > 0 && to_height >= from_height) + { + const std::uint64_t offset = std::max(from_height, start_height); + if (offset <= to_height && to_height - offset + 1 < distribution.size()) + distribution.resize(to_height - offset + 1); + } + + if (amount == 0) + { + d.cached_from = from_height; + d.cached_to = to_height; + d.cached_distribution = distribution; + d.cached_start_height = start_height; + d.cached_base = base; + d.cached = true; + } + + return process_distribution(cumulative, start_height, std::move(distribution), base); + } +} // rpc +} // cryptonote diff --git a/src/rpc/rpc_handler.h b/src/rpc/rpc_handler.h index 64bade5a819..3cccef78a6f 100644 --- a/src/rpc/rpc_handler.h +++ b/src/rpc/rpc_handler.h @@ -28,24 +28,35 @@ #pragma once +#include +#include #include +#include namespace cryptonote { +class core; namespace rpc { +struct output_distribution_data +{ + std::vector distribution; + std::uint64_t start_height; + std::uint64_t base; +}; class RpcHandler { public: + RpcHandler() { } + virtual ~RpcHandler() { } virtual std::string handle(const std::string& request) = 0; - RpcHandler() { } - - virtual ~RpcHandler() { } + static boost::optional + get_output_distribution(core& src, std::uint64_t amount, std::uint64_t from_height, std::uint64_t to_height, bool cumulative); }; diff --git a/src/serialization/json_object.cpp b/src/serialization/json_object.cpp index 89a1dbd2392..7980e89538f 100644 --- a/src/serialization/json_object.cpp +++ b/src/serialization/json_object.cpp @@ -1232,6 +1232,29 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::DaemonInfo& inf GET_FROM_JSON_OBJECT(val, info.start_time, start_time); } +void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::output_distribution& dist, rapidjson::Value& val) +{ + val.SetObject(); + + INSERT_INTO_JSON_OBJECT(val, doc, distribution, dist.data.distribution); + INSERT_INTO_JSON_OBJECT(val, doc, amount, dist.amount); + INSERT_INTO_JSON_OBJECT(val, doc, start_height, dist.data.start_height); + INSERT_INTO_JSON_OBJECT(val, doc, base, dist.data.base); +} + +void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::output_distribution& dist) +{ + if (!val.IsObject()) + { + throw WRONG_TYPE("json object"); + } + + GET_FROM_JSON_OBJECT(val, dist.data.distribution, distribution); + GET_FROM_JSON_OBJECT(val, dist.amount, amount); + GET_FROM_JSON_OBJECT(val, dist.data.start_height, start_height); + GET_FROM_JSON_OBJECT(val, dist.data.base, base); +} + } // namespace json } // namespace cryptonote diff --git a/src/serialization/json_object.h b/src/serialization/json_object.h index da3351fe3ae..b6384ca0db6 100644 --- a/src/serialization/json_object.h +++ b/src/serialization/json_object.h @@ -281,6 +281,9 @@ void fromJsonValue(const rapidjson::Value& val, rct::mgSig& sig); void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::DaemonInfo& info, rapidjson::Value& val); void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::DaemonInfo& info); +void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::output_distribution& dist, rapidjson::Value& val); +void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::output_distribution& dist); + template typename std::enable_if::value, void>::type toJsonValue(rapidjson::Document& doc, const Map& map, rapidjson::Value& val); diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 6b3a8533e3e..21793b58d50 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -2865,8 +2865,8 @@ bool wallet2::get_rct_distribution(uint64_t &start_height, std::vector MWARNING("Failed to request output distribution: results are not for amount 0"); return false; } - start_height = res.distributions[0].start_height; - distribution = std::move(res.distributions[0].distribution); + start_height = res.distributions[0].data.start_height; + distribution = std::move(res.distributions[0].data.distribution); return true; } //---------------------------------------------------------------------------------------------------- @@ -6747,13 +6747,13 @@ void wallet2::get_outs(std::vector> { if (d.amount == amount) { - THROW_WALLET_EXCEPTION_IF(d.start_height > segregation_fork_height, error::get_output_distribution, "Distribution start_height too high"); - THROW_WALLET_EXCEPTION_IF(segregation_fork_height - d.start_height >= d.distribution.size(), error::get_output_distribution, "Distribution size too small"); - THROW_WALLET_EXCEPTION_IF(segregation_fork_height - RECENT_OUTPUT_BLOCKS - d.start_height >= d.distribution.size(), error::get_output_distribution, "Distribution size too small"); + THROW_WALLET_EXCEPTION_IF(d.data.start_height > segregation_fork_height, error::get_output_distribution, "Distribution start_height too high"); + THROW_WALLET_EXCEPTION_IF(segregation_fork_height - d.data.start_height >= d.data.distribution.size(), error::get_output_distribution, "Distribution size too small"); + THROW_WALLET_EXCEPTION_IF(segregation_fork_height - RECENT_OUTPUT_BLOCKS - d.data.start_height >= d.data.distribution.size(), error::get_output_distribution, "Distribution size too small"); THROW_WALLET_EXCEPTION_IF(segregation_fork_height <= RECENT_OUTPUT_BLOCKS, error::wallet_internal_error, "Fork height too low"); - THROW_WALLET_EXCEPTION_IF(segregation_fork_height - RECENT_OUTPUT_BLOCKS < d.start_height, error::get_output_distribution, "Bad start height"); - uint64_t till_fork = d.distribution[segregation_fork_height - d.start_height]; - uint64_t recent = till_fork - d.distribution[segregation_fork_height - RECENT_OUTPUT_BLOCKS - d.start_height]; + THROW_WALLET_EXCEPTION_IF(segregation_fork_height - RECENT_OUTPUT_BLOCKS < d.data.start_height, error::get_output_distribution, "Bad start height"); + uint64_t till_fork = d.data.distribution[segregation_fork_height - d.data.start_height]; + uint64_t recent = till_fork - d.data.distribution[segregation_fork_height - RECENT_OUTPUT_BLOCKS - d.data.start_height]; segregation_limit[amount] = std::make_pair(till_fork, recent); found = true; break; From c4fbfd786ab15e51a39183ae6124977a13ae0a3d Mon Sep 17 00:00:00 2001 From: Pol Mauri Date: Tue, 23 Oct 2018 23:11:54 -0700 Subject: [PATCH 0657/1404] Small function declaration cleanup in slow-hash.c - These functions are declared twice in slow-hash.c. Remove one of the copies. - The declarations have the wrong return type, should be void, not int. Function definitions here: https://github.com/monero-project/monero/blob/1e74586ee99e4bd89626d2eb4d23883cd91f0f81/src/crypto/aesb.c#L151-L180 Test plan: make release-test --- src/crypto/slow-hash.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/crypto/slow-hash.c b/src/crypto/slow-hash.c index 40cfb0461e5..ed61e101736 100644 --- a/src/crypto/slow-hash.c +++ b/src/crypto/slow-hash.c @@ -47,8 +47,8 @@ #define INIT_SIZE_BLK 8 #define INIT_SIZE_BYTE (INIT_SIZE_BLK * AES_BLOCK_SIZE) -extern int aesb_single_round(const uint8_t *in, uint8_t*out, const uint8_t *expandedKey); -extern int aesb_pseudo_round(const uint8_t *in, uint8_t *out, const uint8_t *expandedKey); +extern void aesb_single_round(const uint8_t *in, uint8_t *out, const uint8_t *expandedKey); +extern void aesb_pseudo_round(const uint8_t *in, uint8_t *out, const uint8_t *expandedKey); #define VARIANT1_1(p) \ do if (variant == 1) \ @@ -1408,9 +1408,6 @@ static void (*const extra_hashes[4])(const void *, size_t, char *) = { hash_extra_blake, hash_extra_groestl, hash_extra_jh, hash_extra_skein }; -extern int aesb_single_round(const uint8_t *in, uint8_t*out, const uint8_t *expandedKey); -extern int aesb_pseudo_round(const uint8_t *in, uint8_t *out, const uint8_t *expandedKey); - static size_t e2i(const uint8_t* a, size_t count) { return (*((uint64_t*)a) / AES_BLOCK_SIZE) & (count - 1); } static void mul(const uint8_t* a, const uint8_t* b, uint8_t* res) { From 07c6280909f8d6bb4d4d8e2399b56a0ce02393c6 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 19 Aug 2018 22:40:30 +0000 Subject: [PATCH 0658/1404] epee: some minor speedup in parsing --- contrib/epee/include/storages/parserse_base_utils.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/contrib/epee/include/storages/parserse_base_utils.h b/contrib/epee/include/storages/parserse_base_utils.h index 8c6c1a64d39..31495ae137b 100644 --- a/contrib/epee/include/storages/parserse_base_utils.h +++ b/contrib/epee/include/storages/parserse_base_utils.h @@ -39,12 +39,14 @@ namespace misc_utils inline std::string transform_to_escape_sequence(const std::string& src) { static const char escaped[] = "\b\f\n\r\t\v\"\\/"; - if (std::find_first_of(src.begin(), src.end(), escaped, escaped + sizeof(escaped)) == src.end()) + std::string::const_iterator it = std::find_first_of(src.begin(), src.end(), escaped, escaped + sizeof(escaped)); + if (it == src.end()) return src; std::string res; res.reserve(2 * src.size()); - for(std::string::const_iterator it = src.begin(); it!=src.end(); ++it) + res.assign(src.begin(), it); + for(; it!=src.end(); ++it) { switch(*it) { From e9fc9ff2684188d9e27ae09695bca186ca28d612 Mon Sep 17 00:00:00 2001 From: Sarang Noether <32460187+SarangNoether@users.noreply.github.com> Date: Wed, 24 Oct 2018 13:41:46 +0000 Subject: [PATCH 0659/1404] Added research information to main readme --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index 6b6f0cac576..9c70fd38ea6 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,15 @@ Portions Copyright (c) 2012-2013 The Cryptonote developers. - Our [Vulnerability Response Process](https://github.com/monero-project/meta/blob/master/VULNERABILITY_RESPONSE_PROCESS.md) encourages responsible disclosure - We are also available via [HackerOne](https://hackerone.com/monero) +## Research + +The [Monero Research Lab](https://src.getmonero.org/resources/research-lab/) coordinates Monero research as a project workgroup. We welcome collaboration and contributions from outside researchers! Because not all Lab work and publications are distributed as traditional preprints or articles, they may be easy to miss if you are conducting literature reviews for your own Monero research. You are encouraged to get in touch with our researchers if you have questions, wish to collaborate, or would like guidance to help avoid unnecessarily duplicating earlier or known work. + +Our researchers are available on IRC in [#monero-research-lab on Freenode](https://webchat.freenode.net/?randomnick=1&channels=%23monero-research-lab&prompt=1&uio=d4) or by email: + +- Sarang Noether, Ph.D.: [sarang@getmonero.org](mailto:sarang@getmonero.org) or [sarang.noether@protonmail.com](mailto:sarang.noether@protonmail.com) +- Surae Noether (Brandon Goodell), Ph.D.: [surae@getmonero.org](mailto:surae@getmonero.org) or [surae.noether@protonmail.com](mailto:surae.noether@protonmail.com) + ## Announcements - You can subscribe to an [announcement listserv](https://lists.getmonero.org) to get critical announcements from the Monero core team. The announcement list can be very helpful for knowing when software updates are needed. From 9168fc9f78c1276c986c7effd63ba4f258417fd9 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 24 Oct 2018 14:43:31 +0000 Subject: [PATCH 0660/1404] Makefile: fix building without a git tree --- Makefile | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Makefile b/Makefile index 40b8839ccea..f9270ebf2c5 100644 --- a/Makefile +++ b/Makefile @@ -28,6 +28,11 @@ ANDROID_STANDALONE_TOOLCHAIN_PATH ?= /usr/local/toolchain +dotgit=$(shell ls -d .git/config) +ifneq ($(dotgit), .git/config) + USE_SINGLE_BUILDDIR=1 +endif + subbuilddir:=$(shell echo `uname | sed -e 's|[:/\\ \(\)]|_|g'`/`git branch | grep '\* ' | cut -f2- -d' '| sed -e 's|[:/\\ \(\)]|_|g'`) ifeq ($(USE_SINGLE_BUILDDIR),) builddir := build/"$(subbuilddir)" From 8a512b1d319847e2f282747268432f0fb7e810ea Mon Sep 17 00:00:00 2001 From: Sarang Noether <32460187+SarangNoether@users.noreply.github.com> Date: Wed, 24 Oct 2018 19:41:44 +0000 Subject: [PATCH 0661/1404] Minor wording changes; added research repository links --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 9c70fd38ea6..1e896dddcc6 100644 --- a/README.md +++ b/README.md @@ -18,12 +18,12 @@ Portions Copyright (c) 2012-2013 The Cryptonote developers. ## Research -The [Monero Research Lab](https://src.getmonero.org/resources/research-lab/) coordinates Monero research as a project workgroup. We welcome collaboration and contributions from outside researchers! Because not all Lab work and publications are distributed as traditional preprints or articles, they may be easy to miss if you are conducting literature reviews for your own Monero research. You are encouraged to get in touch with our researchers if you have questions, wish to collaborate, or would like guidance to help avoid unnecessarily duplicating earlier or known work. +The [Monero Research Lab](https://src.getmonero.org/resources/research-lab/) is an open forum where the community coordinates research into Monero cryptography, protocols, fungibility, analysis, and more. We welcome collaboration and contributions from outside researchers! Because not all Lab work and publications are distributed as traditional preprints or articles, they may be easy to miss if you are conducting literature reviews for your own Monero research. You are encouraged to get in touch with our researchers if you have questions, wish to collaborate, or would like guidance to help avoid unnecessarily duplicating earlier or known work. Our researchers are available on IRC in [#monero-research-lab on Freenode](https://webchat.freenode.net/?randomnick=1&channels=%23monero-research-lab&prompt=1&uio=d4) or by email: -- Sarang Noether, Ph.D.: [sarang@getmonero.org](mailto:sarang@getmonero.org) or [sarang.noether@protonmail.com](mailto:sarang.noether@protonmail.com) -- Surae Noether (Brandon Goodell), Ph.D.: [surae@getmonero.org](mailto:surae@getmonero.org) or [surae.noether@protonmail.com](mailto:surae.noether@protonmail.com) +- Sarang Noether, Ph.D.: [sarang@getmonero.org](mailto:sarang@getmonero.org) or [sarang.noether@protonmail.com](mailto:sarang.noether@protonmail.com); [research repository](https://github.com/SarangNoether/research-lab) +- Surae Noether (Brandon Goodell), Ph.D.: [surae@getmonero.org](mailto:surae@getmonero.org) or [surae.noether@protonmail.com](mailto:surae.noether@protonmail.com); [research repository](https://github.com/b-g-goodell/research-lab) ## Announcements From 0aff49873c28ae6d9718f790860b103ce7142354 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 24 Oct 2018 20:36:25 +0000 Subject: [PATCH 0662/1404] rpc: fix build with older compilers --- src/rpc/daemon_messages.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rpc/daemon_messages.h b/src/rpc/daemon_messages.h index be3138e3b6d..8488bab0b57 100644 --- a/src/rpc/daemon_messages.h +++ b/src/rpc/daemon_messages.h @@ -67,7 +67,7 @@ class classname \ // NOTE: when using a type with multiple template parameters, // replace any comma in the template specifier with the macro // above, or the preprocessor will eat the comma in a bad way. -#define RPC_MESSAGE_MEMBER(type, name) type name = type{} +#define RPC_MESSAGE_MEMBER(type, name) type name = {} namespace cryptonote From a9f9406c5bef07d4cd968b4bcb9116d3115355b8 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 24 Oct 2018 21:54:19 +0000 Subject: [PATCH 0663/1404] unit_tests: notify test special case for the usual weirdo --- tests/unit_tests/notify.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/unit_tests/notify.cpp b/tests/unit_tests/notify.cpp index d6811c6bd5d..62c8a126a09 100644 --- a/tests/unit_tests/notify.cpp +++ b/tests/unit_tests/notify.cpp @@ -42,7 +42,11 @@ TEST(notify, works) ASSERT_TRUE(fd >= 0); close(fd); - const std::string spec = epee::string_tools::get_current_module_folder() + "/test_notifier " + name_template + " %s"; + const std::string spec = epee::string_tools::get_current_module_folder() + "/test_notifier" +#ifdef _WIN32 + + ".exe" +#endif + + " " + name_template + " %s"; tools::Notify notify(spec.c_str()); notify.notify("1111111111111111111111111111111111111111111111111111111111111111"); From ba0faae78939f756cdd7fd03798322207054ce3a Mon Sep 17 00:00:00 2001 From: xiphon Date: Thu, 25 Oct 2018 02:06:23 +0000 Subject: [PATCH 0664/1404] crypto: fixed groestl on big-endian platforms --- src/crypto/groestl.c | 101 +++++++++++++++++++----------------- src/crypto/groestl_tables.h | 37 +++++++++++++ 2 files changed, 91 insertions(+), 47 deletions(-) diff --git a/src/crypto/groestl.c b/src/crypto/groestl.c index c8258add334..d5e2989a885 100644 --- a/src/crypto/groestl.c +++ b/src/crypto/groestl.c @@ -20,9 +20,15 @@ const uint8_t shift_Values[2][8] = {{0,1,2,3,4,5,6,7},{1,3,5,7,0,2,4,6}}; const uint8_t indices_cyclic[15] = {0,1,2,3,4,5,6,7,0,1,2,3,4,5,6}; +#if BYTE_ORDER == LITTLE_ENDIAN #define ROTATE_COLUMN_DOWN(v1, v2, amount_bytes, temp_var) {temp_var = (v1<<(8*amount_bytes))|(v2>>(8*(4-amount_bytes))); \ v2 = (v2<<(8*amount_bytes))|(v1>>(8*(4-amount_bytes))); \ v1 = temp_var;} +#else +#define ROTATE_COLUMN_DOWN(v1, v2, amount_bytes, temp_var) {temp_var = (v1>>(8*amount_bytes))|(v2<<(8*(4-amount_bytes))); \ + v2 = (v2>>(8*amount_bytes))|(v1<<(8*(4-amount_bytes))); \ + v1 = temp_var;} +#endif #define COLUMN(x,y,i,c0,c1,c2,c3,c4,c5,c6,c7,tv1,tv2,tu,tl,t) \ @@ -68,14 +74,14 @@ const uint8_t indices_cyclic[15] = {0,1,2,3,4,5,6,7,0,1,2,3,4,5,6}; static void RND512P(uint8_t *x, uint32_t *y, uint32_t r) { uint32_t temp_v1, temp_v2, temp_upper_value, temp_lower_value, temp; uint32_t* x32 = (uint32_t*)x; - x32[ 0] ^= 0x00000000^r; - x32[ 2] ^= 0x00000010^r; - x32[ 4] ^= 0x00000020^r; - x32[ 6] ^= 0x00000030^r; - x32[ 8] ^= 0x00000040^r; - x32[10] ^= 0x00000050^r; - x32[12] ^= 0x00000060^r; - x32[14] ^= 0x00000070^r; + x32[ 0] ^= SWAP32LE(0x00000000)^r; + x32[ 2] ^= SWAP32LE(0x00000010)^r; + x32[ 4] ^= SWAP32LE(0x00000020)^r; + x32[ 6] ^= SWAP32LE(0x00000030)^r; + x32[ 8] ^= SWAP32LE(0x00000040)^r; + x32[10] ^= SWAP32LE(0x00000050)^r; + x32[12] ^= SWAP32LE(0x00000060)^r; + x32[14] ^= SWAP32LE(0x00000070)^r; COLUMN(x,y, 0, 0, 2, 4, 6, 9, 11, 13, 15, temp_v1, temp_v2, temp_upper_value, temp_lower_value, temp); COLUMN(x,y, 2, 2, 4, 6, 8, 11, 13, 15, 1, temp_v1, temp_v2, temp_upper_value, temp_lower_value, temp); COLUMN(x,y, 4, 4, 6, 8, 10, 13, 15, 1, 3, temp_v1, temp_v2, temp_upper_value, temp_lower_value, temp); @@ -91,21 +97,22 @@ static void RND512Q(uint8_t *x, uint32_t *y, uint32_t r) { uint32_t temp_v1, temp_v2, temp_upper_value, temp_lower_value, temp; uint32_t* x32 = (uint32_t*)x; x32[ 0] = ~x32[ 0]; - x32[ 1] ^= 0xffffffff^r; + x32[ 1] ^= SWAP32LE(0xffffffff)^r; x32[ 2] = ~x32[ 2]; - x32[ 3] ^= 0xefffffff^r; + x32[ 3] ^= SWAP32LE(0xefffffff)^r; x32[ 4] = ~x32[ 4]; - x32[ 5] ^= 0xdfffffff^r; + x32[ 5] ^= SWAP32LE(0xdfffffff)^r; x32[ 6] = ~x32[ 6]; - x32[ 7] ^= 0xcfffffff^r; + x32[ 7] ^= SWAP32LE(0xcfffffff)^r; x32[ 8] = ~x32[ 8]; - x32[ 9] ^= 0xbfffffff^r; + x32[ 9] ^= SWAP32LE(0xbfffffff)^r; x32[10] = ~x32[10]; - x32[11] ^= 0xafffffff^r; + x32[11] ^= SWAP32LE(0xafffffff)^r; x32[12] = ~x32[12]; - x32[13] ^= 0x9fffffff^r; + x32[13] ^= SWAP32LE(0x9fffffff)^r; x32[14] = ~x32[14]; - x32[15] ^= 0x8fffffff^r; + x32[15] ^= SWAP32LE(0x8fffffff)^r; + COLUMN(x,y, 0, 2, 6, 10, 14, 1, 5, 9, 13, temp_v1, temp_v2, temp_upper_value, temp_lower_value, temp); COLUMN(x,y, 2, 4, 8, 12, 0, 3, 7, 11, 15, temp_v1, temp_v2, temp_upper_value, temp_lower_value, temp); COLUMN(x,y, 4, 6, 10, 14, 2, 5, 9, 13, 1, temp_v1, temp_v2, temp_upper_value, temp_lower_value, temp); @@ -130,28 +137,28 @@ static void F512(uint32_t *h, const uint32_t *m) { } /* compute Q(m) */ - RND512Q((uint8_t*)z, y, 0x00000000); - RND512Q((uint8_t*)y, z, 0x01000000); - RND512Q((uint8_t*)z, y, 0x02000000); - RND512Q((uint8_t*)y, z, 0x03000000); - RND512Q((uint8_t*)z, y, 0x04000000); - RND512Q((uint8_t*)y, z, 0x05000000); - RND512Q((uint8_t*)z, y, 0x06000000); - RND512Q((uint8_t*)y, z, 0x07000000); - RND512Q((uint8_t*)z, y, 0x08000000); - RND512Q((uint8_t*)y, Qtmp, 0x09000000); + RND512Q((uint8_t*)z, y, SWAP32LE(0x00000000)); + RND512Q((uint8_t*)y, z, SWAP32LE(0x01000000)); + RND512Q((uint8_t*)z, y, SWAP32LE(0x02000000)); + RND512Q((uint8_t*)y, z, SWAP32LE(0x03000000)); + RND512Q((uint8_t*)z, y, SWAP32LE(0x04000000)); + RND512Q((uint8_t*)y, z, SWAP32LE(0x05000000)); + RND512Q((uint8_t*)z, y, SWAP32LE(0x06000000)); + RND512Q((uint8_t*)y, z, SWAP32LE(0x07000000)); + RND512Q((uint8_t*)z, y, SWAP32LE(0x08000000)); + RND512Q((uint8_t*)y, Qtmp, SWAP32LE(0x09000000)); /* compute P(h+m) */ - RND512P((uint8_t*)Ptmp, y, 0x00000000); - RND512P((uint8_t*)y, z, 0x00000001); - RND512P((uint8_t*)z, y, 0x00000002); - RND512P((uint8_t*)y, z, 0x00000003); - RND512P((uint8_t*)z, y, 0x00000004); - RND512P((uint8_t*)y, z, 0x00000005); - RND512P((uint8_t*)z, y, 0x00000006); - RND512P((uint8_t*)y, z, 0x00000007); - RND512P((uint8_t*)z, y, 0x00000008); - RND512P((uint8_t*)y, Ptmp, 0x00000009); + RND512P((uint8_t*)Ptmp, y, SWAP32LE(0x00000000)); + RND512P((uint8_t*)y, z, SWAP32LE(0x00000001)); + RND512P((uint8_t*)z, y, SWAP32LE(0x00000002)); + RND512P((uint8_t*)y, z, SWAP32LE(0x00000003)); + RND512P((uint8_t*)z, y, SWAP32LE(0x00000004)); + RND512P((uint8_t*)y, z, SWAP32LE(0x00000005)); + RND512P((uint8_t*)z, y, SWAP32LE(0x00000006)); + RND512P((uint8_t*)y, z, SWAP32LE(0x00000007)); + RND512P((uint8_t*)z, y, SWAP32LE(0x00000008)); + RND512P((uint8_t*)y, Ptmp, SWAP32LE(0x00000009)); /* compute P(h+m) + Q(m) + h */ for (i = 0; i < 2*COLS512; i++) { @@ -188,16 +195,16 @@ static void OutputTransformation(hashState *ctx) { for (j = 0; j < 2*COLS512; j++) { temp[j] = ctx->chaining[j]; } - RND512P((uint8_t*)temp, y, 0x00000000); - RND512P((uint8_t*)y, z, 0x00000001); - RND512P((uint8_t*)z, y, 0x00000002); - RND512P((uint8_t*)y, z, 0x00000003); - RND512P((uint8_t*)z, y, 0x00000004); - RND512P((uint8_t*)y, z, 0x00000005); - RND512P((uint8_t*)z, y, 0x00000006); - RND512P((uint8_t*)y, z, 0x00000007); - RND512P((uint8_t*)z, y, 0x00000008); - RND512P((uint8_t*)y, temp, 0x00000009); + RND512P((uint8_t*)temp, y, SWAP32LE(0x00000000)); + RND512P((uint8_t*)y, z, SWAP32LE(0x00000001)); + RND512P((uint8_t*)z, y, SWAP32LE(0x00000002)); + RND512P((uint8_t*)y, z, SWAP32LE(0x00000003)); + RND512P((uint8_t*)z, y, SWAP32LE(0x00000004)); + RND512P((uint8_t*)y, z, SWAP32LE(0x00000005)); + RND512P((uint8_t*)z, y, SWAP32LE(0x00000006)); + RND512P((uint8_t*)y, z, SWAP32LE(0x00000007)); + RND512P((uint8_t*)z, y, SWAP32LE(0x00000008)); + RND512P((uint8_t*)y, temp, SWAP32LE(0x00000009)); for (j = 0; j < 2*COLS512; j++) { ctx->chaining[j] ^= temp[j]; } @@ -213,7 +220,7 @@ static void Init(hashState* ctx) { } /* set initial value */ - ctx->chaining[2*COLS512-1] = u32BIG((uint32_t)HASH_BIT_LEN); + ctx->chaining[2*COLS512-1] = SWAP32LE(u32BIG((uint32_t)HASH_BIT_LEN)); /* set other variables */ ctx->buf_ptr = 0; diff --git a/src/crypto/groestl_tables.h b/src/crypto/groestl_tables.h index c4b368584e1..53594c5697b 100644 --- a/src/crypto/groestl_tables.h +++ b/src/crypto/groestl_tables.h @@ -29,7 +29,10 @@ #ifndef __tables_h #define __tables_h +#include "common/int-util.h" + +#if BYTE_ORDER == LITTLE_ENDIAN const uint32_t T[512] = {0xa5f432c6, 0xc6a597f4, 0x84976ff8, 0xf884eb97, 0x99b05eee, 0xee99c7b0, 0x8d8c7af6, 0xf68df78c, 0xd17e8ff, 0xff0de517, 0xbddc0ad6, 0xd6bdb7dc, 0xb1c816de, 0xdeb1a7c8, 0x54fc6d91, 0x915439fc , 0x50f09060, 0x6050c0f0, 0x3050702, 0x2030405, 0xa9e02ece, 0xcea987e0, 0x7d87d156, 0x567dac87, 0x192bcce7, 0xe719d52b, 0x62a613b5, 0xb56271a6, 0xe6317c4d, 0x4de69a31, 0x9ab559ec, 0xec9ac3b5 , 0x45cf408f, 0x8f4505cf, 0x9dbca31f, 0x1f9d3ebc, 0x40c04989, 0x894009c0, 0x879268fa, 0xfa87ef92, 0x153fd0ef, 0xef15c53f, 0xeb2694b2, 0xb2eb7f26, 0xc940ce8e, 0x8ec90740, 0xb1de6fb, 0xfb0bed1d @@ -62,5 +65,39 @@ const uint32_t T[512] = {0xa5f432c6, 0xc6a597f4, 0x84976ff8, 0xf884eb97, 0x99b05 , 0xb6c1ec2d, 0x2db65ac1, 0x22665a3c, 0x3c227866, 0x92adb815, 0x15922aad, 0x2060a9c9, 0xc9208960, 0x49db5c87, 0x874915db, 0xff1ab0aa, 0xaaff4f1a, 0x7888d850, 0x5078a088, 0x7a8e2ba5, 0xa57a518e , 0x8f8a8903, 0x38f068a, 0xf8134a59, 0x59f8b213, 0x809b9209, 0x980129b, 0x1739231a, 0x1a173439, 0xda751065, 0x65daca75, 0x315384d7, 0xd731b553, 0xc651d584, 0x84c61351, 0xb8d303d0, 0xd0b8bbd3 , 0xc35edc82, 0x82c31f5e, 0xb0cbe229, 0x29b052cb, 0x7799c35a, 0x5a77b499, 0x11332d1e, 0x1e113c33, 0xcb463d7b, 0x7bcbf646, 0xfc1fb7a8, 0xa8fc4b1f, 0xd6610c6d, 0x6dd6da61, 0x3a4e622c, 0x2c3a584e}; +#else +const uint32_t T[512] = {0xc632f4a5, 0xf497a5c6, 0xf86f9784, 0x97eb84f8, 0xee5eb099, 0xb0c799ee, 0xf67a8c8d, 0x8cf78df6, 0xffe8170d, 0x17e50dff, 0xd60adcbd, 0xdcb7bdd6, 0xde16c8b1, 0xc8a7b1de, 0x916dfc54, 0xfc395491 +, 0x6090f050, 0xf0c05060, 0x02070503, 0x05040302, 0xce2ee0a9, 0xe087a9ce, 0x56d1877d, 0x87ac7d56, 0xe7cc2b19, 0x2bd519e7, 0xb513a662, 0xa67162b5, 0x4d7c31e6, 0x319ae64d, 0xec59b59a, 0xb5c39aec +, 0x8f40cf45, 0xcf05458f, 0x1fa3bc9d, 0xbc3e9d1f, 0x8949c040, 0xc0094089, 0xfa689287, 0x92ef87fa, 0xefd03f15, 0x3fc515ef, 0xb29426eb, 0x267febb2, 0x8ece40c9, 0x4007c98e, 0xfbe61d0b, 0x1ded0bfb +, 0x416e2fec, 0x2f82ec41, 0xb31aa967, 0xa97d67b3, 0x5f431cfd, 0x1cbefd5f, 0x456025ea, 0x258aea45, 0x23f9dabf, 0xda46bf23, 0x535102f7, 0x02a6f753, 0xe445a196, 0xa1d396e4, 0x9b76ed5b, 0xed2d5b9b +, 0x75285dc2, 0x5deac275, 0xe1c5241c, 0x24d91ce1, 0x3dd4e9ae, 0xe97aae3d, 0x4cf2be6a, 0xbe986a4c, 0x6c82ee5a, 0xeed85a6c, 0x7ebdc341, 0xc3fc417e, 0xf5f30602, 0x06f102f5, 0x8352d14f, 0xd11d4f83 +, 0x688ce45c, 0xe4d05c68, 0x515607f4, 0x07a2f451, 0xd18d5c34, 0x5cb934d1, 0xf9e11808, 0x18e908f9, 0xe24cae93, 0xaedf93e2, 0xab3e9573, 0x954d73ab, 0x6297f553, 0xf5c45362, 0x2a6b413f, 0x41543f2a +, 0x081c140c, 0x14100c08, 0x9563f652, 0xf6315295, 0x46e9af65, 0xaf8c6546, 0x9d7fe25e, 0xe2215e9d, 0x30487828, 0x78602830, 0x37cff8a1, 0xf86ea137, 0x0a1b110f, 0x11140f0a, 0x2febc4b5, 0xc45eb52f +, 0x0e151b09, 0x1b1c090e, 0x247e5a36, 0x5a483624, 0x1badb69b, 0xb6369b1b, 0xdf98473d, 0x47a53ddf, 0xcda76a26, 0x6a8126cd, 0x4ef5bb69, 0xbb9c694e, 0x7f334ccd, 0x4cfecd7f, 0xea50ba9f, 0xbacf9fea +, 0x123f2d1b, 0x2d241b12, 0x1da4b99e, 0xb93a9e1d, 0x58c49c74, 0x9cb07458, 0x3446722e, 0x72682e34, 0x3641772d, 0x776c2d36, 0xdc11cdb2, 0xcda3b2dc, 0xb49d29ee, 0x2973eeb4, 0x5b4d16fb, 0x16b6fb5b +, 0xa4a501f6, 0x0153f6a4, 0x76a1d74d, 0xd7ec4d76, 0xb714a361, 0xa37561b7, 0x7d3449ce, 0x49face7d, 0x52df8d7b, 0x8da47b52, 0xdd9f423e, 0x42a13edd, 0x5ecd9371, 0x93bc715e, 0x13b1a297, 0xa2269713 +, 0xa6a204f5, 0x0457f5a6, 0xb901b868, 0xb86968b9, 0x00000000, 0x00000000, 0xc1b5742c, 0x74992cc1, 0x40e0a060, 0xa0806040, 0xe3c2211f, 0x21dd1fe3, 0x793a43c8, 0x43f2c879, 0xb69a2ced, 0x2c77edb6 +, 0xd40dd9be, 0xd9b3bed4, 0x8d47ca46, 0xca01468d, 0x671770d9, 0x70ced967, 0x72afdd4b, 0xdde44b72, 0x94ed79de, 0x7933de94, 0x98ff67d4, 0x672bd498, 0xb09323e8, 0x237be8b0, 0x855bde4a, 0xde114a85 +, 0xbb06bd6b, 0xbd6d6bbb, 0xc5bb7e2a, 0x7e912ac5, 0x4f7b34e5, 0x349ee54f, 0xedd73a16, 0x3ac116ed, 0x86d254c5, 0x5417c586, 0x9af862d7, 0x622fd79a, 0x6699ff55, 0xffcc5566, 0x11b6a794, 0xa7229411 +, 0x8ac04acf, 0x4a0fcf8a, 0xe9d93010, 0x30c910e9, 0x040e0a06, 0x0a080604, 0xfe669881, 0x98e781fe, 0xa0ab0bf0, 0x0b5bf0a0, 0x78b4cc44, 0xccf04478, 0x25f0d5ba, 0xd54aba25, 0x4b753ee3, 0x3e96e34b +, 0xa2ac0ef3, 0x0e5ff3a2, 0x5d4419fe, 0x19bafe5d, 0x80db5bc0, 0x5b1bc080, 0x0580858a, 0x850a8a05, 0x3fd3ecad, 0xec7ead3f, 0x21fedfbc, 0xdf42bc21, 0x70a8d848, 0xd8e04870, 0xf1fd0c04, 0x0cf904f1 +, 0x63197adf, 0x7ac6df63, 0x772f58c1, 0x58eec177, 0xaf309f75, 0x9f4575af, 0x42e7a563, 0xa5846342, 0x20705030, 0x50403020, 0xe5cb2e1a, 0x2ed11ae5, 0xfdef120e, 0x12e10efd, 0xbf08b76d, 0xb7656dbf +, 0x8155d44c, 0xd4194c81, 0x18243c14, 0x3c301418, 0x26795f35, 0x5f4c3526, 0xc3b2712f, 0x719d2fc3, 0xbe8638e1, 0x3867e1be, 0x35c8fda2, 0xfd6aa235, 0x88c74fcc, 0x4f0bcc88, 0x2e654b39, 0x4b5c392e +, 0x936af957, 0xf93d5793, 0x55580df2, 0x0daaf255, 0xfc619d82, 0x9de382fc, 0x7ab3c947, 0xc9f4477a, 0xc827efac, 0xef8bacc8, 0xba8832e7, 0x326fe7ba, 0x324f7d2b, 0x7d642b32, 0xe642a495, 0xa4d795e6 +, 0xc03bfba0, 0xfb9ba0c0, 0x19aab398, 0xb3329819, 0x9ef668d1, 0x6827d19e, 0xa322817f, 0x815d7fa3, 0x44eeaa66, 0xaa886644, 0x54d6827e, 0x82a87e54, 0x3bdde6ab, 0xe676ab3b, 0x0b959e83, 0x9e16830b +, 0x8cc945ca, 0x4503ca8c, 0xc7bc7b29, 0x7b9529c7, 0x6b056ed3, 0x6ed6d36b, 0x286c443c, 0x44503c28, 0xa72c8b79, 0x8b5579a7, 0xbc813de2, 0x3d63e2bc, 0x1631271d, 0x272c1d16, 0xad379a76, 0x9a4176ad +, 0xdb964d3b, 0x4dad3bdb, 0x649efa56, 0xfac85664, 0x74a6d24e, 0xd2e84e74, 0x1436221e, 0x22281e14, 0x92e476db, 0x763fdb92, 0x0c121e0a, 0x1e180a0c, 0x48fcb46c, 0xb4906c48, 0xb88f37e4, 0x376be4b8 +, 0x9f78e75d, 0xe7255d9f, 0xbd0fb26e, 0xb2616ebd, 0x43692aef, 0x2a86ef43, 0xc435f1a6, 0xf193a6c4, 0x39dae3a8, 0xe372a839, 0x31c6f7a4, 0xf762a431, 0xd38a5937, 0x59bd37d3, 0xf274868b, 0x86ff8bf2 +, 0xd5835632, 0x56b132d5, 0x8b4ec543, 0xc50d438b, 0x6e85eb59, 0xebdc596e, 0xda18c2b7, 0xc2afb7da, 0x018e8f8c, 0x8f028c01, 0xb11dac64, 0xac7964b1, 0x9cf16dd2, 0x6d23d29c, 0x49723be0, 0x3b92e049 +, 0xd81fc7b4, 0xc7abb4d8, 0xacb915fa, 0x1543faac, 0xf3fa0907, 0x09fd07f3, 0xcfa06f25, 0x6f8525cf, 0xca20eaaf, 0xea8fafca, 0xf47d898e, 0x89f38ef4, 0x476720e9, 0x208ee947, 0x10382818, 0x28201810 +, 0x6f0b64d5, 0x64ded56f, 0xf0738388, 0x83fb88f0, 0x4afbb16f, 0xb1946f4a, 0x5cca9672, 0x96b8725c, 0x38546c24, 0x6c702438, 0x575f08f1, 0x08aef157, 0x732152c7, 0x52e6c773, 0x9764f351, 0xf3355197 +, 0xcbae6523, 0x658d23cb, 0xa125847c, 0x84597ca1, 0xe857bf9c, 0xbfcb9ce8, 0x3e5d6321, 0x637c213e, 0x96ea7cdd, 0x7c37dd96, 0x611e7fdc, 0x7fc2dc61, 0x0d9c9186, 0x911a860d, 0x0f9b9485, 0x941e850f +, 0xe04bab90, 0xabdb90e0, 0x7cbac642, 0xc6f8427c, 0x712657c4, 0x57e2c471, 0xcc29e5aa, 0xe583aacc, 0x90e373d8, 0x733bd890, 0x06090f05, 0x0f0c0506, 0xf7f40301, 0x03f501f7, 0x1c2a3612, 0x3638121c +, 0xc23cfea3, 0xfe9fa3c2, 0x6a8be15f, 0xe1d45f6a, 0xaebe10f9, 0x1047f9ae, 0x69026bd0, 0x6bd2d069, 0x17bfa891, 0xa82e9117, 0x9971e858, 0xe8295899, 0x3a536927, 0x6974273a, 0x27f7d0b9, 0xd04eb927 +, 0xd9914838, 0x48a938d9, 0xebde3513, 0x35cd13eb, 0x2be5ceb3, 0xce56b32b, 0x22775533, 0x55443322, 0xd204d6bb, 0xd6bfbbd2, 0xa9399070, 0x904970a9, 0x07878089, 0x800e8907, 0x33c1f2a7, 0xf266a733 +, 0x2decc1b6, 0xc15ab62d, 0x3c5a6622, 0x6678223c, 0x15b8ad92, 0xad2a9215, 0xc9a96020, 0x608920c9, 0x875cdb49, 0xdb154987, 0xaab01aff, 0x1a4fffaa, 0x50d88878, 0x88a07850, 0xa52b8e7a, 0x8e517aa5 +, 0x03898a8f, 0x8a068f03, 0x594a13f8, 0x13b2f859, 0x09929b80, 0x9b128009, 0x1a233917, 0x3934171a, 0x651075da, 0x75cada65, 0xd7845331, 0x53b531d7, 0x84d551c6, 0x5113c684, 0xd003d3b8, 0xd3bbb8d0 +, 0x82dc5ec3, 0x5e1fc382, 0x29e2cbb0, 0xcb52b029, 0x5ac39977, 0x99b4775a, 0x1e2d3311, 0x333c111e, 0x7b3d46cb, 0x46f6cb7b, 0xa8b71ffc, 0x1f4bfca8, 0x6d0c61d6, 0x61dad66d, 0x2c624e3a, 0x4e583a2c}; +#endif #endif /* __tables_h */ From 91eaea9c72f7104820aff15792ec70a3e69e7f35 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 25 Oct 2018 20:22:13 +0000 Subject: [PATCH 0665/1404] mnemonics: fix words_to_bytes on big endian --- src/mnemonics/electrum-words.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/mnemonics/electrum-words.cpp b/src/mnemonics/electrum-words.cpp index 3d6338856d9..c140bb5d36f 100644 --- a/src/mnemonics/electrum-words.cpp +++ b/src/mnemonics/electrum-words.cpp @@ -335,6 +335,7 @@ namespace crypto return false; } + w[0] = SWAP32LE(w[0]); dst.append((const char*)&w[0], 4); // copy 4 bytes to position memwipe(w, sizeof(w)); } From 1f35de230014a19407bf9c7cc273d4ad7db76484 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 25 Oct 2018 23:02:42 +0000 Subject: [PATCH 0666/1404] simplewallet: display locked/unlocked state in show_transfers it doesn't display the details, which are already displayed in show_transfer --- src/simplewallet/simplewallet.cpp | 9 +++++---- src/wallet/wallet2.h | 3 ++- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 18b596662d7..fcbbb05add3 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -6749,7 +6749,8 @@ bool simple_wallet::show_transfers(const std::vector &args_) payment_id = payment_id.substr(0,16); std::string note = m_wallet->get_tx_note(pd.m_tx_hash); const std::string type = pd.m_coinbase ? tr("block") : tr("in"); - output.insert(std::make_pair(pd.m_block_height, std::make_tuple(epee::console_color_green, type, (boost::format("%25.25s %20.20s %s %s %d %s %s") % get_human_readable_timestamp(pd.m_timestamp) % print_money(pd.m_amount) % string_tools::pod_to_hex(pd.m_tx_hash) % payment_id % pd.m_subaddr_index.minor % "-" % note).str()))); + const bool unlocked = m_wallet->is_tx_spendtime_unlocked(pd.m_unlock_time, pd.m_block_height); + output.insert(std::make_pair(pd.m_block_height, std::make_tuple(epee::console_color_green, type, (boost::format("%8.8s %25.25s %20.20s %s %s %d %s %s") % (unlocked ? "unlocked" : "locked") % get_human_readable_timestamp(pd.m_timestamp) % print_money(pd.m_amount) % string_tools::pod_to_hex(pd.m_tx_hash) % payment_id % pd.m_subaddr_index.minor % "-" % note).str()))); } } @@ -6782,7 +6783,7 @@ bool simple_wallet::show_transfers(const std::vector &args_) if (payment_id.substr(16).find_first_not_of('0') == std::string::npos) payment_id = payment_id.substr(0,16); std::string note = m_wallet->get_tx_note(i->first); - output.insert(std::make_pair(pd.m_block_height, std::make_tuple(epee::console_color_magenta, tr("out"), (boost::format("%25.25s %20.20s %s %s %14.14s %s %s - %s") % get_human_readable_timestamp(pd.m_timestamp) % print_money(pd.m_amount_in - change - fee) % string_tools::pod_to_hex(i->first) % payment_id % print_money(fee) % dests % print_subaddr_indices(pd.m_subaddr_indices) % note).str()))); + output.insert(std::make_pair(pd.m_block_height, std::make_tuple(epee::console_color_magenta, tr("out"), (boost::format("%8.8s %25.25s %20.20s %s %s %14.14s %s %s - %s") % "-" % get_human_readable_timestamp(pd.m_timestamp) % print_money(pd.m_amount_in - change - fee) % string_tools::pod_to_hex(i->first) % payment_id % print_money(fee) % dests % print_subaddr_indices(pd.m_subaddr_indices) % note).str()))); } } @@ -6811,7 +6812,7 @@ bool simple_wallet::show_transfers(const std::vector &args_) std::string double_spend_note; if (i->second.m_double_spend_seen) double_spend_note = tr("[Double spend seen on the network: this transaction may or may not end up being mined] "); - message_writer() << (boost::format("%8.8s %6.6s %25.25s %20.20s %s %s %d %s %s%s") % "pool" % "in" % get_human_readable_timestamp(pd.m_timestamp) % print_money(pd.m_amount) % string_tools::pod_to_hex(pd.m_tx_hash) % payment_id % pd.m_subaddr_index.minor % "-" % note % double_spend_note).str(); + message_writer() << (boost::format("%8.8s %6.6s %8.8s %25.25s %20.20s %s %s %d %s %s%s") % "pool" % "in" % "locked" % get_human_readable_timestamp(pd.m_timestamp) % print_money(pd.m_amount) % string_tools::pod_to_hex(pd.m_tx_hash) % payment_id % pd.m_subaddr_index.minor % "-" % note % double_spend_note).str(); } } catch (const std::exception& e) @@ -6834,7 +6835,7 @@ bool simple_wallet::show_transfers(const std::vector &args_) std::string note = m_wallet->get_tx_note(i->first); bool is_failed = pd.m_state == tools::wallet2::unconfirmed_transfer_details::failed; if ((failed && is_failed) || (!is_failed && pending)) { - message_writer() << (boost::format("%8.8s %6.6s %25.25s %20.20s %s %s %14.14s %s - %s") % (is_failed ? tr("failed") : tr("pending")) % tr("out") % get_human_readable_timestamp(pd.m_timestamp) % print_money(amount - pd.m_change - fee) % string_tools::pod_to_hex(i->first) % payment_id % print_money(fee) % print_subaddr_indices(pd.m_subaddr_indices) % note).str(); + message_writer() << (boost::format("%8.8s %6.6s %8.8s %25.25s %20.20s %s %s %14.14s %s - %s") % (is_failed ? tr("failed") : tr("pending")) % tr("out") % "-" % get_human_readable_timestamp(pd.m_timestamp) % print_money(amount - pd.m_change - fee) % string_tools::pod_to_hex(i->first) % payment_id % print_money(fee) % print_subaddr_indices(pd.m_subaddr_indices) % note).str(); } } } diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 680196f0198..509a5a5a5a4 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -1187,6 +1187,8 @@ namespace tools void set_tx_notify(const std::shared_ptr ¬ify) { m_tx_notify = notify; } + bool is_tx_spendtime_unlocked(uint64_t unlock_time, uint64_t block_height) const; + private: /*! * \brief Stores wallet information to wallet file. @@ -1206,7 +1208,6 @@ namespace tools void process_new_blockchain_entry(const cryptonote::block& b, const cryptonote::block_complete_entry& bche, const parsed_block &parsed_block, const crypto::hash& bl_id, uint64_t height, const std::vector &tx_cache_data, size_t tx_cache_data_offset); void detach_blockchain(uint64_t height); void get_short_chain_history(std::list& ids, uint64_t granularity = 1) const; - bool is_tx_spendtime_unlocked(uint64_t unlock_time, uint64_t block_height) const; bool clear(); void pull_blocks(uint64_t start_height, uint64_t& blocks_start_height, const std::list &short_chain_history, std::vector &blocks, std::vector &o_indices); void pull_hashes(uint64_t start_height, uint64_t& blocks_start_height, const std::list &short_chain_history, std::vector &hashes); From dff0adfee5e658ae9442fd8b723b76e963a5cf45 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 26 Oct 2018 11:46:25 +0000 Subject: [PATCH 0667/1404] cmake: fix readline detection when the readline library is not found --- cmake/FindReadline.cmake | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cmake/FindReadline.cmake b/cmake/FindReadline.cmake index de9ddc46dce..f26911b2612 100644 --- a/cmake/FindReadline.cmake +++ b/cmake/FindReadline.cmake @@ -66,7 +66,9 @@ check_function_exists(rl_copy_text HAVE_COPY_TEXT) check_function_exists(rl_filename_completion_function HAVE_COMPLETION_FUNCTION) if(NOT HAVE_COMPLETION_FUNCTION) - set(CMAKE_REQUIRED_LIBRARIES ${Readline_LIBRARY} ${Termcap_LIBRARY}) + if (Readline_LIBRARY) + set(CMAKE_REQUIRED_LIBRARIES ${Readline_LIBRARY} ${Termcap_LIBRARY}) + endif(Readline_LIBRARY) check_function_exists(rl_copy_text HAVE_COPY_TEXT_TC) check_function_exists(rl_filename_completion_function HAVE_COMPLETION_FUNCTION_TC) set(HAVE_COMPLETION_FUNCTION ${HAVE_COMPLETION_FUNCTION_TC}) From 30f728e3627e28ceedec2ef6ce0424c061c5ed29 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 26 Oct 2018 17:01:05 +0000 Subject: [PATCH 0668/1404] performance_tests: measure ge_frombytes_vartime only not an extra dynamic cast, which might be heavy --- tests/performance_tests/ge_frombytes_vartime.h | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/tests/performance_tests/ge_frombytes_vartime.h b/tests/performance_tests/ge_frombytes_vartime.h index ef9625d6b8f..3f7d55182fa 100644 --- a/tests/performance_tests/ge_frombytes_vartime.h +++ b/tests/performance_tests/ge_frombytes_vartime.h @@ -49,22 +49,29 @@ class test_ge_frombytes_vartime : public multi_tx_test_base<1> if (!base_class::init()) return false; + cryptonote::account_base m_alice; + cryptonote::transaction m_tx; + m_alice.generate(); std::vector destinations; destinations.push_back(tx_destination_entry(1, m_alice.get_keys().m_account_address, false)); - return construct_tx(this->m_miners[this->real_source_idx].get_keys(), this->m_sources, destinations, boost::none, std::vector(), m_tx, 0); + if (!construct_tx(this->m_miners[this->real_source_idx].get_keys(), this->m_sources, destinations, boost::none, std::vector(), m_tx, 0)) + return false; + + const cryptonote::txin_to_key& txin = boost::get(m_tx.vin[0]); + m_key = rct::ki2rct(txin.k_image); + + return true; } bool test() { ge_p3 unp; - const cryptonote::txin_to_key& txin = boost::get(m_tx.vin[0]); - return ge_frombytes_vartime(&unp, (const unsigned char*) &txin.k_image) == 0; + return ge_frombytes_vartime(&unp, (const unsigned char*) &m_key) == 0; } private: - cryptonote::account_base m_alice; - cryptonote::transaction m_tx; + rct::key m_key; }; From 3a056b7393aeaa1e751deb3013a55232110c7225 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 26 Oct 2018 17:02:11 +0000 Subject: [PATCH 0669/1404] performance_tests: add ge_p3_tobytes --- tests/performance_tests/ge_tobytes.h | 79 ++++++++++++++++++++++++++++ tests/performance_tests/main.cpp | 2 + 2 files changed, 81 insertions(+) create mode 100644 tests/performance_tests/ge_tobytes.h diff --git a/tests/performance_tests/ge_tobytes.h b/tests/performance_tests/ge_tobytes.h new file mode 100644 index 00000000000..3d46f4838a5 --- /dev/null +++ b/tests/performance_tests/ge_tobytes.h @@ -0,0 +1,79 @@ +// Copyright (c) 2014-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers + +#pragma once + +#include "crypto/crypto.h" +#include "cryptonote_basic/cryptonote_basic.h" + +#include "single_tx_test_base.h" + +class test_ge_tobytes : public multi_tx_test_base<1> +{ +public: + static const size_t loop_count = 10000; + + typedef multi_tx_test_base<1> base_class; + + bool init() + { + using namespace cryptonote; + + if (!base_class::init()) + return false; + + cryptonote::account_base m_alice; + cryptonote::transaction m_tx; + + m_alice.generate(); + + std::vector destinations; + destinations.push_back(tx_destination_entry(1, m_alice.get_keys().m_account_address, false)); + + if (!construct_tx(this->m_miners[this->real_source_idx].get_keys(), this->m_sources, destinations, boost::none, std::vector(), m_tx, 0)) + return false; + + const cryptonote::txin_to_key& txin = boost::get(m_tx.vin[0]); + if (ge_frombytes_vartime(&m_p3, (const unsigned char*) &txin.k_image) != 0) + return false; + + return true; + } + + bool test() + { + rct::key key; + ge_p3_tobytes(key.bytes, &m_p3); + return true; + } + +private: + ge_p3 m_p3; +}; diff --git a/tests/performance_tests/main.cpp b/tests/performance_tests/main.cpp index 87a1573c2c8..84cd77cfb16 100644 --- a/tests/performance_tests/main.cpp +++ b/tests/performance_tests/main.cpp @@ -42,6 +42,7 @@ #include "derive_public_key.h" #include "derive_secret_key.h" #include "ge_frombytes_vartime.h" +#include "ge_tobytes.h" #include "generate_key_derivation.h" #include "generate_key_image.h" #include "generate_key_image_helper.h" @@ -182,6 +183,7 @@ int main(int argc, char** argv) TEST_PERFORMANCE0(filter, p, test_derive_public_key); TEST_PERFORMANCE0(filter, p, test_derive_secret_key); TEST_PERFORMANCE0(filter, p, test_ge_frombytes_vartime); + TEST_PERFORMANCE0(filter, p, test_ge_tobytes); TEST_PERFORMANCE0(filter, p, test_generate_keypair); TEST_PERFORMANCE0(filter, p, test_sc_reduce32); TEST_PERFORMANCE1(filter, p, test_signature, false); From a5ca7f4fb57634b1900f2e4d55dfd7177e2bc890 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 27 Oct 2018 11:14:52 +0000 Subject: [PATCH 0670/1404] core: fix unmixable special case allowing ring size below 11 --- src/cryptonote_core/blockchain.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 77b6d0b6968..e908c20120f 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -2530,7 +2530,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, } } - if (hf_version >= HF_VERSION_MIN_MIXIN_10 && mixin != 10) + if (((hf_version == HF_VERSION_MIN_MIXIN_10 || hf_version == HF_VERSION_MIN_MIXIN_10+1) && mixin != 10) || (hf_version >= HF_VERSION_MIN_MIXIN_10+2 && mixin > 10)) { MERROR_VER("Tx " << get_transaction_hash(tx) << " has invalid ring size (" << (mixin + 1) << "), it should be 11"); tvc.m_low_mixin = true; From 73e504c1391a1ce130e225a8440558ca298586a2 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 27 Oct 2018 11:15:32 +0000 Subject: [PATCH 0671/1404] rpc: adjust ring size error message now that too high is also possible --- src/rpc/core_rpc_server.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index aa9d3d64bf2..1492fc80b0e 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -700,7 +700,7 @@ namespace cryptonote res.status = "Failed"; res.reason = ""; if ((res.low_mixin = tvc.m_low_mixin)) - add_reason(res.reason, "ring size too small"); + add_reason(res.reason, "bad ring size"); if ((res.double_spend = tvc.m_double_spend)) add_reason(res.reason, "double spend"); if ((res.invalid_input = tvc.m_invalid_input)) From f48db530be93257c563a66b1f46ce305794159be Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 27 Oct 2018 11:19:28 +0000 Subject: [PATCH 0672/1404] unit_tests: fix notify unit test when /tmp does not exist --- tests/unit_tests/notify.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/tests/unit_tests/notify.cpp b/tests/unit_tests/notify.cpp index 84861cb4521..4daeeddee2e 100644 --- a/tests/unit_tests/notify.cpp +++ b/tests/unit_tests/notify.cpp @@ -44,7 +44,14 @@ TEST(notify, works) #ifdef __GLIBC__ mode_t prevmode = umask(077); #endif - char name_template[] = "/tmp/monero-notify-unit-test-XXXXXX"; + const char *tmp = getenv("TEMP"); + if (!tmp) + tmp = "/tmp"; + static const char *filename = "monero-notify-unit-test-XXXXXX"; + const size_t len = strlen(tmp) + 1 + strlen(filename); + char *name_template = (char*)malloc(len + 1); + ASSERT_TRUE(name_template != NULL); + snprintf(name_template, len + 1, "%s/%s", tmp, filename); int fd = mkstemp(name_template); #ifdef __GLIBC__ umask(prevmode); @@ -68,4 +75,5 @@ TEST(notify, works) ASSERT_TRUE(s == "1111111111111111111111111111111111111111111111111111111111111111"); boost::filesystem::remove(name_template); + free(name_template); } From 107f33985dcff17cb2bfa7bc448ab4216b0126ce Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 27 Oct 2018 14:34:15 +0000 Subject: [PATCH 0673/1404] wallet2: fix ring reuse breaking when using histogram --- src/wallet/wallet2.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 37b60c5d73f..509c85afa8d 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -7166,6 +7166,9 @@ void wallet2::get_outs(std::vector> break; } } + bool use_histogram = amount != 0 || !has_rct_distribution; + if (!use_histogram) + num_outs = rct_offsets[rct_offsets.size() - CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE]; // make sure the real outputs we asked for are really included, along // with the correct key and mask: this guards against an active attack From f067bb0c8b9d43a5c6bd04ee19674e8c574aea45 Mon Sep 17 00:00:00 2001 From: xiphon Date: Sat, 27 Oct 2018 15:08:52 +0000 Subject: [PATCH 0674/1404] tests: fix MSYS2 warning 'MONERO_DEFAULT_LOG_CATEGORY redefined' --- contrib/epee/include/misc_log_ex.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/contrib/epee/include/misc_log_ex.h b/contrib/epee/include/misc_log_ex.h index 530f8e63681..9100a8db3b2 100644 --- a/contrib/epee/include/misc_log_ex.h +++ b/contrib/epee/include/misc_log_ex.h @@ -32,7 +32,9 @@ #include "easylogging++.h" +#undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "default" + #define MAX_LOG_FILE_SIZE 104850000 // 100 MB - 7600 bytes #define MAX_LOG_FILES 50 From 0a95cdaa803ac06d9fb29aff7f54e4a316cba8c3 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 27 Oct 2018 17:38:03 +0000 Subject: [PATCH 0675/1404] blockchain_utilities: fix logs and cout output colliding --- src/blockchain_utilities/blockchain_import.cpp | 4 ++-- src/blockchain_utilities/bootstrap_file.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/blockchain_utilities/blockchain_import.cpp b/src/blockchain_utilities/blockchain_import.cpp index 7f92ecd87b1..eae078ea256 100644 --- a/src/blockchain_utilities/blockchain_import.cpp +++ b/src/blockchain_utilities/blockchain_import.cpp @@ -396,7 +396,7 @@ int import_from_file(cryptonote::core& core, const std::string& import_file_path { std::cout << refresh_string << "block " << h-1 << " / " << block_stop - << std::flush; + << "\r" << std::flush; std::cout << ENDL << ENDL; MINFO("Specified block number reached - stopping. block: " << h-1 << " total blocks: " << h); quit = 1; @@ -432,7 +432,7 @@ int import_from_file(cryptonote::core& core, const std::string& import_file_path { std::cout << refresh_string << "block " << h-1 << " / " << block_stop - << std::flush; + << "\r" << std::flush; } if (opt_verify) diff --git a/src/blockchain_utilities/bootstrap_file.cpp b/src/blockchain_utilities/bootstrap_file.cpp index beaad2abc20..a8c46d66105 100644 --- a/src/blockchain_utilities/bootstrap_file.cpp +++ b/src/blockchain_utilities/bootstrap_file.cpp @@ -304,7 +304,7 @@ bool BootstrapFile::store_blockchain_raw(Blockchain* _blockchain_storage, tx_mem } if (m_cur_height % progress_interval == 0) { std::cout << refresh_string; - std::cout << "block " << m_cur_height << "/" << block_stop << std::flush; + std::cout << "block " << m_cur_height << "/" << block_stop << "\r" << std::flush; } } // NOTE: use of NUM_BLOCKS_PER_CHUNK is a placeholder in case multi-block chunks are later supported. @@ -479,7 +479,7 @@ uint64_t BootstrapFile::count_blocks(const std::string& import_file_path, std::s bytes_read += count_bytes(import_file, progress_interval, blocks, quit); h += blocks; std::cout << "\r" << "block height: " << h-1 << - " " << + " \r" << std::flush; // std::cout << refresh_string; From 9335d5a2976d97c294c450a9609e32971801e63b Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 27 Oct 2018 17:54:57 +0000 Subject: [PATCH 0676/1404] wallet2: save ring in the ringdb once a tx is created Even if it is never relayed, it ensures a daemon supplying fake outs on demand will never be asked for a set with the real input being the only intersecting member (only a problem with people who trust their privacy to some stranger's node, but it seems to be a massively common thing, even in Monero) --- src/wallet/wallet2.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 37b60c5d73f..28e5f625d73 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -7258,6 +7258,20 @@ void wallet2::get_outs(std::vector> outs.push_back(v); } } + + // save those outs in the ringdb for reuse + for (size_t i = 0; i < selected_transfers.size(); ++i) + { + const size_t idx = selected_transfers[i]; + THROW_WALLET_EXCEPTION_IF(idx >= m_transfers.size(), error::wallet_internal_error, "selected_transfers entry out of range"); + const transfer_details &td = m_transfers[idx]; + std::vector ring; + ring.reserve(outs[i].size()); + for (const auto &e: outs[i]) + ring.push_back(std::get<0>(e)); + if (!set_ring(td.m_key_image, ring, false)) + MERROR("Failed to set ring for " << td.m_key_image); + } } template From b3e8677c7045858f1ab1dea194442aaedd2c13ca Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 27 Oct 2018 18:07:55 +0000 Subject: [PATCH 0677/1404] cryptonote: use logging functions for errors, not std::cout --- src/cryptonote_basic/cryptonote_basic_impl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cryptonote_basic/cryptonote_basic_impl.cpp b/src/cryptonote_basic/cryptonote_basic_impl.cpp index b18ef1c5cbf..c4e10851e4a 100644 --- a/src/cryptonote_basic/cryptonote_basic_impl.cpp +++ b/src/cryptonote_basic/cryptonote_basic_impl.cpp @@ -328,7 +328,7 @@ bool parse_hash256(const std::string str_hash, crypto::hash& hash) bool res = epee::string_tools::parse_hexstr_to_binbuff(str_hash, buf); if (!res || buf.size() != sizeof(crypto::hash)) { - std::cout << "invalid hash format: <" << str_hash << '>' << std::endl; + MERROR("invalid hash format: " << str_hash); return false; } else From dad5bbfdf9597eb699f70c209c286b4c48cb1bd8 Mon Sep 17 00:00:00 2001 From: cryptochangements34 Date: Sun, 28 Oct 2018 09:50:33 -0400 Subject: [PATCH 0678/1404] return message in stop_mining if mining never started --- src/rpc/core_rpc_server.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index aa9d3d64bf2..acce2d5d08f 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -801,7 +801,14 @@ namespace cryptonote bool core_rpc_server::on_stop_mining(const COMMAND_RPC_STOP_MINING::request& req, COMMAND_RPC_STOP_MINING::response& res) { PERF_TIMER(on_stop_mining); - if(!m_core.get_miner().stop()) + cryptonote::miner &miner= m_core.get_miner(); + if(!miner.is_mining()) + { + res.status = "Mining never started"; + LOG_PRINT_L0(res.status); + return true; + } + if(!miner.stop()) { res.status = "Failed, mining not stopped"; LOG_PRINT_L0(res.status); From e86af52e2f0a990a9797c4e4a3a6b4a6df496c02 Mon Sep 17 00:00:00 2001 From: Nathan Dorfman Date: Thu, 25 Oct 2018 18:03:18 -0600 Subject: [PATCH 0679/1404] wallet2: rewrite keys file in a safer manner --- src/wallet/wallet2.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 37b60c5d73f..f0fc94bc5c0 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -3140,13 +3140,22 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable crypto::chacha20(account_data.data(), account_data.size(), key, keys_file_data.iv, &cipher[0]); keys_file_data.account_data = cipher; - unlock_keys_file(); + std::string tmp_file_name = keys_file_name + ".new"; std::string buf; r = ::serialization::dump_binary(keys_file_data, buf); - r = r && epee::file_io_utils::save_string_to_file(keys_file_name, buf); //and never touch wallet_keys_file again, only read - CHECK_AND_ASSERT_MES(r, false, "failed to generate wallet keys file " << keys_file_name); + r = r && epee::file_io_utils::save_string_to_file(tmp_file_name, buf); + CHECK_AND_ASSERT_MES(r, false, "failed to generate wallet keys file " << tmp_file_name); + + unlock_keys_file(); + std::error_code e = tools::replace_file(tmp_file_name, keys_file_name); lock_keys_file(); + if (e) { + boost::filesystem::remove(tmp_file_name); + LOG_ERROR("failed to update wallet keys file " << keys_file_name); + return false; + } + return true; } //---------------------------------------------------------------------------------------------------- From 9c1c92db659c9cc36232bd0123561f327e22d515 Mon Sep 17 00:00:00 2001 From: xiphon Date: Sat, 27 Oct 2018 14:35:33 +0000 Subject: [PATCH 0680/1404] tests: fix MSYS2 gcc 7.3.0 warning -Wtype-limits --- tests/unit_tests/keccak.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit_tests/keccak.cpp b/tests/unit_tests/keccak.cpp index 4276b0e1d84..37da65d7635 100644 --- a/tests/unit_tests/keccak.cpp +++ b/tests/unit_tests/keccak.cpp @@ -37,7 +37,7 @@ extern "C" { #define TEST_KECCAK(sz, chunks) \ std::string data; \ data.resize(sz); \ - for (size_t i = 0; i < sz; ++i) \ + for (size_t i = 0; i < data.size(); ++i) \ data[i] = i * 17; \ uint8_t md0[32], md1[32]; \ keccak((const uint8_t*)data.data(), data.size(), md0, 32); \ From 6789385cd0b98a834e87a4ee5764f217f7ef4271 Mon Sep 17 00:00:00 2001 From: MoroccanMalinois Date: Mon, 29 Oct 2018 09:17:32 +0100 Subject: [PATCH 0681/1404] Dockerfile: add udev, libusb and hidapi build --- Dockerfile | 40 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index cd3e7df70af..86d833a98f0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -18,7 +18,9 @@ RUN set -ex && \ libtool-bin \ autoconf \ automake \ - bzip2 + bzip2 \ + xsltproc \ + gperf WORKDIR /usr/local @@ -109,6 +111,42 @@ RUN set -ex \ && make check \ && make install +# Udev +ARG UDEV_VERSION=v3.2.6 +ARG UDEV_HASH=0c35b136c08d64064efa55087c54364608e65ed6 +RUN set -ex \ + && git clone https://github.com/gentoo/eudev -b ${UDEV_VERSION} \ + && cd eudev \ + && test `git rev-parse HEAD` = ${UDEV_HASH} || exit 1 \ + && ./autogen.sh \ + && CFLAGS="-fPIC" CXXFLAGS="-fPIC" ./configure --disable-gudev --disable-introspection --disable-hwdb --disable-manpages --disable-shared \ + && make \ + && make install + +# Libusb +ARG USB_VERSION=v1.0.22 +ARG USB_HASH=0034b2afdcdb1614e78edaa2a9e22d5936aeae5d +RUN set -ex \ + && git clone https://github.com/libusb/libusb.git -b ${USB_VERSION} \ + && cd libusb \ + && test `git rev-parse HEAD` = ${USB_HASH} || exit 1 \ + && ./autogen.sh \ + && CFLAGS="-fPIC" CXXFLAGS="-fPIC" ./configure --disable-shared \ + && make \ + && make install + +# Hidapi +ARG HIDAPI_VERSION=hidapi-0.8.0-rc1 +ARG HIDAPI_HASH=40cf516139b5b61e30d9403a48db23d8f915f52c +RUN set -ex \ + && git clone https://github.com/signal11/hidapi -b ${HIDAPI_VERSION} \ + && cd hidapi \ + && test `git rev-parse HEAD` = ${HIDAPI_HASH} || exit 1 \ + && ./bootstrap \ + && CFLAGS="-fPIC" CXXFLAGS="-fPIC" ./configure --enable-static --disable-shared \ + && make \ + && make install + WORKDIR /src COPY . . From cbe0122bf18d702e1fb7c383ff3681544bb74005 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 8 Oct 2018 13:09:18 +0000 Subject: [PATCH 0682/1404] wallet2: initialize amount to 0 in tx_scan_info_t ctor It seems the more prudent thing to do here. It will not catch attempts to use that value before it is initialized when using ASAN or valgrind, but in a case where it does, it will have smaller repercussions. So it seems appropriate in this particular case. Coverity 182498 --- src/wallet/wallet2.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 680196f0198..caf77c60f97 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -229,7 +229,7 @@ namespace tools bool error; boost::optional received; - tx_scan_info_t(): money_transfered(0), error(true) {} + tx_scan_info_t(): amount(0), money_transfered(0), error(true) {} }; struct transfer_details From d9400f69eb8ab69470d6f3040e73a255fc3e3b5e Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 29 Oct 2018 16:10:22 +0000 Subject: [PATCH 0683/1404] serializtion: add missing mainnet and stagenet fields for 0mq Coverity 184940 --- src/serialization/json_object.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/serialization/json_object.cpp b/src/serialization/json_object.cpp index 89a1dbd2392..6e8160d2edf 100644 --- a/src/serialization/json_object.cpp +++ b/src/serialization/json_object.cpp @@ -1192,7 +1192,9 @@ void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::DaemonInfo& in INSERT_INTO_JSON_OBJECT(val, doc, incoming_connections_count, info.incoming_connections_count); INSERT_INTO_JSON_OBJECT(val, doc, white_peerlist_size, info.white_peerlist_size); INSERT_INTO_JSON_OBJECT(val, doc, grey_peerlist_size, info.grey_peerlist_size); + INSERT_INTO_JSON_OBJECT(val, doc, mainnet, info.mainnet); INSERT_INTO_JSON_OBJECT(val, doc, testnet, info.testnet); + INSERT_INTO_JSON_OBJECT(val, doc, stagenet, info.stagenet); INSERT_INTO_JSON_OBJECT(val, doc, nettype, info.nettype); INSERT_INTO_JSON_OBJECT(val, doc, top_block_hash, info.top_block_hash); INSERT_INTO_JSON_OBJECT(val, doc, cumulative_difficulty, info.cumulative_difficulty); @@ -1221,7 +1223,9 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::DaemonInfo& inf GET_FROM_JSON_OBJECT(val, info.incoming_connections_count, incoming_connections_count); GET_FROM_JSON_OBJECT(val, info.white_peerlist_size, white_peerlist_size); GET_FROM_JSON_OBJECT(val, info.grey_peerlist_size, grey_peerlist_size); + GET_FROM_JSON_OBJECT(val, info.mainnet, mainnet); GET_FROM_JSON_OBJECT(val, info.testnet, testnet); + GET_FROM_JSON_OBJECT(val, info.stagenet, stagenet); GET_FROM_JSON_OBJECT(val, info.nettype, nettype); GET_FROM_JSON_OBJECT(val, info.top_block_hash, top_block_hash); GET_FROM_JSON_OBJECT(val, info.cumulative_difficulty, cumulative_difficulty); From 2362baf735a55437e9a6b1dc73b4ccaa3fb9305f Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 29 Oct 2018 16:14:18 +0000 Subject: [PATCH 0684/1404] network_throttle: initialize m_last_sample_time in ctor Coverity 136593 --- contrib/epee/src/network_throttle-detail.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/contrib/epee/src/network_throttle-detail.cpp b/contrib/epee/src/network_throttle-detail.cpp index 28c85bb7823..6f727a1cf19 100644 --- a/contrib/epee/src/network_throttle-detail.cpp +++ b/contrib/epee/src/network_throttle-detail.cpp @@ -150,6 +150,7 @@ network_throttle::network_throttle(const std::string &nameshort, const std::stri m_any_packet_yet = false; m_slot_size = 1.0; // hard coded in few places m_target_speed = 16 * 1024; // other defaults are probably defined in the command-line parsing code when this class is used e.g. as main global throttle + m_last_sample_time = 0; } void network_throttle::set_name(const std::string &name) From 157054b8402b8445485c3cdc9ac8ca0237d3b422 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 29 Oct 2018 16:15:22 +0000 Subject: [PATCH 0685/1404] hardfork: initialize current_fork_index in ctor Also order init list to match actual runtime init order Coverity 136605 --- src/cryptonote_basic/hardfork.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/cryptonote_basic/hardfork.cpp b/src/cryptonote_basic/hardfork.cpp index f05b25901e8..87a3949189c 100644 --- a/src/cryptonote_basic/hardfork.cpp +++ b/src/cryptonote_basic/hardfork.cpp @@ -56,12 +56,13 @@ static uint8_t get_block_version(const cryptonote::block &b) HardFork::HardFork(cryptonote::BlockchainDB &db, uint8_t original_version, uint64_t original_version_till_height, time_t forked_time, time_t update_time, uint64_t window_size, uint8_t default_threshold_percent): db(db), - original_version(original_version), - original_version_till_height(original_version_till_height), forked_time(forked_time), update_time(update_time), window_size(window_size), - default_threshold_percent(default_threshold_percent) + default_threshold_percent(default_threshold_percent), + original_version(original_version), + original_version_till_height(original_version_till_height), + current_fork_index(0) { if (window_size == 0) throw "window_size needs to be strictly positive"; From f3019efe1b04cbb835927c4ec349d57d5991fa17 Mon Sep 17 00:00:00 2001 From: Hasan Pekdemir Date: Sun, 28 Oct 2018 03:50:17 +0100 Subject: [PATCH 0686/1404] wallet-rpc: add on_restore_deterministic RPC call. --- src/wallet/wallet_rpc_server.cpp | 194 +++++++++++++++++++ src/wallet/wallet_rpc_server.h | 2 + src/wallet/wallet_rpc_server_commands_defs.h | 39 +++- 3 files changed, 234 insertions(+), 1 deletion(-) diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index 6dbea2e14f7..9c94093a445 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -3089,6 +3089,200 @@ namespace tools } } //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::on_restore_deterministic_wallet(const wallet_rpc::COMMAND_RPC_RESTORE_DETERMINISTIC_WALLET::request &req, wallet_rpc::COMMAND_RPC_RESTORE_DETERMINISTIC_WALLET::response &res, epee::json_rpc::error &er) + { + if (m_wallet_dir.empty()) + { + er.code = WALLET_RPC_ERROR_CODE_NO_WALLET_DIR; + er.message = "No wallet dir configured"; + return false; + } + + // early check for mandatory fields + if (req.filename.empty()) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "field 'filename' is mandatory. Please provide a filename to save the restored wallet to."; + return false; + } + if (req.seed.empty()) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "field 'seed' is mandatory. Please provide a seed you want to restore from."; + return false; + } + + namespace po = boost::program_options; + po::variables_map vm2; + const char *ptr = strchr(req.filename.c_str(), '/'); + #ifdef _WIN32 + if (!ptr) + ptr = strchr(req.filename.c_str(), '\\'); + if (!ptr) + ptr = strchr(req.filename.c_str(), ':'); + #endif + if (ptr) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "Invalid filename"; + return false; + } + std::string wallet_file = m_wallet_dir + "/" + req.filename; + // check if wallet file already exists + if (!wallet_file.empty()) + { + try + { + boost::system::error_code ignored_ec; + THROW_WALLET_EXCEPTION_IF(boost::filesystem::exists(wallet_file, ignored_ec), error::file_exists, wallet_file); + } + catch (const std::exception &e) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "Wallet already exists."; + return false; + } + } + crypto::secret_key recovery_key; + std::string old_language; + + // check the given seed + { + if (!crypto::ElectrumWords::words_to_bytes(req.seed, recovery_key, old_language)) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "Electrum-style word list failed verification"; + return false; + } + } + + // process seed_offset if given + { + if (!req.seed_offset.empty()) + { + recovery_key = cryptonote::decrypt_key(recovery_key, req.seed_offset); + } + } + { + po::options_description desc("dummy"); + const command_line::arg_descriptor arg_password = {"password", "password"}; + const char *argv[4]; + int argc = 3; + argv[0] = "wallet-rpc"; + argv[1] = "--password"; + argv[2] = req.password.c_str(); + argv[3] = NULL; + vm2 = *m_vm; + command_line::add_arg(desc, arg_password); + po::store(po::parse_command_line(argc, argv, desc), vm2); + } + + auto rc = tools::wallet2::make_new(vm2, true, nullptr); + std::unique_ptr wal; + wal = std::move(rc.first); + if (!wal) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "Failed to create wallet"; + return false; + } + + epee::wipeable_string password = rc.second.password(); + + bool was_deprecated_wallet = ((old_language == crypto::ElectrumWords::old_language_name) || + crypto::ElectrumWords::get_is_old_style_seed(req.seed)); + + std::string mnemonic_language = old_language; + if (was_deprecated_wallet) + { + // The user had used an older version of the wallet with old style mnemonics. + res.was_deprecated = true; + } + + if (old_language == crypto::ElectrumWords::old_language_name) + { + if (req.language.empty()) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "Wallet was using the old seed language. You need to specify a new seed language."; + return false; + } + std::vector language_list; + std::vector language_list_en; + crypto::ElectrumWords::get_language_list(language_list); + crypto::ElectrumWords::get_language_list(language_list_en, true); + if (std::find(language_list.begin(), language_list.end(), req.language) == language_list.end() && + std::find(language_list_en.begin(), language_list_en.end(), req.language) == language_list_en.end()) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "Wallet was using the old seed language, and the specified new seed language is invalid."; + return false; + } + mnemonic_language = req.language; + } + + wal->set_seed_language(mnemonic_language); + + crypto::secret_key recovery_val; + try + { + recovery_val = wal->generate(wallet_file, std::move(rc.second).password(), recovery_key, true, false, false); + MINFO("Wallet has been restored.\n"); + } + catch (const std::exception &e) + { + handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR); + return false; + } + + // // Convert the secret key back to seed + epee::wipeable_string electrum_words; + if (!crypto::ElectrumWords::bytes_to_words(recovery_val, electrum_words, mnemonic_language)) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "Failed to encode seed"; + return false; + } + res.seed = electrum_words.data(); + + if (!wal) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "Failed to generate wallet"; + return false; + } + + // set blockheight if given + try + { + wal->set_refresh_from_block_height(req.restore_height); + wal->rewrite(wallet_file, password); + } + catch (const std::exception &e) + { + handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR); + return false; + } + + if (m_wallet) + { + try + { + m_wallet->store(); + } + catch (const std::exception &e) + { + handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR); + return false; + } + delete m_wallet; + } + m_wallet = wal.release(); + res.address = m_wallet->get_account().get_public_address_str(m_wallet->nettype()); + res.info = "Wallet has been restored successfully."; + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_is_multisig(const wallet_rpc::COMMAND_RPC_IS_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_IS_MULTISIG::response& res, epee::json_rpc::error& er) { if (!m_wallet) return not_open(er); diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h index 887723ed554..abbbe82c57d 100644 --- a/src/wallet/wallet_rpc_server.h +++ b/src/wallet/wallet_rpc_server.h @@ -137,6 +137,7 @@ namespace tools MAP_JON_RPC_WE("open_wallet", on_open_wallet, wallet_rpc::COMMAND_RPC_OPEN_WALLET) MAP_JON_RPC_WE("close_wallet", on_close_wallet, wallet_rpc::COMMAND_RPC_CLOSE_WALLET) MAP_JON_RPC_WE("change_wallet_password", on_change_wallet_password, wallet_rpc::COMMAND_RPC_CHANGE_WALLET_PASSWORD) + MAP_JON_RPC_WE("restore_deterministic_wallet", on_restore_deterministic_wallet, wallet_rpc::COMMAND_RPC_RESTORE_DETERMINISTIC_WALLET) MAP_JON_RPC_WE("is_multisig", on_is_multisig, wallet_rpc::COMMAND_RPC_IS_MULTISIG) MAP_JON_RPC_WE("prepare_multisig", on_prepare_multisig, wallet_rpc::COMMAND_RPC_PREPARE_MULTISIG) MAP_JON_RPC_WE("make_multisig", on_make_multisig, wallet_rpc::COMMAND_RPC_MAKE_MULTISIG) @@ -216,6 +217,7 @@ namespace tools bool on_open_wallet(const wallet_rpc::COMMAND_RPC_OPEN_WALLET::request& req, wallet_rpc::COMMAND_RPC_OPEN_WALLET::response& res, epee::json_rpc::error& er); bool on_close_wallet(const wallet_rpc::COMMAND_RPC_CLOSE_WALLET::request& req, wallet_rpc::COMMAND_RPC_CLOSE_WALLET::response& res, epee::json_rpc::error& er); bool on_change_wallet_password(const wallet_rpc::COMMAND_RPC_CHANGE_WALLET_PASSWORD::request& req, wallet_rpc::COMMAND_RPC_CHANGE_WALLET_PASSWORD::response& res, epee::json_rpc::error& er); + bool on_restore_deterministic_wallet(const wallet_rpc::COMMAND_RPC_RESTORE_DETERMINISTIC_WALLET::request& req, wallet_rpc::COMMAND_RPC_RESTORE_DETERMINISTIC_WALLET::response& res, epee::json_rpc::error& er); bool on_is_multisig(const wallet_rpc::COMMAND_RPC_IS_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_IS_MULTISIG::response& res, epee::json_rpc::error& er); bool on_prepare_multisig(const wallet_rpc::COMMAND_RPC_PREPARE_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_PREPARE_MULTISIG::response& res, epee::json_rpc::error& er); bool on_make_multisig(const wallet_rpc::COMMAND_RPC_MAKE_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_MAKE_MULTISIG::response& res, epee::json_rpc::error& er); diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h index 924f3a0f176..b389c5e83b9 100644 --- a/src/wallet/wallet_rpc_server_commands_defs.h +++ b/src/wallet/wallet_rpc_server_commands_defs.h @@ -47,7 +47,7 @@ // advance which version they will stop working with // Don't go over 32767 for any of these #define WALLET_RPC_VERSION_MAJOR 1 -#define WALLET_RPC_VERSION_MINOR 5 +#define WALLET_RPC_VERSION_MINOR 6 #define MAKE_WALLET_RPC_VERSION(major,minor) (((major)<<16)|(minor)) #define WALLET_RPC_VERSION MAKE_WALLET_RPC_VERSION(WALLET_RPC_VERSION_MAJOR, WALLET_RPC_VERSION_MINOR) namespace tools @@ -1924,6 +1924,43 @@ namespace wallet_rpc }; }; + struct COMMAND_RPC_RESTORE_DETERMINISTIC_WALLET + { + struct request + { + uint64_t restore_height; + std::string filename; + std::string seed; + std::string seed_offset; + std::string password; + std::string language; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_OPT(restore_height, (uint64_t)0) + KV_SERIALIZE(filename) + KV_SERIALIZE(seed) + KV_SERIALIZE(seed_offset) + KV_SERIALIZE(password) + KV_SERIALIZE(language) + END_KV_SERIALIZE_MAP() + }; + + struct response + { + std::string address; + std::string seed; + std::string info; + bool was_deprecated; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(address) + KV_SERIALIZE(seed) + KV_SERIALIZE(info) + KV_SERIALIZE(was_deprecated) + END_KV_SERIALIZE_MAP() + }; + }; + struct COMMAND_RPC_IS_MULTISIG { struct request From aa40047e480c6668a7d2067a5e84230ab551aeec Mon Sep 17 00:00:00 2001 From: Neofito89 <37967500+Neofito89@users.noreply.github.com> Date: Mon, 29 Oct 2018 23:14:25 +0100 Subject: [PATCH 0687/1404] README.md: increase swap size on Raspberry Pi Increased swap size on raspberry pi compiling guide, since it might crash if the raspberry has any service runing in the background --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index be7dc8b2d08..bd332ebc890 100644 --- a/README.md +++ b/README.md @@ -236,7 +236,7 @@ Tested on a Raspberry Pi Zero with a clean install of minimal Raspbian Stretch ( ``` sudo /etc/init.d/dphys-swapfile stop sudo nano /etc/dphys-swapfile - CONF_SWAPSIZE=1024 + CONF_SWAPSIZE=2048 sudo /etc/init.d/dphys-swapfile start ``` * If using an external hard disk without an external power supply, ensure it gets enough power to avoid hardware issues when syncing, by adding the line "max_usb_current=1" to /boot/config.txt @@ -270,7 +270,7 @@ If you are using the older Raspbian Jessie image, compiling Monero is a bit more ``` sudo /etc/init.d/dphys-swapfile stop sudo nano /etc/dphys-swapfile - CONF_SWAPSIZE=1024 + CONF_SWAPSIZE=2048 sudo /etc/init.d/dphys-swapfile start ``` From db24a2e50934010ca0638c28920645365d1b8130 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 25 Oct 2018 21:15:19 +0000 Subject: [PATCH 0688/1404] hash: fix hash_permutation on big endian --- src/crypto/hash.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/crypto/hash.c b/src/crypto/hash.c index 42f272e3473..43ce32957f4 100644 --- a/src/crypto/hash.c +++ b/src/crypto/hash.c @@ -36,7 +36,14 @@ #include "keccak.h" void hash_permutation(union hash_state *state) { +#if BYTE_ORDER == LITTLE_ENDIAN keccakf((uint64_t*)state, 24); +#else + uint64_t le_state[25]; + memcpy_swap64le(le_state, state, 25); + keccakf(le_state, 24); + memcpy_swap64le(state, le_state, 25); +#endif } void hash_process(union hash_state *state, const uint8_t *buf, size_t count) { From 8a7e20f8a5b7984355a169fdbc885500f87a0783 Mon Sep 17 00:00:00 2001 From: xiphon Date: Tue, 30 Oct 2018 05:04:57 +0000 Subject: [PATCH 0689/1404] build: older git versions don't have '-C' flag --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3a685ee8e0a..dd5561f6078 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -176,8 +176,8 @@ if(NOT MANUAL_SUBMODULES) find_package(Git) if(GIT_FOUND) function (check_submodule relative_path) - execute_process(COMMAND git -C ${relative_path} rev-parse "HEAD" WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} OUTPUT_VARIABLE localHead) - execute_process(COMMAND git rev-parse "HEAD:${relative_path}" WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} OUTPUT_VARIABLE checkedHead) + execute_process(COMMAND git rev-parse "HEAD" WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/${relative_path} OUTPUT_VARIABLE localHead) + execute_process(COMMAND git rev-parse "HEAD:${relative_path}" WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} OUTPUT_VARIABLE checkedHead) string(COMPARE EQUAL "${localHead}" "${checkedHead}" upToDate) if (upToDate) message(STATUS "Submodule '${relative_path}' is up-to-date") From 42d8d5085e96732e96fcb39f4b851bc85f4e4a8b Mon Sep 17 00:00:00 2001 From: xiphon Date: Wed, 31 Oct 2018 03:13:55 +0000 Subject: [PATCH 0690/1404] crypto: fix AES encryption on big endian --- src/crypto/aesb.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/crypto/aesb.c b/src/crypto/aesb.c index 5d57b8af4bb..8a22a4b9386 100644 --- a/src/crypto/aesb.c +++ b/src/crypto/aesb.c @@ -19,6 +19,7 @@ Issue Date: 20/12/2007 */ #include +#include "common/int-util.h" #if defined(__cplusplus) extern "C" @@ -50,7 +51,7 @@ extern "C" #define state_out(y,x) so(y,x,0); so(y,x,1); so(y,x,2); so(y,x,3) #define round(rm,y,x,k) rm(y,x,k,0); rm(y,x,k,1); rm(y,x,k,2); rm(y,x,k,3) #define to_byte(x) ((x) & 0xff) -#define bval(x,n) to_byte((x) >> (8 * (n))) +#define bval(x,n) to_byte(SWAP32LE(x) >> (8 * (n))) #define fwd_var(x,r,c)\ ( r == 0 ? ( c == 0 ? s(x,0) : c == 1 ? s(x,1) : c == 2 ? s(x,2) : s(x,3))\ @@ -58,7 +59,7 @@ extern "C" : r == 2 ? ( c == 0 ? s(x,2) : c == 1 ? s(x,3) : c == 2 ? s(x,0) : s(x,1))\ : ( c == 0 ? s(x,3) : c == 1 ? s(x,0) : c == 2 ? s(x,1) : s(x,2))) -#define fwd_rnd(y,x,k,c) (s(y,c) = (k)[c] ^ four_tables(x,t_use(f,n),fwd_var,rf1,c)) +#define fwd_rnd(y,x,k,c) (s(y,c) = (k)[c] ^ SWAP32LE(four_tables(x,t_use(f,n),fwd_var,rf1,c))) #define sb_data(w) {\ w(0x63), w(0x7c), w(0x77), w(0x7b), w(0xf2), w(0x6b), w(0x6f), w(0xc5),\ From 1598f01caf72eb9a8fd93f21213b5ac2671a7f0e Mon Sep 17 00:00:00 2001 From: stoffu Date: Wed, 31 Oct 2018 20:00:57 +0900 Subject: [PATCH 0691/1404] wallet2: use padded bulletproofs for multisig signing Analogous to #4540 --- src/wallet/wallet2.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 37b60c5d73f..6ec67c2e1fa 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -5997,10 +5997,7 @@ bool wallet2::sign_multisig_tx(multisig_tx_set &exported_txs, std::vector 1) - range_proof_type = rct::RangeProofPaddedBulletproof; + range_proof_type = rct::RangeProofPaddedBulletproof; } bool r = cryptonote::construct_tx_with_tx_key(m_account.get_keys(), m_subaddresses, sources, sd.splitted_dsts, ptx.change_dts.addr, sd.extra, tx, sd.unlock_time, ptx.tx_key, ptx.additional_tx_keys, sd.use_rct, range_proof_type, &msout, false); THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sd.sources, sd.splitted_dsts, sd.unlock_time, m_nettype); From 09dbd9cbbc3d04587569f26740f875808f2a3dae Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 31 Oct 2018 12:14:30 +0000 Subject: [PATCH 0692/1404] tx_pool: fix comment about transaction_prefix It was leftover from a change that was undone before commit, but the comment change was let through --- src/cryptonote_core/tx_pool.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h index 7a0cc23bf17..be00f8fc10a 100644 --- a/src/cryptonote_core/tx_pool.h +++ b/src/cryptonote_core/tx_pool.h @@ -510,7 +510,7 @@ namespace cryptonote * @param txd the transaction to check (and info about it) * @param txid the txid of the transaction to check * @param txblob the transaction blob to check - * @param tx the parsed transaction prefix, if successful + * @param tx the parsed transaction, if successful * * @return true if the transaction is good to go, otherwise false */ From cf552c752d3ddc2a7c7fcc4bfab756b794a16865 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 31 Oct 2018 14:43:13 +0000 Subject: [PATCH 0693/1404] tx_pool: allow take_tx to work without m_txs_by_fee_and_receive_time This should make it possible to have two daemons running on the same database again. --- src/cryptonote_core/tx_pool.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index 553a22298e4..86ce83346e1 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -454,8 +454,6 @@ namespace cryptonote CRITICAL_REGION_LOCAL1(m_blockchain); auto sorted_it = find_tx_in_sorted_container(id); - if (sorted_it == m_txs_by_fee_and_receive_time.end()) - return false; try { @@ -489,7 +487,8 @@ namespace cryptonote return false; } - m_txs_by_fee_and_receive_time.erase(sorted_it); + if (sorted_it != m_txs_by_fee_and_receive_time.end()) + m_txs_by_fee_and_receive_time.erase(sorted_it); ++m_cookie; return true; } From 2bd46065ae894d9de026548bc42ec9e4b8f97f89 Mon Sep 17 00:00:00 2001 From: RaskaRuby Date: Wed, 31 Oct 2018 14:47:20 -0700 Subject: [PATCH 0694/1404] Expose limit-rate defaults from command line help --- src/cryptonote_config.h | 2 ++ src/p2p/net_node.cpp | 4 ++-- src/p2p/net_node.h | 4 ++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index c62eeb73828..496678b5eaf 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -113,6 +113,8 @@ #define P2P_DEFAULT_HANDSHAKE_INVOKE_TIMEOUT 5000 //5 seconds #define P2P_DEFAULT_WHITELIST_CONNECTIONS_PERCENT 70 #define P2P_DEFAULT_ANCHOR_CONNECTIONS_COUNT 2 +#define P2P_DEFAULT_LIMIT_RATE_UP 2048 // kB/s +#define P2P_DEFAULT_LIMIT_RATE_DOWN 8192 // kB/s #define P2P_FAILED_ADDR_FORGET_SECONDS (60*60) //1 hour #define P2P_IP_BLOCKTIME (60*60*24) //24 hour diff --git a/src/p2p/net_node.cpp b/src/p2p/net_node.cpp index e9d2061e864..7cad6e077b7 100644 --- a/src/p2p/net_node.cpp +++ b/src/p2p/net_node.cpp @@ -62,8 +62,8 @@ namespace nodetool const command_line::arg_descriptor arg_in_peers = {"in-peers", "set max number of in peers", -1}; const command_line::arg_descriptor arg_tos_flag = {"tos-flag", "set TOS flag", -1}; - const command_line::arg_descriptor arg_limit_rate_up = {"limit-rate-up", "set limit-rate-up [kB/s]", -1}; - const command_line::arg_descriptor arg_limit_rate_down = {"limit-rate-down", "set limit-rate-down [kB/s]", -1}; + const command_line::arg_descriptor arg_limit_rate_up = {"limit-rate-up", "set limit-rate-up [kB/s]", P2P_DEFAULT_LIMIT_RATE_UP}; + const command_line::arg_descriptor arg_limit_rate_down = {"limit-rate-down", "set limit-rate-down [kB/s]", P2P_DEFAULT_LIMIT_RATE_DOWN}; const command_line::arg_descriptor arg_limit_rate = {"limit-rate", "set limit-rate [kB/s]", -1}; const command_line::arg_descriptor arg_save_graph = {"save-graph", "Save data for dr monero", false}; diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h index 90e2f78b1a1..1c348cac462 100644 --- a/src/p2p/net_node.h +++ b/src/p2p/net_node.h @@ -337,8 +337,8 @@ namespace nodetool cryptonote::network_type m_nettype; }; - const int64_t default_limit_up = 2048; // kB/s - const int64_t default_limit_down = 8192; // kB/s + const int64_t default_limit_up = P2P_DEFAULT_LIMIT_RATE_UP; // kB/s + const int64_t default_limit_down = P2P_DEFAULT_LIMIT_RATE_DOWN; // kB/s extern const command_line::arg_descriptor arg_p2p_bind_ip; extern const command_line::arg_descriptor arg_p2p_bind_port; extern const command_line::arg_descriptor arg_p2p_external_port; From 00cc1a1657be9b0d726780cdd541631d1b239da0 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 24 Oct 2018 21:54:19 +0000 Subject: [PATCH 0695/1404] unit_tests: notify test special case for the usual weirdo --- tests/unit_tests/notify.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/unit_tests/notify.cpp b/tests/unit_tests/notify.cpp index e2477a76e7e..84861cb4521 100644 --- a/tests/unit_tests/notify.cpp +++ b/tests/unit_tests/notify.cpp @@ -52,7 +52,11 @@ TEST(notify, works) ASSERT_TRUE(fd >= 0); close(fd); - const std::string spec = epee::string_tools::get_current_module_folder() + "/test_notifier " + name_template + " %s"; + const std::string spec = epee::string_tools::get_current_module_folder() + "/test_notifier" +#ifdef _WIN32 + + ".exe" +#endif + + " " + name_template + " %s"; tools::Notify notify(spec.c_str()); notify.notify("1111111111111111111111111111111111111111111111111111111111111111"); From 6ecc99ad1fd0f2b86641669077130394ab21e71c Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 31 Oct 2018 08:31:13 +0000 Subject: [PATCH 0696/1404] core: avoid unnecessary tx/blob conversions --- src/blockchain_db/blockchain_db.h | 2 +- src/blockchain_db/lmdb/db_lmdb.cpp | 6 ++---- src/blockchain_db/lmdb/db_lmdb.h | 2 +- src/cryptonote_core/blockchain.cpp | 4 ++-- src/cryptonote_core/blockchain.h | 2 +- src/cryptonote_core/cryptonote_core.cpp | 8 ++++---- src/cryptonote_core/cryptonote_core.h | 3 ++- src/cryptonote_core/tx_pool.cpp | 12 +++++++----- src/cryptonote_core/tx_pool.h | 2 +- tests/unit_tests/hardfork.cpp | 2 +- 10 files changed, 22 insertions(+), 21 deletions(-) diff --git a/src/blockchain_db/blockchain_db.h b/src/blockchain_db/blockchain_db.h index 71c46d76b33..7118b08810d 100644 --- a/src/blockchain_db/blockchain_db.h +++ b/src/blockchain_db/blockchain_db.h @@ -1349,7 +1349,7 @@ class BlockchainDB * * @param details the details of the transaction to add */ - virtual void add_txpool_tx(const transaction &tx, const txpool_tx_meta_t& details) = 0; + virtual void add_txpool_tx(const crypto::hash &txid, const cryptonote::blobdata &blob, const txpool_tx_meta_t& details) = 0; /** * @brief update a txpool transaction's metadata diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp index bd91f308ac5..b1248c49264 100644 --- a/src/blockchain_db/lmdb/db_lmdb.cpp +++ b/src/blockchain_db/lmdb/db_lmdb.cpp @@ -1591,7 +1591,7 @@ void BlockchainLMDB::unlock() auto_txn.commit(); \ } while(0) -void BlockchainLMDB::add_txpool_tx(const transaction &tx, const txpool_tx_meta_t &meta) +void BlockchainLMDB::add_txpool_tx(const crypto::hash &txid, const cryptonote::blobdata &blob, const txpool_tx_meta_t &meta) { LOG_PRINT_L3("BlockchainLMDB::" << __func__); check_open(); @@ -1600,8 +1600,6 @@ void BlockchainLMDB::add_txpool_tx(const transaction &tx, const txpool_tx_meta_t CURSOR(txpool_meta) CURSOR(txpool_blob) - const crypto::hash txid = get_transaction_hash(tx); - MDB_val k = {sizeof(txid), (void *)&txid}; MDB_val v = {sizeof(meta), (void *)&meta}; if (auto result = mdb_cursor_put(m_cur_txpool_meta, &k, &v, MDB_NODUPDATA)) { @@ -1610,7 +1608,7 @@ void BlockchainLMDB::add_txpool_tx(const transaction &tx, const txpool_tx_meta_t else throw1(DB_ERROR(lmdb_error("Error adding txpool tx metadata to db transaction: ", result).c_str())); } - MDB_val_copy blob_val(tx_to_blob(tx)); + MDB_val_copy blob_val(blob); if (auto result = mdb_cursor_put(m_cur_txpool_blob, &k, &blob_val, MDB_NODUPDATA)) { if (result == MDB_KEYEXIST) throw1(DB_ERROR("Attempting to add txpool tx blob that's already in the db")); diff --git a/src/blockchain_db/lmdb/db_lmdb.h b/src/blockchain_db/lmdb/db_lmdb.h index e1f748ed8d2..7e76236a5c0 100644 --- a/src/blockchain_db/lmdb/db_lmdb.h +++ b/src/blockchain_db/lmdb/db_lmdb.h @@ -256,7 +256,7 @@ class BlockchainLMDB : public BlockchainDB virtual bool has_key_image(const crypto::key_image& img) const; - virtual void add_txpool_tx(const transaction &tx, const txpool_tx_meta_t& meta); + virtual void add_txpool_tx(const crypto::hash &txid, const cryptonote::blobdata &blob, const txpool_tx_meta_t& meta); virtual void update_txpool_tx(const crypto::hash &txid, const txpool_tx_meta_t& meta); virtual uint64_t get_txpool_tx_count(bool include_unrelayed_txes = true) const; virtual bool txpool_has_tx(const crypto::hash &txid) const; diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 77b6d0b6968..7576e0ed7b1 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -4254,9 +4254,9 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vectoradd_txpool_tx(tx, meta); + m_db->add_txpool_tx(txid, blob, meta); } void Blockchain::update_txpool_tx(const crypto::hash &txid, const txpool_tx_meta_t &meta) diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index f140d77194c..9639d4e7db0 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -939,7 +939,7 @@ namespace cryptonote */ std::list>> get_alternative_chains() const; - void add_txpool_tx(transaction &tx, const txpool_tx_meta_t &meta); + void add_txpool_tx(const crypto::hash &txid, const cryptonote::blobdata &blob, const txpool_tx_meta_t &meta); void update_txpool_tx(const crypto::hash &txid, const txpool_tx_meta_t &meta); void remove_txpool_tx(const crypto::hash &txid); uint64_t get_txpool_tx_count(bool include_unrelayed_txes = true) const; diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index d8c38bf9eac..343be055787 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -909,7 +909,7 @@ namespace cryptonote } const size_t weight = get_transaction_weight(results[i].tx, it->size()); - ok &= add_new_tx(results[i].tx, results[i].hash, results[i].prefix_hash, weight, tvc[i], keeped_by_block, relayed, do_not_relay); + ok &= add_new_tx(results[i].tx, results[i].hash, tx_blobs[i], results[i].prefix_hash, weight, tvc[i], keeped_by_block, relayed, do_not_relay); if(tvc[i].m_verifivation_failed) {MERROR_VER("Transaction verification failed: " << results[i].hash);} else if(tvc[i].m_verifivation_impossible) @@ -1127,7 +1127,7 @@ namespace cryptonote blobdata bl; t_serializable_object_to_blob(tx, bl); size_t tx_weight = get_transaction_weight(tx, bl.size()); - return add_new_tx(tx, tx_hash, tx_prefix_hash, tx_weight, tvc, keeped_by_block, relayed, do_not_relay); + return add_new_tx(tx, tx_hash, bl, tx_prefix_hash, tx_weight, tvc, keeped_by_block, relayed, do_not_relay); } //----------------------------------------------------------------------------------------------- size_t core::get_blockchain_total_transactions() const @@ -1135,7 +1135,7 @@ namespace cryptonote return m_blockchain_storage.get_total_transactions(); } //----------------------------------------------------------------------------------------------- - bool core::add_new_tx(transaction& tx, const crypto::hash& tx_hash, const crypto::hash& tx_prefix_hash, size_t tx_weight, tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay) + bool core::add_new_tx(transaction& tx, const crypto::hash& tx_hash, const cryptonote::blobdata &blob, const crypto::hash& tx_prefix_hash, size_t tx_weight, tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay) { if (keeped_by_block) get_blockchain_storage().on_new_tx_from_block(tx); @@ -1153,7 +1153,7 @@ namespace cryptonote } uint8_t version = m_blockchain_storage.get_current_hard_fork_version(); - return m_mempool.add_tx(tx, tx_hash, tx_weight, tvc, keeped_by_block, relayed, do_not_relay, version); + return m_mempool.add_tx(tx, tx_hash, blob, tx_weight, tvc, keeped_by_block, relayed, do_not_relay, version); } //----------------------------------------------------------------------------------------------- bool core::relay_txpool_transactions() diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index 80c452f53c4..85efc0640d6 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -783,13 +783,14 @@ namespace cryptonote * @copydoc add_new_tx(transaction&, tx_verification_context&, bool) * * @param tx_hash the transaction's hash + * @param blob the transaction as a blob * @param tx_prefix_hash the transaction prefix' hash * @param tx_weight the weight of the transaction * @param relayed whether or not the transaction was relayed to us * @param do_not_relay whether to prevent the transaction from being relayed * */ - bool add_new_tx(transaction& tx, const crypto::hash& tx_hash, const crypto::hash& tx_prefix_hash, size_t tx_weight, tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay); + bool add_new_tx(transaction& tx, const crypto::hash& tx_hash, const cryptonote::blobdata &blob, const crypto::hash& tx_prefix_hash, size_t tx_weight, tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay); /** * @brief add a new transaction to the transaction pool diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index 553a22298e4..d51c8459477 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -111,7 +111,7 @@ namespace cryptonote } //--------------------------------------------------------------------------------- - bool tx_memory_pool::add_tx(transaction &tx, /*const crypto::hash& tx_prefix_hash,*/ const crypto::hash &id, size_t tx_weight, tx_verification_context& tvc, bool kept_by_block, bool relayed, bool do_not_relay, uint8_t version) + bool tx_memory_pool::add_tx(transaction &tx, /*const crypto::hash& tx_prefix_hash,*/ const crypto::hash &id, const cryptonote::blobdata &blob, size_t tx_weight, tx_verification_context& tvc, bool kept_by_block, bool relayed, bool do_not_relay, uint8_t version) { // this should already be called with that lock, but let's make it explicit for clarity CRITICAL_REGION_LOCAL(m_transactions_lock); @@ -249,7 +249,7 @@ namespace cryptonote { CRITICAL_REGION_LOCAL1(m_blockchain); LockedTXN lock(m_blockchain); - m_blockchain.add_txpool_tx(tx, meta); + m_blockchain.add_txpool_tx(id, blob, meta); if (!insert_key_images(tx, id, kept_by_block)) return false; m_txs_by_fee_and_receive_time.emplace(std::pair(fee / (double)tx_weight, receive_time), id); @@ -292,7 +292,7 @@ namespace cryptonote LockedTXN lock(m_blockchain); const crypto::hash txid = get_transaction_hash(tx); m_blockchain.remove_txpool_tx(txid); - m_blockchain.add_txpool_tx(tx, meta); + m_blockchain.add_txpool_tx(txid, blob, meta); if (!insert_key_images(tx, txid, kept_by_block)) return false; m_txs_by_fee_and_receive_time.emplace(std::pair(fee / (double)tx_weight, receive_time), id); @@ -324,9 +324,11 @@ namespace cryptonote { crypto::hash h = null_hash; size_t blob_size = 0; - if (!get_transaction_hash(tx, h, blob_size) || blob_size == 0) + cryptonote::blobdata bl; + t_serializable_object_to_blob(tx, bl); + if (bl.size() == 0 || !get_transaction_hash(tx, h)) return false; - return add_tx(tx, h, get_transaction_weight(tx, blob_size), tvc, keeped_by_block, relayed, do_not_relay, version); + return add_tx(tx, h, bl, get_transaction_weight(tx, bl.size()), tvc, keeped_by_block, relayed, do_not_relay, version); } //--------------------------------------------------------------------------------- size_t tx_memory_pool::get_txpool_weight() const diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h index 7a0cc23bf17..09d134d7b71 100644 --- a/src/cryptonote_core/tx_pool.h +++ b/src/cryptonote_core/tx_pool.h @@ -107,7 +107,7 @@ namespace cryptonote * @param id the transaction's hash * @param tx_weight the transaction's weight */ - bool add_tx(transaction &tx, const crypto::hash &id, size_t tx_weight, tx_verification_context& tvc, bool kept_by_block, bool relayed, bool do_not_relay, uint8_t version); + bool add_tx(transaction &tx, const crypto::hash &id, const cryptonote::blobdata &blob, size_t tx_weight, tx_verification_context& tvc, bool kept_by_block, bool relayed, bool do_not_relay, uint8_t version); /** * @brief add a transaction to the transaction pool diff --git a/tests/unit_tests/hardfork.cpp b/tests/unit_tests/hardfork.cpp index 47177db1c6b..fc488bb1489 100644 --- a/tests/unit_tests/hardfork.cpp +++ b/tests/unit_tests/hardfork.cpp @@ -118,7 +118,7 @@ class TestDB: public BlockchainDB { virtual std::map> get_output_histogram(const std::vector &amounts, bool unlocked, uint64_t recent_cutoff, uint64_t min_count) const { return std::map>(); } virtual bool get_output_distribution(uint64_t amount, uint64_t from_height, uint64_t to_height, std::vector &distribution, uint64_t &base) const { return false; } - virtual void add_txpool_tx(const transaction &tx, const txpool_tx_meta_t& details) {} + virtual void add_txpool_tx(const crypto::hash &txid, const cryptonote::blobdata &blob, const txpool_tx_meta_t& details) {} virtual void update_txpool_tx(const crypto::hash &txid, const txpool_tx_meta_t& details) {} virtual uint64_t get_txpool_tx_count(bool include_unrelayed_txes = true) const { return 0; } virtual bool txpool_has_tx(const crypto::hash &txid) const { return false; } From 593ef5981d0336bbe745936b166cd24ec754cac4 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 31 Oct 2018 13:36:34 +0000 Subject: [PATCH 0697/1404] perf_timer: call reserve on new timer array to avoid reallocations in the vast majority of the time --- src/common/perf_timer.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/common/perf_timer.cpp b/src/common/perf_timer.cpp index 6910ebdd4f7..c5503416fe6 100644 --- a/src/common/perf_timer.cpp +++ b/src/common/perf_timer.cpp @@ -110,6 +110,7 @@ LoggingPerformanceTimer::LoggingPerformanceTimer(const std::string &s, uint64_t { MLOG(level, "PERF ----------"); performance_timers = new std::vector(); + performance_timers->reserve(16); // how deep before realloc } else { From 4f005a77c24d91f9f292530d928913263553fcc2 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 31 Oct 2018 13:51:48 +0000 Subject: [PATCH 0698/1404] tx_pool: remove unnecessary get_transaction_hash --- src/cryptonote_core/tx_pool.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index d51c8459477..a31c9d3ed47 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -290,10 +290,9 @@ namespace cryptonote { CRITICAL_REGION_LOCAL1(m_blockchain); LockedTXN lock(m_blockchain); - const crypto::hash txid = get_transaction_hash(tx); - m_blockchain.remove_txpool_tx(txid); - m_blockchain.add_txpool_tx(txid, blob, meta); - if (!insert_key_images(tx, txid, kept_by_block)) + m_blockchain.remove_txpool_tx(id); + m_blockchain.add_txpool_tx(id, blob, meta); + if (!insert_key_images(tx, id, kept_by_block)) return false; m_txs_by_fee_and_receive_time.emplace(std::pair(fee / (double)tx_weight, receive_time), id); } From bf31447e9bd9e424538abc8f4cb57bd133f439ed Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 31 Oct 2018 13:52:16 +0000 Subject: [PATCH 0699/1404] tx_pool: speed up take_tx for transactions from blocks This happens for every historical tx when syncing, and the unnecessary parsing is actually showing up on profile. Since these are kept cached for just one block, this does not increase memory usage after syncing. --- src/cryptonote_core/tx_pool.cpp | 13 ++++++++++++- src/cryptonote_core/tx_pool.h | 2 ++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index a31c9d3ed47..19467aec70a 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -247,6 +247,8 @@ namespace cryptonote memset(meta.padding, 0, sizeof(meta.padding)); try { + if (kept_by_block) + m_parsed_tx_cache.insert(std::make_pair(id, tx)); CRITICAL_REGION_LOCAL1(m_blockchain); LockedTXN lock(m_blockchain); m_blockchain.add_txpool_tx(id, blob, meta); @@ -288,6 +290,8 @@ namespace cryptonote try { + if (kept_by_block) + m_parsed_tx_cache.insert(std::make_pair(id, tx)); CRITICAL_REGION_LOCAL1(m_blockchain); LockedTXN lock(m_blockchain); m_blockchain.remove_txpool_tx(id); @@ -468,7 +472,12 @@ namespace cryptonote return false; } cryptonote::blobdata txblob = m_blockchain.get_txpool_tx_blob(id); - if (!parse_and_validate_tx_from_blob(txblob, tx)) + auto ci = m_parsed_tx_cache.find(id); + if (ci != m_parsed_tx_cache.end()) + { + tx = ci->second; + } + else if (!parse_and_validate_tx_from_blob(txblob, tx)) { MERROR("Failed to parse tx from txpool"); return false; @@ -911,6 +920,7 @@ namespace cryptonote { CRITICAL_REGION_LOCAL(m_transactions_lock); m_input_cache.clear(); + m_parsed_tx_cache.clear(); return true; } //--------------------------------------------------------------------------------- @@ -918,6 +928,7 @@ namespace cryptonote { CRITICAL_REGION_LOCAL(m_transactions_lock); m_input_cache.clear(); + m_parsed_tx_cache.clear(); return true; } //--------------------------------------------------------------------------------- diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h index 09d134d7b71..6270a30bfdd 100644 --- a/src/cryptonote_core/tx_pool.h +++ b/src/cryptonote_core/tx_pool.h @@ -584,6 +584,8 @@ namespace cryptonote size_t m_txpool_weight; mutable std::unordered_map> m_input_cache; + + std::unordered_map m_parsed_tx_cache; }; } From 741e4a11729db74f52fc4b913833b07dd67eb9b3 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 1 Nov 2018 13:21:35 +0000 Subject: [PATCH 0700/1404] epee: speed up json number parsing --- .../storages/portable_storage_from_json.h | 52 +++++++++++++++---- 1 file changed, 41 insertions(+), 11 deletions(-) diff --git a/contrib/epee/include/storages/portable_storage_from_json.h b/contrib/epee/include/storages/portable_storage_from_json.h index 5b2eafa9a1b..0307b732c52 100644 --- a/contrib/epee/include/storages/portable_storage_from_json.h +++ b/contrib/epee/include/storages/portable_storage_from_json.h @@ -125,16 +125,22 @@ namespace epee { if(is_signed) { - int64_t nval = boost::lexical_cast(val); + errno = 0; + int64_t nval = strtoll(val.c_str(), NULL, 10); + if (errno) throw std::runtime_error("Invalid number: " + val); stg.set_value(name, nval, current_section); }else { - uint64_t nval = boost::lexical_cast(val); + errno = 0; + uint64_t nval = strtoull(val.c_str(), NULL, 10); + if (errno) throw std::runtime_error("Invalid number: " + val); stg.set_value(name, nval, current_section); } }else { - double nval = boost::lexical_cast(val); + errno = 0; + double nval = strtod(val.c_str(), NULL); + if (errno) throw std::runtime_error("Invalid number: " + val); stg.set_value(name, nval, current_section); } state = match_state_wonder_after_value; @@ -208,12 +214,25 @@ namespace epee match_number2(it, buf_end, val, is_v_float, is_signed_val); if(!is_v_float) { - int64_t nval = boost::lexical_cast(val);//bool res = string_tools::string_to_num_fast(val, nval); - h_array = stg.insert_first_value(name, nval, current_section); + if (is_signed_val) + { + errno = 0; + int64_t nval = strtoll(val.c_str(), NULL, 10); + if (errno) throw std::runtime_error("Invalid number: " + val); + h_array = stg.insert_first_value(name, nval, current_section); + }else + { + errno = 0; + uint64_t nval = strtoull(val.c_str(), NULL, 10); + if (errno) throw std::runtime_error("Invalid number: " + val); + h_array = stg.insert_first_value(name, nval, current_section); + } CHECK_AND_ASSERT_THROW_MES(h_array, " failed to insert values section entry"); }else { - double nval = boost::lexical_cast(val);//bool res = string_tools::string_to_num_fast(val, nval); + errno = 0; + double nval = strtod(val.c_str(), NULL); + if (errno) throw std::runtime_error("Invalid number: " + val); h_array = stg.insert_first_value(name, nval, current_section); CHECK_AND_ASSERT_THROW_MES(h_array, " failed to insert values section entry"); } @@ -286,13 +305,24 @@ namespace epee bool insert_res = false; if(!is_v_float) { - int64_t nval = boost::lexical_cast(val); //bool res = string_tools::string_to_num_fast(val, nval); - insert_res = stg.insert_next_value(h_array, nval); - + if (is_signed_val) + { + errno = 0; + int64_t nval = strtoll(val.c_str(), NULL, 10); + if (errno) throw std::runtime_error("Invalid number: " + val); + insert_res = stg.insert_next_value(h_array, nval); + }else + { + errno = 0; + uint64_t nval = strtoull(val.c_str(), NULL, 10); + if (errno) throw std::runtime_error("Invalid number: " + val); + insert_res = stg.insert_next_value(h_array, nval); + } }else { - //TODO: optimize here if need - double nval = boost::lexical_cast(val); //string_tools::string_to_num_fast(val, nval); + errno = 0; + double nval = strtod(val.c_str(), NULL); + if (errno) throw std::runtime_error("Invalid number: " + val); insert_res = stg.insert_next_value(h_array, nval); } CHECK_AND_ASSERT_THROW_MES(insert_res, "Failed to insert next value"); From 14a5c2068f53cfe1af3056375fed2587bc07d320 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 1 Nov 2018 14:51:08 +0000 Subject: [PATCH 0701/1404] p2p: tone down "no incoming connections" warning to info if in peers is 0 Also add an info if not offline, since it weakens the network --- src/p2p/net_node.inl | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index a61b6107f11..fbf265fc9b0 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -1334,12 +1334,19 @@ namespace nodetool template bool node_server::check_incoming_connections() { - if (m_offline || m_hide_my_port) + if (m_offline) return true; if (get_incoming_connections_count() == 0) { - const el::Level level = el::Level::Warning; - MCLOG_RED(level, "global", "No incoming connections - check firewalls/routers allow port " << get_this_peer_port()); + if (m_hide_my_port || m_config.m_net_config.max_in_connection_count == 0) + { + MGINFO("Incoming connections disabled, enable them for full connectivity"); + } + else + { + const el::Level level = el::Level::Warning; + MCLOG_RED(level, "global", "No incoming connections - check firewalls/routers allow port " << get_this_peer_port()); + } } return true; } From 99fbe1008b3e576917a939a5b7fbc7fb26423f18 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 31 Oct 2018 15:15:23 +0000 Subject: [PATCH 0702/1404] db_lmdb: save some string copies for readonly db keys/values --- src/blockchain_db/lmdb/db_lmdb.cpp | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp index b1248c49264..d642069f858 100644 --- a/src/blockchain_db/lmdb/db_lmdb.cpp +++ b/src/blockchain_db/lmdb/db_lmdb.cpp @@ -85,6 +85,10 @@ inline void throw1(const T &e) #define MDB_val_set(var, val) MDB_val var = {sizeof(val), (void *)&val} +#define MDB_val_sized(var, val) MDB_val var = {val.size(), (void *)val.data()} + +#define MDB_val_str(var, val) MDB_val var = {strlen(val) + 1, (void *)val} + template struct MDB_val_copy: public MDB_val { @@ -714,7 +718,8 @@ void BlockchainLMDB::add_block(const block& blk, size_t block_weight, const diff CURSOR(block_info) // this call to mdb_cursor_put will change height() - MDB_val_copy blob(block_to_blob(blk)); + cryptonote::blobdata block_blob(block_to_blob(blk)); + MDB_val_sized(blob, block_blob); result = mdb_cursor_put(m_cur_blocks, &key, &blob, MDB_APPEND); if (result) throw0(DB_ERROR(lmdb_error("Failed to add block blob to db transaction: ", result).c_str())); @@ -828,7 +833,7 @@ uint64_t BlockchainLMDB::add_transaction_data(const crypto::hash& blk_hash, cons throw0(DB_ERROR(lmdb_error("Failed to add tx data to db transaction: ", result).c_str())); cryptonote::blobdata blob = tx_to_blob(tx); - MDB_val_copy blobval(blob); + MDB_val_sized(blobval, blob); std::stringstream ss; binary_archive ba(ss); @@ -836,7 +841,7 @@ uint64_t BlockchainLMDB::add_transaction_data(const crypto::hash& blk_hash, cons if (!r) throw0(DB_ERROR("Failed to serialize pruned tx")); std::string pruned = ss.str(); - MDB_val_copy pruned_blob(pruned); + MDB_val_sized(pruned_blob, pruned); result = mdb_cursor_put(m_cur_txs_pruned, &val_tx_id, &pruned_blob, MDB_APPEND); if (result) throw0(DB_ERROR(lmdb_error("Failed to add pruned tx blob to db transaction: ", result).c_str())); @@ -844,7 +849,7 @@ uint64_t BlockchainLMDB::add_transaction_data(const crypto::hash& blk_hash, cons if (pruned.size() > blob.size()) throw0(DB_ERROR("pruned tx size is larger than tx size")); cryptonote::blobdata prunable(blob.data() + pruned.size(), blob.size() - pruned.size()); - MDB_val_copy prunable_blob(prunable); + MDB_val_sized(prunable_blob, prunable); result = mdb_cursor_put(m_cur_txs_prunable, &val_tx_id, &prunable_blob, MDB_APPEND); if (result) throw0(DB_ERROR(lmdb_error("Failed to add prunable tx blob to db transaction: ", result).c_str())); @@ -1331,7 +1336,7 @@ void BlockchainLMDB::open(const std::string& filename, const int db_flags) bool compatible = true; - MDB_val_copy k("version"); + MDB_val_str(k, "version"); MDB_val v; auto get_result = mdb_get(txn, m_properties, &k, &v); if(get_result == MDB_SUCCESS) @@ -1379,7 +1384,7 @@ void BlockchainLMDB::open(const std::string& filename, const int db_flags) // only write version on an empty DB if (m_height == 0) { - MDB_val_copy k("version"); + MDB_val_str(k, "version"); MDB_val_copy v(VERSION); auto put_result = mdb_put(txn, m_properties, &k, &v, 0); if (put_result != MDB_SUCCESS) @@ -1476,7 +1481,7 @@ void BlockchainLMDB::reset() throw0(DB_ERROR(lmdb_error("Failed to drop m_properties: ", result).c_str())); // init with current version - MDB_val_copy k("version"); + MDB_val_str(k, "version"); MDB_val_copy v(VERSION); if (auto result = mdb_put(txn, m_properties, &k, &v, 0)) throw0(DB_ERROR(lmdb_error("Failed to write version to database: ", result).c_str())); @@ -1608,7 +1613,7 @@ void BlockchainLMDB::add_txpool_tx(const crypto::hash &txid, const cryptonote::b else throw1(DB_ERROR(lmdb_error("Error adding txpool tx metadata to db transaction: ", result).c_str())); } - MDB_val_copy blob_val(blob); + MDB_val_sized(blob_val, blob); if (auto result = mdb_cursor_put(m_cur_txpool_blob, &k, &blob_val, MDB_NODUPDATA)) { if (result == MDB_KEYEXIST) throw1(DB_ERROR("Attempting to add txpool tx blob that's already in the db")); @@ -4047,7 +4052,7 @@ void BlockchainLMDB::migrate_0_1() uint32_t version = 1; v.mv_data = (void *)&version; v.mv_size = sizeof(version); - MDB_val_copy vk("version"); + MDB_val_str(vk, "version"); result = mdb_txn_begin(m_env, NULL, 0, txn); if (result) throw0(DB_ERROR(lmdb_error("Failed to create a transaction for the db: ", result).c_str())); @@ -4189,7 +4194,7 @@ void BlockchainLMDB::migrate_1_2() uint32_t version = 2; v.mv_data = (void *)&version; v.mv_size = sizeof(version); - MDB_val_copy vk("version"); + MDB_val_str(vk, "version"); result = mdb_txn_begin(m_env, NULL, 0, txn); if (result) throw0(DB_ERROR(lmdb_error("Failed to create a transaction for the db: ", result).c_str())); @@ -4324,7 +4329,7 @@ void BlockchainLMDB::migrate_2_3() uint32_t version = 3; v.mv_data = (void *)&version; v.mv_size = sizeof(version); - MDB_val_copy vk("version"); + MDB_val_str(vk, "version"); result = mdb_txn_begin(m_env, NULL, 0, txn); if (result) throw0(DB_ERROR(lmdb_error("Failed to create a transaction for the db: ", result).c_str())); From 03fc731bf23ec828af1999fc8f4a651605ea6a57 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 1 Nov 2018 22:00:01 +0000 Subject: [PATCH 0703/1404] p2p: less frequent incoming connections check --- src/p2p/net_node.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h index 90e2f78b1a1..cc6e47e4f40 100644 --- a/src/p2p/net_node.h +++ b/src/p2p/net_node.h @@ -308,7 +308,7 @@ namespace nodetool epee::math_helper::once_a_time_seconds<1> m_connections_maker_interval; epee::math_helper::once_a_time_seconds<60*30, false> m_peerlist_store_interval; epee::math_helper::once_a_time_seconds<60> m_gray_peerlist_housekeeping_interval; - epee::math_helper::once_a_time_seconds<900, false> m_incoming_connections_interval; + epee::math_helper::once_a_time_seconds<3600, false> m_incoming_connections_interval; std::string m_bind_ip; std::string m_port; From c17a1d431b3e1c39f7ec79f961ee1f0449793bb3 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 1 Nov 2018 22:14:40 +0000 Subject: [PATCH 0704/1404] daemon: use msg_writer, not cout, to display information --- src/daemon/rpc_command_executor.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp index 9a0603a1009..f174653913f 100644 --- a/src/daemon/rpc_command_executor.cpp +++ b/src/daemon/rpc_command_executor.cpp @@ -567,8 +567,8 @@ bool t_rpc_command_executor::print_blockchain_info(uint64_t start_block_index, u for (auto & header : res.headers) { if (!first) - std::cout << std::endl; - std::cout + tools::msg_writer() << "" << std::endl; + tools::msg_writer() << "height: " << header.height << ", timestamp: " << header.timestamp << ", size: " << header.block_size << ", weight: " << header.block_weight << ", transactions: " << header.num_txes << std::endl << "major version: " << (unsigned)header.major_version << ", minor version: " << (unsigned)header.minor_version << std::endl @@ -1313,7 +1313,7 @@ bool t_rpc_command_executor::out_peers(uint64_t limit) } } - std::cout << "Max number of out peers set to " << limit << std::endl; + tools::msg_writer() << "Max number of out peers set to " << limit << std::endl; return true; } @@ -1345,7 +1345,7 @@ bool t_rpc_command_executor::in_peers(uint64_t limit) } } - std::cout << "Max number of in peers set to " << limit << std::endl; + tools::msg_writer() << "Max number of in peers set to " << limit << std::endl; return true; } From 71eb32a976512b294e077861a3123842e20611b6 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 1 Nov 2018 22:17:34 +0000 Subject: [PATCH 0705/1404] dns_utils: do not exit if DNS records are corrupt --- src/common/dns_utils.cpp | 41 +++++++++++++++++++++++++++++++++------- src/common/dns_utils.h | 3 ++- 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/src/common/dns_utils.cpp b/src/common/dns_utils.cpp index f2b27098132..606a2c7b723 100644 --- a/src/common/dns_utils.cpp +++ b/src/common/dns_utils.cpp @@ -37,6 +37,7 @@ #include #include #include +#include using namespace epee; namespace bf = boost::filesystem; @@ -119,10 +120,25 @@ get_builtin_ds(void) namespace tools { +static const char *get_record_name(int record_type) +{ + switch (record_type) + { + case DNS_TYPE_A: return "A"; + case DNS_TYPE_TXT: return "TXT"; + case DNS_TYPE_AAAA: return "AAAA"; + default: return "unknown"; + } +} + // fuck it, I'm tired of dealing with getnameinfo()/inet_ntop/etc -std::string ipv4_to_string(const char* src, size_t len) +boost::optional ipv4_to_string(const char* src, size_t len) { - assert(len >= 4); + if (len < 4) + { + MERROR("Invalid IPv4 address: " << std::string(src, len)); + return boost::none; + } std::stringstream ss; unsigned int bytes[4]; @@ -140,9 +156,13 @@ std::string ipv4_to_string(const char* src, size_t len) // this obviously will need to change, but is here to reflect the above // stop-gap measure and to make the tests pass at least... -std::string ipv6_to_string(const char* src, size_t len) +boost::optional ipv6_to_string(const char* src, size_t len) { - assert(len >= 8); + if (len < 8) + { + MERROR("Invalid IPv4 address: " << std::string(src, len)); + return boost::none; + } std::stringstream ss; unsigned int bytes[8]; @@ -162,8 +182,10 @@ std::string ipv6_to_string(const char* src, size_t len) return ss.str(); } -std::string txt_to_string(const char* src, size_t len) +boost::optional txt_to_string(const char* src, size_t len) { + if (len == 0) + return boost::none; return std::string(src+1, len-1); } @@ -266,7 +288,7 @@ DNSResolver::~DNSResolver() } } -std::vector DNSResolver::get_record(const std::string& url, int record_type, std::string (*reader)(const char *,size_t), bool& dnssec_available, bool& dnssec_valid) +std::vector DNSResolver::get_record(const std::string& url, int record_type, boost::optional (*reader)(const char *,size_t), bool& dnssec_available, bool& dnssec_valid) { std::vector addresses; dnssec_available = false; @@ -289,7 +311,12 @@ std::vector DNSResolver::get_record(const std::string& url, int rec { for (size_t i=0; result->data[i] != NULL; i++) { - addresses.push_back((*reader)(result->data[i], result->len[i])); + boost::optional res = (*reader)(result->data[i], result->len[i]); + if (res) + { + MINFO("Found \"" << *res << "\" in " << get_record_name(record_type) << " record for " << url); + addresses.push_back(*res); + } } } } diff --git a/src/common/dns_utils.h b/src/common/dns_utils.h index f46bca3dd02..3a6ef68a155 100644 --- a/src/common/dns_utils.h +++ b/src/common/dns_utils.h @@ -30,6 +30,7 @@ #include #include #include +#include namespace tools { @@ -143,7 +144,7 @@ class DNSResolver * @return A vector of strings containing the requested record; or an empty vector */ // TODO: modify this to accommodate DNSSEC - std::vector get_record(const std::string& url, int record_type, std::string (*reader)(const char *,size_t), bool& dnssec_available, bool& dnssec_valid); + std::vector get_record(const std::string& url, int record_type, boost::optional (*reader)(const char *,size_t), bool& dnssec_available, bool& dnssec_valid); /** * @brief Checks a string to see if it looks like a URL From 233f00c60171174164fb5170c1a9458748846f54 Mon Sep 17 00:00:00 2001 From: xiphon Date: Fri, 26 Oct 2018 23:07:23 +0000 Subject: [PATCH 0706/1404] cryptonote_core: fix build error gcc 5.4.0 'sign-compare' --- src/cryptonote_core/cryptonote_core.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index d8c38bf9eac..4b806c28279 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -1716,7 +1716,7 @@ namespace cryptonote for (size_t n = 0; n < sizeof(seconds)/sizeof(seconds[0]); ++n) { unsigned int b = 0; - for (time_t ts: timestamps) b += ts >= now - seconds[n]; + for (time_t ts: timestamps) b += ts >= now - static_cast(seconds[n]); const double p = probability(b, seconds[n] / DIFFICULTY_TARGET_V2); MDEBUG("blocks in the last " << seconds[n] / 60 << " minutes: " << b << " (probability " << p << ")"); if (p < threshold) From 97cad7e9796621d6b676a81fef1499037ef16e15 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 31 May 2018 19:25:52 +0100 Subject: [PATCH 0707/1404] db_lmdb: log which output was not found in get_output_key --- src/blockchain_db/lmdb/db_lmdb.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp index 84a083c2602..1674c40dd73 100644 --- a/src/blockchain_db/lmdb/db_lmdb.cpp +++ b/src/blockchain_db/lmdb/db_lmdb.cpp @@ -2499,7 +2499,8 @@ output_data_t BlockchainLMDB::get_output_key(const uint64_t& amount, const uint6 MDB_val_set(v, index); auto get_result = mdb_cursor_get(m_cur_output_amounts, &k, &v, MDB_GET_BOTH); if (get_result == MDB_NOTFOUND) - throw1(OUTPUT_DNE("Attempting to get output pubkey by index, but key does not exist")); + throw1(OUTPUT_DNE(std::string("Attempting to get output pubkey by index, but key does not exist: amount " + + std::to_string(amount) + ", index " + std::to_string(index)).c_str())); else if (get_result) throw0(DB_ERROR("Error attempting to retrieve an output pubkey from the db")); From 1c04c21d6e173dc85a30e33f0aa8fea9f15be3b7 Mon Sep 17 00:00:00 2001 From: stoffu Date: Fri, 2 Nov 2018 21:12:11 +0900 Subject: [PATCH 0708/1404] wallet_rpc_server: include additional tx keys in sign_transfer response Followup on #4552 --- src/wallet/wallet_rpc_server.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index 5e6100dfd39..ac0712d7f3b 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -982,7 +982,11 @@ namespace tools { res.tx_hash_list.push_back(epee::string_tools::pod_to_hex(cryptonote::get_transaction_hash(ptx.tx))); if (req.get_tx_keys) + { res.tx_key_list.push_back(epee::string_tools::pod_to_hex(ptx.tx_key)); + for (const crypto::secret_key& additional_tx_key : ptx.additional_tx_keys) + res.tx_key_list.back() += epee::string_tools::pod_to_hex(additional_tx_key); + } } if (req.export_raw) From 0cfd2ae5e774ff845abb669d6bfd63e5c838039f Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 2 Nov 2018 12:59:06 +0000 Subject: [PATCH 0709/1404] mlocker: fix dtor ordering problem leak the mutex instead, it's a one off --- contrib/epee/src/mlocker.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contrib/epee/src/mlocker.cpp b/contrib/epee/src/mlocker.cpp index 5573d591a17..c3262e8f4bf 100644 --- a/contrib/epee/src/mlocker.cpp +++ b/contrib/epee/src/mlocker.cpp @@ -84,8 +84,8 @@ namespace epee boost::mutex &mlocker::mutex() { - static boost::mutex vmutex; - return vmutex; + static boost::mutex *vmutex = new boost::mutex(); + return *vmutex; } std::map &mlocker::map() { From c22e85fe1ac8f7ee7ab09656ea9b5db63bdc8048 Mon Sep 17 00:00:00 2001 From: stoffu Date: Fri, 2 Nov 2018 23:22:46 +0900 Subject: [PATCH 0710/1404] simplewallet: remove redundant messages on daemon connection failure try_connect_to_daemon with silent=false already prints failure message --- src/simplewallet/simplewallet.cpp | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index f171d35b6de..a810e5dda4f 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -780,10 +780,7 @@ bool simple_wallet::payment_id(const std::vector &args/* = std::vec bool simple_wallet::print_fee_info(const std::vector &args/* = std::vector()*/) { if (!try_connect_to_daemon()) - { - fail_msg_writer() << tr("Cannot connect to daemon"); return true; - } const bool per_byte = m_wallet->use_fork_rules(HF_VERSION_PER_BYTE_FEE); const uint64_t base_fee = m_wallet->get_base_fee(); const char *base = per_byte ? "byte" : "kB"; @@ -4608,10 +4605,7 @@ bool simple_wallet::print_ring_members(const std::vector &args) } if (!try_connect_to_daemon()) - { - fail_msg_writer() << tr("failed to connect to the daemon"); return true; - } SCOPED_WALLET_UNLOCK(); @@ -6489,10 +6480,7 @@ bool simple_wallet::check_spend_proof(const std::vector &args) } if (!try_connect_to_daemon()) - { - fail_msg_writer() << tr("failed to connect to the daemon"); return true; - } std::string sig_str; if (!epee::file_io_utils::load_file_to_string(args[1], sig_str)) @@ -6546,10 +6534,7 @@ bool simple_wallet::get_reserve_proof(const std::vector &args) } if (!try_connect_to_daemon()) - { - fail_msg_writer() << tr("failed to connect to the daemon"); return true; - } SCOPED_WALLET_UNLOCK(); @@ -6577,10 +6562,7 @@ bool simple_wallet::check_reserve_proof(const std::vector &args) } if (!try_connect_to_daemon()) - { - fail_msg_writer() << tr("failed to connect to the daemon"); return true; - } cryptonote::address_parse_info info; if(!cryptonote::get_account_address_from_str_or_url(info, m_wallet->nettype(), args[0], oa_prompter)) From 29ffb6bba8867586986345b4f0c560e5ea5fce85 Mon Sep 17 00:00:00 2001 From: Dusan Klinec Date: Thu, 23 Aug 2018 23:50:53 +0200 Subject: [PATCH 0711/1404] device/trezor: trezor support added --- .gitmodules | 3 + CMakeLists.txt | 11 + external/trezor-common | 1 + src/CMakeLists.txt | 1 + src/cryptonote_basic/account.cpp | 24 +- src/cryptonote_basic/account.h | 1 + src/device/CMakeLists.txt | 2 + src/device/device.hpp | 16 +- src/device/device_cold.hpp | 71 ++ src/device/device_default.cpp | 8 +- src/device/device_ledger.hpp | 1 + src/device_trezor/CMakeLists.txt | 123 +++ src/device_trezor/device_trezor.cpp | 363 +++++++ src/device_trezor/device_trezor.hpp | 132 +++ src/device_trezor/device_trezor_base.cpp | 301 ++++++ src/device_trezor/device_trezor_base.hpp | 301 ++++++ src/device_trezor/trezor.hpp | 44 + src/device_trezor/trezor/exceptions.hpp | 193 ++++ src/device_trezor/trezor/messages/.gitignore | 2 + src/device_trezor/trezor/messages_map.cpp | 125 +++ src/device_trezor/trezor/messages_map.hpp | 94 ++ src/device_trezor/trezor/protocol.cpp | 891 ++++++++++++++++++ src/device_trezor/trezor/protocol.hpp | 300 ++++++ src/device_trezor/trezor/tools/README.md | 36 + .../trezor/tools/build_protob.py | 38 + src/device_trezor/trezor/tools/pb2cpp.py | 186 ++++ src/device_trezor/trezor/transport.cpp | 651 +++++++++++++ src/device_trezor/trezor/transport.hpp | 331 +++++++ src/device_trezor/trezor/trezor_defs.hpp | 48 + src/ringct/rctSigs.h | 2 +- src/simplewallet/simplewallet.cpp | 146 ++- src/simplewallet/simplewallet.h | 3 + src/wallet/CMakeLists.txt | 1 + src/wallet/wallet2.cpp | 158 +++- src/wallet/wallet2.h | 22 +- 35 files changed, 4591 insertions(+), 39 deletions(-) create mode 160000 external/trezor-common create mode 100644 src/device/device_cold.hpp create mode 100644 src/device_trezor/CMakeLists.txt create mode 100644 src/device_trezor/device_trezor.cpp create mode 100644 src/device_trezor/device_trezor.hpp create mode 100644 src/device_trezor/device_trezor_base.cpp create mode 100644 src/device_trezor/device_trezor_base.hpp create mode 100644 src/device_trezor/trezor.hpp create mode 100644 src/device_trezor/trezor/exceptions.hpp create mode 100644 src/device_trezor/trezor/messages/.gitignore create mode 100644 src/device_trezor/trezor/messages_map.cpp create mode 100644 src/device_trezor/trezor/messages_map.hpp create mode 100644 src/device_trezor/trezor/protocol.cpp create mode 100644 src/device_trezor/trezor/protocol.hpp create mode 100644 src/device_trezor/trezor/tools/README.md create mode 100644 src/device_trezor/trezor/tools/build_protob.py create mode 100644 src/device_trezor/trezor/tools/pb2cpp.py create mode 100644 src/device_trezor/trezor/transport.cpp create mode 100644 src/device_trezor/trezor/transport.hpp create mode 100644 src/device_trezor/trezor/trezor_defs.hpp diff --git a/.gitmodules b/.gitmodules index 91bddb0a3f8..6e2339fb942 100644 --- a/.gitmodules +++ b/.gitmodules @@ -9,3 +9,6 @@ [submodule "external/rapidjson"] path = external/rapidjson url = https://github.com/Tencent/rapidjson +[submodule "external/trezor-common"] + path = external/trezor-common + url = https://github.com/trezor/trezor-common.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 3a685ee8e0a..a3dbf974a94 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -190,6 +190,7 @@ if(NOT MANUAL_SUBMODULES) check_submodule(external/miniupnp) check_submodule(external/unbound) check_submodule(external/rapidjson) + check_submodule(external/trezor-common) endif() endif() @@ -512,6 +513,16 @@ else (HIDAPI_FOUND) message(STATUS "Could not find HIDAPI") endif() +# Protobuf, optional. Required for TREZOR. +include(FindProtobuf) +find_package(Protobuf) +if(Protobuf_FOUND) + set(HAVE_PROTOBUF 1) + add_definitions(-DHAVE_PROTOBUF=1) +else(Protobuf_FOUND) + message(STATUS "Could not find Protobuf") +endif() + if(MSVC) add_definitions("/bigobj /MP /W3 /GS- /D_CRT_SECURE_NO_WARNINGS /wd4996 /wd4345 /D_WIN32_WINNT=0x0600 /DWIN32_LEAN_AND_MEAN /DGTEST_HAS_TR1_TUPLE=0 /FIinline_c.h /D__SSE4_1__") # set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /Dinline=__inline") diff --git a/external/trezor-common b/external/trezor-common new file mode 160000 index 00000000000..588f8e03f5a --- /dev/null +++ b/external/trezor-common @@ -0,0 +1 @@ +Subproject commit 588f8e03f5ac111adf719f0a437de67481a26aed diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3b71c38cd22..6ee7effdd05 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -145,3 +145,4 @@ if(PER_BLOCK_CHECKPOINT) endif() add_subdirectory(device) +add_subdirectory(device_trezor) diff --git a/src/cryptonote_basic/account.cpp b/src/cryptonote_basic/account.cpp index 1dc1ad71d04..edbc2c56154 100644 --- a/src/cryptonote_basic/account.cpp +++ b/src/cryptonote_basic/account.cpp @@ -139,6 +139,15 @@ DISABLE_VS_WARNINGS(4244 4345) m_creation_timestamp = 0; } //----------------------------------------------------------------- + void account_base::deinit() + { + try{ + m_keys.get_device().disconnect(); + } catch (const std::exception &e){ + MERROR("Device disconnect exception: " << e.what()); + } + } + //----------------------------------------------------------------- void account_base::forget_spend_key() { m_keys.m_spend_secret_key = crypto::secret_key(); @@ -206,11 +215,16 @@ DISABLE_VS_WARNINGS(4244 4345) void account_base::create_from_device(hw::device &hwdev) { m_keys.set_device(hwdev); - MCDEBUG("ledger", "device type: "< + + +namespace hw { + + typedef struct wallet_shim { + boost::function get_tx_pub_key_from_received_outs; + } wallet_shim; + + class tx_aux_data { + public: + std::vector tx_device_aux; // device generated aux data + std::vector tx_recipients; // as entered by user + }; + + class device_cold { + public: + + using exported_key_image = std::vector>; + + /** + * Key image sync with the cold protocol. + */ + virtual void ki_sync(wallet_shim * wallet, + const std::vector<::tools::wallet2::transfer_details> & transfers, + exported_key_image & ski) =0; + + /** + * Signs unsigned transaction with the cold protocol. + */ + virtual void tx_sign(wallet_shim * wallet, + const ::tools::wallet2::unsigned_tx_set & unsigned_tx, + ::tools::wallet2::signed_tx_set & signed_tx, + tx_aux_data & aux_data) =0; + }; +} + +#endif //MONERO_DEVICE_COLD_H diff --git a/src/device/device_default.cpp b/src/device/device_default.cpp index 68f40d91e1d..1e3d8094998 100644 --- a/src/device/device_default.cpp +++ b/src/device/device_default.cpp @@ -69,17 +69,17 @@ namespace hw { } bool device_default::init(void) { - dfns(); + return true; } bool device_default::release() { - dfns(); + return true; } bool device_default::connect(void) { - dfns(); + return true; } bool device_default::disconnect() { - dfns(); + return true; } bool device_default::set_mode(device_mode mode) { diff --git a/src/device/device_ledger.hpp b/src/device/device_ledger.hpp index dde69fbfdcc..2f5beb044ef 100644 --- a/src/device/device_ledger.hpp +++ b/src/device/device_ledger.hpp @@ -141,6 +141,7 @@ namespace hw { bool set_mode(device_mode mode) override; device_type get_type() const override {return device_type::LEDGER;}; + device_protocol_t device_protocol() const override { return PROTOCOL_PROXY; }; /* ======================================================================= */ /* LOCKER */ diff --git a/src/device_trezor/CMakeLists.txt b/src/device_trezor/CMakeLists.txt new file mode 100644 index 00000000000..c555e9fcdee --- /dev/null +++ b/src/device_trezor/CMakeLists.txt @@ -0,0 +1,123 @@ +# Copyright (c) 2014-2017, The Monero Project +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, are +# permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of +# conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list +# of conditions and the following disclaimer in the documentation and/or other +# materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be +# used to endorse or promote products derived from this software without specific +# prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +# THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +set(TREZOR_PROTOB_H + trezor/messages/messages.pb.h + trezor/messages/messages-common.pb.h + trezor/messages/messages-management.pb.h + trezor/messages/messages-monero.pb.h +) + +set(TREZOR_PROTOB_CPP + trezor/messages/messages.pb.cc + trezor/messages/messages-common.pb.cc + trezor/messages/messages-management.pb.cc + trezor/messages/messages-monero.pb.cc +) + +set(trezor_headers + trezor/exceptions.hpp + trezor/messages_map.hpp + trezor/protocol.hpp + trezor/transport.hpp + device_trezor_base.hpp + device_trezor.hpp + trezor.hpp + ${TREZOR_PROTOB_H} +) + +set(trezor_sources + trezor/messages_map.cpp + trezor/protocol.cpp + trezor/transport.cpp + device_trezor_base.cpp + device_trezor.cpp + ${TREZOR_PROTOB_CPP} +) + +set(trezor_private_headers) + + +include(FindProtobuf) +find_package(Protobuf) # REQUIRED + +# Test for HAVE_PROTOBUF from the parent +if(Protobuf_FOUND AND HAVE_PROTOBUF) + if ("$ENV{PYTHON3}" STREQUAL "") + set(PYTHON3 "python3") + else() + set(PYTHON3 "$ENV{PYTHON3}" CACHE INTERNAL "Copied from environment variable") + endif() + + execute_process(COMMAND ${PYTHON3} tools/build_protob.py WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/trezor RESULT_VARIABLE RET OUTPUT_VARIABLE OUT ERROR_VARIABLE ERR) + if(RET) + message(WARNING "Trezor protobuf messages could not be regenerated (err=${RET}, python ${PYTHON})." + "OUT: ${OUT}, ERR: ${ERR}." + "Please read src/device_trezor/trezor/tools/README.md") + else() + message(STATUS "Trezor protobuf messages regenerated ${OUT}") + set(TREZOR_PROTOBUF_GENERATED 1) + endif() +endif() + + +if(TREZOR_PROTOBUF_GENERATED) + message(STATUS "Trezor support enabled") + + add_definitions(-DPROTOBUF_INLINE_NOT_IN_HEADERS=0) + + monero_private_headers(device_trezor + ${device_private_headers} + ${PROTOBUF_INCLUDE_DIR}) + + monero_add_library(device_trezor + ${trezor_sources} + ${trezor_headers} + ${trezor_private_headers}) + + target_link_libraries(device_trezor + PUBLIC + device + cncrypto + ringct_basic + cryptonote_core + common + ${SODIUM_LIBRARY} + ${Boost_CHRONO_LIBRARY} + ${PROTOBUF_LIBRARY} + PRIVATE + ${EXTRA_LIBRARIES}) + + # set(WITH_DEVICE_TREZOR 1 PARENT_SCOPE) + # add_definitions(-DWITH_DEVICE_TREZOR=1) + +else() + monero_private_headers(device_trezor) + monero_add_library(device_trezor device_trezor.cpp) + target_link_libraries(device_trezor PUBLIC cncrypto) +endif() diff --git a/src/device_trezor/device_trezor.cpp b/src/device_trezor/device_trezor.cpp new file mode 100644 index 00000000000..07c03fc6699 --- /dev/null +++ b/src/device_trezor/device_trezor.cpp @@ -0,0 +1,363 @@ +// Copyright (c) 2017-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +#include "device_trezor.hpp" + +namespace hw { +namespace trezor { + +#if WITH_DEVICE_TREZOR + +#undef MONERO_DEFAULT_LOG_CATEGORY +#define MONERO_DEFAULT_LOG_CATEGORY "device.trezor" + +#define HW_TREZOR_NAME "Trezor" +#define HW_TREZOR_NAME_LITE "TrezorLite" + + static device_trezor *trezor_device = nullptr; + static device_trezor *ensure_trezor_device(){ + if (!trezor_device) { + trezor_device = new device_trezor(); + trezor_device->set_name(HW_TREZOR_NAME); + } + return trezor_device; + } + + void register_all(std::map> ®istry) { + registry.insert(std::make_pair(HW_TREZOR_NAME, std::unique_ptr(ensure_trezor_device()))); + } + + void register_all() { + hw::register_device(HW_TREZOR_NAME, ensure_trezor_device()); + } + + device_trezor::device_trezor() { + + } + + device_trezor::~device_trezor() { + try { + disconnect(); + release(); + } catch(std::exception const& e){ + MWARNING("Could not disconnect and release: " << e.what()); + } + } + + /* ======================================================================= */ + /* WALLET & ADDRESS */ + /* ======================================================================= */ + + bool device_trezor::get_public_address(cryptonote::account_public_address &pubkey) { + try { + auto res = get_address(); + + cryptonote::address_parse_info info{}; + bool r = cryptonote::get_account_address_from_str(info, this->network_type, res->address()); + CHECK_AND_ASSERT_MES(r, false, "Could not parse returned address. Address parse failed: " + res->address()); + CHECK_AND_ASSERT_MES(!info.is_subaddress, false, "Trezor returned a sub address"); + + pubkey = info.address; + return true; + + } catch(std::exception const& e){ + MERROR("Get public address exception: " << e.what()); + return false; + } + } + + bool device_trezor::get_secret_keys(crypto::secret_key &viewkey , crypto::secret_key &spendkey) { + try { + MDEBUG("Loading view-only key from the Trezor. Please check the Trezor for a confirmation."); + auto res = get_view_key(); + CHECK_AND_ASSERT_MES(res->watch_key().size() == 32, false, "Trezor returned invalid view key"); + + spendkey = crypto::null_skey; // not given + memcpy(viewkey.data, res->watch_key().data(), 32); + + return true; + + } catch(std::exception const& e){ + MERROR("Get secret keys exception: " << e.what()); + return false; + } + } + + /* ======================================================================= */ + /* Helpers */ + /* ======================================================================= */ + + /* ======================================================================= */ + /* TREZOR PROTOCOL */ + /* ======================================================================= */ + + std::shared_ptr device_trezor::get_address( + const boost::optional> & path, + const boost::optional & network_type){ + AUTO_LOCK_CMD(); + require_connected(); + test_ping(); + + auto req = std::make_shared(); + this->set_msg_addr(req.get(), path, network_type); + + auto response = this->client_exchange(req); + MTRACE("Get address response received"); + return response; + } + + std::shared_ptr device_trezor::get_view_key( + const boost::optional> & path, + const boost::optional & network_type){ + AUTO_LOCK_CMD(); + require_connected(); + test_ping(); + + auto req = std::make_shared(); + this->set_msg_addr(req.get(), path, network_type); + + auto response = this->client_exchange(req); + MTRACE("Get watch key response received"); + return response; + } + + void device_trezor::ki_sync(wallet_shim * wallet, + const std::vector & transfers, + hw::device_cold::exported_key_image & ski) + { + AUTO_LOCK_CMD(); + require_connected(); + test_ping(); + + std::shared_ptr req; + + std::vector mtds; + std::vector kis; + protocol::ki::key_image_data(wallet, transfers, mtds); + protocol::ki::generate_commitment(mtds, transfers, req); + + this->set_msg_addr(req.get()); + auto ack1 = this->client_exchange(req); + + const auto batch_size = 10; + const auto num_batches = (mtds.size() + batch_size - 1) / batch_size; + for(uint64_t cur = 0; cur < num_batches; ++cur){ + auto step_req = std::make_shared(); + auto idx_finish = std::min(static_cast((cur + 1) * batch_size), static_cast(mtds.size())); + for(uint64_t idx = cur * batch_size; idx < idx_finish; ++idx){ + auto added_tdis = step_req->add_tdis(); + CHECK_AND_ASSERT_THROW_MES(idx < mtds.size(), "Invalid transfer detail index"); + *added_tdis = mtds[idx]; + } + + auto step_ack = this->client_exchange(step_req); + auto kis_size = step_ack->kis_size(); + kis.reserve(static_cast(kis_size)); + for(int i = 0; i < kis_size; ++i){ + auto ckis = step_ack->kis(i); + kis.push_back(ckis); + } + + MTRACE("Batch " << cur << " / " << num_batches << " batches processed"); + } + + auto final_req = std::make_shared(); + auto final_ack = this->client_exchange(final_req); + ski.reserve(kis.size()); + + for(auto & sub : kis){ + char buff[32*3]; + protocol::crypto::chacha::decrypt(sub.blob().data(), sub.blob().size(), + reinterpret_cast(final_ack->enc_key().data()), + reinterpret_cast(sub.iv().data()), buff); + + ::crypto::signature sig{}; + ::crypto::key_image ki; + memcpy(ki.data, buff, 32); + memcpy(sig.c.data, buff + 32, 32); + memcpy(sig.r.data, buff + 64, 32); + ski.push_back(std::make_pair(ki, sig)); + } + } + + + void device_trezor::tx_sign(wallet_shim * wallet, + const tools::wallet2::unsigned_tx_set & unsigned_tx, + tools::wallet2::signed_tx_set & signed_tx, + hw::tx_aux_data & aux_data) + { + size_t num_tx = unsigned_tx.txes.size(); + signed_tx.key_images.clear(); + signed_tx.key_images.resize(unsigned_tx.transfers.size()); + + for(size_t tx_idx = 0; tx_idx < num_tx; ++tx_idx) { + std::shared_ptr signer; + tx_sign(wallet, unsigned_tx, tx_idx, aux_data, signer); + + auto & cdata = signer->tdata(); + auto aux_info_cur = signer->store_tx_aux_info(); + aux_data.tx_device_aux.emplace_back(aux_info_cur); + + // Pending tx reconstruction + signed_tx.ptx.emplace_back(); + auto & cpend = signed_tx.ptx.back(); + cpend.tx = cdata.tx; + cpend.dust = 0; + cpend.fee = 0; + cpend.dust_added_to_fee = false; + cpend.change_dts = cdata.tx_data.change_dts; + cpend.selected_transfers = cdata.tx_data.selected_transfers; + cpend.key_images = ""; + cpend.dests = cdata.tx_data.dests; + cpend.construction_data = cdata.tx_data; + + // Transaction check + cryptonote::blobdata tx_blob; + cryptonote::transaction tx_deserialized; + bool r = cryptonote::t_serializable_object_to_blob(cpend.tx, tx_blob); + CHECK_AND_ASSERT_THROW_MES(r, "Transaction serialization failed"); + r = cryptonote::parse_and_validate_tx_from_blob(tx_blob, tx_deserialized); + CHECK_AND_ASSERT_THROW_MES(r, "Transaction deserialization failed"); + + std::string key_images; + bool all_are_txin_to_key = std::all_of(cdata.tx.vin.begin(), cdata.tx.vin.end(), [&](const cryptonote::txin_v& s_e) -> bool + { + CHECKED_GET_SPECIFIC_VARIANT(s_e, const cryptonote::txin_to_key, in, false); + key_images += boost::to_string(in.k_image) + " "; + return true; + }); + if(!all_are_txin_to_key) { + throw std::invalid_argument("Not all are txin_to_key"); + } + cpend.key_images = key_images; + + // KI sync + size_t num_sources = cdata.tx_data.sources.size(); + CHECK_AND_ASSERT_THROW_MES(num_sources == cdata.source_permutation.size(), "Invalid permutation size"); + CHECK_AND_ASSERT_THROW_MES(num_sources == cdata.tx.vin.size(), "Invalid tx.vin size"); + for(size_t src_idx = 0; src_idx < num_sources; ++src_idx){ + size_t idx_mapped = cdata.source_permutation[src_idx]; + CHECK_AND_ASSERT_THROW_MES(idx_mapped < cdata.tx_data.selected_transfers.size(), "Invalid idx_mapped"); + CHECK_AND_ASSERT_THROW_MES(src_idx < cdata.tx.vin.size(), "Invalid idx_mapped"); + + size_t idx_map_src = cdata.tx_data.selected_transfers[idx_mapped]; + auto vini = boost::get(cdata.tx.vin[src_idx]); + + CHECK_AND_ASSERT_THROW_MES(idx_map_src < signed_tx.key_images.size(), "Invalid key image index"); + signed_tx.key_images[idx_map_src] = vini.k_image; + } + } + } + + void device_trezor::tx_sign(wallet_shim * wallet, + const tools::wallet2::unsigned_tx_set & unsigned_tx, + size_t idx, + hw::tx_aux_data & aux_data, + std::shared_ptr & signer) + { + AUTO_LOCK_CMD(); + require_connected(); + test_ping(); + + CHECK_AND_ASSERT_THROW_MES(idx < unsigned_tx.txes.size(), "Invalid transaction index"); + signer = std::make_shared(wallet, &unsigned_tx, idx, &aux_data); + const tools::wallet2::tx_construction_data & cur_tx = unsigned_tx.txes[idx]; + unsigned long num_sources = cur_tx.sources.size(); + unsigned long num_outputs = cur_tx.splitted_dsts.size(); + + // Step: Init + auto init_msg = signer->step_init(); + this->set_msg_addr(init_msg.get()); + + auto response = this->client_exchange(init_msg); + signer->step_init_ack(response); + + // Step: Set transaction inputs + for(size_t cur_src = 0; cur_src < num_sources; ++cur_src){ + auto src = signer->step_set_input(cur_src); + auto ack = this->client_exchange(src); + signer->step_set_input_ack(ack); + } + + // Step: sort + auto perm_req = signer->step_permutation(); + if (perm_req){ + auto perm_ack = this->client_exchange(perm_req); + signer->step_permutation_ack(perm_ack); + } + + // Step: input_vini + if (!signer->in_memory()){ + for(size_t cur_src = 0; cur_src < num_sources; ++cur_src){ + auto src = signer->step_set_vini_input(cur_src); + auto ack = this->client_exchange(src); + signer->step_set_vini_input_ack(ack); + } + } + + // Step: all inputs set + auto all_inputs_set = signer->step_all_inputs_set(); + auto ack_all_inputs = this->client_exchange(all_inputs_set); + signer->step_all_inputs_set_ack(ack_all_inputs); + + // Step: outputs + for(size_t cur_dst = 0; cur_dst < num_outputs; ++cur_dst){ + auto src = signer->step_set_output(cur_dst); + auto ack = this->client_exchange(src); + signer->step_set_output_ack(ack); + } + + // Step: all outs set + auto all_out_set = signer->step_all_outs_set(); + auto ack_all_out_set = this->client_exchange(all_out_set); + signer->step_all_outs_set_ack(ack_all_out_set, *this); + + // Step: sign each input + for(size_t cur_src = 0; cur_src < num_sources; ++cur_src){ + auto src = signer->step_sign_input(cur_src); + auto ack_sign = this->client_exchange(src); + signer->step_sign_input_ack(ack_sign); + } + + // Step: final + auto final_msg = signer->step_final(); + auto ack_final = this->client_exchange(final_msg); + signer->step_final_ack(ack_final); + } + +#else //WITH_DEVICE_TREZOR + + void register_all(std::map> ®istry) { + } + + void register_all() { + } + +#endif //WITH_DEVICE_TREZOR +}} \ No newline at end of file diff --git a/src/device_trezor/device_trezor.hpp b/src/device_trezor/device_trezor.hpp new file mode 100644 index 00000000000..765c9b82cbe --- /dev/null +++ b/src/device_trezor/device_trezor.hpp @@ -0,0 +1,132 @@ +// Copyright (c) 2017-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef MONERO_DEVICE_TREZOR_H +#define MONERO_DEVICE_TREZOR_H + + +#include +#include +#include "device/device.hpp" +#include "device/device_default.hpp" +#include "device/device_cold.hpp" +#include +#include +#include +#include "cryptonote_config.h" +#include "trezor.hpp" +#include "device_trezor_base.hpp" + +namespace hw { +namespace trezor { + + void register_all(); + void register_all(std::map> ®istry); + +#if WITH_DEVICE_TREZOR + class device_trezor; + + /** + * Main device + */ + class device_trezor : public hw::trezor::device_trezor_base, public hw::device_cold { + protected: + // To speed up blockchain parsing the view key maybe handle here. + crypto::secret_key viewkey; + bool has_view_key; + + public: + device_trezor(); + virtual ~device_trezor() override; + + device_trezor(const device_trezor &device) = delete ; + device_trezor& operator=(const device_trezor &device) = delete; + + explicit operator bool() const override {return true;} + + device_protocol_t device_protocol() const override { return PROTOCOL_COLD; }; + + bool has_ki_cold_sync() const override { return true; } + bool has_tx_cold_sign() const override { return true; } + void set_network_type(cryptonote::network_type network_type) override { this->network_type = network_type; } + + /* ======================================================================= */ + /* WALLET & ADDRESS */ + /* ======================================================================= */ + bool get_public_address(cryptonote::account_public_address &pubkey) override; + bool get_secret_keys(crypto::secret_key &viewkey , crypto::secret_key &spendkey) override; + + /* ======================================================================= */ + /* TREZOR PROTOCOL */ + /* ======================================================================= */ + + /** + * Get address. Throws. + */ + std::shared_ptr get_address( + const boost::optional> & path = boost::none, + const boost::optional & network_type = boost::none); + + /** + * Get watch key from device. Throws. + */ + std::shared_ptr get_view_key( + const boost::optional> & path = boost::none, + const boost::optional & network_type = boost::none); + + /** + * Key image sync with the Trezor. + */ + void ki_sync(wallet_shim * wallet, + const std::vector<::tools::wallet2::transfer_details> & transfers, + hw::device_cold::exported_key_image & ski) override; + + /** + * Signs particular transaction idx in the unsigned set, keeps state in the signer + */ + void tx_sign(wallet_shim * wallet, + const ::tools::wallet2::unsigned_tx_set & unsigned_tx, + size_t idx, + hw::tx_aux_data & aux_data, + std::shared_ptr & signer); + + /** + * Signs unsigned transaction with the Trezor. + */ + void tx_sign(wallet_shim * wallet, + const ::tools::wallet2::unsigned_tx_set & unsigned_tx, + ::tools::wallet2::signed_tx_set & signed_tx, + hw::tx_aux_data & aux_data) override; + }; + +#endif + +} +} +#endif //MONERO_DEVICE_TREZOR_H diff --git a/src/device_trezor/device_trezor_base.cpp b/src/device_trezor/device_trezor_base.cpp new file mode 100644 index 00000000000..3a98bba5a77 --- /dev/null +++ b/src/device_trezor/device_trezor_base.cpp @@ -0,0 +1,301 @@ +// Copyright (c) 2017-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +#include "device_trezor_base.hpp" + +namespace hw { +namespace trezor { + +#if WITH_DEVICE_TREZOR + +#undef MONERO_DEFAULT_LOG_CATEGORY +#define MONERO_DEFAULT_LOG_CATEGORY "device.trezor" + + std::shared_ptr trezor_protocol_callback::on_button_request(const messages::common::ButtonRequest * msg){ + MDEBUG("on_button_request"); + device.on_button_request(); + return std::make_shared(); + } + + std::shared_ptr trezor_protocol_callback::on_pin_matrix_request(const messages::common::PinMatrixRequest * msg){ + MDEBUG("on_pin_request"); + epee::wipeable_string pin; + device.on_pin_request(pin); + auto resp = std::make_shared(); + resp->set_pin(pin.data(), pin.size()); + return resp; + } + + std::shared_ptr trezor_protocol_callback::on_passphrase_request(const messages::common::PassphraseRequest * msg){ + MDEBUG("on_passhprase_request"); + epee::wipeable_string passphrase; + device.on_passphrase_request(msg->on_device(), passphrase); + auto resp = std::make_shared(); + if (!msg->on_device()){ + resp->set_passphrase(passphrase.data(), passphrase.size()); + } + return resp; + } + + std::shared_ptr trezor_protocol_callback::on_passphrase_state_request(const messages::common::PassphraseStateRequest * msg){ + MDEBUG("on_passhprase_state_request"); + device.on_passphrase_state_request(msg->state()); + return std::make_shared(); + } + + const uint32_t device_trezor_base::DEFAULT_BIP44_PATH[] = {0x8000002c, 0x80000080, 0x80000000}; + + device_trezor_base::device_trezor_base() { + + } + + device_trezor_base::~device_trezor_base() { + try { + disconnect(); + release(); + } catch(std::exception const& e){ + MERROR("Could not disconnect and release: " << e.what()); + } + } + + /* ======================================================================= */ + /* SETUP/TEARDOWN */ + /* ======================================================================= */ + + bool device_trezor_base::reset() { + return false; + } + + bool device_trezor_base::set_name(const std::string & name) { + this->full_name = name; + this->name = ""; + + auto delim = name.find(':'); + if (delim != std::string::npos && delim + 1 < name.length()) { + this->name = name.substr(delim + 1); + } + + return true; + } + + const std::string device_trezor_base::get_name() const { + if (this->full_name.empty()) { + return std::string("name).append(">"); + } + return this->full_name; + } + + bool device_trezor_base::init() { + if (!release()){ + MERROR("Release failed"); + return false; + } + + if (!m_protocol_callback){ + m_protocol_callback = std::make_shared(*this); + } + return true; + } + + bool device_trezor_base::release() { + try { + disconnect(); + return true; + + } catch(std::exception const& e){ + MERROR("Release exception: " << e.what()); + return false; + } + } + + bool device_trezor_base::connect() { + disconnect(); + + // Enumerate all available devices + try { + hw::trezor::t_transport_vect trans; + + MDEBUG("Enumerating Trezor devices..."); + enumerate(trans); + + MDEBUG("Enumeration yielded " << trans.size() << " devices"); + for (auto &cur : trans) { + MDEBUG(" device: " << *(cur.get())); + std::string cur_path = cur->get_path(); + if (boost::starts_with(cur_path, this->name)) { + MDEBUG("Device Match: " << cur_path); + m_transport = cur; + break; + } + } + + if (!m_transport) { + MERROR("No matching Trezor device found. Device specifier: \"" + this->name + "\""); + return false; + } + + m_transport->open(); + return true; + + } catch(std::exception const& e){ + MERROR("Open exception: " << e.what()); + return false; + } + } + + bool device_trezor_base::disconnect() { + if (m_transport){ + try { + m_transport->close(); + m_transport = nullptr; + + } catch(std::exception const& e){ + MERROR("Disconnect exception: " << e.what()); + m_transport = nullptr; + return false; + } + } + return true; + } + + /* ======================================================================= */ + /* LOCKER */ + /* ======================================================================= */ + + //lock the device for a long sequence + void device_trezor_base::lock() { + MTRACE("Ask for LOCKING for device " << this->name << " in thread "); + device_locker.lock(); + MTRACE("Device " << this->name << " LOCKed"); + } + + //lock the device for a long sequence + bool device_trezor_base::try_lock() { + MTRACE("Ask for LOCKING(try) for device " << this->name << " in thread "); + bool r = device_locker.try_lock(); + if (r) { + MTRACE("Device " << this->name << " LOCKed(try)"); + } else { + MDEBUG("Device " << this->name << " not LOCKed(try)"); + } + return r; + } + + //unlock the device + void device_trezor_base::unlock() { + MTRACE("Ask for UNLOCKING for device " << this->name << " in thread "); + device_locker.unlock(); + MTRACE("Device " << this->name << " UNLOCKed"); + } + + /* ======================================================================= */ + /* Helpers */ + /* ======================================================================= */ + + void device_trezor_base::require_connected(){ + if (!m_transport){ + throw exc::NotConnectedException(); + } + } + + void device_trezor_base::call_ping_unsafe(){ + auto pingMsg = std::make_shared(); + pingMsg->set_message("PING"); + + auto success = this->client_exchange(pingMsg); // messages::MessageType_Success + MDEBUG("Ping response " << success->message()); + (void)success; + } + + void device_trezor_base::test_ping(){ + require_connected(); + + try { + this->call_ping_unsafe(); + + } catch(exc::TrezorException const& e){ + MINFO("Trezor does not respond: " << e.what()); + throw exc::DeviceNotResponsiveException(std::string("Trezor not responding: ") + e.what()); + } + } + + /* ======================================================================= */ + /* TREZOR PROTOCOL */ + /* ======================================================================= */ + + bool device_trezor_base::ping() { + AUTO_LOCK_CMD(); + if (!m_transport){ + MINFO("Ping failed, device not connected"); + return false; + } + + try { + this->call_ping_unsafe(); + return true; + + } catch(std::exception const& e) { + MERROR("Ping failed, exception thrown " << e.what()); + } catch(...){ + MERROR("Ping failed, general exception thrown" << boost::current_exception_diagnostic_information()); + } + + return false; + } + + void device_trezor_base::on_button_request() + { + if (m_callback){ + m_callback->on_button_request(); + } + } + + void device_trezor_base::on_pin_request(epee::wipeable_string & pin) + { + if (m_callback){ + m_callback->on_pin_request(pin); + } + } + + void device_trezor_base::on_passphrase_request(bool on_device, epee::wipeable_string & passphrase) + { + if (m_callback){ + m_callback->on_passphrase_request(on_device, passphrase); + } + } + + void device_trezor_base::on_passphrase_state_request(const std::string & state) + { + if (m_callback){ + m_callback->on_passphrase_state_request(state); + } + } + +#endif //WITH_DEVICE_TREZOR +}} diff --git a/src/device_trezor/device_trezor_base.hpp b/src/device_trezor/device_trezor_base.hpp new file mode 100644 index 00000000000..644a4933225 --- /dev/null +++ b/src/device_trezor/device_trezor_base.hpp @@ -0,0 +1,301 @@ +// Copyright (c) 2017-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef MONERO_DEVICE_TREZOR_BASE_H +#define MONERO_DEVICE_TREZOR_BASE_H + + +#include +#include +#include "device/device.hpp" +#include "device/device_default.hpp" +#include "device/device_cold.hpp" +#include +#include +#include +#include "cryptonote_config.h" +#include "trezor.hpp" + +//automatic lock one more level on device ensuring the current thread is allowed to use it +#define AUTO_LOCK_CMD() \ + /* lock both mutexes without deadlock*/ \ + boost::lock(device_locker, command_locker); \ + /* make sure both already-locked mutexes are unlocked at the end of scope */ \ + boost::lock_guard lock1(device_locker, boost::adopt_lock); \ + boost::lock_guard lock2(command_locker, boost::adopt_lock) + + +namespace hw { +namespace trezor { + +#if WITH_DEVICE_TREZOR + class device_trezor_base; + + /** + * Trezor device callbacks + */ + class trezor_callback { + public: + virtual void on_button_request() {}; + virtual void on_pin_request(epee::wipeable_string & pin) {}; + virtual void on_passphrase_request(bool on_device, epee::wipeable_string & passphrase) {}; + virtual void on_passphrase_state_request(const std::string & state) {}; + }; + + /** + * Default Trezor protocol client callback + */ + class trezor_protocol_callback { + protected: + device_trezor_base & device; + + public: + explicit trezor_protocol_callback(device_trezor_base & device): device(device) {} + + std::shared_ptr on_button_request(const messages::common::ButtonRequest * msg); + std::shared_ptr on_pin_matrix_request(const messages::common::PinMatrixRequest * msg); + std::shared_ptr on_passphrase_request(const messages::common::PassphraseRequest * msg); + std::shared_ptr on_passphrase_state_request(const messages::common::PassphraseStateRequest * msg); + + std::shared_ptr on_message(const google::protobuf::Message * msg, messages::MessageType message_type){ + MDEBUG("on_general_message"); + return on_message_dispatch(msg, message_type); + } + + std::shared_ptr on_message_dispatch(const google::protobuf::Message * msg, messages::MessageType message_type){ + if (message_type == messages::MessageType_ButtonRequest){ + return on_button_request(dynamic_cast(msg)); + } else if (message_type == messages::MessageType_PassphraseRequest) { + return on_passphrase_request(dynamic_cast(msg)); + } else if (message_type == messages::MessageType_PassphraseStateRequest) { + return on_passphrase_state_request(dynamic_cast(msg)); + } else if (message_type == messages::MessageType_PinMatrixRequest) { + return on_pin_matrix_request(dynamic_cast(msg)); + } else { + return nullptr; + } + } + }; + + /** + * TREZOR device template with basic functions + */ + class device_trezor_base : public hw::core::device_default { + protected: + + // Locker for concurrent access + mutable boost::recursive_mutex device_locker; + mutable boost::mutex command_locker; + + std::shared_ptr m_transport; + std::shared_ptr m_protocol_callback; + std::shared_ptr m_callback; + + std::string full_name; + + cryptonote::network_type network_type; + + // + // Internal methods + // + + void require_connected(); + void call_ping_unsafe(); + void test_ping(); + + /** + * Client communication wrapper, handles specific Trezor protocol. + * + * @throws UnexpectedMessageException if the response message type is different than expected. + * Exception contains message type and the message itself. + */ + template + std::shared_ptr + client_exchange(const std::shared_ptr &req, + const boost::optional & resp_type = boost::none, + const boost::optional> & resp_types = boost::none, + const boost::optional & resp_type_ptr = boost::none, + bool open_session = false, + unsigned depth=0) + { + // Require strictly protocol buffers response in the template. + BOOST_STATIC_ASSERT(boost::is_base_of::value); + const bool accepting_base = boost::is_same::value; + if (resp_types && !accepting_base){ + throw std::invalid_argument("Cannot specify list of accepted types and not using generic response"); + } + + // Open session if required + if (open_session && depth == 0){ + try { + m_transport->open(); + } catch (const std::exception& e) { + std::throw_with_nested(exc::SessionException("Could not open session")); + } + } + + // Scoped session closer + BOOST_SCOPE_EXIT_ALL(&, this) { + if (open_session && depth == 0){ + this->getTransport()->close(); + } + }; + + // Write the request + CHECK_AND_ASSERT_THROW_MES(req, "Request is null"); + this->getTransport()->write(*req); + + // Read the response + std::shared_ptr msg_resp; + hw::trezor::messages::MessageType msg_resp_type; + + // We may have several roundtrips with the handler + this->getTransport()->read(msg_resp, &msg_resp_type); + if (resp_type_ptr){ + *(resp_type_ptr.get()) = msg_resp_type; + } + + // Determine type of expected message response + messages::MessageType required_type = accepting_base ? messages::MessageType_Success : + (resp_type ? resp_type.get() : MessageMapper::get_message_wire_number()); + + if (msg_resp_type == messages::MessageType_Failure) { + throw_failure_exception(dynamic_cast(msg_resp.get())); + + } else if (!accepting_base && msg_resp_type == required_type) { + return message_ptr_retype(msg_resp); + + } else { + auto resp = this->getProtocolCallback()->on_message(msg_resp.get(), msg_resp_type); + if (resp) { + return this->client_exchange(resp, boost::none, resp_types, resp_type_ptr, false, depth + 1); + + } else if (accepting_base && (!resp_types || + std::find(resp_types.get().begin(), resp_types.get().end(), msg_resp_type) != resp_types.get().end())) { + return message_ptr_retype(msg_resp); + + } else { + throw exc::UnexpectedMessageException(msg_resp_type, msg_resp); + } + } + } + + /** + * Utility method to set address_n and network type to the message requets. + */ + template + void set_msg_addr(t_message * msg, + const boost::optional> & path = boost::none, + const boost::optional & network_type = boost::none) + { + CHECK_AND_ASSERT_THROW_MES(msg, "Message is null"); + msg->clear_address_n(); + if (path){ + for(auto x : path.get()){ + msg->add_address_n(x); + } + } else { + for (unsigned int i : DEFAULT_BIP44_PATH) { + msg->add_address_n(i); + } + } + + if (network_type){ + msg->set_network_type(static_cast(network_type.get())); + } else { + msg->set_network_type(static_cast(this->network_type)); + } + } + + public: + device_trezor_base(); + ~device_trezor_base() override; + + device_trezor_base(const device_trezor_base &device) = delete ; + device_trezor_base& operator=(const device_trezor_base &device) = delete; + + explicit operator bool() const override {return true;} + device_type get_type() const override {return device_type::TREZOR;}; + + bool reset(); + + // Default derivation path for Monero + static const uint32_t DEFAULT_BIP44_PATH[3]; + + std::shared_ptr getTransport(){ + return m_transport; + } + + std::shared_ptr getProtocolCallback(){ + return m_protocol_callback; + } + + std::shared_ptr getCallback(){ + return m_callback; + } + + /* ======================================================================= */ + /* SETUP/TEARDOWN */ + /* ======================================================================= */ + bool set_name(const std::string &name) override; + + const std::string get_name() const override; + bool init() override; + bool release() override; + bool connect() override; + bool disconnect() override; + + /* ======================================================================= */ + /* LOCKER */ + /* ======================================================================= */ + void lock() override; + void unlock() override; + bool try_lock() override; + + /* ======================================================================= */ + /* TREZOR PROTOCOL */ + /* ======================================================================= */ + + /** + * Device ping, no-throw + */ + bool ping(); + + // Protocol callbacks + void on_button_request(); + void on_pin_request(epee::wipeable_string & pin); + void on_passphrase_request(bool on_device, epee::wipeable_string & passphrase); + void on_passphrase_state_request(const std::string & state); + }; + +#endif + +} +} +#endif //MONERO_DEVICE_TREZOR_BASE_H diff --git a/src/device_trezor/trezor.hpp b/src/device_trezor/trezor.hpp new file mode 100644 index 00000000000..8abdd2c18de --- /dev/null +++ b/src/device_trezor/trezor.hpp @@ -0,0 +1,44 @@ +// Copyright (c) 2017-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef MONERO_TREZOR_HPP +#define MONERO_TREZOR_HPP + +#include "trezor/trezor_defs.hpp" + +#ifdef HAVE_PROTOBUF +#include "trezor/transport.hpp" +#include "trezor/messages/messages.pb.h" +#include "trezor/messages/messages-common.pb.h" +#include "trezor/messages/messages-management.pb.h" +#include "trezor/messages/messages-monero.pb.h" +#include "trezor/protocol.hpp" +#endif + +#endif //MONERO_TREZOR_HPP diff --git a/src/device_trezor/trezor/exceptions.hpp b/src/device_trezor/trezor/exceptions.hpp new file mode 100644 index 00000000000..197dc43a422 --- /dev/null +++ b/src/device_trezor/trezor/exceptions.hpp @@ -0,0 +1,193 @@ +// Copyright (c) 2017-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef MONERO_EXCEPTIONS_H +#define MONERO_EXCEPTIONS_H + +#include +#include +#include + +namespace hw { +namespace trezor { +namespace exc { + + class SecurityException : public std::exception { + protected: + boost::optional reason; + + public: + SecurityException(): reason("General Security exception"){} + explicit SecurityException(std::string what): reason(what){} + + virtual const char* what() const throw() { + return reason.get().c_str(); + } + }; + + class Poly1305TagInvalid: public SecurityException { + public: + using SecurityException::SecurityException; + Poly1305TagInvalid(): SecurityException("Poly1305 authentication tag invalid"){} + }; + + class TrezorException : public std::exception { + protected: + boost::optional reason; + + public: + TrezorException(): reason("General Trezor exception"){} + explicit TrezorException(std::string what): reason(what){} + + virtual const char* what() const throw() { + return reason.get().c_str(); + } + }; + + class CommunicationException: public TrezorException { + public: + using TrezorException::TrezorException; + CommunicationException(): TrezorException("Trezor communication error"){} + }; + + class EncodingException: public CommunicationException { + public: + using CommunicationException::CommunicationException; + EncodingException(): CommunicationException("Trezor message encoding error"){} + }; + + class NotConnectedException : public CommunicationException { + public: + using CommunicationException::CommunicationException; + NotConnectedException(): CommunicationException("Trezor not connected"){} + }; + + class DeviceNotResponsiveException : public CommunicationException { + public: + using CommunicationException::CommunicationException; + DeviceNotResponsiveException(): CommunicationException("Trezor does not respond to ping"){} + }; + + class DeviceAcquireException : public CommunicationException { + public: + using CommunicationException::CommunicationException; + DeviceAcquireException(): CommunicationException("Trezor could not be acquired"){} + }; + + class SessionException: public CommunicationException { + public: + using CommunicationException::CommunicationException; + SessionException(): CommunicationException("Trezor session expired"){} + }; + + class TimeoutException: public CommunicationException { + public: + using CommunicationException::CommunicationException; + TimeoutException(): CommunicationException("Trezor communication timeout"){} + }; + + class ProtocolException: public CommunicationException { + public: + using CommunicationException::CommunicationException; + ProtocolException(): CommunicationException("Trezor protocol error"){} + }; + + // Communication protocol namespace + // Separated to distinguish between client and Trezor side exceptions. +namespace proto { + + class SecurityException : public ProtocolException { + public: + using ProtocolException::ProtocolException; + SecurityException(): ProtocolException("Security assertion violated in the protocol"){} + }; + + class FailureException : public ProtocolException { + private: + boost::optional code; + boost::optional message; + public: + using ProtocolException::ProtocolException; + FailureException(): ProtocolException("Trezor returned failure"){} + FailureException(boost::optional code, + boost::optional message) + : code(code), message(message) { + reason = "Trezor returned failure: code=" + + (code ? std::to_string(code.get()) : "") + + ", message=" + (message ? message.get() : ""); + }; + }; + + class UnexpectedMessageException : public FailureException { + public: + using FailureException::FailureException; + UnexpectedMessageException(): FailureException("Trezor claims unexpected message received"){} + }; + + class CancelledException : public FailureException { + public: + using FailureException::FailureException; + CancelledException(): FailureException("Trezor returned: cancelled operation"){} + }; + + class PinExpectedException : public FailureException { + public: + using FailureException::FailureException; + PinExpectedException(): FailureException("Trezor claims PIN is expected"){} + }; + + class InvalidPinException : public FailureException { + public: + using FailureException::FailureException; + InvalidPinException(): FailureException("Trezor claims PIN is invalid"){} + }; + + class NotEnoughFundsException : public FailureException { + public: + using FailureException::FailureException; + NotEnoughFundsException(): FailureException("Trezor claims not enough funds"){} + }; + + class NotInitializedException : public FailureException { + public: + using FailureException::FailureException; + NotInitializedException(): FailureException("Trezor claims not initialized"){} + }; + + class FirmwareErrorException : public FailureException { + public: + using FailureException::FailureException; + FirmwareErrorException(): FailureException("Trezor returned firmware error"){} + }; + +} +} +} +} +#endif //MONERO_EXCEPTIONS_H diff --git a/src/device_trezor/trezor/messages/.gitignore b/src/device_trezor/trezor/messages/.gitignore new file mode 100644 index 00000000000..32f7a77e5e2 --- /dev/null +++ b/src/device_trezor/trezor/messages/.gitignore @@ -0,0 +1,2 @@ +# protobuf generated code +* diff --git a/src/device_trezor/trezor/messages_map.cpp b/src/device_trezor/trezor/messages_map.cpp new file mode 100644 index 00000000000..b0d1aa2540e --- /dev/null +++ b/src/device_trezor/trezor/messages_map.cpp @@ -0,0 +1,125 @@ +// Copyright (c) 2017-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +#include "messages_map.hpp" +#include "messages/messages.pb.h" +#include "messages/messages-common.pb.h" +#include "messages/messages-management.pb.h" +#include "messages/messages-monero.pb.h" + +using namespace std; +using namespace hw::trezor; + +namespace hw{ +namespace trezor +{ + + const char * TYPE_PREFIX = "MessageType_"; + const char * PACKAGES[] = { + "hw.trezor.messages.", + "hw.trezor.messages.common.", + "hw.trezor.messages.management.", + "hw.trezor.messages.monero." + }; + + google::protobuf::Message * MessageMapper::get_message(int wire_number) { + return MessageMapper::get_message(static_cast(wire_number)); + } + + google::protobuf::Message * MessageMapper::get_message(messages::MessageType wire_number) { + const string &messageTypeName = hw::trezor::messages::MessageType_Name(wire_number); + if (messageTypeName.empty()) { + throw exc::EncodingException(std::string("Message descriptor not found: ") + std::to_string(wire_number)); + } + + string messageName = messageTypeName.substr(strlen(TYPE_PREFIX)); + return MessageMapper::get_message(messageName); + } + + google::protobuf::Message * MessageMapper::get_message(const std::string & msg_name) { + // Each package instantiation so lookup works + hw::trezor::messages::common::Success::default_instance(); + hw::trezor::messages::management::Cancel::default_instance(); + hw::trezor::messages::monero::MoneroGetAddress::default_instance(); + + google::protobuf::Descriptor const * desc = nullptr; + for(const string &text : PACKAGES){ + desc = google::protobuf::DescriptorPool::generated_pool() + ->FindMessageTypeByName(text + msg_name); + if (desc != nullptr){ + break; + } + } + + if (desc == nullptr){ + throw exc::EncodingException(std::string("Message not found: ") + msg_name); + } + + google::protobuf::Message* message = + google::protobuf::MessageFactory::generated_factory() + ->GetPrototype(desc)->New(); + + return message; + +// // CODEGEN way, fast +// switch(wire_number){ +// case 501: +// return new messages::monero::MoneroTransactionSignRequest(); +// default: +// throw std::runtime_error("not implemented"); +// } +// +// // CODEGEN message -> number: specification +// // messages::MessageType get_message_wire_number(const messages::monero::MoneroTransactionSignRequest * msg) { return 501; } +// // messages::MessageType get_message_wire_number(const messages::management::ping * msg) +// + } + + messages::MessageType MessageMapper::get_message_wire_number(const google::protobuf::Message * msg){ + return MessageMapper::get_message_wire_number(msg->GetDescriptor()->name()); + } + + messages::MessageType MessageMapper::get_message_wire_number(const google::protobuf::Message & msg){ + return MessageMapper::get_message_wire_number(msg.GetDescriptor()->name()); + } + + messages::MessageType MessageMapper::get_message_wire_number(const std::string & msg_name){ + string enumMessageName = std::string(TYPE_PREFIX) + msg_name; + + messages::MessageType res; + bool r = hw::trezor::messages::MessageType_Parse(enumMessageName, &res); + if (!r){ + throw exc::EncodingException(std::string("Message ") + msg_name + " not found"); + } + + return res; + } + +} +} diff --git a/src/device_trezor/trezor/messages_map.hpp b/src/device_trezor/trezor/messages_map.hpp new file mode 100644 index 00000000000..f61338f09f9 --- /dev/null +++ b/src/device_trezor/trezor/messages_map.hpp @@ -0,0 +1,94 @@ +// Copyright (c) 2017-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef MONERO_MESSAGES_MAP_H +#define MONERO_MESSAGES_MAP_H + +#include +#include +#include +#include "exceptions.hpp" + +#include "trezor_defs.hpp" + +#include +#include +#include +#include +#include +#include "google/protobuf/descriptor.pb.h" + +#include "messages/messages.pb.h" + +namespace hw { +namespace trezor { + + class MessageMapper{ + public: + MessageMapper() { + + } + + static ::google::protobuf::Message * get_message(int wire_number); + static ::google::protobuf::Message * get_message(messages::MessageType); + static ::google::protobuf::Message * get_message(const std::string & msg_name); + static messages::MessageType get_message_wire_number(const google::protobuf::Message * msg); + static messages::MessageType get_message_wire_number(const google::protobuf::Message & msg); + static messages::MessageType get_message_wire_number(const std::string & msg_name); + + template + static messages::MessageType get_message_wire_number() { + BOOST_STATIC_ASSERT(boost::is_base_of::value); + return get_message_wire_number(t_message::default_instance().GetDescriptor()->name()); + } + }; + + template + std::shared_ptr message_ptr_retype(std::shared_ptr & in){ + BOOST_STATIC_ASSERT(boost::is_base_of::value); + if (!in){ + return nullptr; + } + + return std::dynamic_pointer_cast(in); + } + + template + std::shared_ptr message_ptr_retype_static(std::shared_ptr & in){ + BOOST_STATIC_ASSERT(boost::is_base_of::value); + if (!in){ + return nullptr; + } + + return std::static_pointer_cast(in); + } + +}} + +#endif //MONERO_MESSAGES_MAP_H diff --git a/src/device_trezor/trezor/protocol.cpp b/src/device_trezor/trezor/protocol.cpp new file mode 100644 index 00000000000..c4a92426cda --- /dev/null +++ b/src/device_trezor/trezor/protocol.cpp @@ -0,0 +1,891 @@ +// Copyright (c) 2017-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +#include "protocol.hpp" +#include +#include +#include +#include +#include +#include +#include +#include "cryptonote_config.h" +#include +#include +#include + +namespace hw{ +namespace trezor{ +namespace protocol{ + + std::string key_to_string(const ::crypto::ec_point & key){ + return std::string(key.data, sizeof(key.data)); + } + + std::string key_to_string(const ::crypto::ec_scalar & key){ + return std::string(key.data, sizeof(key.data)); + } + + std::string key_to_string(const ::crypto::hash & key){ + return std::string(key.data, sizeof(key.data)); + } + + std::string key_to_string(const ::rct::key & key){ + return std::string(reinterpret_cast(key.bytes), sizeof(key.bytes)); + } + + void string_to_key(::crypto::ec_scalar & key, const std::string & str){ + if (str.size() != sizeof(key.data)){ + throw std::invalid_argument(std::string("Key has to have ") + std::to_string(sizeof(key.data)) + " B"); + } + memcpy(key.data, str.data(), sizeof(key.data)); + } + + void string_to_key(::crypto::ec_point & key, const std::string & str){ + if (str.size() != sizeof(key.data)){ + throw std::invalid_argument(std::string("Key has to have ") + std::to_string(sizeof(key.data)) + " B"); + } + memcpy(key.data, str.data(), sizeof(key.data)); + } + + void string_to_key(::rct::key & key, const std::string & str){ + if (str.size() != sizeof(key.bytes)){ + throw std::invalid_argument(std::string("Key has to have ") + std::to_string(sizeof(key.bytes)) + " B"); + } + memcpy(key.bytes, str.data(), sizeof(key.bytes)); + } + +namespace crypto { +namespace chacha { + + void decrypt(const void* ciphertext, size_t length, const uint8_t* key, const uint8_t* iv, char* plaintext){ + if (length < 16){ + throw std::invalid_argument("Ciphertext length too small"); + } + + unsigned long long int cip_len = length; + auto r = crypto_aead_chacha20poly1305_ietf_decrypt( + reinterpret_cast(plaintext), &cip_len, nullptr, + static_cast(ciphertext), length, nullptr, 0, iv, key); + + if (r != 0){ + throw exc::Poly1305TagInvalid(); + } + } + +} +} + + +// Cold Key image sync +namespace ki { + + bool key_image_data(wallet_shim * wallet, + const std::vector & transfers, + std::vector & res) + { + for(auto & td : transfers){ + ::crypto::public_key tx_pub_key = wallet->get_tx_pub_key_from_received_outs(td); + const std::vector<::crypto::public_key> additional_tx_pub_keys = cryptonote::get_additional_tx_pub_keys_from_extra(td.m_tx); + + res.emplace_back(); + auto & cres = res.back(); + + cres.set_out_key(key_to_string(boost::get(td.m_tx.vout[td.m_internal_output_index].target).key)); + cres.set_tx_pub_key(key_to_string(tx_pub_key)); + cres.set_internal_output_index(td.m_internal_output_index); + for(auto & aux : additional_tx_pub_keys){ + cres.add_additional_tx_pub_keys(key_to_string(aux)); + } + } + + return true; + } + + std::string compute_hash(const MoneroTransferDetails & rr){ + KECCAK_CTX kck; + uint8_t md[32]; + + CHECK_AND_ASSERT_THROW_MES(rr.out_key().size() == 32, "Invalid out_key size"); + CHECK_AND_ASSERT_THROW_MES(rr.tx_pub_key().size() == 32, "Invalid tx_pub_key size"); + + keccak_init(&kck); + keccak_update(&kck, reinterpret_cast(rr.out_key().data()), 32); + keccak_update(&kck, reinterpret_cast(rr.tx_pub_key().data()), 32); + for (const auto &aux : rr.additional_tx_pub_keys()){ + CHECK_AND_ASSERT_THROW_MES(aux.size() == 32, "Invalid aux size"); + keccak_update(&kck, reinterpret_cast(aux.data()), 32); + } + + auto index_serialized = tools::get_varint_data(rr.internal_output_index()); + keccak_update(&kck, reinterpret_cast(index_serialized.data()), index_serialized.size()); + keccak_finish(&kck, md); + return std::string(reinterpret_cast(md), sizeof(md)); + } + + void generate_commitment(std::vector & mtds, + const std::vector & transfers, + std::shared_ptr & req) + { + req = std::make_shared(); + + KECCAK_CTX kck; + uint8_t final_hash[32]; + keccak_init(&kck); + + for(auto &cur : mtds){ + auto hash = compute_hash(cur); + keccak_update(&kck, reinterpret_cast(hash.data()), hash.size()); + } + keccak_finish(&kck, final_hash); + + req = std::make_shared(); + req->set_hash(std::string(reinterpret_cast(final_hash), 32)); + req->set_num(transfers.size()); + + std::unordered_map> sub_indices; + for (auto &cur : transfers){ + auto search = sub_indices.emplace(cur.m_subaddr_index.major, std::set()); + auto & st = search.first->second; + st.insert(cur.m_subaddr_index.minor); + } + + for (auto& x: sub_indices){ + auto subs = req->add_subs(); + subs->set_account(x.first); + for(auto minor : x.second){ + subs->add_minor_indices(minor); + } + } + } + +} + +// Cold transaction signing +namespace tx { + + void translate_address(MoneroAccountPublicAddress * dst, const cryptonote::account_public_address * src){ + dst->set_view_public_key(key_to_string(src->m_view_public_key)); + dst->set_spend_public_key(key_to_string(src->m_spend_public_key)); + } + + void translate_dst_entry(MoneroTransactionDestinationEntry * dst, const cryptonote::tx_destination_entry * src){ + dst->set_amount(src->amount); + dst->set_is_subaddress(src->is_subaddress); + translate_address(dst->mutable_addr(), &(src->addr)); + } + + void translate_src_entry(MoneroTransactionSourceEntry * dst, const cryptonote::tx_source_entry * src){ + for(auto & cur : src->outputs){ + auto out = dst->add_outputs(); + out->set_idx(cur.first); + translate_rct_key(out->mutable_key(), &(cur.second)); + } + + dst->set_real_output(src->real_output); + dst->set_real_out_tx_key(key_to_string(src->real_out_tx_key)); + for(auto & cur : src->real_out_additional_tx_keys){ + dst->add_real_out_additional_tx_keys(key_to_string(cur)); + } + + dst->set_real_output_in_tx_index(src->real_output_in_tx_index); + dst->set_amount(src->amount); + dst->set_rct(src->rct); + dst->set_mask(key_to_string(src->mask)); + translate_klrki(dst->mutable_multisig_klrki(), &(src->multisig_kLRki)); + } + + void translate_klrki(MoneroMultisigKLRki * dst, const rct::multisig_kLRki * src){ + dst->set_k(key_to_string(src->k)); + dst->set_l(key_to_string(src->L)); + dst->set_r(key_to_string(src->R)); + dst->set_ki(key_to_string(src->ki)); + } + + void translate_rct_key(MoneroRctKey * dst, const rct::ctkey * src){ + dst->set_dest(key_to_string(src->dest)); + dst->set_commitment(key_to_string(src->mask)); + } + + std::string hash_addr(const MoneroAccountPublicAddress * addr, boost::optional amount, boost::optional is_subaddr){ + return hash_addr(addr->spend_public_key(), addr->view_public_key(), amount, is_subaddr); + } + + std::string hash_addr(const std::string & spend_key, const std::string & view_key, boost::optional amount, boost::optional is_subaddr){ + ::crypto::public_key spend{}, view{}; + if (spend_key.size() != 32 || view_key.size() != 32){ + throw std::invalid_argument("Public keys have invalid sizes"); + } + + memcpy(spend.data, spend_key.data(), 32); + memcpy(view.data, view_key.data(), 32); + return hash_addr(&spend, &view, amount, is_subaddr); + } + + std::string hash_addr(const ::crypto::public_key * spend_key, const ::crypto::public_key * view_key, boost::optional amount, boost::optional is_subaddr){ + char buff[64+8+1]; + size_t offset = 0; + + memcpy(buff + offset, spend_key->data, 32); offset += 32; + memcpy(buff + offset, view_key->data, 32); offset += 32; + + if (amount){ + memcpy(buff + offset, (uint8_t*) &(amount.get()), sizeof(amount.get())); offset += sizeof(amount.get()); + } + + if (is_subaddr){ + buff[offset] = is_subaddr.get(); + offset += 1; + } + + return std::string(buff, offset); + } + + TData::TData() { + in_memory = false; + rsig_type = 0; + cur_input_idx = 0; + cur_output_idx = 0; + cur_batch_idx = 0; + cur_output_in_batch_idx = 0; + } + + Signer::Signer(wallet_shim *wallet2, const unsigned_tx_set * unsigned_tx, size_t tx_idx, hw::tx_aux_data * aux_data) { + m_wallet2 = wallet2; + m_unsigned_tx = unsigned_tx; + m_aux_data = aux_data; + m_tx_idx = tx_idx; + m_ct.tx_data = cur_tx(); + m_multisig = false; + } + + void Signer::extract_payment_id(){ + const std::vector& tx_extra = cur_tx().extra; + m_ct.tsx_data.set_payment_id(""); + + std::vector tx_extra_fields; + cryptonote::parse_tx_extra(tx_extra, tx_extra_fields); // ok if partially parsed + cryptonote::tx_extra_nonce extra_nonce; + + ::crypto::hash payment_id{}; + if (find_tx_extra_field_by_type(tx_extra_fields, extra_nonce)) + { + ::crypto::hash8 payment_id8{}; + if(cryptonote::get_encrypted_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id8)) + { + m_ct.tsx_data.set_payment_id(std::string(payment_id8.data, 8)); + } + else if (cryptonote::get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id)) + { + m_ct.tsx_data.set_payment_id(std::string(payment_id.data, 32)); + } + } + } + + static unsigned get_rsig_type(bool use_bulletproof, size_t num_outputs){ + if (!use_bulletproof){ + return rct::RangeProofBorromean; + } else if (num_outputs > BULLETPROOF_MAX_OUTPUTS){ + return rct::RangeProofMultiOutputBulletproof; + } else { + return rct::RangeProofPaddedBulletproof; + } + } + + static void generate_rsig_batch_sizes(std::vector &batches, unsigned rsig_type, size_t num_outputs){ + size_t amount_batched = 0; + + while(amount_batched < num_outputs){ + if (rsig_type == rct::RangeProofBorromean || rsig_type == rct::RangeProofBulletproof) { + batches.push_back(1); + amount_batched += 1; + + } else if (rsig_type == rct::RangeProofPaddedBulletproof){ + if (num_outputs > BULLETPROOF_MAX_OUTPUTS){ + throw std::invalid_argument("BP padded can support only BULLETPROOF_MAX_OUTPUTS statements"); + } + batches.push_back(num_outputs); + amount_batched += num_outputs; + + } else if (rsig_type == rct::RangeProofMultiOutputBulletproof){ + size_t batch_size = 1; + while (batch_size * 2 + amount_batched <= num_outputs && batch_size * 2 <= BULLETPROOF_MAX_OUTPUTS){ + batch_size *= 2; + } + batch_size = std::min(batch_size, num_outputs - amount_batched); + batches.push_back(batch_size); + amount_batched += batch_size; + + } else { + throw std::invalid_argument("Unknown rsig type"); + } + } + } + + void Signer::compute_integrated_indices(TsxData * tsx_data){ + if (m_aux_data == nullptr || m_aux_data->tx_recipients.empty()){ + return; + } + + auto & chg = tsx_data->change_dts(); + std::string change_hash = hash_addr(&chg.addr(), chg.amount(), chg.is_subaddress()); + + std::vector integrated_indices; + std::set integrated_hashes; + for (auto & cur : m_aux_data->tx_recipients){ + if (!cur.has_payment_id){ + continue; + } + integrated_hashes.emplace(hash_addr(&cur.address.m_spend_public_key, &cur.address.m_view_public_key)); + } + + ssize_t idx = -1; + for (auto & cur : tsx_data->outputs()){ + idx += 1; + + std::string c_hash = hash_addr(&cur.addr(), cur.amount(), cur.is_subaddress()); + if (c_hash == change_hash || cur.is_subaddress()){ + continue; + } + + c_hash = hash_addr(&cur.addr()); + if (integrated_hashes.find(c_hash) != integrated_hashes.end()){ + integrated_indices.push_back((uint32_t)idx); + } + } + + if (!integrated_indices.empty()){ + assign_to_repeatable(tsx_data->mutable_integrated_indices(), integrated_indices.begin(), integrated_indices.end()); + } + } + + std::shared_ptr Signer::step_init(){ + // extract payment ID from construction data + auto & tsx_data = m_ct.tsx_data; + auto & tx = cur_tx(); + + m_ct.tx.version = 2; + m_ct.tx.unlock_time = tx.unlock_time; + + tsx_data.set_version(1); + tsx_data.set_unlock_time(tx.unlock_time); + tsx_data.set_num_inputs(static_cast(tx.sources.size())); + tsx_data.set_mixin(static_cast(tx.sources[0].outputs.size() - 1)); + tsx_data.set_account(tx.subaddr_account); + assign_to_repeatable(tsx_data.mutable_minor_indices(), tx.subaddr_indices.begin(), tx.subaddr_indices.end()); + + // Rsig decision + auto rsig_data = tsx_data.mutable_rsig_data(); + m_ct.rsig_type = get_rsig_type(tx.use_bulletproofs, tx.splitted_dsts.size()); + rsig_data->set_rsig_type(m_ct.rsig_type); + + generate_rsig_batch_sizes(m_ct.grouping_vct, m_ct.rsig_type, tx.splitted_dsts.size()); + assign_to_repeatable(rsig_data->mutable_grouping(), m_ct.grouping_vct.begin(), m_ct.grouping_vct.end()); + + translate_dst_entry(tsx_data.mutable_change_dts(), &(tx.change_dts)); + for(auto & cur : tx.splitted_dsts){ + auto dst = tsx_data.mutable_outputs()->Add(); + translate_dst_entry(dst, &cur); + } + + compute_integrated_indices(&tsx_data); + + int64_t fee = 0; + for(auto & cur_in : tx.sources){ + fee += cur_in.amount; + } + for(auto & cur_out : tx.splitted_dsts){ + fee -= cur_out.amount; + } + if (fee < 0){ + throw std::invalid_argument("Fee cannot be negative"); + } + + tsx_data.set_fee(static_cast(fee)); + this->extract_payment_id(); + + auto init_req = std::make_shared(); + init_req->set_version(0); + init_req->mutable_tsx_data()->CopyFrom(tsx_data); + return init_req; + } + + void Signer::step_init_ack(std::shared_ptr ack){ + m_ct.in_memory = false; + if (ack->has_rsig_data()){ + m_ct.rsig_param = std::make_shared(ack->rsig_data()); + } + + assign_from_repeatable(&(m_ct.tx_out_entr_hmacs), ack->hmacs().begin(), ack->hmacs().end()); + } + + std::shared_ptr Signer::step_set_input(size_t idx){ + CHECK_AND_ASSERT_THROW_MES(idx < cur_tx().sources.size(), "Invalid source index"); + m_ct.cur_input_idx = idx; + auto res = std::make_shared(); + translate_src_entry(res->mutable_src_entr(), &(cur_tx().sources[idx])); + return res; + } + + void Signer::step_set_input_ack(std::shared_ptr ack){ + auto & vini_str = ack->vini(); + + cryptonote::txin_v vini; + if (!cn_deserialize(vini_str.data(), vini_str.size(), vini)){ + throw exc::ProtocolException("Cannot deserialize vin[i]"); + } + + m_ct.tx.vin.emplace_back(vini); + m_ct.tx_in_hmacs.push_back(ack->vini_hmac()); + m_ct.pseudo_outs.push_back(ack->pseudo_out()); + m_ct.pseudo_outs_hmac.push_back(ack->pseudo_out_hmac()); + m_ct.alphas.push_back(ack->pseudo_out_alpha()); + m_ct.spend_encs.push_back(ack->spend_key()); + } + + void Signer::sort_ki(){ + const size_t input_size = cur_tx().sources.size(); + + m_ct.source_permutation.clear(); + for (size_t n = 0; n < input_size; ++n){ + m_ct.source_permutation.push_back(n); + } + + CHECK_AND_ASSERT_THROW_MES(m_ct.tx.vin.size() == input_size, "Invalid vector size"); + std::sort(m_ct.source_permutation.begin(), m_ct.source_permutation.end(), [&](const size_t i0, const size_t i1) { + const cryptonote::txin_to_key &tk0 = boost::get(m_ct.tx.vin[i0]); + const cryptonote::txin_to_key &tk1 = boost::get(m_ct.tx.vin[i1]); + return memcmp(&tk0.k_image, &tk1.k_image, sizeof(tk0.k_image)) > 0; + }); + + CHECK_AND_ASSERT_THROW_MES(m_ct.tx_in_hmacs.size() == input_size, "Invalid vector size"); + CHECK_AND_ASSERT_THROW_MES(m_ct.pseudo_outs.size() == input_size, "Invalid vector size"); + CHECK_AND_ASSERT_THROW_MES(m_ct.pseudo_outs_hmac.size() == input_size, "Invalid vector size"); + CHECK_AND_ASSERT_THROW_MES(m_ct.alphas.size() == input_size, "Invalid vector size"); + CHECK_AND_ASSERT_THROW_MES(m_ct.spend_encs.size() == input_size, "Invalid vector size"); + CHECK_AND_ASSERT_THROW_MES(m_ct.tx_data.sources.size() == input_size, "Invalid vector size"); + + tools::apply_permutation(m_ct.source_permutation, [&](size_t i0, size_t i1){ + std::swap(m_ct.tx.vin[i0], m_ct.tx.vin[i1]); + std::swap(m_ct.tx_in_hmacs[i0], m_ct.tx_in_hmacs[i1]); + std::swap(m_ct.pseudo_outs[i0], m_ct.pseudo_outs[i1]); + std::swap(m_ct.pseudo_outs_hmac[i0], m_ct.pseudo_outs_hmac[i1]); + std::swap(m_ct.alphas[i0], m_ct.alphas[i1]); + std::swap(m_ct.spend_encs[i0], m_ct.spend_encs[i1]); + std::swap(m_ct.tx_data.sources[i0], m_ct.tx_data.sources[i1]); + }); + } + + std::shared_ptr Signer::step_permutation(){ + sort_ki(); + + if (in_memory()){ + return nullptr; + } + + auto res = std::make_shared(); + assign_to_repeatable(res->mutable_perm(), m_ct.source_permutation.begin(), m_ct.source_permutation.end()); + + return res; + } + + void Signer::step_permutation_ack(std::shared_ptr ack){ + if (in_memory()){ + return; + } + } + + std::shared_ptr Signer::step_set_vini_input(size_t idx){ + if (in_memory()){ + return nullptr; + } + CHECK_AND_ASSERT_THROW_MES(idx < m_ct.tx_data.sources.size(), "Invalid transaction index"); + CHECK_AND_ASSERT_THROW_MES(idx < m_ct.tx.vin.size(), "Invalid transaction index"); + CHECK_AND_ASSERT_THROW_MES(idx < m_ct.tx_in_hmacs.size(), "Invalid transaction index"); + + m_ct.cur_input_idx = idx; + auto tx = m_ct.tx_data; + auto res = std::make_shared(); + auto & vini = m_ct.tx.vin[idx]; + translate_src_entry(res->mutable_src_entr(), &(tx.sources[idx])); + res->set_vini(cryptonote::t_serializable_object_to_blob(vini)); + res->set_vini_hmac(m_ct.tx_in_hmacs[idx]); + if (!in_memory()) { + CHECK_AND_ASSERT_THROW_MES(idx < m_ct.pseudo_outs.size(), "Invalid transaction index"); + CHECK_AND_ASSERT_THROW_MES(idx < m_ct.pseudo_outs_hmac.size(), "Invalid transaction index"); + res->set_pseudo_out(m_ct.pseudo_outs[idx]); + res->set_pseudo_out_hmac(m_ct.pseudo_outs_hmac[idx]); + } + + return res; + } + + void Signer::step_set_vini_input_ack(std::shared_ptr ack){ + if (in_memory()){ + return; + } + } + + std::shared_ptr Signer::step_all_inputs_set(){ + return std::make_shared(); + } + + void Signer::step_all_inputs_set_ack(std::shared_ptr ack){ + if (is_offloading()){ + // If offloading, expect rsig configuration. + if (!ack->has_rsig_data()){ + throw exc::ProtocolException("Rsig offloading requires rsig param"); + } + + auto & rsig_data = ack->rsig_data(); + if (!rsig_data.has_mask()){ + throw exc::ProtocolException("Gamma masks not present in offloaded version"); + } + + auto & mask = rsig_data.mask(); + if (mask.size() != 32 * num_outputs()){ + throw exc::ProtocolException("Invalid number of gamma masks"); + } + + m_ct.rsig_gamma.reserve(num_outputs()); + for(size_t c=0; c < num_outputs(); ++c){ + rct::key cmask{}; + memcpy(cmask.bytes, mask.data() + c * 32, 32); + m_ct.rsig_gamma.emplace_back(cmask); + } + } + } + + std::shared_ptr Signer::step_set_output(size_t idx){ + CHECK_AND_ASSERT_THROW_MES(idx < m_ct.tx_data.splitted_dsts.size(), "Invalid transaction index"); + CHECK_AND_ASSERT_THROW_MES(idx < m_ct.tx_out_entr_hmacs.size(), "Invalid transaction index"); + + m_ct.cur_output_idx = idx; + m_ct.cur_output_in_batch_idx += 1; // assumes sequential call to step_set_output() + + auto res = std::make_shared(); + auto & cur_dst = m_ct.tx_data.splitted_dsts[idx]; + translate_dst_entry(res->mutable_dst_entr(), &cur_dst); + res->set_dst_entr_hmac(m_ct.tx_out_entr_hmacs[idx]); + + // Range sig offloading to the host + if (!is_offloading()) { + return res; + } + + CHECK_AND_ASSERT_THROW_MES(m_ct.cur_batch_idx < m_ct.grouping_vct.size(), "Invalid batch index"); + if (m_ct.grouping_vct[m_ct.cur_batch_idx] > m_ct.cur_output_in_batch_idx) { + return res; + } + + auto rsig_data = res->mutable_rsig_data(); + auto batch_size = m_ct.grouping_vct[m_ct.cur_batch_idx]; + + if (!is_req_bulletproof()){ + if (batch_size > 1){ + throw std::invalid_argument("Borromean cannot batch outputs"); + } + + CHECK_AND_ASSERT_THROW_MES(idx < m_ct.rsig_gamma.size(), "Invalid gamma index"); + rct::key C{}, mask = m_ct.rsig_gamma[idx]; + auto genRsig = rct::proveRange(C, mask, cur_dst.amount); // TODO: rsig with given mask + auto serRsig = cn_serialize(genRsig); + m_ct.tx_out_rsigs.emplace_back(genRsig); + rsig_data->set_rsig(serRsig); + + } else { + std::vector amounts; + rct::keyV masks; + CHECK_AND_ASSERT_THROW_MES(idx + 1 >= batch_size, "Invalid index for batching"); + + for(size_t i = 0; i < batch_size; ++i){ + const size_t bidx = 1 + idx - batch_size + i; + CHECK_AND_ASSERT_THROW_MES(bidx < m_ct.tx_data.splitted_dsts.size(), "Invalid gamma index"); + CHECK_AND_ASSERT_THROW_MES(bidx < m_ct.rsig_gamma.size(), "Invalid gamma index"); + + amounts.push_back(m_ct.tx_data.splitted_dsts[bidx].amount); + masks.push_back(m_ct.rsig_gamma[bidx]); + } + + auto bp = bulletproof_PROVE(amounts, masks); + auto serRsig = cn_serialize(bp); + m_ct.tx_out_rsigs.emplace_back(bp); + rsig_data->set_rsig(serRsig); + } + + return res; + } + + void Signer::step_set_output_ack(std::shared_ptr ack){ + cryptonote::tx_out tx_out; + rct::rangeSig range_sig{}; + rct::Bulletproof bproof{}; + rct::ctkey out_pk{}; + rct::ecdhTuple ecdh{}; + + bool has_rsig = false; + std::string rsig_buff; + + if (ack->has_rsig_data()){ + auto & rsig_data = ack->rsig_data(); + + if (rsig_data.has_rsig() && !rsig_data.rsig().empty()){ + has_rsig = true; + rsig_buff = rsig_data.rsig(); + + } else if (rsig_data.rsig_parts_size() > 0){ + has_rsig = true; + for (const auto &it : rsig_data.rsig_parts()) { + rsig_buff += it; + } + } + } + + if (!cn_deserialize(ack->tx_out(), tx_out)){ + throw exc::ProtocolException("Cannot deserialize vout[i]"); + } + + if (!cn_deserialize(ack->out_pk(), out_pk)){ + throw exc::ProtocolException("Cannot deserialize out_pk"); + } + + if (!cn_deserialize(ack->ecdh_info(), ecdh)){ + throw exc::ProtocolException("Cannot deserialize ecdhtuple"); + } + + if (has_rsig && !is_req_bulletproof() && !cn_deserialize(rsig_buff, range_sig)){ + throw exc::ProtocolException("Cannot deserialize rangesig"); + } + + if (has_rsig && is_req_bulletproof() && !cn_deserialize(rsig_buff, bproof)){ + throw exc::ProtocolException("Cannot deserialize bulletproof rangesig"); + } + + m_ct.tx.vout.emplace_back(tx_out); + m_ct.tx_out_hmacs.push_back(ack->vouti_hmac()); + m_ct.tx_out_pk.emplace_back(out_pk); + m_ct.tx_out_ecdh.emplace_back(ecdh); + + if (!has_rsig){ + return; + } + + if (is_req_bulletproof()){ + CHECK_AND_ASSERT_THROW_MES(m_ct.cur_batch_idx < m_ct.grouping_vct.size(), "Invalid batch index"); + auto batch_size = m_ct.grouping_vct[m_ct.cur_batch_idx]; + for (size_t i = 0; i < batch_size; ++i){ + const size_t bidx = 1 + m_ct.cur_output_idx - batch_size + i; + CHECK_AND_ASSERT_THROW_MES(bidx < m_ct.tx_out_pk.size(), "Invalid out index"); + + rct::key commitment = m_ct.tx_out_pk[bidx].mask; + commitment = rct::scalarmultKey(commitment, rct::INV_EIGHT); + bproof.V.push_back(commitment); + } + + m_ct.tx_out_rsigs.emplace_back(bproof); + if (!rct::bulletproof_VERIFY(boost::get(m_ct.tx_out_rsigs.back()))) { + throw exc::ProtocolException("Returned range signature is invalid"); + } + + } else { + m_ct.tx_out_rsigs.emplace_back(range_sig); + + if (!rct::verRange(out_pk.mask, boost::get(m_ct.tx_out_rsigs.back()))) { + throw exc::ProtocolException("Returned range signature is invalid"); + } + } + + m_ct.cur_batch_idx += 1; + m_ct.cur_output_in_batch_idx = 0; + } + + std::shared_ptr Signer::step_all_outs_set(){ + return std::make_shared(); + } + + void Signer::step_all_outs_set_ack(std::shared_ptr ack, hw::device &hwdev){ + m_ct.rv = std::make_shared(); + m_ct.rv->txnFee = ack->rv().txn_fee(); + m_ct.rv->type = static_cast(ack->rv().rv_type()); + string_to_key(m_ct.rv->message, ack->rv().message()); + + // Extra copy + m_ct.tx.extra.clear(); + auto extra = ack->extra(); + auto extra_data = extra.data(); + m_ct.tx.extra.reserve(extra.size()); + for(size_t i = 0; i < extra.size(); ++i){ + m_ct.tx.extra.push_back(static_cast(extra_data[i])); + } + + ::crypto::hash tx_prefix_hash{}; + cryptonote::get_transaction_prefix_hash(m_ct.tx, tx_prefix_hash); + m_ct.tx_prefix_hash = key_to_string(tx_prefix_hash); + if (crypto_verify_32(reinterpret_cast(tx_prefix_hash.data), + reinterpret_cast(ack->tx_prefix_hash().data()))){ + throw exc::proto::SecurityException("Transaction prefix has does not match to the computed value"); + } + + // RctSig + auto num_sources = m_ct.tx_data.sources.size(); + if (is_simple() || is_req_bulletproof()){ + auto dst = &m_ct.rv->pseudoOuts; + if (is_bulletproof()){ + dst = &m_ct.rv->p.pseudoOuts; + } + + dst->clear(); + for (const auto &pseudo_out : m_ct.pseudo_outs) { + dst->emplace_back(); + string_to_key(dst->back(), pseudo_out); + } + + m_ct.rv->mixRing.resize(num_sources); + } else { + m_ct.rv->mixRing.resize(m_ct.tsx_data.mixin()); + m_ct.rv->mixRing[0].resize(num_sources); + } + + CHECK_AND_ASSERT_THROW_MES(m_ct.tx_out_pk.size() == m_ct.tx_out_ecdh.size(), "Invalid vector sizes"); + for(size_t i = 0; i < m_ct.tx_out_ecdh.size(); ++i){ + m_ct.rv->outPk.push_back(m_ct.tx_out_pk[i]); + m_ct.rv->ecdhInfo.push_back(m_ct.tx_out_ecdh[i]); + } + + for(size_t i = 0; i < m_ct.tx_out_rsigs.size(); ++i){ + if (is_bulletproof()){ + m_ct.rv->p.bulletproofs.push_back(boost::get(m_ct.tx_out_rsigs[i])); + } else { + m_ct.rv->p.rangeSigs.push_back(boost::get(m_ct.tx_out_rsigs[i])); + } + } + + rct::key hash_computed = rct::get_pre_mlsag_hash(*(m_ct.rv), hwdev); + auto & hash = ack->full_message_hash(); + + if (hash.size() != 32){ + throw exc::ProtocolException("Returned mlsag hash has invalid size"); + } + + if (crypto_verify_32(reinterpret_cast(hash_computed.bytes), + reinterpret_cast(hash.data()))){ + throw exc::proto::SecurityException("Computed MLSAG does not match"); + } + } + + std::shared_ptr Signer::step_sign_input(size_t idx){ + m_ct.cur_input_idx = idx; + + CHECK_AND_ASSERT_THROW_MES(idx < m_ct.tx_data.sources.size(), "Invalid transaction index"); + CHECK_AND_ASSERT_THROW_MES(idx < m_ct.tx.vin.size(), "Invalid transaction index"); + CHECK_AND_ASSERT_THROW_MES(idx < m_ct.tx_in_hmacs.size(), "Invalid transaction index"); + CHECK_AND_ASSERT_THROW_MES(idx < m_ct.alphas.size(), "Invalid transaction index"); + CHECK_AND_ASSERT_THROW_MES(idx < m_ct.spend_encs.size(), "Invalid transaction index"); + + auto res = std::make_shared(); + translate_src_entry(res->mutable_src_entr(), &(m_ct.tx_data.sources[idx])); + res->set_vini(cryptonote::t_serializable_object_to_blob(m_ct.tx.vin[idx])); + res->set_vini_hmac(m_ct.tx_in_hmacs[idx]); + res->set_pseudo_out_alpha(m_ct.alphas[idx]); + res->set_spend_key(m_ct.spend_encs[idx]); + if (!in_memory()){ + CHECK_AND_ASSERT_THROW_MES(idx < m_ct.pseudo_outs.size(), "Invalid transaction index"); + CHECK_AND_ASSERT_THROW_MES(idx < m_ct.pseudo_outs_hmac.size(), "Invalid transaction index"); + res->set_pseudo_out(m_ct.pseudo_outs[idx]); + res->set_pseudo_out_hmac(m_ct.pseudo_outs_hmac[idx]); + } + return res; + } + + void Signer::step_sign_input_ack(std::shared_ptr ack){ + rct::mgSig mg; + if (!cn_deserialize(ack->signature(), mg)){ + throw exc::ProtocolException("Cannot deserialize mg[i]"); + } + + m_ct.rv->p.MGs.push_back(mg); + } + + std::shared_ptr Signer::step_final(){ + m_ct.tx.rct_signatures = *(m_ct.rv); + return std::make_shared(); + } + + void Signer::step_final_ack(std::shared_ptr ack){ + if (m_multisig){ + auto & cout_key = ack->cout_key(); + for(auto & cur : m_ct.couts){ + if (cur.size() != 12 + 32){ + throw std::invalid_argument("Encrypted cout has invalid length"); + } + + char buff[32]; + auto data = cur.data(); + + crypto::chacha::decrypt(data + 12, 32, reinterpret_cast(cout_key.data()), reinterpret_cast(data), buff); + m_ct.couts_dec.emplace_back(buff, 32); + } + } + + m_ct.enc_salt1 = ack->salt(); + m_ct.enc_salt2 = ack->rand_mult(); + m_ct.enc_keys = ack->tx_enc_keys(); + } + + std::string Signer::store_tx_aux_info(){ + rapidjson::StringBuffer sb; + rapidjson::Writer writer(sb); + + rapidjson::Document json; + json.SetObject(); + + rapidjson::Value valueS(rapidjson::kStringType); + rapidjson::Value valueI(rapidjson::kNumberType); + + valueI.SetInt(1); + json.AddMember("version", valueI, json.GetAllocator()); + + valueS.SetString(m_ct.enc_salt1.c_str(), m_ct.enc_salt1.size()); + json.AddMember("salt1", valueS, json.GetAllocator()); + + valueS.SetString(m_ct.enc_salt2.c_str(), m_ct.enc_salt2.size()); + json.AddMember("salt2", valueS, json.GetAllocator()); + + valueS.SetString(m_ct.enc_keys.c_str(), m_ct.enc_keys.size()); + json.AddMember("enc_keys", valueS, json.GetAllocator()); + + json.Accept(writer); + return sb.GetString(); + } + + +} +} +} +} diff --git a/src/device_trezor/trezor/protocol.hpp b/src/device_trezor/trezor/protocol.hpp new file mode 100644 index 00000000000..99211efed57 --- /dev/null +++ b/src/device_trezor/trezor/protocol.hpp @@ -0,0 +1,300 @@ +// Copyright (c) 2017-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef MONERO_PROTOCOL_H +#define MONERO_PROTOCOL_H + +#include "trezor_defs.hpp" +#include "device/device_cold.hpp" +#include "messages_map.hpp" +#include "transport.hpp" +#include "wallet/wallet2.h" + +namespace hw{ +namespace trezor{ +namespace protocol{ + + std::string key_to_string(const ::crypto::ec_point & key); + std::string key_to_string(const ::crypto::ec_scalar & key); + std::string key_to_string(const ::crypto::hash & key); + std::string key_to_string(const ::rct::key & key); + + void string_to_key(::crypto::ec_scalar & key, const std::string & str); + void string_to_key(::crypto::ec_point & key, const std::string & str); + void string_to_key(::rct::key & key, const std::string & str); + + template + void assign_to_repeatable(::google::protobuf::RepeatedField * dst, const InputIterator begin, const InputIterator end){ + for (InputIterator it = begin; it != end; it++) { + auto s = dst->Add(); + *s = *it; + } + } + + template + void assign_from_repeatable(std::vector * dst, const InputIterator begin, const InputIterator end){ + for (InputIterator it = begin; it != end; it++) { + dst->push_back(*it); + } + }; + + template + bool cn_deserialize(const void * buff, size_t len, T & dst){ + std::stringstream ss; + ss.write(static_cast(buff), len); //ss << tx_blob; + binary_archive ba(ss); + bool r = ::serialization::serialize(ba, dst); + return r; + } + + template + bool cn_deserialize(const std::string & str, T & dst){ + return cn_deserialize(str.data(), str.size(), dst); + } + + template + std::string cn_serialize(T & obj){ + std::ostringstream oss; + binary_archive oar(oss); + bool success = ::serialization::serialize(oar, obj); + if (!success){ + throw exc::EncodingException("Could not CN serialize given object"); + } + return oss.str(); + } + +// Crypto / encryption +namespace crypto { +namespace chacha { + + /** + * Chacha20Poly1305 decryption with tag verification. RFC 7539. + */ + void decrypt(const void* ciphertext, size_t length, const uint8_t* key, const uint8_t* iv, char* plaintext); + +} +} + + +// Cold Key image sync +namespace ki { + + using MoneroTransferDetails = messages::monero::MoneroKeyImageSyncStepRequest_MoneroTransferDetails; + using MoneroSubAddressIndicesList = messages::monero::MoneroKeyImageExportInitRequest_MoneroSubAddressIndicesList; + using MoneroExportedKeyImage = messages::monero::MoneroKeyImageSyncStepAck_MoneroExportedKeyImage; + using exported_key_image = hw::device_cold::exported_key_image; + + /** + * Converts transfer details to the MoneroTransferDetails required for KI sync + */ + bool key_image_data(wallet_shim * wallet, + const std::vector & transfers, + std::vector & res); + + /** + * Computes a hash over MoneroTransferDetails. Commitment used in the KI sync. + */ + std::string compute_hash(const MoneroTransferDetails & rr); + + /** + * Generates KI sync request with commitments computed. + */ + void generate_commitment(std::vector & mtds, + const std::vector & transfers, + std::shared_ptr & req); + +} + +// Cold transaction signing +namespace tx { + using TsxData = messages::monero::MoneroTransactionInitRequest_MoneroTransactionData; + using MoneroTransactionDestinationEntry = messages::monero::MoneroTransactionDestinationEntry; + using MoneroAccountPublicAddress = messages::monero::MoneroTransactionDestinationEntry_MoneroAccountPublicAddress; + using MoneroTransactionSourceEntry = messages::monero::MoneroTransactionSourceEntry; + using MoneroMultisigKLRki = messages::monero::MoneroTransactionSourceEntry_MoneroMultisigKLRki; + using MoneroOutputEntry = messages::monero::MoneroTransactionSourceEntry_MoneroOutputEntry; + using MoneroRctKey = messages::monero::MoneroTransactionSourceEntry_MoneroOutputEntry_MoneroRctKeyPublic; + using MoneroRsigData = messages::monero::MoneroTransactionRsigData; + + using tx_construction_data = tools::wallet2::tx_construction_data; + using unsigned_tx_set = tools::wallet2::unsigned_tx_set; + + void translate_address(MoneroAccountPublicAddress * dst, const cryptonote::account_public_address * src); + void translate_dst_entry(MoneroTransactionDestinationEntry * dst, const cryptonote::tx_destination_entry * src); + void translate_src_entry(MoneroTransactionSourceEntry * dst, const cryptonote::tx_source_entry * src); + void translate_klrki(MoneroMultisigKLRki * dst, const rct::multisig_kLRki * src); + void translate_rct_key(MoneroRctKey * dst, const rct::ctkey * src); + std::string hash_addr(const MoneroAccountPublicAddress * addr, boost::optional amount = boost::none, boost::optional is_subaddr = boost::none); + std::string hash_addr(const std::string & spend_key, const std::string & view_key, boost::optional amount = boost::none, boost::optional is_subaddr = boost::none); + std::string hash_addr(const ::crypto::public_key * spend_key, const ::crypto::public_key * view_key, boost::optional amount = boost::none, boost::optional is_subaddr = boost::none); + + typedef boost::variant rsig_v; + + /** + * Transaction signer state holder. + */ + class TData { + public: + TsxData tsx_data; + tx_construction_data tx_data; + cryptonote::transaction tx; + bool in_memory; + unsigned rsig_type; + std::vector grouping_vct; + std::shared_ptr rsig_param; + size_t cur_input_idx; + size_t cur_output_idx; + size_t cur_batch_idx; + size_t cur_output_in_batch_idx; + + std::vector tx_in_hmacs; + std::vector tx_out_entr_hmacs; + std::vector tx_out_hmacs; + std::vector tx_out_rsigs; + std::vector tx_out_pk; + std::vector tx_out_ecdh; + std::vector source_permutation; + std::vector alphas; + std::vector spend_encs; + std::vector pseudo_outs; + std::vector pseudo_outs_hmac; + std::vector couts; + std::vector couts_dec; + std::vector rsig_gamma; + std::string tx_prefix_hash; + std::string enc_salt1; + std::string enc_salt2; + std::string enc_keys; + + std::shared_ptr rv; + + TData(); + }; + + class Signer { + private: + TData m_ct; + wallet_shim * m_wallet2; + + size_t m_tx_idx; + const unsigned_tx_set * m_unsigned_tx; + hw::tx_aux_data * m_aux_data; + + bool m_multisig; + + const tx_construction_data & cur_tx(){ + CHECK_AND_ASSERT_THROW_MES(m_tx_idx < m_unsigned_tx->txes.size(), "Invalid transaction index"); + return m_unsigned_tx->txes[m_tx_idx]; + } + + void extract_payment_id(); + void compute_integrated_indices(TsxData * tsx_data); + + public: + Signer(wallet_shim * wallet2, const unsigned_tx_set * unsigned_tx, size_t tx_idx = 0, hw::tx_aux_data * aux_data = nullptr); + + std::shared_ptr step_init(); + void step_init_ack(std::shared_ptr ack); + + std::shared_ptr step_set_input(size_t idx); + void step_set_input_ack(std::shared_ptr ack); + + void sort_ki(); + std::shared_ptr step_permutation(); + void step_permutation_ack(std::shared_ptr ack); + + std::shared_ptr step_set_vini_input(size_t idx); + void step_set_vini_input_ack(std::shared_ptr ack); + + std::shared_ptr step_all_inputs_set(); + void step_all_inputs_set_ack(std::shared_ptr ack); + + std::shared_ptr step_set_output(size_t idx); + void step_set_output_ack(std::shared_ptr ack); + + std::shared_ptr step_all_outs_set(); + void step_all_outs_set_ack(std::shared_ptr ack, hw::device &hwdev); + + std::shared_ptr step_sign_input(size_t idx); + void step_sign_input_ack(std::shared_ptr ack); + + std::shared_ptr step_final(); + void step_final_ack(std::shared_ptr ack); + + std::string store_tx_aux_info(); + + bool in_memory() const { + return m_ct.in_memory; + } + + bool is_simple() const { + if (!m_ct.rv){ + throw std::invalid_argument("RV not initialized"); + } + auto tp = m_ct.rv->type; + return tp == rct::RCTTypeSimple; + } + + bool is_req_bulletproof() const { + return m_ct.tx_data.use_bulletproofs; + } + + bool is_bulletproof() const { + if (!m_ct.rv){ + throw std::invalid_argument("RV not initialized"); + } + auto tp = m_ct.rv->type; + return tp == rct::RCTTypeBulletproof; + } + + bool is_offloading() const { + return m_ct.rsig_param && m_ct.rsig_param->offload_type() != 0; + } + + size_t num_outputs() const { + return m_ct.tx_data.splitted_dsts.size(); + } + + size_t num_inputs() const { + return m_ct.tx_data.sources.size(); + } + + const TData & tdata() const { + return m_ct; + } + }; + +} + +} +} +} + + +#endif //MONERO_PROTOCOL_H diff --git a/src/device_trezor/trezor/tools/README.md b/src/device_trezor/trezor/tools/README.md new file mode 100644 index 00000000000..91a8fb3f0b0 --- /dev/null +++ b/src/device_trezor/trezor/tools/README.md @@ -0,0 +1,36 @@ +# Trezor + +## Messages rebuild + +Install `protoc` for your distribution. + +- `protobuf-compiler` +- `libprotobuf-dev` +- `libprotoc-dev` +- `python-protobuf` + +Python 3 is required. If you don't have python 3 quite an easy way is +to use [pyenv]. + +It is also advised to create own python virtual environment so dependencies +are installed in this project-related virtual environment. + +```bash +python -m venv / +``` + +Make sure your python has `protobuf` package installed + +```bash +pip install protobuf +``` + +Regenerate messages: + +``` +./venv/bin/python3 src/device_trezor/trezor/tools/build_protob.py +``` + +The messages regeneration is done also automatically via cmake. + +[pyenv]: https://github.com/pyenv/pyenv diff --git a/src/device_trezor/trezor/tools/build_protob.py b/src/device_trezor/trezor/tools/build_protob.py new file mode 100644 index 00000000000..2611f32966a --- /dev/null +++ b/src/device_trezor/trezor/tools/build_protob.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python +import os +import subprocess +import sys + +CWD = os.path.dirname(os.path.realpath(__file__)) +ROOT_DIR = os.path.abspath(os.path.join(CWD, "..", "..", "..", "..")) +TREZOR_COMMON = os.path.join(ROOT_DIR, "external", "trezor-common") +TREZOR_MESSAGES = os.path.join(CWD, "..", "messages") + +# check for existence of the submodule directory +common_defs = os.path.join(TREZOR_COMMON, "defs") +if not os.path.exists(common_defs): + raise ValueError( + "trezor-common submodule seems to be missing.\n" + + 'Use "git submodule update --init --recursive" to retrieve it.' + ) + +# regenerate messages +try: + selected = [ + "messages.proto", + "messages-common.proto", + "messages-management.proto", + "messages-monero.proto", + ] + proto_srcs = [os.path.join(TREZOR_COMMON, "protob", x) for x in selected] + exec_args = [ + sys.executable, + os.path.join(CWD, "pb2cpp.py"), + "-o", + TREZOR_MESSAGES, + ] + proto_srcs + + subprocess.check_call(exec_args) + +except Exception as e: + raise diff --git a/src/device_trezor/trezor/tools/pb2cpp.py b/src/device_trezor/trezor/tools/pb2cpp.py new file mode 100644 index 00000000000..eaa8a90ed01 --- /dev/null +++ b/src/device_trezor/trezor/tools/pb2cpp.py @@ -0,0 +1,186 @@ +#!/usr/bin/env python3 +# Converts Google's protobuf python definitions of TREZOR wire messages +# to plain-python objects as used in TREZOR Core and python-trezor + +import argparse +import logging +import os +import re +import shutil +import subprocess +import sys +import glob +import tempfile +import hashlib + + +AUTO_HEADER = "# Automatically generated by pb2cpp\n" + +# Fixing GCC7 compilation error +UNDEF_STATEMENT = """ +#ifdef minor +#undef minor +#endif +""" + + +def which(pgm): + path = os.getenv('PATH') + for p in path.split(os.path.pathsep): + p = os.path.join(p, pgm) + if os.path.exists(p) and os.access(p, os.X_OK): + return p + + +PROTOC = which("protoc") +if not PROTOC: + print("protoc command not found") + sys.exit(1) + +PROTOC_PREFIX = os.path.dirname(os.path.dirname(PROTOC)) +PROTOC_INCLUDE = os.path.join(PROTOC_PREFIX, "include") + + +def namespace_file(fpath, package): + """Adds / replaces package name. Simple regex parsing, may use https://github.com/ph4r05/plyprotobuf later""" + with open(fpath) as fh: + fdata = fh.read() + + re_syntax = re.compile(r"^syntax\s*=") + re_package = re.compile(r"^package\s+([^;]+?)\s*;\s*$") + lines = fdata.split("\n") + + line_syntax = None + line_package = None + for idx, line in enumerate(lines): + if line_syntax is None and re_syntax.match(line): + line_syntax = idx + if line_package is None and re_package.match(line): + line_package = idx + + if package is None: + if line_package is None: + return + else: + lines.pop(line_package) + + else: + new_package = "package %s;" % package + if line_package is None: + lines.insert(line_syntax + 1 if line_syntax is not None else 0, new_package) + else: + lines[line_package] = new_package + + new_fdat = "\n".join(lines) + with open(fpath, "w+") as fh: + fh.write(new_fdat) + return new_fdat + + +def protoc(files, out_dir, additional_includes=(), package=None, force=False): + """Compile code with protoc and return the data.""" + + include_dirs = set() + include_dirs.add(PROTOC_INCLUDE) + include_dirs.update(additional_includes) + + with tempfile.TemporaryDirectory() as tmpdir_protob, tempfile.TemporaryDirectory() as tmpdir_out: + include_dirs.add(tmpdir_protob) + + new_files = [] + for file in files: + bname = os.path.basename(file) + tmp_file = os.path.join(tmpdir_protob, bname) + + shutil.copy(file, tmp_file) + if package is not None: + namespace_file(tmp_file, package) + new_files.append(tmp_file) + + protoc_includes = ["-I" + dir for dir in include_dirs if dir] + + exec_args = ( + [ + PROTOC, + "--cpp_out", + tmpdir_out, + ] + + protoc_includes + + new_files + ) + + subprocess.check_call(exec_args) + + # Fixing gcc compilation and clashes with "minor" field name + add_undef(tmpdir_out) + + # Scan output dir, check file differences + update_message_files(tmpdir_out, out_dir, force) + + +def update_message_files(tmpdir_out, out_dir, force=False): + files = glob.glob(os.path.join(tmpdir_out, '*.pb.*')) + for fname in files: + bname = os.path.basename(fname) + dest_file = os.path.join(out_dir, bname) + if not force and os.path.exists(dest_file): + data = open(fname, 'rb').read() + data_hash = hashlib.sha3_256(data).digest() + data_dest = open(dest_file, 'rb').read() + data_dest_hash = hashlib.sha3_256(data_dest).digest() + if data_hash == data_dest_hash: + continue + + shutil.copy(fname, dest_file) + + +def add_undef(out_dir): + files = glob.glob(os.path.join(out_dir, '*.pb.*')) + for fname in files: + with open(fname) as fh: + lines = fh.readlines() + + idx_insertion = None + for idx in range(len(lines)): + if '@@protoc_insertion_point(includes)' in lines[idx]: + idx_insertion = idx + break + + if idx_insertion is None: + pass + + lines.insert(idx_insertion + 1, UNDEF_STATEMENT) + with open(fname, 'w') as fh: + fh.write("".join(lines)) + + +def strip_leader(s, prefix): + """Remove given prefix from underscored name.""" + leader = prefix + "_" + if s.startswith(leader): + return s[len(leader) :] + else: + return s + + +if __name__ == "__main__": + logging.basicConfig(level=logging.DEBUG) + + parser = argparse.ArgumentParser() + # fmt: off + parser.add_argument("proto", nargs="+", help="Protobuf definition files") + parser.add_argument("-o", "--out-dir", help="Directory for generated source code") + parser.add_argument("-n", "--namespace", default=None, help="Message namespace") + parser.add_argument("-I", "--protoc-include", action="append", help="protoc include path") + parser.add_argument("-P", "--protobuf-module", default="protobuf", help="Name of protobuf module") + parser.add_argument("-f", "--force", default=False, help="Overwrite existing files") + # fmt: on + args = parser.parse_args() + + protoc_includes = args.protoc_include or (os.environ.get("PROTOC_INCLUDE"),) + + protoc( + args.proto, args.out_dir, protoc_includes, package=args.namespace, force=args.force + ) + + diff --git a/src/device_trezor/trezor/transport.cpp b/src/device_trezor/trezor/transport.cpp new file mode 100644 index 00000000000..fc86177e19c --- /dev/null +++ b/src/device_trezor/trezor/transport.cpp @@ -0,0 +1,651 @@ +// Copyright (c) 2017-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +#include +#include +#include +#include +#include "transport.hpp" +#include "messages/messages-common.pb.h" + +using namespace std; +using json = rapidjson::Document; + + +namespace hw{ +namespace trezor{ + + bool t_serialize(const std::string & in, std::string & out){ + out = in; + return true; + } + + bool t_serialize(const json_val & in, std::string & out){ + rapidjson::StringBuffer sb; + rapidjson::Writer writer(sb); + in.Accept(writer); + out = sb.GetString(); + return true; + } + + std::string t_serialize(const json_val & in){ + std::string ret; + t_serialize(in, ret); + return ret; + } + + bool t_deserialize(const std::string & in, std::string & out){ + out = in; + return true; + } + + bool t_deserialize(const std::string & in, json & out){ + if (out.Parse(in.c_str()).HasParseError()) { + throw exc::CommunicationException("JSON parse error"); + } + return true; + } + + static std::string json_get_string(const rapidjson::Value & in){ + return std::string(in.GetString()); + } + + // + // Helpers + // + +#define PROTO_HEADER_SIZE 6 + + static size_t message_size(const google::protobuf::Message &req){ + return static_cast(req.ByteSize()); + } + + static size_t serialize_message_buffer_size(size_t msg_size) { + return PROTO_HEADER_SIZE + msg_size; // tag 2B + len 4B + } + + static void serialize_message_header(void * buff, uint16_t tag, uint32_t len){ + uint16_t wire_tag = boost::endian::native_to_big(static_cast(tag)); + uint32_t wire_len = boost::endian::native_to_big(static_cast(len)); + memcpy(buff, (void *) &wire_tag, 2); + memcpy((uint8_t*)buff + 2, (void *) &wire_len, 4); + } + + static void deserialize_message_header(const void * buff, uint16_t & tag, uint32_t & len){ + uint16_t wire_tag; + uint32_t wire_len; + memcpy(&wire_tag, buff, 2); + memcpy(&wire_len, (uint8_t*)buff + 2, 4); + + tag = boost::endian::big_to_native(wire_tag); + len = boost::endian::big_to_native(wire_len); + } + + static void serialize_message(const google::protobuf::Message &req, size_t msg_size, uint8_t * buff, size_t buff_size) { + auto msg_wire_num = MessageMapper::get_message_wire_number(req); + const auto req_buffer_size = serialize_message_buffer_size(msg_size); + if (req_buffer_size > buff_size){ + throw std::invalid_argument("Buffer too small"); + } + + serialize_message_header(buff, msg_wire_num, msg_size); + if (!req.SerializeToArray(buff + 6, msg_size)){ + throw exc::EncodingException("Message serialization error"); + } + } + + // + // Communication protocol + // + +#define REPLEN 64 + + void ProtocolV1::write(Transport & transport, const google::protobuf::Message & req){ + const auto msg_size = message_size(req); + const auto buff_size = serialize_message_buffer_size(msg_size) + 2; + + std::unique_ptr req_buff(new uint8_t[buff_size]); + uint8_t * req_buff_raw = req_buff.get(); + req_buff_raw[0] = '#'; + req_buff_raw[1] = '#'; + + serialize_message(req, msg_size, req_buff_raw + 2, buff_size - 2); + + size_t offset = 0; + uint8_t chunk_buff[REPLEN]; + + // Chunk by chunk upload + while(offset < buff_size){ + auto to_copy = std::min((size_t)(buff_size - offset), (size_t)(REPLEN - 1)); + + chunk_buff[0] = '?'; + memcpy(chunk_buff + 1, req_buff_raw + offset, to_copy); + + // Pad with zeros + if (to_copy < REPLEN - 1){ + memset(chunk_buff + 1 + to_copy, 0, REPLEN - 1 - to_copy); + } + + transport.write_chunk(chunk_buff, REPLEN); + offset += REPLEN - 1; + } + } + + void ProtocolV1::read(Transport & transport, std::shared_ptr & msg, messages::MessageType * msg_type){ + char chunk[REPLEN]; + + // Initial chunk read + size_t nread = transport.read_chunk(chunk, REPLEN); + if (nread != REPLEN){ + throw exc::CommunicationException("Read chunk has invalid size"); + } + + if (strncmp(chunk, "?##", 3) != 0){ + throw exc::CommunicationException("Malformed chunk"); + } + + uint16_t tag; + uint32_t len; + nread -= 3 + 6; + deserialize_message_header(chunk + 3, tag, len); + + std::string data_acc(chunk + 3 + 6, nread); + data_acc.reserve(len); + + while(nread < len){ + const size_t cur = transport.read_chunk(chunk, REPLEN); + if (chunk[0] != '?'){ + throw exc::CommunicationException("Chunk malformed"); + } + + data_acc.append(chunk + 1, cur - 1); + nread += cur - 1; + } + + if (msg_type){ + *msg_type = static_cast(tag); + } + + if (nread < len){ + throw exc::CommunicationException("Response incomplete"); + } + + std::shared_ptr msg_wrap(MessageMapper::get_message(tag)); + if (!msg_wrap->ParseFromArray(data_acc.c_str(), len)){ + throw exc::CommunicationException("Message could not be parsed"); + } + + msg = msg_wrap; + } + + // + // Bridge transport + // + + const char * BridgeTransport::PATH_PREFIX = "bridge:"; + + std::string BridgeTransport::get_path() const { + if (!m_device_path){ + return ""; + } + + std::string path(PATH_PREFIX); + return path + m_device_path.get(); + } + + void BridgeTransport::enumerate(t_transport_vect & res) { + json bridge_res; + std::string req; + + bool req_status = invoke_bridge_http("/enumerate", req, bridge_res, m_http_client); + if (!req_status){ + throw exc::CommunicationException("Bridge enumeration failed"); + } + + for(rapidjson::Value::ConstValueIterator itr = bridge_res.Begin(); itr != bridge_res.End(); ++itr){ + auto element = itr->GetObject(); + auto t = std::make_shared(boost::make_optional(json_get_string(element["path"]))); + t->m_device_info.emplace(); + t->m_device_info->CopyFrom(*itr, t->m_device_info->GetAllocator()); + res.push_back(t); + } + } + + void BridgeTransport::open() { + if (!m_device_path){ + throw exc::CommunicationException("Coud not open, empty device path"); + } + + std::string uri = "/acquire/" + m_device_path.get() + "/null"; + std::string req; + json bridge_res; + bool req_status = invoke_bridge_http(uri, req, bridge_res, m_http_client); + if (!req_status){ + throw exc::CommunicationException("Failed to acquire device"); + } + + m_session = boost::make_optional(json_get_string(bridge_res["session"])); + } + + void BridgeTransport::close() { + if (!m_device_path || !m_session){ + throw exc::CommunicationException("Device not open"); + } + + std::string uri = "/release/" + m_session.get(); + std::string req; + json bridge_res; + bool req_status = invoke_bridge_http(uri, req, bridge_res, m_http_client); + if (!req_status){ + throw exc::CommunicationException("Failed to release device"); + } + + m_session = boost::none; + } + + void BridgeTransport::write(const google::protobuf::Message &req) { + m_response = boost::none; + + const auto msg_size = message_size(req); + const auto buff_size = serialize_message_buffer_size(msg_size); + + std::unique_ptr req_buff(new uint8_t[buff_size]); + uint8_t * req_buff_raw = req_buff.get(); + + serialize_message(req, msg_size, req_buff_raw, buff_size); + + std::string uri = "/call/" + m_session.get(); + std::string req_hex = epee::to_hex::string(epee::span(req_buff_raw, buff_size)); + std::string res_hex; + + bool req_status = invoke_bridge_http(uri, req_hex, res_hex, m_http_client); + if (!req_status){ + throw exc::CommunicationException("Call method failed"); + } + + m_response = res_hex; + } + + void BridgeTransport::read(std::shared_ptr & msg, messages::MessageType * msg_type) { + if (!m_response){ + throw exc::CommunicationException("Could not read, no response stored"); + } + + std::string bin_data; + if (!epee::string_tools::parse_hexstr_to_binbuff(m_response.get(), bin_data)){ + throw exc::CommunicationException("Response is not well hexcoded"); + } + + uint16_t msg_tag; + uint32_t msg_len; + deserialize_message_header(bin_data.c_str(), msg_tag, msg_len); + if (bin_data.size() != msg_len + 6){ + throw exc::CommunicationException("Response is not well hexcoded"); + } + + if (msg_type){ + *msg_type = static_cast(msg_tag); + } + + std::shared_ptr msg_wrap(MessageMapper::get_message(msg_tag)); + if (!msg_wrap->ParseFromArray(bin_data.c_str() + 6, msg_len)){ + throw exc::EncodingException("Response is not well hexcoded"); + } + msg = msg_wrap; + } + + const boost::optional & BridgeTransport::device_info() const { + return m_device_info; + } + + std::ostream& BridgeTransport::dump(std::ostream& o) const { + return o << "BridgeTransport"; + } + + // + // UdpTransport + // + const char * UdpTransport::PATH_PREFIX = "udp:"; + const char * UdpTransport::DEFAULT_HOST = "127.0.0.1"; + const int UdpTransport::DEFAULT_PORT = 21324; + + UdpTransport::UdpTransport(boost::optional device_path, + boost::optional> proto) : + m_io_service(), m_deadline(m_io_service) + { + m_device_port = DEFAULT_PORT; + if (device_path) { + const std::string device_str = device_path.get(); + auto delim = device_str.find(':'); + if (delim == std::string::npos) { + m_device_host = device_str; + } else { + m_device_host = device_str.substr(0, delim); + m_device_port = std::stoi(device_str.substr(delim + 1)); + } + } else { + m_device_host = DEFAULT_HOST; + } + + if (m_device_port <= 1024 || m_device_port > 65535){ + throw std::invalid_argument("Port number invalid"); + } + + if (m_device_host != "localhost" && m_device_host != DEFAULT_HOST){ + throw std::invalid_argument("Local endpoint allowed only"); + } + + m_proto = proto ? proto.get() : std::make_shared(); + } + + std::string UdpTransport::get_path() const { + std::string path(PATH_PREFIX); + return path + m_device_host + ":" + std::to_string(m_device_port); + } + + void UdpTransport::require_socket(){ + if (!m_socket){ + throw exc::NotConnectedException("Socket not connected"); + } + } + + bool UdpTransport::ping(){ + return ping_int(); + } + + bool UdpTransport::ping_int(boost::posix_time::time_duration timeout){ + require_socket(); + try { + std::string req = "PINGPING"; + char res[8]; + + m_socket->send_to(boost::asio::buffer(req.c_str(), req.size()), m_endpoint); + receive(res, 8, nullptr, false, timeout); + + return memcmp(res, "PONGPONG", 8) == 0; + + } catch(...){ + return false; + } + } + + void UdpTransport::enumerate(t_transport_vect & res) { + std::shared_ptr t = std::make_shared(); + bool t_works = false; + + try{ + t->open(); + t_works = t->ping(); + } catch(...) { + + } + t->close(); + if (t_works){ + res.push_back(t); + } + } + + void UdpTransport::open() { + udp::resolver resolver(m_io_service); + udp::resolver::query query(udp::v4(), m_device_host, std::to_string(m_device_port)); + m_endpoint = *resolver.resolve(query); + + m_socket.reset(new udp::socket(m_io_service)); + m_socket->open(udp::v4()); + + m_deadline.expires_at(boost::posix_time::pos_infin); + check_deadline(); + + m_proto->session_begin(*this); + } + + void UdpTransport::close() { + if (!m_socket){ + throw exc::CommunicationException("Socket is already closed"); + } + + m_proto->session_end(*this); + m_socket->close(); + m_socket = nullptr; + } + + void UdpTransport::write_chunk(const void * buff, size_t size){ + require_socket(); + + if (size != 64){ + throw exc::CommunicationException("Invalid chunk size"); + } + + auto written = m_socket->send_to(boost::asio::buffer(buff, size), m_endpoint); + if (size != written){ + throw exc::CommunicationException("Could not send the whole chunk"); + } + } + + size_t UdpTransport::read_chunk(void * buff, size_t size){ + require_socket(); + if (size < 64){ + throw std::invalid_argument("Buffer too small"); + } + + ssize_t len; + while(true) { + try { + boost::system::error_code ec; + len = receive(buff, size, &ec, true); + if (ec == boost::asio::error::operation_aborted) { + continue; + } else if (ec) { + throw exc::CommunicationException(std::string("Comm error: ") + ec.message()); + } + + if (len != 64) { + throw exc::CommunicationException("Invalid chunk size"); + } + + break; + + } catch(exc::CommunicationException const& e){ + throw; + } catch(std::exception const& e){ + MWARNING("Error reading chunk, reason: " << e.what()); + throw exc::CommunicationException(std::string("Chunk read error: ") + std::string(e.what())); + } + } + + return static_cast(len); + } + + ssize_t UdpTransport::receive(void * buff, size_t size, boost::system::error_code * error_code, bool no_throw, boost::posix_time::time_duration timeout){ + boost::system::error_code ec; + boost::asio::mutable_buffer buffer = boost::asio::buffer(buff, size); + + require_socket(); + + // Set a deadline for the asynchronous operation. + m_deadline.expires_from_now(timeout); + + // Set up the variables that receive the result of the asynchronous + // operation. The error code is set to would_block to signal that the + // operation is incomplete. Asio guarantees that its asynchronous + // operations will never fail with would_block, so any other value in + // ec indicates completion. + ec = boost::asio::error::would_block; + std::size_t length = 0; + + // Start the asynchronous operation itself. The handle_receive function + // used as a callback will update the ec and length variables. + m_socket->async_receive_from(boost::asio::buffer(buffer), m_endpoint, + boost::bind(&UdpTransport::handle_receive, _1, _2, &ec, &length)); + + // Block until the asynchronous operation has completed. + do { + m_io_service.run_one(); + } + while (ec == boost::asio::error::would_block); + + if (error_code){ + *error_code = ec; + } + + if (no_throw){ + return length; + } + + // Operation result + if (ec == boost::asio::error::operation_aborted){ + throw exc::TimeoutException(); + + } else if (ec) { + MWARNING("Reading from UDP socket failed: " << ec.message()); + throw exc::CommunicationException(); + + } + + return length; + } + + void UdpTransport::write(const google::protobuf::Message &req) { + m_proto->write(*this, req); + } + + void UdpTransport::read(std::shared_ptr & msg, messages::MessageType * msg_type) { + m_proto->read(*this, msg, msg_type); + } + + void UdpTransport::check_deadline(){ + if (!m_socket){ + return; // no active socket. + } + + // Check whether the deadline has passed. We compare the deadline against + // the current time since a new asynchronous operation may have moved the + // deadline before this actor had a chance to run. + if (m_deadline.expires_at() <= boost::asio::deadline_timer::traits_type::now()) + { + // The deadline has passed. The outstanding asynchronous operation needs + // to be cancelled so that the blocked receive() function will return. + // + // Please note that cancel() has portability issues on some versions of + // Microsoft Windows, and it may be necessary to use close() instead. + // Consult the documentation for cancel() for further information. + m_socket->cancel(); + + // There is no longer an active deadline. The expiry is set to positive + // infinity so that the actor takes no action until a new deadline is set. + m_deadline.expires_at(boost::posix_time::pos_infin); + } + + // Put the actor back to sleep. + m_deadline.async_wait(boost::bind(&UdpTransport::check_deadline, this)); + } + + void UdpTransport::handle_receive(const boost::system::error_code &ec, std::size_t length, + boost::system::error_code *out_ec, std::size_t *out_length) { + *out_ec = ec; + *out_length = length; + } + + std::ostream& UdpTransport::dump(std::ostream& o) const { + return o << "UdpTransport"; + } + + void enumerate(t_transport_vect & res){ + BridgeTransport bt; + bt.enumerate(res); + + hw::trezor::UdpTransport btu; + btu.enumerate(res); + } + + std::shared_ptr transport(const std::string & path){ + if (boost::starts_with(path, BridgeTransport::PATH_PREFIX)){ + return std::make_shared(path.substr(strlen(BridgeTransport::PATH_PREFIX))); + + } else if (boost::starts_with(path, UdpTransport::PATH_PREFIX)){ + return std::make_shared(path.substr(strlen(UdpTransport::PATH_PREFIX))); + + } else { + throw std::invalid_argument("Unknown Trezor device path: " + path); + + } + } + + void throw_failure_exception(const messages::common::Failure * failure) { + if (failure == nullptr){ + throw std::invalid_argument("Failure message cannot be null"); + } + + boost::optional message = failure->has_message() ? boost::make_optional(failure->message()) : boost::none; + boost::optional code = failure->has_code() ? boost::make_optional(static_cast(failure->code())) : boost::none; + if (!code){ + throw exc::proto::FailureException(code, message); + } + + auto ecode = failure->code(); + if (ecode == messages::common::Failure_FailureType_Failure_UnexpectedMessage){ + throw exc::proto::UnexpectedMessageException(code, message); + } else if (ecode == messages::common::Failure_FailureType_Failure_ActionCancelled){ + throw exc::proto::CancelledException(code, message); + } else if (ecode == messages::common::Failure_FailureType_Failure_PinExpected){ + throw exc::proto::PinExpectedException(code, message); + } else if (ecode == messages::common::Failure_FailureType_Failure_PinInvalid){ + throw exc::proto::InvalidPinException(code, message); + } else if (ecode == messages::common::Failure_FailureType_Failure_NotEnoughFunds){ + throw exc::proto::NotEnoughFundsException(code, message); + } else if (ecode == messages::common::Failure_FailureType_Failure_NotInitialized){ + throw exc::proto::NotInitializedException(code, message); + } else if (ecode == messages::common::Failure_FailureType_Failure_FirmwareError){ + throw exc::proto::FirmwareErrorException(code, message); + } else { + throw exc::proto::FailureException(code, message); + } + } + + std::ostream& operator<<(std::ostream& o, hw::trezor::Transport const& t){ + return t.dump(o); + } + + std::ostream& operator<<(std::ostream& o, std::shared_ptr const& t){ + if (!t){ + return o << "None"; + } + + return t->dump(o); + } + +} +} + + diff --git a/src/device_trezor/trezor/transport.hpp b/src/device_trezor/trezor/transport.hpp new file mode 100644 index 00000000000..7b82fd06f6d --- /dev/null +++ b/src/device_trezor/trezor/transport.hpp @@ -0,0 +1,331 @@ +// Copyright (c) 2017-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef MONERO_TRANSPORT_H +#define MONERO_TRANSPORT_H + + +#include +#include +#include +#include + +#include +#include +#include "net/http_client.h" + +#include "rapidjson/document.h" +#include "rapidjson/writer.h" +#include "rapidjson/stringbuffer.h" + +#include "exceptions.hpp" +#include "trezor_defs.hpp" +#include "messages_map.hpp" + +#include "messages/messages.pb.h" +#include "messages/messages-common.pb.h" +#include "messages/messages-management.pb.h" +#include "messages/messages-monero.pb.h" + +namespace hw { +namespace trezor { + + using json = rapidjson::Document; + using json_val = rapidjson::Value; + namespace http = epee::net_utils::http; + + const std::string DEFAULT_BRIDGE = "127.0.0.1:21325"; + + // Base HTTP comm serialization. + bool t_serialize(const std::string & in, std::string & out); + bool t_serialize(const json_val & in, std::string & out); + std::string t_serialize(const json_val & in); + + bool t_deserialize(const std::string & in, std::string & out); + bool t_deserialize(const std::string & in, json & out); + + // Flexible json serialization. HTTP client tailored for bridge API + template + bool invoke_bridge_http(const boost::string_ref uri, const t_req & out_struct, t_res & result_struct, t_transport& transport, const boost::string_ref method = "POST", std::chrono::milliseconds timeout = std::chrono::seconds(180)) + { + std::string req_param; + t_serialize(out_struct, req_param); + + http::fields_list additional_params; + additional_params.push_back(std::make_pair("Origin","https://monero.trezor.io")); + additional_params.push_back(std::make_pair("Content-Type","application/json; charset=utf-8")); + + const http::http_response_info* pri = nullptr; + if(!transport.invoke(uri, method, req_param, timeout, &pri, std::move(additional_params))) + { + MERROR("Failed to invoke http request to " << uri); + return false; + } + + if(!pri) + { + MERROR("Failed to invoke http request to " << uri << ", internal error (null response ptr)"); + return false; + } + + if(pri->m_response_code != 200) + { + MERROR("Failed to invoke http request to " << uri << ", wrong response code: " << pri->m_response_code + << " Response Body: " << pri->m_body); + return false; + } + + return t_deserialize(pri->m_body, result_struct); + } + + // Forward decl + class Transport; + class Protocol; + + // Communication protocol + class Protocol { + public: + Protocol() = default; + virtual ~Protocol() = default; + virtual void session_begin(Transport & transport){ }; + virtual void session_end(Transport & transport){ }; + virtual void write(Transport & transport, const google::protobuf::Message & req)= 0; + virtual void read(Transport & transport, std::shared_ptr & msg, messages::MessageType * msg_type=nullptr)= 0; + }; + + class ProtocolV1 : public Protocol { + public: + ProtocolV1() = default; + virtual ~ProtocolV1() = default; + + void write(Transport & transport, const google::protobuf::Message & req) override; + void read(Transport & transport, std::shared_ptr & msg, messages::MessageType * msg_type=nullptr) override; + }; + + + // Base transport + typedef std::vector> t_transport_vect; + + class Transport { + public: + Transport() = default; + virtual ~Transport() = default; + + virtual bool ping() { return false; }; + virtual std::string get_path() const { return ""; }; + virtual void enumerate(t_transport_vect & res){}; + virtual void open(){}; + virtual void close(){}; + virtual void write(const google::protobuf::Message & req) =0; + virtual void read(std::shared_ptr & msg, messages::MessageType * msg_type=nullptr) =0; + + virtual void write_chunk(const void * buff, size_t size) { }; + virtual size_t read_chunk(void * buff, size_t size) { return 0; }; + virtual std::ostream& dump(std::ostream& o) const { return o << "Transport<>"; } + }; + + // Bridge transport + class BridgeTransport : public Transport { + public: + BridgeTransport( + boost::optional device_path = boost::none, + boost::optional bridge_host = boost::none): + m_device_path(device_path), + m_bridge_host(bridge_host ? bridge_host.get() : DEFAULT_BRIDGE), + m_response(boost::none), + m_session(boost::none), + m_device_info(boost::none) + { + m_http_client.set_server(m_bridge_host, boost::none, false); + } + + virtual ~BridgeTransport() = default; + + static const char * PATH_PREFIX; + + std::string get_path() const override; + void enumerate(t_transport_vect & res) override; + + void open() override; + void close() override; + + void write(const google::protobuf::Message &req) override; + void read(std::shared_ptr & msg, messages::MessageType * msg_type=nullptr) override; + + const boost::optional & device_info() const; + std::ostream& dump(std::ostream& o) const override; + + private: + epee::net_utils::http::http_simple_client m_http_client; + std::string m_bridge_host; + boost::optional m_device_path; + boost::optional m_session; + boost::optional m_response; + boost::optional m_device_info; + }; + + // UdpTransport transport + using boost::asio::ip::udp; + + class UdpTransport : public Transport { + public: + + explicit UdpTransport( + boost::optional device_path=boost::none, + boost::optional> proto=boost::none); + + virtual ~UdpTransport() = default; + + static const char * PATH_PREFIX; + static const char * DEFAULT_HOST; + static const int DEFAULT_PORT; + + bool ping() override; + std::string get_path() const override; + void enumerate(t_transport_vect & res) override; + + void open() override; + void close() override; + + void write(const google::protobuf::Message &req) override; + void read(std::shared_ptr & msg, messages::MessageType * msg_type=nullptr) override; + + void write_chunk(const void * buff, size_t size) override; + size_t read_chunk(void * buff, size_t size) override; + + std::ostream& dump(std::ostream& o) const override; + + private: + void require_socket(); + ssize_t receive(void * buff, size_t size, boost::system::error_code * error_code=nullptr, bool no_throw=false, boost::posix_time::time_duration timeout=boost::posix_time::seconds(10)); + void check_deadline(); + static void handle_receive(const boost::system::error_code& ec, std::size_t length, + boost::system::error_code* out_ec, std::size_t* out_length); + bool ping_int(boost::posix_time::time_duration timeout=boost::posix_time::milliseconds(1500)); + + std::shared_ptr m_proto; + std::string m_device_host; + int m_device_port; + + std::unique_ptr m_socket; + boost::asio::io_service m_io_service; + boost::asio::deadline_timer m_deadline; + udp::endpoint m_endpoint; + }; + + // + // General helpers + // + + /** + * Enumerates all transports + */ + void enumerate(t_transport_vect & res); + + /** + * Transforms path to the transport + */ + std::shared_ptr transport(const std::string & path); + + /** + * Transforms path to the particular transport + */ + template + std::shared_ptr transport_typed(const std::string & path){ + auto t = transport(path); + if (!t){ + return nullptr; + } + + return std::dynamic_pointer_cast(t); + } + + // Exception carries unexpected message being received + namespace exc { + class UnexpectedMessageException: public ProtocolException { + protected: + hw::trezor::messages::MessageType recvType; + std::shared_ptr recvMsg; + + public: + using ProtocolException::ProtocolException; + UnexpectedMessageException(): ProtocolException("Trezor returned unexpected message") {}; + UnexpectedMessageException(hw::trezor::messages::MessageType recvType, + const std::shared_ptr & recvMsg) + : recvType(recvType), recvMsg(recvMsg) { + reason = std::string("Trezor returned unexpected message: ") + std::to_string(recvType); + } + }; + } + + /** + * Throws corresponding failure exception. + */ + [[ noreturn ]] void throw_failure_exception(const messages::common::Failure * failure); + + /** + * Simple wrapper for write-read message exchange with expected message response type. + * + * @throws UnexpectedMessageException if the response message type is different than expected. + * Exception contains message type and the message itself. + */ + template + std::shared_ptr + exchange_message(Transport & transport, const google::protobuf::Message & req, + boost::optional resp_type = boost::none) + { + // Require strictly protocol buffers response in the template. + BOOST_STATIC_ASSERT(boost::is_base_of::value); + + // Write the request + transport.write(req); + + // Read the response + std::shared_ptr msg_resp; + hw::trezor::messages::MessageType msg_resp_type; + transport.read(msg_resp, &msg_resp_type); + + // Determine type of expected message response + messages::MessageType required_type = resp_type ? resp_type.get() : MessageMapper::get_message_wire_number(); + + if (msg_resp_type == required_type) { + return message_ptr_retype(msg_resp); + } else if (msg_resp_type == messages::MessageType_Failure){ + throw_failure_exception(dynamic_cast(msg_resp.get())); + } else { + throw exc::UnexpectedMessageException(msg_resp_type, msg_resp); + } + } + + std::ostream& operator<<(std::ostream& o, hw::trezor::Transport const& t); + std::ostream& operator<<(std::ostream& o, std::shared_ptr const& t); +}} + + +#endif //MONERO_TRANSPORT_H diff --git a/src/device_trezor/trezor/trezor_defs.hpp b/src/device_trezor/trezor/trezor_defs.hpp new file mode 100644 index 00000000000..951a8f80285 --- /dev/null +++ b/src/device_trezor/trezor/trezor_defs.hpp @@ -0,0 +1,48 @@ +// Copyright (c) 2017-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +#if defined(HAVE_PROTOBUF) && !defined(WITHOUT_TREZOR) + #define WITH_DEVICE_TREZOR 1 +#else + #define WITH_DEVICE_TREZOR 0 +#endif + +#ifndef WITH_DEVICE_TREZOR_LITE +#define WITH_DEVICE_TREZOR_LITE 0 +#endif + +// Avoids protobuf undefined macro warning +#ifndef PROTOBUF_INLINE_NOT_IN_HEADERS +#define PROTOBUF_INLINE_NOT_IN_HEADERS 0 +#endif + +// Fixes gcc7 problem with minor macro defined clashing with minor() field. +#ifdef minor +#undef minor +#endif diff --git a/src/ringct/rctSigs.h b/src/ringct/rctSigs.h index ae8bb91d76d..b67a0b99293 100644 --- a/src/ringct/rctSigs.h +++ b/src/ringct/rctSigs.h @@ -133,7 +133,7 @@ namespace rct { xmr_amount decodeRct(const rctSig & rv, const key & sk, unsigned int i, hw::device &hwdev); xmr_amount decodeRctSimple(const rctSig & rv, const key & sk, unsigned int i, key & mask, hw::device &hwdev); xmr_amount decodeRctSimple(const rctSig & rv, const key & sk, unsigned int i, hw::device &hwdev); - + key get_pre_mlsag_hash(const rctSig &rv, hw::device &hwdev); bool signMultisig(rctSig &rv, const std::vector &indices, const keyV &k, const multisig_out &msout, const key &secret_key); } #endif /* RCTSIGS_H */ diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index f171d35b6de..58d4cdcedc4 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -1801,6 +1801,27 @@ bool simple_wallet::version(const std::vector &args) return true; } +bool simple_wallet::cold_sign_tx(const std::vector& ptx_vector, tools::wallet2::signed_tx_set &exported_txs, std::vector &dsts_info, std::function accept_func) +{ + std::vector tx_aux; + + message_writer(console_color_white, false) << tr("Please confirm the transaction on the device"); + + m_wallet->cold_sign_tx(ptx_vector, exported_txs, dsts_info, tx_aux); + + if (accept_func && !accept_func(exported_txs)) + { + MERROR("Transactions rejected by callback"); + return false; + } + + // aux info + m_wallet->cold_tx_aux_import(exported_txs.ptx, tx_aux); + + // import key images + return m_wallet->import_key_images(exported_txs.key_images); +} + bool simple_wallet::set_always_confirm_transfers(const std::vector &args/* = std::vector()*/) { const auto pwd_container = get_and_verify_password(); @@ -2253,6 +2274,33 @@ bool simple_wallet::set_ignore_fractional_outputs(const std::vector return true; } +bool simple_wallet::set_device_name(const std::vector &args/* = std::vector()*/) +{ + const auto pwd_container = get_and_verify_password(); + if (pwd_container) + { + if (args.size() == 0){ + fail_msg_writer() << tr("Device name not specified"); + return true; + } + + m_wallet->device_name(args[0]); + bool r = false; + try { + r = m_wallet->reconnect_device(); + if (!r){ + fail_msg_writer() << tr("Device reconnect failed"); + } + + } catch(const std::exception & e){ + MWARNING("Device reconnect failed: " << e.what()); + fail_msg_writer() << tr("Device reconnect failed: ") << e.what(); + } + + } + return true; +} + bool simple_wallet::help(const std::vector &args/* = std::vector()*/) { if(args.empty()) @@ -2537,6 +2585,10 @@ simple_wallet::simple_wallet() boost::bind(&simple_wallet::import_key_images, this, _1), tr("import_key_images "), tr("Import a signed key images list and verify their spent status.")); + m_cmd_binder.set_handler("hw_key_images_sync", + boost::bind(&simple_wallet::hw_key_images_sync, this, _1), + tr("hw_key_images_sync"), + tr("Synchronizes key images with the hw wallet.")); m_cmd_binder.set_handler("hw_reconnect", boost::bind(&simple_wallet::hw_reconnect, this, _1), tr("hw_reconnect"), @@ -2728,6 +2780,7 @@ bool simple_wallet::set_variable(const std::vector &args) CHECK_SIMPLE_VARIABLE("subaddress-lookahead", set_subaddress_lookahead, tr(":")); CHECK_SIMPLE_VARIABLE("segregation-height", set_segregation_height, tr("unsigned integer")); CHECK_SIMPLE_VARIABLE("ignore-fractional-outputs", set_ignore_fractional_outputs, tr("0 or 1")); + CHECK_SIMPLE_VARIABLE("device-name", set_device_name, tr("")); } fail_msg_writer() << tr("set: unrecognized argument(s)"); return true; @@ -4834,12 +4887,14 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector dsts_info; vector dsts; size_t num_subaddresses = 0; for (size_t i = 0; i < local_args.size(); ) { + dsts_info.emplace_back(); + cryptonote::address_parse_info & info = dsts_info.back(); cryptonote::tx_destination_entry de; - cryptonote::address_parse_info info; bool r = true; // check for a URI @@ -5123,6 +5178,28 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vectorget_account().get_device().has_tx_cold_sign()) + { + try + { + tools::wallet2::signed_tx_set signed_tx; + if (!cold_sign_tx(ptx_vector, signed_tx, dsts_info, [&](const tools::wallet2::signed_tx_set &tx){ return accept_loaded_tx(tx); })){ + fail_msg_writer() << tr("Failed to cold sign transaction with HW wallet"); + return true; + } + + commit_or_save(signed_tx.ptx, m_do_not_relay); + } + catch (const std::exception& e) + { + handle_transfer_exception(std::current_exception(), m_wallet->is_trusted_daemon()); + } + catch (...) + { + LOG_ERROR("Unknown error"); + fail_msg_writer() << tr("unknown error"); + } + } else if (m_wallet->watch_only()) { bool r = m_wallet->save_tx(ptx_vector, "unsigned_monero_tx"); @@ -5545,6 +5622,31 @@ bool simple_wallet::sweep_main(uint64_t below, bool locked, const std::vectorget_account().get_device().has_tx_cold_sign()) + { + try + { + tools::wallet2::signed_tx_set signed_tx; + std::vector dsts_info; + dsts_info.push_back(info); + + if (!cold_sign_tx(ptx_vector, signed_tx, dsts_info, [&](const tools::wallet2::signed_tx_set &tx){ return accept_loaded_tx(tx); })){ + fail_msg_writer() << tr("Failed to cold sign transaction with HW wallet"); + return true; + } + + commit_or_save(signed_tx.ptx, m_do_not_relay); + } + catch (const std::exception& e) + { + handle_transfer_exception(std::current_exception(), m_wallet->is_trusted_daemon()); + } + catch (...) + { + LOG_ERROR("Unknown error"); + fail_msg_writer() << tr("unknown error"); + } + } else if (m_wallet->watch_only()) { bool r = m_wallet->save_tx(ptx_vector, "unsigned_monero_tx"); @@ -7794,6 +7896,48 @@ bool simple_wallet::import_key_images(const std::vector &args) return true; } //---------------------------------------------------------------------------------------------------- +bool simple_wallet::hw_key_images_sync(const std::vector &args) +{ + if (!m_wallet->key_on_device()) + { + fail_msg_writer() << tr("command only supported by HW wallet"); + return true; + } + if (!m_wallet->get_account().get_device().has_ki_cold_sync()) + { + fail_msg_writer() << tr("hw wallet does not support cold KI sync"); + return true; + } + if (!m_wallet->is_trusted_daemon()) + { + fail_msg_writer() << tr("this command requires a trusted daemon. Enable with --trusted-daemon"); + return true; + } + + LOCK_IDLE_SCOPE(); + try + { + message_writer(console_color_white, false) << tr("Please confirm the key image sync on the device"); + + uint64_t spent = 0, unspent = 0; + uint64_t height = m_wallet->cold_key_image_sync(spent, unspent); + if (height > 0) + { + success_msg_writer() << tr("Signed key images imported to height ") << height << ", " + << print_money(spent) << tr(" spent, ") << print_money(unspent) << tr(" unspent"); + } else { + fail_msg_writer() << tr("Failed to import key images"); + } + } + catch (const std::exception &e) + { + fail_msg_writer() << tr("Failed to import key images: ") << e.what(); + return true; + } + + return true; +} +//---------------------------------------------------------------------------------------------------- bool simple_wallet::hw_reconnect(const std::vector &args) { if (!m_wallet->key_on_device()) diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index 39b715b7306..7d813ceb019 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -139,6 +139,7 @@ namespace cryptonote bool set_subaddress_lookahead(const std::vector &args = std::vector()); bool set_segregation_height(const std::vector &args = std::vector()); bool set_ignore_fractional_outputs(const std::vector &args = std::vector()); + bool set_device_name(const std::vector &args = std::vector()); bool help(const std::vector &args = std::vector()); bool start_mining(const std::vector &args); bool stop_mining(const std::vector &args); @@ -200,6 +201,7 @@ namespace cryptonote bool verify(const std::vector &args); bool export_key_images(const std::vector &args); bool import_key_images(const std::vector &args); + bool hw_key_images_sync(const std::vector &args); bool hw_reconnect(const std::vector &args); bool export_outputs(const std::vector &args); bool import_outputs(const std::vector &args); @@ -224,6 +226,7 @@ namespace cryptonote bool unblackball(const std::vector& args); bool blackballed(const std::vector& args); bool version(const std::vector& args); + bool cold_sign_tx(const std::vector& ptx_vector, tools::wallet2::signed_tx_set &exported_txs, std::vector &dsts_info, std::function accept_func); uint64_t get_daemon_blockchain_height(std::string& err); bool try_connect_to_daemon(bool silent = false, uint32_t* version = nullptr); diff --git a/src/wallet/CMakeLists.txt b/src/wallet/CMakeLists.txt index be10b9f6241..4932dd4b681 100644 --- a/src/wallet/CMakeLists.txt +++ b/src/wallet/CMakeLists.txt @@ -57,6 +57,7 @@ target_link_libraries(wallet common cryptonote_core mnemonics + device_trezor ${LMDB_LIBRARY} ${Boost_CHRONO_LIBRARY} ${Boost_SERIALIZATION_LIBRARY} diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 37b60c5d73f..f2e54cf0998 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -71,6 +71,8 @@ using namespace epee; #include "common/notify.h" #include "ringct/rctSigs.h" #include "ringdb.h" +#include "device/device_cold.hpp" +#include "device_trezor/device_trezor.hpp" extern "C" { @@ -768,6 +770,11 @@ uint32_t get_subaddress_clamped_sum(uint32_t idx, uint32_t extra) return idx + extra; } +static void setup_shim(hw::wallet_shim * shim, tools::wallet2 * wallet) +{ + shim->get_tx_pub_key_from_received_outs = boost::bind(&tools::wallet2::get_tx_pub_key_from_received_outs, wallet, _1); +} + //----------------------------------------------------------------- } //namespace @@ -1060,8 +1067,9 @@ bool wallet2::get_multisig_seed(epee::wipeable_string& seed, const epee::wipeabl bool wallet2::reconnect_device() { bool r = true; - hw::device &hwdev = hw::get_device(m_device_name); + hw::device &hwdev = lookup_device(m_device_name); hwdev.set_name(m_device_name); + hwdev.set_network_type(m_nettype); r = hwdev.init(); if (!r){ LOG_PRINT_L2("Could not init device"); @@ -2944,6 +2952,7 @@ bool wallet2::deinit() { m_is_initialized=false; unlock_keys_file(); + m_account.deinit(); return true; } //---------------------------------------------------------------------------------------------------- @@ -3413,13 +3422,20 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_ r = epee::serialization::load_t_from_binary(m_account, account_data); THROW_WALLET_EXCEPTION_IF(!r, error::invalid_password); - if (m_key_device_type == hw::device::device_type::LEDGER) { + if (m_key_device_type == hw::device::device_type::LEDGER || m_key_device_type == hw::device::device_type::TREZOR) { LOG_PRINT_L0("Account on device. Initing device..."); - hw::device &hwdev = hw::get_device(m_device_name); - hwdev.set_name(m_device_name); - hwdev.init(); - hwdev.connect(); + hw::device &hwdev = lookup_device(m_device_name); + THROW_WALLET_EXCEPTION_IF(!hwdev.set_name(m_device_name), error::wallet_internal_error, "Could not set device name " + m_device_name); + hwdev.set_network_type(m_nettype); + THROW_WALLET_EXCEPTION_IF(!hwdev.init(), error::wallet_internal_error, "Could not initialize the device " + m_device_name); + THROW_WALLET_EXCEPTION_IF(!hwdev.connect(), error::wallet_internal_error, "Could not connect to the device " + m_device_name); m_account.set_device(hwdev); + + account_public_address device_account_public_address; + THROW_WALLET_EXCEPTION_IF(!hwdev.get_public_address(device_account_public_address), error::wallet_internal_error, "Cannot get a device address"); + THROW_WALLET_EXCEPTION_IF(device_account_public_address != m_account.get_keys().m_account_address, error::wallet_internal_error, "Device wallet does not match wallet address. " + "Device address: " + cryptonote::get_account_address_as_str(m_nettype, false, device_account_public_address) + + ", wallet address: " + m_account.get_public_address_str(m_nettype)); LOG_PRINT_L0("Device inited..."); } else if (key_on_device()) { THROW_WALLET_EXCEPTION(error::wallet_internal_error, "hardware device not supported"); @@ -3450,7 +3466,7 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_ const cryptonote::account_keys& keys = m_account.get_keys(); hw::device &hwdev = m_account.get_device(); r = r && hwdev.verify_keys(keys.m_view_secret_key, keys.m_account_address.m_view_public_key); - if(!m_watch_only && !m_multisig) + if(!m_watch_only && !m_multisig && hwdev.device_protocol() != hw::device::PROTOCOL_COLD) r = r && hwdev.verify_keys(keys.m_spend_secret_key, keys.m_account_address.m_spend_public_key); THROW_WALLET_EXCEPTION_IF(!r, error::invalid_password); @@ -3474,7 +3490,7 @@ bool wallet2::verify_password(const epee::wipeable_string& password) { // this temporary unlocking is necessary for Windows (otherwise the file couldn't be loaded). unlock_keys_file(); - bool r = verify_password(m_keys_file, password, m_watch_only || m_multisig, m_account.get_device(), m_kdf_rounds); + bool r = verify_password(m_keys_file, password, m_account.get_device().device_protocol() == hw::device::PROTOCOL_COLD || m_watch_only || m_multisig, m_account.get_device(), m_kdf_rounds); lock_keys_file(); return r; } @@ -3914,8 +3930,9 @@ void wallet2::restore(const std::string& wallet_, const epee::wipeable_string& p THROW_WALLET_EXCEPTION_IF(boost::filesystem::exists(m_keys_file, ignored_ec), error::file_exists, m_keys_file); } - auto &hwdev = hw::get_device(device_name); + auto &hwdev = lookup_device(device_name); hwdev.set_name(device_name); + hwdev.set_network_type(m_nettype); m_account.create_from_device(hwdev); m_key_device_type = m_account.get_device().get_type(); @@ -5781,22 +5798,8 @@ bool wallet2::parse_tx_from_str(const std::string &signed_tx_st, std::vector m_transfers.size()) - { - LOG_PRINT_L1("More key images returned that we know outputs for"); - return false; - } - for (size_t i = 0; i < signed_txs.key_images.size(); ++i) - { - transfer_details &td = m_transfers[i]; - if (td.m_key_image_known && !td.m_key_image_partial && td.m_key_image != signed_txs.key_images[i]) - LOG_PRINT_L0("WARNING: imported key image differs from previously known key image at index " << i << ": trusting imported one"); - td.m_key_image = signed_txs.key_images[i]; - m_key_images[m_transfers[i].m_key_image] = i; - td.m_key_image_known = true; - td.m_key_image_partial = false; - m_pub_keys[m_transfers[i].get_public_key()] = i; - } + bool r = import_key_images(signed_txs.key_images); + if (!r) return false; ptx = signed_txs.ptx; @@ -6346,6 +6349,19 @@ crypto::chacha_key wallet2::get_ringdb_key() return *m_ringdb_key; } +void wallet2::register_devices(){ + hw::trezor::register_all(); +} + +hw::device& wallet2::lookup_device(const std::string & device_descriptor){ + if (!m_devices_registered){ + m_devices_registered = true; + register_devices(); + } + + return hw::get_device(device_descriptor); +} + bool wallet2::add_rings(const crypto::chacha_key &key, const cryptonote::transaction_prefix &tx) { if (!m_ringdb) @@ -9084,6 +9100,62 @@ std::vector wallet2::create_transactions_from(const crypton return ptx_vector; } //---------------------------------------------------------------------------------------------------- +void wallet2::cold_tx_aux_import(const std::vector & ptx, const std::vector & tx_device_aux) +{ + CHECK_AND_ASSERT_THROW_MES(ptx.size() == tx_device_aux.size(), "TX aux has invalid size"); + for (size_t i = 0; i < ptx.size(); ++i){ + crypto::hash txid; + txid = get_transaction_hash(ptx[i].tx); + set_tx_device_aux(txid, tx_device_aux[i]); + } +} +//---------------------------------------------------------------------------------------------------- +void wallet2::cold_sign_tx(const std::vector& ptx_vector, signed_tx_set &exported_txs, std::vector &dsts_info, std::vector & tx_device_aux) +{ + auto & hwdev = get_account().get_device(); + if (!hwdev.has_tx_cold_sign()){ + throw std::invalid_argument("Device does not support cold sign protocol"); + } + + unsigned_tx_set txs; + for (auto &tx: ptx_vector) + { + txs.txes.push_back(get_construction_data_with_decrypted_short_payment_id(tx, m_account.get_device())); + } + txs.transfers = m_transfers; + + auto dev_cold = dynamic_cast<::hw::device_cold*>(&hwdev); + CHECK_AND_ASSERT_THROW_MES(dev_cold, "Device does not implement cold signing interface"); + + hw::tx_aux_data aux_data; + hw::wallet_shim wallet_shim; + setup_shim(&wallet_shim, this); + aux_data.tx_recipients = dsts_info; + dev_cold->tx_sign(&wallet_shim, txs, exported_txs, aux_data); + tx_device_aux = aux_data.tx_device_aux; + + MDEBUG("Signed tx data from hw: " << exported_txs.ptx.size() << " transactions"); + for (auto &c_ptx: exported_txs.ptx) LOG_PRINT_L0(cryptonote::obj_to_json_str(c_ptx.tx)); +} +//---------------------------------------------------------------------------------------------------- +uint64_t wallet2::cold_key_image_sync(uint64_t &spent, uint64_t &unspent) { + auto & hwdev = get_account().get_device(); + if (!hwdev.has_ki_cold_sync()){ + throw std::invalid_argument("Device does not support cold ki sync protocol"); + } + + auto dev_cold = dynamic_cast<::hw::device_cold*>(&hwdev); + CHECK_AND_ASSERT_THROW_MES(dev_cold, "Device does not implement cold signing interface"); + + std::vector> ski; + hw::wallet_shim wallet_shim; + setup_shim(&wallet_shim, this); + + dev_cold->ki_sync(&wallet_shim, m_transfers, ski); + + return import_key_images(ski, spent, unspent); +} +//---------------------------------------------------------------------------------------------------- void wallet2::get_hard_fork_info(uint8_t version, uint64_t &earliest_height) const { boost::optional result = m_node_rpc_proxy.get_earliest_height(version, earliest_height); @@ -10240,6 +10312,19 @@ std::string wallet2::get_tx_note(const crypto::hash &txid) const return i->second; } +void wallet2::set_tx_device_aux(const crypto::hash &txid, const std::string &aux) +{ + m_tx_device[txid] = aux; +} + +std::string wallet2::get_tx_device_aux(const crypto::hash &txid) const +{ + std::unordered_map::const_iterator i = m_tx_device.find(txid); + if (i == m_tx_device.end()) + return std::string(); + return i->second; +} + void wallet2::set_attribute(const std::string &key, const std::string &value) { m_attributes[key] = value; @@ -10785,6 +10870,29 @@ uint64_t wallet2::import_key_images(const std::vector key_images) +{ + if (key_images.size() > m_transfers.size()) + { + LOG_PRINT_L1("More key images returned that we know outputs for"); + return false; + } + for (size_t i = 0; i < key_images.size(); ++i) + { + transfer_details &td = m_transfers[i]; + if (td.m_key_image_known && !td.m_key_image_partial && td.m_key_image != key_images[i]) + LOG_PRINT_L0("WARNING: imported key image differs from previously known key image at index " << i << ": trusting imported one"); + td.m_key_image = key_images[i]; + m_key_images[m_transfers[i].m_key_image] = i; + td.m_key_image_known = true; + td.m_key_image_partial = false; + m_pub_keys[m_transfers[i].get_public_key()] = i; + } + + return true; +} + wallet2::payment_container wallet2::export_payments() const { payment_container payments; diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 680196f0198..7d78a3fee76 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -54,6 +54,7 @@ #include "ringct/rctTypes.h" #include "ringct/rctOps.h" #include "checkpoints/checkpoints.h" +#include "serialization/pair.h" #include "wallet_errors.h" #include "common/password.h" @@ -764,6 +765,9 @@ namespace tools std::vector create_transactions_all(uint64_t below, const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector& extra, uint32_t subaddr_account, std::set subaddr_indices); std::vector create_transactions_single(const crypto::key_image &ki, const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector& extra); std::vector create_transactions_from(const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, std::vector unused_transfers_indices, std::vector unused_dust_indices, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector& extra); + void cold_tx_aux_import(const std::vector& ptx, const std::vector& tx_device_aux); + void cold_sign_tx(const std::vector& ptx_vector, signed_tx_set &exported_txs, std::vector &dsts_info, std::vector & tx_device_aux); + uint64_t cold_key_image_sync(uint64_t &spent, uint64_t &unspent); bool load_multisig_tx(cryptonote::blobdata blob, multisig_tx_set &exported_txs, std::function accept_func = NULL); bool load_multisig_tx_from_file(const std::string &filename, multisig_tx_set &exported_txs, std::function accept_func = NULL); bool sign_multisig_tx_from_file(const std::string &filename, std::vector &txids, std::function accept_func); @@ -892,6 +896,9 @@ namespace tools if(ver < 25) return; a & m_last_block_reward; + if(ver < 26) + return; + a & m_tx_device; } /*! @@ -1020,6 +1027,9 @@ namespace tools void set_tx_note(const crypto::hash &txid, const std::string ¬e); std::string get_tx_note(const crypto::hash &txid) const; + void set_tx_device_aux(const crypto::hash &txid, const std::string &aux); + std::string get_tx_device_aux(const crypto::hash &txid) const; + void set_description(const std::string &description); std::string get_description() const; @@ -1074,6 +1084,8 @@ namespace tools std::vector> export_key_images() const; uint64_t import_key_images(const std::vector> &signed_key_images, uint64_t &spent, uint64_t &unspent, bool check_spent = true); uint64_t import_key_images(const std::string &filename, uint64_t &spent, uint64_t &unspent); + bool import_key_images(std::vector key_images); + crypto::public_key get_tx_pub_key_from_received_outs(const tools::wallet2::transfer_details &td) const; void update_pool_state(bool refreshed = false); void remove_obsolete_pool_txs(const std::vector &tx_hashes); @@ -1236,7 +1248,6 @@ namespace tools void set_unspent(size_t idx); void get_outs(std::vector> &outs, const std::vector &selected_transfers, size_t fake_outputs_count); bool tx_add_fake_output(std::vector> &outs, uint64_t global_index, const crypto::public_key& tx_public_key, const rct::key& mask, uint64_t real_index, bool unlocked) const; - crypto::public_key get_tx_pub_key_from_received_outs(const tools::wallet2::transfer_details &td) const; bool should_pick_a_second_output(bool use_rct, size_t n_transfers, const std::vector &unused_transfers_indices, const std::vector &unused_dust_indices) const; std::vector get_only_rct(const std::vector &unused_dust_indices, const std::vector &unused_transfers_indices) const; void scan_output(const cryptonote::transaction &tx, const crypto::public_key &tx_pub_key, size_t i, tx_scan_info_t &tx_scan_info, int &num_vouts_received, std::unordered_map &tx_money_got_in_outs, std::vector &outs); @@ -1253,6 +1264,9 @@ namespace tools crypto::chacha_key get_ringdb_key(); void setup_keys(const epee::wipeable_string &password); + void register_devices(); + hw::device& lookup_device(const std::string & device_descriptor); + bool get_rct_distribution(uint64_t &start_height, std::vector &distribution); uint64_t get_segregation_fork_height() const; @@ -1347,6 +1361,9 @@ namespace tools size_t m_subaddress_lookahead_major, m_subaddress_lookahead_minor; std::string m_device_name; + // Aux transaction data from device + std::unordered_map m_tx_device; + // Light wallet bool m_light_wallet; /* sends view key to daemon for scanning */ uint64_t m_light_wallet_scanned_block_height; @@ -1373,11 +1390,12 @@ namespace tools boost::optional m_encrypt_keys_after_refresh; bool m_unattended; + bool m_devices_registered; std::shared_ptr m_tx_notify; }; } -BOOST_CLASS_VERSION(tools::wallet2, 25) +BOOST_CLASS_VERSION(tools::wallet2, 26) BOOST_CLASS_VERSION(tools::wallet2::transfer_details, 9) BOOST_CLASS_VERSION(tools::wallet2::multisig_info, 1) BOOST_CLASS_VERSION(tools::wallet2::multisig_info::LR, 0) From e61062b6f23cd27dba1da145c8590aff6fbf2f10 Mon Sep 17 00:00:00 2001 From: cryptochangements34 Date: Fri, 2 Nov 2018 19:31:31 -0400 Subject: [PATCH 0712/1404] use current height - 1 for top block height in err msgs --- src/rpc/core_rpc_server.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index aa9d3d64bf2..bd410047a1b 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -1010,7 +1010,7 @@ namespace cryptonote if(m_core.get_current_blockchain_height() <= h) { error_resp.code = CORE_RPC_ERROR_CODE_TOO_BIG_HEIGHT; - error_resp.message = std::string("Too big height: ") + std::to_string(h) + ", current blockchain height = " + std::to_string(m_core.get_current_blockchain_height()); + error_resp.message = std::string("Requested block height: ") + std::to_string(h) + " greater than current top block height: " + std::to_string(m_core.get_current_blockchain_height() - 1); } res = string_tools::pod_to_hex(m_core.get_block_id_by_height(h)); return true; @@ -1457,7 +1457,7 @@ namespace cryptonote if(m_core.get_current_blockchain_height() <= req.height) { error_resp.code = CORE_RPC_ERROR_CODE_TOO_BIG_HEIGHT; - error_resp.message = std::string("Too big height: ") + std::to_string(req.height) + ", current blockchain height = " + std::to_string(m_core.get_current_blockchain_height()); + error_resp.message = std::string("Requested block height: ") + std::to_string(req.height) + " greater than current top block height: " + std::to_string(m_core.get_current_blockchain_height() - 1); return false; } crypto::hash block_hash = m_core.get_block_id_by_height(req.height); @@ -1502,7 +1502,7 @@ namespace cryptonote if(m_core.get_current_blockchain_height() <= req.height) { error_resp.code = CORE_RPC_ERROR_CODE_TOO_BIG_HEIGHT; - error_resp.message = std::string("Too big height: ") + std::to_string(req.height) + ", current blockchain height = " + std::to_string(m_core.get_current_blockchain_height()); + error_resp.message = std::string("Requested block height: ") + std::to_string(req.height) + " greater than current top block height: " + std::to_string(m_core.get_current_blockchain_height() - 1); return false; } block_hash = m_core.get_block_id_by_height(req.height); From 0afdb00b9d4f6ad276b5b2cd902c16ef6e5f7c06 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 3 Nov 2018 11:49:45 +0000 Subject: [PATCH 0713/1404] wallet2: fix print_ring printing double entries for transactions When a tx gets from unconfirmed to conirmed, the rings for that transaction were being added twice --- src/wallet/wallet2.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 37b60c5d73f..659fe0f58a0 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -1919,6 +1919,7 @@ void wallet2::process_outgoing(const crypto::hash &txid, const cryptonote::trans entry.first->second.m_subaddr_indices = subaddr_indices; } + entry.first->second.m_rings.clear(); for (const auto &in: tx.vin) { if (in.type() != typeid(cryptonote::txin_to_key)) From 8f3963d20060104c3a74210cf5cb1c616a2b0712 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 3 Nov 2018 10:31:03 +0000 Subject: [PATCH 0714/1404] wallet2: demote a few uninteresting recurring logs to TRACE --- src/wallet/wallet2.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 37b60c5d73f..f3ef670951d 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -2290,7 +2290,7 @@ void wallet2::remove_obsolete_pool_txs(const std::vector &tx_hashe //---------------------------------------------------------------------------------------------------- void wallet2::update_pool_state(bool refreshed) { - MDEBUG("update_pool_state start"); + MTRACE("update_pool_state start"); auto keys_reencryptor = epee::misc_utils::create_scope_leave_handler([&, this]() { if (m_encrypt_keys_after_refresh) @@ -2309,7 +2309,7 @@ void wallet2::update_pool_state(bool refreshed) THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_transaction_pool_hashes.bin"); THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_transaction_pool_hashes.bin"); THROW_WALLET_EXCEPTION_IF(res.status != CORE_RPC_STATUS_OK, error::get_tx_pool_error); - MDEBUG("update_pool_state got pool"); + MTRACE("update_pool_state got pool"); // remove any pending tx that's not in the pool std::unordered_map::iterator it = m_unconfirmed_txs.begin(); @@ -2366,7 +2366,7 @@ void wallet2::update_pool_state(bool refreshed) } } } - MDEBUG("update_pool_state done first loop"); + MTRACE("update_pool_state done first loop"); // remove pool txes to us that aren't in the pool anymore // but only if we just refreshed, so that the tx can go in @@ -2375,7 +2375,7 @@ void wallet2::update_pool_state(bool refreshed) if (refreshed) remove_obsolete_pool_txs(res.tx_hashes); - MDEBUG("update_pool_state done second loop"); + MTRACE("update_pool_state done second loop"); // gather txids of new pool txes to us std::vector> txids; @@ -2512,7 +2512,7 @@ void wallet2::update_pool_state(bool refreshed) LOG_PRINT_L0("Error calling gettransactions daemon RPC: r " << r << ", status " << res.status); } } - MDEBUG("update_pool_state end"); + MTRACE("update_pool_state end"); } //---------------------------------------------------------------------------------------------------- void wallet2::fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height, std::list &short_chain_history, bool force) From f26ce08c8abbff51f50c13848e8899865862515d Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 26 Oct 2018 14:25:20 +0000 Subject: [PATCH 0715/1404] wallet: add a non destructive blockchain rescan --- src/simplewallet/simplewallet.cpp | 37 ++++++++++++++------ src/simplewallet/simplewallet.h | 5 ++- src/wallet/wallet2.cpp | 22 ++++++++++-- src/wallet/wallet2.h | 2 +- src/wallet/wallet_rpc_server.cpp | 2 +- src/wallet/wallet_rpc_server_commands_defs.h | 3 ++ 6 files changed, 54 insertions(+), 17 deletions(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 18b596662d7..ef13bc889d5 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -2491,6 +2491,7 @@ simple_wallet::simple_wallet() tr("Show the unspent outputs of a specified address within an optional amount range.")); m_cmd_binder.set_handler("rescan_bc", boost::bind(&simple_wallet::rescan_blockchain, this, _1), + tr("rescan_bc [hard]"), tr("Rescan the blockchain from scratch, losing any information which can not be recovered from the blockchain itself.")); m_cmd_binder.set_handler("set_tx_note", boost::bind(&simple_wallet::set_tx_note, this, _1), @@ -4237,15 +4238,15 @@ boost::optional simple_wallet::on_get_password(const char return pwd_container->password(); } //---------------------------------------------------------------------------------------------------- -bool simple_wallet::refresh_main(uint64_t start_height, bool reset, bool is_init) +bool simple_wallet::refresh_main(uint64_t start_height, enum ResetType reset, bool is_init) { if (!try_connect_to_daemon(is_init)) return true; LOCK_IDLE_SCOPE(); - if (reset) - m_wallet->rescan_blockchain(false); + if (reset != ResetNone) + m_wallet->rescan_blockchain(reset == ResetHard, false); #ifdef HAVE_READLINE rdln::suspend_readline pause_readline; @@ -4324,7 +4325,7 @@ bool simple_wallet::refresh(const std::vector& args) start_height = 0; } } - return refresh_main(start_height, false); + return refresh_main(start_height, ResetNone); } //---------------------------------------------------------------------------------------------------- bool simple_wallet::show_balance_unlocked(bool detailed) @@ -6982,15 +6983,29 @@ bool simple_wallet::unspent_outputs(const std::vector &args_) //---------------------------------------------------------------------------------------------------- bool simple_wallet::rescan_blockchain(const std::vector &args_) { - message_writer() << tr("Warning: this will lose any information which can not be recovered from the blockchain."); - message_writer() << tr("This includes destination addresses, tx secret keys, tx notes, etc"); - std::string confirm = input_line(tr("Rescan anyway ? (Y/Yes/N/No): ")); - if(!std::cin.eof()) + bool hard = false; + if (!args_.empty()) { - if (!command_line::is_yes(confirm)) + if (args_[0] != "hard") + { + fail_msg_writer() << tr("usage: rescan_bc [hard]"); return true; + } + hard = true; + } + + if (hard) + { + message_writer() << tr("Warning: this will lose any information which can not be recovered from the blockchain."); + message_writer() << tr("This includes destination addresses, tx secret keys, tx notes, etc"); + std::string confirm = input_line(tr("Rescan anyway ? (Y/Yes/N/No): ")); + if(!std::cin.eof()) + { + if (!command_line::is_yes(confirm)) + return true; + } } - return refresh_main(0, true); + return refresh_main(0, hard ? ResetHard : ResetSoft, true); } //---------------------------------------------------------------------------------------------------- void simple_wallet::wallet_idle_thread() @@ -7038,7 +7053,7 @@ bool simple_wallet::run() // check and display warning, but go on anyway try_connect_to_daemon(); - refresh_main(0, false, true); + refresh_main(0, ResetNone, true); m_auto_refresh_enabled = m_wallet->auto_refresh(); m_idle_thread = boost::thread([&]{wallet_idle_thread();}); diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index 39b715b7306..6eedef4a2e8 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -83,6 +83,9 @@ namespace cryptonote std::string get_commands_str(); std::string get_command_usage(const std::vector &args); private: + + enum ResetType { ResetNone, ResetSoft, ResetHard }; + bool handle_command_line(const boost::program_options::variables_map& vm); bool run_console_handler(); @@ -188,7 +191,7 @@ namespace cryptonote bool show_transfers(const std::vector &args); bool unspent_outputs(const std::vector &args); bool rescan_blockchain(const std::vector &args); - bool refresh_main(uint64_t start_height, bool reset = false, bool is_init = false); + bool refresh_main(uint64_t start_height, ResetType reset, bool is_init = false); bool set_tx_note(const std::vector &args); bool get_tx_note(const std::vector &args); bool set_description(const std::vector &args); diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index c5618375eb6..e666eda2c64 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -5073,11 +5073,27 @@ void wallet2::rescan_spent() } } //---------------------------------------------------------------------------------------------------- -void wallet2::rescan_blockchain(bool refresh) +void wallet2::rescan_blockchain(bool hard, bool refresh) { - clear(); + if(hard) + { + clear(); + setup_new_blockchain(); + } + else + { + m_blockchain.clear(); + m_transfers.clear(); + m_key_images.clear(); + m_pub_keys.clear(); + m_scanned_pool_txs[0].clear(); + m_scanned_pool_txs[1].clear(); - setup_new_blockchain(); + cryptonote::block b; + generate_genesis(b); + m_blockchain.push_back(get_block_hash(b)); + m_last_block_reward = cryptonote::get_outs_money_amount(b.miner_tx); + } if (refresh) this->refresh(false); diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 680196f0198..679f1c1ad29 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -782,7 +782,7 @@ namespace tools uint64_t get_blockchain_current_height() const { return m_light_wallet_blockchain_height ? m_light_wallet_blockchain_height : m_blockchain.size(); } void rescan_spent(); - void rescan_blockchain(bool refresh = true); + void rescan_blockchain(bool hard, bool refresh = true); bool is_transfer_unlocked(const transfer_details& td) const; bool is_transfer_unlocked(uint64_t unlock_time, uint64_t block_height) const; diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index 1b63d65b63b..09f78d593b6 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -1613,7 +1613,7 @@ namespace tools try { - m_wallet->rescan_blockchain(); + m_wallet->rescan_blockchain(req.hard); } catch (const std::exception& e) { diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h index 2377b69e369..1d7307b7ae7 100644 --- a/src/wallet/wallet_rpc_server_commands_defs.h +++ b/src/wallet/wallet_rpc_server_commands_defs.h @@ -992,7 +992,10 @@ namespace wallet_rpc { struct request { + bool hard; + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_OPT(hard, false); END_KV_SERIALIZE_MAP() }; From 177a9d76f99ce259598f85f99b1d2ecf77a7fdd2 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 3 Nov 2018 18:32:05 +0000 Subject: [PATCH 0716/1404] wallet: warn if lockable memory limit is too low --- src/common/util.cpp | 15 +++++++++++++++ src/common/util.h | 2 ++ src/wallet/wallet_args.cpp | 8 ++++++++ 3 files changed, 25 insertions(+) diff --git a/src/common/util.cpp b/src/common/util.cpp index 43973c511f2..58b0d8210f6 100644 --- a/src/common/util.cpp +++ b/src/common/util.cpp @@ -728,6 +728,21 @@ std::string get_nix_version_display_string() return true; } + ssize_t get_lockable_memory() + { +#ifdef __GLIBC__ + struct rlimit rlim; + if (getrlimit(RLIMIT_MEMLOCK, &rlim) < 0) + { + MERROR("Failed to determine the lockable memory limit"); + return -1; + } + return rlim.rlim_cur; +#else + return -1; +#endif + } + bool on_startup() { mlog_configure("", true); diff --git a/src/common/util.h b/src/common/util.h index e793a42b552..1c5c5f4e7f3 100644 --- a/src/common/util.h +++ b/src/common/util.h @@ -221,6 +221,8 @@ namespace tools void set_strict_default_file_permissions(bool strict); + ssize_t get_lockable_memory(); + void set_max_concurrency(unsigned n); unsigned get_max_concurrency(); diff --git a/src/wallet/wallet_args.cpp b/src/wallet/wallet_args.cpp index 95a4e0ad663..b9d0a6a7553 100644 --- a/src/wallet/wallet_args.cpp +++ b/src/wallet/wallet_args.cpp @@ -211,6 +211,14 @@ namespace wallet_args Print(print) << boost::format(wallet_args::tr("Logging to %s")) % log_path; + const ssize_t lockable_memory = tools::get_lockable_memory(); + if (lockable_memory >= 0 && lockable_memory < 256 * 4096) // 256 pages -> at least 256 secret keys and other such small/medium objects + Print(print) << tr("WARNING: You may not have a high enough lockable memory limit") +#ifdef ELPP_OS_UNIX + << ", " << tr("see ulimit -l") +#endif + ; + return {std::move(vm), should_terminate}; } } From 5878fe95ce422a509eae8c5c69e0e6d59e5d64a6 Mon Sep 17 00:00:00 2001 From: stoffu Date: Sun, 4 Nov 2018 10:24:33 +0900 Subject: [PATCH 0717/1404] simplewallet: don't skip asking for password when watch-only --- src/simplewallet/simplewallet.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index f171d35b6de..27bc05a2eb5 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -104,7 +104,7 @@ typedef cryptonote::simple_wallet sw; #define SCOPED_WALLET_UNLOCK() \ LOCK_IDLE_SCOPE(); \ boost::optional pwd_container = boost::none; \ - if (m_wallet->ask_password() && !m_wallet->watch_only() && !(pwd_container = get_and_verify_password())) { return true; } \ + if (m_wallet->ask_password() && !(pwd_container = get_and_verify_password())) { return true; } \ tools::wallet_keys_unlocker unlocker(*m_wallet, pwd_container); enum TransferType { From 5d4f3df887a4a00d151f61aa6baca2583a574e72 Mon Sep 17 00:00:00 2001 From: stoffu Date: Sun, 4 Nov 2018 10:48:49 +0900 Subject: [PATCH 0718/1404] simplewallet: reorganize SCOPED_WALLET_UNLOCK a bit more Followup on #4555 --- src/simplewallet/simplewallet.cpp | 42 ++++++++++++++++++------------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index f171d35b6de..07b270e9161 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -580,12 +580,12 @@ std::string simple_wallet::get_command_usage(const std::vector &arg bool simple_wallet::viewkey(const std::vector &args/* = std::vector()*/) { - SCOPED_WALLET_UNLOCK(); // don't log PAUSE_READLINE(); if (m_wallet->key_on_device()) { std::cout << "secret: On device. Not available" << std::endl; } else { + SCOPED_WALLET_UNLOCK(); printf("secret: "); print_secret_key(m_wallet->get_account().get_keys().m_view_secret_key); putchar('\n'); @@ -602,12 +602,12 @@ bool simple_wallet::spendkey(const std::vector &args/* = std::vecto fail_msg_writer() << tr("wallet is watch-only and has no spend key"); return true; } - SCOPED_WALLET_UNLOCK(); // don't log PAUSE_READLINE(); if (m_wallet->key_on_device()) { std::cout << "secret: On device. Not available" << std::endl; } else { + SCOPED_WALLET_UNLOCK(); printf("secret: "); print_secret_key(m_wallet->get_account().get_keys().m_spend_secret_key); putchar('\n'); @@ -634,8 +634,6 @@ bool simple_wallet::print_seed(bool encrypted) return true; } - SCOPED_WALLET_UNLOCK(); - multisig = m_wallet->multisig(&ready); if (multisig) { @@ -645,7 +643,10 @@ bool simple_wallet::print_seed(bool encrypted) return true; } } - else if (!m_wallet->is_deterministic()) + + SCOPED_WALLET_UNLOCK(); + + if (!multisig && !m_wallet->is_deterministic()) { fail_msg_writer() << tr("wallet is non-deterministic and has no seed"); return true; @@ -1077,11 +1078,12 @@ bool simple_wallet::export_multisig(const std::vector &args) return true; } - SCOPED_WALLET_UNLOCK(); - const std::string filename = args[0]; if (m_wallet->confirm_export_overwrite() && !check_file_overwrite(filename)) return true; + + SCOPED_WALLET_UNLOCK(); + try { cryptonote::blobdata ciphertext = m_wallet->export_multisig(); @@ -1129,8 +1131,6 @@ bool simple_wallet::import_multisig(const std::vector &args) return true; } - SCOPED_WALLET_UNLOCK(); - std::vector info; for (size_t n = 0; n < args.size(); ++n) { @@ -1145,6 +1145,8 @@ bool simple_wallet::import_multisig(const std::vector &args) info.push_back(std::move(data)); } + SCOPED_WALLET_UNLOCK(); + // all read and parsed, actually import try { @@ -1282,11 +1284,11 @@ bool simple_wallet::submit_multisig(const std::vector &args) return true; } - SCOPED_WALLET_UNLOCK(); - if (!try_connect_to_daemon()) return true; + SCOPED_WALLET_UNLOCK(); + std::string filename = args[0]; try { @@ -1350,11 +1352,12 @@ bool simple_wallet::export_raw_multisig(const std::vector &args) return true; } - SCOPED_WALLET_UNLOCK(); - std::string filename = args[0]; if (m_wallet->confirm_export_overwrite() && !check_file_overwrite(filename)) return true; + + SCOPED_WALLET_UNLOCK(); + try { tools::wallet2::multisig_tx_set txs; @@ -5577,7 +5580,6 @@ bool simple_wallet::sweep_main(uint64_t below, bool locked, const std::vector &args_) { - SCOPED_WALLET_UNLOCK(); if (!try_connect_to_daemon()) return true; @@ -5725,6 +5727,8 @@ bool simple_wallet::sweep_single(const std::vector &args_) } } + SCOPED_WALLET_UNLOCK(); + try { // figure out what tx will be necessary @@ -7658,7 +7662,6 @@ bool simple_wallet::sign(const std::vector &args) return true; } - SCOPED_WALLET_UNLOCK(); std::string filename = args[0]; std::string data; bool r = epee::file_io_utils::load_file_to_string(filename, data); @@ -7667,6 +7670,9 @@ bool simple_wallet::sign(const std::vector &args) fail_msg_writer() << tr("failed to read file ") << filename; return true; } + + SCOPED_WALLET_UNLOCK(); + std::string signature = m_wallet->sign(data); success_msg_writer() << signature; return true; @@ -7728,11 +7734,12 @@ bool simple_wallet::export_key_images(const std::vector &args) return true; } - SCOPED_WALLET_UNLOCK(); std::string filename = args[0]; if (m_wallet->confirm_export_overwrite() && !check_file_overwrite(filename)) return true; + SCOPED_WALLET_UNLOCK(); + try { if (!m_wallet->export_key_images(filename)) @@ -7832,11 +7839,12 @@ bool simple_wallet::export_outputs(const std::vector &args) return true; } - SCOPED_WALLET_UNLOCK(); std::string filename = args[0]; if (m_wallet->confirm_export_overwrite() && !check_file_overwrite(filename)) return true; + SCOPED_WALLET_UNLOCK(); + try { std::string data = m_wallet->export_outputs_to_str(); From 6f7a5fd4f70ee981c4a4eb82adb0349a2c033a6b Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 31 Oct 2018 18:52:28 +0000 Subject: [PATCH 0719/1404] db_lmdb: slight speedup getting array data from the blockchain --- src/blockchain_db/lmdb/db_lmdb.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp index d642069f858..c4b16189971 100644 --- a/src/blockchain_db/lmdb/db_lmdb.cpp +++ b/src/blockchain_db/lmdb/db_lmdb.cpp @@ -3160,6 +3160,7 @@ void BlockchainLMDB::get_output_tx_and_index_from_global(const std::vectortx_hash, ot->local_index); - tx_out_indices.push_back(result); + const outtx *ot = (const outtx *)v.mv_data; + tx_out_indices.push_back(tx_out_index(ot->tx_hash, ot->local_index)); } TXN_POSTFIX_RDONLY(); @@ -3188,6 +3188,7 @@ void BlockchainLMDB::get_output_key(const uint64_t &amount, const std::vectordata; + outputs.push_back(okp->data); } else { const pre_rct_outkey *okp = (const pre_rct_outkey *)v.mv_data; + outputs.resize(outputs.size() + 1); + output_data_t &data = outputs.back(); memcpy(&data, &okp->data, sizeof(pre_rct_output_data_t)); data.commitment = rct::zeroCommit(amount); } - outputs.push_back(data); } TXN_POSTFIX_RDONLY(); @@ -3239,6 +3240,7 @@ void BlockchainLMDB::get_output_tx_and_index(const uint64_t& amount, const std:: indices.clear(); std::vector tx_indices; + tx_indices.reserve(offsets.size()); TXN_PREFIX_RDONLY(); RCURSOR(output_amounts); From 1426209a10602e07e80cf93117b1c80ca58ace9b Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 31 Oct 2018 19:02:47 +0000 Subject: [PATCH 0720/1404] blockchain: don't run threads if we have just one function to run --- src/cryptonote_core/blockchain.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 7576e0ed7b1..8b94a30f072 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -4171,7 +4171,7 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vectorcan_thread_bulk_indices()) threads = 1; - if (threads > 1) + if (threads > 1 && amounts.size() > 1) { tools::threadpool::waiter waiter; From 5808530f54d2f24410b24089f6d30163aed71d7b Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 1 Nov 2018 17:24:35 +0000 Subject: [PATCH 0721/1404] blockchain: remove unused output_scan_worker parameter --- src/cryptonote_core/blockchain.cpp | 11 ++++------- src/cryptonote_core/blockchain.h | 4 +--- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 8b94a30f072..e766fd6c816 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -3786,8 +3786,7 @@ bool Blockchain::cleanup_handle_incoming_blocks(bool force_sync) } //------------------------------------------------------------------ -//FIXME: unused parameter txs -void Blockchain::output_scan_worker(const uint64_t amount, const std::vector &offsets, std::vector &outputs, std::unordered_map &txs) const +void Blockchain::output_scan_worker(const uint64_t amount, const std::vector &offsets, std::vector &outputs) const { try { @@ -4164,9 +4163,7 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector> transactions(amounts.size()); - + // gather all the output keys threads = tpool.get_max_concurrency(); if (!m_db->can_thread_bulk_indices()) threads = 1; @@ -4178,7 +4175,7 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector &offsets, - std::vector &outputs, std::unordered_map &txs) const; + std::vector &outputs) const; /** * @brief computes the "short" and "long" hashes for a set of blocks From 5d7c2316045e416d0152dd5cd3a941be3843c67c Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 4 Nov 2018 15:38:59 +0000 Subject: [PATCH 0722/1404] rct: add a zeroCommit cache for common pre-rct case This is called for every pre-rct output at blockchain sync time, and a lot of them wil hit the cache, saving a scalarmult each. --- src/ringct/rctOps.cpp | 186 +++++++++++++++++++++++++++ tests/performance_tests/crypto_ops.h | 4 + tests/performance_tests/main.cpp | 2 + tests/unit_tests/ringct.cpp | 19 +++ 4 files changed, 211 insertions(+) diff --git a/src/ringct/rctOps.cpp b/src/ringct/rctOps.cpp index 41bbf6ca387..c64ee4daf22 100644 --- a/src/ringct/rctOps.cpp +++ b/src/ringct/rctOps.cpp @@ -30,6 +30,7 @@ #include #include "misc_log_ex.h" +#include "cryptonote_basic/cryptonote_format_utils.h" #include "rctOps.h" using namespace crypto; using namespace std; @@ -39,6 +40,183 @@ using namespace std; #define CHECK_AND_ASSERT_THROW_MES_L1(expr, message) {if(!(expr)) {MWARNING(message); throw std::runtime_error(message);}} +struct zero_commitment { uint64_t amount; rct::key commitment; }; +static const zero_commitment zero_commitments[] = { + { (uint64_t)0ull, {0x58, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66} }, + { (uint64_t)1ull, {0x17, 0x38, 0xeb, 0x7a, 0x67, 0x7c, 0x61, 0x49, 0x22, 0x8a, 0x2b, 0xea, 0xa2, 0x1b, 0xea, 0x9e, 0x33, 0x70, 0x80, 0x2d, 0x72, 0xa3, 0xee, 0xc7, 0x90, 0x11, 0x95, 0x80, 0xe0, 0x2b, 0xd5, 0x22} }, + { (uint64_t)2ull, {0x76, 0x24, 0x84, 0x63, 0xa, 0x6, 0x17, 0x17, 0x8d, 0xe, 0x33, 0xf3, 0x2e, 0xe, 0x11, 0x3e, 0xa8, 0x46, 0x86, 0x9d, 0x46, 0x4b, 0xb, 0x6f, 0xf1, 0x3b, 0x29, 0x97, 0x4, 0x9c, 0xda, 0x7d} }, + { (uint64_t)3ull, {0xcf, 0xf7, 0x7b, 0x56, 0x62, 0x1c, 0x4f, 0xef, 0x74, 0xcf, 0x37, 0xc1, 0x78, 0xd4, 0xb5, 0x8a, 0xf4, 0xad, 0x8c, 0xd4, 0x35, 0xfc, 0xb9, 0x62, 0x76, 0xbc, 0x15, 0x9c, 0x7c, 0x6a, 0x28, 0x8c} }, + { (uint64_t)4ull, {0x9a, 0xb8, 0x6c, 0x31, 0xf4, 0x22, 0xd8, 0x21, 0xb5, 0x22, 0x57, 0x30, 0xd1, 0xbf, 0x73, 0xa, 0x9b, 0x91, 0xd2, 0xee, 0xe3, 0x14, 0xb8, 0x4e, 0xbd, 0x4b, 0x93, 0xa6, 0x81, 0x61, 0x82, 0x66} }, + { (uint64_t)5ull, {0x32, 0xee, 0x2f, 0x65, 0x9a, 0xf6, 0x38, 0x58, 0xc2, 0xf7, 0xdc, 0x11, 0x1b, 0x3b, 0xb8, 0xfe, 0xc0, 0x2c, 0xac, 0x42, 0x38, 0x3b, 0xb7, 0x36, 0xde, 0x1, 0x8, 0x6f, 0x38, 0xf0, 0x12, 0x3c} }, + { (uint64_t)6ull, {0x47, 0x26, 0x2b, 0x1e, 0xa6, 0x43, 0x1, 0x6e, 0x38, 0x24, 0x17, 0x53, 0xa4, 0xfb, 0x39, 0x92, 0x9e, 0x31, 0xea, 0x9b, 0xd3, 0x41, 0x1a, 0xb1, 0x7f, 0x16, 0x6e, 0x61, 0xf6, 0xc, 0xe5, 0xa7} }, + { (uint64_t)7ull, {0xc6, 0x32, 0x93, 0x68, 0x79, 0x9a, 0xd, 0xed, 0x4c, 0x20, 0x25, 0x6b, 0xff, 0xe6, 0x45, 0x47, 0xf1, 0x7b, 0xc4, 0x23, 0x95, 0x4, 0xbe, 0x82, 0x4d, 0xff, 0x8a, 0x2b, 0xe1, 0xaf, 0xe3, 0xcd} }, + { (uint64_t)8ull, {0xd5, 0xf1, 0x50, 0x74, 0x33, 0x46, 0x19, 0xf, 0x84, 0x2b, 0x6, 0xb8, 0xfa, 0xe1, 0x20, 0xeb, 0x85, 0x24, 0x7e, 0x9f, 0x6d, 0xec, 0x88, 0xff, 0xa2, 0x23, 0xbf, 0x69, 0x94, 0xe9, 0xc8, 0xc2} }, + { (uint64_t)9ull, {0x56, 00, 0x23, 0x32, 0x9e, 0xc0, 0xfa, 0xf3, 0x3b, 0x5e, 0x3a, 0x5c, 0xb4, 0xea, 0xef, 0xee, 0x38, 0xf8, 0x96, 0x1c, 0x88, 0xb6, 0x6a, 0x2f, 0x19, 0xd4, 0x59, 0x51, 0x96, 0x9c, 0x6d, 0x1f} }, + { (uint64_t)10ull, {0x3, 0x80, 0xdc, 0x24, 0xcc, 0x97, 0xcc, 0xe6, 0x58, 0xc3, 0xa9, 0x47, 0xc5, 0x10, 0x25, 0xde, 0x1a, 0x69, 0x80, 0x3b, 0xdb, 0x50, 0x5, 0xe3, 0xb7, 0xdd, 0xa9, 0xd, 0x68, 0x59, 0xb0, 0x1c} }, + { (uint64_t)20ull, {0x9, 0x3, 0xf6, 0x2e, 0x97, 0x76, 0x47, 0x58, 0xfe, 0xf8, 0x9e, 0x5b, 0xec, 0x29, 0xef, 0x4f, 0xc5, 0xe6, 0x45, 0x4b, 0x2d, 0x47, 0x44, 0x47, 0x36, 0x4, 0x4c, 0x25, 0x2e, 0xe2, 0x8e, 0xba} }, + { (uint64_t)30ull, {0xa2, 0x8b, 0x89, 0xe0, 0xb, 0xed, 0x62, 0x31, 0x68, 0x5b, 0xf9, 0x74, 0x36, 0xf2, 0xba, 0x51, 0xa2, 0x51, 0x55, 0x7f, 0x8d, 0x17, 0xa, 0x78, 0xe3, 0x12, 0xd6, 0x24, 0xbf, 0x60, 0xff, 0xfe} }, + { (uint64_t)40ull, {0xb5, 0xc6, 0x95, 0x55, 0x6a, 0x28, 0x47, 0xb2, 0xe, 0x1c, 0xbb, 0x26, 0xe6, 0xa9, 0xc6, 0x8a, 0x61, 0xc5, 0x50, 0xce, 0xb7, 0xc3, 0x4, 0xfe, 0x92, 0x28, 0x3d, 0x29, 0xa9, 0xb2, 0x43, 0xcb} }, + { (uint64_t)50ull, {0x12, 0x8e, 0xc6, 0xcd, 0xc0, 0x6b, 0x43, 0xc5, 0xd0, 0x9c, 0x3f, 0x65, 0x2a, 0xe3, 0x44, 0x7f, 0x9b, 0x3f, 0x2c, 0x30, 0x91, 0x2d, 0xf0, 0x80, 0x37, 00, 0x85, 0xbc, 0xc, 0x9, 0xef, 0x78} }, + { (uint64_t)60ull, {0x1f, 0x9f, 0x40, 0x3a, 0xae, 0xa7, 0x16, 0xfb, 0xe2, 0x98, 0xa8, 0x14, 0xf1, 0xee, 0xbc, 0x1b, 0x73, 0x16, 0x8c, 0x37, 0xfa, 0xe3, 0x16, 0xeb, 0x65, 0x5, 0x81, 0x6f, 0xc2, 0x20, 0xeb, 0xfb} }, + { (uint64_t)70ull, {0x10, 0xa2, 0x38, 0xc5, 0xe4, 0x8e, 0x4b, 0x93, 0x99, 0xdb, 0xa6, 0xcb, 0xd9, 0x8e, 0x63, 0x54, 0x41, 0x59, 0xe9, 0x8c, 0x93, 0x5a, 0xc0, 0x60, 0x3d, 0x72, 0xde, 0xf, 0xff, 0x31, 0x53, 0xbb} }, + { (uint64_t)80ull, {0x75, 0xab, 0x78, 0xc7, 0x28, 0x1f, 0x69, 0x28, 0xf0, 0x94, 0x86, 0x5, 0x7a, 0x63, 0x64, 0x18, 0x27, 0xc5, 0x74, 0x84, 0xe3, 0xe9, 0x9a, 0x39, 0xf3, 0x12, 0xa4, 0x3a, 0x51, 0x9b, 0xda, 0x8} }, + { (uint64_t)90ull, {0xe9, 0x56, 0x7b, 0xa7, 0x88, 0xb8, 0x5b, 0x82, 0xc8, 0x65, 0x7a, 0x15, 0xa5, 0x48, 0x99, 0x5c, 0xf6, 0xb0, 0xbd, 0xd1, 0xc6, 0x2a, 0xda, 0x77, 0x55, 0xf2, 0x32, 0x3a, 0xd8, 0xa4, 0x8, 0x51} }, + { (uint64_t)100ull, {0xb6, 0x17, 0x36, 0xd5, 0xf2, 0x8d, 0xef, 0x28, 0x61, 0x6a, 0xfc, 0x47, 0x93, 0xe9, 0x9b, 0x27, 0xcd, 0x3e, 0x89, 0xfb, 0x91, 0xc1, 0x13, 0xd4, 0x30, 0x73, 0x65, 0xfb, 0x75, 0xde, 0xdf, 0x88} }, + { (uint64_t)200ull, {0x71, 0x3, 0xeb, 0x72, 0x19, 0x28, 0xd7, 0x91, 0x99, 0x87, 0xf3, 0x50, 0xca, 0xa5, 0x7a, 0xe7, 0xb0, 0x81, 0x57, 0x15, 0x3b, 0x4c, 0x43, 0xd, 0x3e, 0xde, 0xc0, 0xc2, 0x3, 0x7, 0x97, 0x44} }, + { (uint64_t)300ull, {0x24, 0x40, 0x9e, 0x92, 0x2e, 0xce, 0xd1, 0xa0, 0x5e, 0x4e, 0xac, 0xa3, 0xdf, 0x91, 0x19, 0xc3, 0x8a, 0x92, 0x2e, 0xb, 0x66, 0xd0, 0x2d, 0x9d, 0xd2, 0xfb, 0x1d, 0xcc, 0x20, 0xb9, 0xaf, 0xc7} }, + { (uint64_t)400ull, {0xa7, 0x72, 0x9f, 0xa9, 0x32, 0x81, 0x82, 0x99, 0x34, 0x11, 0x5d, 0x47, 0x5a, 0x67, 0x86, 0xa, 0x14, 0x12, 0xc5, 0xe5, 0x95, 0x12, 0x20, 0xd9, 0x60, 0xc2, 0x41, 0xa0, 0x19, 0x1a, 0x9e, 0x65} }, + { (uint64_t)500ull, {0x2e, 0x53, 0xc, 0x6, 0x1c, 0x6d, 0x9e, 0x97, 0xab, 0xaf, 0x46, 0x8c, 0x32, 0xb0, 0xad, 0xa7, 0x49, 0x22, 0x57, 0x72, 0xfc, 0xd1, 0x17, 0x41, 0xcb, 0x5c, 0x3, 0x5c, 0xdd, 0x26, 0x14, 0xe} }, + { (uint64_t)600ull, {0xa5, 0xb, 0x91, 0x9, 0x9d, 0xf1, 0xb1, 0x69, 0x4f, 0x30, 0xb5, 0x8f, 0xe6, 0x77, 0x68, 0x50, 0xdb, 0xdb, 0xf4, 0x6c, 0xed, 0x99, 0x7f, 0x52, 0x62, 0xa8, 0x51, 0x59, 0x40, 0x74, 0xa5, 0x9d} }, + { (uint64_t)700ull, {0x51, 0x2c, 0xf, 0xae, 0xcc, 0xbe, 0xf2, 0xfe, 0xe5, 0x75, 0x4c, 0x6a, 0x45, 0xfd, 0xc0, 0x75, 0x2d, 0x4f, 0x15, 0x22, 0xe7, 0x7f, 0xf0, 0xc4, 0x8d, 0xcb, 0x19, 0x91, 0x8a, 0x68, 0x84, 0xe0} }, + { (uint64_t)800ull, {0xda, 0xa9, 0xf9, 0xa5, 0xb9, 0x71, 0x33, 0x33, 0xe9, 0x8c, 0x5, 0xac, 0xe7, 0x27, 0xcc, 0xe, 0x7d, 0xc3, 0xf1, 0x59, 0x49, 0xe1, 0xef, 0x4d, 0x94, 0xfa, 0x47, 0xd6, 0x8a, 0x34, 0xc6, 0x75} }, + { (uint64_t)900ull, {0xc7, 0x2b, 0x18, 0xc9, 0x17, 0xcd, 0x43, 0xee, 0x78, 0x40, 0x5e, 0x39, 0x83, 0x98, 0xb8, 0x3a, 0xc0, 0x97, 0x7b, 0x25, 0x19, 0x90, 0xd8, 0x13, 0xc, 0x38, 0xba, 0x53, 0xb6, 0x3d, 0xb4, 0xf7} }, + { (uint64_t)1000ull, {0x1, 0xdf, 0x60, 0x91, 0xeb, 0x6a, 0x48, 0xe9, 0xe4, 0x22, 0x25, 0xb, 0xe3, 0x83, 0x88, 0xc8, 0x61, 0xb6, 0x55, 0x55, 0xa7, 0x20, 0xad, 0x15, 0x35, 0x86, 0xfe, 0x2b, 0xd2, 0x2f, 0xa2, 0x3d} }, + { (uint64_t)2000ull, {0x24, 0xf5, 0xb1, 0x34, 0x78, 0x46, 0xaf, 0x22, 0xb5, 0x6f, 0x41, 0x25, 0xb3, 0xe7, 0x67, 0x8c, 0xf8, 0x4b, 0x4f, 0xd2, 0xf9, 0x2e, 0x1c, 0x40, 0xaa, 0x3a, 0x1b, 0xe0, 0xc7, 0x4d, 0x95, 0xe6} }, + { (uint64_t)3000ull, {0xa7, 0x1c, 0x9a, 0x8f, 0x40, 0xc1, 0x25, 0x9c, 0x36, 0x26, 0x27, 0x73, 0xe0, 0x8, 0x20, 0x18, 0x3e, 0x6b, 0x59, 0xe0, 0x71, 0xc9, 0x9b, 0x34, 0x9b, 0xef, 0x8f, 0x7e, 0xd2, 0xc6, 0xad, 0xb9} }, + { (uint64_t)4000ull, {0x98, 0xdc, 0x74, 0xaf, 0x19, 0x89, 0xd3, 0x4b, 0x64, 0x2e, 0xb3, 0x6, 0x2d, 0xbc, 0x9d, 0xca, 0xd8, 0x1, 0xc5, 0x65, 0x27, 0x6, 0x93, 0x99, 0xe7, 0xc4, 0x11, 0xad, 0x14, 0x28, 0x82, 0xf6} }, + { (uint64_t)5000ull, {0x61, 0x76, 0xac, 0x4a, 0xc0, 0x6, 0x5e, 0x49, 0xd6, 0xc4, 0x41, 0xcf, 0x40, 0x4f, 0xad, 0xda, 0xad, 0x44, 0x93, 0xe, 0xf0, 0x3c, 0x68, 0x9, 0xad, 0xd7, 0x77, 0xe4, 0x2f, 0xee, 0x7f, 0x10} }, + { (uint64_t)6000ull, {0x78, 0x79, 0x4, 0x65, 0xf6, 0x60, 0x5b, 0x5a, 0x84, 0x77, 0x36, 0x5a, 0xa6, 0xc2, 0xa4, 0xa5, 0x84, 0x91, 0xc, 0x23, 0x95, 0x2, 0x92, 0x97, 0x52, 0x49, 0xa1, 0xad, 0x7d, 0xf0, 0xf7, 0xe8} }, + { (uint64_t)7000ull, {0x20, 0xa5, 0x60, 0x6b, 0x60, 0x23, 0x95, 0xd6, 0x8e, 0x2f, 0xad, 0x8e, 0xc6, 0x7f, 0x92, 0xde, 0x89, 0xc6, 0x3e, 0x1e, 0x7f, 0xc1, 0xdd, 0x7f, 0x92, 0xff, 0xed, 0xb8, 0xf6, 0x55, 0xfb, 0xd} }, + { (uint64_t)8000ull, {0x9a, 0x78, 0x97, 0x43, 0x98, 0x65, 0x17, 0xd9, 0x5f, 0x4e, 0x80, 0x8b, 0xeb, 0xe6, 0x52, 0xd, 0xe6, 0xcf, 0x8c, 0x51, 0x35, 0xab, 0x36, 0x8, 0x7e, 0x87, 0xe2, 0x76, 0xac, 0x6a, 0x34, 0x1} }, + { (uint64_t)9000ull, {0x5f, 0xc7, 0xaa, 0x48, 0xbb, 0x19, 0x13, 0x58, 0xc7, 0xe3, 0x4d, 0x24, 0xcf, 0x9c, 0x31, 0x16, 0x74, 0x12, 0x7a, 0xb2, 0x45, 0xd0, 0x8f, 0x4e, 0x2c, 0xfd, 0xbf, 0x8f, 0x5, 0xc9, 0x5b, 0xf5} }, + { (uint64_t)10000ull, {0x61, 0x20, 0xe7, 0x76, 0xe9, 0x12, 0xab, 0x10, 0x5a, 0x49, 0xf9, 0xda, 0x2, 0xa6, 0x75, 0x17, 0xc0, 0xa9, 0xb, 0x2b, 0x3e, 0x2d, 0xa3, 0xd, 0xff, 0x34, 0x39, 0x93, 0xdb, 0xec, 0x95, 0x97} }, + { (uint64_t)20000ull, {0x77, 0xbf, 0xb5, 0x37, 0xac, 0xa, 0xbc, 0x41, 0xaa, 0x21, 0xd0, 0xec, 0xd9, 0x18, 0x13, 0x34, 0xd8, 0x6b, 0xa7, 0x86, 0x5a, 0x94, 0x47, 0xf5, 0xc1, 0x58, 0x9a, 0x81, 0xd7, 0xef, 0xb3, 0xbb} }, + { (uint64_t)30000ull, {0x35, 0xf4, 0x5, 0xa9, 0x5f, 0x75, 0x19, 0x2a, 0xe9, 0xc0, 0xd4, 0xf5, 0x88, 0x84, 0x47, 0x14, 0xf6, 0x85, 0x1b, 0x97, 0xce, 0xbd, 0x9f, 0x7c, 0x2, 0xc5, 0xdd, 0xd7, 0xbf, 0x58, 0xff, 0x31} }, + { (uint64_t)40000ull, {0x77, 0x55, 0xbb, 0x3f, 0x38, 0x7c, 0x21, 0xb8, 0xa0, 0xf4, 0x48, 0x1f, 0xbf, 0xa8, 0x8a, 0xbe, 0xee, 0xce, 0xc7, 0x56, 0x53, 0xfc, 0xa1, 0x89, 0x58, 0x39, 0xc1, 0xba, 0x6, 0x47, 0x9f, 0x96} }, + { (uint64_t)50000ull, {0x8b, 0x7e, 0x84, 0xa3, 0x37, 0xb7, 0xb9, 0xcd, 0x5d, 0xb3, 0x63, 0x33, 0x8, 0xad, 0x51, 0x86, 0xa3, 0x59, 0xd, 0xff, 0xb8, 0x23, 0x1e, 0x2f, 0x31, 0xfd, 0x20, 0x42, 0x54, 0x9f, 0xfb, 0xe2} }, + { (uint64_t)60000ull, {0xef, 0xfd, 0xa6, 0x25, 0x15, 0xea, 0xb1, 0xbc, 0x1e, 0xbd, 0x74, 0x92, 0x94, 0x9b, 0x1, 0x22, 0xc3, 0x9f, 0x71, 0xa, 0x65, 0x16, 0xec, 0x66, 0x8c, 0x37, 0x61, 0xe6, 0xcc, 0x36, 0x1f, 0x25} }, + { (uint64_t)70000ull, {0x16, 0xba, 0x89, 00, 0xf3, 0x6f, 0xf, 0x6c, 0x46, 0x1c, 0xb, 0xe7, 0x64, 0xae, 0xee, 0x48, 0x86, 0x6, 0xb0, 0x53, 0xed, 0xdc, 0x10, 0xb5, 0x9a, 0x3e, 0xde, 0xcd, 0x23, 0xd4, 0x4f, 0xc0} }, + { (uint64_t)80000ull, {0x4d, 0xd4, 0x70, 0x3b, 0x7b, 0x7f, 0xcf, 0xe7, 0x2a, 0x2e, 0x4f, 0x31, 0xa4, 0x34, 0x17, 0xf9, 0xc0, 0xda, 0x64, 0x2f, 0xd0, 0xa9, 0x29, 0xb8, 0xf5, 0xed, 0xd8, 0x3, 0x7f, 0x93, 0xc5, 0xb3} }, + { (uint64_t)90000ull, {0x8e, 0xfc, 0x3, 0x20, 0x40, 0xbd, 0x90, 0x41, 0xda, 0x3d, 0xb0, 0x9b, 0xa1, 0x3d, 0xa2, 0xa5, 0xd1, 0xb8, 0x12, 0x3, 0xa, 0x5a, 0x36, 0x7c, 0x58, 0x94, 0xbd, 0x54, 0x11, 0x9, 0xe7, 0x30} }, + { (uint64_t)100000ull, {0xbd, 0x2e, 0xb1, 0x97, 0x83, 0x57, 0x1c, 0xf2, 0x22, 0x2c, 0x81, 0xb, 0x69, 0xf, 0xc7, 0x66, 0x64, 0x57, 0xae, 0x20, 0x92, 0x5b, 0x90, 0x5, 0xce, 0xe6, 0x1d, 0xf2, 0x66, 0x6f, 0xdc, 0xb7} }, + { (uint64_t)200000ull, {0x83, 0xd4, 0xcd, 0xdd, 0xc1, 0x44, 0x87, 0x32, 0xf2, 0x97, 0x7c, 0x41, 0xaa, 0xa7, 0x1f, 0xe6, 0xde, 0x9c, 0x17, 0x6d, 0xa8, 0x99, 0xee, 0xbf, 0xfc, 0x1b, 0xb, 0xa9, 0xea, 0x92, 0x97, 0x90} }, + { (uint64_t)300000ull, {0xcc, 0xc0, 0x6b, 0x44, 0xc3, 0x1, 0x38, 0x6, 0x30, 0x45, 0xed, 0x1, 0xd2, 0x45, 0xd8, 0x14, 0x3, 0xb6, 0x36, 0x52, 0xeb, 0xc4, 0xf9, 0x96, 0x7f, 0xd, 0x7f, 0x38, 0x69, 0x7f, 0x46, 0x16} }, + { (uint64_t)400000ull, {0x1b, 0xbf, 0xe7, 0xe, 0xca, 0xf1, 0xdd, 0xd7, 0xf1, 0x2, 0x36, 0xf6, 0x8a, 0x41, 00, 0xb, 0x5d, 0xab, 0x2d, 0x47, 0x5c, 0xb9, 0x2f, 0x62, 0xc2, 0xd6, 0x84, 0xcf, 0x57, 0x69, 0xfb, 0x84} }, + { (uint64_t)500000ull, {0xf1, 0xb1, 0xcd, 0xaa, 0x78, 0x14, 0x95, 0x36, 0xf, 0x53, 0x31, 0x81, 0xaa, 0x58, 0xc8, 0xbd, 0xae, 0x6a, 0x77, 0x98, 0xd0, 0x2d, 0xab, 0x6d, 0x56, 0x26, 0x81, 0x27, 0x67, 0x9, 0xe7, 0x1} }, + { (uint64_t)600000ull, {0xd5, 0x26, 0x7d, 0x60, 0xd4, 0xfe, 0x9b, 0xc5, 0xfe, 0xfa, 0x7d, 0x3f, 0xe0, 0x7c, 0xd1, 0xfa, 0xd4, 0x55, 0x73, 0xd5, 0xae, 0x19, 0x10, 0xda, 0x7, 0x3e, 0x6d, 0x2d, 0xf9, 0xe2, 0x4, 0x39} }, + { (uint64_t)700000ull, {0xb, 0x58, 0x11, 0x25, 0xc2, 0xc4, 0x83, 0xc9, 0xa3, 0xd8, 0xbc, 0x8, 0x32, 0x2f, 0x26, 0xaa, 0x1f, 0xc5, 0xe, 0x41, 0x53, 0x2c, 0x1b, 0x9d, 0xf6, 0x26, 0xb0, 0x9, 0xd7, 0x88, 0x67, 0xcf} }, + { (uint64_t)800000ull, {0xf5, 0xb3, 0xd1, 0x8f, 0x66, 0xd0, 0xf9, 0x17, 0x5c, 0x30, 0x83, 0xb5, 0xf8, 0x7, 0x8e, 0xaf, 0xa8, 0x9e, 0xf8, 0x1d, 0xe7, 0x15, 0x8, 0xbc, 0x25, 0x1f, 0x5c, 0x5f, 0xe7, 0x25, 0x2e, 0x6} }, + { (uint64_t)900000ull, {0x1, 0xde, 0x40, 0x2c, 0x4b, 00, 0x43, 0x4, 0x2e, 0xae, 0x9e, 0xde, 0xa1, 0x49, 0x2b, 0x9d, 0x82, 0xb7, 0xbc, 0x36, 0x68, 0xe9, 0xb5, 0x84, 0xb0, 0x31, 0x3d, 0x44, 0x50, 0x53, 0x40, 0x74} }, + { (uint64_t)1000000ull, {0x53, 0x4b, 0x85, 0xc7, 0x89, 0x3f, 0x66, 0xf0, 0x26, 0xb6, 0x5e, 0xd7, 0xe7, 0xa4, 0xb8, 0xc9, 0xf4, 0xb, 0xe3, 0x1b, 0xcd, 0xa, 0x3d, 0xcd, 0x27, 0xc4, 0x71, 0x2, 0x56, 0x51, 0x65, 0x3} }, + { (uint64_t)2000000ull, {0xcd, 0xb5, 0xda, 0xfa, 0x53, 0x10, 0xf5, 0x26, 0x2f, 0xfc, 0x9, 0x26, 0xd0, 0xdf, 0x6e, 0xeb, 0xee, 0x2d, 0x52, 0xa9, 0x8d, 0xc6, 0x9f, 0xd, 0xc5, 0xe4, 0xeb, 0xf0, 0xc1, 0xa8, 0x77, 0x2e} }, + { (uint64_t)3000000ull, {0x9e, 0x75, 0x63, 0xf0, 0x33, 0x59, 0xea, 0x31, 0xe2, 0x91, 0xe7, 0xf0, 0xb8, 0x74, 0x17, 0xbc, 0xf5, 0xb2, 0x34, 0xee, 0x8b, 0x7e, 0x5b, 0x4, 0x41, 0x73, 0xbf, 00, 0x46, 0x86, 0x7c, 0x57} }, + { (uint64_t)4000000ull, {0xfd, 0x2a, 0xeb, 0xd, 0x5e, 0xe5, 0x3b, 0x77, 0xf2, 0xb1, 0xe3, 0xac, 0x75, 0x2d, 0x19, 0x38, 0x9f, 0xc5, 0xba, 0xa0, 0xf8, 0xd7, 0x64, 0x48, 0xa5, 0x9f, 0x99, 0x85, 0xa4, 0x8d, 0xa, 0x25} }, + { (uint64_t)5000000ull, {0xc0, 0xbe, 0x4f, 0xb8, 0x77, 0xb9, 0xce, 0x50, 0x87, 0x71, 0x32, 0x3b, 0xcf, 0x1f, 0xb9, 0x48, 0x47, 0x10, 0xee, 0x23, 0x2, 00, 0x6, 0xc3, 0xe8, 0xca, 0xac, 0x6e, 0x4f, 0x2, 0xfa, 0xbf} }, + { (uint64_t)6000000ull, {0xfc, 0x44, 0x5c, 0xa3, 0x84, 0xf3, 0x3e, 0x55, 0x8d, 0xc1, 0x56, 0x44, 0x9d, 0x3f, 0xba, 0x6a, 0xfd, 0x54, 0xc3, 0x42, 0xe6, 0x35, 0x11, 0xf, 0xe7, 0x9c, 0x16, 0xc7, 0x17, 0xf7, 0xd4, 0xf7} }, + { (uint64_t)7000000ull, {0xd8, 0x9, 0x2b, 0x8d, 0x45, 0xdb, 0x54, 0xa5, 0x6d, 0x64, 0xe8, 0x9, 0x4a, 0x6, 0x22, 0xe2, 0x6e, 0x8a, 0x2e, 0xec, 0xb9, 0x3, 0xb2, 0xe1, 0xf7, 0x5a, 0x83, 0x7b, 0x3a, 0xd8, 0x55, 0x4a} }, + { (uint64_t)8000000ull, {0x10, 0x4, 0x5c, 0x91, 0xdb, 0xad, 0x8a, 0x6a, 0x81, 0x62, 0x4a, 0xe0, 0xcf, 0x20, 0x5d, 0xb9, 0x97, 0x3e, 0xe8, 0x42, 0x3e, 0x97, 0xaf, 0x58, 0xa6, 0x1c, 0xfa, 0x7a, 0x78, 0x66, 0xf4, 0x1} }, + { (uint64_t)9000000ull, {0x11, 0x5c, 0x20, 0x9e, 0xe1, 0xde, 0xf3, 0x10, 0xce, 0xc9, 0xa6, 0xd1, 0x6c, 0xe6, 0x27, 0xec, 0xbd, 0xb9, 0xff, 0x2c, 0x23, 0x9, 0x3c, 0x24, 0xc8, 0x6c, 0x1b, 0xf2, 0x50, 0xd4, 0xb5, 0x85} }, + { (uint64_t)10000000ull, {0x2f, 0x99, 0xd9, 0x74, 0x44, 0x18, 0x66, 0x9, 0x49, 0xba, 0x43, 0x35, 0x61, 0xc6, 0x5, 0xb6, 0xf7, 0xbe, 0x8f, 0x82, 0xa, 0x93, 0xcb, 0x2a, 0xed, 0xa9, 0x7c, 0x87, 0x32, 0x92, 0x56, 0x49} }, + { (uint64_t)20000000ull, {0xc6, 0x77, 0x1f, 0xab, 0x14, 0xb, 0x75, 0xf4, 0xef, 0xd0, 0x97, 0xfc, 0xe1, 0x82, 0x6b, 0x80, 0xba, 0xe3, 0x16, 0xbc, 0xec, 0x28, 0x86, 0x9b, 0x3a, 0x1b, 0xf1, 0xbc, 0x6e, 0x4d, 0x20, 0x43} }, + { (uint64_t)30000000ull, {0xa1, 0xe9, 0xed, 0x3e, 0xf6, 0x5a, 0x9d, 0x52, 0x6c, 0xc2, 0x62, 0x5, 0x88, 0x12, 0x1, 0xd8, 0xa8, 0xf2, 0xc4, 0x40, 0x9f, 0xa3, 0x64, 0x10, 0x72, 0x96, 0xb9, 0xf9, 0x6a, 0x61, 0xb3, 0x58} }, + { (uint64_t)40000000ull, {0xb5, 0x31, 0x2d, 0xc7, 0x72, 0x94, 0xab, 0x9b, 0xc8, 0xbf, 0xd1, 0x39, 0x1e, 0x9a, 0xca, 0x92, 0x45, 0xe2, 0x28, 0xf7, 0x4b, 0x49, 0x74, 0xfc, 0x29, 0xad, 0x1c, 0x31, 0xcb, 0xe3, 0xe6, 0xa3} }, + { (uint64_t)50000000ull, {0xb8, 0xab, 0xc9, 0xff, 0xf6, 0x84, 0x1d, 0x2e, 0xa0, 0x13, 0x5a, 0x21, 0x72, 0xd3, 0xa7, 0xb, 0xfc, 0x2b, 0x70, 0x22, 0x8, 0xcd, 0x4a, 0x43, 0xc6, 0x30, 0xbe, 0xb1, 0xb8, 0xa0, 0x32, 0x8b} }, + { (uint64_t)60000000ull, {0x63, 0x90, 0xe1, 0xdb, 0x81, 0xb0, 0xea, 0x5c, 0xe2, 0x73, 0x94, 0x14, 0xe5, 0x2b, 0x7, 0x98, 0xd8, 0x2e, 0xb8, 0xe9, 0xae, 0xc5, 0x6d, 0xfe, 0x7e, 0x2c, 0x64, 0x11, 0xab, 0x79, 0x41, 0x87} }, + { (uint64_t)70000000ull, {0x7e, 0x51, 0xaf, 0xee, 0x5b, 0xc9, 0x71, 0x52, 0x9d, 0x64, 0x4d, 0xcd, 0x7f, 0x2a, 0x2a, 0xb0, 0x26, 0x69, 0xce, 0x2c, 0xb5, 0x7, 0xa6, 0x2d, 0xfc, 0x93, 0x17, 0x6c, 0xb6, 0xdf, 0x41, 0x38} }, + { (uint64_t)80000000ull, {0xf3, 0x7b, 0x94, 0x6b, 0x8b, 0x24, 0x88, 0xeb, 0xee, 0x1c, 0x6, 0xc1, 0x27, 0xfb, 0xe5, 0xfa, 0x5e, 0xfd, 0x62, 0x36, 0x9d, 0xd5, 0xaa, 0xda, 0xed, 0xd8, 0x88, 0x50, 0x1d, 0x3b, 0x7e, 0x3b} }, + { (uint64_t)90000000ull, {0x46, 0xcb, 0x76, 0x57, 0xf6, 0x1c, 0x83, 0x7c, 0xec, 0x80, 0x74, 0xbb, 0xb0, 0xf5, 0x2e, 0x7f, 0xc5, 0x9a, 0xd, 0x94, 0xe0, 0x17, 00, 0x9a, 0xbe, 0x25, 0x65, 0x2e, 0x4a, 0xd2, 0xe5, 0x3d} }, + { (uint64_t)100000000ull, {0x66, 0x7b, 0x8e, 0x6f, 0x6a, 0x4b, 0x91, 0x89, 0x76, 0xd9, 0x73, 0x5a, 0x43, 0x36, 0x7d, 0xc7, 0x59, 0x2c, 0x87, 0xd0, 0xa1, 0xf8, 0x15, 0xc6, 0xe8, 0x7d, 0xf1, 0x1a, 0x13, 0x50, 0x9f, 0xb2} }, + { (uint64_t)200000000ull, {0x3b, 0xcb, 0x51, 0x48, 0x1, 0x64, 0x1b, 0x62, 0x55, 0x93, 0x8c, 0xc5, 0x3, 0x76, 0x2d, 0x35, 0xce, 0x6, 0xd7, 0x5f, 0xe9, 0x50, 0x95, 0x9a, 0x1a, 0xab, 0x21, 0x4b, 0x50, 0x9b, 0x10, 0xb} }, + { (uint64_t)300000000ull, {0xa5, 0x92, 0x6f, 0x3, 0x1e, 0x6b, 0x15, 0xeb, 0x86, 0x23, 0x51, 0x8, 0xab, 0xb1, 0xaf, 0x90, 0xc5, 0xb1, 0x62, 0xc3, 0x99, 0x8c, 0x8b, 0xbb, 0x3f, 0xfb, 0xb0, 0x72, 0x9d, 0xa9, 0x45, 0x7b} }, + { (uint64_t)400000000ull, {0xfe, 0x35, 0xb6, 0x99, 0x44, 0x41, 0xe, 0xaf, 0x81, 0x5b, 0xdc, 0xd0, 0xa4, 0xd7, 0x1e, 0xf9, 0xfc, 0x66, 0x86, 0x48, 0xad, 0x43, 0x74, 0x3b, 0x3, 0x5a, 0xed, 0x2c, 0x17, 0xc1, 0x38, 0x7a} }, + { (uint64_t)500000000ull, {0x22, 0x22, 0xd6, 0x70, 0xb8, 0x7d, 0x9b, 0x47, 0xb8, 0xb9, 0x5c, 0x8c, 0x39, 0x7b, 0xc5, 0x2e, 0x2b, 0x46, 0xa6, 0x48, 0xb0, 0x2, 0xa0, 0x48, 0x5a, 0x37, 0x5c, 0xd8, 0x1f, 0x4a, 0x54, 0x5f} }, + { (uint64_t)600000000ull, {0xd3, 0x23, 0x8a, 0x4a, 0x8b, 0x71, 0xab, 0x46, 0xd1, 0x53, 0x4, 0xac, 0xfa, 0x2f, 0x40, 0xbf, 0x5e, 0xa6, 0x3b, 0x3d, 0x86, 0x4a, 0x79, 0xfa, 0x84, 0x25, 0xd2, 0x65, 0x5a, 0xe7, 0x7, 0x6f} }, + { (uint64_t)700000000ull, {0xa8, 0xff, 0x28, 0x3f, 0xcf, 0xf0, 0x53, 0xd3, 0x44, 0xc8, 0xf7, 0x56, 0x4f, 0x40, 0x24, 0xb6, 0x6b, 0xfa, 0x45, 0x9f, 0x47, 0x6f, 0xd, 0x73, 0xc, 0x91, 0x39, 0x90, 0x8b, 0x2d, 0x64, 0x7e} }, + { (uint64_t)800000000ull, {0xf2, 0xda, 0xf8, 0x88, 0xc4, 0x46, 0x57, 0x1, 0xc0, 0xe6, 0x1e, 0x12, 0xc3, 0xfb, 0xd4, 0xea, 0x79, 0xc7, 0xec, 0xb4, 0xf0, 0xc4, 0xb1, 0x54, 0xc5, 0x1a, 0x24, 0xd1, 0xe9, 0x21, 0x28, 0xba} }, + { (uint64_t)900000000ull, {0x11, 0x6a, 0xe5, 0xd2, 0x9c, 0xec, 0x72, 0xaa, 0xc5, 0x57, 0xcb, 0x14, 0xe2, 0xcd, 0xd5, 0x53, 0xe5, 0x88, 0xff, 0x8b, 0x81, 0x78, 0x26, 0x1, 0x99, 0xc4, 0xc, 0xae, 0xa2, 0x12, 0xcb, 0x63} }, + { (uint64_t)1000000000ull, {0x8c, 0xe6, 0x48, 0x33, 0xce, 0xc9, 00, 0xcb, 0x6d, 0x5a, 0xc4, 0x6f, 0xc0, 0x23, 0x7d, 0x8f, 0x24, 0x39, 0xc3, 0xdf, 0xa2, 0x38, 0xba, 0xf9, 0xcc, 0x94, 0x16, 0x6a, 0xd2, 0xe8, 0x98, 0x87} }, + { (uint64_t)2000000000ull, {0x37, 0x8d, 0x3c, 0x5d, 0xbb, 0xa4, 0x82, 0x3d, 0x33, 0x12, 0xbb, 0x61, 0xfc, 0x6, 0x75, 0xa1, 0xbb, 0x39, 0x89, 0xf3, 0x97, 0x1, 0xeb, 0xd, 0x5c, 0xe4, 0xde, 0x5b, 0xd, 0x90, 0x74, 0x72} }, + { (uint64_t)3000000000ull, {0x7f, 0xa2, 0xd0, 0xa5, 0x99, 0xe7, 0x97, 0x2e, 0x74, 0xcb, 0x75, 0xf9, 0x8a, 0xf4, 0x84, 0xfc, 0x85, 0x19, 0xcb, 0x7e, 0x25, 0xb9, 0x84, 0xa7, 0x6d, 0x8b, 0xc2, 0xba, 0x8d, 0xaf, 0xde, 0xd8} }, + { (uint64_t)4000000000ull, {0xda, 0x3a, 0xcb, 00, 0xab, 0x2d, 0x8d, 0xcc, 0xac, 0xec, 0x8f, 0x77, 0x59, 0x21, 0xc4, 0xe, 0x26, 0xb1, 0xff, 0xbe, 0xca, 0x9e, 0xb7, 0xe6, 0x57, 0x25, 0x6f, 0x59, 0x68, 0xf2, 0x34, 0x1c} }, + { (uint64_t)5000000000ull, {0x34, 0x6, 0xd7, 0x9a, 0x50, 0xd8, 0x14, 0xa9, 0xcc, 0xed, 0x3b, 0x24, 0x4, 0xed, 0x3e, 0x1b, 0x8d, 0xa6, 0x21, 0x98, 0x8c, 0x43, 0xb1, 0x93, 0x69, 0x42, 0xf4, 0x94, 0xa, 0xc5, 0xbf, 0x6a} }, + { (uint64_t)6000000000ull, {0xf8, 0x3e, 0xe8, 0xc1, 0x62, 0xfc, 0x52, 0xa0, 0x8, 0x9f, 0x46, 0xe8, 0x29, 0xc2, 0xea, 0xf6, 0xa1, 0x9f, 0xd5, 0x96, 0xcd, 0x12, 0xb3, 0xe8, 0x19, 0xd5, 0x67, 0x69, 0x44, 0xf, 0x7b, 0x4e} }, + { (uint64_t)7000000000ull, {0x8c, 0x72, 0x7d, 0x24, 0x57, 0xf3, 0x4b, 0x2f, 0xdb, 0x6a, 0xdf, 0x69, 0x1a, 0xb3, 0x5f, 0xaa, 0xe4, 0xff, 0x23, 0x4c, 0x28, 0xb4, 0x4e, 0x9f, 0xd3, 0x71, 0x8e, 0xef, 0xec, 0x41, 0x75, 0x80} }, + { (uint64_t)8000000000ull, {0x4a, 0x2e, 0x2f, 0x76, 0xe3, 0x5d, 0xcb, 0xa8, 0x97, 0xa3, 0xae, 0x72, 0xc4, 0x27, 0xd, 0x9c, 0x13, 0x17, 0x14, 0xed, 0x19, 0x1b, 0x55, 0x5c, 0x5e, 0x1, 0xe4, 0x75, 0x7c, 0xba, 0xe7, 0x2c} }, + { (uint64_t)9000000000ull, {0x3f, 0x9f, 0xc, 0x4, 0xc0, 0xb9, 0xec, 0x9b, 0x4d, 0x11, 0x7c, 0x5f, 0xc9, 0xf1, 0x8a, 0x20, 0xf2, 0xb3, 0xfa, 0xcc, 0xa4, 0xc8, 0xae, 0x41, 0xaf, 0x7c, 0x8, 0xe9, 0xe0, 0xef, 0xb9, 0x81} }, + { (uint64_t)10000000000ull, {0x97, 0xc9, 0x2a, 0x29, 0x1, 0x5e, 0xcb, 0x49, 0xf8, 0x9, 0x5, 0x45, 0xe0, 0x1f, 0xf9, 0x78, 0x6c, 0xae, 0x40, 0x57, 0x73, 0x47, 0x61, 0x18, 0x24, 0xf4, 0xb6, 0x59, 0x9f, 0xf5, 0xd3, 0x64} }, + { (uint64_t)20000000000ull, {0x9, 0xba, 0xed, 0x9a, 0x3c, 0x44, 0xb2, 0x22, 0x85, 0xa0, 0xae, 0xa4, 0x14, 0x8c, 0xa7, 0xde, 0x9b, 0xea, 0x96, 0x3c, 0xf6, 0x96, 0x23, 0xb6, 0x83, 0x44, 0x5c, 0xa, 0x10, 0xa5, 0x86, 0x77} }, + { (uint64_t)30000000000ull, {0x45, 0xac, 0xaf, 0x1d, 0xe2, 0x89, 0x6d, 0xe8, 0x72, 0x84, 0xff, 0xed, 0x57, 0x8b, 0x77, 0x14, 0xf5, 0x18, 0xa6, 0x18, 0xe2, 0xae, 0x6f, 0x90, 0xae, 0x4f, 0x70, 0x13, 0xa2, 0x8e, 0x99, 0xe0} }, + { (uint64_t)40000000000ull, {0x8, 0xb8, 0x47, 0x36, 0x42, 0x24, 0xe2, 0x9c, 0xe3, 0x36, 0x63, 0x93, 0xc2, 0xe1, 0x1e, 0xfc, 0x75, 0x55, 0xde, 0xe1, 0xa0, 0x5f, 0x91, 0xa7, 0x2e, 0x61, 0x11, 0x76, 0x84, 0xdd, 0xbe, 0x29} }, + { (uint64_t)50000000000ull, {0x6c, 0x8e, 0xe, 0x4a, 0x63, 0x4f, 0x85, 0x9a, 0x31, 0xab, 0x2f, 0x7a, 0x78, 0xc0, 0xc4, 0xa5, 0x93, 0x8c, 0xb7, 0x7f, 0x3, 0x35, 0x50, 0xa4, 0x7d, 0x7e, 0x31, 0x81, 0xb6, 0xb2, 0x6e, 0xc0} }, + { (uint64_t)60000000000ull, {0x66, 0xc2, 0xa0, 0x9, 0x65, 0xf9, 0xbf, 0xcb, 0xb1, 0x1e, 0xa0, 0x3c, 0xf1, 0xd6, 0x31, 0xb0, 0xe, 0x8a, 0x1e, 0xf7, 0xa6, 0xb, 0x1b, 0xe4, 0xa5, 0xac, 0x9, 0x23, 0xb, 0xf8, 0x17, 0x3f} }, + { (uint64_t)70000000000ull, {0x63, 0x51, 0xd7, 0x74, 0xc0, 0x2c, 0x5a, 0x9d, 0xee, 0xcf, 0xdb, 0xab, 0x70, 0x96, 0x68, 0x59, 0x8c, 0x47, 0xe4, 0xb1, 0x78, 0x2c, 0xe5, 0xae, 0x31, 0x6a, 0xf7, 0x40, 0xa6, 0x6f, 0x7e, 0x30} }, + { (uint64_t)80000000000ull, {0x5a, 0xcc, 0xfd, 0x16, 0x22, 0x79, 0xa5, 0x1c, 0x8b, 0x3b, 0xd5, 0xd3, 0x67, 0x9e, 0x91, 0x89, 0x67, 0xa2, 0x64, 0xea, 0x6, 0x3d, 0x37, 0xdf, 0xf5, 0xe3, 0x45, 0x7e, 0xc3, 0x7, 0xd4, 0x57} }, + { (uint64_t)90000000000ull, {0xb7, 0x47, 0xfc, 0x1, 0xc6, 0xf0, 0xc7, 0x49, 0x67, 0x3a, 0x29, 0x10, 0x25, 0xc, 0x2e, 0x23, 0xcb, 0x38, 0x27, 0x4d, 0x63, 0xb4, 0x2f, 0x52, 0x1b, 0x84, 0x63, 0x56, 0xe4, 0x13, 0x61, 0x8f} }, + { (uint64_t)100000000000ull, {0x9, 0x42, 0x84, 0x3d, 0x6f, 0x69, 0xe1, 0xcf, 0x3d, 0x99, 0xc9, 0x9f, 0xc, 0x97, 0xc0, 0xe6, 0xe5, 0x78, 0x93, 0x5a, 0xf6, 0xa8, 0xbd, 0xb8, 0xf8, 0x1d, 0x5b, 0x90, 0xbd, 0xe7, 0xcc, 0x10} }, + { (uint64_t)200000000000ull, {0x56, 0x4c, 0x64, 0xea, 0x50, 0xe4, 0xbd, 0x20, 0xdb, 0x58, 0x5d, 0xb5, 0x87, 0xb1, 0xf7, 0x64, 0xa2, 0x62, 0xd8, 0x46, 0xa6, 0xb0, 0xa2, 0x4b, 0x43, 0x27, 0x60, 0xd2, 0xf9, 0xde, 0x66, 0x5b} }, + { (uint64_t)300000000000ull, {0xac, 0x65, 0x83, 0x41, 0x5b, 0xd6, 0x4c, 0x3, 0x35, 0x97, 0xf9, 0x28, 0xa4, 0xb5, 0xd4, 0xf4, 0x78, 0x9e, 0xa8, 0xb2, 0x87, 0x82, 0x73, 0x89, 0xa8, 0x1e, 0xb6, 0x62, 0x9e, 0xc5, 0xb8, 0x50} }, + { (uint64_t)400000000000ull, {0x52, 0xf4, 0x9d, 0x89, 0xcf, 0x74, 0x13, 0x2f, 0xc7, 0x43, 0x2e, 0x6a, 0x6b, 0xef, 0xcf, 0xf3, 0xfd, 0x13, 0xd6, 0x3b, 0x51, 0x60, 0xab, 0x1c, 0xe6, 0x4a, 0xb0, 0xd1, 0x21, 0xcd, 0xa9, 0x9a} }, + { (uint64_t)500000000000ull, {0xe9, 0xaa, 0x7c, 0x81, 0xcd, 0xb5, 0xb3, 0x14, 0x8f, 0xb7, 0x62, 0x80, 0x63, 0xcd, 0x7a, 0x7, 0xd1, 0xad, 0xd1, 0x64, 0x3c, 0xed, 0xd3, 0xfa, 0x34, 0x47, 0x9d, 0x85, 0x9c, 0xc5, 0x62, 0x65} }, + { (uint64_t)600000000000ull, {0x98, 0x27, 0xae, 0x31, 0xe5, 0xc2, 0xa7, 0x78, 0x39, 0xf6, 0xb, 0x83, 0xab, 0x45, 0x78, 0xe2, 0xa0, 0x1e, 0xfa, 0x4b, 0x3b, 0x14, 0xcc, 0x72, 0x73, 0x14, 0xff, 0xd7, 0x15, 0x53, 0x63, 0xbf} }, + { (uint64_t)700000000000ull, {0x72, 0x91, 0x6a, 0x79, 0x27, 0xff, 0x13, 0x24, 0xd4, 0x98, 0x40, 0xec, 0xc0, 0x98, 0x68, 0xb8, 0xf3, 0x15, 0xe4, 0xf1, 0xf6, 0xd4, 0x45, 0x8d, 0x37, 0x5e, 0xc7, 0x45, 0xfc, 0x2e, 0x63, 0x53} }, + { (uint64_t)800000000000ull, {0x66, 0x76, 0xe0, 0x4, 0xf, 0xa4, 0xb8, 0x22, 0x9c, 0x61, 0x69, 0xc, 0x71, 0x32, 0x22, 0xcf, 0x3d, 0x37, 0xb9, 0x49, 0x3b, 0x49, 0x6, 0x80, 0xbb, 0x48, 0xd8, 0xd5, 0x1a, 0xde, 0x95, 0xf2} }, + { (uint64_t)900000000000ull, {0x41, 0x54, 0xb3, 0x46, 0x5a, 0x43, 0x72, 0x67, 0x1e, 0xa9, 0xe0, 0x64, 0xa7, 0xca, 0xa6, 0x6e, 0x14, 0xb4, 0x98, 0x6a, 0x46, 0x68, 0x91, 0x8a, 0xfa, 0x57, 0x9b, 0xf1, 0xed, 0x25, 0x6, 0xdd} }, + { (uint64_t)1000000000000ull, {0xbb, 0x6f, 0x70, 0x62, 0xca, 0x30, 0x6d, 0x67, 0x2a, 0x73, 0xe, 0x2a, 0x2f, 0x21, 0x9b, 0xdb, 0xe4, 0xc, 0x9f, 0xb3, 0xfe, 0x4d, 0x60, 0x13, 0x69, 0x2a, 0xf9, 0x3c, 0xdb, 0x2e, 0xc, 0xd1} }, + { (uint64_t)2000000000000ull, {0xbc, 0xe, 0xae, 0x5b, 0x9c, 0x6a, 0xd6, 0x38, 0x7a, 0x41, 0x19, 0x3c, 0x46, 0xf3, 0xc1, 0xd0, 0x71, 0x6d, 0x77, 0xd6, 0x4e, 0x22, 0xb2, 0xe0, 0x7b, 0x4b, 0xce, 0x75, 0x67, 0x65, 0xa2, 0xb} }, + { (uint64_t)3000000000000ull, {0x10, 0xc, 0x6f, 0x13, 0x42, 0xb7, 0x1b, 0x73, 0xed, 0xdd, 0xc5, 0x49, 0x2b, 0xe9, 0x23, 0x18, 0x2f, 00, 0xa6, 0x83, 0x48, 0x8e, 0xc3, 0xa2, 0xa1, 0xc7, 0xa9, 0x49, 0xcb, 0xe5, 0x77, 0x68} }, + { (uint64_t)4000000000000ull, {0x41, 0x9f, 0x7c, 0x94, 0x91, 0x4, 0x34, 0xf, 0xd3, 0xce, 0x85, 0x94, 0x8d, 0x2e, 0xf9, 0xf0, 0xdd, 0x4b, 0xb3, 0xd9, 0x2f, 0x5a, 0x78, 0x2c, 0x5f, 0x78, 0x4, 0xb7, 0x52, 0x9a, 0x13, 0xc6} }, + { (uint64_t)5000000000000ull, {0x40, 0x65, 0x34, 0x98, 0xbe, 0xa0, 0x22, 0xe3, 0x36, 0x5a, 0x3, 0xe5, 0x75, 0x25, 0xba, 0x65, 0x96, 0x53, 0x76, 0x24, 0x4f, 0xff, 0x10, 0x73, 0xe, 0xd9, 0x7a, 0x73, 0xb7, 0x53, 0x1, 0x91} }, + { (uint64_t)6000000000000ull, {0xdb, 0x1c, 0x7c, 0xf6, 0x8, 0x91, 0xf9, 0x65, 0xeb, 0xa9, 0xc6, 0x2, 0x24, 00, 0x63, 0xe, 00, 0x47, 0x95, 0x34, 0xe6, 0xf5, 0xb5, 0x33, 0xdc, 0xfc, 0x83, 0x19, 0x38, 0x52, 0x2c, 0x78} }, + { (uint64_t)7000000000000ull, {0x59, 0xa0, 0x3a, 0x31, 0x53, 0xa9, 0x94, 0xd7, 0x23, 0x27, 0xe4, 0xd9, 0x24, 0x21, 0xd3, 0xe3, 0x29, 0x1b, 0x1f, 0xa1, 0xb2, 0x40, 0xde, 0x44, 0xb9, 0x2d, 0x7f, 0x62, 0xec, 0x1, 0x28, 0xf1} }, + { (uint64_t)8000000000000ull, {0xb2, 0x80, 0xb9, 0x3b, 0x1e, 0x43, 0x88, 00, 0x73, 0xea, 0x4a, 0xa0, 0xef, 0x11, 0x4, 0xf8, 0x24, 0xbd, 0x12, 0x7a, 0x4a, 0x3d, 0xa2, 0x13, 0x92, 0x65, 0xf, 0xe8, 0xc6, 0x55, 0xb6, 0xc5} }, + { (uint64_t)9000000000000ull, {0xda, 0xf0, 0xd3, 0xe9, 0x32, 0x17, 0xd8, 0xe9, 0x5a, 0xbf, 0xdd, 0xf1, 0x3b, 0x7f, 0xd4, 0x8e, 0x34, 0x47, 0xad, 0x9, 0x23, 0x26, 0xb8, 0x99, 0xed, 0x58, 0x1f, 0xd5, 0xf8, 0x6, 0xc5, 0x6} }, + { (uint64_t)10000000000000ull, {0x16, 0x3d, 0xd6, 0x82, 0xec, 0x97, 0x7c, 0xdd, 0xa5, 0x95, 0x31, 0xda, 0x3f, 0xfa, 0x72, 0x99, 0x8a, 0x6f, 0x88, 0x37, 0xab, 0xad, 0xc6, 0x36, 0xaa, 0xed, 0xc8, 0xbe, 0x19, 0xb2, 0xd7, 0xc7} }, + { (uint64_t)20000000000000ull, {0x2, 0xfa, 0x35, 0x3a, 0xa8, 0x4e, 0xa8, 0xc4, 0x4c, 0x80, 0x23, 0x6, 0x5d, 0x79, 0x41, 0x60, 0x6b, 0x1f, 0xa5, 0xc2, 0x64, 0xdc, 0xcf, 0x46, 0xdc, 0x64, 0x94, 0xeb, 0xe9, 0x60, 0x6f, 0x20} }, + { (uint64_t)30000000000000ull, {0x87, 0x5, 0xd, 0xab, 0xf5, 0xb2, 0x3e, 0x8b, 0x79, 0x81, 0x3f, 0x4e, 0xd7, 0x6a, 0xa4, 0xad, 0xd2, 0x25, 0xdd, 0x2a, 0x50, 0x89, 0xaf, 0x6, 0x7d, 0xa7, 0x7c, 0xcb, 0x6e, 0xc5, 0x59, 0x46} }, + { (uint64_t)40000000000000ull, {0xaa, 0xe6, 0xb2, 0xc8, 0xa2, 0x9e, 0x4d, 0xbc, 0x63, 0x76, 0xc1, 0x72, 0x5, 0xfb, 0x2, 0x85, 0xe7, 0xd7, 0xd3, 0x25, 0x32, 0x3c, 0xd5, 0x26, 0xf, 0x98, 0xad, 0xff, 0xf7, 0xd4, 0xd4, 0xfb} }, + { (uint64_t)50000000000000ull, {0x9d, 0x79, 0x28, 0x82, 0x12, 0xa1, 0xe2, 0x3c, 0x9, 0x9f, 0xb2, 0xd8, 0xf0, 0xd0, 0xdb, 0xd3, 0xc2, 0xec, 0xd7, 0x58, 0xb9, 0xe6, 0xb5, 0xb4, 0xf2, 0x90, 0x60, 0x7, 0x9f, 0x19, 0x66, 0x9f} }, + { (uint64_t)60000000000000ull, {0x18, 0x90, 0x10, 0x6f, 0x1b, 0x97, 0xbc, 0x2d, 0xa, 0xe3, 0x96, 0xe5, 0xe5, 0x5e, 0xbf, 0xcc, 0x8e, 0xf6, 0x91, 0x7f, 0xb1, 0x96, 0xcb, 0x2b, 0x1e, 0x80, 0x25, 0x5d, 0x54, 0xb6, 0x87, 0x10} }, + { (uint64_t)70000000000000ull, {0x57, 0xd3, 0x4e, 0xf7, 0x54, 0x3b, 0xe4, 0x7b, 0x7b, 0xf4, 0x97, 0xce, 0x4a, 0x17, 0x6e, 0x78, 0xc6, 0xd6, 0x5c, 0xd3, 0x27, 0xf6, 0x4b, 0xa7, 0x5c, 0x27, 0xd1, 0x57, 0xb3, 0x37, 0x12, 0x5d} }, + { (uint64_t)80000000000000ull, {0x5e, 0xcb, 0x10, 0x15, 0x4b, 0x96, 0xca, 0xb5, 0x5e, 0x9, 0x46, 0x83, 0xf8, 0xdb, 0xff, 0x7f, 0x56, 0x63, 0x5f, 0xa6, 0x64, 0x97, 0xee, 0x9e, 0x24, 0xe, 0x83, 0x63, 0x7c, 0x7c, 0x87, 0x72} }, + { (uint64_t)90000000000000ull, {0x42, 0x32, 0x69, 0x98, 0x51, 0x30, 0xf1, 0x66, 0x51, 0x6a, 0x5b, 0xa8, 0x61, 0x9, 0x6d, 0x72, 0xec, 0xcc, 0x67, 0xad, 0xab, 0xa4, 0x5e, 0xb3, 0x73, 0x9a, 0xe, 0xbc, 0x61, 0xa3, 0x20, 0xae} }, + { (uint64_t)100000000000000ull, {0xa8, 0xd1, 0x60, 0x95, 0x91, 0x49, 0x8f, 0xa7, 0xc2, 0x94, 0x27, 0xad, 0x89, 0x31, 0xaf, 0x36, 0xc5, 0x2d, 0xc9, 0x7b, 0x4a, 0x11, 0xe7, 0x47, 0xa9, 0x56, 0xc2, 0x8c, 0x42, 0x54, 0xcf, 0xd4} }, + { (uint64_t)200000000000000ull, {0x23, 0x14, 0x49, 00, 0xa8, 0x66, 0xe8, 0xc1, 0xbf, 0x40, 0x98, 0xda, 0xa9, 0x48, 0xb9, 0x86, 0xf3, 0x84, 0xe, 0x5a, 0x7d, 0x21, 0x5e, 0xf0, 0xd5, 0x64, 0xef, 0xd8, 0xbe, 0xc6, 0x83, 0x15} }, + { (uint64_t)300000000000000ull, {0x6a, 0x51, 0x47, 0x3c, 0x86, 0xed, 0xad, 0x53, 0x51, 0x4b, 0x3f, 0x95, 0x97, 0xed, 0x21, 0xae, 00, 0x81, 0x51, 0xa0, 0x9e, 0x43, 0xad, 0xdd, 0x45, 0xd1, 0x74, 0x63, 0xc5, 0x34, 0x3, 0x97} }, + { (uint64_t)400000000000000ull, {0x8, 0xbd, 0xd4, 0xc3, 0xe4, 0x53, 0x1b, 0x29, 0x7a, 0x70, 00, 0x1e, 0xb8, 0xa4, 0xf1, 0x98, 0xdc, 0x3b, 0xd4, 0xf1, 0xf5, 0x60, 0x9a, 0xda, 0x98, 0xf6, 0xd9, 0x5f, 0x9a, 0x1a, 0x30, 0x2e} }, + { (uint64_t)500000000000000ull, {0x97, 0x55, 0x70, 0xea, 0x12, 0xde, 0x5a, 0xf5, 0xc5, 0x36, 0xbd, 0xb6, 0x83, 0x54, 0xfb, 0xc8, 0x32, 0x21, 0x50, 0xfc, 0x56, 0x83, 0x7c, 0x4b, 0x78, 0xa9, 0x85, 0x76, 0x5d, 0x2a, 0x70, 0x99} }, + { (uint64_t)600000000000000ull, {0xa7, 0xa6, 0x39, 0x93, 0x41, 0xcb, 0x4d, 0x67, 0x76, 0xcd, 0x94, 0xd, 0x1d, 0x6a, 0xb0, 0xac, 0xa, 0xbf, 0x56, 0x93, 0x6a, 0x35, 0x31, 0xdf, 0xe9, 0x6c, 0x23, 0x69, 0x97, 0x8e, 0x49, 0xfa} }, + { (uint64_t)700000000000000ull, {0x55, 0x9, 0x3e, 0x5e, 0xeb, 0xca, 0x3, 0x88, 0x48, 0xdc, 0x99, 0x7e, 0x31, 0x95, 0xec, 0xc5, 0x8f, 0xb4, 0xa5, 0x71, 0xb9, 0x52, 0x56, 0xc0, 0xff, 0x49, 0xbe, 0xd0, 0xf1, 0x65, 0x22, 0xbd} }, + { (uint64_t)800000000000000ull, {0xbb, 0xc6, 0x18, 0x2, 0x24, 0xaf, 0xd3, 0x38, 0xa6, 0xf4, 0xa0, 0x6b, 0x11, 0x98, 0x40, 0x68, 0xeb, 0x36, 0x35, 0xe7, 0xe5, 0x47, 0x66, 0x69, 0x78, 0x83, 0xaf, 0xbd, 0xce, 0xad, 0x2f, 0x31} }, + { (uint64_t)900000000000000ull, {0x61, 0x3a, 0xa1, 0x2c, 0xc0, 0xa1, 0x9b, 0xc8, 0x43, 0x63, 0x50, 0xbb, 0xc0, 0xf6, 0x16, 0x32, 0x6e, 0x64, 0x85, 0x83, 0x33, 0x4a, 0x32, 0x65, 0x16, 0x29, 0xe9, 0x5, 0xc5, 0x20, 0x62, 0x69} }, + { (uint64_t)1000000000000000ull, {0x52, 0xdd, 0xf8, 0x81, 0x13, 0xa0, 0xfc, 0xf2, 0x12, 0x90, 0x95, 0xc6, 0x18, 0x91, 0xbe, 0x88, 0x5c, 0x9, 0x30, 0x8, 0xeb, 0xc4, 0x65, 0xc, 0xb0, 0xee, 0xa5, 0x60, 0xcd, 0x4d, 0x75, 0x1b} }, + { (uint64_t)2000000000000000ull, {0x75, 0xbd, 0xfc, 0x35, 0xa6, 0xdf, 0x76, 0xe5, 0x98, 0x8e, 0xd9, 0xe3, 0x10, 0xa5, 0x89, 0x16, 0xae, 0xf0, 0xc5, 0xf0, 0x5b, 0x89, 0x22, 0xea, 0xae, 0x2c, 0xf9, 0x8f, 0x58, 0x42, 0x3c, 0xe3} }, + { (uint64_t)3000000000000000ull, {0x88, 0x98, 0x93, 0xe8, 0x7d, 0x56, 0x9f, 0x14, 0xb2, 0x48, 0xd1, 0xed, 0x93, 0xe8, 0xce, 0x60, 0xbb, 0xe3, 0x73, 0x69, 0xb0, 0xd6, 0xc7, 0xa1, 0x86, 0x89, 0x33, 0xd3, 0xc3, 0xda, 0x9a, 0x72} }, + { (uint64_t)4000000000000000ull, {0x88, 0x3e, 0xf3, 0x4b, 0xa2, 0xc1, 0x91, 0xf4, 0x9d, 0x3c, 0xc6, 0xad, 0xa0, 0xaf, 0xf1, 0xcf, 0xb1, 0x77, 0xbd, 0x9e, 0xd4, 0xb3, 0xa5, 0x37, 0x84, 0xb7, 0xf1, 0x62, 0x9b, 0xed, 0x17, 0x41} }, + { (uint64_t)5000000000000000ull, {0xa2, 0x90, 0x7c, 0x39, 0x84, 0xb1, 0x4a, 0xb1, 0xf4, 0xda, 0x58, 0xc2, 0xc8, 0x2d, 0x6b, 0x24, 0xf1, 0x29, 0x49, 0x9, 0x75, 0xfc, 0x4a, 0x33, 0x3d, 0x25, 0xa1, 0xf9, 0x2b, 0xc4, 0x32, 0xb6} }, + { (uint64_t)6000000000000000ull, {0xa0, 0x7d, 0x9f, 0x18, 0x95, 0x1f, 0xf2, 0x32, 0xcf, 0x4e, 0xc0, 0xee, 0x2f, 0xbc, 0xc3, 0xe1, 0x1b, 0x2c, 0xaf, 0xc9, 0x57, 0x65, 0x82, 0x10, 0x38, 0x1e, 0x3e, 0xe4, 0xed, 0xec, 0x2e, 0x7a} }, + { (uint64_t)7000000000000000ull, {0x66, 0x80, 0x21, 0xd5, 0xde, 0x8c, 0xa4, 0xc1, 0x8f, 0x5a, 0x74, 0xf2, 0x78, 0x69, 0xc4, 0xd6, 0xd4, 0x93, 0xa3, 0x30, 0x39, 0x3c, 0xf0, 0x26, 0x41, 0xff, 0xa8, 0x56, 0x7b, 0xa5, 0x36, 0x20} }, + { (uint64_t)8000000000000000ull, {0xe0, 0x48, 0x7a, 0xc4, 0x5a, 0x82, 0x59, 0xe3, 0xe5, 0xf2, 0xd9, 0xb8, 0xf6, 0xb8, 0xfa, 0x26, 0x9a, 0x63, 0x49, 0x71, 0xa2, 0xf7, 0xc2, 0x1a, 0x54, 0x17, 0x76, 0x81, 0xeb, 0x2, 0xbd, 0x4a} }, + { (uint64_t)9000000000000000ull, {0x98, 0x92, 0x6a, 0x3a, 0xf0, 0x5b, 0xf4, 0xa9, 0x8d, 0xf9, 0xf6, 0x4a, 0xe7, 0xb9, 0xda, 0x45, 0xa7, 0x6, 0xc3, 0xf8, 0x39, 0x5e, 0x47, 0x1f, 0x96, 0xed, 0x3c, 0x6, 0x6, 0xbe, 0xbb, 0x71} }, + { (uint64_t)10000000000000000ull, {0x80, 0xad, 0xb7, 0xd, 0x46, 0xf6, 0x3a, 0x75, 0x64, 0xa3, 0xf6, 0x71, 0xd9, 0xba, 0x95, 0x71, 0xb7, 0xf7, 0x95, 0xa9, 0x63, 0x38, 0x2a, 0x4d, 0x9f, 0xaf, 0x2d, 0x54, 0xf6, 0xc6, 0x84, 0x29} }, + { (uint64_t)20000000000000000ull, {0xae, 0xbd, 0x97, 0x42, 0x1f, 0x3f, 0xca, 0xe8, 0x95, 0x18, 0x60, 0xe6, 0xd9, 0xd1, 0xf3, 0xec, 0x59, 0x73, 0xa2, 0xf7, 0x66, 0x88, 0x4b, 0xfe, 0x17, 0x50, 0x79, 0x51, 0xe4, 0x62, 0xc6, 0x63} }, + { (uint64_t)30000000000000000ull, {0x61, 0x2, 0x6c, 0x84, 0x2a, 0x6a, 0x22, 0x25, 0x74, 0x6b, 0x19, 0x6b, 0x56, 0x89, 0xe1, 0x18, 0xf5, 0x41, 0x34, 0x15, 0x98, 0x1d, 0x7, 0x73, 0x62, 0xb2, 0xe7, 0xb9, 0xac, 0xa5, 0x28, 0x16} }, + { (uint64_t)40000000000000000ull, {0x52, 0x54, 0xb5, 0x78, 0xe8, 0x57, 0x9a, 0x27, 0x3b, 0x89, 0x8e, 0x65, 0x9d, 0xd3, 0xe1, 0xa1, 0xcf, 0xba, 0x12, 0x47, 0x26, 0x64, 0xbd, 0x4e, 0x7f, 0x9a, 0x13, 0xb1, 0xfc, 0xee, 0x2, 0x93} }, + { (uint64_t)50000000000000000ull, {0x7b, 0x2a, 0xb, 00, 0xcf, 0xdc, 0xa9, 0x51, 0x46, 0xcf, 0x80, 0x95, 0xdd, 0x2b, 0x82, 0x90, 0x91, 0xb0, 0xf2, 0xd5, 0xbb, 0xc, 0x33, 0x82, 0x2d, 0x8b, 0x43, 0x42, 0x69, 0xcd, 0x2a, 0x42} }, + { (uint64_t)60000000000000000ull, {0x21, 0x57, 0x4f, 0xed, 0x15, 0x1a, 0x2f, 0x9f, 0x64, 0xa4, 0x5b, 0xe2, 0x8a, 0x3a, 0xf5, 0x88, 0xe9, 0xf2, 0xd1, 0x71, 0x35, 0xa3, 0x53, 0x7f, 0x7, 0xfd, 0x6a, 0xef, 0xa2, 0x9f, 0x2, 0xaf} }, + { (uint64_t)70000000000000000ull, {0x1a, 0xf2, 0x41, 0xe1, 0x38, 0x27, 0x98, 0x29, 0xac, 0x6a, 0xe6, 0x2f, 0xf, 0x33, 0x20, 0x4b, 0xb2, 0x8a, 0xfd, 0x6, 0x5c, 0x42, 0x59, 0x3b, 0xdc, 0x79, 0x14, 0x85, 0x97, 0x5b, 0x26, 0x95} }, + { (uint64_t)80000000000000000ull, {0xa8, 0xc8, 0xb8, 0x7b, 0x51, 0x2d, 0xef, 0x9b, 0x5e, 0x50, 0xe, 0xb4, 0x98, 0xaf, 0x86, 0xaa, 0xd2, 0x46, 0x4a, 0xea, 0xe7, 0x6d, 0xb1, 0xf6, 0x5d, 0x23, 0x26, 0xce, 0x90, 0x26, 0xec, 0x69} }, + { (uint64_t)90000000000000000ull, {0x3d, 0x78, 0x73, 0x63, 0x95, 0xf1, 0xd7, 0xde, 0x8e, 0x16, 0xc0, 0xb5, 0xa9, 0x9f, 0x4d, 0xc4, 0xeb, 0x8f, 0x22, 0xac, 0xc1, 0x5b, 0x21, 0x42, 0x44, 0x1d, 0xbd, 0x8d, 0x2c, 0x31, 0xb9, 0xce} }, + { (uint64_t)100000000000000000ull, {0x27, 0x27, 0xd4, 0x93, 0x2f, 0x98, 0x39, 0xe4, 0x3b, 0x6b, 0xf5, 0xfb, 0x29, 0xa3, 0xbe, 0x4c, 0x9, 0xb, 0x6e, 0xb9, 0x31, 00, 0xbb, 0x92, 0x58, 0x1a, 0xdb, 0x8d, 0xd2, 0xb6, 0x61, 0x54} }, + { (uint64_t)200000000000000000ull, {0xae, 0x96, 0x78, 0x2e, 0xf2, 0xc4, 0xdf, 0x7d, 0x2e, 0x4, 0xcc, 0xf9, 0xef, 0x76, 0x23, 0x7f, 0x17, 0xc, 0x97, 0x3, 0xb4, 0x92, 0xc0, 0x78, 0x52, 0x6e, 0xb1, 0xf6, 0x85, 0x3d, 0xb1, 0x33} }, + { (uint64_t)300000000000000000ull, {0x17, 0x43, 0xfe, 0xab, 0x12, 0xad, 0xe5, 0xfe, 0x12, 0x53, 0x22, 0x27, 0x2f, 0xd1, 0x40, 0x6b, 0x74, 0xe8, 0x19, 0x70, 0x32, 0x68, 0x46, 0x22, 0xee, 0x79, 0xab, 0xcd, 0x94, 0x93, 0x66, 0x4c} }, + { (uint64_t)400000000000000000ull, {0x7, 0x9b, 0xf2, 0xa9, 0x6e, 0x16, 0x6e, 0xf9, 0xe6, 0xb2, 0x23, 0x1d, 0xb9, 0x85, 0x8b, 0x99, 0x98, 0x7f, 0x49, 0x33, 0x87, 0xde, 0xeb, 0xd5, 0x17, 0x48, 0x54, 0x9a, 0xd, 0xf7, 0xdc, 0x44} }, + { (uint64_t)500000000000000000ull, {0xca, 0xba, 0x97, 0x98, 0x51, 0x6d, 0xad, 0x3, 0x38, 0xd0, 0x6e, 0x10, 0x6d, 0x76, 0xa2, 0x1, 0x93, 0x7a, 0xce, 0x4c, 0x91, 0x53, 0x9e, 0x61, 0x7d, 0x89, 0x28, 0x73, 0x6, 0xa3, 0x92, 0xb1} }, + { (uint64_t)600000000000000000ull, {0x6b, 0x8, 0x7f, 0x48, 0xb3, 0xd7, 0xaa, 0xc9, 0x57, 0xc4, 0x52, 0xe5, 0x1a, 0x18, 0xd7, 0x26, 0xb, 0xf8, 0xc8, 0x56, 0xc4, 0xc7, 0x1e, 0x48, 0xf6, 0x49, 0xae, 00, 0x4a, 0xf6, 0x8f, 0x13} }, + { (uint64_t)700000000000000000ull, {0x9e, 0xed, 0x8b, 0x23, 0x1f, 0x79, 0x4c, 0x46, 0x5c, 0xbe, 0x88, 0x40, 0xd0, 0xf1, 0x6f, 0x7b, 0x9f, 0x9c, 0x6e, 0xb4, 0x9c, 0x20, 0x7d, 0xe9, 0xd8, 0x55, 0x11, 0x83, 0xd0, 0xc7, 0x6e, 0x43} }, + { (uint64_t)800000000000000000ull, {0x58, 0x4a, 0x78, 0x93, 0x13, 0x7e, 0xbd, 0x2, 0x8b, 0xa7, 0x59, 0x82, 0xc3, 0x39, 0xb7, 0x66, 0xaa, 0xda, 0xad, 0xf9, 0x14, 0x50, 0xf9, 0x40, 0x7d, 0x2a, 0x97, 0xd7, 0xf6, 0xb1, 0x93, 0x5e} }, + { (uint64_t)900000000000000000ull, {0x7, 0xce, 0x54, 0xb1, 0x18, 0x26, 0xa1, 0x75, 0x23, 0x13, 0x55, 0x1a, 00, 0x20, 0xfd, 0x79, 0x8a, 00, 0x9e, 0x20, 0xcd, 0xb2, 0x40, 0x1d, 0x52, 0x51, 0xc1, 0x55, 0x8e, 0xea, 0xd2, 0x6c} }, + { (uint64_t)1000000000000000000ull, {0x39, 0x80, 0x7f, 0x3d, 0xce, 0xb0, 0xa6, 0xfe, 0x34, 0xa7, 0xa1, 0xed, 0xc6, 0x9b, 0x78, 0xff, 0xbe, 0xd5, 0xa7, 0x8c, 0x6c, 0x87, 0x5d, 0xda, 0x96, 0x69, 0xdb, 0xb2, 0x95, 0x70, 0xf0, 0xf4} }, + { (uint64_t)2000000000000000000ull, {0xda, 0x74, 00, 0x86, 0xf1, 0x5c, 0xe8, 0x21, 0xe9, 0xd, 0x50, 0xaf, 0xcf, 0x80, 0x9c, 0x7e, 0x18, 0x51, 0x90, 0x1b, 0xa3, 0x5f, 0x9f, 0x63, 0x78, 0xd6, 0x40, 0x7c, 0xb9, 0xc7, 0xa2, 0x75} }, + { (uint64_t)3000000000000000000ull, {0x7, 0xa1, 0x75, 0x63, 0xae, 0xf5, 0xcf, 0xd0, 0x36, 0xfa, 0x64, 0xd4, 0xb1, 0x97, 0xa9, 0x51, 0xc0, 0xd2, 0x87, 0x2b, 0xd, 0xb6, 0xf9, 0xbe, 0x47, 0xe6, 0x7c, 0xa6, 0xb5, 0x35, 0xe2, 0x6e} }, + { (uint64_t)4000000000000000000ull, {0xe3, 0x49, 0xf7, 0xeb, 0xe5, 0x11, 0x39, 0xfe, 0xd5, 0x69, 0x40, 0x37, 0xd1, 0x14, 0xb7, 0xbd, 0x45, 0xdd, 0xa, 0x6a, 0xf0, 0x4b, 0x62, 0xec, 0xa4, 0xd8, 0xcd, 0x55, 0x2a, 0x14, 0xe3, 0xfb} }, + { (uint64_t)5000000000000000000ull, {0x8d, 0x59, 0x7e, 0xa9, 0xf5, 0x79, 0x9a, 0x4d, 0x15, 0x3d, 0x82, 0xd6, 0xf7, 0xbe, 0xa0, 0x2e, 0x52, 0x40, 0xa2, 0xc8, 0x9b, 0x4, 0x1e, 0x6, 0x2f, 0x37, 0xbc, 0x7b, 0x82, 0xa0, 0xac, 0x55} }, + { (uint64_t)6000000000000000000ull, {0xa3, 0x43, 0xa7, 0xe1, 0x14, 0x4d, 0x33, 0x50, 0xf, 0x3e, 0xfd, 0x38, 0x15, 0x82, 0xdd, 0xc5, 0xd0, 0x18, 0x3e, 0x5d, 0xcf, 0x8a, 0xfa, 0x64, 0xbb, 0x67, 0x6c, 0x97, 0x3e, 0x3d, 0x1a, 0xb1} }, + { (uint64_t)7000000000000000000ull, {0x89, 0xe9, 0x3e, 0xe9, 0xf2, 0x4d, 0x72, 0x61, 0xe5, 0x44, 0xca, 0x8f, 0x9, 0xa7, 0x40, 0x4e, 0xe3, 0xa9, 0xe, 0xe2, 0x50, 0x7d, 0xda, 0xcf, 0x41, 0x2a, 0x58, 0xc, 0x9, 0x65, 0x1c, 0x53} }, + { (uint64_t)8000000000000000000ull, {0xc5, 0x94, 0x10, 0x81, 0x54, 0x69, 0xf4, 0x59, 0xd1, 0x5a, 0x6f, 0xe3, 0xf2, 0xa1, 0x1b, 0xa6, 0x31, 0x12, 0xfa, 0xaa, 0xc5, 0x3d, 0xbc, 0x52, 0x5d, 0x3c, 0xfa, 0xb1, 0xfa, 0x9c, 0x3d, 0xdb} }, + { (uint64_t)9000000000000000000ull, {0x9d, 0xe7, 0xcb, 0xb, 0x8d, 0x7b, 0xac, 0x47, 0xff, 0xd3, 0x93, 0x1b, 0xcd, 0x82, 0xcd, 0xd5, 0x35, 0xc, 0x29, 0x34, 0xb1, 0x6e, 0xb, 0x64, 0x32, 0xab, 0xf7, 0xcb, 0x4b, 0x5c, 0x37, 0x6d} }, + { (uint64_t)10000000000000000000ull, {0x65, 0x8d, 0x1, 0x37, 0x6d, 0x18, 0x63, 0xe7, 0x7b, 0x9, 0x6f, 0x98, 0xe6, 0xe5, 0x13, 0xc2, 0x4, 0x10, 0xf5, 0xc7, 0xfb, 0x18, 0xa6, 0xe5, 0x9a, 0x52, 0x66, 0x84, 0x5c, 0xd9, 0xb1, 0xe3} }, +}; + namespace rct { //Various key initialization functions @@ -143,6 +321,14 @@ namespace rct { } key zeroCommit(xmr_amount amount) { + const zero_commitment *begin = zero_commitments; + const zero_commitment *end = zero_commitments + sizeof(zero_commitments) / sizeof(zero_commitments[0]); + const zero_commitment value{amount, rct::zero()}; + const auto it = std::lower_bound(begin, end, value, [](const zero_commitment &e0, const zero_commitment &e1){ return e0.amount < e1.amount; }); + if (it != end && it->amount == amount) + { + return it->commitment; + } key am = d2h(amount); key bH = scalarmultH(am); return addKeys(G, bH); diff --git a/tests/performance_tests/crypto_ops.h b/tests/performance_tests/crypto_ops.h index 3c68583c5a6..25ab008db74 100644 --- a/tests/performance_tests/crypto_ops.h +++ b/tests/performance_tests/crypto_ops.h @@ -40,6 +40,7 @@ enum test_op op_sc_mul, op_ge_add_raw, op_ge_add_p3_p3, + op_zeroCommitCached, ops_fast, op_addKeys, @@ -54,6 +55,7 @@ enum test_op op_addKeys3, op_addKeys3_2, op_isInMainSubgroup, + op_zeroCommitUncached, }; template @@ -108,6 +110,8 @@ class test_crypto_ops case op_addKeys3: rct::addKeys3(key, scalar0, point0, scalar1, precomp1); break; case op_addKeys3_2: rct::addKeys3(key, scalar0, precomp0, scalar1, precomp1); break; case op_isInMainSubgroup: rct::isInMainSubgroup(point0); break; + case op_zeroCommitUncached: rct::zeroCommit(9001); break; + case op_zeroCommitCached: rct::zeroCommit(9000); break; default: return false; } return true; diff --git a/tests/performance_tests/main.cpp b/tests/performance_tests/main.cpp index 87a1573c2c8..3c71cb99fb6 100644 --- a/tests/performance_tests/main.cpp +++ b/tests/performance_tests/main.cpp @@ -256,6 +256,8 @@ int main(int argc, char** argv) TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_addKeys3); TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_addKeys3_2); TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_isInMainSubgroup); + TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_zeroCommitUncached); + TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_zeroCommitCached); TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_bos_coster, 2); TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_bos_coster, 4); diff --git a/tests/unit_tests/ringct.cpp b/tests/unit_tests/ringct.cpp index 3877ef7859c..52bdb00cf68 100644 --- a/tests/unit_tests/ringct.cpp +++ b/tests/unit_tests/ringct.cpp @@ -1086,6 +1086,25 @@ TEST(ringct, zeroCommmit) ASSERT_EQ(z, manual); } +static rct::key uncachedZeroCommit(uint64_t amount) +{ + const rct::key am = rct::d2h(amount); + const rct::key bH = rct::scalarmultH(am); + return rct::addKeys(rct::G, bH); +} + +TEST(ringct, zeroCommitCache) +{ + ASSERT_EQ(rct::zeroCommit(0), uncachedZeroCommit(0)); + ASSERT_EQ(rct::zeroCommit(1), uncachedZeroCommit(1)); + ASSERT_EQ(rct::zeroCommit(2), uncachedZeroCommit(2)); + ASSERT_EQ(rct::zeroCommit(10), uncachedZeroCommit(10)); + ASSERT_EQ(rct::zeroCommit(200), uncachedZeroCommit(200)); + ASSERT_EQ(rct::zeroCommit(1000000000), uncachedZeroCommit(1000000000)); + ASSERT_EQ(rct::zeroCommit(3000000000000), uncachedZeroCommit(3000000000000)); + ASSERT_EQ(rct::zeroCommit(900000000000000), uncachedZeroCommit(900000000000000)); +} + TEST(ringct, H) { ge_p3 p3; From e198b06e7aed865b02198a53934fd63f94dc4ced Mon Sep 17 00:00:00 2001 From: Hasan Pekdemir Date: Sun, 4 Nov 2018 16:24:30 +0100 Subject: [PATCH 0723/1404] Fix: out_of_hashchain_bounds_error in refresh 15:43 < hahsun> Im on stagenet and I suddenly get this exception: 2018-11-04 14:42:52.416 [RPC0] ERROR wallet.wallet2 src/wallet/wallet2.cpp:2070 !m_blockchain.is_in_bounds(current_index). THROW EXCEPTION: error::out_of_hashchain_bounds_error 16:01 <+moneromooo> OK, possibly because the blckchain is always seeded with the genesis block hash... 16:02 <+moneromooo> So that case should be allowed, assuming it doesn't break the code around it. 16:05 <+moneromooo> OK if stop_height == size || (size==1 && stop_heigt ==0) 16:05 <+moneromooo> Throw if not that. 16:06 < hahsun> k --- src/wallet/wallet2.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 37b60c5d73f..6f494fd056a 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -2720,7 +2720,7 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo short_chain_history.clear(); get_short_chain_history(short_chain_history); fast_refresh(stop_height, blocks_start_height, short_chain_history, true); - THROW_WALLET_EXCEPTION_IF(m_blockchain.size() != stop_height, error::wallet_internal_error, "Unexpected hashchain size"); + THROW_WALLET_EXCEPTION_IF((m_blockchain.size() == stop_height || (m_blockchain.size() == 1 && stop_height == 0) ? false : true), error::wallet_internal_error, "Unexpected hashchain size"); THROW_WALLET_EXCEPTION_IF(m_blockchain.offset() != 0, error::wallet_internal_error, "Unexpected hashchain offset"); for (const auto &h: tip) m_blockchain.push_back(h); From f92682ec0aff270f36901b1231213da7f02e8c27 Mon Sep 17 00:00:00 2001 From: Jethro Grassie Date: Wed, 31 Oct 2018 23:11:09 -0400 Subject: [PATCH 0724/1404] build: ubuntu 16 ppc build fixes --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a3dbf974a94..d31c494b27d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -148,7 +148,7 @@ if(ARCH_ID STREQUAL "powerpc64" OR ARCH_ID STREQUAL "ppc64") set(PPC 0) endif() -if(ARCH_ID STREQUAL "powerpc") +if(ARCH_ID STREQUAL "powerpc" OR ARCH_ID STREQUAL "ppc") set(PPC64LE 0) set(PPC64 0) set(PPC 1) From 769ae42a7b0e7e73a13ef578ec9a0ecb06df22c1 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 10 Sep 2018 13:08:34 +0000 Subject: [PATCH 0725/1404] wallet2: faster output and key image import/export --- src/wallet/wallet2.cpp | 94 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 76 insertions(+), 18 deletions(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index c27e4e82016..31246d0b17e 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -69,6 +69,7 @@ using namespace epee; #include "common/base58.h" #include "common/dns_utils.h" #include "common/notify.h" +#include "common/perf_timer.h" #include "ringct/rctSigs.h" #include "ringdb.h" #include "device/device_cold.hpp" @@ -10517,11 +10518,13 @@ crypto::public_key wallet2::get_tx_pub_key_from_received_outs(const tools::walle bool wallet2::export_key_images(const std::string &filename) const { + PERF_TIMER(export_key_images); std::vector> ski = export_key_images(); std::string magic(KEY_IMAGE_EXPORT_FILE_MAGIC, strlen(KEY_IMAGE_EXPORT_FILE_MAGIC)); const cryptonote::account_public_address &keys = get_account().get_keys().m_account_address; std::string data; + data.reserve(ski.size() * (sizeof(crypto::key_image) + sizeof(crypto::signature)) + 2 * sizeof(crypto::public_key)); data += std::string((const char *)&keys.m_spend_public_key, sizeof(crypto::public_key)); data += std::string((const char *)&keys.m_view_public_key, sizeof(crypto::public_key)); for (const auto &i: ski) @@ -10531,6 +10534,7 @@ bool wallet2::export_key_images(const std::string &filename) const } // encrypt data, keep magic plaintext + PERF_TIMER(export_key_images_encrypt); std::string ciphertext = encrypt_with_view_secret_key(data); return epee::file_io_utils::save_string_to_file(filename, magic + ciphertext); } @@ -10538,6 +10542,7 @@ bool wallet2::export_key_images(const std::string &filename) const //---------------------------------------------------------------------------------------------------- std::vector> wallet2::export_key_images() const { + PERF_TIMER(export_key_images_raw); std::vector> ski; ski.reserve(m_transfers.size()); @@ -10590,6 +10595,7 @@ std::vector> wallet2::export_key uint64_t wallet2::import_key_images(const std::string &filename, uint64_t &spent, uint64_t &unspent) { + PERF_TIMER(import_key_images_fsu); std::string data; bool r = epee::file_io_utils::load_file_to_string(filename, data); @@ -10603,6 +10609,7 @@ uint64_t wallet2::import_key_images(const std::string &filename, uint64_t &spent try { + PERF_TIMER(import_key_images_decrypt); data = decrypt_with_view_secret_key(std::string(data, magiclen)); } catch (const std::exception &e) @@ -10641,6 +10648,7 @@ uint64_t wallet2::import_key_images(const std::string &filename, uint64_t &spent //---------------------------------------------------------------------------------------------------- uint64_t wallet2::import_key_images(const std::vector> &signed_key_images, uint64_t &spent, uint64_t &unspent, bool check_spent) { + PERF_TIMER(import_key_images_lots); COMMAND_RPC_IS_KEY_IMAGE_SPENT::request req = AUTO_VAL_INIT(req); COMMAND_RPC_IS_KEY_IMAGE_SPENT::response daemon_resp = AUTO_VAL_INIT(daemon_resp); @@ -10654,6 +10662,9 @@ uint64_t wallet2::import_key_images(const std::vector(out.target); const crypto::public_key pkey = o.key; - std::vector pkeys; - pkeys.push_back(&pkey); - THROW_WALLET_EXCEPTION_IF(!(rct::scalarmultKey(rct::ki2rct(key_image), rct::curveOrder()) == rct::identity()), - error::wallet_internal_error, "Key image out of validity domain: input " + boost::lexical_cast(n) + "/" - + boost::lexical_cast(signed_key_images.size()) + ", key image " + epee::string_tools::pod_to_hex(key_image)); - - THROW_WALLET_EXCEPTION_IF(!crypto::check_ring_signature((const crypto::hash&)key_image, key_image, pkeys, &signature), - error::signature_check_failed, boost::lexical_cast(n) + "/" - + boost::lexical_cast(signed_key_images.size()) + ", key image " + epee::string_tools::pod_to_hex(key_image) - + ", signature " + epee::string_tools::pod_to_hex(signature) + ", pubkey " + epee::string_tools::pod_to_hex(*pkeys[0])); + if (!td.m_key_image_known || !(key_image == td.m_key_image)) + { + std::vector pkeys; + pkeys.push_back(&pkey); + THROW_WALLET_EXCEPTION_IF(!(rct::scalarmultKey(rct::ki2rct(key_image), rct::curveOrder()) == rct::identity()), + error::wallet_internal_error, "Key image out of validity domain: input " + boost::lexical_cast(n) + "/" + + boost::lexical_cast(signed_key_images.size()) + ", key image " + epee::string_tools::pod_to_hex(key_image)); + THROW_WALLET_EXCEPTION_IF(!crypto::check_ring_signature((const crypto::hash&)key_image, key_image, pkeys, &signature), + error::signature_check_failed, boost::lexical_cast(n) + "/" + + boost::lexical_cast(signed_key_images.size()) + ", key image " + epee::string_tools::pod_to_hex(key_image) + + ", signature " + epee::string_tools::pod_to_hex(signature) + ", pubkey " + epee::string_tools::pod_to_hex(*pkeys[0])); + } req.key_images.push_back(epee::string_tools::pod_to_hex(key_image)); } + PERF_TIMER_STOP(import_key_images_A); + PERF_TIMER_START(import_key_images_B); for (size_t n = 0; n < signed_key_images.size(); ++n) { m_transfers[n].m_key_image = signed_key_images[n].first; @@ -10688,9 +10703,11 @@ uint64_t wallet2::import_key_images(const std::vector spent_key_images; + PERF_TIMER_START(import_key_images_C); for (const transfer_details &td: m_transfers) { for (const cryptonote::txin_v& in : td.m_tx.vin) @@ -10721,10 +10739,12 @@ uint64_t wallet2::import_key_images(const std::vector(in).k_image, td.m_txid)); } } + PERF_TIMER_STOP(import_key_images_C); + PERF_TIMER_START(import_key_images_D); for(size_t i = 0; i < signed_key_images.size(); ++i) { - transfer_details &td = m_transfers[i]; + const transfer_details &td = m_transfers[i]; uint64_t amount = td.amount(); if (td.m_spent) spent += amount; @@ -10742,6 +10762,8 @@ uint64_t wallet2::import_key_images(const std::vectorsecond); } } + PERF_TIMER_STOP(import_key_images_D); + MDEBUG("Total: " << print_money(spent) << " spent, " << print_money(unspent) << " unspent"); if (check_spent) @@ -10751,8 +10773,12 @@ uint64_t wallet2::import_key_images(const std::vector wallet2::export_outputs() const { + PERF_TIMER(export_outputs); std::vector outs; outs.reserve(m_transfers.size()); @@ -10970,29 +11002,53 @@ std::vector wallet2::export_outputs() const //---------------------------------------------------------------------------------------------------- std::string wallet2::export_outputs_to_str() const { - std::vector outs = export_outputs(); + PERF_TIMER(export_outputs_to_str); std::stringstream oss; boost::archive::portable_binary_oarchive ar(oss); - ar << outs; + ar << m_transfers; std::string magic(OUTPUT_EXPORT_FILE_MAGIC, strlen(OUTPUT_EXPORT_FILE_MAGIC)); const cryptonote::account_public_address &keys = get_account().get_keys().m_account_address; std::string header; header += std::string((const char *)&keys.m_spend_public_key, sizeof(crypto::public_key)); header += std::string((const char *)&keys.m_view_public_key, sizeof(crypto::public_key)); + PERF_TIMER(export_outputs_encryption); std::string ciphertext = encrypt_with_view_secret_key(header + oss.str()); return magic + ciphertext; } //---------------------------------------------------------------------------------------------------- size_t wallet2::import_outputs(const std::vector &outputs) { - m_transfers.clear(); - m_transfers.reserve(outputs.size()); + PERF_TIMER(import_outputs); + const size_t original_size = m_transfers.size(); + m_transfers.resize(outputs.size()); for (size_t i = 0; i < outputs.size(); ++i) { transfer_details td = outputs[i]; + // skip those we've already imported, or which have different data + if (i < original_size) + { + // compare the data used to create the key image below + const transfer_details &org_td = m_transfers[i]; + if (!org_td.m_key_image_known) + goto process; +#define CMPF(f) if (!(td.f == org_td.f)) goto process + CMPF(m_txid); + CMPF(m_key_image); + CMPF(m_internal_output_index); +#undef CMPF + if (!(get_transaction_prefix_hash(td.m_tx) == get_transaction_prefix_hash(org_td.m_tx))) + goto process; + + // copy anyway, since the comparison does not include ancillary fields which may have changed + m_transfers[i] = std::move(td); + continue; + } + +process: + // the hot wallet wouldn't have known about key images (except if we already exported them) cryptonote::keypair in_ephemeral; @@ -11011,9 +11067,9 @@ size_t wallet2::import_outputs(const std::vector(i)); - m_key_images[td.m_key_image] = m_transfers.size(); - m_pub_keys[td.get_public_key()] = m_transfers.size(); - m_transfers.push_back(std::move(td)); + m_key_images[td.m_key_image] = i; + m_pub_keys[td.get_public_key()] = i; + m_transfers[i] = std::move(td); } return m_transfers.size(); @@ -11021,6 +11077,7 @@ size_t wallet2::import_outputs(const std::vector Date: Wed, 24 Oct 2018 18:24:11 +0000 Subject: [PATCH 0726/1404] wallet2: only export necessary outputs and key images and disable annoying test that requires ridiculous amounts of skullduggery every time some format changes --- src/device_trezor/device_trezor.cpp | 5 +- src/simplewallet/simplewallet.cpp | 4 +- src/wallet/wallet2.cpp | 122 ++++++++++++------- src/wallet/wallet2.h | 24 +++- src/wallet/wallet_rpc_server.cpp | 13 +- src/wallet/wallet_rpc_server_commands_defs.h | 6 +- tests/data/fuzz/cold-outputs/OUTPUTS2 | Bin 256 -> 581 bytes tests/fuzz/cold-outputs.cpp | 2 +- tests/unit_tests/serialization.cpp | 14 ++- 9 files changed, 125 insertions(+), 65 deletions(-) diff --git a/src/device_trezor/device_trezor.cpp b/src/device_trezor/device_trezor.cpp index 07c03fc6699..f55cbb15dd7 100644 --- a/src/device_trezor/device_trezor.cpp +++ b/src/device_trezor/device_trezor.cpp @@ -212,9 +212,10 @@ namespace trezor { tools::wallet2::signed_tx_set & signed_tx, hw::tx_aux_data & aux_data) { + CHECK_AND_ASSERT_THROW_MES(unsigned_tx.transfers.first == 0, "Unsuported non zero offset"); size_t num_tx = unsigned_tx.txes.size(); signed_tx.key_images.clear(); - signed_tx.key_images.resize(unsigned_tx.transfers.size()); + signed_tx.key_images.resize(unsigned_tx.transfers.second.size()); for(size_t tx_idx = 0; tx_idx < num_tx; ++tx_idx) { std::shared_ptr signer; @@ -360,4 +361,4 @@ namespace trezor { } #endif //WITH_DEVICE_TREZOR -}} \ No newline at end of file +}} diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 58d4cdcedc4..babc59ea3c2 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -6105,8 +6105,8 @@ bool simple_wallet::accept_loaded_tx(const std::function get_num_txes, bool simple_wallet::accept_loaded_tx(const tools::wallet2::unsigned_tx_set &txs) { std::string extra_message; - if (!txs.transfers.empty()) - extra_message = (boost::format("%u outputs to import. ") % (unsigned)txs.transfers.size()).str(); + if (!txs.transfers.second.empty()) + extra_message = (boost::format("%u outputs to import. ") % (unsigned)txs.transfers.second.size()).str(); return accept_loaded_tx([&txs](){return txs.txes.size();}, [&txs](size_t n)->const tools::wallet2::tx_construction_data&{return txs.txes[n];}, extra_message); } //---------------------------------------------------------------------------------------------------- diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 31246d0b17e..a90a93321cd 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -113,11 +113,11 @@ using namespace cryptonote; #define SUBADDRESS_LOOKAHEAD_MAJOR 50 #define SUBADDRESS_LOOKAHEAD_MINOR 200 -#define KEY_IMAGE_EXPORT_FILE_MAGIC "Monero key image export\002" +#define KEY_IMAGE_EXPORT_FILE_MAGIC "Monero key image export\003" #define MULTISIG_EXPORT_FILE_MAGIC "Monero multisig export\001" -#define OUTPUT_EXPORT_FILE_MAGIC "Monero output export\003" +#define OUTPUT_EXPORT_FILE_MAGIC "Monero output export\004" #define SEGREGATION_FORK_HEIGHT 99999999 #define TESTNET_SEGREGATION_FORK_HEIGHT 99999999 @@ -1606,6 +1606,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote td.m_txid = txid; td.m_key_image = tx_scan_info[o].ki; td.m_key_image_known = !m_watch_only && !m_multisig; + td.m_key_image_requested = false; td.m_key_image_partial = m_multisig; td.m_amount = amount; td.m_pk_index = pk_index - 1; @@ -5466,7 +5467,7 @@ std::string wallet2::dump_tx_to_str(const std::vector &ptx_vector) c txs.txes.push_back(get_construction_data_with_decrypted_short_payment_id(tx, m_account.get_device())); } - txs.transfers = m_transfers; + txs.transfers = export_outputs(); // save as binary std::ostringstream oss; boost::archive::portable_binary_oarchive ar(oss); @@ -7952,6 +7953,7 @@ void wallet2::light_wallet_get_unspent_outs() td.m_key_image = unspent_key_image; td.m_key_image_known = !m_watch_only && !m_multisig; + td.m_key_image_requested = false; td.m_key_image_partial = m_multisig; td.m_amount = o.amount; td.m_pk_index = 0; @@ -9125,7 +9127,7 @@ void wallet2::cold_sign_tx(const std::vector& ptx_vector, signed_tx_ { txs.txes.push_back(get_construction_data_with_decrypted_short_payment_id(tx, m_account.get_device())); } - txs.transfers = m_transfers; + txs.transfers = std::make_pair(0, m_transfers); auto dev_cold = dynamic_cast<::hw::device_cold*>(&hwdev); CHECK_AND_ASSERT_THROW_MES(dev_cold, "Device does not implement cold signing interface"); @@ -9156,7 +9158,7 @@ uint64_t wallet2::cold_key_image_sync(uint64_t &spent, uint64_t &unspent) { dev_cold->ki_sync(&wallet_shim, m_transfers, ski); - return import_key_images(ski, spent, unspent); + return import_key_images(ski, 0, spent, unspent); } //---------------------------------------------------------------------------------------------------- void wallet2::get_hard_fork_info(uint8_t version, uint64_t &earliest_height) const @@ -10519,15 +10521,21 @@ crypto::public_key wallet2::get_tx_pub_key_from_received_outs(const tools::walle bool wallet2::export_key_images(const std::string &filename) const { PERF_TIMER(export_key_images); - std::vector> ski = export_key_images(); + std::pair>> ski = export_key_images(); std::string magic(KEY_IMAGE_EXPORT_FILE_MAGIC, strlen(KEY_IMAGE_EXPORT_FILE_MAGIC)); const cryptonote::account_public_address &keys = get_account().get_keys().m_account_address; + const uint32_t offset = ski.first; std::string data; - data.reserve(ski.size() * (sizeof(crypto::key_image) + sizeof(crypto::signature)) + 2 * sizeof(crypto::public_key)); + data.reserve(4 + ski.second.size() * (sizeof(crypto::key_image) + sizeof(crypto::signature)) + 2 * sizeof(crypto::public_key)); + data.resize(4); + data[0] = offset & 0xff; + data[1] = (offset >> 8) & 0xff; + data[2] = (offset >> 16) & 0xff; + data[3] = (offset >> 24) & 0xff; data += std::string((const char *)&keys.m_spend_public_key, sizeof(crypto::public_key)); data += std::string((const char *)&keys.m_view_public_key, sizeof(crypto::public_key)); - for (const auto &i: ski) + for (const auto &i: ski.second) { data += std::string((const char *)&i.first, sizeof(crypto::key_image)); data += std::string((const char *)&i.second, sizeof(crypto::signature)); @@ -10540,13 +10548,17 @@ bool wallet2::export_key_images(const std::string &filename) const } //---------------------------------------------------------------------------------------------------- -std::vector> wallet2::export_key_images() const +std::pair>> wallet2::export_key_images() const { PERF_TIMER(export_key_images_raw); std::vector> ski; - ski.reserve(m_transfers.size()); - for (size_t n = 0; n < m_transfers.size(); ++n) + size_t offset = 0; + while (offset < m_transfers.size() && !m_transfers[offset].m_key_image_requested) + ++offset; + + ski.reserve(m_transfers.size() - offset); + for (size_t n = offset; n < m_transfers.size(); ++n) { const transfer_details &td = m_transfers[n]; @@ -10590,7 +10602,7 @@ std::vector> wallet2::export_key ski.push_back(std::make_pair(td.m_key_image, signature)); } - return ski; + return std::make_pair(offset, ski); } uint64_t wallet2::import_key_images(const std::string &filename, uint64_t &spent, uint64_t &unspent) @@ -10617,15 +10629,17 @@ uint64_t wallet2::import_key_images(const std::string &filename, uint64_t &spent THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string("Failed to decrypt ") + filename + ": " + e.what()); } - const size_t headerlen = 2 * sizeof(crypto::public_key); + const size_t headerlen = 4 + 2 * sizeof(crypto::public_key); THROW_WALLET_EXCEPTION_IF(data.size() < headerlen, error::wallet_internal_error, std::string("Bad data size from file ") + filename); - const crypto::public_key &public_spend_key = *(const crypto::public_key*)&data[0]; - const crypto::public_key &public_view_key = *(const crypto::public_key*)&data[sizeof(crypto::public_key)]; + const uint32_t offset = (uint8_t)data[0] | (((uint8_t)data[1]) << 8) | (((uint8_t)data[2]) << 16) | (((uint8_t)data[3]) << 24); + const crypto::public_key &public_spend_key = *(const crypto::public_key*)&data[4]; + const crypto::public_key &public_view_key = *(const crypto::public_key*)&data[4 + sizeof(crypto::public_key)]; const cryptonote::account_public_address &keys = get_account().get_keys().m_account_address; if (public_spend_key != keys.m_spend_public_key || public_view_key != keys.m_view_public_key) { THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string( "Key images from ") + filename + " are for a different account"); } + THROW_WALLET_EXCEPTION_IF(offset > m_transfers.size(), error::wallet_internal_error, "Offset larger than known outputs"); const size_t record_size = sizeof(crypto::key_image) + sizeof(crypto::signature); THROW_WALLET_EXCEPTION_IF((data.size() - headerlen) % record_size, @@ -10642,20 +10656,21 @@ uint64_t wallet2::import_key_images(const std::string &filename, uint64_t &spent ski.push_back(std::make_pair(key_image, signature)); } - return import_key_images(ski, spent, unspent); + return import_key_images(ski, offset, spent, unspent); } //---------------------------------------------------------------------------------------------------- -uint64_t wallet2::import_key_images(const std::vector> &signed_key_images, uint64_t &spent, uint64_t &unspent, bool check_spent) +uint64_t wallet2::import_key_images(const std::vector> &signed_key_images, size_t offset, uint64_t &spent, uint64_t &unspent, bool check_spent) { PERF_TIMER(import_key_images_lots); COMMAND_RPC_IS_KEY_IMAGE_SPENT::request req = AUTO_VAL_INIT(req); COMMAND_RPC_IS_KEY_IMAGE_SPENT::response daemon_resp = AUTO_VAL_INIT(daemon_resp); - THROW_WALLET_EXCEPTION_IF(signed_key_images.size() > m_transfers.size(), error::wallet_internal_error, + THROW_WALLET_EXCEPTION_IF(offset > m_transfers.size(), error::wallet_internal_error, "Offset larger than known outputs"); + THROW_WALLET_EXCEPTION_IF(signed_key_images.size() > m_transfers.size() - offset, error::wallet_internal_error, "The blockchain is out of date compared to the signed key images"); - if (signed_key_images.empty()) + if (signed_key_images.empty() && offset == 0) { spent = 0; unspent = 0; @@ -10667,7 +10682,7 @@ uint64_t wallet2::import_key_images(const std::vector pkeys; pkeys.push_back(&pkey); THROW_WALLET_EXCEPTION_IF(!(rct::scalarmultKey(rct::ki2rct(key_image), rct::curveOrder()) == rct::identity()), - error::wallet_internal_error, "Key image out of validity domain: input " + boost::lexical_cast(n) + "/" + error::wallet_internal_error, "Key image out of validity domain: input " + boost::lexical_cast(n + offset) + "/" + boost::lexical_cast(signed_key_images.size()) + ", key image " + epee::string_tools::pod_to_hex(key_image)); THROW_WALLET_EXCEPTION_IF(!crypto::check_ring_signature((const crypto::hash&)key_image, key_image, pkeys, &signature), - error::signature_check_failed, boost::lexical_cast(n) + "/" + error::signature_check_failed, boost::lexical_cast(n + offset) + "/" + boost::lexical_cast(signed_key_images.size()) + ", key image " + epee::string_tools::pod_to_hex(key_image) + ", signature " + epee::string_tools::pod_to_hex(signature) + ", pubkey " + epee::string_tools::pod_to_hex(*pkeys[0])); } @@ -10698,10 +10713,11 @@ uint64_t wallet2::import_key_images(const std::vector key_images) td.m_key_image = key_images[i]; m_key_images[m_transfers[i].m_key_image] = i; td.m_key_image_known = true; + td.m_key_image_requested = false; td.m_key_image_partial = false; m_pub_keys[m_transfers[i].get_public_key()] = i; } @@ -10984,20 +11001,24 @@ void wallet2::import_blockchain(const std::tuple wallet2::export_outputs() const +std::pair> wallet2::export_outputs() const { PERF_TIMER(export_outputs); std::vector outs; - outs.reserve(m_transfers.size()); - for (size_t n = 0; n < m_transfers.size(); ++n) + size_t offset = 0; + while (offset < m_transfers.size() && m_transfers[offset].m_key_image_known) + ++offset; + + outs.reserve(m_transfers.size() - offset); + for (size_t n = offset; n < m_transfers.size(); ++n) { const transfer_details &td = m_transfers[n]; outs.push_back(td); } - return outs; + return std::make_pair(offset, outs); } //---------------------------------------------------------------------------------------------------- std::string wallet2::export_outputs_to_str() const @@ -11006,7 +11027,7 @@ std::string wallet2::export_outputs_to_str() const std::stringstream oss; boost::archive::portable_binary_oarchive ar(oss); - ar << m_transfers; + ar << export_outputs(); std::string magic(OUTPUT_EXPORT_FILE_MAGIC, strlen(OUTPUT_EXPORT_FILE_MAGIC)); const cryptonote::account_public_address &keys = get_account().get_keys().m_account_address; @@ -11018,20 +11039,27 @@ std::string wallet2::export_outputs_to_str() const return magic + ciphertext; } //---------------------------------------------------------------------------------------------------- -size_t wallet2::import_outputs(const std::vector &outputs) +size_t wallet2::import_outputs(const std::pair> &outputs) { PERF_TIMER(import_outputs); + + THROW_WALLET_EXCEPTION_IF(outputs.first > m_transfers.size(), error::wallet_internal_error, + "Imported outputs omit more outputs that we know of"); + + const size_t offset = outputs.first; const size_t original_size = m_transfers.size(); - m_transfers.resize(outputs.size()); - for (size_t i = 0; i < outputs.size(); ++i) + m_transfers.resize(offset + outputs.second.size()); + for (size_t i = 0; i < offset; ++i) + m_transfers[i].m_key_image_requested = false; + for (size_t i = 0; i < outputs.second.size(); ++i) { - transfer_details td = outputs[i]; + transfer_details td = outputs.second[i]; // skip those we've already imported, or which have different data - if (i < original_size) + if (i + offset < original_size) { // compare the data used to create the key image below - const transfer_details &org_td = m_transfers[i]; + const transfer_details &org_td = m_transfers[i + offset]; if (!org_td.m_key_image_known) goto process; #define CMPF(f) if (!(td.f == org_td.f)) goto process @@ -11043,7 +11071,7 @@ size_t wallet2::import_outputs(const std::vector(i)); + THROW_WALLET_EXCEPTION_IF(td.m_tx.vout.empty(), error::wallet_internal_error, "tx with no outputs at index " + boost::lexical_cast(i + offset)); crypto::public_key tx_pub_key = get_tx_pub_key_from_received_outs(td); const std::vector additional_tx_pub_keys = get_additional_tx_pub_keys_from_extra(td.m_tx); @@ -11063,13 +11091,14 @@ size_t wallet2::import_outputs(const std::vector(i)); + error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key at index " + boost::lexical_cast(i + offset)); - m_key_images[td.m_key_image] = i; - m_pub_keys[td.get_public_key()] = i; - m_transfers[i] = std::move(td); + m_key_images[td.m_key_image] = i + offset; + m_pub_keys[td.get_public_key()] = i + offset; + m_transfers[i + offset] = std::move(td); } return m_transfers.size(); @@ -11114,7 +11143,7 @@ size_t wallet2::import_outputs_from_str(const std::string &outputs_st) std::string body(data, headerlen); std::stringstream iss; iss << body; - std::vector outputs; + std::pair> outputs; try { boost::archive::portable_binary_iarchive ar(iss); @@ -11306,6 +11335,7 @@ void wallet2::update_multisig_rescan_info(const std::vector txes; - wallet2::transfer_container transfers; + std::pair transfers; }; struct signed_tx_set @@ -1071,9 +1073,9 @@ namespace tools bool verify_with_public_key(const std::string &data, const crypto::public_key &public_key, const std::string &signature) const; // Import/Export wallet data - std::vector export_outputs() const; + std::pair> export_outputs() const; std::string export_outputs_to_str() const; - size_t import_outputs(const std::vector &outputs); + size_t import_outputs(const std::pair> &outputs); size_t import_outputs_from_str(const std::string &outputs_st); payment_container export_payments() const; void import_payments(const payment_container &payments); @@ -1081,8 +1083,8 @@ namespace tools std::tuple> export_blockchain() const; void import_blockchain(const std::tuple> &bc); bool export_key_images(const std::string &filename) const; - std::vector> export_key_images() const; - uint64_t import_key_images(const std::vector> &signed_key_images, uint64_t &spent, uint64_t &unspent, bool check_spent = true); + std::pair>> export_key_images() const; + uint64_t import_key_images(const std::vector> &signed_key_images, size_t offset, uint64_t &spent, uint64_t &unspent, bool check_spent = true); uint64_t import_key_images(const std::string &filename, uint64_t &spent, uint64_t &unspent); bool import_key_images(std::vector key_images); crypto::public_key get_tx_pub_key_from_received_outs(const tools::wallet2::transfer_details &td) const; @@ -1396,7 +1398,7 @@ namespace tools }; } BOOST_CLASS_VERSION(tools::wallet2, 26) -BOOST_CLASS_VERSION(tools::wallet2::transfer_details, 9) +BOOST_CLASS_VERSION(tools::wallet2::transfer_details, 10) BOOST_CLASS_VERSION(tools::wallet2::multisig_info, 1) BOOST_CLASS_VERSION(tools::wallet2::multisig_info::LR, 0) BOOST_CLASS_VERSION(tools::wallet2::multisig_tx_set, 1) @@ -1454,6 +1456,10 @@ namespace boost x.m_multisig_k.clear(); x.m_multisig_info.clear(); } + if (ver < 10) + { + x.m_key_image_requested = false; + } } template @@ -1535,6 +1541,12 @@ namespace boost a & x.m_multisig_info; a & x.m_multisig_k; a & x.m_key_image_partial; + if (ver < 10) + { + initialize_transfer_details(a, x, ver); + return; + } + a & x.m_key_image_requested; } template diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index 5e6100dfd39..eabdd9a6abe 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -2460,12 +2460,13 @@ namespace tools if (!m_wallet) return not_open(er); try { - std::vector> ski = m_wallet->export_key_images(); - res.signed_key_images.resize(ski.size()); - for (size_t n = 0; n < ski.size(); ++n) + std::pair>> ski = m_wallet->export_key_images(); + res.offset = ski.first; + res.signed_key_images.resize(ski.second.size()); + for (size_t n = 0; n < ski.second.size(); ++n) { - res.signed_key_images[n].key_image = epee::string_tools::pod_to_hex(ski[n].first); - res.signed_key_images[n].signature = epee::string_tools::pod_to_hex(ski[n].second); + res.signed_key_images[n].key_image = epee::string_tools::pod_to_hex(ski.second[n].first); + res.signed_key_images[n].signature = epee::string_tools::pod_to_hex(ski.second[n].second); } } @@ -2518,7 +2519,7 @@ namespace tools ski[n].second = *reinterpret_cast(bd.data()); } uint64_t spent = 0, unspent = 0; - uint64_t height = m_wallet->import_key_images(ski, spent, unspent); + uint64_t height = m_wallet->import_key_images(ski, req.offset, spent, unspent); res.spent = spent; res.unspent = unspent; res.height = height; diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h index 924f3a0f176..026b75a9e2c 100644 --- a/src/wallet/wallet_rpc_server_commands_defs.h +++ b/src/wallet/wallet_rpc_server_commands_defs.h @@ -47,7 +47,7 @@ // advance which version they will stop working with // Don't go over 32767 for any of these #define WALLET_RPC_VERSION_MAJOR 1 -#define WALLET_RPC_VERSION_MINOR 5 +#define WALLET_RPC_VERSION_MINOR 6 #define MAKE_WALLET_RPC_VERSION(major,minor) (((major)<<16)|(minor)) #define WALLET_RPC_VERSION MAKE_WALLET_RPC_VERSION(WALLET_RPC_VERSION_MAJOR, WALLET_RPC_VERSION_MINOR) namespace tools @@ -1579,9 +1579,11 @@ namespace wallet_rpc struct response { + uint32_t offset; std::vector signed_key_images; BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(offset); KV_SERIALIZE(signed_key_images); END_KV_SERIALIZE_MAP() }; @@ -1602,9 +1604,11 @@ namespace wallet_rpc struct request { + uint32_t offset; std::vector signed_key_images; BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_OPT(offset, (uint32_t)0); KV_SERIALIZE(signed_key_images); END_KV_SERIALIZE_MAP() }; diff --git a/tests/data/fuzz/cold-outputs/OUTPUTS2 b/tests/data/fuzz/cold-outputs/OUTPUTS2 index 907bcdb913d7c4cd244f1ac8a18fd812db5869d8..33cf39024f6bd7c087fd01a35a9568d8304b724a 100644 GIT binary patch literal 581 zcmV-L0=oT8Z*FCBZy;}VbZ~WaAZ2)PZ*p`5G4tp3cqZ(Q5}Lcq2LyX_vmER9<1fN# z;*B`&%2cAssqBhs7ekHk$|^si5j`U``G{LIVivfC8iZw}9~KF$kF4DmoU&PRJHU!& zS;G|gMb_d#19^;aZ+Y037ZNTANPHS+DoA>h23FQyh;r!auZUcZxi(X7heD@2@iW+w z0SK=Pl3EVpvuA=kZpg`Rk~l*!oHqxWwjuL{VWmn=m>JUwv!8>@pDE6H*@*#|ZM$R$-vX9~+^G5~+%jjzW;{_#A-5$@=t#1a* z!+531rB9Y8w7fu48`;L|1XuYsFVDEGhu}ZXn{+s{_{(T5YkX0RX|q2;LY0iQ*ohQX zGA3j4jzP8NWF*EDA*$Di2 literal 256 zcmZQzU|?WmWMpSxWM{4q;bUI*mI)-n#JoF%46;)#V-V~ikQu5!sS8sy! z>^zWVQ1<}UV+U~8F)%8WoR}1N$H(Wnqxq3V8G?7I^sZsxql`hnhN GWB>qDdP|}J diff --git a/tests/fuzz/cold-outputs.cpp b/tests/fuzz/cold-outputs.cpp index 488a3b93118..29b3ed267ae 100644 --- a/tests/fuzz/cold-outputs.cpp +++ b/tests/fuzz/cold-outputs.cpp @@ -77,7 +77,7 @@ int ColdOutputsFuzzer::run(const std::string &filename) s = std::string("\x01\x16serialization::archive") + s; try { - std::vector outputs; + std::pair> outputs; std::stringstream iss; iss << s; boost::archive::portable_binary_iarchive ar(iss); diff --git a/tests/unit_tests/serialization.cpp b/tests/unit_tests/serialization.cpp index 2f7b5aac76b..e1404d637cc 100644 --- a/tests/unit_tests/serialization.cpp +++ b/tests/unit_tests/serialization.cpp @@ -908,9 +908,21 @@ TEST(Serialization, portability_outputs) ASSERT_TRUE(td2.m_pk_index == 0); } +struct unsigned_tx_set +{ + std::vector txes; + tools::wallet2::transfer_container transfers; +}; +template +inline void serialize(Archive &a, unsigned_tx_set &x, const boost::serialization::version_type ver) +{ + a & x.txes; + a & x.transfers; +} #define UNSIGNED_TX_PREFIX "Monero unsigned tx set\003" TEST(Serialization, portability_unsigned_tx) { + const boost::filesystem::path filename = unit_test::data_dir / "unsigned_monero_tx"; std::string s; const cryptonote::network_type nettype = cryptonote::TESTNET; @@ -918,7 +930,7 @@ TEST(Serialization, portability_unsigned_tx) ASSERT_TRUE(r); const size_t magiclen = strlen(UNSIGNED_TX_PREFIX); ASSERT_FALSE(strncmp(s.c_str(), UNSIGNED_TX_PREFIX, magiclen)); - tools::wallet2::unsigned_tx_set exported_txs; + unsigned_tx_set exported_txs; s = s.substr(magiclen); r = false; try From 5f614ba968388ae231579c392f8e793d569b9b3d Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 24 Oct 2018 13:23:44 +0000 Subject: [PATCH 0727/1404] simplewallet: print the number of show/all transfers --- src/simplewallet/simplewallet.cpp | 8 ++++++-- tests/unit_tests/serialization.cpp | 1 - 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index babc59ea3c2..f54f3f12971 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -4483,7 +4483,7 @@ bool simple_wallet::show_incoming_transfers(const std::vector& args tools::wallet2::transfer_container transfers; m_wallet->get_transfers(transfers); - bool transfers_found = false; + size_t transfers_found = 0; for (const auto& td : transfers) { if (!filter || available != td.m_spent) @@ -4496,7 +4496,6 @@ bool simple_wallet::show_incoming_transfers(const std::vector& args if (verbose) verbose_string = (boost::format("%68s%68s") % tr("pubkey") % tr("key image")).str(); message_writer() << boost::format("%21s%8s%12s%8s%16s%68s%16s%s") % tr("amount") % tr("spent") % tr("unlocked") % tr("ringct") % tr("global index") % tr("tx id") % tr("addr index") % verbose_string; - transfers_found = true; } std::string verbose_string; if (verbose) @@ -4511,6 +4510,7 @@ bool simple_wallet::show_incoming_transfers(const std::vector& args td.m_txid % td.m_subaddr_index.minor % verbose_string; + ++transfers_found; } } @@ -4529,6 +4529,10 @@ bool simple_wallet::show_incoming_transfers(const std::vector& args success_msg_writer() << tr("No incoming unavailable transfers"); } } + else + { + success_msg_writer() << boost::format("Found %u/%u transfers") % transfers_found % transfers.size(); + } return true; } diff --git a/tests/unit_tests/serialization.cpp b/tests/unit_tests/serialization.cpp index e1404d637cc..b4517af2f58 100644 --- a/tests/unit_tests/serialization.cpp +++ b/tests/unit_tests/serialization.cpp @@ -922,7 +922,6 @@ inline void serialize(Archive &a, unsigned_tx_set &x, const boost::serialization #define UNSIGNED_TX_PREFIX "Monero unsigned tx set\003" TEST(Serialization, portability_unsigned_tx) { - const boost::filesystem::path filename = unit_test::data_dir / "unsigned_monero_tx"; std::string s; const cryptonote::network_type nettype = cryptonote::TESTNET; From e178bf234ac78a689db0f9d1bec64dca4d41c350 Mon Sep 17 00:00:00 2001 From: Dusan Klinec Date: Sun, 4 Nov 2018 22:17:04 +0100 Subject: [PATCH 0728/1404] rpc: fix linking error of 6097472a, get_output_distribution Undefined symbols for architecture x86_64: "cryptonote::core::get_output_distribution(unsigned long long, unsigned long long, unsigned long long, unsigned long long&, std::__1::vector >&, unsigned long long&) const", referenced from: cryptonote::rpc::RpcHandler::get_output_distribution(cryptonote::core&, unsigned long long, unsigned long long, unsigned long long, bool) in rpc_handler.cpp.o --- src/rpc/CMakeLists.txt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/rpc/CMakeLists.txt b/src/rpc/CMakeLists.txt index 8b4c27e3ec4..8fc42b7e3bb 100644 --- a/src/rpc/CMakeLists.txt +++ b/src/rpc/CMakeLists.txt @@ -27,11 +27,11 @@ # THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. set(rpc_base_sources - rpc_args.cpp - rpc_handler.cpp) + rpc_args.cpp) set(rpc_sources core_rpc_server.cpp + rpc_handler.cpp instanciations) set(daemon_messages_sources @@ -44,10 +44,10 @@ set(daemon_rpc_server_sources set(rpc_base_headers - rpc_args.h - rpc_handler.h) + rpc_args.h) -set(rpc_headers) +set(rpc_headers + rpc_handler.cpp) set(daemon_rpc_server_headers) From b3067962faf5b2863fc9e72a603a65bd2e5aa55f Mon Sep 17 00:00:00 2001 From: xiphon Date: Mon, 5 Nov 2018 00:13:51 +0000 Subject: [PATCH 0729/1404] cryptonote_core: avoid gratuitous recalculations in check_block_rate() --- src/cryptonote_core/cryptonote_core.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 4b806c28279..c405c996a1e 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -1716,7 +1716,8 @@ namespace cryptonote for (size_t n = 0; n < sizeof(seconds)/sizeof(seconds[0]); ++n) { unsigned int b = 0; - for (time_t ts: timestamps) b += ts >= now - static_cast(seconds[n]); + const time_t time_boundary = now - static_cast(seconds[n]); + for (time_t ts: timestamps) b += ts >= time_boundary; const double p = probability(b, seconds[n] / DIFFICULTY_TARGET_V2); MDEBUG("blocks in the last " << seconds[n] / 60 << " minutes: " << b << " (probability " << p << ")"); if (p < threshold) From f49884543ce77a1bf132653fe1346d1603a4ea64 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 5 Nov 2018 00:34:16 +0000 Subject: [PATCH 0730/1404] perf_timer: remove stray debug addition --- src/common/perf_timer.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/common/perf_timer.h b/src/common/perf_timer.h index 1d4dee5b553..584434a0da0 100644 --- a/src/common/perf_timer.h +++ b/src/common/perf_timer.h @@ -53,7 +53,6 @@ class PerformanceTimer void resume(); uint64_t value() const { return ticks; } -void set(uint64_t v){ticks=v;} protected: uint64_t ticks; From 7ae36e91f6ce75ec24b3a25478992b3a71f9ee35 Mon Sep 17 00:00:00 2001 From: stoffu Date: Mon, 5 Nov 2018 12:13:15 +0900 Subject: [PATCH 0731/1404] wallet_rpc_server: account for watch-only/non-deterministic/multisig when querying seed Followup on #4653 --- src/wallet/wallet_rpc_server.cpp | 33 +++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index 5e6100dfd39..bec0a3eeb05 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -1742,11 +1742,42 @@ namespace tools if (req.key_type.compare("mnemonic") == 0) { epee::wipeable_string seed; - if (!m_wallet->get_seed(seed)) + bool ready; + if (m_wallet->multisig(&ready)) { + if (!ready) + { + er.code = WALLET_RPC_ERROR_CODE_NOT_MULTISIG; + er.message = "This wallet is multisig, but not yet finalized"; + return false; + } + if (!m_wallet->get_multisig_seed(seed)) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "Failed to get multisig seed."; + return false; + } + } + else + { + if (m_wallet->watch_only()) + { + er.code = WALLET_RPC_ERROR_CODE_WATCH_ONLY; + er.message = "The wallet is watch-only. Cannot display seed."; + return false; + } + if (!m_wallet->is_deterministic()) + { er.code = WALLET_RPC_ERROR_CODE_NON_DETERMINISTIC; er.message = "The wallet is non-deterministic. Cannot display seed."; return false; + } + if (!m_wallet->get_seed(seed)) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "Failed to get seed."; + return false; + } } res.key = std::string(seed.data(), seed.size()); // send to the network, then wipe RAM :D } From fa3f756bfb4f0595bd2dbe0e22a9d2f00f4a0ef5 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 5 Nov 2018 11:15:02 +0000 Subject: [PATCH 0732/1404] unit_tests: fix leak when the test check fails Coverity 189527 --- tests/unit_tests/notify.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unit_tests/notify.cpp b/tests/unit_tests/notify.cpp index 4daeeddee2e..edc4eabdf0b 100644 --- a/tests/unit_tests/notify.cpp +++ b/tests/unit_tests/notify.cpp @@ -49,7 +49,8 @@ TEST(notify, works) tmp = "/tmp"; static const char *filename = "monero-notify-unit-test-XXXXXX"; const size_t len = strlen(tmp) + 1 + strlen(filename); - char *name_template = (char*)malloc(len + 1); + std::unique_ptr name_template_((char*)malloc(len + 1)); + char *name_template = name_template_.get(); ASSERT_TRUE(name_template != NULL); snprintf(name_template, len + 1, "%s/%s", tmp, filename); int fd = mkstemp(name_template); @@ -75,5 +76,4 @@ TEST(notify, works) ASSERT_TRUE(s == "1111111111111111111111111111111111111111111111111111111111111111"); boost::filesystem::remove(name_template); - free(name_template); } From 00907c3987af00d6eb8ff7144a184a74906a8edf Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 5 Nov 2018 14:23:00 +0000 Subject: [PATCH 0733/1404] rct: speedup commit a little saves a conversion, and uses a double scalarmult instead of two scalarmults --- src/ringct/rctOps.cpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/ringct/rctOps.cpp b/src/ringct/rctOps.cpp index 41bbf6ca387..6c0588cf6cb 100644 --- a/src/ringct/rctOps.cpp +++ b/src/ringct/rctOps.cpp @@ -117,8 +117,7 @@ namespace rct { //generates C =aG + bH from b, a is given.. void genC(key & C, const key & a, xmr_amount amount) { - key bH = scalarmultH(d2h(amount)); - addKeys1(C, a, bH); + addKeys2(C, a, d2h(amount), rct::H); } //generates a / Pedersen commitment to the amount @@ -149,10 +148,8 @@ namespace rct { } key commit(xmr_amount amount, const key &mask) { - key c = scalarmultBase(mask); - key am = d2h(amount); - key bH = scalarmultH(am); - addKeys(c, c, bH); + key c; + genC(c, mask, amount); return c; } From 6a1062f56001968dd593757403091cdbec36f2cb Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 5 Nov 2018 14:31:49 +0000 Subject: [PATCH 0734/1404] bulletproofs: reserve vector memory when known in advance --- src/ringct/bulletproofs.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ringct/bulletproofs.cc b/src/ringct/bulletproofs.cc index bed48769a37..1592e74e4a4 100644 --- a/src/ringct/bulletproofs.cc +++ b/src/ringct/bulletproofs.cc @@ -112,6 +112,7 @@ static void init_exponents() if (init_done) return; std::vector data; + data.reserve(maxN*maxM*2); for (size_t i = 0; i < maxN*maxM; ++i) { Hi[i] = get_exponent(rct::H, i * 2); @@ -1056,6 +1057,7 @@ bool bulletproof_VERIFY(const std::vector &proofs) bool bulletproof_VERIFY(const std::vector &proofs) { std::vector proof_pointers; + proof_pointers.reserve(proofs.size()); for (const Bulletproof &proof: proofs) proof_pointers.push_back(&proof); return bulletproof_VERIFY(proof_pointers); From 3b14d9727b6683dc9cd15352254e126e030250d8 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 5 Nov 2018 14:43:35 +0000 Subject: [PATCH 0735/1404] blockchain: use uint64_t for block height, not size_t for consistency --- src/cryptonote_core/blockchain.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 77b6d0b6968..e69ea6b086d 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -853,7 +853,7 @@ difficulty_type Blockchain::get_difficulty_for_next_block() } else { - size_t offset = height - std::min < size_t > (height, static_cast(DIFFICULTY_BLOCKS_COUNT)); + uint64_t offset = height - std::min (height, static_cast(DIFFICULTY_BLOCKS_COUNT)); if (offset == 0) ++offset; From ac23b10f152b483116419418c4cb11849285cf92 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 5 Nov 2018 14:43:58 +0000 Subject: [PATCH 0736/1404] blockchain: fix innocuous difficulty cache inconsistency This inconsistent state would not actually be used in practice --- src/cryptonote_core/blockchain.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index e69ea6b086d..82f8579b197 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -831,6 +831,7 @@ difficulty_type Blockchain::get_difficulty_for_next_block() std::vector timestamps; std::vector difficulties; auto height = m_db->height(); + top_hash = get_tail_id(); // get it again now that we have the lock // ND: Speedup // 1. Keep a list of the last 735 (or less) blocks that is used to compute difficulty, // then when the next block difficulty is queried, push the latest height data and From b620443b08d35f113a91d54cb87704afa53f0836 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 5 Nov 2018 16:13:07 +0000 Subject: [PATCH 0737/1404] epee: log HTTP/RPC calls at info level It's useful info to have when investigating logs --- contrib/epee/include/net/http_server_handlers_map2.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/contrib/epee/include/net/http_server_handlers_map2.h b/contrib/epee/include/net/http_server_handlers_map2.h index 00a867d3e9c..997c801d1e2 100644 --- a/contrib/epee/include/net/http_server_handlers_map2.h +++ b/contrib/epee/include/net/http_server_handlers_map2.h @@ -39,7 +39,7 @@ epee::net_utils::http::http_response_info& response, \ context_type& m_conn_context) \ {\ - LOG_PRINT_L2("HTTP [" << m_conn_context.m_remote_address.host_str() << "] " << query_info.m_http_method_str << " " << query_info.m_URI); \ + MINFO("HTTP [" << m_conn_context.m_remote_address.host_str() << "] " << query_info.m_http_method_str << " " << query_info.m_URI); \ response.m_response_code = 200; \ response.m_response_comment = "Ok"; \ if(!handle_http_request_map(query_info, response, m_conn_context)) \ @@ -68,6 +68,7 @@ CHECK_AND_ASSERT_MES(parse_res, false, "Failed to parse json: \r\n" << query_info.m_body); \ uint64_t ticks1 = epee::misc_utils::get_tick_count(); \ boost::value_initialized resp;\ + MINFO(m_conn_context << "calling " << s_pattern); \ if(!callback_f(static_cast(req), static_cast(resp))) \ { \ LOG_ERROR("Failed to " << #callback_f << "()"); \ @@ -95,6 +96,7 @@ CHECK_AND_ASSERT_MES(parse_res, false, "Failed to parse bin body data, body size=" << query_info.m_body.size()); \ uint64_t ticks1 = misc_utils::get_tick_count(); \ boost::value_initialized resp;\ + MINFO(m_conn_context << "calling " << s_pattern); \ if(!callback_f(static_cast(req), static_cast(resp))) \ { \ LOG_ERROR("Failed to " << #callback_f << "()"); \ @@ -179,6 +181,7 @@ epee::json_rpc::error_response fail_resp = AUTO_VAL_INIT(fail_resp); \ fail_resp.jsonrpc = "2.0"; \ fail_resp.id = req.id; \ + MINFO(m_conn_context << "Calling RPC method " << method_name); \ if(!callback_f(req.params, resp.result, fail_resp.error)) \ { \ epee::serialization::store_t_to_json(static_cast(fail_resp), response_info.m_body); \ @@ -197,6 +200,7 @@ epee::json_rpc::error_response fail_resp = AUTO_VAL_INIT(fail_resp); \ fail_resp.jsonrpc = "2.0"; \ fail_resp.id = req.id; \ + MINFO(m_conn_context << "calling RPC method " << method_name); \ if(!callback_f(req.params, resp.result, fail_resp.error, m_conn_context, response_info)) \ { \ epee::serialization::store_t_to_json(static_cast(fail_resp), response_info.m_body); \ @@ -210,6 +214,7 @@ else if(callback_name == method_name) \ { \ PREPARE_OBJECTS_FROM_JSON(command_type) \ + MINFO(m_conn_context << "calling RPC method " << method_name); \ if(!callback_f(req.params, resp.result)) \ { \ epee::json_rpc::error_response fail_resp = AUTO_VAL_INIT(fail_resp); \ From 6e1282b60043adf7193beb78d7ccc2f11c630748 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 30 Oct 2018 12:16:11 +0000 Subject: [PATCH 0738/1404] wallet2: fix off by one in output picking and take into account wallet level minimum spend age --- src/wallet/wallet2.cpp | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index c27e4e82016..43984073893 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -6814,21 +6814,23 @@ void wallet2::get_outs(std::vector> //static const double shape = m_testnet ? 17.02 : 17.28; static const double scale = 1/1.61; std::gamma_distribution gamma(shape, scale); + THROW_WALLET_EXCEPTION_IF(rct_offsets.size() <= CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE, error::wallet_internal_error, "Bad offset calculation"); + uint64_t last_usable_block = rct_offsets.size() - 1; auto pick_gamma = [&]() { double x = gamma(engine); x = exp(x); uint64_t block_offset = x / DIFFICULTY_TARGET_V2; // this assumes constant target over the whole rct range - if (block_offset >= rct_offsets.size() - 1) + if (block_offset > last_usable_block - CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE) return std::numeric_limits::max(); // bad pick - block_offset = rct_offsets.size() - 2 - block_offset; - THROW_WALLET_EXCEPTION_IF(block_offset >= rct_offsets.size() - 1, error::wallet_internal_error, "Bad offset calculation"); - THROW_WALLET_EXCEPTION_IF(rct_offsets[block_offset + 1] < rct_offsets[block_offset], + block_offset = last_usable_block - block_offset; + THROW_WALLET_EXCEPTION_IF(block_offset > last_usable_block, error::wallet_internal_error, "Bad offset calculation"); + THROW_WALLET_EXCEPTION_IF(block_offset > 0 && rct_offsets[block_offset] < rct_offsets[block_offset - 1], error::get_output_distribution, "Decreasing offsets in rct distribution: " + - std::to_string(block_offset) + ": " + std::to_string(rct_offsets[block_offset]) + ", " + - std::to_string(block_offset + 1) + ": " + std::to_string(rct_offsets[block_offset + 1])); + std::to_string(block_offset - 1) + ": " + std::to_string(rct_offsets[block_offset - 1]) + ", " + + std::to_string(block_offset) + ": " + std::to_string(rct_offsets[block_offset])); uint64_t first_block_offset = block_offset, last_block_offset = block_offset; - for (size_t half_window = 0; half_window < GAMMA_PICK_HALF_WINDOW; ++half_window) + for (size_t half_window = 0; half_window <= GAMMA_PICK_HALF_WINDOW; ++half_window) { // end when we have a non empty block uint64_t cum0 = first_block_offset > 0 ? rct_offsets[first_block_offset] - rct_offsets[first_block_offset - 1] : rct_offsets[0]; @@ -6837,19 +6839,24 @@ void wallet2::get_outs(std::vector> uint64_t cum1 = last_block_offset > 0 ? rct_offsets[last_block_offset] - rct_offsets[last_block_offset - 1] : rct_offsets[0]; if (cum1 > 1) break; - if (first_block_offset == 0 && last_block_offset >= rct_offsets.size() - 2) + if (first_block_offset == 0 && last_block_offset >= last_usable_block) break; // expand up to bounds if (first_block_offset > 0) --first_block_offset; - if (last_block_offset < rct_offsets.size() - 1) + else + return std::numeric_limits::max(); // bad pick + if (last_block_offset < last_usable_block - CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE) ++last_block_offset; + else + return std::numeric_limits::max(); // bad pick } - const uint64_t n_rct = rct_offsets[last_block_offset] - (first_block_offset == 0 ? 0 : rct_offsets[first_block_offset - 1]); + const uint64_t first_rct = first_block_offset == 0 ? 0 : rct_offsets[first_block_offset - 1]; + const uint64_t n_rct = rct_offsets[last_block_offset] - first_rct; if (n_rct == 0) return rct_offsets[block_offset] ? rct_offsets[block_offset] - 1 : 0; - MDEBUG("Picking 1/" << n_rct << " in " << (last_block_offset - first_block_offset + 1) << " blocks centered around " << block_offset); - return rct_offsets[first_block_offset] + crypto::rand() % n_rct; + MDEBUG("Picking 1/" << n_rct << " in " << (last_block_offset - first_block_offset + 1) << " blocks centered around " << block_offset + rct_start_height); + return first_rct + crypto::rand() % n_rct; }; size_t num_selected_transfers = 0; From 1fd017cf7756369aff4ac6b598cf86a130f78e12 Mon Sep 17 00:00:00 2001 From: buricl Date: Tue, 6 Nov 2018 00:17:22 +0100 Subject: [PATCH 0739/1404] Fix build of monero-gui by adding device_trezor to wallet_merged --- src/wallet/CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/wallet/CMakeLists.txt b/src/wallet/CMakeLists.txt index 4932dd4b681..4e3fb1ae531 100644 --- a/src/wallet/CMakeLists.txt +++ b/src/wallet/CMakeLists.txt @@ -120,7 +120,8 @@ if (BUILD_GUI_DEPS) ringct ringct_basic checkpoints - version) + version + device_trezor) foreach(lib ${libs_to_merge}) list(APPEND objlibs $) # matches naming convention in src/CMakeLists.txt From 2587aec121435ade996dbc533eab9503d0fc84fd Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 24 Oct 2018 08:30:33 +0000 Subject: [PATCH 0740/1404] easylogging++: update to latest upstream (v9.96.5) --- external/easylogging++/ea_config.h | 1 + external/easylogging++/easylogging++.cc | 305 +++++++++++++++++------- external/easylogging++/easylogging++.h | 283 ++++++++++------------ 3 files changed, 349 insertions(+), 240 deletions(-) diff --git a/external/easylogging++/ea_config.h b/external/easylogging++/ea_config.h index 4c74925d389..4fb48ce3edd 100644 --- a/external/easylogging++/ea_config.h +++ b/external/easylogging++/ea_config.h @@ -6,6 +6,7 @@ #define ELPP_NO_CHECK_MACROS #define ELPP_WINSOCK2 #define ELPP_NO_DEBUG_MACROS +#define ELPP_UTC_DATETIME #ifdef EASYLOGGING_CC #if !(!defined __GNUC__ || defined __MINGW32__ || defined __MINGW64__ || defined __ANDROID__) diff --git a/external/easylogging++/easylogging++.cc b/external/easylogging++/easylogging++.cc index 1139008e237..d57f3f3a0b1 100644 --- a/external/easylogging++/easylogging++.cc +++ b/external/easylogging++/easylogging++.cc @@ -1,13 +1,14 @@ // // Bismillah ar-Rahmaan ar-Raheem // -// Easylogging++ v9.94.1 +// Easylogging++ v9.96.5 // Cross-platform logging library for C++ applications // -// Copyright (c) 2017 muflihun.com +// Copyright (c) 2012-2018 Muflihun Labs +// Copyright (c) 2012-2018 @abumusamq // // This library is released under the MIT Licence. -// http://labs.muflihun.com/easyloggingpp/licence.php +// https://github.com/muflihun/easyloggingpp/blob/master/LICENSE // // https://github.com/muflihun/easyloggingpp // https://muflihun.github.io/easyloggingpp @@ -25,8 +26,95 @@ INITIALIZE_EASYLOGGINGPP namespace el { -// el::base::utils +// el::base namespace base { +// el::base::consts +namespace consts { + +// Level log values - These are values that are replaced in place of %level format specifier +// Extra spaces after format specifiers are only for readability purposes in log files +static const base::type::char_t* kInfoLevelLogValue = ELPP_LITERAL("INFO"); +static const base::type::char_t* kDebugLevelLogValue = ELPP_LITERAL("DEBUG"); +static const base::type::char_t* kWarningLevelLogValue = ELPP_LITERAL("WARNING"); +static const base::type::char_t* kErrorLevelLogValue = ELPP_LITERAL("ERROR"); +static const base::type::char_t* kFatalLevelLogValue = ELPP_LITERAL("FATAL"); +static const base::type::char_t* kVerboseLevelLogValue = + ELPP_LITERAL("VERBOSE"); // will become VERBOSE-x where x = verbose level +static const base::type::char_t* kTraceLevelLogValue = ELPP_LITERAL("TRACE"); +static const base::type::char_t* kInfoLevelShortLogValue = ELPP_LITERAL("I"); +static const base::type::char_t* kDebugLevelShortLogValue = ELPP_LITERAL("D"); +static const base::type::char_t* kWarningLevelShortLogValue = ELPP_LITERAL("W"); +static const base::type::char_t* kErrorLevelShortLogValue = ELPP_LITERAL("E"); +static const base::type::char_t* kFatalLevelShortLogValue = ELPP_LITERAL("F"); +static const base::type::char_t* kVerboseLevelShortLogValue = ELPP_LITERAL("V"); +static const base::type::char_t* kTraceLevelShortLogValue = ELPP_LITERAL("T"); +// Format specifiers - These are used to define log format +static const base::type::char_t* kAppNameFormatSpecifier = ELPP_LITERAL("%app"); +static const base::type::char_t* kLoggerIdFormatSpecifier = ELPP_LITERAL("%logger"); +static const base::type::char_t* kThreadIdFormatSpecifier = ELPP_LITERAL("%thread"); +static const base::type::char_t* kSeverityLevelFormatSpecifier = ELPP_LITERAL("%level"); +static const base::type::char_t* kSeverityLevelShortFormatSpecifier = ELPP_LITERAL("%levshort"); +static const base::type::char_t* kDateTimeFormatSpecifier = ELPP_LITERAL("%datetime"); +static const base::type::char_t* kLogFileFormatSpecifier = ELPP_LITERAL("%file"); +static const base::type::char_t* kLogFileBaseFormatSpecifier = ELPP_LITERAL("%fbase"); +static const base::type::char_t* kLogLineFormatSpecifier = ELPP_LITERAL("%line"); +static const base::type::char_t* kLogLocationFormatSpecifier = ELPP_LITERAL("%loc"); +static const base::type::char_t* kLogFunctionFormatSpecifier = ELPP_LITERAL("%func"); +static const base::type::char_t* kCurrentUserFormatSpecifier = ELPP_LITERAL("%user"); +static const base::type::char_t* kCurrentHostFormatSpecifier = ELPP_LITERAL("%host"); +static const base::type::char_t* kMessageFormatSpecifier = ELPP_LITERAL("%msg"); +static const base::type::char_t* kVerboseLevelFormatSpecifier = ELPP_LITERAL("%vlevel"); +static const char* kDateTimeFormatSpecifierForFilename = "%datetime"; +// Date/time +static const char* kDays[7] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" }; +static const char* kDaysAbbrev[7] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; +static const char* kMonths[12] = { "January", "February", "March", "Apri", "May", "June", "July", "August", + "September", "October", "November", "December" + }; +static const char* kMonthsAbbrev[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; +static const char* kDefaultDateTimeFormat = "%Y-%M-%d %H:%m:%s,%g"; +static const char* kDefaultDateTimeFormatInFilename = "%Y-%M-%d_%H-%m"; +static const int kYearBase = 1900; +static const char* kAm = "AM"; +static const char* kPm = "PM"; +// Miscellaneous constants + +static const char* kNullPointer = "nullptr"; +#if ELPP_VARIADIC_TEMPLATES_SUPPORTED +#endif // ELPP_VARIADIC_TEMPLATES_SUPPORTED +static const base::type::VerboseLevel kMaxVerboseLevel = 9; +static const char* kUnknownUser = "user"; +static const char* kUnknownHost = "unknown-host"; + + +//---------------- DEFAULT LOG FILE ----------------------- + +#if defined(ELPP_NO_DEFAULT_LOG_FILE) +# if ELPP_OS_UNIX +static const char* kDefaultLogFile = "/dev/null"; +# elif ELPP_OS_WINDOWS +static const char* kDefaultLogFile = "nul"; +# endif // ELPP_OS_UNIX +#elif defined(ELPP_DEFAULT_LOG_FILE) +static const char* kDefaultLogFile = ELPP_DEFAULT_LOG_FILE; +#else +static const char* kDefaultLogFile = "myeasylog.log"; +#endif // defined(ELPP_NO_DEFAULT_LOG_FILE) + + +#if !defined(ELPP_DISABLE_LOG_FILE_FROM_ARG) +static const char* kDefaultLogFileParam = "--default-log-file"; +#endif // !defined(ELPP_DISABLE_LOG_FILE_FROM_ARG) +#if defined(ELPP_LOGGING_FLAGS_FROM_ARG) +static const char* kLoggingFlagsParam = "--logging-flags"; +#endif // defined(ELPP_LOGGING_FLAGS_FROM_ARG) +static const char* kValidLoggerIdSymbols = + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._"; +static const char* kConfigurationComment = "##"; +static const char* kConfigurationLevel = "*"; +static const char* kConfigurationLoggerId = "--"; +} +// el::base::utils namespace utils { /// @brief Aborts application due with user-defined status @@ -303,11 +391,7 @@ void Configurations::set(Configuration* conf) { void Configurations::setToDefault(void) { setGlobally(ConfigurationType::Enabled, std::string("true"), true); -#if !defined(ELPP_NO_DEFAULT_LOG_FILE) setGlobally(ConfigurationType::Filename, std::string(base::consts::kDefaultLogFile), true); -#else - ELPP_UNUSED(base::consts::kDefaultLogFile); -#endif // !defined(ELPP_NO_DEFAULT_LOG_FILE) #if defined(ELPP_NO_LOG_TO_FILE) setGlobally(ConfigurationType::ToFile, std::string("false"), true); #else @@ -336,9 +420,7 @@ void Configurations::setRemainingToDefault(void) { #else unsafeSetIfNotExist(Level::Global, ConfigurationType::Enabled, std::string("true")); #endif // defined(ELPP_NO_LOG_TO_FILE) -#if !defined(ELPP_NO_DEFAULT_LOG_FILE) unsafeSetIfNotExist(Level::Global, ConfigurationType::Filename, std::string(base::consts::kDefaultLogFile)); -#endif // !defined(ELPP_NO_DEFAULT_LOG_FILE) unsafeSetIfNotExist(Level::Global, ConfigurationType::ToStandardOutput, std::string("true")); unsafeSetIfNotExist(Level::Global, ConfigurationType::SubsecondPrecision, std::string("3")); unsafeSetIfNotExist(Level::Global, ConfigurationType::PerformanceTracking, std::string("true")); @@ -596,7 +678,6 @@ void Logger::configure(const Configurations& configurations) { if (m_typedConfigurations != nullptr) { Configurations* c = const_cast(m_typedConfigurations->configurations()); if (c->hasConfiguration(Level::Global, ConfigurationType::Filename)) { - // This check is definitely needed for cases like ELPP_NO_DEFAULT_LOG_FILE flush(); } } @@ -640,7 +721,11 @@ void Logger::flush(Level level, base::type::fstream_t* fs) { } if (fs != nullptr) { fs->flush(); - m_unflushedCount.find(level)->second = 0; + std::unordered_map::iterator iter = m_unflushedCount.find(level); + if (iter != m_unflushedCount.end()) { + iter->second = 0; + } + Helpers::validateFileRolling(this, level); } } @@ -699,10 +784,9 @@ std::size_t File::getSizeOfFile(base::type::fstream_t* fs) { if (fs == nullptr) { return 0; } - std::streampos currPos = fs->tellg(); - fs->seekg(0, fs->end); + // Since the file stream is appended to or truncated, the current + // offset is the file size. std::size_t size = static_cast(fs->tellg()); - fs->seekg(currPos); return size; } @@ -894,7 +978,10 @@ void Str::replaceFirstWithEscape(base::type::string_t& str, const base::type::st #endif // defined(ELPP_UNICODE) std::string& Str::toUpper(std::string& str) { - std::transform(str.begin(), str.end(), str.begin(), ::toupper); + std::transform(str.begin(), str.end(), str.begin(), + [](char c) { + return static_cast(::toupper(c)); + }); return str; } @@ -1022,11 +1109,13 @@ const std::string OS::getBashOutput(const char* command) { char hBuff[4096]; if (fgets(hBuff, sizeof(hBuff), proc) != nullptr) { pclose(proc); - const size_t len = strlen(hBuff); - if (len > 0 && hBuff[len - 1] == '\n') { - hBuff[len- 1] = '\0'; + const std::size_t buffLen = strlen(hBuff); + if (buffLen > 0 && hBuff[buffLen - 1] == '\n') { + hBuff[buffLen- 1] = '\0'; } return std::string(hBuff); + } else { + pclose(proc); } return std::string(); #else @@ -1172,19 +1261,23 @@ unsigned long long DateTime::getTimeDifference(const struct timeval& endTime, co struct ::tm* DateTime::buildTimeInfo(struct timeval* currTime, struct ::tm* timeInfo) { #if ELPP_OS_UNIX time_t rawTime = currTime->tv_sec; - ::gmtime_r(&rawTime, timeInfo); + ::elpptime_r(&rawTime, timeInfo); return timeInfo; #else # if ELPP_COMPILER_MSVC ELPP_UNUSED(currTime); time_t t; +# if defined(_USE_32BIT_TIME_T) + _time32(&t); +# else _time64(&t); - gmtime_s(timeInfo, &t); +# endif + elpptime_s(timeInfo, &t); return timeInfo; # else // For any other compilers that don't have CRT warnings issue e.g, MinGW or TDM GCC- we use different method time_t rawTime = currTime->tv_sec; - struct tm* tmInf = gmtime(&rawTime); + struct tm* tmInf = elpptime(&rawTime); *timeInfo = *tmInf; return timeInfo; # endif // ELPP_COMPILER_MSVC @@ -1292,7 +1385,8 @@ bool CommandLineArgs::hasParamWithValue(const char* paramKey) const { } const char* CommandLineArgs::getParamValue(const char* paramKey) const { - return m_paramsWithValue.find(std::string(paramKey))->second.c_str(); + std::unordered_map::const_iterator iter = m_paramsWithValue.find(std::string(paramKey)); + return iter != m_paramsWithValue.end() ? iter->second.c_str() : ""; } bool CommandLineArgs::hasParam(const char* paramKey) const { @@ -1641,10 +1735,11 @@ void TypedConfigurations::build(Configurations* configurations) { } else if (conf->configurationType() == ConfigurationType::PerformanceTracking) { setValue(Level::Global, getBool(conf->value()), &m_performanceTrackingMap); } else if (conf->configurationType() == ConfigurationType::MaxLogFileSize) { - setValue(conf->level(), static_cast(getULong(conf->value())), &m_maxLogFileSizeMap); -#if !defined(ELPP_NO_DEFAULT_LOG_FILE) - withFileSizeLimit.push_back(conf); -#endif // !defined(ELPP_NO_DEFAULT_LOG_FILE) + auto v = getULong(conf->value()); + setValue(conf->level(), static_cast(v), &m_maxLogFileSizeMap); + if (v != 0) { + withFileSizeLimit.push_back(conf); + } } else if (conf->configurationType() == ConfigurationType::LogFlushThreshold) { setValue(conf->level(), static_cast(getULong(conf->value())), &m_logFlushThresholdMap); } @@ -1718,12 +1813,6 @@ std::string TypedConfigurations::resolveFilename(const std::string& filename) { } void TypedConfigurations::insertFile(Level level, const std::string& fullFilename) { -#if defined(ELPP_NO_LOG_TO_FILE) - setValue(level, false, &m_toFileMap); - ELPP_UNUSED(fullFilename); - m_fileStreamMap.insert(std::make_pair(level, base::FileStreamPtr(nullptr))); - return; -#endif if (fullFilename.empty()) return; std::string resolvedFilename = resolveFilename(fullFilename); @@ -1861,8 +1950,10 @@ bool RegisteredLoggers::remove(const std::string& id) { if (id == base::consts::kDefaultLoggerId) { return false; } + // get has internal lock Logger* logger = base::utils::Registry::get(id); if (logger != nullptr) { + // unregister has internal lock unregister(logger); } return true; @@ -2066,9 +2157,11 @@ bool VRegistry::allowed(base::type::VerboseLevel vlevel, const char* file) { if (m_modules.empty() || file == nullptr) { return vlevel <= m_level; } else { - std::map::iterator it = m_modules.begin(); + char baseFilename[base::consts::kSourceFilenameMaxLength] = ""; + base::utils::File::buildBaseFilename(file, baseFilename); + std::unordered_map::iterator it = m_modules.begin(); for (; it != m_modules.end(); ++it) { - if (base::utils::Str::wildCardMatch(file, it->first.c_str())) { + if (base::utils::Str::wildCardMatch(baseFilename, it->first.c_str())) { return vlevel <= it->second; } } @@ -2176,7 +2269,7 @@ Storage::~Storage(void) { } bool Storage::hasCustomFormatSpecifier(const char* formatSpecifier) { - base::threading::ScopedLock scopedLock(lock()); + base::threading::ScopedLock scopedLock(customFormatSpecifiersLock()); return std::find(m_customFormatSpecifiers.begin(), m_customFormatSpecifiers.end(), formatSpecifier) != m_customFormatSpecifiers.end(); } @@ -2185,12 +2278,12 @@ void Storage::installCustomFormatSpecifier(const CustomFormatSpecifier& customFo if (hasCustomFormatSpecifier(customFormatSpecifier.formatSpecifier())) { return; } - base::threading::ScopedLock scopedLock(lock()); + base::threading::ScopedLock scopedLock(customFormatSpecifiersLock()); m_customFormatSpecifiers.push_back(customFormatSpecifier); } bool Storage::uninstallCustomFormatSpecifier(const char* formatSpecifier) { - base::threading::ScopedLock scopedLock(lock()); + base::threading::ScopedLock scopedLock(customFormatSpecifiersLock()); std::vector::iterator it = std::find(m_customFormatSpecifiers.begin(), m_customFormatSpecifiers.end(), formatSpecifier); if (it != m_customFormatSpecifiers.end() && strcmp(formatSpecifier, it->formatSpecifier()) == 0) { @@ -2228,9 +2321,35 @@ void Storage::setApplicationArguments(int argc, char** argv) { #endif // defined(ELPP_LOGGING_FLAGS_FROM_ARG) } +} // namespace base + +// LogDispatchCallback +void LogDispatchCallback::handle(const LogDispatchData* data) { +#if defined(ELPP_THREAD_SAFE) + base::threading::ScopedLock scopedLock(m_fileLocksMapLock); + std::string filename = data->logMessage()->logger()->typedConfigurations()->filename(data->logMessage()->level()); + auto lock = m_fileLocks.find(filename); + if (lock == m_fileLocks.end()) { + m_fileLocks.emplace(std::make_pair(filename, std::unique_ptr(new base::threading::Mutex))); + } +#endif +} + +base::threading::Mutex& LogDispatchCallback::fileHandle(const LogDispatchData* data) { + auto it = m_fileLocks.find(data->logMessage()->logger()->typedConfigurations()->filename(data->logMessage()->level())); + return *(it->second.get()); +} + +namespace base { // DefaultLogDispatchCallback void DefaultLogDispatchCallback::handle(const LogDispatchData* data) { +#if defined(ELPP_THREAD_SAFE) +#if 0 + LogDispatchCallback::handle(data); + base::threading::ScopedLock scopedLock(fileHandle(data)); +#endif +#endif m_data = data; dispatch(m_data->logMessage()->logger()->logBuilder()->build(m_data->logMessage(), m_data->dispatchAction() == base::DispatchAction::NormalLog || m_data->dispatchAction() == base::DispatchAction::FileOnlyLog)); @@ -2481,6 +2600,8 @@ base::type::string_t DefaultLogBuilder::build(const LogMessage* logMessage, bool base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kMessageFormatSpecifier, logMessage->message()); } #if !defined(ELPP_DISABLE_CUSTOM_FORMAT_SPECIFIERS) + el::base::threading::ScopedLock lock_(ELPP->customFormatSpecifiersLock()); + ELPP_UNUSED(lock_); for (std::vector::const_iterator it = ELPP->customFormatSpecifiers()->begin(); it != ELPP->customFormatSpecifiers()->end(); ++it) { std::string fs(it->formatSpecifier()); @@ -2501,10 +2622,15 @@ void LogDispatcher::dispatch(void) { if (!m_proceed) { return; } +#ifndef ELPP_NO_GLOBAL_LOCK + // see https://github.com/muflihun/easyloggingpp/issues/580 + // global lock is turned off by default unless + // ELPP_NO_GLOBAL_LOCK is defined base::threading::ScopedLock scopedLock(ELPP->lock()); - base::TypedConfigurations* tc = m_logMessage.logger()->m_typedConfigurations; +#endif + base::TypedConfigurations* tc = m_logMessage->logger()->m_typedConfigurations; if (ELPP->hasFlag(LoggingFlag::StrictLogFileSizeCheck)) { - tc->validateFileRolling(m_logMessage.level(), ELPP->preRollOutCallback()); + tc->validateFileRolling(m_logMessage->level(), ELPP->preRollOutCallback()); } LogDispatchCallback* callback = nullptr; LogDispatchData data; @@ -2512,7 +2638,7 @@ void LogDispatcher::dispatch(void) { : ELPP->m_logDispatchCallbacks) { callback = h.second.get(); if (callback != nullptr && callback->enabled()) { - data.setLogMessage(&m_logMessage); + data.setLogMessage(m_logMessage); data.setDispatchAction(m_dispatchAction); callback->handle(&data); } @@ -2559,6 +2685,7 @@ Writer& Writer::construct(int count, const char* loggerIds, ...) { va_list loggersList; va_start(loggersList, loggerIds); const char* id = loggerIds; + m_loggerIds.reserve(count); for (int i = 0; i < count; ++i) { m_loggerIds.push_back(std::string(id)); id = va_arg(loggersList, const char*); @@ -2577,12 +2704,12 @@ void Writer::initializeLogger(const std::string& loggerId, bool lookup, bool nee m_logger = ELPP->registeredLoggers()->get(loggerId, ELPP->hasFlag(LoggingFlag::CreateLoggerAutomatically)); } if (m_logger == nullptr) { - ELPP->acquireLock(); - if (!ELPP->registeredLoggers()->has(std::string(base::consts::kDefaultLoggerId))) { - // Somehow default logger has been unregistered. Not good! Register again - ELPP->registeredLoggers()->get(std::string(base::consts::kDefaultLoggerId)); + { + if (!ELPP->registeredLoggers()->has(std::string(base::consts::kDefaultLoggerId))) { + // Somehow default logger has been unregistered. Not good! Register again + ELPP->registeredLoggers()->get(std::string(base::consts::kDefaultLoggerId)); + } } - ELPP->releaseLock(); // Need to unlock it for next writer Writer(Level::Debug, m_file, m_line, m_func).construct(1, base::consts::kDefaultLoggerId) << "Logger [" << loggerId << "] is not registered yet!"; m_proceed = false; @@ -2643,8 +2770,13 @@ void Writer::processDispatch() { void Writer::triggerDispatch(void) { if (m_proceed) { - base::LogDispatcher(m_proceed, LogMessage(m_level, m_file, m_line, m_func, m_verboseLevel, - m_logger), m_dispatchAction).dispatch(); + if (m_msg == nullptr) { + LogMessage msg(m_level, m_file, m_line, m_func, m_verboseLevel, + m_logger); + base::LogDispatcher(m_proceed, &msg, m_dispatchAction).dispatch(); + } else { + base::LogDispatcher(m_proceed, m_msg, m_dispatchAction).dispatch(); + } } if (m_logger != nullptr) { m_logger->stream().str(ELPP_LITERAL("")); @@ -2657,7 +2789,7 @@ void Writer::triggerDispatch(void) { std::stringstream reasonStream; reasonStream << "Fatal log at [" << m_file << ":" << m_line << "]" << " If you wish to disable 'abort on fatal log' please use " - << "el::Helpers::addFlag(el::LoggingFlag::DisableApplicationAbortOnFatalLog)"; + << "el::Loggers::addFlag(el::LoggingFlag::DisableApplicationAbortOnFatalLog)"; base::utils::abort(1, reasonStream.str()); } m_proceed = false; @@ -2773,18 +2905,19 @@ namespace debug { // StackTrace -StackTrace::StackTraceEntry::StackTraceEntry(std::size_t index, const char* loc, const char* demang, const char* hex, - const char* addr) { - m_index = index; - m_location = std::string(loc); - m_demangled = std::string(demang); - m_hex = std::string(hex); - m_addr = std::string(addr); +StackTrace::StackTraceEntry::StackTraceEntry(std::size_t index, const std::string& loc, const std::string& demang, + const std::string& hex, + const std::string& addr) : + m_index(index), + m_location(loc), + m_demangled(demang), + m_hex(hex), + m_addr(addr) { } std::ostream& operator<<(std::ostream& ss, const StackTrace::StackTraceEntry& si) { - ss << "[" << si.m_index << "] " << si.m_location << (si.m_demangled.empty() ? "" : ":") << si.m_demangled - << (si.m_hex.empty() ? "" : "+") << si.m_hex << si.m_addr; + ss << "[" << si.m_index << "] " << si.m_location << (si.m_hex.empty() ? "" : "+") << si.m_hex << " " << si.m_addr << + (si.m_demangled.empty() ? "" : ":") << si.m_demangled; return ss; } @@ -2804,44 +2937,40 @@ void StackTrace::generateNew(void) { char** strings = backtrace_symbols(stack, size); if (size > kStackStart) { // Skip StackTrace c'tor and generateNew for (std::size_t i = kStackStart; i < size; ++i) { - char* mangName = nullptr; - char* hex = nullptr; - char* addr = nullptr; - for (char* c = strings[i]; *c; ++c) { - switch (*c) { - case '(': - mangName = c; - break; - case '+': - hex = c; - break; - case ')': - addr = c; - break; - default: - break; - } + std::string mangName; + std::string location; + std::string hex; + std::string addr; + + // entry: 2 crash.cpp.bin 0x0000000101552be5 _ZN2el4base5debug10StackTraceC1Ev + 21 + const std::string line(strings[i]); + auto p = line.find("_"); + if (p != std::string::npos) { + mangName = line.substr(p); + mangName = mangName.substr(0, mangName.find(" +")); + } + p = line.find("0x"); + if (p != std::string::npos) { + addr = line.substr(p); + addr = addr.substr(0, addr.find("_")); } // Perform demangling if parsed properly - if (mangName != nullptr && hex != nullptr && addr != nullptr && mangName < hex) { - *mangName++ = '\0'; - *hex++ = '\0'; - *addr++ = '\0'; + if (!mangName.empty()) { int status = 0; - char* demangName = abi::__cxa_demangle(mangName, 0, 0, &status); + char* demangName = abi::__cxa_demangle(mangName.data(), 0, 0, &status); // if demangling is successful, output the demangled function name if (status == 0) { // Success (see http://gcc.gnu.org/onlinedocs/libstdc++/libstdc++-html-USERS-4.3/a01696.html) - StackTraceEntry entry(i - 1, strings[i], demangName, hex, addr); + StackTraceEntry entry(i - 1, location, demangName, hex, addr); m_stack.push_back(entry); } else { // Not successful - we will use mangled name - StackTraceEntry entry(i - 1, strings[i], mangName, hex, addr); + StackTraceEntry entry(i - 1, location, mangName, hex, addr); m_stack.push_back(entry); } free(demangName); } else { - StackTraceEntry entry(i - 1, strings[i]); + StackTraceEntry entry(i - 1, line); m_stack.push_back(entry); } } @@ -2875,6 +3004,9 @@ static std::string crashReason(int sig) { } /// @brief Logs reason of crash from sig static void logCrashReason(int sig, bool stackTraceIfAvailable, Level level, const char* logger) { + if (sig == SIGINT && ELPP->hasFlag(el::LoggingFlag::IgnoreSigInt)) { + return; + } std::stringstream ss; ss << "CRASH HANDLED; "; ss << crashReason(sig); @@ -2953,7 +3085,6 @@ void Helpers::logCrashReason(int sig, bool stackTraceIfAvailable, Level level, c // Loggers Logger* Loggers::getLogger(const std::string& identity, bool registerIfNotAvailable) { - base::threading::ScopedLock scopedLock(ELPP->lock()); return ELPP->registeredLoggers()->get(identity, registerIfNotAvailable); } @@ -2962,12 +3093,10 @@ void Loggers::setDefaultLogBuilder(el::LogBuilderPtr& logBuilderPtr) { } bool Loggers::unregisterLogger(const std::string& identity) { - base::threading::ScopedLock scopedLock(ELPP->lock()); return ELPP->registeredLoggers()->remove(identity); } bool Loggers::hasLogger(const std::string& identity) { - base::threading::ScopedLock scopedLock(ELPP->lock()); return ELPP->registeredLoggers()->has(identity); } @@ -3137,11 +3266,11 @@ const std::string &Loggers::getFilenameCommonPrefix() { // VersionInfo const std::string VersionInfo::version(void) { - return std::string("9.94.1"); + return std::string("9.96.5"); } /// @brief Release date of current version const std::string VersionInfo::releaseDate(void) { - return std::string("25-02-2017 0813hrs"); + return std::string("07-09-2018 0950hrs"); } } // namespace el diff --git a/external/easylogging++/easylogging++.h b/external/easylogging++/easylogging++.h index 8bf94c546b2..046252a5b3e 100644 --- a/external/easylogging++/easylogging++.h +++ b/external/easylogging++/easylogging++.h @@ -1,18 +1,20 @@ // // Bismillah ar-Rahmaan ar-Raheem // -// Easylogging++ v9.94.1 +// Easylogging++ v9.96.5 // Single-header only, cross-platform logging library for C++ applications // -// Copyright (c) 2017 muflihun.com +// Copyright (c) 2012-2018 Muflihun Labs +// Copyright (c) 2012-2018 @abumusamq // // This library is released under the MIT Licence. -// http://labs.muflihun.com/easyloggingpp/licence.php +// https://github.com/muflihun/easyloggingpp/blob/master/LICENSE // // https://github.com/muflihun/easyloggingpp // https://muflihun.github.io/easyloggingpp // http://muflihun.com // + #ifndef EASYLOGGINGPP_H #define EASYLOGGINGPP_H #include "ea_config.h" @@ -94,7 +96,7 @@ #else # define ELPP_OS_MAC 0 #endif -#if (defined(__FreeBSD__)) +#if (defined(__FreeBSD__) || defined(__FreeBSD_kernel__)) # define ELPP_OS_FREEBSD 1 #else # define ELPP_OS_FREEBSD 0 @@ -114,13 +116,23 @@ #else # define ELPP_OS_SOLARIS 0 #endif +#if (defined(_AIX)) +# define ELPP_OS_AIX 1 +#else +# define ELPP_OS_AIX 0 +#endif +#if (defined(__NetBSD__)) +# define ELPP_OS_NETBSD 1 +#else +# define ELPP_OS_NETBSD 0 +#endif #if (defined(__DragonFly__)) # define ELPP_OS_DRAGONFLY 1 #else # define ELPP_OS_DRAGONFLY 0 #endif // Unix -#if ((ELPP_OS_LINUX || ELPP_OS_MAC || ELPP_OS_FREEBSD || ELPP_OS_SOLARIS || ELPP_OS_DRAGONFLY || ELPP_OS_OPENBSD || ELPP_OS_NETBSD ) && (!ELPP_OS_WINDOWS)) +#if ((ELPP_OS_LINUX || ELPP_OS_MAC || ELPP_OS_FREEBSD || ELPP_OS_NETBSD || ELPP_OS_SOLARIS || ELPP_OS_AIX || ELPP_OS_DRAGONFLY || ELPP_OS_OPENBSD) && (!ELPP_OS_WINDOWS)) # define ELPP_OS_UNIX 1 #else # define ELPP_OS_UNIX 0 @@ -205,7 +217,7 @@ ELPP_INTERNAL_DEBUGGING_OUT_INFO << ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStre # define ELPP_INTERNAL_INFO(lvl, msg) #endif // (defined(ELPP_DEBUG_INFO)) #if (defined(ELPP_FEATURE_ALL)) || (defined(ELPP_FEATURE_CRASH_LOG)) -# if (ELPP_COMPILER_GCC && !ELPP_MINGW && !ELPP_OS_OPENBSD && !ELPP_OS_NETBSD) +# if (ELPP_COMPILER_GCC && !ELPP_MINGW && !ELPP_OS_OPENBSD && !ELPP_OS_NETBSD && !ELPP_OS_ANDROID) # define ELPP_STACKTRACE 1 # else # define ELPP_STACKTRACE 0 @@ -386,6 +398,7 @@ ELPP_INTERNAL_DEBUGGING_OUT_INFO << ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStre #include #include #include +#include #include #include #include @@ -423,9 +436,6 @@ ELPP_INTERNAL_DEBUGGING_OUT_INFO << ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStre # if defined(ELPP_LOG_STD_ARRAY) # include # endif // defined(ELPP_LOG_STD_ARRAY) -# if defined(ELPP_LOG_UNORDERED_MAP) -# include -# endif // defined(ELPP_LOG_UNORDERED_MAP) # if defined(ELPP_LOG_UNORDERED_SET) # include # endif // defined(ELPP_UNORDERED_SET) @@ -460,6 +470,15 @@ ELPP_INTERNAL_DEBUGGING_OUT_INFO << ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStre // For logging wxWidgets based classes & templates # include #endif // defined(ELPP_WXWIDGETS_LOGGING) +#if defined(ELPP_UTC_DATETIME) +# define elpptime_r gmtime_r +# define elpptime_s gmtime_s +# define elpptime gmtime +#else +# define elpptime_r localtime_r +# define elpptime_s localtime_s +# define elpptime localtime +#endif // defined(ELPP_UTC_DATETIME) // Forward declarations namespace el { class Logger; @@ -584,6 +603,16 @@ enum class Level : base::type::EnumType { /// @brief Represents unknown level Unknown = 1010 }; +} // namespace el +namespace std { +template<> struct hash { + public: + std::size_t operator()(const el::Level& l) const { + return hash {}(static_cast(l)); + } +}; +} +namespace el { /// @brief Static class that contains helper functions for el::Level class LevelHelper : base::StaticClass { public: @@ -710,113 +739,41 @@ enum class LoggingFlag : base::type::EnumType { /// @brief Adds spaces b/w logs that separated by left-shift operator AutoSpacing = 8192, /// @brief Preserves time format and does not convert it to sec, hour etc (performance tracking only) - FixedTimeFormat = 16384 + FixedTimeFormat = 16384, + // @brief Ignore SIGINT or crash + IgnoreSigInt = 32768, }; namespace base { /// @brief Namespace containing constants used internally. namespace consts { -#if defined(__GNUC__) && !defined(__clang__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wunused-variable" -#endif -// Level log values - These are values that are replaced in place of %level format specifier -static const base::type::char_t* kInfoLevelLogValue = ELPP_LITERAL("INFO "); -static const base::type::char_t* kDebugLevelLogValue = ELPP_LITERAL("DEBUG"); -static const base::type::char_t* kWarningLevelLogValue = ELPP_LITERAL("WARN "); -static const base::type::char_t* kErrorLevelLogValue = ELPP_LITERAL("ERROR"); -static const base::type::char_t* kFatalLevelLogValue = ELPP_LITERAL("FATAL"); -static const base::type::char_t* kVerboseLevelLogValue = ELPP_LITERAL("VER"); -static const base::type::char_t* kTraceLevelLogValue = ELPP_LITERAL("TRACE"); -static const base::type::char_t* kInfoLevelShortLogValue = ELPP_LITERAL("I"); -static const base::type::char_t* kDebugLevelShortLogValue = ELPP_LITERAL("D"); -static const base::type::char_t* kWarningLevelShortLogValue = ELPP_LITERAL("W"); -static const base::type::char_t* kErrorLevelShortLogValue = ELPP_LITERAL("E"); -static const base::type::char_t* kFatalLevelShortLogValue = ELPP_LITERAL("F"); -static const base::type::char_t* kVerboseLevelShortLogValue = ELPP_LITERAL("V"); -static const base::type::char_t* kTraceLevelShortLogValue = ELPP_LITERAL("T"); -// Format specifiers - These are used to define log format -static const base::type::char_t* kAppNameFormatSpecifier = ELPP_LITERAL("%app"); -static const base::type::char_t* kLoggerIdFormatSpecifier = ELPP_LITERAL("%logger"); -static const base::type::char_t* kThreadIdFormatSpecifier = ELPP_LITERAL("%thread"); -static const base::type::char_t* kSeverityLevelFormatSpecifier = ELPP_LITERAL("%level"); -static const base::type::char_t* kSeverityLevelShortFormatSpecifier = ELPP_LITERAL("%levshort"); -static const base::type::char_t* kDateTimeFormatSpecifier = ELPP_LITERAL("%datetime"); -static const base::type::char_t* kLogFileFormatSpecifier = ELPP_LITERAL("%file"); -static const base::type::char_t* kLogFileBaseFormatSpecifier = ELPP_LITERAL("%fbase"); -static const base::type::char_t* kLogLineFormatSpecifier = ELPP_LITERAL("%line"); -static const base::type::char_t* kLogLocationFormatSpecifier = ELPP_LITERAL("%loc"); -static const base::type::char_t* kLogFunctionFormatSpecifier = ELPP_LITERAL("%func"); -static const base::type::char_t* kCurrentUserFormatSpecifier = ELPP_LITERAL("%user"); -static const base::type::char_t* kCurrentHostFormatSpecifier = ELPP_LITERAL("%host"); -static const base::type::char_t* kMessageFormatSpecifier = ELPP_LITERAL("%msg"); -static const base::type::char_t* kVerboseLevelFormatSpecifier = ELPP_LITERAL("%vlevel"); -static const char* kDateTimeFormatSpecifierForFilename = "%datetime"; -// Date/time -static const char* kDays[7] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" }; -static const char* kDaysAbbrev[7] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; -static const char* kMonths[12] = { "January", "February", "March", "Apri", "May", "June", "July", "August", - "September", "October", "November", "December" - }; -static const char* kMonthsAbbrev[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; -static const char* kDefaultDateTimeFormat = "%Y-%M-%d %H:%m:%s,%g"; -static const char* kDefaultDateTimeFormatInFilename = "%Y-%M-%d_%H-%m"; -static const int kYearBase = 1900; -static const char* kAm = "AM"; -static const char* kPm = "PM"; -// Miscellaneous constants +static const char kFormatSpecifierCharValue = 'v'; +static const char kFormatSpecifierChar = '%'; +static const unsigned int kMaxLogPerCounter = 100000; +static const unsigned int kMaxLogPerContainer = 100; +static const unsigned int kDefaultSubsecondPrecision = 3; + #ifdef ELPP_DEFAULT_LOGGER static const char* kDefaultLoggerId = ELPP_DEFAULT_LOGGER; #else static const char* kDefaultLoggerId = "default"; #endif + #ifdef ELPP_DEFAULT_PERFORMANCE_LOGGER static const char* kPerformanceLoggerId = ELPP_DEFAULT_PERFORMANCE_LOGGER; #else static const char* kPerformanceLoggerId = "performance"; #endif + #if defined(ELPP_SYSLOG) static const char* kSysLogLoggerId = "syslog"; #endif // defined(ELPP_SYSLOG) -static const char* kNullPointer = "nullptr"; -static const char kFormatSpecifierChar = '%'; -#if ELPP_VARIADIC_TEMPLATES_SUPPORTED -static const char kFormatSpecifierCharValue = 'v'; -#endif // ELPP_VARIADIC_TEMPLATES_SUPPORTED -static const unsigned int kMaxLogPerContainer = 100; -static const unsigned int kMaxLogPerCounter = 100000; -static const unsigned int kDefaultSubsecondPrecision = 3; -static const base::type::VerboseLevel kMaxVerboseLevel = 9; -static const char* kUnknownUser = "user"; -static const char* kUnknownHost = "unknown-host"; -#if defined(ELPP_DEFAULT_LOG_FILE) -static const char* kDefaultLogFile = ELPP_DEFAULT_LOG_FILE; -#else -# if ELPP_OS_UNIX -# if ELPP_OS_ANDROID -static const char* kDefaultLogFile = "logs/myeasylog.log"; -# else -static const char* kDefaultLogFile = "logs/myeasylog.log"; -# endif // ELPP_OS_ANDROID -# elif ELPP_OS_WINDOWS -static const char* kDefaultLogFile = "logs\\myeasylog.log"; -# endif // ELPP_OS_UNIX -#endif // defined(ELPP_DEFAULT_LOG_FILE) -#if !defined(ELPP_DISABLE_LOG_FILE_FROM_ARG) -static const char* kDefaultLogFileParam = "--default-log-file"; -#endif // !defined(ELPP_DISABLE_LOG_FILE_FROM_ARG) -#if defined(ELPP_LOGGING_FLAGS_FROM_ARG) -static const char* kLoggingFlagsParam = "--logging-flags"; -#endif // defined(ELPP_LOGGING_FLAGS_FROM_ARG) + #if ELPP_OS_WINDOWS static const char* kFilePathSeperator = "\\"; #else static const char* kFilePathSeperator = "/"; #endif // ELPP_OS_WINDOWS -static const char* kValidLoggerIdSymbols = - "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._"; -static const char* kConfigurationComment = "##"; -static const char* kConfigurationLevel = "*"; -static const char* kConfigurationLoggerId = "--"; + static const std::size_t kSourceFilenameMaxLength = 100; static const std::size_t kSourceLineMaxLength = 10; static const Level kPerformanceTrackerDefaultLevel = Level::Info; @@ -861,9 +818,6 @@ const struct { }, }; static const int kCrashSignalsCount = sizeof(kCrashSignals) / sizeof(kCrashSignals[0]); -#if defined(__GNUC__) && !defined(__clang__) -#pragma GCC diagnostic pop -#endif } // namespace consts } // namespace base typedef std::function PreRollOutCallback; @@ -1270,8 +1224,8 @@ class DateTime : base::StaticClass { base::TimestampUnit timestampUnit); - private: static struct ::tm* buildTimeInfo(struct timeval* currTime, struct ::tm* timeInfo); + private: static char* parseFormat(char* buf, std::size_t bufSz, const char* format, const struct tm* tInfo, std::size_t msec, const base::SubsecondPrecision* ssPrec); }; @@ -1310,7 +1264,7 @@ class CommandLineArgs { private: int m_argc; char** m_argv; - std::map m_paramsWithValue; + std::unordered_map m_paramsWithValue; std::vector m_params; }; /// @brief Abstract registry (aka repository) that provides basic interface for pointer repository specified by T_Ptr type. @@ -1435,7 +1389,7 @@ class AbstractRegistry : public base::threading::ThreadSafe { /// of AbstractRegistry. Any implementation of this class should be /// explicitly (by using lock functions) template -class Registry : public AbstractRegistry> { +class Registry : public AbstractRegistry> { public: typedef typename Registry::iterator iterator; typedef typename Registry::const_iterator const_iterator; @@ -1485,8 +1439,8 @@ class Registry : public AbstractRegistry> { void unregister(const T_Key& uniqKey) { T_Ptr* existing = get(uniqKey); if (existing != nullptr) { - base::utils::safeDelete(existing); this->list().erase(uniqKey); + base::utils::safeDelete(existing); } } @@ -1499,7 +1453,7 @@ class Registry : public AbstractRegistry> { } private: - virtual void deepCopy(const AbstractRegistry>& sr) ELPP_FINAL { + virtual void deepCopy(const AbstractRegistry>& sr) ELPP_FINAL { for (const_iterator it = sr.cbegin(); it != sr.cend(); ++it) { registerNew(it->first, new T_Ptr(*it->second)); } @@ -1599,7 +1553,7 @@ class RegistryWithPred : public AbstractRegistry> { class Utils { public: template - static bool installCallback(const std::string& id, std::map* mapT) { + static bool installCallback(const std::string& id, std::unordered_map* mapT) { if (mapT->find(id) == mapT->end()) { mapT->insert(std::make_pair(id, TPtr(new T()))); return true; @@ -1608,15 +1562,15 @@ class Utils { } template - static void uninstallCallback(const std::string& id, std::map* mapT) { + static void uninstallCallback(const std::string& id, std::unordered_map* mapT) { if (mapT->find(id) != mapT->end()) { mapT->erase(id); } } template - static T* callback(const std::string& id, std::map* mapT) { - typename std::map::iterator iter = mapT->find(id); + static T* callback(const std::string& id, std::unordered_map* mapT) { + typename std::unordered_map::iterator iter = mapT->find(id); if (iter != mapT->end()) { return static_cast(iter->second.get()); } @@ -1961,7 +1915,7 @@ class Configurations : public base::utils::RegistryWithPred FileStreamPtr; -typedef std::map LogStreamsReferenceMap; +typedef std::unordered_map LogStreamsReferenceMap; /// @brief Configurations with data types. /// /// @detail el::Configurations have string based values. This is whats used internally in order to read correct configurations. @@ -1998,16 +1952,16 @@ class TypedConfigurations : public base::threading::ThreadSafe { private: Configurations* m_configurations; - std::map m_enabledMap; - std::map m_toFileMap; - std::map m_filenameMap; - std::map m_toStandardOutputMap; - std::map m_logFormatMap; - std::map m_subsecondPrecisionMap; - std::map m_performanceTrackingMap; - std::map m_fileStreamMap; - std::map m_maxLogFileSizeMap; - std::map m_logFlushThresholdMap; + std::unordered_map m_enabledMap; + std::unordered_map m_toFileMap; + std::unordered_map m_filenameMap; + std::unordered_map m_toStandardOutputMap; + std::unordered_map m_logFormatMap; + std::unordered_map m_subsecondPrecisionMap; + std::unordered_map m_performanceTrackingMap; + std::unordered_map m_fileStreamMap; + std::unordered_map m_maxLogFileSizeMap; + std::unordered_map m_logFlushThresholdMap; base::LogStreamsReferenceMap* m_logStreamsReference; friend class el::Helpers; @@ -2017,21 +1971,21 @@ class TypedConfigurations : public base::threading::ThreadSafe { friend class el::base::LogDispatcher; template - inline Conf_T getConfigByVal(Level level, const std::map* confMap, const char* confName) { + inline Conf_T getConfigByVal(Level level, const std::unordered_map* confMap, const char* confName) { base::threading::ScopedLock scopedLock(lock()); return unsafeGetConfigByVal(level, confMap, confName); // This is not unsafe anymore - mutex locked in scope } template - inline Conf_T& getConfigByRef(Level level, std::map* confMap, const char* confName) { + inline Conf_T& getConfigByRef(Level level, std::unordered_map* confMap, const char* confName) { base::threading::ScopedLock scopedLock(lock()); return unsafeGetConfigByRef(level, confMap, confName); // This is not unsafe anymore - mutex locked in scope } template - Conf_T unsafeGetConfigByVal(Level level, const std::map* confMap, const char* confName) { + Conf_T unsafeGetConfigByVal(Level level, const std::unordered_map* confMap, const char* confName) { ELPP_UNUSED(confName); - typename std::map::const_iterator it = confMap->find(level); + typename std::unordered_map::const_iterator it = confMap->find(level); if (it == confMap->end()) { try { return confMap->at(Level::Global); @@ -2046,9 +2000,9 @@ class TypedConfigurations : public base::threading::ThreadSafe { } template - Conf_T& unsafeGetConfigByRef(Level level, std::map* confMap, const char* confName) { + Conf_T& unsafeGetConfigByRef(Level level, std::unordered_map* confMap, const char* confName) { ELPP_UNUSED(confName); - typename std::map::iterator it = confMap->find(level); + typename std::unordered_map::iterator it = confMap->find(level); if (it == confMap->end()) { try { return confMap->at(Level::Global); @@ -2062,14 +2016,15 @@ class TypedConfigurations : public base::threading::ThreadSafe { } template - void setValue(Level level, const Conf_T& value, std::map* confMap, bool includeGlobalLevel = true) { + void setValue(Level level, const Conf_T& value, std::unordered_map* confMap, + bool includeGlobalLevel = true) { // If map is empty and we are allowed to add into generic level (Level::Global), do it! if (confMap->empty() && includeGlobalLevel) { confMap->insert(std::make_pair(Level::Global, value)); return; } // If same value exist in generic level already, dont add it to explicit level - typename std::map::iterator it = confMap->find(Level::Global); + typename std::unordered_map::iterator it = confMap->find(Level::Global); if (it != confMap->end() && it->second == value) { return; } @@ -2231,21 +2186,26 @@ class LogDispatchData { inline base::DispatchAction dispatchAction(void) const { return m_dispatchAction; } - private: - LogMessage* m_logMessage; - base::DispatchAction m_dispatchAction; - friend class base::LogDispatcher; - inline void setLogMessage(LogMessage* logMessage) { m_logMessage = logMessage; } inline void setDispatchAction(base::DispatchAction dispatchAction) { m_dispatchAction = dispatchAction; } + private: + LogMessage* m_logMessage; + base::DispatchAction m_dispatchAction; + friend class base::LogDispatcher; + }; class LogDispatchCallback : public Callback { + protected: + virtual void handle(const LogDispatchData* data); + base::threading::Mutex& fileHandle(const LogDispatchData* data); private: friend class base::LogDispatcher; + std::unordered_map> m_fileLocks; + base::threading::Mutex m_fileLocksMapLock; }; class PerformanceTrackingCallback : public Callback { private: @@ -2363,7 +2323,7 @@ inline void FUNCTION_NAME(const T&); std::string m_parentApplicationName; bool m_isConfigured; Configurations m_configurations; - std::map m_unflushedCount; + std::unordered_map m_unflushedCount; base::LogStreamsReferenceMap* m_logStreamsReference; LogBuilderPtr m_logBuilder; @@ -2469,7 +2429,7 @@ class RegisteredLoggers : public base::utils::Registry { LogBuilderPtr m_defaultLogBuilder; Configurations m_defaultConfigurations; base::LogStreamsReferenceMap m_logStreamsReference; - std::map m_loggerRegistrationCallbacks; + std::unordered_map m_loggerRegistrationCallbacks; friend class el::base::Storage; void unsafeFlushAll(void); @@ -2507,7 +2467,7 @@ class VRegistry : base::NoCopy, public base::threading::ThreadSafe { bool allowed(base::type::VerboseLevel vlevel, const char* file); - inline const std::map& modules(void) const { + inline const std::unordered_map& modules(void) const { return m_modules; } @@ -2529,7 +2489,7 @@ class VRegistry : base::NoCopy, public base::threading::ThreadSafe { private: base::type::VerboseLevel m_level; base::type::EnumType* m_pFlags; - std::map m_modules; + std::unordered_map m_modules; std::vector> m_categories; std::map m_cached_allowed_categories; std::string m_categoriesString; @@ -2717,6 +2677,10 @@ class Storage : base::NoCopy, public base::threading::ThreadSafe { return &m_customFormatSpecifiers; } + base::threading::Mutex& customFormatSpecifiersLock() { + return m_customFormatSpecifiersLock; + } + inline void setLoggingLevel(Level level) { m_loggingLevel = level; } @@ -2757,13 +2721,13 @@ class Storage : base::NoCopy, public base::threading::ThreadSafe { /// @brief Sets thread name for current thread. Requires std::thread inline void setThreadName(const std::string& name) { if (name.empty()) return; - base::threading::ScopedLock scopedLock(lock()); + base::threading::ScopedLock scopedLock(m_threadNamesLock); m_threadNames[base::threading::getCurrentThreadId()] = name; } inline std::string getThreadName(const std::string& threadId) { - base::threading::ScopedLock scopedLock(lock()); - std::map::const_iterator it = m_threadNames.find(threadId); + base::threading::ScopedLock scopedLock(m_threadNamesLock); + std::unordered_map::const_iterator it = m_threadNames.find(threadId); if (it == m_threadNames.end()) { return threadId; } @@ -2783,10 +2747,12 @@ class Storage : base::NoCopy, public base::threading::ThreadSafe { #endif // ELPP_ASYNC_LOGGING base::utils::CommandLineArgs m_commandLineArgs; PreRollOutCallback m_preRollOutCallback; - std::map m_logDispatchCallbacks; - std::map m_performanceTrackingCallbacks; - std::map m_threadNames; + std::unordered_map m_logDispatchCallbacks; + std::unordered_map m_performanceTrackingCallbacks; + std::unordered_map m_threadNames; std::vector m_customFormatSpecifiers; + base::threading::Mutex m_customFormatSpecifiersLock; + base::threading::Mutex m_threadNamesLock; Level m_loggingLevel; friend class el::Helpers; @@ -2829,7 +2795,7 @@ class AsyncDispatchWorker : public base::IWorker, public base::threading::Thread void run(void); void setContinueRunning(bool value) { - base::threading::ScopedLock scopedLock(m_continueRunningMutex); + base::threading::ScopedLock scopedLock(m_continueRunningLock); m_continueRunning = value; } @@ -2839,7 +2805,7 @@ class AsyncDispatchWorker : public base::IWorker, public base::threading::Thread private: std::condition_variable cv; bool m_continueRunning; - base::threading::Mutex m_continueRunningMutex; + base::threading::Mutex m_continueRunningLock; }; #endif // ELPP_ASYNC_LOGGING } // namespace base @@ -2851,9 +2817,9 @@ class DefaultLogBuilder : public LogBuilder { /// @brief Dispatches log messages class LogDispatcher : base::NoCopy { public: - LogDispatcher(bool proceed, LogMessage&& logMessage, base::DispatchAction dispatchAction) : + LogDispatcher(bool proceed, LogMessage* logMessage, base::DispatchAction dispatchAction) : m_proceed(proceed), - m_logMessage(std::move(logMessage)), + m_logMessage(logMessage), m_dispatchAction(std::move(dispatchAction)) { } @@ -2861,7 +2827,7 @@ class LogDispatcher : base::NoCopy { private: bool m_proceed; - LogMessage m_logMessage; + LogMessage* m_logMessage; base::DispatchAction m_dispatchAction; }; #if defined(ELPP_STL_LOGGING) @@ -3274,10 +3240,15 @@ class Writer : base::NoCopy { Writer(Level level, const char* file, base::type::LineNumber line, const char* func, base::DispatchAction dispatchAction = base::DispatchAction::NormalLog, base::type::VerboseLevel verboseLevel = 0) : - m_level(level), m_file(file), m_line(line), m_func(func), m_verboseLevel(verboseLevel), + m_msg(nullptr), m_level(level), m_file(file), m_line(line), m_func(func), m_verboseLevel(verboseLevel), m_logger(nullptr), m_proceed(false), m_dispatchAction(dispatchAction) { } + Writer(LogMessage* msg, base::DispatchAction dispatchAction = base::DispatchAction::NormalLog) : + m_msg(msg), m_level(msg != nullptr ? msg->level() : Level::Unknown), + m_line(0), m_logger(nullptr), m_proceed(false), m_dispatchAction(dispatchAction) { + } + virtual ~Writer(void) { processDispatch(); } @@ -3320,6 +3291,7 @@ class Writer : base::NoCopy { Writer& construct(Logger* logger, bool needLock = true); Writer& construct(int count, const char* loggerIds, ...); protected: + LogMessage* m_msg; Level m_level; const char* m_file; const base::type::LineNumber m_line; @@ -3378,6 +3350,7 @@ void Logger::log_(Level level, int vlevel, const T& log) { base::DispatchAction::NormalLog, vlevel).construct(this, false) << log; } else { stream().str(ELPP_LITERAL("")); + releaseLock(); } } else { base::Writer(level, "FILE", 0, "FUNCTION").construct(this, false) << log; @@ -3385,23 +3358,23 @@ void Logger::log_(Level level, int vlevel, const T& log) { } template inline void Logger::log(Level level, const char* s, const T& value, const Args&... args) { - base::threading::ScopedLock scopedLock(lock()); + acquireLock(); // released in Writer! log_(level, 0, s, value, args...); } template inline void Logger::log(Level level, const T& log) { - base::threading::ScopedLock scopedLock(lock()); + acquireLock(); // released in Writer! log_(level, 0, log); } # if ELPP_VERBOSE_LOG template inline void Logger::verbose(int vlevel, const char* s, const T& value, const Args&... args) { - base::threading::ScopedLock scopedLock(lock()); + acquireLock(); // released in Writer! log_(el::Level::Verbose, vlevel, s, value, args...); } template inline void Logger::verbose(int vlevel, const T& log) { - base::threading::ScopedLock scopedLock(lock()); + acquireLock(); // released in Writer! log_(el::Level::Verbose, vlevel, log); } # else @@ -3656,8 +3629,9 @@ class StackTrace : base::NoCopy { static const unsigned int kStackStart = 2; // We want to skip c'tor and StackTrace::generateNew() class StackTraceEntry { public: - StackTraceEntry(std::size_t index, const char* loc, const char* demang, const char* hex, const char* addr); - StackTraceEntry(std::size_t index, char* loc) : + StackTraceEntry(std::size_t index, const std::string& loc, const std::string& demang, const std::string& hex, + const std::string& addr); + StackTraceEntry(std::size_t index, const std::string& loc) : m_index(index), m_location(loc) { } @@ -3842,6 +3816,11 @@ class Helpers : base::StaticClass { static inline const el::base::utils::CommandLineArgs* commandLineArgs(void) { return ELPP->commandLineArgs(); } + /// @brief Reserve space for custom format specifiers for performance + /// @see std::vector::reserve + static inline void reserveCustomFormatSpecifiers(std::size_t size) { + ELPP->m_customFormatSpecifiers.reserve(size); + } /// @brief Installs user defined format specifier and handler static inline void installCustomFormatSpecifier(const CustomFormatSpecifier& customFormatSpecifier) { ELPP->installCustomFormatSpecifier(customFormatSpecifier); From 3dba7f252e080ca9ce9c86dca033e6b0b00ee21d Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 2 Nov 2018 22:27:25 +0000 Subject: [PATCH 0741/1404] protocol: option to pad transaction relay to the next kB To help protect one's privacy from traffic volume analysis for people using Tor or I2P. This will really fly once we relay txes on a timer rather than on demand, though. Off by default for now since it's wasteful and doesn't bring anything until I2P's in. --- src/cryptonote_core/cryptonote_core.cpp | 10 +++++- src/cryptonote_core/cryptonote_core.h | 8 +++++ .../cryptonote_protocol_defs.h | 2 ++ .../cryptonote_protocol_handler.inl | 31 +++++++++++++++++++ tests/core_proxy/core_proxy.h | 1 + tests/unit_tests/ban.cpp | 1 + 6 files changed, 52 insertions(+), 1 deletion(-) diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index d8c38bf9eac..c5549936594 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -163,6 +163,11 @@ namespace cryptonote , "Relay blocks as normal blocks" , false }; + static const command_line::arg_descriptor arg_pad_transactions = { + "pad-transactions" + , "Pad relayed transactions to help defend against traffic volume analysis" + , false + }; static const command_line::arg_descriptor arg_max_txpool_weight = { "max-txpool-weight" , "Set maximum txpool weight in bytes." @@ -188,7 +193,8 @@ namespace cryptonote m_disable_dns_checkpoints(false), m_update_download(0), m_nettype(UNDEFINED), - m_update_available(false) + m_update_available(false), + m_pad_transactions(false) { m_checkpoints_updating.clear(); set_cryptonote_protocol(pprotocol); @@ -282,6 +288,7 @@ namespace cryptonote command_line::add_arg(desc, arg_offline); command_line::add_arg(desc, arg_disable_dns_checkpoints); command_line::add_arg(desc, arg_max_txpool_weight); + command_line::add_arg(desc, arg_pad_transactions); command_line::add_arg(desc, arg_block_notify); miner::init_options(desc); @@ -320,6 +327,7 @@ namespace cryptonote set_enforce_dns_checkpoints(command_line::get_arg(vm, arg_dns_checkpoints)); test_drop_download_height(command_line::get_arg(vm, arg_test_drop_download_height)); m_fluffy_blocks_enabled = !get_arg(vm, arg_no_fluffy_blocks); + m_pad_transactions = get_arg(vm, arg_pad_transactions); m_offline = get_arg(vm, arg_offline); m_disable_dns_checkpoints = get_arg(vm, arg_disable_dns_checkpoints); if (!command_line::is_arg_defaulted(vm, arg_fluffy_blocks)) diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index 80c452f53c4..cef42d207b9 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -756,6 +756,13 @@ namespace cryptonote */ bool fluffy_blocks_enabled() const { return m_fluffy_blocks_enabled; } + /** + * @brief get whether transaction relay should be padded + * + * @return whether transaction relay should be padded + */ + bool pad_transactions() const { return m_pad_transactions; } + /** * @brief check a set of hashes against the precompiled hash set * @@ -1014,6 +1021,7 @@ namespace cryptonote bool m_fluffy_blocks_enabled; bool m_offline; + bool m_pad_transactions; }; } diff --git a/src/cryptonote_protocol/cryptonote_protocol_defs.h b/src/cryptonote_protocol/cryptonote_protocol_defs.h index db159f0f464..d5bb50930db 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_defs.h +++ b/src/cryptonote_protocol/cryptonote_protocol_defs.h @@ -146,9 +146,11 @@ namespace cryptonote struct request { std::vector txs; + std::string _; // padding BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(txs) + KV_SERIALIZE(_) END_KV_SERIALIZE_MAP() }; }; diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl index c2c660e8cfe..6efdcc25ea6 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl @@ -1724,8 +1724,39 @@ skip: bool t_cryptonote_protocol_handler::relay_transactions(NOTIFY_NEW_TRANSACTIONS::request& arg, cryptonote_connection_context& exclude_context) { // no check for success, so tell core they're relayed unconditionally + const bool pad_transactions = m_core.pad_transactions(); + size_t bytes = pad_transactions ? 9 /* header */ + 4 /* 1 + 'txs' */ + tools::get_varint_data(arg.txs.size()).size() : 0; for(auto tx_blob_it = arg.txs.begin(); tx_blob_it!=arg.txs.end(); ++tx_blob_it) + { m_core.on_transaction_relayed(*tx_blob_it); + if (pad_transactions) + bytes += tools::get_varint_data(tx_blob_it->size()).size() + tx_blob_it->size(); + } + + if (pad_transactions) + { + // stuff some dummy bytes in to stay safe from traffic volume analysis + static constexpr size_t granularity = 1024; + size_t padding = granularity - bytes % granularity; + const size_t overhead = 2 /* 1 + '_' */ + tools::get_varint_data(padding).size(); + if (overhead > padding) + padding = 0; + else + padding -= overhead; + arg._ = std::string(padding, ' '); + + std::string arg_buff; + epee::serialization::store_t_to_binary(arg, arg_buff); + + // we probably lowballed the payload size a bit, so added a but too much. Fix this now. + size_t remove = arg_buff.size() % granularity; + if (remove > arg._.size()) + arg._.clear(); + else + arg._.resize(arg._.size() - remove); + // if the size of _ moved enough, we might lose byte in size encoding, we don't care + } + return relay_post_notify(arg, exclude_context); } //------------------------------------------------------------------------------------------------------------------------ diff --git a/tests/core_proxy/core_proxy.h b/tests/core_proxy/core_proxy.h index 7d36a0f689f..023c220ae9c 100644 --- a/tests/core_proxy/core_proxy.h +++ b/tests/core_proxy/core_proxy.h @@ -104,5 +104,6 @@ namespace tests cryptonote::difficulty_type get_block_cumulative_difficulty(uint64_t height) const { return 0; } bool fluffy_blocks_enabled() const { return false; } uint64_t prevalidate_block_hashes(uint64_t height, const std::vector &hashes) { return 0; } + bool pad_transactions() const { return false; } }; } diff --git a/tests/unit_tests/ban.cpp b/tests/unit_tests/ban.cpp index e3dbdaef154..12625a949fa 100644 --- a/tests/unit_tests/ban.cpp +++ b/tests/unit_tests/ban.cpp @@ -83,6 +83,7 @@ class test_core cryptonote::difficulty_type get_block_cumulative_difficulty(uint64_t height) const { return 0; } bool fluffy_blocks_enabled() const { return false; } uint64_t prevalidate_block_hashes(uint64_t height, const std::vector &hashes) { return 0; } + bool pad_transactions() { return false; } void stop() {} }; From 840bf155a0ed28902de3d0546dec58fac7966131 Mon Sep 17 00:00:00 2001 From: xiphon Date: Tue, 6 Nov 2018 21:45:00 +0000 Subject: [PATCH 0742/1404] build: fix Ubuntu 16.04 (GCC 5.4.0) compilation --- src/wallet/wallet2.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 58ed5dcadca..ebf857b787f 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -11069,7 +11069,8 @@ std::string wallet2::export_outputs_to_str() const std::stringstream oss; boost::archive::portable_binary_oarchive ar(oss); - ar << export_outputs(); + const auto& outputs = export_outputs(); + ar << outputs; std::string magic(OUTPUT_EXPORT_FILE_MAGIC, strlen(OUTPUT_EXPORT_FILE_MAGIC)); const cryptonote::account_public_address &keys = get_account().get_keys().m_account_address; From 1a4d160311df8b3aaf0e33fe0c3b77f1862c395e Mon Sep 17 00:00:00 2001 From: stoffu Date: Wed, 7 Nov 2018 10:47:10 +0900 Subject: [PATCH 0743/1404] wallet2: remove redundant chacha key generation in store_keys --- src/wallet/wallet2.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 58ed5dcadca..df947712e36 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -3144,7 +3144,6 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable account_data = buffer.GetString(); // Encrypt the entire JSON object. - crypto::generate_chacha_key(password.data(), password.size(), key, m_kdf_rounds); std::string cipher; cipher.resize(account_data.size()); keys_file_data.iv = crypto::rand(); From ba2055a22066c9bba26b39b0d8368dc49b1e023c Mon Sep 17 00:00:00 2001 From: stoffu Date: Wed, 7 Nov 2018 15:09:43 +0900 Subject: [PATCH 0744/1404] api/wallet: fix compile errors made in #4720 --- src/wallet/api/wallet.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index ddf2d74ffd9..7cd3b65bbec 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -507,7 +507,7 @@ bool WalletImpl::createWatchOnly(const std::string &path, const std::string &pas auto key_images = m_wallet->export_key_images(); uint64_t spent = 0; uint64_t unspent = 0; - view_wallet->import_key_images(key_images,spent,unspent,false); + view_wallet->import_key_images(key_images.second, key_images.first, spent, unspent, false); clearStatus(); } catch (const std::exception &e) { LOG_ERROR("Error creating view only wallet: " << e.what()); @@ -1051,8 +1051,8 @@ UnsignedTransaction *WalletImpl::loadUnsignedTx(const std::string &unsigned_file // Check tx data and construct confirmation message std::string extra_message; - if (!transaction->m_unsigned_tx_set.transfers.empty()) - extra_message = (boost::format("%u outputs to import. ") % (unsigned)transaction->m_unsigned_tx_set.transfers.size()).str(); + if (!transaction->m_unsigned_tx_set.transfers.second.empty()) + extra_message = (boost::format("%u outputs to import. ") % (unsigned)transaction->m_unsigned_tx_set.transfers.second.size()).str(); transaction->checkLoadedTx([&transaction](){return transaction->m_unsigned_tx_set.txes.size();}, [&transaction](size_t n)->const tools::wallet2::tx_construction_data&{return transaction->m_unsigned_tx_set.txes[n];}, extra_message); setStatus(transaction->status(), transaction->errorString()); From 7c298f5d14e9ef47b812983fbb8a8f446b536fb5 Mon Sep 17 00:00:00 2001 From: Martijn Otto Date: Wed, 7 Nov 2018 09:24:50 +0100 Subject: [PATCH 0745/1404] No longer use a list for registering self references in the abstract tcp server Updated assert message Use a local variable that won't destruct at the end of the if-branch Updated comment --- contrib/epee/include/net/abstract_tcp_server2.h | 3 ++- contrib/epee/include/net/abstract_tcp_server2.inl | 13 ++++++++----- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/contrib/epee/include/net/abstract_tcp_server2.h b/contrib/epee/include/net/abstract_tcp_server2.h index 3f726a35272..df2b9d1b22d 100644 --- a/contrib/epee/include/net/abstract_tcp_server2.h +++ b/contrib/epee/include/net/abstract_tcp_server2.h @@ -155,7 +155,8 @@ namespace net_utils //this should be the last one, because it could be wait on destructor, while other activities possible on other threads t_protocol_handler m_protocol_handler; //typename t_protocol_handler::config_type m_dummy_config; - std::list > > m_self_refs; // add_ref/release support + size_t m_reference_count = 0; // reference count managed through add_ref/release support + boost::shared_ptr > m_self_ref; // the reference to hold critical_section m_self_refs_lock; critical_section m_chunking_lock; // held while we add small chunks of the big do_send() to small do_send_chunk() critical_section m_shutdown_lock; // held while shutting down diff --git a/contrib/epee/include/net/abstract_tcp_server2.inl b/contrib/epee/include/net/abstract_tcp_server2.inl index 9b03941ee93..a74eb1f26ca 100644 --- a/contrib/epee/include/net/abstract_tcp_server2.inl +++ b/contrib/epee/include/net/abstract_tcp_server2.inl @@ -230,7 +230,8 @@ PRAGMA_WARNING_DISABLE_VS(4355) //_dbg3("[sock " << socket_.native_handle() << "] add_ref 2, m_peer_number=" << mI->m_peer_number); if(m_was_shutdown) return false; - m_self_refs.push_back(self); + ++m_reference_count; + m_self_ref = std::move(self); return true; CATCH_ENTRY_L0("connection::add_ref()", false); } @@ -242,10 +243,12 @@ PRAGMA_WARNING_DISABLE_VS(4355) boost::shared_ptr > back_connection_copy; LOG_TRACE_CC(context, "[sock " << socket_.native_handle() << "] release"); CRITICAL_REGION_BEGIN(m_self_refs_lock); - CHECK_AND_ASSERT_MES(m_self_refs.size(), false, "[sock " << socket_.native_handle() << "] m_self_refs empty at connection::release() call"); - //erasing from container without additional copy can cause start deleting object, including m_self_refs - back_connection_copy = m_self_refs.back(); - m_self_refs.pop_back(); + CHECK_AND_ASSERT_MES(m_reference_count, false, "[sock " << socket_.native_handle() << "] m_reference_count already at 0 at connection::release() call"); + // is this the last reference? + if (--m_reference_count == 0) { + // move the held reference to a local variable, keeping the object alive until the function terminates + std::swap(back_connection_copy, m_self_ref); + } CRITICAL_REGION_END(); return true; CATCH_ENTRY_L0("connection::release()", false); From e27e421f989aea2c89d6cdd1d73297243540e0d4 Mon Sep 17 00:00:00 2001 From: TheCharlatan Date: Wed, 7 Nov 2018 18:27:52 +0100 Subject: [PATCH 0746/1404] Fix version prefix in gitian build The version prefix 'v' should just be set constantly. Reflect this change in the README as well. This should allow building commits as well, if a commit is passed in instead of a tag. --- contrib/gitian/README.md | 27 +++++++-------------------- contrib/gitian/gitian-build.py | 2 +- 2 files changed, 8 insertions(+), 21 deletions(-) diff --git a/contrib/gitian/README.md b/contrib/gitian/README.md index 4bd326f229f..0b13df344c8 100644 --- a/contrib/gitian/README.md +++ b/contrib/gitian/README.md @@ -17,19 +17,6 @@ More independent Gitian builders are needed, which is why this guide exists. It is preferred you follow these steps yourself instead of using someone else's VM image to avoid 'contaminating' the build. -Table of Contents ------------------- - -Please note that these instructions have been forked from bitcoin's gitian build -instructions. Please also consult their documentation, when running into problems. -The signing is left as inherited from bitcoin at the moment. - -- [Preparing the Gitian builder host](#preparing-the-gitian-builder-host) -- [Getting and building the inputs](#getting-and-building-the-inputs) -- [Building Binaries](#building-bitcoin-core) -- [Signing externally](#signing-externally) -- [Uploading signatures](#uploading-signatures) - Preparing the Gitian builder host --------------------------------- @@ -93,10 +80,10 @@ cp monero/contrib/gitian/gitian-build.py . Setup the required environment, you only need to do this once: ``` -./gitian-build.py --setup fluffypony 0.14.0 +./gitian-build.py --setup fluffypony v0.14.0 ``` -Where `fluffypony` is your Github name and `0.14.0` is the version tag you want to build (without `v`). +Where `fluffypony` is your Github name and `v0.14.0` is the version tag you want to build. While gitian and this build script does provide a way for you to sign the build directly, it is recommended to sign in a seperate step. This script is only there for convenience. Seperate steps for building can still be taken. @@ -113,7 +100,7 @@ Build Binaries ----------------------------- To build the most recent tag: - `./gitian-build.py --detach-sign --no-commit -b fluffypony 0.14.0` + `./gitian-build.py --detach-sign --no-commit -b fluffypony v0.14.0` To speed up the build, use `-j 5 -m 5000` as the first arguments, where `5` is the number of CPU's you allocated to the VM plus one, and 5000 is a little bit less than then the MB's of RAM you allocated. If there is memory corruption on your machine, try to tweak these values. @@ -123,7 +110,7 @@ If you do detached, offline signing, you need to copy these uncommited changes t ``` export NAME=fluffypony -export VERSION=0.14 +export VERSION=v0.14.0 gpg --output $VERSION-linux/$NAME/monero-linux-$VERSION-build.assert.sig --detach-sign $VERSION-linux/$NAME/monero-linux-$VERSION-build.assert gpg --output $VERSION-osx-unsigned/$NAME/monero-osx-$VERSION-build.assert.sig --detach-sign $VERSION-osx-unsigned/$NAME/monero-osx-$VERSION-build.assert gpg --output $VERSION-win-unsigned/$NAME/monero-win-$VERSION-build.assert.sig --detach-sign $VERSION-win-unsigned/$NAME/monero-win-$VERSION-build.assert @@ -133,9 +120,9 @@ Make a pull request (both the `.assert` and `.assert.sig` files) to the [monero-project/gitian.sigs](https://github.com/monero-project/gitian.sigs/) repository: ``` -git checkout -b 0.14.0 -git commit -S -a -m "Add $NAME 0.14.0" -git push --set-upstream $NAME 0.14.0 +git checkout -b v0.14.0 +git commit -S -a -m "Add $NAME v0.14.0" +git push --set-upstream $NAME v0.14.0 ``` ```bash diff --git a/contrib/gitian/gitian-build.py b/contrib/gitian/gitian-build.py index 99c64e9dd02..df1ba0d6b54 100755 --- a/contrib/gitian/gitian-build.py +++ b/contrib/gitian/gitian-build.py @@ -165,7 +165,7 @@ def main(): # Add leading 'v' for tags if args.commit and args.pull: raise Exception('Cannot have both commit and pull') - args.commit = ('' if args.commit else) + args.version + args.commit = args.commit if args.commit else args.version if args.setup: setup() From dffec2587fa26e2a4a8b0bda8bf0a23d5073fa3b Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 9 Nov 2018 09:38:47 +0000 Subject: [PATCH 0747/1404] db_lmdb: error out if the db needs migration in read only mode --- src/blockchain_db/lmdb/db_lmdb.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp index 1674c40dd73..8c66ef73915 100644 --- a/src/blockchain_db/lmdb/db_lmdb.cpp +++ b/src/blockchain_db/lmdb/db_lmdb.cpp @@ -1345,6 +1345,15 @@ void BlockchainLMDB::open(const std::string& filename, const int db_flags) #if VERSION > 0 else if (db_version < VERSION) { + if (mdb_flags & MDB_RDONLY) + { + txn.abort(); + mdb_env_close(m_env); + m_open = false; + MFATAL("Existing lmdb database needs to be converted, which cannot be done on a read-only database."); + MFATAL("Please run monerod once to convert the database."); + return; + } // Note that there was a schema change within version 0 as well. // See commit e5d2680094ee15889934fe28901e4e133cda56f2 2015/07/10 // We don't handle the old format previous to that commit. From 9da6c52bb2fa50eefed4e4ea8db1b651da9157bd Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 27 Apr 2018 10:20:09 +0100 Subject: [PATCH 0748/1404] unit_tests: add logging unit test --- tests/unit_tests/CMakeLists.txt | 1 + tests/unit_tests/logging.cpp | 177 ++++++++++++++++++++++++++++++++ 2 files changed, 178 insertions(+) create mode 100644 tests/unit_tests/logging.cpp diff --git a/tests/unit_tests/CMakeLists.txt b/tests/unit_tests/CMakeLists.txt index 7687e3c52fd..e63f0058f3a 100644 --- a/tests/unit_tests/CMakeLists.txt +++ b/tests/unit_tests/CMakeLists.txt @@ -54,6 +54,7 @@ set(unit_tests_sources hashchain.cpp http.cpp keccak.cpp + logging.cpp main.cpp memwipe.cpp mlocker.cpp diff --git a/tests/unit_tests/logging.cpp b/tests/unit_tests/logging.cpp new file mode 100644 index 00000000000..476e92befae --- /dev/null +++ b/tests/unit_tests/logging.cpp @@ -0,0 +1,177 @@ +// Copyright (c) 2016-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers + +#include +#include "gtest/gtest.h" +#include "file_io_utils.h" +#include "misc_log_ex.h" + +static std::string log_filename; + +static void init() +{ + boost::filesystem::path p = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path(); + log_filename = p.string(); + mlog_configure(log_filename, false, 0); +} + +static void cleanup() +{ + boost::filesystem::remove(log_filename); +} + +static size_t nlines(const std::string &str) +{ + size_t n = 0; + for (const char *ptr = str.c_str(); *ptr; ++ptr) + if (*ptr == '\n') + ++n; + return n; +} + +static bool load_log_to_string(const std::string &filename, std::string &str) +{ + if (!epee::file_io_utils::load_file_to_string(filename, str)) + return false; + for (const char *ptr = str.c_str(); *ptr; ++ptr) + { + if (*ptr == '\n') + { + std::string prefix = std::string(str.c_str(), ptr - str.c_str()); + if (prefix.find("New log categories:") != std::string::npos) + { + str = std::string(ptr + 1, strlen(ptr + 1)); + break; + } + } + } + return true; +} + +static void log() +{ + MFATAL("fatal"); + MERROR("error"); + MWARNING("warning"); + MINFO("info"); + MDEBUG("debug"); + MTRACE("trace"); + + MCINFO("a.b.c.d", "a.b.c.d"); + MCINFO("a.b.c.e", "a.b.c.e"); + MCINFO("global", "global"); + MCINFO("x.y.z", "x.y.z"); + MCINFO("y.y.z", "y.y.z"); + MCINFO("x.y.x", "x.y.x"); +} + +TEST(logging, no_logs) +{ + init(); + mlog_set_categories(""); + log(); + std::string str; + ASSERT_TRUE(load_log_to_string(log_filename, str)); + ASSERT_TRUE(str == ""); + cleanup(); +} + +TEST(logging, default) +{ + init(); + log(); + std::string str; + ASSERT_TRUE(load_log_to_string(log_filename, str)); + ASSERT_TRUE(str.find("global") != std::string::npos); + ASSERT_TRUE(str.find("fatal") != std::string::npos); + ASSERT_TRUE(str.find("error") != std::string::npos); + ASSERT_TRUE(str.find("debug") == std::string::npos); + ASSERT_TRUE(str.find("trace") == std::string::npos); + cleanup(); +} + +TEST(logging, all) +{ + init(); + mlog_set_categories("*:TRACE"); + log(); + std::string str; + ASSERT_TRUE(load_log_to_string(log_filename, str)); + ASSERT_TRUE(str.find("global") != std::string::npos); + ASSERT_TRUE(str.find("fatal") != std::string::npos); + ASSERT_TRUE(str.find("error") != std::string::npos); + ASSERT_TRUE(str.find("debug") != std::string::npos); + ASSERT_TRUE(str.find("trace") != std::string::npos); + cleanup(); +} + +TEST(logging, glob_suffix) +{ + init(); + mlog_set_categories("x.y*:TRACE"); + log(); + std::string str; + ASSERT_TRUE(load_log_to_string(log_filename, str)); + ASSERT_TRUE(str.find("global") == std::string::npos); + ASSERT_TRUE(str.find("x.y.z") != std::string::npos); + ASSERT_TRUE(str.find("x.y.x") != std::string::npos); + ASSERT_TRUE(str.find("y.y.z") == std::string::npos); + cleanup(); +} + +TEST(logging, glob_prefix) +{ + init(); + mlog_set_categories("*y.z:TRACE"); + log(); + std::string str; + ASSERT_TRUE(load_log_to_string(log_filename, str)); + ASSERT_TRUE(str.find("global") == std::string::npos); + ASSERT_TRUE(str.find("x.y.z") != std::string::npos); + ASSERT_TRUE(str.find("x.y.x") == std::string::npos); + ASSERT_TRUE(str.find("y.y.z") != std::string::npos); + cleanup(); +} + +TEST(logging, last_precedence) +{ + init(); + mlog_set_categories("gobal:FATAL,glo*:DEBUG"); + log(); + std::string str; + ASSERT_TRUE(load_log_to_string(log_filename, str)); + ASSERT_TRUE(nlines(str) == 1); + ASSERT_TRUE(str.find("global") != std::string::npos); + ASSERT_TRUE(str.find("x.y.z") == std::string::npos); + ASSERT_TRUE(str.find("x.y.x") == std::string::npos); + ASSERT_TRUE(str.find("y.y.z") == std::string::npos); + cleanup(); +} + From 2c7195d80c4639e67f2f6d1115558402ae738b94 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 9 Nov 2018 19:57:31 +0000 Subject: [PATCH 0749/1404] bulletproofs: avoid std::vector allocations for slice --- src/ringct/bulletproofs.cc | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/ringct/bulletproofs.cc b/src/ringct/bulletproofs.cc index bed48769a37..2b9976b62c0 100644 --- a/src/ringct/bulletproofs.cc +++ b/src/ringct/bulletproofs.cc @@ -31,6 +31,7 @@ #include #include #include "misc_log_ex.h" +#include "span.h" #include "common/perf_timer.h" #include "cryptonote_config.h" extern "C" @@ -217,7 +218,7 @@ static rct::key vector_power_sum(const rct::key &x, size_t n) } /* Given two scalar arrays, construct the inner product */ -static rct::key inner_product(const rct::keyV &a, const rct::keyV &b) +static rct::key inner_product(const epee::span &a, const epee::span &b) { CHECK_AND_ASSERT_THROW_MES(a.size() == b.size(), "Incompatible sizes of a and b"); rct::key res = rct::zero(); @@ -228,6 +229,11 @@ static rct::key inner_product(const rct::keyV &a, const rct::keyV &b) return res; } +static rct::key inner_product(const rct::keyV &a, const rct::keyV &b) +{ + return inner_product(epee::span(a.data(), a.size()), epee::span(b.data(), b.size())); +} + /* Given two scalar arrays, construct the Hadamard product */ static rct::keyV hadamard(const rct::keyV &a, const rct::keyV &b) { @@ -293,7 +299,7 @@ static rct::keyV vector_subtract(const rct::keyV &a, const rct::key &b) } /* Multiply a scalar and a vector */ -static rct::keyV vector_scalar(const rct::keyV &a, const rct::key &x) +static rct::keyV vector_scalar(const epee::span &a, const rct::key &x) { rct::keyV res(a.size()); for (size_t i = 0; i < a.size(); ++i) @@ -303,6 +309,11 @@ static rct::keyV vector_scalar(const rct::keyV &a, const rct::key &x) return res; } +static rct::keyV vector_scalar(const rct::keyV &a, const rct::key &x) +{ + return vector_scalar(epee::span(a.data(), a.size()), x); +} + /* Create a vector from copies of a single value */ static rct::keyV vector_dup(const rct::key &x, size_t N) { @@ -400,17 +411,12 @@ static rct::keyV invert(rct::keyV x) } /* Compute the slice of a vector */ -static rct::keyV slice(const rct::keyV &a, size_t start, size_t stop) +static epee::span slice(const rct::keyV &a, size_t start, size_t stop) { CHECK_AND_ASSERT_THROW_MES(start < a.size(), "Invalid start index"); CHECK_AND_ASSERT_THROW_MES(stop <= a.size(), "Invalid stop index"); CHECK_AND_ASSERT_THROW_MES(start < stop, "Invalid start/stop indices"); - rct::keyV res(stop - start); - for (size_t i = start; i < stop; ++i) - { - res[i - start] = a[i]; - } - return res; + return epee::span(&a[start], stop - start); } static rct::key hash_cache_mash(rct::key &hash_cache, const rct::key &mash0, const rct::key &mash1) From a93571410ee928fdb59c24f33dd31dcb26c13171 Mon Sep 17 00:00:00 2001 From: sachaaaaa Date: Thu, 8 Nov 2018 16:12:11 +1100 Subject: [PATCH 0750/1404] Add new command "export_transfers" to save transfers to csv --- src/simplewallet/simplewallet.cpp | 272 +++++++++++++++++++++++++----- src/simplewallet/simplewallet.h | 18 ++ 2 files changed, 244 insertions(+), 46 deletions(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 702ff22cb0f..dcb7b582c22 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -43,6 +43,7 @@ #include #include #include +#include #include "include_base_utils.h" #include "common/i18n.h" #include "common/command_line.h" @@ -2541,6 +2542,10 @@ simple_wallet::simple_wallet() "Pending or Failed: \"failed\"|\"pending\", \"out\", Time, Amount*, Transaction Hash, Payment ID, Fee, Input addresses**, \"-\", Note\n\n" "* Excluding change and fee.\n" "** Set of address indices used as inputs in this transfer.")); + m_cmd_binder.set_handler("export_transfers", + boost::bind(&simple_wallet::export_transfers, this, _1), + tr("export_transfers [in|out|all|pending|failed|coinbase] [index=[,,...]] [ []] [output=]"), + tr("Export to CSV the incoming/outgoing transfers within an optional height range.")); m_cmd_binder.set_handler("unspent_outputs", boost::bind(&simple_wallet::unspent_outputs, this, _1), tr("unspent_outputs [index=[,,...]] [ []]"), @@ -6763,9 +6768,9 @@ static std::string get_human_readable_timespan(std::chrono::seconds seconds) return sw::tr("a long time"); } //---------------------------------------------------------------------------------------------------- -bool simple_wallet::show_transfers(const std::vector &args_) +// mutates local_args as it parses and consumes arguments +bool simple_wallet::get_transfers(std::vector& local_args, std::vector& transfers) { - std::vector local_args = args_; bool in = true; bool out = true; bool pending = true; @@ -6774,15 +6779,7 @@ bool simple_wallet::show_transfers(const std::vector &args_) bool coinbase = true; uint64_t min_height = 0; uint64_t max_height = (uint64_t)-1; - boost::optional subaddr_index; - if(local_args.size() > 4) { - fail_msg_writer() << tr("usage: show_transfers [in|out|all|pending|failed|coinbase] [index=[,,...]] [ []]"); - return true; - } - - LOCK_IDLE_SCOPE(); - // optional in/out selector if (local_args.size() > 0) { if (local_args[0] == "in" || local_args[0] == "incoming") { @@ -6820,38 +6817,34 @@ bool simple_wallet::show_transfers(const std::vector &args_) if (local_args.size() > 0 && local_args[0].substr(0, 6) == "index=") { if (!parse_subaddress_indices(local_args[0], subaddr_indices)) - return true; + return false; local_args.erase(local_args.begin()); } // min height - if (local_args.size() > 0) { + if (local_args.size() > 0 && local_args[0].find('=') == std::string::npos) { try { min_height = boost::lexical_cast(local_args[0]); } catch (const boost::bad_lexical_cast &) { fail_msg_writer() << tr("bad min_height parameter:") << " " << local_args[0]; - return true; + return false; } local_args.erase(local_args.begin()); } // max height - if (local_args.size() > 0) { + if (local_args.size() > 0 && local_args[0].find('=') == std::string::npos) { try { max_height = boost::lexical_cast(local_args[0]); } catch (const boost::bad_lexical_cast &) { fail_msg_writer() << tr("bad max_height parameter:") << " " << local_args[0]; - return true; + return false; } local_args.erase(local_args.begin()); } - std::multimap> output; - - PAUSE_READLINE(); - if (in || coinbase) { std::list> payments; m_wallet->get_payments(payments, min_height, max_height, m_current_subaddress_account, subaddr_indices); @@ -6863,24 +6856,26 @@ bool simple_wallet::show_transfers(const std::vector &args_) if (payment_id.substr(16).find_first_not_of('0') == std::string::npos) payment_id = payment_id.substr(0,16); std::string note = m_wallet->get_tx_note(pd.m_tx_hash); + std::string destination = m_wallet->get_subaddress_as_str({m_current_subaddress_account, pd.m_subaddr_index.minor}); const std::string type = pd.m_coinbase ? tr("block") : tr("in"); const bool unlocked = m_wallet->is_tx_spendtime_unlocked(pd.m_unlock_time, pd.m_block_height); - output.insert(std::make_pair(pd.m_block_height, std::make_tuple(epee::console_color_green, type, (boost::format("%8.8s %25.25s %20.20s %s %s %d %s %s") % (unlocked ? "unlocked" : "locked") % get_human_readable_timestamp(pd.m_timestamp) % print_money(pd.m_amount) % string_tools::pod_to_hex(pd.m_tx_hash) % payment_id % pd.m_subaddr_index.minor % "-" % note).str()))); + transfers.push_back({ + pd.m_block_height, + pd.m_timestamp, + type, + true, + pd.m_amount, + pd.m_tx_hash, + payment_id, + 0, + {{destination, pd.m_amount}}, + {pd.m_subaddr_index.minor}, + note, + (unlocked) ? "unlocked" : "locked" + }); } } - auto print_subaddr_indices = [](const std::set& indices) - { - stringstream ss; - bool first = true; - for (uint32_t i : indices) - { - ss << (first ? "" : ",") << i; - first = false; - } - return ss.str(); - }; - if (out) { std::list> payments; m_wallet->get_payments_out(payments, min_height, max_height, m_current_subaddress_account, subaddr_indices); @@ -6888,27 +6883,31 @@ bool simple_wallet::show_transfers(const std::vector &args_) const tools::wallet2::confirmed_transfer_details &pd = i->second; uint64_t change = pd.m_change == (uint64_t)-1 ? 0 : pd.m_change; // change may not be known uint64_t fee = pd.m_amount_in - pd.m_amount_out; - std::string dests; + std::vector> destinations; for (const auto &d: pd.m_dests) { - if (!dests.empty()) - dests += ", "; - dests += get_account_address_as_str(m_wallet->nettype(), d.is_subaddress, d.addr) + ": " + print_money(d.amount); + destinations.push_back({get_account_address_as_str(m_wallet->nettype(), d.is_subaddress, d.addr), d.amount}); } std::string payment_id = string_tools::pod_to_hex(i->second.m_payment_id); if (payment_id.substr(16).find_first_not_of('0') == std::string::npos) payment_id = payment_id.substr(0,16); std::string note = m_wallet->get_tx_note(i->first); - output.insert(std::make_pair(pd.m_block_height, std::make_tuple(epee::console_color_magenta, tr("out"), (boost::format("%8.8s %25.25s %20.20s %s %s %14.14s %s %s - %s") % "-" % get_human_readable_timestamp(pd.m_timestamp) % print_money(pd.m_amount_in - change - fee) % string_tools::pod_to_hex(i->first) % payment_id % print_money(fee) % dests % print_subaddr_indices(pd.m_subaddr_indices) % note).str()))); + transfers.push_back({ + pd.m_block_height, + pd.m_timestamp, + "out", + true, + pd.m_amount_in - change - fee, + i->first, + payment_id, + fee, + destinations, + pd.m_subaddr_indices, + note, + "-" + }); } } - // print in and out sorted by height - for (std::multimap>::const_iterator i = output.begin(); i != output.end(); ++i) { - message_writer(std::get<0>(i->second), false) << - boost::format("%8.8llu %6.6s %s") % - ((unsigned long long)i->first) % std::get<1>(i->second) % std::get<2>(i->second); - } - if (pool) { try { @@ -6924,10 +6923,24 @@ bool simple_wallet::show_transfers(const std::vector &args_) if (payment_id.substr(16).find_first_not_of('0') == std::string::npos) payment_id = payment_id.substr(0,16); std::string note = m_wallet->get_tx_note(pd.m_tx_hash); + std::string destination = m_wallet->get_subaddress_as_str({m_current_subaddress_account, pd.m_subaddr_index.minor}); std::string double_spend_note; if (i->second.m_double_spend_seen) double_spend_note = tr("[Double spend seen on the network: this transaction may or may not end up being mined] "); - message_writer() << (boost::format("%8.8s %6.6s %8.8s %25.25s %20.20s %s %s %d %s %s%s") % "pool" % "in" % "locked" % get_human_readable_timestamp(pd.m_timestamp) % print_money(pd.m_amount) % string_tools::pod_to_hex(pd.m_tx_hash) % payment_id % pd.m_subaddr_index.minor % "-" % note % double_spend_note).str(); + transfers.push_back({ + "pool", + pd.m_timestamp, + "in", + false, + pd.m_amount, + pd.m_tx_hash, + payment_id, + 0, + {{destination, pd.m_amount}}, + {pd.m_subaddr_index.minor}, + note + double_spend_note, + "locked" + }); } } catch (const std::exception& e) @@ -6944,20 +6957,187 @@ bool simple_wallet::show_transfers(const std::vector &args_) const tools::wallet2::unconfirmed_transfer_details &pd = i->second; uint64_t amount = pd.m_amount_in; uint64_t fee = amount - pd.m_amount_out; + std::vector> destinations; + for (const auto &d: pd.m_dests) { + destinations.push_back({get_account_address_as_str(m_wallet->nettype(), d.is_subaddress, d.addr), d.amount}); + } std::string payment_id = string_tools::pod_to_hex(i->second.m_payment_id); if (payment_id.substr(16).find_first_not_of('0') == std::string::npos) payment_id = payment_id.substr(0,16); std::string note = m_wallet->get_tx_note(i->first); bool is_failed = pd.m_state == tools::wallet2::unconfirmed_transfer_details::failed; if ((failed && is_failed) || (!is_failed && pending)) { - message_writer() << (boost::format("%8.8s %6.6s %8.8s %25.25s %20.20s %s %s %14.14s %s - %s") % (is_failed ? tr("failed") : tr("pending")) % tr("out") % "-" % get_human_readable_timestamp(pd.m_timestamp) % print_money(amount - pd.m_change - fee) % string_tools::pod_to_hex(i->first) % payment_id % print_money(fee) % print_subaddr_indices(pd.m_subaddr_indices) % note).str(); + transfers.push_back({ + (is_failed ? "failed" : "pending"), + pd.m_timestamp, + "out", + false, + amount - pd.m_change - fee, + i->first, + payment_id, + fee, + destinations, + pd.m_subaddr_indices, + note, + "-" + }); + } + } + } + // sort by block, then by timestamp (unconfirmed last) + std::sort(transfers.begin(), transfers.end(), [](const transfer_view& a, const transfer_view& b) -> bool { + if (a.confirmed && !b.confirmed) + return true; + if (a.block == b.block) + return a.timestamp < b.timestamp; + return a.block < b.block; + }); + + return true; +} +//---------------------------------------------------------------------------------------------------- +bool simple_wallet::show_transfers(const std::vector &args_) +{ + std::vector local_args = args_; + + if(local_args.size() > 4) { + fail_msg_writer() << tr("usage: show_transfers [in|out|all|pending|failed|coinbase] [index=[,,...]] [ []]"); + return true; + } + + LOCK_IDLE_SCOPE(); + + std::vector all_transfers; + + if (!get_transfers(local_args, all_transfers)) + return true; + + PAUSE_READLINE(); + + for (const auto& transfer : all_transfers) + { + const auto color = transfer.confirmed ? ((transfer.direction == "in" || transfer.direction == "block") ? console_color_green : console_color_magenta) : console_color_white; + + std::string destinations = "-"; + if (!transfer.outputs.empty()) + { + destinations = ""; + for (const auto& output : transfer.outputs) + { + if (!destinations.empty()) + destinations += ", "; + destinations += (transfer.direction == "in" ? output.first.substr(0, 6) : output.first) + ":" + print_money(output.second); } } + + auto formatter = boost::format("%8.8llu %6.6s %8.8s %25.25s %20.20s %s %s %14.14s %s %s - %s"); + + message_writer(color, false) << formatter + % transfer.block + % transfer.direction + % transfer.unlocked + % get_human_readable_timestamp(transfer.timestamp) + % print_money(transfer.amount) + % string_tools::pod_to_hex(transfer.hash) + % transfer.payment_id + % print_money(transfer.fee) + % destinations + % boost::algorithm::join(transfer.index | boost::adaptors::transformed([](uint32_t i) { return std::to_string(i); }), ", ") + % transfer.note; } return true; } //---------------------------------------------------------------------------------------------------- +bool simple_wallet::export_transfers(const std::vector& args_) +{ + std::vector local_args = args_; + + if(local_args.size() > 5) { + fail_msg_writer() << tr("usage: export_transfers [in|out|all|pending|failed|coinbase] [index=[,,...]] [ []] [output=]"); + return true; + } + + LOCK_IDLE_SCOPE(); + + std::vector all_transfers; + + // might consumes arguments in local_args + if (!get_transfers(local_args, all_transfers)) + return true; + + // output filename + std::string filename = (boost::format("output%u.csv") % m_current_subaddress_account).str(); + if (local_args.size() > 0 && local_args[0].substr(0, 7) == "output=") + { + filename = local_args[0].substr(7, -1); + local_args.erase(local_args.begin()); + } + + std::ofstream file(filename); + + // header + file << + boost::format("%8.8s,%9.9s,%8.8s,%25.25s,%20.20s,%20.20s,%64.64s,%16.16s,%14.14s,%100.100s,%20.20s,%s,%s") % + tr("block") % tr("direction") % tr("unlocked") % tr("timestamp") % tr("amount") % tr("running balance") % tr("hash") % tr("payment ID") % tr("fee") % tr("destination") % tr("amount") % tr("index") % tr("note") + << std::endl; + + uint64_t running_balance = 0; + auto formatter = boost::format("%8.8llu,%9.9s,%8.8s,%25.25s,%20.20s,%20.20s,%64.64s,%16.16s,%14.14s,%100.100s,%20.20s,\"%s\",%s"); + + for (const auto& transfer : all_transfers) + { + // ignore unconfirmed transfers in running balance + if (transfer.confirmed) + { + if (transfer.direction == "in" || transfer.direction == "block") + running_balance += transfer.amount; + else + running_balance -= transfer.amount + transfer.fee; + } + + file << formatter + % transfer.block + % transfer.direction + % transfer.unlocked + % get_human_readable_timestamp(transfer.timestamp) + % print_money(transfer.amount) + % print_money(running_balance) + % string_tools::pod_to_hex(transfer.hash) + % transfer.payment_id + % print_money(transfer.fee) + % (transfer.outputs.size() ? transfer.outputs[0].first : "-") + % (transfer.outputs.size() ? print_money(transfer.outputs[0].second) : "") + % boost::algorithm::join(transfer.index | boost::adaptors::transformed([](uint32_t i) { return std::to_string(i); }), ", ") + % transfer.note + << std::endl; + + for (size_t i = 1; i < transfer.outputs.size(); ++i) + { + file << formatter + % "" + % "" + % "" + % "" + % "" + % "" + % "" + % "" + % "" + % transfer.outputs[i].first + % print_money(transfer.outputs[i].second) + % "" + % "" + << std::endl; + } + } + file.close(); + + success_msg_writer() << tr("CSV exported to ") << filename; + + return true; +} +//---------------------------------------------------------------------------------------------------- bool simple_wallet::unspent_outputs(const std::vector &args_) { if(args_.size() > 3) diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index 26d51a4311a..421afbeda68 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -190,6 +190,7 @@ namespace cryptonote bool get_reserve_proof(const std::vector &args); bool check_reserve_proof(const std::vector &args); bool show_transfers(const std::vector &args); + bool export_transfers(const std::vector &args); bool unspent_outputs(const std::vector &args); bool rescan_blockchain(const std::vector &args); bool refresh_main(uint64_t start_height, ResetType reset, bool is_init = false); @@ -241,6 +242,23 @@ namespace cryptonote std::string get_prompt() const; bool print_seed(bool encrypted); + struct transfer_view + { + boost::variant block; + uint64_t timestamp; + std::string direction; + bool confirmed; + uint64_t amount; + crypto::hash hash; + std::string payment_id; + uint64_t fee; + std::vector> outputs; + std::set index; + std::string note; + std::string unlocked; + }; + bool get_transfers(std::vector& args_, std::vector& transfers); + /*! * \brief Prints the seed with a nice message * \param seed seed to print From 8dde0d4899ff8a346d510dd638b32931c3f5631e Mon Sep 17 00:00:00 2001 From: Gingeropolous Date: Sun, 11 Nov 2018 22:51:53 -0500 Subject: [PATCH 0751/1404] readme 0.13.0.4 in the table its the one thing I do. I change the table. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3b542220fd7..a5ba68ae95a 100644 --- a/README.md +++ b/README.md @@ -117,8 +117,8 @@ Dates are provided in the format YYYY-MM-DD. | 1288616 | 2017-04-15 | v5 | v0.10.3.0 | v0.10.3.1 | Adjusted minimum blocksize and fee algorithm | | 1400000 | 2017-09-16 | v6 | v0.11.0.0 | v0.11.0.0 | Allow only RingCT transactions, allow only >= ringsize 5 | | 1546000 | 2018-04-06 | v7 | v0.12.0.0 | v0.12.3.0 | Cryptonight variant 1, ringsize >= 7, sorted inputs -| 1685555 | 2018-10-18 | v8 | v0.13.0.0 | v0.13.0.0 | max transaction size at half the penalty free block size, bulletproofs enabled, cryptonight variant 2, fixed ringsize [11](https://youtu.be/KOO5S4vxi0o) -| 1686275 | 2018-10-19 | v9 | v0.13.0.0 | v0.13.0.0 | bulletproofs required +| 1685555 | 2018-10-18 | v8 | v0.13.0.0 | v0.13.0.4 | max transaction size at half the penalty free block size, bulletproofs enabled, cryptonight variant 2, fixed ringsize [11](https://youtu.be/KOO5S4vxi0o) +| 1686275 | 2018-10-19 | v9 | v0.13.0.0 | v0.13.0.4 | bulletproofs required | XXXXXXX | 2019-04-XX | XX | XXXXXXXXX | XXXXXXXXX | X X's indicate that these details have not been determined as of commit date. From 37d5b8d9c2fa9af258e6dc429544b95342b7dbf4 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 12 Nov 2018 22:35:21 +0000 Subject: [PATCH 0752/1404] CMakeLists.txt: add -ftemplate-depth=900 --- CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 388eee3fb40..69177c85f81 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -816,6 +816,9 @@ else() set(DEBUG_FLAGS "${DEBUG_FLAGS} -O0 ") endif() + # At least some CLANGs default to not enough for monero + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ftemplate-depth=900") + if(NOT DEFINED USE_LTO_DEFAULT) set(USE_LTO_DEFAULT false) endif() From d01bdaca12bd759462f91783ca63ec178f6c3268 Mon Sep 17 00:00:00 2001 From: xiphon Date: Tue, 13 Nov 2018 12:15:18 +0000 Subject: [PATCH 0753/1404] common: fix base58 gcc -Werror=implicit-fallthrough --- src/common/base58.cpp | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/src/common/base58.cpp b/src/common/base58.cpp index 75556cad94c..b28a04f2005 100644 --- a/src/common/base58.cpp +++ b/src/common/base58.cpp @@ -109,20 +109,8 @@ namespace tools assert(1 <= size && size <= sizeof(uint64_t)); uint64_t res = 0; - switch (9 - size) - { - case 1: res |= *data++; /* FALLTHRU */ - case 2: res <<= 8; res |= *data++; /* FALLTHRU */ - case 3: res <<= 8; res |= *data++; /* FALLTHRU */ - case 4: res <<= 8; res |= *data++; /* FALLTHRU */ - case 5: res <<= 8; res |= *data++; /* FALLTHRU */ - case 6: res <<= 8; res |= *data++; /* FALLTHRU */ - case 7: res <<= 8; res |= *data++; /* FALLTHRU */ - case 8: res <<= 8; res |= *data; break; - default: assert(false); - } - - return res; + memcpy(reinterpret_cast(&res) + sizeof(uint64_t) - size, data, size); + return SWAP64BE(res); } void uint_64_to_8be(uint64_t num, size_t size, uint8_t* data) From f0459abbb981e88c91c5d16e734fd4ffc6b1a590 Mon Sep 17 00:00:00 2001 From: stoffu Date: Thu, 15 Nov 2018 00:18:31 +0900 Subject: [PATCH 0754/1404] cmake: move Boost_LOCALE_LIBRARY out of ICU_LIBRARIES --- CMakeLists.txt | 4 ++-- src/simplewallet/CMakeLists.txt | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 388eee3fb40..311af76dcc0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -898,9 +898,9 @@ if(MINGW) set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wa,-mbig-obj") set(EXTRA_LIBRARIES mswsock;ws2_32;iphlpapi;crypt32;bcrypt) if(DEPENDS) - set(ICU_LIBRARIES ${Boost_LOCALE_LIBRARY} sicuio sicuin sicuuc sicudt sicutu iconv) + set(ICU_LIBRARIES sicuio sicuin sicuuc sicudt sicutu iconv) else() - set(ICU_LIBRARIES ${Boost_LOCALE_LIBRARY} icuio icuin icuuc icudt icutu iconv) + set(ICU_LIBRARIES icuio icuin icuuc icudt icutu iconv) endif() elseif(APPLE OR OPENBSD OR ANDROID) set(EXTRA_LIBRARIES "") diff --git a/src/simplewallet/CMakeLists.txt b/src/simplewallet/CMakeLists.txt index c31cdebde10..e292f85dd18 100644 --- a/src/simplewallet/CMakeLists.txt +++ b/src/simplewallet/CMakeLists.txt @@ -53,6 +53,7 @@ target_link_libraries(simplewallet ${Boost_CHRONO_LIBRARY} ${Boost_PROGRAM_OPTIONS_LIBRARY} ${Boost_FILESYSTEM_LIBRARY} + ${Boost_LOCALE_LIBRARY} ${ICU_LIBRARIES} ${Boost_THREAD_LIBRARY} ${CMAKE_THREAD_LIBS_INIT} From 6d3311acebff2b7b9cfdceb24fc9ee04b79275bb Mon Sep 17 00:00:00 2001 From: stoffu Date: Thu, 15 Nov 2018 00:19:41 +0900 Subject: [PATCH 0755/1404] libwallet_api_tests: add missing dependency on Boost Locale and ICU --- tests/libwallet_api_tests/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/libwallet_api_tests/CMakeLists.txt b/tests/libwallet_api_tests/CMakeLists.txt index ef1b666ed1f..1a9cbc5a630 100644 --- a/tests/libwallet_api_tests/CMakeLists.txt +++ b/tests/libwallet_api_tests/CMakeLists.txt @@ -50,6 +50,8 @@ target_link_libraries(libwallet_api_tests ${Boost_FILESYSTEM_LIBRARY} ${Boost_THREAD_LIBRARY} ${Boost_SYSTEM_LIBRARY} + ${Boost_LOCALE_LIBRARY} + ${ICU_LIBRARIES} ${GTEST_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ${EXTRA_LIBRARIES}) From 79b4dae66fdf0619deeec9d1bd49808f83a70421 Mon Sep 17 00:00:00 2001 From: stoffu Date: Mon, 5 Nov 2018 11:45:48 +0900 Subject: [PATCH 0756/1404] simplewallet: slightly adjust wording when printing multisig seed --- src/simplewallet/simplewallet.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 58d4cdcedc4..fa23ce1674c 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -2906,9 +2906,9 @@ bool simple_wallet::ask_wallet_create_if_needed() */ void simple_wallet::print_seed(const epee::wipeable_string &seed) { - success_msg_writer(true) << "\n" << tr("NOTE: the following 25 words can be used to recover access to your wallet. " + success_msg_writer(true) << "\n" << boost::format(tr("NOTE: the following %s can be used to recover access to your wallet. " "Write them down and store them somewhere safe and secure. Please do not store them in " - "your email or on file storage services outside of your immediate control.\n"); + "your email or on file storage services outside of your immediate control.\n")) % (m_wallet->multisig() ? tr("string") : tr("25 words")); // don't log int space_index = 0; size_t len = seed.size(); From 2b3595d0fe056ec4245ad3177d56c1e92da1ff9e Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 15 Nov 2018 13:51:54 +0000 Subject: [PATCH 0757/1404] various: do not propagate exception through dtor Coverity 189689, 189690, 189692, 189695 --- contrib/epee/include/mlocker.h | 2 +- contrib/epee/src/mlocker.cpp | 3 ++- src/common/http_connection.h | 3 ++- src/wallet/wallet2.cpp | 7 ++++++- 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/contrib/epee/include/mlocker.h b/contrib/epee/include/mlocker.h index d2fc2ed5825..a6d94b3d289 100644 --- a/contrib/epee/include/mlocker.h +++ b/contrib/epee/include/mlocker.h @@ -73,7 +73,7 @@ namespace epee mlocked(const T &&t): T(t) { mlocker::lock(this, sizeof(T)); } mlocked(const mlocked &&mt): T(mt) { mlocker::lock(this, sizeof(T)); } mlocked &operator=(const mlocked &mt) { T::operator=(mt); return *this; } - ~mlocked() { mlocker::unlock(this, sizeof(T)); } + ~mlocked() { try { mlocker::unlock(this, sizeof(T)); } catch (...) { /* do not propagate */ } } }; template diff --git a/contrib/epee/src/mlocker.cpp b/contrib/epee/src/mlocker.cpp index 5573d591a17..eb863b9af69 100644 --- a/contrib/epee/src/mlocker.cpp +++ b/contrib/epee/src/mlocker.cpp @@ -108,7 +108,8 @@ namespace epee mlocker::~mlocker() { - unlock(ptr, len); + try { unlock(ptr, len); } + catch (...) { /* ignore and do not propagate through the dtor */ } } void mlocker::lock(void *ptr, size_t len) diff --git a/src/common/http_connection.h b/src/common/http_connection.h index 9fc6be261d4..554dd832bfb 100644 --- a/src/common/http_connection.h +++ b/src/common/http_connection.h @@ -55,7 +55,8 @@ class t_http_connection { { if (m_ok) { - mp_http_client->disconnect(); + try { mp_http_client->disconnect(); } + catch (...) { /* do not propagate through dtor */ } } } diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 6f919d12c4b..0eda7f52d86 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -815,7 +815,12 @@ wallet_keys_unlocker::~wallet_keys_unlocker() { if (!locked) return; - w.encrypt_keys(key); + try { w.encrypt_keys(key); } + catch (...) + { + MERROR("Failed to re-encrypt wallet keys"); + // do not propagate through dtor, we'd crash + } } wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended): From bd98e99c8095cf05c4d3282c888a87745ad1c559 Mon Sep 17 00:00:00 2001 From: Martijn Otto Date: Thu, 15 Nov 2018 17:29:34 +0100 Subject: [PATCH 0758/1404] Removed a lot of unnecessary includes --- .../epee/include/net/abstract_tcp_server2.h | 1 - .../epee/include/net/abstract_tcp_server2.inl | 5 +-- contrib/epee/include/net/connection_basic.hpp | 13 +------- .../epee/include/net/http_server_impl_base.h | 3 +- contrib/epee/src/connection_basic.cpp | 33 ------------------- contrib/epee/src/net_utils_base.cpp | 2 -- contrib/epee/src/network_throttle-detail.cpp | 16 --------- contrib/epee/src/readline_buffer.cpp | 4 +-- contrib/epee/tests/src/net/test_net.h | 4 ++- src/blockchain_db/blockchain_db.h | 1 - src/blockchain_db/lmdb/db_lmdb.cpp | 2 -- src/checkpoints/checkpoints.cpp | 8 ++--- src/checkpoints/checkpoints.h | 1 - src/common/base58.cpp | 1 - src/common/command_line.cpp | 3 -- src/common/dns_utils.cpp | 2 -- src/common/download.cpp | 3 -- src/common/i18n.cpp | 2 -- src/common/password.cpp | 3 -- src/common/threadpool.cpp | 4 --- src/crypto/crypto.cpp | 1 - src/crypto/crypto.h | 3 -- .../cryptonote_format_utils.cpp | 5 ++- src/cryptonote_basic/miner.cpp | 31 ++++++++--------- src/cryptonote_basic/miner.h | 5 --- src/cryptonote_core/cryptonote_core.cpp | 3 -- src/cryptonote_core/cryptonote_core.h | 1 - .../cryptonote_protocol_handler-base.cpp | 22 ------------- src/daemonizer/posix_fork.cpp | 1 - src/mnemonics/electrum-words.cpp | 8 ----- src/mnemonics/electrum-words.h | 1 - src/p2p/net_node.h | 4 ++- src/ringct/bulletproofs.cc | 1 + src/rpc/zmq_server.cpp | 1 - src/wallet/node_rpc_proxy.cpp | 1 - src/wallet/ringdb.cpp | 1 + src/wallet/wallet2.h | 2 ++ tests/net_load_tests/net_load_tests.h | 2 -- .../epee_levin_protocol_handler_async.cpp | 2 +- 39 files changed, 38 insertions(+), 168 deletions(-) diff --git a/contrib/epee/include/net/abstract_tcp_server2.h b/contrib/epee/include/net/abstract_tcp_server2.h index 3f726a35272..0c2d504d0ef 100644 --- a/contrib/epee/include/net/abstract_tcp_server2.h +++ b/contrib/epee/include/net/abstract_tcp_server2.h @@ -36,7 +36,6 @@ #define _ABSTRACT_TCP_SERVER2_H_ -#include #include #include #include diff --git a/contrib/epee/include/net/abstract_tcp_server2.inl b/contrib/epee/include/net/abstract_tcp_server2.inl index 9b03941ee93..ed66ae72016 100644 --- a/contrib/epee/include/net/abstract_tcp_server2.inl +++ b/contrib/epee/include/net/abstract_tcp_server2.inl @@ -32,16 +32,13 @@ -//#include "net_utils_base.h" -#include +#include #include -#include #include #include #include #include #include // TODO -#include // TODO #include // TODO #include "warnings.h" #include "string_tools.h" diff --git a/contrib/epee/include/net/connection_basic.hpp b/contrib/epee/include/net/connection_basic.hpp index 7e875004705..9b6fc14a760 100644 --- a/contrib/epee/include/net/connection_basic.hpp +++ b/contrib/epee/include/net/connection_basic.hpp @@ -42,22 +42,11 @@ #define INCLUDED_p2p_connection_basic_hpp -#include #include -#include -#include -#include #include +#include #include -#include -#include -#include -#include -#include -#include - -#include #include "net/net_utils_base.h" #include "syncobj.h" diff --git a/contrib/epee/include/net/http_server_impl_base.h b/contrib/epee/include/net/http_server_impl_base.h index 1a97e610a61..5669824c131 100644 --- a/contrib/epee/include/net/http_server_impl_base.h +++ b/contrib/epee/include/net/http_server_impl_base.h @@ -33,7 +33,8 @@ #include #include -#include "net/http_server_cp2.h" +#include "net/abstract_tcp_server2.h" +#include "http_protocol_handler.h" #include "net/http_server_handlers_map2.h" #undef MONERO_DEFAULT_LOG_CATEGORY diff --git a/contrib/epee/src/connection_basic.cpp b/contrib/epee/src/connection_basic.cpp index 9ab4858392f..7d145ee466d 100644 --- a/contrib/epee/src/connection_basic.cpp +++ b/contrib/epee/src/connection_basic.cpp @@ -34,47 +34,15 @@ #include "net/connection_basic.hpp" -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "syncobj.h" - #include "net/net_utils_base.h" #include "misc_log_ex.h" -#include -#include -#include -#include -#include -#include #include #include -#include #include "misc_language.h" #include "pragma_comp_defs.h" -#include -#include #include -#include -#include #include -#include -#include "net/abstract_tcp_server2.h" // TODO: #include "net/network_throttle-detail.hpp" @@ -161,7 +129,6 @@ connection_basic::connection_basic(boost::asio::io_service& io_service, std::ato try { boost::system::error_code e; remote_addr_str = socket_.remote_endpoint(e).address().to_string(); } catch(...){} ; _note("Spawned connection p2p#"<m_peer_number<<" to " << remote_addr_str << " currently we have sockets count:" << m_ref_sock_count); - //boost::filesystem::create_directories("log/dr-monero/net/"); } connection_basic::~connection_basic() noexcept(false) { diff --git a/contrib/epee/src/net_utils_base.cpp b/contrib/epee/src/net_utils_base.cpp index 2f4015e814d..354c3d2c3cb 100644 --- a/contrib/epee/src/net_utils_base.cpp +++ b/contrib/epee/src/net_utils_base.cpp @@ -2,8 +2,6 @@ #include "net/net_utils_base.h" #include "string_tools.h" -#include -#include #include "net/local_ip.h" namespace epee { namespace net_utils diff --git a/contrib/epee/src/network_throttle-detail.cpp b/contrib/epee/src/network_throttle-detail.cpp index 28c85bb7823..b436e07fd70 100644 --- a/contrib/epee/src/network_throttle-detail.cpp +++ b/contrib/epee/src/network_throttle-detail.cpp @@ -32,20 +32,11 @@ /* rfree: implementation for throttle details */ -#include #include #include -#include -#include #include #include -#include -#include -#include -#include -#include -#include #include @@ -53,14 +44,7 @@ #include "net/net_utils_base.h" #include "misc_log_ex.h" -#include -#include -#include #include -#include -#include -#include -#include #include "misc_language.h" #include "pragma_comp_defs.h" #include diff --git a/contrib/epee/src/readline_buffer.cpp b/contrib/epee/src/readline_buffer.cpp index da264471f39..c5949da0a92 100644 --- a/contrib/epee/src/readline_buffer.cpp +++ b/contrib/epee/src/readline_buffer.cpp @@ -1,9 +1,9 @@ #include "readline_buffer.h" #include #include -#include #include -#include +#include +#include #include static void install_line_handler(); diff --git a/contrib/epee/tests/src/net/test_net.h b/contrib/epee/tests/src/net/test_net.h index 04fef089ce6..51b1f1ec695 100644 --- a/contrib/epee/tests/src/net/test_net.h +++ b/contrib/epee/tests/src/net/test_net.h @@ -29,7 +29,9 @@ #include #include -#include "net/levin_server_cp2.h" +#include "net/abstract_tcp_server2.h" +#include "net/levin_protocol_handler.h" +#include "net/levin_protocol_handler_async.h" #include "storages/abstract_invoke.h" namespace epee diff --git a/src/blockchain_db/blockchain_db.h b/src/blockchain_db/blockchain_db.h index 71c46d76b33..c0a2f883b44 100644 --- a/src/blockchain_db/blockchain_db.h +++ b/src/blockchain_db/blockchain_db.h @@ -30,7 +30,6 @@ #pragma once -#include #include #include #include diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp index 1674c40dd73..49d45f4b6ce 100644 --- a/src/blockchain_db/lmdb/db_lmdb.cpp +++ b/src/blockchain_db/lmdb/db_lmdb.cpp @@ -29,10 +29,8 @@ #include #include -#include #include // std::unique_ptr #include // memcpy -#include #include "string_tools.h" #include "file_io_utils.h" diff --git a/src/checkpoints/checkpoints.cpp b/src/checkpoints/checkpoints.cpp index 6251fcc91a3..1807d44d91b 100644 --- a/src/checkpoints/checkpoints.cpp +++ b/src/checkpoints/checkpoints.cpp @@ -28,17 +28,15 @@ // // Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers -#include "include_base_utils.h" - -using namespace epee; - #include "checkpoints.h" #include "common/dns_utils.h" -#include "include_base_utils.h" #include "string_tools.h" #include "storages/portable_storage_template_helper.h" // epee json include #include "serialization/keyvalue_serialization.h" +#include + +using namespace epee; #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "checkpoints" diff --git a/src/checkpoints/checkpoints.h b/src/checkpoints/checkpoints.h index 61be2c27acc..ad2b44d1a80 100644 --- a/src/checkpoints/checkpoints.h +++ b/src/checkpoints/checkpoints.h @@ -30,7 +30,6 @@ #pragma once #include -#include #include "misc_log_ex.h" #include "crypto/hash.h" #include "cryptonote_config.h" diff --git a/src/common/base58.cpp b/src/common/base58.cpp index 75556cad94c..3ad2c44b06b 100644 --- a/src/common/base58.cpp +++ b/src/common/base58.cpp @@ -36,7 +36,6 @@ #include "crypto/hash.h" #include "int-util.h" -#include "util.h" #include "varint.h" namespace tools diff --git a/src/common/command_line.cpp b/src/common/command_line.cpp index 7980b381fee..35135ea18df 100644 --- a/src/common/command_line.cpp +++ b/src/common/command_line.cpp @@ -31,10 +31,7 @@ #include "command_line.h" #include #include -#include #include "common/i18n.h" -#include "cryptonote_config.h" -#include "string_tools.h" namespace command_line { diff --git a/src/common/dns_utils.cpp b/src/common/dns_utils.cpp index f2b27098132..1d38fbf420d 100644 --- a/src/common/dns_utils.cpp +++ b/src/common/dns_utils.cpp @@ -33,12 +33,10 @@ #include #include "include_base_utils.h" #include -#include #include #include #include using namespace epee; -namespace bf = boost::filesystem; #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "net.dns" diff --git a/src/common/download.cpp b/src/common/download.cpp index 6698a5abfe0..58ce0595fb4 100644 --- a/src/common/download.cpp +++ b/src/common/download.cpp @@ -29,10 +29,7 @@ #include #include #include -#include #include -#include "cryptonote_config.h" -#include "include_base_utils.h" #include "file_io_utils.h" #include "net/http_client.h" #include "download.h" diff --git a/src/common/i18n.cpp b/src/common/i18n.cpp index 4a89876fbc2..ffe8d8b52dd 100644 --- a/src/common/i18n.cpp +++ b/src/common/i18n.cpp @@ -31,9 +31,7 @@ #include #include #include -#include "include_base_utils.h" #include "file_io_utils.h" -#include "common/util.h" #include "common/i18n.h" #include "translation_files.h" diff --git a/src/common/password.cpp b/src/common/password.cpp index b3c51128f59..5f5cb800ab0 100644 --- a/src/common/password.cpp +++ b/src/common/password.cpp @@ -31,7 +31,6 @@ #include "password.h" #include -#include #include #if defined(_WIN32) @@ -42,8 +41,6 @@ #include #endif -#include "memwipe.h" - #define EOT 0x4 namespace diff --git a/src/common/threadpool.cpp b/src/common/threadpool.cpp index 37825e31d07..cbf7163c512 100644 --- a/src/common/threadpool.cpp +++ b/src/common/threadpool.cpp @@ -28,10 +28,6 @@ #include "misc_log_ex.h" #include "common/threadpool.h" -#include -#include -#include - #include "cryptonote_config.h" #include "common/util.h" diff --git a/src/crypto/crypto.cpp b/src/crypto/crypto.cpp index ad7721cf052..ddf072f68ac 100644 --- a/src/crypto/crypto.cpp +++ b/src/crypto/crypto.cpp @@ -34,7 +34,6 @@ #include #include #include -#include #include #include #include diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h index 33cc0a25a3b..f22df123077 100644 --- a/src/crypto/crypto.h +++ b/src/crypto/crypto.h @@ -32,14 +32,11 @@ #include #include -#include -#include #include #include #include #include "common/pod-class.h" -#include "common/util.h" #include "memwipe.h" #include "mlocker.h" #include "generic-ops.h" diff --git a/src/cryptonote_basic/cryptonote_format_utils.cpp b/src/cryptonote_basic/cryptonote_format_utils.cpp index e26aac76b17..d41bc1087a2 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.cpp +++ b/src/cryptonote_basic/cryptonote_format_utils.cpp @@ -28,9 +28,6 @@ // // Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers -#include "include_base_utils.h" -using namespace epee; - #include #include #include "wipeable_string.h" @@ -42,6 +39,8 @@ using namespace epee; #include "crypto/hash.h" #include "ringct/rctSigs.h" +using namespace epee; + #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "cn" diff --git a/src/cryptonote_basic/miner.cpp b/src/cryptonote_basic/miner.cpp index d8ca2dd3576..9bc99f05376 100644 --- a/src/cryptonote_basic/miner.cpp +++ b/src/cryptonote_basic/miner.cpp @@ -33,8 +33,6 @@ #include #include #include -#include -#include "include_base_utils.h" #include "misc_language.h" #include "syncobj.h" #include "cryptonote_basic_impl.h" @@ -54,19 +52,22 @@ #include #include #include -#endif - -#ifdef __FreeBSD__ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#elif defined(__linux__) + #include + #include + #include + #include +#elif defined(__FreeBSD__) + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include #endif #undef MONERO_DEFAULT_LOG_CATEGORY diff --git a/src/cryptonote_basic/miner.h b/src/cryptonote_basic/miner.h index 2bff784c7ed..e16d9f3b8bb 100644 --- a/src/cryptonote_basic/miner.h +++ b/src/cryptonote_basic/miner.h @@ -38,11 +38,6 @@ #include "math_helper.h" #ifdef _WIN32 #include -#elif defined(__linux__) -#include -#include -#include -#include #endif namespace cryptonote diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index c405c996a1e..00537e8b720 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -30,13 +30,11 @@ #include -#include "include_base_utils.h" #include "string_tools.h" using namespace epee; #include #include "cryptonote_core.h" -#include "common/command_line.h" #include "common/util.h" #include "common/updates.h" #include "common/download.h" @@ -45,7 +43,6 @@ using namespace epee; #include "warnings.h" #include "crypto/crypto.h" #include "cryptonote_config.h" -#include "cryptonote_tx_utils.h" #include "misc_language.h" #include "file_io_utils.h" #include diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index 4eca2a57ba2..7ddba77b280 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -34,7 +34,6 @@ #include #include -#include #include "cryptonote_protocol/cryptonote_protocol_handler_common.h" #include "storages/portable_storage_template_helper.h" diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler-base.cpp b/src/cryptonote_protocol/cryptonote_protocol_handler-base.cpp index c9fd40d88f8..6d9ad9028a2 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler-base.cpp +++ b/src/cryptonote_protocol/cryptonote_protocol_handler-base.cpp @@ -30,20 +30,8 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#include #include #include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include #include @@ -51,24 +39,14 @@ #include "net/net_utils_base.h" #include "misc_log_ex.h" -#include -#include -#include #include -#include -#include #include #include #include "misc_language.h" #include "pragma_comp_defs.h" -#include -#include #include -#include -#include - #include "cryptonote_protocol_handler.h" #include "net/network_throttle.hpp" diff --git a/src/daemonizer/posix_fork.cpp b/src/daemonizer/posix_fork.cpp index 3cbee9c5147..5af4e1a4a3b 100644 --- a/src/daemonizer/posix_fork.cpp +++ b/src/daemonizer/posix_fork.cpp @@ -12,7 +12,6 @@ #include #include #include -#include #ifndef TMPDIR #define TMPDIR "/tmp" diff --git a/src/mnemonics/electrum-words.cpp b/src/mnemonics/electrum-words.cpp index 94071c23ccc..e6d2a6b76cc 100644 --- a/src/mnemonics/electrum-words.cpp +++ b/src/mnemonics/electrum-words.cpp @@ -37,22 +37,14 @@ */ #include -#include -#include #include #include #include -#include #include "wipeable_string.h" #include "misc_language.h" -#include "crypto/crypto.h" // for declaration of crypto::secret_key -#include #include "common/int-util.h" #include "mnemonics/electrum-words.h" -#include -#include #include -#include #include "chinese_simplified.h" #include "english.h" diff --git a/src/mnemonics/electrum-words.h b/src/mnemonics/electrum-words.h index 5401b977993..60d2c5f15f8 100644 --- a/src/mnemonics/electrum-words.h +++ b/src/mnemonics/electrum-words.h @@ -41,7 +41,6 @@ #include #include -#include #include "crypto/crypto.h" // for declaration of crypto::secret_key namespace epee { class wipeable_string; } diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h index 90e2f78b1a1..19908880faf 100644 --- a/src/p2p/net_node.h +++ b/src/p2p/net_node.h @@ -38,7 +38,9 @@ #include "cryptonote_config.h" #include "warnings.h" -#include "net/levin_server_cp2.h" +#include "net/abstract_tcp_server2.h" +#include "net/levin_protocol_handler.h" +#include "net/levin_protocol_handler_async.h" #include "p2p_protocol_defs.h" #include "storages/levin_abstract_invoke2.h" #include "net_peerlist.h" diff --git a/src/ringct/bulletproofs.cc b/src/ringct/bulletproofs.cc index bed48769a37..63365613dfd 100644 --- a/src/ringct/bulletproofs.cc +++ b/src/ringct/bulletproofs.cc @@ -30,6 +30,7 @@ #include #include +#include #include "misc_log_ex.h" #include "common/perf_timer.h" #include "cryptonote_config.h" diff --git a/src/rpc/zmq_server.cpp b/src/rpc/zmq_server.cpp index edd3e666968..a2ff76668e2 100644 --- a/src/rpc/zmq_server.cpp +++ b/src/rpc/zmq_server.cpp @@ -27,7 +27,6 @@ // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "zmq_server.h" -#include namespace cryptonote { diff --git a/src/wallet/node_rpc_proxy.cpp b/src/wallet/node_rpc_proxy.cpp index 346c052b576..605531e59f8 100644 --- a/src/wallet/node_rpc_proxy.cpp +++ b/src/wallet/node_rpc_proxy.cpp @@ -28,7 +28,6 @@ #include "node_rpc_proxy.h" #include "rpc/core_rpc_server_commands_defs.h" -#include "common/json_util.h" #include "storages/http_abstract_invoke.h" using namespace epee; diff --git a/src/wallet/ringdb.cpp b/src/wallet/ringdb.cpp index f562d6c0687..b69022af422 100644 --- a/src/wallet/ringdb.cpp +++ b/src/wallet/ringdb.cpp @@ -30,6 +30,7 @@ #include #include #include +#include "common/util.h" #include "misc_log_ex.h" #include "misc_language.h" #include "wallet_errors.h" diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index dbfd45c5359..d3435f0d517 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -37,6 +37,7 @@ #include #include #include +#include #include #include "include_base_utils.h" @@ -49,6 +50,7 @@ #include "cryptonote_basic/cryptonote_format_utils.h" #include "cryptonote_core/cryptonote_tx_utils.h" #include "common/unordered_containers_boost_serialization.h" +#include "common/util.h" #include "crypto/chacha.h" #include "crypto/hash.h" #include "ringct/rctTypes.h" diff --git a/tests/net_load_tests/net_load_tests.h b/tests/net_load_tests/net_load_tests.h index 7f3c6dfe9da..7e92c21b9d3 100644 --- a/tests/net_load_tests/net_load_tests.h +++ b/tests/net_load_tests/net_load_tests.h @@ -137,7 +137,6 @@ namespace net_load_tests public: open_close_test_helper(test_tcp_server& tcp_server, size_t open_request_target, size_t max_opened_connection_count) : m_tcp_server(tcp_server) - , m_open_request_target(open_request_target) , m_max_opened_connection_count(max_opened_connection_count) , m_opened_connection_count(0) , m_next_opened_conn_idx(0) @@ -203,7 +202,6 @@ namespace net_load_tests private: test_tcp_server& m_tcp_server; - size_t m_open_request_target; size_t m_max_opened_connection_count; std::atomic m_opened_connection_count; std::atomic m_next_opened_conn_idx; diff --git a/tests/unit_tests/epee_levin_protocol_handler_async.cpp b/tests/unit_tests/epee_levin_protocol_handler_async.cpp index 72d8f3205ea..10e62c16723 100644 --- a/tests/unit_tests/epee_levin_protocol_handler_async.cpp +++ b/tests/unit_tests/epee_levin_protocol_handler_async.cpp @@ -294,7 +294,7 @@ TEST_F(positive_test_connection_to_levin_protocol_handler_calls, handler_initial TEST_F(positive_test_connection_to_levin_protocol_handler_calls, concurent_handler_initialization_and_destruction_is_correct) { const size_t connection_count = 10000; - auto create_and_destroy_connections = [this, connection_count]() + auto create_and_destroy_connections = [this]() { std::vector connections(connection_count); for (size_t i = 0; i < connection_count; ++i) From c5ee14ae6e0a70ec229b1b4077326bf7e9953c59 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 7 Nov 2018 21:56:13 +0000 Subject: [PATCH 0759/1404] json_archive: initialize inner_array_size in ctor Coverity 136581 --- src/serialization/json_archive.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/serialization/json_archive.h b/src/serialization/json_archive.h index f906b5d3b1c..04436c21c0f 100644 --- a/src/serialization/json_archive.h +++ b/src/serialization/json_archive.h @@ -113,7 +113,7 @@ struct json_archive; template <> struct json_archive : public json_archive_base { - json_archive(stream_type &s, bool indent = false) : base_type(s, indent) { } + json_archive(stream_type &s, bool indent = false) : base_type(s, indent), inner_array_size_(0) { } template static auto promote_to_printable_integer_type(T v) -> decltype(+v) From f6187cd8111e5f7e812d7a153f8cd64ac2c04cb9 Mon Sep 17 00:00:00 2001 From: Howard Chu Date: Wed, 14 Nov 2018 21:56:32 +0000 Subject: [PATCH 0760/1404] epee: speed up parse_hexstr_to_binbuff a little --- contrib/epee/include/string_tools.h | 50 ++++++++++++++++++++--------- 1 file changed, 35 insertions(+), 15 deletions(-) diff --git a/contrib/epee/include/string_tools.h b/contrib/epee/include/string_tools.h index aba065cc79c..6a063cc36d6 100644 --- a/contrib/epee/include/string_tools.h +++ b/contrib/epee/include/string_tools.h @@ -59,6 +59,26 @@ #pragma comment (lib, "Rpcrt4.lib") #endif +static const constexpr unsigned char isx[256] = +{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 10, 11, 12, 13, 14, 15, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 10, 11, 12, 13, 14, 15, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +}; + namespace epee { namespace string_tools @@ -98,30 +118,30 @@ namespace string_tools } //---------------------------------------------------------------------------- template - bool parse_hexstr_to_binbuff(const std::basic_string& s, std::basic_string& res, bool allow_partial_byte = false) + bool parse_hexstr_to_binbuff(const std::basic_string& s, std::basic_string& res) { res.clear(); - if (!allow_partial_byte && (s.size() & 1)) + if (s.size() & 1) return false; try { - long v = 0; - for(size_t i = 0; i < (s.size() + 1) / 2; i++) + res.resize(s.size() / 2); + unsigned char *dst = (unsigned char *)res.data(); + const unsigned char *src = (const unsigned char *)s.data(); + for(size_t i = 0; i < s.size(); i += 2) { - CharT byte_str[3]; - size_t copied = s.copy(byte_str, 2, 2 * i); - byte_str[copied] = CharT(0); - CharT* endptr; - v = strtoul(byte_str, &endptr, 16); - if (v < 0 || 0xFF < v || endptr != byte_str + copied) - { - return false; - } - res.push_back(static_cast(v)); + int tmp = *src++; + tmp = isx[tmp]; + if (tmp == 0xff) return false; + int t2 = *src++; + t2 = isx[t2]; + if (t2 == 0xff) return false; + *dst++ = (tmp << 4) | t2; } return true; - }catch(...) + } + catch(...) { return false; } From 6671110ca3db97f1169dd853fbb22ad725708e4a Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 15 Nov 2018 12:40:55 +0000 Subject: [PATCH 0761/1404] unit_tests: add a test for parse_hexstr_to_binbuff --- tests/unit_tests/epee_utils.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/unit_tests/epee_utils.cpp b/tests/unit_tests/epee_utils.cpp index c2b0b764707..b3c812dca9c 100644 --- a/tests/unit_tests/epee_utils.cpp +++ b/tests/unit_tests/epee_utils.cpp @@ -456,6 +456,19 @@ TEST(StringTools, PodToHex) ); } +TEST(StringTools, ParseHex) +{ + static const char data[] = "a10b68c2"; + for (size_t i = 0; i < sizeof(data); i += 2) + { + std::string res; + ASSERT_TRUE(epee::string_tools::parse_hexstr_to_binbuff(std::string(data, i), res)); + std::string hex = epee::string_tools::buff_to_hex_nodelimer(res); + ASSERT_EQ(hex.size(), i); + ASSERT_EQ(memcmp(data, hex.data(), i), 0); + } +} + TEST(StringTools, GetIpString) { EXPECT_EQ( From b36353e2681825474a9ddc6a6ccfe6098995a9f5 Mon Sep 17 00:00:00 2001 From: xiphon Date: Thu, 15 Nov 2018 23:32:42 +0000 Subject: [PATCH 0762/1404] unit_tests: add some hex parsing test for non hex input --- tests/unit_tests/epee_utils.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/unit_tests/epee_utils.cpp b/tests/unit_tests/epee_utils.cpp index b3c812dca9c..c384ce9a547 100644 --- a/tests/unit_tests/epee_utils.cpp +++ b/tests/unit_tests/epee_utils.cpp @@ -469,6 +469,22 @@ TEST(StringTools, ParseHex) } } +TEST(StringTools, ParseNotHex) +{ + std::string res; + for (size_t i = 0; i < 256; ++i) + { + std::string inputHexString = std::string(2, static_cast(i)); + if ((i >= '0' && i <= '9') || (i >= 'A' && i <= 'F') || (i >= 'a' && i <= 'f')) { + ASSERT_TRUE(epee::string_tools::parse_hexstr_to_binbuff(inputHexString, res)); + } else { + ASSERT_FALSE(epee::string_tools::parse_hexstr_to_binbuff(inputHexString, res)); + } + } + + ASSERT_FALSE(epee::string_tools::parse_hexstr_to_binbuff(std::string("a"), res)); +} + TEST(StringTools, GetIpString) { EXPECT_EQ( From 6f2497bc7ac1865329527947afd6dacb0b1b6a06 Mon Sep 17 00:00:00 2001 From: doy-lee Date: Fri, 16 Nov 2018 15:32:05 +1100 Subject: [PATCH 0763/1404] Don't cache nettype in core_rpc_server use m_core This can go out of sync with m_core's nettype if you run in fakechain mode since entering fakechain mode is done through code not the command line and core_rpc_server only looks at the command line to figure out the nettype. --- src/daemon/daemon.cpp | 7 ++----- src/daemon/rpc.h | 3 +-- src/rpc/core_rpc_server.cpp | 30 +++++++++++++++++------------- src/rpc/core_rpc_server.h | 3 +-- 4 files changed, 21 insertions(+), 22 deletions(-) diff --git a/src/daemon/daemon.cpp b/src/daemon/daemon.cpp index 49d6d49cf71..13478f39dc2 100644 --- a/src/daemon/daemon.cpp +++ b/src/daemon/daemon.cpp @@ -75,18 +75,15 @@ struct t_internals { protocol.set_p2p_endpoint(p2p.get()); core.set_protocol(protocol.get()); - const auto testnet = command_line::get_arg(vm, cryptonote::arg_testnet_on); - const auto stagenet = command_line::get_arg(vm, cryptonote::arg_stagenet_on); - const auto regtest = command_line::get_arg(vm, cryptonote::arg_regtest_on); const auto restricted = command_line::get_arg(vm, cryptonote::core_rpc_server::arg_restricted_rpc); const auto main_rpc_port = command_line::get_arg(vm, cryptonote::core_rpc_server::arg_rpc_bind_port); - rpcs.emplace_back(new t_rpc{vm, core, p2p, restricted, testnet ? cryptonote::TESTNET : stagenet ? cryptonote::STAGENET : regtest ? cryptonote::FAKECHAIN : cryptonote::MAINNET, main_rpc_port, "core"}); + rpcs.emplace_back(new t_rpc{vm, core, p2p, restricted, main_rpc_port, "core"}); auto restricted_rpc_port_arg = cryptonote::core_rpc_server::arg_rpc_restricted_bind_port; if(!command_line::is_arg_defaulted(vm, restricted_rpc_port_arg)) { auto restricted_rpc_port = command_line::get_arg(vm, restricted_rpc_port_arg); - rpcs.emplace_back(new t_rpc{vm, core, p2p, true, testnet ? cryptonote::TESTNET : stagenet ? cryptonote::STAGENET : cryptonote::MAINNET, restricted_rpc_port, "restricted"}); + rpcs.emplace_back(new t_rpc{vm, core, p2p, true, restricted_rpc_port, "restricted"}); } } }; diff --git a/src/daemon/rpc.h b/src/daemon/rpc.h index 9621b0d0154..37dffc097a9 100644 --- a/src/daemon/rpc.h +++ b/src/daemon/rpc.h @@ -54,7 +54,6 @@ class t_rpc final , t_core & core , t_p2p & p2p , const bool restricted - , const cryptonote::network_type nettype , const std::string & port , const std::string & description ) @@ -62,7 +61,7 @@ class t_rpc final { MGINFO("Initializing " << m_description << " RPC server..."); - if (!m_server.init(vm, restricted, nettype, port)) + if (!m_server.init(vm, restricted, port)) { throw std::runtime_error("Failed to initialize " + m_description + " RPC server."); } diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index c2e71bef8b9..6bf34e9fd41 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -91,12 +91,10 @@ namespace cryptonote bool core_rpc_server::init( const boost::program_options::variables_map& vm , const bool restricted - , const network_type nettype , const std::string& port ) { m_restricted = restricted; - m_nettype = nettype; m_net_server.set_threads_prefix("RPC"); auto rpc_config = cryptonote::rpc_args::process(vm); @@ -191,10 +189,13 @@ namespace cryptonote res.rpc_connections_count = get_connections_count(); res.white_peerlist_size = m_p2p.get_peerlist_manager().get_white_peers_count(); res.grey_peerlist_size = m_p2p.get_peerlist_manager().get_gray_peers_count(); - res.mainnet = m_nettype == MAINNET; - res.testnet = m_nettype == TESTNET; - res.stagenet = m_nettype == STAGENET; - res.nettype = m_nettype == MAINNET ? "mainnet" : m_nettype == TESTNET ? "testnet" : m_nettype == STAGENET ? "stagenet" : "fakechain"; + + cryptonote::network_type net_type = nettype(); + res.mainnet = net_type == MAINNET; + res.testnet = net_type == TESTNET; + res.stagenet = net_type == STAGENET; + res.nettype = net_type == MAINNET ? "mainnet" : net_type == TESTNET ? "testnet" : net_type == STAGENET ? "stagenet" : "fakechain"; + res.cumulative_difficulty = m_core.get_blockchain_storage().get_db().get_block_cumulative_difficulty(res.height - 1); res.block_size_limit = res.block_weight_limit = m_core.get_blockchain_storage().get_current_cumulative_block_weight_limit(); res.block_size_median = res.block_weight_median = m_core.get_blockchain_storage().get_current_cumulative_block_weight_median(); @@ -750,7 +751,7 @@ namespace cryptonote PERF_TIMER(on_start_mining); CHECK_CORE_READY(); cryptonote::address_parse_info info; - if(!get_account_address_from_str(info, m_nettype, req.miner_address)) + if(!get_account_address_from_str(info, nettype(), req.miner_address)) { res.status = "Failed, wrong address"; LOG_PRINT_L0(res.status); @@ -831,7 +832,7 @@ namespace cryptonote res.speed = lMiner.get_speed(); res.threads_count = lMiner.get_threads_count(); const account_public_address& lMiningAdr = lMiner.get_mining_address(); - res.address = get_account_address_as_str(m_nettype, false, lMiningAdr); + res.address = get_account_address_as_str(nettype(), false, lMiningAdr); } res.status = CORE_RPC_STATUS_OK; @@ -1064,7 +1065,7 @@ namespace cryptonote cryptonote::address_parse_info info; - if(!req.wallet_address.size() || !cryptonote::get_account_address_from_str(info, m_nettype, req.wallet_address)) + if(!req.wallet_address.size() || !cryptonote::get_account_address_from_str(info, nettype(), req.wallet_address)) { error_resp.code = CORE_RPC_ERROR_CODE_WRONG_WALLET_ADDRESS; error_resp.message = "Failed to parse wallet address"; @@ -1590,10 +1591,13 @@ namespace cryptonote res.rpc_connections_count = get_connections_count(); res.white_peerlist_size = m_p2p.get_peerlist_manager().get_white_peers_count(); res.grey_peerlist_size = m_p2p.get_peerlist_manager().get_gray_peers_count(); - res.mainnet = m_nettype == MAINNET; - res.testnet = m_nettype == TESTNET; - res.stagenet = m_nettype == STAGENET; - res.nettype = m_nettype == MAINNET ? "mainnet" : m_nettype == TESTNET ? "testnet" : m_nettype == STAGENET ? "stagenet" : "fakechain"; + + cryptonote::network_type net_type = nettype(); + res.mainnet = net_type == MAINNET; + res.testnet = net_type == TESTNET; + res.stagenet = net_type == STAGENET; + res.nettype = net_type == MAINNET ? "mainnet" : net_type == TESTNET ? "testnet" : net_type == STAGENET ? "stagenet" : "fakechain"; + res.cumulative_difficulty = m_core.get_blockchain_storage().get_db().get_block_cumulative_difficulty(res.height - 1); res.block_size_limit = res.block_weight_limit = m_core.get_blockchain_storage().get_current_cumulative_block_weight_limit(); res.block_size_median = res.block_weight_median = m_core.get_blockchain_storage().get_current_cumulative_block_weight_median(); diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h index 3ba882b2355..8f0d6112c16 100644 --- a/src/rpc/core_rpc_server.h +++ b/src/rpc/core_rpc_server.h @@ -70,10 +70,9 @@ namespace cryptonote bool init( const boost::program_options::variables_map& vm, const bool restricted, - const network_type nettype, const std::string& port ); - network_type nettype() const { return m_nettype; } + network_type nettype() const { return m_core.get_nettype(); } CHAIN_HTTP_TO_MAP2(connection_context); //forward http requests to uri map From b39fdf8ebe5a19d43be53813f93f107b7410f39e Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 31 Oct 2018 13:27:51 +0000 Subject: [PATCH 0764/1404] slow-hash: fix for big endian --- src/crypto/slow-hash.c | 41 +++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/src/crypto/slow-hash.c b/src/crypto/slow-hash.c index ed61e101736..0e753f1d93b 100644 --- a/src/crypto/slow-hash.c +++ b/src/crypto/slow-hash.c @@ -109,8 +109,8 @@ extern void aesb_pseudo_round(const uint8_t *in, uint8_t *out, const uint8_t *ex memcpy(b + AES_BLOCK_SIZE, state.hs.b + 64, AES_BLOCK_SIZE); \ xor64(b + AES_BLOCK_SIZE, state.hs.b + 80); \ xor64(b + AES_BLOCK_SIZE + 8, state.hs.b + 88); \ - division_result = state.hs.w[12]; \ - sqrt_result = state.hs.w[13]; \ + division_result = SWAP64LE(state.hs.w[12]); \ + sqrt_result = SWAP64LE(state.hs.w[13]); \ } while (0) #define VARIANT2_SHUFFLE_ADD_SSE2(base_ptr, offset) \ @@ -145,30 +145,31 @@ extern void aesb_pseudo_round(const uint8_t *in, uint8_t *out, const uint8_t *ex const uint64_t chunk1_old[2] = { chunk1[0], chunk1[1] }; \ \ uint64_t b1[2]; \ - memcpy(b1, b + 16, 16); \ - chunk1[0] = chunk3[0] + b1[0]; \ - chunk1[1] = chunk3[1] + b1[1]; \ + memcpy_swap64le(b1, b + 16, 2); \ + chunk1[0] = SWAP64LE(chunk3[0] + b1[0]); \ + chunk1[1] = SWAP64LE(chunk3[1] + b1[1]); \ \ uint64_t a0[2]; \ - memcpy(a0, a, 16); \ - chunk3[0] = chunk2[0] + a0[0]; \ - chunk3[1] = chunk2[1] + a0[1]; \ + memcpy_swap64le(a0, a, 2); \ + chunk3[0] = SWAP64LE(chunk2[0] + a0[0]); \ + chunk3[1] = SWAP64LE(chunk2[1] + a0[1]); \ \ uint64_t b0[2]; \ - memcpy(b0, b, 16); \ - chunk2[0] = chunk1_old[0] + b0[0]; \ - chunk2[1] = chunk1_old[1] + b0[1]; \ + memcpy_swap64le(b0, b, 2); \ + chunk2[0] = SWAP64LE(chunk1_old[0] + b0[0]); \ + chunk2[1] = SWAP64LE(chunk1_old[1] + b0[1]); \ } while (0) #define VARIANT2_INTEGER_MATH_DIVISION_STEP(b, ptr) \ - ((uint64_t*)(b))[0] ^= division_result ^ (sqrt_result << 32); \ + uint64_t tmpx = division_result ^ (sqrt_result << 32); \ + ((uint64_t*)(b))[0] ^= SWAP64LE(tmpx); \ { \ - const uint64_t dividend = ((uint64_t*)(ptr))[1]; \ - const uint32_t divisor = (((uint64_t*)(ptr))[0] + (uint32_t)(sqrt_result << 1)) | 0x80000001UL; \ + const uint64_t dividend = SWAP64LE(((uint64_t*)(ptr))[1]); \ + const uint32_t divisor = (SWAP64LE(((uint64_t*)(ptr))[0]) + (uint32_t)(sqrt_result << 1)) | 0x80000001UL; \ division_result = ((uint32_t)(dividend / divisor)) + \ (((uint64_t)(dividend % divisor)) << 32); \ } \ - const uint64_t sqrt_input = ((uint64_t*)(ptr))[0] + division_result + const uint64_t sqrt_input = SWAP64LE(((uint64_t*)(ptr))[0]) + division_result #define VARIANT2_INTEGER_MATH_SSE2(b, ptr) \ do if (variant >= 2) \ @@ -207,10 +208,10 @@ extern void aesb_pseudo_round(const uint8_t *in, uint8_t *out, const uint8_t *ex #define VARIANT2_2() \ do if (variant >= 2) \ { \ - *U64(hp_state + (j ^ 0x10)) ^= hi; \ - *(U64(hp_state + (j ^ 0x10)) + 1) ^= lo; \ - hi ^= *U64(hp_state + (j ^ 0x20)); \ - lo ^= *(U64(hp_state + (j ^ 0x20)) + 1); \ + *U64(hp_state + (j ^ 0x10)) ^= SWAP64LE(hi); \ + *(U64(hp_state + (j ^ 0x10)) + 1) ^= SWAP64LE(lo); \ + hi ^= SWAP64LE(*U64(hp_state + (j ^ 0x20))); \ + lo ^= SWAP64LE(*(U64(hp_state + (j ^ 0x20)) + 1)); \ } while (0) @@ -1408,7 +1409,7 @@ static void (*const extra_hashes[4])(const void *, size_t, char *) = { hash_extra_blake, hash_extra_groestl, hash_extra_jh, hash_extra_skein }; -static size_t e2i(const uint8_t* a, size_t count) { return (*((uint64_t*)a) / AES_BLOCK_SIZE) & (count - 1); } +static size_t e2i(const uint8_t* a, size_t count) { return (SWAP64LE(*((uint64_t*)a)) / AES_BLOCK_SIZE) & (count - 1); } static void mul(const uint8_t* a, const uint8_t* b, uint8_t* res) { uint64_t a0, b0; From 2a48c2a2868ca8f2b55468c1de71336c0e440d8b Mon Sep 17 00:00:00 2001 From: xiphon Date: Fri, 2 Nov 2018 09:33:30 +0000 Subject: [PATCH 0765/1404] slow-hash: some more big endian fixes --- src/crypto/slow-hash.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/crypto/slow-hash.c b/src/crypto/slow-hash.c index 0e753f1d93b..dcbabccab8e 100644 --- a/src/crypto/slow-hash.c +++ b/src/crypto/slow-hash.c @@ -146,18 +146,18 @@ extern void aesb_pseudo_round(const uint8_t *in, uint8_t *out, const uint8_t *ex \ uint64_t b1[2]; \ memcpy_swap64le(b1, b + 16, 2); \ - chunk1[0] = SWAP64LE(chunk3[0] + b1[0]); \ - chunk1[1] = SWAP64LE(chunk3[1] + b1[1]); \ + chunk1[0] = SWAP64LE(SWAP64LE(chunk3[0]) + b1[0]); \ + chunk1[1] = SWAP64LE(SWAP64LE(chunk3[1]) + b1[1]); \ \ uint64_t a0[2]; \ memcpy_swap64le(a0, a, 2); \ - chunk3[0] = SWAP64LE(chunk2[0] + a0[0]); \ - chunk3[1] = SWAP64LE(chunk2[1] + a0[1]); \ + chunk3[0] = SWAP64LE(SWAP64LE(chunk2[0]) + a0[0]); \ + chunk3[1] = SWAP64LE(SWAP64LE(chunk2[1]) + a0[1]); \ \ uint64_t b0[2]; \ memcpy_swap64le(b0, b, 2); \ - chunk2[0] = SWAP64LE(chunk1_old[0] + b0[0]); \ - chunk2[1] = SWAP64LE(chunk1_old[1] + b0[1]); \ + chunk2[0] = SWAP64LE(SWAP64LE(chunk1_old[0]) + b0[0]); \ + chunk2[1] = SWAP64LE(SWAP64LE(chunk1_old[1]) + b0[1]); \ } while (0) #define VARIANT2_INTEGER_MATH_DIVISION_STEP(b, ptr) \ From 872c7eb26abec64478e8c2f68e13f12b93b7ed46 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 6 Nov 2018 11:02:27 +0000 Subject: [PATCH 0766/1404] Revert "blockchain: simplify output distribution code" This reverts commit b2bb9312a75781e714acf3c406634b3d4cded418. --- src/cryptonote_core/blockchain.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index f3105114eb6..030798ad7c9 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -1831,10 +1831,15 @@ bool Blockchain::get_output_distribution(uint64_t amount, uint64_t from_height, { std::vector heights; heights.reserve(to_height + 1 - start_height); - for (uint64_t h = start_height; h <= to_height; ++h) + uint64_t real_start_height = start_height > 0 ? start_height-1 : start_height; + for (uint64_t h = real_start_height; h <= to_height; ++h) heights.push_back(h); distribution = m_db->get_block_cumulative_rct_outputs(heights); - base = 0; + if (start_height > 0) + { + base = distribution[0]; + distribution.erase(distribution.begin()); + } return true; } else From 0936dae8a4efc62f4ca54c4872ee2562961372df Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 6 Nov 2018 14:21:57 +0000 Subject: [PATCH 0767/1404] blockchain: remove "0 is height" shortcut from get_output_distribution This prevents asking for just 0, and the RPC layer already does this --- src/cryptonote_core/blockchain.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 030798ad7c9..798f67e090d 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -1823,8 +1823,6 @@ bool Blockchain::get_output_distribution(uint64_t amount, uint64_t from_height, uint64_t db_height = m_db->height(); if (db_height == 0) return false; - if (to_height == 0) - to_height = db_height - 1; if (start_height >= db_height || to_height >= db_height) return false; if (amount == 0) From 3900fb779fbd3ebdb0eeb8c9d574bdd85c35ecf3 Mon Sep 17 00:00:00 2001 From: Tadeas Moravec Date: Fri, 16 Nov 2018 14:53:31 +0000 Subject: [PATCH 0768/1404] Enhance help text for incoming_transfers. --- src/simplewallet/simplewallet.cpp | 8 +++++--- translations/monero.ts | 7 +++++-- translations/monero_fr.ts | 11 +++++++---- translations/monero_it.ts | 9 ++++++--- translations/monero_ja.ts | 7 +++++-- translations/monero_sv.ts | 11 +++++++---- 6 files changed, 35 insertions(+), 18 deletions(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 702ff22cb0f..505e5525e07 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -2347,7 +2347,9 @@ simple_wallet::simple_wallet() m_cmd_binder.set_handler("incoming_transfers", boost::bind(&simple_wallet::show_incoming_transfers, this, _1), tr("incoming_transfers [available|unavailable] [verbose] [index=[,[,...]]]"), - tr("Show the incoming transfers, all or filtered by availability and address index.")); + tr("Show the incoming transfers, all or filtered by availability and address index.\n\n" + "Output format:\n" + "Amount, Spent(\"T\"|\"F\"), \"locked\"|\"unlocked\", RingCT, Global Index, Transaction Hash, Address Index, [Public Key, Key Image] ")); m_cmd_binder.set_handler("payments", boost::bind(&simple_wallet::show_payments, this, _1), tr("payments [ ... ]"), @@ -4435,7 +4437,7 @@ bool simple_wallet::show_incoming_transfers(const std::vector& args { if (args.size() > 3) { - fail_msg_writer() << tr("usage: incoming_transfers [available|unavailable] [verbose] [index=]"); + fail_msg_writer() << tr("usage: incoming_transfers [available|unavailable] [verbose] [index=[,[,...]]]"); return true; } auto local_args = args; @@ -4477,7 +4479,7 @@ bool simple_wallet::show_incoming_transfers(const std::vector& args if (local_args.size() > 0) { - fail_msg_writer() << tr("usage: incoming_transfers [available|unavailable] [verbose] [index=]"); + fail_msg_writer() << tr("usage: incoming_transfers [available|unavailable] [verbose] [index=[,[,...]]]"); return true; } diff --git a/translations/monero.ts b/translations/monero.ts index e28a2a05802..d17317ae912 100644 --- a/translations/monero.ts +++ b/translations/monero.ts @@ -1453,7 +1453,10 @@ This transaction will unlock on block %llu, in approximately %s days (assuming 2 - Show the incoming transfers, all or filtered by availability and address index. + Show the incoming transfers, all or filtered by availability and address index. + +Output format: +Amount, Spent("T"|"F"), "locked"|"unlocked", RingCT, Global Index, Transaction Hash, Address Index, [Public Key, Key Image] @@ -2826,7 +2829,7 @@ your wallet again (your wallet keys are NOT at risk in any case). - usage: incoming_transfers [available|unavailable] [verbose] [index=<N>] + usage: incoming_transfers [available|unavailable] [verbose] [index=<N1>[,<N2>[,...]]] diff --git a/translations/monero_fr.ts b/translations/monero_fr.ts index 27c38ee1011..19a125fdbb8 100644 --- a/translations/monero_fr.ts +++ b/translations/monero_fr.ts @@ -1541,8 +1541,11 @@ Cette transaction sera déverrouillée au bloc %llu, dans approximativement %s j - Show the incoming transfers, all or filtered by availability and address index. - Afficher les transferts entrants, tous ou filtrés par disponibilité et index d'adresse. + Show the incoming transfers, all or filtered by availability and address index. + +Output format: +Amount, Spent("T"|"F"), "locked"|"unlocked", RingCT, Global Index, Transaction Hash, Address Index, [Public Key, Key Image] + Afficher les transferts entrants, tous ou filtrés par disponibilité et index d'adresse. @@ -2271,8 +2274,8 @@ votre portefeuille à nouveau (mais les clés de votre portefeuille ne risquent - usage: incoming_transfers [available|unavailable] [verbose] [index=<N>] - usage: incoming_transfers [available|unavailable] [verbose] [index=<N>] + usage: incoming_transfers [available|unavailable] [verbose] [index=<N1>[,<N2>[,...]]] + usage: incoming_transfers [available|unavailable] [verbose] [index=<N1>[,<N2>[,...]]] diff --git a/translations/monero_it.ts b/translations/monero_it.ts index 5ab96d7dc3d..09872fea80b 100644 --- a/translations/monero_it.ts +++ b/translations/monero_it.ts @@ -1364,8 +1364,11 @@ Questa transazione verrà sbloccata al blocco %llu, in approssimativamente %s gi - Show the incoming transfers, all or filtered by availability and address index. - Mostra i trasferimenti in entrata, tutti o filtrati per disponibilità ed indice di indirizzo. + Show the incoming transfers, all or filtered by availability and address index. + +Output format: +Amount, Spent("T"|"F"), "locked"|"unlocked", RingCT, Global Index, Transaction Hash, Address Index, [Public Key, Key Image] + Mostra i trasferimenti in entrata, tutti o filtrati per disponibilità ed indice di indirizzo. @@ -2191,7 +2194,7 @@ your wallet again (your wallet keys are NOT at risk in any case). - usage: incoming_transfers [available|unavailable] [verbose] [index=<N>] + usage: incoming_transfers [available|unavailable] [verbose] [index=<N1>[,<N2>[,...]]] diff --git a/translations/monero_ja.ts b/translations/monero_ja.ts index 17815d982be..7305b42f8ae 100644 --- a/translations/monero_ja.ts +++ b/translations/monero_ja.ts @@ -1461,7 +1461,10 @@ This transaction will unlock on block %llu, in approximately %s days (assuming 2 - Show the incoming transfers, all or filtered by availability and address index. + Show the incoming transfers, all or filtered by availability and address index. + +Output format: +Amount, Spent("T"|"F"), "locked"|"unlocked", RingCT, Global Index, Transaction Hash, Address Index, [Public Key, Key Image] @@ -2279,7 +2282,7 @@ your wallet again (your wallet keys are NOT at risk in any case). - usage: incoming_transfers [available|unavailable] [verbose] [index=<N>] + usage: incoming_transfers [available|unavailable] [verbose] [index=<N1>[,<N2>[,...]]] diff --git a/translations/monero_sv.ts b/translations/monero_sv.ts index b2387cdb196..26ad43f7bf4 100644 --- a/translations/monero_sv.ts +++ b/translations/monero_sv.ts @@ -1469,8 +1469,11 @@ Denna transaktion låses upp vid block %llu, om ungefär %s dagar (förutsatt en - Show the incoming transfers, all or filtered by availability and address index. - Visa inkommande överföringar: alla eller filtrerade efter tillgänglighet och adressindex. + Show the incoming transfers, all or filtered by availability and address index. + +Output format: +Amount, Spent("T"|"F"), "locked"|"unlocked", RingCT, Global Index, Transaction Hash, Address Index, [Public Key, Key Image] + Visa inkommande överföringar: alla eller filtrerade efter tillgänglighet och adressindex. @@ -2351,8 +2354,8 @@ din plånbok igen (din plånboks nycklar är dock INTE hotade i vilket fall som - usage: incoming_transfers [available|unavailable] [verbose] [index=<N>] - användning: incoming_transfers [available|unavailable] [verbose] [index=<N>] + usage: incoming_transfers [available|unavailable] [verbose] [index=<N1>[,<N2>[,...]]] + användning: incoming_transfers [available|unavailable] [verbose] [index=<N1>[,<N2>[,...]]] From 3998a937e506cef027fa267adce16425ed6193d4 Mon Sep 17 00:00:00 2001 From: Tadeas Moravec Date: Fri, 16 Nov 2018 15:15:35 +0000 Subject: [PATCH 0769/1404] Enhance help text for print_ring. --- src/simplewallet/simplewallet.cpp | 6 ++++-- translations/monero.ts | 7 +++++-- translations/monero_fr.ts | 7 +++++-- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 505e5525e07..1b86e0ec50f 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -1408,7 +1408,7 @@ bool simple_wallet::print_ring(const std::vector &args) crypto::hash txid; if (args.size() != 1) { - fail_msg_writer() << tr("usage: print_ring "); + fail_msg_writer() << tr("usage: print_ring | "); return true; } @@ -2653,7 +2653,9 @@ simple_wallet::simple_wallet() m_cmd_binder.set_handler("print_ring", boost::bind(&simple_wallet::print_ring, this, _1), tr("print_ring | "), - tr("Print the ring(s) used to spend a given key image or transaction (if the ring size is > 1)")); + tr("Print the ring(s) used to spend a given key image or transaction (if the ring size is > 1)\n\n" + "Output format:\n" + "Key Image, \"absolute\", list of rings")); m_cmd_binder.set_handler("set_ring", boost::bind(&simple_wallet::set_ring, this, _1), tr("set_ring | ( absolute|relative [...] )"), diff --git a/translations/monero.ts b/translations/monero.ts index d17317ae912..23eadff843e 100644 --- a/translations/monero.ts +++ b/translations/monero.ts @@ -1196,7 +1196,7 @@ This transaction will unlock on block %llu, in approximately %s days (assuming 2 - usage: print_ring <key_image|txid> + usage: print_ring <key_image> | <txid> @@ -1979,7 +1979,10 @@ Pending or Failed: "failed"|"pending", "o - Print the ring(s) used to spend a given key image or transaction (if the ring size is > 1) + Print the ring(s) used to spend a given key image or transaction (if the ring size is > 1) + +Output format: +Key Image, "absolute", list of rings diff --git a/translations/monero_fr.ts b/translations/monero_fr.ts index 19a125fdbb8..238ba17df6d 100644 --- a/translations/monero_fr.ts +++ b/translations/monero_fr.ts @@ -2987,8 +2987,11 @@ subaddress-lookahead <major>:<minor> - Print the ring(s) used to spend a given key image or transaction (if the ring size is > 1) - Afficher le(s) cercle(s) utilisé(s) pour dépenser une image de clé ou une transaction (si la taille de cercle est > 1) + Print the ring(s) used to spend a given key image or transaction (if the ring size is > 1) + +Output format: +Key Image, "absolute", list of rings + Afficher le(s) cercle(s) utilisé(s) pour dépenser une image de clé ou une transaction (si la taille de cercle est > 1) From 31d80027b5cc59b48205334a9e361c6a75099592 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 6 Nov 2018 14:23:26 +0000 Subject: [PATCH 0770/1404] tests: add unit tests for get_output_distribution --- src/cryptonote_core/blockchain.cpp | 1 + src/rpc/core_rpc_server.cpp | 2 +- src/rpc/daemon_handler.cpp | 2 +- src/rpc/rpc_handler.cpp | 4 +- src/rpc/rpc_handler.h | 2 +- tests/unit_tests/CMakeLists.txt | 1 + tests/unit_tests/hardfork.cpp | 94 +------------ tests/unit_tests/output_distribution.cpp | 171 +++++++++++++++++++++++ tests/unit_tests/testdb.h | 146 +++++++++++++++++++ 9 files changed, 331 insertions(+), 92 deletions(-) create mode 100644 tests/unit_tests/output_distribution.cpp create mode 100644 tests/unit_tests/testdb.h diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 798f67e090d..e80e3f66c2a 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -1805,6 +1805,7 @@ bool Blockchain::get_output_distribution(uint64_t amount, uint64_t from_height, case STAGENET: start_height = stagenet_hard_forks[3].height; break; case TESTNET: start_height = testnet_hard_forks[3].height; break; case MAINNET: start_height = mainnet_hard_forks[3].height; break; + case FAKECHAIN: start_height = 0; break; default: return false; } } diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index c2e71bef8b9..df9eee781ac 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -2125,7 +2125,7 @@ namespace cryptonote const uint64_t req_to_height = req.to_height ? req.to_height : (m_core.get_current_blockchain_height() - 1); for (uint64_t amount: req.amounts) { - auto data = rpc::RpcHandler::get_output_distribution(m_core, amount, req.from_height, req_to_height, req.cumulative); + auto data = rpc::RpcHandler::get_output_distribution([this](uint64_t amount, uint64_t from, uint64_t to, uint64_t &start_height, std::vector &distribution, uint64_t &base) { return m_core.get_output_distribution(amount, from, to, start_height, distribution, base); }, amount, req.from_height, req_to_height, req.cumulative); if (!data) { error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR; diff --git a/src/rpc/daemon_handler.cpp b/src/rpc/daemon_handler.cpp index 8822bd378fc..64a5cc858ac 100644 --- a/src/rpc/daemon_handler.cpp +++ b/src/rpc/daemon_handler.cpp @@ -751,7 +751,7 @@ namespace rpc const uint64_t req_to_height = req.to_height ? req.to_height : (m_core.get_current_blockchain_height() - 1); for (std::uint64_t amount : req.amounts) { - auto data = get_output_distribution(m_core, amount, req.from_height, req_to_height, req.cumulative); + auto data = rpc::RpcHandler::get_output_distribution([this](uint64_t amount, uint64_t from, uint64_t to, uint64_t &start_height, std::vector &distribution, uint64_t &base) { return m_core.get_output_distribution(amount, from, to, start_height, distribution, base); }, amount, req.from_height, req_to_height, req.cumulative); if (!data) { res.distributions.clear(); diff --git a/src/rpc/rpc_handler.cpp b/src/rpc/rpc_handler.cpp index d4beb192858..63664bf8b08 100644 --- a/src/rpc/rpc_handler.cpp +++ b/src/rpc/rpc_handler.cpp @@ -26,7 +26,7 @@ namespace rpc } boost::optional - RpcHandler::get_output_distribution(core& src, std::uint64_t amount, std::uint64_t from_height, std::uint64_t to_height, bool cumulative) + RpcHandler::get_output_distribution(const std::function&, uint64_t&)> &f, uint64_t amount, uint64_t from_height, uint64_t to_height, bool cumulative) { static struct D { @@ -43,7 +43,7 @@ namespace rpc std::vector distribution; std::uint64_t start_height, base; - if (!src.get_output_distribution(amount, from_height, to_height, start_height, distribution, base)) + if (!f(amount, from_height, to_height, start_height, distribution, base)) return boost::none; if (to_height > 0 && to_height >= from_height) diff --git a/src/rpc/rpc_handler.h b/src/rpc/rpc_handler.h index 3cccef78a6f..e0d520408d0 100644 --- a/src/rpc/rpc_handler.h +++ b/src/rpc/rpc_handler.h @@ -56,7 +56,7 @@ class RpcHandler virtual std::string handle(const std::string& request) = 0; static boost::optional - get_output_distribution(core& src, std::uint64_t amount, std::uint64_t from_height, std::uint64_t to_height, bool cumulative); + get_output_distribution(const std::function&, uint64_t&)> &f, uint64_t amount, uint64_t from_height, uint64_t to_height, bool cumulative); }; diff --git a/tests/unit_tests/CMakeLists.txt b/tests/unit_tests/CMakeLists.txt index 7687e3c52fd..a46f11b5ff3 100644 --- a/tests/unit_tests/CMakeLists.txt +++ b/tests/unit_tests/CMakeLists.txt @@ -62,6 +62,7 @@ set(unit_tests_sources multiexp.cpp multisig.cpp notify.cpp + output_distribution.cpp parse_amount.cpp random.cpp serialization.cpp diff --git a/tests/unit_tests/hardfork.cpp b/tests/unit_tests/hardfork.cpp index fc488bb1489..ec8d1d202b8 100644 --- a/tests/unit_tests/hardfork.cpp +++ b/tests/unit_tests/hardfork.cpp @@ -34,101 +34,19 @@ #include "blockchain_db/blockchain_db.h" #include "cryptonote_basic/cryptonote_format_utils.h" #include "cryptonote_basic/hardfork.h" +#include "testdb.h" using namespace cryptonote; #define BLOCKS_PER_YEAR 525960 #define SECONDS_PER_YEAR 31557600 +namespace +{ -class TestDB: public BlockchainDB { +class TestDB: public BaseTestDB { public: - TestDB() {}; - virtual void open(const std::string& filename, const int db_flags = 0) { } - virtual void close() {} - virtual void sync() {} - virtual void safesyncmode(const bool onoff) {} - virtual void reset() {} - virtual std::vector get_filenames() const { return std::vector(); } - virtual bool remove_data_file(const std::string& folder) const { return true; } - virtual std::string get_db_name() const { return std::string(); } - virtual bool lock() { return true; } - virtual void unlock() { } - virtual bool batch_start(uint64_t batch_num_blocks=0, uint64_t batch_bytes=0) { return true; } - virtual void batch_stop() {} - virtual void set_batch_transactions(bool) {} - virtual void block_txn_start(bool readonly=false) {} - virtual void block_txn_stop() {} - virtual void block_txn_abort() {} - virtual void drop_hard_fork_info() {} - virtual bool block_exists(const crypto::hash& h, uint64_t *height) const { return false; } - virtual blobdata get_block_blob_from_height(const uint64_t& height) const { return cryptonote::t_serializable_object_to_blob(get_block_from_height(height)); } - virtual blobdata get_block_blob(const crypto::hash& h) const { return blobdata(); } - virtual bool get_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const { return false; } - virtual bool get_pruned_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const { return false; } - virtual bool get_prunable_tx_hash(const crypto::hash& tx_hash, crypto::hash &prunable_hash) const { return false; } - virtual uint64_t get_block_height(const crypto::hash& h) const { return 0; } - virtual block_header get_block_header(const crypto::hash& h) const { return block_header(); } - virtual uint64_t get_block_timestamp(const uint64_t& height) const { return 0; } - virtual std::vector get_block_cumulative_rct_outputs(const std::vector &heights) const { return {}; } - virtual uint64_t get_top_block_timestamp() const { return 0; } - virtual size_t get_block_weight(const uint64_t& height) const { return 128; } - virtual difficulty_type get_block_cumulative_difficulty(const uint64_t& height) const { return 10; } - virtual difficulty_type get_block_difficulty(const uint64_t& height) const { return 0; } - virtual uint64_t get_block_already_generated_coins(const uint64_t& height) const { return 10000000000; } - virtual crypto::hash get_block_hash_from_height(const uint64_t& height) const { return crypto::hash(); } - virtual std::vector get_blocks_range(const uint64_t& h1, const uint64_t& h2) const { return std::vector(); } - virtual std::vector get_hashes_range(const uint64_t& h1, const uint64_t& h2) const { return std::vector(); } - virtual crypto::hash top_block_hash() const { return crypto::hash(); } - virtual block get_top_block() const { return block(); } virtual uint64_t height() const { return blocks.size(); } - virtual bool tx_exists(const crypto::hash& h) const { return false; } - virtual bool tx_exists(const crypto::hash& h, uint64_t& tx_index) const { return false; } - virtual uint64_t get_tx_unlock_time(const crypto::hash& h) const { return 0; } - virtual transaction get_tx(const crypto::hash& h) const { return transaction(); } - virtual bool get_tx(const crypto::hash& h, transaction &tx) const { return false; } - virtual uint64_t get_tx_count() const { return 0; } - virtual std::vector get_tx_list(const std::vector& hlist) const { return std::vector(); } - virtual uint64_t get_tx_block_height(const crypto::hash& h) const { return 0; } - virtual uint64_t get_num_outputs(const uint64_t& amount) const { return 1; } - virtual uint64_t get_indexing_base() const { return 0; } - virtual output_data_t get_output_key(const uint64_t& amount, const uint64_t& index) { return output_data_t(); } - virtual tx_out_index get_output_tx_and_index_from_global(const uint64_t& index) const { return tx_out_index(); } - virtual tx_out_index get_output_tx_and_index(const uint64_t& amount, const uint64_t& index) const { return tx_out_index(); } - virtual void get_output_tx_and_index(const uint64_t& amount, const std::vector &offsets, std::vector &indices) const {} - virtual void get_output_key(const uint64_t &amount, const std::vector &offsets, std::vector &outputs, bool allow_partial = false) {} - virtual bool can_thread_bulk_indices() const { return false; } - virtual std::vector get_tx_output_indices(const crypto::hash& h) const { return std::vector(); } - virtual std::vector get_tx_amount_output_indices(const uint64_t tx_index) const { return std::vector(); } - virtual bool has_key_image(const crypto::key_image& img) const { return false; } - virtual void remove_block() { blocks.pop_back(); } - virtual uint64_t add_transaction_data(const crypto::hash& blk_hash, const transaction& tx, const crypto::hash& tx_hash, const crypto::hash& tx_prunable_hash) {return 0;} - virtual void remove_transaction_data(const crypto::hash& tx_hash, const transaction& tx) {} - virtual uint64_t add_output(const crypto::hash& tx_hash, const tx_out& tx_output, const uint64_t& local_index, const uint64_t unlock_time, const rct::key *commitment) {return 0;} - virtual void add_tx_amount_output_indices(const uint64_t tx_index, const std::vector& amount_output_indices) {} - virtual void add_spent_key(const crypto::key_image& k_image) {} - virtual void remove_spent_key(const crypto::key_image& k_image) {} - - virtual bool for_all_key_images(std::function) const { return true; } - virtual bool for_blocks_range(const uint64_t&, const uint64_t&, std::function) const { return true; } - virtual bool for_all_transactions(std::function, bool pruned) const { return true; } - virtual bool for_all_outputs(std::function f) const { return true; } - virtual bool for_all_outputs(uint64_t amount, const std::function &f) const { return true; } - virtual bool is_read_only() const { return false; } - virtual std::map> get_output_histogram(const std::vector &amounts, bool unlocked, uint64_t recent_cutoff, uint64_t min_count) const { return std::map>(); } - virtual bool get_output_distribution(uint64_t amount, uint64_t from_height, uint64_t to_height, std::vector &distribution, uint64_t &base) const { return false; } - - virtual void add_txpool_tx(const crypto::hash &txid, const cryptonote::blobdata &blob, const txpool_tx_meta_t& details) {} - virtual void update_txpool_tx(const crypto::hash &txid, const txpool_tx_meta_t& details) {} - virtual uint64_t get_txpool_tx_count(bool include_unrelayed_txes = true) const { return 0; } - virtual bool txpool_has_tx(const crypto::hash &txid) const { return false; } - virtual void remove_txpool_tx(const crypto::hash& txid) {} - virtual bool get_txpool_tx_meta(const crypto::hash& txid, txpool_tx_meta_t &meta) const { return false; } - virtual bool get_txpool_tx_blob(const crypto::hash& txid, cryptonote::blobdata &bd) const { return false; } - virtual uint64_t get_database_size() const { return 0; } - virtual cryptonote::blobdata get_txpool_tx_blob(const crypto::hash& txid) const { return ""; } - virtual bool for_all_txpool_txes(std::function, bool include_blob = false, bool include_unrelayed_txes = false) const { return false; } - virtual void add_block( const block& blk , size_t block_weight , const difficulty_type& cumulative_difficulty @@ -138,6 +56,7 @@ class TestDB: public BlockchainDB { ) { blocks.push_back(blk); } + virtual void remove_block() { blocks.pop_back(); } virtual block get_block_from_height(const uint64_t& height) const { return blocks.at(height); } @@ -149,13 +68,14 @@ class TestDB: public BlockchainDB { virtual uint8_t get_hard_fork_version(uint64_t height) const { return versions.at(height); } - virtual void check_hard_fork_info() {} private: std::vector blocks; std::deque versions; }; +} + static cryptonote::block mkblock(uint8_t version, uint8_t vote) { cryptonote::block b; diff --git a/tests/unit_tests/output_distribution.cpp b/tests/unit_tests/output_distribution.cpp new file mode 100644 index 00000000000..649752ac7c4 --- /dev/null +++ b/tests/unit_tests/output_distribution.cpp @@ -0,0 +1,171 @@ +// Copyright (c) 2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "gtest/gtest.h" +#include "misc_log_ex.h" +#include "rpc/rpc_handler.h" +#include "blockchain_db/blockchain_db.h" +#include "cryptonote_core/cryptonote_core.h" +#include "cryptonote_core/tx_pool.h" +#include "cryptonote_core/blockchain.h" +#include "testdb.h" + +static const uint64_t test_distribution[32] = { + 0, 0, 0, 0, 0, 1, 5, 1, 4, 0, 0, 1, 0, 1, 2, 3, 1, 0, 2, 0, 1, 3, 8, 1, 3, 5, 7, 1, 5, 0, 2, 3 +}; +static const size_t test_distribution_size = sizeof(test_distribution) / sizeof(test_distribution[0]); + +namespace +{ + +class TestDB: public BaseTestDB +{ +public: + TestDB(size_t bc_height = test_distribution_size): blockchain_height(bc_height) { m_open = true; } + virtual uint64_t height() const override { return blockchain_height; } + + std::vector get_block_cumulative_rct_outputs(const std::vector &heights) const override + { + std::vector d; + for (uint64_t h: heights) + { + uint64_t c = 0; + for (uint64_t i = 0; i <= h; ++i) + c += test_distribution[i]; + d.push_back(c); + } + return d; + } + + uint64_t blockchain_height; +}; + +} + +bool get_output_distribution(uint64_t amount, uint64_t from, uint64_t to, uint64_t &start_height, std::vector &distribution, uint64_t &base) +{ + std::unique_ptr bc; + cryptonote::tx_memory_pool txpool(*bc); + bc.reset(new cryptonote::Blockchain(txpool)); + struct get_test_options { + const std::pair hard_forks[2]; + const cryptonote::test_options test_options = { + hard_forks + }; + get_test_options():hard_forks{std::make_pair((uint8_t)1, (uint64_t)0), std::make_pair((uint8_t)0, (uint64_t)0)}{} + } opts; + cryptonote::Blockchain *blockchain = bc.get(); + bool r = blockchain->init(new TestDB(test_distribution_size), cryptonote::FAKECHAIN, true, &opts.test_options, 0, NULL); + return r && bc->get_output_distribution(amount, from, to, start_height, distribution, base); +} + +TEST(output_distribution, extend) +{ + boost::optional res; + + res = cryptonote::rpc::RpcHandler::get_output_distribution(::get_output_distribution, 0, 28, 29, false); + ASSERT_TRUE(res != boost::none); + ASSERT_EQ(res->distribution.size(), 2); + ASSERT_EQ(res->distribution, std::vector({5, 0})); + + res = cryptonote::rpc::RpcHandler::get_output_distribution(::get_output_distribution, 0, 28, 29, true); + ASSERT_TRUE(res != boost::none); + ASSERT_EQ(res->distribution.size(), 2); + ASSERT_EQ(res->distribution, std::vector({55, 55})); + + res = cryptonote::rpc::RpcHandler::get_output_distribution(::get_output_distribution, 0, 28, 30, false); + ASSERT_TRUE(res != boost::none); + ASSERT_EQ(res->distribution.size(), 3); + ASSERT_EQ(res->distribution, std::vector({5, 0, 2})); + + res = cryptonote::rpc::RpcHandler::get_output_distribution(::get_output_distribution, 0, 28, 30, true); + ASSERT_TRUE(res != boost::none); + ASSERT_EQ(res->distribution.size(), 3); + ASSERT_EQ(res->distribution, std::vector({55, 55, 57})); + + res = cryptonote::rpc::RpcHandler::get_output_distribution(::get_output_distribution, 0, 28, 31, false); + ASSERT_TRUE(res != boost::none); + ASSERT_EQ(res->distribution.size(), 4); + ASSERT_EQ(res->distribution, std::vector({5, 0, 2, 3})); + + res = cryptonote::rpc::RpcHandler::get_output_distribution(::get_output_distribution, 0, 28, 31, true); + ASSERT_TRUE(res != boost::none); + ASSERT_EQ(res->distribution.size(), 4); + ASSERT_EQ(res->distribution, std::vector({55, 55, 57, 60})); +} + +TEST(output_distribution, one) +{ + boost::optional res; + + res = cryptonote::rpc::RpcHandler::get_output_distribution(::get_output_distribution, 0, 0, 0, false); + ASSERT_TRUE(res != boost::none); + ASSERT_EQ(res->distribution.size(), 1); + ASSERT_EQ(res->distribution.back(), 0); +} + +TEST(output_distribution, full_cumulative) +{ + boost::optional res; + + res = cryptonote::rpc::RpcHandler::get_output_distribution(::get_output_distribution, 0, 0, 31, true); + ASSERT_TRUE(res != boost::none); + ASSERT_EQ(res->distribution.size(), 32); + ASSERT_EQ(res->distribution.back(), 60); +} + +TEST(output_distribution, full_noncumulative) +{ + boost::optional res; + + res = cryptonote::rpc::RpcHandler::get_output_distribution(::get_output_distribution, 0, 0, 31, false); + ASSERT_TRUE(res != boost::none); + ASSERT_EQ(res->distribution.size(), 32); + for (size_t i = 0; i < 32; ++i) + ASSERT_EQ(res->distribution[i], test_distribution[i]); +} + +TEST(output_distribution, part_cumulative) +{ + boost::optional res; + + res = cryptonote::rpc::RpcHandler::get_output_distribution(::get_output_distribution, 0, 4, 8, true); + ASSERT_TRUE(res != boost::none); + ASSERT_EQ(res->distribution.size(), 5); + ASSERT_EQ(res->distribution, std::vector({0, 1, 6, 7, 11})); +} + +TEST(output_distribution, part_noncumulative) +{ + boost::optional res; + + res = cryptonote::rpc::RpcHandler::get_output_distribution(::get_output_distribution, 0, 4, 8, false); + ASSERT_TRUE(res != boost::none); + ASSERT_EQ(res->distribution.size(), 5); + ASSERT_EQ(res->distribution, std::vector({0, 1, 5, 1, 4})); +} diff --git a/tests/unit_tests/testdb.h b/tests/unit_tests/testdb.h new file mode 100644 index 00000000000..b6962cc41a9 --- /dev/null +++ b/tests/unit_tests/testdb.h @@ -0,0 +1,146 @@ +// Copyright (c) 2014-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers + +#pragma once + +#include +#include +#include +#include "gtest/gtest.h" + +#include "blockchain_db/blockchain_db.h" + +class BaseTestDB: public cryptonote::BlockchainDB { +public: + BaseTestDB() {} + virtual void open(const std::string& filename, const int db_flags = 0) { } + virtual void close() {} + virtual void sync() {} + virtual void safesyncmode(const bool onoff) {} + virtual void reset() {} + virtual std::vector get_filenames() const { return std::vector(); } + virtual bool remove_data_file(const std::string& folder) const { return true; } + virtual std::string get_db_name() const { return std::string(); } + virtual bool lock() { return true; } + virtual void unlock() { } + virtual bool batch_start(uint64_t batch_num_blocks=0, uint64_t batch_bytes=0) { return true; } + virtual void batch_stop() {} + virtual void set_batch_transactions(bool) {} + virtual void block_txn_start(bool readonly=false) {} + virtual void block_txn_stop() {} + virtual void block_txn_abort() {} + virtual void drop_hard_fork_info() {} + virtual bool block_exists(const crypto::hash& h, uint64_t *height) const { return false; } + virtual cryptonote::blobdata get_block_blob_from_height(const uint64_t& height) const { return cryptonote::t_serializable_object_to_blob(get_block_from_height(height)); } + virtual cryptonote::blobdata get_block_blob(const crypto::hash& h) const { return cryptonote::blobdata(); } + virtual bool get_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const { return false; } + virtual bool get_pruned_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const { return false; } + virtual bool get_prunable_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const { return false; } + virtual bool get_prunable_tx_hash(const crypto::hash& tx_hash, crypto::hash &prunable_hash) const { return false; } + virtual uint64_t get_block_height(const crypto::hash& h) const { return 0; } + virtual cryptonote::block_header get_block_header(const crypto::hash& h) const { return cryptonote::block_header(); } + virtual uint64_t get_block_timestamp(const uint64_t& height) const { return 0; } + virtual std::vector get_block_cumulative_rct_outputs(const std::vector &heights) const { return {}; } + virtual uint64_t get_top_block_timestamp() const { return 0; } + virtual size_t get_block_weight(const uint64_t& height) const { return 128; } + virtual cryptonote::difficulty_type get_block_cumulative_difficulty(const uint64_t& height) const { return 10; } + virtual cryptonote::difficulty_type get_block_difficulty(const uint64_t& height) const { return 0; } + virtual uint64_t get_block_already_generated_coins(const uint64_t& height) const { return 10000000000; } + virtual crypto::hash get_block_hash_from_height(const uint64_t& height) const { return crypto::hash(); } + virtual std::vector get_blocks_range(const uint64_t& h1, const uint64_t& h2) const { return std::vector(); } + virtual std::vector get_hashes_range(const uint64_t& h1, const uint64_t& h2) const { return std::vector(); } + virtual crypto::hash top_block_hash() const { return crypto::hash(); } + virtual cryptonote::block get_top_block() const { return cryptonote::block(); } + virtual uint64_t height() const { return 1; } + virtual bool tx_exists(const crypto::hash& h) const { return false; } + virtual bool tx_exists(const crypto::hash& h, uint64_t& tx_index) const { return false; } + virtual uint64_t get_tx_unlock_time(const crypto::hash& h) const { return 0; } + virtual cryptonote::transaction get_tx(const crypto::hash& h) const { return cryptonote::transaction(); } + virtual bool get_tx(const crypto::hash& h, cryptonote::transaction &tx) const { return false; } + virtual uint64_t get_tx_count() const { return 0; } + virtual std::vector get_tx_list(const std::vector& hlist) const { return std::vector(); } + virtual uint64_t get_tx_block_height(const crypto::hash& h) const { return 0; } + virtual uint64_t get_num_outputs(const uint64_t& amount) const { return 1; } + virtual uint64_t get_indexing_base() const { return 0; } + virtual cryptonote::output_data_t get_output_key(const uint64_t& amount, const uint64_t& index) { return cryptonote::output_data_t(); } + virtual cryptonote::tx_out_index get_output_tx_and_index_from_global(const uint64_t& index) const { return cryptonote::tx_out_index(); } + virtual cryptonote::tx_out_index get_output_tx_and_index(const uint64_t& amount, const uint64_t& index) const { return cryptonote::tx_out_index(); } + virtual void get_output_tx_and_index(const uint64_t& amount, const std::vector &offsets, std::vector &indices) const {} + virtual void get_output_key(const uint64_t &amount, const std::vector &offsets, std::vector &outputs, bool allow_partial = false) {} + virtual bool can_thread_bulk_indices() const { return false; } + virtual std::vector get_tx_output_indices(const crypto::hash& h) const { return std::vector(); } + virtual std::vector get_tx_amount_output_indices(const uint64_t tx_index) const { return std::vector(); } + virtual bool has_key_image(const crypto::key_image& img) const { return false; } + virtual void remove_block() { } + virtual uint64_t add_transaction_data(const crypto::hash& blk_hash, const cryptonote::transaction& tx, const crypto::hash& tx_hash, const crypto::hash& tx_prunable_hash) {return 0;} + virtual void remove_transaction_data(const crypto::hash& tx_hash, const cryptonote::transaction& tx) {} + virtual uint64_t add_output(const crypto::hash& tx_hash, const cryptonote::tx_out& tx_output, const uint64_t& local_index, const uint64_t unlock_time, const rct::key *commitment) {return 0;} + virtual void add_tx_amount_output_indices(const uint64_t tx_index, const std::vector& amount_output_indices) {} + virtual void add_spent_key(const crypto::key_image& k_image) {} + virtual void remove_spent_key(const crypto::key_image& k_image) {} + + virtual bool for_all_key_images(std::function) const { return true; } + virtual bool for_blocks_range(const uint64_t&, const uint64_t&, std::function) const { return true; } + virtual bool for_all_transactions(std::function, bool pruned) const { return true; } + virtual bool for_all_outputs(std::function f) const { return true; } + virtual bool for_all_outputs(uint64_t amount, const std::function &f) const { return true; } + virtual bool is_read_only() const { return false; } + virtual std::map> get_output_histogram(const std::vector &amounts, bool unlocked, uint64_t recent_cutoff, uint64_t min_count) const { return std::map>(); } + virtual bool get_output_distribution(uint64_t amount, uint64_t from_height, uint64_t to_height, std::vector &distribution, uint64_t &base) const { return false; } + + virtual void add_txpool_tx(const crypto::hash &txid, const cryptonote::blobdata &blob, const cryptonote::txpool_tx_meta_t& details) {} + virtual void update_txpool_tx(const crypto::hash &txid, const cryptonote::txpool_tx_meta_t& details) {} + virtual uint64_t get_txpool_tx_count(bool include_unrelayed_txes = true) const { return 0; } + virtual bool txpool_has_tx(const crypto::hash &txid) const { return false; } + virtual void remove_txpool_tx(const crypto::hash& txid) {} + virtual bool get_txpool_tx_meta(const crypto::hash& txid, cryptonote::txpool_tx_meta_t &meta) const { return false; } + virtual bool get_txpool_tx_blob(const crypto::hash& txid, cryptonote::blobdata &bd) const { return false; } + virtual uint64_t get_database_size() const { return 0; } + virtual cryptonote::blobdata get_txpool_tx_blob(const crypto::hash& txid) const { return ""; } + virtual bool for_all_txpool_txes(std::function, bool include_blob = false, bool include_unrelayed_txes = false) const { return false; } + + virtual void add_block( const cryptonote::block& blk + , size_t block_weight + , const cryptonote::difficulty_type& cumulative_difficulty + , const uint64_t& coins_generated + , uint64_t num_rct_outs + , const crypto::hash& blk_hash + ) { } + virtual cryptonote::block get_block_from_height(const uint64_t& height) const { return cryptonote::block(); } + virtual void set_hard_fork_version(uint64_t height, uint8_t version) {} + virtual uint8_t get_hard_fork_version(uint64_t height) const { return 0; } + virtual void check_hard_fork_info() {} + + virtual uint32_t get_blockchain_pruning_seed() const { return 0; } + virtual bool prune_blockchain(uint32_t pruning_seed = 0) { return true; } + virtual bool update_pruning() { return true; } + virtual bool check_pruning() { return true; } +}; + From 80be2dca2b8abdc0dae515187038475062987bd8 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 14 Nov 2018 10:46:14 +0000 Subject: [PATCH 0771/1404] unit_tests: don't take the address of an unwrapped secret key --- tests/unit_tests/crypto.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/unit_tests/crypto.cpp b/tests/unit_tests/crypto.cpp index 29fa88f9dad..e09ec7f7ac6 100644 --- a/tests/unit_tests/crypto.cpp +++ b/tests/unit_tests/crypto.cpp @@ -47,6 +47,9 @@ namespace "8b655970153799af2aeadc9ff1add0ea6c7251d54154cfa92c173a0dd39c1f94" "6c7251d54154cfa92c173a0dd39c1f948b655970153799af2aeadc9ff1add0ea"; + template void *addressof(T &t) { return &t; } + template<> void *addressof(crypto::secret_key &k) { return addressof(unwrap(unwrap(k))); } + template bool is_formatted() { @@ -55,7 +58,7 @@ namespace static_assert(alignof(T) == 1, "T must have 1 byte alignment"); static_assert(sizeof(T) <= sizeof(source), "T is too large for source"); static_assert(sizeof(T) * 2 <= sizeof(expected), "T is too large for destination"); - std::memcpy(std::addressof(value), source, sizeof(T)); + std::memcpy(addressof(value), source, sizeof(T)); std::stringstream out; out << "BEGIN" << value << "END"; From 707c2f836b3136bc88b5aed374a4b3128f9596a7 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 17 Nov 2018 13:15:03 +0000 Subject: [PATCH 0772/1404] Remove -Werror It is an annoying piece of garbage --- CMakeLists.txt | 3 --- contrib/CMakeLists.txt | 5 ----- contrib/epee/tests/src/CMakeLists.txt | 4 ++-- src/CMakeLists.txt | 5 ----- 4 files changed, 2 insertions(+), 15 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0c38c673abf..88a9b8834a6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -599,9 +599,6 @@ else() set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${ARCH_FLAG}") set(WARNINGS "-Wall -Wextra -Wpointer-arith -Wundef -Wvla -Wwrite-strings -Wno-error=extra -Wno-error=deprecated-declarations -Wno-unused-parameter -Wno-unused-variable -Wno-error=unused-variable -Wno-error=undef -Wno-error=uninitialized") - if(NOT MINGW) - set(WARNINGS_AS_ERRORS_FLAG "-Werror") - endif() if(CMAKE_C_COMPILER_ID STREQUAL "Clang") if(ARM) set(WARNINGS "${WARNINGS} -Wno-error=inline-asm") diff --git a/contrib/CMakeLists.txt b/contrib/CMakeLists.txt index 990a05c0879..3bebbe0d2d9 100644 --- a/contrib/CMakeLists.txt +++ b/contrib/CMakeLists.txt @@ -26,10 +26,5 @@ # 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. -# warnings are cleared only for GCC on Linux -if (NOT (MINGW OR APPLE OR FREEBSD OR OPENBSD OR DRAGONFLY)) - add_compile_options("${WARNINGS_AS_ERRORS_FLAG}") # applies only to targets that follow -endif() - add_subdirectory(epee) diff --git a/contrib/epee/tests/src/CMakeLists.txt b/contrib/epee/tests/src/CMakeLists.txt index c7d31735baf..4807fa7ead8 100644 --- a/contrib/epee/tests/src/CMakeLists.txt +++ b/contrib/epee/tests/src/CMakeLists.txt @@ -14,8 +14,8 @@ IF (MSVC) include_directories(SYSTEM platform/msvc) ELSE() # set stuff for other systems - SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c11 -Wall -Werror") - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -Werror -Wno-reorder") + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c11 -Wall") + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -Wno-reorder") ENDIF() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6ee7effdd05..a0b62da77e1 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -34,11 +34,6 @@ if (WIN32 OR STATIC) add_definitions(-DMINIUPNP_STATICLIB) endif () -# warnings are cleared only for GCC on Linux -if (NOT (MINGW OR APPLE OR FREEBSD OR OPENBSD OR DRAGONFLY)) - add_compile_options("${WARNINGS_AS_ERRORS_FLAG}") # applies only to targets that follow -endif() - function (monero_private_headers group) source_group("${group}\\Private" FILES From 60f36386e4c844040371974f01da07ed02942529 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 19 Nov 2018 17:55:53 +0000 Subject: [PATCH 0773/1404] Avoid unnecessary temp block and copy ctor block already has a default ctor, and the extra object churn due to its innards (vectors, etc) is pointless. --- src/cryptonote_basic/miner.cpp | 2 +- src/cryptonote_core/blockchain.cpp | 2 +- src/rpc/core_rpc_server.cpp | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/cryptonote_basic/miner.cpp b/src/cryptonote_basic/miner.cpp index d8ca2dd3576..339d6858053 100644 --- a/src/cryptonote_basic/miner.cpp +++ b/src/cryptonote_basic/miner.cpp @@ -146,7 +146,7 @@ namespace cryptonote //----------------------------------------------------------------------------------------------------- bool miner::request_block_template() { - block bl = AUTO_VAL_INIT(bl); + block bl; difficulty_type di = AUTO_VAL_INIT(di); uint64_t height = AUTO_VAL_INIT(height); uint64_t expected_reward; //only used for RPC calls - could possibly be useful here too? diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index f3105114eb6..ae620fc80cf 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -404,7 +404,7 @@ bool Blockchain::init(BlockchainDB* db, const network_type nettype, bool offline if(!m_db->height()) { MINFO("Blockchain not loaded, generating genesis block."); - block bl = boost::value_initialized(); + block bl; block_verification_context bvc = boost::value_initialized(); generate_genesis_block(bl, get_config(m_nettype).GENESIS_TX, get_config(m_nettype).GENESIS_NONCE); add_new_block(bl, bvc); diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index c2e71bef8b9..bf70e13717c 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -1077,7 +1077,7 @@ namespace cryptonote return false; } - block b = AUTO_VAL_INIT(b); + block b; cryptonote::blobdata blob_reserve; blob_reserve.resize(req.reserve_size, 0); if(!m_core.get_block_template(b, info.address, res.difficulty, res.height, res.expected_reward, blob_reserve)) @@ -1148,7 +1148,7 @@ namespace cryptonote // Fixing of high orphan issue for most pools // Thanks Boolberry! - block b = AUTO_VAL_INIT(b); + block b; if(!parse_and_validate_block_from_blob(blockblob, b)) { error_resp.code = CORE_RPC_ERROR_CODE_WRONG_BLOCKBLOB; @@ -1216,7 +1216,7 @@ namespace cryptonote error_resp.message = "Wrong block blob"; return false; } - block b = AUTO_VAL_INIT(b); + block b; if(!parse_and_validate_block_from_blob(blockblob, b)) { error_resp.code = CORE_RPC_ERROR_CODE_WRONG_BLOCKBLOB; From fc99f177f3bc29322fc93839e4f238c7fba9a64f Mon Sep 17 00:00:00 2001 From: xiphon Date: Mon, 19 Nov 2018 22:36:10 +0000 Subject: [PATCH 0774/1404] lmdb: fix gcc 7.3.0 'implicit-fallthrough' warning --- src/blockchain_db/lmdb/db_lmdb.cpp | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp index ea3638a8585..622278784a6 100644 --- a/src/blockchain_db/lmdb/db_lmdb.cpp +++ b/src/blockchain_db/lmdb/db_lmdb.cpp @@ -4358,16 +4358,12 @@ void BlockchainLMDB::migrate_2_3() void BlockchainLMDB::migrate(const uint32_t oldversion) { - switch(oldversion) { - case 0: - migrate_0_1(); /* FALLTHRU */ - case 1: - migrate_1_2(); /* FALLTHRU */ - case 2: - migrate_2_3(); /* FALLTHRU */ - default: - ; - } + if (oldversion < 1) + migrate_0_1(); + if (oldversion < 2) + migrate_1_2(); + if (oldversion < 3) + migrate_2_3(); } } // namespace cryptonote From b9b307d11ade4ebc4a5fb6cc89d7f14992a7b6a0 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 8 Nov 2018 18:26:59 +0000 Subject: [PATCH 0775/1404] rpc: speedup get_output_distribution and decrease the amount of data carried around --- src/rpc/core_rpc_server.cpp | 43 ++++++++++++++++- src/rpc/core_rpc_server.h | 2 + src/rpc/core_rpc_server_commands_defs.h | 62 ++++++++++++++++++++++++- src/wallet/wallet2.cpp | 7 ++- 4 files changed, 110 insertions(+), 4 deletions(-) diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index df9eee781ac..f029d1d5ad5 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -2133,7 +2133,7 @@ namespace cryptonote return false; } - res.distributions.push_back({std::move(*data), amount, req.binary}); + res.distributions.push_back({std::move(*data), amount, "", req.binary, req.compress}); } } catch (const std::exception &e) @@ -2147,6 +2147,47 @@ namespace cryptonote return true; } //------------------------------------------------------------------------------------------------------------------------------ + bool core_rpc_server::on_get_output_distribution_bin(const COMMAND_RPC_GET_OUTPUT_DISTRIBUTION::request& req, COMMAND_RPC_GET_OUTPUT_DISTRIBUTION::response& res) + { + PERF_TIMER(on_get_output_distribution_bin); + + bool r; + if (use_bootstrap_daemon_if_necessary(invoke_http_mode::BIN, "/get_output_distribution.bin", req, res, r)) + return r; + + res.status = "Failed"; + + if (!req.binary) + { + res.status = "Binary only call"; + return false; + } + try + { + // 0 is placeholder for the whole chain + const uint64_t req_to_height = req.to_height ? req.to_height : (m_core.get_current_blockchain_height() - 1); + for (uint64_t amount: req.amounts) + { + auto data = rpc::RpcHandler::get_output_distribution([this](uint64_t amount, uint64_t from, uint64_t to, uint64_t &start_height, std::vector &distribution, uint64_t &base) { return m_core.get_output_distribution(amount, from, to, start_height, distribution, base); }, amount, req.from_height, req_to_height, req.cumulative); + if (!data) + { + res.status = "Failed to get output distribution"; + return false; + } + + res.distributions.push_back({std::move(*data), amount, "", req.binary, req.compress}); + } + } + catch (const std::exception &e) + { + res.status = "Failed to get output distribution"; + return false; + } + + res.status = CORE_RPC_STATUS_OK; + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ const command_line::arg_descriptor core_rpc_server::arg_rpc_bind_port = { diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h index 3ba882b2355..8ada0af1581 100644 --- a/src/rpc/core_rpc_server.h +++ b/src/rpc/core_rpc_server.h @@ -117,6 +117,7 @@ namespace cryptonote MAP_URI_AUTO_JON2_IF("/stop_save_graph", on_stop_save_graph, COMMAND_RPC_STOP_SAVE_GRAPH, !m_restricted) MAP_URI_AUTO_JON2("/get_outs", on_get_outs, COMMAND_RPC_GET_OUTPUTS) MAP_URI_AUTO_JON2_IF("/update", on_update, COMMAND_RPC_UPDATE, !m_restricted) + MAP_URI_AUTO_BIN2("/get_output_distribution.bin", on_get_output_distribution_bin, COMMAND_RPC_GET_OUTPUT_DISTRIBUTION) BEGIN_JSON_RPC_MAP("/json_rpc") MAP_JON_RPC("get_block_count", on_getblockcount, COMMAND_RPC_GETBLOCKCOUNT) MAP_JON_RPC("getblockcount", on_getblockcount, COMMAND_RPC_GETBLOCKCOUNT) @@ -187,6 +188,7 @@ namespace cryptonote bool on_start_save_graph(const COMMAND_RPC_START_SAVE_GRAPH::request& req, COMMAND_RPC_START_SAVE_GRAPH::response& res); bool on_stop_save_graph(const COMMAND_RPC_STOP_SAVE_GRAPH::request& req, COMMAND_RPC_STOP_SAVE_GRAPH::response& res); bool on_update(const COMMAND_RPC_UPDATE::request& req, COMMAND_RPC_UPDATE::response& res); + bool on_get_output_distribution_bin(const COMMAND_RPC_GET_OUTPUT_DISTRIBUTION::request& req, COMMAND_RPC_GET_OUTPUT_DISTRIBUTION::response& res); //json_rpc bool on_getblockcount(const COMMAND_RPC_GETBLOCKCOUNT::request& req, COMMAND_RPC_GETBLOCKCOUNT::response& res); diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index 8e8df7a520b..27c77eecc95 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -34,6 +34,40 @@ #include "cryptonote_basic/difficulty.h" #include "crypto/hash.h" #include "rpc/rpc_handler.h" +#include "common/varint.h" +#include "common/perf_timer.h" + +namespace +{ + template + std::string compress_integer_array(const std::vector &v) + { + std::string s; + s.resize(v.size() * (sizeof(T) * 8 / 7 + 1)); + char *ptr = (char*)s.data(); + for (const T &t: v) + tools::write_varint(ptr, t); + s.resize(ptr - s.data()); + return s; + } + + template + std::vector decompress_integer_array(const std::string &s) + { + std::vector v; + v.reserve(s.size()); + int read = 0; + const std::string::const_iterator end = s.end(); + for (std::string::const_iterator i = s.begin(); i != end; std::advance(i, read)) + { + T t; + read = tools::read_varint(std::string::const_iterator(i), s.end(), t); + CHECK_AND_ASSERT_THROW_MES(read > 0 && read <= 256, "Error decompressing data"); + v.push_back(t); + } + return v; + } +} namespace cryptonote { @@ -2222,6 +2256,7 @@ namespace cryptonote uint64_t to_height; bool cumulative; bool binary; + bool compress; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(amounts) @@ -2229,6 +2264,7 @@ namespace cryptonote KV_SERIALIZE_OPT(to_height, (uint64_t)0) KV_SERIALIZE_OPT(cumulative, false) KV_SERIALIZE_OPT(binary, true) + KV_SERIALIZE_OPT(compress, false) END_KV_SERIALIZE_MAP() }; @@ -2236,14 +2272,38 @@ namespace cryptonote { rpc::output_distribution_data data; uint64_t amount; + std::string compressed_data; bool binary; + bool compress; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(amount) KV_SERIALIZE_N(data.start_height, "start_height") KV_SERIALIZE(binary) + KV_SERIALIZE(compress) if (this_ref.binary) - KV_SERIALIZE_CONTAINER_POD_AS_BLOB_N(data.distribution, "distribution") + { + if (is_store) + { + if (this_ref.compress) + { + const_cast(this_ref.compressed_data) = compress_integer_array(this_ref.data.distribution); + KV_SERIALIZE(compressed_data) + } + else + KV_SERIALIZE_CONTAINER_POD_AS_BLOB_N(data.distribution, "distribution") + } + else + { + if (this_ref.compress) + { + KV_SERIALIZE(compressed_data) + const_cast&>(this_ref.data.distribution) = decompress_integer_array(this_ref.compressed_data); + } + else + KV_SERIALIZE_CONTAINER_POD_AS_BLOB_N(data.distribution, "distribution") + } + } else KV_SERIALIZE_N(data.distribution, "distribution") KV_SERIALIZE_N(data.base, "base") diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 129b96733bc..c9781f0c8bd 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -2852,10 +2852,11 @@ bool wallet2::get_rct_distribution(uint64_t &start_height, std::vector cryptonote::COMMAND_RPC_GET_OUTPUT_DISTRIBUTION::response res = AUTO_VAL_INIT(res); req.amounts.push_back(0); req.from_height = 0; - req.cumulative = true; + req.cumulative = false; req.binary = true; + req.compress = true; m_daemon_rpc_mutex.lock(); - bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_output_distribution", req, res, m_http_client, rpc_timeout); + bool r = net_utils::invoke_http_bin("/get_output_distribution.bin", req, res, m_http_client, rpc_timeout); m_daemon_rpc_mutex.unlock(); if (!r) { @@ -2882,6 +2883,8 @@ bool wallet2::get_rct_distribution(uint64_t &start_height, std::vector MWARNING("Failed to request output distribution: results are not for amount 0"); return false; } + for (size_t i = 1; i < res.distributions[0].data.distribution.size(); ++i) + res.distributions[0].data.distribution[i] += res.distributions[0].data.distribution[i-1]; start_height = res.distributions[0].data.start_height; distribution = std::move(res.distributions[0].data.distribution); return true; From 6732fc7fde22d37a78318d350baaeb1c03158fab Mon Sep 17 00:00:00 2001 From: naughtyfox Date: Mon, 12 Nov 2018 20:46:00 +0300 Subject: [PATCH 0776/1404] Fix issue 4793 - M/N multisig transaction signature --- src/common/CMakeLists.txt | 6 ++- src/common/combinator.cpp | 50 ++++++++++++++++++++ src/common/combinator.h | 96 +++++++++++++++++++++++++++++++++++++++ src/wallet/wallet2.cpp | 77 ++++++++++++++++++++++++------- src/wallet/wallet2.h | 4 +- 5 files changed, 213 insertions(+), 20 deletions(-) create mode 100644 src/common/combinator.cpp create mode 100644 src/common/combinator.h diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index aed9bfee73e..3045c003cbe 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -43,7 +43,8 @@ set(common_sources spawn.cpp threadpool.cpp updates.cpp - aligned.c) + aligned.c + combinator.cpp) if (STACK_TRACE) list(APPEND common_sources stack_trace.cpp) @@ -77,7 +78,8 @@ set(common_private_headers stack_trace.h threadpool.h updates.h - aligned.h) + aligned.h + combinator.h) monero_private_headers(common ${common_private_headers}) diff --git a/src/common/combinator.cpp b/src/common/combinator.cpp new file mode 100644 index 00000000000..cb4fbc908f3 --- /dev/null +++ b/src/common/combinator.cpp @@ -0,0 +1,50 @@ +// Copyright (c) 2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers + +#include "combinator.h" + +namespace tools { + +uint64_t combinations_count(uint32_t k, uint32_t n) +{ + if (k > n) { + throw std::runtime_error("k must not be greater than n"); + } + + uint64_t c = 1; + for (uint64_t i = 1; i <= k; ++i) { + c *= n--; + c /= i; + } + + return c; +} + +} diff --git a/src/common/combinator.h b/src/common/combinator.h new file mode 100644 index 00000000000..72c6800d54f --- /dev/null +++ b/src/common/combinator.h @@ -0,0 +1,96 @@ +// Copyright (c) 2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers + +#pragma once + +#include +#include + +namespace tools { + +uint64_t combinations_count(uint32_t k, uint32_t n); + +template +class Combinator { +public: + Combinator(const std::vector& v) : origin(v) { } + + std::vector> combine(size_t k); + +private: + void doCombine(size_t from, size_t k); + + std::vector origin; + std::vector> combinations; + std::vector current; +}; + +template +std::vector> Combinator::combine(size_t k) +{ + if (k > origin.size()) + { + throw std::runtime_error("k must be smaller than elements number"); + } + + if (k == 0) + { + throw std::runtime_error("k must be greater than zero"); + } + + combinations.clear(); + doCombine(0, k); + return combinations; +} + +template +void Combinator::doCombine(size_t from, size_t k) +{ + current.push_back(0); + + for (size_t i = from; i <= origin.size() - k; ++i) + { + current.back() = i; + + if (k > 1) { + doCombine(i + 1, k - 1); + } else { + std::vector comb; + for (auto ind: current) { + comb.push_back(origin[ind]); + } + combinations.push_back(comb); + } + } + + current.pop_back(); +} + +} //namespace tools diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index ebf857b787f..8fcecb40245 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -67,6 +67,7 @@ using namespace epee; #include "common/json_util.h" #include "memwipe.h" #include "common/base58.h" +#include "common/combinator.h" #include "common/dns_utils.h" #include "common/notify.h" #include "common/perf_timer.h" @@ -177,6 +178,20 @@ namespace return public_keys; } + + bool keys_intersect(const std::unordered_set& s1, const std::unordered_set& s2) + { + if (s1.empty() || s2.empty()) + return false; + + for (const auto& e: s1) + { + if (s2.find(e) != s2.end()) + return true; + } + + return false; + } } namespace @@ -6045,7 +6060,7 @@ bool wallet2::sign_multisig_tx(multisig_tx_set &exported_txs, std::vector multisig_signers; size_t n_multisig_txes = 0; + std::vector> ignore_sets; if (m_multisig && !m_transfers.empty()) { const crypto::public_key local_signer = get_multisig_signer_public_key(); size_t n_available_signers = 1; + + // At this step we need to define set of participants available for signature, + // i.e. those of them who exchanged with multisig info's for (const crypto::public_key &signer: m_multisig_signers) { if (signer == local_signer) continue; - multisig_signers.push_front(signer); for (const auto &i: m_transfers[0].m_multisig_info) { if (i.m_signer == signer) { - multisig_signers.pop_front(); multisig_signers.push_back(signer); ++n_available_signers; break; } } } - multisig_signers.push_back(local_signer); + // n_available_signers includes the transaction creator, but multisig_signers doesn't MDEBUG("We can use " << n_available_signers << "/" << m_multisig_signers.size() << " other signers"); - THROW_WALLET_EXCEPTION_IF(n_available_signers+1 < m_multisig_threshold, error::multisig_import_needed); - n_multisig_txes = n_available_signers == m_multisig_signers.size() ? m_multisig_threshold : 1; + THROW_WALLET_EXCEPTION_IF(n_available_signers < m_multisig_threshold, error::multisig_import_needed); + if (n_available_signers > m_multisig_threshold) + { + // If there more potential signers (those who exchanged with multisig info) + // than threshold needed some of them should be skipped since we don't know + // who will sign tx and who won't. Hence we don't contribute their LR pairs to the signature. + + // We create as many transactions as many combinations of excluded signers may be. + // For example, if we have 2/4 wallet and wallets are: A, B, C and D. Let A be + // transaction creator, so we need just 1 signature from set of B, C, D. + // Using "excluding" logic here we have to exclude 2-of-3 wallets. Combinations go as follows: + // BC, BD, and CD. We save these sets to use later and counting the number of required txs. + tools::Combinator c(std::vector(multisig_signers.begin(), multisig_signers.end())); + auto ignore_combinations = c.combine(multisig_signers.size() + 1 - m_multisig_threshold); + for (const auto& combination: ignore_combinations) + { + ignore_sets.push_back(std::unordered_set(combination.begin(), combination.end())); + } + + n_multisig_txes = ignore_sets.size(); + } + else + { + // If we have exact count of signers just to fit in threshold we don't exclude anyone and create 1 transaction + n_multisig_txes = 1; + } MDEBUG("We will create " << n_multisig_txes << " txes"); } @@ -7603,8 +7644,8 @@ void wallet2::transfer_selected_rct(std::vector() : ignore_sets.front(); + src.multisig_kLRki = get_multisig_composite_kLRki(idx, ignore_set, used_L, used_L); } else src.multisig_kLRki = rct::multisig_kLRki({rct::zero(), rct::zero(), rct::zero(), rct::zero()}); @@ -7666,7 +7707,7 @@ void wallet2::transfer_selected_rct(std::vector multisig_sigs; if (m_multisig) { - crypto::public_key ignore = m_multisig_threshold == m_multisig_signers.size() ? crypto::null_pkey : multisig_signers.front(); + auto ignore = ignore_sets.empty() ? std::unordered_set() : ignore_sets.front(); multisig_sigs.push_back({tx.rct_signatures, ignore, used_L, std::unordered_set(), msout}); if (m_multisig_threshold < m_multisig_signers.size()) @@ -7674,7 +7715,7 @@ void wallet2::transfer_selected_rct(std::vector new_used_L; size_t src_idx = 0; @@ -7682,7 +7723,7 @@ void wallet2::transfer_selected_rct(std::vector(), msout}); + multisig_sigs.push_back({ms_tx.rct_signatures, ignore_sets[ignore_index], new_used_L, std::unordered_set(), msout}); ms_tx.rct_signatures = tx.rct_signatures; THROW_WALLET_EXCEPTION_IF(cryptonote::get_transaction_hash(ms_tx) != cryptonote::get_transaction_hash(tx), error::wallet_internal_error, "Multisig txes differ by more than the signatures"); @@ -11265,7 +11306,7 @@ rct::multisig_kLRki wallet2::get_multisig_kLRki(size_t n, const rct::key &k) con return kLRki; } //---------------------------------------------------------------------------------------------------- -rct::multisig_kLRki wallet2::get_multisig_composite_kLRki(size_t n, const crypto::public_key &ignore, std::unordered_set &used_L, std::unordered_set &new_used_L) const +rct::multisig_kLRki wallet2::get_multisig_composite_kLRki(size_t n, const std::unordered_set &ignore_set, std::unordered_set &used_L, std::unordered_set &new_used_L) const { CHECK_AND_ASSERT_THROW_MES(n < m_transfers.size(), "Bad transfer index"); @@ -11276,8 +11317,9 @@ rct::multisig_kLRki wallet2::get_multisig_composite_kLRki(size_t n, const crypto size_t n_signers_used = 1; for (const auto &p: m_transfers[n].m_multisig_info) { - if (p.m_signer == ignore) + if (ignore_set.find(p.m_signer) != ignore_set.end()) continue; + for (const auto &lr: p.m_LR) { if (used_L.find(lr.m_L) != used_L.end()) @@ -11336,7 +11378,10 @@ cryptonote::blobdata wallet2::export_multisig() info[n].m_partial_key_images.push_back(ki); } - size_t nlr = m_multisig_threshold < m_multisig_signers.size() ? m_multisig_threshold - 1 : 1; + // Wallet tries to create as many transactions as many signers combinations. We calculate the maximum number here as follows: + // if we have 2/4 wallet with signers: A, B, C, D and A is a transaction creator it will need to pick up 1 signer from 3 wallets left. + // That means counting combinations for excluding 2-of-3 wallets (k = total signers count - threshold, n = total signers count - 1). + size_t nlr = tools::combinations_count(m_multisig_signers.size() - m_multisig_threshold, m_multisig_signers.size() - 1); for (size_t m = 0; m < nlr; ++m) { td.m_multisig_k.push_back(rct::skGen()); diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index dbfd45c5359..823da27a8ea 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -374,7 +374,7 @@ namespace tools struct multisig_sig { rct::rctSig sigs; - crypto::public_key ignore; + std::unordered_set ignore; std::unordered_set used_L; std::unordered_set signing_keys; rct::multisig_out msout; @@ -1256,7 +1256,7 @@ namespace tools void scan_output(const cryptonote::transaction &tx, const crypto::public_key &tx_pub_key, size_t i, tx_scan_info_t &tx_scan_info, int &num_vouts_received, std::unordered_map &tx_money_got_in_outs, std::vector &outs); void trim_hashchain(); crypto::key_image get_multisig_composite_key_image(size_t n) const; - rct::multisig_kLRki get_multisig_composite_kLRki(size_t n, const crypto::public_key &ignore, std::unordered_set &used_L, std::unordered_set &new_used_L) const; + rct::multisig_kLRki get_multisig_composite_kLRki(size_t n, const std::unordered_set &ignore_set, std::unordered_set &used_L, std::unordered_set &new_used_L) const; rct::multisig_kLRki get_multisig_kLRki(size_t n, const rct::key &k) const; rct::key get_multisig_k(size_t idx, const std::unordered_set &used_L) const; void update_multisig_rescan_info(const std::vector> &multisig_k, const std::vector> &info, size_t n); From 23829ebb09cfa016044f6e5a0b361de1e04705ad Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 20 Nov 2018 15:24:16 +0000 Subject: [PATCH 0777/1404] mlocker: don't throw from lock/unlock This prevents exceptions from showing up in various awkward places such as dtors, since the only exception that can be thrown is a lock failure, and nothing handles a lock failure anyway. --- contrib/epee/src/mlocker.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/contrib/epee/src/mlocker.cpp b/contrib/epee/src/mlocker.cpp index c3262e8f4bf..7ac79bd9a66 100644 --- a/contrib/epee/src/mlocker.cpp +++ b/contrib/epee/src/mlocker.cpp @@ -113,6 +113,8 @@ namespace epee void mlocker::lock(void *ptr, size_t len) { + TRY_ENTRY(); + size_t page_size = get_page_size(); if (page_size == 0) return; @@ -123,10 +125,14 @@ namespace epee for (size_t page = first; page <= last; ++page) lock_page(page); ++num_locked_objects; + + CATCH_ENTRY_L1("mlocker::lock", void()); } void mlocker::unlock(void *ptr, size_t len) { + TRY_ENTRY(); + size_t page_size = get_page_size(); if (page_size == 0) return; @@ -136,6 +142,8 @@ namespace epee for (size_t page = first; page <= last; ++page) unlock_page(page); --num_locked_objects; + + CATCH_ENTRY_L1("mlocker::lock", void()); } size_t mlocker::get_num_locked_pages() From c25260f51c02eede8adfc5ac06ddb443e64c87cb Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 17 Nov 2018 12:47:59 +0000 Subject: [PATCH 0778/1404] protocol: fix incorrect tx hash in log --- src/cryptonote_protocol/cryptonote_protocol_handler.inl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl index c2c660e8cfe..6081ddd89c7 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl @@ -1077,8 +1077,10 @@ skip: if(tvc[i].m_verifivation_failed) { if (!m_p2p->for_connection(span_connection_id, [&](cryptonote_connection_context& context, nodetool::peerid_type peer_id, uint32_t f)->bool{ + cryptonote::transaction tx; + parse_and_validate_tx_from_blob(*it, tx); // must succeed if we got here LOG_ERROR_CCONTEXT("transaction verification failed on NOTIFY_RESPONSE_GET_OBJECTS, tx_id = " - << epee::string_tools::pod_to_hex(get_blob_hash(*it)) << ", dropping connection"); + << epee::string_tools::pod_to_hex(cryptonote::get_transaction_hash(tx)) << ", dropping connection"); drop_connection(context, false, true); return 1; })) From 96e6b439705cff5806974bb34e4b46db40dd3dbd Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 20 Nov 2018 22:26:50 +0000 Subject: [PATCH 0779/1404] blockchain_stats: don't use gmtime_r on Windows In some cases, it doesn't like it (I don't know the details). Factor into a new epee function --- contrib/epee/include/misc_os_dependent.h | 9 +++++++++ contrib/epee/src/mlog.cpp | 8 ++------ src/blockchain_utilities/blockchain_stats.cpp | 2 +- src/simplewallet/simplewallet.cpp | 6 +----- 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/contrib/epee/include/misc_os_dependent.h b/contrib/epee/include/misc_os_dependent.h index 0d09683d6e0..5fffde8d5a0 100644 --- a/contrib/epee/include/misc_os_dependent.h +++ b/contrib/epee/include/misc_os_dependent.h @@ -122,6 +122,15 @@ namespace misc_utils return boost::lexical_cast(GetCurrentThreadId()); #elif defined(__GNUC__) return boost::lexical_cast(pthread_self()); +#endif + } + + inline bool get_gmt_time(time_t t, struct tm &tm) + { +#ifdef _WIN32 + return gmtime_s(&tm, &t); +#else + return gmtime_r(&t, &tm); #endif } } diff --git a/contrib/epee/src/mlog.cpp b/contrib/epee/src/mlog.cpp index 638155b6be3..00d8483881f 100644 --- a/contrib/epee/src/mlog.cpp +++ b/contrib/epee/src/mlog.cpp @@ -40,6 +40,7 @@ #include #include #include "string_tools.h" +#include "misc_os_dependent.h" #include "misc_log_ex.h" #undef MONERO_DEFAULT_LOG_CATEGORY @@ -58,12 +59,7 @@ static std::string generate_log_filename(const char *base) char tmp[200]; struct tm tm; time_t now = time(NULL); - if -#ifdef WIN32 - (!gmtime_s(&tm, &now)) -#else - (!gmtime_r(&now, &tm)) -#endif + if (!epee::misc_utils::get_gmt_time(now, tm)) snprintf(tmp, sizeof(tmp), "part-%u", ++fallback_counter); else strftime(tmp, sizeof(tmp), "%Y-%m-%d-%H-%M-%S", &tm); diff --git a/src/blockchain_utilities/blockchain_stats.cpp b/src/blockchain_utilities/blockchain_stats.cpp index 716b33cae08..aae8f333b27 100644 --- a/src/blockchain_utilities/blockchain_stats.cpp +++ b/src/blockchain_utilities/blockchain_stats.cpp @@ -234,7 +234,7 @@ plot 'stats.csv' index "DATA" using (timecolumn(1,"%Y-%m-%d")):4 with lines, '' } time_t tt = blk.timestamp; char timebuf[64]; - gmtime_r(&tt, &currtm); + epee::misc_utils::get_gmt_time(tt, currtm); if (!prevtm.tm_year) prevtm = currtm; // catch change of day diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index d9fd0c13e3f..1c431ade65b 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -6727,11 +6727,7 @@ static std::string get_human_readable_timestamp(uint64_t ts) return ""; time_t tt = ts; struct tm tm; -#ifdef WIN32 - gmtime_s(&tm, &tt); -#else - gmtime_r(&tt, &tm); -#endif + epee::misc_utils::get_gmt_time(tt, tm); uint64_t now = time(NULL); uint64_t diff = ts > now ? ts - now : now - ts; strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", &tm); From 6bfcc57395fb3ce0c00b35a07b3f6227fd2606de Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 21 Nov 2018 12:46:11 +0000 Subject: [PATCH 0780/1404] scoped_message_writer: protect all std::cout usage from readline --- src/common/scoped_message_writer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/scoped_message_writer.h b/src/common/scoped_message_writer.h index d887a13c980..42f439ad874 100644 --- a/src/common/scoped_message_writer.h +++ b/src/common/scoped_message_writer.h @@ -101,13 +101,13 @@ class scoped_message_writer MCLOG_FILE(m_log_level, "msgwriter", m_oss.str()); + PAUSE_READLINE(); if (epee::console_color_default == m_color) { std::cout << m_oss.str(); } else { - PAUSE_READLINE(); set_console_color(m_color, m_bright); std::cout << m_oss.str(); epee::reset_console_color(); From 517f25efd1d8cc6f10f777b6f8ddd79a088fae4b Mon Sep 17 00:00:00 2001 From: Jethro Grassie Date: Tue, 20 Nov 2018 13:18:08 -0500 Subject: [PATCH 0781/1404] rpc: add version to get_info --- src/rpc/CMakeLists.txt | 3 +++ src/rpc/core_rpc_server.cpp | 2 ++ src/rpc/core_rpc_server_commands_defs.h | 4 +++- src/rpc/daemon_handler.cpp | 2 ++ src/rpc/message_data_structs.h | 1 + 5 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/rpc/CMakeLists.txt b/src/rpc/CMakeLists.txt index 8fc42b7e3bb..d2c4a33cbb3 100644 --- a/src/rpc/CMakeLists.txt +++ b/src/rpc/CMakeLists.txt @@ -112,6 +112,7 @@ target_link_libraries(rpc common cryptonote_core cryptonote_protocol + version ${Boost_REGEX_LIBRARY} ${Boost_THREAD_LIBRARY} PRIVATE @@ -121,6 +122,7 @@ target_link_libraries(daemon_messages LINK_PRIVATE cryptonote_core cryptonote_protocol + version serialization ${EXTRA_LIBRARIES}) @@ -129,6 +131,7 @@ target_link_libraries(daemon_rpc_server rpc cryptonote_core cryptonote_protocol + version daemon_messages serialization ${Boost_CHRONO_LIBRARY} diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index df9eee781ac..48c9a5aa04b 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -210,6 +210,7 @@ namespace cryptonote } res.database_size = m_core.get_blockchain_storage().get_db().get_database_size(); res.update_available = m_core.is_update_available(); + res.version = m_restricted ? "" : MONERO_VERSION; return true; } //------------------------------------------------------------------------------------------------------------------------------ @@ -1609,6 +1610,7 @@ namespace cryptonote } res.database_size = m_core.get_blockchain_storage().get_db().get_database_size(); res.update_available = m_core.is_update_available(); + res.version = m_restricted ? "" : MONERO_VERSION; return true; } //------------------------------------------------------------------------------------------------------------------------------ diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index 8e8df7a520b..5cf7fd8e814 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -50,7 +50,7 @@ namespace cryptonote // advance which version they will stop working with // Don't go over 32767 for any of these #define CORE_RPC_VERSION_MAJOR 2 -#define CORE_RPC_VERSION_MINOR 1 +#define CORE_RPC_VERSION_MINOR 2 #define MAKE_CORE_RPC_VERSION(major,minor) (((major)<<16)|(minor)) #define CORE_RPC_VERSION MAKE_CORE_RPC_VERSION(CORE_RPC_VERSION_MAJOR, CORE_RPC_VERSION_MINOR) @@ -892,6 +892,7 @@ namespace cryptonote bool was_bootstrap_ever_used; uint64_t database_size; bool update_available; + std::string version; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(status) @@ -926,6 +927,7 @@ namespace cryptonote KV_SERIALIZE(was_bootstrap_ever_used) KV_SERIALIZE(database_size) KV_SERIALIZE(update_available) + KV_SERIALIZE(version) END_KV_SERIALIZE_MAP() }; }; diff --git a/src/rpc/daemon_handler.cpp b/src/rpc/daemon_handler.cpp index 64a5cc858ac..e2885dbb59e 100644 --- a/src/rpc/daemon_handler.cpp +++ b/src/rpc/daemon_handler.cpp @@ -34,6 +34,7 @@ #include "cryptonote_basic/cryptonote_format_utils.h" #include "cryptonote_basic/blobdatatype.h" #include "ringct/rctSigs.h" +#include "version.h" namespace cryptonote { @@ -437,6 +438,7 @@ namespace rpc res.info.block_size_limit = res.info.block_weight_limit = m_core.get_blockchain_storage().get_current_cumulative_block_weight_limit(); res.info.block_size_median = res.info.block_weight_median = m_core.get_blockchain_storage().get_current_cumulative_block_weight_median(); res.info.start_time = (uint64_t)m_core.get_start_time(); + res.info.version = MONERO_VERSION; res.status = Message::STATUS_OK; res.error_details = ""; diff --git a/src/rpc/message_data_structs.h b/src/rpc/message_data_structs.h index 3b56aff1509..e09b6749e3b 100644 --- a/src/rpc/message_data_structs.h +++ b/src/rpc/message_data_structs.h @@ -191,6 +191,7 @@ namespace rpc uint64_t block_size_median; uint64_t block_weight_median; uint64_t start_time; + std::string version; }; struct output_distribution From 40485a73b6fce47324aecf8b182164f72be0ae76 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 22 Nov 2018 01:39:12 +0000 Subject: [PATCH 0782/1404] mlocker: fix access to global lock map after dtor on exit as the lock, it now leaks --- contrib/epee/src/mlocker.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contrib/epee/src/mlocker.cpp b/contrib/epee/src/mlocker.cpp index c3262e8f4bf..b35393081e8 100644 --- a/contrib/epee/src/mlocker.cpp +++ b/contrib/epee/src/mlocker.cpp @@ -89,8 +89,8 @@ namespace epee } std::map &mlocker::map() { - static std::map vmap; - return vmap; + static std::map *vmap = new std::map(); + return *vmap; } size_t mlocker::get_page_size() From 56e616e8556192aba1e61de51bf3b5086e8d0f29 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 22 Nov 2018 01:40:13 +0000 Subject: [PATCH 0783/1404] wallet2: add n_vouts to capture list --- src/wallet/wallet2.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 129b96733bc..81f232561c7 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -2149,7 +2149,7 @@ void wallet2::process_parsed_blocks(uint64_t start_height, const std::vector= tx_cache_data.size(), error::wallet_internal_error, "txidx out of range"); const size_t n_vouts = m_refresh_type == RefreshType::RefreshOptimizeCoinbase ? 1 : parsed_blocks[i].block.miner_tx.vout.size(); - tpool.submit(&waiter, [&, i, txidx](){ geniod(parsed_blocks[i].block.miner_tx, n_vouts, txidx); }, true); + tpool.submit(&waiter, [&, i, n_vouts, txidx](){ geniod(parsed_blocks[i].block.miner_tx, n_vouts, txidx); }, true); } ++txidx; for (size_t j = 0; j < parsed_blocks[i].txes.size(); ++j) From b5573fc231df88a4673719dfa2997f7283b181fa Mon Sep 17 00:00:00 2001 From: stoffu Date: Thu, 22 Nov 2018 14:45:52 +0900 Subject: [PATCH 0784/1404] wallet2: resume processing when tx extra is partially broken Motivated by https://monero.stackexchange.com/questions/10483 Some exchanges appear to have customized the wallet software in an inappropriate way, making the tx extra field partially unreadable. PR #3716 changed the wallet behavior disallowing such partially valid tx extra. An example tx reported by the user is e87c675a85f34ecac58a8846613d25062f1813e1023c552b705afad32b972c38 where the normal tx pubkey appears again with the aditional tx pubkeys tag `04` which is inappropriate. --- src/wallet/wallet2.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 129b96733bc..42a5b3fdaff 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -1362,8 +1362,8 @@ void wallet2::cache_tx_data(const cryptonote::transaction& tx, const crypto::has { // Extra may only be partially parsed, it's OK if tx_extra_fields contains public key LOG_PRINT_L0("Transaction extra has unsupported format: " << txid); - tx_cache_data.tx_extra_fields.clear(); - return; + if (tx_cache_data.tx_extra_fields.empty()) + return; } // Don't try to extract tx public key if tx has no ouputs From b0d9d6051f4c3e548cb6d1da9e49279fd3a6b700 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 22 Nov 2018 11:15:06 +0000 Subject: [PATCH 0785/1404] cn_deserialize: allow parsing partially valid tx extra --- src/debug_utilities/cn_deserialize.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/debug_utilities/cn_deserialize.cpp b/src/debug_utilities/cn_deserialize.cpp index 3e25522300d..83422083be8 100644 --- a/src/debug_utilities/cn_deserialize.cpp +++ b/src/debug_utilities/cn_deserialize.cpp @@ -169,6 +169,7 @@ int main(int argc, char* argv[]) return 1; } + bool full; cryptonote::block block; cryptonote::transaction tx; std::vector fields; @@ -200,9 +201,9 @@ int main(int argc, char* argv[]) std::cout << "No fields were found in tx_extra" << std::endl; } } - else if (cryptonote::parse_tx_extra(std::vector(blob.begin(), blob.end()), fields) && !fields.empty()) + else if (((full = cryptonote::parse_tx_extra(std::vector(blob.begin(), blob.end()), fields)) || true) && !fields.empty()) { - std::cout << "Parsed tx_extra:" << std::endl; + std::cout << "Parsed" << (full ? "" : " partial") << " tx_extra:" << std::endl; print_extra_fields(fields); } else From c28e3d2dae392c7df9c04447079ac156ed299f42 Mon Sep 17 00:00:00 2001 From: stoffu Date: Fri, 23 Nov 2018 12:28:04 +0900 Subject: [PATCH 0786/1404] rctOps: add braces to suppress warnings --- src/ringct/rctOps.cpp | 346 +++++++++++++++++++++--------------------- 1 file changed, 173 insertions(+), 173 deletions(-) diff --git a/src/ringct/rctOps.cpp b/src/ringct/rctOps.cpp index 4db543f648b..0ec654af6fb 100644 --- a/src/ringct/rctOps.cpp +++ b/src/ringct/rctOps.cpp @@ -42,179 +42,179 @@ using namespace std; struct zero_commitment { uint64_t amount; rct::key commitment; }; static const zero_commitment zero_commitments[] = { - { (uint64_t)0ull, {0x58, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66} }, - { (uint64_t)1ull, {0x17, 0x38, 0xeb, 0x7a, 0x67, 0x7c, 0x61, 0x49, 0x22, 0x8a, 0x2b, 0xea, 0xa2, 0x1b, 0xea, 0x9e, 0x33, 0x70, 0x80, 0x2d, 0x72, 0xa3, 0xee, 0xc7, 0x90, 0x11, 0x95, 0x80, 0xe0, 0x2b, 0xd5, 0x22} }, - { (uint64_t)2ull, {0x76, 0x24, 0x84, 0x63, 0xa, 0x6, 0x17, 0x17, 0x8d, 0xe, 0x33, 0xf3, 0x2e, 0xe, 0x11, 0x3e, 0xa8, 0x46, 0x86, 0x9d, 0x46, 0x4b, 0xb, 0x6f, 0xf1, 0x3b, 0x29, 0x97, 0x4, 0x9c, 0xda, 0x7d} }, - { (uint64_t)3ull, {0xcf, 0xf7, 0x7b, 0x56, 0x62, 0x1c, 0x4f, 0xef, 0x74, 0xcf, 0x37, 0xc1, 0x78, 0xd4, 0xb5, 0x8a, 0xf4, 0xad, 0x8c, 0xd4, 0x35, 0xfc, 0xb9, 0x62, 0x76, 0xbc, 0x15, 0x9c, 0x7c, 0x6a, 0x28, 0x8c} }, - { (uint64_t)4ull, {0x9a, 0xb8, 0x6c, 0x31, 0xf4, 0x22, 0xd8, 0x21, 0xb5, 0x22, 0x57, 0x30, 0xd1, 0xbf, 0x73, 0xa, 0x9b, 0x91, 0xd2, 0xee, 0xe3, 0x14, 0xb8, 0x4e, 0xbd, 0x4b, 0x93, 0xa6, 0x81, 0x61, 0x82, 0x66} }, - { (uint64_t)5ull, {0x32, 0xee, 0x2f, 0x65, 0x9a, 0xf6, 0x38, 0x58, 0xc2, 0xf7, 0xdc, 0x11, 0x1b, 0x3b, 0xb8, 0xfe, 0xc0, 0x2c, 0xac, 0x42, 0x38, 0x3b, 0xb7, 0x36, 0xde, 0x1, 0x8, 0x6f, 0x38, 0xf0, 0x12, 0x3c} }, - { (uint64_t)6ull, {0x47, 0x26, 0x2b, 0x1e, 0xa6, 0x43, 0x1, 0x6e, 0x38, 0x24, 0x17, 0x53, 0xa4, 0xfb, 0x39, 0x92, 0x9e, 0x31, 0xea, 0x9b, 0xd3, 0x41, 0x1a, 0xb1, 0x7f, 0x16, 0x6e, 0x61, 0xf6, 0xc, 0xe5, 0xa7} }, - { (uint64_t)7ull, {0xc6, 0x32, 0x93, 0x68, 0x79, 0x9a, 0xd, 0xed, 0x4c, 0x20, 0x25, 0x6b, 0xff, 0xe6, 0x45, 0x47, 0xf1, 0x7b, 0xc4, 0x23, 0x95, 0x4, 0xbe, 0x82, 0x4d, 0xff, 0x8a, 0x2b, 0xe1, 0xaf, 0xe3, 0xcd} }, - { (uint64_t)8ull, {0xd5, 0xf1, 0x50, 0x74, 0x33, 0x46, 0x19, 0xf, 0x84, 0x2b, 0x6, 0xb8, 0xfa, 0xe1, 0x20, 0xeb, 0x85, 0x24, 0x7e, 0x9f, 0x6d, 0xec, 0x88, 0xff, 0xa2, 0x23, 0xbf, 0x69, 0x94, 0xe9, 0xc8, 0xc2} }, - { (uint64_t)9ull, {0x56, 00, 0x23, 0x32, 0x9e, 0xc0, 0xfa, 0xf3, 0x3b, 0x5e, 0x3a, 0x5c, 0xb4, 0xea, 0xef, 0xee, 0x38, 0xf8, 0x96, 0x1c, 0x88, 0xb6, 0x6a, 0x2f, 0x19, 0xd4, 0x59, 0x51, 0x96, 0x9c, 0x6d, 0x1f} }, - { (uint64_t)10ull, {0x3, 0x80, 0xdc, 0x24, 0xcc, 0x97, 0xcc, 0xe6, 0x58, 0xc3, 0xa9, 0x47, 0xc5, 0x10, 0x25, 0xde, 0x1a, 0x69, 0x80, 0x3b, 0xdb, 0x50, 0x5, 0xe3, 0xb7, 0xdd, 0xa9, 0xd, 0x68, 0x59, 0xb0, 0x1c} }, - { (uint64_t)20ull, {0x9, 0x3, 0xf6, 0x2e, 0x97, 0x76, 0x47, 0x58, 0xfe, 0xf8, 0x9e, 0x5b, 0xec, 0x29, 0xef, 0x4f, 0xc5, 0xe6, 0x45, 0x4b, 0x2d, 0x47, 0x44, 0x47, 0x36, 0x4, 0x4c, 0x25, 0x2e, 0xe2, 0x8e, 0xba} }, - { (uint64_t)30ull, {0xa2, 0x8b, 0x89, 0xe0, 0xb, 0xed, 0x62, 0x31, 0x68, 0x5b, 0xf9, 0x74, 0x36, 0xf2, 0xba, 0x51, 0xa2, 0x51, 0x55, 0x7f, 0x8d, 0x17, 0xa, 0x78, 0xe3, 0x12, 0xd6, 0x24, 0xbf, 0x60, 0xff, 0xfe} }, - { (uint64_t)40ull, {0xb5, 0xc6, 0x95, 0x55, 0x6a, 0x28, 0x47, 0xb2, 0xe, 0x1c, 0xbb, 0x26, 0xe6, 0xa9, 0xc6, 0x8a, 0x61, 0xc5, 0x50, 0xce, 0xb7, 0xc3, 0x4, 0xfe, 0x92, 0x28, 0x3d, 0x29, 0xa9, 0xb2, 0x43, 0xcb} }, - { (uint64_t)50ull, {0x12, 0x8e, 0xc6, 0xcd, 0xc0, 0x6b, 0x43, 0xc5, 0xd0, 0x9c, 0x3f, 0x65, 0x2a, 0xe3, 0x44, 0x7f, 0x9b, 0x3f, 0x2c, 0x30, 0x91, 0x2d, 0xf0, 0x80, 0x37, 00, 0x85, 0xbc, 0xc, 0x9, 0xef, 0x78} }, - { (uint64_t)60ull, {0x1f, 0x9f, 0x40, 0x3a, 0xae, 0xa7, 0x16, 0xfb, 0xe2, 0x98, 0xa8, 0x14, 0xf1, 0xee, 0xbc, 0x1b, 0x73, 0x16, 0x8c, 0x37, 0xfa, 0xe3, 0x16, 0xeb, 0x65, 0x5, 0x81, 0x6f, 0xc2, 0x20, 0xeb, 0xfb} }, - { (uint64_t)70ull, {0x10, 0xa2, 0x38, 0xc5, 0xe4, 0x8e, 0x4b, 0x93, 0x99, 0xdb, 0xa6, 0xcb, 0xd9, 0x8e, 0x63, 0x54, 0x41, 0x59, 0xe9, 0x8c, 0x93, 0x5a, 0xc0, 0x60, 0x3d, 0x72, 0xde, 0xf, 0xff, 0x31, 0x53, 0xbb} }, - { (uint64_t)80ull, {0x75, 0xab, 0x78, 0xc7, 0x28, 0x1f, 0x69, 0x28, 0xf0, 0x94, 0x86, 0x5, 0x7a, 0x63, 0x64, 0x18, 0x27, 0xc5, 0x74, 0x84, 0xe3, 0xe9, 0x9a, 0x39, 0xf3, 0x12, 0xa4, 0x3a, 0x51, 0x9b, 0xda, 0x8} }, - { (uint64_t)90ull, {0xe9, 0x56, 0x7b, 0xa7, 0x88, 0xb8, 0x5b, 0x82, 0xc8, 0x65, 0x7a, 0x15, 0xa5, 0x48, 0x99, 0x5c, 0xf6, 0xb0, 0xbd, 0xd1, 0xc6, 0x2a, 0xda, 0x77, 0x55, 0xf2, 0x32, 0x3a, 0xd8, 0xa4, 0x8, 0x51} }, - { (uint64_t)100ull, {0xb6, 0x17, 0x36, 0xd5, 0xf2, 0x8d, 0xef, 0x28, 0x61, 0x6a, 0xfc, 0x47, 0x93, 0xe9, 0x9b, 0x27, 0xcd, 0x3e, 0x89, 0xfb, 0x91, 0xc1, 0x13, 0xd4, 0x30, 0x73, 0x65, 0xfb, 0x75, 0xde, 0xdf, 0x88} }, - { (uint64_t)200ull, {0x71, 0x3, 0xeb, 0x72, 0x19, 0x28, 0xd7, 0x91, 0x99, 0x87, 0xf3, 0x50, 0xca, 0xa5, 0x7a, 0xe7, 0xb0, 0x81, 0x57, 0x15, 0x3b, 0x4c, 0x43, 0xd, 0x3e, 0xde, 0xc0, 0xc2, 0x3, 0x7, 0x97, 0x44} }, - { (uint64_t)300ull, {0x24, 0x40, 0x9e, 0x92, 0x2e, 0xce, 0xd1, 0xa0, 0x5e, 0x4e, 0xac, 0xa3, 0xdf, 0x91, 0x19, 0xc3, 0x8a, 0x92, 0x2e, 0xb, 0x66, 0xd0, 0x2d, 0x9d, 0xd2, 0xfb, 0x1d, 0xcc, 0x20, 0xb9, 0xaf, 0xc7} }, - { (uint64_t)400ull, {0xa7, 0x72, 0x9f, 0xa9, 0x32, 0x81, 0x82, 0x99, 0x34, 0x11, 0x5d, 0x47, 0x5a, 0x67, 0x86, 0xa, 0x14, 0x12, 0xc5, 0xe5, 0x95, 0x12, 0x20, 0xd9, 0x60, 0xc2, 0x41, 0xa0, 0x19, 0x1a, 0x9e, 0x65} }, - { (uint64_t)500ull, {0x2e, 0x53, 0xc, 0x6, 0x1c, 0x6d, 0x9e, 0x97, 0xab, 0xaf, 0x46, 0x8c, 0x32, 0xb0, 0xad, 0xa7, 0x49, 0x22, 0x57, 0x72, 0xfc, 0xd1, 0x17, 0x41, 0xcb, 0x5c, 0x3, 0x5c, 0xdd, 0x26, 0x14, 0xe} }, - { (uint64_t)600ull, {0xa5, 0xb, 0x91, 0x9, 0x9d, 0xf1, 0xb1, 0x69, 0x4f, 0x30, 0xb5, 0x8f, 0xe6, 0x77, 0x68, 0x50, 0xdb, 0xdb, 0xf4, 0x6c, 0xed, 0x99, 0x7f, 0x52, 0x62, 0xa8, 0x51, 0x59, 0x40, 0x74, 0xa5, 0x9d} }, - { (uint64_t)700ull, {0x51, 0x2c, 0xf, 0xae, 0xcc, 0xbe, 0xf2, 0xfe, 0xe5, 0x75, 0x4c, 0x6a, 0x45, 0xfd, 0xc0, 0x75, 0x2d, 0x4f, 0x15, 0x22, 0xe7, 0x7f, 0xf0, 0xc4, 0x8d, 0xcb, 0x19, 0x91, 0x8a, 0x68, 0x84, 0xe0} }, - { (uint64_t)800ull, {0xda, 0xa9, 0xf9, 0xa5, 0xb9, 0x71, 0x33, 0x33, 0xe9, 0x8c, 0x5, 0xac, 0xe7, 0x27, 0xcc, 0xe, 0x7d, 0xc3, 0xf1, 0x59, 0x49, 0xe1, 0xef, 0x4d, 0x94, 0xfa, 0x47, 0xd6, 0x8a, 0x34, 0xc6, 0x75} }, - { (uint64_t)900ull, {0xc7, 0x2b, 0x18, 0xc9, 0x17, 0xcd, 0x43, 0xee, 0x78, 0x40, 0x5e, 0x39, 0x83, 0x98, 0xb8, 0x3a, 0xc0, 0x97, 0x7b, 0x25, 0x19, 0x90, 0xd8, 0x13, 0xc, 0x38, 0xba, 0x53, 0xb6, 0x3d, 0xb4, 0xf7} }, - { (uint64_t)1000ull, {0x1, 0xdf, 0x60, 0x91, 0xeb, 0x6a, 0x48, 0xe9, 0xe4, 0x22, 0x25, 0xb, 0xe3, 0x83, 0x88, 0xc8, 0x61, 0xb6, 0x55, 0x55, 0xa7, 0x20, 0xad, 0x15, 0x35, 0x86, 0xfe, 0x2b, 0xd2, 0x2f, 0xa2, 0x3d} }, - { (uint64_t)2000ull, {0x24, 0xf5, 0xb1, 0x34, 0x78, 0x46, 0xaf, 0x22, 0xb5, 0x6f, 0x41, 0x25, 0xb3, 0xe7, 0x67, 0x8c, 0xf8, 0x4b, 0x4f, 0xd2, 0xf9, 0x2e, 0x1c, 0x40, 0xaa, 0x3a, 0x1b, 0xe0, 0xc7, 0x4d, 0x95, 0xe6} }, - { (uint64_t)3000ull, {0xa7, 0x1c, 0x9a, 0x8f, 0x40, 0xc1, 0x25, 0x9c, 0x36, 0x26, 0x27, 0x73, 0xe0, 0x8, 0x20, 0x18, 0x3e, 0x6b, 0x59, 0xe0, 0x71, 0xc9, 0x9b, 0x34, 0x9b, 0xef, 0x8f, 0x7e, 0xd2, 0xc6, 0xad, 0xb9} }, - { (uint64_t)4000ull, {0x98, 0xdc, 0x74, 0xaf, 0x19, 0x89, 0xd3, 0x4b, 0x64, 0x2e, 0xb3, 0x6, 0x2d, 0xbc, 0x9d, 0xca, 0xd8, 0x1, 0xc5, 0x65, 0x27, 0x6, 0x93, 0x99, 0xe7, 0xc4, 0x11, 0xad, 0x14, 0x28, 0x82, 0xf6} }, - { (uint64_t)5000ull, {0x61, 0x76, 0xac, 0x4a, 0xc0, 0x6, 0x5e, 0x49, 0xd6, 0xc4, 0x41, 0xcf, 0x40, 0x4f, 0xad, 0xda, 0xad, 0x44, 0x93, 0xe, 0xf0, 0x3c, 0x68, 0x9, 0xad, 0xd7, 0x77, 0xe4, 0x2f, 0xee, 0x7f, 0x10} }, - { (uint64_t)6000ull, {0x78, 0x79, 0x4, 0x65, 0xf6, 0x60, 0x5b, 0x5a, 0x84, 0x77, 0x36, 0x5a, 0xa6, 0xc2, 0xa4, 0xa5, 0x84, 0x91, 0xc, 0x23, 0x95, 0x2, 0x92, 0x97, 0x52, 0x49, 0xa1, 0xad, 0x7d, 0xf0, 0xf7, 0xe8} }, - { (uint64_t)7000ull, {0x20, 0xa5, 0x60, 0x6b, 0x60, 0x23, 0x95, 0xd6, 0x8e, 0x2f, 0xad, 0x8e, 0xc6, 0x7f, 0x92, 0xde, 0x89, 0xc6, 0x3e, 0x1e, 0x7f, 0xc1, 0xdd, 0x7f, 0x92, 0xff, 0xed, 0xb8, 0xf6, 0x55, 0xfb, 0xd} }, - { (uint64_t)8000ull, {0x9a, 0x78, 0x97, 0x43, 0x98, 0x65, 0x17, 0xd9, 0x5f, 0x4e, 0x80, 0x8b, 0xeb, 0xe6, 0x52, 0xd, 0xe6, 0xcf, 0x8c, 0x51, 0x35, 0xab, 0x36, 0x8, 0x7e, 0x87, 0xe2, 0x76, 0xac, 0x6a, 0x34, 0x1} }, - { (uint64_t)9000ull, {0x5f, 0xc7, 0xaa, 0x48, 0xbb, 0x19, 0x13, 0x58, 0xc7, 0xe3, 0x4d, 0x24, 0xcf, 0x9c, 0x31, 0x16, 0x74, 0x12, 0x7a, 0xb2, 0x45, 0xd0, 0x8f, 0x4e, 0x2c, 0xfd, 0xbf, 0x8f, 0x5, 0xc9, 0x5b, 0xf5} }, - { (uint64_t)10000ull, {0x61, 0x20, 0xe7, 0x76, 0xe9, 0x12, 0xab, 0x10, 0x5a, 0x49, 0xf9, 0xda, 0x2, 0xa6, 0x75, 0x17, 0xc0, 0xa9, 0xb, 0x2b, 0x3e, 0x2d, 0xa3, 0xd, 0xff, 0x34, 0x39, 0x93, 0xdb, 0xec, 0x95, 0x97} }, - { (uint64_t)20000ull, {0x77, 0xbf, 0xb5, 0x37, 0xac, 0xa, 0xbc, 0x41, 0xaa, 0x21, 0xd0, 0xec, 0xd9, 0x18, 0x13, 0x34, 0xd8, 0x6b, 0xa7, 0x86, 0x5a, 0x94, 0x47, 0xf5, 0xc1, 0x58, 0x9a, 0x81, 0xd7, 0xef, 0xb3, 0xbb} }, - { (uint64_t)30000ull, {0x35, 0xf4, 0x5, 0xa9, 0x5f, 0x75, 0x19, 0x2a, 0xe9, 0xc0, 0xd4, 0xf5, 0x88, 0x84, 0x47, 0x14, 0xf6, 0x85, 0x1b, 0x97, 0xce, 0xbd, 0x9f, 0x7c, 0x2, 0xc5, 0xdd, 0xd7, 0xbf, 0x58, 0xff, 0x31} }, - { (uint64_t)40000ull, {0x77, 0x55, 0xbb, 0x3f, 0x38, 0x7c, 0x21, 0xb8, 0xa0, 0xf4, 0x48, 0x1f, 0xbf, 0xa8, 0x8a, 0xbe, 0xee, 0xce, 0xc7, 0x56, 0x53, 0xfc, 0xa1, 0x89, 0x58, 0x39, 0xc1, 0xba, 0x6, 0x47, 0x9f, 0x96} }, - { (uint64_t)50000ull, {0x8b, 0x7e, 0x84, 0xa3, 0x37, 0xb7, 0xb9, 0xcd, 0x5d, 0xb3, 0x63, 0x33, 0x8, 0xad, 0x51, 0x86, 0xa3, 0x59, 0xd, 0xff, 0xb8, 0x23, 0x1e, 0x2f, 0x31, 0xfd, 0x20, 0x42, 0x54, 0x9f, 0xfb, 0xe2} }, - { (uint64_t)60000ull, {0xef, 0xfd, 0xa6, 0x25, 0x15, 0xea, 0xb1, 0xbc, 0x1e, 0xbd, 0x74, 0x92, 0x94, 0x9b, 0x1, 0x22, 0xc3, 0x9f, 0x71, 0xa, 0x65, 0x16, 0xec, 0x66, 0x8c, 0x37, 0x61, 0xe6, 0xcc, 0x36, 0x1f, 0x25} }, - { (uint64_t)70000ull, {0x16, 0xba, 0x89, 00, 0xf3, 0x6f, 0xf, 0x6c, 0x46, 0x1c, 0xb, 0xe7, 0x64, 0xae, 0xee, 0x48, 0x86, 0x6, 0xb0, 0x53, 0xed, 0xdc, 0x10, 0xb5, 0x9a, 0x3e, 0xde, 0xcd, 0x23, 0xd4, 0x4f, 0xc0} }, - { (uint64_t)80000ull, {0x4d, 0xd4, 0x70, 0x3b, 0x7b, 0x7f, 0xcf, 0xe7, 0x2a, 0x2e, 0x4f, 0x31, 0xa4, 0x34, 0x17, 0xf9, 0xc0, 0xda, 0x64, 0x2f, 0xd0, 0xa9, 0x29, 0xb8, 0xf5, 0xed, 0xd8, 0x3, 0x7f, 0x93, 0xc5, 0xb3} }, - { (uint64_t)90000ull, {0x8e, 0xfc, 0x3, 0x20, 0x40, 0xbd, 0x90, 0x41, 0xda, 0x3d, 0xb0, 0x9b, 0xa1, 0x3d, 0xa2, 0xa5, 0xd1, 0xb8, 0x12, 0x3, 0xa, 0x5a, 0x36, 0x7c, 0x58, 0x94, 0xbd, 0x54, 0x11, 0x9, 0xe7, 0x30} }, - { (uint64_t)100000ull, {0xbd, 0x2e, 0xb1, 0x97, 0x83, 0x57, 0x1c, 0xf2, 0x22, 0x2c, 0x81, 0xb, 0x69, 0xf, 0xc7, 0x66, 0x64, 0x57, 0xae, 0x20, 0x92, 0x5b, 0x90, 0x5, 0xce, 0xe6, 0x1d, 0xf2, 0x66, 0x6f, 0xdc, 0xb7} }, - { (uint64_t)200000ull, {0x83, 0xd4, 0xcd, 0xdd, 0xc1, 0x44, 0x87, 0x32, 0xf2, 0x97, 0x7c, 0x41, 0xaa, 0xa7, 0x1f, 0xe6, 0xde, 0x9c, 0x17, 0x6d, 0xa8, 0x99, 0xee, 0xbf, 0xfc, 0x1b, 0xb, 0xa9, 0xea, 0x92, 0x97, 0x90} }, - { (uint64_t)300000ull, {0xcc, 0xc0, 0x6b, 0x44, 0xc3, 0x1, 0x38, 0x6, 0x30, 0x45, 0xed, 0x1, 0xd2, 0x45, 0xd8, 0x14, 0x3, 0xb6, 0x36, 0x52, 0xeb, 0xc4, 0xf9, 0x96, 0x7f, 0xd, 0x7f, 0x38, 0x69, 0x7f, 0x46, 0x16} }, - { (uint64_t)400000ull, {0x1b, 0xbf, 0xe7, 0xe, 0xca, 0xf1, 0xdd, 0xd7, 0xf1, 0x2, 0x36, 0xf6, 0x8a, 0x41, 00, 0xb, 0x5d, 0xab, 0x2d, 0x47, 0x5c, 0xb9, 0x2f, 0x62, 0xc2, 0xd6, 0x84, 0xcf, 0x57, 0x69, 0xfb, 0x84} }, - { (uint64_t)500000ull, {0xf1, 0xb1, 0xcd, 0xaa, 0x78, 0x14, 0x95, 0x36, 0xf, 0x53, 0x31, 0x81, 0xaa, 0x58, 0xc8, 0xbd, 0xae, 0x6a, 0x77, 0x98, 0xd0, 0x2d, 0xab, 0x6d, 0x56, 0x26, 0x81, 0x27, 0x67, 0x9, 0xe7, 0x1} }, - { (uint64_t)600000ull, {0xd5, 0x26, 0x7d, 0x60, 0xd4, 0xfe, 0x9b, 0xc5, 0xfe, 0xfa, 0x7d, 0x3f, 0xe0, 0x7c, 0xd1, 0xfa, 0xd4, 0x55, 0x73, 0xd5, 0xae, 0x19, 0x10, 0xda, 0x7, 0x3e, 0x6d, 0x2d, 0xf9, 0xe2, 0x4, 0x39} }, - { (uint64_t)700000ull, {0xb, 0x58, 0x11, 0x25, 0xc2, 0xc4, 0x83, 0xc9, 0xa3, 0xd8, 0xbc, 0x8, 0x32, 0x2f, 0x26, 0xaa, 0x1f, 0xc5, 0xe, 0x41, 0x53, 0x2c, 0x1b, 0x9d, 0xf6, 0x26, 0xb0, 0x9, 0xd7, 0x88, 0x67, 0xcf} }, - { (uint64_t)800000ull, {0xf5, 0xb3, 0xd1, 0x8f, 0x66, 0xd0, 0xf9, 0x17, 0x5c, 0x30, 0x83, 0xb5, 0xf8, 0x7, 0x8e, 0xaf, 0xa8, 0x9e, 0xf8, 0x1d, 0xe7, 0x15, 0x8, 0xbc, 0x25, 0x1f, 0x5c, 0x5f, 0xe7, 0x25, 0x2e, 0x6} }, - { (uint64_t)900000ull, {0x1, 0xde, 0x40, 0x2c, 0x4b, 00, 0x43, 0x4, 0x2e, 0xae, 0x9e, 0xde, 0xa1, 0x49, 0x2b, 0x9d, 0x82, 0xb7, 0xbc, 0x36, 0x68, 0xe9, 0xb5, 0x84, 0xb0, 0x31, 0x3d, 0x44, 0x50, 0x53, 0x40, 0x74} }, - { (uint64_t)1000000ull, {0x53, 0x4b, 0x85, 0xc7, 0x89, 0x3f, 0x66, 0xf0, 0x26, 0xb6, 0x5e, 0xd7, 0xe7, 0xa4, 0xb8, 0xc9, 0xf4, 0xb, 0xe3, 0x1b, 0xcd, 0xa, 0x3d, 0xcd, 0x27, 0xc4, 0x71, 0x2, 0x56, 0x51, 0x65, 0x3} }, - { (uint64_t)2000000ull, {0xcd, 0xb5, 0xda, 0xfa, 0x53, 0x10, 0xf5, 0x26, 0x2f, 0xfc, 0x9, 0x26, 0xd0, 0xdf, 0x6e, 0xeb, 0xee, 0x2d, 0x52, 0xa9, 0x8d, 0xc6, 0x9f, 0xd, 0xc5, 0xe4, 0xeb, 0xf0, 0xc1, 0xa8, 0x77, 0x2e} }, - { (uint64_t)3000000ull, {0x9e, 0x75, 0x63, 0xf0, 0x33, 0x59, 0xea, 0x31, 0xe2, 0x91, 0xe7, 0xf0, 0xb8, 0x74, 0x17, 0xbc, 0xf5, 0xb2, 0x34, 0xee, 0x8b, 0x7e, 0x5b, 0x4, 0x41, 0x73, 0xbf, 00, 0x46, 0x86, 0x7c, 0x57} }, - { (uint64_t)4000000ull, {0xfd, 0x2a, 0xeb, 0xd, 0x5e, 0xe5, 0x3b, 0x77, 0xf2, 0xb1, 0xe3, 0xac, 0x75, 0x2d, 0x19, 0x38, 0x9f, 0xc5, 0xba, 0xa0, 0xf8, 0xd7, 0x64, 0x48, 0xa5, 0x9f, 0x99, 0x85, 0xa4, 0x8d, 0xa, 0x25} }, - { (uint64_t)5000000ull, {0xc0, 0xbe, 0x4f, 0xb8, 0x77, 0xb9, 0xce, 0x50, 0x87, 0x71, 0x32, 0x3b, 0xcf, 0x1f, 0xb9, 0x48, 0x47, 0x10, 0xee, 0x23, 0x2, 00, 0x6, 0xc3, 0xe8, 0xca, 0xac, 0x6e, 0x4f, 0x2, 0xfa, 0xbf} }, - { (uint64_t)6000000ull, {0xfc, 0x44, 0x5c, 0xa3, 0x84, 0xf3, 0x3e, 0x55, 0x8d, 0xc1, 0x56, 0x44, 0x9d, 0x3f, 0xba, 0x6a, 0xfd, 0x54, 0xc3, 0x42, 0xe6, 0x35, 0x11, 0xf, 0xe7, 0x9c, 0x16, 0xc7, 0x17, 0xf7, 0xd4, 0xf7} }, - { (uint64_t)7000000ull, {0xd8, 0x9, 0x2b, 0x8d, 0x45, 0xdb, 0x54, 0xa5, 0x6d, 0x64, 0xe8, 0x9, 0x4a, 0x6, 0x22, 0xe2, 0x6e, 0x8a, 0x2e, 0xec, 0xb9, 0x3, 0xb2, 0xe1, 0xf7, 0x5a, 0x83, 0x7b, 0x3a, 0xd8, 0x55, 0x4a} }, - { (uint64_t)8000000ull, {0x10, 0x4, 0x5c, 0x91, 0xdb, 0xad, 0x8a, 0x6a, 0x81, 0x62, 0x4a, 0xe0, 0xcf, 0x20, 0x5d, 0xb9, 0x97, 0x3e, 0xe8, 0x42, 0x3e, 0x97, 0xaf, 0x58, 0xa6, 0x1c, 0xfa, 0x7a, 0x78, 0x66, 0xf4, 0x1} }, - { (uint64_t)9000000ull, {0x11, 0x5c, 0x20, 0x9e, 0xe1, 0xde, 0xf3, 0x10, 0xce, 0xc9, 0xa6, 0xd1, 0x6c, 0xe6, 0x27, 0xec, 0xbd, 0xb9, 0xff, 0x2c, 0x23, 0x9, 0x3c, 0x24, 0xc8, 0x6c, 0x1b, 0xf2, 0x50, 0xd4, 0xb5, 0x85} }, - { (uint64_t)10000000ull, {0x2f, 0x99, 0xd9, 0x74, 0x44, 0x18, 0x66, 0x9, 0x49, 0xba, 0x43, 0x35, 0x61, 0xc6, 0x5, 0xb6, 0xf7, 0xbe, 0x8f, 0x82, 0xa, 0x93, 0xcb, 0x2a, 0xed, 0xa9, 0x7c, 0x87, 0x32, 0x92, 0x56, 0x49} }, - { (uint64_t)20000000ull, {0xc6, 0x77, 0x1f, 0xab, 0x14, 0xb, 0x75, 0xf4, 0xef, 0xd0, 0x97, 0xfc, 0xe1, 0x82, 0x6b, 0x80, 0xba, 0xe3, 0x16, 0xbc, 0xec, 0x28, 0x86, 0x9b, 0x3a, 0x1b, 0xf1, 0xbc, 0x6e, 0x4d, 0x20, 0x43} }, - { (uint64_t)30000000ull, {0xa1, 0xe9, 0xed, 0x3e, 0xf6, 0x5a, 0x9d, 0x52, 0x6c, 0xc2, 0x62, 0x5, 0x88, 0x12, 0x1, 0xd8, 0xa8, 0xf2, 0xc4, 0x40, 0x9f, 0xa3, 0x64, 0x10, 0x72, 0x96, 0xb9, 0xf9, 0x6a, 0x61, 0xb3, 0x58} }, - { (uint64_t)40000000ull, {0xb5, 0x31, 0x2d, 0xc7, 0x72, 0x94, 0xab, 0x9b, 0xc8, 0xbf, 0xd1, 0x39, 0x1e, 0x9a, 0xca, 0x92, 0x45, 0xe2, 0x28, 0xf7, 0x4b, 0x49, 0x74, 0xfc, 0x29, 0xad, 0x1c, 0x31, 0xcb, 0xe3, 0xe6, 0xa3} }, - { (uint64_t)50000000ull, {0xb8, 0xab, 0xc9, 0xff, 0xf6, 0x84, 0x1d, 0x2e, 0xa0, 0x13, 0x5a, 0x21, 0x72, 0xd3, 0xa7, 0xb, 0xfc, 0x2b, 0x70, 0x22, 0x8, 0xcd, 0x4a, 0x43, 0xc6, 0x30, 0xbe, 0xb1, 0xb8, 0xa0, 0x32, 0x8b} }, - { (uint64_t)60000000ull, {0x63, 0x90, 0xe1, 0xdb, 0x81, 0xb0, 0xea, 0x5c, 0xe2, 0x73, 0x94, 0x14, 0xe5, 0x2b, 0x7, 0x98, 0xd8, 0x2e, 0xb8, 0xe9, 0xae, 0xc5, 0x6d, 0xfe, 0x7e, 0x2c, 0x64, 0x11, 0xab, 0x79, 0x41, 0x87} }, - { (uint64_t)70000000ull, {0x7e, 0x51, 0xaf, 0xee, 0x5b, 0xc9, 0x71, 0x52, 0x9d, 0x64, 0x4d, 0xcd, 0x7f, 0x2a, 0x2a, 0xb0, 0x26, 0x69, 0xce, 0x2c, 0xb5, 0x7, 0xa6, 0x2d, 0xfc, 0x93, 0x17, 0x6c, 0xb6, 0xdf, 0x41, 0x38} }, - { (uint64_t)80000000ull, {0xf3, 0x7b, 0x94, 0x6b, 0x8b, 0x24, 0x88, 0xeb, 0xee, 0x1c, 0x6, 0xc1, 0x27, 0xfb, 0xe5, 0xfa, 0x5e, 0xfd, 0x62, 0x36, 0x9d, 0xd5, 0xaa, 0xda, 0xed, 0xd8, 0x88, 0x50, 0x1d, 0x3b, 0x7e, 0x3b} }, - { (uint64_t)90000000ull, {0x46, 0xcb, 0x76, 0x57, 0xf6, 0x1c, 0x83, 0x7c, 0xec, 0x80, 0x74, 0xbb, 0xb0, 0xf5, 0x2e, 0x7f, 0xc5, 0x9a, 0xd, 0x94, 0xe0, 0x17, 00, 0x9a, 0xbe, 0x25, 0x65, 0x2e, 0x4a, 0xd2, 0xe5, 0x3d} }, - { (uint64_t)100000000ull, {0x66, 0x7b, 0x8e, 0x6f, 0x6a, 0x4b, 0x91, 0x89, 0x76, 0xd9, 0x73, 0x5a, 0x43, 0x36, 0x7d, 0xc7, 0x59, 0x2c, 0x87, 0xd0, 0xa1, 0xf8, 0x15, 0xc6, 0xe8, 0x7d, 0xf1, 0x1a, 0x13, 0x50, 0x9f, 0xb2} }, - { (uint64_t)200000000ull, {0x3b, 0xcb, 0x51, 0x48, 0x1, 0x64, 0x1b, 0x62, 0x55, 0x93, 0x8c, 0xc5, 0x3, 0x76, 0x2d, 0x35, 0xce, 0x6, 0xd7, 0x5f, 0xe9, 0x50, 0x95, 0x9a, 0x1a, 0xab, 0x21, 0x4b, 0x50, 0x9b, 0x10, 0xb} }, - { (uint64_t)300000000ull, {0xa5, 0x92, 0x6f, 0x3, 0x1e, 0x6b, 0x15, 0xeb, 0x86, 0x23, 0x51, 0x8, 0xab, 0xb1, 0xaf, 0x90, 0xc5, 0xb1, 0x62, 0xc3, 0x99, 0x8c, 0x8b, 0xbb, 0x3f, 0xfb, 0xb0, 0x72, 0x9d, 0xa9, 0x45, 0x7b} }, - { (uint64_t)400000000ull, {0xfe, 0x35, 0xb6, 0x99, 0x44, 0x41, 0xe, 0xaf, 0x81, 0x5b, 0xdc, 0xd0, 0xa4, 0xd7, 0x1e, 0xf9, 0xfc, 0x66, 0x86, 0x48, 0xad, 0x43, 0x74, 0x3b, 0x3, 0x5a, 0xed, 0x2c, 0x17, 0xc1, 0x38, 0x7a} }, - { (uint64_t)500000000ull, {0x22, 0x22, 0xd6, 0x70, 0xb8, 0x7d, 0x9b, 0x47, 0xb8, 0xb9, 0x5c, 0x8c, 0x39, 0x7b, 0xc5, 0x2e, 0x2b, 0x46, 0xa6, 0x48, 0xb0, 0x2, 0xa0, 0x48, 0x5a, 0x37, 0x5c, 0xd8, 0x1f, 0x4a, 0x54, 0x5f} }, - { (uint64_t)600000000ull, {0xd3, 0x23, 0x8a, 0x4a, 0x8b, 0x71, 0xab, 0x46, 0xd1, 0x53, 0x4, 0xac, 0xfa, 0x2f, 0x40, 0xbf, 0x5e, 0xa6, 0x3b, 0x3d, 0x86, 0x4a, 0x79, 0xfa, 0x84, 0x25, 0xd2, 0x65, 0x5a, 0xe7, 0x7, 0x6f} }, - { (uint64_t)700000000ull, {0xa8, 0xff, 0x28, 0x3f, 0xcf, 0xf0, 0x53, 0xd3, 0x44, 0xc8, 0xf7, 0x56, 0x4f, 0x40, 0x24, 0xb6, 0x6b, 0xfa, 0x45, 0x9f, 0x47, 0x6f, 0xd, 0x73, 0xc, 0x91, 0x39, 0x90, 0x8b, 0x2d, 0x64, 0x7e} }, - { (uint64_t)800000000ull, {0xf2, 0xda, 0xf8, 0x88, 0xc4, 0x46, 0x57, 0x1, 0xc0, 0xe6, 0x1e, 0x12, 0xc3, 0xfb, 0xd4, 0xea, 0x79, 0xc7, 0xec, 0xb4, 0xf0, 0xc4, 0xb1, 0x54, 0xc5, 0x1a, 0x24, 0xd1, 0xe9, 0x21, 0x28, 0xba} }, - { (uint64_t)900000000ull, {0x11, 0x6a, 0xe5, 0xd2, 0x9c, 0xec, 0x72, 0xaa, 0xc5, 0x57, 0xcb, 0x14, 0xe2, 0xcd, 0xd5, 0x53, 0xe5, 0x88, 0xff, 0x8b, 0x81, 0x78, 0x26, 0x1, 0x99, 0xc4, 0xc, 0xae, 0xa2, 0x12, 0xcb, 0x63} }, - { (uint64_t)1000000000ull, {0x8c, 0xe6, 0x48, 0x33, 0xce, 0xc9, 00, 0xcb, 0x6d, 0x5a, 0xc4, 0x6f, 0xc0, 0x23, 0x7d, 0x8f, 0x24, 0x39, 0xc3, 0xdf, 0xa2, 0x38, 0xba, 0xf9, 0xcc, 0x94, 0x16, 0x6a, 0xd2, 0xe8, 0x98, 0x87} }, - { (uint64_t)2000000000ull, {0x37, 0x8d, 0x3c, 0x5d, 0xbb, 0xa4, 0x82, 0x3d, 0x33, 0x12, 0xbb, 0x61, 0xfc, 0x6, 0x75, 0xa1, 0xbb, 0x39, 0x89, 0xf3, 0x97, 0x1, 0xeb, 0xd, 0x5c, 0xe4, 0xde, 0x5b, 0xd, 0x90, 0x74, 0x72} }, - { (uint64_t)3000000000ull, {0x7f, 0xa2, 0xd0, 0xa5, 0x99, 0xe7, 0x97, 0x2e, 0x74, 0xcb, 0x75, 0xf9, 0x8a, 0xf4, 0x84, 0xfc, 0x85, 0x19, 0xcb, 0x7e, 0x25, 0xb9, 0x84, 0xa7, 0x6d, 0x8b, 0xc2, 0xba, 0x8d, 0xaf, 0xde, 0xd8} }, - { (uint64_t)4000000000ull, {0xda, 0x3a, 0xcb, 00, 0xab, 0x2d, 0x8d, 0xcc, 0xac, 0xec, 0x8f, 0x77, 0x59, 0x21, 0xc4, 0xe, 0x26, 0xb1, 0xff, 0xbe, 0xca, 0x9e, 0xb7, 0xe6, 0x57, 0x25, 0x6f, 0x59, 0x68, 0xf2, 0x34, 0x1c} }, - { (uint64_t)5000000000ull, {0x34, 0x6, 0xd7, 0x9a, 0x50, 0xd8, 0x14, 0xa9, 0xcc, 0xed, 0x3b, 0x24, 0x4, 0xed, 0x3e, 0x1b, 0x8d, 0xa6, 0x21, 0x98, 0x8c, 0x43, 0xb1, 0x93, 0x69, 0x42, 0xf4, 0x94, 0xa, 0xc5, 0xbf, 0x6a} }, - { (uint64_t)6000000000ull, {0xf8, 0x3e, 0xe8, 0xc1, 0x62, 0xfc, 0x52, 0xa0, 0x8, 0x9f, 0x46, 0xe8, 0x29, 0xc2, 0xea, 0xf6, 0xa1, 0x9f, 0xd5, 0x96, 0xcd, 0x12, 0xb3, 0xe8, 0x19, 0xd5, 0x67, 0x69, 0x44, 0xf, 0x7b, 0x4e} }, - { (uint64_t)7000000000ull, {0x8c, 0x72, 0x7d, 0x24, 0x57, 0xf3, 0x4b, 0x2f, 0xdb, 0x6a, 0xdf, 0x69, 0x1a, 0xb3, 0x5f, 0xaa, 0xe4, 0xff, 0x23, 0x4c, 0x28, 0xb4, 0x4e, 0x9f, 0xd3, 0x71, 0x8e, 0xef, 0xec, 0x41, 0x75, 0x80} }, - { (uint64_t)8000000000ull, {0x4a, 0x2e, 0x2f, 0x76, 0xe3, 0x5d, 0xcb, 0xa8, 0x97, 0xa3, 0xae, 0x72, 0xc4, 0x27, 0xd, 0x9c, 0x13, 0x17, 0x14, 0xed, 0x19, 0x1b, 0x55, 0x5c, 0x5e, 0x1, 0xe4, 0x75, 0x7c, 0xba, 0xe7, 0x2c} }, - { (uint64_t)9000000000ull, {0x3f, 0x9f, 0xc, 0x4, 0xc0, 0xb9, 0xec, 0x9b, 0x4d, 0x11, 0x7c, 0x5f, 0xc9, 0xf1, 0x8a, 0x20, 0xf2, 0xb3, 0xfa, 0xcc, 0xa4, 0xc8, 0xae, 0x41, 0xaf, 0x7c, 0x8, 0xe9, 0xe0, 0xef, 0xb9, 0x81} }, - { (uint64_t)10000000000ull, {0x97, 0xc9, 0x2a, 0x29, 0x1, 0x5e, 0xcb, 0x49, 0xf8, 0x9, 0x5, 0x45, 0xe0, 0x1f, 0xf9, 0x78, 0x6c, 0xae, 0x40, 0x57, 0x73, 0x47, 0x61, 0x18, 0x24, 0xf4, 0xb6, 0x59, 0x9f, 0xf5, 0xd3, 0x64} }, - { (uint64_t)20000000000ull, {0x9, 0xba, 0xed, 0x9a, 0x3c, 0x44, 0xb2, 0x22, 0x85, 0xa0, 0xae, 0xa4, 0x14, 0x8c, 0xa7, 0xde, 0x9b, 0xea, 0x96, 0x3c, 0xf6, 0x96, 0x23, 0xb6, 0x83, 0x44, 0x5c, 0xa, 0x10, 0xa5, 0x86, 0x77} }, - { (uint64_t)30000000000ull, {0x45, 0xac, 0xaf, 0x1d, 0xe2, 0x89, 0x6d, 0xe8, 0x72, 0x84, 0xff, 0xed, 0x57, 0x8b, 0x77, 0x14, 0xf5, 0x18, 0xa6, 0x18, 0xe2, 0xae, 0x6f, 0x90, 0xae, 0x4f, 0x70, 0x13, 0xa2, 0x8e, 0x99, 0xe0} }, - { (uint64_t)40000000000ull, {0x8, 0xb8, 0x47, 0x36, 0x42, 0x24, 0xe2, 0x9c, 0xe3, 0x36, 0x63, 0x93, 0xc2, 0xe1, 0x1e, 0xfc, 0x75, 0x55, 0xde, 0xe1, 0xa0, 0x5f, 0x91, 0xa7, 0x2e, 0x61, 0x11, 0x76, 0x84, 0xdd, 0xbe, 0x29} }, - { (uint64_t)50000000000ull, {0x6c, 0x8e, 0xe, 0x4a, 0x63, 0x4f, 0x85, 0x9a, 0x31, 0xab, 0x2f, 0x7a, 0x78, 0xc0, 0xc4, 0xa5, 0x93, 0x8c, 0xb7, 0x7f, 0x3, 0x35, 0x50, 0xa4, 0x7d, 0x7e, 0x31, 0x81, 0xb6, 0xb2, 0x6e, 0xc0} }, - { (uint64_t)60000000000ull, {0x66, 0xc2, 0xa0, 0x9, 0x65, 0xf9, 0xbf, 0xcb, 0xb1, 0x1e, 0xa0, 0x3c, 0xf1, 0xd6, 0x31, 0xb0, 0xe, 0x8a, 0x1e, 0xf7, 0xa6, 0xb, 0x1b, 0xe4, 0xa5, 0xac, 0x9, 0x23, 0xb, 0xf8, 0x17, 0x3f} }, - { (uint64_t)70000000000ull, {0x63, 0x51, 0xd7, 0x74, 0xc0, 0x2c, 0x5a, 0x9d, 0xee, 0xcf, 0xdb, 0xab, 0x70, 0x96, 0x68, 0x59, 0x8c, 0x47, 0xe4, 0xb1, 0x78, 0x2c, 0xe5, 0xae, 0x31, 0x6a, 0xf7, 0x40, 0xa6, 0x6f, 0x7e, 0x30} }, - { (uint64_t)80000000000ull, {0x5a, 0xcc, 0xfd, 0x16, 0x22, 0x79, 0xa5, 0x1c, 0x8b, 0x3b, 0xd5, 0xd3, 0x67, 0x9e, 0x91, 0x89, 0x67, 0xa2, 0x64, 0xea, 0x6, 0x3d, 0x37, 0xdf, 0xf5, 0xe3, 0x45, 0x7e, 0xc3, 0x7, 0xd4, 0x57} }, - { (uint64_t)90000000000ull, {0xb7, 0x47, 0xfc, 0x1, 0xc6, 0xf0, 0xc7, 0x49, 0x67, 0x3a, 0x29, 0x10, 0x25, 0xc, 0x2e, 0x23, 0xcb, 0x38, 0x27, 0x4d, 0x63, 0xb4, 0x2f, 0x52, 0x1b, 0x84, 0x63, 0x56, 0xe4, 0x13, 0x61, 0x8f} }, - { (uint64_t)100000000000ull, {0x9, 0x42, 0x84, 0x3d, 0x6f, 0x69, 0xe1, 0xcf, 0x3d, 0x99, 0xc9, 0x9f, 0xc, 0x97, 0xc0, 0xe6, 0xe5, 0x78, 0x93, 0x5a, 0xf6, 0xa8, 0xbd, 0xb8, 0xf8, 0x1d, 0x5b, 0x90, 0xbd, 0xe7, 0xcc, 0x10} }, - { (uint64_t)200000000000ull, {0x56, 0x4c, 0x64, 0xea, 0x50, 0xe4, 0xbd, 0x20, 0xdb, 0x58, 0x5d, 0xb5, 0x87, 0xb1, 0xf7, 0x64, 0xa2, 0x62, 0xd8, 0x46, 0xa6, 0xb0, 0xa2, 0x4b, 0x43, 0x27, 0x60, 0xd2, 0xf9, 0xde, 0x66, 0x5b} }, - { (uint64_t)300000000000ull, {0xac, 0x65, 0x83, 0x41, 0x5b, 0xd6, 0x4c, 0x3, 0x35, 0x97, 0xf9, 0x28, 0xa4, 0xb5, 0xd4, 0xf4, 0x78, 0x9e, 0xa8, 0xb2, 0x87, 0x82, 0x73, 0x89, 0xa8, 0x1e, 0xb6, 0x62, 0x9e, 0xc5, 0xb8, 0x50} }, - { (uint64_t)400000000000ull, {0x52, 0xf4, 0x9d, 0x89, 0xcf, 0x74, 0x13, 0x2f, 0xc7, 0x43, 0x2e, 0x6a, 0x6b, 0xef, 0xcf, 0xf3, 0xfd, 0x13, 0xd6, 0x3b, 0x51, 0x60, 0xab, 0x1c, 0xe6, 0x4a, 0xb0, 0xd1, 0x21, 0xcd, 0xa9, 0x9a} }, - { (uint64_t)500000000000ull, {0xe9, 0xaa, 0x7c, 0x81, 0xcd, 0xb5, 0xb3, 0x14, 0x8f, 0xb7, 0x62, 0x80, 0x63, 0xcd, 0x7a, 0x7, 0xd1, 0xad, 0xd1, 0x64, 0x3c, 0xed, 0xd3, 0xfa, 0x34, 0x47, 0x9d, 0x85, 0x9c, 0xc5, 0x62, 0x65} }, - { (uint64_t)600000000000ull, {0x98, 0x27, 0xae, 0x31, 0xe5, 0xc2, 0xa7, 0x78, 0x39, 0xf6, 0xb, 0x83, 0xab, 0x45, 0x78, 0xe2, 0xa0, 0x1e, 0xfa, 0x4b, 0x3b, 0x14, 0xcc, 0x72, 0x73, 0x14, 0xff, 0xd7, 0x15, 0x53, 0x63, 0xbf} }, - { (uint64_t)700000000000ull, {0x72, 0x91, 0x6a, 0x79, 0x27, 0xff, 0x13, 0x24, 0xd4, 0x98, 0x40, 0xec, 0xc0, 0x98, 0x68, 0xb8, 0xf3, 0x15, 0xe4, 0xf1, 0xf6, 0xd4, 0x45, 0x8d, 0x37, 0x5e, 0xc7, 0x45, 0xfc, 0x2e, 0x63, 0x53} }, - { (uint64_t)800000000000ull, {0x66, 0x76, 0xe0, 0x4, 0xf, 0xa4, 0xb8, 0x22, 0x9c, 0x61, 0x69, 0xc, 0x71, 0x32, 0x22, 0xcf, 0x3d, 0x37, 0xb9, 0x49, 0x3b, 0x49, 0x6, 0x80, 0xbb, 0x48, 0xd8, 0xd5, 0x1a, 0xde, 0x95, 0xf2} }, - { (uint64_t)900000000000ull, {0x41, 0x54, 0xb3, 0x46, 0x5a, 0x43, 0x72, 0x67, 0x1e, 0xa9, 0xe0, 0x64, 0xa7, 0xca, 0xa6, 0x6e, 0x14, 0xb4, 0x98, 0x6a, 0x46, 0x68, 0x91, 0x8a, 0xfa, 0x57, 0x9b, 0xf1, 0xed, 0x25, 0x6, 0xdd} }, - { (uint64_t)1000000000000ull, {0xbb, 0x6f, 0x70, 0x62, 0xca, 0x30, 0x6d, 0x67, 0x2a, 0x73, 0xe, 0x2a, 0x2f, 0x21, 0x9b, 0xdb, 0xe4, 0xc, 0x9f, 0xb3, 0xfe, 0x4d, 0x60, 0x13, 0x69, 0x2a, 0xf9, 0x3c, 0xdb, 0x2e, 0xc, 0xd1} }, - { (uint64_t)2000000000000ull, {0xbc, 0xe, 0xae, 0x5b, 0x9c, 0x6a, 0xd6, 0x38, 0x7a, 0x41, 0x19, 0x3c, 0x46, 0xf3, 0xc1, 0xd0, 0x71, 0x6d, 0x77, 0xd6, 0x4e, 0x22, 0xb2, 0xe0, 0x7b, 0x4b, 0xce, 0x75, 0x67, 0x65, 0xa2, 0xb} }, - { (uint64_t)3000000000000ull, {0x10, 0xc, 0x6f, 0x13, 0x42, 0xb7, 0x1b, 0x73, 0xed, 0xdd, 0xc5, 0x49, 0x2b, 0xe9, 0x23, 0x18, 0x2f, 00, 0xa6, 0x83, 0x48, 0x8e, 0xc3, 0xa2, 0xa1, 0xc7, 0xa9, 0x49, 0xcb, 0xe5, 0x77, 0x68} }, - { (uint64_t)4000000000000ull, {0x41, 0x9f, 0x7c, 0x94, 0x91, 0x4, 0x34, 0xf, 0xd3, 0xce, 0x85, 0x94, 0x8d, 0x2e, 0xf9, 0xf0, 0xdd, 0x4b, 0xb3, 0xd9, 0x2f, 0x5a, 0x78, 0x2c, 0x5f, 0x78, 0x4, 0xb7, 0x52, 0x9a, 0x13, 0xc6} }, - { (uint64_t)5000000000000ull, {0x40, 0x65, 0x34, 0x98, 0xbe, 0xa0, 0x22, 0xe3, 0x36, 0x5a, 0x3, 0xe5, 0x75, 0x25, 0xba, 0x65, 0x96, 0x53, 0x76, 0x24, 0x4f, 0xff, 0x10, 0x73, 0xe, 0xd9, 0x7a, 0x73, 0xb7, 0x53, 0x1, 0x91} }, - { (uint64_t)6000000000000ull, {0xdb, 0x1c, 0x7c, 0xf6, 0x8, 0x91, 0xf9, 0x65, 0xeb, 0xa9, 0xc6, 0x2, 0x24, 00, 0x63, 0xe, 00, 0x47, 0x95, 0x34, 0xe6, 0xf5, 0xb5, 0x33, 0xdc, 0xfc, 0x83, 0x19, 0x38, 0x52, 0x2c, 0x78} }, - { (uint64_t)7000000000000ull, {0x59, 0xa0, 0x3a, 0x31, 0x53, 0xa9, 0x94, 0xd7, 0x23, 0x27, 0xe4, 0xd9, 0x24, 0x21, 0xd3, 0xe3, 0x29, 0x1b, 0x1f, 0xa1, 0xb2, 0x40, 0xde, 0x44, 0xb9, 0x2d, 0x7f, 0x62, 0xec, 0x1, 0x28, 0xf1} }, - { (uint64_t)8000000000000ull, {0xb2, 0x80, 0xb9, 0x3b, 0x1e, 0x43, 0x88, 00, 0x73, 0xea, 0x4a, 0xa0, 0xef, 0x11, 0x4, 0xf8, 0x24, 0xbd, 0x12, 0x7a, 0x4a, 0x3d, 0xa2, 0x13, 0x92, 0x65, 0xf, 0xe8, 0xc6, 0x55, 0xb6, 0xc5} }, - { (uint64_t)9000000000000ull, {0xda, 0xf0, 0xd3, 0xe9, 0x32, 0x17, 0xd8, 0xe9, 0x5a, 0xbf, 0xdd, 0xf1, 0x3b, 0x7f, 0xd4, 0x8e, 0x34, 0x47, 0xad, 0x9, 0x23, 0x26, 0xb8, 0x99, 0xed, 0x58, 0x1f, 0xd5, 0xf8, 0x6, 0xc5, 0x6} }, - { (uint64_t)10000000000000ull, {0x16, 0x3d, 0xd6, 0x82, 0xec, 0x97, 0x7c, 0xdd, 0xa5, 0x95, 0x31, 0xda, 0x3f, 0xfa, 0x72, 0x99, 0x8a, 0x6f, 0x88, 0x37, 0xab, 0xad, 0xc6, 0x36, 0xaa, 0xed, 0xc8, 0xbe, 0x19, 0xb2, 0xd7, 0xc7} }, - { (uint64_t)20000000000000ull, {0x2, 0xfa, 0x35, 0x3a, 0xa8, 0x4e, 0xa8, 0xc4, 0x4c, 0x80, 0x23, 0x6, 0x5d, 0x79, 0x41, 0x60, 0x6b, 0x1f, 0xa5, 0xc2, 0x64, 0xdc, 0xcf, 0x46, 0xdc, 0x64, 0x94, 0xeb, 0xe9, 0x60, 0x6f, 0x20} }, - { (uint64_t)30000000000000ull, {0x87, 0x5, 0xd, 0xab, 0xf5, 0xb2, 0x3e, 0x8b, 0x79, 0x81, 0x3f, 0x4e, 0xd7, 0x6a, 0xa4, 0xad, 0xd2, 0x25, 0xdd, 0x2a, 0x50, 0x89, 0xaf, 0x6, 0x7d, 0xa7, 0x7c, 0xcb, 0x6e, 0xc5, 0x59, 0x46} }, - { (uint64_t)40000000000000ull, {0xaa, 0xe6, 0xb2, 0xc8, 0xa2, 0x9e, 0x4d, 0xbc, 0x63, 0x76, 0xc1, 0x72, 0x5, 0xfb, 0x2, 0x85, 0xe7, 0xd7, 0xd3, 0x25, 0x32, 0x3c, 0xd5, 0x26, 0xf, 0x98, 0xad, 0xff, 0xf7, 0xd4, 0xd4, 0xfb} }, - { (uint64_t)50000000000000ull, {0x9d, 0x79, 0x28, 0x82, 0x12, 0xa1, 0xe2, 0x3c, 0x9, 0x9f, 0xb2, 0xd8, 0xf0, 0xd0, 0xdb, 0xd3, 0xc2, 0xec, 0xd7, 0x58, 0xb9, 0xe6, 0xb5, 0xb4, 0xf2, 0x90, 0x60, 0x7, 0x9f, 0x19, 0x66, 0x9f} }, - { (uint64_t)60000000000000ull, {0x18, 0x90, 0x10, 0x6f, 0x1b, 0x97, 0xbc, 0x2d, 0xa, 0xe3, 0x96, 0xe5, 0xe5, 0x5e, 0xbf, 0xcc, 0x8e, 0xf6, 0x91, 0x7f, 0xb1, 0x96, 0xcb, 0x2b, 0x1e, 0x80, 0x25, 0x5d, 0x54, 0xb6, 0x87, 0x10} }, - { (uint64_t)70000000000000ull, {0x57, 0xd3, 0x4e, 0xf7, 0x54, 0x3b, 0xe4, 0x7b, 0x7b, 0xf4, 0x97, 0xce, 0x4a, 0x17, 0x6e, 0x78, 0xc6, 0xd6, 0x5c, 0xd3, 0x27, 0xf6, 0x4b, 0xa7, 0x5c, 0x27, 0xd1, 0x57, 0xb3, 0x37, 0x12, 0x5d} }, - { (uint64_t)80000000000000ull, {0x5e, 0xcb, 0x10, 0x15, 0x4b, 0x96, 0xca, 0xb5, 0x5e, 0x9, 0x46, 0x83, 0xf8, 0xdb, 0xff, 0x7f, 0x56, 0x63, 0x5f, 0xa6, 0x64, 0x97, 0xee, 0x9e, 0x24, 0xe, 0x83, 0x63, 0x7c, 0x7c, 0x87, 0x72} }, - { (uint64_t)90000000000000ull, {0x42, 0x32, 0x69, 0x98, 0x51, 0x30, 0xf1, 0x66, 0x51, 0x6a, 0x5b, 0xa8, 0x61, 0x9, 0x6d, 0x72, 0xec, 0xcc, 0x67, 0xad, 0xab, 0xa4, 0x5e, 0xb3, 0x73, 0x9a, 0xe, 0xbc, 0x61, 0xa3, 0x20, 0xae} }, - { (uint64_t)100000000000000ull, {0xa8, 0xd1, 0x60, 0x95, 0x91, 0x49, 0x8f, 0xa7, 0xc2, 0x94, 0x27, 0xad, 0x89, 0x31, 0xaf, 0x36, 0xc5, 0x2d, 0xc9, 0x7b, 0x4a, 0x11, 0xe7, 0x47, 0xa9, 0x56, 0xc2, 0x8c, 0x42, 0x54, 0xcf, 0xd4} }, - { (uint64_t)200000000000000ull, {0x23, 0x14, 0x49, 00, 0xa8, 0x66, 0xe8, 0xc1, 0xbf, 0x40, 0x98, 0xda, 0xa9, 0x48, 0xb9, 0x86, 0xf3, 0x84, 0xe, 0x5a, 0x7d, 0x21, 0x5e, 0xf0, 0xd5, 0x64, 0xef, 0xd8, 0xbe, 0xc6, 0x83, 0x15} }, - { (uint64_t)300000000000000ull, {0x6a, 0x51, 0x47, 0x3c, 0x86, 0xed, 0xad, 0x53, 0x51, 0x4b, 0x3f, 0x95, 0x97, 0xed, 0x21, 0xae, 00, 0x81, 0x51, 0xa0, 0x9e, 0x43, 0xad, 0xdd, 0x45, 0xd1, 0x74, 0x63, 0xc5, 0x34, 0x3, 0x97} }, - { (uint64_t)400000000000000ull, {0x8, 0xbd, 0xd4, 0xc3, 0xe4, 0x53, 0x1b, 0x29, 0x7a, 0x70, 00, 0x1e, 0xb8, 0xa4, 0xf1, 0x98, 0xdc, 0x3b, 0xd4, 0xf1, 0xf5, 0x60, 0x9a, 0xda, 0x98, 0xf6, 0xd9, 0x5f, 0x9a, 0x1a, 0x30, 0x2e} }, - { (uint64_t)500000000000000ull, {0x97, 0x55, 0x70, 0xea, 0x12, 0xde, 0x5a, 0xf5, 0xc5, 0x36, 0xbd, 0xb6, 0x83, 0x54, 0xfb, 0xc8, 0x32, 0x21, 0x50, 0xfc, 0x56, 0x83, 0x7c, 0x4b, 0x78, 0xa9, 0x85, 0x76, 0x5d, 0x2a, 0x70, 0x99} }, - { (uint64_t)600000000000000ull, {0xa7, 0xa6, 0x39, 0x93, 0x41, 0xcb, 0x4d, 0x67, 0x76, 0xcd, 0x94, 0xd, 0x1d, 0x6a, 0xb0, 0xac, 0xa, 0xbf, 0x56, 0x93, 0x6a, 0x35, 0x31, 0xdf, 0xe9, 0x6c, 0x23, 0x69, 0x97, 0x8e, 0x49, 0xfa} }, - { (uint64_t)700000000000000ull, {0x55, 0x9, 0x3e, 0x5e, 0xeb, 0xca, 0x3, 0x88, 0x48, 0xdc, 0x99, 0x7e, 0x31, 0x95, 0xec, 0xc5, 0x8f, 0xb4, 0xa5, 0x71, 0xb9, 0x52, 0x56, 0xc0, 0xff, 0x49, 0xbe, 0xd0, 0xf1, 0x65, 0x22, 0xbd} }, - { (uint64_t)800000000000000ull, {0xbb, 0xc6, 0x18, 0x2, 0x24, 0xaf, 0xd3, 0x38, 0xa6, 0xf4, 0xa0, 0x6b, 0x11, 0x98, 0x40, 0x68, 0xeb, 0x36, 0x35, 0xe7, 0xe5, 0x47, 0x66, 0x69, 0x78, 0x83, 0xaf, 0xbd, 0xce, 0xad, 0x2f, 0x31} }, - { (uint64_t)900000000000000ull, {0x61, 0x3a, 0xa1, 0x2c, 0xc0, 0xa1, 0x9b, 0xc8, 0x43, 0x63, 0x50, 0xbb, 0xc0, 0xf6, 0x16, 0x32, 0x6e, 0x64, 0x85, 0x83, 0x33, 0x4a, 0x32, 0x65, 0x16, 0x29, 0xe9, 0x5, 0xc5, 0x20, 0x62, 0x69} }, - { (uint64_t)1000000000000000ull, {0x52, 0xdd, 0xf8, 0x81, 0x13, 0xa0, 0xfc, 0xf2, 0x12, 0x90, 0x95, 0xc6, 0x18, 0x91, 0xbe, 0x88, 0x5c, 0x9, 0x30, 0x8, 0xeb, 0xc4, 0x65, 0xc, 0xb0, 0xee, 0xa5, 0x60, 0xcd, 0x4d, 0x75, 0x1b} }, - { (uint64_t)2000000000000000ull, {0x75, 0xbd, 0xfc, 0x35, 0xa6, 0xdf, 0x76, 0xe5, 0x98, 0x8e, 0xd9, 0xe3, 0x10, 0xa5, 0x89, 0x16, 0xae, 0xf0, 0xc5, 0xf0, 0x5b, 0x89, 0x22, 0xea, 0xae, 0x2c, 0xf9, 0x8f, 0x58, 0x42, 0x3c, 0xe3} }, - { (uint64_t)3000000000000000ull, {0x88, 0x98, 0x93, 0xe8, 0x7d, 0x56, 0x9f, 0x14, 0xb2, 0x48, 0xd1, 0xed, 0x93, 0xe8, 0xce, 0x60, 0xbb, 0xe3, 0x73, 0x69, 0xb0, 0xd6, 0xc7, 0xa1, 0x86, 0x89, 0x33, 0xd3, 0xc3, 0xda, 0x9a, 0x72} }, - { (uint64_t)4000000000000000ull, {0x88, 0x3e, 0xf3, 0x4b, 0xa2, 0xc1, 0x91, 0xf4, 0x9d, 0x3c, 0xc6, 0xad, 0xa0, 0xaf, 0xf1, 0xcf, 0xb1, 0x77, 0xbd, 0x9e, 0xd4, 0xb3, 0xa5, 0x37, 0x84, 0xb7, 0xf1, 0x62, 0x9b, 0xed, 0x17, 0x41} }, - { (uint64_t)5000000000000000ull, {0xa2, 0x90, 0x7c, 0x39, 0x84, 0xb1, 0x4a, 0xb1, 0xf4, 0xda, 0x58, 0xc2, 0xc8, 0x2d, 0x6b, 0x24, 0xf1, 0x29, 0x49, 0x9, 0x75, 0xfc, 0x4a, 0x33, 0x3d, 0x25, 0xa1, 0xf9, 0x2b, 0xc4, 0x32, 0xb6} }, - { (uint64_t)6000000000000000ull, {0xa0, 0x7d, 0x9f, 0x18, 0x95, 0x1f, 0xf2, 0x32, 0xcf, 0x4e, 0xc0, 0xee, 0x2f, 0xbc, 0xc3, 0xe1, 0x1b, 0x2c, 0xaf, 0xc9, 0x57, 0x65, 0x82, 0x10, 0x38, 0x1e, 0x3e, 0xe4, 0xed, 0xec, 0x2e, 0x7a} }, - { (uint64_t)7000000000000000ull, {0x66, 0x80, 0x21, 0xd5, 0xde, 0x8c, 0xa4, 0xc1, 0x8f, 0x5a, 0x74, 0xf2, 0x78, 0x69, 0xc4, 0xd6, 0xd4, 0x93, 0xa3, 0x30, 0x39, 0x3c, 0xf0, 0x26, 0x41, 0xff, 0xa8, 0x56, 0x7b, 0xa5, 0x36, 0x20} }, - { (uint64_t)8000000000000000ull, {0xe0, 0x48, 0x7a, 0xc4, 0x5a, 0x82, 0x59, 0xe3, 0xe5, 0xf2, 0xd9, 0xb8, 0xf6, 0xb8, 0xfa, 0x26, 0x9a, 0x63, 0x49, 0x71, 0xa2, 0xf7, 0xc2, 0x1a, 0x54, 0x17, 0x76, 0x81, 0xeb, 0x2, 0xbd, 0x4a} }, - { (uint64_t)9000000000000000ull, {0x98, 0x92, 0x6a, 0x3a, 0xf0, 0x5b, 0xf4, 0xa9, 0x8d, 0xf9, 0xf6, 0x4a, 0xe7, 0xb9, 0xda, 0x45, 0xa7, 0x6, 0xc3, 0xf8, 0x39, 0x5e, 0x47, 0x1f, 0x96, 0xed, 0x3c, 0x6, 0x6, 0xbe, 0xbb, 0x71} }, - { (uint64_t)10000000000000000ull, {0x80, 0xad, 0xb7, 0xd, 0x46, 0xf6, 0x3a, 0x75, 0x64, 0xa3, 0xf6, 0x71, 0xd9, 0xba, 0x95, 0x71, 0xb7, 0xf7, 0x95, 0xa9, 0x63, 0x38, 0x2a, 0x4d, 0x9f, 0xaf, 0x2d, 0x54, 0xf6, 0xc6, 0x84, 0x29} }, - { (uint64_t)20000000000000000ull, {0xae, 0xbd, 0x97, 0x42, 0x1f, 0x3f, 0xca, 0xe8, 0x95, 0x18, 0x60, 0xe6, 0xd9, 0xd1, 0xf3, 0xec, 0x59, 0x73, 0xa2, 0xf7, 0x66, 0x88, 0x4b, 0xfe, 0x17, 0x50, 0x79, 0x51, 0xe4, 0x62, 0xc6, 0x63} }, - { (uint64_t)30000000000000000ull, {0x61, 0x2, 0x6c, 0x84, 0x2a, 0x6a, 0x22, 0x25, 0x74, 0x6b, 0x19, 0x6b, 0x56, 0x89, 0xe1, 0x18, 0xf5, 0x41, 0x34, 0x15, 0x98, 0x1d, 0x7, 0x73, 0x62, 0xb2, 0xe7, 0xb9, 0xac, 0xa5, 0x28, 0x16} }, - { (uint64_t)40000000000000000ull, {0x52, 0x54, 0xb5, 0x78, 0xe8, 0x57, 0x9a, 0x27, 0x3b, 0x89, 0x8e, 0x65, 0x9d, 0xd3, 0xe1, 0xa1, 0xcf, 0xba, 0x12, 0x47, 0x26, 0x64, 0xbd, 0x4e, 0x7f, 0x9a, 0x13, 0xb1, 0xfc, 0xee, 0x2, 0x93} }, - { (uint64_t)50000000000000000ull, {0x7b, 0x2a, 0xb, 00, 0xcf, 0xdc, 0xa9, 0x51, 0x46, 0xcf, 0x80, 0x95, 0xdd, 0x2b, 0x82, 0x90, 0x91, 0xb0, 0xf2, 0xd5, 0xbb, 0xc, 0x33, 0x82, 0x2d, 0x8b, 0x43, 0x42, 0x69, 0xcd, 0x2a, 0x42} }, - { (uint64_t)60000000000000000ull, {0x21, 0x57, 0x4f, 0xed, 0x15, 0x1a, 0x2f, 0x9f, 0x64, 0xa4, 0x5b, 0xe2, 0x8a, 0x3a, 0xf5, 0x88, 0xe9, 0xf2, 0xd1, 0x71, 0x35, 0xa3, 0x53, 0x7f, 0x7, 0xfd, 0x6a, 0xef, 0xa2, 0x9f, 0x2, 0xaf} }, - { (uint64_t)70000000000000000ull, {0x1a, 0xf2, 0x41, 0xe1, 0x38, 0x27, 0x98, 0x29, 0xac, 0x6a, 0xe6, 0x2f, 0xf, 0x33, 0x20, 0x4b, 0xb2, 0x8a, 0xfd, 0x6, 0x5c, 0x42, 0x59, 0x3b, 0xdc, 0x79, 0x14, 0x85, 0x97, 0x5b, 0x26, 0x95} }, - { (uint64_t)80000000000000000ull, {0xa8, 0xc8, 0xb8, 0x7b, 0x51, 0x2d, 0xef, 0x9b, 0x5e, 0x50, 0xe, 0xb4, 0x98, 0xaf, 0x86, 0xaa, 0xd2, 0x46, 0x4a, 0xea, 0xe7, 0x6d, 0xb1, 0xf6, 0x5d, 0x23, 0x26, 0xce, 0x90, 0x26, 0xec, 0x69} }, - { (uint64_t)90000000000000000ull, {0x3d, 0x78, 0x73, 0x63, 0x95, 0xf1, 0xd7, 0xde, 0x8e, 0x16, 0xc0, 0xb5, 0xa9, 0x9f, 0x4d, 0xc4, 0xeb, 0x8f, 0x22, 0xac, 0xc1, 0x5b, 0x21, 0x42, 0x44, 0x1d, 0xbd, 0x8d, 0x2c, 0x31, 0xb9, 0xce} }, - { (uint64_t)100000000000000000ull, {0x27, 0x27, 0xd4, 0x93, 0x2f, 0x98, 0x39, 0xe4, 0x3b, 0x6b, 0xf5, 0xfb, 0x29, 0xa3, 0xbe, 0x4c, 0x9, 0xb, 0x6e, 0xb9, 0x31, 00, 0xbb, 0x92, 0x58, 0x1a, 0xdb, 0x8d, 0xd2, 0xb6, 0x61, 0x54} }, - { (uint64_t)200000000000000000ull, {0xae, 0x96, 0x78, 0x2e, 0xf2, 0xc4, 0xdf, 0x7d, 0x2e, 0x4, 0xcc, 0xf9, 0xef, 0x76, 0x23, 0x7f, 0x17, 0xc, 0x97, 0x3, 0xb4, 0x92, 0xc0, 0x78, 0x52, 0x6e, 0xb1, 0xf6, 0x85, 0x3d, 0xb1, 0x33} }, - { (uint64_t)300000000000000000ull, {0x17, 0x43, 0xfe, 0xab, 0x12, 0xad, 0xe5, 0xfe, 0x12, 0x53, 0x22, 0x27, 0x2f, 0xd1, 0x40, 0x6b, 0x74, 0xe8, 0x19, 0x70, 0x32, 0x68, 0x46, 0x22, 0xee, 0x79, 0xab, 0xcd, 0x94, 0x93, 0x66, 0x4c} }, - { (uint64_t)400000000000000000ull, {0x7, 0x9b, 0xf2, 0xa9, 0x6e, 0x16, 0x6e, 0xf9, 0xe6, 0xb2, 0x23, 0x1d, 0xb9, 0x85, 0x8b, 0x99, 0x98, 0x7f, 0x49, 0x33, 0x87, 0xde, 0xeb, 0xd5, 0x17, 0x48, 0x54, 0x9a, 0xd, 0xf7, 0xdc, 0x44} }, - { (uint64_t)500000000000000000ull, {0xca, 0xba, 0x97, 0x98, 0x51, 0x6d, 0xad, 0x3, 0x38, 0xd0, 0x6e, 0x10, 0x6d, 0x76, 0xa2, 0x1, 0x93, 0x7a, 0xce, 0x4c, 0x91, 0x53, 0x9e, 0x61, 0x7d, 0x89, 0x28, 0x73, 0x6, 0xa3, 0x92, 0xb1} }, - { (uint64_t)600000000000000000ull, {0x6b, 0x8, 0x7f, 0x48, 0xb3, 0xd7, 0xaa, 0xc9, 0x57, 0xc4, 0x52, 0xe5, 0x1a, 0x18, 0xd7, 0x26, 0xb, 0xf8, 0xc8, 0x56, 0xc4, 0xc7, 0x1e, 0x48, 0xf6, 0x49, 0xae, 00, 0x4a, 0xf6, 0x8f, 0x13} }, - { (uint64_t)700000000000000000ull, {0x9e, 0xed, 0x8b, 0x23, 0x1f, 0x79, 0x4c, 0x46, 0x5c, 0xbe, 0x88, 0x40, 0xd0, 0xf1, 0x6f, 0x7b, 0x9f, 0x9c, 0x6e, 0xb4, 0x9c, 0x20, 0x7d, 0xe9, 0xd8, 0x55, 0x11, 0x83, 0xd0, 0xc7, 0x6e, 0x43} }, - { (uint64_t)800000000000000000ull, {0x58, 0x4a, 0x78, 0x93, 0x13, 0x7e, 0xbd, 0x2, 0x8b, 0xa7, 0x59, 0x82, 0xc3, 0x39, 0xb7, 0x66, 0xaa, 0xda, 0xad, 0xf9, 0x14, 0x50, 0xf9, 0x40, 0x7d, 0x2a, 0x97, 0xd7, 0xf6, 0xb1, 0x93, 0x5e} }, - { (uint64_t)900000000000000000ull, {0x7, 0xce, 0x54, 0xb1, 0x18, 0x26, 0xa1, 0x75, 0x23, 0x13, 0x55, 0x1a, 00, 0x20, 0xfd, 0x79, 0x8a, 00, 0x9e, 0x20, 0xcd, 0xb2, 0x40, 0x1d, 0x52, 0x51, 0xc1, 0x55, 0x8e, 0xea, 0xd2, 0x6c} }, - { (uint64_t)1000000000000000000ull, {0x39, 0x80, 0x7f, 0x3d, 0xce, 0xb0, 0xa6, 0xfe, 0x34, 0xa7, 0xa1, 0xed, 0xc6, 0x9b, 0x78, 0xff, 0xbe, 0xd5, 0xa7, 0x8c, 0x6c, 0x87, 0x5d, 0xda, 0x96, 0x69, 0xdb, 0xb2, 0x95, 0x70, 0xf0, 0xf4} }, - { (uint64_t)2000000000000000000ull, {0xda, 0x74, 00, 0x86, 0xf1, 0x5c, 0xe8, 0x21, 0xe9, 0xd, 0x50, 0xaf, 0xcf, 0x80, 0x9c, 0x7e, 0x18, 0x51, 0x90, 0x1b, 0xa3, 0x5f, 0x9f, 0x63, 0x78, 0xd6, 0x40, 0x7c, 0xb9, 0xc7, 0xa2, 0x75} }, - { (uint64_t)3000000000000000000ull, {0x7, 0xa1, 0x75, 0x63, 0xae, 0xf5, 0xcf, 0xd0, 0x36, 0xfa, 0x64, 0xd4, 0xb1, 0x97, 0xa9, 0x51, 0xc0, 0xd2, 0x87, 0x2b, 0xd, 0xb6, 0xf9, 0xbe, 0x47, 0xe6, 0x7c, 0xa6, 0xb5, 0x35, 0xe2, 0x6e} }, - { (uint64_t)4000000000000000000ull, {0xe3, 0x49, 0xf7, 0xeb, 0xe5, 0x11, 0x39, 0xfe, 0xd5, 0x69, 0x40, 0x37, 0xd1, 0x14, 0xb7, 0xbd, 0x45, 0xdd, 0xa, 0x6a, 0xf0, 0x4b, 0x62, 0xec, 0xa4, 0xd8, 0xcd, 0x55, 0x2a, 0x14, 0xe3, 0xfb} }, - { (uint64_t)5000000000000000000ull, {0x8d, 0x59, 0x7e, 0xa9, 0xf5, 0x79, 0x9a, 0x4d, 0x15, 0x3d, 0x82, 0xd6, 0xf7, 0xbe, 0xa0, 0x2e, 0x52, 0x40, 0xa2, 0xc8, 0x9b, 0x4, 0x1e, 0x6, 0x2f, 0x37, 0xbc, 0x7b, 0x82, 0xa0, 0xac, 0x55} }, - { (uint64_t)6000000000000000000ull, {0xa3, 0x43, 0xa7, 0xe1, 0x14, 0x4d, 0x33, 0x50, 0xf, 0x3e, 0xfd, 0x38, 0x15, 0x82, 0xdd, 0xc5, 0xd0, 0x18, 0x3e, 0x5d, 0xcf, 0x8a, 0xfa, 0x64, 0xbb, 0x67, 0x6c, 0x97, 0x3e, 0x3d, 0x1a, 0xb1} }, - { (uint64_t)7000000000000000000ull, {0x89, 0xe9, 0x3e, 0xe9, 0xf2, 0x4d, 0x72, 0x61, 0xe5, 0x44, 0xca, 0x8f, 0x9, 0xa7, 0x40, 0x4e, 0xe3, 0xa9, 0xe, 0xe2, 0x50, 0x7d, 0xda, 0xcf, 0x41, 0x2a, 0x58, 0xc, 0x9, 0x65, 0x1c, 0x53} }, - { (uint64_t)8000000000000000000ull, {0xc5, 0x94, 0x10, 0x81, 0x54, 0x69, 0xf4, 0x59, 0xd1, 0x5a, 0x6f, 0xe3, 0xf2, 0xa1, 0x1b, 0xa6, 0x31, 0x12, 0xfa, 0xaa, 0xc5, 0x3d, 0xbc, 0x52, 0x5d, 0x3c, 0xfa, 0xb1, 0xfa, 0x9c, 0x3d, 0xdb} }, - { (uint64_t)9000000000000000000ull, {0x9d, 0xe7, 0xcb, 0xb, 0x8d, 0x7b, 0xac, 0x47, 0xff, 0xd3, 0x93, 0x1b, 0xcd, 0x82, 0xcd, 0xd5, 0x35, 0xc, 0x29, 0x34, 0xb1, 0x6e, 0xb, 0x64, 0x32, 0xab, 0xf7, 0xcb, 0x4b, 0x5c, 0x37, 0x6d} }, - { (uint64_t)10000000000000000000ull, {0x65, 0x8d, 0x1, 0x37, 0x6d, 0x18, 0x63, 0xe7, 0x7b, 0x9, 0x6f, 0x98, 0xe6, 0xe5, 0x13, 0xc2, 0x4, 0x10, 0xf5, 0xc7, 0xfb, 0x18, 0xa6, 0xe5, 0x9a, 0x52, 0x66, 0x84, 0x5c, 0xd9, 0xb1, 0xe3} }, + { (uint64_t)0ull, {{0x58, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66}} }, + { (uint64_t)1ull, {{0x17, 0x38, 0xeb, 0x7a, 0x67, 0x7c, 0x61, 0x49, 0x22, 0x8a, 0x2b, 0xea, 0xa2, 0x1b, 0xea, 0x9e, 0x33, 0x70, 0x80, 0x2d, 0x72, 0xa3, 0xee, 0xc7, 0x90, 0x11, 0x95, 0x80, 0xe0, 0x2b, 0xd5, 0x22}} }, + { (uint64_t)2ull, {{0x76, 0x24, 0x84, 0x63, 0xa, 0x6, 0x17, 0x17, 0x8d, 0xe, 0x33, 0xf3, 0x2e, 0xe, 0x11, 0x3e, 0xa8, 0x46, 0x86, 0x9d, 0x46, 0x4b, 0xb, 0x6f, 0xf1, 0x3b, 0x29, 0x97, 0x4, 0x9c, 0xda, 0x7d}} }, + { (uint64_t)3ull, {{0xcf, 0xf7, 0x7b, 0x56, 0x62, 0x1c, 0x4f, 0xef, 0x74, 0xcf, 0x37, 0xc1, 0x78, 0xd4, 0xb5, 0x8a, 0xf4, 0xad, 0x8c, 0xd4, 0x35, 0xfc, 0xb9, 0x62, 0x76, 0xbc, 0x15, 0x9c, 0x7c, 0x6a, 0x28, 0x8c}} }, + { (uint64_t)4ull, {{0x9a, 0xb8, 0x6c, 0x31, 0xf4, 0x22, 0xd8, 0x21, 0xb5, 0x22, 0x57, 0x30, 0xd1, 0xbf, 0x73, 0xa, 0x9b, 0x91, 0xd2, 0xee, 0xe3, 0x14, 0xb8, 0x4e, 0xbd, 0x4b, 0x93, 0xa6, 0x81, 0x61, 0x82, 0x66}} }, + { (uint64_t)5ull, {{0x32, 0xee, 0x2f, 0x65, 0x9a, 0xf6, 0x38, 0x58, 0xc2, 0xf7, 0xdc, 0x11, 0x1b, 0x3b, 0xb8, 0xfe, 0xc0, 0x2c, 0xac, 0x42, 0x38, 0x3b, 0xb7, 0x36, 0xde, 0x1, 0x8, 0x6f, 0x38, 0xf0, 0x12, 0x3c}} }, + { (uint64_t)6ull, {{0x47, 0x26, 0x2b, 0x1e, 0xa6, 0x43, 0x1, 0x6e, 0x38, 0x24, 0x17, 0x53, 0xa4, 0xfb, 0x39, 0x92, 0x9e, 0x31, 0xea, 0x9b, 0xd3, 0x41, 0x1a, 0xb1, 0x7f, 0x16, 0x6e, 0x61, 0xf6, 0xc, 0xe5, 0xa7}} }, + { (uint64_t)7ull, {{0xc6, 0x32, 0x93, 0x68, 0x79, 0x9a, 0xd, 0xed, 0x4c, 0x20, 0x25, 0x6b, 0xff, 0xe6, 0x45, 0x47, 0xf1, 0x7b, 0xc4, 0x23, 0x95, 0x4, 0xbe, 0x82, 0x4d, 0xff, 0x8a, 0x2b, 0xe1, 0xaf, 0xe3, 0xcd}} }, + { (uint64_t)8ull, {{0xd5, 0xf1, 0x50, 0x74, 0x33, 0x46, 0x19, 0xf, 0x84, 0x2b, 0x6, 0xb8, 0xfa, 0xe1, 0x20, 0xeb, 0x85, 0x24, 0x7e, 0x9f, 0x6d, 0xec, 0x88, 0xff, 0xa2, 0x23, 0xbf, 0x69, 0x94, 0xe9, 0xc8, 0xc2}} }, + { (uint64_t)9ull, {{0x56, 00, 0x23, 0x32, 0x9e, 0xc0, 0xfa, 0xf3, 0x3b, 0x5e, 0x3a, 0x5c, 0xb4, 0xea, 0xef, 0xee, 0x38, 0xf8, 0x96, 0x1c, 0x88, 0xb6, 0x6a, 0x2f, 0x19, 0xd4, 0x59, 0x51, 0x96, 0x9c, 0x6d, 0x1f}} }, + { (uint64_t)10ull, {{0x3, 0x80, 0xdc, 0x24, 0xcc, 0x97, 0xcc, 0xe6, 0x58, 0xc3, 0xa9, 0x47, 0xc5, 0x10, 0x25, 0xde, 0x1a, 0x69, 0x80, 0x3b, 0xdb, 0x50, 0x5, 0xe3, 0xb7, 0xdd, 0xa9, 0xd, 0x68, 0x59, 0xb0, 0x1c}} }, + { (uint64_t)20ull, {{0x9, 0x3, 0xf6, 0x2e, 0x97, 0x76, 0x47, 0x58, 0xfe, 0xf8, 0x9e, 0x5b, 0xec, 0x29, 0xef, 0x4f, 0xc5, 0xe6, 0x45, 0x4b, 0x2d, 0x47, 0x44, 0x47, 0x36, 0x4, 0x4c, 0x25, 0x2e, 0xe2, 0x8e, 0xba}} }, + { (uint64_t)30ull, {{0xa2, 0x8b, 0x89, 0xe0, 0xb, 0xed, 0x62, 0x31, 0x68, 0x5b, 0xf9, 0x74, 0x36, 0xf2, 0xba, 0x51, 0xa2, 0x51, 0x55, 0x7f, 0x8d, 0x17, 0xa, 0x78, 0xe3, 0x12, 0xd6, 0x24, 0xbf, 0x60, 0xff, 0xfe}} }, + { (uint64_t)40ull, {{0xb5, 0xc6, 0x95, 0x55, 0x6a, 0x28, 0x47, 0xb2, 0xe, 0x1c, 0xbb, 0x26, 0xe6, 0xa9, 0xc6, 0x8a, 0x61, 0xc5, 0x50, 0xce, 0xb7, 0xc3, 0x4, 0xfe, 0x92, 0x28, 0x3d, 0x29, 0xa9, 0xb2, 0x43, 0xcb}} }, + { (uint64_t)50ull, {{0x12, 0x8e, 0xc6, 0xcd, 0xc0, 0x6b, 0x43, 0xc5, 0xd0, 0x9c, 0x3f, 0x65, 0x2a, 0xe3, 0x44, 0x7f, 0x9b, 0x3f, 0x2c, 0x30, 0x91, 0x2d, 0xf0, 0x80, 0x37, 00, 0x85, 0xbc, 0xc, 0x9, 0xef, 0x78}} }, + { (uint64_t)60ull, {{0x1f, 0x9f, 0x40, 0x3a, 0xae, 0xa7, 0x16, 0xfb, 0xe2, 0x98, 0xa8, 0x14, 0xf1, 0xee, 0xbc, 0x1b, 0x73, 0x16, 0x8c, 0x37, 0xfa, 0xe3, 0x16, 0xeb, 0x65, 0x5, 0x81, 0x6f, 0xc2, 0x20, 0xeb, 0xfb}} }, + { (uint64_t)70ull, {{0x10, 0xa2, 0x38, 0xc5, 0xe4, 0x8e, 0x4b, 0x93, 0x99, 0xdb, 0xa6, 0xcb, 0xd9, 0x8e, 0x63, 0x54, 0x41, 0x59, 0xe9, 0x8c, 0x93, 0x5a, 0xc0, 0x60, 0x3d, 0x72, 0xde, 0xf, 0xff, 0x31, 0x53, 0xbb}} }, + { (uint64_t)80ull, {{0x75, 0xab, 0x78, 0xc7, 0x28, 0x1f, 0x69, 0x28, 0xf0, 0x94, 0x86, 0x5, 0x7a, 0x63, 0x64, 0x18, 0x27, 0xc5, 0x74, 0x84, 0xe3, 0xe9, 0x9a, 0x39, 0xf3, 0x12, 0xa4, 0x3a, 0x51, 0x9b, 0xda, 0x8}} }, + { (uint64_t)90ull, {{0xe9, 0x56, 0x7b, 0xa7, 0x88, 0xb8, 0x5b, 0x82, 0xc8, 0x65, 0x7a, 0x15, 0xa5, 0x48, 0x99, 0x5c, 0xf6, 0xb0, 0xbd, 0xd1, 0xc6, 0x2a, 0xda, 0x77, 0x55, 0xf2, 0x32, 0x3a, 0xd8, 0xa4, 0x8, 0x51}} }, + { (uint64_t)100ull, {{0xb6, 0x17, 0x36, 0xd5, 0xf2, 0x8d, 0xef, 0x28, 0x61, 0x6a, 0xfc, 0x47, 0x93, 0xe9, 0x9b, 0x27, 0xcd, 0x3e, 0x89, 0xfb, 0x91, 0xc1, 0x13, 0xd4, 0x30, 0x73, 0x65, 0xfb, 0x75, 0xde, 0xdf, 0x88}} }, + { (uint64_t)200ull, {{0x71, 0x3, 0xeb, 0x72, 0x19, 0x28, 0xd7, 0x91, 0x99, 0x87, 0xf3, 0x50, 0xca, 0xa5, 0x7a, 0xe7, 0xb0, 0x81, 0x57, 0x15, 0x3b, 0x4c, 0x43, 0xd, 0x3e, 0xde, 0xc0, 0xc2, 0x3, 0x7, 0x97, 0x44}} }, + { (uint64_t)300ull, {{0x24, 0x40, 0x9e, 0x92, 0x2e, 0xce, 0xd1, 0xa0, 0x5e, 0x4e, 0xac, 0xa3, 0xdf, 0x91, 0x19, 0xc3, 0x8a, 0x92, 0x2e, 0xb, 0x66, 0xd0, 0x2d, 0x9d, 0xd2, 0xfb, 0x1d, 0xcc, 0x20, 0xb9, 0xaf, 0xc7}} }, + { (uint64_t)400ull, {{0xa7, 0x72, 0x9f, 0xa9, 0x32, 0x81, 0x82, 0x99, 0x34, 0x11, 0x5d, 0x47, 0x5a, 0x67, 0x86, 0xa, 0x14, 0x12, 0xc5, 0xe5, 0x95, 0x12, 0x20, 0xd9, 0x60, 0xc2, 0x41, 0xa0, 0x19, 0x1a, 0x9e, 0x65}} }, + { (uint64_t)500ull, {{0x2e, 0x53, 0xc, 0x6, 0x1c, 0x6d, 0x9e, 0x97, 0xab, 0xaf, 0x46, 0x8c, 0x32, 0xb0, 0xad, 0xa7, 0x49, 0x22, 0x57, 0x72, 0xfc, 0xd1, 0x17, 0x41, 0xcb, 0x5c, 0x3, 0x5c, 0xdd, 0x26, 0x14, 0xe}} }, + { (uint64_t)600ull, {{0xa5, 0xb, 0x91, 0x9, 0x9d, 0xf1, 0xb1, 0x69, 0x4f, 0x30, 0xb5, 0x8f, 0xe6, 0x77, 0x68, 0x50, 0xdb, 0xdb, 0xf4, 0x6c, 0xed, 0x99, 0x7f, 0x52, 0x62, 0xa8, 0x51, 0x59, 0x40, 0x74, 0xa5, 0x9d}} }, + { (uint64_t)700ull, {{0x51, 0x2c, 0xf, 0xae, 0xcc, 0xbe, 0xf2, 0xfe, 0xe5, 0x75, 0x4c, 0x6a, 0x45, 0xfd, 0xc0, 0x75, 0x2d, 0x4f, 0x15, 0x22, 0xe7, 0x7f, 0xf0, 0xc4, 0x8d, 0xcb, 0x19, 0x91, 0x8a, 0x68, 0x84, 0xe0}} }, + { (uint64_t)800ull, {{0xda, 0xa9, 0xf9, 0xa5, 0xb9, 0x71, 0x33, 0x33, 0xe9, 0x8c, 0x5, 0xac, 0xe7, 0x27, 0xcc, 0xe, 0x7d, 0xc3, 0xf1, 0x59, 0x49, 0xe1, 0xef, 0x4d, 0x94, 0xfa, 0x47, 0xd6, 0x8a, 0x34, 0xc6, 0x75}} }, + { (uint64_t)900ull, {{0xc7, 0x2b, 0x18, 0xc9, 0x17, 0xcd, 0x43, 0xee, 0x78, 0x40, 0x5e, 0x39, 0x83, 0x98, 0xb8, 0x3a, 0xc0, 0x97, 0x7b, 0x25, 0x19, 0x90, 0xd8, 0x13, 0xc, 0x38, 0xba, 0x53, 0xb6, 0x3d, 0xb4, 0xf7}} }, + { (uint64_t)1000ull, {{0x1, 0xdf, 0x60, 0x91, 0xeb, 0x6a, 0x48, 0xe9, 0xe4, 0x22, 0x25, 0xb, 0xe3, 0x83, 0x88, 0xc8, 0x61, 0xb6, 0x55, 0x55, 0xa7, 0x20, 0xad, 0x15, 0x35, 0x86, 0xfe, 0x2b, 0xd2, 0x2f, 0xa2, 0x3d}} }, + { (uint64_t)2000ull, {{0x24, 0xf5, 0xb1, 0x34, 0x78, 0x46, 0xaf, 0x22, 0xb5, 0x6f, 0x41, 0x25, 0xb3, 0xe7, 0x67, 0x8c, 0xf8, 0x4b, 0x4f, 0xd2, 0xf9, 0x2e, 0x1c, 0x40, 0xaa, 0x3a, 0x1b, 0xe0, 0xc7, 0x4d, 0x95, 0xe6}} }, + { (uint64_t)3000ull, {{0xa7, 0x1c, 0x9a, 0x8f, 0x40, 0xc1, 0x25, 0x9c, 0x36, 0x26, 0x27, 0x73, 0xe0, 0x8, 0x20, 0x18, 0x3e, 0x6b, 0x59, 0xe0, 0x71, 0xc9, 0x9b, 0x34, 0x9b, 0xef, 0x8f, 0x7e, 0xd2, 0xc6, 0xad, 0xb9}} }, + { (uint64_t)4000ull, {{0x98, 0xdc, 0x74, 0xaf, 0x19, 0x89, 0xd3, 0x4b, 0x64, 0x2e, 0xb3, 0x6, 0x2d, 0xbc, 0x9d, 0xca, 0xd8, 0x1, 0xc5, 0x65, 0x27, 0x6, 0x93, 0x99, 0xe7, 0xc4, 0x11, 0xad, 0x14, 0x28, 0x82, 0xf6}} }, + { (uint64_t)5000ull, {{0x61, 0x76, 0xac, 0x4a, 0xc0, 0x6, 0x5e, 0x49, 0xd6, 0xc4, 0x41, 0xcf, 0x40, 0x4f, 0xad, 0xda, 0xad, 0x44, 0x93, 0xe, 0xf0, 0x3c, 0x68, 0x9, 0xad, 0xd7, 0x77, 0xe4, 0x2f, 0xee, 0x7f, 0x10}} }, + { (uint64_t)6000ull, {{0x78, 0x79, 0x4, 0x65, 0xf6, 0x60, 0x5b, 0x5a, 0x84, 0x77, 0x36, 0x5a, 0xa6, 0xc2, 0xa4, 0xa5, 0x84, 0x91, 0xc, 0x23, 0x95, 0x2, 0x92, 0x97, 0x52, 0x49, 0xa1, 0xad, 0x7d, 0xf0, 0xf7, 0xe8}} }, + { (uint64_t)7000ull, {{0x20, 0xa5, 0x60, 0x6b, 0x60, 0x23, 0x95, 0xd6, 0x8e, 0x2f, 0xad, 0x8e, 0xc6, 0x7f, 0x92, 0xde, 0x89, 0xc6, 0x3e, 0x1e, 0x7f, 0xc1, 0xdd, 0x7f, 0x92, 0xff, 0xed, 0xb8, 0xf6, 0x55, 0xfb, 0xd}} }, + { (uint64_t)8000ull, {{0x9a, 0x78, 0x97, 0x43, 0x98, 0x65, 0x17, 0xd9, 0x5f, 0x4e, 0x80, 0x8b, 0xeb, 0xe6, 0x52, 0xd, 0xe6, 0xcf, 0x8c, 0x51, 0x35, 0xab, 0x36, 0x8, 0x7e, 0x87, 0xe2, 0x76, 0xac, 0x6a, 0x34, 0x1}} }, + { (uint64_t)9000ull, {{0x5f, 0xc7, 0xaa, 0x48, 0xbb, 0x19, 0x13, 0x58, 0xc7, 0xe3, 0x4d, 0x24, 0xcf, 0x9c, 0x31, 0x16, 0x74, 0x12, 0x7a, 0xb2, 0x45, 0xd0, 0x8f, 0x4e, 0x2c, 0xfd, 0xbf, 0x8f, 0x5, 0xc9, 0x5b, 0xf5}} }, + { (uint64_t)10000ull, {{0x61, 0x20, 0xe7, 0x76, 0xe9, 0x12, 0xab, 0x10, 0x5a, 0x49, 0xf9, 0xda, 0x2, 0xa6, 0x75, 0x17, 0xc0, 0xa9, 0xb, 0x2b, 0x3e, 0x2d, 0xa3, 0xd, 0xff, 0x34, 0x39, 0x93, 0xdb, 0xec, 0x95, 0x97}} }, + { (uint64_t)20000ull, {{0x77, 0xbf, 0xb5, 0x37, 0xac, 0xa, 0xbc, 0x41, 0xaa, 0x21, 0xd0, 0xec, 0xd9, 0x18, 0x13, 0x34, 0xd8, 0x6b, 0xa7, 0x86, 0x5a, 0x94, 0x47, 0xf5, 0xc1, 0x58, 0x9a, 0x81, 0xd7, 0xef, 0xb3, 0xbb}} }, + { (uint64_t)30000ull, {{0x35, 0xf4, 0x5, 0xa9, 0x5f, 0x75, 0x19, 0x2a, 0xe9, 0xc0, 0xd4, 0xf5, 0x88, 0x84, 0x47, 0x14, 0xf6, 0x85, 0x1b, 0x97, 0xce, 0xbd, 0x9f, 0x7c, 0x2, 0xc5, 0xdd, 0xd7, 0xbf, 0x58, 0xff, 0x31}} }, + { (uint64_t)40000ull, {{0x77, 0x55, 0xbb, 0x3f, 0x38, 0x7c, 0x21, 0xb8, 0xa0, 0xf4, 0x48, 0x1f, 0xbf, 0xa8, 0x8a, 0xbe, 0xee, 0xce, 0xc7, 0x56, 0x53, 0xfc, 0xa1, 0x89, 0x58, 0x39, 0xc1, 0xba, 0x6, 0x47, 0x9f, 0x96}} }, + { (uint64_t)50000ull, {{0x8b, 0x7e, 0x84, 0xa3, 0x37, 0xb7, 0xb9, 0xcd, 0x5d, 0xb3, 0x63, 0x33, 0x8, 0xad, 0x51, 0x86, 0xa3, 0x59, 0xd, 0xff, 0xb8, 0x23, 0x1e, 0x2f, 0x31, 0xfd, 0x20, 0x42, 0x54, 0x9f, 0xfb, 0xe2}} }, + { (uint64_t)60000ull, {{0xef, 0xfd, 0xa6, 0x25, 0x15, 0xea, 0xb1, 0xbc, 0x1e, 0xbd, 0x74, 0x92, 0x94, 0x9b, 0x1, 0x22, 0xc3, 0x9f, 0x71, 0xa, 0x65, 0x16, 0xec, 0x66, 0x8c, 0x37, 0x61, 0xe6, 0xcc, 0x36, 0x1f, 0x25}} }, + { (uint64_t)70000ull, {{0x16, 0xba, 0x89, 00, 0xf3, 0x6f, 0xf, 0x6c, 0x46, 0x1c, 0xb, 0xe7, 0x64, 0xae, 0xee, 0x48, 0x86, 0x6, 0xb0, 0x53, 0xed, 0xdc, 0x10, 0xb5, 0x9a, 0x3e, 0xde, 0xcd, 0x23, 0xd4, 0x4f, 0xc0}} }, + { (uint64_t)80000ull, {{0x4d, 0xd4, 0x70, 0x3b, 0x7b, 0x7f, 0xcf, 0xe7, 0x2a, 0x2e, 0x4f, 0x31, 0xa4, 0x34, 0x17, 0xf9, 0xc0, 0xda, 0x64, 0x2f, 0xd0, 0xa9, 0x29, 0xb8, 0xf5, 0xed, 0xd8, 0x3, 0x7f, 0x93, 0xc5, 0xb3}} }, + { (uint64_t)90000ull, {{0x8e, 0xfc, 0x3, 0x20, 0x40, 0xbd, 0x90, 0x41, 0xda, 0x3d, 0xb0, 0x9b, 0xa1, 0x3d, 0xa2, 0xa5, 0xd1, 0xb8, 0x12, 0x3, 0xa, 0x5a, 0x36, 0x7c, 0x58, 0x94, 0xbd, 0x54, 0x11, 0x9, 0xe7, 0x30}} }, + { (uint64_t)100000ull, {{0xbd, 0x2e, 0xb1, 0x97, 0x83, 0x57, 0x1c, 0xf2, 0x22, 0x2c, 0x81, 0xb, 0x69, 0xf, 0xc7, 0x66, 0x64, 0x57, 0xae, 0x20, 0x92, 0x5b, 0x90, 0x5, 0xce, 0xe6, 0x1d, 0xf2, 0x66, 0x6f, 0xdc, 0xb7}} }, + { (uint64_t)200000ull, {{0x83, 0xd4, 0xcd, 0xdd, 0xc1, 0x44, 0x87, 0x32, 0xf2, 0x97, 0x7c, 0x41, 0xaa, 0xa7, 0x1f, 0xe6, 0xde, 0x9c, 0x17, 0x6d, 0xa8, 0x99, 0xee, 0xbf, 0xfc, 0x1b, 0xb, 0xa9, 0xea, 0x92, 0x97, 0x90}} }, + { (uint64_t)300000ull, {{0xcc, 0xc0, 0x6b, 0x44, 0xc3, 0x1, 0x38, 0x6, 0x30, 0x45, 0xed, 0x1, 0xd2, 0x45, 0xd8, 0x14, 0x3, 0xb6, 0x36, 0x52, 0xeb, 0xc4, 0xf9, 0x96, 0x7f, 0xd, 0x7f, 0x38, 0x69, 0x7f, 0x46, 0x16}} }, + { (uint64_t)400000ull, {{0x1b, 0xbf, 0xe7, 0xe, 0xca, 0xf1, 0xdd, 0xd7, 0xf1, 0x2, 0x36, 0xf6, 0x8a, 0x41, 00, 0xb, 0x5d, 0xab, 0x2d, 0x47, 0x5c, 0xb9, 0x2f, 0x62, 0xc2, 0xd6, 0x84, 0xcf, 0x57, 0x69, 0xfb, 0x84}} }, + { (uint64_t)500000ull, {{0xf1, 0xb1, 0xcd, 0xaa, 0x78, 0x14, 0x95, 0x36, 0xf, 0x53, 0x31, 0x81, 0xaa, 0x58, 0xc8, 0xbd, 0xae, 0x6a, 0x77, 0x98, 0xd0, 0x2d, 0xab, 0x6d, 0x56, 0x26, 0x81, 0x27, 0x67, 0x9, 0xe7, 0x1}} }, + { (uint64_t)600000ull, {{0xd5, 0x26, 0x7d, 0x60, 0xd4, 0xfe, 0x9b, 0xc5, 0xfe, 0xfa, 0x7d, 0x3f, 0xe0, 0x7c, 0xd1, 0xfa, 0xd4, 0x55, 0x73, 0xd5, 0xae, 0x19, 0x10, 0xda, 0x7, 0x3e, 0x6d, 0x2d, 0xf9, 0xe2, 0x4, 0x39}} }, + { (uint64_t)700000ull, {{0xb, 0x58, 0x11, 0x25, 0xc2, 0xc4, 0x83, 0xc9, 0xa3, 0xd8, 0xbc, 0x8, 0x32, 0x2f, 0x26, 0xaa, 0x1f, 0xc5, 0xe, 0x41, 0x53, 0x2c, 0x1b, 0x9d, 0xf6, 0x26, 0xb0, 0x9, 0xd7, 0x88, 0x67, 0xcf}} }, + { (uint64_t)800000ull, {{0xf5, 0xb3, 0xd1, 0x8f, 0x66, 0xd0, 0xf9, 0x17, 0x5c, 0x30, 0x83, 0xb5, 0xf8, 0x7, 0x8e, 0xaf, 0xa8, 0x9e, 0xf8, 0x1d, 0xe7, 0x15, 0x8, 0xbc, 0x25, 0x1f, 0x5c, 0x5f, 0xe7, 0x25, 0x2e, 0x6}} }, + { (uint64_t)900000ull, {{0x1, 0xde, 0x40, 0x2c, 0x4b, 00, 0x43, 0x4, 0x2e, 0xae, 0x9e, 0xde, 0xa1, 0x49, 0x2b, 0x9d, 0x82, 0xb7, 0xbc, 0x36, 0x68, 0xe9, 0xb5, 0x84, 0xb0, 0x31, 0x3d, 0x44, 0x50, 0x53, 0x40, 0x74}} }, + { (uint64_t)1000000ull, {{0x53, 0x4b, 0x85, 0xc7, 0x89, 0x3f, 0x66, 0xf0, 0x26, 0xb6, 0x5e, 0xd7, 0xe7, 0xa4, 0xb8, 0xc9, 0xf4, 0xb, 0xe3, 0x1b, 0xcd, 0xa, 0x3d, 0xcd, 0x27, 0xc4, 0x71, 0x2, 0x56, 0x51, 0x65, 0x3}} }, + { (uint64_t)2000000ull, {{0xcd, 0xb5, 0xda, 0xfa, 0x53, 0x10, 0xf5, 0x26, 0x2f, 0xfc, 0x9, 0x26, 0xd0, 0xdf, 0x6e, 0xeb, 0xee, 0x2d, 0x52, 0xa9, 0x8d, 0xc6, 0x9f, 0xd, 0xc5, 0xe4, 0xeb, 0xf0, 0xc1, 0xa8, 0x77, 0x2e}} }, + { (uint64_t)3000000ull, {{0x9e, 0x75, 0x63, 0xf0, 0x33, 0x59, 0xea, 0x31, 0xe2, 0x91, 0xe7, 0xf0, 0xb8, 0x74, 0x17, 0xbc, 0xf5, 0xb2, 0x34, 0xee, 0x8b, 0x7e, 0x5b, 0x4, 0x41, 0x73, 0xbf, 00, 0x46, 0x86, 0x7c, 0x57}} }, + { (uint64_t)4000000ull, {{0xfd, 0x2a, 0xeb, 0xd, 0x5e, 0xe5, 0x3b, 0x77, 0xf2, 0xb1, 0xe3, 0xac, 0x75, 0x2d, 0x19, 0x38, 0x9f, 0xc5, 0xba, 0xa0, 0xf8, 0xd7, 0x64, 0x48, 0xa5, 0x9f, 0x99, 0x85, 0xa4, 0x8d, 0xa, 0x25}} }, + { (uint64_t)5000000ull, {{0xc0, 0xbe, 0x4f, 0xb8, 0x77, 0xb9, 0xce, 0x50, 0x87, 0x71, 0x32, 0x3b, 0xcf, 0x1f, 0xb9, 0x48, 0x47, 0x10, 0xee, 0x23, 0x2, 00, 0x6, 0xc3, 0xe8, 0xca, 0xac, 0x6e, 0x4f, 0x2, 0xfa, 0xbf}} }, + { (uint64_t)6000000ull, {{0xfc, 0x44, 0x5c, 0xa3, 0x84, 0xf3, 0x3e, 0x55, 0x8d, 0xc1, 0x56, 0x44, 0x9d, 0x3f, 0xba, 0x6a, 0xfd, 0x54, 0xc3, 0x42, 0xe6, 0x35, 0x11, 0xf, 0xe7, 0x9c, 0x16, 0xc7, 0x17, 0xf7, 0xd4, 0xf7}} }, + { (uint64_t)7000000ull, {{0xd8, 0x9, 0x2b, 0x8d, 0x45, 0xdb, 0x54, 0xa5, 0x6d, 0x64, 0xe8, 0x9, 0x4a, 0x6, 0x22, 0xe2, 0x6e, 0x8a, 0x2e, 0xec, 0xb9, 0x3, 0xb2, 0xe1, 0xf7, 0x5a, 0x83, 0x7b, 0x3a, 0xd8, 0x55, 0x4a}} }, + { (uint64_t)8000000ull, {{0x10, 0x4, 0x5c, 0x91, 0xdb, 0xad, 0x8a, 0x6a, 0x81, 0x62, 0x4a, 0xe0, 0xcf, 0x20, 0x5d, 0xb9, 0x97, 0x3e, 0xe8, 0x42, 0x3e, 0x97, 0xaf, 0x58, 0xa6, 0x1c, 0xfa, 0x7a, 0x78, 0x66, 0xf4, 0x1}} }, + { (uint64_t)9000000ull, {{0x11, 0x5c, 0x20, 0x9e, 0xe1, 0xde, 0xf3, 0x10, 0xce, 0xc9, 0xa6, 0xd1, 0x6c, 0xe6, 0x27, 0xec, 0xbd, 0xb9, 0xff, 0x2c, 0x23, 0x9, 0x3c, 0x24, 0xc8, 0x6c, 0x1b, 0xf2, 0x50, 0xd4, 0xb5, 0x85}} }, + { (uint64_t)10000000ull, {{0x2f, 0x99, 0xd9, 0x74, 0x44, 0x18, 0x66, 0x9, 0x49, 0xba, 0x43, 0x35, 0x61, 0xc6, 0x5, 0xb6, 0xf7, 0xbe, 0x8f, 0x82, 0xa, 0x93, 0xcb, 0x2a, 0xed, 0xa9, 0x7c, 0x87, 0x32, 0x92, 0x56, 0x49}} }, + { (uint64_t)20000000ull, {{0xc6, 0x77, 0x1f, 0xab, 0x14, 0xb, 0x75, 0xf4, 0xef, 0xd0, 0x97, 0xfc, 0xe1, 0x82, 0x6b, 0x80, 0xba, 0xe3, 0x16, 0xbc, 0xec, 0x28, 0x86, 0x9b, 0x3a, 0x1b, 0xf1, 0xbc, 0x6e, 0x4d, 0x20, 0x43}} }, + { (uint64_t)30000000ull, {{0xa1, 0xe9, 0xed, 0x3e, 0xf6, 0x5a, 0x9d, 0x52, 0x6c, 0xc2, 0x62, 0x5, 0x88, 0x12, 0x1, 0xd8, 0xa8, 0xf2, 0xc4, 0x40, 0x9f, 0xa3, 0x64, 0x10, 0x72, 0x96, 0xb9, 0xf9, 0x6a, 0x61, 0xb3, 0x58}} }, + { (uint64_t)40000000ull, {{0xb5, 0x31, 0x2d, 0xc7, 0x72, 0x94, 0xab, 0x9b, 0xc8, 0xbf, 0xd1, 0x39, 0x1e, 0x9a, 0xca, 0x92, 0x45, 0xe2, 0x28, 0xf7, 0x4b, 0x49, 0x74, 0xfc, 0x29, 0xad, 0x1c, 0x31, 0xcb, 0xe3, 0xe6, 0xa3}} }, + { (uint64_t)50000000ull, {{0xb8, 0xab, 0xc9, 0xff, 0xf6, 0x84, 0x1d, 0x2e, 0xa0, 0x13, 0x5a, 0x21, 0x72, 0xd3, 0xa7, 0xb, 0xfc, 0x2b, 0x70, 0x22, 0x8, 0xcd, 0x4a, 0x43, 0xc6, 0x30, 0xbe, 0xb1, 0xb8, 0xa0, 0x32, 0x8b}} }, + { (uint64_t)60000000ull, {{0x63, 0x90, 0xe1, 0xdb, 0x81, 0xb0, 0xea, 0x5c, 0xe2, 0x73, 0x94, 0x14, 0xe5, 0x2b, 0x7, 0x98, 0xd8, 0x2e, 0xb8, 0xe9, 0xae, 0xc5, 0x6d, 0xfe, 0x7e, 0x2c, 0x64, 0x11, 0xab, 0x79, 0x41, 0x87}} }, + { (uint64_t)70000000ull, {{0x7e, 0x51, 0xaf, 0xee, 0x5b, 0xc9, 0x71, 0x52, 0x9d, 0x64, 0x4d, 0xcd, 0x7f, 0x2a, 0x2a, 0xb0, 0x26, 0x69, 0xce, 0x2c, 0xb5, 0x7, 0xa6, 0x2d, 0xfc, 0x93, 0x17, 0x6c, 0xb6, 0xdf, 0x41, 0x38}} }, + { (uint64_t)80000000ull, {{0xf3, 0x7b, 0x94, 0x6b, 0x8b, 0x24, 0x88, 0xeb, 0xee, 0x1c, 0x6, 0xc1, 0x27, 0xfb, 0xe5, 0xfa, 0x5e, 0xfd, 0x62, 0x36, 0x9d, 0xd5, 0xaa, 0xda, 0xed, 0xd8, 0x88, 0x50, 0x1d, 0x3b, 0x7e, 0x3b}} }, + { (uint64_t)90000000ull, {{0x46, 0xcb, 0x76, 0x57, 0xf6, 0x1c, 0x83, 0x7c, 0xec, 0x80, 0x74, 0xbb, 0xb0, 0xf5, 0x2e, 0x7f, 0xc5, 0x9a, 0xd, 0x94, 0xe0, 0x17, 00, 0x9a, 0xbe, 0x25, 0x65, 0x2e, 0x4a, 0xd2, 0xe5, 0x3d}} }, + { (uint64_t)100000000ull, {{0x66, 0x7b, 0x8e, 0x6f, 0x6a, 0x4b, 0x91, 0x89, 0x76, 0xd9, 0x73, 0x5a, 0x43, 0x36, 0x7d, 0xc7, 0x59, 0x2c, 0x87, 0xd0, 0xa1, 0xf8, 0x15, 0xc6, 0xe8, 0x7d, 0xf1, 0x1a, 0x13, 0x50, 0x9f, 0xb2}} }, + { (uint64_t)200000000ull, {{0x3b, 0xcb, 0x51, 0x48, 0x1, 0x64, 0x1b, 0x62, 0x55, 0x93, 0x8c, 0xc5, 0x3, 0x76, 0x2d, 0x35, 0xce, 0x6, 0xd7, 0x5f, 0xe9, 0x50, 0x95, 0x9a, 0x1a, 0xab, 0x21, 0x4b, 0x50, 0x9b, 0x10, 0xb}} }, + { (uint64_t)300000000ull, {{0xa5, 0x92, 0x6f, 0x3, 0x1e, 0x6b, 0x15, 0xeb, 0x86, 0x23, 0x51, 0x8, 0xab, 0xb1, 0xaf, 0x90, 0xc5, 0xb1, 0x62, 0xc3, 0x99, 0x8c, 0x8b, 0xbb, 0x3f, 0xfb, 0xb0, 0x72, 0x9d, 0xa9, 0x45, 0x7b}} }, + { (uint64_t)400000000ull, {{0xfe, 0x35, 0xb6, 0x99, 0x44, 0x41, 0xe, 0xaf, 0x81, 0x5b, 0xdc, 0xd0, 0xa4, 0xd7, 0x1e, 0xf9, 0xfc, 0x66, 0x86, 0x48, 0xad, 0x43, 0x74, 0x3b, 0x3, 0x5a, 0xed, 0x2c, 0x17, 0xc1, 0x38, 0x7a}} }, + { (uint64_t)500000000ull, {{0x22, 0x22, 0xd6, 0x70, 0xb8, 0x7d, 0x9b, 0x47, 0xb8, 0xb9, 0x5c, 0x8c, 0x39, 0x7b, 0xc5, 0x2e, 0x2b, 0x46, 0xa6, 0x48, 0xb0, 0x2, 0xa0, 0x48, 0x5a, 0x37, 0x5c, 0xd8, 0x1f, 0x4a, 0x54, 0x5f}} }, + { (uint64_t)600000000ull, {{0xd3, 0x23, 0x8a, 0x4a, 0x8b, 0x71, 0xab, 0x46, 0xd1, 0x53, 0x4, 0xac, 0xfa, 0x2f, 0x40, 0xbf, 0x5e, 0xa6, 0x3b, 0x3d, 0x86, 0x4a, 0x79, 0xfa, 0x84, 0x25, 0xd2, 0x65, 0x5a, 0xe7, 0x7, 0x6f}} }, + { (uint64_t)700000000ull, {{0xa8, 0xff, 0x28, 0x3f, 0xcf, 0xf0, 0x53, 0xd3, 0x44, 0xc8, 0xf7, 0x56, 0x4f, 0x40, 0x24, 0xb6, 0x6b, 0xfa, 0x45, 0x9f, 0x47, 0x6f, 0xd, 0x73, 0xc, 0x91, 0x39, 0x90, 0x8b, 0x2d, 0x64, 0x7e}} }, + { (uint64_t)800000000ull, {{0xf2, 0xda, 0xf8, 0x88, 0xc4, 0x46, 0x57, 0x1, 0xc0, 0xe6, 0x1e, 0x12, 0xc3, 0xfb, 0xd4, 0xea, 0x79, 0xc7, 0xec, 0xb4, 0xf0, 0xc4, 0xb1, 0x54, 0xc5, 0x1a, 0x24, 0xd1, 0xe9, 0x21, 0x28, 0xba}} }, + { (uint64_t)900000000ull, {{0x11, 0x6a, 0xe5, 0xd2, 0x9c, 0xec, 0x72, 0xaa, 0xc5, 0x57, 0xcb, 0x14, 0xe2, 0xcd, 0xd5, 0x53, 0xe5, 0x88, 0xff, 0x8b, 0x81, 0x78, 0x26, 0x1, 0x99, 0xc4, 0xc, 0xae, 0xa2, 0x12, 0xcb, 0x63}} }, + { (uint64_t)1000000000ull, {{0x8c, 0xe6, 0x48, 0x33, 0xce, 0xc9, 00, 0xcb, 0x6d, 0x5a, 0xc4, 0x6f, 0xc0, 0x23, 0x7d, 0x8f, 0x24, 0x39, 0xc3, 0xdf, 0xa2, 0x38, 0xba, 0xf9, 0xcc, 0x94, 0x16, 0x6a, 0xd2, 0xe8, 0x98, 0x87}} }, + { (uint64_t)2000000000ull, {{0x37, 0x8d, 0x3c, 0x5d, 0xbb, 0xa4, 0x82, 0x3d, 0x33, 0x12, 0xbb, 0x61, 0xfc, 0x6, 0x75, 0xa1, 0xbb, 0x39, 0x89, 0xf3, 0x97, 0x1, 0xeb, 0xd, 0x5c, 0xe4, 0xde, 0x5b, 0xd, 0x90, 0x74, 0x72}} }, + { (uint64_t)3000000000ull, {{0x7f, 0xa2, 0xd0, 0xa5, 0x99, 0xe7, 0x97, 0x2e, 0x74, 0xcb, 0x75, 0xf9, 0x8a, 0xf4, 0x84, 0xfc, 0x85, 0x19, 0xcb, 0x7e, 0x25, 0xb9, 0x84, 0xa7, 0x6d, 0x8b, 0xc2, 0xba, 0x8d, 0xaf, 0xde, 0xd8}} }, + { (uint64_t)4000000000ull, {{0xda, 0x3a, 0xcb, 00, 0xab, 0x2d, 0x8d, 0xcc, 0xac, 0xec, 0x8f, 0x77, 0x59, 0x21, 0xc4, 0xe, 0x26, 0xb1, 0xff, 0xbe, 0xca, 0x9e, 0xb7, 0xe6, 0x57, 0x25, 0x6f, 0x59, 0x68, 0xf2, 0x34, 0x1c}} }, + { (uint64_t)5000000000ull, {{0x34, 0x6, 0xd7, 0x9a, 0x50, 0xd8, 0x14, 0xa9, 0xcc, 0xed, 0x3b, 0x24, 0x4, 0xed, 0x3e, 0x1b, 0x8d, 0xa6, 0x21, 0x98, 0x8c, 0x43, 0xb1, 0x93, 0x69, 0x42, 0xf4, 0x94, 0xa, 0xc5, 0xbf, 0x6a}} }, + { (uint64_t)6000000000ull, {{0xf8, 0x3e, 0xe8, 0xc1, 0x62, 0xfc, 0x52, 0xa0, 0x8, 0x9f, 0x46, 0xe8, 0x29, 0xc2, 0xea, 0xf6, 0xa1, 0x9f, 0xd5, 0x96, 0xcd, 0x12, 0xb3, 0xe8, 0x19, 0xd5, 0x67, 0x69, 0x44, 0xf, 0x7b, 0x4e}} }, + { (uint64_t)7000000000ull, {{0x8c, 0x72, 0x7d, 0x24, 0x57, 0xf3, 0x4b, 0x2f, 0xdb, 0x6a, 0xdf, 0x69, 0x1a, 0xb3, 0x5f, 0xaa, 0xe4, 0xff, 0x23, 0x4c, 0x28, 0xb4, 0x4e, 0x9f, 0xd3, 0x71, 0x8e, 0xef, 0xec, 0x41, 0x75, 0x80}} }, + { (uint64_t)8000000000ull, {{0x4a, 0x2e, 0x2f, 0x76, 0xe3, 0x5d, 0xcb, 0xa8, 0x97, 0xa3, 0xae, 0x72, 0xc4, 0x27, 0xd, 0x9c, 0x13, 0x17, 0x14, 0xed, 0x19, 0x1b, 0x55, 0x5c, 0x5e, 0x1, 0xe4, 0x75, 0x7c, 0xba, 0xe7, 0x2c}} }, + { (uint64_t)9000000000ull, {{0x3f, 0x9f, 0xc, 0x4, 0xc0, 0xb9, 0xec, 0x9b, 0x4d, 0x11, 0x7c, 0x5f, 0xc9, 0xf1, 0x8a, 0x20, 0xf2, 0xb3, 0xfa, 0xcc, 0xa4, 0xc8, 0xae, 0x41, 0xaf, 0x7c, 0x8, 0xe9, 0xe0, 0xef, 0xb9, 0x81}} }, + { (uint64_t)10000000000ull, {{0x97, 0xc9, 0x2a, 0x29, 0x1, 0x5e, 0xcb, 0x49, 0xf8, 0x9, 0x5, 0x45, 0xe0, 0x1f, 0xf9, 0x78, 0x6c, 0xae, 0x40, 0x57, 0x73, 0x47, 0x61, 0x18, 0x24, 0xf4, 0xb6, 0x59, 0x9f, 0xf5, 0xd3, 0x64}} }, + { (uint64_t)20000000000ull, {{0x9, 0xba, 0xed, 0x9a, 0x3c, 0x44, 0xb2, 0x22, 0x85, 0xa0, 0xae, 0xa4, 0x14, 0x8c, 0xa7, 0xde, 0x9b, 0xea, 0x96, 0x3c, 0xf6, 0x96, 0x23, 0xb6, 0x83, 0x44, 0x5c, 0xa, 0x10, 0xa5, 0x86, 0x77}} }, + { (uint64_t)30000000000ull, {{0x45, 0xac, 0xaf, 0x1d, 0xe2, 0x89, 0x6d, 0xe8, 0x72, 0x84, 0xff, 0xed, 0x57, 0x8b, 0x77, 0x14, 0xf5, 0x18, 0xa6, 0x18, 0xe2, 0xae, 0x6f, 0x90, 0xae, 0x4f, 0x70, 0x13, 0xa2, 0x8e, 0x99, 0xe0}} }, + { (uint64_t)40000000000ull, {{0x8, 0xb8, 0x47, 0x36, 0x42, 0x24, 0xe2, 0x9c, 0xe3, 0x36, 0x63, 0x93, 0xc2, 0xe1, 0x1e, 0xfc, 0x75, 0x55, 0xde, 0xe1, 0xa0, 0x5f, 0x91, 0xa7, 0x2e, 0x61, 0x11, 0x76, 0x84, 0xdd, 0xbe, 0x29}} }, + { (uint64_t)50000000000ull, {{0x6c, 0x8e, 0xe, 0x4a, 0x63, 0x4f, 0x85, 0x9a, 0x31, 0xab, 0x2f, 0x7a, 0x78, 0xc0, 0xc4, 0xa5, 0x93, 0x8c, 0xb7, 0x7f, 0x3, 0x35, 0x50, 0xa4, 0x7d, 0x7e, 0x31, 0x81, 0xb6, 0xb2, 0x6e, 0xc0}} }, + { (uint64_t)60000000000ull, {{0x66, 0xc2, 0xa0, 0x9, 0x65, 0xf9, 0xbf, 0xcb, 0xb1, 0x1e, 0xa0, 0x3c, 0xf1, 0xd6, 0x31, 0xb0, 0xe, 0x8a, 0x1e, 0xf7, 0xa6, 0xb, 0x1b, 0xe4, 0xa5, 0xac, 0x9, 0x23, 0xb, 0xf8, 0x17, 0x3f}} }, + { (uint64_t)70000000000ull, {{0x63, 0x51, 0xd7, 0x74, 0xc0, 0x2c, 0x5a, 0x9d, 0xee, 0xcf, 0xdb, 0xab, 0x70, 0x96, 0x68, 0x59, 0x8c, 0x47, 0xe4, 0xb1, 0x78, 0x2c, 0xe5, 0xae, 0x31, 0x6a, 0xf7, 0x40, 0xa6, 0x6f, 0x7e, 0x30}} }, + { (uint64_t)80000000000ull, {{0x5a, 0xcc, 0xfd, 0x16, 0x22, 0x79, 0xa5, 0x1c, 0x8b, 0x3b, 0xd5, 0xd3, 0x67, 0x9e, 0x91, 0x89, 0x67, 0xa2, 0x64, 0xea, 0x6, 0x3d, 0x37, 0xdf, 0xf5, 0xe3, 0x45, 0x7e, 0xc3, 0x7, 0xd4, 0x57}} }, + { (uint64_t)90000000000ull, {{0xb7, 0x47, 0xfc, 0x1, 0xc6, 0xf0, 0xc7, 0x49, 0x67, 0x3a, 0x29, 0x10, 0x25, 0xc, 0x2e, 0x23, 0xcb, 0x38, 0x27, 0x4d, 0x63, 0xb4, 0x2f, 0x52, 0x1b, 0x84, 0x63, 0x56, 0xe4, 0x13, 0x61, 0x8f}} }, + { (uint64_t)100000000000ull, {{0x9, 0x42, 0x84, 0x3d, 0x6f, 0x69, 0xe1, 0xcf, 0x3d, 0x99, 0xc9, 0x9f, 0xc, 0x97, 0xc0, 0xe6, 0xe5, 0x78, 0x93, 0x5a, 0xf6, 0xa8, 0xbd, 0xb8, 0xf8, 0x1d, 0x5b, 0x90, 0xbd, 0xe7, 0xcc, 0x10}} }, + { (uint64_t)200000000000ull, {{0x56, 0x4c, 0x64, 0xea, 0x50, 0xe4, 0xbd, 0x20, 0xdb, 0x58, 0x5d, 0xb5, 0x87, 0xb1, 0xf7, 0x64, 0xa2, 0x62, 0xd8, 0x46, 0xa6, 0xb0, 0xa2, 0x4b, 0x43, 0x27, 0x60, 0xd2, 0xf9, 0xde, 0x66, 0x5b}} }, + { (uint64_t)300000000000ull, {{0xac, 0x65, 0x83, 0x41, 0x5b, 0xd6, 0x4c, 0x3, 0x35, 0x97, 0xf9, 0x28, 0xa4, 0xb5, 0xd4, 0xf4, 0x78, 0x9e, 0xa8, 0xb2, 0x87, 0x82, 0x73, 0x89, 0xa8, 0x1e, 0xb6, 0x62, 0x9e, 0xc5, 0xb8, 0x50}} }, + { (uint64_t)400000000000ull, {{0x52, 0xf4, 0x9d, 0x89, 0xcf, 0x74, 0x13, 0x2f, 0xc7, 0x43, 0x2e, 0x6a, 0x6b, 0xef, 0xcf, 0xf3, 0xfd, 0x13, 0xd6, 0x3b, 0x51, 0x60, 0xab, 0x1c, 0xe6, 0x4a, 0xb0, 0xd1, 0x21, 0xcd, 0xa9, 0x9a}} }, + { (uint64_t)500000000000ull, {{0xe9, 0xaa, 0x7c, 0x81, 0xcd, 0xb5, 0xb3, 0x14, 0x8f, 0xb7, 0x62, 0x80, 0x63, 0xcd, 0x7a, 0x7, 0xd1, 0xad, 0xd1, 0x64, 0x3c, 0xed, 0xd3, 0xfa, 0x34, 0x47, 0x9d, 0x85, 0x9c, 0xc5, 0x62, 0x65}} }, + { (uint64_t)600000000000ull, {{0x98, 0x27, 0xae, 0x31, 0xe5, 0xc2, 0xa7, 0x78, 0x39, 0xf6, 0xb, 0x83, 0xab, 0x45, 0x78, 0xe2, 0xa0, 0x1e, 0xfa, 0x4b, 0x3b, 0x14, 0xcc, 0x72, 0x73, 0x14, 0xff, 0xd7, 0x15, 0x53, 0x63, 0xbf}} }, + { (uint64_t)700000000000ull, {{0x72, 0x91, 0x6a, 0x79, 0x27, 0xff, 0x13, 0x24, 0xd4, 0x98, 0x40, 0xec, 0xc0, 0x98, 0x68, 0xb8, 0xf3, 0x15, 0xe4, 0xf1, 0xf6, 0xd4, 0x45, 0x8d, 0x37, 0x5e, 0xc7, 0x45, 0xfc, 0x2e, 0x63, 0x53}} }, + { (uint64_t)800000000000ull, {{0x66, 0x76, 0xe0, 0x4, 0xf, 0xa4, 0xb8, 0x22, 0x9c, 0x61, 0x69, 0xc, 0x71, 0x32, 0x22, 0xcf, 0x3d, 0x37, 0xb9, 0x49, 0x3b, 0x49, 0x6, 0x80, 0xbb, 0x48, 0xd8, 0xd5, 0x1a, 0xde, 0x95, 0xf2}} }, + { (uint64_t)900000000000ull, {{0x41, 0x54, 0xb3, 0x46, 0x5a, 0x43, 0x72, 0x67, 0x1e, 0xa9, 0xe0, 0x64, 0xa7, 0xca, 0xa6, 0x6e, 0x14, 0xb4, 0x98, 0x6a, 0x46, 0x68, 0x91, 0x8a, 0xfa, 0x57, 0x9b, 0xf1, 0xed, 0x25, 0x6, 0xdd}} }, + { (uint64_t)1000000000000ull, {{0xbb, 0x6f, 0x70, 0x62, 0xca, 0x30, 0x6d, 0x67, 0x2a, 0x73, 0xe, 0x2a, 0x2f, 0x21, 0x9b, 0xdb, 0xe4, 0xc, 0x9f, 0xb3, 0xfe, 0x4d, 0x60, 0x13, 0x69, 0x2a, 0xf9, 0x3c, 0xdb, 0x2e, 0xc, 0xd1}} }, + { (uint64_t)2000000000000ull, {{0xbc, 0xe, 0xae, 0x5b, 0x9c, 0x6a, 0xd6, 0x38, 0x7a, 0x41, 0x19, 0x3c, 0x46, 0xf3, 0xc1, 0xd0, 0x71, 0x6d, 0x77, 0xd6, 0x4e, 0x22, 0xb2, 0xe0, 0x7b, 0x4b, 0xce, 0x75, 0x67, 0x65, 0xa2, 0xb}} }, + { (uint64_t)3000000000000ull, {{0x10, 0xc, 0x6f, 0x13, 0x42, 0xb7, 0x1b, 0x73, 0xed, 0xdd, 0xc5, 0x49, 0x2b, 0xe9, 0x23, 0x18, 0x2f, 00, 0xa6, 0x83, 0x48, 0x8e, 0xc3, 0xa2, 0xa1, 0xc7, 0xa9, 0x49, 0xcb, 0xe5, 0x77, 0x68}} }, + { (uint64_t)4000000000000ull, {{0x41, 0x9f, 0x7c, 0x94, 0x91, 0x4, 0x34, 0xf, 0xd3, 0xce, 0x85, 0x94, 0x8d, 0x2e, 0xf9, 0xf0, 0xdd, 0x4b, 0xb3, 0xd9, 0x2f, 0x5a, 0x78, 0x2c, 0x5f, 0x78, 0x4, 0xb7, 0x52, 0x9a, 0x13, 0xc6}} }, + { (uint64_t)5000000000000ull, {{0x40, 0x65, 0x34, 0x98, 0xbe, 0xa0, 0x22, 0xe3, 0x36, 0x5a, 0x3, 0xe5, 0x75, 0x25, 0xba, 0x65, 0x96, 0x53, 0x76, 0x24, 0x4f, 0xff, 0x10, 0x73, 0xe, 0xd9, 0x7a, 0x73, 0xb7, 0x53, 0x1, 0x91}} }, + { (uint64_t)6000000000000ull, {{0xdb, 0x1c, 0x7c, 0xf6, 0x8, 0x91, 0xf9, 0x65, 0xeb, 0xa9, 0xc6, 0x2, 0x24, 00, 0x63, 0xe, 00, 0x47, 0x95, 0x34, 0xe6, 0xf5, 0xb5, 0x33, 0xdc, 0xfc, 0x83, 0x19, 0x38, 0x52, 0x2c, 0x78}} }, + { (uint64_t)7000000000000ull, {{0x59, 0xa0, 0x3a, 0x31, 0x53, 0xa9, 0x94, 0xd7, 0x23, 0x27, 0xe4, 0xd9, 0x24, 0x21, 0xd3, 0xe3, 0x29, 0x1b, 0x1f, 0xa1, 0xb2, 0x40, 0xde, 0x44, 0xb9, 0x2d, 0x7f, 0x62, 0xec, 0x1, 0x28, 0xf1}} }, + { (uint64_t)8000000000000ull, {{0xb2, 0x80, 0xb9, 0x3b, 0x1e, 0x43, 0x88, 00, 0x73, 0xea, 0x4a, 0xa0, 0xef, 0x11, 0x4, 0xf8, 0x24, 0xbd, 0x12, 0x7a, 0x4a, 0x3d, 0xa2, 0x13, 0x92, 0x65, 0xf, 0xe8, 0xc6, 0x55, 0xb6, 0xc5}} }, + { (uint64_t)9000000000000ull, {{0xda, 0xf0, 0xd3, 0xe9, 0x32, 0x17, 0xd8, 0xe9, 0x5a, 0xbf, 0xdd, 0xf1, 0x3b, 0x7f, 0xd4, 0x8e, 0x34, 0x47, 0xad, 0x9, 0x23, 0x26, 0xb8, 0x99, 0xed, 0x58, 0x1f, 0xd5, 0xf8, 0x6, 0xc5, 0x6}} }, + { (uint64_t)10000000000000ull, {{0x16, 0x3d, 0xd6, 0x82, 0xec, 0x97, 0x7c, 0xdd, 0xa5, 0x95, 0x31, 0xda, 0x3f, 0xfa, 0x72, 0x99, 0x8a, 0x6f, 0x88, 0x37, 0xab, 0xad, 0xc6, 0x36, 0xaa, 0xed, 0xc8, 0xbe, 0x19, 0xb2, 0xd7, 0xc7}} }, + { (uint64_t)20000000000000ull, {{0x2, 0xfa, 0x35, 0x3a, 0xa8, 0x4e, 0xa8, 0xc4, 0x4c, 0x80, 0x23, 0x6, 0x5d, 0x79, 0x41, 0x60, 0x6b, 0x1f, 0xa5, 0xc2, 0x64, 0xdc, 0xcf, 0x46, 0xdc, 0x64, 0x94, 0xeb, 0xe9, 0x60, 0x6f, 0x20}} }, + { (uint64_t)30000000000000ull, {{0x87, 0x5, 0xd, 0xab, 0xf5, 0xb2, 0x3e, 0x8b, 0x79, 0x81, 0x3f, 0x4e, 0xd7, 0x6a, 0xa4, 0xad, 0xd2, 0x25, 0xdd, 0x2a, 0x50, 0x89, 0xaf, 0x6, 0x7d, 0xa7, 0x7c, 0xcb, 0x6e, 0xc5, 0x59, 0x46}} }, + { (uint64_t)40000000000000ull, {{0xaa, 0xe6, 0xb2, 0xc8, 0xa2, 0x9e, 0x4d, 0xbc, 0x63, 0x76, 0xc1, 0x72, 0x5, 0xfb, 0x2, 0x85, 0xe7, 0xd7, 0xd3, 0x25, 0x32, 0x3c, 0xd5, 0x26, 0xf, 0x98, 0xad, 0xff, 0xf7, 0xd4, 0xd4, 0xfb}} }, + { (uint64_t)50000000000000ull, {{0x9d, 0x79, 0x28, 0x82, 0x12, 0xa1, 0xe2, 0x3c, 0x9, 0x9f, 0xb2, 0xd8, 0xf0, 0xd0, 0xdb, 0xd3, 0xc2, 0xec, 0xd7, 0x58, 0xb9, 0xe6, 0xb5, 0xb4, 0xf2, 0x90, 0x60, 0x7, 0x9f, 0x19, 0x66, 0x9f}} }, + { (uint64_t)60000000000000ull, {{0x18, 0x90, 0x10, 0x6f, 0x1b, 0x97, 0xbc, 0x2d, 0xa, 0xe3, 0x96, 0xe5, 0xe5, 0x5e, 0xbf, 0xcc, 0x8e, 0xf6, 0x91, 0x7f, 0xb1, 0x96, 0xcb, 0x2b, 0x1e, 0x80, 0x25, 0x5d, 0x54, 0xb6, 0x87, 0x10}} }, + { (uint64_t)70000000000000ull, {{0x57, 0xd3, 0x4e, 0xf7, 0x54, 0x3b, 0xe4, 0x7b, 0x7b, 0xf4, 0x97, 0xce, 0x4a, 0x17, 0x6e, 0x78, 0xc6, 0xd6, 0x5c, 0xd3, 0x27, 0xf6, 0x4b, 0xa7, 0x5c, 0x27, 0xd1, 0x57, 0xb3, 0x37, 0x12, 0x5d}} }, + { (uint64_t)80000000000000ull, {{0x5e, 0xcb, 0x10, 0x15, 0x4b, 0x96, 0xca, 0xb5, 0x5e, 0x9, 0x46, 0x83, 0xf8, 0xdb, 0xff, 0x7f, 0x56, 0x63, 0x5f, 0xa6, 0x64, 0x97, 0xee, 0x9e, 0x24, 0xe, 0x83, 0x63, 0x7c, 0x7c, 0x87, 0x72}} }, + { (uint64_t)90000000000000ull, {{0x42, 0x32, 0x69, 0x98, 0x51, 0x30, 0xf1, 0x66, 0x51, 0x6a, 0x5b, 0xa8, 0x61, 0x9, 0x6d, 0x72, 0xec, 0xcc, 0x67, 0xad, 0xab, 0xa4, 0x5e, 0xb3, 0x73, 0x9a, 0xe, 0xbc, 0x61, 0xa3, 0x20, 0xae}} }, + { (uint64_t)100000000000000ull, {{0xa8, 0xd1, 0x60, 0x95, 0x91, 0x49, 0x8f, 0xa7, 0xc2, 0x94, 0x27, 0xad, 0x89, 0x31, 0xaf, 0x36, 0xc5, 0x2d, 0xc9, 0x7b, 0x4a, 0x11, 0xe7, 0x47, 0xa9, 0x56, 0xc2, 0x8c, 0x42, 0x54, 0xcf, 0xd4}} }, + { (uint64_t)200000000000000ull, {{0x23, 0x14, 0x49, 00, 0xa8, 0x66, 0xe8, 0xc1, 0xbf, 0x40, 0x98, 0xda, 0xa9, 0x48, 0xb9, 0x86, 0xf3, 0x84, 0xe, 0x5a, 0x7d, 0x21, 0x5e, 0xf0, 0xd5, 0x64, 0xef, 0xd8, 0xbe, 0xc6, 0x83, 0x15}} }, + { (uint64_t)300000000000000ull, {{0x6a, 0x51, 0x47, 0x3c, 0x86, 0xed, 0xad, 0x53, 0x51, 0x4b, 0x3f, 0x95, 0x97, 0xed, 0x21, 0xae, 00, 0x81, 0x51, 0xa0, 0x9e, 0x43, 0xad, 0xdd, 0x45, 0xd1, 0x74, 0x63, 0xc5, 0x34, 0x3, 0x97}} }, + { (uint64_t)400000000000000ull, {{0x8, 0xbd, 0xd4, 0xc3, 0xe4, 0x53, 0x1b, 0x29, 0x7a, 0x70, 00, 0x1e, 0xb8, 0xa4, 0xf1, 0x98, 0xdc, 0x3b, 0xd4, 0xf1, 0xf5, 0x60, 0x9a, 0xda, 0x98, 0xf6, 0xd9, 0x5f, 0x9a, 0x1a, 0x30, 0x2e}} }, + { (uint64_t)500000000000000ull, {{0x97, 0x55, 0x70, 0xea, 0x12, 0xde, 0x5a, 0xf5, 0xc5, 0x36, 0xbd, 0xb6, 0x83, 0x54, 0xfb, 0xc8, 0x32, 0x21, 0x50, 0xfc, 0x56, 0x83, 0x7c, 0x4b, 0x78, 0xa9, 0x85, 0x76, 0x5d, 0x2a, 0x70, 0x99}} }, + { (uint64_t)600000000000000ull, {{0xa7, 0xa6, 0x39, 0x93, 0x41, 0xcb, 0x4d, 0x67, 0x76, 0xcd, 0x94, 0xd, 0x1d, 0x6a, 0xb0, 0xac, 0xa, 0xbf, 0x56, 0x93, 0x6a, 0x35, 0x31, 0xdf, 0xe9, 0x6c, 0x23, 0x69, 0x97, 0x8e, 0x49, 0xfa}} }, + { (uint64_t)700000000000000ull, {{0x55, 0x9, 0x3e, 0x5e, 0xeb, 0xca, 0x3, 0x88, 0x48, 0xdc, 0x99, 0x7e, 0x31, 0x95, 0xec, 0xc5, 0x8f, 0xb4, 0xa5, 0x71, 0xb9, 0x52, 0x56, 0xc0, 0xff, 0x49, 0xbe, 0xd0, 0xf1, 0x65, 0x22, 0xbd}} }, + { (uint64_t)800000000000000ull, {{0xbb, 0xc6, 0x18, 0x2, 0x24, 0xaf, 0xd3, 0x38, 0xa6, 0xf4, 0xa0, 0x6b, 0x11, 0x98, 0x40, 0x68, 0xeb, 0x36, 0x35, 0xe7, 0xe5, 0x47, 0x66, 0x69, 0x78, 0x83, 0xaf, 0xbd, 0xce, 0xad, 0x2f, 0x31}} }, + { (uint64_t)900000000000000ull, {{0x61, 0x3a, 0xa1, 0x2c, 0xc0, 0xa1, 0x9b, 0xc8, 0x43, 0x63, 0x50, 0xbb, 0xc0, 0xf6, 0x16, 0x32, 0x6e, 0x64, 0x85, 0x83, 0x33, 0x4a, 0x32, 0x65, 0x16, 0x29, 0xe9, 0x5, 0xc5, 0x20, 0x62, 0x69}} }, + { (uint64_t)1000000000000000ull, {{0x52, 0xdd, 0xf8, 0x81, 0x13, 0xa0, 0xfc, 0xf2, 0x12, 0x90, 0x95, 0xc6, 0x18, 0x91, 0xbe, 0x88, 0x5c, 0x9, 0x30, 0x8, 0xeb, 0xc4, 0x65, 0xc, 0xb0, 0xee, 0xa5, 0x60, 0xcd, 0x4d, 0x75, 0x1b}} }, + { (uint64_t)2000000000000000ull, {{0x75, 0xbd, 0xfc, 0x35, 0xa6, 0xdf, 0x76, 0xe5, 0x98, 0x8e, 0xd9, 0xe3, 0x10, 0xa5, 0x89, 0x16, 0xae, 0xf0, 0xc5, 0xf0, 0x5b, 0x89, 0x22, 0xea, 0xae, 0x2c, 0xf9, 0x8f, 0x58, 0x42, 0x3c, 0xe3}} }, + { (uint64_t)3000000000000000ull, {{0x88, 0x98, 0x93, 0xe8, 0x7d, 0x56, 0x9f, 0x14, 0xb2, 0x48, 0xd1, 0xed, 0x93, 0xe8, 0xce, 0x60, 0xbb, 0xe3, 0x73, 0x69, 0xb0, 0xd6, 0xc7, 0xa1, 0x86, 0x89, 0x33, 0xd3, 0xc3, 0xda, 0x9a, 0x72}} }, + { (uint64_t)4000000000000000ull, {{0x88, 0x3e, 0xf3, 0x4b, 0xa2, 0xc1, 0x91, 0xf4, 0x9d, 0x3c, 0xc6, 0xad, 0xa0, 0xaf, 0xf1, 0xcf, 0xb1, 0x77, 0xbd, 0x9e, 0xd4, 0xb3, 0xa5, 0x37, 0x84, 0xb7, 0xf1, 0x62, 0x9b, 0xed, 0x17, 0x41}} }, + { (uint64_t)5000000000000000ull, {{0xa2, 0x90, 0x7c, 0x39, 0x84, 0xb1, 0x4a, 0xb1, 0xf4, 0xda, 0x58, 0xc2, 0xc8, 0x2d, 0x6b, 0x24, 0xf1, 0x29, 0x49, 0x9, 0x75, 0xfc, 0x4a, 0x33, 0x3d, 0x25, 0xa1, 0xf9, 0x2b, 0xc4, 0x32, 0xb6}} }, + { (uint64_t)6000000000000000ull, {{0xa0, 0x7d, 0x9f, 0x18, 0x95, 0x1f, 0xf2, 0x32, 0xcf, 0x4e, 0xc0, 0xee, 0x2f, 0xbc, 0xc3, 0xe1, 0x1b, 0x2c, 0xaf, 0xc9, 0x57, 0x65, 0x82, 0x10, 0x38, 0x1e, 0x3e, 0xe4, 0xed, 0xec, 0x2e, 0x7a}} }, + { (uint64_t)7000000000000000ull, {{0x66, 0x80, 0x21, 0xd5, 0xde, 0x8c, 0xa4, 0xc1, 0x8f, 0x5a, 0x74, 0xf2, 0x78, 0x69, 0xc4, 0xd6, 0xd4, 0x93, 0xa3, 0x30, 0x39, 0x3c, 0xf0, 0x26, 0x41, 0xff, 0xa8, 0x56, 0x7b, 0xa5, 0x36, 0x20}} }, + { (uint64_t)8000000000000000ull, {{0xe0, 0x48, 0x7a, 0xc4, 0x5a, 0x82, 0x59, 0xe3, 0xe5, 0xf2, 0xd9, 0xb8, 0xf6, 0xb8, 0xfa, 0x26, 0x9a, 0x63, 0x49, 0x71, 0xa2, 0xf7, 0xc2, 0x1a, 0x54, 0x17, 0x76, 0x81, 0xeb, 0x2, 0xbd, 0x4a}} }, + { (uint64_t)9000000000000000ull, {{0x98, 0x92, 0x6a, 0x3a, 0xf0, 0x5b, 0xf4, 0xa9, 0x8d, 0xf9, 0xf6, 0x4a, 0xe7, 0xb9, 0xda, 0x45, 0xa7, 0x6, 0xc3, 0xf8, 0x39, 0x5e, 0x47, 0x1f, 0x96, 0xed, 0x3c, 0x6, 0x6, 0xbe, 0xbb, 0x71}} }, + { (uint64_t)10000000000000000ull, {{0x80, 0xad, 0xb7, 0xd, 0x46, 0xf6, 0x3a, 0x75, 0x64, 0xa3, 0xf6, 0x71, 0xd9, 0xba, 0x95, 0x71, 0xb7, 0xf7, 0x95, 0xa9, 0x63, 0x38, 0x2a, 0x4d, 0x9f, 0xaf, 0x2d, 0x54, 0xf6, 0xc6, 0x84, 0x29}} }, + { (uint64_t)20000000000000000ull, {{0xae, 0xbd, 0x97, 0x42, 0x1f, 0x3f, 0xca, 0xe8, 0x95, 0x18, 0x60, 0xe6, 0xd9, 0xd1, 0xf3, 0xec, 0x59, 0x73, 0xa2, 0xf7, 0x66, 0x88, 0x4b, 0xfe, 0x17, 0x50, 0x79, 0x51, 0xe4, 0x62, 0xc6, 0x63}} }, + { (uint64_t)30000000000000000ull, {{0x61, 0x2, 0x6c, 0x84, 0x2a, 0x6a, 0x22, 0x25, 0x74, 0x6b, 0x19, 0x6b, 0x56, 0x89, 0xe1, 0x18, 0xf5, 0x41, 0x34, 0x15, 0x98, 0x1d, 0x7, 0x73, 0x62, 0xb2, 0xe7, 0xb9, 0xac, 0xa5, 0x28, 0x16}} }, + { (uint64_t)40000000000000000ull, {{0x52, 0x54, 0xb5, 0x78, 0xe8, 0x57, 0x9a, 0x27, 0x3b, 0x89, 0x8e, 0x65, 0x9d, 0xd3, 0xe1, 0xa1, 0xcf, 0xba, 0x12, 0x47, 0x26, 0x64, 0xbd, 0x4e, 0x7f, 0x9a, 0x13, 0xb1, 0xfc, 0xee, 0x2, 0x93}} }, + { (uint64_t)50000000000000000ull, {{0x7b, 0x2a, 0xb, 00, 0xcf, 0xdc, 0xa9, 0x51, 0x46, 0xcf, 0x80, 0x95, 0xdd, 0x2b, 0x82, 0x90, 0x91, 0xb0, 0xf2, 0xd5, 0xbb, 0xc, 0x33, 0x82, 0x2d, 0x8b, 0x43, 0x42, 0x69, 0xcd, 0x2a, 0x42}} }, + { (uint64_t)60000000000000000ull, {{0x21, 0x57, 0x4f, 0xed, 0x15, 0x1a, 0x2f, 0x9f, 0x64, 0xa4, 0x5b, 0xe2, 0x8a, 0x3a, 0xf5, 0x88, 0xe9, 0xf2, 0xd1, 0x71, 0x35, 0xa3, 0x53, 0x7f, 0x7, 0xfd, 0x6a, 0xef, 0xa2, 0x9f, 0x2, 0xaf}} }, + { (uint64_t)70000000000000000ull, {{0x1a, 0xf2, 0x41, 0xe1, 0x38, 0x27, 0x98, 0x29, 0xac, 0x6a, 0xe6, 0x2f, 0xf, 0x33, 0x20, 0x4b, 0xb2, 0x8a, 0xfd, 0x6, 0x5c, 0x42, 0x59, 0x3b, 0xdc, 0x79, 0x14, 0x85, 0x97, 0x5b, 0x26, 0x95}} }, + { (uint64_t)80000000000000000ull, {{0xa8, 0xc8, 0xb8, 0x7b, 0x51, 0x2d, 0xef, 0x9b, 0x5e, 0x50, 0xe, 0xb4, 0x98, 0xaf, 0x86, 0xaa, 0xd2, 0x46, 0x4a, 0xea, 0xe7, 0x6d, 0xb1, 0xf6, 0x5d, 0x23, 0x26, 0xce, 0x90, 0x26, 0xec, 0x69}} }, + { (uint64_t)90000000000000000ull, {{0x3d, 0x78, 0x73, 0x63, 0x95, 0xf1, 0xd7, 0xde, 0x8e, 0x16, 0xc0, 0xb5, 0xa9, 0x9f, 0x4d, 0xc4, 0xeb, 0x8f, 0x22, 0xac, 0xc1, 0x5b, 0x21, 0x42, 0x44, 0x1d, 0xbd, 0x8d, 0x2c, 0x31, 0xb9, 0xce}} }, + { (uint64_t)100000000000000000ull, {{0x27, 0x27, 0xd4, 0x93, 0x2f, 0x98, 0x39, 0xe4, 0x3b, 0x6b, 0xf5, 0xfb, 0x29, 0xa3, 0xbe, 0x4c, 0x9, 0xb, 0x6e, 0xb9, 0x31, 00, 0xbb, 0x92, 0x58, 0x1a, 0xdb, 0x8d, 0xd2, 0xb6, 0x61, 0x54}} }, + { (uint64_t)200000000000000000ull, {{0xae, 0x96, 0x78, 0x2e, 0xf2, 0xc4, 0xdf, 0x7d, 0x2e, 0x4, 0xcc, 0xf9, 0xef, 0x76, 0x23, 0x7f, 0x17, 0xc, 0x97, 0x3, 0xb4, 0x92, 0xc0, 0x78, 0x52, 0x6e, 0xb1, 0xf6, 0x85, 0x3d, 0xb1, 0x33}} }, + { (uint64_t)300000000000000000ull, {{0x17, 0x43, 0xfe, 0xab, 0x12, 0xad, 0xe5, 0xfe, 0x12, 0x53, 0x22, 0x27, 0x2f, 0xd1, 0x40, 0x6b, 0x74, 0xe8, 0x19, 0x70, 0x32, 0x68, 0x46, 0x22, 0xee, 0x79, 0xab, 0xcd, 0x94, 0x93, 0x66, 0x4c}} }, + { (uint64_t)400000000000000000ull, {{0x7, 0x9b, 0xf2, 0xa9, 0x6e, 0x16, 0x6e, 0xf9, 0xe6, 0xb2, 0x23, 0x1d, 0xb9, 0x85, 0x8b, 0x99, 0x98, 0x7f, 0x49, 0x33, 0x87, 0xde, 0xeb, 0xd5, 0x17, 0x48, 0x54, 0x9a, 0xd, 0xf7, 0xdc, 0x44}} }, + { (uint64_t)500000000000000000ull, {{0xca, 0xba, 0x97, 0x98, 0x51, 0x6d, 0xad, 0x3, 0x38, 0xd0, 0x6e, 0x10, 0x6d, 0x76, 0xa2, 0x1, 0x93, 0x7a, 0xce, 0x4c, 0x91, 0x53, 0x9e, 0x61, 0x7d, 0x89, 0x28, 0x73, 0x6, 0xa3, 0x92, 0xb1}} }, + { (uint64_t)600000000000000000ull, {{0x6b, 0x8, 0x7f, 0x48, 0xb3, 0xd7, 0xaa, 0xc9, 0x57, 0xc4, 0x52, 0xe5, 0x1a, 0x18, 0xd7, 0x26, 0xb, 0xf8, 0xc8, 0x56, 0xc4, 0xc7, 0x1e, 0x48, 0xf6, 0x49, 0xae, 00, 0x4a, 0xf6, 0x8f, 0x13}} }, + { (uint64_t)700000000000000000ull, {{0x9e, 0xed, 0x8b, 0x23, 0x1f, 0x79, 0x4c, 0x46, 0x5c, 0xbe, 0x88, 0x40, 0xd0, 0xf1, 0x6f, 0x7b, 0x9f, 0x9c, 0x6e, 0xb4, 0x9c, 0x20, 0x7d, 0xe9, 0xd8, 0x55, 0x11, 0x83, 0xd0, 0xc7, 0x6e, 0x43}} }, + { (uint64_t)800000000000000000ull, {{0x58, 0x4a, 0x78, 0x93, 0x13, 0x7e, 0xbd, 0x2, 0x8b, 0xa7, 0x59, 0x82, 0xc3, 0x39, 0xb7, 0x66, 0xaa, 0xda, 0xad, 0xf9, 0x14, 0x50, 0xf9, 0x40, 0x7d, 0x2a, 0x97, 0xd7, 0xf6, 0xb1, 0x93, 0x5e}} }, + { (uint64_t)900000000000000000ull, {{0x7, 0xce, 0x54, 0xb1, 0x18, 0x26, 0xa1, 0x75, 0x23, 0x13, 0x55, 0x1a, 00, 0x20, 0xfd, 0x79, 0x8a, 00, 0x9e, 0x20, 0xcd, 0xb2, 0x40, 0x1d, 0x52, 0x51, 0xc1, 0x55, 0x8e, 0xea, 0xd2, 0x6c}} }, + { (uint64_t)1000000000000000000ull, {{0x39, 0x80, 0x7f, 0x3d, 0xce, 0xb0, 0xa6, 0xfe, 0x34, 0xa7, 0xa1, 0xed, 0xc6, 0x9b, 0x78, 0xff, 0xbe, 0xd5, 0xa7, 0x8c, 0x6c, 0x87, 0x5d, 0xda, 0x96, 0x69, 0xdb, 0xb2, 0x95, 0x70, 0xf0, 0xf4}} }, + { (uint64_t)2000000000000000000ull, {{0xda, 0x74, 00, 0x86, 0xf1, 0x5c, 0xe8, 0x21, 0xe9, 0xd, 0x50, 0xaf, 0xcf, 0x80, 0x9c, 0x7e, 0x18, 0x51, 0x90, 0x1b, 0xa3, 0x5f, 0x9f, 0x63, 0x78, 0xd6, 0x40, 0x7c, 0xb9, 0xc7, 0xa2, 0x75}} }, + { (uint64_t)3000000000000000000ull, {{0x7, 0xa1, 0x75, 0x63, 0xae, 0xf5, 0xcf, 0xd0, 0x36, 0xfa, 0x64, 0xd4, 0xb1, 0x97, 0xa9, 0x51, 0xc0, 0xd2, 0x87, 0x2b, 0xd, 0xb6, 0xf9, 0xbe, 0x47, 0xe6, 0x7c, 0xa6, 0xb5, 0x35, 0xe2, 0x6e}} }, + { (uint64_t)4000000000000000000ull, {{0xe3, 0x49, 0xf7, 0xeb, 0xe5, 0x11, 0x39, 0xfe, 0xd5, 0x69, 0x40, 0x37, 0xd1, 0x14, 0xb7, 0xbd, 0x45, 0xdd, 0xa, 0x6a, 0xf0, 0x4b, 0x62, 0xec, 0xa4, 0xd8, 0xcd, 0x55, 0x2a, 0x14, 0xe3, 0xfb}} }, + { (uint64_t)5000000000000000000ull, {{0x8d, 0x59, 0x7e, 0xa9, 0xf5, 0x79, 0x9a, 0x4d, 0x15, 0x3d, 0x82, 0xd6, 0xf7, 0xbe, 0xa0, 0x2e, 0x52, 0x40, 0xa2, 0xc8, 0x9b, 0x4, 0x1e, 0x6, 0x2f, 0x37, 0xbc, 0x7b, 0x82, 0xa0, 0xac, 0x55}} }, + { (uint64_t)6000000000000000000ull, {{0xa3, 0x43, 0xa7, 0xe1, 0x14, 0x4d, 0x33, 0x50, 0xf, 0x3e, 0xfd, 0x38, 0x15, 0x82, 0xdd, 0xc5, 0xd0, 0x18, 0x3e, 0x5d, 0xcf, 0x8a, 0xfa, 0x64, 0xbb, 0x67, 0x6c, 0x97, 0x3e, 0x3d, 0x1a, 0xb1}} }, + { (uint64_t)7000000000000000000ull, {{0x89, 0xe9, 0x3e, 0xe9, 0xf2, 0x4d, 0x72, 0x61, 0xe5, 0x44, 0xca, 0x8f, 0x9, 0xa7, 0x40, 0x4e, 0xe3, 0xa9, 0xe, 0xe2, 0x50, 0x7d, 0xda, 0xcf, 0x41, 0x2a, 0x58, 0xc, 0x9, 0x65, 0x1c, 0x53}} }, + { (uint64_t)8000000000000000000ull, {{0xc5, 0x94, 0x10, 0x81, 0x54, 0x69, 0xf4, 0x59, 0xd1, 0x5a, 0x6f, 0xe3, 0xf2, 0xa1, 0x1b, 0xa6, 0x31, 0x12, 0xfa, 0xaa, 0xc5, 0x3d, 0xbc, 0x52, 0x5d, 0x3c, 0xfa, 0xb1, 0xfa, 0x9c, 0x3d, 0xdb}} }, + { (uint64_t)9000000000000000000ull, {{0x9d, 0xe7, 0xcb, 0xb, 0x8d, 0x7b, 0xac, 0x47, 0xff, 0xd3, 0x93, 0x1b, 0xcd, 0x82, 0xcd, 0xd5, 0x35, 0xc, 0x29, 0x34, 0xb1, 0x6e, 0xb, 0x64, 0x32, 0xab, 0xf7, 0xcb, 0x4b, 0x5c, 0x37, 0x6d}} }, + { (uint64_t)10000000000000000000ull, {{0x65, 0x8d, 0x1, 0x37, 0x6d, 0x18, 0x63, 0xe7, 0x7b, 0x9, 0x6f, 0x98, 0xe6, 0xe5, 0x13, 0xc2, 0x4, 0x10, 0xf5, 0xc7, 0xfb, 0x18, 0xa6, 0xe5, 0x9a, 0x52, 0x66, 0x84, 0x5c, 0xd9, 0xb1, 0xe3}} }, }; namespace rct { From 4cd881369a836406240ff1295739995362c9ce62 Mon Sep 17 00:00:00 2001 From: Hom DX Date: Fri, 23 Nov 2018 00:52:22 +0300 Subject: [PATCH 0787/1404] Updated dependencies Cmake 3.13, CPPZMQ 4.3.0, OPENSSL 1.1.0j --- Dockerfile | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Dockerfile b/Dockerfile index 86d833a98f0..0728c6ce8d6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -25,9 +25,9 @@ RUN set -ex && \ WORKDIR /usr/local #Cmake -ARG CMAKE_VERSION=3.12.1 -ARG CMAKE_VERSION_DOT=v3.12 -ARG CMAKE_HASH=c53d5c2ce81d7a957ee83e3e635c8cda5dfe20c9d501a4828ee28e1615e57ab2 +ARG CMAKE_VERSION=3.13.0 +ARG CMAKE_VERSION_DOT=v3.13 +ARG CMAKE_HASH=4058b2f1a53c026564e8936698d56c3b352d90df067b195cb749a97a3d273c90 RUN set -ex \ && curl -s -O https://cmake.org/files/${CMAKE_VERSION_DOT}/cmake-${CMAKE_VERSION}.tar.gz \ && echo "${CMAKE_HASH} cmake-${CMAKE_VERSION}.tar.gz" | sha256sum -c \ @@ -51,8 +51,8 @@ RUN set -ex \ ENV BOOST_ROOT /usr/local/boost_${BOOST_VERSION} # OpenSSL -ARG OPENSSL_VERSION=1.1.0h -ARG OPENSSL_HASH=5835626cde9e99656585fc7aaa2302a73a7e1340bf8c14fd635a62c66802a517 +ARG OPENSSL_VERSION=1.1.0j +ARG OPENSSL_HASH=31bec6c203ce1a8e93d5994f4ed304c63ccf07676118b6634edded12ad1b3246 RUN set -ex \ && curl -s -O https://www.openssl.org/source/openssl-${OPENSSL_VERSION}.tar.gz \ && echo "${OPENSSL_HASH} openssl-${OPENSSL_VERSION}.tar.gz" | sha256sum -c \ @@ -78,8 +78,8 @@ RUN set -ex \ && ldconfig # zmq.hpp -ARG CPPZMQ_VERSION=v4.2.3 -ARG CPPZMQ_HASH=6aa3ab686e916cb0e62df7fa7d12e0b13ae9fae6 +ARG CPPZMQ_VERSION=v4.3.0 +ARG CPPZMQ_HASH=213da0b04ae3b4d846c9abc46bab87f86bfb9cf4 RUN set -ex \ && git clone https://github.com/zeromq/cppzmq.git -b ${CPPZMQ_VERSION} \ && cd cppzmq \ From 3002307418b9ea904a078f726ed8c13306d0f6b8 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 23 Nov 2018 13:11:40 +0000 Subject: [PATCH 0788/1404] tests: slow_memmem now returns size_t Makes more sense than uint64_t for an offset, and agrees with the %zu used to print results. Found by codacy.com --- src/rpc/core_rpc_server.cpp | 2 +- tests/unit_tests/slow_memmem.cpp | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index df9eee781ac..d7968731a5c 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -1026,7 +1026,7 @@ namespace cryptonote //------------------------------------------------------------------------------------------------------------------------------ // equivalent of strstr, but with arbitrary bytes (ie, NULs) // This does not differentiate between "not found" and "found at offset 0" - uint64_t slow_memmem(const void* start_buff, size_t buflen,const void* pat,size_t patlen) + size_t slow_memmem(const void* start_buff, size_t buflen,const void* pat,size_t patlen) { const void* buf = start_buff; const void* end=(const char*)buf+buflen; diff --git a/tests/unit_tests/slow_memmem.cpp b/tests/unit_tests/slow_memmem.cpp index 1c67f5b587c..436259bee08 100644 --- a/tests/unit_tests/slow_memmem.cpp +++ b/tests/unit_tests/slow_memmem.cpp @@ -45,7 +45,7 @@ //#define VERBOSE #ifdef TEST_ORIGINAL -uint64_t slow_memmem_original(void* start_buff, size_t buflen,void* pat,size_t patlen) +size_t slow_memmem_original(void* start_buff, size_t buflen,void* pat,size_t patlen) { void* buf = start_buff; void* end=(char*)buf+buflen-patlen; @@ -63,7 +63,7 @@ uint64_t slow_memmem_original(void* start_buff, size_t buflen,void* pat,size_t p #define slow_memmem slow_memmem_original #else namespace cryptonote { - uint64_t slow_memmem(const void* start_buff, size_t buflen,const void* pat,size_t patlen); + size_t slow_memmem(const void* start_buff, size_t buflen,const void* pat,size_t patlen); } using namespace cryptonote; #endif @@ -73,7 +73,7 @@ static const struct { const char *buf; size_t patlen; const char *pat; - uint64_t res; + size_t res; } T[]={ {0,"",0,"",0}, {1,"",0,"",0}, @@ -117,7 +117,7 @@ TEST(slowmem,Success) memcpy(buf,T[n].buf,T[n].buflen); void *pat=malloc(T[n].patlen); memcpy(pat,T[n].pat,T[n].patlen); - uint64_t res=slow_memmem(buf,T[n].buflen,pat,T[n].patlen); + size_t res=slow_memmem(buf,T[n].buflen,pat,T[n].patlen); free(pat); free(buf); ASSERT_EQ(res,T[n].res); From 611639710d9f3665999b4ec71a52efc73aca4853 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 23 Nov 2018 13:25:15 +0000 Subject: [PATCH 0789/1404] a few minor (but easy) performance tweaks Found by codacy.com --- src/common/base58.cpp | 2 +- src/common/base58.h | 2 +- src/cryptonote_basic/cryptonote_basic_impl.cpp | 2 +- src/cryptonote_basic/cryptonote_basic_impl.h | 2 +- src/cryptonote_core/blockchain.cpp | 6 +++--- src/cryptonote_core/cryptonote_tx_utils.cpp | 6 +++--- src/cryptonote_core/cryptonote_tx_utils.h | 6 +++--- src/ringct/rctSigs.cpp | 4 ++-- src/ringct/rctSigs.h | 4 ++-- src/wallet/wallet2.cpp | 4 ++-- src/wallet/wallet2.h | 4 ++-- 11 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/common/base58.cpp b/src/common/base58.cpp index b28a04f2005..432105da4c7 100644 --- a/src/common/base58.cpp +++ b/src/common/base58.cpp @@ -235,7 +235,7 @@ namespace tools return encode(buf); } - bool decode_addr(std::string addr, uint64_t& tag, std::string& data) + bool decode_addr(const std::string &addr, uint64_t& tag, std::string& data) { std::string addr_data; bool r = decode(addr, addr_data); diff --git a/src/common/base58.h b/src/common/base58.h index 02ca969564d..69611859d71 100644 --- a/src/common/base58.h +++ b/src/common/base58.h @@ -41,6 +41,6 @@ namespace tools bool decode(const std::string& enc, std::string& data); std::string encode_addr(uint64_t tag, const std::string& data); - bool decode_addr(std::string addr, uint64_t& tag, std::string& data); + bool decode_addr(const std::string &addr, uint64_t& tag, std::string& data); } } diff --git a/src/cryptonote_basic/cryptonote_basic_impl.cpp b/src/cryptonote_basic/cryptonote_basic_impl.cpp index c4e10851e4a..f10e0d86ec3 100644 --- a/src/cryptonote_basic/cryptonote_basic_impl.cpp +++ b/src/cryptonote_basic/cryptonote_basic_impl.cpp @@ -322,7 +322,7 @@ namespace cryptonote { } //-------------------------------------------------------------------------------- -bool parse_hash256(const std::string str_hash, crypto::hash& hash) +bool parse_hash256(const std::string &str_hash, crypto::hash& hash) { std::string buf; bool res = epee::string_tools::parse_hexstr_to_binbuff(str_hash, buf); diff --git a/src/cryptonote_basic/cryptonote_basic_impl.h b/src/cryptonote_basic/cryptonote_basic_impl.h index c804a88fa55..0b8131a7a09 100644 --- a/src/cryptonote_basic/cryptonote_basic_impl.h +++ b/src/cryptonote_basic/cryptonote_basic_impl.h @@ -124,5 +124,5 @@ namespace cryptonote { bool operator ==(const cryptonote::block& a, const cryptonote::block& b); } -bool parse_hash256(const std::string str_hash, crypto::hash& hash); +bool parse_hash256(const std::string &str_hash, crypto::hash& hash); diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index e80e3f66c2a..104ad4de2e7 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -929,7 +929,7 @@ bool Blockchain::rollback_blockchain_switching(std::list& original_chain, m_hardfork->reorganize_from_chain_height(rollback_height); MINFO("Rollback to height " << rollback_height << " was successful."); - if (original_chain.size()) + if (!original_chain.empty()) { MINFO("Restoration to previous blockchain successful as well."); } @@ -1484,7 +1484,7 @@ bool Blockchain::handle_alternative_block(const block& b, const crypto::hash& id // if block to be added connects to known blocks that aren't part of the // main chain -- that is, if we're adding on to an alternate chain - if(alt_chain.size()) + if(!alt_chain.empty()) { // make sure alt chain doesn't somehow start past the end of the main chain CHECK_AND_ASSERT_MES(m_db->height() > alt_chain.front()->second.height, false, "main blockchain wrong height"); @@ -1857,7 +1857,7 @@ bool Blockchain::find_blockchain_supplement(const std::list& qbloc // make sure the request includes at least the genesis block, otherwise // how can we expect to sync from the client that the block list came from? - if(!qblock_ids.size()) + if(qblock_ids.empty()) { MCERROR("net.p2p", "Client sent wrong NOTIFY_REQUEST_CHAIN: m_block_ids.size()=" << qblock_ids.size() << ", dropping connection"); return false; diff --git a/src/cryptonote_core/cryptonote_tx_utils.cpp b/src/cryptonote_core/cryptonote_tx_utils.cpp index 4fc2736a6fc..f443d638caf 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.cpp +++ b/src/cryptonote_core/cryptonote_tx_utils.cpp @@ -198,7 +198,7 @@ namespace cryptonote return addr.m_view_public_key; } //--------------------------------------------------------------- - bool construct_tx_with_tx_key(const account_keys& sender_account_keys, const std::unordered_map& subaddresses, std::vector& sources, std::vector& destinations, const boost::optional& change_addr, std::vector extra, transaction& tx, uint64_t unlock_time, const crypto::secret_key &tx_key, const std::vector &additional_tx_keys, bool rct, rct::RangeProofType range_proof_type, rct::multisig_out *msout, bool shuffle_outs) + bool construct_tx_with_tx_key(const account_keys& sender_account_keys, const std::unordered_map& subaddresses, std::vector& sources, std::vector& destinations, const boost::optional& change_addr, const std::vector &extra, transaction& tx, uint64_t unlock_time, const crypto::secret_key &tx_key, const std::vector &additional_tx_keys, bool rct, rct::RangeProofType range_proof_type, rct::multisig_out *msout, bool shuffle_outs) { hw::device &hwdev = sender_account_keys.get_device(); @@ -610,7 +610,7 @@ namespace cryptonote return true; } //--------------------------------------------------------------- - bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map& subaddresses, std::vector& sources, std::vector& destinations, const boost::optional& change_addr, std::vector extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector &additional_tx_keys, bool rct, rct::RangeProofType range_proof_type, rct::multisig_out *msout) + bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map& subaddresses, std::vector& sources, std::vector& destinations, const boost::optional& change_addr, const std::vector &extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector &additional_tx_keys, bool rct, rct::RangeProofType range_proof_type, rct::multisig_out *msout) { hw::device &hwdev = sender_account_keys.get_device(); hwdev.open_tx(tx_key); @@ -633,7 +633,7 @@ namespace cryptonote return r; } //--------------------------------------------------------------- - bool construct_tx(const account_keys& sender_account_keys, std::vector& sources, const std::vector& destinations, const boost::optional& change_addr, std::vector extra, transaction& tx, uint64_t unlock_time) + bool construct_tx(const account_keys& sender_account_keys, std::vector& sources, const std::vector& destinations, const boost::optional& change_addr, const std::vector &extra, transaction& tx, uint64_t unlock_time) { std::unordered_map subaddresses; subaddresses[sender_account_keys.m_account_address.m_spend_public_key] = {0,0}; diff --git a/src/cryptonote_core/cryptonote_tx_utils.h b/src/cryptonote_core/cryptonote_tx_utils.h index f2cf7b6ca52..87edafe9d87 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.h +++ b/src/cryptonote_core/cryptonote_tx_utils.h @@ -89,9 +89,9 @@ namespace cryptonote //--------------------------------------------------------------- crypto::public_key get_destination_view_key_pub(const std::vector &destinations, const boost::optional& change_addr); - bool construct_tx(const account_keys& sender_account_keys, std::vector &sources, const std::vector& destinations, const boost::optional& change_addr, std::vector extra, transaction& tx, uint64_t unlock_time); - bool construct_tx_with_tx_key(const account_keys& sender_account_keys, const std::unordered_map& subaddresses, std::vector& sources, std::vector& destinations, const boost::optional& change_addr, std::vector extra, transaction& tx, uint64_t unlock_time, const crypto::secret_key &tx_key, const std::vector &additional_tx_keys, bool rct = false, rct::RangeProofType range_proof_type = rct::RangeProofBorromean, rct::multisig_out *msout = NULL, bool shuffle_outs = true); - bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map& subaddresses, std::vector& sources, std::vector& destinations, const boost::optional& change_addr, std::vector extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector &additional_tx_keys, bool rct = false, rct::RangeProofType range_proof_type = rct::RangeProofBorromean, rct::multisig_out *msout = NULL); + bool construct_tx(const account_keys& sender_account_keys, std::vector &sources, const std::vector& destinations, const boost::optional& change_addr, const std::vector &extra, transaction& tx, uint64_t unlock_time); + bool construct_tx_with_tx_key(const account_keys& sender_account_keys, const std::unordered_map& subaddresses, std::vector& sources, std::vector& destinations, const boost::optional& change_addr, const std::vector &extra, transaction& tx, uint64_t unlock_time, const crypto::secret_key &tx_key, const std::vector &additional_tx_keys, bool rct = false, rct::RangeProofType range_proof_type = rct::RangeProofBorromean, rct::multisig_out *msout = NULL, bool shuffle_outs = true); + bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map& subaddresses, std::vector& sources, std::vector& destinations, const boost::optional& change_addr, const std::vector &extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector &additional_tx_keys, bool rct = false, rct::RangeProofType range_proof_type = rct::RangeProofBorromean, rct::multisig_out *msout = NULL); bool generate_genesis_block( block& bl diff --git a/src/ringct/rctSigs.cpp b/src/ringct/rctSigs.cpp index dccd188677d..5ad785c961c 100644 --- a/src/ringct/rctSigs.cpp +++ b/src/ringct/rctSigs.cpp @@ -444,7 +444,7 @@ namespace rct { // this shows that sum inputs = sum outputs //Ver: // verifies the above sig is created corretly - mgSig proveRctMG(const key &message, const ctkeyM & pubs, const ctkeyV & inSk, const ctkeyV &outSk, const ctkeyV & outPk, const multisig_kLRki *kLRki, key *mscout, unsigned int index, key txnFeeKey, hw::device &hwdev) { + mgSig proveRctMG(const key &message, const ctkeyM & pubs, const ctkeyV & inSk, const ctkeyV &outSk, const ctkeyV & outPk, const multisig_kLRki *kLRki, key *mscout, unsigned int index, const key &txnFeeKey, hw::device &hwdev) { mgSig mg; //setup vars size_t cols = pubs.size(); @@ -534,7 +534,7 @@ namespace rct { // this shows that sum inputs = sum outputs //Ver: // verifies the above sig is created corretly - bool verRctMG(const mgSig &mg, const ctkeyM & pubs, const ctkeyV & outPk, key txnFeeKey, const key &message) { + bool verRctMG(const mgSig &mg, const ctkeyM & pubs, const ctkeyV & outPk, const key &txnFeeKey, const key &message) { PERF_TIMER(verRctMG); //setup vars size_t cols = pubs.size(); diff --git a/src/ringct/rctSigs.h b/src/ringct/rctSigs.h index b67a0b99293..459edc6008e 100644 --- a/src/ringct/rctSigs.h +++ b/src/ringct/rctSigs.h @@ -96,9 +96,9 @@ namespace rct { // this shows that sum inputs = sum outputs //Ver: // verifies the above sig is created corretly - mgSig proveRctMG(const ctkeyM & pubs, const ctkeyV & inSk, const keyV &outMasks, const ctkeyV & outPk, const multisig_kLRki *kLRki, key *mscout, unsigned int index, key txnFee, const key &message, hw::device &hwdev); + mgSig proveRctMG(const ctkeyM & pubs, const ctkeyV & inSk, const keyV &outMasks, const ctkeyV & outPk, const multisig_kLRki *kLRki, key *mscout, unsigned int index, const key &txnFee, const key &message, hw::device &hwdev); mgSig proveRctMGSimple(const key & message, const ctkeyV & pubs, const ctkey & inSk, const key &a , const key &Cout, const multisig_kLRki *kLRki, key *mscout, unsigned int index, hw::device &hwdev); - bool verRctMG(const mgSig &mg, const ctkeyM & pubs, const ctkeyV & outPk, key txnFee, const key &message); + bool verRctMG(const mgSig &mg, const ctkeyM & pubs, const ctkeyV & outPk, const key &txnFee, const key &message); bool verRctMGSimple(const key &message, const mgSig &mg, const ctkeyV & pubs, const key & C); //These functions get keys from blockchain diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 129b96733bc..0db110c00f8 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -4292,7 +4292,7 @@ std::string wallet2::make_multisig(const epee::wipeable_string &password, return make_multisig(password, secret_keys, public_keys, threshold); } -bool wallet2::finalize_multisig(const epee::wipeable_string &password, std::unordered_set pkeys, std::vector signers) +bool wallet2::finalize_multisig(const epee::wipeable_string &password, const std::unordered_set &pkeys, std::vector signers) { exchange_multisig_keys(password, pkeys, signers); return true; @@ -10420,7 +10420,7 @@ const std::pair, std::vector>& w return m_account_tags; } -void wallet2::set_account_tag(const std::set account_indices, const std::string& tag) +void wallet2::set_account_tag(const std::set &account_indices, const std::string& tag) { for (uint32_t account_index : account_indices) { diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index eb076313115..bcd975d3fcd 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -592,7 +592,7 @@ namespace tools /*! * \brief Finalizes creation of a multisig wallet */ - bool finalize_multisig(const epee::wipeable_string &password, std::unordered_set pkeys, std::vector signers); + bool finalize_multisig(const epee::wipeable_string &password, const std::unordered_set &pkeys, std::vector signers); /*! * Get a packaged multisig information string */ @@ -1045,7 +1045,7 @@ namespace tools * \param account_indices Indices of accounts. * \param tag Tag's name. If empty, the accounts become untagged. */ - void set_account_tag(const std::set account_indices, const std::string& tag); + void set_account_tag(const std::set &account_indices, const std::string& tag); /*! * \brief Set the label of the given tag. * \param tag Tag's name (which must be non-empty). From d4f50cb109c45c1f50700cbe2e88ec891eb7d469 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 23 Nov 2018 13:47:51 +0000 Subject: [PATCH 0790/1404] remove some unused code Found by codacy.com --- .../blockchain_import.cpp | 2 +- src/common/dns_utils.cpp | 2 +- src/cryptonote_core/cryptonote_core.cpp | 8 +-- src/cryptonote_core/cryptonote_core.h | 3 +- src/daemon/core.h | 17 +---- src/device/device_ledger.cpp | 2 - src/rpc/daemon_messages.cpp | 72 ------------------- src/simplewallet/simplewallet.cpp | 1 - src/wallet/wallet2.cpp | 5 -- tests/core_tests/chaingen.h | 2 +- tests/core_tests/transaction_tests.cpp | 3 - tests/unit_tests/mnemonics.cpp | 1 - tests/unit_tests/multisig.cpp | 2 +- tests/unit_tests/ringct.cpp | 2 - 14 files changed, 8 insertions(+), 114 deletions(-) diff --git a/src/blockchain_utilities/blockchain_import.cpp b/src/blockchain_utilities/blockchain_import.cpp index eae078ea256..2a8a6f494af 100644 --- a/src/blockchain_utilities/blockchain_import.cpp +++ b/src/blockchain_utilities/blockchain_import.cpp @@ -764,7 +764,7 @@ int main(int argc, char* argv[]) #else const GetCheckpointsCallback& get_checkpoints = nullptr; #endif - if (!core.init(vm, nullptr, nullptr, get_checkpoints)) + if (!core.init(vm, nullptr, get_checkpoints)) { std::cerr << "Failed to initialize core" << ENDL; return 1; diff --git a/src/common/dns_utils.cpp b/src/common/dns_utils.cpp index 606a2c7b723..4aa777c4d4f 100644 --- a/src/common/dns_utils.cpp +++ b/src/common/dns_utils.cpp @@ -305,7 +305,7 @@ std::vector DNSResolver::get_record(const std::string& url, int rec // call DNS resolver, blocking. if return value not zero, something went wrong if (!ub_resolve(m_data->m_ub_context, string_copy(url.c_str()), record_type, DNS_CLASS_IN, &result)) { - dnssec_available = (result->secure || (!result->secure && result->bogus)); + dnssec_available = (result->secure || result->bogus); dnssec_valid = result->secure && !result->bogus; if (result->havedata) { diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 10ab3fe65b1..c164ce9377d 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -389,7 +389,7 @@ namespace cryptonote return m_blockchain_storage.get_alternative_blocks_count(); } //----------------------------------------------------------------------------------------------- - bool core::init(const boost::program_options::variables_map& vm, const char *config_subdir, const cryptonote::test_options *test_options, const GetCheckpointsCallback& get_checkpoints/* = nullptr */) + bool core::init(const boost::program_options::variables_map& vm, const cryptonote::test_options *test_options, const GetCheckpointsCallback& get_checkpoints/* = nullptr */) { start_time = std::time(nullptr); @@ -399,10 +399,6 @@ namespace cryptonote m_nettype = FAKECHAIN; } bool r = handle_command_line(vm); - std::string m_config_folder_mempool = m_config_folder; - - if (config_subdir) - m_config_folder_mempool = m_config_folder_mempool + "/" + config_subdir; std::string db_type = command_line::get_arg(vm, cryptonote::arg_db_type); std::string db_sync_mode = command_line::get_arg(vm, cryptonote::arg_db_sync_mode); @@ -834,7 +830,7 @@ namespace cryptonote TRY_ENTRY(); CRITICAL_REGION_LOCAL(m_incoming_tx_lock); - struct result { bool res; cryptonote::transaction tx; crypto::hash hash; crypto::hash prefix_hash; bool in_txpool; bool in_blockchain; }; + struct result { bool res; cryptonote::transaction tx; crypto::hash hash; crypto::hash prefix_hash; }; std::vector results(tx_blobs.size()); tvc.resize(tx_blobs.size()); diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index 2eb6c842b89..a8bd688c391 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -242,13 +242,12 @@ namespace cryptonote * a miner instance with parameters given on the command line (or defaults) * * @param vm command line parameters - * @param config_subdir subdirectory for config storage * @param test_options configuration options for testing * @param get_checkpoints if set, will be called to get checkpoints data, must return checkpoints data pointer and size or nullptr if there ain't any checkpoints for specific network type * * @return false if one of the init steps fails, otherwise true */ - bool init(const boost::program_options::variables_map& vm, const char *config_subdir = NULL, const test_options *test_options = NULL, const GetCheckpointsCallback& get_checkpoints = nullptr); + bool init(const boost::program_options::variables_map& vm, const test_options *test_options = NULL, const GetCheckpointsCallback& get_checkpoints = nullptr); /** * @copydoc Blockchain::reset_and_set_genesis_block diff --git a/src/daemon/core.h b/src/daemon/core.h index d1defd573c6..c15d8d2363f 100644 --- a/src/daemon/core.h +++ b/src/daemon/core.h @@ -67,31 +67,16 @@ class t_core final m_core.set_cryptonote_protocol(&protocol); } - std::string get_config_subdir() const - { - bool testnet = command_line::get_arg(m_vm_HACK, cryptonote::arg_testnet_on); - bool stagenet = command_line::get_arg(m_vm_HACK, cryptonote::arg_stagenet_on); - bool mainnet = !testnet && !stagenet; - std::string port = command_line::get_arg(m_vm_HACK, nodetool::arg_p2p_bind_port); - if ((mainnet && port != std::to_string(::config::P2P_DEFAULT_PORT)) - || (testnet && port != std::to_string(::config::testnet::P2P_DEFAULT_PORT)) - || (stagenet && port != std::to_string(::config::stagenet::P2P_DEFAULT_PORT))) { - return port; - } - return std::string(); - } - bool run() { //initialize core here MGINFO("Initializing core..."); - std::string config_subdir = get_config_subdir(); #if defined(PER_BLOCK_CHECKPOINT) const cryptonote::GetCheckpointsCallback& get_checkpoints = blocks::GetCheckpointsData; #else const cryptonote::GetCheckpointsCallback& get_checkpoints = nullptr; #endif - if (!m_core.init(m_vm_HACK, config_subdir.empty() ? NULL : config_subdir.c_str(), nullptr, get_checkpoints)) + if (!m_core.init(m_vm_HACK, nullptr, get_checkpoints)) { return false; } diff --git a/src/device/device_ledger.cpp b/src/device/device_ledger.cpp index 0a86e6987ca..bfb41bbe4a4 100644 --- a/src/device/device_ledger.cpp +++ b/src/device/device_ledger.cpp @@ -789,8 +789,6 @@ namespace hw { } #ifdef DEBUG_HWDEVICE - bool recover_x = recover; - const crypto::secret_key recovery_key_x = recovery_key; crypto::public_key pub_x; crypto::secret_key sec_x; #endif diff --git a/src/rpc/daemon_messages.cpp b/src/rpc/daemon_messages.cpp index fa848cff46c..7c74420148b 100644 --- a/src/rpc/daemon_messages.cpp +++ b/src/rpc/daemon_messages.cpp @@ -177,8 +177,6 @@ rapidjson::Value GetTransactions::Request::toJson(rapidjson::Document& doc) cons { auto val = Message::toJson(doc); - auto& al = doc.GetAllocator(); - INSERT_INTO_JSON_OBJECT(val, doc, tx_hashes, tx_hashes); return val; @@ -193,8 +191,6 @@ rapidjson::Value GetTransactions::Response::toJson(rapidjson::Document& doc) con { rapidjson::Value val(rapidjson::kObjectType); - auto& al = doc.GetAllocator(); - INSERT_INTO_JSON_OBJECT(val, doc, txs, txs); INSERT_INTO_JSON_OBJECT(val, doc, missed_hashes, missed_hashes); @@ -212,8 +208,6 @@ rapidjson::Value KeyImagesSpent::Request::toJson(rapidjson::Document& doc) const { auto val = Message::toJson(doc); - auto& al = doc.GetAllocator(); - INSERT_INTO_JSON_OBJECT(val, doc, key_images, key_images); return val; @@ -228,8 +222,6 @@ rapidjson::Value KeyImagesSpent::Response::toJson(rapidjson::Document& doc) cons { auto val = Message::toJson(doc); - auto& al = doc.GetAllocator(); - INSERT_INTO_JSON_OBJECT(val, doc, spent_status, spent_status); return val; @@ -245,8 +237,6 @@ rapidjson::Value GetTxGlobalOutputIndices::Request::toJson(rapidjson::Document& { auto val = Message::toJson(doc); - auto& al = doc.GetAllocator(); - INSERT_INTO_JSON_OBJECT(val, doc, tx_hash, tx_hash); return val; @@ -261,8 +251,6 @@ rapidjson::Value GetTxGlobalOutputIndices::Response::toJson(rapidjson::Document& { auto val = Message::toJson(doc); - auto& al = doc.GetAllocator(); - INSERT_INTO_JSON_OBJECT(val, doc, output_indices, output_indices); return val; @@ -277,8 +265,6 @@ rapidjson::Value SendRawTx::Request::toJson(rapidjson::Document& doc) const { auto val = Message::toJson(doc); - auto& al = doc.GetAllocator(); - INSERT_INTO_JSON_OBJECT(val, doc, tx, tx); INSERT_INTO_JSON_OBJECT(val, doc, relay, relay); @@ -295,8 +281,6 @@ rapidjson::Value SendRawTx::Response::toJson(rapidjson::Document& doc) const { auto val = Message::toJson(doc); - auto& al = doc.GetAllocator(); - INSERT_INTO_JSON_OBJECT(val, doc, relayed, relayed); return val; @@ -312,8 +296,6 @@ rapidjson::Value StartMining::Request::toJson(rapidjson::Document& doc) const { auto val = Message::toJson(doc); - auto& al = doc.GetAllocator(); - INSERT_INTO_JSON_OBJECT(val, doc, miner_address, miner_address); INSERT_INTO_JSON_OBJECT(val, doc, threads_count, threads_count); INSERT_INTO_JSON_OBJECT(val, doc, do_background_mining, do_background_mining); @@ -372,8 +354,6 @@ rapidjson::Value MiningStatus::Response::toJson(rapidjson::Document& doc) const { auto val = Message::toJson(doc); - auto& al = doc.GetAllocator(); - INSERT_INTO_JSON_OBJECT(val, doc, active, active); INSERT_INTO_JSON_OBJECT(val, doc, speed, speed); INSERT_INTO_JSON_OBJECT(val, doc, threads_count, threads_count); @@ -406,8 +386,6 @@ rapidjson::Value GetInfo::Response::toJson(rapidjson::Document& doc) const { auto val = Message::toJson(doc); - auto& al = doc.GetAllocator(); - INSERT_INTO_JSON_OBJECT(val, doc, info, info); return val; @@ -423,8 +401,6 @@ rapidjson::Value SaveBC::Request::toJson(rapidjson::Document& doc) const { auto val = Message::toJson(doc); - auto& al = doc.GetAllocator(); - return val; } @@ -436,8 +412,6 @@ rapidjson::Value SaveBC::Response::toJson(rapidjson::Document& doc) const { auto val = Message::toJson(doc); - auto& al = doc.GetAllocator(); - return val; } @@ -450,8 +424,6 @@ rapidjson::Value GetBlockHash::Request::toJson(rapidjson::Document& doc) const { auto val = Message::toJson(doc); - auto& al = doc.GetAllocator(); - INSERT_INTO_JSON_OBJECT(val, doc, height, height); return val; @@ -466,8 +438,6 @@ rapidjson::Value GetBlockHash::Response::toJson(rapidjson::Document& doc) const { auto val = Message::toJson(doc); - auto& al = doc.GetAllocator(); - INSERT_INTO_JSON_OBJECT(val, doc, hash, hash); return val; @@ -483,8 +453,6 @@ rapidjson::Value GetLastBlockHeader::Request::toJson(rapidjson::Document& doc) c { auto val = Message::toJson(doc); - auto& al = doc.GetAllocator(); - return val; } @@ -496,8 +464,6 @@ rapidjson::Value GetLastBlockHeader::Response::toJson(rapidjson::Document& doc) { auto val = Message::toJson(doc); - auto& al = doc.GetAllocator(); - INSERT_INTO_JSON_OBJECT(val, doc, header, header); return val; @@ -513,8 +479,6 @@ rapidjson::Value GetBlockHeaderByHash::Request::toJson(rapidjson::Document& doc) { auto val = Message::toJson(doc); - auto& al = doc.GetAllocator(); - INSERT_INTO_JSON_OBJECT(val, doc, hash, hash); return val; @@ -529,8 +493,6 @@ rapidjson::Value GetBlockHeaderByHash::Response::toJson(rapidjson::Document& doc { auto val = Message::toJson(doc); - auto& al = doc.GetAllocator(); - INSERT_INTO_JSON_OBJECT(val, doc, header, header); return val; @@ -546,8 +508,6 @@ rapidjson::Value GetBlockHeaderByHeight::Request::toJson(rapidjson::Document& do { auto val = Message::toJson(doc); - auto& al = doc.GetAllocator(); - INSERT_INTO_JSON_OBJECT(val, doc, height, height); return val; @@ -562,8 +522,6 @@ rapidjson::Value GetBlockHeaderByHeight::Response::toJson(rapidjson::Document& d { auto val = Message::toJson(doc); - auto& al = doc.GetAllocator(); - INSERT_INTO_JSON_OBJECT(val, doc, header, header); return val; @@ -579,8 +537,6 @@ rapidjson::Value GetBlockHeadersByHeight::Request::toJson(rapidjson::Document& d { auto val = Message::toJson(doc); - auto& al = doc.GetAllocator(); - INSERT_INTO_JSON_OBJECT(val, doc, heights, heights); return val; @@ -595,8 +551,6 @@ rapidjson::Value GetBlockHeadersByHeight::Response::toJson(rapidjson::Document& { auto val = Message::toJson(doc); - auto& al = doc.GetAllocator(); - INSERT_INTO_JSON_OBJECT(val, doc, headers, headers); return val; @@ -612,8 +566,6 @@ rapidjson::Value GetPeerList::Request::toJson(rapidjson::Document& doc) const { auto val = Message::toJson(doc); - auto& al = doc.GetAllocator(); - return val; } @@ -625,8 +577,6 @@ rapidjson::Value GetPeerList::Response::toJson(rapidjson::Document& doc) const { auto val = Message::toJson(doc); - auto& al = doc.GetAllocator(); - INSERT_INTO_JSON_OBJECT(val, doc, white_list, white_list); INSERT_INTO_JSON_OBJECT(val, doc, gray_list, gray_list); @@ -679,8 +629,6 @@ rapidjson::Value GetTransactionPool::Response::toJson(rapidjson::Document& doc) { auto val = Message::toJson(doc); - auto& al = doc.GetAllocator(); - INSERT_INTO_JSON_OBJECT(val, doc, transactions, transactions); INSERT_INTO_JSON_OBJECT(val, doc, key_images, key_images); @@ -698,8 +646,6 @@ rapidjson::Value HardForkInfo::Request::toJson(rapidjson::Document& doc) const { auto val = Message::toJson(doc); - auto& al = doc.GetAllocator(); - INSERT_INTO_JSON_OBJECT(val, doc, version, version); return val; @@ -714,8 +660,6 @@ rapidjson::Value HardForkInfo::Response::toJson(rapidjson::Document& doc) const { auto val = Message::toJson(doc); - auto& al = doc.GetAllocator(); - INSERT_INTO_JSON_OBJECT(val, doc, info, info); return val; @@ -731,8 +675,6 @@ rapidjson::Value GetOutputHistogram::Request::toJson(rapidjson::Document& doc) c { auto val = Message::toJson(doc); - auto& al = doc.GetAllocator(); - INSERT_INTO_JSON_OBJECT(val, doc, amounts, amounts); INSERT_INTO_JSON_OBJECT(val, doc, min_count, min_count); INSERT_INTO_JSON_OBJECT(val, doc, max_count, max_count); @@ -755,8 +697,6 @@ rapidjson::Value GetOutputHistogram::Response::toJson(rapidjson::Document& doc) { auto val = Message::toJson(doc); - auto& al = doc.GetAllocator(); - INSERT_INTO_JSON_OBJECT(val, doc, histogram, histogram); return val; @@ -772,8 +712,6 @@ rapidjson::Value GetOutputKeys::Request::toJson(rapidjson::Document& doc) const { auto val = Message::toJson(doc); - auto& al = doc.GetAllocator(); - INSERT_INTO_JSON_OBJECT(val, doc, outputs, outputs); return val; @@ -788,8 +726,6 @@ rapidjson::Value GetOutputKeys::Response::toJson(rapidjson::Document& doc) const { auto val = Message::toJson(doc); - auto& al = doc.GetAllocator(); - INSERT_INTO_JSON_OBJECT(val, doc, keys, keys); return val; @@ -814,8 +750,6 @@ rapidjson::Value GetRPCVersion::Response::toJson(rapidjson::Document& doc) const { auto val = Message::toJson(doc); - auto& al = doc.GetAllocator(); - INSERT_INTO_JSON_OBJECT(val, doc, version, version); return val; @@ -830,8 +764,6 @@ rapidjson::Value GetFeeEstimate::Request::toJson(rapidjson::Document& doc) const { auto val = Message::toJson(doc); - auto& al = doc.GetAllocator(); - INSERT_INTO_JSON_OBJECT(val, doc, num_grace_blocks, num_grace_blocks); return val; @@ -846,8 +778,6 @@ rapidjson::Value GetFeeEstimate::Response::toJson(rapidjson::Document& doc) cons { auto val = Message::toJson(doc); - auto& al = doc.GetAllocator(); - INSERT_INTO_JSON_OBJECT(val, doc, estimated_base_fee, estimated_base_fee); INSERT_INTO_JSON_OBJECT(val, doc, fee_mask, fee_mask); INSERT_INTO_JSON_OBJECT(val, doc, size_scale, size_scale); @@ -867,7 +797,6 @@ void GetFeeEstimate::Response::fromJson(rapidjson::Value& val) rapidjson::Value GetOutputDistribution::Request::toJson(rapidjson::Document& doc) const { auto val = Message::toJson(doc); - auto& al = doc.GetAllocator(); INSERT_INTO_JSON_OBJECT(val, doc, amounts, amounts); INSERT_INTO_JSON_OBJECT(val, doc, from_height, from_height); @@ -888,7 +817,6 @@ void GetOutputDistribution::Request::fromJson(rapidjson::Value& val) rapidjson::Value GetOutputDistribution::Response::toJson(rapidjson::Document& doc) const { auto val = Message::toJson(doc); - auto& al = doc.GetAllocator(); INSERT_INTO_JSON_OBJECT(val, doc, status, status); INSERT_INTO_JSON_OBJECT(val, doc, distributions, distributions); diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index d9fd0c13e3f..1816509186d 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -6733,7 +6733,6 @@ static std::string get_human_readable_timestamp(uint64_t ts) gmtime_r(&tt, &tm); #endif uint64_t now = time(NULL); - uint64_t diff = ts > now ? ts - now : now - ts; strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", &tm); return std::string(buffer); } diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 0db110c00f8..54045db5fb8 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -1356,8 +1356,6 @@ void wallet2::scan_output(const cryptonote::transaction &tx, const crypto::publi //---------------------------------------------------------------------------------------------------- void wallet2::cache_tx_data(const cryptonote::transaction& tx, const crypto::hash &txid, tx_cache_data &tx_cache_data) const { - const cryptonote::account_keys& keys = m_account.get_keys(); - if(!parse_tx_extra(tx.extra, tx_cache_data.tx_extra_fields)) { // Extra may only be partially parsed, it's OK if tx_extra_fields contains public key @@ -8105,7 +8103,6 @@ void wallet2::light_wallet_get_address_txs() // for balance calculation uint64_t wallet_total_sent = 0; - uint64_t wallet_total_unlocked_sent = 0; // txs in pool std::vector pool_txs; @@ -11327,7 +11324,6 @@ cryptonote::blobdata wallet2::export_multisig() for (size_t n = 0; n < m_transfers.size(); ++n) { transfer_details &td = m_transfers[n]; - const std::vector additional_tx_pub_keys = get_additional_tx_pub_keys_from_extra(td.m_tx); crypto::key_image ki; td.m_multisig_k.clear(); info[n].m_LR.clear(); @@ -11356,7 +11352,6 @@ cryptonote::blobdata wallet2::export_multisig() boost::archive::portable_binary_oarchive ar(oss); ar << info; - std::string magic(MULTISIG_EXPORT_FILE_MAGIC, strlen(MULTISIG_EXPORT_FILE_MAGIC)); const cryptonote::account_public_address &keys = get_account().get_keys().m_account_address; std::string header; header += std::string((const char *)&keys.m_spend_public_key, sizeof(crypto::public_key)); diff --git a/tests/core_tests/chaingen.h b/tests/core_tests/chaingen.h index 6b9277a307f..b380aca01f3 100644 --- a/tests/core_tests/chaingen.h +++ b/tests/core_tests/chaingen.h @@ -511,7 +511,7 @@ inline bool do_replay_events(std::vector& events) // FIXME: make sure that vm has arg_testnet_on set to true or false if // this test needs for it to be so. get_test_options gto; - if (!c.init(vm, NULL, >o.test_options)) + if (!c.init(vm, >o.test_options)) { MERROR("Failed to init core"); return false; diff --git a/tests/core_tests/transaction_tests.cpp b/tests/core_tests/transaction_tests.cpp index 3c6954bc68a..810dec6fcef 100644 --- a/tests/core_tests/transaction_tests.cpp +++ b/tests/core_tests/transaction_tests.cpp @@ -54,9 +54,6 @@ bool test_transaction_generation_and_ring_signature() account_base miner_acc6; miner_acc6.generate(); - std::string add_str = miner_acc3.get_public_address_str(MAINNET); - - account_base rv_acc; rv_acc.generate(); account_base rv_acc2; diff --git a/tests/unit_tests/mnemonics.cpp b/tests/unit_tests/mnemonics.cpp index 0b74a6b94aa..4dc2d931eb9 100644 --- a/tests/unit_tests/mnemonics.cpp +++ b/tests/unit_tests/mnemonics.cpp @@ -75,7 +75,6 @@ namespace */ void test_language(const Language::Base &language) { - const std::vector &word_list = language.get_word_list(); epee::wipeable_string w_seed = "", w_return_seed = ""; std::string seed, return_seed; // Generate a random seed without checksum diff --git a/tests/unit_tests/multisig.cpp b/tests/unit_tests/multisig.cpp index 7268f2690dd..eb319686398 100644 --- a/tests/unit_tests/multisig.cpp +++ b/tests/unit_tests/multisig.cpp @@ -112,7 +112,7 @@ static void make_wallets(std::vector& wallets, unsigned int M) } for (auto& wallet: wallets) { - ASSERT_FALSE(wallet.multisig() || wallet.multisig() || wallet.multisig()); + ASSERT_FALSE(wallet.multisig()); } std::vector mxis; diff --git a/tests/unit_tests/ringct.cpp b/tests/unit_tests/ringct.cpp index 52bdb00cf68..ccef5f3e7e7 100644 --- a/tests/unit_tests/ringct.cpp +++ b/tests/unit_tests/ringct.cpp @@ -968,8 +968,6 @@ static rctSig make_sig() #define TEST_rctSig_elements(name, op) \ TEST(ringct, rctSig_##name) \ { \ - const uint64_t inputs[] = {1000, 1000}; \ - const uint64_t outputs[] = {1000, 1000}; \ rct::rctSig sig = make_sig(); \ ASSERT_TRUE(rct::verRct(sig)); \ op; \ From 5d9915ab9e18deed74d44676897295813d71f553 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 23 Nov 2018 13:51:58 +0000 Subject: [PATCH 0791/1404] cryptonote: fix get_unit for non default settings Found by codacy.com --- src/cryptonote_basic/cryptonote_format_utils.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cryptonote_basic/cryptonote_format_utils.cpp b/src/cryptonote_basic/cryptonote_format_utils.cpp index e26aac76b17..c2e6cbe27ba 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.cpp +++ b/src/cryptonote_basic/cryptonote_format_utils.cpp @@ -883,7 +883,7 @@ namespace cryptonote { if (decimal_point == (unsigned int)-1) decimal_point = default_decimal_point; - switch (std::atomic_load(&default_decimal_point)) + switch (decimal_point) { case 12: return "monero"; @@ -896,7 +896,7 @@ namespace cryptonote case 0: return "piconero"; default: - ASSERT_MES_AND_THROW("Invalid decimal point specification: " << default_decimal_point); + ASSERT_MES_AND_THROW("Invalid decimal point specification: " << decimal_point); } } //--------------------------------------------------------------- From 0dac3c6428a739771795496bb74066b154929ab2 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 23 Nov 2018 14:08:28 +0000 Subject: [PATCH 0792/1404] unit_tests: do not rethrow a copy of an exception Found by codacy.com --- tests/unit_tests/subaddress.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit_tests/subaddress.cpp b/tests/unit_tests/subaddress.cpp index 8ff4c5f0d1b..67802d736b2 100644 --- a/tests/unit_tests/subaddress.cpp +++ b/tests/unit_tests/subaddress.cpp @@ -49,7 +49,7 @@ class WalletSubaddress : public ::testing::Test catch (const std::exception& e) { LOG_ERROR("failed to generate wallet: " << e.what()); - throw e; + throw; } w1.add_subaddress_account(test_label); From 1a0733e534dee0dae140e38a789c3ab5008e7844 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 23 Nov 2018 14:14:54 +0000 Subject: [PATCH 0793/1404] windows_service: fix memory leak Found by codacy.com --- src/daemonizer/windows_service.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/daemonizer/windows_service.cpp b/src/daemonizer/windows_service.cpp index b344e1a4b19..1302fa57804 100644 --- a/src/daemonizer/windows_service.cpp +++ b/src/daemonizer/windows_service.cpp @@ -70,8 +70,9 @@ namespace { } else { - return std::string{p_error_text}; + std::string ret{p_error_text}; LocalFree(p_error_text); + return ret; } } From aee7a4e3642e384c05d9721deb449b39f91857eb Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 23 Nov 2018 14:20:08 +0000 Subject: [PATCH 0794/1404] wallet_rpc_server: do not use RPC data if the call failed Found by codacy.com --- src/wallet/wallet_rpc_server.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index 0eb09b9f1fd..d7dc2914e5a 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -2891,7 +2891,8 @@ namespace tools cryptonote::COMMAND_RPC_GET_HEIGHT::response hres; hres.height = 0; bool r = wal->invoke_http_json("/getheight", hreq, hres); - wal->set_refresh_from_block_height(hres.height); + if (r) + wal->set_refresh_from_block_height(hres.height); crypto::secret_key dummy_key; try { wal->generate(wallet_file, req.password, dummy_key, false, false); From 3de7d52f7cdbf8cab4f9b4b1acd99dd989819917 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 23 Nov 2018 15:55:14 +0000 Subject: [PATCH 0795/1404] unit_tests: fix malloc/delete mismatch --- tests/unit_tests/notify.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit_tests/notify.cpp b/tests/unit_tests/notify.cpp index edc4eabdf0b..105d21ca8c8 100644 --- a/tests/unit_tests/notify.cpp +++ b/tests/unit_tests/notify.cpp @@ -49,7 +49,7 @@ TEST(notify, works) tmp = "/tmp"; static const char *filename = "monero-notify-unit-test-XXXXXX"; const size_t len = strlen(tmp) + 1 + strlen(filename); - std::unique_ptr name_template_((char*)malloc(len + 1)); + std::unique_ptr name_template_(new char[len + 1]); char *name_template = name_template_.get(); ASSERT_TRUE(name_template != NULL); snprintf(name_template, len + 1, "%s/%s", tmp, filename); From 9b5efad29474416427cfb9b6a696b5dcb57cc919 Mon Sep 17 00:00:00 2001 From: stoffu Date: Sat, 24 Nov 2018 08:25:38 +0900 Subject: [PATCH 0796/1404] simplewallet: enable donation on testnet/stagenet for easier testing --- src/simplewallet/simplewallet.cpp | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index d9fd0c13e3f..6b7681893b3 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -5942,12 +5942,6 @@ bool simple_wallet::sweep_below(const std::vector &args_) //---------------------------------------------------------------------------------------------------- bool simple_wallet::donate(const std::vector &args_) { - if(m_wallet->nettype() != cryptonote::MAINNET) - { - fail_msg_writer() << tr("donations are not enabled on the testnet or on the stagenet"); - return true; - } - std::vector local_args = args_; if(local_args.empty() || local_args.size() > 5) { @@ -5969,11 +5963,30 @@ bool simple_wallet::donate(const std::vector &args_) amount_str = local_args.back(); local_args.pop_back(); // push back address, amount, payment id - local_args.push_back(MONERO_DONATION_ADDR); + std::string address_str; + if (m_wallet->nettype() != cryptonote::MAINNET) + { + // if not mainnet, convert donation address string to the relevant network type + address_parse_info info; + if (!cryptonote::get_account_address_from_str(info, cryptonote::MAINNET, MONERO_DONATION_ADDR)) + { + fail_msg_writer() << tr("Failed to parse donation address: ") << MONERO_DONATION_ADDR; + return true; + } + address_str = cryptonote::get_account_address_as_str(m_wallet->nettype(), info.is_subaddress, info.address); + } + else + { + address_str = MONERO_DONATION_ADDR; + } + local_args.push_back(address_str); local_args.push_back(amount_str); if (!payment_id_str.empty()) local_args.push_back(payment_id_str); - message_writer() << (boost::format(tr("Donating %s %s to The Monero Project (donate.getmonero.org or %s).")) % amount_str % cryptonote::get_unit(cryptonote::get_default_decimal_point()) % MONERO_DONATION_ADDR).str(); + if (m_wallet->nettype() == cryptonote::MAINNET) + message_writer() << (boost::format(tr("Donating %s %s to The Monero Project (donate.getmonero.org or %s).")) % amount_str % cryptonote::get_unit(cryptonote::get_default_decimal_point()) % MONERO_DONATION_ADDR).str(); + else + message_writer() << (boost::format(tr("Donating %s %s to %s.")) % amount_str % cryptonote::get_unit(cryptonote::get_default_decimal_point()) % address_str).str(); transfer(local_args); return true; } From 2be31b4c9ceb4fde1a3968ab0ae2fa4d33facc5d Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 24 Nov 2018 10:52:20 +0000 Subject: [PATCH 0797/1404] blockchain_blackball: spot when all outputs of an amount are spent --- .../blockchain_blackball.cpp | 96 ++++++++++++++++++- 1 file changed, 95 insertions(+), 1 deletion(-) diff --git a/src/blockchain_utilities/blockchain_blackball.cpp b/src/blockchain_utilities/blockchain_blackball.cpp index d2ce5cf7672..73819bd2537 100644 --- a/src/blockchain_utilities/blockchain_blackball.cpp +++ b/src/blockchain_utilities/blockchain_blackball.cpp @@ -59,6 +59,7 @@ static MDB_dbi dbi_relative_rings; static MDB_dbi dbi_outputs; static MDB_dbi dbi_processed_txidx; static MDB_dbi dbi_spent; +static MDB_dbi dbi_per_amount; static MDB_dbi dbi_ring_instances; static MDB_dbi dbi_stats; static MDB_env *env = NULL; @@ -238,7 +239,7 @@ static void init(std::string cache_filename) dbr = mdb_env_create(&env); CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to create LDMB environment: " + std::string(mdb_strerror(dbr))); - dbr = mdb_env_set_maxdbs(env, 6); + dbr = mdb_env_set_maxdbs(env, 7); CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to set max env dbs: " + std::string(mdb_strerror(dbr))); const std::string actual_filename = get_cache_filename(cache_filename); dbr = mdb_env_open(env, actual_filename.c_str(), flags, 0664); @@ -265,6 +266,10 @@ static void init(std::string cache_filename) CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to open LMDB dbi: " + std::string(mdb_strerror(dbr))); mdb_set_dupsort(txn, dbi_spent, compare_uint64); + dbr = mdb_dbi_open(txn, "per_amount", MDB_CREATE | MDB_INTEGERKEY, &dbi_per_amount); + CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to open LMDB dbi: " + std::string(mdb_strerror(dbr))); + mdb_set_compare(txn, dbi_per_amount, compare_uint64); + dbr = mdb_dbi_open(txn, "ring_instances", MDB_CREATE, &dbi_ring_instances); CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to open LMDB dbi: " + std::string(mdb_strerror(dbr))); @@ -283,6 +288,7 @@ static void close() mdb_dbi_close(env, dbi_relative_rings); mdb_dbi_close(env, dbi_outputs); mdb_dbi_close(env, dbi_processed_txidx); + mdb_dbi_close(env, dbi_per_amount); mdb_dbi_close(env, dbi_spent); mdb_dbi_close(env, dbi_ring_instances); mdb_dbi_close(env, dbi_stats); @@ -585,6 +591,55 @@ static std::vector get_spent_outputs(MDB_txn *txn) return outs; } +static void get_per_amount_outputs(MDB_txn *txn, uint64_t amount, uint64_t &total, uint64_t &spent) +{ + MDB_cursor *cur; + int dbr = mdb_cursor_open(txn, dbi_per_amount, &cur); + CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to open cursor for per amount outputs: " + std::string(mdb_strerror(dbr))); + MDB_val k, v; + mdb_size_t count = 0; + k.mv_size = sizeof(uint64_t); + k.mv_data = (void*)&amount; + dbr = mdb_cursor_get(cur, &k, &v, MDB_SET); + if (dbr == MDB_NOTFOUND) + { + total = spent = 0; + } + else + { + CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to get per amount outputs: " + std::string(mdb_strerror(dbr))); + total = ((const uint64_t*)v.mv_data)[0]; + spent = ((const uint64_t*)v.mv_data)[1]; + } + mdb_cursor_close(cur); +} + +static void inc_per_amount_outputs(MDB_txn *txn, uint64_t amount, uint64_t total, uint64_t spent) +{ + MDB_cursor *cur; + int dbr = mdb_cursor_open(txn, dbi_per_amount, &cur); + CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to open cursor for per amount outputs: " + std::string(mdb_strerror(dbr))); + MDB_val k, v; + mdb_size_t count = 0; + k.mv_size = sizeof(uint64_t); + k.mv_data = (void*)&amount; + dbr = mdb_cursor_get(cur, &k, &v, MDB_SET); + if (dbr == 0) + { + total += ((const uint64_t*)v.mv_data)[0]; + spent += ((const uint64_t*)v.mv_data)[1]; + } + else + { + CHECK_AND_ASSERT_THROW_MES(dbr == MDB_NOTFOUND, "Failed to get per amount outputs: " + std::string(mdb_strerror(dbr))); + } + uint64_t data[2] = {total, spent}; + v.mv_size = 2 * sizeof(uint64_t); + v.mv_data = (void*)data; + dbr = mdb_cursor_put(cur, &k, &v, 0); + mdb_cursor_close(cur); +} + static uint64_t get_processed_txidx(const std::string &name) { MDB_txn *txn; @@ -1193,6 +1248,7 @@ int main(int argc, char* argv[]) for_all_transactions(filename, start_idx, n_txes, [&](const cryptonote::transaction_prefix &tx)->bool { std::cout << "\r" << start_idx << "/" << n_txes << " \r" << std::flush; + const bool miner_tx = tx.vin.size() == 1 && tx.vin[0].type() == typeid(txin_gen); for (const auto &in: tx.vin) { if (in.type() != typeid(txin_to_key)) @@ -1210,6 +1266,9 @@ int main(int argc, char* argv[]) std::vector new_ring = canonicalize(txin.key_offsets); const uint32_t ring_size = txin.key_offsets.size(); const uint64_t instances = inc_ring_instances(txn, txin.amount, new_ring); + uint64_t pa_total = 0, pa_spent = 0; + if (!opt_rct_only) + get_per_amount_outputs(txn, txin.amount, pa_total, pa_spent); if (n == 0 && ring_size == 1) { const std::pair output = std::make_pair(txin.amount, absolute[0]); @@ -1237,6 +1296,21 @@ int main(int argc, char* argv[]) inc_stat(txn, txin.amount ? "pre-rct-duplicate-rings" : "rct-duplicate-rings"); } } + else if (n == 0 && !opt_rct_only && pa_spent + 1 == pa_total) + { + for (size_t o = 0; o < pa_total; ++o) + { + const std::pair output = std::make_pair(txin.amount, o); + if (opt_verbose) + { + MINFO("Marking output " << output.first << "/" << output.second << " as spent, due to as many outputs of that amount being spent as exist so far"); + std::cout << "\r" << start_idx << "/" << n_txes << " \r" << std::flush; + } + blackballs.push_back(output); + if (add_spent_output(cur, output_data(txin.amount, o))) + inc_stat(txn, txin.amount ? "pre-rct-full-count" : "rct-full-count"); + } + } else if (n == 0 && opt_check_subsets && get_ring_subset_instances(txn, txin.amount, new_ring) >= new_ring.size()) { for (size_t o = 0; o < new_ring.size(); ++o) @@ -1299,9 +1373,28 @@ int main(int argc, char* argv[]) } } if (n == 0) + { set_relative_ring(txn, txin.k_image, new_ring); + if (!opt_rct_only) + inc_per_amount_outputs(txn, txin.amount, 0, 1); + } } set_processed_txidx(txn, canonical, start_idx+1); + if (!opt_rct_only) + { + for (const auto &out: tx.vout) + { + uint64_t amount = out.amount; + if (miner_tx && tx.version >= 2) + amount = 0; + + if (opt_rct_only && amount != 0) + continue; + if (out.target.type() != typeid(txout_to_key)) + continue; + inc_per_amount_outputs(txn, amount, 1, 0); + } + } ++records; if (records >= records_per_sync) @@ -1433,6 +1526,7 @@ int main(int argc, char* argv[]) { "pre-rct-ring-size-1", pre_rct }, { "rct-ring-size-1", rct }, { "pre-rct-duplicate-rings", pre_rct }, { "rct-duplicate-rings", rct }, { "pre-rct-subset-rings", pre_rct }, { "rct-subset-rings", rct }, + { "pre-rct-full-count", pre_rct }, { "rct-full-count", rct }, { "pre-rct-key-image-attack", pre_rct }, { "rct-key-image-attack", rct }, { "pre-rct-extra", pre_rct }, { "rct-ring-extra", rct }, { "pre-rct-chain-reaction", pre_rct }, { "rct-chain-reaction", rct }, From 2ffe53d9e668c1993fc6f1cbcd7d74c895a5fbb7 Mon Sep 17 00:00:00 2001 From: Dusan Klinec Date: Mon, 5 Nov 2018 00:38:58 +0100 Subject: [PATCH 0798/1404] device/trezor: webusb transport added, cmake fixes - webusb transport based on libusb added. Provides direct access to Trezor via USB, no need for Trezor bridge. - trezor protocol message handler improved, no recursion used. Ready for upcoming integration tests. - libusb (for docker) bumped from v1.0.9 to v1.0.22, newer version required for webusb transport, for device enumeration. - cmake improvements and fixes. Cmake Trezor checks are moved to a dedicated CheckTrezor.cmake file. In case of a problem Trezor is excluded from build. - ifdefs made consistent to Ledger. - UDP Transport enumeration disabled by default in release mode --- CMakeLists.txt | 13 +- cmake/CheckTrezor.cmake | 79 +++++ cmake/FindLibUSB.cmake | 140 ++++++++ cmake/test-libusb-version.c | 52 +++ contrib/depends/packages/libusb.mk | 8 +- src/device_trezor/CMakeLists.txt | 42 +-- src/device_trezor/device_trezor.cpp | 3 +- src/device_trezor/device_trezor.hpp | 2 +- src/device_trezor/device_trezor_base.cpp | 127 ++++--- src/device_trezor/device_trezor_base.hpp | 112 +++---- src/device_trezor/trezor.hpp | 2 +- src/device_trezor/trezor/tools/README.md | 25 +- src/device_trezor/trezor/tools/pb2cpp.py | 61 +++- src/device_trezor/trezor/transport.cpp | 403 ++++++++++++++++++++++- src/device_trezor/trezor/transport.hpp | 67 ++++ src/device_trezor/trezor/trezor_defs.hpp | 39 ++- 16 files changed, 970 insertions(+), 205 deletions(-) create mode 100644 cmake/CheckTrezor.cmake create mode 100644 cmake/FindLibUSB.cmake create mode 100644 cmake/test-libusb-version.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 0c38c673abf..d942d31cc57 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -513,15 +513,8 @@ else (HIDAPI_FOUND) message(STATUS "Could not find HIDAPI") endif() -# Protobuf, optional. Required for TREZOR. -include(FindProtobuf) -find_package(Protobuf) -if(Protobuf_FOUND) - set(HAVE_PROTOBUF 1) - add_definitions(-DHAVE_PROTOBUF=1) -else(Protobuf_FOUND) - message(STATUS "Could not find Protobuf") -endif() +# Trezor support check +include(CheckTrezor) if(MSVC) add_definitions("/bigobj /MP /W3 /GS- /D_CRT_SECURE_NO_WARNINGS /wd4996 /wd4345 /D_WIN32_WINNT=0x0600 /DWIN32_LEAN_AND_MEAN /DGTEST_HAS_TR1_TUPLE=0 /FIinline_c.h /D__SSE4_1__") @@ -921,7 +914,7 @@ endif() list(APPEND EXTRA_LIBRARIES ${CMAKE_DL_LIBS}) -if (HIDAPI_FOUND) +if (HIDAPI_FOUND OR LibUSB_COMPILE_TEST_PASSED) if (APPLE) if(DEPENDS) list(APPEND EXTRA_LIBRARIES "-framework Foundation -framework IOKit") diff --git a/cmake/CheckTrezor.cmake b/cmake/CheckTrezor.cmake new file mode 100644 index 00000000000..ea21237fdd2 --- /dev/null +++ b/cmake/CheckTrezor.cmake @@ -0,0 +1,79 @@ +OPTION(USE_DEVICE_TREZOR "Trezor support compilation" ON) +OPTION(USE_DEVICE_TREZOR_LIBUSB "Trezor LibUSB compilation" ON) +OPTION(USE_DEVICE_TREZOR_UDP_RELEASE "Trezor UdpTransport in release mode" OFF) + +# Use Trezor master switch +if (USE_DEVICE_TREZOR) + # Protobuf is required to build protobuf messages for Trezor + include(FindProtobuf OPTIONAL) + find_package(Protobuf) + if(NOT Protobuf_FOUND) + message(STATUS "Could not find Protobuf") + endif() + +else() + message(STATUS "Trezor support disabled by USE_DEVICE_TREZOR") +endif() + +if(Protobuf_FOUND AND USE_DEVICE_TREZOR) + if (NOT "$ENV{TREZOR_PYTHON}" STREQUAL "") + set(TREZOR_PYTHON "$ENV{TREZOR_PYTHON}" CACHE INTERNAL "Copied from environment variable TREZOR_PYTHON") + else() + find_package(Python QUIET COMPONENTS Interpreter) # cmake 3.12+ + if(Python_Interpreter_FOUND) + set(TREZOR_PYTHON "${Python_EXECUTABLE}") + endif() + endif() + + if(NOT TREZOR_PYTHON) + find_package(PythonInterp) + if(PYTHONINTERP_FOUND AND PYTHON_EXECUTABLE) + set(TREZOR_PYTHON "${PYTHON_EXECUTABLE}") + endif() + endif() + + if(NOT TREZOR_PYTHON) + message(STATUS "Trezor: Python not found") + endif() +endif() + +# Try to build protobuf messages +if(Protobuf_FOUND AND USE_DEVICE_TREZOR AND TREZOR_PYTHON) + set(ENV{PROTOBUF_INCLUDE_DIRS} "${Protobuf_INCLUDE_DIRS}") + set(ENV{PROTOBUF_PROTOC_EXECUTABLE} "${Protobuf_PROTOC_EXECUTABLE}") + execute_process(COMMAND ${TREZOR_PYTHON} tools/build_protob.py WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/../src/device_trezor/trezor RESULT_VARIABLE RET OUTPUT_VARIABLE OUT ERROR_VARIABLE ERR) + if(RET) + message(WARNING "Trezor protobuf messages could not be regenerated (err=${RET}, python ${PYTHON})." + "OUT: ${OUT}, ERR: ${ERR}." + "Please read src/device_trezor/trezor/tools/README.md") + else() + message(STATUS "Trezor protobuf messages regenerated ${OUT}") + set(DEVICE_TREZOR_READY 1) + add_definitions(-DDEVICE_TREZOR_READY=1) + + if(CMAKE_BUILD_TYPE STREQUAL "Debug") + add_definitions(-DTREZOR_DEBUG=1) + endif() + + if(USE_DEVICE_TREZOR_UDP_RELEASE) + add_definitions(-DWITH_DEVICE_TREZOR_UDP_RELEASE=1) + endif() + + if (Protobuf_INCLUDE_DIR) + include_directories(${Protobuf_INCLUDE_DIR}) + endif() + + # LibUSB support, check for particular version + # Include support only if compilation test passes + if (USE_DEVICE_TREZOR_LIBUSB) + find_package(LibUSB) + endif() + + if (LibUSB_COMPILE_TEST_PASSED) + add_definitions(-DHAVE_TREZOR_LIBUSB=1) + if(LibUSB_INCLUDE_DIRS) + include_directories(${LibUSB_INCLUDE_DIRS}) + endif() + endif() + endif() +endif() diff --git a/cmake/FindLibUSB.cmake b/cmake/FindLibUSB.cmake new file mode 100644 index 00000000000..7e3bf156efa --- /dev/null +++ b/cmake/FindLibUSB.cmake @@ -0,0 +1,140 @@ +# - Find libusb for portable USB support +# This module will find libusb as published by +# http://libusb.sf.net and +# http://libusb-win32.sf.net +# +# It will use PkgConfig if present and supported, else search +# it on its own. If the LibUSB_ROOT_DIR environment variable +# is defined, it will be used as base path. +# The following standard variables get defined: +# LibUSB_FOUND: true if LibUSB was found +# LibUSB_HEADER_FILE: the location of the C header file +# LibUSB_INCLUDE_DIRS: the directory that contains the include file +# LibUSB_LIBRARIES: the library +# source: https://github.com/IntelRealSense/librealsense + +include ( CheckLibraryExists ) +include ( CheckIncludeFile ) + +find_package ( PkgConfig ) +if ( PKG_CONFIG_FOUND ) + pkg_check_modules ( PKGCONFIG_LIBUSB libusb-1.0 ) + if ( NOT PKGCONFIG_LIBUSB_FOUND ) + pkg_check_modules ( PKGCONFIG_LIBUSB libusb ) + endif ( NOT PKGCONFIG_LIBUSB_FOUND ) +endif ( PKG_CONFIG_FOUND ) + +if ( PKGCONFIG_LIBUSB_FOUND ) + set ( LibUSB_INCLUDE_DIRS ${PKGCONFIG_LIBUSB_INCLUDE_DIRS} ) + foreach ( i ${PKGCONFIG_LIBUSB_LIBRARIES} ) + string ( REGEX MATCH "[^-]*" ibase "${i}" ) + find_library ( ${ibase}_LIBRARY + NAMES ${i} + PATHS ${PKGCONFIG_LIBUSB_LIBRARY_DIRS} + ) + if ( ${ibase}_LIBRARY ) + list ( APPEND LibUSB_LIBRARIES ${${ibase}_LIBRARY} ) + endif ( ${ibase}_LIBRARY ) + mark_as_advanced ( ${ibase}_LIBRARY ) + endforeach ( i ) + +else ( PKGCONFIG_LIBUSB_FOUND ) + find_file ( LibUSB_HEADER_FILE + NAMES + libusb.h usb.h + PATHS + $ENV{ProgramFiles}/LibUSB-Win32 + $ENV{LibUSB_ROOT_DIR} + PATH_SUFFIXES + include + libusb-1.0 + include/libusb-1.0 + ) + mark_as_advanced ( LibUSB_HEADER_FILE ) + get_filename_component ( LibUSB_INCLUDE_DIRS "${LibUSB_HEADER_FILE}" PATH ) + + if ( ${CMAKE_SYSTEM_NAME} STREQUAL "Windows" ) + # LibUSB-Win32 binary distribution contains several libs. + # Use the lib that got compiled with the same compiler. + if ( MSVC ) + if ( WIN32 ) + set ( LibUSB_LIBRARY_PATH_SUFFIX lib/msvc ) + else ( WIN32 ) + set ( LibUSB_LIBRARY_PATH_SUFFIX lib/msvc_x64 ) + endif ( WIN32 ) + elseif ( BORLAND ) + set ( LibUSB_LIBRARY_PATH_SUFFIX lib/bcc ) + elseif ( CMAKE_COMPILER_IS_GNUCC ) + set ( LibUSB_LIBRARY_PATH_SUFFIX lib/gcc ) + endif ( MSVC ) + endif ( ${CMAKE_SYSTEM_NAME} STREQUAL "Windows" ) + + find_library ( usb_LIBRARY + NAMES + usb-1.0 libusb usb + PATHS + $ENV{ProgramFiles}/LibUSB-Win32 + $ENV{LibUSB_ROOT_DIR} + PATH_SUFFIXES + ${LibUSB_LIBRARY_PATH_SUFFIX} + ) + mark_as_advanced ( usb_LIBRARY ) + if ( usb_LIBRARY ) + set ( LibUSB_LIBRARIES ${usb_LIBRARY} ) + endif ( usb_LIBRARY ) + +endif ( PKGCONFIG_LIBUSB_FOUND ) + +if ( LibUSB_INCLUDE_DIRS AND LibUSB_LIBRARIES ) + set ( LibUSB_FOUND true ) +endif ( LibUSB_INCLUDE_DIRS AND LibUSB_LIBRARIES ) + +if ( LibUSB_FOUND ) + set ( CMAKE_REQUIRED_INCLUDES "${LibUSB_INCLUDE_DIRS}" ) + check_include_file ( "${LibUSB_HEADER_FILE}" LibUSB_FOUND ) +endif ( LibUSB_FOUND ) + +if ( LibUSB_FOUND ) + check_library_exists ( "${LibUSB_LIBRARIES}" usb_open "" LibUSB_FOUND ) + check_library_exists ( "${LibUSB_LIBRARIES}" libusb_get_device_list "" LibUSB_VERSION_1.0 ) + check_library_exists ( "${LibUSB_LIBRARIES}" libusb_get_port_numbers "" LibUSB_VERSION_1.0.16 ) + + # Library 1.0.16+ compilation test. + # The check_library_exists does not work well on Apple with shared libs. + if (APPLE OR LibUSB_VERSION_1.0.16) + if (APPLE) + if(DEPENDS) + list(APPEND TEST_COMPILE_EXTRA_LIBRARIES "-framework Foundation -framework IOKit") + else() + find_library(COREFOUNDATION CoreFoundation) + find_library(IOKIT IOKit) + list(APPEND TEST_COMPILE_EXTRA_LIBRARIES ${IOKIT}) + list(APPEND TEST_COMPILE_EXTRA_LIBRARIES ${COREFOUNDATION}) + endif() + endif() + if (WIN32) + list(APPEND TEST_COMPILE_EXTRA_LIBRARIES setupapi) + endif() + list(APPEND TEST_COMPILE_EXTRA_LIBRARIES ${LibUSB_LIBRARIES}) + + try_compile(LibUSB_COMPILE_TEST_PASSED + ${CMAKE_BINARY_DIR} + "${CMAKE_SOURCE_DIR}/cmake/test-libusb-version.c" + CMAKE_FLAGS + "-DINCLUDE_DIRECTORIES=${LibUSB_INCLUDE_DIRS}" + "-DLINK_DIRECTORIES=${LibUSB_LIBRARIES}" + LINK_LIBRARIES ${TEST_COMPILE_EXTRA_LIBRARIES} + OUTPUT_VARIABLE OUTPUT) + unset(TEST_COMPILE_EXTRA_LIBRARIES) + message(STATUS "LibUSB Compilation test: ${LibUSB_COMPILE_TEST_PASSED}") + endif() +endif ( LibUSB_FOUND ) + +if ( NOT LibUSB_FOUND ) + if ( NOT LibUSB_FIND_QUIETLY ) + message ( STATUS "LibUSB not found, try setting LibUSB_ROOT_DIR environment variable." ) + endif ( NOT LibUSB_FIND_QUIETLY ) + if ( LibUSB_FIND_REQUIRED ) + message ( FATAL_ERROR "" ) + endif ( LibUSB_FIND_REQUIRED ) +endif ( NOT LibUSB_FOUND ) diff --git a/cmake/test-libusb-version.c b/cmake/test-libusb-version.c new file mode 100644 index 00000000000..309e4ad2721 --- /dev/null +++ b/cmake/test-libusb-version.c @@ -0,0 +1,52 @@ +// Copyright (c) 2014-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include + +#define UNUSED(expr) (void)(expr) + +int main(int argc, char *argv[]) { + libusb_device **devs; + libusb_context *ctx = NULL; + + int r = libusb_init(&ctx); UNUSED(r); + ssize_t cnt = libusb_get_device_list(ctx, &devs); UNUSED(cnt); + + struct libusb_device_descriptor desc; + r = libusb_get_device_descriptor(devs[0], &desc); UNUSED(r); + uint8_t bus_id = libusb_get_bus_number(devs[0]); UNUSED(bus_id); + uint8_t addr = libusb_get_device_address(devs[0]); UNUSED(addr); + + uint8_t tmp_path[16]; + r = libusb_get_port_numbers(devs[0], tmp_path, sizeof(tmp_path)); + UNUSED(r); + UNUSED(tmp_path); + + libusb_free_device_list(devs, 1); + libusb_exit(ctx); +} diff --git a/contrib/depends/packages/libusb.mk b/contrib/depends/packages/libusb.mk index 47f8b3cbc59..e9663ace0bd 100644 --- a/contrib/depends/packages/libusb.mk +++ b/contrib/depends/packages/libusb.mk @@ -1,8 +1,8 @@ package=libusb -$(package)_version=1.0.9 -$(package)_download_path=http://sourceforge.net/projects/libusb/files/libusb-1.0/libusb-1.0.9/ +$(package)_version=1.0.22 +$(package)_download_path=http://sourceforge.net/projects/libusb/files/libusb-1.0/libusb-$($(package)_version)/ $(package)_file_name=$(package)-$($(package)_version).tar.bz2 -$(package)_sha256_hash=e920eedc2d06b09606611c99ec7304413c6784cba6e33928e78243d323195f9b +$(package)_sha256_hash=75aeb9d59a4fdb800d329a545c2e6799f732362193b465ea198f2aa275518157 define $(package)_preprocess_cmds autoreconf -i @@ -10,7 +10,7 @@ endef define $(package)_set_vars $(package)_config_opts=--disable-shared - $(package)_config_opts_linux=--with-pic + $(package)_config_opts_linux=--with-pic --disable-udev endef define $(package)_config_cmds diff --git a/src/device_trezor/CMakeLists.txt b/src/device_trezor/CMakeLists.txt index c555e9fcdee..b27c843b6b3 100644 --- a/src/device_trezor/CMakeLists.txt +++ b/src/device_trezor/CMakeLists.txt @@ -63,37 +63,20 @@ set(trezor_sources set(trezor_private_headers) -include(FindProtobuf) -find_package(Protobuf) # REQUIRED - -# Test for HAVE_PROTOBUF from the parent -if(Protobuf_FOUND AND HAVE_PROTOBUF) - if ("$ENV{PYTHON3}" STREQUAL "") - set(PYTHON3 "python3") - else() - set(PYTHON3 "$ENV{PYTHON3}" CACHE INTERNAL "Copied from environment variable") - endif() - - execute_process(COMMAND ${PYTHON3} tools/build_protob.py WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/trezor RESULT_VARIABLE RET OUTPUT_VARIABLE OUT ERROR_VARIABLE ERR) - if(RET) - message(WARNING "Trezor protobuf messages could not be regenerated (err=${RET}, python ${PYTHON})." - "OUT: ${OUT}, ERR: ${ERR}." - "Please read src/device_trezor/trezor/tools/README.md") - else() - message(STATUS "Trezor protobuf messages regenerated ${OUT}") - set(TREZOR_PROTOBUF_GENERATED 1) - endif() -endif() - - -if(TREZOR_PROTOBUF_GENERATED) +# Protobuf and LibUSB processed by CheckTrezor +if(DEVICE_TREZOR_READY) message(STATUS "Trezor support enabled") add_definitions(-DPROTOBUF_INLINE_NOT_IN_HEADERS=0) + set(TREZOR_LIBUSB_LIBRARIES "") + if(LibUSB_COMPILE_TEST_PASSED) + list(APPEND TREZOR_LIBUSB_LIBRARIES ${LibUSB_LIBRARIES}) + message(STATUS "Trezor compatible LibUSB found at: ${LibUSB_INCLUDE_DIRS}") + endif() + monero_private_headers(device_trezor - ${device_private_headers} - ${PROTOBUF_INCLUDE_DIR}) + ${device_private_headers}) monero_add_library(device_trezor ${trezor_sources} @@ -109,14 +92,13 @@ if(TREZOR_PROTOBUF_GENERATED) common ${SODIUM_LIBRARY} ${Boost_CHRONO_LIBRARY} - ${PROTOBUF_LIBRARY} + ${Protobuf_LIBRARY} + ${TREZOR_LIBUSB_LIBRARIES} PRIVATE ${EXTRA_LIBRARIES}) - # set(WITH_DEVICE_TREZOR 1 PARENT_SCOPE) - # add_definitions(-DWITH_DEVICE_TREZOR=1) - else() + message(STATUS "Trezor support disabled") monero_private_headers(device_trezor) monero_add_library(device_trezor device_trezor.cpp) target_link_libraries(device_trezor PUBLIC cncrypto) diff --git a/src/device_trezor/device_trezor.cpp b/src/device_trezor/device_trezor.cpp index f55cbb15dd7..5096fcea8ab 100644 --- a/src/device_trezor/device_trezor.cpp +++ b/src/device_trezor/device_trezor.cpp @@ -32,13 +32,12 @@ namespace hw { namespace trezor { -#if WITH_DEVICE_TREZOR +#ifdef WITH_DEVICE_TREZOR #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "device.trezor" #define HW_TREZOR_NAME "Trezor" -#define HW_TREZOR_NAME_LITE "TrezorLite" static device_trezor *trezor_device = nullptr; static device_trezor *ensure_trezor_device(){ diff --git a/src/device_trezor/device_trezor.hpp b/src/device_trezor/device_trezor.hpp index 765c9b82cbe..a2134574c91 100644 --- a/src/device_trezor/device_trezor.hpp +++ b/src/device_trezor/device_trezor.hpp @@ -49,7 +49,7 @@ namespace trezor { void register_all(); void register_all(std::map> ®istry); -#if WITH_DEVICE_TREZOR +#ifdef WITH_DEVICE_TREZOR class device_trezor; /** diff --git a/src/device_trezor/device_trezor_base.cpp b/src/device_trezor/device_trezor_base.cpp index 3a98bba5a77..38c20c30b41 100644 --- a/src/device_trezor/device_trezor_base.cpp +++ b/src/device_trezor/device_trezor_base.cpp @@ -32,43 +32,11 @@ namespace hw { namespace trezor { -#if WITH_DEVICE_TREZOR +#ifdef WITH_DEVICE_TREZOR #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "device.trezor" - std::shared_ptr trezor_protocol_callback::on_button_request(const messages::common::ButtonRequest * msg){ - MDEBUG("on_button_request"); - device.on_button_request(); - return std::make_shared(); - } - - std::shared_ptr trezor_protocol_callback::on_pin_matrix_request(const messages::common::PinMatrixRequest * msg){ - MDEBUG("on_pin_request"); - epee::wipeable_string pin; - device.on_pin_request(pin); - auto resp = std::make_shared(); - resp->set_pin(pin.data(), pin.size()); - return resp; - } - - std::shared_ptr trezor_protocol_callback::on_passphrase_request(const messages::common::PassphraseRequest * msg){ - MDEBUG("on_passhprase_request"); - epee::wipeable_string passphrase; - device.on_passphrase_request(msg->on_device(), passphrase); - auto resp = std::make_shared(); - if (!msg->on_device()){ - resp->set_passphrase(passphrase.data(), passphrase.size()); - } - return resp; - } - - std::shared_ptr trezor_protocol_callback::on_passphrase_state_request(const messages::common::PassphraseStateRequest * msg){ - MDEBUG("on_passhprase_state_request"); - device.on_passphrase_state_request(msg->state()); - return std::make_shared(); - } - const uint32_t device_trezor_base::DEFAULT_BIP44_PATH[] = {0x8000002c, 0x80000080, 0x80000000}; device_trezor_base::device_trezor_base() { @@ -117,9 +85,6 @@ namespace trezor { return false; } - if (!m_protocol_callback){ - m_protocol_callback = std::make_shared(*this); - } return true; } @@ -245,6 +210,49 @@ namespace trezor { } } + void device_trezor_base::write_raw(const google::protobuf::Message * msg){ + require_connected(); + CHECK_AND_ASSERT_THROW_MES(msg, "Empty message"); + this->getTransport()->write(*msg); + } + + GenericMessage device_trezor_base::read_raw(){ + require_connected(); + std::shared_ptr msg_resp; + hw::trezor::messages::MessageType msg_resp_type; + + this->getTransport()->read(msg_resp, &msg_resp_type); + return GenericMessage(msg_resp_type, msg_resp); + } + + GenericMessage device_trezor_base::call_raw(const google::protobuf::Message * msg) { + write_raw(msg); + return read_raw(); + } + + bool device_trezor_base::message_handler(GenericMessage & input){ + // Later if needed this generic message handler can be replaced by a pointer to + // a protocol message handler which by default points to the device class which implements + // the default handler. + switch(input.m_type){ + case messages::MessageType_ButtonRequest: + on_button_request(input, dynamic_cast(input.m_msg.get())); + return true; + case messages::MessageType_PassphraseRequest: + on_passphrase_request(input, dynamic_cast(input.m_msg.get())); + return true; + case messages::MessageType_PassphraseStateRequest: + on_passphrase_state_request(input, dynamic_cast(input.m_msg.get())); + return true; + case messages::MessageType_PinMatrixRequest: + on_pin_request(input, dynamic_cast(input.m_msg.get())); + return true; + default: + return false; + } + } + + /* ======================================================================= */ /* TREZOR PROTOCOL */ /* ======================================================================= */ @@ -269,32 +277,67 @@ namespace trezor { return false; } - void device_trezor_base::on_button_request() + void device_trezor_base::on_button_request(GenericMessage & resp, const messages::common::ButtonRequest * msg) { + CHECK_AND_ASSERT_THROW_MES(msg, "Empty message"); + MDEBUG("on_button_request, code: " << msg->code()); + + messages::common::ButtonAck ack; + write_raw(&ack); + if (m_callback){ m_callback->on_button_request(); } + + resp = read_raw(); } - void device_trezor_base::on_pin_request(epee::wipeable_string & pin) + void device_trezor_base::on_pin_request(GenericMessage & resp, const messages::common::PinMatrixRequest * msg) { + MDEBUG("on_pin_request"); + CHECK_AND_ASSERT_THROW_MES(msg, "Empty message"); + + epee::wipeable_string pin; + if (m_callback){ m_callback->on_pin_request(pin); } + + // TODO: remove PIN from memory + messages::common::PinMatrixAck m; + m.set_pin(pin.data(), pin.size()); + resp = call_raw(&m); } - void device_trezor_base::on_passphrase_request(bool on_device, epee::wipeable_string & passphrase) + void device_trezor_base::on_passphrase_request(GenericMessage & resp, const messages::common::PassphraseRequest * msg) { + CHECK_AND_ASSERT_THROW_MES(msg, "Empty message"); + MDEBUG("on_passhprase_request, on device: " << msg->on_device()); + epee::wipeable_string passphrase; + if (m_callback){ - m_callback->on_passphrase_request(on_device, passphrase); + m_callback->on_passphrase_request(msg->on_device(), passphrase); } + + messages::common::PassphraseAck m; + if (!msg->on_device()){ + // TODO: remove passphrase from memory + m.set_passphrase(passphrase.data(), passphrase.size()); + } + resp = call_raw(&m); } - void device_trezor_base::on_passphrase_state_request(const std::string & state) + void device_trezor_base::on_passphrase_state_request(GenericMessage & resp, const messages::common::PassphraseStateRequest * msg) { + MDEBUG("on_passhprase_state_request"); + CHECK_AND_ASSERT_THROW_MES(msg, "Empty message"); + if (m_callback){ - m_callback->on_passphrase_state_request(state); + m_callback->on_passphrase_state_request(msg->state()); } + + messages::common::PassphraseStateAck m; + resp = call_raw(&m); } #endif //WITH_DEVICE_TREZOR diff --git a/src/device_trezor/device_trezor_base.hpp b/src/device_trezor/device_trezor_base.hpp index 644a4933225..88d4194947a 100644 --- a/src/device_trezor/device_trezor_base.hpp +++ b/src/device_trezor/device_trezor_base.hpp @@ -54,7 +54,7 @@ namespace hw { namespace trezor { -#if WITH_DEVICE_TREZOR +#ifdef WITH_DEVICE_TREZOR class device_trezor_base; /** @@ -68,41 +68,6 @@ namespace trezor { virtual void on_passphrase_state_request(const std::string & state) {}; }; - /** - * Default Trezor protocol client callback - */ - class trezor_protocol_callback { - protected: - device_trezor_base & device; - - public: - explicit trezor_protocol_callback(device_trezor_base & device): device(device) {} - - std::shared_ptr on_button_request(const messages::common::ButtonRequest * msg); - std::shared_ptr on_pin_matrix_request(const messages::common::PinMatrixRequest * msg); - std::shared_ptr on_passphrase_request(const messages::common::PassphraseRequest * msg); - std::shared_ptr on_passphrase_state_request(const messages::common::PassphraseStateRequest * msg); - - std::shared_ptr on_message(const google::protobuf::Message * msg, messages::MessageType message_type){ - MDEBUG("on_general_message"); - return on_message_dispatch(msg, message_type); - } - - std::shared_ptr on_message_dispatch(const google::protobuf::Message * msg, messages::MessageType message_type){ - if (message_type == messages::MessageType_ButtonRequest){ - return on_button_request(dynamic_cast(msg)); - } else if (message_type == messages::MessageType_PassphraseRequest) { - return on_passphrase_request(dynamic_cast(msg)); - } else if (message_type == messages::MessageType_PassphraseStateRequest) { - return on_passphrase_state_request(dynamic_cast(msg)); - } else if (message_type == messages::MessageType_PinMatrixRequest) { - return on_pin_matrix_request(dynamic_cast(msg)); - } else { - return nullptr; - } - } - }; - /** * TREZOR device template with basic functions */ @@ -114,7 +79,6 @@ namespace trezor { mutable boost::mutex command_locker; std::shared_ptr m_transport; - std::shared_ptr m_protocol_callback; std::shared_ptr m_callback; std::string full_name; @@ -129,6 +93,15 @@ namespace trezor { void call_ping_unsafe(); void test_ping(); + // Communication methods + + void write_raw(const google::protobuf::Message * msg); + GenericMessage read_raw(); + GenericMessage call_raw(const google::protobuf::Message * msg); + + // Trezor message protocol handler. Handles specific signalling messages. + bool message_handler(GenericMessage & input); + /** * Client communication wrapper, handles specific Trezor protocol. * @@ -141,8 +114,7 @@ namespace trezor { const boost::optional & resp_type = boost::none, const boost::optional> & resp_types = boost::none, const boost::optional & resp_type_ptr = boost::none, - bool open_session = false, - unsigned depth=0) + bool open_session = false) { // Require strictly protocol buffers response in the template. BOOST_STATIC_ASSERT(boost::is_base_of::value); @@ -151,8 +123,12 @@ namespace trezor { throw std::invalid_argument("Cannot specify list of accepted types and not using generic response"); } + // Determine type of expected message response + const messages::MessageType required_type = accepting_base ? messages::MessageType_Success : + (resp_type ? resp_type.get() : MessageMapper::get_message_wire_number()); + // Open session if required - if (open_session && depth == 0){ + if (open_session){ try { m_transport->open(); } catch (const std::exception& e) { @@ -162,47 +138,37 @@ namespace trezor { // Scoped session closer BOOST_SCOPE_EXIT_ALL(&, this) { - if (open_session && depth == 0){ + if (open_session){ this->getTransport()->close(); } }; - // Write the request + // Write/read the request CHECK_AND_ASSERT_THROW_MES(req, "Request is null"); - this->getTransport()->write(*req); + auto msg_resp = call_raw(req.get()); - // Read the response - std::shared_ptr msg_resp; - hw::trezor::messages::MessageType msg_resp_type; + bool processed = false; + do { + processed = message_handler(msg_resp); + } while(processed); - // We may have several roundtrips with the handler - this->getTransport()->read(msg_resp, &msg_resp_type); + // Response section if (resp_type_ptr){ - *(resp_type_ptr.get()) = msg_resp_type; + *(resp_type_ptr.get()) = msg_resp.m_type; } - // Determine type of expected message response - messages::MessageType required_type = accepting_base ? messages::MessageType_Success : - (resp_type ? resp_type.get() : MessageMapper::get_message_wire_number()); + if (msg_resp.m_type == messages::MessageType_Failure) { + throw_failure_exception(dynamic_cast(msg_resp.m_msg.get())); - if (msg_resp_type == messages::MessageType_Failure) { - throw_failure_exception(dynamic_cast(msg_resp.get())); + } else if (!accepting_base && msg_resp.m_type == required_type) { + return message_ptr_retype(msg_resp.m_msg); - } else if (!accepting_base && msg_resp_type == required_type) { - return message_ptr_retype(msg_resp); + } else if (accepting_base && (!resp_types || + std::find(resp_types.get().begin(), resp_types.get().end(), msg_resp.m_type) != resp_types.get().end())) { + return message_ptr_retype(msg_resp.m_msg); } else { - auto resp = this->getProtocolCallback()->on_message(msg_resp.get(), msg_resp_type); - if (resp) { - return this->client_exchange(resp, boost::none, resp_types, resp_type_ptr, false, depth + 1); - - } else if (accepting_base && (!resp_types || - std::find(resp_types.get().begin(), resp_types.get().end(), msg_resp_type) != resp_types.get().end())) { - return message_ptr_retype(msg_resp); - - } else { - throw exc::UnexpectedMessageException(msg_resp_type, msg_resp); - } + throw exc::UnexpectedMessageException(msg_resp.m_type, msg_resp.m_msg); } } @@ -252,10 +218,6 @@ namespace trezor { return m_transport; } - std::shared_ptr getProtocolCallback(){ - return m_protocol_callback; - } - std::shared_ptr getCallback(){ return m_callback; } @@ -288,10 +250,10 @@ namespace trezor { bool ping(); // Protocol callbacks - void on_button_request(); - void on_pin_request(epee::wipeable_string & pin); - void on_passphrase_request(bool on_device, epee::wipeable_string & passphrase); - void on_passphrase_state_request(const std::string & state); + void on_button_request(GenericMessage & resp, const messages::common::ButtonRequest * msg); + void on_pin_request(GenericMessage & resp, const messages::common::PinMatrixRequest * msg); + void on_passphrase_request(GenericMessage & resp, const messages::common::PassphraseRequest * msg); + void on_passphrase_state_request(GenericMessage & resp, const messages::common::PassphraseStateRequest * msg); }; #endif diff --git a/src/device_trezor/trezor.hpp b/src/device_trezor/trezor.hpp index 8abdd2c18de..97dc0a9573c 100644 --- a/src/device_trezor/trezor.hpp +++ b/src/device_trezor/trezor.hpp @@ -32,7 +32,7 @@ #include "trezor/trezor_defs.hpp" -#ifdef HAVE_PROTOBUF +#ifdef WITH_DEVICE_TREZOR #include "trezor/transport.hpp" #include "trezor/messages/messages.pb.h" #include "trezor/messages/messages-common.pb.h" diff --git a/src/device_trezor/trezor/tools/README.md b/src/device_trezor/trezor/tools/README.md index 91a8fb3f0b0..b176017ac3c 100644 --- a/src/device_trezor/trezor/tools/README.md +++ b/src/device_trezor/trezor/tools/README.md @@ -2,33 +2,28 @@ ## Messages rebuild -Install `protoc` for your distribution. +Install `protoc` for your distribution. Requirements: - `protobuf-compiler` - `libprotobuf-dev` -- `libprotoc-dev` -- `python-protobuf` +- `python` -Python 3 is required. If you don't have python 3 quite an easy way is -to use [pyenv]. -It is also advised to create own python virtual environment so dependencies -are installed in this project-related virtual environment. +Soft requirement: Python 3, can be easily installed with [pyenv]. -```bash -python -m venv / -``` +### Python 2 -Make sure your python has `protobuf` package installed +Workaround if there is no Python3 available: ```bash -pip install protobuf +pip install backports.tempfile ``` -Regenerate messages: +### Regenerate messages -``` -./venv/bin/python3 src/device_trezor/trezor/tools/build_protob.py +```bash +cd src/device_trezor/trezor +python tools/build_protob.py ``` The messages regeneration is done also automatically via cmake. diff --git a/src/device_trezor/trezor/tools/pb2cpp.py b/src/device_trezor/trezor/tools/pb2cpp.py index eaa8a90ed01..4d7cc775f00 100644 --- a/src/device_trezor/trezor/tools/pb2cpp.py +++ b/src/device_trezor/trezor/tools/pb2cpp.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python3 +#!/usr/bin/env python # Converts Google's protobuf python definitions of TREZOR wire messages # to plain-python objects as used in TREZOR Core and python-trezor @@ -8,11 +8,19 @@ import re import shutil import subprocess -import sys import glob -import tempfile import hashlib +try: + from tempfile import TemporaryDirectory +except: + # Py2 backward compatibility, optionally installed by user + # pip install backports.tempfile + try: + from backports.tempfile import TemporaryDirectory + except: + raise EnvironmentError('TemporaryDirectory could not be imported. Try: pip install backports.tempfile') + AUTO_HEADER = "# Automatically generated by pb2cpp\n" @@ -23,6 +31,9 @@ #endif """ +PROTOC = None +PROTOC_INCLUDE = None + def which(pgm): path = os.getenv('PATH') @@ -32,15 +43,6 @@ def which(pgm): return p -PROTOC = which("protoc") -if not PROTOC: - print("protoc command not found") - sys.exit(1) - -PROTOC_PREFIX = os.path.dirname(os.path.dirname(PROTOC)) -PROTOC_INCLUDE = os.path.join(PROTOC_PREFIX, "include") - - def namespace_file(fpath, package): """Adds / replaces package name. Simple regex parsing, may use https://github.com/ph4r05/plyprotobuf later""" with open(fpath) as fh: @@ -82,9 +84,10 @@ def protoc(files, out_dir, additional_includes=(), package=None, force=False): include_dirs = set() include_dirs.add(PROTOC_INCLUDE) - include_dirs.update(additional_includes) + if additional_includes: + include_dirs.update(additional_includes) - with tempfile.TemporaryDirectory() as tmpdir_protob, tempfile.TemporaryDirectory() as tmpdir_out: + with TemporaryDirectory() as tmpdir_protob, TemporaryDirectory() as tmpdir_out: include_dirs.add(tmpdir_protob) new_files = [] @@ -125,9 +128,9 @@ def update_message_files(tmpdir_out, out_dir, force=False): dest_file = os.path.join(out_dir, bname) if not force and os.path.exists(dest_file): data = open(fname, 'rb').read() - data_hash = hashlib.sha3_256(data).digest() + data_hash = hashlib.sha256(data).digest() data_dest = open(dest_file, 'rb').read() - data_dest_hash = hashlib.sha3_256(data_dest).digest() + data_dest_hash = hashlib.sha256(data_dest).digest() if data_hash == data_dest_hash: continue @@ -163,7 +166,8 @@ def strip_leader(s, prefix): return s -if __name__ == "__main__": +def main(): + global PROTOC, PROTOC_INCLUDE logging.basicConfig(level=logging.DEBUG) parser = argparse.ArgumentParser() @@ -179,8 +183,31 @@ def strip_leader(s, prefix): protoc_includes = args.protoc_include or (os.environ.get("PROTOC_INCLUDE"),) + PROTOBUF_INCLUDE_DIRS = os.getenv("PROTOBUF_INCLUDE_DIRS", None) + PROTOBUF_PROTOC_EXECUTABLE = os.getenv("PROTOBUF_PROTOC_EXECUTABLE", None) + + if PROTOBUF_PROTOC_EXECUTABLE and not os.path.exists(PROTOBUF_PROTOC_EXECUTABLE): + raise ValueError("PROTOBUF_PROTOC_EXECUTABLE set but not found: %s" % PROTOBUF_PROTOC_EXECUTABLE) + + elif PROTOBUF_PROTOC_EXECUTABLE: + PROTOC = PROTOBUF_PROTOC_EXECUTABLE + + else: + if os.name == "nt": + PROTOC = which("protoc.exe") + else: + PROTOC = which("protoc") + + if not PROTOC: + raise ValueError("protoc command not found. Set PROTOBUF_PROTOC_EXECUTABLE env var to the protoc binary and optionally PROTOBUF_INCLUDE_DIRS") + + PROTOC_PREFIX = os.path.dirname(os.path.dirname(PROTOC)) + PROTOC_INCLUDE = PROTOBUF_INCLUDE_DIRS if PROTOBUF_INCLUDE_DIRS else os.path.join(PROTOC_PREFIX, "include") + protoc( args.proto, args.out_dir, protoc_includes, package=args.namespace, force=args.force ) +if __name__ == "__main__": + main() diff --git a/src/device_trezor/trezor/transport.cpp b/src/device_trezor/trezor/transport.cpp index fc86177e19c..814537eb633 100644 --- a/src/device_trezor/trezor/transport.cpp +++ b/src/device_trezor/trezor/transport.cpp @@ -27,13 +27,21 @@ // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // +#ifdef WITH_DEVICE_TREZOR_WEBUSB +#include +#endif + #include #include #include #include +#include #include "transport.hpp" #include "messages/messages-common.pb.h" +#undef MONERO_DEFAULT_LOG_CATEGORY +#define MONERO_DEFAULT_LOG_CATEGORY "device.trezor.transport" + using namespace std; using json = rapidjson::Document; @@ -581,12 +589,400 @@ namespace trezor{ << ">"; } +#ifdef WITH_DEVICE_TREZOR_WEBUSB + + static bool is_trezor1(libusb_device_descriptor * info){ + return info->idVendor == 0x534C && info->idProduct == 0x0001; + } + + static bool is_trezor2(libusb_device_descriptor * info){ + return info->idVendor == 0x1209 && info->idProduct == 0x53C1; + } + + static bool is_trezor2_bl(libusb_device_descriptor * info){ + return info->idVendor == 0x1209 && info->idProduct == 0x53C0; + } + + static uint8_t get_trezor_dev_mask(libusb_device_descriptor * info){ + uint8_t mask = 0; + CHECK_AND_ASSERT_THROW_MES(info, "Empty device descriptor"); + mask |= is_trezor1(info) ? 1 : 0; + mask |= is_trezor2(info) ? 2 : 0; + mask |= is_trezor2_bl(info) ? 4 : 0; + return mask; + } + + static void set_libusb_log(libusb_context *ctx){ + CHECK_AND_ASSERT_THROW_MES(ctx, "Null libusb context"); + + // http://libusb.sourceforge.net/api-1.0/group__libusb__lib.html +#if defined(LIBUSB_API_VERSION) && (LIBUSB_API_VERSION >= 0x01000106) +# define TREZOR_LIBUSB_SET_DEBUG(ctx, level) libusb_set_option(ctx, LIBUSB_OPTION_LOG_LEVEL, level) +#else +# define TREZOR_LIBUSB_SET_DEBUG(ctx, level) libusb_set_debug(ctx, level) +#endif + + if (ELPP->vRegistry()->allowed(el::Level::Debug, MONERO_DEFAULT_LOG_CATEGORY)) + TREZOR_LIBUSB_SET_DEBUG(ctx, 3); + else if (ELPP->vRegistry()->allowed(el::Level::Warning, MONERO_DEFAULT_LOG_CATEGORY)) + TREZOR_LIBUSB_SET_DEBUG(ctx, 2); + else if (ELPP->vRegistry()->allowed(el::Level::Error, MONERO_DEFAULT_LOG_CATEGORY)) + TREZOR_LIBUSB_SET_DEBUG(ctx, 1); + +#undef TREZOR_LIBUSB_SET_DEBUG + } + + static int get_libusb_ports(libusb_device *dev, std::vector &path){ + uint8_t tmp_path[16]; + int r = libusb_get_port_numbers(dev, tmp_path, sizeof(tmp_path)); + CHECK_AND_ASSERT_MES(r != LIBUSB_ERROR_OVERFLOW, -1, "Libusb path array too small"); + CHECK_AND_ASSERT_MES(r >= 0, -1, "Libusb path array error"); + + path.resize(r); + for (int i = 0; i < r; i++){ + path[i] = tmp_path[i]; + } + + return 0; + } + + static std::string get_usb_path(uint8_t bus_id, const std::vector &path){ + std::stringstream ss; + ss << WebUsbTransport::PATH_PREFIX << (boost::format("%03d") % ((int)bus_id)); + for(uint8_t port : path){ + ss << ":" << ((int) port); + } + return ss.str(); + } + + const char * WebUsbTransport::PATH_PREFIX = "webusb:"; + + WebUsbTransport::WebUsbTransport( + boost::optional descriptor, + boost::optional> proto + ): m_conn_count(0), + m_usb_session(nullptr), m_usb_device(nullptr), m_usb_device_handle(nullptr), + m_bus_id(-1), m_device_addr(-1) + { + if (descriptor){ + libusb_device_descriptor * desc = new libusb_device_descriptor; + memcpy(desc, descriptor.get(), sizeof(libusb_device_descriptor)); + this->m_usb_device_desc.reset(desc); + } + + m_proto = proto ? proto.get() : std::make_shared(); + +#ifdef WITH_TREZOR_DEBUG + m_debug_mode = false; +#endif + } + + WebUsbTransport::~WebUsbTransport(){ + if (m_usb_device){ + close(); + } + + if (m_usb_session) { + libusb_exit(m_usb_session); + m_usb_session = nullptr; + } + } + + void WebUsbTransport::require_device() const{ + if (!m_usb_device_desc){ + throw std::runtime_error("No USB device specified"); + } + } + + void WebUsbTransport::require_connected() const{ + require_device(); + if (!m_usb_device_handle){ + throw std::runtime_error("USB Device not opened"); + } + } + + void WebUsbTransport::enumerate(t_transport_vect & res) { + int r; + libusb_device **devs; + libusb_context *ctx = nullptr; + + r = libusb_init(&ctx); + CHECK_AND_ASSERT_THROW_MES(r >= 0, "Unable to init libusb"); + + set_libusb_log(ctx); + + ssize_t cnt = libusb_get_device_list(ctx, &devs); + if (cnt < 0){ + libusb_exit(ctx); + throw std::runtime_error("Unable to enumerate libusb devices"); + } + + MTRACE("Libusb devices: " << cnt); + + for(ssize_t i = 0; i < cnt; i++) { + libusb_device_descriptor desc{}; + r = libusb_get_device_descriptor(devs[i], &desc); + if (r < 0){ + MERROR("Unable to get libusb device descriptor " << i); + continue; + } + + const auto trezor_mask = get_trezor_dev_mask(&desc); + if (!trezor_mask){ + continue; + } + + MTRACE("Found Trezor device: " << desc.idVendor << ":" << desc.idProduct << " mask " << (int)trezor_mask); + + auto t = std::make_shared(boost::make_optional(&desc)); + t->m_bus_id = libusb_get_bus_number(devs[i]); + t->m_device_addr = libusb_get_device_address(devs[i]); + + // Port resolution may fail. Non-critical error, just addressing precision is decreased. + get_libusb_ports(devs[i], t->m_port_numbers); + + res.push_back(t); + } + + libusb_free_device_list(devs, 1); + libusb_exit(ctx); + } + + std::string WebUsbTransport::get_path() const { + if (!m_usb_device_desc){ + return ""; + } + + return get_usb_path(static_cast(m_bus_id), m_port_numbers); + }; + + void WebUsbTransport::open() { + const int interface = get_interface(); + if (m_conn_count > 0){ + MTRACE("Already opened, count: " << m_conn_count); + m_conn_count += 1; + return; + } + +#define TREZOR_DESTROY_SESSION() do { libusb_exit(m_usb_session); m_usb_session = nullptr; } while(0) + + int r; + libusb_device **devs = nullptr; + + if (m_usb_session) { + TREZOR_DESTROY_SESSION(); + } + + r = libusb_init(&m_usb_session); + CHECK_AND_ASSERT_THROW_MES(r >= 0, "Unable to init libusb"); + set_libusb_log(m_usb_session); + + bool found = false; + int open_res = 0; + + ssize_t cnt = libusb_get_device_list(m_usb_session, &devs); + if (cnt < 0){ + TREZOR_DESTROY_SESSION(); + throw std::runtime_error("Unable to enumerate libusb devices"); + } + + for (ssize_t i = 0; i < cnt; i++) { + libusb_device_descriptor desc{}; + r = libusb_get_device_descriptor(devs[i], &desc); + if (r < 0){ + MERROR("Unable to get libusb device descriptor " << i); + continue; + } + + const auto trezor_mask = get_trezor_dev_mask(&desc); + if (!trezor_mask) { + continue; + } + + auto bus_id = libusb_get_bus_number(devs[i]); + std::vector path; + + // Port resolution may fail. Non-critical error, just addressing precision is decreased. + get_libusb_ports(devs[i], path); + + MTRACE("Found Trezor device: " << desc.idVendor << ":" << desc.idProduct + << ", mask: " << (int)trezor_mask + << ". path: " << get_usb_path(bus_id, path)); + + if (bus_id == m_bus_id && path == m_port_numbers) { + found = true; + m_usb_device = devs[i]; + open_res = libusb_open(m_usb_device, &m_usb_device_handle); + break; + } + } + + libusb_free_device_list(devs, 1); + + if (!found){ + TREZOR_DESTROY_SESSION(); + throw exc::DeviceAcquireException("Device not found"); + + } else if (found && open_res != 0) { + m_usb_device_handle = nullptr; + m_usb_device = nullptr; + TREZOR_DESTROY_SESSION(); + throw exc::DeviceAcquireException("Unable to open libusb device"); + } + + r = libusb_claim_interface(m_usb_device_handle, interface); + + if (r != 0){ + libusb_close(m_usb_device_handle); + m_usb_device_handle = nullptr; + m_usb_device = nullptr; + TREZOR_DESTROY_SESSION(); + throw exc::DeviceAcquireException("Unable to claim libusb device"); + } + + m_conn_count += 1; + m_proto->session_begin(*this); + +#undef TREZOR_DESTROY_SESSION + }; + + void WebUsbTransport::close() { + m_conn_count -= 1; + + if (m_conn_count < 0){ + MERROR("Close counter is negative: " << m_conn_count); + + } else if (m_conn_count == 0){ + MTRACE("Closing webusb device"); + + m_proto->session_end(*this); + + int r = libusb_release_interface(m_usb_device_handle, get_interface()); + if (r != 0){ + MERROR("Could not release libusb interface: " << r); + } + + m_usb_device = nullptr; + if (m_usb_device_handle) { + libusb_close(m_usb_device_handle); + m_usb_device_handle = nullptr; + } + + if (m_usb_session) { + libusb_exit(m_usb_session); + m_usb_session = nullptr; + } + } + }; + + + int WebUsbTransport::get_interface() const{ + const int INTERFACE_NORMAL = 0; +#ifdef WITH_TREZOR_DEBUG + const int INTERFACE_DEBUG = 1; + return m_debug_mode ? INTERFACE_DEBUG : INTERFACE_NORMAL; +#else + return INTERFACE_NORMAL; +#endif + } + + unsigned char WebUsbTransport::get_endpoint() const{ + const unsigned char ENDPOINT_NORMAL = 1; +#ifdef WITH_TREZOR_DEBUG + const unsigned char ENDPOINT_DEBUG = 2; + return m_debug_mode ? ENDPOINT_DEBUG : ENDPOINT_NORMAL; +#else + return ENDPOINT_NORMAL; +#endif + } + + void WebUsbTransport::write(const google::protobuf::Message &req) { + m_proto->write(*this, req); + }; + + void WebUsbTransport::read(std::shared_ptr & msg, messages::MessageType * msg_type) { + m_proto->read(*this, msg, msg_type); + }; + + void WebUsbTransport::write_chunk(const void * buff, size_t size) { + require_connected(); + if (size != REPLEN){ + throw exc::CommunicationException("Invalid chunk size: "); + } + + unsigned char endpoint = get_endpoint(); + endpoint = (endpoint & ~LIBUSB_ENDPOINT_DIR_MASK) | LIBUSB_ENDPOINT_OUT; + + int transferred = 0; + int r = libusb_interrupt_transfer(m_usb_device_handle, endpoint, (unsigned char*)buff, (int)size, &transferred, 0); + CHECK_AND_ASSERT_THROW_MES(r == 0, "Unable to transfer, r: " << r); + if (transferred != (int)size){ + throw exc::CommunicationException("Could not transfer chunk"); + } + }; + + size_t WebUsbTransport::read_chunk(void * buff, size_t size) { + require_connected(); + unsigned char endpoint = get_endpoint(); + endpoint = (endpoint & ~LIBUSB_ENDPOINT_DIR_MASK) | LIBUSB_ENDPOINT_IN; + + int transferred = 0; + int r = libusb_interrupt_transfer(m_usb_device_handle, endpoint, (unsigned char*)buff, (int)size, &transferred, 0); + CHECK_AND_ASSERT_THROW_MES(r == 0, "Unable to transfer, r: " << r); + if (transferred != (int)size){ + throw exc::CommunicationException("Could not read the chunk"); + } + + return transferred; + }; + + std::ostream& WebUsbTransport::dump(std::ostream& o) const { + o << "WebUsbTransport"; + }; + +#endif // WITH_DEVICE_TREZOR_WEBUSB + void enumerate(t_transport_vect & res){ BridgeTransport bt; - bt.enumerate(res); + try{ + bt.enumerate(res); + } catch (const std::exception & e){ + MERROR("BridgeTransport enumeration failed:" << e.what()); + } + +#ifdef WITH_DEVICE_TREZOR_WEBUSB + hw::trezor::WebUsbTransport btw; + try{ + btw.enumerate(res); + } catch (const std::exception & e){ + MERROR("WebUsbTransport enumeration failed:" << e.what()); + } +#endif +#ifdef WITH_DEVICE_TREZOR_UDP hw::trezor::UdpTransport btu; - btu.enumerate(res); + try{ + btu.enumerate(res); + } catch (const std::exception & e){ + MERROR("UdpTransport enumeration failed:" << e.what()); + } +#endif } std::shared_ptr transport(const std::string & path){ @@ -633,6 +1029,9 @@ namespace trezor{ } } + GenericMessage::GenericMessage(messages::MessageType m_type, const shared_ptr &m_msg) + : m_type(m_type), m_msg(m_msg), m_empty(false) {} + std::ostream& operator<<(std::ostream& o, hw::trezor::Transport const& t){ return t.dump(o); } diff --git a/src/device_trezor/trezor/transport.hpp b/src/device_trezor/trezor/transport.hpp index 7b82fd06f6d..50c31cf7328 100644 --- a/src/device_trezor/trezor/transport.hpp +++ b/src/device_trezor/trezor/transport.hpp @@ -239,6 +239,59 @@ namespace trezor { udp::endpoint m_endpoint; }; +#ifdef WITH_DEVICE_TREZOR_WEBUSB +#include + + class WebUsbTransport : public Transport { + public: + + explicit WebUsbTransport( + boost::optional descriptor = boost::none, + boost::optional> proto = boost::none + ); + + virtual ~WebUsbTransport(); + + static const char * PATH_PREFIX; + + std::string get_path() const override; + void enumerate(t_transport_vect & res) override; + + void open() override; + void close() override; + + void write(const google::protobuf::Message &req) override; + void read(std::shared_ptr & msg, messages::MessageType * msg_type=nullptr) override; + + void write_chunk(const void * buff, size_t size) override; + size_t read_chunk(void * buff, size_t size) override; + + std::ostream& dump(std::ostream& o) const override; + + private: + void require_device() const; + void require_connected() const; + int get_interface() const; + unsigned char get_endpoint() const; + + int m_conn_count; + std::shared_ptr m_proto; + + libusb_context *m_usb_session; + libusb_device *m_usb_device; + libusb_device_handle *m_usb_device_handle; + std::unique_ptr m_usb_device_desc; + std::vector m_port_numbers; + int m_bus_id; + int m_device_addr; + +#ifdef WITH_TREZOR_DEBUG + bool m_debug_mode; +#endif + }; + +#endif + // // General helpers // @@ -289,6 +342,20 @@ namespace trezor { */ [[ noreturn ]] void throw_failure_exception(const messages::common::Failure * failure); + /** + * Generic message holder, type + obj + */ + class GenericMessage { + public: + GenericMessage(): m_empty(true) {} + GenericMessage(messages::MessageType m_type, const std::shared_ptr &m_msg); + bool empty() const { return m_empty; } + + hw::trezor::messages::MessageType m_type; + std::shared_ptr m_msg; + bool m_empty; + }; + /** * Simple wrapper for write-read message exchange with expected message response type. * diff --git a/src/device_trezor/trezor/trezor_defs.hpp b/src/device_trezor/trezor/trezor_defs.hpp index 951a8f80285..30e76eadced 100644 --- a/src/device_trezor/trezor/trezor_defs.hpp +++ b/src/device_trezor/trezor/trezor_defs.hpp @@ -27,14 +27,41 @@ // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -#if defined(HAVE_PROTOBUF) && !defined(WITHOUT_TREZOR) - #define WITH_DEVICE_TREZOR 1 -#else - #define WITH_DEVICE_TREZOR 0 +#ifndef USE_DEVICE_TREZOR +#define USE_DEVICE_TREZOR 1 #endif -#ifndef WITH_DEVICE_TREZOR_LITE -#define WITH_DEVICE_TREZOR_LITE 0 +// HAVE_TREZOR_READY set by cmake after protobuf messages +// were generated successfully and all minimal dependencies are met. +#ifndef DEVICE_TREZOR_READY +#undef USE_DEVICE_TREZOR +#define USE_DEVICE_TREZOR 0 +#endif + +#if USE_DEVICE_TREZOR +#define WITH_DEVICE_TREZOR 1 +#endif + +#ifndef WITH_DEVICE_TREZOR +#undef WITH_DEVICE_TREZOR_LITE +#endif + +#if defined(HAVE_TREZOR_LIBUSB) && USE_DEVICE_TREZOR +#define WITH_DEVICE_TREZOR_WEBUSB 1 +#endif + +// Enable / disable UDP in the enumeration +#ifndef USE_DEVICE_TREZOR_UDP +#define USE_DEVICE_TREZOR_UDP 1 +#endif + +// Enable / disable UDP in the enumeration for release +#ifndef USE_DEVICE_TREZOR_UDP_RELEASE +#define USE_DEVICE_TREZOR_UDP_RELEASE 0 +#endif + +#if USE_DEVICE_TREZOR_UDP && (USE_DEVICE_TREZOR_UDP_RELEASE || defined(TREZOR_DEBUG)) +#define WITH_DEVICE_TREZOR_UDP 1 #endif // Avoids protobuf undefined macro warning From 1132436f972643328e81c3f8929178351a9b96fc Mon Sep 17 00:00:00 2001 From: Martijn Otto Date: Tue, 20 Nov 2018 17:11:06 +0100 Subject: [PATCH 0799/1404] Only show a single mlock() error, to avoid flooding the log --- contrib/epee/src/mlocker.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/contrib/epee/src/mlocker.cpp b/contrib/epee/src/mlocker.cpp index c3262e8f4bf..1e831caf1a9 100644 --- a/contrib/epee/src/mlocker.cpp +++ b/contrib/epee/src/mlocker.cpp @@ -38,6 +38,12 @@ #include "syncobj.h" #include "mlocker.h" +#include + +// did an mlock operation previously fail? we only +// want to log an error once and be done with it +static std::atomic previously_failed{ false }; + static size_t query_page_size() { #if defined HAVE_MLOCK @@ -59,8 +65,8 @@ static void do_lock(void *ptr, size_t len) { #if defined HAVE_MLOCK int ret = mlock(ptr, len); - if (ret < 0) - MERROR("Error locking page at " << ptr << ": " << strerror(errno)); + if (ret < 0 && !previously_failed.exchange(true)) + MERROR("Error locking page at " << ptr << ": " << strerror(errno) << ", subsequent mlock errors will be silenced"); #else #warning Missing do_lock implementation #endif @@ -70,7 +76,10 @@ static void do_unlock(void *ptr, size_t len) { #if defined HAVE_MLOCK int ret = munlock(ptr, len); - if (ret < 0) + // check whether we previously failed, but don't set it, this is just + // to pacify the errors of mlock()ing failed, in which case unlocking + // is also not going to work of course + if (ret < 0 && !previously_failed.load()) MERROR("Error unlocking page at " << ptr << ": " << strerror(errno)); #else #warning Missing implementation of page size detection From 506472e0c6a1961ada5e1e68ff10def760e4316f Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 26 Nov 2018 17:19:25 +0000 Subject: [PATCH 0800/1404] protocol: fix use after free when dropping a connection --- src/cryptonote_protocol/cryptonote_protocol_handler.inl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl index c2c660e8cfe..1a26e7ee87f 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl @@ -1738,9 +1738,9 @@ skip: if (add_fail) m_p2p->add_host_fail(context.m_remote_address); - m_p2p->drop_connection(context); - m_block_queue.flush_spans(context.m_connection_id, flush_all_spans); + + m_p2p->drop_connection(context); } //------------------------------------------------------------------------------------------------------------------------ template From fc98f7a0a13c1246b67ed4d7fb40705bb68a8b93 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 7 Nov 2018 21:13:00 +0000 Subject: [PATCH 0801/1404] rpc: speedup get_outs.bin --- contrib/epee/include/span.h | 2 ++ src/blockchain_db/blockchain_db.h | 4 +-- src/blockchain_db/lmdb/db_lmdb.cpp | 14 ++++++---- src/blockchain_db/lmdb/db_lmdb.h | 2 +- src/cryptonote_core/blockchain.cpp | 34 +++++++++++++++++++------ src/rpc/core_rpc_server_commands_defs.h | 4 ++- src/wallet/wallet2.cpp | 1 + tests/unit_tests/testdb.h | 2 +- 8 files changed, 45 insertions(+), 18 deletions(-) diff --git a/contrib/epee/include/span.h b/contrib/epee/include/span.h index 174915ecfb7..b1296a0b794 100644 --- a/contrib/epee/include/span.h +++ b/contrib/epee/include/span.h @@ -109,6 +109,8 @@ namespace epee constexpr std::size_t size() const noexcept { return len; } constexpr std::size_t size_bytes() const noexcept { return size() * sizeof(value_type); } + const T &operator[](size_t idx) const { return ptr[idx]; } + private: T* ptr; std::size_t len; diff --git a/src/blockchain_db/blockchain_db.h b/src/blockchain_db/blockchain_db.h index 7118b08810d..53e33898a2b 100644 --- a/src/blockchain_db/blockchain_db.h +++ b/src/blockchain_db/blockchain_db.h @@ -1307,11 +1307,11 @@ class BlockchainDB * get_output_data(const uint64_t& amount, const uint64_t& index) * but for a list of outputs rather than just one. * - * @param amount an output amount + * @param amounts an output amount, or as many as offsets * @param offsets a list of amount-specific output indices * @param outputs return-by-reference a list of outputs' metadata */ - virtual void get_output_key(const uint64_t &amount, const std::vector &offsets, std::vector &outputs, bool allow_partial = false) = 0; + virtual void get_output_key(const epee::span &amounts, const std::vector &offsets, std::vector &outputs, bool allow_partial = false) = 0; /* * FIXME: Need to check with git blame and ask what this does to diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp index ea3638a8585..19f2de4dc1f 100644 --- a/src/blockchain_db/lmdb/db_lmdb.cpp +++ b/src/blockchain_db/lmdb/db_lmdb.cpp @@ -3197,8 +3197,11 @@ void BlockchainLMDB::get_output_tx_and_index_from_global(const std::vector &offsets, std::vector &outputs, bool allow_partial) +void BlockchainLMDB::get_output_key(const epee::span &amounts, const std::vector &offsets, std::vector &outputs, bool allow_partial) { + if (amounts.size() != 1 && amounts.size() != offsets.size()) + throw0(DB_ERROR("Invalid sizes of amounts and offets")); + LOG_PRINT_L3("BlockchainLMDB::" << __func__); TIME_MEASURE_START(db3); check_open(); @@ -3209,10 +3212,11 @@ void BlockchainLMDB::get_output_key(const uint64_t &amount, const std::vector(amount) + ", index " + boost::lexical_cast(index) + ", count " + boost::lexical_cast(get_num_outputs(amount)) + "), but key does not exist (current height " + boost::lexical_cast(height()) + ")").c_str())); + throw1(OUTPUT_DNE((std::string("Attempting to get output pubkey by global index (amount ") + boost::lexical_cast(amount) + ", index " + boost::lexical_cast(offsets[i]) + ", count " + boost::lexical_cast(get_num_outputs(amount)) + "), but key does not exist (current height " + boost::lexical_cast(height()) + ")").c_str())); } else if (get_result) throw0(DB_ERROR(lmdb_error("Error attempting to retrieve an output pubkey from the db", get_result).c_str())); diff --git a/src/blockchain_db/lmdb/db_lmdb.h b/src/blockchain_db/lmdb/db_lmdb.h index 7e76236a5c0..26159ab4d9b 100644 --- a/src/blockchain_db/lmdb/db_lmdb.h +++ b/src/blockchain_db/lmdb/db_lmdb.h @@ -243,7 +243,7 @@ class BlockchainLMDB : public BlockchainDB virtual uint64_t get_num_outputs(const uint64_t& amount) const; virtual output_data_t get_output_key(const uint64_t& amount, const uint64_t& index); - virtual void get_output_key(const uint64_t &amount, const std::vector &offsets, std::vector &outputs, bool allow_partial = false); + virtual void get_output_key(const epee::span &amounts, const std::vector &offsets, std::vector &outputs, bool allow_partial = false); virtual tx_out_index get_output_tx_and_index_from_global(const uint64_t& index) const; virtual void get_output_tx_and_index_from_global(const std::vector &global_indices, diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index e80e3f66c2a..bfc5dbbe091 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -229,7 +229,7 @@ bool Blockchain::scan_outputkeys_for_indexes(size_t tx_version, const txin_to_ke { try { - m_db->get_output_key(tx_in_to_key.amount, absolute_offsets, outputs, true); + m_db->get_output_key(epee::span(&tx_in_to_key.amount, 1), absolute_offsets, outputs, true); if (absolute_offsets.size() != outputs.size()) { MERROR_VER("Output does not exist! amount = " << tx_in_to_key.amount); @@ -255,7 +255,7 @@ bool Blockchain::scan_outputkeys_for_indexes(size_t tx_version, const txin_to_ke add_offsets.push_back(absolute_offsets[i]); try { - m_db->get_output_key(tx_in_to_key.amount, add_offsets, add_outputs, true); + m_db->get_output_key(epee::span(&tx_in_to_key.amount, 1), add_offsets, add_outputs, true); if (add_offsets.size() != add_outputs.size()) { MERROR_VER("Output does not exist! amount = " << tx_in_to_key.amount); @@ -1767,16 +1767,34 @@ bool Blockchain::get_outs(const COMMAND_RPC_GET_OUTPUTS_BIN::request& req, COMMA res.outs.clear(); res.outs.reserve(req.outputs.size()); + + std::vector data; try { + std::vector amounts, offsets; + amounts.reserve(req.outputs.size()); + offsets.reserve(req.outputs.size()); for (const auto &i: req.outputs) { - // get tx_hash, tx_out_index from DB - const output_data_t od = m_db->get_output_key(i.amount, i.index); - tx_out_index toi = m_db->get_output_tx_and_index(i.amount, i.index); - bool unlocked = is_tx_spendtime_unlocked(m_db->get_tx_unlock_time(toi.first)); + amounts.push_back(i.amount); + offsets.push_back(i.index); + } + m_db->get_output_key(epee::span(amounts.data(), amounts.size()), offsets, data); + if (data.size() != req.outputs.size()) + { + MERROR("Unexpected output data size: expected " << req.outputs.size() << ", got " << data.size()); + return false; + } + for (const auto &t: data) + res.outs.push_back({t.pubkey, t.commitment, is_tx_spendtime_unlocked(t.unlock_time), t.height, crypto::null_hash}); - res.outs.push_back({od.pubkey, od.commitment, unlocked, od.height, toi.first}); + if (req.get_txid) + { + for (size_t i = 0; i < req.outputs.size(); ++i) + { + tx_out_index toi = m_db->get_output_tx_and_index(req.outputs[i].amount, req.outputs[i].index); + res.outs[i].txid = toi.first; + } } } catch (const std::exception &e) @@ -3795,7 +3813,7 @@ void Blockchain::output_scan_worker(const uint64_t amount, const std::vectorget_output_key(amount, offsets, outputs, true); + m_db->get_output_key(epee::span(&amount, 1), offsets, outputs, true); } catch (const std::exception& e) { diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index 8e8df7a520b..94caab9f8ac 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -50,7 +50,7 @@ namespace cryptonote // advance which version they will stop working with // Don't go over 32767 for any of these #define CORE_RPC_VERSION_MAJOR 2 -#define CORE_RPC_VERSION_MINOR 1 +#define CORE_RPC_VERSION_MINOR 2 #define MAKE_CORE_RPC_VERSION(major,minor) (((major)<<16)|(minor)) #define CORE_RPC_VERSION MAKE_CORE_RPC_VERSION(CORE_RPC_VERSION_MAJOR, CORE_RPC_VERSION_MINOR) @@ -697,9 +697,11 @@ namespace cryptonote struct request { std::vector outputs; + bool get_txid; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(outputs) + KV_SERIALIZE_OPT(get_txid, true) END_KV_SERIALIZE_MAP() }; diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 31be95a6195..1638e920d51 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -7185,6 +7185,7 @@ void wallet2::get_outs(std::vector> } // get the keys for those + req.get_txid = false; m_daemon_rpc_mutex.lock(); bool r = epee::net_utils::invoke_http_bin("/get_outs.bin", req, daemon_resp, m_http_client, rpc_timeout); m_daemon_rpc_mutex.unlock(); diff --git a/tests/unit_tests/testdb.h b/tests/unit_tests/testdb.h index b6962cc41a9..a9c77292087 100644 --- a/tests/unit_tests/testdb.h +++ b/tests/unit_tests/testdb.h @@ -93,7 +93,7 @@ class BaseTestDB: public cryptonote::BlockchainDB { virtual cryptonote::tx_out_index get_output_tx_and_index_from_global(const uint64_t& index) const { return cryptonote::tx_out_index(); } virtual cryptonote::tx_out_index get_output_tx_and_index(const uint64_t& amount, const uint64_t& index) const { return cryptonote::tx_out_index(); } virtual void get_output_tx_and_index(const uint64_t& amount, const std::vector &offsets, std::vector &indices) const {} - virtual void get_output_key(const uint64_t &amount, const std::vector &offsets, std::vector &outputs, bool allow_partial = false) {} + virtual void get_output_key(const epee::span &amounts, const std::vector &offsets, std::vector &outputs, bool allow_partial = false) {} virtual bool can_thread_bulk_indices() const { return false; } virtual std::vector get_tx_output_indices(const crypto::hash& h) const { return std::vector(); } virtual std::vector get_tx_amount_output_indices(const uint64_t tx_index) const { return std::vector(); } From 0e2a5d75de9a503ae24a347395ae524072d394c6 Mon Sep 17 00:00:00 2001 From: stoffu Date: Wed, 7 Nov 2018 11:19:09 +0900 Subject: [PATCH 0802/1404] simplewallet: use is_transfer_unlocked instead of is_tx_spendtime_unlocked for show_transfers Followup on #4728 --- src/simplewallet/simplewallet.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index d9fd0c13e3f..784ceffe9f6 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -6844,7 +6844,7 @@ bool simple_wallet::get_transfers(std::vector& local_args, std::vec std::string note = m_wallet->get_tx_note(pd.m_tx_hash); std::string destination = m_wallet->get_subaddress_as_str({m_current_subaddress_account, pd.m_subaddr_index.minor}); const std::string type = pd.m_coinbase ? tr("block") : tr("in"); - const bool unlocked = m_wallet->is_tx_spendtime_unlocked(pd.m_unlock_time, pd.m_block_height); + const bool unlocked = m_wallet->is_transfer_unlocked(pd.m_unlock_time, pd.m_block_height); transfers.push_back({ pd.m_block_height, pd.m_timestamp, From e98ae34e4b3487cfa2e7b2278349a49671c2e901 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 26 Nov 2018 23:42:14 +0000 Subject: [PATCH 0803/1404] core: fix adding new pre-hoh block when a tx is already in the pool --- src/cryptonote_core/cryptonote_core.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 10ab3fe65b1..348c788b9b8 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -900,13 +900,15 @@ namespace cryptonote bool ok = true; it = tx_blobs.begin(); for (size_t i = 0; i < tx_blobs.size(); i++, ++it) { - if (already_have[i]) - continue; if (!results[i].res) { ok = false; continue; } + if (keeped_by_block) + get_blockchain_storage().on_new_tx_from_block(results[i].tx); + if (already_have[i]) + continue; const size_t weight = get_transaction_weight(results[i].tx, it->size()); ok &= add_new_tx(results[i].tx, results[i].hash, tx_blobs[i], results[i].prefix_hash, weight, tvc[i], keeped_by_block, relayed, do_not_relay); @@ -1137,9 +1139,6 @@ namespace cryptonote //----------------------------------------------------------------------------------------------- bool core::add_new_tx(transaction& tx, const crypto::hash& tx_hash, const cryptonote::blobdata &blob, const crypto::hash& tx_prefix_hash, size_t tx_weight, tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay) { - if (keeped_by_block) - get_blockchain_storage().on_new_tx_from_block(tx); - if(m_mempool.have_tx(tx_hash)) { LOG_PRINT_L2("tx " << tx_hash << "already have transaction in tx_pool"); From 7cc27b367ecdfe1dca80ef24c6dcc513cb046dba Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 11 Nov 2018 21:31:48 +0000 Subject: [PATCH 0804/1404] Revert "easylogging++: make the logger handle early/late logging" This reverts commit 7f8bdeb35c73c70b2b65e30aa2a1cb93696355b3. --- external/easylogging++/easylogging++.cc | 12 ------------ external/easylogging++/easylogging++.h | 9 ++++----- 2 files changed, 4 insertions(+), 17 deletions(-) diff --git a/external/easylogging++/easylogging++.cc b/external/easylogging++/easylogging++.cc index d57f3f3a0b1..81179b0a33f 100644 --- a/external/easylogging++/easylogging++.cc +++ b/external/easylogging++/easylogging++.cc @@ -2191,17 +2191,6 @@ void VRegistry::setFromArgs(const base::utils::CommandLineArgs* commandLineArgs) # define ELPP_DEFAULT_LOGGING_FLAGS 0x0 #endif // !defined(ELPP_DEFAULT_LOGGING_FLAGS) // Storage -el::base::type::StoragePointer getresetELPP(bool reset) -{ - static el::base::type::StoragePointer p(new el::base::Storage(el::LogBuilderPtr(new el::base::DefaultLogBuilder()))); - if (reset) - p = NULL; - return p; -} -el::base::type::StoragePointer el::base::Storage::getELPP() -{ - return getresetELPP(false); -} #if ELPP_ASYNC_LOGGING Storage::Storage(const LogBuilderPtr& defaultLogBuilder, base::IWorker* asyncDispatchWorker) : #else @@ -2250,7 +2239,6 @@ Storage::Storage(const LogBuilderPtr& defaultLogBuilder) : Storage::~Storage(void) { ELPP_INTERNAL_INFO(4, "Destroying storage"); - getresetELPP(true); #if ELPP_ASYNC_LOGGING ELPP_INTERNAL_INFO(5, "Replacing log dispatch callback to synchronous"); uninstallLogDispatchCallback(std::string("AsyncLogDispatchCallback")); diff --git a/external/easylogging++/easylogging++.h b/external/easylogging++/easylogging++.h index 046252a5b3e..375010d461b 100644 --- a/external/easylogging++/easylogging++.h +++ b/external/easylogging++/easylogging++.h @@ -2734,8 +2734,6 @@ class Storage : base::NoCopy, public base::threading::ThreadSafe { return it->second; } - static el::base::type::StoragePointer getELPP(); - private: base::RegisteredHitCounters* m_registeredHitCounters; base::RegisteredLoggers* m_registeredLoggers; @@ -2770,7 +2768,7 @@ class Storage : base::NoCopy, public base::threading::ThreadSafe { } }; extern ELPP_EXPORT base::type::StoragePointer elStorage; -#define ELPP el::base::Storage::getELPP() +#define ELPP el::base::elStorage class DefaultLogDispatchCallback : public LogDispatchCallback { protected: void handle(const LogDispatchData* data); @@ -4613,9 +4611,10 @@ el::base::debug::CrashHandler elCrashHandler(ELPP_USE_DEF_CRASH_HANDLER); \ } #if ELPP_ASYNC_LOGGING -# define INITIALIZE_EASYLOGGINGPP ELPP_INIT_EASYLOGGINGPP(NULL) +# define INITIALIZE_EASYLOGGINGPP ELPP_INIT_EASYLOGGINGPP(new el::base::Storage(el::LogBuilderPtr(new el::base::DefaultLogBuilder()),\ +new el::base::AsyncDispatchWorker())) #else -# define INITIALIZE_EASYLOGGINGPP ELPP_INIT_EASYLOGGINGPP(NULL) +# define INITIALIZE_EASYLOGGINGPP ELPP_INIT_EASYLOGGINGPP(new el::base::Storage(el::LogBuilderPtr(new el::base::DefaultLogBuilder()))) #endif // ELPP_ASYNC_LOGGING #define INITIALIZE_NULL_EASYLOGGINGPP \ namespace el {\ From 721aacd88e780b30416deb66a68b87da8fe244ae Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 11 Nov 2018 21:34:37 +0000 Subject: [PATCH 0805/1404] easylogging++: faster access to logging Turns out getting the global shared_ptr hits the profile, and passing it around still keeps it at close to ~1% CPU, which is too much for mostly silent logging. Leak the object instead, which is even safer for late logging. --- contrib/valgrind/monero.supp | 9 +++++++++ external/easylogging++/easylogging++.cc | 6 ++++++ external/easylogging++/easylogging++.h | 6 ++++-- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/contrib/valgrind/monero.supp b/contrib/valgrind/monero.supp index 16e34e82feb..015b05a1c17 100644 --- a/contrib/valgrind/monero.supp +++ b/contrib/valgrind/monero.supp @@ -17,3 +17,12 @@ fun:maybe_unlock_and_signal_one > ... } + +{ + we leak the logger, for performance reasons in on-the-fly init + Memcheck:Leak + match-leak-kinds: definite + fun:_Znwm + fun:_ZN2el4base7Storage7getELPPEv + ... +} diff --git a/external/easylogging++/easylogging++.cc b/external/easylogging++/easylogging++.cc index 81179b0a33f..c24fb80cf22 100644 --- a/external/easylogging++/easylogging++.cc +++ b/external/easylogging++/easylogging++.cc @@ -2191,6 +2191,12 @@ void VRegistry::setFromArgs(const base::utils::CommandLineArgs* commandLineArgs) # define ELPP_DEFAULT_LOGGING_FLAGS 0x0 #endif // !defined(ELPP_DEFAULT_LOGGING_FLAGS) // Storage +el::base::type::StoragePointer &el::base::Storage::getELPP() +{ + if (!el::base::elStorage) + el::base::elStorage = new el::base::Storage(el::LogBuilderPtr(new el::base::DefaultLogBuilder())); + return el::base::elStorage; +} #if ELPP_ASYNC_LOGGING Storage::Storage(const LogBuilderPtr& defaultLogBuilder, base::IWorker* asyncDispatchWorker) : #else diff --git a/external/easylogging++/easylogging++.h b/external/easylogging++/easylogging++.h index 375010d461b..acf2a767409 100644 --- a/external/easylogging++/easylogging++.h +++ b/external/easylogging++/easylogging++.h @@ -552,7 +552,7 @@ typedef std::ostream ostream_t; typedef unsigned int EnumType; typedef unsigned short VerboseLevel; typedef unsigned long int LineNumber; -typedef std::shared_ptr StoragePointer; +typedef base::Storage *StoragePointer; typedef std::shared_ptr LogDispatchCallbackPtr; typedef std::shared_ptr PerformanceTrackingCallbackPtr; typedef std::shared_ptr LoggerRegistrationCallbackPtr; @@ -2734,6 +2734,8 @@ class Storage : base::NoCopy, public base::threading::ThreadSafe { return it->second; } + static el::base::type::StoragePointer &getELPP(); + private: base::RegisteredHitCounters* m_registeredHitCounters; base::RegisteredLoggers* m_registeredLoggers; @@ -2768,7 +2770,7 @@ class Storage : base::NoCopy, public base::threading::ThreadSafe { } }; extern ELPP_EXPORT base::type::StoragePointer elStorage; -#define ELPP el::base::elStorage +#define ELPP el::base::Storage::getELPP() class DefaultLogDispatchCallback : public LogDispatchCallback { protected: void handle(const LogDispatchData* data); From 5ca4994c9c31228139317c4278e1868598a0b975 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 7 Nov 2018 14:57:41 +0000 Subject: [PATCH 0806/1404] rpc: speed up the common get_output_distribution case while syncing --- src/rpc/rpc_handler.cpp | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/rpc/rpc_handler.cpp b/src/rpc/rpc_handler.cpp index 63664bf8b08..e0a81c70f4e 100644 --- a/src/rpc/rpc_handler.cpp +++ b/src/rpc/rpc_handler.cpp @@ -43,8 +43,25 @@ namespace rpc std::vector distribution; std::uint64_t start_height, base; - if (!f(amount, from_height, to_height, start_height, distribution, base)) - return boost::none; + + // see if we can extend the cache - a common case + if (d.cached && amount == 0 && d.cached_from == from_height && to_height > d.cached_to) + { + std::vector new_distribution; + if (!f(amount, d.cached_to + 1, to_height, start_height, new_distribution, base)) + return boost::none; + distribution = d.cached_distribution; + distribution.reserve(distribution.size() + new_distribution.size()); + for (const auto &e: new_distribution) + distribution.push_back(e); + start_height = d.cached_start_height; + base = d.cached_base; + } + else + { + if (!f(amount, from_height, to_height, start_height, distribution, base)) + return boost::none; + } if (to_height > 0 && to_height >= from_height) { From 756684bb28482d953f822bbb9940c9b482b40054 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 10 Nov 2018 13:46:37 +0000 Subject: [PATCH 0807/1404] blockchain: avoid unnecessary DB lookups when syncing Some of the inputs for block in a span will be from other earlier blocks in that span. Keep track of those outputs so we don't have to look them up again after those early blocks are added to the blockchain. --- src/cryptonote_core/blockchain.cpp | 117 +++++++++++++++++++++-------- src/cryptonote_core/blockchain.h | 4 +- 2 files changed, 86 insertions(+), 35 deletions(-) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index bfc5dbbe091..a6e35acc865 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -3723,7 +3723,7 @@ void Blockchain::set_enforce_dns_checkpoints(bool enforce_checkpoints) } //------------------------------------------------------------------ -void Blockchain::block_longhash_worker(uint64_t height, const std::vector &blocks, std::unordered_map &map) const +void Blockchain::block_longhash_worker(uint64_t height, const epee::span &blocks, std::unordered_map &map) const { TIME_MEASURE_START(t); slow_hash_allocate_state(); @@ -3809,11 +3809,33 @@ bool Blockchain::cleanup_handle_incoming_blocks(bool force_sync) } //------------------------------------------------------------------ -void Blockchain::output_scan_worker(const uint64_t amount, const std::vector &offsets, std::vector &outputs) const +void Blockchain::output_scan_worker(const uint64_t amount, const std::vector &offsets, std::vector &outputs, const std::vector &extra_tx_map) const { try { m_db->get_output_key(epee::span(&amount, 1), offsets, outputs, true); + if (outputs.size() < offsets.size()) + { + const uint64_t n_outputs = m_db->get_num_outputs(amount); + for (size_t i = outputs.size(); i < offsets.size(); ++i) + { + uint64_t idx = offsets[i]; + if (idx < n_outputs) + { + MWARNING("Index " << idx << " not found in db for amount " << amount << ", but it is less than the number of entries"); + break; + } + else if (idx < n_outputs + extra_tx_map.size()) + { + outputs.push_back(extra_tx_map[idx - n_outputs]); + } + else + { + MWARNING("missed " << amount << "/" << idx << " in " << extra_tx_map.size() << " (chain " << n_outputs << ")"); + break; + } + } + } } catch (const std::exception& e) { @@ -3928,6 +3950,34 @@ uint64_t Blockchain::prevalidate_block_hashes(uint64_t height, const std::vector // vs [k_image, output_keys] (m_scan_table). This is faster because it takes advantage of bulk queries // and is threaded if possible. The table (m_scan_table) will be used later when querying output // keys. +static bool update_output_map(std::map> &extra_tx_map, const transaction &tx, uint64_t height, bool miner) +{ + MTRACE("Blockchain::" << __func__); + for (size_t i = 0; i < tx.vout.size(); ++i) + { + const auto &out = tx.vout[i]; + if (out.target.type() != typeid(txout_to_key)) + continue; + const txout_to_key &out_to_key = boost::get(out.target); + rct::key commitment; + uint64_t amount = out.amount; + if (miner && tx.version == 2) + { + commitment = rct::zeroCommit(amount); + amount = 0; + } + else if (tx.version > 1) + { + CHECK_AND_ASSERT_MES(i < tx.rct_signatures.outPk.size(), false, "Invalid outPk size"); + commitment = tx.rct_signatures.outPk[i].mask; + } + else + commitment = rct::zero(); + extra_tx_map[amount].push_back(output_data_t{out_to_key.key, tx.unlock_time, height, commitment}); + } + return true; +} + bool Blockchain::prepare_handle_incoming_blocks(const std::vector &blocks_entry) { MTRACE("Blockchain::" << __func__); @@ -3974,42 +4024,40 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vectorheight() + blocks_entry.size()) < m_blocks_hash_check.size()) + const uint64_t height = m_db->height(); + if ((height + blocks_entry.size()) < m_blocks_hash_check.size()) return true; bool blocks_exist = false; tools::threadpool& tpool = tools::threadpool::getInstance(); - uint64_t threads = tpool.get_max_concurrency(); + unsigned threads = tpool.get_max_concurrency(); + std::vector blocks; + blocks.resize(blocks_entry.size()); - if (blocks_entry.size() > 1 && threads > 1 && m_max_prepare_blocks_threads > 1) + if (1) { // limit threads, default limit = 4 if(threads > m_max_prepare_blocks_threads) threads = m_max_prepare_blocks_threads; - uint64_t height = m_db->height(); - int batches = blocks_entry.size() / threads; - int extra = blocks_entry.size() % threads; + unsigned int batches = blocks_entry.size() / threads; + unsigned int extra = blocks_entry.size() % threads; MDEBUG("block_batches: " << batches); std::vector> maps(threads); - std::vector < std::vector < block >> blocks(threads); auto it = blocks_entry.begin(); + unsigned blockidx = 0; - for (uint64_t i = 0; i < threads; i++) + for (unsigned i = 0; i < threads; i++) { - blocks[i].reserve(batches + 1); - for (int j = 0; j < batches; j++) + for (unsigned int j = 0; j < batches; j++, ++blockidx) { - block block; + block &block = blocks[blockidx]; if (!parse_and_validate_block_from_blob(it->block, block)) - { - std::advance(it, 1); - continue; - } + return false; // check first block and skip all blocks if its not chained properly - if (i == 0 && j == 0) + if (blockidx == 0) { crypto::hash tophash = m_db->top_block_hash(); if (block.prev_id != tophash) @@ -4024,20 +4072,16 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vectorblock, block)) - { - std::advance(it, 1); - continue; - } + return false; if (have_block(get_block_hash(block))) { @@ -4045,7 +4089,6 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector(&blocks[i], nblocks), std::ref(maps[i])), true); + thread_height += nblocks; } waiter.wait(&tpool); @@ -4100,7 +4146,7 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector> offset_map; // [output] stores all output_data_t for each absolute_offset - std::map> tx_map; + std::map> tx_map, extra_tx_map; std::vector> txes(total_txs); #define SCAN_TABLE_QUIT(m) \ @@ -4111,12 +4157,14 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector= txes.size()) @@ -4175,7 +4223,10 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector &offsets, - std::vector &outputs) const; + std::vector &outputs, const std::vector &extra_tx_map) const; /** * @brief computes the "short" and "long" hashes for a set of blocks @@ -927,7 +927,7 @@ namespace cryptonote * @param blocks the blocks to be hashed * @param map return-by-reference the hashes for each block */ - void block_longhash_worker(uint64_t height, const std::vector &blocks, + void block_longhash_worker(uint64_t height, const epee::span &blocks, std::unordered_map &map) const; /** From 17b45725af754a3887f0c59bd5c67c58278d71eb Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 24 Nov 2018 14:49:04 +0000 Subject: [PATCH 0808/1404] Outputs where all amounts are known spent can now be pruned Only for pre rct for obvious reasons. Note: DO NOT use a known spent list which includes outputs which are not known spent. If the list includes any output that's just strongly thought to be spent, but not provably so, you risk finding yourself unable to sync past the point where that output is spent. I estimate only 200 MB saved on current mainnet though, unless the new blackballing rule unearths a good amount of large-amount-set extra spent outs. --- src/blockchain_db/blockchain_db.h | 7 + src/blockchain_db/lmdb/db_lmdb.cpp | 70 ++++- src/blockchain_db/lmdb/db_lmdb.h | 2 + src/blockchain_utilities/CMakeLists.txt | 33 +++ .../blockchain_prune_known_spent_data.cpp | 268 ++++++++++++++++++ tests/unit_tests/testdb.h | 1 + 6 files changed, 377 insertions(+), 4 deletions(-) create mode 100644 src/blockchain_utilities/blockchain_prune_known_spent_data.cpp diff --git a/src/blockchain_db/blockchain_db.h b/src/blockchain_db/blockchain_db.h index 53e33898a2b..a8f4eafe0ea 100644 --- a/src/blockchain_db/blockchain_db.h +++ b/src/blockchain_db/blockchain_db.h @@ -1405,6 +1405,13 @@ class BlockchainDB */ virtual cryptonote::blobdata get_txpool_tx_blob(const crypto::hash& txid) const = 0; + /** + * @brief prune output data for the given amount + * + * @param amount the amount for which to prune data + */ + virtual void prune_outputs(uint64_t amount) = 0; + /** * @brief runs a function over all txpool transactions * diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp index d260caa7572..142e86b5db1 100644 --- a/src/blockchain_db/lmdb/db_lmdb.cpp +++ b/src/blockchain_db/lmdb/db_lmdb.cpp @@ -1077,6 +1077,60 @@ void BlockchainLMDB::remove_output(const uint64_t amount, const uint64_t& out_in throw0(DB_ERROR(lmdb_error(std::string("Error deleting amount for output index ").append(boost::lexical_cast(out_index).append(": ")).c_str(), result).c_str())); } +void BlockchainLMDB::prune_outputs(uint64_t amount) +{ + LOG_PRINT_L3("BlockchainLMDB::" << __func__); + check_open(); + mdb_txn_cursors *m_cursors = &m_wcursors; + CURSOR(output_amounts); + CURSOR(output_txs); + + MINFO("Pruning outputs for amount " << amount); + + MDB_val v; + MDB_val_set(k, amount); + int result = mdb_cursor_get(m_cur_output_amounts, &k, &v, MDB_SET); + if (result == MDB_NOTFOUND) + return; + if (result) + throw0(DB_ERROR(lmdb_error("Error looking up outputs: ", result).c_str())); + + // gather output ids + mdb_size_t num_elems; + mdb_cursor_count(m_cur_output_amounts, &num_elems); + MINFO(num_elems << " outputs found"); + std::vector output_ids; + output_ids.reserve(num_elems); + while (1) + { + const pre_rct_outkey *okp = (const pre_rct_outkey *)v.mv_data; + output_ids.push_back(okp->output_id); + MDEBUG("output id " << okp->output_id); + result = mdb_cursor_get(m_cur_output_amounts, &k, &v, MDB_NEXT_DUP); + if (result == MDB_NOTFOUND) + break; + if (result) + throw0(DB_ERROR(lmdb_error("Error counting outputs: ", result).c_str())); + } + if (output_ids.size() != num_elems) + throw0(DB_ERROR("Unexpected number of outputs")); + + result = mdb_cursor_del(m_cur_output_amounts, MDB_NODUPDATA); + if (result) + throw0(DB_ERROR(lmdb_error("Error deleting outputs: ", result).c_str())); + + for (uint64_t output_id: output_ids) + { + MDB_val_set(v, output_id); + result = mdb_cursor_get(m_cur_output_txs, (MDB_val *)&zerokval, &v, MDB_GET_BOTH); + if (result) + throw0(DB_ERROR(lmdb_error("Error looking up output: ", result).c_str())); + result = mdb_cursor_del(m_cur_output_txs, 0); + if (result) + throw0(DB_ERROR(lmdb_error("Error deleting output: ", result).c_str())); + } +} + void BlockchainLMDB::add_spent_key(const crypto::key_image& k_image) { LOG_PRINT_L3("BlockchainLMDB::" << __func__); @@ -2231,11 +2285,19 @@ uint64_t BlockchainLMDB::num_outputs() const TXN_PREFIX_RDONLY(); int result; - // get current height - MDB_stat db_stats; - if ((result = mdb_stat(m_txn, m_output_txs, &db_stats))) + RCURSOR(output_txs) + + uint64_t num = 0; + MDB_val k, v; + result = mdb_cursor_get(m_cur_output_txs, &k, &v, MDB_LAST); + if (result == MDB_NOTFOUND) + num = 0; + else if (result == 0) + num = 1 + ((const outtx*)v.mv_data)->output_id; + else throw0(DB_ERROR(lmdb_error("Failed to query m_output_txs: ", result).c_str())); - return db_stats.ms_entries; + + return num; } bool BlockchainLMDB::tx_exists(const crypto::hash& h) const diff --git a/src/blockchain_db/lmdb/db_lmdb.h b/src/blockchain_db/lmdb/db_lmdb.h index 26159ab4d9b..6db2412405c 100644 --- a/src/blockchain_db/lmdb/db_lmdb.h +++ b/src/blockchain_db/lmdb/db_lmdb.h @@ -345,6 +345,8 @@ class BlockchainLMDB : public BlockchainDB void remove_output(const uint64_t amount, const uint64_t& out_index); + virtual void prune_outputs(uint64_t amount); + virtual void add_spent_key(const crypto::key_image& k_image); virtual void remove_spent_key(const crypto::key_image& k_image); diff --git a/src/blockchain_utilities/CMakeLists.txt b/src/blockchain_utilities/CMakeLists.txt index c9ad1cebe84..ddf575c29d1 100644 --- a/src/blockchain_utilities/CMakeLists.txt +++ b/src/blockchain_utilities/CMakeLists.txt @@ -81,6 +81,17 @@ monero_private_headers(blockchain_usage +set(blockchain_prune_known_spent_data_sources + blockchain_prune_known_spent_data.cpp + ) + +set(blockchain_prune_known_spent_data_private_headers) + +monero_private_headers(blockchain_prune_known_spent_data + ${blockchain_prune_known_spent_data_private_headers}) + + + set(blockchain_ancestry_sources blockchain_ancestry.cpp ) @@ -265,3 +276,25 @@ set_property(TARGET blockchain_stats PROPERTY OUTPUT_NAME "monero-blockchain-stats") install(TARGETS blockchain_stats DESTINATION bin) + +monero_add_executable(blockchain_prune_known_spent_data + ${blockchain_prune_known_spent_data_sources} + ${blockchain_prune_known_spent_data_private_headers}) + +target_link_libraries(blockchain_prune_known_spent_data + PRIVATE + cryptonote_core + blockchain_db + p2p + version + epee + ${Boost_FILESYSTEM_LIBRARY} + ${Boost_SYSTEM_LIBRARY} + ${Boost_THREAD_LIBRARY} + ${CMAKE_THREAD_LIBS_INIT} + ${EXTRA_LIBRARIES}) + +set_property(TARGET blockchain_prune_known_spent_data + PROPERTY + OUTPUT_NAME "monero-blockchain-prune-known-spent-data") +install(TARGETS blockchain_prune_known_spent_data DESTINATION bin) diff --git a/src/blockchain_utilities/blockchain_prune_known_spent_data.cpp b/src/blockchain_utilities/blockchain_prune_known_spent_data.cpp new file mode 100644 index 00000000000..1a5869e9d9d --- /dev/null +++ b/src/blockchain_utilities/blockchain_prune_known_spent_data.cpp @@ -0,0 +1,268 @@ +// Copyright (c) 2014-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include +#include "common/command_line.h" +#include "serialization/crypto.h" +#include "cryptonote_core/tx_pool.h" +#include "cryptonote_core/cryptonote_core.h" +#include "cryptonote_core/blockchain.h" +#include "blockchain_db/blockchain_db.h" +#include "blockchain_db/db_types.h" +#include "version.h" + +#undef MONERO_DEFAULT_LOG_CATEGORY +#define MONERO_DEFAULT_LOG_CATEGORY "bcutil" + +namespace po = boost::program_options; +using namespace epee; +using namespace cryptonote; + +static std::map load_outputs(const std::string &filename) +{ + std::map outputs; + uint64_t amount = std::numeric_limits::max(); + FILE *f; + + f = fopen(filename.c_str(), "r"); + if (!f) + { + MERROR("Failed to load outputs from " << filename << ": " << strerror(errno)); + return {}; + } + while (1) + { + char s[256]; + if (!fgets(s, sizeof(s), f)) + break; + if (feof(f)) + break; + const size_t len = strlen(s); + if (len > 0 && s[len - 1] == '\n') + s[len - 1] = 0; + if (!s[0]) + continue; + std::pair output; + uint64_t offset, num_offsets; + if (sscanf(s, "@%" PRIu64, &amount) == 1) + { + continue; + } + if (amount == std::numeric_limits::max()) + { + MERROR("Bad format in " << filename); + continue; + } + if (sscanf(s, "%" PRIu64 "*%" PRIu64, &offset, &num_offsets) == 2 && num_offsets < std::numeric_limits::max() - offset) + { + outputs[amount] += num_offsets; + } + else if (sscanf(s, "%" PRIu64, &offset) == 1) + { + outputs[amount] += 1; + } + else + { + MERROR("Bad format in " << filename); + continue; + } + } + fclose(f); + return outputs; +} + +int main(int argc, char* argv[]) +{ + TRY_ENTRY(); + + epee::string_tools::set_module_name_and_folder(argv[0]); + + std::string default_db_type = "lmdb"; + + std::string available_dbs = cryptonote::blockchain_db_types(", "); + available_dbs = "available: " + available_dbs; + + uint32_t log_level = 0; + + tools::on_startup(); + + po::options_description desc_cmd_only("Command line options"); + po::options_description desc_cmd_sett("Command line options and settings options"); + const command_line::arg_descriptor arg_log_level = {"log-level", "0-4 or categories", ""}; + const command_line::arg_descriptor arg_database = { + "database", available_dbs.c_str(), default_db_type + }; + const command_line::arg_descriptor arg_verbose = {"verbose", "Verbose output", false}; + const command_line::arg_descriptor arg_dry_run = {"dry-run", "Do not actually prune", false}; + const command_line::arg_descriptor arg_input = {"input", "Path to the known spent outputs file"}; + + command_line::add_arg(desc_cmd_sett, cryptonote::arg_data_dir); + command_line::add_arg(desc_cmd_sett, cryptonote::arg_testnet_on); + command_line::add_arg(desc_cmd_sett, cryptonote::arg_stagenet_on); + command_line::add_arg(desc_cmd_sett, arg_log_level); + command_line::add_arg(desc_cmd_sett, arg_database); + command_line::add_arg(desc_cmd_sett, arg_verbose); + command_line::add_arg(desc_cmd_sett, arg_dry_run); + command_line::add_arg(desc_cmd_sett, arg_input); + command_line::add_arg(desc_cmd_only, command_line::arg_help); + + po::options_description desc_options("Allowed options"); + desc_options.add(desc_cmd_only).add(desc_cmd_sett); + + po::variables_map vm; + bool r = command_line::handle_error_helper(desc_options, [&]() + { + auto parser = po::command_line_parser(argc, argv).options(desc_options); + po::store(parser.run(), vm); + po::notify(vm); + return true; + }); + if (! r) + return 1; + + if (command_line::get_arg(vm, command_line::arg_help)) + { + std::cout << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")" << ENDL << ENDL; + std::cout << desc_options << std::endl; + return 1; + } + + mlog_configure(mlog_get_default_log_path("monero-blockchain-prune-known-spent-data.log"), true); + if (!command_line::is_arg_defaulted(vm, arg_log_level)) + mlog_set_log(command_line::get_arg(vm, arg_log_level).c_str()); + else + mlog_set_log(std::string(std::to_string(log_level) + ",bcutil:INFO").c_str()); + + LOG_PRINT_L0("Starting..."); + + std::string opt_data_dir = command_line::get_arg(vm, cryptonote::arg_data_dir); + bool opt_testnet = command_line::get_arg(vm, cryptonote::arg_testnet_on); + bool opt_stagenet = command_line::get_arg(vm, cryptonote::arg_stagenet_on); + network_type net_type = opt_testnet ? TESTNET : opt_stagenet ? STAGENET : MAINNET; + bool opt_verbose = command_line::get_arg(vm, arg_verbose); + bool opt_dry_run = command_line::get_arg(vm, arg_dry_run); + + std::string db_type = command_line::get_arg(vm, arg_database); + if (!cryptonote::blockchain_valid_db_type(db_type)) + { + std::cerr << "Invalid database type: " << db_type << std::endl; + return 1; + } + + const std::string input = command_line::get_arg(vm, arg_input); + if (input.empty()) + { + LOG_PRINT_L0("No input given"); + return 1; + } + + LOG_PRINT_L0("Initializing source blockchain (BlockchainDB)"); + std::unique_ptr core_storage; + tx_memory_pool m_mempool(*core_storage); + core_storage.reset(new Blockchain(m_mempool)); + BlockchainDB *db = new_db(db_type); + if (db == NULL) + { + LOG_ERROR("Attempted to use non-existent database type: " << db_type); + throw std::runtime_error("Attempting to use non-existent database type"); + } + LOG_PRINT_L0("database: " << db_type); + + const std::string filename = (boost::filesystem::path(opt_data_dir) / db->get_db_name()).string(); + LOG_PRINT_L0("Loading blockchain from folder " << filename << " ..."); + + try + { + db->open(filename, 0); + } + catch (const std::exception& e) + { + LOG_PRINT_L0("Error opening database: " << e.what()); + return 1; + } + r = core_storage->init(db, net_type); + + CHECK_AND_ASSERT_MES(r, 1, "Failed to initialize source blockchain storage"); + LOG_PRINT_L0("Source blockchain storage initialized OK"); + + LOG_PRINT_L0("Loading known spent data..."); + const std::map known_spent_outputs = load_outputs(input); + + LOG_PRINT_L0("Pruning known spent data..."); + + bool stop_requested = false; + tools::signal_handler::install([&stop_requested](int type) { + stop_requested = true; + }); + + db->batch_start(); + + size_t num_total_outputs = 0, num_prunable_outputs = 0, num_known_spent_outputs = 0, num_eligible_outputs = 0, num_eligible_known_spent_outputs = 0; + for (auto i = known_spent_outputs.begin(); i != known_spent_outputs.end(); ++i) + { + uint64_t num_outputs = db->get_num_outputs(i->first); + num_total_outputs += num_outputs; + num_known_spent_outputs += i->second; + if (i->first == 0 || is_valid_decomposed_amount(i->first)) + { + if (opt_verbose) + MINFO("Ignoring output value " << i->first << ", with " << num_outputs << " outputs"); + continue; + } + num_eligible_outputs += num_outputs; + num_eligible_known_spent_outputs += i->second; + if (opt_verbose) + MINFO(i->first << ": " << i->second << "/" << num_outputs); + if (num_outputs > i->second) + continue; + if (num_outputs && num_outputs < i->second) + { + MERROR("More outputs are spent than known for amount " << i->first << ", not touching"); + continue; + } + if (opt_verbose) + MINFO("Pruning data for " << num_outputs << " outputs"); + if (!opt_dry_run) + db->prune_outputs(i->first); + num_prunable_outputs += i->second; + } + + db->batch_stop(); + + MINFO("Total outputs: " << num_total_outputs); + MINFO("Known spent outputs: " << num_known_spent_outputs); + MINFO("Eligible outputs: " << num_eligible_outputs); + MINFO("Eligible known spent outputs: " << num_eligible_known_spent_outputs); + MINFO("Prunable outputs: " << num_prunable_outputs); + + LOG_PRINT_L0("Blockchain known spent data pruned OK"); + core_storage->deinit(); + return 0; + + CATCH_ENTRY("Error", 1); +} diff --git a/tests/unit_tests/testdb.h b/tests/unit_tests/testdb.h index a9c77292087..5d9ba583369 100644 --- a/tests/unit_tests/testdb.h +++ b/tests/unit_tests/testdb.h @@ -142,5 +142,6 @@ class BaseTestDB: public cryptonote::BlockchainDB { virtual bool prune_blockchain(uint32_t pruning_seed = 0) { return true; } virtual bool update_pruning() { return true; } virtual bool check_pruning() { return true; } + virtual void prune_outputs(uint64_t amount) {} }; From a48f2dab00a81ab716f38022dc3197d841b63b7e Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 27 Nov 2018 15:44:01 +0000 Subject: [PATCH 0809/1404] blockchain_prune_known_spent_data: blackball file is now optional If not present, the tool will scan the blockchain, since scanning for this is fairly fast. --- .../blockchain_prune_known_spent_data.cpp | 51 ++++++++++++++++--- 1 file changed, 44 insertions(+), 7 deletions(-) diff --git a/src/blockchain_utilities/blockchain_prune_known_spent_data.cpp b/src/blockchain_utilities/blockchain_prune_known_spent_data.cpp index 1a5869e9d9d..f6136c1ba7e 100644 --- a/src/blockchain_utilities/blockchain_prune_known_spent_data.cpp +++ b/src/blockchain_utilities/blockchain_prune_known_spent_data.cpp @@ -175,11 +175,6 @@ int main(int argc, char* argv[]) } const std::string input = command_line::get_arg(vm, arg_input); - if (input.empty()) - { - LOG_PRINT_L0("No input given"); - return 1; - } LOG_PRINT_L0("Initializing source blockchain (BlockchainDB)"); std::unique_ptr core_storage; @@ -210,8 +205,50 @@ int main(int argc, char* argv[]) CHECK_AND_ASSERT_MES(r, 1, "Failed to initialize source blockchain storage"); LOG_PRINT_L0("Source blockchain storage initialized OK"); - LOG_PRINT_L0("Loading known spent data..."); - const std::map known_spent_outputs = load_outputs(input); + std::map known_spent_outputs; + if (input.empty()) + { + std::map> outputs; + + LOG_PRINT_L0("Scanning for known spent data..."); + db->for_all_transactions([&](const crypto::hash &txid, const cryptonote::transaction &tx){ + const bool miner_tx = tx.vin.size() == 1 && tx.vin[0].type() == typeid(txin_gen); + for (const auto &in: tx.vin) + { + if (in.type() != typeid(txin_to_key)) + continue; + const auto &txin = boost::get(in); + if (txin.amount == 0) + continue; + + outputs[txin.amount].second++; + } + + for (const auto &out: tx.vout) + { + uint64_t amount = out.amount; + if (miner_tx && tx.version >= 2) + amount = 0; + if (amount == 0) + continue; + if (out.target.type() != typeid(txout_to_key)) + continue; + + outputs[amount].first++; + } + return true; + }, true); + + for (const auto &i: outputs) + { + known_spent_outputs[i.first] = i.second.second; + } + } + else + { + LOG_PRINT_L0("Loading known spent data..."); + known_spent_outputs = load_outputs(input); + } LOG_PRINT_L0("Pruning known spent data..."); From dc1c12528d5cf86759993614e1fdd7d7bf04ff15 Mon Sep 17 00:00:00 2001 From: Jason Wong Date: Sun, 25 Nov 2018 22:08:07 +0100 Subject: [PATCH 0810/1404] add command pop_blocks add new public method to Blockchain and update according to code review update after review: better lock/unlock, try catch and coding style --- src/cryptonote_core/blockchain.cpp | 32 +++++++++++++++++++++++++ src/cryptonote_core/blockchain.h | 7 ++++++ src/daemon/command_parser_executor.cpp | 25 +++++++++++++++++++ src/daemon/command_parser_executor.h | 2 ++ src/daemon/command_server.cpp | 6 +++++ src/daemon/rpc_command_executor.cpp | 27 +++++++++++++++++++++ src/daemon/rpc_command_executor.h | 2 ++ src/rpc/core_rpc_server.cpp | 12 ++++++++++ src/rpc/core_rpc_server.h | 2 ++ src/rpc/core_rpc_server_commands_defs.h | 23 ++++++++++++++++++ 10 files changed, 138 insertions(+) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index bfc5dbbe091..009c5e2ed76 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -576,6 +576,38 @@ bool Blockchain::deinit() return true; } //------------------------------------------------------------------ +// This function removes blocks from the top of blockchain. +// It starts a batch and calls private method pop_block_from_blockchain(). +void Blockchain::pop_blocks(uint64_t nblocks) +{ + uint64_t i; + CRITICAL_REGION_LOCAL(m_tx_pool); + CRITICAL_REGION_LOCAL1(m_blockchain_lock); + + while (!m_db->batch_start()) + { + m_blockchain_lock.unlock(); + m_tx_pool.unlock(); + epee::misc_utils::sleep_no_w(1000); + m_tx_pool.lock(); + m_blockchain_lock.lock(); + } + + try + { + for (i=0; i < nblocks; ++i) + { + pop_block_from_blockchain(); + } + } + catch (const std::exception& e) + { + LOG_ERROR("Error when popping blocks, only " << i << " blocks are popped: " << e.what()); + } + + m_db->batch_stop(); +} +//------------------------------------------------------------------ // This function tells BlockchainDB to remove the top block from the // blockchain and then returns all transactions (except the miner tx, of course) // from it to the tx_pool diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index dfe833fb45f..f1e366c9ef7 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -967,6 +967,13 @@ namespace cryptonote */ std::vector get_last_block_timestamps(unsigned int blocks) const; + /** + * @brief removes blocks from the top of the blockchain + * + * @param nblocks number of blocks to be removed + */ + void pop_blocks(uint64_t nblocks); + private: // TODO: evaluate whether or not each of these typedefs are left over from blockchain_storage diff --git a/src/daemon/command_parser_executor.cpp b/src/daemon/command_parser_executor.cpp index 1638cf505ab..853cde9c393 100644 --- a/src/daemon/command_parser_executor.cpp +++ b/src/daemon/command_parser_executor.cpp @@ -674,6 +674,31 @@ bool t_command_parser_executor::sync_info(const std::vector& args) return m_executor.sync_info(); } +bool t_command_parser_executor::pop_blocks(const std::vector& args) +{ + if (args.size() != 1) + { + std::cout << "Exactly one parameter is needed" << std::endl; + return false; + } + + try + { + uint64_t nblocks = boost::lexical_cast(args[0]); + if (nblocks < 1) + { + std::cout << "number of blocks must be greater than 0" << std::endl; + return false; + } + return m_executor.pop_blocks(nblocks); + } + catch (const boost::bad_lexical_cast&) + { + std::cout << "number of blocks must be a number greater than 0" << std::endl; + } + return false; +} + bool t_command_parser_executor::version(const std::vector& args) { std::cout << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")" << std::endl; diff --git a/src/daemon/command_parser_executor.h b/src/daemon/command_parser_executor.h index a700701716c..e2844e8b75b 100644 --- a/src/daemon/command_parser_executor.h +++ b/src/daemon/command_parser_executor.h @@ -139,6 +139,8 @@ class t_command_parser_executor final bool sync_info(const std::vector& args); + bool pop_blocks(const std::vector& args); + bool version(const std::vector& args); }; diff --git a/src/daemon/command_server.cpp b/src/daemon/command_server.cpp index 35504f2c903..527ed2cf1fb 100644 --- a/src/daemon/command_server.cpp +++ b/src/daemon/command_server.cpp @@ -281,6 +281,12 @@ t_command_server::t_command_server( , std::bind(&t_command_parser_executor::sync_info, &m_parser, p::_1) , "Print information about the blockchain sync state." ); + m_command_lookup.set_handler( + "pop_blocks" + , std::bind(&t_command_parser_executor::pop_blocks, &m_parser, p::_1) + , "pop_blocks " + , "Remove blocks from end of blockchain" + ); m_command_lookup.set_handler( "version" , std::bind(&t_command_parser_executor::version, &m_parser, p::_1) diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp index 5ae9851a7a6..015e1e1f968 100644 --- a/src/daemon/rpc_command_executor.cpp +++ b/src/daemon/rpc_command_executor.cpp @@ -1967,4 +1967,31 @@ bool t_rpc_command_executor::sync_info() return true; } +bool t_rpc_command_executor::pop_blocks(uint64_t num_blocks) +{ + cryptonote::COMMAND_RPC_POP_BLOCKS::request req; + cryptonote::COMMAND_RPC_POP_BLOCKS::response res; + std::string fail_message = "pop_blocks failed"; + + req.nblocks = num_blocks; + if (m_is_rpc) + { + if (!m_rpc_client->rpc_request(req, res, "/pop_blocks", fail_message.c_str())) + { + return true; + } + } + else + { + if (!m_rpc_server->on_pop_blocks(req, res) || res.status != CORE_RPC_STATUS_OK) + { + tools::fail_msg_writer() << make_error(fail_message, res.status); + return true; + } + } + tools::success_msg_writer() << "new height: " << res.height; + + return true; +} + }// namespace daemonize diff --git a/src/daemon/rpc_command_executor.h b/src/daemon/rpc_command_executor.h index 9e6010c5b38..592584a5f7a 100644 --- a/src/daemon/rpc_command_executor.h +++ b/src/daemon/rpc_command_executor.h @@ -152,6 +152,8 @@ class t_rpc_command_executor final { bool relay_tx(const std::string &txid); bool sync_info(); + + bool pop_blocks(uint64_t num_blocks); }; } // namespace daemonize diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index f029d1d5ad5..2fb70c9952a 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -2021,6 +2021,18 @@ namespace cryptonote return true; } //------------------------------------------------------------------------------------------------------------------------------ + bool core_rpc_server::on_pop_blocks(const COMMAND_RPC_POP_BLOCKS::request& req, COMMAND_RPC_POP_BLOCKS::response& res) + { + PERF_TIMER(on_pop_blocks); + + m_core.get_blockchain_storage().pop_blocks(req.nblocks); + + res.height = m_core.get_current_blockchain_height(); + res.status = CORE_RPC_STATUS_OK; + + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_relay_tx(const COMMAND_RPC_RELAY_TX::request& req, COMMAND_RPC_RELAY_TX::response& res, epee::json_rpc::error& error_resp) { PERF_TIMER(on_relay_tx); diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h index 8ada0af1581..9edbc71d761 100644 --- a/src/rpc/core_rpc_server.h +++ b/src/rpc/core_rpc_server.h @@ -118,6 +118,7 @@ namespace cryptonote MAP_URI_AUTO_JON2("/get_outs", on_get_outs, COMMAND_RPC_GET_OUTPUTS) MAP_URI_AUTO_JON2_IF("/update", on_update, COMMAND_RPC_UPDATE, !m_restricted) MAP_URI_AUTO_BIN2("/get_output_distribution.bin", on_get_output_distribution_bin, COMMAND_RPC_GET_OUTPUT_DISTRIBUTION) + MAP_URI_AUTO_JON2_IF("/pop_blocks", on_pop_blocks, COMMAND_RPC_POP_BLOCKS, !m_restricted) BEGIN_JSON_RPC_MAP("/json_rpc") MAP_JON_RPC("get_block_count", on_getblockcount, COMMAND_RPC_GETBLOCKCOUNT) MAP_JON_RPC("getblockcount", on_getblockcount, COMMAND_RPC_GETBLOCKCOUNT) @@ -189,6 +190,7 @@ namespace cryptonote bool on_stop_save_graph(const COMMAND_RPC_STOP_SAVE_GRAPH::request& req, COMMAND_RPC_STOP_SAVE_GRAPH::response& res); bool on_update(const COMMAND_RPC_UPDATE::request& req, COMMAND_RPC_UPDATE::response& res); bool on_get_output_distribution_bin(const COMMAND_RPC_GET_OUTPUT_DISTRIBUTION::request& req, COMMAND_RPC_GET_OUTPUT_DISTRIBUTION::response& res); + bool on_pop_blocks(const COMMAND_RPC_POP_BLOCKS::request& req, COMMAND_RPC_POP_BLOCKS::response& res); //json_rpc bool on_getblockcount(const COMMAND_RPC_GETBLOCKCOUNT::request& req, COMMAND_RPC_GETBLOCKCOUNT::response& res); diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index ce0be9c4175..c5dbdcbc4a3 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -2326,4 +2326,27 @@ namespace cryptonote }; }; + struct COMMAND_RPC_POP_BLOCKS + { + struct request + { + uint64_t nblocks; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(nblocks); + END_KV_SERIALIZE_MAP() + }; + + struct response + { + std::string status; + uint64_t height; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(status) + KV_SERIALIZE(height) + END_KV_SERIALIZE_MAP() + }; + }; + } From 318cc78457136ec778d6dff43b36f9447d95a43d Mon Sep 17 00:00:00 2001 From: Dusan Klinec Date: Sun, 11 Nov 2018 20:07:25 +0100 Subject: [PATCH 0811/1404] device/trezor: passphrase entry on host - simple device callback object added. Device can request passphrase/PIN entry via the callback or notify user some action is required - callback is routed to wallet2, which routes the callback to i_wallet_callback so CLI or GUI wallets can support passphrase entry for HW tokens - wallet: device open needs wallet callback first - passphrase protected device needs wallet callback so user can enter passphrase --- src/device/device.hpp | 9 ++++ src/device_trezor/device_trezor_base.cpp | 6 +-- src/device_trezor/device_trezor_base.hpp | 19 +++------ src/simplewallet/simplewallet.cpp | 37 ++++++++++++++++- src/simplewallet/simplewallet.h | 3 ++ src/wallet/wallet2.cpp | 52 ++++++++++++++++++++++-- src/wallet/wallet2.h | 22 ++++++++++ src/wallet/wallet_errors.h | 8 ++++ 8 files changed, 134 insertions(+), 22 deletions(-) diff --git a/src/device/device.hpp b/src/device/device.hpp index dd9ad43327e..3e782d21a47 100644 --- a/src/device/device.hpp +++ b/src/device/device.hpp @@ -80,6 +80,14 @@ namespace hw { return false; } + class i_device_callback { + public: + virtual void on_button_request() {} + virtual void on_pin_request(epee::wipeable_string & pin) {} + virtual void on_passphrase_request(bool on_device, epee::wipeable_string & passphrase) {} + virtual ~i_device_callback() = default; + }; + class device { protected: std::string name; @@ -129,6 +137,7 @@ namespace hw { virtual device_type get_type() const = 0; virtual device_protocol_t device_protocol() const { return PROTOCOL_DEFAULT; }; + virtual void set_callback(i_device_callback * callback) {}; /* ======================================================================= */ /* LOCKER */ diff --git a/src/device_trezor/device_trezor_base.cpp b/src/device_trezor/device_trezor_base.cpp index 38c20c30b41..dcfc9cb67b6 100644 --- a/src/device_trezor/device_trezor_base.cpp +++ b/src/device_trezor/device_trezor_base.cpp @@ -39,7 +39,7 @@ namespace trezor { const uint32_t device_trezor_base::DEFAULT_BIP44_PATH[] = {0x8000002c, 0x80000080, 0x80000000}; - device_trezor_base::device_trezor_base() { + device_trezor_base::device_trezor_base(): m_callback(nullptr) { } @@ -332,10 +332,6 @@ namespace trezor { MDEBUG("on_passhprase_state_request"); CHECK_AND_ASSERT_THROW_MES(msg, "Empty message"); - if (m_callback){ - m_callback->on_passphrase_state_request(msg->state()); - } - messages::common::PassphraseStateAck m; resp = call_raw(&m); } diff --git a/src/device_trezor/device_trezor_base.hpp b/src/device_trezor/device_trezor_base.hpp index 88d4194947a..a4e92bf7853 100644 --- a/src/device_trezor/device_trezor_base.hpp +++ b/src/device_trezor/device_trezor_base.hpp @@ -57,17 +57,6 @@ namespace trezor { #ifdef WITH_DEVICE_TREZOR class device_trezor_base; - /** - * Trezor device callbacks - */ - class trezor_callback { - public: - virtual void on_button_request() {}; - virtual void on_pin_request(epee::wipeable_string & pin) {}; - virtual void on_passphrase_request(bool on_device, epee::wipeable_string & passphrase) {}; - virtual void on_passphrase_state_request(const std::string & state) {}; - }; - /** * TREZOR device template with basic functions */ @@ -79,7 +68,7 @@ namespace trezor { mutable boost::mutex command_locker; std::shared_ptr m_transport; - std::shared_ptr m_callback; + i_device_callback * m_callback; std::string full_name; @@ -218,7 +207,11 @@ namespace trezor { return m_transport; } - std::shared_ptr getCallback(){ + void set_callback(i_device_callback * callback) override { + m_callback = callback; + } + + i_device_callback * get_callback(){ return m_callback; } diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index d9fd0c13e3f..6d19235c545 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -3786,6 +3786,7 @@ boost::optional simple_wallet::new_wallet(const boost::pr { auto rc = tools::wallet2::make_new(vm, false, password_prompter); m_wallet = std::move(rc.first); + m_wallet->callback(this); if (!m_wallet) { return {}; @@ -3893,7 +3894,7 @@ bool simple_wallet::open_wallet(const boost::program_options::variables_map& vm) epee::wipeable_string password; try { - auto rc = tools::wallet2::make_from_file(vm, false, m_wallet_file, password_prompter); + auto rc = tools::wallet2::make_from_file(vm, false, "", password_prompter); m_wallet = std::move(rc.first); password = std::move(std::move(rc.second).password()); if (!m_wallet) @@ -3901,6 +3902,8 @@ bool simple_wallet::open_wallet(const boost::program_options::variables_map& vm) return false; } + m_wallet->callback(this); + m_wallet->load(m_wallet_file, password); std::string prefix; bool ready; uint32_t threshold, total; @@ -4304,6 +4307,38 @@ boost::optional simple_wallet::on_get_password(const char return pwd_container->password(); } //---------------------------------------------------------------------------------------------------- +void simple_wallet::on_button_request() +{ + message_writer(console_color_white, false) << tr("Device requires attention"); +} +//---------------------------------------------------------------------------------------------------- +void simple_wallet::on_pin_request(epee::wipeable_string & pin) +{ +#ifdef HAVE_READLINE + rdln::suspend_readline pause_readline; +#endif + std::string msg = tr("Enter device PIN"); + auto pwd_container = tools::password_container::prompt(false, msg.c_str()); + THROW_WALLET_EXCEPTION_IF(!pwd_container, tools::error::password_entry_failed, tr("Failed to read device PIN")); + pin = pwd_container->password(); +} +//---------------------------------------------------------------------------------------------------- +void simple_wallet::on_passphrase_request(bool on_device, epee::wipeable_string & passphrase) +{ + if (on_device){ + message_writer(console_color_white, true) << tr("Please enter the device passphrase on the device"); + return; + } + +#ifdef HAVE_READLINE + rdln::suspend_readline pause_readline; +#endif + std::string msg = tr("Enter device passphrase"); + auto pwd_container = tools::password_container::prompt(false, msg.c_str()); + THROW_WALLET_EXCEPTION_IF(!pwd_container, tools::error::password_entry_failed, tr("Failed to read device passphrase")); + passphrase = pwd_container->password(); +} +//---------------------------------------------------------------------------------------------------- bool simple_wallet::refresh_main(uint64_t start_height, enum ResetType reset, bool is_init) { if (!try_connect_to_daemon(is_init)) diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index 421afbeda68..7e2edba7f96 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -287,6 +287,9 @@ namespace cryptonote virtual void on_money_spent(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& in_tx, uint64_t amount, const cryptonote::transaction& spend_tx, const cryptonote::subaddress_index& subaddr_index); virtual void on_skip_transaction(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx); virtual boost::optional on_get_password(const char *reason); + virtual void on_button_request(); + virtual void on_pin_request(epee::wipeable_string & pin); + virtual void on_passphrase_request(bool on_device, epee::wipeable_string & passphrase); //---------------------------------------------------------- friend class refresh_progress_reporter_t; diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 49aef4350d9..ae6ed0937cc 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -818,6 +818,24 @@ wallet_keys_unlocker::~wallet_keys_unlocker() w.encrypt_keys(key); } +void wallet_device_callback::on_button_request() +{ + if (wallet) + wallet->on_button_request(); +} + +void wallet_device_callback::on_pin_request(epee::wipeable_string & pin) +{ + if (wallet) + wallet->on_pin_request(pin); +} + +void wallet_device_callback::on_passphrase_request(bool on_device, epee::wipeable_string & passphrase) +{ + if (wallet) + wallet->on_passphrase_request(on_device, passphrase); +} + wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended): m_multisig_rescan_info(NULL), m_multisig_rescan_k(NULL), @@ -929,7 +947,7 @@ std::pair, password_container> wallet2::make_from_file( return {nullptr, password_container{}}; } auto wallet = make_basic(vm, unattended, opts, password_prompter); - if (wallet) + if (wallet && !wallet_file.empty()) { wallet->load(wallet_file, pwd->password()); } @@ -1071,15 +1089,16 @@ bool wallet2::reconnect_device() hw::device &hwdev = lookup_device(m_device_name); hwdev.set_name(m_device_name); hwdev.set_network_type(m_nettype); + hwdev.set_callback(get_device_callback()); r = hwdev.init(); if (!r){ - LOG_PRINT_L2("Could not init device"); + MERROR("Could not init device"); return false; } r = hwdev.connect(); if (!r){ - LOG_PRINT_L2("Could not connect to the device"); + MERROR("Could not connect to the device"); return false; } @@ -3441,6 +3460,7 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_ hw::device &hwdev = lookup_device(m_device_name); THROW_WALLET_EXCEPTION_IF(!hwdev.set_name(m_device_name), error::wallet_internal_error, "Could not set device name " + m_device_name); hwdev.set_network_type(m_nettype); + hwdev.set_callback(get_device_callback()); THROW_WALLET_EXCEPTION_IF(!hwdev.init(), error::wallet_internal_error, "Could not initialize the device " + m_device_name); THROW_WALLET_EXCEPTION_IF(!hwdev.connect(), error::wallet_internal_error, "Could not connect to the device " + m_device_name); m_account.set_device(hwdev); @@ -3947,6 +3967,7 @@ void wallet2::restore(const std::string& wallet_, const epee::wipeable_string& p auto &hwdev = lookup_device(device_name); hwdev.set_name(device_name); hwdev.set_network_type(m_nettype); + hwdev.set_callback(get_device_callback()); m_account.create_from_device(hwdev); m_key_device_type = m_account.get_device().get_type(); @@ -11940,4 +11961,29 @@ uint64_t wallet2::get_segregation_fork_height() const void wallet2::generate_genesis(cryptonote::block& b) const { cryptonote::generate_genesis_block(b, get_config(m_nettype).GENESIS_TX, get_config(m_nettype).GENESIS_NONCE); } +//---------------------------------------------------------------------------------------------------- +wallet_device_callback * wallet2::get_device_callback() +{ + if (!m_device_callback){ + m_device_callback.reset(new wallet_device_callback(this)); + } + return m_device_callback.get(); +}//---------------------------------------------------------------------------------------------------- +void wallet2::on_button_request() +{ + if (0 != m_callback) + m_callback->on_button_request(); +} +//---------------------------------------------------------------------------------------------------- +void wallet2::on_pin_request(epee::wipeable_string & pin) +{ + if (0 != m_callback) + m_callback->on_pin_request(pin); +} +//---------------------------------------------------------------------------------------------------- +void wallet2::on_passphrase_request(bool on_device, epee::wipeable_string & passphrase) +{ + if (0 != m_callback) + m_callback->on_passphrase_request(on_device, passphrase); +} } diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index eb076313115..c07054177e2 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -98,11 +98,26 @@ namespace tools virtual void on_lw_money_received(uint64_t height, const crypto::hash &txid, uint64_t amount) {} virtual void on_lw_unconfirmed_money_received(uint64_t height, const crypto::hash &txid, uint64_t amount) {} virtual void on_lw_money_spent(uint64_t height, const crypto::hash &txid, uint64_t amount) {} + // Device callbacks + virtual void on_button_request() {} + virtual void on_pin_request(epee::wipeable_string & pin) {} + virtual void on_passphrase_request(bool on_device, epee::wipeable_string & passphrase) {} // Common callbacks virtual void on_pool_tx_removed(const crypto::hash &txid) {} virtual ~i_wallet2_callback() {} }; + class wallet_device_callback : public hw::i_device_callback + { + public: + wallet_device_callback(wallet2 * wallet): wallet(wallet) {}; + void on_button_request() override; + void on_pin_request(epee::wipeable_string & pin) override; + void on_passphrase_request(bool on_device, epee::wipeable_string & passphrase) override; + private: + wallet2 * wallet; + }; + struct tx_dust_policy { uint64_t dust_threshold; @@ -154,6 +169,7 @@ namespace tools { friend class ::Serialization_portability_wallet_Test; friend class wallet_keys_unlocker; + friend class wallet_device_callback; public: static constexpr const std::chrono::seconds rpc_timeout = std::chrono::minutes(3) + std::chrono::seconds(30); @@ -1285,6 +1301,11 @@ namespace tools void setup_new_blockchain(); void create_keys_file(const std::string &wallet_, bool watch_only, const epee::wipeable_string &password, bool create_address_file); + wallet_device_callback * get_device_callback(); + void on_button_request(); + void on_pin_request(epee::wipeable_string & pin); + void on_passphrase_request(bool on_device, epee::wipeable_string & passphrase); + cryptonote::account_base m_account; boost::optional m_daemon_login; std::string m_daemon_address; @@ -1396,6 +1417,7 @@ namespace tools bool m_devices_registered; std::shared_ptr m_tx_notify; + std::unique_ptr m_device_callback; }; } BOOST_CLASS_VERSION(tools::wallet2, 26) diff --git a/src/wallet/wallet_errors.h b/src/wallet/wallet_errors.h index b3141985dae..e2caee5d25d 100644 --- a/src/wallet/wallet_errors.h +++ b/src/wallet/wallet_errors.h @@ -219,6 +219,14 @@ namespace tools } }; //---------------------------------------------------------------------------------------------------- + struct password_entry_failed : public wallet_runtime_error + { + explicit password_entry_failed(std::string&& loc, const std::string &msg = "Password entry failed") + : wallet_runtime_error(std::move(loc), msg) + { + } + }; + //---------------------------------------------------------------------------------------------------- const char* const file_error_messages[] = { "file already exists", "file not found", From ac665418f0a7ef92ce9b27ecdd88914482f19ee5 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 30 Nov 2018 13:33:29 +0000 Subject: [PATCH 0812/1404] ringct: fix dummy bulletproofs on ledger in fake mode Ledger does some basic checks on them --- src/ringct/rctSigs.cpp | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/src/ringct/rctSigs.cpp b/src/ringct/rctSigs.cpp index dccd188677d..5497b927e68 100644 --- a/src/ringct/rctSigs.cpp +++ b/src/ringct/rctSigs.cpp @@ -46,13 +46,34 @@ using namespace std; namespace { - rct::Bulletproof make_dummy_bulletproof(size_t n_outs) + rct::Bulletproof make_dummy_bulletproof(const std::vector &outamounts, rct::keyV &C, rct::keyV &masks) { + const size_t n_outs = outamounts.size(); const rct::key I = rct::identity(); size_t nrl = 0; while ((1u << nrl) < n_outs) ++nrl; nrl += 6; + + C.resize(n_outs); + masks.resize(n_outs); + for (size_t i = 0; i < n_outs; ++i) + { + masks[i] = I; + rct::key sv8, sv; + sv = rct::zero(); + sv.bytes[0] = outamounts[i] & 255; + sv.bytes[1] = (outamounts[i] >> 8) & 255; + sv.bytes[2] = (outamounts[i] >> 16) & 255; + sv.bytes[3] = (outamounts[i] >> 24) & 255; + sv.bytes[4] = (outamounts[i] >> 32) & 255; + sv.bytes[5] = (outamounts[i] >> 40) & 255; + sv.bytes[6] = (outamounts[i] >> 48) & 255; + sv.bytes[7] = (outamounts[i] >> 56) & 255; + sc_mul(sv8.bytes, sv.bytes, rct::INV_EIGHT.bytes); + rct::addKeys2(C[i], rct::INV_EIGHT, sv8, rct::H); + } + return rct::Bulletproof{rct::keyV(n_outs, I), I, I, I, I, I, I, rct::keyV(nrl, I), rct::keyV(nrl, I), I, I, I}; } } @@ -769,9 +790,7 @@ namespace rct { if (hwdev.get_mode() == hw::device::TRANSACTION_CREATE_FAKE) { // use a fake bulletproof for speed - rv.p.bulletproofs.push_back(make_dummy_bulletproof(outamounts.size())); - C = rct::keyV(outamounts.size(), I); - masks = rct::keyV(outamounts.size(), I); + rv.p.bulletproofs.push_back(make_dummy_bulletproof(outamounts, C, masks)); } else { @@ -799,9 +818,7 @@ namespace rct { if (hwdev.get_mode() == hw::device::TRANSACTION_CREATE_FAKE) { // use a fake bulletproof for speed - rv.p.bulletproofs.push_back(make_dummy_bulletproof(batch_amounts.size())); - C = rct::keyV(batch_amounts.size(), I); - masks = rct::keyV(batch_amounts.size(), I); + rv.p.bulletproofs.push_back(make_dummy_bulletproof(batch_amounts, C, masks)); } else { From aba9a9c27777cd0649e9946566d87030f78d8bec Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 30 Nov 2018 14:53:38 +0000 Subject: [PATCH 0813/1404] daemon: stop miner before we bring the whole thing down This avoids the miner erroring out trying to submit blocks to a core that's already shut down (and avoids pegging the CPU while we're busy shutting down). --- src/cryptonote_core/cryptonote_core.cpp | 1 + src/daemon/daemon.cpp | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 10ab3fe65b1..dd79c0e670b 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -247,6 +247,7 @@ namespace cryptonote //----------------------------------------------------------------------------------- void core::stop() { + m_miner.stop(); m_blockchain_storage.cancel(); tools::download_async_handle handle; diff --git a/src/daemon/daemon.cpp b/src/daemon/daemon.cpp index 49d6d49cf71..85b8780f015 100644 --- a/src/daemon/daemon.cpp +++ b/src/daemon/daemon.cpp @@ -198,7 +198,6 @@ bool t_daemon::run(bool interactive) for(auto& rpc : mp_internals->rpcs) rpc->stop(); - mp_internals->core.get().get_miner().stop(); MGINFO("Node stopped."); return true; } @@ -220,7 +219,6 @@ void t_daemon::stop() { throw std::runtime_error{"Can't stop stopped daemon"}; } - mp_internals->core.get().get_miner().stop(); mp_internals->p2p.stop(); for(auto& rpc : mp_internals->rpcs) rpc->stop(); From 0c5dd3161b0f8c962a77cd27cff58043d85e509c Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 30 Nov 2018 14:54:46 +0000 Subject: [PATCH 0814/1404] cryptonote: add a set_null for transaction_prefix Since it's all inline, I suspect the compiler will merge the duplicate stores anyway. --- src/cryptonote_basic/cryptonote_basic.h | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/cryptonote_basic/cryptonote_basic.h b/src/cryptonote_basic/cryptonote_basic.h index b0eabb0aa97..645e99f91d4 100644 --- a/src/cryptonote_basic/cryptonote_basic.h +++ b/src/cryptonote_basic/cryptonote_basic.h @@ -175,7 +175,15 @@ namespace cryptonote END_SERIALIZE() public: - transaction_prefix(){} + transaction_prefix(){ set_null(); } + void set_null() + { + version = 1; + unlock_time = 0; + vin.clear(); + vout.clear(); + extra.clear(); + } }; class transaction: public transaction_prefix @@ -302,17 +310,12 @@ namespace cryptonote inline transaction::~transaction() { - //set_null(); } inline void transaction::set_null() { - version = 1; - unlock_time = 0; - vin.clear(); - vout.clear(); - extra.clear(); + transaction_prefix::set_null(); signatures.clear(); rct_signatures.type = rct::RCTTypeNull; set_hash_valid(false); From 4d71d463739fa20d21588266b70f58609d79cb2e Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 30 Nov 2018 16:52:50 +0000 Subject: [PATCH 0815/1404] mlocker: remove early page size log It comes before the logger is initialized, so gets displayed even though it should not be by default, and apparenly comes too early for (some versions of) Android, where it crashes. --- contrib/epee/src/mlocker.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/contrib/epee/src/mlocker.cpp b/contrib/epee/src/mlocker.cpp index c3262e8f4bf..4c48cbb58b9 100644 --- a/contrib/epee/src/mlocker.cpp +++ b/contrib/epee/src/mlocker.cpp @@ -47,7 +47,6 @@ static size_t query_page_size() MERROR("Failed to determine page size"); return 0; } - MINFO("Page size: " << ret); return ret; #else #warning Missing query_page_size implementation From 9b69a0ae019b366ad9511263680d6d08f50d6f25 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 30 Nov 2018 17:55:16 +0000 Subject: [PATCH 0816/1404] daemon: print monero version at startup when calling a detached daemon So people who want a timstamp get a timestamp --- src/daemon/main.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/daemon/main.cpp b/src/daemon/main.cpp index f483ba6c92c..35017d9ef0d 100644 --- a/src/daemon/main.cpp +++ b/src/daemon/main.cpp @@ -216,6 +216,16 @@ int main(int argc, char const * argv[]) // after logs initialized tools::create_directories_if_necessary(data_dir.string()); +#ifdef STACK_TRACE + tools::set_stack_trace_log(log_file_path.filename().string()); +#endif // STACK_TRACE + + if (!command_line::is_arg_defaulted(vm, daemon_args::arg_max_concurrency)) + tools::set_max_concurrency(command_line::get_arg(vm, daemon_args::arg_max_concurrency)); + + // logging is now set up + MGINFO("Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")"); + // If there are positional options, we're running a daemon command { auto command = command_line::get_arg(vm, daemon_args::arg_command); @@ -276,16 +286,6 @@ int main(int argc, char const * argv[]) } } -#ifdef STACK_TRACE - tools::set_stack_trace_log(log_file_path.filename().string()); -#endif // STACK_TRACE - - if (!command_line::is_arg_defaulted(vm, daemon_args::arg_max_concurrency)) - tools::set_max_concurrency(command_line::get_arg(vm, daemon_args::arg_max_concurrency)); - - // logging is now set up - MGINFO("Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")"); - MINFO("Moving from main() into the daemonize now."); return daemonizer::daemonize(argc, argv, daemonize::t_executor{}, vm) ? 0 : 1; From ab783b1700fc14ed04bf14fc9df2c6c7b6e90a41 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 30 Nov 2018 17:58:58 +0000 Subject: [PATCH 0817/1404] easylogging++: ensure logger is initialized before main --- external/easylogging++/easylogging++.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/external/easylogging++/easylogging++.cc b/external/easylogging++/easylogging++.cc index d57f3f3a0b1..00b8b830b51 100644 --- a/external/easylogging++/easylogging++.cc +++ b/external/easylogging++/easylogging++.cc @@ -2202,6 +2202,7 @@ el::base::type::StoragePointer el::base::Storage::getELPP() { return getresetELPP(false); } +static struct EnsureELPP { EnsureELPP() { el::base::Storage::getELPP(); } } ensureELPP; #if ELPP_ASYNC_LOGGING Storage::Storage(const LogBuilderPtr& defaultLogBuilder, base::IWorker* asyncDispatchWorker) : #else From 4f74a31ecdaac3d338985fecc2de0882675745d2 Mon Sep 17 00:00:00 2001 From: Dimitris Apostolou Date: Sat, 24 Nov 2018 15:31:49 +0200 Subject: [PATCH 0818/1404] http -> https --- contrib/depends/packages/bdb.mk | 2 +- contrib/depends/packages/ldns.mk | 2 +- contrib/depends/packages/libICE.mk | 2 +- contrib/depends/packages/libSM.mk | 2 +- contrib/depends/packages/libusb.mk | 2 +- contrib/depends/packages/native_cdrkit.mk | 2 +- contrib/depends/packages/qt.mk | 2 +- contrib/depends/packages/unbound.mk | 2 +- contrib/depends/packages/unwind.mk | 2 +- contrib/depends/packages/xproto.mk | 2 +- contrib/depends/packages/zlib.mk | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/contrib/depends/packages/bdb.mk b/contrib/depends/packages/bdb.mk index 6c9876c2c7c..050a60adde6 100644 --- a/contrib/depends/packages/bdb.mk +++ b/contrib/depends/packages/bdb.mk @@ -1,6 +1,6 @@ package=bdb $(package)_version=4.8.30 -$(package)_download_path=http://download.oracle.com/berkeley-db +$(package)_download_path=https://download.oracle.com/berkeley-db $(package)_file_name=db-$($(package)_version).NC.tar.gz $(package)_sha256_hash=12edc0df75bf9abd7f82f821795bcee50f42cb2e5f76a6a281b85732798364ef $(package)_build_subdir=build_unix diff --git a/contrib/depends/packages/ldns.mk b/contrib/depends/packages/ldns.mk index a9565a58129..0b7c3806a16 100644 --- a/contrib/depends/packages/ldns.mk +++ b/contrib/depends/packages/ldns.mk @@ -1,6 +1,6 @@ package=ldns $(package)_version=1.6.17 -$(package)_download_path=http://www.nlnetlabs.nl/downloads/ldns/ +$(package)_download_path=https://www.nlnetlabs.nl/downloads/ldns/ $(package)_file_name=$(package)-$($(package)_version).tar.gz $(package)_sha256_hash=8b88e059452118e8949a2752a55ce59bc71fa5bc414103e17f5b6b06f9bcc8cd $(package)_dependencies=openssl diff --git a/contrib/depends/packages/libICE.mk b/contrib/depends/packages/libICE.mk index fc60323b1c9..a897d9aed77 100644 --- a/contrib/depends/packages/libICE.mk +++ b/contrib/depends/packages/libICE.mk @@ -1,6 +1,6 @@ package=libICE $(package)_version=1.0.9 -$(package)_download_path=http://xorg.freedesktop.org/releases/individual/lib/ +$(package)_download_path=https://xorg.freedesktop.org/releases/individual/lib/ $(package)_file_name=$(package)-$($(package)_version).tar.bz2 $(package)_sha256_hash=8f7032f2c1c64352b5423f6b48a8ebdc339cc63064af34d66a6c9aa79759e202 $(package)_dependencies=xtrans xproto diff --git a/contrib/depends/packages/libSM.mk b/contrib/depends/packages/libSM.mk index 0f9307ca76a..83fcd4cdb56 100644 --- a/contrib/depends/packages/libSM.mk +++ b/contrib/depends/packages/libSM.mk @@ -1,6 +1,6 @@ package=libSM $(package)_version=1.2.2 -$(package)_download_path=http://xorg.freedesktop.org/releases/individual/lib/ +$(package)_download_path=https://xorg.freedesktop.org/releases/individual/lib/ $(package)_file_name=$(package)-$($(package)_version).tar.bz2 $(package)_sha256_hash=0baca8c9f5d934450a70896c4ad38d06475521255ca63b717a6510fdb6e287bd $(package)_dependencies=xtrans xproto libICE diff --git a/contrib/depends/packages/libusb.mk b/contrib/depends/packages/libusb.mk index e9663ace0bd..6d60cce26d5 100644 --- a/contrib/depends/packages/libusb.mk +++ b/contrib/depends/packages/libusb.mk @@ -1,6 +1,6 @@ package=libusb $(package)_version=1.0.22 -$(package)_download_path=http://sourceforge.net/projects/libusb/files/libusb-1.0/libusb-$($(package)_version)/ +$(package)_download_path=https://sourceforge.net/projects/libusb/files/libusb-1.0/libusb-$($(package)_version)/ $(package)_file_name=$(package)-$($(package)_version).tar.bz2 $(package)_sha256_hash=75aeb9d59a4fdb800d329a545c2e6799f732362193b465ea198f2aa275518157 diff --git a/contrib/depends/packages/native_cdrkit.mk b/contrib/depends/packages/native_cdrkit.mk index cf694edb30e..8243458ec85 100644 --- a/contrib/depends/packages/native_cdrkit.mk +++ b/contrib/depends/packages/native_cdrkit.mk @@ -1,6 +1,6 @@ package=native_cdrkit $(package)_version=1.1.11 -$(package)_download_path=http://distro.ibiblio.org/fatdog/source/600/c +$(package)_download_path=https://distro.ibiblio.org/fatdog/source/600/c $(package)_file_name=cdrkit-$($(package)_version).tar.bz2 $(package)_sha256_hash=b50d64c214a65b1a79afe3a964c691931a4233e2ba605d793eb85d0ac3652564 $(package)_patches=cdrkit-deterministic.patch diff --git a/contrib/depends/packages/qt.mk b/contrib/depends/packages/qt.mk index 32ca4a84c5c..bca2926cb02 100644 --- a/contrib/depends/packages/qt.mk +++ b/contrib/depends/packages/qt.mk @@ -1,6 +1,6 @@ PACKAGE=qt $(package)_version=5.7.1 -$(package)_download_path=http://download.qt.io/official_releases/qt/5.7/$($(package)_version)/submodules +$(package)_download_path=https://download.qt.io/archive/qt/5.7/5.7.1/submodules $(package)_suffix=opensource-src-$($(package)_version).tar.gz $(package)_file_name=qtbase-$($(package)_suffix) $(package)_sha256_hash=95f83e532d23b3ddbde7973f380ecae1bac13230340557276f75f2e37984e410 diff --git a/contrib/depends/packages/unbound.mk b/contrib/depends/packages/unbound.mk index beeeb54c170..733a7f2326b 100644 --- a/contrib/depends/packages/unbound.mk +++ b/contrib/depends/packages/unbound.mk @@ -1,6 +1,6 @@ package=unbound $(package)_version=1.6.8 -$(package)_download_path=http://www.unbound.net/downloads/ +$(package)_download_path=https://www.unbound.net/downloads/ $(package)_file_name=$(package)-$($(package)_version).tar.gz $(package)_sha256_hash=e3b428e33f56a45417107448418865fe08d58e0e7fea199b855515f60884dd49 $(package)_dependencies=openssl expat ldns diff --git a/contrib/depends/packages/unwind.mk b/contrib/depends/packages/unwind.mk index e1bcb35b2d5..543f868a5dd 100644 --- a/contrib/depends/packages/unwind.mk +++ b/contrib/depends/packages/unwind.mk @@ -1,6 +1,6 @@ package=unwind $(package)_version=1.2 -$(package)_download_path=http://download.savannah.nongnu.org/releases/libunwind +$(package)_download_path=https://download.savannah.nongnu.org/releases/libunwind $(package)_file_name=lib$(package)-$($(package)_version).tar.gz $(package)_sha256_hash=1de38ffbdc88bd694d10081865871cd2bfbb02ad8ef9e1606aee18d65532b992 diff --git a/contrib/depends/packages/xproto.mk b/contrib/depends/packages/xproto.mk index 50a90b26850..52fe253c77c 100644 --- a/contrib/depends/packages/xproto.mk +++ b/contrib/depends/packages/xproto.mk @@ -1,6 +1,6 @@ package=xproto $(package)_version=7.0.26 -$(package)_download_path=http://xorg.freedesktop.org/releases/individual/proto +$(package)_download_path=https://xorg.freedesktop.org/releases/individual/proto $(package)_file_name=$(package)-$($(package)_version).tar.bz2 $(package)_sha256_hash=636162c1759805a5a0114a369dffdeccb8af8c859ef6e1445f26a4e6e046514f diff --git a/contrib/depends/packages/zlib.mk b/contrib/depends/packages/zlib.mk index 589490800f8..1600b11a01e 100644 --- a/contrib/depends/packages/zlib.mk +++ b/contrib/depends/packages/zlib.mk @@ -1,6 +1,6 @@ package=zlib $(package)_version=1.2.11 -$(package)_download_path=http://www.zlib.net +$(package)_download_path=https://www.zlib.net $(package)_file_name=$(package)-$($(package)_version).tar.gz $(package)_sha256_hash=c3e5e9fdd5004dcb542feda5ee4f0ff0744628baf8ed2dd5d66f8ca1197cb1a1 From 6456cb415a16aab24694a246309f4c7f0ae5667c Mon Sep 17 00:00:00 2001 From: Tadeas Moravec Date: Sat, 1 Dec 2018 13:03:32 +0000 Subject: [PATCH 0819/1404] Bulletproof: Initialize members in default construtor. Fixing a build warning on g++ 7.3.0 --- src/ringct/rctTypes.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ringct/rctTypes.h b/src/ringct/rctTypes.h index 18290637bd2..487ea6f32e2 100644 --- a/src/ringct/rctTypes.h +++ b/src/ringct/rctTypes.h @@ -187,7 +187,8 @@ namespace rct { rct::keyV L, R; rct::key a, b, t; - Bulletproof() {} + Bulletproof(): + A({}), S({}), T1({}), T2({}), taux({}), mu({}), a({}), b({}), t({}) {} Bulletproof(const rct::key &V, const rct::key &A, const rct::key &S, const rct::key &T1, const rct::key &T2, const rct::key &taux, const rct::key &mu, const rct::keyV &L, const rct::keyV &R, const rct::key &a, const rct::key &b, const rct::key &t): V({V}), A(A), S(S), T1(T1), T2(T2), taux(taux), mu(mu), L(L), R(R), a(a), b(b), t(t) {} Bulletproof(const rct::keyV &V, const rct::key &A, const rct::key &S, const rct::key &T1, const rct::key &T2, const rct::key &taux, const rct::key &mu, const rct::keyV &L, const rct::keyV &R, const rct::key &a, const rct::key &b, const rct::key &t): From affff949f9aa32f329afc6f82f4ec119ef7ea6b5 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 2 Dec 2018 13:06:41 +0000 Subject: [PATCH 0820/1404] blockchain: fix race between two external mining threads --- src/cryptonote_core/blockchain.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index bfc5dbbe091..21d1809ab71 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -1237,7 +1237,9 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m uint64_t already_generated_coins; uint64_t pool_cookie; - CRITICAL_REGION_BEGIN(m_blockchain_lock); + m_tx_pool.lock(); + const auto unlock_guard = epee::misc_utils::create_scope_leave_handler([&]() { m_tx_pool.unlock(); }); + CRITICAL_REGION_LOCAL(m_blockchain_lock); height = m_db->height(); if (m_btc_valid) { // The pool cookie is atomic. The lack of locking is OK, as if it changes @@ -1273,8 +1275,6 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m median_weight = m_current_block_cumul_weight_limit / 2; already_generated_coins = m_db->get_block_already_generated_coins(height - 1); - CRITICAL_REGION_END(); - size_t txs_weight; uint64_t fee; if (!m_tx_pool.fill_block_template(b, median_weight, already_generated_coins, txs_weight, fee, expected_reward, m_hardfork->get_current_version())) @@ -1285,7 +1285,6 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m #if defined(DEBUG_CREATE_BLOCK_TEMPLATE) size_t real_txs_weight = 0; uint64_t real_fee = 0; - CRITICAL_REGION_BEGIN(m_tx_pool.m_transactions_lock); for(crypto::hash &cur_hash: b.tx_hashes) { auto cur_res = m_tx_pool.m_transactions.find(cur_hash); @@ -1329,7 +1328,6 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m { LOG_ERROR("Creating block template: error: wrongly calculated fee"); } - CRITICAL_REGION_END(); MDEBUG("Creating block template: height " << height << ", median weight " << median_weight << ", already generated coins " << already_generated_coins << From ea85de4f020e34264e8e5f7bb625126be87677d5 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 3 Dec 2018 19:07:59 +0000 Subject: [PATCH 0821/1404] CONTRIBUTING: mention not changing spelling/typoes in code --- CONTRIBUTING.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3de9cd5ceeb..883d366aa89 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -19,7 +19,8 @@ posted to #monero-dev on irc.freenode.net). Patches should be self contained. A good rule of thumb is to have one patch per separate issue, feature, or logical change. Also, no -other changes, such as random whitespace changes or reindentation. +other changes, such as random whitespace changes, reindentation, +or fixing typoes, spelling, or wording, unless user visible. Following the code style of the particular chunk of code you're modifying is encouraged. Proper squashing should be done (eg, if you're making a buggy patch, then a later patch to fix the bug, From 7d9aeb71958ceb1efad488200ee8ccf92510b583 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 3 Dec 2018 21:33:49 +0000 Subject: [PATCH 0822/1404] easylogging++: avoid uneeded temporary std::string object --- external/easylogging++/easylogging++.cc | 15 +++++++-------- external/easylogging++/easylogging++.h | 2 +- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/external/easylogging++/easylogging++.cc b/external/easylogging++/easylogging++.cc index d57f3f3a0b1..800d9a4dd69 100644 --- a/external/easylogging++/easylogging++.cc +++ b/external/easylogging++/easylogging++.cc @@ -2130,24 +2130,23 @@ static int priority(Level level) { return 7; } -bool VRegistry::allowed(Level level, const char* category) { +bool VRegistry::allowed(Level level, const std::string &category) { base::threading::ScopedLock scopedLock(lock()); - const std::string scategory = category; - const std::map::const_iterator it = m_cached_allowed_categories.find(scategory); + const std::map::const_iterator it = m_cached_allowed_categories.find(category); if (it != m_cached_allowed_categories.end()) return priority(level) <= it->second; - if (m_categories.empty() || category == nullptr) { + if (m_categories.empty()) { return false; } else { std::vector>::const_reverse_iterator it = m_categories.rbegin(); for (; it != m_categories.rend(); ++it) { - if (base::utils::Str::wildCardMatch(category, it->first.c_str())) { + if (base::utils::Str::wildCardMatch(category.c_str(), it->first.c_str())) { const int p = priority(it->second); - m_cached_allowed_categories.insert(std::make_pair(std::move(scategory), p)); + m_cached_allowed_categories.insert(std::make_pair(category, p)); return priority(level) <= p; } } - m_cached_allowed_categories.insert(std::make_pair(std::move(scategory), -1)); + m_cached_allowed_categories.insert(std::make_pair(category, -1)); return false; } } @@ -2720,7 +2719,7 @@ void Writer::initializeLogger(const std::string& loggerId, bool lookup, bool nee } if (ELPP->hasFlag(LoggingFlag::HierarchicalLogging)) { m_proceed = m_level == Level::Verbose ? m_logger->enabled(m_level) : - ELPP->vRegistry()->allowed(m_level, loggerId.c_str()); + ELPP->vRegistry()->allowed(m_level, loggerId); } else { m_proceed = m_logger->enabled(m_level); } diff --git a/external/easylogging++/easylogging++.h b/external/easylogging++/easylogging++.h index 046252a5b3e..9edec93173c 100644 --- a/external/easylogging++/easylogging++.h +++ b/external/easylogging++/easylogging++.h @@ -2463,7 +2463,7 @@ class VRegistry : base::NoCopy, public base::threading::ThreadSafe { void setModules(const char* modules); - bool allowed(Level level, const char* category); + bool allowed(Level level, const std::string &category); bool allowed(base::type::VerboseLevel vlevel, const char* file); From 5a76933903ef2f29532588d359862831a3644128 Mon Sep 17 00:00:00 2001 From: TheCharlatan Date: Sat, 13 Oct 2018 17:07:56 +0200 Subject: [PATCH 0823/1404] Add glibc back compat code To ensure that the binaries compiled by gitian run across many linux distributions, enforce 2.17 as the minimum libc version supported. --- CMakeLists.txt | 9 +- README.md | 2 + contrib/gitian/gitian-linux.yml | 2 +- contrib/gitian/symbol-check.py | 163 ----------------------------- src/common/CMakeLists.txt | 4 + src/common/compat/glibc_compat.cpp | 98 +++++++++++++++++ 6 files changed, 113 insertions(+), 165 deletions(-) delete mode 100755 contrib/gitian/symbol-check.py create mode 100644 src/common/compat/glibc_compat.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 0c38c673abf..78dc14acbc6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -696,6 +696,13 @@ else() set(LD_SECURITY_FLAGS "${LD_SECURITY_FLAGS} -Wl,-z,noexecheap") endif() + if(BACKCOMPAT) + add_definitions(-DFDELT_TYPE=long\ int) + add_linker_flag_if_supported(-Wl,--wrap=__divmoddi4 LD_BACKCOMPAT_FLAGS) + add_linker_flag_if_supported(-Wl,--wrap=glob LD_BACKCOMPAT_FLAGS) + message(STATUS "Using Lib C back compat flags: ${LD_BACKCOMPAT_FLAGS}") + endif() + # some windows linker bits if (WIN32) add_linker_flag_if_supported(-Wl,--dynamicbase LD_SECURITY_FLAGS) @@ -708,7 +715,7 @@ else() set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c11 -D_GNU_SOURCE ${MINGW_FLAG} ${STATIC_ASSERT_FLAG} ${WARNINGS} ${C_WARNINGS} ${COVERAGE_FLAGS} ${PIC_FLAG} ${C_SECURITY_FLAGS}") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -D_GNU_SOURCE ${MINGW_FLAG} ${STATIC_ASSERT_CPP_FLAG} ${WARNINGS} ${CXX_WARNINGS} ${COVERAGE_FLAGS} ${PIC_FLAG} ${CXX_SECURITY_FLAGS}") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${LD_SECURITY_FLAGS}") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${LD_SECURITY_FLAGS} ${LD_BACKCOMPAT_FLAGS}") # With GCC 6.1.1 the compiled binary malfunctions due to aliasing. Until that # is fixed in the code (Issue #847), force compiler to be conservative. diff --git a/README.md b/README.md index 472ca580cf0..d8a3a78443a 100644 --- a/README.md +++ b/README.md @@ -521,6 +521,8 @@ The required packages are the names for each toolchain on apt. Depending on your Using `depends` might also be easier to compile Monero on Windows than using MSYS. Activate Windows Subsystem for Linux (WSL) with a distro (for example Ubuntu), install the apt build-essentials and follow the `depends` steps as depicted above. +The produced binaries still link libc dynamically. If the binary is compiled on a current distribution, it might not run on an older distribution with an older installation of libc. Passing `-DBACKCOMPAT=ON` to cmake will make sure that the binary will run on systems having at least libc version 2.17. + ## Installing Monero from a package **DISCLAIMER: These packages are not part of this repository or maintained by this project's contributors, and as such, do not go through the same review process to ensure their trustworthiness and security.** diff --git a/contrib/gitian/gitian-linux.yml b/contrib/gitian/gitian-linux.yml index 473a7720df2..3bb25c31471 100644 --- a/contrib/gitian/gitian-linux.yml +++ b/contrib/gitian/gitian-linux.yml @@ -152,7 +152,7 @@ script: | for i in ${HOSTS}; do export PATH=${BASEPREFIX}/${i}/native/bin:${ORIGPATH} mkdir build && cd build - cmake .. -DCMAKE_TOOLCHAIN_FILE=${BASEPREFIX}/${i}/share/toolchain.cmake + cmake .. -DCMAKE_TOOLCHAIN_FILE=${BASEPREFIX}/${i}/share/toolchain.cmake -DBACKCOMPAT=ON make DISTNAME=monero-${i} mv bin ${DISTNAME} diff --git a/contrib/gitian/symbol-check.py b/contrib/gitian/symbol-check.py deleted file mode 100755 index 6808e77da73..00000000000 --- a/contrib/gitian/symbol-check.py +++ /dev/null @@ -1,163 +0,0 @@ -#!/usr/bin/env python3 -# 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. -''' -A script to check that the (Linux) executables produced by gitian only contain -allowed gcc, glibc and libstdc++ version symbols. This makes sure they are -still compatible with the minimum supported Linux distribution versions. - -Example usage: - - find ../gitian-builder/build -type f -executable | xargs python contrib/devtools/symbol-check.py -''' -import subprocess -import re -import sys -import os - -# Debian 6.0.9 (Squeeze) has: -# -# - g++ version 4.4.5 (https://packages.debian.org/search?suite=default§ion=all&arch=any&searchon=names&keywords=g%2B%2B) -# - libc version 2.11.3 (https://packages.debian.org/search?suite=default§ion=all&arch=any&searchon=names&keywords=libc6) -# - libstdc++ version 4.4.5 (https://packages.debian.org/search?suite=default§ion=all&arch=any&searchon=names&keywords=libstdc%2B%2B6) -# -# Ubuntu 10.04.4 (Lucid Lynx) has: -# -# - g++ version 4.4.3 (http://packages.ubuntu.com/search?keywords=g%2B%2B&searchon=names&suite=lucid§ion=all) -# - libc version 2.11.1 (http://packages.ubuntu.com/search?keywords=libc6&searchon=names&suite=lucid§ion=all) -# - libstdc++ version 4.4.3 (http://packages.ubuntu.com/search?suite=lucid§ion=all&arch=any&keywords=libstdc%2B%2B&searchon=names) -# -# Taking the minimum of these as our target. -# -# According to GNU ABI document (http://gcc.gnu.org/onlinedocs/libstdc++/manual/abi.html) this corresponds to: -# GCC 4.4.0: GCC_4.4.0 -# GCC 4.4.2: GLIBCXX_3.4.13, CXXABI_1.3.3 -# (glibc) GLIBC_2_11 -# -MAX_VERSIONS = { -'GCC': (4,4,0), -'CXXABI': (1,3,3), -'GLIBCXX': (3,4,13), -'GLIBC': (2,11) -} -# See here for a description of _IO_stdin_used: -# https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=634261#109 - -# Ignore symbols that are exported as part of every executable -IGNORE_EXPORTS = { -'_edata', '_end', '_init', '__bss_start', '_fini', '_IO_stdin_used', 'stdin', 'stdout', 'stderr' -} -READELF_CMD = os.getenv('READELF', '/usr/bin/readelf') -CPPFILT_CMD = os.getenv('CPPFILT', '/usr/bin/c++filt') -# Allowed NEEDED libraries -ALLOWED_LIBRARIES = { -# bitcoind and bitcoin-qt -'libgcc_s.so.1', # GCC base support -'libc.so.6', # C library -'libpthread.so.0', # threading -'libanl.so.1', # DNS resolve -'libm.so.6', # math library -'librt.so.1', # real-time (clock) -'ld-linux-x86-64.so.2', # 64-bit dynamic linker -'ld-linux.so.2', # 32-bit dynamic linker -# bitcoin-qt only -'libX11-xcb.so.1', # part of X11 -'libX11.so.6', # part of X11 -'libxcb.so.1', # part of X11 -'libfontconfig.so.1', # font support -'libfreetype.so.6', # font parsing -'libdl.so.2' # programming interface to dynamic linker -} - -class CPPFilt(object): - ''' - Demangle C++ symbol names. - - Use a pipe to the 'c++filt' command. - ''' - def __init__(self): - self.proc = subprocess.Popen(CPPFILT_CMD, stdin=subprocess.PIPE, stdout=subprocess.PIPE, universal_newlines=True) - - def __call__(self, mangled): - self.proc.stdin.write(mangled + '\n') - self.proc.stdin.flush() - return self.proc.stdout.readline().rstrip() - - def close(self): - self.proc.stdin.close() - self.proc.stdout.close() - self.proc.wait() - -def read_symbols(executable, imports=True): - ''' - Parse an ELF executable and return a list of (symbol,version) tuples - for dynamic, imported symbols. - ''' - p = subprocess.Popen([READELF_CMD, '--dyn-syms', '-W', executable], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE, universal_newlines=True) - (stdout, stderr) = p.communicate() - if p.returncode: - raise IOError('Could not read symbols for %s: %s' % (executable, stderr.strip())) - syms = [] - for line in stdout.splitlines(): - line = line.split() - if len(line)>7 and re.match('[0-9]+:$', line[0]): - (sym, _, version) = line[7].partition('@') - is_import = line[6] == 'UND' - if version.startswith('@'): - version = version[1:] - if is_import == imports: - syms.append((sym, version)) - return syms - -def check_version(max_versions, version): - if '_' in version: - (lib, _, ver) = version.rpartition('_') - else: - lib = version - ver = '0' - ver = tuple([int(x) for x in ver.split('.')]) - if not lib in max_versions: - return False - return ver <= max_versions[lib] - -def read_libraries(filename): - p = subprocess.Popen([READELF_CMD, '-d', '-W', filename], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE, universal_newlines=True) - (stdout, stderr) = p.communicate() - if p.returncode: - raise IOError('Error opening file') - libraries = [] - for line in stdout.splitlines(): - tokens = line.split() - if len(tokens)>2 and tokens[1] == '(NEEDED)': - match = re.match('^Shared library: \[(.*)\]$', ' '.join(tokens[2:])) - if match: - libraries.append(match.group(1)) - else: - raise ValueError('Unparseable (NEEDED) specification') - return libraries - -if __name__ == '__main__': - cppfilt = CPPFilt() - retval = 0 - for filename in sys.argv[1:]: - # Check imported symbols - for sym,version in read_symbols(filename, True): - if version and not check_version(MAX_VERSIONS, version): - print('%s: symbol %s from unsupported version %s' % (filename, cppfilt(sym), version)) - retval = 1 - # Check exported symbols - for sym,version in read_symbols(filename, False): - if sym in IGNORE_EXPORTS: - continue - print('%s: export of symbol %s not allowed' % (filename, cppfilt(sym))) - retval = 1 - # Check dependency libraries - for library_name in read_libraries(filename): - if library_name not in ALLOWED_LIBRARIES: - print('%s: NEEDED library %s is not allowed' % (filename, library_name)) - retval = 1 - - sys.exit(retval) - - diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index aed9bfee73e..1aaab555da8 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -49,6 +49,10 @@ if (STACK_TRACE) list(APPEND common_sources stack_trace.cpp) endif() +if (BACKCOMPAT) + list(APPEND common_sources compat/glibc_compat.cpp) +endif() + set(common_headers) set(common_private_headers diff --git a/src/common/compat/glibc_compat.cpp b/src/common/compat/glibc_compat.cpp new file mode 100644 index 00000000000..bf567987d34 --- /dev/null +++ b/src/common/compat/glibc_compat.cpp @@ -0,0 +1,98 @@ +#include +#include +#include +#include +#include +#include +#include + +#if defined(HAVE_SYS_SELECT_H) +#include +#endif + +// Prior to GLIBC_2.14, memcpy was aliased to memmove. +extern "C" void* memmove(void* a, const void* b, size_t c); +//extern "C" void* memset(void* a, int b, long unsigned int c); +extern "C" void* memcpy(void* a, const void* b, size_t c) +{ + return memmove(a, b, c); +} + +extern "C" void __chk_fail(void) __attribute__((__noreturn__)); + +#if defined(__i386__) || defined(__arm__) + +extern "C" int64_t __udivmoddi4(uint64_t u, uint64_t v, uint64_t* rp); + +extern "C" int64_t __wrap___divmoddi4(int64_t u, int64_t v, int64_t* rp) +{ + int32_t c1 = 0, c2 = 0; + int64_t uu = u, vv = v; + int64_t w; + int64_t r; + + if (uu < 0) { + c1 = ~c1, c2 = ~c2, uu = -uu; + } + if (vv < 0) { + c1 = ~c1, vv = -vv; + } + + w = __udivmoddi4(uu, vv, (uint64_t*)&r); + if (c1) + w = -w; + if (c2) + r = -r; + + *rp = r; + return w; +} +#endif + +/* glibc-internal users use __explicit_bzero_chk, and explicit_bzero + redirects to that. */ +#undef explicit_bzero +/* Set LEN bytes of S to 0. The compiler will not delete a call to + this function, even if S is dead after the call. */ +void +explicit_bzero (void *s, size_t len) +{ + memset (s, '\0', len); + /* Compiler barrier. */ + asm volatile ("" ::: "memory"); +} + +// Redefine explicit_bzero_chk +void +__explicit_bzero_chk (void *dst, size_t len, size_t dstlen) +{ + /* Inline __memset_chk to avoid a PLT reference to __memset_chk. */ + if (__glibc_unlikely (dstlen < len)) + __chk_fail (); + memset (dst, '\0', len); + /* Compiler barrier. */ + asm volatile ("" ::: "memory"); +} +/* libc-internal references use the hidden + __explicit_bzero_chk_internal symbol. This is necessary if + __explicit_bzero_chk is implemented as an IFUNC because some + targets do not support hidden references to IFUNC symbols. */ +#define strong_alias (__explicit_bzero_chk, __explicit_bzero_chk_internal) + +#undef glob +extern "C" int glob_old(const char * pattern, int flags, int (*errfunc) (const char *epath, int eerrno), glob_t *pglob); +#ifdef __i386__ +__asm__(".symver glob_old,glob@GLIBC_2.1"); +#elif defined(__amd64__) +__asm__(".symver glob_old,glob@GLIBC_2.2.5"); +#elif defined(__arm__) +__asm(".symver glob_old,glob@GLIBC_2.4"); +#elif defined(__aarch64__) +__asm__(".symver glob_old,glob@GLIBC_2.17"); +#endif + +extern "C" int __wrap_glob(const char * pattern, int flags, int (*errfunc) (const char *epath, int eerrno), glob_t *pglob) +{ + return glob_old(pattern, flags, errfunc, pglob); +} + From 3cf85f0e835c951d32e3074f8f5f45bc9f35cbad Mon Sep 17 00:00:00 2001 From: Norman Moeschter Date: Tue, 4 Dec 2018 07:27:08 +0100 Subject: [PATCH 0824/1404] Changed RECIEVED to RECEIVED in log messages. --- contrib/epee/include/net/levin_client_async.h | 2 +- contrib/epee/include/net/levin_protocol_handler_async.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/contrib/epee/include/net/levin_client_async.h b/contrib/epee/include/net/levin_client_async.h index b3a46465bf0..ed92f4b95d9 100644 --- a/contrib/epee/include/net/levin_client_async.h +++ b/contrib/epee/include/net/levin_client_async.h @@ -431,7 +431,7 @@ namespace levin } CRITICAL_REGION_END(); - LOG_PRINT_L4("LEVIN_PACKET_RECIEVED. [len=" << head.m_cb << ", flags=" << head.m_flags << ", is_cmd=" << head.m_have_to_return_data <<", cmd_id = " << head.m_command << ", pr_v=" << head.m_protocol_version << ", uid=" << string_tools::get_str_from_guid_a(head.m_id) << "]"); + LOG_PRINT_L4("LEVIN_PACKET_RECEIVED. [len=" << head.m_cb << ", flags=" << head.m_flags << ", is_cmd=" << head.m_have_to_return_data <<", cmd_id = " << head.m_command << ", pr_v=" << head.m_protocol_version << ", uid=" << string_tools::get_str_from_guid_a(head.m_id) << "]"); if(is_request) { diff --git a/contrib/epee/include/net/levin_protocol_handler_async.h b/contrib/epee/include/net/levin_protocol_handler_async.h index 08aa1d468b8..4df7337cd37 100644 --- a/contrib/epee/include/net/levin_protocol_handler_async.h +++ b/contrib/epee/include/net/levin_protocol_handler_async.h @@ -415,7 +415,7 @@ class async_protocol_handler bool is_response = (m_oponent_protocol_ver == LEVIN_PROTOCOL_VER_1 && m_current_head.m_flags&LEVIN_PACKET_RESPONSE); - MDEBUG(m_connection_context << "LEVIN_PACKET_RECIEVED. [len=" << m_current_head.m_cb + MDEBUG(m_connection_context << "LEVIN_PACKET_RECEIVED. [len=" << m_current_head.m_cb << ", flags" << m_current_head.m_flags << ", r?=" << m_current_head.m_have_to_return_data <<", cmd = " << m_current_head.m_command From ec1a62b50dedb65f14db30dbafb1035018cf3421 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 18 Nov 2018 10:17:24 +0000 Subject: [PATCH 0825/1404] move int-util.h to epee --- {src/common => contrib/epee/include}/int-util.h | 0 src/common/CMakeLists.txt | 1 - src/crypto/aesb.c | 2 +- src/crypto/chacha.c | 2 +- src/crypto/groestl_tables.h | 2 +- src/crypto/hash-ops.h | 2 +- src/crypto/keccak.c | 2 +- src/crypto/skein_port.h | 2 +- src/crypto/slow-hash.c | 2 +- src/cryptonote_basic/cryptonote_basic_impl.cpp | 2 +- src/cryptonote_basic/difficulty.cpp | 2 +- src/cryptonote_core/blockchain.cpp | 2 +- src/cryptonote_core/tx_pool.cpp | 2 +- src/device/device_default.cpp | 2 +- src/mnemonics/electrum-words.cpp | 2 +- tests/unit_tests/mul_div.cpp | 2 +- 16 files changed, 14 insertions(+), 15 deletions(-) rename {src/common => contrib/epee/include}/int-util.h (100%) diff --git a/src/common/int-util.h b/contrib/epee/include/int-util.h similarity index 100% rename from src/common/int-util.h rename to contrib/epee/include/int-util.h diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 3045c003cbe..5da23c94409 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -63,7 +63,6 @@ set(common_private_headers error.h expect.h http_connection.h - int-util.h notify.h pod-class.h rpc_client.h diff --git a/src/crypto/aesb.c b/src/crypto/aesb.c index 8a22a4b9386..efdeef8d153 100644 --- a/src/crypto/aesb.c +++ b/src/crypto/aesb.c @@ -19,7 +19,7 @@ Issue Date: 20/12/2007 */ #include -#include "common/int-util.h" +#include "int-util.h" #if defined(__cplusplus) extern "C" diff --git a/src/crypto/chacha.c b/src/crypto/chacha.c index 5d3edb98d83..d734e8b1b25 100644 --- a/src/crypto/chacha.c +++ b/src/crypto/chacha.c @@ -11,7 +11,7 @@ Public domain. #endif #include "chacha.h" -#include "common/int-util.h" +#include "int-util.h" #include "warnings.h" /* diff --git a/src/crypto/groestl_tables.h b/src/crypto/groestl_tables.h index 53594c5697b..12472dced3c 100644 --- a/src/crypto/groestl_tables.h +++ b/src/crypto/groestl_tables.h @@ -29,7 +29,7 @@ #ifndef __tables_h #define __tables_h -#include "common/int-util.h" +#include "int-util.h" #if BYTE_ORDER == LITTLE_ENDIAN diff --git a/src/crypto/hash-ops.h b/src/crypto/hash-ops.h index d77d55cf30a..77b52e2d4a1 100644 --- a/src/crypto/hash-ops.h +++ b/src/crypto/hash-ops.h @@ -37,7 +37,7 @@ #include #include -#include "common/int-util.h" +#include "int-util.h" #include "warnings.h" static inline void *padd(void *p, size_t i) { diff --git a/src/crypto/keccak.c b/src/crypto/keccak.c index b095b5ce2ec..17091126296 100644 --- a/src/crypto/keccak.c +++ b/src/crypto/keccak.c @@ -5,7 +5,7 @@ #include #include #include -#include "common/int-util.h" +#include "int-util.h" #include "hash-ops.h" #include "keccak.h" diff --git a/src/crypto/skein_port.h b/src/crypto/skein_port.h index a50a28e6baf..8a1640e5767 100644 --- a/src/crypto/skein_port.h +++ b/src/crypto/skein_port.h @@ -114,7 +114,7 @@ typedef uint64_t u64b_t; /* 64-bit unsigned integer */ #ifndef SKEIN_NEED_SWAP /* compile-time "override" for endianness? */ -#include "common/int-util.h" +#include "int-util.h" #define IS_BIG_ENDIAN 4321 /* byte 0 is most significant (mc68k) */ #define IS_LITTLE_ENDIAN 1234 /* byte 0 is least significant (i386) */ diff --git a/src/crypto/slow-hash.c b/src/crypto/slow-hash.c index dcbabccab8e..ae0bd4e98ff 100644 --- a/src/crypto/slow-hash.c +++ b/src/crypto/slow-hash.c @@ -35,7 +35,7 @@ #include #include -#include "common/int-util.h" +#include "int-util.h" #include "hash-ops.h" #include "oaes_lib.h" #include "variant2_int_sqrt.h" diff --git a/src/cryptonote_basic/cryptonote_basic_impl.cpp b/src/cryptonote_basic/cryptonote_basic_impl.cpp index c4e10851e4a..41b5f19f010 100644 --- a/src/cryptonote_basic/cryptonote_basic_impl.cpp +++ b/src/cryptonote_basic/cryptonote_basic_impl.cpp @@ -40,7 +40,7 @@ using namespace epee; #include "misc_language.h" #include "common/base58.h" #include "crypto/hash.h" -#include "common/int-util.h" +#include "int-util.h" #include "common/dns_utils.h" #undef MONERO_DEFAULT_LOG_CATEGORY diff --git a/src/cryptonote_basic/difficulty.cpp b/src/cryptonote_basic/difficulty.cpp index cb2a00a1289..55e3e93b3c3 100644 --- a/src/cryptonote_basic/difficulty.cpp +++ b/src/cryptonote_basic/difficulty.cpp @@ -34,7 +34,7 @@ #include #include -#include "common/int-util.h" +#include "int-util.h" #include "crypto/hash.h" #include "cryptonote_config.h" #include "difficulty.h" diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 80e0a983e33..ca569a16f12 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -44,7 +44,7 @@ #include "misc_language.h" #include "profile_tools.h" #include "file_io_utils.h" -#include "common/int-util.h" +#include "int-util.h" #include "common/threadpool.h" #include "common/boost_serialization_helper.h" #include "warnings.h" diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index e2900916b05..d4b4e4d349b 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -40,7 +40,7 @@ #include "blockchain.h" #include "blockchain_db/blockchain_db.h" #include "common/boost_serialization_helper.h" -#include "common/int-util.h" +#include "int-util.h" #include "misc_language.h" #include "warnings.h" #include "common/perf_timer.h" diff --git a/src/device/device_default.cpp b/src/device/device_default.cpp index 1e3d8094998..2286998a402 100644 --- a/src/device/device_default.cpp +++ b/src/device/device_default.cpp @@ -31,7 +31,7 @@ #include "device_default.hpp" -#include "common/int-util.h" +#include "int-util.h" #include "cryptonote_basic/account.h" #include "cryptonote_basic/subaddress_index.h" #include "ringct/rctOps.h" diff --git a/src/mnemonics/electrum-words.cpp b/src/mnemonics/electrum-words.cpp index e6d2a6b76cc..b1e3bdcd5de 100644 --- a/src/mnemonics/electrum-words.cpp +++ b/src/mnemonics/electrum-words.cpp @@ -42,7 +42,7 @@ #include #include "wipeable_string.h" #include "misc_language.h" -#include "common/int-util.h" +#include "int-util.h" #include "mnemonics/electrum-words.h" #include diff --git a/tests/unit_tests/mul_div.cpp b/tests/unit_tests/mul_div.cpp index 8408d0da1cb..768b95d4b0f 100644 --- a/tests/unit_tests/mul_div.cpp +++ b/tests/unit_tests/mul_div.cpp @@ -30,7 +30,7 @@ #include "gtest/gtest.h" -#include "common/int-util.h" +#include "int-util.h" namespace { From 9c923bad9b4a7a2dfaf949255cea9ac4ff33eee6 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 18 Nov 2018 10:18:35 +0000 Subject: [PATCH 0826/1404] epee: fix network packet header field endianness --- contrib/epee/include/net/levin_client.inl | 41 ++++++------- contrib/epee/include/net/levin_helper.h | 41 +++++++++---- .../epee/include/net/levin_protocol_handler.h | 19 ++++-- .../net/levin_protocol_handler_async.h | 60 +++++++++++++------ .../epee/include/storages/portable_storage.h | 9 +-- 5 files changed, 113 insertions(+), 57 deletions(-) diff --git a/contrib/epee/include/net/levin_client.inl b/contrib/epee/include/net/levin_client.inl index ab7c32c32cb..a580e81fdaf 100644 --- a/contrib/epee/include/net/levin_client.inl +++ b/contrib/epee/include/net/levin_client.inl @@ -80,10 +80,10 @@ int levin_client_impl::invoke(int command, const std::string& in_buff, std::stri return -1; bucket_head head = {0}; - head.m_signature = LEVIN_SIGNATURE; - head.m_cb = in_buff.size(); + head.m_signature = SWAP64LE(LEVIN_SIGNATURE); + head.m_cb = SWAP64LE(in_buff.size()); head.m_have_to_return_data = true; - head.m_command = command; + head.m_command = SWAP32LE(command); if(!m_transport.send(&head, sizeof(head))) return -1; @@ -97,7 +97,7 @@ int levin_client_impl::invoke(int command, const std::string& in_buff, std::stri head = *(bucket_head*)local_buff.data(); - if(head.m_signature!=LEVIN_SIGNATURE) + if(head.m_signature!=SWAP64LE(LEVIN_SIGNATURE)) { LOG_PRINT_L1("Signature mismatch in response"); return -1; @@ -116,10 +116,10 @@ int levin_client_impl::notify(int command, const std::string& in_buff) return -1; bucket_head head = {0}; - head.m_signature = LEVIN_SIGNATURE; - head.m_cb = in_buff.size(); + head.m_signature = SWAP64LE(LEVIN_SIGNATURE); + head.m_cb = SWAP64LE(in_buff.size()); head.m_have_to_return_data = false; - head.m_command = command; + head.m_command = SWAP32LE(command); if(!m_transport.send((const char*)&head, sizeof(head))) return -1; @@ -139,12 +139,13 @@ inline return -1; bucket_head2 head = {0}; - head.m_signature = LEVIN_SIGNATURE; - head.m_cb = in_buff.size(); + head.m_signature = SWAP64LE(LEVIN_SIGNATURE); + head.m_cb = SWAP64LE(in_buff.size()); head.m_have_to_return_data = true; - head.m_command = command; - head.m_protocol_version = LEVIN_PROTOCOL_VER_1; - head.m_flags = LEVIN_PACKET_REQUEST; + head.m_command = SWAP32LE(command); + head.m_return_code = SWAP32LE(0); + head.m_flags = SWAP32LE(LEVIN_PACKET_REQUEST); + head.m_protocol_version = SWAP32LE(LEVIN_PROTOCOL_VER_1); if(!m_transport.send(&head, sizeof(head))) return -1; @@ -157,14 +158,13 @@ inline head = *(bucket_head2*)local_buff.data(); - - if(head.m_signature!=LEVIN_SIGNATURE) + if(head.m_signature != SWAP64LE(LEVIN_SIGNATURE)) { LOG_PRINT_L1("Signature mismatch in response"); return -1; } - if(!m_transport.recv_n(buff_out, head.m_cb)) + if(!m_transport.recv_n(buff_out, SWAP64LE(head.m_cb))) return -1; return head.m_return_code; @@ -177,12 +177,13 @@ inline return -1; bucket_head2 head = {0}; - head.m_signature = LEVIN_SIGNATURE; - head.m_cb = in_buff.size(); + head.m_signature = SWAP64LE(LEVIN_SIGNATURE); + head.m_cb = SWAP64LE(in_buff.size()); head.m_have_to_return_data = false; - head.m_command = command; - head.m_protocol_version = LEVIN_PROTOCOL_VER_1; - head.m_flags = LEVIN_PACKET_REQUEST; + head.m_command = SWAP32LE(command); + head.m_return_code = SWAP32LE(0); + head.m_flags = SWAP32LE(LEVIN_PACKET_REQUEST); + head.m_protocol_version = SWAP32LE(LEVIN_PROTOCOL_VER_1); if(!m_transport.send((const char*)&head, sizeof(head))) return -1; diff --git a/contrib/epee/include/net/levin_helper.h b/contrib/epee/include/net/levin_helper.h index 05560dd9080..da926a91410 100644 --- a/contrib/epee/include/net/levin_helper.h +++ b/contrib/epee/include/net/levin_helper.h @@ -30,6 +30,7 @@ #include "levin_base.h" #include "serializeble_struct_helper.h" +#include "int-util.h" #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "net" @@ -43,11 +44,11 @@ namespace levin { buff.resize(sizeof(levin::bucket_head)); levin::bucket_head& head = *(levin::bucket_head*)(&buff[0]); - head.m_signature = LEVIN_SIGNATURE; + head.m_signature = SWAP64LE(LEVIN_SIGNATURE); head.m_cb = 0; head.m_have_to_return_data = true; - head.m_command = command_id; - head.m_return_code = 1; + head.m_command = SWAP32LE(command_id); + head.m_return_code = SWAP32LE(1); head.m_reservedA = rand(); //probably some flags in future head.m_reservedB = rand(); //probably some check summ in future @@ -55,7 +56,7 @@ namespace levin if(!StorageNamed::save_struct_as_storage_to_buff_t(t, buff_strg)) return false; - head.m_cb = buff_strg.size(); + head.m_cb = SWAP64LE(buff_strg.size()); buff.append(buff_strg); return true; } @@ -65,15 +66,15 @@ namespace levin { buff.resize(sizeof(levin::bucket_head)); levin::bucket_head& head = *(levin::bucket_head*)(&buff[0]); - head.m_signature = LEVIN_SIGNATURE; + head.m_signature = SWAP64LE(LEVIN_SIGNATURE); head.m_cb = 0; head.m_have_to_return_data = true; - head.m_command = command_id; - head.m_return_code = 1; + head.m_command = SWAP32LE(command_id); + head.m_return_code = SWAP32LE(1); head.m_reservedA = rand(); //probably some flags in future head.m_reservedB = rand(); //probably some check summ in future - head.m_cb = data.size(); + head.m_cb = SWAP64LE(data.size()); buff.append(data); return true; } @@ -86,7 +87,17 @@ namespace levin return false; } - levin::bucket_head& head = *(levin::bucket_head*)(&buff[0]); +#if BYTE_ORDER == LITTLE_ENDIAN + levin::bucket_head &head = *(levin::bucket_head*)(&buff[0]); +#else + levin::bucket_head head = *(levin::bucket_head*)(&buff[0]); + head.m_signature = SWAP64LE(head.m_signature); + head.m_cb = SWAP64LE(head.m_cb); + head.m_command = SWAP32LE(head.m_command); + head.m_return_code = SWAP32LE(head.m_return_code); + head.m_reservedA = SWAP32LE(head.m_reservedA); + head.m_reservedB = SWAP32LE(head.m_reservedB); +#endif if(head.m_signature != LEVIN_SIGNATURE) { LOG_PRINT_L3("Failed to read signature in levin message, at load_struct_from_levin_message"); @@ -113,7 +124,17 @@ namespace levin return false; } - levin::bucket_head& head = *(levin::bucket_head*)(&buff[0]); +#if BYTE_ORDER == LITTLE_ENDIAN + levin::bucket_head &head = *(levin::bucket_head*)(&buff[0]); +#else + levin::bucket_head head = *(levin::bucket_head*)(&buff[0]); + head.m_signature = SWAP64LE(head.m_signature); + head.m_cb = SWAP64LE(head.m_cb); + head.m_command = SWAP32LE(head.m_command); + head.m_return_code = SWAP32LE(head.m_return_code); + head.m_reservedA = SWAP32LE(head.m_reservedA); + head.m_reservedB = SWAP32LE(head.m_reservedB); +#endif if(head.m_signature != LEVIN_SIGNATURE) { LOG_ERROR("Failed to read signature in levin message, at load_struct_from_levin_message"); diff --git a/contrib/epee/include/net/levin_protocol_handler.h b/contrib/epee/include/net/levin_protocol_handler.h index b3a75bedc55..791766762a9 100644 --- a/contrib/epee/include/net/levin_protocol_handler.h +++ b/contrib/epee/include/net/levin_protocol_handler.h @@ -31,6 +31,7 @@ #include #include "levin_base.h" +#include "int-util.h" #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "net" @@ -103,7 +104,7 @@ namespace levin case conn_state_reading_head: if(m_cach_in_buffer.size() < sizeof(bucket_head)) { - if(m_cach_in_buffer.size() >= sizeof(uint64_t) && *((uint64_t*)m_cach_in_buffer.data()) != LEVIN_SIGNATURE) + if(m_cach_in_buffer.size() >= sizeof(uint64_t) && *((uint64_t*)m_cach_in_buffer.data()) != SWAP64LE(LEVIN_SIGNATURE)) { LOG_ERROR_CC(m_conn_context, "Signature mismatch on accepted connection"); return false; @@ -112,13 +113,23 @@ namespace levin break; } { - bucket_head* phead = (bucket_head*)m_cach_in_buffer.data(); - if(LEVIN_SIGNATURE != phead->m_signature) +#if BYTE_ORDER == LITTLE_ENDIAN + bucket_head &phead = *(bucket_head*)m_cach_in_buffer.data(); +#else + bucket_head phead = *(bucket_head*)m_cach_in_buffer.data(); + phead.m_signature = SWAP64LE(phead.m_signature); + phead.m_cb = SWAP64LE(phead.m_cb); + phead.m_command = SWAP32LE(phead.m_command); + phead.m_return_code = SWAP32LE(phead.m_return_code); + phead.m_reservedA = SWAP32LE(phead.m_reservedA); + phead.m_reservedB = SWAP32LE(phead.m_reservedB); +#endif + if(LEVIN_SIGNATURE != phead.m_signature) { LOG_ERROR_CC(m_conn_context, "Signature mismatch on accepted connection"); return false; } - m_current_head = *phead; + m_current_head = phead; } m_cach_in_buffer.erase(0, sizeof(bucket_head)); m_state = conn_state_reading_body; diff --git a/contrib/epee/include/net/levin_protocol_handler_async.h b/contrib/epee/include/net/levin_protocol_handler_async.h index 08aa1d468b8..6b1528caff5 100644 --- a/contrib/epee/include/net/levin_protocol_handler_async.h +++ b/contrib/epee/include/net/levin_protocol_handler_async.h @@ -37,6 +37,7 @@ #include "misc_language.h" #include "syncobj.h" #include "misc_os_dependent.h" +#include "int-util.h" #include #include @@ -469,7 +470,18 @@ class async_protocol_handler m_current_head.m_have_to_return_data = false; m_current_head.m_protocol_version = LEVIN_PROTOCOL_VER_1; m_current_head.m_flags = LEVIN_PACKET_RESPONSE; +#if BYTE_ORDER == LITTLE_ENDIAN std::string send_buff((const char*)&m_current_head, sizeof(m_current_head)); +#else + bucket_head2 head = m_current_head; + head.m_signature = SWAP64LE(head.m_signature); + head.m_cb = SWAP64LE(head.m_cb); + head.m_command = SWAP32LE(head.m_command); + head.m_return_code = SWAP32LE(head.m_return_code); + head.m_flags = SWAP32LE(head.m_flags); + head.m_protocol_version = SWAP32LE(head.m_protocol_version); + std::string send_buff((const char*)&head, sizeof(head)); +#endif send_buff += return_buff; CRITICAL_REGION_BEGIN(m_send_lock); if(!m_pservice_endpoint->do_send(send_buff.data(), send_buff.size())) @@ -491,7 +503,7 @@ class async_protocol_handler { if(m_cache_in_buffer.size() < sizeof(bucket_head2)) { - if(m_cache_in_buffer.size() >= sizeof(uint64_t) && *((uint64_t*)m_cache_in_buffer.data()) != LEVIN_SIGNATURE) + if(m_cache_in_buffer.size() >= sizeof(uint64_t) && *((uint64_t*)m_cache_in_buffer.data()) != SWAP64LE(LEVIN_SIGNATURE)) { MWARNING(m_connection_context << "Signature mismatch, connection will be closed"); return false; @@ -500,13 +512,23 @@ class async_protocol_handler break; } - bucket_head2* phead = (bucket_head2*)m_cache_in_buffer.data(); - if(LEVIN_SIGNATURE != phead->m_signature) +#if BYTE_ORDER == LITTLE_ENDIAN + bucket_head2& phead = *(bucket_head2*)m_cache_in_buffer.data(); +#else + bucket_head2 phead = *(bucket_head2*)m_cache_in_buffer.data(); + phead.m_signature = SWAP64LE(phead.m_signature); + phead.m_cb = SWAP64LE(phead.m_cb); + phead.m_command = SWAP32LE(phead.m_command); + phead.m_return_code = SWAP32LE(phead.m_return_code); + phead.m_flags = SWAP32LE(phead.m_flags); + phead.m_protocol_version = SWAP32LE(phead.m_protocol_version); +#endif + if(LEVIN_SIGNATURE != phead.m_signature) { LOG_ERROR_CC(m_connection_context, "Signature mismatch, connection will be closed"); return false; } - m_current_head = *phead; + m_current_head = phead; m_cache_in_buffer.erase(0, sizeof(bucket_head2)); m_state = stream_state_body; @@ -566,13 +588,13 @@ class async_protocol_handler } bucket_head2 head = {0}; - head.m_signature = LEVIN_SIGNATURE; - head.m_cb = in_buff.size(); + head.m_signature = SWAP64LE(LEVIN_SIGNATURE); + head.m_cb = SWAP64LE(in_buff.size()); head.m_have_to_return_data = true; - head.m_flags = LEVIN_PACKET_REQUEST; - head.m_command = command; - head.m_protocol_version = LEVIN_PROTOCOL_VER_1; + head.m_flags = SWAP32LE(LEVIN_PACKET_REQUEST); + head.m_command = SWAP32LE(command); + head.m_protocol_version = SWAP32LE(LEVIN_PROTOCOL_VER_1); boost::interprocess::ipcdetail::atomic_write32(&m_invoke_buf_ready, 0); CRITICAL_REGION_BEGIN(m_send_lock); @@ -624,13 +646,13 @@ class async_protocol_handler return LEVIN_ERROR_CONNECTION_DESTROYED; bucket_head2 head = {0}; - head.m_signature = LEVIN_SIGNATURE; - head.m_cb = in_buff.size(); + head.m_signature = SWAP64LE(LEVIN_SIGNATURE); + head.m_cb = SWAP64LE(in_buff.size()); head.m_have_to_return_data = true; - head.m_flags = LEVIN_PACKET_REQUEST; - head.m_command = command; - head.m_protocol_version = LEVIN_PROTOCOL_VER_1; + head.m_flags = SWAP32LE(LEVIN_PACKET_REQUEST); + head.m_command = SWAP32LE(command); + head.m_protocol_version = SWAP32LE(LEVIN_PROTOCOL_VER_1); boost::interprocess::ipcdetail::atomic_write32(&m_invoke_buf_ready, 0); CRITICAL_REGION_BEGIN(m_send_lock); @@ -698,13 +720,13 @@ class async_protocol_handler return LEVIN_ERROR_CONNECTION_DESTROYED; bucket_head2 head = {0}; - head.m_signature = LEVIN_SIGNATURE; + head.m_signature = SWAP64LE(LEVIN_SIGNATURE); head.m_have_to_return_data = false; - head.m_cb = in_buff.size(); + head.m_cb = SWAP64LE(in_buff.size()); - head.m_command = command; - head.m_protocol_version = LEVIN_PROTOCOL_VER_1; - head.m_flags = LEVIN_PACKET_REQUEST; + head.m_command = SWAP32LE(command); + head.m_protocol_version = SWAP32LE(LEVIN_PROTOCOL_VER_1); + head.m_flags = SWAP32LE(LEVIN_PACKET_REQUEST); CRITICAL_REGION_BEGIN(m_send_lock); if(!m_pservice_endpoint->do_send(&head, sizeof(head))) { diff --git a/contrib/epee/include/storages/portable_storage.h b/contrib/epee/include/storages/portable_storage.h index 2023e2f2aa2..0f0c6210f77 100644 --- a/contrib/epee/include/storages/portable_storage.h +++ b/contrib/epee/include/storages/portable_storage.h @@ -35,6 +35,7 @@ #include "portable_storage_to_json.h" #include "portable_storage_from_json.h" #include "portable_storage_val_converters.h" +#include "int-util.h" namespace epee { @@ -135,8 +136,8 @@ namespace epee TRY_ENTRY(); std::stringstream ss; storage_block_header sbh = AUTO_VAL_INIT(sbh); - sbh.m_signature_a = PORTABLE_STORAGE_SIGNATUREA; - sbh.m_signature_b = PORTABLE_STORAGE_SIGNATUREB; + sbh.m_signature_a = SWAP32LE(PORTABLE_STORAGE_SIGNATUREA); + sbh.m_signature_b = SWAP32LE(PORTABLE_STORAGE_SIGNATUREB); sbh.m_ver = PORTABLE_STORAGE_FORMAT_VER; ss.write((const char*)&sbh, sizeof(storage_block_header)); pack_entry_to_buff(ss, m_root); @@ -154,8 +155,8 @@ namespace epee return false; } storage_block_header* pbuff = (storage_block_header*)source.data(); - if(pbuff->m_signature_a != PORTABLE_STORAGE_SIGNATUREA || - pbuff->m_signature_b != PORTABLE_STORAGE_SIGNATUREB + if(pbuff->m_signature_a != SWAP32LE(PORTABLE_STORAGE_SIGNATUREA) || + pbuff->m_signature_b != SWAP32LE(PORTABLE_STORAGE_SIGNATUREB) ) { LOG_ERROR("portable_storage: wrong binary format - signature mismatch"); From 243f010edc50bb588779d4d63e73e16e59bc27bc Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 20 Nov 2018 21:41:03 +0000 Subject: [PATCH 0827/1404] rpc: mask values that are nobody else's business in restricted RPC --- src/rpc/core_rpc_server.cpp | 53 ++++++++++++++++++++----------------- 1 file changed, 29 insertions(+), 24 deletions(-) diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index 1bb353bb414..715858583be 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -182,20 +182,19 @@ namespace cryptonote res.target = m_core.get_blockchain_storage().get_difficulty_target(); res.tx_count = m_core.get_blockchain_storage().get_total_transactions() - res.height; //without coinbase res.tx_pool_size = m_core.get_pool_transactions_count(); - res.alt_blocks_count = m_core.get_blockchain_storage().get_alternative_blocks_count(); - uint64_t total_conn = m_p2p.get_connections_count(); - res.outgoing_connections_count = m_p2p.get_outgoing_connections_count(); - res.incoming_connections_count = total_conn - res.outgoing_connections_count; - res.rpc_connections_count = get_connections_count(); - res.white_peerlist_size = m_p2p.get_peerlist_manager().get_white_peers_count(); - res.grey_peerlist_size = m_p2p.get_peerlist_manager().get_gray_peers_count(); + res.alt_blocks_count = m_restricted ? 0 : m_core.get_blockchain_storage().get_alternative_blocks_count(); + uint64_t total_conn = m_restricted ? 0 : m_p2p.get_connections_count(); + res.outgoing_connections_count = m_restricted ? 0 : m_p2p.get_outgoing_connections_count(); + res.incoming_connections_count = m_restricted ? 0 : (total_conn - res.outgoing_connections_count); + res.rpc_connections_count = m_restricted ? 0 : get_connections_count(); + res.white_peerlist_size = m_restricted ? 0 : m_p2p.get_peerlist_manager().get_white_peers_count(); + res.grey_peerlist_size = m_restricted ? 0 : m_p2p.get_peerlist_manager().get_gray_peers_count(); cryptonote::network_type net_type = nettype(); res.mainnet = net_type == MAINNET; res.testnet = net_type == TESTNET; res.stagenet = net_type == STAGENET; res.nettype = net_type == MAINNET ? "mainnet" : net_type == TESTNET ? "testnet" : net_type == STAGENET ? "stagenet" : "fakechain"; - res.cumulative_difficulty = m_core.get_blockchain_storage().get_db().get_block_cumulative_difficulty(res.height - 1); res.block_size_limit = res.block_weight_limit = m_core.get_blockchain_storage().get_current_cumulative_block_weight_limit(); res.block_size_median = res.block_weight_median = m_core.get_blockchain_storage().get_current_cumulative_block_weight_median(); @@ -203,14 +202,17 @@ namespace cryptonote res.start_time = m_restricted ? 0 : (uint64_t)m_core.get_start_time(); res.free_space = m_restricted ? std::numeric_limits::max() : m_core.get_free_space(); res.offline = m_core.offline(); - res.bootstrap_daemon_address = m_bootstrap_daemon_address; - res.height_without_bootstrap = res.height; + res.bootstrap_daemon_address = m_restricted ? "" : m_bootstrap_daemon_address; + res.height_without_bootstrap = m_restricted ? 0 : res.height; + if (m_restricted) + res.was_bootstrap_ever_used = false; + else { boost::shared_lock lock(m_bootstrap_daemon_mutex); res.was_bootstrap_ever_used = m_was_bootstrap_ever_used; } - res.database_size = m_core.get_blockchain_storage().get_db().get_database_size(); - res.update_available = m_core.is_update_available(); + res.database_size = m_restricted ? 0 : m_core.get_blockchain_storage().get_db().get_database_size(); + res.update_available = m_restricted ? false : m_core.is_update_available(); res.version = m_restricted ? "" : MONERO_VERSION; return true; } @@ -1585,13 +1587,13 @@ namespace cryptonote res.target = m_core.get_blockchain_storage().get_current_hard_fork_version() < 2 ? DIFFICULTY_TARGET_V1 : DIFFICULTY_TARGET_V2; res.tx_count = m_core.get_blockchain_storage().get_total_transactions() - res.height; //without coinbase res.tx_pool_size = m_core.get_pool_transactions_count(); - res.alt_blocks_count = m_core.get_blockchain_storage().get_alternative_blocks_count(); - uint64_t total_conn = m_p2p.get_connections_count(); - res.outgoing_connections_count = m_p2p.get_outgoing_connections_count(); - res.incoming_connections_count = total_conn - res.outgoing_connections_count; - res.rpc_connections_count = get_connections_count(); - res.white_peerlist_size = m_p2p.get_peerlist_manager().get_white_peers_count(); - res.grey_peerlist_size = m_p2p.get_peerlist_manager().get_gray_peers_count(); + res.alt_blocks_count = m_restricted ? 0 : m_core.get_blockchain_storage().get_alternative_blocks_count(); + uint64_t total_conn = m_restricted ? 0 : m_p2p.get_connections_count(); + res.outgoing_connections_count = m_restricted ? 0 : m_p2p.get_outgoing_connections_count(); + res.incoming_connections_count = m_restricted ? 0 : (total_conn - res.outgoing_connections_count); + res.rpc_connections_count = m_restricted ? 0 : get_connections_count(); + res.white_peerlist_size = m_restricted ? 0 : m_p2p.get_peerlist_manager().get_white_peers_count(); + res.grey_peerlist_size = m_restricted ? 0 : m_p2p.get_peerlist_manager().get_gray_peers_count(); cryptonote::network_type net_type = nettype(); res.mainnet = net_type == MAINNET; @@ -1603,17 +1605,20 @@ namespace cryptonote res.block_size_limit = res.block_weight_limit = m_core.get_blockchain_storage().get_current_cumulative_block_weight_limit(); res.block_size_median = res.block_weight_median = m_core.get_blockchain_storage().get_current_cumulative_block_weight_median(); res.status = CORE_RPC_STATUS_OK; - res.start_time = (uint64_t)m_core.get_start_time(); + res.start_time = m_restricted ? 0 : (uint64_t)m_core.get_start_time(); res.free_space = m_restricted ? std::numeric_limits::max() : m_core.get_free_space(); res.offline = m_core.offline(); - res.bootstrap_daemon_address = m_bootstrap_daemon_address; - res.height_without_bootstrap = res.height; + res.bootstrap_daemon_address = m_restricted ? "" : m_bootstrap_daemon_address; + res.height_without_bootstrap = m_restricted ? 0 : res.height; + if (m_restricted) + res.was_bootstrap_ever_used = false; + else { boost::shared_lock lock(m_bootstrap_daemon_mutex); res.was_bootstrap_ever_used = m_was_bootstrap_ever_used; } - res.database_size = m_core.get_blockchain_storage().get_db().get_database_size(); - res.update_available = m_core.is_update_available(); + res.database_size = m_restricted ? 0 : m_core.get_blockchain_storage().get_db().get_database_size(); + res.update_available = m_restricted ? false : m_core.is_update_available(); res.version = m_restricted ? "" : MONERO_VERSION; return true; } From d21dad70ddda771bf33a19f9a53c48cc91d8464f Mon Sep 17 00:00:00 2001 From: Dusan Klinec Date: Mon, 12 Nov 2018 00:07:25 +0100 Subject: [PATCH 0828/1404] device: enable to use multiple independent device wallets - adds a new option `--hw-device-deriv-path` to the simple wallet. Enables to specify wallet derivation path / wallet code (path avoided so it can be misinterpreted as a file path). - devices can use different derivation mechanisms. Trezor uses standard SLIP-10 mechanism with fixed SLIP-44 prefix for Monero - Trezor: when empty, the default derivation mechanism is used with 44'/128'/0'. When entered the derivation path is 44'/128'/PATH. - Trezor: the path is always taken as elements are hardened (1<<31 bit turned on) --- src/device/device.hpp | 1 + src/device_trezor/device_trezor_base.cpp | 39 +++++++++++++++++++++++- src/device_trezor/device_trezor_base.hpp | 10 +++++- src/simplewallet/simplewallet.cpp | 2 ++ src/wallet/wallet2.cpp | 19 ++++++++++++ src/wallet/wallet2.h | 4 +++ 6 files changed, 73 insertions(+), 2 deletions(-) diff --git a/src/device/device.hpp b/src/device/device.hpp index 3e782d21a47..399648f01d5 100644 --- a/src/device/device.hpp +++ b/src/device/device.hpp @@ -138,6 +138,7 @@ namespace hw { virtual device_protocol_t device_protocol() const { return PROTOCOL_DEFAULT; }; virtual void set_callback(i_device_callback * callback) {}; + virtual void set_derivation_path(const std::string &derivation_path) {}; /* ======================================================================= */ /* LOCKER */ diff --git a/src/device_trezor/device_trezor_base.cpp b/src/device_trezor/device_trezor_base.cpp index dcfc9cb67b6..fddc6082c73 100644 --- a/src/device_trezor/device_trezor_base.cpp +++ b/src/device_trezor/device_trezor_base.cpp @@ -28,6 +28,9 @@ // #include "device_trezor_base.hpp" +#include +#include +#include namespace hw { namespace trezor { @@ -36,8 +39,9 @@ namespace trezor { #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "device.trezor" +#define TREZOR_BIP44_HARDENED_ZERO 0x80000000 - const uint32_t device_trezor_base::DEFAULT_BIP44_PATH[] = {0x8000002c, 0x80000080, 0x80000000}; + const uint32_t device_trezor_base::DEFAULT_BIP44_PATH[] = {0x8000002c, 0x80000080}; device_trezor_base::device_trezor_base(): m_callback(nullptr) { @@ -252,6 +256,39 @@ namespace trezor { } } + void device_trezor_base::ensure_derivation_path() noexcept { + if (m_wallet_deriv_path.empty()){ + m_wallet_deriv_path.push_back(TREZOR_BIP44_HARDENED_ZERO); // default 0' + } + } + + void device_trezor_base::set_derivation_path(const std::string &deriv_path){ + this->m_wallet_deriv_path.clear(); + + if (deriv_path.empty() || deriv_path == "-"){ + ensure_derivation_path(); + return; + } + + CHECK_AND_ASSERT_THROW_MES(deriv_path.size() <= 255, "Derivation path is too long"); + + std::vector fields; + boost::split(fields, deriv_path, boost::is_any_of("/")); + CHECK_AND_ASSERT_THROW_MES(fields.size() <= 10, "Derivation path is too long"); + + boost::regex rgx("^([0-9]+)'?$"); + boost::cmatch match; + + this->m_wallet_deriv_path.reserve(fields.size()); + for(const std::string & cur : fields){ + const bool ok = boost::regex_match(cur.c_str(), match, rgx); + CHECK_AND_ASSERT_THROW_MES(ok, "Invalid wallet code: " << deriv_path << ". Invalid path element: " << cur); + CHECK_AND_ASSERT_THROW_MES(match[0].length() > 0, "Invalid wallet code: " << deriv_path << ". Invalid path element: " << cur); + + const unsigned long cidx = std::stoul(match[0].str()) | TREZOR_BIP44_HARDENED_ZERO; + this->m_wallet_deriv_path.push_back((unsigned int)cidx); + } + } /* ======================================================================= */ /* TREZOR PROTOCOL */ diff --git a/src/device_trezor/device_trezor_base.hpp b/src/device_trezor/device_trezor_base.hpp index a4e92bf7853..4d205ad7a6e 100644 --- a/src/device_trezor/device_trezor_base.hpp +++ b/src/device_trezor/device_trezor_base.hpp @@ -71,6 +71,7 @@ namespace trezor { i_device_callback * m_callback; std::string full_name; + std::vector m_wallet_deriv_path; cryptonote::network_type network_type; @@ -81,6 +82,7 @@ namespace trezor { void require_connected(); void call_ping_unsafe(); void test_ping(); + void ensure_derivation_path() noexcept; // Communication methods @@ -176,9 +178,13 @@ namespace trezor { msg->add_address_n(x); } } else { + ensure_derivation_path(); for (unsigned int i : DEFAULT_BIP44_PATH) { msg->add_address_n(i); } + for (unsigned int i : m_wallet_deriv_path) { + msg->add_address_n(i); + } } if (network_type){ @@ -201,7 +207,7 @@ namespace trezor { bool reset(); // Default derivation path for Monero - static const uint32_t DEFAULT_BIP44_PATH[3]; + static const uint32_t DEFAULT_BIP44_PATH[2]; std::shared_ptr getTransport(){ return m_transport; @@ -215,6 +221,8 @@ namespace trezor { return m_callback; } + void set_derivation_path(const std::string &deriv_path) override; + /* ======================================================================= */ /* SETUP/TEARDOWN */ /* ======================================================================= */ diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 6d19235c545..07084d15b09 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -3804,9 +3804,11 @@ boost::optional simple_wallet::new_wallet(const boost::pr m_wallet->set_refresh_from_block_height(m_restore_height); auto device_desc = tools::wallet2::device_name_option(vm); + auto device_derivation_path = tools::wallet2::device_derivation_path_option(vm); try { bool create_address_file = command_line::get_arg(vm, arg_create_address_file); + m_wallet->device_derivation_path(device_derivation_path); m_wallet->restore(m_wallet_file, std::move(rc.second).password(), device_desc.empty() ? "Ledger" : device_desc, create_address_file); message_writer(console_color_white, true) << tr("Generated new wallet on hw device: ") << m_wallet->get_account().get_public_address_str(m_wallet->nettype()); diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index ae6ed0937cc..83aae6de3d6 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -207,6 +207,7 @@ struct options { }; const command_line::arg_descriptor kdf_rounds = {"kdf-rounds", tools::wallet2::tr("Number of rounds for the key derivation function"), 1}; const command_line::arg_descriptor hw_device = {"hw-device", tools::wallet2::tr("HW device to use"), ""}; + const command_line::arg_descriptor hw_device_derivation_path = {"hw-device-deriv-path", tools::wallet2::tr("HW device wallet derivation path (e.g., SLIP-10)"), ""}; const command_line::arg_descriptor tx_notify = { "tx-notify" , "Run a program for each new incoming transaction, '%s' will be replaced by the transaction hash" , "" }; }; @@ -259,6 +260,7 @@ std::unique_ptr make_basic(const boost::program_options::variabl auto daemon_host = command_line::get_arg(vm, opts.daemon_host); auto daemon_port = command_line::get_arg(vm, opts.daemon_port); auto device_name = command_line::get_arg(vm, opts.hw_device); + auto device_derivation_path = command_line::get_arg(vm, opts.hw_device_derivation_path); THROW_WALLET_EXCEPTION_IF(!daemon_address.empty() && !daemon_host.empty() && 0 != daemon_port, tools::error::wallet_internal_error, tools::wallet2::tr("can't specify daemon host or port more than once")); @@ -314,6 +316,7 @@ std::unique_ptr make_basic(const boost::program_options::variabl boost::filesystem::path ringdb_path = command_line::get_arg(vm, opts.shared_ringdb_dir); wallet->set_ring_database(ringdb_path.string()); wallet->device_name(device_name); + wallet->device_derivation_path(device_derivation_path); try { @@ -912,6 +915,11 @@ std::string wallet2::device_name_option(const boost::program_options::variables_ return command_line::get_arg(vm, options().hw_device); } +std::string wallet2::device_derivation_path_option(const boost::program_options::variables_map &vm) +{ + return command_line::get_arg(vm, options().hw_device_derivation_path); +} + void wallet2::init_options(boost::program_options::options_description& desc_params) { const options opts{}; @@ -928,6 +936,7 @@ void wallet2::init_options(boost::program_options::options_description& desc_par command_line::add_arg(desc_params, opts.shared_ringdb_dir); command_line::add_arg(desc_params, opts.kdf_rounds); command_line::add_arg(desc_params, opts.hw_device); + command_line::add_arg(desc_params, opts.hw_device_derivation_path); command_line::add_arg(desc_params, opts.tx_notify); } @@ -1089,6 +1098,7 @@ bool wallet2::reconnect_device() hw::device &hwdev = lookup_device(m_device_name); hwdev.set_name(m_device_name); hwdev.set_network_type(m_nettype); + hwdev.set_derivation_path(m_device_derivation_path); hwdev.set_callback(get_device_callback()); r = hwdev.init(); if (!r){ @@ -3160,6 +3170,9 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable value.SetString(m_device_name.c_str(), m_device_name.size()); json.AddMember("device_name", value, json.GetAllocator()); + value.SetString(m_device_derivation_path.c_str(), m_device_derivation_path.size()); + json.AddMember("device_derivation_path", value, json.GetAllocator()); + // Serialize the JSON object rapidjson::StringBuffer buffer; rapidjson::Writer writer(buffer); @@ -3279,6 +3292,7 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_ m_subaddress_lookahead_major = SUBADDRESS_LOOKAHEAD_MAJOR; m_subaddress_lookahead_minor = SUBADDRESS_LOOKAHEAD_MINOR; m_device_name = ""; + m_device_derivation_path = ""; m_key_device_type = hw::device::device_type::SOFTWARE; encrypted_secret_keys = false; } @@ -3446,6 +3460,9 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_ m_device_name = m_key_device_type == hw::device::device_type::LEDGER ? "Ledger" : "default"; } } + + GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, device_derivation_path, std::string, String, false, std::string()); + m_device_derivation_path = field_device_derivation_path; } else { @@ -3460,6 +3477,7 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_ hw::device &hwdev = lookup_device(m_device_name); THROW_WALLET_EXCEPTION_IF(!hwdev.set_name(m_device_name), error::wallet_internal_error, "Could not set device name " + m_device_name); hwdev.set_network_type(m_nettype); + hwdev.set_derivation_path(m_device_derivation_path); hwdev.set_callback(get_device_callback()); THROW_WALLET_EXCEPTION_IF(!hwdev.init(), error::wallet_internal_error, "Could not initialize the device " + m_device_name); THROW_WALLET_EXCEPTION_IF(!hwdev.connect(), error::wallet_internal_error, "Could not connect to the device " + m_device_name); @@ -3967,6 +3985,7 @@ void wallet2::restore(const std::string& wallet_, const epee::wipeable_string& p auto &hwdev = lookup_device(device_name); hwdev.set_name(device_name); hwdev.set_network_type(m_nettype); + hwdev.set_derivation_path(m_device_derivation_path); hwdev.set_callback(get_device_callback()); m_account.create_from_device(hwdev); diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index c07054177e2..a588e800325 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -191,6 +191,7 @@ namespace tools static bool has_testnet_option(const boost::program_options::variables_map& vm); static bool has_stagenet_option(const boost::program_options::variables_map& vm); static std::string device_name_option(const boost::program_options::variables_map& vm); + static std::string device_derivation_path_option(const boost::program_options::variables_map &vm); static void init_options(boost::program_options::options_description& desc_params); //! Uses stdin and stdout. Returns a wallet2 if no errors. @@ -978,6 +979,8 @@ namespace tools void confirm_non_default_ring_size(bool always) { m_confirm_non_default_ring_size = always; } const std::string & device_name() const { return m_device_name; } void device_name(const std::string & device_name) { m_device_name = device_name; } + const std::string & device_derivation_path() const { return m_device_derivation_path; } + void device_derivation_path(const std::string &device_derivation_path) { m_device_derivation_path = device_derivation_path; } bool get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key, std::vector &additional_tx_keys) const; void set_tx_key(const crypto::hash &txid, const crypto::secret_key &tx_key, const std::vector &additional_tx_keys); @@ -1384,6 +1387,7 @@ namespace tools std::unordered_set m_scanned_pool_txs[2]; size_t m_subaddress_lookahead_major, m_subaddress_lookahead_minor; std::string m_device_name; + std::string m_device_derivation_path; // Aux transaction data from device std::unordered_map m_tx_device; From 9cf636af696edfa5ad5d0679c95a67f9a15e0635 Mon Sep 17 00:00:00 2001 From: Dusan Klinec Date: Mon, 12 Nov 2018 04:13:54 +0100 Subject: [PATCH 0829/1404] device/trezor: ask for KI sync on first refresh When doing a first refresh on HW-token based wallet KI sync is required if money were received. Received money may indicate wallet was already used before the restore I.e., some transaction could have been already sent from the wallet. The spent UTXO would not be detected as spent which could lead to double spending errors on submitting a new transaction. Thus if the wallet is HW-token based with the cold signing protocol and the first refresh detected received money the user is asked to perform the key image sync. --- src/simplewallet/simplewallet.cpp | 53 ++++++++++++++++++++++++------- src/simplewallet/simplewallet.h | 2 ++ src/wallet/wallet2.cpp | 14 +++++--- src/wallet/wallet2.h | 7 +++- 4 files changed, 58 insertions(+), 18 deletions(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 07084d15b09..c85cbfb8ac7 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -4341,6 +4341,29 @@ void simple_wallet::on_passphrase_request(bool on_device, epee::wipeable_string passphrase = pwd_container->password(); } //---------------------------------------------------------------------------------------------------- +void simple_wallet::on_refresh_finished(uint64_t start_height, uint64_t fetched_blocks, bool is_init, bool received_money) +{ + // Key image sync after the first refresh + if (!m_wallet->get_account().get_device().has_tx_cold_sign()) { + return; + } + + if (!received_money || m_wallet->get_device_last_key_image_sync() != 0) { + return; + } + + // Finished first refresh for HW device and money received -> KI sync + message_writer() << "\n" << tr("The first refresh has finished for the HW-based wallet with received money. hw_key_images_sync is needed. "); + + std::string accepted = input_line(tr("Do you want to do it now? (Y/Yes/N/No): ")); + if (std::cin.eof() || !command_line::is_yes(accepted)) { + message_writer(console_color_red, false) << tr("hw_key_images_sync skipped. Run command manually before a transfer."); + return; + } + + key_images_sync_intern(); +} +//---------------------------------------------------------------------------------------------------- bool simple_wallet::refresh_main(uint64_t start_height, enum ResetType reset, bool is_init) { if (!try_connect_to_daemon(is_init)) @@ -4358,13 +4381,14 @@ bool simple_wallet::refresh_main(uint64_t start_height, enum ResetType reset, bo message_writer() << tr("Starting refresh..."); uint64_t fetched_blocks = 0; + bool received_money = false; bool ok = false; std::ostringstream ss; try { m_in_manual_refresh.store(true, std::memory_order_relaxed); epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){m_in_manual_refresh.store(false, std::memory_order_relaxed);}); - m_wallet->refresh(m_wallet->is_trusted_daemon(), start_height, fetched_blocks); + m_wallet->refresh(m_wallet->is_trusted_daemon(), start_height, fetched_blocks, received_money); ok = true; // Clear line "Height xxx of xxx" std::cout << "\r \r"; @@ -4372,6 +4396,7 @@ bool simple_wallet::refresh_main(uint64_t start_height, enum ResetType reset, bo if (is_init) print_accounts(); show_balance_unlocked(); + on_refresh_finished(start_height, fetched_blocks, is_init, received_money); } catch (const tools::error::daemon_busy&) { @@ -8134,13 +8159,13 @@ bool simple_wallet::hw_key_images_sync(const std::vector &args) fail_msg_writer() << tr("hw wallet does not support cold KI sync"); return true; } - if (!m_wallet->is_trusted_daemon()) - { - fail_msg_writer() << tr("this command requires a trusted daemon. Enable with --trusted-daemon"); - return true; - } LOCK_IDLE_SCOPE(); + key_images_sync_intern(); + return true; +} +//---------------------------------------------------------------------------------------------------- +void simple_wallet::key_images_sync_intern(){ try { message_writer(console_color_white, false) << tr("Please confirm the key image sync on the device"); @@ -8149,19 +8174,23 @@ bool simple_wallet::hw_key_images_sync(const std::vector &args) uint64_t height = m_wallet->cold_key_image_sync(spent, unspent); if (height > 0) { - success_msg_writer() << tr("Signed key images imported to height ") << height << ", " - << print_money(spent) << tr(" spent, ") << print_money(unspent) << tr(" unspent"); - } else { + success_msg_writer() << tr("Key images synchronized to height ") << height; + if (!m_wallet->is_trusted_daemon()) + { + message_writer() << tr("Running untrusted daemon, cannot determine which transaction output is spent. Use a trusted daemon with --trusted-daemon and run rescan_spent"); + } else + { + success_msg_writer() << print_money(spent) << tr(" spent, ") << print_money(unspent) << tr(" unspent"); + } + } + else { fail_msg_writer() << tr("Failed to import key images"); } } catch (const std::exception &e) { fail_msg_writer() << tr("Failed to import key images: ") << e.what(); - return true; } - - return true; } //---------------------------------------------------------------------------------------------------- bool simple_wallet::hw_reconnect(const std::vector &args) diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index 7e2edba7f96..665879cac12 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -241,6 +241,8 @@ namespace cryptonote bool print_ring_members(const std::vector& ptx_vector, std::ostream& ostr); std::string get_prompt() const; bool print_seed(bool encrypted); + void key_images_sync_intern(); + void on_refresh_finished(uint64_t start_height, uint64_t fetched_blocks, bool is_init, bool received_money); struct transfer_view { diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 83aae6de3d6..46bd473531e 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -892,7 +892,8 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended): m_ringdb(), m_last_block_reward(0), m_encrypt_keys_after_refresh(boost::none), - m_unattended(unattended) + m_unattended(unattended), + m_device_last_key_image_sync(0) { } @@ -3009,6 +3010,7 @@ bool wallet2::clear() m_subaddresses.clear(); m_subaddress_labels.clear(); m_multisig_rounds_passed = 0; + m_device_last_key_image_sync = 0; return true; } @@ -9235,9 +9237,7 @@ void wallet2::cold_sign_tx(const std::vector& ptx_vector, signed_tx_ //---------------------------------------------------------------------------------------------------- uint64_t wallet2::cold_key_image_sync(uint64_t &spent, uint64_t &unspent) { auto & hwdev = get_account().get_device(); - if (!hwdev.has_ki_cold_sync()){ - throw std::invalid_argument("Device does not support cold ki sync protocol"); - } + CHECK_AND_ASSERT_THROW_MES(hwdev.has_ki_cold_sync(), "Device does not support cold ki sync protocol"); auto dev_cold = dynamic_cast<::hw::device_cold*>(&hwdev); CHECK_AND_ASSERT_THROW_MES(dev_cold, "Device does not implement cold signing interface"); @@ -9248,7 +9248,11 @@ uint64_t wallet2::cold_key_image_sync(uint64_t &spent, uint64_t &unspent) { dev_cold->ki_sync(&wallet_shim, m_transfers, ski); - return import_key_images(ski, 0, spent, unspent); + // Call COMMAND_RPC_IS_KEY_IMAGE_SPENT only if daemon is trusted. + uint64_t import_res = import_key_images(ski, 0, spent, unspent, is_trusted_daemon()); + m_device_last_key_image_sync = time(NULL); + + return import_res; } //---------------------------------------------------------------------------------------------------- void wallet2::get_hard_fork_info(uint8_t version, uint64_t &earliest_height) const diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index a588e800325..4d121598b61 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -810,6 +810,7 @@ namespace tools bool is_transfer_unlocked(uint64_t unlock_time, uint64_t block_height) const; uint64_t get_last_block_reward() const { return m_last_block_reward; } + uint64_t get_device_last_key_image_sync() const { return m_device_last_key_image_sync; } template inline void serialize(t_archive &a, const unsigned int ver) @@ -918,6 +919,9 @@ namespace tools if(ver < 26) return; a & m_tx_device; + if(ver < 27) + return; + a & m_device_last_key_image_sync; } /*! @@ -1388,6 +1392,7 @@ namespace tools size_t m_subaddress_lookahead_major, m_subaddress_lookahead_minor; std::string m_device_name; std::string m_device_derivation_path; + uint64_t m_device_last_key_image_sync; // Aux transaction data from device std::unordered_map m_tx_device; @@ -1424,7 +1429,7 @@ namespace tools std::unique_ptr m_device_callback; }; } -BOOST_CLASS_VERSION(tools::wallet2, 26) +BOOST_CLASS_VERSION(tools::wallet2, 27) BOOST_CLASS_VERSION(tools::wallet2::transfer_details, 10) BOOST_CLASS_VERSION(tools::wallet2::multisig_info, 1) BOOST_CLASS_VERSION(tools::wallet2::multisig_info::LR, 0) From 65b9bca70e3aa0aabc135f952da3695fdab00918 Mon Sep 17 00:00:00 2001 From: Dusan Klinec Date: Thu, 29 Nov 2018 04:22:52 +0100 Subject: [PATCH 0830/1404] device/trezor: python2 compatibility - bundle dependencies --- src/device_trezor/trezor/tools/README.md | 22 ++- src/device_trezor/trezor/tools/pb2cpp.py | 14 +- .../trezor/tools/py2backports/__init__.py | 0 .../trezor/tools/py2backports/tempfile.py | 72 +++++++++ .../trezor/tools/py2backports/weakref.py | 148 ++++++++++++++++++ 5 files changed, 248 insertions(+), 8 deletions(-) create mode 100644 src/device_trezor/trezor/tools/py2backports/__init__.py create mode 100644 src/device_trezor/trezor/tools/py2backports/tempfile.py create mode 100644 src/device_trezor/trezor/tools/py2backports/weakref.py diff --git a/src/device_trezor/trezor/tools/README.md b/src/device_trezor/trezor/tools/README.md index b176017ac3c..0802e734a09 100644 --- a/src/device_trezor/trezor/tools/README.md +++ b/src/device_trezor/trezor/tools/README.md @@ -10,14 +10,28 @@ Install `protoc` for your distribution. Requirements: Soft requirement: Python 3, can be easily installed with [pyenv]. +If Python 3 is used there are no additional python dependencies. -### Python 2 +Since Cmake 3.12 the `FindPython` module is used to locate the Python +interpreter in your system. It preferably searches for Python 3, if none +is found, it searches for Python 2. -Workaround if there is no Python3 available: +Lower version of the cmake uses another module which does not guarantee +ordering. If you want to override the selected python you can do it in +the following way: ```bash -pip install backports.tempfile -``` +export TREZOR_PYTHON=`which python3` +``` + + +### Python 2.7+ + +Python 3 has `tempfile.TemporaryDirectory` available but Python 2 lacks +this class so the message generation code uses `backports.tempfile` package +bundled in the repository. + +The minimal Python versions are 2.7 and 3.4 ### Regenerate messages diff --git a/src/device_trezor/trezor/tools/pb2cpp.py b/src/device_trezor/trezor/tools/pb2cpp.py index 4d7cc775f00..3e0318ea552 100644 --- a/src/device_trezor/trezor/tools/pb2cpp.py +++ b/src/device_trezor/trezor/tools/pb2cpp.py @@ -14,12 +14,18 @@ try: from tempfile import TemporaryDirectory except: - # Py2 backward compatibility, optionally installed by user - # pip install backports.tempfile + # Py2 backward compatibility, using bundled sources. + # Original source: pip install backports.tempfile try: - from backports.tempfile import TemporaryDirectory + # Try bundled python version + import sys + sys.path.append(os.path.dirname(__file__)) + from py2backports.tempfile import TemporaryDirectory + except: - raise EnvironmentError('TemporaryDirectory could not be imported. Try: pip install backports.tempfile') + raise EnvironmentError('Python 2.7+ or 3.4+ is required. ' + 'TemporaryDirectory is not available in Python 2.' + 'Try to specify python to use, e.g.: "export TREZOR_PYTHON=`which python3`"') AUTO_HEADER = "# Automatically generated by pb2cpp\n" diff --git a/src/device_trezor/trezor/tools/py2backports/__init__.py b/src/device_trezor/trezor/tools/py2backports/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/device_trezor/trezor/tools/py2backports/tempfile.py b/src/device_trezor/trezor/tools/py2backports/tempfile.py new file mode 100644 index 00000000000..e259dea3f8a --- /dev/null +++ b/src/device_trezor/trezor/tools/py2backports/tempfile.py @@ -0,0 +1,72 @@ +""" +https://github.com/pjdelport/backports.tempfile/blob/master/src/backports/tempfile.py +Partial backport of Python 3.5's tempfile module: + TemporaryDirectory +Backport modifications are marked with marked with "XXX backport". +""" +from __future__ import absolute_import + +import sys +import warnings as _warnings +from shutil import rmtree as _rmtree + +from py2backports.weakref import finalize + + +# XXX backport: Rather than backporting all of mkdtemp(), we just create a +# thin wrapper implementing its Python 3.5 signature. +if sys.version_info < (3, 5): + from tempfile import mkdtemp as old_mkdtemp + + def mkdtemp(suffix=None, prefix=None, dir=None): + """ + Wrap `tempfile.mkdtemp()` to make the suffix and prefix optional (like Python 3.5). + """ + kwargs = {k: v for (k, v) in + dict(suffix=suffix, prefix=prefix, dir=dir).items() + if v is not None} + return old_mkdtemp(**kwargs) + +else: + from tempfile import mkdtemp + + +# XXX backport: ResourceWarning was added in Python 3.2. +# For earlier versions, fall back to RuntimeWarning instead. +_ResourceWarning = RuntimeWarning if sys.version_info < (3, 2) else ResourceWarning + + +class TemporaryDirectory(object): + """Create and return a temporary directory. This has the same + behavior as mkdtemp but can be used as a context manager. For + example: + with TemporaryDirectory() as tmpdir: + ... + Upon exiting the context, the directory and everything contained + in it are removed. + """ + + def __init__(self, suffix=None, prefix=None, dir=None): + self.name = mkdtemp(suffix, prefix, dir) + self._finalizer = finalize( + self, self._cleanup, self.name, + warn_message="Implicitly cleaning up {!r}".format(self)) + + @classmethod + def _cleanup(cls, name, warn_message): + _rmtree(name) + _warnings.warn(warn_message, _ResourceWarning) + + + def __repr__(self): + return "<{} {!r}>".format(self.__class__.__name__, self.name) + + def __enter__(self): + return self.name + + def __exit__(self, exc, value, tb): + self.cleanup() + + def cleanup(self): + if self._finalizer.detach(): + _rmtree(self.name) diff --git a/src/device_trezor/trezor/tools/py2backports/weakref.py b/src/device_trezor/trezor/tools/py2backports/weakref.py new file mode 100644 index 00000000000..eb646812b4c --- /dev/null +++ b/src/device_trezor/trezor/tools/py2backports/weakref.py @@ -0,0 +1,148 @@ +""" +https://github.com/pjdelport/backports.weakref/blob/master/src/backports/weakref.py +Partial backport of Python 3.6's weakref module: + finalize (new in Python 3.4) +Backport modifications are marked with "XXX backport". +""" +from __future__ import absolute_import + +import itertools +import sys +from weakref import ref + +__all__ = ['finalize'] + + +class finalize(object): + """Class for finalization of weakrefable objects + finalize(obj, func, *args, **kwargs) returns a callable finalizer + object which will be called when obj is garbage collected. The + first time the finalizer is called it evaluates func(*arg, **kwargs) + and returns the result. After this the finalizer is dead, and + calling it just returns None. + When the program exits any remaining finalizers for which the + atexit attribute is true will be run in reverse order of creation. + By default atexit is true. + """ + + # Finalizer objects don't have any state of their own. They are + # just used as keys to lookup _Info objects in the registry. This + # ensures that they cannot be part of a ref-cycle. + + __slots__ = () + _registry = {} + _shutdown = False + _index_iter = itertools.count() + _dirty = False + _registered_with_atexit = False + + class _Info(object): + __slots__ = ("weakref", "func", "args", "kwargs", "atexit", "index") + + def __init__(self, obj, func, *args, **kwargs): + if not self._registered_with_atexit: + # We may register the exit function more than once because + # of a thread race, but that is harmless + import atexit + atexit.register(self._exitfunc) + finalize._registered_with_atexit = True + info = self._Info() + info.weakref = ref(obj, self) + info.func = func + info.args = args + info.kwargs = kwargs or None + info.atexit = True + info.index = next(self._index_iter) + self._registry[self] = info + finalize._dirty = True + + def __call__(self, _=None): + """If alive then mark as dead and return func(*args, **kwargs); + otherwise return None""" + info = self._registry.pop(self, None) + if info and not self._shutdown: + return info.func(*info.args, **(info.kwargs or {})) + + def detach(self): + """If alive then mark as dead and return (obj, func, args, kwargs); + otherwise return None""" + info = self._registry.get(self) + obj = info and info.weakref() + if obj is not None and self._registry.pop(self, None): + return (obj, info.func, info.args, info.kwargs or {}) + + def peek(self): + """If alive then return (obj, func, args, kwargs); + otherwise return None""" + info = self._registry.get(self) + obj = info and info.weakref() + if obj is not None: + return (obj, info.func, info.args, info.kwargs or {}) + + @property + def alive(self): + """Whether finalizer is alive""" + return self in self._registry + + @property + def atexit(self): + """Whether finalizer should be called at exit""" + info = self._registry.get(self) + return bool(info) and info.atexit + + @atexit.setter + def atexit(self, value): + info = self._registry.get(self) + if info: + info.atexit = bool(value) + + def __repr__(self): + info = self._registry.get(self) + obj = info and info.weakref() + if obj is None: + return '<%s object at %#x; dead>' % (type(self).__name__, id(self)) + else: + return '<%s object at %#x; for %r at %#x>' % \ + (type(self).__name__, id(self), type(obj).__name__, id(obj)) + + @classmethod + def _select_for_exit(cls): + # Return live finalizers marked for exit, oldest first + L = [(f,i) for (f,i) in cls._registry.items() if i.atexit] + L.sort(key=lambda item:item[1].index) + return [f for (f,i) in L] + + @classmethod + def _exitfunc(cls): + # At shutdown invoke finalizers for which atexit is true. + # This is called once all other non-daemonic threads have been + # joined. + reenable_gc = False + try: + if cls._registry: + import gc + if gc.isenabled(): + reenable_gc = True + gc.disable() + pending = None + while True: + if pending is None or finalize._dirty: + pending = cls._select_for_exit() + finalize._dirty = False + if not pending: + break + f = pending.pop() + try: + # gc is disabled, so (assuming no daemonic + # threads) the following is the only line in + # this function which might trigger creation + # of a new finalizer + f() + except Exception: + sys.excepthook(*sys.exc_info()) + assert f not in cls._registry + finally: + # prevent any more finalizers from executing during shutdown + finalize._shutdown = True + if reenable_gc: + gc.enable() From d71f89e2a26720021f7509f8c1eee87645f48529 Mon Sep 17 00:00:00 2001 From: Dusan Klinec Date: Wed, 28 Nov 2018 22:22:11 +0100 Subject: [PATCH 0831/1404] device/trezor: device/trezor: correct device initialization, status check - checks if the device is in the correct usable state - implements check for the v2.0.9 firmware which does not support payment IDs - simple transacttion check, payment id fmt consistency - minor fixes, refactoring, webusb session counting fix --- cmake/CheckTrezor.cmake | 2 +- src/device_trezor/device_trezor.cpp | 77 +++++++++++++++++++++--- src/device_trezor/device_trezor.hpp | 5 +- src/device_trezor/device_trezor_base.cpp | 58 ++++++++++++++++-- src/device_trezor/device_trezor_base.hpp | 19 +++++- src/device_trezor/trezor/transport.cpp | 2 +- 6 files changed, 140 insertions(+), 23 deletions(-) diff --git a/cmake/CheckTrezor.cmake b/cmake/CheckTrezor.cmake index ea21237fdd2..bcd3cfc2c66 100644 --- a/cmake/CheckTrezor.cmake +++ b/cmake/CheckTrezor.cmake @@ -56,7 +56,7 @@ if(Protobuf_FOUND AND USE_DEVICE_TREZOR AND TREZOR_PYTHON) endif() if(USE_DEVICE_TREZOR_UDP_RELEASE) - add_definitions(-DWITH_DEVICE_TREZOR_UDP_RELEASE=1) + add_definitions(-DUSE_DEVICE_TREZOR_UDP_RELEASE=1) endif() if (Protobuf_INCLUDE_DIR) diff --git a/src/device_trezor/device_trezor.cpp b/src/device_trezor/device_trezor.cpp index 5096fcea8ab..8868fb99598 100644 --- a/src/device_trezor/device_trezor.cpp +++ b/src/device_trezor/device_trezor.cpp @@ -121,7 +121,8 @@ namespace trezor { const boost::optional & network_type){ AUTO_LOCK_CMD(); require_connected(); - test_ping(); + device_state_reset_unsafe(); + require_initialized(); auto req = std::make_shared(); this->set_msg_addr(req.get(), path, network_type); @@ -136,7 +137,8 @@ namespace trezor { const boost::optional & network_type){ AUTO_LOCK_CMD(); require_connected(); - test_ping(); + device_state_reset_unsafe(); + require_initialized(); auto req = std::make_shared(); this->set_msg_addr(req.get(), path, network_type); @@ -152,7 +154,8 @@ namespace trezor { { AUTO_LOCK_CMD(); require_connected(); - test_ping(); + device_state_reset_unsafe(); + require_initialized(); std::shared_ptr req; @@ -238,12 +241,11 @@ namespace trezor { cpend.construction_data = cdata.tx_data; // Transaction check - cryptonote::blobdata tx_blob; - cryptonote::transaction tx_deserialized; - bool r = cryptonote::t_serializable_object_to_blob(cpend.tx, tx_blob); - CHECK_AND_ASSERT_THROW_MES(r, "Transaction serialization failed"); - r = cryptonote::parse_and_validate_tx_from_blob(tx_blob, tx_deserialized); - CHECK_AND_ASSERT_THROW_MES(r, "Transaction deserialization failed"); + try { + transaction_check(cdata, aux_data); + } catch(const std::exception &e){ + throw exc::ProtocolException(std::string("Transaction verification failed: ") + e.what()); + } std::string key_images; bool all_are_txin_to_key = std::all_of(cdata.tx.vin.begin(), cdata.tx.vin.end(), [&](const cryptonote::txin_v& s_e) -> bool @@ -283,7 +285,8 @@ namespace trezor { { AUTO_LOCK_CMD(); require_connected(); - test_ping(); + device_state_reset_unsafe(); + require_initialized(); CHECK_AND_ASSERT_THROW_MES(idx < unsigned_tx.txes.size(), "Invalid transaction index"); signer = std::make_shared(wallet, &unsigned_tx, idx, &aux_data); @@ -294,6 +297,7 @@ namespace trezor { // Step: Init auto init_msg = signer->step_init(); this->set_msg_addr(init_msg.get()); + transaction_pre_check(init_msg); auto response = this->client_exchange(init_msg); signer->step_init_ack(response); @@ -351,6 +355,59 @@ namespace trezor { signer->step_final_ack(ack_final); } + void device_trezor::transaction_pre_check(std::shared_ptr init_msg) + { + CHECK_AND_ASSERT_THROW_MES(init_msg, "TransactionInitRequest is empty"); + CHECK_AND_ASSERT_THROW_MES(init_msg->has_tsx_data(), "TransactionInitRequest has no transaction data"); + CHECK_AND_ASSERT_THROW_MES(m_features, "Device state not initialized"); // make sure the caller did not reset features + const bool nonce_required = init_msg->tsx_data().has_payment_id() && init_msg->tsx_data().payment_id().size() > 0; + + if (nonce_required){ + // Versions 2.0.9 and lower do not support payment ID + CHECK_AND_ASSERT_THROW_MES(m_features->has_major_version() && m_features->has_minor_version() && m_features->has_patch_version(), "Invalid Trezor firmware version information"); + const uint32_t vma = m_features->major_version(); + const uint32_t vmi = m_features->minor_version(); + const uint32_t vpa = m_features->patch_version(); + if (vma < 2 || (vma == 2 && vmi == 0 && vpa <= 9)) { + throw exc::TrezorException("Trezor firmware 2.0.9 and lower does not support transactions with short payment IDs or integrated addresses. Please update."); + } + } + } + + void device_trezor::transaction_check(const protocol::tx::TData & tdata, const hw::tx_aux_data & aux_data) + { + // Simple serialization check + cryptonote::blobdata tx_blob; + cryptonote::transaction tx_deserialized; + bool r = cryptonote::t_serializable_object_to_blob(tdata.tx, tx_blob); + CHECK_AND_ASSERT_THROW_MES(r, "Transaction serialization failed"); + r = cryptonote::parse_and_validate_tx_from_blob(tx_blob, tx_deserialized); + CHECK_AND_ASSERT_THROW_MES(r, "Transaction deserialization failed"); + + // Extras check + std::vector tx_extra_fields; + cryptonote::tx_extra_nonce nonce; + + r = cryptonote::parse_tx_extra(tdata.tx.extra, tx_extra_fields); + CHECK_AND_ASSERT_THROW_MES(r, "tx.extra parsing failed"); + + const bool nonce_required = tdata.tsx_data.has_payment_id() && tdata.tsx_data.payment_id().size() > 0; + const bool has_nonce = cryptonote::find_tx_extra_field_by_type(tx_extra_fields, nonce); + CHECK_AND_ASSERT_THROW_MES(has_nonce == nonce_required, "Transaction nonce presence inconsistent"); + + if (nonce_required){ + const std::string & payment_id = tdata.tsx_data.payment_id(); + if (payment_id.size() == 32){ + crypto::hash payment_id_long{}; + CHECK_AND_ASSERT_THROW_MES(cryptonote::get_payment_id_from_tx_extra_nonce(nonce.nonce, payment_id_long), "Long payment ID not present"); + + } else if (payment_id.size() == 8){ + crypto::hash8 payment_id_short{}; + CHECK_AND_ASSERT_THROW_MES(cryptonote::get_encrypted_payment_id_from_tx_extra_nonce(nonce.nonce, payment_id_short), "Short payment ID not present"); + } + } + } + #else //WITH_DEVICE_TREZOR void register_all(std::map> ®istry) { diff --git a/src/device_trezor/device_trezor.hpp b/src/device_trezor/device_trezor.hpp index a2134574c91..1f08be88788 100644 --- a/src/device_trezor/device_trezor.hpp +++ b/src/device_trezor/device_trezor.hpp @@ -57,9 +57,8 @@ namespace trezor { */ class device_trezor : public hw::trezor::device_trezor_base, public hw::device_cold { protected: - // To speed up blockchain parsing the view key maybe handle here. - crypto::secret_key viewkey; - bool has_view_key; + void transaction_pre_check(std::shared_ptr init_msg); + void transaction_check(const protocol::tx::TData & tdata, const hw::tx_aux_data & aux_data); public: device_trezor(); diff --git a/src/device_trezor/device_trezor_base.cpp b/src/device_trezor/device_trezor_base.cpp index fddc6082c73..5071932eec8 100644 --- a/src/device_trezor/device_trezor_base.cpp +++ b/src/device_trezor/device_trezor_base.cpp @@ -65,7 +65,7 @@ namespace trezor { } bool device_trezor_base::set_name(const std::string & name) { - this->full_name = name; + this->m_full_name = name; this->name = ""; auto delim = name.find(':'); @@ -77,10 +77,10 @@ namespace trezor { } const std::string device_trezor_base::get_name() const { - if (this->full_name.empty()) { + if (this->m_full_name.empty()) { return std::string("name).append(">"); } - return this->full_name; + return this->m_full_name; } bool device_trezor_base::init() { @@ -139,6 +139,9 @@ namespace trezor { } bool device_trezor_base::disconnect() { + m_device_state.clear(); + m_features.reset(); + if (m_transport){ try { m_transport->close(); @@ -193,6 +196,25 @@ namespace trezor { } } + void device_trezor_base::require_initialized(){ + if (!m_features){ + throw exc::TrezorException("Device state not initialized"); + } + + if (m_features->has_bootloader_mode() && m_features->bootloader_mode()){ + throw exc::TrezorException("Device is in the bootloader mode"); + } + + if (m_features->has_firmware_present() && !m_features->firmware_present()){ + throw exc::TrezorException("Device has no firmware loaded"); + } + + // Hard requirement on initialized field, has to be there. + if (!m_features->has_initialized() || !m_features->initialized()){ + throw exc::TrezorException("Device is not initialized"); + } + } + void device_trezor_base::call_ping_unsafe(){ auto pingMsg = std::make_shared(); pingMsg->set_message("PING"); @@ -217,7 +239,7 @@ namespace trezor { void device_trezor_base::write_raw(const google::protobuf::Message * msg){ require_connected(); CHECK_AND_ASSERT_THROW_MES(msg, "Empty message"); - this->getTransport()->write(*msg); + this->get_transport()->write(*msg); } GenericMessage device_trezor_base::read_raw(){ @@ -225,7 +247,7 @@ namespace trezor { std::shared_ptr msg_resp; hw::trezor::messages::MessageType msg_resp_type; - this->getTransport()->read(msg_resp, &msg_resp_type); + this->get_transport()->read(msg_resp, &msg_resp_type); return GenericMessage(msg_resp_type, msg_resp); } @@ -314,6 +336,25 @@ namespace trezor { return false; } + void device_trezor_base::device_state_reset_unsafe() + { + require_connected(); + auto initMsg = std::make_shared(); + + if(!m_device_state.empty()) { + initMsg->set_allocated_state(&m_device_state); + } + + m_features = this->client_exchange(initMsg); + initMsg->release_state(); + } + + void device_trezor_base::device_state_reset() + { + AUTO_LOCK_CMD(); + device_state_reset_unsafe(); + } + void device_trezor_base::on_button_request(GenericMessage & resp, const messages::common::ButtonRequest * msg) { CHECK_AND_ASSERT_THROW_MES(msg, "Empty message"); @@ -361,7 +402,13 @@ namespace trezor { // TODO: remove passphrase from memory m.set_passphrase(passphrase.data(), passphrase.size()); } + + if (!m_device_state.empty()){ + m.set_allocated_state(&m_device_state); + } + resp = call_raw(&m); + m.release_state(); } void device_trezor_base::on_passphrase_state_request(GenericMessage & resp, const messages::common::PassphraseStateRequest * msg) @@ -369,6 +416,7 @@ namespace trezor { MDEBUG("on_passhprase_state_request"); CHECK_AND_ASSERT_THROW_MES(msg, "Empty message"); + m_device_state = msg->state(); messages::common::PassphraseStateAck m; resp = call_raw(&m); } diff --git a/src/device_trezor/device_trezor_base.hpp b/src/device_trezor/device_trezor_base.hpp index 4d205ad7a6e..3c35e8acac1 100644 --- a/src/device_trezor/device_trezor_base.hpp +++ b/src/device_trezor/device_trezor_base.hpp @@ -70,8 +70,10 @@ namespace trezor { std::shared_ptr m_transport; i_device_callback * m_callback; - std::string full_name; + std::string m_full_name; std::vector m_wallet_deriv_path; + std::string m_device_state; // returned after passphrase entry, session + std::shared_ptr m_features; // features from the last device reset cryptonote::network_type network_type; @@ -80,8 +82,10 @@ namespace trezor { // void require_connected(); + void require_initialized(); void call_ping_unsafe(); void test_ping(); + void device_state_reset_unsafe(); void ensure_derivation_path() noexcept; // Communication methods @@ -130,7 +134,7 @@ namespace trezor { // Scoped session closer BOOST_SCOPE_EXIT_ALL(&, this) { if (open_session){ - this->getTransport()->close(); + this->get_transport()->close(); } }; @@ -209,7 +213,7 @@ namespace trezor { // Default derivation path for Monero static const uint32_t DEFAULT_BIP44_PATH[2]; - std::shared_ptr getTransport(){ + std::shared_ptr get_transport(){ return m_transport; } @@ -221,6 +225,10 @@ namespace trezor { return m_callback; } + std::shared_ptr & get_features() { + return m_features; + } + void set_derivation_path(const std::string &deriv_path) override; /* ======================================================================= */ @@ -250,6 +258,11 @@ namespace trezor { */ bool ping(); + /** + * Performs Initialize call to the Trezor, resets to known state. + */ + void device_state_reset(); + // Protocol callbacks void on_button_request(GenericMessage & resp, const messages::common::ButtonRequest * msg); void on_pin_request(GenericMessage & resp, const messages::common::PinMatrixRequest * msg); diff --git a/src/device_trezor/trezor/transport.cpp b/src/device_trezor/trezor/transport.cpp index 814537eb633..cd66e59e826 100644 --- a/src/device_trezor/trezor/transport.cpp +++ b/src/device_trezor/trezor/transport.cpp @@ -840,7 +840,7 @@ namespace trezor{ throw exc::DeviceAcquireException("Unable to claim libusb device"); } - m_conn_count += 1; + m_conn_count = 1; m_proto->session_begin(*this); #undef TREZOR_DESTROY_SESSION From 9e64a71e7d1fd70cdf94a3b78782ad06828362b3 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 5 Dec 2018 00:12:29 +0000 Subject: [PATCH 0832/1404] blockchain: call deinit in dtor This ensures the io service that runs in another thread cannot access data after it's deleted --- src/cryptonote_core/blockchain.cpp | 17 ++++++++++------- src/cryptonote_core/blockchain.h | 5 +++++ 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 80e0a983e33..97b8f076514 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -171,6 +171,11 @@ Blockchain::Blockchain(tx_memory_pool& tx_pool) : LOG_PRINT_L3("Blockchain::" << __func__); } //------------------------------------------------------------------ +Blockchain::~Blockchain() +{ + deinit(); +} +//------------------------------------------------------------------ bool Blockchain::have_tx(const crypto::hash &id) const { LOG_PRINT_L3("Blockchain::" << __func__); @@ -550,15 +555,13 @@ bool Blockchain::deinit() // as this should be called if handling a SIGSEGV, need to check // if m_db is a NULL pointer (and thus may have caused the illegal // memory operation), otherwise we may cause a loop. - if (m_db == NULL) - { - throw DB_ERROR("The db pointer is null in Blockchain, the blockchain may be corrupt!"); - } - try { - m_db->close(); - MTRACE("Local blockchain read/write activity stopped successfully"); + if (m_db) + { + m_db->close(); + MTRACE("Local blockchain read/write activity stopped successfully"); + } } catch (const std::exception& e) { diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index dfe833fb45f..835e5addc81 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -120,6 +120,11 @@ namespace cryptonote */ Blockchain(tx_memory_pool& tx_pool); + /** + * @brief Blockchain destructor + */ + ~Blockchain(); + /** * @brief Initialize the Blockchain state * From 1cfd6f1060ca1a8f731638826c3a436a10ccb37e Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 5 Dec 2018 00:30:39 +0000 Subject: [PATCH 0833/1404] unit_tests: strengthen notify test against OS scheduling --- tests/unit_tests/notify.cpp | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/tests/unit_tests/notify.cpp b/tests/unit_tests/notify.cpp index edc4eabdf0b..a5c570bf420 100644 --- a/tests/unit_tests/notify.cpp +++ b/tests/unit_tests/notify.cpp @@ -69,11 +69,22 @@ TEST(notify, works) tools::Notify notify(spec.c_str()); notify.notify("1111111111111111111111111111111111111111111111111111111111111111"); - epee::misc_utils::sleep_no_w(100); - - std::string s; - ASSERT_TRUE(epee::file_io_utils::load_file_to_string(name_template, s)); - ASSERT_TRUE(s == "1111111111111111111111111111111111111111111111111111111111111111"); + bool ok = false; + for (int i = 0; i < 10; ++i) + { + epee::misc_utils::sleep_no_w(100); + std::string s; + if (epee::file_io_utils::load_file_to_string(name_template, s)) + { + if (s == "1111111111111111111111111111111111111111111111111111111111111111") + { + ok = true; + break; + } + std::cout << "got: [" << s << "]" << std::endl; + } + } boost::filesystem::remove(name_template); + ASSERT_TRUE(ok); } From 1505dd38c96172a7a6846b33a052b82a4e9f9a85 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 5 Dec 2018 14:07:45 +0000 Subject: [PATCH 0834/1404] util: set MONERO_DEFAULT_LOG_CATEGORY Otherwise it'd end up with whatever was included last --- src/common/util.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/common/util.cpp b/src/common/util.cpp index 58b0d8210f6..ab84b318553 100644 --- a/src/common/util.cpp +++ b/src/common/util.cpp @@ -81,6 +81,9 @@ using namespace epee; #include #include +#undef MONERO_DEFAULT_LOG_CATEGORY +#define MONERO_DEFAULT_LOG_CATEGORY "util" + namespace tools { std::function signal_handler::m_handler; From 6644b9b7b72919f43d05585a4c2590cec8a7beda Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 5 Dec 2018 19:20:19 +0000 Subject: [PATCH 0835/1404] blockchain_db: remove a couple unused functions --- src/blockchain_db/berkeleydb/db_bdb.cpp | 23 ----------------------- src/blockchain_db/berkeleydb/db_bdb.h | 18 ------------------ src/blockchain_db/lmdb/db_lmdb.cpp | 23 ----------------------- src/blockchain_db/lmdb/db_lmdb.h | 18 ------------------ 4 files changed, 82 deletions(-) diff --git a/src/blockchain_db/berkeleydb/db_bdb.cpp b/src/blockchain_db/berkeleydb/db_bdb.cpp index 6c79120e8ae..b27a00a695f 100644 --- a/src/blockchain_db/berkeleydb/db_bdb.cpp +++ b/src/blockchain_db/berkeleydb/db_bdb.cpp @@ -714,29 +714,6 @@ bool BlockchainBDB::for_all_outputs(std::function ba(ss); - tx_out o; - - if (!(::serialization::serialize(ba, o))) - throw1(DB_ERROR("Error deserializing tx output blob")); - - return o; -} - uint64_t BlockchainBDB::get_output_global_index(const uint64_t& amount, const uint64_t& index) { LOG_PRINT_L3("BlockchainBDB::" << __func__); diff --git a/src/blockchain_db/berkeleydb/db_bdb.h b/src/blockchain_db/berkeleydb/db_bdb.h index 76d0a05174a..e80adae9e1f 100644 --- a/src/blockchain_db/berkeleydb/db_bdb.h +++ b/src/blockchain_db/berkeleydb/db_bdb.h @@ -391,24 +391,6 @@ class BlockchainBDB : public BlockchainDB virtual void check_hard_fork_info(); virtual void drop_hard_fork_info(); - /** - * @brief convert a tx output to a blob for storage - * - * @param output the output to convert - * - * @return the resultant blob - */ - blobdata output_to_blob(const tx_out& output) const; - - /** - * @brief convert a tx output blob to a tx output - * - * @param blob the blob to convert - * - * @return the resultant tx output - */ - tx_out output_from_blob(const blobdata& blob) const; - /** * @brief get the global index of the index-th output of the given amount * diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp index 5af5d19034f..125eff88f07 100644 --- a/src/blockchain_db/lmdb/db_lmdb.cpp +++ b/src/blockchain_db/lmdb/db_lmdb.cpp @@ -1112,29 +1112,6 @@ void BlockchainLMDB::remove_spent_key(const crypto::key_image& k_image) } } -blobdata BlockchainLMDB::output_to_blob(const tx_out& output) const -{ - LOG_PRINT_L3("BlockchainLMDB::" << __func__); - blobdata b; - if (!t_serializable_object_to_blob(output, b)) - throw1(DB_ERROR("Error serializing output to blob")); - return b; -} - -tx_out BlockchainLMDB::output_from_blob(const blobdata& blob) const -{ - LOG_PRINT_L3("BlockchainLMDB::" << __func__); - std::stringstream ss; - ss << blob; - binary_archive ba(ss); - tx_out o; - - if (!(::serialization::serialize(ba, o))) - throw1(DB_ERROR("Error deserializing tx output blob")); - - return o; -} - void BlockchainLMDB::check_open() const { // LOG_PRINT_L3("BlockchainLMDB::" << __func__); diff --git a/src/blockchain_db/lmdb/db_lmdb.h b/src/blockchain_db/lmdb/db_lmdb.h index 26159ab4d9b..12261dca3a3 100644 --- a/src/blockchain_db/lmdb/db_lmdb.h +++ b/src/blockchain_db/lmdb/db_lmdb.h @@ -357,24 +357,6 @@ class BlockchainLMDB : public BlockchainDB virtual void check_hard_fork_info(); virtual void drop_hard_fork_info(); - /** - * @brief convert a tx output to a blob for storage - * - * @param output the output to convert - * - * @return the resultant blob - */ - blobdata output_to_blob(const tx_out& output) const; - - /** - * @brief convert a tx output blob to a tx output - * - * @param blob the blob to convert - * - * @return the resultant tx output - */ - tx_out output_from_blob(const blobdata& blob) const; - void check_open() const; virtual bool is_read_only() const; From ce594f5af78e4722122735d67a019325f1c495cb Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 5 Dec 2018 19:21:43 +0000 Subject: [PATCH 0836/1404] blockchain_db: allocate known size vector only once --- src/blockchain_db/blockchain_db.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/blockchain_db/blockchain_db.cpp b/src/blockchain_db/blockchain_db.cpp index be0ffeac378..c25798c1e0c 100644 --- a/src/blockchain_db/blockchain_db.cpp +++ b/src/blockchain_db/blockchain_db.cpp @@ -170,7 +170,7 @@ void BlockchainDB::add_transaction(const crypto::hash& blk_hash, const transacti uint64_t tx_id = add_transaction_data(blk_hash, tx, tx_hash, tx_prunable_hash); - std::vector amount_output_indices; + std::vector amount_output_indices(tx.vout.size()); // iterate tx.vout using indices instead of C++11 foreach syntax because // we need the index @@ -183,13 +183,13 @@ void BlockchainDB::add_transaction(const crypto::hash& blk_hash, const transacti cryptonote::tx_out vout = tx.vout[i]; rct::key commitment = rct::zeroCommit(vout.amount); vout.amount = 0; - amount_output_indices.push_back(add_output(tx_hash, vout, i, tx.unlock_time, - &commitment)); + amount_output_indices[i] = add_output(tx_hash, vout, i, tx.unlock_time, + &commitment); } else { - amount_output_indices.push_back(add_output(tx_hash, tx.vout[i], i, tx.unlock_time, - tx.version > 1 ? &tx.rct_signatures.outPk[i].mask : NULL)); + amount_output_indices[i] = add_output(tx_hash, tx.vout[i], i, tx.unlock_time, + tx.version > 1 ? &tx.rct_signatures.outPk[i].mask : NULL); } } add_tx_amount_output_indices(tx_id, amount_output_indices); From 833269834d7a93d475df3091c362873501d1cb88 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 5 Dec 2018 19:26:49 +0000 Subject: [PATCH 0837/1404] db_lmdb: inline check_open, it's trivial and called everywhere --- src/blockchain_db/lmdb/db_lmdb.cpp | 13 ++++++------- src/blockchain_db/lmdb/db_lmdb.h | 2 +- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp index 125eff88f07..30d5fae7139 100644 --- a/src/blockchain_db/lmdb/db_lmdb.cpp +++ b/src/blockchain_db/lmdb/db_lmdb.cpp @@ -456,6 +456,12 @@ inline int lmdb_txn_renew(MDB_txn *txn) return res; } +inline void BlockchainLMDB::check_open() const +{ + if (!m_open) + throw0(DB_ERROR("DB operation attempted on a not-open DB instance")); +} + void BlockchainLMDB::do_resize(uint64_t increase_size) { LOG_PRINT_L3("BlockchainLMDB::" << __func__); @@ -1112,13 +1118,6 @@ void BlockchainLMDB::remove_spent_key(const crypto::key_image& k_image) } } -void BlockchainLMDB::check_open() const -{ -// LOG_PRINT_L3("BlockchainLMDB::" << __func__); - if (!m_open) - throw0(DB_ERROR("DB operation attempted on a not-open DB instance")); -} - BlockchainLMDB::~BlockchainLMDB() { LOG_PRINT_L3("BlockchainLMDB::" << __func__); diff --git a/src/blockchain_db/lmdb/db_lmdb.h b/src/blockchain_db/lmdb/db_lmdb.h index 12261dca3a3..9dad5fd0170 100644 --- a/src/blockchain_db/lmdb/db_lmdb.h +++ b/src/blockchain_db/lmdb/db_lmdb.h @@ -357,7 +357,7 @@ class BlockchainLMDB : public BlockchainDB virtual void check_hard_fork_info(); virtual void drop_hard_fork_info(); - void check_open() const; + inline void check_open() const; virtual bool is_read_only() const; From 5511563e92ca9ef62008eab587ccb15aa6de7b11 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 5 Dec 2018 19:27:39 +0000 Subject: [PATCH 0838/1404] db_lmdb: avoid pointless division --- src/blockchain_db/lmdb/db_lmdb.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp index 30d5fae7139..5a079936775 100644 --- a/src/blockchain_db/lmdb/db_lmdb.cpp +++ b/src/blockchain_db/lmdb/db_lmdb.cpp @@ -3106,7 +3106,7 @@ uint64_t BlockchainLMDB::add_block(const block& blk, size_t block_weight, const check_open(); uint64_t m_height = height(); - if (m_height % 1000 == 0) + if (m_height % 1024 == 0) { // for batch mode, DB resize check is done at start of batch transaction if (! m_batch_active && need_resize()) From d1efe3d91c4b1bbb8178a67b445733f5560df1bb Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 5 Dec 2018 20:34:10 +0000 Subject: [PATCH 0839/1404] cryptonote: set tx hash on newly parsed txes when known --- src/cryptonote_basic/cryptonote_basic.h | 2 ++ src/cryptonote_basic/cryptonote_format_utils.cpp | 1 + src/cryptonote_core/tx_pool.cpp | 16 +++++++++++----- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/cryptonote_basic/cryptonote_basic.h b/src/cryptonote_basic/cryptonote_basic.h index b0eabb0aa97..47674c9b319 100644 --- a/src/cryptonote_basic/cryptonote_basic.h +++ b/src/cryptonote_basic/cryptonote_basic.h @@ -203,6 +203,8 @@ namespace cryptonote void set_hash_valid(bool v) const { hash_valid.store(v,std::memory_order_release); } bool is_blob_size_valid() const { return blob_size_valid.load(std::memory_order_acquire); } void set_blob_size_valid(bool v) const { blob_size_valid.store(v,std::memory_order_release); } + void set_hash(const crypto::hash &h) { hash = h; set_hash_valid(true); } + void set_blob_size(size_t sz) { blob_size = sz; set_blob_size_valid(true); } BEGIN_SERIALIZE_OBJECT() if (!typename Archive::is_saving()) diff --git a/src/cryptonote_basic/cryptonote_format_utils.cpp b/src/cryptonote_basic/cryptonote_format_utils.cpp index d41bc1087a2..ea420e0469b 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.cpp +++ b/src/cryptonote_basic/cryptonote_format_utils.cpp @@ -184,6 +184,7 @@ namespace cryptonote CHECK_AND_ASSERT_MES(r, false, "Failed to parse transaction from blob"); CHECK_AND_ASSERT_MES(expand_transaction_1(tx, false), false, "Failed to expand transaction data"); tx.invalidate_hashes(); + tx.set_blob_size(tx_blob.size()); return true; } //--------------------------------------------------------------- diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index e2900916b05..120edd87bc2 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -480,6 +480,10 @@ namespace cryptonote MERROR("Failed to parse tx from txpool"); return false; } + else + { + tx.set_hash(id); + } tx_weight = meta.weight; fee = meta.fee; relayed = meta.relayed; @@ -782,6 +786,7 @@ namespace cryptonote // continue return true; } + tx.set_hash(txid); txi.tx_json = obj_to_json_str(tx); txi.blob_size = bd->size(); txi.weight = meta.weight; @@ -847,14 +852,13 @@ namespace cryptonote m_blockchain.for_all_txpool_txes([&tx_infos, key_image_infos](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd){ cryptonote::rpc::tx_in_pool txi; txi.tx_hash = txid; - transaction tx; - if (!parse_and_validate_tx_from_blob(*bd, tx)) + if (!parse_and_validate_tx_from_blob(*bd, txi.tx)) { MERROR("Failed to parse tx from txpool"); // continue return true; } - txi.tx = tx; + txi.tx.set_hash(txid); txi.blob_size = bd->size(); txi.weight = meta.weight; txi.fee = meta.fee; @@ -990,21 +994,23 @@ namespace cryptonote { struct transction_parser { - transction_parser(const cryptonote::blobdata &txblob, transaction &tx): txblob(txblob), tx(tx), parsed(false) {} + transction_parser(const cryptonote::blobdata &txblob, const crypto::hash &txid, transaction &tx): txblob(txblob), txid(txid), tx(tx), parsed(false) {} cryptonote::transaction &operator()() { if (!parsed) { if (!parse_and_validate_tx_from_blob(txblob, tx)) throw std::runtime_error("failed to parse transaction blob"); + tx.set_hash(txid); parsed = true; } return tx; } const cryptonote::blobdata &txblob; + const crypto::hash &txid; transaction &tx; bool parsed; - } lazy_tx(txblob, tx); + } lazy_tx(txblob, txid, tx); //not the best implementation at this time, sorry :( //check is ring_signature already checked ? From 9cc68a2f74f5722c0c0e9854825bcc24f17f6187 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 5 Dec 2018 20:34:35 +0000 Subject: [PATCH 0840/1404] tx_pool: add a few std::move where it can make a difference --- src/cryptonote_core/tx_pool.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index 120edd87bc2..16a86b19f0f 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -663,7 +663,8 @@ namespace cryptonote // continue return true; } - txs.push_back(tx); + tx.set_hash(txid); + txs.push_back(std::move(tx)); return true; }, true, include_unrelayed_txes); } @@ -803,7 +804,7 @@ namespace cryptonote txi.last_relayed_time = include_sensitive_data ? meta.last_relayed_time : 0; txi.do_not_relay = meta.do_not_relay; txi.double_spend_seen = meta.double_spend_seen; - tx_infos.push_back(txi); + tx_infos.push_back(std::move(txi)); return true; }, true, include_sensitive_data); @@ -885,7 +886,7 @@ namespace cryptonote } const crypto::key_image& k_image = kee.first; - key_image_infos[k_image] = tx_hashes; + key_image_infos[k_image] = std::move(tx_hashes); } return true; } From db5737413efda040a2095e12dea9256811682499 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 5 Dec 2018 14:52:43 +0000 Subject: [PATCH 0841/1404] util: use fcntl instead of flock, for compatibility in particular with NFS --- src/common/util.cpp | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/src/common/util.cpp b/src/common/util.cpp index ab84b318553..99da737c464 100644 --- a/src/common/util.cpp +++ b/src/common/util.cpp @@ -84,6 +84,29 @@ using namespace epee; #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "util" +namespace +{ + +#ifndef _WIN32 +static int flock_exnb(int fd) +{ + struct flock fl; + int ret; + + memset(&fl, 0, sizeof(fl)); + fl.l_type = F_WRLCK; + fl.l_whence = SEEK_SET; + fl.l_start = 0; + fl.l_len = 0; + ret = fcntl(fd, F_SETLK, &fl); + if (ret < 0) + MERROR("Error locking fd " << fd << ": " << errno << " (" << strerror(errno) << ")"); + return ret; +} +#endif + +} + namespace tools { std::function signal_handler::m_handler; @@ -184,7 +207,7 @@ namespace tools struct stat wstats = {}; if (fstat(fdw, std::addressof(wstats)) == 0 && rstats.st_dev == wstats.st_dev && rstats.st_ino == wstats.st_ino && - flock(fdw, (LOCK_EX | LOCK_NB)) == 0 && ftruncate(fdw, 0) == 0) + flock_exnb(fdw) == 0 && ftruncate(fdw, 0) == 0) { std::FILE* file = fdopen(fdw, "w"); if (file) return {file, std::move(name)}; @@ -237,10 +260,10 @@ namespace tools MERROR("Failed to open " << filename << ": " << std::error_code(GetLastError(), std::system_category())); } #else - m_fd = open(filename.c_str(), O_RDONLY | O_CREAT | O_CLOEXEC, 0666); + m_fd = open(filename.c_str(), O_RDWR | O_CREAT | O_CLOEXEC, 0666); if (m_fd != -1) { - if (flock(m_fd, LOCK_EX | LOCK_NB) == -1) + if (flock_exnb(m_fd) == -1) { MERROR("Failed to lock " << filename << ": " << std::strerror(errno)); close(m_fd); From 85807dfb25bad6f4606c9b81a98b1abb95da8335 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 7 Dec 2018 12:32:32 +0000 Subject: [PATCH 0842/1404] add a once_a_time_milliseconds class --- contrib/epee/include/math_helper.h | 39 +++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/contrib/epee/include/math_helper.h b/contrib/epee/include/math_helper.h index ef839f609a6..e22e8ee6e06 100644 --- a/contrib/epee/include/math_helper.h +++ b/contrib/epee/include/math_helper.h @@ -230,35 +230,56 @@ namespace math_helper } } - template - class once_a_time_seconds + template + class once_a_time { + uint64_t get_time() const + { +#ifdef _WIN32 + FILETIME fileTime; + GetSystemTimeAsFileTime(&fileTime); + unsigned __int64 present = 0; + present |= fileTime.dwHighDateTime; + present = present << 32; + present |= fileTime.dwLowDateTime; + present /= 10; // mic-sec +#else + struct timeval tv; + gettimeofday(&tv, NULL); + return tv.tv_sec * 1000000 + tv.tv_usec; +#endif + } + public: - once_a_time_seconds():m_interval(default_interval) + once_a_time():m_interval(default_interval * scale) { m_last_worked_time = 0; if(!start_immediate) - time(&m_last_worked_time); + m_last_worked_time = get_time(); } template bool do_call(functor_t functr) { - time_t current_time = 0; - time(¤t_time); + uint64_t current_time = get_time(); if(current_time - m_last_worked_time > m_interval) { bool res = functr(); - time(&m_last_worked_time); + m_last_worked_time = get_time(); return res; } return true; } private: - time_t m_last_worked_time; - time_t m_interval; + uint64_t m_last_worked_time; + uint64_t m_interval; }; + + template + class once_a_time_seconds: public once_a_time<1000000, default_interval, start_immediate> {}; + template + class once_a_time_milliseconds: public once_a_time<1000, default_interval, start_immediate> {}; } } From 1d892ec88122be46fe7592cfd1cd60e5b3adfa68 Mon Sep 17 00:00:00 2001 From: selsta Date: Fri, 7 Dec 2018 13:37:23 +0100 Subject: [PATCH 0843/1404] simplewallet: donate command validate amount --- src/simplewallet/simplewallet.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index bcf6acbd56d..7e974bc501f 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -5970,8 +5970,18 @@ bool simple_wallet::donate(const std::vector &args_) local_args.pop_back(); } // get amount and pop - amount_str = local_args.back(); - local_args.pop_back(); + uint64_t amount; + bool ok = cryptonote::parse_amount(amount, local_args.back()); + if (ok && amount != 0) + { + amount_str = local_args.back(); + local_args.pop_back(); + } + else + { + fail_msg_writer() << tr("amount is wrong: ") << local_args.back() << ", " << tr("expected number from 0 to ") << print_money(std::numeric_limits::max()); + return true; + } // push back address, amount, payment id local_args.push_back(MONERO_DONATION_ADDR); local_args.push_back(amount_str); From 5464725a29c7ea8bc917d447bf04ba481f791fe9 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 7 Dec 2018 12:32:50 +0000 Subject: [PATCH 0844/1404] protocol: change standby mode to not wait sleeping --- .../cryptonote_protocol_handler.h | 2 ++ .../cryptonote_protocol_handler.inl | 34 ++++++++++++------- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.h b/src/cryptonote_protocol/cryptonote_protocol_handler.h index d7b74c06d6e..618b635cc85 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.h +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.h @@ -131,6 +131,7 @@ namespace cryptonote bool should_download_next_span(cryptonote_connection_context& context) const; void drop_connection(cryptonote_connection_context &context, bool add_fail, bool flush_all_spans); bool kick_idle_peers(); + bool check_standby_peers(); int try_add_next_blocks(cryptonote_connection_context &context); t_core& m_core; @@ -143,6 +144,7 @@ namespace cryptonote boost::mutex m_sync_lock; block_queue m_block_queue; epee::math_helper::once_a_time_seconds<30> m_idle_peer_kicker; + epee::math_helper::once_a_time_milliseconds<100> m_standby_checker; boost::mutex m_buffer_mutex; double get_avg_block_size(); diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl index 6081ddd89c7..67baa942907 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl @@ -1210,6 +1210,7 @@ skip: bool t_cryptonote_protocol_handler::on_idle() { m_idle_peer_kicker.do_call(boost::bind(&t_cryptonote_protocol_handler::kick_idle_peers, this)); + m_standby_checker.do_call(boost::bind(&t_cryptonote_protocol_handler::check_standby_peers, this)); return m_core.on_idle(); } //------------------------------------------------------------------------------------------------------------------------ @@ -1245,6 +1246,22 @@ skip: } //------------------------------------------------------------------------------------------------------------------------ template + bool t_cryptonote_protocol_handler::check_standby_peers() + { + m_p2p->for_each_connection([&](cryptonote_connection_context& context, nodetool::peerid_type peer_id, uint32_t support_flags)->bool + { + if (context.m_state == cryptonote_connection_context::state_standby) + { + LOG_PRINT_CCONTEXT_L2("requesting callback"); + ++context.m_callback_request_count; + m_p2p->request_callback(context); + } + return true; + }); + return true; + } + //------------------------------------------------------------------------------------------------------------------------ + template int t_cryptonote_protocol_handler::handle_request_chain(int command, NOTIFY_REQUEST_CHAIN::request& arg, cryptonote_connection_context& context) { MLOG_P2P_MESSAGE("Received NOTIFY_REQUEST_CHAIN (" << arg.block_ids.size() << " blocks"); @@ -1338,14 +1355,13 @@ skip: bool start_from_current_chain = false; if (!force_next_span) { - bool first = true; - while (1) + do { size_t nblocks = m_block_queue.get_num_filled_spans(); size_t size = m_block_queue.get_data_size(); if (nblocks < BLOCK_QUEUE_NBLOCKS_THRESHOLD || size < BLOCK_QUEUE_SIZE_THRESHOLD) { - if (!first) + if (context.m_state != cryptonote_connection_context::state_standby) { LOG_DEBUG_CC(context, "Block queue is " << nblocks << " and " << size << ", resuming"); } @@ -1368,10 +1384,9 @@ skip: break; } - if (first) + if (context.m_state != cryptonote_connection_context::state_standby) { LOG_DEBUG_CC(context, "Block queue is " << nblocks << " and " << size << ", pausing"); - first = false; context.m_state = cryptonote_connection_context::state_standby; } @@ -1385,13 +1400,8 @@ skip: return true; } - for (size_t n = 0; n < 50; ++n) - { - if (m_stopping) - return true; - boost::this_thread::sleep_for(boost::chrono::milliseconds(100)); - } - } + return true; + } while(0); context.m_state = cryptonote_connection_context::state_synchronizing; } From b21a60efd98f32fe27d5aa16fe086c4bc9d403ed Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 7 Dec 2018 13:13:24 +0000 Subject: [PATCH 0845/1404] mlocker: set default log category --- contrib/epee/src/mlocker.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/contrib/epee/src/mlocker.cpp b/contrib/epee/src/mlocker.cpp index 8e24e84389b..12dff791843 100644 --- a/contrib/epee/src/mlocker.cpp +++ b/contrib/epee/src/mlocker.cpp @@ -40,6 +40,9 @@ #include +#undef MONERO_DEFAULT_LOG_CATEGORY +#define MONERO_DEFAULT_LOG_CATEGORY "mlocker" + // did an mlock operation previously fail? we only // want to log an error once and be done with it static std::atomic previously_failed{ false }; From 570dd3690e501b5c8a3ee8169456539d231b2536 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 5 Dec 2018 22:25:27 +0000 Subject: [PATCH 0846/1404] p2p: use vector instead of list for peer lists --- src/p2p/net_node.h | 6 +++--- src/p2p/net_node.inl | 10 +++++----- src/p2p/net_peerlist.h | 15 +++++++++------ src/p2p/p2p_protocol_defs.h | 20 ++++++++++---------- src/rpc/core_rpc_server.cpp | 7 ++++--- tests/unit_tests/test_peerlist.cpp | 6 +++--- 6 files changed, 34 insertions(+), 30 deletions(-) diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h index 8930418bdbd..4db0a6cb72f 100644 --- a/src/p2p/net_node.h +++ b/src/p2p/net_node.h @@ -197,12 +197,12 @@ namespace nodetool const boost::program_options::variables_map& vm ); bool idle_worker(); - bool handle_remote_peerlist(const std::list& peerlist, time_t local_time, const epee::net_utils::connection_context_base& context); + bool handle_remote_peerlist(const std::vector& peerlist, time_t local_time, const epee::net_utils::connection_context_base& context); bool get_local_node_data(basic_node_data& node_data); //bool get_local_handshake_data(handshake_data& hshd); - bool merge_peerlist_with_local(const std::list& bs); - bool fix_time_delta(std::list& local_peerlist, time_t local_time, int64_t& delta); + bool merge_peerlist_with_local(const std::vector& bs); + bool fix_time_delta(std::vector& local_peerlist, time_t local_time, int64_t& delta); bool connections_maker(); bool peer_sync_idle_maker(); diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index fbf265fc9b0..5b845fe15b9 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -1374,7 +1374,7 @@ namespace nodetool } //----------------------------------------------------------------------------------- template - bool node_server::fix_time_delta(std::list& local_peerlist, time_t local_time, int64_t& delta) + bool node_server::fix_time_delta(std::vector& local_peerlist, time_t local_time, int64_t& delta) { //fix time delta time_t now = 0; @@ -1394,10 +1394,10 @@ namespace nodetool } //----------------------------------------------------------------------------------- template - bool node_server::handle_remote_peerlist(const std::list& peerlist, time_t local_time, const epee::net_utils::connection_context_base& context) + bool node_server::handle_remote_peerlist(const std::vector& peerlist, time_t local_time, const epee::net_utils::connection_context_base& context) { int64_t delta = 0; - std::list peerlist_ = peerlist; + std::vector peerlist_ = peerlist; if(!fix_time_delta(peerlist_, local_time, delta)) return false; LOG_DEBUG_CC(context, "REMOTE PEERLIST: TIME_DELTA: " << delta << ", remote peerlist size=" << peerlist_.size()); @@ -1779,8 +1779,8 @@ namespace nodetool template bool node_server::log_peerlist() { - std::list pl_white; - std::list pl_gray; + std::vector pl_white; + std::vector pl_gray; m_peerlist.get_peerlist_full(pl_gray, pl_white); MINFO(ENDL << "Peerlist white:" << ENDL << print_peerlist_to_string(pl_white) << ENDL << "Peerlist gray:" << ENDL << print_peerlist_to_string(pl_gray) ); return true; diff --git a/src/p2p/net_peerlist.h b/src/p2p/net_peerlist.h index 1d609a37e1a..e7aad5abe48 100644 --- a/src/p2p/net_peerlist.h +++ b/src/p2p/net_peerlist.h @@ -68,9 +68,9 @@ namespace nodetool bool deinit(); size_t get_white_peers_count(){CRITICAL_REGION_LOCAL(m_peerlist_lock); return m_peers_white.size();} size_t get_gray_peers_count(){CRITICAL_REGION_LOCAL(m_peerlist_lock); return m_peers_gray.size();} - bool merge_peerlist(const std::list& outer_bs); - bool get_peerlist_head(std::list& bs_head, uint32_t depth = P2P_DEFAULT_PEERS_IN_HANDSHAKE); - bool get_peerlist_full(std::list& pl_gray, std::list& pl_white); + bool merge_peerlist(const std::vector& outer_bs); + bool get_peerlist_head(std::vector& bs_head, uint32_t depth = P2P_DEFAULT_PEERS_IN_HANDSHAKE); + bool get_peerlist_full(std::vector& pl_gray, std::vector& pl_white); bool get_white_peer_by_index(peerlist_entry& p, size_t i); bool get_gray_peer_by_index(peerlist_entry& p, size_t i); bool append_with_peer_white(const peerlist_entry& pr); @@ -265,7 +265,7 @@ namespace nodetool } //-------------------------------------------------------------------------------------------------- inline - bool peerlist_manager::merge_peerlist(const std::list& outer_bs) + bool peerlist_manager::merge_peerlist(const std::vector& outer_bs) { CRITICAL_REGION_LOCAL(m_peerlist_lock); for(const peerlist_entry& be: outer_bs) @@ -315,12 +315,13 @@ namespace nodetool } //-------------------------------------------------------------------------------------------------- inline - bool peerlist_manager::get_peerlist_head(std::list& bs_head, uint32_t depth) + bool peerlist_manager::get_peerlist_head(std::vector& bs_head, uint32_t depth) { CRITICAL_REGION_LOCAL(m_peerlist_lock); peers_indexed::index::type& by_time_index=m_peers_white.get(); uint32_t cnt = 0; + bs_head.reserve(depth); for(const peers_indexed::value_type& vl: boost::adaptors::reverse(by_time_index)) { if(!vl.last_seen) @@ -335,16 +336,18 @@ namespace nodetool } //-------------------------------------------------------------------------------------------------- inline - bool peerlist_manager::get_peerlist_full(std::list& pl_gray, std::list& pl_white) + bool peerlist_manager::get_peerlist_full(std::vector& pl_gray, std::vector& pl_white) { CRITICAL_REGION_LOCAL(m_peerlist_lock); peers_indexed::index::type& by_time_index_gr=m_peers_gray.get(); + pl_gray.resize(pl_gray.size() + by_time_index_gr.size()); for(const peers_indexed::value_type& vl: boost::adaptors::reverse(by_time_index_gr)) { pl_gray.push_back(vl); } peers_indexed::index::type& by_time_index_wt=m_peers_white.get(); + pl_white.resize(pl_white.size() + by_time_index_wt.size()); for(const peers_indexed::value_type& vl: boost::adaptors::reverse(by_time_index_wt)) { pl_white.push_back(vl); diff --git a/src/p2p/p2p_protocol_defs.h b/src/p2p/p2p_protocol_defs.h index e793e19b63d..bb9d2635c29 100644 --- a/src/p2p/p2p_protocol_defs.h +++ b/src/p2p/p2p_protocol_defs.h @@ -114,7 +114,7 @@ namespace nodetool #pragma pack(pop) inline - std::string print_peerlist_to_string(const std::list& pl) + std::string print_peerlist_to_string(const std::vector& pl) { time_t now_time = 0; time(&now_time); @@ -189,7 +189,7 @@ namespace nodetool { basic_node_data node_data; t_playload_type payload_data; - std::list local_peerlist_new; + std::vector local_peerlist_new; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(node_data) @@ -198,7 +198,7 @@ namespace nodetool { // saving: save both, so old and new peers can understand it KV_SERIALIZE(local_peerlist_new) - std::list> local_peerlist; + std::vector> local_peerlist; for (const auto &p: this_ref.local_peerlist_new) { if (p.adr.get_type_id() == epee::net_utils::ipv4_network_address::ID) @@ -217,7 +217,7 @@ namespace nodetool // loading: load old list only if there is no new one if (!epee::serialization::selector::serialize(this_ref.local_peerlist_new, stg, hparent_section, "local_peerlist_new")) { - std::list> local_peerlist; + std::vector> local_peerlist; epee::serialization::selector::serialize_stl_container_pod_val_as_blob(local_peerlist, stg, hparent_section, "local_peerlist"); for (const auto &p: local_peerlist) ((response&)this_ref).local_peerlist_new.push_back(peerlist_entry({epee::net_utils::ipv4_network_address(p.adr.ip, p.adr.port), p.id, p.last_seen})); @@ -248,7 +248,7 @@ namespace nodetool { uint64_t local_time; t_playload_type payload_data; - std::list local_peerlist_new; + std::vector local_peerlist_new; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(local_time) @@ -257,7 +257,7 @@ namespace nodetool { // saving: save both, so old and new peers can understand it KV_SERIALIZE(local_peerlist_new) - std::list> local_peerlist; + std::vector> local_peerlist; for (const auto &p: this_ref.local_peerlist_new) { if (p.adr.get_type_id() == epee::net_utils::ipv4_network_address::ID) @@ -276,7 +276,7 @@ namespace nodetool // loading: load old list only if there is no new one if (!epee::serialization::selector::serialize(this_ref.local_peerlist_new, stg, hparent_section, "local_peerlist_new")) { - std::list> local_peerlist; + std::vector> local_peerlist; epee::serialization::selector::serialize_stl_container_pod_val_as_blob(local_peerlist, stg, hparent_section, "local_peerlist"); for (const auto &p: local_peerlist) ((response&)this_ref).local_peerlist_new.push_back(peerlist_entry({epee::net_utils::ipv4_network_address(p.adr.ip, p.adr.port), p.id, p.last_seen})); @@ -389,9 +389,9 @@ namespace nodetool struct response { - std::list local_peerlist_white; - std::list local_peerlist_gray; - std::list connections_list; + std::vector local_peerlist_white; + std::vector local_peerlist_gray; + std::vector connections_list; peerid_type my_id; uint64_t local_time; BEGIN_KV_SERIALIZE_MAP() diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index cefba39f753..9c0f84450b8 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -857,11 +857,11 @@ namespace cryptonote bool core_rpc_server::on_get_peer_list(const COMMAND_RPC_GET_PEER_LIST::request& req, COMMAND_RPC_GET_PEER_LIST::response& res) { PERF_TIMER(on_get_peer_list); - std::list white_list; - std::list gray_list; + std::vector white_list; + std::vector gray_list; m_p2p.get_peerlist_manager().get_peerlist_full(gray_list, white_list); - + res.white_list.reserve(white_list.size()); for (auto & entry : white_list) { if (entry.adr.get_type_id() == epee::net_utils::ipv4_network_address::ID) @@ -871,6 +871,7 @@ namespace cryptonote res.white_list.emplace_back(entry.id, entry.adr.str(), entry.last_seen); } + res.gray_list.reserve(gray_list.size()); for (auto & entry : gray_list) { if (entry.adr.get_type_id() == epee::net_utils::ipv4_network_address::ID) diff --git a/tests/unit_tests/test_peerlist.cpp b/tests/unit_tests/test_peerlist.cpp index c6572c6c2cb..f638c6251c1 100644 --- a/tests/unit_tests/test_peerlist.cpp +++ b/tests/unit_tests/test_peerlist.cpp @@ -42,7 +42,7 @@ TEST(peer_list, peer_list_general) #define ADD_GRAY_NODE(addr_, id_, last_seen_) { nodetool::peerlist_entry ple; ple.last_seen=last_seen_;ple.adr = addr_; ple.id = id_;plm.append_with_peer_gray(ple);} #define ADD_WHITE_NODE(addr_, id_, last_seen_) { nodetool::peerlist_entry ple;ple.last_seen=last_seen_; ple.adr = addr_; ple.id = id_;plm.append_with_peer_white(ple);} -#define PRINT_HEAD(step) {std::list bs_head; bool r = plm.get_peerlist_head(bs_head, 100);std::cout << "step " << step << ": " << bs_head.size() << std::endl;} +#define PRINT_HEAD(step) {std::vector bs_head; bool r = plm.get_peerlist_head(bs_head, 100);std::cout << "step " << step << ": " << bs_head.size() << std::endl;} ADD_GRAY_NODE(MAKE_IPV4_ADDRESS(123,43,12,1, 8080), 121241, 34345); ADD_GRAY_NODE(MAKE_IPV4_ADDRESS(123,43,12,2, 8080), 121241, 34345); @@ -58,7 +58,7 @@ TEST(peer_list, peer_list_general) size_t gray_list_size = plm.get_gray_peers_count(); ASSERT_EQ(gray_list_size, 1); - std::list bs_head; + std::vector bs_head; bool r = plm.get_peerlist_head(bs_head, 100); std::cout << bs_head.size() << std::endl; ASSERT_TRUE(r); @@ -78,7 +78,7 @@ TEST(peer_list, merge_peer_lists) //ADD_NODE_TO_PL("\2", \3, 0x\1, (1353346618 -(\4*60*60*24+\5*60*60+\6*60+\7 )));\n nodetool::peerlist_manager plm; plm.init(false); - std::list outer_bs; + std::vector outer_bs; #define ADD_NODE_TO_PL(ip_, port_, id_, timestamp_) { nodetool::peerlist_entry ple; epee::string_tools::get_ip_int32_from_string(ple.adr.ip, ip_); ple.last_seen = timestamp_; ple.adr.port = port_; ple.id = id_;outer_bs.push_back(ple);} From 68f045de8ce886ce638289e34db4a2649e2ed23c Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 6 Dec 2018 00:38:24 +0000 Subject: [PATCH 0847/1404] easylogging++: check allowed categories before logging --- contrib/epee/include/misc_log_ex.h | 23 +++++++++++++++-------- external/easylogging++/easylogging++.cc | 19 +++++++++++++++++++ external/easylogging++/easylogging++.h | 2 ++ 3 files changed, 36 insertions(+), 8 deletions(-) diff --git a/contrib/epee/include/misc_log_ex.h b/contrib/epee/include/misc_log_ex.h index 9100a8db3b2..1ff9da3a742 100644 --- a/contrib/epee/include/misc_log_ex.h +++ b/contrib/epee/include/misc_log_ex.h @@ -38,14 +38,21 @@ #define MAX_LOG_FILE_SIZE 104850000 // 100 MB - 7600 bytes #define MAX_LOG_FILES 50 -#define MCFATAL(cat,x) CLOG(FATAL,cat) << x -#define MCERROR(cat,x) CLOG(ERROR,cat) << x -#define MCWARNING(cat,x) CLOG(WARNING,cat) << x -#define MCINFO(cat,x) CLOG(INFO,cat) << x -#define MCDEBUG(cat,x) CLOG(DEBUG,cat) << x -#define MCTRACE(cat,x) CLOG(TRACE,cat) << x -#define MCLOG(level,cat,x) ELPP_WRITE_LOG(el::base::Writer, level, el::base::DispatchAction::NormalLog, cat) << x -#define MCLOG_FILE(level,cat,x) ELPP_WRITE_LOG(el::base::Writer, level, el::base::DispatchAction::FileOnlyLog, cat) << x +#define MCLOG_TYPE(level, cat, type, x) do { \ + if (ELPP->vRegistry()->allowed(level, cat)) { \ + el::base::Writer(level, __FILE__, __LINE__, ELPP_FUNC, type).construct(cat) << x; \ + } \ + } while (0) + +#define MCLOG(level, cat, x) MCLOG_TYPE(level, cat, el::base::DispatchAction::NormalLog, x) +#define MCLOG_FILE(level, cat, x) MCLOG_TYPE(level, cat, el::base::DispatchAction::FileOnlyLog, x) + +#define MCFATAL(cat,x) MCLOG(el::Level::Fatal,cat, x) +#define MCERROR(cat,x) MCLOG(el::Level::Error,cat, x) +#define MCWARNING(cat,x) MCLOG(el::Level::Warning,cat, x) +#define MCINFO(cat,x) MCLOG(el::Level::Info,cat, x) +#define MCDEBUG(cat,x) MCLOG(el::Level::Debug,cat, x) +#define MCTRACE(cat,x) MCLOG(el::Level::Trace,cat, x) #define MCLOG_COLOR(level,cat,color,x) MCLOG(level,cat,"\033[1;" color "m" << x << "\033[0m") #define MCLOG_RED(level,cat,x) MCLOG_COLOR(level,cat,"31",x) diff --git a/external/easylogging++/easylogging++.cc b/external/easylogging++/easylogging++.cc index d57f3f3a0b1..aa8c80a8af0 100644 --- a/external/easylogging++/easylogging++.cc +++ b/external/easylogging++/easylogging++.cc @@ -2699,6 +2699,12 @@ Writer& Writer::construct(int count, const char* loggerIds, ...) { return *this; } +Writer& Writer::construct(const char *loggerId) { + initializeLogger(ELPP->registeredLoggers()->get(loggerId, ELPP->hasFlag(LoggingFlag::CreateLoggerAutomatically))); + m_messageBuilder.initialize(m_logger); + return *this; +} + void Writer::initializeLogger(const std::string& loggerId, bool lookup, bool needLock) { if (lookup) { m_logger = ELPP->registeredLoggers()->get(loggerId, ELPP->hasFlag(LoggingFlag::CreateLoggerAutomatically)); @@ -2727,6 +2733,19 @@ void Writer::initializeLogger(const std::string& loggerId, bool lookup, bool nee } } +void Writer::initializeLogger(Logger *logger, bool needLock) { + m_logger = logger; + if (m_logger == nullptr) { + m_proceed = false; + } else { + if (needLock) { + m_logger->acquireLock(); // This should not be unlocked by checking m_proceed because + // m_proceed can be changed by lines below + } + m_proceed = true; + } +} + void Writer::processDispatch() { #if ELPP_LOGGING_ENABLED if (ELPP->hasFlag(LoggingFlag::MultiLoggerSupport)) { diff --git a/external/easylogging++/easylogging++.h b/external/easylogging++/easylogging++.h index 046252a5b3e..a642d2748a3 100644 --- a/external/easylogging++/easylogging++.h +++ b/external/easylogging++/easylogging++.h @@ -3290,6 +3290,7 @@ class Writer : base::NoCopy { Writer& construct(Logger* logger, bool needLock = true); Writer& construct(int count, const char* loggerIds, ...); + Writer& construct(const char *loggerId); protected: LogMessage* m_msg; Level m_level; @@ -3305,6 +3306,7 @@ class Writer : base::NoCopy { friend class el::Helpers; void initializeLogger(const std::string& loggerId, bool lookup = true, bool needLock = true); + void initializeLogger(Logger *logger, bool needLock = true); void processDispatch(); void triggerDispatch(void); }; From dbbb3ce9d8075eae564b6c62db81e063958ea065 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 29 Nov 2018 22:01:48 +0000 Subject: [PATCH 0848/1404] cryptonote: don't serialize for blob size if already known --- .../cryptonote_format_utils.cpp | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/cryptonote_basic/cryptonote_format_utils.cpp b/src/cryptonote_basic/cryptonote_format_utils.cpp index 55d7d23f86b..f11031ed369 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.cpp +++ b/src/cryptonote_basic/cryptonote_format_utils.cpp @@ -379,11 +379,19 @@ namespace cryptonote //--------------------------------------------------------------- uint64_t get_transaction_weight(const transaction &tx) { - std::ostringstream s; - binary_archive a(s); - ::serialization::serialize(a, const_cast(tx)); - const cryptonote::blobdata blob = s.str(); - return get_transaction_weight(tx, blob.size()); + size_t blob_size; + if (tx.is_blob_size_valid()) + { + blob_size = tx.blob_size; + } + else + { + std::ostringstream s; + binary_archive a(s); + ::serialization::serialize(a, const_cast(tx)); + blob_size = s.str().size(); + } + return get_transaction_weight(tx, blob_size); } //--------------------------------------------------------------- bool get_tx_fee(const transaction& tx, uint64_t & fee) From c1581a5bb8398936d0e388fc18a4c08d06883ca6 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 31 Oct 2018 13:26:54 +0000 Subject: [PATCH 0849/1404] perf_timer: only log to file --- src/common/perf_timer.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/common/perf_timer.cpp b/src/common/perf_timer.cpp index d9f1f65c1ae..d287949bafa 100644 --- a/src/common/perf_timer.cpp +++ b/src/common/perf_timer.cpp @@ -33,6 +33,9 @@ #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "perf" +#define PERF_LOG(level, cat, x) \ + ELPP_WRITE_LOG(el::base::Writer, level, el::base::DispatchAction::FileOnlyLog, cat) << x + namespace tools { uint64_t get_tick_count() @@ -108,7 +111,7 @@ LoggingPerformanceTimer::LoggingPerformanceTimer(const std::string &s, const std { if (!performance_timers) { - MCLOG(level, cat.c_str(), "PERF ----------"); + PERF_LOG(level, cat.c_str(), "PERF ----------"); performance_timers = new std::vector(); performance_timers->reserve(16); // how deep before realloc } @@ -118,7 +121,7 @@ LoggingPerformanceTimer::LoggingPerformanceTimer(const std::string &s, const std if (!pt->started && !pt->paused) { size_t size = 0; for (const auto *tmp: *performance_timers) if (!tmp->paused) ++size; - MCLOG(pt->level, cat.c_str(), "PERF " << std::string((size-1) * 2, ' ') << " " << pt->name); + PERF_LOG(pt->level, cat.c_str(), "PERF " << std::string((size-1) * 2, ' ') << " " << pt->name); pt->started = true; } } @@ -138,7 +141,7 @@ LoggingPerformanceTimer::~LoggingPerformanceTimer() char s[12]; snprintf(s, sizeof(s), "%8llu ", (unsigned long long)(ticks_to_ns(ticks) / (1000000000 / unit))); size_t size = 0; for (const auto *tmp: *performance_timers) if (!tmp->paused || tmp==this) ++size; - MCLOG(level, cat.c_str(), "PERF " << s << std::string(size * 2, ' ') << " " << name); + PERF_LOG(level, cat.c_str(), "PERF " << s << std::string(size * 2, ' ') << " " << name); if (performance_timers->empty()) { delete performance_timers; From 6a507dab6f935b0d8c417299ba4f5be4882582bb Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 11 Mar 2018 11:17:29 +0000 Subject: [PATCH 0850/1404] perf_timer: add a way to get and reset the current time --- src/common/perf_timer.cpp | 16 ++++++++++++++++ src/common/perf_timer.h | 4 ++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/common/perf_timer.cpp b/src/common/perf_timer.cpp index d287949bafa..fc51dc7f72d 100644 --- a/src/common/perf_timer.cpp +++ b/src/common/perf_timer.cpp @@ -165,4 +165,20 @@ void PerformanceTimer::resume() paused = false; } +void PerformanceTimer::reset() +{ + if (paused) + ticks = 0; + else + ticks = get_tick_count(); +} + +uint64_t PerformanceTimer::value() const +{ + uint64_t v = ticks; + if (!paused) + v = get_tick_count() - v; + return ticks_to_ns(v); +} + } diff --git a/src/common/perf_timer.h b/src/common/perf_timer.h index 584434a0da0..c33aaae12b8 100644 --- a/src/common/perf_timer.h +++ b/src/common/perf_timer.h @@ -51,8 +51,8 @@ class PerformanceTimer ~PerformanceTimer(); void pause(); void resume(); - - uint64_t value() const { return ticks; } + void reset(); + uint64_t value() const; protected: uint64_t ticks; From 93c59b29a8638fcdeb15c1a6dd6efe954586a680 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 6 Dec 2018 01:07:34 +0000 Subject: [PATCH 0851/1404] perf_timer: check allowed categories before logging --- src/common/perf_timer.cpp | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/src/common/perf_timer.cpp b/src/common/perf_timer.cpp index fc51dc7f72d..3e135783371 100644 --- a/src/common/perf_timer.cpp +++ b/src/common/perf_timer.cpp @@ -33,8 +33,12 @@ #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "perf" +#define PERF_LOG_ALWAYS(level, cat, x) \ + el::base::Writer(level, __FILE__, __LINE__, ELPP_FUNC, el::base::DispatchAction::FileOnlyLog).construct(cat) << x #define PERF_LOG(level, cat, x) \ - ELPP_WRITE_LOG(el::base::Writer, level, el::base::DispatchAction::FileOnlyLog, cat) << x + do { \ + if (ELPP->vRegistry()->allowed(level, cat)) PERF_LOG_ALWAYS(level, cat, x); \ + } while(0) namespace tools { @@ -109,9 +113,11 @@ PerformanceTimer::PerformanceTimer(bool paused): started(true), paused(paused) LoggingPerformanceTimer::LoggingPerformanceTimer(const std::string &s, const std::string &cat, uint64_t unit, el::Level l): PerformanceTimer(), name(s), cat(cat), unit(unit), level(l) { + const bool log = ELPP->vRegistry()->allowed(level, cat.c_str()); if (!performance_timers) { - PERF_LOG(level, cat.c_str(), "PERF ----------"); + if (log) + PERF_LOG_ALWAYS(level, cat.c_str(), "PERF ----------"); performance_timers = new std::vector(); performance_timers->reserve(16); // how deep before realloc } @@ -120,8 +126,11 @@ LoggingPerformanceTimer::LoggingPerformanceTimer(const std::string &s, const std LoggingPerformanceTimer *pt = performance_timers->back(); if (!pt->started && !pt->paused) { - size_t size = 0; for (const auto *tmp: *performance_timers) if (!tmp->paused) ++size; - PERF_LOG(pt->level, cat.c_str(), "PERF " << std::string((size-1) * 2, ' ') << " " << pt->name); + if (log) + { + size_t size = 0; for (const auto *tmp: *performance_timers) if (!tmp->paused) ++size; + PERF_LOG_ALWAYS(pt->level, cat.c_str(), "PERF " << std::string((size-1) * 2, ' ') << " " << pt->name); + } pt->started = true; } } @@ -138,10 +147,14 @@ LoggingPerformanceTimer::~LoggingPerformanceTimer() { pause(); performance_timers->pop_back(); - char s[12]; - snprintf(s, sizeof(s), "%8llu ", (unsigned long long)(ticks_to_ns(ticks) / (1000000000 / unit))); - size_t size = 0; for (const auto *tmp: *performance_timers) if (!tmp->paused || tmp==this) ++size; - PERF_LOG(level, cat.c_str(), "PERF " << s << std::string(size * 2, ' ') << " " << name); + const bool log = ELPP->vRegistry()->allowed(level, cat.c_str()); + if (log) + { + char s[12]; + snprintf(s, sizeof(s), "%8llu ", (unsigned long long)(ticks_to_ns(ticks) / (1000000000 / unit))); + size_t size = 0; for (const auto *tmp: *performance_timers) if (!tmp->paused || tmp==this) ++size; + PERF_LOG_ALWAYS(level, cat.c_str(), "PERF " << s << std::string(size * 2, ' ') << " " << name); + } if (performance_timers->empty()) { delete performance_timers; From 25e5a8539c7f78e4b63bfb155bd3c6508bfbcca3 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 7 Dec 2018 16:34:56 +0000 Subject: [PATCH 0852/1404] singleton: fix missing *this return value in operator= while there, disable both operator= and copy ctor, since they are not supposed to be around for a singleton --- src/mnemonics/singleton.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mnemonics/singleton.h b/src/mnemonics/singleton.h index a15c2b9ae02..d317a2d8d6e 100644 --- a/src/mnemonics/singleton.h +++ b/src/mnemonics/singleton.h @@ -50,8 +50,8 @@ namespace Language class Singleton { Singleton() {} - Singleton(Singleton &s) {} - Singleton& operator=(const Singleton&) {} + Singleton(Singleton &s) = delete; + Singleton& operator=(const Singleton&) = delete; public: static T* instance() { From 3a3858dc908934be82ffe4f0d693ebb518397a28 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 26 Nov 2018 00:19:10 +0000 Subject: [PATCH 0853/1404] epee: avoid string allocation when parsing a pod from string --- contrib/epee/include/string_tools.h | 48 ++++++++--------------------- src/checkpoints/checkpoints.cpp | 4 +-- 2 files changed, 14 insertions(+), 38 deletions(-) diff --git a/contrib/epee/include/string_tools.h b/contrib/epee/include/string_tools.h index 6a063cc36d6..0d8eca72790 100644 --- a/contrib/epee/include/string_tools.h +++ b/contrib/epee/include/string_tools.h @@ -117,16 +117,12 @@ namespace string_tools return to_hex::string(to_byte_span(to_span(src))); } //---------------------------------------------------------------------------- - template - bool parse_hexstr_to_binbuff(const std::basic_string& s, std::basic_string& res) + inline bool parse_hexstr_to_binbuff(const epee::span s, epee::span& res) { - res.clear(); - if (s.size() & 1) - return false; - try - { - res.resize(s.size() / 2); - unsigned char *dst = (unsigned char *)res.data(); + if (s.size() != res.size() * 2) + return false; + + unsigned char *dst = (unsigned char *)&res[0]; const unsigned char *src = (const unsigned char *)s.data(); for(size_t i = 0; i < s.size(); i += 2) { @@ -140,28 +136,15 @@ namespace string_tools } return true; - } - catch(...) - { - return false; - } } //---------------------------------------------------------------------------- - template - bool parse_tpod_from_hex_string(const std::string& str_hash, t_pod_type& t_pod) + inline bool parse_hexstr_to_binbuff(const std::string& s, std::string& res) { - static_assert(std::is_pod::value, "expected pod type"); - std::string buf; - bool res = epee::string_tools::parse_hexstr_to_binbuff(str_hash, buf); - if (!res || buf.size() != sizeof(t_pod_type)) - { + if (s.size() & 1) return false; - } - else - { - buf.copy(reinterpret_cast(&t_pod), sizeof(t_pod_type)); - return true; - } + res.resize(s.size() / 2); + epee::span rspan((char*)&res[0], res.size()); + return parse_hexstr_to_binbuff(epee::to_span(s), rspan); } //---------------------------------------------------------------------------- PUSH_WARNINGS @@ -360,17 +343,10 @@ POP_WARNINGS bool hex_to_pod(const std::string& hex_str, t_pod_type& s) { static_assert(std::is_pod::value, "expected pod type"); - std::string hex_str_tr = trim(hex_str); if(sizeof(s)*2 != hex_str.size()) return false; - std::string bin_buff; - if(!parse_hexstr_to_binbuff(hex_str_tr, bin_buff)) - return false; - if(bin_buff.size()!=sizeof(s)) - return false; - - s = *(t_pod_type*)bin_buff.data(); - return true; + epee::span rspan((char*)&s, sizeof(s)); + return parse_hexstr_to_binbuff(epee::to_span(hex_str), rspan); } //---------------------------------------------------------------------------- template diff --git a/src/checkpoints/checkpoints.cpp b/src/checkpoints/checkpoints.cpp index 1807d44d91b..96f575b2df6 100644 --- a/src/checkpoints/checkpoints.cpp +++ b/src/checkpoints/checkpoints.cpp @@ -74,7 +74,7 @@ namespace cryptonote bool checkpoints::add_checkpoint(uint64_t height, const std::string& hash_str) { crypto::hash h = crypto::null_hash; - bool r = epee::string_tools::parse_tpod_from_hex_string(hash_str, h); + bool r = epee::string_tools::hex_to_pod(hash_str, h); CHECK_AND_ASSERT_MES(r, false, "Failed to parse checkpoint hash string into binary representation!"); // return false if adding at a height we already have AND the hash is different @@ -292,7 +292,7 @@ namespace cryptonote // parse the second part as crypto::hash, // if this fails move on to the next record std::string hashStr = record.substr(pos + 1); - if (!epee::string_tools::parse_tpod_from_hex_string(hashStr, hash)) + if (!epee::string_tools::hex_to_pod(hashStr, hash)) { continue; } From a13eb0a1a45b0ae255d97c2539c19e8bd701a1b8 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 26 Nov 2018 15:49:17 +0000 Subject: [PATCH 0854/1404] epee: speed up string matching a bit --- contrib/epee/include/storages/parserse_base_utils.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/contrib/epee/include/storages/parserse_base_utils.h b/contrib/epee/include/storages/parserse_base_utils.h index 31495ae137b..d73fbde3a8b 100644 --- a/contrib/epee/include/storages/parserse_base_utils.h +++ b/contrib/epee/include/storages/parserse_base_utils.h @@ -91,11 +91,15 @@ namespace misc_utils */ inline void match_string2(std::string::const_iterator& star_end_string, std::string::const_iterator buf_end, std::string& val) { - val.clear(); - val.reserve(std::distance(star_end_string, buf_end)); bool escape_mode = false; std::string::const_iterator it = star_end_string; ++it; + std::string::const_iterator fi = it; + while (fi != buf_end && *fi != '\\' && *fi != '\"') + ++fi; + val.assign(it, fi); + val.reserve(std::distance(star_end_string, buf_end)); + it = fi; for(;it != buf_end;it++) { if(escape_mode/*prev_ch == '\\'*/) From 2d7b0236eb85a767261611fb558c841a9f790293 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 7 Dec 2018 18:23:01 +0000 Subject: [PATCH 0855/1404] wallet2: clear all payments on soft rescan_bc They'll get duplicated otherwise --- src/wallet/wallet2.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index bbb801716e6..ee54faaa4f6 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -5139,6 +5139,10 @@ void wallet2::rescan_blockchain(bool hard, bool refresh) m_transfers.clear(); m_key_images.clear(); m_pub_keys.clear(); + m_unconfirmed_txs.clear(); + m_payments.clear(); + m_confirmed_txs.clear(); + m_unconfirmed_payments.clear(); m_scanned_pool_txs[0].clear(); m_scanned_pool_txs[1].clear(); From 0e2f5cb5fc84fb5d4afcfe020c9d6ae5fdaf3290 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 8 Dec 2018 10:52:25 +0000 Subject: [PATCH 0856/1404] perf_timer: make all logs Info level and make them not default at log level 1 --- contrib/epee/src/mlog.cpp | 2 +- src/common/perf_timer.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/contrib/epee/src/mlog.cpp b/contrib/epee/src/mlog.cpp index 00d8483881f..9b6b832d1a2 100644 --- a/contrib/epee/src/mlog.cpp +++ b/contrib/epee/src/mlog.cpp @@ -103,7 +103,7 @@ static const char *get_default_categories(int level) categories = "*:WARNING,net:FATAL,net.http:FATAL,net.p2p:FATAL,net.cn:FATAL,global:INFO,verify:FATAL,stacktrace:INFO,logging:INFO,msgwriter:INFO"; break; case 1: - categories = "*:INFO,global:INFO,stacktrace:INFO,logging:INFO,msgwriter:INFO"; + categories = "*:INFO,global:INFO,stacktrace:INFO,logging:INFO,msgwriter:INFO,perf.*:DEBUG"; break; case 2: categories = "*:DEBUG"; diff --git a/src/common/perf_timer.h b/src/common/perf_timer.h index 584434a0da0..267f94161e5 100644 --- a/src/common/perf_timer.h +++ b/src/common/perf_timer.h @@ -63,7 +63,7 @@ class PerformanceTimer class LoggingPerformanceTimer: public PerformanceTimer { public: - LoggingPerformanceTimer(const std::string &s, const std::string &cat, uint64_t unit, el::Level l = el::Level::Debug); + LoggingPerformanceTimer(const std::string &s, const std::string &cat, uint64_t unit, el::Level l = el::Level::Info); ~LoggingPerformanceTimer(); private: From b56b5b52eb47ab1ad67de1cd09f8af1eff1c882d Mon Sep 17 00:00:00 2001 From: Jethro Grassie Date: Sat, 8 Dec 2018 20:52:54 -0500 Subject: [PATCH 0857/1404] ignore child process when exec --- src/common/spawn.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/common/spawn.cpp b/src/common/spawn.cpp index 0a2ce83872b..b2d03f62f97 100644 --- a/src/common/spawn.cpp +++ b/src/common/spawn.cpp @@ -35,6 +35,7 @@ #include #else #include +#include #endif #include "misc_log_ex.h" @@ -114,7 +115,10 @@ int spawn(const char *filename, const std::vector& args, bool wait) if (pid > 0) { if (!wait) + { + signal(SIGCHLD, SIG_IGN); return 0; + } while (1) { From 08bcbd65a574db3456eec948d27a7b206f120ebd Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 11 Dec 2018 20:30:42 +0000 Subject: [PATCH 0858/1404] update expiry date for both signing and encryption keys --- utils/gpg_keys/moneromooo.asc | 50 +++++++++++++++++------------------ 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/utils/gpg_keys/moneromooo.asc b/utils/gpg_keys/moneromooo.asc index cce18093767..4275b6c0eca 100644 --- a/utils/gpg_keys/moneromooo.asc +++ b/utils/gpg_keys/moneromooo.asc @@ -13,18 +13,18 @@ p7gDvxXOGxzq0sqfPTWTBdCj1OPfunHbbeH8ypwBlNpwVG40fJdya+Dqjwu25qX6 Xh5vxLzeJTBmlawa97MCliPvzzJgW9qHRVCa9lLloGVYLiUOS0N+dZ/r/QARAQAB tD5tb25lcm9tb29vLW1vbmVybyA8bW9uZXJvbW9vby1tb25lcm9AdXNlcnMubm9y ZXBseS5naXRodWIuY29tPokCPwQTAQIAKQIbAwcLCQgHAwIBBhUIAgkKCwQWAgMB -Ah4BAheABQJbof8TBQkLMcqRAAoJEGhvB0VNbO/DOrcP/1eG3gkIgq+lXKlv/lta -ZFl8jS5iQUwxTHv8s+O7pbL2mwKt5oM8QmXVMFLlaanENmH0y/DhWRYIYuKTifDN -tXOxENCZhxgjlVxAtnMdD2J8MJANzV2MYGLbGQeFAuZHIL2LMfvUENLYx/jJpe7f -93kXZoQio7rIolnlbM1QoJLxi/7HlOt/VsopJlV2wAmOmlpyWDnOZtUtZgiCGV6a -apFzTs2m3n2GP7+8PG/W01/jVyFqixGW8cWVZORBMhjro2JqrRvw7U+Ypk2o7Em4 -SAnAJyzqpTDh9zf0QWkQXN83YThB3dk1M1FXOyKtZ8G+YVNfs5Ldr7tHNbqKi2v8 -lPaPIDQ7UmxZhy9vraKss0/3AEXaiE2eSvLc4eCcLiS4yVQ2KJpK5TUvz3cP1D5C -USsjxUZvolUELBtNaRVAsxX6OAyA8FjwJ8AKCqyR0NHvrbYhj54N1Js8PWtb+qqk -5sBLKOwEPaWM1uG6SJabrY1xu2VNLOGdJA9bRfyyzfpXksD0lFgwLKki6ReqqoMS -QiVdymhcp55J4tFwsDkzJX7296d1x3r7GAIQMLNc7YizukWfNtvUkSUsAwp+RKUx -TTwkwL1ztzSRpt3hGMJ9SfULTaQD7YAYfz4kitMBNpm90CLFa4CeINu98gE9Ogmc -R+CGQOHLUC3rXubgRCiQg5wfuQINBFQym34BEADHtTHduZFdu76RAzqTjT94F92L +Ah4BAheABQJcEB4SBQkLn+mRAAoJEGhvB0VNbO/DzSIQALXk6ST5Uoxh5+NLBJgI +GR2NOCFwsU+97VOkZWZnzpk+JdUrq/I9JmWWTegGKu3it5YvM8xY1zD1l+RXlnmY +Bg7oT6N63lL8hBhxGm9Hk2A1VJlEjtgtvjbg5yLSdvjxJL1vrmchWGwFWoHT9uIh +oefemb3TQ1U+ADVsW42jXca650/rqvsUMpmQbDfHxUBIfpbxgTWz8sHuMfhFB3R0 +nzSuZ1E1Pbg5jWYxd6sXpb1VFZ7TaQNVGB+4wMe8AH8/0qziLVTXvSkoer6qxU+r +e9M9/Rgue1FJSzZogUekIqyNLsvAeATSCYzi8NoywkUbAq6AqazJtdcOD+7M2/Vl +pdQEnIKSO/9xrziUQifRsPx4LOjf2d2Hvor/rBJYwoI1cFgR5pmH30Mi7OY0mMmm +2TMUPPz7YiVY+RPPpY3Mg5oPRe6sHVAdFYBmc0PTR4v92iYgfQXzoCd5HOJI9QB3 +SwrKz7cNLQwob2za1AXZ3sUp+yok6QMt8Tk6xIbMAjeR4jVeP8iQHhiVS1zx5z/g +lCAfb0hJnykoANgIZvJR/L+tuZjVStqnXjHgreY3IjlWTaNjZ/X5uMvd6zivqI9a +aEmqw/ZDCXRMAtjUCpUvpNlqAgqqbQsx6MFDnMbd4BuKQONeAFLm/Rp31nIvt1vb ++Iu3vkXBbbjb5yMu/dcpgSRUuQINBFQym34BEADHtTHduZFdu76RAzqTjT94F92L xSSopLSk7/sdLWTc2ERmjDId7dKmqrL1Kh2kqAtHY3Rq8Y839LGmbJCzI1kJyOHF o9jkEI93sqXcztLjizPVukqClOZNt3NV/nvefH6JSdqWcnC4V1mQr2Ztl0j+51i+ NYVwGjlsOMlBER+LW/s7egRqAQonrcEB5vsSAzd8mOlNKjRAnDCV+C21GDKxzb80 @@ -35,17 +35,17 @@ xRFfhoiiPyjjPRmJ+/iG3KXLzEiMfbyTFzGkX3Z9BJTxemUx8JOSVQXa++t4w39J UwzwBKNItDhtQqJpCaF43fJ4ykLMJi5gRpgqtb+T3CF0abXNII1IfS8a0fSpd48d 6hzoCVqpvWsI1fOY5Ui0BIgubNhkr4OJDCWBT5zhxjCJ3QiUSKyyqjfw1Fpuf/0Y CSA9Q9FSCq9qTppJs5ITHVjhWw2zxrJEG+P2+dvryBhV9l4T2xx1oHqlKX8zzLBG -kS8NmnxoRFQs5rZYvQARAQABiQIlBBgBAgAPAhsMBQJX3tl5BQkHbqT4AAoJEGhv -B0VNbO/DrN4QAJVTg2l91LSxb2dTikXbkRCPev1Eiqjd3d4TZIyE7yeYreHUyfcz -sytEsMUpd5nqqL/QMOwgC8Dm+77Yp480atI/c0wQh30EfJq1YP4q/gwv8EtriFo6 -ZaqYNzajmB8otz6yyb5RD9S6ocZX5b22nnNM/ihiTQJiqJKC2XHPQH3/grniU9if -WXUlY7Fo+8lRn/aRPMjYT2elsugru8GoplDMyfPRymnMJmTwNPYkg0Dnm2JUXYhH -VLO6Ebh7fUChsiGNmSp+siv7fcZIf20+LvoymgyT+49LrrQMvRleOqaJc1LBKaK6 -1yU+jLk1f9bFCMORQoTxvIVaSnjMWAeKDxi8nwuVfUJpQYXmbOOqMZgoFi6t+U7/ -T6Cz1CGKv3JgEveWyHeOvsjopej3a4Hmk7QGM/xJrd83roUjypjx5lTeMDvcPlPs -IQOy33qWguR1xoEnwAp9ov/meS7HtUOoC0m9ROZqWT6ArN+1ONplFP4GTsuW91Qz -pacQMl09F0KF1MacAdpauCOKj+wy9XPUW8v3kWZufUSgNtOLHS+kVumkqdJuJ0aB -7Ezc1yYY6DwRFOdlUpTJkRMozyPRvS15Lz/vPEhcbxMHRAg611CS2CqTEIbk2chJ -QBlj77a45lDTXGQHFVYXlbXx/HWb5zOGpNji0QhY1hOABRo78DT5yda+ -=ksQj +kS8NmnxoRFQs5rZYvQARAQABiQIlBBgBAgAPAhsMBQJcEB4eBQkLn+meAAoJEGhv +B0VNbO/DjzYP/2A6HtpvPkD+s/0+Ghmp5Rw7HIvZz0RnCsvM3qVQqVn3JTqHXyhD +5GmZrCxhliW1nRBITaKSRHXSGeE/O1BtK6eW/Z1P6bWJVd+R9vhaZlLU2sswiFzu +q3s7bL/Fo9VRc0SX6j6JDh1FZ3JcebUfY59sixHLqZuAk9m97z6H1NjS+f4pB5Tp +n1nnRgdvYk4tjlk4mmluIAwjq8Ll6gw6ntwjX9Gq9OblutTr2MZDyTIOxcG54ZN2 +2t5JA7Syh0He6dn/NLlb//bPPnic3GuYfkgKht8M1XCJfnDKju+I9qRGf1DYIjP7 +m24IDC0MM9Fo7KIUY+vcV0J/BIkkZtZ9xh0iEEnR/KqYXmClym+EiWCwPhLloKL3 +6cZr5MaiYJsodYa73x35pSGvioUrBDb78v8nCNhoTUXzZv8E0s/0YI2UZSGhVEC6 +ANNXPvKeOdibGNDM4JOwETabkEng38UE6Oa7Qonra2MWsPegh+mnZ7/sqrktQRMB +T/Xne6vapDONeLeY2hVcT0j9f/S8rgpHPjP4/hmYE9d38Euwa7TZ0lHWcmJzQy6F +7HfGR3oYAYAwRnvl+kYUGZ46u9Nodi4+wycFc4+IpwFtnlUzRBOcOilnR2X6KFJW +SiVss1i7ECcWaKCBNN1MrpwGWeuSbCQ00c3bxe6ZN6goB1t2u1KAZqMK +=HFHX -----END PGP PUBLIC KEY BLOCK----- From 118db4f357fd4a415bb80018bc5f58130ba64e61 Mon Sep 17 00:00:00 2001 From: TheCharlatan Date: Sat, 24 Nov 2018 15:20:44 +0100 Subject: [PATCH 0859/1404] Cleanup leftovers from migrating depends from bitcoin Depends still contained some leftovers, like the `wallet` target that included bdb from bitcoin. This commit removes these unneeded targets, the miniupnpc package and the berkeley db package. Reflect the changes in the README as well. --- contrib/depends/Makefile | 18 +--------------- contrib/depends/README.md | 14 ++++-------- contrib/depends/config.site.in | 19 ---------------- contrib/depends/packages/bdb.mk | 31 --------------------------- contrib/depends/packages/miniupnpc.mk | 28 ------------------------ contrib/depends/packages/packages.mk | 2 -- 6 files changed, 5 insertions(+), 107 deletions(-) delete mode 100644 contrib/depends/packages/bdb.mk delete mode 100644 contrib/depends/packages/miniupnpc.mk diff --git a/contrib/depends/Makefile b/contrib/depends/Makefile index b0c032c09c8..bf33d706d4f 100644 --- a/contrib/depends/Makefile +++ b/contrib/depends/Makefile @@ -3,9 +3,6 @@ SOURCES_PATH ?= $(BASEDIR)/sources BASE_CACHE ?= $(BASEDIR)/built SDK_PATH ?= $(BASEDIR)/SDKs -NO_QT ?= -NO_WALLET ?= -NO_UPNP ?= FALLBACK_DOWNLOAD_PATH ?= https://bitcoincore.org/depends-sources BUILD = $(shell ./config.guess) @@ -97,17 +94,10 @@ $(host_arch)_$(host_os)_id_string+=$(shell $(host_CXX) --version 2>/dev/null) $(host_arch)_$(host_os)_id_string+=$(shell $(host_RANLIB) --version 2>/dev/null) $(host_arch)_$(host_os)_id_string+=$(shell $(host_STRIP) --version 2>/dev/null) -qt_packages_$(NO_QT) = $(qt_packages) $(qt_$(host_os)_packages) $(qt_$(host_arch)_$(host_os)_packages) -wallet_packages_$(NO_WALLET) = $(wallet_packages) -upnp_packages_$(NO_UPNP) = $(upnp_packages) -packages += $($(host_arch)_$(host_os)_packages) $($(host_os)_packages) $(qt_packages_) $(wallet_packages_) $(upnp_packages_) +packages += $($(host_arch)_$(host_os)_packages) $($(host_os)_packages) native_packages += $($(host_arch)_$(host_os)_native_packages) $($(host_os)_native_packages) -ifneq ($(qt_packages_),) -native_packages += $(qt_native_packages) -endif - all_packages = $(packages) $(native_packages) meta_depends = Makefile funcs.mk builders/default.mk hosts/default.mk hosts/$(host_os).mk builders/$(build_os).mk @@ -153,9 +143,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|@allow_host_packages@|$(ALLOW_HOST_PACKAGES)|' \ - -e 's|@no_qt@|$(NO_QT)|' \ - -e 's|@no_wallet@|$(NO_WALLET)|' \ - -e 's|@no_upnp@|$(NO_UPNP)|' \ -e 's|@debug@|$(DEBUG)|' \ $< > $@ $(AT)touch $@ @@ -176,9 +163,6 @@ $(host_prefix)/share/toolchain.cmake : toolchain.cmake.in $(host_prefix)/.stamp_ -e 's|@CPPFLAGS@|$(strip $(host_CPPFLAGS) $(host_$(release_type)_CPPFLAGS))|' \ -e 's|@LDFLAGS@|$(strip $(host_LDFLAGS) $(host_$(release_type)_LDFLAGS))|' \ -e 's|@allow_host_packages@|$(ALLOW_HOST_PACKAGES)|' \ - -e 's|@no_qt@|$(NO_QT)|' \ - -e 's|@no_wallet@|$(NO_WALLET)|' \ - -e 's|@no_upnp@|$(NO_UPNP)|' \ -e 's|@debug@|$(DEBUG)|' \ -e 's|@depends@|$(host_cmake)|' \ -e 's|@prefix@|$($(host_arch)_$(host_os)_prefix)|'\ diff --git a/contrib/depends/README.md b/contrib/depends/README.md index dd28245695a..59739836941 100644 --- a/contrib/depends/README.md +++ b/contrib/depends/README.md @@ -12,11 +12,11 @@ For example: make HOST=x86_64-w64-mingw32 -j4 -A prefix will be generated that's suitable for plugging into Bitcoin's -configure. In the above example, a dir named x86_64-w64-mingw32 will be -created. To use it for Bitcoin: +A toolchain will be generated that's suitable for plugging into Monero's +cmake. In the above example, a dir named x86_64-w64-mingw32 will be +created. To use it for Monero: - ./configure --prefix=`pwd`/depends/x86_64-w64-mingw32 + cmake -DCMAKE_TOOLCHAIN=`pwd`/contrib/depends/x86_64-w64-mingw32 Common `host-platform-triplets` for cross compilation are: @@ -35,16 +35,10 @@ The following can be set when running make: make FOO=bar 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 - NO_QT: Don't download/build/cache qt and its dependencies - 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 HOST_ID_SALT: Optional salt to use when generating host package ids BUILD_ID_SALT: Optional salt to use when generating build package ids -If some packages are not built, for example `make NO_WALLET=1`, the appropriate -options will be passed to bitcoin's configure. In this case, `--disable-wallet`. - Additional targets: download: run 'make download' to fetch all sources without building them diff --git a/contrib/depends/config.site.in b/contrib/depends/config.site.in index 0a4a9c327e2..dd91bcb2aa4 100644 --- a/contrib/depends/config.site.in +++ b/contrib/depends/config.site.in @@ -13,25 +13,6 @@ fi if test -z $with_qt_translationdir; then with_qt_translationdir=$depends_prefix/translations fi -if test -z $with_qt_bindir && test -z "@no_qt@"; then - with_qt_bindir=$depends_prefix/native/bin -fi -if test -z $with_protoc_bindir && test -z "@no_qt@"; then - with_protoc_bindir=$depends_prefix/native/bin -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 -z $with_gui && test -n "@no_qt@"; then - with_gui=no -fi if test x@host_os@ = xdarwin; then BREW=no diff --git a/contrib/depends/packages/bdb.mk b/contrib/depends/packages/bdb.mk deleted file mode 100644 index 050a60adde6..00000000000 --- a/contrib/depends/packages/bdb.mk +++ /dev/null @@ -1,31 +0,0 @@ -package=bdb -$(package)_version=4.8.30 -$(package)_download_path=https://download.oracle.com/berkeley-db -$(package)_file_name=db-$($(package)_version).NC.tar.gz -$(package)_sha256_hash=12edc0df75bf9abd7f82f821795bcee50f42cb2e5f76a6a281b85732798364ef -$(package)_build_subdir=build_unix - -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/' dbinc/atomic.h && \ - sed -i.old 's/atomic_init/atomic_init_db/' dbinc/atomic.h mp/mp_region.c mp/mp_mvcc.c mp/mp_fget.c mutex/mut_method.c mutex/mut_tas.c && \ - cp -f $(BASEDIR)/config.guess $(BASEDIR)/config.sub dist -endef - -define $(package)_config_cmds - ../dist/$($(package)_autoconf) -endef - -define $(package)_build_cmds - $(MAKE) libdb_cxx-4.8.a libdb-4.8.a -endef - -define $(package)_stage_cmds - $(MAKE) DESTDIR=$($(package)_staging_dir) install_lib install_include -endef diff --git a/contrib/depends/packages/miniupnpc.mk b/contrib/depends/packages/miniupnpc.mk deleted file mode 100644 index 1bb8cb5d266..00000000000 --- a/contrib/depends/packages/miniupnpc.mk +++ /dev/null @@ -1,28 +0,0 @@ -package=miniupnpc -$(package)_version=2.0.20170509 -$(package)_download_path=http://miniupnp.free.fr/files -$(package)_file_name=$(package)-$($(package)_version).tar.gz -$(package)_sha256_hash=d3c368627f5cdfb66d3ebd64ca39ba54d6ff14a61966dbecb8dd296b7039f16a - -define $(package)_set_vars -$(package)_build_opts=CC="$($(package)_cc)" -$(package)_build_opts_darwin=OS=Darwin LIBTOOL="$($(package)_libtool)" -$(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 -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/contrib/depends/packages/packages.mk b/contrib/depends/packages/packages.mk index f814c14d63c..234adbdd419 100644 --- a/contrib/depends/packages/packages.mk +++ b/contrib/depends/packages/packages.mk @@ -1,8 +1,6 @@ packages:=boost openssl libevent zeromq cppzmq zlib expat ldns cppzmq readline libiconv qt hidapi native_packages := native_ccache -wallet_packages=bdb - darwin_native_packages = native_biplist native_ds_store native_mac_alias darwin_packages = sodium-darwin From aaafa8a946211f5d5dc0e6e26f636f12a9700e70 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 24 Jun 2018 21:59:24 +0100 Subject: [PATCH 0860/1404] ringct: avoid repeated point conversion --- src/ringct/rctSigs.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/ringct/rctSigs.cpp b/src/ringct/rctSigs.cpp index baa649f82c0..316f0e5e81b 100644 --- a/src/ringct/rctSigs.cpp +++ b/src/ringct/rctSigs.cpp @@ -605,10 +605,19 @@ namespace rct { keyV tmp(rows + 1); size_t i; keyM M(cols, tmp); + ge_p3 Cp3; + CHECK_AND_ASSERT_MES_L1(ge_frombytes_vartime(&Cp3, C.bytes) == 0, false, "point conv failed"); + ge_cached Ccached; + ge_p3_to_cached(&Ccached, &Cp3); + ge_p1p1 p1; //create the matrix to mg sig for (i = 0; i < cols; i++) { M[i][0] = pubs[i].dest; - subKeys(M[i][1], pubs[i].mask, C); + ge_p3 p3; + CHECK_AND_ASSERT_MES_L1(ge_frombytes_vartime(&p3, pubs[i].mask.bytes) == 0, false, "point conv failed"); + ge_sub(&p1, &p3, &Ccached); + ge_p1p1_to_p3(&p3, &p1); + ge_p3_tobytes(M[i][1].bytes, &p3); } //DP(C); return MLSAG_Ver(message, M, mg, rows); From b97059fc8ef4ecb491b759a4aa5b3bc7754fb4e5 Mon Sep 17 00:00:00 2001 From: erciccione Date: Wed, 12 Dec 2018 14:44:43 +0100 Subject: [PATCH 0861/1404] add erciccione's pgp key --- utils/gpg_keys/erciccione.asc | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 utils/gpg_keys/erciccione.asc diff --git a/utils/gpg_keys/erciccione.asc b/utils/gpg_keys/erciccione.asc new file mode 100644 index 00000000000..109941d554d --- /dev/null +++ b/utils/gpg_keys/erciccione.asc @@ -0,0 +1,30 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mQENBFohQ30BCADktKHeTg48Dm4oU9JPrfHXzY3sZtTQsqsQGmYiFT8nQjRnSzic +dZe4sh2W4UYQSLFw7pEb7a51ZYkz5+gFKcj3NmIaEpNra7+SEtszoS3IALHUyGGQ ++nFDxUHwTV5OeIUXPHs2AnCVZa4yEWmavXFiPL35+dcFkwyUA5psd97dFIbtkPQ4 +nT2RCc2emy6NHVE5L7pzlBe9CeFkFyovs847d3WDa/96TT4JMRm34qvUrSvOkqti +xrui8hGwNedAw64ObiJhQLa3rvQ8bCtTLNHhpdc+zhBi3L5nIdwquMd5Bd6zHfmW +HHyXEoRTZYMRabNBSR9FrsjGBiHG9wuEK2B7ABEBAAG0NWVyY2ljY2lvbmVAcHJv +dG9ubWFpbC5jb20gPGVyY2ljY2lvbmVAcHJvdG9ubWFpbC5jb20+iQE1BBABCAAp +BQJaIUN9BgsJBwgDAgkQdir4xgjlbN8EFQgKAgMWAgECGQECGwMCHgEAAFc+CAC2 +7BDCYPWfXCKtaqP/jzay46nai66hSyuIJ2sd4RSbQlVNCwFUQ2NtUwDxtzdzB3bk +AfsqrkdSS7V82bfZPOoYapWikjA3LGd2GHvClKNmIgIg3CvrAByD9okhLYOmxChU +5/FzMgSgDdbez4Z+XXzUothirHcHR7J+PtzqMZWusuyNqfXlWgPNPbAHG/hm5RLw +s6uGOLDqhJDiqi0HjC/p9ponPAJ3g0jfECnXFp7R32EgbBHExT/g8e3L4J3Q8y/E +WrZiQsIc89srANw5YIcjlr/i3PY8tyrelpLDVBj05802WYamYx10Zd2epM4NglBs ++YjiWpSM9fTi9QKFCHyAuQENBFohQ30BCADe4FJcus4e3FzsagKdAM/iFeZ4BE2l +gVgSg8CVWIYlTAmeK+SBPfsxvwOWxmx/KkirWn3hOSA2U6mp9+g6hr9rLqWNwatM +OsitrJlWG2aPcuAmlzLFvtH0Vbilh0m4B98g2XUa94HtrlGdPAoYPquS1HsfL284 +CHpQXXOypOS+/spTK/OlpdUaPaVyqVfE3kzXYqt24RbDcsOvUzSzVRCA8kBp6azm +1qoqDboP112Ax7OkV0FGc/kd5PNMPTOnVR+4wVXWhl6gAI1I2JFvo3EJlxtddyTB +Rkx06jbYRyPj9KCEC4CBx9qsNlo7TUHgObAr+CLX4I5hkEWW0pS98vlpABEBAAGJ +AR8EGAEIABMFAlohQ30JEHYq+MYI5WzfAhsMAAD1twgAxjbETzOUeq/bWwVoGzmh +SMJajZs2c5Pv0SAwi5QZCoRefp2HYfpWcBsfZG84B0EmJb9Wwu3wrXWBf96xMTFM +neU0+L2+RcH8uH7/C2MlVaZjFv5dPPJAtwbY2BQl1bX5DrEwZ8QH7IUXZmWfjmZ3 +ww5llfCAyVCxMn+tnIJnk/7HODY0t1LmusFvZsAVsWIgdndKImXM4CTzirfmDks8 +TxpXfaJzU3B3KTIzMgEvwtTLJfgf07Foy4ITIl+1zS7gLL0fXldtx6fJJMvANsLf +JrRjafh21qfJb3I9TUH8G7C0Ln3LspBGsWZ4f+fjq4LEg7SCOAJSeBwfOtMJeCrU +/Q== +=a70Z +-----END PGP PUBLIC KEY BLOCK----- From 1ebcd7b9b0b6c6a81d7439a4ba717df364c94db8 Mon Sep 17 00:00:00 2001 From: rbrunner7 Date: Sun, 28 Oct 2018 14:46:58 +0100 Subject: [PATCH 0862/1404] MMS (Multisig Messaging System): Initial version --- src/common/util.cpp | 12 + src/common/util.h | 2 + src/simplewallet/simplewallet.cpp | 1495 +++++++++++++++++++++++++--- src/simplewallet/simplewallet.h | 45 +- src/wallet/CMakeLists.txt | 9 +- src/wallet/message_store.cpp | 1445 +++++++++++++++++++++++++++ src/wallet/message_store.h | 420 ++++++++ src/wallet/message_transporter.cpp | 317 ++++++ src/wallet/message_transporter.h | 113 +++ src/wallet/wallet2.cpp | 117 ++- src/wallet/wallet2.h | 14 +- src/wallet/wallet_errors.h | 25 + 12 files changed, 3882 insertions(+), 132 deletions(-) create mode 100644 src/wallet/message_store.cpp create mode 100644 src/wallet/message_store.h create mode 100644 src/wallet/message_transporter.cpp create mode 100644 src/wallet/message_transporter.h diff --git a/src/common/util.cpp b/src/common/util.cpp index 58b0d8210f6..448f792ff34 100644 --- a/src/common/util.cpp +++ b/src/common/util.cpp @@ -58,6 +58,7 @@ #include "include_base_utils.h" #include "file_io_utils.h" #include "wipeable_string.h" +#include "misc_os_dependent.h" using namespace epee; #include "crypto/crypto.h" @@ -1025,4 +1026,15 @@ std::string get_nix_version_display_string() #endif } + std::string get_human_readable_timestamp(uint64_t ts) + { + char buffer[64]; + if (ts < 1234567890) + return ""; + time_t tt = ts; + struct tm tm; + misc_utils::get_gmt_time(tt, tm); + strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", &tm); + return std::string(buffer); + } } diff --git a/src/common/util.h b/src/common/util.h index 1c5c5f4e7f3..d5aca15d14d 100644 --- a/src/common/util.h +++ b/src/common/util.h @@ -242,4 +242,6 @@ namespace tools #endif void closefrom(int fd); + + std::string get_human_readable_timestamp(uint64_t ts); } diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index b729c4bb7fc..2817027742a 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -65,6 +65,7 @@ #include "wallet/wallet_args.h" #include "version.h" #include +#include "wallet/message_store.h" #ifdef WIN32 #include @@ -102,12 +103,14 @@ typedef cryptonote::simple_wallet sw; m_auto_refresh_enabled.store(auto_refresh_enabled, std::memory_order_relaxed); \ }) -#define SCOPED_WALLET_UNLOCK() \ +#define SCOPED_WALLET_UNLOCK_ON_BAD_PASSWORD(code) \ LOCK_IDLE_SCOPE(); \ boost::optional pwd_container = boost::none; \ - if (m_wallet->ask_password() && !(pwd_container = get_and_verify_password())) { return true; } \ + if (m_wallet->ask_password() && !(pwd_container = get_and_verify_password())) { code; } \ tools::wallet_keys_unlocker unlocker(*m_wallet, pwd_container); +#define SCOPED_WALLET_UNLOCK() SCOPED_WALLET_UNLOCK_ON_BAD_PASSWORD(return true;) + enum TransferType { Transfer, TransferLocked, @@ -835,66 +838,84 @@ bool simple_wallet::print_fee_info(const std::vector &args/* = std: } bool simple_wallet::prepare_multisig(const std::vector &args) +{ + prepare_multisig_main(args, false); + return true; +} + +bool simple_wallet::prepare_multisig_main(const std::vector &args, bool called_by_mms) { if (m_wallet->key_on_device()) { fail_msg_writer() << tr("command not supported by HW wallet"); - return true; + return false; } if (m_wallet->multisig()) { fail_msg_writer() << tr("This wallet is already multisig"); - return true; + return false; } if (m_wallet->watch_only()) { fail_msg_writer() << tr("wallet is watch-only and cannot be made multisig"); - return true; + return false; } if(m_wallet->get_num_transfer_details()) { fail_msg_writer() << tr("This wallet has been used before, please use a new wallet to create a multisig wallet"); - return true; + return false; } - SCOPED_WALLET_UNLOCK(); + SCOPED_WALLET_UNLOCK_ON_BAD_PASSWORD(return false;); std::string multisig_info = m_wallet->get_multisig_info(); success_msg_writer() << multisig_info; success_msg_writer() << tr("Send this multisig info to all other participants, then use make_multisig [...] with others' multisig info"); success_msg_writer() << tr("This includes the PRIVATE view key, so needs to be disclosed only to that multisig wallet's participants "); + + if (called_by_mms) + { + get_message_store().process_wallet_created_data(get_multisig_wallet_state(), mms::message_type::key_set, multisig_info); + } + return true; } bool simple_wallet::make_multisig(const std::vector &args) +{ + make_multisig_main(args, false); + return true; +} + +bool simple_wallet::make_multisig_main(const std::vector &args, bool called_by_mms) { if (m_wallet->key_on_device()) { fail_msg_writer() << tr("command not supported by HW wallet"); - return true; + return false; } if (m_wallet->multisig()) { fail_msg_writer() << tr("This wallet is already multisig"); - return true; + return false; } if (m_wallet->watch_only()) { fail_msg_writer() << tr("wallet is watch-only and cannot be made multisig"); - return true; + return false; } if(m_wallet->get_num_transfer_details()) { fail_msg_writer() << tr("This wallet has been used before, please use a new wallet to create a multisig wallet"); - return true; + return false; } if (args.size() < 2) { fail_msg_writer() << tr("usage: make_multisig [...]"); - return true; + return false; } // parse threshold @@ -902,14 +923,14 @@ bool simple_wallet::make_multisig(const std::vector &args) if (!string_tools::get_xtype_from_string(threshold, args[0])) { fail_msg_writer() << tr("Invalid threshold"); - return true; + return false; } const auto orig_pwd_container = get_and_verify_password(); if(orig_pwd_container == boost::none) { fail_msg_writer() << tr("Your original password was incorrect."); - return true; + return false; } LOCK_IDLE_SCOPE(); @@ -924,20 +945,24 @@ bool simple_wallet::make_multisig(const std::vector &args) success_msg_writer() << tr("Another step is needed"); success_msg_writer() << multisig_extra_info; success_msg_writer() << tr("Send this multisig info to all other participants, then use exchange_multisig_keys [...] with others' multisig info"); + if (called_by_mms) + { + get_message_store().process_wallet_created_data(get_multisig_wallet_state(), mms::message_type::additional_key_set, multisig_extra_info); + } return true; } } catch (const std::exception &e) { fail_msg_writer() << tr("Error creating multisig: ") << e.what(); - return true; + return false; } uint32_t total; if (!m_wallet->multisig(NULL, &threshold, &total)) { fail_msg_writer() << tr("Error creating multisig: new wallet is not multisig"); - return true; + return false; } success_msg_writer() << std::to_string(threshold) << "/" << total << tr(" multisig address: ") << m_wallet->get_account().get_public_address_str(m_wallet->nettype()); @@ -997,35 +1022,41 @@ bool simple_wallet::finalize_multisig(const std::vector &args) return true; } -bool simple_wallet::exchange_multisig_keys(const std::vector &args) { +bool simple_wallet::exchange_multisig_keys(const std::vector &args) +{ + exchange_multisig_keys_main(args, false); + return true; +} + +bool simple_wallet::exchange_multisig_keys_main(const std::vector &args, bool called_by_mms) { bool ready; if (m_wallet->key_on_device()) { fail_msg_writer() << tr("command not supported by HW wallet"); - return true; + return false; } if (!m_wallet->multisig(&ready)) { fail_msg_writer() << tr("This wallet is not multisig"); - return true; + return false; } if (ready) { fail_msg_writer() << tr("This wallet is already finalized"); - return true; + return false; } const auto orig_pwd_container = get_and_verify_password(); if(orig_pwd_container == boost::none) { fail_msg_writer() << tr("Your original password was incorrect."); - return true; + return false; } if (args.size() < 2) { fail_msg_writer() << tr("usage: exchange_multisig_keys [...]"); - return true; + return false; } try @@ -1036,6 +1067,10 @@ bool simple_wallet::exchange_multisig_keys(const std::vector &args) message_writer() << tr("Another step is needed"); message_writer() << multisig_extra_info; message_writer() << tr("Send this multisig info to all other participants, then use exchange_multisig_keys [...] with others' multisig info"); + if (called_by_mms) + { + get_message_store().process_wallet_created_data(get_multisig_wallet_state(), mms::message_type::additional_key_set, multisig_extra_info); + } return true; } else { uint32_t threshold, total; @@ -1046,58 +1081,71 @@ bool simple_wallet::exchange_multisig_keys(const std::vector &args) catch (const std::exception &e) { fail_msg_writer() << tr("Failed to perform multisig keys exchange: ") << e.what(); - return true; + return false; } return true; } bool simple_wallet::export_multisig(const std::vector &args) +{ + export_multisig_main(args, false); + return true; +} + +bool simple_wallet::export_multisig_main(const std::vector &args, bool called_by_mms) { bool ready; if (m_wallet->key_on_device()) { fail_msg_writer() << tr("command not supported by HW wallet"); - return true; + return false; } if (!m_wallet->multisig(&ready)) { fail_msg_writer() << tr("This wallet is not multisig"); - return true; + return false; } if (!ready) { fail_msg_writer() << tr("This multisig wallet is not yet finalized"); - return true; + return false; } if (args.size() != 1) { fail_msg_writer() << tr("usage: export_multisig_info "); - return true; + return false; } const std::string filename = args[0]; - if (m_wallet->confirm_export_overwrite() && !check_file_overwrite(filename)) + if (!called_by_mms && m_wallet->confirm_export_overwrite() && !check_file_overwrite(filename)) return true; - SCOPED_WALLET_UNLOCK(); + SCOPED_WALLET_UNLOCK_ON_BAD_PASSWORD(return false;); try { cryptonote::blobdata ciphertext = m_wallet->export_multisig(); - bool r = epee::file_io_utils::save_string_to_file(filename, ciphertext); - if (!r) + if (called_by_mms) { - fail_msg_writer() << tr("failed to save file ") << filename; - return true; + get_message_store().process_wallet_created_data(get_multisig_wallet_state(), mms::message_type::multisig_sync_data, ciphertext); + } + else + { + bool r = epee::file_io_utils::save_string_to_file(filename, ciphertext); + if (!r) + { + fail_msg_writer() << tr("failed to save file ") << filename; + return false; + } } } catch (const std::exception &e) { LOG_ERROR("Error exporting multisig info: " << e.what()); fail_msg_writer() << tr("Error exporting multisig info: ") << e.what(); - return true; + return false; } success_msg_writer() << tr("Multisig info exported to ") << filename; @@ -1105,45 +1153,58 @@ bool simple_wallet::export_multisig(const std::vector &args) } bool simple_wallet::import_multisig(const std::vector &args) +{ + import_multisig_main(args, false); + return true; +} + +bool simple_wallet::import_multisig_main(const std::vector &args, bool called_by_mms) { bool ready; uint32_t threshold, total; if (m_wallet->key_on_device()) { fail_msg_writer() << tr("command not supported by HW wallet"); - return true; + return false; } if (!m_wallet->multisig(&ready, &threshold, &total)) { fail_msg_writer() << tr("This wallet is not multisig"); - return true; + return false; } if (!ready) { fail_msg_writer() << tr("This multisig wallet is not yet finalized"); - return true; + return false; } if (args.size() < threshold - 1) { fail_msg_writer() << tr("usage: import_multisig_info [...] - one for each other participant"); - return true; + return false; } std::vector info; for (size_t n = 0; n < args.size(); ++n) { - const std::string filename = args[n]; - std::string data; - bool r = epee::file_io_utils::load_file_to_string(filename, data); - if (!r) + if (called_by_mms) { - fail_msg_writer() << tr("failed to read file ") << filename; - return true; + info.push_back(args[n]); + } + else + { + const std::string &filename = args[n]; + std::string data; + bool r = epee::file_io_utils::load_file_to_string(filename, data); + if (!r) + { + fail_msg_writer() << tr("failed to read file ") << filename; + return false; + } + info.push_back(std::move(data)); } - info.push_back(std::move(data)); } - SCOPED_WALLET_UNLOCK(); + SCOPED_WALLET_UNLOCK_ON_BAD_PASSWORD(return false;); // all read and parsed, actually import try @@ -1158,7 +1219,7 @@ bool simple_wallet::import_multisig(const std::vector &args) catch (const std::exception &e) { fail_msg_writer() << tr("Failed to import multisig info: ") << e.what(); - return true; + return false; } if (m_wallet->is_trusted_daemon()) { @@ -1169,11 +1230,13 @@ bool simple_wallet::import_multisig(const std::vector &args) catch (const std::exception &e) { message_writer() << tr("Failed to update spent status after importing multisig info: ") << e.what(); + return false; } } else { message_writer() << tr("Untrusted daemon, spent status may be incorrect. Use a trusted daemon and run \"rescan_spent\""); + return false; } return true; } @@ -1185,52 +1248,94 @@ bool simple_wallet::accept_loaded_tx(const tools::wallet2::multisig_tx_set &txs) } bool simple_wallet::sign_multisig(const std::vector &args) +{ + sign_multisig_main(args, false); + return true; +} + +bool simple_wallet::sign_multisig_main(const std::vector &args, bool called_by_mms) { bool ready; if (m_wallet->key_on_device()) { fail_msg_writer() << tr("command not supported by HW wallet"); - return true; + return false; } if(!m_wallet->multisig(&ready)) { fail_msg_writer() << tr("This is not a multisig wallet"); - return true; + return false; } if (!ready) { fail_msg_writer() << tr("This multisig wallet is not yet finalized"); - return true; + return false; } if (args.size() != 1) { fail_msg_writer() << tr("usage: sign_multisig "); - return true; + return false; } - SCOPED_WALLET_UNLOCK(); + SCOPED_WALLET_UNLOCK_ON_BAD_PASSWORD(return false;); std::string filename = args[0]; std::vector txids; uint32_t signers = 0; try { - bool r = m_wallet->sign_multisig_tx_from_file(filename, txids, [&](const tools::wallet2::multisig_tx_set &tx){ signers = tx.m_signers.size(); return accept_loaded_tx(tx); }); - if (!r) + if (called_by_mms) { - fail_msg_writer() << tr("Failed to sign multisig transaction"); - return true; + tools::wallet2::multisig_tx_set exported_txs; + std::string ciphertext; + bool r = m_wallet->load_multisig_tx(args[0], exported_txs, [&](const tools::wallet2::multisig_tx_set &tx){ signers = tx.m_signers.size(); return accept_loaded_tx(tx); }); + if (r) + { + r = m_wallet->sign_multisig_tx(exported_txs, txids); + } + if (r) + { + ciphertext = m_wallet->save_multisig_tx(exported_txs); + if (ciphertext.empty()) + { + r = false; + } + } + if (r) + { + mms::message_type message_type = mms::message_type::fully_signed_tx; + if (txids.empty()) + { + message_type = mms::message_type::partially_signed_tx; + } + get_message_store().process_wallet_created_data(get_multisig_wallet_state(), message_type, ciphertext); + filename = "MMS"; // for the messages below + } + else + { + fail_msg_writer() << tr("Failed to sign multisig transaction"); + return false; + } + } + else + { + bool r = m_wallet->sign_multisig_tx_from_file(filename, txids, [&](const tools::wallet2::multisig_tx_set &tx){ signers = tx.m_signers.size(); return accept_loaded_tx(tx); }); + if (!r) + { + fail_msg_writer() << tr("Failed to sign multisig transaction"); + return false; + } } } catch (const tools::error::multisig_export_needed& e) { fail_msg_writer() << tr("Multisig error: ") << e.what(); - return true; + return false; } catch (const std::exception &e) { fail_msg_writer() << tr("Failed to sign multisig transaction: ") << e.what(); - return true; + return false; } if (txids.empty()) @@ -1258,50 +1363,68 @@ bool simple_wallet::sign_multisig(const std::vector &args) } bool simple_wallet::submit_multisig(const std::vector &args) +{ + submit_multisig_main(args, false); + return true; +} + +bool simple_wallet::submit_multisig_main(const std::vector &args, bool called_by_mms) { bool ready; uint32_t threshold; if (m_wallet->key_on_device()) { fail_msg_writer() << tr("command not supported by HW wallet"); - return true; + return false; } if (!m_wallet->multisig(&ready, &threshold)) { fail_msg_writer() << tr("This is not a multisig wallet"); - return true; + return false; } if (!ready) { fail_msg_writer() << tr("This multisig wallet is not yet finalized"); - return true; + return false; } if (args.size() != 1) { fail_msg_writer() << tr("usage: submit_multisig "); - return true; + return false; } if (!try_connect_to_daemon()) - return true; + return false; - SCOPED_WALLET_UNLOCK(); + SCOPED_WALLET_UNLOCK_ON_BAD_PASSWORD(return false;); std::string filename = args[0]; try { tools::wallet2::multisig_tx_set txs; - bool r = m_wallet->load_multisig_tx_from_file(filename, txs, [&](const tools::wallet2::multisig_tx_set &tx){ return accept_loaded_tx(tx); }); - if (!r) + if (called_by_mms) { - fail_msg_writer() << tr("Failed to load multisig transaction from file"); - return true; + bool r = m_wallet->load_multisig_tx(args[0], txs, [&](const tools::wallet2::multisig_tx_set &tx){ return accept_loaded_tx(tx); }); + if (!r) + { + fail_msg_writer() << tr("Failed to load multisig transaction from MMS"); + return false; + } + } + else + { + bool r = m_wallet->load_multisig_tx_from_file(filename, txs, [&](const tools::wallet2::multisig_tx_set &tx){ return accept_loaded_tx(tx); }); + if (!r) + { + fail_msg_writer() << tr("Failed to load multisig transaction from file"); + return false; + } } if (txs.m_signers.size() < threshold) { fail_msg_writer() << (boost::format(tr("Multisig transaction signed by only %u signers, needs %u more signatures")) % txs.m_signers.size() % (threshold - txs.m_signers.size())).str(); - return true; + return false; } // actually commit the transactions @@ -1320,6 +1443,7 @@ bool simple_wallet::submit_multisig(const std::vector &args) { LOG_ERROR("unknown error"); fail_msg_writer() << tr("unknown error"); + return false; } return true; @@ -2308,6 +2432,12 @@ bool simple_wallet::help(const std::vector &args/* = std::vector" + std::vector mms_args(1, args.front() + " " + args.back()); + success_msg_writer() << get_command_usage(mms_args); + } else { success_msg_writer() << get_command_usage(args); @@ -2655,6 +2785,89 @@ simple_wallet::simple_wallet() boost::bind(&simple_wallet::export_raw_multisig, this, _1), tr("export_raw_multisig_tx "), tr("Export a signed multisig transaction to a file")); + m_cmd_binder.set_handler("mms", + boost::bind(&simple_wallet::mms, this, _1), + tr("mms [ []]"), + tr("Interface with the MMS (Multisig Messaging System)\n" + " is one of:\n" + " init, info, signer, list, next, sync, transfer, delete, send, receive, export, note, show, set, help\n" + " send_signer_config, start_auto_config, stop_auto_config, auto_config\n" + "Get help about a subcommand with: help mms , or mms help ")); + m_cmd_binder.set_handler("mms init", + boost::bind(&simple_wallet::mms, this, _1), + tr("mms init / "), + tr("Initialize and configure the MMS for M/N = number of required signers/number of authorized signers multisig")); + m_cmd_binder.set_handler("mms info", + boost::bind(&simple_wallet::mms, this, _1), + tr("mms info"), + tr("Display current MMS configuration")); + m_cmd_binder.set_handler("mms signer", + boost::bind(&simple_wallet::mms, this, _1), + tr("mms signer [